From 517a942ef6e8cbf59bca95d3fd510fe4d3e76a6f Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 23 Aug 2023 13:06:26 -0600 Subject: [PATCH 001/309] [1658]: Write core of protos and stub out the tx and query endpoints. --- proto/provenance/exchange/v1/events.proto | 149 ++++++++++++++++ proto/provenance/exchange/v1/market.proto | 131 ++++++++++++++ proto/provenance/exchange/v1/orders.proto | 65 +++++++ proto/provenance/exchange/v1/params.proto | 25 +++ proto/provenance/exchange/v1/qenesis.proto | 27 +++ proto/provenance/exchange/v1/query.proto | 127 ++++++++++++++ proto/provenance/exchange/v1/tx.proto | 191 +++++++++++++++++++++ 7 files changed, 715 insertions(+) create mode 100644 proto/provenance/exchange/v1/events.proto create mode 100644 proto/provenance/exchange/v1/market.proto create mode 100644 proto/provenance/exchange/v1/orders.proto create mode 100644 proto/provenance/exchange/v1/params.proto create mode 100644 proto/provenance/exchange/v1/qenesis.proto create mode 100644 proto/provenance/exchange/v1/query.proto create mode 100644 proto/provenance/exchange/v1/tx.proto diff --git a/proto/provenance/exchange/v1/events.proto b/proto/provenance/exchange/v1/events.proto new file mode 100644 index 0000000000..03902dbd5b --- /dev/null +++ b/proto/provenance/exchange/v1/events.proto @@ -0,0 +1,149 @@ +syntax = "proto3"; +package provenance.exchange.v1; + +option go_package = "github.com/provenance-io/provenance/x/exchange"; + +option java_package = "io.provenance.exchange.v1"; +option java_multiple_files = true; + +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; + +// TODO[1658]: Examine the difference between EventOrderPartiallyFilled and EventOrderPartiallyFilled2 to see if it's okay to have a Coins field instead of a string. Then remove one of them. + +// EventOrderCreated is an event emitted when an order is created. +message EventOrderCreated { + // order_id is the numerical identifier of the order created. + uint64 order_id = 1; + // order_type is the type of order, e.g. "ask" or "bid". + string order_type = 2; +} + +// EventOrderCancelled is an event emitted when an order is cancelled. +message EventOrderCancelled { + // order_id is the numerical identifier of the order created. + uint64 order_id = 1; + // cancelled_by is the account that triggered the cancellation of the order. + string cancelled_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +// EventOrderFilled is an event emitted when an order has been filled in full. This event is also used for orders that were previously partially filled, but have now been filled in full. +message EventOrderFilled { + // order_id is the numerical identifier of the order created. + uint64 order_id = 1; +} + +// EventOrderPartiallyFilled is an event emitted when an order filled in part and still has more left to fill. +message EventOrderPartiallyFilled { + // order_id is the numerical identifier of the order created. + uint64 order_id = 1; + // amount_filled is the amount of assets that were filled (and removed from the order). + repeated cosmos.base.v1beta1.Coin assets_filled = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // fees_filled is the amount of fees removed from the order. + repeated cosmos.base.v1beta1.Coin fees_filled = 3 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// EventOrderPartiallyFilled2 is an event emitted when an order filled in part and still has more left to fill. +message EventOrderPartiallyFilled2 { + // order_id is the numerical identifier of the order created. + uint64 order_id = 1; + // amount_filled is the amount of assets that were filled (and removed from the order). + string assets_filled = 2; + // fees_filled is the amount of settlement fees removed from the order. + string fees_filled = 3; +} + +// EventMarketWithdraw is an event emitted when a withdrawal of a market's collected fees is made. +message EventMarketWithdraw { + // market_id is the numerical identifier of the market. + uint32 market_id = 1; + // amount_withdrawn is the amount of funds withdrawn from the market account. + repeated cosmos.base.v1beta1.Coin amount_withdrawn = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // destination is the account that received the funds. + string destination = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // withdrawn_by is the account that requested the withdrawal. + string withdrawn_by = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +// EventMarketDetailsUpdated is an event emitted when a market's details are updated. +message EventMarketDetailsUpdated { + // market_id is the numerical identifier of the market. + uint32 market_id = 1; + // updated_by is the account that updated the details. + string updated_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +// EventMarketEnabled is an event emitted when a market is enabled. +message EventMarketEnabled { + // market_id is the numerical identifier of the market. + uint32 market_id = 1; + // updated_by is the account that enabled the market. + string updated_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +// EventMarketDisabled is an event emitted when a market is disabled. +message EventMarketDisabled { + // market_id is the numerical identifier of the market. + uint32 market_id = 1; + // updated_by is the account that disabled the market. + string updated_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +// EventMarketUserSettleUpdated is an event emitted when a market's self_settle option is updated. +message EventMarketUserSettleUpdated { + // market_id is the numerical identifier of the market. + uint32 market_id = 1; + // updated_by is the account that updated the self_settle option. + string updated_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +// EventMarketPermissionsUpdated is an event emitted when a market's permissions are updated. +message EventMarketPermissionsUpdated { + // market_id is the numerical identifier of the market. + uint32 market_id = 1; + // updated_by is the account that updated the permissions. + string updated_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +// EventMarketReqAttrUpdated is an event emitted when a market's required attributes are updated. +message EventMarketReqAttrUpdated { + // market_id is the numerical identifier of the market. + uint32 market_id = 1; + // updated_by is the account that updated the required attributes. + string updated_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +// EventCreateMarketSubmitted is an event emitted during CreateMarket indicating that a governance proposal was submitted to create a market. +message EventCreateMarketSubmitted { + // market_id is the numerical identifier of the market. + uint32 market_id = 1; + // proposal_id is the identifier of the governance proposal that was submitted to create the market. + uint64 proposal_id = 2; + // submitted_by is the account that requested the creation of the market. + string submitted_by = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +// EventMarketCreated is an event emitted when a market has been created. +message EventMarketCreated { + // market_id is the numerical identifier of the market. + uint32 market_id = 1; +} + +// EventMarketFeesUpdated is an event emitted when a market's fees have been updated. +message EventMarketFeesUpdated { + // market_id is the numerical identifier of the market. + uint32 market_id = 1; +} + +// EventParamsUpdated is an event emitted when the exchange module's params have been updated. +message EventParamsUpdated {} \ No newline at end of file diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto new file mode 100644 index 0000000000..221ac2657b --- /dev/null +++ b/proto/provenance/exchange/v1/market.proto @@ -0,0 +1,131 @@ +syntax = "proto3"; +package provenance.exchange.v1; + +option go_package = "github.com/provenance-io/provenance/x/exchange"; + +option java_package = "io.provenance.exchange.v1"; +option java_multiple_files = true; + +import "cosmos/auth/v1beta1/auth.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; + +// TODO[1658]: Investigate whether the yaml gogoproto.moretags are still needed. + +// MarketAccount is an account type for use with the accounts module to hold some basic information about a market. +message MarketAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + // base_account is the base cosmos account information. + cosmos.auth.v1beta1.BaseAccount base_account = 1 + [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"base_account\""]; + + // market_id is the numerical identifier for this market. + uint32 market_id = 2; + // market_details is some human-consumable information about this market. + MarketDetails market_details = 3 [(gogoproto.nullable) = false]; +} + +// MarketDetails contains information about a market. +message MarketDetails { + // name is a moniker that people can use to refer to this market. + string name = 1; + // description extra information about this market. The field is meant to be human-readable. + string description = 2; + // website_url is a url people can use to get to this market, or at least get more information about this market. + string website_url = 3; + // icon_uri is a uri for an icon to associate with this market. + string icon_uri = 4; +} + +// Market contains all information about a market. +message Market { + // market_id is the numerical identifier for this market. + uint32 market_id = 1; + // market_details is some information about this market. + MarketDetails market_details = 2 [(gogoproto.nullable) = false]; + // fee_create_ask_flat is the flat fee charged for creating an ask order. + // Each coin entry is a separate option. When an ask is created, one of these must be paid. + // If empty, no fee is required to create an ask order. + repeated cosmos.base.v1beta1.Coin fee_create_ask_flat = 3 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // fee_create_bid_flat is the flat fee charged for creating a bid order. + // Each coin entry is a separate option. When a bid is created, one of these must be paid. + // If empty, no fee is required to create a bid order. + repeated cosmos.base.v1beta1.Coin fee_create_bid_flat = 4 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // fee_settlement_seller_flat is the flat fee charged to the seller during settlement. + // Each coin entry is a separate option. When an ask is settled, the seller will pay the amount in the denom that matches the price they received. + repeated cosmos.base.v1beta1.Coin fee_settlement_seller_flat = 5 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // fee_settlement_seller_ratios is the fee to charge a seller during settlement based on the price they are receiving. + // The price and fee denoms must be equal for each entry, and only one entry for any given denom is allowed. + repeated FeeRatio fee_settlement_seller_ratios = 6; + // fee_settlement_buyer_flat is the flat fee charged to the buyer during settlement. + // Each coin entry is a separate option. When a bid is created, the settlement fees provided must contain one of these. + repeated cosmos.base.v1beta1.Coin fee_settlement_buyer_flat = 7 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // fee_settlement_buyer_ratios is the fee to charge a buyer during settlement based on the price they are spending. + // The price and fee denoms do not have to equal. + // Multiple entries for any given price or fee denom are allowed, but each price denom to fee denom pair can only have one entry. + repeated FeeRatio fee_settlement_buyer_ratios = 8; + // accepting_orders is whether this market is allowing orders to be created for it. + bool accepting_orders = 9; + // allow_user_settlement is whether this market allows users to initiate their own settlements. + // For example, the FillBids and FillAsks endpoints are available if and only if this is true. + // The MarketSettle endpoint is only available to market actors regardless of the value of this field. + bool allow_user_settlement = 10; + // access_grants is the list of addresses and permissions granted for this market. + repeated AccessGrant access_grants = 11; + // req_attr_create_ask is a list of attributes required on an account for that account to be allowed to create an ask order. + // TODO[1658]: Add comment about wildcards. + repeated string req_attr_create_ask = 12; + // req_attr_create_ask is a list of attributes required on an account for that account to be allowed to create an ask order. + // TODO[1658]: Add comment about wildcards. + repeated string req_attr_create_bid = 13; +} + +// FeeRatio defines a ratio of price amount to fee amount. +// For an order to be valid, its price must be evenly divisible by a FeeRatio's price. +message FeeRatio { + // price is the unit the order price is divided by to get how much of the fee should apply. + cosmos.base.v1beta1.Coin price = 1; + // fee is the amount to charge per price unit. + cosmos.base.v1beta1.Coin fee = 2; +} + +// AddrPermissions associates an address with a list of permissions available for that address. +message AccessGrant { + // address is the address that these permissions apply to. + string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // allowed is the list of permissions available for the address. + repeated Permission permissions = 2; +} + +// Permission defines the different types of permission that can be given to an account for a market. +enum Permission { + // PERMISSION_UNSPECIFIED is the zero-value Permission; it is an error to use it. + PERMISSION_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "unspecified"]; + // PERMISSION_SETTLE is the ability to use the Settle Tx endpoint on behalf of a market. + PERMISSION_SETTLE = 1 [(gogoproto.enumvalue_customname) = "settle"]; + // PERMISSION_CANCEL is the ability to use the Cancel Tx endpoint on behalf of a market. + PERMISSION_CANCEL = 2 [(gogoproto.enumvalue_customname) = "cancel"]; + // PERMISSION_WITHDRAW is the ability to use the MarketWithdraw Tx endpoint. + PERMISSION_WITHDRAW = 3 [(gogoproto.enumvalue_customname) = "withdraw"]; + // PERMISSION_UPDATE is the ability to use the MarketUpdate* Tx endpoints. + PERMISSION_UPDATE = 4 [(gogoproto.enumvalue_customname) = "update"]; + // PERMISSION_PERMISSIONS is the ability to use the MarketManagePermissions Tx endpoint. + PERMISSION_PERMISSIONS = 5 [(gogoproto.enumvalue_customname) = "permissions"]; + // PERMISSION_ATTRIBUTES is the ability to use the MarketManageReqAttrs Tx endpoint. + PERMISSION_ATTRIBUTES = 6 [(gogoproto.enumvalue_customname) = "attributes"]; +} \ No newline at end of file diff --git a/proto/provenance/exchange/v1/orders.proto b/proto/provenance/exchange/v1/orders.proto new file mode 100644 index 0000000000..cf9c5987d5 --- /dev/null +++ b/proto/provenance/exchange/v1/orders.proto @@ -0,0 +1,65 @@ +syntax = "proto3"; +package provenance.exchange.v1; + +option go_package = "github.com/provenance-io/provenance/x/exchange"; + +option java_package = "io.provenance.exchange.v1"; +option java_multiple_files = true; + +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; + +// Order associates an order id with one of the order types. +message Order { + // order_id is the numerical identifier for this order. + uint64 order_id = 1; + // order is the specifics of this order. + oneof order { + // ask_order is the information about this order if it represents an ask order. + AskOrder ask_order = 2; + // bid_order is the information about this order if it represents a bid order. + BidOrder bid_order = 3; + } +} + +// AskOrder represents someone's desire to sell something at a minimum price. +message AskOrder { + // market_id identifies the market that this order belongs to. + uint32 market_id = 1; + // seller is the address of the account that owns this order and has the assets to sell. + string seller = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // assets are the things that the seller wishes to sell. A hold is placed on this until the order is filled or cancelled. + repeated cosmos.base.v1beta1.Coin assets = 3 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // price is the minimum amount that the seller is willing to accept for the assets. The seller's settlement proportional fee (and possibly the settlement flat fee) is taken out of the amount the seller receives, so it's possible that the seller will still receive less than this price. + cosmos.base.v1beta1.Coin price = 4; + // seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. If this denom is the same denom as the price, it will come out of the actual price received. If this denom is different, the amount must be in the seller's account and a hold is placed on it until the order is filled or cancelled. + cosmos.base.v1beta1.Coin seller_settlement_flat_fee = 5; + // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the order must be either filled in full or not filled at all. + bool allow_partial = 6; +} + +// BidOrder represents someone's desire to buy something at a specific price. +message BidOrder { + // market_id identifies the market that this order belongs to. + uint32 market_id = 1; + // buyer is the address of the account that owns this order and has the price to spend. + string buyer = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // assets are the things that the buyer wishes to buy. + repeated cosmos.base.v1beta1.Coin assets = 3 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // price is the amount that the buyer will pay for the assets. A hold is placed on this until the order is filled or cancelled. + cosmos.base.v1beta1.Coin price = 4; + // buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay when the order is settled (in addition to the price). A hold is placed on this until the order is filled or cancelled. + repeated cosmos.base.v1beta1.Coin buyer_settlement_fees = 5 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the order must be either filled in full or not filled at all. + bool allow_partial = 6; +} \ No newline at end of file diff --git a/proto/provenance/exchange/v1/params.proto b/proto/provenance/exchange/v1/params.proto new file mode 100644 index 0000000000..e3bfa2d883 --- /dev/null +++ b/proto/provenance/exchange/v1/params.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package provenance.exchange.v1; + +option go_package = "github.com/provenance-io/provenance/x/exchange"; + +option java_package = "io.provenance.exchange.v1"; +option java_multiple_files = true; + +import "gogoproto/gogo.proto"; + +// Params is a representation of the exchange module parameters. +message Params { + // default_split is the default proportion of fees the exchange receives in basis points. E.g. 100 = 1%. Min = 0, Max = 10000. It is used if there isn't an applicable denom-specific split defined. + uint32 default_split = 1; + // denom_splits are the denom-specific amounts the exchange receives. + repeated DenomSplit denom_splits = 2; +} + +// DenomSplit associates a coin denomination with an amount the exchange receives for that denom. +message DenomSplit { + // denom is the coin denomination this split applies to. + string denom = 1; + // split is the proportion of fees the exchange receives for this denom in basis points. E.g. 100 = 1%. Min = 0, Max = 10000. + uint32 split = 2; +} \ No newline at end of file diff --git a/proto/provenance/exchange/v1/qenesis.proto b/proto/provenance/exchange/v1/qenesis.proto new file mode 100644 index 0000000000..8a5591d5fe --- /dev/null +++ b/proto/provenance/exchange/v1/qenesis.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package provenance.exchange.v1; + +option go_package = "github.com/provenance-io/provenance/x/exchange"; + +option java_package = "io.provenance.exchange.v1"; +option java_multiple_files = true; + +import "gogoproto/gogo.proto"; +import "provenance/exchange/v1/market.proto"; +import "provenance/exchange/v1/orders.proto"; +import "provenance/exchange/v1/params.proto"; + +// GenesisState is the data that should be loaded into the exchange module during genesis. +message GenesisState { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // params defines all the parameters of the exchange module. + Params params = 1 [(gogoproto.nullable) = false]; + + // markets are all of the markets to create at genesis. + repeated Market markets = 2; + + // orders are all the orders to create at genesis. + repeated Order orders = 3; +} \ No newline at end of file diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto new file mode 100644 index 0000000000..50f18573cc --- /dev/null +++ b/proto/provenance/exchange/v1/query.proto @@ -0,0 +1,127 @@ +syntax = "proto3"; +package provenance.exchange.v1; + +option go_package = "github.com/provenance-io/provenance/x/exchange"; + +option java_package = "io.provenance.exchange.v1"; +option java_multiple_files = true; + +import "google/api/annotations.proto"; + +// Query is the service for exchange module's query endpoints. +service Query { + // QueryOrderFeeCalc calculates the fees that will be associated with the provided order. + rpc QueryOrderFeeCalc(QueryOrderFeeCalcRequest) returns (QueryOrderFeeCalcResponse) { + option (google.api.http).get = "/provenance/exchange/v1/fees/order"; + } + + // QuerySettlementFeeCalc calculates the fees that will be associated with the provided settlement. + rpc QuerySettlementFeeCalc(QuerySettlementFeeCalcRequest) returns (QuerySettlementFeeCalcResponse) { + option (google.api.http).get = "/provenance/exchange/v1/fees/settlement"; + } + + // QueryGetOrder looks up an order by id. + rpc QueryGetOrder(QueryGetOrderRequest) returns (QueryGetOrderResponse) { + option (google.api.http).get = "/provenance/exchange/v1/order/{order_id}"; + } + + // QueryGetMarketOrders looks up the orders in a market. + rpc QueryGetMarketOrders(QueryGetMarketOrdersRequest) returns (QueryGetMarketOrdersResponse) { + option (google.api.http).get = "/provenance/exchange/v1/market/{market_id}/orders"; + } + + // QueryGetAddressOrders looks up the orders from the provided address. + rpc QueryGetAddressOrders(QueryGetAddressOrdersRequest) returns (QueryGetAddressOrdersResponse) { + option (google.api.http).get = "/provenance/exchange/v1/orders/{address}"; + } + + // QueryGetAllOrders gets all orders in the exchange module. + rpc QueryGetAllOrders(QueryGetAllOrdersRequest) returns (QueryGetAllOrdersResponse) { + option (google.api.http).get = "/provenance/exchange/v1/orders"; + } + + // QueryMarketInfo returns the information/details about a market. + rpc QueryMarketInfo(QueryMarketInfoRequest) returns (QueryMarketInfoResponse) { + option (google.api.http) = { + get: "/provenance/exchange/v1/market/info" + additional_bindings: {get: "/provenance/exchange/v1/market/details"} + }; + } + + // QueryParams returns the exchange module parameters. + rpc QueryParams(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/provenance/exchange/v1/params"; + } +} + +// QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint. +message QueryOrderFeeCalcRequest { + // TODO[1658]: QueryOrderFeeCalcRequest +} +// QueryOrderFeeCalcResponse is a response message for the QueryOrderFeeCalc endpoint. +message QueryOrderFeeCalcResponse { + // TODO[1658]: QueryOrderFeeCalcResponse +} + +// QuerySettlementFeeCalcRequest is a request message for the QuerySettlementFeeCalc endpoint. +message QuerySettlementFeeCalcRequest { + // TODO[1658]: QuerySettlementFeeCalcRequest +} +// QuerySettlementFeeCalcResponse is a response message for the QuerySettlementFeeCalc endpoint. +message QuerySettlementFeeCalcResponse { + // TODO[1658]: QuerySettlementFeeCalcResponse +} + +// QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. +message QueryGetOrderRequest { + // TODO[1658]: QueryGetOrderRequest +} +// QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. +message QueryGetOrderResponse { + // TODO[1658]: QueryGetOrderResponse +} + +// QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. +message QueryGetMarketOrdersRequest { + // TODO[1658]: QueryGetMarketOrdersRequest +} +// QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders endpoint. +message QueryGetMarketOrdersResponse { + // TODO[1658]: QueryGetMarketOrdersResponse +} + +// QueryGetAddressOrdersRequest is a request message for the QueryGetAddressOrders endpoint. +message QueryGetAddressOrdersRequest { + // TODO[1658]: QueryGetAddressOrdersRequest +} +// QueryGetAddressOrdersResponse is a response message for the QueryGetAddressOrders endpoint. +message QueryGetAddressOrdersResponse { + // TODO[1658]: QueryGetAddressOrdersResponse +} + +// QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint. +message QueryGetAllOrdersRequest { + // TODO[1658]: QueryGetAllOrdersRequest +} +// QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoint. +message QueryGetAllOrdersResponse { + // TODO[1658]: QueryGetAllOrdersResponse +} + +// QueryMarketInfoRequest is a request message for the QueryMarketInfo endpoint. +message QueryMarketInfoRequest { + // TODO[1658]: QueryMarketInfoRequest +} +// QueryMarketInfoResponse is a response message for the QueryMarketInfo endpoint. +message QueryMarketInfoResponse { + // TODO[1658]: QueryMarketInfoResponse +} + +// QueryParamsRequest is a request message for the QueryParams endpoint. +message QueryParamsRequest { + // TODO[1658]: QueryParamsRequest +} +// QueryParamsResponse is a response message for the QueryParams endpoint. +message QueryParamsResponse { + // TODO[1658]: QueryParamsResponse +} \ No newline at end of file diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto new file mode 100644 index 0000000000..9636e5ec1b --- /dev/null +++ b/proto/provenance/exchange/v1/tx.proto @@ -0,0 +1,191 @@ +syntax = "proto3"; +package provenance.exchange.v1; + +option go_package = "github.com/provenance-io/provenance/x/exchange"; + +option java_package = "io.provenance.exchange.v1"; +option java_multiple_files = true; + +import "cosmos_proto/cosmos.proto"; + +// Msg is the service for exchange module's tx endpoints. +service Msg { + // CreateAsk creates an ask order (to sell something you own). + rpc CreateAsk(MsgCreateAskRequest) returns (MsgCreateAskResponse); + + // CreateBid creates an bid order (to buy something you want). + rpc CreateBid(MsgCreateBidRequest) returns (MsgCreateBidResponse); + + // CancelOrder cancels an order. + rpc CancelOrder(MsgCancelOrderRequest) returns (MsgCancelOrderResponse); + + // FillBids uses the assets in your account to fulfill one or more bids (similar to a fill-or-cancel ask). + rpc FillBids(MsgFillBidsRequest) returns (MsgFillBidsResponse); + + // FillAsks uses the funds in your account to fulfill one or more asks (similar to a fill-or-cancel bid). + rpc FillAsks(MsgFillAsksRequest) returns (MsgFillAsksResponse); + + // MarketSettle is a market endpoint to trigger the settlement of orders. + rpc MarketSettle(MsgMarketSettleRequest) returns (MsgMarketSettleResponse); + + // MarketWithdraw is a market endpoint to withdraw fees that have been collected. + rpc MarketWithdraw(MsgMarketWithdrawRequest) returns (MsgMarketWithdrawResponse); + + // MarketUpdateDetails is a market endpoint to update its details. + rpc MarketUpdateDetails(MsgMarketUpdateDetailsRequest) returns (MsgMarketUpdateDetailsResponse); + + // MarketUpdateEnabled is a market endpoint to update whether its accepting orders. + rpc MarketUpdateEnabled(MsgMarketUpdateEnabledRequest) returns (MsgMarketUpdateEnabledResponse); + + // MarketUpdateUserSettle is a market endpoint to update whether it allows user-initiated settlement. + rpc MarketUpdateUserSettle(MsgMarketUpdateUserSettleRequest) returns (MsgMarketUpdateUserSettleResponse); + + // MarketManagePermissions is a market endpoint to manage a market's user permissions. + rpc MarketManagePermissions(MsgMarketManagePermissionsRequest) returns (MsgMarketManagePermissionsResponse); + + // MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. + rpc MarketManageReqAttrs(MsgMarketManageReqAttrsRequest) returns (MsgMarketManageReqAttrsResponse); + + // CreateMarket reserves the next market id and submits a GovCreateMarket governance proposal to create the market. + rpc CreateMarket(MsgCreateMarketRequest) returns (MsgCreateMarketResponse); + + // GovCreateMarket is a governance proposal endpoint for creating a market. + // The CreateMarket endpoint should be used to submit one of these unless you don't need to know the market id until after the proposal passes. + rpc GovCreateMarket(MsgGovCreateMarketRequest) returns (MsgGovCreateMarketResponse); + + // GovManageFees is a governance proposal endpoint for updating a market's fees. + rpc GovManageFees(MsgGovManageFeesRequest) returns (MsgGovManageFeesResponse); + + // GovUpdateParams is a governance proposal endpoint for updating the exchange module's params. + rpc GovUpdateParams(MsgGovUpdateParamsRequest) returns (MsgGovUpdateParamsResponse); +} + +// MsgCreateAskRequest is a request message for the CreateAsk endpoint. +message MsgCreateAskRequest { + // TODO[1658]: MsgCreateAskRequest +} + +// MsgCreateAskResponse is a response message for the CreateAsk endpoint. +message MsgCreateAskResponse {} + +// MsgCreateBidRequest is a request message for the CreateBid endpoint. +message MsgCreateBidRequest { + // TODO[1658]: MsgCreateBidRequest +} + +// MsgCreateBidResponse is a response message for the CreateBid endpoint. +message MsgCreateBidResponse {} + +// MsgCancelOrderRequest is a request message for the CancelOrder endpoint. +message MsgCancelOrderRequest { + // TODO[1658]: MsgCancelOrderRequest +} + +// MsgCancelOrderResponse is a response message for the CancelOrder endpoint. +message MsgCancelOrderResponse {} + +// MsgFillBidsRequest is a request message for the FillBids endpoint. +message MsgFillBidsRequest { + // TODO[1658]: MsgFillBidsRequest +} + +// MsgFillBidsResponse is a response message for the FillBids endpoint. +message MsgFillBidsResponse {} + +// MsgFillAsksRequest is a request message for the FillAsks endpoint. +message MsgFillAsksRequest { + // TODO[1658]: MsgFillAsksRequest +} + +// MsgFillAsksResponse is a response message for the FillAsks endpoint. +message MsgFillAsksResponse {} + +// MsgMarketSettleRequest is a request message for the MarketSettle endpoint. +message MsgMarketSettleRequest { + // TODO[1658]: MsgMarketSettleRequest +} + +// MsgMarketSettleResponse is a response message for the MarketSettle endpoint. +message MsgMarketSettleResponse {} + +// MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. +message MsgMarketWithdrawRequest { + // TODO[1658]: MsgMarketWithdrawRequest +} + +// MsgMarketWithdrawResponse is a response message for the MarketWithdraw endpoint. +message MsgMarketWithdrawResponse {} + +// MsgMarketUpdateDetailsRequest is a request message for the MarketUpdateDetails endpoint. +message MsgMarketUpdateDetailsRequest { + // TODO[1658]: MsgMarketUpdateDetailsRequest +} + +// MsgMarketUpdateDetailsResponse is a response message for the MarketUpdateDetails endpoint. +message MsgMarketUpdateDetailsResponse {} + +// MsgMarketUpdateEnabledRequest is a request message for the MarketUpdateEnabled endpoint. +message MsgMarketUpdateEnabledRequest { + // TODO[1658]: MsgMarketUpdateEnabledRequest +} + +// MsgMarketUpdateEnabledResponse is a response message for the MarketUpdateEnabled endpoint. +message MsgMarketUpdateEnabledResponse {} + +// MsgMarketUpdateUserSettleRequest is a request message for the MarketUpdateUserSettle endpoint. +message MsgMarketUpdateUserSettleRequest { + // TODO[1658]: MsgMarketUpdateUserSettleRequest +} + +// MsgMarketUpdateUserSettleResponse is a response message for the MarketUpdateUserSettle endpoint. +message MsgMarketUpdateUserSettleResponse {} + +// MsgMarketManagePermissionsRequest is a request message for the MarketManagePermissions endpoint. +message MsgMarketManagePermissionsRequest { + // TODO[1658]: MsgMarketManagePermissionsRequest +} + +// MsgMarketManagePermissionsResponse is a response message for the MarketManagePermissions endpoint. +message MsgMarketManagePermissionsResponse {} + +// MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs endpoint. +message MsgMarketManageReqAttrsRequest { + // TODO[1658]: MsgMarketManageReqAttrsRequest +} + +// MsgMarketManageReqAttrsResponse is a response message for the MarketManageReqAttrs endpoint. +message MsgMarketManageReqAttrsResponse {} + +//rpc CreateMarket(MsgCreateMarketRequest) returns (MsgCreateMarketResponse); + +// MsgCreateMarketRequest is a request message for the CreateMarket endpoint. +message MsgCreateMarketRequest { + // TODO[1658]: MsgCreateMarketRequest +} + +// MsgCreateMarketResponse is a response message for the CreateMarket endpoint. +message MsgCreateMarketResponse {} + +// MsgGovCreateMarketRequest is a request message for the GovCreateMarket endpoint. +message MsgGovCreateMarketRequest { + // TODO[1658]: MsgGovCreateMarketRequest +} + +// MsgGovCreateMarketResponse is a response message for the GovCreateMarket endpoint. +message MsgGovCreateMarketResponse {} + +// MsgGovManageFeesRequest is a request message for the GovManageFees endpoint. +message MsgGovManageFeesRequest { + // TODO[1658]: MsgGovManageFeesRequest +} + +// MsgGovManageFeesResponse is a response message for the GovManageFees endpoint. +message MsgGovManageFeesResponse {} + +// MsgGovUpdateParamsRequest is a request message for the GovUpdateParams endpoint. +message MsgGovUpdateParamsRequest { + // TODO[1658]: MsgGovUpdateParamsRequest +} + +// MsgGovUpdateParamsResponse is a response message for the GovUpdateParams endpoint. +message MsgGovUpdateParamsResponse {} From 1f1f9a90a4d55260f45cc8af0c357d26b5c1227c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 23 Aug 2023 13:17:37 -0600 Subject: [PATCH 002/309] [1658]: proto-format and fix proto-lint errors. --- proto/provenance/exchange/v1/events.proto | 27 +++++++------- proto/provenance/exchange/v1/market.proto | 44 ++++++++++------------- proto/provenance/exchange/v1/orders.proto | 41 +++++++++++---------- proto/provenance/exchange/v1/params.proto | 9 ++--- proto/provenance/exchange/v1/tx.proto | 7 ++-- 5 files changed, 60 insertions(+), 68 deletions(-) diff --git a/proto/provenance/exchange/v1/events.proto b/proto/provenance/exchange/v1/events.proto index 03902dbd5b..6a9e08b613 100644 --- a/proto/provenance/exchange/v1/events.proto +++ b/proto/provenance/exchange/v1/events.proto @@ -10,7 +10,8 @@ import "cosmos/base/v1beta1/coin.proto"; import "cosmos_proto/cosmos.proto"; import "gogoproto/gogo.proto"; -// TODO[1658]: Examine the difference between EventOrderPartiallyFilled and EventOrderPartiallyFilled2 to see if it's okay to have a Coins field instead of a string. Then remove one of them. +// TODO[1658]: Examine the difference between EventOrderPartiallyFilled and EventOrderPartiallyFilled2 to see if it's +// okay to have a Coins field instead of a string. Then remove one of them. // EventOrderCreated is an event emitted when an order is created. message EventOrderCreated { @@ -28,7 +29,8 @@ message EventOrderCancelled { string cancelled_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; } -// EventOrderFilled is an event emitted when an order has been filled in full. This event is also used for orders that were previously partially filled, but have now been filled in full. +// EventOrderFilled is an event emitted when an order has been filled in full. +// This event is also used for orders that were previously partially filled, but have now been filled in full. message EventOrderFilled { // order_id is the numerical identifier of the order created. uint64 order_id = 1; @@ -39,15 +41,11 @@ message EventOrderPartiallyFilled { // order_id is the numerical identifier of the order created. uint64 order_id = 1; // amount_filled is the amount of assets that were filled (and removed from the order). - repeated cosmos.base.v1beta1.Coin assets_filled = 2 [ - (gogoproto.nullable) = false, - (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" - ]; + repeated cosmos.base.v1beta1.Coin assets_filled = 2 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; // fees_filled is the amount of fees removed from the order. - repeated cosmos.base.v1beta1.Coin fees_filled = 3 [ - (gogoproto.nullable) = false, - (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" - ]; + repeated cosmos.base.v1beta1.Coin fees_filled = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; } // EventOrderPartiallyFilled2 is an event emitted when an order filled in part and still has more left to fill. @@ -65,10 +63,8 @@ message EventMarketWithdraw { // market_id is the numerical identifier of the market. uint32 market_id = 1; // amount_withdrawn is the amount of funds withdrawn from the market account. - repeated cosmos.base.v1beta1.Coin amount_withdrawn = 2 [ - (gogoproto.nullable) = false, - (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" - ]; + repeated cosmos.base.v1beta1.Coin amount_withdrawn = 2 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; // destination is the account that received the funds. string destination = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // withdrawn_by is the account that requested the withdrawal. @@ -123,7 +119,8 @@ message EventMarketReqAttrUpdated { string updated_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; } -// EventCreateMarketSubmitted is an event emitted during CreateMarket indicating that a governance proposal was submitted to create a market. +// EventCreateMarketSubmitted is an event emitted during CreateMarket indicating that a governance +// proposal was submitted to create a market. message EventCreateMarketSubmitted { // market_id is the numerical identifier of the market. uint32 market_id = 1; diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index 221ac2657b..ff39fc0800 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -15,12 +15,12 @@ import "gogoproto/gogo.proto"; // MarketAccount is an account type for use with the accounts module to hold some basic information about a market. message MarketAccount { - option (gogoproto.goproto_getters) = false; - option (gogoproto.goproto_stringer) = false; + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; // base_account is the base cosmos account information. cosmos.auth.v1beta1.BaseAccount base_account = 1 - [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"base_account\""]; + [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"base_account\""]; // market_id is the numerical identifier for this market. uint32 market_id = 2; @@ -49,35 +49,29 @@ message Market { // fee_create_ask_flat is the flat fee charged for creating an ask order. // Each coin entry is a separate option. When an ask is created, one of these must be paid. // If empty, no fee is required to create an ask order. - repeated cosmos.base.v1beta1.Coin fee_create_ask_flat = 3 [ - (gogoproto.nullable) = false, - (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" - ]; + repeated cosmos.base.v1beta1.Coin fee_create_ask_flat = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; // fee_create_bid_flat is the flat fee charged for creating a bid order. // Each coin entry is a separate option. When a bid is created, one of these must be paid. // If empty, no fee is required to create a bid order. - repeated cosmos.base.v1beta1.Coin fee_create_bid_flat = 4 [ - (gogoproto.nullable) = false, - (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" - ]; + repeated cosmos.base.v1beta1.Coin fee_create_bid_flat = 4 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; // fee_settlement_seller_flat is the flat fee charged to the seller during settlement. - // Each coin entry is a separate option. When an ask is settled, the seller will pay the amount in the denom that matches the price they received. - repeated cosmos.base.v1beta1.Coin fee_settlement_seller_flat = 5 [ - (gogoproto.nullable) = false, - (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" - ]; + // Each coin entry is a separate option. + // When an ask is settled, the seller will pay the amount in the denom that matches the price they received. + repeated cosmos.base.v1beta1.Coin fee_settlement_seller_flat = 5 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; // fee_settlement_seller_ratios is the fee to charge a seller during settlement based on the price they are receiving. // The price and fee denoms must be equal for each entry, and only one entry for any given denom is allowed. repeated FeeRatio fee_settlement_seller_ratios = 6; // fee_settlement_buyer_flat is the flat fee charged to the buyer during settlement. - // Each coin entry is a separate option. When a bid is created, the settlement fees provided must contain one of these. - repeated cosmos.base.v1beta1.Coin fee_settlement_buyer_flat = 7 [ - (gogoproto.nullable) = false, - (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" - ]; + // Each coin entry is a separate option. + // When a bid is created, the settlement fees provided must contain one of these. + repeated cosmos.base.v1beta1.Coin fee_settlement_buyer_flat = 7 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; // fee_settlement_buyer_ratios is the fee to charge a buyer during settlement based on the price they are spending. - // The price and fee denoms do not have to equal. - // Multiple entries for any given price or fee denom are allowed, but each price denom to fee denom pair can only have one entry. + // The price and fee denoms do not have to equal. Multiple entries for any given price or fee denom are allowed, but + // each price denom to fee denom pair can only have one entry. repeated FeeRatio fee_settlement_buyer_ratios = 8; // accepting_orders is whether this market is allowing orders to be created for it. bool accepting_orders = 9; @@ -87,10 +81,10 @@ message Market { bool allow_user_settlement = 10; // access_grants is the list of addresses and permissions granted for this market. repeated AccessGrant access_grants = 11; - // req_attr_create_ask is a list of attributes required on an account for that account to be allowed to create an ask order. + // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. // TODO[1658]: Add comment about wildcards. repeated string req_attr_create_ask = 12; - // req_attr_create_ask is a list of attributes required on an account for that account to be allowed to create an ask order. + // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. // TODO[1658]: Add comment about wildcards. repeated string req_attr_create_bid = 13; } diff --git a/proto/provenance/exchange/v1/orders.proto b/proto/provenance/exchange/v1/orders.proto index cf9c5987d5..6fe3512d9f 100644 --- a/proto/provenance/exchange/v1/orders.proto +++ b/proto/provenance/exchange/v1/orders.proto @@ -29,16 +29,20 @@ message AskOrder { uint32 market_id = 1; // seller is the address of the account that owns this order and has the assets to sell. string seller = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - // assets are the things that the seller wishes to sell. A hold is placed on this until the order is filled or cancelled. - repeated cosmos.base.v1beta1.Coin assets = 3 [ - (gogoproto.nullable) = false, - (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" - ]; - // price is the minimum amount that the seller is willing to accept for the assets. The seller's settlement proportional fee (and possibly the settlement flat fee) is taken out of the amount the seller receives, so it's possible that the seller will still receive less than this price. + // assets are the things that the seller wishes to sell. + // A hold is placed on this until the order is filled or cancelled. + repeated cosmos.base.v1beta1.Coin assets = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + // price is the minimum amount that the seller is willing to accept for the assets. The seller's settlement + // proportional fee (and possibly the settlement flat fee) is taken out of the amount the seller receives, + // so it's possible that the seller will still receive less than this price. cosmos.base.v1beta1.Coin price = 4; - // seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. If this denom is the same denom as the price, it will come out of the actual price received. If this denom is different, the amount must be in the seller's account and a hold is placed on it until the order is filled or cancelled. + // seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. If this denom is the + // same denom as the price, it will come out of the actual price received. If this denom is different, the amount must + // be in the seller's account and a hold is placed on it until the order is filled or cancelled. cosmos.base.v1beta1.Coin seller_settlement_flat_fee = 5; - // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the order must be either filled in full or not filled at all. + // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the + // order must be either filled in full or not filled at all. bool allow_partial = 6; } @@ -49,17 +53,16 @@ message BidOrder { // buyer is the address of the account that owns this order and has the price to spend. string buyer = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // assets are the things that the buyer wishes to buy. - repeated cosmos.base.v1beta1.Coin assets = 3 [ - (gogoproto.nullable) = false, - (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" - ]; - // price is the amount that the buyer will pay for the assets. A hold is placed on this until the order is filled or cancelled. + repeated cosmos.base.v1beta1.Coin assets = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + // price is the amount that the buyer will pay for the assets. + // A hold is placed on this until the order is filled or cancelled. cosmos.base.v1beta1.Coin price = 4; - // buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay when the order is settled (in addition to the price). A hold is placed on this until the order is filled or cancelled. - repeated cosmos.base.v1beta1.Coin buyer_settlement_fees = 5 [ - (gogoproto.nullable) = false, - (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" - ]; - // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the order must be either filled in full or not filled at all. + // buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) + // when the order is settled. A hold is placed on this until the order is filled or cancelled. + repeated cosmos.base.v1beta1.Coin buyer_settlement_fees = 5 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the + // order must be either filled in full or not filled at all. bool allow_partial = 6; } \ No newline at end of file diff --git a/proto/provenance/exchange/v1/params.proto b/proto/provenance/exchange/v1/params.proto index e3bfa2d883..f8952cb6d7 100644 --- a/proto/provenance/exchange/v1/params.proto +++ b/proto/provenance/exchange/v1/params.proto @@ -6,11 +6,11 @@ option go_package = "github.com/provenance-io/provenance/x/exchange"; option java_package = "io.provenance.exchange.v1"; option java_multiple_files = true; -import "gogoproto/gogo.proto"; - // Params is a representation of the exchange module parameters. message Params { - // default_split is the default proportion of fees the exchange receives in basis points. E.g. 100 = 1%. Min = 0, Max = 10000. It is used if there isn't an applicable denom-specific split defined. + // default_split is the default proportion of fees the exchange receives in basis points. + // It is used if there isn't an applicable denom-specific split defined. + // E.g. 100 = 1%. Min = 0, Max = 10000. uint32 default_split = 1; // denom_splits are the denom-specific amounts the exchange receives. repeated DenomSplit denom_splits = 2; @@ -20,6 +20,7 @@ message Params { message DenomSplit { // denom is the coin denomination this split applies to. string denom = 1; - // split is the proportion of fees the exchange receives for this denom in basis points. E.g. 100 = 1%. Min = 0, Max = 10000. + // split is the proportion of fees the exchange receives for this denom in basis points. + // E.g. 100 = 1%. Min = 0, Max = 10000. uint32 split = 2; } \ No newline at end of file diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 9636e5ec1b..3dff5c8346 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -6,8 +6,6 @@ option go_package = "github.com/provenance-io/provenance/x/exchange"; option java_package = "io.provenance.exchange.v1"; option java_multiple_files = true; -import "cosmos_proto/cosmos.proto"; - // Msg is the service for exchange module's tx endpoints. service Msg { // CreateAsk creates an ask order (to sell something you own). @@ -50,7 +48,8 @@ service Msg { rpc CreateMarket(MsgCreateMarketRequest) returns (MsgCreateMarketResponse); // GovCreateMarket is a governance proposal endpoint for creating a market. - // The CreateMarket endpoint should be used to submit one of these unless you don't need to know the market id until after the proposal passes. + // The CreateMarket endpoint should be used to submit one of these unless you don't need to know the market id until + // after the proposal passes. rpc GovCreateMarket(MsgGovCreateMarketRequest) returns (MsgGovCreateMarketResponse); // GovManageFees is a governance proposal endpoint for updating a market's fees. @@ -156,8 +155,6 @@ message MsgMarketManageReqAttrsRequest { // MsgMarketManageReqAttrsResponse is a response message for the MarketManageReqAttrs endpoint. message MsgMarketManageReqAttrsResponse {} -//rpc CreateMarket(MsgCreateMarketRequest) returns (MsgCreateMarketResponse); - // MsgCreateMarketRequest is a request message for the CreateMarket endpoint. message MsgCreateMarketRequest { // TODO[1658]: MsgCreateMarketRequest From cc95da995912ad5888a255e2ae8d79449cfff076 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 23 Aug 2023 13:21:18 -0600 Subject: [PATCH 003/309] [1658]: Fill the query requests enough for proto-gen to work. --- proto/provenance/exchange/v1/query.proto | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 50f18573cc..5a0977705c 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -6,6 +6,7 @@ option go_package = "github.com/provenance-io/provenance/x/exchange"; option java_package = "io.provenance.exchange.v1"; option java_multiple_files = true; +import "cosmos_proto/cosmos.proto"; import "google/api/annotations.proto"; // Query is the service for exchange module's query endpoints. @@ -74,6 +75,7 @@ message QuerySettlementFeeCalcResponse { // QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. message QueryGetOrderRequest { + uint64 order_id = 1; // TODO[1658]: QueryGetOrderRequest } // QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. @@ -83,6 +85,7 @@ message QueryGetOrderResponse { // QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. message QueryGetMarketOrdersRequest { + uint32 market_id = 1; // TODO[1658]: QueryGetMarketOrdersRequest } // QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders endpoint. @@ -92,6 +95,7 @@ message QueryGetMarketOrdersResponse { // QueryGetAddressOrdersRequest is a request message for the QueryGetAddressOrders endpoint. message QueryGetAddressOrdersRequest { + string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // TODO[1658]: QueryGetAddressOrdersRequest } // QueryGetAddressOrdersResponse is a response message for the QueryGetAddressOrders endpoint. From 7ff7556fc41ab071e1d56100053caf6430c7ade4 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 23 Aug 2023 13:21:37 -0600 Subject: [PATCH 004/309] [1658]: make proto-gen --- docs/proto-docs.md | 1216 ++++++++++ x/exchange/events.pb.go | 3547 ++++++++++++++++++++++++++++ x/exchange/market.pb.go | 2210 ++++++++++++++++++ x/exchange/orders.pb.go | 1409 +++++++++++ x/exchange/params.pb.go | 581 +++++ x/exchange/qenesis.pb.go | 433 ++++ x/exchange/query.pb.go | 2575 ++++++++++++++++++++ x/exchange/query.pb.gw.go | 752 ++++++ x/exchange/tx.pb.go | 4679 +++++++++++++++++++++++++++++++++++++ 9 files changed, 17402 insertions(+) create mode 100644 x/exchange/events.pb.go create mode 100644 x/exchange/market.pb.go create mode 100644 x/exchange/orders.pb.go create mode 100644 x/exchange/params.pb.go create mode 100644 x/exchange/qenesis.pb.go create mode 100644 x/exchange/query.pb.go create mode 100644 x/exchange/query.pb.gw.go create mode 100644 x/exchange/tx.pb.go diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 035a4ff511..e473d8fb33 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -52,6 +52,101 @@ - [Msg](#provenance.attribute.v1.Msg) +- [provenance/exchange/v1/events.proto](#provenance/exchange/v1/events.proto) + - [EventCreateMarketSubmitted](#provenance.exchange.v1.EventCreateMarketSubmitted) + - [EventMarketCreated](#provenance.exchange.v1.EventMarketCreated) + - [EventMarketDetailsUpdated](#provenance.exchange.v1.EventMarketDetailsUpdated) + - [EventMarketDisabled](#provenance.exchange.v1.EventMarketDisabled) + - [EventMarketEnabled](#provenance.exchange.v1.EventMarketEnabled) + - [EventMarketFeesUpdated](#provenance.exchange.v1.EventMarketFeesUpdated) + - [EventMarketPermissionsUpdated](#provenance.exchange.v1.EventMarketPermissionsUpdated) + - [EventMarketReqAttrUpdated](#provenance.exchange.v1.EventMarketReqAttrUpdated) + - [EventMarketUserSettleUpdated](#provenance.exchange.v1.EventMarketUserSettleUpdated) + - [EventMarketWithdraw](#provenance.exchange.v1.EventMarketWithdraw) + - [EventOrderCancelled](#provenance.exchange.v1.EventOrderCancelled) + - [EventOrderCreated](#provenance.exchange.v1.EventOrderCreated) + - [EventOrderFilled](#provenance.exchange.v1.EventOrderFilled) + - [EventOrderPartiallyFilled](#provenance.exchange.v1.EventOrderPartiallyFilled) + - [EventOrderPartiallyFilled2](#provenance.exchange.v1.EventOrderPartiallyFilled2) + - [EventParamsUpdated](#provenance.exchange.v1.EventParamsUpdated) + +- [provenance/exchange/v1/market.proto](#provenance/exchange/v1/market.proto) + - [AccessGrant](#provenance.exchange.v1.AccessGrant) + - [FeeRatio](#provenance.exchange.v1.FeeRatio) + - [Market](#provenance.exchange.v1.Market) + - [MarketAccount](#provenance.exchange.v1.MarketAccount) + - [MarketDetails](#provenance.exchange.v1.MarketDetails) + + - [Permission](#provenance.exchange.v1.Permission) + +- [provenance/exchange/v1/orders.proto](#provenance/exchange/v1/orders.proto) + - [AskOrder](#provenance.exchange.v1.AskOrder) + - [BidOrder](#provenance.exchange.v1.BidOrder) + - [Order](#provenance.exchange.v1.Order) + +- [provenance/exchange/v1/params.proto](#provenance/exchange/v1/params.proto) + - [DenomSplit](#provenance.exchange.v1.DenomSplit) + - [Params](#provenance.exchange.v1.Params) + +- [provenance/exchange/v1/qenesis.proto](#provenance/exchange/v1/qenesis.proto) + - [GenesisState](#provenance.exchange.v1.GenesisState) + +- [provenance/exchange/v1/query.proto](#provenance/exchange/v1/query.proto) + - [QueryGetAddressOrdersRequest](#provenance.exchange.v1.QueryGetAddressOrdersRequest) + - [QueryGetAddressOrdersResponse](#provenance.exchange.v1.QueryGetAddressOrdersResponse) + - [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) + - [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) + - [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) + - [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) + - [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) + - [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) + - [QueryMarketInfoRequest](#provenance.exchange.v1.QueryMarketInfoRequest) + - [QueryMarketInfoResponse](#provenance.exchange.v1.QueryMarketInfoResponse) + - [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) + - [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) + - [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) + - [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) + - [QuerySettlementFeeCalcRequest](#provenance.exchange.v1.QuerySettlementFeeCalcRequest) + - [QuerySettlementFeeCalcResponse](#provenance.exchange.v1.QuerySettlementFeeCalcResponse) + + - [Query](#provenance.exchange.v1.Query) + +- [provenance/exchange/v1/tx.proto](#provenance/exchange/v1/tx.proto) + - [MsgCancelOrderRequest](#provenance.exchange.v1.MsgCancelOrderRequest) + - [MsgCancelOrderResponse](#provenance.exchange.v1.MsgCancelOrderResponse) + - [MsgCreateAskRequest](#provenance.exchange.v1.MsgCreateAskRequest) + - [MsgCreateAskResponse](#provenance.exchange.v1.MsgCreateAskResponse) + - [MsgCreateBidRequest](#provenance.exchange.v1.MsgCreateBidRequest) + - [MsgCreateBidResponse](#provenance.exchange.v1.MsgCreateBidResponse) + - [MsgCreateMarketRequest](#provenance.exchange.v1.MsgCreateMarketRequest) + - [MsgCreateMarketResponse](#provenance.exchange.v1.MsgCreateMarketResponse) + - [MsgFillAsksRequest](#provenance.exchange.v1.MsgFillAsksRequest) + - [MsgFillAsksResponse](#provenance.exchange.v1.MsgFillAsksResponse) + - [MsgFillBidsRequest](#provenance.exchange.v1.MsgFillBidsRequest) + - [MsgFillBidsResponse](#provenance.exchange.v1.MsgFillBidsResponse) + - [MsgGovCreateMarketRequest](#provenance.exchange.v1.MsgGovCreateMarketRequest) + - [MsgGovCreateMarketResponse](#provenance.exchange.v1.MsgGovCreateMarketResponse) + - [MsgGovManageFeesRequest](#provenance.exchange.v1.MsgGovManageFeesRequest) + - [MsgGovManageFeesResponse](#provenance.exchange.v1.MsgGovManageFeesResponse) + - [MsgGovUpdateParamsRequest](#provenance.exchange.v1.MsgGovUpdateParamsRequest) + - [MsgGovUpdateParamsResponse](#provenance.exchange.v1.MsgGovUpdateParamsResponse) + - [MsgMarketManagePermissionsRequest](#provenance.exchange.v1.MsgMarketManagePermissionsRequest) + - [MsgMarketManagePermissionsResponse](#provenance.exchange.v1.MsgMarketManagePermissionsResponse) + - [MsgMarketManageReqAttrsRequest](#provenance.exchange.v1.MsgMarketManageReqAttrsRequest) + - [MsgMarketManageReqAttrsResponse](#provenance.exchange.v1.MsgMarketManageReqAttrsResponse) + - [MsgMarketSettleRequest](#provenance.exchange.v1.MsgMarketSettleRequest) + - [MsgMarketSettleResponse](#provenance.exchange.v1.MsgMarketSettleResponse) + - [MsgMarketUpdateDetailsRequest](#provenance.exchange.v1.MsgMarketUpdateDetailsRequest) + - [MsgMarketUpdateDetailsResponse](#provenance.exchange.v1.MsgMarketUpdateDetailsResponse) + - [MsgMarketUpdateEnabledRequest](#provenance.exchange.v1.MsgMarketUpdateEnabledRequest) + - [MsgMarketUpdateEnabledResponse](#provenance.exchange.v1.MsgMarketUpdateEnabledResponse) + - [MsgMarketUpdateUserSettleRequest](#provenance.exchange.v1.MsgMarketUpdateUserSettleRequest) + - [MsgMarketUpdateUserSettleResponse](#provenance.exchange.v1.MsgMarketUpdateUserSettleResponse) + - [MsgMarketWithdrawRequest](#provenance.exchange.v1.MsgMarketWithdrawRequest) + - [MsgMarketWithdrawResponse](#provenance.exchange.v1.MsgMarketWithdrawResponse) + + - [Msg](#provenance.exchange.v1.Msg) + - [provenance/marker/v1/accessgrant.proto](#provenance/marker/v1/accessgrant.proto) - [AccessGrant](#provenance.marker.v1.AccessGrant) @@ -1184,6 +1279,1127 @@ Msg defines the attribute module Msg service. + +

Top

+ +## provenance/exchange/v1/events.proto + + + + + +### EventCreateMarketSubmitted +EventCreateMarketSubmitted is an event emitted during CreateMarket indicating that a governance +proposal was submitted to create a market. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | +| `proposal_id` | [uint64](#uint64) | | proposal_id is the identifier of the governance proposal that was submitted to create the market. | +| `submitted_by` | [string](#string) | | submitted_by is the account that requested the creation of the market. | + + + + + + + + +### EventMarketCreated +EventMarketCreated is an event emitted when a market has been created. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | + + + + + + + + +### EventMarketDetailsUpdated +EventMarketDetailsUpdated is an event emitted when a market's details are updated. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | +| `updated_by` | [string](#string) | | updated_by is the account that updated the details. | + + + + + + + + +### EventMarketDisabled +EventMarketDisabled is an event emitted when a market is disabled. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | +| `updated_by` | [string](#string) | | updated_by is the account that disabled the market. | + + + + + + + + +### EventMarketEnabled +EventMarketEnabled is an event emitted when a market is enabled. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | +| `updated_by` | [string](#string) | | updated_by is the account that enabled the market. | + + + + + + + + +### EventMarketFeesUpdated +EventMarketFeesUpdated is an event emitted when a market's fees have been updated. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | + + + + + + + + +### EventMarketPermissionsUpdated +EventMarketPermissionsUpdated is an event emitted when a market's permissions are updated. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | +| `updated_by` | [string](#string) | | updated_by is the account that updated the permissions. | + + + + + + + + +### EventMarketReqAttrUpdated +EventMarketReqAttrUpdated is an event emitted when a market's required attributes are updated. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | +| `updated_by` | [string](#string) | | updated_by is the account that updated the required attributes. | + + + + + + + + +### EventMarketUserSettleUpdated +EventMarketUserSettleUpdated is an event emitted when a market's self_settle option is updated. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | +| `updated_by` | [string](#string) | | updated_by is the account that updated the self_settle option. | + + + + + + + + +### EventMarketWithdraw +EventMarketWithdraw is an event emitted when a withdrawal of a market's collected fees is made. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | +| `amount_withdrawn` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | amount_withdrawn is the amount of funds withdrawn from the market account. | +| `destination` | [string](#string) | | destination is the account that received the funds. | +| `withdrawn_by` | [string](#string) | | withdrawn_by is the account that requested the withdrawal. | + + + + + + + + +### EventOrderCancelled +EventOrderCancelled is an event emitted when an order is cancelled. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order created. | +| `cancelled_by` | [string](#string) | | cancelled_by is the account that triggered the cancellation of the order. | + + + + + + + + +### EventOrderCreated +EventOrderCreated is an event emitted when an order is created. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order created. | +| `order_type` | [string](#string) | | order_type is the type of order, e.g. "ask" or "bid". | + + + + + + + + +### EventOrderFilled +EventOrderFilled is an event emitted when an order has been filled in full. +This event is also used for orders that were previously partially filled, but have now been filled in full. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order created. | + + + + + + + + +### EventOrderPartiallyFilled +EventOrderPartiallyFilled is an event emitted when an order filled in part and still has more left to fill. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order created. | +| `assets_filled` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | amount_filled is the amount of assets that were filled (and removed from the order). | +| `fees_filled` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | fees_filled is the amount of fees removed from the order. | + + + + + + + + +### EventOrderPartiallyFilled2 +EventOrderPartiallyFilled2 is an event emitted when an order filled in part and still has more left to fill. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order created. | +| `assets_filled` | [string](#string) | | amount_filled is the amount of assets that were filled (and removed from the order). | +| `fees_filled` | [string](#string) | | fees_filled is the amount of settlement fees removed from the order. | + + + + + + + + +### EventParamsUpdated +EventParamsUpdated is an event emitted when the exchange module's params have been updated. + + + + + + + + + + + + + + + + +

Top

+ +## provenance/exchange/v1/market.proto + + + + + +### AccessGrant +AddrPermissions associates an address with a list of permissions available for that address. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | address is the address that these permissions apply to. | +| `permissions` | [Permission](#provenance.exchange.v1.Permission) | repeated | allowed is the list of permissions available for the address. | + + + + + + + + +### FeeRatio +FeeRatio defines a ratio of price amount to fee amount. +For an order to be valid, its price must be evenly divisible by a FeeRatio's price. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | price is the unit the order price is divided by to get how much of the fee should apply. | +| `fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | fee is the amount to charge per price unit. | + + + + + + + + +### Market +Market contains all information about a market. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier for this market. | +| `market_details` | [MarketDetails](#provenance.exchange.v1.MarketDetails) | | market_details is some information about this market. | +| `fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | fee_create_ask_flat is the flat fee charged for creating an ask order. Each coin entry is a separate option. When an ask is created, one of these must be paid. If empty, no fee is required to create an ask order. | +| `fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | fee_create_bid_flat is the flat fee charged for creating a bid order. Each coin entry is a separate option. When a bid is created, one of these must be paid. If empty, no fee is required to create a bid order. | +| `fee_settlement_seller_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | fee_settlement_seller_flat is the flat fee charged to the seller during settlement. Each coin entry is a separate option. When an ask is settled, the seller will pay the amount in the denom that matches the price they received. | +| `fee_settlement_seller_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | fee_settlement_seller_ratios is the fee to charge a seller during settlement based on the price they are receiving. The price and fee denoms must be equal for each entry, and only one entry for any given denom is allowed. | +| `fee_settlement_buyer_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | fee_settlement_buyer_flat is the flat fee charged to the buyer during settlement. Each coin entry is a separate option. When a bid is created, the settlement fees provided must contain one of these. | +| `fee_settlement_buyer_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | fee_settlement_buyer_ratios is the fee to charge a buyer during settlement based on the price they are spending. The price and fee denoms do not have to equal. Multiple entries for any given price or fee denom are allowed, but each price denom to fee denom pair can only have one entry. | +| `accepting_orders` | [bool](#bool) | | accepting_orders is whether this market is allowing orders to be created for it. | +| `allow_user_settlement` | [bool](#bool) | | allow_user_settlement is whether this market allows users to initiate their own settlements. For example, the FillBids and FillAsks endpoints are available if and only if this is true. The MarketSettle endpoint is only available to market actors regardless of the value of this field. | +| `access_grants` | [AccessGrant](#provenance.exchange.v1.AccessGrant) | repeated | access_grants is the list of addresses and permissions granted for this market. | +| `req_attr_create_ask` | [string](#string) | repeated | req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. TODO[1658]: Add comment about wildcards. | +| `req_attr_create_bid` | [string](#string) | repeated | req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. TODO[1658]: Add comment about wildcards. | + + + + + + + + +### MarketAccount +MarketAccount is an account type for use with the accounts module to hold some basic information about a market. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `base_account` | [cosmos.auth.v1beta1.BaseAccount](#cosmos.auth.v1beta1.BaseAccount) | | base_account is the base cosmos account information. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier for this market. | +| `market_details` | [MarketDetails](#provenance.exchange.v1.MarketDetails) | | market_details is some human-consumable information about this market. | + + + + + + + + +### MarketDetails +MarketDetails contains information about a market. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `name` | [string](#string) | | name is a moniker that people can use to refer to this market. | +| `description` | [string](#string) | | description extra information about this market. The field is meant to be human-readable. | +| `website_url` | [string](#string) | | website_url is a url people can use to get to this market, or at least get more information about this market. | +| `icon_uri` | [string](#string) | | icon_uri is a uri for an icon to associate with this market. | + + + + + + + + + + +### Permission +Permission defines the different types of permission that can be given to an account for a market. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| PERMISSION_UNSPECIFIED | 0 | PERMISSION_UNSPECIFIED is the zero-value Permission; it is an error to use it. | +| PERMISSION_SETTLE | 1 | PERMISSION_SETTLE is the ability to use the Settle Tx endpoint on behalf of a market. | +| PERMISSION_CANCEL | 2 | PERMISSION_CANCEL is the ability to use the Cancel Tx endpoint on behalf of a market. | +| PERMISSION_WITHDRAW | 3 | PERMISSION_WITHDRAW is the ability to use the MarketWithdraw Tx endpoint. | +| PERMISSION_UPDATE | 4 | PERMISSION_UPDATE is the ability to use the MarketUpdate* Tx endpoints. | +| PERMISSION_PERMISSIONS | 5 | PERMISSION_PERMISSIONS is the ability to use the MarketManagePermissions Tx endpoint. | +| PERMISSION_ATTRIBUTES | 6 | PERMISSION_ATTRIBUTES is the ability to use the MarketManageReqAttrs Tx endpoint. | + + + + + + + + + + + +

Top

+ +## provenance/exchange/v1/orders.proto + + + + + +### AskOrder +AskOrder represents someone's desire to sell something at a minimum price. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id identifies the market that this order belongs to. | +| `seller` | [string](#string) | | seller is the address of the account that owns this order and has the assets to sell. | +| `assets` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | assets are the things that the seller wishes to sell. A hold is placed on this until the order is filled or cancelled. | +| `price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | price is the minimum amount that the seller is willing to accept for the assets. The seller's settlement proportional fee (and possibly the settlement flat fee) is taken out of the amount the seller receives, so it's possible that the seller will still receive less than this price. | +| `seller_settlement_flat_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. If this denom is the same denom as the price, it will come out of the actual price received. If this denom is different, the amount must be in the seller's account and a hold is placed on it until the order is filled or cancelled. | +| `allow_partial` | [bool](#bool) | | allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the order must be either filled in full or not filled at all. | + + + + + + + + +### BidOrder +BidOrder represents someone's desire to buy something at a specific price. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id identifies the market that this order belongs to. | +| `buyer` | [string](#string) | | buyer is the address of the account that owns this order and has the price to spend. | +| `assets` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | assets are the things that the buyer wishes to buy. | +| `price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | price is the amount that the buyer will pay for the assets. A hold is placed on this until the order is filled or cancelled. | +| `buyer_settlement_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) when the order is settled. A hold is placed on this until the order is filled or cancelled. | +| `allow_partial` | [bool](#bool) | | allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the order must be either filled in full or not filled at all. | + + + + + + + + +### Order +Order associates an order id with one of the order types. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier for this order. | +| `ask_order` | [AskOrder](#provenance.exchange.v1.AskOrder) | | ask_order is the information about this order if it represents an ask order. | +| `bid_order` | [BidOrder](#provenance.exchange.v1.BidOrder) | | bid_order is the information about this order if it represents a bid order. | + + + + + + + + + + + + + + + + +

Top

+ +## provenance/exchange/v1/params.proto + + + + + +### DenomSplit +DenomSplit associates a coin denomination with an amount the exchange receives for that denom. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `denom` | [string](#string) | | denom is the coin denomination this split applies to. | +| `split` | [uint32](#uint32) | | split is the proportion of fees the exchange receives for this denom in basis points. E.g. 100 = 1%. Min = 0, Max = 10000. | + + + + + + + + +### Params +Params is a representation of the exchange module parameters. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `default_split` | [uint32](#uint32) | | default_split is the default proportion of fees the exchange receives in basis points. It is used if there isn't an applicable denom-specific split defined. E.g. 100 = 1%. Min = 0, Max = 10000. | +| `denom_splits` | [DenomSplit](#provenance.exchange.v1.DenomSplit) | repeated | denom_splits are the denom-specific amounts the exchange receives. | + + + + + + + + + + + + + + + + +

Top

+ +## provenance/exchange/v1/qenesis.proto + + + + + +### GenesisState +GenesisState is the data that should be loaded into the exchange module during genesis. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#provenance.exchange.v1.Params) | | params defines all the parameters of the exchange module. | +| `markets` | [Market](#provenance.exchange.v1.Market) | repeated | markets are all of the markets to create at genesis. | +| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are all the orders to create at genesis. | + + + + + + + + + + + + + + + + +

Top

+ +## provenance/exchange/v1/query.proto + + + + + +### QueryGetAddressOrdersRequest +QueryGetAddressOrdersRequest is a request message for the QueryGetAddressOrders endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | TODO[1658]: QueryGetAddressOrdersRequest | + + + + + + + + +### QueryGetAddressOrdersResponse +QueryGetAddressOrdersResponse is a response message for the QueryGetAddressOrders endpoint. + + + + + + + + +### QueryGetAllOrdersRequest +QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint. + + + + + + + + +### QueryGetAllOrdersResponse +QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoint. + + + + + + + + +### QueryGetMarketOrdersRequest +QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | TODO[1658]: QueryGetMarketOrdersRequest | + + + + + + + + +### QueryGetMarketOrdersResponse +QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders endpoint. + + + + + + + + +### QueryGetOrderRequest +QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order_id` | [uint64](#uint64) | | TODO[1658]: QueryGetOrderRequest | + + + + + + + + +### QueryGetOrderResponse +QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. + + + + + + + + +### QueryMarketInfoRequest +QueryMarketInfoRequest is a request message for the QueryMarketInfo endpoint. + + + + + + + + +### QueryMarketInfoResponse +QueryMarketInfoResponse is a response message for the QueryMarketInfo endpoint. + + + + + + + + +### QueryOrderFeeCalcRequest +QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint. + + + + + + + + +### QueryOrderFeeCalcResponse +QueryOrderFeeCalcResponse is a response message for the QueryOrderFeeCalc endpoint. + + + + + + + + +### QueryParamsRequest +QueryParamsRequest is a request message for the QueryParams endpoint. + + + + + + + + +### QueryParamsResponse +QueryParamsResponse is a response message for the QueryParams endpoint. + + + + + + + + +### QuerySettlementFeeCalcRequest +QuerySettlementFeeCalcRequest is a request message for the QuerySettlementFeeCalc endpoint. + + + + + + + + +### QuerySettlementFeeCalcResponse +QuerySettlementFeeCalcResponse is a response message for the QuerySettlementFeeCalc endpoint. + + + + + + + + + + + + + + +### Query +Query is the service for exchange module's query endpoints. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `QueryOrderFeeCalc` | [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) | [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) | QueryOrderFeeCalc calculates the fees that will be associated with the provided order. | GET|/provenance/exchange/v1/fees/order| +| `QuerySettlementFeeCalc` | [QuerySettlementFeeCalcRequest](#provenance.exchange.v1.QuerySettlementFeeCalcRequest) | [QuerySettlementFeeCalcResponse](#provenance.exchange.v1.QuerySettlementFeeCalcResponse) | QuerySettlementFeeCalc calculates the fees that will be associated with the provided settlement. | GET|/provenance/exchange/v1/fees/settlement| +| `QueryGetOrder` | [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) | [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) | QueryGetOrder looks up an order by id. | GET|/provenance/exchange/v1/order/{order_id}| +| `QueryGetMarketOrders` | [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) | [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) | QueryGetMarketOrders looks up the orders in a market. | GET|/provenance/exchange/v1/market/{market_id}/orders| +| `QueryGetAddressOrders` | [QueryGetAddressOrdersRequest](#provenance.exchange.v1.QueryGetAddressOrdersRequest) | [QueryGetAddressOrdersResponse](#provenance.exchange.v1.QueryGetAddressOrdersResponse) | QueryGetAddressOrders looks up the orders from the provided address. | GET|/provenance/exchange/v1/orders/{address}| +| `QueryGetAllOrders` | [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) | [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) | QueryGetAllOrders gets all orders in the exchange module. | GET|/provenance/exchange/v1/orders| +| `QueryMarketInfo` | [QueryMarketInfoRequest](#provenance.exchange.v1.QueryMarketInfoRequest) | [QueryMarketInfoResponse](#provenance.exchange.v1.QueryMarketInfoResponse) | QueryMarketInfo returns the information/details about a market. | GET|/provenance/exchange/v1/market/infoGET|/provenance/exchange/v1/market/details| +| `QueryParams` | [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) | [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) | QueryParams returns the exchange module parameters. | GET|/provenance/exchange/v1/params| + + + + + + +

Top

+ +## provenance/exchange/v1/tx.proto + + + + + +### MsgCancelOrderRequest +MsgCancelOrderRequest is a request message for the CancelOrder endpoint. + + + + + + + + +### MsgCancelOrderResponse +MsgCancelOrderResponse is a response message for the CancelOrder endpoint. + + + + + + + + +### MsgCreateAskRequest +MsgCreateAskRequest is a request message for the CreateAsk endpoint. + + + + + + + + +### MsgCreateAskResponse +MsgCreateAskResponse is a response message for the CreateAsk endpoint. + + + + + + + + +### MsgCreateBidRequest +MsgCreateBidRequest is a request message for the CreateBid endpoint. + + + + + + + + +### MsgCreateBidResponse +MsgCreateBidResponse is a response message for the CreateBid endpoint. + + + + + + + + +### MsgCreateMarketRequest +MsgCreateMarketRequest is a request message for the CreateMarket endpoint. + + + + + + + + +### MsgCreateMarketResponse +MsgCreateMarketResponse is a response message for the CreateMarket endpoint. + + + + + + + + +### MsgFillAsksRequest +MsgFillAsksRequest is a request message for the FillAsks endpoint. + + + + + + + + +### MsgFillAsksResponse +MsgFillAsksResponse is a response message for the FillAsks endpoint. + + + + + + + + +### MsgFillBidsRequest +MsgFillBidsRequest is a request message for the FillBids endpoint. + + + + + + + + +### MsgFillBidsResponse +MsgFillBidsResponse is a response message for the FillBids endpoint. + + + + + + + + +### MsgGovCreateMarketRequest +MsgGovCreateMarketRequest is a request message for the GovCreateMarket endpoint. + + + + + + + + +### MsgGovCreateMarketResponse +MsgGovCreateMarketResponse is a response message for the GovCreateMarket endpoint. + + + + + + + + +### MsgGovManageFeesRequest +MsgGovManageFeesRequest is a request message for the GovManageFees endpoint. + + + + + + + + +### MsgGovManageFeesResponse +MsgGovManageFeesResponse is a response message for the GovManageFees endpoint. + + + + + + + + +### MsgGovUpdateParamsRequest +MsgGovUpdateParamsRequest is a request message for the GovUpdateParams endpoint. + + + + + + + + +### MsgGovUpdateParamsResponse +MsgGovUpdateParamsResponse is a response message for the GovUpdateParams endpoint. + + + + + + + + +### MsgMarketManagePermissionsRequest +MsgMarketManagePermissionsRequest is a request message for the MarketManagePermissions endpoint. + + + + + + + + +### MsgMarketManagePermissionsResponse +MsgMarketManagePermissionsResponse is a response message for the MarketManagePermissions endpoint. + + + + + + + + +### MsgMarketManageReqAttrsRequest +MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs endpoint. + + + + + + + + +### MsgMarketManageReqAttrsResponse +MsgMarketManageReqAttrsResponse is a response message for the MarketManageReqAttrs endpoint. + + + + + + + + +### MsgMarketSettleRequest +MsgMarketSettleRequest is a request message for the MarketSettle endpoint. + + + + + + + + +### MsgMarketSettleResponse +MsgMarketSettleResponse is a response message for the MarketSettle endpoint. + + + + + + + + +### MsgMarketUpdateDetailsRequest +MsgMarketUpdateDetailsRequest is a request message for the MarketUpdateDetails endpoint. + + + + + + + + +### MsgMarketUpdateDetailsResponse +MsgMarketUpdateDetailsResponse is a response message for the MarketUpdateDetails endpoint. + + + + + + + + +### MsgMarketUpdateEnabledRequest +MsgMarketUpdateEnabledRequest is a request message for the MarketUpdateEnabled endpoint. + + + + + + + + +### MsgMarketUpdateEnabledResponse +MsgMarketUpdateEnabledResponse is a response message for the MarketUpdateEnabled endpoint. + + + + + + + + +### MsgMarketUpdateUserSettleRequest +MsgMarketUpdateUserSettleRequest is a request message for the MarketUpdateUserSettle endpoint. + + + + + + + + +### MsgMarketUpdateUserSettleResponse +MsgMarketUpdateUserSettleResponse is a response message for the MarketUpdateUserSettle endpoint. + + + + + + + + +### MsgMarketWithdrawRequest +MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. + + + + + + + + +### MsgMarketWithdrawResponse +MsgMarketWithdrawResponse is a response message for the MarketWithdraw endpoint. + + + + + + + + + + + + + + +### Msg +Msg is the service for exchange module's tx endpoints. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `CreateAsk` | [MsgCreateAskRequest](#provenance.exchange.v1.MsgCreateAskRequest) | [MsgCreateAskResponse](#provenance.exchange.v1.MsgCreateAskResponse) | CreateAsk creates an ask order (to sell something you own). | | +| `CreateBid` | [MsgCreateBidRequest](#provenance.exchange.v1.MsgCreateBidRequest) | [MsgCreateBidResponse](#provenance.exchange.v1.MsgCreateBidResponse) | CreateBid creates an bid order (to buy something you want). | | +| `CancelOrder` | [MsgCancelOrderRequest](#provenance.exchange.v1.MsgCancelOrderRequest) | [MsgCancelOrderResponse](#provenance.exchange.v1.MsgCancelOrderResponse) | CancelOrder cancels an order. | | +| `FillBids` | [MsgFillBidsRequest](#provenance.exchange.v1.MsgFillBidsRequest) | [MsgFillBidsResponse](#provenance.exchange.v1.MsgFillBidsResponse) | FillBids uses the assets in your account to fulfill one or more bids (similar to a fill-or-cancel ask). | | +| `FillAsks` | [MsgFillAsksRequest](#provenance.exchange.v1.MsgFillAsksRequest) | [MsgFillAsksResponse](#provenance.exchange.v1.MsgFillAsksResponse) | FillAsks uses the funds in your account to fulfill one or more asks (similar to a fill-or-cancel bid). | | +| `MarketSettle` | [MsgMarketSettleRequest](#provenance.exchange.v1.MsgMarketSettleRequest) | [MsgMarketSettleResponse](#provenance.exchange.v1.MsgMarketSettleResponse) | MarketSettle is a market endpoint to trigger the settlement of orders. | | +| `MarketWithdraw` | [MsgMarketWithdrawRequest](#provenance.exchange.v1.MsgMarketWithdrawRequest) | [MsgMarketWithdrawResponse](#provenance.exchange.v1.MsgMarketWithdrawResponse) | MarketWithdraw is a market endpoint to withdraw fees that have been collected. | | +| `MarketUpdateDetails` | [MsgMarketUpdateDetailsRequest](#provenance.exchange.v1.MsgMarketUpdateDetailsRequest) | [MsgMarketUpdateDetailsResponse](#provenance.exchange.v1.MsgMarketUpdateDetailsResponse) | MarketUpdateDetails is a market endpoint to update its details. | | +| `MarketUpdateEnabled` | [MsgMarketUpdateEnabledRequest](#provenance.exchange.v1.MsgMarketUpdateEnabledRequest) | [MsgMarketUpdateEnabledResponse](#provenance.exchange.v1.MsgMarketUpdateEnabledResponse) | MarketUpdateEnabled is a market endpoint to update whether its accepting orders. | | +| `MarketUpdateUserSettle` | [MsgMarketUpdateUserSettleRequest](#provenance.exchange.v1.MsgMarketUpdateUserSettleRequest) | [MsgMarketUpdateUserSettleResponse](#provenance.exchange.v1.MsgMarketUpdateUserSettleResponse) | MarketUpdateUserSettle is a market endpoint to update whether it allows user-initiated settlement. | | +| `MarketManagePermissions` | [MsgMarketManagePermissionsRequest](#provenance.exchange.v1.MsgMarketManagePermissionsRequest) | [MsgMarketManagePermissionsResponse](#provenance.exchange.v1.MsgMarketManagePermissionsResponse) | MarketManagePermissions is a market endpoint to manage a market's user permissions. | | +| `MarketManageReqAttrs` | [MsgMarketManageReqAttrsRequest](#provenance.exchange.v1.MsgMarketManageReqAttrsRequest) | [MsgMarketManageReqAttrsResponse](#provenance.exchange.v1.MsgMarketManageReqAttrsResponse) | MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. | | +| `CreateMarket` | [MsgCreateMarketRequest](#provenance.exchange.v1.MsgCreateMarketRequest) | [MsgCreateMarketResponse](#provenance.exchange.v1.MsgCreateMarketResponse) | CreateMarket reserves the next market id and submits a GovCreateMarket governance proposal to create the market. | | +| `GovCreateMarket` | [MsgGovCreateMarketRequest](#provenance.exchange.v1.MsgGovCreateMarketRequest) | [MsgGovCreateMarketResponse](#provenance.exchange.v1.MsgGovCreateMarketResponse) | GovCreateMarket is a governance proposal endpoint for creating a market. The CreateMarket endpoint should be used to submit one of these unless you don't need to know the market id until after the proposal passes. | | +| `GovManageFees` | [MsgGovManageFeesRequest](#provenance.exchange.v1.MsgGovManageFeesRequest) | [MsgGovManageFeesResponse](#provenance.exchange.v1.MsgGovManageFeesResponse) | GovManageFees is a governance proposal endpoint for updating a market's fees. | | +| `GovUpdateParams` | [MsgGovUpdateParamsRequest](#provenance.exchange.v1.MsgGovUpdateParamsRequest) | [MsgGovUpdateParamsResponse](#provenance.exchange.v1.MsgGovUpdateParamsResponse) | GovUpdateParams is a governance proposal endpoint for updating the exchange module's params. | | + + + + +

Top

diff --git a/x/exchange/events.pb.go b/x/exchange/events.pb.go new file mode 100644 index 0000000000..6e289f2427 --- /dev/null +++ b/x/exchange/events.pb.go @@ -0,0 +1,3547 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: provenance/exchange/v1/events.proto + +package exchange + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// EventOrderCreated is an event emitted when an order is created. +type EventOrderCreated struct { + // order_id is the numerical identifier of the order created. + OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + // order_type is the type of order, e.g. "ask" or "bid". + OrderType string `protobuf:"bytes,2,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` +} + +func (m *EventOrderCreated) Reset() { *m = EventOrderCreated{} } +func (m *EventOrderCreated) String() string { return proto.CompactTextString(m) } +func (*EventOrderCreated) ProtoMessage() {} +func (*EventOrderCreated) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{0} +} +func (m *EventOrderCreated) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventOrderCreated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventOrderCreated.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventOrderCreated) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventOrderCreated.Merge(m, src) +} +func (m *EventOrderCreated) XXX_Size() int { + return m.Size() +} +func (m *EventOrderCreated) XXX_DiscardUnknown() { + xxx_messageInfo_EventOrderCreated.DiscardUnknown(m) +} + +var xxx_messageInfo_EventOrderCreated proto.InternalMessageInfo + +func (m *EventOrderCreated) GetOrderId() uint64 { + if m != nil { + return m.OrderId + } + return 0 +} + +func (m *EventOrderCreated) GetOrderType() string { + if m != nil { + return m.OrderType + } + return "" +} + +// EventOrderCancelled is an event emitted when an order is cancelled. +type EventOrderCancelled struct { + // order_id is the numerical identifier of the order created. + OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + // cancelled_by is the account that triggered the cancellation of the order. + CancelledBy string `protobuf:"bytes,2,opt,name=cancelled_by,json=cancelledBy,proto3" json:"cancelled_by,omitempty"` +} + +func (m *EventOrderCancelled) Reset() { *m = EventOrderCancelled{} } +func (m *EventOrderCancelled) String() string { return proto.CompactTextString(m) } +func (*EventOrderCancelled) ProtoMessage() {} +func (*EventOrderCancelled) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{1} +} +func (m *EventOrderCancelled) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventOrderCancelled) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventOrderCancelled.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventOrderCancelled) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventOrderCancelled.Merge(m, src) +} +func (m *EventOrderCancelled) XXX_Size() int { + return m.Size() +} +func (m *EventOrderCancelled) XXX_DiscardUnknown() { + xxx_messageInfo_EventOrderCancelled.DiscardUnknown(m) +} + +var xxx_messageInfo_EventOrderCancelled proto.InternalMessageInfo + +func (m *EventOrderCancelled) GetOrderId() uint64 { + if m != nil { + return m.OrderId + } + return 0 +} + +func (m *EventOrderCancelled) GetCancelledBy() string { + if m != nil { + return m.CancelledBy + } + return "" +} + +// EventOrderFilled is an event emitted when an order has been filled in full. +// This event is also used for orders that were previously partially filled, but have now been filled in full. +type EventOrderFilled struct { + // order_id is the numerical identifier of the order created. + OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` +} + +func (m *EventOrderFilled) Reset() { *m = EventOrderFilled{} } +func (m *EventOrderFilled) String() string { return proto.CompactTextString(m) } +func (*EventOrderFilled) ProtoMessage() {} +func (*EventOrderFilled) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{2} +} +func (m *EventOrderFilled) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventOrderFilled) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventOrderFilled.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventOrderFilled) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventOrderFilled.Merge(m, src) +} +func (m *EventOrderFilled) XXX_Size() int { + return m.Size() +} +func (m *EventOrderFilled) XXX_DiscardUnknown() { + xxx_messageInfo_EventOrderFilled.DiscardUnknown(m) +} + +var xxx_messageInfo_EventOrderFilled proto.InternalMessageInfo + +func (m *EventOrderFilled) GetOrderId() uint64 { + if m != nil { + return m.OrderId + } + return 0 +} + +// EventOrderPartiallyFilled is an event emitted when an order filled in part and still has more left to fill. +type EventOrderPartiallyFilled struct { + // order_id is the numerical identifier of the order created. + OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + // amount_filled is the amount of assets that were filled (and removed from the order). + AssetsFilled github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=assets_filled,json=assetsFilled,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"assets_filled"` + // fees_filled is the amount of fees removed from the order. + FeesFilled github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=fees_filled,json=feesFilled,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"fees_filled"` +} + +func (m *EventOrderPartiallyFilled) Reset() { *m = EventOrderPartiallyFilled{} } +func (m *EventOrderPartiallyFilled) String() string { return proto.CompactTextString(m) } +func (*EventOrderPartiallyFilled) ProtoMessage() {} +func (*EventOrderPartiallyFilled) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{3} +} +func (m *EventOrderPartiallyFilled) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventOrderPartiallyFilled) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventOrderPartiallyFilled.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventOrderPartiallyFilled) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventOrderPartiallyFilled.Merge(m, src) +} +func (m *EventOrderPartiallyFilled) XXX_Size() int { + return m.Size() +} +func (m *EventOrderPartiallyFilled) XXX_DiscardUnknown() { + xxx_messageInfo_EventOrderPartiallyFilled.DiscardUnknown(m) +} + +var xxx_messageInfo_EventOrderPartiallyFilled proto.InternalMessageInfo + +func (m *EventOrderPartiallyFilled) GetOrderId() uint64 { + if m != nil { + return m.OrderId + } + return 0 +} + +func (m *EventOrderPartiallyFilled) GetAssetsFilled() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.AssetsFilled + } + return nil +} + +func (m *EventOrderPartiallyFilled) GetFeesFilled() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.FeesFilled + } + return nil +} + +// EventOrderPartiallyFilled2 is an event emitted when an order filled in part and still has more left to fill. +type EventOrderPartiallyFilled2 struct { + // order_id is the numerical identifier of the order created. + OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + // amount_filled is the amount of assets that were filled (and removed from the order). + AssetsFilled string `protobuf:"bytes,2,opt,name=assets_filled,json=assetsFilled,proto3" json:"assets_filled,omitempty"` + // fees_filled is the amount of settlement fees removed from the order. + FeesFilled string `protobuf:"bytes,3,opt,name=fees_filled,json=feesFilled,proto3" json:"fees_filled,omitempty"` +} + +func (m *EventOrderPartiallyFilled2) Reset() { *m = EventOrderPartiallyFilled2{} } +func (m *EventOrderPartiallyFilled2) String() string { return proto.CompactTextString(m) } +func (*EventOrderPartiallyFilled2) ProtoMessage() {} +func (*EventOrderPartiallyFilled2) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{4} +} +func (m *EventOrderPartiallyFilled2) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventOrderPartiallyFilled2) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventOrderPartiallyFilled2.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventOrderPartiallyFilled2) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventOrderPartiallyFilled2.Merge(m, src) +} +func (m *EventOrderPartiallyFilled2) XXX_Size() int { + return m.Size() +} +func (m *EventOrderPartiallyFilled2) XXX_DiscardUnknown() { + xxx_messageInfo_EventOrderPartiallyFilled2.DiscardUnknown(m) +} + +var xxx_messageInfo_EventOrderPartiallyFilled2 proto.InternalMessageInfo + +func (m *EventOrderPartiallyFilled2) GetOrderId() uint64 { + if m != nil { + return m.OrderId + } + return 0 +} + +func (m *EventOrderPartiallyFilled2) GetAssetsFilled() string { + if m != nil { + return m.AssetsFilled + } + return "" +} + +func (m *EventOrderPartiallyFilled2) GetFeesFilled() string { + if m != nil { + return m.FeesFilled + } + return "" +} + +// EventMarketWithdraw is an event emitted when a withdrawal of a market's collected fees is made. +type EventMarketWithdraw struct { + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // amount_withdrawn is the amount of funds withdrawn from the market account. + AmountWithdrawn github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=amount_withdrawn,json=amountWithdrawn,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount_withdrawn"` + // destination is the account that received the funds. + Destination string `protobuf:"bytes,3,opt,name=destination,proto3" json:"destination,omitempty"` + // withdrawn_by is the account that requested the withdrawal. + WithdrawnBy string `protobuf:"bytes,4,opt,name=withdrawn_by,json=withdrawnBy,proto3" json:"withdrawn_by,omitempty"` +} + +func (m *EventMarketWithdraw) Reset() { *m = EventMarketWithdraw{} } +func (m *EventMarketWithdraw) String() string { return proto.CompactTextString(m) } +func (*EventMarketWithdraw) ProtoMessage() {} +func (*EventMarketWithdraw) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{5} +} +func (m *EventMarketWithdraw) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventMarketWithdraw) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventMarketWithdraw.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventMarketWithdraw) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventMarketWithdraw.Merge(m, src) +} +func (m *EventMarketWithdraw) XXX_Size() int { + return m.Size() +} +func (m *EventMarketWithdraw) XXX_DiscardUnknown() { + xxx_messageInfo_EventMarketWithdraw.DiscardUnknown(m) +} + +var xxx_messageInfo_EventMarketWithdraw proto.InternalMessageInfo + +func (m *EventMarketWithdraw) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *EventMarketWithdraw) GetAmountWithdrawn() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.AmountWithdrawn + } + return nil +} + +func (m *EventMarketWithdraw) GetDestination() string { + if m != nil { + return m.Destination + } + return "" +} + +func (m *EventMarketWithdraw) GetWithdrawnBy() string { + if m != nil { + return m.WithdrawnBy + } + return "" +} + +// EventMarketDetailsUpdated is an event emitted when a market's details are updated. +type EventMarketDetailsUpdated struct { + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // updated_by is the account that updated the details. + UpdatedBy string `protobuf:"bytes,2,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"` +} + +func (m *EventMarketDetailsUpdated) Reset() { *m = EventMarketDetailsUpdated{} } +func (m *EventMarketDetailsUpdated) String() string { return proto.CompactTextString(m) } +func (*EventMarketDetailsUpdated) ProtoMessage() {} +func (*EventMarketDetailsUpdated) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{6} +} +func (m *EventMarketDetailsUpdated) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventMarketDetailsUpdated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventMarketDetailsUpdated.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventMarketDetailsUpdated) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventMarketDetailsUpdated.Merge(m, src) +} +func (m *EventMarketDetailsUpdated) XXX_Size() int { + return m.Size() +} +func (m *EventMarketDetailsUpdated) XXX_DiscardUnknown() { + xxx_messageInfo_EventMarketDetailsUpdated.DiscardUnknown(m) +} + +var xxx_messageInfo_EventMarketDetailsUpdated proto.InternalMessageInfo + +func (m *EventMarketDetailsUpdated) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *EventMarketDetailsUpdated) GetUpdatedBy() string { + if m != nil { + return m.UpdatedBy + } + return "" +} + +// EventMarketEnabled is an event emitted when a market is enabled. +type EventMarketEnabled struct { + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // updated_by is the account that enabled the market. + UpdatedBy string `protobuf:"bytes,2,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"` +} + +func (m *EventMarketEnabled) Reset() { *m = EventMarketEnabled{} } +func (m *EventMarketEnabled) String() string { return proto.CompactTextString(m) } +func (*EventMarketEnabled) ProtoMessage() {} +func (*EventMarketEnabled) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{7} +} +func (m *EventMarketEnabled) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventMarketEnabled) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventMarketEnabled.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventMarketEnabled) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventMarketEnabled.Merge(m, src) +} +func (m *EventMarketEnabled) XXX_Size() int { + return m.Size() +} +func (m *EventMarketEnabled) XXX_DiscardUnknown() { + xxx_messageInfo_EventMarketEnabled.DiscardUnknown(m) +} + +var xxx_messageInfo_EventMarketEnabled proto.InternalMessageInfo + +func (m *EventMarketEnabled) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *EventMarketEnabled) GetUpdatedBy() string { + if m != nil { + return m.UpdatedBy + } + return "" +} + +// EventMarketDisabled is an event emitted when a market is disabled. +type EventMarketDisabled struct { + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // updated_by is the account that disabled the market. + UpdatedBy string `protobuf:"bytes,2,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"` +} + +func (m *EventMarketDisabled) Reset() { *m = EventMarketDisabled{} } +func (m *EventMarketDisabled) String() string { return proto.CompactTextString(m) } +func (*EventMarketDisabled) ProtoMessage() {} +func (*EventMarketDisabled) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{8} +} +func (m *EventMarketDisabled) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventMarketDisabled) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventMarketDisabled.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventMarketDisabled) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventMarketDisabled.Merge(m, src) +} +func (m *EventMarketDisabled) XXX_Size() int { + return m.Size() +} +func (m *EventMarketDisabled) XXX_DiscardUnknown() { + xxx_messageInfo_EventMarketDisabled.DiscardUnknown(m) +} + +var xxx_messageInfo_EventMarketDisabled proto.InternalMessageInfo + +func (m *EventMarketDisabled) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *EventMarketDisabled) GetUpdatedBy() string { + if m != nil { + return m.UpdatedBy + } + return "" +} + +// EventMarketUserSettleUpdated is an event emitted when a market's self_settle option is updated. +type EventMarketUserSettleUpdated struct { + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // updated_by is the account that updated the self_settle option. + UpdatedBy string `protobuf:"bytes,2,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"` +} + +func (m *EventMarketUserSettleUpdated) Reset() { *m = EventMarketUserSettleUpdated{} } +func (m *EventMarketUserSettleUpdated) String() string { return proto.CompactTextString(m) } +func (*EventMarketUserSettleUpdated) ProtoMessage() {} +func (*EventMarketUserSettleUpdated) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{9} +} +func (m *EventMarketUserSettleUpdated) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventMarketUserSettleUpdated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventMarketUserSettleUpdated.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventMarketUserSettleUpdated) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventMarketUserSettleUpdated.Merge(m, src) +} +func (m *EventMarketUserSettleUpdated) XXX_Size() int { + return m.Size() +} +func (m *EventMarketUserSettleUpdated) XXX_DiscardUnknown() { + xxx_messageInfo_EventMarketUserSettleUpdated.DiscardUnknown(m) +} + +var xxx_messageInfo_EventMarketUserSettleUpdated proto.InternalMessageInfo + +func (m *EventMarketUserSettleUpdated) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *EventMarketUserSettleUpdated) GetUpdatedBy() string { + if m != nil { + return m.UpdatedBy + } + return "" +} + +// EventMarketPermissionsUpdated is an event emitted when a market's permissions are updated. +type EventMarketPermissionsUpdated struct { + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // updated_by is the account that updated the permissions. + UpdatedBy string `protobuf:"bytes,2,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"` +} + +func (m *EventMarketPermissionsUpdated) Reset() { *m = EventMarketPermissionsUpdated{} } +func (m *EventMarketPermissionsUpdated) String() string { return proto.CompactTextString(m) } +func (*EventMarketPermissionsUpdated) ProtoMessage() {} +func (*EventMarketPermissionsUpdated) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{10} +} +func (m *EventMarketPermissionsUpdated) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventMarketPermissionsUpdated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventMarketPermissionsUpdated.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventMarketPermissionsUpdated) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventMarketPermissionsUpdated.Merge(m, src) +} +func (m *EventMarketPermissionsUpdated) XXX_Size() int { + return m.Size() +} +func (m *EventMarketPermissionsUpdated) XXX_DiscardUnknown() { + xxx_messageInfo_EventMarketPermissionsUpdated.DiscardUnknown(m) +} + +var xxx_messageInfo_EventMarketPermissionsUpdated proto.InternalMessageInfo + +func (m *EventMarketPermissionsUpdated) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *EventMarketPermissionsUpdated) GetUpdatedBy() string { + if m != nil { + return m.UpdatedBy + } + return "" +} + +// EventMarketReqAttrUpdated is an event emitted when a market's required attributes are updated. +type EventMarketReqAttrUpdated struct { + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // updated_by is the account that updated the required attributes. + UpdatedBy string `protobuf:"bytes,2,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"` +} + +func (m *EventMarketReqAttrUpdated) Reset() { *m = EventMarketReqAttrUpdated{} } +func (m *EventMarketReqAttrUpdated) String() string { return proto.CompactTextString(m) } +func (*EventMarketReqAttrUpdated) ProtoMessage() {} +func (*EventMarketReqAttrUpdated) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{11} +} +func (m *EventMarketReqAttrUpdated) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventMarketReqAttrUpdated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventMarketReqAttrUpdated.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventMarketReqAttrUpdated) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventMarketReqAttrUpdated.Merge(m, src) +} +func (m *EventMarketReqAttrUpdated) XXX_Size() int { + return m.Size() +} +func (m *EventMarketReqAttrUpdated) XXX_DiscardUnknown() { + xxx_messageInfo_EventMarketReqAttrUpdated.DiscardUnknown(m) +} + +var xxx_messageInfo_EventMarketReqAttrUpdated proto.InternalMessageInfo + +func (m *EventMarketReqAttrUpdated) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *EventMarketReqAttrUpdated) GetUpdatedBy() string { + if m != nil { + return m.UpdatedBy + } + return "" +} + +// EventCreateMarketSubmitted is an event emitted during CreateMarket indicating that a governance +// proposal was submitted to create a market. +type EventCreateMarketSubmitted struct { + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // proposal_id is the identifier of the governance proposal that was submitted to create the market. + ProposalId uint64 `protobuf:"varint,2,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty"` + // submitted_by is the account that requested the creation of the market. + SubmittedBy string `protobuf:"bytes,3,opt,name=submitted_by,json=submittedBy,proto3" json:"submitted_by,omitempty"` +} + +func (m *EventCreateMarketSubmitted) Reset() { *m = EventCreateMarketSubmitted{} } +func (m *EventCreateMarketSubmitted) String() string { return proto.CompactTextString(m) } +func (*EventCreateMarketSubmitted) ProtoMessage() {} +func (*EventCreateMarketSubmitted) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{12} +} +func (m *EventCreateMarketSubmitted) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventCreateMarketSubmitted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventCreateMarketSubmitted.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventCreateMarketSubmitted) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventCreateMarketSubmitted.Merge(m, src) +} +func (m *EventCreateMarketSubmitted) XXX_Size() int { + return m.Size() +} +func (m *EventCreateMarketSubmitted) XXX_DiscardUnknown() { + xxx_messageInfo_EventCreateMarketSubmitted.DiscardUnknown(m) +} + +var xxx_messageInfo_EventCreateMarketSubmitted proto.InternalMessageInfo + +func (m *EventCreateMarketSubmitted) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *EventCreateMarketSubmitted) GetProposalId() uint64 { + if m != nil { + return m.ProposalId + } + return 0 +} + +func (m *EventCreateMarketSubmitted) GetSubmittedBy() string { + if m != nil { + return m.SubmittedBy + } + return "" +} + +// EventMarketCreated is an event emitted when a market has been created. +type EventMarketCreated struct { + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` +} + +func (m *EventMarketCreated) Reset() { *m = EventMarketCreated{} } +func (m *EventMarketCreated) String() string { return proto.CompactTextString(m) } +func (*EventMarketCreated) ProtoMessage() {} +func (*EventMarketCreated) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{13} +} +func (m *EventMarketCreated) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventMarketCreated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventMarketCreated.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventMarketCreated) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventMarketCreated.Merge(m, src) +} +func (m *EventMarketCreated) XXX_Size() int { + return m.Size() +} +func (m *EventMarketCreated) XXX_DiscardUnknown() { + xxx_messageInfo_EventMarketCreated.DiscardUnknown(m) +} + +var xxx_messageInfo_EventMarketCreated proto.InternalMessageInfo + +func (m *EventMarketCreated) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +// EventMarketFeesUpdated is an event emitted when a market's fees have been updated. +type EventMarketFeesUpdated struct { + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` +} + +func (m *EventMarketFeesUpdated) Reset() { *m = EventMarketFeesUpdated{} } +func (m *EventMarketFeesUpdated) String() string { return proto.CompactTextString(m) } +func (*EventMarketFeesUpdated) ProtoMessage() {} +func (*EventMarketFeesUpdated) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{14} +} +func (m *EventMarketFeesUpdated) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventMarketFeesUpdated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventMarketFeesUpdated.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventMarketFeesUpdated) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventMarketFeesUpdated.Merge(m, src) +} +func (m *EventMarketFeesUpdated) XXX_Size() int { + return m.Size() +} +func (m *EventMarketFeesUpdated) XXX_DiscardUnknown() { + xxx_messageInfo_EventMarketFeesUpdated.DiscardUnknown(m) +} + +var xxx_messageInfo_EventMarketFeesUpdated proto.InternalMessageInfo + +func (m *EventMarketFeesUpdated) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +// EventParamsUpdated is an event emitted when the exchange module's params have been updated. +type EventParamsUpdated struct { +} + +func (m *EventParamsUpdated) Reset() { *m = EventParamsUpdated{} } +func (m *EventParamsUpdated) String() string { return proto.CompactTextString(m) } +func (*EventParamsUpdated) ProtoMessage() {} +func (*EventParamsUpdated) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{15} +} +func (m *EventParamsUpdated) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventParamsUpdated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventParamsUpdated.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventParamsUpdated) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventParamsUpdated.Merge(m, src) +} +func (m *EventParamsUpdated) XXX_Size() int { + return m.Size() +} +func (m *EventParamsUpdated) XXX_DiscardUnknown() { + xxx_messageInfo_EventParamsUpdated.DiscardUnknown(m) +} + +var xxx_messageInfo_EventParamsUpdated proto.InternalMessageInfo + +func init() { + proto.RegisterType((*EventOrderCreated)(nil), "provenance.exchange.v1.EventOrderCreated") + proto.RegisterType((*EventOrderCancelled)(nil), "provenance.exchange.v1.EventOrderCancelled") + proto.RegisterType((*EventOrderFilled)(nil), "provenance.exchange.v1.EventOrderFilled") + proto.RegisterType((*EventOrderPartiallyFilled)(nil), "provenance.exchange.v1.EventOrderPartiallyFilled") + proto.RegisterType((*EventOrderPartiallyFilled2)(nil), "provenance.exchange.v1.EventOrderPartiallyFilled2") + proto.RegisterType((*EventMarketWithdraw)(nil), "provenance.exchange.v1.EventMarketWithdraw") + proto.RegisterType((*EventMarketDetailsUpdated)(nil), "provenance.exchange.v1.EventMarketDetailsUpdated") + proto.RegisterType((*EventMarketEnabled)(nil), "provenance.exchange.v1.EventMarketEnabled") + proto.RegisterType((*EventMarketDisabled)(nil), "provenance.exchange.v1.EventMarketDisabled") + proto.RegisterType((*EventMarketUserSettleUpdated)(nil), "provenance.exchange.v1.EventMarketUserSettleUpdated") + proto.RegisterType((*EventMarketPermissionsUpdated)(nil), "provenance.exchange.v1.EventMarketPermissionsUpdated") + proto.RegisterType((*EventMarketReqAttrUpdated)(nil), "provenance.exchange.v1.EventMarketReqAttrUpdated") + proto.RegisterType((*EventCreateMarketSubmitted)(nil), "provenance.exchange.v1.EventCreateMarketSubmitted") + proto.RegisterType((*EventMarketCreated)(nil), "provenance.exchange.v1.EventMarketCreated") + proto.RegisterType((*EventMarketFeesUpdated)(nil), "provenance.exchange.v1.EventMarketFeesUpdated") + proto.RegisterType((*EventParamsUpdated)(nil), "provenance.exchange.v1.EventParamsUpdated") +} + +func init() { + proto.RegisterFile("provenance/exchange/v1/events.proto", fileDescriptor_c1b69385a348cffa) +} + +var fileDescriptor_c1b69385a348cffa = []byte{ + // 691 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4d, 0x4f, 0xdb, 0x4c, + 0x10, 0x8e, 0x03, 0x7a, 0x5f, 0xb2, 0x01, 0xbd, 0xbc, 0x2e, 0x42, 0x09, 0x2d, 0x09, 0x32, 0x97, + 0x5c, 0x62, 0x37, 0x54, 0x55, 0xa5, 0xf6, 0x44, 0xf8, 0x90, 0x38, 0xa0, 0x46, 0xa1, 0xa8, 0x52, + 0x2f, 0xd1, 0x3a, 0x1e, 0xc2, 0x16, 0x7b, 0xd7, 0xec, 0x6e, 0x02, 0x3e, 0xf4, 0x3f, 0xf4, 0xd4, + 0xfe, 0x80, 0xde, 0x7a, 0xee, 0x8f, 0xe0, 0x88, 0x7a, 0xea, 0xa9, 0xad, 0xe0, 0x8f, 0x54, 0xeb, + 0xb5, 0x13, 0xa7, 0x1f, 0x09, 0x87, 0xe6, 0x94, 0xec, 0xec, 0x33, 0xf3, 0xcc, 0xce, 0x3c, 0xbb, + 0x63, 0xb4, 0x19, 0x72, 0x36, 0x00, 0x8a, 0x69, 0x17, 0x1c, 0xb8, 0xec, 0x9e, 0x62, 0xda, 0x03, + 0x67, 0xd0, 0x70, 0x60, 0x00, 0x54, 0x0a, 0x3b, 0xe4, 0x4c, 0x32, 0x73, 0x75, 0x04, 0xb2, 0x53, + 0x90, 0x3d, 0x68, 0xac, 0x55, 0xba, 0x4c, 0x04, 0x4c, 0x38, 0x2e, 0x16, 0xca, 0xc9, 0x05, 0x89, + 0x1b, 0x4e, 0x97, 0x11, 0xaa, 0xfd, 0xd6, 0xca, 0x7a, 0xbf, 0x13, 0xaf, 0x1c, 0xbd, 0x48, 0xb6, + 0x56, 0x7a, 0xac, 0xc7, 0xb4, 0x5d, 0xfd, 0xd3, 0x56, 0xeb, 0x10, 0xfd, 0xbf, 0xa7, 0x88, 0x9f, + 0x73, 0x0f, 0xf8, 0x0e, 0x07, 0x2c, 0xc1, 0x33, 0xcb, 0x68, 0x81, 0xa9, 0x75, 0x87, 0x78, 0x25, + 0x63, 0xc3, 0xa8, 0xcd, 0xb7, 0xff, 0x8d, 0xd7, 0x07, 0x9e, 0xb9, 0x8e, 0x90, 0xde, 0x92, 0x51, + 0x08, 0xa5, 0xfc, 0x86, 0x51, 0x2b, 0xb4, 0x0b, 0xb1, 0xe5, 0x45, 0x14, 0x82, 0x15, 0xa0, 0x7b, + 0x99, 0x70, 0x2a, 0x7d, 0xdf, 0x9f, 0x1c, 0xf0, 0x19, 0x5a, 0xec, 0xa6, 0xb8, 0x8e, 0x1b, 0xe9, + 0x90, 0xcd, 0xd2, 0xe7, 0x4f, 0xf5, 0x95, 0x24, 0xfd, 0x6d, 0xcf, 0xe3, 0x20, 0xc4, 0x91, 0xe4, + 0x84, 0xf6, 0xda, 0xc5, 0x21, 0xba, 0x19, 0x59, 0x75, 0xb4, 0x3c, 0xa2, 0xdb, 0x27, 0x53, 0xb8, + 0xac, 0x77, 0x79, 0x54, 0x1e, 0xe1, 0x5b, 0x98, 0x4b, 0x82, 0x7d, 0x3f, 0x9a, 0xea, 0x68, 0x86, + 0x68, 0x09, 0x0b, 0x01, 0x52, 0x74, 0x4e, 0x62, 0x6c, 0x29, 0xbf, 0x31, 0x57, 0x2b, 0x6e, 0x95, + 0xed, 0x24, 0x45, 0xd5, 0x0e, 0x3b, 0x69, 0x87, 0xbd, 0xc3, 0x08, 0x6d, 0x3e, 0xbc, 0xfa, 0x5a, + 0xcd, 0x7d, 0xfc, 0x56, 0xad, 0xf5, 0x88, 0x3c, 0xed, 0xbb, 0x76, 0x97, 0x05, 0x49, 0x3b, 0x92, + 0x9f, 0xba, 0xf0, 0xce, 0x1c, 0x55, 0x44, 0x11, 0x3b, 0x88, 0xf6, 0xa2, 0x66, 0x48, 0x92, 0xf1, + 0x51, 0xf1, 0x04, 0x60, 0xc8, 0x37, 0xf7, 0xf7, 0xf9, 0x90, 0x8a, 0xaf, 0xd9, 0xac, 0x37, 0x68, + 0xed, 0x8f, 0x75, 0xd9, 0x9a, 0x54, 0x98, 0xcd, 0x5f, 0x0b, 0xa3, 0x14, 0x31, 0x7e, 0x96, 0xea, + 0xcf, 0x67, 0x51, 0x90, 0x2c, 0xfd, 0x87, 0x7c, 0x22, 0x9b, 0x43, 0xcc, 0xcf, 0x40, 0xbe, 0x24, + 0xf2, 0xd4, 0xe3, 0xf8, 0xc2, 0xbc, 0x8f, 0x0a, 0x41, 0x6c, 0x49, 0x99, 0x97, 0xda, 0x0b, 0xda, + 0x70, 0xe0, 0x99, 0x03, 0xb4, 0x8c, 0x03, 0xd6, 0xa7, 0xb2, 0x73, 0x91, 0xe0, 0xe9, 0x2c, 0xda, + 0xf2, 0x9f, 0x26, 0x49, 0x73, 0xa2, 0xe6, 0x53, 0x54, 0xf4, 0x40, 0x48, 0x42, 0xb1, 0x24, 0x8c, + 0xea, 0xd3, 0x4c, 0xd2, 0x6b, 0x06, 0xac, 0xc4, 0x3e, 0x4c, 0x56, 0x89, 0x7d, 0x7e, 0x9a, 0xf3, + 0x10, 0xdd, 0x8c, 0xac, 0xf3, 0x44, 0xbc, 0xba, 0x48, 0xbb, 0x20, 0x31, 0xf1, 0xc5, 0x71, 0xe8, + 0xc5, 0x57, 0x76, 0x62, 0xa9, 0x9e, 0x20, 0xd4, 0xd7, 0xb8, 0xbb, 0xdc, 0xb0, 0x42, 0x82, 0x6d, + 0x46, 0xd6, 0x6b, 0x64, 0x66, 0x28, 0xf7, 0x28, 0x76, 0xfd, 0x99, 0x71, 0x9d, 0x8d, 0x69, 0x60, + 0x97, 0x88, 0x59, 0x92, 0x49, 0xf4, 0x20, 0x43, 0x76, 0x2c, 0x80, 0x1f, 0x81, 0x94, 0x3e, 0xcc, + 0xb6, 0x9c, 0x7d, 0xb4, 0x9e, 0x61, 0x6d, 0x01, 0x0f, 0x88, 0x10, 0x84, 0xd1, 0x19, 0x77, 0x71, + 0x5c, 0x38, 0x6d, 0x38, 0xdf, 0x96, 0x92, 0xcf, 0x96, 0xf2, 0xbd, 0x91, 0xbc, 0x28, 0x7a, 0xa4, + 0x68, 0xe6, 0xa3, 0xbe, 0x1b, 0x10, 0x39, 0x95, 0xb4, 0x8a, 0x8a, 0x21, 0x67, 0x21, 0x13, 0xd8, + 0x57, 0xdb, 0xf9, 0xf8, 0xc5, 0x41, 0xa9, 0x49, 0x8f, 0x0c, 0x91, 0x86, 0x52, 0x79, 0x4d, 0xbd, + 0x82, 0x43, 0x74, 0x33, 0xb2, 0x1a, 0x63, 0x92, 0x4e, 0x27, 0xde, 0xa4, 0x84, 0xac, 0xc7, 0x68, + 0x35, 0xe3, 0xb2, 0x0f, 0x70, 0xa7, 0x7e, 0x59, 0x2b, 0x09, 0x53, 0x0b, 0x73, 0x1c, 0xa4, 0x2e, + 0x4d, 0xb8, 0xba, 0xa9, 0x18, 0xd7, 0x37, 0x15, 0xe3, 0xfb, 0x4d, 0xc5, 0x78, 0x7b, 0x5b, 0xc9, + 0x5d, 0xdf, 0x56, 0x72, 0x5f, 0x6e, 0x2b, 0x39, 0x54, 0x26, 0xf1, 0x50, 0xfe, 0xcd, 0xd8, 0x6f, + 0x19, 0xaf, 0xec, 0xcc, 0x83, 0x35, 0x02, 0xd5, 0x09, 0xcb, 0xac, 0x9c, 0xcb, 0xe1, 0x07, 0x85, + 0xfb, 0x4f, 0x3c, 0xde, 0x1f, 0xfd, 0x08, 0x00, 0x00, 0xff, 0xff, 0x66, 0x79, 0xe5, 0xe7, 0x6e, + 0x08, 0x00, 0x00, +} + +func (m *EventOrderCreated) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventOrderCreated) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventOrderCreated) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.OrderType) > 0 { + i -= len(m.OrderType) + copy(dAtA[i:], m.OrderType) + i = encodeVarintEvents(dAtA, i, uint64(len(m.OrderType))) + i-- + dAtA[i] = 0x12 + } + if m.OrderId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.OrderId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventOrderCancelled) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventOrderCancelled) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventOrderCancelled) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CancelledBy) > 0 { + i -= len(m.CancelledBy) + copy(dAtA[i:], m.CancelledBy) + i = encodeVarintEvents(dAtA, i, uint64(len(m.CancelledBy))) + i-- + dAtA[i] = 0x12 + } + if m.OrderId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.OrderId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventOrderFilled) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventOrderFilled) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventOrderFilled) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.OrderId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.OrderId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventOrderPartiallyFilled) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventOrderPartiallyFilled) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventOrderPartiallyFilled) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FeesFilled) > 0 { + for iNdEx := len(m.FeesFilled) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FeesFilled[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.AssetsFilled) > 0 { + for iNdEx := len(m.AssetsFilled) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AssetsFilled[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.OrderId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.OrderId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventOrderPartiallyFilled2) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventOrderPartiallyFilled2) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventOrderPartiallyFilled2) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FeesFilled) > 0 { + i -= len(m.FeesFilled) + copy(dAtA[i:], m.FeesFilled) + i = encodeVarintEvents(dAtA, i, uint64(len(m.FeesFilled))) + i-- + dAtA[i] = 0x1a + } + if len(m.AssetsFilled) > 0 { + i -= len(m.AssetsFilled) + copy(dAtA[i:], m.AssetsFilled) + i = encodeVarintEvents(dAtA, i, uint64(len(m.AssetsFilled))) + i-- + dAtA[i] = 0x12 + } + if m.OrderId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.OrderId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventMarketWithdraw) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventMarketWithdraw) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventMarketWithdraw) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.WithdrawnBy) > 0 { + i -= len(m.WithdrawnBy) + copy(dAtA[i:], m.WithdrawnBy) + i = encodeVarintEvents(dAtA, i, uint64(len(m.WithdrawnBy))) + i-- + dAtA[i] = 0x22 + } + if len(m.Destination) > 0 { + i -= len(m.Destination) + copy(dAtA[i:], m.Destination) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Destination))) + i-- + dAtA[i] = 0x1a + } + if len(m.AmountWithdrawn) > 0 { + for iNdEx := len(m.AmountWithdrawn) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AmountWithdrawn[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventMarketDetailsUpdated) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventMarketDetailsUpdated) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventMarketDetailsUpdated) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UpdatedBy) > 0 { + i -= len(m.UpdatedBy) + copy(dAtA[i:], m.UpdatedBy) + i = encodeVarintEvents(dAtA, i, uint64(len(m.UpdatedBy))) + i-- + dAtA[i] = 0x12 + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventMarketEnabled) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventMarketEnabled) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventMarketEnabled) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UpdatedBy) > 0 { + i -= len(m.UpdatedBy) + copy(dAtA[i:], m.UpdatedBy) + i = encodeVarintEvents(dAtA, i, uint64(len(m.UpdatedBy))) + i-- + dAtA[i] = 0x12 + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventMarketDisabled) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventMarketDisabled) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventMarketDisabled) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UpdatedBy) > 0 { + i -= len(m.UpdatedBy) + copy(dAtA[i:], m.UpdatedBy) + i = encodeVarintEvents(dAtA, i, uint64(len(m.UpdatedBy))) + i-- + dAtA[i] = 0x12 + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventMarketUserSettleUpdated) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventMarketUserSettleUpdated) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventMarketUserSettleUpdated) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UpdatedBy) > 0 { + i -= len(m.UpdatedBy) + copy(dAtA[i:], m.UpdatedBy) + i = encodeVarintEvents(dAtA, i, uint64(len(m.UpdatedBy))) + i-- + dAtA[i] = 0x12 + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventMarketPermissionsUpdated) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventMarketPermissionsUpdated) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventMarketPermissionsUpdated) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UpdatedBy) > 0 { + i -= len(m.UpdatedBy) + copy(dAtA[i:], m.UpdatedBy) + i = encodeVarintEvents(dAtA, i, uint64(len(m.UpdatedBy))) + i-- + dAtA[i] = 0x12 + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventMarketReqAttrUpdated) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventMarketReqAttrUpdated) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventMarketReqAttrUpdated) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UpdatedBy) > 0 { + i -= len(m.UpdatedBy) + copy(dAtA[i:], m.UpdatedBy) + i = encodeVarintEvents(dAtA, i, uint64(len(m.UpdatedBy))) + i-- + dAtA[i] = 0x12 + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventCreateMarketSubmitted) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventCreateMarketSubmitted) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventCreateMarketSubmitted) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.SubmittedBy) > 0 { + i -= len(m.SubmittedBy) + copy(dAtA[i:], m.SubmittedBy) + i = encodeVarintEvents(dAtA, i, uint64(len(m.SubmittedBy))) + i-- + dAtA[i] = 0x1a + } + if m.ProposalId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.ProposalId)) + i-- + dAtA[i] = 0x10 + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventMarketCreated) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventMarketCreated) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventMarketCreated) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventMarketFeesUpdated) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventMarketFeesUpdated) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventMarketFeesUpdated) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventParamsUpdated) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventParamsUpdated) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventParamsUpdated) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { + offset -= sovEvents(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *EventOrderCreated) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OrderId != 0 { + n += 1 + sovEvents(uint64(m.OrderId)) + } + l = len(m.OrderType) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventOrderCancelled) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OrderId != 0 { + n += 1 + sovEvents(uint64(m.OrderId)) + } + l = len(m.CancelledBy) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventOrderFilled) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OrderId != 0 { + n += 1 + sovEvents(uint64(m.OrderId)) + } + return n +} + +func (m *EventOrderPartiallyFilled) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OrderId != 0 { + n += 1 + sovEvents(uint64(m.OrderId)) + } + if len(m.AssetsFilled) > 0 { + for _, e := range m.AssetsFilled { + l = e.Size() + n += 1 + l + sovEvents(uint64(l)) + } + } + if len(m.FeesFilled) > 0 { + for _, e := range m.FeesFilled { + l = e.Size() + n += 1 + l + sovEvents(uint64(l)) + } + } + return n +} + +func (m *EventOrderPartiallyFilled2) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OrderId != 0 { + n += 1 + sovEvents(uint64(m.OrderId)) + } + l = len(m.AssetsFilled) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.FeesFilled) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventMarketWithdraw) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } + if len(m.AmountWithdrawn) > 0 { + for _, e := range m.AmountWithdrawn { + l = e.Size() + n += 1 + l + sovEvents(uint64(l)) + } + } + l = len(m.Destination) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.WithdrawnBy) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventMarketDetailsUpdated) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } + l = len(m.UpdatedBy) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventMarketEnabled) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } + l = len(m.UpdatedBy) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventMarketDisabled) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } + l = len(m.UpdatedBy) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventMarketUserSettleUpdated) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } + l = len(m.UpdatedBy) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventMarketPermissionsUpdated) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } + l = len(m.UpdatedBy) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventMarketReqAttrUpdated) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } + l = len(m.UpdatedBy) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventCreateMarketSubmitted) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } + if m.ProposalId != 0 { + n += 1 + sovEvents(uint64(m.ProposalId)) + } + l = len(m.SubmittedBy) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventMarketCreated) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } + return n +} + +func (m *EventMarketFeesUpdated) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } + return n +} + +func (m *EventParamsUpdated) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovEvents(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozEvents(x uint64) (n int) { + return sovEvents(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *EventOrderCreated) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventOrderCreated: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventOrderCreated: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) + } + m.OrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OrderType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventOrderCancelled) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventOrderCancelled: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventOrderCancelled: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) + } + m.OrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CancelledBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CancelledBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventOrderFilled) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventOrderFilled: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventOrderFilled: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) + } + m.OrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventOrderPartiallyFilled) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventOrderPartiallyFilled: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventOrderPartiallyFilled: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) + } + m.OrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetsFilled", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AssetsFilled = append(m.AssetsFilled, types.Coin{}) + if err := m.AssetsFilled[len(m.AssetsFilled)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeesFilled", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeesFilled = append(m.FeesFilled, types.Coin{}) + if err := m.FeesFilled[len(m.FeesFilled)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventOrderPartiallyFilled2) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventOrderPartiallyFilled2: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventOrderPartiallyFilled2: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) + } + m.OrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetsFilled", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AssetsFilled = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeesFilled", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeesFilled = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventMarketWithdraw) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventMarketWithdraw: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventMarketWithdraw: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AmountWithdrawn", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AmountWithdrawn = append(m.AmountWithdrawn, types.Coin{}) + if err := m.AmountWithdrawn[len(m.AmountWithdrawn)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Destination", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Destination = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WithdrawnBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.WithdrawnBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventMarketDetailsUpdated) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventMarketDetailsUpdated: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventMarketDetailsUpdated: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdatedBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UpdatedBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventMarketEnabled) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventMarketEnabled: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventMarketEnabled: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdatedBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UpdatedBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventMarketDisabled) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventMarketDisabled: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventMarketDisabled: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdatedBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UpdatedBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventMarketUserSettleUpdated) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventMarketUserSettleUpdated: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventMarketUserSettleUpdated: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdatedBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UpdatedBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventMarketPermissionsUpdated) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventMarketPermissionsUpdated: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventMarketPermissionsUpdated: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdatedBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UpdatedBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventMarketReqAttrUpdated) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventMarketReqAttrUpdated: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventMarketReqAttrUpdated: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdatedBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UpdatedBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventCreateMarketSubmitted) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventCreateMarketSubmitted: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventCreateMarketSubmitted: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProposalId", wireType) + } + m.ProposalId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProposalId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubmittedBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SubmittedBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventMarketCreated) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventMarketCreated: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventMarketCreated: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventMarketFeesUpdated) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventMarketFeesUpdated: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventMarketFeesUpdated: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventParamsUpdated) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventParamsUpdated: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventParamsUpdated: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipEvents(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvents + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvents + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvents + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthEvents + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEvents + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthEvents + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthEvents = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEvents = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEvents = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go new file mode 100644 index 0000000000..50a8697292 --- /dev/null +++ b/x/exchange/market.pb.go @@ -0,0 +1,2210 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: provenance/exchange/v1/market.proto + +package exchange + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types1 "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/x/auth/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Permission defines the different types of permission that can be given to an account for a market. +type Permission int32 + +const ( + // PERMISSION_UNSPECIFIED is the zero-value Permission; it is an error to use it. + Permission_unspecified Permission = 0 + // PERMISSION_SETTLE is the ability to use the Settle Tx endpoint on behalf of a market. + Permission_settle Permission = 1 + // PERMISSION_CANCEL is the ability to use the Cancel Tx endpoint on behalf of a market. + Permission_cancel Permission = 2 + // PERMISSION_WITHDRAW is the ability to use the MarketWithdraw Tx endpoint. + Permission_withdraw Permission = 3 + // PERMISSION_UPDATE is the ability to use the MarketUpdate* Tx endpoints. + Permission_update Permission = 4 + // PERMISSION_PERMISSIONS is the ability to use the MarketManagePermissions Tx endpoint. + Permission_permissions Permission = 5 + // PERMISSION_ATTRIBUTES is the ability to use the MarketManageReqAttrs Tx endpoint. + Permission_attributes Permission = 6 +) + +var Permission_name = map[int32]string{ + 0: "PERMISSION_UNSPECIFIED", + 1: "PERMISSION_SETTLE", + 2: "PERMISSION_CANCEL", + 3: "PERMISSION_WITHDRAW", + 4: "PERMISSION_UPDATE", + 5: "PERMISSION_PERMISSIONS", + 6: "PERMISSION_ATTRIBUTES", +} + +var Permission_value = map[string]int32{ + "PERMISSION_UNSPECIFIED": 0, + "PERMISSION_SETTLE": 1, + "PERMISSION_CANCEL": 2, + "PERMISSION_WITHDRAW": 3, + "PERMISSION_UPDATE": 4, + "PERMISSION_PERMISSIONS": 5, + "PERMISSION_ATTRIBUTES": 6, +} + +func (x Permission) String() string { + return proto.EnumName(Permission_name, int32(x)) +} + +func (Permission) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_d5cf198f1dd7e167, []int{0} +} + +// MarketAccount is an account type for use with the accounts module to hold some basic information about a market. +type MarketAccount struct { + // base_account is the base cosmos account information. + *types.BaseAccount `protobuf:"bytes,1,opt,name=base_account,json=baseAccount,proto3,embedded=base_account" json:"base_account,omitempty" yaml:"base_account"` + // market_id is the numerical identifier for this market. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // market_details is some human-consumable information about this market. + MarketDetails MarketDetails `protobuf:"bytes,3,opt,name=market_details,json=marketDetails,proto3" json:"market_details"` +} + +func (m *MarketAccount) Reset() { *m = MarketAccount{} } +func (*MarketAccount) ProtoMessage() {} +func (*MarketAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_d5cf198f1dd7e167, []int{0} +} +func (m *MarketAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MarketAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MarketAccount.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MarketAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_MarketAccount.Merge(m, src) +} +func (m *MarketAccount) XXX_Size() int { + return m.Size() +} +func (m *MarketAccount) XXX_DiscardUnknown() { + xxx_messageInfo_MarketAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_MarketAccount proto.InternalMessageInfo + +// MarketDetails contains information about a market. +type MarketDetails struct { + // name is a moniker that people can use to refer to this market. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // description extra information about this market. The field is meant to be human-readable. + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + // website_url is a url people can use to get to this market, or at least get more information about this market. + WebsiteUrl string `protobuf:"bytes,3,opt,name=website_url,json=websiteUrl,proto3" json:"website_url,omitempty"` + // icon_uri is a uri for an icon to associate with this market. + IconUri string `protobuf:"bytes,4,opt,name=icon_uri,json=iconUri,proto3" json:"icon_uri,omitempty"` +} + +func (m *MarketDetails) Reset() { *m = MarketDetails{} } +func (m *MarketDetails) String() string { return proto.CompactTextString(m) } +func (*MarketDetails) ProtoMessage() {} +func (*MarketDetails) Descriptor() ([]byte, []int) { + return fileDescriptor_d5cf198f1dd7e167, []int{1} +} +func (m *MarketDetails) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MarketDetails) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MarketDetails.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MarketDetails) XXX_Merge(src proto.Message) { + xxx_messageInfo_MarketDetails.Merge(m, src) +} +func (m *MarketDetails) XXX_Size() int { + return m.Size() +} +func (m *MarketDetails) XXX_DiscardUnknown() { + xxx_messageInfo_MarketDetails.DiscardUnknown(m) +} + +var xxx_messageInfo_MarketDetails proto.InternalMessageInfo + +func (m *MarketDetails) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *MarketDetails) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *MarketDetails) GetWebsiteUrl() string { + if m != nil { + return m.WebsiteUrl + } + return "" +} + +func (m *MarketDetails) GetIconUri() string { + if m != nil { + return m.IconUri + } + return "" +} + +// Market contains all information about a market. +type Market struct { + // market_id is the numerical identifier for this market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // market_details is some information about this market. + MarketDetails MarketDetails `protobuf:"bytes,2,opt,name=market_details,json=marketDetails,proto3" json:"market_details"` + // fee_create_ask_flat is the flat fee charged for creating an ask order. + // Each coin entry is a separate option. When an ask is created, one of these must be paid. + // If empty, no fee is required to create an ask order. + FeeCreateAskFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=fee_create_ask_flat,json=feeCreateAskFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"fee_create_ask_flat"` + // fee_create_bid_flat is the flat fee charged for creating a bid order. + // Each coin entry is a separate option. When a bid is created, one of these must be paid. + // If empty, no fee is required to create a bid order. + FeeCreateBidFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=fee_create_bid_flat,json=feeCreateBidFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"fee_create_bid_flat"` + // fee_settlement_seller_flat is the flat fee charged to the seller during settlement. + // Each coin entry is a separate option. + // When an ask is settled, the seller will pay the amount in the denom that matches the price they received. + FeeSettlementSellerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,5,rep,name=fee_settlement_seller_flat,json=feeSettlementSellerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"fee_settlement_seller_flat"` + // fee_settlement_seller_ratios is the fee to charge a seller during settlement based on the price they are receiving. + // The price and fee denoms must be equal for each entry, and only one entry for any given denom is allowed. + FeeSettlementSellerRatios []*FeeRatio `protobuf:"bytes,6,rep,name=fee_settlement_seller_ratios,json=feeSettlementSellerRatios,proto3" json:"fee_settlement_seller_ratios,omitempty"` + // fee_settlement_buyer_flat is the flat fee charged to the buyer during settlement. + // Each coin entry is a separate option. + // When a bid is created, the settlement fees provided must contain one of these. + FeeSettlementBuyerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,7,rep,name=fee_settlement_buyer_flat,json=feeSettlementBuyerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"fee_settlement_buyer_flat"` + // fee_settlement_buyer_ratios is the fee to charge a buyer during settlement based on the price they are spending. + // The price and fee denoms do not have to equal. Multiple entries for any given price or fee denom are allowed, but + // each price denom to fee denom pair can only have one entry. + FeeSettlementBuyerRatios []*FeeRatio `protobuf:"bytes,8,rep,name=fee_settlement_buyer_ratios,json=feeSettlementBuyerRatios,proto3" json:"fee_settlement_buyer_ratios,omitempty"` + // accepting_orders is whether this market is allowing orders to be created for it. + AcceptingOrders bool `protobuf:"varint,9,opt,name=accepting_orders,json=acceptingOrders,proto3" json:"accepting_orders,omitempty"` + // allow_user_settlement is whether this market allows users to initiate their own settlements. + // For example, the FillBids and FillAsks endpoints are available if and only if this is true. + // The MarketSettle endpoint is only available to market actors regardless of the value of this field. + AllowUserSettlement bool `protobuf:"varint,10,opt,name=allow_user_settlement,json=allowUserSettlement,proto3" json:"allow_user_settlement,omitempty"` + // access_grants is the list of addresses and permissions granted for this market. + AccessGrants []*AccessGrant `protobuf:"bytes,11,rep,name=access_grants,json=accessGrants,proto3" json:"access_grants,omitempty"` + // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. + // TODO[1658]: Add comment about wildcards. + ReqAttrCreateAsk []string `protobuf:"bytes,12,rep,name=req_attr_create_ask,json=reqAttrCreateAsk,proto3" json:"req_attr_create_ask,omitempty"` + // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. + // TODO[1658]: Add comment about wildcards. + ReqAttrCreateBid []string `protobuf:"bytes,13,rep,name=req_attr_create_bid,json=reqAttrCreateBid,proto3" json:"req_attr_create_bid,omitempty"` +} + +func (m *Market) Reset() { *m = Market{} } +func (m *Market) String() string { return proto.CompactTextString(m) } +func (*Market) ProtoMessage() {} +func (*Market) Descriptor() ([]byte, []int) { + return fileDescriptor_d5cf198f1dd7e167, []int{2} +} +func (m *Market) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Market) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Market.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Market) XXX_Merge(src proto.Message) { + xxx_messageInfo_Market.Merge(m, src) +} +func (m *Market) XXX_Size() int { + return m.Size() +} +func (m *Market) XXX_DiscardUnknown() { + xxx_messageInfo_Market.DiscardUnknown(m) +} + +var xxx_messageInfo_Market proto.InternalMessageInfo + +func (m *Market) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *Market) GetMarketDetails() MarketDetails { + if m != nil { + return m.MarketDetails + } + return MarketDetails{} +} + +func (m *Market) GetFeeCreateAskFlat() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.FeeCreateAskFlat + } + return nil +} + +func (m *Market) GetFeeCreateBidFlat() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.FeeCreateBidFlat + } + return nil +} + +func (m *Market) GetFeeSettlementSellerFlat() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.FeeSettlementSellerFlat + } + return nil +} + +func (m *Market) GetFeeSettlementSellerRatios() []*FeeRatio { + if m != nil { + return m.FeeSettlementSellerRatios + } + return nil +} + +func (m *Market) GetFeeSettlementBuyerFlat() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.FeeSettlementBuyerFlat + } + return nil +} + +func (m *Market) GetFeeSettlementBuyerRatios() []*FeeRatio { + if m != nil { + return m.FeeSettlementBuyerRatios + } + return nil +} + +func (m *Market) GetAcceptingOrders() bool { + if m != nil { + return m.AcceptingOrders + } + return false +} + +func (m *Market) GetAllowUserSettlement() bool { + if m != nil { + return m.AllowUserSettlement + } + return false +} + +func (m *Market) GetAccessGrants() []*AccessGrant { + if m != nil { + return m.AccessGrants + } + return nil +} + +func (m *Market) GetReqAttrCreateAsk() []string { + if m != nil { + return m.ReqAttrCreateAsk + } + return nil +} + +func (m *Market) GetReqAttrCreateBid() []string { + if m != nil { + return m.ReqAttrCreateBid + } + return nil +} + +// FeeRatio defines a ratio of price amount to fee amount. +// For an order to be valid, its price must be evenly divisible by a FeeRatio's price. +type FeeRatio struct { + // price is the unit the order price is divided by to get how much of the fee should apply. + Price *types1.Coin `protobuf:"bytes,1,opt,name=price,proto3" json:"price,omitempty"` + // fee is the amount to charge per price unit. + Fee *types1.Coin `protobuf:"bytes,2,opt,name=fee,proto3" json:"fee,omitempty"` +} + +func (m *FeeRatio) Reset() { *m = FeeRatio{} } +func (m *FeeRatio) String() string { return proto.CompactTextString(m) } +func (*FeeRatio) ProtoMessage() {} +func (*FeeRatio) Descriptor() ([]byte, []int) { + return fileDescriptor_d5cf198f1dd7e167, []int{3} +} +func (m *FeeRatio) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FeeRatio) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FeeRatio.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *FeeRatio) XXX_Merge(src proto.Message) { + xxx_messageInfo_FeeRatio.Merge(m, src) +} +func (m *FeeRatio) XXX_Size() int { + return m.Size() +} +func (m *FeeRatio) XXX_DiscardUnknown() { + xxx_messageInfo_FeeRatio.DiscardUnknown(m) +} + +var xxx_messageInfo_FeeRatio proto.InternalMessageInfo + +func (m *FeeRatio) GetPrice() *types1.Coin { + if m != nil { + return m.Price + } + return nil +} + +func (m *FeeRatio) GetFee() *types1.Coin { + if m != nil { + return m.Fee + } + return nil +} + +// AddrPermissions associates an address with a list of permissions available for that address. +type AccessGrant struct { + // address is the address that these permissions apply to. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // allowed is the list of permissions available for the address. + Permissions []Permission `protobuf:"varint,2,rep,packed,name=permissions,proto3,enum=provenance.exchange.v1.Permission" json:"permissions,omitempty"` +} + +func (m *AccessGrant) Reset() { *m = AccessGrant{} } +func (m *AccessGrant) String() string { return proto.CompactTextString(m) } +func (*AccessGrant) ProtoMessage() {} +func (*AccessGrant) Descriptor() ([]byte, []int) { + return fileDescriptor_d5cf198f1dd7e167, []int{4} +} +func (m *AccessGrant) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AccessGrant) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AccessGrant.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AccessGrant) XXX_Merge(src proto.Message) { + xxx_messageInfo_AccessGrant.Merge(m, src) +} +func (m *AccessGrant) XXX_Size() int { + return m.Size() +} +func (m *AccessGrant) XXX_DiscardUnknown() { + xxx_messageInfo_AccessGrant.DiscardUnknown(m) +} + +var xxx_messageInfo_AccessGrant proto.InternalMessageInfo + +func (m *AccessGrant) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *AccessGrant) GetPermissions() []Permission { + if m != nil { + return m.Permissions + } + return nil +} + +func init() { + proto.RegisterEnum("provenance.exchange.v1.Permission", Permission_name, Permission_value) + proto.RegisterType((*MarketAccount)(nil), "provenance.exchange.v1.MarketAccount") + proto.RegisterType((*MarketDetails)(nil), "provenance.exchange.v1.MarketDetails") + proto.RegisterType((*Market)(nil), "provenance.exchange.v1.Market") + proto.RegisterType((*FeeRatio)(nil), "provenance.exchange.v1.FeeRatio") + proto.RegisterType((*AccessGrant)(nil), "provenance.exchange.v1.AccessGrant") +} + +func init() { + proto.RegisterFile("provenance/exchange/v1/market.proto", fileDescriptor_d5cf198f1dd7e167) +} + +var fileDescriptor_d5cf198f1dd7e167 = []byte{ + // 1003 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0xf6, 0xc6, 0xf9, 0x61, 0x8f, 0x93, 0xd4, 0x4c, 0xda, 0xb0, 0x76, 0x90, 0xbd, 0xb8, 0xaa, + 0xe4, 0x52, 0xc5, 0x26, 0xe1, 0xd6, 0x9b, 0x9d, 0x38, 0xd4, 0x52, 0x9b, 0x46, 0x6b, 0x5b, 0x95, + 0xb8, 0x2c, 0xb3, 0xbb, 0xcf, 0xce, 0x28, 0xeb, 0x5d, 0x77, 0x66, 0x36, 0x69, 0xb8, 0x71, 0x81, + 0x2a, 0x27, 0x8e, 0x5c, 0x22, 0x95, 0x2b, 0x67, 0xfe, 0x88, 0x1e, 0x23, 0x4e, 0x9c, 0x02, 0x4a, + 0xae, 0x9c, 0xf8, 0x0b, 0xd0, 0xce, 0x6c, 0xec, 0x6d, 0xb0, 0x01, 0x89, 0x72, 0xf2, 0xbe, 0xf7, + 0x7d, 0xf3, 0xbe, 0xef, 0xbd, 0xf9, 0x21, 0xa3, 0xfb, 0x23, 0x16, 0x1c, 0x83, 0x4f, 0x7c, 0x07, + 0xea, 0xf0, 0xca, 0x39, 0x24, 0xfe, 0x00, 0xea, 0xc7, 0x5b, 0xf5, 0x21, 0x61, 0x47, 0x20, 0x6a, + 0x23, 0x16, 0x88, 0x00, 0xaf, 0x4f, 0x48, 0xb5, 0x1b, 0x52, 0xed, 0x78, 0xab, 0x58, 0x72, 0x02, + 0x3e, 0x0c, 0x78, 0x9d, 0x84, 0xe2, 0xb0, 0x7e, 0xbc, 0x65, 0x83, 0x20, 0x5b, 0x32, 0x50, 0xeb, + 0xc6, 0xb8, 0x4d, 0x38, 0x8c, 0x71, 0x27, 0xa0, 0x7e, 0x8c, 0x17, 0x14, 0x6e, 0xc9, 0xa8, 0xae, + 0x82, 0x18, 0xba, 0x3b, 0x08, 0x06, 0x81, 0xca, 0x47, 0x5f, 0x2a, 0x5b, 0xf9, 0x5d, 0x43, 0x2b, + 0xcf, 0xa4, 0xb3, 0x86, 0xe3, 0x04, 0xa1, 0x2f, 0xf0, 0x97, 0x68, 0x39, 0xaa, 0x6e, 0x11, 0x15, + 0xeb, 0x9a, 0xa1, 0x55, 0x73, 0xdb, 0x46, 0x2d, 0x2e, 0x26, 0xcd, 0xc4, 0xca, 0xb5, 0x26, 0xe1, + 0x10, 0xaf, 0x6b, 0x6e, 0x5c, 0x5c, 0x96, 0xb5, 0x3f, 0x2e, 0xcb, 0x6b, 0xa7, 0x64, 0xe8, 0x3d, + 0xae, 0x24, 0x6b, 0x54, 0xcc, 0x9c, 0x3d, 0x61, 0xe2, 0x0d, 0x94, 0x55, 0xc3, 0xb0, 0xa8, 0xab, + 0xcf, 0x19, 0x5a, 0x75, 0xc5, 0xcc, 0xa8, 0x44, 0xdb, 0xc5, 0x26, 0x5a, 0x8d, 0x41, 0x17, 0x04, + 0xa1, 0x1e, 0xd7, 0xd3, 0xd2, 0xc0, 0x83, 0xda, 0xf4, 0x91, 0xd5, 0x94, 0xfb, 0x5d, 0x45, 0x6e, + 0xce, 0xbf, 0xbd, 0x2c, 0xa7, 0xcc, 0x95, 0x61, 0x32, 0xf9, 0x38, 0xf3, 0xfa, 0x4d, 0x39, 0xf5, + 0xfd, 0x9b, 0x72, 0xaa, 0xf2, 0xf5, 0xb8, 0xdd, 0x18, 0xc3, 0x18, 0xcd, 0xfb, 0x64, 0x08, 0xb2, + 0xcd, 0xac, 0x29, 0xbf, 0xb1, 0x81, 0x72, 0x2e, 0x70, 0x87, 0xd1, 0x91, 0xa0, 0x81, 0x2f, 0x2d, + 0x66, 0xcd, 0x64, 0x0a, 0x97, 0x51, 0xee, 0x04, 0x6c, 0x4e, 0x05, 0x58, 0x21, 0xf3, 0xa4, 0xc5, + 0xac, 0x89, 0xe2, 0x54, 0x8f, 0x79, 0xb8, 0x80, 0x32, 0xd4, 0x09, 0x7c, 0x2b, 0x64, 0x54, 0x9f, + 0x97, 0xe8, 0x52, 0x14, 0xf7, 0x18, 0xad, 0x5c, 0x64, 0xd0, 0xa2, 0xf2, 0xf0, 0xee, 0x24, 0xb4, + 0x7f, 0x9c, 0xc4, 0xdc, 0x7f, 0x9d, 0x04, 0xfe, 0x0a, 0xad, 0xf5, 0x01, 0x2c, 0x87, 0x01, 0x11, + 0x60, 0x11, 0x7e, 0x64, 0xf5, 0x3d, 0x22, 0xf4, 0xb4, 0x91, 0xae, 0xe6, 0xb6, 0x0b, 0x37, 0x7b, + 0x1c, 0x6d, 0xd6, 0x78, 0x8f, 0x77, 0x02, 0xea, 0x37, 0x3f, 0x8d, 0x8a, 0xfd, 0xf8, 0x6b, 0xb9, + 0x3a, 0xa0, 0xe2, 0x30, 0xb4, 0x6b, 0x4e, 0x30, 0x8c, 0x4f, 0x57, 0xfc, 0xb3, 0xc9, 0xdd, 0xa3, + 0xba, 0x38, 0x1d, 0x01, 0x97, 0x0b, 0xb8, 0x99, 0xef, 0x03, 0xec, 0x48, 0x99, 0x06, 0x3f, 0xda, + 0xf3, 0x88, 0xb8, 0xa5, 0x6d, 0x53, 0x57, 0x69, 0xcf, 0xff, 0x9f, 0xda, 0x4d, 0xea, 0x4a, 0xed, + 0xd7, 0x1a, 0x2a, 0x46, 0xe2, 0x1c, 0x84, 0xf0, 0x60, 0x08, 0xbe, 0xb0, 0x38, 0x78, 0x1e, 0x30, + 0xe5, 0x61, 0xe1, 0xfd, 0x7b, 0xf8, 0xb0, 0x0f, 0xd0, 0x19, 0xab, 0x75, 0xa4, 0x98, 0xb4, 0x42, + 0xd0, 0x47, 0xd3, 0x9d, 0x30, 0x22, 0x68, 0xc0, 0xf5, 0x45, 0xe9, 0xc5, 0x98, 0xb5, 0xc9, 0x7b, + 0x00, 0x66, 0x44, 0x34, 0x0b, 0x53, 0x04, 0x24, 0xc2, 0xf1, 0x37, 0x1a, 0x2a, 0xdc, 0xd2, 0xb0, + 0xc3, 0xd3, 0x9b, 0x66, 0x97, 0xde, 0x7f, 0xb3, 0xeb, 0xef, 0x78, 0x69, 0x46, 0x5a, 0xb2, 0x57, + 0x0b, 0x6d, 0x4c, 0xf5, 0x11, 0xb7, 0x9a, 0xf9, 0x97, 0xad, 0xea, 0x7f, 0x2d, 0x1f, 0x77, 0xfa, + 0x10, 0xe5, 0x89, 0xe3, 0xc0, 0x48, 0x50, 0x7f, 0x60, 0x05, 0xcc, 0x05, 0xc6, 0xf5, 0xac, 0xa1, + 0x55, 0x33, 0xe6, 0x9d, 0x71, 0xfe, 0xb9, 0x4c, 0xe3, 0x6d, 0x74, 0x8f, 0x78, 0x5e, 0x70, 0x62, + 0x85, 0x1c, 0x58, 0xc2, 0x92, 0x8e, 0x24, 0x7f, 0x4d, 0x82, 0x3d, 0x0e, 0x6c, 0xa2, 0x84, 0x9f, + 0xa0, 0x95, 0xa8, 0x0c, 0xe7, 0xd6, 0x80, 0x11, 0x5f, 0x70, 0x3d, 0x27, 0x1d, 0xdf, 0x9f, 0xe5, + 0xb8, 0x21, 0xc9, 0x9f, 0x47, 0x5c, 0x73, 0x99, 0x4c, 0x02, 0x8e, 0x37, 0xd1, 0x1a, 0x83, 0x97, + 0x16, 0x11, 0x82, 0x25, 0x6e, 0x9f, 0xbe, 0x6c, 0xa4, 0xab, 0x59, 0x33, 0xcf, 0xe0, 0x65, 0x43, + 0x08, 0x36, 0xbe, 0x2f, 0xd3, 0xe8, 0x36, 0x75, 0xf5, 0x95, 0x29, 0xf4, 0x26, 0x75, 0x2b, 0x87, + 0x28, 0x73, 0x33, 0x2c, 0x5c, 0x47, 0x0b, 0x23, 0x46, 0x1d, 0x88, 0x1f, 0xee, 0xd9, 0xfb, 0x6c, + 0x2a, 0x1e, 0x7e, 0x84, 0xd2, 0x7d, 0x80, 0xf8, 0x71, 0xf9, 0x1b, 0x7a, 0xc4, 0xaa, 0x7c, 0xab, + 0xa1, 0x5c, 0xa2, 0x4b, 0xbc, 0x8d, 0x96, 0x88, 0xeb, 0x32, 0xe0, 0x5c, 0xbd, 0xa0, 0x4d, 0xfd, + 0xe7, 0x9f, 0x36, 0xef, 0xc6, 0x35, 0x1a, 0x0a, 0xe9, 0x08, 0x46, 0xfd, 0x81, 0x79, 0x43, 0xc4, + 0xbb, 0x28, 0x37, 0x02, 0x36, 0xa4, 0x9c, 0xd3, 0xc0, 0x8f, 0x5e, 0xb5, 0x74, 0x75, 0x75, 0xbb, + 0x32, 0x6b, 0xa6, 0x07, 0x63, 0xaa, 0x99, 0x5c, 0xf6, 0xc9, 0x0f, 0x73, 0x08, 0x4d, 0x30, 0xfc, + 0x08, 0xad, 0x1f, 0xb4, 0xcc, 0x67, 0xed, 0x4e, 0xa7, 0xfd, 0x7c, 0xdf, 0xea, 0xed, 0x77, 0x0e, + 0x5a, 0x3b, 0xed, 0xbd, 0x76, 0x6b, 0x37, 0x9f, 0x2a, 0xde, 0x39, 0x3b, 0x37, 0x72, 0xa1, 0xcf, + 0x47, 0xe0, 0xd0, 0x3e, 0x05, 0x17, 0x7f, 0x8c, 0x3e, 0x48, 0x90, 0x3b, 0xad, 0x6e, 0xf7, 0x69, + 0x2b, 0xaf, 0x15, 0xd1, 0xd9, 0xb9, 0xb1, 0xa8, 0x4e, 0xc6, 0x2d, 0xca, 0x4e, 0x63, 0x7f, 0xa7, + 0xf5, 0x34, 0x3f, 0xa7, 0x28, 0x4e, 0x64, 0xd2, 0xc3, 0x0f, 0xd0, 0x5a, 0x82, 0xf2, 0xa2, 0xdd, + 0x7d, 0xb2, 0x6b, 0x36, 0x5e, 0xe4, 0xd3, 0xc5, 0xe5, 0xb3, 0x73, 0x23, 0x73, 0x42, 0xc5, 0xa1, + 0xcb, 0xc8, 0xc9, 0xad, 0x4a, 0xbd, 0x83, 0xdd, 0x46, 0xb7, 0x95, 0x9f, 0x57, 0x95, 0xc2, 0x91, + 0x4b, 0x04, 0xdc, 0x32, 0x3f, 0xf9, 0xec, 0xe4, 0x17, 0x94, 0xf9, 0x44, 0xe3, 0xf8, 0x21, 0xba, + 0x97, 0x20, 0x37, 0xba, 0x5d, 0xb3, 0xdd, 0xec, 0x75, 0x5b, 0x9d, 0xfc, 0x62, 0x71, 0xf5, 0xec, + 0xdc, 0x40, 0xd1, 0xa1, 0xa1, 0x76, 0x28, 0x80, 0x37, 0xe1, 0xed, 0x55, 0x49, 0xbb, 0xb8, 0x2a, + 0x69, 0xbf, 0x5d, 0x95, 0xb4, 0xef, 0xae, 0x4b, 0xa9, 0x8b, 0xeb, 0x52, 0xea, 0x97, 0xeb, 0x52, + 0x0a, 0x15, 0x68, 0x30, 0x63, 0xe0, 0x07, 0xda, 0x17, 0xb5, 0xc4, 0xc5, 0x9f, 0x90, 0x36, 0x69, + 0x90, 0x88, 0xea, 0xaf, 0xc6, 0xff, 0x6e, 0xec, 0x45, 0xf9, 0x5f, 0xe2, 0xb3, 0x3f, 0x03, 0x00, + 0x00, 0xff, 0xff, 0x53, 0xb1, 0xc9, 0x54, 0xfb, 0x08, 0x00, 0x00, +} + +func (m *MarketAccount) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MarketAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MarketAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.MarketDetails.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.MarketId != 0 { + i = encodeVarintMarket(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } + if m.BaseAccount != nil { + { + size, err := m.BaseAccount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MarketDetails) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MarketDetails) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MarketDetails) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.IconUri) > 0 { + i -= len(m.IconUri) + copy(dAtA[i:], m.IconUri) + i = encodeVarintMarket(dAtA, i, uint64(len(m.IconUri))) + i-- + dAtA[i] = 0x22 + } + if len(m.WebsiteUrl) > 0 { + i -= len(m.WebsiteUrl) + copy(dAtA[i:], m.WebsiteUrl) + i = encodeVarintMarket(dAtA, i, uint64(len(m.WebsiteUrl))) + i-- + dAtA[i] = 0x1a + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintMarket(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintMarket(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Market) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Market) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Market) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ReqAttrCreateBid) > 0 { + for iNdEx := len(m.ReqAttrCreateBid) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ReqAttrCreateBid[iNdEx]) + copy(dAtA[i:], m.ReqAttrCreateBid[iNdEx]) + i = encodeVarintMarket(dAtA, i, uint64(len(m.ReqAttrCreateBid[iNdEx]))) + i-- + dAtA[i] = 0x6a + } + } + if len(m.ReqAttrCreateAsk) > 0 { + for iNdEx := len(m.ReqAttrCreateAsk) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ReqAttrCreateAsk[iNdEx]) + copy(dAtA[i:], m.ReqAttrCreateAsk[iNdEx]) + i = encodeVarintMarket(dAtA, i, uint64(len(m.ReqAttrCreateAsk[iNdEx]))) + i-- + dAtA[i] = 0x62 + } + } + if len(m.AccessGrants) > 0 { + for iNdEx := len(m.AccessGrants) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AccessGrants[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } + } + if m.AllowUserSettlement { + i-- + if m.AllowUserSettlement { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x50 + } + if m.AcceptingOrders { + i-- + if m.AcceptingOrders { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x48 + } + if len(m.FeeSettlementBuyerRatios) > 0 { + for iNdEx := len(m.FeeSettlementBuyerRatios) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FeeSettlementBuyerRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } + if len(m.FeeSettlementBuyerFlat) > 0 { + for iNdEx := len(m.FeeSettlementBuyerFlat) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FeeSettlementBuyerFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if len(m.FeeSettlementSellerRatios) > 0 { + for iNdEx := len(m.FeeSettlementSellerRatios) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FeeSettlementSellerRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if len(m.FeeSettlementSellerFlat) > 0 { + for iNdEx := len(m.FeeSettlementSellerFlat) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FeeSettlementSellerFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.FeeCreateBidFlat) > 0 { + for iNdEx := len(m.FeeCreateBidFlat) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FeeCreateBidFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.FeeCreateAskFlat) > 0 { + for iNdEx := len(m.FeeCreateAskFlat) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FeeCreateAskFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + { + size, err := m.MarketDetails.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if m.MarketId != 0 { + i = encodeVarintMarket(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *FeeRatio) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FeeRatio) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FeeRatio) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Fee != nil { + { + size, err := m.Fee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Price != nil { + { + size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AccessGrant) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AccessGrant) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AccessGrant) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Permissions) > 0 { + dAtA7 := make([]byte, len(m.Permissions)*10) + var j6 int + for _, num := range m.Permissions { + for num >= 1<<7 { + dAtA7[j6] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j6++ + } + dAtA7[j6] = uint8(num) + j6++ + } + i -= j6 + copy(dAtA[i:], dAtA7[:j6]) + i = encodeVarintMarket(dAtA, i, uint64(j6)) + i-- + dAtA[i] = 0x12 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintMarket(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintMarket(dAtA []byte, offset int, v uint64) int { + offset -= sovMarket(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MarketAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BaseAccount != nil { + l = m.BaseAccount.Size() + n += 1 + l + sovMarket(uint64(l)) + } + if m.MarketId != 0 { + n += 1 + sovMarket(uint64(m.MarketId)) + } + l = m.MarketDetails.Size() + n += 1 + l + sovMarket(uint64(l)) + return n +} + +func (m *MarketDetails) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovMarket(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovMarket(uint64(l)) + } + l = len(m.WebsiteUrl) + if l > 0 { + n += 1 + l + sovMarket(uint64(l)) + } + l = len(m.IconUri) + if l > 0 { + n += 1 + l + sovMarket(uint64(l)) + } + return n +} + +func (m *Market) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovMarket(uint64(m.MarketId)) + } + l = m.MarketDetails.Size() + n += 1 + l + sovMarket(uint64(l)) + if len(m.FeeCreateAskFlat) > 0 { + for _, e := range m.FeeCreateAskFlat { + l = e.Size() + n += 1 + l + sovMarket(uint64(l)) + } + } + if len(m.FeeCreateBidFlat) > 0 { + for _, e := range m.FeeCreateBidFlat { + l = e.Size() + n += 1 + l + sovMarket(uint64(l)) + } + } + if len(m.FeeSettlementSellerFlat) > 0 { + for _, e := range m.FeeSettlementSellerFlat { + l = e.Size() + n += 1 + l + sovMarket(uint64(l)) + } + } + if len(m.FeeSettlementSellerRatios) > 0 { + for _, e := range m.FeeSettlementSellerRatios { + l = e.Size() + n += 1 + l + sovMarket(uint64(l)) + } + } + if len(m.FeeSettlementBuyerFlat) > 0 { + for _, e := range m.FeeSettlementBuyerFlat { + l = e.Size() + n += 1 + l + sovMarket(uint64(l)) + } + } + if len(m.FeeSettlementBuyerRatios) > 0 { + for _, e := range m.FeeSettlementBuyerRatios { + l = e.Size() + n += 1 + l + sovMarket(uint64(l)) + } + } + if m.AcceptingOrders { + n += 2 + } + if m.AllowUserSettlement { + n += 2 + } + if len(m.AccessGrants) > 0 { + for _, e := range m.AccessGrants { + l = e.Size() + n += 1 + l + sovMarket(uint64(l)) + } + } + if len(m.ReqAttrCreateAsk) > 0 { + for _, s := range m.ReqAttrCreateAsk { + l = len(s) + n += 1 + l + sovMarket(uint64(l)) + } + } + if len(m.ReqAttrCreateBid) > 0 { + for _, s := range m.ReqAttrCreateBid { + l = len(s) + n += 1 + l + sovMarket(uint64(l)) + } + } + return n +} + +func (m *FeeRatio) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Price != nil { + l = m.Price.Size() + n += 1 + l + sovMarket(uint64(l)) + } + if m.Fee != nil { + l = m.Fee.Size() + n += 1 + l + sovMarket(uint64(l)) + } + return n +} + +func (m *AccessGrant) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovMarket(uint64(l)) + } + if len(m.Permissions) > 0 { + l = 0 + for _, e := range m.Permissions { + l += sovMarket(uint64(e)) + } + n += 1 + sovMarket(uint64(l)) + l + } + return n +} + +func sovMarket(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMarket(x uint64) (n int) { + return sovMarket(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MarketAccount) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MarketAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MarketAccount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseAccount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BaseAccount == nil { + m.BaseAccount = &types.BaseAccount{} + } + if err := m.BaseAccount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketDetails", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MarketDetails.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMarket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMarket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MarketDetails) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MarketDetails: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MarketDetails: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WebsiteUrl", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.WebsiteUrl = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IconUri", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IconUri = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMarket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMarket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Market) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Market: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Market: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketDetails", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MarketDetails.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeCreateAskFlat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeeCreateAskFlat = append(m.FeeCreateAskFlat, types1.Coin{}) + if err := m.FeeCreateAskFlat[len(m.FeeCreateAskFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeCreateBidFlat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeeCreateBidFlat = append(m.FeeCreateBidFlat, types1.Coin{}) + if err := m.FeeCreateBidFlat[len(m.FeeCreateBidFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeSettlementSellerFlat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeeSettlementSellerFlat = append(m.FeeSettlementSellerFlat, types1.Coin{}) + if err := m.FeeSettlementSellerFlat[len(m.FeeSettlementSellerFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeSettlementSellerRatios", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeeSettlementSellerRatios = append(m.FeeSettlementSellerRatios, &FeeRatio{}) + if err := m.FeeSettlementSellerRatios[len(m.FeeSettlementSellerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeSettlementBuyerFlat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeeSettlementBuyerFlat = append(m.FeeSettlementBuyerFlat, types1.Coin{}) + if err := m.FeeSettlementBuyerFlat[len(m.FeeSettlementBuyerFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeSettlementBuyerRatios", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeeSettlementBuyerRatios = append(m.FeeSettlementBuyerRatios, &FeeRatio{}) + if err := m.FeeSettlementBuyerRatios[len(m.FeeSettlementBuyerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AcceptingOrders", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AcceptingOrders = bool(v != 0) + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowUserSettlement", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowUserSettlement = bool(v != 0) + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccessGrants", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AccessGrants = append(m.AccessGrants, &AccessGrant{}) + if err := m.AccessGrants[len(m.AccessGrants)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReqAttrCreateAsk", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ReqAttrCreateAsk = append(m.ReqAttrCreateAsk, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReqAttrCreateBid", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ReqAttrCreateBid = append(m.ReqAttrCreateBid, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMarket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMarket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FeeRatio) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FeeRatio: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FeeRatio: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Price", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Price == nil { + m.Price = &types1.Coin{} + } + if err := m.Price.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Fee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Fee == nil { + m.Fee = &types1.Coin{} + } + if err := m.Fee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMarket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMarket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AccessGrant) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AccessGrant: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AccessGrant: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType == 0 { + var v Permission + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= Permission(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Permissions = append(m.Permissions, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + if elementCount != 0 && len(m.Permissions) == 0 { + m.Permissions = make([]Permission, 0, elementCount) + } + for iNdEx < postIndex { + var v Permission + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= Permission(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Permissions = append(m.Permissions, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType) + } + default: + iNdEx = preIndex + skippy, err := skipMarket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMarket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMarket(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMarket + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMarket + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMarket + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthMarket + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMarket + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMarket + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMarket = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMarket = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMarket = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/exchange/orders.pb.go b/x/exchange/orders.pb.go new file mode 100644 index 0000000000..c5077d008b --- /dev/null +++ b/x/exchange/orders.pb.go @@ -0,0 +1,1409 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: provenance/exchange/v1/orders.proto + +package exchange + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Order associates an order id with one of the order types. +type Order struct { + // order_id is the numerical identifier for this order. + OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + // order is the specifics of this order. + // + // Types that are valid to be assigned to Order: + // *Order_AskOrder + // *Order_BidOrder + Order isOrder_Order `protobuf_oneof:"order"` +} + +func (m *Order) Reset() { *m = Order{} } +func (m *Order) String() string { return proto.CompactTextString(m) } +func (*Order) ProtoMessage() {} +func (*Order) Descriptor() ([]byte, []int) { + return fileDescriptor_dab7cbe63f582471, []int{0} +} +func (m *Order) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Order) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Order.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Order) XXX_Merge(src proto.Message) { + xxx_messageInfo_Order.Merge(m, src) +} +func (m *Order) XXX_Size() int { + return m.Size() +} +func (m *Order) XXX_DiscardUnknown() { + xxx_messageInfo_Order.DiscardUnknown(m) +} + +var xxx_messageInfo_Order proto.InternalMessageInfo + +type isOrder_Order interface { + isOrder_Order() + MarshalTo([]byte) (int, error) + Size() int +} + +type Order_AskOrder struct { + AskOrder *AskOrder `protobuf:"bytes,2,opt,name=ask_order,json=askOrder,proto3,oneof" json:"ask_order,omitempty"` +} +type Order_BidOrder struct { + BidOrder *BidOrder `protobuf:"bytes,3,opt,name=bid_order,json=bidOrder,proto3,oneof" json:"bid_order,omitempty"` +} + +func (*Order_AskOrder) isOrder_Order() {} +func (*Order_BidOrder) isOrder_Order() {} + +func (m *Order) GetOrder() isOrder_Order { + if m != nil { + return m.Order + } + return nil +} + +func (m *Order) GetOrderId() uint64 { + if m != nil { + return m.OrderId + } + return 0 +} + +func (m *Order) GetAskOrder() *AskOrder { + if x, ok := m.GetOrder().(*Order_AskOrder); ok { + return x.AskOrder + } + return nil +} + +func (m *Order) GetBidOrder() *BidOrder { + if x, ok := m.GetOrder().(*Order_BidOrder); ok { + return x.BidOrder + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*Order) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*Order_AskOrder)(nil), + (*Order_BidOrder)(nil), + } +} + +// AskOrder represents someone's desire to sell something at a minimum price. +type AskOrder struct { + // market_id identifies the market that this order belongs to. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // seller is the address of the account that owns this order and has the assets to sell. + Seller string `protobuf:"bytes,2,opt,name=seller,proto3" json:"seller,omitempty"` + // assets are the things that the seller wishes to sell. + // A hold is placed on this until the order is filled or cancelled. + Assets github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=assets,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"assets"` + // price is the minimum amount that the seller is willing to accept for the assets. The seller's settlement + // proportional fee (and possibly the settlement flat fee) is taken out of the amount the seller receives, + // so it's possible that the seller will still receive less than this price. + Price *types.Coin `protobuf:"bytes,4,opt,name=price,proto3" json:"price,omitempty"` + // seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. If this denom is the + // same denom as the price, it will come out of the actual price received. If this denom is different, the amount must + // be in the seller's account and a hold is placed on it until the order is filled or cancelled. + SellerSettlementFlatFee *types.Coin `protobuf:"bytes,5,opt,name=seller_settlement_flat_fee,json=sellerSettlementFlatFee,proto3" json:"seller_settlement_flat_fee,omitempty"` + // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the + // order must be either filled in full or not filled at all. + AllowPartial bool `protobuf:"varint,6,opt,name=allow_partial,json=allowPartial,proto3" json:"allow_partial,omitempty"` +} + +func (m *AskOrder) Reset() { *m = AskOrder{} } +func (m *AskOrder) String() string { return proto.CompactTextString(m) } +func (*AskOrder) ProtoMessage() {} +func (*AskOrder) Descriptor() ([]byte, []int) { + return fileDescriptor_dab7cbe63f582471, []int{1} +} +func (m *AskOrder) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AskOrder) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AskOrder.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AskOrder) XXX_Merge(src proto.Message) { + xxx_messageInfo_AskOrder.Merge(m, src) +} +func (m *AskOrder) XXX_Size() int { + return m.Size() +} +func (m *AskOrder) XXX_DiscardUnknown() { + xxx_messageInfo_AskOrder.DiscardUnknown(m) +} + +var xxx_messageInfo_AskOrder proto.InternalMessageInfo + +func (m *AskOrder) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *AskOrder) GetSeller() string { + if m != nil { + return m.Seller + } + return "" +} + +func (m *AskOrder) GetAssets() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Assets + } + return nil +} + +func (m *AskOrder) GetPrice() *types.Coin { + if m != nil { + return m.Price + } + return nil +} + +func (m *AskOrder) GetSellerSettlementFlatFee() *types.Coin { + if m != nil { + return m.SellerSettlementFlatFee + } + return nil +} + +func (m *AskOrder) GetAllowPartial() bool { + if m != nil { + return m.AllowPartial + } + return false +} + +// BidOrder represents someone's desire to buy something at a specific price. +type BidOrder struct { + // market_id identifies the market that this order belongs to. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // buyer is the address of the account that owns this order and has the price to spend. + Buyer string `protobuf:"bytes,2,opt,name=buyer,proto3" json:"buyer,omitempty"` + // assets are the things that the buyer wishes to buy. + Assets github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=assets,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"assets"` + // price is the amount that the buyer will pay for the assets. + // A hold is placed on this until the order is filled or cancelled. + Price *types.Coin `protobuf:"bytes,4,opt,name=price,proto3" json:"price,omitempty"` + // buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) + // when the order is settled. A hold is placed on this until the order is filled or cancelled. + BuyerSettlementFees github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,5,rep,name=buyer_settlement_fees,json=buyerSettlementFees,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"buyer_settlement_fees"` + // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the + // order must be either filled in full or not filled at all. + AllowPartial bool `protobuf:"varint,6,opt,name=allow_partial,json=allowPartial,proto3" json:"allow_partial,omitempty"` +} + +func (m *BidOrder) Reset() { *m = BidOrder{} } +func (m *BidOrder) String() string { return proto.CompactTextString(m) } +func (*BidOrder) ProtoMessage() {} +func (*BidOrder) Descriptor() ([]byte, []int) { + return fileDescriptor_dab7cbe63f582471, []int{2} +} +func (m *BidOrder) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BidOrder) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BidOrder.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BidOrder) XXX_Merge(src proto.Message) { + xxx_messageInfo_BidOrder.Merge(m, src) +} +func (m *BidOrder) XXX_Size() int { + return m.Size() +} +func (m *BidOrder) XXX_DiscardUnknown() { + xxx_messageInfo_BidOrder.DiscardUnknown(m) +} + +var xxx_messageInfo_BidOrder proto.InternalMessageInfo + +func (m *BidOrder) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *BidOrder) GetBuyer() string { + if m != nil { + return m.Buyer + } + return "" +} + +func (m *BidOrder) GetAssets() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Assets + } + return nil +} + +func (m *BidOrder) GetPrice() *types.Coin { + if m != nil { + return m.Price + } + return nil +} + +func (m *BidOrder) GetBuyerSettlementFees() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.BuyerSettlementFees + } + return nil +} + +func (m *BidOrder) GetAllowPartial() bool { + if m != nil { + return m.AllowPartial + } + return false +} + +func init() { + proto.RegisterType((*Order)(nil), "provenance.exchange.v1.Order") + proto.RegisterType((*AskOrder)(nil), "provenance.exchange.v1.AskOrder") + proto.RegisterType((*BidOrder)(nil), "provenance.exchange.v1.BidOrder") +} + +func init() { + proto.RegisterFile("provenance/exchange/v1/orders.proto", fileDescriptor_dab7cbe63f582471) +} + +var fileDescriptor_dab7cbe63f582471 = []byte{ + // 517 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xb1, 0x6e, 0xd3, 0x40, + 0x18, 0x8e, 0x9b, 0x3a, 0x75, 0xae, 0x74, 0x31, 0x05, 0x9c, 0x20, 0xb9, 0x56, 0xba, 0x78, 0xc9, + 0xb9, 0x29, 0x0f, 0x80, 0x6a, 0xa4, 0x8a, 0x4e, 0x54, 0xae, 0xc4, 0xc0, 0x62, 0x9d, 0xed, 0xbf, + 0xee, 0x29, 0x8e, 0xcf, 0xf2, 0x5d, 0x43, 0x3b, 0xf1, 0x0a, 0x48, 0xbc, 0x05, 0x12, 0x1b, 0x0f, + 0xd1, 0xb1, 0x62, 0x62, 0x02, 0x94, 0xac, 0x3c, 0x04, 0xf2, 0xdd, 0x25, 0x0d, 0x12, 0x2d, 0x1d, + 0x18, 0x98, 0x7c, 0xff, 0x7f, 0xdf, 0xf7, 0xfd, 0x9f, 0xbe, 0x5f, 0x3e, 0xb4, 0x5b, 0xd5, 0x6c, + 0x0a, 0x25, 0x29, 0x53, 0x08, 0xe0, 0x22, 0x3d, 0x23, 0x65, 0x0e, 0xc1, 0x74, 0x14, 0xb0, 0x3a, + 0x83, 0x9a, 0xe3, 0xaa, 0x66, 0x82, 0xd9, 0x8f, 0x6f, 0x40, 0x78, 0x01, 0xc2, 0xd3, 0x51, 0xdf, + 0x4d, 0x19, 0x9f, 0x30, 0x1e, 0x24, 0x84, 0x37, 0xa4, 0x04, 0x04, 0x19, 0x05, 0x29, 0xa3, 0xa5, + 0xe2, 0xf5, 0x7b, 0xea, 0x3e, 0x96, 0x55, 0xa0, 0x0a, 0x7d, 0xb5, 0x9d, 0xb3, 0x9c, 0xa9, 0x7e, + 0x73, 0x52, 0xdd, 0xc1, 0x27, 0x03, 0x99, 0xaf, 0x9a, 0xc9, 0x76, 0x0f, 0x59, 0xd2, 0x42, 0x4c, + 0x33, 0xc7, 0xf0, 0x0c, 0x7f, 0x3d, 0xda, 0x90, 0xf5, 0x51, 0x66, 0x3f, 0x47, 0x5d, 0xc2, 0xc7, + 0xb1, 0x2c, 0x9d, 0x35, 0xcf, 0xf0, 0x37, 0xf7, 0x3d, 0xfc, 0x67, 0x87, 0xf8, 0x80, 0x8f, 0xa5, + 0xde, 0xcb, 0x56, 0x64, 0x11, 0x7d, 0x6e, 0x04, 0x12, 0x9a, 0x69, 0x81, 0xf6, 0xdd, 0x02, 0x21, + 0xcd, 0x96, 0x02, 0x89, 0x3e, 0x87, 0x1b, 0xc8, 0x94, 0xe4, 0xc1, 0xcf, 0x35, 0x64, 0x2d, 0x46, + 0xd8, 0x4f, 0x51, 0x77, 0x42, 0xea, 0x31, 0x88, 0x85, 0xe7, 0xad, 0xc8, 0x52, 0x8d, 0xa3, 0xcc, + 0xde, 0x43, 0x1d, 0x0e, 0x45, 0xa1, 0x1d, 0x77, 0x43, 0xe7, 0xcb, 0xe7, 0xe1, 0xb6, 0x4e, 0xe4, + 0x20, 0xcb, 0x6a, 0xe0, 0xfc, 0x44, 0xd4, 0xb4, 0xcc, 0x23, 0x8d, 0xb3, 0x53, 0xd4, 0x21, 0x9c, + 0x83, 0xe0, 0x4e, 0xdb, 0x6b, 0xfb, 0x9b, 0xfb, 0x3d, 0xac, 0xe1, 0x4d, 0xda, 0x58, 0xa7, 0x8d, + 0x5f, 0x30, 0x5a, 0x86, 0x7b, 0x57, 0xdf, 0x76, 0x5a, 0x1f, 0xbf, 0xef, 0xf8, 0x39, 0x15, 0x67, + 0xe7, 0x09, 0x4e, 0xd9, 0x44, 0xa7, 0xad, 0x3f, 0x43, 0x9e, 0x8d, 0x03, 0x71, 0x59, 0x01, 0x97, + 0x04, 0x1e, 0x69, 0x69, 0x3b, 0x40, 0x66, 0x55, 0xd3, 0x14, 0x9c, 0x75, 0x19, 0xc3, 0xed, 0x33, + 0x22, 0x85, 0xb3, 0x5f, 0xa3, 0xbe, 0xf2, 0x17, 0x73, 0x10, 0xa2, 0x80, 0x09, 0x94, 0x22, 0x3e, + 0x2d, 0x88, 0x88, 0x4f, 0x01, 0x1c, 0xf3, 0x6f, 0x2a, 0x4f, 0x14, 0xf9, 0x64, 0xc9, 0x3d, 0x2c, + 0x88, 0x38, 0x04, 0xb0, 0x77, 0xd1, 0x16, 0x29, 0x0a, 0xf6, 0x36, 0xae, 0x48, 0x2d, 0x28, 0x29, + 0x9c, 0x8e, 0x67, 0xf8, 0x56, 0xf4, 0x40, 0x36, 0x8f, 0x55, 0x6f, 0xf0, 0xa1, 0x8d, 0xac, 0xc5, + 0x42, 0xee, 0x8e, 0x1b, 0x23, 0x33, 0x39, 0xbf, 0xbc, 0x47, 0xda, 0x0a, 0xf6, 0x9f, 0x86, 0xfd, + 0x0e, 0x3d, 0x92, 0xf6, 0x7e, 0xcb, 0x1a, 0x80, 0x3b, 0xe6, 0xbf, 0x37, 0xf9, 0x50, 0x4e, 0x5a, + 0x59, 0x0c, 0x00, 0xbf, 0xd7, 0x56, 0x42, 0xb8, 0x9a, 0xb9, 0xc6, 0xf5, 0xcc, 0x35, 0x7e, 0xcc, + 0x5c, 0xe3, 0xfd, 0xdc, 0x6d, 0x5d, 0xcf, 0xdd, 0xd6, 0xd7, 0xb9, 0xdb, 0x42, 0x3d, 0xca, 0x6e, + 0xf9, 0xaf, 0x8e, 0x8d, 0x37, 0x78, 0xc5, 0xda, 0x0d, 0x68, 0x48, 0xd9, 0x4a, 0x15, 0x5c, 0x2c, + 0x1f, 0xa5, 0xa4, 0x23, 0x9f, 0x88, 0x67, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0xba, 0xa4, 0x90, + 0x35, 0xb2, 0x04, 0x00, 0x00, +} + +func (m *Order) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Order) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Order) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Order != nil { + { + size := m.Order.Size() + i -= size + if _, err := m.Order.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if m.OrderId != 0 { + i = encodeVarintOrders(dAtA, i, uint64(m.OrderId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Order_AskOrder) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Order_AskOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.AskOrder != nil { + { + size, err := m.AskOrder.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *Order_BidOrder) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Order_BidOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.BidOrder != nil { + { + size, err := m.BidOrder.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + return len(dAtA) - i, nil +} +func (m *AskOrder) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AskOrder) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AskOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AllowPartial { + i-- + if m.AllowPartial { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if m.SellerSettlementFlatFee != nil { + { + size, err := m.SellerSettlementFlatFee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.Price != nil { + { + size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if len(m.Assets) > 0 { + for iNdEx := len(m.Assets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Assets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Seller) > 0 { + i -= len(m.Seller) + copy(dAtA[i:], m.Seller) + i = encodeVarintOrders(dAtA, i, uint64(len(m.Seller))) + i-- + dAtA[i] = 0x12 + } + if m.MarketId != 0 { + i = encodeVarintOrders(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *BidOrder) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BidOrder) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BidOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AllowPartial { + i-- + if m.AllowPartial { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if len(m.BuyerSettlementFees) > 0 { + for iNdEx := len(m.BuyerSettlementFees) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BuyerSettlementFees[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if m.Price != nil { + { + size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if len(m.Assets) > 0 { + for iNdEx := len(m.Assets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Assets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Buyer) > 0 { + i -= len(m.Buyer) + copy(dAtA[i:], m.Buyer) + i = encodeVarintOrders(dAtA, i, uint64(len(m.Buyer))) + i-- + dAtA[i] = 0x12 + } + if m.MarketId != 0 { + i = encodeVarintOrders(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintOrders(dAtA []byte, offset int, v uint64) int { + offset -= sovOrders(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Order) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OrderId != 0 { + n += 1 + sovOrders(uint64(m.OrderId)) + } + if m.Order != nil { + n += m.Order.Size() + } + return n +} + +func (m *Order_AskOrder) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AskOrder != nil { + l = m.AskOrder.Size() + n += 1 + l + sovOrders(uint64(l)) + } + return n +} +func (m *Order_BidOrder) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BidOrder != nil { + l = m.BidOrder.Size() + n += 1 + l + sovOrders(uint64(l)) + } + return n +} +func (m *AskOrder) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovOrders(uint64(m.MarketId)) + } + l = len(m.Seller) + if l > 0 { + n += 1 + l + sovOrders(uint64(l)) + } + if len(m.Assets) > 0 { + for _, e := range m.Assets { + l = e.Size() + n += 1 + l + sovOrders(uint64(l)) + } + } + if m.Price != nil { + l = m.Price.Size() + n += 1 + l + sovOrders(uint64(l)) + } + if m.SellerSettlementFlatFee != nil { + l = m.SellerSettlementFlatFee.Size() + n += 1 + l + sovOrders(uint64(l)) + } + if m.AllowPartial { + n += 2 + } + return n +} + +func (m *BidOrder) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovOrders(uint64(m.MarketId)) + } + l = len(m.Buyer) + if l > 0 { + n += 1 + l + sovOrders(uint64(l)) + } + if len(m.Assets) > 0 { + for _, e := range m.Assets { + l = e.Size() + n += 1 + l + sovOrders(uint64(l)) + } + } + if m.Price != nil { + l = m.Price.Size() + n += 1 + l + sovOrders(uint64(l)) + } + if len(m.BuyerSettlementFees) > 0 { + for _, e := range m.BuyerSettlementFees { + l = e.Size() + n += 1 + l + sovOrders(uint64(l)) + } + } + if m.AllowPartial { + n += 2 + } + return n +} + +func sovOrders(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozOrders(x uint64) (n int) { + return sovOrders(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Order) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Order: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Order: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) + } + m.OrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AskOrder", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOrders + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthOrders + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &AskOrder{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Order = &Order_AskOrder{v} + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BidOrder", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOrders + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthOrders + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &BidOrder{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Order = &Order_BidOrder{v} + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOrders(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthOrders + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AskOrder) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AskOrder: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AskOrder: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Seller", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOrders + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOrders + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Seller = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Assets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOrders + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthOrders + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Assets = append(m.Assets, types.Coin{}) + if err := m.Assets[len(m.Assets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Price", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOrders + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthOrders + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Price == nil { + m.Price = &types.Coin{} + } + if err := m.Price.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SellerSettlementFlatFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOrders + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthOrders + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SellerSettlementFlatFee == nil { + m.SellerSettlementFlatFee = &types.Coin{} + } + if err := m.SellerSettlementFlatFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowPartial", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowPartial = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipOrders(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthOrders + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BidOrder) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BidOrder: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BidOrder: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Buyer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOrders + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOrders + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Buyer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Assets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOrders + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthOrders + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Assets = append(m.Assets, types.Coin{}) + if err := m.Assets[len(m.Assets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Price", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOrders + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthOrders + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Price == nil { + m.Price = &types.Coin{} + } + if err := m.Price.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BuyerSettlementFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOrders + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthOrders + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BuyerSettlementFees = append(m.BuyerSettlementFees, types.Coin{}) + if err := m.BuyerSettlementFees[len(m.BuyerSettlementFees)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowPartial", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowPartial = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipOrders(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthOrders + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipOrders(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowOrders + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowOrders + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowOrders + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthOrders + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupOrders + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthOrders + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthOrders = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowOrders = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupOrders = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/exchange/params.pb.go b/x/exchange/params.pb.go new file mode 100644 index 0000000000..c87a3d0b20 --- /dev/null +++ b/x/exchange/params.pb.go @@ -0,0 +1,581 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: provenance/exchange/v1/params.proto + +package exchange + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Params is a representation of the exchange module parameters. +type Params struct { + // default_split is the default proportion of fees the exchange receives in basis points. + // It is used if there isn't an applicable denom-specific split defined. + // E.g. 100 = 1%. Min = 0, Max = 10000. + DefaultSplit uint32 `protobuf:"varint,1,opt,name=default_split,json=defaultSplit,proto3" json:"default_split,omitempty"` + // denom_splits are the denom-specific amounts the exchange receives. + DenomSplits []*DenomSplit `protobuf:"bytes,2,rep,name=denom_splits,json=denomSplits,proto3" json:"denom_splits,omitempty"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_5d689cfc7a7422f1, []int{0} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetDefaultSplit() uint32 { + if m != nil { + return m.DefaultSplit + } + return 0 +} + +func (m *Params) GetDenomSplits() []*DenomSplit { + if m != nil { + return m.DenomSplits + } + return nil +} + +// DenomSplit associates a coin denomination with an amount the exchange receives for that denom. +type DenomSplit struct { + // denom is the coin denomination this split applies to. + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` + // split is the proportion of fees the exchange receives for this denom in basis points. + // E.g. 100 = 1%. Min = 0, Max = 10000. + Split uint32 `protobuf:"varint,2,opt,name=split,proto3" json:"split,omitempty"` +} + +func (m *DenomSplit) Reset() { *m = DenomSplit{} } +func (m *DenomSplit) String() string { return proto.CompactTextString(m) } +func (*DenomSplit) ProtoMessage() {} +func (*DenomSplit) Descriptor() ([]byte, []int) { + return fileDescriptor_5d689cfc7a7422f1, []int{1} +} +func (m *DenomSplit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DenomSplit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DenomSplit.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DenomSplit) XXX_Merge(src proto.Message) { + xxx_messageInfo_DenomSplit.Merge(m, src) +} +func (m *DenomSplit) XXX_Size() int { + return m.Size() +} +func (m *DenomSplit) XXX_DiscardUnknown() { + xxx_messageInfo_DenomSplit.DiscardUnknown(m) +} + +var xxx_messageInfo_DenomSplit proto.InternalMessageInfo + +func (m *DenomSplit) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + +func (m *DenomSplit) GetSplit() uint32 { + if m != nil { + return m.Split + } + return 0 +} + +func init() { + proto.RegisterType((*Params)(nil), "provenance.exchange.v1.Params") + proto.RegisterType((*DenomSplit)(nil), "provenance.exchange.v1.DenomSplit") +} + +func init() { + proto.RegisterFile("provenance/exchange/v1/params.proto", fileDescriptor_5d689cfc7a7422f1) +} + +var fileDescriptor_5d689cfc7a7422f1 = []byte{ + // 235 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2e, 0x28, 0xca, 0x2f, + 0x4b, 0xcd, 0x4b, 0xcc, 0x4b, 0x4e, 0xd5, 0x4f, 0xad, 0x48, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0xd5, + 0x2f, 0x33, 0xd4, 0x2f, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, + 0x12, 0x43, 0x28, 0xd2, 0x83, 0x29, 0xd2, 0x2b, 0x33, 0x54, 0x2a, 0xe1, 0x62, 0x0b, 0x00, 0xab, + 0x13, 0x52, 0xe6, 0xe2, 0x4d, 0x49, 0x4d, 0x4b, 0x2c, 0xcd, 0x29, 0x89, 0x2f, 0x2e, 0xc8, 0xc9, + 0x2c, 0x91, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0d, 0xe2, 0x81, 0x0a, 0x06, 0x83, 0xc4, 0x84, 0x5c, + 0xb9, 0x78, 0x52, 0x52, 0xf3, 0xf2, 0x73, 0x21, 0x4a, 0x8a, 0x25, 0x98, 0x14, 0x98, 0x35, 0xb8, + 0x8d, 0x94, 0xf4, 0xb0, 0x9b, 0xae, 0xe7, 0x02, 0x52, 0x0b, 0xd6, 0x19, 0xc4, 0x9d, 0x02, 0x67, + 0x17, 0x2b, 0x59, 0x70, 0x71, 0x21, 0xa4, 0x84, 0x44, 0xb8, 0x58, 0xc1, 0x92, 0x60, 0x1b, 0x39, + 0x83, 0x20, 0x1c, 0x90, 0x28, 0xc4, 0x1d, 0x4c, 0x60, 0x77, 0x40, 0x38, 0x4e, 0xa9, 0x27, 0x1e, + 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, + 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0xc0, 0x25, 0x99, 0x99, 0x8f, 0xc3, 0x19, 0x01, 0x8c, + 0x51, 0x7a, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x08, 0x45, 0xba, + 0x99, 0xf9, 0x48, 0x3c, 0xfd, 0x0a, 0x78, 0xf0, 0x25, 0xb1, 0x81, 0x43, 0xcd, 0x18, 0x10, 0x00, + 0x00, 0xff, 0xff, 0x70, 0xb8, 0x10, 0x1a, 0x5c, 0x01, 0x00, 0x00, +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DenomSplits) > 0 { + for iNdEx := len(m.DenomSplits) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DenomSplits[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.DefaultSplit != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.DefaultSplit)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *DenomSplit) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DenomSplit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DenomSplit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Split != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.Split)) + i-- + dAtA[i] = 0x10 + } + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintParams(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintParams(dAtA []byte, offset int, v uint64) int { + offset -= sovParams(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DefaultSplit != 0 { + n += 1 + sovParams(uint64(m.DefaultSplit)) + } + if len(m.DenomSplits) > 0 { + for _, e := range m.DenomSplits { + l = e.Size() + n += 1 + l + sovParams(uint64(l)) + } + } + return n +} + +func (m *DenomSplit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + if m.Split != 0 { + n += 1 + sovParams(uint64(m.Split)) + } + return n +} + +func sovParams(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozParams(x uint64) (n int) { + return sovParams(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DefaultSplit", wireType) + } + m.DefaultSplit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DefaultSplit |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DenomSplits", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DenomSplits = append(m.DenomSplits, &DenomSplit{}) + if err := m.DenomSplits[len(m.DenomSplits)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DenomSplit) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DenomSplit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DenomSplit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Split", wireType) + } + m.Split = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Split |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipParams(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthParams + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupParams + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthParams + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthParams = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowParams = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupParams = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/exchange/qenesis.pb.go b/x/exchange/qenesis.pb.go new file mode 100644 index 0000000000..eacc86ea0b --- /dev/null +++ b/x/exchange/qenesis.pb.go @@ -0,0 +1,433 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: provenance/exchange/v1/qenesis.proto + +package exchange + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState is the data that should be loaded into the exchange module during genesis. +type GenesisState struct { + // params defines all the parameters of the exchange module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + // markets are all of the markets to create at genesis. + Markets []*Market `protobuf:"bytes,2,rep,name=markets,proto3" json:"markets,omitempty"` + // orders are all the orders to create at genesis. + Orders []*Order `protobuf:"bytes,3,rep,name=orders,proto3" json:"orders,omitempty"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_dca953db3e677d28, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func init() { + proto.RegisterType((*GenesisState)(nil), "provenance.exchange.v1.GenesisState") +} + +func init() { + proto.RegisterFile("provenance/exchange/v1/qenesis.proto", fileDescriptor_dca953db3e677d28) +} + +var fileDescriptor_dca953db3e677d28 = []byte{ + // 272 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x29, 0x28, 0xca, 0x2f, + 0x4b, 0xcd, 0x4b, 0xcc, 0x4b, 0x4e, 0xd5, 0x4f, 0xad, 0x48, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0xd5, + 0x2f, 0x33, 0xd4, 0x2f, 0x4c, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, + 0x17, 0x12, 0x43, 0xa8, 0xd2, 0x83, 0xa9, 0xd2, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, + 0x07, 0x2b, 0xd1, 0x07, 0xb1, 0x20, 0xaa, 0xa5, 0x94, 0x71, 0x98, 0x99, 0x9b, 0x58, 0x94, 0x9d, + 0x5a, 0x42, 0x40, 0x51, 0x7e, 0x51, 0x4a, 0x6a, 0x51, 0x31, 0x01, 0x45, 0x05, 0x89, 0x45, 0x89, + 0xb9, 0x50, 0x45, 0x4a, 0xc7, 0x19, 0xb9, 0x78, 0xdc, 0x21, 0xce, 0x0d, 0x2e, 0x49, 0x2c, 0x49, + 0x15, 0xb2, 0xe1, 0x62, 0x83, 0x28, 0x90, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd3, 0xc3, + 0xee, 0x7c, 0xbd, 0x00, 0xb0, 0x2a, 0x27, 0x96, 0x13, 0xf7, 0xe4, 0x19, 0x82, 0xa0, 0x7a, 0x84, + 0x2c, 0xb8, 0xd8, 0x21, 0x0e, 0x2d, 0x96, 0x60, 0x52, 0x60, 0xc6, 0xa7, 0xdd, 0x17, 0xac, 0x2c, + 0x08, 0xa6, 0x5c, 0xc8, 0x94, 0x8b, 0x0d, 0xe2, 0x7a, 0x09, 0x66, 0xb0, 0x46, 0x59, 0x5c, 0x1a, + 0xfd, 0x41, 0xaa, 0x82, 0xa0, 0x8a, 0xad, 0x38, 0x3a, 0x16, 0xc8, 0x33, 0xbc, 0x58, 0x20, 0xcf, + 0xe0, 0x94, 0x7a, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, + 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x5c, 0x92, 0x99, 0xf9, + 0x38, 0x0c, 0x0b, 0x60, 0x8c, 0xd2, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, + 0xd5, 0x47, 0x28, 0xd2, 0xcd, 0xcc, 0x47, 0xe2, 0xe9, 0x57, 0xc0, 0x03, 0x30, 0x89, 0x0d, 0x1c, + 0x6e, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x96, 0x4d, 0x65, 0x96, 0xfc, 0x01, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Orders) > 0 { + for iNdEx := len(m.Orders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Orders[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Markets) > 0 { + for iNdEx := len(m.Markets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Markets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintQenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovQenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQenesis(uint64(l)) + if len(m.Markets) > 0 { + for _, e := range m.Markets { + l = e.Size() + n += 1 + l + sovQenesis(uint64(l)) + } + } + if len(m.Orders) > 0 { + for _, e := range m.Orders { + l = e.Size() + n += 1 + l + sovQenesis(uint64(l)) + } + } + return n +} + +func sovQenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQenesis(x uint64) (n int) { + return sovQenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Markets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Markets = append(m.Markets, &Market{}) + if err := m.Markets[len(m.Markets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Orders", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Orders = append(m.Orders, &Order{}) + if err := m.Orders[len(m.Orders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go new file mode 100644 index 0000000000..5c5a2c81d4 --- /dev/null +++ b/x/exchange/query.pb.go @@ -0,0 +1,2575 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: provenance/exchange/v1/query.proto + +package exchange + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint. +type QueryOrderFeeCalcRequest struct { +} + +func (m *QueryOrderFeeCalcRequest) Reset() { *m = QueryOrderFeeCalcRequest{} } +func (m *QueryOrderFeeCalcRequest) String() string { return proto.CompactTextString(m) } +func (*QueryOrderFeeCalcRequest) ProtoMessage() {} +func (*QueryOrderFeeCalcRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{0} +} +func (m *QueryOrderFeeCalcRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryOrderFeeCalcRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryOrderFeeCalcRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryOrderFeeCalcRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryOrderFeeCalcRequest.Merge(m, src) +} +func (m *QueryOrderFeeCalcRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryOrderFeeCalcRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryOrderFeeCalcRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryOrderFeeCalcRequest proto.InternalMessageInfo + +// QueryOrderFeeCalcResponse is a response message for the QueryOrderFeeCalc endpoint. +type QueryOrderFeeCalcResponse struct { +} + +func (m *QueryOrderFeeCalcResponse) Reset() { *m = QueryOrderFeeCalcResponse{} } +func (m *QueryOrderFeeCalcResponse) String() string { return proto.CompactTextString(m) } +func (*QueryOrderFeeCalcResponse) ProtoMessage() {} +func (*QueryOrderFeeCalcResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{1} +} +func (m *QueryOrderFeeCalcResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryOrderFeeCalcResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryOrderFeeCalcResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryOrderFeeCalcResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryOrderFeeCalcResponse.Merge(m, src) +} +func (m *QueryOrderFeeCalcResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryOrderFeeCalcResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryOrderFeeCalcResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryOrderFeeCalcResponse proto.InternalMessageInfo + +// QuerySettlementFeeCalcRequest is a request message for the QuerySettlementFeeCalc endpoint. +type QuerySettlementFeeCalcRequest struct { +} + +func (m *QuerySettlementFeeCalcRequest) Reset() { *m = QuerySettlementFeeCalcRequest{} } +func (m *QuerySettlementFeeCalcRequest) String() string { return proto.CompactTextString(m) } +func (*QuerySettlementFeeCalcRequest) ProtoMessage() {} +func (*QuerySettlementFeeCalcRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{2} +} +func (m *QuerySettlementFeeCalcRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuerySettlementFeeCalcRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuerySettlementFeeCalcRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QuerySettlementFeeCalcRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuerySettlementFeeCalcRequest.Merge(m, src) +} +func (m *QuerySettlementFeeCalcRequest) XXX_Size() int { + return m.Size() +} +func (m *QuerySettlementFeeCalcRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QuerySettlementFeeCalcRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QuerySettlementFeeCalcRequest proto.InternalMessageInfo + +// QuerySettlementFeeCalcResponse is a response message for the QuerySettlementFeeCalc endpoint. +type QuerySettlementFeeCalcResponse struct { +} + +func (m *QuerySettlementFeeCalcResponse) Reset() { *m = QuerySettlementFeeCalcResponse{} } +func (m *QuerySettlementFeeCalcResponse) String() string { return proto.CompactTextString(m) } +func (*QuerySettlementFeeCalcResponse) ProtoMessage() {} +func (*QuerySettlementFeeCalcResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{3} +} +func (m *QuerySettlementFeeCalcResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuerySettlementFeeCalcResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuerySettlementFeeCalcResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QuerySettlementFeeCalcResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuerySettlementFeeCalcResponse.Merge(m, src) +} +func (m *QuerySettlementFeeCalcResponse) XXX_Size() int { + return m.Size() +} +func (m *QuerySettlementFeeCalcResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QuerySettlementFeeCalcResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QuerySettlementFeeCalcResponse proto.InternalMessageInfo + +// QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. +type QueryGetOrderRequest struct { + OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` +} + +func (m *QueryGetOrderRequest) Reset() { *m = QueryGetOrderRequest{} } +func (m *QueryGetOrderRequest) String() string { return proto.CompactTextString(m) } +func (*QueryGetOrderRequest) ProtoMessage() {} +func (*QueryGetOrderRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{4} +} +func (m *QueryGetOrderRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetOrderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetOrderRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetOrderRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetOrderRequest.Merge(m, src) +} +func (m *QueryGetOrderRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryGetOrderRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetOrderRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetOrderRequest proto.InternalMessageInfo + +func (m *QueryGetOrderRequest) GetOrderId() uint64 { + if m != nil { + return m.OrderId + } + return 0 +} + +// QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. +type QueryGetOrderResponse struct { +} + +func (m *QueryGetOrderResponse) Reset() { *m = QueryGetOrderResponse{} } +func (m *QueryGetOrderResponse) String() string { return proto.CompactTextString(m) } +func (*QueryGetOrderResponse) ProtoMessage() {} +func (*QueryGetOrderResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{5} +} +func (m *QueryGetOrderResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetOrderResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetOrderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetOrderResponse.Merge(m, src) +} +func (m *QueryGetOrderResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryGetOrderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetOrderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetOrderResponse proto.InternalMessageInfo + +// QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. +type QueryGetMarketOrdersRequest struct { + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` +} + +func (m *QueryGetMarketOrdersRequest) Reset() { *m = QueryGetMarketOrdersRequest{} } +func (m *QueryGetMarketOrdersRequest) String() string { return proto.CompactTextString(m) } +func (*QueryGetMarketOrdersRequest) ProtoMessage() {} +func (*QueryGetMarketOrdersRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{6} +} +func (m *QueryGetMarketOrdersRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetMarketOrdersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetMarketOrdersRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetMarketOrdersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetMarketOrdersRequest.Merge(m, src) +} +func (m *QueryGetMarketOrdersRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryGetMarketOrdersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetMarketOrdersRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetMarketOrdersRequest proto.InternalMessageInfo + +func (m *QueryGetMarketOrdersRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +// QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders endpoint. +type QueryGetMarketOrdersResponse struct { +} + +func (m *QueryGetMarketOrdersResponse) Reset() { *m = QueryGetMarketOrdersResponse{} } +func (m *QueryGetMarketOrdersResponse) String() string { return proto.CompactTextString(m) } +func (*QueryGetMarketOrdersResponse) ProtoMessage() {} +func (*QueryGetMarketOrdersResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{7} +} +func (m *QueryGetMarketOrdersResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetMarketOrdersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetMarketOrdersResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetMarketOrdersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetMarketOrdersResponse.Merge(m, src) +} +func (m *QueryGetMarketOrdersResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryGetMarketOrdersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetMarketOrdersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetMarketOrdersResponse proto.InternalMessageInfo + +// QueryGetAddressOrdersRequest is a request message for the QueryGetAddressOrders endpoint. +type QueryGetAddressOrdersRequest struct { + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (m *QueryGetAddressOrdersRequest) Reset() { *m = QueryGetAddressOrdersRequest{} } +func (m *QueryGetAddressOrdersRequest) String() string { return proto.CompactTextString(m) } +func (*QueryGetAddressOrdersRequest) ProtoMessage() {} +func (*QueryGetAddressOrdersRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{8} +} +func (m *QueryGetAddressOrdersRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetAddressOrdersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetAddressOrdersRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetAddressOrdersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetAddressOrdersRequest.Merge(m, src) +} +func (m *QueryGetAddressOrdersRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryGetAddressOrdersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetAddressOrdersRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetAddressOrdersRequest proto.InternalMessageInfo + +func (m *QueryGetAddressOrdersRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +// QueryGetAddressOrdersResponse is a response message for the QueryGetAddressOrders endpoint. +type QueryGetAddressOrdersResponse struct { +} + +func (m *QueryGetAddressOrdersResponse) Reset() { *m = QueryGetAddressOrdersResponse{} } +func (m *QueryGetAddressOrdersResponse) String() string { return proto.CompactTextString(m) } +func (*QueryGetAddressOrdersResponse) ProtoMessage() {} +func (*QueryGetAddressOrdersResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{9} +} +func (m *QueryGetAddressOrdersResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetAddressOrdersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetAddressOrdersResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetAddressOrdersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetAddressOrdersResponse.Merge(m, src) +} +func (m *QueryGetAddressOrdersResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryGetAddressOrdersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetAddressOrdersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetAddressOrdersResponse proto.InternalMessageInfo + +// QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint. +type QueryGetAllOrdersRequest struct { +} + +func (m *QueryGetAllOrdersRequest) Reset() { *m = QueryGetAllOrdersRequest{} } +func (m *QueryGetAllOrdersRequest) String() string { return proto.CompactTextString(m) } +func (*QueryGetAllOrdersRequest) ProtoMessage() {} +func (*QueryGetAllOrdersRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{10} +} +func (m *QueryGetAllOrdersRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetAllOrdersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetAllOrdersRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetAllOrdersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetAllOrdersRequest.Merge(m, src) +} +func (m *QueryGetAllOrdersRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryGetAllOrdersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetAllOrdersRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetAllOrdersRequest proto.InternalMessageInfo + +// QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoint. +type QueryGetAllOrdersResponse struct { +} + +func (m *QueryGetAllOrdersResponse) Reset() { *m = QueryGetAllOrdersResponse{} } +func (m *QueryGetAllOrdersResponse) String() string { return proto.CompactTextString(m) } +func (*QueryGetAllOrdersResponse) ProtoMessage() {} +func (*QueryGetAllOrdersResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{11} +} +func (m *QueryGetAllOrdersResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetAllOrdersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetAllOrdersResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetAllOrdersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetAllOrdersResponse.Merge(m, src) +} +func (m *QueryGetAllOrdersResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryGetAllOrdersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetAllOrdersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetAllOrdersResponse proto.InternalMessageInfo + +// QueryMarketInfoRequest is a request message for the QueryMarketInfo endpoint. +type QueryMarketInfoRequest struct { +} + +func (m *QueryMarketInfoRequest) Reset() { *m = QueryMarketInfoRequest{} } +func (m *QueryMarketInfoRequest) String() string { return proto.CompactTextString(m) } +func (*QueryMarketInfoRequest) ProtoMessage() {} +func (*QueryMarketInfoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{12} +} +func (m *QueryMarketInfoRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryMarketInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryMarketInfoRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryMarketInfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryMarketInfoRequest.Merge(m, src) +} +func (m *QueryMarketInfoRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryMarketInfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryMarketInfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryMarketInfoRequest proto.InternalMessageInfo + +// QueryMarketInfoResponse is a response message for the QueryMarketInfo endpoint. +type QueryMarketInfoResponse struct { +} + +func (m *QueryMarketInfoResponse) Reset() { *m = QueryMarketInfoResponse{} } +func (m *QueryMarketInfoResponse) String() string { return proto.CompactTextString(m) } +func (*QueryMarketInfoResponse) ProtoMessage() {} +func (*QueryMarketInfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{13} +} +func (m *QueryMarketInfoResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryMarketInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryMarketInfoResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryMarketInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryMarketInfoResponse.Merge(m, src) +} +func (m *QueryMarketInfoResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryMarketInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryMarketInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryMarketInfoResponse proto.InternalMessageInfo + +// QueryParamsRequest is a request message for the QueryParams endpoint. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{14} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is a response message for the QueryParams endpoint. +type QueryParamsResponse struct { +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{15} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*QueryOrderFeeCalcRequest)(nil), "provenance.exchange.v1.QueryOrderFeeCalcRequest") + proto.RegisterType((*QueryOrderFeeCalcResponse)(nil), "provenance.exchange.v1.QueryOrderFeeCalcResponse") + proto.RegisterType((*QuerySettlementFeeCalcRequest)(nil), "provenance.exchange.v1.QuerySettlementFeeCalcRequest") + proto.RegisterType((*QuerySettlementFeeCalcResponse)(nil), "provenance.exchange.v1.QuerySettlementFeeCalcResponse") + proto.RegisterType((*QueryGetOrderRequest)(nil), "provenance.exchange.v1.QueryGetOrderRequest") + proto.RegisterType((*QueryGetOrderResponse)(nil), "provenance.exchange.v1.QueryGetOrderResponse") + proto.RegisterType((*QueryGetMarketOrdersRequest)(nil), "provenance.exchange.v1.QueryGetMarketOrdersRequest") + proto.RegisterType((*QueryGetMarketOrdersResponse)(nil), "provenance.exchange.v1.QueryGetMarketOrdersResponse") + proto.RegisterType((*QueryGetAddressOrdersRequest)(nil), "provenance.exchange.v1.QueryGetAddressOrdersRequest") + proto.RegisterType((*QueryGetAddressOrdersResponse)(nil), "provenance.exchange.v1.QueryGetAddressOrdersResponse") + proto.RegisterType((*QueryGetAllOrdersRequest)(nil), "provenance.exchange.v1.QueryGetAllOrdersRequest") + proto.RegisterType((*QueryGetAllOrdersResponse)(nil), "provenance.exchange.v1.QueryGetAllOrdersResponse") + proto.RegisterType((*QueryMarketInfoRequest)(nil), "provenance.exchange.v1.QueryMarketInfoRequest") + proto.RegisterType((*QueryMarketInfoResponse)(nil), "provenance.exchange.v1.QueryMarketInfoResponse") + proto.RegisterType((*QueryParamsRequest)(nil), "provenance.exchange.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "provenance.exchange.v1.QueryParamsResponse") +} + +func init() { + proto.RegisterFile("provenance/exchange/v1/query.proto", fileDescriptor_00949b75b1c10bfe) +} + +var fileDescriptor_00949b75b1c10bfe = []byte{ + // 685 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xcb, 0x6e, 0xd3, 0x40, + 0x14, 0xed, 0x20, 0xa0, 0xed, 0x54, 0x15, 0xe2, 0xd2, 0x47, 0xe2, 0x16, 0x53, 0x19, 0x54, 0x4a, + 0xa1, 0x9e, 0xa6, 0xa5, 0x48, 0xb0, 0xa3, 0x48, 0xa0, 0x2e, 0x50, 0x4b, 0x2a, 0x36, 0xdd, 0x54, + 0x6e, 0x3c, 0x4d, 0x2d, 0x1c, 0x4f, 0xea, 0x71, 0xa2, 0xa2, 0x28, 0x9b, 0x7e, 0x00, 0x42, 0x62, + 0x8f, 0x58, 0xf0, 0x09, 0x08, 0xb1, 0xe0, 0x03, 0x60, 0x57, 0xc1, 0x86, 0x25, 0x4a, 0xf8, 0x10, + 0x94, 0xf1, 0xd8, 0x89, 0x13, 0x3f, 0x92, 0xa5, 0xe7, 0x9e, 0x73, 0xcf, 0xc9, 0xcc, 0xbd, 0x47, + 0xc1, 0x5a, 0xd5, 0x65, 0x75, 0xea, 0x18, 0x4e, 0x89, 0x12, 0x7a, 0x56, 0x3a, 0x31, 0x9c, 0x32, + 0x25, 0xf5, 0x02, 0x39, 0xad, 0x51, 0xf7, 0xad, 0x5e, 0x75, 0x99, 0xc7, 0x60, 0xae, 0x8b, 0xd1, + 0x03, 0x8c, 0x5e, 0x2f, 0x28, 0xf9, 0x12, 0xe3, 0x15, 0xc6, 0x0f, 0x05, 0x8a, 0xf8, 0x1f, 0x3e, + 0x45, 0x59, 0x2c, 0x33, 0x56, 0xb6, 0x29, 0x31, 0xaa, 0x16, 0x31, 0x1c, 0x87, 0x79, 0x86, 0x67, + 0x31, 0x47, 0x56, 0x35, 0x05, 0xe7, 0x5e, 0x75, 0xfa, 0xef, 0xba, 0x26, 0x75, 0x9f, 0x53, 0xfa, + 0xcc, 0xb0, 0x4b, 0x45, 0x7a, 0x5a, 0xa3, 0xdc, 0xd3, 0x16, 0x70, 0x3e, 0xa6, 0xc6, 0xab, 0xcc, + 0xe1, 0x54, 0xbb, 0x85, 0x6f, 0x8a, 0xe2, 0x3e, 0xf5, 0x3c, 0x9b, 0x56, 0xa8, 0xe3, 0xf5, 0xb1, + 0x97, 0xb0, 0x9a, 0x04, 0x90, 0x2d, 0x0a, 0x78, 0x46, 0x20, 0x5e, 0x50, 0x4f, 0x48, 0x48, 0x26, + 0xe4, 0xf1, 0x04, 0xeb, 0x7c, 0x1f, 0x5a, 0x66, 0x0e, 0x2d, 0xa1, 0x95, 0xcb, 0xc5, 0x71, 0xf1, + 0xbd, 0x63, 0x6a, 0xf3, 0x78, 0xb6, 0x8f, 0x22, 0x7b, 0x3d, 0xc1, 0x0b, 0x41, 0xe1, 0xa5, 0xe1, + 0xbe, 0x91, 0x65, 0x1e, 0xb4, 0x5c, 0xc0, 0x93, 0x15, 0x71, 0x1c, 0xf4, 0x9c, 0x2e, 0x4e, 0xf8, + 0x07, 0x3b, 0xa6, 0xa6, 0xe2, 0xc5, 0x78, 0xae, 0xec, 0x5d, 0xec, 0xd6, 0x9f, 0x9a, 0xa6, 0x4b, + 0x39, 0x8f, 0x36, 0xdf, 0xc0, 0xe3, 0x86, 0x7f, 0x2e, 0x5a, 0x4f, 0x6e, 0xe7, 0x7e, 0x7d, 0x59, + 0x9b, 0x91, 0x8f, 0x20, 0x19, 0xfb, 0x9e, 0x6b, 0x39, 0xe5, 0x62, 0x00, 0x0c, 0xaf, 0x6f, 0xb0, + 0xa7, 0x14, 0x0d, 0x1e, 0xa6, 0x03, 0xb0, 0xed, 0x88, 0x60, 0xf8, 0x30, 0xd1, 0x9a, 0x24, 0xe6, + 0xf0, 0x9c, 0x28, 0xfa, 0x3f, 0x65, 0xc7, 0x39, 0x66, 0x01, 0x2d, 0x8f, 0xe7, 0x07, 0x2a, 0x92, + 0x34, 0x83, 0x41, 0x94, 0xf6, 0x0c, 0xd7, 0xa8, 0x84, 0x3a, 0xb3, 0xf8, 0x46, 0xe4, 0xd4, 0x07, + 0x6f, 0x9c, 0x4f, 0xe1, 0x2b, 0xe2, 0x1c, 0x3e, 0x23, 0x7c, 0x7d, 0x60, 0x44, 0x60, 0x5d, 0x8f, + 0x9f, 0x52, 0x3d, 0x69, 0xd2, 0x94, 0xc2, 0x08, 0x0c, 0xe9, 0x78, 0xf5, 0xfc, 0xf7, 0xbf, 0x0f, + 0x97, 0xee, 0x80, 0x46, 0x12, 0xd6, 0xe6, 0x98, 0x52, 0x4e, 0xc4, 0xdc, 0xc0, 0x37, 0x24, 0xef, + 0x64, 0x60, 0x16, 0x61, 0x2b, 0x55, 0x39, 0x69, 0xb8, 0x95, 0x47, 0xa3, 0xd2, 0xa4, 0x6b, 0x22, + 0x5c, 0xdf, 0x83, 0xbb, 0xa9, 0xae, 0x79, 0xc8, 0x87, 0x8f, 0x08, 0x4f, 0x47, 0x26, 0x1e, 0x1e, + 0xa4, 0x4a, 0xf7, 0xed, 0x92, 0xb2, 0x36, 0x24, 0x5a, 0xfa, 0x5b, 0x17, 0xfe, 0x56, 0x61, 0x25, + 0xc9, 0x9f, 0xb8, 0x50, 0xd2, 0x08, 0xf6, 0xb3, 0x09, 0xdf, 0x51, 0x77, 0x8b, 0x7b, 0xb7, 0x07, + 0x36, 0xb3, 0x94, 0x63, 0xf6, 0x54, 0x79, 0x38, 0x1a, 0x49, 0xba, 0x7e, 0x2c, 0x5c, 0x6f, 0x42, + 0x21, 0xc9, 0xb5, 0xbf, 0xea, 0xa4, 0x11, 0x66, 0x40, 0xd3, 0xff, 0x21, 0x1c, 0xbe, 0xa2, 0x6e, + 0xa2, 0x44, 0x16, 0x11, 0x32, 0xad, 0xc4, 0x65, 0x81, 0xb2, 0x35, 0x22, 0x6b, 0xa4, 0x7b, 0xe7, + 0xa4, 0x21, 0xf3, 0xa3, 0x09, 0x9f, 0x82, 0xd5, 0xeb, 0x0d, 0x81, 0x8c, 0xd5, 0x8b, 0xc9, 0x92, + 0x8c, 0xd5, 0x8b, 0x4d, 0x98, 0x65, 0x61, 0x76, 0x09, 0xd4, 0x74, 0xb3, 0xf0, 0x13, 0xe1, 0x6b, + 0x7d, 0x81, 0x03, 0x7a, 0xaa, 0xdc, 0x40, 0x66, 0x29, 0x64, 0x68, 0xbc, 0x34, 0xf7, 0x5a, 0x98, + 0xdb, 0x85, 0xdb, 0x19, 0xb3, 0x60, 0x39, 0xc7, 0xec, 0x60, 0x05, 0x96, 0x33, 0x60, 0x26, 0xf5, + 0x0c, 0xcb, 0xe6, 0xf0, 0x0e, 0xe1, 0xa9, 0x9e, 0x2c, 0x84, 0xd5, 0x54, 0x5f, 0x91, 0x18, 0x55, + 0xee, 0x0f, 0x85, 0x1d, 0xf6, 0x72, 0xab, 0x02, 0xbf, 0x4d, 0x7f, 0xb4, 0x54, 0x74, 0xd1, 0x52, + 0xd1, 0xdf, 0x96, 0x8a, 0xde, 0xb7, 0xd5, 0xb1, 0x8b, 0xb6, 0x3a, 0xf6, 0xa7, 0xad, 0x8e, 0xe1, + 0xbc, 0xc5, 0x12, 0x04, 0xf7, 0xd0, 0x81, 0x5e, 0xb6, 0xbc, 0x93, 0xda, 0x91, 0x5e, 0x62, 0x95, + 0x1e, 0x81, 0x35, 0x8b, 0xf5, 0xca, 0x9d, 0x85, 0x82, 0x47, 0x57, 0xc5, 0xdf, 0x84, 0xcd, 0xff, + 0x01, 0x00, 0x00, 0xff, 0xff, 0x63, 0xe3, 0x39, 0xae, 0x9d, 0x08, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // QueryOrderFeeCalc calculates the fees that will be associated with the provided order. + QueryOrderFeeCalc(ctx context.Context, in *QueryOrderFeeCalcRequest, opts ...grpc.CallOption) (*QueryOrderFeeCalcResponse, error) + // QuerySettlementFeeCalc calculates the fees that will be associated with the provided settlement. + QuerySettlementFeeCalc(ctx context.Context, in *QuerySettlementFeeCalcRequest, opts ...grpc.CallOption) (*QuerySettlementFeeCalcResponse, error) + // QueryGetOrder looks up an order by id. + QueryGetOrder(ctx context.Context, in *QueryGetOrderRequest, opts ...grpc.CallOption) (*QueryGetOrderResponse, error) + // QueryGetMarketOrders looks up the orders in a market. + QueryGetMarketOrders(ctx context.Context, in *QueryGetMarketOrdersRequest, opts ...grpc.CallOption) (*QueryGetMarketOrdersResponse, error) + // QueryGetAddressOrders looks up the orders from the provided address. + QueryGetAddressOrders(ctx context.Context, in *QueryGetAddressOrdersRequest, opts ...grpc.CallOption) (*QueryGetAddressOrdersResponse, error) + // QueryGetAllOrders gets all orders in the exchange module. + QueryGetAllOrders(ctx context.Context, in *QueryGetAllOrdersRequest, opts ...grpc.CallOption) (*QueryGetAllOrdersResponse, error) + // QueryMarketInfo returns the information/details about a market. + QueryMarketInfo(ctx context.Context, in *QueryMarketInfoRequest, opts ...grpc.CallOption) (*QueryMarketInfoResponse, error) + // QueryParams returns the exchange module parameters. + QueryParams(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) QueryOrderFeeCalc(ctx context.Context, in *QueryOrderFeeCalcRequest, opts ...grpc.CallOption) (*QueryOrderFeeCalcResponse, error) { + out := new(QueryOrderFeeCalcResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryOrderFeeCalc", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) QuerySettlementFeeCalc(ctx context.Context, in *QuerySettlementFeeCalcRequest, opts ...grpc.CallOption) (*QuerySettlementFeeCalcResponse, error) { + out := new(QuerySettlementFeeCalcResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QuerySettlementFeeCalc", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) QueryGetOrder(ctx context.Context, in *QueryGetOrderRequest, opts ...grpc.CallOption) (*QueryGetOrderResponse, error) { + out := new(QueryGetOrderResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetOrder", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) QueryGetMarketOrders(ctx context.Context, in *QueryGetMarketOrdersRequest, opts ...grpc.CallOption) (*QueryGetMarketOrdersResponse, error) { + out := new(QueryGetMarketOrdersResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetMarketOrders", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) QueryGetAddressOrders(ctx context.Context, in *QueryGetAddressOrdersRequest, opts ...grpc.CallOption) (*QueryGetAddressOrdersResponse, error) { + out := new(QueryGetAddressOrdersResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetAddressOrders", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) QueryGetAllOrders(ctx context.Context, in *QueryGetAllOrdersRequest, opts ...grpc.CallOption) (*QueryGetAllOrdersResponse, error) { + out := new(QueryGetAllOrdersResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetAllOrders", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) QueryMarketInfo(ctx context.Context, in *QueryMarketInfoRequest, opts ...grpc.CallOption) (*QueryMarketInfoResponse, error) { + out := new(QueryMarketInfoResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryMarketInfo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) QueryParams(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // QueryOrderFeeCalc calculates the fees that will be associated with the provided order. + QueryOrderFeeCalc(context.Context, *QueryOrderFeeCalcRequest) (*QueryOrderFeeCalcResponse, error) + // QuerySettlementFeeCalc calculates the fees that will be associated with the provided settlement. + QuerySettlementFeeCalc(context.Context, *QuerySettlementFeeCalcRequest) (*QuerySettlementFeeCalcResponse, error) + // QueryGetOrder looks up an order by id. + QueryGetOrder(context.Context, *QueryGetOrderRequest) (*QueryGetOrderResponse, error) + // QueryGetMarketOrders looks up the orders in a market. + QueryGetMarketOrders(context.Context, *QueryGetMarketOrdersRequest) (*QueryGetMarketOrdersResponse, error) + // QueryGetAddressOrders looks up the orders from the provided address. + QueryGetAddressOrders(context.Context, *QueryGetAddressOrdersRequest) (*QueryGetAddressOrdersResponse, error) + // QueryGetAllOrders gets all orders in the exchange module. + QueryGetAllOrders(context.Context, *QueryGetAllOrdersRequest) (*QueryGetAllOrdersResponse, error) + // QueryMarketInfo returns the information/details about a market. + QueryMarketInfo(context.Context, *QueryMarketInfoRequest) (*QueryMarketInfoResponse, error) + // QueryParams returns the exchange module parameters. + QueryParams(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) QueryOrderFeeCalc(ctx context.Context, req *QueryOrderFeeCalcRequest) (*QueryOrderFeeCalcResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryOrderFeeCalc not implemented") +} +func (*UnimplementedQueryServer) QuerySettlementFeeCalc(ctx context.Context, req *QuerySettlementFeeCalcRequest) (*QuerySettlementFeeCalcResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QuerySettlementFeeCalc not implemented") +} +func (*UnimplementedQueryServer) QueryGetOrder(ctx context.Context, req *QueryGetOrderRequest) (*QueryGetOrderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryGetOrder not implemented") +} +func (*UnimplementedQueryServer) QueryGetMarketOrders(ctx context.Context, req *QueryGetMarketOrdersRequest) (*QueryGetMarketOrdersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryGetMarketOrders not implemented") +} +func (*UnimplementedQueryServer) QueryGetAddressOrders(ctx context.Context, req *QueryGetAddressOrdersRequest) (*QueryGetAddressOrdersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryGetAddressOrders not implemented") +} +func (*UnimplementedQueryServer) QueryGetAllOrders(ctx context.Context, req *QueryGetAllOrdersRequest) (*QueryGetAllOrdersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryGetAllOrders not implemented") +} +func (*UnimplementedQueryServer) QueryMarketInfo(ctx context.Context, req *QueryMarketInfoRequest) (*QueryMarketInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryMarketInfo not implemented") +} +func (*UnimplementedQueryServer) QueryParams(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryParams not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_QueryOrderFeeCalc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryOrderFeeCalcRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryOrderFeeCalc(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryOrderFeeCalc", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryOrderFeeCalc(ctx, req.(*QueryOrderFeeCalcRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_QuerySettlementFeeCalc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QuerySettlementFeeCalcRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QuerySettlementFeeCalc(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QuerySettlementFeeCalc", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QuerySettlementFeeCalc(ctx, req.(*QuerySettlementFeeCalcRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_QueryGetOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryGetOrderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryGetOrder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryGetOrder", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryGetOrder(ctx, req.(*QueryGetOrderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_QueryGetMarketOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryGetMarketOrdersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryGetMarketOrders(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryGetMarketOrders", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryGetMarketOrders(ctx, req.(*QueryGetMarketOrdersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_QueryGetAddressOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryGetAddressOrdersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryGetAddressOrders(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryGetAddressOrders", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryGetAddressOrders(ctx, req.(*QueryGetAddressOrdersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_QueryGetAllOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryGetAllOrdersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryGetAllOrders(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryGetAllOrders", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryGetAllOrders(ctx, req.(*QueryGetAllOrdersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_QueryMarketInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryMarketInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryMarketInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryMarketInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryMarketInfo(ctx, req.(*QueryMarketInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_QueryParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryParams(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "provenance.exchange.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "QueryOrderFeeCalc", + Handler: _Query_QueryOrderFeeCalc_Handler, + }, + { + MethodName: "QuerySettlementFeeCalc", + Handler: _Query_QuerySettlementFeeCalc_Handler, + }, + { + MethodName: "QueryGetOrder", + Handler: _Query_QueryGetOrder_Handler, + }, + { + MethodName: "QueryGetMarketOrders", + Handler: _Query_QueryGetMarketOrders_Handler, + }, + { + MethodName: "QueryGetAddressOrders", + Handler: _Query_QueryGetAddressOrders_Handler, + }, + { + MethodName: "QueryGetAllOrders", + Handler: _Query_QueryGetAllOrders_Handler, + }, + { + MethodName: "QueryMarketInfo", + Handler: _Query_QueryMarketInfo_Handler, + }, + { + MethodName: "QueryParams", + Handler: _Query_QueryParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "provenance/exchange/v1/query.proto", +} + +func (m *QueryOrderFeeCalcRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryOrderFeeCalcRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryOrderFeeCalcRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryOrderFeeCalcResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryOrderFeeCalcResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryOrderFeeCalcResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QuerySettlementFeeCalcRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QuerySettlementFeeCalcRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuerySettlementFeeCalcRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QuerySettlementFeeCalcResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QuerySettlementFeeCalcResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuerySettlementFeeCalcResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryGetOrderRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetOrderRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetOrderRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.OrderId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.OrderId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryGetOrderResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetOrderResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetOrderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryGetMarketOrdersRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetMarketOrdersRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetMarketOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MarketId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryGetMarketOrdersResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetMarketOrdersResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetMarketOrdersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryGetAddressOrdersRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetAddressOrdersRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetAddressOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryGetAddressOrdersResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetAddressOrdersResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetAddressOrdersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryGetAllOrdersRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetAllOrdersRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetAllOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryGetAllOrdersResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetAllOrdersResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetAllOrdersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryMarketInfoRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryMarketInfoRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryMarketInfoRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryMarketInfoResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryMarketInfoResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryMarketInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryOrderFeeCalcRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryOrderFeeCalcResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QuerySettlementFeeCalcRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QuerySettlementFeeCalcResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryGetOrderRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OrderId != 0 { + n += 1 + sovQuery(uint64(m.OrderId)) + } + return n +} + +func (m *QueryGetOrderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryGetMarketOrdersRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovQuery(uint64(m.MarketId)) + } + return n +} + +func (m *QueryGetMarketOrdersResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryGetAddressOrdersRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryGetAddressOrdersResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryGetAllOrdersRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryGetAllOrdersResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryMarketInfoRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryMarketInfoResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryOrderFeeCalcRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryOrderFeeCalcRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryOrderFeeCalcRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryOrderFeeCalcResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryOrderFeeCalcResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryOrderFeeCalcResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QuerySettlementFeeCalcRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QuerySettlementFeeCalcRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QuerySettlementFeeCalcRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QuerySettlementFeeCalcResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QuerySettlementFeeCalcResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QuerySettlementFeeCalcResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetOrderRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetOrderRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetOrderRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) + } + m.OrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetOrderResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetOrderResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetOrderResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetMarketOrdersRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetMarketOrdersRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetMarketOrdersRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetMarketOrdersResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetMarketOrdersResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetMarketOrdersResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetAddressOrdersRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetAddressOrdersRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetAddressOrdersRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetAddressOrdersResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetAddressOrdersResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetAddressOrdersResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetAllOrdersRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetAllOrdersRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetAllOrdersRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetAllOrdersResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetAllOrdersResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetAllOrdersResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryMarketInfoRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryMarketInfoRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryMarketInfoRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryMarketInfoResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryMarketInfoResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryMarketInfoResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/exchange/query.pb.gw.go b/x/exchange/query.pb.gw.go new file mode 100644 index 0000000000..0903e975d0 --- /dev/null +++ b/x/exchange/query.pb.gw.go @@ -0,0 +1,752 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: provenance/exchange/v1/query.proto + +/* +Package exchange is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package exchange + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage + +func request_Query_QueryOrderFeeCalc_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryOrderFeeCalcRequest + var metadata runtime.ServerMetadata + + msg, err := client.QueryOrderFeeCalc(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryOrderFeeCalc_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryOrderFeeCalcRequest + var metadata runtime.ServerMetadata + + msg, err := server.QueryOrderFeeCalc(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_QuerySettlementFeeCalc_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QuerySettlementFeeCalcRequest + var metadata runtime.ServerMetadata + + msg, err := client.QuerySettlementFeeCalc(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QuerySettlementFeeCalc_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QuerySettlementFeeCalcRequest + var metadata runtime.ServerMetadata + + msg, err := server.QuerySettlementFeeCalc(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_QueryGetOrder_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetOrderRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["order_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "order_id") + } + + protoReq.OrderId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "order_id", err) + } + + msg, err := client.QueryGetOrder(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryGetOrder_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetOrderRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["order_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "order_id") + } + + protoReq.OrderId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "order_id", err) + } + + msg, err := server.QueryGetOrder(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_QueryGetMarketOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetMarketOrdersRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + msg, err := client.QueryGetMarketOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryGetMarketOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetMarketOrdersRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + msg, err := server.QueryGetMarketOrders(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_QueryGetAddressOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetAddressOrdersRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + msg, err := client.QueryGetAddressOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryGetAddressOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetAddressOrdersRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + msg, err := server.QueryGetAddressOrders(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_QueryGetAllOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetAllOrdersRequest + var metadata runtime.ServerMetadata + + msg, err := client.QueryGetAllOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryGetAllOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetAllOrdersRequest + var metadata runtime.ServerMetadata + + msg, err := server.QueryGetAllOrders(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_QueryMarketInfo_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryMarketInfoRequest + var metadata runtime.ServerMetadata + + msg, err := client.QueryMarketInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryMarketInfo_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryMarketInfoRequest + var metadata runtime.ServerMetadata + + msg, err := server.QueryMarketInfo(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_QueryMarketInfo_1(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryMarketInfoRequest + var metadata runtime.ServerMetadata + + msg, err := client.QueryMarketInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryMarketInfo_1(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryMarketInfoRequest + var metadata runtime.ServerMetadata + + msg, err := server.QueryMarketInfo(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.QueryParams(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.QueryParams(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_QueryOrderFeeCalc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryOrderFeeCalc_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryOrderFeeCalc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QuerySettlementFeeCalc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QuerySettlementFeeCalc_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QuerySettlementFeeCalc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetOrder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryGetOrder_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetOrder_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetMarketOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryGetMarketOrders_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetMarketOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetAddressOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryGetAddressOrders_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetAddressOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetAllOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryGetAllOrders_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetAllOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryMarketInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryMarketInfo_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryMarketInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryMarketInfo_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryMarketInfo_1(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryMarketInfo_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryParams_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryParams_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryParams_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_QueryOrderFeeCalc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryOrderFeeCalc_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryOrderFeeCalc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QuerySettlementFeeCalc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QuerySettlementFeeCalc_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QuerySettlementFeeCalc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetOrder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryGetOrder_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetOrder_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetMarketOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryGetMarketOrders_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetMarketOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetAddressOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryGetAddressOrders_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetAddressOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetAllOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryGetAllOrders_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetAllOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryMarketInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryMarketInfo_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryMarketInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryMarketInfo_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryMarketInfo_1(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryMarketInfo_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryParams_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryParams_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryParams_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_QueryOrderFeeCalc_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "fees", "order"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QuerySettlementFeeCalc_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "fees", "settlement"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryGetOrder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "order", "order_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryGetMarketOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"provenance", "exchange", "v1", "market", "market_id", "orders"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryGetAddressOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "orders", "address"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryGetAllOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "orders"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryMarketInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "market", "info"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryMarketInfo_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "market", "details"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_QueryOrderFeeCalc_0 = runtime.ForwardResponseMessage + + forward_Query_QuerySettlementFeeCalc_0 = runtime.ForwardResponseMessage + + forward_Query_QueryGetOrder_0 = runtime.ForwardResponseMessage + + forward_Query_QueryGetMarketOrders_0 = runtime.ForwardResponseMessage + + forward_Query_QueryGetAddressOrders_0 = runtime.ForwardResponseMessage + + forward_Query_QueryGetAllOrders_0 = runtime.ForwardResponseMessage + + forward_Query_QueryMarketInfo_0 = runtime.ForwardResponseMessage + + forward_Query_QueryMarketInfo_1 = runtime.ForwardResponseMessage + + forward_Query_QueryParams_0 = runtime.ForwardResponseMessage +) diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go new file mode 100644 index 0000000000..7a500333e2 --- /dev/null +++ b/x/exchange/tx.pb.go @@ -0,0 +1,4679 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: provenance/exchange/v1/tx.proto + +package exchange + +import ( + context "context" + fmt "fmt" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgCreateAskRequest is a request message for the CreateAsk endpoint. +type MsgCreateAskRequest struct { +} + +func (m *MsgCreateAskRequest) Reset() { *m = MsgCreateAskRequest{} } +func (m *MsgCreateAskRequest) String() string { return proto.CompactTextString(m) } +func (*MsgCreateAskRequest) ProtoMessage() {} +func (*MsgCreateAskRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{0} +} +func (m *MsgCreateAskRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateAskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateAskRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateAskRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateAskRequest.Merge(m, src) +} +func (m *MsgCreateAskRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateAskRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateAskRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateAskRequest proto.InternalMessageInfo + +// MsgCreateAskResponse is a response message for the CreateAsk endpoint. +type MsgCreateAskResponse struct { +} + +func (m *MsgCreateAskResponse) Reset() { *m = MsgCreateAskResponse{} } +func (m *MsgCreateAskResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateAskResponse) ProtoMessage() {} +func (*MsgCreateAskResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{1} +} +func (m *MsgCreateAskResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateAskResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateAskResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateAskResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateAskResponse.Merge(m, src) +} +func (m *MsgCreateAskResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateAskResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateAskResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateAskResponse proto.InternalMessageInfo + +// MsgCreateBidRequest is a request message for the CreateBid endpoint. +type MsgCreateBidRequest struct { +} + +func (m *MsgCreateBidRequest) Reset() { *m = MsgCreateBidRequest{} } +func (m *MsgCreateBidRequest) String() string { return proto.CompactTextString(m) } +func (*MsgCreateBidRequest) ProtoMessage() {} +func (*MsgCreateBidRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{2} +} +func (m *MsgCreateBidRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateBidRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateBidRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateBidRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateBidRequest.Merge(m, src) +} +func (m *MsgCreateBidRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateBidRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateBidRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateBidRequest proto.InternalMessageInfo + +// MsgCreateBidResponse is a response message for the CreateBid endpoint. +type MsgCreateBidResponse struct { +} + +func (m *MsgCreateBidResponse) Reset() { *m = MsgCreateBidResponse{} } +func (m *MsgCreateBidResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateBidResponse) ProtoMessage() {} +func (*MsgCreateBidResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{3} +} +func (m *MsgCreateBidResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateBidResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateBidResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateBidResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateBidResponse.Merge(m, src) +} +func (m *MsgCreateBidResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateBidResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateBidResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateBidResponse proto.InternalMessageInfo + +// MsgCancelOrderRequest is a request message for the CancelOrder endpoint. +type MsgCancelOrderRequest struct { +} + +func (m *MsgCancelOrderRequest) Reset() { *m = MsgCancelOrderRequest{} } +func (m *MsgCancelOrderRequest) String() string { return proto.CompactTextString(m) } +func (*MsgCancelOrderRequest) ProtoMessage() {} +func (*MsgCancelOrderRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{4} +} +func (m *MsgCancelOrderRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCancelOrderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCancelOrderRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCancelOrderRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCancelOrderRequest.Merge(m, src) +} +func (m *MsgCancelOrderRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgCancelOrderRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCancelOrderRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCancelOrderRequest proto.InternalMessageInfo + +// MsgCancelOrderResponse is a response message for the CancelOrder endpoint. +type MsgCancelOrderResponse struct { +} + +func (m *MsgCancelOrderResponse) Reset() { *m = MsgCancelOrderResponse{} } +func (m *MsgCancelOrderResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCancelOrderResponse) ProtoMessage() {} +func (*MsgCancelOrderResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{5} +} +func (m *MsgCancelOrderResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCancelOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCancelOrderResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCancelOrderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCancelOrderResponse.Merge(m, src) +} +func (m *MsgCancelOrderResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCancelOrderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCancelOrderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCancelOrderResponse proto.InternalMessageInfo + +// MsgFillBidsRequest is a request message for the FillBids endpoint. +type MsgFillBidsRequest struct { +} + +func (m *MsgFillBidsRequest) Reset() { *m = MsgFillBidsRequest{} } +func (m *MsgFillBidsRequest) String() string { return proto.CompactTextString(m) } +func (*MsgFillBidsRequest) ProtoMessage() {} +func (*MsgFillBidsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{6} +} +func (m *MsgFillBidsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgFillBidsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgFillBidsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgFillBidsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgFillBidsRequest.Merge(m, src) +} +func (m *MsgFillBidsRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgFillBidsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgFillBidsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgFillBidsRequest proto.InternalMessageInfo + +// MsgFillBidsResponse is a response message for the FillBids endpoint. +type MsgFillBidsResponse struct { +} + +func (m *MsgFillBidsResponse) Reset() { *m = MsgFillBidsResponse{} } +func (m *MsgFillBidsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgFillBidsResponse) ProtoMessage() {} +func (*MsgFillBidsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{7} +} +func (m *MsgFillBidsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgFillBidsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgFillBidsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgFillBidsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgFillBidsResponse.Merge(m, src) +} +func (m *MsgFillBidsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgFillBidsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgFillBidsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgFillBidsResponse proto.InternalMessageInfo + +// MsgFillAsksRequest is a request message for the FillAsks endpoint. +type MsgFillAsksRequest struct { +} + +func (m *MsgFillAsksRequest) Reset() { *m = MsgFillAsksRequest{} } +func (m *MsgFillAsksRequest) String() string { return proto.CompactTextString(m) } +func (*MsgFillAsksRequest) ProtoMessage() {} +func (*MsgFillAsksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{8} +} +func (m *MsgFillAsksRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgFillAsksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgFillAsksRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgFillAsksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgFillAsksRequest.Merge(m, src) +} +func (m *MsgFillAsksRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgFillAsksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgFillAsksRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgFillAsksRequest proto.InternalMessageInfo + +// MsgFillAsksResponse is a response message for the FillAsks endpoint. +type MsgFillAsksResponse struct { +} + +func (m *MsgFillAsksResponse) Reset() { *m = MsgFillAsksResponse{} } +func (m *MsgFillAsksResponse) String() string { return proto.CompactTextString(m) } +func (*MsgFillAsksResponse) ProtoMessage() {} +func (*MsgFillAsksResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{9} +} +func (m *MsgFillAsksResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgFillAsksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgFillAsksResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgFillAsksResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgFillAsksResponse.Merge(m, src) +} +func (m *MsgFillAsksResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgFillAsksResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgFillAsksResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgFillAsksResponse proto.InternalMessageInfo + +// MsgMarketSettleRequest is a request message for the MarketSettle endpoint. +type MsgMarketSettleRequest struct { +} + +func (m *MsgMarketSettleRequest) Reset() { *m = MsgMarketSettleRequest{} } +func (m *MsgMarketSettleRequest) String() string { return proto.CompactTextString(m) } +func (*MsgMarketSettleRequest) ProtoMessage() {} +func (*MsgMarketSettleRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{10} +} +func (m *MsgMarketSettleRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketSettleRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketSettleRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketSettleRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketSettleRequest.Merge(m, src) +} +func (m *MsgMarketSettleRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketSettleRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketSettleRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketSettleRequest proto.InternalMessageInfo + +// MsgMarketSettleResponse is a response message for the MarketSettle endpoint. +type MsgMarketSettleResponse struct { +} + +func (m *MsgMarketSettleResponse) Reset() { *m = MsgMarketSettleResponse{} } +func (m *MsgMarketSettleResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMarketSettleResponse) ProtoMessage() {} +func (*MsgMarketSettleResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{11} +} +func (m *MsgMarketSettleResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketSettleResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketSettleResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketSettleResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketSettleResponse.Merge(m, src) +} +func (m *MsgMarketSettleResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketSettleResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketSettleResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketSettleResponse proto.InternalMessageInfo + +// MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. +type MsgMarketWithdrawRequest struct { +} + +func (m *MsgMarketWithdrawRequest) Reset() { *m = MsgMarketWithdrawRequest{} } +func (m *MsgMarketWithdrawRequest) String() string { return proto.CompactTextString(m) } +func (*MsgMarketWithdrawRequest) ProtoMessage() {} +func (*MsgMarketWithdrawRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{12} +} +func (m *MsgMarketWithdrawRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketWithdrawRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketWithdrawRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketWithdrawRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketWithdrawRequest.Merge(m, src) +} +func (m *MsgMarketWithdrawRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketWithdrawRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketWithdrawRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketWithdrawRequest proto.InternalMessageInfo + +// MsgMarketWithdrawResponse is a response message for the MarketWithdraw endpoint. +type MsgMarketWithdrawResponse struct { +} + +func (m *MsgMarketWithdrawResponse) Reset() { *m = MsgMarketWithdrawResponse{} } +func (m *MsgMarketWithdrawResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMarketWithdrawResponse) ProtoMessage() {} +func (*MsgMarketWithdrawResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{13} +} +func (m *MsgMarketWithdrawResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketWithdrawResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketWithdrawResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketWithdrawResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketWithdrawResponse.Merge(m, src) +} +func (m *MsgMarketWithdrawResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketWithdrawResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketWithdrawResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketWithdrawResponse proto.InternalMessageInfo + +// MsgMarketUpdateDetailsRequest is a request message for the MarketUpdateDetails endpoint. +type MsgMarketUpdateDetailsRequest struct { +} + +func (m *MsgMarketUpdateDetailsRequest) Reset() { *m = MsgMarketUpdateDetailsRequest{} } +func (m *MsgMarketUpdateDetailsRequest) String() string { return proto.CompactTextString(m) } +func (*MsgMarketUpdateDetailsRequest) ProtoMessage() {} +func (*MsgMarketUpdateDetailsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{14} +} +func (m *MsgMarketUpdateDetailsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketUpdateDetailsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketUpdateDetailsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketUpdateDetailsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketUpdateDetailsRequest.Merge(m, src) +} +func (m *MsgMarketUpdateDetailsRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketUpdateDetailsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketUpdateDetailsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketUpdateDetailsRequest proto.InternalMessageInfo + +// MsgMarketUpdateDetailsResponse is a response message for the MarketUpdateDetails endpoint. +type MsgMarketUpdateDetailsResponse struct { +} + +func (m *MsgMarketUpdateDetailsResponse) Reset() { *m = MsgMarketUpdateDetailsResponse{} } +func (m *MsgMarketUpdateDetailsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMarketUpdateDetailsResponse) ProtoMessage() {} +func (*MsgMarketUpdateDetailsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{15} +} +func (m *MsgMarketUpdateDetailsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketUpdateDetailsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketUpdateDetailsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketUpdateDetailsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketUpdateDetailsResponse.Merge(m, src) +} +func (m *MsgMarketUpdateDetailsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketUpdateDetailsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketUpdateDetailsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketUpdateDetailsResponse proto.InternalMessageInfo + +// MsgMarketUpdateEnabledRequest is a request message for the MarketUpdateEnabled endpoint. +type MsgMarketUpdateEnabledRequest struct { +} + +func (m *MsgMarketUpdateEnabledRequest) Reset() { *m = MsgMarketUpdateEnabledRequest{} } +func (m *MsgMarketUpdateEnabledRequest) String() string { return proto.CompactTextString(m) } +func (*MsgMarketUpdateEnabledRequest) ProtoMessage() {} +func (*MsgMarketUpdateEnabledRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{16} +} +func (m *MsgMarketUpdateEnabledRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketUpdateEnabledRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketUpdateEnabledRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketUpdateEnabledRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketUpdateEnabledRequest.Merge(m, src) +} +func (m *MsgMarketUpdateEnabledRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketUpdateEnabledRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketUpdateEnabledRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketUpdateEnabledRequest proto.InternalMessageInfo + +// MsgMarketUpdateEnabledResponse is a response message for the MarketUpdateEnabled endpoint. +type MsgMarketUpdateEnabledResponse struct { +} + +func (m *MsgMarketUpdateEnabledResponse) Reset() { *m = MsgMarketUpdateEnabledResponse{} } +func (m *MsgMarketUpdateEnabledResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMarketUpdateEnabledResponse) ProtoMessage() {} +func (*MsgMarketUpdateEnabledResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{17} +} +func (m *MsgMarketUpdateEnabledResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketUpdateEnabledResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketUpdateEnabledResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketUpdateEnabledResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketUpdateEnabledResponse.Merge(m, src) +} +func (m *MsgMarketUpdateEnabledResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketUpdateEnabledResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketUpdateEnabledResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketUpdateEnabledResponse proto.InternalMessageInfo + +// MsgMarketUpdateUserSettleRequest is a request message for the MarketUpdateUserSettle endpoint. +type MsgMarketUpdateUserSettleRequest struct { +} + +func (m *MsgMarketUpdateUserSettleRequest) Reset() { *m = MsgMarketUpdateUserSettleRequest{} } +func (m *MsgMarketUpdateUserSettleRequest) String() string { return proto.CompactTextString(m) } +func (*MsgMarketUpdateUserSettleRequest) ProtoMessage() {} +func (*MsgMarketUpdateUserSettleRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{18} +} +func (m *MsgMarketUpdateUserSettleRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketUpdateUserSettleRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketUpdateUserSettleRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketUpdateUserSettleRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketUpdateUserSettleRequest.Merge(m, src) +} +func (m *MsgMarketUpdateUserSettleRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketUpdateUserSettleRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketUpdateUserSettleRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketUpdateUserSettleRequest proto.InternalMessageInfo + +// MsgMarketUpdateUserSettleResponse is a response message for the MarketUpdateUserSettle endpoint. +type MsgMarketUpdateUserSettleResponse struct { +} + +func (m *MsgMarketUpdateUserSettleResponse) Reset() { *m = MsgMarketUpdateUserSettleResponse{} } +func (m *MsgMarketUpdateUserSettleResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMarketUpdateUserSettleResponse) ProtoMessage() {} +func (*MsgMarketUpdateUserSettleResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{19} +} +func (m *MsgMarketUpdateUserSettleResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketUpdateUserSettleResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketUpdateUserSettleResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketUpdateUserSettleResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketUpdateUserSettleResponse.Merge(m, src) +} +func (m *MsgMarketUpdateUserSettleResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketUpdateUserSettleResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketUpdateUserSettleResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketUpdateUserSettleResponse proto.InternalMessageInfo + +// MsgMarketManagePermissionsRequest is a request message for the MarketManagePermissions endpoint. +type MsgMarketManagePermissionsRequest struct { +} + +func (m *MsgMarketManagePermissionsRequest) Reset() { *m = MsgMarketManagePermissionsRequest{} } +func (m *MsgMarketManagePermissionsRequest) String() string { return proto.CompactTextString(m) } +func (*MsgMarketManagePermissionsRequest) ProtoMessage() {} +func (*MsgMarketManagePermissionsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{20} +} +func (m *MsgMarketManagePermissionsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketManagePermissionsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketManagePermissionsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketManagePermissionsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketManagePermissionsRequest.Merge(m, src) +} +func (m *MsgMarketManagePermissionsRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketManagePermissionsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketManagePermissionsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketManagePermissionsRequest proto.InternalMessageInfo + +// MsgMarketManagePermissionsResponse is a response message for the MarketManagePermissions endpoint. +type MsgMarketManagePermissionsResponse struct { +} + +func (m *MsgMarketManagePermissionsResponse) Reset() { *m = MsgMarketManagePermissionsResponse{} } +func (m *MsgMarketManagePermissionsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMarketManagePermissionsResponse) ProtoMessage() {} +func (*MsgMarketManagePermissionsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{21} +} +func (m *MsgMarketManagePermissionsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketManagePermissionsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketManagePermissionsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketManagePermissionsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketManagePermissionsResponse.Merge(m, src) +} +func (m *MsgMarketManagePermissionsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketManagePermissionsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketManagePermissionsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketManagePermissionsResponse proto.InternalMessageInfo + +// MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs endpoint. +type MsgMarketManageReqAttrsRequest struct { +} + +func (m *MsgMarketManageReqAttrsRequest) Reset() { *m = MsgMarketManageReqAttrsRequest{} } +func (m *MsgMarketManageReqAttrsRequest) String() string { return proto.CompactTextString(m) } +func (*MsgMarketManageReqAttrsRequest) ProtoMessage() {} +func (*MsgMarketManageReqAttrsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{22} +} +func (m *MsgMarketManageReqAttrsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketManageReqAttrsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketManageReqAttrsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketManageReqAttrsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketManageReqAttrsRequest.Merge(m, src) +} +func (m *MsgMarketManageReqAttrsRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketManageReqAttrsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketManageReqAttrsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketManageReqAttrsRequest proto.InternalMessageInfo + +// MsgMarketManageReqAttrsResponse is a response message for the MarketManageReqAttrs endpoint. +type MsgMarketManageReqAttrsResponse struct { +} + +func (m *MsgMarketManageReqAttrsResponse) Reset() { *m = MsgMarketManageReqAttrsResponse{} } +func (m *MsgMarketManageReqAttrsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMarketManageReqAttrsResponse) ProtoMessage() {} +func (*MsgMarketManageReqAttrsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{23} +} +func (m *MsgMarketManageReqAttrsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketManageReqAttrsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketManageReqAttrsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketManageReqAttrsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketManageReqAttrsResponse.Merge(m, src) +} +func (m *MsgMarketManageReqAttrsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketManageReqAttrsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketManageReqAttrsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketManageReqAttrsResponse proto.InternalMessageInfo + +// MsgCreateMarketRequest is a request message for the CreateMarket endpoint. +type MsgCreateMarketRequest struct { +} + +func (m *MsgCreateMarketRequest) Reset() { *m = MsgCreateMarketRequest{} } +func (m *MsgCreateMarketRequest) String() string { return proto.CompactTextString(m) } +func (*MsgCreateMarketRequest) ProtoMessage() {} +func (*MsgCreateMarketRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{24} +} +func (m *MsgCreateMarketRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateMarketRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateMarketRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateMarketRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateMarketRequest.Merge(m, src) +} +func (m *MsgCreateMarketRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateMarketRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateMarketRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateMarketRequest proto.InternalMessageInfo + +// MsgCreateMarketResponse is a response message for the CreateMarket endpoint. +type MsgCreateMarketResponse struct { +} + +func (m *MsgCreateMarketResponse) Reset() { *m = MsgCreateMarketResponse{} } +func (m *MsgCreateMarketResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateMarketResponse) ProtoMessage() {} +func (*MsgCreateMarketResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{25} +} +func (m *MsgCreateMarketResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateMarketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateMarketResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateMarketResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateMarketResponse.Merge(m, src) +} +func (m *MsgCreateMarketResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateMarketResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateMarketResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateMarketResponse proto.InternalMessageInfo + +// MsgGovCreateMarketRequest is a request message for the GovCreateMarket endpoint. +type MsgGovCreateMarketRequest struct { +} + +func (m *MsgGovCreateMarketRequest) Reset() { *m = MsgGovCreateMarketRequest{} } +func (m *MsgGovCreateMarketRequest) String() string { return proto.CompactTextString(m) } +func (*MsgGovCreateMarketRequest) ProtoMessage() {} +func (*MsgGovCreateMarketRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{26} +} +func (m *MsgGovCreateMarketRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgGovCreateMarketRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgGovCreateMarketRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgGovCreateMarketRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgGovCreateMarketRequest.Merge(m, src) +} +func (m *MsgGovCreateMarketRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgGovCreateMarketRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgGovCreateMarketRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgGovCreateMarketRequest proto.InternalMessageInfo + +// MsgGovCreateMarketResponse is a response message for the GovCreateMarket endpoint. +type MsgGovCreateMarketResponse struct { +} + +func (m *MsgGovCreateMarketResponse) Reset() { *m = MsgGovCreateMarketResponse{} } +func (m *MsgGovCreateMarketResponse) String() string { return proto.CompactTextString(m) } +func (*MsgGovCreateMarketResponse) ProtoMessage() {} +func (*MsgGovCreateMarketResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{27} +} +func (m *MsgGovCreateMarketResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgGovCreateMarketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgGovCreateMarketResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgGovCreateMarketResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgGovCreateMarketResponse.Merge(m, src) +} +func (m *MsgGovCreateMarketResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgGovCreateMarketResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgGovCreateMarketResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgGovCreateMarketResponse proto.InternalMessageInfo + +// MsgGovManageFeesRequest is a request message for the GovManageFees endpoint. +type MsgGovManageFeesRequest struct { +} + +func (m *MsgGovManageFeesRequest) Reset() { *m = MsgGovManageFeesRequest{} } +func (m *MsgGovManageFeesRequest) String() string { return proto.CompactTextString(m) } +func (*MsgGovManageFeesRequest) ProtoMessage() {} +func (*MsgGovManageFeesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{28} +} +func (m *MsgGovManageFeesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgGovManageFeesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgGovManageFeesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgGovManageFeesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgGovManageFeesRequest.Merge(m, src) +} +func (m *MsgGovManageFeesRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgGovManageFeesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgGovManageFeesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgGovManageFeesRequest proto.InternalMessageInfo + +// MsgGovManageFeesResponse is a response message for the GovManageFees endpoint. +type MsgGovManageFeesResponse struct { +} + +func (m *MsgGovManageFeesResponse) Reset() { *m = MsgGovManageFeesResponse{} } +func (m *MsgGovManageFeesResponse) String() string { return proto.CompactTextString(m) } +func (*MsgGovManageFeesResponse) ProtoMessage() {} +func (*MsgGovManageFeesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{29} +} +func (m *MsgGovManageFeesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgGovManageFeesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgGovManageFeesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgGovManageFeesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgGovManageFeesResponse.Merge(m, src) +} +func (m *MsgGovManageFeesResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgGovManageFeesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgGovManageFeesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgGovManageFeesResponse proto.InternalMessageInfo + +// MsgGovUpdateParamsRequest is a request message for the GovUpdateParams endpoint. +type MsgGovUpdateParamsRequest struct { +} + +func (m *MsgGovUpdateParamsRequest) Reset() { *m = MsgGovUpdateParamsRequest{} } +func (m *MsgGovUpdateParamsRequest) String() string { return proto.CompactTextString(m) } +func (*MsgGovUpdateParamsRequest) ProtoMessage() {} +func (*MsgGovUpdateParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{30} +} +func (m *MsgGovUpdateParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgGovUpdateParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgGovUpdateParamsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgGovUpdateParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgGovUpdateParamsRequest.Merge(m, src) +} +func (m *MsgGovUpdateParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgGovUpdateParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgGovUpdateParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgGovUpdateParamsRequest proto.InternalMessageInfo + +// MsgGovUpdateParamsResponse is a response message for the GovUpdateParams endpoint. +type MsgGovUpdateParamsResponse struct { +} + +func (m *MsgGovUpdateParamsResponse) Reset() { *m = MsgGovUpdateParamsResponse{} } +func (m *MsgGovUpdateParamsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgGovUpdateParamsResponse) ProtoMessage() {} +func (*MsgGovUpdateParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{31} +} +func (m *MsgGovUpdateParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgGovUpdateParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgGovUpdateParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgGovUpdateParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgGovUpdateParamsResponse.Merge(m, src) +} +func (m *MsgGovUpdateParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgGovUpdateParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgGovUpdateParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgGovUpdateParamsResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgCreateAskRequest)(nil), "provenance.exchange.v1.MsgCreateAskRequest") + proto.RegisterType((*MsgCreateAskResponse)(nil), "provenance.exchange.v1.MsgCreateAskResponse") + proto.RegisterType((*MsgCreateBidRequest)(nil), "provenance.exchange.v1.MsgCreateBidRequest") + proto.RegisterType((*MsgCreateBidResponse)(nil), "provenance.exchange.v1.MsgCreateBidResponse") + proto.RegisterType((*MsgCancelOrderRequest)(nil), "provenance.exchange.v1.MsgCancelOrderRequest") + proto.RegisterType((*MsgCancelOrderResponse)(nil), "provenance.exchange.v1.MsgCancelOrderResponse") + proto.RegisterType((*MsgFillBidsRequest)(nil), "provenance.exchange.v1.MsgFillBidsRequest") + proto.RegisterType((*MsgFillBidsResponse)(nil), "provenance.exchange.v1.MsgFillBidsResponse") + proto.RegisterType((*MsgFillAsksRequest)(nil), "provenance.exchange.v1.MsgFillAsksRequest") + proto.RegisterType((*MsgFillAsksResponse)(nil), "provenance.exchange.v1.MsgFillAsksResponse") + proto.RegisterType((*MsgMarketSettleRequest)(nil), "provenance.exchange.v1.MsgMarketSettleRequest") + proto.RegisterType((*MsgMarketSettleResponse)(nil), "provenance.exchange.v1.MsgMarketSettleResponse") + proto.RegisterType((*MsgMarketWithdrawRequest)(nil), "provenance.exchange.v1.MsgMarketWithdrawRequest") + proto.RegisterType((*MsgMarketWithdrawResponse)(nil), "provenance.exchange.v1.MsgMarketWithdrawResponse") + proto.RegisterType((*MsgMarketUpdateDetailsRequest)(nil), "provenance.exchange.v1.MsgMarketUpdateDetailsRequest") + proto.RegisterType((*MsgMarketUpdateDetailsResponse)(nil), "provenance.exchange.v1.MsgMarketUpdateDetailsResponse") + proto.RegisterType((*MsgMarketUpdateEnabledRequest)(nil), "provenance.exchange.v1.MsgMarketUpdateEnabledRequest") + proto.RegisterType((*MsgMarketUpdateEnabledResponse)(nil), "provenance.exchange.v1.MsgMarketUpdateEnabledResponse") + proto.RegisterType((*MsgMarketUpdateUserSettleRequest)(nil), "provenance.exchange.v1.MsgMarketUpdateUserSettleRequest") + proto.RegisterType((*MsgMarketUpdateUserSettleResponse)(nil), "provenance.exchange.v1.MsgMarketUpdateUserSettleResponse") + proto.RegisterType((*MsgMarketManagePermissionsRequest)(nil), "provenance.exchange.v1.MsgMarketManagePermissionsRequest") + proto.RegisterType((*MsgMarketManagePermissionsResponse)(nil), "provenance.exchange.v1.MsgMarketManagePermissionsResponse") + proto.RegisterType((*MsgMarketManageReqAttrsRequest)(nil), "provenance.exchange.v1.MsgMarketManageReqAttrsRequest") + proto.RegisterType((*MsgMarketManageReqAttrsResponse)(nil), "provenance.exchange.v1.MsgMarketManageReqAttrsResponse") + proto.RegisterType((*MsgCreateMarketRequest)(nil), "provenance.exchange.v1.MsgCreateMarketRequest") + proto.RegisterType((*MsgCreateMarketResponse)(nil), "provenance.exchange.v1.MsgCreateMarketResponse") + proto.RegisterType((*MsgGovCreateMarketRequest)(nil), "provenance.exchange.v1.MsgGovCreateMarketRequest") + proto.RegisterType((*MsgGovCreateMarketResponse)(nil), "provenance.exchange.v1.MsgGovCreateMarketResponse") + proto.RegisterType((*MsgGovManageFeesRequest)(nil), "provenance.exchange.v1.MsgGovManageFeesRequest") + proto.RegisterType((*MsgGovManageFeesResponse)(nil), "provenance.exchange.v1.MsgGovManageFeesResponse") + proto.RegisterType((*MsgGovUpdateParamsRequest)(nil), "provenance.exchange.v1.MsgGovUpdateParamsRequest") + proto.RegisterType((*MsgGovUpdateParamsResponse)(nil), "provenance.exchange.v1.MsgGovUpdateParamsResponse") +} + +func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } + +var fileDescriptor_e333fcffc093bd1b = []byte{ + // 697 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0x5b, 0x4f, 0xd4, 0x40, + 0x14, 0xa6, 0x31, 0x31, 0x7a, 0xbc, 0x90, 0x0c, 0xb0, 0xc0, 0xa8, 0x05, 0x56, 0x9f, 0x44, 0x5a, + 0xc0, 0x88, 0x97, 0x37, 0x56, 0x85, 0xa7, 0x8d, 0x04, 0x43, 0x4c, 0x7c, 0x1b, 0xb6, 0xe3, 0xd2, + 0x50, 0xda, 0x65, 0x66, 0x58, 0xf7, 0x07, 0x98, 0x18, 0x1f, 0x4c, 0xf8, 0x59, 0x3e, 0xf2, 0xe8, + 0xa3, 0x81, 0x3f, 0x62, 0x96, 0x99, 0x5e, 0xa6, 0x9d, 0x5e, 0x96, 0xc7, 0x9d, 0xf3, 0x5d, 0xce, + 0x6c, 0x7b, 0xbe, 0x53, 0x58, 0x1a, 0xb0, 0x68, 0x48, 0x43, 0x12, 0xf6, 0xa8, 0x4b, 0x47, 0xbd, + 0x23, 0x12, 0xf6, 0xa9, 0x3b, 0xdc, 0x70, 0xc5, 0xc8, 0x19, 0xb0, 0x48, 0x44, 0xa8, 0x95, 0x02, + 0x9c, 0x18, 0xe0, 0x0c, 0x37, 0xda, 0x73, 0x30, 0xd3, 0xe5, 0xfd, 0xf7, 0x8c, 0x12, 0x41, 0xb7, + 0xf9, 0xf1, 0x3e, 0x3d, 0x3d, 0xa3, 0x5c, 0xb4, 0x5b, 0x30, 0xab, 0x1f, 0xf3, 0x41, 0x14, 0x72, + 0xaa, 0xc1, 0x3b, 0xbe, 0x67, 0x82, 0x5f, 0x1f, 0x2b, 0xf8, 0x3c, 0xcc, 0x8d, 0xcf, 0xc7, 0xae, + 0xc1, 0x27, 0xe6, 0x51, 0x16, 0x13, 0x16, 0xa0, 0x95, 0x2f, 0x28, 0xca, 0x2c, 0xa0, 0x2e, 0xef, + 0xef, 0xf8, 0x41, 0xd0, 0xf1, 0x3d, 0x1e, 0xe3, 0xa5, 0x6f, 0x7a, 0x5a, 0x00, 0x6f, 0xf3, 0x63, + 0x03, 0x58, 0x9e, 0x2a, 0xb0, 0xf4, 0xec, 0x12, 0x76, 0x4c, 0xc5, 0x67, 0x2a, 0x44, 0x40, 0x63, + 0xc2, 0x22, 0xcc, 0x17, 0x2a, 0x8a, 0x84, 0x61, 0x21, 0x29, 0x7d, 0xf1, 0xc5, 0x91, 0xc7, 0xc8, + 0xf7, 0x98, 0xf6, 0x08, 0x16, 0x0d, 0x35, 0x45, 0x5c, 0x82, 0x27, 0x49, 0xf1, 0x60, 0xe0, 0x11, + 0x41, 0x3f, 0x50, 0x41, 0xfc, 0x20, 0xe9, 0x72, 0x19, 0xec, 0x32, 0x40, 0xa9, 0xc4, 0xc7, 0x90, + 0x1c, 0x06, 0xd4, 0x2b, 0x97, 0x48, 0x00, 0x4a, 0xa2, 0x0d, 0xcb, 0x39, 0xc4, 0x01, 0xa7, 0x4c, + 0xbf, 0xfd, 0x53, 0x58, 0xa9, 0xc0, 0x28, 0xa1, 0x2c, 0xa8, 0x4b, 0x42, 0xd2, 0xa7, 0x7b, 0x94, + 0x9d, 0xf8, 0x9c, 0xfb, 0x51, 0x98, 0x5c, 0xe9, 0x19, 0xb4, 0xab, 0x40, 0x4a, 0x2a, 0xdb, 0xb5, + 0x44, 0xed, 0xd3, 0xd3, 0x6d, 0x21, 0x58, 0xa2, 0xb3, 0x02, 0x4b, 0xa5, 0x08, 0xed, 0x61, 0xca, + 0x37, 0x4e, 0x02, 0xf5, 0x87, 0xa9, 0x57, 0x14, 0x49, 0x3e, 0xb0, 0xdd, 0x68, 0x68, 0xe2, 0x3d, + 0x06, 0x6c, 0x2a, 0x2a, 0xaa, 0x54, 0xdd, 0x8d, 0x86, 0xb2, 0x9f, 0x1d, 0x4a, 0x93, 0x6e, 0xe5, + 0x2b, 0x92, 0x2b, 0xe5, 0x1d, 0xe5, 0x1f, 0xbb, 0x47, 0x18, 0x39, 0xe1, 0x05, 0x47, 0xbd, 0x28, + 0xa9, 0x9b, 0xbf, 0xa6, 0xe1, 0x56, 0x97, 0xf7, 0xd1, 0x37, 0xb8, 0x9b, 0xcc, 0x21, 0x5a, 0x75, + 0xcc, 0x73, 0xec, 0x18, 0x86, 0x18, 0xbf, 0x68, 0x06, 0x96, 0x7e, 0xa9, 0x4f, 0xc7, 0xf7, 0x1a, + 0xf8, 0xa4, 0xd3, 0xdf, 0xc0, 0x27, 0x93, 0x09, 0x28, 0x80, 0x7b, 0x99, 0xb9, 0x47, 0x6b, 0x55, + 0xe4, 0x42, 0x70, 0x60, 0xa7, 0x29, 0x5c, 0xb9, 0xf5, 0xe0, 0x4e, 0x9c, 0x1a, 0xe8, 0x79, 0x05, + 0x37, 0x17, 0x38, 0x78, 0xb5, 0x11, 0x56, 0x37, 0x19, 0xa7, 0x4d, 0xad, 0x49, 0x26, 0xa8, 0x6a, + 0x4d, 0xb2, 0xf1, 0x85, 0x22, 0xb8, 0x9f, 0x4d, 0x28, 0x54, 0xf5, 0x4f, 0x18, 0x42, 0x0e, 0xbb, + 0x8d, 0xf1, 0xca, 0xf0, 0x0c, 0x1e, 0xea, 0xd9, 0x86, 0xd6, 0x6b, 0x25, 0x72, 0x11, 0x89, 0x37, + 0x26, 0x60, 0x28, 0xdb, 0x1f, 0x16, 0xcc, 0x18, 0x52, 0x11, 0xbd, 0xaa, 0x95, 0x32, 0xc5, 0x2c, + 0xde, 0x9a, 0x94, 0x56, 0xd2, 0x86, 0x4a, 0xd6, 0xc6, 0x6d, 0xe8, 0x51, 0xdd, 0xb8, 0x8d, 0x5c, + 0x80, 0xa3, 0xdf, 0x16, 0xb4, 0xcc, 0xd1, 0x8c, 0xde, 0x34, 0x94, 0x2c, 0x24, 0x3e, 0x7e, 0x7b, + 0x03, 0xa6, 0xea, 0xe7, 0xdc, 0x82, 0xf9, 0x92, 0x80, 0x47, 0xf5, 0xb2, 0x65, 0x9b, 0x03, 0xbf, + 0xbb, 0x09, 0x55, 0xb5, 0xf4, 0xd3, 0x82, 0x59, 0xd3, 0xae, 0x40, 0x5b, 0x0d, 0x45, 0x73, 0xeb, + 0x07, 0xbf, 0x9e, 0x98, 0x97, 0x8e, 0x68, 0x76, 0x79, 0x54, 0x8e, 0xa8, 0x61, 0x05, 0x55, 0x8e, + 0xa8, 0x69, 0x2b, 0xa1, 0x11, 0x4c, 0xe7, 0x16, 0x16, 0xaa, 0x9a, 0x38, 0xf3, 0xe6, 0xc3, 0x9b, + 0x93, 0x50, 0x94, 0x33, 0x83, 0x07, 0xda, 0xc6, 0x43, 0x6e, 0xb5, 0x48, 0x61, 0x6d, 0xe2, 0xf5, + 0xe6, 0x04, 0xed, 0xb6, 0xd9, 0x65, 0x59, 0x77, 0x5b, 0xc3, 0xd6, 0xad, 0xbb, 0xad, 0x69, 0x17, + 0x77, 0xe8, 0x9f, 0x4b, 0xdb, 0xba, 0xb8, 0xb4, 0xad, 0x7f, 0x97, 0xb6, 0x75, 0x7e, 0x65, 0x4f, + 0x5d, 0x5c, 0xd9, 0x53, 0x7f, 0xaf, 0xec, 0x29, 0x58, 0xf4, 0xa3, 0x12, 0xbd, 0x3d, 0xeb, 0xab, + 0xd3, 0xf7, 0xc5, 0xd1, 0xd9, 0xa1, 0xd3, 0x8b, 0x4e, 0xdc, 0x14, 0xb4, 0xe6, 0x47, 0x99, 0x5f, + 0xee, 0x28, 0xf9, 0x60, 0x3f, 0xbc, 0x7d, 0xfd, 0xad, 0xfe, 0xf2, 0x7f, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xa1, 0xfc, 0xc3, 0xf5, 0xce, 0x0b, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // CreateAsk creates an ask order (to sell something you own). + CreateAsk(ctx context.Context, in *MsgCreateAskRequest, opts ...grpc.CallOption) (*MsgCreateAskResponse, error) + // CreateBid creates an bid order (to buy something you want). + CreateBid(ctx context.Context, in *MsgCreateBidRequest, opts ...grpc.CallOption) (*MsgCreateBidResponse, error) + // CancelOrder cancels an order. + CancelOrder(ctx context.Context, in *MsgCancelOrderRequest, opts ...grpc.CallOption) (*MsgCancelOrderResponse, error) + // FillBids uses the assets in your account to fulfill one or more bids (similar to a fill-or-cancel ask). + FillBids(ctx context.Context, in *MsgFillBidsRequest, opts ...grpc.CallOption) (*MsgFillBidsResponse, error) + // FillAsks uses the funds in your account to fulfill one or more asks (similar to a fill-or-cancel bid). + FillAsks(ctx context.Context, in *MsgFillAsksRequest, opts ...grpc.CallOption) (*MsgFillAsksResponse, error) + // MarketSettle is a market endpoint to trigger the settlement of orders. + MarketSettle(ctx context.Context, in *MsgMarketSettleRequest, opts ...grpc.CallOption) (*MsgMarketSettleResponse, error) + // MarketWithdraw is a market endpoint to withdraw fees that have been collected. + MarketWithdraw(ctx context.Context, in *MsgMarketWithdrawRequest, opts ...grpc.CallOption) (*MsgMarketWithdrawResponse, error) + // MarketUpdateDetails is a market endpoint to update its details. + MarketUpdateDetails(ctx context.Context, in *MsgMarketUpdateDetailsRequest, opts ...grpc.CallOption) (*MsgMarketUpdateDetailsResponse, error) + // MarketUpdateEnabled is a market endpoint to update whether its accepting orders. + MarketUpdateEnabled(ctx context.Context, in *MsgMarketUpdateEnabledRequest, opts ...grpc.CallOption) (*MsgMarketUpdateEnabledResponse, error) + // MarketUpdateUserSettle is a market endpoint to update whether it allows user-initiated settlement. + MarketUpdateUserSettle(ctx context.Context, in *MsgMarketUpdateUserSettleRequest, opts ...grpc.CallOption) (*MsgMarketUpdateUserSettleResponse, error) + // MarketManagePermissions is a market endpoint to manage a market's user permissions. + MarketManagePermissions(ctx context.Context, in *MsgMarketManagePermissionsRequest, opts ...grpc.CallOption) (*MsgMarketManagePermissionsResponse, error) + // MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. + MarketManageReqAttrs(ctx context.Context, in *MsgMarketManageReqAttrsRequest, opts ...grpc.CallOption) (*MsgMarketManageReqAttrsResponse, error) + // CreateMarket reserves the next market id and submits a GovCreateMarket governance proposal to create the market. + CreateMarket(ctx context.Context, in *MsgCreateMarketRequest, opts ...grpc.CallOption) (*MsgCreateMarketResponse, error) + // GovCreateMarket is a governance proposal endpoint for creating a market. + // The CreateMarket endpoint should be used to submit one of these unless you don't need to know the market id until + // after the proposal passes. + GovCreateMarket(ctx context.Context, in *MsgGovCreateMarketRequest, opts ...grpc.CallOption) (*MsgGovCreateMarketResponse, error) + // GovManageFees is a governance proposal endpoint for updating a market's fees. + GovManageFees(ctx context.Context, in *MsgGovManageFeesRequest, opts ...grpc.CallOption) (*MsgGovManageFeesResponse, error) + // GovUpdateParams is a governance proposal endpoint for updating the exchange module's params. + GovUpdateParams(ctx context.Context, in *MsgGovUpdateParamsRequest, opts ...grpc.CallOption) (*MsgGovUpdateParamsResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) CreateAsk(ctx context.Context, in *MsgCreateAskRequest, opts ...grpc.CallOption) (*MsgCreateAskResponse, error) { + out := new(MsgCreateAskResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/CreateAsk", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) CreateBid(ctx context.Context, in *MsgCreateBidRequest, opts ...grpc.CallOption) (*MsgCreateBidResponse, error) { + out := new(MsgCreateBidResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/CreateBid", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) CancelOrder(ctx context.Context, in *MsgCancelOrderRequest, opts ...grpc.CallOption) (*MsgCancelOrderResponse, error) { + out := new(MsgCancelOrderResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/CancelOrder", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) FillBids(ctx context.Context, in *MsgFillBidsRequest, opts ...grpc.CallOption) (*MsgFillBidsResponse, error) { + out := new(MsgFillBidsResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/FillBids", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) FillAsks(ctx context.Context, in *MsgFillAsksRequest, opts ...grpc.CallOption) (*MsgFillAsksResponse, error) { + out := new(MsgFillAsksResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/FillAsks", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) MarketSettle(ctx context.Context, in *MsgMarketSettleRequest, opts ...grpc.CallOption) (*MsgMarketSettleResponse, error) { + out := new(MsgMarketSettleResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/MarketSettle", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) MarketWithdraw(ctx context.Context, in *MsgMarketWithdrawRequest, opts ...grpc.CallOption) (*MsgMarketWithdrawResponse, error) { + out := new(MsgMarketWithdrawResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/MarketWithdraw", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) MarketUpdateDetails(ctx context.Context, in *MsgMarketUpdateDetailsRequest, opts ...grpc.CallOption) (*MsgMarketUpdateDetailsResponse, error) { + out := new(MsgMarketUpdateDetailsResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/MarketUpdateDetails", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) MarketUpdateEnabled(ctx context.Context, in *MsgMarketUpdateEnabledRequest, opts ...grpc.CallOption) (*MsgMarketUpdateEnabledResponse, error) { + out := new(MsgMarketUpdateEnabledResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/MarketUpdateEnabled", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) MarketUpdateUserSettle(ctx context.Context, in *MsgMarketUpdateUserSettleRequest, opts ...grpc.CallOption) (*MsgMarketUpdateUserSettleResponse, error) { + out := new(MsgMarketUpdateUserSettleResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/MarketUpdateUserSettle", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) MarketManagePermissions(ctx context.Context, in *MsgMarketManagePermissionsRequest, opts ...grpc.CallOption) (*MsgMarketManagePermissionsResponse, error) { + out := new(MsgMarketManagePermissionsResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/MarketManagePermissions", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) MarketManageReqAttrs(ctx context.Context, in *MsgMarketManageReqAttrsRequest, opts ...grpc.CallOption) (*MsgMarketManageReqAttrsResponse, error) { + out := new(MsgMarketManageReqAttrsResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/MarketManageReqAttrs", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) CreateMarket(ctx context.Context, in *MsgCreateMarketRequest, opts ...grpc.CallOption) (*MsgCreateMarketResponse, error) { + out := new(MsgCreateMarketResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/CreateMarket", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) GovCreateMarket(ctx context.Context, in *MsgGovCreateMarketRequest, opts ...grpc.CallOption) (*MsgGovCreateMarketResponse, error) { + out := new(MsgGovCreateMarketResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/GovCreateMarket", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) GovManageFees(ctx context.Context, in *MsgGovManageFeesRequest, opts ...grpc.CallOption) (*MsgGovManageFeesResponse, error) { + out := new(MsgGovManageFeesResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/GovManageFees", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) GovUpdateParams(ctx context.Context, in *MsgGovUpdateParamsRequest, opts ...grpc.CallOption) (*MsgGovUpdateParamsResponse, error) { + out := new(MsgGovUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/GovUpdateParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // CreateAsk creates an ask order (to sell something you own). + CreateAsk(context.Context, *MsgCreateAskRequest) (*MsgCreateAskResponse, error) + // CreateBid creates an bid order (to buy something you want). + CreateBid(context.Context, *MsgCreateBidRequest) (*MsgCreateBidResponse, error) + // CancelOrder cancels an order. + CancelOrder(context.Context, *MsgCancelOrderRequest) (*MsgCancelOrderResponse, error) + // FillBids uses the assets in your account to fulfill one or more bids (similar to a fill-or-cancel ask). + FillBids(context.Context, *MsgFillBidsRequest) (*MsgFillBidsResponse, error) + // FillAsks uses the funds in your account to fulfill one or more asks (similar to a fill-or-cancel bid). + FillAsks(context.Context, *MsgFillAsksRequest) (*MsgFillAsksResponse, error) + // MarketSettle is a market endpoint to trigger the settlement of orders. + MarketSettle(context.Context, *MsgMarketSettleRequest) (*MsgMarketSettleResponse, error) + // MarketWithdraw is a market endpoint to withdraw fees that have been collected. + MarketWithdraw(context.Context, *MsgMarketWithdrawRequest) (*MsgMarketWithdrawResponse, error) + // MarketUpdateDetails is a market endpoint to update its details. + MarketUpdateDetails(context.Context, *MsgMarketUpdateDetailsRequest) (*MsgMarketUpdateDetailsResponse, error) + // MarketUpdateEnabled is a market endpoint to update whether its accepting orders. + MarketUpdateEnabled(context.Context, *MsgMarketUpdateEnabledRequest) (*MsgMarketUpdateEnabledResponse, error) + // MarketUpdateUserSettle is a market endpoint to update whether it allows user-initiated settlement. + MarketUpdateUserSettle(context.Context, *MsgMarketUpdateUserSettleRequest) (*MsgMarketUpdateUserSettleResponse, error) + // MarketManagePermissions is a market endpoint to manage a market's user permissions. + MarketManagePermissions(context.Context, *MsgMarketManagePermissionsRequest) (*MsgMarketManagePermissionsResponse, error) + // MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. + MarketManageReqAttrs(context.Context, *MsgMarketManageReqAttrsRequest) (*MsgMarketManageReqAttrsResponse, error) + // CreateMarket reserves the next market id and submits a GovCreateMarket governance proposal to create the market. + CreateMarket(context.Context, *MsgCreateMarketRequest) (*MsgCreateMarketResponse, error) + // GovCreateMarket is a governance proposal endpoint for creating a market. + // The CreateMarket endpoint should be used to submit one of these unless you don't need to know the market id until + // after the proposal passes. + GovCreateMarket(context.Context, *MsgGovCreateMarketRequest) (*MsgGovCreateMarketResponse, error) + // GovManageFees is a governance proposal endpoint for updating a market's fees. + GovManageFees(context.Context, *MsgGovManageFeesRequest) (*MsgGovManageFeesResponse, error) + // GovUpdateParams is a governance proposal endpoint for updating the exchange module's params. + GovUpdateParams(context.Context, *MsgGovUpdateParamsRequest) (*MsgGovUpdateParamsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) CreateAsk(ctx context.Context, req *MsgCreateAskRequest) (*MsgCreateAskResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateAsk not implemented") +} +func (*UnimplementedMsgServer) CreateBid(ctx context.Context, req *MsgCreateBidRequest) (*MsgCreateBidResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateBid not implemented") +} +func (*UnimplementedMsgServer) CancelOrder(ctx context.Context, req *MsgCancelOrderRequest) (*MsgCancelOrderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CancelOrder not implemented") +} +func (*UnimplementedMsgServer) FillBids(ctx context.Context, req *MsgFillBidsRequest) (*MsgFillBidsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FillBids not implemented") +} +func (*UnimplementedMsgServer) FillAsks(ctx context.Context, req *MsgFillAsksRequest) (*MsgFillAsksResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FillAsks not implemented") +} +func (*UnimplementedMsgServer) MarketSettle(ctx context.Context, req *MsgMarketSettleRequest) (*MsgMarketSettleResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MarketSettle not implemented") +} +func (*UnimplementedMsgServer) MarketWithdraw(ctx context.Context, req *MsgMarketWithdrawRequest) (*MsgMarketWithdrawResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MarketWithdraw not implemented") +} +func (*UnimplementedMsgServer) MarketUpdateDetails(ctx context.Context, req *MsgMarketUpdateDetailsRequest) (*MsgMarketUpdateDetailsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MarketUpdateDetails not implemented") +} +func (*UnimplementedMsgServer) MarketUpdateEnabled(ctx context.Context, req *MsgMarketUpdateEnabledRequest) (*MsgMarketUpdateEnabledResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MarketUpdateEnabled not implemented") +} +func (*UnimplementedMsgServer) MarketUpdateUserSettle(ctx context.Context, req *MsgMarketUpdateUserSettleRequest) (*MsgMarketUpdateUserSettleResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MarketUpdateUserSettle not implemented") +} +func (*UnimplementedMsgServer) MarketManagePermissions(ctx context.Context, req *MsgMarketManagePermissionsRequest) (*MsgMarketManagePermissionsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MarketManagePermissions not implemented") +} +func (*UnimplementedMsgServer) MarketManageReqAttrs(ctx context.Context, req *MsgMarketManageReqAttrsRequest) (*MsgMarketManageReqAttrsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MarketManageReqAttrs not implemented") +} +func (*UnimplementedMsgServer) CreateMarket(ctx context.Context, req *MsgCreateMarketRequest) (*MsgCreateMarketResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateMarket not implemented") +} +func (*UnimplementedMsgServer) GovCreateMarket(ctx context.Context, req *MsgGovCreateMarketRequest) (*MsgGovCreateMarketResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GovCreateMarket not implemented") +} +func (*UnimplementedMsgServer) GovManageFees(ctx context.Context, req *MsgGovManageFeesRequest) (*MsgGovManageFeesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GovManageFees not implemented") +} +func (*UnimplementedMsgServer) GovUpdateParams(ctx context.Context, req *MsgGovUpdateParamsRequest) (*MsgGovUpdateParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GovUpdateParams not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_CreateAsk_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateAskRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateAsk(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/CreateAsk", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateAsk(ctx, req.(*MsgCreateAskRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_CreateBid_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateBidRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateBid(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/CreateBid", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateBid(ctx, req.(*MsgCreateBidRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_CancelOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCancelOrderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CancelOrder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/CancelOrder", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CancelOrder(ctx, req.(*MsgCancelOrderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_FillBids_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgFillBidsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).FillBids(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/FillBids", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).FillBids(ctx, req.(*MsgFillBidsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_FillAsks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgFillAsksRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).FillAsks(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/FillAsks", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).FillAsks(ctx, req.(*MsgFillAsksRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_MarketSettle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMarketSettleRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MarketSettle(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/MarketSettle", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MarketSettle(ctx, req.(*MsgMarketSettleRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_MarketWithdraw_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMarketWithdrawRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MarketWithdraw(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/MarketWithdraw", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MarketWithdraw(ctx, req.(*MsgMarketWithdrawRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_MarketUpdateDetails_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMarketUpdateDetailsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MarketUpdateDetails(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/MarketUpdateDetails", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MarketUpdateDetails(ctx, req.(*MsgMarketUpdateDetailsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_MarketUpdateEnabled_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMarketUpdateEnabledRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MarketUpdateEnabled(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/MarketUpdateEnabled", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MarketUpdateEnabled(ctx, req.(*MsgMarketUpdateEnabledRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_MarketUpdateUserSettle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMarketUpdateUserSettleRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MarketUpdateUserSettle(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/MarketUpdateUserSettle", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MarketUpdateUserSettle(ctx, req.(*MsgMarketUpdateUserSettleRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_MarketManagePermissions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMarketManagePermissionsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MarketManagePermissions(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/MarketManagePermissions", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MarketManagePermissions(ctx, req.(*MsgMarketManagePermissionsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_MarketManageReqAttrs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMarketManageReqAttrsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MarketManageReqAttrs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/MarketManageReqAttrs", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MarketManageReqAttrs(ctx, req.(*MsgMarketManageReqAttrsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_CreateMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateMarketRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateMarket(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/CreateMarket", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateMarket(ctx, req.(*MsgCreateMarketRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_GovCreateMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgGovCreateMarketRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).GovCreateMarket(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/GovCreateMarket", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).GovCreateMarket(ctx, req.(*MsgGovCreateMarketRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_GovManageFees_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgGovManageFeesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).GovManageFees(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/GovManageFees", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).GovManageFees(ctx, req.(*MsgGovManageFeesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_GovUpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgGovUpdateParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).GovUpdateParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/GovUpdateParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).GovUpdateParams(ctx, req.(*MsgGovUpdateParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "provenance.exchange.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateAsk", + Handler: _Msg_CreateAsk_Handler, + }, + { + MethodName: "CreateBid", + Handler: _Msg_CreateBid_Handler, + }, + { + MethodName: "CancelOrder", + Handler: _Msg_CancelOrder_Handler, + }, + { + MethodName: "FillBids", + Handler: _Msg_FillBids_Handler, + }, + { + MethodName: "FillAsks", + Handler: _Msg_FillAsks_Handler, + }, + { + MethodName: "MarketSettle", + Handler: _Msg_MarketSettle_Handler, + }, + { + MethodName: "MarketWithdraw", + Handler: _Msg_MarketWithdraw_Handler, + }, + { + MethodName: "MarketUpdateDetails", + Handler: _Msg_MarketUpdateDetails_Handler, + }, + { + MethodName: "MarketUpdateEnabled", + Handler: _Msg_MarketUpdateEnabled_Handler, + }, + { + MethodName: "MarketUpdateUserSettle", + Handler: _Msg_MarketUpdateUserSettle_Handler, + }, + { + MethodName: "MarketManagePermissions", + Handler: _Msg_MarketManagePermissions_Handler, + }, + { + MethodName: "MarketManageReqAttrs", + Handler: _Msg_MarketManageReqAttrs_Handler, + }, + { + MethodName: "CreateMarket", + Handler: _Msg_CreateMarket_Handler, + }, + { + MethodName: "GovCreateMarket", + Handler: _Msg_GovCreateMarket_Handler, + }, + { + MethodName: "GovManageFees", + Handler: _Msg_GovManageFees_Handler, + }, + { + MethodName: "GovUpdateParams", + Handler: _Msg_GovUpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "provenance/exchange/v1/tx.proto", +} + +func (m *MsgCreateAskRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateAskRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateAskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCreateAskResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateAskResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateAskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCreateBidRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateBidRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateBidRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCreateBidResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateBidResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateBidResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCancelOrderRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCancelOrderRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCancelOrderRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCancelOrderResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCancelOrderResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCancelOrderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgFillBidsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgFillBidsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgFillBidsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgFillBidsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgFillBidsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgFillBidsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgFillAsksRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgFillAsksRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgFillAsksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgFillAsksResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgFillAsksResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgFillAsksResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketSettleRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketSettleRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketSettleRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketSettleResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketSettleResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketSettleResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketWithdrawRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketWithdrawRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketWithdrawRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketWithdrawResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketWithdrawResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketWithdrawResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketUpdateDetailsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketUpdateDetailsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketUpdateDetailsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketUpdateDetailsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketUpdateDetailsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketUpdateDetailsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketUpdateEnabledRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketUpdateEnabledRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketUpdateEnabledRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketUpdateEnabledResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketUpdateEnabledResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketUpdateEnabledResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketUpdateUserSettleRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketUpdateUserSettleRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketUpdateUserSettleRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketUpdateUserSettleResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketUpdateUserSettleResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketUpdateUserSettleResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketManagePermissionsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketManagePermissionsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketManagePermissionsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketManagePermissionsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketManagePermissionsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketManagePermissionsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketManageReqAttrsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketManageReqAttrsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketManageReqAttrsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMarketManageReqAttrsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketManageReqAttrsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketManageReqAttrsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCreateMarketRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateMarketRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateMarketRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCreateMarketResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateMarketResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateMarketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgGovCreateMarketRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgGovCreateMarketRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgGovCreateMarketRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgGovCreateMarketResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgGovCreateMarketResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgGovCreateMarketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgGovManageFeesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgGovManageFeesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgGovManageFeesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgGovManageFeesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgGovManageFeesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgGovUpdateParamsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgGovUpdateParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgGovUpdateParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgGovUpdateParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgGovUpdateParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgGovUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgCreateAskRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCreateAskResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCreateBidRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCreateBidResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCancelOrderRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCancelOrderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgFillBidsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgFillBidsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgFillAsksRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgFillAsksResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketSettleRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketSettleResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketWithdrawRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketWithdrawResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketUpdateDetailsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketUpdateDetailsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketUpdateEnabledRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketUpdateEnabledResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketUpdateUserSettleRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketUpdateUserSettleResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketManagePermissionsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketManagePermissionsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketManageReqAttrsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMarketManageReqAttrsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCreateMarketRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCreateMarketResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgGovCreateMarketRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgGovCreateMarketResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgGovManageFeesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgGovManageFeesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgGovUpdateParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgGovUpdateParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgCreateAskRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateAskRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateAskRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreateAskResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateAskResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateAskResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreateBidRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateBidRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateBidRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreateBidResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateBidResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateBidResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCancelOrderRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCancelOrderRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCancelOrderRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCancelOrderResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCancelOrderResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCancelOrderResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgFillBidsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgFillBidsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgFillBidsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgFillBidsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgFillBidsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgFillBidsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgFillAsksRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgFillAsksRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgFillAsksRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgFillAsksResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgFillAsksResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgFillAsksResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketSettleRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketSettleRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketSettleRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketSettleResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketSettleResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketSettleResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketWithdrawRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketWithdrawRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketWithdrawRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketWithdrawResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketWithdrawResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketWithdrawResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketUpdateDetailsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketUpdateDetailsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketUpdateDetailsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketUpdateDetailsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketUpdateDetailsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketUpdateDetailsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketUpdateEnabledRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketUpdateEnabledRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketUpdateEnabledRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketUpdateEnabledResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketUpdateEnabledResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketUpdateEnabledResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketUpdateUserSettleRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketUpdateUserSettleRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketUpdateUserSettleRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketUpdateUserSettleResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketUpdateUserSettleResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketUpdateUserSettleResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketManagePermissionsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketManagePermissionsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketManagePermissionsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketManagePermissionsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketManagePermissionsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketManagePermissionsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketManageReqAttrsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketManageReqAttrsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketManageReqAttrsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketManageReqAttrsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketManageReqAttrsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketManageReqAttrsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreateMarketRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateMarketRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateMarketRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreateMarketResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateMarketResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateMarketResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgGovCreateMarketRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgGovCreateMarketRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgGovCreateMarketRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgGovCreateMarketResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgGovCreateMarketResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgGovCreateMarketResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgGovManageFeesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgGovManageFeesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgGovManageFeesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgGovManageFeesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgGovManageFeesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgGovUpdateParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgGovUpdateParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgGovUpdateParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgGovUpdateParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgGovUpdateParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgGovUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) From d508c9724c0a09160b2ff9124d089900d53f9ed7 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 23 Aug 2023 13:37:20 -0600 Subject: [PATCH 005/309] [1658]: Remove the EventOrderPartiallyFilled2 message and change EventOrderPartiallyFilled to use strings. When coins are used, the field value ends up being long and annoying and hard to read because it's a js representation of coins all split out and escaped to be in the quoted field. It's should be just as usable as a coins string, though, and take up much less space. --- docs/proto-docs.md | 24 +- proto/provenance/exchange/v1/events.proto | 26 +- x/exchange/events.pb.go | 449 ++++------------------ 3 files changed, 80 insertions(+), 419 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index e473d8fb33..048f9efdfc 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -67,7 +67,6 @@ - [EventOrderCreated](#provenance.exchange.v1.EventOrderCreated) - [EventOrderFilled](#provenance.exchange.v1.EventOrderFilled) - [EventOrderPartiallyFilled](#provenance.exchange.v1.EventOrderPartiallyFilled) - - [EventOrderPartiallyFilled2](#provenance.exchange.v1.EventOrderPartiallyFilled2) - [EventParamsUpdated](#provenance.exchange.v1.EventParamsUpdated) - [provenance/exchange/v1/market.proto](#provenance/exchange/v1/market.proto) @@ -1439,7 +1438,7 @@ EventMarketWithdraw is an event emitted when a withdrawal of a market's collecte | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | -| `amount_withdrawn` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | amount_withdrawn is the amount of funds withdrawn from the market account. | +| `amount_withdrawn` | [string](#string) | | amount_withdrawn is the coins amount string of funds withdrawn from the market account. | | `destination` | [string](#string) | | destination is the account that received the funds. | | `withdrawn_by` | [string](#string) | | withdrawn_by is the account that requested the withdrawal. | @@ -1505,25 +1504,8 @@ EventOrderPartiallyFilled is an event emitted when an order filled in part and s | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order created. | -| `assets_filled` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | amount_filled is the amount of assets that were filled (and removed from the order). | -| `fees_filled` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | fees_filled is the amount of fees removed from the order. | - - - - - - - - -### EventOrderPartiallyFilled2 -EventOrderPartiallyFilled2 is an event emitted when an order filled in part and still has more left to fill. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order created. | -| `assets_filled` | [string](#string) | | amount_filled is the amount of assets that were filled (and removed from the order). | -| `fees_filled` | [string](#string) | | fees_filled is the amount of settlement fees removed from the order. | +| `assets_filled` | [string](#string) | | amount_filled is the coins amount string of assets that were filled (and removed from the order). | +| `fees_filled` | [string](#string) | | fees_filled is the coins amount string of fees removed from the order. | diff --git a/proto/provenance/exchange/v1/events.proto b/proto/provenance/exchange/v1/events.proto index 6a9e08b613..f83b7198e5 100644 --- a/proto/provenance/exchange/v1/events.proto +++ b/proto/provenance/exchange/v1/events.proto @@ -6,12 +6,7 @@ option go_package = "github.com/provenance-io/provenance/x/exchange"; option java_package = "io.provenance.exchange.v1"; option java_multiple_files = true; -import "cosmos/base/v1beta1/coin.proto"; import "cosmos_proto/cosmos.proto"; -import "gogoproto/gogo.proto"; - -// TODO[1658]: Examine the difference between EventOrderPartiallyFilled and EventOrderPartiallyFilled2 to see if it's -// okay to have a Coins field instead of a string. Then remove one of them. // EventOrderCreated is an event emitted when an order is created. message EventOrderCreated { @@ -40,21 +35,9 @@ message EventOrderFilled { message EventOrderPartiallyFilled { // order_id is the numerical identifier of the order created. uint64 order_id = 1; - // amount_filled is the amount of assets that were filled (and removed from the order). - repeated cosmos.base.v1beta1.Coin assets_filled = 2 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; - // fees_filled is the amount of fees removed from the order. - repeated cosmos.base.v1beta1.Coin fees_filled = 3 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; -} - -// EventOrderPartiallyFilled2 is an event emitted when an order filled in part and still has more left to fill. -message EventOrderPartiallyFilled2 { - // order_id is the numerical identifier of the order created. - uint64 order_id = 1; - // amount_filled is the amount of assets that were filled (and removed from the order). + // amount_filled is the coins amount string of assets that were filled (and removed from the order). string assets_filled = 2; - // fees_filled is the amount of settlement fees removed from the order. + // fees_filled is the coins amount string of fees removed from the order. string fees_filled = 3; } @@ -62,9 +45,8 @@ message EventOrderPartiallyFilled2 { message EventMarketWithdraw { // market_id is the numerical identifier of the market. uint32 market_id = 1; - // amount_withdrawn is the amount of funds withdrawn from the market account. - repeated cosmos.base.v1beta1.Coin amount_withdrawn = 2 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + // amount_withdrawn is the coins amount string of funds withdrawn from the market account. + string amount_withdrawn = 2; // destination is the account that received the funds. string destination = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // withdrawn_by is the account that requested the withdrawal. diff --git a/x/exchange/events.pb.go b/x/exchange/events.pb.go index 6e289f2427..c9886e6a91 100644 --- a/x/exchange/events.pb.go +++ b/x/exchange/events.pb.go @@ -6,9 +6,6 @@ package exchange import ( fmt "fmt" _ "github.com/cosmos/cosmos-proto" - github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" - types "github.com/cosmos/cosmos-sdk/types" - _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" math "math" @@ -187,10 +184,10 @@ func (m *EventOrderFilled) GetOrderId() uint64 { type EventOrderPartiallyFilled struct { // order_id is the numerical identifier of the order created. OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` - // amount_filled is the amount of assets that were filled (and removed from the order). - AssetsFilled github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=assets_filled,json=assetsFilled,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"assets_filled"` - // fees_filled is the amount of fees removed from the order. - FeesFilled github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=fees_filled,json=feesFilled,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"fees_filled"` + // amount_filled is the coins amount string of assets that were filled (and removed from the order). + AssetsFilled string `protobuf:"bytes,2,opt,name=assets_filled,json=assetsFilled,proto3" json:"assets_filled,omitempty"` + // fees_filled is the coins amount string of fees removed from the order. + FeesFilled string `protobuf:"bytes,3,opt,name=fees_filled,json=feesFilled,proto3" json:"fees_filled,omitempty"` } func (m *EventOrderPartiallyFilled) Reset() { *m = EventOrderPartiallyFilled{} } @@ -233,78 +230,14 @@ func (m *EventOrderPartiallyFilled) GetOrderId() uint64 { return 0 } -func (m *EventOrderPartiallyFilled) GetAssetsFilled() github_com_cosmos_cosmos_sdk_types.Coins { - if m != nil { - return m.AssetsFilled - } - return nil -} - -func (m *EventOrderPartiallyFilled) GetFeesFilled() github_com_cosmos_cosmos_sdk_types.Coins { - if m != nil { - return m.FeesFilled - } - return nil -} - -// EventOrderPartiallyFilled2 is an event emitted when an order filled in part and still has more left to fill. -type EventOrderPartiallyFilled2 struct { - // order_id is the numerical identifier of the order created. - OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` - // amount_filled is the amount of assets that were filled (and removed from the order). - AssetsFilled string `protobuf:"bytes,2,opt,name=assets_filled,json=assetsFilled,proto3" json:"assets_filled,omitempty"` - // fees_filled is the amount of settlement fees removed from the order. - FeesFilled string `protobuf:"bytes,3,opt,name=fees_filled,json=feesFilled,proto3" json:"fees_filled,omitempty"` -} - -func (m *EventOrderPartiallyFilled2) Reset() { *m = EventOrderPartiallyFilled2{} } -func (m *EventOrderPartiallyFilled2) String() string { return proto.CompactTextString(m) } -func (*EventOrderPartiallyFilled2) ProtoMessage() {} -func (*EventOrderPartiallyFilled2) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{4} -} -func (m *EventOrderPartiallyFilled2) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *EventOrderPartiallyFilled2) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_EventOrderPartiallyFilled2.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *EventOrderPartiallyFilled2) XXX_Merge(src proto.Message) { - xxx_messageInfo_EventOrderPartiallyFilled2.Merge(m, src) -} -func (m *EventOrderPartiallyFilled2) XXX_Size() int { - return m.Size() -} -func (m *EventOrderPartiallyFilled2) XXX_DiscardUnknown() { - xxx_messageInfo_EventOrderPartiallyFilled2.DiscardUnknown(m) -} - -var xxx_messageInfo_EventOrderPartiallyFilled2 proto.InternalMessageInfo - -func (m *EventOrderPartiallyFilled2) GetOrderId() uint64 { - if m != nil { - return m.OrderId - } - return 0 -} - -func (m *EventOrderPartiallyFilled2) GetAssetsFilled() string { +func (m *EventOrderPartiallyFilled) GetAssetsFilled() string { if m != nil { return m.AssetsFilled } return "" } -func (m *EventOrderPartiallyFilled2) GetFeesFilled() string { +func (m *EventOrderPartiallyFilled) GetFeesFilled() string { if m != nil { return m.FeesFilled } @@ -315,8 +248,8 @@ func (m *EventOrderPartiallyFilled2) GetFeesFilled() string { type EventMarketWithdraw struct { // market_id is the numerical identifier of the market. MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` - // amount_withdrawn is the amount of funds withdrawn from the market account. - AmountWithdrawn github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=amount_withdrawn,json=amountWithdrawn,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount_withdrawn"` + // amount_withdrawn is the coins amount string of funds withdrawn from the market account. + AmountWithdrawn string `protobuf:"bytes,2,opt,name=amount_withdrawn,json=amountWithdrawn,proto3" json:"amount_withdrawn,omitempty"` // destination is the account that received the funds. Destination string `protobuf:"bytes,3,opt,name=destination,proto3" json:"destination,omitempty"` // withdrawn_by is the account that requested the withdrawal. @@ -327,7 +260,7 @@ func (m *EventMarketWithdraw) Reset() { *m = EventMarketWithdraw{} } func (m *EventMarketWithdraw) String() string { return proto.CompactTextString(m) } func (*EventMarketWithdraw) ProtoMessage() {} func (*EventMarketWithdraw) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{5} + return fileDescriptor_c1b69385a348cffa, []int{4} } func (m *EventMarketWithdraw) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -363,11 +296,11 @@ func (m *EventMarketWithdraw) GetMarketId() uint32 { return 0 } -func (m *EventMarketWithdraw) GetAmountWithdrawn() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *EventMarketWithdraw) GetAmountWithdrawn() string { if m != nil { return m.AmountWithdrawn } - return nil + return "" } func (m *EventMarketWithdraw) GetDestination() string { @@ -396,7 +329,7 @@ func (m *EventMarketDetailsUpdated) Reset() { *m = EventMarketDetailsUpd func (m *EventMarketDetailsUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketDetailsUpdated) ProtoMessage() {} func (*EventMarketDetailsUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{6} + return fileDescriptor_c1b69385a348cffa, []int{5} } func (m *EventMarketDetailsUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -451,7 +384,7 @@ func (m *EventMarketEnabled) Reset() { *m = EventMarketEnabled{} } func (m *EventMarketEnabled) String() string { return proto.CompactTextString(m) } func (*EventMarketEnabled) ProtoMessage() {} func (*EventMarketEnabled) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{7} + return fileDescriptor_c1b69385a348cffa, []int{6} } func (m *EventMarketEnabled) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -506,7 +439,7 @@ func (m *EventMarketDisabled) Reset() { *m = EventMarketDisabled{} } func (m *EventMarketDisabled) String() string { return proto.CompactTextString(m) } func (*EventMarketDisabled) ProtoMessage() {} func (*EventMarketDisabled) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{8} + return fileDescriptor_c1b69385a348cffa, []int{7} } func (m *EventMarketDisabled) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -561,7 +494,7 @@ func (m *EventMarketUserSettleUpdated) Reset() { *m = EventMarketUserSet func (m *EventMarketUserSettleUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketUserSettleUpdated) ProtoMessage() {} func (*EventMarketUserSettleUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{9} + return fileDescriptor_c1b69385a348cffa, []int{8} } func (m *EventMarketUserSettleUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -616,7 +549,7 @@ func (m *EventMarketPermissionsUpdated) Reset() { *m = EventMarketPermis func (m *EventMarketPermissionsUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketPermissionsUpdated) ProtoMessage() {} func (*EventMarketPermissionsUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{10} + return fileDescriptor_c1b69385a348cffa, []int{9} } func (m *EventMarketPermissionsUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -671,7 +604,7 @@ func (m *EventMarketReqAttrUpdated) Reset() { *m = EventMarketReqAttrUpd func (m *EventMarketReqAttrUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketReqAttrUpdated) ProtoMessage() {} func (*EventMarketReqAttrUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{11} + return fileDescriptor_c1b69385a348cffa, []int{10} } func (m *EventMarketReqAttrUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -729,7 +662,7 @@ func (m *EventCreateMarketSubmitted) Reset() { *m = EventCreateMarketSub func (m *EventCreateMarketSubmitted) String() string { return proto.CompactTextString(m) } func (*EventCreateMarketSubmitted) ProtoMessage() {} func (*EventCreateMarketSubmitted) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{12} + return fileDescriptor_c1b69385a348cffa, []int{11} } func (m *EventCreateMarketSubmitted) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -789,7 +722,7 @@ func (m *EventMarketCreated) Reset() { *m = EventMarketCreated{} } func (m *EventMarketCreated) String() string { return proto.CompactTextString(m) } func (*EventMarketCreated) ProtoMessage() {} func (*EventMarketCreated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{13} + return fileDescriptor_c1b69385a348cffa, []int{12} } func (m *EventMarketCreated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -835,7 +768,7 @@ func (m *EventMarketFeesUpdated) Reset() { *m = EventMarketFeesUpdated{} func (m *EventMarketFeesUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketFeesUpdated) ProtoMessage() {} func (*EventMarketFeesUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{14} + return fileDescriptor_c1b69385a348cffa, []int{13} } func (m *EventMarketFeesUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -879,7 +812,7 @@ func (m *EventParamsUpdated) Reset() { *m = EventParamsUpdated{} } func (m *EventParamsUpdated) String() string { return proto.CompactTextString(m) } func (*EventParamsUpdated) ProtoMessage() {} func (*EventParamsUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{15} + return fileDescriptor_c1b69385a348cffa, []int{14} } func (m *EventParamsUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -913,7 +846,6 @@ func init() { proto.RegisterType((*EventOrderCancelled)(nil), "provenance.exchange.v1.EventOrderCancelled") proto.RegisterType((*EventOrderFilled)(nil), "provenance.exchange.v1.EventOrderFilled") proto.RegisterType((*EventOrderPartiallyFilled)(nil), "provenance.exchange.v1.EventOrderPartiallyFilled") - proto.RegisterType((*EventOrderPartiallyFilled2)(nil), "provenance.exchange.v1.EventOrderPartiallyFilled2") proto.RegisterType((*EventMarketWithdraw)(nil), "provenance.exchange.v1.EventMarketWithdraw") proto.RegisterType((*EventMarketDetailsUpdated)(nil), "provenance.exchange.v1.EventMarketDetailsUpdated") proto.RegisterType((*EventMarketEnabled)(nil), "provenance.exchange.v1.EventMarketEnabled") @@ -932,51 +864,44 @@ func init() { } var fileDescriptor_c1b69385a348cffa = []byte{ - // 691 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4d, 0x4f, 0xdb, 0x4c, - 0x10, 0x8e, 0x03, 0x7a, 0x5f, 0xb2, 0x01, 0xbd, 0xbc, 0x2e, 0x42, 0x09, 0x2d, 0x09, 0x32, 0x97, - 0x5c, 0x62, 0x37, 0x54, 0x55, 0xa5, 0xf6, 0x44, 0xf8, 0x90, 0x38, 0xa0, 0x46, 0xa1, 0xa8, 0x52, - 0x2f, 0xd1, 0x3a, 0x1e, 0xc2, 0x16, 0x7b, 0xd7, 0xec, 0x6e, 0x02, 0x3e, 0xf4, 0x3f, 0xf4, 0xd4, - 0xfe, 0x80, 0xde, 0x7a, 0xee, 0x8f, 0xe0, 0x88, 0x7a, 0xea, 0xa9, 0xad, 0xe0, 0x8f, 0x54, 0xeb, - 0xb5, 0x13, 0xa7, 0x1f, 0x09, 0x87, 0xe6, 0x94, 0xec, 0xec, 0x33, 0xf3, 0xcc, 0xce, 0x3c, 0xbb, - 0x63, 0xb4, 0x19, 0x72, 0x36, 0x00, 0x8a, 0x69, 0x17, 0x1c, 0xb8, 0xec, 0x9e, 0x62, 0xda, 0x03, - 0x67, 0xd0, 0x70, 0x60, 0x00, 0x54, 0x0a, 0x3b, 0xe4, 0x4c, 0x32, 0x73, 0x75, 0x04, 0xb2, 0x53, - 0x90, 0x3d, 0x68, 0xac, 0x55, 0xba, 0x4c, 0x04, 0x4c, 0x38, 0x2e, 0x16, 0xca, 0xc9, 0x05, 0x89, - 0x1b, 0x4e, 0x97, 0x11, 0xaa, 0xfd, 0xd6, 0xca, 0x7a, 0xbf, 0x13, 0xaf, 0x1c, 0xbd, 0x48, 0xb6, - 0x56, 0x7a, 0xac, 0xc7, 0xb4, 0x5d, 0xfd, 0xd3, 0x56, 0xeb, 0x10, 0xfd, 0xbf, 0xa7, 0x88, 0x9f, - 0x73, 0x0f, 0xf8, 0x0e, 0x07, 0x2c, 0xc1, 0x33, 0xcb, 0x68, 0x81, 0xa9, 0x75, 0x87, 0x78, 0x25, - 0x63, 0xc3, 0xa8, 0xcd, 0xb7, 0xff, 0x8d, 0xd7, 0x07, 0x9e, 0xb9, 0x8e, 0x90, 0xde, 0x92, 0x51, - 0x08, 0xa5, 0xfc, 0x86, 0x51, 0x2b, 0xb4, 0x0b, 0xb1, 0xe5, 0x45, 0x14, 0x82, 0x15, 0xa0, 0x7b, - 0x99, 0x70, 0x2a, 0x7d, 0xdf, 0x9f, 0x1c, 0xf0, 0x19, 0x5a, 0xec, 0xa6, 0xb8, 0x8e, 0x1b, 0xe9, - 0x90, 0xcd, 0xd2, 0xe7, 0x4f, 0xf5, 0x95, 0x24, 0xfd, 0x6d, 0xcf, 0xe3, 0x20, 0xc4, 0x91, 0xe4, - 0x84, 0xf6, 0xda, 0xc5, 0x21, 0xba, 0x19, 0x59, 0x75, 0xb4, 0x3c, 0xa2, 0xdb, 0x27, 0x53, 0xb8, - 0xac, 0x77, 0x79, 0x54, 0x1e, 0xe1, 0x5b, 0x98, 0x4b, 0x82, 0x7d, 0x3f, 0x9a, 0xea, 0x68, 0x86, - 0x68, 0x09, 0x0b, 0x01, 0x52, 0x74, 0x4e, 0x62, 0x6c, 0x29, 0xbf, 0x31, 0x57, 0x2b, 0x6e, 0x95, - 0xed, 0x24, 0x45, 0xd5, 0x0e, 0x3b, 0x69, 0x87, 0xbd, 0xc3, 0x08, 0x6d, 0x3e, 0xbc, 0xfa, 0x5a, - 0xcd, 0x7d, 0xfc, 0x56, 0xad, 0xf5, 0x88, 0x3c, 0xed, 0xbb, 0x76, 0x97, 0x05, 0x49, 0x3b, 0x92, - 0x9f, 0xba, 0xf0, 0xce, 0x1c, 0x55, 0x44, 0x11, 0x3b, 0x88, 0xf6, 0xa2, 0x66, 0x48, 0x92, 0xf1, - 0x51, 0xf1, 0x04, 0x60, 0xc8, 0x37, 0xf7, 0xf7, 0xf9, 0x90, 0x8a, 0xaf, 0xd9, 0xac, 0x37, 0x68, - 0xed, 0x8f, 0x75, 0xd9, 0x9a, 0x54, 0x98, 0xcd, 0x5f, 0x0b, 0xa3, 0x14, 0x31, 0x7e, 0x96, 0xea, - 0xcf, 0x67, 0x51, 0x90, 0x2c, 0xfd, 0x87, 0x7c, 0x22, 0x9b, 0x43, 0xcc, 0xcf, 0x40, 0xbe, 0x24, - 0xf2, 0xd4, 0xe3, 0xf8, 0xc2, 0xbc, 0x8f, 0x0a, 0x41, 0x6c, 0x49, 0x99, 0x97, 0xda, 0x0b, 0xda, - 0x70, 0xe0, 0x99, 0x03, 0xb4, 0x8c, 0x03, 0xd6, 0xa7, 0xb2, 0x73, 0x91, 0xe0, 0xe9, 0x2c, 0xda, - 0xf2, 0x9f, 0x26, 0x49, 0x73, 0xa2, 0xe6, 0x53, 0x54, 0xf4, 0x40, 0x48, 0x42, 0xb1, 0x24, 0x8c, - 0xea, 0xd3, 0x4c, 0xd2, 0x6b, 0x06, 0xac, 0xc4, 0x3e, 0x4c, 0x56, 0x89, 0x7d, 0x7e, 0x9a, 0xf3, - 0x10, 0xdd, 0x8c, 0xac, 0xf3, 0x44, 0xbc, 0xba, 0x48, 0xbb, 0x20, 0x31, 0xf1, 0xc5, 0x71, 0xe8, - 0xc5, 0x57, 0x76, 0x62, 0xa9, 0x9e, 0x20, 0xd4, 0xd7, 0xb8, 0xbb, 0xdc, 0xb0, 0x42, 0x82, 0x6d, - 0x46, 0xd6, 0x6b, 0x64, 0x66, 0x28, 0xf7, 0x28, 0x76, 0xfd, 0x99, 0x71, 0x9d, 0x8d, 0x69, 0x60, - 0x97, 0x88, 0x59, 0x92, 0x49, 0xf4, 0x20, 0x43, 0x76, 0x2c, 0x80, 0x1f, 0x81, 0x94, 0x3e, 0xcc, - 0xb6, 0x9c, 0x7d, 0xb4, 0x9e, 0x61, 0x6d, 0x01, 0x0f, 0x88, 0x10, 0x84, 0xd1, 0x19, 0x77, 0x71, - 0x5c, 0x38, 0x6d, 0x38, 0xdf, 0x96, 0x92, 0xcf, 0x96, 0xf2, 0xbd, 0x91, 0xbc, 0x28, 0x7a, 0xa4, - 0x68, 0xe6, 0xa3, 0xbe, 0x1b, 0x10, 0x39, 0x95, 0xb4, 0x8a, 0x8a, 0x21, 0x67, 0x21, 0x13, 0xd8, - 0x57, 0xdb, 0xf9, 0xf8, 0xc5, 0x41, 0xa9, 0x49, 0x8f, 0x0c, 0x91, 0x86, 0x52, 0x79, 0x4d, 0xbd, - 0x82, 0x43, 0x74, 0x33, 0xb2, 0x1a, 0x63, 0x92, 0x4e, 0x27, 0xde, 0xa4, 0x84, 0xac, 0xc7, 0x68, - 0x35, 0xe3, 0xb2, 0x0f, 0x70, 0xa7, 0x7e, 0x59, 0x2b, 0x09, 0x53, 0x0b, 0x73, 0x1c, 0xa4, 0x2e, - 0x4d, 0xb8, 0xba, 0xa9, 0x18, 0xd7, 0x37, 0x15, 0xe3, 0xfb, 0x4d, 0xc5, 0x78, 0x7b, 0x5b, 0xc9, - 0x5d, 0xdf, 0x56, 0x72, 0x5f, 0x6e, 0x2b, 0x39, 0x54, 0x26, 0xf1, 0x50, 0xfe, 0xcd, 0xd8, 0x6f, - 0x19, 0xaf, 0xec, 0xcc, 0x83, 0x35, 0x02, 0xd5, 0x09, 0xcb, 0xac, 0x9c, 0xcb, 0xe1, 0x07, 0x85, - 0xfb, 0x4f, 0x3c, 0xde, 0x1f, 0xfd, 0x08, 0x00, 0x00, 0xff, 0xff, 0x66, 0x79, 0xe5, 0xe7, 0x6e, - 0x08, 0x00, 0x00, + // 586 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xc1, 0x4e, 0xdb, 0x4c, + 0x10, 0xc7, 0x31, 0x1f, 0xfa, 0x4a, 0x26, 0xa0, 0x52, 0x17, 0x21, 0x42, 0x8b, 0x41, 0xe6, 0x42, + 0x0f, 0xd8, 0x8a, 0xaa, 0xaa, 0x52, 0x7b, 0xc2, 0x05, 0x24, 0x0e, 0xa8, 0x51, 0x52, 0x54, 0xa9, + 0x97, 0x68, 0x13, 0x0f, 0xc9, 0x16, 0x7b, 0xd7, 0xec, 0x6e, 0x02, 0x96, 0xfa, 0x10, 0xbd, 0xf5, + 0x45, 0xfa, 0x10, 0x3d, 0xa2, 0x1e, 0xaa, 0x1e, 0xab, 0xe4, 0x45, 0x2a, 0x7b, 0x6d, 0xc7, 0x91, + 0xaa, 0x84, 0x4b, 0x8e, 0x33, 0xfe, 0xcd, 0xfc, 0x67, 0x67, 0xd6, 0xb3, 0x70, 0x10, 0x09, 0x3e, + 0x44, 0x46, 0x58, 0x17, 0x5d, 0xbc, 0xeb, 0xf6, 0x09, 0xeb, 0xa1, 0x3b, 0xac, 0xbb, 0x38, 0x44, + 0xa6, 0xa4, 0x13, 0x09, 0xae, 0xb8, 0xb9, 0x35, 0x81, 0x9c, 0x1c, 0x72, 0x86, 0xf5, 0x9d, 0x5a, + 0x97, 0xcb, 0x90, 0xcb, 0x76, 0x4a, 0xb9, 0xda, 0xd0, 0x21, 0xf6, 0x05, 0x3c, 0x39, 0x4d, 0x52, + 0xbc, 0x17, 0x3e, 0x8a, 0x77, 0x02, 0x89, 0x42, 0xdf, 0xac, 0xc1, 0x2a, 0x4f, 0xec, 0x36, 0xf5, + 0xb7, 0x8d, 0x7d, 0xe3, 0x70, 0xa5, 0xf9, 0x28, 0xb5, 0xcf, 0x7d, 0x73, 0x17, 0x40, 0x7f, 0x52, + 0x71, 0x84, 0xdb, 0xcb, 0xfb, 0xc6, 0x61, 0xa5, 0x59, 0x49, 0x3d, 0x1f, 0xe2, 0x08, 0xed, 0x10, + 0x9e, 0x96, 0xd2, 0x25, 0x85, 0x04, 0xc1, 0xec, 0x84, 0x6f, 0x61, 0xad, 0x9b, 0x73, 0xed, 0x4e, + 0xac, 0x53, 0x7a, 0xdb, 0x3f, 0xbf, 0x1f, 0x6d, 0x66, 0x85, 0x1e, 0xfb, 0xbe, 0x40, 0x29, 0x5b, + 0x4a, 0x50, 0xd6, 0x6b, 0x56, 0x0b, 0xda, 0x8b, 0xed, 0x23, 0xd8, 0x98, 0xc8, 0x9d, 0xd1, 0x39, + 0x5a, 0xf6, 0x17, 0xa8, 0x4d, 0xf0, 0x06, 0x11, 0x8a, 0x92, 0x20, 0x88, 0xe7, 0xc6, 0x99, 0x07, + 0xb0, 0x4e, 0xa4, 0x44, 0x25, 0xdb, 0x57, 0x29, 0x9b, 0x9d, 0x7b, 0x4d, 0x3b, 0xb3, 0xf8, 0x3d, + 0xa8, 0x5e, 0x21, 0x16, 0xc8, 0x7f, 0x29, 0x02, 0x89, 0x4b, 0x03, 0xf6, 0x2f, 0x23, 0x6b, 0xce, + 0x05, 0x11, 0xd7, 0xa8, 0x3e, 0x52, 0xd5, 0xf7, 0x05, 0xb9, 0x35, 0x9f, 0x41, 0x25, 0x4c, 0x3d, + 0xb9, 0xf2, 0x7a, 0x73, 0x55, 0x3b, 0xce, 0x7d, 0xf3, 0x05, 0x6c, 0x90, 0x90, 0x0f, 0x98, 0x6a, + 0xdf, 0x66, 0x3c, 0xcb, 0xd4, 0x1f, 0x6b, 0x7f, 0x9e, 0x86, 0x99, 0x6f, 0xa0, 0xea, 0xa3, 0x54, + 0x94, 0x11, 0x45, 0x39, 0xd3, 0x05, 0xcc, 0x6a, 0x64, 0x09, 0x4e, 0xa6, 0x50, 0xe4, 0x4f, 0xa6, + 0xb0, 0x32, 0x2f, 0xb8, 0xa0, 0xbd, 0xd8, 0xbe, 0xc9, 0xda, 0xaa, 0xcf, 0x75, 0x82, 0x8a, 0xd0, + 0x40, 0x5e, 0x46, 0x7e, 0x7a, 0x97, 0x66, 0x9e, 0xee, 0x35, 0xc0, 0x40, 0x73, 0x0f, 0x19, 0x7d, + 0x25, 0x63, 0xbd, 0xd8, 0xfe, 0x0c, 0x66, 0x49, 0xf2, 0x94, 0x91, 0x4e, 0xb0, 0x30, 0xad, 0xeb, + 0xa9, 0xb1, 0x9d, 0x50, 0xb9, 0x48, 0x31, 0x05, 0xcf, 0x4b, 0x62, 0x97, 0x12, 0x45, 0x0b, 0x95, + 0x0a, 0x70, 0xb1, 0xed, 0x1c, 0xc0, 0x6e, 0x49, 0xb5, 0x81, 0x22, 0xa4, 0x52, 0x52, 0xce, 0x16, + 0x3c, 0xc5, 0xe9, 0x8b, 0xd3, 0xc4, 0x9b, 0x63, 0xa5, 0xc4, 0x62, 0x25, 0xbf, 0x19, 0xb0, 0x93, + 0x6a, 0xea, 0x5d, 0xa7, 0x95, 0x5b, 0x83, 0x4e, 0x48, 0xd5, 0x5c, 0xd1, 0x3d, 0xa8, 0x46, 0x82, + 0x47, 0x5c, 0x92, 0x20, 0xf9, 0xbc, 0x9c, 0x2e, 0x09, 0xc8, 0x5d, 0x7a, 0x97, 0xc9, 0x3c, 0x55, + 0x52, 0xd7, 0xdc, 0x5f, 0xb0, 0xa0, 0xbd, 0xd8, 0xae, 0x4f, 0x5d, 0xe9, 0x7c, 0x15, 0xcf, 0x2a, + 0xc8, 0x7e, 0x05, 0x5b, 0xa5, 0x90, 0x33, 0xc4, 0x07, 0xcd, 0xcb, 0xde, 0xcc, 0x94, 0x1a, 0x44, + 0x90, 0x30, 0x0f, 0xf1, 0xf0, 0xc7, 0xc8, 0x32, 0xee, 0x47, 0x96, 0xf1, 0x67, 0x64, 0x19, 0x5f, + 0xc7, 0xd6, 0xd2, 0xfd, 0xd8, 0x5a, 0xfa, 0x3d, 0xb6, 0x96, 0xa0, 0x46, 0xb9, 0xf3, 0xef, 0x97, + 0xa5, 0x61, 0x7c, 0x72, 0x7a, 0x54, 0xf5, 0x07, 0x1d, 0xa7, 0xcb, 0x43, 0x77, 0x02, 0x1d, 0x51, + 0x5e, 0xb2, 0xdc, 0xbb, 0xe2, 0xcd, 0xea, 0xfc, 0x9f, 0xbe, 0x3b, 0x2f, 0xff, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x33, 0xb3, 0x39, 0x5f, 0xd1, 0x06, 0x00, 0x00, } func (m *EventOrderCreated) Marshal() (dAtA []byte, err error) { @@ -1093,62 +1018,6 @@ func (m *EventOrderPartiallyFilled) MarshalTo(dAtA []byte) (int, error) { } func (m *EventOrderPartiallyFilled) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.FeesFilled) > 0 { - for iNdEx := len(m.FeesFilled) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.FeesFilled[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintEvents(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - } - if len(m.AssetsFilled) > 0 { - for iNdEx := len(m.AssetsFilled) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.AssetsFilled[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintEvents(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - } - if m.OrderId != 0 { - i = encodeVarintEvents(dAtA, i, uint64(m.OrderId)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *EventOrderPartiallyFilled2) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *EventOrderPartiallyFilled2) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *EventOrderPartiallyFilled2) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -1210,18 +1079,11 @@ func (m *EventMarketWithdraw) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x1a } if len(m.AmountWithdrawn) > 0 { - for iNdEx := len(m.AmountWithdrawn) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.AmountWithdrawn[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintEvents(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } + i -= len(m.AmountWithdrawn) + copy(dAtA[i:], m.AmountWithdrawn) + i = encodeVarintEvents(dAtA, i, uint64(len(m.AmountWithdrawn))) + i-- + dAtA[i] = 0x12 } if m.MarketId != 0 { i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) @@ -1616,30 +1478,6 @@ func (m *EventOrderFilled) Size() (n int) { } func (m *EventOrderPartiallyFilled) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.OrderId != 0 { - n += 1 + sovEvents(uint64(m.OrderId)) - } - if len(m.AssetsFilled) > 0 { - for _, e := range m.AssetsFilled { - l = e.Size() - n += 1 + l + sovEvents(uint64(l)) - } - } - if len(m.FeesFilled) > 0 { - for _, e := range m.FeesFilled { - l = e.Size() - n += 1 + l + sovEvents(uint64(l)) - } - } - return n -} - -func (m *EventOrderPartiallyFilled2) Size() (n int) { if m == nil { return 0 } @@ -1668,11 +1506,9 @@ func (m *EventMarketWithdraw) Size() (n int) { if m.MarketId != 0 { n += 1 + sovEvents(uint64(m.MarketId)) } - if len(m.AmountWithdrawn) > 0 { - for _, e := range m.AmountWithdrawn { - l = e.Size() - n += 1 + l + sovEvents(uint64(l)) - } + l = len(m.AmountWithdrawn) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) } l = len(m.Destination) if l > 0 { @@ -2139,143 +1975,6 @@ func (m *EventOrderPartiallyFilled) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: EventOrderPartiallyFilled: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) - } - m.OrderId = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvents - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.OrderId |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AssetsFilled", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvents - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthEvents - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthEvents - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.AssetsFilled = append(m.AssetsFilled, types.Coin{}) - if err := m.AssetsFilled[len(m.AssetsFilled)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FeesFilled", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvents - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthEvents - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthEvents - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.FeesFilled = append(m.FeesFilled, types.Coin{}) - if err := m.FeesFilled[len(m.FeesFilled)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipEvents(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthEvents - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *EventOrderPartiallyFilled2) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvents - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: EventOrderPartiallyFilled2: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: EventOrderPartiallyFilled2: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) @@ -2432,7 +2131,7 @@ func (m *EventMarketWithdraw) Unmarshal(dAtA []byte) error { if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AmountWithdrawn", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEvents @@ -2442,25 +2141,23 @@ func (m *EventMarketWithdraw) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthEvents } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthEvents } if postIndex > l { return io.ErrUnexpectedEOF } - m.AmountWithdrawn = append(m.AmountWithdrawn, types.Coin{}) - if err := m.AmountWithdrawn[len(m.AmountWithdrawn)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.AmountWithdrawn = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { From a978c99e220e4fb179d7af796d0131ae92d1a92c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 23 Aug 2023 13:53:58 -0600 Subject: [PATCH 006/309] [1658]: Create the skeleton module and keeper. --- x/exchange/keeper/keeper.go | 21 +++++ x/exchange/keys.go | 9 ++ x/exchange/module/module.go | 171 ++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 x/exchange/keeper/keeper.go create mode 100644 x/exchange/keys.go create mode 100644 x/exchange/module/module.go diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go new file mode 100644 index 0000000000..99df2e41be --- /dev/null +++ b/x/exchange/keeper/keeper.go @@ -0,0 +1,21 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" +) + +type Keeper struct { + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + // TODO[1658]: Finish the Keeper struct. +} + +func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey) Keeper { + // TODO[1658]: Finish NewKeeper. + rv := Keeper{ + cdc: cdc, + storeKey: storeKey, + } + return rv +} diff --git a/x/exchange/keys.go b/x/exchange/keys.go new file mode 100644 index 0000000000..4c2afb7bb4 --- /dev/null +++ b/x/exchange/keys.go @@ -0,0 +1,9 @@ +package exchange + +const ( + // ModuleName is the name of the exchange module. + ModuleName = "exchange" + + // StoreKey is the store key string for the exchange module. + StoreKey = ModuleName +) diff --git a/x/exchange/module/module.go b/x/exchange/module/module.go new file mode 100644 index 0000000000..88b4bbab0d --- /dev/null +++ b/x/exchange/module/module.go @@ -0,0 +1,171 @@ +package module + +import ( + "context" + "encoding/json" + "fmt" + "math/rand" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + sdkclient "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + + "github.com/provenance-io/provenance/x/exchange" + "github.com/provenance-io/provenance/x/exchange/keeper" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} +) + +type AppModule struct { + AppModuleBasic + keeper keeper.Keeper +} + +func NewAppModule(cdc codec.Codec, exchangeKeeper keeper.Keeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{cdc: cdc}, + keeper: exchangeKeeper, + } +} + +type AppModuleBasic struct { + cdc codec.Codec +} + +func (AppModuleBasic) Name() string { + return exchange.ModuleName +} + +// DefaultGenesis returns default genesis state as raw bytes for the exchange module. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + // TODO[1658]: Create DefaultGenesisState() + panic("not implemented") + // return cdc.MustMarshalJSON(exchange.DefaultGenesisState()) +} + +// ValidateGenesis performs genesis state validation for the exchange module. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ sdkclient.TxEncodingConfig, bz json.RawMessage) error { + var data exchange.GenesisState + if err := cdc.UnmarshalJSON(bz, &data); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", exchange.ModuleName, err) + } + // TODO[1658]: Create GenesisState.Validate() + panic("not implemented") + //return data.Validate() +} + +// GetQueryCmd returns the cli query commands for the exchange module. +func (a AppModuleBasic) GetQueryCmd() *cobra.Command { + // TODO[1658]: Create cli.QueryCmd() + panic("not implemented") + //return cli.QueryCmd() +} + +// GetTxCmd returns the transaction commands for the exchange module. +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + // TODO[1658]: Create cli.TxCmd() + panic("not implemented") + //return cli.TxCmd() +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the exchange module. +func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx sdkclient.Context, mux *runtime.ServeMux) { + if err := exchange.RegisterQueryHandlerClient(context.Background(), mux, exchange.NewQueryClient(clientCtx)); err != nil { + panic(err) + } +} + +// RegisterInterfaces registers the exchange module's interface types +func (AppModuleBasic) RegisterInterfaces(_ cdctypes.InterfaceRegistry) { + // TODO[1658]: Create something for RegisterInterfaces to call. +} + +// RegisterLegacyAminoCodec registers the exchange module's types for the given codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(_ *codec.LegacyAmino) {} + +// RegisterInvariants registers the invariants for the exchange module. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} + +// Deprecated: Route returns the message routing key for the exchange module, empty. +func (am AppModule) Route() sdk.Route { return sdk.Route{} } + +// Deprecated: QuerierRoute returns the route we respond to for abci queries, "". +func (AppModule) QuerierRoute() string { return "" } + +// Deprecated: LegacyQuerierHandler returns the exchange module sdk.Querier (nil). +func (am AppModule) LegacyQuerierHandler(_ *codec.LegacyAmino) sdk.Querier { return nil } + +// InitGenesis performs genesis initialization for the exchange module. It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState exchange.GenesisState + cdc.MustUnmarshalJSON(data, &genesisState) + // TODO[1658]: Create keeper.InitGenesis + panic("not implemented") + //am.keeper.InitGenesis(ctx, &genesisState) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the exchange module. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + // TODO[1658]: Create keeper.InitGenesis + panic("not implemented") + //gs := am.keeper.ExportGenesis(ctx) + //return cdc.MustMarshalJSON(gs) +} + +// RegisterServices registers a gRPC query service to respond to the exchange-specific gRPC queries. +func (am AppModule) RegisterServices(cfg module.Configurator) { + // TODO[1658]: Uncomment this once the keeper implements the query server. + // exchange.RegisterQueryServer(cfg.QueryServer(), am.keeper) +} + +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + +// ____________________________________________________________________________ + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the exchange module. +func (am AppModule) GenerateGenesisState(simState *module.SimulationState) { + // TODO[1658]: Create simulation.RandomizedGenState(simState) + panic("not implemented") + //simulation.RandomizedGenState(simState) +} + +// ProposalContents returns all the exchange content functions used to +// simulate governance proposals, of which there are none for the exchange module. +func (am AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { + return nil +} + +// RandomizedParams returns randomized exchange param changes for the simulator, +// of which there are none since this module doesn't use the params module. +func (AppModule) RandomizedParams(_ *rand.Rand) []simtypes.ParamChange { return nil } + +// RegisterStoreDecoder registers a decoder for exchange module's types +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + // TODO[1658]: Create simulation.NewDecodeStore(am.cdc) + panic("not implemented") + // sdr[exchange.StoreKey] = simulation.NewDecodeStore(am.cdc) +} + +// WeightedOperations returns the all the exchange module operations with their respective weights, +// of which there are none. +func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { + // TODO[1658]: Create the WeightedOperations. + panic("not implemented") +} From 84c299a3cc4bb917b090e822a4d2040354ee5b92 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 23 Aug 2023 14:51:23 -0600 Subject: [PATCH 007/309] [1658]: Make the single coin fields not nullable. --- proto/provenance/exchange/v1/market.proto | 4 +- proto/provenance/exchange/v1/orders.proto | 6 +- x/exchange/market.pb.go | 194 ++++++++++------------ x/exchange/orders.pb.go | 167 ++++++++----------- 4 files changed, 168 insertions(+), 203 deletions(-) diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index ff39fc0800..54bdd6093b 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -93,9 +93,9 @@ message Market { // For an order to be valid, its price must be evenly divisible by a FeeRatio's price. message FeeRatio { // price is the unit the order price is divided by to get how much of the fee should apply. - cosmos.base.v1beta1.Coin price = 1; + cosmos.base.v1beta1.Coin price = 1 [(gogoproto.nullable) = false]; // fee is the amount to charge per price unit. - cosmos.base.v1beta1.Coin fee = 2; + cosmos.base.v1beta1.Coin fee = 2 [(gogoproto.nullable) = false]; } // AddrPermissions associates an address with a list of permissions available for that address. diff --git a/proto/provenance/exchange/v1/orders.proto b/proto/provenance/exchange/v1/orders.proto index 6fe3512d9f..bdcb1db839 100644 --- a/proto/provenance/exchange/v1/orders.proto +++ b/proto/provenance/exchange/v1/orders.proto @@ -36,11 +36,11 @@ message AskOrder { // price is the minimum amount that the seller is willing to accept for the assets. The seller's settlement // proportional fee (and possibly the settlement flat fee) is taken out of the amount the seller receives, // so it's possible that the seller will still receive less than this price. - cosmos.base.v1beta1.Coin price = 4; + cosmos.base.v1beta1.Coin price = 4 [(gogoproto.nullable) = false]; // seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. If this denom is the // same denom as the price, it will come out of the actual price received. If this denom is different, the amount must // be in the seller's account and a hold is placed on it until the order is filled or cancelled. - cosmos.base.v1beta1.Coin seller_settlement_flat_fee = 5; + cosmos.base.v1beta1.Coin seller_settlement_flat_fee = 5 [(gogoproto.nullable) = false]; // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the // order must be either filled in full or not filled at all. bool allow_partial = 6; @@ -57,7 +57,7 @@ message BidOrder { [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; // price is the amount that the buyer will pay for the assets. // A hold is placed on this until the order is filled or cancelled. - cosmos.base.v1beta1.Coin price = 4; + cosmos.base.v1beta1.Coin price = 4 [(gogoproto.nullable) = false]; // buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) // when the order is settled. A hold is placed on this until the order is filled or cancelled. repeated cosmos.base.v1beta1.Coin buyer_settlement_fees = 5 diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index 50a8697292..d4b3d4f40e 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -363,9 +363,9 @@ func (m *Market) GetReqAttrCreateBid() []string { // For an order to be valid, its price must be evenly divisible by a FeeRatio's price. type FeeRatio struct { // price is the unit the order price is divided by to get how much of the fee should apply. - Price *types1.Coin `protobuf:"bytes,1,opt,name=price,proto3" json:"price,omitempty"` + Price types1.Coin `protobuf:"bytes,1,opt,name=price,proto3" json:"price"` // fee is the amount to charge per price unit. - Fee *types1.Coin `protobuf:"bytes,2,opt,name=fee,proto3" json:"fee,omitempty"` + Fee types1.Coin `protobuf:"bytes,2,opt,name=fee,proto3" json:"fee"` } func (m *FeeRatio) Reset() { *m = FeeRatio{} } @@ -401,18 +401,18 @@ func (m *FeeRatio) XXX_DiscardUnknown() { var xxx_messageInfo_FeeRatio proto.InternalMessageInfo -func (m *FeeRatio) GetPrice() *types1.Coin { +func (m *FeeRatio) GetPrice() types1.Coin { if m != nil { return m.Price } - return nil + return types1.Coin{} } -func (m *FeeRatio) GetFee() *types1.Coin { +func (m *FeeRatio) GetFee() types1.Coin { if m != nil { return m.Fee } - return nil + return types1.Coin{} } // AddrPermissions associates an address with a list of permissions available for that address. @@ -484,70 +484,70 @@ func init() { } var fileDescriptor_d5cf198f1dd7e167 = []byte{ - // 1003 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcf, 0x6f, 0x1b, 0x45, - 0x14, 0xf6, 0xc6, 0xf9, 0x61, 0x8f, 0x93, 0xd4, 0x4c, 0xda, 0xb0, 0x76, 0x90, 0xbd, 0xb8, 0xaa, - 0xe4, 0x52, 0xc5, 0x26, 0xe1, 0xd6, 0x9b, 0x9d, 0x38, 0xd4, 0x52, 0x9b, 0x46, 0x6b, 0x5b, 0x95, - 0xb8, 0x2c, 0xb3, 0xbb, 0xcf, 0xce, 0x28, 0xeb, 0x5d, 0x77, 0x66, 0x36, 0x69, 0xb8, 0x71, 0x81, - 0x2a, 0x27, 0x8e, 0x5c, 0x22, 0x95, 0x2b, 0x67, 0xfe, 0x88, 0x1e, 0x23, 0x4e, 0x9c, 0x02, 0x4a, - 0xae, 0x9c, 0xf8, 0x0b, 0xd0, 0xce, 0x6c, 0xec, 0x6d, 0xb0, 0x01, 0x89, 0x72, 0xf2, 0xbe, 0xf7, - 0x7d, 0xf3, 0xbe, 0xef, 0xbd, 0xf9, 0x21, 0xa3, 0xfb, 0x23, 0x16, 0x1c, 0x83, 0x4f, 0x7c, 0x07, - 0xea, 0xf0, 0xca, 0x39, 0x24, 0xfe, 0x00, 0xea, 0xc7, 0x5b, 0xf5, 0x21, 0x61, 0x47, 0x20, 0x6a, - 0x23, 0x16, 0x88, 0x00, 0xaf, 0x4f, 0x48, 0xb5, 0x1b, 0x52, 0xed, 0x78, 0xab, 0x58, 0x72, 0x02, - 0x3e, 0x0c, 0x78, 0x9d, 0x84, 0xe2, 0xb0, 0x7e, 0xbc, 0x65, 0x83, 0x20, 0x5b, 0x32, 0x50, 0xeb, - 0xc6, 0xb8, 0x4d, 0x38, 0x8c, 0x71, 0x27, 0xa0, 0x7e, 0x8c, 0x17, 0x14, 0x6e, 0xc9, 0xa8, 0xae, - 0x82, 0x18, 0xba, 0x3b, 0x08, 0x06, 0x81, 0xca, 0x47, 0x5f, 0x2a, 0x5b, 0xf9, 0x5d, 0x43, 0x2b, - 0xcf, 0xa4, 0xb3, 0x86, 0xe3, 0x04, 0xa1, 0x2f, 0xf0, 0x97, 0x68, 0x39, 0xaa, 0x6e, 0x11, 0x15, - 0xeb, 0x9a, 0xa1, 0x55, 0x73, 0xdb, 0x46, 0x2d, 0x2e, 0x26, 0xcd, 0xc4, 0xca, 0xb5, 0x26, 0xe1, - 0x10, 0xaf, 0x6b, 0x6e, 0x5c, 0x5c, 0x96, 0xb5, 0x3f, 0x2e, 0xcb, 0x6b, 0xa7, 0x64, 0xe8, 0x3d, - 0xae, 0x24, 0x6b, 0x54, 0xcc, 0x9c, 0x3d, 0x61, 0xe2, 0x0d, 0x94, 0x55, 0xc3, 0xb0, 0xa8, 0xab, - 0xcf, 0x19, 0x5a, 0x75, 0xc5, 0xcc, 0xa8, 0x44, 0xdb, 0xc5, 0x26, 0x5a, 0x8d, 0x41, 0x17, 0x04, - 0xa1, 0x1e, 0xd7, 0xd3, 0xd2, 0xc0, 0x83, 0xda, 0xf4, 0x91, 0xd5, 0x94, 0xfb, 0x5d, 0x45, 0x6e, - 0xce, 0xbf, 0xbd, 0x2c, 0xa7, 0xcc, 0x95, 0x61, 0x32, 0xf9, 0x38, 0xf3, 0xfa, 0x4d, 0x39, 0xf5, - 0xfd, 0x9b, 0x72, 0xaa, 0xf2, 0xf5, 0xb8, 0xdd, 0x18, 0xc3, 0x18, 0xcd, 0xfb, 0x64, 0x08, 0xb2, - 0xcd, 0xac, 0x29, 0xbf, 0xb1, 0x81, 0x72, 0x2e, 0x70, 0x87, 0xd1, 0x91, 0xa0, 0x81, 0x2f, 0x2d, - 0x66, 0xcd, 0x64, 0x0a, 0x97, 0x51, 0xee, 0x04, 0x6c, 0x4e, 0x05, 0x58, 0x21, 0xf3, 0xa4, 0xc5, - 0xac, 0x89, 0xe2, 0x54, 0x8f, 0x79, 0xb8, 0x80, 0x32, 0xd4, 0x09, 0x7c, 0x2b, 0x64, 0x54, 0x9f, - 0x97, 0xe8, 0x52, 0x14, 0xf7, 0x18, 0xad, 0x5c, 0x64, 0xd0, 0xa2, 0xf2, 0xf0, 0xee, 0x24, 0xb4, - 0x7f, 0x9c, 0xc4, 0xdc, 0x7f, 0x9d, 0x04, 0xfe, 0x0a, 0xad, 0xf5, 0x01, 0x2c, 0x87, 0x01, 0x11, - 0x60, 0x11, 0x7e, 0x64, 0xf5, 0x3d, 0x22, 0xf4, 0xb4, 0x91, 0xae, 0xe6, 0xb6, 0x0b, 0x37, 0x7b, - 0x1c, 0x6d, 0xd6, 0x78, 0x8f, 0x77, 0x02, 0xea, 0x37, 0x3f, 0x8d, 0x8a, 0xfd, 0xf8, 0x6b, 0xb9, - 0x3a, 0xa0, 0xe2, 0x30, 0xb4, 0x6b, 0x4e, 0x30, 0x8c, 0x4f, 0x57, 0xfc, 0xb3, 0xc9, 0xdd, 0xa3, - 0xba, 0x38, 0x1d, 0x01, 0x97, 0x0b, 0xb8, 0x99, 0xef, 0x03, 0xec, 0x48, 0x99, 0x06, 0x3f, 0xda, - 0xf3, 0x88, 0xb8, 0xa5, 0x6d, 0x53, 0x57, 0x69, 0xcf, 0xff, 0x9f, 0xda, 0x4d, 0xea, 0x4a, 0xed, - 0xd7, 0x1a, 0x2a, 0x46, 0xe2, 0x1c, 0x84, 0xf0, 0x60, 0x08, 0xbe, 0xb0, 0x38, 0x78, 0x1e, 0x30, - 0xe5, 0x61, 0xe1, 0xfd, 0x7b, 0xf8, 0xb0, 0x0f, 0xd0, 0x19, 0xab, 0x75, 0xa4, 0x98, 0xb4, 0x42, - 0xd0, 0x47, 0xd3, 0x9d, 0x30, 0x22, 0x68, 0xc0, 0xf5, 0x45, 0xe9, 0xc5, 0x98, 0xb5, 0xc9, 0x7b, - 0x00, 0x66, 0x44, 0x34, 0x0b, 0x53, 0x04, 0x24, 0xc2, 0xf1, 0x37, 0x1a, 0x2a, 0xdc, 0xd2, 0xb0, - 0xc3, 0xd3, 0x9b, 0x66, 0x97, 0xde, 0x7f, 0xb3, 0xeb, 0xef, 0x78, 0x69, 0x46, 0x5a, 0xb2, 0x57, - 0x0b, 0x6d, 0x4c, 0xf5, 0x11, 0xb7, 0x9a, 0xf9, 0x97, 0xad, 0xea, 0x7f, 0x2d, 0x1f, 0x77, 0xfa, - 0x10, 0xe5, 0x89, 0xe3, 0xc0, 0x48, 0x50, 0x7f, 0x60, 0x05, 0xcc, 0x05, 0xc6, 0xf5, 0xac, 0xa1, - 0x55, 0x33, 0xe6, 0x9d, 0x71, 0xfe, 0xb9, 0x4c, 0xe3, 0x6d, 0x74, 0x8f, 0x78, 0x5e, 0x70, 0x62, - 0x85, 0x1c, 0x58, 0xc2, 0x92, 0x8e, 0x24, 0x7f, 0x4d, 0x82, 0x3d, 0x0e, 0x6c, 0xa2, 0x84, 0x9f, - 0xa0, 0x95, 0xa8, 0x0c, 0xe7, 0xd6, 0x80, 0x11, 0x5f, 0x70, 0x3d, 0x27, 0x1d, 0xdf, 0x9f, 0xe5, - 0xb8, 0x21, 0xc9, 0x9f, 0x47, 0x5c, 0x73, 0x99, 0x4c, 0x02, 0x8e, 0x37, 0xd1, 0x1a, 0x83, 0x97, - 0x16, 0x11, 0x82, 0x25, 0x6e, 0x9f, 0xbe, 0x6c, 0xa4, 0xab, 0x59, 0x33, 0xcf, 0xe0, 0x65, 0x43, - 0x08, 0x36, 0xbe, 0x2f, 0xd3, 0xe8, 0x36, 0x75, 0xf5, 0x95, 0x29, 0xf4, 0x26, 0x75, 0x2b, 0x87, - 0x28, 0x73, 0x33, 0x2c, 0x5c, 0x47, 0x0b, 0x23, 0x46, 0x1d, 0x88, 0x1f, 0xee, 0xd9, 0xfb, 0x6c, - 0x2a, 0x1e, 0x7e, 0x84, 0xd2, 0x7d, 0x80, 0xf8, 0x71, 0xf9, 0x1b, 0x7a, 0xc4, 0xaa, 0x7c, 0xab, - 0xa1, 0x5c, 0xa2, 0x4b, 0xbc, 0x8d, 0x96, 0x88, 0xeb, 0x32, 0xe0, 0x5c, 0xbd, 0xa0, 0x4d, 0xfd, - 0xe7, 0x9f, 0x36, 0xef, 0xc6, 0x35, 0x1a, 0x0a, 0xe9, 0x08, 0x46, 0xfd, 0x81, 0x79, 0x43, 0xc4, - 0xbb, 0x28, 0x37, 0x02, 0x36, 0xa4, 0x9c, 0xd3, 0xc0, 0x8f, 0x5e, 0xb5, 0x74, 0x75, 0x75, 0xbb, - 0x32, 0x6b, 0xa6, 0x07, 0x63, 0xaa, 0x99, 0x5c, 0xf6, 0xc9, 0x0f, 0x73, 0x08, 0x4d, 0x30, 0xfc, - 0x08, 0xad, 0x1f, 0xb4, 0xcc, 0x67, 0xed, 0x4e, 0xa7, 0xfd, 0x7c, 0xdf, 0xea, 0xed, 0x77, 0x0e, - 0x5a, 0x3b, 0xed, 0xbd, 0x76, 0x6b, 0x37, 0x9f, 0x2a, 0xde, 0x39, 0x3b, 0x37, 0x72, 0xa1, 0xcf, - 0x47, 0xe0, 0xd0, 0x3e, 0x05, 0x17, 0x7f, 0x8c, 0x3e, 0x48, 0x90, 0x3b, 0xad, 0x6e, 0xf7, 0x69, - 0x2b, 0xaf, 0x15, 0xd1, 0xd9, 0xb9, 0xb1, 0xa8, 0x4e, 0xc6, 0x2d, 0xca, 0x4e, 0x63, 0x7f, 0xa7, - 0xf5, 0x34, 0x3f, 0xa7, 0x28, 0x4e, 0x64, 0xd2, 0xc3, 0x0f, 0xd0, 0x5a, 0x82, 0xf2, 0xa2, 0xdd, - 0x7d, 0xb2, 0x6b, 0x36, 0x5e, 0xe4, 0xd3, 0xc5, 0xe5, 0xb3, 0x73, 0x23, 0x73, 0x42, 0xc5, 0xa1, - 0xcb, 0xc8, 0xc9, 0xad, 0x4a, 0xbd, 0x83, 0xdd, 0x46, 0xb7, 0x95, 0x9f, 0x57, 0x95, 0xc2, 0x91, - 0x4b, 0x04, 0xdc, 0x32, 0x3f, 0xf9, 0xec, 0xe4, 0x17, 0x94, 0xf9, 0x44, 0xe3, 0xf8, 0x21, 0xba, - 0x97, 0x20, 0x37, 0xba, 0x5d, 0xb3, 0xdd, 0xec, 0x75, 0x5b, 0x9d, 0xfc, 0x62, 0x71, 0xf5, 0xec, - 0xdc, 0x40, 0xd1, 0xa1, 0xa1, 0x76, 0x28, 0x80, 0x37, 0xe1, 0xed, 0x55, 0x49, 0xbb, 0xb8, 0x2a, - 0x69, 0xbf, 0x5d, 0x95, 0xb4, 0xef, 0xae, 0x4b, 0xa9, 0x8b, 0xeb, 0x52, 0xea, 0x97, 0xeb, 0x52, - 0x0a, 0x15, 0x68, 0x30, 0x63, 0xe0, 0x07, 0xda, 0x17, 0xb5, 0xc4, 0xc5, 0x9f, 0x90, 0x36, 0x69, - 0x90, 0x88, 0xea, 0xaf, 0xc6, 0xff, 0x6e, 0xec, 0x45, 0xf9, 0x5f, 0xe2, 0xb3, 0x3f, 0x03, 0x00, - 0x00, 0xff, 0xff, 0x53, 0xb1, 0xc9, 0x54, 0xfb, 0x08, 0x00, 0x00, + // 1007 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4f, 0x6f, 0xdb, 0xc6, + 0x13, 0x15, 0x2d, 0xff, 0x91, 0x56, 0xb6, 0xa3, 0xdf, 0x3a, 0xf1, 0x8f, 0x92, 0x0b, 0x89, 0x55, + 0x10, 0x40, 0x69, 0x61, 0xa9, 0x76, 0xd1, 0x4b, 0x6e, 0x92, 0x2d, 0x37, 0x02, 0x12, 0xc7, 0xa0, + 0x24, 0x04, 0xe8, 0x85, 0x5d, 0x92, 0x23, 0x79, 0x61, 0x8a, 0x54, 0x76, 0x97, 0x76, 0xdc, 0x5b, + 0x2f, 0x6d, 0xe0, 0x53, 0x8f, 0xbd, 0x18, 0x48, 0xaf, 0x3d, 0xf7, 0x43, 0xe4, 0x68, 0xf4, 0xd4, + 0x93, 0x5b, 0xd8, 0xd7, 0x9e, 0xfa, 0x09, 0x0a, 0xee, 0xd2, 0x12, 0xa3, 0xca, 0x4d, 0x81, 0xa6, + 0x27, 0x71, 0xe6, 0xbd, 0x9d, 0xf7, 0x66, 0x96, 0x1a, 0x10, 0xdd, 0x1f, 0xb1, 0xe0, 0x18, 0x7c, + 0xe2, 0x3b, 0x50, 0x87, 0x97, 0xce, 0x21, 0xf1, 0x07, 0x50, 0x3f, 0xde, 0xaa, 0x0f, 0x09, 0x3b, + 0x02, 0x51, 0x1b, 0xb1, 0x40, 0x04, 0x78, 0x7d, 0x42, 0xaa, 0xdd, 0x90, 0x6a, 0xc7, 0x5b, 0xc5, + 0x92, 0x13, 0xf0, 0x61, 0xc0, 0xeb, 0x24, 0x14, 0x87, 0xf5, 0xe3, 0x2d, 0x1b, 0x04, 0xd9, 0x92, + 0x81, 0x3a, 0x37, 0xc6, 0x6d, 0xc2, 0x61, 0x8c, 0x3b, 0x01, 0xf5, 0x63, 0xbc, 0xa0, 0x70, 0x4b, + 0x46, 0x75, 0x15, 0xc4, 0xd0, 0xdd, 0x41, 0x30, 0x08, 0x54, 0x3e, 0x7a, 0x52, 0xd9, 0xca, 0xef, + 0x1a, 0x5a, 0x79, 0x2a, 0x9d, 0x35, 0x1c, 0x27, 0x08, 0x7d, 0x81, 0xbf, 0x44, 0xcb, 0x51, 0x75, + 0x8b, 0xa8, 0x58, 0xd7, 0x0c, 0xad, 0x9a, 0xdb, 0x36, 0x6a, 0x71, 0x31, 0x69, 0x26, 0x56, 0xae, + 0x35, 0x09, 0x87, 0xf8, 0x5c, 0x73, 0xe3, 0xe2, 0xb2, 0xac, 0xfd, 0x71, 0x59, 0x5e, 0x3b, 0x25, + 0x43, 0xef, 0x51, 0x25, 0x59, 0xa3, 0x62, 0xe6, 0xec, 0x09, 0x13, 0x6f, 0xa0, 0xac, 0x1a, 0x86, + 0x45, 0x5d, 0x7d, 0xce, 0xd0, 0xaa, 0x2b, 0x66, 0x46, 0x25, 0xda, 0x2e, 0x36, 0xd1, 0x6a, 0x0c, + 0xba, 0x20, 0x08, 0xf5, 0xb8, 0x9e, 0x96, 0x06, 0x1e, 0xd4, 0x66, 0x8f, 0xac, 0xa6, 0xdc, 0xef, + 0x2a, 0x72, 0x73, 0xfe, 0xcd, 0x65, 0x39, 0x65, 0xae, 0x0c, 0x93, 0xc9, 0x47, 0x99, 0x57, 0xaf, + 0xcb, 0xa9, 0xef, 0x5f, 0x97, 0x53, 0x95, 0xaf, 0xc7, 0xed, 0xc6, 0x18, 0xc6, 0x68, 0xde, 0x27, + 0x43, 0x90, 0x6d, 0x66, 0x4d, 0xf9, 0x8c, 0x0d, 0x94, 0x73, 0x81, 0x3b, 0x8c, 0x8e, 0x04, 0x0d, + 0x7c, 0x69, 0x31, 0x6b, 0x26, 0x53, 0xb8, 0x8c, 0x72, 0x27, 0x60, 0x73, 0x2a, 0xc0, 0x0a, 0x99, + 0x27, 0x2d, 0x66, 0x4d, 0x14, 0xa7, 0x7a, 0xcc, 0xc3, 0x05, 0x94, 0xa1, 0x4e, 0xe0, 0x5b, 0x21, + 0xa3, 0xfa, 0xbc, 0x44, 0x97, 0xa2, 0xb8, 0xc7, 0x68, 0xe5, 0x22, 0x83, 0x16, 0x95, 0x87, 0xb7, + 0x27, 0xa1, 0xbd, 0x73, 0x12, 0x73, 0xff, 0x76, 0x12, 0xf8, 0x2b, 0xb4, 0xd6, 0x07, 0xb0, 0x1c, + 0x06, 0x44, 0x80, 0x45, 0xf8, 0x91, 0xd5, 0xf7, 0x88, 0xd0, 0xd3, 0x46, 0xba, 0x9a, 0xdb, 0x2e, + 0xdc, 0xdc, 0x71, 0x74, 0x59, 0xe3, 0x3b, 0xde, 0x09, 0xa8, 0xdf, 0xfc, 0x24, 0x2a, 0xf6, 0xe3, + 0xaf, 0xe5, 0xea, 0x80, 0x8a, 0xc3, 0xd0, 0xae, 0x39, 0xc1, 0x30, 0x7e, 0xbb, 0xe2, 0x9f, 0x4d, + 0xee, 0x1e, 0xd5, 0xc5, 0xe9, 0x08, 0xb8, 0x3c, 0xc0, 0xcd, 0x7c, 0x1f, 0x60, 0x47, 0xca, 0x34, + 0xf8, 0xd1, 0x9e, 0x47, 0xc4, 0x94, 0xb6, 0x4d, 0x5d, 0xa5, 0x3d, 0xff, 0x5f, 0x6a, 0x37, 0xa9, + 0x2b, 0xb5, 0x5f, 0x69, 0xa8, 0x18, 0x89, 0x73, 0x10, 0xc2, 0x83, 0x21, 0xf8, 0xc2, 0xe2, 0xe0, + 0x79, 0xc0, 0x94, 0x87, 0x85, 0xf7, 0xef, 0xe1, 0xff, 0x7d, 0x80, 0xce, 0x58, 0xad, 0x23, 0xc5, + 0xa4, 0x15, 0x82, 0x3e, 0x98, 0xed, 0x84, 0x11, 0x41, 0x03, 0xae, 0x2f, 0x4a, 0x2f, 0xc6, 0x6d, + 0x97, 0xbc, 0x07, 0x60, 0x46, 0x44, 0xb3, 0x30, 0x43, 0x40, 0x22, 0x1c, 0x7f, 0xa3, 0xa1, 0xc2, + 0x94, 0x86, 0x1d, 0x9e, 0xde, 0x34, 0xbb, 0xf4, 0xfe, 0x9b, 0x5d, 0x7f, 0xcb, 0x4b, 0x33, 0xd2, + 0x92, 0xbd, 0x5a, 0x68, 0x63, 0xa6, 0x8f, 0xb8, 0xd5, 0xcc, 0x3f, 0x6c, 0x55, 0xff, 0x6b, 0xf9, + 0xb8, 0xd3, 0x87, 0x28, 0x4f, 0x1c, 0x07, 0x46, 0x82, 0xfa, 0x03, 0x2b, 0x60, 0x2e, 0x30, 0xae, + 0x67, 0x0d, 0xad, 0x9a, 0x31, 0xef, 0x8c, 0xf3, 0xcf, 0x64, 0x1a, 0x6f, 0xa3, 0x7b, 0xc4, 0xf3, + 0x82, 0x13, 0x2b, 0xe4, 0xc0, 0x12, 0x96, 0x74, 0x24, 0xf9, 0x6b, 0x12, 0xec, 0x71, 0x60, 0x13, + 0x25, 0xfc, 0x18, 0xad, 0x44, 0x65, 0x38, 0xb7, 0x06, 0x8c, 0xf8, 0x82, 0xeb, 0x39, 0xe9, 0xf8, + 0xfe, 0x6d, 0x8e, 0x1b, 0x92, 0xfc, 0x79, 0xc4, 0x35, 0x97, 0xc9, 0x24, 0xe0, 0x78, 0x13, 0xad, + 0x31, 0x78, 0x61, 0x11, 0x21, 0x58, 0xe2, 0xdf, 0xa7, 0x2f, 0x1b, 0xe9, 0x6a, 0xd6, 0xcc, 0x33, + 0x78, 0xd1, 0x10, 0x82, 0x8d, 0xff, 0x2f, 0xb3, 0xe8, 0x36, 0x75, 0xf5, 0x95, 0x19, 0xf4, 0x26, + 0x75, 0x2b, 0x02, 0x65, 0x6e, 0x86, 0x85, 0x3f, 0x43, 0x0b, 0x23, 0x46, 0x1d, 0x88, 0x17, 0xf7, + 0xdf, 0xdc, 0xb3, 0xda, 0x10, 0x8a, 0x8d, 0xb7, 0x50, 0xba, 0x0f, 0x10, 0xaf, 0x98, 0x77, 0x1e, + 0x8a, 0xb8, 0x95, 0x6f, 0x35, 0x94, 0x4b, 0x74, 0x8c, 0xb7, 0xd1, 0x12, 0x71, 0x5d, 0x06, 0x9c, + 0xab, 0x6d, 0xda, 0xd4, 0x7f, 0xfe, 0x69, 0xf3, 0x6e, 0x5c, 0xa9, 0xa1, 0x90, 0x8e, 0x60, 0xd4, + 0x1f, 0x98, 0x37, 0x44, 0xbc, 0x8b, 0x72, 0x23, 0x60, 0x43, 0xca, 0x39, 0x0d, 0xfc, 0x68, 0xc3, + 0xa5, 0xab, 0xab, 0xdb, 0x95, 0xdb, 0xe6, 0x7b, 0x30, 0xa6, 0x9a, 0xc9, 0x63, 0x1f, 0xfd, 0x30, + 0x87, 0xd0, 0x04, 0xc3, 0x1f, 0xa3, 0xf5, 0x83, 0x96, 0xf9, 0xb4, 0xdd, 0xe9, 0xb4, 0x9f, 0xed, + 0x5b, 0xbd, 0xfd, 0xce, 0x41, 0x6b, 0xa7, 0xbd, 0xd7, 0x6e, 0xed, 0xe6, 0x53, 0xc5, 0x3b, 0x67, + 0xe7, 0x46, 0x2e, 0xf4, 0xf9, 0x08, 0x1c, 0xda, 0xa7, 0xe0, 0xe2, 0x0f, 0xd1, 0xff, 0x12, 0xe4, + 0x4e, 0xab, 0xdb, 0x7d, 0xd2, 0xca, 0x6b, 0x45, 0x74, 0x76, 0x6e, 0x2c, 0xaa, 0xb7, 0x64, 0x8a, + 0xb2, 0xd3, 0xd8, 0xdf, 0x69, 0x3d, 0xc9, 0xcf, 0x29, 0x8a, 0x13, 0x99, 0xf4, 0xf0, 0x03, 0xb4, + 0x96, 0xa0, 0x3c, 0x6f, 0x77, 0x1f, 0xef, 0x9a, 0x8d, 0xe7, 0xf9, 0x74, 0x71, 0xf9, 0xec, 0xdc, + 0xc8, 0x9c, 0x50, 0x71, 0xe8, 0x32, 0x72, 0x32, 0x55, 0xa9, 0x77, 0xb0, 0xdb, 0xe8, 0xb6, 0xf2, + 0xf3, 0xaa, 0x52, 0x38, 0x72, 0x89, 0x80, 0x29, 0xf3, 0x93, 0xc7, 0x4e, 0x7e, 0x41, 0x99, 0x4f, + 0x34, 0x8e, 0x1f, 0xa2, 0x7b, 0x09, 0x72, 0xa3, 0xdb, 0x35, 0xdb, 0xcd, 0x5e, 0xb7, 0xd5, 0xc9, + 0x2f, 0x16, 0x57, 0xcf, 0xce, 0x0d, 0x14, 0xbd, 0x40, 0xd4, 0x0e, 0x05, 0xf0, 0x26, 0xbc, 0xb9, + 0x2a, 0x69, 0x17, 0x57, 0x25, 0xed, 0xb7, 0xab, 0x92, 0xf6, 0xdd, 0x75, 0x29, 0x75, 0x71, 0x5d, + 0x4a, 0xfd, 0x72, 0x5d, 0x4a, 0xa1, 0x02, 0x0d, 0x6e, 0x19, 0xf8, 0x81, 0xf6, 0x45, 0x2d, 0xb1, + 0x04, 0x26, 0xa4, 0x4d, 0x1a, 0x24, 0xa2, 0xfa, 0xcb, 0xf1, 0x97, 0x8e, 0xbd, 0x28, 0xbf, 0x2b, + 0x3e, 0xfd, 0x33, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x1a, 0xfc, 0x68, 0x07, 0x09, 0x00, 0x00, } func (m *MarketAccount) Marshal() (dAtA []byte, err error) { @@ -845,30 +845,26 @@ func (m *FeeRatio) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.Fee != nil { - { - size, err := m.Fee.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintMarket(dAtA, i, uint64(size)) + { + size, err := m.Fee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - i-- - dAtA[i] = 0x12 + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) } - if m.Price != nil { - { - size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintMarket(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + { + size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - i-- - dAtA[i] = 0xa + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa return len(dAtA) - i, nil } @@ -1054,14 +1050,10 @@ func (m *FeeRatio) Size() (n int) { } var l int _ = l - if m.Price != nil { - l = m.Price.Size() - n += 1 + l + sovMarket(uint64(l)) - } - if m.Fee != nil { - l = m.Fee.Size() - n += 1 + l + sovMarket(uint64(l)) - } + l = m.Price.Size() + n += 1 + l + sovMarket(uint64(l)) + l = m.Fee.Size() + n += 1 + l + sovMarket(uint64(l)) return n } @@ -1909,9 +1901,6 @@ func (m *FeeRatio) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Price == nil { - m.Price = &types1.Coin{} - } if err := m.Price.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -1945,9 +1934,6 @@ func (m *FeeRatio) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Fee == nil { - m.Fee = &types1.Coin{} - } if err := m.Fee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } diff --git a/x/exchange/orders.pb.go b/x/exchange/orders.pb.go index c5077d008b..9cb459842c 100644 --- a/x/exchange/orders.pb.go +++ b/x/exchange/orders.pb.go @@ -135,11 +135,11 @@ type AskOrder struct { // price is the minimum amount that the seller is willing to accept for the assets. The seller's settlement // proportional fee (and possibly the settlement flat fee) is taken out of the amount the seller receives, // so it's possible that the seller will still receive less than this price. - Price *types.Coin `protobuf:"bytes,4,opt,name=price,proto3" json:"price,omitempty"` + Price types.Coin `protobuf:"bytes,4,opt,name=price,proto3" json:"price"` // seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. If this denom is the // same denom as the price, it will come out of the actual price received. If this denom is different, the amount must // be in the seller's account and a hold is placed on it until the order is filled or cancelled. - SellerSettlementFlatFee *types.Coin `protobuf:"bytes,5,opt,name=seller_settlement_flat_fee,json=sellerSettlementFlatFee,proto3" json:"seller_settlement_flat_fee,omitempty"` + SellerSettlementFlatFee types.Coin `protobuf:"bytes,5,opt,name=seller_settlement_flat_fee,json=sellerSettlementFlatFee,proto3" json:"seller_settlement_flat_fee"` // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the // order must be either filled in full or not filled at all. AllowPartial bool `protobuf:"varint,6,opt,name=allow_partial,json=allowPartial,proto3" json:"allow_partial,omitempty"` @@ -199,18 +199,18 @@ func (m *AskOrder) GetAssets() github_com_cosmos_cosmos_sdk_types.Coins { return nil } -func (m *AskOrder) GetPrice() *types.Coin { +func (m *AskOrder) GetPrice() types.Coin { if m != nil { return m.Price } - return nil + return types.Coin{} } -func (m *AskOrder) GetSellerSettlementFlatFee() *types.Coin { +func (m *AskOrder) GetSellerSettlementFlatFee() types.Coin { if m != nil { return m.SellerSettlementFlatFee } - return nil + return types.Coin{} } func (m *AskOrder) GetAllowPartial() bool { @@ -230,7 +230,7 @@ type BidOrder struct { Assets github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=assets,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"assets"` // price is the amount that the buyer will pay for the assets. // A hold is placed on this until the order is filled or cancelled. - Price *types.Coin `protobuf:"bytes,4,opt,name=price,proto3" json:"price,omitempty"` + Price types.Coin `protobuf:"bytes,4,opt,name=price,proto3" json:"price"` // buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) // when the order is settled. A hold is placed on this until the order is filled or cancelled. BuyerSettlementFees github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,5,rep,name=buyer_settlement_fees,json=buyerSettlementFees,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"buyer_settlement_fees"` @@ -293,11 +293,11 @@ func (m *BidOrder) GetAssets() github_com_cosmos_cosmos_sdk_types.Coins { return nil } -func (m *BidOrder) GetPrice() *types.Coin { +func (m *BidOrder) GetPrice() types.Coin { if m != nil { return m.Price } - return nil + return types.Coin{} } func (m *BidOrder) GetBuyerSettlementFees() github_com_cosmos_cosmos_sdk_types.Coins { @@ -325,40 +325,40 @@ func init() { } var fileDescriptor_dab7cbe63f582471 = []byte{ - // 517 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xb1, 0x6e, 0xd3, 0x40, - 0x18, 0x8e, 0x9b, 0x3a, 0x75, 0xae, 0x74, 0x31, 0x05, 0x9c, 0x20, 0xb9, 0x56, 0xba, 0x78, 0xc9, - 0xb9, 0x29, 0x0f, 0x80, 0x6a, 0xa4, 0x8a, 0x4e, 0x54, 0xae, 0xc4, 0xc0, 0x62, 0x9d, 0xed, 0xbf, - 0xee, 0x29, 0x8e, 0xcf, 0xf2, 0x5d, 0x43, 0x3b, 0xf1, 0x0a, 0x48, 0xbc, 0x05, 0x12, 0x1b, 0x0f, - 0xd1, 0xb1, 0x62, 0x62, 0x02, 0x94, 0xac, 0x3c, 0x04, 0xf2, 0xdd, 0x25, 0x0d, 0x12, 0x2d, 0x1d, - 0x18, 0x98, 0x7c, 0xff, 0x7f, 0xdf, 0xf7, 0xfd, 0x9f, 0xbe, 0x5f, 0x3e, 0xb4, 0x5b, 0xd5, 0x6c, - 0x0a, 0x25, 0x29, 0x53, 0x08, 0xe0, 0x22, 0x3d, 0x23, 0x65, 0x0e, 0xc1, 0x74, 0x14, 0xb0, 0x3a, - 0x83, 0x9a, 0xe3, 0xaa, 0x66, 0x82, 0xd9, 0x8f, 0x6f, 0x40, 0x78, 0x01, 0xc2, 0xd3, 0x51, 0xdf, - 0x4d, 0x19, 0x9f, 0x30, 0x1e, 0x24, 0x84, 0x37, 0xa4, 0x04, 0x04, 0x19, 0x05, 0x29, 0xa3, 0xa5, - 0xe2, 0xf5, 0x7b, 0xea, 0x3e, 0x96, 0x55, 0xa0, 0x0a, 0x7d, 0xb5, 0x9d, 0xb3, 0x9c, 0xa9, 0x7e, - 0x73, 0x52, 0xdd, 0xc1, 0x27, 0x03, 0x99, 0xaf, 0x9a, 0xc9, 0x76, 0x0f, 0x59, 0xd2, 0x42, 0x4c, - 0x33, 0xc7, 0xf0, 0x0c, 0x7f, 0x3d, 0xda, 0x90, 0xf5, 0x51, 0x66, 0x3f, 0x47, 0x5d, 0xc2, 0xc7, - 0xb1, 0x2c, 0x9d, 0x35, 0xcf, 0xf0, 0x37, 0xf7, 0x3d, 0xfc, 0x67, 0x87, 0xf8, 0x80, 0x8f, 0xa5, - 0xde, 0xcb, 0x56, 0x64, 0x11, 0x7d, 0x6e, 0x04, 0x12, 0x9a, 0x69, 0x81, 0xf6, 0xdd, 0x02, 0x21, - 0xcd, 0x96, 0x02, 0x89, 0x3e, 0x87, 0x1b, 0xc8, 0x94, 0xe4, 0xc1, 0xcf, 0x35, 0x64, 0x2d, 0x46, - 0xd8, 0x4f, 0x51, 0x77, 0x42, 0xea, 0x31, 0x88, 0x85, 0xe7, 0xad, 0xc8, 0x52, 0x8d, 0xa3, 0xcc, - 0xde, 0x43, 0x1d, 0x0e, 0x45, 0xa1, 0x1d, 0x77, 0x43, 0xe7, 0xcb, 0xe7, 0xe1, 0xb6, 0x4e, 0xe4, - 0x20, 0xcb, 0x6a, 0xe0, 0xfc, 0x44, 0xd4, 0xb4, 0xcc, 0x23, 0x8d, 0xb3, 0x53, 0xd4, 0x21, 0x9c, - 0x83, 0xe0, 0x4e, 0xdb, 0x6b, 0xfb, 0x9b, 0xfb, 0x3d, 0xac, 0xe1, 0x4d, 0xda, 0x58, 0xa7, 0x8d, - 0x5f, 0x30, 0x5a, 0x86, 0x7b, 0x57, 0xdf, 0x76, 0x5a, 0x1f, 0xbf, 0xef, 0xf8, 0x39, 0x15, 0x67, - 0xe7, 0x09, 0x4e, 0xd9, 0x44, 0xa7, 0xad, 0x3f, 0x43, 0x9e, 0x8d, 0x03, 0x71, 0x59, 0x01, 0x97, - 0x04, 0x1e, 0x69, 0x69, 0x3b, 0x40, 0x66, 0x55, 0xd3, 0x14, 0x9c, 0x75, 0x19, 0xc3, 0xed, 0x33, - 0x22, 0x85, 0xb3, 0x5f, 0xa3, 0xbe, 0xf2, 0x17, 0x73, 0x10, 0xa2, 0x80, 0x09, 0x94, 0x22, 0x3e, - 0x2d, 0x88, 0x88, 0x4f, 0x01, 0x1c, 0xf3, 0x6f, 0x2a, 0x4f, 0x14, 0xf9, 0x64, 0xc9, 0x3d, 0x2c, - 0x88, 0x38, 0x04, 0xb0, 0x77, 0xd1, 0x16, 0x29, 0x0a, 0xf6, 0x36, 0xae, 0x48, 0x2d, 0x28, 0x29, - 0x9c, 0x8e, 0x67, 0xf8, 0x56, 0xf4, 0x40, 0x36, 0x8f, 0x55, 0x6f, 0xf0, 0xa1, 0x8d, 0xac, 0xc5, - 0x42, 0xee, 0x8e, 0x1b, 0x23, 0x33, 0x39, 0xbf, 0xbc, 0x47, 0xda, 0x0a, 0xf6, 0x9f, 0x86, 0xfd, - 0x0e, 0x3d, 0x92, 0xf6, 0x7e, 0xcb, 0x1a, 0x80, 0x3b, 0xe6, 0xbf, 0x37, 0xf9, 0x50, 0x4e, 0x5a, - 0x59, 0x0c, 0x00, 0xbf, 0xd7, 0x56, 0x42, 0xb8, 0x9a, 0xb9, 0xc6, 0xf5, 0xcc, 0x35, 0x7e, 0xcc, - 0x5c, 0xe3, 0xfd, 0xdc, 0x6d, 0x5d, 0xcf, 0xdd, 0xd6, 0xd7, 0xb9, 0xdb, 0x42, 0x3d, 0xca, 0x6e, - 0xf9, 0xaf, 0x8e, 0x8d, 0x37, 0x78, 0xc5, 0xda, 0x0d, 0x68, 0x48, 0xd9, 0x4a, 0x15, 0x5c, 0x2c, - 0x1f, 0xa5, 0xa4, 0x23, 0x9f, 0x88, 0x67, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0xba, 0xa4, 0x90, - 0x35, 0xb2, 0x04, 0x00, 0x00, + // 521 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0x31, 0x6f, 0xd3, 0x4c, + 0x18, 0x8e, 0x9b, 0x38, 0x75, 0xae, 0x5f, 0x17, 0x7f, 0x05, 0x9c, 0x20, 0xb9, 0x51, 0xba, 0x78, + 0xc9, 0xb9, 0x29, 0x62, 0x46, 0x35, 0x52, 0x45, 0x27, 0x2a, 0x77, 0x43, 0x48, 0xd6, 0xd9, 0x7e, + 0xeb, 0x9e, 0xe2, 0xf8, 0x2c, 0xdf, 0x35, 0xb4, 0x13, 0x7f, 0x81, 0x95, 0xbf, 0x80, 0xc4, 0xc6, + 0x8f, 0xe8, 0x58, 0x31, 0x31, 0x01, 0x4a, 0xfe, 0x04, 0x23, 0xf2, 0xdd, 0x25, 0x0d, 0x12, 0x44, + 0x19, 0x18, 0x98, 0x7c, 0xef, 0x7b, 0xcf, 0xfb, 0x3c, 0x8f, 0x9e, 0x57, 0x3e, 0x74, 0x50, 0x56, + 0x6c, 0x0a, 0x05, 0x29, 0x12, 0xf0, 0xe1, 0x3a, 0xb9, 0x24, 0x45, 0x06, 0xfe, 0x74, 0xe4, 0xb3, + 0x2a, 0x85, 0x8a, 0xe3, 0xb2, 0x62, 0x82, 0xd9, 0x0f, 0xef, 0x41, 0x78, 0x01, 0xc2, 0xd3, 0x51, + 0xcf, 0x4d, 0x18, 0x9f, 0x30, 0xee, 0xc7, 0x84, 0xd7, 0x43, 0x31, 0x08, 0x32, 0xf2, 0x13, 0x46, + 0x0b, 0x35, 0xd7, 0xeb, 0xaa, 0xfb, 0x48, 0x56, 0xbe, 0x2a, 0xf4, 0xd5, 0x5e, 0xc6, 0x32, 0xa6, + 0xfa, 0xf5, 0x49, 0x75, 0x07, 0x1f, 0x0d, 0x64, 0xbe, 0xac, 0x95, 0xed, 0x2e, 0xb2, 0xa4, 0x85, + 0x88, 0xa6, 0x8e, 0xd1, 0x37, 0xbc, 0x56, 0xb8, 0x2d, 0xeb, 0xd3, 0xd4, 0x7e, 0x86, 0x3a, 0x84, + 0x8f, 0x23, 0x59, 0x3a, 0x5b, 0x7d, 0xc3, 0xdb, 0x39, 0xea, 0xe3, 0xdf, 0x3b, 0xc4, 0xc7, 0x7c, + 0x2c, 0xf9, 0x5e, 0x34, 0x42, 0x8b, 0xe8, 0x73, 0x4d, 0x10, 0xd3, 0x54, 0x13, 0x34, 0xd7, 0x13, + 0x04, 0x34, 0x5d, 0x12, 0xc4, 0xfa, 0x1c, 0x6c, 0x23, 0x53, 0x0e, 0x0f, 0x7e, 0x6c, 0x21, 0x6b, + 0x21, 0x61, 0x3f, 0x46, 0x9d, 0x09, 0xa9, 0xc6, 0x20, 0x16, 0x9e, 0x77, 0x43, 0x4b, 0x35, 0x4e, + 0x53, 0xfb, 0x10, 0xb5, 0x39, 0xe4, 0xb9, 0x76, 0xdc, 0x09, 0x9c, 0xcf, 0x9f, 0x86, 0x7b, 0x3a, + 0x91, 0xe3, 0x34, 0xad, 0x80, 0xf3, 0x73, 0x51, 0xd1, 0x22, 0x0b, 0x35, 0xce, 0x4e, 0x50, 0x9b, + 0x70, 0x0e, 0x82, 0x3b, 0xcd, 0x7e, 0xd3, 0xdb, 0x39, 0xea, 0x62, 0x0d, 0xaf, 0xd3, 0xc6, 0x3a, + 0x6d, 0xfc, 0x9c, 0xd1, 0x22, 0x38, 0xbc, 0xfd, 0xba, 0xdf, 0xf8, 0xf0, 0x6d, 0xdf, 0xcb, 0xa8, + 0xb8, 0xbc, 0x8a, 0x71, 0xc2, 0x26, 0x3a, 0x6d, 0xfd, 0x19, 0xf2, 0x74, 0xec, 0x8b, 0x9b, 0x12, + 0xb8, 0x1c, 0xe0, 0xa1, 0xa6, 0xb6, 0x9f, 0x22, 0xb3, 0xac, 0x68, 0x02, 0x4e, 0x4b, 0xc6, 0xb0, + 0x46, 0xa3, 0x55, 0x6b, 0x84, 0x0a, 0x6d, 0xbf, 0x46, 0x3d, 0xe5, 0x32, 0xe2, 0x20, 0x44, 0x0e, + 0x13, 0x28, 0x44, 0x74, 0x91, 0x13, 0x11, 0x5d, 0x00, 0x38, 0xe6, 0x66, 0x5c, 0x8f, 0x14, 0xc5, + 0xf9, 0x92, 0xe1, 0x24, 0x27, 0xe2, 0x04, 0xc0, 0x3e, 0x40, 0xbb, 0x24, 0xcf, 0xd9, 0x9b, 0xa8, + 0x24, 0x95, 0xa0, 0x24, 0x77, 0xda, 0x7d, 0xc3, 0xb3, 0xc2, 0xff, 0x64, 0xf3, 0x4c, 0xf5, 0x06, + 0xef, 0x9b, 0xc8, 0x5a, 0x2c, 0x67, 0x7d, 0xf4, 0x18, 0x99, 0xf1, 0xd5, 0xcd, 0x06, 0xc9, 0x2b, + 0xd8, 0x3f, 0x1d, 0xfc, 0x5b, 0xf4, 0x40, 0x9a, 0xfc, 0x25, 0x77, 0x00, 0xee, 0x98, 0x7f, 0xdf, + 0xea, 0xff, 0x52, 0x69, 0x65, 0x3d, 0x00, 0x7c, 0xa3, 0xdd, 0x04, 0x70, 0x3b, 0x73, 0x8d, 0xbb, + 0x99, 0x6b, 0x7c, 0x9f, 0xb9, 0xc6, 0xbb, 0xb9, 0xdb, 0xb8, 0x9b, 0xbb, 0x8d, 0x2f, 0x73, 0xb7, + 0x81, 0xba, 0x94, 0xfd, 0xe1, 0x4f, 0x3b, 0x33, 0x5e, 0xe1, 0x15, 0x6b, 0xf7, 0xa0, 0x21, 0x65, + 0x2b, 0x95, 0x7f, 0xbd, 0x7c, 0xa6, 0xe2, 0xb6, 0x7c, 0x34, 0x9e, 0xfc, 0x0c, 0x00, 0x00, 0xff, + 0xff, 0xa4, 0x50, 0xc3, 0x4d, 0xc4, 0x04, 0x00, 0x00, } func (m *Order) Marshal() (dAtA []byte, err error) { @@ -470,30 +470,26 @@ func (m *AskOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x30 } - if m.SellerSettlementFlatFee != nil { - { - size, err := m.SellerSettlementFlatFee.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintOrders(dAtA, i, uint64(size)) + { + size, err := m.SellerSettlementFlatFee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - i-- - dAtA[i] = 0x2a + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) } - if m.Price != nil { - { - size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintOrders(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a + { + size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - i-- - dAtA[i] = 0x22 + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x22 if len(m.Assets) > 0 { for iNdEx := len(m.Assets) - 1; iNdEx >= 0; iNdEx-- { { @@ -567,18 +563,16 @@ func (m *BidOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x2a } } - if m.Price != nil { - { - size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintOrders(dAtA, i, uint64(size)) + { + size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - i-- - dAtA[i] = 0x22 + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x22 if len(m.Assets) > 0 { for iNdEx := len(m.Assets) - 1; iNdEx >= 0; iNdEx-- { { @@ -677,14 +671,10 @@ func (m *AskOrder) Size() (n int) { n += 1 + l + sovOrders(uint64(l)) } } - if m.Price != nil { - l = m.Price.Size() - n += 1 + l + sovOrders(uint64(l)) - } - if m.SellerSettlementFlatFee != nil { - l = m.SellerSettlementFlatFee.Size() - n += 1 + l + sovOrders(uint64(l)) - } + l = m.Price.Size() + n += 1 + l + sovOrders(uint64(l)) + l = m.SellerSettlementFlatFee.Size() + n += 1 + l + sovOrders(uint64(l)) if m.AllowPartial { n += 2 } @@ -710,10 +700,8 @@ func (m *BidOrder) Size() (n int) { n += 1 + l + sovOrders(uint64(l)) } } - if m.Price != nil { - l = m.Price.Size() - n += 1 + l + sovOrders(uint64(l)) - } + l = m.Price.Size() + n += 1 + l + sovOrders(uint64(l)) if len(m.BuyerSettlementFees) > 0 { for _, e := range m.BuyerSettlementFees { l = e.Size() @@ -1014,9 +1002,6 @@ func (m *AskOrder) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Price == nil { - m.Price = &types.Coin{} - } if err := m.Price.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -1050,9 +1035,6 @@ func (m *AskOrder) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.SellerSettlementFlatFee == nil { - m.SellerSettlementFlatFee = &types.Coin{} - } if err := m.SellerSettlementFlatFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -1241,9 +1223,6 @@ func (m *BidOrder) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Price == nil { - m.Price = &types.Coin{} - } if err := m.Price.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } From a0738a06e0d85fa5a5c540cc297d1095a68cb300 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 23 Aug 2023 17:26:49 -0600 Subject: [PATCH 008/309] [1658]: Allow the params to be nil in genesis. --- proto/provenance/exchange/v1/qenesis.proto | 2 +- x/exchange/qenesis.pb.go | 53 ++++++++++++---------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/proto/provenance/exchange/v1/qenesis.proto b/proto/provenance/exchange/v1/qenesis.proto index 8a5591d5fe..2f842f4bd9 100644 --- a/proto/provenance/exchange/v1/qenesis.proto +++ b/proto/provenance/exchange/v1/qenesis.proto @@ -17,7 +17,7 @@ message GenesisState { option (gogoproto.goproto_getters) = false; // params defines all the parameters of the exchange module. - Params params = 1 [(gogoproto.nullable) = false]; + Params params = 1; // markets are all of the markets to create at genesis. repeated Market markets = 2; diff --git a/x/exchange/qenesis.pb.go b/x/exchange/qenesis.pb.go index eacc86ea0b..aecf26bbfc 100644 --- a/x/exchange/qenesis.pb.go +++ b/x/exchange/qenesis.pb.go @@ -26,7 +26,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // GenesisState is the data that should be loaded into the exchange module during genesis. type GenesisState struct { // params defines all the parameters of the exchange module. - Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` // markets are all of the markets to create at genesis. Markets []*Market `protobuf:"bytes,2,rep,name=markets,proto3" json:"markets,omitempty"` // orders are all the orders to create at genesis. @@ -75,24 +75,24 @@ func init() { } var fileDescriptor_dca953db3e677d28 = []byte{ - // 272 bytes of a gzipped FileDescriptorProto + // 265 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x29, 0x28, 0xca, 0x2f, 0x4b, 0xcd, 0x4b, 0xcc, 0x4b, 0x4e, 0xd5, 0x4f, 0xad, 0x48, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0xd5, 0x2f, 0x33, 0xd4, 0x2f, 0x4c, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x43, 0xa8, 0xd2, 0x83, 0xa9, 0xd2, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x2b, 0xd1, 0x07, 0xb1, 0x20, 0xaa, 0xa5, 0x94, 0x71, 0x98, 0x99, 0x9b, 0x58, 0x94, 0x9d, 0x5a, 0x42, 0x40, 0x51, 0x7e, 0x51, 0x4a, 0x6a, 0x51, 0x31, 0x01, 0x45, 0x05, 0x89, 0x45, 0x89, - 0xb9, 0x50, 0x45, 0x4a, 0xc7, 0x19, 0xb9, 0x78, 0xdc, 0x21, 0xce, 0x0d, 0x2e, 0x49, 0x2c, 0x49, - 0x15, 0xb2, 0xe1, 0x62, 0x83, 0x28, 0x90, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd3, 0xc3, - 0xee, 0x7c, 0xbd, 0x00, 0xb0, 0x2a, 0x27, 0x96, 0x13, 0xf7, 0xe4, 0x19, 0x82, 0xa0, 0x7a, 0x84, - 0x2c, 0xb8, 0xd8, 0x21, 0x0e, 0x2d, 0x96, 0x60, 0x52, 0x60, 0xc6, 0xa7, 0xdd, 0x17, 0xac, 0x2c, - 0x08, 0xa6, 0x5c, 0xc8, 0x94, 0x8b, 0x0d, 0xe2, 0x7a, 0x09, 0x66, 0xb0, 0x46, 0x59, 0x5c, 0x1a, - 0xfd, 0x41, 0xaa, 0x82, 0xa0, 0x8a, 0xad, 0x38, 0x3a, 0x16, 0xc8, 0x33, 0xbc, 0x58, 0x20, 0xcf, - 0xe0, 0x94, 0x7a, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, - 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x5c, 0x92, 0x99, 0xf9, - 0x38, 0x0c, 0x0b, 0x60, 0x8c, 0xd2, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, - 0xd5, 0x47, 0x28, 0xd2, 0xcd, 0xcc, 0x47, 0xe2, 0xe9, 0x57, 0xc0, 0x03, 0x30, 0x89, 0x0d, 0x1c, - 0x6e, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x96, 0x4d, 0x65, 0x96, 0xfc, 0x01, 0x00, 0x00, + 0xb9, 0x50, 0x45, 0x4a, 0x07, 0x19, 0xb9, 0x78, 0xdc, 0x21, 0xce, 0x0d, 0x2e, 0x49, 0x2c, 0x49, + 0x15, 0x32, 0xe3, 0x62, 0x83, 0x28, 0x90, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd3, 0xc3, + 0xee, 0x7c, 0xbd, 0x00, 0xb0, 0xaa, 0x20, 0xa8, 0x6a, 0x21, 0x0b, 0x2e, 0x76, 0x88, 0x13, 0x8b, + 0x25, 0x98, 0x14, 0x98, 0xf1, 0x69, 0xf4, 0x05, 0x2b, 0x0b, 0x82, 0x29, 0x17, 0x32, 0xe5, 0x62, + 0x83, 0xb8, 0x5b, 0x82, 0x19, 0xac, 0x51, 0x16, 0x97, 0x46, 0x7f, 0x90, 0xaa, 0x20, 0xa8, 0x62, + 0x2b, 0x8e, 0x8e, 0x05, 0xf2, 0x0c, 0x2f, 0x16, 0xc8, 0x33, 0x38, 0xa5, 0x9e, 0x78, 0x24, 0xc7, + 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, + 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x03, 0x97, 0x64, 0x66, 0x3e, 0x0e, 0xc3, 0x02, 0x18, 0xa3, 0xf4, + 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x11, 0x8a, 0x74, 0x33, 0xf3, + 0x91, 0x78, 0xfa, 0x15, 0xf0, 0xa0, 0x4b, 0x62, 0x03, 0x87, 0x98, 0x31, 0x20, 0x00, 0x00, 0xff, + 0xff, 0xa5, 0x94, 0x18, 0x9e, 0xf6, 0x01, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -143,16 +143,18 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x12 } } - { - size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err + if m.Params != nil { + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQenesis(dAtA, i, uint64(size)) } - i -= size - i = encodeVarintQenesis(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa } - i-- - dAtA[i] = 0xa return len(dAtA) - i, nil } @@ -173,8 +175,10 @@ func (m *GenesisState) Size() (n int) { } var l int _ = l - l = m.Params.Size() - n += 1 + l + sovQenesis(uint64(l)) + if m.Params != nil { + l = m.Params.Size() + n += 1 + l + sovQenesis(uint64(l)) + } if len(m.Markets) > 0 { for _, e := range m.Markets { l = e.Size() @@ -254,6 +258,9 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } + if m.Params == nil { + m.Params = &Params{} + } if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } From 05bd5fc0271c353b0f77b7efef180e70a15051c0 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 24 Aug 2023 17:36:42 -0600 Subject: [PATCH 009/309] [1658]: Refactor the name keeper's Normalize method to extract the stateless stuff into the types package as NormalizeName, IsValidName, IsValidNameSegment, and IsValidUUID. --- x/name/keeper/keeper.go | 57 ++------- x/name/types/name.go | 54 ++++++++ x/name/types/name_test.go | 251 +++++++++++++++++++++++++++++++++++++- 3 files changed, 315 insertions(+), 47 deletions(-) diff --git a/x/name/keeper/keeper.go b/x/name/keeper/keeper.go index 44da7a3ef3..b74dabcf2c 100644 --- a/x/name/keeper/keeper.go +++ b/x/name/keeper/keeper.go @@ -4,9 +4,6 @@ import ( "bytes" "fmt" "strings" - "unicode" - - "github.com/google/uuid" "github.com/tendermint/tendermint/libs/log" @@ -252,58 +249,26 @@ func (k Keeper) IterateRecords(ctx sdk.Context, prefix []byte, handle func(recor // Normalize returns a name is storage format. func (k Keeper) Normalize(ctx sdk.Context, name string) (string, error) { - comps := make([]string, 0) - for _, comp := range strings.Split(name, ".") { - comp = strings.ToLower(strings.TrimSpace(comp)) - lenComp := uint32(len(comp)) - isUUID := isValidUUID(comp) - if lenComp < k.GetMinSegmentLength(ctx) { + normalized := types.NormalizeName(name) + segCount := uint32(0) + for _, segment := range strings.Split(normalized, ".") { + segCount++ + segLen := uint32(len(segment)) + isUUID := types.IsValidUUID(segment) + if segLen < k.GetMinSegmentLength(ctx) { return "", types.ErrNameSegmentTooShort } - if lenComp > k.GetMaxSegmentLength(ctx) && !isUUID { + if segLen > k.GetMaxSegmentLength(ctx) && !isUUID { return "", types.ErrNameSegmentTooLong } - if !isValid(comp) { + if !types.IsValidNameSegment(segment) { return "", types.ErrNameInvalid } - comps = append(comps, comp) } - if uint32(len(comps)) > k.GetMaxNameLevels(ctx) { + if segCount > k.GetMaxNameLevels(ctx) { return "", types.ErrNameHasTooManySegments } - return strings.Join(comps, "."), nil -} - -// Check whether a name component is valid -func isValid(s string) bool { - // Allow valid UUID - if isValidUUID(s) { - return true - } - // Only allow a single dash if not a UUID - if strings.Count(s, "-") > 1 { - return false - } - for _, c := range s { - if c == '-' { - continue - } - if !unicode.IsGraphic(c) { - return false - } - if !unicode.IsLower(c) && !unicode.IsDigit(c) { - return false - } - } - return true -} - -// Ensure a string can be parsed into a UUID. -func isValidUUID(s string) bool { - if _, err := uuid.Parse(s); err != nil { - return false - } - return true + return normalized, nil } func (k Keeper) addRecord(ctx sdk.Context, name string, addr sdk.AccAddress, restrict, isModifiable bool) error { diff --git a/x/name/types/name.go b/x/name/types/name.go index ea1d707b16..81199ddeec 100644 --- a/x/name/types/name.go +++ b/x/name/types/name.go @@ -3,6 +3,9 @@ package types import ( "fmt" "strings" + "unicode" + + "github.com/google/uuid" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -34,3 +37,54 @@ func (nr NameRecord) Validate() error { } return nil } + +// NormalizeName lower-cases and strips out spaces around each segment in the provided string. +func NormalizeName(name string) string { + nameSegments := strings.Split(name, ".") + segments := make([]string, len(nameSegments)) + for i, nameSegment := range nameSegments { + segments[i] = strings.ToLower(strings.TrimSpace(nameSegment)) + } + return strings.Join(segments, ".") +} + +// IsValidName returns true if the provided name is valid after normalization and without consideration of length limits. +func IsValidName(name string) bool { + for _, segment := range strings.Split(NormalizeName(name), ".") { + if !IsValidNameSegment(segment) { + return false + } + } + return true +} + +// IsValidNameSegment returns true if the provided string is a valid name segment. +// Name segments can only contain dashes, digits, and lower-case letters. +// If it's not a uuid, it can have at most one dash. +// +// The length of the segment is not considered here because the length limits are defined in state. +func IsValidNameSegment(segment string) bool { + // Allow valid UUID + if IsValidUUID(segment) { + return true + } + // Only allow a single dash if not a UUID + if strings.Count(segment, "-") > 1 { + return false + } + // Only allow dashes, lowercase characters and digits. + for _, c := range segment { + if c != '-' && !unicode.IsLower(c) && !unicode.IsDigit(c) { + return false + } + } + return true +} + +// IsValidUUID returns true if the provided string is a valid UUID string. +func IsValidUUID(str string) bool { + if _, err := uuid.Parse(str); err == nil { + return true + } + return false +} diff --git a/x/name/types/name_test.go b/x/name/types/name_test.go index b816ed3358..36ea8c41e5 100644 --- a/x/name/types/name_test.go +++ b/x/name/types/name_test.go @@ -2,11 +2,15 @@ package types import ( "fmt" + "strings" "testing" + "unicode" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/suite" ) type NameRecordTestSuite struct { @@ -19,6 +23,7 @@ func TestNameRecordSuite(t *testing.T) { s.addr = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) suite.Run(t, s) } + func (s *NameRecordTestSuite) TestNameRecordString() { nr := NewNameRecord("example", s.addr, true) s.Require().Equal(fmt.Sprintf("example: %s [restricted]", s.addr.String()), nr.String()) @@ -65,3 +70,247 @@ func (s *NameRecordTestSuite) TestNameRecordValidateBasic() { }) } } + +func TestNormalizeName(t *testing.T) { + tests := []struct { + name string + input string + exp string + }{ + {name: "empty string", input: "", exp: ""}, + {name: "two spaces", input: " ", exp: ""}, + {name: "with middle space", input: " a b ", exp: "a b"}, + {name: "upper case", input: "ABcDE", exp: "abcde"}, + {name: "space around first segment", input: " ghi .def.abc", exp: "ghi.def.abc"}, + {name: "space around middle segment", input: "ghi. def .abc", exp: "ghi.def.abc"}, + {name: "space around third segment", input: "ghi.def. abc ", exp: "ghi.def.abc"}, + {name: "middle segment has upper case", input: "ghi.DeF.abc", exp: "ghi.def.abc"}, + {name: "empty first segment", input: ".def.abc", exp: ".def.abc"}, + {name: "first segment is a space", input: " .def.abc", exp: ".def.abc"}, + {name: "empty last segment", input: "ghi.def.", exp: "ghi.def."}, + {name: "last segment is a space", input: "ghi.def. ", exp: "ghi.def."}, + {name: "empty middle segment", input: "ghi..abc", exp: "ghi..abc"}, + {name: "middle segment is a space", input: "ghi. .abc", exp: "ghi..abc"}, + {name: "middle segment is a bell", input: "ghi.\a.abc", exp: "ghi.\a.abc"}, + {name: "middle segment is three dashes", input: "ghi.---.abc", exp: "ghi.---.abc"}, + { + name: "middle segment is upper-case uuid", + input: "ghi.CFE48CAA-223E-44E1-8AD7-9181A30D4D91.abc", + exp: "ghi.cfe48caa-223e-44e1-8ad7-9181a30d4d91.abc", + }, + { + name: "middle segment is lower-case uuid with extra spaces around it", + input: "ghi. cfe48caa-223e-44e1-8ad7-9181a30d4d91 .abc", + exp: "ghi.cfe48caa-223e-44e1-8ad7-9181a30d4d91.abc", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + normalized := NormalizeName(tc.input) + assert.Equal(t, tc.exp, normalized, "NormalizeName(%q)", tc.input) + }) + } +} + +func TestIsValidName(t *testing.T) { + tests := []struct { + name string + input string + exp bool + }{ + {name: "empty string", input: "", exp: true}, + {name: "two spaces", input: " ", exp: true}, + {name: "with middle space", input: " a b ", exp: false}, + {name: "upper case", input: "ABcDE", exp: true}, + {name: "space around first segment", input: " ghi .def.abc", exp: true}, + {name: "space around middle segment", input: "ghi. def .abc", exp: true}, + {name: "space around third segment", input: "ghi.def. abc ", exp: true}, + {name: "middle segment has upper case", input: "ghi.DeF.abc", exp: true}, + {name: "empty first segment", input: ".def.abc", exp: true}, + {name: "first segment is a space", input: " .def.abc", exp: true}, + {name: "empty last segment", input: "ghi.def.", exp: true}, + {name: "last segment is a space", input: "ghi.def. ", exp: true}, + {name: "empty middle segment", input: "ghi..abc", exp: true}, + {name: "middle segment is a space", input: "ghi. .abc", exp: true}, + {name: "one segment two dashes", input: "a-b-c", exp: false}, + {name: "two segments one dash each", input: "a-1.b-2", exp: true}, + {name: "comma in first segment", input: "a,1.b-2", exp: false}, + {name: "comma in second segment", input: "a-1.b,2", exp: false}, + {name: "space in middle of first segment", input: "a 1.b-2", exp: false}, + {name: "space in middle of second segment", input: "a-1.b 2", exp: false}, + {name: "newline in middle of second segment", input: "a-1.b\n2", exp: false}, + {name: "middle segment has middlespace", input: "a. b c .d", exp: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ok := IsValidName(tc.input) + assert.Equal(t, tc.exp, ok, "IsValidName(%q)", tc.input) + }) + } +} + +func TestIsValidNameSegment(t *testing.T) { + tests := []struct { + name string + seg string + exp bool + }{ + {name: "empty", seg: "", exp: true}, + {name: "uuid with dashes", seg: "01234567-8909-8765-4321-012345678901", exp: true}, + {name: "uuid without dashes", seg: "01234567890987654321012345678901", exp: true}, + {name: "one dash", seg: "-", exp: true}, + {name: "two dashes", seg: "--", exp: false}, + {name: "all english lower-case letters, a dash, and all arabic digits", seg: "abcdefghijklmnopqrstuvwxyz-0123456789", exp: true}, + {name: "with a newline", seg: "ab\nde", exp: false}, + {name: "with a space", seg: "ab de", exp: false}, + {name: "with an underscore", seg: "ab_de", exp: false}, + {name: "single quoted", seg: "'abcde'", exp: false}, + {name: "double quoted", seg: `"abcde"`, exp: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ok := IsValidNameSegment(tc.seg) + assert.Equal(t, tc.exp, ok, "IsValidNameSegment(%q)", tc.seg) + }) + } +} + +func TestIsValidNameSegmentChars(t *testing.T) { + type testerFunc func(t *testing.T, r rune, tableName string, lo uint32, hi uint32, stride uint32) bool + assertRuneIsOkay := func(t *testing.T, r rune, tableName string, lo uint32, hi uint32, stride uint32) bool { + isGraphic := unicode.IsGraphic(r) + if !assert.True(t, isGraphic, "IsGraphic(%q = %u) %s{%u, %u, %d}", r, r, tableName, lo, hi, stride) { + return false + } + isValid := IsValidNameSegment(string(r)) + return assert.True(t, isValid, "IsValidNameSegment(%q = %u) %s{%u, %u, %d}", r, r, tableName, lo, hi, stride) + } + assertRuneIsInvalid := func(t *testing.T, r rune, tableName string, lo uint32, hi uint32, stride uint32) bool { + isValid := IsValidNameSegment(string(r)) + return assert.False(t, isValid, "IsValidNameSegment(%q = %u) %s{%u, %u, %d}", r, r, tableName, lo, hi, stride) + } + containsRune := func(r rune, rz []rune) bool { + for _, z := range rz { + if r == z { + return true + } + } + return false + } + + tests := []struct { + name string + table *unicode.RangeTable + tableName string + tester testerFunc + skips []rune + }{ + { + name: "all lower-case letters are okay", + table: unicode.Lower, + tableName: "Lower", + tester: assertRuneIsOkay, + }, + { + name: "all digits are okay", + table: unicode.Digit, + tableName: "Digit", + tester: assertRuneIsOkay, + }, + { + name: "no upper-case letters are allowed", + table: unicode.Upper, + tableName: "Upper", + tester: assertRuneIsInvalid, + }, + { + name: "most punctuation is not okay", + table: unicode.Punct, + tableName: "Punct", + tester: assertRuneIsInvalid, + skips: []rune{'-'}, + }, + { + name: "no control/other characters are allowed", + table: unicode.Other, + tableName: "Other", + tester: assertRuneIsInvalid, + }, + { + name: "no space characters are allowed", + table: unicode.Space, + tableName: "Space", + tester: assertRuneIsInvalid, + }, + { + name: "no marks are allowed", + table: unicode.Mark, + tableName: "Mark", + tester: assertRuneIsInvalid, + }, + { + name: "no symbols are allowed", + table: unicode.Symbol, + tableName: "Symbol", + tester: assertRuneIsInvalid, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + for _, row := range tc.table.R16 { + rv := row.Lo + for rv <= row.Hi { + r := rune(rv) + if !containsRune(r, tc.skips) { + tc.tester(t, r, tc.tableName, uint32(row.Lo), uint32(row.Hi), uint32(row.Stride)) + } + if rv+row.Stride <= rv { + break + } + rv += row.Stride + } + } + for _, row := range tc.table.R32 { + rv := row.Lo + for rv <= row.Hi { + r := rune(rv) + if !containsRune(r, tc.skips) { + tc.tester(t, r, tc.tableName, row.Lo, row.Hi, row.Stride) + } + if rv+row.Stride <= rv { + break + } + rv += row.Stride + } + } + }) + } +} + +func TestIsValidUUID(t *testing.T) { + tests := []struct { + name string + str string + exp bool + }{ + {name: "upper case", str: "4FEBAF0F-1BA7-473E-B62A-B1F3C44067C0", exp: true}, + {name: "lower case ", str: "4febaf0f-1ba7-473e-b62a-b1f3c44067c0", exp: true}, + {name: "no dashes", str: "4FEBAF0F1BA7473EB62AB1F3C44067C0", exp: true}, + {name: "empty", str: "", exp: false}, + {name: "whitespace", str: strings.Repeat(" ", 32), exp: false}, + {name: "one short ", str: "4febaf0f-1ba7-473e-b62a-b1f3c44067c", exp: false}, + {name: "bad char ", str: "4febaf0f-1ba7-473e-b62a-b1f3c44067cg", exp: false}, + {name: "missing a couple dashes", str: "4febaf0f-1ba7473eb62a-b1f3c44067c0", exp: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ok := IsValidUUID(tc.str) + assert.Equal(t, tc.exp, ok, "IsValidUUID(%q)", tc.str) + }) + } +} From 7e83f1bcfd068c0942dee9942ea1b356f96a86b9 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 24 Aug 2023 18:29:40 -0600 Subject: [PATCH 010/309] [1658]: Don't use the auto-stringer on the Fee ratio. --- proto/provenance/exchange/v1/market.proto | 1 + x/exchange/market.pb.go | 134 +++++++++++----------- 2 files changed, 68 insertions(+), 67 deletions(-) diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index 54bdd6093b..f7fce9c67e 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -92,6 +92,7 @@ message Market { // FeeRatio defines a ratio of price amount to fee amount. // For an order to be valid, its price must be evenly divisible by a FeeRatio's price. message FeeRatio { + option (gogoproto.goproto_stringer) = false; // price is the unit the order price is divided by to get how much of the fee should apply. cosmos.base.v1beta1.Coin price = 1 [(gogoproto.nullable) = false]; // fee is the amount to charge per price unit. diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index d4b3d4f40e..82e3b56b28 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -368,9 +368,8 @@ type FeeRatio struct { Fee types1.Coin `protobuf:"bytes,2,opt,name=fee,proto3" json:"fee"` } -func (m *FeeRatio) Reset() { *m = FeeRatio{} } -func (m *FeeRatio) String() string { return proto.CompactTextString(m) } -func (*FeeRatio) ProtoMessage() {} +func (m *FeeRatio) Reset() { *m = FeeRatio{} } +func (*FeeRatio) ProtoMessage() {} func (*FeeRatio) Descriptor() ([]byte, []int) { return fileDescriptor_d5cf198f1dd7e167, []int{3} } @@ -484,70 +483,71 @@ func init() { } var fileDescriptor_d5cf198f1dd7e167 = []byte{ - // 1007 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4f, 0x6f, 0xdb, 0xc6, - 0x13, 0x15, 0x2d, 0xff, 0x91, 0x56, 0xb6, 0xa3, 0xdf, 0x3a, 0xf1, 0x8f, 0x92, 0x0b, 0x89, 0x55, - 0x10, 0x40, 0x69, 0x61, 0xa9, 0x76, 0xd1, 0x4b, 0x6e, 0x92, 0x2d, 0x37, 0x02, 0x12, 0xc7, 0xa0, - 0x24, 0x04, 0xe8, 0x85, 0x5d, 0x92, 0x23, 0x79, 0x61, 0x8a, 0x54, 0x76, 0x97, 0x76, 0xdc, 0x5b, - 0x2f, 0x6d, 0xe0, 0x53, 0x8f, 0xbd, 0x18, 0x48, 0xaf, 0x3d, 0xf7, 0x43, 0xe4, 0x68, 0xf4, 0xd4, - 0x93, 0x5b, 0xd8, 0xd7, 0x9e, 0xfa, 0x09, 0x0a, 0xee, 0xd2, 0x12, 0xa3, 0xca, 0x4d, 0x81, 0xa6, - 0x27, 0x71, 0xe6, 0xbd, 0x9d, 0xf7, 0x66, 0x96, 0x1a, 0x10, 0xdd, 0x1f, 0xb1, 0xe0, 0x18, 0x7c, - 0xe2, 0x3b, 0x50, 0x87, 0x97, 0xce, 0x21, 0xf1, 0x07, 0x50, 0x3f, 0xde, 0xaa, 0x0f, 0x09, 0x3b, - 0x02, 0x51, 0x1b, 0xb1, 0x40, 0x04, 0x78, 0x7d, 0x42, 0xaa, 0xdd, 0x90, 0x6a, 0xc7, 0x5b, 0xc5, - 0x92, 0x13, 0xf0, 0x61, 0xc0, 0xeb, 0x24, 0x14, 0x87, 0xf5, 0xe3, 0x2d, 0x1b, 0x04, 0xd9, 0x92, - 0x81, 0x3a, 0x37, 0xc6, 0x6d, 0xc2, 0x61, 0x8c, 0x3b, 0x01, 0xf5, 0x63, 0xbc, 0xa0, 0x70, 0x4b, - 0x46, 0x75, 0x15, 0xc4, 0xd0, 0xdd, 0x41, 0x30, 0x08, 0x54, 0x3e, 0x7a, 0x52, 0xd9, 0xca, 0xef, - 0x1a, 0x5a, 0x79, 0x2a, 0x9d, 0x35, 0x1c, 0x27, 0x08, 0x7d, 0x81, 0xbf, 0x44, 0xcb, 0x51, 0x75, - 0x8b, 0xa8, 0x58, 0xd7, 0x0c, 0xad, 0x9a, 0xdb, 0x36, 0x6a, 0x71, 0x31, 0x69, 0x26, 0x56, 0xae, - 0x35, 0x09, 0x87, 0xf8, 0x5c, 0x73, 0xe3, 0xe2, 0xb2, 0xac, 0xfd, 0x71, 0x59, 0x5e, 0x3b, 0x25, - 0x43, 0xef, 0x51, 0x25, 0x59, 0xa3, 0x62, 0xe6, 0xec, 0x09, 0x13, 0x6f, 0xa0, 0xac, 0x1a, 0x86, - 0x45, 0x5d, 0x7d, 0xce, 0xd0, 0xaa, 0x2b, 0x66, 0x46, 0x25, 0xda, 0x2e, 0x36, 0xd1, 0x6a, 0x0c, - 0xba, 0x20, 0x08, 0xf5, 0xb8, 0x9e, 0x96, 0x06, 0x1e, 0xd4, 0x66, 0x8f, 0xac, 0xa6, 0xdc, 0xef, - 0x2a, 0x72, 0x73, 0xfe, 0xcd, 0x65, 0x39, 0x65, 0xae, 0x0c, 0x93, 0xc9, 0x47, 0x99, 0x57, 0xaf, - 0xcb, 0xa9, 0xef, 0x5f, 0x97, 0x53, 0x95, 0xaf, 0xc7, 0xed, 0xc6, 0x18, 0xc6, 0x68, 0xde, 0x27, - 0x43, 0x90, 0x6d, 0x66, 0x4d, 0xf9, 0x8c, 0x0d, 0x94, 0x73, 0x81, 0x3b, 0x8c, 0x8e, 0x04, 0x0d, - 0x7c, 0x69, 0x31, 0x6b, 0x26, 0x53, 0xb8, 0x8c, 0x72, 0x27, 0x60, 0x73, 0x2a, 0xc0, 0x0a, 0x99, - 0x27, 0x2d, 0x66, 0x4d, 0x14, 0xa7, 0x7a, 0xcc, 0xc3, 0x05, 0x94, 0xa1, 0x4e, 0xe0, 0x5b, 0x21, - 0xa3, 0xfa, 0xbc, 0x44, 0x97, 0xa2, 0xb8, 0xc7, 0x68, 0xe5, 0x22, 0x83, 0x16, 0x95, 0x87, 0xb7, - 0x27, 0xa1, 0xbd, 0x73, 0x12, 0x73, 0xff, 0x76, 0x12, 0xf8, 0x2b, 0xb4, 0xd6, 0x07, 0xb0, 0x1c, - 0x06, 0x44, 0x80, 0x45, 0xf8, 0x91, 0xd5, 0xf7, 0x88, 0xd0, 0xd3, 0x46, 0xba, 0x9a, 0xdb, 0x2e, - 0xdc, 0xdc, 0x71, 0x74, 0x59, 0xe3, 0x3b, 0xde, 0x09, 0xa8, 0xdf, 0xfc, 0x24, 0x2a, 0xf6, 0xe3, - 0xaf, 0xe5, 0xea, 0x80, 0x8a, 0xc3, 0xd0, 0xae, 0x39, 0xc1, 0x30, 0x7e, 0xbb, 0xe2, 0x9f, 0x4d, - 0xee, 0x1e, 0xd5, 0xc5, 0xe9, 0x08, 0xb8, 0x3c, 0xc0, 0xcd, 0x7c, 0x1f, 0x60, 0x47, 0xca, 0x34, - 0xf8, 0xd1, 0x9e, 0x47, 0xc4, 0x94, 0xb6, 0x4d, 0x5d, 0xa5, 0x3d, 0xff, 0x5f, 0x6a, 0x37, 0xa9, - 0x2b, 0xb5, 0x5f, 0x69, 0xa8, 0x18, 0x89, 0x73, 0x10, 0xc2, 0x83, 0x21, 0xf8, 0xc2, 0xe2, 0xe0, - 0x79, 0xc0, 0x94, 0x87, 0x85, 0xf7, 0xef, 0xe1, 0xff, 0x7d, 0x80, 0xce, 0x58, 0xad, 0x23, 0xc5, - 0xa4, 0x15, 0x82, 0x3e, 0x98, 0xed, 0x84, 0x11, 0x41, 0x03, 0xae, 0x2f, 0x4a, 0x2f, 0xc6, 0x6d, - 0x97, 0xbc, 0x07, 0x60, 0x46, 0x44, 0xb3, 0x30, 0x43, 0x40, 0x22, 0x1c, 0x7f, 0xa3, 0xa1, 0xc2, - 0x94, 0x86, 0x1d, 0x9e, 0xde, 0x34, 0xbb, 0xf4, 0xfe, 0x9b, 0x5d, 0x7f, 0xcb, 0x4b, 0x33, 0xd2, - 0x92, 0xbd, 0x5a, 0x68, 0x63, 0xa6, 0x8f, 0xb8, 0xd5, 0xcc, 0x3f, 0x6c, 0x55, 0xff, 0x6b, 0xf9, - 0xb8, 0xd3, 0x87, 0x28, 0x4f, 0x1c, 0x07, 0x46, 0x82, 0xfa, 0x03, 0x2b, 0x60, 0x2e, 0x30, 0xae, - 0x67, 0x0d, 0xad, 0x9a, 0x31, 0xef, 0x8c, 0xf3, 0xcf, 0x64, 0x1a, 0x6f, 0xa3, 0x7b, 0xc4, 0xf3, - 0x82, 0x13, 0x2b, 0xe4, 0xc0, 0x12, 0x96, 0x74, 0x24, 0xf9, 0x6b, 0x12, 0xec, 0x71, 0x60, 0x13, - 0x25, 0xfc, 0x18, 0xad, 0x44, 0x65, 0x38, 0xb7, 0x06, 0x8c, 0xf8, 0x82, 0xeb, 0x39, 0xe9, 0xf8, - 0xfe, 0x6d, 0x8e, 0x1b, 0x92, 0xfc, 0x79, 0xc4, 0x35, 0x97, 0xc9, 0x24, 0xe0, 0x78, 0x13, 0xad, - 0x31, 0x78, 0x61, 0x11, 0x21, 0x58, 0xe2, 0xdf, 0xa7, 0x2f, 0x1b, 0xe9, 0x6a, 0xd6, 0xcc, 0x33, - 0x78, 0xd1, 0x10, 0x82, 0x8d, 0xff, 0x2f, 0xb3, 0xe8, 0x36, 0x75, 0xf5, 0x95, 0x19, 0xf4, 0x26, - 0x75, 0x2b, 0x02, 0x65, 0x6e, 0x86, 0x85, 0x3f, 0x43, 0x0b, 0x23, 0x46, 0x1d, 0x88, 0x17, 0xf7, - 0xdf, 0xdc, 0xb3, 0xda, 0x10, 0x8a, 0x8d, 0xb7, 0x50, 0xba, 0x0f, 0x10, 0xaf, 0x98, 0x77, 0x1e, - 0x8a, 0xb8, 0x95, 0x6f, 0x35, 0x94, 0x4b, 0x74, 0x8c, 0xb7, 0xd1, 0x12, 0x71, 0x5d, 0x06, 0x9c, - 0xab, 0x6d, 0xda, 0xd4, 0x7f, 0xfe, 0x69, 0xf3, 0x6e, 0x5c, 0xa9, 0xa1, 0x90, 0x8e, 0x60, 0xd4, - 0x1f, 0x98, 0x37, 0x44, 0xbc, 0x8b, 0x72, 0x23, 0x60, 0x43, 0xca, 0x39, 0x0d, 0xfc, 0x68, 0xc3, - 0xa5, 0xab, 0xab, 0xdb, 0x95, 0xdb, 0xe6, 0x7b, 0x30, 0xa6, 0x9a, 0xc9, 0x63, 0x1f, 0xfd, 0x30, - 0x87, 0xd0, 0x04, 0xc3, 0x1f, 0xa3, 0xf5, 0x83, 0x96, 0xf9, 0xb4, 0xdd, 0xe9, 0xb4, 0x9f, 0xed, - 0x5b, 0xbd, 0xfd, 0xce, 0x41, 0x6b, 0xa7, 0xbd, 0xd7, 0x6e, 0xed, 0xe6, 0x53, 0xc5, 0x3b, 0x67, - 0xe7, 0x46, 0x2e, 0xf4, 0xf9, 0x08, 0x1c, 0xda, 0xa7, 0xe0, 0xe2, 0x0f, 0xd1, 0xff, 0x12, 0xe4, - 0x4e, 0xab, 0xdb, 0x7d, 0xd2, 0xca, 0x6b, 0x45, 0x74, 0x76, 0x6e, 0x2c, 0xaa, 0xb7, 0x64, 0x8a, - 0xb2, 0xd3, 0xd8, 0xdf, 0x69, 0x3d, 0xc9, 0xcf, 0x29, 0x8a, 0x13, 0x99, 0xf4, 0xf0, 0x03, 0xb4, - 0x96, 0xa0, 0x3c, 0x6f, 0x77, 0x1f, 0xef, 0x9a, 0x8d, 0xe7, 0xf9, 0x74, 0x71, 0xf9, 0xec, 0xdc, - 0xc8, 0x9c, 0x50, 0x71, 0xe8, 0x32, 0x72, 0x32, 0x55, 0xa9, 0x77, 0xb0, 0xdb, 0xe8, 0xb6, 0xf2, - 0xf3, 0xaa, 0x52, 0x38, 0x72, 0x89, 0x80, 0x29, 0xf3, 0x93, 0xc7, 0x4e, 0x7e, 0x41, 0x99, 0x4f, - 0x34, 0x8e, 0x1f, 0xa2, 0x7b, 0x09, 0x72, 0xa3, 0xdb, 0x35, 0xdb, 0xcd, 0x5e, 0xb7, 0xd5, 0xc9, - 0x2f, 0x16, 0x57, 0xcf, 0xce, 0x0d, 0x14, 0xbd, 0x40, 0xd4, 0x0e, 0x05, 0xf0, 0x26, 0xbc, 0xb9, - 0x2a, 0x69, 0x17, 0x57, 0x25, 0xed, 0xb7, 0xab, 0x92, 0xf6, 0xdd, 0x75, 0x29, 0x75, 0x71, 0x5d, - 0x4a, 0xfd, 0x72, 0x5d, 0x4a, 0xa1, 0x02, 0x0d, 0x6e, 0x19, 0xf8, 0x81, 0xf6, 0x45, 0x2d, 0xb1, - 0x04, 0x26, 0xa4, 0x4d, 0x1a, 0x24, 0xa2, 0xfa, 0xcb, 0xf1, 0x97, 0x8e, 0xbd, 0x28, 0xbf, 0x2b, - 0x3e, 0xfd, 0x33, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x1a, 0xfc, 0x68, 0x07, 0x09, 0x00, 0x00, + // 1010 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x41, 0x6f, 0x1a, 0x47, + 0x14, 0x66, 0x0d, 0xb6, 0x61, 0xb0, 0x1d, 0x3a, 0x4e, 0xdc, 0x05, 0x57, 0xb0, 0x25, 0x8a, 0x44, + 0x5a, 0x19, 0x6a, 0x57, 0xbd, 0xf8, 0x06, 0x36, 0x6e, 0x90, 0x12, 0xc7, 0x5a, 0x40, 0x91, 0x7a, + 0xd9, 0x0e, 0xbb, 0x0f, 0x3c, 0xf2, 0xb2, 0x4b, 0x66, 0x66, 0xed, 0x38, 0xb7, 0x5e, 0xda, 0xc8, + 0xa7, 0x1e, 0x7b, 0xb1, 0x94, 0x5e, 0x7b, 0xee, 0x8f, 0xc8, 0xd1, 0xea, 0xa9, 0x27, 0xb7, 0xb2, + 0xaf, 0x3d, 0xf5, 0x17, 0x54, 0x3b, 0xb3, 0x86, 0x0d, 0xc5, 0x4d, 0xa5, 0xa6, 0x27, 0xf6, 0xbd, + 0xef, 0x9b, 0xf7, 0x7d, 0xef, 0xcd, 0xf2, 0xb4, 0xe8, 0xfe, 0x88, 0xf9, 0xc7, 0xe0, 0x11, 0xcf, + 0x86, 0x1a, 0xbc, 0xb0, 0x0f, 0x89, 0x37, 0x80, 0xda, 0xf1, 0x66, 0x6d, 0x48, 0xd8, 0x11, 0x88, + 0xea, 0x88, 0xf9, 0xc2, 0xc7, 0x6b, 0x13, 0x52, 0xf5, 0x86, 0x54, 0x3d, 0xde, 0x2c, 0x14, 0x6d, + 0x9f, 0x0f, 0x7d, 0x5e, 0x23, 0x81, 0x38, 0xac, 0x1d, 0x6f, 0xf6, 0x40, 0x90, 0x4d, 0x19, 0xa8, + 0x73, 0x63, 0xbc, 0x47, 0x38, 0x8c, 0x71, 0xdb, 0xa7, 0x5e, 0x84, 0xe7, 0x15, 0x6e, 0xc9, 0xa8, + 0xa6, 0x82, 0x08, 0xba, 0x3b, 0xf0, 0x07, 0xbe, 0xca, 0x87, 0x4f, 0x2a, 0x5b, 0xfe, 0x43, 0x43, + 0xcb, 0x4f, 0xa4, 0xb3, 0xba, 0x6d, 0xfb, 0x81, 0x27, 0xf0, 0xd7, 0x68, 0x29, 0xac, 0x6e, 0x11, + 0x15, 0xeb, 0x9a, 0xa1, 0x55, 0xb2, 0x5b, 0x46, 0x35, 0x2a, 0x26, 0xcd, 0x44, 0xca, 0xd5, 0x06, + 0xe1, 0x10, 0x9d, 0x6b, 0xac, 0x5f, 0x5c, 0x96, 0xb4, 0x3f, 0x2f, 0x4b, 0xab, 0xa7, 0x64, 0xe8, + 0x6e, 0x97, 0xe3, 0x35, 0xca, 0x66, 0xb6, 0x37, 0x61, 0xe2, 0x75, 0x94, 0x51, 0xc3, 0xb0, 0xa8, + 0xa3, 0xcf, 0x19, 0x5a, 0x65, 0xd9, 0x4c, 0xab, 0x44, 0xcb, 0xc1, 0x26, 0x5a, 0x89, 0x40, 0x07, + 0x04, 0xa1, 0x2e, 0xd7, 0x93, 0xd2, 0xc0, 0x83, 0xea, 0xec, 0x91, 0x55, 0x95, 0xfb, 0x5d, 0x45, + 0x6e, 0xa4, 0xde, 0x5c, 0x96, 0x12, 0xe6, 0xf2, 0x30, 0x9e, 0xdc, 0x4e, 0xbf, 0x7a, 0x5d, 0x4a, + 0xfc, 0xf0, 0xba, 0x94, 0x28, 0x7f, 0x33, 0x6e, 0x37, 0xc2, 0x30, 0x46, 0x29, 0x8f, 0x0c, 0x41, + 0xb6, 0x99, 0x31, 0xe5, 0x33, 0x36, 0x50, 0xd6, 0x01, 0x6e, 0x33, 0x3a, 0x12, 0xd4, 0xf7, 0xa4, + 0xc5, 0x8c, 0x19, 0x4f, 0xe1, 0x12, 0xca, 0x9e, 0x40, 0x8f, 0x53, 0x01, 0x56, 0xc0, 0x5c, 0x69, + 0x31, 0x63, 0xa2, 0x28, 0xd5, 0x65, 0x2e, 0xce, 0xa3, 0x34, 0xb5, 0x7d, 0xcf, 0x0a, 0x18, 0xd5, + 0x53, 0x12, 0x5d, 0x0c, 0xe3, 0x2e, 0xa3, 0xe5, 0x8b, 0x34, 0x5a, 0x50, 0x1e, 0xde, 0x9e, 0x84, + 0xf6, 0xce, 0x49, 0xcc, 0xfd, 0xd7, 0x49, 0xe0, 0x97, 0x68, 0xb5, 0x0f, 0x60, 0xd9, 0x0c, 0x88, + 0x00, 0x8b, 0xf0, 0x23, 0xab, 0xef, 0x12, 0xa1, 0x27, 0x8d, 0x64, 0x25, 0xbb, 0x95, 0xbf, 0xb9, + 0xe3, 0xf0, 0xb2, 0xc6, 0x77, 0xbc, 0xe3, 0x53, 0xaf, 0xf1, 0x59, 0x58, 0xec, 0xa7, 0xdf, 0x4a, + 0x95, 0x01, 0x15, 0x87, 0x41, 0xaf, 0x6a, 0xfb, 0xc3, 0xe8, 0xed, 0x8a, 0x7e, 0x36, 0xb8, 0x73, + 0x54, 0x13, 0xa7, 0x23, 0xe0, 0xf2, 0x00, 0x37, 0x73, 0x7d, 0x80, 0x1d, 0x29, 0x53, 0xe7, 0x47, + 0x7b, 0x2e, 0x11, 0x53, 0xda, 0x3d, 0xea, 0x28, 0xed, 0xd4, 0xff, 0xa9, 0xdd, 0xa0, 0x8e, 0xd4, + 0x7e, 0xa5, 0xa1, 0x42, 0x28, 0xce, 0x41, 0x08, 0x17, 0x86, 0xe0, 0x09, 0x8b, 0x83, 0xeb, 0x02, + 0x53, 0x1e, 0xe6, 0xdf, 0xbf, 0x87, 0x0f, 0xfb, 0x00, 0xed, 0xb1, 0x5a, 0x5b, 0x8a, 0x49, 0x2b, + 0x04, 0x7d, 0x34, 0xdb, 0x09, 0x23, 0x82, 0xfa, 0x5c, 0x5f, 0x90, 0x5e, 0x8c, 0xdb, 0x2e, 0x79, + 0x0f, 0xc0, 0x0c, 0x89, 0x66, 0x7e, 0x86, 0x80, 0x44, 0x38, 0xfe, 0x56, 0x43, 0xf9, 0x29, 0x8d, + 0x5e, 0x70, 0x7a, 0xd3, 0xec, 0xe2, 0xfb, 0x6f, 0x76, 0xed, 0x2d, 0x2f, 0x8d, 0x50, 0x4b, 0xf6, + 0x6a, 0xa1, 0xf5, 0x99, 0x3e, 0xa2, 0x56, 0xd3, 0xff, 0xb2, 0x55, 0xfd, 0xef, 0xe5, 0xa3, 0x4e, + 0x1f, 0xa2, 0x1c, 0xb1, 0x6d, 0x18, 0x09, 0xea, 0x0d, 0x2c, 0x9f, 0x39, 0xc0, 0xb8, 0x9e, 0x31, + 0xb4, 0x4a, 0xda, 0xbc, 0x33, 0xce, 0x3f, 0x95, 0x69, 0xbc, 0x85, 0xee, 0x11, 0xd7, 0xf5, 0x4f, + 0xac, 0x80, 0x03, 0x8b, 0x59, 0xd2, 0x91, 0xe4, 0xaf, 0x4a, 0xb0, 0xcb, 0x81, 0x4d, 0x94, 0xf0, + 0x23, 0xb4, 0x1c, 0x96, 0xe1, 0xdc, 0x1a, 0x30, 0xe2, 0x09, 0xae, 0x67, 0xa5, 0xe3, 0xfb, 0xb7, + 0x39, 0xae, 0x4b, 0xf2, 0x97, 0x21, 0xd7, 0x5c, 0x22, 0x93, 0x80, 0xe3, 0x0d, 0xb4, 0xca, 0xe0, + 0xb9, 0x45, 0x84, 0x60, 0xb1, 0x7f, 0x9f, 0xbe, 0x64, 0x24, 0x2b, 0x19, 0x33, 0xc7, 0xe0, 0x79, + 0x5d, 0x08, 0x36, 0xfe, 0xbf, 0xcc, 0xa2, 0xf7, 0xa8, 0xa3, 0x2f, 0xcf, 0xa0, 0x37, 0xa8, 0x53, + 0x7e, 0x89, 0xd2, 0x37, 0xc3, 0xc2, 0x5f, 0xa0, 0xf9, 0x11, 0xa3, 0x36, 0x44, 0x8b, 0xfb, 0x1f, + 0xee, 0x59, 0x6d, 0x08, 0xc5, 0xc6, 0x9b, 0x28, 0xd9, 0x07, 0x88, 0x56, 0xcc, 0x3b, 0x0f, 0x85, + 0xdc, 0xed, 0x94, 0x5c, 0xa9, 0xdf, 0x69, 0x28, 0x1b, 0xeb, 0x1b, 0x6f, 0xa1, 0x45, 0xe2, 0x38, + 0x0c, 0x38, 0x57, 0x3b, 0xb5, 0xa1, 0xff, 0xf2, 0xf3, 0xc6, 0xdd, 0xa8, 0x5e, 0x5d, 0x21, 0x6d, + 0xc1, 0xa8, 0x37, 0x30, 0x6f, 0x88, 0x78, 0x17, 0x65, 0x47, 0xc0, 0x86, 0x94, 0x73, 0xea, 0x7b, + 0xe1, 0x9e, 0x4b, 0x56, 0x56, 0xb6, 0xca, 0xb7, 0x4d, 0xf9, 0x60, 0x4c, 0x35, 0xe3, 0xc7, 0x3e, + 0xf9, 0x71, 0x0e, 0xa1, 0x09, 0x86, 0x3f, 0x45, 0x6b, 0x07, 0x4d, 0xf3, 0x49, 0xab, 0xdd, 0x6e, + 0x3d, 0xdd, 0xb7, 0xba, 0xfb, 0xed, 0x83, 0xe6, 0x4e, 0x6b, 0xaf, 0xd5, 0xdc, 0xcd, 0x25, 0x0a, + 0x77, 0xce, 0xce, 0x8d, 0x6c, 0xe0, 0xf1, 0x11, 0xd8, 0xb4, 0x4f, 0xc1, 0xc1, 0x1f, 0xa3, 0x0f, + 0x62, 0xe4, 0x76, 0xb3, 0xd3, 0x79, 0xdc, 0xcc, 0x69, 0x05, 0x74, 0x76, 0x6e, 0x2c, 0xa8, 0x77, + 0x65, 0x8a, 0xb2, 0x53, 0xdf, 0xdf, 0x69, 0x3e, 0xce, 0xcd, 0x29, 0x8a, 0x1d, 0x9a, 0x74, 0xf1, + 0x03, 0xb4, 0x1a, 0xa3, 0x3c, 0x6b, 0x75, 0x1e, 0xed, 0x9a, 0xf5, 0x67, 0xb9, 0x64, 0x61, 0xe9, + 0xec, 0xdc, 0x48, 0x9f, 0x50, 0x71, 0xe8, 0x30, 0x72, 0x32, 0x55, 0xa9, 0x7b, 0xb0, 0x5b, 0xef, + 0x34, 0x73, 0x29, 0x55, 0x29, 0x18, 0x39, 0x44, 0xc0, 0x94, 0xf9, 0xc9, 0x63, 0x3b, 0x37, 0xaf, + 0xcc, 0xc7, 0x1a, 0xc7, 0x0f, 0xd1, 0xbd, 0x18, 0xb9, 0xde, 0xe9, 0x98, 0xad, 0x46, 0xb7, 0xd3, + 0x6c, 0xe7, 0x16, 0x0a, 0x2b, 0x67, 0xe7, 0x06, 0x0a, 0x5f, 0x23, 0xda, 0x0b, 0x04, 0xf0, 0x06, + 0xbc, 0xb9, 0x2a, 0x6a, 0x17, 0x57, 0x45, 0xed, 0xf7, 0xab, 0xa2, 0xf6, 0xfd, 0x75, 0x31, 0x71, + 0x71, 0x5d, 0x4c, 0xfc, 0x7a, 0x5d, 0x4c, 0xa0, 0x3c, 0xf5, 0x6f, 0x19, 0xf8, 0x81, 0xf6, 0x55, + 0x35, 0xb6, 0x0a, 0x26, 0xa4, 0x0d, 0xea, 0xc7, 0xa2, 0xda, 0x8b, 0xf1, 0xf7, 0x4e, 0x6f, 0x41, + 0x7e, 0x5d, 0x7c, 0xfe, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x91, 0xc0, 0x12, 0x3a, 0x0d, 0x09, + 0x00, 0x00, } func (m *MarketAccount) Marshal() (dAtA []byte, err error) { From 749d9059f8b1c8b1cee48463e0f173dbf8949240 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 24 Aug 2023 19:10:04 -0600 Subject: [PATCH 011/309] [1658]: Make the seller settlement fees coin nullable since it's not required. --- proto/provenance/exchange/v1/orders.proto | 2 +- x/exchange/orders.pb.go | 99 ++++++++++++----------- 2 files changed, 54 insertions(+), 47 deletions(-) diff --git a/proto/provenance/exchange/v1/orders.proto b/proto/provenance/exchange/v1/orders.proto index bdcb1db839..a53d4d7606 100644 --- a/proto/provenance/exchange/v1/orders.proto +++ b/proto/provenance/exchange/v1/orders.proto @@ -40,7 +40,7 @@ message AskOrder { // seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. If this denom is the // same denom as the price, it will come out of the actual price received. If this denom is different, the amount must // be in the seller's account and a hold is placed on it until the order is filled or cancelled. - cosmos.base.v1beta1.Coin seller_settlement_flat_fee = 5 [(gogoproto.nullable) = false]; + cosmos.base.v1beta1.Coin seller_settlement_flat_fee = 5; // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the // order must be either filled in full or not filled at all. bool allow_partial = 6; diff --git a/x/exchange/orders.pb.go b/x/exchange/orders.pb.go index 9cb459842c..e01dfc4d42 100644 --- a/x/exchange/orders.pb.go +++ b/x/exchange/orders.pb.go @@ -139,7 +139,7 @@ type AskOrder struct { // seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. If this denom is the // same denom as the price, it will come out of the actual price received. If this denom is different, the amount must // be in the seller's account and a hold is placed on it until the order is filled or cancelled. - SellerSettlementFlatFee types.Coin `protobuf:"bytes,5,opt,name=seller_settlement_flat_fee,json=sellerSettlementFlatFee,proto3" json:"seller_settlement_flat_fee"` + SellerSettlementFlatFee *types.Coin `protobuf:"bytes,5,opt,name=seller_settlement_flat_fee,json=sellerSettlementFlatFee,proto3" json:"seller_settlement_flat_fee,omitempty"` // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the // order must be either filled in full or not filled at all. AllowPartial bool `protobuf:"varint,6,opt,name=allow_partial,json=allowPartial,proto3" json:"allow_partial,omitempty"` @@ -206,11 +206,11 @@ func (m *AskOrder) GetPrice() types.Coin { return types.Coin{} } -func (m *AskOrder) GetSellerSettlementFlatFee() types.Coin { +func (m *AskOrder) GetSellerSettlementFlatFee() *types.Coin { if m != nil { return m.SellerSettlementFlatFee } - return types.Coin{} + return nil } func (m *AskOrder) GetAllowPartial() bool { @@ -326,39 +326,39 @@ func init() { var fileDescriptor_dab7cbe63f582471 = []byte{ // 521 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0x31, 0x6f, 0xd3, 0x4c, - 0x18, 0x8e, 0x9b, 0x38, 0x75, 0xae, 0x5f, 0x17, 0x7f, 0x05, 0x9c, 0x20, 0xb9, 0x51, 0xba, 0x78, - 0xc9, 0xb9, 0x29, 0x62, 0x46, 0x35, 0x52, 0x45, 0x27, 0x2a, 0x77, 0x43, 0x48, 0xd6, 0xd9, 0x7e, - 0xeb, 0x9e, 0xe2, 0xf8, 0x2c, 0xdf, 0x35, 0xb4, 0x13, 0x7f, 0x81, 0x95, 0xbf, 0x80, 0xc4, 0xc6, - 0x8f, 0xe8, 0x58, 0x31, 0x31, 0x01, 0x4a, 0xfe, 0x04, 0x23, 0xf2, 0xdd, 0x25, 0x0d, 0x12, 0x44, - 0x19, 0x18, 0x98, 0x7c, 0xef, 0x7b, 0xcf, 0xfb, 0x3c, 0x8f, 0x9e, 0x57, 0x3e, 0x74, 0x50, 0x56, - 0x6c, 0x0a, 0x05, 0x29, 0x12, 0xf0, 0xe1, 0x3a, 0xb9, 0x24, 0x45, 0x06, 0xfe, 0x74, 0xe4, 0xb3, - 0x2a, 0x85, 0x8a, 0xe3, 0xb2, 0x62, 0x82, 0xd9, 0x0f, 0xef, 0x41, 0x78, 0x01, 0xc2, 0xd3, 0x51, - 0xcf, 0x4d, 0x18, 0x9f, 0x30, 0xee, 0xc7, 0x84, 0xd7, 0x43, 0x31, 0x08, 0x32, 0xf2, 0x13, 0x46, - 0x0b, 0x35, 0xd7, 0xeb, 0xaa, 0xfb, 0x48, 0x56, 0xbe, 0x2a, 0xf4, 0xd5, 0x5e, 0xc6, 0x32, 0xa6, - 0xfa, 0xf5, 0x49, 0x75, 0x07, 0x1f, 0x0d, 0x64, 0xbe, 0xac, 0x95, 0xed, 0x2e, 0xb2, 0xa4, 0x85, - 0x88, 0xa6, 0x8e, 0xd1, 0x37, 0xbc, 0x56, 0xb8, 0x2d, 0xeb, 0xd3, 0xd4, 0x7e, 0x86, 0x3a, 0x84, - 0x8f, 0x23, 0x59, 0x3a, 0x5b, 0x7d, 0xc3, 0xdb, 0x39, 0xea, 0xe3, 0xdf, 0x3b, 0xc4, 0xc7, 0x7c, - 0x2c, 0xf9, 0x5e, 0x34, 0x42, 0x8b, 0xe8, 0x73, 0x4d, 0x10, 0xd3, 0x54, 0x13, 0x34, 0xd7, 0x13, - 0x04, 0x34, 0x5d, 0x12, 0xc4, 0xfa, 0x1c, 0x6c, 0x23, 0x53, 0x0e, 0x0f, 0x7e, 0x6c, 0x21, 0x6b, - 0x21, 0x61, 0x3f, 0x46, 0x9d, 0x09, 0xa9, 0xc6, 0x20, 0x16, 0x9e, 0x77, 0x43, 0x4b, 0x35, 0x4e, - 0x53, 0xfb, 0x10, 0xb5, 0x39, 0xe4, 0xb9, 0x76, 0xdc, 0x09, 0x9c, 0xcf, 0x9f, 0x86, 0x7b, 0x3a, - 0x91, 0xe3, 0x34, 0xad, 0x80, 0xf3, 0x73, 0x51, 0xd1, 0x22, 0x0b, 0x35, 0xce, 0x4e, 0x50, 0x9b, - 0x70, 0x0e, 0x82, 0x3b, 0xcd, 0x7e, 0xd3, 0xdb, 0x39, 0xea, 0x62, 0x0d, 0xaf, 0xd3, 0xc6, 0x3a, - 0x6d, 0xfc, 0x9c, 0xd1, 0x22, 0x38, 0xbc, 0xfd, 0xba, 0xdf, 0xf8, 0xf0, 0x6d, 0xdf, 0xcb, 0xa8, - 0xb8, 0xbc, 0x8a, 0x71, 0xc2, 0x26, 0x3a, 0x6d, 0xfd, 0x19, 0xf2, 0x74, 0xec, 0x8b, 0x9b, 0x12, - 0xb8, 0x1c, 0xe0, 0xa1, 0xa6, 0xb6, 0x9f, 0x22, 0xb3, 0xac, 0x68, 0x02, 0x4e, 0x4b, 0xc6, 0xb0, - 0x46, 0xa3, 0x55, 0x6b, 0x84, 0x0a, 0x6d, 0xbf, 0x46, 0x3d, 0xe5, 0x32, 0xe2, 0x20, 0x44, 0x0e, - 0x13, 0x28, 0x44, 0x74, 0x91, 0x13, 0x11, 0x5d, 0x00, 0x38, 0xe6, 0x66, 0x5c, 0x8f, 0x14, 0xc5, - 0xf9, 0x92, 0xe1, 0x24, 0x27, 0xe2, 0x04, 0xc0, 0x3e, 0x40, 0xbb, 0x24, 0xcf, 0xd9, 0x9b, 0xa8, - 0x24, 0x95, 0xa0, 0x24, 0x77, 0xda, 0x7d, 0xc3, 0xb3, 0xc2, 0xff, 0x64, 0xf3, 0x4c, 0xf5, 0x06, - 0xef, 0x9b, 0xc8, 0x5a, 0x2c, 0x67, 0x7d, 0xf4, 0x18, 0x99, 0xf1, 0xd5, 0xcd, 0x06, 0xc9, 0x2b, - 0xd8, 0x3f, 0x1d, 0xfc, 0x5b, 0xf4, 0x40, 0x9a, 0xfc, 0x25, 0x77, 0x00, 0xee, 0x98, 0x7f, 0xdf, - 0xea, 0xff, 0x52, 0x69, 0x65, 0x3d, 0x00, 0x7c, 0xa3, 0xdd, 0x04, 0x70, 0x3b, 0x73, 0x8d, 0xbb, - 0x99, 0x6b, 0x7c, 0x9f, 0xb9, 0xc6, 0xbb, 0xb9, 0xdb, 0xb8, 0x9b, 0xbb, 0x8d, 0x2f, 0x73, 0xb7, - 0x81, 0xba, 0x94, 0xfd, 0xe1, 0x4f, 0x3b, 0x33, 0x5e, 0xe1, 0x15, 0x6b, 0xf7, 0xa0, 0x21, 0x65, - 0x2b, 0x95, 0x7f, 0xbd, 0x7c, 0xa6, 0xe2, 0xb6, 0x7c, 0x34, 0x9e, 0xfc, 0x0c, 0x00, 0x00, 0xff, - 0xff, 0xa4, 0x50, 0xc3, 0x4d, 0xc4, 0x04, 0x00, 0x00, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xb1, 0x6e, 0xd3, 0x40, + 0x18, 0x8e, 0x9b, 0x38, 0x75, 0xae, 0x74, 0x31, 0x05, 0x9c, 0x20, 0xb9, 0x51, 0xba, 0x78, 0xc9, + 0xb9, 0x29, 0x62, 0x46, 0x35, 0x52, 0x45, 0x27, 0x2a, 0x57, 0x62, 0x60, 0xb1, 0xce, 0xf6, 0x5f, + 0xf7, 0x14, 0xc7, 0x67, 0xf9, 0xae, 0xa1, 0x9d, 0x78, 0x05, 0x56, 0x5e, 0x01, 0x89, 0x8d, 0x87, + 0xe8, 0x58, 0x31, 0x31, 0x01, 0x4a, 0xde, 0x80, 0x27, 0x40, 0xbe, 0xbb, 0xa4, 0x41, 0x82, 0xd0, + 0x81, 0x81, 0xc9, 0xf7, 0xff, 0xf7, 0x7d, 0xdf, 0xff, 0xe9, 0xfb, 0xe5, 0x43, 0x7b, 0x65, 0xc5, + 0xa6, 0x50, 0x90, 0x22, 0x01, 0x1f, 0x2e, 0x93, 0x73, 0x52, 0x64, 0xe0, 0x4f, 0x47, 0x3e, 0xab, + 0x52, 0xa8, 0x38, 0x2e, 0x2b, 0x26, 0x98, 0xfd, 0xf0, 0x16, 0x84, 0x17, 0x20, 0x3c, 0x1d, 0xf5, + 0xdc, 0x84, 0xf1, 0x09, 0xe3, 0x7e, 0x4c, 0x78, 0x4d, 0x8a, 0x41, 0x90, 0x91, 0x9f, 0x30, 0x5a, + 0x28, 0x5e, 0xaf, 0xab, 0xee, 0x23, 0x59, 0xf9, 0xaa, 0xd0, 0x57, 0x3b, 0x19, 0xcb, 0x98, 0xea, + 0xd7, 0x27, 0xd5, 0x1d, 0x7c, 0x34, 0x90, 0xf9, 0xb2, 0x9e, 0x6c, 0x77, 0x91, 0x25, 0x2d, 0x44, + 0x34, 0x75, 0x8c, 0xbe, 0xe1, 0xb5, 0xc2, 0x4d, 0x59, 0x1f, 0xa7, 0xf6, 0x33, 0xd4, 0x21, 0x7c, + 0x1c, 0xc9, 0xd2, 0xd9, 0xe8, 0x1b, 0xde, 0xd6, 0x41, 0x1f, 0xff, 0xde, 0x21, 0x3e, 0xe4, 0x63, + 0xa9, 0xf7, 0xa2, 0x11, 0x5a, 0x44, 0x9f, 0x6b, 0x81, 0x98, 0xa6, 0x5a, 0xa0, 0xb9, 0x5e, 0x20, + 0xa0, 0xe9, 0x52, 0x20, 0xd6, 0xe7, 0x60, 0x13, 0x99, 0x92, 0x3c, 0xf8, 0xb1, 0x81, 0xac, 0xc5, + 0x08, 0xfb, 0x31, 0xea, 0x4c, 0x48, 0x35, 0x06, 0xb1, 0xf0, 0xbc, 0x1d, 0x5a, 0xaa, 0x71, 0x9c, + 0xda, 0xfb, 0xa8, 0xcd, 0x21, 0xcf, 0xb5, 0xe3, 0x4e, 0xe0, 0x7c, 0xfe, 0x34, 0xdc, 0xd1, 0x89, + 0x1c, 0xa6, 0x69, 0x05, 0x9c, 0x9f, 0x8a, 0x8a, 0x16, 0x59, 0xa8, 0x71, 0x76, 0x82, 0xda, 0x84, + 0x73, 0x10, 0xdc, 0x69, 0xf6, 0x9b, 0xde, 0xd6, 0x41, 0x17, 0x6b, 0x78, 0x9d, 0x36, 0xd6, 0x69, + 0xe3, 0xe7, 0x8c, 0x16, 0xc1, 0xfe, 0xf5, 0xd7, 0xdd, 0xc6, 0x87, 0x6f, 0xbb, 0x5e, 0x46, 0xc5, + 0xf9, 0x45, 0x8c, 0x13, 0x36, 0xd1, 0x69, 0xeb, 0xcf, 0x90, 0xa7, 0x63, 0x5f, 0x5c, 0x95, 0xc0, + 0x25, 0x81, 0x87, 0x5a, 0xda, 0x7e, 0x8a, 0xcc, 0xb2, 0xa2, 0x09, 0x38, 0x2d, 0x19, 0xc3, 0x9a, + 0x19, 0xad, 0x7a, 0x46, 0xa8, 0xd0, 0xf6, 0x2b, 0xd4, 0x53, 0x2e, 0x23, 0x0e, 0x42, 0xe4, 0x30, + 0x81, 0x42, 0x44, 0x67, 0x39, 0x11, 0xd1, 0x19, 0x80, 0x63, 0xfe, 0x45, 0x2b, 0x7c, 0xa4, 0xc8, + 0xa7, 0x4b, 0xee, 0x51, 0x4e, 0xc4, 0x11, 0x80, 0xbd, 0x87, 0xb6, 0x49, 0x9e, 0xb3, 0x37, 0x51, + 0x49, 0x2a, 0x41, 0x49, 0xee, 0xb4, 0xfb, 0x86, 0x67, 0x85, 0xf7, 0x64, 0xf3, 0x44, 0xf5, 0x06, + 0xef, 0x9b, 0xc8, 0x5a, 0xac, 0x65, 0x7d, 0xe8, 0x18, 0x99, 0xf1, 0xc5, 0xd5, 0x1d, 0x32, 0x57, + 0xb0, 0xff, 0x3a, 0xf2, 0xb7, 0xe8, 0x81, 0x34, 0xf9, 0x4b, 0xe2, 0x00, 0xdc, 0x31, 0xff, 0xbd, + 0xd5, 0xfb, 0x72, 0xd2, 0xca, 0x7a, 0x00, 0xf8, 0x9d, 0x76, 0x13, 0xc0, 0xf5, 0xcc, 0x35, 0x6e, + 0x66, 0xae, 0xf1, 0x7d, 0xe6, 0x1a, 0xef, 0xe6, 0x6e, 0xe3, 0x66, 0xee, 0x36, 0xbe, 0xcc, 0xdd, + 0x06, 0xea, 0x52, 0xf6, 0x87, 0x7f, 0xec, 0xc4, 0x78, 0x8d, 0x57, 0xac, 0xdd, 0x82, 0x86, 0x94, + 0xad, 0x54, 0xfe, 0xe5, 0xf2, 0x81, 0x8a, 0xdb, 0xf2, 0xb9, 0x78, 0xf2, 0x33, 0x00, 0x00, 0xff, + 0xff, 0x9b, 0xd7, 0x7f, 0xac, 0xbe, 0x04, 0x00, 0x00, } func (m *Order) Marshal() (dAtA []byte, err error) { @@ -470,16 +470,18 @@ func (m *AskOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x30 } - { - size, err := m.SellerSettlementFlatFee.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err + if m.SellerSettlementFlatFee != nil { + { + size, err := m.SellerSettlementFlatFee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) } - i -= size - i = encodeVarintOrders(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a } - i-- - dAtA[i] = 0x2a { size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -673,8 +675,10 @@ func (m *AskOrder) Size() (n int) { } l = m.Price.Size() n += 1 + l + sovOrders(uint64(l)) - l = m.SellerSettlementFlatFee.Size() - n += 1 + l + sovOrders(uint64(l)) + if m.SellerSettlementFlatFee != nil { + l = m.SellerSettlementFlatFee.Size() + n += 1 + l + sovOrders(uint64(l)) + } if m.AllowPartial { n += 2 } @@ -1035,6 +1039,9 @@ func (m *AskOrder) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } + if m.SellerSettlementFlatFee == nil { + m.SellerSettlementFlatFee = &types.Coin{} + } if err := m.SellerSettlementFlatFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } From 7a7ca9244c7b14ac89364374f96a83c5116c4234 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 24 Aug 2023 19:31:54 -0600 Subject: [PATCH 012/309] [1658]: Get going on the types. Get most of the type validation done (but untested). --- x/exchange/events.go | 106 ++++++++++++ x/exchange/events_test.go | 216 ++++++++++++++++++++++++ x/exchange/genesis.go | 42 +++++ x/exchange/genesis_test.go | 7 + x/exchange/market.go | 334 +++++++++++++++++++++++++++++++++++++ x/exchange/market_test.go | 33 ++++ x/exchange/orders.go | 202 ++++++++++++++++++++++ x/exchange/orders_test.go | 303 +++++++++++++++++++++++++++++++++ x/exchange/params.go | 66 ++++++++ x/exchange/params_test.go | 231 +++++++++++++++++++++++++ 10 files changed, 1540 insertions(+) create mode 100644 x/exchange/events.go create mode 100644 x/exchange/events_test.go create mode 100644 x/exchange/genesis.go create mode 100644 x/exchange/genesis_test.go create mode 100644 x/exchange/market.go create mode 100644 x/exchange/market_test.go create mode 100644 x/exchange/orders.go create mode 100644 x/exchange/orders_test.go create mode 100644 x/exchange/params.go create mode 100644 x/exchange/params_test.go diff --git a/x/exchange/events.go b/x/exchange/events.go new file mode 100644 index 0000000000..67bc0bccf7 --- /dev/null +++ b/x/exchange/events.go @@ -0,0 +1,106 @@ +package exchange + +import sdk "github.com/cosmos/cosmos-sdk/types" + +func NewEventOrderCreated(order *Order) *EventOrderCreated { + return &EventOrderCreated{ + OrderId: order.GetOrderId(), + OrderType: order.OrderType(), + } +} + +func NewEventOrderCancelled(orderID uint64, cancelledBy sdk.AccAddress) *EventOrderCancelled { + return &EventOrderCancelled{ + OrderId: orderID, + CancelledBy: cancelledBy.String(), + } +} + +func NewEventOrderFilled(orderID uint64) *EventOrderFilled { + return &EventOrderFilled{ + OrderId: orderID, + } +} + +func NewEventOrderPartiallyFilled(orderID uint64, assetsFilled, feesFilled sdk.Coins) *EventOrderPartiallyFilled { + return &EventOrderPartiallyFilled{ + OrderId: orderID, + AssetsFilled: assetsFilled.String(), + FeesFilled: feesFilled.String(), + } +} + +func NewEventMarketWithdraw(marketID uint32, amountWithdrawn sdk.Coins, destination, withdrawnBy sdk.AccAddress) *EventMarketWithdraw { + return &EventMarketWithdraw{ + MarketId: marketID, + AmountWithdrawn: amountWithdrawn.String(), + Destination: destination.String(), + WithdrawnBy: withdrawnBy.String(), + } +} + +func NewEventMarketDetailsUpdated(marketID uint32, updatedBy sdk.AccAddress) *EventMarketDetailsUpdated { + return &EventMarketDetailsUpdated{ + MarketId: marketID, + UpdatedBy: updatedBy.String(), + } +} + +func NewEventMarketEnabled(marketID uint32, updatedBy sdk.AccAddress) *EventMarketEnabled { + return &EventMarketEnabled{ + MarketId: marketID, + UpdatedBy: updatedBy.String(), + } +} + +func NewEventMarketDisabled(marketID uint32, updatedBy sdk.AccAddress) *EventMarketDisabled { + return &EventMarketDisabled{ + MarketId: marketID, + UpdatedBy: updatedBy.String(), + } +} + +func NewEventMarketUserSettleUpdated(marketID uint32, updatedBy sdk.AccAddress) *EventMarketUserSettleUpdated { + return &EventMarketUserSettleUpdated{ + MarketId: marketID, + UpdatedBy: updatedBy.String(), + } +} + +func NewEventMarketPermissionsUpdated(marketID uint32, updatedBy sdk.AccAddress) *EventMarketPermissionsUpdated { + return &EventMarketPermissionsUpdated{ + MarketId: marketID, + UpdatedBy: updatedBy.String(), + } +} + +func NewEventMarketReqAttrUpdated(marketID uint32, updatedBy sdk.AccAddress) *EventMarketReqAttrUpdated { + return &EventMarketReqAttrUpdated{ + MarketId: marketID, + UpdatedBy: updatedBy.String(), + } +} + +func NewEventCreateMarketSubmitted(marketID uint32, proposalID uint64, submittedBy sdk.AccAddress) *EventCreateMarketSubmitted { + return &EventCreateMarketSubmitted{ + MarketId: marketID, + ProposalId: proposalID, + SubmittedBy: submittedBy.String(), + } +} + +func NewEventMarketCreated(marketID uint32) *EventMarketCreated { + return &EventMarketCreated{ + MarketId: marketID, + } +} + +func NewEventMarketFeesUpdated(marketID uint32) *EventMarketFeesUpdated { + return &EventMarketFeesUpdated{ + MarketId: marketID, + } +} + +func NewEventParamsUpdated() *EventParamsUpdated { + return &EventParamsUpdated{} +} diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go new file mode 100644 index 0000000000..69c4dfc6e7 --- /dev/null +++ b/x/exchange/events_test.go @@ -0,0 +1,216 @@ +package exchange + +import ( + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// assertEverythingSet asserts that the provided proto.Message has a value for all fields. +// Returns true on success, false if one or more things are missing. +func assertEverythingSet(t *testing.T, tev proto.Message, typeString string) bool { + t.Helper() + event, err := sdk.TypedEventToEvent(tev) + if !assert.NoError(t, err, "TypedEventToEvent(%T)", tev) { + return false + } + + expType := "provenance.exchange.v1." + typeString + rv := assert.Equal(t, expType, event.Type, "%T event.Type") + for i, attrs := range event.Attributes { + rv = assert.NotEmpty(t, attrs.Key, "%T event.attributes[%d].Key", tev, i) && rv + rv = assert.NotEqual(t, `""`, attrs.Key, "%T event.attributes[%d].Key", tev, i) && rv + rv = assert.NotEmpty(t, attrs.Value, "%T event.attributes[%d].Value", tev, i) && rv + rv = assert.NotEqual(t, `""`, attrs.Value, "%T event.attributes[%d].Value", tev, i) && rv + } + return rv +} + +func TestNewEventOrderCreated(t *testing.T) { + tests := []struct { + name string + order *Order + expected *EventOrderCreated + expPanic interface{} + }{ + { + name: "nil order", + order: nil, + expPanic: "OrderType() missing case for ", + }, + { + name: "order with ask", + order: NewOrder(1).WithAsk(&AskOrder{}), + expected: &EventOrderCreated{ + OrderId: 1, + OrderType: "ask", + }, + }, + { + name: "order with bid", + order: NewOrder(2).WithBid(&BidOrder{}), + expected: &EventOrderCreated{ + OrderId: 2, + OrderType: "bid", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual *EventOrderCreated + testFunc := func() { + actual = NewEventOrderCreated(tc.order) + } + + if tc.expPanic != nil { + require.PanicsWithValue(t, tc.expPanic, testFunc, "NewEventOrderCreated") + } else { + require.NotPanics(t, testFunc, "NewEventOrderCreated") + assert.Equal(t, tc.expected, actual, "NewEventOrderCreated result") + assertEverythingSet(t, actual, "EventOrderCreated") + } + }) + } +} + +func TestNewEventOrderCancelled(t *testing.T) { + orderID := uint64(101) + cancelledBy := sdk.AccAddress("cancelledBy_________") + + event := NewEventOrderCancelled(orderID, cancelledBy) + assert.Equal(t, orderID, event.OrderId, "OrderId") + assert.Equal(t, cancelledBy.String(), event.CancelledBy, "CancelledBy") + assertEverythingSet(t, event, "EventOrderCancelled") +} + +func TestNewEventOrderFilled(t *testing.T) { + orderID := uint64(5) + + event := NewEventOrderFilled(orderID) + assert.Equal(t, orderID, event.OrderId, "OrderId") + assertEverythingSet(t, event, "EventOrderFilled") +} + +func TestNewEventOrderPartiallyFilled(t *testing.T) { + orderID := uint64(18) + assetsFilled := sdk.NewCoins(sdk.NewInt64Coin("first", 111), sdk.NewInt64Coin("second", 22)) + feesFilled := sdk.NewCoins(sdk.NewInt64Coin("charge", 8), sdk.NewInt64Coin("fee", 15)) + + event := NewEventOrderPartiallyFilled(orderID, assetsFilled, feesFilled) + assert.Equal(t, orderID, event.OrderId, "OrderId") + assert.Equal(t, assetsFilled.String(), event.AssetsFilled, "AssetsFilled") + assert.Equal(t, feesFilled.String(), event.FeesFilled, "FeesFilled") + assertEverythingSet(t, event, "EventOrderPartiallyFilled") +} + +func TestNewEventMarketWithdraw(t *testing.T) { + marketID := uint32(55) + amountWithdrawn := sdk.NewCoins(sdk.NewInt64Coin("mine", 188382), sdk.NewInt64Coin("yours", 3)) + destination := sdk.AccAddress("destination_________") + withdrawnBy := sdk.AccAddress("withdrawnBy_________") + + event := NewEventMarketWithdraw(marketID, amountWithdrawn, destination, withdrawnBy) + assert.Equal(t, marketID, event.MarketId, "MarketId") + assert.Equal(t, amountWithdrawn.String(), event.AmountWithdrawn, "AmountWithdrawn") + assert.Equal(t, destination.String(), event.Destination, "Destination") + assert.Equal(t, withdrawnBy.String(), event.WithdrawnBy, "WithdrawnBy") + assertEverythingSet(t, event, "EventMarketWithdraw") +} + +func TestNewEventMarketDetailsUpdated(t *testing.T) { + marketID := uint32(84) + updatedBy := sdk.AccAddress("updatedBy___________") + + event := NewEventMarketDetailsUpdated(marketID, updatedBy) + assert.Equal(t, marketID, event.MarketId, "MarketId") + assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") + assertEverythingSet(t, event, "EventMarketDetailsUpdated") +} + +func TestNewEventMarketEnabled(t *testing.T) { + marketID := uint32(919) + updatedBy := sdk.AccAddress("updatedBy___________") + + event := NewEventMarketEnabled(marketID, updatedBy) + assert.Equal(t, marketID, event.MarketId, "MarketId") + assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") + assertEverythingSet(t, event, "EventMarketEnabled") +} + +func TestNewEventMarketDisabled(t *testing.T) { + marketID := uint32(5555) + updatedBy := sdk.AccAddress("updatedBy___________") + + event := NewEventMarketDisabled(marketID, updatedBy) + assert.Equal(t, marketID, event.MarketId, "MarketId") + assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") + assertEverythingSet(t, event, "EventMarketDisabled") +} + +func TestNewEventMarketUserSettleUpdated(t *testing.T) { + marketID := uint32(123) + updatedBy := sdk.AccAddress("updatedBy___________") + + event := NewEventMarketUserSettleUpdated(marketID, updatedBy) + assert.Equal(t, marketID, event.MarketId, "MarketId") + assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") + assertEverythingSet(t, event, "EventMarketUserSettleUpdated") +} + +func TestNewEventMarketPermissionsUpdated(t *testing.T) { + marketID := uint32(5432) + updatedBy := sdk.AccAddress("updatedBy___________") + + event := NewEventMarketPermissionsUpdated(marketID, updatedBy) + assert.Equal(t, marketID, event.MarketId, "MarketId") + assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") + assertEverythingSet(t, event, "EventMarketPermissionsUpdated") +} + +func TestNewEventMarketReqAttrUpdated(t *testing.T) { + marketID := uint32(3334) + updatedBy := sdk.AccAddress("updatedBy___________") + + event := NewEventMarketReqAttrUpdated(marketID, updatedBy) + assert.Equal(t, marketID, event.MarketId, "MarketId") + assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") + assertEverythingSet(t, event, "EventMarketReqAttrUpdated") +} + +func TestNewEventCreateMarketSubmitted(t *testing.T) { + marketID := uint32(5445) + proposalID := uint64(88888) + submittedBy := sdk.AccAddress("submittedBy_________") + + event := NewEventCreateMarketSubmitted(marketID, proposalID, submittedBy) + assert.Equal(t, marketID, event.MarketId, "MarketId") + assert.Equal(t, proposalID, event.ProposalId, "ProposalId") + assert.Equal(t, submittedBy.String(), event.SubmittedBy, "SubmittedBy") + assertEverythingSet(t, event, "EventCreateMarketSubmitted") +} + +func TestNewEventMarketCreated(t *testing.T) { + marketID := uint32(10111213) + + event := NewEventMarketCreated(marketID) + assert.Equal(t, marketID, event.MarketId, "MarketId") + assertEverythingSet(t, event, "EventMarketCreated") +} + +func TestNewEventMarketFeesUpdated(t *testing.T) { + marketID := uint32(1415) + + event := NewEventMarketFeesUpdated(marketID) + assert.Equal(t, marketID, event.MarketId, "MarketId") + assertEverythingSet(t, event, "EventMarketFeesUpdated") +} + +func TestNewEventParamsUpdated(t *testing.T) { + event := NewEventParamsUpdated() + assertEverythingSet(t, event, "EventParamsUpdated") +} diff --git a/x/exchange/genesis.go b/x/exchange/genesis.go new file mode 100644 index 0000000000..83a56e37df --- /dev/null +++ b/x/exchange/genesis.go @@ -0,0 +1,42 @@ +package exchange + +import ( + "errors" + "fmt" +) + +func NewGenesisState(params *Params, markets []*Market, orders []*Order) *GenesisState { + return &GenesisState{ + Params: params, + Markets: markets, + Orders: orders, + } +} + +func DefaultGenesisState() *GenesisState { + return NewGenesisState(DefaultParams(), nil, nil) +} + +func (g GenesisState) Validate() error { + var errs []error + + if g.Params != nil { + if err := g.Params.Validate(); err != nil { + errs = append(errs, err) + } + } + + for i, market := range g.Markets { + if err := market.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid market[%d]: %w", i, err)) + } + } + + for i, order := range g.Orders { + if err := order.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid order[%d]: %w", i, err)) + } + } + + return errors.Join(errs...) +} diff --git a/x/exchange/genesis_test.go b/x/exchange/genesis_test.go new file mode 100644 index 0000000000..61439fca98 --- /dev/null +++ b/x/exchange/genesis_test.go @@ -0,0 +1,7 @@ +package exchange + +// TODO[1658]: func TestNewGenesisState(t *testing.t) + +// TODO[1658]: func TestDefaultGenesisState(t *testing.t) + +// TODO[1658]: func TestGenesisState_Validate(t *testing.t) diff --git a/x/exchange/market.go b/x/exchange/market.go new file mode 100644 index 0000000000..af761406e8 --- /dev/null +++ b/x/exchange/market.go @@ -0,0 +1,334 @@ +package exchange + +import ( + "errors" + "fmt" + "sort" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + nametypes "github.com/provenance-io/provenance/x/name/types" +) + +const ( + // MaxName is the maximum length of MarketDetails.Name + MaxName = 250 + // MaxDescription is the maximum length of MarketDetails.Description + MaxDescription = 2000 + // MaxWebsiteUrl is the maximum length of MarketDetails.WebsiteUrl + MaxWebsiteUrl = 200 + // MaxIconUri is the maximum length of MarketDetails.IconUri + MaxIconUri = 2000 +) + +// Validate makes sure that everything in this Market is valid. +// The MarketId is allowed to be zero in here. +// Some uses might require it to have a value, but that check is left up to the caller. +func (m Market) Validate() error { + // Nothing to check on the MarketId. It's allowed to be zero to indicate to use the next one. + + errs := []error{ + m.MarketDetails.Validate(), + m.FeeCreateAskFlat.Validate(), + m.FeeCreateBidFlat.Validate(), + m.FeeSettlementSellerFlat.Validate(), + m.FeeSettlementBuyerFlat.Validate(), + ValidateFeeRatios(m.FeeSettlementSellerRatios, m.FeeSettlementBuyerRatios), + ValidateAccessGrants(m.AccessGrants), + } + + // Nothing to check for with the AcceptingOrders and AllowUserSettlement booleans. + + if err := ValidateReqAttrs(m.ReqAttrCreateAsk); err != nil { + errs = append(errs, fmt.Errorf("invalid create ask required attributes: %w", err)) + } + if err := ValidateReqAttrs(m.ReqAttrCreateBid); err != nil { + errs = append(errs, fmt.Errorf("invalid create bid required attributes: %w", err)) + } + + return errors.Join(errs...) +} + +// Validate returns an error if anything in this MarketDetails is invalid. +func (d MarketDetails) Validate() error { + var errs []error + if len(d.Name) > MaxName { + errs = append(errs, fmt.Errorf("name length %d exceeds maximum length of %d", len(d.Name), MaxName)) + } + if len(d.Description) > MaxDescription { + errs = append(errs, fmt.Errorf("description length %d exceeds maximum length of %d", len(d.Description), MaxDescription)) + } + if len(d.WebsiteUrl) > MaxWebsiteUrl { + errs = append(errs, fmt.Errorf("website_url length %d exceeds maximum length of %d", len(d.WebsiteUrl), MaxWebsiteUrl)) + } + if len(d.IconUri) > MaxIconUri { + errs = append(errs, fmt.Errorf("icon_uri length %d exceeds maximum length of %d", len(d.IconUri), MaxIconUri)) + } + return errors.Join(errs...) +} + +// ValidateSellerFeeRatios returns an error if the provided seller fee ratios contains an invalid entry. +func ValidateSellerFeeRatios(ratios []*FeeRatio) error { + seen := make(map[string]bool) + dups := make(map[string]bool) + var errs []error + for _, ratio := range ratios { + key := ratio.Price.Denom + if seen[key] && !dups[key] { + errs = append(errs, fmt.Errorf("seller fee ratio denom %q appears in multiple ratios", ratio.Price.Denom)) + dups[key] = true + } + seen[key] = true + + if ratio.Price.Denom != ratio.Fee.Denom { + errs = append(errs, fmt.Errorf("seller fee ratio price denom %q does not equal fee denom %q", ratio.Price.Denom, ratio.Fee.Denom)) + } else { + errs = append(errs, ratio.Validate()) + } + } + return errors.Join(errs...) +} + +// ValidateBuyerFeeRatios returns an error if the provided buyer fee ratios contains an invalid entry. +func ValidateBuyerFeeRatios(ratios []*FeeRatio) error { + seen := make(map[string]bool) + dups := make(map[string]bool) + var errs []error + for _, ratio := range ratios { + key := ratio.Price.Denom + ":" + ratio.Fee.Denom + if seen[key] && !dups[key] { + errs = append(errs, fmt.Errorf("buyer fee ratio pair %q to %q appears in multiple ratios", ratio.Price.Denom, ratio.Fee.Denom)) + dups[key] = true + } + seen[key] = true + + errs = append(errs, ratio.Validate()) + } + return errors.Join(errs...) +} + +// ValidateFeeRatios makes sure that the provided fee ratios are valid and have the same price denoms. +func ValidateFeeRatios(sellerRatios, buyerRatios []*FeeRatio) error { + var errs []error + if err := ValidateSellerFeeRatios(sellerRatios); err != nil { + errs = append(errs, err) + } + if err := ValidateBuyerFeeRatios(buyerRatios); err != nil { + errs = append(errs, err) + } + if len(errs) != 0 { + return errors.Join(errs...) + } + + // Make sure the denoms in the prices are the same in the two list. + // For the seller ones, we know there's only one entry per denom. + sellerPriceDenoms := make([]string, len(sellerRatios)) + for i, ratio := range sellerRatios { + sellerPriceDenoms[i] = ratio.Price.Denom + } + + // For the buyer ones, there can be multiple per price denom. + buyerPriceDenomsMap := make(map[string]bool) + buyerPriceDenoms := make([]string, 0) + for _, ratio := range buyerRatios { + if !buyerPriceDenomsMap[ratio.Price.Denom] { + buyerPriceDenoms = append(buyerPriceDenoms, ratio.Price.Denom) + buyerPriceDenomsMap[ratio.Price.Denom] = true + } + } + + for _, denom := range sellerPriceDenoms { + if !contains(buyerPriceDenoms, denom) { + errs = append(errs, fmt.Errorf("denom %s is defined in the seller settlement fee ratios but not buyer", denom)) + } + } + + for _, denom := range buyerPriceDenoms { + if !contains(sellerPriceDenoms, denom) { + errs = append(errs, fmt.Errorf("denom %s is defined in the buyer settlement fee ratios but not seller", denom)) + } + } + + return errors.Join(errs...) +} + +// contains returns true if the provided toFind is present in the provided vals. +func contains[T comparable](vals []T, toFind T) bool { + for _, v := range vals { + if toFind == v { + return true + } + } + return false +} + +// String returns a string representation of this FeeRatio. +func (r FeeRatio) String() string { + return fmt.Sprintf("%s:%s", r.Price, r.Fee) +} + +// Validate returns an error if this FeeRatio is invalid. +func (r FeeRatio) Validate() error { + if r.Price.Denom == r.Fee.Denom && r.Fee.Amount.GT(r.Price.Amount) { + return fmt.Errorf("fee amount %q cannot be greater than price amount %q", r.Fee, r.Price) + } + return nil +} + +// ValidateAccessGrants returns an error if any of the provided access grants are invalid. +func ValidateAccessGrants(accessGrants []*AccessGrant) error { + var errs []error + seen := make(map[string]bool) + dups := make(map[string]bool) + for _, ag := range accessGrants { + if seen[ag.Address] && !dups[ag.Address] { + errs = append(errs, fmt.Errorf("%s appears in multiple access grant entries")) + dups[ag.Address] = true + } + seen[ag.Address] = true + errs = append(errs, ag.Validate()) + } + return errors.Join(errs...) +} + +// Validate returns an error if there is anything wrong with this AccessGrant. +func (a AccessGrant) Validate() error { + _, err := sdk.AccAddressFromBech32(a.Address) + if err != nil { + return err + } + if len(a.Permissions) == 0 { + return fmt.Errorf("no permissions provided for %s", a.Address) + } + seen := make(map[Permission]bool) + for _, perm := range a.Permissions { + if seen[perm] { + return fmt.Errorf("%s appears multiple times for %s", perm.String(), a.Address) + } + seen[perm] = true + if err = perm.Validate(); err != nil { + return err + } + } + return nil +} + +// SimpleString returns a lower-cased version of the permission.String() without the leading "permission_" +// E.g. "settle", or "update". +func (p Permission) SimpleString() string { + return strings.ToLower(strings.TrimPrefix(p.String(), "PERMISSION_")) +} + +// Validate returns an error if this Permission is unspecified or an unknown value. +func (p Permission) Validate() error { + if p == Permission_unspecified { + return errors.New("permission is unspecified") + } + _, exists := Permission_name[int32(p)] + if !exists { + return fmt.Errorf("permission %d does not exist") + } + return nil +} + +// AllPermissions returns all permission values except unspecified. +func AllPermissions() []Permission { + rv := make([]Permission, 0, len(Permission_name)-1) + for val := range Permission_name { + if val != 0 { + rv = append(rv, Permission(val)) + } + } + sort.Slice(rv, func(i, j int) bool { + return rv[i] < rv[j] + }) + return rv +} + +// ParsePermission converts the provided permission string into a Permission value. +// An error is returned if unknown or Permission_unspecified. +// Example inputs: "settle", "CanCel", "WITHDRAW", "permission_update", "PermISSion_PErmissioNs", "PERMISSION_ATTRIBUTES" +func ParsePermission(permission string) (Permission, error) { + permUC := strings.ToUpper(permission) + val, found := Permission_value["PERMISSION_"+permUC] + if found { + if val != 0 { + return Permission(val), nil + } + } else { + val, found = Permission_value[permUC] + if found && val != 0 { + return Permission(val), nil + } + } + return Permission_unspecified, fmt.Errorf("invalid permission: %q", permission) +} + +// ParsePermissions converts the provided permissions strings into a []Permission. +// An error is returned if any are unknown or Permission_unspecified. +// See also: ParsePermission. +func ParsePermissions(permissions ...string) ([]Permission, error) { + rv := make([]Permission, len(permissions)) + var errs []error + for i, perm := range permissions { + var err error + rv[i], err = ParsePermission(perm) + if err != nil { + errs = append(errs, err) + } + } + return rv, errors.Join(errs...) +} + +// ValidateReqAttrs makes sure that each provided attribute is valid and that no duplicate entries are provided. +func ValidateReqAttrs(attrLists ...[]string) error { + var errs []error + seen := make(map[string]bool) + dups := make(map[string]bool) + for _, attrs := range attrLists { + for _, attr := range attrs { + if seen[attr] { + if !dups[attr] { + errs = append(errs, fmt.Errorf("duplicate required attribute entry: %q", attr)) + dups[attr] = true + } + continue + } + seen[attr] = true + if !IsValidReqAttr(attr) { + errs = append(errs, fmt.Errorf("invalid required attribute %q", attr)) + } + } + } + return errors.Join(errs...) +} + +// IsValidReqAttr returns true if the provided string is a valid required attribute entry. +func IsValidReqAttr(reqAttr string) bool { + // If it's already valid, we're all good. + if nametypes.IsValidName(reqAttr) { + return true + } + + // If there isn't a wildcard in it, there's no saving it. + if !strings.Contains(reqAttr, "*") { + return false + } + + // Get the normalized version so we can more accurately check things on it. + normalized := nametypes.NormalizeName(reqAttr) + + // If it's just a wildcard, allow it. + if normalized == "*" { + return true + } + + // The first segment can be a * wildcard. + // If that's what we've got, make sure everything after it is valid. + if normalized[:2] == "*." { + return nametypes.IsValidName(normalized[2:]) + } + + // Nothing left that might save it. + return false +} diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go new file mode 100644 index 0000000000..1aadfdcc6b --- /dev/null +++ b/x/exchange/market_test.go @@ -0,0 +1,33 @@ +package exchange + +// TODO[1658]: func TestMarket_Validate(t *testing.T) + +// TODO[1658]: func TestMarketDetails_Validate(t *testing.T) + +// TODO[1658]: func TestValidateSellerFeeRatios(t *testing.T) + +// TODO[1658]: func TestValidateBuyerFeeRatios(t *testing.T) + +// TODO[1658]: func TestValidateFeeRatios(t *testing.T) + +// TODO[1658]: func TestFeeRatio_String(t *testing.T) + +// TODO[1658]: func TestFeeRatio_Validate(t *testing.T) + +// TODO[1658]: func TestValidateAccessGrants(t *testing.T) + +// TODO[1658]: func TestAccessGrant_Validate(t *testing.T) + +// TODO[1658]: func TestPermission_SimpleString(t *testing.T) + +// TODO[1658]: func TestPermission_Validate(t *testing.T) + +// TODO[1658]: func TestAllPermissions(t *testing.T) + +// TODO[1658]: func TestParsePermission(t *testing.T) + +// TODO[1658]: func TestParsePermissions(t *testing.T) + +// TODO[1658]: func TestValidateReqAttrs(t *testing.T) + +// TODO[1658]: func TestIsValidReqAttr(t *testing.T) diff --git a/x/exchange/orders.go b/x/exchange/orders.go new file mode 100644 index 0000000000..7d9a33325e --- /dev/null +++ b/x/exchange/orders.go @@ -0,0 +1,202 @@ +package exchange + +import ( + "errors" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Define the type strings and bytes to use for each order type. +// These must all be unique. If you add entries, be sure to update TestOrderTypesAndBytes too. +const ( + OrderTypeAsk = "ask" + OrderTypeByteAsk = 0x00 + + OrderTypeBid = "bid" + OrderTypeByteBid = 0x01 +) + +// NewOrder creates a new empty Order with the provided order id. +// The order details are set using one of: WithAsk, WithBid. +func NewOrder(orderID uint64) *Order { + return &Order{OrderId: orderID} +} + +// WithAsk updates this to contain the provided AskOrder and returns itself. +func (o *Order) WithAsk(askOrder *AskOrder) *Order { + o.Order = &Order_AskOrder{AskOrder: askOrder} + return o +} + +// WithBid updates this to contain the provided BidOrder and returns itself. +func (o *Order) WithBid(bidOrder *BidOrder) *Order { + o.Order = &Order_BidOrder{BidOrder: bidOrder} + return o +} + +// OrderType returns a string indicating what type this order is. +// See: OrderTypeAsk, OrderTypeBid +// Panics if the order details are not set or are something unexpected. +func (o Order) OrderType() string { + switch v := o.GetOrder().(type) { + case *Order_AskOrder: + return OrderTypeAsk + case *Order_BidOrder: + return OrderTypeBid + default: + // If OrderType() is called without the order being set yet, it's a programming error, so panic. + // If it's a type without a case, the case needs to be added, so panic. + panic(fmt.Sprintf("OrderType() missing case for %T", v)) + } +} + +// OrderTypeByte returns the type byte for this order. +// See: OrderTypeByteAsk, OrderTypeByteBid +// Panics if the order details are not set or are something unexpected. +func (o Order) OrderTypeByte() byte { + switch v := o.GetOrder().(type) { + case *Order_AskOrder: + return OrderTypeByteAsk + case *Order_BidOrder: + return OrderTypeByteBid + default: + // If OrderType() is called without the order being set yet, it's a programming error, so panic. + // If it's a type without a case, the case needs to be added, so panic. + panic(fmt.Sprintf("OrderTypeByte() missing case for %T", v)) + } +} + +func (o Order) Validate() error { + if o.OrderId == 0 { + return errors.New("invalid order id: must not be zero") + } + switch v := o.GetOrder().(type) { + case *Order_AskOrder: + return v.AskOrder.Validate() + case *Order_BidOrder: + return v.BidOrder.Validate() + default: + return fmt.Errorf("unknown order type %T", v) + } +} + +// Validate returns an error if anything in this ask order is invalid. +func (a AskOrder) Validate() error { + var errs []error + + // The market id must be provided. + if a.MarketId == 0 { + errs = append(errs, errors.New("invalid market id: must not be zero")) + } + + // The seller address must be valid and not empty. + if _, err := sdk.AccAddressFromBech32(a.Seller); err != nil { + errs = append(errs, fmt.Errorf("invalid seller: %w", err)) + } + + // The price must not be zero and must be a valid coin. + // The Coin.Validate() method allows the coin to be zero (but not negative). + var priceDenom string + if a.Price.IsZero() { + errs = append(errs, errors.New("invalid price: cannot be zero")) + } else { + if err := a.Price.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid price: %w", err)) + } else { + priceDenom = a.Price.Denom + } + } + + // The Coins.Validate method does NOT allow any coin entries to be zero (or negative). + // It does allow there to not be any entries, though, which we don't want to allow here. + // We also don't want to allow the price denom to also be in the assets. + if err := a.Assets.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid assets: %w", err)) + } else { + switch { + case len(a.Assets) == 0: + errs = append(errs, errors.New("invalid assets: must not be empty")) + case len(priceDenom) > 0: + for _, asset := range a.Assets { + if priceDenom == asset.Denom { + errs = append(errs, fmt.Errorf("invalid assets: cannot contain price denom %s", priceDenom)) + break + } + } + } + } + + if a.SellerSettlementFlatFee != nil { + // Again, a Coin can be zero according to Validate. + // A seller settlement flat fee is optional, but if they provided something, + // it must have a positive (non-zero) value. + if err := a.SellerSettlementFlatFee.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid seller settlement flat fee: %w", err)) + } else if a.SellerSettlementFlatFee.IsZero() { + errs = append(errs, fmt.Errorf("invalid seller settlement flat fee: %s amount cannot be zero", a.SellerSettlementFlatFee.Denom)) + } + } + + // Nothing to check on the AllowPartial boolean. + + return errors.Join(errs...) +} + +// Validate returns an error if anything in this ask order is invalid. +func (a BidOrder) Validate() error { + var errs []error + + // The market id must be provided. + if a.MarketId == 0 { + errs = append(errs, errors.New("invalid market id: must not be zero")) + } + + // The seller address must be valid and not empty. + if _, err := sdk.AccAddressFromBech32(a.Buyer); err != nil { + errs = append(errs, fmt.Errorf("invalid buyer: %w", err)) + } + + // The price must not be zero and must be a valid coin. + // The Coin.Validate() method allows the coin to be zero (but not negative). + var priceDenom string + if a.Price.IsZero() { + errs = append(errs, errors.New("invalid price: cannot be zero")) + } else { + if err := a.Price.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid price: %w", err)) + } else { + priceDenom = a.Price.Denom + } + } + + // The Coins.Validate method does NOT allow any coin entries to be zero (or negative). + // It does allow there to not be any entries, though, which we don't want to allow here. + // We also don't want to allow the price denom to also be in the assets. + if err := a.Assets.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid assets: %w", err)) + } else { + switch { + case len(a.Assets) == 0: + errs = append(errs, errors.New("invalid assets: must not be empty")) + case len(priceDenom) > 0: + for _, asset := range a.Assets { + if priceDenom == asset.Denom { + errs = append(errs, fmt.Errorf("invalid assets: cannot contain price denom %s", priceDenom)) + break + } + } + } + } + + if len(a.BuyerSettlementFees) > 0 { + // If there are buyer settlement fees, they must all be valid and positive (non-zero). + if err := a.BuyerSettlementFees.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid buyer settlement fees: %w", err)) + } + } + + // Nothing to check on the AllowPartial boolean. + + return errors.Join(errs...) +} diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go new file mode 100644 index 0000000000..f5d94b87b2 --- /dev/null +++ b/x/exchange/orders_test.go @@ -0,0 +1,303 @@ +package exchange + +import ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// copyCoins creates a copy of the provided coins slice with copies of each entry. +func copyCoins(coins sdk.Coins) sdk.Coins { + if coins == nil { + return nil + } + rv := make(sdk.Coins, len(coins)) + for i, coin := range coins { + rv[i] = copyCoin(coin) + } + return rv +} + +// copyCoin returns a copy of the provided coin. +func copyCoin(coin sdk.Coin) sdk.Coin { + return sdk.NewInt64Coin(coin.Denom, coin.Amount.Int64()) +} + +// copyCoinP returns a copy of the provided *coin. +func copyCoinP(coin *sdk.Coin) *sdk.Coin { + if coin == nil { + return nil + } + rv := copyCoin(*coin) + return &rv +} + +func TestOrderTypesAndBytes(t *testing.T) { + values := []struct { + name string + str string + b byte + }{ + {name: "Ask", str: OrderTypeAsk, b: OrderTypeByteAsk}, + {name: "Bid", str: OrderTypeBid, b: OrderTypeByteBid}, + } + + ot := func(name string) string { + return "OrderType" + name + } + otb := func(name string) string { + return "OrderTypeByte" + name + } + + knownTypeStrings := make(map[string]string) + knownTypeBytes := make(map[byte]string) + var errs []error + + for _, value := range values { + strKnownBy, strKnown := knownTypeStrings[value.str] + if strKnown { + errs = append(errs, fmt.Errorf("type string %q used by both %s and %s", value.str, ot(strKnownBy), ot(value.name))) + } + knownTypeStrings[value.str] = value.name + + bKnownBy, bKnown := knownTypeBytes[value.b] + if bKnown { + errs = append(errs, fmt.Errorf("type byte %#X used by both %s and %s", value.b, otb(bKnownBy), otb(value.name))) + } + knownTypeBytes[value.b] = value.name + } + + err := errors.Join(errs...) + assert.NoError(t, err, "checking for duplicate values") +} + +func TestNewOrder(t *testing.T) { + tests := []struct { + name string + orderID uint64 + }{ + {name: "zero", orderID: 0}, + {name: "one", orderID: 1}, + {name: "two", orderID: 2}, + {name: "max uint8", orderID: 255}, + {name: "max uint16", orderID: 65_535}, + {name: "max uint32", orderID: 4_294_967_295}, + {name: "max uint64", orderID: 18_446_744_073_709_551_615}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + order := NewOrder(tc.orderID) + assert.Equal(t, tc.orderID, order.OrderId, "order.OrderID") + assert.Nil(t, order.Order, "order.Order") + }) + } +} + +func TestOrder_WithAsk(t *testing.T) { + origOrderID := uint64(5) + base := &Order{OrderId: origOrderID} + origAsk := &AskOrder{ + MarketId: 12, + Seller: "some seller", + Assets: sdk.NewCoins(sdk.NewInt64Coin("water", 8)), + Price: sdk.NewInt64Coin("sand", 1), + SellerSettlementFlatFee: &sdk.Coin{Denom: "banana", Amount: sdkmath.NewInt(3)}, + AllowPartial: true, + } + ask := &AskOrder{ + MarketId: origAsk.MarketId, + Seller: origAsk.Seller, + Assets: copyCoins(origAsk.Assets), + Price: copyCoin(origAsk.Price), + SellerSettlementFlatFee: copyCoinP(origAsk.SellerSettlementFlatFee), + AllowPartial: origAsk.AllowPartial, + } + + var order *Order + testFunc := func() { + order = base.WithAsk(ask) + } + require.NotPanics(t, testFunc, "WithAsk") + + // Make sure the returned reference is the same as the base (receiver). + assert.Same(t, base, order, "WithAsk result (actual) vs receiver (expected)") + + // Make sure the order id didn't change. + assert.Equal(t, int(origOrderID), int(base.OrderId), "OrderId of result") + + // Make sure the AskOrder in the Order is the same reference as the one provided to WithAsk + orderAsk := order.GetAskOrder() + assert.Same(t, ask, orderAsk, "the ask in the resulting order (actual) vs what was provided (expected)") + + // Make sure nothing in the ask changed during WithAsk. + assert.Equal(t, origAsk, orderAsk, "the ask in the resulting order (actual) vs what the ask was before being provided (expected)") +} + +func TestOrder_WithBid(t *testing.T) { + origOrderID := uint64(4) + base := &Order{OrderId: origOrderID} + origBid := &BidOrder{ + MarketId: 11, + Buyer: "some buyer", + Assets: sdk.NewCoins(sdk.NewInt64Coin("agua", 7)), + Price: sdk.NewInt64Coin("dirt", 2), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("grape", 3)), + AllowPartial: true, + } + bid := &BidOrder{ + MarketId: origBid.MarketId, + Buyer: origBid.Buyer, + Assets: copyCoins(origBid.Assets), + Price: copyCoin(origBid.Price), + BuyerSettlementFees: copyCoins(origBid.BuyerSettlementFees), + AllowPartial: origBid.AllowPartial, + } + + var order *Order + testFunc := func() { + order = base.WithBid(bid) + } + require.NotPanics(t, testFunc, "WithBid") + + // Make sure the returned reference is the same as the base (receiver). + assert.Same(t, base, order, "WithBid result (actual) vs receiver (expected)") + + // Make sure the order id didn't change. + assert.Equal(t, int(origOrderID), int(base.OrderId), "OrderId of result") + + // Make sure the BidOrder in the Order is the same reference as the one provided to WithBid + orderBid := order.GetBidOrder() + assert.Same(t, bid, orderBid, "the bid in the resulting order (actual) vs what was provided (expected)") + + // Make sure nothing in the bid changed during WithBid. + assert.Equal(t, origBid, orderBid, "the bid in the resulting order (actual) vs what the bid was before being provided (expected)") +} + +// unknownOrderType is thing that implements isOrder_Order so it can be +// added to an Order, but isn't a type that there is anything defined for. +type unknownOrderType struct{} + +var _ isOrder_Order = (*unknownOrderType)(nil) + +func (o *unknownOrderType) isOrder_Order() {} +func (o *unknownOrderType) MarshalTo([]byte) (int, error) { + return 0, nil +} +func (o *unknownOrderType) Size() int { + return 0 +} + +func TestOrder_OrderType(t *testing.T) { + tests := []struct { + name string + order *Order + expected string + expPanic interface{} + }{ + { + name: "AskOrder", + order: NewOrder(1).WithAsk(&AskOrder{}), + expected: OrderTypeAsk, + }, + { + name: "BidOrder", + order: NewOrder(2).WithBid(&BidOrder{}), + expected: OrderTypeBid, + }, + { + name: "nil base order", + order: nil, + expPanic: "OrderType() missing case for ", + }, + { + name: "nil inside order", + order: NewOrder(3), + expPanic: "OrderType() missing case for ", + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + expPanic: "OrderType() missing case for *exchange.unknownOrderType", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.order.OrderType() + } + + if tc.expPanic != nil { + require.PanicsWithValue(t, tc.expPanic, testFunc, "OrderType") + } else { + require.NotPanics(t, testFunc, "OrderType") + assert.Equal(t, tc.expected, actual, "OrderType result") + } + }) + } +} + +func TestOrder_OrderTypeByte(t *testing.T) { + tests := []struct { + name string + order *Order + expected byte + expPanic interface{} + }{ + { + name: "AskOrder", + order: NewOrder(1).WithAsk(&AskOrder{}), + expected: OrderTypeByteAsk, + }, + { + name: "BidOrder", + order: NewOrder(2).WithBid(&BidOrder{}), + expected: OrderTypeByteBid, + }, + { + name: "nil base order", + order: nil, + expPanic: "OrderTypeByte() missing case for ", + }, + { + name: "nil inside order", + order: NewOrder(3), + expPanic: "OrderTypeByte() missing case for ", + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + expPanic: "OrderTypeByte() missing case for *exchange.unknownOrderType", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual byte + testFunc := func() { + actual = tc.order.OrderTypeByte() + } + + if tc.expPanic != nil { + require.PanicsWithValue(t, tc.expPanic, testFunc, "OrderTypeByte") + } else { + require.NotPanics(t, testFunc, "OrderTypeByte") + assert.Equal(t, tc.expected, actual, "OrderTypeByte result") + } + }) + } +} + +// TODO[1658]: func TestOrder_Validate(t *testing.T) + +// TODO[1658]: func TestAskOrder_Validate(t *testing.T) + +// TODO[1658]: func TestBidOrder_Validate(t *testing.T) diff --git a/x/exchange/params.go b/x/exchange/params.go new file mode 100644 index 0000000000..a838936c93 --- /dev/null +++ b/x/exchange/params.go @@ -0,0 +1,66 @@ +package exchange + +import ( + "errors" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // DefaultDefaultSplit is the default value used for the DefaultSplit parameter. + // TODO[1658]: Discuss what this should be with someone who would know. + DefaultDefaultSplit = uint32(500) + + // MaxSplit is the maximum split value. 10,000 basis points = 100%. + MaxSplit = uint32(10_000) +) + +func NewParams(defaultSplit uint32, denomSplits []*DenomSplit) *Params { + return &Params{ + DefaultSplit: defaultSplit, + DenomSplits: denomSplits, + } +} + +// DefaultParams returns the default exchange module params. +func DefaultParams() *Params { + return NewParams(DefaultDefaultSplit, nil) +} + +// Validate returns an error if there's something wrong with these params. +func (p Params) Validate() error { + var errs []error + if err := validateSplit("default", p.DefaultSplit); err != nil { + errs = append(errs, err) + } + for _, split := range p.DenomSplits { + if err := split.Validate(); err != nil { + errs = append(errs, err) + } + } + return errors.Join(errs...) +} + +func NewDenomSplit(denom string, split uint32) *DenomSplit { + return &DenomSplit{ + Denom: denom, + Split: split, + } +} + +// Validate returns an error if there's something wrong with this DenomSplit. +func (d DenomSplit) Validate() error { + if err := sdk.ValidateDenom(d.Denom); err != nil { + return err + } + return validateSplit(d.Denom, d.Split) +} + +// validateSplit makes sure that the provided split value is in the valid range. +func validateSplit(name string, split uint32) error { + if split > MaxSplit { + return fmt.Errorf("%s split %d cannot be greater than %d", name, split, MaxSplit) + } + return nil +} diff --git a/x/exchange/params_test.go b/x/exchange/params_test.go new file mode 100644 index 0000000000..bcd7fb538e --- /dev/null +++ b/x/exchange/params_test.go @@ -0,0 +1,231 @@ +package exchange + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMaxSplit(t *testing.T) { + // The MaxSplit should never be changed. + // But if it is changed for some reason, it can never be more than 100%. + absoluteMax := uint32(10_000) + assert.LessOrEqual(t, MaxSplit, absoluteMax) +} + +func TestNewParams(t *testing.T) { + tests := []struct { + name string + defaultSplit uint32 + denomSplits []*DenomSplit + expected *Params + }{ + { + name: "zero values", + defaultSplit: 0, + denomSplits: nil, + expected: &Params{}, + }, + { + name: "100 nil", + defaultSplit: 100, + denomSplits: nil, + expected: &Params{DefaultSplit: 100}, + }, + { + name: "123 with two denom splits", + defaultSplit: 123, + denomSplits: []*DenomSplit{ + {Denom: "atom", Split: 234}, + {Denom: "nhash", Split: 56}, + }, + expected: &Params{ + DefaultSplit: 123, + DenomSplits: []*DenomSplit{ + {Denom: "atom", Split: 234}, + {Denom: "nhash", Split: 56}, + }, + }, + }, + { + name: "defaults", + defaultSplit: DefaultDefaultSplit, + denomSplits: nil, + expected: DefaultParams(), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + actual := NewParams(tc.defaultSplit, tc.denomSplits) + assert.Equal(t, tc.expected, actual, "NewParams") + }) + } +} + +func TestDefaultParams(t *testing.T) { + actual := DefaultParams() + assert.Equal(t, int(DefaultDefaultSplit), int(actual.DefaultSplit), "DefaultSplit") + assert.Nil(t, actual.DenomSplits, "DenomSplits") +} + +func TestParams_Validate(t *testing.T) { + tests := []struct { + name string + params Params + expErr []string + }{ + { + name: "zero values", + params: Params{}, + expErr: nil, + }, + { + name: "default values", + params: *DefaultParams(), + expErr: nil, + }, + { + name: "bad default split", + params: Params{DefaultSplit: 10_001}, + expErr: []string{"default split 10001 cannot be greater than 10000"}, + }, + { + name: "one denom split and it is bad", + params: Params{DenomSplits: []*DenomSplit{{Denom: "badcointype", Split: 10_001}}}, + expErr: []string{"badcointype split 10001 cannot be greater than 10000"}, + }, + { + name: "bad default and three bad denom splits", + params: Params{ + DefaultSplit: 10_001, + DenomSplits: []*DenomSplit{ + {Denom: "badcointype", Split: 10_002}, + {Denom: "x", Split: 3}, + {Denom: "thisIsNotGood", Split: 10_003}, + }, + }, + expErr: []string{ + "default split 10001 cannot be greater than 10000", + "badcointype split 10002 cannot be greater than 10000", + "invalid denom: x", + "thisIsNotGood split 10003 cannot be greater than 10000", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.params.Validate() + + // TODO[1658]: Replace this with testutils.AssertErrorContents(t, err, tc.expErr, "Validate") + if len(tc.expErr) > 0 { + if assert.Error(t, err, "Validate") { + for _, exp := range tc.expErr { + assert.ErrorContains(t, err, exp, "Validate\nExpecting: %q", exp) + } + } + } else { + assert.NoError(t, err, "Validate") + } + }) + } +} + +func TestNewDenomSplit(t *testing.T) { + tests := []struct { + name string + denom string + split uint32 + exp *DenomSplit + }{ + { + name: "zero values", + denom: "", + split: 0, + exp: &DenomSplit{}, + }, + { + name: "denom without split", + denom: "somedenom", + split: 0, + exp: &DenomSplit{Denom: "somedenom"}, + }, + { + name: "split without denom", + denom: "", + split: 541, + exp: &DenomSplit{Split: 541}, + }, + { + name: "both provided", + denom: "anotherdenom", + split: 565, + exp: &DenomSplit{Denom: "anotherdenom", Split: 565}, + }, + { + name: "neither value valid", + denom: "z", + split: 10_008, + exp: &DenomSplit{Denom: "z", Split: 10_008}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + actual := NewDenomSplit(tc.denom, tc.split) + assert.Equal(t, tc.exp, actual, "NewDenomSplit") + }) + } +} + +func TestDenomSplit_Validate(t *testing.T) { + tests := []struct { + name string + denomSplit DenomSplit + expErr string + }{ + { + name: "no denom", + denomSplit: DenomSplit{Denom: "", Split: 5}, + expErr: "invalid denom: ", + }, + { + name: "invalid denom", + denomSplit: DenomSplit{Denom: "f", Split: 6}, + expErr: "invalid denom: f", + }, + { + name: "invalid split", + denomSplit: DenomSplit{Denom: "thisDenomIsOkay", Split: 10_123}, + expErr: "thisDenomIsOkay split 10123 cannot be greater than 10000", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.denomSplit.Validate() + // TODO[1658]: Replace this with assertErrorValue(t, err, tc.expErr, "Validate") + if len(tc.expErr) > 0 { + assert.EqualError(t, err, tc.expErr, "Validate") + } else { + assert.NoError(t, err, "Validate") + } + }) + } +} + +func TestValidateSplit(t *testing.T) { + name := "" + split := MaxSplit + 1 + splitStr := fmt.Sprintf("%d", split) + maxSplitStr := fmt.Sprintf("%d", MaxSplit) + + err := validateSplit(name, split) + if assert.Error(t, err, "validateSplit") { + assert.ErrorContains(t, err, name, "provided name") + assert.ErrorContains(t, err, splitStr, "provided split value") + assert.ErrorContains(t, err, maxSplitStr, "maximum split value") + } +} From 218d58f4027bc7deabbb221faed477dc76626b01 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 24 Aug 2023 19:33:37 -0600 Subject: [PATCH 013/309] [1658]: Fix the name of genesis.proto (was qenesis.proto) and regenerate code. --- docs/proto-docs.md | 6 +- .../v1/{qenesis.proto => genesis.proto} | 0 x/exchange/genesis.pb.go | 440 ++++++++++++++++++ 3 files changed, 443 insertions(+), 3 deletions(-) rename proto/provenance/exchange/v1/{qenesis.proto => genesis.proto} (100%) create mode 100644 x/exchange/genesis.pb.go diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 048f9efdfc..17b18274f1 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -87,7 +87,7 @@ - [DenomSplit](#provenance.exchange.v1.DenomSplit) - [Params](#provenance.exchange.v1.Params) -- [provenance/exchange/v1/qenesis.proto](#provenance/exchange/v1/qenesis.proto) +- [provenance/exchange/v1/genesis.proto](#provenance/exchange/v1/genesis.proto) - [GenesisState](#provenance.exchange.v1.GenesisState) - [provenance/exchange/v1/query.proto](#provenance/exchange/v1/query.proto) @@ -1780,10 +1780,10 @@ Params is a representation of the exchange module parameters. - +

Top

-## provenance/exchange/v1/qenesis.proto +## provenance/exchange/v1/genesis.proto diff --git a/proto/provenance/exchange/v1/qenesis.proto b/proto/provenance/exchange/v1/genesis.proto similarity index 100% rename from proto/provenance/exchange/v1/qenesis.proto rename to proto/provenance/exchange/v1/genesis.proto diff --git a/x/exchange/genesis.pb.go b/x/exchange/genesis.pb.go new file mode 100644 index 0000000000..e3733d06eb --- /dev/null +++ b/x/exchange/genesis.pb.go @@ -0,0 +1,440 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: provenance/exchange/v1/genesis.proto + +package exchange + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState is the data that should be loaded into the exchange module during genesis. +type GenesisState struct { + // params defines all the parameters of the exchange module. + Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` + // markets are all of the markets to create at genesis. + Markets []*Market `protobuf:"bytes,2,rep,name=markets,proto3" json:"markets,omitempty"` + // orders are all the orders to create at genesis. + Orders []*Order `protobuf:"bytes,3,rep,name=orders,proto3" json:"orders,omitempty"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_087ceebafabf03c9, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func init() { + proto.RegisterType((*GenesisState)(nil), "provenance.exchange.v1.GenesisState") +} + +func init() { + proto.RegisterFile("provenance/exchange/v1/genesis.proto", fileDescriptor_087ceebafabf03c9) +} + +var fileDescriptor_087ceebafabf03c9 = []byte{ + // 265 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x29, 0x28, 0xca, 0x2f, + 0x4b, 0xcd, 0x4b, 0xcc, 0x4b, 0x4e, 0xd5, 0x4f, 0xad, 0x48, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0xd5, + 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, + 0x17, 0x12, 0x43, 0xa8, 0xd2, 0x83, 0xa9, 0xd2, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, + 0x07, 0x2b, 0xd1, 0x07, 0xb1, 0x20, 0xaa, 0xa5, 0x94, 0x71, 0x98, 0x99, 0x9b, 0x58, 0x94, 0x9d, + 0x5a, 0x42, 0x40, 0x51, 0x7e, 0x51, 0x4a, 0x6a, 0x51, 0x31, 0x01, 0x45, 0x05, 0x89, 0x45, 0x89, + 0xb9, 0x50, 0x45, 0x4a, 0x07, 0x19, 0xb9, 0x78, 0xdc, 0x21, 0xce, 0x0d, 0x2e, 0x49, 0x2c, 0x49, + 0x15, 0x32, 0xe3, 0x62, 0x83, 0x28, 0x90, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd3, 0xc3, + 0xee, 0x7c, 0xbd, 0x00, 0xb0, 0xaa, 0x20, 0xa8, 0x6a, 0x21, 0x0b, 0x2e, 0x76, 0x88, 0x13, 0x8b, + 0x25, 0x98, 0x14, 0x98, 0xf1, 0x69, 0xf4, 0x05, 0x2b, 0x0b, 0x82, 0x29, 0x17, 0x32, 0xe5, 0x62, + 0x83, 0xb8, 0x5b, 0x82, 0x19, 0xac, 0x51, 0x16, 0x97, 0x46, 0x7f, 0x90, 0xaa, 0x20, 0xa8, 0x62, + 0x2b, 0x8e, 0x8e, 0x05, 0xf2, 0x0c, 0x2f, 0x16, 0xc8, 0x33, 0x38, 0xa5, 0x9e, 0x78, 0x24, 0xc7, + 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, + 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x03, 0x97, 0x64, 0x66, 0x3e, 0x0e, 0xc3, 0x02, 0x18, 0xa3, 0xf4, + 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x11, 0x8a, 0x74, 0x33, 0xf3, + 0x91, 0x78, 0xfa, 0x15, 0xf0, 0xa0, 0x4b, 0x62, 0x03, 0x87, 0x98, 0x31, 0x20, 0x00, 0x00, 0xff, + 0xff, 0x31, 0x50, 0xe7, 0x5f, 0xf6, 0x01, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Orders) > 0 { + for iNdEx := len(m.Orders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Orders[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Markets) > 0 { + for iNdEx := len(m.Markets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Markets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.Params != nil { + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Params != nil { + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + if len(m.Markets) > 0 { + for _, e := range m.Markets { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Orders) > 0 { + for _, e := range m.Orders { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = &Params{} + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Markets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Markets = append(m.Markets, &Market{}) + if err := m.Markets[len(m.Markets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Orders", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Orders = append(m.Orders, &Order{}) + if err := m.Orders[len(m.Orders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) From 55a6f9ac65e4238a6ba1f6d0813cf6eb73c4a624 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 25 Aug 2023 11:25:44 -0600 Subject: [PATCH 014/309] [1658]: Knock out a couple TODOs in the module releated to genesis. --- x/exchange/module/module.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/x/exchange/module/module.go b/x/exchange/module/module.go index 88b4bbab0d..b906e08b39 100644 --- a/x/exchange/module/module.go +++ b/x/exchange/module/module.go @@ -50,9 +50,7 @@ func (AppModuleBasic) Name() string { // DefaultGenesis returns default genesis state as raw bytes for the exchange module. func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { - // TODO[1658]: Create DefaultGenesisState() - panic("not implemented") - // return cdc.MustMarshalJSON(exchange.DefaultGenesisState()) + return cdc.MustMarshalJSON(exchange.DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the exchange module. @@ -61,9 +59,7 @@ func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ sdkclient.TxEncodin if err := cdc.UnmarshalJSON(bz, &data); err != nil { return fmt.Errorf("failed to unmarshal %s genesis state: %w", exchange.ModuleName, err) } - // TODO[1658]: Create GenesisState.Validate() - panic("not implemented") - //return data.Validate() + return data.Validate() } // GetQueryCmd returns the cli query commands for the exchange module. From b89f67124537113e06fdb5c01ff6446a02f1b8a2 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 25 Aug 2023 12:12:52 -0600 Subject: [PATCH 015/309] [1658]: Delete the qenesis.pb.go file that was from the missnamed proto file. --- x/exchange/qenesis.pb.go | 440 --------------------------------------- 1 file changed, 440 deletions(-) delete mode 100644 x/exchange/qenesis.pb.go diff --git a/x/exchange/qenesis.pb.go b/x/exchange/qenesis.pb.go deleted file mode 100644 index aecf26bbfc..0000000000 --- a/x/exchange/qenesis.pb.go +++ /dev/null @@ -1,440 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: provenance/exchange/v1/qenesis.proto - -package exchange - -import ( - fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -// GenesisState is the data that should be loaded into the exchange module during genesis. -type GenesisState struct { - // params defines all the parameters of the exchange module. - Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` - // markets are all of the markets to create at genesis. - Markets []*Market `protobuf:"bytes,2,rep,name=markets,proto3" json:"markets,omitempty"` - // orders are all the orders to create at genesis. - Orders []*Order `protobuf:"bytes,3,rep,name=orders,proto3" json:"orders,omitempty"` -} - -func (m *GenesisState) Reset() { *m = GenesisState{} } -func (m *GenesisState) String() string { return proto.CompactTextString(m) } -func (*GenesisState) ProtoMessage() {} -func (*GenesisState) Descriptor() ([]byte, []int) { - return fileDescriptor_dca953db3e677d28, []int{0} -} -func (m *GenesisState) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *GenesisState) XXX_Merge(src proto.Message) { - xxx_messageInfo_GenesisState.Merge(m, src) -} -func (m *GenesisState) XXX_Size() int { - return m.Size() -} -func (m *GenesisState) XXX_DiscardUnknown() { - xxx_messageInfo_GenesisState.DiscardUnknown(m) -} - -var xxx_messageInfo_GenesisState proto.InternalMessageInfo - -func init() { - proto.RegisterType((*GenesisState)(nil), "provenance.exchange.v1.GenesisState") -} - -func init() { - proto.RegisterFile("provenance/exchange/v1/qenesis.proto", fileDescriptor_dca953db3e677d28) -} - -var fileDescriptor_dca953db3e677d28 = []byte{ - // 265 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x29, 0x28, 0xca, 0x2f, - 0x4b, 0xcd, 0x4b, 0xcc, 0x4b, 0x4e, 0xd5, 0x4f, 0xad, 0x48, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0xd5, - 0x2f, 0x33, 0xd4, 0x2f, 0x4c, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, - 0x17, 0x12, 0x43, 0xa8, 0xd2, 0x83, 0xa9, 0xd2, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, - 0x07, 0x2b, 0xd1, 0x07, 0xb1, 0x20, 0xaa, 0xa5, 0x94, 0x71, 0x98, 0x99, 0x9b, 0x58, 0x94, 0x9d, - 0x5a, 0x42, 0x40, 0x51, 0x7e, 0x51, 0x4a, 0x6a, 0x51, 0x31, 0x01, 0x45, 0x05, 0x89, 0x45, 0x89, - 0xb9, 0x50, 0x45, 0x4a, 0x07, 0x19, 0xb9, 0x78, 0xdc, 0x21, 0xce, 0x0d, 0x2e, 0x49, 0x2c, 0x49, - 0x15, 0x32, 0xe3, 0x62, 0x83, 0x28, 0x90, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd3, 0xc3, - 0xee, 0x7c, 0xbd, 0x00, 0xb0, 0xaa, 0x20, 0xa8, 0x6a, 0x21, 0x0b, 0x2e, 0x76, 0x88, 0x13, 0x8b, - 0x25, 0x98, 0x14, 0x98, 0xf1, 0x69, 0xf4, 0x05, 0x2b, 0x0b, 0x82, 0x29, 0x17, 0x32, 0xe5, 0x62, - 0x83, 0xb8, 0x5b, 0x82, 0x19, 0xac, 0x51, 0x16, 0x97, 0x46, 0x7f, 0x90, 0xaa, 0x20, 0xa8, 0x62, - 0x2b, 0x8e, 0x8e, 0x05, 0xf2, 0x0c, 0x2f, 0x16, 0xc8, 0x33, 0x38, 0xa5, 0x9e, 0x78, 0x24, 0xc7, - 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, - 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x03, 0x97, 0x64, 0x66, 0x3e, 0x0e, 0xc3, 0x02, 0x18, 0xa3, 0xf4, - 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x11, 0x8a, 0x74, 0x33, 0xf3, - 0x91, 0x78, 0xfa, 0x15, 0xf0, 0xa0, 0x4b, 0x62, 0x03, 0x87, 0x98, 0x31, 0x20, 0x00, 0x00, 0xff, - 0xff, 0xa5, 0x94, 0x18, 0x9e, 0xf6, 0x01, 0x00, 0x00, -} - -func (m *GenesisState) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Orders) > 0 { - for iNdEx := len(m.Orders) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Orders[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintQenesis(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - } - if len(m.Markets) > 0 { - for iNdEx := len(m.Markets) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Markets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintQenesis(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - } - if m.Params != nil { - { - size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintQenesis(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func encodeVarintQenesis(dAtA []byte, offset int, v uint64) int { - offset -= sovQenesis(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *GenesisState) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Params != nil { - l = m.Params.Size() - n += 1 + l + sovQenesis(uint64(l)) - } - if len(m.Markets) > 0 { - for _, e := range m.Markets { - l = e.Size() - n += 1 + l + sovQenesis(uint64(l)) - } - } - if len(m.Orders) > 0 { - for _, e := range m.Orders { - l = e.Size() - n += 1 + l + sovQenesis(uint64(l)) - } - } - return n -} - -func sovQenesis(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozQenesis(x uint64) (n int) { - return sovQenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *GenesisState) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQenesis - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQenesis - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthQenesis - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthQenesis - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Params == nil { - m.Params = &Params{} - } - if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Markets", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQenesis - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthQenesis - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthQenesis - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Markets = append(m.Markets, &Market{}) - if err := m.Markets[len(m.Markets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Orders", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQenesis - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthQenesis - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthQenesis - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Orders = append(m.Orders, &Order{}) - if err := m.Orders[len(m.Orders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipQenesis(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthQenesis - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipQenesis(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowQenesis - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowQenesis - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowQenesis - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthQenesis - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupQenesis - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthQenesis - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthQenesis = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowQenesis = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupQenesis = fmt.Errorf("proto: unexpected end of group") -) From bf5cd7853c34c8753ba5e83372066590d0839324 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 25 Aug 2023 18:51:10 -0600 Subject: [PATCH 016/309] [1658]: Create GetMarketAddress. --- x/exchange/keys.go | 13 +++++++++++ x/exchange/keys_test.go | 49 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 x/exchange/keys_test.go diff --git a/x/exchange/keys.go b/x/exchange/keys.go index 4c2afb7bb4..feee4d7eda 100644 --- a/x/exchange/keys.go +++ b/x/exchange/keys.go @@ -1,5 +1,13 @@ package exchange +import ( + "fmt" + + "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + const ( // ModuleName is the name of the exchange module. ModuleName = "exchange" @@ -7,3 +15,8 @@ const ( // StoreKey is the store key string for the exchange module. StoreKey = ModuleName ) + +// GetMarketAddress returns the module account address for the given marketID. +func GetMarketAddress(marketID uint32) sdk.AccAddress { + return sdk.AccAddress(crypto.AddressHash([]byte(fmt.Sprintf("%s/%d", ModuleName, marketID)))) +} diff --git a/x/exchange/keys_test.go b/x/exchange/keys_test.go new file mode 100644 index 0000000000..8dd77894fb --- /dev/null +++ b/x/exchange/keys_test.go @@ -0,0 +1,49 @@ +package exchange + +import ( + "encoding/hex" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestGetMarketAddress(t *testing.T) { + // The goal here is to make sure that, a) they're significantly different + // from each other, b) they never change, and c) they're 20 bytes long. + // I got the expected values from running each entry and copying the result. + // They should never change unexpectedly, though. + + // I'm defining them as hex strings that get converted to byte slices instead + // of using a bech32 string because I want the actual bytes to be more visible. + // Plus, a hex string is the most compact way to express them as bytes. + addrFromHex := func(h string) sdk.AccAddress { + rv, err := hex.DecodeString(h) + require.NoError(t, err, "hex.DecodeString(%q)", h) + return rv + } + + tests := []struct { + marketID uint32 + addr sdk.AccAddress + }{ + {marketID: 1, addr: addrFromHex("D18FB05DF1B728AFDD46597395A7633328D87A94")}, + {marketID: 2, addr: addrFromHex("ADAF1E5727865007ECD20745A56550E8DB291C3F")}, + {marketID: 3, addr: addrFromHex("71D44FD9168EAE5442A2706C7E6165CDEE19C60D")}, + {marketID: 10, addr: addrFromHex("BA24034E603CB283114FDDFF5A187B147CC3522F")}, + {marketID: 128, addr: addrFromHex("AD15421CCD165A4354C890114A1C00595FF4814C")}, + {marketID: 32_768, addr: addrFromHex("B77B8B74C236B3E9661590C5D783B905E871F105")}, + {marketID: 2_147_483_648, addr: addrFromHex("9B598419EBEC767EEEE35873D265E1FF18E98101")}, + {marketID: 4_294_967_295, addr: addrFromHex("9C1E5A70066142E072369976E9B94044BD10A943")}, + } + + for _, tc := range tests { + t.Run(fmt.Sprintf("market id %d", tc.marketID), func(t *testing.T) { + addr := GetMarketAddress(tc.marketID) + assert.Equal(t, tc.addr, addr, "GetMarketAddress(%d)", tc.marketID) + }) + } +} From c533b64a4eda5bbc8252a6c066236496841e707b Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 25 Aug 2023 18:53:43 -0600 Subject: [PATCH 017/309] [1658]: Stub out the msg funcs and create the codec. --- x/exchange/codec.go | 33 +++++++ x/exchange/market.go | 5 +- x/exchange/module/module.go | 4 +- x/exchange/msg.go | 182 ++++++++++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 x/exchange/codec.go create mode 100644 x/exchange/msg.go diff --git a/x/exchange/codec.go b/x/exchange/codec.go new file mode 100644 index 0000000000..98ea4fcba6 --- /dev/null +++ b/x/exchange/codec.go @@ -0,0 +1,33 @@ +package exchange + +import ( + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// RegisterInterfaces registers implementations for the tx messages +func RegisterInterfaces(registry types.InterfaceRegistry) { + messages := make([]proto.Message, len(allRequestMsgs)) + for i, msg := range allRequestMsgs { + messages[i] = msg + } + registry.RegisterImplementations((*sdk.Msg)(nil), messages...) + + registry.RegisterInterface( + "provenance.exchange.v1.MarketAccount", + (*authtypes.AccountI)(nil), + &MarketAccount{}, + ) + + registry.RegisterInterface( + "provenance.exchange.v1.MarketAccount", + (*authtypes.GenesisAccount)(nil), + &MarketAccount{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} diff --git a/x/exchange/market.go b/x/exchange/market.go index af761406e8..3bfa9eef91 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -7,6 +7,7 @@ import ( "strings" sdk "github.com/cosmos/cosmos-sdk/types" + nametypes "github.com/provenance-io/provenance/x/name/types" ) @@ -182,7 +183,7 @@ func ValidateAccessGrants(accessGrants []*AccessGrant) error { dups := make(map[string]bool) for _, ag := range accessGrants { if seen[ag.Address] && !dups[ag.Address] { - errs = append(errs, fmt.Errorf("%s appears in multiple access grant entries")) + errs = append(errs, fmt.Errorf("%s appears in multiple access grant entries", ag.Address)) dups[ag.Address] = true } seen[ag.Address] = true @@ -226,7 +227,7 @@ func (p Permission) Validate() error { } _, exists := Permission_name[int32(p)] if !exists { - return fmt.Errorf("permission %d does not exist") + return fmt.Errorf("permission %d does not exist", p) } return nil } diff --git a/x/exchange/module/module.go b/x/exchange/module/module.go index b906e08b39..1826aef65f 100644 --- a/x/exchange/module/module.go +++ b/x/exchange/module/module.go @@ -84,8 +84,8 @@ func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx sdkclient.Context, m } // RegisterInterfaces registers the exchange module's interface types -func (AppModuleBasic) RegisterInterfaces(_ cdctypes.InterfaceRegistry) { - // TODO[1658]: Create something for RegisterInterfaces to call. +func (AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + exchange.RegisterInterfaces(registry) } // RegisterLegacyAminoCodec registers the exchange module's types for the given codec. diff --git a/x/exchange/msg.go b/x/exchange/msg.go new file mode 100644 index 0000000000..ae53ca01b9 --- /dev/null +++ b/x/exchange/msg.go @@ -0,0 +1,182 @@ +package exchange + +import sdk "github.com/cosmos/cosmos-sdk/types" + +var allRequestMsgs = []sdk.Msg{ + (*MsgCreateAskRequest)(nil), + (*MsgCreateBidRequest)(nil), + (*MsgCancelOrderRequest)(nil), + (*MsgFillBidsRequest)(nil), + (*MsgFillAsksRequest)(nil), + (*MsgMarketSettleRequest)(nil), + (*MsgMarketWithdrawRequest)(nil), + (*MsgMarketUpdateDetailsRequest)(nil), + (*MsgMarketUpdateEnabledRequest)(nil), + (*MsgMarketUpdateUserSettleRequest)(nil), + (*MsgMarketManagePermissionsRequest)(nil), + (*MsgMarketManageReqAttrsRequest)(nil), + (*MsgCreateMarketRequest)(nil), + (*MsgGovCreateMarketRequest)(nil), + (*MsgGovManageFeesRequest)(nil), + (*MsgGovUpdateParamsRequest)(nil), +} + +func (m MsgCreateAskRequest) ValidateBasic() error { + // TODO[1658]: MsgCreateAskRequest.ValidateBasic() + return nil +} + +func (m MsgCreateAskRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgCreateAskRequest.GetSigners + panic("not implemented") +} + +func (m MsgCreateBidRequest) ValidateBasic() error { + // TODO[1658]: MsgCreateBidRequest.ValidateBasic() + return nil +} + +func (m MsgCreateBidRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgCreateBidRequest.GetSigners + panic("not implemented") +} + +func (m MsgCancelOrderRequest) ValidateBasic() error { + // TODO[1658]: MsgCancelOrderRequest.ValidateBasic() + return nil +} + +func (m MsgCancelOrderRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgCancelOrderRequest.GetSigners + panic("not implemented") +} + +func (m MsgFillBidsRequest) ValidateBasic() error { + // TODO[1658]: MsgFillBidsRequest.ValidateBasic() + return nil +} + +func (m MsgFillBidsRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgFillBidsRequest.GetSigners + panic("not implemented") +} + +func (m MsgFillAsksRequest) ValidateBasic() error { + // TODO[1658]: MsgFillAsksRequest.ValidateBasic() + return nil +} + +func (m MsgFillAsksRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgFillAsksRequest.GetSigners + panic("not implemented") +} + +func (m MsgMarketSettleRequest) ValidateBasic() error { + // TODO[1658]: MsgMarketSettleRequest.ValidateBasic() + return nil +} + +func (m MsgMarketSettleRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgMarketSettleRequest.GetSigners + panic("not implemented") +} + +func (m MsgMarketWithdrawRequest) ValidateBasic() error { + // TODO[1658]: MsgMarketWithdrawRequest.ValidateBasic() + return nil +} + +func (m MsgMarketWithdrawRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgMarketWithdrawRequest.GetSigners + panic("not implemented") +} + +func (m MsgMarketUpdateDetailsRequest) ValidateBasic() error { + // TODO[1658]: MsgMarketUpdateDetailsRequest.ValidateBasic() + return nil +} + +func (m MsgMarketUpdateDetailsRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgMarketUpdateDetailsRequest.GetSigners + panic("not implemented") +} + +func (m MsgMarketUpdateEnabledRequest) ValidateBasic() error { + // TODO[1658]: MsgMarketUpdateEnabledRequest.ValidateBasic() + return nil +} + +func (m MsgMarketUpdateEnabledRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgMarketUpdateEnabledRequest.GetSigners + panic("not implemented") +} + +func (m MsgMarketUpdateUserSettleRequest) ValidateBasic() error { + // TODO[1658]: MsgMarketUpdateUserSettleRequest.ValidateBasic() + return nil +} + +func (m MsgMarketUpdateUserSettleRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgMarketUpdateUserSettleRequest.GetSigners + panic("not implemented") +} + +func (m MsgMarketManagePermissionsRequest) ValidateBasic() error { + // TODO[1658]: MsgMarketManagePermissionsRequest.ValidateBasic() + return nil +} + +func (m MsgMarketManagePermissionsRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgMarketManagePermissionsRequest.GetSigners + panic("not implemented") +} + +func (m MsgMarketManageReqAttrsRequest) ValidateBasic() error { + // TODO[1658]: MsgMarketManageReqAttrsRequest.ValidateBasic() + return nil +} + +func (m MsgMarketManageReqAttrsRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgMarketManageReqAttrsRequest.GetSigners + panic("not implemented") +} + +func (m MsgCreateMarketRequest) ValidateBasic() error { + // TODO[1658]: MsgCreateMarketRequest.ValidateBasic() + return nil +} + +func (m MsgCreateMarketRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgCreateMarketRequest.GetSigners + panic("not implemented") +} + +func (m MsgGovCreateMarketRequest) ValidateBasic() error { + // TODO[1658]: MsgGovCreateMarketRequest.ValidateBasic() + return nil +} + +func (m MsgGovCreateMarketRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgGovCreateMarketRequest.GetSigners + panic("not implemented") +} + +func (m MsgGovManageFeesRequest) ValidateBasic() error { + // TODO[1658]: MsgGovManageFeesRequest.ValidateBasic() + return nil +} + +func (m MsgGovManageFeesRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgGovManageFeesRequest.GetSigners + panic("not implemented") +} + +func (m MsgGovUpdateParamsRequest) ValidateBasic() error { + // TODO[1658]: MsgGovUpdateParamsRequest.ValidateBasic() + return nil +} + +func (m MsgGovUpdateParamsRequest) GetSigners() []sdk.AccAddress { + // TODO[1658]: MsgGovUpdateParamsRequest.GetSigners + panic("not implemented") +} From 133f3afff3cfd842047f97274ce21592c6a72e1e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 25 Aug 2023 18:58:08 -0600 Subject: [PATCH 018/309] [1658]: Fix some variable names and add a nolint where it's being dumb. --- x/exchange/market.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index 3bfa9eef91..237e280885 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -16,10 +16,10 @@ const ( MaxName = 250 // MaxDescription is the maximum length of MarketDetails.Description MaxDescription = 2000 - // MaxWebsiteUrl is the maximum length of MarketDetails.WebsiteUrl - MaxWebsiteUrl = 200 - // MaxIconUri is the maximum length of MarketDetails.IconUri - MaxIconUri = 2000 + // MaxWebsiteURL is the maximum length of MarketDetails.WebsiteUrl + MaxWebsiteURL = 200 + // MaxIconURI is the maximum length of MarketDetails.IconUri + MaxIconURI = 2000 ) // Validate makes sure that everything in this Market is valid. @@ -59,11 +59,11 @@ func (d MarketDetails) Validate() error { if len(d.Description) > MaxDescription { errs = append(errs, fmt.Errorf("description length %d exceeds maximum length of %d", len(d.Description), MaxDescription)) } - if len(d.WebsiteUrl) > MaxWebsiteUrl { - errs = append(errs, fmt.Errorf("website_url length %d exceeds maximum length of %d", len(d.WebsiteUrl), MaxWebsiteUrl)) + if len(d.WebsiteUrl) > MaxWebsiteURL { + errs = append(errs, fmt.Errorf("website_url length %d exceeds maximum length of %d", len(d.WebsiteUrl), MaxWebsiteURL)) } - if len(d.IconUri) > MaxIconUri { - errs = append(errs, fmt.Errorf("icon_uri length %d exceeds maximum length of %d", len(d.IconUri), MaxIconUri)) + if len(d.IconUri) > MaxIconURI { + errs = append(errs, fmt.Errorf("icon_uri length %d exceeds maximum length of %d", len(d.IconUri), MaxIconURI)) } return errors.Join(errs...) } @@ -178,7 +178,7 @@ func (r FeeRatio) Validate() error { // ValidateAccessGrants returns an error if any of the provided access grants are invalid. func ValidateAccessGrants(accessGrants []*AccessGrant) error { - var errs []error + var errs []error //nolint:prealloc // Stupid linter. It'll almost always be nil. No reason to pre-allocate anything. seen := make(map[string]bool) dups := make(map[string]bool) for _, ag := range accessGrants { From 6e581a9eb3b8672f57457c6982955d3c3b48b279 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 25 Aug 2023 19:01:34 -0600 Subject: [PATCH 019/309] [1658]: Assert that the MarketAccount implements the AccountI and GenesisAccount interfaces. Nothing to actually implement though since MarketAccount extends BaseAccount. --- x/exchange/market.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/x/exchange/market.go b/x/exchange/market.go index 237e280885..dcd9abe205 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -7,6 +7,7 @@ import ( "strings" sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" nametypes "github.com/provenance-io/provenance/x/name/types" ) @@ -22,6 +23,11 @@ const ( MaxIconURI = 2000 ) +var ( + _ authtypes.AccountI = (*MarketAccount)(nil) + _ authtypes.GenesisAccount = (*MarketAccount)(nil) +) + // Validate makes sure that everything in this Market is valid. // The MarketId is allowed to be zero in here. // Some uses might require it to have a value, but that check is left up to the caller. From fbef345966ced0f7bcb9a438e9f1b8275c51488d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 25 Aug 2023 19:11:06 -0600 Subject: [PATCH 020/309] [1658]: Add a comment about wildcards to the req attrs thing. --- docs/proto-docs.md | 12 ++++++++++-- proto/provenance/exchange/v1/market.proto | 12 ++++++++++-- x/exchange/market.pb.go | 12 ++++++++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 17b18274f1..5c5bee45f6 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1590,8 +1590,16 @@ Market contains all information about a market. | `accepting_orders` | [bool](#bool) | | accepting_orders is whether this market is allowing orders to be created for it. | | `allow_user_settlement` | [bool](#bool) | | allow_user_settlement is whether this market allows users to initiate their own settlements. For example, the FillBids and FillAsks endpoints are available if and only if this is true. The MarketSettle endpoint is only available to market actors regardless of the value of this field. | | `access_grants` | [AccessGrant](#provenance.exchange.v1.AccessGrant) | repeated | access_grants is the list of addresses and permissions granted for this market. | -| `req_attr_create_ask` | [string](#string) | repeated | req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. TODO[1658]: Add comment about wildcards. | -| `req_attr_create_bid` | [string](#string) | repeated | req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. TODO[1658]: Add comment about wildcards. | +| `req_attr_create_ask` | [string](#string) | repeated | req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. + +An entry that starts with "*." will match any attributes that end with the rest of it. E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a". + +An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. | +| `req_attr_create_bid` | [string](#string) | repeated | req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. + +An entry that starts with "*." will match any attributes that end with the rest of it. E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a". + +An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. | diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index f7fce9c67e..c81a4f828e 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -82,10 +82,18 @@ message Market { // access_grants is the list of addresses and permissions granted for this market. repeated AccessGrant access_grants = 11; // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. - // TODO[1658]: Add comment about wildcards. + // + // An entry that starts with "*." will match any attributes that end with the rest of it. + // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a". + // + // An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. repeated string req_attr_create_ask = 12; // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. - // TODO[1658]: Add comment about wildcards. + // + // An entry that starts with "*." will match any attributes that end with the rest of it. + // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a". + // + // An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. repeated string req_attr_create_bid = 13; } diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index 82e3b56b28..415899d18d 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -228,10 +228,18 @@ type Market struct { // access_grants is the list of addresses and permissions granted for this market. AccessGrants []*AccessGrant `protobuf:"bytes,11,rep,name=access_grants,json=accessGrants,proto3" json:"access_grants,omitempty"` // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. - // TODO[1658]: Add comment about wildcards. + // + // An entry that starts with "*." will match any attributes that end with the rest of it. + // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a". + // + // An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. ReqAttrCreateAsk []string `protobuf:"bytes,12,rep,name=req_attr_create_ask,json=reqAttrCreateAsk,proto3" json:"req_attr_create_ask,omitempty"` // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. - // TODO[1658]: Add comment about wildcards. + // + // An entry that starts with "*." will match any attributes that end with the rest of it. + // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a". + // + // An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. ReqAttrCreateBid []string `protobuf:"bytes,13,rep,name=req_attr_create_bid,json=reqAttrCreateBid,proto3" json:"req_attr_create_bid,omitempty"` } From 4d7a8697e04e1e857d13f9150680e4bcfcdba2ae Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 25 Aug 2023 19:38:18 -0600 Subject: [PATCH 021/309] [1658]: TestOrder_Validate. --- x/exchange/orders_test.go | 45 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index f5d94b87b2..a898fab4ea 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -296,7 +296,50 @@ func TestOrder_OrderTypeByte(t *testing.T) { } } -// TODO[1658]: func TestOrder_Validate(t *testing.T) +func TestOrder_Validate(t *testing.T) { + // Annoyingly, sdkmath.Int{} (the zero value) causes panic whenever you + // try to do anything with it. So we have to give it something. + zeroCoin := sdk.Coin{Amount: sdkmath.ZeroInt()} + tests := []struct { + name string + Order *Order + exp []string + }{ + { + name: "order id is zero", + Order: NewOrder(0), + exp: []string{"invalid order id: must not be zero"}, + }, + { + name: "unknown order type", + Order: &Order{OrderId: 1, Order: &unknownOrderType{}}, + exp: []string{"unknown order type *exchange.unknownOrderType"}, + }, + { + name: "ask order error", + Order: NewOrder(1).WithAsk(&AskOrder{MarketId: 0, Price: zeroCoin}), + exp: []string{"invalid market id: must not be zero"}, + }, + { + name: "bid order error", + Order: NewOrder(1).WithBid(&BidOrder{MarketId: 0, Price: zeroCoin}), + exp: []string{"invalid market id: must not be zero"}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.Order.Validate() + if len(tc.exp) > 0 { + if assert.Error(t, err, "Validate() error") { + for _, exp := range tc.exp { + assert.ErrorContains(t, err, exp, "Validate() error\nExpecting: %q", exp) + } + } + } + }) + } +} // TODO[1658]: func TestAskOrder_Validate(t *testing.T) From 2efb2b089e13e3286c4c350fd1c30f462c12df24 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 10:46:49 -0600 Subject: [PATCH 022/309] [1658]: Unit tests on AskOrder and BidOrder Validate methods. --- x/exchange/orders_test.go | 513 +++++++++++++++++++++++++++++++++++++- 1 file changed, 501 insertions(+), 12 deletions(-) diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index a898fab4ea..5b6c2a11e2 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -211,11 +211,6 @@ func TestOrder_OrderType(t *testing.T) { order: NewOrder(2).WithBid(&BidOrder{}), expected: OrderTypeBid, }, - { - name: "nil base order", - order: nil, - expPanic: "OrderType() missing case for ", - }, { name: "nil inside order", order: NewOrder(3), @@ -235,6 +230,7 @@ func TestOrder_OrderType(t *testing.T) { actual = tc.order.OrderType() } + // TODO[1658]: Refactor to use testutils.RequirePanicEquals(t, testFunc, tc.expPanic, "OrderType") if tc.expPanic != nil { require.PanicsWithValue(t, tc.expPanic, testFunc, "OrderType") } else { @@ -262,11 +258,6 @@ func TestOrder_OrderTypeByte(t *testing.T) { order: NewOrder(2).WithBid(&BidOrder{}), expected: OrderTypeByteBid, }, - { - name: "nil base order", - order: nil, - expPanic: "OrderTypeByte() missing case for ", - }, { name: "nil inside order", order: NewOrder(3), @@ -286,6 +277,7 @@ func TestOrder_OrderTypeByte(t *testing.T) { actual = tc.order.OrderTypeByte() } + // TODO[1658]: Refactor to use testutils.RequirePanicEquals(t, testFunc, tc.expPanic, "OrderTypeByte") if tc.expPanic != nil { require.PanicsWithValue(t, tc.expPanic, testFunc, "OrderTypeByte") } else { @@ -330,17 +322,514 @@ func TestOrder_Validate(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { err := tc.Order.Validate() + + // TODO[1658]: Replace this with testutil.AssertErrorContents(t, err, tc.exp, "Validate() error") + if len(tc.exp) > 0 { + if assert.Error(t, err, "Validate() error") { + for _, exp := range tc.exp { + assert.ErrorContains(t, err, exp, "Validate() error\nExpecting: %q", exp) + } + } + } else { + assert.NoError(t, err, "Validate() error") + } + }) + } +} + +func TestAskOrder_Validate(t *testing.T) { + coin := func(amount int64, denom string) *sdk.Coin { + return &sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + coins := func(coins string) sdk.Coins { + rv, err := sdk.ParseCoinsNormalized(coins) + require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) + return rv + } + + tests := []struct { + name string + order AskOrder + exp []string + }{ + { + name: "control", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("control_address_____").String(), + Assets: coins("99bender"), + Price: *coin(42, "farnsworth"), + SellerSettlementFlatFee: coin(1, "farnsworth"), + AllowPartial: false, + }, + exp: nil, + }, + { + name: "allow partial", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("control_address_____").String(), + Assets: coins("99bender"), + Price: *coin(42, "farnsworth"), + SellerSettlementFlatFee: coin(1, "farnsworth"), + AllowPartial: true, + }, + exp: nil, + }, + { + name: "nil seller settlement flat fee", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("control_address_____").String(), + Assets: coins("99bender"), + Price: *coin(42, "farnsworth"), + SellerSettlementFlatFee: nil, + AllowPartial: false, + }, + exp: nil, + }, + { + name: "multiple assets", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: coins("12amy,99bender,8fry,112leela,1zoidberg"), + Price: *coin(42, "farnsworth"), + }, + exp: nil, + }, + { + name: "market id zero", + order: AskOrder{ + MarketId: 0, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: *coin(42, "farnsworth"), + }, + exp: []string{"invalid market id: must not be zero"}, + }, + { + name: "invalid seller", + order: AskOrder{ + MarketId: 1, + Seller: "shady_address_______", + Assets: coins("99bender"), + Price: *coin(42, "farnsworth"), + }, + exp: []string{"invalid seller: ", "invalid separator index -1"}, + }, + { + name: "no seller", + order: AskOrder{ + MarketId: 1, + Seller: "", + Assets: coins("99bender"), + Price: *coin(42, "farnsworth"), + }, + exp: []string{"invalid seller: ", "empty address string is not allowed"}, + }, + { + name: "zero price", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: *coin(0, "farnsworth"), + }, + exp: []string{"invalid price: cannot be zero"}, + }, + { + name: "negative price", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: *coin(-24, "farnsworth"), + }, + exp: []string{"invalid price: ", "negative coin amount: -24"}, + }, + { + name: "invalid price denom", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: *coin(42, "7"), + }, + exp: []string{"invalid price: ", "invalid denom: 7"}, + }, + { + name: "zero amount in assets", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: sdk.Coins{*coin(99, "bender"), *coin(0, "leela"), *coin(1, "zoidberg")}, + Price: *coin(42, "farnsworth"), + }, + exp: []string{"invalid assets: ", "coin leela amount is not positive"}, + }, + { + name: "negative amount in assets", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: sdk.Coins{*coin(99, "bender"), *coin(-1, "leela"), *coin(1, "zoidberg")}, + Price: *coin(42, "farnsworth"), + }, + exp: []string{"invalid assets: ", "coin leela amount is not positive"}, + }, + { + name: "invalid denom in assets", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: sdk.Coins{*coin(99, "bender"), *coin(1, "x"), *coin(1, "zoidberg")}, + Price: *coin(42, "farnsworth"), + }, + exp: []string{"invalid assets: ", "invalid denom: x"}, + }, + { + name: "nil assets", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: nil, + Price: *coin(42, "farnsworth"), + }, + exp: []string{"invalid assets: must not be empty"}, + }, + { + name: "empty assets", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: sdk.Coins{}, + Price: *coin(42, "farnsworth"), + }, + exp: []string{"invalid assets: must not be empty"}, + }, + { + name: "price denom in assets", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender,2farnsworth,44amy"), + Price: *coin(42, "farnsworth"), + }, + exp: []string{"invalid assets: cannot contain price denom farnsworth"}, + }, + { + name: "invalid seller settlement flat fee denom", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: *coin(42, "farnsworth"), + SellerSettlementFlatFee: coin(13, "x"), + }, + exp: []string{"invalid seller settlement flat fee: ", "invalid denom: x"}, + }, + { + name: "zero seller settlement flat fee denom", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: *coin(42, "farnsworth"), + SellerSettlementFlatFee: coin(0, "nibbler"), + }, + exp: []string{"invalid seller settlement flat fee: ", "nibbler amount cannot be zero"}, + }, + { + name: "negative seller settlement flat fee", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: *coin(42, "farnsworth"), + SellerSettlementFlatFee: coin(-3, "nibbler"), + }, + exp: []string{"invalid seller settlement flat fee: ", "negative coin amount: -3"}, + }, + { + name: "multiple problems", + order: AskOrder{ + Price: *coin(0, ""), + SellerSettlementFlatFee: coin(0, ""), + }, + exp: []string{ + "invalid market id: ", + "invalid seller: ", + "invalid price: ", + "invalid assets: ", + "invalid seller settlement flat fee: ", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.order.Validate() + + // TODO[1658]: Replace this with testutil.AssertErrorContents(t, err, tc.exp, "Validate() error") if len(tc.exp) > 0 { if assert.Error(t, err, "Validate() error") { for _, exp := range tc.exp { assert.ErrorContains(t, err, exp, "Validate() error\nExpecting: %q", exp) } } + } else { + assert.NoError(t, err, "Validate() error") } }) } } -// TODO[1658]: func TestAskOrder_Validate(t *testing.T) +func TestBidOrder_Validate(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + coins := func(coins string) sdk.Coins { + rv, err := sdk.ParseCoinsNormalized(coins) + require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) + return rv + } -// TODO[1658]: func TestBidOrder_Validate(t *testing.T) + tests := []struct { + name string + order BidOrder + exp []string + }{ + { + name: "control", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("control_address_____").String(), + Assets: coins("99bender"), + Price: coin(42, "farnsworth"), + BuyerSettlementFees: coins("1farnsworth"), + AllowPartial: false, + }, + exp: nil, + }, + { + name: "allow partial", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("control_address_____").String(), + Assets: coins("99bender"), + Price: coin(42, "farnsworth"), + BuyerSettlementFees: coins("1farnsworth"), + AllowPartial: true, + }, + exp: nil, + }, + { + name: "nil buyer settlement fees", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("control_address_____").String(), + Assets: coins("99bender"), + Price: coin(42, "farnsworth"), + BuyerSettlementFees: nil, + AllowPartial: false, + }, + exp: nil, + }, + { + name: "empty buyer settlement fees", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("control_address_____").String(), + Assets: coins("99bender"), + Price: coin(42, "farnsworth"), + BuyerSettlementFees: sdk.Coins{}, + AllowPartial: false, + }, + exp: nil, + }, + { + name: "multiple assets", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: coins("12amy,99bender,8fry,112leela,1zoidberg"), + Price: coin(42, "farnsworth"), + }, + exp: nil, + }, + { + name: "market id zero", + order: BidOrder{ + MarketId: 0, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: coin(42, "farnsworth"), + }, + exp: []string{"invalid market id: must not be zero"}, + }, + { + name: "invalid buyer", + order: BidOrder{ + MarketId: 1, + Buyer: "shady_address_______", + Assets: coins("99bender"), + Price: coin(42, "farnsworth"), + }, + exp: []string{"invalid buyer: ", "invalid separator index -1"}, + }, + { + name: "no buyer", + order: BidOrder{ + MarketId: 1, + Buyer: "", + Assets: coins("99bender"), + Price: coin(42, "farnsworth"), + }, + exp: []string{"invalid buyer: ", "empty address string is not allowed"}, + }, + { + name: "zero price", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: coin(0, "farnsworth"), + }, + exp: []string{"invalid price: cannot be zero"}, + }, + { + name: "negative price", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: coin(-24, "farnsworth"), + }, + exp: []string{"invalid price: ", "negative coin amount: -24"}, + }, + { + name: "invalid price denom", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: coin(42, "7"), + }, + exp: []string{"invalid price: ", "invalid denom: 7"}, + }, + { + name: "zero amount in assets", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: sdk.Coins{coin(99, "bender"), coin(0, "leela"), coin(1, "zoidberg")}, + Price: coin(42, "farnsworth"), + }, + exp: []string{"invalid assets: ", "coin leela amount is not positive"}, + }, + { + name: "negative amount in assets", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: sdk.Coins{coin(99, "bender"), coin(-1, "leela"), coin(1, "zoidberg")}, + Price: coin(42, "farnsworth"), + }, + exp: []string{"invalid assets: ", "coin leela amount is not positive"}, + }, + { + name: "invalid denom in assets", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: sdk.Coins{coin(99, "bender"), coin(1, "x"), coin(1, "zoidberg")}, + Price: coin(42, "farnsworth"), + }, + exp: []string{"invalid assets: ", "invalid denom: x"}, + }, + { + name: "nil assets", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: nil, + Price: coin(42, "farnsworth"), + }, + exp: []string{"invalid assets: must not be empty"}, + }, + { + name: "empty assets", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: sdk.Coins{}, + Price: coin(42, "farnsworth"), + }, + exp: []string{"invalid assets: must not be empty"}, + }, + { + name: "price denom in assets", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender,2farnsworth,44amy"), + Price: coin(42, "farnsworth"), + }, + exp: []string{"invalid assets: cannot contain price denom farnsworth"}, + }, + { + name: "invalid denom in buyer settlement fees", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: coin(42, "farnsworth"), + BuyerSettlementFees: sdk.Coins{coin(1, "farnsworth"), coin(2, "x")}, + }, + exp: []string{"invalid buyer settlement fees: ", "invalid denom: x"}, + }, + { + name: "negative buyer settlement fees", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("another_address_____").String(), + Assets: coins("99bender"), + Price: coin(42, "farnsworth"), + BuyerSettlementFees: sdk.Coins{coin(3, "farnsworth"), coin(-3, "nibbler")}, + }, + exp: []string{"invalid buyer settlement fees: ", "coin nibbler amount is not positive"}, + }, + { + name: "multiple problems", + order: BidOrder{ + Price: coin(0, ""), + BuyerSettlementFees: sdk.Coins{coin(0, "")}, + }, + exp: []string{ + "invalid market id: ", + "invalid buyer: ", + "invalid price: ", + "invalid assets: ", + "invalid buyer settlement fees: ", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.order.Validate() + + // TODO[1658]: Replace this with testutil.AssertErrorContents(t, err, tc.exp, "Validate() error") + if len(tc.exp) > 0 { + if assert.Error(t, err, "Validate() error") { + for _, exp := range tc.exp { + assert.ErrorContains(t, err, exp, "Validate() error\nExpecting: %q", exp) + } + } + } else { + assert.NoError(t, err, "Validate() error") + } + }) + } +} From eca977f0d2f9dd19601f5606e488b34e3c78a5a0 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 11:58:22 -0600 Subject: [PATCH 023/309] [1658]: Write some req attr matching functions and their unit tests. --- x/exchange/market.go | 39 ++- x/exchange/market_test.go | 500 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 538 insertions(+), 1 deletion(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index dcd9abe205..ac79e133bd 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -100,7 +100,7 @@ func ValidateSellerFeeRatios(ratios []*FeeRatio) error { func ValidateBuyerFeeRatios(ratios []*FeeRatio) error { seen := make(map[string]bool) dups := make(map[string]bool) - var errs []error + var errs []error //nolint:prealloc // ideally always empty, so no reason to preallocate anything. for _, ratio := range ratios { key := ratio.Price.Denom + ":" + ratio.Fee.Denom if seen[key] && !dups[key] { @@ -339,3 +339,40 @@ func IsValidReqAttr(reqAttr string) bool { // Nothing left that might save it. return false } + +// FindUnmatchedReqAttrs returns all required attributes that don't have a match in the provided account attributes. +// This assumes that reqAttrs and accAttrs have all been normalized. +func FindUnmatchedReqAttrs(reqAttrs, accAttrs []string) []string { + var rv []string + for _, reqAttr := range reqAttrs { + if !HasReqAttrMatch(reqAttr, accAttrs) { + rv = append(rv, reqAttr) + } + } + return rv +} + +// HasReqAttrMatch returns true if one (or more) accAttrs is a match for the provided required attribute. +// This assumes that reqAttr and accAttrs have all been normalized. +func HasReqAttrMatch(reqAttr string, accAttrs []string) bool { + for _, accAttr := range accAttrs { + if IsReqAttrMatch(reqAttr, accAttr) { + return true + } + } + return false +} + +// IsReqAttrMatch returns true if the provide account attribute is a match for the given required attribute. +// This assumes that reqAttr and accAttr have both been normalized. +func IsReqAttrMatch(reqAttr, accAttr string) bool { + if len(reqAttr) == 0 { + return false + } + if strings.HasPrefix(reqAttr, "*.") { + // reqAttr[1:] is used here (instead of [2:]) because we need that . to be + // part of the match. Otherwise "*.b.a" would match "c.b.a" as well as "c.evilb.a". + return strings.HasSuffix(accAttr, reqAttr[1:]) + } + return reqAttr == accAttr +} diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 1aadfdcc6b..58f4752631 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -1,5 +1,11 @@ package exchange +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + // TODO[1658]: func TestMarket_Validate(t *testing.T) // TODO[1658]: func TestMarketDetails_Validate(t *testing.T) @@ -31,3 +37,497 @@ package exchange // TODO[1658]: func TestValidateReqAttrs(t *testing.T) // TODO[1658]: func TestIsValidReqAttr(t *testing.T) + +func TestFindUnmatchedReqAttrs(t *testing.T) { + tests := []struct { + name string + reqAttrs []string + accAttrs []string + exp []string + }{ + { + name: "nil req attrs", + reqAttrs: nil, + accAttrs: []string{"one"}, + exp: nil, + }, + { + name: "empty req attrs", + reqAttrs: []string{}, + accAttrs: []string{"one"}, + exp: nil, + }, + { + name: "one req attr no wildcard: in acc attrs", + reqAttrs: []string{"one"}, + accAttrs: []string{"one", "two"}, + exp: nil, + }, + { + name: "one req attr with wildcard: in acc attrs", + reqAttrs: []string{"*.one"}, + accAttrs: []string{"zero.one", "two"}, + exp: nil, + }, + { + name: "three req attrs: nil acc attrs", + reqAttrs: []string{"*.desk", "nickel.dime.quarter", "*.x.y.z"}, + accAttrs: nil, + exp: []string{"*.desk", "nickel.dime.quarter", "*.x.y.z"}, + }, + { + name: "three req attrs: empty acc attrs", + reqAttrs: []string{"*.desk", "nickel.dime.quarter", "*.x.y.z"}, + accAttrs: []string{}, + exp: []string{"*.desk", "nickel.dime.quarter", "*.x.y.z"}, + }, + { + name: "three req attrs: only first in acc attrs", + reqAttrs: []string{"*.desk", "nickel.dime.quarter", "*.x.y.z"}, + accAttrs: []string{"lamp.corner.desk"}, + exp: []string{"nickel.dime.quarter", "*.x.y.z"}, + }, + { + name: "three req attrs: only second in acc attrs", + reqAttrs: []string{"*.desk", "nickel.dime.quarter", "*.x.y.z"}, + accAttrs: []string{"nickel.dime.quarter"}, + exp: []string{"*.desk", "*.x.y.z"}, + }, + { + name: "three req attrs: only third in acc attrs", + reqAttrs: []string{"*.desk", "nickel.dime.quarter", "*.x.y.z"}, + accAttrs: []string{"w.x.y.z"}, + exp: []string{"*.desk", "nickel.dime.quarter"}, + }, + { + name: "three req attrs: missing first", + reqAttrs: []string{"*.desk", "nickel.dime.quarter", "*.x.y.z"}, + accAttrs: []string{"nickel.dime.quarter", "w.x.y.z"}, + exp: []string{"*.desk"}, + }, + { + name: "three req attrs: missing middle", + reqAttrs: []string{"*.desk", "nickel.dime.quarter", "*.x.y.z"}, + accAttrs: []string{"lamp.corner.desk", "w.x.y.z"}, + exp: []string{"nickel.dime.quarter"}, + }, + { + name: "three req attrs: missing last", + reqAttrs: []string{"*.desk", "nickel.dime.quarter", "*.x.y.z"}, + accAttrs: []string{"lamp.corner.desk", "nickel.dime.quarter"}, + exp: []string{"*.x.y.z"}, + }, + { + name: "three req attrs: has all", + reqAttrs: []string{"*.desk", "nickel.dime.quarter", "*.x.y.z"}, + accAttrs: []string{"just.some.first", "w.x.y.z", "other", "lamp.corner.desk", "random.entry", "nickel.dime.quarter", "what.is.this"}, + exp: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + unmatched := FindUnmatchedReqAttrs(tc.reqAttrs, tc.accAttrs) + assert.Equal(t, tc.exp, unmatched, "FindUnmatchedReqAttrs(%q, %q", tc.reqAttrs, tc.accAttrs) + }) + } +} + +func TestHasReqAttrMatch(t *testing.T) { + tests := []struct { + name string + reqAttr string + accAttrs []string + exp bool + }{ + { + name: "nil acc attrs", + reqAttr: "nickel.dime.quarter", + accAttrs: nil, + exp: false, + }, + { + name: "empty acc attrs", + reqAttr: "nickel.dime.quarter", + accAttrs: []string{}, + exp: false, + }, + { + name: "no wildcard: not in acc attrs", + reqAttr: "nickel.dime.quarter", + accAttrs: []string{ + "xnickel.dime.quarter", + "nickelx.dime.quarter", + "nickel.xdime.quarter", + "nickel.dimex.quarter", + "nickel.dime.xquarter", + "nickel.dime.quarterx", + "penny.nickel.dime.quarter", + "nickel.dime.quarter.dollar", + }, + exp: false, + }, + { + name: "no wildcard: only one in acc attrs", + reqAttr: "nickel.dime.quarter", + accAttrs: []string{"nickel.dime.quarter"}, + exp: true, + }, + { + name: "no wildcard: first in acc attrs", + reqAttr: "nickel.dime.quarter", + accAttrs: []string{ + "nickel.dime.quarter", + "xnickel.dime.quarter", + "nickelx.dime.quarter", + "nickel.xdime.quarter", + "nickel.dimex.quarter", + "nickel.dime.xquarter", + "nickel.dime.quarterx", + "penny.nickel.dime.quarter", + "nickel.dime.quarter.dollar", + }, + exp: true, + }, + { + name: "no wildcard: in middle of acc attrs", + reqAttr: "nickel.dime.quarter", + accAttrs: []string{ + "xnickel.dime.quarter", + "nickelx.dime.quarter", + "nickel.xdime.quarter", + "nickel.dimex.quarter", + "nickel.dime.quarter", + "nickel.dime.xquarter", + "nickel.dime.quarterx", + "penny.nickel.dime.quarter", + "nickel.dime.quarter.dollar", + }, + exp: true, + }, + { + name: "no wildcard: at end of acc attrs", + reqAttr: "nickel.dime.quarter", + accAttrs: []string{ + "xnickel.dime.quarter", + "nickelx.dime.quarter", + "nickel.xdime.quarter", + "nickel.dimex.quarter", + "nickel.dime.xquarter", + "nickel.dime.quarterx", + "penny.nickel.dime.quarter", + "nickel.dime.quarter.dollar", + "nickel.dime.quarter", + }, + exp: true, + }, + + { + name: "with wildcard: no match", + reqAttr: "*.dime.quarter", + accAttrs: []string{ + "dime.quarter", + "penny.xdime.quarter", + "penny.dimex.quarter", + "penny.dime.xquarter", + "penny.dime.quarterx", + "penny.quarter", + "penny.dime", + }, + exp: false, + }, + { + name: "with wildcard: matches only entry", + reqAttr: "*.dime.quarter", + accAttrs: []string{ + "penny.dime.quarter", + }, + exp: true, + }, + { + name: "with wildcard: matches first entry", + reqAttr: "*.dime.quarter", + accAttrs: []string{ + "penny.dime.quarter", + "dime.quarter", + "penny.xdime.quarter", + "penny.dimex.quarter", + "penny.dime.xquarter", + "penny.dime.quarterx", + "penny.quarter", + "penny.dime", + }, + exp: true, + }, + { + name: "with wildcard: matches middle entry", + reqAttr: "*.dime.quarter", + accAttrs: []string{ + "dime.quarter", + "penny.xdime.quarter", + "penny.dimex.quarter", + "penny.dime.xquarter", + "penny.dime.quarter", + "penny.dime.quarterx", + "penny.quarter", + "penny.dime", + }, + exp: true, + }, + { + name: "with wildcard: matches last entry", + reqAttr: "*.dime.quarter", + accAttrs: []string{ + "dime.quarter", + "penny.xdime.quarter", + "penny.dimex.quarter", + "penny.dime.xquarter", + "penny.dime.quarterx", + "penny.quarter", + "penny.dime", + "penny.dime.quarter", + }, + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + hasMatch := HasReqAttrMatch(tc.reqAttr, tc.accAttrs) + assert.Equal(t, tc.exp, hasMatch, "HasReqAttrMatch(%q, %q)", tc.reqAttr, tc.accAttrs) + }) + } +} + +func TestIsReqAttrMatch(t *testing.T) { + tests := []struct { + name string + reqAttr string + accAttr string + exp bool + }{ + { + name: "empty req attr", + reqAttr: "", + accAttr: "foo", + exp: false, + }, + { + name: "no wildcard: exact match", + reqAttr: "penny.dime.quarter", + accAttr: "penny.dime.quarter", + exp: true, + }, + { + name: "no wildcard: opposite order", + reqAttr: "penny.dime.quarter", + accAttr: "quarter.dime.penny", + exp: false, + }, + { + name: "no wildcard: missing 1st char from 1st name", + reqAttr: "penny.dime.quarter", + accAttr: "enny.dime.quarter", + exp: false, + }, + { + name: "no wildcard: missing last char from 1st name", + reqAttr: "penny.dime.quarter", + accAttr: "penn.dime.quarter", + exp: false, + }, + { + name: "no wildcard: missing 1st char from middle name", + reqAttr: "penny.dime.quarter", + accAttr: "penny.ime.quarter", + exp: false, + }, + { + name: "no wildcard: missing last char from middle name", + reqAttr: "penny.dime.quarter", + accAttr: "penny.dim.quarter", + exp: false, + }, + { + name: "no wildcard: missing 1st char from last name", + reqAttr: "penny.dime.quarter", + accAttr: "penny.dime.uarter", + exp: false, + }, + { + name: "no wildcard: missing last char from last name", + reqAttr: "penny.dime.quarter", + accAttr: "penny.dime.quarte", + exp: false, + }, + { + name: "no wildcard: extra char at start of first name", + reqAttr: "penny.dime.quarter", + accAttr: "xpenny.dime.quarter", + exp: false, + }, + { + name: "no wildcard: extra char at end of first name", + reqAttr: "penny.dime.quarter", + accAttr: "pennyx.dime.quarter", + exp: false, + }, + { + name: "no wildcard: extra char at start of middle name", + reqAttr: "penny.dime.quarter", + accAttr: "penny.xdime.quarter", + exp: false, + }, + { + name: "no wildcard: extra char at end of middle name", + reqAttr: "penny.dime.quarter", + accAttr: "penny.dimex.quarter", + exp: false, + }, + { + name: "no wildcard: extra char at start of last name", + reqAttr: "penny.dime.quarter", + accAttr: "penny.dime.xquarter", + exp: false, + }, + { + name: "no wildcard: extra char at end of first name", + reqAttr: "penny.dime.quarter", + accAttr: "penny.dime.quarterx", + exp: false, + }, + { + name: "no wildcard: extra name at start", + reqAttr: "penny.dime.quarter", + accAttr: "mil.penny.dime.quarter", + exp: false, + }, + { + name: "no wildcard: extra name at end", + reqAttr: "penny.dime.quarter", + accAttr: "penny.dime.quarter.dollar", + exp: false, + }, + { + name: "with wildcard: missing 1st char from 1st name", + reqAttr: "*.penny.dime.quarter", + accAttr: "enny.dime.quarter", + exp: false, + }, + { + name: "with wildcard: missing last char from 1st name", + reqAttr: "*.penny.dime.quarter", + accAttr: "penn.dime.quarter", + exp: false, + }, + { + name: "with wildcard: missing 1st char from middle name", + reqAttr: "*.penny.dime.quarter", + accAttr: "penny.ime.quarter", + exp: false, + }, + { + name: "with wildcard: missing last char from middle name", + reqAttr: "*.penny.dime.quarter", + accAttr: "penny.dim.quarter", + exp: false, + }, + { + name: "with wildcard: missing 1st char from last name", + reqAttr: "*.penny.dime.quarter", + accAttr: "penny.dime.uarter", + exp: false, + }, + { + name: "with wildcard: missing last char from last name", + reqAttr: "*.penny.dime.quarter", + accAttr: "penny.dime.quarte", + exp: false, + }, + { + name: "with wildcard: extra char at start of first name", + reqAttr: "*.penny.dime.quarter", + accAttr: "xpenny.dime.quarter", + exp: false, + }, + { + name: "with wildcard: extra char at end of first name", + reqAttr: "*.penny.dime.quarter", + accAttr: "pennyx.dime.quarter", + exp: false, + }, + { + name: "with wildcard: extra char at start of middle name", + reqAttr: "*.penny.dime.quarter", + accAttr: "penny.xdime.quarter", + exp: false, + }, + { + name: "with wildcard: extra char at end of middle name", + reqAttr: "*.penny.dime.quarter", + accAttr: "penny.dimex.quarter", + exp: false, + }, + { + name: "with wildcard: extra char at start of last name", + reqAttr: "*.penny.dime.quarter", + accAttr: "penny.dime.xquarter", + exp: false, + }, + { + name: "with wildcard: extra char at end of first name", + reqAttr: "*.penny.dime.quarter", + accAttr: "penny.dime.quarterx", + exp: false, + }, + { + name: "with wildcard: extra name at start", + reqAttr: "*.penny.dime.quarter", + accAttr: "mil.penny.dime.quarter", + exp: true, + }, + { + name: "with wildcard: two extra names at start", + reqAttr: "*.penny.dime.quarter", + accAttr: "scraps.mil.penny.dime.quarter", + exp: true, + }, + { + name: "with wildcard: extra name at start but wrong 1st req name", + reqAttr: "*.penny.dime.quarter", + accAttr: "mil.xpenny.dime.quarter", + exp: false, + }, + { + name: "with wildcard: only base name", + reqAttr: "*.penny.dime.quarter", + accAttr: "penny.dime.quarter", + exp: false, + }, + { + name: "with wildcard: extra name at end", + reqAttr: "*.penny.dime.quarter", + accAttr: "penny.dime.quarter.dollar", + exp: false, + }, + { + name: "with wildcard: extra name at start but wrong base order", + reqAttr: "*.penny.dime.quarter", + accAttr: "dollar.quarter.dime.penny", + exp: false, + }, + { + name: "star in middle", + reqAttr: "penny.*.quarter", + accAttr: "penny.dime.quarter", + exp: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + isMatch := IsReqAttrMatch(tc.reqAttr, tc.accAttr) + assert.Equal(t, tc.exp, isMatch, "IsReqAttrMatch(%q, %q)", tc.reqAttr, tc.accAttr) + }) + } +} From bfbaeeb2f666b01d4896a6b08c1853a4b1279a27 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 12:28:48 -0600 Subject: [PATCH 024/309] [1658]: Tweak some name stuff. Make IsValidName assume the name provided has been normalized and use that in the keeper's Normalize function. --- x/name/keeper/keeper.go | 6 +++--- x/name/types/name.go | 5 +++-- x/name/types/name_test.go | 18 +++++++++--------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/x/name/keeper/keeper.go b/x/name/keeper/keeper.go index b74dabcf2c..b81a2f0394 100644 --- a/x/name/keeper/keeper.go +++ b/x/name/keeper/keeper.go @@ -250,6 +250,9 @@ func (k Keeper) IterateRecords(ctx sdk.Context, prefix []byte, handle func(recor // Normalize returns a name is storage format. func (k Keeper) Normalize(ctx sdk.Context, name string) (string, error) { normalized := types.NormalizeName(name) + if !types.IsValidName(normalized) { + return "", types.ErrNameInvalid + } segCount := uint32(0) for _, segment := range strings.Split(normalized, ".") { segCount++ @@ -261,9 +264,6 @@ func (k Keeper) Normalize(ctx sdk.Context, name string) (string, error) { if segLen > k.GetMaxSegmentLength(ctx) && !isUUID { return "", types.ErrNameSegmentTooLong } - if !types.IsValidNameSegment(segment) { - return "", types.ErrNameInvalid - } } if segCount > k.GetMaxNameLevels(ctx) { return "", types.ErrNameHasTooManySegments diff --git a/x/name/types/name.go b/x/name/types/name.go index 81199ddeec..e4640ab945 100644 --- a/x/name/types/name.go +++ b/x/name/types/name.go @@ -48,9 +48,10 @@ func NormalizeName(name string) string { return strings.Join(segments, ".") } -// IsValidName returns true if the provided name is valid after normalization and without consideration of length limits. +// IsValidName returns true if the provided name is valid (without consideration of length limits). +// It is assumed that the name provided has been normalized using NormalizeName. func IsValidName(name string) bool { - for _, segment := range strings.Split(NormalizeName(name), ".") { + for _, segment := range strings.Split(name, ".") { if !IsValidNameSegment(segment) { return false } diff --git a/x/name/types/name_test.go b/x/name/types/name_test.go index 36ea8c41e5..0fddac52d6 100644 --- a/x/name/types/name_test.go +++ b/x/name/types/name_test.go @@ -120,19 +120,19 @@ func TestIsValidName(t *testing.T) { exp bool }{ {name: "empty string", input: "", exp: true}, - {name: "two spaces", input: " ", exp: true}, + {name: "two spaces", input: " ", exp: false}, {name: "with middle space", input: " a b ", exp: false}, - {name: "upper case", input: "ABcDE", exp: true}, - {name: "space around first segment", input: " ghi .def.abc", exp: true}, - {name: "space around middle segment", input: "ghi. def .abc", exp: true}, - {name: "space around third segment", input: "ghi.def. abc ", exp: true}, - {name: "middle segment has upper case", input: "ghi.DeF.abc", exp: true}, + {name: "upper case", input: "ABcDE", exp: false}, + {name: "space around first segment", input: " ghi .def.abc", exp: false}, + {name: "space around middle segment", input: "ghi. def .abc", exp: false}, + {name: "space around third segment", input: "ghi.def. abc ", exp: false}, + {name: "middle segment has upper case", input: "ghi.DeF.abc", exp: false}, {name: "empty first segment", input: ".def.abc", exp: true}, - {name: "first segment is a space", input: " .def.abc", exp: true}, + {name: "first segment is a space", input: " .def.abc", exp: false}, {name: "empty last segment", input: "ghi.def.", exp: true}, - {name: "last segment is a space", input: "ghi.def. ", exp: true}, + {name: "last segment is a space", input: "ghi.def. ", exp: false}, {name: "empty middle segment", input: "ghi..abc", exp: true}, - {name: "middle segment is a space", input: "ghi. .abc", exp: true}, + {name: "middle segment is a space", input: "ghi. .abc", exp: false}, {name: "one segment two dashes", input: "a-b-c", exp: false}, {name: "two segments one dash each", input: "a-1.b-2", exp: true}, {name: "comma in first segment", input: "a,1.b-2", exp: false}, From 3a153d9aff3a3dd1b21f6a21ce2e7efce9228551 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 12:42:41 -0600 Subject: [PATCH 025/309] [1658]: Update IsReqAttrMatch to allow a req attr with just the star char to match any non-empty account attribute. Make it so that an empty account attribute string does not match an empty required attribute string. --- x/exchange/market.go | 5 ++++- x/exchange/market_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index ac79e133bd..3a37a87af9 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -366,9 +366,12 @@ func HasReqAttrMatch(reqAttr string, accAttrs []string) bool { // IsReqAttrMatch returns true if the provide account attribute is a match for the given required attribute. // This assumes that reqAttr and accAttr have both been normalized. func IsReqAttrMatch(reqAttr, accAttr string) bool { - if len(reqAttr) == 0 { + if len(reqAttr) == 0 || len(accAttr) == 0 { return false } + if reqAttr == "*" { + return true + } if strings.HasPrefix(reqAttr, "*.") { // reqAttr[1:] is used here (instead of [2:]) because we need that . to be // part of the match. Otherwise "*.b.a" would match "c.b.a" as well as "c.evilb.a". diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 58f4752631..dcd0c74fa1 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -312,6 +312,18 @@ func TestIsReqAttrMatch(t *testing.T) { accAttr: "foo", exp: false, }, + { + name: "empty acc attr", + reqAttr: "foo", + accAttr: "", + exp: false, + }, + { + name: "both empty", + reqAttr: "", + accAttr: "", + exp: false, + }, { name: "no wildcard: exact match", reqAttr: "penny.dime.quarter", @@ -522,6 +534,24 @@ func TestIsReqAttrMatch(t *testing.T) { accAttr: "penny.dime.quarter", exp: false, }, + { + name: "just a star: empty account attribute", + reqAttr: "*", + accAttr: "", + exp: false, + }, + { + name: "just wildcard: empty account attribute", + reqAttr: "*.", + accAttr: "", + exp: false, + }, + { + name: "just a star: account attribute has value", + reqAttr: "*", + accAttr: "penny.dime.quarter", + exp: true, + }, } for _, tc := range tests { From 98103cb653b351280be26a285df5a56158845903 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 12:47:36 -0600 Subject: [PATCH 026/309] [1658]: Refactor IsValidReqAttr now that IsValidName expects normalized input, and write unit tests for it. --- x/exchange/market.go | 26 ++++++++------------------ x/exchange/market_test.go | 25 ++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index 3a37a87af9..f040ade8b7 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -312,32 +312,22 @@ func ValidateReqAttrs(attrLists ...[]string) error { // IsValidReqAttr returns true if the provided string is a valid required attribute entry. func IsValidReqAttr(reqAttr string) bool { - // If it's already valid, we're all good. - if nametypes.IsValidName(reqAttr) { - return true - } - - // If there isn't a wildcard in it, there's no saving it. - if !strings.Contains(reqAttr, "*") { - return false - } - - // Get the normalized version so we can more accurately check things on it. normalized := nametypes.NormalizeName(reqAttr) - // If it's just a wildcard, allow it. + // Allow it to just be the wildcard character. if normalized == "*" { return true } - // The first segment can be a * wildcard. - // If that's what we've got, make sure everything after it is valid. - if normalized[:2] == "*." { - return nametypes.IsValidName(normalized[2:]) + // A leading wildcard segment is valid for us, but not the name module. So, remove it if it's there. + normalized = strings.TrimPrefix(normalized, "*.") + + // IsValidName doesn't consider length, so an empty string is valid by it, but not valid in here. + if len(normalized) == 0 { + return false } - // Nothing left that might save it. - return false + return nametypes.IsValidName(normalized) } // FindUnmatchedReqAttrs returns all required attributes that don't have a match in the provided account attributes. diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index dcd0c74fa1..4b657103c8 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -36,7 +36,30 @@ import ( // TODO[1658]: func TestValidateReqAttrs(t *testing.T) -// TODO[1658]: func TestIsValidReqAttr(t *testing.T) +func TestIsValidReqAttr(t *testing.T) { + tests := []struct { + name string + reqAttr string + exp bool + }{ + {name: "already valid and normalized", reqAttr: "x.y.z", exp: true}, + {name: "already valid but not normalized", reqAttr: " x . y . z ", exp: true}, + {name: "invalid character", reqAttr: "x._y.z", exp: false}, + {name: "just the wildcard", reqAttr: " * ", exp: true}, + {name: "just star dot", reqAttr: "*. ", exp: false}, + {name: "star dot valid", reqAttr: "* . x . y . z", exp: true}, + {name: "star dot invalid", reqAttr: "* . x . _y . z", exp: false}, + {name: "empty string", reqAttr: "", exp: false}, + {name: "wildcard in middle", reqAttr: "x.*.y.z", exp: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ok := IsValidReqAttr(tc.reqAttr) + assert.Equal(t, tc.exp, ok, "IsValidReqAttr(%q)", tc.reqAttr) + }) + } +} func TestFindUnmatchedReqAttrs(t *testing.T) { tests := []struct { From 12273868a363f8593698e71a39b08c853dc92ea7 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 13:23:53 -0600 Subject: [PATCH 027/309] [1658]: Tweak ValidateReqAttrs to do duplicate checking on the normalized names and only print one error for a name. Tweak IsValidReqAttr to assume the provided attr is normalized. Write unit tests on ValidateReqAttrs. --- x/exchange/market.go | 25 +++--- x/exchange/market_test.go | 178 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 185 insertions(+), 18 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index f040ade8b7..36b8e52c74 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -291,19 +291,21 @@ func ParsePermissions(permissions ...string) ([]Permission, error) { func ValidateReqAttrs(attrLists ...[]string) error { var errs []error seen := make(map[string]bool) - dups := make(map[string]bool) + bad := make(map[string]bool) for _, attrs := range attrLists { for _, attr := range attrs { - if seen[attr] { - if !dups[attr] { + normalized := nametypes.NormalizeName(attr) + if seen[normalized] { + if !bad[normalized] { errs = append(errs, fmt.Errorf("duplicate required attribute entry: %q", attr)) - dups[attr] = true + bad[normalized] = true } continue } - seen[attr] = true - if !IsValidReqAttr(attr) { + seen[normalized] = true + if !IsValidReqAttr(normalized) { errs = append(errs, fmt.Errorf("invalid required attribute %q", attr)) + bad[normalized] = true } } } @@ -311,23 +313,22 @@ func ValidateReqAttrs(attrLists ...[]string) error { } // IsValidReqAttr returns true if the provided string is a valid required attribute entry. +// Assumes that the provided reqAttr has already been normalized. func IsValidReqAttr(reqAttr string) bool { - normalized := nametypes.NormalizeName(reqAttr) - // Allow it to just be the wildcard character. - if normalized == "*" { + if reqAttr == "*" { return true } // A leading wildcard segment is valid for us, but not the name module. So, remove it if it's there. - normalized = strings.TrimPrefix(normalized, "*.") + reqAttr = strings.TrimPrefix(reqAttr, "*.") // IsValidName doesn't consider length, so an empty string is valid by it, but not valid in here. - if len(normalized) == 0 { + if len(reqAttr) == 0 { return false } - return nametypes.IsValidName(normalized) + return nametypes.IsValidName(reqAttr) } // FindUnmatchedReqAttrs returns all required attributes that don't have a match in the provided account attributes. diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 4b657103c8..47b7f01b5a 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -1,6 +1,7 @@ package exchange import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -34,7 +35,170 @@ import ( // TODO[1658]: func TestParsePermissions(t *testing.T) -// TODO[1658]: func TestValidateReqAttrs(t *testing.T) +func TestValidateReqAttrs(t *testing.T) { + joinErrs := func(errs ...string) string { + return strings.Join(errs, "\n") + } + + tests := []struct { + name string + attrLists [][]string + exp string + }{ + { + name: "nil lists", + attrLists: nil, + exp: "", + }, + { + name: "no lists", + attrLists: [][]string{}, + exp: "", + }, + { + name: "two empty lists", + attrLists: [][]string{{}, {}}, + exp: "", + }, + { + name: "one list: three valid entries: normalized", + attrLists: [][]string{ + {"*.wildcard", "penny.nickel.dime", "*.example.pb"}, + }, + exp: "", + }, + { + name: "one list: three valid entries: not normalized", + attrLists: [][]string{ + {" * . wildcard ", " penny . nickel . dime ", " * . example . pb "}, + }, + exp: "", + }, + { + name: "one list: three entries: first invalid", + attrLists: [][]string{ + {"x.*.wildcard", "penny.nickel.dime", "*.example.pb"}, + }, + exp: `invalid required attribute "x.*.wildcard"`, + }, + { + name: "one list: three entries: second invalid", + attrLists: [][]string{ + {"*.wildcard", "penny.nic kel.dime", "*.example.pb"}, + }, + exp: `invalid required attribute "penny.nic kel.dime"`, + }, + { + name: "one list: three entries: third invalid", + attrLists: [][]string{ + {"*.wildcard", "penny.nickel.dime", "*.ex-am-ple.pb"}, + }, + exp: `invalid required attribute "*.ex-am-ple.pb"`, + }, + { + name: "one list: duplicate entries", + attrLists: [][]string{ + {"*.multi", "*.multi", "*.multi"}, + }, + exp: `duplicate required attribute entry: "*.multi"`, + }, + { + name: "one list: duplicate bad entries", + attrLists: [][]string{ + {"bad.*.example", "bad. * .example"}, + }, + exp: `invalid required attribute "bad.*.example"`, + }, + { + name: "one list: multiple problems", + attrLists: [][]string{ + { + "one.multi", "x.*.wildcard", "x.*.wildcard", "one.multi", "two.multi", + "penny.nic kel.dime", "one.multi", "two.multi", "*.ex-am-ple.pb", "two.multi", + }, + }, + exp: joinErrs( + `invalid required attribute "x.*.wildcard"`, + `duplicate required attribute entry: "one.multi"`, + `invalid required attribute "penny.nic kel.dime"`, + `duplicate required attribute entry: "two.multi"`, + `invalid required attribute "*.ex-am-ple.pb"`, + ), + }, + { + name: "two lists: second has invalid first", + attrLists: [][]string{ + {"*.ok", "also.okay.by.me", "this.makes.me.happy"}, + {"x.*.wildcard", "penny.nickel.dime", "*.example.pb"}, + }, + exp: `invalid required attribute "x.*.wildcard"`, + }, + { + name: "two lists: second has invalid middle", + attrLists: [][]string{ + {"*.ok", "also.okay.by.me", "this.makes.me.happy"}, + {"*.wildcard", "penny.nic kel.dime", "*.example.pb"}, + }, + exp: `invalid required attribute "penny.nic kel.dime"`, + }, + { + name: "two lists: second has invalid last", + attrLists: [][]string{ + {"*.ok", "also.okay.by.me", "this.makes.me.happy"}, + {"*.wildcard", "penny.nickel.dime", "*.ex-am-ple.pb"}, + }, + exp: `invalid required attribute "*.ex-am-ple.pb"`, + }, + { + name: "two lists: same entry in both but one is not normalized", + attrLists: [][]string{ + {"this.attr.is.twice"}, + {" This . Attr . Is . TWice"}, + }, + exp: `duplicate required attribute entry: " This . Attr . Is . TWice"`, + }, + { + name: "two lists: multiple problems", + attrLists: [][]string{ + {"one.multi", "x.*.wildcard", "x.*.wildcard", "one.multi", "two.multi"}, + {"penny.nic kel.dime", "one.multi", "two.multi", "*.ex-am-ple.pb", "two.multi"}, + }, + exp: joinErrs( + `invalid required attribute "x.*.wildcard"`, + `duplicate required attribute entry: "one.multi"`, + `invalid required attribute "penny.nic kel.dime"`, + `duplicate required attribute entry: "two.multi"`, + `invalid required attribute "*.ex-am-ple.pb"`, + ), + }, + { + name: "many lists: multiple problems", + attrLists: [][]string{ + {" one . multi "}, {"x.*.wildcard"}, {"x.*.wildcard"}, {"one.multi"}, {" two.multi "}, + {"penny.nic kel.dime"}, {"one.multi"}, {"two.multi"}, {"*.ex-am-ple.pb"}, {"two.multi"}, + }, + exp: joinErrs( + `invalid required attribute "x.*.wildcard"`, + `duplicate required attribute entry: "one.multi"`, + `invalid required attribute "penny.nic kel.dime"`, + `duplicate required attribute entry: "two.multi"`, + `invalid required attribute "*.ex-am-ple.pb"`, + ), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := ValidateReqAttrs(tc.attrLists...) + // TODO[1658]: Replace this with testutils.AssertErrorValue(t, err, tc.exp, "ValidateReqAttrs") + if len(tc.exp) > 0 { + assert.EqualError(t, err, tc.exp, "ValidateReqAttrs") + } else { + assert.NoError(t, err, "ValidateReqAttrs") + } + }) + } +} func TestIsValidReqAttr(t *testing.T) { tests := []struct { @@ -43,12 +207,14 @@ func TestIsValidReqAttr(t *testing.T) { exp bool }{ {name: "already valid and normalized", reqAttr: "x.y.z", exp: true}, - {name: "already valid but not normalized", reqAttr: " x . y . z ", exp: true}, + {name: "already valid but not normalized", reqAttr: " x . y . z ", exp: false}, {name: "invalid character", reqAttr: "x._y.z", exp: false}, - {name: "just the wildcard", reqAttr: " * ", exp: true}, - {name: "just star dot", reqAttr: "*. ", exp: false}, - {name: "star dot valid", reqAttr: "* . x . y . z", exp: true}, - {name: "star dot invalid", reqAttr: "* . x . _y . z", exp: false}, + {name: "just the wildcard", reqAttr: "*", exp: true}, + {name: "just the wildcard not normalized", reqAttr: " * ", exp: false}, + {name: "just star dot", reqAttr: "*.", exp: false}, + {name: "star dot valid", reqAttr: "*.x.y.z", exp: true}, + {name: "star dot valid not normalized", reqAttr: "* . x . y . z", exp: false}, + {name: "star dot invalid", reqAttr: "*.x._y.z", exp: false}, {name: "empty string", reqAttr: "", exp: false}, {name: "wildcard in middle", reqAttr: "x.*.y.z", exp: false}, } From 5e9a483a944b0bb64fc1689c1c75c27d2b656613 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 14:19:02 -0600 Subject: [PATCH 028/309] [1658]: nil in nil out ParsePermissions. Trim whitespace in ParsePermission. Unit tests on Permission.SimpleString, Permission.Validate, AllPermissions, ParsePermission, and ParsePermissions. --- x/exchange/market.go | 5 +- x/exchange/market_test.go | 375 +++++++++++++++++++++++++++++++++++++- 2 files changed, 374 insertions(+), 6 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index 36b8e52c74..fbfd741d64 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -256,7 +256,7 @@ func AllPermissions() []Permission { // An error is returned if unknown or Permission_unspecified. // Example inputs: "settle", "CanCel", "WITHDRAW", "permission_update", "PermISSion_PErmissioNs", "PERMISSION_ATTRIBUTES" func ParsePermission(permission string) (Permission, error) { - permUC := strings.ToUpper(permission) + permUC := strings.ToUpper(strings.TrimSpace(permission)) val, found := Permission_value["PERMISSION_"+permUC] if found { if val != 0 { @@ -275,6 +275,9 @@ func ParsePermission(permission string) (Permission, error) { // An error is returned if any are unknown or Permission_unspecified. // See also: ParsePermission. func ParsePermissions(permissions ...string) ([]Permission, error) { + if len(permissions) == 0 { + return nil, nil + } rv := make([]Permission, len(permissions)) var errs []error for i, perm := range permissions { diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 47b7f01b5a..74943e0f58 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -1,10 +1,12 @@ package exchange import ( + "sort" "strings" "testing" "github.com/stretchr/testify/assert" + "golang.org/x/exp/maps" ) // TODO[1658]: func TestMarket_Validate(t *testing.T) @@ -25,15 +27,378 @@ import ( // TODO[1658]: func TestAccessGrant_Validate(t *testing.T) -// TODO[1658]: func TestPermission_SimpleString(t *testing.T) +func TestPermission_SimpleString(t *testing.T) { + tests := []struct { + name string + p Permission + exp string + }{ + { + name: "unspecified", + p: Permission_unspecified, + exp: "unspecified", + }, + { + name: "settle", + p: Permission_settle, + exp: "settle", + }, + { + name: "cancel", + p: Permission_cancel, + exp: "cancel", + }, + { + name: "withdraw", + p: Permission_withdraw, + exp: "withdraw", + }, + { + name: "update", + p: Permission_update, + exp: "update", + }, + { + name: "permissions", + p: Permission_permissions, + exp: "permissions", + }, + { + name: "attributes", + p: Permission_attributes, + exp: "attributes", + }, + { + name: "negative 1", + p: -1, + exp: "-1", + }, + { + name: "unknown value", + p: 99, + exp: "99", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + actual := tc.p.SimpleString() + assert.Equal(t, tc.exp, actual, "%s.SimpleString()", tc.p) + }) + } +} + +func TestPermission_Validate(t *testing.T) { + tests := []struct { + name string + p Permission + exp string + }{ + { + name: "unspecified", + p: Permission_unspecified, + exp: "permission is unspecified", + }, + { + name: "settle", + p: Permission_settle, + exp: "", + }, + { + name: "cancel", + p: Permission_cancel, + exp: "", + }, + { + name: "withdraw", + p: Permission_withdraw, + exp: "", + }, + { + name: "update", + p: Permission_update, + exp: "", + }, + { + name: "permissions", + p: Permission_permissions, + exp: "", + }, + { + name: "attributes", + p: Permission_attributes, + exp: "", + }, + { + name: "negative 1", + p: -1, + exp: "permission -1 does not exist", + }, + { + name: "unknown value", + p: 99, + exp: "permission 99 does not exist", + }, + } -// TODO[1658]: func TestPermission_Validate(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.p.Validate() -// TODO[1658]: func TestAllPermissions(t *testing.T) + // TODO: Refactor this to testutils.AssertErrorValue(t, err, tc.exp, "Validate()") + if len(tc.exp) > 0 { + assert.EqualError(t, err, tc.exp, "Validate()") + } else { + assert.NoError(t, err, "Validate()") + } + }) + } -// TODO[1658]: func TestParsePermission(t *testing.T) + t.Run("all values have a test case", func(t *testing.T) { + allVals := maps.Keys(Permission_name) + sort.Slice(allVals, func(i, j int) bool { + return allVals[i] < allVals[j] + }) + + for _, val := range allVals { + perm := Permission(val) + hasTest := false + for _, tc := range tests { + if tc.p == perm { + hasTest = true + break + } + } + assert.True(t, hasTest, "No test case found that expects the %s permission", perm) + } + }) +} -// TODO[1658]: func TestParsePermissions(t *testing.T) +func TestAllPermissions(t *testing.T) { + expected := []Permission{ + Permission_settle, + Permission_cancel, + Permission_withdraw, + Permission_update, + Permission_permissions, + Permission_attributes, + } + + actual := AllPermissions() + assert.Equal(t, expected, actual, "AllPermissions()") +} + +func TestParsePermission(t *testing.T) { + tests := []struct { + permission string + expected Permission + expErr string + }{ + // Permission_settle + {permission: "settle", expected: Permission_settle}, + {permission: " settle", expected: Permission_settle}, + {permission: "settle ", expected: Permission_settle}, + {permission: "SETTLE", expected: Permission_settle}, + {permission: "SeTTle", expected: Permission_settle}, + {permission: "permission_settle", expected: Permission_settle}, + {permission: "PERMISSION_SETTLE", expected: Permission_settle}, + {permission: "pERmiSSion_seTTle", expected: Permission_settle}, + + // Permission_cancel + {permission: "cancel", expected: Permission_cancel}, + {permission: " cancel", expected: Permission_cancel}, + {permission: "cancel ", expected: Permission_cancel}, + {permission: "CANCEL", expected: Permission_cancel}, + {permission: "caNCel", expected: Permission_cancel}, + {permission: "permission_cancel", expected: Permission_cancel}, + {permission: "PERMISSION_CANCEL", expected: Permission_cancel}, + {permission: "pERmiSSion_CanCEl", expected: Permission_cancel}, + + // Permission_withdraw + {permission: "withdraw", expected: Permission_withdraw}, + {permission: " withdraw", expected: Permission_withdraw}, + {permission: "withdraw ", expected: Permission_withdraw}, + {permission: "WITHDRAW", expected: Permission_withdraw}, + {permission: "wiTHdRaw", expected: Permission_withdraw}, + {permission: "permission_withdraw", expected: Permission_withdraw}, + {permission: "PERMISSION_WITHDRAW", expected: Permission_withdraw}, + {permission: "pERmiSSion_wIThdrAw", expected: Permission_withdraw}, + + // Permission_update + {permission: "update", expected: Permission_update}, + {permission: " update", expected: Permission_update}, + {permission: "update ", expected: Permission_update}, + {permission: "UPDATE", expected: Permission_update}, + {permission: "uPDaTe", expected: Permission_update}, + {permission: "permission_update", expected: Permission_update}, + {permission: "PERMISSION_UPDATE", expected: Permission_update}, + {permission: "pERmiSSion_UpdAtE", expected: Permission_update}, + + // Permission_permissions + {permission: "permissions", expected: Permission_permissions}, + {permission: " permissions", expected: Permission_permissions}, + {permission: "permissions ", expected: Permission_permissions}, + {permission: "PERMISSIONS", expected: Permission_permissions}, + {permission: "pErmiSSions", expected: Permission_permissions}, + {permission: "permission_permissions", expected: Permission_permissions}, + {permission: "PERMISSION_PERMISSIONS", expected: Permission_permissions}, + {permission: "pERmiSSion_perMIssIons", expected: Permission_permissions}, + + // Permission_attributes + {permission: "attributes", expected: Permission_attributes}, + {permission: " attributes", expected: Permission_attributes}, + {permission: "attributes ", expected: Permission_attributes}, + {permission: "ATTRIBUTES", expected: Permission_attributes}, + {permission: "aTTribuTes", expected: Permission_attributes}, + {permission: "permission_attributes", expected: Permission_attributes}, + {permission: "PERMISSION_ATTRIBUTES", expected: Permission_attributes}, + {permission: "pERmiSSion_attRiButes", expected: Permission_attributes}, + + // Permission_unspecified + {permission: "unspecified", expErr: `invalid permission: "unspecified"`}, + {permission: " unspecified", expErr: `invalid permission: " unspecified"`}, + {permission: "unspecified ", expErr: `invalid permission: "unspecified "`}, + {permission: "UNSPECIFIED", expErr: `invalid permission: "UNSPECIFIED"`}, + {permission: "unsPeCiFied", expErr: `invalid permission: "unsPeCiFied"`}, + {permission: "permission_unspecified", expErr: `invalid permission: "permission_unspecified"`}, + {permission: "PERMISSION_UNSPECIFIED", expErr: `invalid permission: "PERMISSION_UNSPECIFIED"`}, + {permission: "pERmiSSion_uNSpEcifiEd", expErr: `invalid permission: "pERmiSSion_uNSpEcifiEd"`}, + + // Invalid + {permission: "ettle", expErr: `invalid permission: "ettle"`}, + {permission: "settl", expErr: `invalid permission: "settl"`}, + {permission: "set tle", expErr: `invalid permission: "set tle"`}, + + {permission: "ancel", expErr: `invalid permission: "ancel"`}, + {permission: "cance", expErr: `invalid permission: "cance"`}, + {permission: "can cel", expErr: `invalid permission: "can cel"`}, + + {permission: "ithdraw", expErr: `invalid permission: "ithdraw"`}, + {permission: "withdra", expErr: `invalid permission: "withdra"`}, + {permission: "with draw", expErr: `invalid permission: "with draw"`}, + + {permission: "pdate", expErr: `invalid permission: "pdate"`}, + {permission: "updat", expErr: `invalid permission: "updat"`}, + {permission: "upd ate", expErr: `invalid permission: "upd ate"`}, + + {permission: "ermissions", expErr: `invalid permission: "ermissions"`}, + {permission: "permission", expErr: `invalid permission: "permission"`}, + {permission: "permission_permission", expErr: `invalid permission: "permission_permission"`}, + {permission: "permis sions", expErr: `invalid permission: "permis sions"`}, + + {permission: "ttributes", expErr: `invalid permission: "ttributes"`}, + {permission: "attribute", expErr: `invalid permission: "attribute"`}, + {permission: "attr ibutes", expErr: `invalid permission: "attr ibutes"`}, + + {permission: "", expErr: `invalid permission: ""`}, + } + + for _, tc := range tests { + name := tc.permission + if len(tc.permission) == 0 { + name = "empty" + } + t.Run(name, func(t *testing.T) { + perm, err := ParsePermission(tc.permission) + + if len(tc.expErr) > 0 { + assert.EqualError(t, err, tc.expErr, "ParsePermission(%q) error", tc.permission) + } else { + assert.NoError(t, err, "ParsePermission(%q) error", tc.permission) + } + + assert.Equal(t, tc.expected, perm, "ParsePermission(%q) result", tc.permission) + }) + } + + t.Run("all values have a test case", func(t *testing.T) { + allVals := maps.Keys(Permission_name) + sort.Slice(allVals, func(i, j int) bool { + return allVals[i] < allVals[j] + }) + + for _, val := range allVals { + perm := Permission(val) + hasTest := false + for _, tc := range tests { + if tc.expected == perm { + hasTest = true + break + } + } + assert.True(t, hasTest, "No test case found that expects the %s permission", perm) + } + }) +} + +func TestParsePermissions(t *testing.T) { + tests := []struct { + name string + permissions []string + expected []Permission + expErr string + }{ + { + name: "nil permissions", + permissions: nil, + expected: nil, + expErr: "", + }, + { + name: "empty permissions", + permissions: nil, + expected: nil, + expErr: "", + }, + { + name: "one of each permission", + permissions: []string{"settle", "cancel", "PERMISSION_WITHDRAW", "permission_update", "permissions", "attributes"}, + expected: []Permission{ + Permission_settle, + Permission_cancel, + Permission_withdraw, + Permission_update, + Permission_permissions, + Permission_attributes, + }, + }, + { + name: "one bad entry", + permissions: []string{"settle", "what", "cancel"}, + expected: []Permission{ + Permission_settle, + Permission_unspecified, + Permission_cancel, + }, + expErr: `invalid permission: "what"`, + }, + { + name: "two bad entries", + permissions: []string{"nope", "withdraw", "notgood"}, + expected: []Permission{ + Permission_unspecified, + Permission_withdraw, + Permission_unspecified, + }, + expErr: `invalid permission: "nope"` + "\n" + + `invalid permission: "notgood"`, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + perms, err := ParsePermissions(tc.permissions...) + + // TODO: Refactor to use testutils.AssertErrorValue(t, err, tc.expErr, "ParsePermissions(%q) error", tc.permissions) + if len(tc.expErr) > 0 { + assert.EqualError(t, err, tc.expErr, "ParsePermissions(%q) error", tc.permissions) + } else { + assert.NoError(t, err, "ParsePermissions(%q) error", tc.permissions) + } + assert.Equal(t, tc.expected, perms, "ParsePermissions(%q) result", tc.permissions) + }) + } +} func TestValidateReqAttrs(t *testing.T) { joinErrs := func(errs ...string) string { From ea2032dac4887e6a62454e340a27e12a0391c084 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 14:45:53 -0600 Subject: [PATCH 029/309] [1658]: Tweak error messages in AccessGrant.Validate. Prevent nil entries in ValidateAccessGrants. Unit tests on ValidateAccessGrants and AccessGrant.Validate. --- x/exchange/market.go | 19 ++-- x/exchange/market_test.go | 198 +++++++++++++++++++++++++++++++++++++- 2 files changed, 208 insertions(+), 9 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index fbfd741d64..017d8666d0 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -184,16 +184,21 @@ func (r FeeRatio) Validate() error { // ValidateAccessGrants returns an error if any of the provided access grants are invalid. func ValidateAccessGrants(accessGrants []*AccessGrant) error { - var errs []error //nolint:prealloc // Stupid linter. It'll almost always be nil. No reason to pre-allocate anything. + errs := make([]error, len(accessGrants)) seen := make(map[string]bool) dups := make(map[string]bool) - for _, ag := range accessGrants { + for i, ag := range accessGrants { + if ag == nil { + errs[i] = fmt.Errorf("nil access grant not allowed") + continue + } if seen[ag.Address] && !dups[ag.Address] { - errs = append(errs, fmt.Errorf("%s appears in multiple access grant entries", ag.Address)) + errs[i] = fmt.Errorf("%s appears in multiple access grant entries", ag.Address) dups[ag.Address] = true + continue } seen[ag.Address] = true - errs = append(errs, ag.Validate()) + errs[i] = ag.Validate() } return errors.Join(errs...) } @@ -202,7 +207,7 @@ func ValidateAccessGrants(accessGrants []*AccessGrant) error { func (a AccessGrant) Validate() error { _, err := sdk.AccAddressFromBech32(a.Address) if err != nil { - return err + return fmt.Errorf("invalid address: %w", err) } if len(a.Permissions) == 0 { return fmt.Errorf("no permissions provided for %s", a.Address) @@ -210,11 +215,11 @@ func (a AccessGrant) Validate() error { seen := make(map[Permission]bool) for _, perm := range a.Permissions { if seen[perm] { - return fmt.Errorf("%s appears multiple times for %s", perm.String(), a.Address) + return fmt.Errorf("%s appears multiple times for %s", perm.SimpleString(), a.Address) } seen[perm] = true if err = perm.Validate(); err != nil { - return err + return fmt.Errorf("%w for %s", err, a.Address) } } return nil diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 74943e0f58..1322c879b2 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -7,6 +7,8 @@ import ( "github.com/stretchr/testify/assert" "golang.org/x/exp/maps" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // TODO[1658]: func TestMarket_Validate(t *testing.T) @@ -23,9 +25,201 @@ import ( // TODO[1658]: func TestFeeRatio_Validate(t *testing.T) -// TODO[1658]: func TestValidateAccessGrants(t *testing.T) +func TestValidateAccessGrants(t *testing.T) { + joinErrs := func(errs ...string) string { + return strings.Join(errs, "\n") + } + addrDup := sdk.AccAddress("duplicate_address___").String() + addr1 := sdk.AccAddress("address_1___________").String() + addr2 := sdk.AccAddress("address_2___________").String() + addr3 := sdk.AccAddress("address_3___________").String() -// TODO[1658]: func TestAccessGrant_Validate(t *testing.T) + tests := []struct { + name string + grants []*AccessGrant + exp string + }{ + { + name: "nil grants", + grants: nil, + exp: "", + }, + { + name: "empty grants", + grants: []*AccessGrant{}, + exp: "", + }, + { + name: "nil entry", + grants: []*AccessGrant{nil}, + exp: "nil access grant not allowed", + }, + { + name: "duplicate address", + grants: []*AccessGrant{ + {Address: addrDup, Permissions: []Permission{Permission_settle}}, + {Address: addrDup, Permissions: []Permission{Permission_cancel}}, + }, + exp: sdk.AccAddress("duplicate_address___").String() + " appears in multiple access grant entries", + }, + { + name: "three entries: all valid", + grants: []*AccessGrant{ + {Address: addr1, Permissions: AllPermissions()}, + {Address: addr2, Permissions: AllPermissions()}, + {Address: addr3, Permissions: AllPermissions()}, + }, + exp: "", + }, + { + name: "three entries: invalid first", + grants: []*AccessGrant{ + {Address: addr1, Permissions: []Permission{-1}}, + {Address: addr2, Permissions: AllPermissions()}, + {Address: addr3, Permissions: AllPermissions()}, + }, + exp: "permission -1 does not exist for " + addr1, + }, + { + name: "three entries: invalid second", + grants: []*AccessGrant{ + {Address: addr1, Permissions: AllPermissions()}, + {Address: addr2, Permissions: []Permission{-1}}, + {Address: addr3, Permissions: AllPermissions()}, + }, + exp: "permission -1 does not exist for " + addr2, + }, + { + name: "three entries: invalid second", + grants: []*AccessGrant{ + {Address: addr1, Permissions: AllPermissions()}, + {Address: addr2, Permissions: AllPermissions()}, + {Address: addr3, Permissions: []Permission{-1}}, + }, + exp: "permission -1 does not exist for " + addr3, + }, + { + name: "three entries: only valid first", + grants: []*AccessGrant{ + {Address: addr1, Permissions: AllPermissions()}, + {Address: addr2, Permissions: []Permission{0}}, + {Address: addr3, Permissions: []Permission{-1}}, + }, + exp: joinErrs( + "permission is unspecified for "+addr2, + "permission -1 does not exist for "+addr3, + ), + }, + { + name: "three entries: only valid second", + grants: []*AccessGrant{ + {Address: addr1, Permissions: []Permission{0}}, + {Address: addr2, Permissions: AllPermissions()}, + {Address: addr3, Permissions: []Permission{-1}}, + }, + exp: joinErrs( + "permission is unspecified for "+addr1, + "permission -1 does not exist for "+addr3, + ), + }, + { + name: "three entries: only valid third", + grants: []*AccessGrant{ + {Address: addr1, Permissions: []Permission{0}}, + {Address: addr2, Permissions: []Permission{-1}}, + {Address: addr3, Permissions: AllPermissions()}, + }, + exp: joinErrs( + "permission is unspecified for "+addr1, + "permission -1 does not exist for "+addr2, + ), + }, + { + name: "three entries: all same address", + grants: []*AccessGrant{ + {Address: addrDup, Permissions: AllPermissions()}, + {Address: addrDup, Permissions: AllPermissions()}, + {Address: addrDup, Permissions: AllPermissions()}, + }, + exp: addrDup + " appears in multiple access grant entries", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := ValidateAccessGrants(tc.grants) + + // TODO[1658]: Refactor to use testutils.AssertErrorValue(t, err, tc.exp, "ValidateAccessGrants") + if len(tc.exp) > 0 { + assert.EqualError(t, err, tc.exp, "ValidateAccessGrants") + } else { + assert.NoError(t, err, "ValidateAccessGrants") + } + }) + } +} + +func TestAccessGrant_Validate(t *testing.T) { + addr := sdk.AccAddress("addr________________").String() + tests := []struct { + name string + a AccessGrant + exp string + }{ + { + name: "invalid address", + a: AccessGrant{Address: "invalid_address_____", Permissions: []Permission{Permission_settle}}, + exp: "invalid address: decoding bech32 failed: invalid separator index -1", + }, + { + name: "nil permissions", + a: AccessGrant{Address: addr, Permissions: nil}, + exp: "no permissions provided for " + addr, + }, + { + name: "empty permissions", + a: AccessGrant{Address: addr, Permissions: []Permission{}}, + exp: "no permissions provided for " + addr, + }, + { + name: "duplicate entry", + a: AccessGrant{ + Address: addr, + Permissions: []Permission{ + Permission_settle, + Permission_cancel, + Permission_settle, + }, + }, + exp: "settle appears multiple times for " + addr, + }, + { + name: "invalid entry", + a: AccessGrant{ + Address: addr, + Permissions: []Permission{ + Permission_withdraw, + -1, + Permission_attributes, + }, + }, + exp: "permission -1 does not exist for " + addr, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.a.Validate() + + // TODO[1658]: Refactor this to testutils.AssertErrorValue(t, err, tc.exp, "Validate") + if len(tc.exp) > 0 { + assert.EqualError(t, err, tc.exp, "Validate") + } else { + assert.NoError(t, err, "Validate") + } + }) + } +} func TestPermission_SimpleString(t *testing.T) { tests := []struct { From f586fef3fc8bd46106e71e059ea6eeacba9ef230 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 16:15:28 -0600 Subject: [PATCH 030/309] [1658]: Make FeeRatio.Validate return an error if the price is zero or negative, or if the fee is negative. Tweak the fee ratio validation to always identify which list the error is in, prevent nil entries, and bypass validation if there aren't any entries. Unit tests on FeeRatio.Validate, FeeRatio.String, ValidateFeeRatios, ValidateBuyerFeeRatios, and ValidateSellerFeeRatios. --- x/exchange/market.go | 121 +++++---- x/exchange/market_test.go | 501 +++++++++++++++++++++++++++++++++++++- 2 files changed, 574 insertions(+), 48 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index 017d8666d0..144c7e8022 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -74,46 +74,6 @@ func (d MarketDetails) Validate() error { return errors.Join(errs...) } -// ValidateSellerFeeRatios returns an error if the provided seller fee ratios contains an invalid entry. -func ValidateSellerFeeRatios(ratios []*FeeRatio) error { - seen := make(map[string]bool) - dups := make(map[string]bool) - var errs []error - for _, ratio := range ratios { - key := ratio.Price.Denom - if seen[key] && !dups[key] { - errs = append(errs, fmt.Errorf("seller fee ratio denom %q appears in multiple ratios", ratio.Price.Denom)) - dups[key] = true - } - seen[key] = true - - if ratio.Price.Denom != ratio.Fee.Denom { - errs = append(errs, fmt.Errorf("seller fee ratio price denom %q does not equal fee denom %q", ratio.Price.Denom, ratio.Fee.Denom)) - } else { - errs = append(errs, ratio.Validate()) - } - } - return errors.Join(errs...) -} - -// ValidateBuyerFeeRatios returns an error if the provided buyer fee ratios contains an invalid entry. -func ValidateBuyerFeeRatios(ratios []*FeeRatio) error { - seen := make(map[string]bool) - dups := make(map[string]bool) - var errs []error //nolint:prealloc // ideally always empty, so no reason to preallocate anything. - for _, ratio := range ratios { - key := ratio.Price.Denom + ":" + ratio.Fee.Denom - if seen[key] && !dups[key] { - errs = append(errs, fmt.Errorf("buyer fee ratio pair %q to %q appears in multiple ratios", ratio.Price.Denom, ratio.Fee.Denom)) - dups[key] = true - } - seen[key] = true - - errs = append(errs, ratio.Validate()) - } - return errors.Join(errs...) -} - // ValidateFeeRatios makes sure that the provided fee ratios are valid and have the same price denoms. func ValidateFeeRatios(sellerRatios, buyerRatios []*FeeRatio) error { var errs []error @@ -146,19 +106,88 @@ func ValidateFeeRatios(sellerRatios, buyerRatios []*FeeRatio) error { for _, denom := range sellerPriceDenoms { if !contains(buyerPriceDenoms, denom) { - errs = append(errs, fmt.Errorf("denom %s is defined in the seller settlement fee ratios but not buyer", denom)) + errs = append(errs, fmt.Errorf("denom %q is defined in the seller settlement fee ratios but not buyer", denom)) } } for _, denom := range buyerPriceDenoms { if !contains(sellerPriceDenoms, denom) { - errs = append(errs, fmt.Errorf("denom %s is defined in the buyer settlement fee ratios but not seller", denom)) + errs = append(errs, fmt.Errorf("denom %q is defined in the buyer settlement fee ratios but not seller", denom)) } } return errors.Join(errs...) } +// ValidateSellerFeeRatios returns an error if the provided seller fee ratios contains an invalid entry. +func ValidateSellerFeeRatios(ratios []*FeeRatio) error { + if len(ratios) == 0 { + return nil + } + + seen := make(map[string]bool) + dups := make(map[string]bool) + var errs []error + for _, ratio := range ratios { + if ratio == nil { + errs = append(errs, errors.New("nil seller fee ratio not allowed")) + continue + } + + key := ratio.Price.Denom + if seen[key] { + if !dups[key] { + errs = append(errs, fmt.Errorf("seller fee ratio denom %q appears in multiple ratios", ratio.Price.Denom)) + dups[key] = true + } + continue + } + seen[key] = true + + if ratio.Price.Denom != ratio.Fee.Denom { + errs = append(errs, fmt.Errorf("seller fee ratio price denom %q does not equal fee denom %q", ratio.Price.Denom, ratio.Fee.Denom)) + continue + } + + if err := ratio.Validate(); err != nil { + errs = append(errs, fmt.Errorf("seller fee ratio %w", err)) + } + } + return errors.Join(errs...) +} + +// ValidateBuyerFeeRatios returns an error if the provided buyer fee ratios contains an invalid entry. +func ValidateBuyerFeeRatios(ratios []*FeeRatio) error { + if len(ratios) == 0 { + return nil + } + + seen := make(map[string]bool) + dups := make(map[string]bool) + var errs []error + for _, ratio := range ratios { + if ratio == nil { + errs = append(errs, errors.New("nil buyer fee ratio not allowed")) + continue + } + + key := ratio.Price.Denom + ":" + ratio.Fee.Denom + if seen[key] { + if !dups[key] { + errs = append(errs, fmt.Errorf("buyer fee ratio pair %q to %q appears in multiple ratios", ratio.Price.Denom, ratio.Fee.Denom)) + dups[key] = true + } + continue + } + seen[key] = true + + if err := ratio.Validate(); err != nil { + errs = append(errs, fmt.Errorf("buyer fee ratio %w", err)) + } + } + return errors.Join(errs...) +} + // contains returns true if the provided toFind is present in the provided vals. func contains[T comparable](vals []T, toFind T) bool { for _, v := range vals { @@ -176,6 +205,12 @@ func (r FeeRatio) String() string { // Validate returns an error if this FeeRatio is invalid. func (r FeeRatio) Validate() error { + if !r.Price.Amount.IsPositive() { + return fmt.Errorf("price amount %q must be positive", r.Price) + } + if r.Fee.Amount.IsNegative() { + return fmt.Errorf("fee amount %q cannot be negative", r.Fee) + } if r.Price.Denom == r.Fee.Denom && r.Fee.Amount.GT(r.Price.Amount) { return fmt.Errorf("fee amount %q cannot be greater than price amount %q", r.Fee, r.Price) } @@ -189,7 +224,7 @@ func ValidateAccessGrants(accessGrants []*AccessGrant) error { dups := make(map[string]bool) for i, ag := range accessGrants { if ag == nil { - errs[i] = fmt.Errorf("nil access grant not allowed") + errs[i] = errors.New("nil access grant not allowed") continue } if seen[ag.Address] && !dups[ag.Address] { diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 1322c879b2..e81c123cab 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -6,8 +6,10 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "golang.org/x/exp/maps" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -15,15 +17,504 @@ import ( // TODO[1658]: func TestMarketDetails_Validate(t *testing.T) -// TODO[1658]: func TestValidateSellerFeeRatios(t *testing.T) +func TestValidateFeeRatios(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + joinErrs := func(errs ...string) string { + return strings.Join(errs, "\n") + } -// TODO[1658]: func TestValidateBuyerFeeRatios(t *testing.T) + tests := []struct { + name string + sellerRatios []*FeeRatio + buyerRatios []*FeeRatio + exp string + }{ + { + name: "nil nil", + sellerRatios: nil, + buyerRatios: nil, + exp: "", + }, + { + name: "nil empty", + sellerRatios: nil, + buyerRatios: []*FeeRatio{}, + exp: "", + }, + { + name: "empty nil", + sellerRatios: []*FeeRatio{}, + buyerRatios: nil, + exp: "", + }, + { + name: "empty empty", + sellerRatios: []*FeeRatio{}, + buyerRatios: []*FeeRatio{}, + exp: "", + }, + { + name: "nil seller entry", + sellerRatios: []*FeeRatio{nil}, + buyerRatios: nil, + exp: "nil seller fee ratio not allowed", + }, + { + name: "nil buyer entry", + sellerRatios: nil, + buyerRatios: []*FeeRatio{nil}, + exp: "nil buyer fee ratio not allowed", + }, + { + name: "multiple errors from sellers and buyers", + sellerRatios: []*FeeRatio{ + nil, + {Price: coin(0, "leela"), Fee: coin(1, "leela")}, + }, + buyerRatios: []*FeeRatio{ + nil, + {Price: coin(0, "leela"), Fee: coin(1, "leela")}, + }, + exp: joinErrs( + "nil seller fee ratio not allowed", + `seller fee ratio price amount "0leela" must be positive`, + "nil buyer fee ratio not allowed", + `buyer fee ratio price amount "0leela" must be positive`, + ), + }, + { + name: "sellers have price denom that buyers do not", + sellerRatios: []*FeeRatio{ + {Price: coin(100, "leela"), Fee: coin(1, "leela")}, + {Price: coin(500, "fry"), Fee: coin(3, "fry")}, + }, + buyerRatios: []*FeeRatio{ + {Price: coin(100, "leela"), Fee: coin(3, "leela")}, + }, + exp: `denom "fry" is defined in the seller settlement fee ratios but not buyer`, + }, + { + name: "sellers have two price denoms that buyers do not", + sellerRatios: []*FeeRatio{ + {Price: coin(100, "leela"), Fee: coin(1, "leela")}, + {Price: coin(500, "fry"), Fee: coin(3, "fry")}, + }, + buyerRatios: nil, + exp: joinErrs( + `denom "leela" is defined in the seller settlement fee ratios but not buyer`, + `denom "fry" is defined in the seller settlement fee ratios but not buyer`, + ), + }, + { + name: "buyers have price denom that sellers do not", + sellerRatios: []*FeeRatio{ + {Price: coin(100, "leela"), Fee: coin(3, "leela")}, + }, + buyerRatios: []*FeeRatio{ + {Price: coin(100, "leela"), Fee: coin(1, "leela")}, + {Price: coin(500, "fry"), Fee: coin(3, "leela")}, + }, + exp: `denom "fry" is defined in the buyer settlement fee ratios but not seller`, + }, + { + name: "buyers have two price denoms that sellers do not", + sellerRatios: nil, + buyerRatios: []*FeeRatio{ + {Price: coin(100, "leela"), Fee: coin(1, "leela")}, + {Price: coin(500, "fry"), Fee: coin(3, "leela")}, + }, + exp: joinErrs( + `denom "leela" is defined in the buyer settlement fee ratios but not seller`, + `denom "fry" is defined in the buyer settlement fee ratios but not seller`, + ), + }, + { + name: "two buyers and two sellers and four price denoms", + sellerRatios: []*FeeRatio{ + {Price: coin(100, "leela"), Fee: coin(1, "leela")}, + {Price: coin(500, "fry"), Fee: coin(3, "fry")}, + }, + buyerRatios: []*FeeRatio{ + {Price: coin(100, "bender"), Fee: coin(1, "leela")}, + {Price: coin(3, "professor"), Fee: coin(500, "fry")}, + }, + exp: joinErrs( + `denom "leela" is defined in the seller settlement fee ratios but not buyer`, + `denom "fry" is defined in the seller settlement fee ratios but not buyer`, + `denom "bender" is defined in the buyer settlement fee ratios but not seller`, + `denom "professor" is defined in the buyer settlement fee ratios but not seller`, + ), + }, + { + name: "three seller ratios and many buyer ratios all legit", + sellerRatios: []*FeeRatio{ + {Price: coin(100, "leela"), Fee: coin(1, "leela")}, + {Price: coin(500, "fry"), Fee: coin(3, "fry")}, + {Price: coin(300, "bender"), Fee: coin(7, "bender")}, + }, + buyerRatios: []*FeeRatio{ + {Price: coin(10, "leela"), Fee: coin(1, "leela")}, + {Price: coin(11, "leela"), Fee: coin(2, "fry")}, + {Price: coin(12, "leela"), Fee: coin(3, "bender")}, + {Price: coin(1, "leela"), Fee: coin(3, "professor")}, + {Price: coin(50, "fry"), Fee: coin(1, "leela")}, + {Price: coin(51, "fry"), Fee: coin(2, "fry")}, + {Price: coin(52, "fry"), Fee: coin(3, "bender")}, + {Price: coin(1, "fry"), Fee: coin(2, "professor")}, + {Price: coin(30, "bender"), Fee: coin(1, "leela")}, + {Price: coin(31, "bender"), Fee: coin(2, "fry")}, + {Price: coin(32, "bender"), Fee: coin(3, "bender")}, + {Price: coin(1, "bender"), Fee: coin(1, "professor")}, + }, + exp: "", + }, + } -// TODO[1658]: func TestValidateFeeRatios(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = ValidateFeeRatios(tc.sellerRatios, tc.buyerRatios) + } + require.NotPanics(t, testFunc, "ValidateFeeRatios") -// TODO[1658]: func TestFeeRatio_String(t *testing.T) + // TODO[1658]: Refactor to testutils.AssertErrorValue(t, err, tc.exp, "ValidateFeeRatios") + if len(tc.exp) > 0 { + assert.EqualError(t, err, tc.exp, "ValidateFeeRatios") + } else { + assert.NoError(t, err, "ValidateFeeRatios") + } + }) + } +} -// TODO[1658]: func TestFeeRatio_Validate(t *testing.T) +func TestValidateSellerFeeRatios(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + joinErrs := func(errs ...string) string { + return strings.Join(errs, "\n") + } + + tests := []struct { + name string + ratios []*FeeRatio + exp string + }{ + { + name: "nil ratios", + ratios: nil, + exp: "", + }, + { + name: "empty ratios", + ratios: []*FeeRatio{}, + exp: "", + }, + { + name: "one ratio: nil", + ratios: []*FeeRatio{nil}, + exp: "nil seller fee ratio not allowed", + }, + { + name: "one ratio: different denoms", + ratios: []*FeeRatio{{Price: coin(3, "hermes"), Fee: coin(2, "mom")}}, + exp: `seller fee ratio price denom "hermes" does not equal fee denom "mom"`, + }, + { + name: "one ratio: same denoms", + ratios: []*FeeRatio{{Price: coin(3, "mom"), Fee: coin(2, "mom")}}, + exp: "", + }, + { + name: "one ratio: invalid", + ratios: []*FeeRatio{{Price: coin(0, "hermes"), Fee: coin(2, "hermes")}}, + exp: `seller fee ratio price amount "0hermes" must be positive`, + }, + { + name: "two with same denom", + ratios: []*FeeRatio{ + {Price: coin(3, "hermes"), Fee: coin(2, "hermes")}, + {Price: coin(6, "hermes"), Fee: coin(4, "hermes")}, + }, + exp: `seller fee ratio denom "hermes" appears in multiple ratios`, + }, + { + name: "three with diffrent denoms", + ratios: []*FeeRatio{ + {Price: coin(30, "leela"), Fee: coin(1, "leela")}, + {Price: coin(5, "fry"), Fee: coin(1, "fry")}, + {Price: coin(100, "professor"), Fee: coin(1, "professor")}, + }, + exp: "", + }, + { + name: "multiple errors", + ratios: []*FeeRatio{ + {Price: coin(3, "mom"), Fee: coin(2, "hermes")}, + {Price: coin(0, "hermes"), Fee: coin(2, "hermes")}, + {Price: coin(6, "bender"), Fee: coin(4, "bender")}, + nil, + {Price: coin(1, "hermes"), Fee: coin(2, "hermes")}, + {Price: coin(2, "bender"), Fee: coin(1, "bender")}, + // This one is ignored because we've already complained about multiple hermes. + {Price: coin(30, "hermes"), Fee: coin(2, "hermes")}, + }, + exp: joinErrs( + `seller fee ratio price denom "mom" does not equal fee denom "hermes"`, + `seller fee ratio price amount "0hermes" must be positive`, + "nil seller fee ratio not allowed", + `seller fee ratio denom "hermes" appears in multiple ratios`, + `seller fee ratio denom "bender" appears in multiple ratios`, + ), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = ValidateSellerFeeRatios(tc.ratios) + } + require.NotPanics(t, testFunc, "ValidateSellerFeeRatios") + + // TODO[1658]: Refactor to testutils.AssertErrorValue(t, err, tc.exp, "ValidateSellerFeeRatios") + if len(tc.exp) > 0 { + assert.EqualError(t, err, tc.exp, "ValidateSellerFeeRatios") + } else { + assert.NoError(t, err, "ValidateSellerFeeRatios") + } + }) + } +} + +func TestValidateBuyerFeeRatios(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + joinErrs := func(errs ...string) string { + return strings.Join(errs, "\n") + } + + tests := []struct { + name string + ratios []*FeeRatio + exp string + }{ + { + name: "nil ratios", + ratios: nil, + exp: "", + }, + { + name: "empty ratios", + ratios: []*FeeRatio{}, + exp: "", + }, + { + name: "one ratio: nil", + ratios: []*FeeRatio{nil}, + exp: "nil buyer fee ratio not allowed", + }, + { + name: "one ratio: different denoms", + ratios: []*FeeRatio{{Price: coin(3, "hermes"), Fee: coin(2, "mom")}}, + exp: "", + }, + { + name: "one ratio: same denoms", + ratios: []*FeeRatio{{Price: coin(3, "mom"), Fee: coin(2, "mom")}}, + exp: "", + }, + { + name: "one ratio: invalid", + ratios: []*FeeRatio{{Price: coin(0, "hermes"), Fee: coin(2, "hermes")}}, + exp: `buyer fee ratio price amount "0hermes" must be positive`, + }, + { + name: "duplicate ratio denoms", + ratios: []*FeeRatio{ + {Price: coin(10, "morbo"), Fee: coin(2, "scruffy")}, + {Price: coin(3, "morbo"), Fee: coin(1, "scruffy")}, + }, + exp: `buyer fee ratio pair "morbo" to "scruffy" appears in multiple ratios`, + }, + { + name: "two ratios one each way", + ratios: []*FeeRatio{ + {Price: coin(10, "leela"), Fee: coin(2, "scruffy")}, + {Price: coin(2, "scruffy"), Fee: coin(8, "leela")}, + }, + exp: "", + }, + { + name: "multiple errors", + ratios: []*FeeRatio{ + {Price: coin(10, "morbo"), Fee: coin(2, "scruffy")}, + {Price: coin(0, "zoidberg"), Fee: coin(1, "amy")}, + {Price: coin(1, "hermes"), Fee: coin(2, "hermes")}, + nil, + {Price: coin(3, "morbo"), Fee: coin(1, "scruffy")}, + {Price: coin(0, "zoidberg"), Fee: coin(1, "amy")}, + // This one has a different fee denom, though, so it's checked. + {Price: coin(1, "zoidberg"), Fee: coin(-1, "fry")}, + // We've already complained about this one, so it doesn't happen again. + {Price: coin(12, "zoidberg"), Fee: coin(55, "amy")}, + }, + exp: joinErrs( + `buyer fee ratio price amount "0zoidberg" must be positive`, + `buyer fee ratio fee amount "2hermes" cannot be greater than price amount "1hermes"`, + "nil buyer fee ratio not allowed", + `buyer fee ratio pair "morbo" to "scruffy" appears in multiple ratios`, + `buyer fee ratio pair "zoidberg" to "amy" appears in multiple ratios`, + `buyer fee ratio fee amount "-1fry" cannot be negative`, + ), + }, + { + name: "two different price denoms to several fee denoms", + ratios: []*FeeRatio{ + {Price: coin(100, "fry"), Fee: coin(1, "fry")}, + {Price: coin(1000, "fry"), Fee: coin(1, "professor")}, + {Price: coin(1, "fry"), Fee: coin(1, "leela")}, + {Price: coin(25, "fry"), Fee: coin(4, "bender")}, + {Price: coin(10, "leela"), Fee: coin(1, "fry")}, + {Price: coin(100, "leela"), Fee: coin(1, "professor")}, + {Price: coin(1000, "leela"), Fee: coin(1, "leela")}, + {Price: coin(35, "leela"), Fee: coin(2, "bender")}, + }, + exp: "", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = ValidateBuyerFeeRatios(tc.ratios) + } + require.NotPanics(t, testFunc, "ValidateBuyerFeeRatios") + + // TODO[1658]: Refactor to testutils.AssertErrorValue(t, err, tc.exp, "ValidateBuyerFeeRatios") + if len(tc.exp) > 0 { + assert.EqualError(t, err, tc.exp, "ValidateBuyerFeeRatios") + } else { + assert.NoError(t, err, "ValidateBuyerFeeRatios") + } + }) + } +} + +func TestFeeRatio_String(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + r FeeRatio + exp string + }{ + { + name: "zero value", + r: FeeRatio{}, + exp: ":", + }, + { + name: "same denoms", + r: FeeRatio{Price: coin(3, "zapp"), Fee: coin(5, "zapp")}, + exp: "3zapp:5zapp", + }, + { + name: "different denoms", + r: FeeRatio{Price: coin(10, "kif"), Fee: coin(5, "zapp")}, + exp: "10kif:5zapp", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.r.String() + } + require.NotPanics(t, testFunc, "FeeRatio.String()") + assert.Equal(t, tc.exp, actual, "FeeRatio.String()") + }) + } +} + +func TestFeeRatio_Validate(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + r FeeRatio + exp string + }{ + { + name: "zero price amount", + r: FeeRatio{Price: coin(0, "fry"), Fee: coin(0, "leela")}, + exp: `price amount "0fry" must be positive`, + }, + { + name: "negative price amount", + r: FeeRatio{Price: coin(-1, "fry"), Fee: coin(0, "leela")}, + exp: `price amount "-1fry" must be positive`, + }, + { + name: "negative fee amount", + r: FeeRatio{Price: coin(1, "fry"), Fee: coin(-1, "leela")}, + exp: `fee amount "-1leela" cannot be negative`, + }, + { + name: "same price and fee", + r: FeeRatio{Price: coin(1, "fry"), Fee: coin(1, "fry")}, + exp: "", + }, + { + name: "same denom fee greater", + r: FeeRatio{Price: coin(1, "fry"), Fee: coin(2, "fry")}, + exp: `fee amount "2fry" cannot be greater than price amount "1fry"`, + }, + { + name: "same denom price greater", + r: FeeRatio{Price: coin(2, "fry"), Fee: coin(1, "fry")}, + exp: "", + }, + { + name: "different denoms fee amount greater", + r: FeeRatio{Price: coin(1, "fry"), Fee: coin(2, "leela")}, + exp: "", + }, + { + name: "different denoms price amount greater", + r: FeeRatio{Price: coin(2, "fry"), Fee: coin(1, "leela")}, + exp: "", + }, + { + name: "different denoms same amounts", + r: FeeRatio{Price: coin(1, "fry"), Fee: coin(1, "leela")}, + exp: "", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.r.Validate() + + // TODO[1658]: Refactor to use testutils.AssertErrorValue(t, err, tc.exp, "Validate") + if len(tc.exp) > 0 { + assert.EqualError(t, err, tc.exp, "Validate") + } else { + assert.NoError(t, err, "Validate") + } + }) + } +} func TestValidateAccessGrants(t *testing.T) { joinErrs := func(errs ...string) string { From dfd0d5c223e799f92f71bbc095f843ed66dbdb70 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 16:29:34 -0600 Subject: [PATCH 031/309] [1658]: Unit tests on MarketDetails.Validate. --- x/exchange/market_test.go | 108 +++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index e81c123cab..3684ec44b9 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -1,6 +1,7 @@ package exchange import ( + "fmt" "sort" "strings" "testing" @@ -15,7 +16,112 @@ import ( // TODO[1658]: func TestMarket_Validate(t *testing.T) -// TODO[1658]: func TestMarketDetails_Validate(t *testing.T) +func TestMarketDetails_Validate(t *testing.T) { + joinErrs := func(errs ...string) string { + return strings.Join(errs, "\n") + } + nameErr := func(over int) string { + return fmt.Sprintf("name length %d exceeds maximum length of %d", MaxName+over, MaxName) + } + descErr := func(over int) string { + return fmt.Sprintf("description length %d exceeds maximum length of %d", MaxDescription+over, MaxDescription) + } + urlErr := func(over int) string { + return fmt.Sprintf("website_url length %d exceeds maximum length of %d", MaxWebsiteURL+over, MaxWebsiteURL) + } + iconErr := func(over int) string { + return fmt.Sprintf("icon_uri length %d exceeds maximum length of %d", MaxIconURI+over, MaxIconURI) + } + + tests := []struct { + name string + details MarketDetails + expErr string + }{ + { + name: "zero value", + details: MarketDetails{}, + expErr: "", + }, + { + name: "name at max length", + details: MarketDetails{Name: strings.Repeat("n", MaxName)}, + expErr: "", + }, + { + name: "name over max length", + details: MarketDetails{Name: strings.Repeat("n", MaxName+1)}, + expErr: nameErr(1), + }, + { + name: "description at max length", + details: MarketDetails{Description: strings.Repeat("d", MaxDescription)}, + expErr: "", + }, + { + name: "description over max length", + details: MarketDetails{Description: strings.Repeat("d", MaxDescription+1)}, + expErr: descErr(1), + }, + { + name: "website url at max length", + details: MarketDetails{WebsiteUrl: strings.Repeat("w", MaxWebsiteURL)}, + expErr: "", + }, + { + name: "website url over max length", + details: MarketDetails{WebsiteUrl: strings.Repeat("w", MaxWebsiteURL+1)}, + expErr: urlErr(1), + }, + { + name: "icon uri at max length", + details: MarketDetails{IconUri: strings.Repeat("i", MaxIconURI)}, + expErr: "", + }, + { + name: "icon uri over max length", + details: MarketDetails{IconUri: strings.Repeat("i", MaxIconURI+1)}, + expErr: iconErr(1), + }, + { + name: "all at max", + details: MarketDetails{ + Name: strings.Repeat("N", MaxName), + Description: strings.Repeat("D", MaxDescription), + WebsiteUrl: strings.Repeat("W", MaxWebsiteURL), + IconUri: strings.Repeat("I", MaxIconURI), + }, + expErr: "", + }, + { + name: "multiple errors", + details: MarketDetails{ + Name: strings.Repeat("N", MaxName+2), + Description: strings.Repeat("D", MaxDescription+3), + WebsiteUrl: strings.Repeat("W", MaxWebsiteURL+4), + IconUri: strings.Repeat("I", MaxIconURI+5), + }, + expErr: joinErrs(nameErr(2), descErr(3), urlErr(4), iconErr(5)), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = tc.details.Validate() + } + require.NotPanics(t, testFunc, "Validate") + + // TODO[1658]: Refactor to testutils.AssertErrorValue(t, err, tc.expErr, "Validate") + if len(tc.expErr) > 0 { + assert.EqualError(t, err, tc.expErr, "Validate") + } else { + assert.NoError(t, err, "Validate") + } + }) + } +} func TestValidateFeeRatios(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { From 335ee887ed30733ce4b6fec6b80b2271085f4434 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 17:31:25 -0600 Subject: [PATCH 032/309] [1658]: Create ValidateFeeOptions and use it in ValidateMarket. Indicate access grant field in access grant errors. Unit tests on ValidateFeeOptions and Market.Validate. --- x/exchange/market.go | 29 ++-- x/exchange/market_test.go | 295 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 301 insertions(+), 23 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index 144c7e8022..85634d3d9b 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -36,10 +36,10 @@ func (m Market) Validate() error { errs := []error{ m.MarketDetails.Validate(), - m.FeeCreateAskFlat.Validate(), - m.FeeCreateBidFlat.Validate(), - m.FeeSettlementSellerFlat.Validate(), - m.FeeSettlementBuyerFlat.Validate(), + ValidateFeeOptions("create ask flat fee", m.FeeCreateAskFlat), + ValidateFeeOptions("create bid flat fee", m.FeeCreateBidFlat), + ValidateFeeOptions("settlement seller flat fee", m.FeeSettlementSellerFlat), + ValidateFeeOptions("settlement buyer flat fee", m.FeeSettlementBuyerFlat), ValidateFeeRatios(m.FeeSettlementSellerRatios, m.FeeSettlementBuyerRatios), ValidateAccessGrants(m.AccessGrants), } @@ -56,6 +56,19 @@ func (m Market) Validate() error { return errors.Join(errs...) } +func ValidateFeeOptions(field string, options []sdk.Coin) error { + for _, coin := range options { + err := coin.Validate() + if err != nil { + return fmt.Errorf("invalid %s option %q: %w", field, coin, err) + } + if coin.IsZero() { + return fmt.Errorf("invalid %s option %q: amount cannot be zero", field, coin) + } + } + return nil +} + // Validate returns an error if anything in this MarketDetails is invalid. func (d MarketDetails) Validate() error { var errs []error @@ -242,19 +255,19 @@ func ValidateAccessGrants(accessGrants []*AccessGrant) error { func (a AccessGrant) Validate() error { _, err := sdk.AccAddressFromBech32(a.Address) if err != nil { - return fmt.Errorf("invalid address: %w", err) + return fmt.Errorf("invalid access grant: invalid address: %w", err) } if len(a.Permissions) == 0 { - return fmt.Errorf("no permissions provided for %s", a.Address) + return fmt.Errorf("invalid access grant: no permissions provided for %s", a.Address) } seen := make(map[Permission]bool) for _, perm := range a.Permissions { if seen[perm] { - return fmt.Errorf("%s appears multiple times for %s", perm.SimpleString(), a.Address) + return fmt.Errorf("invalid access grant: %s appears multiple times for %s", perm.SimpleString(), a.Address) } seen[perm] = true if err = perm.Validate(); err != nil { - return fmt.Errorf("%w for %s", err, a.Address) + return fmt.Errorf("invalid access grant: %w for %s", err, a.Address) } } return nil diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 3684ec44b9..81f8ab7e38 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -14,7 +14,267 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// TODO[1658]: func TestMarket_Validate(t *testing.T) +func TestMarket_Validate(t *testing.T) { + coins := func(coins string) sdk.Coins { + rv, err := sdk.ParseCoinsNormalized(coins) + require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) + return rv + } + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + addr1 := sdk.AccAddress("addr1_______________").String() + addr2 := sdk.AccAddress("addr2_______________").String() + + tests := []struct { + name string + market Market + expErr []string + }{ + { + name: "zero value", + market: Market{}, + expErr: nil, + }, + { + // A little bit of everything that should all be valid. + name: "control", + market: Market{ + MarketId: 1, + MarketDetails: MarketDetails{Name: "Test Market", Description: "Just a market for testing."}, + FeeCreateAskFlat: coins("10nnibbler,5mfry"), + FeeCreateBidFlat: coins("15nnibbler,5mfry"), + FeeSettlementSellerFlat: coins("50nnibler,8mfry"), + FeeSettlementSellerRatios: []*FeeRatio{ + {Price: coin(1000, "nnibler"), Fee: coin(1, "nnibler")}, + {Price: coin(300, "mfry"), Fee: coin(1, "mfry")}, + }, + FeeSettlementBuyerFlat: coins("100nnibler,20mfry"), + FeeSettlementBuyerRatios: []*FeeRatio{ + {Price: coin(500, "nnibler"), Fee: coin(1, "nnibler")}, + {Price: coin(500, "nnibler"), Fee: coin(8, "mfry")}, + {Price: coin(150, "mfry"), Fee: coin(1, "mfry")}, + {Price: coin(1, "mfry"), Fee: coin(1, "nnibler")}, + }, + AcceptingOrders: true, + AllowUserSettlement: true, + AccessGrants: []*AccessGrant{ + {Address: addr1, Permissions: AllPermissions()}, + {Address: addr2, Permissions: []Permission{Permission_settle}}, + }, + ReqAttrCreateAsk: []string{"kyc.ask.path", "*.ask.some.other.path"}, + ReqAttrCreateBid: []string{"kyc.bid.path", "*.bid.some.other.path"}, + }, + expErr: nil, + }, + { + name: "market id 0", + market: Market{MarketId: 0}, + expErr: nil, + }, + { + name: "invalid market details", + market: Market{MarketDetails: MarketDetails{Name: strings.Repeat("n", MaxName+1)}}, + expErr: []string{fmt.Sprintf("name length %d exceeds maximum length of %d", MaxName+1, MaxName)}, + }, + { + name: "invalid fee create ask flat", + market: Market{FeeCreateAskFlat: sdk.Coins{coin(-1, "leela")}}, + expErr: []string{`invalid create ask flat fee option "-1leela": negative coin amount: -1`}, + }, + { + name: "invalid fee create bid flat", + market: Market{FeeCreateBidFlat: sdk.Coins{coin(-1, "leela")}}, + expErr: []string{`invalid create bid flat fee option "-1leela": negative coin amount: -1`}, + }, + { + name: "invalid fee settlement seller flat", + market: Market{FeeSettlementSellerFlat: sdk.Coins{coin(-1, "leela")}}, + expErr: []string{`invalid settlement seller flat fee option "-1leela": negative coin amount: -1`}, + }, + { + name: "invalid fee settlement buyer flat", + market: Market{FeeSettlementBuyerFlat: sdk.Coins{coin(-1, "leela")}}, + expErr: []string{`invalid settlement buyer flat fee option "-1leela": negative coin amount: -1`}, + }, + { + name: "invalid seller ratio", + market: Market{FeeSettlementSellerRatios: []*FeeRatio{{Price: coin(0, "fry"), Fee: coin(1, "fry")}}}, + expErr: []string{`seller fee ratio price amount "0fry" must be positive`}, + }, + { + name: "invalid buyer ratio", + market: Market{FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(0, "fry"), Fee: coin(1, "fry")}}}, + expErr: []string{`buyer fee ratio price amount "0fry" must be positive`}, + }, + { + name: "invalid ratios", + market: Market{ + FeeSettlementSellerRatios: []*FeeRatio{{Price: coin(10, "fry"), Fee: coin(1, "fry")}}, + FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(100, "leela"), Fee: coin(1, "leela")}}, + }, + expErr: []string{ + `denom "fry" is defined in the seller settlement fee ratios but not buyer`, + `denom "leela" is defined in the buyer settlement fee ratios but not seller`, + }, + }, + { + name: "invalid access grants", + market: Market{AccessGrants: []*AccessGrant{{Address: "bad_addr", Permissions: AllPermissions()}}}, + expErr: []string{"invalid access grant: invalid address: decoding bech32 failed: invalid separator index -1"}, + }, + { + name: "invalid ask required attributes", + market: Market{ReqAttrCreateAsk: []string{"this-attr-is-bad"}}, + expErr: []string{`invalid create ask required attributes: invalid required attribute "this-attr-is-bad"`}, + }, + { + name: "invalid bid required attributes", + market: Market{ReqAttrCreateBid: []string{"this-attr-grrrr"}}, + expErr: []string{`invalid create bid required attributes: invalid required attribute "this-attr-grrrr"`}, + }, + { + name: "multiple errors", + market: Market{ + MarketDetails: MarketDetails{Name: strings.Repeat("n", MaxName+1)}, + FeeCreateAskFlat: sdk.Coins{coin(-1, "leela")}, + FeeCreateBidFlat: sdk.Coins{coin(-1, "leela")}, + FeeSettlementSellerFlat: sdk.Coins{coin(-1, "leela")}, + FeeSettlementBuyerFlat: sdk.Coins{coin(-1, "leela")}, + FeeSettlementSellerRatios: []*FeeRatio{{Price: coin(10, "fry"), Fee: coin(1, "fry")}}, + FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(100, "leela"), Fee: coin(1, "leela")}}, + AccessGrants: []*AccessGrant{{Address: "bad_addr", Permissions: AllPermissions()}}, + ReqAttrCreateAsk: []string{"this-attr-is-bad"}, + ReqAttrCreateBid: []string{"this-attr-grrrr"}, + }, + expErr: []string{ + fmt.Sprintf("name length %d exceeds maximum length of %d", MaxName+1, MaxName), + `invalid create ask flat fee option "-1leela": negative coin amount: -1`, + `invalid create bid flat fee option "-1leela": negative coin amount: -1`, + `invalid settlement seller flat fee option "-1leela": negative coin amount: -1`, + `invalid settlement buyer flat fee option "-1leela": negative coin amount: -1`, + `denom "fry" is defined in the seller settlement fee ratios but not buyer`, + `denom "leela" is defined in the buyer settlement fee ratios but not seller`, + "invalid access grant: invalid address: decoding bech32 failed: invalid separator index -1", + `invalid create ask required attributes: invalid required attribute "this-attr-is-bad"`, + `invalid create bid required attributes: invalid required attribute "this-attr-grrrr"`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = tc.market.Validate() + } + require.NotPanics(t, testFunc, "Market.Validate") + + // TODO[1658]: Refactor to testutils.AssertErrorContents(t, err, tc.expErr, "Market.Validate") + if len(tc.expErr) > 0 { + if assert.Error(t, err, "Market.Validate") { + for _, exp := range tc.expErr { + assert.ErrorContains(t, err, exp, "Market.Validate") + } + } + } else { + assert.NoError(t, err, "Market.Validate") + } + }) + } +} + +func TestValidateFeeOptions(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + field string + options []sdk.Coin + expErr string + }{ + { + name: "nil options", + field: "", + options: nil, + expErr: "", + }, + { + name: "empty options", + field: "", + options: []sdk.Coin{}, + expErr: "", + }, + { + name: "one option: good", + field: "", + options: []sdk.Coin{coin(1, "leela")}, + expErr: "", + }, + { + name: "one option: bad denom", + field: "test field one", + options: []sdk.Coin{coin(1, "%")}, + expErr: `invalid test field one option "1%": invalid denom: %`, + }, + { + name: "one option: zero amount", + field: "zero-amount", + options: []sdk.Coin{coin(0, "fry")}, + expErr: `invalid zero-amount option "0fry": amount cannot be zero`, + }, + { + name: "one option: negative amount", + field: "i-pay-u", + options: []sdk.Coin{coin(-1, "nibbler")}, + expErr: `invalid i-pay-u option "-1nibbler": negative coin amount: -1`, + }, + { + name: "three options: all good", + field: "", + options: []sdk.Coin{coin(5, "fry"), coin(2, "leela"), coin(1, "farnsworth")}, + expErr: "", + }, + { + name: "three options: bad first", + field: "coffee", + options: []sdk.Coin{coin(0, "fry"), coin(2, "leela"), coin(1, "farnsworth")}, + expErr: `invalid coffee option "0fry": amount cannot be zero`, + }, + { + name: "three options: bad second", + field: "eyeballs", + options: []sdk.Coin{coin(5, "fry"), coin(0, "leela"), coin(1, "farnsworth")}, + expErr: `invalid eyeballs option "0leela": amount cannot be zero`, + }, + { + name: "three options: bad third", + field: "eyeballs", + options: []sdk.Coin{coin(5, "fry"), coin(2, "leela"), coin(0, "farnsworth")}, + expErr: `invalid eyeballs option "0farnsworth": amount cannot be zero`, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = ValidateFeeOptions(tc.field, tc.options) + } + require.NotPanics(t, testFunc, "ValidateFeeOptions") + + // TODO[1658]: Refactor this to testutils.AssertErrorValue(t, err, tc.expErr, "ValidateFeeOptions") + if len(tc.expErr) > 0 { + assert.EqualError(t, err, tc.expErr, "ValidateFeeOptions") + } else { + assert.NoError(t, err, "ValidateFeeOptions") + } + }) + } +} func TestMarketDetails_Validate(t *testing.T) { joinErrs := func(errs ...string) string { @@ -675,7 +935,7 @@ func TestValidateAccessGrants(t *testing.T) { {Address: addr2, Permissions: AllPermissions()}, {Address: addr3, Permissions: AllPermissions()}, }, - exp: "permission -1 does not exist for " + addr1, + exp: "invalid access grant: permission -1 does not exist for " + addr1, }, { name: "three entries: invalid second", @@ -684,7 +944,7 @@ func TestValidateAccessGrants(t *testing.T) { {Address: addr2, Permissions: []Permission{-1}}, {Address: addr3, Permissions: AllPermissions()}, }, - exp: "permission -1 does not exist for " + addr2, + exp: "invalid access grant: permission -1 does not exist for " + addr2, }, { name: "three entries: invalid second", @@ -693,7 +953,7 @@ func TestValidateAccessGrants(t *testing.T) { {Address: addr2, Permissions: AllPermissions()}, {Address: addr3, Permissions: []Permission{-1}}, }, - exp: "permission -1 does not exist for " + addr3, + exp: "invalid access grant: permission -1 does not exist for " + addr3, }, { name: "three entries: only valid first", @@ -703,8 +963,8 @@ func TestValidateAccessGrants(t *testing.T) { {Address: addr3, Permissions: []Permission{-1}}, }, exp: joinErrs( - "permission is unspecified for "+addr2, - "permission -1 does not exist for "+addr3, + "invalid access grant: permission is unspecified for "+addr2, + "invalid access grant: permission -1 does not exist for "+addr3, ), }, { @@ -715,8 +975,8 @@ func TestValidateAccessGrants(t *testing.T) { {Address: addr3, Permissions: []Permission{-1}}, }, exp: joinErrs( - "permission is unspecified for "+addr1, - "permission -1 does not exist for "+addr3, + "invalid access grant: permission is unspecified for "+addr1, + "invalid access grant: permission -1 does not exist for "+addr3, ), }, { @@ -727,8 +987,8 @@ func TestValidateAccessGrants(t *testing.T) { {Address: addr3, Permissions: AllPermissions()}, }, exp: joinErrs( - "permission is unspecified for "+addr1, - "permission -1 does not exist for "+addr2, + "invalid access grant: permission is unspecified for "+addr1, + "invalid access grant: permission -1 does not exist for "+addr2, ), }, { @@ -763,20 +1023,25 @@ func TestAccessGrant_Validate(t *testing.T) { a AccessGrant exp string }{ + { + name: "control", + a: AccessGrant{Address: addr, Permissions: AllPermissions()}, + exp: "", + }, { name: "invalid address", a: AccessGrant{Address: "invalid_address_____", Permissions: []Permission{Permission_settle}}, - exp: "invalid address: decoding bech32 failed: invalid separator index -1", + exp: "invalid access grant: invalid address: decoding bech32 failed: invalid separator index -1", }, { name: "nil permissions", a: AccessGrant{Address: addr, Permissions: nil}, - exp: "no permissions provided for " + addr, + exp: "invalid access grant: no permissions provided for " + addr, }, { name: "empty permissions", a: AccessGrant{Address: addr, Permissions: []Permission{}}, - exp: "no permissions provided for " + addr, + exp: "invalid access grant: no permissions provided for " + addr, }, { name: "duplicate entry", @@ -788,7 +1053,7 @@ func TestAccessGrant_Validate(t *testing.T) { Permission_settle, }, }, - exp: "settle appears multiple times for " + addr, + exp: "invalid access grant: settle appears multiple times for " + addr, }, { name: "invalid entry", @@ -800,7 +1065,7 @@ func TestAccessGrant_Validate(t *testing.T) { Permission_attributes, }, }, - exp: "permission -1 does not exist for " + addr, + exp: "invalid access grant: permission -1 does not exist for " + addr, }, } From 74f9483630c6c5fa920afb05e2afb6182d70dc82 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 17:40:01 -0600 Subject: [PATCH 033/309] [1658]: Make the Markets and Orders genesis fields not allow nil entries. --- proto/provenance/exchange/v1/genesis.proto | 4 +-- x/exchange/genesis.go | 2 +- x/exchange/genesis.pb.go | 31 +++++++++++----------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/proto/provenance/exchange/v1/genesis.proto b/proto/provenance/exchange/v1/genesis.proto index 2f842f4bd9..6116c5a1b0 100644 --- a/proto/provenance/exchange/v1/genesis.proto +++ b/proto/provenance/exchange/v1/genesis.proto @@ -20,8 +20,8 @@ message GenesisState { Params params = 1; // markets are all of the markets to create at genesis. - repeated Market markets = 2; + repeated Market markets = 2 [(gogoproto.nullable) = false]; // orders are all the orders to create at genesis. - repeated Order orders = 3; + repeated Order orders = 3 [(gogoproto.nullable) = false]; } \ No newline at end of file diff --git a/x/exchange/genesis.go b/x/exchange/genesis.go index 83a56e37df..1e1098dc02 100644 --- a/x/exchange/genesis.go +++ b/x/exchange/genesis.go @@ -5,7 +5,7 @@ import ( "fmt" ) -func NewGenesisState(params *Params, markets []*Market, orders []*Order) *GenesisState { +func NewGenesisState(params *Params, markets []Market, orders []Order) *GenesisState { return &GenesisState{ Params: params, Markets: markets, diff --git a/x/exchange/genesis.pb.go b/x/exchange/genesis.pb.go index e3733d06eb..8ecb92f53a 100644 --- a/x/exchange/genesis.pb.go +++ b/x/exchange/genesis.pb.go @@ -28,9 +28,9 @@ type GenesisState struct { // params defines all the parameters of the exchange module. Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` // markets are all of the markets to create at genesis. - Markets []*Market `protobuf:"bytes,2,rep,name=markets,proto3" json:"markets,omitempty"` + Markets []Market `protobuf:"bytes,2,rep,name=markets,proto3" json:"markets"` // orders are all the orders to create at genesis. - Orders []*Order `protobuf:"bytes,3,rep,name=orders,proto3" json:"orders,omitempty"` + Orders []Order `protobuf:"bytes,3,rep,name=orders,proto3" json:"orders"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -75,24 +75,25 @@ func init() { } var fileDescriptor_087ceebafabf03c9 = []byte{ - // 265 bytes of a gzipped FileDescriptorProto + // 273 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x29, 0x28, 0xca, 0x2f, 0x4b, 0xcd, 0x4b, 0xcc, 0x4b, 0x4e, 0xd5, 0x4f, 0xad, 0x48, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0xd5, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x43, 0xa8, 0xd2, 0x83, 0xa9, 0xd2, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x2b, 0xd1, 0x07, 0xb1, 0x20, 0xaa, 0xa5, 0x94, 0x71, 0x98, 0x99, 0x9b, 0x58, 0x94, 0x9d, 0x5a, 0x42, 0x40, 0x51, 0x7e, 0x51, 0x4a, 0x6a, 0x51, 0x31, 0x01, 0x45, 0x05, 0x89, 0x45, 0x89, - 0xb9, 0x50, 0x45, 0x4a, 0x07, 0x19, 0xb9, 0x78, 0xdc, 0x21, 0xce, 0x0d, 0x2e, 0x49, 0x2c, 0x49, + 0xb9, 0x50, 0x45, 0x4a, 0x67, 0x19, 0xb9, 0x78, 0xdc, 0x21, 0xce, 0x0d, 0x2e, 0x49, 0x2c, 0x49, 0x15, 0x32, 0xe3, 0x62, 0x83, 0x28, 0x90, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd3, 0xc3, - 0xee, 0x7c, 0xbd, 0x00, 0xb0, 0xaa, 0x20, 0xa8, 0x6a, 0x21, 0x0b, 0x2e, 0x76, 0x88, 0x13, 0x8b, - 0x25, 0x98, 0x14, 0x98, 0xf1, 0x69, 0xf4, 0x05, 0x2b, 0x0b, 0x82, 0x29, 0x17, 0x32, 0xe5, 0x62, - 0x83, 0xb8, 0x5b, 0x82, 0x19, 0xac, 0x51, 0x16, 0x97, 0x46, 0x7f, 0x90, 0xaa, 0x20, 0xa8, 0x62, - 0x2b, 0x8e, 0x8e, 0x05, 0xf2, 0x0c, 0x2f, 0x16, 0xc8, 0x33, 0x38, 0xa5, 0x9e, 0x78, 0x24, 0xc7, - 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, - 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x03, 0x97, 0x64, 0x66, 0x3e, 0x0e, 0xc3, 0x02, 0x18, 0xa3, 0xf4, - 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x11, 0x8a, 0x74, 0x33, 0xf3, - 0x91, 0x78, 0xfa, 0x15, 0xf0, 0xa0, 0x4b, 0x62, 0x03, 0x87, 0x98, 0x31, 0x20, 0x00, 0x00, 0xff, - 0xff, 0x31, 0x50, 0xe7, 0x5f, 0xf6, 0x01, 0x00, 0x00, + 0xee, 0x7c, 0xbd, 0x00, 0xb0, 0xaa, 0x20, 0xa8, 0x6a, 0x21, 0x3b, 0x2e, 0x76, 0x88, 0x13, 0x8b, + 0x25, 0x98, 0x14, 0x98, 0xf1, 0x69, 0xf4, 0x05, 0x2b, 0x73, 0x62, 0x39, 0x71, 0x4f, 0x9e, 0x21, + 0x08, 0xa6, 0x49, 0xc8, 0x9a, 0x8b, 0x0d, 0xe2, 0x7a, 0x09, 0x66, 0xb0, 0x76, 0x59, 0x5c, 0xda, + 0xfd, 0x41, 0xaa, 0xa0, 0xba, 0xa1, 0x5a, 0xac, 0x38, 0x3a, 0x16, 0xc8, 0x33, 0xbc, 0x58, 0x20, + 0xcf, 0xe0, 0x94, 0x7a, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, + 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x5c, 0x92, 0x99, + 0xf9, 0x38, 0x8c, 0x0c, 0x60, 0x8c, 0xd2, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, + 0xcf, 0xd5, 0x47, 0x28, 0xd2, 0xcd, 0xcc, 0x47, 0xe2, 0xe9, 0x57, 0xc0, 0x83, 0x31, 0x89, 0x0d, + 0x1c, 0x7a, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x98, 0x3a, 0x7e, 0x02, 0x02, 0x00, + 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -294,7 +295,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Markets = append(m.Markets, &Market{}) + m.Markets = append(m.Markets, Market{}) if err := m.Markets[len(m.Markets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -328,7 +329,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Orders = append(m.Orders, &Order{}) + m.Orders = append(m.Orders, Order{}) if err := m.Orders[len(m.Orders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } From 865cf1a5df00a7aef8378e397421f498dbb562d0 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 17:48:00 -0600 Subject: [PATCH 034/309] [1658]: Make it so the DenomSplits field can't have nil entries. --- proto/provenance/exchange/v1/params.proto | 4 ++- x/exchange/params.go | 2 +- x/exchange/params.pb.go | 34 ++++++++++++----------- x/exchange/params_test.go | 10 +++---- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/proto/provenance/exchange/v1/params.proto b/proto/provenance/exchange/v1/params.proto index f8952cb6d7..73fb3964d7 100644 --- a/proto/provenance/exchange/v1/params.proto +++ b/proto/provenance/exchange/v1/params.proto @@ -6,6 +6,8 @@ option go_package = "github.com/provenance-io/provenance/x/exchange"; option java_package = "io.provenance.exchange.v1"; option java_multiple_files = true; +import "gogoproto/gogo.proto"; + // Params is a representation of the exchange module parameters. message Params { // default_split is the default proportion of fees the exchange receives in basis points. @@ -13,7 +15,7 @@ message Params { // E.g. 100 = 1%. Min = 0, Max = 10000. uint32 default_split = 1; // denom_splits are the denom-specific amounts the exchange receives. - repeated DenomSplit denom_splits = 2; + repeated DenomSplit denom_splits = 2 [(gogoproto.nullable) = false]; } // DenomSplit associates a coin denomination with an amount the exchange receives for that denom. diff --git a/x/exchange/params.go b/x/exchange/params.go index a838936c93..25286a704c 100644 --- a/x/exchange/params.go +++ b/x/exchange/params.go @@ -16,7 +16,7 @@ const ( MaxSplit = uint32(10_000) ) -func NewParams(defaultSplit uint32, denomSplits []*DenomSplit) *Params { +func NewParams(defaultSplit uint32, denomSplits []DenomSplit) *Params { return &Params{ DefaultSplit: defaultSplit, DenomSplits: denomSplits, diff --git a/x/exchange/params.pb.go b/x/exchange/params.pb.go index c87a3d0b20..bba524b724 100644 --- a/x/exchange/params.pb.go +++ b/x/exchange/params.pb.go @@ -5,6 +5,7 @@ package exchange import ( fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" math "math" @@ -29,7 +30,7 @@ type Params struct { // E.g. 100 = 1%. Min = 0, Max = 10000. DefaultSplit uint32 `protobuf:"varint,1,opt,name=default_split,json=defaultSplit,proto3" json:"default_split,omitempty"` // denom_splits are the denom-specific amounts the exchange receives. - DenomSplits []*DenomSplit `protobuf:"bytes,2,rep,name=denom_splits,json=denomSplits,proto3" json:"denom_splits,omitempty"` + DenomSplits []DenomSplit `protobuf:"bytes,2,rep,name=denom_splits,json=denomSplits,proto3" json:"denom_splits"` } func (m *Params) Reset() { *m = Params{} } @@ -72,7 +73,7 @@ func (m *Params) GetDefaultSplit() uint32 { return 0 } -func (m *Params) GetDenomSplits() []*DenomSplit { +func (m *Params) GetDenomSplits() []DenomSplit { if m != nil { return m.DenomSplits } @@ -145,22 +146,23 @@ func init() { } var fileDescriptor_5d689cfc7a7422f1 = []byte{ - // 235 bytes of a gzipped FileDescriptorProto + // 255 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2e, 0x28, 0xca, 0x2f, 0x4b, 0xcd, 0x4b, 0xcc, 0x4b, 0x4e, 0xd5, 0x4f, 0xad, 0x48, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0xd5, 0x2f, 0x33, 0xd4, 0x2f, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, - 0x12, 0x43, 0x28, 0xd2, 0x83, 0x29, 0xd2, 0x2b, 0x33, 0x54, 0x2a, 0xe1, 0x62, 0x0b, 0x00, 0xab, - 0x13, 0x52, 0xe6, 0xe2, 0x4d, 0x49, 0x4d, 0x4b, 0x2c, 0xcd, 0x29, 0x89, 0x2f, 0x2e, 0xc8, 0xc9, - 0x2c, 0x91, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0d, 0xe2, 0x81, 0x0a, 0x06, 0x83, 0xc4, 0x84, 0x5c, - 0xb9, 0x78, 0x52, 0x52, 0xf3, 0xf2, 0x73, 0x21, 0x4a, 0x8a, 0x25, 0x98, 0x14, 0x98, 0x35, 0xb8, - 0x8d, 0x94, 0xf4, 0xb0, 0x9b, 0xae, 0xe7, 0x02, 0x52, 0x0b, 0xd6, 0x19, 0xc4, 0x9d, 0x02, 0x67, - 0x17, 0x2b, 0x59, 0x70, 0x71, 0x21, 0xa4, 0x84, 0x44, 0xb8, 0x58, 0xc1, 0x92, 0x60, 0x1b, 0x39, - 0x83, 0x20, 0x1c, 0x90, 0x28, 0xc4, 0x1d, 0x4c, 0x60, 0x77, 0x40, 0x38, 0x4e, 0xa9, 0x27, 0x1e, - 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, - 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0xc0, 0x25, 0x99, 0x99, 0x8f, 0xc3, 0x19, 0x01, 0x8c, - 0x51, 0x7a, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x08, 0x45, 0xba, - 0x99, 0xf9, 0x48, 0x3c, 0xfd, 0x0a, 0x78, 0xf0, 0x25, 0xb1, 0x81, 0x43, 0xcd, 0x18, 0x10, 0x00, - 0x00, 0xff, 0xff, 0x70, 0xb8, 0x10, 0x1a, 0x5c, 0x01, 0x00, 0x00, + 0x12, 0x43, 0x28, 0xd2, 0x83, 0x29, 0xd2, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, + 0x2b, 0xd1, 0x07, 0xb1, 0x20, 0xaa, 0x95, 0xaa, 0xb8, 0xd8, 0x02, 0xc0, 0xba, 0x85, 0x94, 0xb9, + 0x78, 0x53, 0x52, 0xd3, 0x12, 0x4b, 0x73, 0x4a, 0xe2, 0x8b, 0x0b, 0x72, 0x32, 0x4b, 0x24, 0x18, + 0x15, 0x18, 0x35, 0x78, 0x83, 0x78, 0xa0, 0x82, 0xc1, 0x20, 0x31, 0x21, 0x6f, 0x2e, 0x9e, 0x94, + 0xd4, 0xbc, 0xfc, 0x5c, 0x88, 0x92, 0x62, 0x09, 0x26, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x25, 0x3d, + 0xec, 0x76, 0xea, 0xb9, 0x80, 0xd4, 0x82, 0x75, 0x3a, 0xb1, 0x9c, 0xb8, 0x27, 0xcf, 0x10, 0xc4, + 0x9d, 0x02, 0x17, 0x29, 0x56, 0xb2, 0xe0, 0xe2, 0x42, 0x28, 0x10, 0x12, 0xe1, 0x62, 0x05, 0x4b, + 0x82, 0xed, 0xe5, 0x0c, 0x82, 0x70, 0x40, 0xa2, 0x10, 0xd7, 0x30, 0x81, 0x5d, 0x03, 0xe1, 0x38, + 0xa5, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, + 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x03, 0x97, 0x64, 0x66, 0x3e, 0x0e, + 0xc7, 0x04, 0x30, 0x46, 0xe9, 0xa5, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, + 0x23, 0x14, 0xe9, 0x66, 0xe6, 0x23, 0xf1, 0xf4, 0x2b, 0xe0, 0x41, 0x9b, 0xc4, 0x06, 0x0e, 0x23, + 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x06, 0x26, 0x5b, 0x1a, 0x78, 0x01, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -368,7 +370,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.DenomSplits = append(m.DenomSplits, &DenomSplit{}) + m.DenomSplits = append(m.DenomSplits, DenomSplit{}) if err := m.DenomSplits[len(m.DenomSplits)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } diff --git a/x/exchange/params_test.go b/x/exchange/params_test.go index bcd7fb538e..0fb90cca21 100644 --- a/x/exchange/params_test.go +++ b/x/exchange/params_test.go @@ -18,7 +18,7 @@ func TestNewParams(t *testing.T) { tests := []struct { name string defaultSplit uint32 - denomSplits []*DenomSplit + denomSplits []DenomSplit expected *Params }{ { @@ -36,13 +36,13 @@ func TestNewParams(t *testing.T) { { name: "123 with two denom splits", defaultSplit: 123, - denomSplits: []*DenomSplit{ + denomSplits: []DenomSplit{ {Denom: "atom", Split: 234}, {Denom: "nhash", Split: 56}, }, expected: &Params{ DefaultSplit: 123, - DenomSplits: []*DenomSplit{ + DenomSplits: []DenomSplit{ {Denom: "atom", Split: 234}, {Denom: "nhash", Split: 56}, }, @@ -93,14 +93,14 @@ func TestParams_Validate(t *testing.T) { }, { name: "one denom split and it is bad", - params: Params{DenomSplits: []*DenomSplit{{Denom: "badcointype", Split: 10_001}}}, + params: Params{DenomSplits: []DenomSplit{{Denom: "badcointype", Split: 10_001}}}, expErr: []string{"badcointype split 10001 cannot be greater than 10000"}, }, { name: "bad default and three bad denom splits", params: Params{ DefaultSplit: 10_001, - DenomSplits: []*DenomSplit{ + DenomSplits: []DenomSplit{ {Denom: "badcointype", Split: 10_002}, {Denom: "x", Split: 3}, {Denom: "thisIsNotGood", Split: 10_003}, From 233af404d41aca97c81f8e90f4bd30be8b26e159 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 18:17:11 -0600 Subject: [PATCH 035/309] [1658]: Create an Order.GetMarketID() function and unit test it. --- x/exchange/orders.go | 17 ++++++++++++- x/exchange/orders_test.go | 51 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 7d9a33325e..fdaffc5a2f 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -61,12 +61,27 @@ func (o Order) OrderTypeByte() byte { case *Order_BidOrder: return OrderTypeByteBid default: - // If OrderType() is called without the order being set yet, it's a programming error, so panic. + // If OrderTypeByte() is called without the order being set yet, it's a programming error, so panic. // If it's a type without a case, the case needs to be added, so panic. panic(fmt.Sprintf("OrderTypeByte() missing case for %T", v)) } } +// GetMarketID returns the market id for this order. +// Panics if the order details are not set or are something unexpected. +func (o Order) GetMarketID() uint32 { + switch v := o.GetOrder().(type) { + case *Order_AskOrder: + return v.AskOrder.MarketId + case *Order_BidOrder: + return v.BidOrder.MarketId + default: + // If GetMarketID() is called without the order being set yet, it's a programming error, so panic. + // If it's a type without a case, the case needs to be added, so panic. + panic(fmt.Sprintf("GetMarketID() missing case for %T", v)) + } +} + func (o Order) Validate() error { if o.OrderId == 0 { return errors.New("invalid order id: must not be zero") diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 5b6c2a11e2..f8f5c73c72 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -235,8 +235,8 @@ func TestOrder_OrderType(t *testing.T) { require.PanicsWithValue(t, tc.expPanic, testFunc, "OrderType") } else { require.NotPanics(t, testFunc, "OrderType") - assert.Equal(t, tc.expected, actual, "OrderType result") } + assert.Equal(t, tc.expected, actual, "OrderType result") }) } } @@ -282,8 +282,55 @@ func TestOrder_OrderTypeByte(t *testing.T) { require.PanicsWithValue(t, tc.expPanic, testFunc, "OrderTypeByte") } else { require.NotPanics(t, testFunc, "OrderTypeByte") - assert.Equal(t, tc.expected, actual, "OrderTypeByte result") } + assert.Equal(t, tc.expected, actual, "OrderTypeByte result") + }) + } +} + +func TestOrder_GetMarketID(t *testing.T) { + tests := []struct { + name string + order *Order + expected uint32 + expPanic interface{} + }{ + { + name: "AskOrder", + order: NewOrder(1).WithAsk(&AskOrder{MarketId: 123}), + expected: 123, + }, + { + name: "BidOrder", + order: NewOrder(2).WithBid(&BidOrder{MarketId: 437}), + expected: 437, + }, + { + name: "nil inside order", + order: NewOrder(3), + expPanic: "GetMarketID() missing case for ", + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + expPanic: "GetMarketID() missing case for *exchange.unknownOrderType", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual uint32 + testFunc := func() { + actual = tc.order.GetMarketID() + } + + // TODO[1658]: Refactor to use testutils.RequirePanicEquals(t, testFunc, tc.expPanic, "GetMarketID") + if tc.expPanic != nil { + require.PanicsWithValue(t, tc.expPanic, testFunc, "GetMarketID") + } else { + require.NotPanics(t, testFunc, "GetMarketID") + } + assert.Equal(t, tc.expected, actual, "GetMarketID result") }) } } From ff391a1c973b2014048e76e093c40d1c3554098c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 18:59:43 -0600 Subject: [PATCH 036/309] [1658]: Update GenesisState.Validate to prevent duplicate ids and make sure orders have a valid market id. Unit tests on the genesis funcs. --- x/exchange/genesis.go | 31 ++- x/exchange/genesis_test.go | 433 ++++++++++++++++++++++++++++++++++++- 2 files changed, 460 insertions(+), 4 deletions(-) diff --git a/x/exchange/genesis.go b/x/exchange/genesis.go index 1e1098dc02..a498096507 100644 --- a/x/exchange/genesis.go +++ b/x/exchange/genesis.go @@ -22,19 +22,48 @@ func (g GenesisState) Validate() error { if g.Params != nil { if err := g.Params.Validate(); err != nil { - errs = append(errs, err) + errs = append(errs, fmt.Errorf("invalid params: %w", err)) } } + marketIDs := make(map[uint32]int) for i, market := range g.Markets { + if market.MarketId == 0 { + errs = append(errs, fmt.Errorf("invalid market[%d]: market id cannot be zero", i)) + continue + } + + j, seen := marketIDs[market.MarketId] + if seen { + errs = append(errs, fmt.Errorf("invalid market[%d]: duplicate market id %d seen at [%d]", i, market.MarketId, j)) + continue + } + marketIDs[market.MarketId] = i + if err := market.Validate(); err != nil { errs = append(errs, fmt.Errorf("invalid market[%d]: %w", i, err)) } } + orderIDs := make(map[uint64]int) for i, order := range g.Orders { + if order.OrderId != 0 { + j, seen := orderIDs[order.OrderId] + if seen { + errs = append(errs, fmt.Errorf("invalid order[%d]: duplicate order id %d seen at [%d]", i, order.OrderId, j)) + continue + } + orderIDs[order.OrderId] = i + } + if err := order.Validate(); err != nil { errs = append(errs, fmt.Errorf("invalid order[%d]: %w", i, err)) + continue + } + + _, knownMarket := marketIDs[order.GetMarketID()] + if !knownMarket { + errs = append(errs, fmt.Errorf("invalid order[%d]: unknown market id %d", i, order.GetMarketID())) } } diff --git a/x/exchange/genesis_test.go b/x/exchange/genesis_test.go index 61439fca98..3b9fdc50f9 100644 --- a/x/exchange/genesis_test.go +++ b/x/exchange/genesis_test.go @@ -1,7 +1,434 @@ package exchange -// TODO[1658]: func TestNewGenesisState(t *testing.t) +import ( + "testing" -// TODO[1658]: func TestDefaultGenesisState(t *testing.t) + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" -// TODO[1658]: func TestGenesisState_Validate(t *testing.t) + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestNewGenesisState(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + params *Params + markets []Market + orders []Order + expected *GenesisState + }{ + { + name: "all nil", + params: nil, + markets: nil, + orders: nil, + expected: &GenesisState{}, + }, + { + name: "only params", + params: &Params{ + DefaultSplit: 54, + DenomSplits: []DenomSplit{ + {Denom: "farnsworth", Split: 88}, + {Denom: "fry", Split: 1000}, + }, + }, + expected: &GenesisState{ + Params: &Params{ + DefaultSplit: 54, + DenomSplits: []DenomSplit{ + {Denom: "farnsworth", Split: 88}, + {Denom: "fry", Split: 1000}, + }, + }, + }, + }, + { + name: "only markets", + markets: []Market{ + { + MarketId: 1, + MarketDetails: MarketDetails{Name: "Market One"}, + FeeCreateAskFlat: []sdk.Coin{coin(5, "askd")}, + AcceptingOrders: true, + }, + { + MarketId: 2, + MarketDetails: MarketDetails{Name: "Market Two"}, + FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, + FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, + AccessGrants: []*AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, + ReqAttrCreateBid: []string{"just.some.attr"}, + }, + }, + expected: &GenesisState{ + Params: nil, + Markets: []Market{ + { + MarketId: 1, + MarketDetails: MarketDetails{Name: "Market One"}, + FeeCreateAskFlat: []sdk.Coin{coin(5, "askd")}, + AcceptingOrders: true, + }, + { + MarketId: 2, + MarketDetails: MarketDetails{Name: "Market Two"}, + FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, + FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, + AccessGrants: []*AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, + ReqAttrCreateBid: []string{"just.some.attr"}, + }, + }, + Orders: nil, + }, + }, + { + name: "just orders", + orders: []Order{ + *NewOrder(1).WithAsk(&AskOrder{ + MarketId: 1, + Seller: "seller_addr", + Assets: []sdk.Coin{coin(55, "fry")}, + Price: coin(888, "nibbler"), + }), + *NewOrder(1).WithBid(&BidOrder{ + MarketId: 1, + Buyer: "buyer_addr", + Assets: []sdk.Coin{coin(55, "fry")}, + Price: coin(888, "nibbler"), + }), + }, + expected: &GenesisState{ + Params: nil, + Markets: nil, + Orders: []Order{ + *NewOrder(1).WithAsk(&AskOrder{ + MarketId: 1, + Seller: "seller_addr", + Assets: []sdk.Coin{coin(55, "fry")}, + Price: coin(888, "nibbler"), + }), + *NewOrder(1).WithBid(&BidOrder{ + MarketId: 1, + Buyer: "buyer_addr", + Assets: []sdk.Coin{coin(55, "fry")}, + Price: coin(888, "nibbler"), + }), + }, + }, + }, + { + name: "all three together", + params: &Params{ + DefaultSplit: 54, + DenomSplits: []DenomSplit{ + {Denom: "farnsworth", Split: 88}, + {Denom: "fry", Split: 1000}, + }, + }, + markets: []Market{ + { + MarketId: 1, + MarketDetails: MarketDetails{Name: "Market One"}, + FeeCreateAskFlat: []sdk.Coin{coin(5, "askd")}, + AcceptingOrders: true, + }, + { + MarketId: 2, + MarketDetails: MarketDetails{Name: "Market Two"}, + FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, + FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, + AccessGrants: []*AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, + ReqAttrCreateBid: []string{"just.some.attr"}, + }, + }, + orders: []Order{ + *NewOrder(1).WithAsk(&AskOrder{ + MarketId: 1, + Seller: "seller_addr", + Assets: []sdk.Coin{coin(55, "fry")}, + Price: coin(888, "nibbler"), + }), + *NewOrder(1).WithBid(&BidOrder{ + MarketId: 1, + Buyer: "buyer_addr", + Assets: []sdk.Coin{coin(55, "fry")}, + Price: coin(888, "nibbler"), + }), + }, + expected: &GenesisState{ + Params: &Params{ + DefaultSplit: 54, + DenomSplits: []DenomSplit{ + {Denom: "farnsworth", Split: 88}, + {Denom: "fry", Split: 1000}, + }, + }, + Markets: []Market{ + { + MarketId: 1, + MarketDetails: MarketDetails{Name: "Market One"}, + FeeCreateAskFlat: []sdk.Coin{coin(5, "askd")}, + AcceptingOrders: true, + }, + { + MarketId: 2, + MarketDetails: MarketDetails{Name: "Market Two"}, + FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, + FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, + AccessGrants: []*AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, + ReqAttrCreateBid: []string{"just.some.attr"}, + }, + }, + Orders: []Order{ + *NewOrder(1).WithAsk(&AskOrder{ + MarketId: 1, + Seller: "seller_addr", + Assets: []sdk.Coin{coin(55, "fry")}, + Price: coin(888, "nibbler"), + }), + *NewOrder(1).WithBid(&BidOrder{ + MarketId: 1, + Buyer: "buyer_addr", + Assets: []sdk.Coin{coin(55, "fry")}, + Price: coin(888, "nibbler"), + }), + }, + }, + }, + { + name: "defaults", + params: DefaultParams(), + markets: nil, + orders: nil, + expected: DefaultGenesisState(), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + actual := NewGenesisState(tc.params, tc.markets, tc.orders) + assert.Equal(t, tc.expected, actual, "NewGenesisState") + }) + } +} + +func TestGenesisState_Validate(t *testing.T) { + addr1 := sdk.AccAddress("addr1_______________").String() + coins := func(coins string) sdk.Coins { + rv, err := sdk.ParseCoinsNormalized(coins) + require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) + return rv + } + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + askOrder := func(orderID uint64, marketID uint32, assets string, price string) Order { + priceCoin, err := sdk.ParseCoinNormalized(price) + require.NoError(t, err, "sdk.ParseCoinNormalized(%q)", price) + return *NewOrder(orderID).WithAsk(&AskOrder{ + MarketId: marketID, + Seller: addr1, + Assets: coins(assets), + Price: priceCoin, + }) + } + bidOrder := func(orderID uint64, marketID uint32, assets string, price string) Order { + priceCoin, err := sdk.ParseCoinNormalized(price) + require.NoError(t, err, "sdk.ParseCoinNormalized(%q)", price) + return *NewOrder(orderID).WithBid(&BidOrder{ + MarketId: marketID, + Buyer: addr1, + Assets: coins(assets), + Price: priceCoin, + }) + } + + tests := []struct { + name string + genState GenesisState + expErr []string + }{ + { + name: "zero state", + genState: GenesisState{}, + expErr: nil, + }, + { + name: "default state", + genState: *DefaultGenesisState(), + expErr: nil, + }, + { + name: "empty params", + genState: GenesisState{Params: &Params{}}, + expErr: nil, + }, + { + name: "invalid params", + genState: GenesisState{Params: &Params{DefaultSplit: 10_001}}, + expErr: []string{"invalid params: default split 10001 cannot be greater than 10000"}, + }, + { + name: "three markets, 3 orders each all valid", + genState: GenesisState{ + Params: DefaultParams(), + Markets: []Market{ + {MarketId: 1}, + {MarketId: 2}, + {MarketId: 3}, + }, + Orders: []Order{ + askOrder(1, 1, "99fry", "9leela"), + bidOrder(2, 1, "100fry", "10leela"), + bidOrder(3, 1, "101fry", "11leela"), + askOrder(4, 2, "12zapp", "1farnsworth"), + askOrder(5, 2, "14zapp", "1farnsworth"), + bidOrder(6, 2, "80zapp", "10farnsworth"), + bidOrder(7, 3, "5wong", "50nibbler"), + bidOrder(8, 3, "5wong", "50nibbler"), + bidOrder(9, 3, "5wong", "50nibbler"), + }, + }, + expErr: nil, + }, + { + name: "three markets: all invalid", + genState: GenesisState{ + Markets: []Market{ + {MarketId: 1, FeeCreateAskFlat: sdk.Coins{coin(-1, "kif")}}, + {MarketId: 2, FeeCreateAskFlat: sdk.Coins{coin(-2, "kif")}}, + {MarketId: 3, FeeCreateAskFlat: sdk.Coins{coin(-3, "kif")}}, + }, + }, + expErr: []string{ + `invalid market[0]: invalid create ask flat fee option "-1kif": negative coin amount: -1`, + `invalid market[1]: invalid create ask flat fee option "-2kif": negative coin amount: -2`, + `invalid market[2]: invalid create ask flat fee option "-3kif": negative coin amount: -3`, + }, + }, + { + name: "three markets: all market id zero", + genState: GenesisState{ + Markets: []Market{ + {MarketId: 0}, + {MarketId: 0}, + {MarketId: 0}, + }, + }, + expErr: []string{ + `invalid market[0]: market id cannot be zero`, + `invalid market[1]: market id cannot be zero`, + `invalid market[2]: market id cannot be zero`, + }, + }, + { + name: "three markets: all market id one", + genState: GenesisState{ + Markets: []Market{ + {MarketId: 1}, + {MarketId: 1}, + {MarketId: 1}, + }, + }, + expErr: []string{ + `invalid market[1]: duplicate market id 1 seen at [0]`, + `invalid market[2]: duplicate market id 1 seen at [0]`, + }, + }, + { + name: "three orders: all invalid", + genState: GenesisState{ + Markets: []Market{{MarketId: 4}, {MarketId: 5}, {MarketId: 6}}, + Orders: []Order{ + askOrder(0, 4, "28fry", "2bender"), + bidOrder(0, 5, "28fry", "2bender"), + askOrder(0, 6, "28fry", "2bender"), + }, + }, + expErr: []string{ + `invalid order[0]: invalid order id: must not be zero`, + `invalid order[1]: invalid order id: must not be zero`, + `invalid order[2]: invalid order id: must not be zero`, + }, + }, + { + name: "three orders: all unknown markets", + genState: GenesisState{ + Markets: []Market{{MarketId: 4}, {MarketId: 5}, {MarketId: 6}}, + Orders: []Order{ + askOrder(1, 1, "28fry", "2bender"), + bidOrder(2, 2, "28fry", "2bender"), + askOrder(3, 3, "28fry", "2bender"), + }, + }, + expErr: []string{ + `invalid order[0]: unknown market id 1`, + `invalid order[1]: unknown market id 2`, + `invalid order[2]: unknown market id 3`, + }, + }, + { + name: "three orders: all id one", + genState: GenesisState{ + Markets: []Market{{MarketId: 4}, {MarketId: 5}, {MarketId: 6}}, + Orders: []Order{ + askOrder(1, 4, "28fry", "2bender"), + bidOrder(1, 5, "28fry", "2bender"), + askOrder(1, 6, "28fry", "2bender"), + }, + }, + expErr: []string{ + `invalid order[1]: duplicate order id 1 seen at [0]`, + `invalid order[2]: duplicate order id 1 seen at [0]`, + }, + }, + { + name: "multiple errors", + genState: GenesisState{ + Params: &Params{DefaultSplit: 10_001}, + Markets: []Market{ + {MarketId: 1}, + {MarketId: 2, FeeCreateBidFlat: sdk.Coins{coin(-1, "zapp")}}, + {MarketId: 3}, + }, + Orders: []Order{ + askOrder(1, 1, "28fry", "2bender"), + bidOrder(2, 4, "28fry", "2bender"), + askOrder(3, 3, "28fry", "2bender"), + }, + }, + expErr: []string{ + "invalid params: default split 10001 cannot be greater than 10000", + `invalid market[1]: invalid create bid flat fee option "-1zapp": negative coin amount: -1`, + `invalid order[1]: unknown market id 4`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = tc.genState.Validate() + } + require.NotPanics(t, testFunc, "GenesisState.Validate") + + // TODO[1658]: Replace this with testutils.AssertErrorContents(t, err, tc.expErr, "GenesisState.Validate") + if len(tc.expErr) > 0 { + if assert.Error(t, err, "GenesisState.Validate") { + for _, exp := range tc.expErr { + assert.ErrorContains(t, err, exp, "GenesisState.Validate") + } + } + } else { + assert.NoError(t, err, "GenesisState.Validate") + } + }) + } +} From 62e981107fca4ffc4f97584d929fecb921aba35b Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 28 Aug 2023 19:02:16 -0600 Subject: [PATCH 037/309] [1658]: Fix a test that broke when I changed the receiver for OrderType. --- x/exchange/events_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go index 69c4dfc6e7..cfa848ae7e 100644 --- a/x/exchange/events_test.go +++ b/x/exchange/events_test.go @@ -39,7 +39,7 @@ func TestNewEventOrderCreated(t *testing.T) { }{ { name: "nil order", - order: nil, + order: NewOrder(3), expPanic: "OrderType() missing case for ", }, { From 86dabf937e9ba8ed8472f466aec609b310c579e1 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 29 Aug 2023 13:00:07 -0600 Subject: [PATCH 038/309] [1658]: Get rid of the CreateMarket endpoint and just use the gov version. Can reconsider that later if it's a problem. Fill in the fields of the gov prop messages. Add a query to check a create market gov prop. --- docs/proto-docs.md | 77 +- proto/provenance/exchange/v1/query.proto | 16 +- proto/provenance/exchange/v1/tx.proto | 87 +- x/exchange/msg.go | 11 - x/exchange/query.pb.go | 369 +++++- x/exchange/query.pb.gw.go | 62 + x/exchange/tx.pb.go | 1423 +++++++++++++++++----- 7 files changed, 1624 insertions(+), 421 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index c8e3ae11ec..6c0b6f3857 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -99,6 +99,8 @@ - [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) - [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) - [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) + - [QueryIsValidMarketRequest](#provenance.exchange.v1.QueryIsValidMarketRequest) + - [QueryIsValidMarketResponse](#provenance.exchange.v1.QueryIsValidMarketResponse) - [QueryMarketInfoRequest](#provenance.exchange.v1.QueryMarketInfoRequest) - [QueryMarketInfoResponse](#provenance.exchange.v1.QueryMarketInfoResponse) - [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) @@ -117,8 +119,6 @@ - [MsgCreateAskResponse](#provenance.exchange.v1.MsgCreateAskResponse) - [MsgCreateBidRequest](#provenance.exchange.v1.MsgCreateBidRequest) - [MsgCreateBidResponse](#provenance.exchange.v1.MsgCreateBidResponse) - - [MsgCreateMarketRequest](#provenance.exchange.v1.MsgCreateMarketRequest) - - [MsgCreateMarketResponse](#provenance.exchange.v1.MsgCreateMarketResponse) - [MsgFillAsksRequest](#provenance.exchange.v1.MsgFillAsksRequest) - [MsgFillAsksResponse](#provenance.exchange.v1.MsgFillAsksResponse) - [MsgFillBidsRequest](#provenance.exchange.v1.MsgFillBidsRequest) @@ -1935,6 +1935,26 @@ QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. + + +### QueryIsValidMarketRequest +QueryIsValidMarketRequest is a request message for the QueryIsValidMarket endpoint. + + + + + + + + +### QueryIsValidMarketResponse +QueryIsValidMarketResponse is a response message for the QueryIsValidMarket endpoint. + + + + + + ### QueryMarketInfoRequest @@ -2036,6 +2056,7 @@ Query is the service for exchange module's query endpoints. | `QueryGetAllOrders` | [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) | [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) | QueryGetAllOrders gets all orders in the exchange module. | GET|/provenance/exchange/v1/orders| | `QueryMarketInfo` | [QueryMarketInfoRequest](#provenance.exchange.v1.QueryMarketInfoRequest) | [QueryMarketInfoResponse](#provenance.exchange.v1.QueryMarketInfoResponse) | QueryMarketInfo returns the information/details about a market. | GET|/provenance/exchange/v1/market/infoGET|/provenance/exchange/v1/market/details| | `QueryParams` | [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) | [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) | QueryParams returns the exchange module parameters. | GET|/provenance/exchange/v1/params| +| `QueryIsValidMarket` | [QueryIsValidMarketRequest](#provenance.exchange.v1.QueryIsValidMarketRequest) | [QueryIsValidMarketResponse](#provenance.exchange.v1.QueryIsValidMarketResponse) | QueryIsValidMarket checks the provided market and returns any errors that would be encountered trying to create it. | GET|/provenance/exchange/v1/market/validate| @@ -2108,26 +2129,6 @@ MsgCreateBidResponse is a response message for the CreateBid endpoint. - - -### MsgCreateMarketRequest -MsgCreateMarketRequest is a request message for the CreateMarket endpoint. - - - - - - - - -### MsgCreateMarketResponse -MsgCreateMarketResponse is a response message for the CreateMarket endpoint. - - - - - - ### MsgFillAsksRequest @@ -2174,6 +2175,12 @@ MsgFillBidsResponse is a response message for the FillBids endpoint. MsgGovCreateMarketRequest is a request message for the GovCreateMarket endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `authority` | [string](#string) | | authority should be the governance module account address. | +| `market` | [Market](#provenance.exchange.v1.Market) | | market is the initial market configuration. If the market_id is 0, the next available market_id will be used (once voting ends). If it is not zero, it must not yet be in use when the voting period ends. | + + @@ -2194,6 +2201,23 @@ MsgGovCreateMarketResponse is a response message for the GovCreateMarket endpoin MsgGovManageFeesRequest is a request message for the GovManageFees endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `authority` | [string](#string) | | authority should be the governance module account address. | +| `add_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_ask_flat are the create ask flat fee options to add. | +| `remove_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_ask_flat are the create ask flat fee options to remove. | +| `add_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_bid_flat are the create bid flat fee options to add. | +| `remove_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_bid_flat are the create bid flat fee options to remove. | +| `add_fee_settlement_seller_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_settlement_seller_flat are the seller settlement flat fee options to add. | +| `remove_fee_settlement_seller_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_settlement_seller_flat are the seller settlement flat fee options to remove. | +| `add_fee_settlement_seller_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | add_fee_settlement_seller_ratios are the seller settlement fee ratios to add. | +| `remove_fee_settlement_seller_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | remove_fee_settlement_seller_ratios are the seller settlement fee ratios to remove. | +| `add_fee_settlement_buyer_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_settlement_buyer_flat are the buyer settlement flat fee options to add. | +| `remove_fee_settlement_buyer_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_settlement_buyer_flat are the buyer settlement flat fee options to remove. | +| `add_fee_settlement_buyer_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | add_fee_settlement_buyer_ratios are the buyer settlement fee ratios to add. | +| `remove_fee_settlement_buyer_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | remove_fee_settlement_buyer_ratios are the buyer settlement fee ratios to remove. | + + @@ -2214,6 +2238,12 @@ MsgGovManageFeesResponse is a response message for the GovManageFees endpoint. MsgGovUpdateParamsRequest is a request message for the GovUpdateParams endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `authority` | [string](#string) | | authority should be the governance module account address. | +| `params` | [Params](#provenance.exchange.v1.Params) | | params are the new param values to set | + + @@ -2393,8 +2423,7 @@ Msg is the service for exchange module's tx endpoints. | `MarketUpdateUserSettle` | [MsgMarketUpdateUserSettleRequest](#provenance.exchange.v1.MsgMarketUpdateUserSettleRequest) | [MsgMarketUpdateUserSettleResponse](#provenance.exchange.v1.MsgMarketUpdateUserSettleResponse) | MarketUpdateUserSettle is a market endpoint to update whether it allows user-initiated settlement. | | | `MarketManagePermissions` | [MsgMarketManagePermissionsRequest](#provenance.exchange.v1.MsgMarketManagePermissionsRequest) | [MsgMarketManagePermissionsResponse](#provenance.exchange.v1.MsgMarketManagePermissionsResponse) | MarketManagePermissions is a market endpoint to manage a market's user permissions. | | | `MarketManageReqAttrs` | [MsgMarketManageReqAttrsRequest](#provenance.exchange.v1.MsgMarketManageReqAttrsRequest) | [MsgMarketManageReqAttrsResponse](#provenance.exchange.v1.MsgMarketManageReqAttrsResponse) | MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. | | -| `CreateMarket` | [MsgCreateMarketRequest](#provenance.exchange.v1.MsgCreateMarketRequest) | [MsgCreateMarketResponse](#provenance.exchange.v1.MsgCreateMarketResponse) | CreateMarket reserves the next market id and submits a GovCreateMarket governance proposal to create the market. | | -| `GovCreateMarket` | [MsgGovCreateMarketRequest](#provenance.exchange.v1.MsgGovCreateMarketRequest) | [MsgGovCreateMarketResponse](#provenance.exchange.v1.MsgGovCreateMarketResponse) | GovCreateMarket is a governance proposal endpoint for creating a market. The CreateMarket endpoint should be used to submit one of these unless you don't need to know the market id until after the proposal passes. | | +| `GovCreateMarket` | [MsgGovCreateMarketRequest](#provenance.exchange.v1.MsgGovCreateMarketRequest) | [MsgGovCreateMarketResponse](#provenance.exchange.v1.MsgGovCreateMarketResponse) | GovCreateMarket is a governance proposal endpoint for creating a market. | | | `GovManageFees` | [MsgGovManageFeesRequest](#provenance.exchange.v1.MsgGovManageFeesRequest) | [MsgGovManageFeesResponse](#provenance.exchange.v1.MsgGovManageFeesResponse) | GovManageFees is a governance proposal endpoint for updating a market's fees. | | | `GovUpdateParams` | [MsgGovUpdateParamsRequest](#provenance.exchange.v1.MsgGovUpdateParamsRequest) | [MsgGovUpdateParamsResponse](#provenance.exchange.v1.MsgGovUpdateParamsResponse) | GovUpdateParams is a governance proposal endpoint for updating the exchange module's params. | | diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 5a0977705c..8ac1edb41f 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -53,6 +53,11 @@ service Query { rpc QueryParams(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/provenance/exchange/v1/params"; } + + // QueryIsValidMarket checks the provided market and returns any errors that would be encountered trying to create it. + rpc QueryIsValidMarket(QueryIsValidMarketRequest) returns (QueryIsValidMarketResponse) { + option (google.api.http).get = "/provenance/exchange/v1/market/validate"; + } } // QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint. @@ -128,4 +133,13 @@ message QueryParamsRequest { // QueryParamsResponse is a response message for the QueryParams endpoint. message QueryParamsResponse { // TODO[1658]: QueryParamsResponse -} \ No newline at end of file +} + +// QueryIsValidMarketRequest is a request message for the QueryIsValidMarket endpoint. +message QueryIsValidMarketRequest { + // TODO[1658]: QueryIsValidMarketRequest +} +// QueryIsValidMarketResponse is a response message for the QueryIsValidMarket endpoint. +message QueryIsValidMarketResponse { + // TODO[1658]: QueryIsValidMarketResponse +} diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 3dff5c8346..599da0159e 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -6,6 +6,13 @@ option go_package = "github.com/provenance-io/provenance/x/exchange"; option java_package = "io.provenance.exchange.v1"; option java_multiple_files = true; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/msg/v1/msg.proto"; +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; +import "provenance/exchange/v1/market.proto"; +import "provenance/exchange/v1/params.proto"; + // Msg is the service for exchange module's tx endpoints. service Msg { // CreateAsk creates an ask order (to sell something you own). @@ -44,12 +51,7 @@ service Msg { // MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. rpc MarketManageReqAttrs(MsgMarketManageReqAttrsRequest) returns (MsgMarketManageReqAttrsResponse); - // CreateMarket reserves the next market id and submits a GovCreateMarket governance proposal to create the market. - rpc CreateMarket(MsgCreateMarketRequest) returns (MsgCreateMarketResponse); - // GovCreateMarket is a governance proposal endpoint for creating a market. - // The CreateMarket endpoint should be used to submit one of these unless you don't need to know the market id until - // after the proposal passes. rpc GovCreateMarket(MsgGovCreateMarketRequest) returns (MsgGovCreateMarketResponse); // GovManageFees is a governance proposal endpoint for updating a market's fees. @@ -155,17 +157,17 @@ message MsgMarketManageReqAttrsRequest { // MsgMarketManageReqAttrsResponse is a response message for the MarketManageReqAttrs endpoint. message MsgMarketManageReqAttrsResponse {} -// MsgCreateMarketRequest is a request message for the CreateMarket endpoint. -message MsgCreateMarketRequest { - // TODO[1658]: MsgCreateMarketRequest -} - -// MsgCreateMarketResponse is a response message for the CreateMarket endpoint. -message MsgCreateMarketResponse {} - // MsgGovCreateMarketRequest is a request message for the GovCreateMarket endpoint. message MsgGovCreateMarketRequest { - // TODO[1658]: MsgGovCreateMarketRequest + option (cosmos.msg.v1.signer) = "authority"; + + // authority should be the governance module account address. + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + + // market is the initial market configuration. + // If the market_id is 0, the next available market_id will be used (once voting ends). + // If it is not zero, it must not yet be in use when the voting period ends. + Market market = 2 [(gogoproto.nullable) = false]; } // MsgGovCreateMarketResponse is a response message for the GovCreateMarket endpoint. @@ -173,7 +175,54 @@ message MsgGovCreateMarketResponse {} // MsgGovManageFeesRequest is a request message for the GovManageFees endpoint. message MsgGovManageFeesRequest { - // TODO[1658]: MsgGovManageFeesRequest + option (cosmos.msg.v1.signer) = "authority"; + + // authority should be the governance module account address. + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + + // add_fee_create_ask_flat are the create ask flat fee options to add. + repeated cosmos.base.v1beta1.Coin add_fee_create_ask_flat = 2 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // remove_fee_create_ask_flat are the create ask flat fee options to remove. + repeated cosmos.base.v1beta1.Coin remove_fee_create_ask_flat = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // add_fee_create_bid_flat are the create bid flat fee options to add. + repeated cosmos.base.v1beta1.Coin add_fee_create_bid_flat = 4 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // remove_fee_create_bid_flat are the create bid flat fee options to remove. + repeated cosmos.base.v1beta1.Coin remove_fee_create_bid_flat = 5 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // add_fee_settlement_seller_flat are the seller settlement flat fee options to add. + repeated cosmos.base.v1beta1.Coin add_fee_settlement_seller_flat = 6 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // remove_fee_settlement_seller_flat are the seller settlement flat fee options to remove. + repeated cosmos.base.v1beta1.Coin remove_fee_settlement_seller_flat = 7 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // add_fee_settlement_seller_ratios are the seller settlement fee ratios to add. + repeated FeeRatio add_fee_settlement_seller_ratios = 8 [(gogoproto.nullable) = false]; + + // remove_fee_settlement_seller_ratios are the seller settlement fee ratios to remove. + repeated FeeRatio remove_fee_settlement_seller_ratios = 9 [(gogoproto.nullable) = false]; + + // add_fee_settlement_buyer_flat are the buyer settlement flat fee options to add. + repeated cosmos.base.v1beta1.Coin add_fee_settlement_buyer_flat = 10 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // remove_fee_settlement_buyer_flat are the buyer settlement flat fee options to remove. + repeated cosmos.base.v1beta1.Coin remove_fee_settlement_buyer_flat = 11 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // add_fee_settlement_buyer_ratios are the buyer settlement fee ratios to add. + repeated FeeRatio add_fee_settlement_buyer_ratios = 12 [(gogoproto.nullable) = false]; + + // remove_fee_settlement_buyer_ratios are the buyer settlement fee ratios to remove. + repeated FeeRatio remove_fee_settlement_buyer_ratios = 13 [(gogoproto.nullable) = false]; } // MsgGovManageFeesResponse is a response message for the GovManageFees endpoint. @@ -181,7 +230,13 @@ message MsgGovManageFeesResponse {} // MsgGovUpdateParamsRequest is a request message for the GovUpdateParams endpoint. message MsgGovUpdateParamsRequest { - // TODO[1658]: MsgGovUpdateParamsRequest + option (cosmos.msg.v1.signer) = "authority"; + + // authority should be the governance module account address. + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + + // params are the new param values to set + Params params = 2 [(gogoproto.nullable) = false]; } // MsgGovUpdateParamsResponse is a response message for the GovUpdateParams endpoint. diff --git a/x/exchange/msg.go b/x/exchange/msg.go index ae53ca01b9..7f1ce4aa34 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -15,7 +15,6 @@ var allRequestMsgs = []sdk.Msg{ (*MsgMarketUpdateUserSettleRequest)(nil), (*MsgMarketManagePermissionsRequest)(nil), (*MsgMarketManageReqAttrsRequest)(nil), - (*MsgCreateMarketRequest)(nil), (*MsgGovCreateMarketRequest)(nil), (*MsgGovManageFeesRequest)(nil), (*MsgGovUpdateParamsRequest)(nil), @@ -141,16 +140,6 @@ func (m MsgMarketManageReqAttrsRequest) GetSigners() []sdk.AccAddress { panic("not implemented") } -func (m MsgCreateMarketRequest) ValidateBasic() error { - // TODO[1658]: MsgCreateMarketRequest.ValidateBasic() - return nil -} - -func (m MsgCreateMarketRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgCreateMarketRequest.GetSigners - panic("not implemented") -} - func (m MsgGovCreateMarketRequest) ValidateBasic() error { // TODO[1658]: MsgGovCreateMarketRequest.ValidateBasic() return nil diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index 5c5a2c81d4..9ee041d782 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -645,6 +645,80 @@ func (m *QueryParamsResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo +// QueryIsValidMarketRequest is a request message for the QueryIsValidMarket endpoint. +type QueryIsValidMarketRequest struct { +} + +func (m *QueryIsValidMarketRequest) Reset() { *m = QueryIsValidMarketRequest{} } +func (m *QueryIsValidMarketRequest) String() string { return proto.CompactTextString(m) } +func (*QueryIsValidMarketRequest) ProtoMessage() {} +func (*QueryIsValidMarketRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{16} +} +func (m *QueryIsValidMarketRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryIsValidMarketRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryIsValidMarketRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryIsValidMarketRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryIsValidMarketRequest.Merge(m, src) +} +func (m *QueryIsValidMarketRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryIsValidMarketRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryIsValidMarketRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryIsValidMarketRequest proto.InternalMessageInfo + +// QueryIsValidMarketResponse is a response message for the QueryIsValidMarket endpoint. +type QueryIsValidMarketResponse struct { +} + +func (m *QueryIsValidMarketResponse) Reset() { *m = QueryIsValidMarketResponse{} } +func (m *QueryIsValidMarketResponse) String() string { return proto.CompactTextString(m) } +func (*QueryIsValidMarketResponse) ProtoMessage() {} +func (*QueryIsValidMarketResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{17} +} +func (m *QueryIsValidMarketResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryIsValidMarketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryIsValidMarketResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryIsValidMarketResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryIsValidMarketResponse.Merge(m, src) +} +func (m *QueryIsValidMarketResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryIsValidMarketResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryIsValidMarketResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryIsValidMarketResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*QueryOrderFeeCalcRequest)(nil), "provenance.exchange.v1.QueryOrderFeeCalcRequest") proto.RegisterType((*QueryOrderFeeCalcResponse)(nil), "provenance.exchange.v1.QueryOrderFeeCalcResponse") @@ -662,6 +736,8 @@ func init() { proto.RegisterType((*QueryMarketInfoResponse)(nil), "provenance.exchange.v1.QueryMarketInfoResponse") proto.RegisterType((*QueryParamsRequest)(nil), "provenance.exchange.v1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "provenance.exchange.v1.QueryParamsResponse") + proto.RegisterType((*QueryIsValidMarketRequest)(nil), "provenance.exchange.v1.QueryIsValidMarketRequest") + proto.RegisterType((*QueryIsValidMarketResponse)(nil), "provenance.exchange.v1.QueryIsValidMarketResponse") } func init() { @@ -669,50 +745,53 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 685 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xcb, 0x6e, 0xd3, 0x40, - 0x14, 0xed, 0x20, 0xa0, 0xed, 0x54, 0x15, 0xe2, 0xd2, 0x47, 0xe2, 0x16, 0x53, 0x19, 0x54, 0x4a, - 0xa1, 0x9e, 0xa6, 0xa5, 0x48, 0xb0, 0xa3, 0x48, 0xa0, 0x2e, 0x50, 0x4b, 0x2a, 0x36, 0xdd, 0x54, - 0x6e, 0x3c, 0x4d, 0x2d, 0x1c, 0x4f, 0xea, 0x71, 0xa2, 0xa2, 0x28, 0x9b, 0x7e, 0x00, 0x42, 0x62, - 0x8f, 0x58, 0xf0, 0x09, 0x08, 0xb1, 0xe0, 0x03, 0x60, 0x57, 0xc1, 0x86, 0x25, 0x4a, 0xf8, 0x10, - 0x94, 0xf1, 0xd8, 0x89, 0x13, 0x3f, 0x92, 0xa5, 0xe7, 0x9e, 0x73, 0xcf, 0xc9, 0xcc, 0xbd, 0x47, - 0xc1, 0x5a, 0xd5, 0x65, 0x75, 0xea, 0x18, 0x4e, 0x89, 0x12, 0x7a, 0x56, 0x3a, 0x31, 0x9c, 0x32, - 0x25, 0xf5, 0x02, 0x39, 0xad, 0x51, 0xf7, 0xad, 0x5e, 0x75, 0x99, 0xc7, 0x60, 0xae, 0x8b, 0xd1, - 0x03, 0x8c, 0x5e, 0x2f, 0x28, 0xf9, 0x12, 0xe3, 0x15, 0xc6, 0x0f, 0x05, 0x8a, 0xf8, 0x1f, 0x3e, - 0x45, 0x59, 0x2c, 0x33, 0x56, 0xb6, 0x29, 0x31, 0xaa, 0x16, 0x31, 0x1c, 0x87, 0x79, 0x86, 0x67, - 0x31, 0x47, 0x56, 0x35, 0x05, 0xe7, 0x5e, 0x75, 0xfa, 0xef, 0xba, 0x26, 0x75, 0x9f, 0x53, 0xfa, - 0xcc, 0xb0, 0x4b, 0x45, 0x7a, 0x5a, 0xa3, 0xdc, 0xd3, 0x16, 0x70, 0x3e, 0xa6, 0xc6, 0xab, 0xcc, - 0xe1, 0x54, 0xbb, 0x85, 0x6f, 0x8a, 0xe2, 0x3e, 0xf5, 0x3c, 0x9b, 0x56, 0xa8, 0xe3, 0xf5, 0xb1, - 0x97, 0xb0, 0x9a, 0x04, 0x90, 0x2d, 0x0a, 0x78, 0x46, 0x20, 0x5e, 0x50, 0x4f, 0x48, 0x48, 0x26, - 0xe4, 0xf1, 0x04, 0xeb, 0x7c, 0x1f, 0x5a, 0x66, 0x0e, 0x2d, 0xa1, 0x95, 0xcb, 0xc5, 0x71, 0xf1, - 0xbd, 0x63, 0x6a, 0xf3, 0x78, 0xb6, 0x8f, 0x22, 0x7b, 0x3d, 0xc1, 0x0b, 0x41, 0xe1, 0xa5, 0xe1, - 0xbe, 0x91, 0x65, 0x1e, 0xb4, 0x5c, 0xc0, 0x93, 0x15, 0x71, 0x1c, 0xf4, 0x9c, 0x2e, 0x4e, 0xf8, - 0x07, 0x3b, 0xa6, 0xa6, 0xe2, 0xc5, 0x78, 0xae, 0xec, 0x5d, 0xec, 0xd6, 0x9f, 0x9a, 0xa6, 0x4b, - 0x39, 0x8f, 0x36, 0xdf, 0xc0, 0xe3, 0x86, 0x7f, 0x2e, 0x5a, 0x4f, 0x6e, 0xe7, 0x7e, 0x7d, 0x59, - 0x9b, 0x91, 0x8f, 0x20, 0x19, 0xfb, 0x9e, 0x6b, 0x39, 0xe5, 0x62, 0x00, 0x0c, 0xaf, 0x6f, 0xb0, - 0xa7, 0x14, 0x0d, 0x1e, 0xa6, 0x03, 0xb0, 0xed, 0x88, 0x60, 0xf8, 0x30, 0xd1, 0x9a, 0x24, 0xe6, - 0xf0, 0x9c, 0x28, 0xfa, 0x3f, 0x65, 0xc7, 0x39, 0x66, 0x01, 0x2d, 0x8f, 0xe7, 0x07, 0x2a, 0x92, - 0x34, 0x83, 0x41, 0x94, 0xf6, 0x0c, 0xd7, 0xa8, 0x84, 0x3a, 0xb3, 0xf8, 0x46, 0xe4, 0xd4, 0x07, - 0x6f, 0x9c, 0x4f, 0xe1, 0x2b, 0xe2, 0x1c, 0x3e, 0x23, 0x7c, 0x7d, 0x60, 0x44, 0x60, 0x5d, 0x8f, - 0x9f, 0x52, 0x3d, 0x69, 0xd2, 0x94, 0xc2, 0x08, 0x0c, 0xe9, 0x78, 0xf5, 0xfc, 0xf7, 0xbf, 0x0f, - 0x97, 0xee, 0x80, 0x46, 0x12, 0xd6, 0xe6, 0x98, 0x52, 0x4e, 0xc4, 0xdc, 0xc0, 0x37, 0x24, 0xef, - 0x64, 0x60, 0x16, 0x61, 0x2b, 0x55, 0x39, 0x69, 0xb8, 0x95, 0x47, 0xa3, 0xd2, 0xa4, 0x6b, 0x22, - 0x5c, 0xdf, 0x83, 0xbb, 0xa9, 0xae, 0x79, 0xc8, 0x87, 0x8f, 0x08, 0x4f, 0x47, 0x26, 0x1e, 0x1e, - 0xa4, 0x4a, 0xf7, 0xed, 0x92, 0xb2, 0x36, 0x24, 0x5a, 0xfa, 0x5b, 0x17, 0xfe, 0x56, 0x61, 0x25, - 0xc9, 0x9f, 0xb8, 0x50, 0xd2, 0x08, 0xf6, 0xb3, 0x09, 0xdf, 0x51, 0x77, 0x8b, 0x7b, 0xb7, 0x07, - 0x36, 0xb3, 0x94, 0x63, 0xf6, 0x54, 0x79, 0x38, 0x1a, 0x49, 0xba, 0x7e, 0x2c, 0x5c, 0x6f, 0x42, - 0x21, 0xc9, 0xb5, 0xbf, 0xea, 0xa4, 0x11, 0x66, 0x40, 0xd3, 0xff, 0x21, 0x1c, 0xbe, 0xa2, 0x6e, - 0xa2, 0x44, 0x16, 0x11, 0x32, 0xad, 0xc4, 0x65, 0x81, 0xb2, 0x35, 0x22, 0x6b, 0xa4, 0x7b, 0xe7, - 0xa4, 0x21, 0xf3, 0xa3, 0x09, 0x9f, 0x82, 0xd5, 0xeb, 0x0d, 0x81, 0x8c, 0xd5, 0x8b, 0xc9, 0x92, - 0x8c, 0xd5, 0x8b, 0x4d, 0x98, 0x65, 0x61, 0x76, 0x09, 0xd4, 0x74, 0xb3, 0xf0, 0x13, 0xe1, 0x6b, - 0x7d, 0x81, 0x03, 0x7a, 0xaa, 0xdc, 0x40, 0x66, 0x29, 0x64, 0x68, 0xbc, 0x34, 0xf7, 0x5a, 0x98, - 0xdb, 0x85, 0xdb, 0x19, 0xb3, 0x60, 0x39, 0xc7, 0xec, 0x60, 0x05, 0x96, 0x33, 0x60, 0x26, 0xf5, - 0x0c, 0xcb, 0xe6, 0xf0, 0x0e, 0xe1, 0xa9, 0x9e, 0x2c, 0x84, 0xd5, 0x54, 0x5f, 0x91, 0x18, 0x55, - 0xee, 0x0f, 0x85, 0x1d, 0xf6, 0x72, 0xab, 0x02, 0xbf, 0x4d, 0x7f, 0xb4, 0x54, 0x74, 0xd1, 0x52, - 0xd1, 0xdf, 0x96, 0x8a, 0xde, 0xb7, 0xd5, 0xb1, 0x8b, 0xb6, 0x3a, 0xf6, 0xa7, 0xad, 0x8e, 0xe1, - 0xbc, 0xc5, 0x12, 0x04, 0xf7, 0xd0, 0x81, 0x5e, 0xb6, 0xbc, 0x93, 0xda, 0x91, 0x5e, 0x62, 0x95, - 0x1e, 0x81, 0x35, 0x8b, 0xf5, 0xca, 0x9d, 0x85, 0x82, 0x47, 0x57, 0xc5, 0xdf, 0x84, 0xcd, 0xff, - 0x01, 0x00, 0x00, 0xff, 0xff, 0x63, 0xe3, 0x39, 0xae, 0x9d, 0x08, 0x00, 0x00, + // 729 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xcf, 0x4f, 0x13, 0x41, + 0x14, 0x66, 0x8c, 0x0a, 0x8c, 0x12, 0xe3, 0x93, 0x1f, 0xed, 0x82, 0x2b, 0x59, 0x0d, 0x22, 0xca, + 0x0e, 0x05, 0x31, 0xd1, 0x9b, 0x98, 0x68, 0x7a, 0x30, 0x60, 0x89, 0x1e, 0xb8, 0x90, 0xa1, 0x3b, + 0x94, 0x8d, 0xdb, 0x9d, 0xb2, 0xb3, 0x34, 0x18, 0xc2, 0xc5, 0x3f, 0xc0, 0x98, 0x78, 0x37, 0x1e, + 0x3c, 0x7a, 0x31, 0x31, 0xc6, 0x83, 0x7f, 0x80, 0xde, 0x88, 0x5e, 0x3c, 0x1a, 0xea, 0x1f, 0x62, + 0x3a, 0x3b, 0xdb, 0x76, 0xdb, 0xdd, 0x6d, 0xf7, 0xb8, 0xf3, 0xbe, 0xef, 0x7d, 0x5f, 0x67, 0xde, + 0xfb, 0x52, 0x6c, 0xd4, 0x3c, 0x5e, 0x67, 0x2e, 0x75, 0xcb, 0x8c, 0xb0, 0xc3, 0xf2, 0x1e, 0x75, + 0x2b, 0x8c, 0xd4, 0x0b, 0x64, 0xff, 0x80, 0x79, 0xaf, 0xcc, 0x9a, 0xc7, 0x7d, 0x0e, 0x93, 0x6d, + 0x8c, 0x19, 0x62, 0xcc, 0x7a, 0x41, 0xcb, 0x97, 0xb9, 0xa8, 0x72, 0xb1, 0x2d, 0x51, 0x24, 0xf8, + 0x08, 0x28, 0xda, 0x4c, 0x85, 0xf3, 0x8a, 0xc3, 0x08, 0xad, 0xd9, 0x84, 0xba, 0x2e, 0xf7, 0xa9, + 0x6f, 0x73, 0x57, 0x55, 0x0d, 0x0d, 0xe7, 0x9e, 0x35, 0xfb, 0xaf, 0x7b, 0x16, 0xf3, 0x1e, 0x33, + 0xf6, 0x88, 0x3a, 0xe5, 0x12, 0xdb, 0x3f, 0x60, 0xc2, 0x37, 0xa6, 0x71, 0x3e, 0xa6, 0x26, 0x6a, + 0xdc, 0x15, 0xcc, 0xb8, 0x86, 0xaf, 0xca, 0xe2, 0x26, 0xf3, 0x7d, 0x87, 0x55, 0x99, 0xeb, 0x77, + 0xb1, 0x67, 0xb1, 0x9e, 0x04, 0x50, 0x2d, 0x0a, 0x78, 0x5c, 0x22, 0x9e, 0x30, 0x5f, 0x4a, 0x28, + 0x26, 0xe4, 0xf1, 0x08, 0x6f, 0x7e, 0x6f, 0xdb, 0x56, 0x0e, 0xcd, 0xa2, 0xf9, 0xb3, 0xa5, 0x61, + 0xf9, 0x5d, 0xb4, 0x8c, 0x29, 0x3c, 0xd1, 0x45, 0x51, 0xbd, 0x1e, 0xe0, 0xe9, 0xb0, 0xf0, 0x94, + 0x7a, 0x2f, 0x55, 0x59, 0x84, 0x2d, 0xa7, 0xf1, 0x68, 0x55, 0x1e, 0x87, 0x3d, 0xc7, 0x4a, 0x23, + 0xc1, 0x41, 0xd1, 0x32, 0x74, 0x3c, 0x13, 0xcf, 0x55, 0xbd, 0x4b, 0xed, 0xfa, 0x43, 0xcb, 0xf2, + 0x98, 0x10, 0xd1, 0xe6, 0xcb, 0x78, 0x98, 0x06, 0xe7, 0xb2, 0xf5, 0xe8, 0x5a, 0xee, 0xd7, 0x97, + 0xc5, 0x71, 0xf5, 0x08, 0x8a, 0xb1, 0xe9, 0x7b, 0xb6, 0x5b, 0x29, 0x85, 0xc0, 0xd6, 0xf5, 0xf5, + 0xf6, 0x54, 0xa2, 0xe1, 0xc3, 0x34, 0x01, 0x8e, 0x13, 0x11, 0x6c, 0x3d, 0x4c, 0xb4, 0xa6, 0x88, + 0x39, 0x3c, 0x29, 0x8b, 0xc1, 0x4f, 0x29, 0xba, 0xbb, 0x3c, 0xa4, 0xe5, 0xf1, 0x54, 0x4f, 0x45, + 0x91, 0xc6, 0x31, 0xc8, 0xd2, 0x06, 0xf5, 0x68, 0xb5, 0xa5, 0x33, 0x81, 0xaf, 0x44, 0x4e, 0x15, + 0x38, 0x94, 0x2f, 0x8a, 0x17, 0xd4, 0xb1, 0xad, 0xa0, 0x5d, 0xc8, 0x99, 0xc1, 0x5a, 0x5c, 0x31, + 0xa0, 0x2e, 0x7f, 0xbe, 0x88, 0xcf, 0xc9, 0x32, 0x7c, 0x44, 0xf8, 0x72, 0xcf, 0x74, 0xc1, 0x92, + 0x19, 0x3f, 0xe0, 0x66, 0xd2, 0x90, 0x6a, 0x85, 0x0c, 0x0c, 0xe5, 0x7f, 0xe1, 0xf5, 0xef, 0x7f, + 0xef, 0xce, 0xdc, 0x00, 0x83, 0x24, 0x6c, 0xdc, 0x2e, 0x63, 0x82, 0xc8, 0x91, 0x83, 0x6f, 0x48, + 0x5d, 0x67, 0xcf, 0x18, 0xc3, 0x6a, 0xaa, 0x72, 0xd2, 0x5e, 0x68, 0xf7, 0xb2, 0xd2, 0x94, 0x6b, + 0x22, 0x5d, 0xdf, 0x82, 0x9b, 0xa9, 0xae, 0x45, 0x8b, 0x0f, 0xef, 0x11, 0x1e, 0x8b, 0x2c, 0x0b, + 0xdc, 0x49, 0x95, 0xee, 0x5a, 0x43, 0x6d, 0x71, 0x40, 0xb4, 0xf2, 0xb7, 0x24, 0xfd, 0x2d, 0xc0, + 0x7c, 0x92, 0x3f, 0x79, 0xa1, 0xe4, 0x28, 0x5c, 0xed, 0x63, 0xf8, 0x8e, 0xda, 0x01, 0xd0, 0xb9, + 0x78, 0xb0, 0xd2, 0x4f, 0x39, 0x66, 0xc5, 0xb5, 0xbb, 0xd9, 0x48, 0xca, 0xf5, 0x7d, 0xe9, 0x7a, + 0x05, 0x0a, 0x49, 0xae, 0x83, 0x94, 0x20, 0x47, 0xad, 0xf8, 0x38, 0x0e, 0x7e, 0x88, 0x80, 0xaf, + 0xa8, 0x1d, 0x46, 0x91, 0x1d, 0x86, 0xbe, 0x56, 0xe2, 0x62, 0x44, 0x5b, 0xcd, 0xc8, 0xca, 0x74, + 0xef, 0x82, 0x1c, 0xa9, 0xe8, 0x39, 0x86, 0x0f, 0xe1, 0xea, 0x75, 0xe6, 0x47, 0x9f, 0xd5, 0x8b, + 0x89, 0xa1, 0x3e, 0xab, 0x17, 0x1b, 0x4e, 0x73, 0xd2, 0xec, 0x2c, 0xe8, 0xe9, 0x66, 0xe1, 0x27, + 0xc2, 0x97, 0xba, 0xb2, 0x0a, 0xcc, 0x54, 0xb9, 0x9e, 0xb8, 0xd3, 0xc8, 0xc0, 0x78, 0x65, 0xee, + 0xb9, 0x34, 0xb7, 0x0e, 0xd7, 0xfb, 0xcc, 0x82, 0xed, 0xee, 0xf2, 0xad, 0x79, 0x98, 0xeb, 0x03, + 0xb3, 0x98, 0x4f, 0x6d, 0x47, 0xc0, 0x1b, 0x84, 0x2f, 0x74, 0xc4, 0x28, 0x2c, 0xa4, 0xfa, 0x8a, + 0x24, 0xb0, 0x76, 0x7b, 0x20, 0xec, 0xa0, 0x97, 0x5b, 0x0b, 0x0c, 0x7c, 0x42, 0x2a, 0xed, 0x23, + 0x19, 0x0d, 0xe9, 0xcf, 0x19, 0x17, 0xf6, 0xda, 0x72, 0x16, 0xca, 0xa0, 0x39, 0xa6, 0xae, 0xaf, + 0xde, 0xe4, 0x52, 0x9f, 0xad, 0xb1, 0x1f, 0xa7, 0x3a, 0x3a, 0x39, 0xd5, 0xd1, 0xdf, 0x53, 0x1d, + 0xbd, 0x6d, 0xe8, 0x43, 0x27, 0x0d, 0x7d, 0xe8, 0x4f, 0x43, 0x1f, 0xc2, 0x79, 0x9b, 0x27, 0x18, + 0xd8, 0x40, 0x5b, 0x66, 0xc5, 0xf6, 0xf7, 0x0e, 0x76, 0xcc, 0x32, 0xaf, 0x76, 0x28, 0x2d, 0xda, + 0xbc, 0x53, 0xf7, 0xb0, 0xa5, 0xbc, 0x73, 0x5e, 0xfe, 0x21, 0x5a, 0xf9, 0x1f, 0x00, 0x00, 0xff, + 0xff, 0xae, 0xe1, 0xd2, 0x33, 0x87, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -743,6 +822,8 @@ type QueryClient interface { QueryMarketInfo(ctx context.Context, in *QueryMarketInfoRequest, opts ...grpc.CallOption) (*QueryMarketInfoResponse, error) // QueryParams returns the exchange module parameters. QueryParams(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // QueryIsValidMarket checks the provided market and returns any errors that would be encountered trying to create it. + QueryIsValidMarket(ctx context.Context, in *QueryIsValidMarketRequest, opts ...grpc.CallOption) (*QueryIsValidMarketResponse, error) } type queryClient struct { @@ -825,6 +906,15 @@ func (c *queryClient) QueryParams(ctx context.Context, in *QueryParamsRequest, o return out, nil } +func (c *queryClient) QueryIsValidMarket(ctx context.Context, in *QueryIsValidMarketRequest, opts ...grpc.CallOption) (*QueryIsValidMarketResponse, error) { + out := new(QueryIsValidMarketResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryIsValidMarket", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // QueryOrderFeeCalc calculates the fees that will be associated with the provided order. @@ -843,6 +933,8 @@ type QueryServer interface { QueryMarketInfo(context.Context, *QueryMarketInfoRequest) (*QueryMarketInfoResponse, error) // QueryParams returns the exchange module parameters. QueryParams(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // QueryIsValidMarket checks the provided market and returns any errors that would be encountered trying to create it. + QueryIsValidMarket(context.Context, *QueryIsValidMarketRequest) (*QueryIsValidMarketResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -873,6 +965,9 @@ func (*UnimplementedQueryServer) QueryMarketInfo(ctx context.Context, req *Query func (*UnimplementedQueryServer) QueryParams(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryParams not implemented") } +func (*UnimplementedQueryServer) QueryIsValidMarket(ctx context.Context, req *QueryIsValidMarketRequest) (*QueryIsValidMarketResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryIsValidMarket not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -1022,6 +1117,24 @@ func _Query_QueryParams_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } +func _Query_QueryIsValidMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryIsValidMarketRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryIsValidMarket(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryIsValidMarket", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryIsValidMarket(ctx, req.(*QueryIsValidMarketRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "provenance.exchange.v1.Query", HandlerType: (*QueryServer)(nil), @@ -1058,6 +1171,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "QueryParams", Handler: _Query_QueryParams_Handler, }, + { + MethodName: "QueryIsValidMarket", + Handler: _Query_QueryIsValidMarket_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "provenance/exchange/v1/query.proto", @@ -1448,6 +1565,52 @@ func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *QueryIsValidMarketRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryIsValidMarketRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryIsValidMarketRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryIsValidMarketResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryIsValidMarketResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryIsValidMarketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -1613,6 +1776,24 @@ func (m *QueryParamsResponse) Size() (n int) { return n } +func (m *QueryIsValidMarketRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryIsValidMarketResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2489,6 +2670,106 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryIsValidMarketRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryIsValidMarketRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryIsValidMarketRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryIsValidMarketResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryIsValidMarketResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryIsValidMarketResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/exchange/query.pb.gw.go b/x/exchange/query.pb.gw.go index 0903e975d0..0b90c33f0a 100644 --- a/x/exchange/query.pb.gw.go +++ b/x/exchange/query.pb.gw.go @@ -301,6 +301,24 @@ func local_request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Ma } +func request_Query_QueryIsValidMarket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryIsValidMarketRequest + var metadata runtime.ServerMetadata + + msg, err := client.QueryIsValidMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryIsValidMarket_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryIsValidMarketRequest + var metadata runtime.ServerMetadata + + msg, err := server.QueryIsValidMarket(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -487,6 +505,26 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_QueryIsValidMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryIsValidMarket_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryIsValidMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -708,6 +746,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_QueryIsValidMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryIsValidMarket_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryIsValidMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -729,6 +787,8 @@ var ( pattern_Query_QueryMarketInfo_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "market", "details"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryIsValidMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "market", "validate"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -749,4 +809,6 @@ var ( forward_Query_QueryMarketInfo_1 = runtime.ForwardResponseMessage forward_Query_QueryParams_0 = runtime.ForwardResponseMessage + + forward_Query_QueryIsValidMarket_0 = runtime.ForwardResponseMessage ) diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 7a500333e2..bf68f99da5 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -6,6 +6,11 @@ package exchange import ( context "context" fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" grpc "google.golang.org/grpc" @@ -915,89 +920,21 @@ func (m *MsgMarketManageReqAttrsResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgMarketManageReqAttrsResponse proto.InternalMessageInfo -// MsgCreateMarketRequest is a request message for the CreateMarket endpoint. -type MsgCreateMarketRequest struct { -} - -func (m *MsgCreateMarketRequest) Reset() { *m = MsgCreateMarketRequest{} } -func (m *MsgCreateMarketRequest) String() string { return proto.CompactTextString(m) } -func (*MsgCreateMarketRequest) ProtoMessage() {} -func (*MsgCreateMarketRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{24} -} -func (m *MsgCreateMarketRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *MsgCreateMarketRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_MsgCreateMarketRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *MsgCreateMarketRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgCreateMarketRequest.Merge(m, src) -} -func (m *MsgCreateMarketRequest) XXX_Size() int { - return m.Size() -} -func (m *MsgCreateMarketRequest) XXX_DiscardUnknown() { - xxx_messageInfo_MsgCreateMarketRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_MsgCreateMarketRequest proto.InternalMessageInfo - -// MsgCreateMarketResponse is a response message for the CreateMarket endpoint. -type MsgCreateMarketResponse struct { -} - -func (m *MsgCreateMarketResponse) Reset() { *m = MsgCreateMarketResponse{} } -func (m *MsgCreateMarketResponse) String() string { return proto.CompactTextString(m) } -func (*MsgCreateMarketResponse) ProtoMessage() {} -func (*MsgCreateMarketResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{25} -} -func (m *MsgCreateMarketResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *MsgCreateMarketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_MsgCreateMarketResponse.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *MsgCreateMarketResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgCreateMarketResponse.Merge(m, src) -} -func (m *MsgCreateMarketResponse) XXX_Size() int { - return m.Size() -} -func (m *MsgCreateMarketResponse) XXX_DiscardUnknown() { - xxx_messageInfo_MsgCreateMarketResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_MsgCreateMarketResponse proto.InternalMessageInfo - // MsgGovCreateMarketRequest is a request message for the GovCreateMarket endpoint. type MsgGovCreateMarketRequest struct { + // authority should be the governance module account address. + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // market is the initial market configuration. + // If the market_id is 0, the next available market_id will be used (once voting ends). + // If it is not zero, it must not yet be in use when the voting period ends. + Market Market `protobuf:"bytes,2,opt,name=market,proto3" json:"market"` } func (m *MsgGovCreateMarketRequest) Reset() { *m = MsgGovCreateMarketRequest{} } func (m *MsgGovCreateMarketRequest) String() string { return proto.CompactTextString(m) } func (*MsgGovCreateMarketRequest) ProtoMessage() {} func (*MsgGovCreateMarketRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{26} + return fileDescriptor_e333fcffc093bd1b, []int{24} } func (m *MsgGovCreateMarketRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1026,6 +963,20 @@ func (m *MsgGovCreateMarketRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgGovCreateMarketRequest proto.InternalMessageInfo +func (m *MsgGovCreateMarketRequest) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgGovCreateMarketRequest) GetMarket() Market { + if m != nil { + return m.Market + } + return Market{} +} + // MsgGovCreateMarketResponse is a response message for the GovCreateMarket endpoint. type MsgGovCreateMarketResponse struct { } @@ -1034,7 +985,7 @@ func (m *MsgGovCreateMarketResponse) Reset() { *m = MsgGovCreateMarketRe func (m *MsgGovCreateMarketResponse) String() string { return proto.CompactTextString(m) } func (*MsgGovCreateMarketResponse) ProtoMessage() {} func (*MsgGovCreateMarketResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{27} + return fileDescriptor_e333fcffc093bd1b, []int{25} } func (m *MsgGovCreateMarketResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1065,13 +1016,39 @@ var xxx_messageInfo_MsgGovCreateMarketResponse proto.InternalMessageInfo // MsgGovManageFeesRequest is a request message for the GovManageFees endpoint. type MsgGovManageFeesRequest struct { + // authority should be the governance module account address. + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // add_fee_create_ask_flat are the create ask flat fee options to add. + AddFeeCreateAskFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=add_fee_create_ask_flat,json=addFeeCreateAskFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"add_fee_create_ask_flat"` + // remove_fee_create_ask_flat are the create ask flat fee options to remove. + RemoveFeeCreateAskFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=remove_fee_create_ask_flat,json=removeFeeCreateAskFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"remove_fee_create_ask_flat"` + // add_fee_create_bid_flat are the create bid flat fee options to add. + AddFeeCreateBidFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=add_fee_create_bid_flat,json=addFeeCreateBidFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"add_fee_create_bid_flat"` + // remove_fee_create_bid_flat are the create bid flat fee options to remove. + RemoveFeeCreateBidFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,5,rep,name=remove_fee_create_bid_flat,json=removeFeeCreateBidFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"remove_fee_create_bid_flat"` + // add_fee_settlement_seller_flat are the seller settlement flat fee options to add. + AddFeeSettlementSellerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,6,rep,name=add_fee_settlement_seller_flat,json=addFeeSettlementSellerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"add_fee_settlement_seller_flat"` + // remove_fee_settlement_seller_flat are the seller settlement flat fee options to remove. + RemoveFeeSettlementSellerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,7,rep,name=remove_fee_settlement_seller_flat,json=removeFeeSettlementSellerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"remove_fee_settlement_seller_flat"` + // add_fee_settlement_seller_ratios are the seller settlement fee ratios to add. + AddFeeSettlementSellerRatios []FeeRatio `protobuf:"bytes,8,rep,name=add_fee_settlement_seller_ratios,json=addFeeSettlementSellerRatios,proto3" json:"add_fee_settlement_seller_ratios"` + // remove_fee_settlement_seller_ratios are the seller settlement fee ratios to remove. + RemoveFeeSettlementSellerRatios []FeeRatio `protobuf:"bytes,9,rep,name=remove_fee_settlement_seller_ratios,json=removeFeeSettlementSellerRatios,proto3" json:"remove_fee_settlement_seller_ratios"` + // add_fee_settlement_buyer_flat are the buyer settlement flat fee options to add. + AddFeeSettlementBuyerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,10,rep,name=add_fee_settlement_buyer_flat,json=addFeeSettlementBuyerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"add_fee_settlement_buyer_flat"` + // remove_fee_settlement_buyer_flat are the buyer settlement flat fee options to remove. + RemoveFeeSettlementBuyerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,11,rep,name=remove_fee_settlement_buyer_flat,json=removeFeeSettlementBuyerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"remove_fee_settlement_buyer_flat"` + // add_fee_settlement_buyer_ratios are the buyer settlement fee ratios to add. + AddFeeSettlementBuyerRatios []FeeRatio `protobuf:"bytes,12,rep,name=add_fee_settlement_buyer_ratios,json=addFeeSettlementBuyerRatios,proto3" json:"add_fee_settlement_buyer_ratios"` + // remove_fee_settlement_buyer_ratios are the buyer settlement fee ratios to remove. + RemoveFeeSettlementBuyerRatios []FeeRatio `protobuf:"bytes,13,rep,name=remove_fee_settlement_buyer_ratios,json=removeFeeSettlementBuyerRatios,proto3" json:"remove_fee_settlement_buyer_ratios"` } func (m *MsgGovManageFeesRequest) Reset() { *m = MsgGovManageFeesRequest{} } func (m *MsgGovManageFeesRequest) String() string { return proto.CompactTextString(m) } func (*MsgGovManageFeesRequest) ProtoMessage() {} func (*MsgGovManageFeesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{28} + return fileDescriptor_e333fcffc093bd1b, []int{26} } func (m *MsgGovManageFeesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1100,6 +1077,97 @@ func (m *MsgGovManageFeesRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgGovManageFeesRequest proto.InternalMessageInfo +func (m *MsgGovManageFeesRequest) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgGovManageFeesRequest) GetAddFeeCreateAskFlat() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.AddFeeCreateAskFlat + } + return nil +} + +func (m *MsgGovManageFeesRequest) GetRemoveFeeCreateAskFlat() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.RemoveFeeCreateAskFlat + } + return nil +} + +func (m *MsgGovManageFeesRequest) GetAddFeeCreateBidFlat() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.AddFeeCreateBidFlat + } + return nil +} + +func (m *MsgGovManageFeesRequest) GetRemoveFeeCreateBidFlat() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.RemoveFeeCreateBidFlat + } + return nil +} + +func (m *MsgGovManageFeesRequest) GetAddFeeSettlementSellerFlat() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.AddFeeSettlementSellerFlat + } + return nil +} + +func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementSellerFlat() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.RemoveFeeSettlementSellerFlat + } + return nil +} + +func (m *MsgGovManageFeesRequest) GetAddFeeSettlementSellerRatios() []FeeRatio { + if m != nil { + return m.AddFeeSettlementSellerRatios + } + return nil +} + +func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementSellerRatios() []FeeRatio { + if m != nil { + return m.RemoveFeeSettlementSellerRatios + } + return nil +} + +func (m *MsgGovManageFeesRequest) GetAddFeeSettlementBuyerFlat() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.AddFeeSettlementBuyerFlat + } + return nil +} + +func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementBuyerFlat() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.RemoveFeeSettlementBuyerFlat + } + return nil +} + +func (m *MsgGovManageFeesRequest) GetAddFeeSettlementBuyerRatios() []FeeRatio { + if m != nil { + return m.AddFeeSettlementBuyerRatios + } + return nil +} + +func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementBuyerRatios() []FeeRatio { + if m != nil { + return m.RemoveFeeSettlementBuyerRatios + } + return nil +} + // MsgGovManageFeesResponse is a response message for the GovManageFees endpoint. type MsgGovManageFeesResponse struct { } @@ -1108,7 +1176,7 @@ func (m *MsgGovManageFeesResponse) Reset() { *m = MsgGovManageFeesRespon func (m *MsgGovManageFeesResponse) String() string { return proto.CompactTextString(m) } func (*MsgGovManageFeesResponse) ProtoMessage() {} func (*MsgGovManageFeesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{29} + return fileDescriptor_e333fcffc093bd1b, []int{27} } func (m *MsgGovManageFeesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1139,13 +1207,17 @@ var xxx_messageInfo_MsgGovManageFeesResponse proto.InternalMessageInfo // MsgGovUpdateParamsRequest is a request message for the GovUpdateParams endpoint. type MsgGovUpdateParamsRequest struct { + // authority should be the governance module account address. + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // params are the new param values to set + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` } func (m *MsgGovUpdateParamsRequest) Reset() { *m = MsgGovUpdateParamsRequest{} } func (m *MsgGovUpdateParamsRequest) String() string { return proto.CompactTextString(m) } func (*MsgGovUpdateParamsRequest) ProtoMessage() {} func (*MsgGovUpdateParamsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{30} + return fileDescriptor_e333fcffc093bd1b, []int{28} } func (m *MsgGovUpdateParamsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1174,6 +1246,20 @@ func (m *MsgGovUpdateParamsRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgGovUpdateParamsRequest proto.InternalMessageInfo +func (m *MsgGovUpdateParamsRequest) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgGovUpdateParamsRequest) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + // MsgGovUpdateParamsResponse is a response message for the GovUpdateParams endpoint. type MsgGovUpdateParamsResponse struct { } @@ -1182,7 +1268,7 @@ func (m *MsgGovUpdateParamsResponse) Reset() { *m = MsgGovUpdateParamsRe func (m *MsgGovUpdateParamsResponse) String() string { return proto.CompactTextString(m) } func (*MsgGovUpdateParamsResponse) ProtoMessage() {} func (*MsgGovUpdateParamsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{31} + return fileDescriptor_e333fcffc093bd1b, []int{29} } func (m *MsgGovUpdateParamsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1236,8 +1322,6 @@ func init() { proto.RegisterType((*MsgMarketManagePermissionsResponse)(nil), "provenance.exchange.v1.MsgMarketManagePermissionsResponse") proto.RegisterType((*MsgMarketManageReqAttrsRequest)(nil), "provenance.exchange.v1.MsgMarketManageReqAttrsRequest") proto.RegisterType((*MsgMarketManageReqAttrsResponse)(nil), "provenance.exchange.v1.MsgMarketManageReqAttrsResponse") - proto.RegisterType((*MsgCreateMarketRequest)(nil), "provenance.exchange.v1.MsgCreateMarketRequest") - proto.RegisterType((*MsgCreateMarketResponse)(nil), "provenance.exchange.v1.MsgCreateMarketResponse") proto.RegisterType((*MsgGovCreateMarketRequest)(nil), "provenance.exchange.v1.MsgGovCreateMarketRequest") proto.RegisterType((*MsgGovCreateMarketResponse)(nil), "provenance.exchange.v1.MsgGovCreateMarketResponse") proto.RegisterType((*MsgGovManageFeesRequest)(nil), "provenance.exchange.v1.MsgGovManageFeesRequest") @@ -1249,51 +1333,79 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 697 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0x5b, 0x4f, 0xd4, 0x40, - 0x14, 0xa6, 0x31, 0x31, 0x7a, 0xbc, 0x90, 0x0c, 0xb0, 0xc0, 0xa8, 0x05, 0x56, 0x9f, 0x44, 0x5a, - 0xc0, 0x88, 0x97, 0x37, 0x56, 0x85, 0xa7, 0x8d, 0x04, 0x43, 0x4c, 0x7c, 0x1b, 0xb6, 0xe3, 0xd2, - 0x50, 0xda, 0x65, 0x66, 0x58, 0xf7, 0x07, 0x98, 0x18, 0x1f, 0x4c, 0xf8, 0x59, 0x3e, 0xf2, 0xe8, - 0xa3, 0x81, 0x3f, 0x62, 0x96, 0x99, 0x5e, 0xa6, 0x9d, 0x5e, 0x96, 0xc7, 0x9d, 0xf3, 0x5d, 0xce, - 0x6c, 0x7b, 0xbe, 0x53, 0x58, 0x1a, 0xb0, 0x68, 0x48, 0x43, 0x12, 0xf6, 0xa8, 0x4b, 0x47, 0xbd, - 0x23, 0x12, 0xf6, 0xa9, 0x3b, 0xdc, 0x70, 0xc5, 0xc8, 0x19, 0xb0, 0x48, 0x44, 0xa8, 0x95, 0x02, - 0x9c, 0x18, 0xe0, 0x0c, 0x37, 0xda, 0x73, 0x30, 0xd3, 0xe5, 0xfd, 0xf7, 0x8c, 0x12, 0x41, 0xb7, - 0xf9, 0xf1, 0x3e, 0x3d, 0x3d, 0xa3, 0x5c, 0xb4, 0x5b, 0x30, 0xab, 0x1f, 0xf3, 0x41, 0x14, 0x72, - 0xaa, 0xc1, 0x3b, 0xbe, 0x67, 0x82, 0x5f, 0x1f, 0x2b, 0xf8, 0x3c, 0xcc, 0x8d, 0xcf, 0xc7, 0xae, - 0xc1, 0x27, 0xe6, 0x51, 0x16, 0x13, 0x16, 0xa0, 0x95, 0x2f, 0x28, 0xca, 0x2c, 0xa0, 0x2e, 0xef, - 0xef, 0xf8, 0x41, 0xd0, 0xf1, 0x3d, 0x1e, 0xe3, 0xa5, 0x6f, 0x7a, 0x5a, 0x00, 0x6f, 0xf3, 0x63, - 0x03, 0x58, 0x9e, 0x2a, 0xb0, 0xf4, 0xec, 0x12, 0x76, 0x4c, 0xc5, 0x67, 0x2a, 0x44, 0x40, 0x63, - 0xc2, 0x22, 0xcc, 0x17, 0x2a, 0x8a, 0x84, 0x61, 0x21, 0x29, 0x7d, 0xf1, 0xc5, 0x91, 0xc7, 0xc8, - 0xf7, 0x98, 0xf6, 0x08, 0x16, 0x0d, 0x35, 0x45, 0x5c, 0x82, 0x27, 0x49, 0xf1, 0x60, 0xe0, 0x11, - 0x41, 0x3f, 0x50, 0x41, 0xfc, 0x20, 0xe9, 0x72, 0x19, 0xec, 0x32, 0x40, 0xa9, 0xc4, 0xc7, 0x90, - 0x1c, 0x06, 0xd4, 0x2b, 0x97, 0x48, 0x00, 0x4a, 0xa2, 0x0d, 0xcb, 0x39, 0xc4, 0x01, 0xa7, 0x4c, - 0xbf, 0xfd, 0x53, 0x58, 0xa9, 0xc0, 0x28, 0xa1, 0x2c, 0xa8, 0x4b, 0x42, 0xd2, 0xa7, 0x7b, 0x94, - 0x9d, 0xf8, 0x9c, 0xfb, 0x51, 0x98, 0x5c, 0xe9, 0x19, 0xb4, 0xab, 0x40, 0x4a, 0x2a, 0xdb, 0xb5, - 0x44, 0xed, 0xd3, 0xd3, 0x6d, 0x21, 0x58, 0xa2, 0xb3, 0x02, 0x4b, 0xa5, 0x08, 0xed, 0x61, 0xca, - 0x37, 0x4e, 0x02, 0xf5, 0x87, 0xa9, 0x57, 0x14, 0x49, 0x3e, 0xb0, 0xdd, 0x68, 0x68, 0xe2, 0x3d, - 0x06, 0x6c, 0x2a, 0x2a, 0xaa, 0x54, 0xdd, 0x8d, 0x86, 0xb2, 0x9f, 0x1d, 0x4a, 0x93, 0x6e, 0xe5, - 0x2b, 0x92, 0x2b, 0xe5, 0x1d, 0xe5, 0x1f, 0xbb, 0x47, 0x18, 0x39, 0xe1, 0x05, 0x47, 0xbd, 0x28, - 0xa9, 0x9b, 0xbf, 0xa6, 0xe1, 0x56, 0x97, 0xf7, 0xd1, 0x37, 0xb8, 0x9b, 0xcc, 0x21, 0x5a, 0x75, - 0xcc, 0x73, 0xec, 0x18, 0x86, 0x18, 0xbf, 0x68, 0x06, 0x96, 0x7e, 0xa9, 0x4f, 0xc7, 0xf7, 0x1a, - 0xf8, 0xa4, 0xd3, 0xdf, 0xc0, 0x27, 0x93, 0x09, 0x28, 0x80, 0x7b, 0x99, 0xb9, 0x47, 0x6b, 0x55, - 0xe4, 0x42, 0x70, 0x60, 0xa7, 0x29, 0x5c, 0xb9, 0xf5, 0xe0, 0x4e, 0x9c, 0x1a, 0xe8, 0x79, 0x05, - 0x37, 0x17, 0x38, 0x78, 0xb5, 0x11, 0x56, 0x37, 0x19, 0xa7, 0x4d, 0xad, 0x49, 0x26, 0xa8, 0x6a, - 0x4d, 0xb2, 0xf1, 0x85, 0x22, 0xb8, 0x9f, 0x4d, 0x28, 0x54, 0xf5, 0x4f, 0x18, 0x42, 0x0e, 0xbb, - 0x8d, 0xf1, 0xca, 0xf0, 0x0c, 0x1e, 0xea, 0xd9, 0x86, 0xd6, 0x6b, 0x25, 0x72, 0x11, 0x89, 0x37, - 0x26, 0x60, 0x28, 0xdb, 0x1f, 0x16, 0xcc, 0x18, 0x52, 0x11, 0xbd, 0xaa, 0x95, 0x32, 0xc5, 0x2c, - 0xde, 0x9a, 0x94, 0x56, 0xd2, 0x86, 0x4a, 0xd6, 0xc6, 0x6d, 0xe8, 0x51, 0xdd, 0xb8, 0x8d, 0x5c, - 0x80, 0xa3, 0xdf, 0x16, 0xb4, 0xcc, 0xd1, 0x8c, 0xde, 0x34, 0x94, 0x2c, 0x24, 0x3e, 0x7e, 0x7b, - 0x03, 0xa6, 0xea, 0xe7, 0xdc, 0x82, 0xf9, 0x92, 0x80, 0x47, 0xf5, 0xb2, 0x65, 0x9b, 0x03, 0xbf, - 0xbb, 0x09, 0x55, 0xb5, 0xf4, 0xd3, 0x82, 0x59, 0xd3, 0xae, 0x40, 0x5b, 0x0d, 0x45, 0x73, 0xeb, - 0x07, 0xbf, 0x9e, 0x98, 0x97, 0x8e, 0x68, 0x76, 0x79, 0x54, 0x8e, 0xa8, 0x61, 0x05, 0x55, 0x8e, - 0xa8, 0x69, 0x2b, 0xa1, 0x11, 0x4c, 0xe7, 0x16, 0x16, 0xaa, 0x9a, 0x38, 0xf3, 0xe6, 0xc3, 0x9b, - 0x93, 0x50, 0x94, 0x33, 0x83, 0x07, 0xda, 0xc6, 0x43, 0x6e, 0xb5, 0x48, 0x61, 0x6d, 0xe2, 0xf5, - 0xe6, 0x04, 0xed, 0xb6, 0xd9, 0x65, 0x59, 0x77, 0x5b, 0xc3, 0xd6, 0xad, 0xbb, 0xad, 0x69, 0x17, - 0x77, 0xe8, 0x9f, 0x4b, 0xdb, 0xba, 0xb8, 0xb4, 0xad, 0x7f, 0x97, 0xb6, 0x75, 0x7e, 0x65, 0x4f, - 0x5d, 0x5c, 0xd9, 0x53, 0x7f, 0xaf, 0xec, 0x29, 0x58, 0xf4, 0xa3, 0x12, 0xbd, 0x3d, 0xeb, 0xab, - 0xd3, 0xf7, 0xc5, 0xd1, 0xd9, 0xa1, 0xd3, 0x8b, 0x4e, 0xdc, 0x14, 0xb4, 0xe6, 0x47, 0x99, 0x5f, - 0xee, 0x28, 0xf9, 0x60, 0x3f, 0xbc, 0x7d, 0xfd, 0xad, 0xfe, 0xf2, 0x7f, 0x00, 0x00, 0x00, 0xff, - 0xff, 0xa1, 0xfc, 0xc3, 0xf5, 0xce, 0x0b, 0x00, 0x00, + // 1151 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x41, 0x6f, 0x1b, 0x45, + 0x14, 0xce, 0x92, 0x12, 0x9a, 0x97, 0x26, 0x48, 0x93, 0x34, 0xb1, 0xb7, 0xc9, 0xda, 0x71, 0x38, + 0x44, 0x2d, 0x59, 0x27, 0x41, 0x04, 0xa8, 0xb8, 0xc4, 0x85, 0xf4, 0x14, 0x11, 0x39, 0xaa, 0x90, + 0xb8, 0x58, 0x63, 0xef, 0x64, 0xb3, 0xca, 0x7a, 0xc7, 0xdd, 0x19, 0x9b, 0xe4, 0x08, 0x42, 0xea, + 0x09, 0x51, 0x09, 0x55, 0xe2, 0xca, 0x95, 0x13, 0x07, 0x7e, 0x44, 0x8f, 0x15, 0x27, 0x4e, 0x80, + 0x92, 0x03, 0x7f, 0x03, 0xed, 0xce, 0xec, 0x7a, 0x6d, 0xcf, 0xae, 0xd7, 0xad, 0x7b, 0x6a, 0x3d, + 0xf3, 0xbd, 0xf7, 0x7d, 0xef, 0x73, 0x76, 0xdf, 0x7b, 0x86, 0x52, 0xc7, 0xa7, 0x3d, 0xe2, 0x61, + 0xaf, 0x45, 0xaa, 0xe4, 0xb2, 0x75, 0x8e, 0x3d, 0x9b, 0x54, 0x7b, 0x7b, 0x55, 0x7e, 0x69, 0x76, + 0x7c, 0xca, 0x29, 0x5a, 0xed, 0x03, 0xcc, 0x08, 0x60, 0xf6, 0xf6, 0x74, 0xa3, 0x45, 0x59, 0x9b, + 0xb2, 0x6a, 0x13, 0xb3, 0x20, 0xa0, 0x49, 0x38, 0xde, 0xab, 0xb6, 0xa8, 0xe3, 0x89, 0x38, 0x7d, + 0x4d, 0xde, 0xb7, 0x99, 0x1d, 0xe4, 0x6b, 0x33, 0x5b, 0x5e, 0x14, 0xc5, 0x45, 0x23, 0xfc, 0x54, + 0x15, 0x1f, 0xe4, 0xd5, 0x8a, 0x4d, 0x6d, 0x2a, 0xce, 0x83, 0xff, 0xc9, 0xd3, 0xad, 0x14, 0x89, + 0x6d, 0xec, 0x5f, 0x10, 0x3e, 0x06, 0xd4, 0xc1, 0x3e, 0x6e, 0xcb, 0xfc, 0x95, 0xbb, 0xb0, 0x7c, + 0xcc, 0xec, 0x47, 0x3e, 0xc1, 0x9c, 0x1c, 0xb2, 0x8b, 0x3a, 0x79, 0xda, 0x25, 0x8c, 0x57, 0x56, + 0x61, 0x65, 0xf0, 0x98, 0x75, 0xa8, 0xc7, 0xc8, 0x00, 0xbc, 0xe6, 0x58, 0x2a, 0x78, 0x78, 0x2c, + 0xe1, 0x6b, 0x70, 0x37, 0x38, 0x0f, 0x24, 0xb8, 0x5f, 0xf9, 0x16, 0xf1, 0xa3, 0x80, 0x02, 0xac, + 0x0e, 0x5f, 0xc8, 0x90, 0x15, 0x40, 0xc7, 0xcc, 0x3e, 0x72, 0x5c, 0xb7, 0xe6, 0x58, 0x2c, 0xc2, + 0x0b, 0xde, 0xfe, 0xe9, 0x08, 0xf8, 0x90, 0x5d, 0x28, 0xc0, 0xe2, 0x54, 0x82, 0x05, 0xe7, 0x71, + 0x68, 0xd1, 0x29, 0xe1, 0xdc, 0x25, 0x51, 0x40, 0x11, 0xd6, 0x46, 0x6e, 0x64, 0x90, 0x0e, 0x85, + 0xf8, 0xea, 0x6b, 0x87, 0x9f, 0x5b, 0x3e, 0xfe, 0x36, 0x0a, 0xbb, 0x07, 0x45, 0xc5, 0x9d, 0x0c, + 0x2c, 0xc1, 0x46, 0x7c, 0xf9, 0xa4, 0x63, 0x61, 0x4e, 0xbe, 0x20, 0x1c, 0x3b, 0x6e, 0xac, 0xb2, + 0x0c, 0x46, 0x1a, 0x20, 0x35, 0xc5, 0x97, 0x1e, 0x6e, 0xba, 0xc4, 0x4a, 0x4f, 0x11, 0x03, 0x64, + 0x8a, 0x0a, 0x94, 0x87, 0x10, 0x4f, 0x18, 0xf1, 0x07, 0xab, 0xdf, 0x82, 0xcd, 0x0c, 0x8c, 0x4c, + 0x94, 0x04, 0x1d, 0x63, 0x0f, 0xdb, 0xe4, 0x84, 0xf8, 0x6d, 0x87, 0x31, 0x87, 0x7a, 0x71, 0x49, + 0x1f, 0x40, 0x25, 0x0b, 0x24, 0x53, 0x25, 0x55, 0x0b, 0x54, 0x9d, 0x3c, 0x3d, 0xe4, 0xdc, 0x8f, + 0xf3, 0x6c, 0x42, 0x29, 0x15, 0x21, 0x93, 0xfc, 0xaa, 0x85, 0xe6, 0x3f, 0xa6, 0x3d, 0xf1, 0x57, + 0x27, 0xc0, 0x32, 0x01, 0x3a, 0x80, 0x79, 0xdc, 0xe5, 0xe7, 0xd4, 0x77, 0xf8, 0x55, 0x41, 0x2b, + 0x6b, 0xdb, 0xf3, 0xb5, 0xc2, 0x9f, 0x7f, 0xec, 0xac, 0xc8, 0x47, 0xeb, 0xd0, 0xb2, 0x7c, 0xc2, + 0xd8, 0x29, 0xf7, 0x1d, 0xcf, 0xae, 0xf7, 0xa1, 0xe8, 0x73, 0x98, 0x13, 0x8f, 0x50, 0xe1, 0x9d, + 0xb2, 0xb6, 0xbd, 0xb0, 0x6f, 0x98, 0xea, 0x47, 0xdd, 0x14, 0x74, 0xb5, 0x5b, 0x2f, 0xff, 0x2e, + 0xcd, 0xd4, 0x65, 0xcc, 0xc3, 0xa5, 0xef, 0xff, 0xfb, 0xfd, 0x7e, 0x3f, 0x5b, 0x65, 0x1d, 0x74, + 0x95, 0x44, 0x59, 0xc1, 0x8b, 0xc5, 0xf0, 0xaf, 0xee, 0x31, 0xed, 0x89, 0x12, 0x8f, 0x08, 0x61, + 0x6f, 0xaa, 0xff, 0x3b, 0x0d, 0xd6, 0xb0, 0x65, 0x35, 0xce, 0x08, 0x69, 0xb4, 0x42, 0xd2, 0x06, + 0x66, 0x17, 0x8d, 0x33, 0x17, 0x07, 0x15, 0xcd, 0x6e, 0x2f, 0xec, 0x17, 0x4d, 0x99, 0x23, 0x78, + 0x49, 0x99, 0xf2, 0x25, 0x65, 0x3e, 0xa2, 0x8e, 0x57, 0xdb, 0x0d, 0x8a, 0xf9, 0xed, 0x9f, 0xd2, + 0xb6, 0xed, 0xf0, 0xf3, 0x6e, 0xd3, 0x6c, 0xd1, 0xb6, 0x7c, 0x17, 0xc9, 0x7f, 0x76, 0x98, 0x75, + 0x51, 0xe5, 0x57, 0x1d, 0xc2, 0xc2, 0x00, 0x56, 0x5f, 0xc6, 0x96, 0x75, 0x44, 0x48, 0xfc, 0x9a, + 0x38, 0x72, 0x31, 0x47, 0xcf, 0x34, 0xd0, 0x7d, 0xd2, 0xa6, 0x3d, 0xa2, 0x94, 0x31, 0x3b, 0x7d, + 0x19, 0xab, 0x82, 0x6e, 0x44, 0x89, 0xc2, 0x8d, 0xa6, 0x63, 0x09, 0x19, 0xb7, 0xde, 0xb2, 0x1b, + 0x35, 0xc7, 0xca, 0x70, 0x23, 0x96, 0xf1, 0xee, 0xdb, 0x77, 0x23, 0x52, 0xf2, 0x93, 0x06, 0x46, + 0xe4, 0x06, 0x0b, 0x1f, 0xee, 0x36, 0xf1, 0x78, 0x83, 0x11, 0xd7, 0x25, 0xbe, 0x50, 0x33, 0x37, + 0x7d, 0x35, 0xba, 0x30, 0xe5, 0x34, 0x26, 0x3c, 0x0d, 0xf9, 0x42, 0x45, 0x2f, 0x34, 0xd8, 0x4c, + 0x78, 0x93, 0x22, 0xea, 0xbd, 0xe9, 0x8b, 0xda, 0x88, 0x2d, 0x52, 0xea, 0xf2, 0xa0, 0x9c, 0x6e, + 0x94, 0x8f, 0xb9, 0x43, 0x59, 0xe1, 0x76, 0xa8, 0xaa, 0x9c, 0xf6, 0x7e, 0x38, 0x22, 0xa4, 0x1e, + 0x00, 0xe5, 0x1b, 0x62, 0x5d, 0xed, 0x42, 0x08, 0x61, 0x88, 0xc3, 0x56, 0xa6, 0x0d, 0x92, 0x72, + 0x7e, 0x22, 0xca, 0x52, 0x6a, 0x8d, 0x92, 0xf5, 0x47, 0x0d, 0x36, 0x14, 0x65, 0x36, 0xbb, 0x57, + 0x91, 0xf3, 0x30, 0x7d, 0xe7, 0x8b, 0xc3, 0x46, 0xd4, 0x02, 0xba, 0xd0, 0xf5, 0x9f, 0x35, 0x28, + 0xab, 0x6d, 0x48, 0x48, 0x5a, 0x98, 0xbe, 0xa4, 0x75, 0x85, 0x51, 0x7d, 0x55, 0x2e, 0x94, 0x52, + 0x4d, 0x92, 0xdf, 0xcb, 0x9d, 0x89, 0xbe, 0x97, 0x7b, 0x4a, 0x07, 0xe4, 0x77, 0xe2, 0x43, 0x25, + 0xcb, 0x02, 0x49, 0xb8, 0x38, 0x11, 0xa1, 0x91, 0x56, 0x9f, 0xe0, 0x1c, 0xe9, 0x5a, 0x62, 0xe2, + 0x19, 0x6a, 0x4b, 0x23, 0x5d, 0x57, 0x0c, 0x0a, 0x27, 0xe1, 0x28, 0x39, 0x85, 0xae, 0x2b, 0x66, + 0xd2, 0x71, 0x5d, 0x57, 0xd0, 0x45, 0x5d, 0x57, 0xc4, 0xa4, 0x77, 0xdd, 0x41, 0x89, 0xa2, 0x82, + 0xfd, 0x5f, 0x96, 0x60, 0xf6, 0x98, 0xd9, 0xe8, 0x0c, 0xe6, 0xe3, 0x66, 0x81, 0x1e, 0xa4, 0xb6, + 0xf9, 0xd1, 0xd1, 0x58, 0xff, 0x30, 0x1f, 0x58, 0xf0, 0xf5, 0x79, 0x6a, 0x8e, 0x95, 0x83, 0xa7, + 0x3f, 0x53, 0xe7, 0xe0, 0x49, 0x4c, 0xda, 0xc8, 0x85, 0x85, 0xc4, 0x34, 0x8d, 0x76, 0xb2, 0x82, + 0x47, 0xc6, 0x71, 0xdd, 0xcc, 0x0b, 0x97, 0x6c, 0x2d, 0xb8, 0x1d, 0xcd, 0xe2, 0xe8, 0x7e, 0x46, + 0xec, 0xd0, 0x18, 0xaf, 0x3f, 0xc8, 0x85, 0x1d, 0x24, 0x09, 0x66, 0xf8, 0xb1, 0x24, 0x89, 0xf1, + 0x7f, 0x2c, 0x49, 0x72, 0x29, 0x40, 0x14, 0xee, 0x24, 0xe7, 0x7e, 0x94, 0xe5, 0x84, 0x62, 0x75, + 0xd0, 0xab, 0xb9, 0xf1, 0x92, 0xb0, 0x0b, 0x4b, 0x83, 0x1b, 0x03, 0xda, 0x1d, 0x9b, 0x62, 0x68, + 0xf1, 0xd0, 0xf7, 0x26, 0x88, 0x90, 0xb4, 0x3f, 0x68, 0xb0, 0xac, 0xd8, 0x35, 0xd0, 0xc7, 0x63, + 0x53, 0xa9, 0x96, 0x17, 0xfd, 0x60, 0xd2, 0xb0, 0x14, 0x19, 0x72, 0x5f, 0xc9, 0x2d, 0x63, 0x70, + 0x01, 0xca, 0x2d, 0x63, 0x68, 0x2d, 0x0a, 0x7a, 0xdf, 0xaa, 0x7a, 0xe1, 0x41, 0x9f, 0xe6, 0x4c, + 0x39, 0xb2, 0x47, 0xe9, 0x9f, 0xbd, 0x46, 0xa4, 0xd4, 0xf3, 0x5c, 0x83, 0xb5, 0x94, 0xb5, 0x09, + 0x8d, 0x4f, 0x9b, 0xb6, 0x8f, 0xe9, 0x0f, 0x5f, 0x27, 0x54, 0x4a, 0x7a, 0xa6, 0xc1, 0x8a, 0x6a, + 0x03, 0x43, 0x07, 0x39, 0x93, 0x0e, 0x2d, 0x75, 0xfa, 0x27, 0x13, 0xc7, 0x49, 0x25, 0x97, 0xf0, + 0xfe, 0xd0, 0x0e, 0x85, 0xb2, 0x1e, 0x00, 0xf5, 0x4a, 0xa8, 0xef, 0x4f, 0x12, 0x22, 0x99, 0x7d, + 0x58, 0x1c, 0xe8, 0x83, 0xa8, 0x9a, 0x9d, 0x64, 0x64, 0x91, 0xd3, 0x77, 0xf3, 0x07, 0x0c, 0x54, + 0x9b, 0xec, 0x5d, 0xe3, 0xaa, 0x55, 0xb4, 0xe2, 0x71, 0xd5, 0xaa, 0x5a, 0x63, 0x8d, 0xbc, 0xbc, + 0x36, 0xb4, 0x57, 0xd7, 0x86, 0xf6, 0xef, 0xb5, 0xa1, 0x3d, 0xbf, 0x31, 0x66, 0x5e, 0xdd, 0x18, + 0x33, 0x7f, 0xdd, 0x18, 0x33, 0x50, 0x74, 0x68, 0x4a, 0xbe, 0x13, 0xed, 0x1b, 0x33, 0x31, 0x69, + 0xf5, 0x41, 0x3b, 0x0e, 0x4d, 0x7c, 0xaa, 0x5e, 0xc6, 0xbf, 0x40, 0x35, 0xe7, 0xc2, 0x1f, 0x9e, + 0x3e, 0xfa, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xec, 0x17, 0x46, 0x90, 0x67, 0x13, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1332,11 +1444,7 @@ type MsgClient interface { MarketManagePermissions(ctx context.Context, in *MsgMarketManagePermissionsRequest, opts ...grpc.CallOption) (*MsgMarketManagePermissionsResponse, error) // MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. MarketManageReqAttrs(ctx context.Context, in *MsgMarketManageReqAttrsRequest, opts ...grpc.CallOption) (*MsgMarketManageReqAttrsResponse, error) - // CreateMarket reserves the next market id and submits a GovCreateMarket governance proposal to create the market. - CreateMarket(ctx context.Context, in *MsgCreateMarketRequest, opts ...grpc.CallOption) (*MsgCreateMarketResponse, error) // GovCreateMarket is a governance proposal endpoint for creating a market. - // The CreateMarket endpoint should be used to submit one of these unless you don't need to know the market id until - // after the proposal passes. GovCreateMarket(ctx context.Context, in *MsgGovCreateMarketRequest, opts ...grpc.CallOption) (*MsgGovCreateMarketResponse, error) // GovManageFees is a governance proposal endpoint for updating a market's fees. GovManageFees(ctx context.Context, in *MsgGovManageFeesRequest, opts ...grpc.CallOption) (*MsgGovManageFeesResponse, error) @@ -1460,15 +1568,6 @@ func (c *msgClient) MarketManageReqAttrs(ctx context.Context, in *MsgMarketManag return out, nil } -func (c *msgClient) CreateMarket(ctx context.Context, in *MsgCreateMarketRequest, opts ...grpc.CallOption) (*MsgCreateMarketResponse, error) { - out := new(MsgCreateMarketResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/CreateMarket", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *msgClient) GovCreateMarket(ctx context.Context, in *MsgGovCreateMarketRequest, opts ...grpc.CallOption) (*MsgGovCreateMarketResponse, error) { out := new(MsgGovCreateMarketResponse) err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/GovCreateMarket", in, out, opts...) @@ -1522,11 +1621,7 @@ type MsgServer interface { MarketManagePermissions(context.Context, *MsgMarketManagePermissionsRequest) (*MsgMarketManagePermissionsResponse, error) // MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. MarketManageReqAttrs(context.Context, *MsgMarketManageReqAttrsRequest) (*MsgMarketManageReqAttrsResponse, error) - // CreateMarket reserves the next market id and submits a GovCreateMarket governance proposal to create the market. - CreateMarket(context.Context, *MsgCreateMarketRequest) (*MsgCreateMarketResponse, error) // GovCreateMarket is a governance proposal endpoint for creating a market. - // The CreateMarket endpoint should be used to submit one of these unless you don't need to know the market id until - // after the proposal passes. GovCreateMarket(context.Context, *MsgGovCreateMarketRequest) (*MsgGovCreateMarketResponse, error) // GovManageFees is a governance proposal endpoint for updating a market's fees. GovManageFees(context.Context, *MsgGovManageFeesRequest) (*MsgGovManageFeesResponse, error) @@ -1574,9 +1669,6 @@ func (*UnimplementedMsgServer) MarketManagePermissions(ctx context.Context, req func (*UnimplementedMsgServer) MarketManageReqAttrs(ctx context.Context, req *MsgMarketManageReqAttrsRequest) (*MsgMarketManageReqAttrsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method MarketManageReqAttrs not implemented") } -func (*UnimplementedMsgServer) CreateMarket(ctx context.Context, req *MsgCreateMarketRequest) (*MsgCreateMarketResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateMarket not implemented") -} func (*UnimplementedMsgServer) GovCreateMarket(ctx context.Context, req *MsgGovCreateMarketRequest) (*MsgGovCreateMarketResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GovCreateMarket not implemented") } @@ -1807,24 +1899,6 @@ func _Msg_MarketManageReqAttrs_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } -func _Msg_CreateMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgCreateMarketRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MsgServer).CreateMarket(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/provenance.exchange.v1.Msg/CreateMarket", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).CreateMarket(ctx, req.(*MsgCreateMarketRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _Msg_GovCreateMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MsgGovCreateMarketRequest) if err := dec(in); err != nil { @@ -1931,10 +2005,6 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "MarketManageReqAttrs", Handler: _Msg_MarketManageReqAttrs_Handler, }, - { - MethodName: "CreateMarket", - Handler: _Msg_CreateMarket_Handler, - }, { MethodName: "GovCreateMarket", Handler: _Msg_GovCreateMarket_Handler, @@ -2504,52 +2574,6 @@ func (m *MsgMarketManageReqAttrsResponse) MarshalToSizedBuffer(dAtA []byte) (int return len(dAtA) - i, nil } -func (m *MsgCreateMarketRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *MsgCreateMarketRequest) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MsgCreateMarketRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - return len(dAtA) - i, nil -} - -func (m *MsgCreateMarketResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *MsgCreateMarketResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MsgCreateMarketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - return len(dAtA) - i, nil -} - func (m *MsgGovCreateMarketRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2570,6 +2594,23 @@ func (m *MsgGovCreateMarketRequest) MarshalToSizedBuffer(dAtA []byte) (int, erro _ = i var l int _ = l + { + size, err := m.Market.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2616,6 +2657,181 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if len(m.RemoveFeeSettlementBuyerRatios) > 0 { + for iNdEx := len(m.RemoveFeeSettlementBuyerRatios) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RemoveFeeSettlementBuyerRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6a + } + } + if len(m.AddFeeSettlementBuyerRatios) > 0 { + for iNdEx := len(m.AddFeeSettlementBuyerRatios) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AddFeeSettlementBuyerRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } + } + if len(m.RemoveFeeSettlementBuyerFlat) > 0 { + for iNdEx := len(m.RemoveFeeSettlementBuyerFlat) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RemoveFeeSettlementBuyerFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } + } + if len(m.AddFeeSettlementBuyerFlat) > 0 { + for iNdEx := len(m.AddFeeSettlementBuyerFlat) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AddFeeSettlementBuyerFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + } + if len(m.RemoveFeeSettlementSellerRatios) > 0 { + for iNdEx := len(m.RemoveFeeSettlementSellerRatios) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RemoveFeeSettlementSellerRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if len(m.AddFeeSettlementSellerRatios) > 0 { + for iNdEx := len(m.AddFeeSettlementSellerRatios) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AddFeeSettlementSellerRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } + if len(m.RemoveFeeSettlementSellerFlat) > 0 { + for iNdEx := len(m.RemoveFeeSettlementSellerFlat) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RemoveFeeSettlementSellerFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if len(m.AddFeeSettlementSellerFlat) > 0 { + for iNdEx := len(m.AddFeeSettlementSellerFlat) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AddFeeSettlementSellerFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if len(m.RemoveFeeCreateBidFlat) > 0 { + for iNdEx := len(m.RemoveFeeCreateBidFlat) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RemoveFeeCreateBidFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.AddFeeCreateBidFlat) > 0 { + for iNdEx := len(m.AddFeeCreateBidFlat) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AddFeeCreateBidFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.RemoveFeeCreateAskFlat) > 0 { + for iNdEx := len(m.RemoveFeeCreateAskFlat) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RemoveFeeCreateAskFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.AddFeeCreateAskFlat) > 0 { + for iNdEx := len(m.AddFeeCreateAskFlat) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AddFeeCreateAskFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2662,6 +2878,23 @@ func (m *MsgGovUpdateParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, erro _ = i var l int _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2915,30 +3148,18 @@ func (m *MsgMarketManageReqAttrsResponse) Size() (n int) { return n } -func (m *MsgCreateMarketRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - return n -} - -func (m *MsgCreateMarketResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - return n -} - func (m *MsgGovCreateMarketRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Market.Size() + n += 1 + l + sovTx(uint64(l)) return n } @@ -2957,9 +3178,85 @@ func (m *MsgGovManageFeesRequest) Size() (n int) { } var l int _ = l - return n -} - + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.AddFeeCreateAskFlat) > 0 { + for _, e := range m.AddFeeCreateAskFlat { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.RemoveFeeCreateAskFlat) > 0 { + for _, e := range m.RemoveFeeCreateAskFlat { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.AddFeeCreateBidFlat) > 0 { + for _, e := range m.AddFeeCreateBidFlat { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.RemoveFeeCreateBidFlat) > 0 { + for _, e := range m.RemoveFeeCreateBidFlat { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.AddFeeSettlementSellerFlat) > 0 { + for _, e := range m.AddFeeSettlementSellerFlat { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.RemoveFeeSettlementSellerFlat) > 0 { + for _, e := range m.RemoveFeeSettlementSellerFlat { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.AddFeeSettlementSellerRatios) > 0 { + for _, e := range m.AddFeeSettlementSellerRatios { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.RemoveFeeSettlementSellerRatios) > 0 { + for _, e := range m.RemoveFeeSettlementSellerRatios { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.AddFeeSettlementBuyerFlat) > 0 { + for _, e := range m.AddFeeSettlementBuyerFlat { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.RemoveFeeSettlementBuyerFlat) > 0 { + for _, e := range m.RemoveFeeSettlementBuyerFlat { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.AddFeeSettlementBuyerRatios) > 0 { + for _, e := range m.AddFeeSettlementBuyerRatios { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.RemoveFeeSettlementBuyerRatios) > 0 { + for _, e := range m.RemoveFeeSettlementBuyerRatios { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + func (m *MsgGovManageFeesResponse) Size() (n int) { if m == nil { return 0 @@ -2975,6 +3272,12 @@ func (m *MsgGovUpdateParamsRequest) Size() (n int) { } var l int _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Params.Size() + n += 1 + l + sovTx(uint64(l)) return n } @@ -4193,7 +4496,7 @@ func (m *MsgMarketManageReqAttrsResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgCreateMarketRequest) Unmarshal(dAtA []byte) error { +func (m *MsgGovCreateMarketRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -4216,112 +4519,77 @@ func (m *MsgCreateMarketRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgCreateMarketRequest: wiretype end group for non-group") + return fmt.Errorf("proto: MsgGovCreateMarketRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgCreateMarketRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgGovCreateMarketRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipTx(dAtA[iNdEx:]) - if err != nil { - return err + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTx + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *MsgCreateMarketResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx } - if iNdEx >= l { + if postIndex > l { return io.ErrUnexpectedEOF } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Market", wireType) } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: MsgCreateMarketResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MsgCreateMarketResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipTx(dAtA[iNdEx:]) - if err != nil { - return err + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } } - if (skippy < 0) || (iNdEx+skippy) < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *MsgGovCreateMarketRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx } - if iNdEx >= l { + if postIndex > l { return io.ErrUnexpectedEOF } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break + if err := m.Market.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: MsgGovCreateMarketRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MsgGovCreateMarketRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -4422,6 +4690,446 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgGovManageFeesRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AddFeeCreateAskFlat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AddFeeCreateAskFlat = append(m.AddFeeCreateAskFlat, types.Coin{}) + if err := m.AddFeeCreateAskFlat[len(m.AddFeeCreateAskFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeCreateAskFlat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RemoveFeeCreateAskFlat = append(m.RemoveFeeCreateAskFlat, types.Coin{}) + if err := m.RemoveFeeCreateAskFlat[len(m.RemoveFeeCreateAskFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AddFeeCreateBidFlat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AddFeeCreateBidFlat = append(m.AddFeeCreateBidFlat, types.Coin{}) + if err := m.AddFeeCreateBidFlat[len(m.AddFeeCreateBidFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeCreateBidFlat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RemoveFeeCreateBidFlat = append(m.RemoveFeeCreateBidFlat, types.Coin{}) + if err := m.RemoveFeeCreateBidFlat[len(m.RemoveFeeCreateBidFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AddFeeSettlementSellerFlat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AddFeeSettlementSellerFlat = append(m.AddFeeSettlementSellerFlat, types.Coin{}) + if err := m.AddFeeSettlementSellerFlat[len(m.AddFeeSettlementSellerFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeSettlementSellerFlat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RemoveFeeSettlementSellerFlat = append(m.RemoveFeeSettlementSellerFlat, types.Coin{}) + if err := m.RemoveFeeSettlementSellerFlat[len(m.RemoveFeeSettlementSellerFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AddFeeSettlementSellerRatios", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AddFeeSettlementSellerRatios = append(m.AddFeeSettlementSellerRatios, FeeRatio{}) + if err := m.AddFeeSettlementSellerRatios[len(m.AddFeeSettlementSellerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeSettlementSellerRatios", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RemoveFeeSettlementSellerRatios = append(m.RemoveFeeSettlementSellerRatios, FeeRatio{}) + if err := m.RemoveFeeSettlementSellerRatios[len(m.RemoveFeeSettlementSellerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AddFeeSettlementBuyerFlat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AddFeeSettlementBuyerFlat = append(m.AddFeeSettlementBuyerFlat, types.Coin{}) + if err := m.AddFeeSettlementBuyerFlat[len(m.AddFeeSettlementBuyerFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeSettlementBuyerFlat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RemoveFeeSettlementBuyerFlat = append(m.RemoveFeeSettlementBuyerFlat, types.Coin{}) + if err := m.RemoveFeeSettlementBuyerFlat[len(m.RemoveFeeSettlementBuyerFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AddFeeSettlementBuyerRatios", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AddFeeSettlementBuyerRatios = append(m.AddFeeSettlementBuyerRatios, FeeRatio{}) + if err := m.AddFeeSettlementBuyerRatios[len(m.AddFeeSettlementBuyerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeSettlementBuyerRatios", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RemoveFeeSettlementBuyerRatios = append(m.RemoveFeeSettlementBuyerRatios, FeeRatio{}) + if err := m.RemoveFeeSettlementBuyerRatios[len(m.RemoveFeeSettlementBuyerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -4522,6 +5230,71 @@ func (m *MsgGovUpdateParamsRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgGovUpdateParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) From 437da26c0416c2ba4e8122078e579330f198d380 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 29 Aug 2023 13:12:03 -0600 Subject: [PATCH 039/309] [1658]: Make the entries in the ratios lists non-nullable. --- proto/provenance/exchange/v1/market.proto | 4 +- x/exchange/genesis_test.go | 8 +- x/exchange/market.go | 16 +-- x/exchange/market.pb.go | 142 +++++++++++----------- x/exchange/market_test.go | 120 +++++++----------- 5 files changed, 127 insertions(+), 163 deletions(-) diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index c81a4f828e..cac7c49f32 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -63,7 +63,7 @@ message Market { [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; // fee_settlement_seller_ratios is the fee to charge a seller during settlement based on the price they are receiving. // The price and fee denoms must be equal for each entry, and only one entry for any given denom is allowed. - repeated FeeRatio fee_settlement_seller_ratios = 6; + repeated FeeRatio fee_settlement_seller_ratios = 6 [(gogoproto.nullable) = false]; // fee_settlement_buyer_flat is the flat fee charged to the buyer during settlement. // Each coin entry is a separate option. // When a bid is created, the settlement fees provided must contain one of these. @@ -72,7 +72,7 @@ message Market { // fee_settlement_buyer_ratios is the fee to charge a buyer during settlement based on the price they are spending. // The price and fee denoms do not have to equal. Multiple entries for any given price or fee denom are allowed, but // each price denom to fee denom pair can only have one entry. - repeated FeeRatio fee_settlement_buyer_ratios = 8; + repeated FeeRatio fee_settlement_buyer_ratios = 8 [(gogoproto.nullable) = false]; // accepting_orders is whether this market is allowing orders to be created for it. bool accepting_orders = 9; // allow_user_settlement is whether this market allows users to initiate their own settlements. diff --git a/x/exchange/genesis_test.go b/x/exchange/genesis_test.go index 3b9fdc50f9..7515c76363 100644 --- a/x/exchange/genesis_test.go +++ b/x/exchange/genesis_test.go @@ -61,7 +61,7 @@ func TestNewGenesisState(t *testing.T) { MarketId: 2, MarketDetails: MarketDetails{Name: "Market Two"}, FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, - FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, + FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, AccessGrants: []*AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, ReqAttrCreateBid: []string{"just.some.attr"}, }, @@ -79,7 +79,7 @@ func TestNewGenesisState(t *testing.T) { MarketId: 2, MarketDetails: MarketDetails{Name: "Market Two"}, FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, - FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, + FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, AccessGrants: []*AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, ReqAttrCreateBid: []string{"just.some.attr"}, }, @@ -142,7 +142,7 @@ func TestNewGenesisState(t *testing.T) { MarketId: 2, MarketDetails: MarketDetails{Name: "Market Two"}, FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, - FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, + FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, AccessGrants: []*AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, ReqAttrCreateBid: []string{"just.some.attr"}, }, @@ -180,7 +180,7 @@ func TestNewGenesisState(t *testing.T) { MarketId: 2, MarketDetails: MarketDetails{Name: "Market Two"}, FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, - FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, + FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, AccessGrants: []*AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, ReqAttrCreateBid: []string{"just.some.attr"}, }, diff --git a/x/exchange/market.go b/x/exchange/market.go index 85634d3d9b..6a46e03d41 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -88,7 +88,7 @@ func (d MarketDetails) Validate() error { } // ValidateFeeRatios makes sure that the provided fee ratios are valid and have the same price denoms. -func ValidateFeeRatios(sellerRatios, buyerRatios []*FeeRatio) error { +func ValidateFeeRatios(sellerRatios, buyerRatios []FeeRatio) error { var errs []error if err := ValidateSellerFeeRatios(sellerRatios); err != nil { errs = append(errs, err) @@ -133,7 +133,7 @@ func ValidateFeeRatios(sellerRatios, buyerRatios []*FeeRatio) error { } // ValidateSellerFeeRatios returns an error if the provided seller fee ratios contains an invalid entry. -func ValidateSellerFeeRatios(ratios []*FeeRatio) error { +func ValidateSellerFeeRatios(ratios []FeeRatio) error { if len(ratios) == 0 { return nil } @@ -142,11 +142,6 @@ func ValidateSellerFeeRatios(ratios []*FeeRatio) error { dups := make(map[string]bool) var errs []error for _, ratio := range ratios { - if ratio == nil { - errs = append(errs, errors.New("nil seller fee ratio not allowed")) - continue - } - key := ratio.Price.Denom if seen[key] { if !dups[key] { @@ -170,7 +165,7 @@ func ValidateSellerFeeRatios(ratios []*FeeRatio) error { } // ValidateBuyerFeeRatios returns an error if the provided buyer fee ratios contains an invalid entry. -func ValidateBuyerFeeRatios(ratios []*FeeRatio) error { +func ValidateBuyerFeeRatios(ratios []FeeRatio) error { if len(ratios) == 0 { return nil } @@ -179,11 +174,6 @@ func ValidateBuyerFeeRatios(ratios []*FeeRatio) error { dups := make(map[string]bool) var errs []error for _, ratio := range ratios { - if ratio == nil { - errs = append(errs, errors.New("nil buyer fee ratio not allowed")) - continue - } - key := ratio.Price.Denom + ":" + ratio.Fee.Denom if seen[key] { if !dups[key] { diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index 415899d18d..db8c42c2d7 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -210,7 +210,7 @@ type Market struct { FeeSettlementSellerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,5,rep,name=fee_settlement_seller_flat,json=feeSettlementSellerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"fee_settlement_seller_flat"` // fee_settlement_seller_ratios is the fee to charge a seller during settlement based on the price they are receiving. // The price and fee denoms must be equal for each entry, and only one entry for any given denom is allowed. - FeeSettlementSellerRatios []*FeeRatio `protobuf:"bytes,6,rep,name=fee_settlement_seller_ratios,json=feeSettlementSellerRatios,proto3" json:"fee_settlement_seller_ratios,omitempty"` + FeeSettlementSellerRatios []FeeRatio `protobuf:"bytes,6,rep,name=fee_settlement_seller_ratios,json=feeSettlementSellerRatios,proto3" json:"fee_settlement_seller_ratios"` // fee_settlement_buyer_flat is the flat fee charged to the buyer during settlement. // Each coin entry is a separate option. // When a bid is created, the settlement fees provided must contain one of these. @@ -218,7 +218,7 @@ type Market struct { // fee_settlement_buyer_ratios is the fee to charge a buyer during settlement based on the price they are spending. // The price and fee denoms do not have to equal. Multiple entries for any given price or fee denom are allowed, but // each price denom to fee denom pair can only have one entry. - FeeSettlementBuyerRatios []*FeeRatio `protobuf:"bytes,8,rep,name=fee_settlement_buyer_ratios,json=feeSettlementBuyerRatios,proto3" json:"fee_settlement_buyer_ratios,omitempty"` + FeeSettlementBuyerRatios []FeeRatio `protobuf:"bytes,8,rep,name=fee_settlement_buyer_ratios,json=feeSettlementBuyerRatios,proto3" json:"fee_settlement_buyer_ratios"` // accepting_orders is whether this market is allowing orders to be created for it. AcceptingOrders bool `protobuf:"varint,9,opt,name=accepting_orders,json=acceptingOrders,proto3" json:"accepting_orders,omitempty"` // allow_user_settlement is whether this market allows users to initiate their own settlements. @@ -311,7 +311,7 @@ func (m *Market) GetFeeSettlementSellerFlat() github_com_cosmos_cosmos_sdk_types return nil } -func (m *Market) GetFeeSettlementSellerRatios() []*FeeRatio { +func (m *Market) GetFeeSettlementSellerRatios() []FeeRatio { if m != nil { return m.FeeSettlementSellerRatios } @@ -325,7 +325,7 @@ func (m *Market) GetFeeSettlementBuyerFlat() github_com_cosmos_cosmos_sdk_types. return nil } -func (m *Market) GetFeeSettlementBuyerRatios() []*FeeRatio { +func (m *Market) GetFeeSettlementBuyerRatios() []FeeRatio { if m != nil { return m.FeeSettlementBuyerRatios } @@ -491,71 +491,71 @@ func init() { } var fileDescriptor_d5cf198f1dd7e167 = []byte{ - // 1010 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x41, 0x6f, 0x1a, 0x47, - 0x14, 0x66, 0x0d, 0xb6, 0x61, 0xb0, 0x1d, 0x3a, 0x4e, 0xdc, 0x05, 0x57, 0xb0, 0x25, 0x8a, 0x44, - 0x5a, 0x19, 0x6a, 0x57, 0xbd, 0xf8, 0x06, 0x36, 0x6e, 0x90, 0x12, 0xc7, 0x5a, 0x40, 0x91, 0x7a, - 0xd9, 0x0e, 0xbb, 0x0f, 0x3c, 0xf2, 0xb2, 0x4b, 0x66, 0x66, 0xed, 0x38, 0xb7, 0x5e, 0xda, 0xc8, - 0xa7, 0x1e, 0x7b, 0xb1, 0x94, 0x5e, 0x7b, 0xee, 0x8f, 0xc8, 0xd1, 0xea, 0xa9, 0x27, 0xb7, 0xb2, - 0xaf, 0x3d, 0xf5, 0x17, 0x54, 0x3b, 0xb3, 0x86, 0x0d, 0xc5, 0x4d, 0xa5, 0xa6, 0x27, 0xf6, 0xbd, - 0xef, 0x9b, 0xf7, 0x7d, 0xef, 0xcd, 0xf2, 0xb4, 0xe8, 0xfe, 0x88, 0xf9, 0xc7, 0xe0, 0x11, 0xcf, - 0x86, 0x1a, 0xbc, 0xb0, 0x0f, 0x89, 0x37, 0x80, 0xda, 0xf1, 0x66, 0x6d, 0x48, 0xd8, 0x11, 0x88, - 0xea, 0x88, 0xf9, 0xc2, 0xc7, 0x6b, 0x13, 0x52, 0xf5, 0x86, 0x54, 0x3d, 0xde, 0x2c, 0x14, 0x6d, - 0x9f, 0x0f, 0x7d, 0x5e, 0x23, 0x81, 0x38, 0xac, 0x1d, 0x6f, 0xf6, 0x40, 0x90, 0x4d, 0x19, 0xa8, - 0x73, 0x63, 0xbc, 0x47, 0x38, 0x8c, 0x71, 0xdb, 0xa7, 0x5e, 0x84, 0xe7, 0x15, 0x6e, 0xc9, 0xa8, - 0xa6, 0x82, 0x08, 0xba, 0x3b, 0xf0, 0x07, 0xbe, 0xca, 0x87, 0x4f, 0x2a, 0x5b, 0xfe, 0x43, 0x43, - 0xcb, 0x4f, 0xa4, 0xb3, 0xba, 0x6d, 0xfb, 0x81, 0x27, 0xf0, 0xd7, 0x68, 0x29, 0xac, 0x6e, 0x11, - 0x15, 0xeb, 0x9a, 0xa1, 0x55, 0xb2, 0x5b, 0x46, 0x35, 0x2a, 0x26, 0xcd, 0x44, 0xca, 0xd5, 0x06, - 0xe1, 0x10, 0x9d, 0x6b, 0xac, 0x5f, 0x5c, 0x96, 0xb4, 0x3f, 0x2f, 0x4b, 0xab, 0xa7, 0x64, 0xe8, - 0x6e, 0x97, 0xe3, 0x35, 0xca, 0x66, 0xb6, 0x37, 0x61, 0xe2, 0x75, 0x94, 0x51, 0xc3, 0xb0, 0xa8, - 0xa3, 0xcf, 0x19, 0x5a, 0x65, 0xd9, 0x4c, 0xab, 0x44, 0xcb, 0xc1, 0x26, 0x5a, 0x89, 0x40, 0x07, - 0x04, 0xa1, 0x2e, 0xd7, 0x93, 0xd2, 0xc0, 0x83, 0xea, 0xec, 0x91, 0x55, 0x95, 0xfb, 0x5d, 0x45, - 0x6e, 0xa4, 0xde, 0x5c, 0x96, 0x12, 0xe6, 0xf2, 0x30, 0x9e, 0xdc, 0x4e, 0xbf, 0x7a, 0x5d, 0x4a, - 0xfc, 0xf0, 0xba, 0x94, 0x28, 0x7f, 0x33, 0x6e, 0x37, 0xc2, 0x30, 0x46, 0x29, 0x8f, 0x0c, 0x41, - 0xb6, 0x99, 0x31, 0xe5, 0x33, 0x36, 0x50, 0xd6, 0x01, 0x6e, 0x33, 0x3a, 0x12, 0xd4, 0xf7, 0xa4, - 0xc5, 0x8c, 0x19, 0x4f, 0xe1, 0x12, 0xca, 0x9e, 0x40, 0x8f, 0x53, 0x01, 0x56, 0xc0, 0x5c, 0x69, - 0x31, 0x63, 0xa2, 0x28, 0xd5, 0x65, 0x2e, 0xce, 0xa3, 0x34, 0xb5, 0x7d, 0xcf, 0x0a, 0x18, 0xd5, - 0x53, 0x12, 0x5d, 0x0c, 0xe3, 0x2e, 0xa3, 0xe5, 0x8b, 0x34, 0x5a, 0x50, 0x1e, 0xde, 0x9e, 0x84, - 0xf6, 0xce, 0x49, 0xcc, 0xfd, 0xd7, 0x49, 0xe0, 0x97, 0x68, 0xb5, 0x0f, 0x60, 0xd9, 0x0c, 0x88, - 0x00, 0x8b, 0xf0, 0x23, 0xab, 0xef, 0x12, 0xa1, 0x27, 0x8d, 0x64, 0x25, 0xbb, 0x95, 0xbf, 0xb9, - 0xe3, 0xf0, 0xb2, 0xc6, 0x77, 0xbc, 0xe3, 0x53, 0xaf, 0xf1, 0x59, 0x58, 0xec, 0xa7, 0xdf, 0x4a, - 0x95, 0x01, 0x15, 0x87, 0x41, 0xaf, 0x6a, 0xfb, 0xc3, 0xe8, 0xed, 0x8a, 0x7e, 0x36, 0xb8, 0x73, - 0x54, 0x13, 0xa7, 0x23, 0xe0, 0xf2, 0x00, 0x37, 0x73, 0x7d, 0x80, 0x1d, 0x29, 0x53, 0xe7, 0x47, - 0x7b, 0x2e, 0x11, 0x53, 0xda, 0x3d, 0xea, 0x28, 0xed, 0xd4, 0xff, 0xa9, 0xdd, 0xa0, 0x8e, 0xd4, - 0x7e, 0xa5, 0xa1, 0x42, 0x28, 0xce, 0x41, 0x08, 0x17, 0x86, 0xe0, 0x09, 0x8b, 0x83, 0xeb, 0x02, - 0x53, 0x1e, 0xe6, 0xdf, 0xbf, 0x87, 0x0f, 0xfb, 0x00, 0xed, 0xb1, 0x5a, 0x5b, 0x8a, 0x49, 0x2b, - 0x04, 0x7d, 0x34, 0xdb, 0x09, 0x23, 0x82, 0xfa, 0x5c, 0x5f, 0x90, 0x5e, 0x8c, 0xdb, 0x2e, 0x79, - 0x0f, 0xc0, 0x0c, 0x89, 0x66, 0x7e, 0x86, 0x80, 0x44, 0x38, 0xfe, 0x56, 0x43, 0xf9, 0x29, 0x8d, - 0x5e, 0x70, 0x7a, 0xd3, 0xec, 0xe2, 0xfb, 0x6f, 0x76, 0xed, 0x2d, 0x2f, 0x8d, 0x50, 0x4b, 0xf6, - 0x6a, 0xa1, 0xf5, 0x99, 0x3e, 0xa2, 0x56, 0xd3, 0xff, 0xb2, 0x55, 0xfd, 0xef, 0xe5, 0xa3, 0x4e, - 0x1f, 0xa2, 0x1c, 0xb1, 0x6d, 0x18, 0x09, 0xea, 0x0d, 0x2c, 0x9f, 0x39, 0xc0, 0xb8, 0x9e, 0x31, - 0xb4, 0x4a, 0xda, 0xbc, 0x33, 0xce, 0x3f, 0x95, 0x69, 0xbc, 0x85, 0xee, 0x11, 0xd7, 0xf5, 0x4f, - 0xac, 0x80, 0x03, 0x8b, 0x59, 0xd2, 0x91, 0xe4, 0xaf, 0x4a, 0xb0, 0xcb, 0x81, 0x4d, 0x94, 0xf0, - 0x23, 0xb4, 0x1c, 0x96, 0xe1, 0xdc, 0x1a, 0x30, 0xe2, 0x09, 0xae, 0x67, 0xa5, 0xe3, 0xfb, 0xb7, - 0x39, 0xae, 0x4b, 0xf2, 0x97, 0x21, 0xd7, 0x5c, 0x22, 0x93, 0x80, 0xe3, 0x0d, 0xb4, 0xca, 0xe0, - 0xb9, 0x45, 0x84, 0x60, 0xb1, 0x7f, 0x9f, 0xbe, 0x64, 0x24, 0x2b, 0x19, 0x33, 0xc7, 0xe0, 0x79, - 0x5d, 0x08, 0x36, 0xfe, 0xbf, 0xcc, 0xa2, 0xf7, 0xa8, 0xa3, 0x2f, 0xcf, 0xa0, 0x37, 0xa8, 0x53, - 0x7e, 0x89, 0xd2, 0x37, 0xc3, 0xc2, 0x5f, 0xa0, 0xf9, 0x11, 0xa3, 0x36, 0x44, 0x8b, 0xfb, 0x1f, - 0xee, 0x59, 0x6d, 0x08, 0xc5, 0xc6, 0x9b, 0x28, 0xd9, 0x07, 0x88, 0x56, 0xcc, 0x3b, 0x0f, 0x85, - 0xdc, 0xed, 0x94, 0x5c, 0xa9, 0xdf, 0x69, 0x28, 0x1b, 0xeb, 0x1b, 0x6f, 0xa1, 0x45, 0xe2, 0x38, - 0x0c, 0x38, 0x57, 0x3b, 0xb5, 0xa1, 0xff, 0xf2, 0xf3, 0xc6, 0xdd, 0xa8, 0x5e, 0x5d, 0x21, 0x6d, - 0xc1, 0xa8, 0x37, 0x30, 0x6f, 0x88, 0x78, 0x17, 0x65, 0x47, 0xc0, 0x86, 0x94, 0x73, 0xea, 0x7b, - 0xe1, 0x9e, 0x4b, 0x56, 0x56, 0xb6, 0xca, 0xb7, 0x4d, 0xf9, 0x60, 0x4c, 0x35, 0xe3, 0xc7, 0x3e, - 0xf9, 0x71, 0x0e, 0xa1, 0x09, 0x86, 0x3f, 0x45, 0x6b, 0x07, 0x4d, 0xf3, 0x49, 0xab, 0xdd, 0x6e, - 0x3d, 0xdd, 0xb7, 0xba, 0xfb, 0xed, 0x83, 0xe6, 0x4e, 0x6b, 0xaf, 0xd5, 0xdc, 0xcd, 0x25, 0x0a, - 0x77, 0xce, 0xce, 0x8d, 0x6c, 0xe0, 0xf1, 0x11, 0xd8, 0xb4, 0x4f, 0xc1, 0xc1, 0x1f, 0xa3, 0x0f, - 0x62, 0xe4, 0x76, 0xb3, 0xd3, 0x79, 0xdc, 0xcc, 0x69, 0x05, 0x74, 0x76, 0x6e, 0x2c, 0xa8, 0x77, - 0x65, 0x8a, 0xb2, 0x53, 0xdf, 0xdf, 0x69, 0x3e, 0xce, 0xcd, 0x29, 0x8a, 0x1d, 0x9a, 0x74, 0xf1, - 0x03, 0xb4, 0x1a, 0xa3, 0x3c, 0x6b, 0x75, 0x1e, 0xed, 0x9a, 0xf5, 0x67, 0xb9, 0x64, 0x61, 0xe9, - 0xec, 0xdc, 0x48, 0x9f, 0x50, 0x71, 0xe8, 0x30, 0x72, 0x32, 0x55, 0xa9, 0x7b, 0xb0, 0x5b, 0xef, - 0x34, 0x73, 0x29, 0x55, 0x29, 0x18, 0x39, 0x44, 0xc0, 0x94, 0xf9, 0xc9, 0x63, 0x3b, 0x37, 0xaf, - 0xcc, 0xc7, 0x1a, 0xc7, 0x0f, 0xd1, 0xbd, 0x18, 0xb9, 0xde, 0xe9, 0x98, 0xad, 0x46, 0xb7, 0xd3, - 0x6c, 0xe7, 0x16, 0x0a, 0x2b, 0x67, 0xe7, 0x06, 0x0a, 0x5f, 0x23, 0xda, 0x0b, 0x04, 0xf0, 0x06, - 0xbc, 0xb9, 0x2a, 0x6a, 0x17, 0x57, 0x45, 0xed, 0xf7, 0xab, 0xa2, 0xf6, 0xfd, 0x75, 0x31, 0x71, - 0x71, 0x5d, 0x4c, 0xfc, 0x7a, 0x5d, 0x4c, 0xa0, 0x3c, 0xf5, 0x6f, 0x19, 0xf8, 0x81, 0xf6, 0x55, - 0x35, 0xb6, 0x0a, 0x26, 0xa4, 0x0d, 0xea, 0xc7, 0xa2, 0xda, 0x8b, 0xf1, 0xf7, 0x4e, 0x6f, 0x41, - 0x7e, 0x5d, 0x7c, 0xfe, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x91, 0xc0, 0x12, 0x3a, 0x0d, 0x09, - 0x00, 0x00, + // 1013 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xc1, 0x6f, 0x1a, 0xc7, + 0x17, 0x66, 0x0d, 0xb6, 0x61, 0xb0, 0x1d, 0x7e, 0xe3, 0xc4, 0xbf, 0x05, 0x57, 0xb0, 0x25, 0x8a, + 0x44, 0x5a, 0x19, 0x6a, 0x57, 0xbd, 0xf8, 0x06, 0x36, 0x6e, 0x90, 0x12, 0xc7, 0x5a, 0x40, 0x91, + 0x7a, 0xd9, 0x0e, 0xbb, 0x0f, 0x3c, 0xf2, 0xb2, 0x4b, 0x66, 0x66, 0xed, 0x38, 0xb7, 0x5e, 0xda, + 0xc8, 0xa7, 0x1e, 0x7b, 0xb1, 0x94, 0x5e, 0x7b, 0xee, 0x1f, 0x91, 0xa3, 0xd5, 0x53, 0x0f, 0x95, + 0x5b, 0xd9, 0xd7, 0x9e, 0xfa, 0x17, 0x54, 0x3b, 0xb3, 0x86, 0x0d, 0xc5, 0x8d, 0xaa, 0xa6, 0x27, + 0xf6, 0xbd, 0xef, 0x9b, 0xf7, 0x7d, 0xef, 0xcd, 0xf2, 0xb4, 0xe8, 0xfe, 0x88, 0xf9, 0xc7, 0xe0, + 0x11, 0xcf, 0x86, 0x1a, 0xbc, 0xb0, 0x0f, 0x89, 0x37, 0x80, 0xda, 0xf1, 0x66, 0x6d, 0x48, 0xd8, + 0x11, 0x88, 0xea, 0x88, 0xf9, 0xc2, 0xc7, 0x6b, 0x13, 0x52, 0xf5, 0x86, 0x54, 0x3d, 0xde, 0x2c, + 0x14, 0x6d, 0x9f, 0x0f, 0x7d, 0x5e, 0x23, 0x81, 0x38, 0xac, 0x1d, 0x6f, 0xf6, 0x40, 0x90, 0x4d, + 0x19, 0xa8, 0x73, 0x63, 0xbc, 0x47, 0x38, 0x8c, 0x71, 0xdb, 0xa7, 0x5e, 0x84, 0xe7, 0x15, 0x6e, + 0xc9, 0xa8, 0xa6, 0x82, 0x08, 0xba, 0x3b, 0xf0, 0x07, 0xbe, 0xca, 0x87, 0x4f, 0x2a, 0x5b, 0xfe, + 0x5d, 0x43, 0xcb, 0x4f, 0xa4, 0xb3, 0xba, 0x6d, 0xfb, 0x81, 0x27, 0xf0, 0x97, 0x68, 0x29, 0xac, + 0x6e, 0x11, 0x15, 0xeb, 0x9a, 0xa1, 0x55, 0xb2, 0x5b, 0x46, 0x35, 0x2a, 0x26, 0xcd, 0x44, 0xca, + 0xd5, 0x06, 0xe1, 0x10, 0x9d, 0x6b, 0xac, 0x5f, 0x5c, 0x96, 0xb4, 0x3f, 0x2e, 0x4b, 0xab, 0xa7, + 0x64, 0xe8, 0x6e, 0x97, 0xe3, 0x35, 0xca, 0x66, 0xb6, 0x37, 0x61, 0xe2, 0x75, 0x94, 0x51, 0xc3, + 0xb0, 0xa8, 0xa3, 0xcf, 0x19, 0x5a, 0x65, 0xd9, 0x4c, 0xab, 0x44, 0xcb, 0xc1, 0x26, 0x5a, 0x89, + 0x40, 0x07, 0x04, 0xa1, 0x2e, 0xd7, 0x93, 0xd2, 0xc0, 0x83, 0xea, 0xec, 0x91, 0x55, 0x95, 0xfb, + 0x5d, 0x45, 0x6e, 0xa4, 0xde, 0x5c, 0x96, 0x12, 0xe6, 0xf2, 0x30, 0x9e, 0xdc, 0x4e, 0xbf, 0x7a, + 0x5d, 0x4a, 0x7c, 0xf7, 0xba, 0x94, 0x28, 0x7f, 0x35, 0x6e, 0x37, 0xc2, 0x30, 0x46, 0x29, 0x8f, + 0x0c, 0x41, 0xb6, 0x99, 0x31, 0xe5, 0x33, 0x36, 0x50, 0xd6, 0x01, 0x6e, 0x33, 0x3a, 0x12, 0xd4, + 0xf7, 0xa4, 0xc5, 0x8c, 0x19, 0x4f, 0xe1, 0x12, 0xca, 0x9e, 0x40, 0x8f, 0x53, 0x01, 0x56, 0xc0, + 0x5c, 0x69, 0x31, 0x63, 0xa2, 0x28, 0xd5, 0x65, 0x2e, 0xce, 0xa3, 0x34, 0xb5, 0x7d, 0xcf, 0x0a, + 0x18, 0xd5, 0x53, 0x12, 0x5d, 0x0c, 0xe3, 0x2e, 0xa3, 0xe5, 0x5f, 0xd2, 0x68, 0x41, 0x79, 0x78, + 0x7b, 0x12, 0xda, 0x3b, 0x27, 0x31, 0xf7, 0x6f, 0x27, 0x81, 0x5f, 0xa2, 0xd5, 0x3e, 0x80, 0x65, + 0x33, 0x20, 0x02, 0x2c, 0xc2, 0x8f, 0xac, 0xbe, 0x4b, 0x84, 0x9e, 0x34, 0x92, 0x95, 0xec, 0x56, + 0xfe, 0xe6, 0x8e, 0xc3, 0xcb, 0x1a, 0xdf, 0xf1, 0x8e, 0x4f, 0xbd, 0xc6, 0x27, 0x61, 0xb1, 0x1f, + 0x7e, 0x2d, 0x55, 0x06, 0x54, 0x1c, 0x06, 0xbd, 0xaa, 0xed, 0x0f, 0xa3, 0xb7, 0x2b, 0xfa, 0xd9, + 0xe0, 0xce, 0x51, 0x4d, 0x9c, 0x8e, 0x80, 0xcb, 0x03, 0xdc, 0xcc, 0xf5, 0x01, 0x76, 0xa4, 0x4c, + 0x9d, 0x1f, 0xed, 0xb9, 0x44, 0x4c, 0x69, 0xf7, 0xa8, 0xa3, 0xb4, 0x53, 0xff, 0xa5, 0x76, 0x83, + 0x3a, 0x52, 0xfb, 0x95, 0x86, 0x0a, 0xa1, 0x38, 0x07, 0x21, 0x5c, 0x18, 0x82, 0x27, 0x2c, 0x0e, + 0xae, 0x0b, 0x4c, 0x79, 0x98, 0x7f, 0xff, 0x1e, 0xfe, 0xdf, 0x07, 0x68, 0x8f, 0xd5, 0xda, 0x52, + 0x4c, 0x5a, 0x19, 0xa0, 0x0f, 0x66, 0x3b, 0x61, 0x44, 0x50, 0x9f, 0xeb, 0x0b, 0xd2, 0x8b, 0x71, + 0xdb, 0x25, 0xef, 0x01, 0x98, 0x21, 0x31, 0xba, 0xdf, 0xfc, 0x0c, 0x19, 0x89, 0x73, 0xfc, 0xb5, + 0x86, 0xf2, 0x53, 0x4a, 0xbd, 0xe0, 0xf4, 0xa6, 0xe5, 0xc5, 0xf7, 0xdf, 0xf2, 0xda, 0x5b, 0x5e, + 0x1a, 0xa1, 0x96, 0xec, 0x18, 0xd0, 0xfa, 0x4c, 0x1f, 0x51, 0xc3, 0xe9, 0x7f, 0xd4, 0xb0, 0xfe, + 0x57, 0x91, 0xa8, 0xdf, 0x87, 0x28, 0x47, 0x6c, 0x1b, 0x46, 0x82, 0x7a, 0x03, 0xcb, 0x67, 0x0e, + 0x30, 0xae, 0x67, 0x0c, 0xad, 0x92, 0x36, 0xef, 0x8c, 0xf3, 0x4f, 0x65, 0x1a, 0x6f, 0xa1, 0x7b, + 0xc4, 0x75, 0xfd, 0x13, 0x2b, 0xe0, 0xc0, 0x62, 0xc6, 0x74, 0x24, 0xf9, 0xab, 0x12, 0xec, 0x72, + 0x60, 0x13, 0x25, 0xfc, 0x08, 0x2d, 0x87, 0x65, 0x38, 0xb7, 0x06, 0x8c, 0x78, 0x82, 0xeb, 0x59, + 0xe9, 0xfb, 0xfe, 0x6d, 0xbe, 0xeb, 0x92, 0xfc, 0x79, 0xc8, 0x35, 0x97, 0xc8, 0x24, 0xe0, 0x78, + 0x03, 0xad, 0x32, 0x78, 0x6e, 0x11, 0x21, 0x58, 0xec, 0x9f, 0xa8, 0x2f, 0x19, 0xc9, 0x4a, 0xc6, + 0xcc, 0x31, 0x78, 0x5e, 0x17, 0x82, 0x8d, 0xff, 0x3b, 0xb3, 0xe8, 0x3d, 0xea, 0xe8, 0xcb, 0x33, + 0xe8, 0x0d, 0xea, 0x94, 0x5f, 0xa2, 0xf4, 0xcd, 0xc8, 0xf0, 0x67, 0x68, 0x7e, 0xc4, 0xa8, 0x0d, + 0xd1, 0x12, 0xff, 0x9b, 0xdb, 0x56, 0xc3, 0x55, 0x6c, 0xbc, 0x89, 0x92, 0x7d, 0x80, 0x68, 0xdd, + 0xbc, 0xf3, 0x50, 0xc8, 0xdd, 0x4e, 0xc9, 0xf5, 0xfa, 0x8d, 0x86, 0xb2, 0xb1, 0xbe, 0xf1, 0x16, + 0x5a, 0x24, 0x8e, 0xc3, 0x80, 0x73, 0xb5, 0x5f, 0x1b, 0xfa, 0x4f, 0x3f, 0x6e, 0xdc, 0x8d, 0xea, + 0xd5, 0x15, 0xd2, 0x16, 0x8c, 0x7a, 0x03, 0xf3, 0x86, 0x88, 0x77, 0x51, 0x76, 0x04, 0x6c, 0x48, + 0x39, 0xa7, 0xbe, 0x17, 0xee, 0xbc, 0x64, 0x65, 0x65, 0xab, 0x7c, 0xdb, 0x94, 0x0f, 0xc6, 0x54, + 0x33, 0x7e, 0xec, 0xa3, 0xef, 0xe7, 0x10, 0x9a, 0x60, 0xf8, 0x63, 0xb4, 0x76, 0xd0, 0x34, 0x9f, + 0xb4, 0xda, 0xed, 0xd6, 0xd3, 0x7d, 0xab, 0xbb, 0xdf, 0x3e, 0x68, 0xee, 0xb4, 0xf6, 0x5a, 0xcd, + 0xdd, 0x5c, 0xa2, 0x70, 0xe7, 0xec, 0xdc, 0xc8, 0x06, 0x1e, 0x1f, 0x81, 0x4d, 0xfb, 0x14, 0x1c, + 0xfc, 0x21, 0xfa, 0x5f, 0x8c, 0xdc, 0x6e, 0x76, 0x3a, 0x8f, 0x9b, 0x39, 0xad, 0x80, 0xce, 0xce, + 0x8d, 0x05, 0xf5, 0xae, 0x4c, 0x51, 0x76, 0xea, 0xfb, 0x3b, 0xcd, 0xc7, 0xb9, 0x39, 0x45, 0xb1, + 0x43, 0x93, 0x2e, 0x7e, 0x80, 0x56, 0x63, 0x94, 0x67, 0xad, 0xce, 0xa3, 0x5d, 0xb3, 0xfe, 0x2c, + 0x97, 0x2c, 0x2c, 0x9d, 0x9d, 0x1b, 0xe9, 0x13, 0x2a, 0x0e, 0x1d, 0x46, 0x4e, 0xa6, 0x2a, 0x75, + 0x0f, 0x76, 0xeb, 0x9d, 0x66, 0x2e, 0xa5, 0x2a, 0x05, 0x23, 0x87, 0x08, 0x98, 0x32, 0x3f, 0x79, + 0x6c, 0xe7, 0xe6, 0x95, 0xf9, 0x58, 0xe3, 0xf8, 0x21, 0xba, 0x17, 0x23, 0xd7, 0x3b, 0x1d, 0xb3, + 0xd5, 0xe8, 0x76, 0x9a, 0xed, 0xdc, 0x42, 0x61, 0xe5, 0xec, 0xdc, 0x40, 0xe1, 0x6b, 0x44, 0x7b, + 0x81, 0x00, 0xde, 0x80, 0x37, 0x57, 0x45, 0xed, 0xe2, 0xaa, 0xa8, 0xfd, 0x76, 0x55, 0xd4, 0xbe, + 0xbd, 0x2e, 0x26, 0x2e, 0xae, 0x8b, 0x89, 0x9f, 0xaf, 0x8b, 0x09, 0x94, 0xa7, 0xfe, 0x2d, 0x03, + 0x3f, 0xd0, 0xbe, 0xa8, 0xc6, 0x16, 0xc2, 0x84, 0xb4, 0x41, 0xfd, 0x58, 0x54, 0x7b, 0x31, 0xfe, + 0xf6, 0xe9, 0x2d, 0xc8, 0x2f, 0x8d, 0x4f, 0xff, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x8c, 0x6c, 0x47, + 0x0b, 0x19, 0x09, 0x00, 0x00, } func (m *MarketAccount) Marshal() (dAtA []byte, err error) { @@ -1619,7 +1619,7 @@ func (m *Market) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.FeeSettlementSellerRatios = append(m.FeeSettlementSellerRatios, &FeeRatio{}) + m.FeeSettlementSellerRatios = append(m.FeeSettlementSellerRatios, FeeRatio{}) if err := m.FeeSettlementSellerRatios[len(m.FeeSettlementSellerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -1687,7 +1687,7 @@ func (m *Market) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.FeeSettlementBuyerRatios = append(m.FeeSettlementBuyerRatios, &FeeRatio{}) + m.FeeSettlementBuyerRatios = append(m.FeeSettlementBuyerRatios, FeeRatio{}) if err := m.FeeSettlementBuyerRatios[len(m.FeeSettlementBuyerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 81f8ab7e38..76b03a0c68 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -46,12 +46,12 @@ func TestMarket_Validate(t *testing.T) { FeeCreateAskFlat: coins("10nnibbler,5mfry"), FeeCreateBidFlat: coins("15nnibbler,5mfry"), FeeSettlementSellerFlat: coins("50nnibler,8mfry"), - FeeSettlementSellerRatios: []*FeeRatio{ + FeeSettlementSellerRatios: []FeeRatio{ {Price: coin(1000, "nnibler"), Fee: coin(1, "nnibler")}, {Price: coin(300, "mfry"), Fee: coin(1, "mfry")}, }, FeeSettlementBuyerFlat: coins("100nnibler,20mfry"), - FeeSettlementBuyerRatios: []*FeeRatio{ + FeeSettlementBuyerRatios: []FeeRatio{ {Price: coin(500, "nnibler"), Fee: coin(1, "nnibler")}, {Price: coin(500, "nnibler"), Fee: coin(8, "mfry")}, {Price: coin(150, "mfry"), Fee: coin(1, "mfry")}, @@ -100,19 +100,19 @@ func TestMarket_Validate(t *testing.T) { }, { name: "invalid seller ratio", - market: Market{FeeSettlementSellerRatios: []*FeeRatio{{Price: coin(0, "fry"), Fee: coin(1, "fry")}}}, + market: Market{FeeSettlementSellerRatios: []FeeRatio{{Price: coin(0, "fry"), Fee: coin(1, "fry")}}}, expErr: []string{`seller fee ratio price amount "0fry" must be positive`}, }, { name: "invalid buyer ratio", - market: Market{FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(0, "fry"), Fee: coin(1, "fry")}}}, + market: Market{FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(0, "fry"), Fee: coin(1, "fry")}}}, expErr: []string{`buyer fee ratio price amount "0fry" must be positive`}, }, { name: "invalid ratios", market: Market{ - FeeSettlementSellerRatios: []*FeeRatio{{Price: coin(10, "fry"), Fee: coin(1, "fry")}}, - FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(100, "leela"), Fee: coin(1, "leela")}}, + FeeSettlementSellerRatios: []FeeRatio{{Price: coin(10, "fry"), Fee: coin(1, "fry")}}, + FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(100, "leela"), Fee: coin(1, "leela")}}, }, expErr: []string{ `denom "fry" is defined in the seller settlement fee ratios but not buyer`, @@ -142,8 +142,8 @@ func TestMarket_Validate(t *testing.T) { FeeCreateBidFlat: sdk.Coins{coin(-1, "leela")}, FeeSettlementSellerFlat: sdk.Coins{coin(-1, "leela")}, FeeSettlementBuyerFlat: sdk.Coins{coin(-1, "leela")}, - FeeSettlementSellerRatios: []*FeeRatio{{Price: coin(10, "fry"), Fee: coin(1, "fry")}}, - FeeSettlementBuyerRatios: []*FeeRatio{{Price: coin(100, "leela"), Fee: coin(1, "leela")}}, + FeeSettlementSellerRatios: []FeeRatio{{Price: coin(10, "fry"), Fee: coin(1, "fry")}}, + FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(100, "leela"), Fee: coin(1, "leela")}}, AccessGrants: []*AccessGrant{{Address: "bad_addr", Permissions: AllPermissions()}}, ReqAttrCreateAsk: []string{"this-attr-is-bad"}, ReqAttrCreateBid: []string{"this-attr-grrrr"}, @@ -393,8 +393,8 @@ func TestValidateFeeRatios(t *testing.T) { tests := []struct { name string - sellerRatios []*FeeRatio - buyerRatios []*FeeRatio + sellerRatios []FeeRatio + buyerRatios []FeeRatio exp string }{ { @@ -406,64 +406,52 @@ func TestValidateFeeRatios(t *testing.T) { { name: "nil empty", sellerRatios: nil, - buyerRatios: []*FeeRatio{}, + buyerRatios: []FeeRatio{}, exp: "", }, { name: "empty nil", - sellerRatios: []*FeeRatio{}, + sellerRatios: []FeeRatio{}, buyerRatios: nil, exp: "", }, { name: "empty empty", - sellerRatios: []*FeeRatio{}, - buyerRatios: []*FeeRatio{}, + sellerRatios: []FeeRatio{}, + buyerRatios: []FeeRatio{}, exp: "", }, - { - name: "nil seller entry", - sellerRatios: []*FeeRatio{nil}, - buyerRatios: nil, - exp: "nil seller fee ratio not allowed", - }, - { - name: "nil buyer entry", - sellerRatios: nil, - buyerRatios: []*FeeRatio{nil}, - exp: "nil buyer fee ratio not allowed", - }, { name: "multiple errors from sellers and buyers", - sellerRatios: []*FeeRatio{ - nil, + sellerRatios: []FeeRatio{ + {Price: coin(1, "fry"), Fee: coin(5, "fry")}, {Price: coin(0, "leela"), Fee: coin(1, "leela")}, }, - buyerRatios: []*FeeRatio{ - nil, + buyerRatios: []FeeRatio{ + {Price: coin(1, "fry"), Fee: coin(5, "fry")}, {Price: coin(0, "leela"), Fee: coin(1, "leela")}, }, exp: joinErrs( - "nil seller fee ratio not allowed", + `seller fee ratio fee amount "5fry" cannot be greater than price amount "1fry"`, `seller fee ratio price amount "0leela" must be positive`, - "nil buyer fee ratio not allowed", + `buyer fee ratio fee amount "5fry" cannot be greater than price amount "1fry"`, `buyer fee ratio price amount "0leela" must be positive`, ), }, { name: "sellers have price denom that buyers do not", - sellerRatios: []*FeeRatio{ + sellerRatios: []FeeRatio{ {Price: coin(100, "leela"), Fee: coin(1, "leela")}, {Price: coin(500, "fry"), Fee: coin(3, "fry")}, }, - buyerRatios: []*FeeRatio{ + buyerRatios: []FeeRatio{ {Price: coin(100, "leela"), Fee: coin(3, "leela")}, }, exp: `denom "fry" is defined in the seller settlement fee ratios but not buyer`, }, { name: "sellers have two price denoms that buyers do not", - sellerRatios: []*FeeRatio{ + sellerRatios: []FeeRatio{ {Price: coin(100, "leela"), Fee: coin(1, "leela")}, {Price: coin(500, "fry"), Fee: coin(3, "fry")}, }, @@ -475,10 +463,10 @@ func TestValidateFeeRatios(t *testing.T) { }, { name: "buyers have price denom that sellers do not", - sellerRatios: []*FeeRatio{ + sellerRatios: []FeeRatio{ {Price: coin(100, "leela"), Fee: coin(3, "leela")}, }, - buyerRatios: []*FeeRatio{ + buyerRatios: []FeeRatio{ {Price: coin(100, "leela"), Fee: coin(1, "leela")}, {Price: coin(500, "fry"), Fee: coin(3, "leela")}, }, @@ -487,7 +475,7 @@ func TestValidateFeeRatios(t *testing.T) { { name: "buyers have two price denoms that sellers do not", sellerRatios: nil, - buyerRatios: []*FeeRatio{ + buyerRatios: []FeeRatio{ {Price: coin(100, "leela"), Fee: coin(1, "leela")}, {Price: coin(500, "fry"), Fee: coin(3, "leela")}, }, @@ -498,11 +486,11 @@ func TestValidateFeeRatios(t *testing.T) { }, { name: "two buyers and two sellers and four price denoms", - sellerRatios: []*FeeRatio{ + sellerRatios: []FeeRatio{ {Price: coin(100, "leela"), Fee: coin(1, "leela")}, {Price: coin(500, "fry"), Fee: coin(3, "fry")}, }, - buyerRatios: []*FeeRatio{ + buyerRatios: []FeeRatio{ {Price: coin(100, "bender"), Fee: coin(1, "leela")}, {Price: coin(3, "professor"), Fee: coin(500, "fry")}, }, @@ -515,12 +503,12 @@ func TestValidateFeeRatios(t *testing.T) { }, { name: "three seller ratios and many buyer ratios all legit", - sellerRatios: []*FeeRatio{ + sellerRatios: []FeeRatio{ {Price: coin(100, "leela"), Fee: coin(1, "leela")}, {Price: coin(500, "fry"), Fee: coin(3, "fry")}, {Price: coin(300, "bender"), Fee: coin(7, "bender")}, }, - buyerRatios: []*FeeRatio{ + buyerRatios: []FeeRatio{ {Price: coin(10, "leela"), Fee: coin(1, "leela")}, {Price: coin(11, "leela"), Fee: coin(2, "fry")}, {Price: coin(12, "leela"), Fee: coin(3, "bender")}, @@ -566,7 +554,7 @@ func TestValidateSellerFeeRatios(t *testing.T) { tests := []struct { name string - ratios []*FeeRatio + ratios []FeeRatio exp string }{ { @@ -576,32 +564,27 @@ func TestValidateSellerFeeRatios(t *testing.T) { }, { name: "empty ratios", - ratios: []*FeeRatio{}, + ratios: []FeeRatio{}, exp: "", }, - { - name: "one ratio: nil", - ratios: []*FeeRatio{nil}, - exp: "nil seller fee ratio not allowed", - }, { name: "one ratio: different denoms", - ratios: []*FeeRatio{{Price: coin(3, "hermes"), Fee: coin(2, "mom")}}, + ratios: []FeeRatio{{Price: coin(3, "hermes"), Fee: coin(2, "mom")}}, exp: `seller fee ratio price denom "hermes" does not equal fee denom "mom"`, }, { name: "one ratio: same denoms", - ratios: []*FeeRatio{{Price: coin(3, "mom"), Fee: coin(2, "mom")}}, + ratios: []FeeRatio{{Price: coin(3, "mom"), Fee: coin(2, "mom")}}, exp: "", }, { name: "one ratio: invalid", - ratios: []*FeeRatio{{Price: coin(0, "hermes"), Fee: coin(2, "hermes")}}, + ratios: []FeeRatio{{Price: coin(0, "hermes"), Fee: coin(2, "hermes")}}, exp: `seller fee ratio price amount "0hermes" must be positive`, }, { name: "two with same denom", - ratios: []*FeeRatio{ + ratios: []FeeRatio{ {Price: coin(3, "hermes"), Fee: coin(2, "hermes")}, {Price: coin(6, "hermes"), Fee: coin(4, "hermes")}, }, @@ -609,7 +592,7 @@ func TestValidateSellerFeeRatios(t *testing.T) { }, { name: "three with diffrent denoms", - ratios: []*FeeRatio{ + ratios: []FeeRatio{ {Price: coin(30, "leela"), Fee: coin(1, "leela")}, {Price: coin(5, "fry"), Fee: coin(1, "fry")}, {Price: coin(100, "professor"), Fee: coin(1, "professor")}, @@ -618,11 +601,10 @@ func TestValidateSellerFeeRatios(t *testing.T) { }, { name: "multiple errors", - ratios: []*FeeRatio{ + ratios: []FeeRatio{ {Price: coin(3, "mom"), Fee: coin(2, "hermes")}, {Price: coin(0, "hermes"), Fee: coin(2, "hermes")}, {Price: coin(6, "bender"), Fee: coin(4, "bender")}, - nil, {Price: coin(1, "hermes"), Fee: coin(2, "hermes")}, {Price: coin(2, "bender"), Fee: coin(1, "bender")}, // This one is ignored because we've already complained about multiple hermes. @@ -631,7 +613,6 @@ func TestValidateSellerFeeRatios(t *testing.T) { exp: joinErrs( `seller fee ratio price denom "mom" does not equal fee denom "hermes"`, `seller fee ratio price amount "0hermes" must be positive`, - "nil seller fee ratio not allowed", `seller fee ratio denom "hermes" appears in multiple ratios`, `seller fee ratio denom "bender" appears in multiple ratios`, ), @@ -666,7 +647,7 @@ func TestValidateBuyerFeeRatios(t *testing.T) { tests := []struct { name string - ratios []*FeeRatio + ratios []FeeRatio exp string }{ { @@ -676,32 +657,27 @@ func TestValidateBuyerFeeRatios(t *testing.T) { }, { name: "empty ratios", - ratios: []*FeeRatio{}, + ratios: []FeeRatio{}, exp: "", }, - { - name: "one ratio: nil", - ratios: []*FeeRatio{nil}, - exp: "nil buyer fee ratio not allowed", - }, { name: "one ratio: different denoms", - ratios: []*FeeRatio{{Price: coin(3, "hermes"), Fee: coin(2, "mom")}}, + ratios: []FeeRatio{{Price: coin(3, "hermes"), Fee: coin(2, "mom")}}, exp: "", }, { name: "one ratio: same denoms", - ratios: []*FeeRatio{{Price: coin(3, "mom"), Fee: coin(2, "mom")}}, + ratios: []FeeRatio{{Price: coin(3, "mom"), Fee: coin(2, "mom")}}, exp: "", }, { name: "one ratio: invalid", - ratios: []*FeeRatio{{Price: coin(0, "hermes"), Fee: coin(2, "hermes")}}, + ratios: []FeeRatio{{Price: coin(0, "hermes"), Fee: coin(2, "hermes")}}, exp: `buyer fee ratio price amount "0hermes" must be positive`, }, { name: "duplicate ratio denoms", - ratios: []*FeeRatio{ + ratios: []FeeRatio{ {Price: coin(10, "morbo"), Fee: coin(2, "scruffy")}, {Price: coin(3, "morbo"), Fee: coin(1, "scruffy")}, }, @@ -709,7 +685,7 @@ func TestValidateBuyerFeeRatios(t *testing.T) { }, { name: "two ratios one each way", - ratios: []*FeeRatio{ + ratios: []FeeRatio{ {Price: coin(10, "leela"), Fee: coin(2, "scruffy")}, {Price: coin(2, "scruffy"), Fee: coin(8, "leela")}, }, @@ -717,11 +693,10 @@ func TestValidateBuyerFeeRatios(t *testing.T) { }, { name: "multiple errors", - ratios: []*FeeRatio{ + ratios: []FeeRatio{ {Price: coin(10, "morbo"), Fee: coin(2, "scruffy")}, {Price: coin(0, "zoidberg"), Fee: coin(1, "amy")}, {Price: coin(1, "hermes"), Fee: coin(2, "hermes")}, - nil, {Price: coin(3, "morbo"), Fee: coin(1, "scruffy")}, {Price: coin(0, "zoidberg"), Fee: coin(1, "amy")}, // This one has a different fee denom, though, so it's checked. @@ -732,7 +707,6 @@ func TestValidateBuyerFeeRatios(t *testing.T) { exp: joinErrs( `buyer fee ratio price amount "0zoidberg" must be positive`, `buyer fee ratio fee amount "2hermes" cannot be greater than price amount "1hermes"`, - "nil buyer fee ratio not allowed", `buyer fee ratio pair "morbo" to "scruffy" appears in multiple ratios`, `buyer fee ratio pair "zoidberg" to "amy" appears in multiple ratios`, `buyer fee ratio fee amount "-1fry" cannot be negative`, @@ -740,7 +714,7 @@ func TestValidateBuyerFeeRatios(t *testing.T) { }, { name: "two different price denoms to several fee denoms", - ratios: []*FeeRatio{ + ratios: []FeeRatio{ {Price: coin(100, "fry"), Fee: coin(1, "fry")}, {Price: coin(1000, "fry"), Fee: coin(1, "professor")}, {Price: coin(1, "fry"), Fee: coin(1, "leela")}, From 07f86a8902c305fdd024bbb9af3669c2d770c2d8 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 29 Aug 2023 15:23:49 -0600 Subject: [PATCH 040/309] [1658]: Update ValidateFeeOptions to return all errors instead of the first. Write some new helpers that'll be useful for some msg validation that's coming soon. --- x/exchange/market.go | 87 ++++- x/exchange/market_test.go | 688 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 749 insertions(+), 26 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index 6a46e03d41..7a07391737 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -56,17 +56,31 @@ func (m Market) Validate() error { return errors.Join(errs...) } +// ValidateFeeOptions returns an error if any of the provide coin values is not a valid fee option. func ValidateFeeOptions(field string, options []sdk.Coin) error { + var errs []error + denoms := make(map[string]bool) + dups := make(map[string]bool) for _, coin := range options { + if denoms[coin.Denom] { + if !dups[coin.Denom] { + errs = append(errs, fmt.Errorf("invalid %s option %q: denom used in multiple entries", field, coin)) + dups[coin.Denom] = true + } + continue + } + denoms[coin.Denom] = true + err := coin.Validate() if err != nil { - return fmt.Errorf("invalid %s option %q: %w", field, coin, err) + errs = append(errs, fmt.Errorf("invalid %s option %q: %w", field, coin, err)) + continue } if coin.IsZero() { - return fmt.Errorf("invalid %s option %q: amount cannot be zero", field, coin) + errs = append(errs, fmt.Errorf("invalid %s option %q: amount cannot be zero", field, coin)) } } - return nil + return errors.Join(errs...) } // Validate returns an error if anything in this MarketDetails is invalid. @@ -206,6 +220,15 @@ func (r FeeRatio) String() string { return fmt.Sprintf("%s:%s", r.Price, r.Fee) } +// FeeRatiosString converts the provided ratios into a single string with format ,,... +func FeeRatiosString(ratios []FeeRatio) string { + entries := make([]string, len(ratios)) + for i, ratio := range ratios { + entries[i] = ratio.String() + } + return strings.Join(entries, ",") +} + // Validate returns an error if this FeeRatio is invalid. func (r FeeRatio) Validate() error { if !r.Price.Amount.IsPositive() { @@ -220,6 +243,64 @@ func (r FeeRatio) Validate() error { return nil } +// Equals returns true if this FeeRatio has the same price and fee as the provided other FeeRatio. +func (r FeeRatio) Equals(other FeeRatio) bool { + // Cannot use coin.IsEqual because it panics if the denoms are different, because that makes perfect sense. + // The coin.Equal(interface{}) function behaves as expected, though, but with the extra casting costs. + return r.Price.Equal(other.Price) && r.Fee.Equal(other.Fee) +} + +// IntersectionOfFeeRatios returns all FeeRatios that are in both lists. +func IntersectionOfFeeRatios(ratios1, ratios2 []FeeRatio) []FeeRatio { + var rv []FeeRatio + for _, r1 := range ratios1 { + for _, r2 := range ratios2 { + if r1.Equals(r2) { + rv = append(rv, r1) + break + } + } + } + return rv +} + +// ValidateDisjointFeeRatios returns an error if one or more entries appears in both lists. +func ValidateDisjointFeeRatios(field string, toAdd, toRemove []FeeRatio) error { + shared := IntersectionOfFeeRatios(toAdd, toRemove) + if len(shared) > 0 { + return fmt.Errorf("cannot add and remove the same %s ratios: %s", field, FeeRatiosString(shared)) + } + return nil +} + +// IntersectionOfFeeOptions returns all Coin options that are in both lists. +func IntersectionOfFeeOptions(options1, options2 []sdk.Coin) []sdk.Coin { + var rv []sdk.Coin + for _, c1 := range options1 { + for _, c2 := range options2 { + if c1.Equal(c2) { + rv = append(rv, c1) + break + } + } + } + return rv +} + +// ValidateAddRemoveFeeOptions returns an error if the toAdd list has an invalid +// entry or if the two lists have one or more common entries. +func ValidateAddRemoveFeeOptions(field string, toAdd, toRemove []sdk.Coin) error { + var errs []error + if err := ValidateFeeOptions(field+" to add", toAdd); err != nil { + errs = append(errs, err) + } + shared := IntersectionOfFeeOptions(toAdd, toRemove) + if len(shared) > 0 { + errs = append(errs, fmt.Errorf("cannot add and remove the same %s options: %s", field, sdk.Coins(shared))) + } + return errors.Join(errs...) +} + // ValidateAccessGrants returns an error if any of the provided access grants are invalid. func ValidateAccessGrants(accessGrants []*AccessGrant) error { errs := make([]error, len(accessGrants)) diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 76b03a0c68..88b1054a88 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -186,6 +186,9 @@ func TestMarket_Validate(t *testing.T) { } func TestValidateFeeOptions(t *testing.T) { + joinErrs := func(errs ...string) string { + return strings.Join(errs, "\n") + } coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } @@ -252,9 +255,32 @@ func TestValidateFeeOptions(t *testing.T) { }, { name: "three options: bad third", - field: "eyeballs", + field: "pinky", options: []sdk.Coin{coin(5, "fry"), coin(2, "leela"), coin(0, "farnsworth")}, - expErr: `invalid eyeballs option "0farnsworth": amount cannot be zero`, + expErr: `invalid pinky option "0farnsworth": amount cannot be zero`, + }, + { + name: "duplicated denoms", + field: "duppa-duppa-dup", + options: []sdk.Coin{ + coin(1, "fry"), coin(1, "leela"), + coin(1, "amy"), coin(2, "amy"), + coin(1, "farnsworth"), + coin(2, "fry"), coin(3, "fry")}, + expErr: joinErrs( + `invalid duppa-duppa-dup option "2amy": denom used in multiple entries`, + `invalid duppa-duppa-dup option "2fry": denom used in multiple entries`, + ), + }, + { + name: "three options: all bad", + field: "carp corp", + options: []sdk.Coin{coin(0, "fry"), coin(1, "l"), coin(-12, "farnsworth")}, + expErr: joinErrs( + `invalid carp corp option "0fry": amount cannot be zero`, + `invalid carp corp option "1l": invalid denom: l`, + `invalid carp corp option "-12farnsworth": negative coin amount: -12`, + ), }, } @@ -277,9 +303,7 @@ func TestValidateFeeOptions(t *testing.T) { } func TestMarketDetails_Validate(t *testing.T) { - joinErrs := func(errs ...string) string { - return strings.Join(errs, "\n") - } + nameErr := func(over int) string { return fmt.Sprintf("name length %d exceeds maximum length of %d", MaxName+over, MaxName) } @@ -361,7 +385,7 @@ func TestMarketDetails_Validate(t *testing.T) { WebsiteUrl: strings.Repeat("W", MaxWebsiteURL+4), IconUri: strings.Repeat("I", MaxIconURI+5), }, - expErr: joinErrs(nameErr(2), descErr(3), urlErr(4), iconErr(5)), + expErr: nameErr(2) + "\n" + descErr(3) + "\n" + urlErr(4) + "\n" + iconErr(5), }, } @@ -548,9 +572,6 @@ func TestValidateSellerFeeRatios(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } - joinErrs := func(errs ...string) string { - return strings.Join(errs, "\n") - } tests := []struct { name string @@ -610,12 +631,10 @@ func TestValidateSellerFeeRatios(t *testing.T) { // This one is ignored because we've already complained about multiple hermes. {Price: coin(30, "hermes"), Fee: coin(2, "hermes")}, }, - exp: joinErrs( - `seller fee ratio price denom "mom" does not equal fee denom "hermes"`, - `seller fee ratio price amount "0hermes" must be positive`, - `seller fee ratio denom "hermes" appears in multiple ratios`, + exp: `seller fee ratio price denom "mom" does not equal fee denom "hermes"` + "\n" + + `seller fee ratio price amount "0hermes" must be positive` + "\n" + + `seller fee ratio denom "hermes" appears in multiple ratios` + "\n" + `seller fee ratio denom "bender" appears in multiple ratios`, - ), }, } @@ -641,9 +660,6 @@ func TestValidateBuyerFeeRatios(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } - joinErrs := func(errs ...string) string { - return strings.Join(errs, "\n") - } tests := []struct { name string @@ -704,13 +720,11 @@ func TestValidateBuyerFeeRatios(t *testing.T) { // We've already complained about this one, so it doesn't happen again. {Price: coin(12, "zoidberg"), Fee: coin(55, "amy")}, }, - exp: joinErrs( - `buyer fee ratio price amount "0zoidberg" must be positive`, - `buyer fee ratio fee amount "2hermes" cannot be greater than price amount "1hermes"`, - `buyer fee ratio pair "morbo" to "scruffy" appears in multiple ratios`, - `buyer fee ratio pair "zoidberg" to "amy" appears in multiple ratios`, + exp: `buyer fee ratio price amount "0zoidberg" must be positive` + "\n" + + `buyer fee ratio fee amount "2hermes" cannot be greater than price amount "1hermes"` + "\n" + + `buyer fee ratio pair "morbo" to "scruffy" appears in multiple ratios` + "\n" + + `buyer fee ratio pair "zoidberg" to "amy" appears in multiple ratios` + "\n" + `buyer fee ratio fee amount "-1fry" cannot be negative`, - ), }, { name: "two different price denoms to several fee denoms", @@ -785,6 +799,59 @@ func TestFeeRatio_String(t *testing.T) { } } +func TestFeeRatiosString(t *testing.T) { + feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { + return FeeRatio{ + Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(priceAmount)}, + Fee: sdk.Coin{Denom: feeDenom, Amount: sdkmath.NewInt(feeAmount)}, + } + } + + tests := []struct { + name string + ratios []FeeRatio + exp string + }{ + {name: "nil", ratios: nil, exp: ""}, + {name: "empty", ratios: []FeeRatio{}, exp: ""}, + { + name: "one entry", + ratios: []FeeRatio{feeRatio(1, "pdenom", 2, "fdenom")}, + exp: "1pdenom:2fdenom", + }, + { + name: "two entries", + ratios: []FeeRatio{ + feeRatio(1, "pdenom", 2, "fdenom"), + feeRatio(3, "qdenom", 4, "gdenom"), + }, + exp: "1pdenom:2fdenom,3qdenom:4gdenom", + }, + { + name: "five entries", + ratios: []FeeRatio{ + feeRatio(1, "a", 2, "b"), + feeRatio(3, "c", 4, "d"), + feeRatio(5, "e", 6, "f"), + feeRatio(7, "g", 8, "h"), + feeRatio(9, "i", 10, "j"), + }, + exp: "1a:2b,3c:4d,5e:6f,7g:8h,9i:10j", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = FeeRatiosString(tc.ratios) + } + require.NotPanics(t, testFunc, "FeeRatiosString") + assert.Equal(t, tc.exp, actual, "FeeRatiosString result") + }) + } +} + func TestFeeRatio_Validate(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} @@ -856,6 +923,581 @@ func TestFeeRatio_Validate(t *testing.T) { } } +func TestFeeRatio_Equals(t *testing.T) { + feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { + return FeeRatio{ + Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(priceAmount)}, + Fee: sdk.Coin{Denom: feeDenom, Amount: sdkmath.NewInt(feeAmount)}, + } + } + + tests := []struct { + name string + base FeeRatio + other FeeRatio + exp bool + }{ + { + name: "empty ratios", + base: FeeRatio{}, + other: FeeRatio{}, + exp: true, + }, + { + name: "zero amounts", + base: feeRatio(0, "pdenom", 0, "fdenom"), + other: feeRatio(0, "pdenom", 0, "fdenom"), + exp: true, + }, + { + name: "same price and fee", + base: feeRatio(1, "pdenom", 2, "fdenom"), + other: feeRatio(1, "pdenom", 2, "fdenom"), + exp: true, + }, + { + name: "different base price amount", + base: feeRatio(3, "pdenom", 2, "fdenom"), + other: feeRatio(1, "pdenom", 2, "fdenom"), + exp: false, + }, + { + name: "different base price denom", + base: feeRatio(1, "qdenom", 2, "fdenom"), + other: feeRatio(1, "pdenom", 2, "fdenom"), + exp: false, + }, + { + name: "different base fee amount", + base: feeRatio(1, "pdenom", 3, "fdenom"), + other: feeRatio(1, "pdenom", 2, "fdenom"), + exp: false, + }, + { + name: "different base fee denom", + base: feeRatio(1, "pdenom", 2, "gdenom"), + other: feeRatio(1, "pdenom", 2, "fdenom"), + exp: false, + }, + { + name: "different other price amount", + base: feeRatio(1, "pdenom", 2, "fdenom"), + other: feeRatio(3, "pdenom", 2, "fdenom"), + exp: false, + }, + { + name: "different other price denom", + base: feeRatio(1, "pdenom", 2, "fdenom"), + other: feeRatio(1, "qdenom", 2, "fdenom"), + exp: false, + }, + { + name: "different other fee amount", + base: feeRatio(1, "pdenom", 2, "fdenom"), + other: feeRatio(1, "pdenom", 3, "fdenom"), + exp: false, + }, + { + name: "different other fee denom", + base: feeRatio(1, "pdenom", 2, "fdenom"), + other: feeRatio(1, "pdenom", 2, "gdenom"), + exp: false, + }, + { + name: "negative base price amount", + base: feeRatio(-1, "pdenom", 2, "fdenom"), + other: feeRatio(1, "pdenom", 2, "fdenom"), + exp: false, + }, + { + name: "negative other price amount", + base: feeRatio(1, "pdenom", 2, "fdenom"), + other: feeRatio(-1, "pdenom", 2, "fdenom"), + exp: false, + }, + { + name: "negative base fee amount", + base: feeRatio(1, "pdenom", -2, "fdenom"), + other: feeRatio(1, "pdenom", 2, "fdenom"), + exp: false, + }, + { + name: "negative other fee amount", + base: feeRatio(1, "pdenom", 2, "fdenom"), + other: feeRatio(1, "pdenom", -2, "fdenom"), + exp: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.base.Equals(tc.other) + } + require.NotPanics(t, testFunc, "%s.Equals(%s)", tc.base, tc.other) + assert.Equal(t, tc.exp, actual, "%s.Equals(%s) result", tc.base, tc.other) + }) + } +} + +func TestIntersectionOfFeeRatios(t *testing.T) { + feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { + return FeeRatio{ + Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(priceAmount)}, + Fee: sdk.Coin{Denom: feeDenom, Amount: sdkmath.NewInt(feeAmount)}, + } + } + + tests := []struct { + name string + ratios1 []FeeRatio + ratios2 []FeeRatio + expected []FeeRatio + }{ + {name: "nil nil", ratios1: nil, ratios2: nil, expected: nil}, + {name: "nil empty", ratios1: nil, ratios2: []FeeRatio{}, expected: nil}, + {name: "empty nil", ratios1: []FeeRatio{}, ratios2: nil, expected: nil}, + {name: "empty empty", ratios1: []FeeRatio{}, ratios2: []FeeRatio{}, expected: nil}, + { + name: "one nil", + ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + ratios2: nil, + expected: nil, + }, + { + name: "nil one", + ratios1: nil, + ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + expected: nil, + }, + { + name: "one one equal", + ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + expected: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + }, + { + name: "one one diff first price amount", + ratios1: []FeeRatio{feeRatio(3, "spicy", 2, "lemon")}, + ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + expected: nil, + }, + { + name: "one one diff first price denom", + ratios1: []FeeRatio{feeRatio(1, "bland", 2, "lemon")}, + ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + expected: nil, + }, + { + name: "one one diff first fee amount", + ratios1: []FeeRatio{feeRatio(1, "spicy", 3, "lemon")}, + ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + expected: nil, + }, + { + name: "one one diff first fee denom", + ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "grape")}, + ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + expected: nil, + }, + { + name: "one one diff second price amount", + ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + ratios2: []FeeRatio{feeRatio(3, "spicy", 2, "lemon")}, + expected: nil, + }, + { + name: "one one diff second price denom", + ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + ratios2: []FeeRatio{feeRatio(1, "bland", 2, "lemon")}, + expected: nil, + }, + { + name: "one one diff second fee amount", + ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + ratios2: []FeeRatio{feeRatio(1, "spicy", 3, "lemon")}, + expected: nil, + }, + { + name: "one one diff second fee denom", + ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "bland")}, + expected: nil, + }, + { + name: "three three two common", + ratios1: []FeeRatio{ + feeRatio(1, "lamp", 2, "bag"), + feeRatio(3, "keys", 4, "phone"), + feeRatio(5, "fan", 6, "bottle"), + }, + ratios2: []FeeRatio{ + feeRatio(3, "kays", 4, "phone"), + feeRatio(5, "fan", 6, "bottle"), + feeRatio(1, "lamp", 2, "bag"), + }, + expected: []FeeRatio{ + feeRatio(1, "lamp", 2, "bag"), + feeRatio(5, "fan", 6, "bottle"), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual []FeeRatio + testFunc := func() { + actual = IntersectionOfFeeRatios(tc.ratios1, tc.ratios2) + } + require.NotPanics(t, testFunc, "IntersectionOfFeeRatios") + assert.Equal(t, tc.expected, actual, "IntersectionOfFeeRatios result") + }) + } +} + +func TestValidateDisjointFeeRatios(t *testing.T) { + feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { + return FeeRatio{ + Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(priceAmount)}, + Fee: sdk.Coin{Denom: feeDenom, Amount: sdkmath.NewInt(feeAmount)}, + } + } + + tests := []struct { + name string + field string + toAdd []FeeRatio + toRemove []FeeRatio + expErr string + }{ + {name: "nil nil", toAdd: nil, toRemove: nil, expErr: ""}, + {name: "nil empty", toAdd: nil, toRemove: []FeeRatio{}, expErr: ""}, + {name: "empty nil", toAdd: []FeeRatio{}, toRemove: nil, expErr: ""}, + {name: "empty empty", toAdd: []FeeRatio{}, toRemove: []FeeRatio{}, expErr: ""}, + { + name: "one nil", + toAdd: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + toRemove: nil, + expErr: "", + }, + { + name: "nil one", + toAdd: nil, + toRemove: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + expErr: "", + }, + { + name: "one one equal", + field: "", + toAdd: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + toRemove: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + expErr: "cannot add and remove the same ratios: 1spicy:2lemon", + }, + { + name: "one one diff first price amount", + toAdd: []FeeRatio{feeRatio(3, "spicy", 2, "lemon")}, + toRemove: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + expErr: "", + }, + { + name: "one one diff first price denom", + toAdd: []FeeRatio{feeRatio(1, "bland", 2, "lemon")}, + toRemove: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + expErr: "", + }, + { + name: "one one diff first fee amount", + toAdd: []FeeRatio{feeRatio(1, "spicy", 3, "lemon")}, + toRemove: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + expErr: "", + }, + { + name: "one one diff first fee denom", + toAdd: []FeeRatio{feeRatio(1, "spicy", 2, "grape")}, + toRemove: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + expErr: "", + }, + { + name: "one one diff second price amount", + toAdd: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + toRemove: []FeeRatio{feeRatio(3, "spicy", 2, "lemon")}, + expErr: "", + }, + { + name: "one one diff second price denom", + toAdd: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + toRemove: []FeeRatio{feeRatio(1, "bland", 2, "lemon")}, + expErr: "", + }, + { + name: "one one diff second fee amount", + toAdd: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + toRemove: []FeeRatio{feeRatio(1, "spicy", 3, "lemon")}, + expErr: "", + }, + { + name: "one one diff second fee denom", + toAdd: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + toRemove: []FeeRatio{feeRatio(1, "spicy", 2, "bland")}, + expErr: "", + }, + { + name: "three three two common", + field: "test field name", + toAdd: []FeeRatio{ + feeRatio(1, "lamp", 2, "bag"), + feeRatio(3, "keys", 4, "phone"), + feeRatio(5, "fan", 6, "bottle"), + }, + toRemove: []FeeRatio{ + feeRatio(3, "kays", 4, "phone"), + feeRatio(5, "fan", 6, "bottle"), + feeRatio(1, "lamp", 2, "bag"), + }, + expErr: "cannot add and remove the same test field name ratios: 1lamp:2bag,5fan:6bottle", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = ValidateDisjointFeeRatios(tc.field, tc.toAdd, tc.toRemove) + } + require.NotPanics(t, testFunc, "ValidateDisjointFeeRatios") + + // TODO[1658]: Replace with testutils.AsserErrorValue(t, err, tc.expErr, "ValidateDisjointFeeRatios") + if len(tc.expErr) > 0 { + assert.EqualError(t, err, tc.expErr, "ValidateDisjointFeeRatios") + } else { + assert.NoError(t, err, "ValidateDisjointFeeRatios") + } + }) + } +} + +func TestIntersectionOfFeeOptions(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + options1 []sdk.Coin + options2 []sdk.Coin + expected []sdk.Coin + }{ + {name: "nil nil", options1: nil, options2: nil, expected: nil}, + {name: "nil empty", options1: nil, options2: []sdk.Coin{}, expected: nil}, + {name: "empty nil", options1: []sdk.Coin{}, options2: nil, expected: nil}, + {name: "empty empty", options1: []sdk.Coin{}, options2: []sdk.Coin{}, expected: nil}, + { + name: "one nil", + options1: []sdk.Coin{coin(1, "finger")}, + options2: nil, + expected: nil, + }, + { + name: "nil one", + options1: nil, + options2: []sdk.Coin{coin(1, "finger")}, + expected: nil, + }, + { + name: "one one same", + options1: []sdk.Coin{coin(1, "finger")}, + options2: []sdk.Coin{coin(1, "finger")}, + expected: []sdk.Coin{coin(1, "finger")}, + }, + { + name: "one one different first amount", + options1: []sdk.Coin{coin(2, "finger")}, + options2: []sdk.Coin{coin(1, "finger")}, + expected: nil, + }, + { + name: "one one different first denom", + options1: []sdk.Coin{coin(1, "toe")}, + options2: []sdk.Coin{coin(1, "finger")}, + expected: nil, + }, + { + name: "one one different second amount", + options1: []sdk.Coin{coin(1, "finger")}, + options2: []sdk.Coin{coin(2, "finger")}, + expected: nil, + }, + { + name: "one one different second denom", + options1: []sdk.Coin{coin(1, "finger")}, + options2: []sdk.Coin{coin(1, "toe")}, + expected: nil, + }, + { + name: "three three two common", + options1: []sdk.Coin{coin(1, "finger"), coin(2, "toe"), coin(3, "elbow")}, + options2: []sdk.Coin{coin(5, "toe"), coin(3, "elbow"), coin(1, "finger")}, + expected: []sdk.Coin{coin(1, "finger"), coin(3, "elbow")}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual []sdk.Coin + testFunc := func() { + actual = IntersectionOfFeeOptions(tc.options1, tc.options2) + } + require.NotPanics(t, testFunc, "IntersectionOfFeeOptions") + assert.Equal(t, tc.expected, actual, "IntersectionOfFeeOptions result") + }) + } +} + +func TestValidateAddRemoveFeeOptions(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + joinErrs := func(errs ...string) string { + return strings.Join(errs, "\n") + } + + tests := []struct { + name string + field string + toAdd []sdk.Coin + toRemove []sdk.Coin + expErr string + }{ + {name: "nil nil", toAdd: nil, toRemove: nil, expErr: ""}, + {name: "nil empty", toAdd: nil, toRemove: []sdk.Coin{}, expErr: ""}, + {name: "empty nil", toAdd: []sdk.Coin{}, toRemove: nil, expErr: ""}, + {name: "empty empty", toAdd: []sdk.Coin{}, toRemove: []sdk.Coin{}, expErr: ""}, + { + name: "one nil", + toAdd: []sdk.Coin{coin(1, "finger")}, + toRemove: nil, + expErr: "", + }, + { + name: "nil one", + toAdd: nil, + toRemove: []sdk.Coin{coin(1, "finger")}, + expErr: "", + }, + { + name: "one one same", + field: "", + toAdd: []sdk.Coin{coin(1, "finger")}, + toRemove: []sdk.Coin{coin(1, "finger")}, + expErr: "cannot add and remove the same options: 1finger", + }, + { + name: "one one different first amount", + toAdd: []sdk.Coin{coin(2, "finger")}, + toRemove: []sdk.Coin{coin(1, "finger")}, + expErr: "", + }, + { + name: "one one different first denom", + toAdd: []sdk.Coin{coin(1, "toe")}, + toRemove: []sdk.Coin{coin(1, "finger")}, + expErr: "", + }, + { + name: "one one different second amount", + toAdd: []sdk.Coin{coin(1, "finger")}, + toRemove: []sdk.Coin{coin(2, "finger")}, + expErr: "", + }, + { + name: "one one different second denom", + toAdd: []sdk.Coin{coin(1, "finger")}, + toRemove: []sdk.Coin{coin(1, "toe")}, + expErr: "", + }, + { + name: "three three two common", + field: "body part", + toAdd: []sdk.Coin{coin(1, "finger"), coin(2, "toe"), coin(3, "elbow")}, + toRemove: []sdk.Coin{coin(5, "toe"), coin(3, "elbow"), coin(1, "finger")}, + expErr: "cannot add and remove the same body part options: 1finger,3elbow", + }, + { + name: "adding one invalid", + field: "badabadabada", + toAdd: []sdk.Coin{coin(0, "zerocoin")}, + toRemove: nil, + expErr: `invalid badabadabada to add option "0zerocoin": amount cannot be zero`, + }, + { + name: "adding two invalid entries", + field: "friendly iterative error lookup device (F.I.E.L.D)", + toAdd: []sdk.Coin{coin(-1, "bananas"), coin(3, "okaycoin"), coin(0, "zerocoin"), coin(5, "goodgood")}, + toRemove: nil, + expErr: joinErrs( + `invalid friendly iterative error lookup device (F.I.E.L.D) to add option "-1bananas": negative coin amount: -1`, + `invalid friendly iterative error lookup device (F.I.E.L.D) to add option "0zerocoin": amount cannot be zero`, + ), + }, + { + name: "removing one invalid", + field: "", + toAdd: nil, + toRemove: []sdk.Coin{coin(-1, "bananas")}, + expErr: "", + }, + { + name: "adding and removing one invalid", + field: "fruits", + toAdd: []sdk.Coin{coin(-1, "bananas")}, + toRemove: []sdk.Coin{coin(-1, "bananas")}, + expErr: joinErrs( + `invalid fruits to add option "-1bananas": negative coin amount: -1`, + "cannot add and remove the same fruits options: -1bananas", + ), + }, + { + name: "multiple errors", + field: "merrs!", + toAdd: []sdk.Coin{ + coin(1, "apple"), + coin(3, "l"), + coin(99, "banana"), + coin(-55, "peach"), + coin(14, "copycoin"), + coin(5, "grape"), + }, + toRemove: []sdk.Coin{ + coin(98, "banana"), + coin(14, "copycoin"), + }, + expErr: joinErrs( + `invalid merrs! to add option "3l": invalid denom: l`, + `invalid merrs! to add option "-55peach": negative coin amount: -55`, + "cannot add and remove the same merrs! options: 14copycoin", + ), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = ValidateAddRemoveFeeOptions(tc.field, tc.toAdd, tc.toRemove) + } + require.NotPanics(t, testFunc, "ValidateAddRemoveFeeOptions") + + // TODO[1658]: Refactor to testutils.AssertErrorValue(t, err, tc.expErr, "ValidateAddRemoveFeeOptions") + if len(tc.expErr) > 0 { + assert.EqualError(t, err, tc.expErr, "ValidateAddRemoveFeeOptions") + } else { + assert.NoError(t, err, "ValidateAddRemoveFeeOptions") + } + }) + } +} + func TestValidateAccessGrants(t *testing.T) { joinErrs := func(errs ...string) string { return strings.Join(errs, "\n") From a4b97ed11a21da0c3433ab4a25b240e86edc5f17 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 29 Aug 2023 15:31:32 -0600 Subject: [PATCH 041/309] [1658]: Keep the fee options as []sdk.Coin instead of casting them to an sdk.Coins since they're separate things. --- proto/provenance/exchange/v1/market.proto | 12 +- proto/provenance/exchange/v1/tx.proto | 24 +-- x/exchange/market.pb.go | 145 +++++++++--------- x/exchange/tx.pb.go | 177 +++++++++++----------- 4 files changed, 170 insertions(+), 188 deletions(-) diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index cac7c49f32..d74b179346 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -49,26 +49,22 @@ message Market { // fee_create_ask_flat is the flat fee charged for creating an ask order. // Each coin entry is a separate option. When an ask is created, one of these must be paid. // If empty, no fee is required to create an ask order. - repeated cosmos.base.v1beta1.Coin fee_create_ask_flat = 3 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin fee_create_ask_flat = 3 [(gogoproto.nullable) = false]; // fee_create_bid_flat is the flat fee charged for creating a bid order. // Each coin entry is a separate option. When a bid is created, one of these must be paid. // If empty, no fee is required to create a bid order. - repeated cosmos.base.v1beta1.Coin fee_create_bid_flat = 4 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin fee_create_bid_flat = 4 [(gogoproto.nullable) = false]; // fee_settlement_seller_flat is the flat fee charged to the seller during settlement. // Each coin entry is a separate option. // When an ask is settled, the seller will pay the amount in the denom that matches the price they received. - repeated cosmos.base.v1beta1.Coin fee_settlement_seller_flat = 5 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin fee_settlement_seller_flat = 5 [(gogoproto.nullable) = false]; // fee_settlement_seller_ratios is the fee to charge a seller during settlement based on the price they are receiving. // The price and fee denoms must be equal for each entry, and only one entry for any given denom is allowed. repeated FeeRatio fee_settlement_seller_ratios = 6 [(gogoproto.nullable) = false]; // fee_settlement_buyer_flat is the flat fee charged to the buyer during settlement. // Each coin entry is a separate option. // When a bid is created, the settlement fees provided must contain one of these. - repeated cosmos.base.v1beta1.Coin fee_settlement_buyer_flat = 7 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin fee_settlement_buyer_flat = 7 [(gogoproto.nullable) = false]; // fee_settlement_buyer_ratios is the fee to charge a buyer during settlement based on the price they are spending. // The price and fee denoms do not have to equal. Multiple entries for any given price or fee denom are allowed, but // each price denom to fee denom pair can only have one entry. diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 599da0159e..554ca3c4c3 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -181,28 +181,22 @@ message MsgGovManageFeesRequest { string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // add_fee_create_ask_flat are the create ask flat fee options to add. - repeated cosmos.base.v1beta1.Coin add_fee_create_ask_flat = 2 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin add_fee_create_ask_flat = 2 [(gogoproto.nullable) = false]; // remove_fee_create_ask_flat are the create ask flat fee options to remove. - repeated cosmos.base.v1beta1.Coin remove_fee_create_ask_flat = 3 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin remove_fee_create_ask_flat = 3 [(gogoproto.nullable) = false]; // add_fee_create_bid_flat are the create bid flat fee options to add. - repeated cosmos.base.v1beta1.Coin add_fee_create_bid_flat = 4 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin add_fee_create_bid_flat = 4 [(gogoproto.nullable) = false]; // remove_fee_create_bid_flat are the create bid flat fee options to remove. - repeated cosmos.base.v1beta1.Coin remove_fee_create_bid_flat = 5 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin remove_fee_create_bid_flat = 5 [(gogoproto.nullable) = false]; // add_fee_settlement_seller_flat are the seller settlement flat fee options to add. - repeated cosmos.base.v1beta1.Coin add_fee_settlement_seller_flat = 6 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin add_fee_settlement_seller_flat = 6 [(gogoproto.nullable) = false]; // remove_fee_settlement_seller_flat are the seller settlement flat fee options to remove. - repeated cosmos.base.v1beta1.Coin remove_fee_settlement_seller_flat = 7 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin remove_fee_settlement_seller_flat = 7 [(gogoproto.nullable) = false]; // add_fee_settlement_seller_ratios are the seller settlement fee ratios to add. repeated FeeRatio add_fee_settlement_seller_ratios = 8 [(gogoproto.nullable) = false]; @@ -211,12 +205,10 @@ message MsgGovManageFeesRequest { repeated FeeRatio remove_fee_settlement_seller_ratios = 9 [(gogoproto.nullable) = false]; // add_fee_settlement_buyer_flat are the buyer settlement flat fee options to add. - repeated cosmos.base.v1beta1.Coin add_fee_settlement_buyer_flat = 10 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin add_fee_settlement_buyer_flat = 10 [(gogoproto.nullable) = false]; // remove_fee_settlement_buyer_flat are the buyer settlement flat fee options to remove. - repeated cosmos.base.v1beta1.Coin remove_fee_settlement_buyer_flat = 11 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin remove_fee_settlement_buyer_flat = 11 [(gogoproto.nullable) = false]; // add_fee_settlement_buyer_ratios are the buyer settlement fee ratios to add. repeated FeeRatio add_fee_settlement_buyer_ratios = 12 [(gogoproto.nullable) = false]; diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index db8c42c2d7..0ef6a9a410 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -6,7 +6,6 @@ package exchange import ( fmt "fmt" _ "github.com/cosmos/cosmos-proto" - github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types1 "github.com/cosmos/cosmos-sdk/types" types "github.com/cosmos/cosmos-sdk/x/auth/types" _ "github.com/gogo/protobuf/gogoproto" @@ -199,22 +198,22 @@ type Market struct { // fee_create_ask_flat is the flat fee charged for creating an ask order. // Each coin entry is a separate option. When an ask is created, one of these must be paid. // If empty, no fee is required to create an ask order. - FeeCreateAskFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=fee_create_ask_flat,json=feeCreateAskFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"fee_create_ask_flat"` + FeeCreateAskFlat []types1.Coin `protobuf:"bytes,3,rep,name=fee_create_ask_flat,json=feeCreateAskFlat,proto3" json:"fee_create_ask_flat"` // fee_create_bid_flat is the flat fee charged for creating a bid order. // Each coin entry is a separate option. When a bid is created, one of these must be paid. // If empty, no fee is required to create a bid order. - FeeCreateBidFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=fee_create_bid_flat,json=feeCreateBidFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"fee_create_bid_flat"` + FeeCreateBidFlat []types1.Coin `protobuf:"bytes,4,rep,name=fee_create_bid_flat,json=feeCreateBidFlat,proto3" json:"fee_create_bid_flat"` // fee_settlement_seller_flat is the flat fee charged to the seller during settlement. // Each coin entry is a separate option. // When an ask is settled, the seller will pay the amount in the denom that matches the price they received. - FeeSettlementSellerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,5,rep,name=fee_settlement_seller_flat,json=feeSettlementSellerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"fee_settlement_seller_flat"` + FeeSettlementSellerFlat []types1.Coin `protobuf:"bytes,5,rep,name=fee_settlement_seller_flat,json=feeSettlementSellerFlat,proto3" json:"fee_settlement_seller_flat"` // fee_settlement_seller_ratios is the fee to charge a seller during settlement based on the price they are receiving. // The price and fee denoms must be equal for each entry, and only one entry for any given denom is allowed. FeeSettlementSellerRatios []FeeRatio `protobuf:"bytes,6,rep,name=fee_settlement_seller_ratios,json=feeSettlementSellerRatios,proto3" json:"fee_settlement_seller_ratios"` // fee_settlement_buyer_flat is the flat fee charged to the buyer during settlement. // Each coin entry is a separate option. // When a bid is created, the settlement fees provided must contain one of these. - FeeSettlementBuyerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,7,rep,name=fee_settlement_buyer_flat,json=feeSettlementBuyerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"fee_settlement_buyer_flat"` + FeeSettlementBuyerFlat []types1.Coin `protobuf:"bytes,7,rep,name=fee_settlement_buyer_flat,json=feeSettlementBuyerFlat,proto3" json:"fee_settlement_buyer_flat"` // fee_settlement_buyer_ratios is the fee to charge a buyer during settlement based on the price they are spending. // The price and fee denoms do not have to equal. Multiple entries for any given price or fee denom are allowed, but // each price denom to fee denom pair can only have one entry. @@ -290,21 +289,21 @@ func (m *Market) GetMarketDetails() MarketDetails { return MarketDetails{} } -func (m *Market) GetFeeCreateAskFlat() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *Market) GetFeeCreateAskFlat() []types1.Coin { if m != nil { return m.FeeCreateAskFlat } return nil } -func (m *Market) GetFeeCreateBidFlat() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *Market) GetFeeCreateBidFlat() []types1.Coin { if m != nil { return m.FeeCreateBidFlat } return nil } -func (m *Market) GetFeeSettlementSellerFlat() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *Market) GetFeeSettlementSellerFlat() []types1.Coin { if m != nil { return m.FeeSettlementSellerFlat } @@ -318,7 +317,7 @@ func (m *Market) GetFeeSettlementSellerRatios() []FeeRatio { return nil } -func (m *Market) GetFeeSettlementBuyerFlat() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *Market) GetFeeSettlementBuyerFlat() []types1.Coin { if m != nil { return m.FeeSettlementBuyerFlat } @@ -491,71 +490,69 @@ func init() { } var fileDescriptor_d5cf198f1dd7e167 = []byte{ - // 1013 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xc1, 0x6f, 0x1a, 0xc7, - 0x17, 0x66, 0x0d, 0xb6, 0x61, 0xb0, 0x1d, 0x7e, 0xe3, 0xc4, 0xbf, 0x05, 0x57, 0xb0, 0x25, 0x8a, - 0x44, 0x5a, 0x19, 0x6a, 0x57, 0xbd, 0xf8, 0x06, 0x36, 0x6e, 0x90, 0x12, 0xc7, 0x5a, 0x40, 0x91, - 0x7a, 0xd9, 0x0e, 0xbb, 0x0f, 0x3c, 0xf2, 0xb2, 0x4b, 0x66, 0x66, 0xed, 0x38, 0xb7, 0x5e, 0xda, - 0xc8, 0xa7, 0x1e, 0x7b, 0xb1, 0x94, 0x5e, 0x7b, 0xee, 0x1f, 0x91, 0xa3, 0xd5, 0x53, 0x0f, 0x95, - 0x5b, 0xd9, 0xd7, 0x9e, 0xfa, 0x17, 0x54, 0x3b, 0xb3, 0x86, 0x0d, 0xc5, 0x8d, 0xaa, 0xa6, 0x27, - 0xf6, 0xbd, 0xef, 0x9b, 0xf7, 0x7d, 0xef, 0xcd, 0xf2, 0xb4, 0xe8, 0xfe, 0x88, 0xf9, 0xc7, 0xe0, - 0x11, 0xcf, 0x86, 0x1a, 0xbc, 0xb0, 0x0f, 0x89, 0x37, 0x80, 0xda, 0xf1, 0x66, 0x6d, 0x48, 0xd8, - 0x11, 0x88, 0xea, 0x88, 0xf9, 0xc2, 0xc7, 0x6b, 0x13, 0x52, 0xf5, 0x86, 0x54, 0x3d, 0xde, 0x2c, - 0x14, 0x6d, 0x9f, 0x0f, 0x7d, 0x5e, 0x23, 0x81, 0x38, 0xac, 0x1d, 0x6f, 0xf6, 0x40, 0x90, 0x4d, - 0x19, 0xa8, 0x73, 0x63, 0xbc, 0x47, 0x38, 0x8c, 0x71, 0xdb, 0xa7, 0x5e, 0x84, 0xe7, 0x15, 0x6e, - 0xc9, 0xa8, 0xa6, 0x82, 0x08, 0xba, 0x3b, 0xf0, 0x07, 0xbe, 0xca, 0x87, 0x4f, 0x2a, 0x5b, 0xfe, - 0x5d, 0x43, 0xcb, 0x4f, 0xa4, 0xb3, 0xba, 0x6d, 0xfb, 0x81, 0x27, 0xf0, 0x97, 0x68, 0x29, 0xac, - 0x6e, 0x11, 0x15, 0xeb, 0x9a, 0xa1, 0x55, 0xb2, 0x5b, 0x46, 0x35, 0x2a, 0x26, 0xcd, 0x44, 0xca, - 0xd5, 0x06, 0xe1, 0x10, 0x9d, 0x6b, 0xac, 0x5f, 0x5c, 0x96, 0xb4, 0x3f, 0x2e, 0x4b, 0xab, 0xa7, - 0x64, 0xe8, 0x6e, 0x97, 0xe3, 0x35, 0xca, 0x66, 0xb6, 0x37, 0x61, 0xe2, 0x75, 0x94, 0x51, 0xc3, - 0xb0, 0xa8, 0xa3, 0xcf, 0x19, 0x5a, 0x65, 0xd9, 0x4c, 0xab, 0x44, 0xcb, 0xc1, 0x26, 0x5a, 0x89, - 0x40, 0x07, 0x04, 0xa1, 0x2e, 0xd7, 0x93, 0xd2, 0xc0, 0x83, 0xea, 0xec, 0x91, 0x55, 0x95, 0xfb, - 0x5d, 0x45, 0x6e, 0xa4, 0xde, 0x5c, 0x96, 0x12, 0xe6, 0xf2, 0x30, 0x9e, 0xdc, 0x4e, 0xbf, 0x7a, - 0x5d, 0x4a, 0x7c, 0xf7, 0xba, 0x94, 0x28, 0x7f, 0x35, 0x6e, 0x37, 0xc2, 0x30, 0x46, 0x29, 0x8f, - 0x0c, 0x41, 0xb6, 0x99, 0x31, 0xe5, 0x33, 0x36, 0x50, 0xd6, 0x01, 0x6e, 0x33, 0x3a, 0x12, 0xd4, - 0xf7, 0xa4, 0xc5, 0x8c, 0x19, 0x4f, 0xe1, 0x12, 0xca, 0x9e, 0x40, 0x8f, 0x53, 0x01, 0x56, 0xc0, - 0x5c, 0x69, 0x31, 0x63, 0xa2, 0x28, 0xd5, 0x65, 0x2e, 0xce, 0xa3, 0x34, 0xb5, 0x7d, 0xcf, 0x0a, - 0x18, 0xd5, 0x53, 0x12, 0x5d, 0x0c, 0xe3, 0x2e, 0xa3, 0xe5, 0x5f, 0xd2, 0x68, 0x41, 0x79, 0x78, - 0x7b, 0x12, 0xda, 0x3b, 0x27, 0x31, 0xf7, 0x6f, 0x27, 0x81, 0x5f, 0xa2, 0xd5, 0x3e, 0x80, 0x65, - 0x33, 0x20, 0x02, 0x2c, 0xc2, 0x8f, 0xac, 0xbe, 0x4b, 0x84, 0x9e, 0x34, 0x92, 0x95, 0xec, 0x56, - 0xfe, 0xe6, 0x8e, 0xc3, 0xcb, 0x1a, 0xdf, 0xf1, 0x8e, 0x4f, 0xbd, 0xc6, 0x27, 0x61, 0xb1, 0x1f, - 0x7e, 0x2d, 0x55, 0x06, 0x54, 0x1c, 0x06, 0xbd, 0xaa, 0xed, 0x0f, 0xa3, 0xb7, 0x2b, 0xfa, 0xd9, - 0xe0, 0xce, 0x51, 0x4d, 0x9c, 0x8e, 0x80, 0xcb, 0x03, 0xdc, 0xcc, 0xf5, 0x01, 0x76, 0xa4, 0x4c, - 0x9d, 0x1f, 0xed, 0xb9, 0x44, 0x4c, 0x69, 0xf7, 0xa8, 0xa3, 0xb4, 0x53, 0xff, 0xa5, 0x76, 0x83, - 0x3a, 0x52, 0xfb, 0x95, 0x86, 0x0a, 0xa1, 0x38, 0x07, 0x21, 0x5c, 0x18, 0x82, 0x27, 0x2c, 0x0e, - 0xae, 0x0b, 0x4c, 0x79, 0x98, 0x7f, 0xff, 0x1e, 0xfe, 0xdf, 0x07, 0x68, 0x8f, 0xd5, 0xda, 0x52, - 0x4c, 0x5a, 0x19, 0xa0, 0x0f, 0x66, 0x3b, 0x61, 0x44, 0x50, 0x9f, 0xeb, 0x0b, 0xd2, 0x8b, 0x71, - 0xdb, 0x25, 0xef, 0x01, 0x98, 0x21, 0x31, 0xba, 0xdf, 0xfc, 0x0c, 0x19, 0x89, 0x73, 0xfc, 0xb5, - 0x86, 0xf2, 0x53, 0x4a, 0xbd, 0xe0, 0xf4, 0xa6, 0xe5, 0xc5, 0xf7, 0xdf, 0xf2, 0xda, 0x5b, 0x5e, - 0x1a, 0xa1, 0x96, 0xec, 0x18, 0xd0, 0xfa, 0x4c, 0x1f, 0x51, 0xc3, 0xe9, 0x7f, 0xd4, 0xb0, 0xfe, - 0x57, 0x91, 0xa8, 0xdf, 0x87, 0x28, 0x47, 0x6c, 0x1b, 0x46, 0x82, 0x7a, 0x03, 0xcb, 0x67, 0x0e, - 0x30, 0xae, 0x67, 0x0c, 0xad, 0x92, 0x36, 0xef, 0x8c, 0xf3, 0x4f, 0x65, 0x1a, 0x6f, 0xa1, 0x7b, - 0xc4, 0x75, 0xfd, 0x13, 0x2b, 0xe0, 0xc0, 0x62, 0xc6, 0x74, 0x24, 0xf9, 0xab, 0x12, 0xec, 0x72, - 0x60, 0x13, 0x25, 0xfc, 0x08, 0x2d, 0x87, 0x65, 0x38, 0xb7, 0x06, 0x8c, 0x78, 0x82, 0xeb, 0x59, - 0xe9, 0xfb, 0xfe, 0x6d, 0xbe, 0xeb, 0x92, 0xfc, 0x79, 0xc8, 0x35, 0x97, 0xc8, 0x24, 0xe0, 0x78, - 0x03, 0xad, 0x32, 0x78, 0x6e, 0x11, 0x21, 0x58, 0xec, 0x9f, 0xa8, 0x2f, 0x19, 0xc9, 0x4a, 0xc6, - 0xcc, 0x31, 0x78, 0x5e, 0x17, 0x82, 0x8d, 0xff, 0x3b, 0xb3, 0xe8, 0x3d, 0xea, 0xe8, 0xcb, 0x33, - 0xe8, 0x0d, 0xea, 0x94, 0x5f, 0xa2, 0xf4, 0xcd, 0xc8, 0xf0, 0x67, 0x68, 0x7e, 0xc4, 0xa8, 0x0d, - 0xd1, 0x12, 0xff, 0x9b, 0xdb, 0x56, 0xc3, 0x55, 0x6c, 0xbc, 0x89, 0x92, 0x7d, 0x80, 0x68, 0xdd, - 0xbc, 0xf3, 0x50, 0xc8, 0xdd, 0x4e, 0xc9, 0xf5, 0xfa, 0x8d, 0x86, 0xb2, 0xb1, 0xbe, 0xf1, 0x16, - 0x5a, 0x24, 0x8e, 0xc3, 0x80, 0x73, 0xb5, 0x5f, 0x1b, 0xfa, 0x4f, 0x3f, 0x6e, 0xdc, 0x8d, 0xea, - 0xd5, 0x15, 0xd2, 0x16, 0x8c, 0x7a, 0x03, 0xf3, 0x86, 0x88, 0x77, 0x51, 0x76, 0x04, 0x6c, 0x48, - 0x39, 0xa7, 0xbe, 0x17, 0xee, 0xbc, 0x64, 0x65, 0x65, 0xab, 0x7c, 0xdb, 0x94, 0x0f, 0xc6, 0x54, - 0x33, 0x7e, 0xec, 0xa3, 0xef, 0xe7, 0x10, 0x9a, 0x60, 0xf8, 0x63, 0xb4, 0x76, 0xd0, 0x34, 0x9f, - 0xb4, 0xda, 0xed, 0xd6, 0xd3, 0x7d, 0xab, 0xbb, 0xdf, 0x3e, 0x68, 0xee, 0xb4, 0xf6, 0x5a, 0xcd, - 0xdd, 0x5c, 0xa2, 0x70, 0xe7, 0xec, 0xdc, 0xc8, 0x06, 0x1e, 0x1f, 0x81, 0x4d, 0xfb, 0x14, 0x1c, - 0xfc, 0x21, 0xfa, 0x5f, 0x8c, 0xdc, 0x6e, 0x76, 0x3a, 0x8f, 0x9b, 0x39, 0xad, 0x80, 0xce, 0xce, - 0x8d, 0x05, 0xf5, 0xae, 0x4c, 0x51, 0x76, 0xea, 0xfb, 0x3b, 0xcd, 0xc7, 0xb9, 0x39, 0x45, 0xb1, - 0x43, 0x93, 0x2e, 0x7e, 0x80, 0x56, 0x63, 0x94, 0x67, 0xad, 0xce, 0xa3, 0x5d, 0xb3, 0xfe, 0x2c, - 0x97, 0x2c, 0x2c, 0x9d, 0x9d, 0x1b, 0xe9, 0x13, 0x2a, 0x0e, 0x1d, 0x46, 0x4e, 0xa6, 0x2a, 0x75, - 0x0f, 0x76, 0xeb, 0x9d, 0x66, 0x2e, 0xa5, 0x2a, 0x05, 0x23, 0x87, 0x08, 0x98, 0x32, 0x3f, 0x79, - 0x6c, 0xe7, 0xe6, 0x95, 0xf9, 0x58, 0xe3, 0xf8, 0x21, 0xba, 0x17, 0x23, 0xd7, 0x3b, 0x1d, 0xb3, - 0xd5, 0xe8, 0x76, 0x9a, 0xed, 0xdc, 0x42, 0x61, 0xe5, 0xec, 0xdc, 0x40, 0xe1, 0x6b, 0x44, 0x7b, - 0x81, 0x00, 0xde, 0x80, 0x37, 0x57, 0x45, 0xed, 0xe2, 0xaa, 0xa8, 0xfd, 0x76, 0x55, 0xd4, 0xbe, - 0xbd, 0x2e, 0x26, 0x2e, 0xae, 0x8b, 0x89, 0x9f, 0xaf, 0x8b, 0x09, 0x94, 0xa7, 0xfe, 0x2d, 0x03, - 0x3f, 0xd0, 0xbe, 0xa8, 0xc6, 0x16, 0xc2, 0x84, 0xb4, 0x41, 0xfd, 0x58, 0x54, 0x7b, 0x31, 0xfe, - 0xf6, 0xe9, 0x2d, 0xc8, 0x2f, 0x8d, 0x4f, 0xff, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x8c, 0x6c, 0x47, - 0x0b, 0x19, 0x09, 0x00, 0x00, + // 980 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x41, 0x4f, 0x1b, 0x47, + 0x14, 0xf6, 0x62, 0x03, 0xf6, 0x18, 0x88, 0x3b, 0x24, 0x74, 0x6d, 0x2a, 0x7b, 0xeb, 0x28, 0x92, + 0xd3, 0x0a, 0x5b, 0x50, 0xf5, 0xc2, 0xcd, 0x06, 0xd3, 0x58, 0x4a, 0x08, 0x5a, 0xdb, 0x8a, 0x14, + 0x55, 0xda, 0xce, 0xee, 0x3e, 0x9b, 0x11, 0xeb, 0x5d, 0x67, 0x66, 0x16, 0x92, 0xde, 0x7a, 0x6a, + 0xc5, 0xa9, 0xc7, 0x5e, 0x90, 0xd2, 0x6b, 0xcf, 0xfd, 0x11, 0x39, 0xa2, 0x9e, 0x7a, 0x42, 0x15, + 0x5c, 0x7b, 0xea, 0x2f, 0xa8, 0x76, 0x66, 0xb1, 0x37, 0xd4, 0x88, 0x44, 0xbd, 0xed, 0x7b, 0xdf, + 0xf7, 0xbe, 0xf7, 0xbd, 0x37, 0xc3, 0x60, 0xf4, 0x70, 0xcc, 0x82, 0x63, 0xf0, 0x89, 0xef, 0x40, + 0x03, 0x5e, 0x3b, 0x87, 0xc4, 0x1f, 0x42, 0xe3, 0x78, 0xb3, 0x31, 0x22, 0xec, 0x08, 0x44, 0x7d, + 0xcc, 0x02, 0x11, 0xe0, 0xb5, 0x29, 0xa9, 0x7e, 0x4d, 0xaa, 0x1f, 0x6f, 0x96, 0xca, 0x4e, 0xc0, + 0x47, 0x01, 0x6f, 0x90, 0x50, 0x1c, 0x36, 0x8e, 0x37, 0x6d, 0x10, 0x64, 0x53, 0x06, 0xaa, 0x6e, + 0x82, 0xdb, 0x84, 0xc3, 0x04, 0x77, 0x02, 0xea, 0xc7, 0x78, 0x51, 0xe1, 0x96, 0x8c, 0x1a, 0x2a, + 0x88, 0xa1, 0xfb, 0xc3, 0x60, 0x18, 0xa8, 0x7c, 0xf4, 0xa5, 0xb2, 0xd5, 0xbf, 0x35, 0xb4, 0xfc, + 0x4c, 0x3a, 0x6b, 0x3a, 0x4e, 0x10, 0xfa, 0x02, 0x7f, 0x87, 0x96, 0x22, 0x75, 0x8b, 0xa8, 0x58, + 0xd7, 0x0c, 0xad, 0x96, 0xdf, 0x32, 0xea, 0xb1, 0x98, 0x34, 0x13, 0x77, 0xae, 0xb7, 0x08, 0x87, + 0xb8, 0xae, 0xb5, 0x7e, 0x7e, 0x51, 0xd1, 0xfe, 0xb9, 0xa8, 0xac, 0xbe, 0x21, 0x23, 0x6f, 0xbb, + 0x9a, 0xd4, 0xa8, 0x9a, 0x79, 0x7b, 0xca, 0xc4, 0xeb, 0x28, 0xa7, 0x96, 0x61, 0x51, 0x57, 0x9f, + 0x33, 0xb4, 0xda, 0xb2, 0x99, 0x55, 0x89, 0x8e, 0x8b, 0x4d, 0xb4, 0x12, 0x83, 0x2e, 0x08, 0x42, + 0x3d, 0xae, 0xa7, 0xa5, 0x81, 0x47, 0xf5, 0xd9, 0x2b, 0xab, 0x2b, 0xf7, 0xbb, 0x8a, 0xdc, 0xca, + 0xbc, 0xbb, 0xa8, 0xa4, 0xcc, 0xe5, 0x51, 0x32, 0xb9, 0x9d, 0xfd, 0xe9, 0x6d, 0x25, 0xf5, 0xcb, + 0xdb, 0x4a, 0xaa, 0xfa, 0xc3, 0x64, 0xdc, 0x18, 0xc3, 0x18, 0x65, 0x7c, 0x32, 0x02, 0x39, 0x66, + 0xce, 0x94, 0xdf, 0xd8, 0x40, 0x79, 0x17, 0xb8, 0xc3, 0xe8, 0x58, 0xd0, 0xc0, 0x97, 0x16, 0x73, + 0x66, 0x32, 0x85, 0x2b, 0x28, 0x7f, 0x02, 0x36, 0xa7, 0x02, 0xac, 0x90, 0x79, 0xd2, 0x62, 0xce, + 0x44, 0x71, 0xaa, 0xcf, 0x3c, 0x5c, 0x44, 0x59, 0xea, 0x04, 0xbe, 0x15, 0x32, 0xaa, 0x67, 0x24, + 0xba, 0x18, 0xc5, 0x7d, 0x46, 0xab, 0xbf, 0x2d, 0xa2, 0x05, 0xe5, 0xe1, 0xfd, 0x4d, 0x68, 0x77, + 0x6e, 0x62, 0xee, 0xff, 0x6e, 0x02, 0xef, 0xa3, 0xd5, 0x01, 0x80, 0xe5, 0x30, 0x20, 0x02, 0x2c, + 0xc2, 0x8f, 0xac, 0x81, 0x47, 0x84, 0x9e, 0x36, 0xd2, 0xb5, 0xfc, 0x56, 0xf1, 0xfa, 0x8c, 0xa3, + 0xc3, 0x9a, 0x9c, 0xf1, 0x4e, 0x40, 0xfd, 0x58, 0xac, 0x30, 0x00, 0xd8, 0x91, 0xa5, 0x4d, 0x7e, + 0xb4, 0xe7, 0x11, 0x71, 0x43, 0xcf, 0xa6, 0xae, 0xd2, 0xcb, 0x7c, 0xac, 0x5e, 0x8b, 0xba, 0x52, + 0xef, 0x5b, 0x54, 0x8a, 0xf4, 0x38, 0x08, 0xe1, 0xc1, 0x08, 0x7c, 0x61, 0x71, 0xf0, 0x3c, 0x60, + 0x4a, 0x76, 0xfe, 0xc3, 0x64, 0x3f, 0x1d, 0x00, 0x74, 0x27, 0x0a, 0x5d, 0x29, 0x20, 0xd5, 0x87, + 0xe8, 0xb3, 0xd9, 0xea, 0x8c, 0x08, 0x1a, 0x70, 0x7d, 0x41, 0xea, 0x1b, 0xb7, 0xed, 0x77, 0x0f, + 0xc0, 0x8c, 0x88, 0x71, 0x9b, 0xe2, 0x8c, 0x36, 0x12, 0xe7, 0xf8, 0x25, 0x2a, 0xde, 0x68, 0x64, + 0x87, 0x6f, 0xae, 0xa7, 0x58, 0xfc, 0xb0, 0x29, 0xd6, 0xde, 0x93, 0x6f, 0x45, 0xf5, 0x72, 0x08, + 0x40, 0xeb, 0x33, 0xb5, 0xe3, 0x19, 0xb2, 0x1f, 0x35, 0x83, 0xfe, 0xdf, 0x26, 0xf1, 0x08, 0x8f, + 0x51, 0x81, 0x38, 0x0e, 0x8c, 0x05, 0xf5, 0x87, 0x56, 0xc0, 0x5c, 0x60, 0x5c, 0xcf, 0x19, 0x5a, + 0x2d, 0x6b, 0xde, 0x9b, 0xe4, 0x9f, 0xcb, 0x34, 0xde, 0x42, 0x0f, 0x88, 0xe7, 0x05, 0x27, 0x56, + 0xc8, 0x81, 0x25, 0x8c, 0xe9, 0x48, 0xf2, 0x57, 0x25, 0xd8, 0xe7, 0xc0, 0xa6, 0x9d, 0xf0, 0x13, + 0xb4, 0x1c, 0xc9, 0x70, 0x6e, 0x0d, 0x19, 0xf1, 0x05, 0xd7, 0xf3, 0xd2, 0xf7, 0xc3, 0xdb, 0x7c, + 0x37, 0x25, 0xf9, 0x9b, 0x88, 0x6b, 0x2e, 0x91, 0x69, 0xc0, 0xf1, 0x06, 0x5a, 0x65, 0xf0, 0xca, + 0x22, 0x42, 0xb0, 0xc4, 0xbd, 0xd6, 0x97, 0x8c, 0x74, 0x2d, 0x67, 0x16, 0x18, 0xbc, 0x6a, 0x0a, + 0xc1, 0x26, 0xb7, 0x76, 0x16, 0xdd, 0xa6, 0xae, 0xbe, 0x3c, 0x83, 0xde, 0xa2, 0x6e, 0xf5, 0x7b, + 0x94, 0xbd, 0x5e, 0x19, 0xfe, 0x1a, 0xcd, 0x8f, 0x19, 0x75, 0x20, 0x7e, 0x12, 0xef, 0x3c, 0x41, + 0xc5, 0xc6, 0x9b, 0x28, 0x3d, 0x00, 0x88, 0xff, 0x78, 0xef, 0x2c, 0x8a, 0xb8, 0xdb, 0x19, 0xf9, + 0x58, 0xfd, 0xa8, 0xa1, 0x7c, 0x62, 0x6e, 0xbc, 0x85, 0x16, 0x89, 0xeb, 0x32, 0xe0, 0x5c, 0xbd, + 0x56, 0x2d, 0xfd, 0x8f, 0xdf, 0x37, 0xee, 0xc7, 0x7a, 0x4d, 0x85, 0x74, 0x05, 0xa3, 0xfe, 0xd0, + 0xbc, 0x26, 0xe2, 0x5d, 0x94, 0x1f, 0x03, 0x1b, 0x51, 0xce, 0x69, 0xe0, 0x47, 0x2f, 0x48, 0xba, + 0xb6, 0xb2, 0x55, 0xbd, 0x6d, 0xcb, 0x07, 0x13, 0xaa, 0x99, 0x2c, 0xfb, 0xe2, 0xd7, 0x39, 0x84, + 0xa6, 0x18, 0xfe, 0x12, 0xad, 0x1d, 0xb4, 0xcd, 0x67, 0x9d, 0x6e, 0xb7, 0xf3, 0x7c, 0xdf, 0xea, + 0xef, 0x77, 0x0f, 0xda, 0x3b, 0x9d, 0xbd, 0x4e, 0x7b, 0xb7, 0x90, 0x2a, 0xdd, 0x3b, 0x3d, 0x33, + 0xf2, 0xa1, 0xcf, 0xc7, 0xe0, 0xd0, 0x01, 0x05, 0x17, 0x7f, 0x8e, 0x3e, 0x49, 0x90, 0xbb, 0xed, + 0x5e, 0xef, 0x69, 0xbb, 0xa0, 0x95, 0xd0, 0xe9, 0x99, 0xb1, 0xa0, 0xee, 0xca, 0x0d, 0xca, 0x4e, + 0x73, 0x7f, 0xa7, 0xfd, 0xb4, 0x30, 0xa7, 0x28, 0x4e, 0x64, 0xd2, 0xc3, 0x8f, 0xd0, 0x6a, 0x82, + 0xf2, 0xa2, 0xd3, 0x7b, 0xb2, 0x6b, 0x36, 0x5f, 0x14, 0xd2, 0xa5, 0xa5, 0xd3, 0x33, 0x23, 0x7b, + 0x42, 0xc5, 0xa1, 0xcb, 0xc8, 0xc9, 0x0d, 0xa5, 0xfe, 0xc1, 0x6e, 0xb3, 0xd7, 0x2e, 0x64, 0x94, + 0x52, 0x38, 0x76, 0x89, 0x80, 0x1b, 0xe6, 0xa7, 0x9f, 0xdd, 0xc2, 0xbc, 0x32, 0x9f, 0x18, 0x1c, + 0x3f, 0x46, 0x0f, 0x12, 0xe4, 0x66, 0xaf, 0x67, 0x76, 0x5a, 0xfd, 0x5e, 0xbb, 0x5b, 0x58, 0x28, + 0xad, 0x9c, 0x9e, 0x19, 0x28, 0xba, 0x46, 0xd4, 0x0e, 0x05, 0xf0, 0x16, 0xbc, 0xbb, 0x2c, 0x6b, + 0xe7, 0x97, 0x65, 0xed, 0xaf, 0xcb, 0xb2, 0xf6, 0xf3, 0x55, 0x39, 0x75, 0x7e, 0x55, 0x4e, 0xfd, + 0x79, 0x55, 0x4e, 0xa1, 0x22, 0x0d, 0x6e, 0x59, 0xf8, 0x81, 0xf6, 0xb2, 0x3e, 0xa4, 0xe2, 0x30, + 0xb4, 0xeb, 0x4e, 0x30, 0x6a, 0x4c, 0x49, 0x1b, 0x34, 0x48, 0x44, 0x8d, 0xd7, 0x93, 0x5f, 0x12, + 0xf6, 0x82, 0xfc, 0xbf, 0xfd, 0xd5, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x66, 0xc4, 0xbd, 0xec, + 0x67, 0x08, 0x00, 0x00, } func (m *MarketAccount) Marshal() (dAtA []byte, err error) { diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index bf68f99da5..202189e4f5 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -7,7 +7,6 @@ import ( context "context" fmt "fmt" _ "github.com/cosmos/cosmos-proto" - github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/cosmos-sdk/types/msgservice" _ "github.com/gogo/protobuf/gogoproto" @@ -1019,25 +1018,25 @@ type MsgGovManageFeesRequest struct { // authority should be the governance module account address. Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` // add_fee_create_ask_flat are the create ask flat fee options to add. - AddFeeCreateAskFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=add_fee_create_ask_flat,json=addFeeCreateAskFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"add_fee_create_ask_flat"` + AddFeeCreateAskFlat []types.Coin `protobuf:"bytes,2,rep,name=add_fee_create_ask_flat,json=addFeeCreateAskFlat,proto3" json:"add_fee_create_ask_flat"` // remove_fee_create_ask_flat are the create ask flat fee options to remove. - RemoveFeeCreateAskFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=remove_fee_create_ask_flat,json=removeFeeCreateAskFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"remove_fee_create_ask_flat"` + RemoveFeeCreateAskFlat []types.Coin `protobuf:"bytes,3,rep,name=remove_fee_create_ask_flat,json=removeFeeCreateAskFlat,proto3" json:"remove_fee_create_ask_flat"` // add_fee_create_bid_flat are the create bid flat fee options to add. - AddFeeCreateBidFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=add_fee_create_bid_flat,json=addFeeCreateBidFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"add_fee_create_bid_flat"` + AddFeeCreateBidFlat []types.Coin `protobuf:"bytes,4,rep,name=add_fee_create_bid_flat,json=addFeeCreateBidFlat,proto3" json:"add_fee_create_bid_flat"` // remove_fee_create_bid_flat are the create bid flat fee options to remove. - RemoveFeeCreateBidFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,5,rep,name=remove_fee_create_bid_flat,json=removeFeeCreateBidFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"remove_fee_create_bid_flat"` + RemoveFeeCreateBidFlat []types.Coin `protobuf:"bytes,5,rep,name=remove_fee_create_bid_flat,json=removeFeeCreateBidFlat,proto3" json:"remove_fee_create_bid_flat"` // add_fee_settlement_seller_flat are the seller settlement flat fee options to add. - AddFeeSettlementSellerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,6,rep,name=add_fee_settlement_seller_flat,json=addFeeSettlementSellerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"add_fee_settlement_seller_flat"` + AddFeeSettlementSellerFlat []types.Coin `protobuf:"bytes,6,rep,name=add_fee_settlement_seller_flat,json=addFeeSettlementSellerFlat,proto3" json:"add_fee_settlement_seller_flat"` // remove_fee_settlement_seller_flat are the seller settlement flat fee options to remove. - RemoveFeeSettlementSellerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,7,rep,name=remove_fee_settlement_seller_flat,json=removeFeeSettlementSellerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"remove_fee_settlement_seller_flat"` + RemoveFeeSettlementSellerFlat []types.Coin `protobuf:"bytes,7,rep,name=remove_fee_settlement_seller_flat,json=removeFeeSettlementSellerFlat,proto3" json:"remove_fee_settlement_seller_flat"` // add_fee_settlement_seller_ratios are the seller settlement fee ratios to add. AddFeeSettlementSellerRatios []FeeRatio `protobuf:"bytes,8,rep,name=add_fee_settlement_seller_ratios,json=addFeeSettlementSellerRatios,proto3" json:"add_fee_settlement_seller_ratios"` // remove_fee_settlement_seller_ratios are the seller settlement fee ratios to remove. RemoveFeeSettlementSellerRatios []FeeRatio `protobuf:"bytes,9,rep,name=remove_fee_settlement_seller_ratios,json=removeFeeSettlementSellerRatios,proto3" json:"remove_fee_settlement_seller_ratios"` // add_fee_settlement_buyer_flat are the buyer settlement flat fee options to add. - AddFeeSettlementBuyerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,10,rep,name=add_fee_settlement_buyer_flat,json=addFeeSettlementBuyerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"add_fee_settlement_buyer_flat"` + AddFeeSettlementBuyerFlat []types.Coin `protobuf:"bytes,10,rep,name=add_fee_settlement_buyer_flat,json=addFeeSettlementBuyerFlat,proto3" json:"add_fee_settlement_buyer_flat"` // remove_fee_settlement_buyer_flat are the buyer settlement flat fee options to remove. - RemoveFeeSettlementBuyerFlat github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,11,rep,name=remove_fee_settlement_buyer_flat,json=removeFeeSettlementBuyerFlat,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"remove_fee_settlement_buyer_flat"` + RemoveFeeSettlementBuyerFlat []types.Coin `protobuf:"bytes,11,rep,name=remove_fee_settlement_buyer_flat,json=removeFeeSettlementBuyerFlat,proto3" json:"remove_fee_settlement_buyer_flat"` // add_fee_settlement_buyer_ratios are the buyer settlement fee ratios to add. AddFeeSettlementBuyerRatios []FeeRatio `protobuf:"bytes,12,rep,name=add_fee_settlement_buyer_ratios,json=addFeeSettlementBuyerRatios,proto3" json:"add_fee_settlement_buyer_ratios"` // remove_fee_settlement_buyer_ratios are the buyer settlement fee ratios to remove. @@ -1084,42 +1083,42 @@ func (m *MsgGovManageFeesRequest) GetAuthority() string { return "" } -func (m *MsgGovManageFeesRequest) GetAddFeeCreateAskFlat() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *MsgGovManageFeesRequest) GetAddFeeCreateAskFlat() []types.Coin { if m != nil { return m.AddFeeCreateAskFlat } return nil } -func (m *MsgGovManageFeesRequest) GetRemoveFeeCreateAskFlat() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *MsgGovManageFeesRequest) GetRemoveFeeCreateAskFlat() []types.Coin { if m != nil { return m.RemoveFeeCreateAskFlat } return nil } -func (m *MsgGovManageFeesRequest) GetAddFeeCreateBidFlat() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *MsgGovManageFeesRequest) GetAddFeeCreateBidFlat() []types.Coin { if m != nil { return m.AddFeeCreateBidFlat } return nil } -func (m *MsgGovManageFeesRequest) GetRemoveFeeCreateBidFlat() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *MsgGovManageFeesRequest) GetRemoveFeeCreateBidFlat() []types.Coin { if m != nil { return m.RemoveFeeCreateBidFlat } return nil } -func (m *MsgGovManageFeesRequest) GetAddFeeSettlementSellerFlat() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *MsgGovManageFeesRequest) GetAddFeeSettlementSellerFlat() []types.Coin { if m != nil { return m.AddFeeSettlementSellerFlat } return nil } -func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementSellerFlat() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementSellerFlat() []types.Coin { if m != nil { return m.RemoveFeeSettlementSellerFlat } @@ -1140,14 +1139,14 @@ func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementSellerRatios() []FeeRati return nil } -func (m *MsgGovManageFeesRequest) GetAddFeeSettlementBuyerFlat() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *MsgGovManageFeesRequest) GetAddFeeSettlementBuyerFlat() []types.Coin { if m != nil { return m.AddFeeSettlementBuyerFlat } return nil } -func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementBuyerFlat() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementBuyerFlat() []types.Coin { if m != nil { return m.RemoveFeeSettlementBuyerFlat } @@ -1333,79 +1332,77 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1151 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x41, 0x6f, 0x1b, 0x45, - 0x14, 0xce, 0x92, 0x12, 0x9a, 0x97, 0x26, 0x48, 0x93, 0x34, 0xb1, 0xb7, 0xc9, 0xda, 0x71, 0x38, - 0x44, 0x2d, 0x59, 0x27, 0x41, 0x04, 0xa8, 0xb8, 0xc4, 0x85, 0xf4, 0x14, 0x11, 0x39, 0xaa, 0x90, - 0xb8, 0x58, 0x63, 0xef, 0x64, 0xb3, 0xca, 0x7a, 0xc7, 0xdd, 0x19, 0x9b, 0xe4, 0x08, 0x42, 0xea, - 0x09, 0x51, 0x09, 0x55, 0xe2, 0xca, 0x95, 0x13, 0x07, 0x7e, 0x44, 0x8f, 0x15, 0x27, 0x4e, 0x80, - 0x92, 0x03, 0x7f, 0x03, 0xed, 0xce, 0xec, 0x7a, 0x6d, 0xcf, 0xae, 0xd7, 0xad, 0x7b, 0x6a, 0x3d, - 0xf3, 0xbd, 0xf7, 0x7d, 0xef, 0x73, 0x76, 0xdf, 0x7b, 0x86, 0x52, 0xc7, 0xa7, 0x3d, 0xe2, 0x61, - 0xaf, 0x45, 0xaa, 0xe4, 0xb2, 0x75, 0x8e, 0x3d, 0x9b, 0x54, 0x7b, 0x7b, 0x55, 0x7e, 0x69, 0x76, - 0x7c, 0xca, 0x29, 0x5a, 0xed, 0x03, 0xcc, 0x08, 0x60, 0xf6, 0xf6, 0x74, 0xa3, 0x45, 0x59, 0x9b, - 0xb2, 0x6a, 0x13, 0xb3, 0x20, 0xa0, 0x49, 0x38, 0xde, 0xab, 0xb6, 0xa8, 0xe3, 0x89, 0x38, 0x7d, - 0x4d, 0xde, 0xb7, 0x99, 0x1d, 0xe4, 0x6b, 0x33, 0x5b, 0x5e, 0x14, 0xc5, 0x45, 0x23, 0xfc, 0x54, - 0x15, 0x1f, 0xe4, 0xd5, 0x8a, 0x4d, 0x6d, 0x2a, 0xce, 0x83, 0xff, 0xc9, 0xd3, 0xad, 0x14, 0x89, - 0x6d, 0xec, 0x5f, 0x10, 0x3e, 0x06, 0xd4, 0xc1, 0x3e, 0x6e, 0xcb, 0xfc, 0x95, 0xbb, 0xb0, 0x7c, - 0xcc, 0xec, 0x47, 0x3e, 0xc1, 0x9c, 0x1c, 0xb2, 0x8b, 0x3a, 0x79, 0xda, 0x25, 0x8c, 0x57, 0x56, - 0x61, 0x65, 0xf0, 0x98, 0x75, 0xa8, 0xc7, 0xc8, 0x00, 0xbc, 0xe6, 0x58, 0x2a, 0x78, 0x78, 0x2c, - 0xe1, 0x6b, 0x70, 0x37, 0x38, 0x0f, 0x24, 0xb8, 0x5f, 0xf9, 0x16, 0xf1, 0xa3, 0x80, 0x02, 0xac, - 0x0e, 0x5f, 0xc8, 0x90, 0x15, 0x40, 0xc7, 0xcc, 0x3e, 0x72, 0x5c, 0xb7, 0xe6, 0x58, 0x2c, 0xc2, - 0x0b, 0xde, 0xfe, 0xe9, 0x08, 0xf8, 0x90, 0x5d, 0x28, 0xc0, 0xe2, 0x54, 0x82, 0x05, 0xe7, 0x71, - 0x68, 0xd1, 0x29, 0xe1, 0xdc, 0x25, 0x51, 0x40, 0x11, 0xd6, 0x46, 0x6e, 0x64, 0x90, 0x0e, 0x85, - 0xf8, 0xea, 0x6b, 0x87, 0x9f, 0x5b, 0x3e, 0xfe, 0x36, 0x0a, 0xbb, 0x07, 0x45, 0xc5, 0x9d, 0x0c, - 0x2c, 0xc1, 0x46, 0x7c, 0xf9, 0xa4, 0x63, 0x61, 0x4e, 0xbe, 0x20, 0x1c, 0x3b, 0x6e, 0xac, 0xb2, - 0x0c, 0x46, 0x1a, 0x20, 0x35, 0xc5, 0x97, 0x1e, 0x6e, 0xba, 0xc4, 0x4a, 0x4f, 0x11, 0x03, 0x64, - 0x8a, 0x0a, 0x94, 0x87, 0x10, 0x4f, 0x18, 0xf1, 0x07, 0xab, 0xdf, 0x82, 0xcd, 0x0c, 0x8c, 0x4c, - 0x94, 0x04, 0x1d, 0x63, 0x0f, 0xdb, 0xe4, 0x84, 0xf8, 0x6d, 0x87, 0x31, 0x87, 0x7a, 0x71, 0x49, - 0x1f, 0x40, 0x25, 0x0b, 0x24, 0x53, 0x25, 0x55, 0x0b, 0x54, 0x9d, 0x3c, 0x3d, 0xe4, 0xdc, 0x8f, - 0xf3, 0x6c, 0x42, 0x29, 0x15, 0x21, 0x93, 0xfc, 0xaa, 0x85, 0xe6, 0x3f, 0xa6, 0x3d, 0xf1, 0x57, - 0x27, 0xc0, 0x32, 0x01, 0x3a, 0x80, 0x79, 0xdc, 0xe5, 0xe7, 0xd4, 0x77, 0xf8, 0x55, 0x41, 0x2b, - 0x6b, 0xdb, 0xf3, 0xb5, 0xc2, 0x9f, 0x7f, 0xec, 0xac, 0xc8, 0x47, 0xeb, 0xd0, 0xb2, 0x7c, 0xc2, - 0xd8, 0x29, 0xf7, 0x1d, 0xcf, 0xae, 0xf7, 0xa1, 0xe8, 0x73, 0x98, 0x13, 0x8f, 0x50, 0xe1, 0x9d, - 0xb2, 0xb6, 0xbd, 0xb0, 0x6f, 0x98, 0xea, 0x47, 0xdd, 0x14, 0x74, 0xb5, 0x5b, 0x2f, 0xff, 0x2e, - 0xcd, 0xd4, 0x65, 0xcc, 0xc3, 0xa5, 0xef, 0xff, 0xfb, 0xfd, 0x7e, 0x3f, 0x5b, 0x65, 0x1d, 0x74, - 0x95, 0x44, 0x59, 0xc1, 0x8b, 0xc5, 0xf0, 0xaf, 0xee, 0x31, 0xed, 0x89, 0x12, 0x8f, 0x08, 0x61, - 0x6f, 0xaa, 0xff, 0x3b, 0x0d, 0xd6, 0xb0, 0x65, 0x35, 0xce, 0x08, 0x69, 0xb4, 0x42, 0xd2, 0x06, - 0x66, 0x17, 0x8d, 0x33, 0x17, 0x07, 0x15, 0xcd, 0x6e, 0x2f, 0xec, 0x17, 0x4d, 0x99, 0x23, 0x78, - 0x49, 0x99, 0xf2, 0x25, 0x65, 0x3e, 0xa2, 0x8e, 0x57, 0xdb, 0x0d, 0x8a, 0xf9, 0xed, 0x9f, 0xd2, - 0xb6, 0xed, 0xf0, 0xf3, 0x6e, 0xd3, 0x6c, 0xd1, 0xb6, 0x7c, 0x17, 0xc9, 0x7f, 0x76, 0x98, 0x75, - 0x51, 0xe5, 0x57, 0x1d, 0xc2, 0xc2, 0x00, 0x56, 0x5f, 0xc6, 0x96, 0x75, 0x44, 0x48, 0xfc, 0x9a, - 0x38, 0x72, 0x31, 0x47, 0xcf, 0x34, 0xd0, 0x7d, 0xd2, 0xa6, 0x3d, 0xa2, 0x94, 0x31, 0x3b, 0x7d, - 0x19, 0xab, 0x82, 0x6e, 0x44, 0x89, 0xc2, 0x8d, 0xa6, 0x63, 0x09, 0x19, 0xb7, 0xde, 0xb2, 0x1b, - 0x35, 0xc7, 0xca, 0x70, 0x23, 0x96, 0xf1, 0xee, 0xdb, 0x77, 0x23, 0x52, 0xf2, 0x93, 0x06, 0x46, - 0xe4, 0x06, 0x0b, 0x1f, 0xee, 0x36, 0xf1, 0x78, 0x83, 0x11, 0xd7, 0x25, 0xbe, 0x50, 0x33, 0x37, - 0x7d, 0x35, 0xba, 0x30, 0xe5, 0x34, 0x26, 0x3c, 0x0d, 0xf9, 0x42, 0x45, 0x2f, 0x34, 0xd8, 0x4c, - 0x78, 0x93, 0x22, 0xea, 0xbd, 0xe9, 0x8b, 0xda, 0x88, 0x2d, 0x52, 0xea, 0xf2, 0xa0, 0x9c, 0x6e, - 0x94, 0x8f, 0xb9, 0x43, 0x59, 0xe1, 0x76, 0xa8, 0xaa, 0x9c, 0xf6, 0x7e, 0x38, 0x22, 0xa4, 0x1e, - 0x00, 0xe5, 0x1b, 0x62, 0x5d, 0xed, 0x42, 0x08, 0x61, 0x88, 0xc3, 0x56, 0xa6, 0x0d, 0x92, 0x72, - 0x7e, 0x22, 0xca, 0x52, 0x6a, 0x8d, 0x92, 0xf5, 0x47, 0x0d, 0x36, 0x14, 0x65, 0x36, 0xbb, 0x57, - 0x91, 0xf3, 0x30, 0x7d, 0xe7, 0x8b, 0xc3, 0x46, 0xd4, 0x02, 0xba, 0xd0, 0xf5, 0x9f, 0x35, 0x28, - 0xab, 0x6d, 0x48, 0x48, 0x5a, 0x98, 0xbe, 0xa4, 0x75, 0x85, 0x51, 0x7d, 0x55, 0x2e, 0x94, 0x52, - 0x4d, 0x92, 0xdf, 0xcb, 0x9d, 0x89, 0xbe, 0x97, 0x7b, 0x4a, 0x07, 0xe4, 0x77, 0xe2, 0x43, 0x25, - 0xcb, 0x02, 0x49, 0xb8, 0x38, 0x11, 0xa1, 0x91, 0x56, 0x9f, 0xe0, 0x1c, 0xe9, 0x5a, 0x62, 0xe2, - 0x19, 0x6a, 0x4b, 0x23, 0x5d, 0x57, 0x0c, 0x0a, 0x27, 0xe1, 0x28, 0x39, 0x85, 0xae, 0x2b, 0x66, - 0xd2, 0x71, 0x5d, 0x57, 0xd0, 0x45, 0x5d, 0x57, 0xc4, 0xa4, 0x77, 0xdd, 0x41, 0x89, 0xa2, 0x82, - 0xfd, 0x5f, 0x96, 0x60, 0xf6, 0x98, 0xd9, 0xe8, 0x0c, 0xe6, 0xe3, 0x66, 0x81, 0x1e, 0xa4, 0xb6, - 0xf9, 0xd1, 0xd1, 0x58, 0xff, 0x30, 0x1f, 0x58, 0xf0, 0xf5, 0x79, 0x6a, 0x8e, 0x95, 0x83, 0xa7, - 0x3f, 0x53, 0xe7, 0xe0, 0x49, 0x4c, 0xda, 0xc8, 0x85, 0x85, 0xc4, 0x34, 0x8d, 0x76, 0xb2, 0x82, - 0x47, 0xc6, 0x71, 0xdd, 0xcc, 0x0b, 0x97, 0x6c, 0x2d, 0xb8, 0x1d, 0xcd, 0xe2, 0xe8, 0x7e, 0x46, - 0xec, 0xd0, 0x18, 0xaf, 0x3f, 0xc8, 0x85, 0x1d, 0x24, 0x09, 0x66, 0xf8, 0xb1, 0x24, 0x89, 0xf1, - 0x7f, 0x2c, 0x49, 0x72, 0x29, 0x40, 0x14, 0xee, 0x24, 0xe7, 0x7e, 0x94, 0xe5, 0x84, 0x62, 0x75, - 0xd0, 0xab, 0xb9, 0xf1, 0x92, 0xb0, 0x0b, 0x4b, 0x83, 0x1b, 0x03, 0xda, 0x1d, 0x9b, 0x62, 0x68, - 0xf1, 0xd0, 0xf7, 0x26, 0x88, 0x90, 0xb4, 0x3f, 0x68, 0xb0, 0xac, 0xd8, 0x35, 0xd0, 0xc7, 0x63, - 0x53, 0xa9, 0x96, 0x17, 0xfd, 0x60, 0xd2, 0xb0, 0x14, 0x19, 0x72, 0x5f, 0xc9, 0x2d, 0x63, 0x70, - 0x01, 0xca, 0x2d, 0x63, 0x68, 0x2d, 0x0a, 0x7a, 0xdf, 0xaa, 0x7a, 0xe1, 0x41, 0x9f, 0xe6, 0x4c, - 0x39, 0xb2, 0x47, 0xe9, 0x9f, 0xbd, 0x46, 0xa4, 0xd4, 0xf3, 0x5c, 0x83, 0xb5, 0x94, 0xb5, 0x09, - 0x8d, 0x4f, 0x9b, 0xb6, 0x8f, 0xe9, 0x0f, 0x5f, 0x27, 0x54, 0x4a, 0x7a, 0xa6, 0xc1, 0x8a, 0x6a, - 0x03, 0x43, 0x07, 0x39, 0x93, 0x0e, 0x2d, 0x75, 0xfa, 0x27, 0x13, 0xc7, 0x49, 0x25, 0x97, 0xf0, - 0xfe, 0xd0, 0x0e, 0x85, 0xb2, 0x1e, 0x00, 0xf5, 0x4a, 0xa8, 0xef, 0x4f, 0x12, 0x22, 0x99, 0x7d, - 0x58, 0x1c, 0xe8, 0x83, 0xa8, 0x9a, 0x9d, 0x64, 0x64, 0x91, 0xd3, 0x77, 0xf3, 0x07, 0x0c, 0x54, - 0x9b, 0xec, 0x5d, 0xe3, 0xaa, 0x55, 0xb4, 0xe2, 0x71, 0xd5, 0xaa, 0x5a, 0x63, 0x8d, 0xbc, 0xbc, - 0x36, 0xb4, 0x57, 0xd7, 0x86, 0xf6, 0xef, 0xb5, 0xa1, 0x3d, 0xbf, 0x31, 0x66, 0x5e, 0xdd, 0x18, - 0x33, 0x7f, 0xdd, 0x18, 0x33, 0x50, 0x74, 0x68, 0x4a, 0xbe, 0x13, 0xed, 0x1b, 0x33, 0x31, 0x69, - 0xf5, 0x41, 0x3b, 0x0e, 0x4d, 0x7c, 0xaa, 0x5e, 0xc6, 0xbf, 0x40, 0x35, 0xe7, 0xc2, 0x1f, 0x9e, - 0x3e, 0xfa, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xec, 0x17, 0x46, 0x90, 0x67, 0x13, 0x00, 0x00, + // 1107 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0x41, 0x6f, 0xe3, 0x44, + 0x18, 0xad, 0xd9, 0xa5, 0x6c, 0xbf, 0xee, 0x16, 0x69, 0xda, 0x6d, 0x12, 0x6f, 0xeb, 0xa4, 0x29, + 0x87, 0x6a, 0x97, 0xda, 0xdb, 0x22, 0x0a, 0xac, 0xb8, 0x34, 0x0b, 0xd9, 0x53, 0x44, 0x95, 0xaa, + 0x42, 0x82, 0x43, 0x34, 0x89, 0xa7, 0xae, 0x55, 0xc7, 0x93, 0xf5, 0x4c, 0x42, 0xf7, 0x8a, 0x90, + 0x38, 0x21, 0xed, 0x91, 0x2b, 0x3f, 0x00, 0x89, 0x03, 0x3f, 0x62, 0x8f, 0x2b, 0x4e, 0x9c, 0x10, + 0x6a, 0x0f, 0xfc, 0x0d, 0x64, 0xcf, 0xd8, 0x71, 0xe2, 0x71, 0xe2, 0x14, 0x6e, 0xf5, 0xcc, 0xfb, + 0xde, 0xfb, 0xde, 0xa8, 0xe3, 0xef, 0x39, 0x50, 0x1d, 0x04, 0x74, 0x44, 0x7c, 0xec, 0xf7, 0x88, + 0x45, 0xae, 0x7a, 0x17, 0xd8, 0x77, 0x88, 0x35, 0x3a, 0xb0, 0xf8, 0x95, 0x39, 0x08, 0x28, 0xa7, + 0x68, 0x73, 0x0c, 0x30, 0x63, 0x80, 0x39, 0x3a, 0xd0, 0x8d, 0x1e, 0x65, 0x7d, 0xca, 0xac, 0x2e, + 0x66, 0x61, 0x41, 0x97, 0x70, 0x7c, 0x60, 0xf5, 0xa8, 0xeb, 0x8b, 0x3a, 0xbd, 0x24, 0xf7, 0xfb, + 0xcc, 0x09, 0xf9, 0xfa, 0xcc, 0x91, 0x1b, 0x15, 0xb1, 0xd1, 0x89, 0x9e, 0x2c, 0xf1, 0x20, 0xb7, + 0x36, 0x1c, 0xea, 0x50, 0xb1, 0x1e, 0xfe, 0x25, 0x57, 0x77, 0x73, 0x5a, 0xec, 0xe3, 0xe0, 0x92, + 0xf0, 0x39, 0xa0, 0x01, 0x0e, 0x70, 0x5f, 0xf2, 0xd7, 0x1f, 0xc2, 0x7a, 0x8b, 0x39, 0xcf, 0x03, + 0x82, 0x39, 0x39, 0x66, 0x97, 0x6d, 0xf2, 0x72, 0x48, 0x18, 0xaf, 0x6f, 0xc2, 0xc6, 0xe4, 0x32, + 0x1b, 0x50, 0x9f, 0x91, 0x09, 0x78, 0xc3, 0xb5, 0x55, 0xf0, 0x68, 0x59, 0xc2, 0x4b, 0xf0, 0x30, + 0x5c, 0x0f, 0x5b, 0xf0, 0xbe, 0x0a, 0x6c, 0x12, 0xc4, 0x05, 0x65, 0xd8, 0x9c, 0xde, 0x90, 0x25, + 0x1b, 0x80, 0x5a, 0xcc, 0x69, 0xba, 0x9e, 0xd7, 0x70, 0x6d, 0x16, 0xe3, 0x85, 0xee, 0x78, 0x35, + 0x03, 0x3e, 0x66, 0x97, 0x0a, 0xb0, 0x58, 0x95, 0x60, 0xa1, 0xd9, 0x8a, 0x8e, 0xe8, 0x94, 0x70, + 0xee, 0x91, 0xb8, 0xa0, 0x02, 0xa5, 0xcc, 0x8e, 0x2c, 0xd2, 0xa1, 0x9c, 0x6c, 0x7d, 0xed, 0xf2, + 0x0b, 0x3b, 0xc0, 0xdf, 0xc5, 0x65, 0x8f, 0xa0, 0xa2, 0xd8, 0x93, 0x85, 0x55, 0xd8, 0x4e, 0x36, + 0xcf, 0x06, 0x36, 0xe6, 0xe4, 0x0b, 0xc2, 0xb1, 0xeb, 0x25, 0x5d, 0xd6, 0xc0, 0xc8, 0x03, 0xe4, + 0x52, 0x7c, 0xe9, 0xe3, 0xae, 0x47, 0xec, 0x7c, 0x8a, 0x04, 0x20, 0x29, 0xea, 0x50, 0x9b, 0x42, + 0x9c, 0x31, 0x12, 0x4c, 0xba, 0xdf, 0x85, 0x9d, 0x19, 0x18, 0x49, 0x94, 0x06, 0xb5, 0xb0, 0x8f, + 0x1d, 0x72, 0x42, 0x82, 0xbe, 0xcb, 0x98, 0x4b, 0xfd, 0xc4, 0xd2, 0x07, 0x50, 0x9f, 0x05, 0x92, + 0x54, 0xe9, 0xae, 0x05, 0xaa, 0x4d, 0x5e, 0x1e, 0x73, 0x1e, 0x24, 0x3c, 0x3b, 0x50, 0xcd, 0x45, + 0x48, 0x92, 0x5f, 0xb4, 0xe8, 0xf0, 0x5f, 0xd0, 0x91, 0xf8, 0xaf, 0x13, 0x60, 0x49, 0x80, 0x8e, + 0x60, 0x05, 0x0f, 0xf9, 0x05, 0x0d, 0x5c, 0xfe, 0xaa, 0xac, 0xd5, 0xb4, 0xbd, 0x95, 0x46, 0xf9, + 0x8f, 0xdf, 0xf7, 0x37, 0xe4, 0xd5, 0x3a, 0xb6, 0xed, 0x80, 0x30, 0x76, 0xca, 0x03, 0xd7, 0x77, + 0xda, 0x63, 0x28, 0xfa, 0x1c, 0x96, 0xc5, 0x15, 0x2a, 0xbf, 0x53, 0xd3, 0xf6, 0x56, 0x0f, 0x0d, + 0x53, 0x7d, 0xd5, 0x4d, 0x21, 0xd7, 0xb8, 0xfb, 0xe6, 0xaf, 0xea, 0x52, 0x5b, 0xd6, 0x3c, 0x5b, + 0xfb, 0xfe, 0x9f, 0xdf, 0x1e, 0x8f, 0xd9, 0xea, 0x5b, 0xa0, 0xab, 0x5a, 0x94, 0x0e, 0x7e, 0x85, + 0xe8, 0xbf, 0xee, 0x05, 0x1d, 0x09, 0x8b, 0x4d, 0x42, 0xd8, 0x7f, 0xed, 0xff, 0x0c, 0x4a, 0xd8, + 0xb6, 0x3b, 0xe7, 0x84, 0x74, 0x7a, 0x91, 0x66, 0x07, 0xb3, 0xcb, 0xce, 0xb9, 0x87, 0x43, 0x43, + 0x77, 0xf6, 0x56, 0x0f, 0x2b, 0xa6, 0xa4, 0x08, 0xdf, 0x51, 0xa6, 0x7c, 0x47, 0x99, 0xcf, 0xa9, + 0xeb, 0x4b, 0x2f, 0xeb, 0xd8, 0xb6, 0x9b, 0x84, 0x24, 0x37, 0xbf, 0xe9, 0x61, 0x8e, 0xbe, 0x05, + 0x3d, 0x20, 0x7d, 0x3a, 0x22, 0x4a, 0xe6, 0x3b, 0xc5, 0x98, 0x37, 0x05, 0x45, 0x86, 0x3c, 0xdb, + 0x73, 0xd7, 0xb5, 0x05, 0xf3, 0xdd, 0x5b, 0xf4, 0xdc, 0x70, 0xed, 0xfc, 0x9e, 0x13, 0xe6, 0x77, + 0x6f, 0xd7, 0x73, 0x4c, 0xde, 0x03, 0x23, 0xee, 0x99, 0x45, 0xf7, 0xa4, 0x4f, 0x7c, 0xde, 0x61, + 0xc4, 0xf3, 0x48, 0x20, 0x04, 0x96, 0x8b, 0x09, 0xe8, 0xa2, 0xf5, 0xd3, 0x84, 0xe4, 0x34, 0xe2, + 0x88, 0x44, 0x5c, 0xd8, 0x49, 0x39, 0xc8, 0xd1, 0x79, 0xaf, 0x98, 0xce, 0x76, 0x62, 0x44, 0x29, + 0xe5, 0x43, 0x2d, 0xdf, 0x4f, 0x80, 0xb9, 0x4b, 0x59, 0xf9, 0x5e, 0xa4, 0x54, 0xcb, 0xbb, 0x11, + 0x4d, 0x42, 0xda, 0x21, 0x50, 0x0a, 0x6e, 0xa9, 0x8d, 0x45, 0x10, 0x86, 0x38, 0xec, 0xce, 0xb4, + 0x26, 0x25, 0x57, 0x16, 0x92, 0xac, 0xe6, 0x7a, 0x94, 0xaa, 0x18, 0xb6, 0x15, 0x2e, 0xbb, 0xc3, + 0x57, 0xf1, 0x61, 0x42, 0xb1, 0xc3, 0xac, 0x4c, 0x7b, 0x6b, 0x84, 0x14, 0xd1, 0x41, 0x3a, 0x50, + 0x53, 0x1b, 0x4b, 0xa9, 0xac, 0x16, 0x53, 0xd9, 0x52, 0xd8, 0x19, 0x0b, 0x79, 0x50, 0xcd, 0xf5, + 0x22, 0x4f, 0xef, 0xfe, 0x42, 0xa7, 0xf7, 0x48, 0x69, 0x4a, 0x9e, 0x5c, 0x00, 0xf5, 0x59, 0xb6, + 0xa4, 0xe0, 0x83, 0x85, 0x04, 0x8d, 0x3c, 0x7f, 0x42, 0x33, 0xf3, 0x36, 0x15, 0x93, 0x78, 0xea, + 0x75, 0x99, 0x99, 0x06, 0x62, 0x80, 0x9d, 0x44, 0x11, 0xe7, 0x7f, 0x98, 0x06, 0x22, 0x2b, 0xcd, + 0x9b, 0x06, 0x42, 0x2e, 0x9e, 0x06, 0xa2, 0x26, 0x7f, 0x1a, 0x4c, 0xb6, 0x28, 0x1c, 0x1c, 0xfe, + 0xbc, 0x06, 0x77, 0x5a, 0xcc, 0x41, 0xe7, 0xb0, 0x92, 0xbc, 0x1e, 0xd1, 0x93, 0xdc, 0xf1, 0x93, + 0x8d, 0x6c, 0xfa, 0x87, 0xc5, 0xc0, 0x42, 0x6f, 0xac, 0xd3, 0x70, 0xed, 0x02, 0x3a, 0xe3, 0xac, + 0x57, 0x40, 0x27, 0x95, 0x00, 0x91, 0x07, 0xab, 0xa9, 0x94, 0x87, 0xf6, 0x67, 0x15, 0x67, 0x62, + 0xa2, 0x6e, 0x16, 0x85, 0x4b, 0xb5, 0x1e, 0xdc, 0x8b, 0x33, 0x22, 0x7a, 0x3c, 0xa3, 0x76, 0x2a, + 0x5e, 0xea, 0x4f, 0x0a, 0x61, 0x27, 0x45, 0xc2, 0x6c, 0x39, 0x57, 0x24, 0x15, 0x4b, 0xe7, 0x8a, + 0xa4, 0xc3, 0x2a, 0xa2, 0x70, 0x3f, 0x9d, 0x47, 0xd1, 0xac, 0x93, 0x50, 0x44, 0x5a, 0xdd, 0x2a, + 0x8c, 0x97, 0x82, 0x43, 0x58, 0x9b, 0x4c, 0xb2, 0xe8, 0xe9, 0x5c, 0x8a, 0xa9, 0x40, 0xac, 0x1f, + 0x2c, 0x50, 0x21, 0x65, 0x7f, 0xd0, 0x60, 0x5d, 0x91, 0x81, 0xd1, 0xc7, 0x73, 0xa9, 0x54, 0xa1, + 0x5a, 0x3f, 0x5a, 0xb4, 0x2c, 0xa7, 0x0d, 0x99, 0xa3, 0x0b, 0xb7, 0x31, 0x19, 0xcc, 0x0b, 0xb7, + 0x31, 0x15, 0xd7, 0xd1, 0x4f, 0x1a, 0x6c, 0xaa, 0x83, 0x38, 0xfa, 0xb4, 0x20, 0x65, 0x26, 0xdf, + 0xeb, 0x9f, 0xdd, 0xa2, 0x52, 0xf6, 0xf3, 0x5a, 0x83, 0x52, 0x4e, 0x9c, 0x47, 0xf3, 0x69, 0xf3, + 0xbe, 0x13, 0xf4, 0x67, 0xb7, 0x29, 0x95, 0x2d, 0xfd, 0xa8, 0xc1, 0x86, 0xea, 0xcb, 0x00, 0x1d, + 0x15, 0x24, 0x9d, 0xfa, 0xd8, 0xd0, 0x3f, 0x59, 0xb8, 0x4e, 0x76, 0x72, 0x05, 0xef, 0x4f, 0x65, + 0x7b, 0x34, 0xeb, 0x02, 0xa8, 0x3f, 0x55, 0xf4, 0xc3, 0x45, 0x4a, 0xa4, 0x72, 0x00, 0x0f, 0x26, + 0xe6, 0x20, 0xb2, 0x66, 0x93, 0x64, 0x3e, 0x30, 0xf4, 0xa7, 0xc5, 0x0b, 0x26, 0xdc, 0xa6, 0x67, + 0xd7, 0x3c, 0xb7, 0x8a, 0x51, 0x3c, 0xcf, 0xad, 0x6a, 0x34, 0x36, 0xc8, 0x9b, 0x6b, 0x43, 0x7b, + 0x7b, 0x6d, 0x68, 0x7f, 0x5f, 0x1b, 0xda, 0xeb, 0x1b, 0x63, 0xe9, 0xed, 0x8d, 0xb1, 0xf4, 0xe7, + 0x8d, 0xb1, 0x04, 0x15, 0x97, 0xe6, 0xf0, 0x9d, 0x68, 0xdf, 0x98, 0x8e, 0xcb, 0x2f, 0x86, 0x5d, + 0xb3, 0x47, 0xfb, 0xd6, 0x18, 0xb4, 0xef, 0xd2, 0xd4, 0x93, 0x75, 0x95, 0xfc, 0x32, 0xd2, 0x5d, + 0x8e, 0x7e, 0x10, 0xf9, 0xe8, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x23, 0x27, 0xbe, 0x30, 0xff, + 0x11, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. From 17cdc10314204f4bb8a9e209ee29d8e9a9f94574 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 29 Aug 2023 17:10:32 -0600 Subject: [PATCH 042/309] [1658]: Implement GetSigners and ValidateBasic for the gov props. --- x/exchange/msg.go | 68 ++++- x/exchange/msg_test.go | 618 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 673 insertions(+), 13 deletions(-) create mode 100644 x/exchange/msg_test.go diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 7f1ce4aa34..4fc37c2948 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -1,6 +1,11 @@ package exchange -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + "errors" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) var allRequestMsgs = []sdk.Msg{ (*MsgCreateAskRequest)(nil), @@ -141,31 +146,68 @@ func (m MsgMarketManageReqAttrsRequest) GetSigners() []sdk.AccAddress { } func (m MsgGovCreateMarketRequest) ValidateBasic() error { - // TODO[1658]: MsgGovCreateMarketRequest.ValidateBasic() - return nil + errs := make([]error, 0, 2) + if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil { + errs = append(errs, fmt.Errorf("invalid authority: %w", err)) + } + errs = append(errs, m.Market.Validate()) + return errors.Join(errs...) } func (m MsgGovCreateMarketRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgGovCreateMarketRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Authority) + return []sdk.AccAddress{addr} } func (m MsgGovManageFeesRequest) ValidateBasic() error { - // TODO[1658]: MsgGovManageFeesRequest.ValidateBasic() - return nil + var errs []error + if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil { + errs = append(errs, fmt.Errorf("invalid authority: %w", err)) + } + + if m.HasUpdates() { + errs = append(errs, + ValidateAddRemoveFeeOptions("create ask flat fee", m.AddFeeCreateAskFlat, m.RemoveFeeCreateAskFlat), + ValidateAddRemoveFeeOptions("create bid flat fee", m.AddFeeCreateBidFlat, m.RemoveFeeCreateBidFlat), + ValidateAddRemoveFeeOptions("seller settlement flat fee", m.AddFeeSettlementSellerFlat, m.RemoveFeeSettlementSellerFlat), + ValidateSellerFeeRatios(m.AddFeeSettlementSellerRatios), + ValidateDisjointFeeRatios("seller settlement fee", m.AddFeeSettlementSellerRatios, m.RemoveFeeSettlementSellerRatios), + ValidateAddRemoveFeeOptions("buyer settlement flat fee", m.AddFeeSettlementBuyerFlat, m.RemoveFeeSettlementBuyerFlat), + ValidateBuyerFeeRatios(m.AddFeeSettlementBuyerRatios), + ValidateDisjointFeeRatios("buyer settlement fee", m.AddFeeSettlementBuyerRatios, m.RemoveFeeSettlementBuyerRatios), + ) + } else { + errs = append(errs, errors.New("no updates")) + } + + return errors.Join(errs...) +} + +// HasUpdates returns true if this has at least one fee change, false if devoid of updates. +func (m MsgGovManageFeesRequest) HasUpdates() bool { + return len(m.AddFeeCreateAskFlat) > 0 || len(m.RemoveFeeCreateAskFlat) > 0 || + len(m.AddFeeCreateBidFlat) > 0 || len(m.RemoveFeeCreateBidFlat) > 0 || + len(m.AddFeeSettlementSellerFlat) > 0 || len(m.RemoveFeeSettlementSellerFlat) > 0 || + len(m.AddFeeSettlementSellerRatios) > 0 || len(m.RemoveFeeSettlementSellerRatios) > 0 || + len(m.AddFeeSettlementBuyerFlat) > 0 || len(m.RemoveFeeSettlementBuyerFlat) > 0 || + len(m.AddFeeSettlementBuyerRatios) > 0 || len(m.RemoveFeeSettlementBuyerRatios) > 0 } func (m MsgGovManageFeesRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgGovManageFeesRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Authority) + return []sdk.AccAddress{addr} } func (m MsgGovUpdateParamsRequest) ValidateBasic() error { - // TODO[1658]: MsgGovUpdateParamsRequest.ValidateBasic() - return nil + errs := make([]error, 0, 2) + if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil { + errs = append(errs, fmt.Errorf("invalid authority: %w", err)) + } + errs = append(errs, m.Params.Validate()) + return errors.Join(errs...) } func (m MsgGovUpdateParamsRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgGovUpdateParamsRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Authority) + return []sdk.AccAddress{addr} } diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go new file mode 100644 index 0000000000..c239501d7f --- /dev/null +++ b/x/exchange/msg_test.go @@ -0,0 +1,618 @@ +package exchange + +import ( + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestAllMsgsGetSigners(t *testing.T) { + // getTypeName gets just the type name of the provided thing, e.g. "MsgGovCreateMarketRequest". + getTypeName := func(thing interface{}) string { + rv := fmt.Sprintf("%T", thing) // e.g. "*types.MsgGovCreateMarketRequest" + lastDot := strings.LastIndex(rv, ".") + if lastDot < 0 || lastDot+1 >= len(rv) { + return rv + } + return rv[lastDot+1:] + } + + testAddr := sdk.AccAddress("testAddr____________") + badAddrStr := "badaddr" + badAddrErr := "decoding bech32 failed: invalid bech32 string length 7" + emptyAddrErr := "empty address string is not allowed" + + msgMakers := []func(signer string) sdk.Msg{ + func(signer string) sdk.Msg { + return &MsgGovCreateMarketRequest{Authority: signer} + }, + func(signer string) sdk.Msg { + return &MsgGovManageFeesRequest{Authority: signer} + }, + func(signer string) sdk.Msg { + return &MsgGovUpdateParamsRequest{Authority: signer} + }, + } + + signerCases := []struct { + name string + msgSigner string + expSigners []sdk.AccAddress + expPanic string + }{ + { + name: "no signer", + msgSigner: "", + expPanic: emptyAddrErr, + }, + { + name: "good signer", + msgSigner: testAddr.String(), + expSigners: []sdk.AccAddress{testAddr}, + }, + { + name: "bad signer", + msgSigner: badAddrStr, + expPanic: badAddrErr, + }, + } + + type testCase struct { + name string + msg sdk.Msg + expSigners []sdk.AccAddress + expPanic string + } + + var tests []testCase + hasMaker := make(map[string]bool) + + for _, msgMaker := range msgMakers { + typeName := getTypeName(msgMaker("")) + hasMaker[typeName] = true + for _, tc := range signerCases { + tests = append(tests, testCase{ + name: typeName + " " + tc.name, + msg: msgMaker(tc.msgSigner), + expSigners: tc.expSigners, + expPanic: tc.expPanic, + }) + } + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var signers []sdk.AccAddress + testGetSigners := func() { + signers = tc.msg.GetSigners() + } + + // TODO: Refactor to one of the new testutils panic assertions. + if len(tc.expPanic) > 0 { + require.PanicsWithError(t, tc.expPanic, testGetSigners, "GetSigners") + } else { + require.NotPanics(t, testGetSigners, "GetSigners") + } + assert.Equal(t, tc.expSigners, signers, "GetSigners") + }) + } + + // Make sure all of the GetSigners and GetSignerStrs funcs are tested. + t.Run("all msgs have test case", func(t *testing.T) { + for _, msg := range allRequestMsgs { + typeName := getTypeName(msg) + // If this fails, a maker needs to be defined above for the missing msg type. + assert.True(t, hasMaker[typeName], "whether a GetSigners test exists for %s", typeName) + } + }) +} + +func testValidateBasic(t *testing.T, msg sdk.Msg, expErr []string) { + t.Helper() + var err error + testFunc := func() { + err = msg.ValidateBasic() + } + require.NotPanics(t, testFunc, "%T.ValidateBasic()", msg) + + // TODO[1658]: Refactor to testutils.AssertErrorContents(t, err, tc.expErr, "%T.ValidateBasic() error", msg) + if len(expErr) > 0 { + if assert.Error(t, err, "%T.ValidateBasic() error", msg) { + for _, exp := range expErr { + assert.ErrorContains(t, err, exp, "%T.ValidateBasic() error", msg) + } + } + } else { + assert.NoError(t, err, "%T.ValidateBasic() error", msg) + } +} + +// TODO[1658]: func TestMsgCreateAskRequest_ValidateBasic(t *testing.T) + +// TODO[1658]: func TestMsgCreateBidRequest_ValidateBasic(t *testing.T) + +// TODO[1658]: func TestMsgCancelOrderRequest_ValidateBasic(t *testing.T) + +// TODO[1658]: func TestMsgFillBidsRequest_ValidateBasic(t *testing.T) + +// TODO[1658]: func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) + +// TODO[1658]: func TestMsgMarketSettleRequest_ValidateBasic(t *testing.T) + +// TODO[1658]: func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) + +// TODO[1658]: func TestMsgMarketUpdateDetailsRequest_ValidateBasic(t *testing.T) + +// TODO[1658]: func TestMsgMarketUpdateEnabledRequest_ValidateBasic(t *testing.T) + +// TODO[1658]: func TestMsgMarketUpdateUserSettleRequest_ValidateBasic(t *testing.T) + +// TODO[1658]: func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) + +// TODO[1658]: func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) + +func TestMsgGovCreateMarketRequest_ValidateBasic(t *testing.T) { + authority := sdk.AccAddress("authority___________").String() + + validMarket := Market{ + MarketId: 1, + MarketDetails: MarketDetails{ + Name: "Test Market One", + Description: "This is the first test market", + }, + FeeCreateAskFlat: []sdk.Coin{sdk.NewInt64Coin("nhash", 10)}, + FeeCreateBidFlat: []sdk.Coin{sdk.NewInt64Coin("nhash", 20)}, + FeeSettlementSellerFlat: []sdk.Coin{sdk.NewInt64Coin("nhash", 50)}, + FeeSettlementSellerRatios: []FeeRatio{ + {Price: sdk.NewInt64Coin("nhash", 100), Fee: sdk.NewInt64Coin("nhash", 1)}, + }, + FeeSettlementBuyerFlat: []sdk.Coin{sdk.NewInt64Coin("nhash", 100)}, + FeeSettlementBuyerRatios: []FeeRatio{ + {Price: sdk.NewInt64Coin("nhash", 100), Fee: sdk.NewInt64Coin("nhash", 1)}, + }, + AcceptingOrders: true, + AllowUserSettlement: true, + AccessGrants: []*AccessGrant{ + { + Address: sdk.AccAddress("just_some_addr______").String(), + Permissions: AllPermissions(), + }, + }, + ReqAttrCreateAsk: []string{"one.attr.pb"}, + ReqAttrCreateBid: []string{"*.attr.pb"}, + } + + tests := []struct { + name string + msg MsgGovCreateMarketRequest + expErr []string + }{ + { + name: "zero value", + msg: MsgGovCreateMarketRequest{}, + expErr: []string{"invalid authority"}, + }, + { + name: "control", + msg: MsgGovCreateMarketRequest{ + Authority: authority, + Market: validMarket, + }, + expErr: nil, + }, + { + name: "no authority", + msg: MsgGovCreateMarketRequest{ + Authority: "", + Market: validMarket, + }, + expErr: []string{"invalid authority", "empty address string is not allowed"}, + }, + { + name: "bad authority", + msg: MsgGovCreateMarketRequest{ + Authority: "bad", + Market: validMarket, + }, + expErr: []string{"invalid authority", "decoding bech32 failed"}, + }, + { + name: "invalid market", + msg: MsgGovCreateMarketRequest{ + Authority: authority, + Market: Market{ + FeeCreateAskFlat: []sdk.Coin{{Denom: "badbad", Amount: sdkmath.NewInt(0)}}, + }, + }, + expErr: []string{`invalid create ask flat fee option "0badbad": amount cannot be zero`}, + }, + { + name: "multiple errors", + msg: MsgGovCreateMarketRequest{ + Authority: "", + Market: Market{ + FeeCreateBidFlat: []sdk.Coin{{Denom: "badbad", Amount: sdkmath.NewInt(0)}}, + }, + }, + expErr: []string{ + "invalid authority", + `invalid create bid flat fee option "0badbad": amount cannot be zero`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} + +func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { + authority := sdk.AccAddress("authority___________").String() + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + ratio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { + return FeeRatio{Price: coin(priceAmount, priceDenom), Fee: coin(feeAmount, feeDenom)} + } + + tests := []struct { + name string + msg MsgGovManageFeesRequest + expErr []string + }{ + { + name: "zero value", + msg: MsgGovManageFeesRequest{}, + expErr: []string{"invalid authority", "no updates"}, + }, + { + name: "no authority", + msg: MsgGovManageFeesRequest{ + Authority: "", + AddFeeCreateAskFlat: []sdk.Coin{coin(1, "nhash")}, + }, + expErr: []string{"invalid authority", "empty address string is not allowed"}, + }, + { + name: "bad authority", + msg: MsgGovManageFeesRequest{ + Authority: "bad", + AddFeeCreateAskFlat: []sdk.Coin{coin(1, "nhash")}, + }, + expErr: []string{"invalid authority", "decoding bech32 failed"}, + }, + { + name: "invalid add create ask flat", + msg: MsgGovManageFeesRequest{ + Authority: authority, + AddFeeCreateAskFlat: []sdk.Coin{coin(0, "nhash")}, + }, + expErr: []string{`invalid create ask flat fee to add option "0nhash": amount cannot be zero`}, + }, + { + name: "same add and remove create ask flat", + msg: MsgGovManageFeesRequest{ + Authority: authority, + AddFeeCreateAskFlat: []sdk.Coin{coin(1, "nhash")}, + RemoveFeeCreateAskFlat: []sdk.Coin{coin(1, "nhash")}, + }, + expErr: []string{"cannot add and remove the same create ask flat fee options: 1nhash"}, + }, + { + name: "invalid add create bid flat", + msg: MsgGovManageFeesRequest{ + Authority: authority, + AddFeeCreateBidFlat: []sdk.Coin{coin(0, "nhash")}, + }, + expErr: []string{`invalid create bid flat fee to add option "0nhash": amount cannot be zero`}, + }, + { + name: "same add and remove create bid flat", + msg: MsgGovManageFeesRequest{ + Authority: authority, + AddFeeCreateBidFlat: []sdk.Coin{coin(1, "nhash")}, + RemoveFeeCreateBidFlat: []sdk.Coin{coin(1, "nhash")}, + }, + expErr: []string{"cannot add and remove the same create bid flat fee options: 1nhash"}, + }, + { + name: "invalid add settlement seller flat", + msg: MsgGovManageFeesRequest{ + Authority: authority, + AddFeeSettlementSellerFlat: []sdk.Coin{coin(0, "nhash")}, + }, + expErr: []string{`invalid seller settlement flat fee to add option "0nhash": amount cannot be zero`}, + }, + { + name: "same add and remove settlement seller flat", + msg: MsgGovManageFeesRequest{ + Authority: authority, + AddFeeSettlementSellerFlat: []sdk.Coin{coin(1, "nhash")}, + RemoveFeeSettlementSellerFlat: []sdk.Coin{coin(1, "nhash")}, + }, + expErr: []string{"cannot add and remove the same seller settlement flat fee options: 1nhash"}, + }, + { + name: "invalid add settlement seller ratio", + msg: MsgGovManageFeesRequest{ + Authority: authority, + AddFeeSettlementSellerRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, + }, + expErr: []string{`seller fee ratio fee amount "2nhash" cannot be greater than price amount "1nhash"`}, + }, + { + name: "same add and remove settlement seller ratio", + msg: MsgGovManageFeesRequest{ + Authority: authority, + AddFeeSettlementSellerRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, + RemoveFeeSettlementSellerRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, + }, + expErr: []string{"cannot add and remove the same seller settlement fee ratios: 2nhash:1nhash"}, + }, + { + name: "invalid add settlement buyer flat", + msg: MsgGovManageFeesRequest{ + Authority: authority, + AddFeeSettlementBuyerFlat: []sdk.Coin{coin(0, "nhash")}, + }, + expErr: []string{`invalid buyer settlement flat fee to add option "0nhash": amount cannot be zero`}, + }, + { + name: "same add and remove settlement buyer flat", + msg: MsgGovManageFeesRequest{ + Authority: authority, + AddFeeSettlementBuyerFlat: []sdk.Coin{coin(1, "nhash")}, + RemoveFeeSettlementBuyerFlat: []sdk.Coin{coin(1, "nhash")}, + }, + expErr: []string{"cannot add and remove the same buyer settlement flat fee options: 1nhash"}, + }, + { + name: "invalid add settlement buyer ratio", + msg: MsgGovManageFeesRequest{ + Authority: authority, + AddFeeSettlementBuyerRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, + }, + expErr: []string{`buyer fee ratio fee amount "2nhash" cannot be greater than price amount "1nhash"`}, + }, + { + name: "same add and remove settlement buyer ratio", + msg: MsgGovManageFeesRequest{ + Authority: authority, + AddFeeSettlementBuyerRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, + RemoveFeeSettlementBuyerRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, + }, + expErr: []string{"cannot add and remove the same buyer settlement fee ratios: 2nhash:1nhash"}, + }, + { + name: "multiple errors", + msg: MsgGovManageFeesRequest{ + Authority: "", + AddFeeCreateAskFlat: []sdk.Coin{coin(0, "nhash")}, + RemoveFeeCreateAskFlat: []sdk.Coin{coin(0, "nhash")}, + AddFeeCreateBidFlat: []sdk.Coin{coin(0, "nhash")}, + RemoveFeeCreateBidFlat: []sdk.Coin{coin(0, "nhash")}, + AddFeeSettlementSellerFlat: []sdk.Coin{coin(0, "nhash")}, + RemoveFeeSettlementSellerFlat: []sdk.Coin{coin(0, "nhash")}, + AddFeeSettlementSellerRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, + RemoveFeeSettlementSellerRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, + AddFeeSettlementBuyerFlat: []sdk.Coin{coin(0, "nhash")}, + RemoveFeeSettlementBuyerFlat: []sdk.Coin{coin(0, "nhash")}, + AddFeeSettlementBuyerRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, + RemoveFeeSettlementBuyerRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, + }, + expErr: []string{ + "invalid authority", "empty address string is not allowed", + `invalid create ask flat fee to add option "0nhash": amount cannot be zero`, + "cannot add and remove the same create ask flat fee options: 0nhash", + `invalid create bid flat fee to add option "0nhash": amount cannot be zero`, + "cannot add and remove the same create bid flat fee options: 0nhash", + `invalid seller settlement flat fee to add option "0nhash": amount cannot be zero`, + "cannot add and remove the same seller settlement flat fee options: 0nhash", + `seller fee ratio fee amount "2nhash" cannot be greater than price amount "1nhash"`, + "cannot add and remove the same seller settlement fee ratios: 1nhash:2nhash", + `invalid buyer settlement flat fee to add option "0nhash": amount cannot be zero`, + "cannot add and remove the same buyer settlement flat fee options: 0nhash", + `buyer fee ratio fee amount "2nhash" cannot be greater than price amount "1nhash"`, + "cannot add and remove the same buyer settlement fee ratios: 1nhash:2nhash", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} + +func TestMsgGovManageFeesRequest_HasUpdates(t *testing.T) { + oneCoin := []sdk.Coin{{}} + oneRatio := []FeeRatio{{}} + + tests := []struct { + name string + msg MsgGovManageFeesRequest + exp bool + }{ + { + name: "empty", + msg: MsgGovManageFeesRequest{}, + exp: false, + }, + { + name: "empty except for authority", + msg: MsgGovManageFeesRequest{ + Authority: "authority", + }, + exp: false, + }, + { + name: "one add fee create ask flat", + msg: MsgGovManageFeesRequest{AddFeeCreateAskFlat: oneCoin}, + exp: true, + }, + { + name: "one remove fee create ask flat", + msg: MsgGovManageFeesRequest{RemoveFeeCreateAskFlat: oneCoin}, + exp: true, + }, + { + name: "one add fee create bid flat", + msg: MsgGovManageFeesRequest{AddFeeCreateBidFlat: oneCoin}, + exp: true, + }, + { + name: "one remove fee create bid flat", + msg: MsgGovManageFeesRequest{RemoveFeeCreateBidFlat: oneCoin}, + exp: true, + }, + { + name: "one add fee settlement seller flat", + msg: MsgGovManageFeesRequest{AddFeeSettlementSellerFlat: oneCoin}, + exp: true, + }, + { + name: "one remove fee settlement seller flat", + msg: MsgGovManageFeesRequest{RemoveFeeSettlementSellerFlat: oneCoin}, + exp: true, + }, + { + name: "one add fee settlement seller ratio", + msg: MsgGovManageFeesRequest{AddFeeSettlementSellerRatios: oneRatio}, + exp: true, + }, + { + name: "one remove fee settlement seller ratio", + msg: MsgGovManageFeesRequest{RemoveFeeSettlementSellerRatios: oneRatio}, + exp: true, + }, + { + name: "one add fee settlement buyer flat", + msg: MsgGovManageFeesRequest{AddFeeSettlementBuyerFlat: oneCoin}, + exp: true, + }, + { + name: "one remove fee settlement buyer flat", + msg: MsgGovManageFeesRequest{RemoveFeeSettlementBuyerFlat: oneCoin}, + exp: true, + }, + { + name: "one add fee settlement buyer ratio", + msg: MsgGovManageFeesRequest{AddFeeSettlementBuyerRatios: oneRatio}, + exp: true, + }, + { + name: "one remove fee settlement buyer ratio", + msg: MsgGovManageFeesRequest{RemoveFeeSettlementBuyerRatios: oneRatio}, + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.msg.HasUpdates() + } + require.NotPanics(t, testFunc, "%T.HasUpdates()", tc.msg) + assert.Equal(t, tc.exp, actual, "%T.HasUpdates() result", tc.msg) + }) + } +} + +func TestMsgGovUpdateParamsRequest_ValidateBasic(t *testing.T) { + authority := sdk.AccAddress("authority___________").String() + + tests := []struct { + name string + msg MsgGovUpdateParamsRequest + expErr []string + }{ + { + name: "zero value", + msg: MsgGovUpdateParamsRequest{}, + expErr: []string{"invalid authority", "empty address string is not allowed"}, + }, + { + name: "default params", + msg: MsgGovUpdateParamsRequest{ + Authority: authority, + Params: *DefaultParams(), + }, + expErr: nil, + }, + { + name: "control", + msg: MsgGovUpdateParamsRequest{ + Authority: authority, + Params: Params{ + DefaultSplit: 543, + DenomSplits: []DenomSplit{ + {Denom: "nhash", Split: 222}, + {Denom: "nusdf", Split: 123}, + {Denom: "musdm", Split: 8}, + }, + }, + }, + expErr: nil, + }, + { + name: "no authority", + msg: MsgGovUpdateParamsRequest{ + Authority: "", + Params: *DefaultParams(), + }, + expErr: []string{"invalid authority", "empty address string is not allowed"}, + }, + { + name: "bad authority", + msg: MsgGovUpdateParamsRequest{ + Authority: "bad", + Params: *DefaultParams(), + }, + expErr: []string{"invalid authority", "decoding bech32 failed"}, + }, + { + name: "bad params", + msg: MsgGovUpdateParamsRequest{ + Authority: authority, + Params: Params{ + DefaultSplit: 10_123, + DenomSplits: []DenomSplit{ + {Denom: "x", Split: 500}, + {Denom: "nhash", Split: 20_000}, + }, + }, + }, + expErr: []string{ + "default split 10123 cannot be greater than 10000", + "nhash split 20000 cannot be greater than 10000", + }, + }, + { + name: "multiple errors", + msg: MsgGovUpdateParamsRequest{ + Authority: "", + Params: Params{DefaultSplit: 10_555}, + }, + expErr: []string{ + "invalid authority", + "default split 10555 cannot be greater than 10000", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} From 1f05614571d62c5417a8c79691b669eb99878c9f Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 29 Aug 2023 17:16:01 -0600 Subject: [PATCH 043/309] [1658]: Make the AccessGrants list unable to hold nil values. --- proto/provenance/exchange/v1/market.proto | 2 +- x/exchange/genesis_test.go | 8 +- x/exchange/market.go | 6 +- x/exchange/market.pb.go | 126 +++++++++++----------- x/exchange/market_test.go | 33 +++--- x/exchange/msg_test.go | 2 +- 6 files changed, 84 insertions(+), 93 deletions(-) diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index d74b179346..a684519687 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -76,7 +76,7 @@ message Market { // The MarketSettle endpoint is only available to market actors regardless of the value of this field. bool allow_user_settlement = 10; // access_grants is the list of addresses and permissions granted for this market. - repeated AccessGrant access_grants = 11; + repeated AccessGrant access_grants = 11 [(gogoproto.nullable) = false]; // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. // // An entry that starts with "*." will match any attributes that end with the rest of it. diff --git a/x/exchange/genesis_test.go b/x/exchange/genesis_test.go index 7515c76363..a21aecb362 100644 --- a/x/exchange/genesis_test.go +++ b/x/exchange/genesis_test.go @@ -62,7 +62,7 @@ func TestNewGenesisState(t *testing.T) { MarketDetails: MarketDetails{Name: "Market Two"}, FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, - AccessGrants: []*AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, + AccessGrants: []AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, ReqAttrCreateBid: []string{"just.some.attr"}, }, }, @@ -80,7 +80,7 @@ func TestNewGenesisState(t *testing.T) { MarketDetails: MarketDetails{Name: "Market Two"}, FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, - AccessGrants: []*AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, + AccessGrants: []AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, ReqAttrCreateBid: []string{"just.some.attr"}, }, }, @@ -143,7 +143,7 @@ func TestNewGenesisState(t *testing.T) { MarketDetails: MarketDetails{Name: "Market Two"}, FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, - AccessGrants: []*AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, + AccessGrants: []AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, ReqAttrCreateBid: []string{"just.some.attr"}, }, }, @@ -181,7 +181,7 @@ func TestNewGenesisState(t *testing.T) { MarketDetails: MarketDetails{Name: "Market Two"}, FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, - AccessGrants: []*AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, + AccessGrants: []AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, ReqAttrCreateBid: []string{"just.some.attr"}, }, }, diff --git a/x/exchange/market.go b/x/exchange/market.go index 7a07391737..3d91a6835b 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -302,15 +302,11 @@ func ValidateAddRemoveFeeOptions(field string, toAdd, toRemove []sdk.Coin) error } // ValidateAccessGrants returns an error if any of the provided access grants are invalid. -func ValidateAccessGrants(accessGrants []*AccessGrant) error { +func ValidateAccessGrants(accessGrants []AccessGrant) error { errs := make([]error, len(accessGrants)) seen := make(map[string]bool) dups := make(map[string]bool) for i, ag := range accessGrants { - if ag == nil { - errs[i] = errors.New("nil access grant not allowed") - continue - } if seen[ag.Address] && !dups[ag.Address] { errs[i] = fmt.Errorf("%s appears in multiple access grant entries", ag.Address) dups[ag.Address] = true diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index 0ef6a9a410..42f83d11e7 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -225,7 +225,7 @@ type Market struct { // The MarketSettle endpoint is only available to market actors regardless of the value of this field. AllowUserSettlement bool `protobuf:"varint,10,opt,name=allow_user_settlement,json=allowUserSettlement,proto3" json:"allow_user_settlement,omitempty"` // access_grants is the list of addresses and permissions granted for this market. - AccessGrants []*AccessGrant `protobuf:"bytes,11,rep,name=access_grants,json=accessGrants,proto3" json:"access_grants,omitempty"` + AccessGrants []AccessGrant `protobuf:"bytes,11,rep,name=access_grants,json=accessGrants,proto3" json:"access_grants"` // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. // // An entry that starts with "*." will match any attributes that end with the rest of it. @@ -345,7 +345,7 @@ func (m *Market) GetAllowUserSettlement() bool { return false } -func (m *Market) GetAccessGrants() []*AccessGrant { +func (m *Market) GetAccessGrants() []AccessGrant { if m != nil { return m.AccessGrants } @@ -490,69 +490,69 @@ func init() { } var fileDescriptor_d5cf198f1dd7e167 = []byte{ - // 980 bytes of a gzipped FileDescriptorProto + // 982 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x41, 0x4f, 0x1b, 0x47, 0x14, 0xf6, 0x62, 0x03, 0xf6, 0x18, 0x88, 0x3b, 0x24, 0x74, 0x6d, 0x2a, 0x7b, 0xeb, 0x28, 0x92, - 0xd3, 0x0a, 0x5b, 0x50, 0xf5, 0xc2, 0xcd, 0x06, 0xd3, 0x58, 0x4a, 0x08, 0x5a, 0xdb, 0x8a, 0x14, + 0xd3, 0x0a, 0x5b, 0x50, 0xf5, 0xc2, 0xcd, 0x06, 0xd3, 0x5a, 0x4a, 0x08, 0x5a, 0xdb, 0x8a, 0x14, 0x55, 0xda, 0xce, 0xee, 0x3e, 0x9b, 0x11, 0xeb, 0x5d, 0x67, 0x66, 0x16, 0x92, 0xde, 0x7a, 0x6a, - 0xc5, 0xa9, 0xc7, 0x5e, 0x90, 0xd2, 0x6b, 0xcf, 0xfd, 0x11, 0x39, 0xa2, 0x9e, 0x7a, 0x42, 0x15, - 0x5c, 0x7b, 0xea, 0x2f, 0xa8, 0x76, 0x66, 0xb1, 0x37, 0xd4, 0x88, 0x44, 0xbd, 0xed, 0x7b, 0xdf, - 0xf7, 0xbe, 0xf7, 0xbd, 0x37, 0xc3, 0x60, 0xf4, 0x70, 0xcc, 0x82, 0x63, 0xf0, 0x89, 0xef, 0x40, - 0x03, 0x5e, 0x3b, 0x87, 0xc4, 0x1f, 0x42, 0xe3, 0x78, 0xb3, 0x31, 0x22, 0xec, 0x08, 0x44, 0x7d, - 0xcc, 0x02, 0x11, 0xe0, 0xb5, 0x29, 0xa9, 0x7e, 0x4d, 0xaa, 0x1f, 0x6f, 0x96, 0xca, 0x4e, 0xc0, - 0x47, 0x01, 0x6f, 0x90, 0x50, 0x1c, 0x36, 0x8e, 0x37, 0x6d, 0x10, 0x64, 0x53, 0x06, 0xaa, 0x6e, - 0x82, 0xdb, 0x84, 0xc3, 0x04, 0x77, 0x02, 0xea, 0xc7, 0x78, 0x51, 0xe1, 0x96, 0x8c, 0x1a, 0x2a, - 0x88, 0xa1, 0xfb, 0xc3, 0x60, 0x18, 0xa8, 0x7c, 0xf4, 0xa5, 0xb2, 0xd5, 0xbf, 0x35, 0xb4, 0xfc, - 0x4c, 0x3a, 0x6b, 0x3a, 0x4e, 0x10, 0xfa, 0x02, 0x7f, 0x87, 0x96, 0x22, 0x75, 0x8b, 0xa8, 0x58, - 0xd7, 0x0c, 0xad, 0x96, 0xdf, 0x32, 0xea, 0xb1, 0x98, 0x34, 0x13, 0x77, 0xae, 0xb7, 0x08, 0x87, - 0xb8, 0xae, 0xb5, 0x7e, 0x7e, 0x51, 0xd1, 0xfe, 0xb9, 0xa8, 0xac, 0xbe, 0x21, 0x23, 0x6f, 0xbb, - 0x9a, 0xd4, 0xa8, 0x9a, 0x79, 0x7b, 0xca, 0xc4, 0xeb, 0x28, 0xa7, 0x96, 0x61, 0x51, 0x57, 0x9f, - 0x33, 0xb4, 0xda, 0xb2, 0x99, 0x55, 0x89, 0x8e, 0x8b, 0x4d, 0xb4, 0x12, 0x83, 0x2e, 0x08, 0x42, - 0x3d, 0xae, 0xa7, 0xa5, 0x81, 0x47, 0xf5, 0xd9, 0x2b, 0xab, 0x2b, 0xf7, 0xbb, 0x8a, 0xdc, 0xca, - 0xbc, 0xbb, 0xa8, 0xa4, 0xcc, 0xe5, 0x51, 0x32, 0xb9, 0x9d, 0xfd, 0xe9, 0x6d, 0x25, 0xf5, 0xcb, - 0xdb, 0x4a, 0xaa, 0xfa, 0xc3, 0x64, 0xdc, 0x18, 0xc3, 0x18, 0x65, 0x7c, 0x32, 0x02, 0x39, 0x66, - 0xce, 0x94, 0xdf, 0xd8, 0x40, 0x79, 0x17, 0xb8, 0xc3, 0xe8, 0x58, 0xd0, 0xc0, 0x97, 0x16, 0x73, - 0x66, 0x32, 0x85, 0x2b, 0x28, 0x7f, 0x02, 0x36, 0xa7, 0x02, 0xac, 0x90, 0x79, 0xd2, 0x62, 0xce, - 0x44, 0x71, 0xaa, 0xcf, 0x3c, 0x5c, 0x44, 0x59, 0xea, 0x04, 0xbe, 0x15, 0x32, 0xaa, 0x67, 0x24, - 0xba, 0x18, 0xc5, 0x7d, 0x46, 0xab, 0xbf, 0x2d, 0xa2, 0x05, 0xe5, 0xe1, 0xfd, 0x4d, 0x68, 0x77, - 0x6e, 0x62, 0xee, 0xff, 0x6e, 0x02, 0xef, 0xa3, 0xd5, 0x01, 0x80, 0xe5, 0x30, 0x20, 0x02, 0x2c, - 0xc2, 0x8f, 0xac, 0x81, 0x47, 0x84, 0x9e, 0x36, 0xd2, 0xb5, 0xfc, 0x56, 0xf1, 0xfa, 0x8c, 0xa3, - 0xc3, 0x9a, 0x9c, 0xf1, 0x4e, 0x40, 0xfd, 0x58, 0xac, 0x30, 0x00, 0xd8, 0x91, 0xa5, 0x4d, 0x7e, - 0xb4, 0xe7, 0x11, 0x71, 0x43, 0xcf, 0xa6, 0xae, 0xd2, 0xcb, 0x7c, 0xac, 0x5e, 0x8b, 0xba, 0x52, - 0xef, 0x5b, 0x54, 0x8a, 0xf4, 0x38, 0x08, 0xe1, 0xc1, 0x08, 0x7c, 0x61, 0x71, 0xf0, 0x3c, 0x60, - 0x4a, 0x76, 0xfe, 0xc3, 0x64, 0x3f, 0x1d, 0x00, 0x74, 0x27, 0x0a, 0x5d, 0x29, 0x20, 0xd5, 0x87, - 0xe8, 0xb3, 0xd9, 0xea, 0x8c, 0x08, 0x1a, 0x70, 0x7d, 0x41, 0xea, 0x1b, 0xb7, 0xed, 0x77, 0x0f, - 0xc0, 0x8c, 0x88, 0x71, 0x9b, 0xe2, 0x8c, 0x36, 0x12, 0xe7, 0xf8, 0x25, 0x2a, 0xde, 0x68, 0x64, - 0x87, 0x6f, 0xae, 0xa7, 0x58, 0xfc, 0xb0, 0x29, 0xd6, 0xde, 0x93, 0x6f, 0x45, 0xf5, 0x72, 0x08, - 0x40, 0xeb, 0x33, 0xb5, 0xe3, 0x19, 0xb2, 0x1f, 0x35, 0x83, 0xfe, 0xdf, 0x26, 0xf1, 0x08, 0x8f, - 0x51, 0x81, 0x38, 0x0e, 0x8c, 0x05, 0xf5, 0x87, 0x56, 0xc0, 0x5c, 0x60, 0x5c, 0xcf, 0x19, 0x5a, - 0x2d, 0x6b, 0xde, 0x9b, 0xe4, 0x9f, 0xcb, 0x34, 0xde, 0x42, 0x0f, 0x88, 0xe7, 0x05, 0x27, 0x56, - 0xc8, 0x81, 0x25, 0x8c, 0xe9, 0x48, 0xf2, 0x57, 0x25, 0xd8, 0xe7, 0xc0, 0xa6, 0x9d, 0xf0, 0x13, - 0xb4, 0x1c, 0xc9, 0x70, 0x6e, 0x0d, 0x19, 0xf1, 0x05, 0xd7, 0xf3, 0xd2, 0xf7, 0xc3, 0xdb, 0x7c, - 0x37, 0x25, 0xf9, 0x9b, 0x88, 0x6b, 0x2e, 0x91, 0x69, 0xc0, 0xf1, 0x06, 0x5a, 0x65, 0xf0, 0xca, - 0x22, 0x42, 0xb0, 0xc4, 0xbd, 0xd6, 0x97, 0x8c, 0x74, 0x2d, 0x67, 0x16, 0x18, 0xbc, 0x6a, 0x0a, - 0xc1, 0x26, 0xb7, 0x76, 0x16, 0xdd, 0xa6, 0xae, 0xbe, 0x3c, 0x83, 0xde, 0xa2, 0x6e, 0xf5, 0x7b, - 0x94, 0xbd, 0x5e, 0x19, 0xfe, 0x1a, 0xcd, 0x8f, 0x19, 0x75, 0x20, 0x7e, 0x12, 0xef, 0x3c, 0x41, - 0xc5, 0xc6, 0x9b, 0x28, 0x3d, 0x00, 0x88, 0xff, 0x78, 0xef, 0x2c, 0x8a, 0xb8, 0xdb, 0x19, 0xf9, - 0x58, 0xfd, 0xa8, 0xa1, 0x7c, 0x62, 0x6e, 0xbc, 0x85, 0x16, 0x89, 0xeb, 0x32, 0xe0, 0x5c, 0xbd, - 0x56, 0x2d, 0xfd, 0x8f, 0xdf, 0x37, 0xee, 0xc7, 0x7a, 0x4d, 0x85, 0x74, 0x05, 0xa3, 0xfe, 0xd0, - 0xbc, 0x26, 0xe2, 0x5d, 0x94, 0x1f, 0x03, 0x1b, 0x51, 0xce, 0x69, 0xe0, 0x47, 0x2f, 0x48, 0xba, - 0xb6, 0xb2, 0x55, 0xbd, 0x6d, 0xcb, 0x07, 0x13, 0xaa, 0x99, 0x2c, 0xfb, 0xe2, 0xd7, 0x39, 0x84, - 0xa6, 0x18, 0xfe, 0x12, 0xad, 0x1d, 0xb4, 0xcd, 0x67, 0x9d, 0x6e, 0xb7, 0xf3, 0x7c, 0xdf, 0xea, - 0xef, 0x77, 0x0f, 0xda, 0x3b, 0x9d, 0xbd, 0x4e, 0x7b, 0xb7, 0x90, 0x2a, 0xdd, 0x3b, 0x3d, 0x33, - 0xf2, 0xa1, 0xcf, 0xc7, 0xe0, 0xd0, 0x01, 0x05, 0x17, 0x7f, 0x8e, 0x3e, 0x49, 0x90, 0xbb, 0xed, - 0x5e, 0xef, 0x69, 0xbb, 0xa0, 0x95, 0xd0, 0xe9, 0x99, 0xb1, 0xa0, 0xee, 0xca, 0x0d, 0xca, 0x4e, - 0x73, 0x7f, 0xa7, 0xfd, 0xb4, 0x30, 0xa7, 0x28, 0x4e, 0x64, 0xd2, 0xc3, 0x8f, 0xd0, 0x6a, 0x82, - 0xf2, 0xa2, 0xd3, 0x7b, 0xb2, 0x6b, 0x36, 0x5f, 0x14, 0xd2, 0xa5, 0xa5, 0xd3, 0x33, 0x23, 0x7b, - 0x42, 0xc5, 0xa1, 0xcb, 0xc8, 0xc9, 0x0d, 0xa5, 0xfe, 0xc1, 0x6e, 0xb3, 0xd7, 0x2e, 0x64, 0x94, - 0x52, 0x38, 0x76, 0x89, 0x80, 0x1b, 0xe6, 0xa7, 0x9f, 0xdd, 0xc2, 0xbc, 0x32, 0x9f, 0x18, 0x1c, - 0x3f, 0x46, 0x0f, 0x12, 0xe4, 0x66, 0xaf, 0x67, 0x76, 0x5a, 0xfd, 0x5e, 0xbb, 0x5b, 0x58, 0x28, - 0xad, 0x9c, 0x9e, 0x19, 0x28, 0xba, 0x46, 0xd4, 0x0e, 0x05, 0xf0, 0x16, 0xbc, 0xbb, 0x2c, 0x6b, - 0xe7, 0x97, 0x65, 0xed, 0xaf, 0xcb, 0xb2, 0xf6, 0xf3, 0x55, 0x39, 0x75, 0x7e, 0x55, 0x4e, 0xfd, - 0x79, 0x55, 0x4e, 0xa1, 0x22, 0x0d, 0x6e, 0x59, 0xf8, 0x81, 0xf6, 0xb2, 0x3e, 0xa4, 0xe2, 0x30, - 0xb4, 0xeb, 0x4e, 0x30, 0x6a, 0x4c, 0x49, 0x1b, 0x34, 0x48, 0x44, 0x8d, 0xd7, 0x93, 0x5f, 0x12, - 0xf6, 0x82, 0xfc, 0xbf, 0xfd, 0xd5, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x66, 0xc4, 0xbd, 0xec, - 0x67, 0x08, 0x00, 0x00, + 0xc5, 0xa9, 0xc7, 0x5e, 0x90, 0xd2, 0x7f, 0xd0, 0x43, 0x7f, 0x44, 0x8e, 0xa8, 0xa7, 0x9e, 0x50, + 0x05, 0xd7, 0x9e, 0xfa, 0x0b, 0xaa, 0x9d, 0x59, 0xec, 0x0d, 0x35, 0x22, 0x51, 0x6e, 0xfb, 0xde, + 0xf7, 0xbd, 0xef, 0x7d, 0xef, 0xcd, 0x30, 0x18, 0x3d, 0x1c, 0xb3, 0xe0, 0x18, 0x7c, 0xe2, 0x3b, + 0xd0, 0x80, 0x57, 0xce, 0x21, 0xf1, 0x87, 0xd0, 0x38, 0xde, 0x6c, 0x8c, 0x08, 0x3b, 0x02, 0x51, + 0x1f, 0xb3, 0x40, 0x04, 0x78, 0x6d, 0x4a, 0xaa, 0x5f, 0x93, 0xea, 0xc7, 0x9b, 0xa5, 0xb2, 0x13, + 0xf0, 0x51, 0xc0, 0x1b, 0x24, 0x14, 0x87, 0x8d, 0xe3, 0x4d, 0x1b, 0x04, 0xd9, 0x94, 0x81, 0xaa, + 0x9b, 0xe0, 0x36, 0xe1, 0x30, 0xc1, 0x9d, 0x80, 0xfa, 0x31, 0x5e, 0x54, 0xb8, 0x25, 0xa3, 0x86, + 0x0a, 0x62, 0xe8, 0xfe, 0x30, 0x18, 0x06, 0x2a, 0x1f, 0x7d, 0xa9, 0x6c, 0xf5, 0x1f, 0x0d, 0x2d, + 0x3f, 0x95, 0xce, 0x9a, 0x8e, 0x13, 0x84, 0xbe, 0xc0, 0xdf, 0xa3, 0xa5, 0x48, 0xdd, 0x22, 0x2a, + 0xd6, 0x35, 0x43, 0xab, 0xe5, 0xb7, 0x8c, 0x7a, 0x2c, 0x26, 0xcd, 0xc4, 0x9d, 0xeb, 0x2d, 0xc2, + 0x21, 0xae, 0x6b, 0xad, 0x9f, 0x5f, 0x54, 0xb4, 0x7f, 0x2f, 0x2a, 0xab, 0xaf, 0xc9, 0xc8, 0xdb, + 0xae, 0x26, 0x35, 0xaa, 0x66, 0xde, 0x9e, 0x32, 0xf1, 0x3a, 0xca, 0xa9, 0x65, 0x58, 0xd4, 0xd5, + 0xe7, 0x0c, 0xad, 0xb6, 0x6c, 0x66, 0x55, 0xa2, 0xe3, 0x62, 0x13, 0xad, 0xc4, 0xa0, 0x0b, 0x82, + 0x50, 0x8f, 0xeb, 0x69, 0x69, 0xe0, 0x51, 0x7d, 0xf6, 0xca, 0xea, 0xca, 0xfd, 0xae, 0x22, 0xb7, + 0x32, 0x6f, 0x2f, 0x2a, 0x29, 0x73, 0x79, 0x94, 0x4c, 0x6e, 0x67, 0x7f, 0x7e, 0x53, 0x49, 0xfd, + 0xfa, 0xa6, 0x92, 0xaa, 0xfe, 0x38, 0x19, 0x37, 0xc6, 0x30, 0x46, 0x19, 0x9f, 0x8c, 0x40, 0x8e, + 0x99, 0x33, 0xe5, 0x37, 0x36, 0x50, 0xde, 0x05, 0xee, 0x30, 0x3a, 0x16, 0x34, 0xf0, 0xa5, 0xc5, + 0x9c, 0x99, 0x4c, 0xe1, 0x0a, 0xca, 0x9f, 0x80, 0xcd, 0xa9, 0x00, 0x2b, 0x64, 0x9e, 0xb4, 0x98, + 0x33, 0x51, 0x9c, 0xea, 0x33, 0x0f, 0x17, 0x51, 0x96, 0x3a, 0x81, 0x6f, 0x85, 0x8c, 0xea, 0x19, + 0x89, 0x2e, 0x46, 0x71, 0x9f, 0xd1, 0xea, 0xef, 0x8b, 0x68, 0x41, 0x79, 0x78, 0x77, 0x13, 0xda, + 0x9d, 0x9b, 0x98, 0xfb, 0xd8, 0x4d, 0xe0, 0x7d, 0xb4, 0x3a, 0x00, 0xb0, 0x1c, 0x06, 0x44, 0x80, + 0x45, 0xf8, 0x91, 0x35, 0xf0, 0x88, 0xd0, 0xd3, 0x46, 0xba, 0x96, 0xdf, 0x2a, 0x5e, 0x9f, 0x71, + 0x74, 0x58, 0x93, 0x33, 0xde, 0x09, 0xa8, 0x1f, 0x8b, 0x15, 0x06, 0x00, 0x3b, 0xb2, 0xb4, 0xc9, + 0x8f, 0xf6, 0x3c, 0x22, 0x6e, 0xe8, 0xd9, 0xd4, 0x55, 0x7a, 0x99, 0x0f, 0xd5, 0x6b, 0x51, 0x57, + 0xea, 0x7d, 0x87, 0x4a, 0x91, 0x1e, 0x07, 0x21, 0x3c, 0x18, 0x81, 0x2f, 0x2c, 0x0e, 0x9e, 0x07, + 0x4c, 0xc9, 0xce, 0xbf, 0x9f, 0xec, 0xa7, 0x03, 0x80, 0xee, 0x44, 0xa1, 0x2b, 0x05, 0xa4, 0xfa, + 0x10, 0x7d, 0x36, 0x5b, 0x9d, 0x11, 0x41, 0x03, 0xae, 0x2f, 0x48, 0x7d, 0xe3, 0xb6, 0xfd, 0xee, + 0x01, 0x98, 0x11, 0x31, 0x6e, 0x53, 0x9c, 0xd1, 0x46, 0xe2, 0x1c, 0xbf, 0x40, 0xc5, 0x1b, 0x8d, + 0xec, 0xf0, 0xf5, 0xf5, 0x14, 0x8b, 0xef, 0x37, 0xc5, 0xda, 0x3b, 0xf2, 0xad, 0xa8, 0x5e, 0x0e, + 0x01, 0x68, 0x7d, 0xa6, 0x76, 0x3c, 0x43, 0xf6, 0x83, 0x66, 0xd0, 0xff, 0xdf, 0x24, 0x1e, 0xe1, + 0x31, 0x2a, 0x10, 0xc7, 0x81, 0xb1, 0xa0, 0xfe, 0xd0, 0x0a, 0x98, 0x0b, 0x8c, 0xeb, 0x39, 0x43, + 0xab, 0x65, 0xcd, 0x7b, 0x93, 0xfc, 0x33, 0x99, 0xc6, 0x5b, 0xe8, 0x01, 0xf1, 0xbc, 0xe0, 0xc4, + 0x0a, 0x39, 0xb0, 0x84, 0x31, 0x1d, 0x49, 0xfe, 0xaa, 0x04, 0xfb, 0x1c, 0xd8, 0xb4, 0x13, 0xde, + 0x47, 0xcb, 0x91, 0x0c, 0xe7, 0xd6, 0x90, 0x11, 0x5f, 0x70, 0x3d, 0x2f, 0x7d, 0x3f, 0xbc, 0xcd, + 0x77, 0x53, 0x92, 0xbf, 0x89, 0xb8, 0xb1, 0xf5, 0x25, 0x32, 0x4d, 0x71, 0xbc, 0x81, 0x56, 0x19, + 0xbc, 0xb4, 0x88, 0x10, 0x2c, 0x71, 0xbb, 0xf5, 0x25, 0x23, 0x5d, 0xcb, 0x99, 0x05, 0x06, 0x2f, + 0x9b, 0x42, 0xb0, 0xc9, 0xdd, 0x9d, 0x45, 0xb7, 0xa9, 0xab, 0x2f, 0xcf, 0xa0, 0xb7, 0xa8, 0x5b, + 0xfd, 0x01, 0x65, 0xaf, 0x17, 0x87, 0xbf, 0x46, 0xf3, 0x63, 0x46, 0x1d, 0x88, 0x1f, 0xc6, 0x3b, + 0xcf, 0x51, 0xb1, 0xf1, 0x26, 0x4a, 0x0f, 0x00, 0xe2, 0x3f, 0xe1, 0x3b, 0x8b, 0x22, 0xee, 0x76, + 0x46, 0x3e, 0x59, 0x3f, 0x69, 0x28, 0x9f, 0x98, 0x1e, 0x6f, 0xa1, 0x45, 0xe2, 0xba, 0x0c, 0x38, + 0x57, 0x6f, 0x56, 0x4b, 0xff, 0xf3, 0x8f, 0x8d, 0xfb, 0xb1, 0x5e, 0x53, 0x21, 0x5d, 0xc1, 0xa8, + 0x3f, 0x34, 0xaf, 0x89, 0x78, 0x17, 0xe5, 0xc7, 0xc0, 0x46, 0x94, 0x73, 0x1a, 0xf8, 0xd1, 0x3b, + 0x92, 0xae, 0xad, 0x6c, 0x55, 0x6f, 0xdb, 0xf5, 0xc1, 0x84, 0x6a, 0x26, 0xcb, 0xbe, 0xf8, 0x6d, + 0x0e, 0xa1, 0x29, 0x86, 0xbf, 0x44, 0x6b, 0x07, 0x6d, 0xf3, 0x69, 0xa7, 0xdb, 0xed, 0x3c, 0xdb, + 0xb7, 0xfa, 0xfb, 0xdd, 0x83, 0xf6, 0x4e, 0x67, 0xaf, 0xd3, 0xde, 0x2d, 0xa4, 0x4a, 0xf7, 0x4e, + 0xcf, 0x8c, 0x7c, 0xe8, 0xf3, 0x31, 0x38, 0x74, 0x40, 0xc1, 0xc5, 0x9f, 0xa3, 0x4f, 0x12, 0xe4, + 0x6e, 0xbb, 0xd7, 0x7b, 0xd2, 0x2e, 0x68, 0x25, 0x74, 0x7a, 0x66, 0x2c, 0xa8, 0x1b, 0x73, 0x83, + 0xb2, 0xd3, 0xdc, 0xdf, 0x69, 0x3f, 0x29, 0xcc, 0x29, 0x8a, 0x13, 0x99, 0xf4, 0xf0, 0x23, 0xb4, + 0x9a, 0xa0, 0x3c, 0xef, 0xf4, 0xbe, 0xdd, 0x35, 0x9b, 0xcf, 0x0b, 0xe9, 0xd2, 0xd2, 0xe9, 0x99, + 0x91, 0x3d, 0xa1, 0xe2, 0xd0, 0x65, 0xe4, 0xe4, 0x86, 0x52, 0xff, 0x60, 0xb7, 0xd9, 0x6b, 0x17, + 0x32, 0x4a, 0x29, 0x1c, 0xbb, 0x44, 0xc0, 0x0d, 0xf3, 0xd3, 0xcf, 0x6e, 0x61, 0x5e, 0x99, 0x4f, + 0x0c, 0x8e, 0x1f, 0xa3, 0x07, 0x09, 0x72, 0xb3, 0xd7, 0x33, 0x3b, 0xad, 0x7e, 0xaf, 0xdd, 0x2d, + 0x2c, 0x94, 0x56, 0x4e, 0xcf, 0x0c, 0x14, 0x5d, 0x23, 0x6a, 0x87, 0x02, 0x78, 0x0b, 0xde, 0x5e, + 0x96, 0xb5, 0xf3, 0xcb, 0xb2, 0xf6, 0xf7, 0x65, 0x59, 0xfb, 0xe5, 0xaa, 0x9c, 0x3a, 0xbf, 0x2a, + 0xa7, 0xfe, 0xba, 0x2a, 0xa7, 0x50, 0x91, 0x06, 0xb7, 0x2c, 0xfc, 0x40, 0x7b, 0x51, 0x1f, 0x52, + 0x71, 0x18, 0xda, 0x75, 0x27, 0x18, 0x35, 0xa6, 0xa4, 0x0d, 0x1a, 0x24, 0xa2, 0xc6, 0xab, 0xc9, + 0xef, 0x09, 0x7b, 0x41, 0xfe, 0xf7, 0xfe, 0xea, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd5, 0xe1, + 0x8a, 0xcc, 0x6d, 0x08, 0x00, 0x00, } func (m *MarketAccount) Marshal() (dAtA []byte, err error) { @@ -1758,7 +1758,7 @@ func (m *Market) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.AccessGrants = append(m.AccessGrants, &AccessGrant{}) + m.AccessGrants = append(m.AccessGrants, AccessGrant{}) if err := m.AccessGrants[len(m.AccessGrants)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 88b1054a88..eb7f1a002b 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -59,7 +59,7 @@ func TestMarket_Validate(t *testing.T) { }, AcceptingOrders: true, AllowUserSettlement: true, - AccessGrants: []*AccessGrant{ + AccessGrants: []AccessGrant{ {Address: addr1, Permissions: AllPermissions()}, {Address: addr2, Permissions: []Permission{Permission_settle}}, }, @@ -121,7 +121,7 @@ func TestMarket_Validate(t *testing.T) { }, { name: "invalid access grants", - market: Market{AccessGrants: []*AccessGrant{{Address: "bad_addr", Permissions: AllPermissions()}}}, + market: Market{AccessGrants: []AccessGrant{{Address: "bad_addr", Permissions: AllPermissions()}}}, expErr: []string{"invalid access grant: invalid address: decoding bech32 failed: invalid separator index -1"}, }, { @@ -144,7 +144,7 @@ func TestMarket_Validate(t *testing.T) { FeeSettlementBuyerFlat: sdk.Coins{coin(-1, "leela")}, FeeSettlementSellerRatios: []FeeRatio{{Price: coin(10, "fry"), Fee: coin(1, "fry")}}, FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(100, "leela"), Fee: coin(1, "leela")}}, - AccessGrants: []*AccessGrant{{Address: "bad_addr", Permissions: AllPermissions()}}, + AccessGrants: []AccessGrant{{Address: "bad_addr", Permissions: AllPermissions()}}, ReqAttrCreateAsk: []string{"this-attr-is-bad"}, ReqAttrCreateBid: []string{"this-attr-grrrr"}, }, @@ -1509,7 +1509,7 @@ func TestValidateAccessGrants(t *testing.T) { tests := []struct { name string - grants []*AccessGrant + grants []AccessGrant exp string }{ { @@ -1519,17 +1519,12 @@ func TestValidateAccessGrants(t *testing.T) { }, { name: "empty grants", - grants: []*AccessGrant{}, + grants: []AccessGrant{}, exp: "", }, - { - name: "nil entry", - grants: []*AccessGrant{nil}, - exp: "nil access grant not allowed", - }, { name: "duplicate address", - grants: []*AccessGrant{ + grants: []AccessGrant{ {Address: addrDup, Permissions: []Permission{Permission_settle}}, {Address: addrDup, Permissions: []Permission{Permission_cancel}}, }, @@ -1537,7 +1532,7 @@ func TestValidateAccessGrants(t *testing.T) { }, { name: "three entries: all valid", - grants: []*AccessGrant{ + grants: []AccessGrant{ {Address: addr1, Permissions: AllPermissions()}, {Address: addr2, Permissions: AllPermissions()}, {Address: addr3, Permissions: AllPermissions()}, @@ -1546,7 +1541,7 @@ func TestValidateAccessGrants(t *testing.T) { }, { name: "three entries: invalid first", - grants: []*AccessGrant{ + grants: []AccessGrant{ {Address: addr1, Permissions: []Permission{-1}}, {Address: addr2, Permissions: AllPermissions()}, {Address: addr3, Permissions: AllPermissions()}, @@ -1555,7 +1550,7 @@ func TestValidateAccessGrants(t *testing.T) { }, { name: "three entries: invalid second", - grants: []*AccessGrant{ + grants: []AccessGrant{ {Address: addr1, Permissions: AllPermissions()}, {Address: addr2, Permissions: []Permission{-1}}, {Address: addr3, Permissions: AllPermissions()}, @@ -1564,7 +1559,7 @@ func TestValidateAccessGrants(t *testing.T) { }, { name: "three entries: invalid second", - grants: []*AccessGrant{ + grants: []AccessGrant{ {Address: addr1, Permissions: AllPermissions()}, {Address: addr2, Permissions: AllPermissions()}, {Address: addr3, Permissions: []Permission{-1}}, @@ -1573,7 +1568,7 @@ func TestValidateAccessGrants(t *testing.T) { }, { name: "three entries: only valid first", - grants: []*AccessGrant{ + grants: []AccessGrant{ {Address: addr1, Permissions: AllPermissions()}, {Address: addr2, Permissions: []Permission{0}}, {Address: addr3, Permissions: []Permission{-1}}, @@ -1585,7 +1580,7 @@ func TestValidateAccessGrants(t *testing.T) { }, { name: "three entries: only valid second", - grants: []*AccessGrant{ + grants: []AccessGrant{ {Address: addr1, Permissions: []Permission{0}}, {Address: addr2, Permissions: AllPermissions()}, {Address: addr3, Permissions: []Permission{-1}}, @@ -1597,7 +1592,7 @@ func TestValidateAccessGrants(t *testing.T) { }, { name: "three entries: only valid third", - grants: []*AccessGrant{ + grants: []AccessGrant{ {Address: addr1, Permissions: []Permission{0}}, {Address: addr2, Permissions: []Permission{-1}}, {Address: addr3, Permissions: AllPermissions()}, @@ -1609,7 +1604,7 @@ func TestValidateAccessGrants(t *testing.T) { }, { name: "three entries: all same address", - grants: []*AccessGrant{ + grants: []AccessGrant{ {Address: addrDup, Permissions: AllPermissions()}, {Address: addrDup, Permissions: AllPermissions()}, {Address: addrDup, Permissions: AllPermissions()}, diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index c239501d7f..673d9d3136 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -178,7 +178,7 @@ func TestMsgGovCreateMarketRequest_ValidateBasic(t *testing.T) { }, AcceptingOrders: true, AllowUserSettlement: true, - AccessGrants: []*AccessGrant{ + AccessGrants: []AccessGrant{ { Address: sdk.AccAddress("just_some_addr______").String(), Permissions: AllPermissions(), From b4a36442d070124f87ee4a033e82f7cdf2dd6e2d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 29 Aug 2023 17:34:11 -0600 Subject: [PATCH 044/309] [1658]: Stub out the MsgServer. --- x/exchange/keeper/msg_server.go | 91 +++++++++++++++++++++++++++++++++ x/exchange/module/module.go | 5 +- 2 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 x/exchange/keeper/msg_server.go diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go new file mode 100644 index 0000000000..d9ef652c9a --- /dev/null +++ b/x/exchange/keeper/msg_server.go @@ -0,0 +1,91 @@ +package keeper + +import ( + "context" + + "github.com/provenance-io/provenance/x/exchange" +) + +// MsgServer is an alias for a Keeper that implements the exchange.MsgServer interface. +type MsgServer Keeper + +func NewMsgServer(k Keeper) exchange.MsgServer { + return MsgServer(k) +} + +var _ exchange.MsgServer = MsgServer{} + +func (k MsgServer) CreateAsk(goCtx context.Context, req *exchange.MsgCreateAskRequest) (*exchange.MsgCreateAskResponse, error) { + // TODO[1658]: Implement CreateAsk + panic("not implemented") +} + +func (k MsgServer) CreateBid(goCtx context.Context, req *exchange.MsgCreateBidRequest) (*exchange.MsgCreateBidResponse, error) { + // TODO[1658]: Implement CreateBid + panic("not implemented") +} + +func (k MsgServer) CancelOrder(goCtx context.Context, req *exchange.MsgCancelOrderRequest) (*exchange.MsgCancelOrderResponse, error) { + // TODO[1658]: Implement CancelOrder + panic("not implemented") +} + +func (k MsgServer) FillBids(goCtx context.Context, req *exchange.MsgFillBidsRequest) (*exchange.MsgFillBidsResponse, error) { + // TODO[1658]: Implement FillBids + panic("not implemented") +} + +func (k MsgServer) FillAsks(goCtx context.Context, req *exchange.MsgFillAsksRequest) (*exchange.MsgFillAsksResponse, error) { + // TODO[1658]: Implement FillAsks + panic("not implemented") +} + +func (k MsgServer) MarketSettle(goCtx context.Context, req *exchange.MsgMarketSettleRequest) (*exchange.MsgMarketSettleResponse, error) { + // TODO[1658]: Implement MarketSettle + panic("not implemented") +} + +func (k MsgServer) MarketWithdraw(goCtx context.Context, req *exchange.MsgMarketWithdrawRequest) (*exchange.MsgMarketWithdrawResponse, error) { + // TODO[1658]: Implement MarketWithdraw + panic("not implemented") +} + +func (k MsgServer) MarketUpdateDetails(goCtx context.Context, req *exchange.MsgMarketUpdateDetailsRequest) (*exchange.MsgMarketUpdateDetailsResponse, error) { + // TODO[1658]: Implement MarketUpdateDetails + panic("not implemented") +} + +func (k MsgServer) MarketUpdateEnabled(goCtx context.Context, req *exchange.MsgMarketUpdateEnabledRequest) (*exchange.MsgMarketUpdateEnabledResponse, error) { + // TODO[1658]: Implement MarketUpdateEnabled + panic("not implemented") +} + +func (k MsgServer) MarketUpdateUserSettle(goCtx context.Context, req *exchange.MsgMarketUpdateUserSettleRequest) (*exchange.MsgMarketUpdateUserSettleResponse, error) { + // TODO[1658]: Implement MarketUpdateUserSettle + panic("not implemented") +} + +func (k MsgServer) MarketManagePermissions(goCtx context.Context, req *exchange.MsgMarketManagePermissionsRequest) (*exchange.MsgMarketManagePermissionsResponse, error) { + // TODO[1658]: Implement MarketManagePermissions + panic("not implemented") +} + +func (k MsgServer) MarketManageReqAttrs(goCtx context.Context, req *exchange.MsgMarketManageReqAttrsRequest) (*exchange.MsgMarketManageReqAttrsResponse, error) { + // TODO[1658]: Implement MarketManageReqAttrs + panic("not implemented") +} + +func (k MsgServer) GovCreateMarket(goCtx context.Context, req *exchange.MsgGovCreateMarketRequest) (*exchange.MsgGovCreateMarketResponse, error) { + // TODO[1658]: Implement GovCreateMarket + panic("not implemented") +} + +func (k MsgServer) GovManageFees(goCtx context.Context, req *exchange.MsgGovManageFeesRequest) (*exchange.MsgGovManageFeesResponse, error) { + // TODO[1658]: Implement GovManageFees + panic("not implemented") +} + +func (k MsgServer) GovUpdateParams(goCtx context.Context, req *exchange.MsgGovUpdateParamsRequest) (*exchange.MsgGovUpdateParamsResponse, error) { + // TODO[1658]: Implement GovUpdateParams + panic("not implemented") +} diff --git a/x/exchange/module/module.go b/x/exchange/module/module.go index 1826aef65f..dbde64a812 100644 --- a/x/exchange/module/module.go +++ b/x/exchange/module/module.go @@ -122,10 +122,11 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw //return cdc.MustMarshalJSON(gs) } -// RegisterServices registers a gRPC query service to respond to the exchange-specific gRPC queries. +// RegisterServices registers module services. func (am AppModule) RegisterServices(cfg module.Configurator) { + exchange.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServer(am.keeper)) // TODO[1658]: Uncomment this once the keeper implements the query server. - // exchange.RegisterQueryServer(cfg.QueryServer(), am.keeper) + // exchange.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServer(am.keeper)) } // ConsensusVersion implements AppModule/ConsensusVersion. From 3b0b558d373a946843fc80146597d2aeaf3b5583 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 29 Aug 2023 18:11:04 -0600 Subject: [PATCH 045/309] [1658]: Add the authority to the keeper. --- x/exchange/keeper/keeper.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 99df2e41be..99956a29b8 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -3,19 +3,23 @@ package keeper import ( "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) type Keeper struct { cdc codec.BinaryCodec storeKey storetypes.StoreKey // TODO[1658]: Finish the Keeper struct. + authority string } func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey) Keeper { // TODO[1658]: Finish NewKeeper. rv := Keeper{ - cdc: cdc, - storeKey: storeKey, + cdc: cdc, + storeKey: storeKey, + authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), } return rv } From 19a77bdee3f94204b7618f933248c537e6d719b2 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 29 Aug 2023 18:11:33 -0600 Subject: [PATCH 046/309] [1658]: Create the store key type bytes and state documentation. --- x/exchange/keeper/keys.go | 88 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 x/exchange/keeper/keys.go diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go new file mode 100644 index 0000000000..b09b27699e --- /dev/null +++ b/x/exchange/keeper/keys.go @@ -0,0 +1,88 @@ +package keeper + +// The following keys and values are stored in state: +// +// Params: +// All params entries start with the type byte 0x00. +// The splits are stored as uint16 in big-endian order. +// Default split: 0x00 | split => uint16 +// Specific splits: 0x00 | split | => uint16 +// +// Markets: +// Some aspects of a market are stored using the accounts module and the MarketAccount type. +// Others are stored in the exchange module. +// All market-related entries start with the type byte 0x01 followed by the market_id. +// The is a uint32 in big-endian order (4 bytes). +// The is a single byte as uint8 with the same value as the enum entries. +// +// Market Account Address: 0x01 | | 0x00 => (bytes) +// Market Create Ask Flat Fee: 0x01 | | 0x01 | => (string) +// Market Create Bid Flat Fee: 0x01 | | 0x02 | => (string) +// Market Settlement Seller Flat Fee: 0x01 | | 0x03 | => (string) +// Market Settlement Seller Fee Ratio: 0x01 | | 0x04 | | 0x00 | => comma-separated price and fee amount strings. +// Market Settlement Buyer Flat Fee: 0x01 | | 0x05 | => (string) +// Market Settlement Buyer Fee Ratio: 0x01 | | 0x06 | | 0x00 | => comma-separated price and fee amount strings. +// Market inactive indicator: 0x01 | | 0x07 => nil +// Market self-settle indicator: 0x01 | | 0x08 => nil +// Market permissions: 0x01 | | 0x09 | |
| => nil +// Market Required Attributes: 0x01 | | 0x10 | => comma-separated list of required attribute entries. +// +// Orders: +// Order entries all have the following general format: +// 0x02 | | => protobuf encoding of order type. +// s: +// Ask: 0x00 +// Bid: 0x01 +// Specific entry formats: +// Ask Orders: 0x02 | | 0x00 => protobuf(AskOrder) +// Bid Orders: 0x02 | | 0x01 => protobuf(BidOrder) +// +// A market to order index is maintained with the following format: +// 0x03 | (4 bytes) | (8 bytes) => nil +// +// An address to order index is maintained with the following format: +// 0x04 | len(
) (1 byte) |
| (8 bytes) => nil +// +// An asset type to order index is maintained with the following format: +// 0x05 | | (1 byte) | (8 bytes) => nil + +var ( + // KeyTypeParams is the type byte for params entries. + KeyTypeParams = []byte{0x00} + // KeyTypeMarket is the type byte for market entries. + KeyTypeMarket = []byte{0x01} + // KeyTypeOrder is the type byte for order entries. + KeyTypeOrder = []byte{0x02} + // KeyTypeMarketToOrderIndex is the type byte for entries in the market to order index. + KeyTypeMarketToOrderIndex = []byte{0x03} + // KeyTypeAddressToOrderIndex is the type byte for entries in the address to order index. + KeyTypeAddressToOrderIndex = []byte{0x04} + // KeyTypeAssetToOrderIndex is the type byte for entries in the asset to order index. + KeyTypeAssetToOrderIndex = []byte{0x05} + + // MarketKeyTypeAddress is the market-specific type byte for the market addresses. + MarketKeyTypeAddress = []byte{0x00} + // MarketKeyTypeCreateAskFlat is the market-specific type byte for the create ask flat fees. + MarketKeyTypeCreateAskFlat = []byte{0x01} + // MarketKeyTypeCreateBidFlat is the market-specific type byte for the create bid flat fees. + MarketKeyTypeCreateBidFlat = []byte{0x02} + // MarketKeyTypeSettlementSellerFlat is the market-specific type byte for the seller settlement flat fees. + MarketKeyTypeSettlementSellerFlat = []byte{0x03} + // MarketKeyTypeSettlementSellerRatio is the market-specific type byte for the seller settlement ratios. + MarketKeyTypeSettlementSellerRatio = []byte{0x04} + // MarketKeyTypeSettlementBuyerFlat is the market-specific type byte for the buyer settlement flat fees. + MarketKeyTypeSettlementBuyerFlat = []byte{0x05} + // MarketKeyTypeSettlementBuyerRatio is the market-specific type byte for the buyer settlement ratios. + MarketKeyTypeSettlementBuyerRatio = []byte{0x06} + // MarketKeyTypeInactive is the market-specific type byte for the inactive indicators. + MarketKeyTypeInactive = []byte{0x07} + // MarketKeyTypeSelfSettle is the market-specific type byte for the self-settle indicators. + MarketKeyTypeSelfSettle = []byte{0x08} + // MarketKeyTypePermissions is the market-specific type byte for the market permissions. + MarketKeyTypePermissions = []byte{0x09} + // MarketKeyTypeReqAttr is the market-specific type byte for the market's required attributes lists. + MarketKeyTypeReqAttr = []byte{0x10} + + OrderKeyTypeAsk = []byte{0x00} + OrderKeyTypeBid = []byte{0x01} +) From 0deaa489f5f5e8c5382068b0af14026afdcb5ec5 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 30 Aug 2023 10:57:07 -0600 Subject: [PATCH 047/309] [1658]: Update the keys documentation a bit. --- x/exchange/keeper/keys.go | 62 +++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index b09b27699e..26ea9d66ee 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -3,39 +3,41 @@ package keeper // The following keys and values are stored in state: // // Params: -// All params entries start with the type byte 0x00. -// The splits are stored as uint16 in big-endian order. -// Default split: 0x00 | split => uint16 -// Specific splits: 0x00 | split | => uint16 +// All params entries start with the type byte 0x00. +// The splits are stored as uint16 in big-endian order. +// Default split: 0x00 | split => uint16 +// Specific splits: 0x00 | split | => uint16 // // Markets: -// Some aspects of a market are stored using the accounts module and the MarketAccount type. -// Others are stored in the exchange module. -// All market-related entries start with the type byte 0x01 followed by the market_id. -// The is a uint32 in big-endian order (4 bytes). -// The is a single byte as uint8 with the same value as the enum entries. +// Some aspects of a market are stored using the accounts module and the MarketAccount type. +// Others are stored in the exchange module. +// All market-related entries start with the type byte 0x01 followed by the , then a market key type byte. +// The is a uint32 in big-endian order (4 bytes). // -// Market Account Address: 0x01 | | 0x00 => (bytes) -// Market Create Ask Flat Fee: 0x01 | | 0x01 | => (string) -// Market Create Bid Flat Fee: 0x01 | | 0x02 | => (string) -// Market Settlement Seller Flat Fee: 0x01 | | 0x03 | => (string) -// Market Settlement Seller Fee Ratio: 0x01 | | 0x04 | | 0x00 | => comma-separated price and fee amount strings. -// Market Settlement Buyer Flat Fee: 0x01 | | 0x05 | => (string) -// Market Settlement Buyer Fee Ratio: 0x01 | | 0x06 | | 0x00 | => comma-separated price and fee amount strings. -// Market inactive indicator: 0x01 | | 0x07 => nil -// Market self-settle indicator: 0x01 | | 0x08 => nil -// Market permissions: 0x01 | | 0x09 | |
| => nil -// Market Required Attributes: 0x01 | | 0x10 | => comma-separated list of required attribute entries. +// Market Account Address: 0x01 | | 0x00 => (bytes) +// Market Create Ask Flat Fee: 0x01 | | 0x01 | => (string) +// Market Create Bid Flat Fee: 0x01 | | 0x02 | => (string) +// Market Settlement Seller Flat Fee: 0x01 | | 0x03 | => (string) +// Market Settlement Seller Fee Ratio: 0x01 | | 0x04 | | 0x00 | => comma-separated price and fee amount (string). +// Market Settlement Buyer Flat Fee: 0x01 | | 0x05 | => (string) +// Market Settlement Buyer Fee Ratio: 0x01 | | 0x06 | | 0x00 | => comma-separated price and fee amount (string). +// Market inactive indicator: 0x01 | | 0x07 => nil +// Market self-settle indicator: 0x01 | | 0x08 => nil +// Market permissions: 0x01 | | 0x09 | |
| => nil +// Market Required Attributes: 0x01 | | 0x10 | => comma-separated list of required attribute entries. +// +// The is a single byte as uint8 with the same values as the enum entries. // // Orders: -// Order entries all have the following general format: -// 0x02 | | => protobuf encoding of order type. -// s: -// Ask: 0x00 -// Bid: 0x01 -// Specific entry formats: -// Ask Orders: 0x02 | | 0x00 => protobuf(AskOrder) -// Bid Orders: 0x02 | | 0x01 => protobuf(BidOrder) +// Order entries all have the following general format: +// 0x02 | (8 bytes) | => protobuf encoding of specific order type. +// The is a uint64 in big-endian order (8 bytes). +// values: +// Ask: 0x00 +// Bid: 0x01 +// So, the specific entry formats look like this: +// Ask Orders: 0x02 | (8 bytes) | 0x00 => protobuf(AskOrder) +// Bid Orders: 0x02 | (8 bytes) | 0x01 => protobuf(BidOrder) // // A market to order index is maintained with the following format: // 0x03 | (4 bytes) | (8 bytes) => nil @@ -44,7 +46,7 @@ package keeper // 0x04 | len(
) (1 byte) |
| (8 bytes) => nil // // An asset type to order index is maintained with the following format: -// 0x05 | | (1 byte) | (8 bytes) => nil +// 0x05 | | (1 byte) | (8 bytes) => nil var ( // KeyTypeParams is the type byte for params entries. @@ -83,6 +85,8 @@ var ( // MarketKeyTypeReqAttr is the market-specific type byte for the market's required attributes lists. MarketKeyTypeReqAttr = []byte{0x10} + // OrderKeyTypeAsk is the order-specific type byte for ask orders. OrderKeyTypeAsk = []byte{0x00} + // OrderKeyTypeBid is the order-specific type byte for bid orders. OrderKeyTypeBid = []byte{0x01} ) From bc914b5b26c03ed3ea0ad230a8839d165a712699 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 30 Aug 2023 17:12:21 -0600 Subject: [PATCH 048/309] [1658]: Add todo about some event tests to write. --- x/exchange/events_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go index cfa848ae7e..836081c517 100644 --- a/x/exchange/events_test.go +++ b/x/exchange/events_test.go @@ -214,3 +214,5 @@ func TestNewEventParamsUpdated(t *testing.T) { event := NewEventParamsUpdated() assertEverythingSet(t, event, "EventParamsUpdated") } + +// TODO[1658]: func TestTypedEventToEvent(t *testing.T) From 617bb535ecee634cbe1cd9ee56b9ffa16c79d5f1 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 30 Aug 2023 17:12:43 -0600 Subject: [PATCH 049/309] [1658]: The beginnings of all the key funcs. --- x/exchange/keeper/keys.go | 239 ++++++++++++++++++++++++++++++++++---- 1 file changed, 217 insertions(+), 22 deletions(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 26ea9d66ee..6c31d4d262 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -1,12 +1,21 @@ package keeper +import ( + "encoding/binary" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" + "github.com/provenance-io/provenance/x/exchange" +) + // The following keys and values are stored in state: // // Params: // All params entries start with the type byte 0x00. // The splits are stored as uint16 in big-endian order. -// Default split: 0x00 | split => uint16 -// Specific splits: 0x00 | split | => uint16 +// Default split: 0x00 | "split" => uint16 +// Specific splits: 0x00 | "split" | => uint16 // // Markets: // Some aspects of a market are stored using the accounts module and the MarketAccount type. @@ -48,45 +57,231 @@ package keeper // An asset type to order index is maintained with the following format: // 0x05 | | (1 byte) | (8 bytes) => nil -var ( +const ( // KeyTypeParams is the type byte for params entries. - KeyTypeParams = []byte{0x00} + KeyTypeParams = byte(0x00) // KeyTypeMarket is the type byte for market entries. - KeyTypeMarket = []byte{0x01} + KeyTypeMarket = byte(0x01) // KeyTypeOrder is the type byte for order entries. - KeyTypeOrder = []byte{0x02} + KeyTypeOrder = byte(0x02) // KeyTypeMarketToOrderIndex is the type byte for entries in the market to order index. - KeyTypeMarketToOrderIndex = []byte{0x03} + KeyTypeMarketToOrderIndex = byte(0x03) // KeyTypeAddressToOrderIndex is the type byte for entries in the address to order index. - KeyTypeAddressToOrderIndex = []byte{0x04} + KeyTypeAddressToOrderIndex = byte(0x04) // KeyTypeAssetToOrderIndex is the type byte for entries in the asset to order index. - KeyTypeAssetToOrderIndex = []byte{0x05} + KeyTypeAssetToOrderIndex = byte(0x05) // MarketKeyTypeAddress is the market-specific type byte for the market addresses. - MarketKeyTypeAddress = []byte{0x00} + MarketKeyTypeAddress = byte(0x00) // MarketKeyTypeCreateAskFlat is the market-specific type byte for the create ask flat fees. - MarketKeyTypeCreateAskFlat = []byte{0x01} + MarketKeyTypeCreateAskFlat = byte(0x01) // MarketKeyTypeCreateBidFlat is the market-specific type byte for the create bid flat fees. - MarketKeyTypeCreateBidFlat = []byte{0x02} + MarketKeyTypeCreateBidFlat = byte(0x02) // MarketKeyTypeSettlementSellerFlat is the market-specific type byte for the seller settlement flat fees. - MarketKeyTypeSettlementSellerFlat = []byte{0x03} + MarketKeyTypeSettlementSellerFlat = byte(0x03) // MarketKeyTypeSettlementSellerRatio is the market-specific type byte for the seller settlement ratios. - MarketKeyTypeSettlementSellerRatio = []byte{0x04} + MarketKeyTypeSettlementSellerRatio = byte(0x04) // MarketKeyTypeSettlementBuyerFlat is the market-specific type byte for the buyer settlement flat fees. - MarketKeyTypeSettlementBuyerFlat = []byte{0x05} + MarketKeyTypeSettlementBuyerFlat = byte(0x05) // MarketKeyTypeSettlementBuyerRatio is the market-specific type byte for the buyer settlement ratios. - MarketKeyTypeSettlementBuyerRatio = []byte{0x06} + MarketKeyTypeSettlementBuyerRatio = byte(0x06) // MarketKeyTypeInactive is the market-specific type byte for the inactive indicators. - MarketKeyTypeInactive = []byte{0x07} + MarketKeyTypeInactive = byte(0x07) // MarketKeyTypeSelfSettle is the market-specific type byte for the self-settle indicators. - MarketKeyTypeSelfSettle = []byte{0x08} + MarketKeyTypeSelfSettle = byte(0x08) // MarketKeyTypePermissions is the market-specific type byte for the market permissions. - MarketKeyTypePermissions = []byte{0x09} + MarketKeyTypePermissions = byte(0x09) // MarketKeyTypeReqAttr is the market-specific type byte for the market's required attributes lists. - MarketKeyTypeReqAttr = []byte{0x10} + MarketKeyTypeReqAttr = byte(0x10) // OrderKeyTypeAsk is the order-specific type byte for ask orders. - OrderKeyTypeAsk = []byte{0x00} + OrderKeyTypeAsk = exchange.OrderTypeByteAsk // OrderKeyTypeBid is the order-specific type byte for bid orders. - OrderKeyTypeBid = []byte{0x01} + OrderKeyTypeBid = exchange.OrderTypeByteBid ) + +// TODO[1658]: Add comments to the funcs in keeper/keys.go. + +// TODO[1658]: Split out the market keys for prefixing. + +// concatBzPlusCap creates a single byte slice consisting of the two provided byte slices with some extra capacity in the underlying array. +// The idea is that you can append(...) to the result of this without it needed a new underlying array. +func concatBzPlusCap(typeByte byte, bz1, bz2 []byte, extraCap int) []byte { + rv := make([]byte, 0, 1+len(bz1)+len(bz2)+extraCap) + rv = append(rv, typeByte) + rv = append(rv, bz1...) + rv = append(rv, bz2...) + return rv +} + +// uint16Bz converts the provided uint16 value to a big-endian byte slice of length 2. +func uint16Bz(val uint16) []byte { + rv := make([]byte, 2) + binary.BigEndian.PutUint16(rv, val) + return rv +} + +// uint32Bz converts the provided uint32 value to a big-endian byte slice of length 4. +func uint32Bz(val uint32) []byte { + rv := make([]byte, 4) + binary.BigEndian.PutUint32(rv, val) + return rv +} + +// uint64Bz converts the provided uint64 value to a big-endian byte slice of length 8. +func uint64Bz(val uint64) []byte { + rv := make([]byte, 8) + binary.BigEndian.PutUint64(rv, val) + return rv +} + +func MakeKeyParamsSplit(denom string) []byte { + return concatBzPlusCap(KeyTypeParams, []byte("split"), []byte(denom), 0) +} + +func marketKeyPrefix(marketID uint32, marketTypeByte byte, extraCap int) []byte { + return concatBzPlusCap(KeyTypeMarket, uint32Bz(marketID), []byte{marketTypeByte}, extraCap) +} + +func MakeKeyMarketAccountAddress(marketID uint32) []byte { + return marketKeyPrefix(marketID, MarketKeyTypeAddress, 0) +} + +func MakeKeyMarketCreateAskFlatFee(marketID uint32, denom string) []byte { + rv := marketKeyPrefix(marketID, MarketKeyTypeCreateAskFlat, len(denom)) + rv = append(rv, denom...) + return rv +} + +func MakeKeyMarketCreateBidFlatFee(marketID uint32, denom string) []byte { + rv := marketKeyPrefix(marketID, MarketKeyTypeCreateBidFlat, len(denom)) + rv = append(rv, denom...) + return rv +} + +func MakeKeyMarketSettlementSellerFlatFee(marketID uint32, denom string) []byte { + rv := marketKeyPrefix(marketID, MarketKeyTypeSettlementSellerFlat, len(denom)) + rv = append(rv, denom...) + return rv +} + +func MakeKeyMarketSettlementSellerRatio(marketID uint32, ratio exchange.FeeRatio) []byte { + rv := marketKeyPrefix(marketID, MarketKeyTypeSettlementSellerRatio, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) + rv = append(rv, ratio.Price.Denom...) + rv = append(rv, 0x00) + rv = append(rv, ratio.Fee.Denom...) + return rv +} + +func MakeKeyMarketSettlementBuyerFlatFee(marketID uint32, denom string) []byte { + rv := marketKeyPrefix(marketID, MarketKeyTypeSettlementBuyerFlat, len(denom)) + rv = append(rv, denom...) + return rv +} + +func MakeKeyMarketSettlementBuyerRatio(marketID uint32, ratio exchange.FeeRatio) []byte { + rv := marketKeyPrefix(marketID, MarketKeyTypeSettlementBuyerRatio, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) + rv = append(rv, ratio.Price.Denom...) + rv = append(rv, 0x00) + rv = append(rv, ratio.Fee.Denom...) + return rv +} + +func MakeKeyMarketInactive(marketID uint32) []byte { + return marketKeyPrefix(marketID, MarketKeyTypeInactive, 0) +} + +func MakeKeyMarketSelfSettle(marketID uint32) []byte { + return marketKeyPrefix(marketID, MarketKeyTypeSelfSettle, 0) +} + +func MakeKeyMarketPermissions(marketID uint32, addr sdk.AccAddress, permission exchange.Permission) []byte { + if permission < 0 || permission > 255 { + panic(fmt.Errorf("permission value %d out of range for uint8", permission)) + } + rv := marketKeyPrefix(marketID, MarketKeyTypePermissions, len(addr)+2) + rv = append(rv, address.MustLengthPrefix(addr)...) + rv = append(rv, byte(permission)) + return rv +} + +func MakeKeyMarketReqAttrAsk(marketID uint32) []byte { + rv := marketKeyPrefix(marketID, MarketKeyTypeReqAttr, 1) + rv = append(rv, OrderKeyTypeAsk) + return rv +} + +func MakeKeyMarketReqAttrBid(marketID uint32) []byte { + rv := marketKeyPrefix(marketID, MarketKeyTypeReqAttr, 1) + rv = append(rv, OrderKeyTypeBid) + return rv +} + +func keyPrefixOrder(orderID uint64, extraCap int) []byte { + return concatBzPlusCap(KeyTypeOrder, uint64Bz(orderID), nil, extraCap) +} + +func MakeOrderPrefix(orderID uint64) []byte { + return keyPrefixOrder(orderID, 0) +} + +func MakeOrderKey(order exchange.Order) []byte { + rv := keyPrefixOrder(order.GetOrderId(), 1) + rv = append(rv, order.OrderTypeByte()) + return rv +} + +func indexPrefixMarketToOrder(marketID uint32, extraCap int) []byte { + return concatBzPlusCap(KeyTypeMarketToOrderIndex, uint32Bz(marketID), nil, extraCap) +} + +func MakeIndexPrefixMarketToOrder(marketID uint32) []byte { + return indexPrefixMarketToOrder(marketID, 0) +} + +func MakeIndexKeyMarketToOrder(marketID uint32, orderID uint64) []byte { + rv := indexPrefixMarketToOrder(marketID, 8) + rv = append(rv, uint64Bz(orderID)...) + return rv +} + +func indexPrefixAddressToOrder(addr sdk.AccAddress, extraCap int) []byte { + return concatBzPlusCap(KeyTypeAddressToOrderIndex, address.MustLengthPrefix(addr), nil, extraCap) +} + +func MakeIndexPrefixAddressToOrder(addr sdk.AccAddress) []byte { + return indexPrefixAddressToOrder(addr, 0) +} + +func MakeIndexKeyAddressToOrder(addr sdk.AccAddress, orderID uint64) []byte { + rv := indexPrefixAddressToOrder(addr, 8) + rv = append(rv, uint64Bz(orderID)...) + return rv +} + +func indexPrefixAssetToOrder(assetDenom string, extraCap int) []byte { + return concatBzPlusCap(KeyTypeAssetToOrderIndex, []byte(assetDenom), nil, extraCap) +} + +func MakeIndexPrefixAssetToOrder(assetDenom string) []byte { + return indexPrefixAssetToOrder(assetDenom, 0) +} + +func MakeIndexPrefixAssetToOrderAsks(assetDenom string) []byte { + rv := indexPrefixAssetToOrder(assetDenom, 1) + rv = append(rv, OrderKeyTypeAsk) + return rv +} + +func MakeIndexPrefixAssetToOrderBids(assetDenom string) []byte { + rv := indexPrefixAssetToOrder(assetDenom, 1) + rv = append(rv, OrderKeyTypeBid) + return rv +} + +func MakeIndexKeyAssetToOrder(assetDenom string, order exchange.Order) []byte { + rv := indexPrefixAssetToOrder(assetDenom, 9) + rv = append(rv, order.OrderTypeByte()) + rv = append(rv, uint64Bz(order.GetOrderId())...) + return rv +} From 884183e8a7307bc01a092833bb8bba32fb5b41c4 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 00:42:50 -0600 Subject: [PATCH 050/309] [1658]: Finish up the keys stuff (hopefully), and start in on some unit tests for them. --- x/exchange/keeper/keys.go | 227 +++++++++++++++++++++++++-------- x/exchange/keeper/keys_test.go | 226 ++++++++++++++++++++++++++++++++ 2 files changed, 398 insertions(+), 55 deletions(-) create mode 100644 x/exchange/keeper/keys_test.go diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 6c31d4d262..acaaf7b2ac 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -23,17 +23,16 @@ import ( // All market-related entries start with the type byte 0x01 followed by the , then a market key type byte. // The is a uint32 in big-endian order (4 bytes). // -// Market Account Address: 0x01 | | 0x00 => (bytes) -// Market Create Ask Flat Fee: 0x01 | | 0x01 | => (string) -// Market Create Bid Flat Fee: 0x01 | | 0x02 | => (string) -// Market Settlement Seller Flat Fee: 0x01 | | 0x03 | => (string) -// Market Settlement Seller Fee Ratio: 0x01 | | 0x04 | | 0x00 | => comma-separated price and fee amount (string). -// Market Settlement Buyer Flat Fee: 0x01 | | 0x05 | => (string) -// Market Settlement Buyer Fee Ratio: 0x01 | | 0x06 | | 0x00 | => comma-separated price and fee amount (string). -// Market inactive indicator: 0x01 | | 0x07 => nil -// Market self-settle indicator: 0x01 | | 0x08 => nil -// Market permissions: 0x01 | | 0x09 | |
| => nil -// Market Required Attributes: 0x01 | | 0x10 | => comma-separated list of required attribute entries. +// Market Create Ask Flat Fee: 0x01 | | 0x00 | => (string) +// Market Create Bid Flat Fee: 0x01 | | 0x01 | => (string) +// Market Settlement Seller Flat Fee: 0x01 | | 0x02 | => (string) +// Market Settlement Seller Fee Ratio: 0x01 | | 0x03 | | 0x00 | => comma-separated price and fee amount (string). +// Market Settlement Buyer Flat Fee: 0x01 | | 0x04 | => (string) +// Market Settlement Buyer Fee Ratio: 0x01 | | 0x05 | | 0x00 | => comma-separated price and fee amount (string). +// Market inactive indicator: 0x01 | | 0x06 => nil +// Market self-settle indicator: 0x01 | | 0x07 => nil +// Market permissions: 0x01 | | 0x08 | |
| => nil +// Market Required Attributes: 0x01 | | 0x09 | => comma-separated list of required attribute entries. // // The is a single byte as uint8 with the same values as the enum entries. // @@ -71,28 +70,26 @@ const ( // KeyTypeAssetToOrderIndex is the type byte for entries in the asset to order index. KeyTypeAssetToOrderIndex = byte(0x05) - // MarketKeyTypeAddress is the market-specific type byte for the market addresses. - MarketKeyTypeAddress = byte(0x00) // MarketKeyTypeCreateAskFlat is the market-specific type byte for the create ask flat fees. - MarketKeyTypeCreateAskFlat = byte(0x01) + MarketKeyTypeCreateAskFlat = byte(0x00) // MarketKeyTypeCreateBidFlat is the market-specific type byte for the create bid flat fees. - MarketKeyTypeCreateBidFlat = byte(0x02) + MarketKeyTypeCreateBidFlat = byte(0x01) // MarketKeyTypeSettlementSellerFlat is the market-specific type byte for the seller settlement flat fees. - MarketKeyTypeSettlementSellerFlat = byte(0x03) + MarketKeyTypeSettlementSellerFlat = byte(0x02) // MarketKeyTypeSettlementSellerRatio is the market-specific type byte for the seller settlement ratios. - MarketKeyTypeSettlementSellerRatio = byte(0x04) + MarketKeyTypeSettlementSellerRatio = byte(0x03) // MarketKeyTypeSettlementBuyerFlat is the market-specific type byte for the buyer settlement flat fees. - MarketKeyTypeSettlementBuyerFlat = byte(0x05) + MarketKeyTypeSettlementBuyerFlat = byte(0x04) // MarketKeyTypeSettlementBuyerRatio is the market-specific type byte for the buyer settlement ratios. - MarketKeyTypeSettlementBuyerRatio = byte(0x06) + MarketKeyTypeSettlementBuyerRatio = byte(0x05) // MarketKeyTypeInactive is the market-specific type byte for the inactive indicators. - MarketKeyTypeInactive = byte(0x07) + MarketKeyTypeInactive = byte(0x06) // MarketKeyTypeSelfSettle is the market-specific type byte for the self-settle indicators. - MarketKeyTypeSelfSettle = byte(0x08) + MarketKeyTypeSelfSettle = byte(0x07) // MarketKeyTypePermissions is the market-specific type byte for the market permissions. - MarketKeyTypePermissions = byte(0x09) + MarketKeyTypePermissions = byte(0x08) // MarketKeyTypeReqAttr is the market-specific type byte for the market's required attributes lists. - MarketKeyTypeReqAttr = byte(0x10) + MarketKeyTypeReqAttr = byte(0x09) // OrderKeyTypeAsk is the order-specific type byte for ask orders. OrderKeyTypeAsk = exchange.OrderTypeByteAsk @@ -100,17 +97,12 @@ const ( OrderKeyTypeBid = exchange.OrderTypeByteBid ) -// TODO[1658]: Add comments to the funcs in keeper/keys.go. - -// TODO[1658]: Split out the market keys for prefixing. - -// concatBzPlusCap creates a single byte slice consisting of the two provided byte slices with some extra capacity in the underlying array. +// prepKey creates a single byte slice consisting of the type byte and provided byte slice with some extra capacity in the underlying array. // The idea is that you can append(...) to the result of this without it needed a new underlying array. -func concatBzPlusCap(typeByte byte, bz1, bz2 []byte, extraCap int) []byte { - rv := make([]byte, 0, 1+len(bz1)+len(bz2)+extraCap) +func prepKey(typeByte byte, bz []byte, extraCap int) []byte { + rv := make([]byte, 0, 1+len(bz)+extraCap) rv = append(rv, typeByte) - rv = append(rv, bz1...) - rv = append(rv, bz2...) + rv = append(rv, bz...) return rv } @@ -135,150 +127,275 @@ func uint64Bz(val uint64) []byte { return rv } +// MakeKeyParamsSplit creates the key to use for the params defining the splits. +// A denom of "" is used for the default split value. func MakeKeyParamsSplit(denom string) []byte { - return concatBzPlusCap(KeyTypeParams, []byte("split"), []byte(denom), 0) + rv := prepKey(KeyTypeParams, []byte("split"), len(denom)) + rv = append(rv, denom...) + return rv +} + +// keyPrefixMarket creates the root of a market's key with extra capacity for the rest. +func keyPrefixMarket(marketID uint32, extraCap int) []byte { + return prepKey(KeyTypeMarket, uint32Bz(marketID), extraCap) +} + +// MakeKeyPrefixMarket creates the key prefix for all of a market's entries. +func MakeKeyPrefixMarket(marketID uint32) []byte { + return keyPrefixMarket(marketID, 0) +} + +// keyPrefixMarketType creates the beginnings of a market key with the given market id and entry type byte. +// Similar to prepKey, the idea is that you can append to the result without needing a new underlying array. +func keyPrefixMarketType(marketID uint32, marketTypeByte byte, extraCap int) []byte { + rv := keyPrefixMarket(marketID, extraCap+1) + rv = append(rv, marketTypeByte) + return rv } -func marketKeyPrefix(marketID uint32, marketTypeByte byte, extraCap int) []byte { - return concatBzPlusCap(KeyTypeMarket, uint32Bz(marketID), []byte{marketTypeByte}, extraCap) +// marketKeyPrefixCreateAskFlatFee creates the key prefix for a market's create ask flat fees with extra capacity for the rest. +func marketKeyPrefixCreateAskFlatFee(marketID uint32, extraCap int) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypeCreateAskFlat, extraCap) } -func MakeKeyMarketAccountAddress(marketID uint32) []byte { - return marketKeyPrefix(marketID, MarketKeyTypeAddress, 0) +// MakeKeyPrefixMarketCreateAskFlatFee creates the key prefix for the create ask flat fees for the provided market. +func MakeKeyPrefixMarketCreateAskFlatFee(marketID uint32) []byte { + return marketKeyPrefixCreateAskFlatFee(marketID, 0) } +// MakeKeyMarketCreateAskFlatFee creates the key to use for a create ask flat fee for the given market and denom. func MakeKeyMarketCreateAskFlatFee(marketID uint32, denom string) []byte { - rv := marketKeyPrefix(marketID, MarketKeyTypeCreateAskFlat, len(denom)) + rv := marketKeyPrefixCreateAskFlatFee(marketID, len(denom)) rv = append(rv, denom...) return rv } +// marketKeyPrefixCreateBidFlatFee creates the key prefix for a market's create bid flat fees with extra capacity for the rest. +func marketKeyPrefixCreateBidFlatFee(marketID uint32, extraCap int) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypeCreateBidFlat, extraCap) +} + +// MakeKeyPrefixMarketCreateBidFlatFee creates the key prefix for the create bid flat fees for the provided market. +func MakeKeyPrefixMarketCreateBidFlatFee(marketID uint32) []byte { + return marketKeyPrefixCreateBidFlatFee(marketID, 0) +} + +// MakeKeyMarketCreateBidFlatFee creates the key to use for a create bid flat fee for the given denom. func MakeKeyMarketCreateBidFlatFee(marketID uint32, denom string) []byte { - rv := marketKeyPrefix(marketID, MarketKeyTypeCreateBidFlat, len(denom)) + rv := marketKeyPrefixCreateBidFlatFee(marketID, len(denom)) rv = append(rv, denom...) return rv } +// marketKeyPrefixSettlementSellerFlatFee creates the key prefix for a market's settlement seller flat fees with extra capacity for the rest. +func marketKeyPrefixSettlementSellerFlatFee(marketID uint32, extraCap int) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypeSettlementSellerFlat, extraCap) +} + +// MakeKeyPrefixMarketSettlementSellerFlatFee creates the key prefix for a market's settlement seller flat fees. +func MakeKeyPrefixMarketSettlementSellerFlatFee(marketID uint32) []byte { + return marketKeyPrefixSettlementSellerFlatFee(marketID, 0) +} + +// MakeKeyMarketSettlementSellerFlatFee creates the key for a market's settlement seller flat fee with the given denom. func MakeKeyMarketSettlementSellerFlatFee(marketID uint32, denom string) []byte { - rv := marketKeyPrefix(marketID, MarketKeyTypeSettlementSellerFlat, len(denom)) + rv := marketKeyPrefixSettlementSellerFlatFee(marketID, len(denom)) rv = append(rv, denom...) return rv } +// marketKeyPrefixSettlementSellerRatio creates the key prefix for a market's settlement seller ratios with extra capacity for the rest. +func marketKeyPrefixSettlementSellerRatio(marketID uint32, extraCap int) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypeSettlementSellerRatio, extraCap) +} + +// MakeKeyPrefixMarketSettlementSellerRatio creates the key prefix for a market's settlement seller fee ratios. +func MakeKeyPrefixMarketSettlementSellerRatio(marketID uint32) []byte { + return marketKeyPrefixSettlementSellerRatio(marketID, 0) +} + +// MakeKeyMarketSettlementSellerRatio creates the key to use for the given settlement seller fee ratio in the given market. func MakeKeyMarketSettlementSellerRatio(marketID uint32, ratio exchange.FeeRatio) []byte { - rv := marketKeyPrefix(marketID, MarketKeyTypeSettlementSellerRatio, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) + rv := marketKeyPrefixSettlementSellerRatio(marketID, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) rv = append(rv, ratio.Price.Denom...) rv = append(rv, 0x00) rv = append(rv, ratio.Fee.Denom...) return rv } +// marketKeyPrefixSettlementBuyerFlatFee creates the key prefix for a market's settlement buyer flat fees with extra capacity for the rest. +func marketKeyPrefixSettlementBuyerFlatFee(marketID uint32, extraCap int) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypeSettlementBuyerFlat, extraCap) +} + +// MakeKeyPrefixMarketSettlementBuyerFlatFee creates the key prefix for a market's settlement buyer flat fees. +func MakeKeyPrefixMarketSettlementBuyerFlatFee(marketID uint32) []byte { + return marketKeyPrefixSettlementBuyerFlatFee(marketID, 0) +} + +// MakeKeyMarketSettlementBuyerFlatFee creates th ekey for a market's settlement buyer flat fee with the given denom. func MakeKeyMarketSettlementBuyerFlatFee(marketID uint32, denom string) []byte { - rv := marketKeyPrefix(marketID, MarketKeyTypeSettlementBuyerFlat, len(denom)) + rv := marketKeyPrefixSettlementBuyerFlatFee(marketID, len(denom)) rv = append(rv, denom...) return rv } +// marketKeyPrefixSettlementBuyerRatio creates the key prefix for a market's settlement buyer ratios with extra capacity for the rest. +func marketKeyPrefixSettlementBuyerRatio(marketID uint32, extraCap int) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypeSettlementBuyerRatio, extraCap) +} + +// MakeKeyPrefixMarketSettlementBuyerRatio creates the key prefix for a market's settlement buyer fee ratios. +func MakeKeyPrefixMarketSettlementBuyerRatio(marketID uint32) []byte { + return marketKeyPrefixSettlementBuyerRatio(marketID, 0) +} + +// MakeKeyMarketSettlementBuyerRatio creates the key to use for the given settlement buyer fee ratio in the given market. func MakeKeyMarketSettlementBuyerRatio(marketID uint32, ratio exchange.FeeRatio) []byte { - rv := marketKeyPrefix(marketID, MarketKeyTypeSettlementBuyerRatio, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) + rv := marketKeyPrefixSettlementBuyerRatio(marketID, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) rv = append(rv, ratio.Price.Denom...) rv = append(rv, 0x00) rv = append(rv, ratio.Fee.Denom...) return rv } +// MakeKeyMarketInactive creates the key to use to indicate that a market is inactive. func MakeKeyMarketInactive(marketID uint32) []byte { - return marketKeyPrefix(marketID, MarketKeyTypeInactive, 0) + return keyPrefixMarketType(marketID, MarketKeyTypeInactive, 0) } +// MakeKeyMarketSelfSettle creates the key to use to indicate that a market allows self-settlement. func MakeKeyMarketSelfSettle(marketID uint32) []byte { - return marketKeyPrefix(marketID, MarketKeyTypeSelfSettle, 0) + return keyPrefixMarketType(marketID, MarketKeyTypeSelfSettle, 0) +} + +// marketKeyPrefixPermissions creates the key prefix for a market's permissions with extra capacity for the rest. +func marketKeyPrefixPermissions(marketID uint32, extraCap int) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypePermissions, extraCap) } +// marketKeyPrefixPermissionsForAddress creates the key prefix for an address' permissions in a given market with extra capacity for the rest. +func marketKeyPrefixPermissionsForAddress(marketID uint32, addr sdk.AccAddress, extraCap int) []byte { + rv := marketKeyPrefixPermissions(marketID, 1+len(addr)+extraCap) + rv = append(rv, address.MustLengthPrefix(addr)...) + return rv +} + +// MakeKeyPrefixMarketPermissions creates the key prefix for a market's permissions. +func MakeKeyPrefixMarketPermissions(marketID uint32) []byte { + return marketKeyPrefixPermissions(marketID, 0) +} + +// MakeKeyPrefixMarketPermissionsForAddress creates the key prefix for an address' permissions in a given market. +func MakeKeyPrefixMarketPermissionsForAddress(marketID uint32, addr sdk.AccAddress) []byte { + return marketKeyPrefixPermissionsForAddress(marketID, addr, 0) +} + +// MakeKeyMarketPermissions creates the key to use for a permission granted to an address for a market. func MakeKeyMarketPermissions(marketID uint32, addr sdk.AccAddress, permission exchange.Permission) []byte { if permission < 0 || permission > 255 { panic(fmt.Errorf("permission value %d out of range for uint8", permission)) } - rv := marketKeyPrefix(marketID, MarketKeyTypePermissions, len(addr)+2) - rv = append(rv, address.MustLengthPrefix(addr)...) + rv := marketKeyPrefixPermissionsForAddress(marketID, addr, 1) rv = append(rv, byte(permission)) return rv } +// marketKeyPrefixReqAttr creates the key prefix for a market's required attributes entries with an extra byte of capacity for the order type. +func marketKeyPrefixReqAttr(marketID uint32) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypeReqAttr, 1) +} + +// MakeKeyMarketReqAttrAsk creates the key to use for a market's attributes required to create an ask order. func MakeKeyMarketReqAttrAsk(marketID uint32) []byte { - rv := marketKeyPrefix(marketID, MarketKeyTypeReqAttr, 1) + rv := marketKeyPrefixReqAttr(marketID) rv = append(rv, OrderKeyTypeAsk) return rv } +// MakeKeyMarketReqAttrBid creates the key to use for a market's attributes required to create an bid order. func MakeKeyMarketReqAttrBid(marketID uint32) []byte { - rv := marketKeyPrefix(marketID, MarketKeyTypeReqAttr, 1) + rv := marketKeyPrefixReqAttr(marketID) rv = append(rv, OrderKeyTypeBid) return rv } +// keyPrefixOrder creates the key prefix for orders with the provide extra capacity for additional elements. func keyPrefixOrder(orderID uint64, extraCap int) []byte { - return concatBzPlusCap(KeyTypeOrder, uint64Bz(orderID), nil, extraCap) + return prepKey(KeyTypeOrder, uint64Bz(orderID), extraCap) } -func MakeOrderPrefix(orderID uint64) []byte { +// MakeKeyPrefixOrder creates the key prefix for the given order id. +func MakeKeyPrefixOrder(orderID uint64) []byte { return keyPrefixOrder(orderID, 0) } -func MakeOrderKey(order exchange.Order) []byte { +// MakeKeyOrder makes the key to use for the given order. +func MakeKeyOrder(order exchange.Order) []byte { rv := keyPrefixOrder(order.GetOrderId(), 1) rv = append(rv, order.OrderTypeByte()) return rv } +// indexPrefixMarketToOrder creates the prefix for the market to order prefix entries with some extra space for the rest. func indexPrefixMarketToOrder(marketID uint32, extraCap int) []byte { - return concatBzPlusCap(KeyTypeMarketToOrderIndex, uint32Bz(marketID), nil, extraCap) + return prepKey(KeyTypeMarketToOrderIndex, uint32Bz(marketID), extraCap) } +// MakeIndexPrefixMarketToOrder creates the prefix for the market to order index limited ot the given market id. func MakeIndexPrefixMarketToOrder(marketID uint32) []byte { return indexPrefixMarketToOrder(marketID, 0) } +// MakeIndexKeyMarketToOrder creates the key to use for the market to order index with the given ids. func MakeIndexKeyMarketToOrder(marketID uint32, orderID uint64) []byte { rv := indexPrefixMarketToOrder(marketID, 8) rv = append(rv, uint64Bz(orderID)...) return rv } +// indexPrefixAddressToOrder creates the prefix for the address to order index entries with some extra apace for the rest. func indexPrefixAddressToOrder(addr sdk.AccAddress, extraCap int) []byte { - return concatBzPlusCap(KeyTypeAddressToOrderIndex, address.MustLengthPrefix(addr), nil, extraCap) + return prepKey(KeyTypeAddressToOrderIndex, address.MustLengthPrefix(addr), extraCap) } +// MakeIndexPrefixAddressToOrder creates a key prefix for the address to order index limited to the given address. func MakeIndexPrefixAddressToOrder(addr sdk.AccAddress) []byte { return indexPrefixAddressToOrder(addr, 0) } +// MakeIndexKeyAddressToOrder creates the key to use for the address to order index with the given values. func MakeIndexKeyAddressToOrder(addr sdk.AccAddress, orderID uint64) []byte { rv := indexPrefixAddressToOrder(addr, 8) rv = append(rv, uint64Bz(orderID)...) return rv } +// indexPrefixAssetToOrder creates the prefix for the asset to order index enties with some extra space for the rest. func indexPrefixAssetToOrder(assetDenom string, extraCap int) []byte { - return concatBzPlusCap(KeyTypeAssetToOrderIndex, []byte(assetDenom), nil, extraCap) + return prepKey(KeyTypeAssetToOrderIndex, []byte(assetDenom), extraCap) } +// MakeIndexPrefixAssetToOrder creates a key prefix for the asset to order index limited to the given asset. func MakeIndexPrefixAssetToOrder(assetDenom string) []byte { return indexPrefixAssetToOrder(assetDenom, 0) } +// MakeIndexPrefixAssetToOrderAsks creates a key prefix for the asset to orders limited to the given asset and only ask orders. func MakeIndexPrefixAssetToOrderAsks(assetDenom string) []byte { rv := indexPrefixAssetToOrder(assetDenom, 1) rv = append(rv, OrderKeyTypeAsk) return rv } +// MakeIndexPrefixAssetToOrderBids creates a key prefix for the asset to orders limited to the given asset and only bid orders. func MakeIndexPrefixAssetToOrderBids(assetDenom string) []byte { rv := indexPrefixAssetToOrder(assetDenom, 1) rv = append(rv, OrderKeyTypeBid) return rv } +// MakeIndexKeyAssetToOrder creates the key to use for the asset to order index for the provided values. func MakeIndexKeyAssetToOrder(assetDenom string, order exchange.Order) []byte { rv := indexPrefixAssetToOrder(assetDenom, 9) rv = append(rv, order.OrderTypeByte()) diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go new file mode 100644 index 0000000000..44907745a7 --- /dev/null +++ b/x/exchange/keeper/keys_test.go @@ -0,0 +1,226 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/provenance-io/provenance/x/exchange/keeper" +) + +// concatBz combines all provided byte slices into a single one. +func concatBz(bzs ...[]byte) []byte { + var rv []byte + for _, bz := range bzs { + rv = append(rv, bz...) + } + return rv +} + +func TestKeyTypeUniqueness(t *testing.T) { + type byteEntry struct { + name string + value byte + } + + tests := []struct { + name string + types []byteEntry + }{ + { + name: "base type bytes", + types: []byteEntry{ + {name: "KeyTypeParams", value: keeper.KeyTypeParams}, + {name: "KeyTypeMarket", value: keeper.KeyTypeMarket}, + {name: "KeyTypeOrder", value: keeper.KeyTypeOrder}, + {name: "KeyTypeMarketToOrderIndex", value: keeper.KeyTypeMarketToOrderIndex}, + {name: "KeyTypeAddressToOrderIndex", value: keeper.KeyTypeAddressToOrderIndex}, + {name: "KeyTypeAssetToOrderIndex", value: keeper.KeyTypeAssetToOrderIndex}, + }, + }, + { + name: "market type bytes", + types: []byteEntry{ + {name: "MarketKeyTypeCreateAskFlat", value: keeper.MarketKeyTypeCreateAskFlat}, + {name: "MarketKeyTypeCreateBidFlat", value: keeper.MarketKeyTypeCreateBidFlat}, + {name: "MarketKeyTypeSettlementSellerFlat", value: keeper.MarketKeyTypeSettlementSellerFlat}, + {name: "MarketKeyTypeSettlementSellerRatio", value: keeper.MarketKeyTypeSettlementSellerRatio}, + {name: "MarketKeyTypeSettlementBuyerFlat", value: keeper.MarketKeyTypeSettlementBuyerFlat}, + {name: "MarketKeyTypeSettlementBuyerRatio", value: keeper.MarketKeyTypeSettlementBuyerRatio}, + {name: "MarketKeyTypeInactive", value: keeper.MarketKeyTypeInactive}, + {name: "MarketKeyTypeSelfSettle", value: keeper.MarketKeyTypeSelfSettle}, + {name: "MarketKeyTypePermissions", value: keeper.MarketKeyTypePermissions}, + {name: "MarketKeyTypeReqAttr", value: keeper.MarketKeyTypeReqAttr}, + }, + }, + { + name: "order types", + types: []byteEntry{ + {name: "OrderKeyTypeAsk", value: keeper.OrderKeyTypeAsk}, + {name: "OrderKeyTypeBid", value: keeper.OrderKeyTypeBid}, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + seen := make(map[byte]string) + for _, entry := range tc.types { + prev, found := seen[entry.value] + assert.False(t, found, "byte %#x used for both %s and %s", prev, entry.name) + seen[entry.value] = entry.name + } + }) + } +} + +func TestMakeKeyParamsSplit(t *testing.T) { + tests := []struct { + name string + denom string + exp []byte + }{ + { + name: "empty denom", + denom: "", + exp: concatBz([]byte{keeper.KeyTypeParams}, []byte("split")), + }, + { + name: "nhash", + denom: "nhash", + exp: concatBz([]byte{keeper.KeyTypeParams}, []byte("split"), []byte("nhash")), + }, + { + name: "hex string", + denom: "f019431c60d643b288d91a075bd4c323", // Nothing special. Got it from uuidgen. + exp: concatBz([]byte{keeper.KeyTypeParams}, []byte("split"), []byte("f019431c60d643b288d91a075bd4c323")), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual []byte + testFunc := func() { + actual = keeper.MakeKeyParamsSplit(tc.denom) + } + require.NotPanics(t, testFunc, "MakeKeyParamsSplit(%q)", tc.denom) + assert.Equal(t, tc.exp, actual, "MakeKeyParamsSplit(%q)", tc.denom) + assert.Equal(t, len(actual), cap(actual), "length (expected) vs capacity (actual)") + }) + } +} + +func TestMakeKeyPrefixMarket(t *testing.T) { + tests := []struct { + name string + marketID uint32 + exp []byte + }{ + { + name: "market id 0", + marketID: 0, + exp: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0}, + }, + { + name: "market id 1", + marketID: 1, + exp: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1}, + }, + { + name: "market id 255", + marketID: 255, + exp: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255}, + }, + { + name: "market id 256", + marketID: 256, + exp: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0}, + }, + { + name: "market id 65_536", + marketID: 65_536, + exp: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + exp: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + exp: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual []byte + testFunc := func() { + actual = keeper.MakeKeyPrefixMarket(tc.marketID) + } + require.NotPanics(t, testFunc, "MakeKeyPrefixMarket(%d)", tc.marketID) + assert.Equal(t, tc.exp, actual, "MakeKeyPrefixMarket(%d)", tc.marketID) + assert.Equal(t, len(actual), cap(actual), "length (expected) vs capacity (actual)") + }) + } +} + +// TODO[1658]: func TestMakeKeyPrefixMarketCreateAskFlatFee(t *testing.T) + +// TODO[1658]: func TestMakeKeyMarketCreateAskFlatFee(t *testing.T) + +// TODO[1658]: func TestMakeKeyPrefixMarketCreateBidFlatFee(t *testing.T) + +// TODO[1658]: func TestMakeKeyMarketCreateBidFlatFee(t *testing.T) + +// TODO[1658]: func TestMakeKeyPrefixMarketSettlementSellerFlatFee(t *testing.T) + +// TODO[1658]: func TestMakeKeyMarketSettlementSellerFlatFee(t *testing.T) + +// TODO[1658]: func TestMakeKeyPrefixMarketSettlementSellerRatio(t *testing.T) + +// TODO[1658]: func TestMakeKeyMarketSettlementSellerRatio(t *testing.T) + +// TODO[1658]: func TestMakeKeyPrefixMarketSettlementBuyerFlatFee(t *testing.T) + +// TODO[1658]: func TestMakeKeyMarketSettlementBuyerFlatFee(t *testing.T) + +// TODO[1658]: func TestMakeKeyPrefixMarketSettlementBuyerRatio(t *testing.T) + +// TODO[1658]: func TestMakeKeyMarketSettlementBuyerRatio(t *testing.T) + +// TODO[1658]: func TestMakeKeyMarketInactive(t *testing.T) + +// TODO[1658]: func TestMakeKeyMarketSelfSettle(t *testing.T) + +// TODO[1658]: func TestMakeKeyPrefixMarketPermissions(t *testing.T) + +// TODO[1658]: func TestMakeKeyPrefixMarketPermissionsForAddress(t *testing.T) + +// TODO[1658]: func TestMakeKeyMarketPermissions(t *testing.T) + +// TODO[1658]: func TestMakeKeyMarketReqAttrAsk(t *testing.T) + +// TODO[1658]: func TestMakeKeyMarketReqAttrBid(t *testing.T) + +// TODO[1658]: func TestMakeKeyPrefixOrder(t *testing.T) + +// TODO[1658]: func TestMakeKeyOrder(t *testing.T) + +// TODO[1658]: func TestMakeIndexPrefixMarketToOrder(t *testing.T) + +// TODO[1658]: func TestMakeIndexKeyMarketToOrder(t *testing.T) + +// TODO[1658]: func TestMakeIndexPrefixAddressToOrder(t *testing.T) + +// TODO[1658]: func TestMakeIndexKeyAddressToOrder(t *testing.T) + +// TODO[1658]: func TestMakeIndexPrefixAssetToOrder(t *testing.T) + +// TODO[1658]: func TestMakeIndexPrefixAssetToOrderAsks(t *testing.T) + +// TODO[1658]: func TestMakeIndexPrefixAssetToOrderBids(t *testing.T) + +// TODO[1658]: func TestMakeIndexKeyAssetToOrder(t *testing.T) From a99bc86e46d42684d1df9561a0dbf6c12edd8435 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 12:01:15 -0600 Subject: [PATCH 051/309] [1658]: Change OrderTypeByteAsk and OrderTypeByteBid into byte variables (were defaulted to int). --- x/exchange/keeper/keys.go | 1 + x/exchange/orders.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index acaaf7b2ac..b387f4a406 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -6,6 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/address" + "github.com/provenance-io/provenance/x/exchange" ) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index fdaffc5a2f..f4e71cf117 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -11,10 +11,10 @@ import ( // These must all be unique. If you add entries, be sure to update TestOrderTypesAndBytes too. const ( OrderTypeAsk = "ask" - OrderTypeByteAsk = 0x00 + OrderTypeByteAsk = byte(0x00) OrderTypeBid = "bid" - OrderTypeByteBid = 0x01 + OrderTypeByteBid = byte(0x01) ) // NewOrder creates a new empty Order with the provided order id. From 1441fc3e170d09c820d02527f017c22da1db666c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 12:07:22 -0600 Subject: [PATCH 052/309] [1658]: Change OrderType and OrderTypeByte to GetOrderType and GetOrderTypeByte. --- x/exchange/events.go | 2 +- x/exchange/events_test.go | 2 +- x/exchange/keeper/keys.go | 4 ++-- x/exchange/orders.go | 16 ++++++++-------- x/exchange/orders_test.go | 30 +++++++++++++++--------------- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/x/exchange/events.go b/x/exchange/events.go index 67bc0bccf7..056761405d 100644 --- a/x/exchange/events.go +++ b/x/exchange/events.go @@ -5,7 +5,7 @@ import sdk "github.com/cosmos/cosmos-sdk/types" func NewEventOrderCreated(order *Order) *EventOrderCreated { return &EventOrderCreated{ OrderId: order.GetOrderId(), - OrderType: order.OrderType(), + OrderType: order.GetOrderType(), } } diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go index 836081c517..42d0c53b52 100644 --- a/x/exchange/events_test.go +++ b/x/exchange/events_test.go @@ -40,7 +40,7 @@ func TestNewEventOrderCreated(t *testing.T) { { name: "nil order", order: NewOrder(3), - expPanic: "OrderType() missing case for ", + expPanic: "GetOrderType() missing case for ", }, { name: "order with ask", diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index b387f4a406..ecf157d00d 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -334,7 +334,7 @@ func MakeKeyPrefixOrder(orderID uint64) []byte { // MakeKeyOrder makes the key to use for the given order. func MakeKeyOrder(order exchange.Order) []byte { rv := keyPrefixOrder(order.GetOrderId(), 1) - rv = append(rv, order.OrderTypeByte()) + rv = append(rv, order.GetOrderTypeByte()) return rv } @@ -399,7 +399,7 @@ func MakeIndexPrefixAssetToOrderBids(assetDenom string) []byte { // MakeIndexKeyAssetToOrder creates the key to use for the asset to order index for the provided values. func MakeIndexKeyAssetToOrder(assetDenom string, order exchange.Order) []byte { rv := indexPrefixAssetToOrder(assetDenom, 9) - rv = append(rv, order.OrderTypeByte()) + rv = append(rv, order.GetOrderTypeByte()) rv = append(rv, uint64Bz(order.GetOrderId())...) return rv } diff --git a/x/exchange/orders.go b/x/exchange/orders.go index f4e71cf117..320cee3523 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -35,35 +35,35 @@ func (o *Order) WithBid(bidOrder *BidOrder) *Order { return o } -// OrderType returns a string indicating what type this order is. +// GetOrderType returns a string indicating what type this order is. // See: OrderTypeAsk, OrderTypeBid // Panics if the order details are not set or are something unexpected. -func (o Order) OrderType() string { +func (o Order) GetOrderType() string { switch v := o.GetOrder().(type) { case *Order_AskOrder: return OrderTypeAsk case *Order_BidOrder: return OrderTypeBid default: - // If OrderType() is called without the order being set yet, it's a programming error, so panic. + // If GetOrderType() is called without the order being set yet, it's a programming error, so panic. // If it's a type without a case, the case needs to be added, so panic. - panic(fmt.Sprintf("OrderType() missing case for %T", v)) + panic(fmt.Sprintf("GetOrderType() missing case for %T", v)) } } -// OrderTypeByte returns the type byte for this order. +// GetOrderTypeByte returns the type byte for this order. // See: OrderTypeByteAsk, OrderTypeByteBid // Panics if the order details are not set or are something unexpected. -func (o Order) OrderTypeByte() byte { +func (o Order) GetOrderTypeByte() byte { switch v := o.GetOrder().(type) { case *Order_AskOrder: return OrderTypeByteAsk case *Order_BidOrder: return OrderTypeByteBid default: - // If OrderTypeByte() is called without the order being set yet, it's a programming error, so panic. + // If GetOrderTypeByte() is called without the order being set yet, it's a programming error, so panic. // If it's a type without a case, the case needs to be added, so panic. - panic(fmt.Sprintf("OrderTypeByte() missing case for %T", v)) + panic(fmt.Sprintf("GetOrderTypeByte() missing case for %T", v)) } } diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index f8f5c73c72..4535b05f3e 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -49,7 +49,7 @@ func TestOrderTypesAndBytes(t *testing.T) { } ot := func(name string) string { - return "OrderType" + name + return "GetOrderType" + name } otb := func(name string) string { return "OrderTypeByte" + name @@ -214,12 +214,12 @@ func TestOrder_OrderType(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: "OrderType() missing case for ", + expPanic: "GetOrderType() missing case for ", }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: "OrderType() missing case for *exchange.unknownOrderType", + expPanic: "GetOrderType() missing case for *exchange.unknownOrderType", }, } @@ -227,16 +227,16 @@ func TestOrder_OrderType(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var actual string testFunc := func() { - actual = tc.order.OrderType() + actual = tc.order.GetOrderType() } - // TODO[1658]: Refactor to use testutils.RequirePanicEquals(t, testFunc, tc.expPanic, "OrderType") + // TODO[1658]: Refactor to use testutils.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOrderType") if tc.expPanic != nil { - require.PanicsWithValue(t, tc.expPanic, testFunc, "OrderType") + require.PanicsWithValue(t, tc.expPanic, testFunc, "GetOrderType") } else { - require.NotPanics(t, testFunc, "OrderType") + require.NotPanics(t, testFunc, "GetOrderType") } - assert.Equal(t, tc.expected, actual, "OrderType result") + assert.Equal(t, tc.expected, actual, "GetOrderType result") }) } } @@ -261,12 +261,12 @@ func TestOrder_OrderTypeByte(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: "OrderTypeByte() missing case for ", + expPanic: "GetOrderTypeByte() missing case for ", }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: "OrderTypeByte() missing case for *exchange.unknownOrderType", + expPanic: "GetOrderTypeByte() missing case for *exchange.unknownOrderType", }, } @@ -274,16 +274,16 @@ func TestOrder_OrderTypeByte(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var actual byte testFunc := func() { - actual = tc.order.OrderTypeByte() + actual = tc.order.GetOrderTypeByte() } - // TODO[1658]: Refactor to use testutils.RequirePanicEquals(t, testFunc, tc.expPanic, "OrderTypeByte") + // TODO[1658]: Refactor to use testutils.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOrderTypeByte") if tc.expPanic != nil { - require.PanicsWithValue(t, tc.expPanic, testFunc, "OrderTypeByte") + require.PanicsWithValue(t, tc.expPanic, testFunc, "GetOrderTypeByte") } else { - require.NotPanics(t, testFunc, "OrderTypeByte") + require.NotPanics(t, testFunc, "GetOrderTypeByte") } - assert.Equal(t, tc.expected, actual, "OrderTypeByte result") + assert.Equal(t, tc.expected, actual, "GetOrderTypeByte result") }) } } From 3cff5d5e37b675b6b9ffd495af0c79a0ab4f9857 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 13:30:19 -0600 Subject: [PATCH 053/309] [1658]: Unit tests on the first half-or-so of the key makers. --- x/exchange/keeper/keys.go | 4 + x/exchange/keeper/keys_test.go | 1483 ++++++++++++++++++++++++++++++-- 2 files changed, 1438 insertions(+), 49 deletions(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index ecf157d00d..3fc15face6 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -2,6 +2,7 @@ package keeper import ( "encoding/binary" + "errors" "fmt" sdk "github.com/cosmos/cosmos-sdk/types" @@ -277,6 +278,9 @@ func marketKeyPrefixPermissions(marketID uint32, extraCap int) []byte { // marketKeyPrefixPermissionsForAddress creates the key prefix for an address' permissions in a given market with extra capacity for the rest. func marketKeyPrefixPermissionsForAddress(marketID uint32, addr sdk.AccAddress, extraCap int) []byte { + if len(addr) == 0 { + panic(errors.New("empty address not allowed")) + } rv := marketKeyPrefixPermissions(marketID, 1+len(addr)+extraCap) rv = append(rv, address.MustLengthPrefix(addr)...) return rv diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 44907745a7..b6d84ac1ec 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -6,9 +6,14 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/provenance-io/provenance/x/exchange" "github.com/provenance-io/provenance/x/exchange/keeper" ) +const hexString = "8fde739c8158424b93dfc27b08e40285" // randomly generated using uuidgen + // concatBz combines all provided byte slices into a single one. func concatBz(bzs ...[]byte) []byte { var rv []byte @@ -18,6 +23,46 @@ func concatBz(bzs ...[]byte) []byte { return rv } +// expectedPrefix gives a name to a prefix that a key is expected to have. +type expectedPrefix struct { + name string + value []byte +} + +type keyTestCase struct { + maker func() []byte + expected []byte + expPanic string + expPrefixes []expectedPrefix +} + +// checkKey calls the maker and asserts that the result is as expected. +// Also asserts that the result has the provided prefixes. +func checkKey(t *testing.T, tc keyTestCase, msg string, args ...interface{}) { + t.Helper() + var actual []byte + testFunc := func() { + actual = tc.maker() + } + // TODO[1658]: Replace this with a testutils panic checker. + if len(tc.expPanic) > 0 { + require.PanicsWithErrorf(t, tc.expPanic, testFunc, msg, args...) + } else { + require.NotPanicsf(t, testFunc, msg, args...) + } + assert.Equalf(t, tc.expected, actual, msg+" result", args...) + if len(actual) > 0 { + assert.Equalf(t, len(actual), cap(actual), msg+" result length (expected) vs capacity (actual)", args...) + for _, expPre := range tc.expPrefixes { + actPre := actual + if len(actPre) > len(expPre.value) { + actPre = actPre[:len(expPre.value)] + } + assert.Equalf(t, expPre.value, actPre, msg+" result %s prefix", append(args, expPre.name)) + } + } +} + func TestKeyTypeUniqueness(t *testing.T) { type byteEntry struct { name string @@ -77,36 +122,36 @@ func TestKeyTypeUniqueness(t *testing.T) { func TestMakeKeyParamsSplit(t *testing.T) { tests := []struct { - name string - denom string - exp []byte + name string + denom string + expected []byte }{ { - name: "empty denom", - denom: "", - exp: concatBz([]byte{keeper.KeyTypeParams}, []byte("split")), + name: "empty denom", + denom: "", + expected: concatBz([]byte{keeper.KeyTypeParams}, []byte("split")), }, { - name: "nhash", - denom: "nhash", - exp: concatBz([]byte{keeper.KeyTypeParams}, []byte("split"), []byte("nhash")), + name: "nhash", + denom: "nhash", + expected: concatBz([]byte{keeper.KeyTypeParams}, []byte("split"), []byte("nhash")), }, { - name: "hex string", - denom: "f019431c60d643b288d91a075bd4c323", // Nothing special. Got it from uuidgen. - exp: concatBz([]byte{keeper.KeyTypeParams}, []byte("split"), []byte("f019431c60d643b288d91a075bd4c323")), + name: "hex string", + denom: hexString, + expected: concatBz([]byte{keeper.KeyTypeParams}, []byte("split"), []byte(hexString)), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var actual []byte - testFunc := func() { - actual = keeper.MakeKeyParamsSplit(tc.denom) + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyParamsSplit(tc.denom) + }, + expected: tc.expected, } - require.NotPanics(t, testFunc, "MakeKeyParamsSplit(%q)", tc.denom) - assert.Equal(t, tc.exp, actual, "MakeKeyParamsSplit(%q)", tc.denom) - assert.Equal(t, len(actual), cap(actual), "length (expected) vs capacity (actual)") + checkKey(t, ktc, "MakeKeyParamsSplit(%q)", tc.denom) }) } } @@ -115,91 +160,1431 @@ func TestMakeKeyPrefixMarket(t *testing.T) { tests := []struct { name string marketID uint32 - exp []byte + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyPrefixMarket(tc.marketID) + }, + expected: tc.expected, + } + checkKey(t, ktc, "MakeKeyPrefixMarket(%d)", tc.marketID) + }) + } +} + +func TestMakeKeyPrefixMarketCreateAskFlatFee(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeCreateAskFlat + + tests := []struct { + name string + marketID uint32 + expected []byte }{ { name: "market id 0", marketID: 0, - exp: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, }, { name: "market id 1", marketID: 1, - exp: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, }, { name: "market id 255", marketID: 255, - exp: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255, marketTypeByte}, }, { name: "market id 256", marketID: 256, - exp: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0, marketTypeByte}, }, { name: "market id 65_536", marketID: 65_536, - exp: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0}, + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0, marketTypeByte}, }, { name: "market id 16,777,216", marketID: 16_777_216, - exp: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0}, + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, }, { name: "market id 4,294,967,295", marketID: 4_294_967_295, - exp: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255}, + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var actual []byte - testFunc := func() { - actual = keeper.MakeKeyPrefixMarket(tc.marketID) + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyPrefixMarketCreateAskFlatFee(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + }, } - require.NotPanics(t, testFunc, "MakeKeyPrefixMarket(%d)", tc.marketID) - assert.Equal(t, tc.exp, actual, "MakeKeyPrefixMarket(%d)", tc.marketID) - assert.Equal(t, len(actual), cap(actual), "length (expected) vs capacity (actual)") + checkKey(t, ktc, "MakeKeyPrefixMarketCreateAskFlatFee(%d)", tc.marketID) }) } } -// TODO[1658]: func TestMakeKeyPrefixMarketCreateAskFlatFee(t *testing.T) +func TestMakeKeyMarketCreateAskFlatFee(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeCreateAskFlat -// TODO[1658]: func TestMakeKeyMarketCreateAskFlatFee(t *testing.T) + tests := []struct { + name string + marketID uint32 + denom string + expected []byte + }{ + { + name: "market id 0 no denom", + marketID: 0, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 0 nhash", + marketID: 0, + denom: "nhash", + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, "nhash"...), + }, + { + name: "market id 0 hex string", + marketID: 0, + denom: hexString, + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, hexString...), + }, + { + name: "market id 1 no denom", + marketID: 1, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + }, + { + name: "market id 1 nhash", + marketID: 1, + denom: "nhash", + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, "nhash"...), + }, + { + name: "market id 1 hex string", + marketID: 1, + denom: hexString, + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, hexString...), + }, + { + name: "market id 16,843,009 no denom", + marketID: 16_843_009, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, + }, + { + name: "market id 16,843,009 nhash", + marketID: 16_843_009, + denom: "nhash", + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, "nhash"...), + }, + { + name: "market id 16,843,009 hex string", + marketID: 16_843_009, + denom: hexString, + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, hexString...), + }, + } -// TODO[1658]: func TestMakeKeyPrefixMarketCreateBidFlatFee(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyMarketCreateAskFlatFee(tc.marketID, tc.denom) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + { + name: "MakeKeyPrefixMarket", + value: keeper.MakeKeyPrefixMarket(tc.marketID), + }, + { + name: "MakeKeyPrefixMarketCreateBidFlatFee", + value: keeper.MakeKeyPrefixMarketCreateAskFlatFee(tc.marketID), + }, + }, + } + checkKey(t, ktc, "MakeKeyMarketCreateAskFlatFee(%d)", tc.marketID) + }) + } +} -// TODO[1658]: func TestMakeKeyMarketCreateBidFlatFee(t *testing.T) +func TestMakeKeyPrefixMarketCreateBidFlatFee(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeCreateBidFlat -// TODO[1658]: func TestMakeKeyPrefixMarketSettlementSellerFlatFee(t *testing.T) + tests := []struct { + name string + marketID uint32 + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255, marketTypeByte}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0, marketTypeByte}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte}, + }, + } -// TODO[1658]: func TestMakeKeyMarketSettlementSellerFlatFee(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyPrefixMarketCreateBidFlatFee(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + }, + } + checkKey(t, ktc, "MakeKeyPrefixMarketCreateBidFlatFee(%d)", tc.marketID) + }) + } +} -// TODO[1658]: func TestMakeKeyPrefixMarketSettlementSellerRatio(t *testing.T) +func TestMakeKeyMarketCreateBidFlatFee(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeCreateBidFlat -// TODO[1658]: func TestMakeKeyMarketSettlementSellerRatio(t *testing.T) + tests := []struct { + name string + marketID uint32 + denom string + expected []byte + }{ + { + name: "market id 0 no denom", + marketID: 0, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 0 nhash", + marketID: 0, + denom: "nhash", + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, "nhash"...), + }, + { + name: "market id 0 hex string", + marketID: 0, + denom: hexString, + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, hexString...), + }, + { + name: "market id 1 no denom", + marketID: 1, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + }, + { + name: "market id 1 nhash", + marketID: 1, + denom: "nhash", + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, "nhash"...), + }, + { + name: "market id 1 hex string", + marketID: 1, + denom: hexString, + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, hexString...), + }, + { + name: "market id 16,843,009 no denom", + marketID: 16_843_009, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, + }, + { + name: "market id 16,843,009 nhash", + marketID: 16_843_009, + denom: "nhash", + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, "nhash"...), + }, + { + name: "market id 16,843,009 hex string", + marketID: 16_843_009, + denom: hexString, + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, hexString...), + }, + } -// TODO[1658]: func TestMakeKeyPrefixMarketSettlementBuyerFlatFee(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyMarketCreateBidFlatFee(tc.marketID, tc.denom) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + { + name: "MakeKeyPrefixMarket", + value: keeper.MakeKeyPrefixMarket(tc.marketID), + }, + { + name: "MakeKeyPrefixMarketCreateBidFlatFee", + value: keeper.MakeKeyPrefixMarketCreateBidFlatFee(tc.marketID), + }, + }, + } + checkKey(t, ktc, "MakeKeyMarketCreateBidFlatFee(%d)", tc.marketID) + }) + } +} -// TODO[1658]: func TestMakeKeyMarketSettlementBuyerFlatFee(t *testing.T) +func TestMakeKeyPrefixMarketSettlementSellerFlatFee(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSettlementSellerFlat -// TODO[1658]: func TestMakeKeyPrefixMarketSettlementBuyerRatio(t *testing.T) + tests := []struct { + name string + marketID uint32 + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255, marketTypeByte}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0, marketTypeByte}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte}, + }, + } -// TODO[1658]: func TestMakeKeyMarketSettlementBuyerRatio(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyPrefixMarketSettlementSellerFlatFee(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + }, + } + checkKey(t, ktc, "MakeKeyPrefixMarketSettlementSellerFlatFee(%d)", tc.marketID) + }) + } +} -// TODO[1658]: func TestMakeKeyMarketInactive(t *testing.T) +func TestMakeKeyMarketSettlementSellerFlatFee(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSettlementSellerFlat -// TODO[1658]: func TestMakeKeyMarketSelfSettle(t *testing.T) + tests := []struct { + name string + marketID uint32 + denom string + expected []byte + }{ + { + name: "market id 0 no denom", + marketID: 0, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 0 nhash", + marketID: 0, + denom: "nhash", + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, "nhash"...), + }, + { + name: "market id 0 hex string", + marketID: 0, + denom: hexString, + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, hexString...), + }, + { + name: "market id 1 no denom", + marketID: 1, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + }, + { + name: "market id 1 nhash", + marketID: 1, + denom: "nhash", + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, "nhash"...), + }, + { + name: "market id 1 hex string", + marketID: 1, + denom: hexString, + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, hexString...), + }, + { + name: "market id 16,843,009 no denom", + marketID: 16_843_009, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, + }, + { + name: "market id 16,843,009 nhash", + marketID: 16_843_009, + denom: "nhash", + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, "nhash"...), + }, + { + name: "market id 16,843,009 hex string", + marketID: 16_843_009, + denom: hexString, + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, hexString...), + }, + } -// TODO[1658]: func TestMakeKeyPrefixMarketPermissions(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyMarketSettlementSellerFlatFee(tc.marketID, tc.denom) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + { + name: "MakeKeyPrefixMarket", + value: keeper.MakeKeyPrefixMarket(tc.marketID), + }, + { + name: "MakeKeyPrefixMarketSettlementSellerFlatFee", + value: keeper.MakeKeyPrefixMarketSettlementSellerFlatFee(tc.marketID), + }, + }, + } + checkKey(t, ktc, "MakeKeyMarketSettlementSellerFlatFee(%d)", tc.marketID) + }) + } +} -// TODO[1658]: func TestMakeKeyPrefixMarketPermissionsForAddress(t *testing.T) +func TestMakeKeyPrefixMarketSettlementSellerRatio(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSettlementSellerRatio -// TODO[1658]: func TestMakeKeyMarketPermissions(t *testing.T) + tests := []struct { + name string + marketID uint32 + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255, marketTypeByte}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0, marketTypeByte}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyPrefixMarketSettlementSellerRatio(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + }, + } + checkKey(t, ktc, "MakeKeyPrefixMarketSettlementSellerRatio(%d)", tc.marketID) + }) + } +} + +func TestMakeKeyMarketSettlementSellerRatio(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSettlementSellerRatio + coin := func(denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.OneInt()} + } + + tests := []struct { + name string + marketID uint32 + ratio exchange.FeeRatio + expected []byte + }{ + { + name: "market id 0 both denoms empty", + marketID: 0, + ratio: exchange.FeeRatio{Price: coin(""), Fee: coin("")}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, 0}, + }, + { + name: "market id 1 nhash to empty", + marketID: 1, + ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin("")}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', 0}, + }, + { + name: "market id 1 empty to nhash", + marketID: 1, + ratio: exchange.FeeRatio{Price: coin(""), Fee: coin("nhash")}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 0, 'n', 'h', 'a', 's', 'h'}, + }, + { + name: "market id 1 nhash to nhash", + marketID: 1, + ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin("nhash")}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', 0, 'n', 'h', 'a', 's', 'h'}, + }, + { + name: "market id 16,843,009 nhash to hex string", + marketID: 16_843_009, + ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin(hexString)}, + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', 0}, hexString...), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyMarketSettlementSellerRatio(tc.marketID, tc.ratio) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + { + name: "MakeKeyPrefixMarket", + value: keeper.MakeKeyPrefixMarket(tc.marketID), + }, + { + name: "MakeKeyPrefixMarketSettlementSellerRatio", + value: keeper.MakeKeyPrefixMarketSettlementSellerRatio(tc.marketID), + }, + }, + } + checkKey(t, ktc, "MakeKeyMarketSettlementSellerRatio(%d)", tc.marketID) + }) + } +} + +func TestMakeKeyPrefixMarketSettlementBuyerFlatFee(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSettlementBuyerFlat + + tests := []struct { + name string + marketID uint32 + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255, marketTypeByte}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0, marketTypeByte}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyPrefixMarketSettlementBuyerFlatFee(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + }, + } + checkKey(t, ktc, "MakeKeyPrefixMarketSettlementBuyerFlatFee(%d)", tc.marketID) + }) + } +} + +func TestMakeKeyMarketSettlementBuyerFlatFee(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSettlementBuyerFlat + + tests := []struct { + name string + marketID uint32 + denom string + expected []byte + }{ + { + name: "market id 0 no denom", + marketID: 0, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 0 nhash", + marketID: 0, + denom: "nhash", + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, "nhash"...), + }, + { + name: "market id 0 hex string", + marketID: 0, + denom: hexString, + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, hexString...), + }, + { + name: "market id 1 no denom", + marketID: 1, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + }, + { + name: "market id 1 nhash", + marketID: 1, + denom: "nhash", + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, "nhash"...), + }, + { + name: "market id 1 hex string", + marketID: 1, + denom: hexString, + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, hexString...), + }, + { + name: "market id 16,843,009 no denom", + marketID: 16_843_009, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, + }, + { + name: "market id 16,843,009 nhash", + marketID: 16_843_009, + denom: "nhash", + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, "nhash"...), + }, + { + name: "market id 16,843,009 hex string", + marketID: 16_843_009, + denom: hexString, + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, hexString...), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyMarketSettlementBuyerFlatFee(tc.marketID, tc.denom) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + { + name: "MakeKeyPrefixMarket", + value: keeper.MakeKeyPrefixMarket(tc.marketID), + }, + { + name: "MakeKeyPrefixMarketSettlementBuyerFlatFee", + value: keeper.MakeKeyPrefixMarketSettlementBuyerFlatFee(tc.marketID), + }, + }, + } + checkKey(t, ktc, "MakeKeyMarketSettlementBuyerFlatFee(%d)", tc.marketID) + }) + } +} + +func TestMakeKeyPrefixMarketSettlementBuyerRatio(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSettlementBuyerRatio + + tests := []struct { + name string + marketID uint32 + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255, marketTypeByte}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0, marketTypeByte}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyPrefixMarketSettlementBuyerRatio(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + }, + } + checkKey(t, ktc, "MakeKeyPrefixMarketSettlementBuyerRatio(%d)", tc.marketID) + }) + } +} + +func TestMakeKeyMarketSettlementBuyerRatio(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSettlementBuyerRatio + coin := func(denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.OneInt()} + } + + tests := []struct { + name string + marketID uint32 + ratio exchange.FeeRatio + expected []byte + }{ + { + name: "market id 0 both denoms empty", + marketID: 0, + ratio: exchange.FeeRatio{Price: coin(""), Fee: coin("")}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, 0}, + }, + { + name: "market id 1 nhash to empty", + marketID: 1, + ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin("")}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', 0}, + }, + { + name: "market id 1 empty to nhash", + marketID: 1, + ratio: exchange.FeeRatio{Price: coin(""), Fee: coin("nhash")}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 0, 'n', 'h', 'a', 's', 'h'}, + }, + { + name: "market id 1 nhash to nhash", + marketID: 1, + ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin("nhash")}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', 0, 'n', 'h', 'a', 's', 'h'}, + }, + { + name: "market id 16,843,009 nhash to hex string", + marketID: 16_843_009, + ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin(hexString)}, + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', 0}, hexString...), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyMarketSettlementBuyerRatio(tc.marketID, tc.ratio) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + { + name: "MakeKeyPrefixMarket", + value: keeper.MakeKeyPrefixMarket(tc.marketID), + }, + { + name: "MakeKeyPrefixMarketSettlementBuyerRatio", + value: keeper.MakeKeyPrefixMarketSettlementBuyerRatio(tc.marketID), + }, + }, + } + checkKey(t, ktc, "MakeKeyMarketSettlementBuyerRatio(%d)", tc.marketID) + }) + } +} + +func TestMakeKeyMarketInactive(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeInactive + + tests := []struct { + name string + marketID uint32 + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255, marketTypeByte}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0, marketTypeByte}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyMarketInactive(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + }, + } + checkKey(t, ktc, "MakeKeyMarketInactive(%d)", tc.marketID) + }) + } +} + +func TestMakeKeyMarketSelfSettle(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSelfSettle + + tests := []struct { + name string + marketID uint32 + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255, marketTypeByte}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0, marketTypeByte}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyMarketSelfSettle(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + }, + } + checkKey(t, ktc, "MakeKeyMarketSelfSettle(%d)", tc.marketID) + }) + } +} + +func TestMakeKeyPrefixMarketPermissions(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypePermissions + + tests := []struct { + name string + marketID uint32 + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255, marketTypeByte}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0, marketTypeByte}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0, marketTypeByte}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyPrefixMarketPermissions(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + }, + } + checkKey(t, ktc, "MakeKeyPrefixMarketPermissions(%d)", tc.marketID) + }) + } +} + +func TestMakeKeyPrefixMarketPermissionsForAddress(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypePermissions + + tests := []struct { + name string + marketID uint32 + addr sdk.AccAddress + expected []byte + expPanic string + }{ + { + name: "market id 0 nil addr", + marketID: 0, + addr: nil, + expPanic: "empty address not allowed", + }, + { + name: "market id 0 empty addr", + marketID: 0, + addr: sdk.AccAddress{}, + expPanic: "empty address not allowed", + }, + { + name: "market id 0 5 byte addr", + marketID: 0, + addr: sdk.AccAddress("abcde"), + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, 5}, "abcde"...), + }, + { + name: "market id 0 20 byte addr", + marketID: 0, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, 20}, "abcdefghijklmnopqrst"...), + }, + { + name: "market id 0 32 byte addr", + marketID: 0, + addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, 32}, "abcdefghijklmnopqrstuvwxyzABCDEF"...), + }, + { + name: "market id 1 20 byte addr", + marketID: 1, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 20}, "abcdefghijklmnopqrst"...), + }, + { + name: "market id 1 32 byte addr", + marketID: 1, + addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), + expected: append([]byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 32}, "abcdefghijklmnopqrstuvwxyzABCDEF"...), + }, + { + name: "market id 16,843,009 20 byte addr", + marketID: 16_843_009, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, 20}, "abcdefghijklmnopqrst"...), + }, + { + name: "market id 16,843,009 32 byte addr", + marketID: 16_843_009, + addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, 32}, "abcdefghijklmnopqrstuvwxyzABCDEF"...), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyPrefixMarketPermissionsForAddress(tc.marketID, tc.addr) + }, + expected: tc.expected, + expPanic: tc.expPanic, + } + if len(tc.expPanic) == 0 { + ktc.expPrefixes = []expectedPrefix{ + { + name: "MakeKeyPrefixMarket", + value: keeper.MakeKeyPrefixMarket(tc.marketID), + }, + { + name: "MakeKeyPrefixMarketPermissions", + value: keeper.MakeKeyPrefixMarketPermissions(tc.marketID), + }, + } + } + checkKey(t, ktc, "MakeKeyPrefixMarketPermissionsForAddress(%d)", tc.marketID) + }) + } +} + +func TestMakeKeyMarketPermissions(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypePermissions + + tests := []struct { + name string + marketID uint32 + addr sdk.AccAddress + perm exchange.Permission + expected []byte + expPanic string + }{ + { + name: "market id 0 nil addr", + marketID: 0, + addr: nil, + expPanic: "empty address not allowed", + }, + { + name: "market id 0 empty addr", + marketID: 0, + addr: sdk.AccAddress{}, + expPanic: "empty address not allowed", + }, + { + name: "market id 0 5 byte addr settle", + marketID: 0, + addr: sdk.AccAddress("abcde"), + perm: exchange.Permission_settle, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, 5}, + []byte("abcde"), + []byte{byte(exchange.Permission_settle)}, + ), + }, + { + name: "market id 0 20 byte addr cancel", + marketID: 0, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: exchange.Permission_cancel, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{byte(exchange.Permission_cancel)}, + ), + }, + { + name: "market id 0 32 byte addr withdraw", + marketID: 0, + addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), + perm: exchange.Permission_withdraw, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, 32}, + []byte("abcdefghijklmnopqrstuvwxyzABCDEF"), + []byte{byte(exchange.Permission_withdraw)}, + ), + }, + { + name: "market id 1 20 byte addr settle", + marketID: 1, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: exchange.Permission_settle, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{byte(exchange.Permission_settle)}, + ), + }, + { + name: "market id 20 20 byte addr cancel", + marketID: 20, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: exchange.Permission_cancel, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 0, 0, 0, 20, marketTypeByte, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{byte(exchange.Permission_cancel)}, + ), + }, + { + name: "market id 33 20 byte addr withdraw", + marketID: 33, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: exchange.Permission_withdraw, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 0, 0, 0, 33, marketTypeByte, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{byte(exchange.Permission_withdraw)}, + ), + }, + { + name: "market id 48 20 byte addr update", + marketID: 48, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: exchange.Permission_update, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 0, 0, 0, 48, marketTypeByte, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{byte(exchange.Permission_update)}, + ), + }, + { + name: "market id 52 20 byte addr permissions", + marketID: 52, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: exchange.Permission_permissions, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 0, 0, 0, 52, marketTypeByte, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{byte(exchange.Permission_permissions)}, + ), + }, + { + name: "market id 67 20 byte addr attributes", + marketID: 67, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: exchange.Permission_attributes, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 0, 0, 0, 67, marketTypeByte, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{byte(exchange.Permission_attributes)}, + ), + }, + { + name: "market id 258 32 byte addr update", + marketID: 258, + addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), + perm: exchange.Permission_update, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 0, 0, 1, 2, marketTypeByte, 32}, + []byte("abcdefghijklmnopqrstuvwxyzABCDEF"), + []byte{byte(exchange.Permission_update)}, + ), + }, + { + name: "market id 16,843,009 20 byte addr permissions", + marketID: 16_843_009, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: exchange.Permission_permissions, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{byte(exchange.Permission_permissions)}, + ), + }, + { + name: "market id 16,843,009 32 byte addr attributes", + marketID: 16_843_009, + addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), + perm: exchange.Permission_attributes, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, 32}, + []byte("abcdefghijklmnopqrstuvwxyzABCDEF"), + []byte{byte(exchange.Permission_attributes)}, + ), + }, { + name: "market id 67,305,985 20 byte addr unspecified", + marketID: 67_305_985, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: exchange.Permission_unspecified, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 4, 3, 2, 1, marketTypeByte, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{byte(exchange.Permission_unspecified)}, + ), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyMarketPermissions(tc.marketID, tc.addr, tc.perm) + }, + expected: tc.expected, + expPanic: tc.expPanic, + } + if len(tc.expPanic) == 0 { + ktc.expPrefixes = []expectedPrefix{ + { + name: "MakeKeyPrefixMarket", + value: keeper.MakeKeyPrefixMarket(tc.marketID), + }, + { + name: "MakeKeyPrefixMarketPermissions", + value: keeper.MakeKeyPrefixMarketPermissions(tc.marketID), + }, + { + name: "MakeKeyPrefixMarketPermissionsForAddress", + value: keeper.MakeKeyPrefixMarketPermissionsForAddress(tc.marketID, tc.addr), + }, + } + } + checkKey(t, ktc, "MakeKeyMarketPermissions(%d)", tc.marketID) + }) + } +} // TODO[1658]: func TestMakeKeyMarketReqAttrAsk(t *testing.T) From deddb51ad6c67fa0de68cde474050498b81e9876 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 13:33:33 -0600 Subject: [PATCH 054/309] [1658]: Finish unit tests on the market key makers. --- x/exchange/keeper/keys_test.go | 173 ++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 3 deletions(-) diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index b6d84ac1ec..471ec02030 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -1543,7 +1543,8 @@ func TestMakeKeyMarketPermissions(t *testing.T) { []byte("abcdefghijklmnopqrstuvwxyzABCDEF"), []byte{byte(exchange.Permission_attributes)}, ), - }, { + }, + { name: "market id 67,305,985 20 byte addr unspecified", marketID: 67_305_985, addr: sdk.AccAddress("abcdefghijklmnopqrst"), @@ -1554,6 +1555,42 @@ func TestMakeKeyMarketPermissions(t *testing.T) { []byte{byte(exchange.Permission_unspecified)}, ), }, + { + name: "market id 117,967,114 negative permission", + marketID: 117_967_114, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: -1, + expPanic: "permission value -1 out of range for uint8", + }, + { + name: "market id 117,967,114 permission 0", + marketID: 117_967_114, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: 0, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 7, 8, 9, 10, marketTypeByte, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{0}, + ), + }, + { + name: "market id 1,887,473,824 permission 256", + marketID: 1_887_473_824, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: 256, + expPanic: "permission value 256 out of range for uint8", + }, + { + name: "market id 1,887,473,824 permission 255", + marketID: 1_887_473_824, + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + perm: 255, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 112, 128, 144, 160, marketTypeByte, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{255}, + ), + }, } for _, tc := range tests { @@ -1586,9 +1623,139 @@ func TestMakeKeyMarketPermissions(t *testing.T) { } } -// TODO[1658]: func TestMakeKeyMarketReqAttrAsk(t *testing.T) +func TestMakeKeyMarketReqAttrAsk(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeReqAttr + orderTypeByte := keeper.OrderKeyTypeAsk + + tests := []struct { + name string + marketID uint32 + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte, orderTypeByte}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyMarketReqAttrAsk(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + }, + } + checkKey(t, ktc, "MakeKeyMarketReqAttrAsk(%d)", tc.marketID) + }) + } +} + +func TestMakeKeyMarketReqAttrBid(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeReqAttr + orderTypeByte := keeper.OrderKeyTypeBid -// TODO[1658]: func TestMakeKeyMarketReqAttrBid(t *testing.T) + tests := []struct { + name string + marketID uint32 + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, orderTypeByte}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte, orderTypeByte}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyMarketReqAttrBid(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + }, + } + checkKey(t, ktc, "MakeKeyMarketReqAttrBid(%d)", tc.marketID) + }) + } +} // TODO[1658]: func TestMakeKeyPrefixOrder(t *testing.T) From 4916b14550f02a83e6f367c27f9bd58bfeee040b Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 16:04:06 -0600 Subject: [PATCH 055/309] [1658]: Finish unit tests on the keys funcs. --- x/exchange/keeper/keys.go | 3 + x/exchange/keeper/keys_test.go | 845 ++++++++++++++++++++++++++++++++- 2 files changed, 830 insertions(+), 18 deletions(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 3fc15face6..1090bc8d22 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -361,6 +361,9 @@ func MakeIndexKeyMarketToOrder(marketID uint32, orderID uint64) []byte { // indexPrefixAddressToOrder creates the prefix for the address to order index entries with some extra apace for the rest. func indexPrefixAddressToOrder(addr sdk.AccAddress, extraCap int) []byte { + if len(addr) == 0 { + panic(errors.New("empty address not allowed")) + } return prepKey(KeyTypeAddressToOrderIndex, address.MustLengthPrefix(addr), extraCap) } diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 471ec02030..2767c15ea8 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "bytes" "testing" "github.com/stretchr/testify/assert" @@ -1308,17 +1309,20 @@ func TestMakeKeyPrefixMarketPermissionsForAddress(t *testing.T) { expPanic string }{ { - name: "market id 0 nil addr", - marketID: 0, + name: "nil addr", addr: nil, expPanic: "empty address not allowed", }, { - name: "market id 0 empty addr", - marketID: 0, + name: "empty addr", addr: sdk.AccAddress{}, expPanic: "empty address not allowed", }, + { + name: "256 byte addr", + addr: bytes.Repeat([]byte{'p'}, 256), + expPanic: "address length should be max 255 bytes, got 256: unknown address", + }, { name: "market id 0 5 byte addr", marketID: 0, @@ -1401,17 +1405,20 @@ func TestMakeKeyMarketPermissions(t *testing.T) { expPanic string }{ { - name: "market id 0 nil addr", - marketID: 0, + name: "nil addr", addr: nil, expPanic: "empty address not allowed", }, { - name: "market id 0 empty addr", - marketID: 0, + name: "empty addr", addr: sdk.AccAddress{}, expPanic: "empty address not allowed", }, + { + name: "256 byte addr", + addr: bytes.Repeat([]byte{'p'}, 256), + expPanic: "address length should be max 255 bytes, got 256: unknown address", + }, { name: "market id 0 5 byte addr settle", marketID: 0, @@ -1757,22 +1764,824 @@ func TestMakeKeyMarketReqAttrBid(t *testing.T) { } } -// TODO[1658]: func TestMakeKeyPrefixOrder(t *testing.T) +func TestMakeKeyPrefixOrder(t *testing.T) { + tests := []struct { + name string + orderID uint64 + expected []byte + }{ + { + name: "order id 0", + orderID: 0, + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "order id 1", + orderID: 1, + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 0, 1}, + }, + { + name: "order id 256", + orderID: 256, + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 1, 0}, + }, + { + name: "order id 65,536", + orderID: 65_536, + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 1, 0, 0}, + }, + { + name: "order id 16,777,216", + orderID: 16_777_216, + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 1, 0, 0, 0}, + }, + { + name: "order id 4,294,967,296", + orderID: 4_294_967_296, + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 1, 0, 0, 0, 0}, + }, + { + name: "order id 1,099,511,627,776", + orderID: 1_099_511_627_776, + expected: []byte{keeper.KeyTypeOrder, 0, 0, 1, 0, 0, 0, 0, 0}, + }, + { + name: "order id 281,474,976,710,656", + orderID: 281_474_976_710_656, + expected: []byte{keeper.KeyTypeOrder, 0, 1, 0, 0, 0, 0, 0, 0}, + }, + { + name: "order id 72,057,594,037,927,936", + orderID: 72_057_594_037_927_936, + expected: []byte{keeper.KeyTypeOrder, 1, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "order id 72,340,172,838,076,673", + orderID: 72_340_172_838_076_673, + expected: []byte{keeper.KeyTypeOrder, 1, 1, 1, 1, 1, 1, 1, 1}, + }, + { + name: "order id 1,229,782,938,247,303,441", + orderID: 1_229_782_938_247_303_441, + expected: []byte{keeper.KeyTypeOrder, 17, 17, 17, 17, 17, 17, 17, 17}, + }, + { + name: "order id 18,446,744,073,709,551,615", + orderID: 18_446_744_073_709_551_615, + expected: []byte{keeper.KeyTypeOrder, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyPrefixOrder(tc.orderID) + }, + expected: tc.expected, + } + checkKey(t, ktc, "MakeKeyPrefixOrder(%d)", tc.orderID) + }) + } +} + +func TestMakeKeyOrder(t *testing.T) { + askOrder := func(orderID uint64) exchange.Order { + return *exchange.NewOrder(orderID).WithAsk(&exchange.AskOrder{}) + } + bidOrder := func(orderID uint64) exchange.Order { + return *exchange.NewOrder(orderID).WithBid(&exchange.BidOrder{}) + } + + tests := []struct { + name string + order exchange.Order + expected []byte + expPanic string + }{ + { + name: "order id 0 ask", + order: askOrder(0), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 0, 0, exchange.OrderTypeByteAsk}, + }, + { + name: "order id 0 bid", + order: bidOrder(0), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 0, 0, exchange.OrderTypeByteBid}, + }, + { + name: "order id 1 ask", + order: askOrder(1), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 0, 1, exchange.OrderTypeByteAsk}, + }, + { + name: "order id 1 bid", + order: bidOrder(1), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 0, 1, exchange.OrderTypeByteBid}, + }, + { + name: "order id 256 ask", + order: askOrder(256), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 1, 0, exchange.OrderTypeByteAsk}, + }, + { + name: "order id 256 bid", + order: bidOrder(256), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 1, 0, exchange.OrderTypeByteBid}, + }, + { + name: "order id 65,536 ask", + order: askOrder(65_536), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 1, 0, 0, exchange.OrderTypeByteAsk}, + }, + { + name: "order id 65,536 bid", + order: bidOrder(65_536), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 1, 0, 0, exchange.OrderTypeByteBid}, + }, + { + name: "order id 16,777,216 ask", + order: askOrder(16_777_216), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 1, 0, 0, 0, exchange.OrderTypeByteAsk}, + }, + { + name: "order id 16,777,216 bid", + order: bidOrder(16_777_216), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 1, 0, 0, 0, exchange.OrderTypeByteBid}, + }, + { + name: "order id 4,294,967,296 ask", + order: askOrder(4_294_967_296), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 1, 0, 0, 0, 0, exchange.OrderTypeByteAsk}, + }, + { + name: "order id 4,294,967,296 bid", + order: bidOrder(4_294_967_296), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 1, 0, 0, 0, 0, exchange.OrderTypeByteBid}, + }, + { + name: "order id 1,099,511,627,776 ask", + order: askOrder(1_099_511_627_776), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 1, 0, 0, 0, 0, 0, exchange.OrderTypeByteAsk}, + }, + { + name: "order id 1,099,511,627,776 bid", + order: bidOrder(1_099_511_627_776), + expected: []byte{keeper.KeyTypeOrder, 0, 0, 1, 0, 0, 0, 0, 0, exchange.OrderTypeByteBid}, + }, + { + name: "order id 281,474,976,710,656 ask", + order: askOrder(281_474_976_710_656), + expected: []byte{keeper.KeyTypeOrder, 0, 1, 0, 0, 0, 0, 0, 0, exchange.OrderTypeByteAsk}, + }, + { + name: "order id 281,474,976,710,656 bid", + order: bidOrder(281_474_976_710_656), + expected: []byte{keeper.KeyTypeOrder, 0, 1, 0, 0, 0, 0, 0, 0, exchange.OrderTypeByteBid}, + }, + { + name: "order id 72,057,594,037,927,936 ask", + order: askOrder(72_057_594_037_927_936), + expected: []byte{keeper.KeyTypeOrder, 1, 0, 0, 0, 0, 0, 0, 0, exchange.OrderTypeByteAsk}, + }, + { + name: "order id 72,057,594,037,927,936 bid", + order: bidOrder(72_057_594_037_927_936), + expected: []byte{keeper.KeyTypeOrder, 1, 0, 0, 0, 0, 0, 0, 0, exchange.OrderTypeByteBid}, + }, + { + name: "order id 72,340,172,838,076,673 ask", + order: askOrder(72_340_172_838_076_673), + expected: []byte{keeper.KeyTypeOrder, 1, 1, 1, 1, 1, 1, 1, 1, exchange.OrderTypeByteAsk}, + }, + { + name: "order id 72,340,172,838,076,673 bid", + order: bidOrder(72_340_172_838_076_673), + expected: []byte{keeper.KeyTypeOrder, 1, 1, 1, 1, 1, 1, 1, 1, exchange.OrderTypeByteBid}, + }, + { + name: "order id 1,229,782,938,247,303,441 ask", + order: askOrder(1_229_782_938_247_303_441), + expected: []byte{keeper.KeyTypeOrder, 17, 17, 17, 17, 17, 17, 17, 17, exchange.OrderTypeByteAsk}, + }, + { + name: "order id 1,229,782,938,247,303,441 bid", + order: bidOrder(1_229_782_938_247_303_441), + expected: []byte{keeper.KeyTypeOrder, 17, 17, 17, 17, 17, 17, 17, 17, exchange.OrderTypeByteBid}, + }, + { + name: "order id 18,446,744,073,709,551,615 ask", + order: askOrder(18_446_744_073_709_551_615), + expected: []byte{keeper.KeyTypeOrder, 255, 255, 255, 255, 255, 255, 255, 255, exchange.OrderTypeByteAsk}, + }, + { + name: "order id 18,446,744,073,709,551,615 bid", + order: bidOrder(18_446_744_073_709_551_615), + expected: []byte{keeper.KeyTypeOrder, 255, 255, 255, 255, 255, 255, 255, 255, exchange.OrderTypeByteBid}, + }, + { + name: "nil inside order", + order: exchange.Order{OrderId: 5, Order: nil}, + expPanic: "GetOrderTypeByte() missing case for ", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyOrder(tc.order) + }, + expected: tc.expected, + expPanic: tc.expPanic, + } + if len(tc.expPanic) == 0 { + ktc.expPrefixes = []expectedPrefix{ + {name: "MakeKeyPrefixOrder", value: keeper.MakeKeyPrefixOrder(tc.order.OrderId)}, + } + } + checkKey(t, ktc, "MakeKeyOrder %d %T", tc.order.OrderId, tc.order.Order) + }) + } +} + +func TestMakeIndexPrefixMarketToOrder(t *testing.T) { + tests := []struct { + name string + marketID uint32 + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 1}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 255}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 1, 0}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 1, 0, 0}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 0, 0, 0}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 1, 1, 1}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 255, 255, 255, 255}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeIndexPrefixMarketToOrder(tc.marketID) + }, + expected: tc.expected, + } + checkKey(t, ktc, "MakeIndexPrefixMarketToOrder(%d)", tc.marketID) + }) + } +} + +func TestMakeIndexKeyMarketToOrder(t *testing.T) { + tests := []struct { + name string + marketID uint32 + orderID uint64 + expected []byte + }{ + { + name: "market 0 order 0", + marketID: 0, + orderID: 0, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "market 0 order 1", + marketID: 0, + orderID: 1, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + }, + { + name: "market 0 order 72,340,172,838,076,673", + marketID: 0, + orderID: 72_340_172_838_076_673, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, + }, + { + name: "market 2 order 0", + marketID: 2, + orderID: 0, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "market 2 order 1", + marketID: 2, + orderID: 1, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1}, + }, + { + name: "market 2 order 72,340,172,838,076,673", + marketID: 2, + orderID: 72_340_172_838_076_673, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1}, + }, + { + name: "market 33,686,018 order 0", + marketID: 33_686_018, + orderID: 0, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "market 33,686,018 order 1", + marketID: 33_686_018, + orderID: 1, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1}, + }, + { + name: "market 33,686,018 order 72,340,172,838,076,673", + marketID: 33_686_018, + orderID: 72_340_172_838_076_673, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1}, + }, + + { + name: "market max order max", + marketID: 4_294_967_295, + orderID: 18_446_744_073_709_551_615, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeIndexKeyMarketToOrder(tc.marketID, tc.orderID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "", value: keeper.MakeIndexPrefixMarketToOrder(tc.marketID)}, + }, + } + checkKey(t, ktc, "MakeIndexKeyMarketToOrder(%d, %d)", tc.marketID, tc.orderID) + }) + } +} + +func TestMakeIndexPrefixAddressToOrder(t *testing.T) { + tests := []struct { + name string + addr sdk.AccAddress + expected []byte + expPanic string + }{ + { + name: "nil addr", + addr: nil, + expPanic: "empty address not allowed", + }, + { + name: "empty addr", + addr: sdk.AccAddress{}, + expPanic: "empty address not allowed", + }, + { + name: "256 byte addr", + addr: sdk.AccAddress(bytes.Repeat([]byte{'P'}, 256)), + expPanic: "address length should be max 255 bytes, got 256: unknown address", + }, + { + name: "5 byte addr", + addr: sdk.AccAddress("abcde"), + expected: append([]byte{keeper.KeyTypeAddressToOrderIndex, 5}, "abcde"...), + }, + { + name: "20 byte addr", + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + expected: append([]byte{keeper.KeyTypeAddressToOrderIndex, 20}, "abcdefghijklmnopqrst"...), + }, + { + name: "32 byte addr", + addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), + expected: append([]byte{keeper.KeyTypeAddressToOrderIndex, 32}, "abcdefghijklmnopqrstuvwxyzABCDEF"...), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeIndexPrefixAddressToOrder(tc.addr) + }, + expected: tc.expected, + expPanic: tc.expPanic, + } + checkKey(t, ktc, "MakeIndexPrefixAddressToOrder(%s)", string(tc.addr)) + }) + } +} + +func TestMakeIndexKeyAddressToOrder(t *testing.T) { + tests := []struct { + name string + addr sdk.AccAddress + orderID uint64 + expected []byte + expPanic string + }{ + { + name: "nil addr", + addr: nil, + expPanic: "empty address not allowed", + }, + { + name: "empty addr", + addr: sdk.AccAddress{}, + expPanic: "empty address not allowed", + }, + { + name: "256 byte addr", + addr: sdk.AccAddress(bytes.Repeat([]byte{'P'}, 256)), + expPanic: "address length should be max 255 bytes, got 256: unknown address", + }, + { + name: "5 byte addr order 1", + addr: sdk.AccAddress("abcde"), + orderID: 1, + expected: concatBz( + []byte{keeper.KeyTypeAddressToOrderIndex, 5}, + []byte("abcde"), + []byte{0, 0, 0, 0, 0, 0, 0, 1}, + ), + }, + { + name: "20 byte addr order 1", + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + orderID: 1, + expected: concatBz( + []byte{keeper.KeyTypeAddressToOrderIndex, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{0, 0, 0, 0, 0, 0, 0, 1}, + ), + }, + { + name: "32 byte addr order 1", + addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), + orderID: 1, + expected: concatBz( + []byte{keeper.KeyTypeAddressToOrderIndex, 32}, + []byte("abcdefghijklmnopqrstuvwxyzABCDEF"), + []byte{0, 0, 0, 0, 0, 0, 0, 1}, + ), + }, + { + name: "20 byte addr order 5", + addr: sdk.AccAddress("ABCDEFGHIJKLMNOPQRST"), + orderID: 5, + expected: concatBz( + []byte{keeper.KeyTypeAddressToOrderIndex, 20}, + []byte("ABCDEFGHIJKLMNOPQRST"), + []byte{0, 0, 0, 0, 0, 0, 0, 5}, + ), + }, + { + name: "20 byte addr order 72,623,859,790,382,856", + addr: sdk.AccAddress("ABCDEFGHIJKLMNOPQRST"), + orderID: 72_623_859_790_382_856, + expected: concatBz( + []byte{keeper.KeyTypeAddressToOrderIndex, 20}, + []byte("ABCDEFGHIJKLMNOPQRST"), + []byte{1, 2, 3, 4, 5, 6, 7, 8}, + ), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeIndexKeyAddressToOrder(tc.addr, tc.orderID) + }, + expected: tc.expected, + expPanic: tc.expPanic, + } + if len(tc.expPanic) == 0 { + ktc.expPrefixes = []expectedPrefix{ + {name: "", value: keeper.MakeIndexPrefixAddressToOrder(tc.addr)}, + } + } + checkKey(t, ktc, "MakeIndexKeyAddressToOrder(%s, %d)", string(tc.addr), tc.orderID) + }) + } +} + +func TestMakeIndexPrefixAssetToOrder(t *testing.T) { + tests := []struct { + name string + assetDenom string + expected []byte + }{ + { + name: "empty", + assetDenom: "", + expected: []byte{keeper.KeyTypeAssetToOrderIndex}, + }, + { + name: "1 char denom", + assetDenom: "p", + expected: []byte{keeper.KeyTypeAssetToOrderIndex, 'p'}, + }, + { + name: "nhash", + assetDenom: "nhash", + expected: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h'}, + }, + { + name: "hex string", + assetDenom: hexString, + expected: append([]byte{keeper.KeyTypeAssetToOrderIndex}, hexString...), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeIndexPrefixAssetToOrder(tc.assetDenom) + }, + expected: tc.expected, + } + checkKey(t, ktc, "MakeIndexPrefixAssetToOrder(%q)", tc.assetDenom) + }) + } +} + +func TestMakeIndexPrefixAssetToOrderAsks(t *testing.T) { + orderKeyType := keeper.OrderKeyTypeAsk + tests := []struct { + name string + assetDenom string + expected []byte + }{ + { + name: "empty", + assetDenom: "", + expected: []byte{keeper.KeyTypeAssetToOrderIndex, orderKeyType}, + }, + { + name: "1 char denom", + assetDenom: "p", + expected: []byte{keeper.KeyTypeAssetToOrderIndex, 'p', orderKeyType}, + }, + { + name: "nhash", + assetDenom: "nhash", + expected: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', orderKeyType}, + }, + { + name: "hex string", + assetDenom: hexString, + expected: concatBz( + []byte{keeper.KeyTypeAssetToOrderIndex}, + []byte(hexString), + []byte{orderKeyType}, + ), + }, + } -// TODO[1658]: func TestMakeKeyOrder(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeIndexPrefixAssetToOrderAsks(tc.assetDenom) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeIndexPrefixAssetToOrder", value: keeper.MakeIndexPrefixAssetToOrder(tc.assetDenom)}, + }, + } -// TODO[1658]: func TestMakeIndexPrefixMarketToOrder(t *testing.T) + checkKey(t, ktc, "MakeIndexPrefixAssetToOrderAsks(%q)", tc.assetDenom) + }) + } +} -// TODO[1658]: func TestMakeIndexKeyMarketToOrder(t *testing.T) +func TestMakeIndexPrefixAssetToOrderBids(t *testing.T) { + orderKeyType := keeper.OrderKeyTypeBid + tests := []struct { + name string + assetDenom string + expected []byte + }{ + { + name: "empty", + assetDenom: "", + expected: []byte{keeper.KeyTypeAssetToOrderIndex, orderKeyType}, + }, + { + name: "1 char denom", + assetDenom: "p", + expected: []byte{keeper.KeyTypeAssetToOrderIndex, 'p', orderKeyType}, + }, + { + name: "nhash", + assetDenom: "nhash", + expected: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', orderKeyType}, + }, + { + name: "hex string", + assetDenom: hexString, + expected: concatBz( + []byte{keeper.KeyTypeAssetToOrderIndex}, + []byte(hexString), + []byte{orderKeyType}, + ), + }, + } -// TODO[1658]: func TestMakeIndexPrefixAddressToOrder(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeIndexPrefixAssetToOrderBids(tc.assetDenom) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeIndexPrefixAssetToOrder", value: keeper.MakeIndexPrefixAssetToOrder(tc.assetDenom)}, + }, + } -// TODO[1658]: func TestMakeIndexKeyAddressToOrder(t *testing.T) + checkKey(t, ktc, "MakeIndexPrefixAssetToOrderBids(%q)", tc.assetDenom) + }) + } +} -// TODO[1658]: func TestMakeIndexPrefixAssetToOrder(t *testing.T) +func TestMakeIndexKeyAssetToOrder(t *testing.T) { + askOrder := func(orderID uint64) exchange.Order { + return *exchange.NewOrder(orderID).WithAsk(&exchange.AskOrder{}) + } + bidOrder := func(orderID uint64) exchange.Order { + return *exchange.NewOrder(orderID).WithBid(&exchange.BidOrder{}) + } -// TODO[1658]: func TestMakeIndexPrefixAssetToOrderAsks(t *testing.T) + tests := []struct { + name string + assetDenom string + order exchange.Order + expected []byte + expPanic string + }{ + { + name: "no asset order 0 ask", + assetDenom: "", + order: askOrder(0), + expected: []byte{ + keeper.KeyTypeAssetToOrderIndex, + keeper.OrderKeyTypeAsk, 0, 0, 0, 0, 0, 0, 0, 0, + }, + }, + { + name: "no asset order 0 bid", + assetDenom: "", + order: bidOrder(0), + expected: []byte{ + keeper.KeyTypeAssetToOrderIndex, + keeper.OrderKeyTypeBid, 0, 0, 0, 0, 0, 0, 0, 0, + }, + }, + { + name: "nhash order 1 ask", + assetDenom: "nhash", + order: askOrder(1), + expected: []byte{ + keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', + keeper.OrderKeyTypeAsk, 0, 0, 0, 0, 0, 0, 0, 1, + }, + }, + { + name: "nhash order 1 bid", + assetDenom: "nhash", + order: bidOrder(1), + expected: []byte{ + keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', + keeper.OrderKeyTypeBid, 0, 0, 0, 0, 0, 0, 0, 1, + }, + }, + { + name: "hex string order 5 ask", + assetDenom: hexString, + order: askOrder(5), + expected: concatBz( + []byte{keeper.KeyTypeAssetToOrderIndex}, []byte(hexString), + []byte{keeper.OrderKeyTypeAsk, 0, 0, 0, 0, 0, 0, 0, 5}, + ), + }, + { + name: "hex string order 5 bid", + assetDenom: hexString, + order: bidOrder(5), + expected: concatBz( + []byte{keeper.KeyTypeAssetToOrderIndex}, []byte(hexString), + []byte{keeper.OrderKeyTypeBid, 0, 0, 0, 0, 0, 0, 0, 5}, + ), + }, + { + name: "nhash order 4,294,967,296 ask", + assetDenom: "nhash", + order: askOrder(4_294_967_296), + expected: []byte{ + keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', + keeper.OrderKeyTypeAsk, 0, 0, 0, 1, 0, 0, 0, 0, + }, + }, + { + name: "nhash order 4,294,967,296 bid", + assetDenom: "nhash", + order: bidOrder(4_294_967_296), + expected: []byte{ + keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', + keeper.OrderKeyTypeBid, 0, 0, 0, 1, 0, 0, 0, 0, + }, + }, + { + name: "nhash order max ask", + assetDenom: "nhash", + order: askOrder(18_446_744_073_709_551_615), + expected: []byte{ + keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', + keeper.OrderKeyTypeAsk, 255, 255, 255, 255, 255, 255, 255, 255, + }, + }, + { + name: "nhash order max bid", + assetDenom: "nhash", + order: bidOrder(18_446_744_073_709_551_615), + expected: []byte{ + keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', + keeper.OrderKeyTypeBid, 255, 255, 255, 255, 255, 255, 255, 255, + }, + }, + { + name: "nil inside order", + order: exchange.Order{OrderId: 3, Order: nil}, + expPanic: "GetOrderTypeByte() missing case for ", + }, + } -// TODO[1658]: func TestMakeIndexPrefixAssetToOrderBids(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeIndexKeyAssetToOrder(tc.assetDenom, tc.order) + }, + expected: tc.expected, + expPanic: tc.expPanic, + } + if len(tc.expPanic) == 0 { + ktc.expPrefixes = []expectedPrefix{ + {name: "MakeIndexPrefixAssetToOrder", value: keeper.MakeIndexPrefixAssetToOrder(tc.assetDenom)}, + } + switch v := tc.order.Order.(type) { + case *exchange.Order_AskOrder: + ktc.expPrefixes = append(ktc.expPrefixes, expectedPrefix{ + name: "MakeIndexPrefixAssetToOrderAsks", + value: keeper.MakeIndexPrefixAssetToOrderAsks(tc.assetDenom), + }) + case *exchange.Order_BidOrder: + ktc.expPrefixes = append(ktc.expPrefixes, expectedPrefix{ + name: "MakeIndexPrefixAssetToOrderBids", + value: keeper.MakeIndexPrefixAssetToOrderBids(tc.assetDenom), + }) + default: + assert.Fail(t, "no expected prefix case defined for %T", v) + } + } -// TODO[1658]: func TestMakeIndexKeyAssetToOrder(t *testing.T) + checkKey(t, ktc, "MakeIndexKeyAssetToOrder(%q, %d)", tc.assetDenom, tc.order) + }) + } +} From 93b1c4e45b790f7e48582453ecbbba05a6faf733 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 17:10:01 -0600 Subject: [PATCH 056/309] [1658]: Add a prefixer for the params splits keys. --- x/exchange/keeper/keys.go | 26 +++++++++++++++++++++++++- x/exchange/keeper/keys_test.go | 13 +++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 1090bc8d22..f8ac418500 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -115,6 +115,11 @@ func uint16Bz(val uint16) []byte { return rv } +// uint16FromBz converts the provided bytes into a uint16. +func uint16FromBz(bz []byte) uint16 { + return binary.BigEndian.Uint16(bz) +} + // uint32Bz converts the provided uint32 value to a big-endian byte slice of length 4. func uint32Bz(val uint32) []byte { rv := make([]byte, 4) @@ -122,6 +127,11 @@ func uint32Bz(val uint32) []byte { return rv } +// uint32FromBz converts the provided bytes into a uint32. +func uint32FromBz(bz []byte) uint32 { + return binary.BigEndian.Uint32(bz) +} + // uint64Bz converts the provided uint64 value to a big-endian byte slice of length 8. func uint64Bz(val uint64) []byte { rv := make([]byte, 8) @@ -129,10 +139,24 @@ func uint64Bz(val uint64) []byte { return rv } +// uint64FromBz converts the provided bytes into a uint64. +func uint64FromBz(bz []byte) uint64 { + return binary.BigEndian.Uint64(bz) +} + +func keyPrefixParamsSplit(extraCap int) []byte { + return prepKey(KeyTypeParams, []byte("split"), extraCap) +} + +// MakeKeyPrefixParamsSplit creates the key prefix for all params splits entries. +func MakeKeyPrefixParamsSplit() []byte { + return keyPrefixParamsSplit(0) +} + // MakeKeyParamsSplit creates the key to use for the params defining the splits. // A denom of "" is used for the default split value. func MakeKeyParamsSplit(denom string) []byte { - rv := prepKey(KeyTypeParams, []byte("split"), len(denom)) + rv := keyPrefixParamsSplit(len(denom)) rv = append(rv, denom...) return rv } diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 2767c15ea8..9d85292c43 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -121,6 +121,16 @@ func TestKeyTypeUniqueness(t *testing.T) { } } +func TestMakeKeyPrefixParamsSplit(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyPrefixParamsSplit() + }, + expected: []byte{keeper.KeyTypeParams, 's', 'p', 'l', 'i', 't'}, + } + checkKey(t, ktc, "MakeKeyPrefixParamsSplit") +} + func TestMakeKeyParamsSplit(t *testing.T) { tests := []struct { name string @@ -151,6 +161,9 @@ func TestMakeKeyParamsSplit(t *testing.T) { return keeper.MakeKeyParamsSplit(tc.denom) }, expected: tc.expected, + expPrefixes: []expectedPrefix{ + {name: "MakeKeyPrefixParamsSplit", value: keeper.MakeKeyPrefixParamsSplit()}, + }, } checkKey(t, ktc, "MakeKeyParamsSplit(%q)", tc.denom) }) From 213fde24673fd9173e268ce0c31c6fdc893850a4 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 17:10:43 -0600 Subject: [PATCH 057/309] [1658]: create keeper funcs for getting and setting the params. --- x/exchange/keeper/params.go | 108 ++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 x/exchange/keeper/params.go diff --git a/x/exchange/keeper/params.go b/x/exchange/keeper/params.go new file mode 100644 index 0000000000..edbdbe9012 --- /dev/null +++ b/x/exchange/keeper/params.go @@ -0,0 +1,108 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/provenance-io/provenance/x/exchange" +) + +// iterateParamsSplits iterates over all the params splits in the store (including the default entry). +// cb should return whether to stop early (true = stop now, false = keep going). +func iterateParamsSplits(store sdk.KVStore, cb func(denom string, split uint16) bool) { + // Using an open iterator on a prefixed store here so that iter.Key() doesn't contain the prefix. + pStore := prefix.NewStore(store, MakeKeyPrefixParamsSplit()) + iter := pStore.Iterator(nil, nil) + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + denom := string(iter.Key()) + split := uint16FromBz(iter.Value()) + if cb(denom, split) { + break + } + } +} + +// deleteAllParamsSplits deletes all the params splits in the store. +func deleteAllParamsSplits(store sdk.KVStore) { + var keys [][]byte + + iter := sdk.KVStorePrefixIterator(store, MakeKeyPrefixParamsSplit()) + defer func() { + if iter != nil { + iter.Close() + } + }() + + for ; iter.Valid(); iter.Next() { + keys = append(keys, iter.Key()) + } + iter.Close() + iter = nil + + for _, key := range keys { + store.Delete(key) + } +} + +// setParamsSplit sets the provided params split for the provided denom. +func setParamsSplit(store sdk.KVStore, denom string, split uint16) { + key := MakeKeyParamsSplit(denom) + value := uint16Bz(split) + store.Set(key, value) +} + +// getParamsSplit gets the params split amount for the provided denom, and whether the entry existed. +func getParamsSplit(store sdk.KVStore, denom string) (uint16, bool) { + key := MakeKeyParamsSplit(denom) + if store.Has(key) { + value := store.Get(key) + return uint16FromBz(value), true + } + return 0, false +} + +// SetParams updates the params to match those provided. +// If nil is provided, all params are deleted. +func (k Keeper) SetParams(ctx sdk.Context, params *exchange.Params) { + store := ctx.KVStore(k.storeKey) + deleteAllParamsSplits(store) + if params != nil { + setParamsSplit(store, "", uint16(params.DefaultSplit)) + for _, split := range params.DenomSplits { + setParamsSplit(store, split.Denom, uint16(split.Split)) + } + } +} + +// GetParams gets the exchange module params. +// If there aren't any params in state, nil is returned. +func (k Keeper) GetParams(ctx sdk.Context) *exchange.Params { + var rv *exchange.Params + iterateParamsSplits(ctx.KVStore(k.storeKey), func(denom string, split uint16) bool { + if rv == nil { + rv = &exchange.Params{} + } + if len(denom) == 0 { + rv.DefaultSplit = uint32(split) + } else { + rv.DenomSplits = append(rv.DenomSplits, exchange.DenomSplit{Denom: denom, Split: uint32(split)}) + } + return false + }) + return rv +} + +// GetExchangeSplit gets the split amount for the provided denom. +// If the denom is "", the default is returned. +// If there isn't a specific entry for the provided denom, the default is returned. +func (k Keeper) GetExchangeSplit(ctx sdk.Context, denom string) uint16 { + store := ctx.KVStore(k.storeKey) + split, found := getParamsSplit(store, denom) + // If it wasn't found, and we weren't already looking for the default, look up the default now. + if !found && len(denom) > 0 { + split, _ = getParamsSplit(store, "") + } + return split +} From d7c64adbdcbf929307a9993603bed2dcf9d7484e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 17:32:36 -0600 Subject: [PATCH 058/309] [1658]: Switch to the new assertions funcs in the places where I left a TODO about that. --- x/exchange/genesis_test.go | 13 +---- x/exchange/keeper/keys_test.go | 16 ++--- x/exchange/market_test.go | 104 +++++---------------------------- x/exchange/msg_test.go | 23 ++------ x/exchange/orders_test.go | 62 ++++---------------- x/exchange/params_test.go | 20 ++----- 6 files changed, 49 insertions(+), 189 deletions(-) diff --git a/x/exchange/genesis_test.go b/x/exchange/genesis_test.go index a21aecb362..9971250787 100644 --- a/x/exchange/genesis_test.go +++ b/x/exchange/genesis_test.go @@ -8,6 +8,8 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/provenance-io/provenance/testutil/assertions" ) func TestNewGenesisState(t *testing.T) { @@ -419,16 +421,7 @@ func TestGenesisState_Validate(t *testing.T) { } require.NotPanics(t, testFunc, "GenesisState.Validate") - // TODO[1658]: Replace this with testutils.AssertErrorContents(t, err, tc.expErr, "GenesisState.Validate") - if len(tc.expErr) > 0 { - if assert.Error(t, err, "GenesisState.Validate") { - for _, exp := range tc.expErr { - assert.ErrorContains(t, err, exp, "GenesisState.Validate") - } - } - } else { - assert.NoError(t, err, "GenesisState.Validate") - } + assertions.AssertErrorContents(t, err, tc.expErr, "GenesisState.Validate") }) } } diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 9d85292c43..22e81c2d58 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -5,10 +5,11 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/provenance-io/provenance/testutil/assertions" "github.com/provenance-io/provenance/x/exchange" "github.com/provenance-io/provenance/x/exchange/keeper" ) @@ -41,17 +42,18 @@ type keyTestCase struct { // Also asserts that the result has the provided prefixes. func checkKey(t *testing.T, tc keyTestCase, msg string, args ...interface{}) { t.Helper() + var expPanic []string + if len(tc.expPanic) > 0 { + expPanic = []string{tc.expPanic} + } + var actual []byte testFunc := func() { actual = tc.maker() } - // TODO[1658]: Replace this with a testutils panic checker. - if len(tc.expPanic) > 0 { - require.PanicsWithErrorf(t, tc.expPanic, testFunc, msg, args...) - } else { - require.NotPanicsf(t, testFunc, msg, args...) - } + assertions.RequirePanicContentsf(t, testFunc, expPanic, msg, args...) assert.Equalf(t, tc.expected, actual, msg+" result", args...) + if len(actual) > 0 { assert.Equalf(t, len(actual), cap(actual), msg+" result length (expected) vs capacity (actual)", args...) for _, expPre := range tc.expPrefixes { diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index eb7f1a002b..848b07d9c5 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -12,6 +12,8 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/provenance-io/provenance/testutil/assertions" ) func TestMarket_Validate(t *testing.T) { @@ -171,16 +173,7 @@ func TestMarket_Validate(t *testing.T) { } require.NotPanics(t, testFunc, "Market.Validate") - // TODO[1658]: Refactor to testutils.AssertErrorContents(t, err, tc.expErr, "Market.Validate") - if len(tc.expErr) > 0 { - if assert.Error(t, err, "Market.Validate") { - for _, exp := range tc.expErr { - assert.ErrorContains(t, err, exp, "Market.Validate") - } - } - } else { - assert.NoError(t, err, "Market.Validate") - } + assertions.AssertErrorContents(t, err, tc.expErr, "Market.Validate") }) } } @@ -292,12 +285,7 @@ func TestValidateFeeOptions(t *testing.T) { } require.NotPanics(t, testFunc, "ValidateFeeOptions") - // TODO[1658]: Refactor this to testutils.AssertErrorValue(t, err, tc.expErr, "ValidateFeeOptions") - if len(tc.expErr) > 0 { - assert.EqualError(t, err, tc.expErr, "ValidateFeeOptions") - } else { - assert.NoError(t, err, "ValidateFeeOptions") - } + assertions.AssertErrorValue(t, err, tc.expErr, "ValidateFeeOptions") }) } } @@ -397,12 +385,7 @@ func TestMarketDetails_Validate(t *testing.T) { } require.NotPanics(t, testFunc, "Validate") - // TODO[1658]: Refactor to testutils.AssertErrorValue(t, err, tc.expErr, "Validate") - if len(tc.expErr) > 0 { - assert.EqualError(t, err, tc.expErr, "Validate") - } else { - assert.NoError(t, err, "Validate") - } + assertions.AssertErrorValue(t, err, tc.expErr, "Validate") }) } } @@ -558,12 +541,7 @@ func TestValidateFeeRatios(t *testing.T) { } require.NotPanics(t, testFunc, "ValidateFeeRatios") - // TODO[1658]: Refactor to testutils.AssertErrorValue(t, err, tc.exp, "ValidateFeeRatios") - if len(tc.exp) > 0 { - assert.EqualError(t, err, tc.exp, "ValidateFeeRatios") - } else { - assert.NoError(t, err, "ValidateFeeRatios") - } + assertions.AssertErrorValue(t, err, tc.exp, "ValidateFeeRatios") }) } } @@ -646,12 +624,7 @@ func TestValidateSellerFeeRatios(t *testing.T) { } require.NotPanics(t, testFunc, "ValidateSellerFeeRatios") - // TODO[1658]: Refactor to testutils.AssertErrorValue(t, err, tc.exp, "ValidateSellerFeeRatios") - if len(tc.exp) > 0 { - assert.EqualError(t, err, tc.exp, "ValidateSellerFeeRatios") - } else { - assert.NoError(t, err, "ValidateSellerFeeRatios") - } + assertions.AssertErrorValue(t, err, tc.exp, "ValidateSellerFeeRatios") }) } } @@ -750,12 +723,7 @@ func TestValidateBuyerFeeRatios(t *testing.T) { } require.NotPanics(t, testFunc, "ValidateBuyerFeeRatios") - // TODO[1658]: Refactor to testutils.AssertErrorValue(t, err, tc.exp, "ValidateBuyerFeeRatios") - if len(tc.exp) > 0 { - assert.EqualError(t, err, tc.exp, "ValidateBuyerFeeRatios") - } else { - assert.NoError(t, err, "ValidateBuyerFeeRatios") - } + assertions.AssertErrorValue(t, err, tc.exp, "ValidateBuyerFeeRatios") }) } } @@ -913,12 +881,7 @@ func TestFeeRatio_Validate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { err := tc.r.Validate() - // TODO[1658]: Refactor to use testutils.AssertErrorValue(t, err, tc.exp, "Validate") - if len(tc.exp) > 0 { - assert.EqualError(t, err, tc.exp, "Validate") - } else { - assert.NoError(t, err, "Validate") - } + assertions.AssertErrorValue(t, err, tc.exp, "Validate") }) } } @@ -1267,12 +1230,7 @@ func TestValidateDisjointFeeRatios(t *testing.T) { } require.NotPanics(t, testFunc, "ValidateDisjointFeeRatios") - // TODO[1658]: Replace with testutils.AsserErrorValue(t, err, tc.expErr, "ValidateDisjointFeeRatios") - if len(tc.expErr) > 0 { - assert.EqualError(t, err, tc.expErr, "ValidateDisjointFeeRatios") - } else { - assert.NoError(t, err, "ValidateDisjointFeeRatios") - } + assertions.AssertErrorValue(t, err, tc.expErr, "ValidateDisjointFeeRatios") }) } } @@ -1488,12 +1446,7 @@ func TestValidateAddRemoveFeeOptions(t *testing.T) { } require.NotPanics(t, testFunc, "ValidateAddRemoveFeeOptions") - // TODO[1658]: Refactor to testutils.AssertErrorValue(t, err, tc.expErr, "ValidateAddRemoveFeeOptions") - if len(tc.expErr) > 0 { - assert.EqualError(t, err, tc.expErr, "ValidateAddRemoveFeeOptions") - } else { - assert.NoError(t, err, "ValidateAddRemoveFeeOptions") - } + assertions.AssertErrorValue(t, err, tc.expErr, "ValidateAddRemoveFeeOptions") }) } } @@ -1617,12 +1570,7 @@ func TestValidateAccessGrants(t *testing.T) { t.Run(tc.name, func(t *testing.T) { err := ValidateAccessGrants(tc.grants) - // TODO[1658]: Refactor to use testutils.AssertErrorValue(t, err, tc.exp, "ValidateAccessGrants") - if len(tc.exp) > 0 { - assert.EqualError(t, err, tc.exp, "ValidateAccessGrants") - } else { - assert.NoError(t, err, "ValidateAccessGrants") - } + assertions.AssertErrorValue(t, err, tc.exp, "ValidateAccessGrants") }) } } @@ -1684,12 +1632,7 @@ func TestAccessGrant_Validate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { err := tc.a.Validate() - // TODO[1658]: Refactor this to testutils.AssertErrorValue(t, err, tc.exp, "Validate") - if len(tc.exp) > 0 { - assert.EqualError(t, err, tc.exp, "Validate") - } else { - assert.NoError(t, err, "Validate") - } + assertions.AssertErrorValue(t, err, tc.exp, "Validate") }) } } @@ -1812,12 +1755,7 @@ func TestPermission_Validate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { err := tc.p.Validate() - // TODO: Refactor this to testutils.AssertErrorValue(t, err, tc.exp, "Validate()") - if len(tc.exp) > 0 { - assert.EqualError(t, err, tc.exp, "Validate()") - } else { - assert.NoError(t, err, "Validate()") - } + assertions.AssertErrorValue(t, err, tc.exp, "Validate()") }) } @@ -2056,12 +1994,7 @@ func TestParsePermissions(t *testing.T) { t.Run(tc.name, func(t *testing.T) { perms, err := ParsePermissions(tc.permissions...) - // TODO: Refactor to use testutils.AssertErrorValue(t, err, tc.expErr, "ParsePermissions(%q) error", tc.permissions) - if len(tc.expErr) > 0 { - assert.EqualError(t, err, tc.expErr, "ParsePermissions(%q) error", tc.permissions) - } else { - assert.NoError(t, err, "ParsePermissions(%q) error", tc.permissions) - } + assertions.AssertErrorValue(t, err, tc.expErr, "ParsePermissions(%q) error", tc.permissions) assert.Equal(t, tc.expected, perms, "ParsePermissions(%q) result", tc.permissions) }) } @@ -2222,12 +2155,7 @@ func TestValidateReqAttrs(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { err := ValidateReqAttrs(tc.attrLists...) - // TODO[1658]: Replace this with testutils.AssertErrorValue(t, err, tc.exp, "ValidateReqAttrs") - if len(tc.exp) > 0 { - assert.EqualError(t, err, tc.exp, "ValidateReqAttrs") - } else { - assert.NoError(t, err, "ValidateReqAttrs") - } + assertions.AssertErrorValue(t, err, tc.exp, "ValidateReqAttrs") }) } } diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 673d9d3136..af3e4c559b 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -10,6 +10,8 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/provenance-io/provenance/testutil/assertions" ) func TestAllMsgsGetSigners(t *testing.T) { @@ -38,6 +40,7 @@ func TestAllMsgsGetSigners(t *testing.T) { func(signer string) sdk.Msg { return &MsgGovUpdateParamsRequest{Authority: signer} }, + // TODO[1658]: Add the rest of the messages once they've actually been defined. } signerCases := []struct { @@ -89,16 +92,11 @@ func TestAllMsgsGetSigners(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { var signers []sdk.AccAddress - testGetSigners := func() { + testFunc := func() { signers = tc.msg.GetSigners() } - // TODO: Refactor to one of the new testutils panic assertions. - if len(tc.expPanic) > 0 { - require.PanicsWithError(t, tc.expPanic, testGetSigners, "GetSigners") - } else { - require.NotPanics(t, testGetSigners, "GetSigners") - } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetSigners") assert.Equal(t, tc.expSigners, signers, "GetSigners") }) } @@ -121,16 +119,7 @@ func testValidateBasic(t *testing.T, msg sdk.Msg, expErr []string) { } require.NotPanics(t, testFunc, "%T.ValidateBasic()", msg) - // TODO[1658]: Refactor to testutils.AssertErrorContents(t, err, tc.expErr, "%T.ValidateBasic() error", msg) - if len(expErr) > 0 { - if assert.Error(t, err, "%T.ValidateBasic() error", msg) { - for _, exp := range expErr { - assert.ErrorContains(t, err, exp, "%T.ValidateBasic() error", msg) - } - } - } else { - assert.NoError(t, err, "%T.ValidateBasic() error", msg) - } + assertions.AssertErrorContents(t, err, expErr, "%T.ValidateBasic() error", msg) } // TODO[1658]: func TestMsgCreateAskRequest_ValidateBasic(t *testing.T) diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 4535b05f3e..226210f96c 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -10,6 +10,8 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/provenance-io/provenance/testutil/assertions" ) // copyCoins creates a copy of the provided coins slice with copies of each entry. @@ -199,7 +201,7 @@ func TestOrder_OrderType(t *testing.T) { name string order *Order expected string - expPanic interface{} + expPanic string }{ { name: "AskOrder", @@ -230,12 +232,7 @@ func TestOrder_OrderType(t *testing.T) { actual = tc.order.GetOrderType() } - // TODO[1658]: Refactor to use testutils.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOrderType") - if tc.expPanic != nil { - require.PanicsWithValue(t, tc.expPanic, testFunc, "GetOrderType") - } else { - require.NotPanics(t, testFunc, "GetOrderType") - } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOrderType") assert.Equal(t, tc.expected, actual, "GetOrderType result") }) } @@ -246,7 +243,7 @@ func TestOrder_OrderTypeByte(t *testing.T) { name string order *Order expected byte - expPanic interface{} + expPanic string }{ { name: "AskOrder", @@ -277,12 +274,7 @@ func TestOrder_OrderTypeByte(t *testing.T) { actual = tc.order.GetOrderTypeByte() } - // TODO[1658]: Refactor to use testutils.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOrderTypeByte") - if tc.expPanic != nil { - require.PanicsWithValue(t, tc.expPanic, testFunc, "GetOrderTypeByte") - } else { - require.NotPanics(t, testFunc, "GetOrderTypeByte") - } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOrderTypeByte") assert.Equal(t, tc.expected, actual, "GetOrderTypeByte result") }) } @@ -293,7 +285,7 @@ func TestOrder_GetMarketID(t *testing.T) { name string order *Order expected uint32 - expPanic interface{} + expPanic string }{ { name: "AskOrder", @@ -324,12 +316,7 @@ func TestOrder_GetMarketID(t *testing.T) { actual = tc.order.GetMarketID() } - // TODO[1658]: Refactor to use testutils.RequirePanicEquals(t, testFunc, tc.expPanic, "GetMarketID") - if tc.expPanic != nil { - require.PanicsWithValue(t, tc.expPanic, testFunc, "GetMarketID") - } else { - require.NotPanics(t, testFunc, "GetMarketID") - } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetMarketID") assert.Equal(t, tc.expected, actual, "GetMarketID result") }) } @@ -370,16 +357,7 @@ func TestOrder_Validate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { err := tc.Order.Validate() - // TODO[1658]: Replace this with testutil.AssertErrorContents(t, err, tc.exp, "Validate() error") - if len(tc.exp) > 0 { - if assert.Error(t, err, "Validate() error") { - for _, exp := range tc.exp { - assert.ErrorContains(t, err, exp, "Validate() error\nExpecting: %q", exp) - } - } - } else { - assert.NoError(t, err, "Validate() error") - } + assertions.AssertErrorContents(t, err, tc.exp, "Validate() error") }) } } @@ -618,16 +596,7 @@ func TestAskOrder_Validate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { err := tc.order.Validate() - // TODO[1658]: Replace this with testutil.AssertErrorContents(t, err, tc.exp, "Validate() error") - if len(tc.exp) > 0 { - if assert.Error(t, err, "Validate() error") { - for _, exp := range tc.exp { - assert.ErrorContains(t, err, exp, "Validate() error\nExpecting: %q", exp) - } - } - } else { - assert.NoError(t, err, "Validate() error") - } + assertions.AssertErrorContents(t, err, tc.exp, "Validate() error") }) } } @@ -867,16 +836,7 @@ func TestBidOrder_Validate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { err := tc.order.Validate() - // TODO[1658]: Replace this with testutil.AssertErrorContents(t, err, tc.exp, "Validate() error") - if len(tc.exp) > 0 { - if assert.Error(t, err, "Validate() error") { - for _, exp := range tc.exp { - assert.ErrorContains(t, err, exp, "Validate() error\nExpecting: %q", exp) - } - } - } else { - assert.NoError(t, err, "Validate() error") - } + assertions.AssertErrorContents(t, err, tc.exp, "Validate() error") }) } } diff --git a/x/exchange/params_test.go b/x/exchange/params_test.go index 0fb90cca21..ed16913bb3 100644 --- a/x/exchange/params_test.go +++ b/x/exchange/params_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "github.com/provenance-io/provenance/testutil/assertions" ) func TestMaxSplit(t *testing.T) { @@ -119,16 +121,7 @@ func TestParams_Validate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { err := tc.params.Validate() - // TODO[1658]: Replace this with testutils.AssertErrorContents(t, err, tc.expErr, "Validate") - if len(tc.expErr) > 0 { - if assert.Error(t, err, "Validate") { - for _, exp := range tc.expErr { - assert.ErrorContains(t, err, exp, "Validate\nExpecting: %q", exp) - } - } - } else { - assert.NoError(t, err, "Validate") - } + assertions.AssertErrorContents(t, err, tc.expErr, "Validate") }) } } @@ -206,12 +199,7 @@ func TestDenomSplit_Validate(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { err := tc.denomSplit.Validate() - // TODO[1658]: Replace this with assertErrorValue(t, err, tc.expErr, "Validate") - if len(tc.expErr) > 0 { - assert.EqualError(t, err, tc.expErr, "Validate") - } else { - assert.NoError(t, err, "Validate") - } + assertions.AssertErrorValue(t, err, tc.expErr, "Validate") }) } } From 591c6fed3ab27a7c5e9321ccd4e9195af31176cd Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 17:45:39 -0600 Subject: [PATCH 059/309] [1658]: Implement GovUpdateParams. --- x/exchange/keeper/keeper.go | 5 +++ x/exchange/keeper/msg_server.go | 67 +++++++++++++++++++++++---------- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 99956a29b8..51808144a6 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -23,3 +23,8 @@ func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey) Keeper { } return rv } + +// GetAuthority gets the address (as bech32) that has governance authority. +func (k Keeper) GetAuthority() string { + return k.authority +} diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index d9ef652c9a..8450da8c67 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -3,89 +3,118 @@ package keeper import ( "context" + cerrs "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/provenance-io/provenance/x/exchange" ) // MsgServer is an alias for a Keeper that implements the exchange.MsgServer interface. -type MsgServer Keeper +type MsgServer struct { + Keeper +} func NewMsgServer(k Keeper) exchange.MsgServer { - return MsgServer(k) + return MsgServer{ + Keeper: k, + } } var _ exchange.MsgServer = MsgServer{} -func (k MsgServer) CreateAsk(goCtx context.Context, req *exchange.MsgCreateAskRequest) (*exchange.MsgCreateAskResponse, error) { +func (k MsgServer) CreateAsk(goCtx context.Context, msg *exchange.MsgCreateAskRequest) (*exchange.MsgCreateAskResponse, error) { // TODO[1658]: Implement CreateAsk panic("not implemented") } -func (k MsgServer) CreateBid(goCtx context.Context, req *exchange.MsgCreateBidRequest) (*exchange.MsgCreateBidResponse, error) { +func (k MsgServer) CreateBid(goCtx context.Context, msg *exchange.MsgCreateBidRequest) (*exchange.MsgCreateBidResponse, error) { // TODO[1658]: Implement CreateBid panic("not implemented") } -func (k MsgServer) CancelOrder(goCtx context.Context, req *exchange.MsgCancelOrderRequest) (*exchange.MsgCancelOrderResponse, error) { +func (k MsgServer) CancelOrder(goCtx context.Context, msg *exchange.MsgCancelOrderRequest) (*exchange.MsgCancelOrderResponse, error) { // TODO[1658]: Implement CancelOrder panic("not implemented") } -func (k MsgServer) FillBids(goCtx context.Context, req *exchange.MsgFillBidsRequest) (*exchange.MsgFillBidsResponse, error) { +func (k MsgServer) FillBids(goCtx context.Context, msg *exchange.MsgFillBidsRequest) (*exchange.MsgFillBidsResponse, error) { // TODO[1658]: Implement FillBids panic("not implemented") } -func (k MsgServer) FillAsks(goCtx context.Context, req *exchange.MsgFillAsksRequest) (*exchange.MsgFillAsksResponse, error) { +func (k MsgServer) FillAsks(goCtx context.Context, msg *exchange.MsgFillAsksRequest) (*exchange.MsgFillAsksResponse, error) { // TODO[1658]: Implement FillAsks panic("not implemented") } -func (k MsgServer) MarketSettle(goCtx context.Context, req *exchange.MsgMarketSettleRequest) (*exchange.MsgMarketSettleResponse, error) { +func (k MsgServer) MarketSettle(goCtx context.Context, msg *exchange.MsgMarketSettleRequest) (*exchange.MsgMarketSettleResponse, error) { // TODO[1658]: Implement MarketSettle panic("not implemented") } -func (k MsgServer) MarketWithdraw(goCtx context.Context, req *exchange.MsgMarketWithdrawRequest) (*exchange.MsgMarketWithdrawResponse, error) { +func (k MsgServer) MarketWithdraw(goCtx context.Context, msg *exchange.MsgMarketWithdrawRequest) (*exchange.MsgMarketWithdrawResponse, error) { // TODO[1658]: Implement MarketWithdraw panic("not implemented") } -func (k MsgServer) MarketUpdateDetails(goCtx context.Context, req *exchange.MsgMarketUpdateDetailsRequest) (*exchange.MsgMarketUpdateDetailsResponse, error) { +func (k MsgServer) MarketUpdateDetails(goCtx context.Context, msg *exchange.MsgMarketUpdateDetailsRequest) (*exchange.MsgMarketUpdateDetailsResponse, error) { // TODO[1658]: Implement MarketUpdateDetails panic("not implemented") } -func (k MsgServer) MarketUpdateEnabled(goCtx context.Context, req *exchange.MsgMarketUpdateEnabledRequest) (*exchange.MsgMarketUpdateEnabledResponse, error) { +func (k MsgServer) MarketUpdateEnabled(goCtx context.Context, msg *exchange.MsgMarketUpdateEnabledRequest) (*exchange.MsgMarketUpdateEnabledResponse, error) { // TODO[1658]: Implement MarketUpdateEnabled panic("not implemented") } -func (k MsgServer) MarketUpdateUserSettle(goCtx context.Context, req *exchange.MsgMarketUpdateUserSettleRequest) (*exchange.MsgMarketUpdateUserSettleResponse, error) { +func (k MsgServer) MarketUpdateUserSettle(goCtx context.Context, msg *exchange.MsgMarketUpdateUserSettleRequest) (*exchange.MsgMarketUpdateUserSettleResponse, error) { // TODO[1658]: Implement MarketUpdateUserSettle panic("not implemented") } -func (k MsgServer) MarketManagePermissions(goCtx context.Context, req *exchange.MsgMarketManagePermissionsRequest) (*exchange.MsgMarketManagePermissionsResponse, error) { +func (k MsgServer) MarketManagePermissions(goCtx context.Context, msg *exchange.MsgMarketManagePermissionsRequest) (*exchange.MsgMarketManagePermissionsResponse, error) { // TODO[1658]: Implement MarketManagePermissions panic("not implemented") } -func (k MsgServer) MarketManageReqAttrs(goCtx context.Context, req *exchange.MsgMarketManageReqAttrsRequest) (*exchange.MsgMarketManageReqAttrsResponse, error) { +func (k MsgServer) MarketManageReqAttrs(goCtx context.Context, msg *exchange.MsgMarketManageReqAttrsRequest) (*exchange.MsgMarketManageReqAttrsResponse, error) { // TODO[1658]: Implement MarketManageReqAttrs panic("not implemented") } -func (k MsgServer) GovCreateMarket(goCtx context.Context, req *exchange.MsgGovCreateMarketRequest) (*exchange.MsgGovCreateMarketResponse, error) { +func (k MsgServer) GovCreateMarket(goCtx context.Context, msg *exchange.MsgGovCreateMarketRequest) (*exchange.MsgGovCreateMarketResponse, error) { + if msg.Authority != k.GetAuthority() { + return nil, cerrs.Wrapf(govtypes.ErrInvalidSigner, "expected %s got %s", k.GetAuthority(), msg.Authority) + } + + ctx := sdk.UnwrapSDKContext(goCtx) // TODO[1658]: Implement GovCreateMarket + _ = ctx panic("not implemented") } -func (k MsgServer) GovManageFees(goCtx context.Context, req *exchange.MsgGovManageFeesRequest) (*exchange.MsgGovManageFeesResponse, error) { +func (k MsgServer) GovManageFees(goCtx context.Context, msg *exchange.MsgGovManageFeesRequest) (*exchange.MsgGovManageFeesResponse, error) { + if msg.Authority != k.GetAuthority() { + return nil, cerrs.Wrapf(govtypes.ErrInvalidSigner, "expected %s got %s", k.GetAuthority(), msg.Authority) + } + + ctx := sdk.UnwrapSDKContext(goCtx) // TODO[1658]: Implement GovManageFees + _ = ctx panic("not implemented") } -func (k MsgServer) GovUpdateParams(goCtx context.Context, req *exchange.MsgGovUpdateParamsRequest) (*exchange.MsgGovUpdateParamsResponse, error) { - // TODO[1658]: Implement GovUpdateParams - panic("not implemented") +// GovUpdateParams is a governance proposal endpoint for updating the exchange module's params. +func (k MsgServer) GovUpdateParams(goCtx context.Context, msg *exchange.MsgGovUpdateParamsRequest) (*exchange.MsgGovUpdateParamsResponse, error) { + if msg.Authority != k.GetAuthority() { + return nil, cerrs.Wrapf(govtypes.ErrInvalidSigner, "expected %s got %s", k.GetAuthority(), msg.Authority) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + k.SetParams(ctx, &msg.Params) + + if err := ctx.EventManager().EmitTypedEvent(exchange.NewEventParamsUpdated()); err != nil { + return nil, err + } + return &exchange.MsgGovUpdateParamsResponse{}, nil } From 1cd0424f2b797d60f8a44f1a13e4a68bd83d3b14 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 17:54:59 -0600 Subject: [PATCH 060/309] [1658]: Put iterateParamsSplits in GetParams since that's the only place that wants it. --- x/exchange/keeper/params.go | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/x/exchange/keeper/params.go b/x/exchange/keeper/params.go index edbdbe9012..c5c84f5f2e 100644 --- a/x/exchange/keeper/params.go +++ b/x/exchange/keeper/params.go @@ -7,27 +7,11 @@ import ( "github.com/provenance-io/provenance/x/exchange" ) -// iterateParamsSplits iterates over all the params splits in the store (including the default entry). -// cb should return whether to stop early (true = stop now, false = keep going). -func iterateParamsSplits(store sdk.KVStore, cb func(denom string, split uint16) bool) { - // Using an open iterator on a prefixed store here so that iter.Key() doesn't contain the prefix. - pStore := prefix.NewStore(store, MakeKeyPrefixParamsSplit()) - iter := pStore.Iterator(nil, nil) - defer iter.Close() - - for ; iter.Valid(); iter.Next() { - denom := string(iter.Key()) - split := uint16FromBz(iter.Value()) - if cb(denom, split) { - break - } - } -} - // deleteAllParamsSplits deletes all the params splits in the store. func deleteAllParamsSplits(store sdk.KVStore) { var keys [][]byte + // Using a prefix iterator so that iter.Key() is the whole key (including the prefix). iter := sdk.KVStorePrefixIterator(store, MakeKeyPrefixParamsSplit()) defer func() { if iter != nil { @@ -79,8 +63,16 @@ func (k Keeper) SetParams(ctx sdk.Context, params *exchange.Params) { // GetParams gets the exchange module params. // If there aren't any params in state, nil is returned. func (k Keeper) GetParams(ctx sdk.Context) *exchange.Params { + // Using an open iterator on a prefixed store here so that iter.Key() doesn't contain the prefix. + store := prefix.NewStore(ctx.KVStore(k.storeKey), MakeKeyPrefixParamsSplit()) + iter := store.Iterator(nil, nil) + defer iter.Close() + var rv *exchange.Params - iterateParamsSplits(ctx.KVStore(k.storeKey), func(denom string, split uint16) bool { + for ; iter.Valid(); iter.Next() { + denom := string(iter.Key()) + split := uint16FromBz(iter.Value()) + if rv == nil { rv = &exchange.Params{} } @@ -89,8 +81,8 @@ func (k Keeper) GetParams(ctx sdk.Context) *exchange.Params { } else { rv.DenomSplits = append(rv.DenomSplits, exchange.DenomSplit{Denom: denom, Split: uint32(split)}) } - return false - }) + } + return rv } From 8c3251f44eaed34645fd9be543c07f853e1dcab4 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 18:36:38 -0600 Subject: [PATCH 061/309] [1658]: Create a generic getAllKeys for using in funcs like deleteAllParamsSplits. --- x/exchange/keeper/keeper.go | 39 +++++++++++++++++++++++++++++++++++++ x/exchange/keeper/params.go | 17 +--------------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 51808144a6..b3a37f2443 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -1,8 +1,11 @@ package keeper import ( + sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) @@ -28,3 +31,39 @@ func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey) Keeper { func (k Keeper) GetAuthority() string { return k.authority } + +// getAllKeys gets all the keys in the store with the given prefix. +func getAllKeys(store sdk.KVStore, pre []byte) [][]byte { + // Using a prefix iterator so that iter.Key() is the whole key (including the prefix). + iter := sdk.KVStorePrefixIterator(store, pre) + defer iter.Close() + + var keys [][]byte + for ; iter.Valid(); iter.Next() { + keys = append(keys, iter.Key()) + } + + return keys +} + +// getAllCoins gets all the coin entries from the store with the given prefix. +// The denom comes from the part of the key after the prefix, and the amount comes from the values. +func getAllCoins(store sdk.KVStore, pre []byte) []sdk.Coin { + // Using a prefixed store here so that iter.Key() doesn't contain the prefix. + pStore := prefix.NewStore(store, pre) + iter := pStore.Iterator(nil, nil) + defer iter.Close() + + var coins []sdk.Coin + for ; iter.Valid(); iter.Next() { + denom := string(iter.Key()) + value := string(iter.Value()) + amt, ok := sdkmath.NewIntFromString(value) + if !ok { + continue + } + coins = append(coins, sdk.Coin{Denom: denom, Amount: amt}) + } + + return coins +} diff --git a/x/exchange/keeper/params.go b/x/exchange/keeper/params.go index c5c84f5f2e..80349d8c54 100644 --- a/x/exchange/keeper/params.go +++ b/x/exchange/keeper/params.go @@ -9,22 +9,7 @@ import ( // deleteAllParamsSplits deletes all the params splits in the store. func deleteAllParamsSplits(store sdk.KVStore) { - var keys [][]byte - - // Using a prefix iterator so that iter.Key() is the whole key (including the prefix). - iter := sdk.KVStorePrefixIterator(store, MakeKeyPrefixParamsSplit()) - defer func() { - if iter != nil { - iter.Close() - } - }() - - for ; iter.Valid(); iter.Next() { - keys = append(keys, iter.Key()) - } - iter.Close() - iter = nil - + keys := getAllKeys(store, MakeKeyPrefixParamsSplit()) for _, key := range keys { store.Delete(key) } From bd123e33cd8a699337d1fa6ca796eb2ec3f9b9f9 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 18:37:09 -0600 Subject: [PATCH 062/309] [1658]: Keeper funcs for the create bid and ask flat fees. --- x/exchange/keeper/market.go | 115 ++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 x/exchange/keeper/market.go diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go new file mode 100644 index 0000000000..45130cb673 --- /dev/null +++ b/x/exchange/keeper/market.go @@ -0,0 +1,115 @@ +package keeper + +import ( + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// deleteCreateAskFlatFees deletes all the create-ask flat fees for the given market. +func deleteCreateAskFlatFees(store sdk.KVStore, marketID uint32) { + keys := getAllKeys(store, MakeKeyPrefixMarketCreateAskFlatFee(marketID)) + for _, key := range keys { + store.Delete(key) + } +} + +// setCreateAskFlatFee sets a create-ask flat fee option in the store. +func setCreateAskFlatFee(store sdk.KVStore, marketID uint32, coin sdk.Coin) { + key := MakeKeyMarketCreateAskFlatFee(marketID, coin.Denom) + value := coin.Amount.String() + store.Set(key, []byte(value)) +} + +// getCreateAskFlatFee gets a create-ask flat fee option from the store. +// Returns nil if either no entry for that denom exists, or the entry does exist, but can't be read. +func getCreateAskFlatFee(store sdk.KVStore, marketID uint32, denom string) *sdk.Coin { + key := MakeKeyMarketCreateAskFlatFee(marketID, denom) + if store.Has(key) { + value := string(store.Get(key)) + amt, ok := sdkmath.NewIntFromString(value) + if ok { + return &sdk.Coin{Denom: denom, Amount: amt} + } + } + return nil +} + +// SetCreateAskFlatFees sets the create-ask flat fees for a market. +func (k Keeper) SetCreateAskFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { + store := ctx.KVStore(k.storeKey) + deleteCreateAskFlatFees(store, marketID) + + for _, coin := range options { + setCreateAskFlatFee(store, marketID, coin) + } +} + +// GetCreateAskFlatFees gets the create-ask flat fees for a market. +func (k Keeper) GetCreateAskFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { + return getAllCoins(ctx.KVStore(k.storeKey), MakeKeyPrefixMarketCreateAskFlatFee(marketID)) +} + +// IsAcceptableCreateAskFlatFee returns true if the provide fee has a denom accepted as a create-ask flat fee, +// and the fee amount is at least as much as the required amount of that denom. +func (k Keeper) IsAcceptableCreateAskFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { + reqFee := getCreateAskFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom) + if reqFee == nil { + return false + } + return reqFee.Amount.LTE(fee.Amount) +} + +// deleteCreateBidFlatFees deletes all the create-bid flat fees for the given market. +func deleteCreateBidFlatFees(store sdk.KVStore, marketID uint32) { + keys := getAllKeys(store, MakeKeyPrefixMarketCreateBidFlatFee(marketID)) + for _, key := range keys { + store.Delete(key) + } +} + +// setCreateBidFlatFee sets a create-Bid flat fee option in the store. +func setCreateBidFlatFee(store sdk.KVStore, marketID uint32, coin sdk.Coin) { + key := MakeKeyMarketCreateBidFlatFee(marketID, coin.Denom) + value := coin.Amount.String() + store.Set(key, []byte(value)) +} + +// getCreateBidFlatFee gets a create-bid flat fee option from the store. +// Returns nil if either no entry for that denom exists, or the entry does exist, but can't be read. +func getCreateBidFlatFee(store sdk.KVStore, marketID uint32, denom string) *sdk.Coin { + key := MakeKeyMarketCreateBidFlatFee(marketID, denom) + if store.Has(key) { + value := string(store.Get(key)) + amt, ok := sdkmath.NewIntFromString(value) + if ok { + return &sdk.Coin{Denom: denom, Amount: amt} + } + } + return nil +} + +// SetCreateBidFlatFees sets the create-bid flat fees for a market. +func (k Keeper) SetCreateBidFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { + store := ctx.KVStore(k.storeKey) + deleteCreateBidFlatFees(store, marketID) + + for _, coin := range options { + setCreateBidFlatFee(store, marketID, coin) + } +} + +// GetCreateBidFlatFees gets the create-bid flat fees for a market. +func (k Keeper) GetCreateBidFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { + return getAllCoins(ctx.KVStore(k.storeKey), MakeKeyPrefixMarketCreateBidFlatFee(marketID)) +} + +// IsAcceptableCreateBidFlatFee returns true if the provide fee has a denom accepted as a create-bid flat fee, +// and the fee amount is at least as much as the required amount of that denom. +func (k Keeper) IsAcceptableCreateBidFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { + reqFee := getCreateBidFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom) + if reqFee == nil { + return false + } + return reqFee.Amount.LTE(fee.Amount) +} From 1bf08bc66f34a3c4c61dcbac81b7f7941cf3883c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 31 Aug 2023 18:52:53 -0600 Subject: [PATCH 063/309] [1658]: Hyphenate create-bid and create-ask everywhere and fix a couple lint issues. --- docs/proto-docs.md | 8 +++--- proto/provenance/exchange/v1/tx.proto | 8 +++--- x/exchange/genesis_test.go | 8 +++--- x/exchange/keeper/keeper.go | 1 + x/exchange/keeper/keys.go | 26 +++++++++---------- x/exchange/keeper/msg_server.go | 2 ++ x/exchange/market.go | 8 +++--- x/exchange/market_test.go | 22 ++++++++-------- x/exchange/msg.go | 4 +-- x/exchange/msg_test.go | 36 +++++++++++++-------------- x/exchange/tx.pb.go | 8 +++--- 11 files changed, 67 insertions(+), 64 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 486c6a3448..8ee0c05807 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2222,10 +2222,10 @@ MsgGovManageFeesRequest is a request message for the GovManageFees endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `authority` | [string](#string) | | authority should be the governance module account address. | -| `add_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_ask_flat are the create ask flat fee options to add. | -| `remove_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_ask_flat are the create ask flat fee options to remove. | -| `add_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_bid_flat are the create bid flat fee options to add. | -| `remove_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_bid_flat are the create bid flat fee options to remove. | +| `add_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_ask_flat are the create-ask flat fee options to add. | +| `remove_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_ask_flat are the create-ask flat fee options to remove. | +| `add_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_bid_flat are the create-bid flat fee options to add. | +| `remove_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_bid_flat are the create-bid flat fee options to remove. | | `add_fee_settlement_seller_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_settlement_seller_flat are the seller settlement flat fee options to add. | | `remove_fee_settlement_seller_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_settlement_seller_flat are the seller settlement flat fee options to remove. | | `add_fee_settlement_seller_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | add_fee_settlement_seller_ratios are the seller settlement fee ratios to add. | diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 554ca3c4c3..2d3278b945 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -180,16 +180,16 @@ message MsgGovManageFeesRequest { // authority should be the governance module account address. string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - // add_fee_create_ask_flat are the create ask flat fee options to add. + // add_fee_create_ask_flat are the create-ask flat fee options to add. repeated cosmos.base.v1beta1.Coin add_fee_create_ask_flat = 2 [(gogoproto.nullable) = false]; - // remove_fee_create_ask_flat are the create ask flat fee options to remove. + // remove_fee_create_ask_flat are the create-ask flat fee options to remove. repeated cosmos.base.v1beta1.Coin remove_fee_create_ask_flat = 3 [(gogoproto.nullable) = false]; - // add_fee_create_bid_flat are the create bid flat fee options to add. + // add_fee_create_bid_flat are the create-bid flat fee options to add. repeated cosmos.base.v1beta1.Coin add_fee_create_bid_flat = 4 [(gogoproto.nullable) = false]; - // remove_fee_create_bid_flat are the create bid flat fee options to remove. + // remove_fee_create_bid_flat are the create-bid flat fee options to remove. repeated cosmos.base.v1beta1.Coin remove_fee_create_bid_flat = 5 [(gogoproto.nullable) = false]; // add_fee_settlement_seller_flat are the seller settlement flat fee options to add. diff --git a/x/exchange/genesis_test.go b/x/exchange/genesis_test.go index 9971250787..4bb909a2ad 100644 --- a/x/exchange/genesis_test.go +++ b/x/exchange/genesis_test.go @@ -309,9 +309,9 @@ func TestGenesisState_Validate(t *testing.T) { }, }, expErr: []string{ - `invalid market[0]: invalid create ask flat fee option "-1kif": negative coin amount: -1`, - `invalid market[1]: invalid create ask flat fee option "-2kif": negative coin amount: -2`, - `invalid market[2]: invalid create ask flat fee option "-3kif": negative coin amount: -3`, + `invalid market[0]: invalid create-ask flat fee option "-1kif": negative coin amount: -1`, + `invalid market[1]: invalid create-ask flat fee option "-2kif": negative coin amount: -2`, + `invalid market[2]: invalid create-ask flat fee option "-3kif": negative coin amount: -3`, }, }, { @@ -407,7 +407,7 @@ func TestGenesisState_Validate(t *testing.T) { }, expErr: []string{ "invalid params: default split 10001 cannot be greater than 10000", - `invalid market[1]: invalid create bid flat fee option "-1zapp": negative coin amount: -1`, + `invalid market[1]: invalid create-bid flat fee option "-1zapp": negative coin amount: -1`, `invalid order[1]: unknown market id 4`, }, }, diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index b3a37f2443..d7abd3722f 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -2,6 +2,7 @@ package keeper import ( sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" storetypes "github.com/cosmos/cosmos-sdk/store/types" diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index f8ac418500..689c0851b3 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -25,8 +25,8 @@ import ( // All market-related entries start with the type byte 0x01 followed by the , then a market key type byte. // The is a uint32 in big-endian order (4 bytes). // -// Market Create Ask Flat Fee: 0x01 | | 0x00 | => (string) -// Market Create Bid Flat Fee: 0x01 | | 0x01 | => (string) +// Market Create-Ask Flat Fee: 0x01 | | 0x00 | => (string) +// Market Create-Bid Flat Fee: 0x01 | | 0x01 | => (string) // Market Settlement Seller Flat Fee: 0x01 | | 0x02 | => (string) // Market Settlement Seller Fee Ratio: 0x01 | | 0x03 | | 0x00 | => comma-separated price and fee amount (string). // Market Settlement Buyer Flat Fee: 0x01 | | 0x04 | => (string) @@ -72,9 +72,9 @@ const ( // KeyTypeAssetToOrderIndex is the type byte for entries in the asset to order index. KeyTypeAssetToOrderIndex = byte(0x05) - // MarketKeyTypeCreateAskFlat is the market-specific type byte for the create ask flat fees. + // MarketKeyTypeCreateAskFlat is the market-specific type byte for the create-ask flat fees. MarketKeyTypeCreateAskFlat = byte(0x00) - // MarketKeyTypeCreateBidFlat is the market-specific type byte for the create bid flat fees. + // MarketKeyTypeCreateBidFlat is the market-specific type byte for the create-bid flat fees. MarketKeyTypeCreateBidFlat = byte(0x01) // MarketKeyTypeSettlementSellerFlat is the market-specific type byte for the seller settlement flat fees. MarketKeyTypeSettlementSellerFlat = byte(0x02) @@ -179,34 +179,34 @@ func keyPrefixMarketType(marketID uint32, marketTypeByte byte, extraCap int) []b return rv } -// marketKeyPrefixCreateAskFlatFee creates the key prefix for a market's create ask flat fees with extra capacity for the rest. +// marketKeyPrefixCreateAskFlatFee creates the key prefix for a market's create-ask flat fees with extra capacity for the rest. func marketKeyPrefixCreateAskFlatFee(marketID uint32, extraCap int) []byte { return keyPrefixMarketType(marketID, MarketKeyTypeCreateAskFlat, extraCap) } -// MakeKeyPrefixMarketCreateAskFlatFee creates the key prefix for the create ask flat fees for the provided market. +// MakeKeyPrefixMarketCreateAskFlatFee creates the key prefix for the create-ask flat fees for the provided market. func MakeKeyPrefixMarketCreateAskFlatFee(marketID uint32) []byte { return marketKeyPrefixCreateAskFlatFee(marketID, 0) } -// MakeKeyMarketCreateAskFlatFee creates the key to use for a create ask flat fee for the given market and denom. +// MakeKeyMarketCreateAskFlatFee creates the key to use for a create-ask flat fee for the given market and denom. func MakeKeyMarketCreateAskFlatFee(marketID uint32, denom string) []byte { rv := marketKeyPrefixCreateAskFlatFee(marketID, len(denom)) rv = append(rv, denom...) return rv } -// marketKeyPrefixCreateBidFlatFee creates the key prefix for a market's create bid flat fees with extra capacity for the rest. +// marketKeyPrefixCreateBidFlatFee creates the key prefix for a market's create-bid flat fees with extra capacity for the rest. func marketKeyPrefixCreateBidFlatFee(marketID uint32, extraCap int) []byte { return keyPrefixMarketType(marketID, MarketKeyTypeCreateBidFlat, extraCap) } -// MakeKeyPrefixMarketCreateBidFlatFee creates the key prefix for the create bid flat fees for the provided market. +// MakeKeyPrefixMarketCreateBidFlatFee creates the key prefix for the create-bid flat fees for the provided market. func MakeKeyPrefixMarketCreateBidFlatFee(marketID uint32) []byte { return marketKeyPrefixCreateBidFlatFee(marketID, 0) } -// MakeKeyMarketCreateBidFlatFee creates the key to use for a create bid flat fee for the given denom. +// MakeKeyMarketCreateBidFlatFee creates the key to use for a create-bid flat fee for the given denom. func MakeKeyMarketCreateBidFlatFee(marketID uint32, denom string) []byte { rv := marketKeyPrefixCreateBidFlatFee(marketID, len(denom)) rv = append(rv, denom...) @@ -259,7 +259,7 @@ func MakeKeyPrefixMarketSettlementBuyerFlatFee(marketID uint32) []byte { return marketKeyPrefixSettlementBuyerFlatFee(marketID, 0) } -// MakeKeyMarketSettlementBuyerFlatFee creates th ekey for a market's settlement buyer flat fee with the given denom. +// MakeKeyMarketSettlementBuyerFlatFee creates the key for a market's settlement buyer flat fee with the given denom. func MakeKeyMarketSettlementBuyerFlatFee(marketID uint32, denom string) []byte { rv := marketKeyPrefixSettlementBuyerFlatFee(marketID, len(denom)) rv = append(rv, denom...) @@ -342,7 +342,7 @@ func MakeKeyMarketReqAttrAsk(marketID uint32) []byte { return rv } -// MakeKeyMarketReqAttrBid creates the key to use for a market's attributes required to create an bid order. +// MakeKeyMarketReqAttrBid creates the key to use for a market's attributes required to create a bid order. func MakeKeyMarketReqAttrBid(marketID uint32) []byte { rv := marketKeyPrefixReqAttr(marketID) rv = append(rv, OrderKeyTypeBid) @@ -403,7 +403,7 @@ func MakeIndexKeyAddressToOrder(addr sdk.AccAddress, orderID uint64) []byte { return rv } -// indexPrefixAssetToOrder creates the prefix for the asset to order index enties with some extra space for the rest. +// indexPrefixAssetToOrder creates the prefix for the asset to order index entries with some extra space for the rest. func indexPrefixAssetToOrder(assetDenom string, extraCap int) []byte { return prepKey(KeyTypeAssetToOrderIndex, []byte(assetDenom), extraCap) } diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 8450da8c67..3c876747e3 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -4,8 +4,10 @@ import ( "context" cerrs "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/provenance-io/provenance/x/exchange" ) diff --git a/x/exchange/market.go b/x/exchange/market.go index 3d91a6835b..a1d7ddb51b 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -36,8 +36,8 @@ func (m Market) Validate() error { errs := []error{ m.MarketDetails.Validate(), - ValidateFeeOptions("create ask flat fee", m.FeeCreateAskFlat), - ValidateFeeOptions("create bid flat fee", m.FeeCreateBidFlat), + ValidateFeeOptions("create-ask flat fee", m.FeeCreateAskFlat), + ValidateFeeOptions("create-bid flat fee", m.FeeCreateBidFlat), ValidateFeeOptions("settlement seller flat fee", m.FeeSettlementSellerFlat), ValidateFeeOptions("settlement buyer flat fee", m.FeeSettlementBuyerFlat), ValidateFeeRatios(m.FeeSettlementSellerRatios, m.FeeSettlementBuyerRatios), @@ -47,10 +47,10 @@ func (m Market) Validate() error { // Nothing to check for with the AcceptingOrders and AllowUserSettlement booleans. if err := ValidateReqAttrs(m.ReqAttrCreateAsk); err != nil { - errs = append(errs, fmt.Errorf("invalid create ask required attributes: %w", err)) + errs = append(errs, fmt.Errorf("invalid create-ask required attributes: %w", err)) } if err := ValidateReqAttrs(m.ReqAttrCreateBid); err != nil { - errs = append(errs, fmt.Errorf("invalid create bid required attributes: %w", err)) + errs = append(errs, fmt.Errorf("invalid create-bid required attributes: %w", err)) } return errors.Join(errs...) diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 848b07d9c5..284ae802e6 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -81,14 +81,14 @@ func TestMarket_Validate(t *testing.T) { expErr: []string{fmt.Sprintf("name length %d exceeds maximum length of %d", MaxName+1, MaxName)}, }, { - name: "invalid fee create ask flat", + name: "invalid fee create-ask flat", market: Market{FeeCreateAskFlat: sdk.Coins{coin(-1, "leela")}}, - expErr: []string{`invalid create ask flat fee option "-1leela": negative coin amount: -1`}, + expErr: []string{`invalid create-ask flat fee option "-1leela": negative coin amount: -1`}, }, { - name: "invalid fee create bid flat", + name: "invalid fee create-bid flat", market: Market{FeeCreateBidFlat: sdk.Coins{coin(-1, "leela")}}, - expErr: []string{`invalid create bid flat fee option "-1leela": negative coin amount: -1`}, + expErr: []string{`invalid create-bid flat fee option "-1leela": negative coin amount: -1`}, }, { name: "invalid fee settlement seller flat", @@ -129,12 +129,12 @@ func TestMarket_Validate(t *testing.T) { { name: "invalid ask required attributes", market: Market{ReqAttrCreateAsk: []string{"this-attr-is-bad"}}, - expErr: []string{`invalid create ask required attributes: invalid required attribute "this-attr-is-bad"`}, + expErr: []string{`invalid create-ask required attributes: invalid required attribute "this-attr-is-bad"`}, }, { name: "invalid bid required attributes", market: Market{ReqAttrCreateBid: []string{"this-attr-grrrr"}}, - expErr: []string{`invalid create bid required attributes: invalid required attribute "this-attr-grrrr"`}, + expErr: []string{`invalid create-bid required attributes: invalid required attribute "this-attr-grrrr"`}, }, { name: "multiple errors", @@ -152,15 +152,15 @@ func TestMarket_Validate(t *testing.T) { }, expErr: []string{ fmt.Sprintf("name length %d exceeds maximum length of %d", MaxName+1, MaxName), - `invalid create ask flat fee option "-1leela": negative coin amount: -1`, - `invalid create bid flat fee option "-1leela": negative coin amount: -1`, + `invalid create-ask flat fee option "-1leela": negative coin amount: -1`, + `invalid create-bid flat fee option "-1leela": negative coin amount: -1`, `invalid settlement seller flat fee option "-1leela": negative coin amount: -1`, `invalid settlement buyer flat fee option "-1leela": negative coin amount: -1`, `denom "fry" is defined in the seller settlement fee ratios but not buyer`, `denom "leela" is defined in the buyer settlement fee ratios but not seller`, "invalid access grant: invalid address: decoding bech32 failed: invalid separator index -1", - `invalid create ask required attributes: invalid required attribute "this-attr-is-bad"`, - `invalid create bid required attributes: invalid required attribute "this-attr-grrrr"`, + `invalid create-ask required attributes: invalid required attribute "this-attr-is-bad"`, + `invalid create-bid required attributes: invalid required attribute "this-attr-grrrr"`, }, }, } @@ -590,7 +590,7 @@ func TestValidateSellerFeeRatios(t *testing.T) { exp: `seller fee ratio denom "hermes" appears in multiple ratios`, }, { - name: "three with diffrent denoms", + name: "three with different denoms", ratios: []FeeRatio{ {Price: coin(30, "leela"), Fee: coin(1, "leela")}, {Price: coin(5, "fry"), Fee: coin(1, "fry")}, diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 4fc37c2948..17abaf72ad 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -167,8 +167,8 @@ func (m MsgGovManageFeesRequest) ValidateBasic() error { if m.HasUpdates() { errs = append(errs, - ValidateAddRemoveFeeOptions("create ask flat fee", m.AddFeeCreateAskFlat, m.RemoveFeeCreateAskFlat), - ValidateAddRemoveFeeOptions("create bid flat fee", m.AddFeeCreateBidFlat, m.RemoveFeeCreateBidFlat), + ValidateAddRemoveFeeOptions("create-ask flat fee", m.AddFeeCreateAskFlat, m.RemoveFeeCreateAskFlat), + ValidateAddRemoveFeeOptions("create-bid flat fee", m.AddFeeCreateBidFlat, m.RemoveFeeCreateBidFlat), ValidateAddRemoveFeeOptions("seller settlement flat fee", m.AddFeeSettlementSellerFlat, m.RemoveFeeSettlementSellerFlat), ValidateSellerFeeRatios(m.AddFeeSettlementSellerRatios), ValidateDisjointFeeRatios("seller settlement fee", m.AddFeeSettlementSellerRatios, m.RemoveFeeSettlementSellerRatios), diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index af3e4c559b..462385cee4 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -219,7 +219,7 @@ func TestMsgGovCreateMarketRequest_ValidateBasic(t *testing.T) { FeeCreateAskFlat: []sdk.Coin{{Denom: "badbad", Amount: sdkmath.NewInt(0)}}, }, }, - expErr: []string{`invalid create ask flat fee option "0badbad": amount cannot be zero`}, + expErr: []string{`invalid create-ask flat fee option "0badbad": amount cannot be zero`}, }, { name: "multiple errors", @@ -231,7 +231,7 @@ func TestMsgGovCreateMarketRequest_ValidateBasic(t *testing.T) { }, expErr: []string{ "invalid authority", - `invalid create bid flat fee option "0badbad": amount cannot be zero`, + `invalid create-bid flat fee option "0badbad": amount cannot be zero`, }, }, } @@ -279,38 +279,38 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { expErr: []string{"invalid authority", "decoding bech32 failed"}, }, { - name: "invalid add create ask flat", + name: "invalid add create-ask flat", msg: MsgGovManageFeesRequest{ Authority: authority, AddFeeCreateAskFlat: []sdk.Coin{coin(0, "nhash")}, }, - expErr: []string{`invalid create ask flat fee to add option "0nhash": amount cannot be zero`}, + expErr: []string{`invalid create-ask flat fee to add option "0nhash": amount cannot be zero`}, }, { - name: "same add and remove create ask flat", + name: "same add and remove create-ask flat", msg: MsgGovManageFeesRequest{ Authority: authority, AddFeeCreateAskFlat: []sdk.Coin{coin(1, "nhash")}, RemoveFeeCreateAskFlat: []sdk.Coin{coin(1, "nhash")}, }, - expErr: []string{"cannot add and remove the same create ask flat fee options: 1nhash"}, + expErr: []string{"cannot add and remove the same create-ask flat fee options: 1nhash"}, }, { - name: "invalid add create bid flat", + name: "invalid add create-bid flat", msg: MsgGovManageFeesRequest{ Authority: authority, AddFeeCreateBidFlat: []sdk.Coin{coin(0, "nhash")}, }, - expErr: []string{`invalid create bid flat fee to add option "0nhash": amount cannot be zero`}, + expErr: []string{`invalid create-bid flat fee to add option "0nhash": amount cannot be zero`}, }, { - name: "same add and remove create bid flat", + name: "same add and remove create-bid flat", msg: MsgGovManageFeesRequest{ Authority: authority, AddFeeCreateBidFlat: []sdk.Coin{coin(1, "nhash")}, RemoveFeeCreateBidFlat: []sdk.Coin{coin(1, "nhash")}, }, - expErr: []string{"cannot add and remove the same create bid flat fee options: 1nhash"}, + expErr: []string{"cannot add and remove the same create-bid flat fee options: 1nhash"}, }, { name: "invalid add settlement seller flat", @@ -399,10 +399,10 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { }, expErr: []string{ "invalid authority", "empty address string is not allowed", - `invalid create ask flat fee to add option "0nhash": amount cannot be zero`, - "cannot add and remove the same create ask flat fee options: 0nhash", - `invalid create bid flat fee to add option "0nhash": amount cannot be zero`, - "cannot add and remove the same create bid flat fee options: 0nhash", + `invalid create-ask flat fee to add option "0nhash": amount cannot be zero`, + "cannot add and remove the same create-ask flat fee options: 0nhash", + `invalid create-bid flat fee to add option "0nhash": amount cannot be zero`, + "cannot add and remove the same create-bid flat fee options: 0nhash", `invalid seller settlement flat fee to add option "0nhash": amount cannot be zero`, "cannot add and remove the same seller settlement flat fee options: 0nhash", `seller fee ratio fee amount "2nhash" cannot be greater than price amount "1nhash"`, @@ -444,22 +444,22 @@ func TestMsgGovManageFeesRequest_HasUpdates(t *testing.T) { exp: false, }, { - name: "one add fee create ask flat", + name: "one add fee create-ask flat", msg: MsgGovManageFeesRequest{AddFeeCreateAskFlat: oneCoin}, exp: true, }, { - name: "one remove fee create ask flat", + name: "one remove fee create-ask flat", msg: MsgGovManageFeesRequest{RemoveFeeCreateAskFlat: oneCoin}, exp: true, }, { - name: "one add fee create bid flat", + name: "one add fee create-bid flat", msg: MsgGovManageFeesRequest{AddFeeCreateBidFlat: oneCoin}, exp: true, }, { - name: "one remove fee create bid flat", + name: "one remove fee create-bid flat", msg: MsgGovManageFeesRequest{RemoveFeeCreateBidFlat: oneCoin}, exp: true, }, diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 202189e4f5..8500eb4980 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -1017,13 +1017,13 @@ var xxx_messageInfo_MsgGovCreateMarketResponse proto.InternalMessageInfo type MsgGovManageFeesRequest struct { // authority should be the governance module account address. Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` - // add_fee_create_ask_flat are the create ask flat fee options to add. + // add_fee_create_ask_flat are the create-ask flat fee options to add. AddFeeCreateAskFlat []types.Coin `protobuf:"bytes,2,rep,name=add_fee_create_ask_flat,json=addFeeCreateAskFlat,proto3" json:"add_fee_create_ask_flat"` - // remove_fee_create_ask_flat are the create ask flat fee options to remove. + // remove_fee_create_ask_flat are the create-ask flat fee options to remove. RemoveFeeCreateAskFlat []types.Coin `protobuf:"bytes,3,rep,name=remove_fee_create_ask_flat,json=removeFeeCreateAskFlat,proto3" json:"remove_fee_create_ask_flat"` - // add_fee_create_bid_flat are the create bid flat fee options to add. + // add_fee_create_bid_flat are the create-bid flat fee options to add. AddFeeCreateBidFlat []types.Coin `protobuf:"bytes,4,rep,name=add_fee_create_bid_flat,json=addFeeCreateBidFlat,proto3" json:"add_fee_create_bid_flat"` - // remove_fee_create_bid_flat are the create bid flat fee options to remove. + // remove_fee_create_bid_flat are the create-bid flat fee options to remove. RemoveFeeCreateBidFlat []types.Coin `protobuf:"bytes,5,rep,name=remove_fee_create_bid_flat,json=removeFeeCreateBidFlat,proto3" json:"remove_fee_create_bid_flat"` // add_fee_settlement_seller_flat are the seller settlement flat fee options to add. AddFeeSettlementSellerFlat []types.Coin `protobuf:"bytes,6,rep,name=add_fee_settlement_seller_flat,json=addFeeSettlementSellerFlat,proto3" json:"add_fee_settlement_seller_flat"` From 9faed983f828be35ca9890be6986e45a83ce0f2e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 1 Sep 2023 11:11:08 -0600 Subject: [PATCH 064/309] [1658]: Put the 'buyer' and 'seller' before 'settlement'. --- docs/proto-docs.md | 28 +-- proto/provenance/exchange/v1/events.proto | 4 +- proto/provenance/exchange/v1/market.proto | 16 +- proto/provenance/exchange/v1/tx.proto | 32 +-- x/exchange/events.pb.go | 4 +- x/exchange/genesis_test.go | 8 +- x/exchange/keeper/keys.go | 108 ++++----- x/exchange/keeper/keys_test.go | 98 ++++----- x/exchange/market.go | 6 +- x/exchange/market.pb.go | 220 +++++++++---------- x/exchange/market_test.go | 40 ++-- x/exchange/msg.go | 20 +- x/exchange/msg_test.go | 96 ++++---- x/exchange/tx.pb.go | 254 +++++++++++----------- 14 files changed, 467 insertions(+), 467 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 8ee0c05807..442d2cf2af 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1446,13 +1446,13 @@ EventMarketReqAttrUpdated is an event emitted when a market's required attribute ### EventMarketUserSettleUpdated -EventMarketUserSettleUpdated is an event emitted when a market's self_settle option is updated. +EventMarketUserSettleUpdated is an event emitted when a market's user_settle option is updated. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | -| `updated_by` | [string](#string) | | updated_by is the account that updated the self_settle option. | +| `updated_by` | [string](#string) | | updated_by is the account that updated the user_settle option. | @@ -1613,10 +1613,10 @@ Market contains all information about a market. | `market_details` | [MarketDetails](#provenance.exchange.v1.MarketDetails) | | market_details is some information about this market. | | `fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | fee_create_ask_flat is the flat fee charged for creating an ask order. Each coin entry is a separate option. When an ask is created, one of these must be paid. If empty, no fee is required to create an ask order. | | `fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | fee_create_bid_flat is the flat fee charged for creating a bid order. Each coin entry is a separate option. When a bid is created, one of these must be paid. If empty, no fee is required to create a bid order. | -| `fee_settlement_seller_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | fee_settlement_seller_flat is the flat fee charged to the seller during settlement. Each coin entry is a separate option. When an ask is settled, the seller will pay the amount in the denom that matches the price they received. | -| `fee_settlement_seller_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | fee_settlement_seller_ratios is the fee to charge a seller during settlement based on the price they are receiving. The price and fee denoms must be equal for each entry, and only one entry for any given denom is allowed. | -| `fee_settlement_buyer_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | fee_settlement_buyer_flat is the flat fee charged to the buyer during settlement. Each coin entry is a separate option. When a bid is created, the settlement fees provided must contain one of these. | -| `fee_settlement_buyer_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | fee_settlement_buyer_ratios is the fee to charge a buyer during settlement based on the price they are spending. The price and fee denoms do not have to equal. Multiple entries for any given price or fee denom are allowed, but each price denom to fee denom pair can only have one entry. | +| `fee_seller_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | fee_seller_settlement_flat is the flat fee charged to the seller during settlement. Each coin entry is a separate option. When an ask is settled, the seller will pay the amount in the denom that matches the price they received. | +| `fee_seller_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | fee_seller_settlement_ratios is the fee to charge a seller during settlement based on the price they are receiving. The price and fee denoms must be equal for each entry, and only one entry for any given denom is allowed. | +| `fee_buyer_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | fee_buyer_settlement_flat is the flat fee charged to the buyer during settlement. Each coin entry is a separate option. When a bid is created, the settlement fees provided must contain one of these. | +| `fee_buyer_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | fee_buyer_settlement_ratios is the fee to charge a buyer during settlement based on the price they are spending. The price and fee denoms do not have to equal. Multiple entries for any given price or fee denom are allowed, but each price denom to fee denom pair can only have one entry. | | `accepting_orders` | [bool](#bool) | | accepting_orders is whether this market is allowing orders to be created for it. | | `allow_user_settlement` | [bool](#bool) | | allow_user_settlement is whether this market allows users to initiate their own settlements. For example, the FillBids and FillAsks endpoints are available if and only if this is true. The MarketSettle endpoint is only available to market actors regardless of the value of this field. | | `access_grants` | [AccessGrant](#provenance.exchange.v1.AccessGrant) | repeated | access_grants is the list of addresses and permissions granted for this market. | @@ -2226,14 +2226,14 @@ MsgGovManageFeesRequest is a request message for the GovManageFees endpoint. | `remove_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_ask_flat are the create-ask flat fee options to remove. | | `add_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_bid_flat are the create-bid flat fee options to add. | | `remove_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_bid_flat are the create-bid flat fee options to remove. | -| `add_fee_settlement_seller_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_settlement_seller_flat are the seller settlement flat fee options to add. | -| `remove_fee_settlement_seller_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_settlement_seller_flat are the seller settlement flat fee options to remove. | -| `add_fee_settlement_seller_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | add_fee_settlement_seller_ratios are the seller settlement fee ratios to add. | -| `remove_fee_settlement_seller_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | remove_fee_settlement_seller_ratios are the seller settlement fee ratios to remove. | -| `add_fee_settlement_buyer_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_settlement_buyer_flat are the buyer settlement flat fee options to add. | -| `remove_fee_settlement_buyer_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_settlement_buyer_flat are the buyer settlement flat fee options to remove. | -| `add_fee_settlement_buyer_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | add_fee_settlement_buyer_ratios are the buyer settlement fee ratios to add. | -| `remove_fee_settlement_buyer_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | remove_fee_settlement_buyer_ratios are the buyer settlement fee ratios to remove. | +| `add_fee_seller_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_seller_settlement_flat are the seller settlement flat fee options to add. | +| `remove_fee_seller_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_seller_settlement_flat are the seller settlement flat fee options to remove. | +| `add_fee_seller_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | add_fee_seller_settlement_ratios are the seller settlement fee ratios to add. | +| `remove_fee_seller_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | remove_fee_seller_settlement_ratios are the seller settlement fee ratios to remove. | +| `add_fee_buyer_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_buyer_settlement_flat are the buyer settlement flat fee options to add. | +| `remove_fee_buyer_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_buyer_settlement_flat are the buyer settlement flat fee options to remove. | +| `add_fee_buyer_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | add_fee_buyer_settlement_ratios are the buyer settlement fee ratios to add. | +| `remove_fee_buyer_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | remove_fee_buyer_settlement_ratios are the buyer settlement fee ratios to remove. | diff --git a/proto/provenance/exchange/v1/events.proto b/proto/provenance/exchange/v1/events.proto index f83b7198e5..24ac27910a 100644 --- a/proto/provenance/exchange/v1/events.proto +++ b/proto/provenance/exchange/v1/events.proto @@ -77,11 +77,11 @@ message EventMarketDisabled { string updated_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; } -// EventMarketUserSettleUpdated is an event emitted when a market's self_settle option is updated. +// EventMarketUserSettleUpdated is an event emitted when a market's user_settle option is updated. message EventMarketUserSettleUpdated { // market_id is the numerical identifier of the market. uint32 market_id = 1; - // updated_by is the account that updated the self_settle option. + // updated_by is the account that updated the user_settle option. string updated_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; } diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index a684519687..652c294493 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -54,21 +54,21 @@ message Market { // Each coin entry is a separate option. When a bid is created, one of these must be paid. // If empty, no fee is required to create a bid order. repeated cosmos.base.v1beta1.Coin fee_create_bid_flat = 4 [(gogoproto.nullable) = false]; - // fee_settlement_seller_flat is the flat fee charged to the seller during settlement. + // fee_seller_settlement_flat is the flat fee charged to the seller during settlement. // Each coin entry is a separate option. // When an ask is settled, the seller will pay the amount in the denom that matches the price they received. - repeated cosmos.base.v1beta1.Coin fee_settlement_seller_flat = 5 [(gogoproto.nullable) = false]; - // fee_settlement_seller_ratios is the fee to charge a seller during settlement based on the price they are receiving. + repeated cosmos.base.v1beta1.Coin fee_seller_settlement_flat = 5 [(gogoproto.nullable) = false]; + // fee_seller_settlement_ratios is the fee to charge a seller during settlement based on the price they are receiving. // The price and fee denoms must be equal for each entry, and only one entry for any given denom is allowed. - repeated FeeRatio fee_settlement_seller_ratios = 6 [(gogoproto.nullable) = false]; - // fee_settlement_buyer_flat is the flat fee charged to the buyer during settlement. + repeated FeeRatio fee_seller_settlement_ratios = 6 [(gogoproto.nullable) = false]; + // fee_buyer_settlement_flat is the flat fee charged to the buyer during settlement. // Each coin entry is a separate option. // When a bid is created, the settlement fees provided must contain one of these. - repeated cosmos.base.v1beta1.Coin fee_settlement_buyer_flat = 7 [(gogoproto.nullable) = false]; - // fee_settlement_buyer_ratios is the fee to charge a buyer during settlement based on the price they are spending. + repeated cosmos.base.v1beta1.Coin fee_buyer_settlement_flat = 7 [(gogoproto.nullable) = false]; + // fee_buyer_settlement_ratios is the fee to charge a buyer during settlement based on the price they are spending. // The price and fee denoms do not have to equal. Multiple entries for any given price or fee denom are allowed, but // each price denom to fee denom pair can only have one entry. - repeated FeeRatio fee_settlement_buyer_ratios = 8 [(gogoproto.nullable) = false]; + repeated FeeRatio fee_buyer_settlement_ratios = 8 [(gogoproto.nullable) = false]; // accepting_orders is whether this market is allowing orders to be created for it. bool accepting_orders = 9; // allow_user_settlement is whether this market allows users to initiate their own settlements. diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 2d3278b945..0b22d69bdd 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -192,29 +192,29 @@ message MsgGovManageFeesRequest { // remove_fee_create_bid_flat are the create-bid flat fee options to remove. repeated cosmos.base.v1beta1.Coin remove_fee_create_bid_flat = 5 [(gogoproto.nullable) = false]; - // add_fee_settlement_seller_flat are the seller settlement flat fee options to add. - repeated cosmos.base.v1beta1.Coin add_fee_settlement_seller_flat = 6 [(gogoproto.nullable) = false]; + // add_fee_seller_settlement_flat are the seller settlement flat fee options to add. + repeated cosmos.base.v1beta1.Coin add_fee_seller_settlement_flat = 6 [(gogoproto.nullable) = false]; - // remove_fee_settlement_seller_flat are the seller settlement flat fee options to remove. - repeated cosmos.base.v1beta1.Coin remove_fee_settlement_seller_flat = 7 [(gogoproto.nullable) = false]; + // remove_fee_seller_settlement_flat are the seller settlement flat fee options to remove. + repeated cosmos.base.v1beta1.Coin remove_fee_seller_settlement_flat = 7 [(gogoproto.nullable) = false]; - // add_fee_settlement_seller_ratios are the seller settlement fee ratios to add. - repeated FeeRatio add_fee_settlement_seller_ratios = 8 [(gogoproto.nullable) = false]; + // add_fee_seller_settlement_ratios are the seller settlement fee ratios to add. + repeated FeeRatio add_fee_seller_settlement_ratios = 8 [(gogoproto.nullable) = false]; - // remove_fee_settlement_seller_ratios are the seller settlement fee ratios to remove. - repeated FeeRatio remove_fee_settlement_seller_ratios = 9 [(gogoproto.nullable) = false]; + // remove_fee_seller_settlement_ratios are the seller settlement fee ratios to remove. + repeated FeeRatio remove_fee_seller_settlement_ratios = 9 [(gogoproto.nullable) = false]; - // add_fee_settlement_buyer_flat are the buyer settlement flat fee options to add. - repeated cosmos.base.v1beta1.Coin add_fee_settlement_buyer_flat = 10 [(gogoproto.nullable) = false]; + // add_fee_buyer_settlement_flat are the buyer settlement flat fee options to add. + repeated cosmos.base.v1beta1.Coin add_fee_buyer_settlement_flat = 10 [(gogoproto.nullable) = false]; - // remove_fee_settlement_buyer_flat are the buyer settlement flat fee options to remove. - repeated cosmos.base.v1beta1.Coin remove_fee_settlement_buyer_flat = 11 [(gogoproto.nullable) = false]; + // remove_fee_buyer_settlement_flat are the buyer settlement flat fee options to remove. + repeated cosmos.base.v1beta1.Coin remove_fee_buyer_settlement_flat = 11 [(gogoproto.nullable) = false]; - // add_fee_settlement_buyer_ratios are the buyer settlement fee ratios to add. - repeated FeeRatio add_fee_settlement_buyer_ratios = 12 [(gogoproto.nullable) = false]; + // add_fee_buyer_settlement_ratios are the buyer settlement fee ratios to add. + repeated FeeRatio add_fee_buyer_settlement_ratios = 12 [(gogoproto.nullable) = false]; - // remove_fee_settlement_buyer_ratios are the buyer settlement fee ratios to remove. - repeated FeeRatio remove_fee_settlement_buyer_ratios = 13 [(gogoproto.nullable) = false]; + // remove_fee_buyer_settlement_ratios are the buyer settlement fee ratios to remove. + repeated FeeRatio remove_fee_buyer_settlement_ratios = 13 [(gogoproto.nullable) = false]; } // MsgGovManageFeesResponse is a response message for the GovManageFees endpoint. diff --git a/x/exchange/events.pb.go b/x/exchange/events.pb.go index c9886e6a91..1243e6e281 100644 --- a/x/exchange/events.pb.go +++ b/x/exchange/events.pb.go @@ -482,11 +482,11 @@ func (m *EventMarketDisabled) GetUpdatedBy() string { return "" } -// EventMarketUserSettleUpdated is an event emitted when a market's self_settle option is updated. +// EventMarketUserSettleUpdated is an event emitted when a market's user_settle option is updated. type EventMarketUserSettleUpdated struct { // market_id is the numerical identifier of the market. MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` - // updated_by is the account that updated the self_settle option. + // updated_by is the account that updated the user_settle option. UpdatedBy string `protobuf:"bytes,2,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"` } diff --git a/x/exchange/genesis_test.go b/x/exchange/genesis_test.go index 4bb909a2ad..8ae61d4209 100644 --- a/x/exchange/genesis_test.go +++ b/x/exchange/genesis_test.go @@ -63,7 +63,7 @@ func TestNewGenesisState(t *testing.T) { MarketId: 2, MarketDetails: MarketDetails{Name: "Market Two"}, FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, - FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, + FeeBuyerSettlementRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, AccessGrants: []AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, ReqAttrCreateBid: []string{"just.some.attr"}, }, @@ -81,7 +81,7 @@ func TestNewGenesisState(t *testing.T) { MarketId: 2, MarketDetails: MarketDetails{Name: "Market Two"}, FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, - FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, + FeeBuyerSettlementRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, AccessGrants: []AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, ReqAttrCreateBid: []string{"just.some.attr"}, }, @@ -144,7 +144,7 @@ func TestNewGenesisState(t *testing.T) { MarketId: 2, MarketDetails: MarketDetails{Name: "Market Two"}, FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, - FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, + FeeBuyerSettlementRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, AccessGrants: []AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, ReqAttrCreateBid: []string{"just.some.attr"}, }, @@ -182,7 +182,7 @@ func TestNewGenesisState(t *testing.T) { MarketId: 2, MarketDetails: MarketDetails{Name: "Market Two"}, FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, - FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, + FeeBuyerSettlementRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, AccessGrants: []AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, ReqAttrCreateBid: []string{"just.some.attr"}, }, diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 689c0851b3..4b844da8f1 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -27,12 +27,12 @@ import ( // // Market Create-Ask Flat Fee: 0x01 | | 0x00 | => (string) // Market Create-Bid Flat Fee: 0x01 | | 0x01 | => (string) -// Market Settlement Seller Flat Fee: 0x01 | | 0x02 | => (string) -// Market Settlement Seller Fee Ratio: 0x01 | | 0x03 | | 0x00 | => comma-separated price and fee amount (string). -// Market Settlement Buyer Flat Fee: 0x01 | | 0x04 | => (string) -// Market Settlement Buyer Fee Ratio: 0x01 | | 0x05 | | 0x00 | => comma-separated price and fee amount (string). +// Market Seller Settlement Flat Fee: 0x01 | | 0x02 | => (string) +// Market Seller Settlement Fee Ratio: 0x01 | | 0x03 | | 0x00 | => comma-separated price and fee amount (string). +// Market Buyer Settlement Flat Fee: 0x01 | | 0x04 | => (string) +// Market Buyer Settlement Fee Ratio: 0x01 | | 0x05 | | 0x00 | => comma-separated price and fee amount (string). // Market inactive indicator: 0x01 | | 0x06 => nil -// Market self-settle indicator: 0x01 | | 0x07 => nil +// Market user-settle indicator: 0x01 | | 0x07 => nil // Market permissions: 0x01 | | 0x08 | |
| => nil // Market Required Attributes: 0x01 | | 0x09 | => comma-separated list of required attribute entries. // @@ -76,18 +76,18 @@ const ( MarketKeyTypeCreateAskFlat = byte(0x00) // MarketKeyTypeCreateBidFlat is the market-specific type byte for the create-bid flat fees. MarketKeyTypeCreateBidFlat = byte(0x01) - // MarketKeyTypeSettlementSellerFlat is the market-specific type byte for the seller settlement flat fees. - MarketKeyTypeSettlementSellerFlat = byte(0x02) - // MarketKeyTypeSettlementSellerRatio is the market-specific type byte for the seller settlement ratios. - MarketKeyTypeSettlementSellerRatio = byte(0x03) - // MarketKeyTypeSettlementBuyerFlat is the market-specific type byte for the buyer settlement flat fees. - MarketKeyTypeSettlementBuyerFlat = byte(0x04) - // MarketKeyTypeSettlementBuyerRatio is the market-specific type byte for the buyer settlement ratios. - MarketKeyTypeSettlementBuyerRatio = byte(0x05) + // MarketKeyTypeSellerSettlementFlat is the market-specific type byte for the seller settlement flat fees. + MarketKeyTypeSellerSettlementFlat = byte(0x02) + // MarketKeyTypeSellerSettlementRatio is the market-specific type byte for the seller settlement ratios. + MarketKeyTypeSellerSettlementRatio = byte(0x03) + // MarketKeyTypeBuyerSettlementFlat is the market-specific type byte for the buyer settlement flat fees. + MarketKeyTypeBuyerSettlementFlat = byte(0x04) + // MarketKeyTypeBuyerSettlementRatio is the market-specific type byte for the buyer settlement ratios. + MarketKeyTypeBuyerSettlementRatio = byte(0x05) // MarketKeyTypeInactive is the market-specific type byte for the inactive indicators. MarketKeyTypeInactive = byte(0x06) - // MarketKeyTypeSelfSettle is the market-specific type byte for the self-settle indicators. - MarketKeyTypeSelfSettle = byte(0x07) + // MarketKeyTypeUserSettle is the market-specific type byte for the user-settle indicators. + MarketKeyTypeUserSettle = byte(0x07) // MarketKeyTypePermissions is the market-specific type byte for the market permissions. MarketKeyTypePermissions = byte(0x08) // MarketKeyTypeReqAttr is the market-specific type byte for the market's required attributes lists. @@ -213,72 +213,72 @@ func MakeKeyMarketCreateBidFlatFee(marketID uint32, denom string) []byte { return rv } -// marketKeyPrefixSettlementSellerFlatFee creates the key prefix for a market's settlement seller flat fees with extra capacity for the rest. -func marketKeyPrefixSettlementSellerFlatFee(marketID uint32, extraCap int) []byte { - return keyPrefixMarketType(marketID, MarketKeyTypeSettlementSellerFlat, extraCap) +// marketKeyPrefixSellerSettlementFlatFee creates the key prefix for a market's seller settlement flat fees with extra capacity for the rest. +func marketKeyPrefixSellerSettlementFlatFee(marketID uint32, extraCap int) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypeSellerSettlementFlat, extraCap) } -// MakeKeyPrefixMarketSettlementSellerFlatFee creates the key prefix for a market's settlement seller flat fees. -func MakeKeyPrefixMarketSettlementSellerFlatFee(marketID uint32) []byte { - return marketKeyPrefixSettlementSellerFlatFee(marketID, 0) +// MakeKeyPrefixMarketSellerSettlementFlatFee creates the key prefix for a market's seller settlement flat fees. +func MakeKeyPrefixMarketSellerSettlementFlatFee(marketID uint32) []byte { + return marketKeyPrefixSellerSettlementFlatFee(marketID, 0) } -// MakeKeyMarketSettlementSellerFlatFee creates the key for a market's settlement seller flat fee with the given denom. -func MakeKeyMarketSettlementSellerFlatFee(marketID uint32, denom string) []byte { - rv := marketKeyPrefixSettlementSellerFlatFee(marketID, len(denom)) +// MakeKeyMarketSellerSettlementFlatFee creates the key for a market's seller settlement flat fee with the given denom. +func MakeKeyMarketSellerSettlementFlatFee(marketID uint32, denom string) []byte { + rv := marketKeyPrefixSellerSettlementFlatFee(marketID, len(denom)) rv = append(rv, denom...) return rv } -// marketKeyPrefixSettlementSellerRatio creates the key prefix for a market's settlement seller ratios with extra capacity for the rest. -func marketKeyPrefixSettlementSellerRatio(marketID uint32, extraCap int) []byte { - return keyPrefixMarketType(marketID, MarketKeyTypeSettlementSellerRatio, extraCap) +// marketKeyPrefixSellerSettlementRatio creates the key prefix for a market's seller settlement ratios with extra capacity for the rest. +func marketKeyPrefixSellerSettlementRatio(marketID uint32, extraCap int) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypeSellerSettlementRatio, extraCap) } -// MakeKeyPrefixMarketSettlementSellerRatio creates the key prefix for a market's settlement seller fee ratios. -func MakeKeyPrefixMarketSettlementSellerRatio(marketID uint32) []byte { - return marketKeyPrefixSettlementSellerRatio(marketID, 0) +// MakeKeyPrefixMarketSellerSettlementRatio creates the key prefix for a market's seller settlement fee ratios. +func MakeKeyPrefixMarketSellerSettlementRatio(marketID uint32) []byte { + return marketKeyPrefixSellerSettlementRatio(marketID, 0) } -// MakeKeyMarketSettlementSellerRatio creates the key to use for the given settlement seller fee ratio in the given market. -func MakeKeyMarketSettlementSellerRatio(marketID uint32, ratio exchange.FeeRatio) []byte { - rv := marketKeyPrefixSettlementSellerRatio(marketID, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) +// MakeKeyMarketSellerSettlementRatio creates the key to use for the given seller settlement fee ratio in the given market. +func MakeKeyMarketSellerSettlementRatio(marketID uint32, ratio exchange.FeeRatio) []byte { + rv := marketKeyPrefixSellerSettlementRatio(marketID, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) rv = append(rv, ratio.Price.Denom...) rv = append(rv, 0x00) rv = append(rv, ratio.Fee.Denom...) return rv } -// marketKeyPrefixSettlementBuyerFlatFee creates the key prefix for a market's settlement buyer flat fees with extra capacity for the rest. -func marketKeyPrefixSettlementBuyerFlatFee(marketID uint32, extraCap int) []byte { - return keyPrefixMarketType(marketID, MarketKeyTypeSettlementBuyerFlat, extraCap) +// marketKeyPrefixBuyerSettlementFlatFee creates the key prefix for a market's buyer settlement flat fees with extra capacity for the rest. +func marketKeyPrefixBuyerSettlementFlatFee(marketID uint32, extraCap int) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypeBuyerSettlementFlat, extraCap) } -// MakeKeyPrefixMarketSettlementBuyerFlatFee creates the key prefix for a market's settlement buyer flat fees. -func MakeKeyPrefixMarketSettlementBuyerFlatFee(marketID uint32) []byte { - return marketKeyPrefixSettlementBuyerFlatFee(marketID, 0) +// MakeKeyPrefixMarketBuyerSettlementFlatFee creates the key prefix for a market's buyer settlement flat fees. +func MakeKeyPrefixMarketBuyerSettlementFlatFee(marketID uint32) []byte { + return marketKeyPrefixBuyerSettlementFlatFee(marketID, 0) } -// MakeKeyMarketSettlementBuyerFlatFee creates the key for a market's settlement buyer flat fee with the given denom. -func MakeKeyMarketSettlementBuyerFlatFee(marketID uint32, denom string) []byte { - rv := marketKeyPrefixSettlementBuyerFlatFee(marketID, len(denom)) +// MakeKeyMarketBuyerSettlementFlatFee creates the key for a market's buyer settlement flat fee with the given denom. +func MakeKeyMarketBuyerSettlementFlatFee(marketID uint32, denom string) []byte { + rv := marketKeyPrefixBuyerSettlementFlatFee(marketID, len(denom)) rv = append(rv, denom...) return rv } -// marketKeyPrefixSettlementBuyerRatio creates the key prefix for a market's settlement buyer ratios with extra capacity for the rest. -func marketKeyPrefixSettlementBuyerRatio(marketID uint32, extraCap int) []byte { - return keyPrefixMarketType(marketID, MarketKeyTypeSettlementBuyerRatio, extraCap) +// marketKeyPrefixBuyerSettlementRatio creates the key prefix for a market's buyer settlement ratios with extra capacity for the rest. +func marketKeyPrefixBuyerSettlementRatio(marketID uint32, extraCap int) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypeBuyerSettlementRatio, extraCap) } -// MakeKeyPrefixMarketSettlementBuyerRatio creates the key prefix for a market's settlement buyer fee ratios. -func MakeKeyPrefixMarketSettlementBuyerRatio(marketID uint32) []byte { - return marketKeyPrefixSettlementBuyerRatio(marketID, 0) +// MakeKeyPrefixMarketBuyerSettlementRatio creates the key prefix for a market's buyer settlement fee ratios. +func MakeKeyPrefixMarketBuyerSettlementRatio(marketID uint32) []byte { + return marketKeyPrefixBuyerSettlementRatio(marketID, 0) } -// MakeKeyMarketSettlementBuyerRatio creates the key to use for the given settlement buyer fee ratio in the given market. -func MakeKeyMarketSettlementBuyerRatio(marketID uint32, ratio exchange.FeeRatio) []byte { - rv := marketKeyPrefixSettlementBuyerRatio(marketID, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) +// MakeKeyMarketBuyerSettlementRatio creates the key to use for the given buyer settlement fee ratio in the given market. +func MakeKeyMarketBuyerSettlementRatio(marketID uint32, ratio exchange.FeeRatio) []byte { + rv := marketKeyPrefixBuyerSettlementRatio(marketID, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) rv = append(rv, ratio.Price.Denom...) rv = append(rv, 0x00) rv = append(rv, ratio.Fee.Denom...) @@ -290,9 +290,9 @@ func MakeKeyMarketInactive(marketID uint32) []byte { return keyPrefixMarketType(marketID, MarketKeyTypeInactive, 0) } -// MakeKeyMarketSelfSettle creates the key to use to indicate that a market allows self-settlement. -func MakeKeyMarketSelfSettle(marketID uint32) []byte { - return keyPrefixMarketType(marketID, MarketKeyTypeSelfSettle, 0) +// MakeKeyMarketUserSettle creates the key to use to indicate that a market allows settlement by users. +func MakeKeyMarketUserSettle(marketID uint32) []byte { + return keyPrefixMarketType(marketID, MarketKeyTypeUserSettle, 0) } // marketKeyPrefixPermissions creates the key prefix for a market's permissions with extra capacity for the rest. diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 22e81c2d58..703a1a67d1 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -92,12 +92,12 @@ func TestKeyTypeUniqueness(t *testing.T) { types: []byteEntry{ {name: "MarketKeyTypeCreateAskFlat", value: keeper.MarketKeyTypeCreateAskFlat}, {name: "MarketKeyTypeCreateBidFlat", value: keeper.MarketKeyTypeCreateBidFlat}, - {name: "MarketKeyTypeSettlementSellerFlat", value: keeper.MarketKeyTypeSettlementSellerFlat}, - {name: "MarketKeyTypeSettlementSellerRatio", value: keeper.MarketKeyTypeSettlementSellerRatio}, - {name: "MarketKeyTypeSettlementBuyerFlat", value: keeper.MarketKeyTypeSettlementBuyerFlat}, - {name: "MarketKeyTypeSettlementBuyerRatio", value: keeper.MarketKeyTypeSettlementBuyerRatio}, + {name: "MarketKeyTypeSellerSettlementFlat", value: keeper.MarketKeyTypeSellerSettlementFlat}, + {name: "MarketKeyTypeSellerSettlementRatio", value: keeper.MarketKeyTypeSellerSettlementRatio}, + {name: "MarketKeyTypeBuyerSettlementFlat", value: keeper.MarketKeyTypeBuyerSettlementFlat}, + {name: "MarketKeyTypeBuyerSettlementRatio", value: keeper.MarketKeyTypeBuyerSettlementRatio}, {name: "MarketKeyTypeInactive", value: keeper.MarketKeyTypeInactive}, - {name: "MarketKeyTypeSelfSettle", value: keeper.MarketKeyTypeSelfSettle}, + {name: "MarketKeyTypeUserSettle", value: keeper.MarketKeyTypeUserSettle}, {name: "MarketKeyTypePermissions", value: keeper.MarketKeyTypePermissions}, {name: "MarketKeyTypeReqAttr", value: keeper.MarketKeyTypeReqAttr}, }, @@ -541,8 +541,8 @@ func TestMakeKeyMarketCreateBidFlatFee(t *testing.T) { } } -func TestMakeKeyPrefixMarketSettlementSellerFlatFee(t *testing.T) { - marketTypeByte := keeper.MarketKeyTypeSettlementSellerFlat +func TestMakeKeyPrefixMarketSellerSettlementFlatFee(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSellerSettlementFlat tests := []struct { name string @@ -595,20 +595,20 @@ func TestMakeKeyPrefixMarketSettlementSellerFlatFee(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarketSettlementSellerFlatFee(tc.marketID) + return keeper.MakeKeyPrefixMarketSellerSettlementFlatFee(tc.marketID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, }, } - checkKey(t, ktc, "MakeKeyPrefixMarketSettlementSellerFlatFee(%d)", tc.marketID) + checkKey(t, ktc, "MakeKeyPrefixMarketSellerSettlementFlatFee(%d)", tc.marketID) }) } } -func TestMakeKeyMarketSettlementSellerFlatFee(t *testing.T) { - marketTypeByte := keeper.MarketKeyTypeSettlementSellerFlat +func TestMakeKeyMarketSellerSettlementFlatFee(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSellerSettlementFlat tests := []struct { name string @@ -676,7 +676,7 @@ func TestMakeKeyMarketSettlementSellerFlatFee(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyMarketSettlementSellerFlatFee(tc.marketID, tc.denom) + return keeper.MakeKeyMarketSellerSettlementFlatFee(tc.marketID, tc.denom) }, expected: tc.expected, expPrefixes: []expectedPrefix{ @@ -685,18 +685,18 @@ func TestMakeKeyMarketSettlementSellerFlatFee(t *testing.T) { value: keeper.MakeKeyPrefixMarket(tc.marketID), }, { - name: "MakeKeyPrefixMarketSettlementSellerFlatFee", - value: keeper.MakeKeyPrefixMarketSettlementSellerFlatFee(tc.marketID), + name: "MakeKeyPrefixMarketSellerSettlementFlatFee", + value: keeper.MakeKeyPrefixMarketSellerSettlementFlatFee(tc.marketID), }, }, } - checkKey(t, ktc, "MakeKeyMarketSettlementSellerFlatFee(%d)", tc.marketID) + checkKey(t, ktc, "MakeKeyMarketSellerSettlementFlatFee(%d)", tc.marketID) }) } } -func TestMakeKeyPrefixMarketSettlementSellerRatio(t *testing.T) { - marketTypeByte := keeper.MarketKeyTypeSettlementSellerRatio +func TestMakeKeyPrefixMarketSellerSettlementRatio(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSellerSettlementRatio tests := []struct { name string @@ -749,20 +749,20 @@ func TestMakeKeyPrefixMarketSettlementSellerRatio(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarketSettlementSellerRatio(tc.marketID) + return keeper.MakeKeyPrefixMarketSellerSettlementRatio(tc.marketID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, }, } - checkKey(t, ktc, "MakeKeyPrefixMarketSettlementSellerRatio(%d)", tc.marketID) + checkKey(t, ktc, "MakeKeyPrefixMarketSellerSettlementRatio(%d)", tc.marketID) }) } } -func TestMakeKeyMarketSettlementSellerRatio(t *testing.T) { - marketTypeByte := keeper.MarketKeyTypeSettlementSellerRatio +func TestMakeKeyMarketSellerSettlementRatio(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeSellerSettlementRatio coin := func(denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.OneInt()} } @@ -809,7 +809,7 @@ func TestMakeKeyMarketSettlementSellerRatio(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyMarketSettlementSellerRatio(tc.marketID, tc.ratio) + return keeper.MakeKeyMarketSellerSettlementRatio(tc.marketID, tc.ratio) }, expected: tc.expected, expPrefixes: []expectedPrefix{ @@ -818,18 +818,18 @@ func TestMakeKeyMarketSettlementSellerRatio(t *testing.T) { value: keeper.MakeKeyPrefixMarket(tc.marketID), }, { - name: "MakeKeyPrefixMarketSettlementSellerRatio", - value: keeper.MakeKeyPrefixMarketSettlementSellerRatio(tc.marketID), + name: "MakeKeyPrefixMarketSellerSettlementRatio", + value: keeper.MakeKeyPrefixMarketSellerSettlementRatio(tc.marketID), }, }, } - checkKey(t, ktc, "MakeKeyMarketSettlementSellerRatio(%d)", tc.marketID) + checkKey(t, ktc, "MakeKeyMarketSellerSettlementRatio(%d)", tc.marketID) }) } } -func TestMakeKeyPrefixMarketSettlementBuyerFlatFee(t *testing.T) { - marketTypeByte := keeper.MarketKeyTypeSettlementBuyerFlat +func TestMakeKeyPrefixMarketBuyerSettlementFlatFee(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeBuyerSettlementFlat tests := []struct { name string @@ -882,20 +882,20 @@ func TestMakeKeyPrefixMarketSettlementBuyerFlatFee(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarketSettlementBuyerFlatFee(tc.marketID) + return keeper.MakeKeyPrefixMarketBuyerSettlementFlatFee(tc.marketID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, }, } - checkKey(t, ktc, "MakeKeyPrefixMarketSettlementBuyerFlatFee(%d)", tc.marketID) + checkKey(t, ktc, "MakeKeyPrefixMarketBuyerSettlementFlatFee(%d)", tc.marketID) }) } } -func TestMakeKeyMarketSettlementBuyerFlatFee(t *testing.T) { - marketTypeByte := keeper.MarketKeyTypeSettlementBuyerFlat +func TestMakeKeyMarketBuyerSettlementFlatFee(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeBuyerSettlementFlat tests := []struct { name string @@ -963,7 +963,7 @@ func TestMakeKeyMarketSettlementBuyerFlatFee(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyMarketSettlementBuyerFlatFee(tc.marketID, tc.denom) + return keeper.MakeKeyMarketBuyerSettlementFlatFee(tc.marketID, tc.denom) }, expected: tc.expected, expPrefixes: []expectedPrefix{ @@ -972,18 +972,18 @@ func TestMakeKeyMarketSettlementBuyerFlatFee(t *testing.T) { value: keeper.MakeKeyPrefixMarket(tc.marketID), }, { - name: "MakeKeyPrefixMarketSettlementBuyerFlatFee", - value: keeper.MakeKeyPrefixMarketSettlementBuyerFlatFee(tc.marketID), + name: "MakeKeyPrefixMarketBuyerSettlementFlatFee", + value: keeper.MakeKeyPrefixMarketBuyerSettlementFlatFee(tc.marketID), }, }, } - checkKey(t, ktc, "MakeKeyMarketSettlementBuyerFlatFee(%d)", tc.marketID) + checkKey(t, ktc, "MakeKeyMarketBuyerSettlementFlatFee(%d)", tc.marketID) }) } } -func TestMakeKeyPrefixMarketSettlementBuyerRatio(t *testing.T) { - marketTypeByte := keeper.MarketKeyTypeSettlementBuyerRatio +func TestMakeKeyPrefixMarketBuyerSettlementRatio(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeBuyerSettlementRatio tests := []struct { name string @@ -1036,20 +1036,20 @@ func TestMakeKeyPrefixMarketSettlementBuyerRatio(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarketSettlementBuyerRatio(tc.marketID) + return keeper.MakeKeyPrefixMarketBuyerSettlementRatio(tc.marketID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, }, } - checkKey(t, ktc, "MakeKeyPrefixMarketSettlementBuyerRatio(%d)", tc.marketID) + checkKey(t, ktc, "MakeKeyPrefixMarketBuyerSettlementRatio(%d)", tc.marketID) }) } } -func TestMakeKeyMarketSettlementBuyerRatio(t *testing.T) { - marketTypeByte := keeper.MarketKeyTypeSettlementBuyerRatio +func TestMakeKeyMarketBuyerSettlementRatio(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeBuyerSettlementRatio coin := func(denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.OneInt()} } @@ -1096,7 +1096,7 @@ func TestMakeKeyMarketSettlementBuyerRatio(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyMarketSettlementBuyerRatio(tc.marketID, tc.ratio) + return keeper.MakeKeyMarketBuyerSettlementRatio(tc.marketID, tc.ratio) }, expected: tc.expected, expPrefixes: []expectedPrefix{ @@ -1105,12 +1105,12 @@ func TestMakeKeyMarketSettlementBuyerRatio(t *testing.T) { value: keeper.MakeKeyPrefixMarket(tc.marketID), }, { - name: "MakeKeyPrefixMarketSettlementBuyerRatio", - value: keeper.MakeKeyPrefixMarketSettlementBuyerRatio(tc.marketID), + name: "MakeKeyPrefixMarketBuyerSettlementRatio", + value: keeper.MakeKeyPrefixMarketBuyerSettlementRatio(tc.marketID), }, }, } - checkKey(t, ktc, "MakeKeyMarketSettlementBuyerRatio(%d)", tc.marketID) + checkKey(t, ktc, "MakeKeyMarketBuyerSettlementRatio(%d)", tc.marketID) }) } } @@ -1181,8 +1181,8 @@ func TestMakeKeyMarketInactive(t *testing.T) { } } -func TestMakeKeyMarketSelfSettle(t *testing.T) { - marketTypeByte := keeper.MarketKeyTypeSelfSettle +func TestMakeKeyMarketUserSettle(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeUserSettle tests := []struct { name string @@ -1235,14 +1235,14 @@ func TestMakeKeyMarketSelfSettle(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyMarketSelfSettle(tc.marketID) + return keeper.MakeKeyMarketUserSettle(tc.marketID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, }, } - checkKey(t, ktc, "MakeKeyMarketSelfSettle(%d)", tc.marketID) + checkKey(t, ktc, "MakeKeyMarketUserSettle(%d)", tc.marketID) }) } } diff --git a/x/exchange/market.go b/x/exchange/market.go index a1d7ddb51b..09fe4e961e 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -38,9 +38,9 @@ func (m Market) Validate() error { m.MarketDetails.Validate(), ValidateFeeOptions("create-ask flat fee", m.FeeCreateAskFlat), ValidateFeeOptions("create-bid flat fee", m.FeeCreateBidFlat), - ValidateFeeOptions("settlement seller flat fee", m.FeeSettlementSellerFlat), - ValidateFeeOptions("settlement buyer flat fee", m.FeeSettlementBuyerFlat), - ValidateFeeRatios(m.FeeSettlementSellerRatios, m.FeeSettlementBuyerRatios), + ValidateFeeOptions("seller settlement flat fee", m.FeeSellerSettlementFlat), + ValidateFeeOptions("buyer settlement flat fee", m.FeeBuyerSettlementFlat), + ValidateFeeRatios(m.FeeSellerSettlementRatios, m.FeeBuyerSettlementRatios), ValidateAccessGrants(m.AccessGrants), } diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index 42f83d11e7..33fda5366e 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -203,21 +203,21 @@ type Market struct { // Each coin entry is a separate option. When a bid is created, one of these must be paid. // If empty, no fee is required to create a bid order. FeeCreateBidFlat []types1.Coin `protobuf:"bytes,4,rep,name=fee_create_bid_flat,json=feeCreateBidFlat,proto3" json:"fee_create_bid_flat"` - // fee_settlement_seller_flat is the flat fee charged to the seller during settlement. + // fee_seller_settlement_flat is the flat fee charged to the seller during settlement. // Each coin entry is a separate option. // When an ask is settled, the seller will pay the amount in the denom that matches the price they received. - FeeSettlementSellerFlat []types1.Coin `protobuf:"bytes,5,rep,name=fee_settlement_seller_flat,json=feeSettlementSellerFlat,proto3" json:"fee_settlement_seller_flat"` - // fee_settlement_seller_ratios is the fee to charge a seller during settlement based on the price they are receiving. + FeeSellerSettlementFlat []types1.Coin `protobuf:"bytes,5,rep,name=fee_seller_settlement_flat,json=feeSellerSettlementFlat,proto3" json:"fee_seller_settlement_flat"` + // fee_seller_settlement_ratios is the fee to charge a seller during settlement based on the price they are receiving. // The price and fee denoms must be equal for each entry, and only one entry for any given denom is allowed. - FeeSettlementSellerRatios []FeeRatio `protobuf:"bytes,6,rep,name=fee_settlement_seller_ratios,json=feeSettlementSellerRatios,proto3" json:"fee_settlement_seller_ratios"` - // fee_settlement_buyer_flat is the flat fee charged to the buyer during settlement. + FeeSellerSettlementRatios []FeeRatio `protobuf:"bytes,6,rep,name=fee_seller_settlement_ratios,json=feeSellerSettlementRatios,proto3" json:"fee_seller_settlement_ratios"` + // fee_buyer_settlement_flat is the flat fee charged to the buyer during settlement. // Each coin entry is a separate option. // When a bid is created, the settlement fees provided must contain one of these. - FeeSettlementBuyerFlat []types1.Coin `protobuf:"bytes,7,rep,name=fee_settlement_buyer_flat,json=feeSettlementBuyerFlat,proto3" json:"fee_settlement_buyer_flat"` - // fee_settlement_buyer_ratios is the fee to charge a buyer during settlement based on the price they are spending. + FeeBuyerSettlementFlat []types1.Coin `protobuf:"bytes,7,rep,name=fee_buyer_settlement_flat,json=feeBuyerSettlementFlat,proto3" json:"fee_buyer_settlement_flat"` + // fee_buyer_settlement_ratios is the fee to charge a buyer during settlement based on the price they are spending. // The price and fee denoms do not have to equal. Multiple entries for any given price or fee denom are allowed, but // each price denom to fee denom pair can only have one entry. - FeeSettlementBuyerRatios []FeeRatio `protobuf:"bytes,8,rep,name=fee_settlement_buyer_ratios,json=feeSettlementBuyerRatios,proto3" json:"fee_settlement_buyer_ratios"` + FeeBuyerSettlementRatios []FeeRatio `protobuf:"bytes,8,rep,name=fee_buyer_settlement_ratios,json=feeBuyerSettlementRatios,proto3" json:"fee_buyer_settlement_ratios"` // accepting_orders is whether this market is allowing orders to be created for it. AcceptingOrders bool `protobuf:"varint,9,opt,name=accepting_orders,json=acceptingOrders,proto3" json:"accepting_orders,omitempty"` // allow_user_settlement is whether this market allows users to initiate their own settlements. @@ -303,30 +303,30 @@ func (m *Market) GetFeeCreateBidFlat() []types1.Coin { return nil } -func (m *Market) GetFeeSettlementSellerFlat() []types1.Coin { +func (m *Market) GetFeeSellerSettlementFlat() []types1.Coin { if m != nil { - return m.FeeSettlementSellerFlat + return m.FeeSellerSettlementFlat } return nil } -func (m *Market) GetFeeSettlementSellerRatios() []FeeRatio { +func (m *Market) GetFeeSellerSettlementRatios() []FeeRatio { if m != nil { - return m.FeeSettlementSellerRatios + return m.FeeSellerSettlementRatios } return nil } -func (m *Market) GetFeeSettlementBuyerFlat() []types1.Coin { +func (m *Market) GetFeeBuyerSettlementFlat() []types1.Coin { if m != nil { - return m.FeeSettlementBuyerFlat + return m.FeeBuyerSettlementFlat } return nil } -func (m *Market) GetFeeSettlementBuyerRatios() []FeeRatio { +func (m *Market) GetFeeBuyerSettlementRatios() []FeeRatio { if m != nil { - return m.FeeSettlementBuyerRatios + return m.FeeBuyerSettlementRatios } return nil } @@ -490,69 +490,69 @@ func init() { } var fileDescriptor_d5cf198f1dd7e167 = []byte{ - // 982 bytes of a gzipped FileDescriptorProto + // 979 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x41, 0x4f, 0x1b, 0x47, - 0x14, 0xf6, 0x62, 0x03, 0xf6, 0x18, 0x88, 0x3b, 0x24, 0x74, 0x6d, 0x2a, 0x7b, 0xeb, 0x28, 0x92, - 0xd3, 0x0a, 0x5b, 0x50, 0xf5, 0xc2, 0xcd, 0x06, 0xd3, 0x5a, 0x4a, 0x08, 0x5a, 0xdb, 0x8a, 0x14, - 0x55, 0xda, 0xce, 0xee, 0x3e, 0x9b, 0x11, 0xeb, 0x5d, 0x67, 0x66, 0x16, 0x92, 0xde, 0x7a, 0x6a, - 0xc5, 0xa9, 0xc7, 0x5e, 0x90, 0xd2, 0x7f, 0xd0, 0x43, 0x7f, 0x44, 0x8e, 0xa8, 0xa7, 0x9e, 0x50, - 0x05, 0xd7, 0x9e, 0xfa, 0x0b, 0xaa, 0x9d, 0x59, 0xec, 0x0d, 0x35, 0x22, 0x51, 0x6e, 0xfb, 0xde, - 0xf7, 0xbd, 0xef, 0x7d, 0xef, 0xcd, 0x30, 0x18, 0x3d, 0x1c, 0xb3, 0xe0, 0x18, 0x7c, 0xe2, 0x3b, - 0xd0, 0x80, 0x57, 0xce, 0x21, 0xf1, 0x87, 0xd0, 0x38, 0xde, 0x6c, 0x8c, 0x08, 0x3b, 0x02, 0x51, - 0x1f, 0xb3, 0x40, 0x04, 0x78, 0x6d, 0x4a, 0xaa, 0x5f, 0x93, 0xea, 0xc7, 0x9b, 0xa5, 0xb2, 0x13, - 0xf0, 0x51, 0xc0, 0x1b, 0x24, 0x14, 0x87, 0x8d, 0xe3, 0x4d, 0x1b, 0x04, 0xd9, 0x94, 0x81, 0xaa, - 0x9b, 0xe0, 0x36, 0xe1, 0x30, 0xc1, 0x9d, 0x80, 0xfa, 0x31, 0x5e, 0x54, 0xb8, 0x25, 0xa3, 0x86, - 0x0a, 0x62, 0xe8, 0xfe, 0x30, 0x18, 0x06, 0x2a, 0x1f, 0x7d, 0xa9, 0x6c, 0xf5, 0x1f, 0x0d, 0x2d, - 0x3f, 0x95, 0xce, 0x9a, 0x8e, 0x13, 0x84, 0xbe, 0xc0, 0xdf, 0xa3, 0xa5, 0x48, 0xdd, 0x22, 0x2a, - 0xd6, 0x35, 0x43, 0xab, 0xe5, 0xb7, 0x8c, 0x7a, 0x2c, 0x26, 0xcd, 0xc4, 0x9d, 0xeb, 0x2d, 0xc2, - 0x21, 0xae, 0x6b, 0xad, 0x9f, 0x5f, 0x54, 0xb4, 0x7f, 0x2f, 0x2a, 0xab, 0xaf, 0xc9, 0xc8, 0xdb, - 0xae, 0x26, 0x35, 0xaa, 0x66, 0xde, 0x9e, 0x32, 0xf1, 0x3a, 0xca, 0xa9, 0x65, 0x58, 0xd4, 0xd5, - 0xe7, 0x0c, 0xad, 0xb6, 0x6c, 0x66, 0x55, 0xa2, 0xe3, 0x62, 0x13, 0xad, 0xc4, 0xa0, 0x0b, 0x82, - 0x50, 0x8f, 0xeb, 0x69, 0x69, 0xe0, 0x51, 0x7d, 0xf6, 0xca, 0xea, 0xca, 0xfd, 0xae, 0x22, 0xb7, - 0x32, 0x6f, 0x2f, 0x2a, 0x29, 0x73, 0x79, 0x94, 0x4c, 0x6e, 0x67, 0x7f, 0x7e, 0x53, 0x49, 0xfd, - 0xfa, 0xa6, 0x92, 0xaa, 0xfe, 0x38, 0x19, 0x37, 0xc6, 0x30, 0x46, 0x19, 0x9f, 0x8c, 0x40, 0x8e, - 0x99, 0x33, 0xe5, 0x37, 0x36, 0x50, 0xde, 0x05, 0xee, 0x30, 0x3a, 0x16, 0x34, 0xf0, 0xa5, 0xc5, - 0x9c, 0x99, 0x4c, 0xe1, 0x0a, 0xca, 0x9f, 0x80, 0xcd, 0xa9, 0x00, 0x2b, 0x64, 0x9e, 0xb4, 0x98, - 0x33, 0x51, 0x9c, 0xea, 0x33, 0x0f, 0x17, 0x51, 0x96, 0x3a, 0x81, 0x6f, 0x85, 0x8c, 0xea, 0x19, - 0x89, 0x2e, 0x46, 0x71, 0x9f, 0xd1, 0xea, 0xef, 0x8b, 0x68, 0x41, 0x79, 0x78, 0x77, 0x13, 0xda, - 0x9d, 0x9b, 0x98, 0xfb, 0xd8, 0x4d, 0xe0, 0x7d, 0xb4, 0x3a, 0x00, 0xb0, 0x1c, 0x06, 0x44, 0x80, - 0x45, 0xf8, 0x91, 0x35, 0xf0, 0x88, 0xd0, 0xd3, 0x46, 0xba, 0x96, 0xdf, 0x2a, 0x5e, 0x9f, 0x71, - 0x74, 0x58, 0x93, 0x33, 0xde, 0x09, 0xa8, 0x1f, 0x8b, 0x15, 0x06, 0x00, 0x3b, 0xb2, 0xb4, 0xc9, - 0x8f, 0xf6, 0x3c, 0x22, 0x6e, 0xe8, 0xd9, 0xd4, 0x55, 0x7a, 0x99, 0x0f, 0xd5, 0x6b, 0x51, 0x57, - 0xea, 0x7d, 0x87, 0x4a, 0x91, 0x1e, 0x07, 0x21, 0x3c, 0x18, 0x81, 0x2f, 0x2c, 0x0e, 0x9e, 0x07, - 0x4c, 0xc9, 0xce, 0xbf, 0x9f, 0xec, 0xa7, 0x03, 0x80, 0xee, 0x44, 0xa1, 0x2b, 0x05, 0xa4, 0xfa, - 0x10, 0x7d, 0x36, 0x5b, 0x9d, 0x11, 0x41, 0x03, 0xae, 0x2f, 0x48, 0x7d, 0xe3, 0xb6, 0xfd, 0xee, - 0x01, 0x98, 0x11, 0x31, 0x6e, 0x53, 0x9c, 0xd1, 0x46, 0xe2, 0x1c, 0xbf, 0x40, 0xc5, 0x1b, 0x8d, - 0xec, 0xf0, 0xf5, 0xf5, 0x14, 0x8b, 0xef, 0x37, 0xc5, 0xda, 0x3b, 0xf2, 0xad, 0xa8, 0x5e, 0x0e, - 0x01, 0x68, 0x7d, 0xa6, 0x76, 0x3c, 0x43, 0xf6, 0x83, 0x66, 0xd0, 0xff, 0xdf, 0x24, 0x1e, 0xe1, - 0x31, 0x2a, 0x10, 0xc7, 0x81, 0xb1, 0xa0, 0xfe, 0xd0, 0x0a, 0x98, 0x0b, 0x8c, 0xeb, 0x39, 0x43, - 0xab, 0x65, 0xcd, 0x7b, 0x93, 0xfc, 0x33, 0x99, 0xc6, 0x5b, 0xe8, 0x01, 0xf1, 0xbc, 0xe0, 0xc4, - 0x0a, 0x39, 0xb0, 0x84, 0x31, 0x1d, 0x49, 0xfe, 0xaa, 0x04, 0xfb, 0x1c, 0xd8, 0xb4, 0x13, 0xde, - 0x47, 0xcb, 0x91, 0x0c, 0xe7, 0xd6, 0x90, 0x11, 0x5f, 0x70, 0x3d, 0x2f, 0x7d, 0x3f, 0xbc, 0xcd, - 0x77, 0x53, 0x92, 0xbf, 0x89, 0xb8, 0xb1, 0xf5, 0x25, 0x32, 0x4d, 0x71, 0xbc, 0x81, 0x56, 0x19, - 0xbc, 0xb4, 0x88, 0x10, 0x2c, 0x71, 0xbb, 0xf5, 0x25, 0x23, 0x5d, 0xcb, 0x99, 0x05, 0x06, 0x2f, - 0x9b, 0x42, 0xb0, 0xc9, 0xdd, 0x9d, 0x45, 0xb7, 0xa9, 0xab, 0x2f, 0xcf, 0xa0, 0xb7, 0xa8, 0x5b, - 0xfd, 0x01, 0x65, 0xaf, 0x17, 0x87, 0xbf, 0x46, 0xf3, 0x63, 0x46, 0x1d, 0x88, 0x1f, 0xc6, 0x3b, - 0xcf, 0x51, 0xb1, 0xf1, 0x26, 0x4a, 0x0f, 0x00, 0xe2, 0x3f, 0xe1, 0x3b, 0x8b, 0x22, 0xee, 0x76, - 0x46, 0x3e, 0x59, 0x3f, 0x69, 0x28, 0x9f, 0x98, 0x1e, 0x6f, 0xa1, 0x45, 0xe2, 0xba, 0x0c, 0x38, - 0x57, 0x6f, 0x56, 0x4b, 0xff, 0xf3, 0x8f, 0x8d, 0xfb, 0xb1, 0x5e, 0x53, 0x21, 0x5d, 0xc1, 0xa8, - 0x3f, 0x34, 0xaf, 0x89, 0x78, 0x17, 0xe5, 0xc7, 0xc0, 0x46, 0x94, 0x73, 0x1a, 0xf8, 0xd1, 0x3b, - 0x92, 0xae, 0xad, 0x6c, 0x55, 0x6f, 0xdb, 0xf5, 0xc1, 0x84, 0x6a, 0x26, 0xcb, 0xbe, 0xf8, 0x6d, - 0x0e, 0xa1, 0x29, 0x86, 0xbf, 0x44, 0x6b, 0x07, 0x6d, 0xf3, 0x69, 0xa7, 0xdb, 0xed, 0x3c, 0xdb, - 0xb7, 0xfa, 0xfb, 0xdd, 0x83, 0xf6, 0x4e, 0x67, 0xaf, 0xd3, 0xde, 0x2d, 0xa4, 0x4a, 0xf7, 0x4e, - 0xcf, 0x8c, 0x7c, 0xe8, 0xf3, 0x31, 0x38, 0x74, 0x40, 0xc1, 0xc5, 0x9f, 0xa3, 0x4f, 0x12, 0xe4, - 0x6e, 0xbb, 0xd7, 0x7b, 0xd2, 0x2e, 0x68, 0x25, 0x74, 0x7a, 0x66, 0x2c, 0xa8, 0x1b, 0x73, 0x83, - 0xb2, 0xd3, 0xdc, 0xdf, 0x69, 0x3f, 0x29, 0xcc, 0x29, 0x8a, 0x13, 0x99, 0xf4, 0xf0, 0x23, 0xb4, - 0x9a, 0xa0, 0x3c, 0xef, 0xf4, 0xbe, 0xdd, 0x35, 0x9b, 0xcf, 0x0b, 0xe9, 0xd2, 0xd2, 0xe9, 0x99, - 0x91, 0x3d, 0xa1, 0xe2, 0xd0, 0x65, 0xe4, 0xe4, 0x86, 0x52, 0xff, 0x60, 0xb7, 0xd9, 0x6b, 0x17, - 0x32, 0x4a, 0x29, 0x1c, 0xbb, 0x44, 0xc0, 0x0d, 0xf3, 0xd3, 0xcf, 0x6e, 0x61, 0x5e, 0x99, 0x4f, - 0x0c, 0x8e, 0x1f, 0xa3, 0x07, 0x09, 0x72, 0xb3, 0xd7, 0x33, 0x3b, 0xad, 0x7e, 0xaf, 0xdd, 0x2d, - 0x2c, 0x94, 0x56, 0x4e, 0xcf, 0x0c, 0x14, 0x5d, 0x23, 0x6a, 0x87, 0x02, 0x78, 0x0b, 0xde, 0x5e, - 0x96, 0xb5, 0xf3, 0xcb, 0xb2, 0xf6, 0xf7, 0x65, 0x59, 0xfb, 0xe5, 0xaa, 0x9c, 0x3a, 0xbf, 0x2a, - 0xa7, 0xfe, 0xba, 0x2a, 0xa7, 0x50, 0x91, 0x06, 0xb7, 0x2c, 0xfc, 0x40, 0x7b, 0x51, 0x1f, 0x52, - 0x71, 0x18, 0xda, 0x75, 0x27, 0x18, 0x35, 0xa6, 0xa4, 0x0d, 0x1a, 0x24, 0xa2, 0xc6, 0xab, 0xc9, - 0xef, 0x09, 0x7b, 0x41, 0xfe, 0xf7, 0xfe, 0xea, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd5, 0xe1, - 0x8a, 0xcc, 0x6d, 0x08, 0x00, 0x00, + 0x14, 0xf6, 0x62, 0x03, 0xf6, 0x18, 0x88, 0x3b, 0x24, 0x74, 0x31, 0x95, 0xbd, 0x75, 0x14, 0xc9, + 0x69, 0x85, 0x2d, 0xa8, 0x7a, 0xe1, 0x66, 0x83, 0x69, 0x2d, 0x25, 0x04, 0xad, 0x6d, 0x45, 0x8a, + 0x2a, 0x6d, 0x67, 0x77, 0x9f, 0xcd, 0x88, 0xf5, 0xae, 0x33, 0x33, 0x0b, 0x49, 0x6f, 0x3d, 0xb5, + 0xe2, 0xd4, 0x63, 0x2f, 0x48, 0xe9, 0x3f, 0xe8, 0xa1, 0x3f, 0x22, 0x47, 0xd4, 0x53, 0x4f, 0xa8, + 0x82, 0x6b, 0x4f, 0xfd, 0x05, 0xd5, 0xce, 0x2c, 0xf6, 0xc6, 0x35, 0x22, 0x51, 0x6e, 0xf3, 0xde, + 0xf7, 0xcd, 0xf7, 0xbe, 0xf7, 0x66, 0x3c, 0x5e, 0xf4, 0x70, 0xc4, 0x82, 0x13, 0xf0, 0x89, 0xef, + 0x40, 0x1d, 0x5e, 0x39, 0x47, 0xc4, 0x1f, 0x40, 0xfd, 0x64, 0xab, 0x3e, 0x24, 0xec, 0x18, 0x44, + 0x6d, 0xc4, 0x02, 0x11, 0xe0, 0xb5, 0x09, 0xa9, 0x76, 0x43, 0xaa, 0x9d, 0x6c, 0x15, 0x4b, 0x4e, + 0xc0, 0x87, 0x01, 0xaf, 0x93, 0x50, 0x1c, 0xd5, 0x4f, 0xb6, 0x6c, 0x10, 0x64, 0x4b, 0x06, 0x6a, + 0xdf, 0x18, 0xb7, 0x09, 0x87, 0x31, 0xee, 0x04, 0xd4, 0x8f, 0xf1, 0x75, 0x85, 0x5b, 0x32, 0xaa, + 0xab, 0x20, 0x86, 0xee, 0x0f, 0x82, 0x41, 0xa0, 0xf2, 0xd1, 0x4a, 0x65, 0x2b, 0xff, 0x68, 0x68, + 0xf9, 0xa9, 0x74, 0xd6, 0x70, 0x9c, 0x20, 0xf4, 0x05, 0xfe, 0x1e, 0x2d, 0x45, 0xea, 0x16, 0x51, + 0xb1, 0xae, 0x19, 0x5a, 0x35, 0xbf, 0x6d, 0xd4, 0x62, 0x31, 0x69, 0x26, 0xae, 0x5c, 0x6b, 0x12, + 0x0e, 0xf1, 0xbe, 0xe6, 0xc6, 0xc5, 0x65, 0x59, 0xfb, 0xf7, 0xb2, 0xbc, 0xfa, 0x9a, 0x0c, 0xbd, + 0x9d, 0x4a, 0x52, 0xa3, 0x62, 0xe6, 0xed, 0x09, 0x13, 0x6f, 0xa0, 0x9c, 0x1a, 0x86, 0x45, 0x5d, + 0x7d, 0xce, 0xd0, 0xaa, 0xcb, 0x66, 0x56, 0x25, 0xda, 0x2e, 0x36, 0xd1, 0x4a, 0x0c, 0xba, 0x20, + 0x08, 0xf5, 0xb8, 0x9e, 0x96, 0x06, 0x1e, 0xd5, 0x66, 0x8f, 0xac, 0xa6, 0xdc, 0xef, 0x29, 0x72, + 0x33, 0xf3, 0xf6, 0xb2, 0x9c, 0x32, 0x97, 0x87, 0xc9, 0xe4, 0x4e, 0xf6, 0xe7, 0x37, 0xe5, 0xd4, + 0xaf, 0x6f, 0xca, 0xa9, 0xca, 0x8f, 0xe3, 0x76, 0x63, 0x0c, 0x63, 0x94, 0xf1, 0xc9, 0x10, 0x64, + 0x9b, 0x39, 0x53, 0xae, 0xb1, 0x81, 0xf2, 0x2e, 0x70, 0x87, 0xd1, 0x91, 0xa0, 0x81, 0x2f, 0x2d, + 0xe6, 0xcc, 0x64, 0x0a, 0x97, 0x51, 0xfe, 0x14, 0x6c, 0x4e, 0x05, 0x58, 0x21, 0xf3, 0xa4, 0xc5, + 0x9c, 0x89, 0xe2, 0x54, 0x8f, 0x79, 0x78, 0x1d, 0x65, 0xa9, 0x13, 0xf8, 0x56, 0xc8, 0xa8, 0x9e, + 0x91, 0xe8, 0x62, 0x14, 0xf7, 0x18, 0xad, 0xfc, 0xbe, 0x88, 0x16, 0x94, 0x87, 0x77, 0x27, 0xa1, + 0xdd, 0x39, 0x89, 0xb9, 0x8f, 0x9d, 0x04, 0x3e, 0x40, 0xab, 0x7d, 0x00, 0xcb, 0x61, 0x40, 0x04, + 0x58, 0x84, 0x1f, 0x5b, 0x7d, 0x8f, 0x08, 0x3d, 0x6d, 0xa4, 0xab, 0xf9, 0xed, 0xf5, 0x9b, 0x33, + 0x8e, 0x0e, 0x6b, 0x7c, 0xc6, 0xbb, 0x01, 0xf5, 0x63, 0xb1, 0x42, 0x1f, 0x60, 0x57, 0x6e, 0x6d, + 0xf0, 0xe3, 0x7d, 0x8f, 0x88, 0x29, 0x3d, 0x9b, 0xba, 0x4a, 0x2f, 0xf3, 0xa1, 0x7a, 0x4d, 0xea, + 0x4a, 0xbd, 0xef, 0x50, 0x31, 0xd2, 0xe3, 0xe0, 0x79, 0xc0, 0x2c, 0x0e, 0x42, 0x78, 0x30, 0x04, + 0x5f, 0x28, 0xd9, 0xf9, 0xf7, 0x93, 0xfd, 0xb4, 0x0f, 0xd0, 0x91, 0x0a, 0x9d, 0xb1, 0x80, 0x54, + 0x1f, 0xa0, 0xcf, 0x66, 0xab, 0x33, 0x22, 0x68, 0xc0, 0xf5, 0x05, 0xa9, 0x6f, 0xdc, 0x36, 0xdf, + 0x7d, 0x00, 0x33, 0x22, 0xc6, 0x65, 0xd6, 0x67, 0x94, 0x91, 0x38, 0xc7, 0x2f, 0x50, 0x04, 0x5a, + 0x76, 0xf8, 0x7a, 0x46, 0x17, 0x8b, 0xef, 0xd7, 0xc5, 0x5a, 0x1f, 0xa0, 0x19, 0x09, 0x4c, 0x35, + 0x01, 0x68, 0x63, 0xa6, 0x76, 0xdc, 0x43, 0xf6, 0x83, 0x7a, 0xd0, 0xff, 0x5f, 0x24, 0x6e, 0xe1, + 0x31, 0x2a, 0x10, 0xc7, 0x81, 0x91, 0xa0, 0xfe, 0xc0, 0x0a, 0x98, 0x0b, 0x8c, 0xeb, 0x39, 0x43, + 0xab, 0x66, 0xcd, 0x7b, 0xe3, 0xfc, 0x33, 0x99, 0xc6, 0xdb, 0xe8, 0x01, 0xf1, 0xbc, 0xe0, 0xd4, + 0x0a, 0xf9, 0x3b, 0x96, 0x74, 0x24, 0xf9, 0xab, 0x12, 0xec, 0xf1, 0x64, 0x11, 0x7c, 0x80, 0x96, + 0x23, 0x19, 0xce, 0xad, 0x01, 0x23, 0xbe, 0xe0, 0x7a, 0x5e, 0xfa, 0x7e, 0x78, 0x9b, 0xef, 0x86, + 0x24, 0x7f, 0x13, 0x71, 0x63, 0xeb, 0x4b, 0x64, 0x92, 0xe2, 0x78, 0x13, 0xad, 0x32, 0x78, 0x69, + 0x11, 0x21, 0x58, 0xe2, 0x76, 0xeb, 0x4b, 0x46, 0xba, 0x9a, 0x33, 0x0b, 0x0c, 0x5e, 0x36, 0x84, + 0x60, 0xe3, 0xbb, 0x3b, 0x8b, 0x6e, 0x53, 0x57, 0x5f, 0x9e, 0x41, 0x6f, 0x52, 0xb7, 0xf2, 0x03, + 0xca, 0xde, 0x0c, 0x0e, 0x7f, 0x8d, 0xe6, 0x47, 0x8c, 0x3a, 0x10, 0x3f, 0x8c, 0x77, 0x9e, 0xa3, + 0x62, 0xe3, 0x2d, 0x94, 0xee, 0x03, 0xc4, 0x3f, 0xe1, 0x3b, 0x37, 0x45, 0xdc, 0x9d, 0x8c, 0x7c, + 0xb2, 0x7e, 0xd2, 0x50, 0x3e, 0xd1, 0x3d, 0xde, 0x46, 0x8b, 0xc4, 0x75, 0x19, 0x70, 0xae, 0xde, + 0xac, 0xa6, 0xfe, 0xe7, 0x1f, 0x9b, 0xf7, 0x63, 0xbd, 0x86, 0x42, 0x3a, 0x82, 0x51, 0x7f, 0x60, + 0xde, 0x10, 0xf1, 0x1e, 0xca, 0x8f, 0x80, 0x0d, 0x29, 0xe7, 0x34, 0xf0, 0xa3, 0x77, 0x24, 0x5d, + 0x5d, 0xd9, 0xae, 0xdc, 0x36, 0xeb, 0xc3, 0x31, 0xd5, 0x4c, 0x6e, 0xfb, 0xe2, 0xb7, 0x39, 0x84, + 0x26, 0x18, 0xfe, 0x12, 0xad, 0x1d, 0xb6, 0xcc, 0xa7, 0xed, 0x4e, 0xa7, 0xfd, 0xec, 0xc0, 0xea, + 0x1d, 0x74, 0x0e, 0x5b, 0xbb, 0xed, 0xfd, 0x76, 0x6b, 0xaf, 0x90, 0x2a, 0xde, 0x3b, 0x3b, 0x37, + 0xf2, 0xa1, 0xcf, 0x47, 0xe0, 0xd0, 0x3e, 0x05, 0x17, 0x7f, 0x8e, 0x3e, 0x49, 0x90, 0x3b, 0xad, + 0x6e, 0xf7, 0x49, 0xab, 0xa0, 0x15, 0xd1, 0xd9, 0xb9, 0xb1, 0xa0, 0x6e, 0xcc, 0x14, 0x65, 0xb7, + 0x71, 0xb0, 0xdb, 0x7a, 0x52, 0x98, 0x53, 0x14, 0x27, 0x32, 0xe9, 0xe1, 0x47, 0x68, 0x35, 0x41, + 0x79, 0xde, 0xee, 0x7e, 0xbb, 0x67, 0x36, 0x9e, 0x17, 0xd2, 0xc5, 0xa5, 0xb3, 0x73, 0x23, 0x7b, + 0x4a, 0xc5, 0x91, 0xcb, 0xc8, 0xe9, 0x94, 0x52, 0xef, 0x70, 0xaf, 0xd1, 0x6d, 0x15, 0x32, 0x4a, + 0x29, 0x1c, 0xb9, 0x44, 0xc0, 0x94, 0xf9, 0xc9, 0xb2, 0x53, 0x98, 0x57, 0xe6, 0x13, 0x8d, 0xe3, + 0xc7, 0xe8, 0x41, 0x82, 0xdc, 0xe8, 0x76, 0xcd, 0x76, 0xb3, 0xd7, 0x6d, 0x75, 0x0a, 0x0b, 0xc5, + 0x95, 0xb3, 0x73, 0x03, 0x45, 0xd7, 0x88, 0xda, 0xa1, 0x00, 0xde, 0x84, 0xb7, 0x57, 0x25, 0xed, + 0xe2, 0xaa, 0xa4, 0xfd, 0x7d, 0x55, 0xd2, 0x7e, 0xb9, 0x2e, 0xa5, 0x2e, 0xae, 0x4b, 0xa9, 0xbf, + 0xae, 0x4b, 0x29, 0xb4, 0x4e, 0x83, 0x5b, 0x06, 0x7e, 0xa8, 0xbd, 0xa8, 0x0d, 0xa8, 0x38, 0x0a, + 0xed, 0x9a, 0x13, 0x0c, 0xeb, 0x13, 0xd2, 0x26, 0x0d, 0x12, 0x51, 0xfd, 0xd5, 0xf8, 0x7b, 0xc2, + 0x5e, 0x90, 0xff, 0xde, 0x5f, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0x64, 0xff, 0x9b, 0xb8, 0x6d, + 0x08, 0x00, 0x00, } func (m *MarketAccount) Marshal() (dAtA []byte, err error) { @@ -728,10 +728,10 @@ func (m *Market) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x48 } - if len(m.FeeSettlementBuyerRatios) > 0 { - for iNdEx := len(m.FeeSettlementBuyerRatios) - 1; iNdEx >= 0; iNdEx-- { + if len(m.FeeBuyerSettlementRatios) > 0 { + for iNdEx := len(m.FeeBuyerSettlementRatios) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.FeeSettlementBuyerRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.FeeBuyerSettlementRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -742,10 +742,10 @@ func (m *Market) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x42 } } - if len(m.FeeSettlementBuyerFlat) > 0 { - for iNdEx := len(m.FeeSettlementBuyerFlat) - 1; iNdEx >= 0; iNdEx-- { + if len(m.FeeBuyerSettlementFlat) > 0 { + for iNdEx := len(m.FeeBuyerSettlementFlat) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.FeeSettlementBuyerFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.FeeBuyerSettlementFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -756,10 +756,10 @@ func (m *Market) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x3a } } - if len(m.FeeSettlementSellerRatios) > 0 { - for iNdEx := len(m.FeeSettlementSellerRatios) - 1; iNdEx >= 0; iNdEx-- { + if len(m.FeeSellerSettlementRatios) > 0 { + for iNdEx := len(m.FeeSellerSettlementRatios) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.FeeSettlementSellerRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.FeeSellerSettlementRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -770,10 +770,10 @@ func (m *Market) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x32 } } - if len(m.FeeSettlementSellerFlat) > 0 { - for iNdEx := len(m.FeeSettlementSellerFlat) - 1; iNdEx >= 0; iNdEx-- { + if len(m.FeeSellerSettlementFlat) > 0 { + for iNdEx := len(m.FeeSellerSettlementFlat) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.FeeSettlementSellerFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.FeeSellerSettlementFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -998,26 +998,26 @@ func (m *Market) Size() (n int) { n += 1 + l + sovMarket(uint64(l)) } } - if len(m.FeeSettlementSellerFlat) > 0 { - for _, e := range m.FeeSettlementSellerFlat { + if len(m.FeeSellerSettlementFlat) > 0 { + for _, e := range m.FeeSellerSettlementFlat { l = e.Size() n += 1 + l + sovMarket(uint64(l)) } } - if len(m.FeeSettlementSellerRatios) > 0 { - for _, e := range m.FeeSettlementSellerRatios { + if len(m.FeeSellerSettlementRatios) > 0 { + for _, e := range m.FeeSellerSettlementRatios { l = e.Size() n += 1 + l + sovMarket(uint64(l)) } } - if len(m.FeeSettlementBuyerFlat) > 0 { - for _, e := range m.FeeSettlementBuyerFlat { + if len(m.FeeBuyerSettlementFlat) > 0 { + for _, e := range m.FeeBuyerSettlementFlat { l = e.Size() n += 1 + l + sovMarket(uint64(l)) } } - if len(m.FeeSettlementBuyerRatios) > 0 { - for _, e := range m.FeeSettlementBuyerRatios { + if len(m.FeeBuyerSettlementRatios) > 0 { + for _, e := range m.FeeBuyerSettlementRatios { l = e.Size() n += 1 + l + sovMarket(uint64(l)) } @@ -1555,7 +1555,7 @@ func (m *Market) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FeeSettlementSellerFlat", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field FeeSellerSettlementFlat", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1582,14 +1582,14 @@ func (m *Market) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.FeeSettlementSellerFlat = append(m.FeeSettlementSellerFlat, types1.Coin{}) - if err := m.FeeSettlementSellerFlat[len(m.FeeSettlementSellerFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.FeeSellerSettlementFlat = append(m.FeeSellerSettlementFlat, types1.Coin{}) + if err := m.FeeSellerSettlementFlat[len(m.FeeSellerSettlementFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FeeSettlementSellerRatios", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field FeeSellerSettlementRatios", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1616,14 +1616,14 @@ func (m *Market) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.FeeSettlementSellerRatios = append(m.FeeSettlementSellerRatios, FeeRatio{}) - if err := m.FeeSettlementSellerRatios[len(m.FeeSettlementSellerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.FeeSellerSettlementRatios = append(m.FeeSellerSettlementRatios, FeeRatio{}) + if err := m.FeeSellerSettlementRatios[len(m.FeeSellerSettlementRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FeeSettlementBuyerFlat", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field FeeBuyerSettlementFlat", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1650,14 +1650,14 @@ func (m *Market) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.FeeSettlementBuyerFlat = append(m.FeeSettlementBuyerFlat, types1.Coin{}) - if err := m.FeeSettlementBuyerFlat[len(m.FeeSettlementBuyerFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.FeeBuyerSettlementFlat = append(m.FeeBuyerSettlementFlat, types1.Coin{}) + if err := m.FeeBuyerSettlementFlat[len(m.FeeBuyerSettlementFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FeeSettlementBuyerRatios", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field FeeBuyerSettlementRatios", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1684,8 +1684,8 @@ func (m *Market) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.FeeSettlementBuyerRatios = append(m.FeeSettlementBuyerRatios, FeeRatio{}) - if err := m.FeeSettlementBuyerRatios[len(m.FeeSettlementBuyerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.FeeBuyerSettlementRatios = append(m.FeeBuyerSettlementRatios, FeeRatio{}) + if err := m.FeeBuyerSettlementRatios[len(m.FeeBuyerSettlementRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 284ae802e6..27570fcd11 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -47,13 +47,13 @@ func TestMarket_Validate(t *testing.T) { MarketDetails: MarketDetails{Name: "Test Market", Description: "Just a market for testing."}, FeeCreateAskFlat: coins("10nnibbler,5mfry"), FeeCreateBidFlat: coins("15nnibbler,5mfry"), - FeeSettlementSellerFlat: coins("50nnibler,8mfry"), - FeeSettlementSellerRatios: []FeeRatio{ + FeeSellerSettlementFlat: coins("50nnibler,8mfry"), + FeeSellerSettlementRatios: []FeeRatio{ {Price: coin(1000, "nnibler"), Fee: coin(1, "nnibler")}, {Price: coin(300, "mfry"), Fee: coin(1, "mfry")}, }, - FeeSettlementBuyerFlat: coins("100nnibler,20mfry"), - FeeSettlementBuyerRatios: []FeeRatio{ + FeeBuyerSettlementFlat: coins("100nnibler,20mfry"), + FeeBuyerSettlementRatios: []FeeRatio{ {Price: coin(500, "nnibler"), Fee: coin(1, "nnibler")}, {Price: coin(500, "nnibler"), Fee: coin(8, "mfry")}, {Price: coin(150, "mfry"), Fee: coin(1, "mfry")}, @@ -91,30 +91,30 @@ func TestMarket_Validate(t *testing.T) { expErr: []string{`invalid create-bid flat fee option "-1leela": negative coin amount: -1`}, }, { - name: "invalid fee settlement seller flat", - market: Market{FeeSettlementSellerFlat: sdk.Coins{coin(-1, "leela")}}, - expErr: []string{`invalid settlement seller flat fee option "-1leela": negative coin amount: -1`}, + name: "invalid fee seller settlement flat", + market: Market{FeeSellerSettlementFlat: sdk.Coins{coin(-1, "leela")}}, + expErr: []string{`invalid seller settlement flat fee option "-1leela": negative coin amount: -1`}, }, { - name: "invalid fee settlement buyer flat", - market: Market{FeeSettlementBuyerFlat: sdk.Coins{coin(-1, "leela")}}, - expErr: []string{`invalid settlement buyer flat fee option "-1leela": negative coin amount: -1`}, + name: "invalid fee buyer settlement flat", + market: Market{FeeBuyerSettlementFlat: sdk.Coins{coin(-1, "leela")}}, + expErr: []string{`invalid buyer settlement flat fee option "-1leela": negative coin amount: -1`}, }, { name: "invalid seller ratio", - market: Market{FeeSettlementSellerRatios: []FeeRatio{{Price: coin(0, "fry"), Fee: coin(1, "fry")}}}, + market: Market{FeeSellerSettlementRatios: []FeeRatio{{Price: coin(0, "fry"), Fee: coin(1, "fry")}}}, expErr: []string{`seller fee ratio price amount "0fry" must be positive`}, }, { name: "invalid buyer ratio", - market: Market{FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(0, "fry"), Fee: coin(1, "fry")}}}, + market: Market{FeeBuyerSettlementRatios: []FeeRatio{{Price: coin(0, "fry"), Fee: coin(1, "fry")}}}, expErr: []string{`buyer fee ratio price amount "0fry" must be positive`}, }, { name: "invalid ratios", market: Market{ - FeeSettlementSellerRatios: []FeeRatio{{Price: coin(10, "fry"), Fee: coin(1, "fry")}}, - FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(100, "leela"), Fee: coin(1, "leela")}}, + FeeSellerSettlementRatios: []FeeRatio{{Price: coin(10, "fry"), Fee: coin(1, "fry")}}, + FeeBuyerSettlementRatios: []FeeRatio{{Price: coin(100, "leela"), Fee: coin(1, "leela")}}, }, expErr: []string{ `denom "fry" is defined in the seller settlement fee ratios but not buyer`, @@ -142,10 +142,10 @@ func TestMarket_Validate(t *testing.T) { MarketDetails: MarketDetails{Name: strings.Repeat("n", MaxName+1)}, FeeCreateAskFlat: sdk.Coins{coin(-1, "leela")}, FeeCreateBidFlat: sdk.Coins{coin(-1, "leela")}, - FeeSettlementSellerFlat: sdk.Coins{coin(-1, "leela")}, - FeeSettlementBuyerFlat: sdk.Coins{coin(-1, "leela")}, - FeeSettlementSellerRatios: []FeeRatio{{Price: coin(10, "fry"), Fee: coin(1, "fry")}}, - FeeSettlementBuyerRatios: []FeeRatio{{Price: coin(100, "leela"), Fee: coin(1, "leela")}}, + FeeSellerSettlementFlat: sdk.Coins{coin(-1, "leela")}, + FeeBuyerSettlementFlat: sdk.Coins{coin(-1, "leela")}, + FeeSellerSettlementRatios: []FeeRatio{{Price: coin(10, "fry"), Fee: coin(1, "fry")}}, + FeeBuyerSettlementRatios: []FeeRatio{{Price: coin(100, "leela"), Fee: coin(1, "leela")}}, AccessGrants: []AccessGrant{{Address: "bad_addr", Permissions: AllPermissions()}}, ReqAttrCreateAsk: []string{"this-attr-is-bad"}, ReqAttrCreateBid: []string{"this-attr-grrrr"}, @@ -154,8 +154,8 @@ func TestMarket_Validate(t *testing.T) { fmt.Sprintf("name length %d exceeds maximum length of %d", MaxName+1, MaxName), `invalid create-ask flat fee option "-1leela": negative coin amount: -1`, `invalid create-bid flat fee option "-1leela": negative coin amount: -1`, - `invalid settlement seller flat fee option "-1leela": negative coin amount: -1`, - `invalid settlement buyer flat fee option "-1leela": negative coin amount: -1`, + `invalid seller settlement flat fee option "-1leela": negative coin amount: -1`, + `invalid buyer settlement flat fee option "-1leela": negative coin amount: -1`, `denom "fry" is defined in the seller settlement fee ratios but not buyer`, `denom "leela" is defined in the buyer settlement fee ratios but not seller`, "invalid access grant: invalid address: decoding bech32 failed: invalid separator index -1", diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 17abaf72ad..717ecf3be1 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -169,12 +169,12 @@ func (m MsgGovManageFeesRequest) ValidateBasic() error { errs = append(errs, ValidateAddRemoveFeeOptions("create-ask flat fee", m.AddFeeCreateAskFlat, m.RemoveFeeCreateAskFlat), ValidateAddRemoveFeeOptions("create-bid flat fee", m.AddFeeCreateBidFlat, m.RemoveFeeCreateBidFlat), - ValidateAddRemoveFeeOptions("seller settlement flat fee", m.AddFeeSettlementSellerFlat, m.RemoveFeeSettlementSellerFlat), - ValidateSellerFeeRatios(m.AddFeeSettlementSellerRatios), - ValidateDisjointFeeRatios("seller settlement fee", m.AddFeeSettlementSellerRatios, m.RemoveFeeSettlementSellerRatios), - ValidateAddRemoveFeeOptions("buyer settlement flat fee", m.AddFeeSettlementBuyerFlat, m.RemoveFeeSettlementBuyerFlat), - ValidateBuyerFeeRatios(m.AddFeeSettlementBuyerRatios), - ValidateDisjointFeeRatios("buyer settlement fee", m.AddFeeSettlementBuyerRatios, m.RemoveFeeSettlementBuyerRatios), + ValidateAddRemoveFeeOptions("seller settlement flat fee", m.AddFeeSellerSettlementFlat, m.RemoveFeeSellerSettlementFlat), + ValidateSellerFeeRatios(m.AddFeeSellerSettlementRatios), + ValidateDisjointFeeRatios("seller settlement fee", m.AddFeeSellerSettlementRatios, m.RemoveFeeSellerSettlementRatios), + ValidateAddRemoveFeeOptions("buyer settlement flat fee", m.AddFeeBuyerSettlementFlat, m.RemoveFeeBuyerSettlementFlat), + ValidateBuyerFeeRatios(m.AddFeeBuyerSettlementRatios), + ValidateDisjointFeeRatios("buyer settlement fee", m.AddFeeBuyerSettlementRatios, m.RemoveFeeBuyerSettlementRatios), ) } else { errs = append(errs, errors.New("no updates")) @@ -187,10 +187,10 @@ func (m MsgGovManageFeesRequest) ValidateBasic() error { func (m MsgGovManageFeesRequest) HasUpdates() bool { return len(m.AddFeeCreateAskFlat) > 0 || len(m.RemoveFeeCreateAskFlat) > 0 || len(m.AddFeeCreateBidFlat) > 0 || len(m.RemoveFeeCreateBidFlat) > 0 || - len(m.AddFeeSettlementSellerFlat) > 0 || len(m.RemoveFeeSettlementSellerFlat) > 0 || - len(m.AddFeeSettlementSellerRatios) > 0 || len(m.RemoveFeeSettlementSellerRatios) > 0 || - len(m.AddFeeSettlementBuyerFlat) > 0 || len(m.RemoveFeeSettlementBuyerFlat) > 0 || - len(m.AddFeeSettlementBuyerRatios) > 0 || len(m.RemoveFeeSettlementBuyerRatios) > 0 + len(m.AddFeeSellerSettlementFlat) > 0 || len(m.RemoveFeeSellerSettlementFlat) > 0 || + len(m.AddFeeSellerSettlementRatios) > 0 || len(m.RemoveFeeSellerSettlementRatios) > 0 || + len(m.AddFeeBuyerSettlementFlat) > 0 || len(m.RemoveFeeBuyerSettlementFlat) > 0 || + len(m.AddFeeBuyerSettlementRatios) > 0 || len(m.RemoveFeeBuyerSettlementRatios) > 0 } func (m MsgGovManageFeesRequest) GetSigners() []sdk.AccAddress { diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 462385cee4..ac30030b28 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -157,12 +157,12 @@ func TestMsgGovCreateMarketRequest_ValidateBasic(t *testing.T) { }, FeeCreateAskFlat: []sdk.Coin{sdk.NewInt64Coin("nhash", 10)}, FeeCreateBidFlat: []sdk.Coin{sdk.NewInt64Coin("nhash", 20)}, - FeeSettlementSellerFlat: []sdk.Coin{sdk.NewInt64Coin("nhash", 50)}, - FeeSettlementSellerRatios: []FeeRatio{ + FeeSellerSettlementFlat: []sdk.Coin{sdk.NewInt64Coin("nhash", 50)}, + FeeSellerSettlementRatios: []FeeRatio{ {Price: sdk.NewInt64Coin("nhash", 100), Fee: sdk.NewInt64Coin("nhash", 1)}, }, - FeeSettlementBuyerFlat: []sdk.Coin{sdk.NewInt64Coin("nhash", 100)}, - FeeSettlementBuyerRatios: []FeeRatio{ + FeeBuyerSettlementFlat: []sdk.Coin{sdk.NewInt64Coin("nhash", 100)}, + FeeBuyerSettlementRatios: []FeeRatio{ {Price: sdk.NewInt64Coin("nhash", 100), Fee: sdk.NewInt64Coin("nhash", 1)}, }, AcceptingOrders: true, @@ -313,70 +313,70 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { expErr: []string{"cannot add and remove the same create-bid flat fee options: 1nhash"}, }, { - name: "invalid add settlement seller flat", + name: "invalid add seller settlement flat", msg: MsgGovManageFeesRequest{ Authority: authority, - AddFeeSettlementSellerFlat: []sdk.Coin{coin(0, "nhash")}, + AddFeeSellerSettlementFlat: []sdk.Coin{coin(0, "nhash")}, }, expErr: []string{`invalid seller settlement flat fee to add option "0nhash": amount cannot be zero`}, }, { - name: "same add and remove settlement seller flat", + name: "same add and remove seller settlement flat", msg: MsgGovManageFeesRequest{ Authority: authority, - AddFeeSettlementSellerFlat: []sdk.Coin{coin(1, "nhash")}, - RemoveFeeSettlementSellerFlat: []sdk.Coin{coin(1, "nhash")}, + AddFeeSellerSettlementFlat: []sdk.Coin{coin(1, "nhash")}, + RemoveFeeSellerSettlementFlat: []sdk.Coin{coin(1, "nhash")}, }, expErr: []string{"cannot add and remove the same seller settlement flat fee options: 1nhash"}, }, { - name: "invalid add settlement seller ratio", + name: "invalid add seller settlement ratio", msg: MsgGovManageFeesRequest{ Authority: authority, - AddFeeSettlementSellerRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, + AddFeeSellerSettlementRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, }, expErr: []string{`seller fee ratio fee amount "2nhash" cannot be greater than price amount "1nhash"`}, }, { - name: "same add and remove settlement seller ratio", + name: "same add and remove seller settlement ratio", msg: MsgGovManageFeesRequest{ Authority: authority, - AddFeeSettlementSellerRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, - RemoveFeeSettlementSellerRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, + AddFeeSellerSettlementRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, + RemoveFeeSellerSettlementRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, }, expErr: []string{"cannot add and remove the same seller settlement fee ratios: 2nhash:1nhash"}, }, { - name: "invalid add settlement buyer flat", + name: "invalid add buyer settlement flat", msg: MsgGovManageFeesRequest{ Authority: authority, - AddFeeSettlementBuyerFlat: []sdk.Coin{coin(0, "nhash")}, + AddFeeBuyerSettlementFlat: []sdk.Coin{coin(0, "nhash")}, }, expErr: []string{`invalid buyer settlement flat fee to add option "0nhash": amount cannot be zero`}, }, { - name: "same add and remove settlement buyer flat", + name: "same add and remove buyer settlement flat", msg: MsgGovManageFeesRequest{ Authority: authority, - AddFeeSettlementBuyerFlat: []sdk.Coin{coin(1, "nhash")}, - RemoveFeeSettlementBuyerFlat: []sdk.Coin{coin(1, "nhash")}, + AddFeeBuyerSettlementFlat: []sdk.Coin{coin(1, "nhash")}, + RemoveFeeBuyerSettlementFlat: []sdk.Coin{coin(1, "nhash")}, }, expErr: []string{"cannot add and remove the same buyer settlement flat fee options: 1nhash"}, }, { - name: "invalid add settlement buyer ratio", + name: "invalid add buyer settlement ratio", msg: MsgGovManageFeesRequest{ Authority: authority, - AddFeeSettlementBuyerRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, + AddFeeBuyerSettlementRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, }, expErr: []string{`buyer fee ratio fee amount "2nhash" cannot be greater than price amount "1nhash"`}, }, { - name: "same add and remove settlement buyer ratio", + name: "same add and remove buyer settlement ratio", msg: MsgGovManageFeesRequest{ Authority: authority, - AddFeeSettlementBuyerRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, - RemoveFeeSettlementBuyerRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, + AddFeeBuyerSettlementRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, + RemoveFeeBuyerSettlementRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, }, expErr: []string{"cannot add and remove the same buyer settlement fee ratios: 2nhash:1nhash"}, }, @@ -388,14 +388,14 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { RemoveFeeCreateAskFlat: []sdk.Coin{coin(0, "nhash")}, AddFeeCreateBidFlat: []sdk.Coin{coin(0, "nhash")}, RemoveFeeCreateBidFlat: []sdk.Coin{coin(0, "nhash")}, - AddFeeSettlementSellerFlat: []sdk.Coin{coin(0, "nhash")}, - RemoveFeeSettlementSellerFlat: []sdk.Coin{coin(0, "nhash")}, - AddFeeSettlementSellerRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, - RemoveFeeSettlementSellerRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, - AddFeeSettlementBuyerFlat: []sdk.Coin{coin(0, "nhash")}, - RemoveFeeSettlementBuyerFlat: []sdk.Coin{coin(0, "nhash")}, - AddFeeSettlementBuyerRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, - RemoveFeeSettlementBuyerRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, + AddFeeSellerSettlementFlat: []sdk.Coin{coin(0, "nhash")}, + RemoveFeeSellerSettlementFlat: []sdk.Coin{coin(0, "nhash")}, + AddFeeSellerSettlementRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, + RemoveFeeSellerSettlementRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, + AddFeeBuyerSettlementFlat: []sdk.Coin{coin(0, "nhash")}, + RemoveFeeBuyerSettlementFlat: []sdk.Coin{coin(0, "nhash")}, + AddFeeBuyerSettlementRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, + RemoveFeeBuyerSettlementRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, }, expErr: []string{ "invalid authority", "empty address string is not allowed", @@ -464,43 +464,43 @@ func TestMsgGovManageFeesRequest_HasUpdates(t *testing.T) { exp: true, }, { - name: "one add fee settlement seller flat", - msg: MsgGovManageFeesRequest{AddFeeSettlementSellerFlat: oneCoin}, + name: "one add fee seller settlement flat", + msg: MsgGovManageFeesRequest{AddFeeSellerSettlementFlat: oneCoin}, exp: true, }, { - name: "one remove fee settlement seller flat", - msg: MsgGovManageFeesRequest{RemoveFeeSettlementSellerFlat: oneCoin}, + name: "one remove fee seller settlement flat", + msg: MsgGovManageFeesRequest{RemoveFeeSellerSettlementFlat: oneCoin}, exp: true, }, { - name: "one add fee settlement seller ratio", - msg: MsgGovManageFeesRequest{AddFeeSettlementSellerRatios: oneRatio}, + name: "one add fee seller settlement ratio", + msg: MsgGovManageFeesRequest{AddFeeSellerSettlementRatios: oneRatio}, exp: true, }, { - name: "one remove fee settlement seller ratio", - msg: MsgGovManageFeesRequest{RemoveFeeSettlementSellerRatios: oneRatio}, + name: "one remove fee seller settlement ratio", + msg: MsgGovManageFeesRequest{RemoveFeeSellerSettlementRatios: oneRatio}, exp: true, }, { - name: "one add fee settlement buyer flat", - msg: MsgGovManageFeesRequest{AddFeeSettlementBuyerFlat: oneCoin}, + name: "one add fee buyer settlement flat", + msg: MsgGovManageFeesRequest{AddFeeBuyerSettlementFlat: oneCoin}, exp: true, }, { - name: "one remove fee settlement buyer flat", - msg: MsgGovManageFeesRequest{RemoveFeeSettlementBuyerFlat: oneCoin}, + name: "one remove fee buyer settlement flat", + msg: MsgGovManageFeesRequest{RemoveFeeBuyerSettlementFlat: oneCoin}, exp: true, }, { - name: "one add fee settlement buyer ratio", - msg: MsgGovManageFeesRequest{AddFeeSettlementBuyerRatios: oneRatio}, + name: "one add fee buyer settlement ratio", + msg: MsgGovManageFeesRequest{AddFeeBuyerSettlementRatios: oneRatio}, exp: true, }, { - name: "one remove fee settlement buyer ratio", - msg: MsgGovManageFeesRequest{RemoveFeeSettlementBuyerRatios: oneRatio}, + name: "one remove fee buyer settlement ratio", + msg: MsgGovManageFeesRequest{RemoveFeeBuyerSettlementRatios: oneRatio}, exp: true, }, } diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 8500eb4980..59a50186eb 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -1025,22 +1025,22 @@ type MsgGovManageFeesRequest struct { AddFeeCreateBidFlat []types.Coin `protobuf:"bytes,4,rep,name=add_fee_create_bid_flat,json=addFeeCreateBidFlat,proto3" json:"add_fee_create_bid_flat"` // remove_fee_create_bid_flat are the create-bid flat fee options to remove. RemoveFeeCreateBidFlat []types.Coin `protobuf:"bytes,5,rep,name=remove_fee_create_bid_flat,json=removeFeeCreateBidFlat,proto3" json:"remove_fee_create_bid_flat"` - // add_fee_settlement_seller_flat are the seller settlement flat fee options to add. - AddFeeSettlementSellerFlat []types.Coin `protobuf:"bytes,6,rep,name=add_fee_settlement_seller_flat,json=addFeeSettlementSellerFlat,proto3" json:"add_fee_settlement_seller_flat"` - // remove_fee_settlement_seller_flat are the seller settlement flat fee options to remove. - RemoveFeeSettlementSellerFlat []types.Coin `protobuf:"bytes,7,rep,name=remove_fee_settlement_seller_flat,json=removeFeeSettlementSellerFlat,proto3" json:"remove_fee_settlement_seller_flat"` - // add_fee_settlement_seller_ratios are the seller settlement fee ratios to add. - AddFeeSettlementSellerRatios []FeeRatio `protobuf:"bytes,8,rep,name=add_fee_settlement_seller_ratios,json=addFeeSettlementSellerRatios,proto3" json:"add_fee_settlement_seller_ratios"` - // remove_fee_settlement_seller_ratios are the seller settlement fee ratios to remove. - RemoveFeeSettlementSellerRatios []FeeRatio `protobuf:"bytes,9,rep,name=remove_fee_settlement_seller_ratios,json=removeFeeSettlementSellerRatios,proto3" json:"remove_fee_settlement_seller_ratios"` - // add_fee_settlement_buyer_flat are the buyer settlement flat fee options to add. - AddFeeSettlementBuyerFlat []types.Coin `protobuf:"bytes,10,rep,name=add_fee_settlement_buyer_flat,json=addFeeSettlementBuyerFlat,proto3" json:"add_fee_settlement_buyer_flat"` - // remove_fee_settlement_buyer_flat are the buyer settlement flat fee options to remove. - RemoveFeeSettlementBuyerFlat []types.Coin `protobuf:"bytes,11,rep,name=remove_fee_settlement_buyer_flat,json=removeFeeSettlementBuyerFlat,proto3" json:"remove_fee_settlement_buyer_flat"` - // add_fee_settlement_buyer_ratios are the buyer settlement fee ratios to add. - AddFeeSettlementBuyerRatios []FeeRatio `protobuf:"bytes,12,rep,name=add_fee_settlement_buyer_ratios,json=addFeeSettlementBuyerRatios,proto3" json:"add_fee_settlement_buyer_ratios"` - // remove_fee_settlement_buyer_ratios are the buyer settlement fee ratios to remove. - RemoveFeeSettlementBuyerRatios []FeeRatio `protobuf:"bytes,13,rep,name=remove_fee_settlement_buyer_ratios,json=removeFeeSettlementBuyerRatios,proto3" json:"remove_fee_settlement_buyer_ratios"` + // add_fee_seller_settlement_flat are the seller settlement flat fee options to add. + AddFeeSellerSettlementFlat []types.Coin `protobuf:"bytes,6,rep,name=add_fee_seller_settlement_flat,json=addFeeSellerSettlementFlat,proto3" json:"add_fee_seller_settlement_flat"` + // remove_fee_seller_settlement_flat are the seller settlement flat fee options to remove. + RemoveFeeSellerSettlementFlat []types.Coin `protobuf:"bytes,7,rep,name=remove_fee_seller_settlement_flat,json=removeFeeSellerSettlementFlat,proto3" json:"remove_fee_seller_settlement_flat"` + // add_fee_seller_settlement_ratios are the seller settlement fee ratios to add. + AddFeeSellerSettlementRatios []FeeRatio `protobuf:"bytes,8,rep,name=add_fee_seller_settlement_ratios,json=addFeeSellerSettlementRatios,proto3" json:"add_fee_seller_settlement_ratios"` + // remove_fee_seller_settlement_ratios are the seller settlement fee ratios to remove. + RemoveFeeSellerSettlementRatios []FeeRatio `protobuf:"bytes,9,rep,name=remove_fee_seller_settlement_ratios,json=removeFeeSellerSettlementRatios,proto3" json:"remove_fee_seller_settlement_ratios"` + // add_fee_buyer_settlement_flat are the buyer settlement flat fee options to add. + AddFeeBuyerSettlementFlat []types.Coin `protobuf:"bytes,10,rep,name=add_fee_buyer_settlement_flat,json=addFeeBuyerSettlementFlat,proto3" json:"add_fee_buyer_settlement_flat"` + // remove_fee_buyer_settlement_flat are the buyer settlement flat fee options to remove. + RemoveFeeBuyerSettlementFlat []types.Coin `protobuf:"bytes,11,rep,name=remove_fee_buyer_settlement_flat,json=removeFeeBuyerSettlementFlat,proto3" json:"remove_fee_buyer_settlement_flat"` + // add_fee_buyer_settlement_ratios are the buyer settlement fee ratios to add. + AddFeeBuyerSettlementRatios []FeeRatio `protobuf:"bytes,12,rep,name=add_fee_buyer_settlement_ratios,json=addFeeBuyerSettlementRatios,proto3" json:"add_fee_buyer_settlement_ratios"` + // remove_fee_buyer_settlement_ratios are the buyer settlement fee ratios to remove. + RemoveFeeBuyerSettlementRatios []FeeRatio `protobuf:"bytes,13,rep,name=remove_fee_buyer_settlement_ratios,json=removeFeeBuyerSettlementRatios,proto3" json:"remove_fee_buyer_settlement_ratios"` } func (m *MsgGovManageFeesRequest) Reset() { *m = MsgGovManageFeesRequest{} } @@ -1111,58 +1111,58 @@ func (m *MsgGovManageFeesRequest) GetRemoveFeeCreateBidFlat() []types.Coin { return nil } -func (m *MsgGovManageFeesRequest) GetAddFeeSettlementSellerFlat() []types.Coin { +func (m *MsgGovManageFeesRequest) GetAddFeeSellerSettlementFlat() []types.Coin { if m != nil { - return m.AddFeeSettlementSellerFlat + return m.AddFeeSellerSettlementFlat } return nil } -func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementSellerFlat() []types.Coin { +func (m *MsgGovManageFeesRequest) GetRemoveFeeSellerSettlementFlat() []types.Coin { if m != nil { - return m.RemoveFeeSettlementSellerFlat + return m.RemoveFeeSellerSettlementFlat } return nil } -func (m *MsgGovManageFeesRequest) GetAddFeeSettlementSellerRatios() []FeeRatio { +func (m *MsgGovManageFeesRequest) GetAddFeeSellerSettlementRatios() []FeeRatio { if m != nil { - return m.AddFeeSettlementSellerRatios + return m.AddFeeSellerSettlementRatios } return nil } -func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementSellerRatios() []FeeRatio { +func (m *MsgGovManageFeesRequest) GetRemoveFeeSellerSettlementRatios() []FeeRatio { if m != nil { - return m.RemoveFeeSettlementSellerRatios + return m.RemoveFeeSellerSettlementRatios } return nil } -func (m *MsgGovManageFeesRequest) GetAddFeeSettlementBuyerFlat() []types.Coin { +func (m *MsgGovManageFeesRequest) GetAddFeeBuyerSettlementFlat() []types.Coin { if m != nil { - return m.AddFeeSettlementBuyerFlat + return m.AddFeeBuyerSettlementFlat } return nil } -func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementBuyerFlat() []types.Coin { +func (m *MsgGovManageFeesRequest) GetRemoveFeeBuyerSettlementFlat() []types.Coin { if m != nil { - return m.RemoveFeeSettlementBuyerFlat + return m.RemoveFeeBuyerSettlementFlat } return nil } -func (m *MsgGovManageFeesRequest) GetAddFeeSettlementBuyerRatios() []FeeRatio { +func (m *MsgGovManageFeesRequest) GetAddFeeBuyerSettlementRatios() []FeeRatio { if m != nil { - return m.AddFeeSettlementBuyerRatios + return m.AddFeeBuyerSettlementRatios } return nil } -func (m *MsgGovManageFeesRequest) GetRemoveFeeSettlementBuyerRatios() []FeeRatio { +func (m *MsgGovManageFeesRequest) GetRemoveFeeBuyerSettlementRatios() []FeeRatio { if m != nil { - return m.RemoveFeeSettlementBuyerRatios + return m.RemoveFeeBuyerSettlementRatios } return nil } @@ -1332,7 +1332,7 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1107 bytes of a gzipped FileDescriptorProto + // 1106 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0x41, 0x6f, 0xe3, 0x44, 0x18, 0xad, 0xd9, 0xa5, 0x6c, 0xbf, 0xee, 0x16, 0x69, 0xda, 0x6d, 0x12, 0x6f, 0xeb, 0xa4, 0x29, 0x87, 0x6a, 0x97, 0xda, 0xdb, 0x22, 0x0a, 0xac, 0xb8, 0x34, 0x0b, 0xd9, 0x53, 0x44, 0x95, 0xaa, @@ -1370,39 +1370,39 @@ var fileDescriptor_e333fcffc093bd1b = []byte{ 0xeb, 0x4b, 0x2f, 0xeb, 0xd8, 0xb6, 0x9b, 0x84, 0x24, 0x37, 0xbf, 0xe9, 0x61, 0x8e, 0xbe, 0x05, 0x3d, 0x20, 0x7d, 0x3a, 0x22, 0x4a, 0xe6, 0x3b, 0xc5, 0x98, 0x37, 0x05, 0x45, 0x86, 0x3c, 0xdb, 0x73, 0xd7, 0xb5, 0x05, 0xf3, 0xdd, 0x5b, 0xf4, 0xdc, 0x70, 0xed, 0xfc, 0x9e, 0x13, 0xe6, 0x77, - 0x6f, 0xd7, 0x73, 0x4c, 0xde, 0x03, 0x23, 0xee, 0x99, 0x45, 0xf7, 0xa4, 0x4f, 0x7c, 0xde, 0x61, - 0xc4, 0xf3, 0x48, 0x20, 0x04, 0x96, 0x8b, 0x09, 0xe8, 0xa2, 0xf5, 0xd3, 0x84, 0xe4, 0x34, 0xe2, + 0x6f, 0xd7, 0x73, 0x4c, 0xde, 0x03, 0x23, 0xee, 0x99, 0x11, 0xcf, 0x23, 0x41, 0x87, 0x45, 0xd7, + 0xa5, 0x4f, 0x7c, 0x2e, 0x04, 0x96, 0x8b, 0x09, 0xe8, 0xa2, 0xf5, 0xd3, 0x88, 0xe4, 0x34, 0xe1, 0x88, 0x44, 0x5c, 0xd8, 0x49, 0x39, 0xc8, 0xd1, 0x79, 0xaf, 0x98, 0xce, 0x76, 0x62, 0x44, 0x29, 0xe5, 0x43, 0x2d, 0xdf, 0x4f, 0x80, 0xb9, 0x4b, 0x59, 0xf9, 0x5e, 0xa4, 0x54, 0xcb, 0xbb, 0x11, 0x4d, 0x42, 0xda, 0x21, 0x50, 0x0a, 0x6e, 0xa9, 0x8d, 0x45, 0x10, 0x86, 0x38, 0xec, 0xce, 0xb4, - 0x26, 0x25, 0x57, 0x16, 0x92, 0xac, 0xe6, 0x7a, 0x94, 0xaa, 0x18, 0xb6, 0x15, 0x2e, 0xbb, 0xc3, - 0x57, 0xf1, 0x61, 0x42, 0xb1, 0xc3, 0xac, 0x4c, 0x7b, 0x6b, 0x84, 0x14, 0xd1, 0x41, 0x3a, 0x50, - 0x53, 0x1b, 0x4b, 0xa9, 0xac, 0x16, 0x53, 0xd9, 0x52, 0xd8, 0x19, 0x0b, 0x79, 0x50, 0xcd, 0xf5, - 0x22, 0x4f, 0xef, 0xfe, 0x42, 0xa7, 0xf7, 0x48, 0x69, 0x4a, 0x9e, 0x5c, 0x00, 0xf5, 0x59, 0xb6, - 0xa4, 0xe0, 0x83, 0x85, 0x04, 0x8d, 0x3c, 0x7f, 0x42, 0x33, 0xf3, 0x36, 0x15, 0x93, 0x78, 0xea, - 0x75, 0x99, 0x99, 0x06, 0x62, 0x80, 0x9d, 0x44, 0x11, 0xe7, 0x7f, 0x98, 0x06, 0x22, 0x2b, 0xcd, - 0x9b, 0x06, 0x42, 0x2e, 0x9e, 0x06, 0xa2, 0x26, 0x7f, 0x1a, 0x4c, 0xb6, 0x28, 0x1c, 0x1c, 0xfe, - 0xbc, 0x06, 0x77, 0x5a, 0xcc, 0x41, 0xe7, 0xb0, 0x92, 0xbc, 0x1e, 0xd1, 0x93, 0xdc, 0xf1, 0x93, - 0x8d, 0x6c, 0xfa, 0x87, 0xc5, 0xc0, 0x42, 0x6f, 0xac, 0xd3, 0x70, 0xed, 0x02, 0x3a, 0xe3, 0xac, - 0x57, 0x40, 0x27, 0x95, 0x00, 0x91, 0x07, 0xab, 0xa9, 0x94, 0x87, 0xf6, 0x67, 0x15, 0x67, 0x62, - 0xa2, 0x6e, 0x16, 0x85, 0x4b, 0xb5, 0x1e, 0xdc, 0x8b, 0x33, 0x22, 0x7a, 0x3c, 0xa3, 0x76, 0x2a, - 0x5e, 0xea, 0x4f, 0x0a, 0x61, 0x27, 0x45, 0xc2, 0x6c, 0x39, 0x57, 0x24, 0x15, 0x4b, 0xe7, 0x8a, - 0xa4, 0xc3, 0x2a, 0xa2, 0x70, 0x3f, 0x9d, 0x47, 0xd1, 0xac, 0x93, 0x50, 0x44, 0x5a, 0xdd, 0x2a, - 0x8c, 0x97, 0x82, 0x43, 0x58, 0x9b, 0x4c, 0xb2, 0xe8, 0xe9, 0x5c, 0x8a, 0xa9, 0x40, 0xac, 0x1f, - 0x2c, 0x50, 0x21, 0x65, 0x7f, 0xd0, 0x60, 0x5d, 0x91, 0x81, 0xd1, 0xc7, 0x73, 0xa9, 0x54, 0xa1, - 0x5a, 0x3f, 0x5a, 0xb4, 0x2c, 0xa7, 0x0d, 0x99, 0xa3, 0x0b, 0xb7, 0x31, 0x19, 0xcc, 0x0b, 0xb7, - 0x31, 0x15, 0xd7, 0xd1, 0x4f, 0x1a, 0x6c, 0xaa, 0x83, 0x38, 0xfa, 0xb4, 0x20, 0x65, 0x26, 0xdf, - 0xeb, 0x9f, 0xdd, 0xa2, 0x52, 0xf6, 0xf3, 0x5a, 0x83, 0x52, 0x4e, 0x9c, 0x47, 0xf3, 0x69, 0xf3, - 0xbe, 0x13, 0xf4, 0x67, 0xb7, 0x29, 0x95, 0x2d, 0xfd, 0xa8, 0xc1, 0x86, 0xea, 0xcb, 0x00, 0x1d, - 0x15, 0x24, 0x9d, 0xfa, 0xd8, 0xd0, 0x3f, 0x59, 0xb8, 0x4e, 0x76, 0x72, 0x05, 0xef, 0x4f, 0x65, - 0x7b, 0x34, 0xeb, 0x02, 0xa8, 0x3f, 0x55, 0xf4, 0xc3, 0x45, 0x4a, 0xa4, 0x72, 0x00, 0x0f, 0x26, - 0xe6, 0x20, 0xb2, 0x66, 0x93, 0x64, 0x3e, 0x30, 0xf4, 0xa7, 0xc5, 0x0b, 0x26, 0xdc, 0xa6, 0x67, - 0xd7, 0x3c, 0xb7, 0x8a, 0x51, 0x3c, 0xcf, 0xad, 0x6a, 0x34, 0x36, 0xc8, 0x9b, 0x6b, 0x43, 0x7b, - 0x7b, 0x6d, 0x68, 0x7f, 0x5f, 0x1b, 0xda, 0xeb, 0x1b, 0x63, 0xe9, 0xed, 0x8d, 0xb1, 0xf4, 0xe7, - 0x8d, 0xb1, 0x04, 0x15, 0x97, 0xe6, 0xf0, 0x9d, 0x68, 0xdf, 0x98, 0x8e, 0xcb, 0x2f, 0x86, 0x5d, - 0xb3, 0x47, 0xfb, 0xd6, 0x18, 0xb4, 0xef, 0xd2, 0xd4, 0x93, 0x75, 0x95, 0xfc, 0x32, 0xd2, 0x5d, - 0x8e, 0x7e, 0x10, 0xf9, 0xe8, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x23, 0x27, 0xbe, 0x30, 0xff, - 0x11, 0x00, 0x00, + 0x26, 0x25, 0x57, 0x16, 0x92, 0xac, 0xe6, 0x7a, 0x94, 0xaa, 0x18, 0xb6, 0x63, 0x97, 0xdd, 0xe1, + 0x2b, 0xc5, 0x61, 0x42, 0xb1, 0xc3, 0xac, 0x08, 0x6f, 0x8d, 0x90, 0x63, 0xea, 0x20, 0x1d, 0xa8, + 0xa5, 0x8c, 0xa9, 0x55, 0x56, 0x8b, 0xa9, 0x6c, 0x25, 0x76, 0x54, 0x42, 0x1e, 0x54, 0x73, 0xbd, + 0xc8, 0xd3, 0xbb, 0xbf, 0xd0, 0xe9, 0x3d, 0x52, 0x9a, 0x92, 0x27, 0x17, 0x40, 0x7d, 0x96, 0x2d, + 0x29, 0xf8, 0x60, 0x21, 0x41, 0x23, 0xcf, 0x9f, 0xd0, 0xcc, 0xbc, 0x4d, 0xc5, 0x24, 0x9e, 0x7a, + 0x5d, 0x66, 0xa6, 0x81, 0x18, 0x60, 0x27, 0x51, 0xc4, 0xf9, 0x1f, 0xa6, 0x81, 0xc8, 0x4a, 0xf3, + 0xa6, 0x81, 0x90, 0x8b, 0xa7, 0x81, 0xa8, 0xc9, 0x9f, 0x06, 0x93, 0x2d, 0x0a, 0x07, 0x87, 0x3f, + 0xaf, 0xc1, 0x9d, 0x16, 0x73, 0xd0, 0x39, 0xac, 0x24, 0xaf, 0x47, 0xf4, 0x24, 0x77, 0xfc, 0x64, + 0x23, 0x9b, 0xfe, 0x61, 0x31, 0xb0, 0xd0, 0x1b, 0xeb, 0x34, 0x5c, 0xbb, 0x80, 0xce, 0x38, 0xeb, + 0x15, 0xd0, 0x49, 0x25, 0x40, 0xe4, 0xc1, 0x6a, 0x2a, 0xe5, 0xa1, 0xfd, 0x59, 0xc5, 0x99, 0x98, + 0xa8, 0x9b, 0x45, 0xe1, 0x52, 0xad, 0x07, 0xf7, 0xe2, 0x8c, 0x88, 0x1e, 0xcf, 0xa8, 0x9d, 0x8a, + 0x97, 0xfa, 0x93, 0x42, 0xd8, 0x49, 0x91, 0x30, 0x5b, 0xce, 0x15, 0x49, 0xc5, 0xd2, 0xb9, 0x22, + 0xe9, 0xb0, 0x8a, 0x28, 0xdc, 0x4f, 0xe7, 0x51, 0x34, 0xeb, 0x24, 0x14, 0x91, 0x56, 0xb7, 0x0a, + 0xe3, 0xa5, 0xe0, 0x10, 0xd6, 0x26, 0x93, 0x2c, 0x7a, 0x3a, 0x97, 0x62, 0x2a, 0x10, 0xeb, 0x07, + 0x0b, 0x54, 0x48, 0xd9, 0x1f, 0x34, 0x58, 0x57, 0x64, 0x60, 0xf4, 0xf1, 0x5c, 0x2a, 0x55, 0xa8, + 0xd6, 0x8f, 0x16, 0x2d, 0xcb, 0x69, 0x43, 0xe6, 0xe8, 0xc2, 0x6d, 0x4c, 0x06, 0xf3, 0xc2, 0x6d, + 0x4c, 0xc5, 0x75, 0xf4, 0x93, 0x06, 0x9b, 0xea, 0x20, 0x8e, 0x3e, 0x2d, 0x48, 0x99, 0xc9, 0xf7, + 0xfa, 0x67, 0xb7, 0xa8, 0x94, 0xfd, 0xbc, 0xd6, 0xa0, 0x94, 0x13, 0xe7, 0xd1, 0x7c, 0xda, 0xbc, + 0xef, 0x04, 0xfd, 0xd9, 0x6d, 0x4a, 0x65, 0x4b, 0x3f, 0x6a, 0xb0, 0xa1, 0xfa, 0x32, 0x40, 0x47, + 0x05, 0x49, 0xa7, 0x3e, 0x36, 0xf4, 0x4f, 0x16, 0xae, 0x93, 0x9d, 0x5c, 0xc1, 0xfb, 0x53, 0xd9, + 0x1e, 0xcd, 0xba, 0x00, 0xea, 0x4f, 0x15, 0xfd, 0x70, 0x91, 0x12, 0xa9, 0x1c, 0xc0, 0x83, 0x89, + 0x39, 0x88, 0xac, 0xd9, 0x24, 0x99, 0x0f, 0x0c, 0xfd, 0x69, 0xf1, 0x82, 0x09, 0xb7, 0xe9, 0xd9, + 0x35, 0xcf, 0xad, 0x62, 0x14, 0xcf, 0x73, 0xab, 0x1a, 0x8d, 0x0d, 0xf2, 0xe6, 0xda, 0xd0, 0xde, + 0x5e, 0x1b, 0xda, 0xdf, 0xd7, 0x86, 0xf6, 0xfa, 0xc6, 0x58, 0x7a, 0x7b, 0x63, 0x2c, 0xfd, 0x79, + 0x63, 0x2c, 0x41, 0xc5, 0xa5, 0x39, 0x7c, 0x27, 0xda, 0x37, 0xa6, 0xe3, 0xf2, 0x8b, 0x61, 0xd7, + 0xec, 0xd1, 0xbe, 0x35, 0x06, 0xed, 0xbb, 0x34, 0xf5, 0x64, 0x5d, 0x25, 0xbf, 0x8c, 0x74, 0x97, + 0xa3, 0x1f, 0x44, 0x3e, 0xfa, 0x37, 0x00, 0x00, 0xff, 0xff, 0xd0, 0x7d, 0x80, 0x24, 0xff, 0x11, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2654,10 +2654,10 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l - if len(m.RemoveFeeSettlementBuyerRatios) > 0 { - for iNdEx := len(m.RemoveFeeSettlementBuyerRatios) - 1; iNdEx >= 0; iNdEx-- { + if len(m.RemoveFeeBuyerSettlementRatios) > 0 { + for iNdEx := len(m.RemoveFeeBuyerSettlementRatios) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.RemoveFeeSettlementBuyerRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.RemoveFeeBuyerSettlementRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -2668,10 +2668,10 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) dAtA[i] = 0x6a } } - if len(m.AddFeeSettlementBuyerRatios) > 0 { - for iNdEx := len(m.AddFeeSettlementBuyerRatios) - 1; iNdEx >= 0; iNdEx-- { + if len(m.AddFeeBuyerSettlementRatios) > 0 { + for iNdEx := len(m.AddFeeBuyerSettlementRatios) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.AddFeeSettlementBuyerRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.AddFeeBuyerSettlementRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -2682,10 +2682,10 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) dAtA[i] = 0x62 } } - if len(m.RemoveFeeSettlementBuyerFlat) > 0 { - for iNdEx := len(m.RemoveFeeSettlementBuyerFlat) - 1; iNdEx >= 0; iNdEx-- { + if len(m.RemoveFeeBuyerSettlementFlat) > 0 { + for iNdEx := len(m.RemoveFeeBuyerSettlementFlat) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.RemoveFeeSettlementBuyerFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.RemoveFeeBuyerSettlementFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -2696,10 +2696,10 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) dAtA[i] = 0x5a } } - if len(m.AddFeeSettlementBuyerFlat) > 0 { - for iNdEx := len(m.AddFeeSettlementBuyerFlat) - 1; iNdEx >= 0; iNdEx-- { + if len(m.AddFeeBuyerSettlementFlat) > 0 { + for iNdEx := len(m.AddFeeBuyerSettlementFlat) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.AddFeeSettlementBuyerFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.AddFeeBuyerSettlementFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -2710,10 +2710,10 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) dAtA[i] = 0x52 } } - if len(m.RemoveFeeSettlementSellerRatios) > 0 { - for iNdEx := len(m.RemoveFeeSettlementSellerRatios) - 1; iNdEx >= 0; iNdEx-- { + if len(m.RemoveFeeSellerSettlementRatios) > 0 { + for iNdEx := len(m.RemoveFeeSellerSettlementRatios) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.RemoveFeeSettlementSellerRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.RemoveFeeSellerSettlementRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -2724,10 +2724,10 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) dAtA[i] = 0x4a } } - if len(m.AddFeeSettlementSellerRatios) > 0 { - for iNdEx := len(m.AddFeeSettlementSellerRatios) - 1; iNdEx >= 0; iNdEx-- { + if len(m.AddFeeSellerSettlementRatios) > 0 { + for iNdEx := len(m.AddFeeSellerSettlementRatios) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.AddFeeSettlementSellerRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.AddFeeSellerSettlementRatios[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -2738,10 +2738,10 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) dAtA[i] = 0x42 } } - if len(m.RemoveFeeSettlementSellerFlat) > 0 { - for iNdEx := len(m.RemoveFeeSettlementSellerFlat) - 1; iNdEx >= 0; iNdEx-- { + if len(m.RemoveFeeSellerSettlementFlat) > 0 { + for iNdEx := len(m.RemoveFeeSellerSettlementFlat) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.RemoveFeeSettlementSellerFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.RemoveFeeSellerSettlementFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -2752,10 +2752,10 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) dAtA[i] = 0x3a } } - if len(m.AddFeeSettlementSellerFlat) > 0 { - for iNdEx := len(m.AddFeeSettlementSellerFlat) - 1; iNdEx >= 0; iNdEx-- { + if len(m.AddFeeSellerSettlementFlat) > 0 { + for iNdEx := len(m.AddFeeSellerSettlementFlat) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.AddFeeSettlementSellerFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.AddFeeSellerSettlementFlat[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -3203,50 +3203,50 @@ func (m *MsgGovManageFeesRequest) Size() (n int) { n += 1 + l + sovTx(uint64(l)) } } - if len(m.AddFeeSettlementSellerFlat) > 0 { - for _, e := range m.AddFeeSettlementSellerFlat { + if len(m.AddFeeSellerSettlementFlat) > 0 { + for _, e := range m.AddFeeSellerSettlementFlat { l = e.Size() n += 1 + l + sovTx(uint64(l)) } } - if len(m.RemoveFeeSettlementSellerFlat) > 0 { - for _, e := range m.RemoveFeeSettlementSellerFlat { + if len(m.RemoveFeeSellerSettlementFlat) > 0 { + for _, e := range m.RemoveFeeSellerSettlementFlat { l = e.Size() n += 1 + l + sovTx(uint64(l)) } } - if len(m.AddFeeSettlementSellerRatios) > 0 { - for _, e := range m.AddFeeSettlementSellerRatios { + if len(m.AddFeeSellerSettlementRatios) > 0 { + for _, e := range m.AddFeeSellerSettlementRatios { l = e.Size() n += 1 + l + sovTx(uint64(l)) } } - if len(m.RemoveFeeSettlementSellerRatios) > 0 { - for _, e := range m.RemoveFeeSettlementSellerRatios { + if len(m.RemoveFeeSellerSettlementRatios) > 0 { + for _, e := range m.RemoveFeeSellerSettlementRatios { l = e.Size() n += 1 + l + sovTx(uint64(l)) } } - if len(m.AddFeeSettlementBuyerFlat) > 0 { - for _, e := range m.AddFeeSettlementBuyerFlat { + if len(m.AddFeeBuyerSettlementFlat) > 0 { + for _, e := range m.AddFeeBuyerSettlementFlat { l = e.Size() n += 1 + l + sovTx(uint64(l)) } } - if len(m.RemoveFeeSettlementBuyerFlat) > 0 { - for _, e := range m.RemoveFeeSettlementBuyerFlat { + if len(m.RemoveFeeBuyerSettlementFlat) > 0 { + for _, e := range m.RemoveFeeBuyerSettlementFlat { l = e.Size() n += 1 + l + sovTx(uint64(l)) } } - if len(m.AddFeeSettlementBuyerRatios) > 0 { - for _, e := range m.AddFeeSettlementBuyerRatios { + if len(m.AddFeeBuyerSettlementRatios) > 0 { + for _, e := range m.AddFeeBuyerSettlementRatios { l = e.Size() n += 1 + l + sovTx(uint64(l)) } } - if len(m.RemoveFeeSettlementBuyerRatios) > 0 { - for _, e := range m.RemoveFeeSettlementBuyerRatios { + if len(m.RemoveFeeBuyerSettlementRatios) > 0 { + for _, e := range m.RemoveFeeBuyerSettlementRatios { l = e.Size() n += 1 + l + sovTx(uint64(l)) } @@ -4857,7 +4857,7 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AddFeeSettlementSellerFlat", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AddFeeSellerSettlementFlat", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -4884,14 +4884,14 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.AddFeeSettlementSellerFlat = append(m.AddFeeSettlementSellerFlat, types.Coin{}) - if err := m.AddFeeSettlementSellerFlat[len(m.AddFeeSettlementSellerFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.AddFeeSellerSettlementFlat = append(m.AddFeeSellerSettlementFlat, types.Coin{}) + if err := m.AddFeeSellerSettlementFlat[len(m.AddFeeSellerSettlementFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeSettlementSellerFlat", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeSellerSettlementFlat", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -4918,14 +4918,14 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.RemoveFeeSettlementSellerFlat = append(m.RemoveFeeSettlementSellerFlat, types.Coin{}) - if err := m.RemoveFeeSettlementSellerFlat[len(m.RemoveFeeSettlementSellerFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.RemoveFeeSellerSettlementFlat = append(m.RemoveFeeSellerSettlementFlat, types.Coin{}) + if err := m.RemoveFeeSellerSettlementFlat[len(m.RemoveFeeSellerSettlementFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AddFeeSettlementSellerRatios", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AddFeeSellerSettlementRatios", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -4952,14 +4952,14 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.AddFeeSettlementSellerRatios = append(m.AddFeeSettlementSellerRatios, FeeRatio{}) - if err := m.AddFeeSettlementSellerRatios[len(m.AddFeeSettlementSellerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.AddFeeSellerSettlementRatios = append(m.AddFeeSellerSettlementRatios, FeeRatio{}) + if err := m.AddFeeSellerSettlementRatios[len(m.AddFeeSellerSettlementRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 9: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeSettlementSellerRatios", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeSellerSettlementRatios", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -4986,14 +4986,14 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.RemoveFeeSettlementSellerRatios = append(m.RemoveFeeSettlementSellerRatios, FeeRatio{}) - if err := m.RemoveFeeSettlementSellerRatios[len(m.RemoveFeeSettlementSellerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.RemoveFeeSellerSettlementRatios = append(m.RemoveFeeSellerSettlementRatios, FeeRatio{}) + if err := m.RemoveFeeSellerSettlementRatios[len(m.RemoveFeeSellerSettlementRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 10: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AddFeeSettlementBuyerFlat", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AddFeeBuyerSettlementFlat", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -5020,14 +5020,14 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.AddFeeSettlementBuyerFlat = append(m.AddFeeSettlementBuyerFlat, types.Coin{}) - if err := m.AddFeeSettlementBuyerFlat[len(m.AddFeeSettlementBuyerFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.AddFeeBuyerSettlementFlat = append(m.AddFeeBuyerSettlementFlat, types.Coin{}) + if err := m.AddFeeBuyerSettlementFlat[len(m.AddFeeBuyerSettlementFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 11: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeSettlementBuyerFlat", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeBuyerSettlementFlat", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -5054,14 +5054,14 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.RemoveFeeSettlementBuyerFlat = append(m.RemoveFeeSettlementBuyerFlat, types.Coin{}) - if err := m.RemoveFeeSettlementBuyerFlat[len(m.RemoveFeeSettlementBuyerFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.RemoveFeeBuyerSettlementFlat = append(m.RemoveFeeBuyerSettlementFlat, types.Coin{}) + if err := m.RemoveFeeBuyerSettlementFlat[len(m.RemoveFeeBuyerSettlementFlat)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 12: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AddFeeSettlementBuyerRatios", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AddFeeBuyerSettlementRatios", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -5088,14 +5088,14 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.AddFeeSettlementBuyerRatios = append(m.AddFeeSettlementBuyerRatios, FeeRatio{}) - if err := m.AddFeeSettlementBuyerRatios[len(m.AddFeeSettlementBuyerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.AddFeeBuyerSettlementRatios = append(m.AddFeeBuyerSettlementRatios, FeeRatio{}) + if err := m.AddFeeBuyerSettlementRatios[len(m.AddFeeBuyerSettlementRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 13: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeSettlementBuyerRatios", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeBuyerSettlementRatios", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -5122,8 +5122,8 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.RemoveFeeSettlementBuyerRatios = append(m.RemoveFeeSettlementBuyerRatios, FeeRatio{}) - if err := m.RemoveFeeSettlementBuyerRatios[len(m.RemoveFeeSettlementBuyerRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.RemoveFeeBuyerSettlementRatios = append(m.RemoveFeeBuyerSettlementRatios, FeeRatio{}) + if err := m.RemoveFeeBuyerSettlementRatios[len(m.RemoveFeeBuyerSettlementRatios)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex From b5803000b3ea30a8af1548ed14c6e5331b2f5815 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 1 Sep 2023 11:20:30 -0600 Subject: [PATCH 065/309] [1658]: Tweak the GetSigners test failure message. --- x/exchange/msg_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index ac30030b28..ca81ab0bc5 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -106,7 +106,7 @@ func TestAllMsgsGetSigners(t *testing.T) { for _, msg := range allRequestMsgs { typeName := getTypeName(msg) // If this fails, a maker needs to be defined above for the missing msg type. - assert.True(t, hasMaker[typeName], "whether a GetSigners test exists for %s", typeName) + assert.True(t, hasMaker[typeName], "There is not a GetSigners test case for %s", typeName) } }) } From 421df54a03d2f72d06cc8515e77a8dc1d3544e15 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 1 Sep 2023 12:19:11 -0600 Subject: [PATCH 066/309] [1658]: get/set store stuff for the settlement flat fees. --- x/exchange/keeper/keeper.go | 27 ++---- x/exchange/keeper/market.go | 174 ++++++++++++++++++++++-------------- 2 files changed, 110 insertions(+), 91 deletions(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index d7abd3722f..da728ac2fa 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -1,10 +1,7 @@ package keeper import ( - sdkmath "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -47,24 +44,10 @@ func getAllKeys(store sdk.KVStore, pre []byte) [][]byte { return keys } -// getAllCoins gets all the coin entries from the store with the given prefix. -// The denom comes from the part of the key after the prefix, and the amount comes from the values. -func getAllCoins(store sdk.KVStore, pre []byte) []sdk.Coin { - // Using a prefixed store here so that iter.Key() doesn't contain the prefix. - pStore := prefix.NewStore(store, pre) - iter := pStore.Iterator(nil, nil) - defer iter.Close() - - var coins []sdk.Coin - for ; iter.Valid(); iter.Next() { - denom := string(iter.Key()) - value := string(iter.Value()) - amt, ok := sdkmath.NewIntFromString(value) - if !ok { - continue - } - coins = append(coins, sdk.Coin{Denom: denom, Amount: amt}) +// deleteAll deletes all keys that have the given prefix. +func deleteAll(store sdk.KVStore, pre []byte) { + keys := getAllKeys(store, pre) + for _, key := range keys { + store.Delete(key) } - - return coins } diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 45130cb673..56a6b6d29c 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -2,29 +2,43 @@ package keeper import ( sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" ) -// deleteCreateAskFlatFees deletes all the create-ask flat fees for the given market. -func deleteCreateAskFlatFees(store sdk.KVStore, marketID uint32) { - keys := getAllKeys(store, MakeKeyPrefixMarketCreateAskFlatFee(marketID)) - for _, key := range keys { - store.Delete(key) - } +// flatFeeKeyMakers are the key and prefix maker funcs for a specific flat fee entry. +type flatFeeKeyMakers struct { + key func(marketID uint32, denom string) []byte + prefix func(marketID uint32) []byte } -// setCreateAskFlatFee sets a create-ask flat fee option in the store. -func setCreateAskFlatFee(store sdk.KVStore, marketID uint32, coin sdk.Coin) { - key := MakeKeyMarketCreateAskFlatFee(marketID, coin.Denom) - value := coin.Amount.String() - store.Set(key, []byte(value)) -} +var ( + // createAskKeyMakers are the key and prefix makers for the create-ask flat fees. + createAskKeyMakers = flatFeeKeyMakers{ + key: MakeKeyMarketCreateAskFlatFee, + prefix: MakeKeyPrefixMarketCreateAskFlatFee, + } + // createBidKeyMakers are the key and prefix makers for the create-bid flat fees. + createBidKeyMakers = flatFeeKeyMakers{ + key: MakeKeyMarketCreateBidFlatFee, + prefix: MakeKeyPrefixMarketCreateBidFlatFee, + } + // sellerSettlementFlatKeyMakers are the key and prefix makers for the seller settlement flat fees. + sellerSettlementFlatKeyMakers = flatFeeKeyMakers{ + key: MakeKeyMarketSellerSettlementFlatFee, + prefix: MakeKeyPrefixMarketSellerSettlementFlatFee, + } + // sellerSettlementFlatKeyMakers are the key and prefix makers for the buyer settlement flat fees. + buyerSettlementFlatKeyMakers = flatFeeKeyMakers{ + key: MakeKeyMarketBuyerSettlementFlatFee, + prefix: MakeKeyPrefixMarketBuyerSettlementFlatFee, + } +) -// getCreateAskFlatFee gets a create-ask flat fee option from the store. -// Returns nil if either no entry for that denom exists, or the entry does exist, but can't be read. -func getCreateAskFlatFee(store sdk.KVStore, marketID uint32, denom string) *sdk.Coin { - key := MakeKeyMarketCreateAskFlatFee(marketID, denom) +// getFlatFee is a generic getter for a flat fee coin entry. +func getFlatFee(store sdk.KVStore, marketID uint32, denom string, maker flatFeeKeyMakers) *sdk.Coin { + key := maker.key(marketID, denom) if store.Has(key) { value := string(store.Get(key)) amt, ok := sdkmath.NewIntFromString(value) @@ -35,81 +49,103 @@ func getCreateAskFlatFee(store sdk.KVStore, marketID uint32, denom string) *sdk. return nil } -// SetCreateAskFlatFees sets the create-ask flat fees for a market. -func (k Keeper) SetCreateAskFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { - store := ctx.KVStore(k.storeKey) - deleteCreateAskFlatFees(store, marketID) +// setFlatFee is a generic setter for a single flat fee coin entry. +func setFlatFee(store sdk.KVStore, marketID uint32, coin sdk.Coin, maker flatFeeKeyMakers) { + key := maker.key(marketID, coin.Denom) + value := coin.Amount.String() + store.Set(key, []byte(value)) +} - for _, coin := range options { - setCreateAskFlatFee(store, marketID, coin) +// getAllCoins gets all the coin entries from the store with the given prefix. +// The denom comes from the part of the key after the prefix, and the amount comes from the values. +func (k Keeper) getAllCoins(ctx sdk.Context, pre []byte) []sdk.Coin { + // Using a prefixed store here so that iter.Key() doesn't contain the prefix (just the denom). + pStore := prefix.NewStore(ctx.KVStore(k.storeKey), pre) + iter := pStore.Iterator(nil, nil) + defer iter.Close() + + var coins []sdk.Coin + for ; iter.Valid(); iter.Next() { + denom := string(iter.Key()) + value := string(iter.Value()) + amt, ok := sdkmath.NewIntFromString(value) + if !ok { + continue + } + coins = append(coins, sdk.Coin{Denom: denom, Amount: amt}) } -} -// GetCreateAskFlatFees gets the create-ask flat fees for a market. -func (k Keeper) GetCreateAskFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return getAllCoins(ctx.KVStore(k.storeKey), MakeKeyPrefixMarketCreateAskFlatFee(marketID)) + return coins } -// IsAcceptableCreateAskFlatFee returns true if the provide fee has a denom accepted as a create-ask flat fee, -// and the fee amount is at least as much as the required amount of that denom. -func (k Keeper) IsAcceptableCreateAskFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { - reqFee := getCreateAskFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom) - if reqFee == nil { - return false +// setAllCoins is a generic setter for a set of flat fee options. +// This will delete all previous options then save the ones provided. I.e. if options doesn't have a +// denom that currently exists in the store, those denoms will no longer be in the store after this. +func (k Keeper) setAllCoins(ctx sdk.Context, marketID uint32, options []sdk.Coin, maker flatFeeKeyMakers) { + store := ctx.KVStore(k.storeKey) + deleteAll(store, maker.prefix(marketID)) + for _, coin := range options { + setFlatFee(store, marketID, coin, maker) } - return reqFee.Amount.LTE(fee.Amount) } -// deleteCreateBidFlatFees deletes all the create-bid flat fees for the given market. -func deleteCreateBidFlatFees(store sdk.KVStore, marketID uint32) { - keys := getAllKeys(store, MakeKeyPrefixMarketCreateBidFlatFee(marketID)) - for _, key := range keys { - store.Delete(key) - } +// SetCreateAskFlatFees sets the create-ask flat fees for a market. +func (k Keeper) SetCreateAskFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { + k.setAllCoins(ctx, marketID, options, createAskKeyMakers) } -// setCreateBidFlatFee sets a create-Bid flat fee option in the store. -func setCreateBidFlatFee(store sdk.KVStore, marketID uint32, coin sdk.Coin) { - key := MakeKeyMarketCreateBidFlatFee(marketID, coin.Denom) - value := coin.Amount.String() - store.Set(key, []byte(value)) +// GetCreateAskFlatFees gets the create-ask flat fee options for a market. +func (k Keeper) GetCreateAskFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { + return k.getAllCoins(ctx, createAskKeyMakers.prefix(marketID)) } -// getCreateBidFlatFee gets a create-bid flat fee option from the store. -// Returns nil if either no entry for that denom exists, or the entry does exist, but can't be read. -func getCreateBidFlatFee(store sdk.KVStore, marketID uint32, denom string) *sdk.Coin { - key := MakeKeyMarketCreateBidFlatFee(marketID, denom) - if store.Has(key) { - value := string(store.Get(key)) - amt, ok := sdkmath.NewIntFromString(value) - if ok { - return &sdk.Coin{Denom: denom, Amount: amt} - } - } - return nil +// IsAcceptableCreateAskFlatFee returns true if the provide fee has a denom accepted as a create-ask flat fee, +// and the fee amount is at least as much as the required amount of that denom. +func (k Keeper) IsAcceptableCreateAskFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { + reqFee := getFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom, createAskKeyMakers) + return reqFee != nil && reqFee.Amount.LTE(fee.Amount) } // SetCreateBidFlatFees sets the create-bid flat fees for a market. func (k Keeper) SetCreateBidFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { - store := ctx.KVStore(k.storeKey) - deleteCreateBidFlatFees(store, marketID) - - for _, coin := range options { - setCreateBidFlatFee(store, marketID, coin) - } + k.setAllCoins(ctx, marketID, options, createBidKeyMakers) } -// GetCreateBidFlatFees gets the create-bid flat fees for a market. +// GetCreateBidFlatFees gets the create-bid flat fee options for a market. func (k Keeper) GetCreateBidFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return getAllCoins(ctx.KVStore(k.storeKey), MakeKeyPrefixMarketCreateBidFlatFee(marketID)) + return k.getAllCoins(ctx, createBidKeyMakers.prefix(marketID)) } // IsAcceptableCreateBidFlatFee returns true if the provide fee has a denom accepted as a create-bid flat fee, // and the fee amount is at least as much as the required amount of that denom. func (k Keeper) IsAcceptableCreateBidFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { - reqFee := getCreateBidFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom) - if reqFee == nil { - return false - } - return reqFee.Amount.LTE(fee.Amount) + reqFee := getFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom, createBidKeyMakers) + return reqFee != nil && reqFee.Amount.LTE(fee.Amount) +} + +// SetSellerSettlementFlatFees sets the seller settlement flat fees for a market. +func (k Keeper) SetSellerSettlementFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { + k.setAllCoins(ctx, marketID, options, sellerSettlementFlatKeyMakers) +} + +// GetSellerSettlementFlatFees gets the seller settlement flat fee options for a market. +func (k Keeper) GetSellerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { + return k.getAllCoins(ctx, sellerSettlementFlatKeyMakers.prefix(marketID)) +} + +// IsAcceptableSellerSettlementFlatFee returns true if the provide fee has a denom accepted as a seller settlement +// flat fee, and the fee amount is at least as much as the required amount of that denom. +func (k Keeper) IsAcceptableSellerSettlementFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { + reqFee := getFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom, sellerSettlementFlatKeyMakers) + return reqFee != nil && reqFee.Amount.LTE(fee.Amount) +} + +// SetBuyerSettlementFlatFees sets the buyer settlement flat fees for a market. +func (k Keeper) SetBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { + k.setAllCoins(ctx, marketID, options, buyerSettlementFlatKeyMakers) +} + +// GetBuyerSettlementFlatFees gets the buyer settlement flat fee options for a market. +func (k Keeper) GetBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { + return k.getAllCoins(ctx, buyerSettlementFlatKeyMakers.prefix(marketID)) } From 876ffa3ad44e7c2de7acbdaf47cf5b093187ff2b Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 1 Sep 2023 12:35:31 -0600 Subject: [PATCH 067/309] [1658]: Rename the key prefix makers to start with Get to set them apart from the full key makers a bit more. --- x/exchange/keeper/keys.go | 64 +++++----- x/exchange/keeper/keys_test.go | 208 ++++++++++++++++----------------- x/exchange/keeper/market.go | 8 +- x/exchange/keeper/params.go | 4 +- 4 files changed, 142 insertions(+), 142 deletions(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 4b844da8f1..e3ae5c9240 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -148,8 +148,8 @@ func keyPrefixParamsSplit(extraCap int) []byte { return prepKey(KeyTypeParams, []byte("split"), extraCap) } -// MakeKeyPrefixParamsSplit creates the key prefix for all params splits entries. -func MakeKeyPrefixParamsSplit() []byte { +// GetKeyPrefixParamsSplit creates the key prefix for all params splits entries. +func GetKeyPrefixParamsSplit() []byte { return keyPrefixParamsSplit(0) } @@ -166,8 +166,8 @@ func keyPrefixMarket(marketID uint32, extraCap int) []byte { return prepKey(KeyTypeMarket, uint32Bz(marketID), extraCap) } -// MakeKeyPrefixMarket creates the key prefix for all of a market's entries. -func MakeKeyPrefixMarket(marketID uint32) []byte { +// GetKeyPrefixMarket creates the key prefix for all of a market's entries. +func GetKeyPrefixMarket(marketID uint32) []byte { return keyPrefixMarket(marketID, 0) } @@ -184,8 +184,8 @@ func marketKeyPrefixCreateAskFlatFee(marketID uint32, extraCap int) []byte { return keyPrefixMarketType(marketID, MarketKeyTypeCreateAskFlat, extraCap) } -// MakeKeyPrefixMarketCreateAskFlatFee creates the key prefix for the create-ask flat fees for the provided market. -func MakeKeyPrefixMarketCreateAskFlatFee(marketID uint32) []byte { +// GetKeyPrefixMarketCreateAskFlatFee creates the key prefix for the create-ask flat fees for the provided market. +func GetKeyPrefixMarketCreateAskFlatFee(marketID uint32) []byte { return marketKeyPrefixCreateAskFlatFee(marketID, 0) } @@ -201,8 +201,8 @@ func marketKeyPrefixCreateBidFlatFee(marketID uint32, extraCap int) []byte { return keyPrefixMarketType(marketID, MarketKeyTypeCreateBidFlat, extraCap) } -// MakeKeyPrefixMarketCreateBidFlatFee creates the key prefix for the create-bid flat fees for the provided market. -func MakeKeyPrefixMarketCreateBidFlatFee(marketID uint32) []byte { +// GetKeyPrefixMarketCreateBidFlatFee creates the key prefix for the create-bid flat fees for the provided market. +func GetKeyPrefixMarketCreateBidFlatFee(marketID uint32) []byte { return marketKeyPrefixCreateBidFlatFee(marketID, 0) } @@ -218,8 +218,8 @@ func marketKeyPrefixSellerSettlementFlatFee(marketID uint32, extraCap int) []byt return keyPrefixMarketType(marketID, MarketKeyTypeSellerSettlementFlat, extraCap) } -// MakeKeyPrefixMarketSellerSettlementFlatFee creates the key prefix for a market's seller settlement flat fees. -func MakeKeyPrefixMarketSellerSettlementFlatFee(marketID uint32) []byte { +// GetKeyPrefixMarketSellerSettlementFlatFee creates the key prefix for a market's seller settlement flat fees. +func GetKeyPrefixMarketSellerSettlementFlatFee(marketID uint32) []byte { return marketKeyPrefixSellerSettlementFlatFee(marketID, 0) } @@ -235,8 +235,8 @@ func marketKeyPrefixSellerSettlementRatio(marketID uint32, extraCap int) []byte return keyPrefixMarketType(marketID, MarketKeyTypeSellerSettlementRatio, extraCap) } -// MakeKeyPrefixMarketSellerSettlementRatio creates the key prefix for a market's seller settlement fee ratios. -func MakeKeyPrefixMarketSellerSettlementRatio(marketID uint32) []byte { +// GetKeyPrefixMarketSellerSettlementRatio creates the key prefix for a market's seller settlement fee ratios. +func GetKeyPrefixMarketSellerSettlementRatio(marketID uint32) []byte { return marketKeyPrefixSellerSettlementRatio(marketID, 0) } @@ -254,8 +254,8 @@ func marketKeyPrefixBuyerSettlementFlatFee(marketID uint32, extraCap int) []byte return keyPrefixMarketType(marketID, MarketKeyTypeBuyerSettlementFlat, extraCap) } -// MakeKeyPrefixMarketBuyerSettlementFlatFee creates the key prefix for a market's buyer settlement flat fees. -func MakeKeyPrefixMarketBuyerSettlementFlatFee(marketID uint32) []byte { +// GetKeyPrefixMarketBuyerSettlementFlatFee creates the key prefix for a market's buyer settlement flat fees. +func GetKeyPrefixMarketBuyerSettlementFlatFee(marketID uint32) []byte { return marketKeyPrefixBuyerSettlementFlatFee(marketID, 0) } @@ -271,8 +271,8 @@ func marketKeyPrefixBuyerSettlementRatio(marketID uint32, extraCap int) []byte { return keyPrefixMarketType(marketID, MarketKeyTypeBuyerSettlementRatio, extraCap) } -// MakeKeyPrefixMarketBuyerSettlementRatio creates the key prefix for a market's buyer settlement fee ratios. -func MakeKeyPrefixMarketBuyerSettlementRatio(marketID uint32) []byte { +// GetKeyPrefixMarketBuyerSettlementRatio creates the key prefix for a market's buyer settlement fee ratios. +func GetKeyPrefixMarketBuyerSettlementRatio(marketID uint32) []byte { return marketKeyPrefixBuyerSettlementRatio(marketID, 0) } @@ -310,13 +310,13 @@ func marketKeyPrefixPermissionsForAddress(marketID uint32, addr sdk.AccAddress, return rv } -// MakeKeyPrefixMarketPermissions creates the key prefix for a market's permissions. -func MakeKeyPrefixMarketPermissions(marketID uint32) []byte { +// GetKeyPrefixMarketPermissions creates the key prefix for a market's permissions. +func GetKeyPrefixMarketPermissions(marketID uint32) []byte { return marketKeyPrefixPermissions(marketID, 0) } -// MakeKeyPrefixMarketPermissionsForAddress creates the key prefix for an address' permissions in a given market. -func MakeKeyPrefixMarketPermissionsForAddress(marketID uint32, addr sdk.AccAddress) []byte { +// GetKeyPrefixMarketPermissionsForAddress creates the key prefix for an address' permissions in a given market. +func GetKeyPrefixMarketPermissionsForAddress(marketID uint32, addr sdk.AccAddress) []byte { return marketKeyPrefixPermissionsForAddress(marketID, addr, 0) } @@ -354,8 +354,8 @@ func keyPrefixOrder(orderID uint64, extraCap int) []byte { return prepKey(KeyTypeOrder, uint64Bz(orderID), extraCap) } -// MakeKeyPrefixOrder creates the key prefix for the given order id. -func MakeKeyPrefixOrder(orderID uint64) []byte { +// GetKeyPrefixOrder creates the key prefix for the given order id. +func GetKeyPrefixOrder(orderID uint64) []byte { return keyPrefixOrder(orderID, 0) } @@ -371,8 +371,8 @@ func indexPrefixMarketToOrder(marketID uint32, extraCap int) []byte { return prepKey(KeyTypeMarketToOrderIndex, uint32Bz(marketID), extraCap) } -// MakeIndexPrefixMarketToOrder creates the prefix for the market to order index limited ot the given market id. -func MakeIndexPrefixMarketToOrder(marketID uint32) []byte { +// GetIndexKeyPrefixMarketToOrder creates the prefix for the market to order index limited ot the given market id. +func GetIndexKeyPrefixMarketToOrder(marketID uint32) []byte { return indexPrefixMarketToOrder(marketID, 0) } @@ -391,8 +391,8 @@ func indexPrefixAddressToOrder(addr sdk.AccAddress, extraCap int) []byte { return prepKey(KeyTypeAddressToOrderIndex, address.MustLengthPrefix(addr), extraCap) } -// MakeIndexPrefixAddressToOrder creates a key prefix for the address to order index limited to the given address. -func MakeIndexPrefixAddressToOrder(addr sdk.AccAddress) []byte { +// GetIndexKeyPrefixAddressToOrder creates a key prefix for the address to order index limited to the given address. +func GetIndexKeyPrefixAddressToOrder(addr sdk.AccAddress) []byte { return indexPrefixAddressToOrder(addr, 0) } @@ -408,20 +408,20 @@ func indexPrefixAssetToOrder(assetDenom string, extraCap int) []byte { return prepKey(KeyTypeAssetToOrderIndex, []byte(assetDenom), extraCap) } -// MakeIndexPrefixAssetToOrder creates a key prefix for the asset to order index limited to the given asset. -func MakeIndexPrefixAssetToOrder(assetDenom string) []byte { +// GetIndexKeyPrefixAssetToOrder creates a key prefix for the asset to order index limited to the given asset. +func GetIndexKeyPrefixAssetToOrder(assetDenom string) []byte { return indexPrefixAssetToOrder(assetDenom, 0) } -// MakeIndexPrefixAssetToOrderAsks creates a key prefix for the asset to orders limited to the given asset and only ask orders. -func MakeIndexPrefixAssetToOrderAsks(assetDenom string) []byte { +// GetIndexKeyPrefixAssetToOrderAsks creates a key prefix for the asset to orders limited to the given asset and only ask orders. +func GetIndexKeyPrefixAssetToOrderAsks(assetDenom string) []byte { rv := indexPrefixAssetToOrder(assetDenom, 1) rv = append(rv, OrderKeyTypeAsk) return rv } -// MakeIndexPrefixAssetToOrderBids creates a key prefix for the asset to orders limited to the given asset and only bid orders. -func MakeIndexPrefixAssetToOrderBids(assetDenom string) []byte { +// GetIndexKeyPrefixAssetToOrderBids creates a key prefix for the asset to orders limited to the given asset and only bid orders. +func GetIndexKeyPrefixAssetToOrderBids(assetDenom string) []byte { rv := indexPrefixAssetToOrder(assetDenom, 1) rv = append(rv, OrderKeyTypeBid) return rv diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 703a1a67d1..c3072aee06 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -123,14 +123,14 @@ func TestKeyTypeUniqueness(t *testing.T) { } } -func TestMakeKeyPrefixParamsSplit(t *testing.T) { +func TestGetKeyPrefixParamsSplit(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixParamsSplit() + return keeper.GetKeyPrefixParamsSplit() }, expected: []byte{keeper.KeyTypeParams, 's', 'p', 'l', 'i', 't'}, } - checkKey(t, ktc, "MakeKeyPrefixParamsSplit") + checkKey(t, ktc, "GetKeyPrefixParamsSplit") } func TestMakeKeyParamsSplit(t *testing.T) { @@ -164,7 +164,7 @@ func TestMakeKeyParamsSplit(t *testing.T) { }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeKeyPrefixParamsSplit", value: keeper.MakeKeyPrefixParamsSplit()}, + {name: "GetKeyPrefixParamsSplit", value: keeper.GetKeyPrefixParamsSplit()}, }, } checkKey(t, ktc, "MakeKeyParamsSplit(%q)", tc.denom) @@ -172,7 +172,7 @@ func TestMakeKeyParamsSplit(t *testing.T) { } } -func TestMakeKeyPrefixMarket(t *testing.T) { +func TestGetKeyPrefixMarket(t *testing.T) { tests := []struct { name string marketID uint32 @@ -224,16 +224,16 @@ func TestMakeKeyPrefixMarket(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarket(tc.marketID) + return keeper.GetKeyPrefixMarket(tc.marketID) }, expected: tc.expected, } - checkKey(t, ktc, "MakeKeyPrefixMarket(%d)", tc.marketID) + checkKey(t, ktc, "GetKeyPrefixMarket(%d)", tc.marketID) }) } } -func TestMakeKeyPrefixMarketCreateAskFlatFee(t *testing.T) { +func TestGetKeyPrefixMarketCreateAskFlatFee(t *testing.T) { marketTypeByte := keeper.MarketKeyTypeCreateAskFlat tests := []struct { @@ -287,14 +287,14 @@ func TestMakeKeyPrefixMarketCreateAskFlatFee(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarketCreateAskFlatFee(tc.marketID) + return keeper.GetKeyPrefixMarketCreateAskFlatFee(tc.marketID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + {name: "GetKeyPrefixMarket", value: keeper.GetKeyPrefixMarket(tc.marketID)}, }, } - checkKey(t, ktc, "MakeKeyPrefixMarketCreateAskFlatFee(%d)", tc.marketID) + checkKey(t, ktc, "GetKeyPrefixMarketCreateAskFlatFee(%d)", tc.marketID) }) } } @@ -373,12 +373,12 @@ func TestMakeKeyMarketCreateAskFlatFee(t *testing.T) { expected: tc.expected, expPrefixes: []expectedPrefix{ { - name: "MakeKeyPrefixMarket", - value: keeper.MakeKeyPrefixMarket(tc.marketID), + name: "GetKeyPrefixMarket", + value: keeper.GetKeyPrefixMarket(tc.marketID), }, { - name: "MakeKeyPrefixMarketCreateBidFlatFee", - value: keeper.MakeKeyPrefixMarketCreateAskFlatFee(tc.marketID), + name: "GetKeyPrefixMarketCreateBidFlatFee", + value: keeper.GetKeyPrefixMarketCreateAskFlatFee(tc.marketID), }, }, } @@ -387,7 +387,7 @@ func TestMakeKeyMarketCreateAskFlatFee(t *testing.T) { } } -func TestMakeKeyPrefixMarketCreateBidFlatFee(t *testing.T) { +func TestGetKeyPrefixMarketCreateBidFlatFee(t *testing.T) { marketTypeByte := keeper.MarketKeyTypeCreateBidFlat tests := []struct { @@ -441,14 +441,14 @@ func TestMakeKeyPrefixMarketCreateBidFlatFee(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarketCreateBidFlatFee(tc.marketID) + return keeper.GetKeyPrefixMarketCreateBidFlatFee(tc.marketID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + {name: "GetKeyPrefixMarket", value: keeper.GetKeyPrefixMarket(tc.marketID)}, }, } - checkKey(t, ktc, "MakeKeyPrefixMarketCreateBidFlatFee(%d)", tc.marketID) + checkKey(t, ktc, "GetKeyPrefixMarketCreateBidFlatFee(%d)", tc.marketID) }) } } @@ -527,12 +527,12 @@ func TestMakeKeyMarketCreateBidFlatFee(t *testing.T) { expected: tc.expected, expPrefixes: []expectedPrefix{ { - name: "MakeKeyPrefixMarket", - value: keeper.MakeKeyPrefixMarket(tc.marketID), + name: "GetKeyPrefixMarket", + value: keeper.GetKeyPrefixMarket(tc.marketID), }, { - name: "MakeKeyPrefixMarketCreateBidFlatFee", - value: keeper.MakeKeyPrefixMarketCreateBidFlatFee(tc.marketID), + name: "GetKeyPrefixMarketCreateBidFlatFee", + value: keeper.GetKeyPrefixMarketCreateBidFlatFee(tc.marketID), }, }, } @@ -541,7 +541,7 @@ func TestMakeKeyMarketCreateBidFlatFee(t *testing.T) { } } -func TestMakeKeyPrefixMarketSellerSettlementFlatFee(t *testing.T) { +func TestGetKeyPrefixMarketSellerSettlementFlatFee(t *testing.T) { marketTypeByte := keeper.MarketKeyTypeSellerSettlementFlat tests := []struct { @@ -595,14 +595,14 @@ func TestMakeKeyPrefixMarketSellerSettlementFlatFee(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarketSellerSettlementFlatFee(tc.marketID) + return keeper.GetKeyPrefixMarketSellerSettlementFlatFee(tc.marketID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + {name: "GetKeyPrefixMarket", value: keeper.GetKeyPrefixMarket(tc.marketID)}, }, } - checkKey(t, ktc, "MakeKeyPrefixMarketSellerSettlementFlatFee(%d)", tc.marketID) + checkKey(t, ktc, "GetKeyPrefixMarketSellerSettlementFlatFee(%d)", tc.marketID) }) } } @@ -681,12 +681,12 @@ func TestMakeKeyMarketSellerSettlementFlatFee(t *testing.T) { expected: tc.expected, expPrefixes: []expectedPrefix{ { - name: "MakeKeyPrefixMarket", - value: keeper.MakeKeyPrefixMarket(tc.marketID), + name: "GetKeyPrefixMarket", + value: keeper.GetKeyPrefixMarket(tc.marketID), }, { - name: "MakeKeyPrefixMarketSellerSettlementFlatFee", - value: keeper.MakeKeyPrefixMarketSellerSettlementFlatFee(tc.marketID), + name: "GetKeyPrefixMarketSellerSettlementFlatFee", + value: keeper.GetKeyPrefixMarketSellerSettlementFlatFee(tc.marketID), }, }, } @@ -695,7 +695,7 @@ func TestMakeKeyMarketSellerSettlementFlatFee(t *testing.T) { } } -func TestMakeKeyPrefixMarketSellerSettlementRatio(t *testing.T) { +func TestGetKeyPrefixMarketSellerSettlementRatio(t *testing.T) { marketTypeByte := keeper.MarketKeyTypeSellerSettlementRatio tests := []struct { @@ -749,14 +749,14 @@ func TestMakeKeyPrefixMarketSellerSettlementRatio(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarketSellerSettlementRatio(tc.marketID) + return keeper.GetKeyPrefixMarketSellerSettlementRatio(tc.marketID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + {name: "GetKeyPrefixMarket", value: keeper.GetKeyPrefixMarket(tc.marketID)}, }, } - checkKey(t, ktc, "MakeKeyPrefixMarketSellerSettlementRatio(%d)", tc.marketID) + checkKey(t, ktc, "GetKeyPrefixMarketSellerSettlementRatio(%d)", tc.marketID) }) } } @@ -814,12 +814,12 @@ func TestMakeKeyMarketSellerSettlementRatio(t *testing.T) { expected: tc.expected, expPrefixes: []expectedPrefix{ { - name: "MakeKeyPrefixMarket", - value: keeper.MakeKeyPrefixMarket(tc.marketID), + name: "GetKeyPrefixMarket", + value: keeper.GetKeyPrefixMarket(tc.marketID), }, { - name: "MakeKeyPrefixMarketSellerSettlementRatio", - value: keeper.MakeKeyPrefixMarketSellerSettlementRatio(tc.marketID), + name: "GetKeyPrefixMarketSellerSettlementRatio", + value: keeper.GetKeyPrefixMarketSellerSettlementRatio(tc.marketID), }, }, } @@ -828,7 +828,7 @@ func TestMakeKeyMarketSellerSettlementRatio(t *testing.T) { } } -func TestMakeKeyPrefixMarketBuyerSettlementFlatFee(t *testing.T) { +func TestGetKeyPrefixMarketBuyerSettlementFlatFee(t *testing.T) { marketTypeByte := keeper.MarketKeyTypeBuyerSettlementFlat tests := []struct { @@ -882,14 +882,14 @@ func TestMakeKeyPrefixMarketBuyerSettlementFlatFee(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarketBuyerSettlementFlatFee(tc.marketID) + return keeper.GetKeyPrefixMarketBuyerSettlementFlatFee(tc.marketID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + {name: "GetKeyPrefixMarket", value: keeper.GetKeyPrefixMarket(tc.marketID)}, }, } - checkKey(t, ktc, "MakeKeyPrefixMarketBuyerSettlementFlatFee(%d)", tc.marketID) + checkKey(t, ktc, "GetKeyPrefixMarketBuyerSettlementFlatFee(%d)", tc.marketID) }) } } @@ -968,12 +968,12 @@ func TestMakeKeyMarketBuyerSettlementFlatFee(t *testing.T) { expected: tc.expected, expPrefixes: []expectedPrefix{ { - name: "MakeKeyPrefixMarket", - value: keeper.MakeKeyPrefixMarket(tc.marketID), + name: "GetKeyPrefixMarket", + value: keeper.GetKeyPrefixMarket(tc.marketID), }, { - name: "MakeKeyPrefixMarketBuyerSettlementFlatFee", - value: keeper.MakeKeyPrefixMarketBuyerSettlementFlatFee(tc.marketID), + name: "GetKeyPrefixMarketBuyerSettlementFlatFee", + value: keeper.GetKeyPrefixMarketBuyerSettlementFlatFee(tc.marketID), }, }, } @@ -982,7 +982,7 @@ func TestMakeKeyMarketBuyerSettlementFlatFee(t *testing.T) { } } -func TestMakeKeyPrefixMarketBuyerSettlementRatio(t *testing.T) { +func TestGetKeyPrefixMarketBuyerSettlementRatio(t *testing.T) { marketTypeByte := keeper.MarketKeyTypeBuyerSettlementRatio tests := []struct { @@ -1036,14 +1036,14 @@ func TestMakeKeyPrefixMarketBuyerSettlementRatio(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarketBuyerSettlementRatio(tc.marketID) + return keeper.GetKeyPrefixMarketBuyerSettlementRatio(tc.marketID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + {name: "GetKeyPrefixMarket", value: keeper.GetKeyPrefixMarket(tc.marketID)}, }, } - checkKey(t, ktc, "MakeKeyPrefixMarketBuyerSettlementRatio(%d)", tc.marketID) + checkKey(t, ktc, "GetKeyPrefixMarketBuyerSettlementRatio(%d)", tc.marketID) }) } } @@ -1101,12 +1101,12 @@ func TestMakeKeyMarketBuyerSettlementRatio(t *testing.T) { expected: tc.expected, expPrefixes: []expectedPrefix{ { - name: "MakeKeyPrefixMarket", - value: keeper.MakeKeyPrefixMarket(tc.marketID), + name: "GetKeyPrefixMarket", + value: keeper.GetKeyPrefixMarket(tc.marketID), }, { - name: "MakeKeyPrefixMarketBuyerSettlementRatio", - value: keeper.MakeKeyPrefixMarketBuyerSettlementRatio(tc.marketID), + name: "GetKeyPrefixMarketBuyerSettlementRatio", + value: keeper.GetKeyPrefixMarketBuyerSettlementRatio(tc.marketID), }, }, } @@ -1173,7 +1173,7 @@ func TestMakeKeyMarketInactive(t *testing.T) { }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + {name: "GetKeyPrefixMarket", value: keeper.GetKeyPrefixMarket(tc.marketID)}, }, } checkKey(t, ktc, "MakeKeyMarketInactive(%d)", tc.marketID) @@ -1239,7 +1239,7 @@ func TestMakeKeyMarketUserSettle(t *testing.T) { }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + {name: "GetKeyPrefixMarket", value: keeper.GetKeyPrefixMarket(tc.marketID)}, }, } checkKey(t, ktc, "MakeKeyMarketUserSettle(%d)", tc.marketID) @@ -1247,7 +1247,7 @@ func TestMakeKeyMarketUserSettle(t *testing.T) { } } -func TestMakeKeyPrefixMarketPermissions(t *testing.T) { +func TestGetKeyPrefixMarketPermissions(t *testing.T) { marketTypeByte := keeper.MarketKeyTypePermissions tests := []struct { @@ -1301,19 +1301,19 @@ func TestMakeKeyPrefixMarketPermissions(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarketPermissions(tc.marketID) + return keeper.GetKeyPrefixMarketPermissions(tc.marketID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + {name: "GetKeyPrefixMarket", value: keeper.GetKeyPrefixMarket(tc.marketID)}, }, } - checkKey(t, ktc, "MakeKeyPrefixMarketPermissions(%d)", tc.marketID) + checkKey(t, ktc, "GetKeyPrefixMarketPermissions(%d)", tc.marketID) }) } } -func TestMakeKeyPrefixMarketPermissionsForAddress(t *testing.T) { +func TestGetKeyPrefixMarketPermissionsForAddress(t *testing.T) { marketTypeByte := keeper.MarketKeyTypePermissions tests := []struct { @@ -1386,7 +1386,7 @@ func TestMakeKeyPrefixMarketPermissionsForAddress(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixMarketPermissionsForAddress(tc.marketID, tc.addr) + return keeper.GetKeyPrefixMarketPermissionsForAddress(tc.marketID, tc.addr) }, expected: tc.expected, expPanic: tc.expPanic, @@ -1394,16 +1394,16 @@ func TestMakeKeyPrefixMarketPermissionsForAddress(t *testing.T) { if len(tc.expPanic) == 0 { ktc.expPrefixes = []expectedPrefix{ { - name: "MakeKeyPrefixMarket", - value: keeper.MakeKeyPrefixMarket(tc.marketID), + name: "GetKeyPrefixMarket", + value: keeper.GetKeyPrefixMarket(tc.marketID), }, { - name: "MakeKeyPrefixMarketPermissions", - value: keeper.MakeKeyPrefixMarketPermissions(tc.marketID), + name: "GetKeyPrefixMarketPermissions", + value: keeper.GetKeyPrefixMarketPermissions(tc.marketID), }, } } - checkKey(t, ktc, "MakeKeyPrefixMarketPermissionsForAddress(%d)", tc.marketID) + checkKey(t, ktc, "GetKeyPrefixMarketPermissionsForAddress(%d)", tc.marketID) }) } } @@ -1627,16 +1627,16 @@ func TestMakeKeyMarketPermissions(t *testing.T) { if len(tc.expPanic) == 0 { ktc.expPrefixes = []expectedPrefix{ { - name: "MakeKeyPrefixMarket", - value: keeper.MakeKeyPrefixMarket(tc.marketID), + name: "GetKeyPrefixMarket", + value: keeper.GetKeyPrefixMarket(tc.marketID), }, { - name: "MakeKeyPrefixMarketPermissions", - value: keeper.MakeKeyPrefixMarketPermissions(tc.marketID), + name: "GetKeyPrefixMarketPermissions", + value: keeper.GetKeyPrefixMarketPermissions(tc.marketID), }, { - name: "MakeKeyPrefixMarketPermissionsForAddress", - value: keeper.MakeKeyPrefixMarketPermissionsForAddress(tc.marketID, tc.addr), + name: "GetKeyPrefixMarketPermissionsForAddress", + value: keeper.GetKeyPrefixMarketPermissionsForAddress(tc.marketID, tc.addr), }, } } @@ -1704,7 +1704,7 @@ func TestMakeKeyMarketReqAttrAsk(t *testing.T) { }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + {name: "GetKeyPrefixMarket", value: keeper.GetKeyPrefixMarket(tc.marketID)}, }, } checkKey(t, ktc, "MakeKeyMarketReqAttrAsk(%d)", tc.marketID) @@ -1771,7 +1771,7 @@ func TestMakeKeyMarketReqAttrBid(t *testing.T) { }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeKeyPrefixMarket", value: keeper.MakeKeyPrefixMarket(tc.marketID)}, + {name: "GetKeyPrefixMarket", value: keeper.GetKeyPrefixMarket(tc.marketID)}, }, } checkKey(t, ktc, "MakeKeyMarketReqAttrBid(%d)", tc.marketID) @@ -1779,7 +1779,7 @@ func TestMakeKeyMarketReqAttrBid(t *testing.T) { } } -func TestMakeKeyPrefixOrder(t *testing.T) { +func TestGetKeyPrefixOrder(t *testing.T) { tests := []struct { name string orderID uint64 @@ -1851,11 +1851,11 @@ func TestMakeKeyPrefixOrder(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeKeyPrefixOrder(tc.orderID) + return keeper.GetKeyPrefixOrder(tc.orderID) }, expected: tc.expected, } - checkKey(t, ktc, "MakeKeyPrefixOrder(%d)", tc.orderID) + checkKey(t, ktc, "GetKeyPrefixOrder(%d)", tc.orderID) }) } } @@ -2012,7 +2012,7 @@ func TestMakeKeyOrder(t *testing.T) { } if len(tc.expPanic) == 0 { ktc.expPrefixes = []expectedPrefix{ - {name: "MakeKeyPrefixOrder", value: keeper.MakeKeyPrefixOrder(tc.order.OrderId)}, + {name: "GetKeyPrefixOrder", value: keeper.GetKeyPrefixOrder(tc.order.OrderId)}, } } checkKey(t, ktc, "MakeKeyOrder %d %T", tc.order.OrderId, tc.order.Order) @@ -2020,7 +2020,7 @@ func TestMakeKeyOrder(t *testing.T) { } } -func TestMakeIndexPrefixMarketToOrder(t *testing.T) { +func TestGetIndexKeyPrefixMarketToOrder(t *testing.T) { tests := []struct { name string marketID uint32 @@ -2072,11 +2072,11 @@ func TestMakeIndexPrefixMarketToOrder(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeIndexPrefixMarketToOrder(tc.marketID) + return keeper.GetIndexKeyPrefixMarketToOrder(tc.marketID) }, expected: tc.expected, } - checkKey(t, ktc, "MakeIndexPrefixMarketToOrder(%d)", tc.marketID) + checkKey(t, ktc, "GetIndexKeyPrefixMarketToOrder(%d)", tc.marketID) }) } } @@ -2159,7 +2159,7 @@ func TestMakeIndexKeyMarketToOrder(t *testing.T) { }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "", value: keeper.MakeIndexPrefixMarketToOrder(tc.marketID)}, + {name: "", value: keeper.GetIndexKeyPrefixMarketToOrder(tc.marketID)}, }, } checkKey(t, ktc, "MakeIndexKeyMarketToOrder(%d, %d)", tc.marketID, tc.orderID) @@ -2167,7 +2167,7 @@ func TestMakeIndexKeyMarketToOrder(t *testing.T) { } } -func TestMakeIndexPrefixAddressToOrder(t *testing.T) { +func TestGetIndexKeyPrefixAddressToOrder(t *testing.T) { tests := []struct { name string addr sdk.AccAddress @@ -2210,12 +2210,12 @@ func TestMakeIndexPrefixAddressToOrder(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeIndexPrefixAddressToOrder(tc.addr) + return keeper.GetIndexKeyPrefixAddressToOrder(tc.addr) }, expected: tc.expected, expPanic: tc.expPanic, } - checkKey(t, ktc, "MakeIndexPrefixAddressToOrder(%s)", string(tc.addr)) + checkKey(t, ktc, "GetIndexKeyPrefixAddressToOrder(%s)", string(tc.addr)) }) } } @@ -2306,7 +2306,7 @@ func TestMakeIndexKeyAddressToOrder(t *testing.T) { } if len(tc.expPanic) == 0 { ktc.expPrefixes = []expectedPrefix{ - {name: "", value: keeper.MakeIndexPrefixAddressToOrder(tc.addr)}, + {name: "", value: keeper.GetIndexKeyPrefixAddressToOrder(tc.addr)}, } } checkKey(t, ktc, "MakeIndexKeyAddressToOrder(%s, %d)", string(tc.addr), tc.orderID) @@ -2314,7 +2314,7 @@ func TestMakeIndexKeyAddressToOrder(t *testing.T) { } } -func TestMakeIndexPrefixAssetToOrder(t *testing.T) { +func TestGetIndexKeyPrefixAssetToOrder(t *testing.T) { tests := []struct { name string assetDenom string @@ -2346,16 +2346,16 @@ func TestMakeIndexPrefixAssetToOrder(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeIndexPrefixAssetToOrder(tc.assetDenom) + return keeper.GetIndexKeyPrefixAssetToOrder(tc.assetDenom) }, expected: tc.expected, } - checkKey(t, ktc, "MakeIndexPrefixAssetToOrder(%q)", tc.assetDenom) + checkKey(t, ktc, "GetIndexKeyPrefixAssetToOrder(%q)", tc.assetDenom) }) } } -func TestMakeIndexPrefixAssetToOrderAsks(t *testing.T) { +func TestGetIndexKeyPrefixAssetToOrderAsks(t *testing.T) { orderKeyType := keeper.OrderKeyTypeAsk tests := []struct { name string @@ -2392,20 +2392,20 @@ func TestMakeIndexPrefixAssetToOrderAsks(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeIndexPrefixAssetToOrderAsks(tc.assetDenom) + return keeper.GetIndexKeyPrefixAssetToOrderAsks(tc.assetDenom) }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeIndexPrefixAssetToOrder", value: keeper.MakeIndexPrefixAssetToOrder(tc.assetDenom)}, + {name: "GetIndexKeyPrefixAssetToOrder", value: keeper.GetIndexKeyPrefixAssetToOrder(tc.assetDenom)}, }, } - checkKey(t, ktc, "MakeIndexPrefixAssetToOrderAsks(%q)", tc.assetDenom) + checkKey(t, ktc, "GetIndexKeyPrefixAssetToOrderAsks(%q)", tc.assetDenom) }) } } -func TestMakeIndexPrefixAssetToOrderBids(t *testing.T) { +func TestGetIndexKeyPrefixAssetToOrderBids(t *testing.T) { orderKeyType := keeper.OrderKeyTypeBid tests := []struct { name string @@ -2442,15 +2442,15 @@ func TestMakeIndexPrefixAssetToOrderBids(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeIndexPrefixAssetToOrderBids(tc.assetDenom) + return keeper.GetIndexKeyPrefixAssetToOrderBids(tc.assetDenom) }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "MakeIndexPrefixAssetToOrder", value: keeper.MakeIndexPrefixAssetToOrder(tc.assetDenom)}, + {name: "GetIndexKeyPrefixAssetToOrder", value: keeper.GetIndexKeyPrefixAssetToOrder(tc.assetDenom)}, }, } - checkKey(t, ktc, "MakeIndexPrefixAssetToOrderBids(%q)", tc.assetDenom) + checkKey(t, ktc, "GetIndexKeyPrefixAssetToOrderBids(%q)", tc.assetDenom) }) } } @@ -2578,18 +2578,18 @@ func TestMakeIndexKeyAssetToOrder(t *testing.T) { } if len(tc.expPanic) == 0 { ktc.expPrefixes = []expectedPrefix{ - {name: "MakeIndexPrefixAssetToOrder", value: keeper.MakeIndexPrefixAssetToOrder(tc.assetDenom)}, + {name: "GetIndexKeyPrefixAssetToOrder", value: keeper.GetIndexKeyPrefixAssetToOrder(tc.assetDenom)}, } switch v := tc.order.Order.(type) { case *exchange.Order_AskOrder: ktc.expPrefixes = append(ktc.expPrefixes, expectedPrefix{ - name: "MakeIndexPrefixAssetToOrderAsks", - value: keeper.MakeIndexPrefixAssetToOrderAsks(tc.assetDenom), + name: "GetIndexKeyPrefixAssetToOrderAsks", + value: keeper.GetIndexKeyPrefixAssetToOrderAsks(tc.assetDenom), }) case *exchange.Order_BidOrder: ktc.expPrefixes = append(ktc.expPrefixes, expectedPrefix{ - name: "MakeIndexPrefixAssetToOrderBids", - value: keeper.MakeIndexPrefixAssetToOrderBids(tc.assetDenom), + name: "GetIndexKeyPrefixAssetToOrderBids", + value: keeper.GetIndexKeyPrefixAssetToOrderBids(tc.assetDenom), }) default: assert.Fail(t, "no expected prefix case defined for %T", v) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 56a6b6d29c..a14112d0b2 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -17,22 +17,22 @@ var ( // createAskKeyMakers are the key and prefix makers for the create-ask flat fees. createAskKeyMakers = flatFeeKeyMakers{ key: MakeKeyMarketCreateAskFlatFee, - prefix: MakeKeyPrefixMarketCreateAskFlatFee, + prefix: GetKeyPrefixMarketCreateAskFlatFee, } // createBidKeyMakers are the key and prefix makers for the create-bid flat fees. createBidKeyMakers = flatFeeKeyMakers{ key: MakeKeyMarketCreateBidFlatFee, - prefix: MakeKeyPrefixMarketCreateBidFlatFee, + prefix: GetKeyPrefixMarketCreateBidFlatFee, } // sellerSettlementFlatKeyMakers are the key and prefix makers for the seller settlement flat fees. sellerSettlementFlatKeyMakers = flatFeeKeyMakers{ key: MakeKeyMarketSellerSettlementFlatFee, - prefix: MakeKeyPrefixMarketSellerSettlementFlatFee, + prefix: GetKeyPrefixMarketSellerSettlementFlatFee, } // sellerSettlementFlatKeyMakers are the key and prefix makers for the buyer settlement flat fees. buyerSettlementFlatKeyMakers = flatFeeKeyMakers{ key: MakeKeyMarketBuyerSettlementFlatFee, - prefix: MakeKeyPrefixMarketBuyerSettlementFlatFee, + prefix: GetKeyPrefixMarketBuyerSettlementFlatFee, } ) diff --git a/x/exchange/keeper/params.go b/x/exchange/keeper/params.go index 80349d8c54..3e40735b0b 100644 --- a/x/exchange/keeper/params.go +++ b/x/exchange/keeper/params.go @@ -9,7 +9,7 @@ import ( // deleteAllParamsSplits deletes all the params splits in the store. func deleteAllParamsSplits(store sdk.KVStore) { - keys := getAllKeys(store, MakeKeyPrefixParamsSplit()) + keys := getAllKeys(store, GetKeyPrefixParamsSplit()) for _, key := range keys { store.Delete(key) } @@ -49,7 +49,7 @@ func (k Keeper) SetParams(ctx sdk.Context, params *exchange.Params) { // If there aren't any params in state, nil is returned. func (k Keeper) GetParams(ctx sdk.Context) *exchange.Params { // Using an open iterator on a prefixed store here so that iter.Key() doesn't contain the prefix. - store := prefix.NewStore(ctx.KVStore(k.storeKey), MakeKeyPrefixParamsSplit()) + store := prefix.NewStore(ctx.KVStore(k.storeKey), GetKeyPrefixParamsSplit()) iter := store.Iterator(nil, nil) defer iter.Close() From 9b3bf960a4e679a73ceae3d4a89bed328f7e31ff Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 1 Sep 2023 12:45:29 -0600 Subject: [PATCH 068/309] [1658]: Create a generic iterator and use that for the GetParams func. --- x/exchange/keeper/keeper.go | 17 +++++++++++++++++ x/exchange/keeper/params.go | 17 +++++------------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index da728ac2fa..e85b12c8e8 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -2,6 +2,7 @@ package keeper import ( "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -51,3 +52,19 @@ func deleteAll(store sdk.KVStore, pre []byte) { store.Delete(key) } } + +// iterate iterates over all the entries in the store with the given prefix. +// The key provided to the callback will NOT have the provided prefix; it will be everything after it. +// The callback should return false to continue iteration, or true to stop. +func (k Keeper) iterate(ctx sdk.Context, pre []byte, cb func(key, value []byte) bool) { + // Using an open iterator on a prefixed store here so that iter.Key() doesn't contain the prefix. + store := prefix.NewStore(ctx.KVStore(k.storeKey), pre) + iter := store.Iterator(nil, nil) + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + if cb(iter.Key(), iter.Value()) { + break + } + } +} diff --git a/x/exchange/keeper/params.go b/x/exchange/keeper/params.go index 3e40735b0b..404cb9677e 100644 --- a/x/exchange/keeper/params.go +++ b/x/exchange/keeper/params.go @@ -1,7 +1,6 @@ package keeper import ( - "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/provenance-io/provenance/x/exchange" @@ -48,26 +47,20 @@ func (k Keeper) SetParams(ctx sdk.Context, params *exchange.Params) { // GetParams gets the exchange module params. // If there aren't any params in state, nil is returned. func (k Keeper) GetParams(ctx sdk.Context) *exchange.Params { - // Using an open iterator on a prefixed store here so that iter.Key() doesn't contain the prefix. - store := prefix.NewStore(ctx.KVStore(k.storeKey), GetKeyPrefixParamsSplit()) - iter := store.Iterator(nil, nil) - defer iter.Close() - var rv *exchange.Params - for ; iter.Valid(); iter.Next() { - denom := string(iter.Key()) - split := uint16FromBz(iter.Value()) - + k.iterate(ctx, GetKeyPrefixParamsSplit(), func(key, value []byte) bool { if rv == nil { rv = &exchange.Params{} } + denom := string(key) + split := uint16FromBz(value) if len(denom) == 0 { rv.DefaultSplit = uint32(split) } else { rv.DenomSplits = append(rv.DenomSplits, exchange.DenomSplit{Denom: denom, Split: uint32(split)}) } - } - + return false + }) return rv } From 443e29601fcdaa29556ebd4929ecc4b6a6b0362e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 1 Sep 2023 15:49:45 -0600 Subject: [PATCH 069/309] [1658]; Switch to a record separator byte to separate the price and fee denoms in the key. Create a suffix maker and parser for those keys too. --- x/exchange/keeper/keys.go | 40 +++++++-- x/exchange/keeper/keys_test.go | 148 ++++++++++++++++++++++++++++++--- 2 files changed, 170 insertions(+), 18 deletions(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index e3ae5c9240..586ad28bef 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -1,6 +1,7 @@ package keeper import ( + "bytes" "encoding/binary" "errors" "fmt" @@ -97,6 +98,9 @@ const ( OrderKeyTypeAsk = exchange.OrderTypeByteAsk // OrderKeyTypeBid is the order-specific type byte for bid orders. OrderKeyTypeBid = exchange.OrderTypeByteBid + + // RecordSeparator is the RE ascii control char used to separate records in a byte slice. + RecordSeparator = byte(0x1E) ) // prepKey creates a single byte slice consisting of the type byte and provided byte slice with some extra capacity in the underlying array. @@ -230,6 +234,28 @@ func MakeKeyMarketSellerSettlementFlatFee(marketID uint32, denom string) []byte return rv } +// GetKeySuffixSettlementRatio gets the key suffix bytes to represent the provided fee ratio. +func GetKeySuffixSettlementRatio(ratio exchange.FeeRatio) []byte { + rv := make([]byte, 0, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) + rv = append(rv, ratio.Price.Denom...) + rv = append(rv, RecordSeparator) + rv = append(rv, ratio.Fee.Denom...) + return rv +} + +// ParseKeySuffixSettlementRatio parses the portion +// of a settlement ratio key back into the denom strings. +func ParseKeySuffixSettlementRatio(suffix []byte) (priceDenom, feeDenom string, err error) { + parts := bytes.Split(suffix, []byte{RecordSeparator}) + if len(parts) == 2 { + priceDenom = string(parts[0]) + feeDenom = string(parts[1]) + } else { + err = fmt.Errorf("key suffix %q has %d parts, expected 2", suffix, len(parts)) + } + return +} + // marketKeyPrefixSellerSettlementRatio creates the key prefix for a market's seller settlement ratios with extra capacity for the rest. func marketKeyPrefixSellerSettlementRatio(marketID uint32, extraCap int) []byte { return keyPrefixMarketType(marketID, MarketKeyTypeSellerSettlementRatio, extraCap) @@ -242,10 +268,9 @@ func GetKeyPrefixMarketSellerSettlementRatio(marketID uint32) []byte { // MakeKeyMarketSellerSettlementRatio creates the key to use for the given seller settlement fee ratio in the given market. func MakeKeyMarketSellerSettlementRatio(marketID uint32, ratio exchange.FeeRatio) []byte { - rv := marketKeyPrefixSellerSettlementRatio(marketID, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) - rv = append(rv, ratio.Price.Denom...) - rv = append(rv, 0x00) - rv = append(rv, ratio.Fee.Denom...) + suffix := GetKeySuffixSettlementRatio(ratio) + rv := marketKeyPrefixSellerSettlementRatio(marketID, len(suffix)) + rv = append(rv, suffix...) return rv } @@ -278,10 +303,9 @@ func GetKeyPrefixMarketBuyerSettlementRatio(marketID uint32) []byte { // MakeKeyMarketBuyerSettlementRatio creates the key to use for the given buyer settlement fee ratio in the given market. func MakeKeyMarketBuyerSettlementRatio(marketID uint32, ratio exchange.FeeRatio) []byte { - rv := marketKeyPrefixBuyerSettlementRatio(marketID, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) - rv = append(rv, ratio.Price.Denom...) - rv = append(rv, 0x00) - rv = append(rv, ratio.Fee.Denom...) + suffix := GetKeySuffixSettlementRatio(ratio) + rv := marketKeyPrefixBuyerSettlementRatio(marketID, len(suffix)) + rv = append(rv, suffix...) return rv } diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index c3072aee06..39c0ea4693 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" @@ -695,6 +696,131 @@ func TestMakeKeyMarketSellerSettlementFlatFee(t *testing.T) { } } +func TestGetKeySuffixSettlementRatio(t *testing.T) { + coin := func(denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.OneInt()} + } + tests := []struct { + name string + ratio exchange.FeeRatio + exp []byte + }{ + { + name: "both denoms empty", + ratio: exchange.FeeRatio{Price: coin(""), Fee: coin("")}, + exp: []byte{keeper.RecordSeparator}, + }, + { + name: "empty price nhash fee", + ratio: exchange.FeeRatio{Price: coin(""), Fee: coin("nhash")}, + exp: []byte{keeper.RecordSeparator, 'n', 'h', 'a', 's', 'h'}, + }, + { + name: "nhash price empty fee", + ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin("")}, + exp: []byte{'n', 'h', 'a', 's', 'h', keeper.RecordSeparator}, + }, + { + name: "nhash price nhash fee", + ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin("nhash")}, + exp: []byte{'n', 'h', 'a', 's', 'h', keeper.RecordSeparator, 'n', 'h', 'a', 's', 'h'}, + }, + { + name: "nhash price hex string fee", + ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin(hexString)}, + exp: append([]byte{'n', 'h', 'a', 's', 'h', keeper.RecordSeparator}, hexString...), + }, + { + name: "hex string price nhash fee", + ratio: exchange.FeeRatio{Price: coin(hexString), Fee: coin("nhash")}, + exp: append([]byte(hexString), keeper.RecordSeparator, 'n', 'h', 'a', 's', 'h'), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.GetKeySuffixSettlementRatio(tc.ratio) + }, + expected: tc.exp, + } + checkKey(t, ktc, "GetKeySuffixSettlementRatio(%s)", tc.ratio) + }) + } +} + +func TestParseKeySuffixSettlementRatio(t *testing.T) { + tests := []struct { + name string + suffix []byte + expPriceDenom string + expFeeDenom string + expErr string + }{ + { + name: "both denoms empty", + suffix: []byte{keeper.RecordSeparator}, + expPriceDenom: "", + expFeeDenom: "", + }, + { + name: "empty price nhash fee", + suffix: []byte{keeper.RecordSeparator, 'n', 'h', 'a', 's', 'h'}, + expPriceDenom: "", + expFeeDenom: "nhash", + }, + { + name: "nhash price empty fee", + suffix: []byte{'n', 'h', 'a', 's', 'h', keeper.RecordSeparator}, + expPriceDenom: "nhash", + expFeeDenom: "", + }, + { + name: "nhash price nhash fee", + suffix: []byte{'n', 'h', 'a', 's', 'h', keeper.RecordSeparator, 'n', 'h', 'a', 's', 'h'}, + expPriceDenom: "nhash", + expFeeDenom: "nhash", + }, + { + name: "nhash price hex string fee", + suffix: append([]byte{'n', 'h', 'a', 's', 'h', keeper.RecordSeparator}, hexString...), + expPriceDenom: "nhash", + expFeeDenom: hexString, + }, + { + name: "hex string price nhash fee", + suffix: append([]byte(hexString), keeper.RecordSeparator, 'n', 'h', 'a', 's', 'h'), + expPriceDenom: hexString, + expFeeDenom: "nhash", + }, + { + name: "no record separator", + suffix: []byte("nhashnhash"), + expErr: "key suffix \"nhashnhash\" has 1 parts, expected 2", + }, + { + name: "two record separators", + suffix: []byte{keeper.RecordSeparator, 'b', keeper.RecordSeparator}, + expErr: "key suffix \"\\x1eb\\x1e\" has 3 parts, expected 2", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var priceDenom, feeDenom string + var err error + testFunc := func() { + priceDenom, feeDenom, err = keeper.ParseKeySuffixSettlementRatio(tc.suffix) + } + require.NotPanics(t, testFunc, "ParseKeySuffixSettlementRatio(%q)", tc.suffix) + assertions.AssertErrorValue(t, err, tc.expErr, "ParseKeySuffixSettlementRatio(%q) error", tc.suffix) + assert.Equalf(t, tc.expPriceDenom, priceDenom, "ParseKeySuffixSettlementRatio(%q) price denom", tc.suffix) + assert.Equalf(t, tc.expFeeDenom, feeDenom, "ParseKeySuffixSettlementRatio(%q) fee denom", tc.suffix) + }) + } +} + func TestGetKeyPrefixMarketSellerSettlementRatio(t *testing.T) { marketTypeByte := keeper.MarketKeyTypeSellerSettlementRatio @@ -766,6 +892,7 @@ func TestMakeKeyMarketSellerSettlementRatio(t *testing.T) { coin := func(denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.OneInt()} } + rs := keeper.RecordSeparator tests := []struct { name string @@ -777,31 +904,31 @@ func TestMakeKeyMarketSellerSettlementRatio(t *testing.T) { name: "market id 0 both denoms empty", marketID: 0, ratio: exchange.FeeRatio{Price: coin(""), Fee: coin("")}, - expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, 0}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, rs}, }, { name: "market id 1 nhash to empty", marketID: 1, ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin("")}, - expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', 0}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs}, }, { name: "market id 1 empty to nhash", marketID: 1, ratio: exchange.FeeRatio{Price: coin(""), Fee: coin("nhash")}, - expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 0, 'n', 'h', 'a', 's', 'h'}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, rs, 'n', 'h', 'a', 's', 'h'}, }, { name: "market id 1 nhash to nhash", marketID: 1, ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin("nhash")}, - expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', 0, 'n', 'h', 'a', 's', 'h'}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs, 'n', 'h', 'a', 's', 'h'}, }, { name: "market id 16,843,009 nhash to hex string", marketID: 16_843_009, ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin(hexString)}, - expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', 0}, hexString...), + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs}, hexString...), }, } @@ -1053,6 +1180,7 @@ func TestMakeKeyMarketBuyerSettlementRatio(t *testing.T) { coin := func(denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.OneInt()} } + rs := keeper.RecordSeparator tests := []struct { name string @@ -1064,31 +1192,31 @@ func TestMakeKeyMarketBuyerSettlementRatio(t *testing.T) { name: "market id 0 both denoms empty", marketID: 0, ratio: exchange.FeeRatio{Price: coin(""), Fee: coin("")}, - expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, 0}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, rs}, }, { name: "market id 1 nhash to empty", marketID: 1, ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin("")}, - expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', 0}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs}, }, { name: "market id 1 empty to nhash", marketID: 1, ratio: exchange.FeeRatio{Price: coin(""), Fee: coin("nhash")}, - expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 0, 'n', 'h', 'a', 's', 'h'}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, rs, 'n', 'h', 'a', 's', 'h'}, }, { name: "market id 1 nhash to nhash", marketID: 1, ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin("nhash")}, - expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', 0, 'n', 'h', 'a', 's', 'h'}, + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs, 'n', 'h', 'a', 's', 'h'}, }, { name: "market id 16,843,009 nhash to hex string", marketID: 16_843_009, ratio: exchange.FeeRatio{Price: coin("nhash"), Fee: coin(hexString)}, - expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', 0}, hexString...), + expected: append([]byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs}, hexString...), }, } From 0ab74930c03395646a29e9af4ba5d633cb1bbc23 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 1 Sep 2023 20:27:08 -0600 Subject: [PATCH 070/309] [1658]: Most of the settlement ratio stuff. --- x/exchange/keeper/keeper.go | 13 +- x/exchange/keeper/keys.go | 62 ++++++- x/exchange/keeper/keys_test.go | 218 +++++++++++++++++++++- x/exchange/keeper/market.go | 324 ++++++++++++++++++++++++++++++--- x/exchange/market.go | 17 ++ x/exchange/market_test.go | 84 +++++++++ 6 files changed, 684 insertions(+), 34 deletions(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index e85b12c8e8..58dda2c125 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -56,10 +56,10 @@ func deleteAll(store sdk.KVStore, pre []byte) { // iterate iterates over all the entries in the store with the given prefix. // The key provided to the callback will NOT have the provided prefix; it will be everything after it. // The callback should return false to continue iteration, or true to stop. -func (k Keeper) iterate(ctx sdk.Context, pre []byte, cb func(key, value []byte) bool) { +func iterate(store sdk.KVStore, pre []byte, cb func(key, value []byte) bool) { // Using an open iterator on a prefixed store here so that iter.Key() doesn't contain the prefix. - store := prefix.NewStore(ctx.KVStore(k.storeKey), pre) - iter := store.Iterator(nil, nil) + pStore := prefix.NewStore(store, pre) + iter := pStore.Iterator(nil, nil) defer iter.Close() for ; iter.Valid(); iter.Next() { @@ -68,3 +68,10 @@ func (k Keeper) iterate(ctx sdk.Context, pre []byte, cb func(key, value []byte) } } } + +// iterate iterates over all the entries in the store with the given prefix. +// The key provided to the callback will NOT have the provided prefix; it will be everything after it. +// The callback should return false to continue iteration, or true to stop. +func (k Keeper) iterate(ctx sdk.Context, pre []byte, cb func(key, value []byte) bool) { + iterate(ctx.KVStore(k.storeKey), pre, cb) +} diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 586ad28bef..bfe9cc1d0e 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -6,6 +6,8 @@ import ( "errors" "fmt" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/address" @@ -29,13 +31,13 @@ import ( // Market Create-Ask Flat Fee: 0x01 | | 0x00 | => (string) // Market Create-Bid Flat Fee: 0x01 | | 0x01 | => (string) // Market Seller Settlement Flat Fee: 0x01 | | 0x02 | => (string) -// Market Seller Settlement Fee Ratio: 0x01 | | 0x03 | | 0x00 | => comma-separated price and fee amount (string). +// Market Seller Settlement Fee Ratio: 0x01 | | 0x03 | | 0x1E | => price and fee amounts (strings) separated by 0x1E. // Market Buyer Settlement Flat Fee: 0x01 | | 0x04 | => (string) -// Market Buyer Settlement Fee Ratio: 0x01 | | 0x05 | | 0x00 | => comma-separated price and fee amount (string). +// Market Buyer Settlement Fee Ratio: 0x01 | | 0x05 | | 0x1E | => price and fee amounts (strings) separated by 0x1E. // Market inactive indicator: 0x01 | | 0x06 => nil // Market user-settle indicator: 0x01 | | 0x07 => nil // Market permissions: 0x01 | | 0x08 | |
| => nil -// Market Required Attributes: 0x01 | | 0x09 | => comma-separated list of required attribute entries. +// Market Required Attributes: 0x01 | | 0x09 | => 0x1E-separated list of required attribute entries. // // The is a single byte as uint8 with the same values as the enum entries. // @@ -235,6 +237,7 @@ func MakeKeyMarketSellerSettlementFlatFee(marketID uint32, denom string) []byte } // GetKeySuffixSettlementRatio gets the key suffix bytes to represent the provided fee ratio. +// Result has the format func GetKeySuffixSettlementRatio(ratio exchange.FeeRatio) []byte { rv := make([]byte, 0, len(ratio.Price.Denom)+1+len(ratio.Fee.Denom)) rv = append(rv, ratio.Price.Denom...) @@ -251,11 +254,53 @@ func ParseKeySuffixSettlementRatio(suffix []byte) (priceDenom, feeDenom string, priceDenom = string(parts[0]) feeDenom = string(parts[1]) } else { - err = fmt.Errorf("key suffix %q has %d parts, expected 2", suffix, len(parts)) + err = fmt.Errorf("ratio key suffix %q has %d parts, expected 2", suffix, len(parts)) } return } +// GetFeeRatioStoreValue creates the byte slice to set in the store for a fee ratio's value. +// Result has the format where both amounts are strings (of digits). +// E.g. "100\\1E3" (for a price amount of 100, and fee amount of 3). +func GetFeeRatioStoreValue(ratio exchange.FeeRatio) []byte { + priceAmount := ratio.Price.Amount.String() + feeAmount := ratio.Fee.Amount.String() + rv := make([]byte, 0, len(priceAmount)+1+len(feeAmount)) + rv = append(rv, priceAmount...) + rv = append(rv, RecordSeparator) + rv = append(rv, feeAmount...) + return rv +} + +// ParseFeeRatioStoreValue parses a fee ratio's store value back into the amounts. +// Input is expected to have the format where both amounts are strings (of digits). +// E.g. "100\\1E3" (for a price amount of 100, and fee amount of 3). +func ParseFeeRatioStoreValue(value []byte) (priceAmount, feeAmount sdkmath.Int, err error) { + parts := bytes.Split(value, []byte{RecordSeparator}) + if len(parts) == 2 { + var ok bool + priceAmount, ok = sdkmath.NewIntFromString(string(parts[0])) + if !ok { + err = fmt.Errorf("cannot convert price amount %q to sdkmath.Int", parts[0]) + } + feeAmount, ok = sdkmath.NewIntFromString(string(parts[1])) + if !ok { + err = errors.Join(err, fmt.Errorf("cannot convert fee amount %q to sdkmath.Int", parts[1])) + } + } else { + err = fmt.Errorf("ratio value %q has %d parts, expected 2", value, len(parts)) + } + + // The zero-value of sdkmath.Int{} will panic if anything tries to do anything with it (e.g. convert it to a string). + // Additionally, if there was an error, we don't want either of them to have any left-over values. + if err != nil { + priceAmount = sdkmath.ZeroInt() + feeAmount = sdkmath.ZeroInt() + } + + return +} + // marketKeyPrefixSellerSettlementRatio creates the key prefix for a market's seller settlement ratios with extra capacity for the rest. func marketKeyPrefixSellerSettlementRatio(marketID uint32, extraCap int) []byte { return keyPrefixMarketType(marketID, MarketKeyTypeSellerSettlementRatio, extraCap) @@ -301,6 +346,15 @@ func GetKeyPrefixMarketBuyerSettlementRatio(marketID uint32) []byte { return marketKeyPrefixBuyerSettlementRatio(marketID, 0) } +// GetKeyPrefixMarketBuyerSettlementRatioForPriceDenom creates the key prefix for a market's +// buyer settlement fee ratios that have the provided price denom. +func GetKeyPrefixMarketBuyerSettlementRatioForPriceDenom(marketID uint32, priceDenom string) []byte { + suffix := GetKeySuffixSettlementRatio(exchange.FeeRatio{Price: sdk.Coin{Denom: priceDenom}, Fee: sdk.Coin{Denom: ""}}) + rv := marketKeyPrefixBuyerSettlementRatio(marketID, len(suffix)) + rv = append(rv, suffix...) + return rv +} + // MakeKeyMarketBuyerSettlementRatio creates the key to use for the given buyer settlement fee ratio in the given market. func MakeKeyMarketBuyerSettlementRatio(marketID uint32, ratio exchange.FeeRatio) []byte { suffix := GetKeySuffixSettlementRatio(ratio) diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 39c0ea4693..4d1331bebf 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -797,12 +797,12 @@ func TestParseKeySuffixSettlementRatio(t *testing.T) { { name: "no record separator", suffix: []byte("nhashnhash"), - expErr: "key suffix \"nhashnhash\" has 1 parts, expected 2", + expErr: "ratio key suffix \"nhashnhash\" has 1 parts, expected 2", }, { name: "two record separators", suffix: []byte{keeper.RecordSeparator, 'b', keeper.RecordSeparator}, - expErr: "key suffix \"\\x1eb\\x1e\" has 3 parts, expected 2", + expErr: "ratio key suffix \"\\x1eb\\x1e\" has 3 parts, expected 2", }, } @@ -821,6 +821,220 @@ func TestParseKeySuffixSettlementRatio(t *testing.T) { } } +func TestGetFeeRatioStoreValue(t *testing.T) { + pCoin := func(amount string) sdk.Coin { + amt, ok := sdkmath.NewIntFromString(amount) + require.True(t, ok, "sdkmath.NewIntFromString(%q) ok boolean return value", amount) + return sdk.Coin{Denom: "price", Amount: amt} + } + fCoin := func(amount string) sdk.Coin { + amt, ok := sdkmath.NewIntFromString(amount) + require.True(t, ok, "sdkmath.NewIntFromString(%q) ok boolean return value", amount) + return sdk.Coin{Denom: "fee", Amount: amt} + } + rs := keeper.RecordSeparator + + tests := []struct { + name string + ratio exchange.FeeRatio + exp []byte + }{ + { + name: "zero to zero", + ratio: exchange.FeeRatio{Price: pCoin("0"), Fee: fCoin("0")}, + exp: []byte{'0', rs, '0'}, + }, + { + name: "zero to one", + ratio: exchange.FeeRatio{Price: pCoin("0"), Fee: fCoin("1")}, + exp: []byte{'0', rs, '1'}, + }, + { + name: "one to zero", + ratio: exchange.FeeRatio{Price: pCoin("1"), Fee: fCoin("0")}, + exp: []byte{'1', rs, '0'}, + }, + { + name: "one to one", + ratio: exchange.FeeRatio{Price: pCoin("1"), Fee: fCoin("1")}, + exp: []byte{'1', rs, '1'}, + }, + { + name: "100 to 3", + ratio: exchange.FeeRatio{Price: pCoin("100"), Fee: fCoin("3")}, + exp: []byte{'1', '0', '0', rs, '3'}, + }, + { + name: "3 to 100", + ratio: exchange.FeeRatio{Price: pCoin("3"), Fee: fCoin("100")}, + exp: []byte{'3', rs, '1', '0', '0'}, + }, + { + name: "huge number to 8", + // max uint64 = 18,446,744,073,709,551,615. This is 100 times that. + ratio: exchange.FeeRatio{Price: pCoin("1844674407370955161500"), Fee: fCoin("8")}, + exp: append([]byte("1844674407370955161500"), rs, '8'), + }, + { + name: "15 to huge number", + // max uint64 = 18,446,744,073,709,551,615. This is 100 times that. + ratio: exchange.FeeRatio{Price: pCoin("15"), Fee: fCoin("1844674407370955161500")}, + exp: append([]byte{'1', '5', rs}, "1844674407370955161500"...), + }, + { + name: "two huge numbers", + ratio: exchange.FeeRatio{Price: pCoin("3454125219812878222609890"), Fee: fCoin("8876890151543931493173153")}, + exp: concatBz( + []byte("3454125219812878222609890"), + []byte{rs}, + []byte("8876890151543931493173153"), + ), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.GetFeeRatioStoreValue(tc.ratio) + }, + expected: tc.exp, + } + checkKey(t, ktc, "GetFeeRatioStoreValue(%s)", tc.ratio) + }) + } +} + +func TestParseFeeRatioStoreValue(t *testing.T) { + intAmt := func(amt string) sdkmath.Int { + rv, ok := sdkmath.NewIntFromString(amt) + require.True(t, ok, "sdkmath.NewIntFromString(%q)", amt) + return rv + } + rs := keeper.RecordSeparator + + tests := []struct { + name string + value []byte + expPriceAmount sdkmath.Int + expFeeAmount sdkmath.Int + expErr string + }{ + { + name: "zero to zero", + value: []byte{'0', rs, '0'}, + expPriceAmount: intAmt("0"), + expFeeAmount: intAmt("0"), + }, + { + name: "zero to one", + value: []byte{'0', rs, '1'}, + expPriceAmount: intAmt("0"), + expFeeAmount: intAmt("1"), + }, + { + name: "one to zero", + value: []byte{'1', rs, '0'}, + expPriceAmount: intAmt("1"), + expFeeAmount: intAmt("0"), + }, + { + name: "one to one", + value: []byte{'1', rs, '1'}, + expPriceAmount: intAmt("1"), + expFeeAmount: intAmt("1"), + }, + { + name: "100 to 3", + value: []byte{'1', '0', '0', rs, '3'}, + expPriceAmount: intAmt("100"), + expFeeAmount: intAmt("3"), + }, + { + name: "3 to 100", + value: []byte{'3', rs, '1', '0', '0'}, + expPriceAmount: intAmt("3"), + expFeeAmount: intAmt("100"), + }, + { + name: "huge number to 8", + // max uint64 = 18,446,744,073,709,551,615. This is 100 times that. + value: append([]byte("1844674407370955161500"), rs, '8'), + expPriceAmount: intAmt("1844674407370955161500"), + expFeeAmount: intAmt("8"), + }, + { + name: "15 to huge number", + // max uint64 = 18,446,744,073,709,551,615. This is 100 times that. + value: append([]byte{'1', '5', rs}, "1844674407370955161500"...), + expPriceAmount: intAmt("15"), + expFeeAmount: intAmt("1844674407370955161500"), + }, + { + name: "two huge numbers", + value: concatBz( + []byte("3454125219812878222609890"), + []byte{rs}, + []byte("8876890151543931493173153"), + ), + expPriceAmount: intAmt("3454125219812878222609890"), + expFeeAmount: intAmt("8876890151543931493173153"), + }, + { + name: "invalid char in price", + value: []byte{'1', 'f', '0', rs, '5', '6', '7'}, + expErr: "cannot convert price amount \"1f0\" to sdkmath.Int", + }, + { + name: "invalid char in fee", + value: []byte{'1', '3', '0', rs, '5', 'f', '7'}, + expErr: "cannot convert fee amount \"5f7\" to sdkmath.Int", + }, + { + name: "invalid char in both", + value: []byte{'1', 'f', '0', rs, '5', 'f', '7'}, + expErr: "cannot convert price amount \"1f0\" to sdkmath.Int" + "\n" + + "cannot convert fee amount \"5f7\" to sdkmath.Int", + }, + { + name: "empty to empty", + value: []byte{rs}, + expErr: "cannot convert price amount \"\" to sdkmath.Int" + "\n" + + "cannot convert fee amount \"\" to sdkmath.Int", + }, + { + name: "no record separator", + value: []byte("100"), + expErr: "ratio value \"100\" has 1 parts, expected 2", + }, + { + name: "two record separators", + value: []byte{rs, '1', '0', '0', rs}, + expErr: "ratio value \"\\x1e100\\x1e\" has 3 parts, expected 2", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if len(tc.expErr) > 0 { + tc.expPriceAmount = sdkmath.ZeroInt() + tc.expFeeAmount = sdkmath.ZeroInt() + } + + var priceAmount, feeAmont sdkmath.Int + var err error + testFunc := func() { + priceAmount, feeAmont, err = keeper.ParseFeeRatioStoreValue(tc.value) + } + require.NotPanics(t, testFunc, "ParseFeeRatioStoreValue(%q)", tc.value) + + assertions.AssertErrorValue(t, err, tc.expErr, "ParseFeeRatioStoreValue(%q) error", tc.value) + assert.Equal(t, tc.expPriceAmount.String(), priceAmount.String(), "ParseFeeRatioStoreValue(%q) price amount", tc.value) + assert.Equal(t, tc.expFeeAmount.String(), feeAmont.String(), "ParseFeeRatioStoreValue(%q) fee amount", tc.value) + }) + } +} + func TestGetKeyPrefixMarketSellerSettlementRatio(t *testing.T) { marketTypeByte := keeper.MarketKeyTypeSellerSettlementRatio diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index a14112d0b2..a3e22ce241 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -1,10 +1,14 @@ package keeper import ( + "errors" + "fmt" + sdkmath "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/provenance-io/provenance/x/exchange" ) // flatFeeKeyMakers are the key and prefix maker funcs for a specific flat fee entry. @@ -13,14 +17,19 @@ type flatFeeKeyMakers struct { prefix func(marketID uint32) []byte } +type ratioKeyMakers struct { + key func(marketID uint32, ratio exchange.FeeRatio) []byte + prefix func(marketID uint32) []byte +} + var ( - // createAskKeyMakers are the key and prefix makers for the create-ask flat fees. - createAskKeyMakers = flatFeeKeyMakers{ + // createAskFlatKeyMakers are the key and prefix makers for the create-ask flat fees. + createAskFlatKeyMakers = flatFeeKeyMakers{ key: MakeKeyMarketCreateAskFlatFee, prefix: GetKeyPrefixMarketCreateAskFlatFee, } - // createBidKeyMakers are the key and prefix makers for the create-bid flat fees. - createBidKeyMakers = flatFeeKeyMakers{ + // createBidFlatKeyMakers are the key and prefix makers for the create-bid flat fees. + createBidFlatKeyMakers = flatFeeKeyMakers{ key: MakeKeyMarketCreateBidFlatFee, prefix: GetKeyPrefixMarketCreateBidFlatFee, } @@ -29,13 +38,33 @@ var ( key: MakeKeyMarketSellerSettlementFlatFee, prefix: GetKeyPrefixMarketSellerSettlementFlatFee, } + // sellerSettlementRatioKeyMakers are the key and prefix makers for the seller settlement fee ratios. + sellerSettlementRatioKeyMakers = ratioKeyMakers{ + key: MakeKeyMarketSellerSettlementRatio, + prefix: GetKeyPrefixMarketSellerSettlementRatio, + } // sellerSettlementFlatKeyMakers are the key and prefix makers for the buyer settlement flat fees. buyerSettlementFlatKeyMakers = flatFeeKeyMakers{ key: MakeKeyMarketBuyerSettlementFlatFee, prefix: GetKeyPrefixMarketBuyerSettlementFlatFee, } + // buyerSettlementRatioKeyMakers are the key and prefix makers for the buyer settlement fee ratios. + buyerSettlementRatioKeyMakers = ratioKeyMakers{ + key: MakeKeyMarketBuyerSettlementRatio, + prefix: GetKeyPrefixMarketBuyerSettlementRatio, + } ) +// hasFlatFee returns true if this market has any flat fee for a given type. +func hasFlatFee(store sdk.KVStore, marketID uint32, maker flatFeeKeyMakers) bool { + rv := false + iterate(store, maker.prefix(marketID), func(key, value []byte) bool { + rv = true + return true + }) + return rv +} + // getFlatFee is a generic getter for a flat fee coin entry. func getFlatFee(store sdk.KVStore, marketID uint32, denom string, maker flatFeeKeyMakers) *sdk.Coin { key := maker.key(marketID, denom) @@ -59,22 +88,14 @@ func setFlatFee(store sdk.KVStore, marketID uint32, coin sdk.Coin, maker flatFee // getAllCoins gets all the coin entries from the store with the given prefix. // The denom comes from the part of the key after the prefix, and the amount comes from the values. func (k Keeper) getAllCoins(ctx sdk.Context, pre []byte) []sdk.Coin { - // Using a prefixed store here so that iter.Key() doesn't contain the prefix (just the denom). - pStore := prefix.NewStore(ctx.KVStore(k.storeKey), pre) - iter := pStore.Iterator(nil, nil) - defer iter.Close() - var coins []sdk.Coin - for ; iter.Valid(); iter.Next() { - denom := string(iter.Key()) - value := string(iter.Value()) - amt, ok := sdkmath.NewIntFromString(value) - if !ok { - continue + k.iterate(ctx, pre, func(key, value []byte) bool { + amt, ok := sdkmath.NewIntFromString(string(value)) + if ok { + coins = append(coins, sdk.Coin{Denom: string(key), Amount: amt}) } - coins = append(coins, sdk.Coin{Denom: denom, Amount: amt}) - } - + return false + }) return coins } @@ -89,37 +110,101 @@ func (k Keeper) setAllCoins(ctx sdk.Context, marketID uint32, options []sdk.Coin } } +// hasFeeRatio returns true if this market has any fee ratios for a given type. +func hasFeeRatio(store sdk.KVStore, marketID uint32, maker ratioKeyMakers) bool { + rv := false + iterate(store, maker.prefix(marketID), func(key, value []byte) bool { + rv = true + return true + }) + return rv +} + +// getFeeRatio is a generic getter for a fee ratio entry in the store. +func getFeeRatio(store sdk.KVStore, marketID uint32, priceDenom, feeDenom string, maker ratioKeyMakers) *exchange.FeeRatio { + key := maker.key(marketID, exchange.FeeRatio{Price: sdk.Coin{Denom: priceDenom}, Fee: sdk.Coin{Denom: feeDenom}}) + if store.Has(key) { + value := store.Get(key) + priceAmount, feeAmount, err := ParseFeeRatioStoreValue(value) + if err == nil { + return &exchange.FeeRatio{ + Price: sdk.Coin{Denom: priceDenom, Amount: priceAmount}, + Fee: sdk.Coin{Denom: feeDenom, Amount: feeAmount}, + } + } + } + return nil +} + +// setFeeRatio is a generic setter for a fee ratio entry in the store. +func setFeeRatio(store sdk.KVStore, marketID uint32, ratio exchange.FeeRatio, maker ratioKeyMakers) { + key := maker.key(marketID, ratio) + value := GetFeeRatioStoreValue(ratio) + store.Set(key, value) +} + +// getAllFeeRatios gets all the fee ratio entries from the store with the given prefix. +// The denoms come from the keys and amounts come from the values. +func (k Keeper) getAllFeeRatios(ctx sdk.Context, pre []byte) []exchange.FeeRatio { + var feeRatios []exchange.FeeRatio + k.iterate(ctx, pre, func(key, value []byte) bool { + priceDenom, feeDenom, kerr := ParseKeySuffixSettlementRatio(key) + if kerr == nil { + priceAmount, feeAmount, verr := ParseFeeRatioStoreValue(value) + if verr == nil { + feeRatios = append(feeRatios, exchange.FeeRatio{ + Price: sdk.Coin{Denom: priceDenom, Amount: priceAmount}, + Fee: sdk.Coin{Denom: feeDenom, Amount: feeAmount}, + }) + } + } + return false + }) + return feeRatios +} + +// setAllFeeRatios is a generic setter for a set of fee ratios. +// This will delete all previous options then save the ones provided. I.e. if ratios doesn't have a +// price/fee denom pair that currently exists in the store, those pairs will no longer be in the store after this. +func (k Keeper) setAllFeeRatios(ctx sdk.Context, marketID uint32, ratios []exchange.FeeRatio, maker ratioKeyMakers) { + store := ctx.KVStore(k.storeKey) + deleteAll(store, maker.prefix(marketID)) + for _, ratio := range ratios { + setFeeRatio(store, marketID, ratio, maker) + } +} + // SetCreateAskFlatFees sets the create-ask flat fees for a market. func (k Keeper) SetCreateAskFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { - k.setAllCoins(ctx, marketID, options, createAskKeyMakers) + k.setAllCoins(ctx, marketID, options, createAskFlatKeyMakers) } // GetCreateAskFlatFees gets the create-ask flat fee options for a market. func (k Keeper) GetCreateAskFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return k.getAllCoins(ctx, createAskKeyMakers.prefix(marketID)) + return k.getAllCoins(ctx, createAskFlatKeyMakers.prefix(marketID)) } // IsAcceptableCreateAskFlatFee returns true if the provide fee has a denom accepted as a create-ask flat fee, // and the fee amount is at least as much as the required amount of that denom. func (k Keeper) IsAcceptableCreateAskFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { - reqFee := getFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom, createAskKeyMakers) + reqFee := getFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom, createAskFlatKeyMakers) return reqFee != nil && reqFee.Amount.LTE(fee.Amount) } // SetCreateBidFlatFees sets the create-bid flat fees for a market. func (k Keeper) SetCreateBidFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { - k.setAllCoins(ctx, marketID, options, createBidKeyMakers) + k.setAllCoins(ctx, marketID, options, createBidFlatKeyMakers) } // GetCreateBidFlatFees gets the create-bid flat fee options for a market. func (k Keeper) GetCreateBidFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return k.getAllCoins(ctx, createBidKeyMakers.prefix(marketID)) + return k.getAllCoins(ctx, createBidFlatKeyMakers.prefix(marketID)) } // IsAcceptableCreateBidFlatFee returns true if the provide fee has a denom accepted as a create-bid flat fee, // and the fee amount is at least as much as the required amount of that denom. func (k Keeper) IsAcceptableCreateBidFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { - reqFee := getFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom, createBidKeyMakers) + reqFee := getFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom, createBidFlatKeyMakers) return reqFee != nil && reqFee.Amount.LTE(fee.Amount) } @@ -140,6 +225,29 @@ func (k Keeper) IsAcceptableSellerSettlementFlatFee(ctx sdk.Context, marketID ui return reqFee != nil && reqFee.Amount.LTE(fee.Amount) } +// SetSellerSettlementRatios sets the seller settlement fee ratios for a market. +func (k Keeper) SetSellerSettlementRatios(ctx sdk.Context, marketID uint32, ratios []exchange.FeeRatio) { + k.setAllFeeRatios(ctx, marketID, ratios, sellerSettlementRatioKeyMakers) +} + +// GetSellerSettlementRatios gets the seller settlement fee ratios for a market. +func (k Keeper) GetSellerSettlementRatios(ctx sdk.Context, marketID uint32) []exchange.FeeRatio { + return k.getAllFeeRatios(ctx, sellerSettlementRatioKeyMakers.prefix(marketID)) +} + +// CalculateSellerSettlementFeeFromRatio calculates the seller settlement fee required for the given price. +func (k Keeper) CalculateSellerSettlementFeeFromRatio(ctx sdk.Context, marketID uint32, price sdk.Coin) (sdk.Coin, error) { + ratio := getFeeRatio(ctx.KVStore(k.storeKey), marketID, price.Denom, price.Denom, sellerSettlementRatioKeyMakers) + if ratio == nil { + return sdk.Coin{Amount: sdkmath.ZeroInt()}, fmt.Errorf("no seller settlement fee ratio found for denom %q", price.Denom) + } + rv, err := ratio.ApplyTo(price) + if err != nil { + err = fmt.Errorf("seller settlement fees: %w", err) + } + return rv, err +} + // SetBuyerSettlementFlatFees sets the buyer settlement flat fees for a market. func (k Keeper) SetBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { k.setAllCoins(ctx, marketID, options, buyerSettlementFlatKeyMakers) @@ -149,3 +257,169 @@ func (k Keeper) SetBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32, opt func (k Keeper) GetBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { return k.getAllCoins(ctx, buyerSettlementFlatKeyMakers.prefix(marketID)) } + +// SetBuyerSettlementRatios sets the buyer settlement fee ratios for a market. +func (k Keeper) SetBuyerSettlementRatios(ctx sdk.Context, marketID uint32, ratios []exchange.FeeRatio) { + k.setAllFeeRatios(ctx, marketID, ratios, buyerSettlementRatioKeyMakers) +} + +// GetBuyerSettlementRatios gets the buyer settlement fee ratios for a market. +func (k Keeper) GetBuyerSettlementRatios(ctx sdk.Context, marketID uint32) []exchange.FeeRatio { + return k.getAllFeeRatios(ctx, buyerSettlementRatioKeyMakers.prefix(marketID)) +} + +// GetBuyerSettlementFeeRatiosForPriceDenom gets all the buyer settlement fee ratios in a market that have the give price denom. +func (k Keeper) GetBuyerSettlementFeeRatiosForPriceDenom(ctx sdk.Context, marketID uint32, priceDenom string) []exchange.FeeRatio { + var ratios []exchange.FeeRatio + k.iterate(ctx, GetKeyPrefixMarketBuyerSettlementRatioForPriceDenom(marketID, priceDenom), func(key, value []byte) bool { + feeDenom := string(key) + priceAmount, feeAmount, err := ParseFeeRatioStoreValue(value) + if err == nil { + ratios = append(ratios, exchange.FeeRatio{ + Price: sdk.Coin{Denom: priceDenom, Amount: priceAmount}, + Fee: sdk.Coin{Denom: feeDenom, Amount: feeAmount}, + }) + } + return false + }) + return ratios +} + +// CalculateBuyerSettlementFeeOptionsFromRatios calculates the buyer settlement fee options available for the given price. +func (k Keeper) CalculateBuyerSettlementFeeOptionsFromRatios(ctx sdk.Context, marketID uint32, price sdk.Coin) ([]sdk.Coin, error) { + ratios := k.GetBuyerSettlementFeeRatiosForPriceDenom(ctx, marketID, price.Denom) + if len(ratios) == 0 { + return nil, fmt.Errorf("no buyer settlement fee ratios found with price denom %q", price.Denom) + } + + var errs []error + rv := make([]sdk.Coin, 0, len(ratios)) + for _, ratio := range ratios { + fee, err := ratio.ApplyTo(price) + if err != nil { + errs = append(errs, fmt.Errorf("buyer settlement fees: %w", err)) + } else { + rv = append(rv, fee) + } + } + + // We only return errors if no options are available. + if len(rv) == 0 { + errs = append(errs, fmt.Errorf("no applicable buyer settlement fee ratios found for price %s", price)) + return nil, errors.Join(errs...) + } + return rv, nil +} + +// IsAcceptableBuyerSettlementFee returns true if the provided fee is enough to cover both the buyer settlement flat and percent fees. +func (k Keeper) IsAcceptableBuyerSettlementFee(ctx sdk.Context, marketID uint32, price sdk.Coin, fee sdk.Coins) bool { + flatKeyMaker := buyerSettlementFlatKeyMakers + ratioKeyMaker := buyerSettlementRatioKeyMakers + store := ctx.KVStore(k.storeKey) + flatFeeReq := hasFlatFee(store, marketID, flatKeyMaker) + ratioFeeReq := hasFeeRatio(store, marketID, ratioKeyMaker) + + if !flatFeeReq && !ratioFeeReq { + return true + } + + switch len(fee) { + case 0: + return !flatFeeReq && !ratioFeeReq + case 1: + reqAmt := sdkmath.ZeroInt() + + if flatFeeReq { + flatFee := getFlatFee(store, marketID, fee[0].Denom, flatKeyMaker) + if flatFee == nil { + return false + } + reqAmt = reqAmt.Add(flatFee.Amount) + } + + if ratioFeeReq { + ratio := getFeeRatio(store, marketID, price.Denom, fee[0].Denom, ratioKeyMaker) + if ratio == nil { + return false + } + if ratio != nil { + ratioFee, err := ratio.ApplyTo(price) + if err != nil { + return false + } + reqAmt = reqAmt.Add(ratioFee.Amount) + } + } + + return reqAmt.LTE(fee[0].Amount) + default: + // Find all the flat fee options available for each fee denom, + // and all ratio fee options available for each price denom to fee denom ratio. + var flatFees []sdk.Coin + var ratioFees []sdk.Coin + for _, coin := range fee { + var flatFee, ratioFee *sdk.Coin + + if flatFeeReq { + flatFee = getFlatFee(store, marketID, coin.Denom, flatKeyMaker) + if flatFee != nil && flatFee.Amount.LTE(coin.Amount) { + // If there aren't any ratio fees required, this fee entry covers all required fees. + if !ratioFeeReq { + return true + } + // Keep track of this satisfaction. Yeah. + flatFees = append(flatFees, *flatFee) + } else { + flatFee = nil + } + } + + if ratioFeeReq { + ratio := getFeeRatio(store, marketID, price.Denom, coin.Denom, ratioKeyMaker) + if ratio != nil { + ratioFeeCalc, err := ratio.ApplyTo(price) + if err == nil { + // If there aren't any flat fees required, this fee entry covers all required fees. + if !flatFeeReq { + return true + } + // Keep track of this satisfaction. Uh huh. + ratioFees = append(ratioFees, ratioFeeCalc) + ratioFee = &ratioFeeCalc + } + } + } + + // If this coin's denom is an option for BOTH flat and ratio portions, and is enough to cover the sum, + // this one coin satisfies both together, and we're good to go. + if flatFee != nil && ratioFee != nil && flatFee.Amount.Add(ratioFee.Amount).LTE(coin.Amount) { + return true + } + } + + // If there's no flat fees, but one is required, the flat fee isn't covered in the fee provided. + if len(flatFees) == 0 && hasFlatFee(store, marketID, flatKeyMaker) { + return false + } + // If there's no ratio fees, but one is required, the ratio fee isn't covered in the fee provided. + if len(ratioFees) == 0 && hasFeeRatio(store, marketID, ratioKeyMaker) { + return false + } + // If there's exactly one of each, and they have the same denom, + // the fee of that denom was NOT enough to cover both together. + if len(flatFees) == 1 && len(ratioFees) == 1 && flatFees[0].Denom == ratioFees[0].Denom { + return false + } + + // Flat fee requirements are met because either: + // a) no flat fee was required. + // b) one or more fee coins are enough to cover the flat fee. + // Ratio fee requirements are met because either: + // a) no ratio fee was required. + // b) one or more fee coins are enough to cover the ratio fee. + // We also know that, if there's exactly one of each, they have different denoms, so + // it's NOT the case where (flat or ratio fee) <= (the other type of fee) <= fee provided <= sum of required fees. + // I.e. if there are exactly one of each, they each satisfy a different fee type requirement. + return true + } +} diff --git a/x/exchange/market.go b/x/exchange/market.go index 09fe4e961e..8e2b5bdac6 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -250,6 +250,23 @@ func (r FeeRatio) Equals(other FeeRatio) bool { return r.Price.Equal(other.Price) && r.Fee.Equal(other.Fee) } +// ApplyTo attempts to calculate the fee that results from applying this fee ratio to the provided price. +func (r FeeRatio) ApplyTo(price sdk.Coin) (sdk.Coin, error) { + rv := sdk.Coin{Denom: "", Amount: sdk.ZeroInt()} + if r.Price.Denom != price.Denom { + return rv, fmt.Errorf("cannot apply ratio %s to price %s: incorrect price denom", r, price) + } + if r.Price.Amount.IsZero() { + return rv, fmt.Errorf("cannot apply ratio %s to price %s: division by zero", r, price) + } + if !price.Amount.Mod(r.Price.Amount).IsZero() { + return rv, fmt.Errorf("cannot apply ratio %s to price %s: price amount cannot be evenly divided by ratio price", r, price) + } + rv.Denom = r.Fee.Denom + rv.Amount = price.Amount.Quo(r.Price.Amount).Mul(r.Fee.Amount) + return rv, nil +} + // IntersectionOfFeeRatios returns all FeeRatios that are in both lists. func IntersectionOfFeeRatios(ratios1, ratios2 []FeeRatio) []FeeRatio { var rv []FeeRatio diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 27570fcd11..b2f1b46131 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -1004,6 +1004,90 @@ func TestFeeRatio_Equals(t *testing.T) { } } +func TestFeeRatio_ApplyTo(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + bigCoin := func(amount string, denom string) sdk.Coin { + amt, ok := sdkmath.NewIntFromString(amount) + require.True(t, ok, "sdkmath.NewIntFromString(%q) ok result boolean", amount) + return sdk.Coin{Denom: denom, Amount: amt} + } + feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { + return FeeRatio{ + Price: coin(priceAmount, priceDenom), + Fee: coin(feeAmount, feeDenom), + } + } + + tests := []struct { + name string + ratio FeeRatio + price sdk.Coin + exp sdk.Coin + expErr string + }{ + { + name: "wrong denom", + ratio: feeRatio(1, "pdenom", 1, "fdenom"), + price: coin(1, "fdenom"), + expErr: "cannot apply ratio 1pdenom:1fdenom to price 1fdenom: incorrect price denom", + }, + { + name: "ratio price amount is zero", + ratio: feeRatio(0, "pdenom", 1, "fdenom"), + price: coin(1, "pdenom"), + expErr: "cannot apply ratio 0pdenom:1fdenom to price 1pdenom: division by zero", + }, + { + name: "indivisible", + ratio: feeRatio(14, "pdenom", 1, "fdenom"), + price: coin(7, "pdenom"), + expErr: "cannot apply ratio 14pdenom:1fdenom to price 7pdenom: price amount cannot be evenly divided by ratio price", + }, + { + name: "price equals ratio price", + ratio: feeRatio(55, "pdenom", 3, "fdenom"), + price: coin(55, "pdenom"), + exp: coin(3, "fdenom"), + }, + { + name: "three times ratio price", + ratio: feeRatio(13, "pdenom", 17, "fdenom"), + price: coin(39, "pdenom"), + exp: coin(51, "fdenom"), + }, + { + name: "very big price", + ratio: feeRatio(1_000_000_000, "pdenom", 3, "fdenom"), + price: bigCoin("1000000000000000000000", "pdenom"), + exp: coin(3_000_000_000_000, "fdenom"), + }, + { + name: "same price and fee denoms", + ratio: feeRatio(100, "nhash", 1, "nhash"), + price: coin(5_000_000_000, "nhash"), + exp: coin(50_000_000, "nhash"), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if len(tc.expErr) > 0 { + tc.exp = coin(0, "") + } + var actual sdk.Coin + var err error + testFunc := func() { + actual, err = tc.ratio.ApplyTo(tc.price) + } + require.NotPanics(t, testFunc, "%s.ApplyTo(%s)", tc.ratio, tc.price) + assertions.AssertErrorValue(t, err, tc.expErr, "%s.ApplyTo(%s) error", tc.ratio, tc.price) + assert.Equal(t, tc.exp.String(), actual.String(), "%s.ApplyTo(%s) result", tc.ratio, tc.price) + }) + } +} + func TestIntersectionOfFeeRatios(t *testing.T) { feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { return FeeRatio{ From c66113cc92bc8961f3ed05943196b997846fb1da Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 4 Sep 2023 14:11:23 -0600 Subject: [PATCH 071/309] [1658]: Refactor IsAcceptableBuyerSettlementFee into ValidateBuyerSettlementFee to return more info about why a fee is insufficient. --- x/exchange/keeper/market.go | 173 ++++++++++++++++++------------------ 1 file changed, 88 insertions(+), 85 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index a3e22ce241..6a9af2b25a 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -87,9 +87,9 @@ func setFlatFee(store sdk.KVStore, marketID uint32, coin sdk.Coin, maker flatFee // getAllCoins gets all the coin entries from the store with the given prefix. // The denom comes from the part of the key after the prefix, and the amount comes from the values. -func (k Keeper) getAllCoins(ctx sdk.Context, pre []byte) []sdk.Coin { +func getAllCoins(store sdk.KVStore, pre []byte) []sdk.Coin { var coins []sdk.Coin - k.iterate(ctx, pre, func(key, value []byte) bool { + iterate(store, pre, func(key, value []byte) bool { amt, ok := sdkmath.NewIntFromString(string(value)) if ok { coins = append(coins, sdk.Coin{Denom: string(key), Amount: amt}) @@ -99,6 +99,12 @@ func (k Keeper) getAllCoins(ctx sdk.Context, pre []byte) []sdk.Coin { return coins } +// getAllCoins gets all the coin entries from the store with the given prefix. +// The denom comes from the part of the key after the prefix, and the amount comes from the values. +func (k Keeper) getAllCoins(ctx sdk.Context, pre []byte) []sdk.Coin { + return getAllCoins(ctx.KVStore(k.storeKey), pre) +} + // setAllCoins is a generic setter for a set of flat fee options. // This will delete all previous options then save the ones provided. I.e. if options doesn't have a // denom that currently exists in the store, those denoms will no longer be in the store after this. @@ -311,8 +317,9 @@ func (k Keeper) CalculateBuyerSettlementFeeOptionsFromRatios(ctx sdk.Context, ma return rv, nil } -// IsAcceptableBuyerSettlementFee returns true if the provided fee is enough to cover both the buyer settlement flat and percent fees. -func (k Keeper) IsAcceptableBuyerSettlementFee(ctx sdk.Context, marketID uint32, price sdk.Coin, fee sdk.Coins) bool { +// ValidateBuyerSettlementFee returns an error if the provided fee is not enough to cover both the +// buyer settlement flat and percent fees for the given price. +func (k Keeper) ValidateBuyerSettlementFee(ctx sdk.Context, marketID uint32, price sdk.Coin, fee sdk.Coins) error { flatKeyMaker := buyerSettlementFlatKeyMakers ratioKeyMaker := buyerSettlementRatioKeyMakers store := ctx.KVStore(k.storeKey) @@ -320,106 +327,102 @@ func (k Keeper) IsAcceptableBuyerSettlementFee(ctx sdk.Context, marketID uint32, ratioFeeReq := hasFeeRatio(store, marketID, ratioKeyMaker) if !flatFeeReq && !ratioFeeReq { - return true + // no fee required. All good. + return nil + } + + if len(fee) == 0 { + // a fee is required, but we have none. + return errors.New("insufficient buyer settlement fee: no fee provided") } - switch len(fee) { - case 0: - return !flatFeeReq && !ratioFeeReq - case 1: - reqAmt := sdkmath.ZeroInt() + // Loop through each coin in the fee looking for entries that satisfy the fee requirements. + var flatFeeOk, ratioFeeOk bool + var errs []error + for _, feeCoin := range fee { + var flatFeeAmt, ratioFeeAmt *sdkmath.Int if flatFeeReq { - flatFee := getFlatFee(store, marketID, fee[0].Denom, flatKeyMaker) - if flatFee == nil { - return false + flatFee := getFlatFee(store, marketID, feeCoin.Denom, flatKeyMaker) + switch { + case flatFee == nil: + errs = append(errs, fmt.Errorf("no flat fee options available for denom %s", feeCoin.Denom)) + case feeCoin.Amount.LT(flatFee.Amount): + errs = append(errs, fmt.Errorf("%s is less than required flat fee %s", feeCoin, flatFee)) + case !ratioFeeReq: + // This fee coin covers the flat fee, and there is no ratio fee needed, so we're all good. + return nil + case ratioFeeOk: + // A previous fee coin covered the ratio fee and this one covers the flat fee, so we're all good. + return nil + default: + flatFeeAmt = &flatFee.Amount } - reqAmt = reqAmt.Add(flatFee.Amount) } if ratioFeeReq { - ratio := getFeeRatio(store, marketID, price.Denom, fee[0].Denom, ratioKeyMaker) + ratio := getFeeRatio(store, marketID, price.Denom, feeCoin.Denom, ratioKeyMaker) if ratio == nil { - return false - } - if ratio != nil { + errs = append(errs, fmt.Errorf("no ratio from price denom %s to fee denom %s", + price.Denom, feeCoin.Denom)) + } else { ratioFee, err := ratio.ApplyTo(price) - if err != nil { - return false + switch { + case err != nil: + errs = append(errs, err) + case feeCoin.Amount.LT(ratioFee.Amount): + errs = append(errs, fmt.Errorf("%s is less than required ratio fee %s (based on price %s and ratio %s)", + feeCoin, ratioFee, price, ratio)) + case !flatFeeReq: + // This fee coin covers the ratio fee, and there's no flat fee needed, so we're all good. + return nil + case flatFeeOk: + // A previous fee coin covered the flat fee and this one covers the ratio fee, so we're all good. + return nil + default: + ratioFeeAmt = &ratioFee.Amount } - reqAmt = reqAmt.Add(ratioFee.Amount) } } - return reqAmt.LTE(fee[0].Amount) - default: - // Find all the flat fee options available for each fee denom, - // and all ratio fee options available for each price denom to fee denom ratio. - var flatFees []sdk.Coin - var ratioFees []sdk.Coin - for _, coin := range fee { - var flatFee, ratioFee *sdk.Coin - - if flatFeeReq { - flatFee = getFlatFee(store, marketID, coin.Denom, flatKeyMaker) - if flatFee != nil && flatFee.Amount.LTE(coin.Amount) { - // If there aren't any ratio fees required, this fee entry covers all required fees. - if !ratioFeeReq { - return true - } - // Keep track of this satisfaction. Yeah. - flatFees = append(flatFees, *flatFee) - } else { - flatFee = nil - } - } - - if ratioFeeReq { - ratio := getFeeRatio(store, marketID, price.Denom, coin.Denom, ratioKeyMaker) - if ratio != nil { - ratioFeeCalc, err := ratio.ApplyTo(price) - if err == nil { - // If there aren't any flat fees required, this fee entry covers all required fees. - if !flatFeeReq { - return true - } - // Keep track of this satisfaction. Uh huh. - ratioFees = append(ratioFees, ratioFeeCalc) - ratioFee = &ratioFeeCalc - } - } - } - - // If this coin's denom is an option for BOTH flat and ratio portions, and is enough to cover the sum, - // this one coin satisfies both together, and we're good to go. - if flatFee != nil && ratioFee != nil && flatFee.Amount.Add(ratioFee.Amount).LTE(coin.Amount) { - return true + // If we have both a satisfactory flat and ratio fee, check this fee coin against the sum. + if flatFeeAmt != nil && ratioFeeAmt != nil { + reqAmt := flatFeeAmt.Add(*ratioFeeAmt) + if feeCoin.Amount.LT(reqAmt) { + errs = append(errs, fmt.Errorf("%s is less than combined fee %s%s = flat %s%s + ratio %s%s (based on price %s)", + feeCoin, reqAmt, fee[0].Denom, flatFeeAmt, fee[0].Denom, ratioFeeAmt, fee[0].Denom, price)) + } else { + // This one coin fee is so satisfying. (How satisfying was it?) + // It's so satisfying, it covers both! Thank you for coming to my TED talk. + return nil } } - // If there's no flat fees, but one is required, the flat fee isn't covered in the fee provided. - if len(flatFees) == 0 && hasFlatFee(store, marketID, flatKeyMaker) { - return false - } - // If there's no ratio fees, but one is required, the ratio fee isn't covered in the fee provided. - if len(ratioFees) == 0 && hasFeeRatio(store, marketID, ratioKeyMaker) { - return false + // Keep track of satisfied requirements for the next fee coin. + if flatFeeAmt != nil { + flatFeeOk = true } - // If there's exactly one of each, and they have the same denom, - // the fee of that denom was NOT enough to cover both together. - if len(flatFees) == 1 && len(ratioFees) == 1 && flatFees[0].Denom == ratioFees[0].Denom { - return false + if ratioFeeAmt != nil { + ratioFeeOk = true } + } - // Flat fee requirements are met because either: - // a) no flat fee was required. - // b) one or more fee coins are enough to cover the flat fee. - // Ratio fee requirements are met because either: - // a) no ratio fee was required. - // b) one or more fee coins are enough to cover the ratio fee. - // We also know that, if there's exactly one of each, they have different denoms, so - // it's NOT the case where (flat or ratio fee) <= (the other type of fee) <= fee provided <= sum of required fees. - // I.e. if there are exactly one of each, they each satisfy a different fee type requirement. - return true + // Programmer Sanity check. + // Either we returned earlier or we added at least one entry to errs. + if len(errs) == 0 { + panic("no specific errors identified") + } + + // If a fee type was required, but not satisfied, add that to the errors for easier identification by users. + if flatFeeReq && !flatFeeOk { + errs = append(errs, errors.New("required flat fee not satisfied")) } + if ratioFeeReq && !ratioFeeOk { + errs = append(errs, errors.New("required ratio fee not satisfied")) + } + + // And add an error with the overall reason for this failure. + errs = append(errs, fmt.Errorf("insufficient buyer settlement fee %s", fee)) + + return errors.Join(errs...) } From cbe413029160c2f608c8ba9b33afdee419529a89 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 4 Sep 2023 15:51:54 -0600 Subject: [PATCH 072/309] [1658]: Create keeper.getStore and use that everywhere. Write flat and ratio fee entry updaters. --- x/exchange/keeper/keeper.go | 7 +- x/exchange/keeper/market.go | 175 ++++++++++++++++++++++++------------ x/exchange/keeper/params.go | 4 +- 3 files changed, 127 insertions(+), 59 deletions(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 58dda2c125..6f58026b42 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -69,9 +69,14 @@ func iterate(store sdk.KVStore, pre []byte, cb func(key, value []byte) bool) { } } +// getStore gets the store for the exchange module. +func (k Keeper) getStore(ctx sdk.Context) sdk.KVStore { + return ctx.KVStore(k.storeKey) +} + // iterate iterates over all the entries in the store with the given prefix. // The key provided to the callback will NOT have the provided prefix; it will be everything after it. // The callback should return false to continue iteration, or true to stop. func (k Keeper) iterate(ctx sdk.Context, pre []byte, cb func(key, value []byte) bool) { - iterate(ctx.KVStore(k.storeKey), pre, cb) + iterate(k.getStore(ctx), pre, cb) } diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 6a9af2b25a..0c1f6789cf 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -3,6 +3,7 @@ package keeper import ( "errors" "fmt" + "strings" sdkmath "cosmossdk.io/math" @@ -17,6 +18,7 @@ type flatFeeKeyMakers struct { prefix func(marketID uint32) []byte } +// ratioKeyMakers are the key and prefix maker funcs for a specific ratio fee entry. type ratioKeyMakers struct { key func(marketID uint32, ratio exchange.FeeRatio) []byte prefix func(marketID uint32) []byte @@ -85,11 +87,11 @@ func setFlatFee(store sdk.KVStore, marketID uint32, coin sdk.Coin, maker flatFee store.Set(key, []byte(value)) } -// getAllCoins gets all the coin entries from the store with the given prefix. +// getAllFlatFees gets all the coin entries from the store with the given prefix. // The denom comes from the part of the key after the prefix, and the amount comes from the values. -func getAllCoins(store sdk.KVStore, pre []byte) []sdk.Coin { +func getAllFlatFees(store sdk.KVStore, marketID uint32, maker flatFeeKeyMakers) []sdk.Coin { var coins []sdk.Coin - iterate(store, pre, func(key, value []byte) bool { + iterate(store, maker.prefix(marketID), func(key, value []byte) bool { amt, ok := sdkmath.NewIntFromString(string(value)) if ok { coins = append(coins, sdk.Coin{Denom: string(key), Amount: amt}) @@ -99,23 +101,27 @@ func getAllCoins(store sdk.KVStore, pre []byte) []sdk.Coin { return coins } -// getAllCoins gets all the coin entries from the store with the given prefix. -// The denom comes from the part of the key after the prefix, and the amount comes from the values. -func (k Keeper) getAllCoins(ctx sdk.Context, pre []byte) []sdk.Coin { - return getAllCoins(ctx.KVStore(k.storeKey), pre) -} - -// setAllCoins is a generic setter for a set of flat fee options. +// setAllFlatFees is a generic setter for a set of flat fee options. // This will delete all previous options then save the ones provided. I.e. if options doesn't have a // denom that currently exists in the store, those denoms will no longer be in the store after this. -func (k Keeper) setAllCoins(ctx sdk.Context, marketID uint32, options []sdk.Coin, maker flatFeeKeyMakers) { - store := ctx.KVStore(k.storeKey) +func setAllFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin, maker flatFeeKeyMakers) { deleteAll(store, maker.prefix(marketID)) for _, coin := range options { setFlatFee(store, marketID, coin, maker) } } +// updateFlatFees deletes all the entries with a denom in toDelete, then writes all the toWrite entries. +func updateFlatFees(store sdk.KVStore, marketID uint32, toDelete, toWrite []sdk.Coin, maker flatFeeKeyMakers) { + for _, coin := range toDelete { + key := maker.key(marketID, coin.Denom) + store.Delete(key) + } + for _, coin := range toWrite { + setFlatFee(store, marketID, coin, maker) + } +} + // hasFeeRatio returns true if this market has any fee ratios for a given type. func hasFeeRatio(store sdk.KVStore, marketID uint32, maker ratioKeyMakers) bool { rv := false @@ -151,9 +157,9 @@ func setFeeRatio(store sdk.KVStore, marketID uint32, ratio exchange.FeeRatio, ma // getAllFeeRatios gets all the fee ratio entries from the store with the given prefix. // The denoms come from the keys and amounts come from the values. -func (k Keeper) getAllFeeRatios(ctx sdk.Context, pre []byte) []exchange.FeeRatio { +func getAllFeeRatios(store sdk.KVStore, marketID uint32, maker ratioKeyMakers) []exchange.FeeRatio { var feeRatios []exchange.FeeRatio - k.iterate(ctx, pre, func(key, value []byte) bool { + iterate(store, maker.prefix(marketID), func(key, value []byte) bool { priceDenom, feeDenom, kerr := ParseKeySuffixSettlementRatio(key) if kerr == nil { priceAmount, feeAmount, verr := ParseFeeRatioStoreValue(value) @@ -172,106 +178,146 @@ func (k Keeper) getAllFeeRatios(ctx sdk.Context, pre []byte) []exchange.FeeRatio // setAllFeeRatios is a generic setter for a set of fee ratios. // This will delete all previous options then save the ones provided. I.e. if ratios doesn't have a // price/fee denom pair that currently exists in the store, those pairs will no longer be in the store after this. -func (k Keeper) setAllFeeRatios(ctx sdk.Context, marketID uint32, ratios []exchange.FeeRatio, maker ratioKeyMakers) { - store := ctx.KVStore(k.storeKey) +func setAllFeeRatios(store sdk.KVStore, marketID uint32, ratios []exchange.FeeRatio, maker ratioKeyMakers) { deleteAll(store, maker.prefix(marketID)) for _, ratio := range ratios { setFeeRatio(store, marketID, ratio, maker) } } -// SetCreateAskFlatFees sets the create-ask flat fees for a market. -func (k Keeper) SetCreateAskFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { - k.setAllCoins(ctx, marketID, options, createAskFlatKeyMakers) +// updateFeeRatios deletes all entries with the denom pairs in toDelete, then writes all the toWrite entries. +func updateFeeRatios(store sdk.KVStore, marketID uint32, toDelete, toWrite []exchange.FeeRatio, maker ratioKeyMakers) { + for _, ratio := range toDelete { + key := maker.key(marketID, ratio) + store.Delete(key) + } + for _, ratio := range toWrite { + setFeeRatio(store, marketID, ratio, maker) + } } // GetCreateAskFlatFees gets the create-ask flat fee options for a market. func (k Keeper) GetCreateAskFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return k.getAllCoins(ctx, createAskFlatKeyMakers.prefix(marketID)) + return getAllFlatFees(k.getStore(ctx), marketID, createAskFlatKeyMakers) +} + +// SetCreateAskFlatFees sets the create-ask flat fees for a market. +func (k Keeper) SetCreateAskFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { + setAllFlatFees(k.getStore(ctx), marketID, options, createAskFlatKeyMakers) +} + +// UpdateCreateAskFlatFees deletes all create-ask flat fees to delete then adds the ones to add. +func (k Keeper) UpdateCreateAskFlatFees(ctx sdk.Context, marketID uint32, toDelete, toAdd []sdk.Coin) { + updateFlatFees(k.getStore(ctx), marketID, toDelete, toAdd, createAskFlatKeyMakers) } // IsAcceptableCreateAskFlatFee returns true if the provide fee has a denom accepted as a create-ask flat fee, // and the fee amount is at least as much as the required amount of that denom. func (k Keeper) IsAcceptableCreateAskFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { - reqFee := getFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom, createAskFlatKeyMakers) - return reqFee != nil && reqFee.Amount.LTE(fee.Amount) + reqFee := getFlatFee(k.getStore(ctx), marketID, fee.Denom, createAskFlatKeyMakers) + return reqFee != nil && fee.Amount.GTE(reqFee.Amount) +} + +// GetCreateBidFlatFees gets the create-bid flat fee options for a market. +func (k Keeper) GetCreateBidFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { + return getAllFlatFees(k.getStore(ctx), marketID, createBidFlatKeyMakers) } // SetCreateBidFlatFees sets the create-bid flat fees for a market. func (k Keeper) SetCreateBidFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { - k.setAllCoins(ctx, marketID, options, createBidFlatKeyMakers) + setAllFlatFees(k.getStore(ctx), marketID, options, createBidFlatKeyMakers) } -// GetCreateBidFlatFees gets the create-bid flat fee options for a market. -func (k Keeper) GetCreateBidFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return k.getAllCoins(ctx, createBidFlatKeyMakers.prefix(marketID)) +// UpdateCreateBidFlatFees deletes all create-bid flat fees to delete then adds the ones to add. +func (k Keeper) UpdateCreateBidFlatFees(ctx sdk.Context, marketID uint32, toDelete, toAdd []sdk.Coin) { + updateFlatFees(k.getStore(ctx), marketID, toDelete, toAdd, createBidFlatKeyMakers) } // IsAcceptableCreateBidFlatFee returns true if the provide fee has a denom accepted as a create-bid flat fee, // and the fee amount is at least as much as the required amount of that denom. func (k Keeper) IsAcceptableCreateBidFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { - reqFee := getFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom, createBidFlatKeyMakers) - return reqFee != nil && reqFee.Amount.LTE(fee.Amount) + reqFee := getFlatFee(k.getStore(ctx), marketID, fee.Denom, createBidFlatKeyMakers) + return reqFee != nil && fee.Amount.GTE(reqFee.Amount) +} + +// GetSellerSettlementFlatFees gets the seller settlement flat fee options for a market. +func (k Keeper) GetSellerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { + return getAllFlatFees(k.getStore(ctx), marketID, sellerSettlementFlatKeyMakers) } // SetSellerSettlementFlatFees sets the seller settlement flat fees for a market. func (k Keeper) SetSellerSettlementFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { - k.setAllCoins(ctx, marketID, options, sellerSettlementFlatKeyMakers) + setAllFlatFees(k.getStore(ctx), marketID, options, sellerSettlementFlatKeyMakers) } -// GetSellerSettlementFlatFees gets the seller settlement flat fee options for a market. -func (k Keeper) GetSellerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return k.getAllCoins(ctx, sellerSettlementFlatKeyMakers.prefix(marketID)) +// UpdateSellerSettlementFlatFees deletes all seller settlement flat fees to delete then adds the ones to add. +func (k Keeper) UpdateSellerSettlementFlatFees(ctx sdk.Context, marketID uint32, toDelete, toAdd []sdk.Coin) { + updateFlatFees(k.getStore(ctx), marketID, toDelete, toAdd, sellerSettlementFlatKeyMakers) } // IsAcceptableSellerSettlementFlatFee returns true if the provide fee has a denom accepted as a seller settlement // flat fee, and the fee amount is at least as much as the required amount of that denom. func (k Keeper) IsAcceptableSellerSettlementFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { - reqFee := getFlatFee(ctx.KVStore(k.storeKey), marketID, fee.Denom, sellerSettlementFlatKeyMakers) - return reqFee != nil && reqFee.Amount.LTE(fee.Amount) + reqFee := getFlatFee(k.getStore(ctx), marketID, fee.Denom, sellerSettlementFlatKeyMakers) + return reqFee != nil && fee.Amount.GTE(reqFee.Amount) +} + +// GetSellerSettlementRatios gets the seller settlement fee ratios for a market. +func (k Keeper) GetSellerSettlementRatios(ctx sdk.Context, marketID uint32) []exchange.FeeRatio { + return getAllFeeRatios(k.getStore(ctx), marketID, sellerSettlementRatioKeyMakers) } // SetSellerSettlementRatios sets the seller settlement fee ratios for a market. func (k Keeper) SetSellerSettlementRatios(ctx sdk.Context, marketID uint32, ratios []exchange.FeeRatio) { - k.setAllFeeRatios(ctx, marketID, ratios, sellerSettlementRatioKeyMakers) + setAllFeeRatios(k.getStore(ctx), marketID, ratios, sellerSettlementRatioKeyMakers) } -// GetSellerSettlementRatios gets the seller settlement fee ratios for a market. -func (k Keeper) GetSellerSettlementRatios(ctx sdk.Context, marketID uint32) []exchange.FeeRatio { - return k.getAllFeeRatios(ctx, sellerSettlementRatioKeyMakers.prefix(marketID)) +// UpdateSellerSettlementRatios deletes all seller settlement ratio entries to delete then adds the ones to add. +func (k Keeper) UpdateSellerSettlementRatios(ctx sdk.Context, marketID uint32, toDelete, toAdd []exchange.FeeRatio) { + updateFeeRatios(k.getStore(ctx), marketID, toDelete, toAdd, sellerSettlementRatioKeyMakers) } -// CalculateSellerSettlementFeeFromRatio calculates the seller settlement fee required for the given price. -func (k Keeper) CalculateSellerSettlementFeeFromRatio(ctx sdk.Context, marketID uint32, price sdk.Coin) (sdk.Coin, error) { - ratio := getFeeRatio(ctx.KVStore(k.storeKey), marketID, price.Denom, price.Denom, sellerSettlementRatioKeyMakers) +// CalculateSellerSettlementRatioFee calculates the seller settlement fee required for the given price. +func (k Keeper) CalculateSellerSettlementRatioFee(ctx sdk.Context, marketID uint32, price sdk.Coin) (sdk.Coin, error) { + ratio := getFeeRatio(k.getStore(ctx), marketID, price.Denom, price.Denom, sellerSettlementRatioKeyMakers) if ratio == nil { - return sdk.Coin{Amount: sdkmath.ZeroInt()}, fmt.Errorf("no seller settlement fee ratio found for denom %q", price.Denom) + return sdk.Coin{}, fmt.Errorf("no seller settlement fee ratio found for denom %q", price.Denom) } rv, err := ratio.ApplyTo(price) if err != nil { - err = fmt.Errorf("seller settlement fees: %w", err) + err = fmt.Errorf("invalid seller settlement fees: %w", err) } return rv, err } +// GetBuyerSettlementFlatFees gets the buyer settlement flat fee options for a market. +func (k Keeper) GetBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { + return getAllFlatFees(k.getStore(ctx), marketID, buyerSettlementFlatKeyMakers) +} + // SetBuyerSettlementFlatFees sets the buyer settlement flat fees for a market. func (k Keeper) SetBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { - k.setAllCoins(ctx, marketID, options, buyerSettlementFlatKeyMakers) + setAllFlatFees(k.getStore(ctx), marketID, options, buyerSettlementFlatKeyMakers) } -// GetBuyerSettlementFlatFees gets the buyer settlement flat fee options for a market. -func (k Keeper) GetBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return k.getAllCoins(ctx, buyerSettlementFlatKeyMakers.prefix(marketID)) +// UpdateBuyerSettlementFlatFees deletes all buyer settlement flat fees to delete then adds the ones to add. +func (k Keeper) UpdateBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32, toDelete, toAdd []sdk.Coin) { + updateFlatFees(k.getStore(ctx), marketID, toDelete, toAdd, buyerSettlementFlatKeyMakers) +} + +// GetBuyerSettlementRatios gets the buyer settlement fee ratios for a market. +func (k Keeper) GetBuyerSettlementRatios(ctx sdk.Context, marketID uint32) []exchange.FeeRatio { + return getAllFeeRatios(k.getStore(ctx), marketID, buyerSettlementRatioKeyMakers) } // SetBuyerSettlementRatios sets the buyer settlement fee ratios for a market. func (k Keeper) SetBuyerSettlementRatios(ctx sdk.Context, marketID uint32, ratios []exchange.FeeRatio) { - k.setAllFeeRatios(ctx, marketID, ratios, buyerSettlementRatioKeyMakers) + setAllFeeRatios(k.getStore(ctx), marketID, ratios, buyerSettlementRatioKeyMakers) } -// GetBuyerSettlementRatios gets the buyer settlement fee ratios for a market. -func (k Keeper) GetBuyerSettlementRatios(ctx sdk.Context, marketID uint32) []exchange.FeeRatio { - return k.getAllFeeRatios(ctx, buyerSettlementRatioKeyMakers.prefix(marketID)) +// UpdateBuyerSettlementRatios deletes all buyer settlement ratio entries to delete then adds the ones to add. +func (k Keeper) UpdateBuyerSettlementRatios(ctx sdk.Context, marketID uint32, toDelete, toAdd []exchange.FeeRatio) { + updateFeeRatios(k.getStore(ctx), marketID, toDelete, toAdd, buyerSettlementRatioKeyMakers) } // GetBuyerSettlementFeeRatiosForPriceDenom gets all the buyer settlement fee ratios in a market that have the give price denom. @@ -291,8 +337,8 @@ func (k Keeper) GetBuyerSettlementFeeRatiosForPriceDenom(ctx sdk.Context, market return ratios } -// CalculateBuyerSettlementFeeOptionsFromRatios calculates the buyer settlement fee options available for the given price. -func (k Keeper) CalculateBuyerSettlementFeeOptionsFromRatios(ctx sdk.Context, marketID uint32, price sdk.Coin) ([]sdk.Coin, error) { +// CalculateBuyerSettlementRatioFeeOptions calculates the buyer settlement ratio fee options available for the given price. +func (k Keeper) CalculateBuyerSettlementRatioFeeOptions(ctx sdk.Context, marketID uint32, price sdk.Coin) ([]sdk.Coin, error) { ratios := k.GetBuyerSettlementFeeRatiosForPriceDenom(ctx, marketID, price.Denom) if len(ratios) == 0 { return nil, fmt.Errorf("no buyer settlement fee ratios found with price denom %q", price.Denom) @@ -322,7 +368,7 @@ func (k Keeper) CalculateBuyerSettlementFeeOptionsFromRatios(ctx sdk.Context, ma func (k Keeper) ValidateBuyerSettlementFee(ctx sdk.Context, marketID uint32, price sdk.Coin, fee sdk.Coins) error { flatKeyMaker := buyerSettlementFlatKeyMakers ratioKeyMaker := buyerSettlementRatioKeyMakers - store := ctx.KVStore(k.storeKey) + store := k.getStore(ctx) flatFeeReq := hasFlatFee(store, marketID, flatKeyMaker) ratioFeeReq := hasFeeRatio(store, marketID, ratioKeyMaker) @@ -415,10 +461,13 @@ func (k Keeper) ValidateBuyerSettlementFee(ctx sdk.Context, marketID uint32, pri // If a fee type was required, but not satisfied, add that to the errors for easier identification by users. if flatFeeReq && !flatFeeOk { - errs = append(errs, errors.New("required flat fee not satisfied")) + flatFeeOptions := getAllFlatFees(store, marketID, flatKeyMaker) + errs = append(errs, fmt.Errorf("required flat fee not satisfied, valid options: %s", flatFeeOptions)) } if ratioFeeReq && !ratioFeeOk { - errs = append(errs, errors.New("required ratio fee not satisfied")) + allRatioOptions := getAllFeeRatios(store, marketID, ratioKeyMaker) + errs = append(errs, fmt.Errorf("required ratio fee not satisfied, valid ratios: %s", + mapToJoinedStrings(allRatioOptions, exchange.FeeRatio.String))) } // And add an error with the overall reason for this failure. @@ -426,3 +475,17 @@ func (k Keeper) ValidateBuyerSettlementFee(ctx sdk.Context, marketID uint32, pri return errors.Join(errs...) } + +// mapToJoinedStrings runs the provided mapper on each of the vals and returns the resulting strings joined using ", ". +func mapToJoinedStrings[T any](vals []T, mapper func(entry T) string) string { + return strings.Join(mapToStrings(vals, mapper), ", ") +} + +// mapToStrings runs the provided mapper on each of the vals and returns the resulting slice of strings. +func mapToStrings[T any](vals []T, mapper func(entry T) string) []string { + rv := make([]string, len(vals)) + for i, val := range vals { + rv[i] = mapper(val) + } + return rv +} diff --git a/x/exchange/keeper/params.go b/x/exchange/keeper/params.go index 404cb9677e..20f4d7abc3 100644 --- a/x/exchange/keeper/params.go +++ b/x/exchange/keeper/params.go @@ -34,7 +34,7 @@ func getParamsSplit(store sdk.KVStore, denom string) (uint16, bool) { // SetParams updates the params to match those provided. // If nil is provided, all params are deleted. func (k Keeper) SetParams(ctx sdk.Context, params *exchange.Params) { - store := ctx.KVStore(k.storeKey) + store := k.getStore(ctx) deleteAllParamsSplits(store) if params != nil { setParamsSplit(store, "", uint16(params.DefaultSplit)) @@ -68,7 +68,7 @@ func (k Keeper) GetParams(ctx sdk.Context) *exchange.Params { // If the denom is "", the default is returned. // If there isn't a specific entry for the provided denom, the default is returned. func (k Keeper) GetExchangeSplit(ctx sdk.Context, denom string) uint16 { - store := ctx.KVStore(k.storeKey) + store := k.getStore(ctx) split, found := getParamsSplit(store, denom) // If it wasn't found, and we weren't already looking for the default, look up the default now. if !found && len(denom) > 0 { From 8ea7281c2904473ad3aef75efa827d2615f2bbfa Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 11:06:48 -0600 Subject: [PATCH 073/309] [1658]: The rest of the market store stuff: inactive bool, user-settle bool, permissions, and required attributes. --- x/exchange/keeper/keys.go | 67 ++++++++++-- x/exchange/keeper/keys_test.go | 8 ++ x/exchange/keeper/market.go | 184 +++++++++++++++++++++++++++++++-- 3 files changed, 240 insertions(+), 19 deletions(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index bfe9cc1d0e..8589b6d7f5 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "errors" "fmt" + "strings" sdkmath "cosmossdk.io/math" @@ -150,6 +151,25 @@ func uint64FromBz(bz []byte) uint64 { return binary.BigEndian.Uint64(bz) } +// parseLengthPrefixedAddr extracts the length-prefixed sdk.AccAddress from the front of the provided slice. +func parseLengthPrefixedAddr(key []byte) (sdk.AccAddress, []byte, error) { + if len(key) == 0 { + return nil, nil, errors.New("slice is empty") + } + l := int(key[0]) + if l == 0 { + return nil, nil, errors.New("length byte is 0") + } + if len(key) <= l { + return nil, nil, fmt.Errorf("length byte is %d, but slice length has %d left", l, len(key)-1) + } + if len(key) == l+1 { + return key[1:], nil, nil + } + return key[1 : l+1], key[l+1:], nil +} + +// keyPrefixParamsSplit creates the key prefix for a params "split" entry. func keyPrefixParamsSplit(extraCap int) []byte { return prepKey(KeyTypeParams, []byte("split"), extraCap) } @@ -249,10 +269,13 @@ func GetKeySuffixSettlementRatio(ratio exchange.FeeRatio) []byte { // ParseKeySuffixSettlementRatio parses the portion // of a settlement ratio key back into the denom strings. func ParseKeySuffixSettlementRatio(suffix []byte) (priceDenom, feeDenom string, err error) { - parts := bytes.Split(suffix, []byte{RecordSeparator}) + if len(suffix) == 0 { + return "", "", errors.New("ratio key suffix is empty") + } + parts := strings.Split(string(suffix), string(RecordSeparator)) if len(parts) == 2 { - priceDenom = string(parts[0]) - feeDenom = string(parts[1]) + priceDenom = parts[0] + feeDenom = parts[1] } else { err = fmt.Errorf("ratio key suffix %q has %d parts, expected 2", suffix, len(parts)) } @@ -276,6 +299,20 @@ func GetFeeRatioStoreValue(ratio exchange.FeeRatio) []byte { // Input is expected to have the format where both amounts are strings (of digits). // E.g. "100\\1E3" (for a price amount of 100, and fee amount of 3). func ParseFeeRatioStoreValue(value []byte) (priceAmount, feeAmount sdkmath.Int, err error) { + defer func() { + // The zero-value of sdkmath.Int{} will panic if anything tries to do anything with it (e.g. convert it to a string). + // Additionally, if there was an error, we don't want either of them to have any left-over values. + if err != nil { + priceAmount = sdkmath.ZeroInt() + feeAmount = sdkmath.ZeroInt() + } + }() + + if len(value) == 0 { + err = errors.New("ratio value is empty") + return + } + parts := bytes.Split(value, []byte{RecordSeparator}) if len(parts) == 2 { var ok bool @@ -291,13 +328,6 @@ func ParseFeeRatioStoreValue(value []byte) (priceAmount, feeAmount sdkmath.Int, err = fmt.Errorf("ratio value %q has %d parts, expected 2", value, len(parts)) } - // The zero-value of sdkmath.Int{} will panic if anything tries to do anything with it (e.g. convert it to a string). - // Additionally, if there was an error, we don't want either of them to have any left-over values. - if err != nil { - priceAmount = sdkmath.ZeroInt() - feeAmount = sdkmath.ZeroInt() - } - return } @@ -408,6 +438,18 @@ func MakeKeyMarketPermissions(marketID uint32, addr sdk.AccAddress, permission e return rv } +// ParseKeySuffixMarketPermissions parses the portion of a market permissions key. +func ParseKeySuffixMarketPermissions(suffix []byte) (sdk.AccAddress, exchange.Permission, error) { + addr, remainder, err := parseLengthPrefixedAddr(suffix) + if err != nil { + return nil, 0, fmt.Errorf("cannot parse address from market permissions key: %w", err) + } + if len(remainder) != 1 { + return nil, 0, fmt.Errorf("cannot parse market permissions key: found %d bytes after address, expected 1", len(remainder)) + } + return addr, exchange.Permission(remainder[0]), nil +} + // marketKeyPrefixReqAttr creates the key prefix for a market's required attributes entries with an extra byte of capacity for the order type. func marketKeyPrefixReqAttr(marketID uint32) []byte { return keyPrefixMarketType(marketID, MarketKeyTypeReqAttr, 1) @@ -427,6 +469,11 @@ func MakeKeyMarketReqAttrBid(marketID uint32) []byte { return rv } +// ParseReqAttrStoreValue parses a required attribute store value into it's string slice. +func ParseReqAttrStoreValue(value []byte) []string { + return strings.Split(string(value), string(RecordSeparator)) +} + // keyPrefixOrder creates the key prefix for orders with the provide extra capacity for additional elements. func keyPrefixOrder(orderID uint64, extraCap int) []byte { return prepKey(KeyTypeOrder, uint64Bz(orderID), extraCap) diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 4d1331bebf..a09d864f7a 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -124,6 +124,8 @@ func TestKeyTypeUniqueness(t *testing.T) { } } +// TODO[1658]: func TestParseLengthPrefixedAddr(t *testing.T) + func TestGetKeyPrefixParamsSplit(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { @@ -804,6 +806,7 @@ func TestParseKeySuffixSettlementRatio(t *testing.T) { suffix: []byte{keeper.RecordSeparator, 'b', keeper.RecordSeparator}, expErr: "ratio key suffix \"\\x1eb\\x1e\" has 3 parts, expected 2", }, + // TODO[1658]: Test cases for ParseKeySuffixSettlementRatio empty suffix } for _, tc := range tests { @@ -1012,6 +1015,7 @@ func TestParseFeeRatioStoreValue(t *testing.T) { value: []byte{rs, '1', '0', '0', rs}, expErr: "ratio value \"\\x1e100\\x1e\" has 3 parts, expected 2", }, + // TODO[1658]: Test cases for ParseFeeRatioStoreValue empty value } for _, tc := range tests { @@ -1987,6 +1991,8 @@ func TestMakeKeyMarketPermissions(t *testing.T) { } } +// TODO[1658]: func TestParseKeySuffixMarketPermissions(t *testing.T) + func TestMakeKeyMarketReqAttrAsk(t *testing.T) { marketTypeByte := keeper.MarketKeyTypeReqAttr orderTypeByte := keeper.OrderKeyTypeAsk @@ -2121,6 +2127,8 @@ func TestMakeKeyMarketReqAttrBid(t *testing.T) { } } +// TODO[1658]: func TestParseReqAttrStoreValue(t *testing.T) + func TestGetKeyPrefixOrder(t *testing.T) { tests := []struct { name string diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 0c1f6789cf..9f4fd65d00 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -467,7 +467,7 @@ func (k Keeper) ValidateBuyerSettlementFee(ctx sdk.Context, marketID uint32, pri if ratioFeeReq && !ratioFeeOk { allRatioOptions := getAllFeeRatios(store, marketID, ratioKeyMaker) errs = append(errs, fmt.Errorf("required ratio fee not satisfied, valid ratios: %s", - mapToJoinedStrings(allRatioOptions, exchange.FeeRatio.String))) + exchange.FeeRatiosString(allRatioOptions))) } // And add an error with the overall reason for this failure. @@ -476,16 +476,182 @@ func (k Keeper) ValidateBuyerSettlementFee(ctx sdk.Context, marketID uint32, pri return errors.Join(errs...) } -// mapToJoinedStrings runs the provided mapper on each of the vals and returns the resulting strings joined using ", ". -func mapToJoinedStrings[T any](vals []T, mapper func(entry T) string) string { - return strings.Join(mapToStrings(vals, mapper), ", ") +// IsMarketActive returns true if the provided market is accepting orders. +func (k Keeper) IsMarketActive(ctx sdk.Context, marketID uint32) bool { + key := MakeKeyMarketInactive(marketID) + store := k.getStore(ctx) + return !store.Has(key) +} + +// SetMarketActive sets whether the provided market is accepting orders. +func setMarketActive(store sdk.KVStore, marketID uint32, active bool) { + key := MakeKeyMarketInactive(marketID) + if active { + store.Delete(key) + } else { + store.Set(key, nil) + } } -// mapToStrings runs the provided mapper on each of the vals and returns the resulting slice of strings. -func mapToStrings[T any](vals []T, mapper func(entry T) string) []string { - rv := make([]string, len(vals)) - for i, val := range vals { - rv[i] = mapper(val) +// SetMarketActive sets whether the provided market is accepting orders. +func (k Keeper) SetMarketActive(ctx sdk.Context, marketID uint32, active bool) { + setMarketActive(k.getStore(ctx), marketID, active) +} + +// IsUserSettlementAllowed gets whether user-settlement is allowed for a market. +func (k Keeper) IsUserSettlementAllowed(ctx sdk.Context, marketID uint32) bool { + key := MakeKeyMarketUserSettle(marketID) + store := k.getStore(ctx) + return store.Has(key) +} + +// SetUserSettlementAllowed sets whether user-settlement is allowed for a market. +func setUserSettlementAllowed(store sdk.KVStore, marketID uint32, allowed bool) { + key := MakeKeyMarketUserSettle(marketID) + if allowed { + store.Set(key, nil) + } else { + store.Delete(key) + } +} + +// SetUserSettlementAllowed sets whether user-settlement is allowed for a market. +func (k Keeper) SetUserSettlementAllowed(ctx sdk.Context, marketID uint32, allowed bool) { + setUserSettlementAllowed(k.getStore(ctx), marketID, allowed) +} + +// hasPermission returns true if the provided address has the given permission in the market in question. +func hasPermission(store sdk.KVStore, marketID uint32, addr sdk.AccAddress, permission exchange.Permission) bool { + key := MakeKeyMarketPermissions(marketID, addr, permission) + return store.Has(key) +} + +// grantPermissions updates the store so that the given address has the provided permissions in a market. +func grantPermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress, permissions []exchange.Permission) { + for _, perm := range permissions { + key := MakeKeyMarketPermissions(marketID, addr, perm) + store.Set(key, nil) + } +} + +// revokePermissions updates the store so that the given address does NOT have the provided permissions for the market. +func revokePermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress, permissions []exchange.Permission) { + for _, perm := range permissions { + key := MakeKeyMarketPermissions(marketID, addr, perm) + store.Delete(key) + } +} + +// revokeAllUserPermissions updates the store so that the given address does not have any permissions for the market. +func revokeAllUserPermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress) { + key := GetKeyPrefixMarketPermissionsForAddress(marketID, addr) + deleteAll(store, key) +} + +// revokeAllMarketPermissions clears out all permissions for a market. +func revokeAllMarketPermissions(store sdk.KVStore, marketID uint32) { + key := GetKeyPrefixMarketPermissions(marketID) + deleteAll(store, key) +} + +// setAllMarketPermissions clears out all market permissions then stores just the ones provided. +func setAllMarketPermissions(store sdk.KVStore, marketID uint32, grants []exchange.AccessGrant) { + revokeAllMarketPermissions(store, marketID) + for _, ag := range grants { + grantPermissions(store, marketID, sdk.MustAccAddressFromBech32(ag.Address), ag.Permissions) + } +} + +// updatePermissions revokes all permissions from the provided revokeAll bech32 addresses, then revokes all permissions +// in the toRevoke list, and lastly, grants all the permissions in toGrant. +func updatePermissions(store sdk.KVStore, marketID uint32, revokeAll []string, toRevoke, toGrant []exchange.AccessGrant) { + for _, revAddr := range revokeAll { + revokeAllUserPermissions(store, marketID, sdk.MustAccAddressFromBech32(revAddr)) + } + for _, ag := range toRevoke { + revokePermissions(store, marketID, sdk.MustAccAddressFromBech32(ag.Address), ag.Permissions) } + for _, ag := range toGrant { + grantPermissions(store, marketID, sdk.MustAccAddressFromBech32(ag.Address), ag.Permissions) + } +} + +// HasPermission returns true if the provided addr has the permission in question for a given market. +func (k Keeper) HasPermission(ctx sdk.Context, marketID uint32, addr sdk.AccAddress, permission exchange.Permission) bool { + return hasPermission(k.getStore(ctx), marketID, addr, permission) +} + +// GetUserPermissions gets all permissions that have been granted to a user in a market. +func (k Keeper) GetUserPermissions(ctx sdk.Context, marketID uint32, addr sdk.AccAddress) []exchange.Permission { + var rv []exchange.Permission + k.iterate(ctx, GetKeyPrefixMarketPermissionsForAddress(marketID, addr), func(key, _ []byte) bool { + rv = append(rv, exchange.Permission(key[0])) + return false + }) + return rv +} + +// GetAccessGrants gets all the access grants for a market. +func (k Keeper) GetAccessGrants(ctx sdk.Context, marketID uint32) []exchange.AccessGrant { + var rv []exchange.AccessGrant + var lastAG exchange.AccessGrant + k.iterate(ctx, GetKeyPrefixMarketPermissions(marketID), func(key, _ []byte) bool { + addr, perm, err := ParseKeySuffixMarketPermissions(key) + if err == nil { + if addr.String() != lastAG.Address { + lastAG = exchange.AccessGrant{Address: addr.String()} + rv = append(rv, lastAG) + } + lastAG.Permissions = append(lastAG.Permissions, perm) + } + return false + }) return rv } + +// UpdatePermissions revokes all permissions from the provided revokeAll bech32 addresses, then revokes all permissions +// in the toRevoke list, and lastly, grants all the permissions in toGrant. +func (k Keeper) UpdatePermissions(ctx sdk.Context, marketID uint32, revokeAll []string, toRevoke, toGrant []exchange.AccessGrant) { + updatePermissions(k.getStore(ctx), marketID, revokeAll, toRevoke, toGrant) +} + +// reqAttrKeyMaker is a function that returns a key for required attributes. +type reqAttrKeyMaker func(marketID uint32) []byte + +// getReqAttr gets the required attributes for a market using the provided key maker. +func getReqAttr(store sdk.KVStore, marketID uint32, maker reqAttrKeyMaker) []string { + key := maker(marketID) + value := store.Get(key) + return ParseReqAttrStoreValue(value) +} + +// setReqAttr sets the required attributes for a market using the provided key maker. +func setReqAttr(store sdk.KVStore, marketID uint32, reqAttrs []string, maker reqAttrKeyMaker) { + key := maker(marketID) + if len(reqAttrs) == 0 { + store.Delete(key) + } else { + value := []byte(strings.Join(reqAttrs, string(RecordSeparator))) + store.Set(key, value) + } +} + +// GetReqAttrAsk gets the attributes required to create an ask order. +func (k Keeper) GetReqAttrAsk(ctx sdk.Context, marketID uint32) []string { + return getReqAttr(k.getStore(ctx), marketID, MakeKeyMarketReqAttrAsk) +} + +// SetReqAttrAsk sets the attributes required to create an ask order. +func (k Keeper) SetReqAttrAsk(ctx sdk.Context, marketID uint32, reqAttrs []string) { + setReqAttr(k.getStore(ctx), marketID, reqAttrs, MakeKeyMarketReqAttrAsk) +} + +// GetReqAttrBid gets the attributes required to create a bid order. +func (k Keeper) GetReqAttrBid(ctx sdk.Context, marketID uint32) []string { + return getReqAttr(k.getStore(ctx), marketID, MakeKeyMarketReqAttrBid) +} + +// SetReqAttrBid sets the attributes required to create a bid order. +func (k Keeper) SetReqAttrBid(ctx sdk.Context, marketID uint32, reqAttrs []string) { + setReqAttr(k.getStore(ctx), marketID, reqAttrs, MakeKeyMarketReqAttrBid) +} From b74bdd1683589008a8d7fc3e761c6371092bd241 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 13:30:22 -0600 Subject: [PATCH 074/309] [1658]: Add a last market id store entry and write CreateMarket(...). --- x/exchange/expected_keepers.go | 13 ++++++ x/exchange/keeper/keeper.go | 13 ++++-- x/exchange/keeper/keys.go | 10 ++++ x/exchange/keeper/keys_test.go | 3 ++ x/exchange/keeper/market.go | 83 ++++++++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 x/exchange/expected_keepers.go diff --git a/x/exchange/expected_keepers.go b/x/exchange/expected_keepers.go new file mode 100644 index 0000000000..af4ddf9e62 --- /dev/null +++ b/x/exchange/expected_keepers.go @@ -0,0 +1,13 @@ +package exchange + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +type AccountKeeper interface { + GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI + SetAccount(ctx sdk.Context, acc authtypes.AccountI) + HasAccount(ctx sdk.Context, addr sdk.AccAddress) bool + NewAccount(ctx sdk.Context, acc authtypes.AccountI) authtypes.AccountI +} diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 6f58026b42..5cd999dd75 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -7,21 +7,26 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/provenance-io/provenance/x/exchange" ) type Keeper struct { cdc codec.BinaryCodec storeKey storetypes.StoreKey + + accountKeeper exchange.AccountKeeper + // TODO[1658]: Finish the Keeper struct. authority string } -func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey) Keeper { +func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, accountKeeper exchange.AccountKeeper) Keeper { // TODO[1658]: Finish NewKeeper. rv := Keeper{ - cdc: cdc, - storeKey: storeKey, - authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + cdc: cdc, + storeKey: storeKey, + accountKeeper: accountKeeper, + authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), } return rv } diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 8589b6d7f5..53bc83f754 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -23,6 +23,9 @@ import ( // Default split: 0x00 | "split" => uint16 // Specific splits: 0x00 | "split" | => uint16 // +// Last Market ID: 0x06 => uint32 +// This stores the last auto-selected market id. +// // Markets: // Some aspects of a market are stored using the accounts module and the MarketAccount type. // Others are stored in the exchange module. @@ -65,6 +68,8 @@ import ( const ( // KeyTypeParams is the type byte for params entries. KeyTypeParams = byte(0x00) + // KeyTypeLastMarketID is the type byte for the last auto-selected market id. + KeyTypeLastMarketID = byte(0x06) // KeyTypeMarket is the type byte for market entries. KeyTypeMarket = byte(0x01) // KeyTypeOrder is the type byte for order entries. @@ -187,6 +192,11 @@ func MakeKeyParamsSplit(denom string) []byte { return rv } +// MakeKeyLastMarketID creates the key for the last auto-selected market id. +func MakeKeyLastMarketID() []byte { + return []byte{KeyTypeLastMarketID} +} + // keyPrefixMarket creates the root of a market's key with extra capacity for the rest. func keyPrefixMarket(marketID uint32, extraCap int) []byte { return prepKey(KeyTypeMarket, uint32Bz(marketID), extraCap) diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index a09d864f7a..3a5921d2f3 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -81,6 +81,7 @@ func TestKeyTypeUniqueness(t *testing.T) { name: "base type bytes", types: []byteEntry{ {name: "KeyTypeParams", value: keeper.KeyTypeParams}, + {name: "KeyTypeLastMarketID", value: keeper.KeyTypeLastMarketID}, {name: "KeyTypeMarket", value: keeper.KeyTypeMarket}, {name: "KeyTypeOrder", value: keeper.KeyTypeOrder}, {name: "KeyTypeMarketToOrderIndex", value: keeper.KeyTypeMarketToOrderIndex}, @@ -175,6 +176,8 @@ func TestMakeKeyParamsSplit(t *testing.T) { } } +// TODO[1658]: func TestMakeKeyLastMarketID(t *testing.T) + func TestGetKeyPrefixMarket(t *testing.T) { tests := []struct { name string diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 9f4fd65d00..ae4a1764d9 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -6,6 +6,7 @@ import ( "strings" sdkmath "cosmossdk.io/math" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -609,6 +610,13 @@ func (k Keeper) GetAccessGrants(ctx sdk.Context, marketID uint32) []exchange.Acc return rv } +// SetAccessGrants deletes all access grants on a market and sets just the ones provided. +func (k Keeper) SetAccessGrants(ctx sdk.Context, marketID uint32, grants []exchange.AccessGrant) { + store := k.getStore(ctx) + revokeAllMarketPermissions(store, marketID) + setAllMarketPermissions(store, marketID, grants) +} + // UpdatePermissions revokes all permissions from the provided revokeAll bech32 addresses, then revokes all permissions // in the toRevoke list, and lastly, grants all the permissions in toGrant. func (k Keeper) UpdatePermissions(ctx sdk.Context, marketID uint32, revokeAll []string, toRevoke, toGrant []exchange.AccessGrant) { @@ -655,3 +663,78 @@ func (k Keeper) GetReqAttrBid(ctx sdk.Context, marketID uint32) []string { func (k Keeper) SetReqAttrBid(ctx sdk.Context, marketID uint32, reqAttrs []string) { setReqAttr(k.getStore(ctx), marketID, reqAttrs, MakeKeyMarketReqAttrBid) } + +// getLastAutoMarketID gets the last auto-selected market id. +func getLastAutoMarketID(store sdk.KVStore) uint32 { + key := MakeKeyLastMarketID() + value := store.Get(key) + return uint32FromBz(value) +} + +// setLastAutoMarketID sets the last auto-selected market id to the provided value. +func setLastAutoMarketID(store sdk.KVStore, marketID uint32) { + key := MakeKeyLastMarketID() + value := uint32Bz(marketID) + store.Set(key, value) +} + +// NextMarketID finds the next available market id, updates the last auto-selected +// market id store entry, and returns the unused id it found. +func (k Keeper) NextMarketID(ctx sdk.Context) uint32 { + store := k.getStore(ctx) + marketID := getLastAutoMarketID(store) + 1 + for { + marketAddr := exchange.GetMarketAddress(marketID) + if !k.accountKeeper.HasAccount(ctx, marketAddr) { + break + } + } + setLastAutoMarketID(store, marketID) + return marketID +} + +// CreateMarket saves a new market to the store with all the info provided. +// If the marketId is zero, the next available one will be used. +func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (marketID uint32, err error) { + defer func() { + if r := recover(); r != nil { + if e, ok := r.(error); ok { + err = fmt.Errorf("could not set market: %w", e) + } else { + err = fmt.Errorf("could not set market: %v", r) + } + } + }() + + if market.MarketId == 0 { + market.MarketId = k.NextMarketID(ctx) + } + marketID = market.MarketId + + marketAddr := exchange.GetMarketAddress(marketID) + if k.accountKeeper.HasAccount(ctx, marketAddr) { + return 0, fmt.Errorf("market id %d %s already exists", marketID, marketAddr) + } + + marketAcc := &exchange.MarketAccount{ + BaseAccount: &authtypes.BaseAccount{Address: marketAddr.String()}, + MarketId: marketID, + MarketDetails: market.MarketDetails, + } + k.accountKeeper.NewAccount(ctx, marketAcc) + k.accountKeeper.SetAccount(ctx, marketAcc) + + k.SetCreateAskFlatFees(ctx, marketID, market.FeeCreateAskFlat) + k.SetCreateBidFlatFees(ctx, marketID, market.FeeCreateBidFlat) + k.SetSellerSettlementFlatFees(ctx, marketID, market.FeeSellerSettlementFlat) + k.SetSellerSettlementRatios(ctx, marketID, market.FeeSellerSettlementRatios) + k.SetBuyerSettlementFlatFees(ctx, marketID, market.FeeBuyerSettlementFlat) + k.SetBuyerSettlementRatios(ctx, marketID, market.FeeBuyerSettlementRatios) + k.SetMarketActive(ctx, marketID, market.AcceptingOrders) + k.SetUserSettlementAllowed(ctx, marketID, market.AllowUserSettlement) + k.SetAccessGrants(ctx, marketID, market.AccessGrants) + k.SetReqAttrAsk(ctx, marketID, market.ReqAttrCreateAsk) + k.SetReqAttrBid(ctx, marketID, market.ReqAttrCreateBid) + + return marketID, nil +} From e2d848dfc145a3f4948b534a12cafeb8f1cf042e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 13:44:56 -0600 Subject: [PATCH 075/309] [1658]: Normalize the required attributes when storing them. --- x/exchange/expected_keepers.go | 4 ++++ x/exchange/keeper/keeper.go | 7 ++++++- x/exchange/keeper/market.go | 34 +++++++++++++++++++++++++--------- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/x/exchange/expected_keepers.go b/x/exchange/expected_keepers.go index af4ddf9e62..9ee0711eea 100644 --- a/x/exchange/expected_keepers.go +++ b/x/exchange/expected_keepers.go @@ -11,3 +11,7 @@ type AccountKeeper interface { HasAccount(ctx sdk.Context, addr sdk.AccAddress) bool NewAccount(ctx sdk.Context, acc authtypes.AccountI) authtypes.AccountI } + +type NameKeeper interface { + Normalize(ctx sdk.Context, name string) (string, error) +} diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 5cd999dd75..5cee72c1f4 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/provenance-io/provenance/x/exchange" ) @@ -15,17 +16,21 @@ type Keeper struct { storeKey storetypes.StoreKey accountKeeper exchange.AccountKeeper + nameKeeper exchange.NameKeeper // TODO[1658]: Finish the Keeper struct. authority string } -func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, accountKeeper exchange.AccountKeeper) Keeper { +func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, + accountKeeper exchange.AccountKeeper, nameKeeper exchange.NameKeeper, +) Keeper { // TODO[1658]: Finish NewKeeper. rv := Keeper{ cdc: cdc, storeKey: storeKey, accountKeeper: accountKeeper, + nameKeeper: nameKeeper, authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), } return rv diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index ae4a1764d9..16517965c5 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -634,14 +634,20 @@ func getReqAttr(store sdk.KVStore, marketID uint32, maker reqAttrKeyMaker) []str } // setReqAttr sets the required attributes for a market using the provided key maker. -func setReqAttr(store sdk.KVStore, marketID uint32, reqAttrs []string, maker reqAttrKeyMaker) { +func (k Keeper) setReqAttr(ctx sdk.Context, marketID uint32, reqAttrs []string, maker reqAttrKeyMaker) error { + store := k.getStore(ctx) key := maker(marketID) if len(reqAttrs) == 0 { store.Delete(key) } else { - value := []byte(strings.Join(reqAttrs, string(RecordSeparator))) + reqAttrsNorm, err := k.NormalizeReqAttrs(ctx, reqAttrs) + if err != nil { + return err + } + value := []byte(strings.Join(reqAttrsNorm, string(RecordSeparator))) store.Set(key, value) } + return nil } // GetReqAttrAsk gets the attributes required to create an ask order. @@ -650,8 +656,8 @@ func (k Keeper) GetReqAttrAsk(ctx sdk.Context, marketID uint32) []string { } // SetReqAttrAsk sets the attributes required to create an ask order. -func (k Keeper) SetReqAttrAsk(ctx sdk.Context, marketID uint32, reqAttrs []string) { - setReqAttr(k.getStore(ctx), marketID, reqAttrs, MakeKeyMarketReqAttrAsk) +func (k Keeper) SetReqAttrAsk(ctx sdk.Context, marketID uint32, reqAttrs []string) error { + return k.setReqAttr(ctx, marketID, reqAttrs, MakeKeyMarketReqAttrAsk) } // GetReqAttrBid gets the attributes required to create a bid order. @@ -660,8 +666,8 @@ func (k Keeper) GetReqAttrBid(ctx sdk.Context, marketID uint32) []string { } // SetReqAttrBid sets the attributes required to create a bid order. -func (k Keeper) SetReqAttrBid(ctx sdk.Context, marketID uint32, reqAttrs []string) { - setReqAttr(k.getStore(ctx), marketID, reqAttrs, MakeKeyMarketReqAttrBid) +func (k Keeper) SetReqAttrBid(ctx sdk.Context, marketID uint32, reqAttrs []string) error { + return k.setReqAttr(ctx, marketID, reqAttrs, MakeKeyMarketReqAttrBid) } // getLastAutoMarketID gets the last auto-selected market id. @@ -733,8 +739,18 @@ func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (marketID k.SetMarketActive(ctx, marketID, market.AcceptingOrders) k.SetUserSettlementAllowed(ctx, marketID, market.AllowUserSettlement) k.SetAccessGrants(ctx, marketID, market.AccessGrants) - k.SetReqAttrAsk(ctx, marketID, market.ReqAttrCreateAsk) - k.SetReqAttrBid(ctx, marketID, market.ReqAttrCreateBid) + err = errors.Join(err, k.SetReqAttrAsk(ctx, marketID, market.ReqAttrCreateAsk)) + err = errors.Join(err, k.SetReqAttrBid(ctx, marketID, market.ReqAttrCreateBid)) - return marketID, nil + return marketID, err +} + +// NormalizeReqAttrs normalizes/validates each of the provided require attributes. +func (k Keeper) NormalizeReqAttrs(ctx sdk.Context, reqAttrs []string) ([]string, error) { + rv := make([]string, len(reqAttrs)) + errs := make([]error, len(reqAttrs)) + for i, attr := range reqAttrs { + rv[i], errs[i] = k.nameKeeper.Normalize(ctx, attr) + } + return rv, errors.Join(errs...) } From 9f2a4a547f9d3360bdbbf327e14ecd047b2db6bb Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 13:53:57 -0600 Subject: [PATCH 076/309] [1658]: Fill out GovCreateMarket. --- x/exchange/keeper/msg_server.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 3c876747e3..7f2b480868 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -3,9 +3,8 @@ package keeper import ( "context" - cerrs "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/provenance-io/provenance/x/exchange" @@ -84,20 +83,27 @@ func (k MsgServer) MarketManageReqAttrs(goCtx context.Context, msg *exchange.Msg panic("not implemented") } +// wrongAuthErr returns the error to use when a message's authority isn't what's required. +func (k MsgServer) wrongAuthErr(authority string) error { + return govtypes.ErrInvalidSigner.Wrapf("expected %s got %s", k.GetAuthority(), authority) +} + func (k MsgServer) GovCreateMarket(goCtx context.Context, msg *exchange.MsgGovCreateMarketRequest) (*exchange.MsgGovCreateMarketResponse, error) { if msg.Authority != k.GetAuthority() { - return nil, cerrs.Wrapf(govtypes.ErrInvalidSigner, "expected %s got %s", k.GetAuthority(), msg.Authority) + return nil, k.wrongAuthErr(msg.Authority) } ctx := sdk.UnwrapSDKContext(goCtx) - // TODO[1658]: Implement GovCreateMarket - _ = ctx - panic("not implemented") + _, err := k.CreateMarket(ctx, msg.Market) + if err != nil { + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) + } + return &exchange.MsgGovCreateMarketResponse{}, nil } func (k MsgServer) GovManageFees(goCtx context.Context, msg *exchange.MsgGovManageFeesRequest) (*exchange.MsgGovManageFeesResponse, error) { if msg.Authority != k.GetAuthority() { - return nil, cerrs.Wrapf(govtypes.ErrInvalidSigner, "expected %s got %s", k.GetAuthority(), msg.Authority) + return nil, k.wrongAuthErr(msg.Authority) } ctx := sdk.UnwrapSDKContext(goCtx) @@ -109,7 +115,7 @@ func (k MsgServer) GovManageFees(goCtx context.Context, msg *exchange.MsgGovMana // GovUpdateParams is a governance proposal endpoint for updating the exchange module's params. func (k MsgServer) GovUpdateParams(goCtx context.Context, msg *exchange.MsgGovUpdateParamsRequest) (*exchange.MsgGovUpdateParamsResponse, error) { if msg.Authority != k.GetAuthority() { - return nil, cerrs.Wrapf(govtypes.ErrInvalidSigner, "expected %s got %s", k.GetAuthority(), msg.Authority) + return nil, k.wrongAuthErr(msg.Authority) } ctx := sdk.UnwrapSDKContext(goCtx) From b9c364cbb04855c4051b3beea07a23ed2aafaf62 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 13:57:00 -0600 Subject: [PATCH 077/309] [1658]: Add the endpoint comments to the msg server funcs. --- x/exchange/keeper/msg_server.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 7f2b480868..acf933a882 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -23,61 +23,73 @@ func NewMsgServer(k Keeper) exchange.MsgServer { var _ exchange.MsgServer = MsgServer{} +// CreateAsk creates an ask order (to sell something you own). func (k MsgServer) CreateAsk(goCtx context.Context, msg *exchange.MsgCreateAskRequest) (*exchange.MsgCreateAskResponse, error) { // TODO[1658]: Implement CreateAsk panic("not implemented") } +// CreateBid creates an bid order (to buy something you want). func (k MsgServer) CreateBid(goCtx context.Context, msg *exchange.MsgCreateBidRequest) (*exchange.MsgCreateBidResponse, error) { // TODO[1658]: Implement CreateBid panic("not implemented") } +// CancelOrder cancels an order. func (k MsgServer) CancelOrder(goCtx context.Context, msg *exchange.MsgCancelOrderRequest) (*exchange.MsgCancelOrderResponse, error) { // TODO[1658]: Implement CancelOrder panic("not implemented") } +// FillBids uses the assets in your account to fulfill one or more bids (similar to a fill-or-cancel ask). func (k MsgServer) FillBids(goCtx context.Context, msg *exchange.MsgFillBidsRequest) (*exchange.MsgFillBidsResponse, error) { // TODO[1658]: Implement FillBids panic("not implemented") } +// FillAsks uses the funds in your account to fulfill one or more asks (similar to a fill-or-cancel bid). func (k MsgServer) FillAsks(goCtx context.Context, msg *exchange.MsgFillAsksRequest) (*exchange.MsgFillAsksResponse, error) { // TODO[1658]: Implement FillAsks panic("not implemented") } +// MarketSettle is a market endpoint to trigger the settlement of orders. func (k MsgServer) MarketSettle(goCtx context.Context, msg *exchange.MsgMarketSettleRequest) (*exchange.MsgMarketSettleResponse, error) { // TODO[1658]: Implement MarketSettle panic("not implemented") } +// MarketWithdraw is a market endpoint to withdraw fees that have been collected. func (k MsgServer) MarketWithdraw(goCtx context.Context, msg *exchange.MsgMarketWithdrawRequest) (*exchange.MsgMarketWithdrawResponse, error) { // TODO[1658]: Implement MarketWithdraw panic("not implemented") } +// MarketUpdateDetails is a market endpoint to update its details. func (k MsgServer) MarketUpdateDetails(goCtx context.Context, msg *exchange.MsgMarketUpdateDetailsRequest) (*exchange.MsgMarketUpdateDetailsResponse, error) { // TODO[1658]: Implement MarketUpdateDetails panic("not implemented") } +// MarketUpdateEnabled is a market endpoint to update whether its accepting orders. func (k MsgServer) MarketUpdateEnabled(goCtx context.Context, msg *exchange.MsgMarketUpdateEnabledRequest) (*exchange.MsgMarketUpdateEnabledResponse, error) { // TODO[1658]: Implement MarketUpdateEnabled panic("not implemented") } +// MarketUpdateUserSettle is a market endpoint to update whether it allows user-initiated settlement. func (k MsgServer) MarketUpdateUserSettle(goCtx context.Context, msg *exchange.MsgMarketUpdateUserSettleRequest) (*exchange.MsgMarketUpdateUserSettleResponse, error) { // TODO[1658]: Implement MarketUpdateUserSettle panic("not implemented") } +// MarketManagePermissions is a market endpoint to manage a market's user permissions. func (k MsgServer) MarketManagePermissions(goCtx context.Context, msg *exchange.MsgMarketManagePermissionsRequest) (*exchange.MsgMarketManagePermissionsResponse, error) { // TODO[1658]: Implement MarketManagePermissions panic("not implemented") } +// MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. func (k MsgServer) MarketManageReqAttrs(goCtx context.Context, msg *exchange.MsgMarketManageReqAttrsRequest) (*exchange.MsgMarketManageReqAttrsResponse, error) { // TODO[1658]: Implement MarketManageReqAttrs panic("not implemented") @@ -88,6 +100,7 @@ func (k MsgServer) wrongAuthErr(authority string) error { return govtypes.ErrInvalidSigner.Wrapf("expected %s got %s", k.GetAuthority(), authority) } +// GovCreateMarket is a governance proposal endpoint for creating a market. func (k MsgServer) GovCreateMarket(goCtx context.Context, msg *exchange.MsgGovCreateMarketRequest) (*exchange.MsgGovCreateMarketResponse, error) { if msg.Authority != k.GetAuthority() { return nil, k.wrongAuthErr(msg.Authority) @@ -101,6 +114,7 @@ func (k MsgServer) GovCreateMarket(goCtx context.Context, msg *exchange.MsgGovCr return &exchange.MsgGovCreateMarketResponse{}, nil } +// GovManageFees is a governance proposal endpoint for updating a market's fees. func (k MsgServer) GovManageFees(goCtx context.Context, msg *exchange.MsgGovManageFeesRequest) (*exchange.MsgGovManageFeesResponse, error) { if msg.Authority != k.GetAuthority() { return nil, k.wrongAuthErr(msg.Authority) From 1d274e0fd4c4ee218e1a9916765e27c52bfe0f1d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 13:59:40 -0600 Subject: [PATCH 078/309] [1658]: Add a market id field to the update fees message and a last market id field to genesis. --- docs/proto-docs.md | 2 + proto/provenance/exchange/v1/genesis.proto | 3 + proto/provenance/exchange/v1/tx.proto | 27 ++- x/exchange/genesis.pb.go | 48 +++- x/exchange/tx.pb.go | 249 ++++++++++++--------- 5 files changed, 202 insertions(+), 127 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 442d2cf2af..3e3b276878 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1836,6 +1836,7 @@ GenesisState is the data that should be loaded into the exchange module during g | `params` | [Params](#provenance.exchange.v1.Params) | | params defines all the parameters of the exchange module. | | `markets` | [Market](#provenance.exchange.v1.Market) | repeated | markets are all of the markets to create at genesis. | | `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are all the orders to create at genesis. | +| `last_market_id` | [uint32](#uint32) | | last_market_id is the value of the last auto-selected market id. | @@ -2222,6 +2223,7 @@ MsgGovManageFeesRequest is a request message for the GovManageFees endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `authority` | [string](#string) | | authority should be the governance module account address. | +| `market_id` | [uint32](#uint32) | | market_id is the market id that will get these fee updates. | | `add_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_ask_flat are the create-ask flat fee options to add. | | `remove_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_ask_flat are the create-ask flat fee options to remove. | | `add_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_bid_flat are the create-bid flat fee options to add. | diff --git a/proto/provenance/exchange/v1/genesis.proto b/proto/provenance/exchange/v1/genesis.proto index 6116c5a1b0..aca884d657 100644 --- a/proto/provenance/exchange/v1/genesis.proto +++ b/proto/provenance/exchange/v1/genesis.proto @@ -24,4 +24,7 @@ message GenesisState { // orders are all the orders to create at genesis. repeated Order orders = 3 [(gogoproto.nullable) = false]; + + // last_market_id is the value of the last auto-selected market id. + uint32 last_market_id = 4; } \ No newline at end of file diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 0b22d69bdd..ac8768416e 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -180,41 +180,44 @@ message MsgGovManageFeesRequest { // authority should be the governance module account address. string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market_id is the market id that will get these fee updates. + uint32 market_id = 2; + // add_fee_create_ask_flat are the create-ask flat fee options to add. - repeated cosmos.base.v1beta1.Coin add_fee_create_ask_flat = 2 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin add_fee_create_ask_flat = 3 [(gogoproto.nullable) = false]; // remove_fee_create_ask_flat are the create-ask flat fee options to remove. - repeated cosmos.base.v1beta1.Coin remove_fee_create_ask_flat = 3 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin remove_fee_create_ask_flat = 4 [(gogoproto.nullable) = false]; // add_fee_create_bid_flat are the create-bid flat fee options to add. - repeated cosmos.base.v1beta1.Coin add_fee_create_bid_flat = 4 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin add_fee_create_bid_flat = 5 [(gogoproto.nullable) = false]; // remove_fee_create_bid_flat are the create-bid flat fee options to remove. - repeated cosmos.base.v1beta1.Coin remove_fee_create_bid_flat = 5 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin remove_fee_create_bid_flat = 6 [(gogoproto.nullable) = false]; // add_fee_seller_settlement_flat are the seller settlement flat fee options to add. - repeated cosmos.base.v1beta1.Coin add_fee_seller_settlement_flat = 6 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin add_fee_seller_settlement_flat = 7 [(gogoproto.nullable) = false]; // remove_fee_seller_settlement_flat are the seller settlement flat fee options to remove. - repeated cosmos.base.v1beta1.Coin remove_fee_seller_settlement_flat = 7 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin remove_fee_seller_settlement_flat = 8 [(gogoproto.nullable) = false]; // add_fee_seller_settlement_ratios are the seller settlement fee ratios to add. - repeated FeeRatio add_fee_seller_settlement_ratios = 8 [(gogoproto.nullable) = false]; + repeated FeeRatio add_fee_seller_settlement_ratios = 9 [(gogoproto.nullable) = false]; // remove_fee_seller_settlement_ratios are the seller settlement fee ratios to remove. - repeated FeeRatio remove_fee_seller_settlement_ratios = 9 [(gogoproto.nullable) = false]; + repeated FeeRatio remove_fee_seller_settlement_ratios = 10 [(gogoproto.nullable) = false]; // add_fee_buyer_settlement_flat are the buyer settlement flat fee options to add. - repeated cosmos.base.v1beta1.Coin add_fee_buyer_settlement_flat = 10 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin add_fee_buyer_settlement_flat = 11 [(gogoproto.nullable) = false]; // remove_fee_buyer_settlement_flat are the buyer settlement flat fee options to remove. - repeated cosmos.base.v1beta1.Coin remove_fee_buyer_settlement_flat = 11 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin remove_fee_buyer_settlement_flat = 12 [(gogoproto.nullable) = false]; // add_fee_buyer_settlement_ratios are the buyer settlement fee ratios to add. - repeated FeeRatio add_fee_buyer_settlement_ratios = 12 [(gogoproto.nullable) = false]; + repeated FeeRatio add_fee_buyer_settlement_ratios = 13 [(gogoproto.nullable) = false]; // remove_fee_buyer_settlement_ratios are the buyer settlement fee ratios to remove. - repeated FeeRatio remove_fee_buyer_settlement_ratios = 13 [(gogoproto.nullable) = false]; + repeated FeeRatio remove_fee_buyer_settlement_ratios = 14 [(gogoproto.nullable) = false]; } // MsgGovManageFeesResponse is a response message for the GovManageFees endpoint. diff --git a/x/exchange/genesis.pb.go b/x/exchange/genesis.pb.go index 8ecb92f53a..8417f9d9e6 100644 --- a/x/exchange/genesis.pb.go +++ b/x/exchange/genesis.pb.go @@ -31,6 +31,8 @@ type GenesisState struct { Markets []Market `protobuf:"bytes,2,rep,name=markets,proto3" json:"markets"` // orders are all the orders to create at genesis. Orders []Order `protobuf:"bytes,3,rep,name=orders,proto3" json:"orders"` + // last_market_id is the value of the last auto-selected market id. + LastMarketId uint32 `protobuf:"varint,4,opt,name=last_market_id,json=lastMarketId,proto3" json:"last_market_id,omitempty"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -75,25 +77,26 @@ func init() { } var fileDescriptor_087ceebafabf03c9 = []byte{ - // 273 bytes of a gzipped FileDescriptorProto + // 301 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x29, 0x28, 0xca, 0x2f, 0x4b, 0xcd, 0x4b, 0xcc, 0x4b, 0x4e, 0xd5, 0x4f, 0xad, 0x48, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0xd5, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x43, 0xa8, 0xd2, 0x83, 0xa9, 0xd2, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x2b, 0xd1, 0x07, 0xb1, 0x20, 0xaa, 0xa5, 0x94, 0x71, 0x98, 0x99, 0x9b, 0x58, 0x94, 0x9d, 0x5a, 0x42, 0x40, 0x51, 0x7e, 0x51, 0x4a, 0x6a, 0x51, 0x31, 0x01, 0x45, 0x05, 0x89, 0x45, 0x89, - 0xb9, 0x50, 0x45, 0x4a, 0x67, 0x19, 0xb9, 0x78, 0xdc, 0x21, 0xce, 0x0d, 0x2e, 0x49, 0x2c, 0x49, + 0xb9, 0x50, 0x45, 0x4a, 0x9f, 0x19, 0xb9, 0x78, 0xdc, 0x21, 0xce, 0x0d, 0x2e, 0x49, 0x2c, 0x49, 0x15, 0x32, 0xe3, 0x62, 0x83, 0x28, 0x90, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd3, 0xc3, 0xee, 0x7c, 0xbd, 0x00, 0xb0, 0xaa, 0x20, 0xa8, 0x6a, 0x21, 0x3b, 0x2e, 0x76, 0x88, 0x13, 0x8b, 0x25, 0x98, 0x14, 0x98, 0xf1, 0x69, 0xf4, 0x05, 0x2b, 0x73, 0x62, 0x39, 0x71, 0x4f, 0x9e, 0x21, 0x08, 0xa6, 0x49, 0xc8, 0x9a, 0x8b, 0x0d, 0xe2, 0x7a, 0x09, 0x66, 0xb0, 0x76, 0x59, 0x5c, 0xda, - 0xfd, 0x41, 0xaa, 0xa0, 0xba, 0xa1, 0x5a, 0xac, 0x38, 0x3a, 0x16, 0xc8, 0x33, 0xbc, 0x58, 0x20, - 0xcf, 0xe0, 0x94, 0x7a, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, - 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x5c, 0x92, 0x99, - 0xf9, 0x38, 0x8c, 0x0c, 0x60, 0x8c, 0xd2, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, - 0xcf, 0xd5, 0x47, 0x28, 0xd2, 0xcd, 0xcc, 0x47, 0xe2, 0xe9, 0x57, 0xc0, 0x83, 0x31, 0x89, 0x0d, - 0x1c, 0x7a, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x98, 0x3a, 0x7e, 0x02, 0x02, 0x00, - 0x00, + 0xfd, 0x41, 0xaa, 0xa0, 0xba, 0xa1, 0x5a, 0x84, 0x54, 0xb8, 0xf8, 0x72, 0x12, 0x8b, 0x4b, 0xe2, + 0x21, 0x86, 0xc5, 0x67, 0xa6, 0x48, 0xb0, 0x28, 0x30, 0x6a, 0xf0, 0x06, 0xf1, 0x80, 0x44, 0x21, + 0xf6, 0x79, 0xa6, 0x58, 0x71, 0x74, 0x2c, 0x90, 0x67, 0x78, 0xb1, 0x40, 0x9e, 0xc1, 0x29, 0xf5, + 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, + 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xb8, 0x24, 0x33, 0xf3, 0x71, 0x58, 0x1c, + 0xc0, 0x18, 0xa5, 0x97, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x8f, 0x50, + 0xa4, 0x9b, 0x99, 0x8f, 0xc4, 0xd3, 0xaf, 0x80, 0x07, 0x76, 0x12, 0x1b, 0x38, 0x8c, 0x8d, 0x01, + 0x01, 0x00, 0x00, 0xff, 0xff, 0x74, 0x34, 0x75, 0x11, 0x28, 0x02, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -116,6 +119,11 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.LastMarketId != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.LastMarketId)) + i-- + dAtA[i] = 0x20 + } if len(m.Orders) > 0 { for iNdEx := len(m.Orders) - 1; iNdEx >= 0; iNdEx-- { { @@ -192,6 +200,9 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) } } + if m.LastMarketId != 0 { + n += 1 + sovGenesis(uint64(m.LastMarketId)) + } return n } @@ -334,6 +345,25 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LastMarketId", wireType) + } + m.LastMarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LastMarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 59a50186eb..18a103328a 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -1017,30 +1017,32 @@ var xxx_messageInfo_MsgGovCreateMarketResponse proto.InternalMessageInfo type MsgGovManageFeesRequest struct { // authority should be the governance module account address. Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // market_id is the market id that will get these fee updates. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // add_fee_create_ask_flat are the create-ask flat fee options to add. - AddFeeCreateAskFlat []types.Coin `protobuf:"bytes,2,rep,name=add_fee_create_ask_flat,json=addFeeCreateAskFlat,proto3" json:"add_fee_create_ask_flat"` + AddFeeCreateAskFlat []types.Coin `protobuf:"bytes,3,rep,name=add_fee_create_ask_flat,json=addFeeCreateAskFlat,proto3" json:"add_fee_create_ask_flat"` // remove_fee_create_ask_flat are the create-ask flat fee options to remove. - RemoveFeeCreateAskFlat []types.Coin `protobuf:"bytes,3,rep,name=remove_fee_create_ask_flat,json=removeFeeCreateAskFlat,proto3" json:"remove_fee_create_ask_flat"` + RemoveFeeCreateAskFlat []types.Coin `protobuf:"bytes,4,rep,name=remove_fee_create_ask_flat,json=removeFeeCreateAskFlat,proto3" json:"remove_fee_create_ask_flat"` // add_fee_create_bid_flat are the create-bid flat fee options to add. - AddFeeCreateBidFlat []types.Coin `protobuf:"bytes,4,rep,name=add_fee_create_bid_flat,json=addFeeCreateBidFlat,proto3" json:"add_fee_create_bid_flat"` + AddFeeCreateBidFlat []types.Coin `protobuf:"bytes,5,rep,name=add_fee_create_bid_flat,json=addFeeCreateBidFlat,proto3" json:"add_fee_create_bid_flat"` // remove_fee_create_bid_flat are the create-bid flat fee options to remove. - RemoveFeeCreateBidFlat []types.Coin `protobuf:"bytes,5,rep,name=remove_fee_create_bid_flat,json=removeFeeCreateBidFlat,proto3" json:"remove_fee_create_bid_flat"` + RemoveFeeCreateBidFlat []types.Coin `protobuf:"bytes,6,rep,name=remove_fee_create_bid_flat,json=removeFeeCreateBidFlat,proto3" json:"remove_fee_create_bid_flat"` // add_fee_seller_settlement_flat are the seller settlement flat fee options to add. - AddFeeSellerSettlementFlat []types.Coin `protobuf:"bytes,6,rep,name=add_fee_seller_settlement_flat,json=addFeeSellerSettlementFlat,proto3" json:"add_fee_seller_settlement_flat"` + AddFeeSellerSettlementFlat []types.Coin `protobuf:"bytes,7,rep,name=add_fee_seller_settlement_flat,json=addFeeSellerSettlementFlat,proto3" json:"add_fee_seller_settlement_flat"` // remove_fee_seller_settlement_flat are the seller settlement flat fee options to remove. - RemoveFeeSellerSettlementFlat []types.Coin `protobuf:"bytes,7,rep,name=remove_fee_seller_settlement_flat,json=removeFeeSellerSettlementFlat,proto3" json:"remove_fee_seller_settlement_flat"` + RemoveFeeSellerSettlementFlat []types.Coin `protobuf:"bytes,8,rep,name=remove_fee_seller_settlement_flat,json=removeFeeSellerSettlementFlat,proto3" json:"remove_fee_seller_settlement_flat"` // add_fee_seller_settlement_ratios are the seller settlement fee ratios to add. - AddFeeSellerSettlementRatios []FeeRatio `protobuf:"bytes,8,rep,name=add_fee_seller_settlement_ratios,json=addFeeSellerSettlementRatios,proto3" json:"add_fee_seller_settlement_ratios"` + AddFeeSellerSettlementRatios []FeeRatio `protobuf:"bytes,9,rep,name=add_fee_seller_settlement_ratios,json=addFeeSellerSettlementRatios,proto3" json:"add_fee_seller_settlement_ratios"` // remove_fee_seller_settlement_ratios are the seller settlement fee ratios to remove. - RemoveFeeSellerSettlementRatios []FeeRatio `protobuf:"bytes,9,rep,name=remove_fee_seller_settlement_ratios,json=removeFeeSellerSettlementRatios,proto3" json:"remove_fee_seller_settlement_ratios"` + RemoveFeeSellerSettlementRatios []FeeRatio `protobuf:"bytes,10,rep,name=remove_fee_seller_settlement_ratios,json=removeFeeSellerSettlementRatios,proto3" json:"remove_fee_seller_settlement_ratios"` // add_fee_buyer_settlement_flat are the buyer settlement flat fee options to add. - AddFeeBuyerSettlementFlat []types.Coin `protobuf:"bytes,10,rep,name=add_fee_buyer_settlement_flat,json=addFeeBuyerSettlementFlat,proto3" json:"add_fee_buyer_settlement_flat"` + AddFeeBuyerSettlementFlat []types.Coin `protobuf:"bytes,11,rep,name=add_fee_buyer_settlement_flat,json=addFeeBuyerSettlementFlat,proto3" json:"add_fee_buyer_settlement_flat"` // remove_fee_buyer_settlement_flat are the buyer settlement flat fee options to remove. - RemoveFeeBuyerSettlementFlat []types.Coin `protobuf:"bytes,11,rep,name=remove_fee_buyer_settlement_flat,json=removeFeeBuyerSettlementFlat,proto3" json:"remove_fee_buyer_settlement_flat"` + RemoveFeeBuyerSettlementFlat []types.Coin `protobuf:"bytes,12,rep,name=remove_fee_buyer_settlement_flat,json=removeFeeBuyerSettlementFlat,proto3" json:"remove_fee_buyer_settlement_flat"` // add_fee_buyer_settlement_ratios are the buyer settlement fee ratios to add. - AddFeeBuyerSettlementRatios []FeeRatio `protobuf:"bytes,12,rep,name=add_fee_buyer_settlement_ratios,json=addFeeBuyerSettlementRatios,proto3" json:"add_fee_buyer_settlement_ratios"` + AddFeeBuyerSettlementRatios []FeeRatio `protobuf:"bytes,13,rep,name=add_fee_buyer_settlement_ratios,json=addFeeBuyerSettlementRatios,proto3" json:"add_fee_buyer_settlement_ratios"` // remove_fee_buyer_settlement_ratios are the buyer settlement fee ratios to remove. - RemoveFeeBuyerSettlementRatios []FeeRatio `protobuf:"bytes,13,rep,name=remove_fee_buyer_settlement_ratios,json=removeFeeBuyerSettlementRatios,proto3" json:"remove_fee_buyer_settlement_ratios"` + RemoveFeeBuyerSettlementRatios []FeeRatio `protobuf:"bytes,14,rep,name=remove_fee_buyer_settlement_ratios,json=removeFeeBuyerSettlementRatios,proto3" json:"remove_fee_buyer_settlement_ratios"` } func (m *MsgGovManageFeesRequest) Reset() { *m = MsgGovManageFeesRequest{} } @@ -1083,6 +1085,13 @@ func (m *MsgGovManageFeesRequest) GetAuthority() string { return "" } +func (m *MsgGovManageFeesRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + func (m *MsgGovManageFeesRequest) GetAddFeeCreateAskFlat() []types.Coin { if m != nil { return m.AddFeeCreateAskFlat @@ -1332,77 +1341,78 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1106 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0x41, 0x6f, 0xe3, 0x44, - 0x18, 0xad, 0xd9, 0xa5, 0x6c, 0xbf, 0xee, 0x16, 0x69, 0xda, 0x6d, 0x12, 0x6f, 0xeb, 0xa4, 0x29, - 0x87, 0x6a, 0x97, 0xda, 0xdb, 0x22, 0x0a, 0xac, 0xb8, 0x34, 0x0b, 0xd9, 0x53, 0x44, 0x95, 0xaa, - 0x42, 0x82, 0x43, 0x34, 0x89, 0xa7, 0xae, 0x55, 0xc7, 0x93, 0xf5, 0x4c, 0x42, 0xf7, 0x8a, 0x90, - 0x38, 0x21, 0xed, 0x91, 0x2b, 0x3f, 0x00, 0x89, 0x03, 0x3f, 0x62, 0x8f, 0x2b, 0x4e, 0x9c, 0x10, - 0x6a, 0x0f, 0xfc, 0x0d, 0x64, 0xcf, 0xd8, 0x71, 0xe2, 0x71, 0xe2, 0x14, 0x6e, 0xf5, 0xcc, 0xfb, - 0xde, 0xfb, 0xde, 0xa8, 0xe3, 0xef, 0x39, 0x50, 0x1d, 0x04, 0x74, 0x44, 0x7c, 0xec, 0xf7, 0x88, - 0x45, 0xae, 0x7a, 0x17, 0xd8, 0x77, 0x88, 0x35, 0x3a, 0xb0, 0xf8, 0x95, 0x39, 0x08, 0x28, 0xa7, - 0x68, 0x73, 0x0c, 0x30, 0x63, 0x80, 0x39, 0x3a, 0xd0, 0x8d, 0x1e, 0x65, 0x7d, 0xca, 0xac, 0x2e, - 0x66, 0x61, 0x41, 0x97, 0x70, 0x7c, 0x60, 0xf5, 0xa8, 0xeb, 0x8b, 0x3a, 0xbd, 0x24, 0xf7, 0xfb, - 0xcc, 0x09, 0xf9, 0xfa, 0xcc, 0x91, 0x1b, 0x15, 0xb1, 0xd1, 0x89, 0x9e, 0x2c, 0xf1, 0x20, 0xb7, - 0x36, 0x1c, 0xea, 0x50, 0xb1, 0x1e, 0xfe, 0x25, 0x57, 0x77, 0x73, 0x5a, 0xec, 0xe3, 0xe0, 0x92, - 0xf0, 0x39, 0xa0, 0x01, 0x0e, 0x70, 0x5f, 0xf2, 0xd7, 0x1f, 0xc2, 0x7a, 0x8b, 0x39, 0xcf, 0x03, - 0x82, 0x39, 0x39, 0x66, 0x97, 0x6d, 0xf2, 0x72, 0x48, 0x18, 0xaf, 0x6f, 0xc2, 0xc6, 0xe4, 0x32, - 0x1b, 0x50, 0x9f, 0x91, 0x09, 0x78, 0xc3, 0xb5, 0x55, 0xf0, 0x68, 0x59, 0xc2, 0x4b, 0xf0, 0x30, - 0x5c, 0x0f, 0x5b, 0xf0, 0xbe, 0x0a, 0x6c, 0x12, 0xc4, 0x05, 0x65, 0xd8, 0x9c, 0xde, 0x90, 0x25, - 0x1b, 0x80, 0x5a, 0xcc, 0x69, 0xba, 0x9e, 0xd7, 0x70, 0x6d, 0x16, 0xe3, 0x85, 0xee, 0x78, 0x35, - 0x03, 0x3e, 0x66, 0x97, 0x0a, 0xb0, 0x58, 0x95, 0x60, 0xa1, 0xd9, 0x8a, 0x8e, 0xe8, 0x94, 0x70, - 0xee, 0x91, 0xb8, 0xa0, 0x02, 0xa5, 0xcc, 0x8e, 0x2c, 0xd2, 0xa1, 0x9c, 0x6c, 0x7d, 0xed, 0xf2, - 0x0b, 0x3b, 0xc0, 0xdf, 0xc5, 0x65, 0x8f, 0xa0, 0xa2, 0xd8, 0x93, 0x85, 0x55, 0xd8, 0x4e, 0x36, - 0xcf, 0x06, 0x36, 0xe6, 0xe4, 0x0b, 0xc2, 0xb1, 0xeb, 0x25, 0x5d, 0xd6, 0xc0, 0xc8, 0x03, 0xe4, - 0x52, 0x7c, 0xe9, 0xe3, 0xae, 0x47, 0xec, 0x7c, 0x8a, 0x04, 0x20, 0x29, 0xea, 0x50, 0x9b, 0x42, - 0x9c, 0x31, 0x12, 0x4c, 0xba, 0xdf, 0x85, 0x9d, 0x19, 0x18, 0x49, 0x94, 0x06, 0xb5, 0xb0, 0x8f, - 0x1d, 0x72, 0x42, 0x82, 0xbe, 0xcb, 0x98, 0x4b, 0xfd, 0xc4, 0xd2, 0x07, 0x50, 0x9f, 0x05, 0x92, - 0x54, 0xe9, 0xae, 0x05, 0xaa, 0x4d, 0x5e, 0x1e, 0x73, 0x1e, 0x24, 0x3c, 0x3b, 0x50, 0xcd, 0x45, - 0x48, 0x92, 0x5f, 0xb4, 0xe8, 0xf0, 0x5f, 0xd0, 0x91, 0xf8, 0xaf, 0x13, 0x60, 0x49, 0x80, 0x8e, - 0x60, 0x05, 0x0f, 0xf9, 0x05, 0x0d, 0x5c, 0xfe, 0xaa, 0xac, 0xd5, 0xb4, 0xbd, 0x95, 0x46, 0xf9, - 0x8f, 0xdf, 0xf7, 0x37, 0xe4, 0xd5, 0x3a, 0xb6, 0xed, 0x80, 0x30, 0x76, 0xca, 0x03, 0xd7, 0x77, - 0xda, 0x63, 0x28, 0xfa, 0x1c, 0x96, 0xc5, 0x15, 0x2a, 0xbf, 0x53, 0xd3, 0xf6, 0x56, 0x0f, 0x0d, - 0x53, 0x7d, 0xd5, 0x4d, 0x21, 0xd7, 0xb8, 0xfb, 0xe6, 0xaf, 0xea, 0x52, 0x5b, 0xd6, 0x3c, 0x5b, - 0xfb, 0xfe, 0x9f, 0xdf, 0x1e, 0x8f, 0xd9, 0xea, 0x5b, 0xa0, 0xab, 0x5a, 0x94, 0x0e, 0x7e, 0x85, - 0xe8, 0xbf, 0xee, 0x05, 0x1d, 0x09, 0x8b, 0x4d, 0x42, 0xd8, 0x7f, 0xed, 0xff, 0x0c, 0x4a, 0xd8, - 0xb6, 0x3b, 0xe7, 0x84, 0x74, 0x7a, 0x91, 0x66, 0x07, 0xb3, 0xcb, 0xce, 0xb9, 0x87, 0x43, 0x43, - 0x77, 0xf6, 0x56, 0x0f, 0x2b, 0xa6, 0xa4, 0x08, 0xdf, 0x51, 0xa6, 0x7c, 0x47, 0x99, 0xcf, 0xa9, - 0xeb, 0x4b, 0x2f, 0xeb, 0xd8, 0xb6, 0x9b, 0x84, 0x24, 0x37, 0xbf, 0xe9, 0x61, 0x8e, 0xbe, 0x05, - 0x3d, 0x20, 0x7d, 0x3a, 0x22, 0x4a, 0xe6, 0x3b, 0xc5, 0x98, 0x37, 0x05, 0x45, 0x86, 0x3c, 0xdb, - 0x73, 0xd7, 0xb5, 0x05, 0xf3, 0xdd, 0x5b, 0xf4, 0xdc, 0x70, 0xed, 0xfc, 0x9e, 0x13, 0xe6, 0x77, - 0x6f, 0xd7, 0x73, 0x4c, 0xde, 0x03, 0x23, 0xee, 0x99, 0x11, 0xcf, 0x23, 0x41, 0x87, 0x45, 0xd7, - 0xa5, 0x4f, 0x7c, 0x2e, 0x04, 0x96, 0x8b, 0x09, 0xe8, 0xa2, 0xf5, 0xd3, 0x88, 0xe4, 0x34, 0xe1, - 0x88, 0x44, 0x5c, 0xd8, 0x49, 0x39, 0xc8, 0xd1, 0x79, 0xaf, 0x98, 0xce, 0x76, 0x62, 0x44, 0x29, - 0xe5, 0x43, 0x2d, 0xdf, 0x4f, 0x80, 0xb9, 0x4b, 0x59, 0xf9, 0x5e, 0xa4, 0x54, 0xcb, 0xbb, 0x11, - 0x4d, 0x42, 0xda, 0x21, 0x50, 0x0a, 0x6e, 0xa9, 0x8d, 0x45, 0x10, 0x86, 0x38, 0xec, 0xce, 0xb4, - 0x26, 0x25, 0x57, 0x16, 0x92, 0xac, 0xe6, 0x7a, 0x94, 0xaa, 0x18, 0xb6, 0x63, 0x97, 0xdd, 0xe1, - 0x2b, 0xc5, 0x61, 0x42, 0xb1, 0xc3, 0xac, 0x08, 0x6f, 0x8d, 0x90, 0x63, 0xea, 0x20, 0x1d, 0xa8, - 0xa5, 0x8c, 0xa9, 0x55, 0x56, 0x8b, 0xa9, 0x6c, 0x25, 0x76, 0x54, 0x42, 0x1e, 0x54, 0x73, 0xbd, - 0xc8, 0xd3, 0xbb, 0xbf, 0xd0, 0xe9, 0x3d, 0x52, 0x9a, 0x92, 0x27, 0x17, 0x40, 0x7d, 0x96, 0x2d, - 0x29, 0xf8, 0x60, 0x21, 0x41, 0x23, 0xcf, 0x9f, 0xd0, 0xcc, 0xbc, 0x4d, 0xc5, 0x24, 0x9e, 0x7a, - 0x5d, 0x66, 0xa6, 0x81, 0x18, 0x60, 0x27, 0x51, 0xc4, 0xf9, 0x1f, 0xa6, 0x81, 0xc8, 0x4a, 0xf3, - 0xa6, 0x81, 0x90, 0x8b, 0xa7, 0x81, 0xa8, 0xc9, 0x9f, 0x06, 0x93, 0x2d, 0x0a, 0x07, 0x87, 0x3f, - 0xaf, 0xc1, 0x9d, 0x16, 0x73, 0xd0, 0x39, 0xac, 0x24, 0xaf, 0x47, 0xf4, 0x24, 0x77, 0xfc, 0x64, - 0x23, 0x9b, 0xfe, 0x61, 0x31, 0xb0, 0xd0, 0x1b, 0xeb, 0x34, 0x5c, 0xbb, 0x80, 0xce, 0x38, 0xeb, - 0x15, 0xd0, 0x49, 0x25, 0x40, 0xe4, 0xc1, 0x6a, 0x2a, 0xe5, 0xa1, 0xfd, 0x59, 0xc5, 0x99, 0x98, - 0xa8, 0x9b, 0x45, 0xe1, 0x52, 0xad, 0x07, 0xf7, 0xe2, 0x8c, 0x88, 0x1e, 0xcf, 0xa8, 0x9d, 0x8a, - 0x97, 0xfa, 0x93, 0x42, 0xd8, 0x49, 0x91, 0x30, 0x5b, 0xce, 0x15, 0x49, 0xc5, 0xd2, 0xb9, 0x22, - 0xe9, 0xb0, 0x8a, 0x28, 0xdc, 0x4f, 0xe7, 0x51, 0x34, 0xeb, 0x24, 0x14, 0x91, 0x56, 0xb7, 0x0a, - 0xe3, 0xa5, 0xe0, 0x10, 0xd6, 0x26, 0x93, 0x2c, 0x7a, 0x3a, 0x97, 0x62, 0x2a, 0x10, 0xeb, 0x07, - 0x0b, 0x54, 0x48, 0xd9, 0x1f, 0x34, 0x58, 0x57, 0x64, 0x60, 0xf4, 0xf1, 0x5c, 0x2a, 0x55, 0xa8, - 0xd6, 0x8f, 0x16, 0x2d, 0xcb, 0x69, 0x43, 0xe6, 0xe8, 0xc2, 0x6d, 0x4c, 0x06, 0xf3, 0xc2, 0x6d, - 0x4c, 0xc5, 0x75, 0xf4, 0x93, 0x06, 0x9b, 0xea, 0x20, 0x8e, 0x3e, 0x2d, 0x48, 0x99, 0xc9, 0xf7, - 0xfa, 0x67, 0xb7, 0xa8, 0x94, 0xfd, 0xbc, 0xd6, 0xa0, 0x94, 0x13, 0xe7, 0xd1, 0x7c, 0xda, 0xbc, - 0xef, 0x04, 0xfd, 0xd9, 0x6d, 0x4a, 0x65, 0x4b, 0x3f, 0x6a, 0xb0, 0xa1, 0xfa, 0x32, 0x40, 0x47, - 0x05, 0x49, 0xa7, 0x3e, 0x36, 0xf4, 0x4f, 0x16, 0xae, 0x93, 0x9d, 0x5c, 0xc1, 0xfb, 0x53, 0xd9, - 0x1e, 0xcd, 0xba, 0x00, 0xea, 0x4f, 0x15, 0xfd, 0x70, 0x91, 0x12, 0xa9, 0x1c, 0xc0, 0x83, 0x89, - 0x39, 0x88, 0xac, 0xd9, 0x24, 0x99, 0x0f, 0x0c, 0xfd, 0x69, 0xf1, 0x82, 0x09, 0xb7, 0xe9, 0xd9, - 0x35, 0xcf, 0xad, 0x62, 0x14, 0xcf, 0x73, 0xab, 0x1a, 0x8d, 0x0d, 0xf2, 0xe6, 0xda, 0xd0, 0xde, - 0x5e, 0x1b, 0xda, 0xdf, 0xd7, 0x86, 0xf6, 0xfa, 0xc6, 0x58, 0x7a, 0x7b, 0x63, 0x2c, 0xfd, 0x79, - 0x63, 0x2c, 0x41, 0xc5, 0xa5, 0x39, 0x7c, 0x27, 0xda, 0x37, 0xa6, 0xe3, 0xf2, 0x8b, 0x61, 0xd7, - 0xec, 0xd1, 0xbe, 0x35, 0x06, 0xed, 0xbb, 0x34, 0xf5, 0x64, 0x5d, 0x25, 0xbf, 0x8c, 0x74, 0x97, - 0xa3, 0x1f, 0x44, 0x3e, 0xfa, 0x37, 0x00, 0x00, 0xff, 0xff, 0xd0, 0x7d, 0x80, 0x24, 0xff, 0x11, - 0x00, 0x00, + // 1126 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xc1, 0x4f, 0xe3, 0xc6, + 0x1b, 0xc5, 0x3f, 0xf6, 0x47, 0xe1, 0x63, 0xa1, 0xd2, 0xc0, 0x42, 0x62, 0xc0, 0x09, 0xa1, 0x07, + 0xb4, 0x5b, 0xec, 0x85, 0xaa, 0xb4, 0x5d, 0xf5, 0x42, 0xb6, 0x65, 0xd5, 0x03, 0x2a, 0x0a, 0x42, + 0x95, 0xda, 0x43, 0x34, 0x89, 0x07, 0x33, 0xc2, 0xf1, 0xb0, 0x9e, 0x49, 0xca, 0x5e, 0xab, 0x4a, + 0x3d, 0x55, 0xda, 0x63, 0xaf, 0xfd, 0x0f, 0x7a, 0xe8, 0x1f, 0xb1, 0xea, 0x69, 0xd5, 0x53, 0x4f, + 0x55, 0x05, 0x87, 0xfe, 0x1b, 0x55, 0x3c, 0x63, 0xc7, 0x89, 0xc7, 0x89, 0x43, 0x7b, 0xc3, 0x33, + 0xef, 0x7b, 0xef, 0x7b, 0x23, 0x3c, 0xdf, 0x73, 0xa0, 0x72, 0x1d, 0xb2, 0x1e, 0x09, 0x70, 0xd0, + 0x26, 0x0e, 0xb9, 0x69, 0x5f, 0xe2, 0xc0, 0x23, 0x4e, 0x6f, 0xdf, 0x11, 0x37, 0xf6, 0x75, 0xc8, + 0x04, 0x43, 0x6b, 0x03, 0x80, 0x1d, 0x03, 0xec, 0xde, 0xbe, 0x69, 0xb5, 0x19, 0xef, 0x30, 0xee, + 0xb4, 0x30, 0xef, 0x17, 0xb4, 0x88, 0xc0, 0xfb, 0x4e, 0x9b, 0xd1, 0x40, 0xd6, 0x99, 0xeb, 0x6a, + 0xbf, 0xc3, 0xbd, 0x3e, 0x5f, 0x87, 0x7b, 0x6a, 0xa3, 0x2c, 0x37, 0x9a, 0xd1, 0x93, 0x23, 0x1f, + 0xd4, 0xd6, 0xaa, 0xc7, 0x3c, 0x26, 0xd7, 0xfb, 0x7f, 0xa9, 0xd5, 0x9d, 0x9c, 0x16, 0x3b, 0x38, + 0xbc, 0x22, 0x62, 0x02, 0xe8, 0x1a, 0x87, 0xb8, 0xa3, 0xf8, 0x6b, 0x8f, 0x60, 0xe5, 0x84, 0x7b, + 0xcf, 0x43, 0x82, 0x05, 0x39, 0xe2, 0x57, 0x0d, 0xf2, 0xb2, 0x4b, 0xb8, 0xa8, 0xad, 0xc1, 0xea, + 0xf0, 0x32, 0xbf, 0x66, 0x01, 0x27, 0x43, 0xf0, 0x3a, 0x75, 0x75, 0xf0, 0x68, 0x59, 0xc1, 0xd7, + 0xe1, 0x51, 0x7f, 0xbd, 0xdf, 0x82, 0xff, 0x65, 0xe8, 0x92, 0x30, 0x2e, 0x28, 0xc1, 0xda, 0xe8, + 0x86, 0x2a, 0x59, 0x05, 0x74, 0xc2, 0xbd, 0x63, 0xea, 0xfb, 0x75, 0xea, 0xf2, 0x18, 0x2f, 0x75, + 0x07, 0xab, 0x19, 0xf0, 0x11, 0xbf, 0xd2, 0x80, 0xe5, 0xaa, 0x02, 0x4b, 0xcd, 0x93, 0xe8, 0x88, + 0xce, 0x88, 0x10, 0x3e, 0x89, 0x0b, 0xca, 0xb0, 0x9e, 0xd9, 0x51, 0x45, 0x26, 0x94, 0x92, 0xad, + 0xaf, 0xa8, 0xb8, 0x74, 0x43, 0xfc, 0x6d, 0x5c, 0xb6, 0x01, 0x65, 0xcd, 0x9e, 0x2a, 0xac, 0xc0, + 0x56, 0xb2, 0x79, 0x7e, 0xed, 0x62, 0x41, 0x3e, 0x23, 0x02, 0x53, 0x3f, 0xe9, 0xb2, 0x0a, 0x56, + 0x1e, 0x20, 0x97, 0xe2, 0xf3, 0x00, 0xb7, 0x7c, 0xe2, 0xe6, 0x53, 0x24, 0x00, 0x45, 0x51, 0x83, + 0xea, 0x08, 0xe2, 0x9c, 0x93, 0x70, 0xd8, 0xfd, 0x0e, 0x6c, 0x8f, 0xc1, 0x28, 0xa2, 0x34, 0xe8, + 0x04, 0x07, 0xd8, 0x23, 0xa7, 0x24, 0xec, 0x50, 0xce, 0x29, 0x0b, 0x12, 0x4b, 0xef, 0x41, 0x6d, + 0x1c, 0x48, 0x51, 0xa5, 0xbb, 0x96, 0xa8, 0x06, 0x79, 0x79, 0x24, 0x44, 0x98, 0xf0, 0x6c, 0x43, + 0x25, 0x17, 0xa1, 0x48, 0x7e, 0x36, 0xa2, 0xc3, 0x7f, 0xc1, 0x7a, 0xf2, 0xbf, 0x4e, 0x82, 0x15, + 0x01, 0x3a, 0x84, 0x05, 0xdc, 0x15, 0x97, 0x2c, 0xa4, 0xe2, 0x55, 0xc9, 0xa8, 0x1a, 0xbb, 0x0b, + 0xf5, 0xd2, 0xef, 0xbf, 0xee, 0xad, 0xaa, 0x57, 0xeb, 0xc8, 0x75, 0x43, 0xc2, 0xf9, 0x99, 0x08, + 0x69, 0xe0, 0x35, 0x06, 0x50, 0xf4, 0x29, 0xcc, 0xc9, 0x57, 0xa8, 0xf4, 0xbf, 0xaa, 0xb1, 0xbb, + 0x78, 0x60, 0xd9, 0xfa, 0x57, 0xdd, 0x96, 0x72, 0xf5, 0x07, 0x6f, 0xfe, 0xac, 0xcc, 0x34, 0x54, + 0xcd, 0xb3, 0xe5, 0xef, 0xfe, 0xfe, 0xe5, 0xf1, 0x80, 0xad, 0xb6, 0x09, 0xa6, 0xae, 0x45, 0xe5, + 0xe0, 0x37, 0x88, 0xfe, 0xeb, 0x5e, 0xb0, 0x9e, 0xb4, 0x78, 0x4c, 0x08, 0xff, 0xb7, 0xfd, 0x6f, + 0xc0, 0x82, 0xec, 0xa5, 0x49, 0xdd, 0xc8, 0xc2, 0x52, 0x63, 0x5e, 0x2e, 0x7c, 0xe1, 0xa2, 0x73, + 0x58, 0xc7, 0xae, 0xdb, 0xbc, 0x20, 0xa4, 0xd9, 0x8e, 0x1a, 0x6a, 0x62, 0x7e, 0xd5, 0xbc, 0xf0, + 0xb1, 0x28, 0xcd, 0x56, 0x67, 0x77, 0x17, 0x0f, 0xca, 0xb6, 0xe2, 0xef, 0x5f, 0x60, 0xb6, 0xba, + 0xc0, 0xec, 0xe7, 0x8c, 0x06, 0xca, 0xe8, 0x0a, 0x76, 0xdd, 0x63, 0x42, 0x92, 0x6b, 0xe1, 0xd8, + 0xc7, 0x02, 0x7d, 0x03, 0x66, 0x48, 0x3a, 0xac, 0x47, 0xb4, 0xcc, 0x0f, 0x8a, 0x31, 0xaf, 0x49, + 0x8a, 0x0c, 0x79, 0xb6, 0xe7, 0x16, 0x75, 0x25, 0xf3, 0xff, 0xef, 0xd1, 0x73, 0x9d, 0xba, 0xf9, + 0x3d, 0x27, 0xcc, 0x73, 0xf7, 0xeb, 0x39, 0x26, 0x6f, 0x83, 0x15, 0xf7, 0xcc, 0x89, 0xef, 0x93, + 0xb0, 0xc9, 0xa3, 0x77, 0xa9, 0x43, 0x02, 0x21, 0x05, 0xde, 0x29, 0x26, 0x60, 0xca, 0xd6, 0xcf, + 0x22, 0x92, 0xb3, 0x84, 0x23, 0x12, 0xa1, 0xb0, 0x9d, 0x72, 0x90, 0xa3, 0x33, 0x5f, 0x4c, 0x67, + 0x2b, 0x31, 0xa2, 0x95, 0x0a, 0xa0, 0x9a, 0xef, 0x27, 0xc4, 0x82, 0x32, 0x5e, 0x5a, 0x88, 0x94, + 0xaa, 0x79, 0xaf, 0xcb, 0x31, 0x21, 0x8d, 0x3e, 0x50, 0x09, 0x6e, 0xea, 0x8d, 0x45, 0x10, 0x8e, + 0x04, 0xec, 0x8c, 0xb5, 0xa6, 0x24, 0x61, 0x2a, 0xc9, 0x4a, 0xae, 0x47, 0xa5, 0x8a, 0x61, 0x2b, + 0x76, 0xd9, 0xea, 0xbe, 0xd2, 0x1c, 0xe6, 0x62, 0xb1, 0xc3, 0x2c, 0x4b, 0x6f, 0xf5, 0x3e, 0xc7, + 0xc8, 0x41, 0x7a, 0x50, 0x4d, 0x19, 0xd3, 0xab, 0x3c, 0x2c, 0xa6, 0xb2, 0x99, 0xd8, 0xd1, 0x09, + 0xf9, 0x50, 0xc9, 0xf5, 0xa2, 0x4e, 0x6f, 0x69, 0xaa, 0xd3, 0xdb, 0xd0, 0x9a, 0x52, 0x27, 0x17, + 0x42, 0x6d, 0x9c, 0x2d, 0x25, 0xb8, 0x3c, 0x95, 0xa0, 0x95, 0xe7, 0x4f, 0x6a, 0x66, 0xae, 0x5a, + 0x39, 0xa6, 0x47, 0xee, 0xd2, 0xcc, 0xa8, 0x90, 0xd3, 0xed, 0x34, 0xca, 0x3f, 0xff, 0xc1, 0xa8, + 0x90, 0x41, 0x6a, 0xd2, 0xa8, 0x90, 0x72, 0xf1, 0xa8, 0x90, 0x35, 0xf9, 0xa3, 0x62, 0xb8, 0x45, + 0xe9, 0xe0, 0xe0, 0xa7, 0x65, 0x98, 0x3d, 0xe1, 0x1e, 0xba, 0x80, 0x85, 0xe4, 0x7a, 0x44, 0x4f, + 0x72, 0x67, 0x53, 0x36, 0xcf, 0x99, 0xef, 0x17, 0x03, 0x4b, 0xbd, 0x81, 0x4e, 0x9d, 0xba, 0x05, + 0x74, 0x06, 0x41, 0xb0, 0x80, 0x4e, 0x2a, 0x1e, 0x22, 0x1f, 0x16, 0x53, 0x11, 0x10, 0xed, 0x8d, + 0x2b, 0xce, 0x64, 0x48, 0xd3, 0x2e, 0x0a, 0x57, 0x6a, 0x6d, 0x98, 0x8f, 0x03, 0x24, 0x7a, 0x3c, + 0xa6, 0x76, 0x24, 0x7b, 0x9a, 0x4f, 0x0a, 0x61, 0x87, 0x45, 0xfa, 0xc1, 0x73, 0xa2, 0x48, 0x2a, + 0xb3, 0x4e, 0x14, 0x49, 0x27, 0x59, 0xc4, 0xe0, 0x61, 0x3a, 0xac, 0xa2, 0x71, 0x27, 0xa1, 0xc9, + 0xbb, 0xa6, 0x53, 0x18, 0xaf, 0x04, 0xbb, 0xb0, 0x3c, 0x1c, 0x73, 0xd1, 0xd3, 0x89, 0x14, 0x23, + 0x69, 0xd9, 0xdc, 0x9f, 0xa2, 0x42, 0xc9, 0x7e, 0x6f, 0xc0, 0x8a, 0x26, 0x20, 0xa3, 0x0f, 0x27, + 0x52, 0xe9, 0x12, 0xb7, 0x79, 0x38, 0x6d, 0x59, 0x4e, 0x1b, 0x2a, 0x64, 0x17, 0x6e, 0x63, 0x38, + 0xb5, 0x17, 0x6e, 0x63, 0x24, 0xcb, 0xa3, 0x1f, 0x0d, 0x58, 0xd3, 0xa7, 0x74, 0xf4, 0x71, 0x41, + 0xca, 0x4c, 0xf8, 0x37, 0x3f, 0xb9, 0x47, 0xa5, 0xea, 0xe7, 0xb5, 0x01, 0xeb, 0x39, 0x59, 0x1f, + 0x4d, 0xa6, 0xcd, 0xfb, 0x88, 0x30, 0x9f, 0xdd, 0xa7, 0x54, 0xb5, 0xf4, 0x83, 0x01, 0xab, 0xba, + 0xcf, 0x06, 0x74, 0x58, 0x90, 0x74, 0xe4, 0x4b, 0xc4, 0xfc, 0x68, 0xea, 0x3a, 0xd5, 0xc9, 0x0d, + 0xbc, 0x3b, 0x12, 0xfc, 0xd1, 0xb8, 0x17, 0x40, 0xff, 0x1d, 0x63, 0x1e, 0x4c, 0x53, 0xa2, 0x94, + 0x43, 0x58, 0x1a, 0x9a, 0x83, 0xc8, 0x19, 0x4f, 0x92, 0xf9, 0xfa, 0x30, 0x9f, 0x16, 0x2f, 0x18, + 0x72, 0x9b, 0x9e, 0x5d, 0x93, 0xdc, 0x6a, 0x46, 0xf1, 0x24, 0xb7, 0xba, 0xd1, 0x58, 0x27, 0x6f, + 0x6e, 0x2d, 0xe3, 0xed, 0xad, 0x65, 0xfc, 0x75, 0x6b, 0x19, 0xaf, 0xef, 0xac, 0x99, 0xb7, 0x77, + 0xd6, 0xcc, 0x1f, 0x77, 0xd6, 0x0c, 0x94, 0x29, 0xcb, 0xe1, 0x3b, 0x35, 0xbe, 0xb6, 0x3d, 0x2a, + 0x2e, 0xbb, 0x2d, 0xbb, 0xcd, 0x3a, 0xce, 0x00, 0xb4, 0x47, 0x59, 0xea, 0xc9, 0xb9, 0x49, 0x7e, + 0x36, 0x69, 0xcd, 0x45, 0xbf, 0x96, 0x7c, 0xf0, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfd, 0x92, + 0x12, 0x76, 0x1c, 0x12, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2665,7 +2675,7 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x6a + dAtA[i] = 0x72 } } if len(m.AddFeeBuyerSettlementRatios) > 0 { @@ -2679,7 +2689,7 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x62 + dAtA[i] = 0x6a } } if len(m.RemoveFeeBuyerSettlementFlat) > 0 { @@ -2693,7 +2703,7 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x5a + dAtA[i] = 0x62 } } if len(m.AddFeeBuyerSettlementFlat) > 0 { @@ -2707,7 +2717,7 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x52 + dAtA[i] = 0x5a } } if len(m.RemoveFeeSellerSettlementRatios) > 0 { @@ -2721,7 +2731,7 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x4a + dAtA[i] = 0x52 } } if len(m.AddFeeSellerSettlementRatios) > 0 { @@ -2735,7 +2745,7 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x42 + dAtA[i] = 0x4a } } if len(m.RemoveFeeSellerSettlementFlat) > 0 { @@ -2749,7 +2759,7 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x3a + dAtA[i] = 0x42 } } if len(m.AddFeeSellerSettlementFlat) > 0 { @@ -2763,7 +2773,7 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x32 + dAtA[i] = 0x3a } } if len(m.RemoveFeeCreateBidFlat) > 0 { @@ -2777,7 +2787,7 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x2a + dAtA[i] = 0x32 } } if len(m.AddFeeCreateBidFlat) > 0 { @@ -2791,7 +2801,7 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x22 + dAtA[i] = 0x2a } } if len(m.RemoveFeeCreateAskFlat) > 0 { @@ -2805,7 +2815,7 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x1a + dAtA[i] = 0x22 } } if len(m.AddFeeCreateAskFlat) > 0 { @@ -2819,9 +2829,14 @@ func (m *MsgGovManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x12 + dAtA[i] = 0x1a } } + if m.MarketId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } if len(m.Authority) > 0 { i -= len(m.Authority) copy(dAtA[i:], m.Authority) @@ -3179,6 +3194,9 @@ func (m *MsgGovManageFeesRequest) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } + if m.MarketId != 0 { + n += 1 + sovTx(uint64(m.MarketId)) + } if len(m.AddFeeCreateAskFlat) > 0 { for _, e := range m.AddFeeCreateAskFlat { l = e.Size() @@ -4720,6 +4738,25 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { m.Authority = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AddFeeCreateAskFlat", wireType) } @@ -4753,7 +4790,7 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 3: + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeCreateAskFlat", wireType) } @@ -4787,7 +4824,7 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 4: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AddFeeCreateBidFlat", wireType) } @@ -4821,7 +4858,7 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 5: + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeCreateBidFlat", wireType) } @@ -4855,7 +4892,7 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 6: + case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AddFeeSellerSettlementFlat", wireType) } @@ -4889,7 +4926,7 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 7: + case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeSellerSettlementFlat", wireType) } @@ -4923,7 +4960,7 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 8: + case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AddFeeSellerSettlementRatios", wireType) } @@ -4957,7 +4994,7 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 9: + case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeSellerSettlementRatios", wireType) } @@ -4991,7 +5028,7 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 10: + case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AddFeeBuyerSettlementFlat", wireType) } @@ -5025,7 +5062,7 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 11: + case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeBuyerSettlementFlat", wireType) } @@ -5059,7 +5096,7 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 12: + case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AddFeeBuyerSettlementRatios", wireType) } @@ -5093,7 +5130,7 @@ func (m *MsgGovManageFeesRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 13: + case 14: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RemoveFeeBuyerSettlementRatios", wireType) } From 09c03092b5086487c2d59d9ccc5044bf30bf5afb Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 14:34:39 -0600 Subject: [PATCH 079/309] [1658]: Get rid of NewGenesisState since it doesn't do anything helpful. Make sure MsgGovManageFeesRequest has a market id. --- x/exchange/genesis.go | 13 +-- x/exchange/genesis_test.go | 234 ++++--------------------------------- x/exchange/msg.go | 4 + 3 files changed, 33 insertions(+), 218 deletions(-) diff --git a/x/exchange/genesis.go b/x/exchange/genesis.go index a498096507..af65dbaef9 100644 --- a/x/exchange/genesis.go +++ b/x/exchange/genesis.go @@ -5,16 +5,9 @@ import ( "fmt" ) -func NewGenesisState(params *Params, markets []Market, orders []Order) *GenesisState { - return &GenesisState{ - Params: params, - Markets: markets, - Orders: orders, - } -} - +// DefaultGenesisState returns the default genesis state for the exchange module. func DefaultGenesisState() *GenesisState { - return NewGenesisState(DefaultParams(), nil, nil) + return &GenesisState{Params: DefaultParams()} } func (g GenesisState) Validate() error { @@ -67,5 +60,7 @@ func (g GenesisState) Validate() error { } } + // No validation to do on LastMarketId. + return errors.Join(errs...) } diff --git a/x/exchange/genesis_test.go b/x/exchange/genesis_test.go index 8ae61d4209..0e01841340 100644 --- a/x/exchange/genesis_test.go +++ b/x/exchange/genesis_test.go @@ -3,7 +3,6 @@ package exchange import ( "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" sdkmath "cosmossdk.io/math" @@ -12,214 +11,6 @@ import ( "github.com/provenance-io/provenance/testutil/assertions" ) -func TestNewGenesisState(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} - } - - tests := []struct { - name string - params *Params - markets []Market - orders []Order - expected *GenesisState - }{ - { - name: "all nil", - params: nil, - markets: nil, - orders: nil, - expected: &GenesisState{}, - }, - { - name: "only params", - params: &Params{ - DefaultSplit: 54, - DenomSplits: []DenomSplit{ - {Denom: "farnsworth", Split: 88}, - {Denom: "fry", Split: 1000}, - }, - }, - expected: &GenesisState{ - Params: &Params{ - DefaultSplit: 54, - DenomSplits: []DenomSplit{ - {Denom: "farnsworth", Split: 88}, - {Denom: "fry", Split: 1000}, - }, - }, - }, - }, - { - name: "only markets", - markets: []Market{ - { - MarketId: 1, - MarketDetails: MarketDetails{Name: "Market One"}, - FeeCreateAskFlat: []sdk.Coin{coin(5, "askd")}, - AcceptingOrders: true, - }, - { - MarketId: 2, - MarketDetails: MarketDetails{Name: "Market Two"}, - FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, - FeeBuyerSettlementRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, - AccessGrants: []AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, - ReqAttrCreateBid: []string{"just.some.attr"}, - }, - }, - expected: &GenesisState{ - Params: nil, - Markets: []Market{ - { - MarketId: 1, - MarketDetails: MarketDetails{Name: "Market One"}, - FeeCreateAskFlat: []sdk.Coin{coin(5, "askd")}, - AcceptingOrders: true, - }, - { - MarketId: 2, - MarketDetails: MarketDetails{Name: "Market Two"}, - FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, - FeeBuyerSettlementRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, - AccessGrants: []AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, - ReqAttrCreateBid: []string{"just.some.attr"}, - }, - }, - Orders: nil, - }, - }, - { - name: "just orders", - orders: []Order{ - *NewOrder(1).WithAsk(&AskOrder{ - MarketId: 1, - Seller: "seller_addr", - Assets: []sdk.Coin{coin(55, "fry")}, - Price: coin(888, "nibbler"), - }), - *NewOrder(1).WithBid(&BidOrder{ - MarketId: 1, - Buyer: "buyer_addr", - Assets: []sdk.Coin{coin(55, "fry")}, - Price: coin(888, "nibbler"), - }), - }, - expected: &GenesisState{ - Params: nil, - Markets: nil, - Orders: []Order{ - *NewOrder(1).WithAsk(&AskOrder{ - MarketId: 1, - Seller: "seller_addr", - Assets: []sdk.Coin{coin(55, "fry")}, - Price: coin(888, "nibbler"), - }), - *NewOrder(1).WithBid(&BidOrder{ - MarketId: 1, - Buyer: "buyer_addr", - Assets: []sdk.Coin{coin(55, "fry")}, - Price: coin(888, "nibbler"), - }), - }, - }, - }, - { - name: "all three together", - params: &Params{ - DefaultSplit: 54, - DenomSplits: []DenomSplit{ - {Denom: "farnsworth", Split: 88}, - {Denom: "fry", Split: 1000}, - }, - }, - markets: []Market{ - { - MarketId: 1, - MarketDetails: MarketDetails{Name: "Market One"}, - FeeCreateAskFlat: []sdk.Coin{coin(5, "askd")}, - AcceptingOrders: true, - }, - { - MarketId: 2, - MarketDetails: MarketDetails{Name: "Market Two"}, - FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, - FeeBuyerSettlementRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, - AccessGrants: []AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, - ReqAttrCreateBid: []string{"just.some.attr"}, - }, - }, - orders: []Order{ - *NewOrder(1).WithAsk(&AskOrder{ - MarketId: 1, - Seller: "seller_addr", - Assets: []sdk.Coin{coin(55, "fry")}, - Price: coin(888, "nibbler"), - }), - *NewOrder(1).WithBid(&BidOrder{ - MarketId: 1, - Buyer: "buyer_addr", - Assets: []sdk.Coin{coin(55, "fry")}, - Price: coin(888, "nibbler"), - }), - }, - expected: &GenesisState{ - Params: &Params{ - DefaultSplit: 54, - DenomSplits: []DenomSplit{ - {Denom: "farnsworth", Split: 88}, - {Denom: "fry", Split: 1000}, - }, - }, - Markets: []Market{ - { - MarketId: 1, - MarketDetails: MarketDetails{Name: "Market One"}, - FeeCreateAskFlat: []sdk.Coin{coin(5, "askd")}, - AcceptingOrders: true, - }, - { - MarketId: 2, - MarketDetails: MarketDetails{Name: "Market Two"}, - FeeCreateBidFlat: []sdk.Coin{coin(3, "bidd")}, - FeeBuyerSettlementRatios: []FeeRatio{{Price: coin(7, "buyrr"), Fee: coin(1, "buyrr")}}, - AccessGrants: []AccessGrant{{Address: "addr1", Permissions: AllPermissions()}}, - ReqAttrCreateBid: []string{"just.some.attr"}, - }, - }, - Orders: []Order{ - *NewOrder(1).WithAsk(&AskOrder{ - MarketId: 1, - Seller: "seller_addr", - Assets: []sdk.Coin{coin(55, "fry")}, - Price: coin(888, "nibbler"), - }), - *NewOrder(1).WithBid(&BidOrder{ - MarketId: 1, - Buyer: "buyer_addr", - Assets: []sdk.Coin{coin(55, "fry")}, - Price: coin(888, "nibbler"), - }), - }, - }, - }, - { - name: "defaults", - params: DefaultParams(), - markets: nil, - orders: nil, - expected: DefaultGenesisState(), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - actual := NewGenesisState(tc.params, tc.markets, tc.orders) - assert.Equal(t, tc.expected, actual, "NewGenesisState") - }) - } -} - func TestGenesisState_Validate(t *testing.T) { addr1 := sdk.AccAddress("addr1_______________").String() coins := func(coins string) sdk.Coins { @@ -411,6 +202,31 @@ func TestGenesisState_Validate(t *testing.T) { `invalid order[1]: unknown market id 4`, }, }, + { + name: "last market id 1", + genState: GenesisState{LastMarketId: 1}, + expErr: nil, + }, + { + name: "last market id 256", + genState: GenesisState{LastMarketId: 256}, + expErr: nil, + }, + { + name: "last market id 65,536", + genState: GenesisState{LastMarketId: 65_536}, + expErr: nil, + }, + { + name: "last market id 16,777,216", + genState: GenesisState{LastMarketId: 16_777_216}, + expErr: nil, + }, + { + name: "last market id max uint32", + genState: GenesisState{LastMarketId: 4_294_967_295}, + expErr: nil, + }, } for _, tc := range tests { diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 717ecf3be1..2c843e5b0a 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -165,6 +165,10 @@ func (m MsgGovManageFeesRequest) ValidateBasic() error { errs = append(errs, fmt.Errorf("invalid authority: %w", err)) } + if m.MarketId == 0 { + errs = append(errs, errors.New("market id cannot be zero")) + } + if m.HasUpdates() { errs = append(errs, ValidateAddRemoveFeeOptions("create-ask flat fee", m.AddFeeCreateAskFlat, m.RemoveFeeCreateAskFlat), From f259eb6385b53b4ddca1d5a14fd11a6f9b287dfb Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 14:44:31 -0600 Subject: [PATCH 080/309] [1658]: Implement GovManageFees. --- x/exchange/keeper/msg_server.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index acf933a882..f112a16ca6 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -121,9 +121,14 @@ func (k MsgServer) GovManageFees(goCtx context.Context, msg *exchange.MsgGovMana } ctx := sdk.UnwrapSDKContext(goCtx) - // TODO[1658]: Implement GovManageFees - _ = ctx - panic("not implemented") + k.UpdateCreateAskFlatFees(ctx, msg.MarketId, msg.RemoveFeeCreateAskFlat, msg.AddFeeCreateAskFlat) + k.UpdateCreateBidFlatFees(ctx, msg.MarketId, msg.RemoveFeeCreateBidFlat, msg.AddFeeCreateBidFlat) + k.UpdateSellerSettlementFlatFees(ctx, msg.MarketId, msg.RemoveFeeSellerSettlementFlat, msg.AddFeeSellerSettlementFlat) + k.UpdateSellerSettlementRatios(ctx, msg.MarketId, msg.RemoveFeeSellerSettlementRatios, msg.AddFeeSellerSettlementRatios) + k.UpdateBuyerSettlementFlatFees(ctx, msg.MarketId, msg.RemoveFeeBuyerSettlementFlat, msg.AddFeeBuyerSettlementFlat) + k.UpdateBuyerSettlementRatios(ctx, msg.MarketId, msg.RemoveFeeBuyerSettlementRatios, msg.AddFeeBuyerSettlementRatios) + + return &exchange.MsgGovManageFeesResponse{}, nil } // GovUpdateParams is a governance proposal endpoint for updating the exchange module's params. From 62fab329774d74ed22fb6c0b7b4ca8d824a22fa8 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 14:56:13 -0600 Subject: [PATCH 081/309] [1658]: Create a GetMarket function. --- x/exchange/keeper/keeper.go | 12 ++++++++++++ x/exchange/keeper/market.go | 34 ++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 5cee72c1f4..28c98813d6 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -1,6 +1,8 @@ package keeper import ( + "errors" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -90,3 +92,13 @@ func (k Keeper) getStore(ctx sdk.Context) sdk.KVStore { func (k Keeper) iterate(ctx sdk.Context, pre []byte, cb func(key, value []byte) bool) { iterate(k.getStore(ctx), pre, cb) } + +// NormalizeReqAttrs normalizes/validates each of the provided require attributes. +func (k Keeper) NormalizeReqAttrs(ctx sdk.Context, reqAttrs []string) ([]string, error) { + rv := make([]string, len(reqAttrs)) + errs := make([]error, len(reqAttrs)) + for i, attr := range reqAttrs { + rv[i], errs[i] = k.nameKeeper.Normalize(ctx, attr) + } + return rv, errors.Join(errs...) +} diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 16517965c5..9575d293bf 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -745,12 +745,30 @@ func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (marketID return marketID, err } -// NormalizeReqAttrs normalizes/validates each of the provided require attributes. -func (k Keeper) NormalizeReqAttrs(ctx sdk.Context, reqAttrs []string) ([]string, error) { - rv := make([]string, len(reqAttrs)) - errs := make([]error, len(reqAttrs)) - for i, attr := range reqAttrs { - rv[i], errs[i] = k.nameKeeper.Normalize(ctx, attr) - } - return rv, errors.Join(errs...) +func (k Keeper) GetMarket(ctx sdk.Context, marketID uint32) *exchange.Market { + marketAddr := exchange.GetMarketAddress(marketID) + acc := k.accountKeeper.GetAccount(ctx, marketAddr) + if acc == nil { + return nil + } + marketAcc, ok := acc.(*exchange.MarketAccount) + if !ok { + return nil + } + + market := &exchange.Market{MarketId: marketID} + market.MarketDetails = marketAcc.MarketDetails + market.FeeCreateAskFlat = k.GetCreateAskFlatFees(ctx, marketID) + market.FeeCreateBidFlat = k.GetCreateBidFlatFees(ctx, marketID) + market.FeeSellerSettlementFlat = k.GetSellerSettlementFlatFees(ctx, marketID) + market.FeeSellerSettlementRatios = k.GetSellerSettlementRatios(ctx, marketID) + market.FeeBuyerSettlementFlat = k.GetBuyerSettlementFlatFees(ctx, marketID) + market.FeeBuyerSettlementRatios = k.GetBuyerSettlementRatios(ctx, marketID) + market.AcceptingOrders = k.IsMarketActive(ctx, marketID) + market.AllowUserSettlement = k.IsUserSettlementAllowed(ctx, marketID) + market.AccessGrants = k.GetAccessGrants(ctx, marketID) + market.ReqAttrCreateAsk = k.GetReqAttrAsk(ctx, marketID) + market.ReqAttrCreateBid = k.GetReqAttrBid(ctx, marketID) + + return market } From e1a22b788ae9b1468c75d0122ea7f259f45d9c73 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 15:07:38 -0600 Subject: [PATCH 082/309] [1658]: Create a known market id set of entries in the store. --- x/exchange/keeper/keys.go | 28 ++++++++++++++++++++++++++++ x/exchange/keeper/keys_test.go | 7 +++++++ x/exchange/keeper/market.go | 20 ++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 53bc83f754..1ab277074e 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -26,6 +26,8 @@ import ( // Last Market ID: 0x06 => uint32 // This stores the last auto-selected market id. // +// Known Market IDs: 0x07 | => nil +// // Markets: // Some aspects of a market are stored using the accounts module and the MarketAccount type. // Others are stored in the exchange module. @@ -70,6 +72,8 @@ const ( KeyTypeParams = byte(0x00) // KeyTypeLastMarketID is the type byte for the last auto-selected market id. KeyTypeLastMarketID = byte(0x06) + // KeyTypeKnownMarketID is the type byte for known market id entries. + KeyTypeKnownMarketID = byte(0x07) // KeyTypeMarket is the type byte for market entries. KeyTypeMarket = byte(0x01) // KeyTypeOrder is the type byte for order entries. @@ -197,6 +201,30 @@ func MakeKeyLastMarketID() []byte { return []byte{KeyTypeLastMarketID} } +// keyPrefixKnownMarketID creates the key prefix for a known market id entry. +func keyPrefixKnownMarketID(extraCap int) []byte { + return prepKey(KeyTypeKnownMarketID, nil, extraCap) +} + +// GetKeyPrefixKnownMarketID creates the key prefix for all known market id entries. +func GetKeyPrefixKnownMarketID() []byte { + return keyPrefixKnownMarketID(0) +} + +// MakeKeyKnownMarketID creates the key for a market's known market id entry. +func MakeKeyKnownMarketID(marketID uint32) []byte { + suffix := uint32Bz(marketID) + rv := keyPrefixKnownMarketID(len(suffix)) + rv = append(rv, suffix...) + return rv +} + +// ParseKeySuffixKnownMarketID parses the market id out of a known market id key that doesn't have the type byte. +// Input is expected to have the format . +func ParseKeySuffixKnownMarketID(suffix []byte) uint32 { + return uint32FromBz(suffix) +} + // keyPrefixMarket creates the root of a market's key with extra capacity for the rest. func keyPrefixMarket(marketID uint32, extraCap int) []byte { return prepKey(KeyTypeMarket, uint32Bz(marketID), extraCap) diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 3a5921d2f3..b4111d56fb 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -82,6 +82,7 @@ func TestKeyTypeUniqueness(t *testing.T) { types: []byteEntry{ {name: "KeyTypeParams", value: keeper.KeyTypeParams}, {name: "KeyTypeLastMarketID", value: keeper.KeyTypeLastMarketID}, + {name: "KeyTypeKnownMarketID", value: keeper.KeyTypeKnownMarketID}, {name: "KeyTypeMarket", value: keeper.KeyTypeMarket}, {name: "KeyTypeOrder", value: keeper.KeyTypeOrder}, {name: "KeyTypeMarketToOrderIndex", value: keeper.KeyTypeMarketToOrderIndex}, @@ -178,6 +179,12 @@ func TestMakeKeyParamsSplit(t *testing.T) { // TODO[1658]: func TestMakeKeyLastMarketID(t *testing.T) +// TODO[1658]: func TestGetKeyPrefixKnownMarketID(t *testing.T) + +// TODO[1658]: func TestMakeKeyKnownMarketID(t *testing.T) + +// TODO[1658]: func TestParseKeySuffixKnownMarketID(t *testing.T) + func TestGetKeyPrefixMarket(t *testing.T) { tests := []struct { name string diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 9575d293bf..573eb82c93 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -699,6 +699,23 @@ func (k Keeper) NextMarketID(ctx sdk.Context) uint32 { return marketID } +// SetMarketKnown sets the known market id indicator in the store. +func (k Keeper) SetMarketKnown(ctx sdk.Context, marketID uint32) { + store := k.getStore(ctx) + key := MakeKeyKnownMarketID(marketID) + store.Set(key, nil) +} + +// GetAllMarketIDs gets all the known market ids from the store. +func (k Keeper) GetAllMarketIDs(ctx sdk.Context) []uint32 { + var rv []uint32 + k.iterate(ctx, GetKeyPrefixKnownMarketID(), func(key, _ []byte) bool { + rv = append(rv, ParseKeySuffixKnownMarketID(key)) + return false + }) + return rv +} + // CreateMarket saves a new market to the store with all the info provided. // If the marketId is zero, the next available one will be used. func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (marketID uint32, err error) { @@ -730,6 +747,7 @@ func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (marketID k.accountKeeper.NewAccount(ctx, marketAcc) k.accountKeeper.SetAccount(ctx, marketAcc) + k.SetMarketKnown(ctx, marketID) k.SetCreateAskFlatFees(ctx, marketID, market.FeeCreateAskFlat) k.SetCreateBidFlatFees(ctx, marketID, market.FeeCreateBidFlat) k.SetSellerSettlementFlatFees(ctx, marketID, market.FeeSellerSettlementFlat) @@ -745,6 +763,8 @@ func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (marketID return marketID, err } +// GetMarket reads all the market info from state and returns it. +// Returns nil if the market account doesn't exist or it's not a market account. func (k Keeper) GetMarket(ctx sdk.Context, marketID uint32) *exchange.Market { marketAddr := exchange.GetMarketAddress(marketID) acc := k.accountKeeper.GetAccount(ctx, marketAddr) From f0c245740411e4280bbe33f0099cbe38fc7875b0 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 15:51:38 -0600 Subject: [PATCH 083/309] [1658]: Break out a lot of the keeper funcs into versions that can take in a store. --- x/exchange/keeper/market.go | 279 ++++++++++++++++++++------------ x/exchange/keeper/msg_server.go | 7 +- 2 files changed, 174 insertions(+), 112 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 573eb82c93..ddedf0886a 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -13,6 +13,8 @@ import ( "github.com/provenance-io/provenance/x/exchange" ) +// TODO[1658]: Recheck all the public funcs in here to make sure they're still needed. + // flatFeeKeyMakers are the key and prefix maker funcs for a specific flat fee entry. type flatFeeKeyMakers struct { key func(marketID uint32, denom string) []byte @@ -197,19 +199,24 @@ func updateFeeRatios(store sdk.KVStore, marketID uint32, toDelete, toWrite []exc } } +// getCreateAskFlatFees gets the create-ask flat fee options for a market. +func getCreateAskFlatFees(store sdk.KVStore, marketID uint32) []sdk.Coin { + return getAllFlatFees(store, marketID, createAskFlatKeyMakers) +} + // GetCreateAskFlatFees gets the create-ask flat fee options for a market. func (k Keeper) GetCreateAskFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return getAllFlatFees(k.getStore(ctx), marketID, createAskFlatKeyMakers) + return getCreateAskFlatFees(k.getStore(ctx), marketID) } -// SetCreateAskFlatFees sets the create-ask flat fees for a market. -func (k Keeper) SetCreateAskFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { - setAllFlatFees(k.getStore(ctx), marketID, options, createAskFlatKeyMakers) +// setCreateAskFlatFees sets the create-ask flat fees for a market. +func setCreateAskFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin) { + setAllFlatFees(store, marketID, options, createAskFlatKeyMakers) } -// UpdateCreateAskFlatFees deletes all create-ask flat fees to delete then adds the ones to add. -func (k Keeper) UpdateCreateAskFlatFees(ctx sdk.Context, marketID uint32, toDelete, toAdd []sdk.Coin) { - updateFlatFees(k.getStore(ctx), marketID, toDelete, toAdd, createAskFlatKeyMakers) +// updateCreateAskFlatFees deletes all create-ask flat fees to delete then adds the ones to add. +func updateCreateAskFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd []sdk.Coin) { + updateFlatFees(store, marketID, toDelete, toAdd, createAskFlatKeyMakers) } // IsAcceptableCreateAskFlatFee returns true if the provide fee has a denom accepted as a create-ask flat fee, @@ -219,19 +226,24 @@ func (k Keeper) IsAcceptableCreateAskFlatFee(ctx sdk.Context, marketID uint32, f return reqFee != nil && fee.Amount.GTE(reqFee.Amount) } +// getCreateBidFlatFees gets the create-bid flat fee options for a market. +func getCreateBidFlatFees(store sdk.KVStore, marketID uint32) []sdk.Coin { + return getAllFlatFees(store, marketID, createBidFlatKeyMakers) +} + // GetCreateBidFlatFees gets the create-bid flat fee options for a market. func (k Keeper) GetCreateBidFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return getAllFlatFees(k.getStore(ctx), marketID, createBidFlatKeyMakers) + return getCreateBidFlatFees(k.getStore(ctx), marketID) } -// SetCreateBidFlatFees sets the create-bid flat fees for a market. -func (k Keeper) SetCreateBidFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { - setAllFlatFees(k.getStore(ctx), marketID, options, createBidFlatKeyMakers) +// setCreateBidFlatFees sets the create-bid flat fees for a market. +func setCreateBidFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin) { + setAllFlatFees(store, marketID, options, createBidFlatKeyMakers) } -// UpdateCreateBidFlatFees deletes all create-bid flat fees to delete then adds the ones to add. -func (k Keeper) UpdateCreateBidFlatFees(ctx sdk.Context, marketID uint32, toDelete, toAdd []sdk.Coin) { - updateFlatFees(k.getStore(ctx), marketID, toDelete, toAdd, createBidFlatKeyMakers) +// updateCreateBidFlatFees deletes all create-bid flat fees to delete then adds the ones to add. +func updateCreateBidFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd []sdk.Coin) { + updateFlatFees(store, marketID, toDelete, toAdd, createBidFlatKeyMakers) } // IsAcceptableCreateBidFlatFee returns true if the provide fee has a denom accepted as a create-bid flat fee, @@ -241,19 +253,24 @@ func (k Keeper) IsAcceptableCreateBidFlatFee(ctx sdk.Context, marketID uint32, f return reqFee != nil && fee.Amount.GTE(reqFee.Amount) } +// getSellerSettlementFlatFees gets the seller settlement flat fee options for a market. +func getSellerSettlementFlatFees(store sdk.KVStore, marketID uint32) []sdk.Coin { + return getAllFlatFees(store, marketID, sellerSettlementFlatKeyMakers) +} + // GetSellerSettlementFlatFees gets the seller settlement flat fee options for a market. func (k Keeper) GetSellerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return getAllFlatFees(k.getStore(ctx), marketID, sellerSettlementFlatKeyMakers) + return getSellerSettlementFlatFees(k.getStore(ctx), marketID) } -// SetSellerSettlementFlatFees sets the seller settlement flat fees for a market. -func (k Keeper) SetSellerSettlementFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { - setAllFlatFees(k.getStore(ctx), marketID, options, sellerSettlementFlatKeyMakers) +// setSellerSettlementFlatFees sets the seller settlement flat fees for a market. +func setSellerSettlementFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin) { + setAllFlatFees(store, marketID, options, sellerSettlementFlatKeyMakers) } -// UpdateSellerSettlementFlatFees deletes all seller settlement flat fees to delete then adds the ones to add. -func (k Keeper) UpdateSellerSettlementFlatFees(ctx sdk.Context, marketID uint32, toDelete, toAdd []sdk.Coin) { - updateFlatFees(k.getStore(ctx), marketID, toDelete, toAdd, sellerSettlementFlatKeyMakers) +// updateSellerSettlementFlatFees deletes all seller settlement flat fees to delete then adds the ones to add. +func updateSellerSettlementFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd []sdk.Coin) { + updateFlatFees(store, marketID, toDelete, toAdd, sellerSettlementFlatKeyMakers) } // IsAcceptableSellerSettlementFlatFee returns true if the provide fee has a denom accepted as a seller settlement @@ -263,19 +280,24 @@ func (k Keeper) IsAcceptableSellerSettlementFlatFee(ctx sdk.Context, marketID ui return reqFee != nil && fee.Amount.GTE(reqFee.Amount) } +// getSellerSettlementRatios gets the seller settlement fee ratios for a market. +func getSellerSettlementRatios(store sdk.KVStore, marketID uint32) []exchange.FeeRatio { + return getAllFeeRatios(store, marketID, sellerSettlementRatioKeyMakers) +} + // GetSellerSettlementRatios gets the seller settlement fee ratios for a market. func (k Keeper) GetSellerSettlementRatios(ctx sdk.Context, marketID uint32) []exchange.FeeRatio { - return getAllFeeRatios(k.getStore(ctx), marketID, sellerSettlementRatioKeyMakers) + return getSellerSettlementRatios(k.getStore(ctx), marketID) } -// SetSellerSettlementRatios sets the seller settlement fee ratios for a market. -func (k Keeper) SetSellerSettlementRatios(ctx sdk.Context, marketID uint32, ratios []exchange.FeeRatio) { - setAllFeeRatios(k.getStore(ctx), marketID, ratios, sellerSettlementRatioKeyMakers) +// setSellerSettlementRatios sets the seller settlement fee ratios for a market. +func setSellerSettlementRatios(store sdk.KVStore, marketID uint32, ratios []exchange.FeeRatio) { + setAllFeeRatios(store, marketID, ratios, sellerSettlementRatioKeyMakers) } -// UpdateSellerSettlementRatios deletes all seller settlement ratio entries to delete then adds the ones to add. -func (k Keeper) UpdateSellerSettlementRatios(ctx sdk.Context, marketID uint32, toDelete, toAdd []exchange.FeeRatio) { - updateFeeRatios(k.getStore(ctx), marketID, toDelete, toAdd, sellerSettlementRatioKeyMakers) +// updateSellerSettlementRatios deletes all seller settlement ratio entries to delete then adds the ones to add. +func updateSellerSettlementRatios(store sdk.KVStore, marketID uint32, toDelete, toAdd []exchange.FeeRatio) { + updateFeeRatios(store, marketID, toDelete, toAdd, sellerSettlementRatioKeyMakers) } // CalculateSellerSettlementRatioFee calculates the seller settlement fee required for the given price. @@ -291,40 +313,50 @@ func (k Keeper) CalculateSellerSettlementRatioFee(ctx sdk.Context, marketID uint return rv, err } +// getBuyerSettlementFlatFees gets the buyer settlement flat fee options for a market. +func getBuyerSettlementFlatFees(store sdk.KVStore, marketID uint32) []sdk.Coin { + return getAllFlatFees(store, marketID, buyerSettlementFlatKeyMakers) +} + // GetBuyerSettlementFlatFees gets the buyer settlement flat fee options for a market. func (k Keeper) GetBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return getAllFlatFees(k.getStore(ctx), marketID, buyerSettlementFlatKeyMakers) + return getBuyerSettlementFlatFees(k.getStore(ctx), marketID) +} + +// setBuyerSettlementFlatFees sets the buyer settlement flat fees for a market. +func setBuyerSettlementFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin) { + setAllFlatFees(store, marketID, options, buyerSettlementFlatKeyMakers) } -// SetBuyerSettlementFlatFees sets the buyer settlement flat fees for a market. -func (k Keeper) SetBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32, options []sdk.Coin) { - setAllFlatFees(k.getStore(ctx), marketID, options, buyerSettlementFlatKeyMakers) +// updateBuyerSettlementFlatFees deletes all buyer settlement flat fees to delete then adds the ones to add. +func updateBuyerSettlementFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd []sdk.Coin) { + updateFlatFees(store, marketID, toDelete, toAdd, buyerSettlementFlatKeyMakers) } -// UpdateBuyerSettlementFlatFees deletes all buyer settlement flat fees to delete then adds the ones to add. -func (k Keeper) UpdateBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32, toDelete, toAdd []sdk.Coin) { - updateFlatFees(k.getStore(ctx), marketID, toDelete, toAdd, buyerSettlementFlatKeyMakers) +// getBuyerSettlementRatios gets the buyer settlement fee ratios for a market. +func getBuyerSettlementRatios(store sdk.KVStore, marketID uint32) []exchange.FeeRatio { + return getAllFeeRatios(store, marketID, buyerSettlementRatioKeyMakers) } // GetBuyerSettlementRatios gets the buyer settlement fee ratios for a market. func (k Keeper) GetBuyerSettlementRatios(ctx sdk.Context, marketID uint32) []exchange.FeeRatio { - return getAllFeeRatios(k.getStore(ctx), marketID, buyerSettlementRatioKeyMakers) + return getBuyerSettlementRatios(k.getStore(ctx), marketID) } -// SetBuyerSettlementRatios sets the buyer settlement fee ratios for a market. -func (k Keeper) SetBuyerSettlementRatios(ctx sdk.Context, marketID uint32, ratios []exchange.FeeRatio) { - setAllFeeRatios(k.getStore(ctx), marketID, ratios, buyerSettlementRatioKeyMakers) +// setBuyerSettlementRatios sets the buyer settlement fee ratios for a market. +func setBuyerSettlementRatios(store sdk.KVStore, marketID uint32, ratios []exchange.FeeRatio) { + setAllFeeRatios(store, marketID, ratios, buyerSettlementRatioKeyMakers) } -// UpdateBuyerSettlementRatios deletes all buyer settlement ratio entries to delete then adds the ones to add. -func (k Keeper) UpdateBuyerSettlementRatios(ctx sdk.Context, marketID uint32, toDelete, toAdd []exchange.FeeRatio) { - updateFeeRatios(k.getStore(ctx), marketID, toDelete, toAdd, buyerSettlementRatioKeyMakers) +// updateBuyerSettlementRatios deletes all buyer settlement ratio entries to delete then adds the ones to add. +func updateBuyerSettlementRatios(store sdk.KVStore, marketID uint32, toDelete, toAdd []exchange.FeeRatio) { + updateFeeRatios(store, marketID, toDelete, toAdd, buyerSettlementRatioKeyMakers) } -// GetBuyerSettlementFeeRatiosForPriceDenom gets all the buyer settlement fee ratios in a market that have the give price denom. -func (k Keeper) GetBuyerSettlementFeeRatiosForPriceDenom(ctx sdk.Context, marketID uint32, priceDenom string) []exchange.FeeRatio { +// getBuyerSettlementFeeRatiosForPriceDenom gets all the buyer settlement fee ratios in a market that have the give price denom. +func getBuyerSettlementFeeRatiosForPriceDenom(store sdk.KVStore, marketID uint32, priceDenom string) []exchange.FeeRatio { var ratios []exchange.FeeRatio - k.iterate(ctx, GetKeyPrefixMarketBuyerSettlementRatioForPriceDenom(marketID, priceDenom), func(key, value []byte) bool { + iterate(store, GetKeyPrefixMarketBuyerSettlementRatioForPriceDenom(marketID, priceDenom), func(key, value []byte) bool { feeDenom := string(key) priceAmount, feeAmount, err := ParseFeeRatioStoreValue(value) if err == nil { @@ -340,7 +372,7 @@ func (k Keeper) GetBuyerSettlementFeeRatiosForPriceDenom(ctx sdk.Context, market // CalculateBuyerSettlementRatioFeeOptions calculates the buyer settlement ratio fee options available for the given price. func (k Keeper) CalculateBuyerSettlementRatioFeeOptions(ctx sdk.Context, marketID uint32, price sdk.Coin) ([]sdk.Coin, error) { - ratios := k.GetBuyerSettlementFeeRatiosForPriceDenom(ctx, marketID, price.Denom) + ratios := getBuyerSettlementFeeRatiosForPriceDenom(k.getStore(ctx), marketID, price.Denom) if len(ratios) == 0 { return nil, fmt.Errorf("no buyer settlement fee ratios found with price denom %q", price.Denom) } @@ -477,14 +509,18 @@ func (k Keeper) ValidateBuyerSettlementFee(ctx sdk.Context, marketID uint32, pri return errors.Join(errs...) } -// IsMarketActive returns true if the provided market is accepting orders. -func (k Keeper) IsMarketActive(ctx sdk.Context, marketID uint32) bool { +// isMarketActive returns true if the provided market is accepting orders. +func isMarketActive(store sdk.KVStore, marketID uint32) bool { key := MakeKeyMarketInactive(marketID) - store := k.getStore(ctx) return !store.Has(key) } -// SetMarketActive sets whether the provided market is accepting orders. +// IsMarketActive returns true if the provided market is accepting orders. +func (k Keeper) IsMarketActive(ctx sdk.Context, marketID uint32) bool { + return isMarketActive(k.getStore(ctx), marketID) +} + +// setMarketActive sets whether the provided market is accepting orders. func setMarketActive(store sdk.KVStore, marketID uint32, active bool) { key := MakeKeyMarketInactive(marketID) if active { @@ -499,13 +535,17 @@ func (k Keeper) SetMarketActive(ctx sdk.Context, marketID uint32, active bool) { setMarketActive(k.getStore(ctx), marketID, active) } -// IsUserSettlementAllowed gets whether user-settlement is allowed for a market. -func (k Keeper) IsUserSettlementAllowed(ctx sdk.Context, marketID uint32) bool { +// isUserSettlementAllowed gets whether user-settlement is allowed for a market. +func isUserSettlementAllowed(store sdk.KVStore, marketID uint32) bool { key := MakeKeyMarketUserSettle(marketID) - store := k.getStore(ctx) return store.Has(key) } +// IsUserSettlementAllowed gets whether user-settlement is allowed for a market. +func (k Keeper) IsUserSettlementAllowed(ctx sdk.Context, marketID uint32) bool { + return isUserSettlementAllowed(k.getStore(ctx), marketID) +} + // SetUserSettlementAllowed sets whether user-settlement is allowed for a market. func setUserSettlementAllowed(store sdk.KVStore, marketID uint32, allowed bool) { key := MakeKeyMarketUserSettle(marketID) @@ -582,21 +622,26 @@ func (k Keeper) HasPermission(ctx sdk.Context, marketID uint32, addr sdk.AccAddr return hasPermission(k.getStore(ctx), marketID, addr, permission) } -// GetUserPermissions gets all permissions that have been granted to a user in a market. -func (k Keeper) GetUserPermissions(ctx sdk.Context, marketID uint32, addr sdk.AccAddress) []exchange.Permission { +// getUserPermissions gets all permissions that have been granted to a user in a market. +func getUserPermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress) []exchange.Permission { var rv []exchange.Permission - k.iterate(ctx, GetKeyPrefixMarketPermissionsForAddress(marketID, addr), func(key, _ []byte) bool { + iterate(store, GetKeyPrefixMarketPermissionsForAddress(marketID, addr), func(key, _ []byte) bool { rv = append(rv, exchange.Permission(key[0])) return false }) return rv } -// GetAccessGrants gets all the access grants for a market. -func (k Keeper) GetAccessGrants(ctx sdk.Context, marketID uint32) []exchange.AccessGrant { +// GetUserPermissions gets all permissions that have been granted to a user in a market. +func (k Keeper) GetUserPermissions(ctx sdk.Context, marketID uint32, addr sdk.AccAddress) []exchange.Permission { + return getUserPermissions(k.getStore(ctx), marketID, addr) +} + +// getAccessGrants gets all the access grants for a market. +func getAccessGrants(store sdk.KVStore, marketID uint32) []exchange.AccessGrant { var rv []exchange.AccessGrant var lastAG exchange.AccessGrant - k.iterate(ctx, GetKeyPrefixMarketPermissions(marketID), func(key, _ []byte) bool { + iterate(store, GetKeyPrefixMarketPermissions(marketID), func(key, _ []byte) bool { addr, perm, err := ParseKeySuffixMarketPermissions(key) if err == nil { if addr.String() != lastAG.Address { @@ -610,16 +655,15 @@ func (k Keeper) GetAccessGrants(ctx sdk.Context, marketID uint32) []exchange.Acc return rv } -// SetAccessGrants deletes all access grants on a market and sets just the ones provided. -func (k Keeper) SetAccessGrants(ctx sdk.Context, marketID uint32, grants []exchange.AccessGrant) { - store := k.getStore(ctx) +// setAccessGrants deletes all access grants on a market and sets just the ones provided. +func setAccessGrants(store sdk.KVStore, marketID uint32, grants []exchange.AccessGrant) { revokeAllMarketPermissions(store, marketID) setAllMarketPermissions(store, marketID, grants) } -// UpdatePermissions revokes all permissions from the provided revokeAll bech32 addresses, then revokes all permissions +// UpdateAccessGrants revokes all permissions from the provided revokeAll bech32 addresses, then revokes all permissions // in the toRevoke list, and lastly, grants all the permissions in toGrant. -func (k Keeper) UpdatePermissions(ctx sdk.Context, marketID uint32, revokeAll []string, toRevoke, toGrant []exchange.AccessGrant) { +func (k Keeper) UpdateAccessGrants(ctx sdk.Context, marketID uint32, revokeAll []string, toRevoke, toGrant []exchange.AccessGrant) { updatePermissions(k.getStore(ctx), marketID, revokeAll, toRevoke, toGrant) } @@ -634,40 +678,44 @@ func getReqAttr(store sdk.KVStore, marketID uint32, maker reqAttrKeyMaker) []str } // setReqAttr sets the required attributes for a market using the provided key maker. -func (k Keeper) setReqAttr(ctx sdk.Context, marketID uint32, reqAttrs []string, maker reqAttrKeyMaker) error { - store := k.getStore(ctx) +func setReqAttr(store sdk.KVStore, marketID uint32, reqAttrs []string, maker reqAttrKeyMaker) { key := maker(marketID) if len(reqAttrs) == 0 { store.Delete(key) } else { - reqAttrsNorm, err := k.NormalizeReqAttrs(ctx, reqAttrs) - if err != nil { - return err - } - value := []byte(strings.Join(reqAttrsNorm, string(RecordSeparator))) + value := []byte(strings.Join(reqAttrs, string(RecordSeparator))) store.Set(key, value) } - return nil +} + +// getReqAttrAsk gets the attributes required to create an ask order. +func getReqAttrAsk(store sdk.KVStore, marketID uint32) []string { + return getReqAttr(store, marketID, MakeKeyMarketReqAttrAsk) } // GetReqAttrAsk gets the attributes required to create an ask order. func (k Keeper) GetReqAttrAsk(ctx sdk.Context, marketID uint32) []string { - return getReqAttr(k.getStore(ctx), marketID, MakeKeyMarketReqAttrAsk) + return getReqAttrAsk(k.getStore(ctx), marketID) +} + +// setReqAttrAsk sets the attributes required to create an ask order. +func setReqAttrAsk(store sdk.KVStore, marketID uint32, reqAttrs []string) { + setReqAttr(store, marketID, reqAttrs, MakeKeyMarketReqAttrAsk) } -// SetReqAttrAsk sets the attributes required to create an ask order. -func (k Keeper) SetReqAttrAsk(ctx sdk.Context, marketID uint32, reqAttrs []string) error { - return k.setReqAttr(ctx, marketID, reqAttrs, MakeKeyMarketReqAttrAsk) +// getReqAttrBid gets the attributes required to create a bid order. +func getReqAttrBid(store sdk.KVStore, marketID uint32) []string { + return getReqAttr(store, marketID, MakeKeyMarketReqAttrBid) } // GetReqAttrBid gets the attributes required to create a bid order. func (k Keeper) GetReqAttrBid(ctx sdk.Context, marketID uint32) []string { - return getReqAttr(k.getStore(ctx), marketID, MakeKeyMarketReqAttrBid) + return getReqAttrBid(k.getStore(ctx), marketID) } -// SetReqAttrBid sets the attributes required to create a bid order. -func (k Keeper) SetReqAttrBid(ctx sdk.Context, marketID uint32, reqAttrs []string) error { - return k.setReqAttr(ctx, marketID, reqAttrs, MakeKeyMarketReqAttrBid) +// setReqAttrBid sets the attributes required to create a bid order. +func setReqAttrBid(store sdk.KVStore, marketID uint32, reqAttrs []string) { + setReqAttr(store, marketID, reqAttrs, MakeKeyMarketReqAttrBid) } // getLastAutoMarketID gets the last auto-selected market id. @@ -699,9 +747,8 @@ func (k Keeper) NextMarketID(ctx sdk.Context) uint32 { return marketID } -// SetMarketKnown sets the known market id indicator in the store. -func (k Keeper) SetMarketKnown(ctx sdk.Context, marketID uint32) { - store := k.getStore(ctx) +// setMarketKnown sets the known market id indicator in the store. +func setMarketKnown(store sdk.KVStore, marketID uint32) { key := MakeKeyKnownMarketID(marketID) store.Set(key, nil) } @@ -739,6 +786,13 @@ func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (marketID return 0, fmt.Errorf("market id %d %s already exists", marketID, marketAddr) } + reqAttrCreateAsk, errAsk := k.NormalizeReqAttrs(ctx, market.ReqAttrCreateAsk) + reqAttrCreateBid, errBid := k.NormalizeReqAttrs(ctx, market.ReqAttrCreateAsk) + err = errors.Join(errAsk, errBid) + if err != nil { + return 0, err + } + marketAcc := &exchange.MarketAccount{ BaseAccount: &authtypes.BaseAccount{Address: marketAddr.String()}, MarketId: marketID, @@ -747,18 +801,19 @@ func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (marketID k.accountKeeper.NewAccount(ctx, marketAcc) k.accountKeeper.SetAccount(ctx, marketAcc) - k.SetMarketKnown(ctx, marketID) - k.SetCreateAskFlatFees(ctx, marketID, market.FeeCreateAskFlat) - k.SetCreateBidFlatFees(ctx, marketID, market.FeeCreateBidFlat) - k.SetSellerSettlementFlatFees(ctx, marketID, market.FeeSellerSettlementFlat) - k.SetSellerSettlementRatios(ctx, marketID, market.FeeSellerSettlementRatios) - k.SetBuyerSettlementFlatFees(ctx, marketID, market.FeeBuyerSettlementFlat) - k.SetBuyerSettlementRatios(ctx, marketID, market.FeeBuyerSettlementRatios) - k.SetMarketActive(ctx, marketID, market.AcceptingOrders) - k.SetUserSettlementAllowed(ctx, marketID, market.AllowUserSettlement) - k.SetAccessGrants(ctx, marketID, market.AccessGrants) - err = errors.Join(err, k.SetReqAttrAsk(ctx, marketID, market.ReqAttrCreateAsk)) - err = errors.Join(err, k.SetReqAttrBid(ctx, marketID, market.ReqAttrCreateBid)) + store := k.getStore(ctx) + setMarketKnown(store, marketID) + setCreateAskFlatFees(store, marketID, market.FeeCreateAskFlat) + setCreateBidFlatFees(store, marketID, market.FeeCreateBidFlat) + setSellerSettlementFlatFees(store, marketID, market.FeeSellerSettlementFlat) + setSellerSettlementRatios(store, marketID, market.FeeSellerSettlementRatios) + setBuyerSettlementFlatFees(store, marketID, market.FeeBuyerSettlementFlat) + setBuyerSettlementRatios(store, marketID, market.FeeBuyerSettlementRatios) + setMarketActive(store, marketID, market.AcceptingOrders) + setUserSettlementAllowed(store, marketID, market.AllowUserSettlement) + setAccessGrants(store, marketID, market.AccessGrants) + setReqAttrAsk(store, marketID, reqAttrCreateAsk) + setReqAttrBid(store, marketID, reqAttrCreateBid) return marketID, err } @@ -776,19 +831,31 @@ func (k Keeper) GetMarket(ctx sdk.Context, marketID uint32) *exchange.Market { return nil } + store := k.getStore(ctx) market := &exchange.Market{MarketId: marketID} market.MarketDetails = marketAcc.MarketDetails - market.FeeCreateAskFlat = k.GetCreateAskFlatFees(ctx, marketID) - market.FeeCreateBidFlat = k.GetCreateBidFlatFees(ctx, marketID) - market.FeeSellerSettlementFlat = k.GetSellerSettlementFlatFees(ctx, marketID) - market.FeeSellerSettlementRatios = k.GetSellerSettlementRatios(ctx, marketID) - market.FeeBuyerSettlementFlat = k.GetBuyerSettlementFlatFees(ctx, marketID) - market.FeeBuyerSettlementRatios = k.GetBuyerSettlementRatios(ctx, marketID) - market.AcceptingOrders = k.IsMarketActive(ctx, marketID) - market.AllowUserSettlement = k.IsUserSettlementAllowed(ctx, marketID) - market.AccessGrants = k.GetAccessGrants(ctx, marketID) - market.ReqAttrCreateAsk = k.GetReqAttrAsk(ctx, marketID) - market.ReqAttrCreateBid = k.GetReqAttrBid(ctx, marketID) + market.FeeCreateAskFlat = getCreateAskFlatFees(store, marketID) + market.FeeCreateBidFlat = getCreateBidFlatFees(store, marketID) + market.FeeSellerSettlementFlat = getSellerSettlementFlatFees(store, marketID) + market.FeeSellerSettlementRatios = getSellerSettlementRatios(store, marketID) + market.FeeBuyerSettlementFlat = getBuyerSettlementFlatFees(store, marketID) + market.FeeBuyerSettlementRatios = getBuyerSettlementRatios(store, marketID) + market.AcceptingOrders = isMarketActive(store, marketID) + market.AllowUserSettlement = isUserSettlementAllowed(store, marketID) + market.AccessGrants = getAccessGrants(store, marketID) + market.ReqAttrCreateAsk = getReqAttrAsk(store, marketID) + market.ReqAttrCreateBid = getReqAttrBid(store, marketID) return market } + +// UpdateFees updates all the fees as provided in the MsgGovManageFeesRequest. +func (k Keeper) UpdateFees(ctx sdk.Context, msg *exchange.MsgGovManageFeesRequest) { + store := k.getStore(ctx) + updateCreateAskFlatFees(store, msg.MarketId, msg.RemoveFeeCreateAskFlat, msg.AddFeeCreateAskFlat) + updateCreateBidFlatFees(store, msg.MarketId, msg.RemoveFeeCreateBidFlat, msg.AddFeeCreateBidFlat) + updateSellerSettlementFlatFees(store, msg.MarketId, msg.RemoveFeeSellerSettlementFlat, msg.AddFeeSellerSettlementFlat) + updateSellerSettlementRatios(store, msg.MarketId, msg.RemoveFeeSellerSettlementRatios, msg.AddFeeSellerSettlementRatios) + updateBuyerSettlementFlatFees(store, msg.MarketId, msg.RemoveFeeBuyerSettlementFlat, msg.AddFeeBuyerSettlementFlat) + updateBuyerSettlementRatios(store, msg.MarketId, msg.RemoveFeeBuyerSettlementRatios, msg.AddFeeBuyerSettlementRatios) +} diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index f112a16ca6..c04d414f14 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -121,12 +121,7 @@ func (k MsgServer) GovManageFees(goCtx context.Context, msg *exchange.MsgGovMana } ctx := sdk.UnwrapSDKContext(goCtx) - k.UpdateCreateAskFlatFees(ctx, msg.MarketId, msg.RemoveFeeCreateAskFlat, msg.AddFeeCreateAskFlat) - k.UpdateCreateBidFlatFees(ctx, msg.MarketId, msg.RemoveFeeCreateBidFlat, msg.AddFeeCreateBidFlat) - k.UpdateSellerSettlementFlatFees(ctx, msg.MarketId, msg.RemoveFeeSellerSettlementFlat, msg.AddFeeSellerSettlementFlat) - k.UpdateSellerSettlementRatios(ctx, msg.MarketId, msg.RemoveFeeSellerSettlementRatios, msg.AddFeeSellerSettlementRatios) - k.UpdateBuyerSettlementFlatFees(ctx, msg.MarketId, msg.RemoveFeeBuyerSettlementFlat, msg.AddFeeBuyerSettlementFlat) - k.UpdateBuyerSettlementRatios(ctx, msg.MarketId, msg.RemoveFeeBuyerSettlementRatios, msg.AddFeeBuyerSettlementRatios) + k.UpdateFees(ctx, msg) return &exchange.MsgGovManageFeesResponse{}, nil } From 58420629769a1ea250b3eb5d47df04105d1ab80a Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 15:54:52 -0600 Subject: [PATCH 084/309] [1658]: Add some empty suffix/value tests to ParseKeySuffixSettlementRatio ParseFeeRatioStoreValue tests. --- x/exchange/keeper/keys_test.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index b4111d56fb..d5bf7435ce 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -816,7 +816,16 @@ func TestParseKeySuffixSettlementRatio(t *testing.T) { suffix: []byte{keeper.RecordSeparator, 'b', keeper.RecordSeparator}, expErr: "ratio key suffix \"\\x1eb\\x1e\" has 3 parts, expected 2", }, - // TODO[1658]: Test cases for ParseKeySuffixSettlementRatio empty suffix + { + name: "nil suffix", + suffix: nil, + expErr: "ratio key suffix is empty", + }, + { + name: "empty suffix", + suffix: []byte{}, + expErr: "ratio key suffix is empty", + }, } for _, tc := range tests { @@ -1025,7 +1034,16 @@ func TestParseFeeRatioStoreValue(t *testing.T) { value: []byte{rs, '1', '0', '0', rs}, expErr: "ratio value \"\\x1e100\\x1e\" has 3 parts, expected 2", }, - // TODO[1658]: Test cases for ParseFeeRatioStoreValue empty value + { + name: "nil value", + value: nil, + expErr: "ratio value is empty", + }, + { + name: "empty value", + value: []byte{}, + expErr: "ratio value is empty", + }, } for _, tc := range tests { From 43adb831db74af76b91d56fa7fb3a19db6e1c33a Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 16:03:34 -0600 Subject: [PATCH 085/309] [1659]: Refactor ParseFeeRatioStoreValue due to nakedret linter error. --- x/exchange/keeper/keys.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 1ab277074e..28016c96d9 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -337,18 +337,8 @@ func GetFeeRatioStoreValue(ratio exchange.FeeRatio) []byte { // Input is expected to have the format where both amounts are strings (of digits). // E.g. "100\\1E3" (for a price amount of 100, and fee amount of 3). func ParseFeeRatioStoreValue(value []byte) (priceAmount, feeAmount sdkmath.Int, err error) { - defer func() { - // The zero-value of sdkmath.Int{} will panic if anything tries to do anything with it (e.g. convert it to a string). - // Additionally, if there was an error, we don't want either of them to have any left-over values. - if err != nil { - priceAmount = sdkmath.ZeroInt() - feeAmount = sdkmath.ZeroInt() - } - }() - if len(value) == 0 { - err = errors.New("ratio value is empty") - return + return sdkmath.ZeroInt(), sdkmath.ZeroInt(), errors.New("ratio value is empty") } parts := bytes.Split(value, []byte{RecordSeparator}) @@ -366,7 +356,14 @@ func ParseFeeRatioStoreValue(value []byte) (priceAmount, feeAmount sdkmath.Int, err = fmt.Errorf("ratio value %q has %d parts, expected 2", value, len(parts)) } - return + if err != nil { + // The zero-value of sdkmath.Int{} will panic if anything tries to do anything with it (e.g. convert it to a string). + // Additionally, if there was an error, we don't want either of them to have any left-over values. + priceAmount = sdkmath.ZeroInt() + feeAmount = sdkmath.ZeroInt() + } + + return priceAmount, feeAmount, err } // marketKeyPrefixSellerSettlementRatio creates the key prefix for a market's seller settlement ratios with extra capacity for the rest. From 18684c5b67716d1fcda70240727739030a6f281f Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 5 Sep 2023 17:18:29 -0600 Subject: [PATCH 086/309] [1658]: Take care of the keys unit test TODOs I recently added. --- x/exchange/keeper/export_test.go | 7 + x/exchange/keeper/keys.go | 7 +- x/exchange/keeper/keys_test.go | 444 ++++++++++++++++++++++++++++++- 3 files changed, 449 insertions(+), 9 deletions(-) create mode 100644 x/exchange/keeper/export_test.go diff --git a/x/exchange/keeper/export_test.go b/x/exchange/keeper/export_test.go new file mode 100644 index 0000000000..c510f05659 --- /dev/null +++ b/x/exchange/keeper/export_test.go @@ -0,0 +1,7 @@ +package keeper + +// This file is in the keeper package (not keeper_test) so that it can expose +// some private keeper stuff for unit testing. + +// ParseLengthPrefixedAddr is a test-only exposure of parseLengthPrefixedAddr. +var ParseLengthPrefixedAddr = parseLengthPrefixedAddr diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 28016c96d9..f44c9a5981 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -167,10 +167,10 @@ func parseLengthPrefixedAddr(key []byte) (sdk.AccAddress, []byte, error) { } l := int(key[0]) if l == 0 { - return nil, nil, errors.New("length byte is 0") + return nil, nil, errors.New("length byte is zero") } if len(key) <= l { - return nil, nil, fmt.Errorf("length byte is %d, but slice length has %d left", l, len(key)-1) + return nil, nil, fmt.Errorf("length byte is %d, but slice only has %d left", l, len(key)-1) } if len(key) == l+1 { return key[1:], nil, nil @@ -506,6 +506,9 @@ func MakeKeyMarketReqAttrBid(marketID uint32) []byte { // ParseReqAttrStoreValue parses a required attribute store value into it's string slice. func ParseReqAttrStoreValue(value []byte) []string { + if len(value) == 0 { + return nil + } return strings.Split(string(value), string(RecordSeparator)) } diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index d5bf7435ce..8f85d9ad27 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -126,7 +126,79 @@ func TestKeyTypeUniqueness(t *testing.T) { } } -// TODO[1658]: func TestParseLengthPrefixedAddr(t *testing.T) +func TestParseLengthPrefixedAddr(t *testing.T) { + tests := []struct { + name string + key []byte + expAddr sdk.AccAddress + expRest []byte + expErr string + }{ + { + name: "nil slice", + key: nil, + expErr: "slice is empty", + }, + { + name: "empty slice", + key: []byte{}, + expErr: "slice is empty", + }, + { + name: "first byte is zero", + key: []byte{0, 1, 2, 3}, + expErr: "length byte is zero", + }, + { + name: "too short for length byte 1", + key: []byte{1}, + expErr: "length byte is 1, but slice only has 0 left", + }, + { + name: "really too short for length byte 10", + key: []byte{10, 1}, + expErr: "length byte is 10, but slice only has 1 left", + }, + { + name: "barely too short for length byte 10", + key: []byte{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + expErr: "length byte is 10, but slice only has 9 left", + }, + { + name: "length 5 with nothing left", + key: []byte{5, 1, 2, 3, 4, 5}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5}, + expRest: nil, + }, + { + name: "length 5 with 1 byte left", + key: []byte{5, 1, 2, 3, 4, 5, 11}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5}, + expRest: []byte{11}, + }, + { + name: "length 5 with 5 bytes left", + key: []byte{5, 1, 2, 3, 4, 5, 11, 22, 33, 44, 55}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5}, + expRest: []byte{11, 22, 33, 44, 55}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var addr sdk.AccAddress + var rest []byte + var err error + testFunc := func() { + addr, rest, err = keeper.ParseLengthPrefixedAddr(tc.key) + } + require.NotPanics(t, testFunc, "parseLengthPrefixedAddr(%v)", tc.key) + assertions.AssertErrorValue(t, err, tc.expErr, "parseLengthPrefixedAddr(%v) error", tc.key) + assert.Equal(t, tc.expAddr, addr, "parseLengthPrefixedAddr(%v) address", tc.key) + assert.Equal(t, tc.expRest, rest, "parseLengthPrefixedAddr(%v) the rest", tc.key) + }) + } +} func TestGetKeyPrefixParamsSplit(t *testing.T) { ktc := keyTestCase{ @@ -177,13 +249,179 @@ func TestMakeKeyParamsSplit(t *testing.T) { } } -// TODO[1658]: func TestMakeKeyLastMarketID(t *testing.T) +func TestMakeKeyLastMarketID(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyLastMarketID() + }, + expected: []byte{keeper.KeyTypeLastMarketID}, + } + checkKey(t, ktc, "MakeKeyLastMarketID") +} + +func TestGetKeyPrefixKnownMarketID(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.GetKeyPrefixKnownMarketID() + }, + expected: []byte{keeper.KeyTypeKnownMarketID}, + } + checkKey(t, ktc, "GetKeyPrefixKnownMarketID") +} + +func TestMakeKeyKnownMarketID(t *testing.T) { + tests := []struct { + name string + marketID uint32 + expected []byte + }{ + { + name: "market id 0", + marketID: 0, + expected: []byte{keeper.KeyTypeKnownMarketID, 0, 0, 0, 0}, + }, + { + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeKnownMarketID, 0, 0, 0, 1}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeKnownMarketID, 0, 0, 0, 255}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeKnownMarketID, 0, 0, 1, 0}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeKnownMarketID, 0, 1, 0, 0}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeKnownMarketID, 1, 0, 0, 0}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeKnownMarketID, 1, 1, 1, 1}, + }, + { + name: "market id 33,686,018", + marketID: 33_686_018, + expected: []byte{keeper.KeyTypeKnownMarketID, 2, 2, 2, 2}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeKnownMarketID, 255, 255, 255, 255}, + }, + } -// TODO[1658]: func TestGetKeyPrefixKnownMarketID(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyKnownMarketID(tc.marketID) + }, + expected: tc.expected, + expPanic: "", + expPrefixes: []expectedPrefix{ + { + name: "GetKeyPrefixKnownMarketID", + value: keeper.GetKeyPrefixKnownMarketID(), + }, + }, + } + checkKey(t, ktc, "MakeKeyKnownMarketID(%d)", tc.marketID) + }) + } +} -// TODO[1658]: func TestMakeKeyKnownMarketID(t *testing.T) +func TestParseKeySuffixKnownMarketID(t *testing.T) { + tests := []struct { + name string + suffix []byte + exp uint32 + expPanic string + }{ + { + name: "nil suffix", + suffix: nil, + expPanic: "runtime error: index out of range [3] with length 0", + }, + { + name: "empty suffix", + suffix: []byte{}, + expPanic: "runtime error: index out of range [3] with length 0", + }, + { + name: "three byte suffix", + suffix: []byte{1, 2, 3}, + expPanic: "runtime error: index out of range [3] with length 3", + }, + { + name: "market id 16,909,060 but with extra byte", + suffix: []byte{1, 2, 3, 4, 5}, + exp: 16_909_060, + }, + { + name: "market id 16,909,060", + suffix: []byte{1, 2, 3, 4}, + exp: 16_909_060, + }, + { + name: "market id zero", + suffix: []byte{0, 0, 0, 0}, + exp: 0, + }, + { + name: "market id 1", + suffix: []byte{0, 0, 0, 1}, + exp: 1, + }, + { + name: "market id 255", + suffix: []byte{0, 0, 0, 255}, + exp: 255, + }, + { + name: "market id 256", + suffix: []byte{0, 0, 1, 0}, + exp: 256, + }, + { + name: "market id 65_536", + suffix: []byte{0, 1, 0, 0}, + exp: 65_536, + }, + { + name: "market id 16,777,216", + suffix: []byte{1, 0, 0, 0}, + exp: 16_777_216, + }, + { + name: "market id 4,294,967,295", + suffix: []byte{255, 255, 255, 255}, + exp: 4_294_967_295, + }, + } -// TODO[1658]: func TestParseKeySuffixKnownMarketID(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var marketID uint32 + testFunc := func() { + marketID = keeper.ParseKeySuffixKnownMarketID(tc.suffix) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "ParseKeySuffixKnownMarketID") + assert.Equal(t, tc.exp, marketID, "ParseKeySuffixKnownMarketID result") + }) + } +} func TestGetKeyPrefixMarket(t *testing.T) { tests := []struct { @@ -2019,7 +2257,142 @@ func TestMakeKeyMarketPermissions(t *testing.T) { } } -// TODO[1658]: func TestParseKeySuffixMarketPermissions(t *testing.T) +func TestParseKeySuffixMarketPermissions(t *testing.T) { + tests := []struct { + name string + suffix []byte + expAddr sdk.AccAddress + expPerm exchange.Permission + expErr string + }{ + { + name: "nil suffix", + suffix: nil, + expErr: "cannot parse address from market permissions key: slice is empty", + }, + { + name: "empty suffix", + suffix: []byte{}, + expErr: "cannot parse address from market permissions key: slice is empty", + }, + { + name: "byte length too short", + suffix: []byte{5, 1, 2}, + expErr: "cannot parse address from market permissions key: length byte is 5, but slice only has 2 left", + }, + { + name: "no permission byte", + suffix: []byte{5, 1, 2, 3, 4, 5}, + expErr: "cannot parse market permissions key: found 0 bytes after address, expected 1", + }, + { + name: "two bytes after addr", + suffix: []byte{5, 1, 2, 3, 4, 5, 11, 22}, + expErr: "cannot parse market permissions key: found 2 bytes after address, expected 1", + }, + { + name: "5 byte addr settle", + suffix: []byte{5, 1, 2, 3, 4, 5, byte(exchange.Permission_settle)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5}, + expPerm: exchange.Permission_settle, + }, + { + name: "5 byte addr cancel", + suffix: []byte{5, 1, 2, 3, 4, 5, byte(exchange.Permission_cancel)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5}, + expPerm: exchange.Permission_cancel, + }, + { + name: "5 byte addr withdraw", + suffix: []byte{5, 1, 2, 3, 4, 5, byte(exchange.Permission_withdraw)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5}, + expPerm: exchange.Permission_withdraw, + }, + { + name: "5 byte addr update", + suffix: []byte{5, 1, 2, 3, 4, 5, byte(exchange.Permission_update)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5}, + expPerm: exchange.Permission_update, + }, + { + name: "5 byte addr permissions", + suffix: []byte{5, 1, 2, 3, 4, 5, byte(exchange.Permission_permissions)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5}, + expPerm: exchange.Permission_permissions, + }, + { + name: "5 byte addr attributes", + suffix: []byte{5, 1, 2, 3, 4, 5, byte(exchange.Permission_attributes)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5}, + expPerm: exchange.Permission_attributes, + }, + { + name: "5 byte addr unknown permission", + suffix: []byte{5, 1, 2, 3, 4, 5, 88}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5}, + expPerm: exchange.Permission(88), + }, + { + name: "20 byte addr settle", + suffix: []byte{20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, byte(exchange.Permission_settle)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, + expPerm: exchange.Permission_settle, + }, + { + name: "20 byte addr cancel", + suffix: []byte{20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, byte(exchange.Permission_cancel)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, + expPerm: exchange.Permission_cancel, + }, + { + name: "20 byte addr withdraw", + suffix: []byte{20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, byte(exchange.Permission_withdraw)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, + expPerm: exchange.Permission_withdraw, + }, + { + name: "20 byte addr update", + suffix: []byte{20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, byte(exchange.Permission_update)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, + expPerm: exchange.Permission_update, + }, + { + name: "20 byte addr permissions", + suffix: []byte{20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, byte(exchange.Permission_permissions)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, + expPerm: exchange.Permission_permissions, + }, + { + name: "20 byte addr attributes", + suffix: []byte{20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, byte(exchange.Permission_attributes)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, + expPerm: exchange.Permission_attributes, + }, + { + name: "32 byte addr settle", + suffix: []byte{32, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, byte(exchange.Permission_settle)}, + expAddr: sdk.AccAddress{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}, + expPerm: exchange.Permission_settle, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var addr sdk.AccAddress + var perm exchange.Permission + var err error + testFunc := func() { + addr, perm, err = keeper.ParseKeySuffixMarketPermissions(tc.suffix) + } + require.NotPanics(t, testFunc, "ParseKeySuffixMarketPermissions") + assertions.AssertErrorValue(t, err, tc.expErr, "ParseKeySuffixMarketPermissions error") + assert.Equal(t, tc.expAddr, addr, "ParseKeySuffixMarketPermissions address") + assert.Equal(t, tc.expPerm, perm, "ParseKeySuffixMarketPermissions permission") + }) + } +} func TestMakeKeyMarketReqAttrAsk(t *testing.T) { marketTypeByte := keeper.MarketKeyTypeReqAttr @@ -2155,7 +2528,64 @@ func TestMakeKeyMarketReqAttrBid(t *testing.T) { } } -// TODO[1658]: func TestParseReqAttrStoreValue(t *testing.T) +func TestParseReqAttrStoreValue(t *testing.T) { + rs := keeper.RecordSeparator + + tests := []struct { + name string + value []byte + exp []string + }{ + { + name: "nil value", + value: nil, + exp: nil, + }, + { + name: "empty value", + value: nil, + exp: nil, + }, + { + name: "one long attribute", + value: []byte("one.long.really-long.super-long.attribute"), + exp: []string{"one.long.really-long.super-long.attribute"}, + }, + { + name: "two attributes", + value: []byte{'a', 't', 't', 'r', '1', rs, 's', 'e', 'c', 'o', 'n', 'd'}, + exp: []string{"attr1", "second"}, + }, + { + name: "five attributes", + value: bytes.Join([][]byte{ + []byte("this.is.attr.one"), + []byte("a.second.appears"), + []byte("thrice.is.twice.as.nice"), + []byte("golfers.delight"), + []byte("i.have.nothing.for.fifth"), + }, []byte{rs}), + exp: []string{ + "this.is.attr.one", + "a.second.appears", + "thrice.is.twice.as.nice", + "golfers.delight", + "i.have.nothing.for.fifth", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual []string + testFunc := func() { + actual = keeper.ParseReqAttrStoreValue(tc.value) + } + require.NotPanics(t, testFunc, "ParseReqAttrStoreValue") + assert.Equal(t, tc.exp, actual, "ParseReqAttrStoreValue result") + }) + } +} func TestGetKeyPrefixOrder(t *testing.T) { tests := []struct { From c4960e7045b4ee89826ada5fbef73fb81fd7d041 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 6 Sep 2023 11:00:38 -0600 Subject: [PATCH 087/309] [1658]: Create IsBidOrder and IsAskOrder functions. --- x/exchange/orders.go | 11 ++++++ x/exchange/orders_test.go | 80 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 320cee3523..79c8c161d8 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -82,6 +82,17 @@ func (o Order) GetMarketID() uint32 { } } +// IsAskOrder returns true if this order is an ask order. +func (o Order) IsAskOrder() bool { + return o.GetAskOrder() != nil +} + +// IsBidOrder returns true if this order is a bid order. +func (o Order) IsBidOrder() bool { + return o.GetBidOrder() != nil +} + +// Validate returns an error if anything in this order is invalid. func (o Order) Validate() error { if o.OrderId == 0 { return errors.New("invalid order id: must not be zero") diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 226210f96c..2e9ddce199 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -280,6 +280,86 @@ func TestOrder_OrderTypeByte(t *testing.T) { } } +func TestOrder_IsAskOrder(t *testing.T) { + tests := []struct { + name string + order *Order + exp bool + }{ + { + name: "nil inside order", + order: NewOrder(1), + exp: false, + }, + { + name: "ask order", + order: NewOrder(2).WithAsk(&AskOrder{}), + exp: true, + }, + { + name: "bid order", + order: NewOrder(3).WithBid(&BidOrder{}), + exp: false, + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + exp: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.order.IsAskOrder() + } + require.NotPanics(t, testFunc, "IsAskOrder") + assert.Equal(t, tc.exp, actual, "IsAskOrder result") + }) + } +} + +func TestOrder_IsBidOrder(t *testing.T) { + tests := []struct { + name string + order *Order + exp bool + }{ + { + name: "nil inside order", + order: NewOrder(1), + exp: false, + }, + { + name: "ask order", + order: NewOrder(2).WithAsk(&AskOrder{}), + exp: false, + }, + { + name: "bid order", + order: NewOrder(3).WithBid(&BidOrder{}), + exp: true, + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + exp: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.order.IsBidOrder() + } + require.NotPanics(t, testFunc, "IsBidOrder") + assert.Equal(t, tc.exp, actual, "IsBidOrder result") + }) + } +} + func TestOrder_GetMarketID(t *testing.T) { tests := []struct { name string From a994bd09206945e37b915de66238cd65ce29d950 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 6 Sep 2023 11:14:47 -0600 Subject: [PATCH 088/309] [1658]: Refactor order state store plans; move the type byte into the value. --- x/exchange/keeper/keys.go | 27 ++--- x/exchange/keeper/keys_test.go | 177 +++------------------------------ 2 files changed, 28 insertions(+), 176 deletions(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index f44c9a5981..74ff08ca4d 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -49,14 +49,14 @@ import ( // // Orders: // Order entries all have the following general format: -// 0x02 | (8 bytes) | => protobuf encoding of specific order type. +// 0x02 | (8 bytes) => | protobuf encoding of specific order type. // The is a uint64 in big-endian order (8 bytes). // values: // Ask: 0x00 // Bid: 0x01 // So, the specific entry formats look like this: -// Ask Orders: 0x02 | (8 bytes) | 0x00 => protobuf(AskOrder) -// Bid Orders: 0x02 | (8 bytes) | 0x01 => protobuf(BidOrder) +// Ask Orders: 0x02 | (8 bytes) => 0x00 | protobuf(AskOrder) +// Bid Orders: 0x02 | (8 bytes) => 0x01 | protobuf(BidOrder) // // A market to order index is maintained with the following format: // 0x03 | (4 bytes) | (8 bytes) => nil @@ -512,20 +512,21 @@ func ParseReqAttrStoreValue(value []byte) []string { return strings.Split(string(value), string(RecordSeparator)) } -// keyPrefixOrder creates the key prefix for orders with the provide extra capacity for additional elements. -func keyPrefixOrder(orderID uint64, extraCap int) []byte { - return prepKey(KeyTypeOrder, uint64Bz(orderID), extraCap) +// keyPrefixOrder creates the key prefix for orders with the provided extra capacity for additional elements. +func keyPrefixOrder(extraCap int) []byte { + return prepKey(KeyTypeOrder, nil, extraCap) } -// GetKeyPrefixOrder creates the key prefix for the given order id. -func GetKeyPrefixOrder(orderID uint64) []byte { - return keyPrefixOrder(orderID, 0) +// GetKeyPrefixOrder gets the key prefix for all orders. +func GetKeyPrefixOrder() []byte { + return keyPrefixOrder(0) } -// MakeKeyOrder makes the key to use for the given order. -func MakeKeyOrder(order exchange.Order) []byte { - rv := keyPrefixOrder(order.GetOrderId(), 1) - rv = append(rv, order.GetOrderTypeByte()) +// MakeKeyOrder creates the key to use for an order ID. +func MakeKeyOrder(orderID uint64) []byte { + suffix := uint64Bz(orderID) + rv := keyPrefixOrder(len(suffix)) + rv = append(rv, suffix...) return rv } diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 8f85d9ad27..f3b60cc6ac 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -2588,6 +2588,16 @@ func TestParseReqAttrStoreValue(t *testing.T) { } func TestGetKeyPrefixOrder(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.GetKeyPrefixOrder() + }, + expected: []byte{keeper.KeyTypeOrder}, + } + checkKey(t, ktc, "GetKeyPrefixOrder") +} + +func TestMakeKeyOrder(t *testing.T) { tests := []struct { name string orderID uint64 @@ -2659,171 +2669,12 @@ func TestGetKeyPrefixOrder(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.GetKeyPrefixOrder(tc.orderID) + return keeper.MakeKeyOrder(tc.orderID) }, - expected: tc.expected, - } - checkKey(t, ktc, "GetKeyPrefixOrder(%d)", tc.orderID) - }) - } -} - -func TestMakeKeyOrder(t *testing.T) { - askOrder := func(orderID uint64) exchange.Order { - return *exchange.NewOrder(orderID).WithAsk(&exchange.AskOrder{}) - } - bidOrder := func(orderID uint64) exchange.Order { - return *exchange.NewOrder(orderID).WithBid(&exchange.BidOrder{}) - } - - tests := []struct { - name string - order exchange.Order - expected []byte - expPanic string - }{ - { - name: "order id 0 ask", - order: askOrder(0), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 0, 0, exchange.OrderTypeByteAsk}, - }, - { - name: "order id 0 bid", - order: bidOrder(0), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 0, 0, exchange.OrderTypeByteBid}, - }, - { - name: "order id 1 ask", - order: askOrder(1), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 0, 1, exchange.OrderTypeByteAsk}, - }, - { - name: "order id 1 bid", - order: bidOrder(1), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 0, 1, exchange.OrderTypeByteBid}, - }, - { - name: "order id 256 ask", - order: askOrder(256), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 1, 0, exchange.OrderTypeByteAsk}, - }, - { - name: "order id 256 bid", - order: bidOrder(256), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 0, 1, 0, exchange.OrderTypeByteBid}, - }, - { - name: "order id 65,536 ask", - order: askOrder(65_536), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 1, 0, 0, exchange.OrderTypeByteAsk}, - }, - { - name: "order id 65,536 bid", - order: bidOrder(65_536), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 0, 1, 0, 0, exchange.OrderTypeByteBid}, - }, - { - name: "order id 16,777,216 ask", - order: askOrder(16_777_216), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 1, 0, 0, 0, exchange.OrderTypeByteAsk}, - }, - { - name: "order id 16,777,216 bid", - order: bidOrder(16_777_216), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 0, 1, 0, 0, 0, exchange.OrderTypeByteBid}, - }, - { - name: "order id 4,294,967,296 ask", - order: askOrder(4_294_967_296), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 1, 0, 0, 0, 0, exchange.OrderTypeByteAsk}, - }, - { - name: "order id 4,294,967,296 bid", - order: bidOrder(4_294_967_296), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 0, 1, 0, 0, 0, 0, exchange.OrderTypeByteBid}, - }, - { - name: "order id 1,099,511,627,776 ask", - order: askOrder(1_099_511_627_776), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 1, 0, 0, 0, 0, 0, exchange.OrderTypeByteAsk}, - }, - { - name: "order id 1,099,511,627,776 bid", - order: bidOrder(1_099_511_627_776), - expected: []byte{keeper.KeyTypeOrder, 0, 0, 1, 0, 0, 0, 0, 0, exchange.OrderTypeByteBid}, - }, - { - name: "order id 281,474,976,710,656 ask", - order: askOrder(281_474_976_710_656), - expected: []byte{keeper.KeyTypeOrder, 0, 1, 0, 0, 0, 0, 0, 0, exchange.OrderTypeByteAsk}, - }, - { - name: "order id 281,474,976,710,656 bid", - order: bidOrder(281_474_976_710_656), - expected: []byte{keeper.KeyTypeOrder, 0, 1, 0, 0, 0, 0, 0, 0, exchange.OrderTypeByteBid}, - }, - { - name: "order id 72,057,594,037,927,936 ask", - order: askOrder(72_057_594_037_927_936), - expected: []byte{keeper.KeyTypeOrder, 1, 0, 0, 0, 0, 0, 0, 0, exchange.OrderTypeByteAsk}, - }, - { - name: "order id 72,057,594,037,927,936 bid", - order: bidOrder(72_057_594_037_927_936), - expected: []byte{keeper.KeyTypeOrder, 1, 0, 0, 0, 0, 0, 0, 0, exchange.OrderTypeByteBid}, - }, - { - name: "order id 72,340,172,838,076,673 ask", - order: askOrder(72_340_172_838_076_673), - expected: []byte{keeper.KeyTypeOrder, 1, 1, 1, 1, 1, 1, 1, 1, exchange.OrderTypeByteAsk}, - }, - { - name: "order id 72,340,172,838,076,673 bid", - order: bidOrder(72_340_172_838_076_673), - expected: []byte{keeper.KeyTypeOrder, 1, 1, 1, 1, 1, 1, 1, 1, exchange.OrderTypeByteBid}, - }, - { - name: "order id 1,229,782,938,247,303,441 ask", - order: askOrder(1_229_782_938_247_303_441), - expected: []byte{keeper.KeyTypeOrder, 17, 17, 17, 17, 17, 17, 17, 17, exchange.OrderTypeByteAsk}, - }, - { - name: "order id 1,229,782,938,247,303,441 bid", - order: bidOrder(1_229_782_938_247_303_441), - expected: []byte{keeper.KeyTypeOrder, 17, 17, 17, 17, 17, 17, 17, 17, exchange.OrderTypeByteBid}, - }, - { - name: "order id 18,446,744,073,709,551,615 ask", - order: askOrder(18_446_744_073_709_551_615), - expected: []byte{keeper.KeyTypeOrder, 255, 255, 255, 255, 255, 255, 255, 255, exchange.OrderTypeByteAsk}, - }, - { - name: "order id 18,446,744,073,709,551,615 bid", - order: bidOrder(18_446_744_073_709_551_615), - expected: []byte{keeper.KeyTypeOrder, 255, 255, 255, 255, 255, 255, 255, 255, exchange.OrderTypeByteBid}, - }, - { - name: "nil inside order", - order: exchange.Order{OrderId: 5, Order: nil}, - expPanic: "GetOrderTypeByte() missing case for ", - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - ktc := keyTestCase{ - maker: func() []byte { - return keeper.MakeKeyOrder(tc.order) - }, - expected: tc.expected, - expPanic: tc.expPanic, - } - if len(tc.expPanic) == 0 { - ktc.expPrefixes = []expectedPrefix{ - {name: "GetKeyPrefixOrder", value: keeper.GetKeyPrefixOrder(tc.order.OrderId)}, - } + expected: tc.expected, + expPrefixes: []expectedPrefix{{name: "", value: keeper.GetKeyPrefixOrder()}}, } - checkKey(t, ktc, "MakeKeyOrder %d %T", tc.order.OrderId, tc.order.Order) + checkKey(t, ktc, "MakeKeyOrder(%d)", tc.orderID) }) } } From 6c0b0f827e98d253ec9a0d7fd090b9a71060627d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 6 Sep 2023 15:15:28 -0600 Subject: [PATCH 089/309] [1658]: get/set/delete orders. Tweak some keys stuff related to order indexing. --- x/exchange/keeper/keys.go | 30 ++- x/exchange/keeper/keys_test.go | 374 ++++++++++++++++++++++++++++----- x/exchange/keeper/orders.go | 162 ++++++++++++++ x/exchange/orders.go | 41 +++- x/exchange/orders_test.go | 192 ++++++++++++----- 5 files changed, 673 insertions(+), 126 deletions(-) create mode 100644 x/exchange/keeper/orders.go diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 74ff08ca4d..b19d7713bd 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -59,7 +59,7 @@ import ( // Bid Orders: 0x02 | (8 bytes) => 0x01 | protobuf(BidOrder) // // A market to order index is maintained with the following format: -// 0x03 | (4 bytes) | (8 bytes) => nil +// 0x03 | (4 bytes) | (8 bytes) => // // An address to order index is maintained with the following format: // 0x04 | len(
) (1 byte) |
| (8 bytes) => nil @@ -530,6 +530,11 @@ func MakeKeyOrder(orderID uint64) []byte { return rv } +// ParseKeySuffixOrder parses the order id from the bytes part of an order store key. +func ParseKeySuffixOrder(suffix []byte) uint64 { + return uint64FromBz(suffix) +} + // indexPrefixMarketToOrder creates the prefix for the market to order prefix entries with some extra space for the rest. func indexPrefixMarketToOrder(marketID uint32, extraCap int) []byte { return prepKey(KeyTypeMarketToOrderIndex, uint32Bz(marketID), extraCap) @@ -572,6 +577,13 @@ func indexPrefixAssetToOrder(assetDenom string, extraCap int) []byte { return prepKey(KeyTypeAssetToOrderIndex, []byte(assetDenom), extraCap) } +// indexPrefixAssetToOrderOfType creates the prefix for asset to order index entries for the given asset denom and order type. +func indexPrefixAssetToOrderOfType(assetDenom string, orderTypeByte byte, extraCap int) []byte { + rv := indexPrefixAssetToOrder(assetDenom, 1+extraCap) + rv = append(rv, orderTypeByte) + return rv +} + // GetIndexKeyPrefixAssetToOrder creates a key prefix for the asset to order index limited to the given asset. func GetIndexKeyPrefixAssetToOrder(assetDenom string) []byte { return indexPrefixAssetToOrder(assetDenom, 0) @@ -579,22 +591,18 @@ func GetIndexKeyPrefixAssetToOrder(assetDenom string) []byte { // GetIndexKeyPrefixAssetToOrderAsks creates a key prefix for the asset to orders limited to the given asset and only ask orders. func GetIndexKeyPrefixAssetToOrderAsks(assetDenom string) []byte { - rv := indexPrefixAssetToOrder(assetDenom, 1) - rv = append(rv, OrderKeyTypeAsk) - return rv + return indexPrefixAssetToOrderOfType(assetDenom, OrderKeyTypeAsk, 0) } // GetIndexKeyPrefixAssetToOrderBids creates a key prefix for the asset to orders limited to the given asset and only bid orders. func GetIndexKeyPrefixAssetToOrderBids(assetDenom string) []byte { - rv := indexPrefixAssetToOrder(assetDenom, 1) - rv = append(rv, OrderKeyTypeBid) - return rv + return indexPrefixAssetToOrderOfType(assetDenom, OrderKeyTypeBid, 0) } // MakeIndexKeyAssetToOrder creates the key to use for the asset to order index for the provided values. -func MakeIndexKeyAssetToOrder(assetDenom string, order exchange.Order) []byte { - rv := indexPrefixAssetToOrder(assetDenom, 9) - rv = append(rv, order.GetOrderTypeByte()) - rv = append(rv, uint64Bz(order.GetOrderId())...) +func MakeIndexKeyAssetToOrder(assetDenom string, orderTypeByte byte, orderID uint64) []byte { + suffix := uint64Bz(orderID) + rv := indexPrefixAssetToOrderOfType(assetDenom, orderTypeByte, len(suffix)) + rv = append(rv, suffix...) return rv } diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index f3b60cc6ac..c79694532a 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -360,7 +360,17 @@ func TestParseKeySuffixKnownMarketID(t *testing.T) { expPanic: "runtime error: index out of range [3] with length 0", }, { - name: "three byte suffix", + name: "1 byte suffix", + suffix: []byte{1}, + expPanic: "runtime error: index out of range [3] with length 1", + }, + { + name: "2 byte suffix", + suffix: []byte{1, 2}, + expPanic: "runtime error: index out of range [3] with length 2", + }, + { + name: "3 byte suffix", suffix: []byte{1, 2, 3}, expPanic: "runtime error: index out of range [3] with length 3", }, @@ -1659,6 +1669,137 @@ func TestGetKeyPrefixMarketBuyerSettlementRatio(t *testing.T) { } } +func TestGetKeyPrefixMarketBuyerSettlementRatioForPriceDenom(t *testing.T) { + marketTypeByte := keeper.MarketKeyTypeBuyerSettlementRatio + rs := keeper.RecordSeparator + + tests := []struct { + name string + marketID uint32 + denom string + expected []byte + }{ + { + name: "market id 0 no denom", + marketID: 0, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, rs}, + }, + { + name: "market id 0 nhash", + marketID: 0, + denom: "nhash", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs}, + }, + { + name: "market id 0 hex string", + marketID: 0, + denom: hexString, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 0, 0, 0, 0, marketTypeByte}, + []byte(hexString), + []byte{rs}, + ), + }, + { + name: "market id 1 no denom", + marketID: 1, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, rs}, + }, + { + name: "market id 1 nhash", + marketID: 1, + denom: "nhash", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs}, + }, + { + name: "market id 1 hex string", + marketID: 1, + denom: hexString, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 0, 0, 0, 1, marketTypeByte}, + []byte(hexString), + []byte{rs}, + ), + }, + { + name: "market id 255 nhash", + marketID: 255, + denom: "nhash", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 0, 255, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs}, + }, + { + name: "market id 256 nhash", + marketID: 256, + denom: "nhash", + expected: []byte{keeper.KeyTypeMarket, 0, 0, 1, 0, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs}, + }, + { + name: "market id 65_536 bananas", + marketID: 65_536, + denom: "bananas", + expected: []byte{keeper.KeyTypeMarket, 0, 1, 0, 0, marketTypeByte, 'b', 'a', 'n', 'a', 'n', 'a', 's', rs}, + }, + { + name: "market id 16,777,216 nhash", + marketID: 16_777_216, + denom: "nhash", + expected: []byte{keeper.KeyTypeMarket, 1, 0, 0, 0, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs}, + }, + { + name: "market id 16,843,009 nhash", + marketID: 16_843_009, + denom: "nhash", + expected: []byte{keeper.KeyTypeMarket, 1, 1, 1, 1, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs}, + }, + { + name: "market id 4,294,967,295 no denom", + marketID: 4_294_967_295, + denom: "", + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte, rs}, + }, + { + name: "market id 4,294,967,295 nhash", + marketID: 4_294_967_295, + denom: "nhash", + expected: []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte, 'n', 'h', 'a', 's', 'h', rs}, + }, + { + name: "market id 4,294,967,295 hex string", + marketID: 4_294_967_295, + denom: hexString, + expected: concatBz( + []byte{keeper.KeyTypeMarket, 255, 255, 255, 255, marketTypeByte}, + []byte(hexString), + []byte{rs}, + ), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.GetKeyPrefixMarketBuyerSettlementRatioForPriceDenom(tc.marketID, tc.denom) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + { + name: "GetKeyPrefixMarket", + value: keeper.GetKeyPrefixMarket(tc.marketID), + }, + { + name: "GetKeyPrefixMarketBuyerSettlementRatio", + value: keeper.GetKeyPrefixMarketBuyerSettlementRatio(tc.marketID), + }, + }, + } + checkKey(t, ktc, "GetKeyPrefixMarketBuyerSettlementRatio(%d, %q)", tc.marketID, tc.denom) + }) + } +} + func TestMakeKeyMarketBuyerSettlementRatio(t *testing.T) { marketTypeByte := keeper.MarketKeyTypeBuyerSettlementRatio coin := func(denom string) sdk.Coin { @@ -2679,6 +2820,132 @@ func TestMakeKeyOrder(t *testing.T) { } } +func TestParseKeySuffixOrder(t *testing.T) { + tests := []struct { + name string + suffix []byte + exp uint64 + expPanic string + }{ + { + name: "nil suffix", + suffix: nil, + expPanic: "runtime error: index out of range [7] with length 0", + }, + { + name: "empty suffix", + suffix: []byte{}, + expPanic: "runtime error: index out of range [7] with length 0", + }, + { + name: "1 byte suffix", + suffix: []byte{1}, + expPanic: "runtime error: index out of range [7] with length 1", + }, + { + name: "2 byte suffix", + suffix: []byte{1, 2}, + expPanic: "runtime error: index out of range [7] with length 2", + }, + { + name: "3 byte suffix", + suffix: []byte{1, 2, 3}, + expPanic: "runtime error: index out of range [7] with length 3", + }, + { + name: "4 byte suffix", + suffix: []byte{1, 2, 3, 4}, + expPanic: "runtime error: index out of range [7] with length 4", + }, + { + name: "5 byte suffix", + suffix: []byte{1, 2, 3, 4, 5}, + expPanic: "runtime error: index out of range [7] with length 5", + }, + { + name: "6 byte suffix", + suffix: []byte{1, 2, 3, 4, 5, 6}, + expPanic: "runtime error: index out of range [7] with length 6", + }, + { + name: "7 byte suffix", + suffix: []byte{1, 2, 3, 4, 5, 6, 7}, + expPanic: "runtime error: index out of range [7] with length 7", + }, + { + name: "order id 72,623,859,790,382,856 with an extra byte", + suffix: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + exp: 72_623_859_790_382_856, + }, + { + name: "order id 72,623,859,790,382,856", + suffix: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + exp: 72_623_859_790_382_856, + }, + { + name: "order id 1", + suffix: []byte{0, 0, 0, 0, 0, 0, 0, 1}, + exp: 1, + }, + { + name: "order id 256", + suffix: []byte{0, 0, 0, 0, 0, 0, 1, 0}, + exp: 256, + }, + { + name: "order id 65,536", + suffix: []byte{0, 0, 0, 0, 0, 1, 0, 0}, + exp: 65_536, + }, + { + name: "order id 16,777,216", + suffix: []byte{0, 0, 0, 0, 1, 0, 0, 0}, + exp: 16_777_216, + }, + { + name: "order id 4,294,967,296", + suffix: []byte{0, 0, 0, 1, 0, 0, 0, 0}, + exp: 4_294_967_296, + }, + { + name: "order id 1,099,511,627,776", + suffix: []byte{0, 0, 1, 0, 0, 0, 0, 0}, + exp: 1_099_511_627_776, + }, + { + name: "order id 281,474,976,710,656", + suffix: []byte{0, 1, 0, 0, 0, 0, 0, 0}, + exp: 281_474_976_710_656, + }, + { + name: "order id 72,057,594,037,927,936", + suffix: []byte{1, 0, 0, 0, 0, 0, 0, 0}, + exp: 72_057_594_037_927_936, + }, + { + name: "order id 144,680,345,676,153,346", + suffix: []byte{2, 2, 2, 2, 2, 2, 2, 2}, + exp: 144_680_345_676_153_346, + }, + { + name: "order id max uint64", + suffix: []byte{255, 255, 255, 255, 255, 255, 255, 255}, + exp: 18_446_744_073_709_551_615, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var orderID uint64 + testFunc := func() { + orderID = keeper.ParseKeySuffixOrder(tc.suffix) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "ParseKeySuffixOrder(%v)", tc.suffix) + assert.Equal(t, tc.exp, orderID, "ParseKeySuffixOrder(%v) result", tc.suffix) + }) + } +} + func TestGetIndexKeyPrefixMarketToOrder(t *testing.T) { tests := []struct { name string @@ -3115,122 +3382,121 @@ func TestGetIndexKeyPrefixAssetToOrderBids(t *testing.T) { } func TestMakeIndexKeyAssetToOrder(t *testing.T) { - askOrder := func(orderID uint64) exchange.Order { - return *exchange.NewOrder(orderID).WithAsk(&exchange.AskOrder{}) - } - bidOrder := func(orderID uint64) exchange.Order { - return *exchange.NewOrder(orderID).WithBid(&exchange.BidOrder{}) - } - tests := []struct { - name string - assetDenom string - order exchange.Order - expected []byte - expPanic string + name string + assetDenom string + orderTypeByte byte + orderID uint64 + expected []byte + expPanic string }{ { - name: "no asset order 0 ask", - assetDenom: "", - order: askOrder(0), + name: "no asset order 0 ask", + assetDenom: "", + orderTypeByte: keeper.OrderKeyTypeAsk, + orderID: 0, expected: []byte{ keeper.KeyTypeAssetToOrderIndex, keeper.OrderKeyTypeAsk, 0, 0, 0, 0, 0, 0, 0, 0, }, }, { - name: "no asset order 0 bid", - assetDenom: "", - order: bidOrder(0), + name: "no asset order 0 bid", + assetDenom: "", + orderTypeByte: keeper.OrderKeyTypeBid, + orderID: 0, expected: []byte{ keeper.KeyTypeAssetToOrderIndex, keeper.OrderKeyTypeBid, 0, 0, 0, 0, 0, 0, 0, 0, }, }, { - name: "nhash order 1 ask", - assetDenom: "nhash", - order: askOrder(1), + name: "nhash order 1 ask", + assetDenom: "nhash", + orderTypeByte: keeper.OrderKeyTypeAsk, + orderID: 1, expected: []byte{ keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', keeper.OrderKeyTypeAsk, 0, 0, 0, 0, 0, 0, 0, 1, }, }, { - name: "nhash order 1 bid", - assetDenom: "nhash", - order: bidOrder(1), + name: "nhash order 1 bid", + assetDenom: "nhash", + orderTypeByte: keeper.OrderKeyTypeBid, + orderID: 1, expected: []byte{ keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', keeper.OrderKeyTypeBid, 0, 0, 0, 0, 0, 0, 0, 1, }, }, { - name: "hex string order 5 ask", - assetDenom: hexString, - order: askOrder(5), + name: "hex string order 5 ask", + assetDenom: hexString, + orderTypeByte: keeper.OrderKeyTypeAsk, + orderID: 5, expected: concatBz( []byte{keeper.KeyTypeAssetToOrderIndex}, []byte(hexString), []byte{keeper.OrderKeyTypeAsk, 0, 0, 0, 0, 0, 0, 0, 5}, ), }, { - name: "hex string order 5 bid", - assetDenom: hexString, - order: bidOrder(5), + name: "hex string order 5 bid", + assetDenom: hexString, + orderTypeByte: keeper.OrderKeyTypeBid, + orderID: 5, expected: concatBz( []byte{keeper.KeyTypeAssetToOrderIndex}, []byte(hexString), []byte{keeper.OrderKeyTypeBid, 0, 0, 0, 0, 0, 0, 0, 5}, ), }, { - name: "nhash order 4,294,967,296 ask", - assetDenom: "nhash", - order: askOrder(4_294_967_296), + name: "nhash order 4,294,967,296 ask", + assetDenom: "nhash", + orderTypeByte: keeper.OrderKeyTypeAsk, + orderID: 4_294_967_296, expected: []byte{ keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', keeper.OrderKeyTypeAsk, 0, 0, 0, 1, 0, 0, 0, 0, }, }, { - name: "nhash order 4,294,967,296 bid", - assetDenom: "nhash", - order: bidOrder(4_294_967_296), + name: "nhash order 4,294,967,296 bid", + assetDenom: "nhash", + orderTypeByte: keeper.OrderKeyTypeBid, + orderID: 4_294_967_296, expected: []byte{ keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', keeper.OrderKeyTypeBid, 0, 0, 0, 1, 0, 0, 0, 0, }, }, { - name: "nhash order max ask", - assetDenom: "nhash", - order: askOrder(18_446_744_073_709_551_615), + name: "nhash order max ask", + assetDenom: "nhash", + orderTypeByte: keeper.OrderKeyTypeAsk, + orderID: 18_446_744_073_709_551_615, expected: []byte{ keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', keeper.OrderKeyTypeAsk, 255, 255, 255, 255, 255, 255, 255, 255, }, }, { - name: "nhash order max bid", - assetDenom: "nhash", - order: bidOrder(18_446_744_073_709_551_615), + name: "nhash order max bid", + assetDenom: "nhash", + orderTypeByte: keeper.OrderKeyTypeBid, + orderID: 18_446_744_073_709_551_615, expected: []byte{ keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', keeper.OrderKeyTypeBid, 255, 255, 255, 255, 255, 255, 255, 255, }, }, - { - name: "nil inside order", - order: exchange.Order{OrderId: 3, Order: nil}, - expPanic: "GetOrderTypeByte() missing case for ", - }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeIndexKeyAssetToOrder(tc.assetDenom, tc.order) + return keeper.MakeIndexKeyAssetToOrder(tc.assetDenom, tc.orderTypeByte, tc.orderID) }, expected: tc.expected, expPanic: tc.expPanic, @@ -3239,23 +3505,23 @@ func TestMakeIndexKeyAssetToOrder(t *testing.T) { ktc.expPrefixes = []expectedPrefix{ {name: "GetIndexKeyPrefixAssetToOrder", value: keeper.GetIndexKeyPrefixAssetToOrder(tc.assetDenom)}, } - switch v := tc.order.Order.(type) { - case *exchange.Order_AskOrder: + switch tc.orderTypeByte { + case keeper.OrderKeyTypeAsk: ktc.expPrefixes = append(ktc.expPrefixes, expectedPrefix{ name: "GetIndexKeyPrefixAssetToOrderAsks", value: keeper.GetIndexKeyPrefixAssetToOrderAsks(tc.assetDenom), }) - case *exchange.Order_BidOrder: + case keeper.OrderKeyTypeBid: ktc.expPrefixes = append(ktc.expPrefixes, expectedPrefix{ name: "GetIndexKeyPrefixAssetToOrderBids", value: keeper.GetIndexKeyPrefixAssetToOrderBids(tc.assetDenom), }) default: - assert.Fail(t, "no expected prefix case defined for %T", v) + assert.Fail(t, "no expected prefix case defined for type byte %#x", tc.orderTypeByte) } } - checkKey(t, ktc, "MakeIndexKeyAssetToOrder(%q, %d)", tc.assetDenom, tc.order) + checkKey(t, ktc, "MakeIndexKeyAssetToOrder(%q, %#x, %d)", tc.assetDenom, tc.orderTypeByte, tc.orderID) }) } } diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go new file mode 100644 index 0000000000..2ddf2556c0 --- /dev/null +++ b/x/exchange/keeper/orders.go @@ -0,0 +1,162 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/provenance-io/provenance/x/exchange" +) + +// getOrderStoreKeyValue creates the store key and value representing the provided order. +func (k Keeper) getOrderStoreKeyValue(order exchange.Order) ([]byte, []byte, error) { + // 200 chosen to hopefully be more than what's needed for 99% of orders. + // TODO[1658]: Marshal some ask and bid orders to get their actual sizes and make sure 200 is okay. + key := MakeKeyOrder(order.OrderId) + value := make([]byte, 1, 200) + value[0] = order.GetOrderTypeByte() + switch value[0] { + case OrderKeyTypeAsk: + ask := order.GetAskOrder() + data, err := k.cdc.Marshal(ask) + if err != nil { + return nil, nil, fmt.Errorf("error marshaling ask order: %w", err) + } + value = append(value, data...) + return key, value, nil + case OrderKeyTypeBid: + bid := order.GetBidOrder() + data, err := k.cdc.Marshal(bid) + if err != nil { + return nil, nil, fmt.Errorf("error marshaling bid order: %w", err) + } + value = append(value, data...) + return key, value, nil + default: + // GetOrderTypeByte panics if it's an unknown order type. + // If we get here, it knew of a new order type that doesn't have a case in here. + // So we panic here instead of returning an error because it's a programming error to address. + panic(fmt.Sprintf("SetOrder missing case for order type byte %#x, order type %T", value[0], order.GetOrder())) + } +} + +// parseOrderStoreValue converts an order store value back into an order. +func (k Keeper) parseOrderStoreValue(orderID uint64, value []byte) (*exchange.Order, error) { + if len(value) == 0 { + return nil, nil + } + typeByte, data := value[0], value[1:] + switch typeByte { + case OrderKeyTypeAsk: + var ask exchange.AskOrder + err := k.cdc.Unmarshal(data, &ask) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal ask order: %w", err) + } + return exchange.NewOrder(orderID).WithAsk(&ask), nil + case OrderKeyTypeBid: + var bid exchange.BidOrder + err := k.cdc.Unmarshal(data, &bid) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal bid order: %w", err) + } + return exchange.NewOrder(orderID).WithBid(&bid), nil + default: + // Returning an error here instead of panicking because, at this point, we don't really + // have a trail of what happened to get this unknown entry here. And if we panic, it makes + // it harder to identify and clean up bad entries once we figure that out. + return nil, fmt.Errorf("unknown type byte %#x", typeByte) + } +} + +// getOrderFromStore gets an order from the store. +func (k Keeper) getOrderFromStore(store sdk.KVStore, orderID uint64) (*exchange.Order, error) { + key := MakeKeyOrder(orderID) + value := store.Get(key) + rv, err := k.parseOrderStoreValue(orderID, value) + if err != nil { + return nil, fmt.Errorf("failed to read order %d: %w", orderID, err) + } + return rv, nil +} + +// createIndexEntries creates all the key/value index entries for an order. +func createIndexEntries(order exchange.Order) []sdk.KVPair { + marketID := order.GetMarketID() + orderID := order.GetOrderId() + orderTypeByte := order.GetOrderTypeByte() + owner := order.GetOwner() + addr := sdk.MustAccAddressFromBech32(owner) + assets := order.GetAssets() + + rv := []sdk.KVPair{ + { + Key: MakeIndexKeyMarketToOrder(marketID, orderID), + Value: []byte{orderTypeByte}, + }, + { + Key: MakeIndexKeyAddressToOrder(addr, orderID), + Value: nil, + }, + } + + for _, asset := range assets { + rv = append(rv, sdk.KVPair{ + Key: MakeIndexKeyAssetToOrder(asset.Denom, orderTypeByte, orderID), + Value: nil, + }) + } + + return rv +} + +// setOrderInStore writes an order to the store (along with all it's indexes). +func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { + key, value, err := k.getOrderStoreKeyValue(order) + if err != nil { + return fmt.Errorf("failed to create order %d store key/value: %w", order.GetOrderId(), err) + } + isUpdate := store.Has(key) + store.Set(key, value) + if !isUpdate { + indexEntries := createIndexEntries(order) + for _, entry := range indexEntries { + store.Set(entry.Key, entry.Value) + } + // It is assumed that these index entries cannot change over the life of an order. + // The only change that is allowed to an order is the assets due to partial fulfillment. + // But partial fulfillment is only allowed when there's a single asset type. + // That's why no attempt is made in here to update index entries when the order already exists. + } + return nil +} + +// deleteOrderFromStore deletes an order from the store as well as all its index entries. +func (k Keeper) deleteOrderFromStore(store sdk.KVStore, orderID uint64) { + order, err := k.getOrderFromStore(store, orderID) + key := MakeKeyOrder(orderID) + store.Delete(key) + if err != nil || order == nil { + // Either it couldn't be read or it didn't exist. Either way, nothing more we can do. + return + } + indexEntries := createIndexEntries(*order) + for _, entry := range indexEntries { + store.Delete(entry.Key) + } +} + +// GetOrder gets an order. Returns nil, nil if the order does not exist. +func (k Keeper) GetOrder(ctx sdk.Context, orderID uint64) (*exchange.Order, error) { + return k.getOrderFromStore(k.getStore(ctx), orderID) +} + +// SetOrder stores the provided order in state. +func (k Keeper) SetOrder(ctx sdk.Context, order exchange.Order) error { + return k.setOrderInStore(k.getStore(ctx), order) +} + +// DeleteOrder removes the provided order from state. +func (k Keeper) DeleteOrder(ctx sdk.Context, orderID uint64) { + k.deleteOrderFromStore(k.getStore(ctx), orderID) +} diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 79c8c161d8..39aa9e3f17 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -35,6 +35,16 @@ func (o *Order) WithBid(bidOrder *BidOrder) *Order { return o } +// IsAskOrder returns true if this order is an ask order. +func (o Order) IsAskOrder() bool { + return o.GetAskOrder() != nil +} + +// IsBidOrder returns true if this order is a bid order. +func (o Order) IsBidOrder() bool { + return o.GetBidOrder() != nil +} + // GetOrderType returns a string indicating what type this order is. // See: OrderTypeAsk, OrderTypeBid // Panics if the order details are not set or are something unexpected. @@ -82,14 +92,33 @@ func (o Order) GetMarketID() uint32 { } } -// IsAskOrder returns true if this order is an ask order. -func (o Order) IsAskOrder() bool { - return o.GetAskOrder() != nil +// GetOwner gets the address of the owner of this order. +// E.g. the seller for ask orders, or buyer for bid orders. +func (o Order) GetOwner() string { + switch v := o.GetOrder().(type) { + case *Order_AskOrder: + return v.AskOrder.Seller + case *Order_BidOrder: + return v.BidOrder.Buyer + default: + // If GetOwner() is called without the order being set yet, it's a programming error, so panic. + // If it's a type without a case, the case needs to be added, so panic. + panic(fmt.Sprintf("GetOwner() missing case for %T", v)) + } } -// IsBidOrder returns true if this order is a bid order. -func (o Order) IsBidOrder() bool { - return o.GetBidOrder() != nil +// GetAssets gets the assets in this order. +func (o Order) GetAssets() sdk.Coins { + switch v := o.GetOrder().(type) { + case *Order_AskOrder: + return v.AskOrder.Assets + case *Order_BidOrder: + return v.BidOrder.Assets + default: + // If GetAssets() is called without the order being set yet, it's a programming error, so panic. + // If it's a type without a case, the case needs to be added, so panic. + panic(fmt.Sprintf("GetAssets() missing case for %T", v)) + } } // Validate returns an error if anything in this order is invalid. diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 2e9ddce199..4238a75457 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -196,7 +196,87 @@ func (o *unknownOrderType) Size() int { return 0 } -func TestOrder_OrderType(t *testing.T) { +func TestOrder_IsAskOrder(t *testing.T) { + tests := []struct { + name string + order *Order + exp bool + }{ + { + name: "nil inside order", + order: NewOrder(1), + exp: false, + }, + { + name: "ask order", + order: NewOrder(2).WithAsk(&AskOrder{}), + exp: true, + }, + { + name: "bid order", + order: NewOrder(3).WithBid(&BidOrder{}), + exp: false, + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + exp: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.order.IsAskOrder() + } + require.NotPanics(t, testFunc, "IsAskOrder") + assert.Equal(t, tc.exp, actual, "IsAskOrder result") + }) + } +} + +func TestOrder_IsBidOrder(t *testing.T) { + tests := []struct { + name string + order *Order + exp bool + }{ + { + name: "nil inside order", + order: NewOrder(1), + exp: false, + }, + { + name: "ask order", + order: NewOrder(2).WithAsk(&AskOrder{}), + exp: false, + }, + { + name: "bid order", + order: NewOrder(3).WithBid(&BidOrder{}), + exp: true, + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + exp: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.order.IsBidOrder() + } + require.NotPanics(t, testFunc, "IsBidOrder") + assert.Equal(t, tc.exp, actual, "IsBidOrder result") + }) + } +} + +func TestOrder_GetOrderType(t *testing.T) { tests := []struct { name string order *Order @@ -238,7 +318,7 @@ func TestOrder_OrderType(t *testing.T) { } } -func TestOrder_OrderTypeByte(t *testing.T) { +func TestOrder_GetOrderTypeByte(t *testing.T) { tests := []struct { name string order *Order @@ -280,124 +360,126 @@ func TestOrder_OrderTypeByte(t *testing.T) { } } -func TestOrder_IsAskOrder(t *testing.T) { +func TestOrder_GetMarketID(t *testing.T) { tests := []struct { - name string - order *Order - exp bool + name string + order *Order + expected uint32 + expPanic string }{ { - name: "nil inside order", - order: NewOrder(1), - exp: false, + name: "AskOrder", + order: NewOrder(1).WithAsk(&AskOrder{MarketId: 123}), + expected: 123, }, { - name: "ask order", - order: NewOrder(2).WithAsk(&AskOrder{}), - exp: true, + name: "BidOrder", + order: NewOrder(2).WithBid(&BidOrder{MarketId: 437}), + expected: 437, }, { - name: "bid order", - order: NewOrder(3).WithBid(&BidOrder{}), - exp: false, + name: "nil inside order", + order: NewOrder(3), + expPanic: "GetMarketID() missing case for ", }, { - name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - exp: false, + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + expPanic: "GetMarketID() missing case for *exchange.unknownOrderType", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var actual bool + var actual uint32 testFunc := func() { - actual = tc.order.IsAskOrder() + actual = tc.order.GetMarketID() } - require.NotPanics(t, testFunc, "IsAskOrder") - assert.Equal(t, tc.exp, actual, "IsAskOrder result") + + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetMarketID") + assert.Equal(t, tc.expected, actual, "GetMarketID result") }) } } -func TestOrder_IsBidOrder(t *testing.T) { +func TestOrder_GetOwner(t *testing.T) { tests := []struct { - name string - order *Order - exp bool + name string + order *Order + expected string + expPanic string }{ { - name: "nil inside order", - order: NewOrder(1), - exp: false, + name: "AskOrder", + order: NewOrder(1).WithAsk(&AskOrder{Seller: "I'm a seller!"}), + expected: "I'm a seller!", }, { - name: "ask order", - order: NewOrder(2).WithAsk(&AskOrder{}), - exp: false, + name: "BidOrder", + order: NewOrder(2).WithBid(&BidOrder{Buyer: "gimmie gimmie"}), + expected: "gimmie gimmie", }, { - name: "bid order", - order: NewOrder(3).WithBid(&BidOrder{}), - exp: true, + name: "nil inside order", + order: NewOrder(3), + expPanic: "GetOwner() missing case for ", }, { - name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - exp: false, + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + expPanic: "GetOwner() missing case for *exchange.unknownOrderType", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var actual bool + var owner string testFunc := func() { - actual = tc.order.IsBidOrder() + owner = tc.order.GetOwner() } - require.NotPanics(t, testFunc, "IsBidOrder") - assert.Equal(t, tc.exp, actual, "IsBidOrder result") + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOwner") + assert.Equal(t, tc.expected, owner, "GetOwner result") }) } } -func TestOrder_GetMarketID(t *testing.T) { +func TestOrder_GetAssets(t *testing.T) { tests := []struct { name string order *Order - expected uint32 + expected sdk.Coins expPanic string }{ { name: "AskOrder", - order: NewOrder(1).WithAsk(&AskOrder{MarketId: 123}), - expected: 123, + order: NewOrder(1).WithAsk(&AskOrder{Assets: sdk.NewCoins(sdk.NewInt64Coin("acorns", 85))}), + expected: sdk.NewCoins(sdk.NewInt64Coin("acorns", 85)), }, { name: "BidOrder", - order: NewOrder(2).WithBid(&BidOrder{MarketId: 437}), - expected: 437, + order: NewOrder(2).WithBid(&BidOrder{Assets: sdk.NewCoins(sdk.NewInt64Coin("boogers", 3))}), + expected: sdk.NewCoins(sdk.NewInt64Coin("boogers", 3)), }, { name: "nil inside order", order: NewOrder(3), - expPanic: "GetMarketID() missing case for ", + expPanic: "GetAssets() missing case for ", }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: "GetMarketID() missing case for *exchange.unknownOrderType", + expPanic: "GetAssets() missing case for *exchange.unknownOrderType", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var actual uint32 + var assets sdk.Coins testFunc := func() { - actual = tc.order.GetMarketID() + assets = tc.order.GetAssets() } - - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetMarketID") - assert.Equal(t, tc.expected, actual, "GetMarketID result") + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetAssets") + assert.Equal(t, tc.expected, assets, "GetAssets result") }) } } From b807913c4802d8eae58069a278f5c056e0c3bf65 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 6 Sep 2023 16:45:54 -0600 Subject: [PATCH 090/309] [1658]: Create index key parsers. --- x/exchange/keeper/keys.go | 98 +++++++++ x/exchange/keeper/keys_test.go | 352 +++++++++++++++++++++++++++++++++ 2 files changed, 450 insertions(+) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index b19d7713bd..a9022c6fe6 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -552,6 +552,40 @@ func MakeIndexKeyMarketToOrder(marketID uint32, orderID uint64) []byte { return rv } +// ParseIndexKeyMarketToOrder will extract the market id and order id from a market to order index key. +// The input can have the following formats: +// - | (4 bytes) | (8 bytes) +// - (4 bytes) | (8 bytes) +// - (8 bytes) +// +// In the case where just the is provided, the returned market id will be 0. +func ParseIndexKeyMarketToOrder(key []byte) (uint32, uint64, error) { + var marketIDBz, orderIDBz []byte + switch len(key) { + case 8: + orderIDBz = key + case 12: + marketIDBz = key[:4] + orderIDBz = key[4:] + case 13: + if key[0] != KeyTypeMarketToOrderIndex { + return 0, 0, fmt.Errorf("cannot parse market to order key: unknown type byte %#x, expected %#x", + key[0], KeyTypeMarketToOrderIndex) + } + marketIDBz = key[1:5] + orderIDBz = key[5:] + default: + return 0, 0, fmt.Errorf("cannot parse market to order key: length %d, expected 8, 12, or 13", len(key)) + } + + var marketID uint32 + if len(marketIDBz) > 0 { + marketID = uint32FromBz(marketIDBz) + } + orderID := uint64FromBz(orderIDBz) + return marketID, orderID, nil +} + // indexPrefixAddressToOrder creates the prefix for the address to order index entries with some extra apace for the rest. func indexPrefixAddressToOrder(addr sdk.AccAddress, extraCap int) []byte { if len(addr) == 0 { @@ -572,6 +606,40 @@ func MakeIndexKeyAddressToOrder(addr sdk.AccAddress, orderID uint64) []byte { return rv } +// ParseIndexKeyAddressToOrder will extract what it can from an address to order index key. +// The input can have the following formats: +// - | | | +// - | | +// - +// +// In the case where just the is provided, the returned address will be empty. +func ParseIndexKeyAddressToOrder(key []byte) (sdk.AccAddress, uint64, error) { + if len(key) < 8 { + return nil, 0, fmt.Errorf("cannot parse address to order index key: only has %d bytes, expected at least 8", len(key)) + } + pre, orderIDBz := key[:len(key)-8], key[len(key)-8:] + orderID := uint64FromBz(orderIDBz) + var addr sdk.AccAddress + if len(pre) > 0 { + // Either the first byte is a length byte, or it's the key type byte. + // First check it as a length byte, then, if that fails but it's the key type byte, check the second as the length. + // Either way, there needs to be at least 2 bytes: a length byte then address byte (length 0 isn't allowed). + if len(pre) == 1 { + return nil, 0, fmt.Errorf("cannot parse address to order index key: unable to determine address from single byte %#x", pre[0]) + } + var rest []byte + var err error + addr, rest, err = parseLengthPrefixedAddr(pre) + if (err != nil || len(rest) != 0) && pre[0] == KeyTypeAddressToOrderIndex { + addr, rest, err = parseLengthPrefixedAddr(pre[1:]) + } + if len(addr) == 0 || len(rest) != 0 || err != nil { + return nil, 0, fmt.Errorf("cannot parse address to order index key: unable to determine address from [%d, %d, ...(length %d)]", pre[0], pre[1], len(pre)) + } + } + return addr, orderID, nil +} + // indexPrefixAssetToOrder creates the prefix for the asset to order index entries with some extra space for the rest. func indexPrefixAssetToOrder(assetDenom string, extraCap int) []byte { return prepKey(KeyTypeAssetToOrderIndex, []byte(assetDenom), extraCap) @@ -606,3 +674,33 @@ func MakeIndexKeyAssetToOrder(assetDenom string, orderTypeByte byte, orderID uin rv = append(rv, suffix...) return rv } + +// ParseIndexKeyAssetToOrder extracts the denom, type byte, and order id from an asset to order index key. +// The input can have the following formats: +// - | | | +// - | | +// - | +// - +// +// In the case where just the is provided, the returned denom will be "", and type byte will be 0. +// In the case where just the and are provided, the returned denom will be "". +func ParseIndexKeyAssetToOrder(key []byte) (string, byte, uint64, error) { + if len(key) < 8 { + return "", 0, 0, fmt.Errorf("cannot parse asset to order key: only has %d bytes, expected at least 8", len(key)) + } + unparsed, orderIDBz := key[:len(key)-8], key[len(key)-8:] + var denom string + var typeByte byte + orderID := uint64FromBz(orderIDBz) + if len(unparsed) > 0 { + typeByte = unparsed[len(unparsed)-1] + unparsed = unparsed[:len(unparsed)-1] + } + if len(unparsed) > 0 { + if unparsed[0] == KeyTypeAssetToOrderIndex { + unparsed = unparsed[1:] + } + denom = string(unparsed) + } + return denom, typeByte, orderID, nil +} diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index c79694532a..aa54b400a5 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -3093,6 +3093,117 @@ func TestMakeIndexKeyMarketToOrder(t *testing.T) { } } +func TestParseIndexKeyMarketToOrder(t *testing.T) { + tests := []struct { + name string + key []byte + expMarketID uint32 + expOrderID uint64 + expErr string + }{ + { + name: "nil key", + key: nil, + expErr: "cannot parse market to order key: length 0, expected 8, 12, or 13", + }, + { + name: "empty key", + key: []byte{}, + expErr: "cannot parse market to order key: length 0, expected 8, 12, or 13", + }, + { + name: "7 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7}, + expErr: "cannot parse market to order key: length 7, expected 8, 12, or 13", + }, + { + name: "9 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + expErr: "cannot parse market to order key: length 9, expected 8, 12, or 13", + }, + { + name: "10 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + expErr: "cannot parse market to order key: length 10, expected 8, 12, or 13", + }, + { + name: "11 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, + expErr: "cannot parse market to order key: length 11, expected 8, 12, or 13", + }, + { + name: "14 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + expErr: "cannot parse market to order key: length 14, expected 8, 12, or 13", + }, + { + name: "8 bytes order id 0", + key: []byte{0, 0, 0, 0, 0, 0, 0, 0}, + expOrderID: 0, + }, + { + name: "8 bytes order id 1", + key: []byte{0, 0, 0, 0, 0, 0, 0, 1}, + expMarketID: 0, + expOrderID: 1, + }, + { + name: "8 bytes order id 72,623,859,790,382,856", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "12 bytes market id 1 order id 1", + key: []byte{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, + expMarketID: 1, + expOrderID: 1, + }, + { + name: "12 bytes market id 16,843,009 order id 144,680,345,676,153,346", + key: []byte{1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2}, + expMarketID: 16_843_009, + expOrderID: 144_680_345_676_153_346, + }, + { + name: "13 bytes market id 1 order id 1", + key: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, + expMarketID: 1, + expOrderID: 1, + }, + { + name: "13 bytes market id 16,843,009 order id 144,680,345,676,153,346", + key: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2}, + expMarketID: 16_843_009, + expOrderID: 144_680_345_676_153_346, + }, + { + name: "13 bytes first byte too high", + key: []byte{0x4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, + expErr: "cannot parse market to order key: unknown type byte 0x4, expected 0x3", + }, + { + name: "13 bytes first byte too low", + key: []byte{0x2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, + expErr: "cannot parse market to order key: unknown type byte 0x2, expected 0x3", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var marketID uint32 + var orderID uint64 + var err error + testFunc := func() { + marketID, orderID, err = keeper.ParseIndexKeyMarketToOrder(tc.key) + } + require.NotPanics(t, testFunc, "ParseIndexKeyMarketToOrder(%v)", tc.key) + assertions.AssertErrorValue(t, err, tc.expErr, "ParseIndexKeyMarketToOrder(%v) error", tc.key) + assert.Equal(t, tc.expMarketID, marketID, "ParseIndexKeyMarketToOrder(%v) market id", tc.key) + assert.Equal(t, tc.expOrderID, orderID, "ParseIndexKeyMarketToOrder(%v) order id", tc.key) + }) + } +} + func TestGetIndexKeyPrefixAddressToOrder(t *testing.T) { tests := []struct { name string @@ -3240,6 +3351,155 @@ func TestMakeIndexKeyAddressToOrder(t *testing.T) { } } +func TestParseIndexKeyAddressToOrder(t *testing.T) { + tests := []struct { + name string + key []byte + expAddr sdk.AccAddress + expOrderID uint64 + expErr string + }{ + { + name: "nil key", + key: nil, + expErr: "cannot parse address to order index key: only has 0 bytes, expected at least 8", + }, + { + name: "empty key", + key: []byte{}, + expErr: "cannot parse address to order index key: only has 0 bytes, expected at least 8", + }, + { + name: "7 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7}, + expErr: "cannot parse address to order index key: only has 7 bytes, expected at least 8", + }, + { + name: "just order id 1", + key: []byte{0, 0, 0, 0, 0, 0, 0, 1}, + expOrderID: 1, + }, + { + name: "just order id 72,623,859,790,382,856", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "9 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + expErr: "cannot parse address to order index key: unable to determine address from single byte 0x1", + }, + { + name: "1 byte address order id 72,623,859,790,382,856", + key: []byte{1, 55, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55}, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "length byte 2 but only 1 byte after it", + key: []byte{2, 55, 1, 2, 3, 4, 5, 6, 7, 8}, + expErr: "cannot parse address to order index key: unable to determine address from [2, 55, ...(length 2)]", + }, + { + name: "length byte 2 but 3 bytes after it", + key: []byte{2, 55, 56, 57, 1, 2, 3, 4, 5, 6, 7, 8}, + expErr: "cannot parse address to order index key: unable to determine address from [2, 55, ...(length 4)]", + }, + { + name: "length byte 4 order id 72,623,859,790,382,856", + key: []byte{4, 55, 56, 57, 58, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55, 56, 57, 58}, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "length byte 20 but only 19 byte after it", + key: []byte{20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, + 1, 2, 3, 4, 5, 6, 7, 8}, + expErr: "cannot parse address to order index key: unable to determine address from [20, 101, ...(length 20)]", + }, + { + name: "length byte 20 but 21 byte after it", + key: []byte{20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 1, 2, 3, 4, 5, 6, 7, 8}, + expErr: "cannot parse address to order index key: unable to determine address from [20, 101, ...(length 22)]", + }, + { + name: "20 byte address order id 72,623,859,790,382,856", + key: []byte{20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120}, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "with type byte: 1 byte address order id 72,623,859,790,382,856", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 1, 55, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55}, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "with type byte: length byte 2 but only 1 byte after it", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 2, 55, 1, 2, 3, 4, 5, 6, 7, 8}, + expErr: "cannot parse address to order index key: unable to determine address from [4, 2, ...(length 3)]", + }, + { + name: "with type byte: length byte 2 but 5 bytes after it", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 2, 55, 56, 57, 58, 1, 2, 3, 4, 5, 6, 7, 8}, + expErr: "cannot parse address to order index key: unable to determine address from [4, 2, ...(length 6)]", + }, + { + name: "with type byte: length byte 4 order id 72,623,859,790,382,856", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 4, 55, 56, 57, 58, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55, 56, 57, 58}, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "with type byte: length byte 20 but only 19 byte after it", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 20, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, + 1, 2, 3, 4, 5, 6, 7, 8}, + expErr: "cannot parse address to order index key: unable to determine address from [4, 20, ...(length 21)]", + }, + { + name: "with type byte: length byte 20 but 21 byte after it", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 20, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 1, 2, 3, 4, 5, 6, 7, 8}, + expErr: "cannot parse address to order index key: unable to determine address from [4, 20, ...(length 23)]", + }, + { + name: "with type byte: 20 byte address order id 72,623,859,790,382,856", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 20, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120}, + expOrderID: 72_623_859_790_382_856, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var addr sdk.AccAddress + var orderID uint64 + var err error + testFunc := func() { + addr, orderID, err = keeper.ParseIndexKeyAddressToOrder(tc.key) + } + require.NotPanics(t, testFunc, "ParseIndexKeyAddressToOrder(%v)", tc.key) + assertions.AssertErrorValue(t, err, tc.expErr, "ParseIndexKeyAddressToOrder(%v) error", tc.key) + assert.Equal(t, tc.expAddr, addr, "ParseIndexKeyAddressToOrder(%v) address", tc.key) + assert.Equal(t, tc.expOrderID, orderID, "ParseIndexKeyAddressToOrder(%v) order id", tc.key) + }) + } +} + func TestGetIndexKeyPrefixAssetToOrder(t *testing.T) { tests := []struct { name string @@ -3525,3 +3785,95 @@ func TestMakeIndexKeyAssetToOrder(t *testing.T) { }) } } + +func TestParseIndexKeyAssetToOrder(t *testing.T) { + tests := []struct { + name string + key []byte + expDenom string + expTypeByte byte + expOrderID uint64 + expErr string + }{ + { + name: "nil key", + key: nil, + expErr: "cannot parse asset to order key: only has 0 bytes, expected at least 8", + }, + { + name: "empty key", + key: []byte{}, + expErr: "cannot parse asset to order key: only has 0 bytes, expected at least 8", + }, + { + name: "7 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7}, + expErr: "cannot parse asset to order key: only has 7 bytes, expected at least 8", + }, + { + name: "order id 1", + key: []byte{0, 0, 0, 0, 0, 0, 0, 1}, + expOrderID: 1, + }, + { + name: "order id 578,437,695,752,307,201", + key: []byte{8, 7, 6, 5, 4, 3, 2, 1}, + expOrderID: 578_437_695_752_307_201, + }, + { + name: "order type byte 99 order id 578,437,695,752,307,201", + key: []byte{99, 8, 7, 6, 5, 4, 3, 2, 1}, + expTypeByte: 99, + expOrderID: 578_437_695_752_307_201, + }, + { + name: "nhash order type byte 99 order id 578,437,695,752,307,201", + key: []byte{'n', 'h', 'a', 's', 'h', 99, 8, 7, 6, 5, 4, 3, 2, 1}, + expDenom: "nhash", + expTypeByte: 99, + expOrderID: 578_437_695_752_307_201, + }, + { + name: "hex string order type byte 99 order id 578,437,695,752,307,201", + key: append([]byte(hexString), 99, 8, 7, 6, 5, 4, 3, 2, 1), + expDenom: hexString, + expTypeByte: 99, + expOrderID: 578_437_695_752_307_201, + }, + { + name: "with type byte nhash order type byte 99 order id 578,437,695,752,307,201", + key: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', 99, 8, 7, 6, 5, 4, 3, 2, 1}, + expDenom: "nhash", + expTypeByte: 99, + expOrderID: 578_437_695_752_307_201, + }, + { + name: "with type byte hex string order type byte 99 order id 578,437,695,752,307,201", + key: concatBz( + []byte{keeper.KeyTypeAssetToOrderIndex}, + []byte(hexString), + []byte{99, 8, 7, 6, 5, 4, 3, 2, 1}, + ), + expDenom: hexString, + expTypeByte: 99, + expOrderID: 578_437_695_752_307_201, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var denom string + var typeByte byte + var orderiD uint64 + var err error + testFunc := func() { + denom, typeByte, orderiD, err = keeper.ParseIndexKeyAssetToOrder(tc.key) + } + require.NotPanics(t, testFunc, "ParseIndexKeyAssetToOrder(%v)", tc.key) + assertions.AssertErrorValue(t, err, tc.expErr, "ParseIndexKeyAssetToOrder(%v) error", tc.key) + assert.Equal(t, tc.expDenom, denom, "ParseIndexKeyAssetToOrder(%v) denom", tc.key) + assert.Equal(t, tc.expTypeByte, typeByte, "ParseIndexKeyAssetToOrder(%v) type byte", tc.key) + assert.Equal(t, tc.expOrderID, orderiD, "ParseIndexKeyAssetToOrder(%v) order id", tc.key) + }) + } +} From 7e913a4b70c9c7bc0e6e5a3d98119024e737e95c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 6 Sep 2023 17:06:02 -0600 Subject: [PATCH 091/309] [1658]: Create the index iterators. --- x/exchange/keeper/orders.go | 68 ++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 2ddf2556c0..6478c974d6 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -96,7 +96,7 @@ func createIndexEntries(order exchange.Order) []sdk.KVPair { }, { Key: MakeIndexKeyAddressToOrder(addr, orderID), - Value: nil, + Value: []byte{orderTypeByte}, }, } @@ -160,3 +160,69 @@ func (k Keeper) SetOrder(ctx sdk.Context, order exchange.Order) error { func (k Keeper) DeleteOrder(ctx sdk.Context, orderID uint64) { k.deleteOrderFromStore(k.getStore(ctx), orderID) } + +// IterateMarketOrders iterates over all orders for a market. +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateMarketOrders(ctx sdk.Context, marketID uint32, cb func(orderID uint64, orderTypeByte byte) bool) { + k.iterate(ctx, GetIndexKeyPrefixMarketToOrder(marketID), func(key, value []byte) bool { + if len(value) == 0 { + return false + } + _, orderID, err := ParseIndexKeyMarketToOrder(key) + if err != nil { + return false + } + return cb(orderID, value[0]) + }) +} + +// IterateAddressOrders iterates over all orders for an address. +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateAddressOrders(ctx sdk.Context, addr sdk.AccAddress, cb func(orderID uint64, orderTypeByte byte) bool) { + k.iterate(ctx, GetIndexKeyPrefixAddressToOrder(addr), func(key, value []byte) bool { + if len(value) == 0 { + return false + } + _, orderID, err := ParseIndexKeyAddressToOrder(key) + if err != nil { + return false + } + return cb(orderID, value[0]) + }) +} + +// IterateAssetOrders iterates over all orders for a given asset denom. +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateAssetOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64, orderTypeByte byte) bool) { + k.iterate(ctx, GetIndexKeyPrefixAssetToOrder(assetDenom), func(key, _ []byte) bool { + _, orderTypeByte, orderID, err := ParseIndexKeyAssetToOrder(key) + if err != nil { + return false + } + return cb(orderID, orderTypeByte) + }) +} + +// IterateAssetAskOrders iterates over all ask orders for a given asset denom. +// The callback takes in the order id and should return whether to stop iterating. +func (k Keeper) IterateAssetAskOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64) bool) { + k.iterate(ctx, GetIndexKeyPrefixAssetToOrderAsks(assetDenom), func(key, _ []byte) bool { + _, _, orderID, err := ParseIndexKeyAssetToOrder(key) + if err != nil { + return false + } + return cb(orderID) + }) +} + +// IterateAssetBidOrders iterates over all bid orders for a given asset denom. +// The callback takes in the order id and should return whether to stop iterating. +func (k Keeper) IterateAssetBidOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64) bool) { + k.iterate(ctx, GetIndexKeyPrefixAssetToOrderBids(assetDenom), func(key, _ []byte) bool { + _, _, orderID, err := ParseIndexKeyAssetToOrder(key) + if err != nil { + return false + } + return cb(orderID) + }) +} From 20a190385cc6afcf15c725cc052a145bf2497c6b Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 6 Sep 2023 17:28:22 -0600 Subject: [PATCH 092/309] [1658]: Add the name keeper to the keeper and create CanCreateAsk and CanCreateBid. --- x/exchange/expected_keepers.go | 5 +++++ x/exchange/keeper/keeper.go | 4 +++- x/exchange/keeper/market.go | 31 ++++++++++++++++++++++++++++++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/x/exchange/expected_keepers.go b/x/exchange/expected_keepers.go index 9ee0711eea..715582142f 100644 --- a/x/exchange/expected_keepers.go +++ b/x/exchange/expected_keepers.go @@ -3,6 +3,7 @@ package exchange import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + attrtypes "github.com/provenance-io/provenance/x/attribute/types" ) type AccountKeeper interface { @@ -15,3 +16,7 @@ type AccountKeeper interface { type NameKeeper interface { Normalize(ctx sdk.Context, name string) (string, error) } + +type AttributeKeeper interface { + GetAllAttributesAddr(ctx sdk.Context, addr []byte) ([]attrtypes.Attribute, error) +} diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 28c98813d6..0bc86be040 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -18,6 +18,7 @@ type Keeper struct { storeKey storetypes.StoreKey accountKeeper exchange.AccountKeeper + attrKeeper exchange.AttributeKeeper nameKeeper exchange.NameKeeper // TODO[1658]: Finish the Keeper struct. @@ -25,13 +26,14 @@ type Keeper struct { } func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, - accountKeeper exchange.AccountKeeper, nameKeeper exchange.NameKeeper, + accountKeeper exchange.AccountKeeper, attrKeeper exchange.AttributeKeeper, nameKeeper exchange.NameKeeper, ) Keeper { // TODO[1658]: Finish NewKeeper. rv := Keeper{ cdc: cdc, storeKey: storeKey, accountKeeper: accountKeeper, + attrKeeper: attrKeeper, nameKeeper: nameKeeper, authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), } diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index ddedf0886a..a8604f0d0e 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -6,9 +6,9 @@ import ( "strings" sdkmath "cosmossdk.io/math" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/provenance-io/provenance/x/exchange" ) @@ -859,3 +859,32 @@ func (k Keeper) UpdateFees(ctx sdk.Context, msg *exchange.MsgGovManageFeesReques updateBuyerSettlementFlatFees(store, msg.MarketId, msg.RemoveFeeBuyerSettlementFlat, msg.AddFeeBuyerSettlementFlat) updateBuyerSettlementRatios(store, msg.MarketId, msg.RemoveFeeBuyerSettlementRatios, msg.AddFeeBuyerSettlementRatios) } + +// hasReqAttrs returns true if either reqAttrs is empty or the provide address has all of them on their account. +func (k Keeper) hasReqAttrs(ctx sdk.Context, addr sdk.AccAddress, reqAttrs []string) bool { + if len(reqAttrs) == 0 { + return true + } + attrs, err := k.attrKeeper.GetAllAttributesAddr(ctx, addr) + if err != nil { + return false + } + accAttrs := make([]string, len(attrs)) + for i, attr := range attrs { + accAttrs[i] = attr.Name + } + missing := exchange.FindUnmatchedReqAttrs(reqAttrs, accAttrs) + return len(missing) == 0 +} + +// CanCreateAsk returns true if the provided address is allowed to create an ask order in the given market. +func (k Keeper) CanCreateAsk(ctx sdk.Context, marketID uint32, addr sdk.AccAddress) bool { + reqAttrs := k.GetReqAttrAsk(ctx, marketID) + return k.hasReqAttrs(ctx, addr, reqAttrs) +} + +// CanCreateBid returns true if the provided address is allowed to create a bid order in the given market. +func (k Keeper) CanCreateBid(ctx sdk.Context, marketID uint32, addr sdk.AccAddress) bool { + reqAttrs := k.GetReqAttrBid(ctx, marketID) + return k.hasReqAttrs(ctx, addr, reqAttrs) +} From 834f122676f79065784188f6be1a9eb5c120280c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 6 Sep 2023 17:35:00 -0600 Subject: [PATCH 093/309] [1658]: Add some extra explanation to the req attrs fields. --- docs/proto-docs.md | 8 ++++---- proto/provenance/exchange/v1/market.proto | 8 ++++++-- x/exchange/market.pb.go | 8 ++++++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 3e3b276878..8c2c868442 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1620,14 +1620,14 @@ Market contains all information about a market. | `accepting_orders` | [bool](#bool) | | accepting_orders is whether this market is allowing orders to be created for it. | | `allow_user_settlement` | [bool](#bool) | | allow_user_settlement is whether this market allows users to initiate their own settlements. For example, the FillBids and FillAsks endpoints are available if and only if this is true. The MarketSettle endpoint is only available to market actors regardless of the value of this field. | | `access_grants` | [AccessGrant](#provenance.exchange.v1.AccessGrant) | repeated | access_grants is the list of addresses and permissions granted for this market. | -| `req_attr_create_ask` | [string](#string) | repeated | req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. +| `req_attr_create_ask` | [string](#string) | repeated | req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. An account must have all of these attributes in order to create an ask order in this market. If the list is empty, any account can create ask orders in this market. -An entry that starts with "*." will match any attributes that end with the rest of it. E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a". +An entry that starts with "*." will match any attributes that end with the rest of it. E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "b.x.a", or "c.b.a.x". An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. | -| `req_attr_create_bid` | [string](#string) | repeated | req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. +| `req_attr_create_bid` | [string](#string) | repeated | req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. An account must have all of these attributes in order to create a bid order in this market. If the list is empty, any account can create bid orders in this market. -An entry that starts with "*." will match any attributes that end with the rest of it. E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a". +An entry that starts with "*." will match any attributes that end with the rest of it. E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "c.b.x.a", or "c.b.a.x". An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. | diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index 652c294493..ba6a1db3ae 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -78,16 +78,20 @@ message Market { // access_grants is the list of addresses and permissions granted for this market. repeated AccessGrant access_grants = 11 [(gogoproto.nullable) = false]; // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. + // An account must have all of these attributes in order to create an ask order in this market. + // If the list is empty, any account can create ask orders in this market. // // An entry that starts with "*." will match any attributes that end with the rest of it. - // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a". + // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "b.x.a", or "c.b.a.x". // // An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. repeated string req_attr_create_ask = 12; // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. + // An account must have all of these attributes in order to create a bid order in this market. + // If the list is empty, any account can create bid orders in this market. // // An entry that starts with "*." will match any attributes that end with the rest of it. - // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a". + // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "c.b.x.a", or "c.b.a.x". // // An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. repeated string req_attr_create_bid = 13; diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index 33fda5366e..648a5829de 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -227,16 +227,20 @@ type Market struct { // access_grants is the list of addresses and permissions granted for this market. AccessGrants []AccessGrant `protobuf:"bytes,11,rep,name=access_grants,json=accessGrants,proto3" json:"access_grants"` // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. + // An account must have all of these attributes in order to create an ask order in this market. + // If the list is empty, any account can create ask orders in this market. // // An entry that starts with "*." will match any attributes that end with the rest of it. - // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a". + // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "b.x.a", or "c.b.a.x". // // An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. ReqAttrCreateAsk []string `protobuf:"bytes,12,rep,name=req_attr_create_ask,json=reqAttrCreateAsk,proto3" json:"req_attr_create_ask,omitempty"` // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. + // An account must have all of these attributes in order to create a bid order in this market. + // If the list is empty, any account can create bid orders in this market. // // An entry that starts with "*." will match any attributes that end with the rest of it. - // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a". + // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "c.b.x.a", or "c.b.a.x". // // An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. ReqAttrCreateBid []string `protobuf:"bytes,13,rep,name=req_attr_create_bid,json=reqAttrCreateBid,proto3" json:"req_attr_create_bid,omitempty"` From aec6e34c004ac2c6c9fa814d3e043ba7052bc9f4 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sat, 9 Sep 2023 16:56:22 -0600 Subject: [PATCH 094/309] [1658]: Populate the create ask, create bid and cancel order messages. --- docs/proto-docs.md | 28 ++ proto/provenance/exchange/v1/tx.proto | 32 +- x/exchange/tx.pb.go | 542 ++++++++++++++++++++++---- 3 files changed, 525 insertions(+), 77 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 8c2c868442..d808431843 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2094,6 +2094,12 @@ Query is the service for exchange module's query endpoints. MsgCancelOrderRequest is a request message for the CancelOrder endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `owner` | [string](#string) | | owner is the account that owns the order. E.g. the seller for ask orders or buyer for bid orders. | +| `order_id` | [uint64](#uint64) | | order_id is the id of the order to cancel. | + + @@ -2114,6 +2120,12 @@ MsgCancelOrderResponse is a response message for the CancelOrder endpoint. MsgCreateAskRequest is a request message for the CreateAsk endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `ask_order` | [AskOrder](#provenance.exchange.v1.AskOrder) | | ask_order is the details of the order being created. | +| `order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | order_creation_fee is the fee that is being paid to create this order. | + + @@ -2124,6 +2136,11 @@ MsgCreateAskRequest is a request message for the CreateAsk endpoint. MsgCreateAskResponse is a response message for the CreateAsk endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order_id` | [uint64](#uint64) | | order_id is the id of the order created. | + + @@ -2134,6 +2151,12 @@ MsgCreateAskResponse is a response message for the CreateAsk endpoint. MsgCreateBidRequest is a request message for the CreateBid endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `bid_order` | [BidOrder](#provenance.exchange.v1.BidOrder) | | bid_order is the details of the order being created. | +| `order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | order_creation_fee is the fee that is being paid to create this order. | + + @@ -2144,6 +2167,11 @@ MsgCreateBidRequest is a request message for the CreateBid endpoint. MsgCreateBidResponse is a response message for the CreateBid endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order_id` | [uint64](#uint64) | | order_id is the id of the order created. | + + diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index ac8768416e..31b30e1257 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -11,6 +11,7 @@ import "cosmos/msg/v1/msg.proto"; import "cosmos_proto/cosmos.proto"; import "gogoproto/gogo.proto"; import "provenance/exchange/v1/market.proto"; +import "provenance/exchange/v1/orders.proto"; import "provenance/exchange/v1/params.proto"; // Msg is the service for exchange module's tx endpoints. @@ -63,23 +64,44 @@ service Msg { // MsgCreateAskRequest is a request message for the CreateAsk endpoint. message MsgCreateAskRequest { - // TODO[1658]: MsgCreateAskRequest + option (cosmos.msg.v1.signer) = "ask_order.seller"; + + // ask_order is the details of the order being created. + AskOrder ask_order = 1 [(gogoproto.nullable) = false]; + // order_creation_fee is the fee that is being paid to create this order. + cosmos.base.v1beta1.Coin order_creation_fee = 2; } // MsgCreateAskResponse is a response message for the CreateAsk endpoint. -message MsgCreateAskResponse {} +message MsgCreateAskResponse { + // order_id is the id of the order created. + uint64 order_id = 1; +} // MsgCreateBidRequest is a request message for the CreateBid endpoint. message MsgCreateBidRequest { - // TODO[1658]: MsgCreateBidRequest + option (cosmos.msg.v1.signer) = "bid_order.buyer"; + + // bid_order is the details of the order being created. + BidOrder bid_order = 1 [(gogoproto.nullable) = false]; + // order_creation_fee is the fee that is being paid to create this order. + cosmos.base.v1beta1.Coin order_creation_fee = 2; } // MsgCreateBidResponse is a response message for the CreateBid endpoint. -message MsgCreateBidResponse {} +message MsgCreateBidResponse { + // order_id is the id of the order created. + uint64 order_id = 1; +} // MsgCancelOrderRequest is a request message for the CancelOrder endpoint. message MsgCancelOrderRequest { - // TODO[1658]: MsgCancelOrderRequest + option (cosmos.msg.v1.signer) = "owner"; + + // owner is the account that owns the order. E.g. the seller for ask orders or buyer for bid orders. + string owner = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // order_id is the id of the order to cancel. + uint64 order_id = 2; } // MsgCancelOrderResponse is a response message for the CancelOrder endpoint. diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 18a103328a..ce6d551192 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -33,6 +33,10 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // MsgCreateAskRequest is a request message for the CreateAsk endpoint. type MsgCreateAskRequest struct { + // ask_order is the details of the order being created. + AskOrder AskOrder `protobuf:"bytes,1,opt,name=ask_order,json=askOrder,proto3" json:"ask_order"` + // order_creation_fee is the fee that is being paid to create this order. + OrderCreationFee *types.Coin `protobuf:"bytes,2,opt,name=order_creation_fee,json=orderCreationFee,proto3" json:"order_creation_fee,omitempty"` } func (m *MsgCreateAskRequest) Reset() { *m = MsgCreateAskRequest{} } @@ -68,8 +72,24 @@ func (m *MsgCreateAskRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCreateAskRequest proto.InternalMessageInfo +func (m *MsgCreateAskRequest) GetAskOrder() AskOrder { + if m != nil { + return m.AskOrder + } + return AskOrder{} +} + +func (m *MsgCreateAskRequest) GetOrderCreationFee() *types.Coin { + if m != nil { + return m.OrderCreationFee + } + return nil +} + // MsgCreateAskResponse is a response message for the CreateAsk endpoint. type MsgCreateAskResponse struct { + // order_id is the id of the order created. + OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` } func (m *MsgCreateAskResponse) Reset() { *m = MsgCreateAskResponse{} } @@ -105,8 +125,19 @@ func (m *MsgCreateAskResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCreateAskResponse proto.InternalMessageInfo +func (m *MsgCreateAskResponse) GetOrderId() uint64 { + if m != nil { + return m.OrderId + } + return 0 +} + // MsgCreateBidRequest is a request message for the CreateBid endpoint. type MsgCreateBidRequest struct { + // bid_order is the details of the order being created. + BidOrder BidOrder `protobuf:"bytes,1,opt,name=bid_order,json=bidOrder,proto3" json:"bid_order"` + // order_creation_fee is the fee that is being paid to create this order. + OrderCreationFee *types.Coin `protobuf:"bytes,2,opt,name=order_creation_fee,json=orderCreationFee,proto3" json:"order_creation_fee,omitempty"` } func (m *MsgCreateBidRequest) Reset() { *m = MsgCreateBidRequest{} } @@ -142,8 +173,24 @@ func (m *MsgCreateBidRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCreateBidRequest proto.InternalMessageInfo +func (m *MsgCreateBidRequest) GetBidOrder() BidOrder { + if m != nil { + return m.BidOrder + } + return BidOrder{} +} + +func (m *MsgCreateBidRequest) GetOrderCreationFee() *types.Coin { + if m != nil { + return m.OrderCreationFee + } + return nil +} + // MsgCreateBidResponse is a response message for the CreateBid endpoint. type MsgCreateBidResponse struct { + // order_id is the id of the order created. + OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` } func (m *MsgCreateBidResponse) Reset() { *m = MsgCreateBidResponse{} } @@ -179,8 +226,19 @@ func (m *MsgCreateBidResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCreateBidResponse proto.InternalMessageInfo +func (m *MsgCreateBidResponse) GetOrderId() uint64 { + if m != nil { + return m.OrderId + } + return 0 +} + // MsgCancelOrderRequest is a request message for the CancelOrder endpoint. type MsgCancelOrderRequest struct { + // owner is the account that owns the order. E.g. the seller for ask orders or buyer for bid orders. + Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` + // order_id is the id of the order to cancel. + OrderId uint64 `protobuf:"varint,2,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` } func (m *MsgCancelOrderRequest) Reset() { *m = MsgCancelOrderRequest{} } @@ -216,6 +274,20 @@ func (m *MsgCancelOrderRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCancelOrderRequest proto.InternalMessageInfo +func (m *MsgCancelOrderRequest) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +func (m *MsgCancelOrderRequest) GetOrderId() uint64 { + if m != nil { + return m.OrderId + } + return 0 +} + // MsgCancelOrderResponse is a response message for the CancelOrder endpoint. type MsgCancelOrderResponse struct { } @@ -1341,78 +1413,86 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1126 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xc1, 0x4f, 0xe3, 0xc6, - 0x1b, 0xc5, 0x3f, 0xf6, 0x47, 0xe1, 0x63, 0xa1, 0xd2, 0xc0, 0x42, 0x62, 0xc0, 0x09, 0xa1, 0x07, - 0xb4, 0x5b, 0xec, 0x85, 0xaa, 0xb4, 0x5d, 0xf5, 0x42, 0xb6, 0x65, 0xd5, 0x03, 0x2a, 0x0a, 0x42, - 0x95, 0xda, 0x43, 0x34, 0x89, 0x07, 0x33, 0xc2, 0xf1, 0xb0, 0x9e, 0x49, 0xca, 0x5e, 0xab, 0x4a, - 0x3d, 0x55, 0xda, 0x63, 0xaf, 0xfd, 0x0f, 0x7a, 0xe8, 0x1f, 0xb1, 0xea, 0x69, 0xd5, 0x53, 0x4f, - 0x55, 0x05, 0x87, 0xfe, 0x1b, 0x55, 0x3c, 0x63, 0xc7, 0x89, 0xc7, 0x89, 0x43, 0x7b, 0xc3, 0x33, - 0xef, 0x7b, 0xef, 0x7b, 0x23, 0x3c, 0xdf, 0x73, 0xa0, 0x72, 0x1d, 0xb2, 0x1e, 0x09, 0x70, 0xd0, - 0x26, 0x0e, 0xb9, 0x69, 0x5f, 0xe2, 0xc0, 0x23, 0x4e, 0x6f, 0xdf, 0x11, 0x37, 0xf6, 0x75, 0xc8, - 0x04, 0x43, 0x6b, 0x03, 0x80, 0x1d, 0x03, 0xec, 0xde, 0xbe, 0x69, 0xb5, 0x19, 0xef, 0x30, 0xee, - 0xb4, 0x30, 0xef, 0x17, 0xb4, 0x88, 0xc0, 0xfb, 0x4e, 0x9b, 0xd1, 0x40, 0xd6, 0x99, 0xeb, 0x6a, - 0xbf, 0xc3, 0xbd, 0x3e, 0x5f, 0x87, 0x7b, 0x6a, 0xa3, 0x2c, 0x37, 0x9a, 0xd1, 0x93, 0x23, 0x1f, - 0xd4, 0xd6, 0xaa, 0xc7, 0x3c, 0x26, 0xd7, 0xfb, 0x7f, 0xa9, 0xd5, 0x9d, 0x9c, 0x16, 0x3b, 0x38, - 0xbc, 0x22, 0x62, 0x02, 0xe8, 0x1a, 0x87, 0xb8, 0xa3, 0xf8, 0x6b, 0x8f, 0x60, 0xe5, 0x84, 0x7b, - 0xcf, 0x43, 0x82, 0x05, 0x39, 0xe2, 0x57, 0x0d, 0xf2, 0xb2, 0x4b, 0xb8, 0xa8, 0xad, 0xc1, 0xea, - 0xf0, 0x32, 0xbf, 0x66, 0x01, 0x27, 0x43, 0xf0, 0x3a, 0x75, 0x75, 0xf0, 0x68, 0x59, 0xc1, 0xd7, - 0xe1, 0x51, 0x7f, 0xbd, 0xdf, 0x82, 0xff, 0x65, 0xe8, 0x92, 0x30, 0x2e, 0x28, 0xc1, 0xda, 0xe8, - 0x86, 0x2a, 0x59, 0x05, 0x74, 0xc2, 0xbd, 0x63, 0xea, 0xfb, 0x75, 0xea, 0xf2, 0x18, 0x2f, 0x75, - 0x07, 0xab, 0x19, 0xf0, 0x11, 0xbf, 0xd2, 0x80, 0xe5, 0xaa, 0x02, 0x4b, 0xcd, 0x93, 0xe8, 0x88, - 0xce, 0x88, 0x10, 0x3e, 0x89, 0x0b, 0xca, 0xb0, 0x9e, 0xd9, 0x51, 0x45, 0x26, 0x94, 0x92, 0xad, - 0xaf, 0xa8, 0xb8, 0x74, 0x43, 0xfc, 0x6d, 0x5c, 0xb6, 0x01, 0x65, 0xcd, 0x9e, 0x2a, 0xac, 0xc0, - 0x56, 0xb2, 0x79, 0x7e, 0xed, 0x62, 0x41, 0x3e, 0x23, 0x02, 0x53, 0x3f, 0xe9, 0xb2, 0x0a, 0x56, - 0x1e, 0x20, 0x97, 0xe2, 0xf3, 0x00, 0xb7, 0x7c, 0xe2, 0xe6, 0x53, 0x24, 0x00, 0x45, 0x51, 0x83, - 0xea, 0x08, 0xe2, 0x9c, 0x93, 0x70, 0xd8, 0xfd, 0x0e, 0x6c, 0x8f, 0xc1, 0x28, 0xa2, 0x34, 0xe8, - 0x04, 0x07, 0xd8, 0x23, 0xa7, 0x24, 0xec, 0x50, 0xce, 0x29, 0x0b, 0x12, 0x4b, 0xef, 0x41, 0x6d, - 0x1c, 0x48, 0x51, 0xa5, 0xbb, 0x96, 0xa8, 0x06, 0x79, 0x79, 0x24, 0x44, 0x98, 0xf0, 0x6c, 0x43, - 0x25, 0x17, 0xa1, 0x48, 0x7e, 0x36, 0xa2, 0xc3, 0x7f, 0xc1, 0x7a, 0xf2, 0xbf, 0x4e, 0x82, 0x15, - 0x01, 0x3a, 0x84, 0x05, 0xdc, 0x15, 0x97, 0x2c, 0xa4, 0xe2, 0x55, 0xc9, 0xa8, 0x1a, 0xbb, 0x0b, - 0xf5, 0xd2, 0xef, 0xbf, 0xee, 0xad, 0xaa, 0x57, 0xeb, 0xc8, 0x75, 0x43, 0xc2, 0xf9, 0x99, 0x08, - 0x69, 0xe0, 0x35, 0x06, 0x50, 0xf4, 0x29, 0xcc, 0xc9, 0x57, 0xa8, 0xf4, 0xbf, 0xaa, 0xb1, 0xbb, - 0x78, 0x60, 0xd9, 0xfa, 0x57, 0xdd, 0x96, 0x72, 0xf5, 0x07, 0x6f, 0xfe, 0xac, 0xcc, 0x34, 0x54, - 0xcd, 0xb3, 0xe5, 0xef, 0xfe, 0xfe, 0xe5, 0xf1, 0x80, 0xad, 0xb6, 0x09, 0xa6, 0xae, 0x45, 0xe5, - 0xe0, 0x37, 0x88, 0xfe, 0xeb, 0x5e, 0xb0, 0x9e, 0xb4, 0x78, 0x4c, 0x08, 0xff, 0xb7, 0xfd, 0x6f, - 0xc0, 0x82, 0xec, 0xa5, 0x49, 0xdd, 0xc8, 0xc2, 0x52, 0x63, 0x5e, 0x2e, 0x7c, 0xe1, 0xa2, 0x73, - 0x58, 0xc7, 0xae, 0xdb, 0xbc, 0x20, 0xa4, 0xd9, 0x8e, 0x1a, 0x6a, 0x62, 0x7e, 0xd5, 0xbc, 0xf0, - 0xb1, 0x28, 0xcd, 0x56, 0x67, 0x77, 0x17, 0x0f, 0xca, 0xb6, 0xe2, 0xef, 0x5f, 0x60, 0xb6, 0xba, - 0xc0, 0xec, 0xe7, 0x8c, 0x06, 0xca, 0xe8, 0x0a, 0x76, 0xdd, 0x63, 0x42, 0x92, 0x6b, 0xe1, 0xd8, - 0xc7, 0x02, 0x7d, 0x03, 0x66, 0x48, 0x3a, 0xac, 0x47, 0xb4, 0xcc, 0x0f, 0x8a, 0x31, 0xaf, 0x49, - 0x8a, 0x0c, 0x79, 0xb6, 0xe7, 0x16, 0x75, 0x25, 0xf3, 0xff, 0xef, 0xd1, 0x73, 0x9d, 0xba, 0xf9, - 0x3d, 0x27, 0xcc, 0x73, 0xf7, 0xeb, 0x39, 0x26, 0x6f, 0x83, 0x15, 0xf7, 0xcc, 0x89, 0xef, 0x93, - 0xb0, 0xc9, 0xa3, 0x77, 0xa9, 0x43, 0x02, 0x21, 0x05, 0xde, 0x29, 0x26, 0x60, 0xca, 0xd6, 0xcf, - 0x22, 0x92, 0xb3, 0x84, 0x23, 0x12, 0xa1, 0xb0, 0x9d, 0x72, 0x90, 0xa3, 0x33, 0x5f, 0x4c, 0x67, - 0x2b, 0x31, 0xa2, 0x95, 0x0a, 0xa0, 0x9a, 0xef, 0x27, 0xc4, 0x82, 0x32, 0x5e, 0x5a, 0x88, 0x94, - 0xaa, 0x79, 0xaf, 0xcb, 0x31, 0x21, 0x8d, 0x3e, 0x50, 0x09, 0x6e, 0xea, 0x8d, 0x45, 0x10, 0x8e, - 0x04, 0xec, 0x8c, 0xb5, 0xa6, 0x24, 0x61, 0x2a, 0xc9, 0x4a, 0xae, 0x47, 0xa5, 0x8a, 0x61, 0x2b, - 0x76, 0xd9, 0xea, 0xbe, 0xd2, 0x1c, 0xe6, 0x62, 0xb1, 0xc3, 0x2c, 0x4b, 0x6f, 0xf5, 0x3e, 0xc7, - 0xc8, 0x41, 0x7a, 0x50, 0x4d, 0x19, 0xd3, 0xab, 0x3c, 0x2c, 0xa6, 0xb2, 0x99, 0xd8, 0xd1, 0x09, - 0xf9, 0x50, 0xc9, 0xf5, 0xa2, 0x4e, 0x6f, 0x69, 0xaa, 0xd3, 0xdb, 0xd0, 0x9a, 0x52, 0x27, 0x17, - 0x42, 0x6d, 0x9c, 0x2d, 0x25, 0xb8, 0x3c, 0x95, 0xa0, 0x95, 0xe7, 0x4f, 0x6a, 0x66, 0xae, 0x5a, - 0x39, 0xa6, 0x47, 0xee, 0xd2, 0xcc, 0xa8, 0x90, 0xd3, 0xed, 0x34, 0xca, 0x3f, 0xff, 0xc1, 0xa8, - 0x90, 0x41, 0x6a, 0xd2, 0xa8, 0x90, 0x72, 0xf1, 0xa8, 0x90, 0x35, 0xf9, 0xa3, 0x62, 0xb8, 0x45, - 0xe9, 0xe0, 0xe0, 0xa7, 0x65, 0x98, 0x3d, 0xe1, 0x1e, 0xba, 0x80, 0x85, 0xe4, 0x7a, 0x44, 0x4f, - 0x72, 0x67, 0x53, 0x36, 0xcf, 0x99, 0xef, 0x17, 0x03, 0x4b, 0xbd, 0x81, 0x4e, 0x9d, 0xba, 0x05, - 0x74, 0x06, 0x41, 0xb0, 0x80, 0x4e, 0x2a, 0x1e, 0x22, 0x1f, 0x16, 0x53, 0x11, 0x10, 0xed, 0x8d, - 0x2b, 0xce, 0x64, 0x48, 0xd3, 0x2e, 0x0a, 0x57, 0x6a, 0x6d, 0x98, 0x8f, 0x03, 0x24, 0x7a, 0x3c, - 0xa6, 0x76, 0x24, 0x7b, 0x9a, 0x4f, 0x0a, 0x61, 0x87, 0x45, 0xfa, 0xc1, 0x73, 0xa2, 0x48, 0x2a, - 0xb3, 0x4e, 0x14, 0x49, 0x27, 0x59, 0xc4, 0xe0, 0x61, 0x3a, 0xac, 0xa2, 0x71, 0x27, 0xa1, 0xc9, - 0xbb, 0xa6, 0x53, 0x18, 0xaf, 0x04, 0xbb, 0xb0, 0x3c, 0x1c, 0x73, 0xd1, 0xd3, 0x89, 0x14, 0x23, - 0x69, 0xd9, 0xdc, 0x9f, 0xa2, 0x42, 0xc9, 0x7e, 0x6f, 0xc0, 0x8a, 0x26, 0x20, 0xa3, 0x0f, 0x27, - 0x52, 0xe9, 0x12, 0xb7, 0x79, 0x38, 0x6d, 0x59, 0x4e, 0x1b, 0x2a, 0x64, 0x17, 0x6e, 0x63, 0x38, - 0xb5, 0x17, 0x6e, 0x63, 0x24, 0xcb, 0xa3, 0x1f, 0x0d, 0x58, 0xd3, 0xa7, 0x74, 0xf4, 0x71, 0x41, - 0xca, 0x4c, 0xf8, 0x37, 0x3f, 0xb9, 0x47, 0xa5, 0xea, 0xe7, 0xb5, 0x01, 0xeb, 0x39, 0x59, 0x1f, - 0x4d, 0xa6, 0xcd, 0xfb, 0x88, 0x30, 0x9f, 0xdd, 0xa7, 0x54, 0xb5, 0xf4, 0x83, 0x01, 0xab, 0xba, - 0xcf, 0x06, 0x74, 0x58, 0x90, 0x74, 0xe4, 0x4b, 0xc4, 0xfc, 0x68, 0xea, 0x3a, 0xd5, 0xc9, 0x0d, - 0xbc, 0x3b, 0x12, 0xfc, 0xd1, 0xb8, 0x17, 0x40, 0xff, 0x1d, 0x63, 0x1e, 0x4c, 0x53, 0xa2, 0x94, - 0x43, 0x58, 0x1a, 0x9a, 0x83, 0xc8, 0x19, 0x4f, 0x92, 0xf9, 0xfa, 0x30, 0x9f, 0x16, 0x2f, 0x18, - 0x72, 0x9b, 0x9e, 0x5d, 0x93, 0xdc, 0x6a, 0x46, 0xf1, 0x24, 0xb7, 0xba, 0xd1, 0x58, 0x27, 0x6f, - 0x6e, 0x2d, 0xe3, 0xed, 0xad, 0x65, 0xfc, 0x75, 0x6b, 0x19, 0xaf, 0xef, 0xac, 0x99, 0xb7, 0x77, - 0xd6, 0xcc, 0x1f, 0x77, 0xd6, 0x0c, 0x94, 0x29, 0xcb, 0xe1, 0x3b, 0x35, 0xbe, 0xb6, 0x3d, 0x2a, - 0x2e, 0xbb, 0x2d, 0xbb, 0xcd, 0x3a, 0xce, 0x00, 0xb4, 0x47, 0x59, 0xea, 0xc9, 0xb9, 0x49, 0x7e, - 0x36, 0x69, 0xcd, 0x45, 0xbf, 0x96, 0x7c, 0xf0, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfd, 0x92, - 0x12, 0x76, 0x1c, 0x12, 0x00, 0x00, + // 1253 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0x4f, 0x4f, 0xe3, 0x46, + 0x1b, 0x27, 0x0b, 0xcb, 0x92, 0x87, 0x85, 0x5d, 0xcd, 0x06, 0x48, 0x0c, 0x38, 0x21, 0xbc, 0x87, + 0x57, 0xbb, 0xc5, 0x59, 0xa8, 0x4a, 0x5b, 0xd4, 0x0b, 0xa1, 0x0d, 0xda, 0x03, 0x2a, 0x0a, 0x42, + 0x95, 0xda, 0x43, 0x34, 0x89, 0x07, 0x63, 0xe1, 0x78, 0x58, 0x8f, 0xc9, 0xb2, 0xd7, 0xaa, 0x52, + 0x4f, 0x95, 0xf6, 0xd8, 0x6b, 0xbf, 0x41, 0x0f, 0xbd, 0xb4, 0x9f, 0x60, 0xd5, 0xd3, 0xaa, 0xa7, + 0x9e, 0xaa, 0x0a, 0x0e, 0xfd, 0x1a, 0x95, 0x67, 0xc6, 0x8e, 0x1d, 0xdb, 0xb1, 0x43, 0xf7, 0x86, + 0x3d, 0xbf, 0xe7, 0xf7, 0x67, 0xb0, 0xe7, 0x79, 0x1c, 0xa8, 0x5e, 0x3a, 0x74, 0x40, 0x6c, 0x6c, + 0xf7, 0x48, 0x83, 0x5c, 0xf7, 0xce, 0xb1, 0x6d, 0x90, 0xc6, 0x60, 0xbb, 0xe1, 0x5e, 0x6b, 0x97, + 0x0e, 0x75, 0x29, 0x5a, 0x1e, 0x02, 0x34, 0x1f, 0xa0, 0x0d, 0xb6, 0x15, 0xb5, 0x47, 0x59, 0x9f, + 0xb2, 0x46, 0x17, 0x33, 0xaf, 0xa0, 0x4b, 0x5c, 0xbc, 0xdd, 0xe8, 0x51, 0xd3, 0x16, 0x75, 0xca, + 0x8a, 0x5c, 0xef, 0x33, 0xc3, 0xe3, 0xeb, 0x33, 0x43, 0x2e, 0x54, 0xc4, 0x42, 0x87, 0x5f, 0x35, + 0xc4, 0x85, 0x5c, 0x2a, 0x19, 0xd4, 0xa0, 0xe2, 0xbe, 0xf7, 0x97, 0xbc, 0xbb, 0x99, 0x62, 0xb1, + 0x8f, 0x9d, 0x0b, 0xe2, 0x66, 0x80, 0xa8, 0xa3, 0x13, 0x87, 0x65, 0x80, 0x2e, 0xb1, 0x83, 0xfb, + 0x12, 0x54, 0xff, 0xad, 0x00, 0x4f, 0x8e, 0x98, 0x71, 0xe0, 0x10, 0xec, 0x92, 0x7d, 0x76, 0xd1, + 0x26, 0x2f, 0xaf, 0x08, 0x73, 0xd1, 0x01, 0x14, 0x31, 0xbb, 0xe8, 0x70, 0xc2, 0x72, 0xa1, 0x56, + 0xf8, 0xff, 0xfc, 0x4e, 0x4d, 0x4b, 0xde, 0x1c, 0x6d, 0x9f, 0x5d, 0x7c, 0xe9, 0xe1, 0x9a, 0x33, + 0x6f, 0xff, 0xaa, 0x4e, 0xb5, 0xe7, 0xb0, 0xbc, 0x46, 0x87, 0x80, 0x38, 0x41, 0xa7, 0xe7, 0xd1, + 0x9b, 0xd4, 0xee, 0x9c, 0x11, 0x52, 0xbe, 0xc7, 0xd9, 0x2a, 0x9a, 0xdc, 0x0c, 0x6f, 0x4b, 0x35, + 0xb9, 0xa5, 0xda, 0x01, 0x35, 0xed, 0xf6, 0x63, 0x5e, 0x74, 0x20, 0x6b, 0x5a, 0x84, 0xec, 0x2d, + 0x7d, 0xfb, 0xcf, 0xcf, 0x4f, 0x1f, 0x07, 0x86, 0x34, 0x46, 0x2c, 0x8b, 0x38, 0xf5, 0x6d, 0x28, + 0x45, 0xbd, 0xb3, 0x4b, 0x6a, 0x33, 0x82, 0x2a, 0x30, 0x27, 0x74, 0x4d, 0x9d, 0x7b, 0x9f, 0x69, + 0x3f, 0xe0, 0xd7, 0x2f, 0xf4, 0xfa, 0xaf, 0xe1, 0xbc, 0x4d, 0x53, 0x0f, 0xe5, 0xed, 0x9a, 0x7a, + 0xbe, 0xbc, 0x4d, 0x53, 0x8f, 0xe4, 0xed, 0xca, 0xeb, 0xf7, 0x97, 0xb7, 0xe4, 0xe5, 0x7d, 0x14, + 0x18, 0xd2, 0xba, 0x57, 0xaf, 0x47, 0xe2, 0x72, 0xeb, 0xd9, 0x71, 0x6d, 0x58, 0xf2, 0x4a, 0xbc, + 0x08, 0x16, 0xf7, 0xe8, 0xe7, 0xd5, 0xe0, 0x3e, 0x7d, 0x65, 0xcb, 0xac, 0xc5, 0x66, 0xf9, 0x8f, + 0x5f, 0xb6, 0x4a, 0xd2, 0xe0, 0xbe, 0xae, 0x3b, 0x84, 0xb1, 0x13, 0xd7, 0x31, 0x6d, 0xa3, 0x2d, + 0x60, 0x11, 0x8d, 0x7b, 0x11, 0x8d, 0x3d, 0xf0, 0xcc, 0x0a, 0x58, 0xbd, 0x0c, 0xcb, 0xa3, 0x7a, + 0xc2, 0x64, 0xbd, 0x04, 0xe8, 0x88, 0x19, 0x2d, 0xd3, 0xb2, 0x9a, 0xa6, 0xce, 0xa4, 0x8d, 0xfa, + 0x12, 0xff, 0x6f, 0x0c, 0xef, 0xc6, 0xc0, 0xfb, 0xec, 0x22, 0x01, 0x2c, 0xee, 0x4a, 0xb0, 0xd0, + 0x3c, 0xe2, 0xef, 0xc7, 0x09, 0x71, 0x5d, 0x8b, 0xf8, 0x05, 0x15, 0x58, 0x89, 0xad, 0xc8, 0x22, + 0x05, 0xca, 0xc1, 0xd2, 0x57, 0xa6, 0x7b, 0xae, 0x3b, 0xf8, 0x95, 0x5f, 0xb6, 0x0a, 0x95, 0x84, + 0x35, 0x59, 0x58, 0x85, 0xf5, 0x60, 0xf1, 0xf4, 0x52, 0xc7, 0x2e, 0xf9, 0x9c, 0xb8, 0xd8, 0xb4, + 0x02, 0x97, 0x35, 0x50, 0xd3, 0x00, 0xa9, 0x14, 0x5f, 0xd8, 0xb8, 0x6b, 0x11, 0x3d, 0x9d, 0x22, + 0x00, 0x48, 0x8a, 0x3a, 0xd4, 0x46, 0x10, 0xa7, 0x8c, 0x38, 0xd1, 0xf4, 0x9b, 0xb0, 0x31, 0x06, + 0x23, 0x89, 0xc2, 0xa0, 0x23, 0x6c, 0x63, 0x83, 0x1c, 0x13, 0xa7, 0x6f, 0x32, 0x66, 0x52, 0x3b, + 0x88, 0xf4, 0x3f, 0xa8, 0x8f, 0x03, 0x49, 0xaa, 0xb0, 0x6b, 0x81, 0x6a, 0x93, 0x97, 0xfb, 0xae, + 0xeb, 0x04, 0x3c, 0x1b, 0x50, 0x4d, 0x45, 0x48, 0x92, 0x9f, 0x0a, 0x7c, 0xf3, 0x0f, 0xe9, 0x40, + 0x3c, 0xe7, 0x02, 0xec, 0x3f, 0xb5, 0xbb, 0x50, 0xc4, 0x57, 0xee, 0x39, 0x75, 0x4c, 0xf7, 0x75, + 0xe6, 0x93, 0x3b, 0x84, 0xa2, 0xcf, 0x60, 0x56, 0x9c, 0x9f, 0xf2, 0x65, 0x54, 0xd3, 0x5e, 0x6d, + 0x21, 0x27, 0x5f, 0x6c, 0x59, 0xb3, 0xb7, 0xe8, 0x3d, 0xe0, 0x43, 0xb6, 0xfa, 0x1a, 0x28, 0x49, + 0x16, 0x65, 0x82, 0xdf, 0x81, 0x3f, 0x75, 0x87, 0x74, 0x20, 0x22, 0xb6, 0x08, 0x61, 0xff, 0xd5, + 0xff, 0x2a, 0x14, 0x85, 0x17, 0xff, 0xf5, 0x5b, 0x68, 0xcf, 0x89, 0x1b, 0x2f, 0x74, 0x74, 0x0a, + 0x2b, 0x58, 0xd7, 0xbd, 0xa3, 0x46, 0x9c, 0x3b, 0xa4, 0xe3, 0x1d, 0x94, 0x67, 0x16, 0x76, 0xcb, + 0xd3, 0xb5, 0xe9, 0xb1, 0x47, 0x8f, 0x0c, 0xfa, 0x04, 0xeb, 0x7a, 0x8b, 0x90, 0xe0, 0x20, 0x6d, + 0x59, 0xd8, 0x45, 0xdf, 0x80, 0xe2, 0x90, 0x3e, 0x1d, 0x90, 0x44, 0xe6, 0x99, 0x7c, 0xcc, 0xcb, + 0x82, 0x22, 0x46, 0x1e, 0xf7, 0xec, 0x1d, 0x76, 0x9c, 0xf9, 0xfe, 0x1d, 0x3c, 0x37, 0x4d, 0x3d, + 0xdd, 0x73, 0xc0, 0x3c, 0x7b, 0x37, 0xcf, 0x3e, 0x79, 0x0f, 0x54, 0xdf, 0xb3, 0xe8, 0x3f, 0x1d, + 0xc6, 0xdf, 0xa5, 0x3e, 0xb1, 0x5d, 0x21, 0xf0, 0x20, 0x9f, 0x80, 0x22, 0xac, 0x9f, 0x70, 0x92, + 0x93, 0x80, 0x83, 0x8b, 0x98, 0xb0, 0x11, 0x4a, 0x90, 0xa2, 0x33, 0x97, 0x4f, 0x67, 0x3d, 0x08, + 0x92, 0x28, 0x65, 0x43, 0x2d, 0x3d, 0x8f, 0xe3, 0xf5, 0x22, 0x56, 0x2e, 0x72, 0xa5, 0xd4, 0x4e, + 0xd8, 0x22, 0xa4, 0xed, 0x01, 0xa5, 0xe0, 0x5a, 0x72, 0x30, 0x0e, 0x61, 0xc8, 0x85, 0xcd, 0xb1, + 0xd1, 0xa4, 0x24, 0x4c, 0x24, 0x59, 0x4d, 0xcd, 0x28, 0x55, 0x31, 0xac, 0xfb, 0x29, 0x79, 0x17, + 0x8d, 0x6d, 0xe6, 0x7c, 0xbe, 0xcd, 0xac, 0x88, 0x6c, 0x4d, 0x8f, 0x63, 0x64, 0x23, 0x0d, 0xa8, + 0x85, 0x82, 0x25, 0xab, 0x3c, 0xcc, 0xa7, 0xb2, 0x16, 0xc4, 0x49, 0x12, 0xb2, 0xa0, 0x9a, 0x9a, + 0x45, 0xee, 0xde, 0xc2, 0x44, 0xbb, 0xb7, 0x9a, 0x18, 0x4a, 0xee, 0x9c, 0x03, 0xf5, 0x71, 0xb1, + 0xa4, 0xe0, 0xe2, 0x44, 0x82, 0x6a, 0x5a, 0x3e, 0xa1, 0x19, 0x3b, 0x6a, 0x45, 0x9b, 0x1e, 0x39, + 0x4b, 0x63, 0xad, 0x42, 0x74, 0xb7, 0x63, 0x3e, 0xd7, 0xbe, 0x87, 0x56, 0x21, 0x06, 0xe4, 0xac, + 0x56, 0x21, 0xe4, 0xfc, 0x56, 0x21, 0x6a, 0xd2, 0x5b, 0x45, 0xd4, 0xa2, 0x48, 0xb0, 0xf3, 0xe3, + 0x22, 0x4c, 0x1f, 0x31, 0x03, 0x9d, 0x41, 0x31, 0x38, 0x1e, 0xd1, 0xb3, 0xd4, 0xde, 0x14, 0x1f, + 0xd3, 0x95, 0x0f, 0xf2, 0x81, 0xe5, 0xa0, 0x18, 0xe8, 0x34, 0x4d, 0x3d, 0x87, 0xce, 0x70, 0x3c, + 0xce, 0xa1, 0x13, 0x1e, 0x48, 0x2d, 0x98, 0x0f, 0x8d, 0x80, 0x68, 0x6b, 0x5c, 0x71, 0x6c, 0x34, + 0x55, 0xb4, 0xbc, 0x70, 0xa9, 0xd6, 0x83, 0x39, 0x7f, 0x80, 0x44, 0x4f, 0xc7, 0xd4, 0x8e, 0xcc, + 0x9e, 0xca, 0xb3, 0x5c, 0xd8, 0xa8, 0x88, 0x37, 0x78, 0x66, 0x8a, 0x84, 0x66, 0xd6, 0x4c, 0x91, + 0xf0, 0x24, 0x8b, 0x28, 0x3c, 0x0c, 0x0f, 0xab, 0x68, 0xdc, 0x4e, 0x24, 0xcc, 0xbb, 0x4a, 0x23, + 0x37, 0x5e, 0x0a, 0x5e, 0xc1, 0x62, 0x74, 0xcc, 0x45, 0xcf, 0x33, 0x29, 0x46, 0xa6, 0x65, 0x65, + 0x7b, 0x82, 0x0a, 0x29, 0xfb, 0x9d, 0xf7, 0x11, 0x16, 0x1f, 0x90, 0xd1, 0x47, 0x99, 0x54, 0x49, + 0x13, 0xb7, 0xb2, 0x3b, 0x69, 0x59, 0x8a, 0x0d, 0x39, 0x64, 0xe7, 0xb6, 0x11, 0x9d, 0xda, 0x73, + 0xdb, 0x18, 0x99, 0xe5, 0xd1, 0x0f, 0x05, 0x58, 0x4e, 0x9e, 0xd2, 0xd1, 0x27, 0x39, 0x29, 0x63, + 0xc3, 0xbf, 0xf2, 0xe9, 0x1d, 0x2a, 0xa5, 0x9f, 0x37, 0x05, 0x58, 0x49, 0x99, 0xf5, 0x51, 0x36, + 0x6d, 0xda, 0x47, 0x84, 0xb2, 0x77, 0x97, 0x52, 0x69, 0xe9, 0xfb, 0x02, 0x94, 0x92, 0x3e, 0x1b, + 0xd0, 0x6e, 0x4e, 0xd2, 0x91, 0x2f, 0x11, 0xe5, 0xe3, 0x89, 0xeb, 0xa4, 0x93, 0x6b, 0x78, 0x34, + 0x32, 0xf8, 0xa3, 0x71, 0x2f, 0x40, 0xf2, 0x77, 0x8c, 0xb2, 0x33, 0x49, 0x89, 0x54, 0x76, 0x60, + 0x21, 0xd2, 0x07, 0x51, 0x63, 0x3c, 0x49, 0xec, 0xeb, 0x43, 0x79, 0x9e, 0xbf, 0x20, 0x92, 0x36, + 0xdc, 0xbb, 0xb2, 0xd2, 0x26, 0xb4, 0xe2, 0xac, 0xb4, 0x49, 0xad, 0xb1, 0x49, 0xde, 0xde, 0xa8, + 0x85, 0x77, 0x37, 0x6a, 0xe1, 0xef, 0x1b, 0xb5, 0xf0, 0xe6, 0x56, 0x9d, 0x7a, 0x77, 0xab, 0x4e, + 0xfd, 0x79, 0xab, 0x4e, 0x41, 0xc5, 0xa4, 0x29, 0x7c, 0xc7, 0x85, 0xaf, 0x35, 0xc3, 0x74, 0xcf, + 0xaf, 0xba, 0x5a, 0x8f, 0xf6, 0x1b, 0x43, 0xd0, 0x96, 0x49, 0x43, 0x57, 0x8d, 0xeb, 0xe0, 0xe7, + 0xb0, 0xee, 0x2c, 0xff, 0x15, 0xec, 0xc3, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x99, 0x1f, 0x8f, + 0x47, 0x19, 0x14, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2049,6 +2129,28 @@ func (m *MsgCreateAskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.OrderCreationFee != nil { + { + size, err := m.OrderCreationFee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + { + size, err := m.AskOrder.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa return len(dAtA) - i, nil } @@ -2072,6 +2174,11 @@ func (m *MsgCreateAskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.OrderId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.OrderId)) + i-- + dAtA[i] = 0x8 + } return len(dAtA) - i, nil } @@ -2095,6 +2202,28 @@ func (m *MsgCreateBidRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.OrderCreationFee != nil { + { + size, err := m.OrderCreationFee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + { + size, err := m.BidOrder.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa return len(dAtA) - i, nil } @@ -2118,6 +2247,11 @@ func (m *MsgCreateBidResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.OrderId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.OrderId)) + i-- + dAtA[i] = 0x8 + } return len(dAtA) - i, nil } @@ -2141,6 +2275,18 @@ func (m *MsgCancelOrderRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.OrderId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.OrderId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintTx(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2950,6 +3096,12 @@ func (m *MsgCreateAskRequest) Size() (n int) { } var l int _ = l + l = m.AskOrder.Size() + n += 1 + l + sovTx(uint64(l)) + if m.OrderCreationFee != nil { + l = m.OrderCreationFee.Size() + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -2959,6 +3111,9 @@ func (m *MsgCreateAskResponse) Size() (n int) { } var l int _ = l + if m.OrderId != 0 { + n += 1 + sovTx(uint64(m.OrderId)) + } return n } @@ -2968,6 +3123,12 @@ func (m *MsgCreateBidRequest) Size() (n int) { } var l int _ = l + l = m.BidOrder.Size() + n += 1 + l + sovTx(uint64(l)) + if m.OrderCreationFee != nil { + l = m.OrderCreationFee.Size() + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -2977,6 +3138,9 @@ func (m *MsgCreateBidResponse) Size() (n int) { } var l int _ = l + if m.OrderId != 0 { + n += 1 + sovTx(uint64(m.OrderId)) + } return n } @@ -2986,6 +3150,13 @@ func (m *MsgCancelOrderRequest) Size() (n int) { } var l int _ = l + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.OrderId != 0 { + n += 1 + sovTx(uint64(m.OrderId)) + } return n } @@ -3340,6 +3511,75 @@ func (m *MsgCreateAskRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgCreateAskRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AskOrder", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.AskOrder.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderCreationFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.OrderCreationFee == nil { + m.OrderCreationFee = &types.Coin{} + } + if err := m.OrderCreationFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -3390,6 +3630,25 @@ func (m *MsgCreateAskResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgCreateAskResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) + } + m.OrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -3440,6 +3699,75 @@ func (m *MsgCreateBidRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgCreateBidRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BidOrder", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BidOrder.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderCreationFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.OrderCreationFee == nil { + m.OrderCreationFee = &types.Coin{} + } + if err := m.OrderCreationFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -3490,6 +3818,25 @@ func (m *MsgCreateBidResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgCreateBidResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) + } + m.OrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -3540,6 +3887,57 @@ func (m *MsgCancelOrderRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgCancelOrderRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) + } + m.OrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) From b1409f5c38f0427396b7af7094dd6f98825f15c7 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sat, 9 Sep 2023 17:34:50 -0600 Subject: [PATCH 095/309] [1658]: Impliment Msg funcs for create ask, create bid, and cancel order. Fix AskOrder.Validate() and BidOrder.Validate() to not panic if the Price is a zero-value coin. --- x/exchange/msg.go | 37 ++++++++--- x/exchange/msg_test.go | 134 ++++++++++++++++++++++++++++++++++++-- x/exchange/orders.go | 20 +++--- x/exchange/orders_test.go | 20 ++++++ 4 files changed, 186 insertions(+), 25 deletions(-) diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 2c843e5b0a..26df6c2fb9 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -26,33 +26,52 @@ var allRequestMsgs = []sdk.Msg{ } func (m MsgCreateAskRequest) ValidateBasic() error { - // TODO[1658]: MsgCreateAskRequest.ValidateBasic() + if err := m.AskOrder.Validate(); err != nil { + return err + } + if m.OrderCreationFee != nil { + if err := m.OrderCreationFee.Validate(); err != nil { + return fmt.Errorf("invalid order creation fee: %w", err) + } + } return nil } func (m MsgCreateAskRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgCreateAskRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.AskOrder.Seller) + return []sdk.AccAddress{addr} } func (m MsgCreateBidRequest) ValidateBasic() error { - // TODO[1658]: MsgCreateBidRequest.ValidateBasic() + if err := m.BidOrder.Validate(); err != nil { + return err + } + if m.OrderCreationFee != nil { + if err := m.OrderCreationFee.Validate(); err != nil { + return fmt.Errorf("invalid order creation fee: %w", err) + } + } return nil } func (m MsgCreateBidRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgCreateBidRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.BidOrder.Buyer) + return []sdk.AccAddress{addr} } func (m MsgCancelOrderRequest) ValidateBasic() error { - // TODO[1658]: MsgCancelOrderRequest.ValidateBasic() + if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil { + return fmt.Errorf("invalid owner: %w", err) + } + if m.OrderId == 0 { + return fmt.Errorf("invalid order id: cannot be zero") + } return nil } func (m MsgCancelOrderRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgCancelOrderRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Owner) + return []sdk.AccAddress{addr} } func (m MsgFillBidsRequest) ValidateBasic() error { diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index ca81ab0bc5..a41ef9c3b9 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -31,6 +31,16 @@ func TestAllMsgsGetSigners(t *testing.T) { emptyAddrErr := "empty address string is not allowed" msgMakers := []func(signer string) sdk.Msg{ + func(signer string) sdk.Msg { + return &MsgCreateAskRequest{AskOrder: AskOrder{Seller: signer}} + }, + func(signer string) sdk.Msg { + return &MsgCreateBidRequest{BidOrder: BidOrder{Buyer: signer}} + }, + func(signer string) sdk.Msg { + return &MsgCancelOrderRequest{Owner: signer} + }, + // TODO[1658]: Add the rest of the messages once they've actually been defined. func(signer string) sdk.Msg { return &MsgGovCreateMarketRequest{Authority: signer} }, @@ -40,7 +50,6 @@ func TestAllMsgsGetSigners(t *testing.T) { func(signer string) sdk.Msg { return &MsgGovUpdateParamsRequest{Authority: signer} }, - // TODO[1658]: Add the rest of the messages once they've actually been defined. } signerCases := []struct { @@ -122,11 +131,128 @@ func testValidateBasic(t *testing.T, msg sdk.Msg, expErr []string) { assertions.AssertErrorContents(t, err, expErr, "%T.ValidateBasic() error", msg) } -// TODO[1658]: func TestMsgCreateAskRequest_ValidateBasic(t *testing.T) +func TestMsgCreateAskRequest_ValidateBasic(t *testing.T) { + tests := []struct { + name string + msg MsgCreateAskRequest + expErr []string + }{ + { + name: "empty ask order", + msg: MsgCreateAskRequest{}, + expErr: []string{ + "invalid market id: ", + "invalid seller: ", + "invalid price: ", + "invalid assets: ", + }, + }, + { + name: "invalid fees", + msg: MsgCreateAskRequest{ + AskOrder: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("seller______________").String(), + Assets: sdk.NewCoins(sdk.NewInt64Coin("banana", 99)), + Price: sdk.NewInt64Coin("acorn", 12), + }, + OrderCreationFee: &sdk.Coin{Denom: "cactus", Amount: sdkmath.NewInt(-3)}, + }, + expErr: []string{"invalid order creation fee: negative coin amount: -3"}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} + +func TestMsgCreateBidRequest_ValidateBasic(t *testing.T) { + tests := []struct { + name string + msg MsgCreateBidRequest + expErr []string + }{ + { + name: "empty ask order", + msg: MsgCreateBidRequest{}, + expErr: []string{ + "invalid market id: ", + "invalid buyer: ", + "invalid price: ", + "invalid assets: ", + }, + }, + { + name: "invalid fees", + msg: MsgCreateBidRequest{ + BidOrder: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("buyer_______________").String(), + Assets: sdk.NewCoins(sdk.NewInt64Coin("banana", 99)), + Price: sdk.NewInt64Coin("acorn", 12), + }, + OrderCreationFee: &sdk.Coin{Denom: "cactus", Amount: sdkmath.NewInt(-3)}, + }, + expErr: []string{"invalid order creation fee: negative coin amount: -3"}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} -// TODO[1658]: func TestMsgCreateBidRequest_ValidateBasic(t *testing.T) +func TestMsgCancelOrderRequest_ValidateBasic(t *testing.T) { + tests := []struct { + name string + msg MsgCancelOrderRequest + expErr []string + }{ + { + name: "control", + msg: MsgCancelOrderRequest{ + Owner: sdk.AccAddress("owner_______________").String(), + OrderId: 1, + }, + expErr: nil, + }, + { + name: "missing owner", + msg: MsgCancelOrderRequest{ + Owner: "", + OrderId: 1, + }, + expErr: []string{"invalid owner: ", "empty address string is not allowed"}, + }, + { + name: "invalid owner", + msg: MsgCancelOrderRequest{ + Owner: "notgonnawork", + OrderId: 1, + }, + expErr: []string{"invalid owner: ", "decoding bech32 failed: invalid separator index -1"}, + }, + { + name: "order 0", + msg: MsgCancelOrderRequest{ + Owner: sdk.AccAddress("valid_owner_________").String(), + OrderId: 0, + }, + expErr: []string{"invalid order id: cannot be zero"}, + }, + } -// TODO[1658]: func TestMsgCancelOrderRequest_ValidateBasic(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} // TODO[1658]: func TestMsgFillBidsRequest_ValidateBasic(t *testing.T) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 39aa9e3f17..93c81086af 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -153,14 +153,12 @@ func (a AskOrder) Validate() error { // The price must not be zero and must be a valid coin. // The Coin.Validate() method allows the coin to be zero (but not negative). var priceDenom string - if a.Price.IsZero() { + if err := a.Price.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid price: %w", err)) + } else if a.Price.IsZero() { errs = append(errs, errors.New("invalid price: cannot be zero")) } else { - if err := a.Price.Validate(); err != nil { - errs = append(errs, fmt.Errorf("invalid price: %w", err)) - } else { - priceDenom = a.Price.Denom - } + priceDenom = a.Price.Denom } // The Coins.Validate method does NOT allow any coin entries to be zero (or negative). @@ -215,14 +213,12 @@ func (a BidOrder) Validate() error { // The price must not be zero and must be a valid coin. // The Coin.Validate() method allows the coin to be zero (but not negative). var priceDenom string - if a.Price.IsZero() { + if err := a.Price.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid price: %w", err)) + } else if a.Price.IsZero() { errs = append(errs, errors.New("invalid price: cannot be zero")) } else { - if err := a.Price.Validate(); err != nil { - errs = append(errs, fmt.Errorf("invalid price: %w", err)) - } else { - priceDenom = a.Price.Denom - } + priceDenom = a.Price.Denom } // The Coins.Validate method does NOT allow any coin entries to be zero (or negative). diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 4238a75457..5c0944906a 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -645,6 +645,16 @@ func TestAskOrder_Validate(t *testing.T) { }, exp: []string{"invalid price: ", "invalid denom: 7"}, }, + { + name: "zero-value price", + order: AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("seller_address______").String(), + Assets: coins("99bender"), + Price: sdk.Coin{}, + }, + exp: []string{"invalid price: invalid denom: "}, + }, { name: "zero amount in assets", order: AskOrder{ @@ -896,6 +906,16 @@ func TestBidOrder_Validate(t *testing.T) { }, exp: []string{"invalid price: ", "invalid denom: 7"}, }, + { + name: "zero-value price", + order: BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("buyer_address_______").String(), + Assets: coins("99bender"), + Price: sdk.Coin{}, + }, + exp: []string{"invalid price: invalid denom: "}, + }, { name: "zero amount in assets", order: BidOrder{ From 4afea0ef5c9d53dda2fcec5cab9b96e49ae11e20 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sat, 9 Sep 2023 23:29:28 -0600 Subject: [PATCH 096/309] [1658]: Create a ratio.ApplyToLoosely that doesn't require even divisibility. --- x/exchange/market.go | 48 ++++++++++++++++++--- x/exchange/market_test.go | 90 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 7 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index 8e2b5bdac6..a07188c588 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -6,6 +6,8 @@ import ( "sort" "strings" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -250,20 +252,52 @@ func (r FeeRatio) Equals(other FeeRatio) bool { return r.Price.Equal(other.Price) && r.Fee.Equal(other.Fee) } -// ApplyTo attempts to calculate the fee that results from applying this fee ratio to the provided price. -func (r FeeRatio) ApplyTo(price sdk.Coin) (sdk.Coin, error) { - rv := sdk.Coin{Denom: "", Amount: sdk.ZeroInt()} +// applyLooselyTo returns the amount that results from the application of this ratio to the given price. +// The second return value is whether rounding was needed. I.e. If price / ratio price * fee price is +// not a whole number, the returned amount is increased by one and the second return value will be true. +// If it is a whole number, the second return value is false. +// An error is returned if the price's denom does not equal the ratio's price denom, or if the ratio's price amount is zero. +func (r FeeRatio) applyLooselyTo(price sdk.Coin) (sdkmath.Int, bool, error) { if r.Price.Denom != price.Denom { - return rv, fmt.Errorf("cannot apply ratio %s to price %s: incorrect price denom", r, price) + return sdkmath.ZeroInt(), false, fmt.Errorf("cannot apply ratio %s to price %s: incorrect price denom", r, price) } if r.Price.Amount.IsZero() { - return rv, fmt.Errorf("cannot apply ratio %s to price %s: division by zero", r, price) + return sdkmath.ZeroInt(), false, fmt.Errorf("cannot apply ratio %s to price %s: division by zero", r, price) + } + rv := price.Amount.Mul(r.Fee.Amount) + mustRound := !rv.Mod(r.Price.Amount).IsZero() + rv = rv.Quo(r.Price.Amount) + if mustRound { + rv = rv.Add(sdkmath.OneInt()) } - if !price.Amount.Mod(r.Price.Amount).IsZero() { + return rv, mustRound, nil +} + +// ApplyTo attempts to calculate the fee that results from applying this fee ratio to the provided price. +func (r FeeRatio) ApplyTo(price sdk.Coin) (sdk.Coin, error) { + rv := sdk.Coin{Denom: "", Amount: sdk.ZeroInt()} + amt, wasRounded, err := r.applyLooselyTo(price) + if err != nil { + return rv, err + } + if wasRounded { return rv, fmt.Errorf("cannot apply ratio %s to price %s: price amount cannot be evenly divided by ratio price", r, price) } rv.Denom = r.Fee.Denom - rv.Amount = price.Amount.Quo(r.Price.Amount).Mul(r.Fee.Amount) + rv.Amount = amt + return rv, nil +} + +// ApplyToLoosely calculates the fee that results from applying this fee ratio to the provided price, allowing for the +// ratio to not evenly apply to the price. +func (r FeeRatio) ApplyToLoosely(price sdk.Coin) (sdk.Coin, error) { + rv := sdk.Coin{Denom: "", Amount: sdk.ZeroInt()} + amt, _, err := r.applyLooselyTo(price) + if err != nil { + return rv, err + } + rv.Denom = r.Fee.Denom + rv.Amount = amt return rv, nil } diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index b2f1b46131..4abc3abf44 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -1088,6 +1088,96 @@ func TestFeeRatio_ApplyTo(t *testing.T) { } } +func TestFeeRatio_ApplyToLoosely(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + bigCoin := func(amount string, denom string) sdk.Coin { + amt, ok := sdkmath.NewIntFromString(amount) + require.True(t, ok, "sdkmath.NewIntFromString(%q) ok result boolean", amount) + return sdk.Coin{Denom: denom, Amount: amt} + } + feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { + return FeeRatio{ + Price: coin(priceAmount, priceDenom), + Fee: coin(feeAmount, feeDenom), + } + } + + tests := []struct { + name string + ratio FeeRatio + price sdk.Coin + exp sdk.Coin + expErr string + }{ + { + name: "wrong denom", + ratio: feeRatio(1, "pdenom", 1, "fdenom"), + price: coin(1, "fdenom"), + expErr: "cannot apply ratio 1pdenom:1fdenom to price 1fdenom: incorrect price denom", + }, + { + name: "ratio price amount is zero", + ratio: feeRatio(0, "pdenom", 1, "fdenom"), + price: coin(1, "pdenom"), + expErr: "cannot apply ratio 0pdenom:1fdenom to price 1pdenom: division by zero", + }, + { + name: "price amount is less than ratio price amount", + ratio: feeRatio(14, "pdenom", 3, "fdenom"), + price: coin(7, "pdenom"), + exp: coin(2, "fdenom"), // 7 * 3 / 14 = 1.5 => 2 + }, + { + name: "price amount is not evenly divisible by ratio", + ratio: feeRatio(7, "pdenom", 3, "fdenom"), + price: coin(71, "pdenom"), + exp: coin(31, "fdenom"), // 71 * 3 / 7 = 30.4 => 31 + }, + { + name: "price equals ratio price", + ratio: feeRatio(55, "pdenom", 3, "fdenom"), + price: coin(55, "pdenom"), + exp: coin(3, "fdenom"), + }, + { + name: "three times ratio price", + ratio: feeRatio(13, "pdenom", 17, "fdenom"), + price: coin(39, "pdenom"), + exp: coin(51, "fdenom"), + }, + { + name: "very big price", + ratio: feeRatio(1_000_000_000, "pdenom", 3, "fdenom"), + price: bigCoin("1000000000000000000000", "pdenom"), + exp: coin(3_000_000_000_000, "fdenom"), + }, + { + name: "same price and fee denoms", + ratio: feeRatio(100, "nhash", 1, "nhash"), + price: coin(5_000_000_000, "nhash"), + exp: coin(50_000_000, "nhash"), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if len(tc.expErr) > 0 { + tc.exp = coin(0, "") + } + var actual sdk.Coin + var err error + testFunc := func() { + actual, err = tc.ratio.ApplyToLoosely(tc.price) + } + require.NotPanics(t, testFunc, "%s.ApplyToLoosely(%s)", tc.ratio, tc.price) + assertions.AssertErrorValue(t, err, tc.expErr, "%s.ApplyToLoosely(%s) error", tc.ratio, tc.price) + assert.Equal(t, tc.exp.String(), actual.String(), "%s.ApplyToLoosely(%s) result", tc.ratio, tc.price) + }) + } +} + func TestIntersectionOfFeeRatios(t *testing.T) { feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { return FeeRatio{ From a20d101cbf69c942bf0fa9150f78c068767b5dba Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sat, 9 Sep 2023 23:30:22 -0600 Subject: [PATCH 097/309] [1658]: Create GetHoldAmount methods on the Order, AskOrder, and BidOrder structs. --- x/exchange/orders.go | 50 +++++++++--- x/exchange/orders_test.go | 157 +++++++++++++++++++++++++++++++++++++- 2 files changed, 195 insertions(+), 12 deletions(-) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 93c81086af..260447e8ef 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -121,6 +121,20 @@ func (o Order) GetAssets() sdk.Coins { } } +// GetHoldAmount returns the total amount that should be on hold for this order. +func (o Order) GetHoldAmount() sdk.Coins { + switch v := o.GetOrder().(type) { + case *Order_AskOrder: + return v.AskOrder.GetHoldAmount() + case *Order_BidOrder: + return v.BidOrder.GetHoldAmount() + default: + // If HoldSettlementFee() is called without the order being set yet, it's a programming error, so panic. + // If it's a type without a case, the case needs to be added, so panic. + panic(fmt.Sprintf("GetHoldAmount() missing case for %T", v)) + } +} + // Validate returns an error if anything in this order is invalid. func (o Order) Validate() error { if o.OrderId == 0 { @@ -136,6 +150,15 @@ func (o Order) Validate() error { } } +// GetHoldAmount gets the amount to put on hold for this ask order. +func (a AskOrder) GetHoldAmount() sdk.Coins { + rv := a.Assets + if a.SellerSettlementFlatFee != nil && a.SellerSettlementFlatFee.Denom != a.Price.Denom { + rv = rv.Add(*a.SellerSettlementFlatFee) + } + return rv +} + // Validate returns an error if anything in this ask order is invalid. func (a AskOrder) Validate() error { var errs []error @@ -196,42 +219,47 @@ func (a AskOrder) Validate() error { return errors.Join(errs...) } +// GetHoldAmount gets the amount to put on hold for this ask order. +func (b BidOrder) GetHoldAmount() sdk.Coins { + return b.BuyerSettlementFees.Add(b.Price) +} + // Validate returns an error if anything in this ask order is invalid. -func (a BidOrder) Validate() error { +func (b BidOrder) Validate() error { var errs []error // The market id must be provided. - if a.MarketId == 0 { + if b.MarketId == 0 { errs = append(errs, errors.New("invalid market id: must not be zero")) } // The seller address must be valid and not empty. - if _, err := sdk.AccAddressFromBech32(a.Buyer); err != nil { + if _, err := sdk.AccAddressFromBech32(b.Buyer); err != nil { errs = append(errs, fmt.Errorf("invalid buyer: %w", err)) } // The price must not be zero and must be a valid coin. // The Coin.Validate() method allows the coin to be zero (but not negative). var priceDenom string - if err := a.Price.Validate(); err != nil { + if err := b.Price.Validate(); err != nil { errs = append(errs, fmt.Errorf("invalid price: %w", err)) - } else if a.Price.IsZero() { + } else if b.Price.IsZero() { errs = append(errs, errors.New("invalid price: cannot be zero")) } else { - priceDenom = a.Price.Denom + priceDenom = b.Price.Denom } // The Coins.Validate method does NOT allow any coin entries to be zero (or negative). // It does allow there to not be any entries, though, which we don't want to allow here. // We also don't want to allow the price denom to also be in the assets. - if err := a.Assets.Validate(); err != nil { + if err := b.Assets.Validate(); err != nil { errs = append(errs, fmt.Errorf("invalid assets: %w", err)) } else { switch { - case len(a.Assets) == 0: + case len(b.Assets) == 0: errs = append(errs, errors.New("invalid assets: must not be empty")) case len(priceDenom) > 0: - for _, asset := range a.Assets { + for _, asset := range b.Assets { if priceDenom == asset.Denom { errs = append(errs, fmt.Errorf("invalid assets: cannot contain price denom %s", priceDenom)) break @@ -240,9 +268,9 @@ func (a BidOrder) Validate() error { } } - if len(a.BuyerSettlementFees) > 0 { + if len(b.BuyerSettlementFees) > 0 { // If there are buyer settlement fees, they must all be valid and positive (non-zero). - if err := a.BuyerSettlementFees.Validate(); err != nil { + if err := b.BuyerSettlementFees.Validate(); err != nil { errs = append(errs, fmt.Errorf("invalid buyer settlement fees: %w", err)) } } diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 5c0944906a..8383d1525e 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -479,7 +479,58 @@ func TestOrder_GetAssets(t *testing.T) { assets = tc.order.GetAssets() } assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetAssets") - assert.Equal(t, tc.expected, assets, "GetAssets result") + assert.Equal(t, tc.expected.String(), assets.String(), "GetAssets result") + }) + } +} + +func TestOrder_GetHoldAmount(t *testing.T) { + tests := []struct { + name string + order *Order + expected sdk.Coins + expPanic string + }{ + { + name: "AskOrder", + order: NewOrder(1).WithAsk(&AskOrder{ + Assets: sdk.NewCoins(sdk.NewInt64Coin("acorns", 85)), + SellerSettlementFlatFee: &sdk.Coin{Denom: "bananas", Amount: sdkmath.NewInt(12)}, + }), + expected: sdk.NewCoins(sdk.NewInt64Coin("acorns", 85), sdk.NewInt64Coin("bananas", 12)), + }, + { + name: "BidOrder", + order: NewOrder(2).WithBid(&BidOrder{ + Price: sdk.NewInt64Coin("acorns", 85), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("bananas", 4), sdk.NewInt64Coin("cucumber", 7)), + }), + expected: sdk.NewCoins( + sdk.NewInt64Coin("acorns", 85), + sdk.NewInt64Coin("bananas", 4), + sdk.NewInt64Coin("cucumber", 7), + ), + }, + { + name: "nil inside order", + order: NewOrder(3), + expPanic: "GetHoldAmount() missing case for ", + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + expPanic: "GetHoldAmount() missing case for *exchange.unknownOrderType", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coins + testFunc := func() { + actual = tc.order.GetHoldAmount() + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetHoldAmount") + assert.Equal(t, tc.expected.String(), actual.String(), "GetHoldAmount result") }) } } @@ -524,6 +575,60 @@ func TestOrder_Validate(t *testing.T) { } } +func TestAskOrder_GetHoldAmount(t *testing.T) { + tests := []struct { + name string + order AskOrder + exp sdk.Coins + }{ + { + name: "empty order", + order: AskOrder{}, + exp: nil, + }, + { + name: "just assets", + order: AskOrder{ + Assets: sdk.NewCoins(sdk.NewInt64Coin("acorn", 12), sdk.NewInt64Coin("banana", 99)), + }, + exp: sdk.NewCoins(sdk.NewInt64Coin("acorn", 12), sdk.NewInt64Coin("banana", 99)), + }, + { + name: "settlement fee is different denom from price", + order: AskOrder{ + Assets: sdk.NewCoins(sdk.NewInt64Coin("acorn", 12), sdk.NewInt64Coin("banana", 99)), + Price: sdk.NewInt64Coin("cucumber", 8), + SellerSettlementFlatFee: &sdk.Coin{Denom: "durian", Amount: sdkmath.NewInt(52)}, + }, + exp: sdk.NewCoins( + sdk.NewInt64Coin("acorn", 12), + sdk.NewInt64Coin("banana", 99), + sdk.NewInt64Coin("durian", 52), + ), + }, + { + name: "settlement fee is same denom as price", + order: AskOrder{ + Assets: sdk.NewCoins(sdk.NewInt64Coin("acorn", 12), sdk.NewInt64Coin("banana", 99)), + Price: sdk.NewInt64Coin("cucumber", 8), + SellerSettlementFlatFee: &sdk.Coin{Denom: "cucumber", Amount: sdkmath.NewInt(52)}, + }, + exp: sdk.NewCoins(sdk.NewInt64Coin("acorn", 12), sdk.NewInt64Coin("banana", 99)), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coins + testFunc := func() { + actual = tc.order.GetHoldAmount() + } + require.NotPanics(t, testFunc, "GetHoldAmount()") + assert.Equal(t, tc.exp, actual, "GetHoldAmount() result") + }) + } +} + func TestAskOrder_Validate(t *testing.T) { coin := func(amount int64, denom string) *sdk.Coin { return &sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} @@ -773,6 +878,56 @@ func TestAskOrder_Validate(t *testing.T) { } } +func TestBidOrder_GetHoldAmount(t *testing.T) { + tests := []struct { + name string + order BidOrder + exp sdk.Coins + }{ + { + name: "just price", + order: BidOrder{ + Price: sdk.NewInt64Coin("cucumber", 8), + }, + exp: sdk.NewCoins(sdk.NewInt64Coin("cucumber", 8)), + }, + { + name: "price and settlement fee with shared denom", + order: BidOrder{ + Price: sdk.NewInt64Coin("cucumber", 8), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("acorn", 5), sdk.NewInt64Coin("cucumber", 1)), + }, + exp: sdk.NewCoins( + sdk.NewInt64Coin("acorn", 5), + sdk.NewInt64Coin("cucumber", 9), + ), + }, + { + name: "price and settlement fee with different denom", + order: BidOrder{ + Price: sdk.NewInt64Coin("cucumber", 8), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("acorn", 5), sdk.NewInt64Coin("banana", 1)), + }, + exp: sdk.NewCoins( + sdk.NewInt64Coin("acorn", 5), + sdk.NewInt64Coin("banana", 1), + sdk.NewInt64Coin("cucumber", 8), + ), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coins + testFunc := func() { + actual = tc.order.GetHoldAmount() + } + require.NotPanics(t, testFunc, "GetHoldAmount()") + assert.Equal(t, tc.exp, actual, "GetHoldAmount() result") + }) + } +} + func TestBidOrder_Validate(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} From c0a09bd215d7f5808407b5c3eb076a7f428b58e4 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sat, 9 Sep 2023 23:35:43 -0600 Subject: [PATCH 098/309] [1658]: Add a way to collect fees for a market and the exchange. --- x/exchange/expected_keepers.go | 14 ++++++- x/exchange/keeper/keeper.go | 70 +++++++++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 11 deletions(-) diff --git a/x/exchange/expected_keepers.go b/x/exchange/expected_keepers.go index 715582142f..1b5a5cc5ed 100644 --- a/x/exchange/expected_keepers.go +++ b/x/exchange/expected_keepers.go @@ -13,10 +13,20 @@ type AccountKeeper interface { NewAccount(ctx sdk.Context, acc authtypes.AccountI) authtypes.AccountI } +type AttributeKeeper interface { + GetAllAttributesAddr(ctx sdk.Context, addr []byte) ([]attrtypes.Attribute, error) +} + +type BankKeeper interface { + SendCoins(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error +} + type NameKeeper interface { Normalize(ctx sdk.Context, name string) (string, error) } -type AttributeKeeper interface { - GetAllAttributesAddr(ctx sdk.Context, addr []byte) ([]attrtypes.Attribute, error) +type HoldKeeper interface { + AddHold(ctx sdk.Context, addr sdk.AccAddress, funds sdk.Coins, reason string) error + ReleaseHold(ctx sdk.Context, addr sdk.AccAddress, funds sdk.Coins) error } diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 0bc86be040..bf769ce44f 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -2,6 +2,9 @@ package keeper import ( "errors" + "fmt" + + sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" @@ -19,23 +22,30 @@ type Keeper struct { accountKeeper exchange.AccountKeeper attrKeeper exchange.AttributeKeeper + bankKeeper exchange.BankKeeper + holdKeeper exchange.HoldKeeper nameKeeper exchange.NameKeeper // TODO[1658]: Finish the Keeper struct. - authority string + authority string + feeCollectorName string } -func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, - accountKeeper exchange.AccountKeeper, attrKeeper exchange.AttributeKeeper, nameKeeper exchange.NameKeeper, +func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, feeCollectorName string, + accountKeeper exchange.AccountKeeper, attrKeeper exchange.AttributeKeeper, + bankKeeper exchange.BankKeeper, holdKeeper exchange.HoldKeeper, nameKeeper exchange.NameKeeper, ) Keeper { // TODO[1658]: Finish NewKeeper. rv := Keeper{ - cdc: cdc, - storeKey: storeKey, - accountKeeper: accountKeeper, - attrKeeper: attrKeeper, - nameKeeper: nameKeeper, - authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + cdc: cdc, + storeKey: storeKey, + accountKeeper: accountKeeper, + attrKeeper: attrKeeper, + bankKeeper: bankKeeper, + holdKeeper: holdKeeper, + nameKeeper: nameKeeper, + authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + feeCollectorName: feeCollectorName, } return rv } @@ -45,6 +55,11 @@ func (k Keeper) GetAuthority() string { return k.authority } +// GetFeeCollectorName gets the name of the fee collector. +func (k Keeper) GetFeeCollectorName() string { + return k.feeCollectorName +} + // getAllKeys gets all the keys in the store with the given prefix. func getAllKeys(store sdk.KVStore, pre []byte) [][]byte { // Using a prefix iterator so that iter.Key() is the whole key (including the prefix). @@ -104,3 +119,40 @@ func (k Keeper) NormalizeReqAttrs(ctx sdk.Context, reqAttrs []string) ([]string, } return rv, errors.Join(errs...) } + +// CollectFee will transfer the fee amount to the market account, +// then the exchange's cut from the market to the fee collector. +func (k Keeper) CollectFee(ctx sdk.Context, payer sdk.AccAddress, marketID uint32, feeAmt sdk.Coins) error { + if feeAmt.IsZero() { + return nil + } + exchangeAmt := make(sdk.Coins, 0, len(feeAmt)) + for _, coin := range feeAmt { + if coin.Amount.IsZero() { + continue + } + + split := int64(k.GetExchangeSplit(ctx, coin.Denom)) + if split == 0 { + continue + } + + splitAmt := coin.Amount.Mul(sdkmath.NewInt(split)) + roundUp := !splitAmt.ModRaw(10_000).IsZero() + splitAmt = splitAmt.QuoRaw(10_000) + if roundUp { + splitAmt = splitAmt.Add(sdkmath.OneInt()) + } + exchangeAmt = append(exchangeAmt, sdk.Coin{Denom: coin.Denom, Amount: splitAmt}) + } + + marketAddr := exchange.GetMarketAddress(marketID) + if err := k.bankKeeper.SendCoins(ctx, payer, marketAddr, feeAmt); err != nil { + return fmt.Errorf("error transferring %s from %s to market %d: %w", feeAmt, payer, marketID, err) + } + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, marketAddr, k.feeCollectorName, exchangeAmt); err != nil { + return fmt.Errorf("error collecting exchange fee %s (based off %s) from market %d: %w", exchangeAmt, feeAmt, marketID, err) + } + + return nil +} From 2f757e6d79bc23491404fdb4b9e0609c00ce2f7d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sat, 9 Sep 2023 23:38:55 -0600 Subject: [PATCH 099/309] [1658]: Implement CreateAsk, CreateBid, and CancelOrder. --- x/exchange/keeper/market.go | 116 +++++++++++++++++++------ x/exchange/keeper/msg_server.go | 24 ++++-- x/exchange/keeper/orders.go | 144 +++++++++++++++++++++++++++++--- 3 files changed, 241 insertions(+), 43 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index a8604f0d0e..81df14da4a 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -125,6 +125,26 @@ func updateFlatFees(store sdk.KVStore, marketID uint32, toDelete, toWrite []sdk. } } +// validateFlatFee returns an error if the provided fee is not sufficient to cover the required flat fee. +func validateFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin, name string, maker flatFeeKeyMakers) error { + if !hasFlatFee(store, marketID, maker) { + return nil + } + if fee == nil { + opts := getAllFlatFees(store, marketID, maker) + return fmt.Errorf("no %s fee provided, must be one of: %s", name, sdk.NewCoins(opts...).String()) + } + reqFee := getFlatFee(store, marketID, fee.Denom, maker) + if reqFee == nil { + opts := getAllFlatFees(store, marketID, maker) + return fmt.Errorf("invalid %s fee, must be one of: %s", name, sdk.NewCoins(opts...).String()) + } + if fee.Amount.LT(reqFee.Amount) { + return fmt.Errorf("insufficient %s fee: %q is less than required amount %q", name, fee, reqFee) + } + return nil +} + // hasFeeRatio returns true if this market has any fee ratios for a given type. func hasFeeRatio(store sdk.KVStore, marketID uint32, maker ratioKeyMakers) bool { rv := false @@ -219,11 +239,9 @@ func updateCreateAskFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd updateFlatFees(store, marketID, toDelete, toAdd, createAskFlatKeyMakers) } -// IsAcceptableCreateAskFlatFee returns true if the provide fee has a denom accepted as a create-ask flat fee, -// and the fee amount is at least as much as the required amount of that denom. -func (k Keeper) IsAcceptableCreateAskFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { - reqFee := getFlatFee(k.getStore(ctx), marketID, fee.Denom, createAskFlatKeyMakers) - return reqFee != nil && fee.Amount.GTE(reqFee.Amount) +// validateCreateAskFlatFee returns an error if the provided fee is not a sufficient create ask flat fee. +func validateCreateAskFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin) error { + return validateFlatFee(store, marketID, fee, "ask order creation", createAskFlatKeyMakers) } // getCreateBidFlatFees gets the create-bid flat fee options for a market. @@ -246,11 +264,9 @@ func updateCreateBidFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd updateFlatFees(store, marketID, toDelete, toAdd, createBidFlatKeyMakers) } -// IsAcceptableCreateBidFlatFee returns true if the provide fee has a denom accepted as a create-bid flat fee, -// and the fee amount is at least as much as the required amount of that denom. -func (k Keeper) IsAcceptableCreateBidFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { - reqFee := getFlatFee(k.getStore(ctx), marketID, fee.Denom, createBidFlatKeyMakers) - return reqFee != nil && fee.Amount.GTE(reqFee.Amount) +// validateCreateBidFlatFee returns an error if the provided fee is not a sufficient create bid flat fee. +func validateCreateBidFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin) error { + return validateFlatFee(store, marketID, fee, "bid order creation", createBidFlatKeyMakers) } // getSellerSettlementFlatFees gets the seller settlement flat fee options for a market. @@ -273,11 +289,9 @@ func updateSellerSettlementFlatFees(store sdk.KVStore, marketID uint32, toDelete updateFlatFees(store, marketID, toDelete, toAdd, sellerSettlementFlatKeyMakers) } -// IsAcceptableSellerSettlementFlatFee returns true if the provide fee has a denom accepted as a seller settlement -// flat fee, and the fee amount is at least as much as the required amount of that denom. -func (k Keeper) IsAcceptableSellerSettlementFlatFee(ctx sdk.Context, marketID uint32, fee sdk.Coin) bool { - reqFee := getFlatFee(k.getStore(ctx), marketID, fee.Denom, sellerSettlementFlatKeyMakers) - return reqFee != nil && fee.Amount.GTE(reqFee.Amount) +// validateSellerSettlementFlatFee returns an error if the provided fee is not a sufficient seller settlement flat fee. +func validateSellerSettlementFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin) error { + return validateFlatFee(store, marketID, fee, "seller settlement flat", createBidFlatKeyMakers) } // getSellerSettlementRatios gets the seller settlement fee ratios for a market. @@ -300,17 +314,63 @@ func updateSellerSettlementRatios(store sdk.KVStore, marketID uint32, toDelete, updateFeeRatios(store, marketID, toDelete, toAdd, sellerSettlementRatioKeyMakers) } -// CalculateSellerSettlementRatioFee calculates the seller settlement fee required for the given price. -func (k Keeper) CalculateSellerSettlementRatioFee(ctx sdk.Context, marketID uint32, price sdk.Coin) (sdk.Coin, error) { - ratio := getFeeRatio(k.getStore(ctx), marketID, price.Denom, price.Denom, sellerSettlementRatioKeyMakers) +// getSellerSettlementRatio gets the seller settlement fee ratio for the given market with the provided denom. +func getSellerSettlementRatio(store sdk.KVStore, marketID uint32, priceDenom string) (*exchange.FeeRatio, error) { + ratio := getFeeRatio(store, marketID, priceDenom, priceDenom, sellerSettlementRatioKeyMakers) + if ratio == nil { + if hasFeeRatio(store, marketID, sellerSettlementRatioKeyMakers) { + return nil, fmt.Errorf("no seller settlement fee ratio found for denom %q", priceDenom) + } + } + return ratio, nil +} + +// validateAskPrice validates that the provided ask price denom is acceptable. +func validateAskPrice(store sdk.KVStore, marketID uint32, price sdk.Coin, settlementFlatFee *sdk.Coin) error { + ratio, err := getSellerSettlementRatio(store, marketID, price.Denom) + if err != nil { + return err + } + + // If there is a settlement flat fee with a different denom as the price, a hold is placed on it. + // If there's a settlement flat fee with the same denom as the price, it's paid out of the price along + // with the ratio amount. Assuming the ratio is less than one, the price will always cover the ratio fee amount. + // But if the flat fee is coming out of the price too, it's possible that the price might be less than the total + // fee that will need to come out of it. We want to return an error if that's the case. + if settlementFlatFee != nil && price.Denom == settlementFlatFee.Denom { + if price.Amount.LT(settlementFlatFee.Amount) { + return fmt.Errorf("price %s is less than seller settlement flat fee %s", price, settlementFlatFee) + } + if ratio != nil { + ratioFee, err := ratio.ApplyToLoosely(price) + if err != nil { + return err + } + reqPrice := settlementFlatFee.Add(ratioFee) + if price.IsLT(reqPrice) { + return fmt.Errorf("price %s is less than total required seller settlement fee of %s = %s flat + %s ratio", + price, reqPrice, settlementFlatFee, ratioFee) + } + } + } + + return err +} + +// calculateSellerSettlementRatioFee calculates the seller settlement fee required for the given price. +func calculateSellerSettlementRatioFee(store sdk.KVStore, marketID uint32, price sdk.Coin) (*sdk.Coin, error) { + ratio, err := getSellerSettlementRatio(store, marketID, price.Denom) + if err != nil { + return nil, err + } if ratio == nil { - return sdk.Coin{}, fmt.Errorf("no seller settlement fee ratio found for denom %q", price.Denom) + return nil, nil } rv, err := ratio.ApplyTo(price) if err != nil { - err = fmt.Errorf("invalid seller settlement fees: %w", err) + return nil, fmt.Errorf("invalid seller settlement fees: %w", err) } - return rv, err + return &rv, nil } // getBuyerSettlementFlatFees gets the buyer settlement flat fee options for a market. @@ -396,12 +456,11 @@ func (k Keeper) CalculateBuyerSettlementRatioFeeOptions(ctx sdk.Context, marketI return rv, nil } -// ValidateBuyerSettlementFee returns an error if the provided fee is not enough to cover both the +// validateBuyerSettlementFee returns an error if the provided fee is not enough to cover both the // buyer settlement flat and percent fees for the given price. -func (k Keeper) ValidateBuyerSettlementFee(ctx sdk.Context, marketID uint32, price sdk.Coin, fee sdk.Coins) error { +func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Coin, fee sdk.Coins) error { flatKeyMaker := buyerSettlementFlatKeyMakers ratioKeyMaker := buyerSettlementRatioKeyMakers - store := k.getStore(ctx) flatFeeReq := hasFlatFee(store, marketID, flatKeyMaker) ratioFeeReq := hasFeeRatio(store, marketID, ratioKeyMaker) @@ -753,6 +812,15 @@ func setMarketKnown(store sdk.KVStore, marketID uint32) { store.Set(key, nil) } +// validateMarketExists returns an error if the provided marketID does not exist. +func validateMarketExists(store sdk.KVStore, marketID uint32) error { + key := MakeKeyKnownMarketID(marketID) + if !store.Has(key) { + return fmt.Errorf("market %d does not exist", marketID) + } + return nil +} + // GetAllMarketIDs gets all the known market ids from the store. func (k Keeper) GetAllMarketIDs(ctx sdk.Context) []uint32 { var rv []uint32 diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index c04d414f14..26bbb12225 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -25,20 +25,32 @@ var _ exchange.MsgServer = MsgServer{} // CreateAsk creates an ask order (to sell something you own). func (k MsgServer) CreateAsk(goCtx context.Context, msg *exchange.MsgCreateAskRequest) (*exchange.MsgCreateAskResponse, error) { - // TODO[1658]: Implement CreateAsk - panic("not implemented") + ctx := sdk.UnwrapSDKContext(goCtx) + orderID, err := k.CreateAskOrder(ctx, msg) + if err != nil { + return nil, err + } + return &exchange.MsgCreateAskResponse{OrderId: orderID}, nil } // CreateBid creates an bid order (to buy something you want). func (k MsgServer) CreateBid(goCtx context.Context, msg *exchange.MsgCreateBidRequest) (*exchange.MsgCreateBidResponse, error) { - // TODO[1658]: Implement CreateBid - panic("not implemented") + ctx := sdk.UnwrapSDKContext(goCtx) + orderID, err := k.CreateBidOrder(ctx, msg) + if err != nil { + return nil, err + } + return &exchange.MsgCreateBidResponse{OrderId: orderID}, nil } // CancelOrder cancels an order. func (k MsgServer) CancelOrder(goCtx context.Context, msg *exchange.MsgCancelOrderRequest) (*exchange.MsgCancelOrderResponse, error) { - // TODO[1658]: Implement CancelOrder - panic("not implemented") + ctx := sdk.UnwrapSDKContext(goCtx) + err := k.Keeper.CancelOrder(ctx, msg) + if err != nil { + return nil, err + } + return &exchange.MsgCancelOrderResponse{}, nil } // FillBids uses the assets in your account to fulfill one or more bids (similar to a fill-or-cancel ask). diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 6478c974d6..a1907fe808 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -3,6 +3,7 @@ package keeper import ( "fmt" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/provenance-io/provenance/x/exchange" @@ -110,7 +111,7 @@ func createIndexEntries(order exchange.Order) []sdk.KVPair { return rv } -// setOrderInStore writes an order to the store (along with all it's indexes). +// setOrderInStore writes an order to the store (along with all its indexes). func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { key, value, err := k.getOrderStoreKeyValue(order) if err != nil { @@ -131,16 +132,11 @@ func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { return nil } -// deleteOrderFromStore deletes an order from the store as well as all its index entries. -func (k Keeper) deleteOrderFromStore(store sdk.KVStore, orderID uint64) { - order, err := k.getOrderFromStore(store, orderID) - key := MakeKeyOrder(orderID) +// deleteOrderFromStore deletes an order from the store (along with its indexes). +func deleteOrderFromStore(store sdk.KVStore, order exchange.Order) { + key := MakeKeyOrder(order.OrderId) store.Delete(key) - if err != nil || order == nil { - // Either it couldn't be read or it didn't exist. Either way, nothing more we can do. - return - } - indexEntries := createIndexEntries(*order) + indexEntries := createIndexEntries(order) for _, entry := range indexEntries { store.Delete(entry.Key) } @@ -156,9 +152,17 @@ func (k Keeper) SetOrder(ctx sdk.Context, order exchange.Order) error { return k.setOrderInStore(k.getStore(ctx), order) } -// DeleteOrder removes the provided order from state. -func (k Keeper) DeleteOrder(ctx sdk.Context, orderID uint64) { - k.deleteOrderFromStore(k.getStore(ctx), orderID) +// GetNextOrderID gets the next available order id from the store. +func (k Keeper) GetNextOrderID(ctx sdk.Context) uint64 { + store := prefix.NewStore(k.getStore(ctx), GetKeyPrefixOrder()) + iter := store.ReverseIterator(nil, nil) + defer iter.Close() + if iter.Valid() { + orderIDBz := iter.Key() + orderID := uint64FromBz(orderIDBz) + return orderID + 1 + } + return 1 } // IterateMarketOrders iterates over all orders for a market. @@ -226,3 +230,117 @@ func (k Keeper) IterateAssetBidOrders(ctx sdk.Context, assetDenom string, cb fun return cb(orderID) }) } + +// CreateAskOrder creates an ask order, collects the creation fee, and places all needed holds. +func (k Keeper) CreateAskOrder(ctx sdk.Context, msg *exchange.MsgCreateAskRequest) (uint64, error) { + store := k.getStore(ctx) + marketID := msg.AskOrder.MarketId + seller := sdk.MustAccAddressFromBech32(msg.AskOrder.Seller) + + if err := validateMarketExists(store, marketID); err != nil { + return 0, err + } + if !k.CanCreateAsk(ctx, marketID, seller) { + return 0, fmt.Errorf("account %s is not allowed to create ask orders in market %d", seller, marketID) + } + if err := validateCreateAskFlatFee(store, marketID, msg.OrderCreationFee); err != nil { + return 0, err + } + if err := validateSellerSettlementFlatFee(store, marketID, msg.AskOrder.SellerSettlementFlatFee); err != nil { + return 0, err + } + if err := validateAskPrice(store, marketID, msg.AskOrder.Price, msg.AskOrder.SellerSettlementFlatFee); err != nil { + return 0, err + } + + if msg.OrderCreationFee != nil { + err := k.CollectFee(ctx, seller, marketID, sdk.Coins{*msg.OrderCreationFee}) + if err != nil { + return 0, fmt.Errorf("error collecting ask order creation fee: %w", err) + } + } + + orderID := k.GetNextOrderID(ctx) + order := exchange.NewOrder(orderID).WithAsk(&msg.AskOrder) + if err := k.setOrderInStore(store, *order); err != nil { + return 0, fmt.Errorf("error storing ask order: %w", err) + } + + toHold := order.GetHoldAmount() + if err := k.holdKeeper.AddHold(ctx, seller, toHold, fmt.Sprintf("x/exchange: order %d", order.OrderId)); err != nil { + return 0, fmt.Errorf("error placing hold for ask order: %w", err) + } + + return orderID, nil +} + +// CreateBidOrder creates a bid order, collects the creation fee, and places all needed holds. +func (k Keeper) CreateBidOrder(ctx sdk.Context, msg *exchange.MsgCreateBidRequest) (uint64, error) { + store := k.getStore(ctx) + marketID := msg.BidOrder.MarketId + buyer := sdk.MustAccAddressFromBech32(msg.BidOrder.Buyer) + + if err := validateMarketExists(store, marketID); err != nil { + return 0, err + } + if !k.CanCreateBid(ctx, marketID, buyer) { + return 0, fmt.Errorf("account %s is not allowed to create bid orders in market %d", buyer, marketID) + } + if err := validateCreateBidFlatFee(store, marketID, msg.OrderCreationFee); err != nil { + return 0, err + } + if err := validateBuyerSettlementFee(store, marketID, msg.BidOrder.Price, msg.BidOrder.BuyerSettlementFees); err != nil { + return 0, err + } + + if msg.OrderCreationFee != nil { + err := k.CollectFee(ctx, buyer, marketID, sdk.Coins{*msg.OrderCreationFee}) + if err != nil { + return 0, fmt.Errorf("error collecting bid order creation fee: %w", err) + } + } + + orderID := k.GetNextOrderID(ctx) + order := exchange.NewOrder(orderID).WithBid(&msg.BidOrder) + if err := k.setOrderInStore(store, *order); err != nil { + return 0, fmt.Errorf("error storing bid order: %w", err) + } + + toHold := order.GetHoldAmount() + if err := k.holdKeeper.AddHold(ctx, buyer, toHold, fmt.Sprintf("x/exchange: order %d", order.OrderId)); err != nil { + return 0, fmt.Errorf("error placing hold for bid order: %w", err) + } + + return orderID, nil +} + +// CancelOrder releases an order's held funds and deletes it. +func (k Keeper) CancelOrder(ctx sdk.Context, msg *exchange.MsgCancelOrderRequest) error { + store := k.getStore(ctx) + order, err := k.getOrderFromStore(store, msg.OrderId) + if err != nil { + return err + } + if order == nil { + return fmt.Errorf("order %d does not exist", msg.OrderId) + } + + orderOwner := order.GetOwner() + if msg.Owner != orderOwner && msg.Owner != k.GetAuthority() { + msgOwnerAddr := sdk.MustAccAddressFromBech32(msg.Owner) + if hasPermission(store, order.GetMarketID(), msgOwnerAddr, exchange.Permission_cancel) { + return fmt.Errorf("account %s cannot cancel order %d", msg.Owner, msg.OrderId) + } + } + + orderOwnerAddr := sdk.MustAccAddressFromBech32(orderOwner) + heldAmount := order.GetHoldAmount() + err = k.holdKeeper.ReleaseHold(ctx, orderOwnerAddr, heldAmount) + if err != nil { + return fmt.Errorf("unable to release hold on order %d funds: %w", msg.OrderId, err) + } + + deleteOrderFromStore(store, *order) + + return nil +} From 231bcba40a7afb3cf182bb36edfea66d432ca471 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 10:00:30 -0600 Subject: [PATCH 100/309] [1658]: Fill in the MsgMarketWithdrawRequest message. --- docs/proto-docs.md | 8 + proto/provenance/exchange/v1/tx.proto | 15 +- x/exchange/tx.pb.go | 369 ++++++++++++++++++++------ 3 files changed, 311 insertions(+), 81 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index d808431843..dfd1d74f60 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2432,6 +2432,14 @@ MsgMarketUpdateUserSettleResponse is a response message for the MarketUpdateUser MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `administrator` | [string](#string) | | administrator is the account with withdraw permission requesting the withdrawal. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to withdraw from. | +| `to_address` | [string](#string) | | to_address is the address that will receive the funds. | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | amount is the funds to withdraw. | + + diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 31b30e1257..41d2898085 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -133,7 +133,20 @@ message MsgMarketSettleResponse {} // MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. message MsgMarketWithdrawRequest { - // TODO[1658]: MsgMarketWithdrawRequest + option (cosmos.msg.v1.signer) = "administrator"; + + // administrator is the account with withdraw permission requesting the withdrawal. + string administrator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + + // market_id is the numerical identifier of the market to withdraw from. + uint32 market_id = 2; + + // to_address is the address that will receive the funds. + string to_address = 3; + + // amount is the funds to withdraw. + repeated cosmos.base.v1beta1.Coin amount = 4 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; } // MsgMarketWithdrawResponse is a response message for the MarketWithdraw endpoint. diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index ce6d551192..57e61e0624 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -7,6 +7,7 @@ import ( context "context" fmt "fmt" _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/cosmos-sdk/types/msgservice" _ "github.com/gogo/protobuf/gogoproto" @@ -549,6 +550,14 @@ var xxx_messageInfo_MsgMarketSettleResponse proto.InternalMessageInfo // MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. type MsgMarketWithdrawRequest struct { + // administrator is the account with withdraw permission requesting the withdrawal. + Administrator string `protobuf:"bytes,1,opt,name=administrator,proto3" json:"administrator,omitempty"` + // market_id is the numerical identifier of the market to withdraw from. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // to_address is the address that will receive the funds. + ToAddress string `protobuf:"bytes,3,opt,name=to_address,json=toAddress,proto3" json:"to_address,omitempty"` + // amount is the funds to withdraw. + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` } func (m *MsgMarketWithdrawRequest) Reset() { *m = MsgMarketWithdrawRequest{} } @@ -584,6 +593,34 @@ func (m *MsgMarketWithdrawRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgMarketWithdrawRequest proto.InternalMessageInfo +func (m *MsgMarketWithdrawRequest) GetAdministrator() string { + if m != nil { + return m.Administrator + } + return "" +} + +func (m *MsgMarketWithdrawRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *MsgMarketWithdrawRequest) GetToAddress() string { + if m != nil { + return m.ToAddress + } + return "" +} + +func (m *MsgMarketWithdrawRequest) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Amount + } + return nil +} + // MsgMarketWithdrawResponse is a response message for the MarketWithdraw endpoint. type MsgMarketWithdrawResponse struct { } @@ -1413,86 +1450,91 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1253 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0x4f, 0x4f, 0xe3, 0x46, - 0x1b, 0x27, 0x0b, 0xcb, 0x92, 0x87, 0x85, 0x5d, 0xcd, 0x06, 0x48, 0x0c, 0x38, 0x21, 0xbc, 0x87, - 0x57, 0xbb, 0xc5, 0x59, 0xa8, 0x4a, 0x5b, 0xd4, 0x0b, 0xa1, 0x0d, 0xda, 0x03, 0x2a, 0x0a, 0x42, - 0x95, 0xda, 0x43, 0x34, 0x89, 0x07, 0x63, 0xe1, 0x78, 0x58, 0x8f, 0xc9, 0xb2, 0xd7, 0xaa, 0x52, - 0x4f, 0x95, 0xf6, 0xd8, 0x6b, 0xbf, 0x41, 0x0f, 0xbd, 0xb4, 0x9f, 0x60, 0xd5, 0xd3, 0xaa, 0xa7, - 0x9e, 0xaa, 0x0a, 0x0e, 0xfd, 0x1a, 0x95, 0x67, 0xc6, 0x8e, 0x1d, 0xdb, 0xb1, 0x43, 0xf7, 0x86, - 0x3d, 0xbf, 0xe7, 0xf7, 0x67, 0xb0, 0xe7, 0x79, 0x1c, 0xa8, 0x5e, 0x3a, 0x74, 0x40, 0x6c, 0x6c, - 0xf7, 0x48, 0x83, 0x5c, 0xf7, 0xce, 0xb1, 0x6d, 0x90, 0xc6, 0x60, 0xbb, 0xe1, 0x5e, 0x6b, 0x97, - 0x0e, 0x75, 0x29, 0x5a, 0x1e, 0x02, 0x34, 0x1f, 0xa0, 0x0d, 0xb6, 0x15, 0xb5, 0x47, 0x59, 0x9f, - 0xb2, 0x46, 0x17, 0x33, 0xaf, 0xa0, 0x4b, 0x5c, 0xbc, 0xdd, 0xe8, 0x51, 0xd3, 0x16, 0x75, 0xca, - 0x8a, 0x5c, 0xef, 0x33, 0xc3, 0xe3, 0xeb, 0x33, 0x43, 0x2e, 0x54, 0xc4, 0x42, 0x87, 0x5f, 0x35, - 0xc4, 0x85, 0x5c, 0x2a, 0x19, 0xd4, 0xa0, 0xe2, 0xbe, 0xf7, 0x97, 0xbc, 0xbb, 0x99, 0x62, 0xb1, - 0x8f, 0x9d, 0x0b, 0xe2, 0x66, 0x80, 0xa8, 0xa3, 0x13, 0x87, 0x65, 0x80, 0x2e, 0xb1, 0x83, 0xfb, - 0x12, 0x54, 0xff, 0xad, 0x00, 0x4f, 0x8e, 0x98, 0x71, 0xe0, 0x10, 0xec, 0x92, 0x7d, 0x76, 0xd1, - 0x26, 0x2f, 0xaf, 0x08, 0x73, 0xd1, 0x01, 0x14, 0x31, 0xbb, 0xe8, 0x70, 0xc2, 0x72, 0xa1, 0x56, - 0xf8, 0xff, 0xfc, 0x4e, 0x4d, 0x4b, 0xde, 0x1c, 0x6d, 0x9f, 0x5d, 0x7c, 0xe9, 0xe1, 0x9a, 0x33, - 0x6f, 0xff, 0xaa, 0x4e, 0xb5, 0xe7, 0xb0, 0xbc, 0x46, 0x87, 0x80, 0x38, 0x41, 0xa7, 0xe7, 0xd1, - 0x9b, 0xd4, 0xee, 0x9c, 0x11, 0x52, 0xbe, 0xc7, 0xd9, 0x2a, 0x9a, 0xdc, 0x0c, 0x6f, 0x4b, 0x35, - 0xb9, 0xa5, 0xda, 0x01, 0x35, 0xed, 0xf6, 0x63, 0x5e, 0x74, 0x20, 0x6b, 0x5a, 0x84, 0xec, 0x2d, - 0x7d, 0xfb, 0xcf, 0xcf, 0x4f, 0x1f, 0x07, 0x86, 0x34, 0x46, 0x2c, 0x8b, 0x38, 0xf5, 0x6d, 0x28, - 0x45, 0xbd, 0xb3, 0x4b, 0x6a, 0x33, 0x82, 0x2a, 0x30, 0x27, 0x74, 0x4d, 0x9d, 0x7b, 0x9f, 0x69, - 0x3f, 0xe0, 0xd7, 0x2f, 0xf4, 0xfa, 0xaf, 0xe1, 0xbc, 0x4d, 0x53, 0x0f, 0xe5, 0xed, 0x9a, 0x7a, - 0xbe, 0xbc, 0x4d, 0x53, 0x8f, 0xe4, 0xed, 0xca, 0xeb, 0xf7, 0x97, 0xb7, 0xe4, 0xe5, 0x7d, 0x14, - 0x18, 0xd2, 0xba, 0x57, 0xaf, 0x47, 0xe2, 0x72, 0xeb, 0xd9, 0x71, 0x6d, 0x58, 0xf2, 0x4a, 0xbc, - 0x08, 0x16, 0xf7, 0xe8, 0xe7, 0xd5, 0xe0, 0x3e, 0x7d, 0x65, 0xcb, 0xac, 0xc5, 0x66, 0xf9, 0x8f, - 0x5f, 0xb6, 0x4a, 0xd2, 0xe0, 0xbe, 0xae, 0x3b, 0x84, 0xb1, 0x13, 0xd7, 0x31, 0x6d, 0xa3, 0x2d, - 0x60, 0x11, 0x8d, 0x7b, 0x11, 0x8d, 0x3d, 0xf0, 0xcc, 0x0a, 0x58, 0xbd, 0x0c, 0xcb, 0xa3, 0x7a, - 0xc2, 0x64, 0xbd, 0x04, 0xe8, 0x88, 0x19, 0x2d, 0xd3, 0xb2, 0x9a, 0xa6, 0xce, 0xa4, 0x8d, 0xfa, - 0x12, 0xff, 0x6f, 0x0c, 0xef, 0xc6, 0xc0, 0xfb, 0xec, 0x22, 0x01, 0x2c, 0xee, 0x4a, 0xb0, 0xd0, - 0x3c, 0xe2, 0xef, 0xc7, 0x09, 0x71, 0x5d, 0x8b, 0xf8, 0x05, 0x15, 0x58, 0x89, 0xad, 0xc8, 0x22, - 0x05, 0xca, 0xc1, 0xd2, 0x57, 0xa6, 0x7b, 0xae, 0x3b, 0xf8, 0x95, 0x5f, 0xb6, 0x0a, 0x95, 0x84, - 0x35, 0x59, 0x58, 0x85, 0xf5, 0x60, 0xf1, 0xf4, 0x52, 0xc7, 0x2e, 0xf9, 0x9c, 0xb8, 0xd8, 0xb4, - 0x02, 0x97, 0x35, 0x50, 0xd3, 0x00, 0xa9, 0x14, 0x5f, 0xd8, 0xb8, 0x6b, 0x11, 0x3d, 0x9d, 0x22, - 0x00, 0x48, 0x8a, 0x3a, 0xd4, 0x46, 0x10, 0xa7, 0x8c, 0x38, 0xd1, 0xf4, 0x9b, 0xb0, 0x31, 0x06, - 0x23, 0x89, 0xc2, 0xa0, 0x23, 0x6c, 0x63, 0x83, 0x1c, 0x13, 0xa7, 0x6f, 0x32, 0x66, 0x52, 0x3b, - 0x88, 0xf4, 0x3f, 0xa8, 0x8f, 0x03, 0x49, 0xaa, 0xb0, 0x6b, 0x81, 0x6a, 0x93, 0x97, 0xfb, 0xae, - 0xeb, 0x04, 0x3c, 0x1b, 0x50, 0x4d, 0x45, 0x48, 0x92, 0x9f, 0x0a, 0x7c, 0xf3, 0x0f, 0xe9, 0x40, - 0x3c, 0xe7, 0x02, 0xec, 0x3f, 0xb5, 0xbb, 0x50, 0xc4, 0x57, 0xee, 0x39, 0x75, 0x4c, 0xf7, 0x75, - 0xe6, 0x93, 0x3b, 0x84, 0xa2, 0xcf, 0x60, 0x56, 0x9c, 0x9f, 0xf2, 0x65, 0x54, 0xd3, 0x5e, 0x6d, - 0x21, 0x27, 0x5f, 0x6c, 0x59, 0xb3, 0xb7, 0xe8, 0x3d, 0xe0, 0x43, 0xb6, 0xfa, 0x1a, 0x28, 0x49, - 0x16, 0x65, 0x82, 0xdf, 0x81, 0x3f, 0x75, 0x87, 0x74, 0x20, 0x22, 0xb6, 0x08, 0x61, 0xff, 0xd5, - 0xff, 0x2a, 0x14, 0x85, 0x17, 0xff, 0xf5, 0x5b, 0x68, 0xcf, 0x89, 0x1b, 0x2f, 0x74, 0x74, 0x0a, - 0x2b, 0x58, 0xd7, 0xbd, 0xa3, 0x46, 0x9c, 0x3b, 0xa4, 0xe3, 0x1d, 0x94, 0x67, 0x16, 0x76, 0xcb, - 0xd3, 0xb5, 0xe9, 0xb1, 0x47, 0x8f, 0x0c, 0xfa, 0x04, 0xeb, 0x7a, 0x8b, 0x90, 0xe0, 0x20, 0x6d, - 0x59, 0xd8, 0x45, 0xdf, 0x80, 0xe2, 0x90, 0x3e, 0x1d, 0x90, 0x44, 0xe6, 0x99, 0x7c, 0xcc, 0xcb, - 0x82, 0x22, 0x46, 0x1e, 0xf7, 0xec, 0x1d, 0x76, 0x9c, 0xf9, 0xfe, 0x1d, 0x3c, 0x37, 0x4d, 0x3d, - 0xdd, 0x73, 0xc0, 0x3c, 0x7b, 0x37, 0xcf, 0x3e, 0x79, 0x0f, 0x54, 0xdf, 0xb3, 0xe8, 0x3f, 0x1d, - 0xc6, 0xdf, 0xa5, 0x3e, 0xb1, 0x5d, 0x21, 0xf0, 0x20, 0x9f, 0x80, 0x22, 0xac, 0x9f, 0x70, 0x92, - 0x93, 0x80, 0x83, 0x8b, 0x98, 0xb0, 0x11, 0x4a, 0x90, 0xa2, 0x33, 0x97, 0x4f, 0x67, 0x3d, 0x08, - 0x92, 0x28, 0x65, 0x43, 0x2d, 0x3d, 0x8f, 0xe3, 0xf5, 0x22, 0x56, 0x2e, 0x72, 0xa5, 0xd4, 0x4e, - 0xd8, 0x22, 0xa4, 0xed, 0x01, 0xa5, 0xe0, 0x5a, 0x72, 0x30, 0x0e, 0x61, 0xc8, 0x85, 0xcd, 0xb1, - 0xd1, 0xa4, 0x24, 0x4c, 0x24, 0x59, 0x4d, 0xcd, 0x28, 0x55, 0x31, 0xac, 0xfb, 0x29, 0x79, 0x17, - 0x8d, 0x6d, 0xe6, 0x7c, 0xbe, 0xcd, 0xac, 0x88, 0x6c, 0x4d, 0x8f, 0x63, 0x64, 0x23, 0x0d, 0xa8, - 0x85, 0x82, 0x25, 0xab, 0x3c, 0xcc, 0xa7, 0xb2, 0x16, 0xc4, 0x49, 0x12, 0xb2, 0xa0, 0x9a, 0x9a, - 0x45, 0xee, 0xde, 0xc2, 0x44, 0xbb, 0xb7, 0x9a, 0x18, 0x4a, 0xee, 0x9c, 0x03, 0xf5, 0x71, 0xb1, - 0xa4, 0xe0, 0xe2, 0x44, 0x82, 0x6a, 0x5a, 0x3e, 0xa1, 0x19, 0x3b, 0x6a, 0x45, 0x9b, 0x1e, 0x39, - 0x4b, 0x63, 0xad, 0x42, 0x74, 0xb7, 0x63, 0x3e, 0xd7, 0xbe, 0x87, 0x56, 0x21, 0x06, 0xe4, 0xac, - 0x56, 0x21, 0xe4, 0xfc, 0x56, 0x21, 0x6a, 0xd2, 0x5b, 0x45, 0xd4, 0xa2, 0x48, 0xb0, 0xf3, 0xe3, - 0x22, 0x4c, 0x1f, 0x31, 0x03, 0x9d, 0x41, 0x31, 0x38, 0x1e, 0xd1, 0xb3, 0xd4, 0xde, 0x14, 0x1f, - 0xd3, 0x95, 0x0f, 0xf2, 0x81, 0xe5, 0xa0, 0x18, 0xe8, 0x34, 0x4d, 0x3d, 0x87, 0xce, 0x70, 0x3c, - 0xce, 0xa1, 0x13, 0x1e, 0x48, 0x2d, 0x98, 0x0f, 0x8d, 0x80, 0x68, 0x6b, 0x5c, 0x71, 0x6c, 0x34, - 0x55, 0xb4, 0xbc, 0x70, 0xa9, 0xd6, 0x83, 0x39, 0x7f, 0x80, 0x44, 0x4f, 0xc7, 0xd4, 0x8e, 0xcc, - 0x9e, 0xca, 0xb3, 0x5c, 0xd8, 0xa8, 0x88, 0x37, 0x78, 0x66, 0x8a, 0x84, 0x66, 0xd6, 0x4c, 0x91, - 0xf0, 0x24, 0x8b, 0x28, 0x3c, 0x0c, 0x0f, 0xab, 0x68, 0xdc, 0x4e, 0x24, 0xcc, 0xbb, 0x4a, 0x23, - 0x37, 0x5e, 0x0a, 0x5e, 0xc1, 0x62, 0x74, 0xcc, 0x45, 0xcf, 0x33, 0x29, 0x46, 0xa6, 0x65, 0x65, - 0x7b, 0x82, 0x0a, 0x29, 0xfb, 0x9d, 0xf7, 0x11, 0x16, 0x1f, 0x90, 0xd1, 0x47, 0x99, 0x54, 0x49, - 0x13, 0xb7, 0xb2, 0x3b, 0x69, 0x59, 0x8a, 0x0d, 0x39, 0x64, 0xe7, 0xb6, 0x11, 0x9d, 0xda, 0x73, - 0xdb, 0x18, 0x99, 0xe5, 0xd1, 0x0f, 0x05, 0x58, 0x4e, 0x9e, 0xd2, 0xd1, 0x27, 0x39, 0x29, 0x63, - 0xc3, 0xbf, 0xf2, 0xe9, 0x1d, 0x2a, 0xa5, 0x9f, 0x37, 0x05, 0x58, 0x49, 0x99, 0xf5, 0x51, 0x36, - 0x6d, 0xda, 0x47, 0x84, 0xb2, 0x77, 0x97, 0x52, 0x69, 0xe9, 0xfb, 0x02, 0x94, 0x92, 0x3e, 0x1b, - 0xd0, 0x6e, 0x4e, 0xd2, 0x91, 0x2f, 0x11, 0xe5, 0xe3, 0x89, 0xeb, 0xa4, 0x93, 0x6b, 0x78, 0x34, - 0x32, 0xf8, 0xa3, 0x71, 0x2f, 0x40, 0xf2, 0x77, 0x8c, 0xb2, 0x33, 0x49, 0x89, 0x54, 0x76, 0x60, - 0x21, 0xd2, 0x07, 0x51, 0x63, 0x3c, 0x49, 0xec, 0xeb, 0x43, 0x79, 0x9e, 0xbf, 0x20, 0x92, 0x36, - 0xdc, 0xbb, 0xb2, 0xd2, 0x26, 0xb4, 0xe2, 0xac, 0xb4, 0x49, 0xad, 0xb1, 0x49, 0xde, 0xde, 0xa8, - 0x85, 0x77, 0x37, 0x6a, 0xe1, 0xef, 0x1b, 0xb5, 0xf0, 0xe6, 0x56, 0x9d, 0x7a, 0x77, 0xab, 0x4e, - 0xfd, 0x79, 0xab, 0x4e, 0x41, 0xc5, 0xa4, 0x29, 0x7c, 0xc7, 0x85, 0xaf, 0x35, 0xc3, 0x74, 0xcf, - 0xaf, 0xba, 0x5a, 0x8f, 0xf6, 0x1b, 0x43, 0xd0, 0x96, 0x49, 0x43, 0x57, 0x8d, 0xeb, 0xe0, 0xe7, - 0xb0, 0xee, 0x2c, 0xff, 0x15, 0xec, 0xc3, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x99, 0x1f, 0x8f, - 0x47, 0x19, 0x14, 0x00, 0x00, + // 1343 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0x31, 0x6f, 0xdb, 0x46, + 0x14, 0xb6, 0x62, 0xc7, 0xb1, 0x5e, 0x62, 0x27, 0xb8, 0x28, 0xb6, 0xc4, 0xc4, 0x92, 0xa2, 0x74, + 0x08, 0x92, 0x9a, 0x8a, 0x53, 0x34, 0x6d, 0x8d, 0xa2, 0x80, 0xe5, 0x56, 0x41, 0x06, 0xa3, 0x81, + 0x0c, 0xa3, 0x40, 0x3b, 0x08, 0x27, 0xf1, 0x4c, 0x13, 0xa2, 0x78, 0x0a, 0xef, 0xa4, 0xd8, 0x6b, + 0x51, 0xa0, 0x53, 0xd1, 0x8c, 0x5d, 0xbb, 0x76, 0xea, 0xd0, 0xa5, 0xfd, 0x05, 0x41, 0xa7, 0xa0, + 0x53, 0xa7, 0xb6, 0xb0, 0x87, 0xfe, 0x8d, 0x82, 0x77, 0x27, 0x8a, 0x14, 0x49, 0x91, 0x72, 0x33, + 0xd9, 0x24, 0xbf, 0xf7, 0x7d, 0xef, 0x7b, 0xe4, 0xdd, 0x7b, 0x27, 0xa8, 0x0c, 0x5c, 0x3a, 0x22, + 0x0e, 0x76, 0xba, 0xa4, 0x4e, 0x4e, 0xba, 0xc7, 0xd8, 0x31, 0x49, 0x7d, 0xb4, 0x5d, 0xe7, 0x27, + 0xfa, 0xc0, 0xa5, 0x9c, 0xa2, 0xf5, 0x09, 0x40, 0x1f, 0x03, 0xf4, 0xd1, 0xb6, 0x56, 0xee, 0x52, + 0xd6, 0xa7, 0xac, 0xde, 0xc1, 0xcc, 0x0b, 0xe8, 0x10, 0x8e, 0xb7, 0xeb, 0x5d, 0x6a, 0x39, 0x32, + 0x4e, 0xdb, 0x50, 0xcf, 0xfb, 0xcc, 0xf4, 0xf8, 0xfa, 0xcc, 0x54, 0x0f, 0x4a, 0xf2, 0x41, 0x5b, + 0x5c, 0xd5, 0xe5, 0x85, 0x7a, 0x54, 0x30, 0xa9, 0x49, 0xe5, 0x7d, 0xef, 0x3f, 0x75, 0xf7, 0x5e, + 0x42, 0x8a, 0x7d, 0xec, 0xf6, 0x08, 0x4f, 0x01, 0x51, 0xd7, 0x20, 0x2e, 0x4b, 0x01, 0x0d, 0xb0, + 0x8b, 0xfb, 0x0a, 0x54, 0xfb, 0x2d, 0x07, 0x37, 0xf7, 0x99, 0xb9, 0xe7, 0x12, 0xcc, 0xc9, 0x2e, + 0xeb, 0xb5, 0xc8, 0x8b, 0x21, 0x61, 0x1c, 0xed, 0x41, 0x1e, 0xb3, 0x5e, 0x5b, 0x10, 0x16, 0x73, + 0xd5, 0xdc, 0xfd, 0xab, 0x8f, 0xab, 0x7a, 0x7c, 0x71, 0xf4, 0x5d, 0xd6, 0xfb, 0xdc, 0xc3, 0x35, + 0x96, 0x5e, 0xff, 0x55, 0x59, 0x68, 0xad, 0x60, 0x75, 0x8d, 0x9e, 0x02, 0x12, 0x04, 0xed, 0xae, + 0x47, 0x6f, 0x51, 0xa7, 0x7d, 0x44, 0x48, 0xf1, 0x92, 0x60, 0x2b, 0xe9, 0xaa, 0x18, 0x5e, 0x49, + 0x75, 0x55, 0x52, 0x7d, 0x8f, 0x5a, 0x4e, 0xeb, 0x86, 0x08, 0xda, 0x53, 0x31, 0x4d, 0x42, 0x76, + 0x6e, 0x7d, 0xfd, 0xef, 0xcf, 0x0f, 0x6e, 0xf8, 0x09, 0xe9, 0x8c, 0xd8, 0x36, 0x71, 0x6b, 0xdb, + 0x50, 0x08, 0xe7, 0xce, 0x06, 0xd4, 0x61, 0x04, 0x95, 0x60, 0x45, 0xea, 0x5a, 0x86, 0xc8, 0x7d, + 0xa9, 0x75, 0x45, 0x5c, 0x3f, 0x33, 0x6a, 0xbf, 0x06, 0xfd, 0x36, 0x2c, 0x23, 0xe0, 0xb7, 0x63, + 0x19, 0xd9, 0xfc, 0x36, 0x2c, 0x23, 0xe4, 0xb7, 0xa3, 0xae, 0xdf, 0x9e, 0xdf, 0x82, 0xe7, 0xf7, + 0xba, 0x9f, 0x90, 0xde, 0x19, 0x9e, 0x4e, 0xd9, 0x15, 0xa9, 0xa7, 0xdb, 0x75, 0xe0, 0x96, 0x17, + 0xe2, 0x59, 0xb0, 0x45, 0x8e, 0x63, 0xbf, 0x3a, 0x5c, 0xa6, 0x2f, 0x1d, 0xe5, 0x35, 0xdf, 0x28, + 0xfe, 0xf1, 0xcb, 0x56, 0x41, 0x25, 0xb8, 0x6b, 0x18, 0x2e, 0x61, 0xec, 0x80, 0xbb, 0x96, 0x63, + 0xb6, 0x24, 0x2c, 0xa4, 0x71, 0x29, 0xa4, 0xb1, 0x03, 0x5e, 0xb2, 0x12, 0x56, 0x2b, 0xc2, 0xfa, + 0xb4, 0x9e, 0x4c, 0xb2, 0x56, 0x00, 0xb4, 0xcf, 0xcc, 0xa6, 0x65, 0xdb, 0x0d, 0xcb, 0x60, 0x2a, + 0x8d, 0xda, 0x2d, 0xf1, 0x36, 0x26, 0x77, 0x23, 0xe0, 0x5d, 0xd6, 0x8b, 0x01, 0xcb, 0xbb, 0x0a, + 0x2c, 0x35, 0xf7, 0xc5, 0xfa, 0x38, 0x20, 0x9c, 0xdb, 0x64, 0x1c, 0x50, 0x82, 0x8d, 0xc8, 0x13, + 0x15, 0xf4, 0xfd, 0x25, 0x28, 0xfa, 0xcf, 0xbe, 0xb0, 0xf8, 0xb1, 0xe1, 0xe2, 0x97, 0xe3, 0xe2, + 0x7c, 0x02, 0xab, 0xd8, 0xe8, 0x5b, 0x8e, 0xc5, 0xb8, 0x8b, 0x39, 0x4d, 0x2f, 0x52, 0x18, 0x8e, + 0x6e, 0x43, 0x5e, 0x2e, 0xd7, 0x71, 0xb5, 0x56, 0x5b, 0x2b, 0xf2, 0xc6, 0x33, 0x03, 0x6d, 0x02, + 0x70, 0xda, 0xc6, 0x32, 0xbe, 0xb8, 0xe8, 0x31, 0xb7, 0xf2, 0x9c, 0x2a, 0x42, 0xd4, 0x85, 0x65, + 0xdc, 0xa7, 0x43, 0x87, 0x17, 0x97, 0xaa, 0x8b, 0x33, 0xbf, 0x9b, 0xc6, 0x23, 0xef, 0xf3, 0xfb, + 0xe9, 0xef, 0xca, 0x7d, 0xd3, 0xe2, 0xc7, 0xc3, 0x8e, 0xde, 0xa5, 0x7d, 0xb5, 0xc3, 0xa8, 0x3f, + 0x5b, 0xcc, 0xe8, 0xd5, 0xf9, 0xe9, 0x80, 0x30, 0x11, 0xc0, 0x5a, 0x8a, 0x7a, 0x07, 0x79, 0xaf, + 0x2c, 0x9c, 0x74, 0xed, 0x36, 0x94, 0x62, 0x0a, 0xa2, 0xca, 0x55, 0x81, 0x4d, 0xff, 0xe1, 0xe1, + 0xc0, 0xc0, 0x9c, 0x7c, 0x4a, 0x38, 0xb6, 0x6c, 0xff, 0xdd, 0x54, 0xa1, 0x9c, 0x04, 0x48, 0xa4, + 0xf8, 0xcc, 0xc1, 0x1d, 0x9b, 0x18, 0xc9, 0x14, 0x3e, 0x40, 0x51, 0xd4, 0xa0, 0x3a, 0x85, 0x38, + 0x64, 0xc4, 0x0d, 0xbf, 0xf3, 0x7b, 0x70, 0x77, 0x06, 0x46, 0x11, 0x05, 0x41, 0xfb, 0xd8, 0xc1, + 0x26, 0x79, 0x4e, 0xdc, 0xbe, 0xc5, 0x98, 0x45, 0x1d, 0xdf, 0xd2, 0x3b, 0x50, 0x9b, 0x05, 0x52, + 0x54, 0xc1, 0xac, 0x25, 0xaa, 0x45, 0x5e, 0xec, 0x72, 0xee, 0xfa, 0x3c, 0x77, 0xa1, 0x92, 0x88, + 0x50, 0x24, 0x3f, 0xe6, 0x44, 0xf1, 0x9f, 0xd2, 0x91, 0x5c, 0xdd, 0x12, 0x3c, 0xfe, 0x1c, 0x9f, + 0x40, 0x1e, 0x0f, 0xf9, 0x31, 0x75, 0x2d, 0x7e, 0x9a, 0xfa, 0x29, 0x4e, 0xa0, 0xe8, 0x63, 0x58, + 0x96, 0x5f, 0x9d, 0xda, 0x82, 0xca, 0x49, 0x1b, 0x9a, 0x94, 0x53, 0xdb, 0x99, 0x8a, 0xd9, 0x59, + 0xf3, 0xbe, 0x91, 0x09, 0x5b, 0xed, 0x0e, 0x68, 0x71, 0x29, 0x2a, 0x07, 0xbf, 0x83, 0x58, 0x6b, + 0x4f, 0xe9, 0x48, 0x5a, 0x6c, 0x12, 0xc2, 0xfe, 0x6f, 0xfe, 0x33, 0x97, 0xd1, 0x21, 0x6c, 0x60, + 0xc3, 0xf0, 0x36, 0x58, 0xb9, 0xdb, 0x92, 0xb6, 0xd7, 0x1e, 0x8e, 0x6c, 0xcc, 0x8b, 0x8b, 0x69, + 0x0b, 0x47, 0x1a, 0xbd, 0x89, 0x0d, 0xa3, 0x49, 0x88, 0xdf, 0x3e, 0x9a, 0x36, 0xe6, 0xe8, 0x2b, + 0xd0, 0x5c, 0xd2, 0xa7, 0x23, 0x12, 0xcb, 0xbc, 0x94, 0x8d, 0x79, 0x5d, 0x52, 0x44, 0xc8, 0xa3, + 0x39, 0x7b, 0x5b, 0xbc, 0x60, 0xbe, 0x7c, 0x81, 0x9c, 0x1b, 0x96, 0x91, 0x9c, 0xb3, 0xcf, 0xbc, + 0x7c, 0xb1, 0x9c, 0xc7, 0xe4, 0x5d, 0x28, 0x8f, 0x73, 0x96, 0x5d, 0xb7, 0xcd, 0xc4, 0x5a, 0xea, + 0x13, 0x87, 0x4b, 0x81, 0x2b, 0xd9, 0x04, 0x34, 0x99, 0xfa, 0x81, 0x20, 0x39, 0xf0, 0x39, 0x84, + 0x88, 0x05, 0x77, 0x03, 0x0e, 0x12, 0x74, 0x56, 0xb2, 0xe9, 0x6c, 0xfa, 0x46, 0x62, 0xa5, 0x1c, + 0xa8, 0x26, 0xfb, 0x71, 0xbd, 0x0e, 0xcc, 0x8a, 0x79, 0xa1, 0x94, 0xd8, 0xff, 0x9b, 0x84, 0xb4, + 0x3c, 0xa0, 0x12, 0xbc, 0x13, 0x6f, 0x4c, 0x40, 0x18, 0xe2, 0x70, 0x6f, 0xa6, 0x35, 0x25, 0x09, + 0x73, 0x49, 0x56, 0x12, 0x3d, 0x2a, 0x55, 0x0c, 0x9b, 0x63, 0x97, 0x62, 0x76, 0x88, 0x14, 0xf3, + 0x6a, 0xb6, 0x62, 0x96, 0xa4, 0xb7, 0x86, 0xc7, 0x31, 0x55, 0x48, 0x13, 0xaa, 0x01, 0x63, 0xf1, + 0x2a, 0xd7, 0xb2, 0xa9, 0xdc, 0xf1, 0xed, 0xc4, 0x09, 0xd9, 0x50, 0x49, 0xf4, 0xa2, 0xaa, 0xb7, + 0x3a, 0x57, 0xf5, 0x6e, 0xc7, 0x9a, 0x52, 0x95, 0x73, 0xa1, 0x36, 0xcb, 0x96, 0x12, 0x5c, 0x9b, + 0x4b, 0xb0, 0x9c, 0xe4, 0x4f, 0x6a, 0x46, 0xb6, 0x5a, 0x4d, 0xcc, 0x26, 0x53, 0x7b, 0x69, 0xa4, + 0x55, 0xc8, 0xee, 0xf6, 0x5c, 0x4c, 0xf3, 0x6f, 0xa1, 0x55, 0xc8, 0x63, 0x41, 0x5a, 0xab, 0x90, + 0x72, 0xe3, 0x56, 0x21, 0x63, 0x92, 0x5b, 0x45, 0x38, 0x45, 0xe9, 0xe0, 0xf1, 0x0f, 0x6b, 0xb0, + 0xb8, 0xcf, 0x4c, 0x74, 0x04, 0x79, 0x7f, 0x7b, 0x44, 0x0f, 0x13, 0x7b, 0x53, 0xf4, 0x70, 0xa2, + 0xbd, 0x9b, 0x0d, 0xac, 0xc6, 0x63, 0x5f, 0xa7, 0x61, 0x19, 0x19, 0x74, 0x26, 0x87, 0x82, 0x0c, + 0x3a, 0xc1, 0x31, 0xdc, 0x86, 0xab, 0x81, 0xc1, 0x17, 0x6d, 0xcd, 0x0a, 0x8e, 0x0c, 0xe4, 0x9a, + 0x9e, 0x15, 0xae, 0xd4, 0xba, 0xb0, 0x32, 0x1e, 0x9b, 0xd1, 0x83, 0x19, 0xb1, 0x53, 0x13, 0xb7, + 0xf6, 0x30, 0x13, 0x36, 0x2c, 0xe2, 0x8d, 0xdb, 0xa9, 0x22, 0x81, 0x49, 0x3d, 0x55, 0x24, 0x38, + 0xbf, 0x23, 0x0a, 0xd7, 0x82, 0x23, 0x3a, 0x9a, 0x55, 0x89, 0x98, 0x29, 0x5f, 0xab, 0x67, 0xc6, + 0x2b, 0xc1, 0x21, 0xac, 0x85, 0xc7, 0x5c, 0xf4, 0x28, 0x95, 0x62, 0xea, 0x88, 0xa0, 0x6d, 0xcf, + 0x11, 0xa1, 0x64, 0xbf, 0xf1, 0x8e, 0x9e, 0xd1, 0x01, 0x19, 0xbd, 0x9f, 0x4a, 0x15, 0x37, 0x71, + 0x6b, 0x4f, 0xe6, 0x0d, 0x4b, 0x48, 0x43, 0x0d, 0xd9, 0x99, 0xd3, 0x08, 0x4f, 0xed, 0x99, 0xd3, + 0x98, 0x9a, 0xe5, 0xd1, 0x77, 0x39, 0x58, 0x8f, 0x9f, 0xd2, 0xd1, 0x87, 0x19, 0x29, 0x23, 0xc3, + 0xbf, 0xf6, 0xd1, 0x05, 0x22, 0x55, 0x3e, 0xaf, 0x72, 0xb0, 0x91, 0x30, 0xeb, 0xa3, 0x74, 0xda, + 0xa4, 0x43, 0x84, 0xb6, 0x73, 0x91, 0x50, 0x95, 0xd2, 0xb7, 0x39, 0x28, 0xc4, 0x1d, 0x1b, 0xd0, + 0x93, 0x8c, 0xa4, 0x53, 0x27, 0x11, 0xed, 0x83, 0xb9, 0xe3, 0x54, 0x26, 0x27, 0x70, 0x7d, 0x6a, + 0xf0, 0x47, 0xb3, 0x16, 0x40, 0xfc, 0x39, 0x46, 0x7b, 0x3c, 0x4f, 0x88, 0x52, 0x76, 0x61, 0x35, + 0xd4, 0x07, 0x51, 0x7d, 0x36, 0x49, 0xe4, 0xf4, 0xa1, 0x3d, 0xca, 0x1e, 0x10, 0x72, 0x1b, 0xec, + 0x5d, 0x69, 0x6e, 0x63, 0x5a, 0x71, 0x9a, 0xdb, 0xb8, 0xd6, 0xd8, 0x20, 0xaf, 0xcf, 0xca, 0xb9, + 0x37, 0x67, 0xe5, 0xdc, 0x3f, 0x67, 0xe5, 0xdc, 0xab, 0xf3, 0xf2, 0xc2, 0x9b, 0xf3, 0xf2, 0xc2, + 0x9f, 0xe7, 0xe5, 0x05, 0x28, 0x59, 0x34, 0x81, 0xef, 0x79, 0xee, 0x4b, 0x3d, 0xf0, 0x03, 0xc0, + 0x04, 0xb4, 0x65, 0xd1, 0xc0, 0x55, 0xfd, 0xc4, 0xff, 0x11, 0xb0, 0xb3, 0x2c, 0x7e, 0xfb, 0x7b, + 0xef, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x15, 0x72, 0x8f, 0x0f, 0x15, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2471,6 +2513,39 @@ func (m *MsgMarketWithdrawRequest) MarshalToSizedBuffer(dAtA []byte) (int, error _ = i var l int _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.ToAddress) > 0 { + i -= len(m.ToAddress) + copy(dAtA[i:], m.ToAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ToAddress))) + i-- + dAtA[i] = 0x1a + } + if m.MarketId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Administrator) > 0 { + i -= len(m.Administrator) + copy(dAtA[i:], m.Administrator) + i = encodeVarintTx(dAtA, i, uint64(len(m.Administrator))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -3229,6 +3304,23 @@ func (m *MsgMarketWithdrawRequest) Size() (n int) { } var l int _ = l + l = len(m.Administrator) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.MarketId != 0 { + n += 1 + sovTx(uint64(m.MarketId)) + } + l = len(m.ToAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } return n } @@ -4338,6 +4430,123 @@ func (m *MsgMarketWithdrawRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgMarketWithdrawRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Administrator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Administrator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ToAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = append(m.Amount, types.Coin{}) + if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) From ebcf5606468f50b88bcd42c7cebf02963edf3264 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 10:51:31 -0600 Subject: [PATCH 101/309] [1658]: Msg methods for MsgMarketWithdrawRequest. --- x/exchange/msg.go | 27 ++++++-- x/exchange/msg_test.go | 150 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 169 insertions(+), 8 deletions(-) diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 26df6c2fb9..bbb24a5bb2 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -105,13 +105,32 @@ func (m MsgMarketSettleRequest) GetSigners() []sdk.AccAddress { } func (m MsgMarketWithdrawRequest) ValidateBasic() error { - // TODO[1658]: MsgMarketWithdrawRequest.ValidateBasic() - return nil + var errs []error + + if _, err := sdk.AccAddressFromBech32(m.Administrator); err != nil { + errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Administrator, err)) + } + + if m.MarketId == 0 { + errs = append(errs, errors.New("invalid market id: cannot be zero")) + } + + if _, err := sdk.AccAddressFromBech32(m.ToAddress); err != nil { + errs = append(errs, fmt.Errorf("invalid to address %q: %w", m.ToAddress, err)) + } + + if err := m.Amount.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid amount %q: %w", m.Amount, err)) + } else if m.Amount.IsZero() { + errs = append(errs, fmt.Errorf("invalid amount %q: cannot be zero", m.Amount)) + } + + return errors.Join(errs...) } func (m MsgMarketWithdrawRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgMarketWithdrawRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Administrator) + return []sdk.AccAddress{addr} } func (m MsgMarketUpdateDetailsRequest) ValidateBasic() error { diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index a41ef9c3b9..d6243af10a 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -40,7 +40,17 @@ func TestAllMsgsGetSigners(t *testing.T) { func(signer string) sdk.Msg { return &MsgCancelOrderRequest{Owner: signer} }, - // TODO[1658]: Add the rest of the messages once they've actually been defined. + // TODO[1658]: Add MsgFillBidsRequest once it's actually been defined. + // TODO[1658]: Add MsgFillAsksRequest once it's actually been defined. + // TODO[1658]: Add MsgMarketSettleRequest once it's actually been defined. + func(signer string) sdk.Msg { + return &MsgMarketWithdrawRequest{Administrator: signer} + }, + // TODO[1658]: Add MsgMarketUpdateDetailsRequest once it's actually been defined. + // TODO[1658]: Add MsgMarketUpdateEnabledRequest once it's actually been defined. + // TODO[1658]: Add MsgMarketUpdateUserSettleRequest once it's actually been defined. + // TODO[1658]: Add MsgMarketManagePermissionsRequest once it's actually been defined. + // TODO[1658]: Add MsgMarketManageReqAttrsRequest once it's actually been defined. func(signer string) sdk.Msg { return &MsgGovCreateMarketRequest{Authority: signer} }, @@ -114,8 +124,10 @@ func TestAllMsgsGetSigners(t *testing.T) { t.Run("all msgs have test case", func(t *testing.T) { for _, msg := range allRequestMsgs { typeName := getTypeName(msg) - // If this fails, a maker needs to be defined above for the missing msg type. - assert.True(t, hasMaker[typeName], "There is not a GetSigners test case for %s", typeName) + t.Run(typeName, func(t *testing.T) { + // If this fails, a maker needs to be defined above for the missing msg type. + assert.True(t, hasMaker[typeName], "There is not a GetSigners test case for %s", typeName) + }) } }) } @@ -260,7 +272,137 @@ func TestMsgCancelOrderRequest_ValidateBasic(t *testing.T) { // TODO[1658]: func TestMsgMarketSettleRequest_ValidateBasic(t *testing.T) -// TODO[1658]: func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) +func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + goodAdminAddr := sdk.AccAddress("Administrator_______").String() + goodToAddr := sdk.AccAddress("ToAddress___________").String() + goodCoins := sdk.NewCoins(coin(3, "acorns")) + + tests := []struct { + name string + msg MsgMarketWithdrawRequest + expErr []string + }{ + { + name: "control", + msg: MsgMarketWithdrawRequest{ + Administrator: goodAdminAddr, + MarketId: 1, + ToAddress: goodToAddr, + Amount: goodCoins, + }, + expErr: nil, + }, + { + name: "no administrator", + msg: MsgMarketWithdrawRequest{ + Administrator: "", + MarketId: 1, + ToAddress: goodToAddr, + Amount: goodCoins, + }, + expErr: []string{`invalid administrator ""`, "empty address string is not allowed"}, + }, + { + name: "bad administrator", + msg: MsgMarketWithdrawRequest{ + Administrator: "notright", + MarketId: 1, + ToAddress: goodToAddr, + Amount: goodCoins, + }, + expErr: []string{`invalid administrator "notright"`, "decoding bech32 failed"}, + }, + { + name: "market id zero", + msg: MsgMarketWithdrawRequest{ + Administrator: goodAdminAddr, + MarketId: 0, + ToAddress: goodToAddr, + Amount: goodCoins, + }, + expErr: []string{"invalid market id", "cannot be zero"}, + }, + { + name: "no to address", + msg: MsgMarketWithdrawRequest{ + Administrator: goodAdminAddr, + MarketId: 1, + ToAddress: "", + Amount: goodCoins, + }, + expErr: []string{`invalid to address ""`, "empty address string is not allowed"}, + }, + { + name: "bad to address", + msg: MsgMarketWithdrawRequest{ + Administrator: goodAdminAddr, + MarketId: 1, + ToAddress: "notright", + Amount: goodCoins, + }, + expErr: []string{`invalid to address "notright"`, "decoding bech32 failed"}, + }, + { + name: "invalid denom in amount", + msg: MsgMarketWithdrawRequest{ + Administrator: goodAdminAddr, + MarketId: 1, + ToAddress: goodToAddr, + Amount: sdk.Coins{coin(3, "x")}, + }, + expErr: []string{`invalid amount "3x"`, "invalid denom: x"}, + }, + { + name: "negative amount", + msg: MsgMarketWithdrawRequest{ + Administrator: goodAdminAddr, + MarketId: 1, + ToAddress: goodToAddr, + Amount: sdk.Coins{coin(-1, "negcoin")}, + }, + expErr: []string{`invalid amount "-1negcoin"`, "coin -1negcoin amount is not positive"}, + }, + { + name: "empty amount", + msg: MsgMarketWithdrawRequest{ + Administrator: goodAdminAddr, + MarketId: 1, + ToAddress: goodToAddr, + Amount: sdk.Coins{}, + }, + expErr: []string{`invalid amount ""`, "cannot be zero"}, + }, + { + name: "zero coin amount", + msg: MsgMarketWithdrawRequest{ + Administrator: goodAdminAddr, + MarketId: 1, + ToAddress: goodToAddr, + Amount: sdk.Coins{coin(0, "acorn"), coin(0, "banana")}, + }, + expErr: []string{`invalid amount "0acorn,0banana"`, "coin 0acorn amount is not positive"}, + }, + { + name: "multiple errors", + msg: MsgMarketWithdrawRequest{}, + expErr: []string{ + "invalid administrator", + "invalid market id", + "invalid to address", + "invalid amount", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} // TODO[1658]: func TestMsgMarketUpdateDetailsRequest_ValidateBasic(t *testing.T) From ab2c92560fc8a1dcea9db58bfa37d379c9254aff Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 10:54:20 -0600 Subject: [PATCH 102/309] [1658]: Implement MarketWithdraw. --- x/exchange/keeper/market.go | 20 ++++++++++++++++++++ x/exchange/keeper/msg_server.go | 13 +++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 81df14da4a..2117b4def7 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -956,3 +956,23 @@ func (k Keeper) CanCreateBid(ctx sdk.Context, marketID uint32, addr sdk.AccAddre reqAttrs := k.GetReqAttrBid(ctx, marketID) return k.hasReqAttrs(ctx, addr, reqAttrs) } + +// CanWithdrawMarketFunds returns true if the provided admin bech32 address has permission to +// withdraw funds from the given market's account. +func (k Keeper) CanWithdrawMarketFunds(ctx sdk.Context, marketID uint32, admin string) bool { + if admin == k.GetAuthority() { + return true + } + adminAddr := sdk.MustAccAddressFromBech32(admin) + return hasPermission(k.getStore(ctx), marketID, adminAddr, exchange.Permission_withdraw) +} + +// WithdrawMarketFunds transfers funds from a market account to another account. +func (k Keeper) WithdrawMarketFunds(ctx sdk.Context, marketID uint32, toAddr sdk.AccAddress, amount sdk.Coins) error { + marketAddr := exchange.GetMarketAddress(marketID) + err := k.bankKeeper.SendCoins(ctx, marketAddr, toAddr, amount) + if err != nil { + return fmt.Errorf("failed to withdraw %s from market %d: %w", amount, marketID, err) + } + return nil +} diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 26bbb12225..e651e8bc72 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -2,6 +2,7 @@ package keeper import ( "context" + "fmt" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -73,8 +74,16 @@ func (k MsgServer) MarketSettle(goCtx context.Context, msg *exchange.MsgMarketSe // MarketWithdraw is a market endpoint to withdraw fees that have been collected. func (k MsgServer) MarketWithdraw(goCtx context.Context, msg *exchange.MsgMarketWithdrawRequest) (*exchange.MsgMarketWithdrawResponse, error) { - // TODO[1658]: Implement MarketWithdraw - panic("not implemented") + ctx := sdk.UnwrapSDKContext(goCtx) + if !k.CanWithdrawMarketFunds(ctx, msg.MarketId, msg.Administrator) { + return nil, fmt.Errorf("account %s does not have withdraw permission for market %d", msg.Administrator, msg.MarketId) + } + toAddr := sdk.MustAccAddressFromBech32(msg.ToAddress) + err := k.WithdrawMarketFunds(ctx, msg.MarketId, toAddr, msg.Amount) + if err != nil { + return nil, err + } + return &exchange.MsgMarketWithdrawResponse{}, nil } // MarketUpdateDetails is a market endpoint to update its details. From 8a946e9e4ab5e7dd4be8976e0a7b702c338c9736 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 11:17:17 -0600 Subject: [PATCH 103/309] [1658]: Put an authority check short-circuit into hasPermission. Create IsAuthority keeper method for bette readability. --- x/exchange/keeper/keeper.go | 5 +++++ x/exchange/keeper/market.go | 21 ++++++++++++--------- x/exchange/keeper/msg_server.go | 10 +++++----- x/exchange/keeper/orders.go | 7 ++----- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index bf769ce44f..52567aacd4 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -55,6 +55,11 @@ func (k Keeper) GetAuthority() string { return k.authority } +// IsAuthority returns true if the provided address bech32 string is the authority address. +func (k Keeper) IsAuthority(addr string) bool { + return addr == k.authority +} + // GetFeeCollectorName gets the name of the fee collector. func (k Keeper) GetFeeCollectorName() string { return k.feeCollectorName diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 2117b4def7..0a3dadaf6d 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -621,7 +621,14 @@ func (k Keeper) SetUserSettlementAllowed(ctx sdk.Context, marketID uint32, allow } // hasPermission returns true if the provided address has the given permission in the market in question. -func hasPermission(store sdk.KVStore, marketID uint32, addr sdk.AccAddress, permission exchange.Permission) bool { +func (k Keeper) hasPermission(store sdk.KVStore, marketID uint32, address string, permission exchange.Permission) bool { + if k.IsAuthority(address) { + return true + } + addr, err := sdk.AccAddressFromBech32(address) + if err != nil { + return false + } key := MakeKeyMarketPermissions(marketID, addr, permission) return store.Has(key) } @@ -676,9 +683,9 @@ func updatePermissions(store sdk.KVStore, marketID uint32, revokeAll []string, t } } -// HasPermission returns true if the provided addr has the permission in question for a given market. -func (k Keeper) HasPermission(ctx sdk.Context, marketID uint32, addr sdk.AccAddress, permission exchange.Permission) bool { - return hasPermission(k.getStore(ctx), marketID, addr, permission) +// HasPermission returns true if the provided address has the permission in question for a given market. +func (k Keeper) HasPermission(ctx sdk.Context, marketID uint32, address string, permission exchange.Permission) bool { + return k.hasPermission(k.getStore(ctx), marketID, address, permission) } // getUserPermissions gets all permissions that have been granted to a user in a market. @@ -960,11 +967,7 @@ func (k Keeper) CanCreateBid(ctx sdk.Context, marketID uint32, addr sdk.AccAddre // CanWithdrawMarketFunds returns true if the provided admin bech32 address has permission to // withdraw funds from the given market's account. func (k Keeper) CanWithdrawMarketFunds(ctx sdk.Context, marketID uint32, admin string) bool { - if admin == k.GetAuthority() { - return true - } - adminAddr := sdk.MustAccAddressFromBech32(admin) - return hasPermission(k.getStore(ctx), marketID, adminAddr, exchange.Permission_withdraw) + return k.HasPermission(ctx, marketID, admin, exchange.Permission_withdraw) } // WithdrawMarketFunds transfers funds from a market account to another account. diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index e651e8bc72..047931ae53 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -117,13 +117,13 @@ func (k MsgServer) MarketManageReqAttrs(goCtx context.Context, msg *exchange.Msg } // wrongAuthErr returns the error to use when a message's authority isn't what's required. -func (k MsgServer) wrongAuthErr(authority string) error { - return govtypes.ErrInvalidSigner.Wrapf("expected %s got %s", k.GetAuthority(), authority) +func (k MsgServer) wrongAuthErr(badAuthority string) error { + return govtypes.ErrInvalidSigner.Wrapf("expected %s got %s", k.GetAuthority(), badAuthority) } // GovCreateMarket is a governance proposal endpoint for creating a market. func (k MsgServer) GovCreateMarket(goCtx context.Context, msg *exchange.MsgGovCreateMarketRequest) (*exchange.MsgGovCreateMarketResponse, error) { - if msg.Authority != k.GetAuthority() { + if !k.IsAuthority(msg.Authority) { return nil, k.wrongAuthErr(msg.Authority) } @@ -137,7 +137,7 @@ func (k MsgServer) GovCreateMarket(goCtx context.Context, msg *exchange.MsgGovCr // GovManageFees is a governance proposal endpoint for updating a market's fees. func (k MsgServer) GovManageFees(goCtx context.Context, msg *exchange.MsgGovManageFeesRequest) (*exchange.MsgGovManageFeesResponse, error) { - if msg.Authority != k.GetAuthority() { + if !k.IsAuthority(msg.Authority) { return nil, k.wrongAuthErr(msg.Authority) } @@ -149,7 +149,7 @@ func (k MsgServer) GovManageFees(goCtx context.Context, msg *exchange.MsgGovMana // GovUpdateParams is a governance proposal endpoint for updating the exchange module's params. func (k MsgServer) GovUpdateParams(goCtx context.Context, msg *exchange.MsgGovUpdateParamsRequest) (*exchange.MsgGovUpdateParamsResponse, error) { - if msg.Authority != k.GetAuthority() { + if !k.IsAuthority(msg.Authority) { return nil, k.wrongAuthErr(msg.Authority) } diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index a1907fe808..1a50b3ed16 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -326,11 +326,8 @@ func (k Keeper) CancelOrder(ctx sdk.Context, msg *exchange.MsgCancelOrderRequest } orderOwner := order.GetOwner() - if msg.Owner != orderOwner && msg.Owner != k.GetAuthority() { - msgOwnerAddr := sdk.MustAccAddressFromBech32(msg.Owner) - if hasPermission(store, order.GetMarketID(), msgOwnerAddr, exchange.Permission_cancel) { - return fmt.Errorf("account %s cannot cancel order %d", msg.Owner, msg.OrderId) - } + if msg.Owner != orderOwner && !k.hasPermission(store, order.GetMarketID(), msg.Owner, exchange.Permission_cancel) { + return fmt.Errorf("account %s cannot cancel order %d", msg.Owner, msg.OrderId) } orderOwnerAddr := sdk.MustAccAddressFromBech32(orderOwner) From 48fb67f00b2b86f7058812835c29dded88c3abc8 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 11:46:10 -0600 Subject: [PATCH 104/309] [1658]: Get rid of hasPermission(store, ...) and just use HasPermission(ctx, ...) instead. Make some order keeper stuff private. --- x/exchange/keeper/market.go | 25 ++++++------ x/exchange/keeper/msg_server.go | 6 +-- x/exchange/keeper/orders.go | 67 ++++++++++++++------------------- 3 files changed, 43 insertions(+), 55 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 0a3dadaf6d..9ad180b932 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -620,19 +620,6 @@ func (k Keeper) SetUserSettlementAllowed(ctx sdk.Context, marketID uint32, allow setUserSettlementAllowed(k.getStore(ctx), marketID, allowed) } -// hasPermission returns true if the provided address has the given permission in the market in question. -func (k Keeper) hasPermission(store sdk.KVStore, marketID uint32, address string, permission exchange.Permission) bool { - if k.IsAuthority(address) { - return true - } - addr, err := sdk.AccAddressFromBech32(address) - if err != nil { - return false - } - key := MakeKeyMarketPermissions(marketID, addr, permission) - return store.Has(key) -} - // grantPermissions updates the store so that the given address has the provided permissions in a market. func grantPermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress, permissions []exchange.Permission) { for _, perm := range permissions { @@ -684,8 +671,18 @@ func updatePermissions(store sdk.KVStore, marketID uint32, revokeAll []string, t } // HasPermission returns true if the provided address has the permission in question for a given market. +// Also returns true if the provided address is the authority address. func (k Keeper) HasPermission(ctx sdk.Context, marketID uint32, address string, permission exchange.Permission) bool { - return k.hasPermission(k.getStore(ctx), marketID, address, permission) + if k.IsAuthority(address) { + return true + } + addr, err := sdk.AccAddressFromBech32(address) + if err != nil { + return false + } + store := k.getStore(ctx) + key := MakeKeyMarketPermissions(marketID, addr, permission) + return store.Has(key) } // getUserPermissions gets all permissions that have been granted to a user in a market. diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 047931ae53..42273e8516 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -27,7 +27,7 @@ var _ exchange.MsgServer = MsgServer{} // CreateAsk creates an ask order (to sell something you own). func (k MsgServer) CreateAsk(goCtx context.Context, msg *exchange.MsgCreateAskRequest) (*exchange.MsgCreateAskResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - orderID, err := k.CreateAskOrder(ctx, msg) + orderID, err := k.createAskOrder(ctx, msg) if err != nil { return nil, err } @@ -37,7 +37,7 @@ func (k MsgServer) CreateAsk(goCtx context.Context, msg *exchange.MsgCreateAskRe // CreateBid creates an bid order (to buy something you want). func (k MsgServer) CreateBid(goCtx context.Context, msg *exchange.MsgCreateBidRequest) (*exchange.MsgCreateBidResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - orderID, err := k.CreateBidOrder(ctx, msg) + orderID, err := k.createBidOrder(ctx, msg) if err != nil { return nil, err } @@ -47,7 +47,7 @@ func (k MsgServer) CreateBid(goCtx context.Context, msg *exchange.MsgCreateBidRe // CancelOrder cancels an order. func (k MsgServer) CancelOrder(goCtx context.Context, msg *exchange.MsgCancelOrderRequest) (*exchange.MsgCancelOrderResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - err := k.Keeper.CancelOrder(ctx, msg) + err := k.cancelOrder(ctx, msg.OrderId, msg.Owner) if err != nil { return nil, err } diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 1a50b3ed16..a5c7e0ce60 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -70,17 +70,6 @@ func (k Keeper) parseOrderStoreValue(orderID uint64, value []byte) (*exchange.Or } } -// getOrderFromStore gets an order from the store. -func (k Keeper) getOrderFromStore(store sdk.KVStore, orderID uint64) (*exchange.Order, error) { - key := MakeKeyOrder(orderID) - value := store.Get(key) - rv, err := k.parseOrderStoreValue(orderID, value) - if err != nil { - return nil, fmt.Errorf("failed to read order %d: %w", orderID, err) - } - return rv, nil -} - // createIndexEntries creates all the key/value index entries for an order. func createIndexEntries(order exchange.Order) []sdk.KVPair { marketID := order.GetMarketID() @@ -125,15 +114,16 @@ func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { store.Set(entry.Key, entry.Value) } // It is assumed that these index entries cannot change over the life of an order. - // The only change that is allowed to an order is the assets due to partial fulfillment. - // But partial fulfillment is only allowed when there's a single asset type. + // The only change that is allowed to an order is the assets (due to partial fulfillment). + // Partial fulfillment is only allowed when there's a single asset type (denom), though. // That's why no attempt is made in here to update index entries when the order already exists. } return nil } -// deleteOrderFromStore deletes an order from the store (along with its indexes). -func deleteOrderFromStore(store sdk.KVStore, order exchange.Order) { +// deleteOrder deletes an order (along with its indexes). +func (k Keeper) deleteOrder(ctx sdk.Context, order exchange.Order) { + store := k.getStore(ctx) key := MakeKeyOrder(order.OrderId) store.Delete(key) indexEntries := createIndexEntries(order) @@ -144,16 +134,18 @@ func deleteOrderFromStore(store sdk.KVStore, order exchange.Order) { // GetOrder gets an order. Returns nil, nil if the order does not exist. func (k Keeper) GetOrder(ctx sdk.Context, orderID uint64) (*exchange.Order, error) { - return k.getOrderFromStore(k.getStore(ctx), orderID) -} - -// SetOrder stores the provided order in state. -func (k Keeper) SetOrder(ctx sdk.Context, order exchange.Order) error { - return k.setOrderInStore(k.getStore(ctx), order) + store := k.getStore(ctx) + key := MakeKeyOrder(orderID) + value := store.Get(key) + rv, err := k.parseOrderStoreValue(orderID, value) + if err != nil { + return nil, fmt.Errorf("failed to read order %d: %w", orderID, err) + } + return rv, nil } -// GetNextOrderID gets the next available order id from the store. -func (k Keeper) GetNextOrderID(ctx sdk.Context) uint64 { +// getNextOrderID gets the next available order id from the store. +func (k Keeper) getNextOrderID(ctx sdk.Context) uint64 { store := prefix.NewStore(k.getStore(ctx), GetKeyPrefixOrder()) iter := store.ReverseIterator(nil, nil) defer iter.Close() @@ -231,8 +223,8 @@ func (k Keeper) IterateAssetBidOrders(ctx sdk.Context, assetDenom string, cb fun }) } -// CreateAskOrder creates an ask order, collects the creation fee, and places all needed holds. -func (k Keeper) CreateAskOrder(ctx sdk.Context, msg *exchange.MsgCreateAskRequest) (uint64, error) { +// createAskOrder creates an ask order, collects the creation fee, and places all needed holds. +func (k Keeper) createAskOrder(ctx sdk.Context, msg *exchange.MsgCreateAskRequest) (uint64, error) { store := k.getStore(ctx) marketID := msg.AskOrder.MarketId seller := sdk.MustAccAddressFromBech32(msg.AskOrder.Seller) @@ -260,7 +252,7 @@ func (k Keeper) CreateAskOrder(ctx sdk.Context, msg *exchange.MsgCreateAskReques } } - orderID := k.GetNextOrderID(ctx) + orderID := k.getNextOrderID(ctx) order := exchange.NewOrder(orderID).WithAsk(&msg.AskOrder) if err := k.setOrderInStore(store, *order); err != nil { return 0, fmt.Errorf("error storing ask order: %w", err) @@ -274,8 +266,8 @@ func (k Keeper) CreateAskOrder(ctx sdk.Context, msg *exchange.MsgCreateAskReques return orderID, nil } -// CreateBidOrder creates a bid order, collects the creation fee, and places all needed holds. -func (k Keeper) CreateBidOrder(ctx sdk.Context, msg *exchange.MsgCreateBidRequest) (uint64, error) { +// createBidOrder creates a bid order, collects the creation fee, and places all needed holds. +func (k Keeper) createBidOrder(ctx sdk.Context, msg *exchange.MsgCreateBidRequest) (uint64, error) { store := k.getStore(ctx) marketID := msg.BidOrder.MarketId buyer := sdk.MustAccAddressFromBech32(msg.BidOrder.Buyer) @@ -300,7 +292,7 @@ func (k Keeper) CreateBidOrder(ctx sdk.Context, msg *exchange.MsgCreateBidReques } } - orderID := k.GetNextOrderID(ctx) + orderID := k.getNextOrderID(ctx) order := exchange.NewOrder(orderID).WithBid(&msg.BidOrder) if err := k.setOrderInStore(store, *order); err != nil { return 0, fmt.Errorf("error storing bid order: %w", err) @@ -314,30 +306,29 @@ func (k Keeper) CreateBidOrder(ctx sdk.Context, msg *exchange.MsgCreateBidReques return orderID, nil } -// CancelOrder releases an order's held funds and deletes it. -func (k Keeper) CancelOrder(ctx sdk.Context, msg *exchange.MsgCancelOrderRequest) error { - store := k.getStore(ctx) - order, err := k.getOrderFromStore(store, msg.OrderId) +// cancelOrder releases an order's held funds and deletes it. +func (k Keeper) cancelOrder(ctx sdk.Context, orderID uint64, signer string) error { + order, err := k.GetOrder(ctx, orderID) if err != nil { return err } if order == nil { - return fmt.Errorf("order %d does not exist", msg.OrderId) + return fmt.Errorf("order %d does not exist", orderID) } orderOwner := order.GetOwner() - if msg.Owner != orderOwner && !k.hasPermission(store, order.GetMarketID(), msg.Owner, exchange.Permission_cancel) { - return fmt.Errorf("account %s cannot cancel order %d", msg.Owner, msg.OrderId) + if signer != orderOwner && !k.HasPermission(ctx, order.GetMarketID(), signer, exchange.Permission_cancel) { + return fmt.Errorf("account %s does not have permission to cancel order %d", signer, orderID) } orderOwnerAddr := sdk.MustAccAddressFromBech32(orderOwner) heldAmount := order.GetHoldAmount() err = k.holdKeeper.ReleaseHold(ctx, orderOwnerAddr, heldAmount) if err != nil { - return fmt.Errorf("unable to release hold on order %d funds: %w", msg.OrderId, err) + return fmt.Errorf("unable to release hold on order %d funds: %w", orderID, err) } - deleteOrderFromStore(store, *order) + k.deleteOrder(ctx, *order) return nil } From 5e06f466297c456a655e67d3fed333b36a9ed84b Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 12:07:19 -0600 Subject: [PATCH 105/309] [1658]: Make some of the order keeper stuff public again. Change CreateAskOrder and CreateBidOrder to take in the components instead of msg. Also make them validate the ask/bid order since it's now a public keeper function again which might be used by other modules. --- x/exchange/keeper/market.go | 4 +-- x/exchange/keeper/msg_server.go | 6 ++-- x/exchange/keeper/orders.go | 50 +++++++++++++++++++-------------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 9ad180b932..b0b17b134d 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -325,7 +325,7 @@ func getSellerSettlementRatio(store sdk.KVStore, marketID uint32, priceDenom str return ratio, nil } -// validateAskPrice validates that the provided ask price denom is acceptable. +// validateAskPrice validates that the provided ask price is acceptable. func validateAskPrice(store sdk.KVStore, marketID uint32, price sdk.Coin, settlementFlatFee *sdk.Coin) error { ratio, err := getSellerSettlementRatio(store, marketID, price.Denom) if err != nil { @@ -354,7 +354,7 @@ func validateAskPrice(store sdk.KVStore, marketID uint32, price sdk.Coin, settle } } - return err + return nil } // calculateSellerSettlementRatioFee calculates the seller settlement fee required for the given price. diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 42273e8516..f0720de88f 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -27,7 +27,7 @@ var _ exchange.MsgServer = MsgServer{} // CreateAsk creates an ask order (to sell something you own). func (k MsgServer) CreateAsk(goCtx context.Context, msg *exchange.MsgCreateAskRequest) (*exchange.MsgCreateAskResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - orderID, err := k.createAskOrder(ctx, msg) + orderID, err := k.CreateAskOrder(ctx, msg.AskOrder, msg.OrderCreationFee) if err != nil { return nil, err } @@ -37,7 +37,7 @@ func (k MsgServer) CreateAsk(goCtx context.Context, msg *exchange.MsgCreateAskRe // CreateBid creates an bid order (to buy something you want). func (k MsgServer) CreateBid(goCtx context.Context, msg *exchange.MsgCreateBidRequest) (*exchange.MsgCreateBidResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - orderID, err := k.createBidOrder(ctx, msg) + orderID, err := k.CreateBidOrder(ctx, msg.BidOrder, msg.OrderCreationFee) if err != nil { return nil, err } @@ -47,7 +47,7 @@ func (k MsgServer) CreateBid(goCtx context.Context, msg *exchange.MsgCreateBidRe // CancelOrder cancels an order. func (k MsgServer) CancelOrder(goCtx context.Context, msg *exchange.MsgCancelOrderRequest) (*exchange.MsgCancelOrderResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - err := k.cancelOrder(ctx, msg.OrderId, msg.Owner) + err := k.Keeper.CancelOrder(ctx, msg.OrderId, msg.Owner) if err != nil { return nil, err } diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index a5c7e0ce60..f4e654a897 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -223,11 +223,15 @@ func (k Keeper) IterateAssetBidOrders(ctx sdk.Context, assetDenom string, cb fun }) } -// createAskOrder creates an ask order, collects the creation fee, and places all needed holds. -func (k Keeper) createAskOrder(ctx sdk.Context, msg *exchange.MsgCreateAskRequest) (uint64, error) { +// CreateAskOrder creates an ask order, collects the creation fee, and places all needed holds. +func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, creationFee *sdk.Coin) (uint64, error) { + if err := askOrder.Validate(); err != nil { + return 0, err + } + + seller := sdk.MustAccAddressFromBech32(askOrder.Seller) + marketID := askOrder.MarketId store := k.getStore(ctx) - marketID := msg.AskOrder.MarketId - seller := sdk.MustAccAddressFromBech32(msg.AskOrder.Seller) if err := validateMarketExists(store, marketID); err != nil { return 0, err @@ -235,25 +239,25 @@ func (k Keeper) createAskOrder(ctx sdk.Context, msg *exchange.MsgCreateAskReques if !k.CanCreateAsk(ctx, marketID, seller) { return 0, fmt.Errorf("account %s is not allowed to create ask orders in market %d", seller, marketID) } - if err := validateCreateAskFlatFee(store, marketID, msg.OrderCreationFee); err != nil { + if err := validateCreateAskFlatFee(store, marketID, creationFee); err != nil { return 0, err } - if err := validateSellerSettlementFlatFee(store, marketID, msg.AskOrder.SellerSettlementFlatFee); err != nil { + if err := validateSellerSettlementFlatFee(store, marketID, askOrder.SellerSettlementFlatFee); err != nil { return 0, err } - if err := validateAskPrice(store, marketID, msg.AskOrder.Price, msg.AskOrder.SellerSettlementFlatFee); err != nil { + if err := validateAskPrice(store, marketID, askOrder.Price, askOrder.SellerSettlementFlatFee); err != nil { return 0, err } - if msg.OrderCreationFee != nil { - err := k.CollectFee(ctx, seller, marketID, sdk.Coins{*msg.OrderCreationFee}) + if creationFee != nil { + err := k.CollectFee(ctx, seller, marketID, sdk.Coins{*creationFee}) if err != nil { return 0, fmt.Errorf("error collecting ask order creation fee: %w", err) } } orderID := k.getNextOrderID(ctx) - order := exchange.NewOrder(orderID).WithAsk(&msg.AskOrder) + order := exchange.NewOrder(orderID).WithAsk(&askOrder) if err := k.setOrderInStore(store, *order); err != nil { return 0, fmt.Errorf("error storing ask order: %w", err) } @@ -266,11 +270,15 @@ func (k Keeper) createAskOrder(ctx sdk.Context, msg *exchange.MsgCreateAskReques return orderID, nil } -// createBidOrder creates a bid order, collects the creation fee, and places all needed holds. -func (k Keeper) createBidOrder(ctx sdk.Context, msg *exchange.MsgCreateBidRequest) (uint64, error) { +// CreateBidOrder creates a bid order, collects the creation fee, and places all needed holds. +func (k Keeper) CreateBidOrder(ctx sdk.Context, bidOrder exchange.BidOrder, creationFee *sdk.Coin) (uint64, error) { + if err := bidOrder.Validate(); err != nil { + return 0, err + } + + buyer := sdk.MustAccAddressFromBech32(bidOrder.Buyer) + marketID := bidOrder.MarketId store := k.getStore(ctx) - marketID := msg.BidOrder.MarketId - buyer := sdk.MustAccAddressFromBech32(msg.BidOrder.Buyer) if err := validateMarketExists(store, marketID); err != nil { return 0, err @@ -278,22 +286,22 @@ func (k Keeper) createBidOrder(ctx sdk.Context, msg *exchange.MsgCreateBidReques if !k.CanCreateBid(ctx, marketID, buyer) { return 0, fmt.Errorf("account %s is not allowed to create bid orders in market %d", buyer, marketID) } - if err := validateCreateBidFlatFee(store, marketID, msg.OrderCreationFee); err != nil { + if err := validateCreateBidFlatFee(store, marketID, creationFee); err != nil { return 0, err } - if err := validateBuyerSettlementFee(store, marketID, msg.BidOrder.Price, msg.BidOrder.BuyerSettlementFees); err != nil { + if err := validateBuyerSettlementFee(store, marketID, bidOrder.Price, bidOrder.BuyerSettlementFees); err != nil { return 0, err } - if msg.OrderCreationFee != nil { - err := k.CollectFee(ctx, buyer, marketID, sdk.Coins{*msg.OrderCreationFee}) + if creationFee != nil { + err := k.CollectFee(ctx, buyer, marketID, sdk.Coins{*creationFee}) if err != nil { return 0, fmt.Errorf("error collecting bid order creation fee: %w", err) } } orderID := k.getNextOrderID(ctx) - order := exchange.NewOrder(orderID).WithBid(&msg.BidOrder) + order := exchange.NewOrder(orderID).WithBid(&bidOrder) if err := k.setOrderInStore(store, *order); err != nil { return 0, fmt.Errorf("error storing bid order: %w", err) } @@ -306,8 +314,8 @@ func (k Keeper) createBidOrder(ctx sdk.Context, msg *exchange.MsgCreateBidReques return orderID, nil } -// cancelOrder releases an order's held funds and deletes it. -func (k Keeper) cancelOrder(ctx sdk.Context, orderID uint64, signer string) error { +// CancelOrder releases an order's held funds and deletes it. +func (k Keeper) CancelOrder(ctx sdk.Context, orderID uint64, signer string) error { order, err := k.GetOrder(ctx, orderID) if err != nil { return err From bef968a4d5466cad22bb476bf4606c0ceb56eb27 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 12:34:05 -0600 Subject: [PATCH 106/309] [1658]: Change QueryIsValidMarket into QueryValidateCreateMarket and add QueryValidateManageFees. Also add the market id to the QueryMarketInfoRequest url and get rid of the /info and /details parts of the url. --- docs/proto-docs.md | 81 +++- proto/provenance/exchange/v1/query.proto | 39 +- x/exchange/query.pb.go | 570 ++++++++++++++++++----- x/exchange/query.pb.gw.go | 158 +++++-- 4 files changed, 659 insertions(+), 189 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index dfd1d74f60..6729a0246a 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -99,8 +99,6 @@ - [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) - [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) - [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) - - [QueryIsValidMarketRequest](#provenance.exchange.v1.QueryIsValidMarketRequest) - - [QueryIsValidMarketResponse](#provenance.exchange.v1.QueryIsValidMarketResponse) - [QueryMarketInfoRequest](#provenance.exchange.v1.QueryMarketInfoRequest) - [QueryMarketInfoResponse](#provenance.exchange.v1.QueryMarketInfoResponse) - [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) @@ -109,6 +107,10 @@ - [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) - [QuerySettlementFeeCalcRequest](#provenance.exchange.v1.QuerySettlementFeeCalcRequest) - [QuerySettlementFeeCalcResponse](#provenance.exchange.v1.QuerySettlementFeeCalcResponse) + - [QueryValidateCreateMarketRequest](#provenance.exchange.v1.QueryValidateCreateMarketRequest) + - [QueryValidateCreateMarketResponse](#provenance.exchange.v1.QueryValidateCreateMarketResponse) + - [QueryValidateManageFeesRequest](#provenance.exchange.v1.QueryValidateManageFeesRequest) + - [QueryValidateManageFeesResponse](#provenance.exchange.v1.QueryValidateManageFeesResponse) - [Query](#provenance.exchange.v1.Query) @@ -1954,32 +1956,17 @@ QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. - - -### QueryIsValidMarketRequest -QueryIsValidMarketRequest is a request message for the QueryIsValidMarket endpoint. - - - - - - - - -### QueryIsValidMarketResponse -QueryIsValidMarketResponse is a response message for the QueryIsValidMarket endpoint. - - - - - - ### QueryMarketInfoRequest QueryMarketInfoRequest is a request message for the QueryMarketInfo endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | TODO[1658]: QueryMarketInfoRequest | + + @@ -2053,6 +2040,51 @@ QuerySettlementFeeCalcResponse is a response message for the QuerySettlementFeeC + + + +### QueryValidateCreateMarketRequest +QueryValidateCreateMarketRequest is a request message for the QueryValidateCreateMarket endpoint. + + + + + + + + +### QueryValidateCreateMarketResponse +QueryValidateCreateMarketResponse is a response message for the QueryValidateCreateMarket endpoint. + + + + + + + + +### QueryValidateManageFeesRequest +QueryValidateManageFeesRequest is a request message for the QueryValidateManageFees endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | TODO[1658]: QueryValidateManageFeesRequest | + + + + + + + + +### QueryValidateManageFeesResponse +QueryValidateManageFeesResponse is a response message for the QueryValidateManageFees endpoint. + + + + + @@ -2073,9 +2105,10 @@ Query is the service for exchange module's query endpoints. | `QueryGetMarketOrders` | [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) | [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) | QueryGetMarketOrders looks up the orders in a market. | GET|/provenance/exchange/v1/market/{market_id}/orders| | `QueryGetAddressOrders` | [QueryGetAddressOrdersRequest](#provenance.exchange.v1.QueryGetAddressOrdersRequest) | [QueryGetAddressOrdersResponse](#provenance.exchange.v1.QueryGetAddressOrdersResponse) | QueryGetAddressOrders looks up the orders from the provided address. | GET|/provenance/exchange/v1/orders/{address}| | `QueryGetAllOrders` | [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) | [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) | QueryGetAllOrders gets all orders in the exchange module. | GET|/provenance/exchange/v1/orders| -| `QueryMarketInfo` | [QueryMarketInfoRequest](#provenance.exchange.v1.QueryMarketInfoRequest) | [QueryMarketInfoResponse](#provenance.exchange.v1.QueryMarketInfoResponse) | QueryMarketInfo returns the information/details about a market. | GET|/provenance/exchange/v1/market/infoGET|/provenance/exchange/v1/market/details| +| `QueryMarketInfo` | [QueryMarketInfoRequest](#provenance.exchange.v1.QueryMarketInfoRequest) | [QueryMarketInfoResponse](#provenance.exchange.v1.QueryMarketInfoResponse) | QueryMarketInfo returns the information/details about a market. | GET|/provenance/exchange/v1/market/{market_id}| | `QueryParams` | [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) | [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) | QueryParams returns the exchange module parameters. | GET|/provenance/exchange/v1/params| -| `QueryIsValidMarket` | [QueryIsValidMarketRequest](#provenance.exchange.v1.QueryIsValidMarketRequest) | [QueryIsValidMarketResponse](#provenance.exchange.v1.QueryIsValidMarketResponse) | QueryIsValidMarket checks the provided market and returns any errors that would be encountered trying to create it. | GET|/provenance/exchange/v1/market/validate| +| `QueryValidateCreateMarket` | [QueryValidateCreateMarketRequest](#provenance.exchange.v1.QueryValidateCreateMarketRequest) | [QueryValidateCreateMarketResponse](#provenance.exchange.v1.QueryValidateCreateMarketResponse) | QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. | GET|/provenance/exchange/v1/validate/create_market| +| `QueryValidateManageFees` | [QueryValidateManageFeesRequest](#provenance.exchange.v1.QueryValidateManageFeesRequest) | [QueryValidateManageFeesResponse](#provenance.exchange.v1.QueryValidateManageFeesResponse) | QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. | GET|/provenance/exchange/v1/market/{market_id}/validate/manage_fees| diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 8ac1edb41f..19d3ca9d8c 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -43,10 +43,7 @@ service Query { // QueryMarketInfo returns the information/details about a market. rpc QueryMarketInfo(QueryMarketInfoRequest) returns (QueryMarketInfoResponse) { - option (google.api.http) = { - get: "/provenance/exchange/v1/market/info" - additional_bindings: {get: "/provenance/exchange/v1/market/details"} - }; + option (google.api.http).get = "/provenance/exchange/v1/market/{market_id}"; } // QueryParams returns the exchange module parameters. @@ -54,9 +51,14 @@ service Query { option (google.api.http).get = "/provenance/exchange/v1/params"; } - // QueryIsValidMarket checks the provided market and returns any errors that would be encountered trying to create it. - rpc QueryIsValidMarket(QueryIsValidMarketRequest) returns (QueryIsValidMarketResponse) { - option (google.api.http).get = "/provenance/exchange/v1/market/validate"; + // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. + rpc QueryValidateCreateMarket(QueryValidateCreateMarketRequest) returns (QueryValidateCreateMarketResponse) { + option (google.api.http).get = "/provenance/exchange/v1/validate/create_market"; + } + + // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. + rpc QueryValidateManageFees(QueryValidateManageFeesRequest) returns (QueryValidateManageFeesResponse) { + option (google.api.http).get = "/provenance/exchange/v1/market/{market_id}/validate/manage_fees"; } } @@ -119,6 +121,7 @@ message QueryGetAllOrdersResponse { // QueryMarketInfoRequest is a request message for the QueryMarketInfo endpoint. message QueryMarketInfoRequest { + uint32 market_id = 1; // TODO[1658]: QueryMarketInfoRequest } // QueryMarketInfoResponse is a response message for the QueryMarketInfo endpoint. @@ -135,11 +138,21 @@ message QueryParamsResponse { // TODO[1658]: QueryParamsResponse } -// QueryIsValidMarketRequest is a request message for the QueryIsValidMarket endpoint. -message QueryIsValidMarketRequest { - // TODO[1658]: QueryIsValidMarketRequest +// QueryValidateCreateMarketRequest is a request message for the QueryValidateCreateMarket endpoint. +message QueryValidateCreateMarketRequest { + // TODO[1658]: QueryValidateCreateMarketRequest +} +// QueryValidateCreateMarketResponse is a response message for the QueryValidateCreateMarket endpoint. +message QueryValidateCreateMarketResponse { + // TODO[1658]: QueryValidateCreateMarketResponse +} + +// QueryValidateManageFeesRequest is a request message for the QueryValidateManageFees endpoint. +message QueryValidateManageFeesRequest { + uint32 market_id = 1; + // TODO[1658]: QueryValidateManageFeesRequest } -// QueryIsValidMarketResponse is a response message for the QueryIsValidMarket endpoint. -message QueryIsValidMarketResponse { - // TODO[1658]: QueryIsValidMarketResponse +// QueryValidateManageFeesResponse is a response message for the QueryValidateManageFees endpoint. +message QueryValidateManageFeesResponse { + // TODO[1658]: QueryValidateManageFeesResponse } diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index 9ee041d782..fb71b0b949 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -499,6 +499,7 @@ var xxx_messageInfo_QueryGetAllOrdersResponse proto.InternalMessageInfo // QueryMarketInfoRequest is a request message for the QueryMarketInfo endpoint. type QueryMarketInfoRequest struct { + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` } func (m *QueryMarketInfoRequest) Reset() { *m = QueryMarketInfoRequest{} } @@ -534,6 +535,13 @@ func (m *QueryMarketInfoRequest) XXX_DiscardUnknown() { var xxx_messageInfo_QueryMarketInfoRequest proto.InternalMessageInfo +func (m *QueryMarketInfoRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + // QueryMarketInfoResponse is a response message for the QueryMarketInfo endpoint. type QueryMarketInfoResponse struct { } @@ -645,22 +653,22 @@ func (m *QueryParamsResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo -// QueryIsValidMarketRequest is a request message for the QueryIsValidMarket endpoint. -type QueryIsValidMarketRequest struct { +// QueryValidateCreateMarketRequest is a request message for the QueryValidateCreateMarket endpoint. +type QueryValidateCreateMarketRequest struct { } -func (m *QueryIsValidMarketRequest) Reset() { *m = QueryIsValidMarketRequest{} } -func (m *QueryIsValidMarketRequest) String() string { return proto.CompactTextString(m) } -func (*QueryIsValidMarketRequest) ProtoMessage() {} -func (*QueryIsValidMarketRequest) Descriptor() ([]byte, []int) { +func (m *QueryValidateCreateMarketRequest) Reset() { *m = QueryValidateCreateMarketRequest{} } +func (m *QueryValidateCreateMarketRequest) String() string { return proto.CompactTextString(m) } +func (*QueryValidateCreateMarketRequest) ProtoMessage() {} +func (*QueryValidateCreateMarketRequest) Descriptor() ([]byte, []int) { return fileDescriptor_00949b75b1c10bfe, []int{16} } -func (m *QueryIsValidMarketRequest) XXX_Unmarshal(b []byte) error { +func (m *QueryValidateCreateMarketRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryIsValidMarketRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryValidateCreateMarketRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryIsValidMarketRequest.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryValidateCreateMarketRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -670,34 +678,72 @@ func (m *QueryIsValidMarketRequest) XXX_Marshal(b []byte, deterministic bool) ([ return b[:n], nil } } -func (m *QueryIsValidMarketRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryIsValidMarketRequest.Merge(m, src) +func (m *QueryValidateCreateMarketRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidateCreateMarketRequest.Merge(m, src) } -func (m *QueryIsValidMarketRequest) XXX_Size() int { +func (m *QueryValidateCreateMarketRequest) XXX_Size() int { return m.Size() } -func (m *QueryIsValidMarketRequest) XXX_DiscardUnknown() { - xxx_messageInfo_QueryIsValidMarketRequest.DiscardUnknown(m) +func (m *QueryValidateCreateMarketRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidateCreateMarketRequest.DiscardUnknown(m) } -var xxx_messageInfo_QueryIsValidMarketRequest proto.InternalMessageInfo +var xxx_messageInfo_QueryValidateCreateMarketRequest proto.InternalMessageInfo -// QueryIsValidMarketResponse is a response message for the QueryIsValidMarket endpoint. -type QueryIsValidMarketResponse struct { +// QueryValidateCreateMarketResponse is a response message for the QueryValidateCreateMarket endpoint. +type QueryValidateCreateMarketResponse struct { } -func (m *QueryIsValidMarketResponse) Reset() { *m = QueryIsValidMarketResponse{} } -func (m *QueryIsValidMarketResponse) String() string { return proto.CompactTextString(m) } -func (*QueryIsValidMarketResponse) ProtoMessage() {} -func (*QueryIsValidMarketResponse) Descriptor() ([]byte, []int) { +func (m *QueryValidateCreateMarketResponse) Reset() { *m = QueryValidateCreateMarketResponse{} } +func (m *QueryValidateCreateMarketResponse) String() string { return proto.CompactTextString(m) } +func (*QueryValidateCreateMarketResponse) ProtoMessage() {} +func (*QueryValidateCreateMarketResponse) Descriptor() ([]byte, []int) { return fileDescriptor_00949b75b1c10bfe, []int{17} } -func (m *QueryIsValidMarketResponse) XXX_Unmarshal(b []byte) error { +func (m *QueryValidateCreateMarketResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidateCreateMarketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidateCreateMarketResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryValidateCreateMarketResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidateCreateMarketResponse.Merge(m, src) +} +func (m *QueryValidateCreateMarketResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidateCreateMarketResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidateCreateMarketResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidateCreateMarketResponse proto.InternalMessageInfo + +// QueryValidateManageFeesRequest is a request message for the QueryValidateManageFees endpoint. +type QueryValidateManageFeesRequest struct { + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` +} + +func (m *QueryValidateManageFeesRequest) Reset() { *m = QueryValidateManageFeesRequest{} } +func (m *QueryValidateManageFeesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryValidateManageFeesRequest) ProtoMessage() {} +func (*QueryValidateManageFeesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{18} +} +func (m *QueryValidateManageFeesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryIsValidMarketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryValidateManageFeesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryIsValidMarketResponse.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryValidateManageFeesRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -707,17 +753,61 @@ func (m *QueryIsValidMarketResponse) XXX_Marshal(b []byte, deterministic bool) ( return b[:n], nil } } -func (m *QueryIsValidMarketResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryIsValidMarketResponse.Merge(m, src) +func (m *QueryValidateManageFeesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidateManageFeesRequest.Merge(m, src) } -func (m *QueryIsValidMarketResponse) XXX_Size() int { +func (m *QueryValidateManageFeesRequest) XXX_Size() int { return m.Size() } -func (m *QueryIsValidMarketResponse) XXX_DiscardUnknown() { - xxx_messageInfo_QueryIsValidMarketResponse.DiscardUnknown(m) +func (m *QueryValidateManageFeesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidateManageFeesRequest.DiscardUnknown(m) } -var xxx_messageInfo_QueryIsValidMarketResponse proto.InternalMessageInfo +var xxx_messageInfo_QueryValidateManageFeesRequest proto.InternalMessageInfo + +func (m *QueryValidateManageFeesRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +// QueryValidateManageFeesResponse is a response message for the QueryValidateManageFees endpoint. +type QueryValidateManageFeesResponse struct { +} + +func (m *QueryValidateManageFeesResponse) Reset() { *m = QueryValidateManageFeesResponse{} } +func (m *QueryValidateManageFeesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryValidateManageFeesResponse) ProtoMessage() {} +func (*QueryValidateManageFeesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{19} +} +func (m *QueryValidateManageFeesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidateManageFeesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidateManageFeesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryValidateManageFeesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidateManageFeesResponse.Merge(m, src) +} +func (m *QueryValidateManageFeesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidateManageFeesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidateManageFeesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidateManageFeesResponse proto.InternalMessageInfo func init() { proto.RegisterType((*QueryOrderFeeCalcRequest)(nil), "provenance.exchange.v1.QueryOrderFeeCalcRequest") @@ -736,8 +826,10 @@ func init() { proto.RegisterType((*QueryMarketInfoResponse)(nil), "provenance.exchange.v1.QueryMarketInfoResponse") proto.RegisterType((*QueryParamsRequest)(nil), "provenance.exchange.v1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "provenance.exchange.v1.QueryParamsResponse") - proto.RegisterType((*QueryIsValidMarketRequest)(nil), "provenance.exchange.v1.QueryIsValidMarketRequest") - proto.RegisterType((*QueryIsValidMarketResponse)(nil), "provenance.exchange.v1.QueryIsValidMarketResponse") + proto.RegisterType((*QueryValidateCreateMarketRequest)(nil), "provenance.exchange.v1.QueryValidateCreateMarketRequest") + proto.RegisterType((*QueryValidateCreateMarketResponse)(nil), "provenance.exchange.v1.QueryValidateCreateMarketResponse") + proto.RegisterType((*QueryValidateManageFeesRequest)(nil), "provenance.exchange.v1.QueryValidateManageFeesRequest") + proto.RegisterType((*QueryValidateManageFeesResponse)(nil), "provenance.exchange.v1.QueryValidateManageFeesResponse") } func init() { @@ -745,53 +837,57 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 729 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xcf, 0x4f, 0x13, 0x41, - 0x14, 0x66, 0x8c, 0x0a, 0x8c, 0x12, 0xe3, 0x93, 0x1f, 0xed, 0x82, 0x2b, 0x59, 0x0d, 0x22, 0xca, - 0x0e, 0x05, 0x31, 0xd1, 0x9b, 0x98, 0x68, 0x7a, 0x30, 0x60, 0x89, 0x1e, 0xb8, 0x90, 0xa1, 0x3b, - 0x94, 0x8d, 0xdb, 0x9d, 0xb2, 0xb3, 0x34, 0x18, 0xc2, 0xc5, 0x3f, 0xc0, 0x98, 0x78, 0x37, 0x1e, - 0x3c, 0x7a, 0x31, 0x31, 0xc6, 0x83, 0x7f, 0x80, 0xde, 0x88, 0x5e, 0x3c, 0x1a, 0xea, 0x1f, 0x62, - 0x3a, 0x3b, 0xdb, 0x76, 0xdb, 0xdd, 0x6d, 0xf7, 0xb8, 0xf3, 0xbe, 0xef, 0x7d, 0x5f, 0x67, 0xde, - 0xfb, 0x52, 0x6c, 0xd4, 0x3c, 0x5e, 0x67, 0x2e, 0x75, 0xcb, 0x8c, 0xb0, 0xc3, 0xf2, 0x1e, 0x75, - 0x2b, 0x8c, 0xd4, 0x0b, 0x64, 0xff, 0x80, 0x79, 0xaf, 0xcc, 0x9a, 0xc7, 0x7d, 0x0e, 0x93, 0x6d, - 0x8c, 0x19, 0x62, 0xcc, 0x7a, 0x41, 0xcb, 0x97, 0xb9, 0xa8, 0x72, 0xb1, 0x2d, 0x51, 0x24, 0xf8, - 0x08, 0x28, 0xda, 0x4c, 0x85, 0xf3, 0x8a, 0xc3, 0x08, 0xad, 0xd9, 0x84, 0xba, 0x2e, 0xf7, 0xa9, - 0x6f, 0x73, 0x57, 0x55, 0x0d, 0x0d, 0xe7, 0x9e, 0x35, 0xfb, 0xaf, 0x7b, 0x16, 0xf3, 0x1e, 0x33, - 0xf6, 0x88, 0x3a, 0xe5, 0x12, 0xdb, 0x3f, 0x60, 0xc2, 0x37, 0xa6, 0x71, 0x3e, 0xa6, 0x26, 0x6a, - 0xdc, 0x15, 0xcc, 0xb8, 0x86, 0xaf, 0xca, 0xe2, 0x26, 0xf3, 0x7d, 0x87, 0x55, 0x99, 0xeb, 0x77, - 0xb1, 0x67, 0xb1, 0x9e, 0x04, 0x50, 0x2d, 0x0a, 0x78, 0x5c, 0x22, 0x9e, 0x30, 0x5f, 0x4a, 0x28, - 0x26, 0xe4, 0xf1, 0x08, 0x6f, 0x7e, 0x6f, 0xdb, 0x56, 0x0e, 0xcd, 0xa2, 0xf9, 0xb3, 0xa5, 0x61, - 0xf9, 0x5d, 0xb4, 0x8c, 0x29, 0x3c, 0xd1, 0x45, 0x51, 0xbd, 0x1e, 0xe0, 0xe9, 0xb0, 0xf0, 0x94, - 0x7a, 0x2f, 0x55, 0x59, 0x84, 0x2d, 0xa7, 0xf1, 0x68, 0x55, 0x1e, 0x87, 0x3d, 0xc7, 0x4a, 0x23, - 0xc1, 0x41, 0xd1, 0x32, 0x74, 0x3c, 0x13, 0xcf, 0x55, 0xbd, 0x4b, 0xed, 0xfa, 0x43, 0xcb, 0xf2, - 0x98, 0x10, 0xd1, 0xe6, 0xcb, 0x78, 0x98, 0x06, 0xe7, 0xb2, 0xf5, 0xe8, 0x5a, 0xee, 0xd7, 0x97, - 0xc5, 0x71, 0xf5, 0x08, 0x8a, 0xb1, 0xe9, 0x7b, 0xb6, 0x5b, 0x29, 0x85, 0xc0, 0xd6, 0xf5, 0xf5, - 0xf6, 0x54, 0xa2, 0xe1, 0xc3, 0x34, 0x01, 0x8e, 0x13, 0x11, 0x6c, 0x3d, 0x4c, 0xb4, 0xa6, 0x88, - 0x39, 0x3c, 0x29, 0x8b, 0xc1, 0x4f, 0x29, 0xba, 0xbb, 0x3c, 0xa4, 0xe5, 0xf1, 0x54, 0x4f, 0x45, - 0x91, 0xc6, 0x31, 0xc8, 0xd2, 0x06, 0xf5, 0x68, 0xb5, 0xa5, 0x33, 0x81, 0xaf, 0x44, 0x4e, 0x15, - 0x38, 0x94, 0x2f, 0x8a, 0x17, 0xd4, 0xb1, 0xad, 0xa0, 0x5d, 0xc8, 0x99, 0xc1, 0x5a, 0x5c, 0x31, - 0xa0, 0x2e, 0x7f, 0xbe, 0x88, 0xcf, 0xc9, 0x32, 0x7c, 0x44, 0xf8, 0x72, 0xcf, 0x74, 0xc1, 0x92, - 0x19, 0x3f, 0xe0, 0x66, 0xd2, 0x90, 0x6a, 0x85, 0x0c, 0x0c, 0xe5, 0x7f, 0xe1, 0xf5, 0xef, 0x7f, - 0xef, 0xce, 0xdc, 0x00, 0x83, 0x24, 0x6c, 0xdc, 0x2e, 0x63, 0x82, 0xc8, 0x91, 0x83, 0x6f, 0x48, - 0x5d, 0x67, 0xcf, 0x18, 0xc3, 0x6a, 0xaa, 0x72, 0xd2, 0x5e, 0x68, 0xf7, 0xb2, 0xd2, 0x94, 0x6b, - 0x22, 0x5d, 0xdf, 0x82, 0x9b, 0xa9, 0xae, 0x45, 0x8b, 0x0f, 0xef, 0x11, 0x1e, 0x8b, 0x2c, 0x0b, - 0xdc, 0x49, 0x95, 0xee, 0x5a, 0x43, 0x6d, 0x71, 0x40, 0xb4, 0xf2, 0xb7, 0x24, 0xfd, 0x2d, 0xc0, - 0x7c, 0x92, 0x3f, 0x79, 0xa1, 0xe4, 0x28, 0x5c, 0xed, 0x63, 0xf8, 0x8e, 0xda, 0x01, 0xd0, 0xb9, - 0x78, 0xb0, 0xd2, 0x4f, 0x39, 0x66, 0xc5, 0xb5, 0xbb, 0xd9, 0x48, 0xca, 0xf5, 0x7d, 0xe9, 0x7a, - 0x05, 0x0a, 0x49, 0xae, 0x83, 0x94, 0x20, 0x47, 0xad, 0xf8, 0x38, 0x0e, 0x7e, 0x88, 0x80, 0xaf, - 0xa8, 0x1d, 0x46, 0x91, 0x1d, 0x86, 0xbe, 0x56, 0xe2, 0x62, 0x44, 0x5b, 0xcd, 0xc8, 0xca, 0x74, - 0xef, 0x82, 0x1c, 0xa9, 0xe8, 0x39, 0x86, 0x0f, 0xe1, 0xea, 0x75, 0xe6, 0x47, 0x9f, 0xd5, 0x8b, - 0x89, 0xa1, 0x3e, 0xab, 0x17, 0x1b, 0x4e, 0x73, 0xd2, 0xec, 0x2c, 0xe8, 0xe9, 0x66, 0xe1, 0x27, - 0xc2, 0x97, 0xba, 0xb2, 0x0a, 0xcc, 0x54, 0xb9, 0x9e, 0xb8, 0xd3, 0xc8, 0xc0, 0x78, 0x65, 0xee, - 0xb9, 0x34, 0xb7, 0x0e, 0xd7, 0xfb, 0xcc, 0x82, 0xed, 0xee, 0xf2, 0xad, 0x79, 0x98, 0xeb, 0x03, - 0xb3, 0x98, 0x4f, 0x6d, 0x47, 0xc0, 0x1b, 0x84, 0x2f, 0x74, 0xc4, 0x28, 0x2c, 0xa4, 0xfa, 0x8a, - 0x24, 0xb0, 0x76, 0x7b, 0x20, 0xec, 0xa0, 0x97, 0x5b, 0x0b, 0x0c, 0x7c, 0x42, 0x2a, 0xed, 0x23, - 0x19, 0x0d, 0xe9, 0xcf, 0x19, 0x17, 0xf6, 0xda, 0x72, 0x16, 0xca, 0xa0, 0x39, 0xa6, 0xae, 0xaf, - 0xde, 0xe4, 0x52, 0x9f, 0xad, 0xb1, 0x1f, 0xa7, 0x3a, 0x3a, 0x39, 0xd5, 0xd1, 0xdf, 0x53, 0x1d, - 0xbd, 0x6d, 0xe8, 0x43, 0x27, 0x0d, 0x7d, 0xe8, 0x4f, 0x43, 0x1f, 0xc2, 0x79, 0x9b, 0x27, 0x18, - 0xd8, 0x40, 0x5b, 0x66, 0xc5, 0xf6, 0xf7, 0x0e, 0x76, 0xcc, 0x32, 0xaf, 0x76, 0x28, 0x2d, 0xda, - 0xbc, 0x53, 0xf7, 0xb0, 0xa5, 0xbc, 0x73, 0x5e, 0xfe, 0x21, 0x5a, 0xf9, 0x1f, 0x00, 0x00, 0xff, - 0xff, 0xae, 0xe1, 0xd2, 0x33, 0x87, 0x09, 0x00, 0x00, + // 794 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcd, 0x4e, 0x14, 0x4d, + 0x14, 0xa5, 0xbe, 0x7c, 0x0a, 0x94, 0x41, 0xe3, 0x95, 0xbf, 0x69, 0xb0, 0x19, 0x5a, 0xa3, 0x38, + 0x42, 0x17, 0x03, 0x82, 0x62, 0x62, 0x0c, 0x90, 0x40, 0x58, 0x10, 0x71, 0x48, 0x5c, 0xb8, 0x99, + 0x14, 0x33, 0xc5, 0xd0, 0x71, 0xa6, 0x6b, 0xe8, 0x6e, 0x26, 0x18, 0x42, 0x62, 0x7c, 0x00, 0x63, + 0xe2, 0xde, 0xb8, 0x30, 0x3e, 0x81, 0x31, 0x2e, 0x7c, 0x00, 0x96, 0x44, 0x36, 0x2e, 0x0d, 0xf8, + 0x20, 0x66, 0xaa, 0xab, 0xe7, 0xaf, 0x7f, 0xa6, 0xdb, 0x65, 0xd7, 0x3d, 0xe7, 0xde, 0x53, 0x55, + 0xf7, 0x9e, 0x6a, 0xac, 0x55, 0x2d, 0x5e, 0x63, 0x26, 0x35, 0x0b, 0x8c, 0xb0, 0xc3, 0xc2, 0x1e, + 0x35, 0x4b, 0x8c, 0xd4, 0xb2, 0x64, 0xff, 0x80, 0x59, 0xaf, 0xf5, 0xaa, 0xc5, 0x1d, 0x0e, 0xc3, + 0x4d, 0x8c, 0xee, 0x61, 0xf4, 0x5a, 0x56, 0x49, 0x15, 0xb8, 0x5d, 0xe1, 0x76, 0x5e, 0xa0, 0x88, + 0xfb, 0xe1, 0x52, 0x94, 0xf1, 0x12, 0xe7, 0xa5, 0x32, 0x23, 0xb4, 0x6a, 0x10, 0x6a, 0x9a, 0xdc, + 0xa1, 0x8e, 0xc1, 0x4d, 0x19, 0xd5, 0x14, 0x3c, 0xfa, 0xbc, 0x9e, 0xff, 0x99, 0x55, 0x64, 0xd6, + 0x1a, 0x63, 0xab, 0xb4, 0x5c, 0xc8, 0xb1, 0xfd, 0x03, 0x66, 0x3b, 0xda, 0x18, 0x4e, 0x05, 0xc4, + 0xec, 0x2a, 0x37, 0x6d, 0xa6, 0x4d, 0xe0, 0x9b, 0x22, 0xb8, 0xcd, 0x1c, 0xa7, 0xcc, 0x2a, 0xcc, + 0x74, 0x3a, 0xd8, 0x69, 0xac, 0x86, 0x01, 0x64, 0x8a, 0x2c, 0x1e, 0x14, 0x88, 0x75, 0xe6, 0x88, + 0x12, 0x92, 0x09, 0x29, 0xdc, 0xc7, 0xeb, 0xdf, 0x79, 0xa3, 0x38, 0x8a, 0xd2, 0x68, 0xea, 0xff, + 0x5c, 0xaf, 0xf8, 0xde, 0x28, 0x6a, 0x23, 0x78, 0xa8, 0x83, 0x22, 0x73, 0x3d, 0xc6, 0x63, 0x5e, + 0x60, 0x93, 0x5a, 0xaf, 0x64, 0xd8, 0xf6, 0x52, 0x8e, 0xe1, 0xfe, 0x8a, 0x58, 0xf6, 0x72, 0x0e, + 0xe4, 0xfa, 0xdc, 0x85, 0x8d, 0xa2, 0xa6, 0xe2, 0xf1, 0x60, 0xae, 0xcc, 0x9d, 0x6b, 0xc6, 0x97, + 0x8b, 0x45, 0x8b, 0xd9, 0x76, 0x7b, 0xf2, 0x39, 0xdc, 0x4b, 0xdd, 0x75, 0x91, 0xba, 0x7f, 0x65, + 0xf4, 0xe7, 0xd7, 0x99, 0x41, 0x79, 0x09, 0x92, 0xb1, 0xed, 0x58, 0x86, 0x59, 0xca, 0x79, 0xc0, + 0xc6, 0xf1, 0xf9, 0x73, 0xca, 0xa2, 0xde, 0xc5, 0xd4, 0x01, 0xe5, 0x72, 0x5b, 0xc1, 0xc6, 0xc5, + 0xb4, 0xc7, 0x24, 0x71, 0x01, 0x0f, 0x8b, 0xa0, 0xbb, 0x95, 0x0d, 0x73, 0x97, 0xc7, 0x3a, 0x84, + 0x14, 0x1e, 0xf1, 0xd1, 0x64, 0xc6, 0x41, 0x0c, 0x22, 0xb4, 0x45, 0x2d, 0x5a, 0x69, 0x88, 0x18, + 0xc2, 0x37, 0xda, 0x56, 0x25, 0x58, 0xc3, 0x69, 0xb1, 0xfc, 0x82, 0x96, 0x8d, 0x22, 0x75, 0xd8, + 0xaa, 0xc5, 0xa8, 0xc3, 0xdc, 0xac, 0x1e, 0xf5, 0x16, 0x9e, 0x8c, 0xc0, 0xc8, 0x44, 0x4f, 0x64, + 0xff, 0x78, 0xa0, 0x4d, 0x6a, 0xd2, 0x12, 0x5b, 0x63, 0x2c, 0xde, 0xa5, 0x4e, 0xe2, 0x89, 0x50, + 0xba, 0x5b, 0x61, 0xee, 0xcd, 0x55, 0x7c, 0x49, 0x60, 0xe0, 0x33, 0xc2, 0xd7, 0x7d, 0xad, 0x0e, + 0xb3, 0x7a, 0xf0, 0xb4, 0xe9, 0x61, 0x13, 0xa3, 0x64, 0x13, 0x30, 0xe4, 0x36, 0x33, 0x6f, 0xcf, + 0xfe, 0x7c, 0xf8, 0xef, 0x36, 0x68, 0x24, 0x64, 0xfc, 0x77, 0x19, 0xb3, 0x89, 0xe8, 0x7f, 0xf8, + 0x8e, 0xe4, 0xdd, 0xfa, 0x66, 0x0a, 0x16, 0x22, 0x2b, 0x87, 0x0d, 0xa9, 0xb2, 0x98, 0x94, 0x26, + 0x55, 0x13, 0xa1, 0xfa, 0x1e, 0xdc, 0x8d, 0x54, 0x6d, 0x37, 0xf8, 0xf0, 0x11, 0xe1, 0x81, 0xb6, + 0xc9, 0x85, 0xe9, 0xc8, 0xd2, 0x1d, 0x9e, 0xa0, 0xcc, 0xc4, 0x44, 0x4b, 0x7d, 0xb3, 0x42, 0x5f, + 0x06, 0xa6, 0xc2, 0xf4, 0x89, 0x03, 0x25, 0x47, 0x9e, 0xcf, 0x1c, 0xc3, 0x0f, 0xd4, 0x74, 0xa3, + 0x56, 0x17, 0x80, 0xf9, 0x6e, 0x95, 0x03, 0xfc, 0x46, 0x79, 0x90, 0x8c, 0x24, 0x55, 0x2f, 0x09, + 0xd5, 0xf3, 0x90, 0x0d, 0x53, 0xed, 0x76, 0x37, 0x39, 0x6a, 0xb4, 0xfd, 0xb1, 0xbb, 0x11, 0x1b, + 0xbe, 0xa1, 0xa6, 0x33, 0xb6, 0x19, 0x0a, 0x74, 0x95, 0x12, 0xe4, 0x69, 0xca, 0x42, 0x42, 0x56, + 0xa2, 0x73, 0xb7, 0xc9, 0x91, 0xf4, 0xc1, 0x63, 0xf8, 0xe4, 0x8d, 0x5e, 0xab, 0x99, 0x75, 0x19, + 0xbd, 0x00, 0x4f, 0xec, 0x32, 0x7a, 0x81, 0x4e, 0x79, 0x47, 0x88, 0x4d, 0x83, 0x1a, 0x2d, 0x16, + 0xbe, 0x20, 0x7c, 0xad, 0xc3, 0x1b, 0x41, 0x8f, 0x2c, 0xe7, 0xf3, 0x5e, 0x85, 0xc4, 0xc6, 0x4b, + 0x71, 0x73, 0x42, 0xdc, 0x34, 0x64, 0xe2, 0xf7, 0x02, 0xbc, 0x43, 0xf8, 0x4a, 0x8b, 0x27, 0x43, + 0x26, 0xb2, 0x68, 0x9b, 0x9d, 0x2b, 0xf7, 0x63, 0x61, 0xe3, 0x9e, 0x5c, 0xd5, 0x15, 0x70, 0x82, + 0xe4, 0x4b, 0x15, 0xe4, 0xf4, 0xf0, 0x28, 0xb2, 0x64, 0xc4, 0x03, 0xa2, 0x2c, 0xfd, 0x03, 0x53, + 0x4a, 0x5f, 0x14, 0xd2, 0x67, 0x41, 0x0f, 0x93, 0x5e, 0x93, 0x6c, 0x52, 0x10, 0xf4, 0xbc, 0x7b, + 0xbe, 0x70, 0x86, 0xe4, 0x03, 0xe9, 0x7f, 0x50, 0x60, 0x31, 0x96, 0x1c, 0xdf, 0x03, 0xa6, 0x3c, + 0x4c, 0xcc, 0x93, 0x9b, 0x58, 0x17, 0x9b, 0x58, 0x86, 0xa7, 0x09, 0x8c, 0xa2, 0xb1, 0xaf, 0x8a, + 0xc8, 0x97, 0xaf, 0x3b, 0xf4, 0x0a, 0x3b, 0x39, 0x57, 0xd1, 0xe9, 0xb9, 0x8a, 0x7e, 0x9f, 0xab, + 0xe8, 0xfd, 0x85, 0xda, 0x73, 0x7a, 0xa1, 0xf6, 0xfc, 0xba, 0x50, 0x7b, 0x70, 0xca, 0xe0, 0x21, + 0xea, 0xb6, 0xd0, 0x4b, 0xbd, 0x64, 0x38, 0x7b, 0x07, 0x3b, 0x7a, 0x81, 0x57, 0x5a, 0x14, 0xcc, + 0x18, 0xbc, 0x55, 0xcf, 0x61, 0x43, 0xd1, 0xce, 0x65, 0xf1, 0xb3, 0x39, 0xff, 0x37, 0x00, 0x00, + 0xff, 0xff, 0x28, 0x02, 0x0e, 0xa9, 0xe3, 0x0a, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -822,8 +918,10 @@ type QueryClient interface { QueryMarketInfo(ctx context.Context, in *QueryMarketInfoRequest, opts ...grpc.CallOption) (*QueryMarketInfoResponse, error) // QueryParams returns the exchange module parameters. QueryParams(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) - // QueryIsValidMarket checks the provided market and returns any errors that would be encountered trying to create it. - QueryIsValidMarket(ctx context.Context, in *QueryIsValidMarketRequest, opts ...grpc.CallOption) (*QueryIsValidMarketResponse, error) + // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. + QueryValidateCreateMarket(ctx context.Context, in *QueryValidateCreateMarketRequest, opts ...grpc.CallOption) (*QueryValidateCreateMarketResponse, error) + // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. + QueryValidateManageFees(ctx context.Context, in *QueryValidateManageFeesRequest, opts ...grpc.CallOption) (*QueryValidateManageFeesResponse, error) } type queryClient struct { @@ -906,9 +1004,18 @@ func (c *queryClient) QueryParams(ctx context.Context, in *QueryParamsRequest, o return out, nil } -func (c *queryClient) QueryIsValidMarket(ctx context.Context, in *QueryIsValidMarketRequest, opts ...grpc.CallOption) (*QueryIsValidMarketResponse, error) { - out := new(QueryIsValidMarketResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryIsValidMarket", in, out, opts...) +func (c *queryClient) QueryValidateCreateMarket(ctx context.Context, in *QueryValidateCreateMarketRequest, opts ...grpc.CallOption) (*QueryValidateCreateMarketResponse, error) { + out := new(QueryValidateCreateMarketResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryValidateCreateMarket", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) QueryValidateManageFees(ctx context.Context, in *QueryValidateManageFeesRequest, opts ...grpc.CallOption) (*QueryValidateManageFeesResponse, error) { + out := new(QueryValidateManageFeesResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryValidateManageFees", in, out, opts...) if err != nil { return nil, err } @@ -933,8 +1040,10 @@ type QueryServer interface { QueryMarketInfo(context.Context, *QueryMarketInfoRequest) (*QueryMarketInfoResponse, error) // QueryParams returns the exchange module parameters. QueryParams(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) - // QueryIsValidMarket checks the provided market and returns any errors that would be encountered trying to create it. - QueryIsValidMarket(context.Context, *QueryIsValidMarketRequest) (*QueryIsValidMarketResponse, error) + // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. + QueryValidateCreateMarket(context.Context, *QueryValidateCreateMarketRequest) (*QueryValidateCreateMarketResponse, error) + // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. + QueryValidateManageFees(context.Context, *QueryValidateManageFeesRequest) (*QueryValidateManageFeesResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -965,8 +1074,11 @@ func (*UnimplementedQueryServer) QueryMarketInfo(ctx context.Context, req *Query func (*UnimplementedQueryServer) QueryParams(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryParams not implemented") } -func (*UnimplementedQueryServer) QueryIsValidMarket(ctx context.Context, req *QueryIsValidMarketRequest) (*QueryIsValidMarketResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryIsValidMarket not implemented") +func (*UnimplementedQueryServer) QueryValidateCreateMarket(ctx context.Context, req *QueryValidateCreateMarketRequest) (*QueryValidateCreateMarketResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryValidateCreateMarket not implemented") +} +func (*UnimplementedQueryServer) QueryValidateManageFees(ctx context.Context, req *QueryValidateManageFeesRequest) (*QueryValidateManageFeesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryValidateManageFees not implemented") } func RegisterQueryServer(s grpc1.Server, srv QueryServer) { @@ -1117,20 +1229,38 @@ func _Query_QueryParams_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } -func _Query_QueryIsValidMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QueryIsValidMarketRequest) +func _Query_QueryValidateCreateMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidateCreateMarketRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryIsValidMarket(ctx, in) + return srv.(QueryServer).QueryValidateCreateMarket(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryIsValidMarket", + FullMethod: "/provenance.exchange.v1.Query/QueryValidateCreateMarket", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryIsValidMarket(ctx, req.(*QueryIsValidMarketRequest)) + return srv.(QueryServer).QueryValidateCreateMarket(ctx, req.(*QueryValidateCreateMarketRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_QueryValidateManageFees_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidateManageFeesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryValidateManageFees(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryValidateManageFees", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryValidateManageFees(ctx, req.(*QueryValidateManageFeesRequest)) } return interceptor(ctx, in, info, handler) } @@ -1172,8 +1302,12 @@ var _Query_serviceDesc = grpc.ServiceDesc{ Handler: _Query_QueryParams_Handler, }, { - MethodName: "QueryIsValidMarket", - Handler: _Query_QueryIsValidMarket_Handler, + MethodName: "QueryValidateCreateMarket", + Handler: _Query_QueryValidateCreateMarket_Handler, + }, + { + MethodName: "QueryValidateManageFees", + Handler: _Query_QueryValidateManageFees_Handler, }, }, Streams: []grpc.StreamDesc{}, @@ -1493,6 +1627,11 @@ func (m *QueryMarketInfoRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if m.MarketId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } return len(dAtA) - i, nil } @@ -1565,7 +1704,53 @@ func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *QueryIsValidMarketRequest) Marshal() (dAtA []byte, err error) { +func (m *QueryValidateCreateMarketRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryValidateCreateMarketRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidateCreateMarketRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryValidateCreateMarketResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryValidateCreateMarketResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidateCreateMarketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryValidateManageFeesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1575,20 +1760,25 @@ func (m *QueryIsValidMarketRequest) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryIsValidMarketRequest) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryValidateManageFeesRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryIsValidMarketRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryValidateManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l + if m.MarketId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } return len(dAtA) - i, nil } -func (m *QueryIsValidMarketResponse) Marshal() (dAtA []byte, err error) { +func (m *QueryValidateManageFeesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1598,12 +1788,12 @@ func (m *QueryIsValidMarketResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryIsValidMarketResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryValidateManageFeesResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryIsValidMarketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryValidateManageFeesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -1746,6 +1936,9 @@ func (m *QueryMarketInfoRequest) Size() (n int) { } var l int _ = l + if m.MarketId != 0 { + n += 1 + sovQuery(uint64(m.MarketId)) + } return n } @@ -1776,16 +1969,37 @@ func (m *QueryParamsResponse) Size() (n int) { return n } -func (m *QueryIsValidMarketRequest) Size() (n int) { +func (m *QueryValidateCreateMarketRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryValidateCreateMarketResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryValidateManageFeesRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l + if m.MarketId != 0 { + n += 1 + sovQuery(uint64(m.MarketId)) + } return n } -func (m *QueryIsValidMarketResponse) Size() (n int) { +func (m *QueryValidateManageFeesResponse) Size() (n int) { if m == nil { return 0 } @@ -2499,6 +2713,25 @@ func (m *QueryMarketInfoRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: QueryMarketInfoRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -2670,7 +2903,7 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryIsValidMarketRequest) Unmarshal(dAtA []byte) error { +func (m *QueryValidateCreateMarketRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2693,10 +2926,10 @@ func (m *QueryIsValidMarketRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryIsValidMarketRequest: wiretype end group for non-group") + return fmt.Errorf("proto: QueryValidateCreateMarketRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryIsValidMarketRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryValidateCreateMarketRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: @@ -2720,7 +2953,126 @@ func (m *QueryIsValidMarketRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryIsValidMarketResponse) Unmarshal(dAtA []byte) error { +func (m *QueryValidateCreateMarketResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryValidateCreateMarketResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidateCreateMarketResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryValidateManageFeesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryValidateManageFeesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidateManageFeesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryValidateManageFeesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2743,10 +3095,10 @@ func (m *QueryIsValidMarketResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryIsValidMarketResponse: wiretype end group for non-group") + return fmt.Errorf("proto: QueryValidateManageFeesResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryIsValidMarketResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryValidateManageFeesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: diff --git a/x/exchange/query.pb.gw.go b/x/exchange/query.pb.gw.go index 0b90c33f0a..44641a4c7e 100644 --- a/x/exchange/query.pb.gw.go +++ b/x/exchange/query.pb.gw.go @@ -251,6 +251,24 @@ func request_Query_QueryMarketInfo_0(ctx context.Context, marshaler runtime.Mars var protoReq QueryMarketInfoRequest var metadata runtime.ServerMetadata + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + msg, err := client.QueryMarketInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -260,61 +278,115 @@ func local_request_Query_QueryMarketInfo_0(ctx context.Context, marshaler runtim var protoReq QueryMarketInfoRequest var metadata runtime.ServerMetadata + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + msg, err := server.QueryMarketInfo(ctx, &protoReq) return msg, metadata, err } -func request_Query_QueryMarketInfo_1(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryMarketInfoRequest +func request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest var metadata runtime.ServerMetadata - msg, err := client.QueryMarketInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.QueryParams(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryMarketInfo_1(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryMarketInfoRequest +func local_request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest var metadata runtime.ServerMetadata - msg, err := server.QueryMarketInfo(ctx, &protoReq) + msg, err := server.QueryParams(ctx, &protoReq) return msg, metadata, err } -func request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryParamsRequest +func request_Query_QueryValidateCreateMarket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidateCreateMarketRequest var metadata runtime.ServerMetadata - msg, err := client.QueryParams(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.QueryValidateCreateMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryParamsRequest +func local_request_Query_QueryValidateCreateMarket_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidateCreateMarketRequest var metadata runtime.ServerMetadata - msg, err := server.QueryParams(ctx, &protoReq) + msg, err := server.QueryValidateCreateMarket(ctx, &protoReq) return msg, metadata, err } -func request_Query_QueryIsValidMarket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryIsValidMarketRequest +func request_Query_QueryValidateManageFees_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidateManageFeesRequest var metadata runtime.ServerMetadata - msg, err := client.QueryIsValidMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + msg, err := client.QueryValidateManageFees(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryIsValidMarket_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryIsValidMarketRequest +func local_request_Query_QueryValidateManageFees_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidateManageFeesRequest var metadata runtime.ServerMetadata - msg, err := server.QueryIsValidMarket(ctx, &protoReq) + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + msg, err := server.QueryValidateManageFees(ctx, &protoReq) return msg, metadata, err } @@ -465,7 +537,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) - mux.Handle("GET", pattern_Query_QueryMarketInfo_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_QueryParams_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -474,18 +546,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryMarketInfo_1(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_QueryParams_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryMarketInfo_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryParams_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryParams_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_QueryValidateCreateMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -494,18 +566,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryParams_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_QueryValidateCreateMarket_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryParams_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryValidateCreateMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryIsValidMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_QueryValidateManageFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -514,14 +586,14 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryIsValidMarket_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_QueryValidateManageFees_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryIsValidMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryValidateManageFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -706,7 +778,7 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) - mux.Handle("GET", pattern_Query_QueryMarketInfo_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_QueryParams_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -715,18 +787,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryMarketInfo_1(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_QueryParams_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryMarketInfo_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryParams_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryParams_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_QueryValidateCreateMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -735,18 +807,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryParams_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_QueryValidateCreateMarket_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryParams_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryValidateCreateMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryIsValidMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_QueryValidateManageFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -755,14 +827,14 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryIsValidMarket_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_QueryValidateManageFees_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryIsValidMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryValidateManageFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -782,13 +854,13 @@ var ( pattern_Query_QueryGetAllOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "orders"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryMarketInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "market", "info"}, "", runtime.AssumeColonVerbOpt(false))) - - pattern_Query_QueryMarketInfo_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "market", "details"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryMarketInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "market", "market_id"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryIsValidMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "market", "validate"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryValidateCreateMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "validate", "create_market"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryValidateManageFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 2, 6}, []string{"provenance", "exchange", "v1", "market", "market_id", "validate", "manage_fees"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -806,9 +878,9 @@ var ( forward_Query_QueryMarketInfo_0 = runtime.ForwardResponseMessage - forward_Query_QueryMarketInfo_1 = runtime.ForwardResponseMessage - forward_Query_QueryParams_0 = runtime.ForwardResponseMessage - forward_Query_QueryIsValidMarket_0 = runtime.ForwardResponseMessage + forward_Query_QueryValidateCreateMarket_0 = runtime.ForwardResponseMessage + + forward_Query_QueryValidateManageFees_0 = runtime.ForwardResponseMessage ) From 1a1dc7980422c340d97dac84c7c7359c634de348 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 12:42:58 -0600 Subject: [PATCH 107/309] [1658]: Fix 'an bid' in a couple places. --- docs/proto-docs.md | 2 +- proto/provenance/exchange/v1/tx.proto | 2 +- x/exchange/keeper/msg_server.go | 2 +- x/exchange/tx.pb.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 6729a0246a..544527d91a 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2501,7 +2501,7 @@ Msg is the service for exchange module's tx endpoints. | Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | | ----------- | ------------ | ------------- | ------------| ------- | -------- | | `CreateAsk` | [MsgCreateAskRequest](#provenance.exchange.v1.MsgCreateAskRequest) | [MsgCreateAskResponse](#provenance.exchange.v1.MsgCreateAskResponse) | CreateAsk creates an ask order (to sell something you own). | | -| `CreateBid` | [MsgCreateBidRequest](#provenance.exchange.v1.MsgCreateBidRequest) | [MsgCreateBidResponse](#provenance.exchange.v1.MsgCreateBidResponse) | CreateBid creates an bid order (to buy something you want). | | +| `CreateBid` | [MsgCreateBidRequest](#provenance.exchange.v1.MsgCreateBidRequest) | [MsgCreateBidResponse](#provenance.exchange.v1.MsgCreateBidResponse) | CreateBid creates a bid order (to buy something you want). | | | `CancelOrder` | [MsgCancelOrderRequest](#provenance.exchange.v1.MsgCancelOrderRequest) | [MsgCancelOrderResponse](#provenance.exchange.v1.MsgCancelOrderResponse) | CancelOrder cancels an order. | | | `FillBids` | [MsgFillBidsRequest](#provenance.exchange.v1.MsgFillBidsRequest) | [MsgFillBidsResponse](#provenance.exchange.v1.MsgFillBidsResponse) | FillBids uses the assets in your account to fulfill one or more bids (similar to a fill-or-cancel ask). | | | `FillAsks` | [MsgFillAsksRequest](#provenance.exchange.v1.MsgFillAsksRequest) | [MsgFillAsksResponse](#provenance.exchange.v1.MsgFillAsksResponse) | FillAsks uses the funds in your account to fulfill one or more asks (similar to a fill-or-cancel bid). | | diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 41d2898085..07b89cc4bf 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -19,7 +19,7 @@ service Msg { // CreateAsk creates an ask order (to sell something you own). rpc CreateAsk(MsgCreateAskRequest) returns (MsgCreateAskResponse); - // CreateBid creates an bid order (to buy something you want). + // CreateBid creates a bid order (to buy something you want). rpc CreateBid(MsgCreateBidRequest) returns (MsgCreateBidResponse); // CancelOrder cancels an order. diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index f0720de88f..5298f2083e 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -34,7 +34,7 @@ func (k MsgServer) CreateAsk(goCtx context.Context, msg *exchange.MsgCreateAskRe return &exchange.MsgCreateAskResponse{OrderId: orderID}, nil } -// CreateBid creates an bid order (to buy something you want). +// CreateBid creates a bid order (to buy something you want). func (k MsgServer) CreateBid(goCtx context.Context, msg *exchange.MsgCreateBidRequest) (*exchange.MsgCreateBidResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) orderID, err := k.CreateBidOrder(ctx, msg.BidOrder, msg.OrderCreationFee) diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 57e61e0624..350523e358 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -1551,7 +1551,7 @@ const _ = grpc.SupportPackageIsVersion4 type MsgClient interface { // CreateAsk creates an ask order (to sell something you own). CreateAsk(ctx context.Context, in *MsgCreateAskRequest, opts ...grpc.CallOption) (*MsgCreateAskResponse, error) - // CreateBid creates an bid order (to buy something you want). + // CreateBid creates a bid order (to buy something you want). CreateBid(ctx context.Context, in *MsgCreateBidRequest, opts ...grpc.CallOption) (*MsgCreateBidResponse, error) // CancelOrder cancels an order. CancelOrder(ctx context.Context, in *MsgCancelOrderRequest, opts ...grpc.CallOption) (*MsgCancelOrderResponse, error) @@ -1728,7 +1728,7 @@ func (c *msgClient) GovUpdateParams(ctx context.Context, in *MsgGovUpdateParamsR type MsgServer interface { // CreateAsk creates an ask order (to sell something you own). CreateAsk(context.Context, *MsgCreateAskRequest) (*MsgCreateAskResponse, error) - // CreateBid creates an bid order (to buy something you want). + // CreateBid creates a bid order (to buy something you want). CreateBid(context.Context, *MsgCreateBidRequest) (*MsgCreateBidResponse, error) // CancelOrder cancels an order. CancelOrder(context.Context, *MsgCancelOrderRequest) (*MsgCancelOrderResponse, error) From 5203189b188d42d6d38936566d8b9964c4c09b29 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 12:51:11 -0600 Subject: [PATCH 108/309] [1658]: Fill in content of MsgMarketManageReqAttrsRequest. --- docs/proto-docs.md | 9 + proto/provenance/exchange/v1/tx.proto | 13 +- x/exchange/tx.pb.go | 451 +++++++++++++++++++++----- 3 files changed, 387 insertions(+), 86 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 544527d91a..cde7e7abfb 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2365,6 +2365,15 @@ MsgMarketManagePermissionsResponse is a response message for the MarketManagePer MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `administrator` | [string](#string) | | administrator is the account with withdraw permission requesting the withdrawal. | +| `create_ask_to_add` | [string](#string) | repeated | create_ask_to_add are the attributes that should now also be required to create an ask order. | +| `create_ask_to_remove` | [string](#string) | repeated | create_ask_to_add are the attributes that should no longer be required to create an ask order. | +| `create_bid_to_add` | [string](#string) | repeated | create_ask_to_add are the attributes that should now also be required to create a bid order. | +| `create_bid_to_remove` | [string](#string) | repeated | create_ask_to_add are the attributes that should no longer be required to create a bid order. | + + diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 07b89cc4bf..b6294fb0bc 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -186,7 +186,18 @@ message MsgMarketManagePermissionsResponse {} // MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs endpoint. message MsgMarketManageReqAttrsRequest { - // TODO[1658]: MsgMarketManageReqAttrsRequest + option (cosmos.msg.v1.signer) = "administrator"; + + // administrator is the account with withdraw permission requesting the withdrawal. + string administrator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // create_ask_to_add are the attributes that should now also be required to create an ask order. + repeated string create_ask_to_add = 2; + // create_ask_to_add are the attributes that should no longer be required to create an ask order. + repeated string create_ask_to_remove = 3; + // create_ask_to_add are the attributes that should now also be required to create a bid order. + repeated string create_bid_to_add = 4; + // create_ask_to_add are the attributes that should no longer be required to create a bid order. + repeated string create_bid_to_remove = 5; } // MsgMarketManageReqAttrsResponse is a response message for the MarketManageReqAttrs endpoint. diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 350523e358..e642210c41 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -956,6 +956,16 @@ var xxx_messageInfo_MsgMarketManagePermissionsResponse proto.InternalMessageInfo // MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs endpoint. type MsgMarketManageReqAttrsRequest struct { + // administrator is the account with withdraw permission requesting the withdrawal. + Administrator string `protobuf:"bytes,1,opt,name=administrator,proto3" json:"administrator,omitempty"` + // create_ask_to_add are the attributes that should now also be required to create an ask order. + CreateAskToAdd []string `protobuf:"bytes,2,rep,name=create_ask_to_add,json=createAskToAdd,proto3" json:"create_ask_to_add,omitempty"` + // create_ask_to_add are the attributes that should no longer be required to create an ask order. + CreateAskToRemove []string `protobuf:"bytes,3,rep,name=create_ask_to_remove,json=createAskToRemove,proto3" json:"create_ask_to_remove,omitempty"` + // create_ask_to_add are the attributes that should now also be required to create a bid order. + CreateBidToAdd []string `protobuf:"bytes,4,rep,name=create_bid_to_add,json=createBidToAdd,proto3" json:"create_bid_to_add,omitempty"` + // create_ask_to_add are the attributes that should no longer be required to create a bid order. + CreateBidToRemove []string `protobuf:"bytes,5,rep,name=create_bid_to_remove,json=createBidToRemove,proto3" json:"create_bid_to_remove,omitempty"` } func (m *MsgMarketManageReqAttrsRequest) Reset() { *m = MsgMarketManageReqAttrsRequest{} } @@ -991,6 +1001,41 @@ func (m *MsgMarketManageReqAttrsRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgMarketManageReqAttrsRequest proto.InternalMessageInfo +func (m *MsgMarketManageReqAttrsRequest) GetAdministrator() string { + if m != nil { + return m.Administrator + } + return "" +} + +func (m *MsgMarketManageReqAttrsRequest) GetCreateAskToAdd() []string { + if m != nil { + return m.CreateAskToAdd + } + return nil +} + +func (m *MsgMarketManageReqAttrsRequest) GetCreateAskToRemove() []string { + if m != nil { + return m.CreateAskToRemove + } + return nil +} + +func (m *MsgMarketManageReqAttrsRequest) GetCreateBidToAdd() []string { + if m != nil { + return m.CreateBidToAdd + } + return nil +} + +func (m *MsgMarketManageReqAttrsRequest) GetCreateBidToRemove() []string { + if m != nil { + return m.CreateBidToRemove + } + return nil +} + // MsgMarketManageReqAttrsResponse is a response message for the MarketManageReqAttrs endpoint. type MsgMarketManageReqAttrsResponse struct { } @@ -1450,91 +1495,96 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1343 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0x31, 0x6f, 0xdb, 0x46, - 0x14, 0xb6, 0x62, 0xc7, 0xb1, 0x5e, 0x62, 0x27, 0xb8, 0x28, 0xb6, 0xc4, 0xc4, 0x92, 0xa2, 0x74, - 0x08, 0x92, 0x9a, 0x8a, 0x53, 0x34, 0x6d, 0x8d, 0xa2, 0x80, 0xe5, 0x56, 0x41, 0x06, 0xa3, 0x81, - 0x0c, 0xa3, 0x40, 0x3b, 0x08, 0x27, 0xf1, 0x4c, 0x13, 0xa2, 0x78, 0x0a, 0xef, 0xa4, 0xd8, 0x6b, - 0x51, 0xa0, 0x53, 0xd1, 0x8c, 0x5d, 0xbb, 0x76, 0xea, 0xd0, 0xa5, 0xfd, 0x05, 0x41, 0xa7, 0xa0, - 0x53, 0xa7, 0xb6, 0xb0, 0x87, 0xfe, 0x8d, 0x82, 0x77, 0x27, 0x8a, 0x14, 0x49, 0x91, 0x72, 0x33, - 0xd9, 0x24, 0xbf, 0xf7, 0x7d, 0xef, 0x7b, 0xe4, 0xdd, 0x7b, 0x27, 0xa8, 0x0c, 0x5c, 0x3a, 0x22, - 0x0e, 0x76, 0xba, 0xa4, 0x4e, 0x4e, 0xba, 0xc7, 0xd8, 0x31, 0x49, 0x7d, 0xb4, 0x5d, 0xe7, 0x27, - 0xfa, 0xc0, 0xa5, 0x9c, 0xa2, 0xf5, 0x09, 0x40, 0x1f, 0x03, 0xf4, 0xd1, 0xb6, 0x56, 0xee, 0x52, - 0xd6, 0xa7, 0xac, 0xde, 0xc1, 0xcc, 0x0b, 0xe8, 0x10, 0x8e, 0xb7, 0xeb, 0x5d, 0x6a, 0x39, 0x32, - 0x4e, 0xdb, 0x50, 0xcf, 0xfb, 0xcc, 0xf4, 0xf8, 0xfa, 0xcc, 0x54, 0x0f, 0x4a, 0xf2, 0x41, 0x5b, - 0x5c, 0xd5, 0xe5, 0x85, 0x7a, 0x54, 0x30, 0xa9, 0x49, 0xe5, 0x7d, 0xef, 0x3f, 0x75, 0xf7, 0x5e, - 0x42, 0x8a, 0x7d, 0xec, 0xf6, 0x08, 0x4f, 0x01, 0x51, 0xd7, 0x20, 0x2e, 0x4b, 0x01, 0x0d, 0xb0, - 0x8b, 0xfb, 0x0a, 0x54, 0xfb, 0x2d, 0x07, 0x37, 0xf7, 0x99, 0xb9, 0xe7, 0x12, 0xcc, 0xc9, 0x2e, - 0xeb, 0xb5, 0xc8, 0x8b, 0x21, 0x61, 0x1c, 0xed, 0x41, 0x1e, 0xb3, 0x5e, 0x5b, 0x10, 0x16, 0x73, - 0xd5, 0xdc, 0xfd, 0xab, 0x8f, 0xab, 0x7a, 0x7c, 0x71, 0xf4, 0x5d, 0xd6, 0xfb, 0xdc, 0xc3, 0x35, - 0x96, 0x5e, 0xff, 0x55, 0x59, 0x68, 0xad, 0x60, 0x75, 0x8d, 0x9e, 0x02, 0x12, 0x04, 0xed, 0xae, - 0x47, 0x6f, 0x51, 0xa7, 0x7d, 0x44, 0x48, 0xf1, 0x92, 0x60, 0x2b, 0xe9, 0xaa, 0x18, 0x5e, 0x49, - 0x75, 0x55, 0x52, 0x7d, 0x8f, 0x5a, 0x4e, 0xeb, 0x86, 0x08, 0xda, 0x53, 0x31, 0x4d, 0x42, 0x76, - 0x6e, 0x7d, 0xfd, 0xef, 0xcf, 0x0f, 0x6e, 0xf8, 0x09, 0xe9, 0x8c, 0xd8, 0x36, 0x71, 0x6b, 0xdb, - 0x50, 0x08, 0xe7, 0xce, 0x06, 0xd4, 0x61, 0x04, 0x95, 0x60, 0x45, 0xea, 0x5a, 0x86, 0xc8, 0x7d, - 0xa9, 0x75, 0x45, 0x5c, 0x3f, 0x33, 0x6a, 0xbf, 0x06, 0xfd, 0x36, 0x2c, 0x23, 0xe0, 0xb7, 0x63, - 0x19, 0xd9, 0xfc, 0x36, 0x2c, 0x23, 0xe4, 0xb7, 0xa3, 0xae, 0xdf, 0x9e, 0xdf, 0x82, 0xe7, 0xf7, - 0xba, 0x9f, 0x90, 0xde, 0x19, 0x9e, 0x4e, 0xd9, 0x15, 0xa9, 0xa7, 0xdb, 0x75, 0xe0, 0x96, 0x17, - 0xe2, 0x59, 0xb0, 0x45, 0x8e, 0x63, 0xbf, 0x3a, 0x5c, 0xa6, 0x2f, 0x1d, 0xe5, 0x35, 0xdf, 0x28, - 0xfe, 0xf1, 0xcb, 0x56, 0x41, 0x25, 0xb8, 0x6b, 0x18, 0x2e, 0x61, 0xec, 0x80, 0xbb, 0x96, 0x63, - 0xb6, 0x24, 0x2c, 0xa4, 0x71, 0x29, 0xa4, 0xb1, 0x03, 0x5e, 0xb2, 0x12, 0x56, 0x2b, 0xc2, 0xfa, - 0xb4, 0x9e, 0x4c, 0xb2, 0x56, 0x00, 0xb4, 0xcf, 0xcc, 0xa6, 0x65, 0xdb, 0x0d, 0xcb, 0x60, 0x2a, - 0x8d, 0xda, 0x2d, 0xf1, 0x36, 0x26, 0x77, 0x23, 0xe0, 0x5d, 0xd6, 0x8b, 0x01, 0xcb, 0xbb, 0x0a, - 0x2c, 0x35, 0xf7, 0xc5, 0xfa, 0x38, 0x20, 0x9c, 0xdb, 0x64, 0x1c, 0x50, 0x82, 0x8d, 0xc8, 0x13, - 0x15, 0xf4, 0xfd, 0x25, 0x28, 0xfa, 0xcf, 0xbe, 0xb0, 0xf8, 0xb1, 0xe1, 0xe2, 0x97, 0xe3, 0xe2, - 0x7c, 0x02, 0xab, 0xd8, 0xe8, 0x5b, 0x8e, 0xc5, 0xb8, 0x8b, 0x39, 0x4d, 0x2f, 0x52, 0x18, 0x8e, - 0x6e, 0x43, 0x5e, 0x2e, 0xd7, 0x71, 0xb5, 0x56, 0x5b, 0x2b, 0xf2, 0xc6, 0x33, 0x03, 0x6d, 0x02, - 0x70, 0xda, 0xc6, 0x32, 0xbe, 0xb8, 0xe8, 0x31, 0xb7, 0xf2, 0x9c, 0x2a, 0x42, 0xd4, 0x85, 0x65, - 0xdc, 0xa7, 0x43, 0x87, 0x17, 0x97, 0xaa, 0x8b, 0x33, 0xbf, 0x9b, 0xc6, 0x23, 0xef, 0xf3, 0xfb, - 0xe9, 0xef, 0xca, 0x7d, 0xd3, 0xe2, 0xc7, 0xc3, 0x8e, 0xde, 0xa5, 0x7d, 0xb5, 0xc3, 0xa8, 0x3f, - 0x5b, 0xcc, 0xe8, 0xd5, 0xf9, 0xe9, 0x80, 0x30, 0x11, 0xc0, 0x5a, 0x8a, 0x7a, 0x07, 0x79, 0xaf, - 0x2c, 0x9c, 0x74, 0xed, 0x36, 0x94, 0x62, 0x0a, 0xa2, 0xca, 0x55, 0x81, 0x4d, 0xff, 0xe1, 0xe1, - 0xc0, 0xc0, 0x9c, 0x7c, 0x4a, 0x38, 0xb6, 0x6c, 0xff, 0xdd, 0x54, 0xa1, 0x9c, 0x04, 0x48, 0xa4, - 0xf8, 0xcc, 0xc1, 0x1d, 0x9b, 0x18, 0xc9, 0x14, 0x3e, 0x40, 0x51, 0xd4, 0xa0, 0x3a, 0x85, 0x38, - 0x64, 0xc4, 0x0d, 0xbf, 0xf3, 0x7b, 0x70, 0x77, 0x06, 0x46, 0x11, 0x05, 0x41, 0xfb, 0xd8, 0xc1, - 0x26, 0x79, 0x4e, 0xdc, 0xbe, 0xc5, 0x98, 0x45, 0x1d, 0xdf, 0xd2, 0x3b, 0x50, 0x9b, 0x05, 0x52, - 0x54, 0xc1, 0xac, 0x25, 0xaa, 0x45, 0x5e, 0xec, 0x72, 0xee, 0xfa, 0x3c, 0x77, 0xa1, 0x92, 0x88, - 0x50, 0x24, 0x3f, 0xe6, 0x44, 0xf1, 0x9f, 0xd2, 0x91, 0x5c, 0xdd, 0x12, 0x3c, 0xfe, 0x1c, 0x9f, - 0x40, 0x1e, 0x0f, 0xf9, 0x31, 0x75, 0x2d, 0x7e, 0x9a, 0xfa, 0x29, 0x4e, 0xa0, 0xe8, 0x63, 0x58, - 0x96, 0x5f, 0x9d, 0xda, 0x82, 0xca, 0x49, 0x1b, 0x9a, 0x94, 0x53, 0xdb, 0x99, 0x8a, 0xd9, 0x59, - 0xf3, 0xbe, 0x91, 0x09, 0x5b, 0xed, 0x0e, 0x68, 0x71, 0x29, 0x2a, 0x07, 0xbf, 0x83, 0x58, 0x6b, - 0x4f, 0xe9, 0x48, 0x5a, 0x6c, 0x12, 0xc2, 0xfe, 0x6f, 0xfe, 0x33, 0x97, 0xd1, 0x21, 0x6c, 0x60, - 0xc3, 0xf0, 0x36, 0x58, 0xb9, 0xdb, 0x92, 0xb6, 0xd7, 0x1e, 0x8e, 0x6c, 0xcc, 0x8b, 0x8b, 0x69, - 0x0b, 0x47, 0x1a, 0xbd, 0x89, 0x0d, 0xa3, 0x49, 0x88, 0xdf, 0x3e, 0x9a, 0x36, 0xe6, 0xe8, 0x2b, - 0xd0, 0x5c, 0xd2, 0xa7, 0x23, 0x12, 0xcb, 0xbc, 0x94, 0x8d, 0x79, 0x5d, 0x52, 0x44, 0xc8, 0xa3, - 0x39, 0x7b, 0x5b, 0xbc, 0x60, 0xbe, 0x7c, 0x81, 0x9c, 0x1b, 0x96, 0x91, 0x9c, 0xb3, 0xcf, 0xbc, - 0x7c, 0xb1, 0x9c, 0xc7, 0xe4, 0x5d, 0x28, 0x8f, 0x73, 0x96, 0x5d, 0xb7, 0xcd, 0xc4, 0x5a, 0xea, - 0x13, 0x87, 0x4b, 0x81, 0x2b, 0xd9, 0x04, 0x34, 0x99, 0xfa, 0x81, 0x20, 0x39, 0xf0, 0x39, 0x84, - 0x88, 0x05, 0x77, 0x03, 0x0e, 0x12, 0x74, 0x56, 0xb2, 0xe9, 0x6c, 0xfa, 0x46, 0x62, 0xa5, 0x1c, - 0xa8, 0x26, 0xfb, 0x71, 0xbd, 0x0e, 0xcc, 0x8a, 0x79, 0xa1, 0x94, 0xd8, 0xff, 0x9b, 0x84, 0xb4, - 0x3c, 0xa0, 0x12, 0xbc, 0x13, 0x6f, 0x4c, 0x40, 0x18, 0xe2, 0x70, 0x6f, 0xa6, 0x35, 0x25, 0x09, - 0x73, 0x49, 0x56, 0x12, 0x3d, 0x2a, 0x55, 0x0c, 0x9b, 0x63, 0x97, 0x62, 0x76, 0x88, 0x14, 0xf3, - 0x6a, 0xb6, 0x62, 0x96, 0xa4, 0xb7, 0x86, 0xc7, 0x31, 0x55, 0x48, 0x13, 0xaa, 0x01, 0x63, 0xf1, - 0x2a, 0xd7, 0xb2, 0xa9, 0xdc, 0xf1, 0xed, 0xc4, 0x09, 0xd9, 0x50, 0x49, 0xf4, 0xa2, 0xaa, 0xb7, - 0x3a, 0x57, 0xf5, 0x6e, 0xc7, 0x9a, 0x52, 0x95, 0x73, 0xa1, 0x36, 0xcb, 0x96, 0x12, 0x5c, 0x9b, - 0x4b, 0xb0, 0x9c, 0xe4, 0x4f, 0x6a, 0x46, 0xb6, 0x5a, 0x4d, 0xcc, 0x26, 0x53, 0x7b, 0x69, 0xa4, - 0x55, 0xc8, 0xee, 0xf6, 0x5c, 0x4c, 0xf3, 0x6f, 0xa1, 0x55, 0xc8, 0x63, 0x41, 0x5a, 0xab, 0x90, - 0x72, 0xe3, 0x56, 0x21, 0x63, 0x92, 0x5b, 0x45, 0x38, 0x45, 0xe9, 0xe0, 0xf1, 0x0f, 0x6b, 0xb0, - 0xb8, 0xcf, 0x4c, 0x74, 0x04, 0x79, 0x7f, 0x7b, 0x44, 0x0f, 0x13, 0x7b, 0x53, 0xf4, 0x70, 0xa2, - 0xbd, 0x9b, 0x0d, 0xac, 0xc6, 0x63, 0x5f, 0xa7, 0x61, 0x19, 0x19, 0x74, 0x26, 0x87, 0x82, 0x0c, - 0x3a, 0xc1, 0x31, 0xdc, 0x86, 0xab, 0x81, 0xc1, 0x17, 0x6d, 0xcd, 0x0a, 0x8e, 0x0c, 0xe4, 0x9a, - 0x9e, 0x15, 0xae, 0xd4, 0xba, 0xb0, 0x32, 0x1e, 0x9b, 0xd1, 0x83, 0x19, 0xb1, 0x53, 0x13, 0xb7, - 0xf6, 0x30, 0x13, 0x36, 0x2c, 0xe2, 0x8d, 0xdb, 0xa9, 0x22, 0x81, 0x49, 0x3d, 0x55, 0x24, 0x38, - 0xbf, 0x23, 0x0a, 0xd7, 0x82, 0x23, 0x3a, 0x9a, 0x55, 0x89, 0x98, 0x29, 0x5f, 0xab, 0x67, 0xc6, - 0x2b, 0xc1, 0x21, 0xac, 0x85, 0xc7, 0x5c, 0xf4, 0x28, 0x95, 0x62, 0xea, 0x88, 0xa0, 0x6d, 0xcf, - 0x11, 0xa1, 0x64, 0xbf, 0xf1, 0x8e, 0x9e, 0xd1, 0x01, 0x19, 0xbd, 0x9f, 0x4a, 0x15, 0x37, 0x71, - 0x6b, 0x4f, 0xe6, 0x0d, 0x4b, 0x48, 0x43, 0x0d, 0xd9, 0x99, 0xd3, 0x08, 0x4f, 0xed, 0x99, 0xd3, - 0x98, 0x9a, 0xe5, 0xd1, 0x77, 0x39, 0x58, 0x8f, 0x9f, 0xd2, 0xd1, 0x87, 0x19, 0x29, 0x23, 0xc3, - 0xbf, 0xf6, 0xd1, 0x05, 0x22, 0x55, 0x3e, 0xaf, 0x72, 0xb0, 0x91, 0x30, 0xeb, 0xa3, 0x74, 0xda, - 0xa4, 0x43, 0x84, 0xb6, 0x73, 0x91, 0x50, 0x95, 0xd2, 0xb7, 0x39, 0x28, 0xc4, 0x1d, 0x1b, 0xd0, - 0x93, 0x8c, 0xa4, 0x53, 0x27, 0x11, 0xed, 0x83, 0xb9, 0xe3, 0x54, 0x26, 0x27, 0x70, 0x7d, 0x6a, - 0xf0, 0x47, 0xb3, 0x16, 0x40, 0xfc, 0x39, 0x46, 0x7b, 0x3c, 0x4f, 0x88, 0x52, 0x76, 0x61, 0x35, - 0xd4, 0x07, 0x51, 0x7d, 0x36, 0x49, 0xe4, 0xf4, 0xa1, 0x3d, 0xca, 0x1e, 0x10, 0x72, 0x1b, 0xec, - 0x5d, 0x69, 0x6e, 0x63, 0x5a, 0x71, 0x9a, 0xdb, 0xb8, 0xd6, 0xd8, 0x20, 0xaf, 0xcf, 0xca, 0xb9, - 0x37, 0x67, 0xe5, 0xdc, 0x3f, 0x67, 0xe5, 0xdc, 0xab, 0xf3, 0xf2, 0xc2, 0x9b, 0xf3, 0xf2, 0xc2, - 0x9f, 0xe7, 0xe5, 0x05, 0x28, 0x59, 0x34, 0x81, 0xef, 0x79, 0xee, 0x4b, 0x3d, 0xf0, 0x03, 0xc0, - 0x04, 0xb4, 0x65, 0xd1, 0xc0, 0x55, 0xfd, 0xc4, 0xff, 0x11, 0xb0, 0xb3, 0x2c, 0x7e, 0xfb, 0x7b, - 0xef, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x15, 0x72, 0x8f, 0x0f, 0x15, 0x00, 0x00, + // 1413 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xcf, 0x6f, 0x1b, 0xc5, + 0x17, 0x8f, 0x93, 0x34, 0x8d, 0x5f, 0x9b, 0xb4, 0x9d, 0xba, 0x89, 0xbd, 0x6d, 0x6c, 0xd7, 0xfd, + 0x1e, 0xfa, 0x6d, 0xc9, 0xba, 0x29, 0xa2, 0x40, 0x84, 0x90, 0xe2, 0x40, 0xaa, 0x1e, 0x22, 0x2a, + 0x87, 0x0a, 0x09, 0x0e, 0xd6, 0xd8, 0x3b, 0x75, 0x46, 0xb1, 0x77, 0xd2, 0x9d, 0x49, 0x9a, 0x5e, + 0x11, 0x12, 0x27, 0x44, 0x8f, 0x5c, 0xb9, 0x22, 0x0e, 0x1c, 0xb8, 0xc0, 0x5f, 0x50, 0x71, 0xaa, + 0x38, 0x71, 0x02, 0xd4, 0x1e, 0xf8, 0x37, 0xd0, 0xfc, 0xf0, 0x7a, 0xd7, 0xfb, 0xd3, 0xa1, 0xa7, + 0x64, 0x77, 0x3e, 0xef, 0xf3, 0x79, 0x9f, 0xb7, 0xb3, 0x6f, 0xf6, 0x19, 0x6a, 0x87, 0x1e, 0x3b, + 0x26, 0x2e, 0x76, 0x7b, 0xa4, 0x49, 0x4e, 0x7a, 0xfb, 0xd8, 0xed, 0x93, 0xe6, 0xf1, 0x46, 0x53, + 0x9c, 0xd8, 0x87, 0x1e, 0x13, 0x0c, 0xad, 0x8c, 0x01, 0xf6, 0x08, 0x60, 0x1f, 0x6f, 0x58, 0xd5, + 0x1e, 0xe3, 0x43, 0xc6, 0x9b, 0x5d, 0xcc, 0x65, 0x40, 0x97, 0x08, 0xbc, 0xd1, 0xec, 0x31, 0xea, + 0xea, 0x38, 0x6b, 0xd5, 0xac, 0x0f, 0x79, 0x5f, 0xf2, 0x0d, 0x79, 0xdf, 0x2c, 0x54, 0xf4, 0x42, + 0x47, 0x5d, 0x35, 0xf5, 0x85, 0x59, 0x2a, 0xf5, 0x59, 0x9f, 0xe9, 0xfb, 0xf2, 0x3f, 0x73, 0xf7, + 0x46, 0x42, 0x8a, 0x43, 0xec, 0x1d, 0x10, 0x91, 0x01, 0x62, 0x9e, 0x43, 0x3c, 0x9e, 0x01, 0x3a, + 0xc4, 0x1e, 0x1e, 0x1a, 0x50, 0xe3, 0xd7, 0x02, 0x5c, 0xde, 0xe5, 0xfd, 0x6d, 0x8f, 0x60, 0x41, + 0xb6, 0xf8, 0x41, 0x9b, 0x3c, 0x39, 0x22, 0x5c, 0xa0, 0x6d, 0x28, 0x62, 0x7e, 0xd0, 0x51, 0x84, + 0xe5, 0x42, 0xbd, 0x70, 0xf3, 0xdc, 0xdd, 0xba, 0x1d, 0x5f, 0x1c, 0x7b, 0x8b, 0x1f, 0x7c, 0x22, + 0x71, 0xad, 0xf9, 0x17, 0x7f, 0xd6, 0x66, 0xda, 0x8b, 0xd8, 0x5c, 0xa3, 0xfb, 0x80, 0x14, 0x41, + 0xa7, 0x27, 0xe9, 0x29, 0x73, 0x3b, 0x8f, 0x09, 0x29, 0xcf, 0x2a, 0xb6, 0x8a, 0x6d, 0x8a, 0x21, + 0x4b, 0x6a, 0x9b, 0x92, 0xda, 0xdb, 0x8c, 0xba, 0xed, 0x8b, 0x2a, 0x68, 0xdb, 0xc4, 0xec, 0x10, + 0xb2, 0x79, 0xe5, 0xcb, 0x7f, 0x7e, 0xba, 0x75, 0xd1, 0x4f, 0xc8, 0xe6, 0x64, 0x30, 0x20, 0x5e, + 0x63, 0x03, 0x4a, 0xe1, 0xdc, 0xf9, 0x21, 0x73, 0x39, 0x41, 0x15, 0x58, 0xd4, 0xba, 0xd4, 0x51, + 0xb9, 0xcf, 0xb7, 0xcf, 0xaa, 0xeb, 0x07, 0x4e, 0xe3, 0x97, 0xa0, 0xdf, 0x16, 0x75, 0x02, 0x7e, + 0xbb, 0xd4, 0xc9, 0xe7, 0xb7, 0x45, 0x9d, 0x90, 0xdf, 0xae, 0xb9, 0x7e, 0x73, 0x7e, 0x4b, 0xd2, + 0xef, 0x05, 0x3f, 0x21, 0xbb, 0x7b, 0xf4, 0x6c, 0xc2, 0xae, 0x4a, 0x3d, 0xdb, 0xae, 0x0b, 0x57, + 0x64, 0x88, 0xb4, 0x30, 0x50, 0x39, 0x8e, 0xfc, 0xda, 0x70, 0x86, 0x3d, 0x75, 0x8d, 0xd7, 0x62, + 0xab, 0xfc, 0xfb, 0xcf, 0xeb, 0x25, 0x93, 0xe0, 0x96, 0xe3, 0x78, 0x84, 0xf3, 0x3d, 0xe1, 0x51, + 0xb7, 0xdf, 0xd6, 0xb0, 0x90, 0xc6, 0x6c, 0x48, 0x63, 0x13, 0x64, 0xb2, 0x1a, 0xd6, 0x28, 0xc3, + 0xca, 0xa4, 0x9e, 0x4e, 0xb2, 0x51, 0x02, 0xb4, 0xcb, 0xfb, 0x3b, 0x74, 0x30, 0x68, 0x51, 0x87, + 0x9b, 0x34, 0x1a, 0x57, 0xd4, 0xd3, 0x18, 0xdf, 0x8d, 0x80, 0xb7, 0xf8, 0x41, 0x0c, 0x58, 0xdf, + 0x35, 0x60, 0xad, 0xb9, 0xab, 0xde, 0x8f, 0x3d, 0x22, 0xc4, 0x80, 0x8c, 0x02, 0x2a, 0xb0, 0x1a, + 0x59, 0x31, 0x41, 0xdf, 0xce, 0x42, 0xd9, 0x5f, 0xfb, 0x8c, 0x8a, 0x7d, 0xc7, 0xc3, 0x4f, 0x47, + 0xc5, 0xf9, 0x10, 0x96, 0xb0, 0x33, 0xa4, 0x2e, 0xe5, 0xc2, 0xc3, 0x82, 0x65, 0x17, 0x29, 0x0c, + 0x47, 0x57, 0xa1, 0xa8, 0x5f, 0xd7, 0x51, 0xb5, 0x96, 0xda, 0x8b, 0xfa, 0xc6, 0x03, 0x07, 0xad, + 0x01, 0x08, 0xd6, 0xc1, 0x3a, 0xbe, 0x3c, 0x27, 0x99, 0xdb, 0x45, 0xc1, 0x0c, 0x21, 0xea, 0xc1, + 0x02, 0x1e, 0xb2, 0x23, 0x57, 0x94, 0xe7, 0xeb, 0x73, 0xa9, 0xfb, 0xa6, 0x75, 0x47, 0x6e, 0xbf, + 0x1f, 0xfe, 0xaa, 0xdd, 0xec, 0x53, 0xb1, 0x7f, 0xd4, 0xb5, 0x7b, 0x6c, 0x68, 0x3a, 0x8c, 0xf9, + 0xb3, 0xce, 0x9d, 0x83, 0xa6, 0x78, 0x76, 0x48, 0xb8, 0x0a, 0xe0, 0x6d, 0x43, 0xbd, 0x89, 0xe4, + 0x23, 0x0b, 0x27, 0xdd, 0xb8, 0x0a, 0x95, 0x98, 0x82, 0x98, 0x72, 0xd5, 0x60, 0xcd, 0x5f, 0x7c, + 0x74, 0xe8, 0x60, 0x41, 0x3e, 0x22, 0x02, 0xd3, 0x81, 0xff, 0x6c, 0xea, 0x50, 0x4d, 0x02, 0x24, + 0x52, 0x7c, 0xec, 0xe2, 0xee, 0x80, 0x38, 0xc9, 0x14, 0x3e, 0xc0, 0x50, 0x34, 0xa0, 0x3e, 0x81, + 0x78, 0xc4, 0x89, 0x17, 0x7e, 0xe6, 0x37, 0xe0, 0x7a, 0x0a, 0xc6, 0x10, 0x05, 0x41, 0xbb, 0xd8, + 0xc5, 0x7d, 0xf2, 0x90, 0x78, 0x43, 0xca, 0x39, 0x65, 0xae, 0x6f, 0xe9, 0x7f, 0xd0, 0x48, 0x03, + 0x19, 0xaa, 0x1f, 0x67, 0x03, 0x69, 0x6b, 0x58, 0x9b, 0x3c, 0xd9, 0x12, 0xc2, 0xe3, 0x6f, 0x6a, + 0x3b, 0xfd, 0x1f, 0x2e, 0xa9, 0x86, 0x42, 0x3a, 0xb2, 0x03, 0xea, 0xcd, 0x53, 0x9e, 0xad, 0xcf, + 0xdd, 0x2c, 0xb6, 0x97, 0x7b, 0xa3, 0xe6, 0xf7, 0xa9, 0xdc, 0x41, 0xa8, 0x09, 0xa5, 0x30, 0xd4, + 0x23, 0x43, 0x76, 0x4c, 0xca, 0x73, 0x0a, 0x7d, 0x29, 0x80, 0x6e, 0xab, 0x85, 0x00, 0xb7, 0xec, + 0x36, 0x86, 0x7b, 0x3e, 0xc8, 0xdd, 0xa2, 0xce, 0x24, 0xb7, 0x81, 0x1a, 0xee, 0x33, 0x41, 0x6e, + 0x85, 0xd6, 0xdc, 0xb1, 0xbb, 0xec, 0x3a, 0xd4, 0x12, 0xab, 0x65, 0x2a, 0xfa, 0x7d, 0x41, 0xed, + 0xc4, 0xfb, 0xec, 0x58, 0xb7, 0x3a, 0x0d, 0x1e, 0x15, 0xf3, 0x1e, 0x14, 0xf1, 0x91, 0xd8, 0x67, + 0x1e, 0x15, 0xcf, 0x32, 0x0b, 0x39, 0x86, 0xa2, 0x0f, 0x60, 0x41, 0xbf, 0x82, 0xa6, 0x1f, 0x57, + 0x93, 0xba, 0xbb, 0x96, 0x33, 0xbd, 0xdd, 0xc4, 0x6c, 0x2e, 0x4b, 0x2b, 0x63, 0xb6, 0xc6, 0x35, + 0xb0, 0xe2, 0x52, 0x34, 0x0e, 0x7e, 0x03, 0xd5, 0x78, 0xee, 0xb3, 0x63, 0x6d, 0x71, 0x87, 0x10, + 0xfe, 0x5f, 0xf3, 0x4f, 0xed, 0x29, 0x8f, 0x60, 0x15, 0x3b, 0x8e, 0x3c, 0x6d, 0x3a, 0x81, 0xc7, + 0xff, 0x78, 0x80, 0x85, 0x7a, 0xf2, 0xa9, 0x5d, 0x44, 0x1b, 0xbd, 0x8c, 0x1d, 0x67, 0x87, 0x10, + 0xff, 0x2c, 0xdd, 0x19, 0x60, 0x81, 0xbe, 0x00, 0x4b, 0x3f, 0xe3, 0x58, 0xe6, 0xf9, 0x7c, 0xcc, + 0x2b, 0x9a, 0x22, 0x42, 0x1e, 0xcd, 0x59, 0x6e, 0x2b, 0xc5, 0x7c, 0xe6, 0x14, 0x39, 0xb7, 0xa8, + 0x93, 0x9c, 0xb3, 0xcf, 0xbc, 0x70, 0xba, 0x9c, 0x47, 0xe4, 0x3d, 0xa8, 0x8e, 0x72, 0xd6, 0x9f, + 0x20, 0x1d, 0xae, 0x1a, 0xcb, 0x90, 0xb8, 0x42, 0x0b, 0x9c, 0xcd, 0x27, 0x60, 0xe9, 0xd4, 0xf7, + 0x14, 0xc9, 0x9e, 0xcf, 0xa1, 0x44, 0x28, 0x5c, 0x0f, 0x38, 0x48, 0xd0, 0x59, 0xcc, 0xa7, 0xb3, + 0xe6, 0x1b, 0x89, 0x95, 0x72, 0xa1, 0x9e, 0xec, 0xc7, 0x93, 0x9f, 0x23, 0xbc, 0x5c, 0x54, 0x4a, + 0x89, 0x1f, 0x43, 0x3b, 0x84, 0xb4, 0x25, 0xd0, 0x08, 0x5e, 0x8b, 0x37, 0xa6, 0x20, 0x1c, 0x09, + 0xb8, 0x91, 0x6a, 0xcd, 0x48, 0xc2, 0x54, 0x92, 0xb5, 0x44, 0x8f, 0x46, 0x15, 0xc3, 0xda, 0xc8, + 0xa5, 0xfa, 0x90, 0x8a, 0x14, 0xf3, 0x5c, 0xbe, 0x62, 0x56, 0xb4, 0xb7, 0x96, 0xe4, 0x98, 0x28, + 0x64, 0x1f, 0xea, 0x01, 0x63, 0xf1, 0x2a, 0xe7, 0xf3, 0xa9, 0x5c, 0xf3, 0xed, 0xc4, 0x09, 0x0d, + 0xa0, 0x96, 0xe8, 0xc5, 0x54, 0x6f, 0x69, 0xaa, 0xea, 0x5d, 0x8d, 0x35, 0x65, 0x2a, 0xe7, 0x41, + 0x23, 0xcd, 0x96, 0x11, 0x5c, 0x9e, 0x4a, 0xb0, 0x9a, 0xe4, 0x4f, 0x6b, 0x46, 0x5a, 0xad, 0xa5, + 0x3e, 0xd4, 0x26, 0x7a, 0x69, 0xe4, 0xa8, 0xd0, 0x47, 0xfd, 0x43, 0x35, 0xda, 0xbc, 0x81, 0xa3, + 0x42, 0xcf, 0x48, 0x59, 0x47, 0x85, 0x96, 0x1b, 0x1d, 0x15, 0x3a, 0x26, 0xf9, 0xa8, 0x08, 0xa7, + 0xa8, 0x1d, 0xdc, 0xfd, 0x6e, 0x19, 0xe6, 0x76, 0x79, 0x1f, 0x3d, 0x86, 0xa2, 0xdf, 0x1e, 0xd1, + 0xed, 0xc4, 0xb3, 0x29, 0x3a, 0xa9, 0x59, 0x6f, 0xe5, 0x03, 0x9b, 0x59, 0xc1, 0xd7, 0x69, 0x51, + 0x27, 0x87, 0xce, 0x78, 0x42, 0xca, 0xa1, 0x13, 0x9c, 0x49, 0x06, 0x70, 0x2e, 0x30, 0x05, 0xa0, + 0xf5, 0xb4, 0xe0, 0xc8, 0x74, 0x62, 0xd9, 0x79, 0xe1, 0x46, 0xad, 0x07, 0x8b, 0xa3, 0x19, 0x02, + 0xdd, 0x4a, 0x89, 0x9d, 0x18, 0x3f, 0xac, 0xdb, 0xb9, 0xb0, 0x61, 0x11, 0x39, 0x7b, 0x64, 0x8a, + 0x04, 0xc6, 0x96, 0x4c, 0x91, 0xe0, 0x30, 0x83, 0x18, 0x9c, 0x0f, 0xce, 0x2b, 0x28, 0xad, 0x12, + 0x31, 0x23, 0x8f, 0xd5, 0xcc, 0x8d, 0x37, 0x82, 0x47, 0xb0, 0x1c, 0xfe, 0xe6, 0x47, 0x77, 0x32, + 0x29, 0x26, 0xe6, 0x25, 0x6b, 0x63, 0x8a, 0x08, 0x23, 0xfb, 0x95, 0x9c, 0xc3, 0xa3, 0xd3, 0x02, + 0x7a, 0x27, 0x93, 0x2a, 0x6e, 0xfc, 0xb0, 0xee, 0x4d, 0x1b, 0x96, 0x90, 0x86, 0x99, 0x38, 0x72, + 0xa7, 0x11, 0x1e, 0x61, 0x72, 0xa7, 0x31, 0x31, 0xd8, 0xa0, 0x6f, 0x0a, 0xb0, 0x12, 0x3f, 0xb2, + 0xa0, 0xf7, 0x72, 0x52, 0x46, 0x26, 0x21, 0xeb, 0xfd, 0x53, 0x44, 0x9a, 0x7c, 0x9e, 0x17, 0x60, + 0x35, 0x61, 0xf0, 0x41, 0xd9, 0xb4, 0x49, 0x13, 0x95, 0xb5, 0x79, 0x9a, 0x50, 0x93, 0xd2, 0xd7, + 0x05, 0x28, 0xc5, 0x8d, 0x0d, 0xe8, 0x5e, 0x4e, 0xd2, 0x89, 0xa9, 0xcc, 0x7a, 0x77, 0xea, 0x38, + 0x93, 0xc9, 0x09, 0x5c, 0x98, 0xf8, 0xf0, 0x47, 0x69, 0x2f, 0x40, 0xfc, 0x1c, 0x63, 0xdd, 0x9d, + 0x26, 0xc4, 0x28, 0x7b, 0xb0, 0x14, 0x3a, 0x07, 0x51, 0x33, 0x9d, 0x24, 0x32, 0x7d, 0x58, 0x77, + 0xf2, 0x07, 0x84, 0xdc, 0x06, 0xcf, 0xae, 0x2c, 0xb7, 0x31, 0x47, 0x71, 0x96, 0xdb, 0xb8, 0xa3, + 0xb1, 0x45, 0x5e, 0xbc, 0xaa, 0x16, 0x5e, 0xbe, 0xaa, 0x16, 0xfe, 0x7e, 0x55, 0x2d, 0x3c, 0x7f, + 0x5d, 0x9d, 0x79, 0xf9, 0xba, 0x3a, 0xf3, 0xc7, 0xeb, 0xea, 0x0c, 0x54, 0x28, 0x4b, 0xe0, 0x7b, + 0x58, 0xf8, 0xdc, 0x0e, 0xfc, 0x1a, 0x32, 0x06, 0xad, 0x53, 0x16, 0xb8, 0x6a, 0x9e, 0xf8, 0xbf, + 0x88, 0x76, 0x17, 0xd4, 0x0f, 0xa1, 0x6f, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x2e, 0x02, 0x18, + 0x79, 0x1c, 0x16, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2776,6 +2826,49 @@ func (m *MsgMarketManageReqAttrsRequest) MarshalToSizedBuffer(dAtA []byte) (int, _ = i var l int _ = l + if len(m.CreateBidToRemove) > 0 { + for iNdEx := len(m.CreateBidToRemove) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.CreateBidToRemove[iNdEx]) + copy(dAtA[i:], m.CreateBidToRemove[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.CreateBidToRemove[iNdEx]))) + i-- + dAtA[i] = 0x2a + } + } + if len(m.CreateBidToAdd) > 0 { + for iNdEx := len(m.CreateBidToAdd) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.CreateBidToAdd[iNdEx]) + copy(dAtA[i:], m.CreateBidToAdd[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.CreateBidToAdd[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.CreateAskToRemove) > 0 { + for iNdEx := len(m.CreateAskToRemove) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.CreateAskToRemove[iNdEx]) + copy(dAtA[i:], m.CreateAskToRemove[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.CreateAskToRemove[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.CreateAskToAdd) > 0 { + for iNdEx := len(m.CreateAskToAdd) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.CreateAskToAdd[iNdEx]) + copy(dAtA[i:], m.CreateAskToAdd[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.CreateAskToAdd[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Administrator) > 0 { + i -= len(m.Administrator) + copy(dAtA[i:], m.Administrator) + i = encodeVarintTx(dAtA, i, uint64(len(m.Administrator))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -3411,6 +3504,34 @@ func (m *MsgMarketManageReqAttrsRequest) Size() (n int) { } var l int _ = l + l = len(m.Administrator) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.CreateAskToAdd) > 0 { + for _, s := range m.CreateAskToAdd { + l = len(s) + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.CreateAskToRemove) > 0 { + for _, s := range m.CreateAskToRemove { + l = len(s) + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.CreateBidToAdd) > 0 { + for _, s := range m.CreateBidToAdd { + l = len(s) + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.CreateBidToRemove) > 0 { + for _, s := range m.CreateBidToRemove { + l = len(s) + n += 1 + l + sovTx(uint64(l)) + } + } return n } @@ -5047,6 +5168,166 @@ func (m *MsgMarketManageReqAttrsRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgMarketManageReqAttrsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Administrator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Administrator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreateAskToAdd", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CreateAskToAdd = append(m.CreateAskToAdd, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreateAskToRemove", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CreateAskToRemove = append(m.CreateAskToRemove, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreateBidToAdd", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CreateBidToAdd = append(m.CreateBidToAdd, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreateBidToRemove", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CreateBidToRemove = append(m.CreateBidToRemove, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) From a7b1d0aeceafb8fb8bcff28490e19b498cd703ae Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 13:20:42 -0600 Subject: [PATCH 109/309] [1658]: Refactor ValidateReqAttrs to only take in a single list. --- x/exchange/market.go | 48 +++++------ x/exchange/market_test.go | 172 +++++++++++--------------------------- 2 files changed, 70 insertions(+), 150 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index a07188c588..2a728a1f6d 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -34,9 +34,8 @@ var ( // The MarketId is allowed to be zero in here. // Some uses might require it to have a value, but that check is left up to the caller. func (m Market) Validate() error { - // Nothing to check on the MarketId. It's allowed to be zero to indicate to use the next one. - - errs := []error{ + return errors.Join( + // Nothing to check on the MarketId. It's allowed to be zero to indicate to use the next one. m.MarketDetails.Validate(), ValidateFeeOptions("create-ask flat fee", m.FeeCreateAskFlat), ValidateFeeOptions("create-bid flat fee", m.FeeCreateBidFlat), @@ -44,18 +43,10 @@ func (m Market) Validate() error { ValidateFeeOptions("buyer settlement flat fee", m.FeeBuyerSettlementFlat), ValidateFeeRatios(m.FeeSellerSettlementRatios, m.FeeBuyerSettlementRatios), ValidateAccessGrants(m.AccessGrants), - } - - // Nothing to check for with the AcceptingOrders and AllowUserSettlement booleans. - - if err := ValidateReqAttrs(m.ReqAttrCreateAsk); err != nil { - errs = append(errs, fmt.Errorf("invalid create-ask required attributes: %w", err)) - } - if err := ValidateReqAttrs(m.ReqAttrCreateBid); err != nil { - errs = append(errs, fmt.Errorf("invalid create-bid required attributes: %w", err)) - } - - return errors.Join(errs...) + // Nothing to check for with the AcceptingOrders and AllowUserSettlement booleans. + ValidateReqAttrs("create-ask", m.ReqAttrCreateAsk), + ValidateReqAttrs("create-bid", m.ReqAttrCreateBid), + ) } // ValidateFeeOptions returns an error if any of the provide coin values is not a valid fee option. @@ -462,25 +453,24 @@ func ParsePermissions(permissions ...string) ([]Permission, error) { } // ValidateReqAttrs makes sure that each provided attribute is valid and that no duplicate entries are provided. -func ValidateReqAttrs(attrLists ...[]string) error { +func ValidateReqAttrs(field string, attrs []string) error { var errs []error seen := make(map[string]bool) bad := make(map[string]bool) - for _, attrs := range attrLists { - for _, attr := range attrs { - normalized := nametypes.NormalizeName(attr) - if seen[normalized] { - if !bad[normalized] { - errs = append(errs, fmt.Errorf("duplicate required attribute entry: %q", attr)) - bad[normalized] = true - } - continue - } - seen[normalized] = true - if !IsValidReqAttr(normalized) { - errs = append(errs, fmt.Errorf("invalid required attribute %q", attr)) + for _, attr := range attrs { + normalized := nametypes.NormalizeName(attr) + if seen[normalized] { + if !bad[normalized] { + errs = append(errs, fmt.Errorf("duplicate %s required attribute: %q", + field, attr)) bad[normalized] = true } + continue + } + seen[normalized] = true + if !IsValidReqAttr(normalized) { + errs = append(errs, fmt.Errorf("invalid %s required attribute %q", field, attr)) + bad[normalized] = true } } return errors.Join(errs...) diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 4abc3abf44..12fa2519bf 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -2180,155 +2180,85 @@ func TestValidateReqAttrs(t *testing.T) { } tests := []struct { - name string - attrLists [][]string - exp string + name string + field string + attrs []string + exp string }{ { - name: "nil lists", - attrLists: nil, - exp: "", - }, - { - name: "no lists", - attrLists: [][]string{}, - exp: "", + name: "nil list", + field: "FEYULD", + attrs: nil, + exp: "", }, { - name: "two empty lists", - attrLists: [][]string{{}, {}}, - exp: "", + name: "empty list", + field: "FEYULD", + attrs: []string{}, + exp: "", }, { - name: "one list: three valid entries: normalized", - attrLists: [][]string{ - {"*.wildcard", "penny.nickel.dime", "*.example.pb"}, - }, - exp: "", - }, - { - name: "one list: three valid entries: not normalized", - attrLists: [][]string{ - {" * . wildcard ", " penny . nickel . dime ", " * . example . pb "}, - }, - exp: "", + name: "three valid entries: normalized", + field: "FEYULD", + attrs: []string{"*.wildcard", "penny.nickel.dime", "*.example.pb"}, + exp: "", }, { - name: "one list: three entries: first invalid", - attrLists: [][]string{ - {"x.*.wildcard", "penny.nickel.dime", "*.example.pb"}, - }, - exp: `invalid required attribute "x.*.wildcard"`, + name: "three valid entries: not normalized", + field: "FEYULD", + attrs: []string{" * . wildcard ", " penny . nickel . dime ", " * . example . pb "}, + exp: "", }, { - name: "one list: three entries: second invalid", - attrLists: [][]string{ - {"*.wildcard", "penny.nic kel.dime", "*.example.pb"}, - }, - exp: `invalid required attribute "penny.nic kel.dime"`, + name: "three entries: first invalid", + field: "FEYULD", + attrs: []string{"x.*.wildcard", "penny.nickel.dime", "*.example.pb"}, + exp: `invalid FEYULD required attribute "x.*.wildcard"`, }, { - name: "one list: three entries: third invalid", - attrLists: [][]string{ - {"*.wildcard", "penny.nickel.dime", "*.ex-am-ple.pb"}, - }, - exp: `invalid required attribute "*.ex-am-ple.pb"`, + name: "three entries: second invalid", + field: "fee-yield", + attrs: []string{"*.wildcard", "penny.nic kel.dime", "*.example.pb"}, + exp: `invalid fee-yield required attribute "penny.nic kel.dime"`, }, { - name: "one list: duplicate entries", - attrLists: [][]string{ - {"*.multi", "*.multi", "*.multi"}, - }, - exp: `duplicate required attribute entry: "*.multi"`, + name: "three entries: third invalid", + field: "fee-yelled", + attrs: []string{"*.wildcard", "penny.nickel.dime", "*.ex-am-ple.pb"}, + exp: `invalid fee-yelled required attribute "*.ex-am-ple.pb"`, }, { - name: "one list: duplicate bad entries", - attrLists: [][]string{ - {"bad.*.example", "bad. * .example"}, - }, - exp: `invalid required attribute "bad.*.example"`, + name: "duplicate entries", + field: "just some field name thingy", + attrs: []string{"*.multi", "*.multi", "*.multi"}, + exp: `duplicate just some field name thingy required attribute: "*.multi"`, }, { - name: "one list: multiple problems", - attrLists: [][]string{ - { - "one.multi", "x.*.wildcard", "x.*.wildcard", "one.multi", "two.multi", - "penny.nic kel.dime", "one.multi", "two.multi", "*.ex-am-ple.pb", "two.multi", - }, - }, - exp: joinErrs( - `invalid required attribute "x.*.wildcard"`, - `duplicate required attribute entry: "one.multi"`, - `invalid required attribute "penny.nic kel.dime"`, - `duplicate required attribute entry: "two.multi"`, - `invalid required attribute "*.ex-am-ple.pb"`, - ), - }, - { - name: "two lists: second has invalid first", - attrLists: [][]string{ - {"*.ok", "also.okay.by.me", "this.makes.me.happy"}, - {"x.*.wildcard", "penny.nickel.dime", "*.example.pb"}, - }, - exp: `invalid required attribute "x.*.wildcard"`, - }, - { - name: "two lists: second has invalid middle", - attrLists: [][]string{ - {"*.ok", "also.okay.by.me", "this.makes.me.happy"}, - {"*.wildcard", "penny.nic kel.dime", "*.example.pb"}, - }, - exp: `invalid required attribute "penny.nic kel.dime"`, - }, - { - name: "two lists: second has invalid last", - attrLists: [][]string{ - {"*.ok", "also.okay.by.me", "this.makes.me.happy"}, - {"*.wildcard", "penny.nickel.dime", "*.ex-am-ple.pb"}, - }, - exp: `invalid required attribute "*.ex-am-ple.pb"`, - }, - { - name: "two lists: same entry in both but one is not normalized", - attrLists: [][]string{ - {"this.attr.is.twice"}, - {" This . Attr . Is . TWice"}, - }, - exp: `duplicate required attribute entry: " This . Attr . Is . TWice"`, - }, - { - name: "two lists: multiple problems", - attrLists: [][]string{ - {"one.multi", "x.*.wildcard", "x.*.wildcard", "one.multi", "two.multi"}, - {"penny.nic kel.dime", "one.multi", "two.multi", "*.ex-am-ple.pb", "two.multi"}, - }, - exp: joinErrs( - `invalid required attribute "x.*.wildcard"`, - `duplicate required attribute entry: "one.multi"`, - `invalid required attribute "penny.nic kel.dime"`, - `duplicate required attribute entry: "two.multi"`, - `invalid required attribute "*.ex-am-ple.pb"`, - ), + name: "duplicate bad entries", + field: "bananas", + attrs: []string{"bad.*.example", "bad. * .example"}, + exp: `invalid bananas required attribute "bad.*.example"`, }, { - name: "many lists: multiple problems", - attrLists: [][]string{ - {" one . multi "}, {"x.*.wildcard"}, {"x.*.wildcard"}, {"one.multi"}, {" two.multi "}, - {"penny.nic kel.dime"}, {"one.multi"}, {"two.multi"}, {"*.ex-am-ple.pb"}, {"two.multi"}, + name: "multiple problems", + field: "♪ but a bit ain't one ♪", + attrs: []string{ + "one.multi", "x.*.wildcard", "x.*.wildcard", "one.multi", "two.multi", + "penny.nic kel.dime", "one.multi", "two.multi", "*.ex-am-ple.pb", "two.multi", }, exp: joinErrs( - `invalid required attribute "x.*.wildcard"`, - `duplicate required attribute entry: "one.multi"`, - `invalid required attribute "penny.nic kel.dime"`, - `duplicate required attribute entry: "two.multi"`, - `invalid required attribute "*.ex-am-ple.pb"`, + `invalid ♪ but a bit ain't one ♪ required attribute "x.*.wildcard"`, + `duplicate ♪ but a bit ain't one ♪ required attribute: "one.multi"`, + `invalid ♪ but a bit ain't one ♪ required attribute "penny.nic kel.dime"`, + `duplicate ♪ but a bit ain't one ♪ required attribute: "two.multi"`, + `invalid ♪ but a bit ain't one ♪ required attribute "*.ex-am-ple.pb"`, ), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - err := ValidateReqAttrs(tc.attrLists...) + err := ValidateReqAttrs(tc.field, tc.attrs) assertions.AssertErrorValue(t, err, tc.exp, "ValidateReqAttrs") }) } From 04fca3736a9a81f7e1779b50ad1b248f6cd333b6 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 13:35:43 -0600 Subject: [PATCH 110/309] [1658]: Implement the Msg methods for MsgMarketManageReqAttrsRequest. --- x/exchange/market.go | 28 ++++++++++++++++++++++++++++ x/exchange/msg.go | 27 +++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index 2a728a1f6d..ab7f598f6d 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -476,6 +476,34 @@ func ValidateReqAttrs(field string, attrs []string) error { return errors.Join(errs...) } +// IntersectionOfStrings returns all string options that are in both lists. +func IntersectionOfStrings(options1, options2 []string) []string { + var rv []string + for _, o1 := range options1 { + for _, o2 := range options2 { + if o1 == o2 { + rv = append(rv, o1) + break + } + } + } + return rv +} + +// ValidateAddRemoveReqAttrs returns an error if the toAdd list has an invalid +// entry or if the two lists have one or more common entries. +func ValidateAddRemoveReqAttrs(field string, toAdd, toRemove []string) error { + var errs []error + if err := ValidateReqAttrs(field+" to add", toAdd); err != nil { + errs = append(errs, err) + } + shared := IntersectionOfStrings(toAdd, toRemove) + if len(shared) > 0 { + errs = append(errs, fmt.Errorf("cannot add and remove the same %s options: %s", field, strings.Join(shared, ","))) + } + return errors.Join(errs...) +} + // IsValidReqAttr returns true if the provided string is a valid required attribute entry. // Assumes that the provided reqAttr has already been normalized. func IsValidReqAttr(reqAttr string) bool { diff --git a/x/exchange/msg.go b/x/exchange/msg.go index bbb24a5bb2..7d0e5d7f2d 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -174,13 +174,32 @@ func (m MsgMarketManagePermissionsRequest) GetSigners() []sdk.AccAddress { } func (m MsgMarketManageReqAttrsRequest) ValidateBasic() error { - // TODO[1658]: MsgMarketManageReqAttrsRequest.ValidateBasic() - return nil + var errs []error + + if _, err := sdk.AccAddressFromBech32(m.Administrator); err != nil { + errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Administrator, err)) + } + + if m.HasUpdates() { + errs = append(errs, + ValidateAddRemoveReqAttrs("create-ask", m.CreateAskToAdd, m.CreateAskToRemove), + ValidateAddRemoveReqAttrs("create-bid", m.CreateBidToAdd, m.CreateBidToRemove), + ) + } else { + errs = append(errs, errors.New("no updates")) + } + + return errors.Join(errs...) +} + +func (m MsgMarketManageReqAttrsRequest) HasUpdates() bool { + return len(m.CreateAskToAdd) > 0 || len(m.CreateAskToRemove) > 0 || + len(m.CreateBidToAdd) > 0 || len(m.CreateBidToRemove) > 0 } func (m MsgMarketManageReqAttrsRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgMarketManageReqAttrsRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Administrator) + return []sdk.AccAddress{addr} } func (m MsgGovCreateMarketRequest) ValidateBasic() error { From 7ca7d09f9159266fdfc04c9a794d2beca644f0bb Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 14:43:41 -0600 Subject: [PATCH 111/309] [1658]: Create a generic intersection function and use that for string, coin, and fee ratio slices. --- x/exchange/market.go | 86 +++++----- x/exchange/market_test.go | 351 +++++++++++++++++++++++++++++++++----- 2 files changed, 350 insertions(+), 87 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index ab7f598f6d..2de752ab22 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -292,43 +292,15 @@ func (r FeeRatio) ApplyToLoosely(price sdk.Coin) (sdk.Coin, error) { return rv, nil } -// IntersectionOfFeeRatios returns all FeeRatios that are in both lists. -func IntersectionOfFeeRatios(ratios1, ratios2 []FeeRatio) []FeeRatio { - var rv []FeeRatio - for _, r1 := range ratios1 { - for _, r2 := range ratios2 { - if r1.Equals(r2) { - rv = append(rv, r1) - break - } - } - } - return rv -} - // ValidateDisjointFeeRatios returns an error if one or more entries appears in both lists. func ValidateDisjointFeeRatios(field string, toAdd, toRemove []FeeRatio) error { - shared := IntersectionOfFeeRatios(toAdd, toRemove) + shared := Intersection(toAdd, toRemove, FeeRatio.Equals) if len(shared) > 0 { return fmt.Errorf("cannot add and remove the same %s ratios: %s", field, FeeRatiosString(shared)) } return nil } -// IntersectionOfFeeOptions returns all Coin options that are in both lists. -func IntersectionOfFeeOptions(options1, options2 []sdk.Coin) []sdk.Coin { - var rv []sdk.Coin - for _, c1 := range options1 { - for _, c2 := range options2 { - if c1.Equal(c2) { - rv = append(rv, c1) - break - } - } - } - return rv -} - // ValidateAddRemoveFeeOptions returns an error if the toAdd list has an invalid // entry or if the two lists have one or more common entries. func ValidateAddRemoveFeeOptions(field string, toAdd, toRemove []sdk.Coin) error { @@ -336,7 +308,7 @@ func ValidateAddRemoveFeeOptions(field string, toAdd, toRemove []sdk.Coin) error if err := ValidateFeeOptions(field+" to add", toAdd); err != nil { errs = append(errs, err) } - shared := IntersectionOfFeeOptions(toAdd, toRemove) + shared := Intersection(toAdd, toRemove, CoinEquals) if len(shared) > 0 { errs = append(errs, fmt.Errorf("cannot add and remove the same %s options: %s", field, sdk.Coins(shared))) } @@ -476,20 +448,6 @@ func ValidateReqAttrs(field string, attrs []string) error { return errors.Join(errs...) } -// IntersectionOfStrings returns all string options that are in both lists. -func IntersectionOfStrings(options1, options2 []string) []string { - var rv []string - for _, o1 := range options1 { - for _, o2 := range options2 { - if o1 == o2 { - rv = append(rv, o1) - break - } - } - } - return rv -} - // ValidateAddRemoveReqAttrs returns an error if the toAdd list has an invalid // entry or if the two lists have one or more common entries. func ValidateAddRemoveReqAttrs(field string, toAdd, toRemove []string) error { @@ -497,7 +455,7 @@ func ValidateAddRemoveReqAttrs(field string, toAdd, toRemove []string) error { if err := ValidateReqAttrs(field+" to add", toAdd); err != nil { errs = append(errs, err) } - shared := IntersectionOfStrings(toAdd, toRemove) + shared := Intersection(toAdd, toRemove, StringEquals) if len(shared) > 0 { errs = append(errs, fmt.Errorf("cannot add and remove the same %s options: %s", field, strings.Join(shared, ","))) } @@ -562,3 +520,41 @@ func IsReqAttrMatch(reqAttr, accAttr string) bool { } return reqAttr == accAttr } + +// CoinEquals returns true if the two provided coin entries are equal. +// Designed for use with Intersection. +// +// We can't just provide sdk.Coin.isEqual to Intersection because that PANICS if the denoms are different. +// And we can't provide sdk.Coin.Equal to Intersection because it takes in an interface{} (instead of sdk.Coin). +func CoinEquals(a, b sdk.Coin) bool { + return a.Equal(b) +} + +// StringEquals returns true if the two provided strings are equal. +// Designed for use with Intersection. +func StringEquals(a, b string) bool { + return a == b +} + +// Intersection returns all entries that are in both lists. +func Intersection[T any](list1, list2 []T, equals func(T, T) bool) []T { + var rv []T + for _, a := range list1 { + for _, b := range list2 { + if equals(a, b) { + alreadyHave := false + for _, r := range rv { + if equals(a, r) { + alreadyHave = true + break + } + } + if !alreadyHave { + rv = append(rv, a) + } + break + } + } + } + return rv +} diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 12fa2519bf..f6651010aa 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -129,12 +129,12 @@ func TestMarket_Validate(t *testing.T) { { name: "invalid ask required attributes", market: Market{ReqAttrCreateAsk: []string{"this-attr-is-bad"}}, - expErr: []string{`invalid create-ask required attributes: invalid required attribute "this-attr-is-bad"`}, + expErr: []string{`invalid create-ask required attribute "this-attr-is-bad"`}, }, { name: "invalid bid required attributes", market: Market{ReqAttrCreateBid: []string{"this-attr-grrrr"}}, - expErr: []string{`invalid create-bid required attributes: invalid required attribute "this-attr-grrrr"`}, + expErr: []string{`invalid create-bid required attribute "this-attr-grrrr"`}, }, { name: "multiple errors", @@ -159,8 +159,8 @@ func TestMarket_Validate(t *testing.T) { `denom "fry" is defined in the seller settlement fee ratios but not buyer`, `denom "leela" is defined in the buyer settlement fee ratios but not seller`, "invalid access grant: invalid address: decoding bech32 failed: invalid separator index -1", - `invalid create-ask required attributes: invalid required attribute "this-attr-is-bad"`, - `invalid create-bid required attributes: invalid required attribute "this-attr-grrrr"`, + `invalid create-ask required attribute "this-attr-is-bad"`, + `invalid create-bid required attribute "this-attr-grrrr"`, }, }, } @@ -1178,7 +1178,7 @@ func TestFeeRatio_ApplyToLoosely(t *testing.T) { } } -func TestIntersectionOfFeeRatios(t *testing.T) { +func intersectionFeeRatioEqualsTests(t *testing.T) { feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { return FeeRatio{ Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(priceAmount)}, @@ -1188,88 +1188,88 @@ func TestIntersectionOfFeeRatios(t *testing.T) { tests := []struct { name string - ratios1 []FeeRatio - ratios2 []FeeRatio + options1 []FeeRatio + options2 []FeeRatio expected []FeeRatio }{ - {name: "nil nil", ratios1: nil, ratios2: nil, expected: nil}, - {name: "nil empty", ratios1: nil, ratios2: []FeeRatio{}, expected: nil}, - {name: "empty nil", ratios1: []FeeRatio{}, ratios2: nil, expected: nil}, - {name: "empty empty", ratios1: []FeeRatio{}, ratios2: []FeeRatio{}, expected: nil}, + {name: "nil nil", options1: nil, options2: nil, expected: nil}, + {name: "nil empty", options1: nil, options2: []FeeRatio{}, expected: nil}, + {name: "empty nil", options1: []FeeRatio{}, options2: nil, expected: nil}, + {name: "empty empty", options1: []FeeRatio{}, options2: []FeeRatio{}, expected: nil}, { name: "one nil", - ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, - ratios2: nil, + options1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + options2: nil, expected: nil, }, { name: "nil one", - ratios1: nil, - ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + options1: nil, + options2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, expected: nil, }, { name: "one one equal", - ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, - ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + options1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + options2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, expected: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, }, { name: "one one diff first price amount", - ratios1: []FeeRatio{feeRatio(3, "spicy", 2, "lemon")}, - ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + options1: []FeeRatio{feeRatio(3, "spicy", 2, "lemon")}, + options2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, expected: nil, }, { name: "one one diff first price denom", - ratios1: []FeeRatio{feeRatio(1, "bland", 2, "lemon")}, - ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + options1: []FeeRatio{feeRatio(1, "bland", 2, "lemon")}, + options2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, expected: nil, }, { name: "one one diff first fee amount", - ratios1: []FeeRatio{feeRatio(1, "spicy", 3, "lemon")}, - ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + options1: []FeeRatio{feeRatio(1, "spicy", 3, "lemon")}, + options2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, expected: nil, }, { name: "one one diff first fee denom", - ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "grape")}, - ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + options1: []FeeRatio{feeRatio(1, "spicy", 2, "grape")}, + options2: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, expected: nil, }, { name: "one one diff second price amount", - ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, - ratios2: []FeeRatio{feeRatio(3, "spicy", 2, "lemon")}, + options1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + options2: []FeeRatio{feeRatio(3, "spicy", 2, "lemon")}, expected: nil, }, { name: "one one diff second price denom", - ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, - ratios2: []FeeRatio{feeRatio(1, "bland", 2, "lemon")}, + options1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + options2: []FeeRatio{feeRatio(1, "bland", 2, "lemon")}, expected: nil, }, { name: "one one diff second fee amount", - ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, - ratios2: []FeeRatio{feeRatio(1, "spicy", 3, "lemon")}, + options1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + options2: []FeeRatio{feeRatio(1, "spicy", 3, "lemon")}, expected: nil, }, { name: "one one diff second fee denom", - ratios1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, - ratios2: []FeeRatio{feeRatio(1, "spicy", 2, "bland")}, + options1: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, + options2: []FeeRatio{feeRatio(1, "spicy", 2, "bland")}, expected: nil, }, { name: "three three two common", - ratios1: []FeeRatio{ + options1: []FeeRatio{ feeRatio(1, "lamp", 2, "bag"), feeRatio(3, "keys", 4, "phone"), feeRatio(5, "fan", 6, "bottle"), }, - ratios2: []FeeRatio{ + options2: []FeeRatio{ feeRatio(3, "kays", 4, "phone"), feeRatio(5, "fan", 6, "bottle"), feeRatio(1, "lamp", 2, "bag"), @@ -1285,10 +1285,10 @@ func TestIntersectionOfFeeRatios(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var actual []FeeRatio testFunc := func() { - actual = IntersectionOfFeeRatios(tc.ratios1, tc.ratios2) + actual = Intersection(tc.options1, tc.options2, FeeRatio.Equals) } - require.NotPanics(t, testFunc, "IntersectionOfFeeRatios") - assert.Equal(t, tc.expected, actual, "IntersectionOfFeeRatios result") + require.NotPanics(t, testFunc, "Intersection(FeeRatio.Equals)") + assert.Equal(t, tc.expected, actual, "Intersection(FeeRatio.Equals) result") }) } } @@ -1409,7 +1409,7 @@ func TestValidateDisjointFeeRatios(t *testing.T) { } } -func TestIntersectionOfFeeOptions(t *testing.T) { +func intersectionCoinEqualsTests(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } @@ -1478,10 +1478,10 @@ func TestIntersectionOfFeeOptions(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var actual []sdk.Coin testFunc := func() { - actual = IntersectionOfFeeOptions(tc.options1, tc.options2) + actual = Intersection(tc.options1, tc.options2, CoinEquals) } - require.NotPanics(t, testFunc, "IntersectionOfFeeOptions") - assert.Equal(t, tc.expected, actual, "IntersectionOfFeeOptions result") + require.NotPanics(t, testFunc, "Intersection(CoinEquals)") + assert.Equal(t, tc.expected, actual, "Intersection(CoinEquals) result") }) } } @@ -2264,6 +2264,171 @@ func TestValidateReqAttrs(t *testing.T) { } } +func intersectionStringEqualsTests(t *testing.T) { + tests := []struct { + name string + options1 []string + options2 []string + expected []string + }{ + {name: "nil lists", options1: nil, options2: nil, expected: nil}, + {name: "empty lists", options1: []string{}, options2: []string{}, expected: nil}, + {name: "one item to nil", options1: []string{"one"}, options2: nil, expected: nil}, + {name: "nil to one item", options1: nil, options2: []string{"one"}, expected: nil}, + {name: "one to one: different", options1: []string{"one"}, options2: []string{"two"}, expected: nil}, + {name: "one to one: same", options1: []string{"one"}, options2: []string{"one"}, expected: []string{"one"}}, + { + name: "one to three: none common", + options1: []string{"one"}, + options2: []string{"two", "three", "four"}, + expected: nil, + }, + { + name: "one to three: first", + options1: []string{"one"}, + options2: []string{"one", "three", "four"}, + expected: []string{"one"}, + }, + { + name: "one to three: second", + options1: []string{"one"}, + options2: []string{"two", "one", "four"}, + expected: []string{"one"}, + }, + { + name: "one to three: third", + options1: []string{"one"}, + options2: []string{"two", "three", "one"}, + expected: []string{"one"}, + }, + { + name: "one to three: all equal", + options1: []string{"one"}, + options2: []string{"one", "one", "one"}, + expected: []string{"one"}, + }, + { + name: "three to one: none common", + options1: []string{"one", "two", "three"}, + options2: []string{"four"}, + expected: nil, + }, + { + name: "three to one: first", + options1: []string{"one", "two", "three"}, + options2: []string{"one"}, + expected: []string{"one"}, + }, + { + name: "three to one: second", + options1: []string{"one", "two", "three"}, + options2: []string{"two"}, + expected: []string{"two"}, + }, + { + name: "three to one: third", + options1: []string{"one", "two", "three"}, + options2: []string{"three"}, + expected: []string{"three"}, + }, + { + name: "three to one: all equal", + options1: []string{"one", "one", "one"}, + options2: []string{"one"}, + expected: []string{"one"}, + }, + { + name: "three to three: none common", + options1: []string{"one", "two", "three"}, + options2: []string{"four", "five", "six"}, + expected: nil, + }, + { + name: "three to three: one common: first to first", + options1: []string{"one", "two", "three"}, + options2: []string{"one", "five", "six"}, + expected: []string{"one"}, + }, + { + name: "three to three: one common: first to second", + options1: []string{"one", "two", "three"}, + options2: []string{"four", "one", "six"}, + expected: []string{"one"}, + }, + { + name: "three to three: one common: first to third", + options1: []string{"one", "two", "three"}, + options2: []string{"four", "five", "one"}, + expected: []string{"one"}, + }, + { + name: "three to three: one common: second to first", + options1: []string{"one", "two", "three"}, + options2: []string{"two", "five", "six"}, + expected: []string{"two"}, + }, + { + name: "three to three: one common: second to second", + options1: []string{"one", "two", "three"}, + options2: []string{"four", "two", "six"}, + expected: []string{"two"}, + }, + { + name: "three to three: one common: second to third", + options1: []string{"one", "two", "three"}, + options2: []string{"four", "five", "two"}, + expected: []string{"two"}, + }, + { + name: "three to three: one common: third to first", + options1: []string{"one", "two", "three"}, + options2: []string{"three", "five", "six"}, + expected: []string{"three"}, + }, + { + name: "three to three: one common: third to second", + options1: []string{"one", "two", "three"}, + options2: []string{"four", "three", "six"}, + expected: []string{"three"}, + }, + { + name: "three to three: one common: third to third", + options1: []string{"one", "two", "three"}, + options2: []string{"four", "five", "three"}, + expected: []string{"three"}, + }, + { + name: "three to three: two common", + options1: []string{"one", "two", "three"}, + options2: []string{"two", "five", "one"}, + expected: []string{"one", "two"}, + }, + { + name: "three to three: same lists different order", + options1: []string{"one", "two", "three"}, + options2: []string{"two", "three", "one"}, + expected: []string{"one", "two", "three"}, + }, + { + name: "three to three: all equal", + options1: []string{"one", "one", "one"}, + options2: []string{"one", "one", "one"}, + expected: []string{"one"}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual []string + testFunc := func() { + actual = Intersection(tc.options1, tc.options2, StringEquals) + } + require.NotPanics(t, testFunc, "Intersection(StringEquals)") + assert.Equal(t, tc.expected, actual, "Intersection(StringEquals) result") + }) + } +} + func TestIsValidReqAttr(t *testing.T) { tests := []struct { name string @@ -2814,3 +2979,105 @@ func TestIsReqAttrMatch(t *testing.T) { }) } } + +func TestCoinEquals(t *testing.T) { + tests := []struct { + name string + a sdk.Coin + b sdk.Coin + exp bool + }{ + { + name: "zero-value coins", + a: sdk.Coin{}, + b: sdk.Coin{}, + exp: true, + }, + { + name: "different amounts", + a: sdk.NewInt64Coin("pear", 2), + b: sdk.NewInt64Coin("pear", 3), + exp: false, + }, + { + name: "different denoms", + a: sdk.NewInt64Coin("pear", 2), + b: sdk.NewInt64Coin("onion", 2), + exp: false, + }, + { + name: "same denom and amount", + a: sdk.NewInt64Coin("pear", 2), + b: sdk.NewInt64Coin("pear", 2), + exp: true, + }, + { + name: "same denom zero amounts", + a: sdk.NewInt64Coin("pear", 0), + b: sdk.NewInt64Coin("pear", 0), + exp: true, + }, + { + name: "diff denom zero amounts", + a: sdk.NewInt64Coin("pear", 0), + b: sdk.NewInt64Coin("onion", 0), + exp: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = CoinEquals(tc.a, tc.b) + } + require.NotPanics(t, testFunc, "CoinEquals(%q, %q)", tc.a, tc.b) + assert.Equal(t, tc.exp, actual, "CoinEquals(%q, %q) result", tc.a, tc.b) + }) + } +} + +func TestStringEquals(t *testing.T) { + tests := []struct { + a string + b string + exp bool + }{ + {a: "", b: "", exp: true}, + {a: "c", b: "c", exp: true}, + {a: "a b", b: "a b", exp: true}, + {a: "x.y.z", b: "x.y.z", exp: true}, + {a: "*.y.z", b: "x.y.z", exp: false}, + {a: "a", b: "b", exp: false}, + {a: "a", b: "ba", exp: false}, + {a: "a", b: "ba", exp: false}, + {a: "ab", b: "b", exp: false}, + {a: "ba", b: "b", exp: false}, + {a: "cccc", b: "ccccc", exp: false}, + {a: "ccccc", b: "cccc", exp: false}, + {a: "a b", b: "b a", exp: false}, + {a: "a b", b: "a-b", exp: false}, + {a: "c", b: " c", exp: false}, + {a: "c", b: "c ", exp: false}, + {a: " c", b: "c", exp: false}, + {a: "c ", b: "c", exp: false}, + {a: "c ", b: " c", exp: false}, + } + + for _, tc := range tests { + t.Run(fmt.Sprintf("%q|%q", tc.a, tc.b), func(t *testing.T) { + var actual bool + testFunc := func() { + actual = StringEquals(tc.a, tc.b) + } + require.NotPanics(t, testFunc, "StringEquals(%q, %q)", tc.a, tc.b) + assert.Equal(t, tc.exp, actual, "StringEquals(%q, %q) result", tc.a, tc.b) + }) + } +} + +func TestIntersection(t *testing.T) { + t.Run("of fee ratios", intersectionFeeRatioEqualsTests) + t.Run("of coins", intersectionCoinEqualsTests) + t.Run("of strings", intersectionStringEqualsTests) +} From 44349c75a768f88d1711f4bd487911d228a25ade Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 15:37:23 -0600 Subject: [PATCH 112/309] [1658]: Repurpose contains to work on any with a provided equals function and use that for intersection. Create containsString that uses the new contains func for strings. Make intersection private and make public non-generic versions. Use strings.EqualFold for attribute intersection. Remove some colons from error messages that were right before quoted values. --- x/exchange/market.go | 97 +++++++------- x/exchange/market_test.go | 269 +++++++++++++++++++++----------------- 2 files changed, 197 insertions(+), 169 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index 2de752ab22..70595cbe71 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -125,13 +125,13 @@ func ValidateFeeRatios(sellerRatios, buyerRatios []FeeRatio) error { } for _, denom := range sellerPriceDenoms { - if !contains(buyerPriceDenoms, denom) { + if !containsString(buyerPriceDenoms, denom) { errs = append(errs, fmt.Errorf("denom %q is defined in the seller settlement fee ratios but not buyer", denom)) } } for _, denom := range buyerPriceDenoms { - if !contains(sellerPriceDenoms, denom) { + if !containsString(sellerPriceDenoms, denom) { errs = append(errs, fmt.Errorf("denom %q is defined in the buyer settlement fee ratios but not seller", denom)) } } @@ -198,14 +198,11 @@ func ValidateBuyerFeeRatios(ratios []FeeRatio) error { return errors.Join(errs...) } -// contains returns true if the provided toFind is present in the provided vals. -func contains[T comparable](vals []T, toFind T) bool { - for _, v := range vals { - if toFind == v { - return true - } - } - return false +// containsString returns true if the string to find is in the vals slice. +func containsString(vals []string, toFind string) bool { + return contains(vals, toFind, func(a, b string) bool { + return a == b + }) } // String returns a string representation of this FeeRatio. @@ -292,15 +289,34 @@ func (r FeeRatio) ApplyToLoosely(price sdk.Coin) (sdk.Coin, error) { return rv, nil } +// IntersectionOfFeeRatios returns each FeeRatio entry that is in both lists. +func IntersectionOfFeeRatios(list1, list2 []FeeRatio) []FeeRatio { + return intersection(list1, list2, FeeRatio.Equals) +} + // ValidateDisjointFeeRatios returns an error if one or more entries appears in both lists. func ValidateDisjointFeeRatios(field string, toAdd, toRemove []FeeRatio) error { - shared := Intersection(toAdd, toRemove, FeeRatio.Equals) + shared := IntersectionOfFeeRatios(toAdd, toRemove) if len(shared) > 0 { - return fmt.Errorf("cannot add and remove the same %s ratios: %s", field, FeeRatiosString(shared)) + return fmt.Errorf("cannot add and remove the same %s ratios %s", field, FeeRatiosString(shared)) } return nil } +// CoinEquals returns true if the two provided coin entries are equal. +// Designed for use with intersection. +// +// We can't just provide sdk.Coin.isEqual to intersection because that PANICS if the denoms are different. +// And we can't provide sdk.Coin.Equal to intersection because it takes in an interface{} (instead of sdk.Coin). +func CoinEquals(a, b sdk.Coin) bool { + return a.Equal(b) +} + +// IntersectionOfCoins returns each sdk.Coin entry that is in both lists. +func IntersectionOfCoins(list1, list2 []sdk.Coin) []sdk.Coin { + return intersection(list1, list2, CoinEquals) +} + // ValidateAddRemoveFeeOptions returns an error if the toAdd list has an invalid // entry or if the two lists have one or more common entries. func ValidateAddRemoveFeeOptions(field string, toAdd, toRemove []sdk.Coin) error { @@ -308,9 +324,9 @@ func ValidateAddRemoveFeeOptions(field string, toAdd, toRemove []sdk.Coin) error if err := ValidateFeeOptions(field+" to add", toAdd); err != nil { errs = append(errs, err) } - shared := Intersection(toAdd, toRemove, CoinEquals) + shared := IntersectionOfCoins(toAdd, toRemove) if len(shared) > 0 { - errs = append(errs, fmt.Errorf("cannot add and remove the same %s options: %s", field, sdk.Coins(shared))) + errs = append(errs, fmt.Errorf("cannot add and remove the same %s options %s", field, sdk.Coins(shared))) } return errors.Join(errs...) } @@ -433,7 +449,7 @@ func ValidateReqAttrs(field string, attrs []string) error { normalized := nametypes.NormalizeName(attr) if seen[normalized] { if !bad[normalized] { - errs = append(errs, fmt.Errorf("duplicate %s required attribute: %q", + errs = append(errs, fmt.Errorf("duplicate %s required attribute %q", field, attr)) bad[normalized] = true } @@ -448,6 +464,11 @@ func ValidateReqAttrs(field string, attrs []string) error { return errors.Join(errs...) } +// IntersectionOfAttributes returns each attribute that is in both lists. +func IntersectionOfAttributes(list1, list2 []string) []string { + return intersection(list1, list2, strings.EqualFold) +} + // ValidateAddRemoveReqAttrs returns an error if the toAdd list has an invalid // entry or if the two lists have one or more common entries. func ValidateAddRemoveReqAttrs(field string, toAdd, toRemove []string) error { @@ -455,9 +476,10 @@ func ValidateAddRemoveReqAttrs(field string, toAdd, toRemove []string) error { if err := ValidateReqAttrs(field+" to add", toAdd); err != nil { errs = append(errs, err) } - shared := Intersection(toAdd, toRemove, StringEquals) + // attributes are lower-cased during attribute, so we should use a case-insensitive matcher. + shared := IntersectionOfAttributes(toAdd, toRemove) if len(shared) > 0 { - errs = append(errs, fmt.Errorf("cannot add and remove the same %s options: %s", field, strings.Join(shared, ","))) + errs = append(errs, fmt.Errorf("cannot add and remove the same %s required attributes \"%s\"", field, strings.Join(shared, "\",\""))) } return errors.Join(errs...) } @@ -521,39 +543,22 @@ func IsReqAttrMatch(reqAttr, accAttr string) bool { return reqAttr == accAttr } -// CoinEquals returns true if the two provided coin entries are equal. -// Designed for use with Intersection. -// -// We can't just provide sdk.Coin.isEqual to Intersection because that PANICS if the denoms are different. -// And we can't provide sdk.Coin.Equal to Intersection because it takes in an interface{} (instead of sdk.Coin). -func CoinEquals(a, b sdk.Coin) bool { - return a.Equal(b) -} - -// StringEquals returns true if the two provided strings are equal. -// Designed for use with Intersection. -func StringEquals(a, b string) bool { - return a == b +// contains returns true if the provided toFind is present in the provided vals. +func contains[T any](vals []T, toFind T, equals func(T, T) bool) bool { + for _, v := range vals { + if equals(toFind, v) { + return true + } + } + return false } -// Intersection returns all entries that are in both lists. -func Intersection[T any](list1, list2 []T, equals func(T, T) bool) []T { +// intersection returns each entry that is in both lists. +func intersection[T any](list1, list2 []T, equals func(T, T) bool) []T { var rv []T for _, a := range list1 { - for _, b := range list2 { - if equals(a, b) { - alreadyHave := false - for _, r := range rv { - if equals(a, r) { - alreadyHave = true - break - } - } - if !alreadyHave { - rv = append(rv, a) - } - break - } + if contains(list2, a, equals) && !contains(rv, a, equals) { + rv = append(rv, a) } } return rv diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index f6651010aa..eff1f3ae5c 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -1178,7 +1178,7 @@ func TestFeeRatio_ApplyToLoosely(t *testing.T) { } } -func intersectionFeeRatioEqualsTests(t *testing.T) { +func TestIntersectionOfFeeRatios(t *testing.T) { feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { return FeeRatio{ Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(priceAmount)}, @@ -1285,10 +1285,10 @@ func intersectionFeeRatioEqualsTests(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var actual []FeeRatio testFunc := func() { - actual = Intersection(tc.options1, tc.options2, FeeRatio.Equals) + actual = IntersectionOfFeeRatios(tc.options1, tc.options2) } - require.NotPanics(t, testFunc, "Intersection(FeeRatio.Equals)") - assert.Equal(t, tc.expected, actual, "Intersection(FeeRatio.Equals) result") + require.NotPanics(t, testFunc, "IntersectionOfFeeRatios") + assert.Equal(t, tc.expected, actual, "IntersectionOfFeeRatios result") }) } } @@ -1329,7 +1329,7 @@ func TestValidateDisjointFeeRatios(t *testing.T) { field: "", toAdd: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, toRemove: []FeeRatio{feeRatio(1, "spicy", 2, "lemon")}, - expErr: "cannot add and remove the same ratios: 1spicy:2lemon", + expErr: "cannot add and remove the same ratios 1spicy:2lemon", }, { name: "one one diff first price amount", @@ -1392,7 +1392,7 @@ func TestValidateDisjointFeeRatios(t *testing.T) { feeRatio(5, "fan", 6, "bottle"), feeRatio(1, "lamp", 2, "bag"), }, - expErr: "cannot add and remove the same test field name ratios: 1lamp:2bag,5fan:6bottle", + expErr: "cannot add and remove the same test field name ratios 1lamp:2bag,5fan:6bottle", }, } @@ -1409,7 +1409,64 @@ func TestValidateDisjointFeeRatios(t *testing.T) { } } -func intersectionCoinEqualsTests(t *testing.T) { +func TestCoinEquals(t *testing.T) { + tests := []struct { + name string + a sdk.Coin + b sdk.Coin + exp bool + }{ + { + name: "zero-value coins", + a: sdk.Coin{}, + b: sdk.Coin{}, + exp: true, + }, + { + name: "different amounts", + a: sdk.NewInt64Coin("pear", 2), + b: sdk.NewInt64Coin("pear", 3), + exp: false, + }, + { + name: "different denoms", + a: sdk.NewInt64Coin("pear", 2), + b: sdk.NewInt64Coin("onion", 2), + exp: false, + }, + { + name: "same denom and amount", + a: sdk.NewInt64Coin("pear", 2), + b: sdk.NewInt64Coin("pear", 2), + exp: true, + }, + { + name: "same denom zero amounts", + a: sdk.NewInt64Coin("pear", 0), + b: sdk.NewInt64Coin("pear", 0), + exp: true, + }, + { + name: "diff denom zero amounts", + a: sdk.NewInt64Coin("pear", 0), + b: sdk.NewInt64Coin("onion", 0), + exp: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = CoinEquals(tc.a, tc.b) + } + require.NotPanics(t, testFunc, "CoinEquals(%q, %q)", tc.a, tc.b) + assert.Equal(t, tc.exp, actual, "CoinEquals(%q, %q) result", tc.a, tc.b) + }) + } +} + +func TestIntersectionOfCoins(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } @@ -1478,10 +1535,10 @@ func intersectionCoinEqualsTests(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var actual []sdk.Coin testFunc := func() { - actual = Intersection(tc.options1, tc.options2, CoinEquals) + actual = IntersectionOfCoins(tc.options1, tc.options2) } - require.NotPanics(t, testFunc, "Intersection(CoinEquals)") - assert.Equal(t, tc.expected, actual, "Intersection(CoinEquals) result") + require.NotPanics(t, testFunc, "IntersectionOfCoins") + assert.Equal(t, tc.expected, actual, "IntersectionOfCoins result") }) } } @@ -1522,7 +1579,7 @@ func TestValidateAddRemoveFeeOptions(t *testing.T) { field: "", toAdd: []sdk.Coin{coin(1, "finger")}, toRemove: []sdk.Coin{coin(1, "finger")}, - expErr: "cannot add and remove the same options: 1finger", + expErr: "cannot add and remove the same options 1finger", }, { name: "one one different first amount", @@ -1553,7 +1610,7 @@ func TestValidateAddRemoveFeeOptions(t *testing.T) { field: "body part", toAdd: []sdk.Coin{coin(1, "finger"), coin(2, "toe"), coin(3, "elbow")}, toRemove: []sdk.Coin{coin(5, "toe"), coin(3, "elbow"), coin(1, "finger")}, - expErr: "cannot add and remove the same body part options: 1finger,3elbow", + expErr: "cannot add and remove the same body part options 1finger,3elbow", }, { name: "adding one invalid", @@ -1586,7 +1643,7 @@ func TestValidateAddRemoveFeeOptions(t *testing.T) { toRemove: []sdk.Coin{coin(-1, "bananas")}, expErr: joinErrs( `invalid fruits to add option "-1bananas": negative coin amount: -1`, - "cannot add and remove the same fruits options: -1bananas", + "cannot add and remove the same fruits options -1bananas", ), }, { @@ -1607,7 +1664,7 @@ func TestValidateAddRemoveFeeOptions(t *testing.T) { expErr: joinErrs( `invalid merrs! to add option "3l": invalid denom: l`, `invalid merrs! to add option "-55peach": negative coin amount: -55`, - "cannot add and remove the same merrs! options: 14copycoin", + "cannot add and remove the same merrs! options 14copycoin", ), }, } @@ -2231,7 +2288,7 @@ func TestValidateReqAttrs(t *testing.T) { name: "duplicate entries", field: "just some field name thingy", attrs: []string{"*.multi", "*.multi", "*.multi"}, - exp: `duplicate just some field name thingy required attribute: "*.multi"`, + exp: `duplicate just some field name thingy required attribute "*.multi"`, }, { name: "duplicate bad entries", @@ -2248,9 +2305,9 @@ func TestValidateReqAttrs(t *testing.T) { }, exp: joinErrs( `invalid ♪ but a bit ain't one ♪ required attribute "x.*.wildcard"`, - `duplicate ♪ but a bit ain't one ♪ required attribute: "one.multi"`, + `duplicate ♪ but a bit ain't one ♪ required attribute "one.multi"`, `invalid ♪ but a bit ain't one ♪ required attribute "penny.nic kel.dime"`, - `duplicate ♪ but a bit ain't one ♪ required attribute: "two.multi"`, + `duplicate ♪ but a bit ain't one ♪ required attribute "two.multi"`, `invalid ♪ but a bit ain't one ♪ required attribute "*.ex-am-ple.pb"`, ), }, @@ -2264,7 +2321,7 @@ func TestValidateReqAttrs(t *testing.T) { } } -func intersectionStringEqualsTests(t *testing.T) { +func TestIntersectionOfAttributes(t *testing.T) { tests := []struct { name string options1 []string @@ -2421,10 +2478,78 @@ func intersectionStringEqualsTests(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var actual []string testFunc := func() { - actual = Intersection(tc.options1, tc.options2, StringEquals) + actual = IntersectionOfAttributes(tc.options1, tc.options2) } - require.NotPanics(t, testFunc, "Intersection(StringEquals)") - assert.Equal(t, tc.expected, actual, "Intersection(StringEquals) result") + require.NotPanics(t, testFunc, "IntersectionOfAttributes") + assert.Equal(t, tc.expected, actual, "IntersectionOfAttributes result") + }) + } +} + +func TestValidateAddRemoveReqAttrs(t *testing.T) { + tests := []struct { + name string + field string + toAdd []string + toRemove []string + expErr string + }{ + { + name: "nil lists", + field: "-<({[FIELD]})>-", + toAdd: nil, + toRemove: nil, + }, + { + name: "empty lists", + field: "-<({[FIELD]})>-", + toAdd: []string{}, + toRemove: []string{}, + }, + { + name: "invalid to-add entry", + field: "-<({[FIELD]})>-", + toAdd: []string{"this has too many spaces"}, + toRemove: nil, + expErr: "invalid -<({[FIELD]})>- to add required attribute \"this has too many spaces\"", + }, + { + name: "invalid to-remove entry", + field: "-<({[FIELD]})>-", + toAdd: nil, + toRemove: []string{"this has too many spaces"}, + }, + { + name: "duplicate to-add entry", + field: "-<({[FIELD]})>-", + toAdd: []string{"abc", "def", "abc", "abc"}, + toRemove: nil, + expErr: "duplicate -<({[FIELD]})>- to add required attribute \"abc\"", + }, + { + name: "same entry in both lists", + field: "-<({[FIELD]})>-", + toAdd: []string{"one", "two", "three"}, + toRemove: []string{"four", "five", "six", "two", "seven"}, + expErr: "cannot add and remove the same -<({[FIELD]})>- required attributes \"two\"", + }, + { + name: "two entries in both lists differently cased", + field: "-<({[FIELD]})>-", + toAdd: []string{"one", "SEvEN", "two", "three"}, + toRemove: []string{"four", "five", "six", "Two", "seven"}, + expErr: "cannot add and remove the same -<({[FIELD]})>- required attributes \"SEvEN\",\"two\"", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = ValidateAddRemoveReqAttrs(tc.field, tc.toAdd, tc.toRemove) + } + require.NotPanics(t, testFunc, "ValidateAddRemoveReqAttrs") + assertions.AssertErrorValue(t, err, tc.expErr, "ValidateAddRemoveReqAttrs error") }) } } @@ -2979,105 +3104,3 @@ func TestIsReqAttrMatch(t *testing.T) { }) } } - -func TestCoinEquals(t *testing.T) { - tests := []struct { - name string - a sdk.Coin - b sdk.Coin - exp bool - }{ - { - name: "zero-value coins", - a: sdk.Coin{}, - b: sdk.Coin{}, - exp: true, - }, - { - name: "different amounts", - a: sdk.NewInt64Coin("pear", 2), - b: sdk.NewInt64Coin("pear", 3), - exp: false, - }, - { - name: "different denoms", - a: sdk.NewInt64Coin("pear", 2), - b: sdk.NewInt64Coin("onion", 2), - exp: false, - }, - { - name: "same denom and amount", - a: sdk.NewInt64Coin("pear", 2), - b: sdk.NewInt64Coin("pear", 2), - exp: true, - }, - { - name: "same denom zero amounts", - a: sdk.NewInt64Coin("pear", 0), - b: sdk.NewInt64Coin("pear", 0), - exp: true, - }, - { - name: "diff denom zero amounts", - a: sdk.NewInt64Coin("pear", 0), - b: sdk.NewInt64Coin("onion", 0), - exp: false, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual bool - testFunc := func() { - actual = CoinEquals(tc.a, tc.b) - } - require.NotPanics(t, testFunc, "CoinEquals(%q, %q)", tc.a, tc.b) - assert.Equal(t, tc.exp, actual, "CoinEquals(%q, %q) result", tc.a, tc.b) - }) - } -} - -func TestStringEquals(t *testing.T) { - tests := []struct { - a string - b string - exp bool - }{ - {a: "", b: "", exp: true}, - {a: "c", b: "c", exp: true}, - {a: "a b", b: "a b", exp: true}, - {a: "x.y.z", b: "x.y.z", exp: true}, - {a: "*.y.z", b: "x.y.z", exp: false}, - {a: "a", b: "b", exp: false}, - {a: "a", b: "ba", exp: false}, - {a: "a", b: "ba", exp: false}, - {a: "ab", b: "b", exp: false}, - {a: "ba", b: "b", exp: false}, - {a: "cccc", b: "ccccc", exp: false}, - {a: "ccccc", b: "cccc", exp: false}, - {a: "a b", b: "b a", exp: false}, - {a: "a b", b: "a-b", exp: false}, - {a: "c", b: " c", exp: false}, - {a: "c", b: "c ", exp: false}, - {a: " c", b: "c", exp: false}, - {a: "c ", b: "c", exp: false}, - {a: "c ", b: " c", exp: false}, - } - - for _, tc := range tests { - t.Run(fmt.Sprintf("%q|%q", tc.a, tc.b), func(t *testing.T) { - var actual bool - testFunc := func() { - actual = StringEquals(tc.a, tc.b) - } - require.NotPanics(t, testFunc, "StringEquals(%q, %q)", tc.a, tc.b) - assert.Equal(t, tc.exp, actual, "StringEquals(%q, %q) result", tc.a, tc.b) - }) - } -} - -func TestIntersection(t *testing.T) { - t.Run("of fee ratios", intersectionFeeRatioEqualsTests) - t.Run("of coins", intersectionCoinEqualsTests) - t.Run("of strings", intersectionStringEqualsTests) -} From dbdf85cbb3e6b26949f598b153c8e21c64fa637c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 15:56:21 -0600 Subject: [PATCH 113/309] [1658]: Unit tests on MsgMarketManageReqAttrsRequest.ValidateBasic(). --- x/exchange/msg_test.go | 138 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 2 deletions(-) diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index d6243af10a..109c40c86b 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -10,7 +10,6 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/provenance-io/provenance/testutil/assertions" ) @@ -412,7 +411,142 @@ func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) { // TODO[1658]: func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) -// TODO[1658]: func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) +func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { + goodAdmin := sdk.AccAddress("goodAdmin___________").String() + + tests := []struct { + name string + msg MsgMarketManageReqAttrsRequest + expErr []string + }{ + { + name: "no admin", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: "", + CreateAskToAdd: []string{"abc"}, + }, + expErr: []string{"invalid administrator", "empty address string is not allowed"}, + }, + { + name: "bad admin", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: "not1valid", + CreateAskToAdd: []string{"abc"}, + }, + expErr: []string{"invalid administrator", "decoding bech32 failed"}, + }, + { + name: "no updates", + msg: MsgMarketManageReqAttrsRequest{Administrator: goodAdmin}, + expErr: []string{"no updates"}, + }, + { + name: "invalid create ask to add entry", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: goodAdmin, + CreateAskToAdd: []string{"in-valid-attr"}, + }, + expErr: []string{"invalid create-ask to add required attribute \"in-valid-attr\""}, + }, + { + name: "invalid create ask to remove entry", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: goodAdmin, + CreateAskToRemove: []string{"in-valid-attr"}, + }, + }, + { + name: "invalid create bid to add entry", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: goodAdmin, + CreateBidToAdd: []string{"in-valid-attr"}, + }, + expErr: []string{"invalid create-bid to add required attribute \"in-valid-attr\""}, + }, + { + name: "invalid create bid to remove entry", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: goodAdmin, + CreateBidToRemove: []string{"in-valid-attr"}, + }, + }, + { + name: "add and remove same create ask entry", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: goodAdmin, + CreateAskToAdd: []string{"abc", "def", "ghi"}, + CreateAskToRemove: []string{"jkl", "abc"}, + }, + expErr: []string{"cannot add and remove the same create-ask required attributes \"abc\""}, + }, + { + name: "add and remove same create bid entry", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: goodAdmin, + CreateBidToAdd: []string{"abc", "def", "ghi"}, + CreateBidToRemove: []string{"jkl", "abc"}, + }, + expErr: []string{"cannot add and remove the same create-bid required attributes \"abc\""}, + }, + { + name: "add to create-ask the same as remove from create-bid", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: goodAdmin, + CreateAskToAdd: []string{"abc", "def", "ghi"}, + CreateBidToRemove: []string{"jkl", "abc"}, + }, + }, + { + name: "add to create-bid the same as remove from create-ask", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: goodAdmin, + CreateBidToAdd: []string{"abc", "def", "ghi"}, + CreateAskToRemove: []string{"jkl", "abc"}, + }, + }, + { + name: "add one to and remove one from each", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: goodAdmin, + CreateAskToAdd: []string{"to-add.ask"}, + CreateAskToRemove: []string{"to-remove.ask"}, + CreateBidToAdd: []string{"to-add.bid"}, + CreateBidToRemove: []string{"to-remove.bid"}, + }, + }, + { + name: "no admin and no updates", + msg: MsgMarketManageReqAttrsRequest{}, + expErr: []string{ + "invalid administrator", + "no updates", + }, + }, + { + name: "multiple errors", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: "not1valid", + CreateAskToAdd: []string{"bad-ask-attr", "dup-ask"}, + CreateAskToRemove: []string{"dup-ask"}, + CreateBidToAdd: []string{"bad-bid-attr", "dup-bid"}, + CreateBidToRemove: []string{"dup-bid"}, + }, + expErr: []string{ + "invalid administrator", + "invalid create-ask to add required attribute \"bad-ask-attr\"", + "cannot add and remove the same create-ask required attributes \"dup-ask\"", + "invalid create-bid to add required attribute \"bad-bid-attr\"", + "cannot add and remove the same create-bid required attributes \"dup-bid\"", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} func TestMsgGovCreateMarketRequest_ValidateBasic(t *testing.T) { authority := sdk.AccAddress("authority___________").String() From 5d83522b20d88c6cd613ecab57dbfc1f86d37e6f Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 16:06:45 -0600 Subject: [PATCH 114/309] [1658]: Add the missing market_id field to MsgMarketManageReqAttrsRequest. --- docs/proto-docs.md | 1 + proto/provenance/exchange/v1/tx.proto | 10 +- x/exchange/tx.pb.go | 238 +++++++++++++++----------- 3 files changed, 144 insertions(+), 105 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index cde7e7abfb..3e16cdb096 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2368,6 +2368,7 @@ MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `administrator` | [string](#string) | | administrator is the account with withdraw permission requesting the withdrawal. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | | `create_ask_to_add` | [string](#string) | repeated | create_ask_to_add are the attributes that should now also be required to create an ask order. | | `create_ask_to_remove` | [string](#string) | repeated | create_ask_to_add are the attributes that should no longer be required to create an ask order. | | `create_bid_to_add` | [string](#string) | repeated | create_ask_to_add are the attributes that should now also be required to create a bid order. | diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index b6294fb0bc..917dc0dc86 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -190,14 +190,16 @@ message MsgMarketManageReqAttrsRequest { // administrator is the account with withdraw permission requesting the withdrawal. string administrator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market_id is the numerical identifier of the market to update required attributes for. + uint32 market_id = 2; // create_ask_to_add are the attributes that should now also be required to create an ask order. - repeated string create_ask_to_add = 2; + repeated string create_ask_to_add = 3; // create_ask_to_add are the attributes that should no longer be required to create an ask order. - repeated string create_ask_to_remove = 3; + repeated string create_ask_to_remove = 4; // create_ask_to_add are the attributes that should now also be required to create a bid order. - repeated string create_bid_to_add = 4; + repeated string create_bid_to_add = 5; // create_ask_to_add are the attributes that should no longer be required to create a bid order. - repeated string create_bid_to_remove = 5; + repeated string create_bid_to_remove = 6; } // MsgMarketManageReqAttrsResponse is a response message for the MarketManageReqAttrs endpoint. diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index e642210c41..3ebc4a71da 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -958,14 +958,16 @@ var xxx_messageInfo_MsgMarketManagePermissionsResponse proto.InternalMessageInfo type MsgMarketManageReqAttrsRequest struct { // administrator is the account with withdraw permission requesting the withdrawal. Administrator string `protobuf:"bytes,1,opt,name=administrator,proto3" json:"administrator,omitempty"` + // market_id is the numerical identifier of the market to update required attributes for. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // create_ask_to_add are the attributes that should now also be required to create an ask order. - CreateAskToAdd []string `protobuf:"bytes,2,rep,name=create_ask_to_add,json=createAskToAdd,proto3" json:"create_ask_to_add,omitempty"` + CreateAskToAdd []string `protobuf:"bytes,3,rep,name=create_ask_to_add,json=createAskToAdd,proto3" json:"create_ask_to_add,omitempty"` // create_ask_to_add are the attributes that should no longer be required to create an ask order. - CreateAskToRemove []string `protobuf:"bytes,3,rep,name=create_ask_to_remove,json=createAskToRemove,proto3" json:"create_ask_to_remove,omitempty"` + CreateAskToRemove []string `protobuf:"bytes,4,rep,name=create_ask_to_remove,json=createAskToRemove,proto3" json:"create_ask_to_remove,omitempty"` // create_ask_to_add are the attributes that should now also be required to create a bid order. - CreateBidToAdd []string `protobuf:"bytes,4,rep,name=create_bid_to_add,json=createBidToAdd,proto3" json:"create_bid_to_add,omitempty"` + CreateBidToAdd []string `protobuf:"bytes,5,rep,name=create_bid_to_add,json=createBidToAdd,proto3" json:"create_bid_to_add,omitempty"` // create_ask_to_add are the attributes that should no longer be required to create a bid order. - CreateBidToRemove []string `protobuf:"bytes,5,rep,name=create_bid_to_remove,json=createBidToRemove,proto3" json:"create_bid_to_remove,omitempty"` + CreateBidToRemove []string `protobuf:"bytes,6,rep,name=create_bid_to_remove,json=createBidToRemove,proto3" json:"create_bid_to_remove,omitempty"` } func (m *MsgMarketManageReqAttrsRequest) Reset() { *m = MsgMarketManageReqAttrsRequest{} } @@ -1008,6 +1010,13 @@ func (m *MsgMarketManageReqAttrsRequest) GetAdministrator() string { return "" } +func (m *MsgMarketManageReqAttrsRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + func (m *MsgMarketManageReqAttrsRequest) GetCreateAskToAdd() []string { if m != nil { return m.CreateAskToAdd @@ -1495,96 +1504,96 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1413 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xcf, 0x6f, 0x1b, 0xc5, - 0x17, 0x8f, 0x93, 0x34, 0x8d, 0x5f, 0x9b, 0xb4, 0x9d, 0xba, 0x89, 0xbd, 0x6d, 0x6c, 0xd7, 0xfd, - 0x1e, 0xfa, 0x6d, 0xc9, 0xba, 0x29, 0xa2, 0x40, 0x84, 0x90, 0xe2, 0x40, 0xaa, 0x1e, 0x22, 0x2a, - 0x87, 0x0a, 0x09, 0x0e, 0xd6, 0xd8, 0x3b, 0x75, 0x46, 0xb1, 0x77, 0xd2, 0x9d, 0x49, 0x9a, 0x5e, - 0x11, 0x12, 0x27, 0x44, 0x8f, 0x5c, 0xb9, 0x22, 0x0e, 0x1c, 0xb8, 0xc0, 0x5f, 0x50, 0x71, 0xaa, - 0x38, 0x71, 0x02, 0xd4, 0x1e, 0xf8, 0x37, 0xd0, 0xfc, 0xf0, 0x7a, 0xd7, 0xfb, 0xd3, 0xa1, 0xa7, - 0x64, 0x77, 0x3e, 0xef, 0xf3, 0x79, 0x9f, 0xb7, 0xb3, 0x6f, 0xf6, 0x19, 0x6a, 0x87, 0x1e, 0x3b, - 0x26, 0x2e, 0x76, 0x7b, 0xa4, 0x49, 0x4e, 0x7a, 0xfb, 0xd8, 0xed, 0x93, 0xe6, 0xf1, 0x46, 0x53, - 0x9c, 0xd8, 0x87, 0x1e, 0x13, 0x0c, 0xad, 0x8c, 0x01, 0xf6, 0x08, 0x60, 0x1f, 0x6f, 0x58, 0xd5, - 0x1e, 0xe3, 0x43, 0xc6, 0x9b, 0x5d, 0xcc, 0x65, 0x40, 0x97, 0x08, 0xbc, 0xd1, 0xec, 0x31, 0xea, - 0xea, 0x38, 0x6b, 0xd5, 0xac, 0x0f, 0x79, 0x5f, 0xf2, 0x0d, 0x79, 0xdf, 0x2c, 0x54, 0xf4, 0x42, - 0x47, 0x5d, 0x35, 0xf5, 0x85, 0x59, 0x2a, 0xf5, 0x59, 0x9f, 0xe9, 0xfb, 0xf2, 0x3f, 0x73, 0xf7, - 0x46, 0x42, 0x8a, 0x43, 0xec, 0x1d, 0x10, 0x91, 0x01, 0x62, 0x9e, 0x43, 0x3c, 0x9e, 0x01, 0x3a, - 0xc4, 0x1e, 0x1e, 0x1a, 0x50, 0xe3, 0xd7, 0x02, 0x5c, 0xde, 0xe5, 0xfd, 0x6d, 0x8f, 0x60, 0x41, - 0xb6, 0xf8, 0x41, 0x9b, 0x3c, 0x39, 0x22, 0x5c, 0xa0, 0x6d, 0x28, 0x62, 0x7e, 0xd0, 0x51, 0x84, - 0xe5, 0x42, 0xbd, 0x70, 0xf3, 0xdc, 0xdd, 0xba, 0x1d, 0x5f, 0x1c, 0x7b, 0x8b, 0x1f, 0x7c, 0x22, - 0x71, 0xad, 0xf9, 0x17, 0x7f, 0xd6, 0x66, 0xda, 0x8b, 0xd8, 0x5c, 0xa3, 0xfb, 0x80, 0x14, 0x41, - 0xa7, 0x27, 0xe9, 0x29, 0x73, 0x3b, 0x8f, 0x09, 0x29, 0xcf, 0x2a, 0xb6, 0x8a, 0x6d, 0x8a, 0x21, - 0x4b, 0x6a, 0x9b, 0x92, 0xda, 0xdb, 0x8c, 0xba, 0xed, 0x8b, 0x2a, 0x68, 0xdb, 0xc4, 0xec, 0x10, - 0xb2, 0x79, 0xe5, 0xcb, 0x7f, 0x7e, 0xba, 0x75, 0xd1, 0x4f, 0xc8, 0xe6, 0x64, 0x30, 0x20, 0x5e, - 0x63, 0x03, 0x4a, 0xe1, 0xdc, 0xf9, 0x21, 0x73, 0x39, 0x41, 0x15, 0x58, 0xd4, 0xba, 0xd4, 0x51, - 0xb9, 0xcf, 0xb7, 0xcf, 0xaa, 0xeb, 0x07, 0x4e, 0xe3, 0x97, 0xa0, 0xdf, 0x16, 0x75, 0x02, 0x7e, - 0xbb, 0xd4, 0xc9, 0xe7, 0xb7, 0x45, 0x9d, 0x90, 0xdf, 0xae, 0xb9, 0x7e, 0x73, 0x7e, 0x4b, 0xd2, - 0xef, 0x05, 0x3f, 0x21, 0xbb, 0x7b, 0xf4, 0x6c, 0xc2, 0xae, 0x4a, 0x3d, 0xdb, 0xae, 0x0b, 0x57, - 0x64, 0x88, 0xb4, 0x30, 0x50, 0x39, 0x8e, 0xfc, 0xda, 0x70, 0x86, 0x3d, 0x75, 0x8d, 0xd7, 0x62, - 0xab, 0xfc, 0xfb, 0xcf, 0xeb, 0x25, 0x93, 0xe0, 0x96, 0xe3, 0x78, 0x84, 0xf3, 0x3d, 0xe1, 0x51, - 0xb7, 0xdf, 0xd6, 0xb0, 0x90, 0xc6, 0x6c, 0x48, 0x63, 0x13, 0x64, 0xb2, 0x1a, 0xd6, 0x28, 0xc3, - 0xca, 0xa4, 0x9e, 0x4e, 0xb2, 0x51, 0x02, 0xb4, 0xcb, 0xfb, 0x3b, 0x74, 0x30, 0x68, 0x51, 0x87, - 0x9b, 0x34, 0x1a, 0x57, 0xd4, 0xd3, 0x18, 0xdf, 0x8d, 0x80, 0xb7, 0xf8, 0x41, 0x0c, 0x58, 0xdf, - 0x35, 0x60, 0xad, 0xb9, 0xab, 0xde, 0x8f, 0x3d, 0x22, 0xc4, 0x80, 0x8c, 0x02, 0x2a, 0xb0, 0x1a, - 0x59, 0x31, 0x41, 0xdf, 0xce, 0x42, 0xd9, 0x5f, 0xfb, 0x8c, 0x8a, 0x7d, 0xc7, 0xc3, 0x4f, 0x47, - 0xc5, 0xf9, 0x10, 0x96, 0xb0, 0x33, 0xa4, 0x2e, 0xe5, 0xc2, 0xc3, 0x82, 0x65, 0x17, 0x29, 0x0c, - 0x47, 0x57, 0xa1, 0xa8, 0x5f, 0xd7, 0x51, 0xb5, 0x96, 0xda, 0x8b, 0xfa, 0xc6, 0x03, 0x07, 0xad, - 0x01, 0x08, 0xd6, 0xc1, 0x3a, 0xbe, 0x3c, 0x27, 0x99, 0xdb, 0x45, 0xc1, 0x0c, 0x21, 0xea, 0xc1, - 0x02, 0x1e, 0xb2, 0x23, 0x57, 0x94, 0xe7, 0xeb, 0x73, 0xa9, 0xfb, 0xa6, 0x75, 0x47, 0x6e, 0xbf, - 0x1f, 0xfe, 0xaa, 0xdd, 0xec, 0x53, 0xb1, 0x7f, 0xd4, 0xb5, 0x7b, 0x6c, 0x68, 0x3a, 0x8c, 0xf9, - 0xb3, 0xce, 0x9d, 0x83, 0xa6, 0x78, 0x76, 0x48, 0xb8, 0x0a, 0xe0, 0x6d, 0x43, 0xbd, 0x89, 0xe4, - 0x23, 0x0b, 0x27, 0xdd, 0xb8, 0x0a, 0x95, 0x98, 0x82, 0x98, 0x72, 0xd5, 0x60, 0xcd, 0x5f, 0x7c, - 0x74, 0xe8, 0x60, 0x41, 0x3e, 0x22, 0x02, 0xd3, 0x81, 0xff, 0x6c, 0xea, 0x50, 0x4d, 0x02, 0x24, - 0x52, 0x7c, 0xec, 0xe2, 0xee, 0x80, 0x38, 0xc9, 0x14, 0x3e, 0xc0, 0x50, 0x34, 0xa0, 0x3e, 0x81, - 0x78, 0xc4, 0x89, 0x17, 0x7e, 0xe6, 0x37, 0xe0, 0x7a, 0x0a, 0xc6, 0x10, 0x05, 0x41, 0xbb, 0xd8, - 0xc5, 0x7d, 0xf2, 0x90, 0x78, 0x43, 0xca, 0x39, 0x65, 0xae, 0x6f, 0xe9, 0x7f, 0xd0, 0x48, 0x03, - 0x19, 0xaa, 0x1f, 0x67, 0x03, 0x69, 0x6b, 0x58, 0x9b, 0x3c, 0xd9, 0x12, 0xc2, 0xe3, 0x6f, 0x6a, - 0x3b, 0xfd, 0x1f, 0x2e, 0xa9, 0x86, 0x42, 0x3a, 0xb2, 0x03, 0xea, 0xcd, 0x53, 0x9e, 0xad, 0xcf, - 0xdd, 0x2c, 0xb6, 0x97, 0x7b, 0xa3, 0xe6, 0xf7, 0xa9, 0xdc, 0x41, 0xa8, 0x09, 0xa5, 0x30, 0xd4, - 0x23, 0x43, 0x76, 0x4c, 0xca, 0x73, 0x0a, 0x7d, 0x29, 0x80, 0x6e, 0xab, 0x85, 0x00, 0xb7, 0xec, - 0x36, 0x86, 0x7b, 0x3e, 0xc8, 0xdd, 0xa2, 0xce, 0x24, 0xb7, 0x81, 0x1a, 0xee, 0x33, 0x41, 0x6e, - 0x85, 0xd6, 0xdc, 0xb1, 0xbb, 0xec, 0x3a, 0xd4, 0x12, 0xab, 0x65, 0x2a, 0xfa, 0x7d, 0x41, 0xed, - 0xc4, 0xfb, 0xec, 0x58, 0xb7, 0x3a, 0x0d, 0x1e, 0x15, 0xf3, 0x1e, 0x14, 0xf1, 0x91, 0xd8, 0x67, - 0x1e, 0x15, 0xcf, 0x32, 0x0b, 0x39, 0x86, 0xa2, 0x0f, 0x60, 0x41, 0xbf, 0x82, 0xa6, 0x1f, 0x57, - 0x93, 0xba, 0xbb, 0x96, 0x33, 0xbd, 0xdd, 0xc4, 0x6c, 0x2e, 0x4b, 0x2b, 0x63, 0xb6, 0xc6, 0x35, - 0xb0, 0xe2, 0x52, 0x34, 0x0e, 0x7e, 0x03, 0xd5, 0x78, 0xee, 0xb3, 0x63, 0x6d, 0x71, 0x87, 0x10, - 0xfe, 0x5f, 0xf3, 0x4f, 0xed, 0x29, 0x8f, 0x60, 0x15, 0x3b, 0x8e, 0x3c, 0x6d, 0x3a, 0x81, 0xc7, - 0xff, 0x78, 0x80, 0x85, 0x7a, 0xf2, 0xa9, 0x5d, 0x44, 0x1b, 0xbd, 0x8c, 0x1d, 0x67, 0x87, 0x10, - 0xff, 0x2c, 0xdd, 0x19, 0x60, 0x81, 0xbe, 0x00, 0x4b, 0x3f, 0xe3, 0x58, 0xe6, 0xf9, 0x7c, 0xcc, - 0x2b, 0x9a, 0x22, 0x42, 0x1e, 0xcd, 0x59, 0x6e, 0x2b, 0xc5, 0x7c, 0xe6, 0x14, 0x39, 0xb7, 0xa8, - 0x93, 0x9c, 0xb3, 0xcf, 0xbc, 0x70, 0xba, 0x9c, 0x47, 0xe4, 0x3d, 0xa8, 0x8e, 0x72, 0xd6, 0x9f, - 0x20, 0x1d, 0xae, 0x1a, 0xcb, 0x90, 0xb8, 0x42, 0x0b, 0x9c, 0xcd, 0x27, 0x60, 0xe9, 0xd4, 0xf7, - 0x14, 0xc9, 0x9e, 0xcf, 0xa1, 0x44, 0x28, 0x5c, 0x0f, 0x38, 0x48, 0xd0, 0x59, 0xcc, 0xa7, 0xb3, - 0xe6, 0x1b, 0x89, 0x95, 0x72, 0xa1, 0x9e, 0xec, 0xc7, 0x93, 0x9f, 0x23, 0xbc, 0x5c, 0x54, 0x4a, - 0x89, 0x1f, 0x43, 0x3b, 0x84, 0xb4, 0x25, 0xd0, 0x08, 0x5e, 0x8b, 0x37, 0xa6, 0x20, 0x1c, 0x09, - 0xb8, 0x91, 0x6a, 0xcd, 0x48, 0xc2, 0x54, 0x92, 0xb5, 0x44, 0x8f, 0x46, 0x15, 0xc3, 0xda, 0xc8, - 0xa5, 0xfa, 0x90, 0x8a, 0x14, 0xf3, 0x5c, 0xbe, 0x62, 0x56, 0xb4, 0xb7, 0x96, 0xe4, 0x98, 0x28, - 0x64, 0x1f, 0xea, 0x01, 0x63, 0xf1, 0x2a, 0xe7, 0xf3, 0xa9, 0x5c, 0xf3, 0xed, 0xc4, 0x09, 0x0d, - 0xa0, 0x96, 0xe8, 0xc5, 0x54, 0x6f, 0x69, 0xaa, 0xea, 0x5d, 0x8d, 0x35, 0x65, 0x2a, 0xe7, 0x41, - 0x23, 0xcd, 0x96, 0x11, 0x5c, 0x9e, 0x4a, 0xb0, 0x9a, 0xe4, 0x4f, 0x6b, 0x46, 0x5a, 0xad, 0xa5, - 0x3e, 0xd4, 0x26, 0x7a, 0x69, 0xe4, 0xa8, 0xd0, 0x47, 0xfd, 0x43, 0x35, 0xda, 0xbc, 0x81, 0xa3, - 0x42, 0xcf, 0x48, 0x59, 0x47, 0x85, 0x96, 0x1b, 0x1d, 0x15, 0x3a, 0x26, 0xf9, 0xa8, 0x08, 0xa7, - 0xa8, 0x1d, 0xdc, 0xfd, 0x6e, 0x19, 0xe6, 0x76, 0x79, 0x1f, 0x3d, 0x86, 0xa2, 0xdf, 0x1e, 0xd1, - 0xed, 0xc4, 0xb3, 0x29, 0x3a, 0xa9, 0x59, 0x6f, 0xe5, 0x03, 0x9b, 0x59, 0xc1, 0xd7, 0x69, 0x51, - 0x27, 0x87, 0xce, 0x78, 0x42, 0xca, 0xa1, 0x13, 0x9c, 0x49, 0x06, 0x70, 0x2e, 0x30, 0x05, 0xa0, - 0xf5, 0xb4, 0xe0, 0xc8, 0x74, 0x62, 0xd9, 0x79, 0xe1, 0x46, 0xad, 0x07, 0x8b, 0xa3, 0x19, 0x02, - 0xdd, 0x4a, 0x89, 0x9d, 0x18, 0x3f, 0xac, 0xdb, 0xb9, 0xb0, 0x61, 0x11, 0x39, 0x7b, 0x64, 0x8a, - 0x04, 0xc6, 0x96, 0x4c, 0x91, 0xe0, 0x30, 0x83, 0x18, 0x9c, 0x0f, 0xce, 0x2b, 0x28, 0xad, 0x12, - 0x31, 0x23, 0x8f, 0xd5, 0xcc, 0x8d, 0x37, 0x82, 0x47, 0xb0, 0x1c, 0xfe, 0xe6, 0x47, 0x77, 0x32, - 0x29, 0x26, 0xe6, 0x25, 0x6b, 0x63, 0x8a, 0x08, 0x23, 0xfb, 0x95, 0x9c, 0xc3, 0xa3, 0xd3, 0x02, - 0x7a, 0x27, 0x93, 0x2a, 0x6e, 0xfc, 0xb0, 0xee, 0x4d, 0x1b, 0x96, 0x90, 0x86, 0x99, 0x38, 0x72, - 0xa7, 0x11, 0x1e, 0x61, 0x72, 0xa7, 0x31, 0x31, 0xd8, 0xa0, 0x6f, 0x0a, 0xb0, 0x12, 0x3f, 0xb2, - 0xa0, 0xf7, 0x72, 0x52, 0x46, 0x26, 0x21, 0xeb, 0xfd, 0x53, 0x44, 0x9a, 0x7c, 0x9e, 0x17, 0x60, - 0x35, 0x61, 0xf0, 0x41, 0xd9, 0xb4, 0x49, 0x13, 0x95, 0xb5, 0x79, 0x9a, 0x50, 0x93, 0xd2, 0xd7, - 0x05, 0x28, 0xc5, 0x8d, 0x0d, 0xe8, 0x5e, 0x4e, 0xd2, 0x89, 0xa9, 0xcc, 0x7a, 0x77, 0xea, 0x38, - 0x93, 0xc9, 0x09, 0x5c, 0x98, 0xf8, 0xf0, 0x47, 0x69, 0x2f, 0x40, 0xfc, 0x1c, 0x63, 0xdd, 0x9d, - 0x26, 0xc4, 0x28, 0x7b, 0xb0, 0x14, 0x3a, 0x07, 0x51, 0x33, 0x9d, 0x24, 0x32, 0x7d, 0x58, 0x77, - 0xf2, 0x07, 0x84, 0xdc, 0x06, 0xcf, 0xae, 0x2c, 0xb7, 0x31, 0x47, 0x71, 0x96, 0xdb, 0xb8, 0xa3, - 0xb1, 0x45, 0x5e, 0xbc, 0xaa, 0x16, 0x5e, 0xbe, 0xaa, 0x16, 0xfe, 0x7e, 0x55, 0x2d, 0x3c, 0x7f, - 0x5d, 0x9d, 0x79, 0xf9, 0xba, 0x3a, 0xf3, 0xc7, 0xeb, 0xea, 0x0c, 0x54, 0x28, 0x4b, 0xe0, 0x7b, - 0x58, 0xf8, 0xdc, 0x0e, 0xfc, 0x1a, 0x32, 0x06, 0xad, 0x53, 0x16, 0xb8, 0x6a, 0x9e, 0xf8, 0xbf, - 0x88, 0x76, 0x17, 0xd4, 0x0f, 0xa1, 0x6f, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x2e, 0x02, 0x18, - 0x79, 0x1c, 0x16, 0x00, 0x00, + // 1412 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xcf, 0x6f, 0x1b, 0x45, + 0x18, 0x8d, 0x93, 0x34, 0x8d, 0xbf, 0x36, 0x69, 0x3b, 0x75, 0x13, 0x7b, 0xdb, 0xd8, 0xae, 0xcb, + 0xa1, 0xb4, 0x64, 0xdd, 0x14, 0x51, 0x20, 0x42, 0x48, 0x71, 0x20, 0x55, 0x0f, 0x11, 0xd5, 0x86, + 0x0a, 0x09, 0x0e, 0xd6, 0xd8, 0x3b, 0x75, 0x46, 0xb1, 0x77, 0xd2, 0x9d, 0x49, 0x9a, 0x5e, 0x11, + 0x12, 0x27, 0x44, 0x8f, 0x5c, 0xb9, 0x72, 0xe2, 0xc0, 0x05, 0xfe, 0x82, 0xc2, 0xa9, 0xe2, 0xc4, + 0x09, 0x50, 0x73, 0xe0, 0xdf, 0x40, 0xf3, 0xc3, 0x9b, 0x5d, 0x7b, 0x7f, 0x39, 0x54, 0x9c, 0xda, + 0xdd, 0x7d, 0xdf, 0x7b, 0xdf, 0xfb, 0x76, 0x32, 0xb3, 0xcf, 0x50, 0xdb, 0xf7, 0xd9, 0x21, 0xf1, + 0xb0, 0xd7, 0x25, 0x4d, 0x72, 0xd4, 0xdd, 0xc5, 0x5e, 0x8f, 0x34, 0x0f, 0xd7, 0x9a, 0xe2, 0xc8, + 0xde, 0xf7, 0x99, 0x60, 0x68, 0xe9, 0x04, 0x60, 0x0f, 0x01, 0xf6, 0xe1, 0x9a, 0x55, 0xed, 0x32, + 0x3e, 0x60, 0xbc, 0xd9, 0xc1, 0x5c, 0x16, 0x74, 0x88, 0xc0, 0x6b, 0xcd, 0x2e, 0xa3, 0x9e, 0xae, + 0xb3, 0x96, 0xcd, 0xf3, 0x01, 0xef, 0x49, 0xbe, 0x01, 0xef, 0x99, 0x07, 0x15, 0xfd, 0xa0, 0xad, + 0xae, 0x9a, 0xfa, 0xc2, 0x3c, 0x2a, 0xf5, 0x58, 0x8f, 0xe9, 0xfb, 0xf2, 0x7f, 0xe6, 0xee, 0x8d, + 0x84, 0x16, 0x07, 0xd8, 0xdf, 0x23, 0x22, 0x03, 0xc4, 0x7c, 0x97, 0xf8, 0x3c, 0x03, 0xb4, 0x8f, + 0x7d, 0x3c, 0x30, 0xa0, 0xc6, 0x2f, 0x05, 0xb8, 0xbc, 0xcd, 0x7b, 0x9b, 0x3e, 0xc1, 0x82, 0x6c, + 0xf0, 0x3d, 0x87, 0x3c, 0x39, 0x20, 0x5c, 0xa0, 0x4d, 0x28, 0x62, 0xbe, 0xd7, 0x56, 0x84, 0xe5, + 0x42, 0xbd, 0x70, 0xf3, 0xdc, 0xdd, 0xba, 0x1d, 0x3f, 0x1c, 0x7b, 0x83, 0xef, 0x7d, 0x22, 0x71, + 0xad, 0xd9, 0x17, 0x7f, 0xd6, 0xa6, 0x9c, 0x79, 0x6c, 0xae, 0xd1, 0x7d, 0x40, 0x8a, 0xa0, 0xdd, + 0x95, 0xf4, 0x94, 0x79, 0xed, 0xc7, 0x84, 0x94, 0xa7, 0x15, 0x5b, 0xc5, 0x36, 0xc3, 0x90, 0x23, + 0xb5, 0xcd, 0x48, 0xed, 0x4d, 0x46, 0x3d, 0xe7, 0xa2, 0x2a, 0xda, 0x34, 0x35, 0x5b, 0x84, 0xac, + 0x5f, 0xf9, 0xf2, 0x9f, 0x1f, 0x6f, 0x5d, 0x0c, 0x1a, 0xb2, 0x39, 0xe9, 0xf7, 0x89, 0xdf, 0x58, + 0x83, 0x52, 0xb4, 0x77, 0xbe, 0xcf, 0x3c, 0x4e, 0x50, 0x05, 0xe6, 0xb5, 0x2e, 0x75, 0x55, 0xef, + 0xb3, 0xce, 0x59, 0x75, 0xfd, 0xc0, 0x6d, 0xfc, 0x1c, 0xf6, 0xdb, 0xa2, 0x6e, 0xc8, 0x6f, 0x87, + 0xba, 0xf9, 0xfc, 0xb6, 0xa8, 0x1b, 0xf1, 0xdb, 0x31, 0xd7, 0xaf, 0xcf, 0x6f, 0x49, 0xfa, 0xbd, + 0x10, 0x34, 0x64, 0x77, 0x0e, 0x9e, 0x8d, 0xd8, 0x55, 0xad, 0x67, 0xdb, 0xf5, 0xe0, 0x8a, 0x2c, + 0x91, 0x16, 0xfa, 0xaa, 0xc7, 0xa1, 0x5f, 0x1b, 0xce, 0xb0, 0xa7, 0x9e, 0xf1, 0x5a, 0x6c, 0x95, + 0x7f, 0xff, 0x69, 0xb5, 0x64, 0x1a, 0xdc, 0x70, 0x5d, 0x9f, 0x70, 0xbe, 0x23, 0x7c, 0xea, 0xf5, + 0x1c, 0x0d, 0x8b, 0x68, 0x4c, 0x47, 0x34, 0xd6, 0x41, 0x36, 0xab, 0x61, 0x8d, 0x32, 0x2c, 0x8d, + 0xea, 0xe9, 0x26, 0x1b, 0x25, 0x40, 0xdb, 0xbc, 0xb7, 0x45, 0xfb, 0xfd, 0x16, 0x75, 0xb9, 0x69, + 0xa3, 0x71, 0x45, 0xbd, 0x8d, 0x93, 0xbb, 0x63, 0xe0, 0x0d, 0xbe, 0x17, 0x03, 0xd6, 0x77, 0x0d, + 0x58, 0x6b, 0x6e, 0xab, 0xbf, 0x8f, 0x1d, 0x22, 0x44, 0x9f, 0x0c, 0x0b, 0x2a, 0xb0, 0x3c, 0xf6, + 0xc4, 0x14, 0x7d, 0x3b, 0x0d, 0xe5, 0xe0, 0xd9, 0x67, 0x54, 0xec, 0xba, 0x3e, 0x7e, 0x3a, 0x1c, + 0xce, 0x87, 0xb0, 0x80, 0xdd, 0x01, 0xf5, 0x28, 0x17, 0x3e, 0x16, 0x2c, 0x7b, 0x48, 0x51, 0x38, + 0xba, 0x0a, 0x45, 0xfd, 0xe7, 0x3a, 0x9c, 0xd6, 0x82, 0x33, 0xaf, 0x6f, 0x3c, 0x70, 0xd1, 0x0a, + 0x80, 0x60, 0x6d, 0xac, 0xeb, 0xcb, 0x33, 0x92, 0xd9, 0x29, 0x0a, 0x66, 0x08, 0x51, 0x17, 0xe6, + 0xf0, 0x80, 0x1d, 0x78, 0xa2, 0x3c, 0x5b, 0x9f, 0x49, 0x5d, 0x37, 0xad, 0x3b, 0x72, 0xf9, 0xfd, + 0xf0, 0x57, 0xed, 0x66, 0x8f, 0x8a, 0xdd, 0x83, 0x8e, 0xdd, 0x65, 0x03, 0xb3, 0xc3, 0x98, 0x7f, + 0x56, 0xb9, 0xbb, 0xd7, 0x14, 0xcf, 0xf6, 0x09, 0x57, 0x05, 0xdc, 0x31, 0xd4, 0xeb, 0x48, 0xbe, + 0xb2, 0x68, 0xd3, 0x8d, 0xab, 0x50, 0x89, 0x19, 0x88, 0x19, 0x57, 0x0d, 0x56, 0x82, 0x87, 0x8f, + 0xf6, 0x5d, 0x2c, 0xc8, 0x47, 0x44, 0x60, 0xda, 0x0f, 0xde, 0x4d, 0x1d, 0xaa, 0x49, 0x80, 0x44, + 0x8a, 0x8f, 0x3d, 0xdc, 0xe9, 0x13, 0x37, 0x99, 0x22, 0x00, 0x18, 0x8a, 0x06, 0xd4, 0x47, 0x10, + 0x8f, 0x38, 0xf1, 0xa3, 0xef, 0xfc, 0x06, 0x5c, 0x4f, 0xc1, 0x18, 0xa2, 0x30, 0x68, 0x1b, 0x7b, + 0xb8, 0x47, 0x1e, 0x12, 0x7f, 0x40, 0x39, 0xa7, 0xcc, 0x0b, 0x2c, 0xbd, 0x01, 0x8d, 0x34, 0x90, + 0xa1, 0xfa, 0x75, 0x3a, 0xd4, 0xb6, 0x86, 0x39, 0xe4, 0xc9, 0x86, 0x10, 0x3e, 0xff, 0x5f, 0x96, + 0xd3, 0x9b, 0x70, 0x49, 0xed, 0x36, 0xa4, 0x2d, 0xb7, 0x47, 0xbd, 0xb2, 0xca, 0x33, 0xf5, 0x99, + 0x9b, 0x45, 0x67, 0xb1, 0x3b, 0xdc, 0x19, 0x3f, 0x95, 0xcb, 0x0b, 0x35, 0xa1, 0x14, 0x85, 0xfa, + 0x64, 0xc0, 0x0e, 0x89, 0x5a, 0x68, 0x45, 0xe7, 0x52, 0x08, 0xed, 0xa8, 0x07, 0x21, 0x6e, 0xb9, + 0x15, 0x19, 0xee, 0x33, 0x61, 0xee, 0x16, 0x75, 0x47, 0xb9, 0x0d, 0xd4, 0x70, 0xcf, 0x85, 0xb9, + 0x15, 0x5a, 0x73, 0xc7, 0x2e, 0xc1, 0xeb, 0x50, 0x4b, 0x1c, 0xa5, 0x19, 0xf7, 0xf7, 0x05, 0xb5, + 0x4c, 0xef, 0xb3, 0x43, 0xbd, 0x0f, 0x6a, 0xf0, 0x70, 0xd2, 0xf7, 0xa0, 0x88, 0x0f, 0xc4, 0x2e, + 0xf3, 0xa9, 0x78, 0x96, 0x39, 0xe5, 0x13, 0x28, 0xfa, 0x00, 0xe6, 0xf4, 0x40, 0xcd, 0x66, 0x5d, + 0x4d, 0xda, 0xfa, 0xb5, 0x9c, 0xd9, 0xf8, 0x4d, 0xcd, 0xfa, 0xa2, 0xb4, 0x72, 0xc2, 0xd6, 0xb8, + 0x06, 0x56, 0x5c, 0x8b, 0xc6, 0xc1, 0x6f, 0xa0, 0x76, 0xa5, 0xfb, 0xec, 0x50, 0x5b, 0xdc, 0x22, + 0x84, 0xff, 0xd7, 0xfe, 0x53, 0x57, 0xc8, 0x23, 0x58, 0xc6, 0xae, 0x2b, 0x8f, 0xa2, 0x76, 0xe8, + 0xf5, 0x3f, 0xee, 0x63, 0xa1, 0xd6, 0x49, 0xea, 0x16, 0xa3, 0x8d, 0x5e, 0xc6, 0xae, 0xbb, 0x45, + 0x48, 0x70, 0xd0, 0x6e, 0xf5, 0xb1, 0x40, 0x5f, 0x80, 0xa5, 0xdf, 0x71, 0x2c, 0xf3, 0x6c, 0x3e, + 0xe6, 0x25, 0x4d, 0x31, 0x46, 0x3e, 0xde, 0xb3, 0x5c, 0x56, 0x8a, 0xf9, 0xcc, 0x29, 0x7a, 0x6e, + 0x51, 0x37, 0xb9, 0xe7, 0x80, 0x79, 0xee, 0x74, 0x3d, 0x0f, 0xc9, 0xbb, 0x50, 0x1d, 0xf6, 0xac, + 0xbf, 0x4f, 0xda, 0x5c, 0xed, 0x3a, 0x03, 0xe2, 0x09, 0x2d, 0x70, 0x36, 0x9f, 0x80, 0xa5, 0x5b, + 0xdf, 0x51, 0x24, 0x3b, 0x01, 0x87, 0x12, 0xa1, 0x70, 0x3d, 0xe4, 0x20, 0x41, 0x67, 0x3e, 0x9f, + 0xce, 0x4a, 0x60, 0x24, 0x56, 0xca, 0x83, 0x7a, 0xb2, 0x1f, 0x5f, 0x7e, 0xab, 0xf0, 0x72, 0x51, + 0x29, 0x25, 0x7e, 0x29, 0x6d, 0x11, 0xe2, 0x48, 0xa0, 0x11, 0xbc, 0x16, 0x6f, 0x4c, 0x41, 0x38, + 0x12, 0x70, 0x23, 0xd5, 0x9a, 0x91, 0x84, 0x89, 0x24, 0x6b, 0x89, 0x1e, 0x8d, 0x2a, 0x86, 0x95, + 0xa1, 0x4b, 0xf5, 0x95, 0x35, 0x36, 0xcc, 0x73, 0xf9, 0x86, 0x59, 0xd1, 0xde, 0x5a, 0x92, 0x63, + 0x64, 0x90, 0x3d, 0xa8, 0x87, 0x8c, 0xc5, 0xab, 0x9c, 0xcf, 0xa7, 0x72, 0x2d, 0xb0, 0x13, 0x27, + 0xd4, 0x87, 0x5a, 0xa2, 0x17, 0x33, 0xbd, 0x85, 0x89, 0xa6, 0x77, 0x35, 0xd6, 0x94, 0x99, 0x9c, + 0x0f, 0x8d, 0x34, 0x5b, 0x46, 0x70, 0x71, 0x22, 0xc1, 0x6a, 0x92, 0x3f, 0xad, 0x39, 0xb6, 0xd5, + 0x5a, 0xea, 0x2b, 0x6e, 0x64, 0x2f, 0x1d, 0x3b, 0x2a, 0xf4, 0x77, 0xc0, 0x43, 0x95, 0x7b, 0x5e, + 0xc3, 0x51, 0xa1, 0x03, 0x54, 0xd6, 0x51, 0xa1, 0xe5, 0x86, 0x47, 0x85, 0xae, 0x49, 0x3e, 0x2a, + 0xa2, 0x2d, 0x6a, 0x07, 0x77, 0xbf, 0x5b, 0x84, 0x99, 0x6d, 0xde, 0x43, 0x8f, 0xa1, 0x18, 0x6c, + 0x8f, 0xe8, 0x76, 0xe2, 0xd9, 0x34, 0x1e, 0xe3, 0xac, 0xb7, 0xf2, 0x81, 0x4d, 0x90, 0x08, 0x74, + 0x5a, 0xd4, 0xcd, 0xa1, 0x73, 0x12, 0x9f, 0x72, 0xe8, 0x84, 0x03, 0x4b, 0x1f, 0xce, 0x85, 0x22, + 0x02, 0x5a, 0x4d, 0x2b, 0x1e, 0x8b, 0x2e, 0x96, 0x9d, 0x17, 0x6e, 0xd4, 0xba, 0x30, 0x3f, 0x0c, + 0x18, 0xe8, 0x56, 0x4a, 0xed, 0x48, 0x36, 0xb1, 0x6e, 0xe7, 0xc2, 0x46, 0x45, 0x64, 0x30, 0xc9, + 0x14, 0x09, 0x65, 0x9a, 0x4c, 0x91, 0x70, 0xd2, 0x41, 0x0c, 0xce, 0x87, 0xc3, 0x0c, 0x4a, 0x9b, + 0x44, 0x4c, 0x1e, 0xb2, 0x9a, 0xb9, 0xf1, 0x46, 0xf0, 0x00, 0x16, 0xa3, 0x81, 0x00, 0xdd, 0xc9, + 0xa4, 0x18, 0x09, 0x53, 0xd6, 0xda, 0x04, 0x15, 0x46, 0xf6, 0x2b, 0x19, 0xd2, 0xc7, 0xa3, 0x04, + 0x7a, 0x27, 0x93, 0x2a, 0x2e, 0x9b, 0x58, 0xf7, 0x26, 0x2d, 0x4b, 0x68, 0xc3, 0xc4, 0x91, 0xdc, + 0x6d, 0x44, 0xf3, 0x4d, 0xee, 0x36, 0x46, 0x52, 0x0f, 0xfa, 0xa6, 0x00, 0x4b, 0xf1, 0x79, 0x06, + 0xbd, 0x97, 0x93, 0x72, 0x2c, 0x26, 0x59, 0xef, 0x9f, 0xa2, 0xd2, 0xf4, 0xf3, 0xbc, 0x00, 0xcb, + 0x09, 0xa9, 0x08, 0x65, 0xd3, 0x26, 0xc5, 0x2d, 0x6b, 0xfd, 0x34, 0xa5, 0xa6, 0xa5, 0xaf, 0x0b, + 0x50, 0x8a, 0x8b, 0x0d, 0xe8, 0x5e, 0x4e, 0xd2, 0x91, 0xc8, 0x66, 0xbd, 0x3b, 0x71, 0x9d, 0xe9, + 0xe4, 0x08, 0x2e, 0x8c, 0x7c, 0xf8, 0xa3, 0xb4, 0x3f, 0x80, 0xf8, 0x1c, 0x63, 0xdd, 0x9d, 0xa4, + 0xc4, 0x28, 0xfb, 0xb0, 0x10, 0x39, 0x07, 0x51, 0x33, 0x9d, 0x64, 0x2c, 0x7d, 0x58, 0x77, 0xf2, + 0x17, 0x44, 0xdc, 0x86, 0xcf, 0xae, 0x2c, 0xb7, 0x31, 0x47, 0x71, 0x96, 0xdb, 0xb8, 0xa3, 0xb1, + 0x45, 0x5e, 0xbc, 0xaa, 0x16, 0x5e, 0xbe, 0xaa, 0x16, 0xfe, 0x7e, 0x55, 0x2d, 0x3c, 0x3f, 0xae, + 0x4e, 0xbd, 0x3c, 0xae, 0x4e, 0xfd, 0x71, 0x5c, 0x9d, 0x82, 0x0a, 0x65, 0x09, 0x7c, 0x0f, 0x0b, + 0x9f, 0xdb, 0xa1, 0x9f, 0x4a, 0x4e, 0x40, 0xab, 0x94, 0x85, 0xae, 0x9a, 0x47, 0xc1, 0xcf, 0xa5, + 0x9d, 0x39, 0xf5, 0x2b, 0xe9, 0xdb, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xaa, 0xfa, 0xf3, 0x45, + 0x39, 0x16, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2832,7 +2841,7 @@ func (m *MsgMarketManageReqAttrsRequest) MarshalToSizedBuffer(dAtA []byte) (int, copy(dAtA[i:], m.CreateBidToRemove[iNdEx]) i = encodeVarintTx(dAtA, i, uint64(len(m.CreateBidToRemove[iNdEx]))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x32 } } if len(m.CreateBidToAdd) > 0 { @@ -2841,7 +2850,7 @@ func (m *MsgMarketManageReqAttrsRequest) MarshalToSizedBuffer(dAtA []byte) (int, copy(dAtA[i:], m.CreateBidToAdd[iNdEx]) i = encodeVarintTx(dAtA, i, uint64(len(m.CreateBidToAdd[iNdEx]))) i-- - dAtA[i] = 0x22 + dAtA[i] = 0x2a } } if len(m.CreateAskToRemove) > 0 { @@ -2850,7 +2859,7 @@ func (m *MsgMarketManageReqAttrsRequest) MarshalToSizedBuffer(dAtA []byte) (int, copy(dAtA[i:], m.CreateAskToRemove[iNdEx]) i = encodeVarintTx(dAtA, i, uint64(len(m.CreateAskToRemove[iNdEx]))) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x22 } } if len(m.CreateAskToAdd) > 0 { @@ -2859,9 +2868,14 @@ func (m *MsgMarketManageReqAttrsRequest) MarshalToSizedBuffer(dAtA []byte) (int, copy(dAtA[i:], m.CreateAskToAdd[iNdEx]) i = encodeVarintTx(dAtA, i, uint64(len(m.CreateAskToAdd[iNdEx]))) i-- - dAtA[i] = 0x12 + dAtA[i] = 0x1a } } + if m.MarketId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } if len(m.Administrator) > 0 { i -= len(m.Administrator) copy(dAtA[i:], m.Administrator) @@ -3508,6 +3522,9 @@ func (m *MsgMarketManageReqAttrsRequest) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } + if m.MarketId != 0 { + n += 1 + sovTx(uint64(m.MarketId)) + } if len(m.CreateAskToAdd) > 0 { for _, s := range m.CreateAskToAdd { l = len(s) @@ -5201,6 +5218,25 @@ func (m *MsgMarketManageReqAttrsRequest) Unmarshal(dAtA []byte) error { m.Administrator = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CreateAskToAdd", wireType) } @@ -5232,7 +5268,7 @@ func (m *MsgMarketManageReqAttrsRequest) Unmarshal(dAtA []byte) error { } m.CreateAskToAdd = append(m.CreateAskToAdd, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 3: + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CreateAskToRemove", wireType) } @@ -5264,7 +5300,7 @@ func (m *MsgMarketManageReqAttrsRequest) Unmarshal(dAtA []byte) error { } m.CreateAskToRemove = append(m.CreateAskToRemove, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 4: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CreateBidToAdd", wireType) } @@ -5296,7 +5332,7 @@ func (m *MsgMarketManageReqAttrsRequest) Unmarshal(dAtA []byte) error { } m.CreateBidToAdd = append(m.CreateBidToAdd, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 5: + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CreateBidToRemove", wireType) } From a6d08052308cb40074f3f3c52ca6cd5a0e8463b1 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 15 Sep 2023 16:12:44 -0600 Subject: [PATCH 115/309] [1658]: Update MsgMarketManageReqAttrsRequest validation to make sure the market id isn't zero. Fix some other unit tests that broke a while back when I took out some colons from the error messages. --- x/exchange/msg.go | 4 +++ x/exchange/msg_test.go | 59 ++++++++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 7d0e5d7f2d..0ac9e591c9 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -180,6 +180,10 @@ func (m MsgMarketManageReqAttrsRequest) ValidateBasic() error { errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Administrator, err)) } + if m.MarketId == 0 { + errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) + } + if m.HasUpdates() { errs = append(errs, ValidateAddRemoveReqAttrs("create-ask", m.CreateAskToAdd, m.CreateAskToRemove), diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 109c40c86b..e701ab4379 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -49,7 +49,9 @@ func TestAllMsgsGetSigners(t *testing.T) { // TODO[1658]: Add MsgMarketUpdateEnabledRequest once it's actually been defined. // TODO[1658]: Add MsgMarketUpdateUserSettleRequest once it's actually been defined. // TODO[1658]: Add MsgMarketManagePermissionsRequest once it's actually been defined. - // TODO[1658]: Add MsgMarketManageReqAttrsRequest once it's actually been defined. + func(signer string) sdk.Msg { + return &MsgMarketManageReqAttrsRequest{Administrator: signer} + }, func(signer string) sdk.Msg { return &MsgGovCreateMarketRequest{Authority: signer} }, @@ -423,6 +425,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { name: "no admin", msg: MsgMarketManageReqAttrsRequest{ Administrator: "", + MarketId: 1, CreateAskToAdd: []string{"abc"}, }, expErr: []string{"invalid administrator", "empty address string is not allowed"}, @@ -431,19 +434,32 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { name: "bad admin", msg: MsgMarketManageReqAttrsRequest{ Administrator: "not1valid", + MarketId: 1, CreateAskToAdd: []string{"abc"}, }, expErr: []string{"invalid administrator", "decoding bech32 failed"}, }, { - name: "no updates", - msg: MsgMarketManageReqAttrsRequest{Administrator: goodAdmin}, + name: "market id zero", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: goodAdmin, + CreateAskToAdd: []string{"abc"}, + }, + expErr: []string{"invalid market id: cannot be zero"}, + }, + { + name: "no updates", + msg: MsgMarketManageReqAttrsRequest{ + Administrator: goodAdmin, + MarketId: 1, + }, expErr: []string{"no updates"}, }, { name: "invalid create ask to add entry", msg: MsgMarketManageReqAttrsRequest{ Administrator: goodAdmin, + MarketId: 1, CreateAskToAdd: []string{"in-valid-attr"}, }, expErr: []string{"invalid create-ask to add required attribute \"in-valid-attr\""}, @@ -452,6 +468,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { name: "invalid create ask to remove entry", msg: MsgMarketManageReqAttrsRequest{ Administrator: goodAdmin, + MarketId: 1, CreateAskToRemove: []string{"in-valid-attr"}, }, }, @@ -459,6 +476,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { name: "invalid create bid to add entry", msg: MsgMarketManageReqAttrsRequest{ Administrator: goodAdmin, + MarketId: 1, CreateBidToAdd: []string{"in-valid-attr"}, }, expErr: []string{"invalid create-bid to add required attribute \"in-valid-attr\""}, @@ -467,6 +485,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { name: "invalid create bid to remove entry", msg: MsgMarketManageReqAttrsRequest{ Administrator: goodAdmin, + MarketId: 1, CreateBidToRemove: []string{"in-valid-attr"}, }, }, @@ -474,6 +493,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { name: "add and remove same create ask entry", msg: MsgMarketManageReqAttrsRequest{ Administrator: goodAdmin, + MarketId: 1, CreateAskToAdd: []string{"abc", "def", "ghi"}, CreateAskToRemove: []string{"jkl", "abc"}, }, @@ -483,6 +503,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { name: "add and remove same create bid entry", msg: MsgMarketManageReqAttrsRequest{ Administrator: goodAdmin, + MarketId: 1, CreateBidToAdd: []string{"abc", "def", "ghi"}, CreateBidToRemove: []string{"jkl", "abc"}, }, @@ -492,6 +513,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { name: "add to create-ask the same as remove from create-bid", msg: MsgMarketManageReqAttrsRequest{ Administrator: goodAdmin, + MarketId: 1, CreateAskToAdd: []string{"abc", "def", "ghi"}, CreateBidToRemove: []string{"jkl", "abc"}, }, @@ -500,6 +522,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { name: "add to create-bid the same as remove from create-ask", msg: MsgMarketManageReqAttrsRequest{ Administrator: goodAdmin, + MarketId: 1, CreateBidToAdd: []string{"abc", "def", "ghi"}, CreateAskToRemove: []string{"jkl", "abc"}, }, @@ -508,6 +531,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { name: "add one to and remove one from each", msg: MsgMarketManageReqAttrsRequest{ Administrator: goodAdmin, + MarketId: 1, CreateAskToAdd: []string{"to-add.ask"}, CreateAskToRemove: []string{"to-remove.ask"}, CreateBidToAdd: []string{"to-add.bid"}, @@ -515,10 +539,11 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { }, }, { - name: "no admin and no updates", + name: "no admin and no market id and no updates", msg: MsgMarketManageReqAttrsRequest{}, expErr: []string{ "invalid administrator", + "invalid market id", "no updates", }, }, @@ -526,6 +551,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { name: "multiple errors", msg: MsgMarketManageReqAttrsRequest{ Administrator: "not1valid", + MarketId: 0, CreateAskToAdd: []string{"bad-ask-attr", "dup-ask"}, CreateAskToRemove: []string{"dup-ask"}, CreateBidToAdd: []string{"bad-bid-attr", "dup-bid"}, @@ -533,6 +559,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { }, expErr: []string{ "invalid administrator", + "invalid market id", "invalid create-ask to add required attribute \"bad-ask-attr\"", "cannot add and remove the same create-ask required attributes \"dup-ask\"", "invalid create-bid to add required attribute \"bad-bid-attr\"", @@ -695,7 +722,7 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { AddFeeCreateAskFlat: []sdk.Coin{coin(1, "nhash")}, RemoveFeeCreateAskFlat: []sdk.Coin{coin(1, "nhash")}, }, - expErr: []string{"cannot add and remove the same create-ask flat fee options: 1nhash"}, + expErr: []string{"cannot add and remove the same create-ask flat fee options 1nhash"}, }, { name: "invalid add create-bid flat", @@ -712,7 +739,7 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { AddFeeCreateBidFlat: []sdk.Coin{coin(1, "nhash")}, RemoveFeeCreateBidFlat: []sdk.Coin{coin(1, "nhash")}, }, - expErr: []string{"cannot add and remove the same create-bid flat fee options: 1nhash"}, + expErr: []string{"cannot add and remove the same create-bid flat fee options 1nhash"}, }, { name: "invalid add seller settlement flat", @@ -729,7 +756,7 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { AddFeeSellerSettlementFlat: []sdk.Coin{coin(1, "nhash")}, RemoveFeeSellerSettlementFlat: []sdk.Coin{coin(1, "nhash")}, }, - expErr: []string{"cannot add and remove the same seller settlement flat fee options: 1nhash"}, + expErr: []string{"cannot add and remove the same seller settlement flat fee options 1nhash"}, }, { name: "invalid add seller settlement ratio", @@ -746,7 +773,7 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { AddFeeSellerSettlementRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, RemoveFeeSellerSettlementRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, }, - expErr: []string{"cannot add and remove the same seller settlement fee ratios: 2nhash:1nhash"}, + expErr: []string{"cannot add and remove the same seller settlement fee ratios 2nhash:1nhash"}, }, { name: "invalid add buyer settlement flat", @@ -763,7 +790,7 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { AddFeeBuyerSettlementFlat: []sdk.Coin{coin(1, "nhash")}, RemoveFeeBuyerSettlementFlat: []sdk.Coin{coin(1, "nhash")}, }, - expErr: []string{"cannot add and remove the same buyer settlement flat fee options: 1nhash"}, + expErr: []string{"cannot add and remove the same buyer settlement flat fee options 1nhash"}, }, { name: "invalid add buyer settlement ratio", @@ -780,7 +807,7 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { AddFeeBuyerSettlementRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, RemoveFeeBuyerSettlementRatios: []FeeRatio{ratio(2, "nhash", 1, "nhash")}, }, - expErr: []string{"cannot add and remove the same buyer settlement fee ratios: 2nhash:1nhash"}, + expErr: []string{"cannot add and remove the same buyer settlement fee ratios 2nhash:1nhash"}, }, { name: "multiple errors", @@ -802,17 +829,17 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { expErr: []string{ "invalid authority", "empty address string is not allowed", `invalid create-ask flat fee to add option "0nhash": amount cannot be zero`, - "cannot add and remove the same create-ask flat fee options: 0nhash", + "cannot add and remove the same create-ask flat fee options 0nhash", `invalid create-bid flat fee to add option "0nhash": amount cannot be zero`, - "cannot add and remove the same create-bid flat fee options: 0nhash", + "cannot add and remove the same create-bid flat fee options 0nhash", `invalid seller settlement flat fee to add option "0nhash": amount cannot be zero`, - "cannot add and remove the same seller settlement flat fee options: 0nhash", + "cannot add and remove the same seller settlement flat fee options 0nhash", `seller fee ratio fee amount "2nhash" cannot be greater than price amount "1nhash"`, - "cannot add and remove the same seller settlement fee ratios: 1nhash:2nhash", + "cannot add and remove the same seller settlement fee ratios 1nhash:2nhash", `invalid buyer settlement flat fee to add option "0nhash": amount cannot be zero`, - "cannot add and remove the same buyer settlement flat fee options: 0nhash", + "cannot add and remove the same buyer settlement flat fee options 0nhash", `buyer fee ratio fee amount "2nhash" cannot be greater than price amount "1nhash"`, - "cannot add and remove the same buyer settlement fee ratios: 1nhash:2nhash", + "cannot add and remove the same buyer settlement fee ratios 1nhash:2nhash", }, }, } From 41124865d6c9a11ded740bf65783457836658f3a Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 10:36:06 -0600 Subject: [PATCH 116/309] [1658]: Make containsString public. --- x/exchange/market.go | 8 +-- x/exchange/market_test.go | 111 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index 70595cbe71..f048601e20 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -125,13 +125,13 @@ func ValidateFeeRatios(sellerRatios, buyerRatios []FeeRatio) error { } for _, denom := range sellerPriceDenoms { - if !containsString(buyerPriceDenoms, denom) { + if !ContainsString(buyerPriceDenoms, denom) { errs = append(errs, fmt.Errorf("denom %q is defined in the seller settlement fee ratios but not buyer", denom)) } } for _, denom := range buyerPriceDenoms { - if !containsString(sellerPriceDenoms, denom) { + if !ContainsString(sellerPriceDenoms, denom) { errs = append(errs, fmt.Errorf("denom %q is defined in the buyer settlement fee ratios but not seller", denom)) } } @@ -198,8 +198,8 @@ func ValidateBuyerFeeRatios(ratios []FeeRatio) error { return errors.Join(errs...) } -// containsString returns true if the string to find is in the vals slice. -func containsString(vals []string, toFind string) bool { +// ContainsString returns true if the string to find is in the vals slice. +func ContainsString(vals []string, toFind string) bool { return contains(vals, toFind, func(a, b string) bool { return a == b }) diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index eff1f3ae5c..e04ba61932 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -728,6 +728,117 @@ func TestValidateBuyerFeeRatios(t *testing.T) { } } +func TestContainsString(t *testing.T) { + tests := []struct { + name string + vals []string + toFind string + exp bool + }{ + { + name: "nil vals", + vals: nil, + toFind: "", + exp: false, + }, + { + name: "empty vals", + vals: []string{}, + toFind: "", + exp: false, + }, + { + name: "one val: same", + vals: []string{"one"}, + toFind: "one", + exp: true, + }, + { + name: "one val: different", + vals: []string{"one"}, + toFind: "two", + exp: false, + }, + { + name: "one val: space at end of val", + vals: []string{"one "}, + toFind: "one", + exp: false, + }, + { + name: "one val: space at end of toFind", + vals: []string{"one"}, + toFind: "one ", + exp: false, + }, + { + name: "one val: space at start of val", + vals: []string{" one"}, + toFind: "one", + exp: false, + }, + { + name: "one val: space at start of toFind", + vals: []string{"one"}, + toFind: " one", + exp: false, + }, + { + name: "one val: different casing", + vals: []string{"one"}, + toFind: "oNe", + exp: false, + }, + { + name: "three vals: not found", + vals: []string{"one", "two", "three"}, + toFind: "zero", + exp: false, + }, + { + name: "three vals: first", + vals: []string{"one", "two", "three"}, + toFind: "one", + exp: true, + }, + { + name: "three vals: second", + vals: []string{"one", "two", "three"}, + toFind: "two", + exp: true, + }, + { + name: "three vals: third", + vals: []string{"one", "two", "three"}, + toFind: "three", + exp: true, + }, + { + name: "three vals: empty string", + vals: []string{"one", "two", "three"}, + toFind: "", + exp: false, + }, + { + name: "empty string in vals: finding empty string", + vals: []string{"one", "", "three"}, + toFind: "", + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = ContainsString(tc.vals, tc.toFind) + } + require.NotPanics(t, testFunc, "ContainsString(%q, %q)", tc.vals, tc.toFind) + assert.Equal(t, tc.exp, actual, "ContainsString(%q, %q)", tc.vals, tc.toFind) + }) + } +} + func TestFeeRatio_String(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} From c85e52d76f60473f02f03755c1957b4c4f0d92c7 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 11:05:56 -0600 Subject: [PATCH 117/309] [1658]: Implement MarketManageReqAttrs. --- x/exchange/keeper/market.go | 65 +++++++++++++++++++++++++++++++++ x/exchange/keeper/msg_server.go | 11 +++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index b0b17b134d..aa728c5be0 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -976,3 +976,68 @@ func (k Keeper) WithdrawMarketFunds(ctx sdk.Context, marketID uint32, toAddr sdk } return nil } + +// CanManageReqAttrs returns true if the provided admin bech32 address has permission to +// manage required attributes for a given market. +func (k Keeper) CanManageReqAttrs(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_attributes) +} + +// updateReqAttrs updates the required attributes in the store that use the provided key maker by removing then adding +// the provided attributes to the existing entries. +func updateReqAttrs(store sdk.KVStore, marketID uint32, toRemove, toAdd []string, field string, maker reqAttrKeyMaker) error { + var errs []error + curAttrs := getReqAttr(store, marketID, maker) + + for _, attr := range toRemove { + if !exchange.ContainsString(curAttrs, attr) { + errs = append(errs, fmt.Errorf("cannot remove %s required attribute %q: attribute not currently required", field, attr)) + } + } + + var newAttrs []string + for _, attr := range curAttrs { + if !exchange.ContainsString(toRemove, attr) { + newAttrs = append(newAttrs, attr) + } + } + + for _, attr := range toAdd { + if !exchange.ContainsString(curAttrs, attr) { + newAttrs = append(newAttrs, attr) + } else { + errs = append(errs, fmt.Errorf("cannot add %s required attribute %q: attribute already required", field, attr)) + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + + setReqAttr(store, marketID, newAttrs, maker) + return nil +} + +// updateReqAttrsAsk updates the attributes required to create an ask order in the store by removing and adding +// the provided entries to the existing entries. +func updateReqAttrsAsk(store sdk.KVStore, marketID uint32, toRemove, toAdd []string) error { + return updateReqAttrs(store, marketID, toRemove, toAdd, "create ask", MakeKeyMarketReqAttrAsk) +} + +// updateReqAttrsBid updates the attributes required to create a bid order in the store by removing and adding +// the provided entries to the existing entries. +func updateReqAttrsBid(store sdk.KVStore, marketID uint32, toRemove, toAdd []string) error { + return updateReqAttrs(store, marketID, toRemove, toAdd, "create bid", MakeKeyMarketReqAttrBid) +} + +// UpdateReqAttrs updates the required attributes in the store using the provided changes. +// The caller is responsible for making sure this update should be allowed (e.g. by calling CanManageReqAttrs first). +func (k Keeper) UpdateReqAttrs(ctx sdk.Context, msg *exchange.MsgMarketManageReqAttrsRequest) error { + marketID := msg.MarketId + store := k.getStore(ctx) + errs := []error{ + updateReqAttrsAsk(store, marketID, msg.CreateAskToRemove, msg.CreateAskToAdd), + updateReqAttrsBid(store, marketID, msg.CreateBidToRemove, msg.CreateBidToAdd), + } + return errors.Join(errs...) +} diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 5298f2083e..b66350996c 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -112,8 +112,15 @@ func (k MsgServer) MarketManagePermissions(goCtx context.Context, msg *exchange. // MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. func (k MsgServer) MarketManageReqAttrs(goCtx context.Context, msg *exchange.MsgMarketManageReqAttrsRequest) (*exchange.MsgMarketManageReqAttrsResponse, error) { - // TODO[1658]: Implement MarketManageReqAttrs - panic("not implemented") + ctx := sdk.UnwrapSDKContext(goCtx) + if !k.CanWithdrawMarketFunds(ctx, msg.MarketId, msg.Administrator) { + return nil, fmt.Errorf("account %s does not have permission to manage required attributes for market %d", msg.Administrator, msg.MarketId) + } + err := k.UpdateReqAttrs(ctx, msg) + if err != nil { + return nil, err + } + return &exchange.MsgMarketManageReqAttrsResponse{}, nil } // wrongAuthErr returns the error to use when a message's authority isn't what's required. From e906de18fcfc3a4648124a5f444947ca52a98f86 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 11:17:57 -0600 Subject: [PATCH 118/309] [1658]: Add the admin and market_id fields to the market management protos. Change the administrator field in MsgMarketManageReqAttrsRequest and MsgMarketWithdrawRequest to admin. --- docs/proto-docs.md | 44 +- proto/provenance/exchange/v1/tx.proto | 42 +- x/exchange/keeper/msg_server.go | 8 +- x/exchange/msg.go | 12 +- x/exchange/msg_test.go | 114 ++--- x/exchange/tx.pb.go | 664 +++++++++++++++++++++----- 6 files changed, 697 insertions(+), 187 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 3e16cdb096..4802198363 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2345,6 +2345,14 @@ MsgGovUpdateParamsResponse is a response message for the GovUpdateParams endpoin MsgMarketManagePermissionsRequest is a request message for the MarketManagePermissions endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `admin` | [string](#string) | | admin is the account with "permissions" permission requesting this change. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. + +TODO[1658]: MsgMarketManagePermissionsRequest | + + @@ -2367,7 +2375,7 @@ MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `administrator` | [string](#string) | | administrator is the account with withdraw permission requesting the withdrawal. | +| `admin` | [string](#string) | | admin is the account with "attributes" permission requesting this change. | | `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | | `create_ask_to_add` | [string](#string) | repeated | create_ask_to_add are the attributes that should now also be required to create an ask order. | | `create_ask_to_remove` | [string](#string) | repeated | create_ask_to_add are the attributes that should no longer be required to create an ask order. | @@ -2395,6 +2403,14 @@ MsgMarketManageReqAttrsResponse is a response message for the MarketManageReqAtt MsgMarketSettleRequest is a request message for the MarketSettle endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `admin` | [string](#string) | | admin is the account with "settle" permission requesting this settlement. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. + +TODO[1658]: MsgMarketSettleRequest | + + @@ -2415,6 +2431,14 @@ MsgMarketSettleResponse is a response message for the MarketSettle endpoint. MsgMarketUpdateDetailsRequest is a request message for the MarketUpdateDetails endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `admin` | [string](#string) | | admin is the account with "update" permission requesting this change. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. + +TODO[1658]: MsgMarketUpdateDetailsRequest | + + @@ -2435,6 +2459,14 @@ MsgMarketUpdateDetailsResponse is a response message for the MarketUpdateDetails MsgMarketUpdateEnabledRequest is a request message for the MarketUpdateEnabled endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `admin` | [string](#string) | | admin is the account with "update" permission requesting this change. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. + +TODO[1658]: MsgMarketUpdateEnabledRequest | + + @@ -2455,6 +2487,14 @@ MsgMarketUpdateEnabledResponse is a response message for the MarketUpdateEnabled MsgMarketUpdateUserSettleRequest is a request message for the MarketUpdateUserSettle endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `admin` | [string](#string) | | admin is the account with "update" permission requesting this change. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. + +TODO[1658]: MsgMarketUpdateUserSettleRequest | + + @@ -2477,7 +2517,7 @@ MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `administrator` | [string](#string) | | administrator is the account with withdraw permission requesting the withdrawal. | +| `admin` | [string](#string) | | admin is the account with withdraw permission requesting the withdrawal. | | `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to withdraw from. | | `to_address` | [string](#string) | | to_address is the address that will receive the funds. | | `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | amount is the funds to withdraw. | diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 917dc0dc86..62c574d5c6 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -125,6 +125,12 @@ message MsgFillAsksResponse {} // MsgMarketSettleRequest is a request message for the MarketSettle endpoint. message MsgMarketSettleRequest { + option (cosmos.msg.v1.signer) = "admin"; + + // admin is the account with "settle" permission requesting this settlement. + string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market_id is the numerical identifier of the market to update required attributes for. + uint32 market_id = 2; // TODO[1658]: MsgMarketSettleRequest } @@ -133,10 +139,10 @@ message MsgMarketSettleResponse {} // MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. message MsgMarketWithdrawRequest { - option (cosmos.msg.v1.signer) = "administrator"; + option (cosmos.msg.v1.signer) = "admin"; - // administrator is the account with withdraw permission requesting the withdrawal. - string administrator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // admin is the account with withdraw permission requesting the withdrawal. + string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // market_id is the numerical identifier of the market to withdraw from. uint32 market_id = 2; @@ -154,6 +160,12 @@ message MsgMarketWithdrawResponse {} // MsgMarketUpdateDetailsRequest is a request message for the MarketUpdateDetails endpoint. message MsgMarketUpdateDetailsRequest { + option (cosmos.msg.v1.signer) = "admin"; + + // admin is the account with "update" permission requesting this change. + string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market_id is the numerical identifier of the market to update required attributes for. + uint32 market_id = 2; // TODO[1658]: MsgMarketUpdateDetailsRequest } @@ -162,6 +174,12 @@ message MsgMarketUpdateDetailsResponse {} // MsgMarketUpdateEnabledRequest is a request message for the MarketUpdateEnabled endpoint. message MsgMarketUpdateEnabledRequest { + option (cosmos.msg.v1.signer) = "admin"; + + // admin is the account with "update" permission requesting this change. + string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market_id is the numerical identifier of the market to update required attributes for. + uint32 market_id = 2; // TODO[1658]: MsgMarketUpdateEnabledRequest } @@ -170,6 +188,12 @@ message MsgMarketUpdateEnabledResponse {} // MsgMarketUpdateUserSettleRequest is a request message for the MarketUpdateUserSettle endpoint. message MsgMarketUpdateUserSettleRequest { + option (cosmos.msg.v1.signer) = "admin"; + + // admin is the account with "update" permission requesting this change. + string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market_id is the numerical identifier of the market to update required attributes for. + uint32 market_id = 2; // TODO[1658]: MsgMarketUpdateUserSettleRequest } @@ -178,6 +202,12 @@ message MsgMarketUpdateUserSettleResponse {} // MsgMarketManagePermissionsRequest is a request message for the MarketManagePermissions endpoint. message MsgMarketManagePermissionsRequest { + option (cosmos.msg.v1.signer) = "admin"; + + // admin is the account with "permissions" permission requesting this change. + string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market_id is the numerical identifier of the market to update required attributes for. + uint32 market_id = 2; // TODO[1658]: MsgMarketManagePermissionsRequest } @@ -186,10 +216,10 @@ message MsgMarketManagePermissionsResponse {} // MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs endpoint. message MsgMarketManageReqAttrsRequest { - option (cosmos.msg.v1.signer) = "administrator"; + option (cosmos.msg.v1.signer) = "admin"; - // administrator is the account with withdraw permission requesting the withdrawal. - string administrator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // admin is the account with "attributes" permission requesting this change. + string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // market_id is the numerical identifier of the market to update required attributes for. uint32 market_id = 2; // create_ask_to_add are the attributes that should now also be required to create an ask order. diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index b66350996c..9ec7d03f03 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -75,8 +75,8 @@ func (k MsgServer) MarketSettle(goCtx context.Context, msg *exchange.MsgMarketSe // MarketWithdraw is a market endpoint to withdraw fees that have been collected. func (k MsgServer) MarketWithdraw(goCtx context.Context, msg *exchange.MsgMarketWithdrawRequest) (*exchange.MsgMarketWithdrawResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if !k.CanWithdrawMarketFunds(ctx, msg.MarketId, msg.Administrator) { - return nil, fmt.Errorf("account %s does not have withdraw permission for market %d", msg.Administrator, msg.MarketId) + if !k.CanWithdrawMarketFunds(ctx, msg.MarketId, msg.Admin) { + return nil, fmt.Errorf("account %s does not have withdraw permission for market %d", msg.Admin, msg.MarketId) } toAddr := sdk.MustAccAddressFromBech32(msg.ToAddress) err := k.WithdrawMarketFunds(ctx, msg.MarketId, toAddr, msg.Amount) @@ -113,8 +113,8 @@ func (k MsgServer) MarketManagePermissions(goCtx context.Context, msg *exchange. // MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. func (k MsgServer) MarketManageReqAttrs(goCtx context.Context, msg *exchange.MsgMarketManageReqAttrsRequest) (*exchange.MsgMarketManageReqAttrsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if !k.CanWithdrawMarketFunds(ctx, msg.MarketId, msg.Administrator) { - return nil, fmt.Errorf("account %s does not have permission to manage required attributes for market %d", msg.Administrator, msg.MarketId) + if !k.CanWithdrawMarketFunds(ctx, msg.MarketId, msg.Admin) { + return nil, fmt.Errorf("account %s does not have permission to manage required attributes for market %d", msg.Admin, msg.MarketId) } err := k.UpdateReqAttrs(ctx, msg) if err != nil { diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 0ac9e591c9..6fbb9d4dc0 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -107,8 +107,8 @@ func (m MsgMarketSettleRequest) GetSigners() []sdk.AccAddress { func (m MsgMarketWithdrawRequest) ValidateBasic() error { var errs []error - if _, err := sdk.AccAddressFromBech32(m.Administrator); err != nil { - errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Administrator, err)) + if _, err := sdk.AccAddressFromBech32(m.Admin); err != nil { + errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Admin, err)) } if m.MarketId == 0 { @@ -129,7 +129,7 @@ func (m MsgMarketWithdrawRequest) ValidateBasic() error { } func (m MsgMarketWithdrawRequest) GetSigners() []sdk.AccAddress { - addr := sdk.MustAccAddressFromBech32(m.Administrator) + addr := sdk.MustAccAddressFromBech32(m.Admin) return []sdk.AccAddress{addr} } @@ -176,8 +176,8 @@ func (m MsgMarketManagePermissionsRequest) GetSigners() []sdk.AccAddress { func (m MsgMarketManageReqAttrsRequest) ValidateBasic() error { var errs []error - if _, err := sdk.AccAddressFromBech32(m.Administrator); err != nil { - errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Administrator, err)) + if _, err := sdk.AccAddressFromBech32(m.Admin); err != nil { + errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Admin, err)) } if m.MarketId == 0 { @@ -202,7 +202,7 @@ func (m MsgMarketManageReqAttrsRequest) HasUpdates() bool { } func (m MsgMarketManageReqAttrsRequest) GetSigners() []sdk.AccAddress { - addr := sdk.MustAccAddressFromBech32(m.Administrator) + addr := sdk.MustAccAddressFromBech32(m.Admin) return []sdk.AccAddress{addr} } diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index e701ab4379..1a6fe73166 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -43,14 +43,14 @@ func TestAllMsgsGetSigners(t *testing.T) { // TODO[1658]: Add MsgFillAsksRequest once it's actually been defined. // TODO[1658]: Add MsgMarketSettleRequest once it's actually been defined. func(signer string) sdk.Msg { - return &MsgMarketWithdrawRequest{Administrator: signer} + return &MsgMarketWithdrawRequest{Admin: signer} }, // TODO[1658]: Add MsgMarketUpdateDetailsRequest once it's actually been defined. // TODO[1658]: Add MsgMarketUpdateEnabledRequest once it's actually been defined. // TODO[1658]: Add MsgMarketUpdateUserSettleRequest once it's actually been defined. // TODO[1658]: Add MsgMarketManagePermissionsRequest once it's actually been defined. func(signer string) sdk.Msg { - return &MsgMarketManageReqAttrsRequest{Administrator: signer} + return &MsgMarketManageReqAttrsRequest{Admin: signer} }, func(signer string) sdk.Msg { return &MsgGovCreateMarketRequest{Authority: signer} @@ -289,100 +289,100 @@ func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) { { name: "control", msg: MsgMarketWithdrawRequest{ - Administrator: goodAdminAddr, - MarketId: 1, - ToAddress: goodToAddr, - Amount: goodCoins, + Admin: goodAdminAddr, + MarketId: 1, + ToAddress: goodToAddr, + Amount: goodCoins, }, expErr: nil, }, { name: "no administrator", msg: MsgMarketWithdrawRequest{ - Administrator: "", - MarketId: 1, - ToAddress: goodToAddr, - Amount: goodCoins, + Admin: "", + MarketId: 1, + ToAddress: goodToAddr, + Amount: goodCoins, }, expErr: []string{`invalid administrator ""`, "empty address string is not allowed"}, }, { name: "bad administrator", msg: MsgMarketWithdrawRequest{ - Administrator: "notright", - MarketId: 1, - ToAddress: goodToAddr, - Amount: goodCoins, + Admin: "notright", + MarketId: 1, + ToAddress: goodToAddr, + Amount: goodCoins, }, expErr: []string{`invalid administrator "notright"`, "decoding bech32 failed"}, }, { name: "market id zero", msg: MsgMarketWithdrawRequest{ - Administrator: goodAdminAddr, - MarketId: 0, - ToAddress: goodToAddr, - Amount: goodCoins, + Admin: goodAdminAddr, + MarketId: 0, + ToAddress: goodToAddr, + Amount: goodCoins, }, expErr: []string{"invalid market id", "cannot be zero"}, }, { name: "no to address", msg: MsgMarketWithdrawRequest{ - Administrator: goodAdminAddr, - MarketId: 1, - ToAddress: "", - Amount: goodCoins, + Admin: goodAdminAddr, + MarketId: 1, + ToAddress: "", + Amount: goodCoins, }, expErr: []string{`invalid to address ""`, "empty address string is not allowed"}, }, { name: "bad to address", msg: MsgMarketWithdrawRequest{ - Administrator: goodAdminAddr, - MarketId: 1, - ToAddress: "notright", - Amount: goodCoins, + Admin: goodAdminAddr, + MarketId: 1, + ToAddress: "notright", + Amount: goodCoins, }, expErr: []string{`invalid to address "notright"`, "decoding bech32 failed"}, }, { name: "invalid denom in amount", msg: MsgMarketWithdrawRequest{ - Administrator: goodAdminAddr, - MarketId: 1, - ToAddress: goodToAddr, - Amount: sdk.Coins{coin(3, "x")}, + Admin: goodAdminAddr, + MarketId: 1, + ToAddress: goodToAddr, + Amount: sdk.Coins{coin(3, "x")}, }, expErr: []string{`invalid amount "3x"`, "invalid denom: x"}, }, { name: "negative amount", msg: MsgMarketWithdrawRequest{ - Administrator: goodAdminAddr, - MarketId: 1, - ToAddress: goodToAddr, - Amount: sdk.Coins{coin(-1, "negcoin")}, + Admin: goodAdminAddr, + MarketId: 1, + ToAddress: goodToAddr, + Amount: sdk.Coins{coin(-1, "negcoin")}, }, expErr: []string{`invalid amount "-1negcoin"`, "coin -1negcoin amount is not positive"}, }, { name: "empty amount", msg: MsgMarketWithdrawRequest{ - Administrator: goodAdminAddr, - MarketId: 1, - ToAddress: goodToAddr, - Amount: sdk.Coins{}, + Admin: goodAdminAddr, + MarketId: 1, + ToAddress: goodToAddr, + Amount: sdk.Coins{}, }, expErr: []string{`invalid amount ""`, "cannot be zero"}, }, { name: "zero coin amount", msg: MsgMarketWithdrawRequest{ - Administrator: goodAdminAddr, - MarketId: 1, - ToAddress: goodToAddr, - Amount: sdk.Coins{coin(0, "acorn"), coin(0, "banana")}, + Admin: goodAdminAddr, + MarketId: 1, + ToAddress: goodToAddr, + Amount: sdk.Coins{coin(0, "acorn"), coin(0, "banana")}, }, expErr: []string{`invalid amount "0acorn,0banana"`, "coin 0acorn amount is not positive"}, }, @@ -424,7 +424,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "no admin", msg: MsgMarketManageReqAttrsRequest{ - Administrator: "", + Admin: "", MarketId: 1, CreateAskToAdd: []string{"abc"}, }, @@ -433,7 +433,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "bad admin", msg: MsgMarketManageReqAttrsRequest{ - Administrator: "not1valid", + Admin: "not1valid", MarketId: 1, CreateAskToAdd: []string{"abc"}, }, @@ -442,7 +442,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "market id zero", msg: MsgMarketManageReqAttrsRequest{ - Administrator: goodAdmin, + Admin: goodAdmin, CreateAskToAdd: []string{"abc"}, }, expErr: []string{"invalid market id: cannot be zero"}, @@ -450,15 +450,15 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "no updates", msg: MsgMarketManageReqAttrsRequest{ - Administrator: goodAdmin, - MarketId: 1, + Admin: goodAdmin, + MarketId: 1, }, expErr: []string{"no updates"}, }, { name: "invalid create ask to add entry", msg: MsgMarketManageReqAttrsRequest{ - Administrator: goodAdmin, + Admin: goodAdmin, MarketId: 1, CreateAskToAdd: []string{"in-valid-attr"}, }, @@ -467,7 +467,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "invalid create ask to remove entry", msg: MsgMarketManageReqAttrsRequest{ - Administrator: goodAdmin, + Admin: goodAdmin, MarketId: 1, CreateAskToRemove: []string{"in-valid-attr"}, }, @@ -475,7 +475,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "invalid create bid to add entry", msg: MsgMarketManageReqAttrsRequest{ - Administrator: goodAdmin, + Admin: goodAdmin, MarketId: 1, CreateBidToAdd: []string{"in-valid-attr"}, }, @@ -484,7 +484,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "invalid create bid to remove entry", msg: MsgMarketManageReqAttrsRequest{ - Administrator: goodAdmin, + Admin: goodAdmin, MarketId: 1, CreateBidToRemove: []string{"in-valid-attr"}, }, @@ -492,7 +492,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "add and remove same create ask entry", msg: MsgMarketManageReqAttrsRequest{ - Administrator: goodAdmin, + Admin: goodAdmin, MarketId: 1, CreateAskToAdd: []string{"abc", "def", "ghi"}, CreateAskToRemove: []string{"jkl", "abc"}, @@ -502,7 +502,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "add and remove same create bid entry", msg: MsgMarketManageReqAttrsRequest{ - Administrator: goodAdmin, + Admin: goodAdmin, MarketId: 1, CreateBidToAdd: []string{"abc", "def", "ghi"}, CreateBidToRemove: []string{"jkl", "abc"}, @@ -512,7 +512,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "add to create-ask the same as remove from create-bid", msg: MsgMarketManageReqAttrsRequest{ - Administrator: goodAdmin, + Admin: goodAdmin, MarketId: 1, CreateAskToAdd: []string{"abc", "def", "ghi"}, CreateBidToRemove: []string{"jkl", "abc"}, @@ -521,7 +521,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "add to create-bid the same as remove from create-ask", msg: MsgMarketManageReqAttrsRequest{ - Administrator: goodAdmin, + Admin: goodAdmin, MarketId: 1, CreateBidToAdd: []string{"abc", "def", "ghi"}, CreateAskToRemove: []string{"jkl", "abc"}, @@ -530,7 +530,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "add one to and remove one from each", msg: MsgMarketManageReqAttrsRequest{ - Administrator: goodAdmin, + Admin: goodAdmin, MarketId: 1, CreateAskToAdd: []string{"to-add.ask"}, CreateAskToRemove: []string{"to-remove.ask"}, @@ -550,7 +550,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { { name: "multiple errors", msg: MsgMarketManageReqAttrsRequest{ - Administrator: "not1valid", + Admin: "not1valid", MarketId: 0, CreateAskToAdd: []string{"bad-ask-attr", "dup-ask"}, CreateAskToRemove: []string{"dup-ask"}, diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 3ebc4a71da..caefa93ddb 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -476,6 +476,10 @@ var xxx_messageInfo_MsgFillAsksResponse proto.InternalMessageInfo // MsgMarketSettleRequest is a request message for the MarketSettle endpoint. type MsgMarketSettleRequest struct { + // admin is the account with "settle" permission requesting this settlement. + Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` + // market_id is the numerical identifier of the market to update required attributes for. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` } func (m *MsgMarketSettleRequest) Reset() { *m = MsgMarketSettleRequest{} } @@ -511,6 +515,20 @@ func (m *MsgMarketSettleRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgMarketSettleRequest proto.InternalMessageInfo +func (m *MsgMarketSettleRequest) GetAdmin() string { + if m != nil { + return m.Admin + } + return "" +} + +func (m *MsgMarketSettleRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + // MsgMarketSettleResponse is a response message for the MarketSettle endpoint. type MsgMarketSettleResponse struct { } @@ -550,8 +568,8 @@ var xxx_messageInfo_MsgMarketSettleResponse proto.InternalMessageInfo // MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. type MsgMarketWithdrawRequest struct { - // administrator is the account with withdraw permission requesting the withdrawal. - Administrator string `protobuf:"bytes,1,opt,name=administrator,proto3" json:"administrator,omitempty"` + // admin is the account with withdraw permission requesting the withdrawal. + Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` // market_id is the numerical identifier of the market to withdraw from. MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // to_address is the address that will receive the funds. @@ -593,9 +611,9 @@ func (m *MsgMarketWithdrawRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgMarketWithdrawRequest proto.InternalMessageInfo -func (m *MsgMarketWithdrawRequest) GetAdministrator() string { +func (m *MsgMarketWithdrawRequest) GetAdmin() string { if m != nil { - return m.Administrator + return m.Admin } return "" } @@ -660,6 +678,10 @@ var xxx_messageInfo_MsgMarketWithdrawResponse proto.InternalMessageInfo // MsgMarketUpdateDetailsRequest is a request message for the MarketUpdateDetails endpoint. type MsgMarketUpdateDetailsRequest struct { + // admin is the account with "update" permission requesting this change. + Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` + // market_id is the numerical identifier of the market to update required attributes for. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` } func (m *MsgMarketUpdateDetailsRequest) Reset() { *m = MsgMarketUpdateDetailsRequest{} } @@ -695,6 +717,20 @@ func (m *MsgMarketUpdateDetailsRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgMarketUpdateDetailsRequest proto.InternalMessageInfo +func (m *MsgMarketUpdateDetailsRequest) GetAdmin() string { + if m != nil { + return m.Admin + } + return "" +} + +func (m *MsgMarketUpdateDetailsRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + // MsgMarketUpdateDetailsResponse is a response message for the MarketUpdateDetails endpoint. type MsgMarketUpdateDetailsResponse struct { } @@ -734,6 +770,10 @@ var xxx_messageInfo_MsgMarketUpdateDetailsResponse proto.InternalMessageInfo // MsgMarketUpdateEnabledRequest is a request message for the MarketUpdateEnabled endpoint. type MsgMarketUpdateEnabledRequest struct { + // admin is the account with "update" permission requesting this change. + Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` + // market_id is the numerical identifier of the market to update required attributes for. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` } func (m *MsgMarketUpdateEnabledRequest) Reset() { *m = MsgMarketUpdateEnabledRequest{} } @@ -769,6 +809,20 @@ func (m *MsgMarketUpdateEnabledRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgMarketUpdateEnabledRequest proto.InternalMessageInfo +func (m *MsgMarketUpdateEnabledRequest) GetAdmin() string { + if m != nil { + return m.Admin + } + return "" +} + +func (m *MsgMarketUpdateEnabledRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + // MsgMarketUpdateEnabledResponse is a response message for the MarketUpdateEnabled endpoint. type MsgMarketUpdateEnabledResponse struct { } @@ -808,6 +862,10 @@ var xxx_messageInfo_MsgMarketUpdateEnabledResponse proto.InternalMessageInfo // MsgMarketUpdateUserSettleRequest is a request message for the MarketUpdateUserSettle endpoint. type MsgMarketUpdateUserSettleRequest struct { + // admin is the account with "update" permission requesting this change. + Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` + // market_id is the numerical identifier of the market to update required attributes for. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` } func (m *MsgMarketUpdateUserSettleRequest) Reset() { *m = MsgMarketUpdateUserSettleRequest{} } @@ -843,6 +901,20 @@ func (m *MsgMarketUpdateUserSettleRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgMarketUpdateUserSettleRequest proto.InternalMessageInfo +func (m *MsgMarketUpdateUserSettleRequest) GetAdmin() string { + if m != nil { + return m.Admin + } + return "" +} + +func (m *MsgMarketUpdateUserSettleRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + // MsgMarketUpdateUserSettleResponse is a response message for the MarketUpdateUserSettle endpoint. type MsgMarketUpdateUserSettleResponse struct { } @@ -882,6 +954,10 @@ var xxx_messageInfo_MsgMarketUpdateUserSettleResponse proto.InternalMessageInfo // MsgMarketManagePermissionsRequest is a request message for the MarketManagePermissions endpoint. type MsgMarketManagePermissionsRequest struct { + // admin is the account with "permissions" permission requesting this change. + Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` + // market_id is the numerical identifier of the market to update required attributes for. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` } func (m *MsgMarketManagePermissionsRequest) Reset() { *m = MsgMarketManagePermissionsRequest{} } @@ -917,6 +993,20 @@ func (m *MsgMarketManagePermissionsRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgMarketManagePermissionsRequest proto.InternalMessageInfo +func (m *MsgMarketManagePermissionsRequest) GetAdmin() string { + if m != nil { + return m.Admin + } + return "" +} + +func (m *MsgMarketManagePermissionsRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + // MsgMarketManagePermissionsResponse is a response message for the MarketManagePermissions endpoint. type MsgMarketManagePermissionsResponse struct { } @@ -956,8 +1046,8 @@ var xxx_messageInfo_MsgMarketManagePermissionsResponse proto.InternalMessageInfo // MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs endpoint. type MsgMarketManageReqAttrsRequest struct { - // administrator is the account with withdraw permission requesting the withdrawal. - Administrator string `protobuf:"bytes,1,opt,name=administrator,proto3" json:"administrator,omitempty"` + // admin is the account with "attributes" permission requesting this change. + Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` // market_id is the numerical identifier of the market to update required attributes for. MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // create_ask_to_add are the attributes that should now also be required to create an ask order. @@ -1003,9 +1093,9 @@ func (m *MsgMarketManageReqAttrsRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgMarketManageReqAttrsRequest proto.InternalMessageInfo -func (m *MsgMarketManageReqAttrsRequest) GetAdministrator() string { +func (m *MsgMarketManageReqAttrsRequest) GetAdmin() string { if m != nil { - return m.Administrator + return m.Admin } return "" } @@ -1504,96 +1594,96 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1412 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xcf, 0x6f, 0x1b, 0x45, - 0x18, 0x8d, 0x93, 0x34, 0x8d, 0xbf, 0x36, 0x69, 0x3b, 0x75, 0x13, 0x7b, 0xdb, 0xd8, 0xae, 0xcb, - 0xa1, 0xb4, 0x64, 0xdd, 0x14, 0x51, 0x20, 0x42, 0x48, 0x71, 0x20, 0x55, 0x0f, 0x11, 0xd5, 0x86, - 0x0a, 0x09, 0x0e, 0xd6, 0xd8, 0x3b, 0x75, 0x46, 0xb1, 0x77, 0xd2, 0x9d, 0x49, 0x9a, 0x5e, 0x11, - 0x12, 0x27, 0x44, 0x8f, 0x5c, 0xb9, 0x72, 0xe2, 0xc0, 0x05, 0xfe, 0x82, 0xc2, 0xa9, 0xe2, 0xc4, - 0x09, 0x50, 0x73, 0xe0, 0xdf, 0x40, 0xf3, 0xc3, 0x9b, 0x5d, 0x7b, 0x7f, 0x39, 0x54, 0x9c, 0xda, - 0xdd, 0x7d, 0xdf, 0x7b, 0xdf, 0xfb, 0x76, 0x32, 0xb3, 0xcf, 0x50, 0xdb, 0xf7, 0xd9, 0x21, 0xf1, - 0xb0, 0xd7, 0x25, 0x4d, 0x72, 0xd4, 0xdd, 0xc5, 0x5e, 0x8f, 0x34, 0x0f, 0xd7, 0x9a, 0xe2, 0xc8, - 0xde, 0xf7, 0x99, 0x60, 0x68, 0xe9, 0x04, 0x60, 0x0f, 0x01, 0xf6, 0xe1, 0x9a, 0x55, 0xed, 0x32, - 0x3e, 0x60, 0xbc, 0xd9, 0xc1, 0x5c, 0x16, 0x74, 0x88, 0xc0, 0x6b, 0xcd, 0x2e, 0xa3, 0x9e, 0xae, - 0xb3, 0x96, 0xcd, 0xf3, 0x01, 0xef, 0x49, 0xbe, 0x01, 0xef, 0x99, 0x07, 0x15, 0xfd, 0xa0, 0xad, - 0xae, 0x9a, 0xfa, 0xc2, 0x3c, 0x2a, 0xf5, 0x58, 0x8f, 0xe9, 0xfb, 0xf2, 0x7f, 0xe6, 0xee, 0x8d, - 0x84, 0x16, 0x07, 0xd8, 0xdf, 0x23, 0x22, 0x03, 0xc4, 0x7c, 0x97, 0xf8, 0x3c, 0x03, 0xb4, 0x8f, - 0x7d, 0x3c, 0x30, 0xa0, 0xc6, 0x2f, 0x05, 0xb8, 0xbc, 0xcd, 0x7b, 0x9b, 0x3e, 0xc1, 0x82, 0x6c, - 0xf0, 0x3d, 0x87, 0x3c, 0x39, 0x20, 0x5c, 0xa0, 0x4d, 0x28, 0x62, 0xbe, 0xd7, 0x56, 0x84, 0xe5, - 0x42, 0xbd, 0x70, 0xf3, 0xdc, 0xdd, 0xba, 0x1d, 0x3f, 0x1c, 0x7b, 0x83, 0xef, 0x7d, 0x22, 0x71, - 0xad, 0xd9, 0x17, 0x7f, 0xd6, 0xa6, 0x9c, 0x79, 0x6c, 0xae, 0xd1, 0x7d, 0x40, 0x8a, 0xa0, 0xdd, - 0x95, 0xf4, 0x94, 0x79, 0xed, 0xc7, 0x84, 0x94, 0xa7, 0x15, 0x5b, 0xc5, 0x36, 0xc3, 0x90, 0x23, - 0xb5, 0xcd, 0x48, 0xed, 0x4d, 0x46, 0x3d, 0xe7, 0xa2, 0x2a, 0xda, 0x34, 0x35, 0x5b, 0x84, 0xac, - 0x5f, 0xf9, 0xf2, 0x9f, 0x1f, 0x6f, 0x5d, 0x0c, 0x1a, 0xb2, 0x39, 0xe9, 0xf7, 0x89, 0xdf, 0x58, - 0x83, 0x52, 0xb4, 0x77, 0xbe, 0xcf, 0x3c, 0x4e, 0x50, 0x05, 0xe6, 0xb5, 0x2e, 0x75, 0x55, 0xef, - 0xb3, 0xce, 0x59, 0x75, 0xfd, 0xc0, 0x6d, 0xfc, 0x1c, 0xf6, 0xdb, 0xa2, 0x6e, 0xc8, 0x6f, 0x87, - 0xba, 0xf9, 0xfc, 0xb6, 0xa8, 0x1b, 0xf1, 0xdb, 0x31, 0xd7, 0xaf, 0xcf, 0x6f, 0x49, 0xfa, 0xbd, - 0x10, 0x34, 0x64, 0x77, 0x0e, 0x9e, 0x8d, 0xd8, 0x55, 0xad, 0x67, 0xdb, 0xf5, 0xe0, 0x8a, 0x2c, - 0x91, 0x16, 0xfa, 0xaa, 0xc7, 0xa1, 0x5f, 0x1b, 0xce, 0xb0, 0xa7, 0x9e, 0xf1, 0x5a, 0x6c, 0x95, - 0x7f, 0xff, 0x69, 0xb5, 0x64, 0x1a, 0xdc, 0x70, 0x5d, 0x9f, 0x70, 0xbe, 0x23, 0x7c, 0xea, 0xf5, - 0x1c, 0x0d, 0x8b, 0x68, 0x4c, 0x47, 0x34, 0xd6, 0x41, 0x36, 0xab, 0x61, 0x8d, 0x32, 0x2c, 0x8d, - 0xea, 0xe9, 0x26, 0x1b, 0x25, 0x40, 0xdb, 0xbc, 0xb7, 0x45, 0xfb, 0xfd, 0x16, 0x75, 0xb9, 0x69, - 0xa3, 0x71, 0x45, 0xbd, 0x8d, 0x93, 0xbb, 0x63, 0xe0, 0x0d, 0xbe, 0x17, 0x03, 0xd6, 0x77, 0x0d, - 0x58, 0x6b, 0x6e, 0xab, 0xbf, 0x8f, 0x1d, 0x22, 0x44, 0x9f, 0x0c, 0x0b, 0x2a, 0xb0, 0x3c, 0xf6, - 0xc4, 0x14, 0x7d, 0x3b, 0x0d, 0xe5, 0xe0, 0xd9, 0x67, 0x54, 0xec, 0xba, 0x3e, 0x7e, 0x3a, 0x1c, - 0xce, 0x87, 0xb0, 0x80, 0xdd, 0x01, 0xf5, 0x28, 0x17, 0x3e, 0x16, 0x2c, 0x7b, 0x48, 0x51, 0x38, - 0xba, 0x0a, 0x45, 0xfd, 0xe7, 0x3a, 0x9c, 0xd6, 0x82, 0x33, 0xaf, 0x6f, 0x3c, 0x70, 0xd1, 0x0a, - 0x80, 0x60, 0x6d, 0xac, 0xeb, 0xcb, 0x33, 0x92, 0xd9, 0x29, 0x0a, 0x66, 0x08, 0x51, 0x17, 0xe6, - 0xf0, 0x80, 0x1d, 0x78, 0xa2, 0x3c, 0x5b, 0x9f, 0x49, 0x5d, 0x37, 0xad, 0x3b, 0x72, 0xf9, 0xfd, - 0xf0, 0x57, 0xed, 0x66, 0x8f, 0x8a, 0xdd, 0x83, 0x8e, 0xdd, 0x65, 0x03, 0xb3, 0xc3, 0x98, 0x7f, - 0x56, 0xb9, 0xbb, 0xd7, 0x14, 0xcf, 0xf6, 0x09, 0x57, 0x05, 0xdc, 0x31, 0xd4, 0xeb, 0x48, 0xbe, - 0xb2, 0x68, 0xd3, 0x8d, 0xab, 0x50, 0x89, 0x19, 0x88, 0x19, 0x57, 0x0d, 0x56, 0x82, 0x87, 0x8f, - 0xf6, 0x5d, 0x2c, 0xc8, 0x47, 0x44, 0x60, 0xda, 0x0f, 0xde, 0x4d, 0x1d, 0xaa, 0x49, 0x80, 0x44, - 0x8a, 0x8f, 0x3d, 0xdc, 0xe9, 0x13, 0x37, 0x99, 0x22, 0x00, 0x18, 0x8a, 0x06, 0xd4, 0x47, 0x10, - 0x8f, 0x38, 0xf1, 0xa3, 0xef, 0xfc, 0x06, 0x5c, 0x4f, 0xc1, 0x18, 0xa2, 0x30, 0x68, 0x1b, 0x7b, - 0xb8, 0x47, 0x1e, 0x12, 0x7f, 0x40, 0x39, 0xa7, 0xcc, 0x0b, 0x2c, 0xbd, 0x01, 0x8d, 0x34, 0x90, - 0xa1, 0xfa, 0x75, 0x3a, 0xd4, 0xb6, 0x86, 0x39, 0xe4, 0xc9, 0x86, 0x10, 0x3e, 0xff, 0x5f, 0x96, - 0xd3, 0x9b, 0x70, 0x49, 0xed, 0x36, 0xa4, 0x2d, 0xb7, 0x47, 0xbd, 0xb2, 0xca, 0x33, 0xf5, 0x99, - 0x9b, 0x45, 0x67, 0xb1, 0x3b, 0xdc, 0x19, 0x3f, 0x95, 0xcb, 0x0b, 0x35, 0xa1, 0x14, 0x85, 0xfa, - 0x64, 0xc0, 0x0e, 0x89, 0x5a, 0x68, 0x45, 0xe7, 0x52, 0x08, 0xed, 0xa8, 0x07, 0x21, 0x6e, 0xb9, - 0x15, 0x19, 0xee, 0x33, 0x61, 0xee, 0x16, 0x75, 0x47, 0xb9, 0x0d, 0xd4, 0x70, 0xcf, 0x85, 0xb9, - 0x15, 0x5a, 0x73, 0xc7, 0x2e, 0xc1, 0xeb, 0x50, 0x4b, 0x1c, 0xa5, 0x19, 0xf7, 0xf7, 0x05, 0xb5, - 0x4c, 0xef, 0xb3, 0x43, 0xbd, 0x0f, 0x6a, 0xf0, 0x70, 0xd2, 0xf7, 0xa0, 0x88, 0x0f, 0xc4, 0x2e, - 0xf3, 0xa9, 0x78, 0x96, 0x39, 0xe5, 0x13, 0x28, 0xfa, 0x00, 0xe6, 0xf4, 0x40, 0xcd, 0x66, 0x5d, - 0x4d, 0xda, 0xfa, 0xb5, 0x9c, 0xd9, 0xf8, 0x4d, 0xcd, 0xfa, 0xa2, 0xb4, 0x72, 0xc2, 0xd6, 0xb8, - 0x06, 0x56, 0x5c, 0x8b, 0xc6, 0xc1, 0x6f, 0xa0, 0x76, 0xa5, 0xfb, 0xec, 0x50, 0x5b, 0xdc, 0x22, - 0x84, 0xff, 0xd7, 0xfe, 0x53, 0x57, 0xc8, 0x23, 0x58, 0xc6, 0xae, 0x2b, 0x8f, 0xa2, 0x76, 0xe8, - 0xf5, 0x3f, 0xee, 0x63, 0xa1, 0xd6, 0x49, 0xea, 0x16, 0xa3, 0x8d, 0x5e, 0xc6, 0xae, 0xbb, 0x45, - 0x48, 0x70, 0xd0, 0x6e, 0xf5, 0xb1, 0x40, 0x5f, 0x80, 0xa5, 0xdf, 0x71, 0x2c, 0xf3, 0x6c, 0x3e, - 0xe6, 0x25, 0x4d, 0x31, 0x46, 0x3e, 0xde, 0xb3, 0x5c, 0x56, 0x8a, 0xf9, 0xcc, 0x29, 0x7a, 0x6e, - 0x51, 0x37, 0xb9, 0xe7, 0x80, 0x79, 0xee, 0x74, 0x3d, 0x0f, 0xc9, 0xbb, 0x50, 0x1d, 0xf6, 0xac, - 0xbf, 0x4f, 0xda, 0x5c, 0xed, 0x3a, 0x03, 0xe2, 0x09, 0x2d, 0x70, 0x36, 0x9f, 0x80, 0xa5, 0x5b, - 0xdf, 0x51, 0x24, 0x3b, 0x01, 0x87, 0x12, 0xa1, 0x70, 0x3d, 0xe4, 0x20, 0x41, 0x67, 0x3e, 0x9f, - 0xce, 0x4a, 0x60, 0x24, 0x56, 0xca, 0x83, 0x7a, 0xb2, 0x1f, 0x5f, 0x7e, 0xab, 0xf0, 0x72, 0x51, - 0x29, 0x25, 0x7e, 0x29, 0x6d, 0x11, 0xe2, 0x48, 0xa0, 0x11, 0xbc, 0x16, 0x6f, 0x4c, 0x41, 0x38, - 0x12, 0x70, 0x23, 0xd5, 0x9a, 0x91, 0x84, 0x89, 0x24, 0x6b, 0x89, 0x1e, 0x8d, 0x2a, 0x86, 0x95, - 0xa1, 0x4b, 0xf5, 0x95, 0x35, 0x36, 0xcc, 0x73, 0xf9, 0x86, 0x59, 0xd1, 0xde, 0x5a, 0x92, 0x63, - 0x64, 0x90, 0x3d, 0xa8, 0x87, 0x8c, 0xc5, 0xab, 0x9c, 0xcf, 0xa7, 0x72, 0x2d, 0xb0, 0x13, 0x27, - 0xd4, 0x87, 0x5a, 0xa2, 0x17, 0x33, 0xbd, 0x85, 0x89, 0xa6, 0x77, 0x35, 0xd6, 0x94, 0x99, 0x9c, - 0x0f, 0x8d, 0x34, 0x5b, 0x46, 0x70, 0x71, 0x22, 0xc1, 0x6a, 0x92, 0x3f, 0xad, 0x39, 0xb6, 0xd5, - 0x5a, 0xea, 0x2b, 0x6e, 0x64, 0x2f, 0x1d, 0x3b, 0x2a, 0xf4, 0x77, 0xc0, 0x43, 0x95, 0x7b, 0x5e, - 0xc3, 0x51, 0xa1, 0x03, 0x54, 0xd6, 0x51, 0xa1, 0xe5, 0x86, 0x47, 0x85, 0xae, 0x49, 0x3e, 0x2a, - 0xa2, 0x2d, 0x6a, 0x07, 0x77, 0xbf, 0x5b, 0x84, 0x99, 0x6d, 0xde, 0x43, 0x8f, 0xa1, 0x18, 0x6c, - 0x8f, 0xe8, 0x76, 0xe2, 0xd9, 0x34, 0x1e, 0xe3, 0xac, 0xb7, 0xf2, 0x81, 0x4d, 0x90, 0x08, 0x74, - 0x5a, 0xd4, 0xcd, 0xa1, 0x73, 0x12, 0x9f, 0x72, 0xe8, 0x84, 0x03, 0x4b, 0x1f, 0xce, 0x85, 0x22, - 0x02, 0x5a, 0x4d, 0x2b, 0x1e, 0x8b, 0x2e, 0x96, 0x9d, 0x17, 0x6e, 0xd4, 0xba, 0x30, 0x3f, 0x0c, - 0x18, 0xe8, 0x56, 0x4a, 0xed, 0x48, 0x36, 0xb1, 0x6e, 0xe7, 0xc2, 0x46, 0x45, 0x64, 0x30, 0xc9, - 0x14, 0x09, 0x65, 0x9a, 0x4c, 0x91, 0x70, 0xd2, 0x41, 0x0c, 0xce, 0x87, 0xc3, 0x0c, 0x4a, 0x9b, - 0x44, 0x4c, 0x1e, 0xb2, 0x9a, 0xb9, 0xf1, 0x46, 0xf0, 0x00, 0x16, 0xa3, 0x81, 0x00, 0xdd, 0xc9, - 0xa4, 0x18, 0x09, 0x53, 0xd6, 0xda, 0x04, 0x15, 0x46, 0xf6, 0x2b, 0x19, 0xd2, 0xc7, 0xa3, 0x04, - 0x7a, 0x27, 0x93, 0x2a, 0x2e, 0x9b, 0x58, 0xf7, 0x26, 0x2d, 0x4b, 0x68, 0xc3, 0xc4, 0x91, 0xdc, - 0x6d, 0x44, 0xf3, 0x4d, 0xee, 0x36, 0x46, 0x52, 0x0f, 0xfa, 0xa6, 0x00, 0x4b, 0xf1, 0x79, 0x06, - 0xbd, 0x97, 0x93, 0x72, 0x2c, 0x26, 0x59, 0xef, 0x9f, 0xa2, 0xd2, 0xf4, 0xf3, 0xbc, 0x00, 0xcb, - 0x09, 0xa9, 0x08, 0x65, 0xd3, 0x26, 0xc5, 0x2d, 0x6b, 0xfd, 0x34, 0xa5, 0xa6, 0xa5, 0xaf, 0x0b, - 0x50, 0x8a, 0x8b, 0x0d, 0xe8, 0x5e, 0x4e, 0xd2, 0x91, 0xc8, 0x66, 0xbd, 0x3b, 0x71, 0x9d, 0xe9, - 0xe4, 0x08, 0x2e, 0x8c, 0x7c, 0xf8, 0xa3, 0xb4, 0x3f, 0x80, 0xf8, 0x1c, 0x63, 0xdd, 0x9d, 0xa4, - 0xc4, 0x28, 0xfb, 0xb0, 0x10, 0x39, 0x07, 0x51, 0x33, 0x9d, 0x64, 0x2c, 0x7d, 0x58, 0x77, 0xf2, - 0x17, 0x44, 0xdc, 0x86, 0xcf, 0xae, 0x2c, 0xb7, 0x31, 0x47, 0x71, 0x96, 0xdb, 0xb8, 0xa3, 0xb1, - 0x45, 0x5e, 0xbc, 0xaa, 0x16, 0x5e, 0xbe, 0xaa, 0x16, 0xfe, 0x7e, 0x55, 0x2d, 0x3c, 0x3f, 0xae, - 0x4e, 0xbd, 0x3c, 0xae, 0x4e, 0xfd, 0x71, 0x5c, 0x9d, 0x82, 0x0a, 0x65, 0x09, 0x7c, 0x0f, 0x0b, - 0x9f, 0xdb, 0xa1, 0x9f, 0x4a, 0x4e, 0x40, 0xab, 0x94, 0x85, 0xae, 0x9a, 0x47, 0xc1, 0xcf, 0xa5, - 0x9d, 0x39, 0xf5, 0x2b, 0xe9, 0xdb, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xaa, 0xfa, 0xf3, 0x45, - 0x39, 0x16, 0x00, 0x00, + // 1414 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0xcf, 0x6f, 0x1b, 0xc5, + 0x17, 0xcf, 0x36, 0x69, 0x1a, 0xbf, 0xb6, 0x69, 0x3b, 0x75, 0x13, 0x7b, 0xdb, 0x38, 0xae, 0xfb, + 0x3d, 0xf4, 0xdb, 0x92, 0x75, 0x53, 0x44, 0x81, 0x88, 0x4b, 0x1c, 0x48, 0xc5, 0x21, 0xa2, 0xda, + 0x50, 0x21, 0xc1, 0xc1, 0x1a, 0x7b, 0xa7, 0x9b, 0x55, 0xd6, 0x3b, 0xe9, 0xce, 0xd8, 0x4d, 0x05, + 0x27, 0x84, 0xc4, 0x09, 0xa9, 0x47, 0xae, 0x5c, 0x39, 0x81, 0xc4, 0x05, 0xfe, 0x82, 0x8a, 0x53, + 0xc5, 0x89, 0x13, 0xa0, 0xe6, 0xc0, 0x9f, 0xc0, 0x15, 0xed, 0xcc, 0x78, 0xbd, 0x6b, 0xef, 0x2f, + 0x87, 0x06, 0x4e, 0xc9, 0xee, 0xbe, 0xf7, 0xf9, 0xf1, 0x76, 0x67, 0xde, 0x3c, 0xc3, 0xea, 0x81, + 0x4f, 0x07, 0xc4, 0xc3, 0x5e, 0x97, 0x34, 0xc9, 0x61, 0x77, 0x0f, 0x7b, 0x36, 0x69, 0x0e, 0xd6, + 0x9b, 0xfc, 0xd0, 0x38, 0xf0, 0x29, 0xa7, 0x68, 0x69, 0x14, 0x60, 0x0c, 0x03, 0x8c, 0xc1, 0xba, + 0x5e, 0xeb, 0x52, 0xd6, 0xa3, 0xac, 0xd9, 0xc1, 0x2c, 0x48, 0xe8, 0x10, 0x8e, 0xd7, 0x9b, 0x5d, + 0xea, 0x78, 0x32, 0x4f, 0x5f, 0x56, 0xcf, 0x7b, 0xcc, 0x0e, 0xf0, 0x7a, 0xcc, 0x56, 0x0f, 0xaa, + 0xf2, 0x41, 0x5b, 0x5c, 0x35, 0xe5, 0x85, 0x7a, 0x54, 0xb6, 0xa9, 0x4d, 0xe5, 0xfd, 0xe0, 0x3f, + 0x75, 0xf7, 0x46, 0x8a, 0xc4, 0x1e, 0xf6, 0xf7, 0x09, 0xcf, 0x09, 0xa2, 0xbe, 0x45, 0x7c, 0x96, + 0x13, 0x74, 0x80, 0x7d, 0xdc, 0x53, 0x41, 0x8d, 0x9f, 0x34, 0xb8, 0xbc, 0xc3, 0xec, 0x2d, 0x9f, + 0x60, 0x4e, 0x36, 0xd9, 0xbe, 0x49, 0x1e, 0xf7, 0x09, 0xe3, 0x68, 0x0b, 0x4a, 0x98, 0xed, 0xb7, + 0x05, 0x60, 0x45, 0xab, 0x6b, 0x37, 0xcf, 0xde, 0xad, 0x1b, 0xc9, 0xc5, 0x31, 0x36, 0xd9, 0xfe, + 0x07, 0x41, 0x5c, 0x6b, 0xee, 0xf9, 0x6f, 0xab, 0x33, 0xe6, 0x02, 0x56, 0xd7, 0xe8, 0x3e, 0x20, + 0x01, 0xd0, 0xee, 0x06, 0xf0, 0x0e, 0xf5, 0xda, 0x8f, 0x08, 0xa9, 0x9c, 0x12, 0x68, 0x55, 0x43, + 0x15, 0x23, 0x28, 0xa9, 0xa1, 0x4a, 0x6a, 0x6c, 0x51, 0xc7, 0x33, 0x2f, 0x8a, 0xa4, 0x2d, 0x95, + 0xb3, 0x4d, 0xc8, 0xc6, 0x95, 0xcf, 0xff, 0xfc, 0xee, 0xd6, 0xc5, 0x50, 0x90, 0xc1, 0x88, 0xeb, + 0x12, 0xbf, 0xb1, 0x0e, 0xe5, 0xb8, 0x76, 0x76, 0x40, 0x3d, 0x46, 0x50, 0x15, 0x16, 0x24, 0xaf, + 0x63, 0x09, 0xed, 0x73, 0xe6, 0x19, 0x71, 0xfd, 0xbe, 0xd5, 0xf8, 0x31, 0xea, 0xb7, 0xe5, 0x58, + 0x11, 0xbf, 0x1d, 0xc7, 0x2a, 0xe6, 0xb7, 0xe5, 0x58, 0x31, 0xbf, 0x1d, 0x75, 0xfd, 0xea, 0xfc, + 0x96, 0x03, 0xbf, 0x17, 0x42, 0x41, 0x46, 0xa7, 0xff, 0x74, 0xcc, 0xae, 0x90, 0x9e, 0x6f, 0xd7, + 0x83, 0x2b, 0x41, 0x4a, 0x60, 0xc1, 0x15, 0x1a, 0x87, 0x7e, 0x0d, 0x38, 0x4d, 0x9f, 0x78, 0xca, + 0x6b, 0xa9, 0x55, 0xf9, 0xe5, 0x87, 0xb5, 0xb2, 0x12, 0xb8, 0x69, 0x59, 0x3e, 0x61, 0x6c, 0x97, + 0xfb, 0x8e, 0x67, 0x9b, 0x32, 0x2c, 0xc6, 0x71, 0x2a, 0xc6, 0xb1, 0x01, 0x81, 0x58, 0x19, 0xd6, + 0xa8, 0xc0, 0xd2, 0x38, 0x9f, 0x14, 0xd9, 0x28, 0x03, 0xda, 0x61, 0xf6, 0xb6, 0xe3, 0xba, 0x2d, + 0xc7, 0x62, 0x4a, 0x46, 0xe3, 0x8a, 0x78, 0x1b, 0xa3, 0xbb, 0x13, 0xc1, 0x9b, 0x6c, 0x3f, 0x21, + 0x58, 0xde, 0x55, 0xc1, 0x8f, 0x05, 0xe7, 0x8e, 0x58, 0x1f, 0xbb, 0x84, 0x73, 0x97, 0x44, 0x4c, + 0x62, 0xab, 0xe7, 0x78, 0xf9, 0x26, 0x45, 0x18, 0xba, 0x0a, 0x25, 0xb9, 0xcc, 0x86, 0x2e, 0xcf, + 0x9b, 0x0b, 0xf2, 0x46, 0x68, 0x53, 0x04, 0x36, 0xaa, 0xb0, 0x3c, 0x41, 0xa9, 0xd4, 0xfc, 0xa5, + 0x41, 0x25, 0x7c, 0xf6, 0x91, 0xc3, 0xf7, 0x2c, 0x1f, 0x3f, 0x39, 0x09, 0x41, 0x68, 0x05, 0x80, + 0xd3, 0x36, 0x96, 0x79, 0x95, 0xd9, 0x00, 0xd1, 0x2c, 0x71, 0xaa, 0x80, 0x50, 0x17, 0xe6, 0x71, + 0x8f, 0xf6, 0x3d, 0x5e, 0x99, 0xab, 0xcf, 0x66, 0x7e, 0x80, 0xad, 0x3b, 0xc1, 0x77, 0xfc, 0xed, + 0xef, 0xab, 0x37, 0x6d, 0x87, 0xef, 0xf5, 0x3b, 0x46, 0x97, 0xf6, 0xd4, 0x56, 0xa5, 0xfe, 0xac, + 0x31, 0x6b, 0xbf, 0xc9, 0x9f, 0x1e, 0x10, 0x26, 0x12, 0x98, 0xa9, 0xa0, 0x63, 0x45, 0xb9, 0x0a, + 0xd5, 0x04, 0xe3, 0xaa, 0x2c, 0x87, 0xb0, 0x12, 0x3e, 0x7c, 0x78, 0x60, 0x61, 0x4e, 0xde, 0x25, + 0x1c, 0x3b, 0x2e, 0x3b, 0xf1, 0x77, 0x55, 0x87, 0x5a, 0x1a, 0x73, 0xaa, 0xb6, 0xf7, 0x3c, 0xdc, + 0x71, 0x89, 0xf5, 0x1f, 0x68, 0x0b, 0x99, 0x95, 0xb6, 0x4f, 0xa1, 0x3e, 0x16, 0xf1, 0x90, 0x11, + 0xff, 0x5f, 0xfa, 0xcc, 0x6f, 0xc0, 0xf5, 0x0c, 0x72, 0xa5, 0xf0, 0xb3, 0x48, 0xd0, 0x0e, 0xf6, + 0xb0, 0x4d, 0x1e, 0x10, 0xbf, 0xe7, 0x30, 0xe6, 0x50, 0xef, 0xe4, 0xdf, 0xee, 0xff, 0xa0, 0x91, + 0xc5, 0xae, 0x34, 0x7e, 0x7f, 0x2a, 0x52, 0x68, 0x19, 0x66, 0x92, 0xc7, 0x9b, 0x9c, 0xfb, 0x27, + 0xa2, 0x10, 0xfd, 0x1f, 0x2e, 0x89, 0x16, 0x40, 0xda, 0x41, 0xcf, 0x92, 0xab, 0xb4, 0x32, 0x5b, + 0x9f, 0xbd, 0x59, 0x32, 0x17, 0xbb, 0xc3, 0x76, 0xf5, 0x61, 0xb0, 0x54, 0x51, 0x13, 0xca, 0xf1, + 0x50, 0x9f, 0xf4, 0xe8, 0x80, 0x88, 0x45, 0x5b, 0x32, 0x2f, 0x45, 0xa2, 0x4d, 0xf1, 0x20, 0x82, + 0x1d, 0xf4, 0x07, 0x85, 0x7d, 0x3a, 0x8a, 0xdd, 0x72, 0xac, 0x71, 0x6c, 0x15, 0xaa, 0xb0, 0xe7, + 0xa3, 0xd8, 0x22, 0x5a, 0x62, 0xc7, 0x2a, 0x7b, 0x1d, 0x56, 0x53, 0x4b, 0xa6, 0xca, 0xfa, 0x8d, + 0x26, 0x96, 0xfc, 0x7d, 0x3a, 0x90, 0x4d, 0x49, 0x06, 0x0f, 0x2b, 0x7a, 0x0f, 0x4a, 0xb8, 0xcf, + 0xf7, 0xa8, 0xef, 0xf0, 0xa7, 0xb9, 0x55, 0x1d, 0x85, 0xa2, 0x77, 0x60, 0x5e, 0x16, 0x52, 0x75, + 0xce, 0x5a, 0x5a, 0x1f, 0x96, 0x74, 0xaa, 0x0b, 0xab, 0x9c, 0x8d, 0xc5, 0xc0, 0xc2, 0x08, 0xad, + 0x71, 0x0d, 0xf4, 0x24, 0x89, 0xca, 0xc1, 0xcf, 0x20, 0x76, 0xf2, 0xfb, 0x74, 0x20, 0x2d, 0x6e, + 0x13, 0xc2, 0xfe, 0xa9, 0xfe, 0xcc, 0x2f, 0xe3, 0x21, 0x2c, 0x63, 0xcb, 0x0a, 0xce, 0x05, 0xed, + 0xc8, 0x6b, 0x7f, 0xe4, 0x62, 0x2e, 0xbe, 0x8f, 0xcc, 0x6d, 0x5a, 0x1a, 0xbd, 0x8c, 0x2d, 0x6b, + 0x9b, 0x90, 0xf0, 0xd4, 0xb3, 0xed, 0x62, 0x8e, 0x3e, 0x01, 0x5d, 0xbe, 0xdb, 0x44, 0xe4, 0xb9, + 0x62, 0xc8, 0x4b, 0x12, 0x62, 0x02, 0x7c, 0x52, 0x73, 0xf0, 0x39, 0x09, 0xe4, 0xd3, 0xc7, 0xd0, + 0xdc, 0x72, 0xac, 0x74, 0xcd, 0x21, 0xf2, 0xfc, 0xf1, 0x34, 0x0f, 0xc1, 0xbb, 0x50, 0x1b, 0x6a, + 0x96, 0x87, 0xc5, 0x36, 0x13, 0xdb, 0x56, 0x8f, 0x78, 0x5c, 0x12, 0x9c, 0x29, 0x46, 0xa0, 0x4b, + 0xe9, 0xbb, 0x02, 0x64, 0x37, 0xc4, 0x10, 0x24, 0x0e, 0x5c, 0x8f, 0x38, 0x48, 0xe1, 0x59, 0x28, + 0xc6, 0xb3, 0x12, 0x1a, 0x49, 0xa4, 0xf2, 0xa0, 0x9e, 0xee, 0xc7, 0x0f, 0x0e, 0x8e, 0xac, 0x52, + 0x12, 0x4c, 0xa9, 0xc7, 0xd6, 0x6d, 0x42, 0xcc, 0x20, 0x50, 0x11, 0x5e, 0x4b, 0x36, 0x26, 0x42, + 0x18, 0xe2, 0x70, 0x23, 0xd3, 0x9a, 0xa2, 0x84, 0xa9, 0x28, 0x57, 0x53, 0x3d, 0x2a, 0x56, 0x0c, + 0x2b, 0x43, 0x97, 0xe2, 0xc8, 0x3b, 0x51, 0xcc, 0xb3, 0xc5, 0x8a, 0x59, 0x95, 0xde, 0x5a, 0x01, + 0xc6, 0x58, 0x21, 0x6d, 0xa8, 0x47, 0x8c, 0x25, 0xb3, 0x9c, 0x2b, 0xc6, 0x72, 0x2d, 0xb4, 0x93, + 0x44, 0xe4, 0xc2, 0x6a, 0xaa, 0x17, 0x55, 0xbd, 0xf3, 0x53, 0x55, 0xef, 0x6a, 0xa2, 0x29, 0x55, + 0x39, 0x1f, 0x1a, 0x59, 0xb6, 0x14, 0xe1, 0xe2, 0x54, 0x84, 0xb5, 0x34, 0x7f, 0x92, 0x73, 0x62, + 0xab, 0xd5, 0xc5, 0xc9, 0x77, 0x6c, 0x2f, 0x9d, 0x68, 0x15, 0xf2, 0x20, 0xf1, 0x40, 0x0c, 0xa1, + 0xaf, 0xa0, 0x55, 0xc8, 0x69, 0x36, 0xaf, 0x55, 0x48, 0xba, 0x61, 0xab, 0x90, 0x39, 0xe9, 0xad, + 0x22, 0x2e, 0x51, 0x3a, 0xb8, 0xfb, 0xf5, 0x22, 0xcc, 0xee, 0x30, 0x1b, 0x3d, 0x82, 0x52, 0xb8, + 0x3d, 0xa2, 0xdb, 0xa9, 0xbd, 0x69, 0x72, 0xa6, 0xd6, 0x5f, 0x2b, 0x16, 0xac, 0xa6, 0xba, 0x90, + 0xa7, 0xe5, 0x58, 0x05, 0x78, 0x46, 0xb3, 0x6c, 0x01, 0x9e, 0xe8, 0xf4, 0xe8, 0xc2, 0xd9, 0xc8, + 0xbc, 0x86, 0xd6, 0xb2, 0x92, 0x27, 0xe6, 0x48, 0xdd, 0x28, 0x1a, 0xae, 0xd8, 0xba, 0xb0, 0x30, + 0x9c, 0xf6, 0xd0, 0xad, 0x8c, 0xdc, 0xb1, 0x41, 0x51, 0xbf, 0x5d, 0x28, 0x36, 0x4e, 0x12, 0x4c, + 0x89, 0xb9, 0x24, 0x91, 0x01, 0x33, 0x97, 0x24, 0x3a, 0x76, 0x22, 0x0a, 0xe7, 0xa2, 0x03, 0x20, + 0xca, 0xaa, 0x44, 0xc2, 0x70, 0xaa, 0x37, 0x0b, 0xc7, 0x2b, 0xc2, 0x3e, 0x2c, 0xc6, 0x87, 0x2b, + 0x74, 0x27, 0x17, 0x62, 0x6c, 0x00, 0xd5, 0xd7, 0xa7, 0xc8, 0x50, 0xb4, 0x5f, 0x68, 0x70, 0x39, + 0x61, 0x7a, 0x42, 0x6f, 0xe4, 0x42, 0x25, 0xcd, 0x79, 0xfa, 0xbd, 0x69, 0xd3, 0x52, 0x64, 0xa8, + 0x41, 0xa9, 0xb0, 0x8c, 0xf8, 0x48, 0x57, 0x58, 0xc6, 0xd8, 0x3c, 0x86, 0xbe, 0xd2, 0x60, 0x29, + 0x79, 0x20, 0x42, 0x6f, 0x15, 0x84, 0x9c, 0x18, 0xe0, 0xf4, 0xb7, 0x8f, 0x91, 0xa9, 0xf4, 0x3c, + 0xd3, 0x60, 0x39, 0x65, 0xfa, 0x41, 0xf9, 0xb0, 0x69, 0xf3, 0x9a, 0xbe, 0x71, 0x9c, 0x54, 0x25, + 0xe9, 0x4b, 0x0d, 0xca, 0x49, 0x63, 0x03, 0xba, 0x57, 0x10, 0x74, 0x6c, 0x34, 0xd3, 0xdf, 0x9c, + 0x3a, 0x4f, 0x29, 0x39, 0x84, 0x0b, 0x63, 0x07, 0x7f, 0x94, 0xb5, 0x00, 0x92, 0xe7, 0x18, 0xfd, + 0xee, 0x34, 0x29, 0x8a, 0xd9, 0x87, 0xf3, 0xb1, 0x3e, 0x88, 0x9a, 0xd9, 0x20, 0x13, 0xd3, 0x87, + 0x7e, 0xa7, 0x78, 0x42, 0xcc, 0x6d, 0xb4, 0x77, 0xe5, 0xb9, 0x4d, 0x68, 0xc5, 0x79, 0x6e, 0x93, + 0x5a, 0x63, 0x8b, 0x3c, 0x7f, 0x59, 0xd3, 0x5e, 0xbc, 0xac, 0x69, 0x7f, 0xbc, 0xac, 0x69, 0xcf, + 0x8e, 0x6a, 0x33, 0x2f, 0x8e, 0x6a, 0x33, 0xbf, 0x1e, 0xd5, 0x66, 0xa0, 0xea, 0xd0, 0x14, 0xbc, + 0x07, 0xda, 0xc7, 0x46, 0xe4, 0xe7, 0xa6, 0x51, 0xd0, 0x9a, 0x43, 0x23, 0x57, 0xcd, 0xc3, 0xf0, + 0xb7, 0xeb, 0xce, 0xbc, 0xf8, 0xc9, 0xfa, 0xf5, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0xd0, 0x5f, + 0xe8, 0x90, 0xc6, 0x17, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2526,6 +2616,18 @@ func (m *MsgMarketSettleRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if m.MarketId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Admin) > 0 { + i -= len(m.Admin) + copy(dAtA[i:], m.Admin) + i = encodeVarintTx(dAtA, i, uint64(len(m.Admin))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2598,10 +2700,10 @@ func (m *MsgMarketWithdrawRequest) MarshalToSizedBuffer(dAtA []byte) (int, error i-- dAtA[i] = 0x10 } - if len(m.Administrator) > 0 { - i -= len(m.Administrator) - copy(dAtA[i:], m.Administrator) - i = encodeVarintTx(dAtA, i, uint64(len(m.Administrator))) + if len(m.Admin) > 0 { + i -= len(m.Admin) + copy(dAtA[i:], m.Admin) + i = encodeVarintTx(dAtA, i, uint64(len(m.Admin))) i-- dAtA[i] = 0xa } @@ -2651,6 +2753,18 @@ func (m *MsgMarketUpdateDetailsRequest) MarshalToSizedBuffer(dAtA []byte) (int, _ = i var l int _ = l + if m.MarketId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Admin) > 0 { + i -= len(m.Admin) + copy(dAtA[i:], m.Admin) + i = encodeVarintTx(dAtA, i, uint64(len(m.Admin))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2697,6 +2811,18 @@ func (m *MsgMarketUpdateEnabledRequest) MarshalToSizedBuffer(dAtA []byte) (int, _ = i var l int _ = l + if m.MarketId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Admin) > 0 { + i -= len(m.Admin) + copy(dAtA[i:], m.Admin) + i = encodeVarintTx(dAtA, i, uint64(len(m.Admin))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2743,6 +2869,18 @@ func (m *MsgMarketUpdateUserSettleRequest) MarshalToSizedBuffer(dAtA []byte) (in _ = i var l int _ = l + if m.MarketId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Admin) > 0 { + i -= len(m.Admin) + copy(dAtA[i:], m.Admin) + i = encodeVarintTx(dAtA, i, uint64(len(m.Admin))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2789,6 +2927,18 @@ func (m *MsgMarketManagePermissionsRequest) MarshalToSizedBuffer(dAtA []byte) (i _ = i var l int _ = l + if m.MarketId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Admin) > 0 { + i -= len(m.Admin) + copy(dAtA[i:], m.Admin) + i = encodeVarintTx(dAtA, i, uint64(len(m.Admin))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2876,10 +3026,10 @@ func (m *MsgMarketManageReqAttrsRequest) MarshalToSizedBuffer(dAtA []byte) (int, i-- dAtA[i] = 0x10 } - if len(m.Administrator) > 0 { - i -= len(m.Administrator) - copy(dAtA[i:], m.Administrator) - i = encodeVarintTx(dAtA, i, uint64(len(m.Administrator))) + if len(m.Admin) > 0 { + i -= len(m.Admin) + copy(dAtA[i:], m.Admin) + i = encodeVarintTx(dAtA, i, uint64(len(m.Admin))) i-- dAtA[i] = 0xa } @@ -3393,6 +3543,13 @@ func (m *MsgMarketSettleRequest) Size() (n int) { } var l int _ = l + l = len(m.Admin) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.MarketId != 0 { + n += 1 + sovTx(uint64(m.MarketId)) + } return n } @@ -3411,7 +3568,7 @@ func (m *MsgMarketWithdrawRequest) Size() (n int) { } var l int _ = l - l = len(m.Administrator) + l = len(m.Admin) if l > 0 { n += 1 + l + sovTx(uint64(l)) } @@ -3446,6 +3603,13 @@ func (m *MsgMarketUpdateDetailsRequest) Size() (n int) { } var l int _ = l + l = len(m.Admin) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.MarketId != 0 { + n += 1 + sovTx(uint64(m.MarketId)) + } return n } @@ -3464,6 +3628,13 @@ func (m *MsgMarketUpdateEnabledRequest) Size() (n int) { } var l int _ = l + l = len(m.Admin) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.MarketId != 0 { + n += 1 + sovTx(uint64(m.MarketId)) + } return n } @@ -3482,6 +3653,13 @@ func (m *MsgMarketUpdateUserSettleRequest) Size() (n int) { } var l int _ = l + l = len(m.Admin) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.MarketId != 0 { + n += 1 + sovTx(uint64(m.MarketId)) + } return n } @@ -3500,6 +3678,13 @@ func (m *MsgMarketManagePermissionsRequest) Size() (n int) { } var l int _ = l + l = len(m.Admin) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.MarketId != 0 { + n += 1 + sovTx(uint64(m.MarketId)) + } return n } @@ -3518,7 +3703,7 @@ func (m *MsgMarketManageReqAttrsRequest) Size() (n int) { } var l int _ = l - l = len(m.Administrator) + l = len(m.Admin) if l > 0 { n += 1 + l + sovTx(uint64(l)) } @@ -4468,6 +4653,57 @@ func (m *MsgMarketSettleRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgMarketSettleRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Admin", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Admin = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -4570,7 +4806,7 @@ func (m *MsgMarketWithdrawRequest) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Administrator", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Admin", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -4598,7 +4834,7 @@ func (m *MsgMarketWithdrawRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Administrator = string(dAtA[iNdEx:postIndex]) + m.Admin = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { @@ -4785,6 +5021,57 @@ func (m *MsgMarketUpdateDetailsRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgMarketUpdateDetailsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Admin", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Admin = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -4885,6 +5172,57 @@ func (m *MsgMarketUpdateEnabledRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgMarketUpdateEnabledRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Admin", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Admin = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -4985,6 +5323,57 @@ func (m *MsgMarketUpdateUserSettleRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgMarketUpdateUserSettleRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Admin", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Admin = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -5085,6 +5474,57 @@ func (m *MsgMarketManagePermissionsRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgMarketManagePermissionsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Admin", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Admin = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -5187,7 +5627,7 @@ func (m *MsgMarketManageReqAttrsRequest) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Administrator", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Admin", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -5215,7 +5655,7 @@ func (m *MsgMarketManageReqAttrsRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Administrator = string(dAtA[iNdEx:postIndex]) + m.Admin = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { From db03bf374924ad468b95afafa31b702f3877096c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 11:28:02 -0600 Subject: [PATCH 119/309] [1658]: Implement GetSigners and the admin and market id validation pieces to the rest of the market management messages. --- x/exchange/msg.go | 80 ++++++++++++++++++++++++++++++++++-------- x/exchange/msg_test.go | 20 ++++++++--- 2 files changed, 80 insertions(+), 20 deletions(-) diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 6fbb9d4dc0..dc7bb7f4ab 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -95,13 +95,23 @@ func (m MsgFillAsksRequest) GetSigners() []sdk.AccAddress { } func (m MsgMarketSettleRequest) ValidateBasic() error { + var errs []error + + if _, err := sdk.AccAddressFromBech32(m.Admin); err != nil { + errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Admin, err)) + } + + if m.MarketId == 0 { + errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) + } + // TODO[1658]: MsgMarketSettleRequest.ValidateBasic() - return nil + return errors.Join(errs...) } func (m MsgMarketSettleRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgMarketSettleRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Admin) + return []sdk.AccAddress{addr} } func (m MsgMarketWithdrawRequest) ValidateBasic() error { @@ -134,43 +144,83 @@ func (m MsgMarketWithdrawRequest) GetSigners() []sdk.AccAddress { } func (m MsgMarketUpdateDetailsRequest) ValidateBasic() error { + var errs []error + + if _, err := sdk.AccAddressFromBech32(m.Admin); err != nil { + errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Admin, err)) + } + + if m.MarketId == 0 { + errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) + } + // TODO[1658]: MsgMarketUpdateDetailsRequest.ValidateBasic() - return nil + return errors.Join(errs...) } func (m MsgMarketUpdateDetailsRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgMarketUpdateDetailsRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Admin) + return []sdk.AccAddress{addr} } func (m MsgMarketUpdateEnabledRequest) ValidateBasic() error { + var errs []error + + if _, err := sdk.AccAddressFromBech32(m.Admin); err != nil { + errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Admin, err)) + } + + if m.MarketId == 0 { + errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) + } + // TODO[1658]: MsgMarketUpdateEnabledRequest.ValidateBasic() - return nil + return errors.Join(errs...) } func (m MsgMarketUpdateEnabledRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgMarketUpdateEnabledRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Admin) + return []sdk.AccAddress{addr} } func (m MsgMarketUpdateUserSettleRequest) ValidateBasic() error { + var errs []error + + if _, err := sdk.AccAddressFromBech32(m.Admin); err != nil { + errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Admin, err)) + } + + if m.MarketId == 0 { + errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) + } + // TODO[1658]: MsgMarketUpdateUserSettleRequest.ValidateBasic() - return nil + return errors.Join(errs...) } func (m MsgMarketUpdateUserSettleRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgMarketUpdateUserSettleRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Admin) + return []sdk.AccAddress{addr} } func (m MsgMarketManagePermissionsRequest) ValidateBasic() error { + var errs []error + + if _, err := sdk.AccAddressFromBech32(m.Admin); err != nil { + errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Admin, err)) + } + + if m.MarketId == 0 { + errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) + } + // TODO[1658]: MsgMarketManagePermissionsRequest.ValidateBasic() - return nil + return errors.Join(errs...) } func (m MsgMarketManagePermissionsRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgMarketManagePermissionsRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Admin) + return []sdk.AccAddress{addr} } func (m MsgMarketManageReqAttrsRequest) ValidateBasic() error { diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 1a6fe73166..94364629f1 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -41,14 +41,24 @@ func TestAllMsgsGetSigners(t *testing.T) { }, // TODO[1658]: Add MsgFillBidsRequest once it's actually been defined. // TODO[1658]: Add MsgFillAsksRequest once it's actually been defined. - // TODO[1658]: Add MsgMarketSettleRequest once it's actually been defined. + func(signer string) sdk.Msg { + return &MsgMarketSettleRequest{Admin: signer} + }, func(signer string) sdk.Msg { return &MsgMarketWithdrawRequest{Admin: signer} }, - // TODO[1658]: Add MsgMarketUpdateDetailsRequest once it's actually been defined. - // TODO[1658]: Add MsgMarketUpdateEnabledRequest once it's actually been defined. - // TODO[1658]: Add MsgMarketUpdateUserSettleRequest once it's actually been defined. - // TODO[1658]: Add MsgMarketManagePermissionsRequest once it's actually been defined. + func(signer string) sdk.Msg { + return &MsgMarketUpdateDetailsRequest{Admin: signer} + }, + func(signer string) sdk.Msg { + return &MsgMarketUpdateEnabledRequest{Admin: signer} + }, + func(signer string) sdk.Msg { + return &MsgMarketUpdateUserSettleRequest{Admin: signer} + }, + func(signer string) sdk.Msg { + return &MsgMarketManagePermissionsRequest{Admin: signer} + }, func(signer string) sdk.Msg { return &MsgMarketManageReqAttrsRequest{Admin: signer} }, From c7802381f1f43144179e095b277ebd8bf3804dda Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 11:35:22 -0600 Subject: [PATCH 120/309] [1658]: Fill in the fields of MsgMarketManagePermissionsRequest. --- docs/proto-docs.md | 7 +- proto/provenance/exchange/v1/tx.proto | 8 +- x/exchange/tx.pb.go | 365 +++++++++++++++++++------- 3 files changed, 286 insertions(+), 94 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 4802198363..5f57a1183b 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2348,9 +2348,10 @@ MsgMarketManagePermissionsRequest is a request message for the MarketManagePermi | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `admin` | [string](#string) | | admin is the account with "permissions" permission requesting this change. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. - -TODO[1658]: MsgMarketManagePermissionsRequest | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | +| `revoke_all` | [string](#string) | repeated | revoke_all are addresses that should have all their permissions revoked. | +| `to_revoke` | [AccessGrant](#provenance.exchange.v1.AccessGrant) | repeated | to_revoke are the specific permissions to remove for addresses. | +| `to_grant` | [AccessGrant](#provenance.exchange.v1.AccessGrant) | repeated | to_grant are the permissions to grant to addresses. | diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 62c574d5c6..a8258cdffe 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -208,7 +208,13 @@ message MsgMarketManagePermissionsRequest { string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // market_id is the numerical identifier of the market to update required attributes for. uint32 market_id = 2; - // TODO[1658]: MsgMarketManagePermissionsRequest + + // revoke_all are addresses that should have all their permissions revoked. + repeated string revoke_all = 3; + // to_revoke are the specific permissions to remove for addresses. + repeated AccessGrant to_revoke = 4 [(gogoproto.nullable) = false]; + // to_grant are the permissions to grant to addresses. + repeated AccessGrant to_grant = 5 [(gogoproto.nullable) = false]; } // MsgMarketManagePermissionsResponse is a response message for the MarketManagePermissions endpoint. diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index caefa93ddb..5fc19cb7b7 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -958,6 +958,12 @@ type MsgMarketManagePermissionsRequest struct { Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` // market_id is the numerical identifier of the market to update required attributes for. MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // revoke_all are addresses that should have all their permissions revoked. + RevokeAll []string `protobuf:"bytes,3,rep,name=revoke_all,json=revokeAll,proto3" json:"revoke_all,omitempty"` + // to_revoke are the specific permissions to remove for addresses. + ToRevoke []AccessGrant `protobuf:"bytes,4,rep,name=to_revoke,json=toRevoke,proto3" json:"to_revoke"` + // to_grant are the permissions to grant to addresses. + ToGrant []AccessGrant `protobuf:"bytes,5,rep,name=to_grant,json=toGrant,proto3" json:"to_grant"` } func (m *MsgMarketManagePermissionsRequest) Reset() { *m = MsgMarketManagePermissionsRequest{} } @@ -1007,6 +1013,27 @@ func (m *MsgMarketManagePermissionsRequest) GetMarketId() uint32 { return 0 } +func (m *MsgMarketManagePermissionsRequest) GetRevokeAll() []string { + if m != nil { + return m.RevokeAll + } + return nil +} + +func (m *MsgMarketManagePermissionsRequest) GetToRevoke() []AccessGrant { + if m != nil { + return m.ToRevoke + } + return nil +} + +func (m *MsgMarketManagePermissionsRequest) GetToGrant() []AccessGrant { + if m != nil { + return m.ToGrant + } + return nil +} + // MsgMarketManagePermissionsResponse is a response message for the MarketManagePermissions endpoint. type MsgMarketManagePermissionsResponse struct { } @@ -1594,96 +1621,99 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1414 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0xcf, 0x6f, 0x1b, 0xc5, - 0x17, 0xcf, 0x36, 0x69, 0x1a, 0xbf, 0xb6, 0x69, 0x3b, 0x75, 0x13, 0x7b, 0xdb, 0x38, 0xae, 0xfb, - 0x3d, 0xf4, 0xdb, 0x92, 0x75, 0x53, 0x44, 0x81, 0x88, 0x4b, 0x1c, 0x48, 0xc5, 0x21, 0xa2, 0xda, - 0x50, 0x21, 0xc1, 0xc1, 0x1a, 0x7b, 0xa7, 0x9b, 0x55, 0xd6, 0x3b, 0xe9, 0xce, 0xd8, 0x4d, 0x05, - 0x27, 0x84, 0xc4, 0x09, 0xa9, 0x47, 0xae, 0x5c, 0x39, 0x81, 0xc4, 0x05, 0xfe, 0x82, 0x8a, 0x53, - 0xc5, 0x89, 0x13, 0xa0, 0xe6, 0xc0, 0x9f, 0xc0, 0x15, 0xed, 0xcc, 0x78, 0xbd, 0x6b, 0xef, 0x2f, - 0x87, 0x06, 0x4e, 0xc9, 0xee, 0xbe, 0xf7, 0xf9, 0xf1, 0x76, 0x67, 0xde, 0x3c, 0xc3, 0xea, 0x81, - 0x4f, 0x07, 0xc4, 0xc3, 0x5e, 0x97, 0x34, 0xc9, 0x61, 0x77, 0x0f, 0x7b, 0x36, 0x69, 0x0e, 0xd6, - 0x9b, 0xfc, 0xd0, 0x38, 0xf0, 0x29, 0xa7, 0x68, 0x69, 0x14, 0x60, 0x0c, 0x03, 0x8c, 0xc1, 0xba, - 0x5e, 0xeb, 0x52, 0xd6, 0xa3, 0xac, 0xd9, 0xc1, 0x2c, 0x48, 0xe8, 0x10, 0x8e, 0xd7, 0x9b, 0x5d, - 0xea, 0x78, 0x32, 0x4f, 0x5f, 0x56, 0xcf, 0x7b, 0xcc, 0x0e, 0xf0, 0x7a, 0xcc, 0x56, 0x0f, 0xaa, - 0xf2, 0x41, 0x5b, 0x5c, 0x35, 0xe5, 0x85, 0x7a, 0x54, 0xb6, 0xa9, 0x4d, 0xe5, 0xfd, 0xe0, 0x3f, - 0x75, 0xf7, 0x46, 0x8a, 0xc4, 0x1e, 0xf6, 0xf7, 0x09, 0xcf, 0x09, 0xa2, 0xbe, 0x45, 0x7c, 0x96, - 0x13, 0x74, 0x80, 0x7d, 0xdc, 0x53, 0x41, 0x8d, 0x9f, 0x34, 0xb8, 0xbc, 0xc3, 0xec, 0x2d, 0x9f, - 0x60, 0x4e, 0x36, 0xd9, 0xbe, 0x49, 0x1e, 0xf7, 0x09, 0xe3, 0x68, 0x0b, 0x4a, 0x98, 0xed, 0xb7, - 0x05, 0x60, 0x45, 0xab, 0x6b, 0x37, 0xcf, 0xde, 0xad, 0x1b, 0xc9, 0xc5, 0x31, 0x36, 0xd9, 0xfe, - 0x07, 0x41, 0x5c, 0x6b, 0xee, 0xf9, 0x6f, 0xab, 0x33, 0xe6, 0x02, 0x56, 0xd7, 0xe8, 0x3e, 0x20, - 0x01, 0xd0, 0xee, 0x06, 0xf0, 0x0e, 0xf5, 0xda, 0x8f, 0x08, 0xa9, 0x9c, 0x12, 0x68, 0x55, 0x43, - 0x15, 0x23, 0x28, 0xa9, 0xa1, 0x4a, 0x6a, 0x6c, 0x51, 0xc7, 0x33, 0x2f, 0x8a, 0xa4, 0x2d, 0x95, - 0xb3, 0x4d, 0xc8, 0xc6, 0x95, 0xcf, 0xff, 0xfc, 0xee, 0xd6, 0xc5, 0x50, 0x90, 0xc1, 0x88, 0xeb, - 0x12, 0xbf, 0xb1, 0x0e, 0xe5, 0xb8, 0x76, 0x76, 0x40, 0x3d, 0x46, 0x50, 0x15, 0x16, 0x24, 0xaf, - 0x63, 0x09, 0xed, 0x73, 0xe6, 0x19, 0x71, 0xfd, 0xbe, 0xd5, 0xf8, 0x31, 0xea, 0xb7, 0xe5, 0x58, - 0x11, 0xbf, 0x1d, 0xc7, 0x2a, 0xe6, 0xb7, 0xe5, 0x58, 0x31, 0xbf, 0x1d, 0x75, 0xfd, 0xea, 0xfc, - 0x96, 0x03, 0xbf, 0x17, 0x42, 0x41, 0x46, 0xa7, 0xff, 0x74, 0xcc, 0xae, 0x90, 0x9e, 0x6f, 0xd7, - 0x83, 0x2b, 0x41, 0x4a, 0x60, 0xc1, 0x15, 0x1a, 0x87, 0x7e, 0x0d, 0x38, 0x4d, 0x9f, 0x78, 0xca, - 0x6b, 0xa9, 0x55, 0xf9, 0xe5, 0x87, 0xb5, 0xb2, 0x12, 0xb8, 0x69, 0x59, 0x3e, 0x61, 0x6c, 0x97, - 0xfb, 0x8e, 0x67, 0x9b, 0x32, 0x2c, 0xc6, 0x71, 0x2a, 0xc6, 0xb1, 0x01, 0x81, 0x58, 0x19, 0xd6, - 0xa8, 0xc0, 0xd2, 0x38, 0x9f, 0x14, 0xd9, 0x28, 0x03, 0xda, 0x61, 0xf6, 0xb6, 0xe3, 0xba, 0x2d, - 0xc7, 0x62, 0x4a, 0x46, 0xe3, 0x8a, 0x78, 0x1b, 0xa3, 0xbb, 0x13, 0xc1, 0x9b, 0x6c, 0x3f, 0x21, - 0x58, 0xde, 0x55, 0xc1, 0x8f, 0x05, 0xe7, 0x8e, 0x58, 0x1f, 0xbb, 0x84, 0x73, 0x97, 0x44, 0x4c, - 0x62, 0xab, 0xe7, 0x78, 0xf9, 0x26, 0x45, 0x18, 0xba, 0x0a, 0x25, 0xb9, 0xcc, 0x86, 0x2e, 0xcf, - 0x9b, 0x0b, 0xf2, 0x46, 0x68, 0x53, 0x04, 0x36, 0xaa, 0xb0, 0x3c, 0x41, 0xa9, 0xd4, 0xfc, 0xa5, - 0x41, 0x25, 0x7c, 0xf6, 0x91, 0xc3, 0xf7, 0x2c, 0x1f, 0x3f, 0x39, 0x09, 0x41, 0x68, 0x05, 0x80, - 0xd3, 0x36, 0x96, 0x79, 0x95, 0xd9, 0x00, 0xd1, 0x2c, 0x71, 0xaa, 0x80, 0x50, 0x17, 0xe6, 0x71, - 0x8f, 0xf6, 0x3d, 0x5e, 0x99, 0xab, 0xcf, 0x66, 0x7e, 0x80, 0xad, 0x3b, 0xc1, 0x77, 0xfc, 0xed, - 0xef, 0xab, 0x37, 0x6d, 0x87, 0xef, 0xf5, 0x3b, 0x46, 0x97, 0xf6, 0xd4, 0x56, 0xa5, 0xfe, 0xac, - 0x31, 0x6b, 0xbf, 0xc9, 0x9f, 0x1e, 0x10, 0x26, 0x12, 0x98, 0xa9, 0xa0, 0x63, 0x45, 0xb9, 0x0a, - 0xd5, 0x04, 0xe3, 0xaa, 0x2c, 0x87, 0xb0, 0x12, 0x3e, 0x7c, 0x78, 0x60, 0x61, 0x4e, 0xde, 0x25, - 0x1c, 0x3b, 0x2e, 0x3b, 0xf1, 0x77, 0x55, 0x87, 0x5a, 0x1a, 0x73, 0xaa, 0xb6, 0xf7, 0x3c, 0xdc, - 0x71, 0x89, 0xf5, 0x1f, 0x68, 0x0b, 0x99, 0x95, 0xb6, 0x4f, 0xa1, 0x3e, 0x16, 0xf1, 0x90, 0x11, - 0xff, 0x5f, 0xfa, 0xcc, 0x6f, 0xc0, 0xf5, 0x0c, 0x72, 0xa5, 0xf0, 0xb3, 0x48, 0xd0, 0x0e, 0xf6, - 0xb0, 0x4d, 0x1e, 0x10, 0xbf, 0xe7, 0x30, 0xe6, 0x50, 0xef, 0xe4, 0xdf, 0xee, 0xff, 0xa0, 0x91, - 0xc5, 0xae, 0x34, 0x7e, 0x7f, 0x2a, 0x52, 0x68, 0x19, 0x66, 0x92, 0xc7, 0x9b, 0x9c, 0xfb, 0x27, - 0xa2, 0x10, 0xfd, 0x1f, 0x2e, 0x89, 0x16, 0x40, 0xda, 0x41, 0xcf, 0x92, 0xab, 0xb4, 0x32, 0x5b, - 0x9f, 0xbd, 0x59, 0x32, 0x17, 0xbb, 0xc3, 0x76, 0xf5, 0x61, 0xb0, 0x54, 0x51, 0x13, 0xca, 0xf1, - 0x50, 0x9f, 0xf4, 0xe8, 0x80, 0x88, 0x45, 0x5b, 0x32, 0x2f, 0x45, 0xa2, 0x4d, 0xf1, 0x20, 0x82, - 0x1d, 0xf4, 0x07, 0x85, 0x7d, 0x3a, 0x8a, 0xdd, 0x72, 0xac, 0x71, 0x6c, 0x15, 0xaa, 0xb0, 0xe7, - 0xa3, 0xd8, 0x22, 0x5a, 0x62, 0xc7, 0x2a, 0x7b, 0x1d, 0x56, 0x53, 0x4b, 0xa6, 0xca, 0xfa, 0x8d, - 0x26, 0x96, 0xfc, 0x7d, 0x3a, 0x90, 0x4d, 0x49, 0x06, 0x0f, 0x2b, 0x7a, 0x0f, 0x4a, 0xb8, 0xcf, - 0xf7, 0xa8, 0xef, 0xf0, 0xa7, 0xb9, 0x55, 0x1d, 0x85, 0xa2, 0x77, 0x60, 0x5e, 0x16, 0x52, 0x75, - 0xce, 0x5a, 0x5a, 0x1f, 0x96, 0x74, 0xaa, 0x0b, 0xab, 0x9c, 0x8d, 0xc5, 0xc0, 0xc2, 0x08, 0xad, - 0x71, 0x0d, 0xf4, 0x24, 0x89, 0xca, 0xc1, 0xcf, 0x20, 0x76, 0xf2, 0xfb, 0x74, 0x20, 0x2d, 0x6e, - 0x13, 0xc2, 0xfe, 0xa9, 0xfe, 0xcc, 0x2f, 0xe3, 0x21, 0x2c, 0x63, 0xcb, 0x0a, 0xce, 0x05, 0xed, - 0xc8, 0x6b, 0x7f, 0xe4, 0x62, 0x2e, 0xbe, 0x8f, 0xcc, 0x6d, 0x5a, 0x1a, 0xbd, 0x8c, 0x2d, 0x6b, - 0x9b, 0x90, 0xf0, 0xd4, 0xb3, 0xed, 0x62, 0x8e, 0x3e, 0x01, 0x5d, 0xbe, 0xdb, 0x44, 0xe4, 0xb9, - 0x62, 0xc8, 0x4b, 0x12, 0x62, 0x02, 0x7c, 0x52, 0x73, 0xf0, 0x39, 0x09, 0xe4, 0xd3, 0xc7, 0xd0, - 0xdc, 0x72, 0xac, 0x74, 0xcd, 0x21, 0xf2, 0xfc, 0xf1, 0x34, 0x0f, 0xc1, 0xbb, 0x50, 0x1b, 0x6a, - 0x96, 0x87, 0xc5, 0x36, 0x13, 0xdb, 0x56, 0x8f, 0x78, 0x5c, 0x12, 0x9c, 0x29, 0x46, 0xa0, 0x4b, - 0xe9, 0xbb, 0x02, 0x64, 0x37, 0xc4, 0x10, 0x24, 0x0e, 0x5c, 0x8f, 0x38, 0x48, 0xe1, 0x59, 0x28, - 0xc6, 0xb3, 0x12, 0x1a, 0x49, 0xa4, 0xf2, 0xa0, 0x9e, 0xee, 0xc7, 0x0f, 0x0e, 0x8e, 0xac, 0x52, - 0x12, 0x4c, 0xa9, 0xc7, 0xd6, 0x6d, 0x42, 0xcc, 0x20, 0x50, 0x11, 0x5e, 0x4b, 0x36, 0x26, 0x42, - 0x18, 0xe2, 0x70, 0x23, 0xd3, 0x9a, 0xa2, 0x84, 0xa9, 0x28, 0x57, 0x53, 0x3d, 0x2a, 0x56, 0x0c, - 0x2b, 0x43, 0x97, 0xe2, 0xc8, 0x3b, 0x51, 0xcc, 0xb3, 0xc5, 0x8a, 0x59, 0x95, 0xde, 0x5a, 0x01, - 0xc6, 0x58, 0x21, 0x6d, 0xa8, 0x47, 0x8c, 0x25, 0xb3, 0x9c, 0x2b, 0xc6, 0x72, 0x2d, 0xb4, 0x93, - 0x44, 0xe4, 0xc2, 0x6a, 0xaa, 0x17, 0x55, 0xbd, 0xf3, 0x53, 0x55, 0xef, 0x6a, 0xa2, 0x29, 0x55, - 0x39, 0x1f, 0x1a, 0x59, 0xb6, 0x14, 0xe1, 0xe2, 0x54, 0x84, 0xb5, 0x34, 0x7f, 0x92, 0x73, 0x62, - 0xab, 0xd5, 0xc5, 0xc9, 0x77, 0x6c, 0x2f, 0x9d, 0x68, 0x15, 0xf2, 0x20, 0xf1, 0x40, 0x0c, 0xa1, - 0xaf, 0xa0, 0x55, 0xc8, 0x69, 0x36, 0xaf, 0x55, 0x48, 0xba, 0x61, 0xab, 0x90, 0x39, 0xe9, 0xad, - 0x22, 0x2e, 0x51, 0x3a, 0xb8, 0xfb, 0xf5, 0x22, 0xcc, 0xee, 0x30, 0x1b, 0x3d, 0x82, 0x52, 0xb8, - 0x3d, 0xa2, 0xdb, 0xa9, 0xbd, 0x69, 0x72, 0xa6, 0xd6, 0x5f, 0x2b, 0x16, 0xac, 0xa6, 0xba, 0x90, - 0xa7, 0xe5, 0x58, 0x05, 0x78, 0x46, 0xb3, 0x6c, 0x01, 0x9e, 0xe8, 0xf4, 0xe8, 0xc2, 0xd9, 0xc8, - 0xbc, 0x86, 0xd6, 0xb2, 0x92, 0x27, 0xe6, 0x48, 0xdd, 0x28, 0x1a, 0xae, 0xd8, 0xba, 0xb0, 0x30, - 0x9c, 0xf6, 0xd0, 0xad, 0x8c, 0xdc, 0xb1, 0x41, 0x51, 0xbf, 0x5d, 0x28, 0x36, 0x4e, 0x12, 0x4c, - 0x89, 0xb9, 0x24, 0x91, 0x01, 0x33, 0x97, 0x24, 0x3a, 0x76, 0x22, 0x0a, 0xe7, 0xa2, 0x03, 0x20, - 0xca, 0xaa, 0x44, 0xc2, 0x70, 0xaa, 0x37, 0x0b, 0xc7, 0x2b, 0xc2, 0x3e, 0x2c, 0xc6, 0x87, 0x2b, - 0x74, 0x27, 0x17, 0x62, 0x6c, 0x00, 0xd5, 0xd7, 0xa7, 0xc8, 0x50, 0xb4, 0x5f, 0x68, 0x70, 0x39, - 0x61, 0x7a, 0x42, 0x6f, 0xe4, 0x42, 0x25, 0xcd, 0x79, 0xfa, 0xbd, 0x69, 0xd3, 0x52, 0x64, 0xa8, - 0x41, 0xa9, 0xb0, 0x8c, 0xf8, 0x48, 0x57, 0x58, 0xc6, 0xd8, 0x3c, 0x86, 0xbe, 0xd2, 0x60, 0x29, - 0x79, 0x20, 0x42, 0x6f, 0x15, 0x84, 0x9c, 0x18, 0xe0, 0xf4, 0xb7, 0x8f, 0x91, 0xa9, 0xf4, 0x3c, - 0xd3, 0x60, 0x39, 0x65, 0xfa, 0x41, 0xf9, 0xb0, 0x69, 0xf3, 0x9a, 0xbe, 0x71, 0x9c, 0x54, 0x25, - 0xe9, 0x4b, 0x0d, 0xca, 0x49, 0x63, 0x03, 0xba, 0x57, 0x10, 0x74, 0x6c, 0x34, 0xd3, 0xdf, 0x9c, - 0x3a, 0x4f, 0x29, 0x39, 0x84, 0x0b, 0x63, 0x07, 0x7f, 0x94, 0xb5, 0x00, 0x92, 0xe7, 0x18, 0xfd, - 0xee, 0x34, 0x29, 0x8a, 0xd9, 0x87, 0xf3, 0xb1, 0x3e, 0x88, 0x9a, 0xd9, 0x20, 0x13, 0xd3, 0x87, - 0x7e, 0xa7, 0x78, 0x42, 0xcc, 0x6d, 0xb4, 0x77, 0xe5, 0xb9, 0x4d, 0x68, 0xc5, 0x79, 0x6e, 0x93, - 0x5a, 0x63, 0x8b, 0x3c, 0x7f, 0x59, 0xd3, 0x5e, 0xbc, 0xac, 0x69, 0x7f, 0xbc, 0xac, 0x69, 0xcf, - 0x8e, 0x6a, 0x33, 0x2f, 0x8e, 0x6a, 0x33, 0xbf, 0x1e, 0xd5, 0x66, 0xa0, 0xea, 0xd0, 0x14, 0xbc, - 0x07, 0xda, 0xc7, 0x46, 0xe4, 0xe7, 0xa6, 0x51, 0xd0, 0x9a, 0x43, 0x23, 0x57, 0xcd, 0xc3, 0xf0, - 0xb7, 0xeb, 0xce, 0xbc, 0xf8, 0xc9, 0xfa, 0xf5, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0xd0, 0x5f, - 0xe8, 0x90, 0xc6, 0x17, 0x00, 0x00, + // 1472 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x4f, 0x6f, 0x1b, 0x45, + 0x14, 0xcf, 0xe6, 0x5f, 0xe3, 0xd7, 0x36, 0x6d, 0xa7, 0x6e, 0x62, 0x6f, 0x1b, 0xc7, 0x75, 0x38, + 0x94, 0x96, 0xac, 0x9b, 0x22, 0x0a, 0x44, 0x5c, 0xe2, 0x14, 0x47, 0x1c, 0x22, 0xaa, 0x0d, 0x15, + 0x12, 0x1c, 0xac, 0xb1, 0x77, 0xba, 0x59, 0x79, 0xbd, 0x93, 0xee, 0x4c, 0xdc, 0x54, 0xdc, 0x10, + 0x12, 0x27, 0xa4, 0x1e, 0xb9, 0x72, 0x84, 0x13, 0x48, 0x5c, 0xe0, 0x13, 0x54, 0x9c, 0x2a, 0x4e, + 0x9c, 0x00, 0xb5, 0x07, 0x3e, 0x02, 0x57, 0xb4, 0x33, 0xe3, 0xcd, 0xae, 0xbd, 0xeb, 0x5d, 0x87, + 0x06, 0x4e, 0xc9, 0xee, 0xbc, 0xf7, 0xfb, 0xf3, 0x3c, 0x9e, 0x37, 0xcf, 0xb0, 0x7a, 0xe0, 0xd3, + 0x3e, 0xf1, 0xb0, 0xd7, 0x21, 0x75, 0x72, 0xd4, 0xd9, 0xc7, 0x9e, 0x4d, 0xea, 0xfd, 0x8d, 0x3a, + 0x3f, 0x32, 0x0e, 0x7c, 0xca, 0x29, 0x5a, 0x3a, 0x0e, 0x30, 0x06, 0x01, 0x46, 0x7f, 0x43, 0xaf, + 0x74, 0x28, 0xeb, 0x51, 0x56, 0x6f, 0x63, 0x16, 0x24, 0xb4, 0x09, 0xc7, 0x1b, 0xf5, 0x0e, 0x75, + 0x3c, 0x99, 0xa7, 0x2f, 0xab, 0xf5, 0x1e, 0xb3, 0x03, 0xbc, 0x1e, 0xb3, 0xd5, 0x42, 0x59, 0x2e, + 0xb4, 0xc4, 0x53, 0x5d, 0x3e, 0xa8, 0xa5, 0xa2, 0x4d, 0x6d, 0x2a, 0xdf, 0x07, 0xff, 0xa9, 0xb7, + 0x6b, 0x29, 0x12, 0x7b, 0xd8, 0xef, 0x12, 0x9e, 0x11, 0x44, 0x7d, 0x8b, 0xf8, 0x2c, 0x23, 0xe8, + 0x00, 0xfb, 0xb8, 0xa7, 0x82, 0x6a, 0x3f, 0x6b, 0x70, 0x79, 0x97, 0xd9, 0xdb, 0x3e, 0xc1, 0x9c, + 0x6c, 0xb1, 0xae, 0x49, 0x1e, 0x1d, 0x12, 0xc6, 0xd1, 0x36, 0x14, 0x30, 0xeb, 0xb6, 0x04, 0x60, + 0x49, 0xab, 0x6a, 0x37, 0xce, 0xde, 0xa9, 0x1a, 0xc9, 0xc5, 0x31, 0xb6, 0x58, 0xf7, 0xc3, 0x20, + 0xae, 0x31, 0xfb, 0xec, 0xf7, 0xd5, 0x29, 0x73, 0x01, 0xab, 0x67, 0xb4, 0x03, 0x48, 0x00, 0xb4, + 0x3a, 0x01, 0xbc, 0x43, 0xbd, 0xd6, 0x43, 0x42, 0x4a, 0xd3, 0x02, 0xad, 0x6c, 0xa8, 0x62, 0x04, + 0x25, 0x35, 0x54, 0x49, 0x8d, 0x6d, 0xea, 0x78, 0xe6, 0x45, 0x91, 0xb4, 0xad, 0x72, 0x9a, 0x84, + 0x6c, 0x5e, 0xf9, 0xfc, 0xaf, 0xef, 0x6f, 0x5e, 0x0c, 0x05, 0x19, 0x8c, 0xb8, 0x2e, 0xf1, 0x6b, + 0x1b, 0x50, 0x8c, 0x6b, 0x67, 0x07, 0xd4, 0x63, 0x04, 0x95, 0x61, 0x41, 0xf2, 0x3a, 0x96, 0xd0, + 0x3e, 0x6b, 0x9e, 0x11, 0xcf, 0x1f, 0x58, 0xb5, 0x9f, 0xa2, 0x7e, 0x1b, 0x8e, 0x15, 0xf1, 0xdb, + 0x76, 0xac, 0x7c, 0x7e, 0x1b, 0x8e, 0x15, 0xf3, 0xdb, 0x56, 0xcf, 0xaf, 0xce, 0x6f, 0x31, 0xf0, + 0x7b, 0x21, 0x14, 0x64, 0xb4, 0x0f, 0x9f, 0x0c, 0xd9, 0x15, 0xd2, 0xb3, 0xed, 0x7a, 0x70, 0x25, + 0x48, 0x09, 0x2c, 0xb8, 0x42, 0xe3, 0xc0, 0xaf, 0x01, 0x73, 0xf4, 0xb1, 0xa7, 0xbc, 0x16, 0x1a, + 0xa5, 0x5f, 0x7f, 0x5c, 0x2f, 0x2a, 0x81, 0x5b, 0x96, 0xe5, 0x13, 0xc6, 0xf6, 0xb8, 0xef, 0x78, + 0xb6, 0x29, 0xc3, 0x62, 0x1c, 0xd3, 0x31, 0x8e, 0x4d, 0x08, 0xc4, 0xca, 0xb0, 0x5a, 0x09, 0x96, + 0x86, 0xf9, 0xa4, 0xc8, 0x5a, 0x11, 0xd0, 0x2e, 0xb3, 0x9b, 0x8e, 0xeb, 0x36, 0x1c, 0x8b, 0x29, + 0x19, 0xb5, 0x2b, 0xe2, 0xd3, 0x38, 0x7e, 0x3b, 0x12, 0xbc, 0xc5, 0xba, 0x09, 0xc1, 0xf2, 0xad, + 0x0a, 0x7e, 0x24, 0x38, 0x77, 0xc5, 0xf7, 0x63, 0x8f, 0x70, 0xee, 0x92, 0x88, 0x49, 0x6c, 0xf5, + 0x1c, 0x2f, 0xdb, 0xa4, 0x08, 0x43, 0x57, 0xa1, 0x20, 0xbf, 0x66, 0x03, 0x97, 0xe7, 0xcd, 0x05, + 0xf9, 0x22, 0xb4, 0x29, 0x02, 0x6b, 0x65, 0x58, 0x1e, 0xa1, 0x54, 0x6a, 0xfe, 0xd6, 0xa0, 0x14, + 0xae, 0x7d, 0xec, 0xf0, 0x7d, 0xcb, 0xc7, 0x8f, 0x4f, 0x43, 0x10, 0x5a, 0x01, 0xe0, 0xb4, 0x85, + 0x65, 0x5e, 0x69, 0x26, 0x40, 0x34, 0x0b, 0x9c, 0x2a, 0x20, 0xd4, 0x81, 0x79, 0xdc, 0xa3, 0x87, + 0x1e, 0x2f, 0xcd, 0x56, 0x67, 0xc6, 0x6e, 0xc0, 0xc6, 0xed, 0x60, 0x1f, 0x7f, 0xf7, 0xc7, 0xea, + 0x0d, 0xdb, 0xe1, 0xfb, 0x87, 0x6d, 0xa3, 0x43, 0x7b, 0xea, 0xa8, 0x52, 0x7f, 0xd6, 0x99, 0xd5, + 0xad, 0xf3, 0x27, 0x07, 0x84, 0x89, 0x04, 0x66, 0x2a, 0xe8, 0x58, 0x51, 0xae, 0x42, 0x39, 0xc1, + 0xb8, 0x2a, 0xcb, 0x11, 0xac, 0x84, 0x8b, 0x0f, 0x0e, 0x2c, 0xcc, 0xc9, 0x3d, 0xc2, 0xb1, 0xe3, + 0xb2, 0x53, 0xff, 0xac, 0xaa, 0x50, 0x49, 0x63, 0x4e, 0xd5, 0xf6, 0xbe, 0x87, 0xdb, 0x2e, 0xb1, + 0xfe, 0x07, 0x6d, 0x21, 0xb3, 0xd2, 0xf6, 0x19, 0x54, 0x87, 0x22, 0x1e, 0x30, 0xe2, 0xff, 0x47, + 0xdb, 0x7c, 0x0d, 0xae, 0x8f, 0x21, 0x57, 0x0a, 0xbf, 0x9d, 0x8e, 0x44, 0xed, 0x62, 0x0f, 0xdb, + 0xe4, 0x3e, 0xf1, 0x7b, 0x0e, 0x63, 0x0e, 0xf5, 0xd8, 0x69, 0xed, 0x7c, 0x9f, 0xf4, 0x69, 0x97, + 0xb4, 0xb0, 0xeb, 0x96, 0x66, 0xaa, 0x33, 0xc1, 0xce, 0x97, 0x6f, 0xb6, 0x5c, 0x17, 0x35, 0xa1, + 0xc0, 0x69, 0x4b, 0x3e, 0xab, 0xcd, 0xbf, 0x96, 0xda, 0xbb, 0x3a, 0x1d, 0xc2, 0xd8, 0x8e, 0x8f, + 0x3d, 0x3e, 0x38, 0xce, 0x39, 0x35, 0x45, 0x2a, 0xba, 0x07, 0x0b, 0x9c, 0xb6, 0xec, 0x60, 0xad, + 0x34, 0x37, 0x29, 0xcc, 0x19, 0x4e, 0xc5, 0x63, 0xac, 0xa0, 0xaf, 0x41, 0x6d, 0x5c, 0xa9, 0x54, + 0x45, 0x7f, 0x98, 0x8e, 0x6c, 0x0b, 0x19, 0x66, 0x92, 0x47, 0x5b, 0x9c, 0xfb, 0xa7, 0x53, 0xce, + 0xd7, 0xe1, 0x92, 0x68, 0x58, 0xa4, 0x15, 0x74, 0x58, 0x79, 0xa6, 0xa8, 0xaa, 0x2e, 0x76, 0x06, + 0xcd, 0xf5, 0xa3, 0xe0, 0x60, 0x41, 0x75, 0x28, 0xc6, 0x43, 0x7d, 0xd2, 0xa3, 0x7d, 0x59, 0xe5, + 0x82, 0x79, 0x29, 0x12, 0x6d, 0x8a, 0x85, 0x08, 0x76, 0xd0, 0xcd, 0x14, 0xf6, 0x5c, 0x14, 0xbb, + 0xe1, 0x58, 0xc3, 0xd8, 0x2a, 0x54, 0x61, 0xcf, 0x47, 0xb1, 0x45, 0xb4, 0xc4, 0x8e, 0x55, 0xf6, + 0x3a, 0xac, 0xa6, 0x96, 0x4c, 0x95, 0xf5, 0x1b, 0x4d, 0x1c, 0x50, 0x3b, 0xb4, 0x2f, 0x5b, 0xa8, + 0x0c, 0x1e, 0x54, 0xf4, 0x2e, 0x14, 0xf0, 0x21, 0xdf, 0xa7, 0xbe, 0xc3, 0x9f, 0x64, 0x56, 0xf5, + 0x38, 0x14, 0xbd, 0x07, 0xf3, 0xb2, 0x90, 0xaa, 0xcf, 0x57, 0xd2, 0xb6, 0x88, 0xa4, 0x53, 0xbb, + 0x43, 0xe5, 0x6c, 0x2e, 0x06, 0x16, 0x8e, 0xd1, 0x6a, 0xd7, 0x40, 0x4f, 0x92, 0xa8, 0x1c, 0xfc, + 0x02, 0xa2, 0xef, 0xec, 0xd0, 0xbe, 0xb4, 0xd8, 0x24, 0x84, 0xfd, 0x5b, 0xfd, 0x63, 0x77, 0xc6, + 0x03, 0x58, 0xc6, 0x96, 0x15, 0xdc, 0x62, 0x5a, 0x91, 0x8f, 0xfd, 0xa1, 0x8b, 0xb9, 0xd8, 0x1f, + 0x63, 0x9b, 0x8a, 0x34, 0x7a, 0x19, 0x5b, 0x56, 0x93, 0x90, 0xf0, 0x8e, 0xd6, 0x74, 0x31, 0x47, + 0x9f, 0x82, 0x2e, 0x3f, 0xdb, 0x44, 0xe4, 0xd9, 0x7c, 0xc8, 0x4b, 0x12, 0x62, 0x04, 0x7c, 0x54, + 0x73, 0xb0, 0x9d, 0x04, 0xf2, 0xdc, 0x09, 0x34, 0x37, 0x1c, 0x2b, 0x5d, 0x73, 0x88, 0x3c, 0x7f, + 0x32, 0xcd, 0x03, 0xf0, 0x0e, 0x54, 0x06, 0x9a, 0xe5, 0xd5, 0xb6, 0xc5, 0xc4, 0x21, 0xdb, 0x23, + 0x1e, 0x97, 0x04, 0x67, 0xf2, 0x11, 0xe8, 0x52, 0xfa, 0x9e, 0x00, 0xd9, 0x0b, 0x31, 0x04, 0x89, + 0x03, 0xd7, 0x23, 0x0e, 0x52, 0x78, 0x16, 0xf2, 0xf1, 0xac, 0x84, 0x46, 0x12, 0xa9, 0x3c, 0xa8, + 0xa6, 0xfb, 0xf1, 0x83, 0x6b, 0x2e, 0x2b, 0x15, 0x04, 0x53, 0xea, 0x25, 0xbb, 0x49, 0x88, 0x19, + 0x04, 0x2a, 0xc2, 0x6b, 0xc9, 0xc6, 0x44, 0x08, 0x43, 0x1c, 0xd6, 0xc6, 0x5a, 0x53, 0x94, 0x30, + 0x11, 0xe5, 0x6a, 0xaa, 0x47, 0xc5, 0x8a, 0x61, 0x65, 0xe0, 0x52, 0x5c, 0xd0, 0x47, 0x8a, 0x79, + 0x36, 0x5f, 0x31, 0xcb, 0xd2, 0x5b, 0x23, 0xc0, 0x18, 0x2a, 0xa4, 0x0d, 0xd5, 0x88, 0xb1, 0x64, + 0x96, 0x73, 0xf9, 0x58, 0xae, 0x85, 0x76, 0x92, 0x88, 0x5c, 0x58, 0x4d, 0xf5, 0xa2, 0xaa, 0x77, + 0x7e, 0xa2, 0xea, 0x5d, 0x4d, 0x34, 0xa5, 0x2a, 0xe7, 0x43, 0x6d, 0x9c, 0x2d, 0x45, 0xb8, 0x38, + 0x11, 0x61, 0x25, 0xcd, 0x9f, 0xe4, 0x1c, 0x39, 0x6a, 0x75, 0x71, 0x4f, 0x1f, 0x3a, 0x4b, 0x47, + 0x5a, 0x85, 0xbc, 0xf6, 0xdc, 0x17, 0x23, 0xf3, 0x2b, 0x68, 0x15, 0x72, 0xf6, 0xce, 0x6a, 0x15, + 0x92, 0x6e, 0xd0, 0x2a, 0x64, 0x4e, 0x7a, 0xab, 0x88, 0x4b, 0x94, 0x0e, 0xee, 0x7c, 0xbd, 0x08, + 0x33, 0xbb, 0xcc, 0x46, 0x0f, 0xa1, 0x10, 0x1e, 0x8f, 0xe8, 0x56, 0x6a, 0x6f, 0x1a, 0xfd, 0x05, + 0x40, 0x7f, 0x23, 0x5f, 0xb0, 0x9a, 0x41, 0x43, 0x9e, 0x86, 0x63, 0xe5, 0xe0, 0x39, 0x9e, 0xbc, + 0x73, 0xf0, 0x44, 0x67, 0x5d, 0x17, 0xce, 0x46, 0xa6, 0x4b, 0xb4, 0x3e, 0x2e, 0x79, 0x64, 0xea, + 0xd5, 0x8d, 0xbc, 0xe1, 0x8a, 0xad, 0x03, 0x0b, 0x83, 0xd9, 0x14, 0xdd, 0x1c, 0x93, 0x3b, 0x34, + 0xd6, 0xea, 0xb7, 0x72, 0xc5, 0xc6, 0x49, 0x82, 0x99, 0x36, 0x93, 0x24, 0x32, 0x0e, 0x67, 0x92, + 0x44, 0x87, 0x64, 0x44, 0xe1, 0x5c, 0x74, 0x5c, 0x45, 0xe3, 0x2a, 0x91, 0x30, 0x4a, 0xeb, 0xf5, + 0xdc, 0xf1, 0x8a, 0xf0, 0x10, 0x16, 0xe3, 0xa3, 0x20, 0xba, 0x9d, 0x09, 0x31, 0x34, 0x2e, 0xeb, + 0x1b, 0x13, 0x64, 0x28, 0xda, 0x2f, 0x34, 0xb8, 0x9c, 0x30, 0xeb, 0xa1, 0xb7, 0x32, 0xa1, 0x92, + 0xa6, 0x52, 0xfd, 0xee, 0xa4, 0x69, 0x29, 0x32, 0xd4, 0x58, 0x97, 0x5b, 0x46, 0x7c, 0x00, 0xcd, + 0x2d, 0x63, 0x68, 0x7a, 0x44, 0x5f, 0x69, 0xb0, 0x94, 0x3c, 0xbe, 0xa1, 0x77, 0x72, 0x42, 0x8e, + 0x8c, 0x9b, 0xfa, 0xbb, 0x27, 0xc8, 0x54, 0x7a, 0x9e, 0x6a, 0xb0, 0x9c, 0x32, 0xfd, 0xa0, 0x6c, + 0xd8, 0xb4, 0xe1, 0x52, 0xdf, 0x3c, 0x49, 0xaa, 0x92, 0xf4, 0xa5, 0x06, 0xc5, 0xa4, 0xb1, 0x01, + 0xdd, 0xcd, 0x09, 0x3a, 0x34, 0x9a, 0xe9, 0x6f, 0x4f, 0x9c, 0xa7, 0x94, 0x1c, 0xc1, 0x85, 0xa1, + 0x8b, 0x3f, 0x1a, 0xf7, 0x05, 0x48, 0x9e, 0x63, 0xf4, 0x3b, 0x93, 0xa4, 0x28, 0x66, 0x1f, 0xce, + 0xc7, 0xfa, 0x20, 0xaa, 0x8f, 0x07, 0x19, 0x99, 0x3e, 0xf4, 0xdb, 0xf9, 0x13, 0x62, 0x6e, 0xa3, + 0xbd, 0x2b, 0xcb, 0x6d, 0x42, 0x2b, 0xce, 0x72, 0x9b, 0xd4, 0x1a, 0x1b, 0xe4, 0xd9, 0x8b, 0x8a, + 0xf6, 0xfc, 0x45, 0x45, 0xfb, 0xf3, 0x45, 0x45, 0x7b, 0xfa, 0xb2, 0x32, 0xf5, 0xfc, 0x65, 0x65, + 0xea, 0xb7, 0x97, 0x95, 0x29, 0x28, 0x3b, 0x34, 0x05, 0xef, 0xbe, 0xf6, 0x89, 0x11, 0xf9, 0x71, + 0xec, 0x38, 0x68, 0xdd, 0xa1, 0x91, 0xa7, 0xfa, 0x51, 0xf8, 0x4b, 0x7b, 0x7b, 0x5e, 0xfc, 0xc0, + 0xfe, 0xe6, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x04, 0xc2, 0xef, 0xaa, 0x74, 0x18, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2927,6 +2957,43 @@ func (m *MsgMarketManagePermissionsRequest) MarshalToSizedBuffer(dAtA []byte) (i _ = i var l int _ = l + if len(m.ToGrant) > 0 { + for iNdEx := len(m.ToGrant) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ToGrant[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.ToRevoke) > 0 { + for iNdEx := len(m.ToRevoke) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ToRevoke[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.RevokeAll) > 0 { + for iNdEx := len(m.RevokeAll) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.RevokeAll[iNdEx]) + copy(dAtA[i:], m.RevokeAll[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.RevokeAll[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } if m.MarketId != 0 { i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) i-- @@ -3685,6 +3752,24 @@ func (m *MsgMarketManagePermissionsRequest) Size() (n int) { if m.MarketId != 0 { n += 1 + sovTx(uint64(m.MarketId)) } + if len(m.RevokeAll) > 0 { + for _, s := range m.RevokeAll { + l = len(s) + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.ToRevoke) > 0 { + for _, e := range m.ToRevoke { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.ToGrant) > 0 { + for _, e := range m.ToGrant { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } return n } @@ -5525,6 +5610,106 @@ func (m *MsgMarketManagePermissionsRequest) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RevokeAll", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RevokeAll = append(m.RevokeAll, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToRevoke", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ToRevoke = append(m.ToRevoke, AccessGrant{}) + if err := m.ToRevoke[len(m.ToRevoke)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToGrant", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ToGrant = append(m.ToGrant, AccessGrant{}) + if err := m.ToGrant[len(m.ToGrant)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) From 8e45670b4794a5d47b90b4e9f0f82be6e38bad3c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 12:06:40 -0600 Subject: [PATCH 121/309] [1658]: Implement MarketManagePermissions. --- x/exchange/keeper/market.go | 91 ++++++++++++++++++++++++--------- x/exchange/keeper/msg_server.go | 11 +++- 2 files changed, 75 insertions(+), 27 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index aa728c5be0..6969809ce8 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -620,6 +620,12 @@ func (k Keeper) SetUserSettlementAllowed(ctx sdk.Context, marketID uint32, allow setUserSettlementAllowed(k.getStore(ctx), marketID, allowed) } +// storeHasPermission returns true if there is an entry in the store for the given market, address, and permissions. +func storeHasPermission(store sdk.KVStore, marketID uint32, addr sdk.AccAddress, permission exchange.Permission) bool { + key := MakeKeyMarketPermissions(marketID, addr, permission) + return store.Has(key) +} + // grantPermissions updates the store so that the given address has the provided permissions in a market. func grantPermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress, permissions []exchange.Permission) { for _, perm := range permissions { @@ -656,20 +662,6 @@ func setAllMarketPermissions(store sdk.KVStore, marketID uint32, grants []exchan } } -// updatePermissions revokes all permissions from the provided revokeAll bech32 addresses, then revokes all permissions -// in the toRevoke list, and lastly, grants all the permissions in toGrant. -func updatePermissions(store sdk.KVStore, marketID uint32, revokeAll []string, toRevoke, toGrant []exchange.AccessGrant) { - for _, revAddr := range revokeAll { - revokeAllUserPermissions(store, marketID, sdk.MustAccAddressFromBech32(revAddr)) - } - for _, ag := range toRevoke { - revokePermissions(store, marketID, sdk.MustAccAddressFromBech32(ag.Address), ag.Permissions) - } - for _, ag := range toGrant { - grantPermissions(store, marketID, sdk.MustAccAddressFromBech32(ag.Address), ag.Permissions) - } -} - // HasPermission returns true if the provided address has the permission in question for a given market. // Also returns true if the provided address is the authority address. func (k Keeper) HasPermission(ctx sdk.Context, marketID uint32, address string, permission exchange.Permission) bool { @@ -680,9 +672,7 @@ func (k Keeper) HasPermission(ctx sdk.Context, marketID uint32, address string, if err != nil { return false } - store := k.getStore(ctx) - key := MakeKeyMarketPermissions(marketID, addr, permission) - return store.Has(key) + return storeHasPermission(k.getStore(ctx), marketID, addr, permission) } // getUserPermissions gets all permissions that have been granted to a user in a market. @@ -724,12 +714,6 @@ func setAccessGrants(store sdk.KVStore, marketID uint32, grants []exchange.Acces setAllMarketPermissions(store, marketID, grants) } -// UpdateAccessGrants revokes all permissions from the provided revokeAll bech32 addresses, then revokes all permissions -// in the toRevoke list, and lastly, grants all the permissions in toGrant. -func (k Keeper) UpdateAccessGrants(ctx sdk.Context, marketID uint32, revokeAll []string, toRevoke, toGrant []exchange.AccessGrant) { - updatePermissions(k.getStore(ctx), marketID, revokeAll, toRevoke, toGrant) -} - // reqAttrKeyMaker is a function that returns a key for required attributes. type reqAttrKeyMaker func(marketID uint32) []byte @@ -962,12 +946,13 @@ func (k Keeper) CanCreateBid(ctx sdk.Context, marketID uint32, addr sdk.AccAddre } // CanWithdrawMarketFunds returns true if the provided admin bech32 address has permission to -// withdraw funds from the given market's account. +// withdraw funds from the given market's account. Also returns true if the provided address is the authority address. func (k Keeper) CanWithdrawMarketFunds(ctx sdk.Context, marketID uint32, admin string) bool { return k.HasPermission(ctx, marketID, admin, exchange.Permission_withdraw) } // WithdrawMarketFunds transfers funds from a market account to another account. +// The caller is responsible for making sure this withdrawal should be allowed (e.g. by calling CanWithdrawMarketFunds first). func (k Keeper) WithdrawMarketFunds(ctx sdk.Context, marketID uint32, toAddr sdk.AccAddress, amount sdk.Coins) error { marketAddr := exchange.GetMarketAddress(marketID) err := k.bankKeeper.SendCoins(ctx, marketAddr, toAddr, amount) @@ -977,8 +962,64 @@ func (k Keeper) WithdrawMarketFunds(ctx sdk.Context, marketID uint32, toAddr sdk return nil } +// CanManagePermissions returns true if the provided admin bech32 address has permission to +// manage user permissions for a given market. Also returns true if the provided address is the authority address. +func (k Keeper) CanManagePermissions(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_permissions) +} + +// UpdatePermissions updates users permissions in the store using the provided changes. +// The caller is responsible for making sure this update should be allowed (e.g. by calling CanManagePermissions first). +func (k Keeper) UpdatePermissions(ctx sdk.Context, msg *exchange.MsgMarketManagePermissionsRequest) error { + marketID := msg.MarketId + store := k.getStore(ctx) + var errs []error + + for _, addrStr := range msg.RevokeAll { + addr := sdk.MustAccAddressFromBech32(addrStr) + perms := getUserPermissions(store, marketID, addr) + if len(perms) > 0 { + if len(errs) == 0 { + revokeAllUserPermissions(store, marketID, addr) + } + } else { + errs = append(errs, fmt.Errorf("account %s does not have any permissions for market %d", addrStr, marketID)) + } + } + + for _, ag := range msg.ToRevoke { + addr := sdk.MustAccAddressFromBech32(ag.Address) + for _, perm := range ag.Permissions { + if !storeHasPermission(store, marketID, addr, perm) { + errs = append(errs, fmt.Errorf("account %s does not have %s for market %d", ag.Address, perm.String(), marketID)) + } + } + if len(errs) == 0 { + revokePermissions(store, marketID, addr, ag.Permissions) + } + } + + for _, ag := range msg.ToGrant { + addr := sdk.MustAccAddressFromBech32(ag.Address) + for _, perm := range ag.Permissions { + if storeHasPermission(store, marketID, addr, perm) { + errs = append(errs, fmt.Errorf("account %s already has %s for market %d", ag.Address, perm.String(), marketID)) + } + } + if len(errs) == 0 { + grantPermissions(store, marketID, addr, ag.Permissions) + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + + return nil +} + // CanManageReqAttrs returns true if the provided admin bech32 address has permission to -// manage required attributes for a given market. +// manage required attributes for a given market. Also returns true if the provided address is the authority address. func (k Keeper) CanManageReqAttrs(ctx sdk.Context, marketID uint32, admin string) bool { return k.HasPermission(ctx, marketID, admin, exchange.Permission_attributes) } diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 9ec7d03f03..b878462868 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -106,8 +106,15 @@ func (k MsgServer) MarketUpdateUserSettle(goCtx context.Context, msg *exchange.M // MarketManagePermissions is a market endpoint to manage a market's user permissions. func (k MsgServer) MarketManagePermissions(goCtx context.Context, msg *exchange.MsgMarketManagePermissionsRequest) (*exchange.MsgMarketManagePermissionsResponse, error) { - // TODO[1658]: Implement MarketManagePermissions - panic("not implemented") + ctx := sdk.UnwrapSDKContext(goCtx) + if !k.CanManagePermissions(ctx, msg.MarketId, msg.Admin) { + return nil, fmt.Errorf("account %s does not have permission to manage permissions for market %d", msg.Admin, msg.MarketId) + } + err := k.UpdatePermissions(ctx, msg) + if err != nil { + return nil, err + } + return &exchange.MsgMarketManagePermissionsResponse{}, nil } // MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. From 4cb6d7dac88e610ea8d20b9c25f62e74803b2ac5 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 12:15:18 -0600 Subject: [PATCH 122/309] [1658]: Create the rest of the Can... funcs for checking permissions. --- x/exchange/keeper/market.go | 54 ++++++++++++++++++++++++------------- x/exchange/keeper/orders.go | 2 +- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 6969809ce8..eede10d2dc 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -675,6 +675,42 @@ func (k Keeper) HasPermission(ctx sdk.Context, marketID uint32, address string, return storeHasPermission(k.getStore(ctx), marketID, addr, permission) } +// CanSettleOrders returns true if the provided admin bech32 address has permission to +// settle orders for a market. Also returns true if the provided address is the authority address. +func (k Keeper) CanSettleOrders(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_settle) +} + +// CanCancelMarketOrders returns true if the provided admin bech32 address has permission to +// cancel orders for a market. Also returns true if the provided address is the authority address. +func (k Keeper) CanCancelMarketOrders(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_cancel) +} + +// CanWithdrawMarketFunds returns true if the provided admin bech32 address has permission to +// withdraw funds from the given market's account. Also returns true if the provided address is the authority address. +func (k Keeper) CanWithdrawMarketFunds(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_withdraw) +} + +// CanUpdateMarket returns true if the provided admin bech32 address has permission to +// update market details and settings. Also returns true if the provided address is the authority address. +func (k Keeper) CanUpdateMarket(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_update) +} + +// CanManagePermissions returns true if the provided admin bech32 address has permission to +// manage user permissions for a given market. Also returns true if the provided address is the authority address. +func (k Keeper) CanManagePermissions(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_permissions) +} + +// CanManageReqAttrs returns true if the provided admin bech32 address has permission to +// manage required attributes for a given market. Also returns true if the provided address is the authority address. +func (k Keeper) CanManageReqAttrs(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_attributes) +} + // getUserPermissions gets all permissions that have been granted to a user in a market. func getUserPermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress) []exchange.Permission { var rv []exchange.Permission @@ -945,12 +981,6 @@ func (k Keeper) CanCreateBid(ctx sdk.Context, marketID uint32, addr sdk.AccAddre return k.hasReqAttrs(ctx, addr, reqAttrs) } -// CanWithdrawMarketFunds returns true if the provided admin bech32 address has permission to -// withdraw funds from the given market's account. Also returns true if the provided address is the authority address. -func (k Keeper) CanWithdrawMarketFunds(ctx sdk.Context, marketID uint32, admin string) bool { - return k.HasPermission(ctx, marketID, admin, exchange.Permission_withdraw) -} - // WithdrawMarketFunds transfers funds from a market account to another account. // The caller is responsible for making sure this withdrawal should be allowed (e.g. by calling CanWithdrawMarketFunds first). func (k Keeper) WithdrawMarketFunds(ctx sdk.Context, marketID uint32, toAddr sdk.AccAddress, amount sdk.Coins) error { @@ -962,12 +992,6 @@ func (k Keeper) WithdrawMarketFunds(ctx sdk.Context, marketID uint32, toAddr sdk return nil } -// CanManagePermissions returns true if the provided admin bech32 address has permission to -// manage user permissions for a given market. Also returns true if the provided address is the authority address. -func (k Keeper) CanManagePermissions(ctx sdk.Context, marketID uint32, admin string) bool { - return k.HasPermission(ctx, marketID, admin, exchange.Permission_permissions) -} - // UpdatePermissions updates users permissions in the store using the provided changes. // The caller is responsible for making sure this update should be allowed (e.g. by calling CanManagePermissions first). func (k Keeper) UpdatePermissions(ctx sdk.Context, msg *exchange.MsgMarketManagePermissionsRequest) error { @@ -1018,12 +1042,6 @@ func (k Keeper) UpdatePermissions(ctx sdk.Context, msg *exchange.MsgMarketManage return nil } -// CanManageReqAttrs returns true if the provided admin bech32 address has permission to -// manage required attributes for a given market. Also returns true if the provided address is the authority address. -func (k Keeper) CanManageReqAttrs(ctx sdk.Context, marketID uint32, admin string) bool { - return k.HasPermission(ctx, marketID, admin, exchange.Permission_attributes) -} - // updateReqAttrs updates the required attributes in the store that use the provided key maker by removing then adding // the provided attributes to the existing entries. func updateReqAttrs(store sdk.KVStore, marketID uint32, toRemove, toAdd []string, field string, maker reqAttrKeyMaker) error { diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index f4e654a897..1a7d3ad53f 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -325,7 +325,7 @@ func (k Keeper) CancelOrder(ctx sdk.Context, orderID uint64, signer string) erro } orderOwner := order.GetOwner() - if signer != orderOwner && !k.HasPermission(ctx, order.GetMarketID(), signer, exchange.Permission_cancel) { + if signer != orderOwner && !k.CanCancelMarketOrders(ctx, order.GetMarketID(), signer) { return fmt.Errorf("account %s does not have permission to cancel order %d", signer, orderID) } From bbf853a8c4a9d2124897fdfc86ee0481798b142f Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 13:39:42 -0600 Subject: [PATCH 123/309] [1658]: Finish MsgMarketManagePermissionsRequest.ValidateBasic(). --- x/exchange/market.go | 40 +++- x/exchange/market_test.go | 384 ++++++++++++++++++++++++++++++++++++-- x/exchange/msg.go | 44 ++++- x/exchange/msg_test.go | 150 ++++++++++++++- 4 files changed, 592 insertions(+), 26 deletions(-) diff --git a/x/exchange/market.go b/x/exchange/market.go index f048601e20..d249a6bdec 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -42,7 +42,7 @@ func (m Market) Validate() error { ValidateFeeOptions("seller settlement flat fee", m.FeeSellerSettlementFlat), ValidateFeeOptions("buyer settlement flat fee", m.FeeBuyerSettlementFlat), ValidateFeeRatios(m.FeeSellerSettlementRatios, m.FeeBuyerSettlementRatios), - ValidateAccessGrants(m.AccessGrants), + ValidateAccessGrantsField("", m.AccessGrants), // Nothing to check for with the AcceptingOrders and AllowUserSettlement booleans. ValidateReqAttrs("create-ask", m.ReqAttrCreateAsk), ValidateReqAttrs("create-bid", m.ReqAttrCreateBid), @@ -331,45 +331,67 @@ func ValidateAddRemoveFeeOptions(field string, toAdd, toRemove []sdk.Coin) error return errors.Join(errs...) } -// ValidateAccessGrants returns an error if any of the provided access grants are invalid. -func ValidateAccessGrants(accessGrants []AccessGrant) error { +// ValidateAccessGrantsField returns an error if any of the provided access grants are invalid. +// The provided field is used in error messages. +func ValidateAccessGrantsField(field string, accessGrants []AccessGrant) error { + if len(field) > 0 && !strings.HasSuffix(field, " ") { + field += " " + } errs := make([]error, len(accessGrants)) seen := make(map[string]bool) dups := make(map[string]bool) for i, ag := range accessGrants { if seen[ag.Address] && !dups[ag.Address] { - errs[i] = fmt.Errorf("%s appears in multiple access grant entries", ag.Address) + errs[i] = fmt.Errorf("%s appears in multiple %saccess grant entries", ag.Address, field) dups[ag.Address] = true continue } seen[ag.Address] = true - errs[i] = ag.Validate() + errs[i] = ag.ValidateInField(field) } return errors.Join(errs...) } // Validate returns an error if there is anything wrong with this AccessGrant. func (a AccessGrant) Validate() error { + return a.ValidateInField("") +} + +// ValidateInField returns an error if there is anything wrong with this AccessGrant. +// The provided field is included in any error message. +func (a AccessGrant) ValidateInField(field string) error { + if len(field) > 0 && !strings.HasSuffix(field, " ") { + field += " " + } _, err := sdk.AccAddressFromBech32(a.Address) if err != nil { - return fmt.Errorf("invalid access grant: invalid address: %w", err) + return fmt.Errorf("invalid %saccess grant: invalid address %q: %w", field, a.Address, err) } if len(a.Permissions) == 0 { - return fmt.Errorf("invalid access grant: no permissions provided for %s", a.Address) + return fmt.Errorf("invalid %saccess grant: no permissions provided for %s", field, a.Address) } seen := make(map[Permission]bool) for _, perm := range a.Permissions { if seen[perm] { - return fmt.Errorf("invalid access grant: %s appears multiple times for %s", perm.SimpleString(), a.Address) + return fmt.Errorf("invalid %saccess grant: %s appears multiple times for %s", field, perm.SimpleString(), a.Address) } seen[perm] = true if err = perm.Validate(); err != nil { - return fmt.Errorf("invalid access grant: %w for %s", err, a.Address) + return fmt.Errorf("invalid %saccess grant: %w for %s", field, err, a.Address) } } return nil } +func (a AccessGrant) Contains(perm Permission) bool { + for _, p := range a.Permissions { + if p == perm { + return true + } + } + return false +} + // SimpleString returns a lower-cased version of the permission.String() without the leading "permission_" // E.g. "settle", or "update". func (p Permission) SimpleString() string { diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index e04ba61932..4e6fd9231d 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -124,7 +124,7 @@ func TestMarket_Validate(t *testing.T) { { name: "invalid access grants", market: Market{AccessGrants: []AccessGrant{{Address: "bad_addr", Permissions: AllPermissions()}}}, - expErr: []string{"invalid access grant: invalid address: decoding bech32 failed: invalid separator index -1"}, + expErr: []string{`invalid access grant: invalid address "bad_addr": decoding bech32 failed: invalid separator index -1`}, }, { name: "invalid ask required attributes", @@ -158,7 +158,7 @@ func TestMarket_Validate(t *testing.T) { `invalid buyer settlement flat fee option "-1leela": negative coin amount: -1`, `denom "fry" is defined in the seller settlement fee ratios but not buyer`, `denom "leela" is defined in the buyer settlement fee ratios but not seller`, - "invalid access grant: invalid address: decoding bech32 failed: invalid separator index -1", + `invalid access grant: invalid address "bad_addr": decoding bech32 failed: invalid separator index -1`, `invalid create-ask required attribute "this-attr-is-bad"`, `invalid create-bid required attribute "this-attr-grrrr"`, }, @@ -1793,7 +1793,7 @@ func TestValidateAddRemoveFeeOptions(t *testing.T) { } } -func TestValidateAccessGrants(t *testing.T) { +func TestValidateAccessGrantsField(t *testing.T) { joinErrs := func(errs ...string) string { return strings.Join(errs, "\n") } @@ -1804,21 +1804,25 @@ func TestValidateAccessGrants(t *testing.T) { tests := []struct { name string + field string grants []AccessGrant exp string }{ { name: "nil grants", + field: "<~FIELD~>", grants: nil, exp: "", }, { name: "empty grants", + field: "<~FIELD~>", grants: []AccessGrant{}, exp: "", }, { - name: "duplicate address", + name: "duplicate address: no field", + field: "", grants: []AccessGrant{ {Address: addrDup, Permissions: []Permission{Permission_settle}}, {Address: addrDup, Permissions: []Permission{Permission_cancel}}, @@ -1826,7 +1830,17 @@ func TestValidateAccessGrants(t *testing.T) { exp: sdk.AccAddress("duplicate_address___").String() + " appears in multiple access grant entries", }, { - name: "three entries: all valid", + name: "duplicate address: with field", + field: "<~FIELD~>", + grants: []AccessGrant{ + {Address: addrDup, Permissions: []Permission{Permission_settle}}, + {Address: addrDup, Permissions: []Permission{Permission_cancel}}, + }, + exp: sdk.AccAddress("duplicate_address___").String() + " appears in multiple <~FIELD~> access grant entries", + }, + { + name: "three entries: all valid", + field: "<~FIELD~>", grants: []AccessGrant{ {Address: addr1, Permissions: AllPermissions()}, {Address: addr2, Permissions: AllPermissions()}, @@ -1835,7 +1849,8 @@ func TestValidateAccessGrants(t *testing.T) { exp: "", }, { - name: "three entries: invalid first", + name: "three entries: invalid first: no field", + field: "", grants: []AccessGrant{ {Address: addr1, Permissions: []Permission{-1}}, {Address: addr2, Permissions: AllPermissions()}, @@ -1844,7 +1859,18 @@ func TestValidateAccessGrants(t *testing.T) { exp: "invalid access grant: permission -1 does not exist for " + addr1, }, { - name: "three entries: invalid second", + name: "three entries: invalid first: with field", + field: "<~FIELD~>", + grants: []AccessGrant{ + {Address: addr1, Permissions: []Permission{-1}}, + {Address: addr2, Permissions: AllPermissions()}, + {Address: addr3, Permissions: AllPermissions()}, + }, + exp: "invalid <~FIELD~> access grant: permission -1 does not exist for " + addr1, + }, + { + name: "three entries: invalid second: no field", + field: "", grants: []AccessGrant{ {Address: addr1, Permissions: AllPermissions()}, {Address: addr2, Permissions: []Permission{-1}}, @@ -1853,7 +1879,18 @@ func TestValidateAccessGrants(t *testing.T) { exp: "invalid access grant: permission -1 does not exist for " + addr2, }, { - name: "three entries: invalid second", + name: "three entries: invalid second: with field", + field: "<~FIELD~>", + grants: []AccessGrant{ + {Address: addr1, Permissions: AllPermissions()}, + {Address: addr2, Permissions: []Permission{-1}}, + {Address: addr3, Permissions: AllPermissions()}, + }, + exp: "invalid <~FIELD~> access grant: permission -1 does not exist for " + addr2, + }, + { + name: "three entries: invalid third: no field", + field: "", grants: []AccessGrant{ {Address: addr1, Permissions: AllPermissions()}, {Address: addr2, Permissions: AllPermissions()}, @@ -1862,7 +1899,18 @@ func TestValidateAccessGrants(t *testing.T) { exp: "invalid access grant: permission -1 does not exist for " + addr3, }, { - name: "three entries: only valid first", + name: "three entries: invalid third: with field", + field: "<~FIELD~>", + grants: []AccessGrant{ + {Address: addr1, Permissions: AllPermissions()}, + {Address: addr2, Permissions: AllPermissions()}, + {Address: addr3, Permissions: []Permission{-1}}, + }, + exp: "invalid <~FIELD~> access grant: permission -1 does not exist for " + addr3, + }, + { + name: "three entries: only valid first: no field", + field: "", grants: []AccessGrant{ {Address: addr1, Permissions: AllPermissions()}, {Address: addr2, Permissions: []Permission{0}}, @@ -1874,7 +1922,21 @@ func TestValidateAccessGrants(t *testing.T) { ), }, { - name: "three entries: only valid second", + name: "three entries: only valid first: with field", + field: "<~FIELD~>", + grants: []AccessGrant{ + {Address: addr1, Permissions: AllPermissions()}, + {Address: addr2, Permissions: []Permission{0}}, + {Address: addr3, Permissions: []Permission{-1}}, + }, + exp: joinErrs( + "invalid <~FIELD~> access grant: permission is unspecified for "+addr2, + "invalid <~FIELD~> access grant: permission -1 does not exist for "+addr3, + ), + }, + { + name: "three entries: only valid second: no field", + field: "", grants: []AccessGrant{ {Address: addr1, Permissions: []Permission{0}}, {Address: addr2, Permissions: AllPermissions()}, @@ -1886,7 +1948,21 @@ func TestValidateAccessGrants(t *testing.T) { ), }, { - name: "three entries: only valid third", + name: "three entries: only valid second: with field", + field: "<~FIELD~>", + grants: []AccessGrant{ + {Address: addr1, Permissions: []Permission{0}}, + {Address: addr2, Permissions: AllPermissions()}, + {Address: addr3, Permissions: []Permission{-1}}, + }, + exp: joinErrs( + "invalid <~FIELD~> access grant: permission is unspecified for "+addr1, + "invalid <~FIELD~> access grant: permission -1 does not exist for "+addr3, + ), + }, + { + name: "three entries: only valid third: no field", + field: "", grants: []AccessGrant{ {Address: addr1, Permissions: []Permission{0}}, {Address: addr2, Permissions: []Permission{-1}}, @@ -1898,7 +1974,21 @@ func TestValidateAccessGrants(t *testing.T) { ), }, { - name: "three entries: all same address", + name: "three entries: only valid third: with field", + field: "<~FIELD~>", + grants: []AccessGrant{ + {Address: addr1, Permissions: []Permission{0}}, + {Address: addr2, Permissions: []Permission{-1}}, + {Address: addr3, Permissions: AllPermissions()}, + }, + exp: joinErrs( + "invalid <~FIELD~> access grant: permission is unspecified for "+addr1, + "invalid <~FIELD~> access grant: permission -1 does not exist for "+addr2, + ), + }, + { + name: "three entries: all same address: no field", + field: "", grants: []AccessGrant{ {Address: addrDup, Permissions: AllPermissions()}, {Address: addrDup, Permissions: AllPermissions()}, @@ -1906,13 +1996,23 @@ func TestValidateAccessGrants(t *testing.T) { }, exp: addrDup + " appears in multiple access grant entries", }, + { + name: "three entries: all same address: with field", + field: "<~FIELD~>", + grants: []AccessGrant{ + {Address: addrDup, Permissions: AllPermissions()}, + {Address: addrDup, Permissions: AllPermissions()}, + {Address: addrDup, Permissions: AllPermissions()}, + }, + exp: addrDup + " appears in multiple <~FIELD~> access grant entries", + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - err := ValidateAccessGrants(tc.grants) + err := ValidateAccessGrantsField(tc.field, tc.grants) - assertions.AssertErrorValue(t, err, tc.exp, "ValidateAccessGrants") + assertions.AssertErrorValue(t, err, tc.exp, "ValidateAccessGrantsField") }) } } @@ -1932,7 +2032,7 @@ func TestAccessGrant_Validate(t *testing.T) { { name: "invalid address", a: AccessGrant{Address: "invalid_address_____", Permissions: []Permission{Permission_settle}}, - exp: "invalid access grant: invalid address: decoding bech32 failed: invalid separator index -1", + exp: `invalid access grant: invalid address "invalid_address_____": decoding bech32 failed: invalid separator index -1`, }, { name: "nil permissions", @@ -1979,6 +2079,260 @@ func TestAccessGrant_Validate(t *testing.T) { } } +func TestAccessGrant_ValidateInField(t *testing.T) { + addr := sdk.AccAddress("addr________________").String() + tests := []struct { + name string + a AccessGrant + field string + exp string + }{ + { + name: "control", + a: AccessGrant{Address: addr, Permissions: AllPermissions()}, + field: "meadow", + exp: "", + }, + { + name: "invalid address: no field", + a: AccessGrant{Address: "invalid_address_____", Permissions: []Permission{Permission_settle}}, + field: "", + exp: `invalid access grant: invalid address "invalid_address_____": decoding bech32 failed: invalid separator index -1`, + }, + { + name: "invalid address: with field", + a: AccessGrant{Address: "invalid_address_____", Permissions: []Permission{Permission_settle}}, + field: "meadow", + exp: `invalid meadow access grant: invalid address "invalid_address_____": decoding bech32 failed: invalid separator index -1`, + }, + { + name: "nil permissions: no field", + a: AccessGrant{Address: addr, Permissions: nil}, + field: "", + exp: "invalid access grant: no permissions provided for " + addr, + }, + { + name: "nil permissions: with field", + a: AccessGrant{Address: addr, Permissions: nil}, + field: "meadow", + exp: "invalid meadow access grant: no permissions provided for " + addr, + }, + { + name: "empty permissions: no field", + a: AccessGrant{Address: addr, Permissions: []Permission{}}, + field: "", + exp: "invalid access grant: no permissions provided for " + addr, + }, + { + name: "empty permissions: with field", + a: AccessGrant{Address: addr, Permissions: []Permission{}}, + field: "meadow", + exp: "invalid meadow access grant: no permissions provided for " + addr, + }, + { + name: "duplicate entry: no field", + a: AccessGrant{ + Address: addr, + Permissions: []Permission{ + Permission_settle, + Permission_cancel, + Permission_settle, + }, + }, + field: "", + exp: "invalid access grant: settle appears multiple times for " + addr, + }, + { + name: "duplicate entry: with field", + a: AccessGrant{ + Address: addr, + Permissions: []Permission{ + Permission_settle, + Permission_cancel, + Permission_settle, + }, + }, + field: "meadow", + exp: "invalid meadow access grant: settle appears multiple times for " + addr, + }, + { + name: "invalid entry: no field", + a: AccessGrant{ + Address: addr, + Permissions: []Permission{ + Permission_withdraw, + -1, + Permission_attributes, + }, + }, + field: "", + exp: "invalid access grant: permission -1 does not exist for " + addr, + }, + { + name: "invalid entry: with field", + a: AccessGrant{ + Address: addr, + Permissions: []Permission{ + Permission_withdraw, + -1, + Permission_attributes, + }, + }, + field: "meadow", + exp: "invalid meadow access grant: permission -1 does not exist for " + addr, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.a.ValidateInField(tc.field) + + assertions.AssertErrorValue(t, err, tc.exp, "ValidateInField(%q)", tc.field) + }) + } +} + +func TestAccessGrant_Contains(t *testing.T) { + tests := []struct { + name string + a AccessGrant + perm Permission + exp bool + }{ + { + name: "nil permissions", + a: AccessGrant{Permissions: nil}, + perm: 0, + exp: false, + }, + { + name: "empty permissions", + a: AccessGrant{Permissions: []Permission{}}, + perm: 0, + exp: false, + }, + { + name: "all permissions: checking unspecified", + a: AccessGrant{Permissions: AllPermissions()}, + perm: Permission_unspecified, + exp: false, + }, + { + name: "all permissions: checking settle", + a: AccessGrant{Permissions: AllPermissions()}, + perm: Permission_settle, + exp: true, + }, + { + name: "all permissions: checking cancel", + a: AccessGrant{Permissions: AllPermissions()}, + perm: Permission_cancel, + exp: true, + }, + { + name: "all permissions: checking withdraw", + a: AccessGrant{Permissions: AllPermissions()}, + perm: Permission_withdraw, + exp: true, + }, + { + name: "all permissions: checking update", + a: AccessGrant{Permissions: AllPermissions()}, + perm: Permission_update, + exp: true, + }, + { + name: "all permissions: checking permissions", + a: AccessGrant{Permissions: AllPermissions()}, + perm: Permission_permissions, + exp: true, + }, + { + name: "all permissions: checking attributes", + a: AccessGrant{Permissions: AllPermissions()}, + perm: Permission_attributes, + exp: true, + }, + { + name: "all permissions: checking unknown", + a: AccessGrant{Permissions: AllPermissions()}, + perm: 99, + exp: false, + }, + { + name: "all permissions: checking negative", + a: AccessGrant{Permissions: AllPermissions()}, + perm: -22, + exp: false, + }, + { + name: "only settle: checking unspecified", + a: AccessGrant{Permissions: []Permission{Permission_settle}}, + perm: Permission_unspecified, + exp: false, + }, + { + name: "only settle: checking settle", + a: AccessGrant{Permissions: []Permission{Permission_settle}}, + perm: Permission_settle, + exp: true, + }, + { + name: "only settle: checking cancel", + a: AccessGrant{Permissions: []Permission{Permission_settle}}, + perm: Permission_cancel, + exp: false, + }, + { + name: "only settle: checking withdraw", + a: AccessGrant{Permissions: []Permission{Permission_settle}}, + perm: Permission_withdraw, + exp: false, + }, + { + name: "only settle: checking update", + a: AccessGrant{Permissions: []Permission{Permission_settle}}, + perm: Permission_update, + exp: false, + }, + { + name: "only settle: checking permissions", + a: AccessGrant{Permissions: []Permission{Permission_settle}}, + perm: Permission_permissions, + exp: false, + }, + { + name: "only settle: checking attributes", + a: AccessGrant{Permissions: []Permission{Permission_settle}}, + perm: Permission_attributes, + exp: false, + }, + { + name: "only settle: checking unknown", + a: AccessGrant{Permissions: []Permission{Permission_settle}}, + perm: 99, + exp: false, + }, + { + name: "only settle: checking negative", + a: AccessGrant{Permissions: []Permission{Permission_settle}}, + perm: -22, + exp: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.a.Contains(tc.perm) + } + require.NotPanics(t, testFunc, "%#v.Contains(%s)", tc.a, tc.perm.SimpleString()) + assert.Equal(t, tc.exp, actual, "%#v.Contains(%s)", tc.a, tc.perm.SimpleString()) + }) + } +} + func TestPermission_SimpleString(t *testing.T) { tests := []struct { name string diff --git a/x/exchange/msg.go b/x/exchange/msg.go index dc7bb7f4ab..9cc31173e7 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -214,10 +214,51 @@ func (m MsgMarketManagePermissionsRequest) ValidateBasic() error { errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) } - // TODO[1658]: MsgMarketManagePermissionsRequest.ValidateBasic() + if m.HasUpdates() { + for _, addrStr := range m.RevokeAll { + if _, err := sdk.AccAddressFromBech32(addrStr); err != nil { + errs = append(errs, fmt.Errorf("invalid revoke-all address %q: %w", addrStr, err)) + } + } + + if err := ValidateAccessGrantsField("to-revoke", m.ToRevoke); err != nil { + errs = append(errs, err) + } + + toRevokeByAddr := make(map[string]AccessGrant) + for _, ag := range m.ToRevoke { + if ContainsString(m.RevokeAll, ag.Address) { + errs = append(errs, fmt.Errorf("address %s appears in both the revoke-all and to-revoke fields", ag.Address)) + } + toRevokeByAddr[ag.Address] = ag + } + + if err := ValidateAccessGrantsField("to-grant", m.ToGrant); err != nil { + errs = append(errs, err) + } + + for _, ag := range m.ToGrant { + toRev, ok := toRevokeByAddr[ag.Address] + if ok { + for _, perm := range ag.Permissions { + if toRev.Contains(perm) { + errs = append(errs, fmt.Errorf("address %s has both revoke and grant %q", ag.Address, perm.SimpleString())) + } + } + } + } + } else { + errs = append(errs, errors.New("no updates")) + } + return errors.Join(errs...) } +// HasUpdates returns true if this has at least one permission change, false if devoid of updates. +func (m MsgMarketManagePermissionsRequest) HasUpdates() bool { + return len(m.RevokeAll) > 0 || len(m.ToRevoke) > 0 || len(m.ToGrant) > 0 +} + func (m MsgMarketManagePermissionsRequest) GetSigners() []sdk.AccAddress { addr := sdk.MustAccAddressFromBech32(m.Admin) return []sdk.AccAddress{addr} @@ -246,6 +287,7 @@ func (m MsgMarketManageReqAttrsRequest) ValidateBasic() error { return errors.Join(errs...) } +// HasUpdates returns true if this has at least one required attribute change, false if devoid of updates. func (m MsgMarketManageReqAttrsRequest) HasUpdates() bool { return len(m.CreateAskToAdd) > 0 || len(m.CreateAskToRemove) > 0 || len(m.CreateBidToAdd) > 0 || len(m.CreateBidToRemove) > 0 diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 94364629f1..74ddc1bf01 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -421,7 +421,155 @@ func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) { // TODO[1658]: func TestMsgMarketUpdateUserSettleRequest_ValidateBasic(t *testing.T) -// TODO[1658]: func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) +func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { + goodAdminAddr := sdk.AccAddress("goodAdminAddr_______").String() + goodAddr1 := sdk.AccAddress("goodAddr1___________").String() + goodAddr2 := sdk.AccAddress("goodAddr2___________").String() + goodAddr3 := sdk.AccAddress("goodAddr3___________").String() + + tests := []struct { + name string + msg MsgMarketManagePermissionsRequest + expErr []string + }{ + { + name: "control", + msg: MsgMarketManagePermissionsRequest{ + Admin: goodAdminAddr, + MarketId: 1, + RevokeAll: []string{goodAddr1}, + ToRevoke: []AccessGrant{{Address: goodAddr2, Permissions: []Permission{Permission_settle}}}, + ToGrant: []AccessGrant{{Address: goodAddr3, Permissions: []Permission{Permission_cancel}}}, + }, + expErr: nil, + }, + { + name: "invalid admin", + msg: MsgMarketManagePermissionsRequest{ + Admin: "bad1admin", + MarketId: 1, + RevokeAll: []string{goodAddr1}, + }, + expErr: []string{"invalid administrator", `"bad1admin"`, "decoding bech32 failed"}, + }, + { + name: "market id zero", + msg: MsgMarketManagePermissionsRequest{ + Admin: goodAdminAddr, + MarketId: 0, + RevokeAll: []string{goodAddr1}, + }, + expErr: []string{"invalid market id", "cannot be zero"}, + }, + { + name: "no updates", + msg: MsgMarketManagePermissionsRequest{ + Admin: goodAdminAddr, + MarketId: 1, + }, + expErr: []string{"no updates"}, + }, + { + name: "two invalid addresses in revoke all", + msg: MsgMarketManagePermissionsRequest{ + Admin: goodAdminAddr, + MarketId: 1, + RevokeAll: []string{"bad1addr", "bad2addr"}, + }, + expErr: []string{ + `invalid revoke-all address "bad1addr": decoding bech32 failed`, + `invalid revoke-all address "bad2addr": decoding bech32 failed`, + }, + }, + { + name: "two invalid to-revoke entries", + msg: MsgMarketManagePermissionsRequest{ + Admin: goodAdminAddr, + MarketId: 1, + ToRevoke: []AccessGrant{ + {Address: "badaddr", Permissions: []Permission{Permission_withdraw}}, + {Address: goodAddr1, Permissions: []Permission{Permission_unspecified}}, + }, + }, + expErr: []string{ + `invalid to-revoke access grant: invalid address "badaddr"`, + `invalid to-revoke access grant: permission is unspecified for ` + goodAddr1, + }, + }, + { + name: "two addrs in both revoke-all and to-revoke", + msg: MsgMarketManagePermissionsRequest{ + Admin: goodAdminAddr, + MarketId: 1, + RevokeAll: []string{goodAddr1, goodAddr2, goodAddr3}, + ToRevoke: []AccessGrant{ + {Address: goodAddr2, Permissions: []Permission{Permission_update}}, + {Address: goodAddr1, Permissions: []Permission{Permission_permissions}}, + }, + }, + expErr: []string{ + "address " + goodAddr2 + " appears in both the revoke-all and to-revoke fields", + "address " + goodAddr1 + " appears in both the revoke-all and to-revoke fields", + }, + }, + { + name: "two invalid to-grant entries", + msg: MsgMarketManagePermissionsRequest{ + Admin: goodAdminAddr, + MarketId: 1, + ToGrant: []AccessGrant{ + {Address: "badaddr", Permissions: []Permission{Permission_withdraw}}, + {Address: goodAddr1, Permissions: []Permission{Permission_unspecified}}, + }, + }, + expErr: []string{ + `invalid to-grant access grant: invalid address "badaddr"`, + `invalid to-grant access grant: permission is unspecified for ` + goodAddr1, + }, + }, + { + name: "revoke all for two addrs and some for one, then add some for all three", + msg: MsgMarketManagePermissionsRequest{ + Admin: goodAdminAddr, + MarketId: 1, + RevokeAll: []string{goodAddr1, goodAddr2}, + ToRevoke: []AccessGrant{{Address: goodAddr3, Permissions: []Permission{Permission_settle}}}, + ToGrant: []AccessGrant{ + {Address: goodAddr1, Permissions: []Permission{Permission_settle, Permission_update}}, + {Address: goodAddr2, Permissions: []Permission{Permission_cancel, Permission_permissions}}, + {Address: goodAddr3, Permissions: []Permission{Permission_withdraw, Permission_attributes}}, + }, + }, + expErr: nil, + }, + { + name: "revoke and grant the same permission for two addresses", + msg: MsgMarketManagePermissionsRequest{ + Admin: goodAdminAddr, + MarketId: 1, + ToRevoke: []AccessGrant{ + {Address: goodAddr2, Permissions: []Permission{Permission_permissions, Permission_cancel, Permission_attributes}}, + {Address: goodAddr1, Permissions: []Permission{Permission_settle, Permission_withdraw, Permission_update}}, + }, + ToGrant: []AccessGrant{ + {Address: goodAddr1, Permissions: []Permission{Permission_withdraw}}, + {Address: goodAddr2, Permissions: []Permission{Permission_attributes, Permission_permissions}}, + }, + }, + expErr: []string{ + "address " + goodAddr1 + " has both revoke and grant \"withdraw\"", + "address " + goodAddr2 + " has both revoke and grant \"attributes\"", + "address " + goodAddr2 + " has both revoke and grant \"permissions\"", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { goodAdmin := sdk.AccAddress("goodAdmin___________").String() From 2c94ac5013e558df17733e76c88ee03922c967a3 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 13:44:42 -0600 Subject: [PATCH 124/309] [1658]: Fill in the MsgMarketUpdateDetailsRequest MsgMarketUpdateEnabledRequest and MsgMarketUpdateUserSettleRequest message fields. --- docs/proto-docs.md | 15 +- proto/provenance/exchange/v1/tx.proto | 14 +- x/exchange/tx.pb.go | 331 ++++++++++++++++++-------- 3 files changed, 255 insertions(+), 105 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 5f57a1183b..802f682802 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2435,9 +2435,8 @@ MsgMarketUpdateDetailsRequest is a request message for the MarketUpdateDetails e | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `admin` | [string](#string) | | admin is the account with "update" permission requesting this change. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. - -TODO[1658]: MsgMarketUpdateDetailsRequest | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | +| `market_details` | [MarketDetails](#provenance.exchange.v1.MarketDetails) | | market_details is some information about this market. | @@ -2463,9 +2462,8 @@ MsgMarketUpdateEnabledRequest is a request message for the MarketUpdateEnabled e | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `admin` | [string](#string) | | admin is the account with "update" permission requesting this change. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. - -TODO[1658]: MsgMarketUpdateEnabledRequest | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | +| `accepting_orders` | [bool](#bool) | | accepting_orders is whether this market is allowing orders to be created for it. | @@ -2491,9 +2489,8 @@ MsgMarketUpdateUserSettleRequest is a request message for the MarketUpdateUserSe | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `admin` | [string](#string) | | admin is the account with "update" permission requesting this change. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. - -TODO[1658]: MsgMarketUpdateUserSettleRequest | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | +| `allow_user_settlement` | [bool](#bool) | | allow_user_settlement is whether this market allows users to initiate their own settlements. For example, the FillBids and FillAsks endpoints are available if and only if this is true. The MarketSettle endpoint is only available to market actors regardless of the value of this field. | diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index a8258cdffe..5f6848ed69 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -166,7 +166,9 @@ message MsgMarketUpdateDetailsRequest { string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // market_id is the numerical identifier of the market to update required attributes for. uint32 market_id = 2; - // TODO[1658]: MsgMarketUpdateDetailsRequest + + // market_details is some information about this market. + MarketDetails market_details = 3 [(gogoproto.nullable) = false]; } // MsgMarketUpdateDetailsResponse is a response message for the MarketUpdateDetails endpoint. @@ -180,7 +182,9 @@ message MsgMarketUpdateEnabledRequest { string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // market_id is the numerical identifier of the market to update required attributes for. uint32 market_id = 2; - // TODO[1658]: MsgMarketUpdateEnabledRequest + + // accepting_orders is whether this market is allowing orders to be created for it. + bool accepting_orders = 3; } // MsgMarketUpdateEnabledResponse is a response message for the MarketUpdateEnabled endpoint. @@ -194,7 +198,11 @@ message MsgMarketUpdateUserSettleRequest { string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // market_id is the numerical identifier of the market to update required attributes for. uint32 market_id = 2; - // TODO[1658]: MsgMarketUpdateUserSettleRequest + + // allow_user_settlement is whether this market allows users to initiate their own settlements. + // For example, the FillBids and FillAsks endpoints are available if and only if this is true. + // The MarketSettle endpoint is only available to market actors regardless of the value of this field. + bool allow_user_settlement = 3; } // MsgMarketUpdateUserSettleResponse is a response message for the MarketUpdateUserSettle endpoint. diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 5fc19cb7b7..8b891ff2cf 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -682,6 +682,8 @@ type MsgMarketUpdateDetailsRequest struct { Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` // market_id is the numerical identifier of the market to update required attributes for. MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // market_details is some information about this market. + MarketDetails MarketDetails `protobuf:"bytes,3,opt,name=market_details,json=marketDetails,proto3" json:"market_details"` } func (m *MsgMarketUpdateDetailsRequest) Reset() { *m = MsgMarketUpdateDetailsRequest{} } @@ -731,6 +733,13 @@ func (m *MsgMarketUpdateDetailsRequest) GetMarketId() uint32 { return 0 } +func (m *MsgMarketUpdateDetailsRequest) GetMarketDetails() MarketDetails { + if m != nil { + return m.MarketDetails + } + return MarketDetails{} +} + // MsgMarketUpdateDetailsResponse is a response message for the MarketUpdateDetails endpoint. type MsgMarketUpdateDetailsResponse struct { } @@ -774,6 +783,8 @@ type MsgMarketUpdateEnabledRequest struct { Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` // market_id is the numerical identifier of the market to update required attributes for. MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // accepting_orders is whether this market is allowing orders to be created for it. + AcceptingOrders bool `protobuf:"varint,3,opt,name=accepting_orders,json=acceptingOrders,proto3" json:"accepting_orders,omitempty"` } func (m *MsgMarketUpdateEnabledRequest) Reset() { *m = MsgMarketUpdateEnabledRequest{} } @@ -823,6 +834,13 @@ func (m *MsgMarketUpdateEnabledRequest) GetMarketId() uint32 { return 0 } +func (m *MsgMarketUpdateEnabledRequest) GetAcceptingOrders() bool { + if m != nil { + return m.AcceptingOrders + } + return false +} + // MsgMarketUpdateEnabledResponse is a response message for the MarketUpdateEnabled endpoint. type MsgMarketUpdateEnabledResponse struct { } @@ -866,6 +884,10 @@ type MsgMarketUpdateUserSettleRequest struct { Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` // market_id is the numerical identifier of the market to update required attributes for. MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // allow_user_settlement is whether this market allows users to initiate their own settlements. + // For example, the FillBids and FillAsks endpoints are available if and only if this is true. + // The MarketSettle endpoint is only available to market actors regardless of the value of this field. + AllowUserSettlement bool `protobuf:"varint,3,opt,name=allow_user_settlement,json=allowUserSettlement,proto3" json:"allow_user_settlement,omitempty"` } func (m *MsgMarketUpdateUserSettleRequest) Reset() { *m = MsgMarketUpdateUserSettleRequest{} } @@ -915,6 +937,13 @@ func (m *MsgMarketUpdateUserSettleRequest) GetMarketId() uint32 { return 0 } +func (m *MsgMarketUpdateUserSettleRequest) GetAllowUserSettlement() bool { + if m != nil { + return m.AllowUserSettlement + } + return false +} + // MsgMarketUpdateUserSettleResponse is a response message for the MarketUpdateUserSettle endpoint. type MsgMarketUpdateUserSettleResponse struct { } @@ -1621,99 +1650,104 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1472 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x4f, 0x6f, 0x1b, 0x45, - 0x14, 0xcf, 0xe6, 0x5f, 0xe3, 0xd7, 0x36, 0x6d, 0xa7, 0x6e, 0x62, 0x6f, 0x1b, 0xc7, 0x75, 0x38, - 0x94, 0x96, 0xac, 0x9b, 0x22, 0x0a, 0x44, 0x5c, 0xe2, 0x14, 0x47, 0x1c, 0x22, 0xaa, 0x0d, 0x15, - 0x12, 0x1c, 0xac, 0xb1, 0x77, 0xba, 0x59, 0x79, 0xbd, 0x93, 0xee, 0x4c, 0xdc, 0x54, 0xdc, 0x10, - 0x12, 0x27, 0xa4, 0x1e, 0xb9, 0x72, 0x84, 0x13, 0x48, 0x5c, 0xe0, 0x13, 0x54, 0x9c, 0x2a, 0x4e, - 0x9c, 0x00, 0xb5, 0x07, 0x3e, 0x02, 0x57, 0xb4, 0x33, 0xe3, 0xcd, 0xae, 0xbd, 0xeb, 0x5d, 0x87, - 0x06, 0x4e, 0xc9, 0xee, 0xbc, 0xf7, 0xfb, 0xf3, 0x3c, 0x9e, 0x37, 0xcf, 0xb0, 0x7a, 0xe0, 0xd3, - 0x3e, 0xf1, 0xb0, 0xd7, 0x21, 0x75, 0x72, 0xd4, 0xd9, 0xc7, 0x9e, 0x4d, 0xea, 0xfd, 0x8d, 0x3a, - 0x3f, 0x32, 0x0e, 0x7c, 0xca, 0x29, 0x5a, 0x3a, 0x0e, 0x30, 0x06, 0x01, 0x46, 0x7f, 0x43, 0xaf, - 0x74, 0x28, 0xeb, 0x51, 0x56, 0x6f, 0x63, 0x16, 0x24, 0xb4, 0x09, 0xc7, 0x1b, 0xf5, 0x0e, 0x75, - 0x3c, 0x99, 0xa7, 0x2f, 0xab, 0xf5, 0x1e, 0xb3, 0x03, 0xbc, 0x1e, 0xb3, 0xd5, 0x42, 0x59, 0x2e, - 0xb4, 0xc4, 0x53, 0x5d, 0x3e, 0xa8, 0xa5, 0xa2, 0x4d, 0x6d, 0x2a, 0xdf, 0x07, 0xff, 0xa9, 0xb7, - 0x6b, 0x29, 0x12, 0x7b, 0xd8, 0xef, 0x12, 0x9e, 0x11, 0x44, 0x7d, 0x8b, 0xf8, 0x2c, 0x23, 0xe8, - 0x00, 0xfb, 0xb8, 0xa7, 0x82, 0x6a, 0x3f, 0x6b, 0x70, 0x79, 0x97, 0xd9, 0xdb, 0x3e, 0xc1, 0x9c, - 0x6c, 0xb1, 0xae, 0x49, 0x1e, 0x1d, 0x12, 0xc6, 0xd1, 0x36, 0x14, 0x30, 0xeb, 0xb6, 0x04, 0x60, - 0x49, 0xab, 0x6a, 0x37, 0xce, 0xde, 0xa9, 0x1a, 0xc9, 0xc5, 0x31, 0xb6, 0x58, 0xf7, 0xc3, 0x20, - 0xae, 0x31, 0xfb, 0xec, 0xf7, 0xd5, 0x29, 0x73, 0x01, 0xab, 0x67, 0xb4, 0x03, 0x48, 0x00, 0xb4, - 0x3a, 0x01, 0xbc, 0x43, 0xbd, 0xd6, 0x43, 0x42, 0x4a, 0xd3, 0x02, 0xad, 0x6c, 0xa8, 0x62, 0x04, - 0x25, 0x35, 0x54, 0x49, 0x8d, 0x6d, 0xea, 0x78, 0xe6, 0x45, 0x91, 0xb4, 0xad, 0x72, 0x9a, 0x84, - 0x6c, 0x5e, 0xf9, 0xfc, 0xaf, 0xef, 0x6f, 0x5e, 0x0c, 0x05, 0x19, 0x8c, 0xb8, 0x2e, 0xf1, 0x6b, - 0x1b, 0x50, 0x8c, 0x6b, 0x67, 0x07, 0xd4, 0x63, 0x04, 0x95, 0x61, 0x41, 0xf2, 0x3a, 0x96, 0xd0, - 0x3e, 0x6b, 0x9e, 0x11, 0xcf, 0x1f, 0x58, 0xb5, 0x9f, 0xa2, 0x7e, 0x1b, 0x8e, 0x15, 0xf1, 0xdb, - 0x76, 0xac, 0x7c, 0x7e, 0x1b, 0x8e, 0x15, 0xf3, 0xdb, 0x56, 0xcf, 0xaf, 0xce, 0x6f, 0x31, 0xf0, - 0x7b, 0x21, 0x14, 0x64, 0xb4, 0x0f, 0x9f, 0x0c, 0xd9, 0x15, 0xd2, 0xb3, 0xed, 0x7a, 0x70, 0x25, - 0x48, 0x09, 0x2c, 0xb8, 0x42, 0xe3, 0xc0, 0xaf, 0x01, 0x73, 0xf4, 0xb1, 0xa7, 0xbc, 0x16, 0x1a, - 0xa5, 0x5f, 0x7f, 0x5c, 0x2f, 0x2a, 0x81, 0x5b, 0x96, 0xe5, 0x13, 0xc6, 0xf6, 0xb8, 0xef, 0x78, - 0xb6, 0x29, 0xc3, 0x62, 0x1c, 0xd3, 0x31, 0x8e, 0x4d, 0x08, 0xc4, 0xca, 0xb0, 0x5a, 0x09, 0x96, - 0x86, 0xf9, 0xa4, 0xc8, 0x5a, 0x11, 0xd0, 0x2e, 0xb3, 0x9b, 0x8e, 0xeb, 0x36, 0x1c, 0x8b, 0x29, - 0x19, 0xb5, 0x2b, 0xe2, 0xd3, 0x38, 0x7e, 0x3b, 0x12, 0xbc, 0xc5, 0xba, 0x09, 0xc1, 0xf2, 0xad, - 0x0a, 0x7e, 0x24, 0x38, 0x77, 0xc5, 0xf7, 0x63, 0x8f, 0x70, 0xee, 0x92, 0x88, 0x49, 0x6c, 0xf5, - 0x1c, 0x2f, 0xdb, 0xa4, 0x08, 0x43, 0x57, 0xa1, 0x20, 0xbf, 0x66, 0x03, 0x97, 0xe7, 0xcd, 0x05, - 0xf9, 0x22, 0xb4, 0x29, 0x02, 0x6b, 0x65, 0x58, 0x1e, 0xa1, 0x54, 0x6a, 0xfe, 0xd6, 0xa0, 0x14, - 0xae, 0x7d, 0xec, 0xf0, 0x7d, 0xcb, 0xc7, 0x8f, 0x4f, 0x43, 0x10, 0x5a, 0x01, 0xe0, 0xb4, 0x85, - 0x65, 0x5e, 0x69, 0x26, 0x40, 0x34, 0x0b, 0x9c, 0x2a, 0x20, 0xd4, 0x81, 0x79, 0xdc, 0xa3, 0x87, - 0x1e, 0x2f, 0xcd, 0x56, 0x67, 0xc6, 0x6e, 0xc0, 0xc6, 0xed, 0x60, 0x1f, 0x7f, 0xf7, 0xc7, 0xea, - 0x0d, 0xdb, 0xe1, 0xfb, 0x87, 0x6d, 0xa3, 0x43, 0x7b, 0xea, 0xa8, 0x52, 0x7f, 0xd6, 0x99, 0xd5, - 0xad, 0xf3, 0x27, 0x07, 0x84, 0x89, 0x04, 0x66, 0x2a, 0xe8, 0x58, 0x51, 0xae, 0x42, 0x39, 0xc1, - 0xb8, 0x2a, 0xcb, 0x11, 0xac, 0x84, 0x8b, 0x0f, 0x0e, 0x2c, 0xcc, 0xc9, 0x3d, 0xc2, 0xb1, 0xe3, - 0xb2, 0x53, 0xff, 0xac, 0xaa, 0x50, 0x49, 0x63, 0x4e, 0xd5, 0xf6, 0xbe, 0x87, 0xdb, 0x2e, 0xb1, - 0xfe, 0x07, 0x6d, 0x21, 0xb3, 0xd2, 0xf6, 0x19, 0x54, 0x87, 0x22, 0x1e, 0x30, 0xe2, 0xff, 0x47, - 0xdb, 0x7c, 0x0d, 0xae, 0x8f, 0x21, 0x57, 0x0a, 0xbf, 0x9d, 0x8e, 0x44, 0xed, 0x62, 0x0f, 0xdb, - 0xe4, 0x3e, 0xf1, 0x7b, 0x0e, 0x63, 0x0e, 0xf5, 0xd8, 0x69, 0xed, 0x7c, 0x9f, 0xf4, 0x69, 0x97, - 0xb4, 0xb0, 0xeb, 0x96, 0x66, 0xaa, 0x33, 0xc1, 0xce, 0x97, 0x6f, 0xb6, 0x5c, 0x17, 0x35, 0xa1, - 0xc0, 0x69, 0x4b, 0x3e, 0xab, 0xcd, 0xbf, 0x96, 0xda, 0xbb, 0x3a, 0x1d, 0xc2, 0xd8, 0x8e, 0x8f, - 0x3d, 0x3e, 0x38, 0xce, 0x39, 0x35, 0x45, 0x2a, 0xba, 0x07, 0x0b, 0x9c, 0xb6, 0xec, 0x60, 0xad, - 0x34, 0x37, 0x29, 0xcc, 0x19, 0x4e, 0xc5, 0x63, 0xac, 0xa0, 0xaf, 0x41, 0x6d, 0x5c, 0xa9, 0x54, - 0x45, 0x7f, 0x98, 0x8e, 0x6c, 0x0b, 0x19, 0x66, 0x92, 0x47, 0x5b, 0x9c, 0xfb, 0xa7, 0x53, 0xce, - 0xd7, 0xe1, 0x92, 0x68, 0x58, 0xa4, 0x15, 0x74, 0x58, 0x79, 0xa6, 0xa8, 0xaa, 0x2e, 0x76, 0x06, - 0xcd, 0xf5, 0xa3, 0xe0, 0x60, 0x41, 0x75, 0x28, 0xc6, 0x43, 0x7d, 0xd2, 0xa3, 0x7d, 0x59, 0xe5, - 0x82, 0x79, 0x29, 0x12, 0x6d, 0x8a, 0x85, 0x08, 0x76, 0xd0, 0xcd, 0x14, 0xf6, 0x5c, 0x14, 0xbb, - 0xe1, 0x58, 0xc3, 0xd8, 0x2a, 0x54, 0x61, 0xcf, 0x47, 0xb1, 0x45, 0xb4, 0xc4, 0x8e, 0x55, 0xf6, - 0x3a, 0xac, 0xa6, 0x96, 0x4c, 0x95, 0xf5, 0x1b, 0x4d, 0x1c, 0x50, 0x3b, 0xb4, 0x2f, 0x5b, 0xa8, - 0x0c, 0x1e, 0x54, 0xf4, 0x2e, 0x14, 0xf0, 0x21, 0xdf, 0xa7, 0xbe, 0xc3, 0x9f, 0x64, 0x56, 0xf5, - 0x38, 0x14, 0xbd, 0x07, 0xf3, 0xb2, 0x90, 0xaa, 0xcf, 0x57, 0xd2, 0xb6, 0x88, 0xa4, 0x53, 0xbb, - 0x43, 0xe5, 0x6c, 0x2e, 0x06, 0x16, 0x8e, 0xd1, 0x6a, 0xd7, 0x40, 0x4f, 0x92, 0xa8, 0x1c, 0xfc, - 0x02, 0xa2, 0xef, 0xec, 0xd0, 0xbe, 0xb4, 0xd8, 0x24, 0x84, 0xfd, 0x5b, 0xfd, 0x63, 0x77, 0xc6, - 0x03, 0x58, 0xc6, 0x96, 0x15, 0xdc, 0x62, 0x5a, 0x91, 0x8f, 0xfd, 0xa1, 0x8b, 0xb9, 0xd8, 0x1f, - 0x63, 0x9b, 0x8a, 0x34, 0x7a, 0x19, 0x5b, 0x56, 0x93, 0x90, 0xf0, 0x8e, 0xd6, 0x74, 0x31, 0x47, - 0x9f, 0x82, 0x2e, 0x3f, 0xdb, 0x44, 0xe4, 0xd9, 0x7c, 0xc8, 0x4b, 0x12, 0x62, 0x04, 0x7c, 0x54, - 0x73, 0xb0, 0x9d, 0x04, 0xf2, 0xdc, 0x09, 0x34, 0x37, 0x1c, 0x2b, 0x5d, 0x73, 0x88, 0x3c, 0x7f, - 0x32, 0xcd, 0x03, 0xf0, 0x0e, 0x54, 0x06, 0x9a, 0xe5, 0xd5, 0xb6, 0xc5, 0xc4, 0x21, 0xdb, 0x23, - 0x1e, 0x97, 0x04, 0x67, 0xf2, 0x11, 0xe8, 0x52, 0xfa, 0x9e, 0x00, 0xd9, 0x0b, 0x31, 0x04, 0x89, - 0x03, 0xd7, 0x23, 0x0e, 0x52, 0x78, 0x16, 0xf2, 0xf1, 0xac, 0x84, 0x46, 0x12, 0xa9, 0x3c, 0xa8, - 0xa6, 0xfb, 0xf1, 0x83, 0x6b, 0x2e, 0x2b, 0x15, 0x04, 0x53, 0xea, 0x25, 0xbb, 0x49, 0x88, 0x19, - 0x04, 0x2a, 0xc2, 0x6b, 0xc9, 0xc6, 0x44, 0x08, 0x43, 0x1c, 0xd6, 0xc6, 0x5a, 0x53, 0x94, 0x30, - 0x11, 0xe5, 0x6a, 0xaa, 0x47, 0xc5, 0x8a, 0x61, 0x65, 0xe0, 0x52, 0x5c, 0xd0, 0x47, 0x8a, 0x79, - 0x36, 0x5f, 0x31, 0xcb, 0xd2, 0x5b, 0x23, 0xc0, 0x18, 0x2a, 0xa4, 0x0d, 0xd5, 0x88, 0xb1, 0x64, - 0x96, 0x73, 0xf9, 0x58, 0xae, 0x85, 0x76, 0x92, 0x88, 0x5c, 0x58, 0x4d, 0xf5, 0xa2, 0xaa, 0x77, - 0x7e, 0xa2, 0xea, 0x5d, 0x4d, 0x34, 0xa5, 0x2a, 0xe7, 0x43, 0x6d, 0x9c, 0x2d, 0x45, 0xb8, 0x38, - 0x11, 0x61, 0x25, 0xcd, 0x9f, 0xe4, 0x1c, 0x39, 0x6a, 0x75, 0x71, 0x4f, 0x1f, 0x3a, 0x4b, 0x47, - 0x5a, 0x85, 0xbc, 0xf6, 0xdc, 0x17, 0x23, 0xf3, 0x2b, 0x68, 0x15, 0x72, 0xf6, 0xce, 0x6a, 0x15, - 0x92, 0x6e, 0xd0, 0x2a, 0x64, 0x4e, 0x7a, 0xab, 0x88, 0x4b, 0x94, 0x0e, 0xee, 0x7c, 0xbd, 0x08, - 0x33, 0xbb, 0xcc, 0x46, 0x0f, 0xa1, 0x10, 0x1e, 0x8f, 0xe8, 0x56, 0x6a, 0x6f, 0x1a, 0xfd, 0x05, - 0x40, 0x7f, 0x23, 0x5f, 0xb0, 0x9a, 0x41, 0x43, 0x9e, 0x86, 0x63, 0xe5, 0xe0, 0x39, 0x9e, 0xbc, - 0x73, 0xf0, 0x44, 0x67, 0x5d, 0x17, 0xce, 0x46, 0xa6, 0x4b, 0xb4, 0x3e, 0x2e, 0x79, 0x64, 0xea, - 0xd5, 0x8d, 0xbc, 0xe1, 0x8a, 0xad, 0x03, 0x0b, 0x83, 0xd9, 0x14, 0xdd, 0x1c, 0x93, 0x3b, 0x34, - 0xd6, 0xea, 0xb7, 0x72, 0xc5, 0xc6, 0x49, 0x82, 0x99, 0x36, 0x93, 0x24, 0x32, 0x0e, 0x67, 0x92, - 0x44, 0x87, 0x64, 0x44, 0xe1, 0x5c, 0x74, 0x5c, 0x45, 0xe3, 0x2a, 0x91, 0x30, 0x4a, 0xeb, 0xf5, - 0xdc, 0xf1, 0x8a, 0xf0, 0x10, 0x16, 0xe3, 0xa3, 0x20, 0xba, 0x9d, 0x09, 0x31, 0x34, 0x2e, 0xeb, - 0x1b, 0x13, 0x64, 0x28, 0xda, 0x2f, 0x34, 0xb8, 0x9c, 0x30, 0xeb, 0xa1, 0xb7, 0x32, 0xa1, 0x92, - 0xa6, 0x52, 0xfd, 0xee, 0xa4, 0x69, 0x29, 0x32, 0xd4, 0x58, 0x97, 0x5b, 0x46, 0x7c, 0x00, 0xcd, - 0x2d, 0x63, 0x68, 0x7a, 0x44, 0x5f, 0x69, 0xb0, 0x94, 0x3c, 0xbe, 0xa1, 0x77, 0x72, 0x42, 0x8e, - 0x8c, 0x9b, 0xfa, 0xbb, 0x27, 0xc8, 0x54, 0x7a, 0x9e, 0x6a, 0xb0, 0x9c, 0x32, 0xfd, 0xa0, 0x6c, - 0xd8, 0xb4, 0xe1, 0x52, 0xdf, 0x3c, 0x49, 0xaa, 0x92, 0xf4, 0xa5, 0x06, 0xc5, 0xa4, 0xb1, 0x01, - 0xdd, 0xcd, 0x09, 0x3a, 0x34, 0x9a, 0xe9, 0x6f, 0x4f, 0x9c, 0xa7, 0x94, 0x1c, 0xc1, 0x85, 0xa1, - 0x8b, 0x3f, 0x1a, 0xf7, 0x05, 0x48, 0x9e, 0x63, 0xf4, 0x3b, 0x93, 0xa4, 0x28, 0x66, 0x1f, 0xce, - 0xc7, 0xfa, 0x20, 0xaa, 0x8f, 0x07, 0x19, 0x99, 0x3e, 0xf4, 0xdb, 0xf9, 0x13, 0x62, 0x6e, 0xa3, - 0xbd, 0x2b, 0xcb, 0x6d, 0x42, 0x2b, 0xce, 0x72, 0x9b, 0xd4, 0x1a, 0x1b, 0xe4, 0xd9, 0x8b, 0x8a, - 0xf6, 0xfc, 0x45, 0x45, 0xfb, 0xf3, 0x45, 0x45, 0x7b, 0xfa, 0xb2, 0x32, 0xf5, 0xfc, 0x65, 0x65, - 0xea, 0xb7, 0x97, 0x95, 0x29, 0x28, 0x3b, 0x34, 0x05, 0xef, 0xbe, 0xf6, 0x89, 0x11, 0xf9, 0x71, - 0xec, 0x38, 0x68, 0xdd, 0xa1, 0x91, 0xa7, 0xfa, 0x51, 0xf8, 0x4b, 0x7b, 0x7b, 0x5e, 0xfc, 0xc0, - 0xfe, 0xe6, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x04, 0xc2, 0xef, 0xaa, 0x74, 0x18, 0x00, 0x00, + // 1550 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcf, 0x6f, 0xdc, 0xc4, + 0x17, 0x8f, 0xf3, 0xab, 0xd9, 0x97, 0x26, 0x6d, 0x27, 0xbf, 0x36, 0x6e, 0xb3, 0xd9, 0x6e, 0xbe, + 0x5f, 0xa9, 0xb4, 0xc4, 0xdb, 0x04, 0x51, 0x20, 0xe2, 0x92, 0x4d, 0x49, 0xc4, 0x21, 0xa2, 0x72, + 0xa9, 0x90, 0xe0, 0xb0, 0x9a, 0xb5, 0xa7, 0x8e, 0x15, 0xaf, 0x27, 0xf5, 0x4c, 0xd2, 0xf4, 0x8a, + 0x90, 0x38, 0x21, 0xf5, 0xc8, 0x15, 0x71, 0x82, 0x0b, 0x20, 0x71, 0x81, 0xbf, 0xa0, 0x42, 0x1c, + 0x2a, 0x4e, 0x9c, 0x00, 0xb5, 0x07, 0xfe, 0x04, 0xae, 0xc8, 0x33, 0xb3, 0x5e, 0x7b, 0xd7, 0x5e, + 0x7b, 0x43, 0x73, 0x6a, 0xed, 0x79, 0xef, 0xf3, 0xe3, 0xad, 0x67, 0x9e, 0x9f, 0x03, 0xab, 0x47, + 0x01, 0x3d, 0x21, 0x3e, 0xf6, 0x2d, 0x52, 0x27, 0xa7, 0xd6, 0x01, 0xf6, 0x1d, 0x52, 0x3f, 0xd9, + 0xa8, 0xf3, 0x53, 0xe3, 0x28, 0xa0, 0x9c, 0xa2, 0xc5, 0x6e, 0x80, 0xd1, 0x09, 0x30, 0x4e, 0x36, + 0xf4, 0x8a, 0x45, 0x59, 0x9b, 0xb2, 0x7a, 0x0b, 0xb3, 0x30, 0xa1, 0x45, 0x38, 0xde, 0xa8, 0x5b, + 0xd4, 0xf5, 0x65, 0x9e, 0xbe, 0xa4, 0xd6, 0xdb, 0xcc, 0x09, 0xf1, 0xda, 0xcc, 0x51, 0x0b, 0xcb, + 0x72, 0xa1, 0x29, 0xae, 0xea, 0xf2, 0x42, 0x2d, 0xcd, 0x3b, 0xd4, 0xa1, 0xf2, 0x7e, 0xf8, 0x3f, + 0x75, 0x77, 0x2d, 0x43, 0x62, 0x1b, 0x07, 0x87, 0x84, 0xe7, 0x04, 0xd1, 0xc0, 0x26, 0x01, 0xcb, + 0x09, 0x3a, 0xc2, 0x01, 0x6e, 0xab, 0xa0, 0xda, 0xcf, 0x1a, 0xcc, 0xed, 0x33, 0x67, 0x27, 0x20, + 0x98, 0x93, 0x6d, 0x76, 0x68, 0x92, 0x47, 0xc7, 0x84, 0x71, 0xb4, 0x03, 0x25, 0xcc, 0x0e, 0x9b, + 0x02, 0xb0, 0xac, 0x55, 0xb5, 0x1b, 0xd3, 0x9b, 0x55, 0x23, 0xbd, 0x38, 0xc6, 0x36, 0x3b, 0xfc, + 0x20, 0x8c, 0x6b, 0x8c, 0x3f, 0xfb, 0x63, 0x75, 0xc4, 0x9c, 0xc2, 0xea, 0x1a, 0xed, 0x01, 0x12, + 0x00, 0x4d, 0x2b, 0x84, 0x77, 0xa9, 0xdf, 0x7c, 0x48, 0x48, 0x79, 0x54, 0xa0, 0x2d, 0x1b, 0xaa, + 0x18, 0x61, 0x49, 0x0d, 0x55, 0x52, 0x63, 0x87, 0xba, 0xbe, 0x79, 0x59, 0x24, 0xed, 0xa8, 0x9c, + 0x5d, 0x42, 0xb6, 0x16, 0x3e, 0xfd, 0xfb, 0xfb, 0x9b, 0x97, 0x23, 0x41, 0x06, 0x23, 0x9e, 0x47, + 0x82, 0xda, 0x06, 0xcc, 0x27, 0xb5, 0xb3, 0x23, 0xea, 0x33, 0x82, 0x96, 0x61, 0x4a, 0xf2, 0xba, + 0xb6, 0xd0, 0x3e, 0x6e, 0x5e, 0x10, 0xd7, 0xef, 0xdb, 0xb5, 0x9f, 0xe2, 0x7e, 0x1b, 0xae, 0x1d, + 0xf3, 0xdb, 0x72, 0xed, 0x62, 0x7e, 0x1b, 0xae, 0x9d, 0xf0, 0xdb, 0x52, 0xd7, 0xaf, 0xce, 0xef, + 0x7c, 0xe8, 0xf7, 0x52, 0x24, 0xc8, 0x68, 0x1d, 0x3f, 0xe9, 0xb1, 0x2b, 0xa4, 0xe7, 0xdb, 0xf5, + 0x61, 0x21, 0x4c, 0x09, 0x2d, 0x78, 0x42, 0x63, 0xc7, 0xaf, 0x01, 0x13, 0xf4, 0xb1, 0xaf, 0xbc, + 0x96, 0x1a, 0xe5, 0xdf, 0x7e, 0x5c, 0x9f, 0x57, 0x02, 0xb7, 0x6d, 0x3b, 0x20, 0x8c, 0xdd, 0xe7, + 0x81, 0xeb, 0x3b, 0xa6, 0x0c, 0x4b, 0x70, 0x8c, 0x26, 0x38, 0xb6, 0x20, 0x14, 0x2b, 0xc3, 0x6a, + 0x65, 0x58, 0xec, 0xe5, 0x93, 0x22, 0x6b, 0xf3, 0x80, 0xf6, 0x99, 0xb3, 0xeb, 0x7a, 0x5e, 0xc3, + 0xb5, 0x99, 0x92, 0x51, 0x5b, 0x10, 0xbf, 0x46, 0xf7, 0x6e, 0x5f, 0xf0, 0x36, 0x3b, 0x4c, 0x09, + 0x96, 0x77, 0x55, 0xf0, 0x23, 0xc1, 0xb9, 0x2f, 0xf6, 0xc7, 0x7d, 0xc2, 0xb9, 0x47, 0x62, 0x26, + 0xb1, 0xdd, 0x76, 0xfd, 0x7c, 0x93, 0x22, 0x0c, 0x5d, 0x85, 0x92, 0xdc, 0x66, 0x1d, 0x97, 0x33, + 0xe6, 0x94, 0xbc, 0x11, 0xd9, 0x14, 0x81, 0xb5, 0x65, 0x58, 0xea, 0xa3, 0x54, 0x6a, 0xfe, 0xd1, + 0xa0, 0x1c, 0xad, 0x7d, 0xe4, 0xf2, 0x03, 0x3b, 0xc0, 0x8f, 0xcf, 0x43, 0x10, 0x5a, 0x01, 0xe0, + 0xb4, 0x89, 0x65, 0x5e, 0x79, 0x2c, 0x44, 0x34, 0x4b, 0x9c, 0x2a, 0x20, 0x64, 0xc1, 0x24, 0x6e, + 0xd3, 0x63, 0x9f, 0x97, 0xc7, 0xab, 0x63, 0x03, 0x1f, 0xc0, 0xc6, 0xed, 0xf0, 0x39, 0xfe, 0xf6, + 0xcf, 0xd5, 0x1b, 0x8e, 0xcb, 0x0f, 0x8e, 0x5b, 0x86, 0x45, 0xdb, 0xea, 0xa8, 0x52, 0xff, 0xac, + 0x33, 0xfb, 0xb0, 0xce, 0x9f, 0x1c, 0x11, 0x26, 0x12, 0x98, 0xa9, 0xa0, 0x13, 0x45, 0xb9, 0x0a, + 0xcb, 0x29, 0xc6, 0x55, 0x59, 0x7e, 0xd5, 0x60, 0x25, 0x5a, 0x7d, 0x70, 0x64, 0x63, 0x4e, 0xee, + 0x12, 0x8e, 0x5d, 0x8f, 0x9d, 0x4b, 0x6d, 0x4c, 0x98, 0x55, 0x8b, 0xb6, 0x64, 0x11, 0xf5, 0x99, + 0xde, 0xfc, 0x7f, 0xd6, 0x9e, 0x96, 0xc2, 0x94, 0x24, 0xb5, 0xb1, 0x67, 0xda, 0xf1, 0x9b, 0x09, + 0xaf, 0x55, 0xa8, 0x64, 0xb9, 0x51, 0x86, 0xbf, 0xee, 0x37, 0xfc, 0x9e, 0x8f, 0x5b, 0x1e, 0xb1, + 0xcf, 0xc5, 0xf0, 0x6b, 0x70, 0x19, 0x5b, 0x16, 0x39, 0xe2, 0xae, 0xef, 0xc8, 0x43, 0x43, 0x5a, + 0x9e, 0x32, 0x2f, 0x45, 0xf7, 0xc5, 0x86, 0xcc, 0xf3, 0x11, 0x89, 0x54, 0x3e, 0xbe, 0xd3, 0xa0, + 0xda, 0x13, 0xf2, 0x80, 0x91, 0xe0, 0xfc, 0x36, 0x1a, 0xda, 0x84, 0x05, 0xec, 0x79, 0xf4, 0x71, + 0xf3, 0x98, 0x91, 0xa0, 0xc9, 0x04, 0x51, 0x9b, 0xf8, 0x5c, 0xf9, 0x99, 0x13, 0x8b, 0x5d, 0x0d, + 0xe1, 0x52, 0xc2, 0xd3, 0x1a, 0x5c, 0x1f, 0x20, 0x58, 0xd9, 0xfa, 0x66, 0x34, 0x16, 0xb5, 0x8f, + 0x7d, 0xec, 0x90, 0x7b, 0x24, 0x68, 0xbb, 0x8c, 0xb9, 0xd4, 0x67, 0xe7, 0xb5, 0x5f, 0x03, 0x72, + 0x42, 0x0f, 0x49, 0x13, 0x7b, 0x5e, 0x79, 0xac, 0x3a, 0x16, 0xee, 0x57, 0x79, 0x67, 0xdb, 0xf3, + 0xd0, 0x2e, 0x94, 0x38, 0x6d, 0xca, 0x6b, 0xb5, 0x65, 0xd7, 0x32, 0x3b, 0xae, 0x65, 0x11, 0xc6, + 0xf6, 0x02, 0xec, 0xf3, 0x4e, 0x13, 0xe2, 0xd4, 0x14, 0xa9, 0xe8, 0x2e, 0x4c, 0x71, 0xda, 0x74, + 0xc2, 0xb5, 0xf2, 0xc4, 0xb0, 0x30, 0x17, 0x38, 0x15, 0x97, 0x89, 0x82, 0xfe, 0x0f, 0x6a, 0x83, + 0x4a, 0xa5, 0x2a, 0xfa, 0xc3, 0x68, 0xec, 0x59, 0x92, 0x61, 0x26, 0x79, 0xb4, 0xcd, 0x79, 0xc0, + 0xce, 0xe9, 0x89, 0xbf, 0x22, 0xda, 0x2c, 0x69, 0x86, 0xef, 0x05, 0xf2, 0x24, 0x54, 0x55, 0x9d, + 0xb5, 0x3a, 0xaf, 0x04, 0x1f, 0x86, 0xc7, 0x21, 0xaa, 0xc3, 0x7c, 0x32, 0x34, 0x20, 0x6d, 0x7a, + 0x22, 0xab, 0x5c, 0x32, 0xaf, 0xc4, 0xa2, 0x4d, 0xb1, 0x10, 0xc3, 0x0e, 0x7b, 0xb0, 0xc2, 0x9e, + 0x88, 0x63, 0x37, 0x5c, 0xbb, 0x17, 0x5b, 0x85, 0x2a, 0xec, 0xc9, 0x38, 0xb6, 0x88, 0x96, 0xd8, + 0x89, 0xca, 0x5e, 0x87, 0xd5, 0xcc, 0x92, 0xa9, 0xb2, 0x7e, 0xa5, 0x89, 0x63, 0x75, 0x8f, 0x9e, + 0xc8, 0xc6, 0x2f, 0x83, 0x3b, 0x15, 0xbd, 0x03, 0x25, 0x7c, 0xcc, 0x0f, 0x68, 0xe0, 0xf2, 0x27, + 0xb9, 0x55, 0xed, 0x86, 0xa2, 0x77, 0x61, 0x52, 0x16, 0x52, 0xbd, 0x9d, 0x54, 0x06, 0x9f, 0x8b, + 0xea, 0xe9, 0x50, 0x39, 0x5b, 0xb3, 0xa1, 0x85, 0x2e, 0x5a, 0xed, 0x1a, 0xe8, 0x69, 0x12, 0x95, + 0x83, 0x5f, 0x40, 0x74, 0xcb, 0x3d, 0x7a, 0x22, 0x2d, 0xee, 0x12, 0xc2, 0xfe, 0xab, 0xfe, 0x81, + 0x4f, 0xc6, 0x03, 0x58, 0xc2, 0xb6, 0x1d, 0xbe, 0x7b, 0x35, 0x63, 0x3f, 0xfb, 0x43, 0x0f, 0x73, + 0xf1, 0x7c, 0x0c, 0x6c, 0x85, 0xd2, 0xe8, 0x1c, 0xb6, 0xed, 0x5d, 0x42, 0xa2, 0x37, 0xcb, 0x5d, + 0x0f, 0x73, 0xf4, 0x09, 0xe8, 0xf2, 0xb7, 0x4d, 0x45, 0x1e, 0x2f, 0x86, 0xbc, 0x28, 0x21, 0xfa, + 0xc0, 0xfb, 0x35, 0x87, 0x8f, 0x93, 0x40, 0x9e, 0x38, 0x83, 0xe6, 0x86, 0x6b, 0x67, 0x6b, 0x8e, + 0x90, 0x27, 0xcf, 0xa6, 0xb9, 0x03, 0x6e, 0x41, 0xa5, 0xa3, 0x59, 0xbe, 0x90, 0xc7, 0x0e, 0x6b, + 0x49, 0x70, 0xa1, 0x18, 0x81, 0x2e, 0xa5, 0xdf, 0x17, 0x20, 0xdd, 0x53, 0x5d, 0x90, 0xb8, 0x70, + 0x3d, 0xe6, 0x20, 0x83, 0x67, 0xaa, 0x18, 0xcf, 0x4a, 0x64, 0x24, 0x95, 0xca, 0x87, 0x6a, 0xb6, + 0x9f, 0x20, 0x7c, 0x39, 0x67, 0xe5, 0x92, 0x60, 0xca, 0x1c, 0x0d, 0x76, 0x09, 0x31, 0xc3, 0x40, + 0x45, 0x78, 0x2d, 0xdd, 0x98, 0x08, 0x61, 0x88, 0xc3, 0xda, 0x40, 0x6b, 0x8a, 0x12, 0x86, 0xa2, + 0x5c, 0xcd, 0xf4, 0xa8, 0x58, 0x31, 0xac, 0x74, 0x5c, 0x8a, 0xb1, 0xa2, 0xaf, 0x98, 0xd3, 0xc5, + 0x8a, 0xb9, 0x2c, 0xbd, 0x35, 0x42, 0x8c, 0x9e, 0x42, 0x3a, 0x50, 0x8d, 0x19, 0x4b, 0x67, 0xb9, + 0x58, 0x8c, 0xe5, 0x5a, 0x64, 0x27, 0x8d, 0xc8, 0x83, 0xd5, 0x4c, 0x2f, 0xaa, 0x7a, 0x33, 0x43, + 0x55, 0xef, 0x6a, 0xaa, 0x29, 0x55, 0xb9, 0x00, 0x6a, 0x83, 0x6c, 0x29, 0xc2, 0xd9, 0xa1, 0x08, + 0x2b, 0x59, 0xfe, 0x24, 0x67, 0xdf, 0x51, 0xab, 0x8b, 0xe9, 0xa2, 0xe7, 0x2c, 0xed, 0x6b, 0x15, + 0xf2, 0xb5, 0xe7, 0x9e, 0x18, 0xf4, 0x5f, 0x41, 0xab, 0x90, 0x5f, 0x0c, 0xf2, 0x5a, 0x85, 0xa4, + 0xeb, 0xb4, 0x0a, 0x99, 0x93, 0xdd, 0x2a, 0x92, 0x12, 0xa5, 0x83, 0xcd, 0x2f, 0x67, 0x61, 0x6c, + 0x9f, 0x39, 0xe8, 0x21, 0x94, 0xa2, 0xe3, 0x11, 0xdd, 0xca, 0xec, 0x4d, 0xfd, 0xdf, 0x2d, 0xf4, + 0xd7, 0x8b, 0x05, 0xab, 0xc9, 0x39, 0xe2, 0x69, 0xb8, 0x76, 0x01, 0x9e, 0xee, 0xf7, 0x82, 0x02, + 0x3c, 0xf1, 0x09, 0xdd, 0x83, 0xe9, 0xd8, 0x4c, 0x8c, 0xd6, 0x07, 0x25, 0xf7, 0xcd, 0xea, 0xba, + 0x51, 0x34, 0x5c, 0xb1, 0x59, 0x30, 0xd5, 0x99, 0xa8, 0xd1, 0xcd, 0x01, 0xb9, 0x3d, 0xc3, 0xb8, + 0x7e, 0xab, 0x50, 0x6c, 0x92, 0x24, 0x9c, 0xc4, 0x73, 0x49, 0x62, 0x43, 0x7c, 0x2e, 0x49, 0x7c, + 0xb4, 0x47, 0x14, 0x2e, 0xc6, 0x87, 0x6c, 0x34, 0xa8, 0x12, 0x29, 0x1f, 0x00, 0xf4, 0x7a, 0xe1, + 0x78, 0x45, 0x78, 0x0c, 0xb3, 0xc9, 0x01, 0x16, 0xdd, 0xce, 0x85, 0xe8, 0x19, 0xf2, 0xf5, 0x8d, + 0x21, 0x32, 0x14, 0xed, 0x67, 0x1a, 0xcc, 0xa5, 0x0c, 0x93, 0xe8, 0xcd, 0x5c, 0xa8, 0xb4, 0x51, + 0x5a, 0xbf, 0x33, 0x6c, 0x5a, 0x86, 0x0c, 0x35, 0x0b, 0x16, 0x96, 0x91, 0x1c, 0x70, 0x0b, 0xcb, + 0xe8, 0x19, 0x39, 0xd1, 0x17, 0x1a, 0x2c, 0xa6, 0x8f, 0x6f, 0xe8, 0xed, 0x82, 0x90, 0x7d, 0x23, + 0xaa, 0xfe, 0xce, 0x19, 0x32, 0x95, 0x9e, 0xa7, 0x1a, 0x2c, 0x65, 0x4c, 0x3f, 0x28, 0x1f, 0x36, + 0x6b, 0xb8, 0xd4, 0xb7, 0xce, 0x92, 0xaa, 0x24, 0x7d, 0xae, 0xc1, 0x7c, 0xda, 0xd8, 0x80, 0xee, + 0x14, 0x04, 0xed, 0x19, 0xcd, 0xf4, 0xb7, 0x86, 0xce, 0x53, 0x4a, 0x4e, 0xe1, 0x52, 0xcf, 0x8b, + 0x3f, 0x1a, 0xb4, 0x01, 0xd2, 0xe7, 0x18, 0x7d, 0x73, 0x98, 0x14, 0xc5, 0x1c, 0xc0, 0x4c, 0xa2, + 0x0f, 0xa2, 0xfa, 0x60, 0x90, 0xbe, 0xe9, 0x43, 0xbf, 0x5d, 0x3c, 0x21, 0xe1, 0x36, 0xde, 0xbb, + 0xf2, 0xdc, 0xa6, 0xb4, 0xe2, 0x3c, 0xb7, 0x69, 0xad, 0xb1, 0x41, 0x9e, 0xbd, 0xa8, 0x68, 0xcf, + 0x5f, 0x54, 0xb4, 0xbf, 0x5e, 0x54, 0xb4, 0xa7, 0x2f, 0x2b, 0x23, 0xcf, 0x5f, 0x56, 0x46, 0x7e, + 0x7f, 0x59, 0x19, 0x81, 0x65, 0x97, 0x66, 0xe0, 0xdd, 0xd3, 0x3e, 0x36, 0x62, 0x9f, 0xf4, 0xba, + 0x41, 0xeb, 0x2e, 0x8d, 0x5d, 0xd5, 0x4f, 0xa3, 0xbf, 0x0f, 0xb4, 0x26, 0xc5, 0x9f, 0x05, 0xde, + 0xf8, 0x37, 0x00, 0x00, 0xff, 0xff, 0x47, 0xe8, 0x1a, 0xb8, 0x2a, 0x19, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2783,6 +2817,16 @@ func (m *MsgMarketUpdateDetailsRequest) MarshalToSizedBuffer(dAtA []byte) (int, _ = i var l int _ = l + { + size, err := m.MarketDetails.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a if m.MarketId != 0 { i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) i-- @@ -2841,6 +2885,16 @@ func (m *MsgMarketUpdateEnabledRequest) MarshalToSizedBuffer(dAtA []byte) (int, _ = i var l int _ = l + if m.AcceptingOrders { + i-- + if m.AcceptingOrders { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } if m.MarketId != 0 { i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) i-- @@ -2899,6 +2953,16 @@ func (m *MsgMarketUpdateUserSettleRequest) MarshalToSizedBuffer(dAtA []byte) (in _ = i var l int _ = l + if m.AllowUserSettlement { + i-- + if m.AllowUserSettlement { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } if m.MarketId != 0 { i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) i-- @@ -3677,6 +3741,8 @@ func (m *MsgMarketUpdateDetailsRequest) Size() (n int) { if m.MarketId != 0 { n += 1 + sovTx(uint64(m.MarketId)) } + l = m.MarketDetails.Size() + n += 1 + l + sovTx(uint64(l)) return n } @@ -3702,6 +3768,9 @@ func (m *MsgMarketUpdateEnabledRequest) Size() (n int) { if m.MarketId != 0 { n += 1 + sovTx(uint64(m.MarketId)) } + if m.AcceptingOrders { + n += 2 + } return n } @@ -3727,6 +3796,9 @@ func (m *MsgMarketUpdateUserSettleRequest) Size() (n int) { if m.MarketId != 0 { n += 1 + sovTx(uint64(m.MarketId)) } + if m.AllowUserSettlement { + n += 2 + } return n } @@ -5157,6 +5229,39 @@ func (m *MsgMarketUpdateDetailsRequest) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketDetails", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MarketDetails.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -5308,6 +5413,26 @@ func (m *MsgMarketUpdateEnabledRequest) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AcceptingOrders", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AcceptingOrders = bool(v != 0) default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -5459,6 +5584,26 @@ func (m *MsgMarketUpdateUserSettleRequest) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowUserSettlement", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowUserSettlement = bool(v != 0) default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) From 75ba1c1ae4cafb788d438dec7930d987b2f37539 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 14:10:55 -0600 Subject: [PATCH 125/309] [1658]: Implement ValidateBasic() for MsgMarketUpdateDetailsRequest MsgMarketUpdateEnabledRequest and MsgMarketUpdateUserSettleRequest. --- x/exchange/msg.go | 11 +- x/exchange/msg_test.go | 288 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 290 insertions(+), 9 deletions(-) diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 9cc31173e7..251d9127b2 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -154,7 +154,10 @@ func (m MsgMarketUpdateDetailsRequest) ValidateBasic() error { errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) } - // TODO[1658]: MsgMarketUpdateDetailsRequest.ValidateBasic() + if err := m.MarketDetails.Validate(); err != nil { + errs = append(errs, err) + } + return errors.Join(errs...) } @@ -174,7 +177,8 @@ func (m MsgMarketUpdateEnabledRequest) ValidateBasic() error { errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) } - // TODO[1658]: MsgMarketUpdateEnabledRequest.ValidateBasic() + // Nothing to validate for the AcceptingOrders field. + return errors.Join(errs...) } @@ -194,7 +198,8 @@ func (m MsgMarketUpdateUserSettleRequest) ValidateBasic() error { errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) } - // TODO[1658]: MsgMarketUpdateUserSettleRequest.ValidateBasic() + // Nothing to validate for the AllowUserSettlement field. + return errors.Join(errs...) } diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 74ddc1bf01..01599f1bc0 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -415,11 +415,261 @@ func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) { } } -// TODO[1658]: func TestMsgMarketUpdateDetailsRequest_ValidateBasic(t *testing.T) +func TestMsgMarketUpdateDetailsRequest_ValidateBasic(t *testing.T) { + admin := sdk.AccAddress("admin_______________").String() + tooLongErr := func(field string, max int) string { + return fmt.Sprintf("%s length %d exceeds maximum length of %d", field, max+1, max) + } + + tests := []struct { + name string + msg MsgMarketUpdateDetailsRequest + expErr []string + }{ + { + name: "control", + msg: MsgMarketUpdateDetailsRequest{ + Admin: admin, + MarketId: 1, + MarketDetails: MarketDetails{ + Name: "MyMarket", + Description: "This is my own market only for me.", + WebsiteUrl: "https://example.com", + IconUri: "https://example.com/icon", + }, + }, + expErr: nil, + }, + { + name: "empty admin", + msg: MsgMarketUpdateDetailsRequest{ + Admin: "", + MarketId: 1, + MarketDetails: MarketDetails{}, + }, + expErr: []string{`invalid administrator ""`, "empty address string is not allowed"}, + }, + { + name: "invalid admin", + msg: MsgMarketUpdateDetailsRequest{ + Admin: "notvalidadmin", + MarketId: 1, + MarketDetails: MarketDetails{}, + }, + expErr: []string{`invalid administrator "notvalidadmin"`, "decoding bech32 failed"}, + }, + { + name: "market id zero", + msg: MsgMarketUpdateDetailsRequest{ + Admin: admin, + MarketId: 0, + MarketDetails: MarketDetails{}, + }, + expErr: []string{"invalid market id", "cannot be zero"}, + }, + { + name: "name too long", + msg: MsgMarketUpdateDetailsRequest{ + Admin: admin, + MarketId: 1, + MarketDetails: MarketDetails{ + Name: strings.Repeat("p", MaxName+1), + }, + }, + expErr: []string{tooLongErr("name", MaxName)}, + }, + { + name: "description too long", + msg: MsgMarketUpdateDetailsRequest{ + Admin: admin, + MarketId: 1, + MarketDetails: MarketDetails{ + Description: strings.Repeat("d", MaxDescription+1), + }, + }, + expErr: []string{tooLongErr("description", MaxDescription)}, + }, + { + name: "website_url too long", + msg: MsgMarketUpdateDetailsRequest{ + Admin: admin, + MarketId: 1, + MarketDetails: MarketDetails{ + WebsiteUrl: strings.Repeat("w", MaxWebsiteURL+1), + }, + }, + expErr: []string{tooLongErr("website_url", MaxWebsiteURL)}, + }, + { + name: "icon_uri too long", + msg: MsgMarketUpdateDetailsRequest{ + Admin: admin, + MarketId: 1, + MarketDetails: MarketDetails{ + IconUri: strings.Repeat("i", MaxIconURI+1), + }, + }, + expErr: []string{tooLongErr("icon_uri", MaxIconURI)}, + }, + { + name: "multiple errors", + msg: MsgMarketUpdateDetailsRequest{ + Admin: "", + MarketId: 0, + MarketDetails: MarketDetails{ + Name: strings.Repeat("p", MaxName+1), + Description: strings.Repeat("d", MaxDescription+1), + WebsiteUrl: strings.Repeat("w", MaxWebsiteURL+1), + IconUri: strings.Repeat("i", MaxIconURI+1), + }, + }, + expErr: []string{ + "invalid administrator", + "invalid market id", + tooLongErr("name", MaxName), + tooLongErr("description", MaxDescription), + tooLongErr("website_url", MaxWebsiteURL), + tooLongErr("icon_uri", MaxIconURI), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} + +func TestMsgMarketUpdateEnabledRequest_ValidateBasic(t *testing.T) { + admin := sdk.AccAddress("admin_______________").String() + + tests := []struct { + name string + msg MsgMarketUpdateEnabledRequest + expErr []string + }{ + { + name: "control: true", + msg: MsgMarketUpdateEnabledRequest{ + Admin: admin, + MarketId: 1, + AcceptingOrders: true, + }, + expErr: nil, + }, + { + name: "control: false", + msg: MsgMarketUpdateEnabledRequest{ + Admin: admin, + MarketId: 1, + AcceptingOrders: true, + }, + expErr: nil, + }, + { + name: "empty admin", + msg: MsgMarketUpdateEnabledRequest{ + Admin: "", + MarketId: 1, + }, + expErr: []string{ + `invalid administrator ""`, "empty address string is not allowed", + }, + }, + { + name: "bad admin", + msg: MsgMarketUpdateEnabledRequest{ + Admin: "badadmin", + MarketId: 1, + }, + expErr: []string{ + `invalid administrator "badadmin"`, "decoding bech32 failed", + }, + }, + { + name: "market id zero", + msg: MsgMarketUpdateEnabledRequest{ + Admin: admin, + MarketId: 0, + }, + expErr: []string{ + "invalid market id", "cannot be zero", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} + +func TestMsgMarketUpdateUserSettleRequest_ValidateBasic(t *testing.T) { + admin := sdk.AccAddress("admin_______________").String() -// TODO[1658]: func TestMsgMarketUpdateEnabledRequest_ValidateBasic(t *testing.T) + tests := []struct { + name string + msg MsgMarketUpdateUserSettleRequest + expErr []string + }{ + { + name: "control: true", + msg: MsgMarketUpdateUserSettleRequest{ + Admin: admin, + MarketId: 1, + AllowUserSettlement: true, + }, + expErr: nil, + }, + { + name: "control: false", + msg: MsgMarketUpdateUserSettleRequest{ + Admin: admin, + MarketId: 1, + AllowUserSettlement: true, + }, + expErr: nil, + }, + { + name: "empty admin", + msg: MsgMarketUpdateUserSettleRequest{ + Admin: "", + MarketId: 1, + }, + expErr: []string{ + `invalid administrator ""`, "empty address string is not allowed", + }, + }, + { + name: "bad admin", + msg: MsgMarketUpdateUserSettleRequest{ + Admin: "badadmin", + MarketId: 1, + }, + expErr: []string{ + `invalid administrator "badadmin"`, "decoding bech32 failed", + }, + }, + { + name: "market id zero", + msg: MsgMarketUpdateUserSettleRequest{ + Admin: admin, + MarketId: 0, + }, + expErr: []string{ + "invalid market id", "cannot be zero", + }, + }, + } -// TODO[1658]: func TestMsgMarketUpdateUserSettleRequest_ValidateBasic(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { goodAdminAddr := sdk.AccAddress("goodAdminAddr_______").String() @@ -443,6 +693,15 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { }, expErr: nil, }, + { + name: "empty admin", + msg: MsgMarketManagePermissionsRequest{ + Admin: "", + MarketId: 1, + RevokeAll: []string{goodAddr1}, + }, + expErr: []string{`invalid administrator ""`, "empty address string is not allowed"}, + }, { name: "invalid admin", msg: MsgMarketManagePermissionsRequest{ @@ -450,7 +709,7 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { MarketId: 1, RevokeAll: []string{goodAddr1}, }, - expErr: []string{"invalid administrator", `"bad1admin"`, "decoding bech32 failed"}, + expErr: []string{`invalid administrator "bad1admin"`, "decoding bech32 failed"}, }, { name: "market id zero", @@ -493,7 +752,7 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { }, expErr: []string{ `invalid to-revoke access grant: invalid address "badaddr"`, - `invalid to-revoke access grant: permission is unspecified for ` + goodAddr1, + "invalid to-revoke access grant: permission is unspecified for " + goodAddr1, }, }, { @@ -524,7 +783,7 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { }, expErr: []string{ `invalid to-grant access grant: invalid address "badaddr"`, - `invalid to-grant access grant: permission is unspecified for ` + goodAddr1, + "invalid to-grant access grant: permission is unspecified for " + goodAddr1, }, }, { @@ -562,6 +821,23 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { "address " + goodAddr2 + " has both revoke and grant \"permissions\"", }, }, + { + name: "multiple errs", + msg: MsgMarketManagePermissionsRequest{ + Admin: "", + MarketId: 0, + RevokeAll: []string{"bad-revoke-addr"}, + ToRevoke: []AccessGrant{{Address: goodAddr1, Permissions: []Permission{Permission_unspecified}}}, + ToGrant: []AccessGrant{{Address: "bad-grant-addr", Permissions: []Permission{Permission_withdraw}}}, + }, + expErr: []string{ + "invalid administrator \"\"", + "invalid market id", + "invalid revoke-all address \"bad-revoke-addr\"", + "invalid to-revoke access grant: permission is unspecified for " + goodAddr1, + `invalid to-grant access grant: invalid address "bad-grant-addr"`, + }, + }, } for _, tc := range tests { From 373380d42b487a1dbef98671cffb92ca40b1c603 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 14:39:18 -0600 Subject: [PATCH 126/309] [1658]: Add the gogoproto equal function to MarketDetails. --- proto/provenance/exchange/v1/market.proto | 2 + x/exchange/market.pb.go | 161 +++++++++++++--------- 2 files changed, 99 insertions(+), 64 deletions(-) diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index ba6a1db3ae..bef0ff2217 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -30,6 +30,8 @@ message MarketAccount { // MarketDetails contains information about a market. message MarketDetails { + option (gogoproto.equal) = true; + // name is a moniker that people can use to refer to this market. string name = 1; // description extra information about this market. The field is meant to be human-readable. diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index 648a5829de..81caaa17f7 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -494,71 +494,104 @@ func init() { } var fileDescriptor_d5cf198f1dd7e167 = []byte{ - // 979 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x41, 0x4f, 0x1b, 0x47, - 0x14, 0xf6, 0x62, 0x03, 0xf6, 0x18, 0x88, 0x3b, 0x24, 0x74, 0x31, 0x95, 0xbd, 0x75, 0x14, 0xc9, - 0x69, 0x85, 0x2d, 0xa8, 0x7a, 0xe1, 0x66, 0x83, 0x69, 0x2d, 0x25, 0x04, 0xad, 0x6d, 0x45, 0x8a, - 0x2a, 0x6d, 0x67, 0x77, 0x9f, 0xcd, 0x88, 0xf5, 0xae, 0x33, 0x33, 0x0b, 0x49, 0x6f, 0x3d, 0xb5, - 0xe2, 0xd4, 0x63, 0x2f, 0x48, 0xe9, 0x3f, 0xe8, 0xa1, 0x3f, 0x22, 0x47, 0xd4, 0x53, 0x4f, 0xa8, - 0x82, 0x6b, 0x4f, 0xfd, 0x05, 0xd5, 0xce, 0x2c, 0xf6, 0xc6, 0x35, 0x22, 0x51, 0x6e, 0xf3, 0xde, - 0xf7, 0xcd, 0xf7, 0xbe, 0xf7, 0x66, 0x3c, 0x5e, 0xf4, 0x70, 0xc4, 0x82, 0x13, 0xf0, 0x89, 0xef, - 0x40, 0x1d, 0x5e, 0x39, 0x47, 0xc4, 0x1f, 0x40, 0xfd, 0x64, 0xab, 0x3e, 0x24, 0xec, 0x18, 0x44, - 0x6d, 0xc4, 0x02, 0x11, 0xe0, 0xb5, 0x09, 0xa9, 0x76, 0x43, 0xaa, 0x9d, 0x6c, 0x15, 0x4b, 0x4e, - 0xc0, 0x87, 0x01, 0xaf, 0x93, 0x50, 0x1c, 0xd5, 0x4f, 0xb6, 0x6c, 0x10, 0x64, 0x4b, 0x06, 0x6a, - 0xdf, 0x18, 0xb7, 0x09, 0x87, 0x31, 0xee, 0x04, 0xd4, 0x8f, 0xf1, 0x75, 0x85, 0x5b, 0x32, 0xaa, - 0xab, 0x20, 0x86, 0xee, 0x0f, 0x82, 0x41, 0xa0, 0xf2, 0xd1, 0x4a, 0x65, 0x2b, 0xff, 0x68, 0x68, - 0xf9, 0xa9, 0x74, 0xd6, 0x70, 0x9c, 0x20, 0xf4, 0x05, 0xfe, 0x1e, 0x2d, 0x45, 0xea, 0x16, 0x51, - 0xb1, 0xae, 0x19, 0x5a, 0x35, 0xbf, 0x6d, 0xd4, 0x62, 0x31, 0x69, 0x26, 0xae, 0x5c, 0x6b, 0x12, - 0x0e, 0xf1, 0xbe, 0xe6, 0xc6, 0xc5, 0x65, 0x59, 0xfb, 0xf7, 0xb2, 0xbc, 0xfa, 0x9a, 0x0c, 0xbd, - 0x9d, 0x4a, 0x52, 0xa3, 0x62, 0xe6, 0xed, 0x09, 0x13, 0x6f, 0xa0, 0x9c, 0x1a, 0x86, 0x45, 0x5d, - 0x7d, 0xce, 0xd0, 0xaa, 0xcb, 0x66, 0x56, 0x25, 0xda, 0x2e, 0x36, 0xd1, 0x4a, 0x0c, 0xba, 0x20, - 0x08, 0xf5, 0xb8, 0x9e, 0x96, 0x06, 0x1e, 0xd5, 0x66, 0x8f, 0xac, 0xa6, 0xdc, 0xef, 0x29, 0x72, - 0x33, 0xf3, 0xf6, 0xb2, 0x9c, 0x32, 0x97, 0x87, 0xc9, 0xe4, 0x4e, 0xf6, 0xe7, 0x37, 0xe5, 0xd4, - 0xaf, 0x6f, 0xca, 0xa9, 0xca, 0x8f, 0xe3, 0x76, 0x63, 0x0c, 0x63, 0x94, 0xf1, 0xc9, 0x10, 0x64, - 0x9b, 0x39, 0x53, 0xae, 0xb1, 0x81, 0xf2, 0x2e, 0x70, 0x87, 0xd1, 0x91, 0xa0, 0x81, 0x2f, 0x2d, - 0xe6, 0xcc, 0x64, 0x0a, 0x97, 0x51, 0xfe, 0x14, 0x6c, 0x4e, 0x05, 0x58, 0x21, 0xf3, 0xa4, 0xc5, - 0x9c, 0x89, 0xe2, 0x54, 0x8f, 0x79, 0x78, 0x1d, 0x65, 0xa9, 0x13, 0xf8, 0x56, 0xc8, 0xa8, 0x9e, - 0x91, 0xe8, 0x62, 0x14, 0xf7, 0x18, 0xad, 0xfc, 0xbe, 0x88, 0x16, 0x94, 0x87, 0x77, 0x27, 0xa1, - 0xdd, 0x39, 0x89, 0xb9, 0x8f, 0x9d, 0x04, 0x3e, 0x40, 0xab, 0x7d, 0x00, 0xcb, 0x61, 0x40, 0x04, - 0x58, 0x84, 0x1f, 0x5b, 0x7d, 0x8f, 0x08, 0x3d, 0x6d, 0xa4, 0xab, 0xf9, 0xed, 0xf5, 0x9b, 0x33, - 0x8e, 0x0e, 0x6b, 0x7c, 0xc6, 0xbb, 0x01, 0xf5, 0x63, 0xb1, 0x42, 0x1f, 0x60, 0x57, 0x6e, 0x6d, - 0xf0, 0xe3, 0x7d, 0x8f, 0x88, 0x29, 0x3d, 0x9b, 0xba, 0x4a, 0x2f, 0xf3, 0xa1, 0x7a, 0x4d, 0xea, - 0x4a, 0xbd, 0xef, 0x50, 0x31, 0xd2, 0xe3, 0xe0, 0x79, 0xc0, 0x2c, 0x0e, 0x42, 0x78, 0x30, 0x04, - 0x5f, 0x28, 0xd9, 0xf9, 0xf7, 0x93, 0xfd, 0xb4, 0x0f, 0xd0, 0x91, 0x0a, 0x9d, 0xb1, 0x80, 0x54, - 0x1f, 0xa0, 0xcf, 0x66, 0xab, 0x33, 0x22, 0x68, 0xc0, 0xf5, 0x05, 0xa9, 0x6f, 0xdc, 0x36, 0xdf, - 0x7d, 0x00, 0x33, 0x22, 0xc6, 0x65, 0xd6, 0x67, 0x94, 0x91, 0x38, 0xc7, 0x2f, 0x50, 0x04, 0x5a, - 0x76, 0xf8, 0x7a, 0x46, 0x17, 0x8b, 0xef, 0xd7, 0xc5, 0x5a, 0x1f, 0xa0, 0x19, 0x09, 0x4c, 0x35, - 0x01, 0x68, 0x63, 0xa6, 0x76, 0xdc, 0x43, 0xf6, 0x83, 0x7a, 0xd0, 0xff, 0x5f, 0x24, 0x6e, 0xe1, - 0x31, 0x2a, 0x10, 0xc7, 0x81, 0x91, 0xa0, 0xfe, 0xc0, 0x0a, 0x98, 0x0b, 0x8c, 0xeb, 0x39, 0x43, - 0xab, 0x66, 0xcd, 0x7b, 0xe3, 0xfc, 0x33, 0x99, 0xc6, 0xdb, 0xe8, 0x01, 0xf1, 0xbc, 0xe0, 0xd4, - 0x0a, 0xf9, 0x3b, 0x96, 0x74, 0x24, 0xf9, 0xab, 0x12, 0xec, 0xf1, 0x64, 0x11, 0x7c, 0x80, 0x96, - 0x23, 0x19, 0xce, 0xad, 0x01, 0x23, 0xbe, 0xe0, 0x7a, 0x5e, 0xfa, 0x7e, 0x78, 0x9b, 0xef, 0x86, - 0x24, 0x7f, 0x13, 0x71, 0x63, 0xeb, 0x4b, 0x64, 0x92, 0xe2, 0x78, 0x13, 0xad, 0x32, 0x78, 0x69, - 0x11, 0x21, 0x58, 0xe2, 0x76, 0xeb, 0x4b, 0x46, 0xba, 0x9a, 0x33, 0x0b, 0x0c, 0x5e, 0x36, 0x84, - 0x60, 0xe3, 0xbb, 0x3b, 0x8b, 0x6e, 0x53, 0x57, 0x5f, 0x9e, 0x41, 0x6f, 0x52, 0xb7, 0xf2, 0x03, - 0xca, 0xde, 0x0c, 0x0e, 0x7f, 0x8d, 0xe6, 0x47, 0x8c, 0x3a, 0x10, 0x3f, 0x8c, 0x77, 0x9e, 0xa3, - 0x62, 0xe3, 0x2d, 0x94, 0xee, 0x03, 0xc4, 0x3f, 0xe1, 0x3b, 0x37, 0x45, 0xdc, 0x9d, 0x8c, 0x7c, - 0xb2, 0x7e, 0xd2, 0x50, 0x3e, 0xd1, 0x3d, 0xde, 0x46, 0x8b, 0xc4, 0x75, 0x19, 0x70, 0xae, 0xde, - 0xac, 0xa6, 0xfe, 0xe7, 0x1f, 0x9b, 0xf7, 0x63, 0xbd, 0x86, 0x42, 0x3a, 0x82, 0x51, 0x7f, 0x60, - 0xde, 0x10, 0xf1, 0x1e, 0xca, 0x8f, 0x80, 0x0d, 0x29, 0xe7, 0x34, 0xf0, 0xa3, 0x77, 0x24, 0x5d, - 0x5d, 0xd9, 0xae, 0xdc, 0x36, 0xeb, 0xc3, 0x31, 0xd5, 0x4c, 0x6e, 0xfb, 0xe2, 0xb7, 0x39, 0x84, - 0x26, 0x18, 0xfe, 0x12, 0xad, 0x1d, 0xb6, 0xcc, 0xa7, 0xed, 0x4e, 0xa7, 0xfd, 0xec, 0xc0, 0xea, - 0x1d, 0x74, 0x0e, 0x5b, 0xbb, 0xed, 0xfd, 0x76, 0x6b, 0xaf, 0x90, 0x2a, 0xde, 0x3b, 0x3b, 0x37, - 0xf2, 0xa1, 0xcf, 0x47, 0xe0, 0xd0, 0x3e, 0x05, 0x17, 0x7f, 0x8e, 0x3e, 0x49, 0x90, 0x3b, 0xad, - 0x6e, 0xf7, 0x49, 0xab, 0xa0, 0x15, 0xd1, 0xd9, 0xb9, 0xb1, 0xa0, 0x6e, 0xcc, 0x14, 0x65, 0xb7, - 0x71, 0xb0, 0xdb, 0x7a, 0x52, 0x98, 0x53, 0x14, 0x27, 0x32, 0xe9, 0xe1, 0x47, 0x68, 0x35, 0x41, - 0x79, 0xde, 0xee, 0x7e, 0xbb, 0x67, 0x36, 0x9e, 0x17, 0xd2, 0xc5, 0xa5, 0xb3, 0x73, 0x23, 0x7b, - 0x4a, 0xc5, 0x91, 0xcb, 0xc8, 0xe9, 0x94, 0x52, 0xef, 0x70, 0xaf, 0xd1, 0x6d, 0x15, 0x32, 0x4a, - 0x29, 0x1c, 0xb9, 0x44, 0xc0, 0x94, 0xf9, 0xc9, 0xb2, 0x53, 0x98, 0x57, 0xe6, 0x13, 0x8d, 0xe3, - 0xc7, 0xe8, 0x41, 0x82, 0xdc, 0xe8, 0x76, 0xcd, 0x76, 0xb3, 0xd7, 0x6d, 0x75, 0x0a, 0x0b, 0xc5, - 0x95, 0xb3, 0x73, 0x03, 0x45, 0xd7, 0x88, 0xda, 0xa1, 0x00, 0xde, 0x84, 0xb7, 0x57, 0x25, 0xed, - 0xe2, 0xaa, 0xa4, 0xfd, 0x7d, 0x55, 0xd2, 0x7e, 0xb9, 0x2e, 0xa5, 0x2e, 0xae, 0x4b, 0xa9, 0xbf, - 0xae, 0x4b, 0x29, 0xb4, 0x4e, 0x83, 0x5b, 0x06, 0x7e, 0xa8, 0xbd, 0xa8, 0x0d, 0xa8, 0x38, 0x0a, - 0xed, 0x9a, 0x13, 0x0c, 0xeb, 0x13, 0xd2, 0x26, 0x0d, 0x12, 0x51, 0xfd, 0xd5, 0xf8, 0x7b, 0xc2, - 0x5e, 0x90, 0xff, 0xde, 0x5f, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0x64, 0xff, 0x9b, 0xb8, 0x6d, - 0x08, 0x00, 0x00, + // 985 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x41, 0x4f, 0x1b, 0xc7, + 0x17, 0xf7, 0x62, 0x03, 0xf6, 0x18, 0x88, 0xff, 0x43, 0xc2, 0x7f, 0x31, 0x95, 0xbd, 0x75, 0x14, + 0xc9, 0x69, 0x85, 0x2d, 0xa8, 0x7a, 0xe1, 0x66, 0x83, 0x69, 0x2d, 0x25, 0x04, 0xad, 0x6d, 0x45, + 0x8a, 0x2a, 0x6d, 0x67, 0x77, 0x9f, 0xcd, 0x88, 0xf5, 0xae, 0x33, 0x33, 0x0b, 0x49, 0xbf, 0x40, + 0x2b, 0x4e, 0x3d, 0xf6, 0x82, 0x44, 0xbf, 0x41, 0x0f, 0xfd, 0x10, 0x39, 0xa2, 0x9e, 0x7a, 0x42, + 0x15, 0x5c, 0x7a, 0xe8, 0xa9, 0x9f, 0xa0, 0xda, 0x99, 0xc5, 0xde, 0xb8, 0x46, 0x24, 0xea, 0x6d, + 0xde, 0xfb, 0xfd, 0xe6, 0xf7, 0x7e, 0xef, 0xcd, 0x78, 0xbc, 0xe8, 0xf1, 0x88, 0x05, 0x27, 0xe0, + 0x13, 0xdf, 0x81, 0x3a, 0xbc, 0x71, 0x8e, 0x88, 0x3f, 0x80, 0xfa, 0xc9, 0x56, 0x7d, 0x48, 0xd8, + 0x31, 0x88, 0xda, 0x88, 0x05, 0x22, 0xc0, 0x6b, 0x13, 0x52, 0xed, 0x96, 0x54, 0x3b, 0xd9, 0x2a, + 0x96, 0x9c, 0x80, 0x0f, 0x03, 0x5e, 0x27, 0xa1, 0x38, 0xaa, 0x9f, 0x6c, 0xd9, 0x20, 0xc8, 0x96, + 0x0c, 0xd4, 0xbe, 0x31, 0x6e, 0x13, 0x0e, 0x63, 0xdc, 0x09, 0xa8, 0x1f, 0xe3, 0xeb, 0x0a, 0xb7, + 0x64, 0x54, 0x57, 0x41, 0x0c, 0x3d, 0x1c, 0x04, 0x83, 0x40, 0xe5, 0xa3, 0x95, 0xca, 0x56, 0xfe, + 0xd2, 0xd0, 0xf2, 0x73, 0xe9, 0xac, 0xe1, 0x38, 0x41, 0xe8, 0x0b, 0xfc, 0x2d, 0x5a, 0x8a, 0xd4, + 0x2d, 0xa2, 0x62, 0x5d, 0x33, 0xb4, 0x6a, 0x7e, 0xdb, 0xa8, 0xc5, 0x62, 0xd2, 0x4c, 0x5c, 0xb9, + 0xd6, 0x24, 0x1c, 0xe2, 0x7d, 0xcd, 0x8d, 0xcb, 0xab, 0xb2, 0xf6, 0xf7, 0x55, 0x79, 0xf5, 0x2d, + 0x19, 0x7a, 0x3b, 0x95, 0xa4, 0x46, 0xc5, 0xcc, 0xdb, 0x13, 0x26, 0xde, 0x40, 0x39, 0x35, 0x0c, + 0x8b, 0xba, 0xfa, 0x9c, 0xa1, 0x55, 0x97, 0xcd, 0xac, 0x4a, 0xb4, 0x5d, 0x6c, 0xa2, 0x95, 0x18, + 0x74, 0x41, 0x10, 0xea, 0x71, 0x3d, 0x2d, 0x0d, 0x3c, 0xa9, 0xcd, 0x1e, 0x59, 0x4d, 0xb9, 0xdf, + 0x53, 0xe4, 0x66, 0xe6, 0xdd, 0x55, 0x39, 0x65, 0x2e, 0x0f, 0x93, 0xc9, 0x9d, 0xec, 0x0f, 0x17, + 0xe5, 0xd4, 0x4f, 0x17, 0xe5, 0x54, 0xe5, 0xfb, 0x71, 0xbb, 0x31, 0x86, 0x31, 0xca, 0xf8, 0x64, + 0x08, 0xb2, 0xcd, 0x9c, 0x29, 0xd7, 0xd8, 0x40, 0x79, 0x17, 0xb8, 0xc3, 0xe8, 0x48, 0xd0, 0xc0, + 0x97, 0x16, 0x73, 0x66, 0x32, 0x85, 0xcb, 0x28, 0x7f, 0x0a, 0x36, 0xa7, 0x02, 0xac, 0x90, 0x79, + 0xd2, 0x62, 0xce, 0x44, 0x71, 0xaa, 0xc7, 0x3c, 0xbc, 0x8e, 0xb2, 0xd4, 0x09, 0x7c, 0x2b, 0x64, + 0x54, 0xcf, 0x48, 0x74, 0x31, 0x8a, 0x7b, 0x8c, 0xee, 0x64, 0xfe, 0xbc, 0x28, 0x6b, 0x95, 0x5f, + 0x16, 0xd1, 0x82, 0x72, 0xf2, 0xfe, 0x3c, 0xb4, 0x7b, 0xe7, 0x31, 0xf7, 0x5f, 0xe7, 0x81, 0x0f, + 0xd0, 0x6a, 0x1f, 0xc0, 0x72, 0x18, 0x10, 0x01, 0x16, 0xe1, 0xc7, 0x56, 0xdf, 0x23, 0x42, 0x4f, + 0x1b, 0xe9, 0x6a, 0x7e, 0x7b, 0xfd, 0xf6, 0xa4, 0xa3, 0x23, 0x1b, 0x9f, 0xf4, 0x6e, 0x40, 0xfd, + 0x58, 0xac, 0xd0, 0x07, 0xd8, 0x95, 0x5b, 0x1b, 0xfc, 0x78, 0xdf, 0x23, 0x62, 0x4a, 0xcf, 0xa6, + 0xae, 0xd2, 0xcb, 0x7c, 0xac, 0x5e, 0x93, 0xba, 0x52, 0xef, 0x1b, 0x54, 0x8c, 0xf4, 0x38, 0x78, + 0x1e, 0x30, 0x8b, 0x83, 0x10, 0x1e, 0x0c, 0xc1, 0x17, 0x4a, 0x76, 0xfe, 0xc3, 0x64, 0xff, 0xdf, + 0x07, 0xe8, 0x48, 0x85, 0xce, 0x58, 0x40, 0xaa, 0x0f, 0xd0, 0x27, 0xb3, 0xd5, 0x19, 0x11, 0x34, + 0xe0, 0xfa, 0x82, 0xd4, 0x37, 0xee, 0x9a, 0xef, 0x3e, 0x80, 0x19, 0x11, 0xe3, 0x32, 0xeb, 0x33, + 0xca, 0x48, 0x9c, 0xe3, 0x57, 0x28, 0x02, 0x2d, 0x3b, 0x7c, 0x3b, 0xa3, 0x8b, 0xc5, 0x0f, 0xeb, + 0x62, 0xad, 0x0f, 0xd0, 0x8c, 0x04, 0xa6, 0x9a, 0x00, 0xb4, 0x31, 0x53, 0x3b, 0xee, 0x21, 0xfb, + 0x51, 0x3d, 0xe8, 0xff, 0x2e, 0x12, 0xb7, 0xf0, 0x14, 0x15, 0x88, 0xe3, 0xc0, 0x48, 0x50, 0x7f, + 0x60, 0x05, 0xcc, 0x05, 0xc6, 0xf5, 0x9c, 0xa1, 0x55, 0xb3, 0xe6, 0x83, 0x71, 0xfe, 0x85, 0x4c, + 0xe3, 0x6d, 0xf4, 0x88, 0x78, 0x5e, 0x70, 0x6a, 0x85, 0xfc, 0x3d, 0x4b, 0x3a, 0x92, 0xfc, 0x55, + 0x09, 0xf6, 0x78, 0xb2, 0x08, 0x3e, 0x40, 0xcb, 0x91, 0x0c, 0xe7, 0xd6, 0x80, 0x11, 0x5f, 0x70, + 0x3d, 0x2f, 0x7d, 0x3f, 0xbe, 0xcb, 0x77, 0x43, 0x92, 0xbf, 0x8a, 0xb8, 0xb1, 0xf5, 0x25, 0x32, + 0x49, 0x71, 0xbc, 0x89, 0x56, 0x19, 0xbc, 0xb6, 0x88, 0x10, 0x2c, 0x71, 0xbb, 0xf5, 0x25, 0x23, + 0x5d, 0xcd, 0x99, 0x05, 0x06, 0xaf, 0x1b, 0x42, 0xb0, 0xf1, 0xdd, 0x9d, 0x45, 0xb7, 0xa9, 0xab, + 0x2f, 0xcf, 0xa0, 0x37, 0xa9, 0x5b, 0xf9, 0x0e, 0x65, 0x6f, 0x07, 0x87, 0xbf, 0x44, 0xf3, 0x23, + 0x46, 0x1d, 0x88, 0x9f, 0xc7, 0x7b, 0xcf, 0x51, 0xb1, 0xf1, 0x16, 0x4a, 0xf7, 0x01, 0xe2, 0x9f, + 0xf0, 0xbd, 0x9b, 0x22, 0xee, 0x4e, 0xe6, 0xf6, 0xe1, 0xca, 0x27, 0xba, 0xc7, 0xdb, 0x68, 0x91, + 0xb8, 0x2e, 0x03, 0xce, 0xd5, 0xcb, 0xd5, 0xd4, 0x7f, 0xfb, 0x75, 0xf3, 0x61, 0xac, 0xd7, 0x50, + 0x48, 0x47, 0x30, 0xea, 0x0f, 0xcc, 0x5b, 0x22, 0xde, 0x43, 0xf9, 0x11, 0xb0, 0x21, 0xe5, 0x9c, + 0x06, 0x7e, 0xf4, 0x8e, 0xa4, 0xab, 0x2b, 0xdb, 0x95, 0xbb, 0x66, 0x7d, 0x38, 0xa6, 0x9a, 0xc9, + 0x6d, 0x9f, 0xfd, 0x3c, 0x87, 0xd0, 0x04, 0xc3, 0x9f, 0xa3, 0xb5, 0xc3, 0x96, 0xf9, 0xbc, 0xdd, + 0xe9, 0xb4, 0x5f, 0x1c, 0x58, 0xbd, 0x83, 0xce, 0x61, 0x6b, 0xb7, 0xbd, 0xdf, 0x6e, 0xed, 0x15, + 0x52, 0xc5, 0x07, 0x67, 0xe7, 0x46, 0x3e, 0xf4, 0xf9, 0x08, 0x1c, 0xda, 0xa7, 0xe0, 0xe2, 0x4f, + 0xd1, 0xff, 0x12, 0xe4, 0x4e, 0xab, 0xdb, 0x7d, 0xd6, 0x2a, 0x68, 0x45, 0x74, 0x76, 0x6e, 0x2c, + 0xa8, 0x1b, 0x33, 0x45, 0xd9, 0x6d, 0x1c, 0xec, 0xb6, 0x9e, 0x15, 0xe6, 0x14, 0xc5, 0x89, 0x4c, + 0x7a, 0xf8, 0x09, 0x5a, 0x4d, 0x50, 0x5e, 0xb6, 0xbb, 0x5f, 0xef, 0x99, 0x8d, 0x97, 0x85, 0x74, + 0x71, 0xe9, 0xec, 0xdc, 0xc8, 0x9e, 0x52, 0x71, 0xe4, 0x32, 0x72, 0x3a, 0xa5, 0xd4, 0x3b, 0xdc, + 0x6b, 0x74, 0x5b, 0x85, 0x8c, 0x52, 0x0a, 0x47, 0x2e, 0x11, 0x30, 0x65, 0x7e, 0xb2, 0xec, 0x14, + 0xe6, 0x95, 0xf9, 0x44, 0xe3, 0xf8, 0x29, 0x7a, 0x94, 0x20, 0x37, 0xba, 0x5d, 0xb3, 0xdd, 0xec, + 0x75, 0x5b, 0x9d, 0xc2, 0x42, 0x71, 0xe5, 0xec, 0xdc, 0x40, 0xd1, 0x35, 0xa2, 0x76, 0x28, 0x80, + 0x37, 0xe1, 0xdd, 0x75, 0x49, 0xbb, 0xbc, 0x2e, 0x69, 0x7f, 0x5c, 0x97, 0xb4, 0x1f, 0x6f, 0x4a, + 0xa9, 0xcb, 0x9b, 0x52, 0xea, 0xf7, 0x9b, 0x52, 0x0a, 0xad, 0xd3, 0xe0, 0x8e, 0x81, 0x1f, 0x6a, + 0xaf, 0x6a, 0x03, 0x2a, 0x8e, 0x42, 0xbb, 0xe6, 0x04, 0xc3, 0xfa, 0x84, 0xb4, 0x49, 0x83, 0x44, + 0x54, 0x7f, 0x33, 0xfe, 0xaa, 0xb0, 0x17, 0xe4, 0x7f, 0xf8, 0x17, 0xff, 0x04, 0x00, 0x00, 0xff, + 0xff, 0x7a, 0x16, 0x37, 0xe4, 0x73, 0x08, 0x00, 0x00, +} + +func (this *MarketDetails) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MarketDetails) + if !ok { + that2, ok := that.(MarketDetails) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Name != that1.Name { + return false + } + if this.Description != that1.Description { + return false + } + if this.WebsiteUrl != that1.WebsiteUrl { + return false + } + if this.IconUri != that1.IconUri { + return false + } + return true } - func (m *MarketAccount) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) From 2111d7a1a2dda850720a13f297fe12c3b8dbf2d5 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 14:40:06 -0600 Subject: [PATCH 127/309] [1658]: Implement MarketUpdateDetails MarketUpdateEnabled and MarketUpdateUserSettle. --- x/exchange/keeper/market.go | 60 +++++++++++++++++++++++++++++++-- x/exchange/keeper/msg_server.go | 33 ++++++++++++++---- 2 files changed, 84 insertions(+), 9 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index eede10d2dc..0da88c0821 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -568,6 +568,27 @@ func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Co return errors.Join(errs...) } +// UpdateMarketDetails updates a market's details. It returns an error if the market account +// isn't found or if there aren't any changes provided. +func (k Keeper) UpdateMarketDetails(ctx sdk.Context, marketID uint32, marketDetails *exchange.MarketDetails) error { + if err := marketDetails.Validate(); err != nil { + return err + } + + marketAcc := k.GetMarketAccount(ctx, marketID) + if marketAcc == nil { + return fmt.Errorf("market %d account not found", marketID) + } + + if marketAcc.MarketDetails.Equal(marketDetails) { + return errors.New("no changes") + } + + marketAcc.MarketDetails = *marketDetails + k.accountKeeper.SetAccount(ctx, marketAcc) + return nil +} + // isMarketActive returns true if the provided market is accepting orders. func isMarketActive(store sdk.KVStore, marketID uint32) bool { key := MakeKeyMarketInactive(marketID) @@ -594,6 +615,18 @@ func (k Keeper) SetMarketActive(ctx sdk.Context, marketID uint32, active bool) { setMarketActive(k.getStore(ctx), marketID, active) } +// UpdateMarketActive updates the active flag for a market. +// An error is returned if the setting is already what is provided. +func (k Keeper) UpdateMarketActive(ctx sdk.Context, marketID uint32, active bool) error { + store := k.getStore(ctx) + current := isMarketActive(store, marketID) + if current == active { + return fmt.Errorf("market %d already has accepting-orders %t", marketID, active) + } + setMarketActive(store, marketID, active) + return nil +} + // isUserSettlementAllowed gets whether user-settlement is allowed for a market. func isUserSettlementAllowed(store sdk.KVStore, marketID uint32) bool { key := MakeKeyMarketUserSettle(marketID) @@ -620,6 +653,18 @@ func (k Keeper) SetUserSettlementAllowed(ctx sdk.Context, marketID uint32, allow setUserSettlementAllowed(k.getStore(ctx), marketID, allowed) } +// UpdateUserSettlementAllowed updates the allow-user-settlement flag for a market. +// An error is returned if the setting is already what is provided. +func (k Keeper) UpdateUserSettlementAllowed(ctx sdk.Context, marketID uint32, allow bool) error { + store := k.getStore(ctx) + current := isUserSettlementAllowed(store, marketID) + if current == allow { + return fmt.Errorf("market %d already has allow-user-settlement %t", marketID, allow) + } + setUserSettlementAllowed(store, marketID, allow) + return nil +} + // storeHasPermission returns true if there is an entry in the store for the given market, address, and permissions. func storeHasPermission(store sdk.KVStore, marketID uint32, addr sdk.AccAddress, permission exchange.Permission) bool { key := MakeKeyMarketPermissions(marketID, addr, permission) @@ -910,9 +955,8 @@ func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (marketID return marketID, err } -// GetMarket reads all the market info from state and returns it. -// Returns nil if the market account doesn't exist or it's not a market account. -func (k Keeper) GetMarket(ctx sdk.Context, marketID uint32) *exchange.Market { +// GetMarketAccount gets a market's account from the account module. +func (k Keeper) GetMarketAccount(ctx sdk.Context, marketID uint32) *exchange.MarketAccount { marketAddr := exchange.GetMarketAddress(marketID) acc := k.accountKeeper.GetAccount(ctx, marketAddr) if acc == nil { @@ -922,6 +966,16 @@ func (k Keeper) GetMarket(ctx sdk.Context, marketID uint32) *exchange.Market { if !ok { return nil } + return marketAcc +} + +// GetMarket reads all the market info from state and returns it. +// Returns nil if the market account doesn't exist or it's not a market account. +func (k Keeper) GetMarket(ctx sdk.Context, marketID uint32) *exchange.Market { + marketAcc := k.GetMarketAccount(ctx, marketID) + if marketAcc == nil { + return nil + } store := k.getStore(ctx) market := &exchange.Market{MarketId: marketID} diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index b878462868..0db27baa88 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -88,20 +88,41 @@ func (k MsgServer) MarketWithdraw(goCtx context.Context, msg *exchange.MsgMarket // MarketUpdateDetails is a market endpoint to update its details. func (k MsgServer) MarketUpdateDetails(goCtx context.Context, msg *exchange.MsgMarketUpdateDetailsRequest) (*exchange.MsgMarketUpdateDetailsResponse, error) { - // TODO[1658]: Implement MarketUpdateDetails - panic("not implemented") + ctx := sdk.UnwrapSDKContext(goCtx) + if !k.CanUpdateMarket(ctx, msg.MarketId, msg.Admin) { + return nil, fmt.Errorf("account %s does not have permission to update market %d", msg.Admin, msg.MarketId) + } + err := k.UpdateMarketDetails(ctx, msg.MarketId, &msg.MarketDetails) + if err != nil { + return nil, err + } + return &exchange.MsgMarketUpdateDetailsResponse{}, nil } // MarketUpdateEnabled is a market endpoint to update whether its accepting orders. func (k MsgServer) MarketUpdateEnabled(goCtx context.Context, msg *exchange.MsgMarketUpdateEnabledRequest) (*exchange.MsgMarketUpdateEnabledResponse, error) { - // TODO[1658]: Implement MarketUpdateEnabled - panic("not implemented") + ctx := sdk.UnwrapSDKContext(goCtx) + if !k.CanUpdateMarket(ctx, msg.MarketId, msg.Admin) { + return nil, fmt.Errorf("account %s does not have permission to update market %d", msg.Admin, msg.MarketId) + } + err := k.UpdateMarketActive(ctx, msg.MarketId, msg.AcceptingOrders) + if err != nil { + return nil, err + } + return &exchange.MsgMarketUpdateEnabledResponse{}, nil } // MarketUpdateUserSettle is a market endpoint to update whether it allows user-initiated settlement. func (k MsgServer) MarketUpdateUserSettle(goCtx context.Context, msg *exchange.MsgMarketUpdateUserSettleRequest) (*exchange.MsgMarketUpdateUserSettleResponse, error) { - // TODO[1658]: Implement MarketUpdateUserSettle - panic("not implemented") + ctx := sdk.UnwrapSDKContext(goCtx) + if !k.CanUpdateMarket(ctx, msg.MarketId, msg.Admin) { + return nil, fmt.Errorf("account %s does not have permission to update market %d", msg.Admin, msg.MarketId) + } + err := k.UpdateUserSettlementAllowed(ctx, msg.MarketId, msg.AllowUserSettlement) + if err != nil { + return nil, err + } + return &exchange.MsgMarketUpdateUserSettleResponse{}, nil } // MarketManagePermissions is a market endpoint to manage a market's user permissions. From 5ac806f2e28da478839f2ae8bf2c69ce87955854 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 14:46:02 -0600 Subject: [PATCH 128/309] [1658]: Tweak some line spacing in tx.proto. --- proto/provenance/exchange/v1/tx.proto | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 5f6848ed69..762288aed3 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -143,13 +143,11 @@ message MsgMarketWithdrawRequest { // admin is the account with withdraw permission requesting the withdrawal. string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - // market_id is the numerical identifier of the market to withdraw from. uint32 market_id = 2; // to_address is the address that will receive the funds. string to_address = 3; - // amount is the funds to withdraw. repeated cosmos.base.v1beta1.Coin amount = 4 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; @@ -236,6 +234,7 @@ message MsgMarketManageReqAttrsRequest { string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // market_id is the numerical identifier of the market to update required attributes for. uint32 market_id = 2; + // create_ask_to_add are the attributes that should now also be required to create an ask order. repeated string create_ask_to_add = 3; // create_ask_to_add are the attributes that should no longer be required to create an ask order. @@ -271,43 +270,36 @@ message MsgGovManageFeesRequest { // authority should be the governance module account address. string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - // market_id is the market id that will get these fee updates. uint32 market_id = 2; // add_fee_create_ask_flat are the create-ask flat fee options to add. repeated cosmos.base.v1beta1.Coin add_fee_create_ask_flat = 3 [(gogoproto.nullable) = false]; - // remove_fee_create_ask_flat are the create-ask flat fee options to remove. repeated cosmos.base.v1beta1.Coin remove_fee_create_ask_flat = 4 [(gogoproto.nullable) = false]; // add_fee_create_bid_flat are the create-bid flat fee options to add. repeated cosmos.base.v1beta1.Coin add_fee_create_bid_flat = 5 [(gogoproto.nullable) = false]; - // remove_fee_create_bid_flat are the create-bid flat fee options to remove. repeated cosmos.base.v1beta1.Coin remove_fee_create_bid_flat = 6 [(gogoproto.nullable) = false]; // add_fee_seller_settlement_flat are the seller settlement flat fee options to add. repeated cosmos.base.v1beta1.Coin add_fee_seller_settlement_flat = 7 [(gogoproto.nullable) = false]; - // remove_fee_seller_settlement_flat are the seller settlement flat fee options to remove. repeated cosmos.base.v1beta1.Coin remove_fee_seller_settlement_flat = 8 [(gogoproto.nullable) = false]; // add_fee_seller_settlement_ratios are the seller settlement fee ratios to add. repeated FeeRatio add_fee_seller_settlement_ratios = 9 [(gogoproto.nullable) = false]; - // remove_fee_seller_settlement_ratios are the seller settlement fee ratios to remove. repeated FeeRatio remove_fee_seller_settlement_ratios = 10 [(gogoproto.nullable) = false]; // add_fee_buyer_settlement_flat are the buyer settlement flat fee options to add. repeated cosmos.base.v1beta1.Coin add_fee_buyer_settlement_flat = 11 [(gogoproto.nullable) = false]; - // remove_fee_buyer_settlement_flat are the buyer settlement flat fee options to remove. repeated cosmos.base.v1beta1.Coin remove_fee_buyer_settlement_flat = 12 [(gogoproto.nullable) = false]; // add_fee_buyer_settlement_ratios are the buyer settlement fee ratios to add. repeated FeeRatio add_fee_buyer_settlement_ratios = 13 [(gogoproto.nullable) = false]; - // remove_fee_buyer_settlement_ratios are the buyer settlement fee ratios to remove. repeated FeeRatio remove_fee_buyer_settlement_ratios = 14 [(gogoproto.nullable) = false]; } From 145e74a68e786d79dea67b0391de0352221f62af Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 14:51:43 -0600 Subject: [PATCH 129/309] [1658]: Rename the owner field in MsgCancelOrderRequest to signer. --- docs/proto-docs.md | 2 +- proto/provenance/exchange/v1/tx.proto | 8 +- x/exchange/keeper/msg_server.go | 2 +- x/exchange/msg.go | 6 +- x/exchange/msg_test.go | 14 +- x/exchange/tx.pb.go | 221 +++++++++++++------------- 6 files changed, 129 insertions(+), 124 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 802f682802..a3538c9506 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2129,7 +2129,7 @@ MsgCancelOrderRequest is a request message for the CancelOrder endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `owner` | [string](#string) | | owner is the account that owns the order. E.g. the seller for ask orders or buyer for bid orders. | +| `signer` | [string](#string) | | signer is the account requesting the order cancelation. It must be either the order owner (e.g. the buyer or seller), the governance module account address, or an account with cancel permission with the market that the order is in. | | `order_id` | [uint64](#uint64) | | order_id is the id of the order to cancel. | diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 762288aed3..347471a907 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -96,10 +96,12 @@ message MsgCreateBidResponse { // MsgCancelOrderRequest is a request message for the CancelOrder endpoint. message MsgCancelOrderRequest { - option (cosmos.msg.v1.signer) = "owner"; + option (cosmos.msg.v1.signer) = "signer"; - // owner is the account that owns the order. E.g. the seller for ask orders or buyer for bid orders. - string owner = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // signer is the account requesting the order cancelation. + // It must be either the order owner (e.g. the buyer or seller), the governance module account address, or an account + // with cancel permission with the market that the order is in. + string signer = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // order_id is the id of the order to cancel. uint64 order_id = 2; } diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 0db27baa88..b4650011bf 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -47,7 +47,7 @@ func (k MsgServer) CreateBid(goCtx context.Context, msg *exchange.MsgCreateBidRe // CancelOrder cancels an order. func (k MsgServer) CancelOrder(goCtx context.Context, msg *exchange.MsgCancelOrderRequest) (*exchange.MsgCancelOrderResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - err := k.Keeper.CancelOrder(ctx, msg.OrderId, msg.Owner) + err := k.Keeper.CancelOrder(ctx, msg.OrderId, msg.Signer) if err != nil { return nil, err } diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 251d9127b2..8a0ef2cf99 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -60,8 +60,8 @@ func (m MsgCreateBidRequest) GetSigners() []sdk.AccAddress { } func (m MsgCancelOrderRequest) ValidateBasic() error { - if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil { - return fmt.Errorf("invalid owner: %w", err) + if _, err := sdk.AccAddressFromBech32(m.Signer); err != nil { + return fmt.Errorf("invalid signer: %w", err) } if m.OrderId == 0 { return fmt.Errorf("invalid order id: cannot be zero") @@ -70,7 +70,7 @@ func (m MsgCancelOrderRequest) ValidateBasic() error { } func (m MsgCancelOrderRequest) GetSigners() []sdk.AccAddress { - addr := sdk.MustAccAddressFromBech32(m.Owner) + addr := sdk.MustAccAddressFromBech32(m.Signer) return []sdk.AccAddress{addr} } diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 01599f1bc0..978a70029d 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -37,7 +37,7 @@ func TestAllMsgsGetSigners(t *testing.T) { return &MsgCreateBidRequest{BidOrder: BidOrder{Buyer: signer}} }, func(signer string) sdk.Msg { - return &MsgCancelOrderRequest{Owner: signer} + return &MsgCancelOrderRequest{Signer: signer} }, // TODO[1658]: Add MsgFillBidsRequest once it's actually been defined. // TODO[1658]: Add MsgFillAsksRequest once it's actually been defined. @@ -239,7 +239,7 @@ func TestMsgCancelOrderRequest_ValidateBasic(t *testing.T) { { name: "control", msg: MsgCancelOrderRequest{ - Owner: sdk.AccAddress("owner_______________").String(), + Signer: sdk.AccAddress("signer______________").String(), OrderId: 1, }, expErr: nil, @@ -247,23 +247,23 @@ func TestMsgCancelOrderRequest_ValidateBasic(t *testing.T) { { name: "missing owner", msg: MsgCancelOrderRequest{ - Owner: "", + Signer: "", OrderId: 1, }, - expErr: []string{"invalid owner: ", "empty address string is not allowed"}, + expErr: []string{"invalid signer: ", "empty address string is not allowed"}, }, { name: "invalid owner", msg: MsgCancelOrderRequest{ - Owner: "notgonnawork", + Signer: "notgonnawork", OrderId: 1, }, - expErr: []string{"invalid owner: ", "decoding bech32 failed: invalid separator index -1"}, + expErr: []string{"invalid signer: ", "decoding bech32 failed: invalid separator index -1"}, }, { name: "order 0", msg: MsgCancelOrderRequest{ - Owner: sdk.AccAddress("valid_owner_________").String(), + Signer: sdk.AccAddress("valid_signer________").String(), OrderId: 0, }, expErr: []string{"invalid order id: cannot be zero"}, diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 8b891ff2cf..a01200c085 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -236,8 +236,10 @@ func (m *MsgCreateBidResponse) GetOrderId() uint64 { // MsgCancelOrderRequest is a request message for the CancelOrder endpoint. type MsgCancelOrderRequest struct { - // owner is the account that owns the order. E.g. the seller for ask orders or buyer for bid orders. - Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` + // signer is the account requesting the order cancelation. + // It must be either the order owner (e.g. the buyer or seller), the governance module account address, or an account + // with cancel permission with the market that the order is in. + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` // order_id is the id of the order to cancel. OrderId uint64 `protobuf:"varint,2,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` } @@ -275,9 +277,9 @@ func (m *MsgCancelOrderRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCancelOrderRequest proto.InternalMessageInfo -func (m *MsgCancelOrderRequest) GetOwner() string { +func (m *MsgCancelOrderRequest) GetSigner() string { if m != nil { - return m.Owner + return m.Signer } return "" } @@ -1650,104 +1652,105 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1550 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcf, 0x6f, 0xdc, 0xc4, - 0x17, 0x8f, 0xf3, 0xab, 0xd9, 0x97, 0x26, 0x6d, 0x27, 0xbf, 0x36, 0x6e, 0xb3, 0xd9, 0x6e, 0xbe, - 0x5f, 0xa9, 0xb4, 0xc4, 0xdb, 0x04, 0x51, 0x20, 0xe2, 0x92, 0x4d, 0x49, 0xc4, 0x21, 0xa2, 0x72, - 0xa9, 0x90, 0xe0, 0xb0, 0x9a, 0xb5, 0xa7, 0x8e, 0x15, 0xaf, 0x27, 0xf5, 0x4c, 0xd2, 0xf4, 0x8a, - 0x90, 0x38, 0x21, 0xf5, 0xc8, 0x15, 0x71, 0x82, 0x0b, 0x20, 0x71, 0x81, 0xbf, 0xa0, 0x42, 0x1c, - 0x2a, 0x4e, 0x9c, 0x00, 0xb5, 0x07, 0xfe, 0x04, 0xae, 0xc8, 0x33, 0xb3, 0x5e, 0x7b, 0xd7, 0x5e, - 0x7b, 0x43, 0x73, 0x6a, 0xed, 0x79, 0xef, 0xf3, 0xe3, 0xad, 0x67, 0x9e, 0x9f, 0x03, 0xab, 0x47, - 0x01, 0x3d, 0x21, 0x3e, 0xf6, 0x2d, 0x52, 0x27, 0xa7, 0xd6, 0x01, 0xf6, 0x1d, 0x52, 0x3f, 0xd9, - 0xa8, 0xf3, 0x53, 0xe3, 0x28, 0xa0, 0x9c, 0xa2, 0xc5, 0x6e, 0x80, 0xd1, 0x09, 0x30, 0x4e, 0x36, - 0xf4, 0x8a, 0x45, 0x59, 0x9b, 0xb2, 0x7a, 0x0b, 0xb3, 0x30, 0xa1, 0x45, 0x38, 0xde, 0xa8, 0x5b, - 0xd4, 0xf5, 0x65, 0x9e, 0xbe, 0xa4, 0xd6, 0xdb, 0xcc, 0x09, 0xf1, 0xda, 0xcc, 0x51, 0x0b, 0xcb, - 0x72, 0xa1, 0x29, 0xae, 0xea, 0xf2, 0x42, 0x2d, 0xcd, 0x3b, 0xd4, 0xa1, 0xf2, 0x7e, 0xf8, 0x3f, - 0x75, 0x77, 0x2d, 0x43, 0x62, 0x1b, 0x07, 0x87, 0x84, 0xe7, 0x04, 0xd1, 0xc0, 0x26, 0x01, 0xcb, - 0x09, 0x3a, 0xc2, 0x01, 0x6e, 0xab, 0xa0, 0xda, 0xcf, 0x1a, 0xcc, 0xed, 0x33, 0x67, 0x27, 0x20, - 0x98, 0x93, 0x6d, 0x76, 0x68, 0x92, 0x47, 0xc7, 0x84, 0x71, 0xb4, 0x03, 0x25, 0xcc, 0x0e, 0x9b, - 0x02, 0xb0, 0xac, 0x55, 0xb5, 0x1b, 0xd3, 0x9b, 0x55, 0x23, 0xbd, 0x38, 0xc6, 0x36, 0x3b, 0xfc, - 0x20, 0x8c, 0x6b, 0x8c, 0x3f, 0xfb, 0x63, 0x75, 0xc4, 0x9c, 0xc2, 0xea, 0x1a, 0xed, 0x01, 0x12, - 0x00, 0x4d, 0x2b, 0x84, 0x77, 0xa9, 0xdf, 0x7c, 0x48, 0x48, 0x79, 0x54, 0xa0, 0x2d, 0x1b, 0xaa, - 0x18, 0x61, 0x49, 0x0d, 0x55, 0x52, 0x63, 0x87, 0xba, 0xbe, 0x79, 0x59, 0x24, 0xed, 0xa8, 0x9c, - 0x5d, 0x42, 0xb6, 0x16, 0x3e, 0xfd, 0xfb, 0xfb, 0x9b, 0x97, 0x23, 0x41, 0x06, 0x23, 0x9e, 0x47, - 0x82, 0xda, 0x06, 0xcc, 0x27, 0xb5, 0xb3, 0x23, 0xea, 0x33, 0x82, 0x96, 0x61, 0x4a, 0xf2, 0xba, - 0xb6, 0xd0, 0x3e, 0x6e, 0x5e, 0x10, 0xd7, 0xef, 0xdb, 0xb5, 0x9f, 0xe2, 0x7e, 0x1b, 0xae, 0x1d, - 0xf3, 0xdb, 0x72, 0xed, 0x62, 0x7e, 0x1b, 0xae, 0x9d, 0xf0, 0xdb, 0x52, 0xd7, 0xaf, 0xce, 0xef, - 0x7c, 0xe8, 0xf7, 0x52, 0x24, 0xc8, 0x68, 0x1d, 0x3f, 0xe9, 0xb1, 0x2b, 0xa4, 0xe7, 0xdb, 0xf5, - 0x61, 0x21, 0x4c, 0x09, 0x2d, 0x78, 0x42, 0x63, 0xc7, 0xaf, 0x01, 0x13, 0xf4, 0xb1, 0xaf, 0xbc, - 0x96, 0x1a, 0xe5, 0xdf, 0x7e, 0x5c, 0x9f, 0x57, 0x02, 0xb7, 0x6d, 0x3b, 0x20, 0x8c, 0xdd, 0xe7, - 0x81, 0xeb, 0x3b, 0xa6, 0x0c, 0x4b, 0x70, 0x8c, 0x26, 0x38, 0xb6, 0x20, 0x14, 0x2b, 0xc3, 0x6a, - 0x65, 0x58, 0xec, 0xe5, 0x93, 0x22, 0x6b, 0xf3, 0x80, 0xf6, 0x99, 0xb3, 0xeb, 0x7a, 0x5e, 0xc3, - 0xb5, 0x99, 0x92, 0x51, 0x5b, 0x10, 0xbf, 0x46, 0xf7, 0x6e, 0x5f, 0xf0, 0x36, 0x3b, 0x4c, 0x09, - 0x96, 0x77, 0x55, 0xf0, 0x23, 0xc1, 0xb9, 0x2f, 0xf6, 0xc7, 0x7d, 0xc2, 0xb9, 0x47, 0x62, 0x26, - 0xb1, 0xdd, 0x76, 0xfd, 0x7c, 0x93, 0x22, 0x0c, 0x5d, 0x85, 0x92, 0xdc, 0x66, 0x1d, 0x97, 0x33, - 0xe6, 0x94, 0xbc, 0x11, 0xd9, 0x14, 0x81, 0xb5, 0x65, 0x58, 0xea, 0xa3, 0x54, 0x6a, 0xfe, 0xd1, - 0xa0, 0x1c, 0xad, 0x7d, 0xe4, 0xf2, 0x03, 0x3b, 0xc0, 0x8f, 0xcf, 0x43, 0x10, 0x5a, 0x01, 0xe0, - 0xb4, 0x89, 0x65, 0x5e, 0x79, 0x2c, 0x44, 0x34, 0x4b, 0x9c, 0x2a, 0x20, 0x64, 0xc1, 0x24, 0x6e, - 0xd3, 0x63, 0x9f, 0x97, 0xc7, 0xab, 0x63, 0x03, 0x1f, 0xc0, 0xc6, 0xed, 0xf0, 0x39, 0xfe, 0xf6, - 0xcf, 0xd5, 0x1b, 0x8e, 0xcb, 0x0f, 0x8e, 0x5b, 0x86, 0x45, 0xdb, 0xea, 0xa8, 0x52, 0xff, 0xac, - 0x33, 0xfb, 0xb0, 0xce, 0x9f, 0x1c, 0x11, 0x26, 0x12, 0x98, 0xa9, 0xa0, 0x13, 0x45, 0xb9, 0x0a, - 0xcb, 0x29, 0xc6, 0x55, 0x59, 0x7e, 0xd5, 0x60, 0x25, 0x5a, 0x7d, 0x70, 0x64, 0x63, 0x4e, 0xee, - 0x12, 0x8e, 0x5d, 0x8f, 0x9d, 0x4b, 0x6d, 0x4c, 0x98, 0x55, 0x8b, 0xb6, 0x64, 0x11, 0xf5, 0x99, - 0xde, 0xfc, 0x7f, 0xd6, 0x9e, 0x96, 0xc2, 0x94, 0x24, 0xb5, 0xb1, 0x67, 0xda, 0xf1, 0x9b, 0x09, - 0xaf, 0x55, 0xa8, 0x64, 0xb9, 0x51, 0x86, 0xbf, 0xee, 0x37, 0xfc, 0x9e, 0x8f, 0x5b, 0x1e, 0xb1, - 0xcf, 0xc5, 0xf0, 0x6b, 0x70, 0x19, 0x5b, 0x16, 0x39, 0xe2, 0xae, 0xef, 0xc8, 0x43, 0x43, 0x5a, - 0x9e, 0x32, 0x2f, 0x45, 0xf7, 0xc5, 0x86, 0xcc, 0xf3, 0x11, 0x89, 0x54, 0x3e, 0xbe, 0xd3, 0xa0, - 0xda, 0x13, 0xf2, 0x80, 0x91, 0xe0, 0xfc, 0x36, 0x1a, 0xda, 0x84, 0x05, 0xec, 0x79, 0xf4, 0x71, - 0xf3, 0x98, 0x91, 0xa0, 0xc9, 0x04, 0x51, 0x9b, 0xf8, 0x5c, 0xf9, 0x99, 0x13, 0x8b, 0x5d, 0x0d, - 0xe1, 0x52, 0xc2, 0xd3, 0x1a, 0x5c, 0x1f, 0x20, 0x58, 0xd9, 0xfa, 0x66, 0x34, 0x16, 0xb5, 0x8f, - 0x7d, 0xec, 0x90, 0x7b, 0x24, 0x68, 0xbb, 0x8c, 0xb9, 0xd4, 0x67, 0xe7, 0xb5, 0x5f, 0x03, 0x72, - 0x42, 0x0f, 0x49, 0x13, 0x7b, 0x5e, 0x79, 0xac, 0x3a, 0x16, 0xee, 0x57, 0x79, 0x67, 0xdb, 0xf3, - 0xd0, 0x2e, 0x94, 0x38, 0x6d, 0xca, 0x6b, 0xb5, 0x65, 0xd7, 0x32, 0x3b, 0xae, 0x65, 0x11, 0xc6, - 0xf6, 0x02, 0xec, 0xf3, 0x4e, 0x13, 0xe2, 0xd4, 0x14, 0xa9, 0xe8, 0x2e, 0x4c, 0x71, 0xda, 0x74, - 0xc2, 0xb5, 0xf2, 0xc4, 0xb0, 0x30, 0x17, 0x38, 0x15, 0x97, 0x89, 0x82, 0xfe, 0x0f, 0x6a, 0x83, - 0x4a, 0xa5, 0x2a, 0xfa, 0xc3, 0x68, 0xec, 0x59, 0x92, 0x61, 0x26, 0x79, 0xb4, 0xcd, 0x79, 0xc0, - 0xce, 0xe9, 0x89, 0xbf, 0x22, 0xda, 0x2c, 0x69, 0x86, 0xef, 0x05, 0xf2, 0x24, 0x54, 0x55, 0x9d, - 0xb5, 0x3a, 0xaf, 0x04, 0x1f, 0x86, 0xc7, 0x21, 0xaa, 0xc3, 0x7c, 0x32, 0x34, 0x20, 0x6d, 0x7a, - 0x22, 0xab, 0x5c, 0x32, 0xaf, 0xc4, 0xa2, 0x4d, 0xb1, 0x10, 0xc3, 0x0e, 0x7b, 0xb0, 0xc2, 0x9e, - 0x88, 0x63, 0x37, 0x5c, 0xbb, 0x17, 0x5b, 0x85, 0x2a, 0xec, 0xc9, 0x38, 0xb6, 0x88, 0x96, 0xd8, - 0x89, 0xca, 0x5e, 0x87, 0xd5, 0xcc, 0x92, 0xa9, 0xb2, 0x7e, 0xa5, 0x89, 0x63, 0x75, 0x8f, 0x9e, - 0xc8, 0xc6, 0x2f, 0x83, 0x3b, 0x15, 0xbd, 0x03, 0x25, 0x7c, 0xcc, 0x0f, 0x68, 0xe0, 0xf2, 0x27, - 0xb9, 0x55, 0xed, 0x86, 0xa2, 0x77, 0x61, 0x52, 0x16, 0x52, 0xbd, 0x9d, 0x54, 0x06, 0x9f, 0x8b, - 0xea, 0xe9, 0x50, 0x39, 0x5b, 0xb3, 0xa1, 0x85, 0x2e, 0x5a, 0xed, 0x1a, 0xe8, 0x69, 0x12, 0x95, - 0x83, 0x5f, 0x40, 0x74, 0xcb, 0x3d, 0x7a, 0x22, 0x2d, 0xee, 0x12, 0xc2, 0xfe, 0xab, 0xfe, 0x81, - 0x4f, 0xc6, 0x03, 0x58, 0xc2, 0xb6, 0x1d, 0xbe, 0x7b, 0x35, 0x63, 0x3f, 0xfb, 0x43, 0x0f, 0x73, - 0xf1, 0x7c, 0x0c, 0x6c, 0x85, 0xd2, 0xe8, 0x1c, 0xb6, 0xed, 0x5d, 0x42, 0xa2, 0x37, 0xcb, 0x5d, - 0x0f, 0x73, 0xf4, 0x09, 0xe8, 0xf2, 0xb7, 0x4d, 0x45, 0x1e, 0x2f, 0x86, 0xbc, 0x28, 0x21, 0xfa, - 0xc0, 0xfb, 0x35, 0x87, 0x8f, 0x93, 0x40, 0x9e, 0x38, 0x83, 0xe6, 0x86, 0x6b, 0x67, 0x6b, 0x8e, - 0x90, 0x27, 0xcf, 0xa6, 0xb9, 0x03, 0x6e, 0x41, 0xa5, 0xa3, 0x59, 0xbe, 0x90, 0xc7, 0x0e, 0x6b, - 0x49, 0x70, 0xa1, 0x18, 0x81, 0x2e, 0xa5, 0xdf, 0x17, 0x20, 0xdd, 0x53, 0x5d, 0x90, 0xb8, 0x70, - 0x3d, 0xe6, 0x20, 0x83, 0x67, 0xaa, 0x18, 0xcf, 0x4a, 0x64, 0x24, 0x95, 0xca, 0x87, 0x6a, 0xb6, - 0x9f, 0x20, 0x7c, 0x39, 0x67, 0xe5, 0x92, 0x60, 0xca, 0x1c, 0x0d, 0x76, 0x09, 0x31, 0xc3, 0x40, - 0x45, 0x78, 0x2d, 0xdd, 0x98, 0x08, 0x61, 0x88, 0xc3, 0xda, 0x40, 0x6b, 0x8a, 0x12, 0x86, 0xa2, - 0x5c, 0xcd, 0xf4, 0xa8, 0x58, 0x31, 0xac, 0x74, 0x5c, 0x8a, 0xb1, 0xa2, 0xaf, 0x98, 0xd3, 0xc5, - 0x8a, 0xb9, 0x2c, 0xbd, 0x35, 0x42, 0x8c, 0x9e, 0x42, 0x3a, 0x50, 0x8d, 0x19, 0x4b, 0x67, 0xb9, - 0x58, 0x8c, 0xe5, 0x5a, 0x64, 0x27, 0x8d, 0xc8, 0x83, 0xd5, 0x4c, 0x2f, 0xaa, 0x7a, 0x33, 0x43, - 0x55, 0xef, 0x6a, 0xaa, 0x29, 0x55, 0xb9, 0x00, 0x6a, 0x83, 0x6c, 0x29, 0xc2, 0xd9, 0xa1, 0x08, - 0x2b, 0x59, 0xfe, 0x24, 0x67, 0xdf, 0x51, 0xab, 0x8b, 0xe9, 0xa2, 0xe7, 0x2c, 0xed, 0x6b, 0x15, - 0xf2, 0xb5, 0xe7, 0x9e, 0x18, 0xf4, 0x5f, 0x41, 0xab, 0x90, 0x5f, 0x0c, 0xf2, 0x5a, 0x85, 0xa4, - 0xeb, 0xb4, 0x0a, 0x99, 0x93, 0xdd, 0x2a, 0x92, 0x12, 0xa5, 0x83, 0xcd, 0x2f, 0x67, 0x61, 0x6c, - 0x9f, 0x39, 0xe8, 0x21, 0x94, 0xa2, 0xe3, 0x11, 0xdd, 0xca, 0xec, 0x4d, 0xfd, 0xdf, 0x2d, 0xf4, - 0xd7, 0x8b, 0x05, 0xab, 0xc9, 0x39, 0xe2, 0x69, 0xb8, 0x76, 0x01, 0x9e, 0xee, 0xf7, 0x82, 0x02, - 0x3c, 0xf1, 0x09, 0xdd, 0x83, 0xe9, 0xd8, 0x4c, 0x8c, 0xd6, 0x07, 0x25, 0xf7, 0xcd, 0xea, 0xba, - 0x51, 0x34, 0x5c, 0xb1, 0x59, 0x30, 0xd5, 0x99, 0xa8, 0xd1, 0xcd, 0x01, 0xb9, 0x3d, 0xc3, 0xb8, - 0x7e, 0xab, 0x50, 0x6c, 0x92, 0x24, 0x9c, 0xc4, 0x73, 0x49, 0x62, 0x43, 0x7c, 0x2e, 0x49, 0x7c, - 0xb4, 0x47, 0x14, 0x2e, 0xc6, 0x87, 0x6c, 0x34, 0xa8, 0x12, 0x29, 0x1f, 0x00, 0xf4, 0x7a, 0xe1, - 0x78, 0x45, 0x78, 0x0c, 0xb3, 0xc9, 0x01, 0x16, 0xdd, 0xce, 0x85, 0xe8, 0x19, 0xf2, 0xf5, 0x8d, - 0x21, 0x32, 0x14, 0xed, 0x67, 0x1a, 0xcc, 0xa5, 0x0c, 0x93, 0xe8, 0xcd, 0x5c, 0xa8, 0xb4, 0x51, - 0x5a, 0xbf, 0x33, 0x6c, 0x5a, 0x86, 0x0c, 0x35, 0x0b, 0x16, 0x96, 0x91, 0x1c, 0x70, 0x0b, 0xcb, - 0xe8, 0x19, 0x39, 0xd1, 0x17, 0x1a, 0x2c, 0xa6, 0x8f, 0x6f, 0xe8, 0xed, 0x82, 0x90, 0x7d, 0x23, - 0xaa, 0xfe, 0xce, 0x19, 0x32, 0x95, 0x9e, 0xa7, 0x1a, 0x2c, 0x65, 0x4c, 0x3f, 0x28, 0x1f, 0x36, - 0x6b, 0xb8, 0xd4, 0xb7, 0xce, 0x92, 0xaa, 0x24, 0x7d, 0xae, 0xc1, 0x7c, 0xda, 0xd8, 0x80, 0xee, - 0x14, 0x04, 0xed, 0x19, 0xcd, 0xf4, 0xb7, 0x86, 0xce, 0x53, 0x4a, 0x4e, 0xe1, 0x52, 0xcf, 0x8b, - 0x3f, 0x1a, 0xb4, 0x01, 0xd2, 0xe7, 0x18, 0x7d, 0x73, 0x98, 0x14, 0xc5, 0x1c, 0xc0, 0x4c, 0xa2, - 0x0f, 0xa2, 0xfa, 0x60, 0x90, 0xbe, 0xe9, 0x43, 0xbf, 0x5d, 0x3c, 0x21, 0xe1, 0x36, 0xde, 0xbb, - 0xf2, 0xdc, 0xa6, 0xb4, 0xe2, 0x3c, 0xb7, 0x69, 0xad, 0xb1, 0x41, 0x9e, 0xbd, 0xa8, 0x68, 0xcf, - 0x5f, 0x54, 0xb4, 0xbf, 0x5e, 0x54, 0xb4, 0xa7, 0x2f, 0x2b, 0x23, 0xcf, 0x5f, 0x56, 0x46, 0x7e, - 0x7f, 0x59, 0x19, 0x81, 0x65, 0x97, 0x66, 0xe0, 0xdd, 0xd3, 0x3e, 0x36, 0x62, 0x9f, 0xf4, 0xba, - 0x41, 0xeb, 0x2e, 0x8d, 0x5d, 0xd5, 0x4f, 0xa3, 0xbf, 0x0f, 0xb4, 0x26, 0xc5, 0x9f, 0x05, 0xde, - 0xf8, 0x37, 0x00, 0x00, 0xff, 0xff, 0x47, 0xe8, 0x1a, 0xb8, 0x2a, 0x19, 0x00, 0x00, + // 1559 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcd, 0x6f, 0xdc, 0x44, + 0x1b, 0x8f, 0xf3, 0xd5, 0xec, 0x93, 0x26, 0x6d, 0x27, 0x5f, 0x1b, 0xb7, 0xd9, 0x6c, 0x37, 0xef, + 0x2b, 0xf5, 0x6d, 0xdf, 0x78, 0x93, 0x20, 0x0a, 0x44, 0x5c, 0xb2, 0x29, 0x89, 0x7a, 0x88, 0xa8, + 0x5c, 0x2a, 0x24, 0x38, 0xac, 0x66, 0xed, 0xa9, 0x63, 0xc5, 0xeb, 0x49, 0x3c, 0x93, 0x6d, 0x7a, + 0x45, 0x48, 0x9c, 0x90, 0x7a, 0xe4, 0x8a, 0x38, 0xc1, 0x05, 0x90, 0xb8, 0xc0, 0x5f, 0x50, 0x21, + 0x0e, 0x15, 0x27, 0x4e, 0x80, 0xda, 0x03, 0x7f, 0x02, 0x57, 0xe4, 0x99, 0xd9, 0x5d, 0x7b, 0xd7, + 0x5e, 0x7b, 0x43, 0x73, 0x6a, 0xed, 0x79, 0x9e, 0xdf, 0xc7, 0xb3, 0x9e, 0x79, 0xfc, 0x38, 0xb0, + 0x7a, 0x1c, 0xd0, 0x16, 0xf1, 0xb1, 0x6f, 0x91, 0x2a, 0x39, 0xb3, 0x0e, 0xb1, 0xef, 0x90, 0x6a, + 0x6b, 0xb3, 0xca, 0xcf, 0x8c, 0xe3, 0x80, 0x72, 0x8a, 0x16, 0xbb, 0x01, 0x46, 0x3b, 0xc0, 0x68, + 0x6d, 0xea, 0x25, 0x8b, 0xb2, 0x26, 0x65, 0xd5, 0x06, 0x66, 0x61, 0x42, 0x83, 0x70, 0xbc, 0x59, + 0xb5, 0xa8, 0xeb, 0xcb, 0x3c, 0x7d, 0x49, 0xad, 0x37, 0x99, 0x13, 0xe2, 0x35, 0x99, 0xa3, 0x16, + 0x96, 0xe5, 0x42, 0x5d, 0x5c, 0x55, 0xe5, 0x85, 0x5a, 0x9a, 0x77, 0xa8, 0x43, 0xe5, 0xfd, 0xf0, + 0x7f, 0xea, 0xee, 0x5a, 0x8a, 0xc4, 0x26, 0x0e, 0x8e, 0x08, 0xcf, 0x08, 0xa2, 0x81, 0x4d, 0x02, + 0x96, 0x11, 0x74, 0x8c, 0x03, 0xdc, 0x54, 0x41, 0x95, 0x9f, 0x34, 0x98, 0x3b, 0x60, 0xce, 0x6e, + 0x40, 0x30, 0x27, 0x3b, 0xec, 0xc8, 0x24, 0x27, 0xa7, 0x84, 0x71, 0xb4, 0x0b, 0x05, 0xcc, 0x8e, + 0xea, 0x02, 0xb0, 0xa8, 0x95, 0xb5, 0x5b, 0xd3, 0x5b, 0x65, 0x23, 0xb9, 0x38, 0xc6, 0x0e, 0x3b, + 0x7a, 0x3f, 0x8c, 0xab, 0x8d, 0x3f, 0xff, 0x7d, 0x75, 0xc4, 0x9c, 0xc2, 0xea, 0x1a, 0xed, 0x03, + 0x12, 0x00, 0x75, 0x2b, 0x84, 0x77, 0xa9, 0x5f, 0x7f, 0x4c, 0x48, 0x71, 0x54, 0xa0, 0x2d, 0x1b, + 0xaa, 0x18, 0x61, 0x49, 0x0d, 0x55, 0x52, 0x63, 0x97, 0xba, 0xbe, 0x79, 0x55, 0x24, 0xed, 0xaa, + 0x9c, 0x3d, 0x42, 0xb6, 0x17, 0x3e, 0xf9, 0xeb, 0xbb, 0xdb, 0x57, 0x3b, 0x82, 0x0c, 0x46, 0x3c, + 0x8f, 0x04, 0x95, 0x4d, 0x98, 0x8f, 0x6b, 0x67, 0xc7, 0xd4, 0x67, 0x04, 0x2d, 0xc3, 0x94, 0xe4, + 0x75, 0x6d, 0xa1, 0x7d, 0xdc, 0xbc, 0x24, 0xae, 0xef, 0xdb, 0x95, 0x1f, 0xa3, 0x7e, 0x6b, 0xae, + 0x1d, 0xf1, 0xdb, 0x70, 0xed, 0x7c, 0x7e, 0x6b, 0xae, 0x1d, 0xf3, 0xdb, 0x50, 0xd7, 0xaf, 0xcf, + 0xef, 0x7c, 0xe8, 0xf7, 0x4a, 0x47, 0x90, 0xd1, 0x38, 0x7d, 0xda, 0x63, 0x57, 0x48, 0xcf, 0xb6, + 0x7b, 0x02, 0x0b, 0x61, 0x4a, 0x68, 0xc1, 0x13, 0x1a, 0xdb, 0x7e, 0x37, 0x60, 0x92, 0xb9, 0x8e, + 0xaf, 0xcc, 0x16, 0x6a, 0xc5, 0x5f, 0x7f, 0x58, 0x9f, 0x57, 0x0a, 0x77, 0x6c, 0x3b, 0x20, 0x8c, + 0x3d, 0xe4, 0x81, 0xeb, 0x3b, 0xa6, 0x8a, 0x8b, 0xb1, 0x8c, 0xc6, 0x58, 0xb6, 0xa7, 0x43, 0xb9, + 0x2a, 0xae, 0x52, 0x84, 0xc5, 0x5e, 0x4a, 0xa9, 0xb3, 0x32, 0x0f, 0xe8, 0x80, 0x39, 0x7b, 0xae, + 0xe7, 0xd5, 0x5c, 0x9b, 0x29, 0x25, 0x95, 0x05, 0xf1, 0x83, 0x74, 0xef, 0xf6, 0x05, 0xef, 0xb0, + 0xa3, 0x84, 0x60, 0x79, 0x57, 0x05, 0x9f, 0x08, 0xce, 0x03, 0xb1, 0x45, 0x1e, 0x12, 0xce, 0x3d, + 0xd2, 0xf6, 0x69, 0xc0, 0x04, 0xb6, 0x9b, 0xae, 0x9f, 0x69, 0x53, 0x86, 0xa1, 0xeb, 0x50, 0x90, + 0x3b, 0xad, 0x6d, 0x73, 0xc6, 0x9c, 0x92, 0x37, 0xee, 0xdb, 0xdb, 0x10, 0xfa, 0x94, 0x81, 0x95, + 0x65, 0x58, 0xea, 0xa3, 0x54, 0x6a, 0xfe, 0xd6, 0xa0, 0xd8, 0x59, 0xfb, 0xd0, 0xe5, 0x87, 0x76, + 0x80, 0x9f, 0x5c, 0x84, 0x20, 0xb4, 0x02, 0xc0, 0x69, 0x1d, 0xcb, 0xbc, 0xe2, 0x58, 0x88, 0x68, + 0x16, 0x38, 0x55, 0x40, 0xc8, 0x82, 0x49, 0xdc, 0xa4, 0xa7, 0x3e, 0x2f, 0x8e, 0x97, 0xc7, 0x06, + 0x3e, 0x83, 0xb5, 0x8d, 0xf0, 0x51, 0xfe, 0xe6, 0x8f, 0xd5, 0x5b, 0x8e, 0xcb, 0x0f, 0x4f, 0x1b, + 0x86, 0x45, 0x9b, 0xea, 0xb4, 0x52, 0xff, 0xac, 0x33, 0xfb, 0xa8, 0xca, 0x9f, 0x1e, 0x13, 0x26, + 0x12, 0x98, 0xa9, 0xa0, 0x63, 0x45, 0xb9, 0x0e, 0xcb, 0x09, 0xc6, 0x55, 0x59, 0x7e, 0xd1, 0x60, + 0xa5, 0xb3, 0xfa, 0xe8, 0xd8, 0xc6, 0x9c, 0xdc, 0x23, 0x1c, 0xbb, 0x1e, 0xbb, 0x90, 0xda, 0x98, + 0x30, 0xab, 0x16, 0x6d, 0xc9, 0x22, 0xea, 0x33, 0xbd, 0xf5, 0xdf, 0xb4, 0x6d, 0x2d, 0x85, 0x29, + 0x49, 0x6a, 0x6f, 0xcf, 0x34, 0xa3, 0x37, 0x63, 0x5e, 0xcb, 0x50, 0x4a, 0x73, 0xa3, 0x0c, 0x7f, + 0xd5, 0x6f, 0xf8, 0x3d, 0x1f, 0x37, 0x3c, 0x62, 0x5f, 0x88, 0xe1, 0xff, 0xc1, 0x55, 0x6c, 0x59, + 0xe4, 0x98, 0xbb, 0xbe, 0x23, 0xcf, 0x0d, 0x69, 0x79, 0xca, 0xbc, 0xd2, 0xb9, 0x2f, 0x36, 0x64, + 0x96, 0x8f, 0x8e, 0x48, 0xe5, 0xe3, 0x5b, 0x0d, 0xca, 0x3d, 0x21, 0x8f, 0x18, 0x09, 0x2e, 0x6e, + 0xa3, 0xa1, 0x2d, 0x58, 0xc0, 0x9e, 0x47, 0x9f, 0xd4, 0x4f, 0x19, 0x09, 0xea, 0x4c, 0x10, 0x35, + 0x89, 0xcf, 0x95, 0x9f, 0x39, 0xb1, 0xd8, 0xd5, 0x10, 0x2e, 0xc5, 0x3c, 0xad, 0xc1, 0xcd, 0x01, + 0x82, 0x95, 0xad, 0xaf, 0x47, 0x23, 0x51, 0x07, 0xd8, 0xc7, 0x0e, 0x79, 0x40, 0x82, 0xa6, 0xcb, + 0x98, 0x4b, 0x7d, 0x76, 0x51, 0xfb, 0x35, 0x20, 0x2d, 0x7a, 0x44, 0xea, 0xd8, 0xf3, 0x8a, 0x63, + 0xe5, 0xb1, 0x70, 0xbf, 0xca, 0x3b, 0x3b, 0x9e, 0x87, 0xf6, 0xa0, 0xc0, 0x69, 0x5d, 0x5e, 0xab, + 0x2d, 0xbb, 0x96, 0xda, 0x74, 0x2d, 0x8b, 0x30, 0xb6, 0x1f, 0x60, 0x9f, 0xb7, 0xfb, 0x10, 0xa7, + 0xa6, 0x48, 0x45, 0xf7, 0x60, 0x8a, 0xd3, 0xba, 0x13, 0xae, 0x15, 0x27, 0x86, 0x85, 0xb9, 0xc4, + 0xa9, 0xb8, 0x8c, 0x15, 0xf4, 0x3f, 0x50, 0x19, 0x54, 0x2a, 0x55, 0xd1, 0xef, 0x47, 0x23, 0xcf, + 0x92, 0x0c, 0x33, 0xc9, 0xc9, 0x0e, 0xe7, 0x01, 0xbb, 0xa0, 0x27, 0xfe, 0x9a, 0xe8, 0xb4, 0xa4, + 0x1e, 0xbe, 0x1a, 0xc8, 0x93, 0x50, 0x55, 0x75, 0xd6, 0x6a, 0xbf, 0x15, 0x7c, 0x10, 0x1e, 0x87, + 0xa8, 0x0a, 0xf3, 0xf1, 0xd0, 0x80, 0x34, 0x69, 0x4b, 0x56, 0xb9, 0x60, 0x5e, 0x8b, 0x44, 0x9b, + 0x62, 0x21, 0x82, 0x1d, 0xb6, 0x61, 0x85, 0x3d, 0x11, 0xc5, 0xae, 0xb9, 0x76, 0x2f, 0xb6, 0x0a, + 0x55, 0xd8, 0x93, 0x51, 0x6c, 0x11, 0x2d, 0xb1, 0x63, 0x95, 0xbd, 0x09, 0xab, 0xa9, 0x25, 0x53, + 0x65, 0xfd, 0x52, 0x13, 0xc7, 0xea, 0x3e, 0x6d, 0xc9, 0xde, 0x2f, 0x83, 0xdb, 0x15, 0xbd, 0x0b, + 0x05, 0x7c, 0xca, 0x0f, 0x69, 0xe0, 0xf2, 0xa7, 0x99, 0x55, 0xed, 0x86, 0xa2, 0x77, 0x61, 0x52, + 0x16, 0x52, 0xbd, 0xa0, 0x94, 0x06, 0x9f, 0x8b, 0xea, 0xe9, 0x50, 0x39, 0xdb, 0xb3, 0xa1, 0x85, + 0x2e, 0x5a, 0xe5, 0x06, 0xe8, 0x49, 0x12, 0x95, 0x83, 0x9f, 0x41, 0x74, 0xcb, 0x7d, 0xda, 0x92, + 0x16, 0xf7, 0x08, 0x61, 0xff, 0x56, 0xff, 0xc0, 0x27, 0xe3, 0x11, 0x2c, 0x61, 0xdb, 0x0e, 0x5f, + 0xbf, 0xea, 0x91, 0x9f, 0xfd, 0xb1, 0x87, 0xb9, 0x78, 0x3e, 0x06, 0xb6, 0x42, 0x69, 0x74, 0x0e, + 0xdb, 0xf6, 0x1e, 0x21, 0x9d, 0x97, 0xcb, 0x3d, 0x0f, 0x73, 0xf4, 0x31, 0xe8, 0xf2, 0xb7, 0x4d, + 0x44, 0x1e, 0xcf, 0x87, 0xbc, 0x28, 0x21, 0xfa, 0xc0, 0xfb, 0x35, 0x87, 0x8f, 0x93, 0x40, 0x9e, + 0x38, 0x87, 0xe6, 0x9a, 0x6b, 0xa7, 0x6b, 0xee, 0x20, 0x4f, 0x9e, 0x4f, 0x73, 0x1b, 0xdc, 0x82, + 0x52, 0x5b, 0xb3, 0x7c, 0x27, 0x8f, 0x1c, 0xd6, 0x92, 0xe0, 0x52, 0x3e, 0x02, 0x5d, 0x4a, 0x7f, + 0x28, 0x40, 0xba, 0xa7, 0xba, 0x20, 0x71, 0xe1, 0x66, 0xc4, 0x41, 0x0a, 0xcf, 0x54, 0x3e, 0x9e, + 0x95, 0x8e, 0x91, 0x44, 0x2a, 0x1f, 0xca, 0xe9, 0x7e, 0x82, 0xf0, 0xfd, 0x9c, 0x15, 0x0b, 0x82, + 0x29, 0x75, 0x3a, 0xd8, 0x23, 0xc4, 0x0c, 0x03, 0x15, 0xe1, 0x8d, 0x64, 0x63, 0x22, 0x84, 0x21, + 0x0e, 0x6b, 0x03, 0xad, 0x29, 0x4a, 0x18, 0x8a, 0x72, 0x35, 0xd5, 0xa3, 0x62, 0xc5, 0xb0, 0xd2, + 0x76, 0x29, 0x26, 0x8b, 0xbe, 0x62, 0x4e, 0xe7, 0x2b, 0xe6, 0xb2, 0xf4, 0x56, 0x0b, 0x31, 0x7a, + 0x0a, 0xe9, 0x40, 0x39, 0x62, 0x2c, 0x99, 0xe5, 0x72, 0x3e, 0x96, 0x1b, 0x1d, 0x3b, 0x49, 0x44, + 0x1e, 0xac, 0xa6, 0x7a, 0x51, 0xd5, 0x9b, 0x19, 0xaa, 0x7a, 0xd7, 0x13, 0x4d, 0xa9, 0xca, 0x05, + 0x50, 0x19, 0x64, 0x4b, 0x11, 0xce, 0x0e, 0x45, 0x58, 0x4a, 0xf3, 0x27, 0x39, 0xfb, 0x8e, 0x5a, + 0x5d, 0x4c, 0x17, 0x3d, 0x67, 0x69, 0x5f, 0xab, 0x90, 0xaf, 0x3d, 0x0f, 0xc4, 0xac, 0xff, 0x1a, + 0x5a, 0x85, 0xfc, 0x68, 0x90, 0xd5, 0x2a, 0x24, 0x5d, 0xbb, 0x55, 0xc8, 0x9c, 0xf4, 0x56, 0x11, + 0x97, 0x28, 0x1d, 0x6c, 0x7d, 0x31, 0x0b, 0x63, 0x07, 0xcc, 0x41, 0x8f, 0xa1, 0xd0, 0x39, 0x1e, + 0xd1, 0x9d, 0xd4, 0xde, 0xd4, 0xff, 0xe9, 0x42, 0xff, 0x7f, 0xbe, 0x60, 0x35, 0x3c, 0x77, 0x78, + 0x6a, 0xae, 0x9d, 0x83, 0xa7, 0xfb, 0xc9, 0x20, 0x07, 0x4f, 0x74, 0x48, 0xf7, 0x60, 0x3a, 0x32, + 0x13, 0xa3, 0xf5, 0x41, 0xc9, 0x7d, 0xe3, 0xba, 0x6e, 0xe4, 0x0d, 0x57, 0x6c, 0x16, 0x4c, 0xb5, + 0x27, 0x6a, 0x74, 0x7b, 0x40, 0x6e, 0xcf, 0x30, 0xae, 0xdf, 0xc9, 0x15, 0x1b, 0x27, 0x09, 0x27, + 0xf1, 0x4c, 0x92, 0xc8, 0x10, 0x9f, 0x49, 0x12, 0x1d, 0xed, 0x11, 0x85, 0xcb, 0xd1, 0x21, 0x1b, + 0x0d, 0xaa, 0x44, 0xc2, 0x07, 0x00, 0xbd, 0x9a, 0x3b, 0x5e, 0x11, 0x9e, 0xc2, 0x6c, 0x7c, 0x80, + 0x45, 0x1b, 0x99, 0x10, 0x3d, 0x43, 0xbe, 0xbe, 0x39, 0x44, 0x86, 0xa2, 0xfd, 0x54, 0x83, 0xb9, + 0x84, 0x61, 0x12, 0xbd, 0x99, 0x09, 0x95, 0x34, 0x4a, 0xeb, 0x77, 0x87, 0x4d, 0x4b, 0x91, 0xa1, + 0x66, 0xc1, 0xdc, 0x32, 0xe2, 0x03, 0x6e, 0x6e, 0x19, 0x3d, 0x23, 0x27, 0xfa, 0x5c, 0x83, 0xc5, + 0xe4, 0xf1, 0x0d, 0xbd, 0x9d, 0x13, 0xb2, 0x6f, 0x44, 0xd5, 0xdf, 0x39, 0x47, 0xa6, 0xd2, 0xf3, + 0x4c, 0x83, 0xa5, 0x94, 0xe9, 0x07, 0x65, 0xc3, 0xa6, 0x0d, 0x97, 0xfa, 0xf6, 0x79, 0x52, 0x95, + 0xa4, 0xcf, 0x34, 0x98, 0x4f, 0x1a, 0x1b, 0xd0, 0xdd, 0x9c, 0xa0, 0x3d, 0xa3, 0x99, 0xfe, 0xd6, + 0xd0, 0x79, 0x4a, 0xc9, 0x19, 0x5c, 0xe9, 0x79, 0xf1, 0x47, 0x83, 0x36, 0x40, 0xf2, 0x1c, 0xa3, + 0x6f, 0x0d, 0x93, 0xa2, 0x98, 0x03, 0x98, 0x89, 0xf5, 0x41, 0x54, 0x1d, 0x0c, 0xd2, 0x37, 0x7d, + 0xe8, 0x1b, 0xf9, 0x13, 0x62, 0x6e, 0xa3, 0xbd, 0x2b, 0xcb, 0x6d, 0x42, 0x2b, 0xce, 0x72, 0x9b, + 0xd4, 0x1a, 0x6b, 0xe4, 0xf9, 0xcb, 0x92, 0xf6, 0xe2, 0x65, 0x49, 0xfb, 0xf3, 0x65, 0x49, 0x7b, + 0xf6, 0xaa, 0x34, 0xf2, 0xe2, 0x55, 0x69, 0xe4, 0xb7, 0x57, 0xa5, 0x11, 0x58, 0x76, 0x69, 0x0a, + 0xde, 0x03, 0xed, 0x23, 0x23, 0xf2, 0x49, 0xaf, 0x1b, 0xb4, 0xee, 0xd2, 0xc8, 0x55, 0xf5, 0xac, + 0xf3, 0x27, 0x82, 0xc6, 0xa4, 0xf8, 0xcb, 0xc0, 0x1b, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x30, + 0x30, 0x5c, 0xc9, 0x2d, 0x19, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2535,10 +2538,10 @@ func (m *MsgCancelOrderRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x10 } - if len(m.Owner) > 0 { - i -= len(m.Owner) - copy(dAtA[i:], m.Owner) - i = encodeVarintTx(dAtA, i, uint64(len(m.Owner))) + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) i-- dAtA[i] = 0xa } @@ -3613,7 +3616,7 @@ func (m *MsgCancelOrderRequest) Size() (n int) { } var l int _ = l - l = len(m.Owner) + l = len(m.Signer) if l > 0 { n += 1 + l + sovTx(uint64(l)) } @@ -4461,7 +4464,7 @@ func (m *MsgCancelOrderRequest) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -4489,7 +4492,7 @@ func (m *MsgCancelOrderRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Owner = string(dAtA[iNdEx:postIndex]) + m.Signer = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { From 066974e62c8f2bb8ab92a7c1bfbfb9ff797a702b Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 15:01:45 -0600 Subject: [PATCH 130/309] [1658]: add fields to MsgFillBidsRequest. --- docs/proto-docs.md | 10 + proto/provenance/exchange/v1/tx.proto | 19 +- x/exchange/tx.pb.go | 590 +++++++++++++++++++++----- 3 files changed, 519 insertions(+), 100 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index a3538c9506..e47dcff42a 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2235,6 +2235,16 @@ MsgFillAsksResponse is a response message for the FillAsks endpoint. MsgFillBidsRequest is a request message for the FillBids endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `seller` | [string](#string) | | seller is the address of the account with the assets to sell. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market with the bids to fill. All bid orders being filled must be in this market. | +| `total_assets` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | total_assets are the things that the seller wishes to sell. It must be the sum of all bid order assets. | +| `bid_order_ids` | [uint64](#uint64) | repeated | bid_order_ids are the ids of the bid orders that you are trying to fill. All ids must be for bid orders, and must be in the same market as the market_id. | +| `seller_settlement_flat_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. | +| `ask_order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | ask_order_creation_fee is the fee that is being paid to create this order. | + + diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 347471a907..43c18e0602 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -111,7 +111,24 @@ message MsgCancelOrderResponse {} // MsgFillBidsRequest is a request message for the FillBids endpoint. message MsgFillBidsRequest { - // TODO[1658]: MsgFillBidsRequest + option (cosmos.msg.v1.signer) = "seller"; + + // seller is the address of the account with the assets to sell. + string seller = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market_id is the numerical identifier of the market with the bids to fill. + // All bid orders being filled must be in this market. + uint32 market_id = 2; + // total_assets are the things that the seller wishes to sell. + // It must be the sum of all bid order assets. + repeated cosmos.base.v1beta1.Coin total_assets = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + // bid_order_ids are the ids of the bid orders that you are trying to fill. + // All ids must be for bid orders, and must be in the same market as the market_id. + repeated uint64 bid_order_ids = 4; + // seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. + cosmos.base.v1beta1.Coin seller_settlement_flat_fee = 5; + // ask_order_creation_fee is the fee that is being paid to create this order. + cosmos.base.v1beta1.Coin ask_order_creation_fee = 6; } // MsgFillBidsResponse is a response message for the FillBids endpoint. diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index a01200c085..44ecae1de5 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -330,6 +330,21 @@ var xxx_messageInfo_MsgCancelOrderResponse proto.InternalMessageInfo // MsgFillBidsRequest is a request message for the FillBids endpoint. type MsgFillBidsRequest struct { + // seller is the address of the account with the assets to sell. + Seller string `protobuf:"bytes,1,opt,name=seller,proto3" json:"seller,omitempty"` + // market_id is the numerical identifier of the market with the bids to fill. + // All bid orders being filled must be in this market. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // total_assets are the things that the seller wishes to sell. + // It must be the sum of all bid order assets. + TotalAssets github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=total_assets,json=totalAssets,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"total_assets"` + // bid_order_ids are the ids of the bid orders that you are trying to fill. + // All ids must be for bid orders, and must be in the same market as the market_id. + BidOrderIds []uint64 `protobuf:"varint,4,rep,packed,name=bid_order_ids,json=bidOrderIds,proto3" json:"bid_order_ids,omitempty"` + // seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. + SellerSettlementFlatFee *types.Coin `protobuf:"bytes,5,opt,name=seller_settlement_flat_fee,json=sellerSettlementFlatFee,proto3" json:"seller_settlement_flat_fee,omitempty"` + // ask_order_creation_fee is the fee that is being paid to create this order. + AskOrderCreationFee *types.Coin `protobuf:"bytes,6,opt,name=ask_order_creation_fee,json=askOrderCreationFee,proto3" json:"ask_order_creation_fee,omitempty"` } func (m *MsgFillBidsRequest) Reset() { *m = MsgFillBidsRequest{} } @@ -365,6 +380,48 @@ func (m *MsgFillBidsRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgFillBidsRequest proto.InternalMessageInfo +func (m *MsgFillBidsRequest) GetSeller() string { + if m != nil { + return m.Seller + } + return "" +} + +func (m *MsgFillBidsRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *MsgFillBidsRequest) GetTotalAssets() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.TotalAssets + } + return nil +} + +func (m *MsgFillBidsRequest) GetBidOrderIds() []uint64 { + if m != nil { + return m.BidOrderIds + } + return nil +} + +func (m *MsgFillBidsRequest) GetSellerSettlementFlatFee() *types.Coin { + if m != nil { + return m.SellerSettlementFlatFee + } + return nil +} + +func (m *MsgFillBidsRequest) GetAskOrderCreationFee() *types.Coin { + if m != nil { + return m.AskOrderCreationFee + } + return nil +} + // MsgFillBidsResponse is a response message for the FillBids endpoint. type MsgFillBidsResponse struct { } @@ -1652,105 +1709,111 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1559 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcd, 0x6f, 0xdc, 0x44, - 0x1b, 0x8f, 0xf3, 0xd5, 0xec, 0x93, 0x26, 0x6d, 0x27, 0x5f, 0x1b, 0xb7, 0xd9, 0x6c, 0x37, 0xef, - 0x2b, 0xf5, 0x6d, 0xdf, 0x78, 0x93, 0x20, 0x0a, 0x44, 0x5c, 0xb2, 0x29, 0x89, 0x7a, 0x88, 0xa8, - 0x5c, 0x2a, 0x24, 0x38, 0xac, 0x66, 0xed, 0xa9, 0x63, 0xc5, 0xeb, 0x49, 0x3c, 0x93, 0x6d, 0x7a, - 0x45, 0x48, 0x9c, 0x90, 0x7a, 0xe4, 0x8a, 0x38, 0xc1, 0x05, 0x90, 0xb8, 0xc0, 0x5f, 0x50, 0x21, - 0x0e, 0x15, 0x27, 0x4e, 0x80, 0xda, 0x03, 0x7f, 0x02, 0x57, 0xe4, 0x99, 0xd9, 0x5d, 0x7b, 0xd7, - 0x5e, 0x7b, 0x43, 0x73, 0x6a, 0xed, 0x79, 0x9e, 0xdf, 0xc7, 0xb3, 0x9e, 0x79, 0xfc, 0x38, 0xb0, - 0x7a, 0x1c, 0xd0, 0x16, 0xf1, 0xb1, 0x6f, 0x91, 0x2a, 0x39, 0xb3, 0x0e, 0xb1, 0xef, 0x90, 0x6a, - 0x6b, 0xb3, 0xca, 0xcf, 0x8c, 0xe3, 0x80, 0x72, 0x8a, 0x16, 0xbb, 0x01, 0x46, 0x3b, 0xc0, 0x68, - 0x6d, 0xea, 0x25, 0x8b, 0xb2, 0x26, 0x65, 0xd5, 0x06, 0x66, 0x61, 0x42, 0x83, 0x70, 0xbc, 0x59, - 0xb5, 0xa8, 0xeb, 0xcb, 0x3c, 0x7d, 0x49, 0xad, 0x37, 0x99, 0x13, 0xe2, 0x35, 0x99, 0xa3, 0x16, - 0x96, 0xe5, 0x42, 0x5d, 0x5c, 0x55, 0xe5, 0x85, 0x5a, 0x9a, 0x77, 0xa8, 0x43, 0xe5, 0xfd, 0xf0, - 0x7f, 0xea, 0xee, 0x5a, 0x8a, 0xc4, 0x26, 0x0e, 0x8e, 0x08, 0xcf, 0x08, 0xa2, 0x81, 0x4d, 0x02, - 0x96, 0x11, 0x74, 0x8c, 0x03, 0xdc, 0x54, 0x41, 0x95, 0x9f, 0x34, 0x98, 0x3b, 0x60, 0xce, 0x6e, - 0x40, 0x30, 0x27, 0x3b, 0xec, 0xc8, 0x24, 0x27, 0xa7, 0x84, 0x71, 0xb4, 0x0b, 0x05, 0xcc, 0x8e, - 0xea, 0x02, 0xb0, 0xa8, 0x95, 0xb5, 0x5b, 0xd3, 0x5b, 0x65, 0x23, 0xb9, 0x38, 0xc6, 0x0e, 0x3b, - 0x7a, 0x3f, 0x8c, 0xab, 0x8d, 0x3f, 0xff, 0x7d, 0x75, 0xc4, 0x9c, 0xc2, 0xea, 0x1a, 0xed, 0x03, - 0x12, 0x00, 0x75, 0x2b, 0x84, 0x77, 0xa9, 0x5f, 0x7f, 0x4c, 0x48, 0x71, 0x54, 0xa0, 0x2d, 0x1b, - 0xaa, 0x18, 0x61, 0x49, 0x0d, 0x55, 0x52, 0x63, 0x97, 0xba, 0xbe, 0x79, 0x55, 0x24, 0xed, 0xaa, - 0x9c, 0x3d, 0x42, 0xb6, 0x17, 0x3e, 0xf9, 0xeb, 0xbb, 0xdb, 0x57, 0x3b, 0x82, 0x0c, 0x46, 0x3c, - 0x8f, 0x04, 0x95, 0x4d, 0x98, 0x8f, 0x6b, 0x67, 0xc7, 0xd4, 0x67, 0x04, 0x2d, 0xc3, 0x94, 0xe4, - 0x75, 0x6d, 0xa1, 0x7d, 0xdc, 0xbc, 0x24, 0xae, 0xef, 0xdb, 0x95, 0x1f, 0xa3, 0x7e, 0x6b, 0xae, - 0x1d, 0xf1, 0xdb, 0x70, 0xed, 0x7c, 0x7e, 0x6b, 0xae, 0x1d, 0xf3, 0xdb, 0x50, 0xd7, 0xaf, 0xcf, - 0xef, 0x7c, 0xe8, 0xf7, 0x4a, 0x47, 0x90, 0xd1, 0x38, 0x7d, 0xda, 0x63, 0x57, 0x48, 0xcf, 0xb6, - 0x7b, 0x02, 0x0b, 0x61, 0x4a, 0x68, 0xc1, 0x13, 0x1a, 0xdb, 0x7e, 0x37, 0x60, 0x92, 0xb9, 0x8e, - 0xaf, 0xcc, 0x16, 0x6a, 0xc5, 0x5f, 0x7f, 0x58, 0x9f, 0x57, 0x0a, 0x77, 0x6c, 0x3b, 0x20, 0x8c, - 0x3d, 0xe4, 0x81, 0xeb, 0x3b, 0xa6, 0x8a, 0x8b, 0xb1, 0x8c, 0xc6, 0x58, 0xb6, 0xa7, 0x43, 0xb9, - 0x2a, 0xae, 0x52, 0x84, 0xc5, 0x5e, 0x4a, 0xa9, 0xb3, 0x32, 0x0f, 0xe8, 0x80, 0x39, 0x7b, 0xae, - 0xe7, 0xd5, 0x5c, 0x9b, 0x29, 0x25, 0x95, 0x05, 0xf1, 0x83, 0x74, 0xef, 0xf6, 0x05, 0xef, 0xb0, - 0xa3, 0x84, 0x60, 0x79, 0x57, 0x05, 0x9f, 0x08, 0xce, 0x03, 0xb1, 0x45, 0x1e, 0x12, 0xce, 0x3d, - 0xd2, 0xf6, 0x69, 0xc0, 0x04, 0xb6, 0x9b, 0xae, 0x9f, 0x69, 0x53, 0x86, 0xa1, 0xeb, 0x50, 0x90, - 0x3b, 0xad, 0x6d, 0x73, 0xc6, 0x9c, 0x92, 0x37, 0xee, 0xdb, 0xdb, 0x10, 0xfa, 0x94, 0x81, 0x95, - 0x65, 0x58, 0xea, 0xa3, 0x54, 0x6a, 0xfe, 0xd6, 0xa0, 0xd8, 0x59, 0xfb, 0xd0, 0xe5, 0x87, 0x76, - 0x80, 0x9f, 0x5c, 0x84, 0x20, 0xb4, 0x02, 0xc0, 0x69, 0x1d, 0xcb, 0xbc, 0xe2, 0x58, 0x88, 0x68, - 0x16, 0x38, 0x55, 0x40, 0xc8, 0x82, 0x49, 0xdc, 0xa4, 0xa7, 0x3e, 0x2f, 0x8e, 0x97, 0xc7, 0x06, - 0x3e, 0x83, 0xb5, 0x8d, 0xf0, 0x51, 0xfe, 0xe6, 0x8f, 0xd5, 0x5b, 0x8e, 0xcb, 0x0f, 0x4f, 0x1b, - 0x86, 0x45, 0x9b, 0xea, 0xb4, 0x52, 0xff, 0xac, 0x33, 0xfb, 0xa8, 0xca, 0x9f, 0x1e, 0x13, 0x26, - 0x12, 0x98, 0xa9, 0xa0, 0x63, 0x45, 0xb9, 0x0e, 0xcb, 0x09, 0xc6, 0x55, 0x59, 0x7e, 0xd1, 0x60, - 0xa5, 0xb3, 0xfa, 0xe8, 0xd8, 0xc6, 0x9c, 0xdc, 0x23, 0x1c, 0xbb, 0x1e, 0xbb, 0x90, 0xda, 0x98, - 0x30, 0xab, 0x16, 0x6d, 0xc9, 0x22, 0xea, 0x33, 0xbd, 0xf5, 0xdf, 0xb4, 0x6d, 0x2d, 0x85, 0x29, - 0x49, 0x6a, 0x6f, 0xcf, 0x34, 0xa3, 0x37, 0x63, 0x5e, 0xcb, 0x50, 0x4a, 0x73, 0xa3, 0x0c, 0x7f, - 0xd5, 0x6f, 0xf8, 0x3d, 0x1f, 0x37, 0x3c, 0x62, 0x5f, 0x88, 0xe1, 0xff, 0xc1, 0x55, 0x6c, 0x59, - 0xe4, 0x98, 0xbb, 0xbe, 0x23, 0xcf, 0x0d, 0x69, 0x79, 0xca, 0xbc, 0xd2, 0xb9, 0x2f, 0x36, 0x64, - 0x96, 0x8f, 0x8e, 0x48, 0xe5, 0xe3, 0x5b, 0x0d, 0xca, 0x3d, 0x21, 0x8f, 0x18, 0x09, 0x2e, 0x6e, - 0xa3, 0xa1, 0x2d, 0x58, 0xc0, 0x9e, 0x47, 0x9f, 0xd4, 0x4f, 0x19, 0x09, 0xea, 0x4c, 0x10, 0x35, - 0x89, 0xcf, 0x95, 0x9f, 0x39, 0xb1, 0xd8, 0xd5, 0x10, 0x2e, 0xc5, 0x3c, 0xad, 0xc1, 0xcd, 0x01, - 0x82, 0x95, 0xad, 0xaf, 0x47, 0x23, 0x51, 0x07, 0xd8, 0xc7, 0x0e, 0x79, 0x40, 0x82, 0xa6, 0xcb, - 0x98, 0x4b, 0x7d, 0x76, 0x51, 0xfb, 0x35, 0x20, 0x2d, 0x7a, 0x44, 0xea, 0xd8, 0xf3, 0x8a, 0x63, - 0xe5, 0xb1, 0x70, 0xbf, 0xca, 0x3b, 0x3b, 0x9e, 0x87, 0xf6, 0xa0, 0xc0, 0x69, 0x5d, 0x5e, 0xab, - 0x2d, 0xbb, 0x96, 0xda, 0x74, 0x2d, 0x8b, 0x30, 0xb6, 0x1f, 0x60, 0x9f, 0xb7, 0xfb, 0x10, 0xa7, - 0xa6, 0x48, 0x45, 0xf7, 0x60, 0x8a, 0xd3, 0xba, 0x13, 0xae, 0x15, 0x27, 0x86, 0x85, 0xb9, 0xc4, - 0xa9, 0xb8, 0x8c, 0x15, 0xf4, 0x3f, 0x50, 0x19, 0x54, 0x2a, 0x55, 0xd1, 0xef, 0x47, 0x23, 0xcf, - 0x92, 0x0c, 0x33, 0xc9, 0xc9, 0x0e, 0xe7, 0x01, 0xbb, 0xa0, 0x27, 0xfe, 0x9a, 0xe8, 0xb4, 0xa4, - 0x1e, 0xbe, 0x1a, 0xc8, 0x93, 0x50, 0x55, 0x75, 0xd6, 0x6a, 0xbf, 0x15, 0x7c, 0x10, 0x1e, 0x87, - 0xa8, 0x0a, 0xf3, 0xf1, 0xd0, 0x80, 0x34, 0x69, 0x4b, 0x56, 0xb9, 0x60, 0x5e, 0x8b, 0x44, 0x9b, - 0x62, 0x21, 0x82, 0x1d, 0xb6, 0x61, 0x85, 0x3d, 0x11, 0xc5, 0xae, 0xb9, 0x76, 0x2f, 0xb6, 0x0a, - 0x55, 0xd8, 0x93, 0x51, 0x6c, 0x11, 0x2d, 0xb1, 0x63, 0x95, 0xbd, 0x09, 0xab, 0xa9, 0x25, 0x53, - 0x65, 0xfd, 0x52, 0x13, 0xc7, 0xea, 0x3e, 0x6d, 0xc9, 0xde, 0x2f, 0x83, 0xdb, 0x15, 0xbd, 0x0b, - 0x05, 0x7c, 0xca, 0x0f, 0x69, 0xe0, 0xf2, 0xa7, 0x99, 0x55, 0xed, 0x86, 0xa2, 0x77, 0x61, 0x52, - 0x16, 0x52, 0xbd, 0xa0, 0x94, 0x06, 0x9f, 0x8b, 0xea, 0xe9, 0x50, 0x39, 0xdb, 0xb3, 0xa1, 0x85, - 0x2e, 0x5a, 0xe5, 0x06, 0xe8, 0x49, 0x12, 0x95, 0x83, 0x9f, 0x41, 0x74, 0xcb, 0x7d, 0xda, 0x92, - 0x16, 0xf7, 0x08, 0x61, 0xff, 0x56, 0xff, 0xc0, 0x27, 0xe3, 0x11, 0x2c, 0x61, 0xdb, 0x0e, 0x5f, - 0xbf, 0xea, 0x91, 0x9f, 0xfd, 0xb1, 0x87, 0xb9, 0x78, 0x3e, 0x06, 0xb6, 0x42, 0x69, 0x74, 0x0e, - 0xdb, 0xf6, 0x1e, 0x21, 0x9d, 0x97, 0xcb, 0x3d, 0x0f, 0x73, 0xf4, 0x31, 0xe8, 0xf2, 0xb7, 0x4d, - 0x44, 0x1e, 0xcf, 0x87, 0xbc, 0x28, 0x21, 0xfa, 0xc0, 0xfb, 0x35, 0x87, 0x8f, 0x93, 0x40, 0x9e, - 0x38, 0x87, 0xe6, 0x9a, 0x6b, 0xa7, 0x6b, 0xee, 0x20, 0x4f, 0x9e, 0x4f, 0x73, 0x1b, 0xdc, 0x82, - 0x52, 0x5b, 0xb3, 0x7c, 0x27, 0x8f, 0x1c, 0xd6, 0x92, 0xe0, 0x52, 0x3e, 0x02, 0x5d, 0x4a, 0x7f, - 0x28, 0x40, 0xba, 0xa7, 0xba, 0x20, 0x71, 0xe1, 0x66, 0xc4, 0x41, 0x0a, 0xcf, 0x54, 0x3e, 0x9e, - 0x95, 0x8e, 0x91, 0x44, 0x2a, 0x1f, 0xca, 0xe9, 0x7e, 0x82, 0xf0, 0xfd, 0x9c, 0x15, 0x0b, 0x82, - 0x29, 0x75, 0x3a, 0xd8, 0x23, 0xc4, 0x0c, 0x03, 0x15, 0xe1, 0x8d, 0x64, 0x63, 0x22, 0x84, 0x21, - 0x0e, 0x6b, 0x03, 0xad, 0x29, 0x4a, 0x18, 0x8a, 0x72, 0x35, 0xd5, 0xa3, 0x62, 0xc5, 0xb0, 0xd2, - 0x76, 0x29, 0x26, 0x8b, 0xbe, 0x62, 0x4e, 0xe7, 0x2b, 0xe6, 0xb2, 0xf4, 0x56, 0x0b, 0x31, 0x7a, - 0x0a, 0xe9, 0x40, 0x39, 0x62, 0x2c, 0x99, 0xe5, 0x72, 0x3e, 0x96, 0x1b, 0x1d, 0x3b, 0x49, 0x44, - 0x1e, 0xac, 0xa6, 0x7a, 0x51, 0xd5, 0x9b, 0x19, 0xaa, 0x7a, 0xd7, 0x13, 0x4d, 0xa9, 0xca, 0x05, - 0x50, 0x19, 0x64, 0x4b, 0x11, 0xce, 0x0e, 0x45, 0x58, 0x4a, 0xf3, 0x27, 0x39, 0xfb, 0x8e, 0x5a, - 0x5d, 0x4c, 0x17, 0x3d, 0x67, 0x69, 0x5f, 0xab, 0x90, 0xaf, 0x3d, 0x0f, 0xc4, 0xac, 0xff, 0x1a, - 0x5a, 0x85, 0xfc, 0x68, 0x90, 0xd5, 0x2a, 0x24, 0x5d, 0xbb, 0x55, 0xc8, 0x9c, 0xf4, 0x56, 0x11, - 0x97, 0x28, 0x1d, 0x6c, 0x7d, 0x31, 0x0b, 0x63, 0x07, 0xcc, 0x41, 0x8f, 0xa1, 0xd0, 0x39, 0x1e, - 0xd1, 0x9d, 0xd4, 0xde, 0xd4, 0xff, 0xe9, 0x42, 0xff, 0x7f, 0xbe, 0x60, 0x35, 0x3c, 0x77, 0x78, - 0x6a, 0xae, 0x9d, 0x83, 0xa7, 0xfb, 0xc9, 0x20, 0x07, 0x4f, 0x74, 0x48, 0xf7, 0x60, 0x3a, 0x32, - 0x13, 0xa3, 0xf5, 0x41, 0xc9, 0x7d, 0xe3, 0xba, 0x6e, 0xe4, 0x0d, 0x57, 0x6c, 0x16, 0x4c, 0xb5, - 0x27, 0x6a, 0x74, 0x7b, 0x40, 0x6e, 0xcf, 0x30, 0xae, 0xdf, 0xc9, 0x15, 0x1b, 0x27, 0x09, 0x27, - 0xf1, 0x4c, 0x92, 0xc8, 0x10, 0x9f, 0x49, 0x12, 0x1d, 0xed, 0x11, 0x85, 0xcb, 0xd1, 0x21, 0x1b, - 0x0d, 0xaa, 0x44, 0xc2, 0x07, 0x00, 0xbd, 0x9a, 0x3b, 0x5e, 0x11, 0x9e, 0xc2, 0x6c, 0x7c, 0x80, - 0x45, 0x1b, 0x99, 0x10, 0x3d, 0x43, 0xbe, 0xbe, 0x39, 0x44, 0x86, 0xa2, 0xfd, 0x54, 0x83, 0xb9, - 0x84, 0x61, 0x12, 0xbd, 0x99, 0x09, 0x95, 0x34, 0x4a, 0xeb, 0x77, 0x87, 0x4d, 0x4b, 0x91, 0xa1, - 0x66, 0xc1, 0xdc, 0x32, 0xe2, 0x03, 0x6e, 0x6e, 0x19, 0x3d, 0x23, 0x27, 0xfa, 0x5c, 0x83, 0xc5, - 0xe4, 0xf1, 0x0d, 0xbd, 0x9d, 0x13, 0xb2, 0x6f, 0x44, 0xd5, 0xdf, 0x39, 0x47, 0xa6, 0xd2, 0xf3, - 0x4c, 0x83, 0xa5, 0x94, 0xe9, 0x07, 0x65, 0xc3, 0xa6, 0x0d, 0x97, 0xfa, 0xf6, 0x79, 0x52, 0x95, - 0xa4, 0xcf, 0x34, 0x98, 0x4f, 0x1a, 0x1b, 0xd0, 0xdd, 0x9c, 0xa0, 0x3d, 0xa3, 0x99, 0xfe, 0xd6, - 0xd0, 0x79, 0x4a, 0xc9, 0x19, 0x5c, 0xe9, 0x79, 0xf1, 0x47, 0x83, 0x36, 0x40, 0xf2, 0x1c, 0xa3, - 0x6f, 0x0d, 0x93, 0xa2, 0x98, 0x03, 0x98, 0x89, 0xf5, 0x41, 0x54, 0x1d, 0x0c, 0xd2, 0x37, 0x7d, - 0xe8, 0x1b, 0xf9, 0x13, 0x62, 0x6e, 0xa3, 0xbd, 0x2b, 0xcb, 0x6d, 0x42, 0x2b, 0xce, 0x72, 0x9b, - 0xd4, 0x1a, 0x6b, 0xe4, 0xf9, 0xcb, 0x92, 0xf6, 0xe2, 0x65, 0x49, 0xfb, 0xf3, 0x65, 0x49, 0x7b, - 0xf6, 0xaa, 0x34, 0xf2, 0xe2, 0x55, 0x69, 0xe4, 0xb7, 0x57, 0xa5, 0x11, 0x58, 0x76, 0x69, 0x0a, - 0xde, 0x03, 0xed, 0x23, 0x23, 0xf2, 0x49, 0xaf, 0x1b, 0xb4, 0xee, 0xd2, 0xc8, 0x55, 0xf5, 0xac, - 0xf3, 0x27, 0x82, 0xc6, 0xa4, 0xf8, 0xcb, 0xc0, 0x1b, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x30, - 0x30, 0x5c, 0xc9, 0x2d, 0x19, 0x00, 0x00, + // 1658 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcf, 0x6f, 0xdc, 0xc4, + 0x17, 0xcf, 0xe6, 0x57, 0xb3, 0x2f, 0x4d, 0xda, 0x4e, 0x7e, 0x6d, 0xdc, 0x66, 0xb3, 0xdd, 0x7c, + 0xbf, 0x52, 0x69, 0xc9, 0x6e, 0x12, 0x44, 0x81, 0x88, 0x4b, 0x36, 0x65, 0xa3, 0x1e, 0x02, 0x95, + 0x4b, 0x41, 0x82, 0x83, 0x35, 0xbb, 0x9e, 0x3a, 0x56, 0xbc, 0x9e, 0xc4, 0x33, 0x49, 0xd3, 0x2b, + 0x42, 0xe2, 0x84, 0xd4, 0x23, 0x57, 0xc4, 0x09, 0x84, 0x04, 0x48, 0x5c, 0xe0, 0x2f, 0xa8, 0x10, + 0x87, 0x8a, 0x13, 0x27, 0x40, 0xed, 0x81, 0x3f, 0x81, 0x2b, 0xf2, 0xcc, 0xd8, 0xb1, 0x77, 0xed, + 0xb5, 0x37, 0x34, 0xa7, 0x64, 0x3c, 0xef, 0x7d, 0xde, 0xe7, 0xf3, 0xe6, 0xe7, 0x9b, 0x85, 0xe5, + 0x03, 0x8f, 0x1e, 0x13, 0x17, 0xbb, 0x6d, 0x52, 0x27, 0x27, 0xed, 0x3d, 0xec, 0x5a, 0xa4, 0x7e, + 0xbc, 0x5e, 0xe7, 0x27, 0xb5, 0x03, 0x8f, 0x72, 0x8a, 0xe6, 0x4f, 0x0d, 0x6a, 0x81, 0x41, 0xed, + 0x78, 0x5d, 0x2b, 0xb7, 0x29, 0xeb, 0x50, 0x56, 0x6f, 0x61, 0xe6, 0x3b, 0xb4, 0x08, 0xc7, 0xeb, + 0xf5, 0x36, 0xb5, 0x5d, 0xe9, 0xa7, 0x2d, 0xa8, 0xfe, 0x0e, 0xb3, 0x7c, 0xbc, 0x0e, 0xb3, 0x54, + 0xc7, 0xa2, 0xec, 0x30, 0x44, 0xab, 0x2e, 0x1b, 0xaa, 0x6b, 0xd6, 0xa2, 0x16, 0x95, 0xdf, 0xfd, + 0xff, 0xd4, 0xd7, 0x95, 0x14, 0x8a, 0x1d, 0xec, 0xed, 0x13, 0x9e, 0x61, 0x44, 0x3d, 0x93, 0x78, + 0x2c, 0xc3, 0xe8, 0x00, 0x7b, 0xb8, 0xa3, 0x8c, 0xaa, 0x3f, 0x17, 0x60, 0x66, 0x97, 0x59, 0xdb, + 0x1e, 0xc1, 0x9c, 0x6c, 0xb1, 0x7d, 0x9d, 0x1c, 0x1e, 0x11, 0xc6, 0xd1, 0x36, 0x14, 0x31, 0xdb, + 0x37, 0x04, 0x60, 0xa9, 0x50, 0x29, 0xdc, 0x98, 0xdc, 0xa8, 0xd4, 0x92, 0x93, 0x53, 0xdb, 0x62, + 0xfb, 0xef, 0xf9, 0x76, 0x8d, 0xd1, 0xa7, 0x7f, 0x2c, 0x0f, 0xe9, 0x13, 0x58, 0xb5, 0xd1, 0x0e, + 0x20, 0x01, 0x60, 0xb4, 0x7d, 0x78, 0x9b, 0xba, 0xc6, 0x43, 0x42, 0x4a, 0xc3, 0x02, 0x6d, 0xb1, + 0xa6, 0x92, 0xe1, 0xa7, 0xb4, 0xa6, 0x52, 0x5a, 0xdb, 0xa6, 0xb6, 0xab, 0x5f, 0x16, 0x4e, 0xdb, + 0xca, 0xa7, 0x49, 0xc8, 0xe6, 0xdc, 0x27, 0x7f, 0x7f, 0x7f, 0xf3, 0x72, 0x48, 0xa8, 0xc6, 0x88, + 0xe3, 0x10, 0xaf, 0xba, 0x0e, 0xb3, 0x71, 0xee, 0xec, 0x80, 0xba, 0x8c, 0xa0, 0x45, 0x98, 0x90, + 0x71, 0x6d, 0x53, 0x70, 0x1f, 0xd5, 0x2f, 0x88, 0xf6, 0x5d, 0xb3, 0xfa, 0x53, 0x54, 0x6f, 0xc3, + 0x36, 0x23, 0x7a, 0x5b, 0xb6, 0x99, 0x4f, 0x6f, 0xc3, 0x36, 0x63, 0x7a, 0x5b, 0xaa, 0xfd, 0xf2, + 0xf4, 0xce, 0xfa, 0x7a, 0x2f, 0x85, 0x84, 0x6a, 0xad, 0xa3, 0xc7, 0x5d, 0x72, 0x05, 0xf5, 0x6c, + 0xb9, 0x87, 0x30, 0xe7, 0xbb, 0xf8, 0x12, 0x1c, 0xc1, 0x31, 0xd0, 0xbb, 0x06, 0xe3, 0xcc, 0xb6, + 0x5c, 0x25, 0xb6, 0xd8, 0x28, 0xfd, 0xf6, 0xe3, 0xea, 0xac, 0x62, 0xb8, 0x65, 0x9a, 0x1e, 0x61, + 0xec, 0x3e, 0xf7, 0x6c, 0xd7, 0xd2, 0x95, 0x5d, 0x2c, 0xca, 0x70, 0x2c, 0xca, 0xe6, 0xa4, 0x4f, + 0x57, 0xd9, 0x55, 0x4b, 0x30, 0xdf, 0x1d, 0x52, 0xf2, 0xac, 0x7e, 0x3b, 0x02, 0x68, 0x97, 0x59, + 0x4d, 0xdb, 0x71, 0x1a, 0xb6, 0xc9, 0xa2, 0x54, 0xc4, 0x78, 0xe6, 0xa0, 0x22, 0xec, 0xd0, 0x55, + 0x28, 0xca, 0xe5, 0x10, 0x70, 0x99, 0xd2, 0x27, 0xe4, 0x87, 0xbb, 0x26, 0x72, 0xe1, 0x22, 0xa7, + 0x1c, 0x3b, 0x06, 0x66, 0x8c, 0x70, 0x56, 0x1a, 0xa9, 0x8c, 0xf4, 0x4d, 0x7f, 0x63, 0xcd, 0x1f, + 0xc5, 0x6f, 0xfe, 0x5c, 0xbe, 0x61, 0xd9, 0x7c, 0xef, 0xa8, 0x55, 0x6b, 0xd3, 0x8e, 0x5a, 0xa8, + 0xea, 0xcf, 0x2a, 0x33, 0xf7, 0xeb, 0xfc, 0xf1, 0x01, 0x61, 0xc2, 0x81, 0xe9, 0x93, 0x22, 0xc0, + 0x96, 0xc0, 0x47, 0x55, 0x98, 0x0a, 0x07, 0xca, 0xb0, 0x4d, 0x56, 0x1a, 0xad, 0x8c, 0xdc, 0x18, + 0xd5, 0x27, 0x83, 0x59, 0x71, 0xd7, 0x64, 0xe8, 0x03, 0xd0, 0x24, 0x75, 0x83, 0x11, 0xce, 0x1d, + 0xd2, 0x21, 0x2e, 0x37, 0x1e, 0x3a, 0x98, 0x8b, 0x09, 0x32, 0x96, 0x35, 0x41, 0x16, 0xa4, 0xf3, + 0xfd, 0xd0, 0xb7, 0xe9, 0x60, 0xde, 0x24, 0x04, 0xbd, 0x0b, 0xf3, 0xe1, 0xa2, 0x88, 0x4f, 0xba, + 0xf1, 0x2c, 0xcc, 0x99, 0x60, 0x95, 0x46, 0xe7, 0x9d, 0x1a, 0x48, 0xb9, 0xba, 0xe6, 0xc4, 0x4a, + 0x39, 0x1d, 0x2d, 0x35, 0x8a, 0xb3, 0xe1, 0x20, 0x6e, 0xb1, 0xfd, 0x60, 0x10, 0x23, 0xc6, 0xf2, + 0xab, 0x32, 0x3e, 0x14, 0x93, 0x61, 0x57, 0x8c, 0x8d, 0xa4, 0x1f, 0x8c, 0x7a, 0x0d, 0xc6, 0xb0, + 0xd9, 0xb1, 0xdd, 0xcc, 0x41, 0x97, 0x66, 0x7d, 0xc7, 0x7c, 0x13, 0x7c, 0xde, 0xd2, 0xb0, 0xba, + 0x08, 0x0b, 0x3d, 0x21, 0x15, 0x9b, 0x7f, 0x0a, 0x50, 0x0a, 0xfb, 0x3e, 0xb4, 0xf9, 0x9e, 0xe9, + 0xe1, 0x47, 0xe7, 0x41, 0x08, 0x2d, 0x01, 0x70, 0x6a, 0x60, 0xe9, 0x57, 0x1a, 0xf1, 0x11, 0xf5, + 0x22, 0xa7, 0x0a, 0x08, 0xb5, 0x61, 0x1c, 0x77, 0xe8, 0x91, 0xcb, 0xc5, 0x64, 0x79, 0xc9, 0xb3, + 0x53, 0x41, 0xc7, 0x92, 0x72, 0x15, 0x16, 0x13, 0x84, 0xab, 0xb4, 0xfc, 0x5a, 0x80, 0xa5, 0xb0, + 0xf7, 0xc1, 0x81, 0x89, 0x39, 0xb9, 0x43, 0x38, 0xb6, 0x1d, 0x76, 0x2e, 0xb9, 0xd1, 0x61, 0x5a, + 0x75, 0x9a, 0x32, 0x8a, 0xc8, 0xcf, 0xe4, 0xc6, 0xff, 0xd3, 0xf6, 0x5b, 0x49, 0x4c, 0x51, 0x52, + 0x9b, 0xee, 0x54, 0x27, 0xfa, 0x31, 0xa6, 0xb5, 0x02, 0xe5, 0x34, 0x35, 0x4a, 0xf0, 0x57, 0xbd, + 0x82, 0xdf, 0x71, 0x71, 0xcb, 0x21, 0xe6, 0xb9, 0x08, 0x7e, 0x05, 0x2e, 0xe3, 0x76, 0x9b, 0x1c, + 0x70, 0xdb, 0xb5, 0xe4, 0x5a, 0x95, 0x92, 0x27, 0xf4, 0x4b, 0xe1, 0x77, 0xb1, 0x14, 0xb3, 0x74, + 0x84, 0x24, 0x95, 0x8e, 0xef, 0x0a, 0x50, 0xe9, 0x32, 0x79, 0xc0, 0x82, 0x7d, 0xe2, 0x5c, 0xa4, + 0x6c, 0xc0, 0x1c, 0x76, 0x1c, 0xfa, 0xc8, 0x38, 0x62, 0xb1, 0xcd, 0x4c, 0xe9, 0x99, 0x11, 0x9d, + 0xa7, 0x1c, 0xfc, 0xae, 0x98, 0xa6, 0x15, 0xb8, 0xde, 0x87, 0xb0, 0x92, 0xf5, 0xf5, 0x70, 0xc4, + 0x6a, 0x17, 0xbb, 0xd8, 0x22, 0xf7, 0x88, 0xd7, 0xb1, 0x19, 0xb3, 0xa9, 0xcb, 0xce, 0x6b, 0xbd, + 0x7a, 0xe4, 0x98, 0xee, 0x13, 0x03, 0x3b, 0x8e, 0x38, 0x32, 0x8a, 0x7a, 0x51, 0x7e, 0xd9, 0x72, + 0x1c, 0xd4, 0x84, 0x22, 0xa7, 0x86, 0x6c, 0xab, 0x25, 0xbb, 0x92, 0x7a, 0x1b, 0x6a, 0xb7, 0x09, + 0x63, 0x3b, 0x1e, 0x76, 0x79, 0x70, 0x41, 0xe0, 0x54, 0x17, 0xae, 0xe8, 0x0e, 0x4c, 0x70, 0x6a, + 0x58, 0x7e, 0x5f, 0x69, 0x6c, 0x50, 0x98, 0x0b, 0x9c, 0x8a, 0x66, 0x2c, 0xa1, 0xff, 0x83, 0x6a, + 0xbf, 0x54, 0xa9, 0x8c, 0xfe, 0x30, 0x1c, 0x99, 0x4b, 0xd2, 0x4c, 0x27, 0x87, 0x5b, 0x9c, 0x7b, + 0xec, 0x9c, 0x66, 0xfc, 0x15, 0x71, 0x1a, 0x11, 0xc3, 0x3f, 0x9e, 0xe4, 0x4e, 0xa8, 0xb2, 0x3a, + 0xdd, 0x0e, 0xae, 0x6b, 0xef, 0xfb, 0xdb, 0x21, 0xaa, 0xc3, 0x6c, 0xdc, 0xd4, 0x23, 0x1d, 0x7a, + 0x2c, 0xb3, 0x5c, 0xd4, 0xaf, 0x44, 0xac, 0x75, 0xd1, 0x11, 0xc1, 0xf6, 0x8f, 0x5d, 0x85, 0x3d, + 0x16, 0xc5, 0x6e, 0xd8, 0x66, 0x37, 0xb6, 0x32, 0x55, 0xd8, 0xe3, 0x51, 0x6c, 0x61, 0x2d, 0xb1, + 0x63, 0x99, 0xbd, 0x0e, 0xcb, 0xa9, 0x29, 0x53, 0x69, 0xfd, 0xb2, 0x20, 0xb6, 0xd5, 0x1d, 0x7a, + 0x2c, 0x2f, 0x65, 0xd2, 0x38, 0xc8, 0xe8, 0x6d, 0x28, 0xe2, 0x23, 0xbe, 0x47, 0x3d, 0x9b, 0x3f, + 0xce, 0xcc, 0xea, 0xa9, 0x29, 0x7a, 0x1b, 0xc6, 0x65, 0x22, 0xd5, 0xcd, 0xb1, 0xdc, 0x7f, 0x5f, + 0x54, 0xb3, 0x43, 0xf9, 0x6c, 0x4e, 0xfb, 0x12, 0x4e, 0xd1, 0xaa, 0xd7, 0x40, 0x4b, 0xa2, 0xa8, + 0x14, 0xfc, 0x02, 0xe2, 0xb4, 0xdc, 0xa1, 0xc7, 0x52, 0x62, 0x93, 0x10, 0xf6, 0x5f, 0xf9, 0xf7, + 0x9d, 0x19, 0x0f, 0x60, 0x01, 0x9b, 0xa6, 0x7f, 0x45, 0x31, 0x22, 0xc3, 0xee, 0x5f, 0x85, 0xb2, + 0x2f, 0x6a, 0x52, 0xe8, 0x0c, 0x36, 0xcd, 0x26, 0x21, 0xe1, 0xad, 0xdf, 0xbf, 0x0b, 0xa1, 0x8f, + 0x41, 0x93, 0x63, 0x9b, 0x88, 0x3c, 0x9a, 0x0f, 0x79, 0x5e, 0x42, 0xf4, 0x80, 0xf7, 0x72, 0xf6, + 0xa7, 0x93, 0x40, 0x1e, 0x3b, 0x03, 0xe7, 0x86, 0x6d, 0xa6, 0x73, 0x0e, 0x91, 0xc7, 0xcf, 0xc6, + 0x39, 0x00, 0x6f, 0x43, 0x39, 0xe0, 0x9c, 0x7c, 0xf3, 0x2c, 0x5d, 0xc8, 0x17, 0x40, 0x93, 0xd4, + 0xef, 0x27, 0xdc, 0x40, 0x91, 0x0d, 0xd7, 0x23, 0x0a, 0x52, 0xe2, 0x4c, 0xe4, 0x8b, 0xb3, 0x14, + 0x0a, 0x49, 0x0c, 0xe5, 0x42, 0x25, 0x5d, 0x8f, 0xe7, 0x5f, 0x60, 0x59, 0xa9, 0x28, 0x22, 0xa5, + 0x96, 0x6d, 0x4d, 0x42, 0x74, 0xdf, 0x50, 0x05, 0xbc, 0x96, 0x2c, 0x4c, 0x98, 0x30, 0xc4, 0x61, + 0xa5, 0xaf, 0x34, 0x15, 0x12, 0x06, 0x0a, 0xb9, 0x9c, 0xaa, 0x51, 0x45, 0xc5, 0xb0, 0x14, 0xa8, + 0x14, 0x25, 0x5f, 0x4f, 0x32, 0x27, 0xf3, 0x25, 0x73, 0x51, 0x6a, 0x6b, 0xf8, 0x18, 0x5d, 0x89, + 0xb4, 0xa0, 0x12, 0x11, 0x96, 0x1c, 0xe5, 0x62, 0xbe, 0x28, 0xd7, 0x42, 0x39, 0x49, 0x81, 0x1c, + 0x58, 0x4e, 0xd5, 0xa2, 0xb2, 0x37, 0x35, 0x50, 0xf6, 0xae, 0x26, 0x8a, 0x52, 0x99, 0xf3, 0xa0, + 0xda, 0x4f, 0x96, 0x0a, 0x38, 0x3d, 0x50, 0xc0, 0x72, 0x9a, 0x3e, 0x19, 0xb3, 0x67, 0xab, 0xd5, + 0x44, 0x75, 0xd1, 0xb5, 0x97, 0xf6, 0x1c, 0x15, 0xf2, 0xda, 0x73, 0x4f, 0x3c, 0xc2, 0xbc, 0x84, + 0xa3, 0x42, 0xbe, 0xe6, 0x64, 0x1d, 0x15, 0x32, 0x5c, 0x70, 0x54, 0x48, 0x9f, 0xf4, 0xa3, 0x22, + 0x4e, 0x51, 0x2a, 0xd8, 0xf8, 0x62, 0x1a, 0x46, 0x76, 0x99, 0x85, 0x1e, 0x42, 0x31, 0xdc, 0x1e, + 0xd1, 0xad, 0xd4, 0xb3, 0xa9, 0xf7, 0x4d, 0x49, 0x7b, 0x35, 0x9f, 0xb1, 0x7a, 0xd5, 0x08, 0xe3, + 0x34, 0x6c, 0x33, 0x47, 0x9c, 0xd3, 0xb7, 0x9c, 0x1c, 0x71, 0xa2, 0xaf, 0x27, 0x0e, 0x4c, 0x46, + 0x1e, 0x2b, 0xd0, 0x6a, 0x3f, 0xe7, 0x9e, 0x77, 0x14, 0xad, 0x96, 0xd7, 0x5c, 0x45, 0x6b, 0xc3, + 0x44, 0x50, 0x51, 0xa3, 0x9b, 0x7d, 0x7c, 0xbb, 0x1e, 0x49, 0xb4, 0x5b, 0xb9, 0x6c, 0xe3, 0x41, + 0xfc, 0x4a, 0x3c, 0x33, 0x48, 0xa4, 0x88, 0xcf, 0x0c, 0x12, 0x2d, 0xed, 0x11, 0x85, 0x8b, 0xd1, + 0x22, 0x1b, 0xf5, 0xcb, 0x44, 0xc2, 0x03, 0x80, 0x56, 0xcf, 0x6d, 0xaf, 0x02, 0x1e, 0xc1, 0x74, + 0xbc, 0x80, 0x45, 0x6b, 0x99, 0x10, 0x5d, 0x45, 0xbe, 0xb6, 0x3e, 0x80, 0x87, 0x0a, 0xfb, 0x69, + 0x01, 0x66, 0x12, 0x8a, 0x49, 0xf4, 0x7a, 0x26, 0x54, 0x52, 0x29, 0xad, 0xdd, 0x1e, 0xd4, 0x2d, + 0x85, 0x86, 0xaa, 0x05, 0x73, 0xd3, 0x88, 0x17, 0xb8, 0xb9, 0x69, 0x74, 0x95, 0x9c, 0xe8, 0xf3, + 0x02, 0xcc, 0x27, 0x97, 0x6f, 0xe8, 0xcd, 0x9c, 0x90, 0x3d, 0x25, 0xaa, 0xf6, 0xd6, 0x19, 0x3c, + 0x15, 0x9f, 0x27, 0x05, 0x58, 0x48, 0xa9, 0x7e, 0x50, 0x36, 0x6c, 0x5a, 0x71, 0xa9, 0x6d, 0x9e, + 0xc5, 0x55, 0x51, 0xfa, 0xac, 0x00, 0xb3, 0x49, 0x65, 0x03, 0xba, 0x9d, 0x13, 0xb4, 0xab, 0x34, + 0xd3, 0xde, 0x18, 0xd8, 0x4f, 0x31, 0x39, 0x81, 0x4b, 0x5d, 0x17, 0x7f, 0xd4, 0x6f, 0x01, 0x24, + 0xd7, 0x31, 0xda, 0xc6, 0x20, 0x2e, 0x2a, 0xb2, 0x07, 0x53, 0xb1, 0x73, 0x10, 0xd5, 0xfb, 0x83, + 0xf4, 0x54, 0x1f, 0xda, 0x5a, 0x7e, 0x87, 0x98, 0xda, 0xe8, 0xd9, 0x95, 0xa5, 0x36, 0xe1, 0x28, + 0xce, 0x52, 0x9b, 0x74, 0x34, 0x36, 0xc8, 0xd3, 0xe7, 0xe5, 0xc2, 0xb3, 0xe7, 0xe5, 0xc2, 0x5f, + 0xcf, 0xcb, 0x85, 0x27, 0x2f, 0xca, 0x43, 0xcf, 0x5e, 0x94, 0x87, 0x7e, 0x7f, 0x51, 0x1e, 0x82, + 0x45, 0x9b, 0xa6, 0xe0, 0xdd, 0x2b, 0x7c, 0x54, 0x8b, 0x3c, 0xe9, 0x9d, 0x1a, 0xad, 0xda, 0x34, + 0xd2, 0xaa, 0x9f, 0x84, 0xbf, 0xdd, 0xb4, 0xc6, 0xc5, 0x4f, 0x36, 0xaf, 0xfd, 0x1b, 0x00, 0x00, + 0xff, 0xff, 0x07, 0xe5, 0xbd, 0xe4, 0xc6, 0x1a, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2591,6 +2654,74 @@ func (m *MsgFillBidsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.AskOrderCreationFee != nil { + { + size, err := m.AskOrderCreationFee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if m.SellerSettlementFlatFee != nil { + { + size, err := m.SellerSettlementFlatFee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if len(m.BidOrderIds) > 0 { + dAtA8 := make([]byte, len(m.BidOrderIds)*10) + var j7 int + for _, num := range m.BidOrderIds { + for num >= 1<<7 { + dAtA8[j7] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j7++ + } + dAtA8[j7] = uint8(num) + j7++ + } + i -= j7 + copy(dAtA[i:], dAtA8[:j7]) + i = encodeVarintTx(dAtA, i, uint64(j7)) + i-- + dAtA[i] = 0x22 + } + if len(m.TotalAssets) > 0 { + for iNdEx := len(m.TotalAssets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TotalAssets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.MarketId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Seller) > 0 { + i -= len(m.Seller) + copy(dAtA[i:], m.Seller) + i = encodeVarintTx(dAtA, i, uint64(len(m.Seller))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -3641,6 +3772,34 @@ func (m *MsgFillBidsRequest) Size() (n int) { } var l int _ = l + l = len(m.Seller) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.MarketId != 0 { + n += 1 + sovTx(uint64(m.MarketId)) + } + if len(m.TotalAssets) > 0 { + for _, e := range m.TotalAssets { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.BidOrderIds) > 0 { + l = 0 + for _, e := range m.BidOrderIds { + l += sovTx(uint64(e)) + } + n += 1 + sovTx(uint64(l)) + l + } + if m.SellerSettlementFlatFee != nil { + l = m.SellerSettlementFlatFee.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.AskOrderCreationFee != nil { + l = m.AskOrderCreationFee.Size() + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -4613,6 +4772,239 @@ func (m *MsgFillBidsRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgFillBidsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Seller", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Seller = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalAssets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TotalAssets = append(m.TotalAssets, types.Coin{}) + if err := m.TotalAssets[len(m.TotalAssets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.BidOrderIds = append(m.BidOrderIds, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.BidOrderIds) == 0 { + m.BidOrderIds = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.BidOrderIds = append(m.BidOrderIds, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field BidOrderIds", wireType) + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SellerSettlementFlatFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SellerSettlementFlatFee == nil { + m.SellerSettlementFlatFee = &types.Coin{} + } + if err := m.SellerSettlementFlatFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AskOrderCreationFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AskOrderCreationFee == nil { + m.AskOrderCreationFee = &types.Coin{} + } + if err := m.AskOrderCreationFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) From 8bd77695021769e29dfec4af13ee039cf824a850 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 16:04:38 -0600 Subject: [PATCH 131/309] [1658]: Implment Msg methods for MsgFillBidsRequest. --- x/exchange/msg.go | 43 +++++++- x/exchange/msg_test.go | 218 +++++++++++++++++++++++++++++++++++--- x/exchange/orders.go | 37 +++++++ x/exchange/orders_test.go | 126 ++++++++++++++++++++++ 4 files changed, 404 insertions(+), 20 deletions(-) diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 8a0ef2cf99..bfa3f6e7f6 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -75,13 +75,48 @@ func (m MsgCancelOrderRequest) GetSigners() []sdk.AccAddress { } func (m MsgFillBidsRequest) ValidateBasic() error { - // TODO[1658]: MsgFillBidsRequest.ValidateBasic() - return nil + var errs []error + + if _, err := sdk.AccAddressFromBech32(m.Seller); err != nil { + errs = append(errs, fmt.Errorf("invalid seller: %w", err)) + } + + if m.MarketId == 0 { + errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) + } + + if err := m.TotalAssets.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid total assets: %w", err)) + } else if m.TotalAssets.IsZero() { + errs = append(errs, fmt.Errorf("invalid total assets: cannot be zero")) + } + + if err := ValidateOrderIDs("bid", m.BidOrderIds); err != nil { + errs = append(errs, err) + } + + if m.SellerSettlementFlatFee != nil { + if err := m.SellerSettlementFlatFee.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid seller settlement flat fee: %w", err)) + } else if m.SellerSettlementFlatFee.IsZero() { + errs = append(errs, fmt.Errorf("invalid seller settlement flat fee: %s amount cannot be zero", m.SellerSettlementFlatFee.Denom)) + } + } + + if m.AskOrderCreationFee != nil { + if err := m.AskOrderCreationFee.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid ask order creation fee: %w", err)) + } else if m.AskOrderCreationFee.IsZero() { + errs = append(errs, fmt.Errorf("invalid ask order creation fee: %s amount cannot be zero", m.AskOrderCreationFee.Denom)) + } + } + + return errors.Join(errs...) } func (m MsgFillBidsRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgFillBidsRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Seller) + return []sdk.AccAddress{addr} } func (m MsgFillAsksRequest) ValidateBasic() error { diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 978a70029d..98541ecb9b 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -13,6 +13,8 @@ import ( "github.com/provenance-io/provenance/testutil/assertions" ) +const emptyAddrErr = "empty address string is not allowed" + func TestAllMsgsGetSigners(t *testing.T) { // getTypeName gets just the type name of the provided thing, e.g. "MsgGovCreateMarketRequest". getTypeName := func(thing interface{}) string { @@ -27,7 +29,6 @@ func TestAllMsgsGetSigners(t *testing.T) { testAddr := sdk.AccAddress("testAddr____________") badAddrStr := "badaddr" badAddrErr := "decoding bech32 failed: invalid bech32 string length 7" - emptyAddrErr := "empty address string is not allowed" msgMakers := []func(signer string) sdk.Msg{ func(signer string) sdk.Msg { @@ -39,7 +40,9 @@ func TestAllMsgsGetSigners(t *testing.T) { func(signer string) sdk.Msg { return &MsgCancelOrderRequest{Signer: signer} }, - // TODO[1658]: Add MsgFillBidsRequest once it's actually been defined. + func(signer string) sdk.Msg { + return &MsgFillBidsRequest{Seller: signer} + }, // TODO[1658]: Add MsgFillAsksRequest once it's actually been defined. func(signer string) sdk.Msg { return &MsgMarketSettleRequest{Admin: signer} @@ -250,7 +253,7 @@ func TestMsgCancelOrderRequest_ValidateBasic(t *testing.T) { Signer: "", OrderId: 1, }, - expErr: []string{"invalid signer: ", "empty address string is not allowed"}, + expErr: []string{"invalid signer: ", emptyAddrErr}, }, { name: "invalid owner", @@ -277,7 +280,190 @@ func TestMsgCancelOrderRequest_ValidateBasic(t *testing.T) { } } -// TODO[1658]: func TestMsgFillBidsRequest_ValidateBasic(t *testing.T) +func TestMsgFillBidsRequest_ValidateBasic(t *testing.T) { + coin := func(amount int64, denom string) *sdk.Coin { + return &sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + seller := sdk.AccAddress("seller______________").String() + + tests := []struct { + name string + msg MsgFillBidsRequest + expErr []string + }{ + { + name: "control", + msg: MsgFillBidsRequest{ + Seller: seller, + MarketId: 1, + TotalAssets: sdk.Coins{*coin(3, "acorn")}, + BidOrderIds: []uint64{1, 2, 3}, + SellerSettlementFlatFee: coin(2, "banana"), + AskOrderCreationFee: coin(8, "cactus"), + }, + expErr: nil, + }, + { + name: "empty seller", + msg: MsgFillBidsRequest{ + Seller: "", + MarketId: 1, + TotalAssets: sdk.Coins{*coin(3, "acorn")}, + BidOrderIds: []uint64{1}, + }, + expErr: []string{"invalid seller", emptyAddrErr}, + }, + { + name: "bad seller", + msg: MsgFillBidsRequest{ + Seller: "not-an-address", + MarketId: 1, + TotalAssets: sdk.Coins{*coin(3, "acorn")}, + BidOrderIds: []uint64{1}, + }, + expErr: []string{"invalid seller", "decoding bech32 failed"}, + }, + { + name: "market id zero", + msg: MsgFillBidsRequest{ + Seller: seller, + MarketId: 0, + TotalAssets: sdk.Coins{*coin(3, "acorn")}, + BidOrderIds: []uint64{1}, + }, + expErr: []string{"invalid market id", "cannot be zero"}, + }, + { + name: "nil total assets", + msg: MsgFillBidsRequest{ + Seller: seller, + MarketId: 1, + TotalAssets: nil, + BidOrderIds: []uint64{1}, + }, + expErr: []string{"invalid total assets", "cannot be zero"}, + }, + { + name: "empty total assets", + msg: MsgFillBidsRequest{ + Seller: seller, + MarketId: 1, + TotalAssets: sdk.Coins{}, + BidOrderIds: []uint64{1}, + }, + expErr: []string{"invalid total assets", "cannot be zero"}, + }, + { + name: "invalid total assets", + msg: MsgFillBidsRequest{ + Seller: seller, + MarketId: 1, + TotalAssets: sdk.Coins{*coin(-1, "acorn")}, + BidOrderIds: []uint64{1}, + }, + expErr: []string{"invalid total assets", "coin -1acorn amount is not positive"}, + }, + { + name: "nil order ids", + msg: MsgFillBidsRequest{ + Seller: seller, + MarketId: 1, + TotalAssets: sdk.Coins{*coin(1, "acorn")}, + BidOrderIds: nil, + }, + expErr: []string{"no bid order ids provided"}, + }, + { + name: "order id zero", + msg: MsgFillBidsRequest{ + Seller: seller, + MarketId: 1, + TotalAssets: sdk.Coins{*coin(1, "acorn")}, + BidOrderIds: []uint64{0}, + }, + expErr: []string{"invalid bid order ids: cannot contain order id zero"}, + }, + { + name: "duplicate order ids", + msg: MsgFillBidsRequest{ + Seller: seller, + MarketId: 1, + TotalAssets: sdk.Coins{*coin(1, "acorn")}, + BidOrderIds: []uint64{1, 2, 1}, + }, + expErr: []string{"duplicate bid order ids provided: [1]"}, + }, + { + name: "invalid seller settlement flat fee", + msg: MsgFillBidsRequest{ + Seller: seller, + MarketId: 1, + TotalAssets: sdk.Coins{*coin(1, "acorn")}, + BidOrderIds: []uint64{1}, + SellerSettlementFlatFee: coin(-1, "catan"), + }, + expErr: []string{"invalid seller settlement flat fee", "negative coin amount: -1"}, + }, + { + name: "seller settlement flat fee with zero amount", + msg: MsgFillBidsRequest{ + Seller: seller, + MarketId: 1, + TotalAssets: sdk.Coins{*coin(1, "acorn")}, + BidOrderIds: []uint64{1}, + SellerSettlementFlatFee: coin(0, "catan"), + }, + expErr: []string{"invalid seller settlement flat fee", "catan amount cannot be zero"}, + }, + { + name: "invalid order creation fee", + msg: MsgFillBidsRequest{ + Seller: seller, + MarketId: 1, + TotalAssets: sdk.Coins{*coin(1, "acorn")}, + BidOrderIds: []uint64{1}, + AskOrderCreationFee: coin(-1, "catan"), + }, + expErr: []string{"invalid ask order creation fee", "negative coin amount: -1"}, + }, + { + name: "order creation fee with zero amount", + msg: MsgFillBidsRequest{ + Seller: seller, + MarketId: 1, + TotalAssets: sdk.Coins{*coin(1, "acorn")}, + BidOrderIds: []uint64{1}, + AskOrderCreationFee: coin(0, "catan"), + }, + expErr: []string{"invalid ask order creation fee", "catan amount cannot be zero"}, + }, + { + name: "multiple errors", + msg: MsgFillBidsRequest{ + Seller: "", + MarketId: 0, + TotalAssets: nil, + BidOrderIds: nil, + SellerSettlementFlatFee: coin(0, "catan"), + AskOrderCreationFee: coin(-1, "catan"), + }, + expErr: []string{ + "invalid seller", + "invalid market id", + "invalid total assets", + "no bid order ids provided", + "invalid seller settlement flat fee", + "invalid ask order creation fee", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} // TODO[1658]: func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) @@ -314,7 +500,7 @@ func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) { ToAddress: goodToAddr, Amount: goodCoins, }, - expErr: []string{`invalid administrator ""`, "empty address string is not allowed"}, + expErr: []string{`invalid administrator ""`, emptyAddrErr}, }, { name: "bad administrator", @@ -344,7 +530,7 @@ func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) { ToAddress: "", Amount: goodCoins, }, - expErr: []string{`invalid to address ""`, "empty address string is not allowed"}, + expErr: []string{`invalid to address ""`, emptyAddrErr}, }, { name: "bad to address", @@ -447,7 +633,7 @@ func TestMsgMarketUpdateDetailsRequest_ValidateBasic(t *testing.T) { MarketId: 1, MarketDetails: MarketDetails{}, }, - expErr: []string{`invalid administrator ""`, "empty address string is not allowed"}, + expErr: []string{`invalid administrator ""`, emptyAddrErr}, }, { name: "invalid admin", @@ -574,7 +760,7 @@ func TestMsgMarketUpdateEnabledRequest_ValidateBasic(t *testing.T) { MarketId: 1, }, expErr: []string{ - `invalid administrator ""`, "empty address string is not allowed", + `invalid administrator ""`, emptyAddrErr, }, }, { @@ -639,7 +825,7 @@ func TestMsgMarketUpdateUserSettleRequest_ValidateBasic(t *testing.T) { MarketId: 1, }, expErr: []string{ - `invalid administrator ""`, "empty address string is not allowed", + `invalid administrator ""`, emptyAddrErr, }, }, { @@ -700,7 +886,7 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { MarketId: 1, RevokeAll: []string{goodAddr1}, }, - expErr: []string{`invalid administrator ""`, "empty address string is not allowed"}, + expErr: []string{`invalid administrator ""`, emptyAddrErr}, }, { name: "invalid admin", @@ -862,7 +1048,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { MarketId: 1, CreateAskToAdd: []string{"abc"}, }, - expErr: []string{"invalid administrator", "empty address string is not allowed"}, + expErr: []string{"invalid administrator", emptyAddrErr}, }, { name: "bad admin", @@ -1064,7 +1250,7 @@ func TestMsgGovCreateMarketRequest_ValidateBasic(t *testing.T) { Authority: "", Market: validMarket, }, - expErr: []string{"invalid authority", "empty address string is not allowed"}, + expErr: []string{"invalid authority", emptyAddrErr}, }, { name: "bad authority", @@ -1131,7 +1317,7 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { Authority: "", AddFeeCreateAskFlat: []sdk.Coin{coin(1, "nhash")}, }, - expErr: []string{"invalid authority", "empty address string is not allowed"}, + expErr: []string{"invalid authority", emptyAddrErr}, }, { name: "bad authority", @@ -1261,7 +1447,7 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { RemoveFeeBuyerSettlementRatios: []FeeRatio{ratio(1, "nhash", 2, "nhash")}, }, expErr: []string{ - "invalid authority", "empty address string is not allowed", + "invalid authority", emptyAddrErr, `invalid create-ask flat fee to add option "0nhash": amount cannot be zero`, "cannot add and remove the same create-ask flat fee options 0nhash", `invalid create-bid flat fee to add option "0nhash": amount cannot be zero`, @@ -1391,7 +1577,7 @@ func TestMsgGovUpdateParamsRequest_ValidateBasic(t *testing.T) { { name: "zero value", msg: MsgGovUpdateParamsRequest{}, - expErr: []string{"invalid authority", "empty address string is not allowed"}, + expErr: []string{"invalid authority", emptyAddrErr}, }, { name: "default params", @@ -1422,7 +1608,7 @@ func TestMsgGovUpdateParamsRequest_ValidateBasic(t *testing.T) { Authority: "", Params: *DefaultParams(), }, - expErr: []string{"invalid authority", "empty address string is not allowed"}, + expErr: []string{"invalid authority", emptyAddrErr}, }, { name: "bad authority", diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 260447e8ef..61988d0f57 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -17,6 +17,43 @@ const ( OrderTypeByteBid = byte(0x01) ) +func findDuplicateIds(orderIDs []uint64) []uint64 { + var rv []uint64 + seen := make(map[uint64]bool) + dups := make(map[uint64]bool) + for _, orderID := range orderIDs { + if seen[orderID] && !dups[orderID] { + rv = append(rv, orderID) + dups[orderID] = true + } + seen[orderID] = true + } + return rv +} + +// ContainsUint64 returns true if the uint64 to find is in the vals slice. +func ContainsUint64(vals []uint64, toFind uint64) bool { + return contains(vals, toFind, func(a, b uint64) bool { + return a == b + }) +} + +// ValidateOrderIDs makes sure that one or more order ids are provided, +// none of them are zero, and there aren't any duplicates. +func ValidateOrderIDs(field string, orderIDs []uint64) error { + if len(orderIDs) == 0 { + return fmt.Errorf("no %s order ids provided", field) + } + if ContainsUint64(orderIDs, 0) { + return fmt.Errorf("invalid %s order ids: cannot contain order id zero", field) + } + dupOrderIDs := findDuplicateIds(orderIDs) + if len(dupOrderIDs) > 0 { + return fmt.Errorf("duplicate %s order ids provided: %v", field, dupOrderIDs) + } + return nil +} + // NewOrder creates a new empty Order with the provided order id. // The order details are set using one of: WithAsk, WithBid. func NewOrder(orderID uint64) *Order { diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 8383d1525e..c6a72cb21c 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -79,6 +79,132 @@ func TestOrderTypesAndBytes(t *testing.T) { assert.NoError(t, err, "checking for duplicate values") } +func TestContainsUint64(t *testing.T) { + tests := []struct { + name string + vals []uint64 + toFind uint64 + exp bool + }{ + { + name: "nil vals", + vals: nil, + toFind: 0, + exp: false, + }, + { + name: "empty vals", + vals: []uint64{}, + toFind: 0, + exp: false, + }, + { + name: "one val: same", + vals: []uint64{1}, + toFind: 1, + exp: true, + }, + { + name: "one val: different", + vals: []uint64{1}, + toFind: 2, + exp: false, + }, + { + name: "three vals: not found", + vals: []uint64{1, 2, 3}, + toFind: 0, + exp: false, + }, + { + name: "three vals: first", + vals: []uint64{1, 2, 3}, + toFind: 1, + exp: true, + }, + { + name: "three vals: second", + vals: []uint64{1, 2, 3}, + toFind: 2, + exp: true, + }, + { + name: "three vals: third", + vals: []uint64{1, 2, 3}, + toFind: 3, + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = ContainsUint64(tc.vals, tc.toFind) + } + require.NotPanics(t, testFunc, "ContainsUint64(%q, %q)", tc.vals, tc.toFind) + assert.Equal(t, tc.exp, actual, "ContainsUint64(%q, %q)", tc.vals, tc.toFind) + }) + } +} + +func TestValidateOrderIDs(t *testing.T) { + tests := []struct { + name string + field string + orderIDs []uint64 + expErr string + }{ + { + name: "control", + field: "testfieldname", + orderIDs: []uint64{1, 18_446_744_073_709_551_615, 5, 65_536, 97}, + expErr: "", + }, + { + name: "nil slice", + field: "testfieldname", + orderIDs: nil, + expErr: "no testfieldname order ids provided", + }, + { + name: "empty slice", + field: "testfieldname", + orderIDs: []uint64{}, + expErr: "no testfieldname order ids provided", + }, + { + name: "contains a zero", + field: "testfieldname", + orderIDs: []uint64{1, 18_446_744_073_709_551_615, 0, 5, 65_536, 97}, + expErr: "invalid testfieldname order ids: cannot contain order id zero", + }, + { + name: "one duplicate entry", + field: "testfieldname", + orderIDs: []uint64{1, 2, 3, 1, 4, 5}, + expErr: "duplicate testfieldname order ids provided: [1]", + }, + { + name: "three duplicate entries", + field: "testfieldname", + orderIDs: []uint64{1, 2, 3, 1, 4, 5, 5, 6, 7, 3, 8, 9}, + expErr: "duplicate testfieldname order ids provided: [1 5 3]", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = ValidateOrderIDs(tc.field, tc.orderIDs) + } + require.NotPanics(t, testFunc, "ValidateOrderIDs(%q, %v)", tc.field, tc.orderIDs) + assertions.AssertErrorValue(t, err, tc.expErr, "ValidateOrderIDs(%q, %v)", tc.field, tc.orderIDs) + }) + } +} + func TestNewOrder(t *testing.T) { tests := []struct { name string From 5f94a69733cfef756915991d9ef24bd21492679b Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 18:50:17 -0600 Subject: [PATCH 132/309] [1658]: Implement FillBids. --- x/exchange/expected_keepers.go | 3 + x/exchange/keeper/keeper.go | 65 ++++++++-- x/exchange/keeper/msg_server.go | 8 +- x/exchange/keeper/orders.go | 215 +++++++++++++++++++++++++++++--- x/exchange/market.go | 2 +- x/exchange/orders.go | 14 +++ x/exchange/orders_test.go | 41 ++++++ 7 files changed, 322 insertions(+), 26 deletions(-) diff --git a/x/exchange/expected_keepers.go b/x/exchange/expected_keepers.go index 1b5a5cc5ed..a95d48cfa7 100644 --- a/x/exchange/expected_keepers.go +++ b/x/exchange/expected_keepers.go @@ -3,6 +3,8 @@ package exchange import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + attrtypes "github.com/provenance-io/provenance/x/attribute/types" ) @@ -20,6 +22,7 @@ type AttributeKeeper interface { type BankKeeper interface { SendCoins(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + InputOutputCoins(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) error } type NameKeeper interface { diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 52567aacd4..7ec5501a1d 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -11,6 +11,7 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/provenance-io/provenance/x/exchange" @@ -125,12 +126,8 @@ func (k Keeper) NormalizeReqAttrs(ctx sdk.Context, reqAttrs []string) ([]string, return rv, errors.Join(errs...) } -// CollectFee will transfer the fee amount to the market account, -// then the exchange's cut from the market to the fee collector. -func (k Keeper) CollectFee(ctx sdk.Context, payer sdk.AccAddress, marketID uint32, feeAmt sdk.Coins) error { - if feeAmt.IsZero() { - return nil - } +// CalculateExchangeSplit calculates the amount that the exchange will keep of the provided fee. +func (k Keeper) CalculateExchangeSplit(ctx sdk.Context, feeAmt sdk.Coins) sdk.Coins { exchangeAmt := make(sdk.Coins, 0, len(feeAmt)) for _, coin := range feeAmt { if coin.Amount.IsZero() { @@ -150,13 +147,65 @@ func (k Keeper) CollectFee(ctx sdk.Context, payer sdk.AccAddress, marketID uint3 } exchangeAmt = append(exchangeAmt, sdk.Coin{Denom: coin.Denom, Amount: splitAmt}) } + return exchangeAmt +} + +// CollectFee will transfer the fee amount to the market account, +// then the exchange's cut from the market to the fee collector. +// If you have fees to collect from multiple payers, consider using CollectFees. +func (k Keeper) CollectFee(ctx sdk.Context, payer sdk.AccAddress, marketID uint32, feeAmt sdk.Coins) error { + if feeAmt.IsZero() { + return nil + } + exchangeAmt := k.CalculateExchangeSplit(ctx, feeAmt) marketAddr := exchange.GetMarketAddress(marketID) if err := k.bankKeeper.SendCoins(ctx, payer, marketAddr, feeAmt); err != nil { return fmt.Errorf("error transferring %s from %s to market %d: %w", feeAmt, payer, marketID, err) } - if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, marketAddr, k.feeCollectorName, exchangeAmt); err != nil { - return fmt.Errorf("error collecting exchange fee %s (based off %s) from market %d: %w", exchangeAmt, feeAmt, marketID, err) + if !exchangeAmt.IsZero() { + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, marketAddr, k.feeCollectorName, exchangeAmt); err != nil { + return fmt.Errorf("error collecting exchange fee %s (based off %s) from market %d: %w", exchangeAmt, feeAmt, marketID, err) + } + } + + return nil +} + +// CollectFees will transfer the inputs to the market account, +// then the exchange's cut from the market to the fee collector. +// If there is only one input, CollectFee is used. +func (k Keeper) CollectFees(ctx sdk.Context, inputs []banktypes.Input, marketID uint32) error { + if len(inputs) == 0 { + return nil + } + if len(inputs) == 1 { + payer, err := sdk.AccAddressFromBech32(inputs[0].Address) + if err != nil { + return fmt.Errorf("invalid payer address %q: %w", inputs[0].Address, err) + } + return k.CollectFee(ctx, payer, marketID, inputs[0].Coins) + } + + var feeAmt sdk.Coins + for _, input := range inputs { + feeAmt = feeAmt.Add(input.Coins...) + } + if feeAmt.IsZero() { + return nil + } + + exchangeAmt := k.CalculateExchangeSplit(ctx, feeAmt) + + marketAddr := exchange.GetMarketAddress(marketID) + outputs := []banktypes.Output{{Address: marketAddr.String(), Coins: feeAmt}} + if err := k.bankKeeper.InputOutputCoins(ctx, inputs, outputs); err != nil { + return fmt.Errorf("error collecting fees for market %d: %w", marketID, err) + } + if !exchangeAmt.IsZero() { + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, marketAddr, k.feeCollectorName, exchangeAmt); err != nil { + return fmt.Errorf("error collecting exchange fee %s (based off %s) from market %d: %w", exchangeAmt, feeAmt, marketID, err) + } } return nil diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index b4650011bf..6fa20b5aec 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -56,8 +56,12 @@ func (k MsgServer) CancelOrder(goCtx context.Context, msg *exchange.MsgCancelOrd // FillBids uses the assets in your account to fulfill one or more bids (similar to a fill-or-cancel ask). func (k MsgServer) FillBids(goCtx context.Context, msg *exchange.MsgFillBidsRequest) (*exchange.MsgFillBidsResponse, error) { - // TODO[1658]: Implement FillBids - panic("not implemented") + ctx := sdk.UnwrapSDKContext(goCtx) + err := k.Keeper.FillBids(ctx, msg) + if err != nil { + return nil, err + } + return &exchange.MsgFillBidsResponse{}, nil } // FillAsks uses the funds in your account to fulfill one or more asks (similar to a fill-or-cancel bid). diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 1a7d3ad53f..c0d12bd838 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -1,10 +1,12 @@ package keeper import ( + "errors" "fmt" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/provenance-io/provenance/x/exchange" ) @@ -100,6 +102,16 @@ func createIndexEntries(order exchange.Order) []sdk.KVPair { return rv } +func (k Keeper) getOrderFromStore(store sdk.KVStore, orderID uint64) (*exchange.Order, error) { + key := MakeKeyOrder(orderID) + value := store.Get(key) + rv, err := k.parseOrderStoreValue(orderID, value) + if err != nil { + return nil, fmt.Errorf("failed to read order %d: %w", orderID, err) + } + return rv, nil +} + // setOrderInStore writes an order to the store (along with all its indexes). func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { key, value, err := k.getOrderStoreKeyValue(order) @@ -121,9 +133,8 @@ func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { return nil } -// deleteOrder deletes an order (along with its indexes). -func (k Keeper) deleteOrder(ctx sdk.Context, order exchange.Order) { - store := k.getStore(ctx) +// deleteAndDeIndexOrder deletes an order from the store along with its indexes. +func deleteAndDeIndexOrder(store sdk.KVStore, order exchange.Order) { key := MakeKeyOrder(order.OrderId) store.Delete(key) indexEntries := createIndexEntries(order) @@ -132,16 +143,14 @@ func (k Keeper) deleteOrder(ctx sdk.Context, order exchange.Order) { } } +// deleteOrder deletes an order (along with its indexes). +func (k Keeper) deleteOrder(ctx sdk.Context, order exchange.Order) { + deleteAndDeIndexOrder(k.getStore(ctx), order) +} + // GetOrder gets an order. Returns nil, nil if the order does not exist. func (k Keeper) GetOrder(ctx sdk.Context, orderID uint64) (*exchange.Order, error) { - store := k.getStore(ctx) - key := MakeKeyOrder(orderID) - value := store.Get(key) - rv, err := k.parseOrderStoreValue(orderID, value) - if err != nil { - return nil, fmt.Errorf("failed to read order %d: %w", orderID, err) - } - return rv, nil + return k.getOrderFromStore(k.getStore(ctx), orderID) } // getNextOrderID gets the next available order id from the store. @@ -229,13 +238,16 @@ func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, crea return 0, err } - seller := sdk.MustAccAddressFromBech32(askOrder.Seller) - marketID := askOrder.MarketId store := k.getStore(ctx) + marketID := askOrder.MarketId if err := validateMarketExists(store, marketID); err != nil { return 0, err } + if !isMarketActive(store, marketID) { + return 0, fmt.Errorf("market %d is not accepting orders", marketID) + } + seller := sdk.MustAccAddressFromBech32(askOrder.Seller) if !k.CanCreateAsk(ctx, marketID, seller) { return 0, fmt.Errorf("account %s is not allowed to create ask orders in market %d", seller, marketID) } @@ -276,13 +288,16 @@ func (k Keeper) CreateBidOrder(ctx sdk.Context, bidOrder exchange.BidOrder, crea return 0, err } - buyer := sdk.MustAccAddressFromBech32(bidOrder.Buyer) - marketID := bidOrder.MarketId store := k.getStore(ctx) + marketID := bidOrder.MarketId if err := validateMarketExists(store, marketID); err != nil { return 0, err } + if !isMarketActive(store, marketID) { + return 0, fmt.Errorf("market %d is not accepting orders", marketID) + } + buyer := sdk.MustAccAddressFromBech32(bidOrder.Buyer) if !k.CanCreateBid(ctx, marketID, buyer) { return 0, fmt.Errorf("account %s is not allowed to create bid orders in market %d", buyer, marketID) } @@ -340,3 +355,173 @@ func (k Keeper) CancelOrder(ctx sdk.Context, orderID uint64, signer string) erro return nil } + +// FillBids settles one or more bid orders for a seller. +func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) error { + if err := msg.ValidateBasic(); err != nil { + return err + } + + marketID := msg.MarketId + store := k.getStore(ctx) + + if err := validateMarketExists(store, marketID); err != nil { + return err + } + if !isMarketActive(store, marketID) { + return fmt.Errorf("market %d is not accepting orders", marketID) + } + if !isUserSettlementAllowed(store, marketID) { + return fmt.Errorf("market %d does not allow user settlement", marketID) + } + seller := sdk.MustAccAddressFromBech32(msg.Seller) + if !k.CanCreateAsk(ctx, marketID, seller) { + return fmt.Errorf("account %s is not allowed to create ask orders in market %d", seller, marketID) + } + if err := validateCreateAskFlatFee(store, marketID, msg.AskOrderCreationFee); err != nil { + return err + } + if err := validateSellerSettlementFlatFee(store, marketID, msg.SellerSettlementFlatFee); err != nil { + return err + } + + var errs []error + orders := make([]*exchange.Order, 0, len(msg.BidOrderIds)) + var totalAssets, totalPrice, totalSellerFee sdk.Coins + assetOutputs := make([]banktypes.Output, 0, len(msg.BidOrderIds)) + priceInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)) + addrIndex := make(map[string]int) + feeInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)+1) + feeAddrIndex := make(map[string]int) + for _, orderID := range msg.BidOrderIds { + order, oerr := k.getOrderFromStore(store, orderID) + if oerr != nil { + errs = append(errs, oerr) + continue + } + if order == nil { + errs = append(errs, fmt.Errorf("order %d not found", orderID)) + continue + } + if !order.IsBidOrder() { + errs = append(errs, fmt.Errorf("order %d is type %s: expected bid", orderID, order.GetOrderType())) + continue + } + + bidOrder := order.GetBidOrder() + orderMarketID := bidOrder.MarketId + buyer := bidOrder.Buyer + assets := bidOrder.Assets + price := bidOrder.Price + heldAmount := bidOrder.GetHoldAmount() + buyerSettlementFees := bidOrder.BuyerSettlementFees + + if orderMarketID != marketID { + errs = append(errs, fmt.Errorf("order %d market id %d does not equal requested market id %d", orderID, orderMarketID, marketID)) + continue + } + if buyer == msg.Seller { + errs = append(errs, fmt.Errorf("order %d has the same buyer %s as the requested seller", orderID, buyer)) + continue + } + sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, price) + if rerr != nil { + errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", orderID, rerr)) + continue + } + buyerAddr, aerr := sdk.AccAddressFromBech32(buyer) + if aerr != nil { + errs = append(errs, fmt.Errorf("invalid buyer %q in order %d: %w", buyer, orderID, aerr)) + continue + } + if err := k.holdKeeper.ReleaseHold(ctx, buyerAddr, heldAmount); err != nil { + errs = append(errs, fmt.Errorf("error releasing hold for order %d: %w", orderID, err)) + continue + } + + orders = append(orders, order) + totalAssets = totalAssets.Add(assets...) + totalPrice = totalPrice.Add(price) + if sellerRatioFee != nil { + totalSellerFee = totalSellerFee.Add(*sellerRatioFee) + } + + i, seen := addrIndex[buyer] + if !seen { + i = len(assetOutputs) + addrIndex[buyer] = i + assetOutputs = append(assetOutputs, banktypes.Output{Address: buyer}) + priceInputs = append(priceInputs, banktypes.Input{Address: buyer}) + } + assetOutputs[i].Coins = assetOutputs[i].Coins.Add(assets...) + priceInputs[i].Coins = priceInputs[i].Coins.Add(price) + + if !buyerSettlementFees.IsZero() { + j, known := feeAddrIndex[buyer] + if !known { + j = len(feeInputs) + feeAddrIndex[buyer] = j + feeInputs = append(feeInputs, banktypes.Input{Address: buyer}) + } + feeInputs[j].Coins = feeInputs[j].Coins.Add(buyerSettlementFees...) + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + + if !safeCoinsEquals(totalAssets, msg.TotalAssets) { + return fmt.Errorf("total assets %q does not equal sum of bid order assets %q", msg.TotalAssets, totalAssets) + } + + if msg.SellerSettlementFlatFee != nil { + totalSellerFee = totalSellerFee.Add(*msg.SellerSettlementFlatFee) + } + if !totalSellerFee.IsZero() { + feeInputs = append(feeInputs, banktypes.Input{Address: msg.Seller, Coins: totalSellerFee}) + } + + assetInputs := []banktypes.Input{{Address: msg.Seller, Coins: msg.TotalAssets}} + priceOutputs := []banktypes.Output{{Address: msg.Seller, Coins: totalPrice}} + + if err := k.bankKeeper.InputOutputCoins(ctx, assetInputs, assetOutputs); err != nil { + return fmt.Errorf("error transferring assets from seller to buyers: %w", err) + } + + if err := k.bankKeeper.InputOutputCoins(ctx, priceInputs, priceOutputs); err != nil { + return fmt.Errorf("error transferring price from buyers to seller: %w", err) + } + + if err := k.CollectFees(ctx, feeInputs, marketID); err != nil { + return fmt.Errorf("error collecting settlement fees: %w", err) + } + + // Collected last so that it's easier for a seller to fill bids without needing those funds first. + // Collected separately so it's not combined with the seller settlement fees in the events. + if msg.AskOrderCreationFee != nil { + if err := k.CollectFee(ctx, seller, marketID, sdk.Coins{*msg.AskOrderCreationFee}); err != nil { + return fmt.Errorf("error collecting create-ask fee %q: %w", msg.AskOrderCreationFee, err) + } + } + + for _, order := range orders { + deleteAndDeIndexOrder(store, *order) + } + + return nil +} + +// safeCoinsEquals returns true if the two provided coins are equal. +// Returns false instead of panicking like sdk.Coins.IsEqual. +func safeCoinsEquals(a, b sdk.Coins) (isEqual bool) { + // The sdk.Coins.IsEqual function will panic if a and b have the same number of entries, but different denoms. + // Really, that stuff is all pretty panic happy. + // In here, we don't really care why it panics, but if it does, they're not equal. + defer func() { + if r := recover(); r != nil { + isEqual = false + } + }() + return a.IsEqual(b) +} diff --git a/x/exchange/market.go b/x/exchange/market.go index d249a6bdec..7b6ae97afe 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -306,7 +306,7 @@ func ValidateDisjointFeeRatios(field string, toAdd, toRemove []FeeRatio) error { // CoinEquals returns true if the two provided coin entries are equal. // Designed for use with intersection. // -// We can't just provide sdk.Coin.isEqual to intersection because that PANICS if the denoms are different. +// We can't just provide sdk.Coin.IsEqual to intersection because that PANICS if the denoms are different. // And we can't provide sdk.Coin.Equal to intersection because it takes in an interface{} (instead of sdk.Coin). func CoinEquals(a, b sdk.Coin) bool { return a.Equal(b) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 61988d0f57..2a64893e15 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -158,6 +158,20 @@ func (o Order) GetAssets() sdk.Coins { } } +// GetPrice gets the price in this order. +func (o Order) GetPrice() sdk.Coin { + switch v := o.GetOrder().(type) { + case *Order_AskOrder: + return v.AskOrder.Price + case *Order_BidOrder: + return v.BidOrder.Price + default: + // If GetPrice() is called without the order being set yet, it's a programming error, so panic. + // If it's a type without a case, the case needs to be added, so panic. + panic(fmt.Sprintf("GetPrice() missing case for %T", v)) + } +} + // GetHoldAmount returns the total amount that should be on hold for this order. func (o Order) GetHoldAmount() sdk.Coins { switch v := o.GetOrder().(type) { diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index c6a72cb21c..114d57a0cd 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -610,6 +610,47 @@ func TestOrder_GetAssets(t *testing.T) { } } +func TestOrder_GetPrice(t *testing.T) { + tests := []struct { + name string + order *Order + expected sdk.Coin + expPanic string + }{ + { + name: "AskOrder", + order: NewOrder(1).WithAsk(&AskOrder{Price: sdk.NewInt64Coin("acorns", 85)}), + expected: sdk.NewInt64Coin("acorns", 85), + }, + { + name: "BidOrder", + order: NewOrder(2).WithBid(&BidOrder{Price: sdk.NewInt64Coin("boogers", 3)}), + expected: sdk.NewInt64Coin("boogers", 3), + }, + { + name: "nil inside order", + order: NewOrder(3), + expPanic: "GetAssets() missing case for ", + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + expPanic: "GetAssets() missing case for *exchange.unknownOrderType", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var price sdk.Coin + testFunc := func() { + price = tc.order.GetPrice() + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetPrice") + assert.Equal(t, tc.expected.String(), price.String(), "GetPrice result") + }) + } +} + func TestOrder_GetHoldAmount(t *testing.T) { tests := []struct { name string From af18063651b532f72e39f00eaf6116f3ca49265d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 18:57:47 -0600 Subject: [PATCH 133/309] [1658]: Fill in the MsgFillAsksRequest fields. --- docs/proto-docs.md | 14 +- proto/provenance/exchange/v1/tx.proto | 25 +- x/exchange/tx.pb.go | 608 +++++++++++++++++++++----- 3 files changed, 535 insertions(+), 112 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index e47dcff42a..c3ff991d09 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2215,6 +2215,16 @@ MsgCreateBidResponse is a response message for the CreateBid endpoint. MsgFillAsksRequest is a request message for the FillAsks endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `buyer` | [string](#string) | | buyer is the address of the account attempting to buy some assets. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market with the asks to fill. All ask orders being filled must be in this market. | +| `total_price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | total_price is the total amount being spent on some assets. It must be the sum of all ask order prices. | +| `ask_order_ids` | [uint64](#uint64) | repeated | ask_order_ids are the ids of the ask orders that you are trying to fill. All ids must be for ask orders, and must be in the same market as the market_id. | +| `buyer_settlement_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) for this settlement. | +| `bid_order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | bid_order_creation_fee is the fee that is being paid to create this order (which is immediately then settled). | + + @@ -2241,8 +2251,8 @@ MsgFillBidsRequest is a request message for the FillBids endpoint. | `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market with the bids to fill. All bid orders being filled must be in this market. | | `total_assets` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | total_assets are the things that the seller wishes to sell. It must be the sum of all bid order assets. | | `bid_order_ids` | [uint64](#uint64) | repeated | bid_order_ids are the ids of the bid orders that you are trying to fill. All ids must be for bid orders, and must be in the same market as the market_id. | -| `seller_settlement_flat_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. | -| `ask_order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | ask_order_creation_fee is the fee that is being paid to create this order. | +| `seller_settlement_flat_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | seller_settlement_flat_fee is the flat fee for sellers that will be charged for this settlement. | +| `ask_order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | ask_order_creation_fee is the fee that is being paid to create this order (which is immediately then settled). | diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 43c18e0602..dc7ba685df 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -125,9 +125,9 @@ message MsgFillBidsRequest { // bid_order_ids are the ids of the bid orders that you are trying to fill. // All ids must be for bid orders, and must be in the same market as the market_id. repeated uint64 bid_order_ids = 4; - // seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. + // seller_settlement_flat_fee is the flat fee for sellers that will be charged for this settlement. cosmos.base.v1beta1.Coin seller_settlement_flat_fee = 5; - // ask_order_creation_fee is the fee that is being paid to create this order. + // ask_order_creation_fee is the fee that is being paid to create this order (which is immediately then settled). cosmos.base.v1beta1.Coin ask_order_creation_fee = 6; } @@ -136,7 +136,26 @@ message MsgFillBidsResponse {} // MsgFillAsksRequest is a request message for the FillAsks endpoint. message MsgFillAsksRequest { - // TODO[1658]: MsgFillAsksRequest + option (cosmos.msg.v1.signer) = "buyer"; + + // buyer is the address of the account attempting to buy some assets. + string buyer = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market_id is the numerical identifier of the market with the asks to fill. + // All ask orders being filled must be in this market. + uint32 market_id = 2; + // total_price is the total amount being spent on some assets. + // It must be the sum of all ask order prices. + repeated cosmos.base.v1beta1.Coin total_price = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + // ask_order_ids are the ids of the ask orders that you are trying to fill. + // All ids must be for ask orders, and must be in the same market as the market_id. + repeated uint64 ask_order_ids = 4; + // buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) + // for this settlement. + repeated cosmos.base.v1beta1.Coin buyer_settlement_fees = 5 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + // bid_order_creation_fee is the fee that is being paid to create this order (which is immediately then settled). + cosmos.base.v1beta1.Coin bid_order_creation_fee = 6; } // MsgFillAsksResponse is a response message for the FillAsks endpoint. diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 44ecae1de5..c20972a501 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -341,9 +341,9 @@ type MsgFillBidsRequest struct { // bid_order_ids are the ids of the bid orders that you are trying to fill. // All ids must be for bid orders, and must be in the same market as the market_id. BidOrderIds []uint64 `protobuf:"varint,4,rep,packed,name=bid_order_ids,json=bidOrderIds,proto3" json:"bid_order_ids,omitempty"` - // seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. + // seller_settlement_flat_fee is the flat fee for sellers that will be charged for this settlement. SellerSettlementFlatFee *types.Coin `protobuf:"bytes,5,opt,name=seller_settlement_flat_fee,json=sellerSettlementFlatFee,proto3" json:"seller_settlement_flat_fee,omitempty"` - // ask_order_creation_fee is the fee that is being paid to create this order. + // ask_order_creation_fee is the fee that is being paid to create this order (which is immediately then settled). AskOrderCreationFee *types.Coin `protobuf:"bytes,6,opt,name=ask_order_creation_fee,json=askOrderCreationFee,proto3" json:"ask_order_creation_fee,omitempty"` } @@ -461,6 +461,22 @@ var xxx_messageInfo_MsgFillBidsResponse proto.InternalMessageInfo // MsgFillAsksRequest is a request message for the FillAsks endpoint. type MsgFillAsksRequest struct { + // buyer is the address of the account attempting to buy some assets. + Buyer string `protobuf:"bytes,1,opt,name=buyer,proto3" json:"buyer,omitempty"` + // market_id is the numerical identifier of the market with the asks to fill. + // All ask orders being filled must be in this market. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // total_price is the total amount being spent on some assets. + // It must be the sum of all ask order prices. + TotalPrice github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=total_price,json=totalPrice,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"total_price"` + // ask_order_ids are the ids of the ask orders that you are trying to fill. + // All ids must be for ask orders, and must be in the same market as the market_id. + AskOrderIds []uint64 `protobuf:"varint,4,rep,packed,name=ask_order_ids,json=askOrderIds,proto3" json:"ask_order_ids,omitempty"` + // buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) + // for this settlement. + BuyerSettlementFees github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,5,rep,name=buyer_settlement_fees,json=buyerSettlementFees,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"buyer_settlement_fees"` + // bid_order_creation_fee is the fee that is being paid to create this order (which is immediately then settled). + BidOrderCreationFee *types.Coin `protobuf:"bytes,6,opt,name=bid_order_creation_fee,json=bidOrderCreationFee,proto3" json:"bid_order_creation_fee,omitempty"` } func (m *MsgFillAsksRequest) Reset() { *m = MsgFillAsksRequest{} } @@ -496,6 +512,48 @@ func (m *MsgFillAsksRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MsgFillAsksRequest proto.InternalMessageInfo +func (m *MsgFillAsksRequest) GetBuyer() string { + if m != nil { + return m.Buyer + } + return "" +} + +func (m *MsgFillAsksRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *MsgFillAsksRequest) GetTotalPrice() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.TotalPrice + } + return nil +} + +func (m *MsgFillAsksRequest) GetAskOrderIds() []uint64 { + if m != nil { + return m.AskOrderIds + } + return nil +} + +func (m *MsgFillAsksRequest) GetBuyerSettlementFees() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.BuyerSettlementFees + } + return nil +} + +func (m *MsgFillAsksRequest) GetBidOrderCreationFee() *types.Coin { + if m != nil { + return m.BidOrderCreationFee + } + return nil +} + // MsgFillAsksResponse is a response message for the FillAsks endpoint. type MsgFillAsksResponse struct { } @@ -1709,111 +1767,116 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1658 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcf, 0x6f, 0xdc, 0xc4, - 0x17, 0xcf, 0xe6, 0x57, 0xb3, 0x2f, 0x4d, 0xda, 0x4e, 0x7e, 0x6d, 0xdc, 0x66, 0xb3, 0xdd, 0x7c, - 0xbf, 0x52, 0x69, 0xc9, 0x6e, 0x12, 0x44, 0x81, 0x88, 0x4b, 0x36, 0x65, 0xa3, 0x1e, 0x02, 0x95, - 0x4b, 0x41, 0x82, 0x83, 0x35, 0xbb, 0x9e, 0x3a, 0x56, 0xbc, 0x9e, 0xc4, 0x33, 0x49, 0xd3, 0x2b, - 0x42, 0xe2, 0x84, 0xd4, 0x23, 0x57, 0xc4, 0x09, 0x84, 0x04, 0x48, 0x5c, 0xe0, 0x2f, 0xa8, 0x10, - 0x87, 0x8a, 0x13, 0x27, 0x40, 0xed, 0x81, 0x3f, 0x81, 0x2b, 0xf2, 0xcc, 0xd8, 0xb1, 0x77, 0xed, - 0xb5, 0x37, 0x34, 0xa7, 0x64, 0x3c, 0xef, 0x7d, 0xde, 0xe7, 0xf3, 0xe6, 0xe7, 0x9b, 0x85, 0xe5, - 0x03, 0x8f, 0x1e, 0x13, 0x17, 0xbb, 0x6d, 0x52, 0x27, 0x27, 0xed, 0x3d, 0xec, 0x5a, 0xa4, 0x7e, - 0xbc, 0x5e, 0xe7, 0x27, 0xb5, 0x03, 0x8f, 0x72, 0x8a, 0xe6, 0x4f, 0x0d, 0x6a, 0x81, 0x41, 0xed, - 0x78, 0x5d, 0x2b, 0xb7, 0x29, 0xeb, 0x50, 0x56, 0x6f, 0x61, 0xe6, 0x3b, 0xb4, 0x08, 0xc7, 0xeb, - 0xf5, 0x36, 0xb5, 0x5d, 0xe9, 0xa7, 0x2d, 0xa8, 0xfe, 0x0e, 0xb3, 0x7c, 0xbc, 0x0e, 0xb3, 0x54, - 0xc7, 0xa2, 0xec, 0x30, 0x44, 0xab, 0x2e, 0x1b, 0xaa, 0x6b, 0xd6, 0xa2, 0x16, 0x95, 0xdf, 0xfd, - 0xff, 0xd4, 0xd7, 0x95, 0x14, 0x8a, 0x1d, 0xec, 0xed, 0x13, 0x9e, 0x61, 0x44, 0x3d, 0x93, 0x78, - 0x2c, 0xc3, 0xe8, 0x00, 0x7b, 0xb8, 0xa3, 0x8c, 0xaa, 0x3f, 0x17, 0x60, 0x66, 0x97, 0x59, 0xdb, - 0x1e, 0xc1, 0x9c, 0x6c, 0xb1, 0x7d, 0x9d, 0x1c, 0x1e, 0x11, 0xc6, 0xd1, 0x36, 0x14, 0x31, 0xdb, - 0x37, 0x04, 0x60, 0xa9, 0x50, 0x29, 0xdc, 0x98, 0xdc, 0xa8, 0xd4, 0x92, 0x93, 0x53, 0xdb, 0x62, - 0xfb, 0xef, 0xf9, 0x76, 0x8d, 0xd1, 0xa7, 0x7f, 0x2c, 0x0f, 0xe9, 0x13, 0x58, 0xb5, 0xd1, 0x0e, - 0x20, 0x01, 0x60, 0xb4, 0x7d, 0x78, 0x9b, 0xba, 0xc6, 0x43, 0x42, 0x4a, 0xc3, 0x02, 0x6d, 0xb1, - 0xa6, 0x92, 0xe1, 0xa7, 0xb4, 0xa6, 0x52, 0x5a, 0xdb, 0xa6, 0xb6, 0xab, 0x5f, 0x16, 0x4e, 0xdb, - 0xca, 0xa7, 0x49, 0xc8, 0xe6, 0xdc, 0x27, 0x7f, 0x7f, 0x7f, 0xf3, 0x72, 0x48, 0xa8, 0xc6, 0x88, - 0xe3, 0x10, 0xaf, 0xba, 0x0e, 0xb3, 0x71, 0xee, 0xec, 0x80, 0xba, 0x8c, 0xa0, 0x45, 0x98, 0x90, - 0x71, 0x6d, 0x53, 0x70, 0x1f, 0xd5, 0x2f, 0x88, 0xf6, 0x5d, 0xb3, 0xfa, 0x53, 0x54, 0x6f, 0xc3, - 0x36, 0x23, 0x7a, 0x5b, 0xb6, 0x99, 0x4f, 0x6f, 0xc3, 0x36, 0x63, 0x7a, 0x5b, 0xaa, 0xfd, 0xf2, - 0xf4, 0xce, 0xfa, 0x7a, 0x2f, 0x85, 0x84, 0x6a, 0xad, 0xa3, 0xc7, 0x5d, 0x72, 0x05, 0xf5, 0x6c, - 0xb9, 0x87, 0x30, 0xe7, 0xbb, 0xf8, 0x12, 0x1c, 0xc1, 0x31, 0xd0, 0xbb, 0x06, 0xe3, 0xcc, 0xb6, - 0x5c, 0x25, 0xb6, 0xd8, 0x28, 0xfd, 0xf6, 0xe3, 0xea, 0xac, 0x62, 0xb8, 0x65, 0x9a, 0x1e, 0x61, - 0xec, 0x3e, 0xf7, 0x6c, 0xd7, 0xd2, 0x95, 0x5d, 0x2c, 0xca, 0x70, 0x2c, 0xca, 0xe6, 0xa4, 0x4f, - 0x57, 0xd9, 0x55, 0x4b, 0x30, 0xdf, 0x1d, 0x52, 0xf2, 0xac, 0x7e, 0x3b, 0x02, 0x68, 0x97, 0x59, - 0x4d, 0xdb, 0x71, 0x1a, 0xb6, 0xc9, 0xa2, 0x54, 0xc4, 0x78, 0xe6, 0xa0, 0x22, 0xec, 0xd0, 0x55, - 0x28, 0xca, 0xe5, 0x10, 0x70, 0x99, 0xd2, 0x27, 0xe4, 0x87, 0xbb, 0x26, 0x72, 0xe1, 0x22, 0xa7, - 0x1c, 0x3b, 0x06, 0x66, 0x8c, 0x70, 0x56, 0x1a, 0xa9, 0x8c, 0xf4, 0x4d, 0x7f, 0x63, 0xcd, 0x1f, - 0xc5, 0x6f, 0xfe, 0x5c, 0xbe, 0x61, 0xd9, 0x7c, 0xef, 0xa8, 0x55, 0x6b, 0xd3, 0x8e, 0x5a, 0xa8, - 0xea, 0xcf, 0x2a, 0x33, 0xf7, 0xeb, 0xfc, 0xf1, 0x01, 0x61, 0xc2, 0x81, 0xe9, 0x93, 0x22, 0xc0, - 0x96, 0xc0, 0x47, 0x55, 0x98, 0x0a, 0x07, 0xca, 0xb0, 0x4d, 0x56, 0x1a, 0xad, 0x8c, 0xdc, 0x18, - 0xd5, 0x27, 0x83, 0x59, 0x71, 0xd7, 0x64, 0xe8, 0x03, 0xd0, 0x24, 0x75, 0x83, 0x11, 0xce, 0x1d, - 0xd2, 0x21, 0x2e, 0x37, 0x1e, 0x3a, 0x98, 0x8b, 0x09, 0x32, 0x96, 0x35, 0x41, 0x16, 0xa4, 0xf3, - 0xfd, 0xd0, 0xb7, 0xe9, 0x60, 0xde, 0x24, 0x04, 0xbd, 0x0b, 0xf3, 0xe1, 0xa2, 0x88, 0x4f, 0xba, - 0xf1, 0x2c, 0xcc, 0x99, 0x60, 0x95, 0x46, 0xe7, 0x9d, 0x1a, 0x48, 0xb9, 0xba, 0xe6, 0xc4, 0x4a, - 0x39, 0x1d, 0x2d, 0x35, 0x8a, 0xb3, 0xe1, 0x20, 0x6e, 0xb1, 0xfd, 0x60, 0x10, 0x23, 0xc6, 0xf2, - 0xab, 0x32, 0x3e, 0x14, 0x93, 0x61, 0x57, 0x8c, 0x8d, 0xa4, 0x1f, 0x8c, 0x7a, 0x0d, 0xc6, 0xb0, - 0xd9, 0xb1, 0xdd, 0xcc, 0x41, 0x97, 0x66, 0x7d, 0xc7, 0x7c, 0x13, 0x7c, 0xde, 0xd2, 0xb0, 0xba, - 0x08, 0x0b, 0x3d, 0x21, 0x15, 0x9b, 0x7f, 0x0a, 0x50, 0x0a, 0xfb, 0x3e, 0xb4, 0xf9, 0x9e, 0xe9, - 0xe1, 0x47, 0xe7, 0x41, 0x08, 0x2d, 0x01, 0x70, 0x6a, 0x60, 0xe9, 0x57, 0x1a, 0xf1, 0x11, 0xf5, - 0x22, 0xa7, 0x0a, 0x08, 0xb5, 0x61, 0x1c, 0x77, 0xe8, 0x91, 0xcb, 0xc5, 0x64, 0x79, 0xc9, 0xb3, - 0x53, 0x41, 0xc7, 0x92, 0x72, 0x15, 0x16, 0x13, 0x84, 0xab, 0xb4, 0xfc, 0x5a, 0x80, 0xa5, 0xb0, - 0xf7, 0xc1, 0x81, 0x89, 0x39, 0xb9, 0x43, 0x38, 0xb6, 0x1d, 0x76, 0x2e, 0xb9, 0xd1, 0x61, 0x5a, - 0x75, 0x9a, 0x32, 0x8a, 0xc8, 0xcf, 0xe4, 0xc6, 0xff, 0xd3, 0xf6, 0x5b, 0x49, 0x4c, 0x51, 0x52, - 0x9b, 0xee, 0x54, 0x27, 0xfa, 0x31, 0xa6, 0xb5, 0x02, 0xe5, 0x34, 0x35, 0x4a, 0xf0, 0x57, 0xbd, - 0x82, 0xdf, 0x71, 0x71, 0xcb, 0x21, 0xe6, 0xb9, 0x08, 0x7e, 0x05, 0x2e, 0xe3, 0x76, 0x9b, 0x1c, - 0x70, 0xdb, 0xb5, 0xe4, 0x5a, 0x95, 0x92, 0x27, 0xf4, 0x4b, 0xe1, 0x77, 0xb1, 0x14, 0xb3, 0x74, - 0x84, 0x24, 0x95, 0x8e, 0xef, 0x0a, 0x50, 0xe9, 0x32, 0x79, 0xc0, 0x82, 0x7d, 0xe2, 0x5c, 0xa4, - 0x6c, 0xc0, 0x1c, 0x76, 0x1c, 0xfa, 0xc8, 0x38, 0x62, 0xb1, 0xcd, 0x4c, 0xe9, 0x99, 0x11, 0x9d, - 0xa7, 0x1c, 0xfc, 0xae, 0x98, 0xa6, 0x15, 0xb8, 0xde, 0x87, 0xb0, 0x92, 0xf5, 0xf5, 0x70, 0xc4, - 0x6a, 0x17, 0xbb, 0xd8, 0x22, 0xf7, 0x88, 0xd7, 0xb1, 0x19, 0xb3, 0xa9, 0xcb, 0xce, 0x6b, 0xbd, - 0x7a, 0xe4, 0x98, 0xee, 0x13, 0x03, 0x3b, 0x8e, 0x38, 0x32, 0x8a, 0x7a, 0x51, 0x7e, 0xd9, 0x72, - 0x1c, 0xd4, 0x84, 0x22, 0xa7, 0x86, 0x6c, 0xab, 0x25, 0xbb, 0x92, 0x7a, 0x1b, 0x6a, 0xb7, 0x09, - 0x63, 0x3b, 0x1e, 0x76, 0x79, 0x70, 0x41, 0xe0, 0x54, 0x17, 0xae, 0xe8, 0x0e, 0x4c, 0x70, 0x6a, - 0x58, 0x7e, 0x5f, 0x69, 0x6c, 0x50, 0x98, 0x0b, 0x9c, 0x8a, 0x66, 0x2c, 0xa1, 0xff, 0x83, 0x6a, - 0xbf, 0x54, 0xa9, 0x8c, 0xfe, 0x30, 0x1c, 0x99, 0x4b, 0xd2, 0x4c, 0x27, 0x87, 0x5b, 0x9c, 0x7b, - 0xec, 0x9c, 0x66, 0xfc, 0x15, 0x71, 0x1a, 0x11, 0xc3, 0x3f, 0x9e, 0xe4, 0x4e, 0xa8, 0xb2, 0x3a, - 0xdd, 0x0e, 0xae, 0x6b, 0xef, 0xfb, 0xdb, 0x21, 0xaa, 0xc3, 0x6c, 0xdc, 0xd4, 0x23, 0x1d, 0x7a, - 0x2c, 0xb3, 0x5c, 0xd4, 0xaf, 0x44, 0xac, 0x75, 0xd1, 0x11, 0xc1, 0xf6, 0x8f, 0x5d, 0x85, 0x3d, - 0x16, 0xc5, 0x6e, 0xd8, 0x66, 0x37, 0xb6, 0x32, 0x55, 0xd8, 0xe3, 0x51, 0x6c, 0x61, 0x2d, 0xb1, - 0x63, 0x99, 0xbd, 0x0e, 0xcb, 0xa9, 0x29, 0x53, 0x69, 0xfd, 0xb2, 0x20, 0xb6, 0xd5, 0x1d, 0x7a, - 0x2c, 0x2f, 0x65, 0xd2, 0x38, 0xc8, 0xe8, 0x6d, 0x28, 0xe2, 0x23, 0xbe, 0x47, 0x3d, 0x9b, 0x3f, - 0xce, 0xcc, 0xea, 0xa9, 0x29, 0x7a, 0x1b, 0xc6, 0x65, 0x22, 0xd5, 0xcd, 0xb1, 0xdc, 0x7f, 0x5f, - 0x54, 0xb3, 0x43, 0xf9, 0x6c, 0x4e, 0xfb, 0x12, 0x4e, 0xd1, 0xaa, 0xd7, 0x40, 0x4b, 0xa2, 0xa8, - 0x14, 0xfc, 0x02, 0xe2, 0xb4, 0xdc, 0xa1, 0xc7, 0x52, 0x62, 0x93, 0x10, 0xf6, 0x5f, 0xf9, 0xf7, - 0x9d, 0x19, 0x0f, 0x60, 0x01, 0x9b, 0xa6, 0x7f, 0x45, 0x31, 0x22, 0xc3, 0xee, 0x5f, 0x85, 0xb2, - 0x2f, 0x6a, 0x52, 0xe8, 0x0c, 0x36, 0xcd, 0x26, 0x21, 0xe1, 0xad, 0xdf, 0xbf, 0x0b, 0xa1, 0x8f, - 0x41, 0x93, 0x63, 0x9b, 0x88, 0x3c, 0x9a, 0x0f, 0x79, 0x5e, 0x42, 0xf4, 0x80, 0xf7, 0x72, 0xf6, - 0xa7, 0x93, 0x40, 0x1e, 0x3b, 0x03, 0xe7, 0x86, 0x6d, 0xa6, 0x73, 0x0e, 0x91, 0xc7, 0xcf, 0xc6, - 0x39, 0x00, 0x6f, 0x43, 0x39, 0xe0, 0x9c, 0x7c, 0xf3, 0x2c, 0x5d, 0xc8, 0x17, 0x40, 0x93, 0xd4, - 0xef, 0x27, 0xdc, 0x40, 0x91, 0x0d, 0xd7, 0x23, 0x0a, 0x52, 0xe2, 0x4c, 0xe4, 0x8b, 0xb3, 0x14, - 0x0a, 0x49, 0x0c, 0xe5, 0x42, 0x25, 0x5d, 0x8f, 0xe7, 0x5f, 0x60, 0x59, 0xa9, 0x28, 0x22, 0xa5, - 0x96, 0x6d, 0x4d, 0x42, 0x74, 0xdf, 0x50, 0x05, 0xbc, 0x96, 0x2c, 0x4c, 0x98, 0x30, 0xc4, 0x61, - 0xa5, 0xaf, 0x34, 0x15, 0x12, 0x06, 0x0a, 0xb9, 0x9c, 0xaa, 0x51, 0x45, 0xc5, 0xb0, 0x14, 0xa8, - 0x14, 0x25, 0x5f, 0x4f, 0x32, 0x27, 0xf3, 0x25, 0x73, 0x51, 0x6a, 0x6b, 0xf8, 0x18, 0x5d, 0x89, - 0xb4, 0xa0, 0x12, 0x11, 0x96, 0x1c, 0xe5, 0x62, 0xbe, 0x28, 0xd7, 0x42, 0x39, 0x49, 0x81, 0x1c, - 0x58, 0x4e, 0xd5, 0xa2, 0xb2, 0x37, 0x35, 0x50, 0xf6, 0xae, 0x26, 0x8a, 0x52, 0x99, 0xf3, 0xa0, - 0xda, 0x4f, 0x96, 0x0a, 0x38, 0x3d, 0x50, 0xc0, 0x72, 0x9a, 0x3e, 0x19, 0xb3, 0x67, 0xab, 0xd5, - 0x44, 0x75, 0xd1, 0xb5, 0x97, 0xf6, 0x1c, 0x15, 0xf2, 0xda, 0x73, 0x4f, 0x3c, 0xc2, 0xbc, 0x84, - 0xa3, 0x42, 0xbe, 0xe6, 0x64, 0x1d, 0x15, 0x32, 0x5c, 0x70, 0x54, 0x48, 0x9f, 0xf4, 0xa3, 0x22, - 0x4e, 0x51, 0x2a, 0xd8, 0xf8, 0x62, 0x1a, 0x46, 0x76, 0x99, 0x85, 0x1e, 0x42, 0x31, 0xdc, 0x1e, - 0xd1, 0xad, 0xd4, 0xb3, 0xa9, 0xf7, 0x4d, 0x49, 0x7b, 0x35, 0x9f, 0xb1, 0x7a, 0xd5, 0x08, 0xe3, - 0x34, 0x6c, 0x33, 0x47, 0x9c, 0xd3, 0xb7, 0x9c, 0x1c, 0x71, 0xa2, 0xaf, 0x27, 0x0e, 0x4c, 0x46, - 0x1e, 0x2b, 0xd0, 0x6a, 0x3f, 0xe7, 0x9e, 0x77, 0x14, 0xad, 0x96, 0xd7, 0x5c, 0x45, 0x6b, 0xc3, - 0x44, 0x50, 0x51, 0xa3, 0x9b, 0x7d, 0x7c, 0xbb, 0x1e, 0x49, 0xb4, 0x5b, 0xb9, 0x6c, 0xe3, 0x41, - 0xfc, 0x4a, 0x3c, 0x33, 0x48, 0xa4, 0x88, 0xcf, 0x0c, 0x12, 0x2d, 0xed, 0x11, 0x85, 0x8b, 0xd1, - 0x22, 0x1b, 0xf5, 0xcb, 0x44, 0xc2, 0x03, 0x80, 0x56, 0xcf, 0x6d, 0xaf, 0x02, 0x1e, 0xc1, 0x74, - 0xbc, 0x80, 0x45, 0x6b, 0x99, 0x10, 0x5d, 0x45, 0xbe, 0xb6, 0x3e, 0x80, 0x87, 0x0a, 0xfb, 0x69, - 0x01, 0x66, 0x12, 0x8a, 0x49, 0xf4, 0x7a, 0x26, 0x54, 0x52, 0x29, 0xad, 0xdd, 0x1e, 0xd4, 0x2d, - 0x85, 0x86, 0xaa, 0x05, 0x73, 0xd3, 0x88, 0x17, 0xb8, 0xb9, 0x69, 0x74, 0x95, 0x9c, 0xe8, 0xf3, - 0x02, 0xcc, 0x27, 0x97, 0x6f, 0xe8, 0xcd, 0x9c, 0x90, 0x3d, 0x25, 0xaa, 0xf6, 0xd6, 0x19, 0x3c, - 0x15, 0x9f, 0x27, 0x05, 0x58, 0x48, 0xa9, 0x7e, 0x50, 0x36, 0x6c, 0x5a, 0x71, 0xa9, 0x6d, 0x9e, - 0xc5, 0x55, 0x51, 0xfa, 0xac, 0x00, 0xb3, 0x49, 0x65, 0x03, 0xba, 0x9d, 0x13, 0xb4, 0xab, 0x34, - 0xd3, 0xde, 0x18, 0xd8, 0x4f, 0x31, 0x39, 0x81, 0x4b, 0x5d, 0x17, 0x7f, 0xd4, 0x6f, 0x01, 0x24, - 0xd7, 0x31, 0xda, 0xc6, 0x20, 0x2e, 0x2a, 0xb2, 0x07, 0x53, 0xb1, 0x73, 0x10, 0xd5, 0xfb, 0x83, - 0xf4, 0x54, 0x1f, 0xda, 0x5a, 0x7e, 0x87, 0x98, 0xda, 0xe8, 0xd9, 0x95, 0xa5, 0x36, 0xe1, 0x28, - 0xce, 0x52, 0x9b, 0x74, 0x34, 0x36, 0xc8, 0xd3, 0xe7, 0xe5, 0xc2, 0xb3, 0xe7, 0xe5, 0xc2, 0x5f, - 0xcf, 0xcb, 0x85, 0x27, 0x2f, 0xca, 0x43, 0xcf, 0x5e, 0x94, 0x87, 0x7e, 0x7f, 0x51, 0x1e, 0x82, - 0x45, 0x9b, 0xa6, 0xe0, 0xdd, 0x2b, 0x7c, 0x54, 0x8b, 0x3c, 0xe9, 0x9d, 0x1a, 0xad, 0xda, 0x34, - 0xd2, 0xaa, 0x9f, 0x84, 0xbf, 0xdd, 0xb4, 0xc6, 0xc5, 0x4f, 0x36, 0xaf, 0xfd, 0x1b, 0x00, 0x00, - 0xff, 0xff, 0x07, 0xe5, 0xbd, 0xe4, 0xc6, 0x1a, 0x00, 0x00, + // 1735 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcf, 0x6f, 0x1b, 0xc5, + 0x17, 0x8f, 0xf3, 0xab, 0xf1, 0x4b, 0x93, 0xb6, 0x93, 0x5f, 0xce, 0xb6, 0x71, 0x5c, 0xe7, 0xfb, + 0x95, 0xfa, 0x6d, 0xbf, 0xb1, 0x93, 0x20, 0x0a, 0x44, 0x5c, 0xe2, 0x94, 0x44, 0x3d, 0x04, 0xa2, + 0x2d, 0x05, 0x09, 0x0e, 0xab, 0xb5, 0x77, 0xea, 0xac, 0xb2, 0xde, 0x71, 0x76, 0x26, 0x69, 0x7a, + 0x42, 0x42, 0x48, 0x9c, 0x90, 0x7a, 0xe4, 0x8a, 0x38, 0x81, 0x90, 0x00, 0x89, 0x0b, 0xfc, 0x05, + 0x15, 0xe2, 0x50, 0x71, 0xe2, 0x04, 0xa8, 0x3d, 0xf0, 0x27, 0x70, 0x45, 0xf3, 0x63, 0xd7, 0xbb, + 0xf6, 0xae, 0x77, 0x1d, 0x92, 0x53, 0xb2, 0x3b, 0xef, 0x7d, 0xde, 0xe7, 0xf3, 0xde, 0xcc, 0xce, + 0xbc, 0x31, 0x2c, 0xb7, 0x3d, 0x72, 0x82, 0x5d, 0xd3, 0x6d, 0xe0, 0x2a, 0x3e, 0x6d, 0x1c, 0x98, + 0x6e, 0x13, 0x57, 0x4f, 0xd6, 0xab, 0xec, 0xb4, 0xd2, 0xf6, 0x08, 0x23, 0x68, 0xbe, 0x63, 0x50, + 0xf1, 0x0d, 0x2a, 0x27, 0xeb, 0x5a, 0xb1, 0x41, 0x68, 0x8b, 0xd0, 0x6a, 0xdd, 0xa4, 0xdc, 0xa1, + 0x8e, 0x99, 0xb9, 0x5e, 0x6d, 0x10, 0xdb, 0x95, 0x7e, 0xda, 0x82, 0x1a, 0x6f, 0xd1, 0x26, 0xc7, + 0x6b, 0xd1, 0xa6, 0x1a, 0x58, 0x94, 0x03, 0x86, 0x78, 0xaa, 0xca, 0x07, 0x35, 0x34, 0xdb, 0x24, + 0x4d, 0x22, 0xdf, 0xf3, 0xff, 0xd4, 0xdb, 0x95, 0x04, 0x8a, 0x2d, 0xd3, 0x3b, 0xc4, 0x2c, 0xc5, + 0x88, 0x78, 0x16, 0xf6, 0x68, 0x8a, 0x51, 0xdb, 0xf4, 0xcc, 0x96, 0x32, 0x2a, 0xff, 0x94, 0x83, + 0x99, 0x3d, 0xda, 0xdc, 0xf6, 0xb0, 0xc9, 0xf0, 0x16, 0x3d, 0xd4, 0xf1, 0xd1, 0x31, 0xa6, 0x0c, + 0x6d, 0x43, 0xde, 0xa4, 0x87, 0x86, 0x00, 0x2c, 0xe4, 0x4a, 0xb9, 0x5b, 0x93, 0x1b, 0xa5, 0x4a, + 0x7c, 0x72, 0x2a, 0x5b, 0xf4, 0xf0, 0x1d, 0x6e, 0x57, 0x1b, 0x7d, 0xf6, 0xfb, 0xf2, 0x90, 0x3e, + 0x61, 0xaa, 0x67, 0xb4, 0x0b, 0x48, 0x00, 0x18, 0x0d, 0x0e, 0x6f, 0x13, 0xd7, 0x78, 0x84, 0x71, + 0x61, 0x58, 0xa0, 0x2d, 0x56, 0x54, 0x32, 0x78, 0x4a, 0x2b, 0x2a, 0xa5, 0x95, 0x6d, 0x62, 0xbb, + 0xfa, 0x55, 0xe1, 0xb4, 0xad, 0x7c, 0x76, 0x30, 0xde, 0x9c, 0xfb, 0xf8, 0xaf, 0xef, 0x6e, 0x5f, + 0x0d, 0x08, 0x55, 0x28, 0x76, 0x1c, 0xec, 0x95, 0xd7, 0x61, 0x36, 0xca, 0x9d, 0xb6, 0x89, 0x4b, + 0x31, 0x5a, 0x84, 0x09, 0x19, 0xd7, 0xb6, 0x04, 0xf7, 0x51, 0xfd, 0x92, 0x78, 0xbe, 0x6f, 0x95, + 0x7f, 0x0c, 0xeb, 0xad, 0xd9, 0x56, 0x48, 0x6f, 0xdd, 0xb6, 0xb2, 0xe9, 0xad, 0xd9, 0x56, 0x44, + 0x6f, 0x5d, 0x3d, 0x9f, 0x9f, 0xde, 0x59, 0xae, 0xf7, 0x4a, 0x40, 0xa8, 0x52, 0x3f, 0x7e, 0xd2, + 0x25, 0x57, 0x50, 0x4f, 0x97, 0x7b, 0x04, 0x73, 0xdc, 0x85, 0x4b, 0x70, 0x04, 0x47, 0x5f, 0xef, + 0x1a, 0x8c, 0x53, 0xbb, 0xe9, 0x2a, 0xb1, 0xf9, 0x5a, 0xe1, 0xd7, 0x1f, 0x56, 0x67, 0x15, 0xc3, + 0x2d, 0xcb, 0xf2, 0x30, 0xa5, 0x0f, 0x98, 0x67, 0xbb, 0x4d, 0x5d, 0xd9, 0x45, 0xa2, 0x0c, 0x47, + 0xa2, 0x6c, 0x4e, 0x72, 0xba, 0xca, 0xae, 0x5c, 0x80, 0xf9, 0xee, 0x90, 0x92, 0x67, 0xf9, 0x9b, + 0x11, 0x40, 0x7b, 0xb4, 0xb9, 0x63, 0x3b, 0x4e, 0xcd, 0xb6, 0x68, 0x98, 0x8a, 0xa8, 0x67, 0x06, + 0x2a, 0xc2, 0x0e, 0x5d, 0x87, 0xbc, 0x5c, 0x0e, 0x3e, 0x97, 0x29, 0x7d, 0x42, 0xbe, 0xb8, 0x6f, + 0x21, 0x17, 0x2e, 0x33, 0xc2, 0x4c, 0xc7, 0x30, 0x29, 0xc5, 0x8c, 0x16, 0x46, 0x4a, 0x23, 0x7d, + 0xd3, 0x5f, 0x5b, 0xe3, 0x55, 0xfc, 0xfa, 0x8f, 0xe5, 0x5b, 0x4d, 0x9b, 0x1d, 0x1c, 0xd7, 0x2b, + 0x0d, 0xd2, 0x52, 0x0b, 0x55, 0xfd, 0x59, 0xa5, 0xd6, 0x61, 0x95, 0x3d, 0x69, 0x63, 0x2a, 0x1c, + 0xa8, 0x3e, 0x29, 0x02, 0x6c, 0x09, 0x7c, 0x54, 0x86, 0xa9, 0xa0, 0x50, 0x86, 0x6d, 0xd1, 0xc2, + 0x68, 0x69, 0xe4, 0xd6, 0xa8, 0x3e, 0xe9, 0xcf, 0x8a, 0xfb, 0x16, 0x45, 0xef, 0x81, 0x26, 0xa9, + 0x1b, 0x14, 0x33, 0xe6, 0xe0, 0x16, 0x76, 0x99, 0xf1, 0xc8, 0x31, 0x99, 0x98, 0x20, 0x63, 0x69, + 0x13, 0x64, 0x41, 0x3a, 0x3f, 0x08, 0x7c, 0x77, 0x1c, 0x93, 0xed, 0x60, 0x8c, 0xde, 0x86, 0xf9, + 0x60, 0x51, 0x44, 0x27, 0xdd, 0x78, 0x1a, 0xe6, 0x8c, 0xbf, 0x4a, 0xc3, 0xf3, 0x4e, 0x15, 0x52, + 0xae, 0xae, 0x39, 0xb1, 0x52, 0x3a, 0xd5, 0x52, 0x55, 0x7c, 0xde, 0xa9, 0xe2, 0x16, 0x3d, 0x0c, + 0xaa, 0x58, 0x81, 0x31, 0x31, 0x4b, 0x53, 0x8b, 0x28, 0xcd, 0xfa, 0xd7, 0xd0, 0x01, 0x99, 0x62, + 0xa3, 0xed, 0xd9, 0x0d, 0x7c, 0x11, 0x25, 0x04, 0x81, 0xbf, 0xcf, 0xe1, 0x79, 0x05, 0x3b, 0x59, + 0x0c, 0x55, 0xd0, 0xcf, 0x10, 0xaf, 0xe0, 0x47, 0x30, 0x27, 0x78, 0x47, 0x0a, 0x88, 0x31, 0x2d, + 0x8c, 0x9d, 0x3f, 0xb7, 0x19, 0x11, 0x29, 0x54, 0x6d, 0x8c, 0x29, 0x2f, 0x75, 0x67, 0x9a, 0x0d, + 0x58, 0x6a, 0x7f, 0x2a, 0x86, 0x4b, 0x0d, 0xbc, 0xd4, 0xb2, 0x16, 0xa1, 0x4a, 0xcb, 0x8a, 0xaa, + 0x4a, 0x1f, 0x89, 0x95, 0xbc, 0x27, 0x8a, 0x22, 0xd9, 0x84, 0x8a, 0x6d, 0x5a, 0x2d, 0xdb, 0x4d, + 0x2f, 0xb6, 0x30, 0xeb, 0x5b, 0x6c, 0xc5, 0x44, 0x18, 0x96, 0x17, 0x61, 0xa1, 0x27, 0xa4, 0x62, + 0xf3, 0x77, 0x0e, 0x0a, 0xc1, 0xd8, 0xfb, 0x36, 0x3b, 0xb0, 0x3c, 0xf3, 0xf1, 0x45, 0x10, 0x42, + 0x4b, 0x00, 0x8c, 0x18, 0xa6, 0xf4, 0x2b, 0x8c, 0x70, 0x44, 0x3d, 0xcf, 0x88, 0x02, 0x42, 0x0d, + 0x18, 0x37, 0x5b, 0xe4, 0xd8, 0x65, 0x62, 0x9e, 0x9c, 0x73, 0xed, 0x15, 0x74, 0x24, 0x29, 0xd7, + 0x61, 0x31, 0x46, 0xb8, 0x4a, 0xcb, 0x2f, 0x39, 0x58, 0x0a, 0x46, 0x1f, 0xb6, 0x2d, 0x93, 0xe1, + 0x7b, 0x98, 0x99, 0xb6, 0x43, 0x2f, 0x24, 0x37, 0x3a, 0x4c, 0xab, 0x41, 0x4b, 0x46, 0x11, 0xf9, + 0x99, 0xdc, 0xf8, 0x6f, 0xd2, 0x66, 0x29, 0x89, 0x29, 0x4a, 0x6a, 0xc7, 0x9c, 0x6a, 0x85, 0x5f, + 0x46, 0xb4, 0x96, 0xa0, 0x98, 0xa4, 0x46, 0x09, 0xfe, 0xb2, 0x57, 0xf0, 0x5b, 0xae, 0x59, 0x77, + 0xb0, 0x75, 0x21, 0x82, 0xff, 0x07, 0x57, 0xcd, 0x46, 0x03, 0xb7, 0x99, 0xed, 0x36, 0xe5, 0xea, + 0x93, 0x92, 0x27, 0xf4, 0x2b, 0xc1, 0x7b, 0xb1, 0xb8, 0xd2, 0x74, 0x04, 0x24, 0x95, 0x8e, 0x6f, + 0x73, 0x50, 0xea, 0x32, 0x79, 0x48, 0xfd, 0x65, 0x7f, 0x21, 0x52, 0x36, 0x60, 0xce, 0x74, 0x1c, + 0xf2, 0xd8, 0x38, 0xa6, 0x91, 0x0f, 0x99, 0xd2, 0x33, 0x23, 0x06, 0x3b, 0x1c, 0xf8, 0x50, 0x44, + 0xd3, 0x0a, 0xdc, 0xec, 0x43, 0x58, 0xc9, 0xfa, 0x6a, 0x38, 0x64, 0xb5, 0x67, 0xba, 0x66, 0x13, + 0xef, 0x63, 0xaf, 0x65, 0x53, 0x6a, 0x13, 0x97, 0x5e, 0xd4, 0x7a, 0xf5, 0xf0, 0x09, 0x39, 0xc4, + 0x86, 0xe9, 0x38, 0x62, 0xb3, 0xc8, 0xeb, 0x79, 0xf9, 0x66, 0xcb, 0x71, 0xd0, 0x0e, 0xe4, 0x19, + 0x31, 0xe4, 0xb3, 0x5a, 0xb2, 0x2b, 0x89, 0x47, 0xd9, 0x46, 0x03, 0x53, 0xba, 0xeb, 0x99, 0x2e, + 0xf3, 0x4f, 0x77, 0x8c, 0xe8, 0xc2, 0x15, 0xdd, 0x83, 0x09, 0x46, 0x8c, 0x26, 0x1f, 0x53, 0x5f, + 0xfd, 0x01, 0x60, 0x2e, 0x31, 0x22, 0x1e, 0x23, 0x09, 0xfd, 0x0f, 0x94, 0xfb, 0xa5, 0x4a, 0x65, + 0xf4, 0xfb, 0xe1, 0xd0, 0x5c, 0x92, 0x66, 0x3a, 0x3e, 0xda, 0x62, 0xcc, 0xa3, 0x17, 0x34, 0xe3, + 0xaf, 0x89, 0xfd, 0x05, 0x1b, 0x7c, 0x57, 0x94, 0x5f, 0x42, 0x95, 0xd5, 0xe9, 0x86, 0x7f, 0xd6, + 0x7e, 0x97, 0x7f, 0x0e, 0x51, 0x15, 0x66, 0xa3, 0xa6, 0x1e, 0x6e, 0x91, 0x13, 0x99, 0xe5, 0xbc, + 0x7e, 0x2d, 0x64, 0xad, 0x8b, 0x81, 0x10, 0x36, 0xdf, 0xcc, 0x14, 0xf6, 0x58, 0x18, 0xbb, 0x66, + 0x5b, 0xdd, 0xd8, 0xca, 0x54, 0x61, 0x8f, 0x87, 0xb1, 0x85, 0xb5, 0xc4, 0x8e, 0x64, 0xf6, 0x26, + 0x2c, 0x27, 0xa6, 0x4c, 0xa5, 0xf5, 0x8b, 0x9c, 0xf8, 0xac, 0xee, 0x92, 0x13, 0x79, 0xa2, 0x96, + 0xc6, 0x7e, 0x46, 0xef, 0x42, 0xde, 0x3c, 0x66, 0x07, 0xc4, 0xb3, 0xd9, 0x93, 0xd4, 0xac, 0x76, + 0x4c, 0xd1, 0x9b, 0x30, 0x2e, 0x13, 0xa9, 0x8e, 0xfd, 0xc5, 0xfe, 0xdf, 0x45, 0x35, 0x3b, 0x94, + 0xcf, 0xe6, 0x34, 0x97, 0xd0, 0x41, 0x2b, 0xdf, 0x00, 0x2d, 0x8e, 0xa2, 0x52, 0xf0, 0x33, 0x88, + 0xdd, 0x72, 0x97, 0x9c, 0x48, 0x89, 0xfc, 0x9c, 0xf0, 0x6f, 0xf9, 0xf7, 0x9d, 0x19, 0x0f, 0x61, + 0xc1, 0xb4, 0x2c, 0x7e, 0xe8, 0x30, 0x42, 0x65, 0xe7, 0xe7, 0xd8, 0xf4, 0x23, 0x9a, 0x14, 0x3a, + 0x63, 0x5a, 0xd6, 0x0e, 0xc6, 0x41, 0xcb, 0xc6, 0x0f, 0xb2, 0xe8, 0x43, 0xd0, 0x64, 0x6d, 0x63, + 0x91, 0x47, 0xb3, 0x21, 0xcf, 0x4b, 0x88, 0x1e, 0xf0, 0x5e, 0xce, 0x7c, 0x3a, 0x09, 0xe4, 0xb1, + 0x33, 0x70, 0xae, 0xd9, 0x56, 0x32, 0xe7, 0x00, 0x79, 0xfc, 0x6c, 0x9c, 0x7d, 0xf0, 0x06, 0x14, + 0x7d, 0xce, 0xf1, 0x6d, 0x43, 0xe1, 0x52, 0xb6, 0x00, 0x9a, 0xa4, 0xfe, 0x20, 0xa6, 0x7d, 0x40, + 0x36, 0xdc, 0x0c, 0x29, 0x48, 0x88, 0x33, 0x91, 0x2d, 0xce, 0x52, 0x20, 0x24, 0x36, 0x94, 0x0b, + 0xa5, 0x64, 0x3d, 0x1e, 0x3f, 0x92, 0xd2, 0x42, 0x5e, 0x44, 0x4a, 0xec, 0xb9, 0x77, 0x30, 0xd6, + 0xb9, 0xa1, 0x0a, 0x78, 0x23, 0x5e, 0x98, 0x30, 0xa1, 0x88, 0xc1, 0x4a, 0x5f, 0x69, 0x2a, 0x24, + 0x0c, 0x14, 0x72, 0x39, 0x51, 0xa3, 0x8a, 0x6a, 0xc2, 0x92, 0xaf, 0xb2, 0xb7, 0x55, 0xe0, 0xc9, + 0x9c, 0xcc, 0x96, 0xcc, 0x45, 0xa9, 0xad, 0xd6, 0xd5, 0x04, 0xf0, 0x44, 0x36, 0xa1, 0x14, 0x12, + 0x16, 0x1f, 0xe5, 0x72, 0xb6, 0x28, 0x37, 0x02, 0x39, 0x71, 0x81, 0x1c, 0x58, 0x4e, 0xd4, 0xa2, + 0xb2, 0x37, 0x35, 0x50, 0xf6, 0xae, 0xc7, 0x8a, 0x52, 0x99, 0xf3, 0xa0, 0xdc, 0x4f, 0x96, 0x0a, + 0x38, 0x3d, 0x50, 0xc0, 0x62, 0x92, 0x3e, 0x19, 0xb3, 0xe7, 0x53, 0xab, 0x89, 0xee, 0xa2, 0xeb, + 0x5b, 0xda, 0xb3, 0x55, 0xc8, 0x63, 0xcf, 0xbe, 0xb8, 0x41, 0x3b, 0x87, 0xad, 0x42, 0x5e, 0xc5, + 0xa5, 0x6d, 0x15, 0x32, 0x9c, 0xbf, 0x55, 0x48, 0x9f, 0xe4, 0xad, 0x22, 0x4a, 0x51, 0x2a, 0xd8, + 0xf8, 0x7c, 0x1a, 0x46, 0xf6, 0x68, 0x13, 0x3d, 0x82, 0x7c, 0xf0, 0x79, 0x44, 0x77, 0x12, 0xf7, + 0xa6, 0xde, 0x0b, 0x41, 0xed, 0xff, 0xd9, 0x8c, 0xd5, 0x95, 0x54, 0x10, 0xa7, 0x66, 0x5b, 0x19, + 0xe2, 0x74, 0x2e, 0xe2, 0x32, 0xc4, 0x09, 0x5f, 0x7d, 0x39, 0x30, 0x19, 0xba, 0x69, 0x42, 0xab, + 0xfd, 0x9c, 0x7b, 0x2e, 0xc1, 0xb4, 0x4a, 0x56, 0x73, 0x15, 0xad, 0x01, 0x13, 0xfe, 0x75, 0x08, + 0xba, 0xdd, 0xc7, 0xb7, 0xeb, 0x86, 0x4b, 0xbb, 0x93, 0xc9, 0x36, 0x1a, 0x84, 0x77, 0xe2, 0xa9, + 0x41, 0x42, 0x17, 0x30, 0xa9, 0x41, 0xc2, 0xad, 0x3d, 0x22, 0x70, 0x39, 0xdc, 0x64, 0xa3, 0x7e, + 0x99, 0x88, 0xb9, 0x00, 0xd0, 0xaa, 0x99, 0xed, 0x55, 0xc0, 0x63, 0x98, 0x8e, 0x36, 0xb0, 0x68, + 0x2d, 0x15, 0xa2, 0xab, 0xc9, 0xd7, 0xd6, 0x07, 0xf0, 0x50, 0x61, 0x3f, 0xc9, 0xc1, 0x4c, 0x4c, + 0x33, 0x89, 0x5e, 0x4d, 0x85, 0x8a, 0x6b, 0xa5, 0xb5, 0xbb, 0x83, 0xba, 0x25, 0xd0, 0x50, 0xbd, + 0x60, 0x66, 0x1a, 0xd1, 0x06, 0x37, 0x33, 0x8d, 0xae, 0x96, 0x13, 0x7d, 0x96, 0x83, 0xf9, 0xf8, + 0xf6, 0x0d, 0xbd, 0x9e, 0x11, 0xb2, 0xa7, 0x45, 0xd5, 0xde, 0x38, 0x83, 0xa7, 0xe2, 0xf3, 0x34, + 0x07, 0x0b, 0x09, 0xdd, 0x0f, 0x4a, 0x87, 0x4d, 0x6a, 0x2e, 0xb5, 0xcd, 0xb3, 0xb8, 0x2a, 0x4a, + 0x9f, 0xe6, 0x60, 0x36, 0xae, 0x6d, 0x40, 0x77, 0x33, 0x82, 0x76, 0xb5, 0x66, 0xda, 0x6b, 0x03, + 0xfb, 0x29, 0x26, 0xa7, 0x70, 0xa5, 0xeb, 0xe0, 0x8f, 0xfa, 0x2d, 0x80, 0xf8, 0x3e, 0x46, 0xdb, + 0x18, 0xc4, 0x45, 0x45, 0xf6, 0x60, 0x2a, 0xb2, 0x0f, 0xa2, 0x6a, 0x7f, 0x90, 0x9e, 0xee, 0x43, + 0x5b, 0xcb, 0xee, 0x10, 0x51, 0x1b, 0xde, 0xbb, 0xd2, 0xd4, 0xc6, 0x6c, 0xc5, 0x69, 0x6a, 0xe3, + 0xb6, 0xc6, 0x1a, 0x7e, 0xf6, 0xa2, 0x98, 0x7b, 0xfe, 0xa2, 0x98, 0xfb, 0xf3, 0x45, 0x31, 0xf7, + 0xf4, 0x65, 0x71, 0xe8, 0xf9, 0xcb, 0xe2, 0xd0, 0x6f, 0x2f, 0x8b, 0x43, 0xb0, 0x68, 0x93, 0x04, + 0xbc, 0xfd, 0xdc, 0x07, 0x95, 0xd0, 0x95, 0x5e, 0xc7, 0x68, 0xd5, 0x26, 0xa1, 0xa7, 0xea, 0x69, + 0xf0, 0xc3, 0x5b, 0x7d, 0x5c, 0xfc, 0xde, 0xf6, 0xca, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xcd, + 0xae, 0xf4, 0x85, 0x83, 0x1c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2768,6 +2831,76 @@ func (m *MsgFillAsksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.BidOrderCreationFee != nil { + { + size, err := m.BidOrderCreationFee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if len(m.BuyerSettlementFees) > 0 { + for iNdEx := len(m.BuyerSettlementFees) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BuyerSettlementFees[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.AskOrderIds) > 0 { + dAtA11 := make([]byte, len(m.AskOrderIds)*10) + var j10 int + for _, num := range m.AskOrderIds { + for num >= 1<<7 { + dAtA11[j10] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j10++ + } + dAtA11[j10] = uint8(num) + j10++ + } + i -= j10 + copy(dAtA[i:], dAtA11[:j10]) + i = encodeVarintTx(dAtA, i, uint64(j10)) + i-- + dAtA[i] = 0x22 + } + if len(m.TotalPrice) > 0 { + for iNdEx := len(m.TotalPrice) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TotalPrice[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.MarketId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Buyer) > 0 { + i -= len(m.Buyer) + copy(dAtA[i:], m.Buyer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Buyer))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -3818,6 +3951,36 @@ func (m *MsgFillAsksRequest) Size() (n int) { } var l int _ = l + l = len(m.Buyer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.MarketId != 0 { + n += 1 + sovTx(uint64(m.MarketId)) + } + if len(m.TotalPrice) > 0 { + for _, e := range m.TotalPrice { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.AskOrderIds) > 0 { + l = 0 + for _, e := range m.AskOrderIds { + l += sovTx(uint64(e)) + } + n += 1 + sovTx(uint64(l)) + l + } + if len(m.BuyerSettlementFees) > 0 { + for _, e := range m.BuyerSettlementFees { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if m.BidOrderCreationFee != nil { + l = m.BidOrderCreationFee.Size() + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -5105,6 +5268,237 @@ func (m *MsgFillAsksRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgFillAsksRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Buyer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Buyer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPrice", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TotalPrice = append(m.TotalPrice, types.Coin{}) + if err := m.TotalPrice[len(m.TotalPrice)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AskOrderIds = append(m.AskOrderIds, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.AskOrderIds) == 0 { + m.AskOrderIds = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AskOrderIds = append(m.AskOrderIds, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field AskOrderIds", wireType) + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BuyerSettlementFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BuyerSettlementFees = append(m.BuyerSettlementFees, types.Coin{}) + if err := m.BuyerSettlementFees[len(m.BuyerSettlementFees)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BidOrderCreationFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BidOrderCreationFee == nil { + m.BidOrderCreationFee = &types.Coin{} + } + if err := m.BidOrderCreationFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) From fb10d1a577bda2b448e8d252ab4a5522b81693c5 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 19:15:24 -0600 Subject: [PATCH 134/309] [1658]: Implement ValidateBasic and GetSigners for MsgFillAsksRequest. --- x/exchange/msg.go | 41 ++++++++- x/exchange/msg_test.go | 189 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 224 insertions(+), 6 deletions(-) diff --git a/x/exchange/msg.go b/x/exchange/msg.go index bfa3f6e7f6..a6030e2e3c 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -120,13 +120,46 @@ func (m MsgFillBidsRequest) GetSigners() []sdk.AccAddress { } func (m MsgFillAsksRequest) ValidateBasic() error { - // TODO[1658]: MsgFillAsksRequest.ValidateBasic() - return nil + var errs []error + + if _, err := sdk.AccAddressFromBech32(m.Buyer); err != nil { + errs = append(errs, fmt.Errorf("invalid buyer: %w", err)) + } + + if m.MarketId == 0 { + errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) + } + + if err := m.TotalPrice.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid total price: %w", err)) + } else if m.TotalPrice.IsZero() { + errs = append(errs, fmt.Errorf("invalid total price: cannot be zero")) + } + + if err := ValidateOrderIDs("ask", m.AskOrderIds); err != nil { + errs = append(errs, err) + } + + if len(m.BuyerSettlementFees) > 0 { + if err := m.BuyerSettlementFees.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid buyer settlement fees: %w", err)) + } + } + + if m.BidOrderCreationFee != nil { + if err := m.BidOrderCreationFee.Validate(); err != nil { + errs = append(errs, fmt.Errorf("invalid bid order creation fee: %w", err)) + } else if m.BidOrderCreationFee.IsZero() { + errs = append(errs, fmt.Errorf("invalid bid order creation fee: %s amount cannot be zero", m.BidOrderCreationFee.Denom)) + } + } + + return errors.Join(errs...) } func (m MsgFillAsksRequest) GetSigners() []sdk.AccAddress { - // TODO[1658]: MsgFillAsksRequest.GetSigners - panic("not implemented") + addr := sdk.MustAccAddressFromBech32(m.Buyer) + return []sdk.AccAddress{addr} } func (m MsgMarketSettleRequest) ValidateBasic() error { diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 98541ecb9b..151b261cc0 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -43,7 +43,9 @@ func TestAllMsgsGetSigners(t *testing.T) { func(signer string) sdk.Msg { return &MsgFillBidsRequest{Seller: signer} }, - // TODO[1658]: Add MsgFillAsksRequest once it's actually been defined. + func(signer string) sdk.Msg { + return &MsgFillAsksRequest{Buyer: signer} + }, func(signer string) sdk.Msg { return &MsgMarketSettleRequest{Admin: signer} }, @@ -465,7 +467,190 @@ func TestMsgFillBidsRequest_ValidateBasic(t *testing.T) { } } -// TODO[1658]: func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) +func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { + coin := func(amount int64, denom string) *sdk.Coin { + return &sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + buyer := sdk.AccAddress("buyer_______________").String() + + tests := []struct { + name string + msg MsgFillAsksRequest + expErr []string + }{ + { + name: "control", + msg: MsgFillAsksRequest{ + Buyer: buyer, + MarketId: 1, + TotalPrice: sdk.Coins{*coin(3, "acorn")}, + AskOrderIds: []uint64{1, 2, 3}, + BuyerSettlementFees: sdk.Coins{*coin(2, "banana")}, + BidOrderCreationFee: coin(8, "cactus"), + }, + expErr: nil, + }, + { + name: "empty buyer", + msg: MsgFillAsksRequest{ + Buyer: "", + MarketId: 1, + TotalPrice: sdk.Coins{*coin(3, "acorn")}, + AskOrderIds: []uint64{1}, + }, + expErr: []string{"invalid buyer", emptyAddrErr}, + }, + { + name: "bad buyer", + msg: MsgFillAsksRequest{ + Buyer: "not-an-address", + MarketId: 1, + TotalPrice: sdk.Coins{*coin(3, "acorn")}, + AskOrderIds: []uint64{1}, + }, + expErr: []string{"invalid buyer", "decoding bech32 failed"}, + }, + { + name: "market id zero", + msg: MsgFillAsksRequest{ + Buyer: buyer, + MarketId: 0, + TotalPrice: sdk.Coins{*coin(3, "acorn")}, + AskOrderIds: []uint64{1}, + }, + expErr: []string{"invalid market id", "cannot be zero"}, + }, + { + name: "nil total price", + msg: MsgFillAsksRequest{ + Buyer: buyer, + MarketId: 1, + TotalPrice: nil, + AskOrderIds: []uint64{1}, + }, + expErr: []string{"invalid total price", "cannot be zero"}, + }, + { + name: "empty total price", + msg: MsgFillAsksRequest{ + Buyer: buyer, + MarketId: 1, + TotalPrice: sdk.Coins{}, + AskOrderIds: []uint64{1}, + }, + expErr: []string{"invalid total price", "cannot be zero"}, + }, + { + name: "invalid total price", + msg: MsgFillAsksRequest{ + Buyer: buyer, + MarketId: 1, + TotalPrice: sdk.Coins{*coin(-1, "acorn")}, + AskOrderIds: []uint64{1}, + }, + expErr: []string{"invalid total price", "coin -1acorn amount is not positive"}, + }, + { + name: "nil order ids", + msg: MsgFillAsksRequest{ + Buyer: buyer, + MarketId: 1, + TotalPrice: sdk.Coins{*coin(1, "acorn")}, + AskOrderIds: nil, + }, + expErr: []string{"no ask order ids provided"}, + }, + { + name: "order id zero", + msg: MsgFillAsksRequest{ + Buyer: buyer, + MarketId: 1, + TotalPrice: sdk.Coins{*coin(1, "acorn")}, + AskOrderIds: []uint64{0}, + }, + expErr: []string{"invalid ask order ids: cannot contain order id zero"}, + }, + { + name: "duplicate order ids", + msg: MsgFillAsksRequest{ + Buyer: buyer, + MarketId: 1, + TotalPrice: sdk.Coins{*coin(1, "acorn")}, + AskOrderIds: []uint64{1, 2, 1}, + }, + expErr: []string{"duplicate ask order ids provided: [1]"}, + }, + { + name: "invalid buyer settlement flat fee", + msg: MsgFillAsksRequest{ + Buyer: buyer, + MarketId: 1, + TotalPrice: sdk.Coins{*coin(1, "acorn")}, + AskOrderIds: []uint64{1}, + BuyerSettlementFees: sdk.Coins{*coin(-1, "catan")}, + }, + expErr: []string{"invalid buyer settlement fees", "coin -1catan amount is not positive"}, + }, + { + name: "buyer settlement flat fee with zero amount", + msg: MsgFillAsksRequest{ + Buyer: buyer, + MarketId: 1, + TotalPrice: sdk.Coins{*coin(1, "acorn")}, + AskOrderIds: []uint64{1}, + BuyerSettlementFees: sdk.Coins{*coin(0, "catan")}, + }, + expErr: []string{"invalid buyer settlement fees", "coin 0catan amount is not positive"}, + }, + { + name: "invalid order creation fee", + msg: MsgFillAsksRequest{ + Buyer: buyer, + MarketId: 1, + TotalPrice: sdk.Coins{*coin(1, "acorn")}, + AskOrderIds: []uint64{1}, + BidOrderCreationFee: coin(-1, "catan"), + }, + expErr: []string{"invalid bid order creation fee", "negative coin amount: -1"}, + }, + { + name: "order creation fee with zero amount", + msg: MsgFillAsksRequest{ + Buyer: buyer, + MarketId: 1, + TotalPrice: sdk.Coins{*coin(1, "acorn")}, + AskOrderIds: []uint64{1}, + BidOrderCreationFee: coin(0, "catan"), + }, + expErr: []string{"invalid bid order creation fee", "catan amount cannot be zero"}, + }, + { + name: "multiple errors", + msg: MsgFillAsksRequest{ + Buyer: "", + MarketId: 0, + TotalPrice: nil, + AskOrderIds: nil, + BuyerSettlementFees: sdk.Coins{*coin(0, "catan")}, + BidOrderCreationFee: coin(-1, "catan"), + }, + expErr: []string{ + "invalid buyer", + "invalid market id", + "invalid total price", + "no ask order ids provided", + "invalid buyer settlement fees", + "invalid bid order creation fee", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} // TODO[1658]: func TestMsgMarketSettleRequest_ValidateBasic(t *testing.T) From 91349b6f933f8b85d6d72347a65f99c6a8f4e9c8 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 19:22:04 -0600 Subject: [PATCH 135/309] [1658]: Change total_price to a single Coin entry since validating settlement fees against a price with multiple denoms is a pain. --- docs/proto-docs.md | 2 +- proto/provenance/exchange/v1/tx.proto | 3 +- x/exchange/msg_test.go | 48 ++--- x/exchange/tx.pb.go | 257 +++++++++++++------------- 4 files changed, 140 insertions(+), 170 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index c3ff991d09..cd63b66f52 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2219,7 +2219,7 @@ MsgFillAsksRequest is a request message for the FillAsks endpoint. | ----- | ---- | ----- | ----------- | | `buyer` | [string](#string) | | buyer is the address of the account attempting to buy some assets. | | `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market with the asks to fill. All ask orders being filled must be in this market. | -| `total_price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | total_price is the total amount being spent on some assets. It must be the sum of all ask order prices. | +| `total_price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | total_price is the total amount being spent on some assets. It must be the sum of all ask order prices. | | `ask_order_ids` | [uint64](#uint64) | repeated | ask_order_ids are the ids of the ask orders that you are trying to fill. All ids must be for ask orders, and must be in the same market as the market_id. | | `buyer_settlement_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) for this settlement. | | `bid_order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | bid_order_creation_fee is the fee that is being paid to create this order (which is immediately then settled). | diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index dc7ba685df..e48ef3a2b9 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -145,8 +145,7 @@ message MsgFillAsksRequest { uint32 market_id = 2; // total_price is the total amount being spent on some assets. // It must be the sum of all ask order prices. - repeated cosmos.base.v1beta1.Coin total_price = 3 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + cosmos.base.v1beta1.Coin total_price = 3 [(gogoproto.nullable) = false]; // ask_order_ids are the ids of the ask orders that you are trying to fill. // All ids must be for ask orders, and must be in the same market as the market_id. repeated uint64 ask_order_ids = 4; diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 151b261cc0..5c0c003f7b 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -483,7 +483,7 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { msg: MsgFillAsksRequest{ Buyer: buyer, MarketId: 1, - TotalPrice: sdk.Coins{*coin(3, "acorn")}, + TotalPrice: *coin(3, "acorn"), AskOrderIds: []uint64{1, 2, 3}, BuyerSettlementFees: sdk.Coins{*coin(2, "banana")}, BidOrderCreationFee: coin(8, "cactus"), @@ -495,7 +495,7 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { msg: MsgFillAsksRequest{ Buyer: "", MarketId: 1, - TotalPrice: sdk.Coins{*coin(3, "acorn")}, + TotalPrice: *coin(3, "acorn"), AskOrderIds: []uint64{1}, }, expErr: []string{"invalid buyer", emptyAddrErr}, @@ -505,7 +505,7 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { msg: MsgFillAsksRequest{ Buyer: "not-an-address", MarketId: 1, - TotalPrice: sdk.Coins{*coin(3, "acorn")}, + TotalPrice: *coin(3, "acorn"), AskOrderIds: []uint64{1}, }, expErr: []string{"invalid buyer", "decoding bech32 failed"}, @@ -515,47 +515,27 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { msg: MsgFillAsksRequest{ Buyer: buyer, MarketId: 0, - TotalPrice: sdk.Coins{*coin(3, "acorn")}, + TotalPrice: *coin(3, "acorn"), AskOrderIds: []uint64{1}, }, expErr: []string{"invalid market id", "cannot be zero"}, }, - { - name: "nil total price", - msg: MsgFillAsksRequest{ - Buyer: buyer, - MarketId: 1, - TotalPrice: nil, - AskOrderIds: []uint64{1}, - }, - expErr: []string{"invalid total price", "cannot be zero"}, - }, - { - name: "empty total price", - msg: MsgFillAsksRequest{ - Buyer: buyer, - MarketId: 1, - TotalPrice: sdk.Coins{}, - AskOrderIds: []uint64{1}, - }, - expErr: []string{"invalid total price", "cannot be zero"}, - }, { name: "invalid total price", msg: MsgFillAsksRequest{ Buyer: buyer, MarketId: 1, - TotalPrice: sdk.Coins{*coin(-1, "acorn")}, + TotalPrice: *coin(-1, "acorn"), AskOrderIds: []uint64{1}, }, - expErr: []string{"invalid total price", "coin -1acorn amount is not positive"}, + expErr: []string{"invalid total price", "negative coin amount: -1"}, }, { name: "nil order ids", msg: MsgFillAsksRequest{ Buyer: buyer, MarketId: 1, - TotalPrice: sdk.Coins{*coin(1, "acorn")}, + TotalPrice: *coin(1, "acorn"), AskOrderIds: nil, }, expErr: []string{"no ask order ids provided"}, @@ -565,7 +545,7 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { msg: MsgFillAsksRequest{ Buyer: buyer, MarketId: 1, - TotalPrice: sdk.Coins{*coin(1, "acorn")}, + TotalPrice: *coin(1, "acorn"), AskOrderIds: []uint64{0}, }, expErr: []string{"invalid ask order ids: cannot contain order id zero"}, @@ -575,7 +555,7 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { msg: MsgFillAsksRequest{ Buyer: buyer, MarketId: 1, - TotalPrice: sdk.Coins{*coin(1, "acorn")}, + TotalPrice: *coin(1, "acorn"), AskOrderIds: []uint64{1, 2, 1}, }, expErr: []string{"duplicate ask order ids provided: [1]"}, @@ -585,7 +565,7 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { msg: MsgFillAsksRequest{ Buyer: buyer, MarketId: 1, - TotalPrice: sdk.Coins{*coin(1, "acorn")}, + TotalPrice: *coin(1, "acorn"), AskOrderIds: []uint64{1}, BuyerSettlementFees: sdk.Coins{*coin(-1, "catan")}, }, @@ -596,7 +576,7 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { msg: MsgFillAsksRequest{ Buyer: buyer, MarketId: 1, - TotalPrice: sdk.Coins{*coin(1, "acorn")}, + TotalPrice: *coin(1, "acorn"), AskOrderIds: []uint64{1}, BuyerSettlementFees: sdk.Coins{*coin(0, "catan")}, }, @@ -607,7 +587,7 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { msg: MsgFillAsksRequest{ Buyer: buyer, MarketId: 1, - TotalPrice: sdk.Coins{*coin(1, "acorn")}, + TotalPrice: *coin(1, "acorn"), AskOrderIds: []uint64{1}, BidOrderCreationFee: coin(-1, "catan"), }, @@ -618,7 +598,7 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { msg: MsgFillAsksRequest{ Buyer: buyer, MarketId: 1, - TotalPrice: sdk.Coins{*coin(1, "acorn")}, + TotalPrice: *coin(1, "acorn"), AskOrderIds: []uint64{1}, BidOrderCreationFee: coin(0, "catan"), }, @@ -629,7 +609,7 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { msg: MsgFillAsksRequest{ Buyer: "", MarketId: 0, - TotalPrice: nil, + TotalPrice: sdk.Coin{}, AskOrderIds: nil, BuyerSettlementFees: sdk.Coins{*coin(0, "catan")}, BidOrderCreationFee: coin(-1, "catan"), diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index c20972a501..845e601c95 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -468,7 +468,7 @@ type MsgFillAsksRequest struct { MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // total_price is the total amount being spent on some assets. // It must be the sum of all ask order prices. - TotalPrice github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=total_price,json=totalPrice,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"total_price"` + TotalPrice types.Coin `protobuf:"bytes,3,opt,name=total_price,json=totalPrice,proto3" json:"total_price"` // ask_order_ids are the ids of the ask orders that you are trying to fill. // All ids must be for ask orders, and must be in the same market as the market_id. AskOrderIds []uint64 `protobuf:"varint,4,rep,packed,name=ask_order_ids,json=askOrderIds,proto3" json:"ask_order_ids,omitempty"` @@ -526,11 +526,11 @@ func (m *MsgFillAsksRequest) GetMarketId() uint32 { return 0 } -func (m *MsgFillAsksRequest) GetTotalPrice() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *MsgFillAsksRequest) GetTotalPrice() types.Coin { if m != nil { return m.TotalPrice } - return nil + return types.Coin{} } func (m *MsgFillAsksRequest) GetAskOrderIds() []uint64 { @@ -1767,116 +1767,116 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1735 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcf, 0x6f, 0x1b, 0xc5, - 0x17, 0x8f, 0xf3, 0xab, 0xf1, 0x4b, 0x93, 0xb6, 0x93, 0x5f, 0xce, 0xb6, 0x71, 0x5c, 0xe7, 0xfb, - 0x95, 0xfa, 0x6d, 0xbf, 0xb1, 0x93, 0x20, 0x0a, 0x44, 0x5c, 0xe2, 0x94, 0x44, 0x3d, 0x04, 0xa2, - 0x2d, 0x05, 0x09, 0x0e, 0xab, 0xb5, 0x77, 0xea, 0xac, 0xb2, 0xde, 0x71, 0x76, 0x26, 0x69, 0x7a, - 0x42, 0x42, 0x48, 0x9c, 0x90, 0x7a, 0xe4, 0x8a, 0x38, 0x81, 0x90, 0x00, 0x89, 0x0b, 0xfc, 0x05, - 0x15, 0xe2, 0x50, 0x71, 0xe2, 0x04, 0xa8, 0x3d, 0xf0, 0x27, 0x70, 0x45, 0xf3, 0x63, 0xd7, 0xbb, - 0xf6, 0xae, 0x77, 0x1d, 0x92, 0x53, 0xb2, 0x3b, 0xef, 0x7d, 0xde, 0xe7, 0xf3, 0xde, 0xcc, 0xce, - 0xbc, 0x31, 0x2c, 0xb7, 0x3d, 0x72, 0x82, 0x5d, 0xd3, 0x6d, 0xe0, 0x2a, 0x3e, 0x6d, 0x1c, 0x98, - 0x6e, 0x13, 0x57, 0x4f, 0xd6, 0xab, 0xec, 0xb4, 0xd2, 0xf6, 0x08, 0x23, 0x68, 0xbe, 0x63, 0x50, - 0xf1, 0x0d, 0x2a, 0x27, 0xeb, 0x5a, 0xb1, 0x41, 0x68, 0x8b, 0xd0, 0x6a, 0xdd, 0xa4, 0xdc, 0xa1, - 0x8e, 0x99, 0xb9, 0x5e, 0x6d, 0x10, 0xdb, 0x95, 0x7e, 0xda, 0x82, 0x1a, 0x6f, 0xd1, 0x26, 0xc7, - 0x6b, 0xd1, 0xa6, 0x1a, 0x58, 0x94, 0x03, 0x86, 0x78, 0xaa, 0xca, 0x07, 0x35, 0x34, 0xdb, 0x24, - 0x4d, 0x22, 0xdf, 0xf3, 0xff, 0xd4, 0xdb, 0x95, 0x04, 0x8a, 0x2d, 0xd3, 0x3b, 0xc4, 0x2c, 0xc5, - 0x88, 0x78, 0x16, 0xf6, 0x68, 0x8a, 0x51, 0xdb, 0xf4, 0xcc, 0x96, 0x32, 0x2a, 0xff, 0x94, 0x83, - 0x99, 0x3d, 0xda, 0xdc, 0xf6, 0xb0, 0xc9, 0xf0, 0x16, 0x3d, 0xd4, 0xf1, 0xd1, 0x31, 0xa6, 0x0c, - 0x6d, 0x43, 0xde, 0xa4, 0x87, 0x86, 0x00, 0x2c, 0xe4, 0x4a, 0xb9, 0x5b, 0x93, 0x1b, 0xa5, 0x4a, - 0x7c, 0x72, 0x2a, 0x5b, 0xf4, 0xf0, 0x1d, 0x6e, 0x57, 0x1b, 0x7d, 0xf6, 0xfb, 0xf2, 0x90, 0x3e, - 0x61, 0xaa, 0x67, 0xb4, 0x0b, 0x48, 0x00, 0x18, 0x0d, 0x0e, 0x6f, 0x13, 0xd7, 0x78, 0x84, 0x71, - 0x61, 0x58, 0xa0, 0x2d, 0x56, 0x54, 0x32, 0x78, 0x4a, 0x2b, 0x2a, 0xa5, 0x95, 0x6d, 0x62, 0xbb, - 0xfa, 0x55, 0xe1, 0xb4, 0xad, 0x7c, 0x76, 0x30, 0xde, 0x9c, 0xfb, 0xf8, 0xaf, 0xef, 0x6e, 0x5f, - 0x0d, 0x08, 0x55, 0x28, 0x76, 0x1c, 0xec, 0x95, 0xd7, 0x61, 0x36, 0xca, 0x9d, 0xb6, 0x89, 0x4b, - 0x31, 0x5a, 0x84, 0x09, 0x19, 0xd7, 0xb6, 0x04, 0xf7, 0x51, 0xfd, 0x92, 0x78, 0xbe, 0x6f, 0x95, - 0x7f, 0x0c, 0xeb, 0xad, 0xd9, 0x56, 0x48, 0x6f, 0xdd, 0xb6, 0xb2, 0xe9, 0xad, 0xd9, 0x56, 0x44, - 0x6f, 0x5d, 0x3d, 0x9f, 0x9f, 0xde, 0x59, 0xae, 0xf7, 0x4a, 0x40, 0xa8, 0x52, 0x3f, 0x7e, 0xd2, - 0x25, 0x57, 0x50, 0x4f, 0x97, 0x7b, 0x04, 0x73, 0xdc, 0x85, 0x4b, 0x70, 0x04, 0x47, 0x5f, 0xef, - 0x1a, 0x8c, 0x53, 0xbb, 0xe9, 0x2a, 0xb1, 0xf9, 0x5a, 0xe1, 0xd7, 0x1f, 0x56, 0x67, 0x15, 0xc3, - 0x2d, 0xcb, 0xf2, 0x30, 0xa5, 0x0f, 0x98, 0x67, 0xbb, 0x4d, 0x5d, 0xd9, 0x45, 0xa2, 0x0c, 0x47, - 0xa2, 0x6c, 0x4e, 0x72, 0xba, 0xca, 0xae, 0x5c, 0x80, 0xf9, 0xee, 0x90, 0x92, 0x67, 0xf9, 0x9b, - 0x11, 0x40, 0x7b, 0xb4, 0xb9, 0x63, 0x3b, 0x4e, 0xcd, 0xb6, 0x68, 0x98, 0x8a, 0xa8, 0x67, 0x06, - 0x2a, 0xc2, 0x0e, 0x5d, 0x87, 0xbc, 0x5c, 0x0e, 0x3e, 0x97, 0x29, 0x7d, 0x42, 0xbe, 0xb8, 0x6f, - 0x21, 0x17, 0x2e, 0x33, 0xc2, 0x4c, 0xc7, 0x30, 0x29, 0xc5, 0x8c, 0x16, 0x46, 0x4a, 0x23, 0x7d, - 0xd3, 0x5f, 0x5b, 0xe3, 0x55, 0xfc, 0xfa, 0x8f, 0xe5, 0x5b, 0x4d, 0x9b, 0x1d, 0x1c, 0xd7, 0x2b, - 0x0d, 0xd2, 0x52, 0x0b, 0x55, 0xfd, 0x59, 0xa5, 0xd6, 0x61, 0x95, 0x3d, 0x69, 0x63, 0x2a, 0x1c, - 0xa8, 0x3e, 0x29, 0x02, 0x6c, 0x09, 0x7c, 0x54, 0x86, 0xa9, 0xa0, 0x50, 0x86, 0x6d, 0xd1, 0xc2, - 0x68, 0x69, 0xe4, 0xd6, 0xa8, 0x3e, 0xe9, 0xcf, 0x8a, 0xfb, 0x16, 0x45, 0xef, 0x81, 0x26, 0xa9, - 0x1b, 0x14, 0x33, 0xe6, 0xe0, 0x16, 0x76, 0x99, 0xf1, 0xc8, 0x31, 0x99, 0x98, 0x20, 0x63, 0x69, - 0x13, 0x64, 0x41, 0x3a, 0x3f, 0x08, 0x7c, 0x77, 0x1c, 0x93, 0xed, 0x60, 0x8c, 0xde, 0x86, 0xf9, - 0x60, 0x51, 0x44, 0x27, 0xdd, 0x78, 0x1a, 0xe6, 0x8c, 0xbf, 0x4a, 0xc3, 0xf3, 0x4e, 0x15, 0x52, - 0xae, 0xae, 0x39, 0xb1, 0x52, 0x3a, 0xd5, 0x52, 0x55, 0x7c, 0xde, 0xa9, 0xe2, 0x16, 0x3d, 0x0c, - 0xaa, 0x58, 0x81, 0x31, 0x31, 0x4b, 0x53, 0x8b, 0x28, 0xcd, 0xfa, 0xd7, 0xd0, 0x01, 0x99, 0x62, - 0xa3, 0xed, 0xd9, 0x0d, 0x7c, 0x11, 0x25, 0x04, 0x81, 0xbf, 0xcf, 0xe1, 0x79, 0x05, 0x3b, 0x59, - 0x0c, 0x55, 0xd0, 0xcf, 0x10, 0xaf, 0xe0, 0x47, 0x30, 0x27, 0x78, 0x47, 0x0a, 0x88, 0x31, 0x2d, - 0x8c, 0x9d, 0x3f, 0xb7, 0x19, 0x11, 0x29, 0x54, 0x6d, 0x8c, 0x29, 0x2f, 0x75, 0x67, 0x9a, 0x0d, - 0x58, 0x6a, 0x7f, 0x2a, 0x86, 0x4b, 0x0d, 0xbc, 0xd4, 0xb2, 0x16, 0xa1, 0x4a, 0xcb, 0x8a, 0xaa, - 0x4a, 0x1f, 0x89, 0x95, 0xbc, 0x27, 0x8a, 0x22, 0xd9, 0x84, 0x8a, 0x6d, 0x5a, 0x2d, 0xdb, 0x4d, - 0x2f, 0xb6, 0x30, 0xeb, 0x5b, 0x6c, 0xc5, 0x44, 0x18, 0x96, 0x17, 0x61, 0xa1, 0x27, 0xa4, 0x62, - 0xf3, 0x77, 0x0e, 0x0a, 0xc1, 0xd8, 0xfb, 0x36, 0x3b, 0xb0, 0x3c, 0xf3, 0xf1, 0x45, 0x10, 0x42, - 0x4b, 0x00, 0x8c, 0x18, 0xa6, 0xf4, 0x2b, 0x8c, 0x70, 0x44, 0x3d, 0xcf, 0x88, 0x02, 0x42, 0x0d, - 0x18, 0x37, 0x5b, 0xe4, 0xd8, 0x65, 0x62, 0x9e, 0x9c, 0x73, 0xed, 0x15, 0x74, 0x24, 0x29, 0xd7, - 0x61, 0x31, 0x46, 0xb8, 0x4a, 0xcb, 0x2f, 0x39, 0x58, 0x0a, 0x46, 0x1f, 0xb6, 0x2d, 0x93, 0xe1, - 0x7b, 0x98, 0x99, 0xb6, 0x43, 0x2f, 0x24, 0x37, 0x3a, 0x4c, 0xab, 0x41, 0x4b, 0x46, 0x11, 0xf9, - 0x99, 0xdc, 0xf8, 0x6f, 0xd2, 0x66, 0x29, 0x89, 0x29, 0x4a, 0x6a, 0xc7, 0x9c, 0x6a, 0x85, 0x5f, - 0x46, 0xb4, 0x96, 0xa0, 0x98, 0xa4, 0x46, 0x09, 0xfe, 0xb2, 0x57, 0xf0, 0x5b, 0xae, 0x59, 0x77, - 0xb0, 0x75, 0x21, 0x82, 0xff, 0x07, 0x57, 0xcd, 0x46, 0x03, 0xb7, 0x99, 0xed, 0x36, 0xe5, 0xea, - 0x93, 0x92, 0x27, 0xf4, 0x2b, 0xc1, 0x7b, 0xb1, 0xb8, 0xd2, 0x74, 0x04, 0x24, 0x95, 0x8e, 0x6f, - 0x73, 0x50, 0xea, 0x32, 0x79, 0x48, 0xfd, 0x65, 0x7f, 0x21, 0x52, 0x36, 0x60, 0xce, 0x74, 0x1c, - 0xf2, 0xd8, 0x38, 0xa6, 0x91, 0x0f, 0x99, 0xd2, 0x33, 0x23, 0x06, 0x3b, 0x1c, 0xf8, 0x50, 0x44, - 0xd3, 0x0a, 0xdc, 0xec, 0x43, 0x58, 0xc9, 0xfa, 0x6a, 0x38, 0x64, 0xb5, 0x67, 0xba, 0x66, 0x13, - 0xef, 0x63, 0xaf, 0x65, 0x53, 0x6a, 0x13, 0x97, 0x5e, 0xd4, 0x7a, 0xf5, 0xf0, 0x09, 0x39, 0xc4, - 0x86, 0xe9, 0x38, 0x62, 0xb3, 0xc8, 0xeb, 0x79, 0xf9, 0x66, 0xcb, 0x71, 0xd0, 0x0e, 0xe4, 0x19, - 0x31, 0xe4, 0xb3, 0x5a, 0xb2, 0x2b, 0x89, 0x47, 0xd9, 0x46, 0x03, 0x53, 0xba, 0xeb, 0x99, 0x2e, - 0xf3, 0x4f, 0x77, 0x8c, 0xe8, 0xc2, 0x15, 0xdd, 0x83, 0x09, 0x46, 0x8c, 0x26, 0x1f, 0x53, 0x5f, - 0xfd, 0x01, 0x60, 0x2e, 0x31, 0x22, 0x1e, 0x23, 0x09, 0xfd, 0x0f, 0x94, 0xfb, 0xa5, 0x4a, 0x65, - 0xf4, 0xfb, 0xe1, 0xd0, 0x5c, 0x92, 0x66, 0x3a, 0x3e, 0xda, 0x62, 0xcc, 0xa3, 0x17, 0x34, 0xe3, - 0xaf, 0x89, 0xfd, 0x05, 0x1b, 0x7c, 0x57, 0x94, 0x5f, 0x42, 0x95, 0xd5, 0xe9, 0x86, 0x7f, 0xd6, - 0x7e, 0x97, 0x7f, 0x0e, 0x51, 0x15, 0x66, 0xa3, 0xa6, 0x1e, 0x6e, 0x91, 0x13, 0x99, 0xe5, 0xbc, - 0x7e, 0x2d, 0x64, 0xad, 0x8b, 0x81, 0x10, 0x36, 0xdf, 0xcc, 0x14, 0xf6, 0x58, 0x18, 0xbb, 0x66, - 0x5b, 0xdd, 0xd8, 0xca, 0x54, 0x61, 0x8f, 0x87, 0xb1, 0x85, 0xb5, 0xc4, 0x8e, 0x64, 0xf6, 0x26, - 0x2c, 0x27, 0xa6, 0x4c, 0xa5, 0xf5, 0x8b, 0x9c, 0xf8, 0xac, 0xee, 0x92, 0x13, 0x79, 0xa2, 0x96, - 0xc6, 0x7e, 0x46, 0xef, 0x42, 0xde, 0x3c, 0x66, 0x07, 0xc4, 0xb3, 0xd9, 0x93, 0xd4, 0xac, 0x76, - 0x4c, 0xd1, 0x9b, 0x30, 0x2e, 0x13, 0xa9, 0x8e, 0xfd, 0xc5, 0xfe, 0xdf, 0x45, 0x35, 0x3b, 0x94, - 0xcf, 0xe6, 0x34, 0x97, 0xd0, 0x41, 0x2b, 0xdf, 0x00, 0x2d, 0x8e, 0xa2, 0x52, 0xf0, 0x33, 0x88, - 0xdd, 0x72, 0x97, 0x9c, 0x48, 0x89, 0xfc, 0x9c, 0xf0, 0x6f, 0xf9, 0xf7, 0x9d, 0x19, 0x0f, 0x61, - 0xc1, 0xb4, 0x2c, 0x7e, 0xe8, 0x30, 0x42, 0x65, 0xe7, 0xe7, 0xd8, 0xf4, 0x23, 0x9a, 0x14, 0x3a, - 0x63, 0x5a, 0xd6, 0x0e, 0xc6, 0x41, 0xcb, 0xc6, 0x0f, 0xb2, 0xe8, 0x43, 0xd0, 0x64, 0x6d, 0x63, - 0x91, 0x47, 0xb3, 0x21, 0xcf, 0x4b, 0x88, 0x1e, 0xf0, 0x5e, 0xce, 0x7c, 0x3a, 0x09, 0xe4, 0xb1, - 0x33, 0x70, 0xae, 0xd9, 0x56, 0x32, 0xe7, 0x00, 0x79, 0xfc, 0x6c, 0x9c, 0x7d, 0xf0, 0x06, 0x14, - 0x7d, 0xce, 0xf1, 0x6d, 0x43, 0xe1, 0x52, 0xb6, 0x00, 0x9a, 0xa4, 0xfe, 0x20, 0xa6, 0x7d, 0x40, - 0x36, 0xdc, 0x0c, 0x29, 0x48, 0x88, 0x33, 0x91, 0x2d, 0xce, 0x52, 0x20, 0x24, 0x36, 0x94, 0x0b, - 0xa5, 0x64, 0x3d, 0x1e, 0x3f, 0x92, 0xd2, 0x42, 0x5e, 0x44, 0x4a, 0xec, 0xb9, 0x77, 0x30, 0xd6, - 0xb9, 0xa1, 0x0a, 0x78, 0x23, 0x5e, 0x98, 0x30, 0xa1, 0x88, 0xc1, 0x4a, 0x5f, 0x69, 0x2a, 0x24, - 0x0c, 0x14, 0x72, 0x39, 0x51, 0xa3, 0x8a, 0x6a, 0xc2, 0x92, 0xaf, 0xb2, 0xb7, 0x55, 0xe0, 0xc9, - 0x9c, 0xcc, 0x96, 0xcc, 0x45, 0xa9, 0xad, 0xd6, 0xd5, 0x04, 0xf0, 0x44, 0x36, 0xa1, 0x14, 0x12, - 0x16, 0x1f, 0xe5, 0x72, 0xb6, 0x28, 0x37, 0x02, 0x39, 0x71, 0x81, 0x1c, 0x58, 0x4e, 0xd4, 0xa2, - 0xb2, 0x37, 0x35, 0x50, 0xf6, 0xae, 0xc7, 0x8a, 0x52, 0x99, 0xf3, 0xa0, 0xdc, 0x4f, 0x96, 0x0a, - 0x38, 0x3d, 0x50, 0xc0, 0x62, 0x92, 0x3e, 0x19, 0xb3, 0xe7, 0x53, 0xab, 0x89, 0xee, 0xa2, 0xeb, - 0x5b, 0xda, 0xb3, 0x55, 0xc8, 0x63, 0xcf, 0xbe, 0xb8, 0x41, 0x3b, 0x87, 0xad, 0x42, 0x5e, 0xc5, - 0xa5, 0x6d, 0x15, 0x32, 0x9c, 0xbf, 0x55, 0x48, 0x9f, 0xe4, 0xad, 0x22, 0x4a, 0x51, 0x2a, 0xd8, - 0xf8, 0x7c, 0x1a, 0x46, 0xf6, 0x68, 0x13, 0x3d, 0x82, 0x7c, 0xf0, 0x79, 0x44, 0x77, 0x12, 0xf7, - 0xa6, 0xde, 0x0b, 0x41, 0xed, 0xff, 0xd9, 0x8c, 0xd5, 0x95, 0x54, 0x10, 0xa7, 0x66, 0x5b, 0x19, - 0xe2, 0x74, 0x2e, 0xe2, 0x32, 0xc4, 0x09, 0x5f, 0x7d, 0x39, 0x30, 0x19, 0xba, 0x69, 0x42, 0xab, - 0xfd, 0x9c, 0x7b, 0x2e, 0xc1, 0xb4, 0x4a, 0x56, 0x73, 0x15, 0xad, 0x01, 0x13, 0xfe, 0x75, 0x08, - 0xba, 0xdd, 0xc7, 0xb7, 0xeb, 0x86, 0x4b, 0xbb, 0x93, 0xc9, 0x36, 0x1a, 0x84, 0x77, 0xe2, 0xa9, - 0x41, 0x42, 0x17, 0x30, 0xa9, 0x41, 0xc2, 0xad, 0x3d, 0x22, 0x70, 0x39, 0xdc, 0x64, 0xa3, 0x7e, - 0x99, 0x88, 0xb9, 0x00, 0xd0, 0xaa, 0x99, 0xed, 0x55, 0xc0, 0x63, 0x98, 0x8e, 0x36, 0xb0, 0x68, - 0x2d, 0x15, 0xa2, 0xab, 0xc9, 0xd7, 0xd6, 0x07, 0xf0, 0x50, 0x61, 0x3f, 0xc9, 0xc1, 0x4c, 0x4c, - 0x33, 0x89, 0x5e, 0x4d, 0x85, 0x8a, 0x6b, 0xa5, 0xb5, 0xbb, 0x83, 0xba, 0x25, 0xd0, 0x50, 0xbd, - 0x60, 0x66, 0x1a, 0xd1, 0x06, 0x37, 0x33, 0x8d, 0xae, 0x96, 0x13, 0x7d, 0x96, 0x83, 0xf9, 0xf8, - 0xf6, 0x0d, 0xbd, 0x9e, 0x11, 0xb2, 0xa7, 0x45, 0xd5, 0xde, 0x38, 0x83, 0xa7, 0xe2, 0xf3, 0x34, - 0x07, 0x0b, 0x09, 0xdd, 0x0f, 0x4a, 0x87, 0x4d, 0x6a, 0x2e, 0xb5, 0xcd, 0xb3, 0xb8, 0x2a, 0x4a, - 0x9f, 0xe6, 0x60, 0x36, 0xae, 0x6d, 0x40, 0x77, 0x33, 0x82, 0x76, 0xb5, 0x66, 0xda, 0x6b, 0x03, - 0xfb, 0x29, 0x26, 0xa7, 0x70, 0xa5, 0xeb, 0xe0, 0x8f, 0xfa, 0x2d, 0x80, 0xf8, 0x3e, 0x46, 0xdb, - 0x18, 0xc4, 0x45, 0x45, 0xf6, 0x60, 0x2a, 0xb2, 0x0f, 0xa2, 0x6a, 0x7f, 0x90, 0x9e, 0xee, 0x43, - 0x5b, 0xcb, 0xee, 0x10, 0x51, 0x1b, 0xde, 0xbb, 0xd2, 0xd4, 0xc6, 0x6c, 0xc5, 0x69, 0x6a, 0xe3, - 0xb6, 0xc6, 0x1a, 0x7e, 0xf6, 0xa2, 0x98, 0x7b, 0xfe, 0xa2, 0x98, 0xfb, 0xf3, 0x45, 0x31, 0xf7, - 0xf4, 0x65, 0x71, 0xe8, 0xf9, 0xcb, 0xe2, 0xd0, 0x6f, 0x2f, 0x8b, 0x43, 0xb0, 0x68, 0x93, 0x04, - 0xbc, 0xfd, 0xdc, 0x07, 0x95, 0xd0, 0x95, 0x5e, 0xc7, 0x68, 0xd5, 0x26, 0xa1, 0xa7, 0xea, 0x69, - 0xf0, 0xc3, 0x5b, 0x7d, 0x5c, 0xfc, 0xde, 0xf6, 0xca, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xcd, - 0xae, 0xf4, 0x85, 0x83, 0x1c, 0x00, 0x00, + // 1739 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcd, 0x6f, 0x1b, 0xd5, + 0x16, 0x8f, 0xf3, 0xd5, 0xf8, 0xa4, 0x49, 0xdb, 0x9b, 0x2f, 0x67, 0xda, 0x38, 0xae, 0xf3, 0x9e, + 0xd4, 0xd7, 0xbe, 0xd8, 0x49, 0x9e, 0x5e, 0xdf, 0x23, 0x62, 0x41, 0x9c, 0x92, 0xa8, 0x8b, 0x40, + 0x34, 0xa5, 0x20, 0xc1, 0x62, 0x34, 0xf6, 0xdc, 0x3a, 0xa3, 0x8c, 0xe7, 0x3a, 0x73, 0x6f, 0xd2, + 0x74, 0x85, 0x84, 0x90, 0x58, 0x21, 0x75, 0xc9, 0x16, 0xc1, 0x06, 0x84, 0x04, 0x48, 0x6c, 0xe0, + 0x2f, 0xa8, 0x10, 0x8b, 0x8a, 0x15, 0x2b, 0x40, 0xed, 0x82, 0x3f, 0x81, 0x2d, 0xba, 0x1f, 0x33, + 0x9e, 0xb1, 0x67, 0x3c, 0xe3, 0x90, 0xac, 0x92, 0x99, 0x7b, 0xce, 0xf9, 0xfd, 0x7e, 0xe7, 0xdc, + 0x3b, 0xf7, 0x9e, 0x6b, 0x58, 0x6e, 0x7b, 0xe4, 0x04, 0xbb, 0xa6, 0xdb, 0xc0, 0x55, 0x7c, 0xda, + 0x38, 0x30, 0xdd, 0x26, 0xae, 0x9e, 0xac, 0x57, 0xd9, 0x69, 0xa5, 0xed, 0x11, 0x46, 0xd0, 0x7c, + 0xc7, 0xa0, 0xe2, 0x1b, 0x54, 0x4e, 0xd6, 0xb5, 0x62, 0x83, 0xd0, 0x16, 0xa1, 0xd5, 0xba, 0x49, + 0xb9, 0x43, 0x1d, 0x33, 0x73, 0xbd, 0xda, 0x20, 0xb6, 0x2b, 0xfd, 0xb4, 0x05, 0x35, 0xde, 0xa2, + 0x4d, 0x1e, 0xaf, 0x45, 0x9b, 0x6a, 0x60, 0x51, 0x0e, 0x18, 0xe2, 0xa9, 0x2a, 0x1f, 0xd4, 0xd0, + 0x6c, 0x93, 0x34, 0x89, 0x7c, 0xcf, 0xff, 0x53, 0x6f, 0x57, 0x12, 0x28, 0xb6, 0x4c, 0xef, 0x10, + 0xb3, 0x14, 0x23, 0xe2, 0x59, 0xd8, 0xa3, 0x29, 0x46, 0x6d, 0xd3, 0x33, 0x5b, 0xca, 0xa8, 0xfc, + 0x43, 0x0e, 0x66, 0xf6, 0x68, 0x73, 0xdb, 0xc3, 0x26, 0xc3, 0x5b, 0xf4, 0x50, 0xc7, 0x47, 0xc7, + 0x98, 0x32, 0xb4, 0x0d, 0x79, 0x93, 0x1e, 0x1a, 0x22, 0x60, 0x21, 0x57, 0xca, 0xdd, 0x9a, 0xdc, + 0x28, 0x55, 0xe2, 0x93, 0x53, 0xd9, 0xa2, 0x87, 0x6f, 0x72, 0xbb, 0xda, 0xe8, 0xb3, 0x5f, 0x97, + 0x87, 0xf4, 0x09, 0x53, 0x3d, 0xa3, 0x5d, 0x40, 0x22, 0x80, 0xd1, 0xe0, 0xe1, 0x6d, 0xe2, 0x1a, + 0x8f, 0x30, 0x2e, 0x0c, 0x8b, 0x68, 0x8b, 0x15, 0x95, 0x0c, 0x9e, 0xd2, 0x8a, 0x4a, 0x69, 0x65, + 0x9b, 0xd8, 0xae, 0x7e, 0x55, 0x38, 0x6d, 0x2b, 0x9f, 0x1d, 0x8c, 0x37, 0xe7, 0x3e, 0xf8, 0xe3, + 0x9b, 0xdb, 0x57, 0x03, 0x42, 0x15, 0x8a, 0x1d, 0x07, 0x7b, 0xe5, 0x75, 0x98, 0x8d, 0x72, 0xa7, + 0x6d, 0xe2, 0x52, 0x8c, 0x16, 0x61, 0x42, 0xe2, 0xda, 0x96, 0xe0, 0x3e, 0xaa, 0x5f, 0x12, 0xcf, + 0xf7, 0xad, 0xf2, 0xf7, 0x61, 0xbd, 0x35, 0xdb, 0x0a, 0xe9, 0xad, 0xdb, 0x56, 0x36, 0xbd, 0x35, + 0xdb, 0x8a, 0xe8, 0xad, 0xab, 0xe7, 0xf3, 0xd3, 0x3b, 0xcb, 0xf5, 0x5e, 0x09, 0x08, 0x55, 0xea, + 0xc7, 0x4f, 0xba, 0xe4, 0x0a, 0xea, 0xe9, 0x72, 0x8f, 0x60, 0x8e, 0xbb, 0x70, 0x09, 0x8e, 0xe0, + 0xe8, 0xeb, 0x5d, 0x83, 0x71, 0x6a, 0x37, 0x5d, 0x25, 0x36, 0x5f, 0x2b, 0xfc, 0xfc, 0xdd, 0xea, + 0xac, 0x62, 0xb8, 0x65, 0x59, 0x1e, 0xa6, 0xf4, 0x01, 0xf3, 0x6c, 0xb7, 0xa9, 0x2b, 0xbb, 0x08, + 0xca, 0x70, 0x04, 0x65, 0x73, 0x92, 0xd3, 0x55, 0x76, 0xe5, 0x02, 0xcc, 0x77, 0x43, 0x4a, 0x9e, + 0xe5, 0xaf, 0x46, 0x00, 0xed, 0xd1, 0xe6, 0x8e, 0xed, 0x38, 0x35, 0xdb, 0xa2, 0x61, 0x2a, 0xa2, + 0x9e, 0x19, 0xa8, 0x08, 0x3b, 0x74, 0x1d, 0xf2, 0x72, 0x39, 0xf8, 0x5c, 0xa6, 0xf4, 0x09, 0xf9, + 0xe2, 0xbe, 0x85, 0x5c, 0xb8, 0xcc, 0x08, 0x33, 0x1d, 0xc3, 0xa4, 0x14, 0x33, 0x5a, 0x18, 0x29, + 0x8d, 0xf4, 0x4d, 0x7f, 0x6d, 0x8d, 0x57, 0xf1, 0xcb, 0xdf, 0x96, 0x6f, 0x35, 0x6d, 0x76, 0x70, + 0x5c, 0xaf, 0x34, 0x48, 0x4b, 0x2d, 0x54, 0xf5, 0x67, 0x95, 0x5a, 0x87, 0x55, 0xf6, 0xa4, 0x8d, + 0xa9, 0x70, 0xa0, 0xfa, 0xa4, 0x00, 0xd8, 0x12, 0xf1, 0x51, 0x19, 0xa6, 0x82, 0x42, 0x19, 0xb6, + 0x45, 0x0b, 0xa3, 0xa5, 0x91, 0x5b, 0xa3, 0xfa, 0xa4, 0x3f, 0x2b, 0xee, 0x5b, 0x14, 0xbd, 0x0d, + 0x9a, 0xa4, 0x6e, 0x50, 0xcc, 0x98, 0x83, 0x5b, 0xd8, 0x65, 0xc6, 0x23, 0xc7, 0x64, 0x62, 0x82, + 0x8c, 0xa5, 0x4d, 0x90, 0x05, 0xe9, 0xfc, 0x20, 0xf0, 0xdd, 0x71, 0x4c, 0xb6, 0x83, 0x31, 0x7a, + 0x03, 0xe6, 0x83, 0x45, 0x11, 0x9d, 0x74, 0xe3, 0x69, 0x31, 0x67, 0xfc, 0x55, 0x1a, 0x9e, 0x77, + 0xaa, 0x90, 0x72, 0x75, 0xcd, 0x89, 0x95, 0xd2, 0xa9, 0x96, 0xaa, 0xe2, 0xe7, 0x9d, 0x2a, 0x6e, + 0xd1, 0xc3, 0xa0, 0x8a, 0x15, 0x18, 0x13, 0xb3, 0x34, 0xb5, 0x88, 0xd2, 0xac, 0x7f, 0x0d, 0x5f, + 0x03, 0x99, 0x62, 0xa3, 0xed, 0xd9, 0x0d, 0x5c, 0x18, 0x49, 0x11, 0xa3, 0x16, 0x22, 0x08, 0x9f, + 0x7d, 0xee, 0xc2, 0xab, 0xd2, 0xc9, 0x4c, 0xa8, 0x2a, 0xbe, 0x6a, 0x5e, 0x95, 0xf7, 0x61, 0x4e, + 0x70, 0x89, 0x14, 0x05, 0x63, 0x5a, 0x18, 0x3b, 0xff, 0x29, 0x33, 0x23, 0x90, 0x42, 0x15, 0xc4, + 0x98, 0xf2, 0xf2, 0x75, 0xa6, 0xce, 0x80, 0xe5, 0xf3, 0xa7, 0x57, 0xb8, 0x7c, 0xc0, 0xcb, 0x27, + 0xf3, 0x1b, 0xaa, 0x9e, 0xac, 0x92, 0xaa, 0xde, 0x91, 0x58, 0x9d, 0x7b, 0x22, 0xd1, 0x92, 0x4d, + 0xa8, 0x80, 0xa6, 0xd5, 0xb2, 0xdd, 0xf4, 0x02, 0x0a, 0xb3, 0xbe, 0x05, 0x54, 0x4c, 0x84, 0x61, + 0x79, 0x11, 0x16, 0x7a, 0x20, 0x15, 0x9b, 0x3f, 0x73, 0x50, 0x08, 0xc6, 0xde, 0xb1, 0xd9, 0x81, + 0xe5, 0x99, 0x8f, 0x2f, 0x82, 0x10, 0x5a, 0x02, 0x60, 0xc4, 0x30, 0xa5, 0x9f, 0x98, 0x50, 0x79, + 0x3d, 0xcf, 0x88, 0x0a, 0x84, 0x1a, 0x30, 0x6e, 0xb6, 0xc8, 0xb1, 0xcb, 0xc4, 0x3c, 0x39, 0xe7, + 0xda, 0xab, 0xd0, 0x91, 0xa4, 0x5c, 0x87, 0xc5, 0x18, 0xe1, 0x2a, 0x2d, 0x3f, 0xe5, 0x60, 0x29, + 0x18, 0x7d, 0xd8, 0xb6, 0x4c, 0x86, 0xef, 0x61, 0x66, 0xda, 0x0e, 0xbd, 0x90, 0xdc, 0xe8, 0x30, + 0xad, 0x06, 0x2d, 0x89, 0xa2, 0x16, 0xdc, 0x3f, 0x93, 0x36, 0x40, 0x49, 0x4c, 0x51, 0x52, 0x8b, + 0x6f, 0xaa, 0x15, 0x7e, 0x19, 0xd1, 0x5a, 0x82, 0x62, 0x92, 0x1a, 0x25, 0xf8, 0xb3, 0x5e, 0xc1, + 0xaf, 0xbb, 0x66, 0xdd, 0xc1, 0xd6, 0x85, 0x08, 0xfe, 0x17, 0x5c, 0x35, 0x1b, 0x0d, 0xdc, 0x66, + 0xb6, 0xdb, 0x94, 0xab, 0x4f, 0x4a, 0x9e, 0xd0, 0xaf, 0x04, 0xef, 0xc5, 0xe2, 0x4a, 0xd3, 0x11, + 0x90, 0x54, 0x3a, 0xbe, 0xce, 0x41, 0xa9, 0xcb, 0xe4, 0x21, 0xf5, 0x97, 0xfd, 0x85, 0x48, 0xd9, + 0x80, 0x39, 0xd3, 0x71, 0xc8, 0x63, 0xe3, 0x98, 0x46, 0x3e, 0x64, 0x4a, 0xcf, 0x8c, 0x18, 0xec, + 0x70, 0xe0, 0x43, 0x11, 0x4d, 0x2b, 0x70, 0xb3, 0x0f, 0x61, 0x25, 0xeb, 0x8b, 0xe1, 0x90, 0xd5, + 0x9e, 0xe9, 0x9a, 0x4d, 0xbc, 0x8f, 0xbd, 0x96, 0x4d, 0xa9, 0x4d, 0x5c, 0x7a, 0x51, 0xeb, 0xd5, + 0xc3, 0x27, 0xe4, 0x10, 0x1b, 0xa6, 0xe3, 0x88, 0x3d, 0x3c, 0xaf, 0xe7, 0xe5, 0x9b, 0x2d, 0xc7, + 0x41, 0x3b, 0x90, 0x67, 0xc4, 0x90, 0xcf, 0x6a, 0xc9, 0xae, 0x24, 0x1e, 0x4f, 0x1b, 0x0d, 0x4c, + 0xe9, 0xae, 0x67, 0xba, 0xcc, 0x3f, 0xb1, 0x31, 0xa2, 0x0b, 0x57, 0x74, 0x0f, 0x26, 0x18, 0x31, + 0x9a, 0x7c, 0x4c, 0x7d, 0xf5, 0x07, 0x08, 0x73, 0x89, 0x11, 0xf1, 0x18, 0x49, 0xe8, 0x3f, 0xa0, + 0xdc, 0x2f, 0x55, 0x2a, 0xa3, 0xdf, 0x0e, 0x87, 0xe6, 0x92, 0x34, 0xd3, 0xf1, 0xd1, 0x16, 0x63, + 0x1e, 0xbd, 0xa0, 0x19, 0x7f, 0x4d, 0xec, 0x2f, 0xd8, 0xe0, 0xbb, 0xa2, 0xfc, 0x12, 0xaa, 0xac, + 0x4e, 0x37, 0xfc, 0xf3, 0xf3, 0x5b, 0xfc, 0x73, 0x88, 0xaa, 0x30, 0x1b, 0x35, 0xf5, 0x70, 0x8b, + 0x9c, 0xc8, 0x2c, 0xe7, 0xf5, 0x6b, 0x21, 0x6b, 0x5d, 0x0c, 0x84, 0x62, 0xf3, 0xcd, 0x4c, 0xc5, + 0x1e, 0x0b, 0xc7, 0xae, 0xd9, 0x56, 0x77, 0x6c, 0x65, 0xaa, 0x62, 0x8f, 0x87, 0x63, 0x0b, 0x6b, + 0x19, 0x3b, 0x92, 0xd9, 0x9b, 0xb0, 0x9c, 0x98, 0x32, 0x95, 0xd6, 0x4f, 0x73, 0xe2, 0xb3, 0xba, + 0x4b, 0x4e, 0xe4, 0x29, 0x59, 0x1a, 0xfb, 0x19, 0xbd, 0x0b, 0x79, 0xf3, 0x98, 0x1d, 0x10, 0xcf, + 0x66, 0x4f, 0x52, 0xb3, 0xda, 0x31, 0x45, 0xaf, 0xc2, 0xb8, 0x4c, 0xa4, 0x3a, 0xca, 0x17, 0xfb, + 0x7f, 0x17, 0xd5, 0xec, 0x50, 0x3e, 0x9b, 0xd3, 0x5c, 0x42, 0x27, 0x5a, 0xf9, 0x06, 0x68, 0x71, + 0x14, 0x95, 0x82, 0x1f, 0x41, 0xec, 0x96, 0xbb, 0xe4, 0x44, 0x4a, 0xe4, 0xe7, 0x84, 0xbf, 0xcb, + 0xbf, 0xef, 0xcc, 0x78, 0x08, 0x0b, 0xa6, 0x65, 0xf1, 0x43, 0x87, 0x11, 0x2a, 0x3b, 0x3f, 0x9b, + 0xa6, 0x9f, 0x9c, 0xa5, 0xd0, 0x19, 0xd3, 0xb2, 0x76, 0x30, 0x0e, 0xda, 0x30, 0x7e, 0x38, 0x45, + 0xef, 0x81, 0x26, 0x6b, 0x1b, 0x1b, 0x79, 0x34, 0x5b, 0xe4, 0x79, 0x19, 0xa2, 0x27, 0x78, 0x2f, + 0x67, 0x3e, 0x9d, 0x44, 0xe4, 0xb1, 0x33, 0x70, 0xae, 0xd9, 0x56, 0x32, 0xe7, 0x20, 0xf2, 0xf8, + 0xd9, 0x38, 0xfb, 0xc1, 0x1b, 0x50, 0xf4, 0x39, 0xc7, 0xb7, 0x02, 0x85, 0x4b, 0xd9, 0x00, 0x34, + 0x49, 0xfd, 0x41, 0x4c, 0x4b, 0x80, 0x6c, 0xb8, 0x19, 0x52, 0x90, 0x80, 0x33, 0x91, 0x0d, 0x67, + 0x29, 0x10, 0x12, 0x0b, 0xe5, 0x42, 0x29, 0x59, 0x8f, 0xc7, 0x8f, 0xa4, 0xb4, 0x90, 0x17, 0x48, + 0x89, 0x7d, 0xf4, 0x0e, 0xc6, 0x3a, 0x37, 0x54, 0x80, 0x37, 0xe2, 0x85, 0x09, 0x13, 0x8a, 0x18, + 0xac, 0xf4, 0x95, 0xa6, 0x20, 0x61, 0x20, 0xc8, 0xe5, 0x44, 0x8d, 0x0a, 0xd5, 0x84, 0x25, 0x5f, + 0x65, 0x6f, 0xab, 0xc0, 0x93, 0x39, 0x99, 0x2d, 0x99, 0x8b, 0x52, 0x5b, 0xad, 0xab, 0x09, 0xe0, + 0x89, 0x6c, 0x42, 0x29, 0x24, 0x2c, 0x1e, 0xe5, 0x72, 0x36, 0x94, 0x1b, 0x81, 0x9c, 0x38, 0x20, + 0x07, 0x96, 0x13, 0xb5, 0xa8, 0xec, 0x4d, 0x0d, 0x94, 0xbd, 0xeb, 0xb1, 0xa2, 0x54, 0xe6, 0x3c, + 0x28, 0xf7, 0x93, 0xa5, 0x00, 0xa7, 0x07, 0x02, 0x2c, 0x26, 0xe9, 0x93, 0x98, 0x3d, 0x9f, 0x5a, + 0x4d, 0x74, 0x17, 0x5d, 0xdf, 0xd2, 0x9e, 0xad, 0x42, 0x1e, 0x7b, 0xf6, 0xc5, 0xad, 0xd8, 0x39, + 0x6c, 0x15, 0xf2, 0x7a, 0x2d, 0x6d, 0xab, 0x90, 0x70, 0xfe, 0x56, 0x21, 0x7d, 0x92, 0xb7, 0x8a, + 0x28, 0x45, 0xa9, 0x60, 0xe3, 0x93, 0x69, 0x18, 0xd9, 0xa3, 0x4d, 0xf4, 0x08, 0xf2, 0xc1, 0xe7, + 0x11, 0xdd, 0x49, 0xdc, 0x9b, 0x7a, 0x2f, 0xf9, 0xb4, 0x7f, 0x67, 0x33, 0x56, 0xd7, 0x4c, 0x01, + 0x4e, 0xcd, 0xb6, 0x32, 0xe0, 0x74, 0x2e, 0xd7, 0x32, 0xe0, 0x84, 0xaf, 0xb3, 0x1c, 0x98, 0x0c, + 0xdd, 0x1e, 0xa1, 0xd5, 0x7e, 0xce, 0x3d, 0x17, 0x5b, 0x5a, 0x25, 0xab, 0xb9, 0x42, 0x6b, 0xc0, + 0x84, 0x7f, 0xc5, 0x81, 0x6e, 0xf7, 0xf1, 0xed, 0xba, 0xb5, 0xd2, 0xee, 0x64, 0xb2, 0x8d, 0x82, + 0xf0, 0x4e, 0x3c, 0x15, 0x24, 0x74, 0xa9, 0x92, 0x0a, 0x12, 0x6e, 0xed, 0x11, 0x81, 0xcb, 0xe1, + 0x26, 0x1b, 0xf5, 0xcb, 0x44, 0xcc, 0x05, 0x80, 0x56, 0xcd, 0x6c, 0xaf, 0x00, 0x8f, 0x61, 0x3a, + 0xda, 0xc0, 0xa2, 0xb5, 0xd4, 0x10, 0x5d, 0x4d, 0xbe, 0xb6, 0x3e, 0x80, 0x87, 0x82, 0xfd, 0x30, + 0x07, 0x33, 0x31, 0xcd, 0x24, 0xfa, 0x6f, 0x6a, 0xa8, 0xb8, 0x56, 0x5a, 0xbb, 0x3b, 0xa8, 0x5b, + 0x02, 0x0d, 0xd5, 0x0b, 0x66, 0xa6, 0x11, 0x6d, 0x70, 0x33, 0xd3, 0xe8, 0x6a, 0x39, 0xd1, 0xc7, + 0x39, 0x98, 0x8f, 0x6f, 0xdf, 0xd0, 0xff, 0x33, 0x86, 0xec, 0x69, 0x51, 0xb5, 0x57, 0xce, 0xe0, + 0xa9, 0xf8, 0x3c, 0xcd, 0xc1, 0x42, 0x42, 0xf7, 0x83, 0xd2, 0xc3, 0x26, 0x35, 0x97, 0xda, 0xe6, + 0x59, 0x5c, 0x15, 0xa5, 0x8f, 0x72, 0x30, 0x1b, 0xd7, 0x36, 0xa0, 0xbb, 0x19, 0x83, 0x76, 0xb5, + 0x66, 0xda, 0xff, 0x06, 0xf6, 0x53, 0x4c, 0x4e, 0xe1, 0x4a, 0xd7, 0xc1, 0x1f, 0xf5, 0x5b, 0x00, + 0xf1, 0x7d, 0x8c, 0xb6, 0x31, 0x88, 0x8b, 0x42, 0xf6, 0x60, 0x2a, 0xb2, 0x0f, 0xa2, 0x6a, 0xff, + 0x20, 0x3d, 0xdd, 0x87, 0xb6, 0x96, 0xdd, 0x21, 0xa2, 0x36, 0xbc, 0x77, 0xa5, 0xa9, 0x8d, 0xd9, + 0x8a, 0xd3, 0xd4, 0xc6, 0x6d, 0x8d, 0x35, 0xfc, 0xec, 0x45, 0x31, 0xf7, 0xfc, 0x45, 0x31, 0xf7, + 0xfb, 0x8b, 0x62, 0xee, 0xe9, 0xcb, 0xe2, 0xd0, 0xf3, 0x97, 0xc5, 0xa1, 0x5f, 0x5e, 0x16, 0x87, + 0x60, 0xd1, 0x26, 0x09, 0xf1, 0xf6, 0x73, 0xef, 0x56, 0x42, 0x57, 0x7a, 0x1d, 0xa3, 0x55, 0x9b, + 0x84, 0x9e, 0xaa, 0xa7, 0xc1, 0x8f, 0x69, 0xf5, 0x71, 0xf1, 0x1b, 0xda, 0x7f, 0xfe, 0x0a, 0x00, + 0x00, 0xff, 0xff, 0x18, 0x1e, 0x88, 0x85, 0x57, 0x1c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2875,20 +2875,16 @@ func (m *MsgFillAsksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x22 } - if len(m.TotalPrice) > 0 { - for iNdEx := len(m.TotalPrice) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.TotalPrice[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a + { + size, err := m.TotalPrice.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x1a if m.MarketId != 0 { i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) i-- @@ -3958,12 +3954,8 @@ func (m *MsgFillAsksRequest) Size() (n int) { if m.MarketId != 0 { n += 1 + sovTx(uint64(m.MarketId)) } - if len(m.TotalPrice) > 0 { - for _, e := range m.TotalPrice { - l = e.Size() - n += 1 + l + sovTx(uint64(l)) - } - } + l = m.TotalPrice.Size() + n += 1 + l + sovTx(uint64(l)) if len(m.AskOrderIds) > 0 { l = 0 for _, e := range m.AskOrderIds { @@ -5348,8 +5340,7 @@ func (m *MsgFillAsksRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.TotalPrice = append(m.TotalPrice, types.Coin{}) - if err := m.TotalPrice[len(m.TotalPrice)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.TotalPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex From 52063e36f4a8e7fddfe45ef672d242219f66c482 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 19:57:33 -0600 Subject: [PATCH 136/309] [1658]: Implement FillAsks. --- x/exchange/keeper/msg_server.go | 8 +- x/exchange/keeper/orders.go | 158 ++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+), 2 deletions(-) diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 6fa20b5aec..c1af8d1b50 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -66,8 +66,12 @@ func (k MsgServer) FillBids(goCtx context.Context, msg *exchange.MsgFillBidsRequ // FillAsks uses the funds in your account to fulfill one or more asks (similar to a fill-or-cancel bid). func (k MsgServer) FillAsks(goCtx context.Context, msg *exchange.MsgFillAsksRequest) (*exchange.MsgFillAsksResponse, error) { - // TODO[1658]: Implement FillAsks - panic("not implemented") + ctx := sdk.UnwrapSDKContext(goCtx) + err := k.Keeper.FillAsks(ctx, msg) + if err != nil { + return nil, err + } + return &exchange.MsgFillAsksResponse{}, nil } // MarketSettle is a market endpoint to trigger the settlement of orders. diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index c0d12bd838..242be3dc1f 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -102,6 +102,7 @@ func createIndexEntries(order exchange.Order) []sdk.KVPair { return rv } +// getOrderFromStore looks up an order from the store. Returns nil, nil if the order does not exist. func (k Keeper) getOrderFromStore(store sdk.KVStore, orderID uint64) (*exchange.Order, error) { key := MakeKeyOrder(orderID) value := store.Get(key) @@ -512,6 +513,163 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro return nil } +// FillAsks settles one or more ask orders for a buyer. +func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) error { + if err := msg.ValidateBasic(); err != nil { + return err + } + + marketID := msg.MarketId + store := k.getStore(ctx) + + if err := validateMarketExists(store, marketID); err != nil { + return err + } + if !isMarketActive(store, marketID) { + return fmt.Errorf("market %d is not accepting orders", marketID) + } + if !isUserSettlementAllowed(store, marketID) { + return fmt.Errorf("market %d does not allow user settlement", marketID) + } + buyer := sdk.MustAccAddressFromBech32(msg.Buyer) + if !k.CanCreateBid(ctx, marketID, buyer) { + return fmt.Errorf("account %s is not allowed to create bid orders in market %d", buyer, marketID) + } + if err := validateCreateBidFlatFee(store, marketID, msg.BidOrderCreationFee); err != nil { + return err + } + if err := validateBuyerSettlementFee(store, marketID, msg.TotalPrice, msg.BuyerSettlementFees); err != nil { + return err + } + + var errs []error + orders := make([]*exchange.Order, 0, len(msg.AskOrderIds)) + var totalAssets, totalPrice sdk.Coins + assetInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)) + priceOutputs := make([]banktypes.Output, 0, len(msg.AskOrderIds)) + addrIndex := make(map[string]int) + feeInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)+1) + feeAddrIndex := make(map[string]int) + for _, orderID := range msg.AskOrderIds { + order, oerr := k.getOrderFromStore(store, orderID) + if oerr != nil { + errs = append(errs, oerr) + continue + } + if order == nil { + errs = append(errs, fmt.Errorf("order %d not found", orderID)) + continue + } + if !order.IsAskOrder() { + errs = append(errs, fmt.Errorf("order %d is type %s: expected ask", orderID, order.GetOrderType())) + continue + } + + askOrder := order.GetAskOrder() + orderMarketID := askOrder.MarketId + seller := askOrder.Seller + assets := askOrder.Assets + price := askOrder.Price + heldAmount := askOrder.GetHoldAmount() + sellerSettlementFlatFee := askOrder.SellerSettlementFlatFee + + if orderMarketID != marketID { + errs = append(errs, fmt.Errorf("order %d market id %d does not equal requested market id %d", orderID, orderMarketID, marketID)) + continue + } + if seller == msg.Buyer { + errs = append(errs, fmt.Errorf("order %d has the same seller %s as the requested buyer", orderID, buyer)) + continue + } + sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, price) + if rerr != nil { + errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", orderID, rerr)) + continue + } + sellerAddr, aerr := sdk.AccAddressFromBech32(seller) + if aerr != nil { + errs = append(errs, fmt.Errorf("invalid seller %q in order %d: %w", buyer, orderID, aerr)) + continue + } + if err := k.holdKeeper.ReleaseHold(ctx, sellerAddr, heldAmount); err != nil { + errs = append(errs, fmt.Errorf("error releasing hold for order %d: %w", orderID, err)) + continue + } + + orders = append(orders, order) + totalAssets = totalAssets.Add(assets...) + totalPrice = totalPrice.Add(price) + var totalSellerFee sdk.Coins + if sellerSettlementFlatFee != nil && !sellerSettlementFlatFee.IsZero() { + totalSellerFee = totalSellerFee.Add(*sellerSettlementFlatFee) + } + if sellerRatioFee != nil && !sellerRatioFee.IsZero() { + totalSellerFee = totalSellerFee.Add(*sellerRatioFee) + } + + i, seen := addrIndex[seller] + if !seen { + i = len(assetInputs) + addrIndex[seller] = i + assetInputs = append(assetInputs, banktypes.Input{Address: seller}) + priceOutputs = append(priceOutputs, banktypes.Output{Address: seller}) + } + assetInputs[i].Coins = assetInputs[i].Coins.Add(assets...) + priceOutputs[i].Coins = priceOutputs[i].Coins.Add(price) + + if !totalSellerFee.IsZero() { + j, known := feeAddrIndex[seller] + if !known { + j = len(feeInputs) + feeAddrIndex[seller] = j + feeInputs = append(feeInputs, banktypes.Input{Address: seller}) + } + feeInputs[j].Coins = feeInputs[j].Coins.Add(totalSellerFee...) + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + + if !safeCoinsEquals(totalPrice, sdk.Coins{msg.TotalPrice}) { + return fmt.Errorf("total price %q does not equal sum of ask order prices %q", msg.TotalPrice, totalPrice) + } + + if !msg.BuyerSettlementFees.IsZero() { + feeInputs = append(feeInputs, banktypes.Input{Address: msg.Buyer, Coins: msg.BuyerSettlementFees}) + } + + assetOutputs := []banktypes.Output{{Address: msg.Buyer, Coins: totalAssets}} + priceInputs := []banktypes.Input{{Address: msg.Buyer, Coins: sdk.Coins{msg.TotalPrice}}} + + if err := k.bankKeeper.InputOutputCoins(ctx, assetInputs, assetOutputs); err != nil { + return fmt.Errorf("error transferring assets from sellers to buyer: %w", err) + } + + if err := k.bankKeeper.InputOutputCoins(ctx, priceInputs, priceOutputs); err != nil { + return fmt.Errorf("error transferring price from buyer to sellers: %w", err) + } + + if err := k.CollectFees(ctx, feeInputs, marketID); err != nil { + return fmt.Errorf("error collecting settlement fees: %w", err) + } + + // Collected last so that it's easier for a seller to fill asks without needing those funds first. + // Collected separately so it's not combined with the buyer settlement fees in the events. + if msg.BidOrderCreationFee != nil { + if err := k.CollectFee(ctx, buyer, marketID, sdk.Coins{*msg.BidOrderCreationFee}); err != nil { + return fmt.Errorf("error collecting create-ask fee %q: %w", msg.BidOrderCreationFee, err) + } + } + + for _, order := range orders { + deleteAndDeIndexOrder(store, *order) + } + + return nil +} + // safeCoinsEquals returns true if the two provided coins are equal. // Returns false instead of panicking like sdk.Coins.IsEqual. func safeCoinsEquals(a, b sdk.Coins) (isEqual bool) { From 425b67d61e9de5f7e31253d7e945fc430896afc1 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 19:59:40 -0600 Subject: [PATCH 137/309] [1658]: Add missing comment on AccessGrant.Contains. --- x/exchange/market.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/exchange/market.go b/x/exchange/market.go index 7b6ae97afe..bae42ae3ab 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -383,6 +383,7 @@ func (a AccessGrant) ValidateInField(field string) error { return nil } +// Contains returns true if this access grant contains the provided permission. func (a AccessGrant) Contains(perm Permission) bool { for _, p := range a.Permissions { if p == perm { From e4723ff672fbfd969b82c1b60e416ec25d64c601 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 18 Sep 2023 20:03:23 -0600 Subject: [PATCH 138/309] [1658]: Fix the GetPrice unit test to look for the correct panic message. --- x/exchange/orders_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 114d57a0cd..372785196d 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -630,12 +630,12 @@ func TestOrder_GetPrice(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: "GetAssets() missing case for ", + expPanic: "GetPrice() missing case for ", }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: "GetAssets() missing case for *exchange.unknownOrderType", + expPanic: "GetPrice() missing case for *exchange.unknownOrderType", }, } From 65983b1a408c3fb6306cb8d8fe00fcbdd31640c0 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 11:24:13 -0600 Subject: [PATCH 139/309] [1658]: Add fields to the MsgMarketSettleRequest message. --- docs/proto-docs.md | 7 +- proto/provenance/exchange/v1/tx.proto | 10 +- x/exchange/tx.pb.go | 486 ++++++++++++++++++++------ 3 files changed, 389 insertions(+), 114 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index cd63b66f52..3c8b552e23 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2427,9 +2427,10 @@ MsgMarketSettleRequest is a request message for the MarketSettle endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `admin` | [string](#string) | | admin is the account with "settle" permission requesting this settlement. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. - -TODO[1658]: MsgMarketSettleRequest | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | +| `ask_order_ids` | [uint64](#uint64) | repeated | ask_order_ids are the ask orders being filled. | +| `bid_order_ids` | [uint64](#uint64) | repeated | bid_order_ids are the bid orders being filled. | +| `expect_partial` | [bool](#bool) | | expect_partial is whether to expect an order to only be partially filled. Set to true to indicate that either the last ask order, or last bid order will be partially filled by this settlement. Set to false to indicate that all provided orders will be filled in full during this settlement. | diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index e48ef3a2b9..e78689fdf6 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -168,7 +168,15 @@ message MsgMarketSettleRequest { string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // market_id is the numerical identifier of the market to update required attributes for. uint32 market_id = 2; - // TODO[1658]: MsgMarketSettleRequest + + // ask_order_ids are the ask orders being filled. + repeated uint64 ask_order_ids = 3; + // bid_order_ids are the bid orders being filled. + repeated uint64 bid_order_ids = 4; + // expect_partial is whether to expect an order to only be partially filled. Set to true to indicate that either + // the last ask order, or last bid order will be partially filled by this settlement. Set to false to indicate + // that all provided orders will be filled in full during this settlement. + bool expect_partial = 5; } // MsgMarketSettleResponse is a response message for the MarketSettle endpoint. diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 845e601c95..a06b3317aa 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -597,6 +597,14 @@ type MsgMarketSettleRequest struct { Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` // market_id is the numerical identifier of the market to update required attributes for. MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // ask_order_ids are the ask orders being filled. + AskOrderIds []uint64 `protobuf:"varint,3,rep,packed,name=ask_order_ids,json=askOrderIds,proto3" json:"ask_order_ids,omitempty"` + // bid_order_ids are the bid orders being filled. + BidOrderIds []uint64 `protobuf:"varint,4,rep,packed,name=bid_order_ids,json=bidOrderIds,proto3" json:"bid_order_ids,omitempty"` + // expect_partial is whether to expect an order to only be partially filled. Set to true to indicate that either + // the last ask order, or last bid order will be partially filled by this settlement. Set to false to indicate + // that all provided orders will be filled in full during this settlement. + ExpectPartial bool `protobuf:"varint,5,opt,name=expect_partial,json=expectPartial,proto3" json:"expect_partial,omitempty"` } func (m *MsgMarketSettleRequest) Reset() { *m = MsgMarketSettleRequest{} } @@ -646,6 +654,27 @@ func (m *MsgMarketSettleRequest) GetMarketId() uint32 { return 0 } +func (m *MsgMarketSettleRequest) GetAskOrderIds() []uint64 { + if m != nil { + return m.AskOrderIds + } + return nil +} + +func (m *MsgMarketSettleRequest) GetBidOrderIds() []uint64 { + if m != nil { + return m.BidOrderIds + } + return nil +} + +func (m *MsgMarketSettleRequest) GetExpectPartial() bool { + if m != nil { + return m.ExpectPartial + } + return false +} + // MsgMarketSettleResponse is a response message for the MarketSettle endpoint. type MsgMarketSettleResponse struct { } @@ -1767,116 +1796,118 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1739 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcd, 0x6f, 0x1b, 0xd5, - 0x16, 0x8f, 0xf3, 0xd5, 0xf8, 0xa4, 0x49, 0xdb, 0x9b, 0x2f, 0x67, 0xda, 0x38, 0xae, 0xf3, 0x9e, - 0xd4, 0xd7, 0xbe, 0xd8, 0x49, 0x9e, 0x5e, 0xdf, 0x23, 0x62, 0x41, 0x9c, 0x92, 0xa8, 0x8b, 0x40, - 0x34, 0xa5, 0x20, 0xc1, 0x62, 0x34, 0xf6, 0xdc, 0x3a, 0xa3, 0x8c, 0xe7, 0x3a, 0x73, 0x6f, 0xd2, - 0x74, 0x85, 0x84, 0x90, 0x58, 0x21, 0x75, 0xc9, 0x16, 0xc1, 0x06, 0x84, 0x04, 0x48, 0x6c, 0xe0, - 0x2f, 0xa8, 0x10, 0x8b, 0x8a, 0x15, 0x2b, 0x40, 0xed, 0x82, 0x3f, 0x81, 0x2d, 0xba, 0x1f, 0x33, - 0x9e, 0xb1, 0x67, 0x3c, 0xe3, 0x90, 0xac, 0x92, 0x99, 0x7b, 0xce, 0xf9, 0xfd, 0x7e, 0xe7, 0xdc, - 0x3b, 0xf7, 0x9e, 0x6b, 0x58, 0x6e, 0x7b, 0xe4, 0x04, 0xbb, 0xa6, 0xdb, 0xc0, 0x55, 0x7c, 0xda, - 0x38, 0x30, 0xdd, 0x26, 0xae, 0x9e, 0xac, 0x57, 0xd9, 0x69, 0xa5, 0xed, 0x11, 0x46, 0xd0, 0x7c, - 0xc7, 0xa0, 0xe2, 0x1b, 0x54, 0x4e, 0xd6, 0xb5, 0x62, 0x83, 0xd0, 0x16, 0xa1, 0xd5, 0xba, 0x49, - 0xb9, 0x43, 0x1d, 0x33, 0x73, 0xbd, 0xda, 0x20, 0xb6, 0x2b, 0xfd, 0xb4, 0x05, 0x35, 0xde, 0xa2, - 0x4d, 0x1e, 0xaf, 0x45, 0x9b, 0x6a, 0x60, 0x51, 0x0e, 0x18, 0xe2, 0xa9, 0x2a, 0x1f, 0xd4, 0xd0, - 0x6c, 0x93, 0x34, 0x89, 0x7c, 0xcf, 0xff, 0x53, 0x6f, 0x57, 0x12, 0x28, 0xb6, 0x4c, 0xef, 0x10, - 0xb3, 0x14, 0x23, 0xe2, 0x59, 0xd8, 0xa3, 0x29, 0x46, 0x6d, 0xd3, 0x33, 0x5b, 0xca, 0xa8, 0xfc, - 0x43, 0x0e, 0x66, 0xf6, 0x68, 0x73, 0xdb, 0xc3, 0x26, 0xc3, 0x5b, 0xf4, 0x50, 0xc7, 0x47, 0xc7, - 0x98, 0x32, 0xb4, 0x0d, 0x79, 0x93, 0x1e, 0x1a, 0x22, 0x60, 0x21, 0x57, 0xca, 0xdd, 0x9a, 0xdc, - 0x28, 0x55, 0xe2, 0x93, 0x53, 0xd9, 0xa2, 0x87, 0x6f, 0x72, 0xbb, 0xda, 0xe8, 0xb3, 0x5f, 0x97, - 0x87, 0xf4, 0x09, 0x53, 0x3d, 0xa3, 0x5d, 0x40, 0x22, 0x80, 0xd1, 0xe0, 0xe1, 0x6d, 0xe2, 0x1a, - 0x8f, 0x30, 0x2e, 0x0c, 0x8b, 0x68, 0x8b, 0x15, 0x95, 0x0c, 0x9e, 0xd2, 0x8a, 0x4a, 0x69, 0x65, - 0x9b, 0xd8, 0xae, 0x7e, 0x55, 0x38, 0x6d, 0x2b, 0x9f, 0x1d, 0x8c, 0x37, 0xe7, 0x3e, 0xf8, 0xe3, - 0x9b, 0xdb, 0x57, 0x03, 0x42, 0x15, 0x8a, 0x1d, 0x07, 0x7b, 0xe5, 0x75, 0x98, 0x8d, 0x72, 0xa7, - 0x6d, 0xe2, 0x52, 0x8c, 0x16, 0x61, 0x42, 0xe2, 0xda, 0x96, 0xe0, 0x3e, 0xaa, 0x5f, 0x12, 0xcf, - 0xf7, 0xad, 0xf2, 0xf7, 0x61, 0xbd, 0x35, 0xdb, 0x0a, 0xe9, 0xad, 0xdb, 0x56, 0x36, 0xbd, 0x35, - 0xdb, 0x8a, 0xe8, 0xad, 0xab, 0xe7, 0xf3, 0xd3, 0x3b, 0xcb, 0xf5, 0x5e, 0x09, 0x08, 0x55, 0xea, - 0xc7, 0x4f, 0xba, 0xe4, 0x0a, 0xea, 0xe9, 0x72, 0x8f, 0x60, 0x8e, 0xbb, 0x70, 0x09, 0x8e, 0xe0, - 0xe8, 0xeb, 0x5d, 0x83, 0x71, 0x6a, 0x37, 0x5d, 0x25, 0x36, 0x5f, 0x2b, 0xfc, 0xfc, 0xdd, 0xea, - 0xac, 0x62, 0xb8, 0x65, 0x59, 0x1e, 0xa6, 0xf4, 0x01, 0xf3, 0x6c, 0xb7, 0xa9, 0x2b, 0xbb, 0x08, - 0xca, 0x70, 0x04, 0x65, 0x73, 0x92, 0xd3, 0x55, 0x76, 0xe5, 0x02, 0xcc, 0x77, 0x43, 0x4a, 0x9e, - 0xe5, 0xaf, 0x46, 0x00, 0xed, 0xd1, 0xe6, 0x8e, 0xed, 0x38, 0x35, 0xdb, 0xa2, 0x61, 0x2a, 0xa2, - 0x9e, 0x19, 0xa8, 0x08, 0x3b, 0x74, 0x1d, 0xf2, 0x72, 0x39, 0xf8, 0x5c, 0xa6, 0xf4, 0x09, 0xf9, - 0xe2, 0xbe, 0x85, 0x5c, 0xb8, 0xcc, 0x08, 0x33, 0x1d, 0xc3, 0xa4, 0x14, 0x33, 0x5a, 0x18, 0x29, - 0x8d, 0xf4, 0x4d, 0x7f, 0x6d, 0x8d, 0x57, 0xf1, 0xcb, 0xdf, 0x96, 0x6f, 0x35, 0x6d, 0x76, 0x70, - 0x5c, 0xaf, 0x34, 0x48, 0x4b, 0x2d, 0x54, 0xf5, 0x67, 0x95, 0x5a, 0x87, 0x55, 0xf6, 0xa4, 0x8d, - 0xa9, 0x70, 0xa0, 0xfa, 0xa4, 0x00, 0xd8, 0x12, 0xf1, 0x51, 0x19, 0xa6, 0x82, 0x42, 0x19, 0xb6, - 0x45, 0x0b, 0xa3, 0xa5, 0x91, 0x5b, 0xa3, 0xfa, 0xa4, 0x3f, 0x2b, 0xee, 0x5b, 0x14, 0xbd, 0x0d, - 0x9a, 0xa4, 0x6e, 0x50, 0xcc, 0x98, 0x83, 0x5b, 0xd8, 0x65, 0xc6, 0x23, 0xc7, 0x64, 0x62, 0x82, - 0x8c, 0xa5, 0x4d, 0x90, 0x05, 0xe9, 0xfc, 0x20, 0xf0, 0xdd, 0x71, 0x4c, 0xb6, 0x83, 0x31, 0x7a, - 0x03, 0xe6, 0x83, 0x45, 0x11, 0x9d, 0x74, 0xe3, 0x69, 0x31, 0x67, 0xfc, 0x55, 0x1a, 0x9e, 0x77, - 0xaa, 0x90, 0x72, 0x75, 0xcd, 0x89, 0x95, 0xd2, 0xa9, 0x96, 0xaa, 0xe2, 0xe7, 0x9d, 0x2a, 0x6e, - 0xd1, 0xc3, 0xa0, 0x8a, 0x15, 0x18, 0x13, 0xb3, 0x34, 0xb5, 0x88, 0xd2, 0xac, 0x7f, 0x0d, 0x5f, - 0x03, 0x99, 0x62, 0xa3, 0xed, 0xd9, 0x0d, 0x5c, 0x18, 0x49, 0x11, 0xa3, 0x16, 0x22, 0x08, 0x9f, - 0x7d, 0xee, 0xc2, 0xab, 0xd2, 0xc9, 0x4c, 0xa8, 0x2a, 0xbe, 0x6a, 0x5e, 0x95, 0xf7, 0x61, 0x4e, - 0x70, 0x89, 0x14, 0x05, 0x63, 0x5a, 0x18, 0x3b, 0xff, 0x29, 0x33, 0x23, 0x90, 0x42, 0x15, 0xc4, - 0x98, 0xf2, 0xf2, 0x75, 0xa6, 0xce, 0x80, 0xe5, 0xf3, 0xa7, 0x57, 0xb8, 0x7c, 0xc0, 0xcb, 0x27, - 0xf3, 0x1b, 0xaa, 0x9e, 0xac, 0x92, 0xaa, 0xde, 0x91, 0x58, 0x9d, 0x7b, 0x22, 0xd1, 0x92, 0x4d, - 0xa8, 0x80, 0xa6, 0xd5, 0xb2, 0xdd, 0xf4, 0x02, 0x0a, 0xb3, 0xbe, 0x05, 0x54, 0x4c, 0x84, 0x61, - 0x79, 0x11, 0x16, 0x7a, 0x20, 0x15, 0x9b, 0x3f, 0x73, 0x50, 0x08, 0xc6, 0xde, 0xb1, 0xd9, 0x81, - 0xe5, 0x99, 0x8f, 0x2f, 0x82, 0x10, 0x5a, 0x02, 0x60, 0xc4, 0x30, 0xa5, 0x9f, 0x98, 0x50, 0x79, - 0x3d, 0xcf, 0x88, 0x0a, 0x84, 0x1a, 0x30, 0x6e, 0xb6, 0xc8, 0xb1, 0xcb, 0xc4, 0x3c, 0x39, 0xe7, - 0xda, 0xab, 0xd0, 0x91, 0xa4, 0x5c, 0x87, 0xc5, 0x18, 0xe1, 0x2a, 0x2d, 0x3f, 0xe5, 0x60, 0x29, - 0x18, 0x7d, 0xd8, 0xb6, 0x4c, 0x86, 0xef, 0x61, 0x66, 0xda, 0x0e, 0xbd, 0x90, 0xdc, 0xe8, 0x30, - 0xad, 0x06, 0x2d, 0x89, 0xa2, 0x16, 0xdc, 0x3f, 0x93, 0x36, 0x40, 0x49, 0x4c, 0x51, 0x52, 0x8b, - 0x6f, 0xaa, 0x15, 0x7e, 0x19, 0xd1, 0x5a, 0x82, 0x62, 0x92, 0x1a, 0x25, 0xf8, 0xb3, 0x5e, 0xc1, - 0xaf, 0xbb, 0x66, 0xdd, 0xc1, 0xd6, 0x85, 0x08, 0xfe, 0x17, 0x5c, 0x35, 0x1b, 0x0d, 0xdc, 0x66, - 0xb6, 0xdb, 0x94, 0xab, 0x4f, 0x4a, 0x9e, 0xd0, 0xaf, 0x04, 0xef, 0xc5, 0xe2, 0x4a, 0xd3, 0x11, - 0x90, 0x54, 0x3a, 0xbe, 0xce, 0x41, 0xa9, 0xcb, 0xe4, 0x21, 0xf5, 0x97, 0xfd, 0x85, 0x48, 0xd9, - 0x80, 0x39, 0xd3, 0x71, 0xc8, 0x63, 0xe3, 0x98, 0x46, 0x3e, 0x64, 0x4a, 0xcf, 0x8c, 0x18, 0xec, - 0x70, 0xe0, 0x43, 0x11, 0x4d, 0x2b, 0x70, 0xb3, 0x0f, 0x61, 0x25, 0xeb, 0x8b, 0xe1, 0x90, 0xd5, - 0x9e, 0xe9, 0x9a, 0x4d, 0xbc, 0x8f, 0xbd, 0x96, 0x4d, 0xa9, 0x4d, 0x5c, 0x7a, 0x51, 0xeb, 0xd5, - 0xc3, 0x27, 0xe4, 0x10, 0x1b, 0xa6, 0xe3, 0x88, 0x3d, 0x3c, 0xaf, 0xe7, 0xe5, 0x9b, 0x2d, 0xc7, - 0x41, 0x3b, 0x90, 0x67, 0xc4, 0x90, 0xcf, 0x6a, 0xc9, 0xae, 0x24, 0x1e, 0x4f, 0x1b, 0x0d, 0x4c, - 0xe9, 0xae, 0x67, 0xba, 0xcc, 0x3f, 0xb1, 0x31, 0xa2, 0x0b, 0x57, 0x74, 0x0f, 0x26, 0x18, 0x31, - 0x9a, 0x7c, 0x4c, 0x7d, 0xf5, 0x07, 0x08, 0x73, 0x89, 0x11, 0xf1, 0x18, 0x49, 0xe8, 0x3f, 0xa0, - 0xdc, 0x2f, 0x55, 0x2a, 0xa3, 0xdf, 0x0e, 0x87, 0xe6, 0x92, 0x34, 0xd3, 0xf1, 0xd1, 0x16, 0x63, - 0x1e, 0xbd, 0xa0, 0x19, 0x7f, 0x4d, 0xec, 0x2f, 0xd8, 0xe0, 0xbb, 0xa2, 0xfc, 0x12, 0xaa, 0xac, - 0x4e, 0x37, 0xfc, 0xf3, 0xf3, 0x5b, 0xfc, 0x73, 0x88, 0xaa, 0x30, 0x1b, 0x35, 0xf5, 0x70, 0x8b, - 0x9c, 0xc8, 0x2c, 0xe7, 0xf5, 0x6b, 0x21, 0x6b, 0x5d, 0x0c, 0x84, 0x62, 0xf3, 0xcd, 0x4c, 0xc5, - 0x1e, 0x0b, 0xc7, 0xae, 0xd9, 0x56, 0x77, 0x6c, 0x65, 0xaa, 0x62, 0x8f, 0x87, 0x63, 0x0b, 0x6b, - 0x19, 0x3b, 0x92, 0xd9, 0x9b, 0xb0, 0x9c, 0x98, 0x32, 0x95, 0xd6, 0x4f, 0x73, 0xe2, 0xb3, 0xba, - 0x4b, 0x4e, 0xe4, 0x29, 0x59, 0x1a, 0xfb, 0x19, 0xbd, 0x0b, 0x79, 0xf3, 0x98, 0x1d, 0x10, 0xcf, - 0x66, 0x4f, 0x52, 0xb3, 0xda, 0x31, 0x45, 0xaf, 0xc2, 0xb8, 0x4c, 0xa4, 0x3a, 0xca, 0x17, 0xfb, - 0x7f, 0x17, 0xd5, 0xec, 0x50, 0x3e, 0x9b, 0xd3, 0x5c, 0x42, 0x27, 0x5a, 0xf9, 0x06, 0x68, 0x71, - 0x14, 0x95, 0x82, 0x1f, 0x41, 0xec, 0x96, 0xbb, 0xe4, 0x44, 0x4a, 0xe4, 0xe7, 0x84, 0xbf, 0xcb, - 0xbf, 0xef, 0xcc, 0x78, 0x08, 0x0b, 0xa6, 0x65, 0xf1, 0x43, 0x87, 0x11, 0x2a, 0x3b, 0x3f, 0x9b, - 0xa6, 0x9f, 0x9c, 0xa5, 0xd0, 0x19, 0xd3, 0xb2, 0x76, 0x30, 0x0e, 0xda, 0x30, 0x7e, 0x38, 0x45, - 0xef, 0x81, 0x26, 0x6b, 0x1b, 0x1b, 0x79, 0x34, 0x5b, 0xe4, 0x79, 0x19, 0xa2, 0x27, 0x78, 0x2f, - 0x67, 0x3e, 0x9d, 0x44, 0xe4, 0xb1, 0x33, 0x70, 0xae, 0xd9, 0x56, 0x32, 0xe7, 0x20, 0xf2, 0xf8, - 0xd9, 0x38, 0xfb, 0xc1, 0x1b, 0x50, 0xf4, 0x39, 0xc7, 0xb7, 0x02, 0x85, 0x4b, 0xd9, 0x00, 0x34, - 0x49, 0xfd, 0x41, 0x4c, 0x4b, 0x80, 0x6c, 0xb8, 0x19, 0x52, 0x90, 0x80, 0x33, 0x91, 0x0d, 0x67, - 0x29, 0x10, 0x12, 0x0b, 0xe5, 0x42, 0x29, 0x59, 0x8f, 0xc7, 0x8f, 0xa4, 0xb4, 0x90, 0x17, 0x48, - 0x89, 0x7d, 0xf4, 0x0e, 0xc6, 0x3a, 0x37, 0x54, 0x80, 0x37, 0xe2, 0x85, 0x09, 0x13, 0x8a, 0x18, - 0xac, 0xf4, 0x95, 0xa6, 0x20, 0x61, 0x20, 0xc8, 0xe5, 0x44, 0x8d, 0x0a, 0xd5, 0x84, 0x25, 0x5f, - 0x65, 0x6f, 0xab, 0xc0, 0x93, 0x39, 0x99, 0x2d, 0x99, 0x8b, 0x52, 0x5b, 0xad, 0xab, 0x09, 0xe0, - 0x89, 0x6c, 0x42, 0x29, 0x24, 0x2c, 0x1e, 0xe5, 0x72, 0x36, 0x94, 0x1b, 0x81, 0x9c, 0x38, 0x20, - 0x07, 0x96, 0x13, 0xb5, 0xa8, 0xec, 0x4d, 0x0d, 0x94, 0xbd, 0xeb, 0xb1, 0xa2, 0x54, 0xe6, 0x3c, - 0x28, 0xf7, 0x93, 0xa5, 0x00, 0xa7, 0x07, 0x02, 0x2c, 0x26, 0xe9, 0x93, 0x98, 0x3d, 0x9f, 0x5a, - 0x4d, 0x74, 0x17, 0x5d, 0xdf, 0xd2, 0x9e, 0xad, 0x42, 0x1e, 0x7b, 0xf6, 0xc5, 0xad, 0xd8, 0x39, - 0x6c, 0x15, 0xf2, 0x7a, 0x2d, 0x6d, 0xab, 0x90, 0x70, 0xfe, 0x56, 0x21, 0x7d, 0x92, 0xb7, 0x8a, - 0x28, 0x45, 0xa9, 0x60, 0xe3, 0x93, 0x69, 0x18, 0xd9, 0xa3, 0x4d, 0xf4, 0x08, 0xf2, 0xc1, 0xe7, - 0x11, 0xdd, 0x49, 0xdc, 0x9b, 0x7a, 0x2f, 0xf9, 0xb4, 0x7f, 0x67, 0x33, 0x56, 0xd7, 0x4c, 0x01, - 0x4e, 0xcd, 0xb6, 0x32, 0xe0, 0x74, 0x2e, 0xd7, 0x32, 0xe0, 0x84, 0xaf, 0xb3, 0x1c, 0x98, 0x0c, - 0xdd, 0x1e, 0xa1, 0xd5, 0x7e, 0xce, 0x3d, 0x17, 0x5b, 0x5a, 0x25, 0xab, 0xb9, 0x42, 0x6b, 0xc0, - 0x84, 0x7f, 0xc5, 0x81, 0x6e, 0xf7, 0xf1, 0xed, 0xba, 0xb5, 0xd2, 0xee, 0x64, 0xb2, 0x8d, 0x82, - 0xf0, 0x4e, 0x3c, 0x15, 0x24, 0x74, 0xa9, 0x92, 0x0a, 0x12, 0x6e, 0xed, 0x11, 0x81, 0xcb, 0xe1, - 0x26, 0x1b, 0xf5, 0xcb, 0x44, 0xcc, 0x05, 0x80, 0x56, 0xcd, 0x6c, 0xaf, 0x00, 0x8f, 0x61, 0x3a, - 0xda, 0xc0, 0xa2, 0xb5, 0xd4, 0x10, 0x5d, 0x4d, 0xbe, 0xb6, 0x3e, 0x80, 0x87, 0x82, 0xfd, 0x30, - 0x07, 0x33, 0x31, 0xcd, 0x24, 0xfa, 0x6f, 0x6a, 0xa8, 0xb8, 0x56, 0x5a, 0xbb, 0x3b, 0xa8, 0x5b, - 0x02, 0x0d, 0xd5, 0x0b, 0x66, 0xa6, 0x11, 0x6d, 0x70, 0x33, 0xd3, 0xe8, 0x6a, 0x39, 0xd1, 0xc7, - 0x39, 0x98, 0x8f, 0x6f, 0xdf, 0xd0, 0xff, 0x33, 0x86, 0xec, 0x69, 0x51, 0xb5, 0x57, 0xce, 0xe0, - 0xa9, 0xf8, 0x3c, 0xcd, 0xc1, 0x42, 0x42, 0xf7, 0x83, 0xd2, 0xc3, 0x26, 0x35, 0x97, 0xda, 0xe6, - 0x59, 0x5c, 0x15, 0xa5, 0x8f, 0x72, 0x30, 0x1b, 0xd7, 0x36, 0xa0, 0xbb, 0x19, 0x83, 0x76, 0xb5, - 0x66, 0xda, 0xff, 0x06, 0xf6, 0x53, 0x4c, 0x4e, 0xe1, 0x4a, 0xd7, 0xc1, 0x1f, 0xf5, 0x5b, 0x00, - 0xf1, 0x7d, 0x8c, 0xb6, 0x31, 0x88, 0x8b, 0x42, 0xf6, 0x60, 0x2a, 0xb2, 0x0f, 0xa2, 0x6a, 0xff, - 0x20, 0x3d, 0xdd, 0x87, 0xb6, 0x96, 0xdd, 0x21, 0xa2, 0x36, 0xbc, 0x77, 0xa5, 0xa9, 0x8d, 0xd9, - 0x8a, 0xd3, 0xd4, 0xc6, 0x6d, 0x8d, 0x35, 0xfc, 0xec, 0x45, 0x31, 0xf7, 0xfc, 0x45, 0x31, 0xf7, - 0xfb, 0x8b, 0x62, 0xee, 0xe9, 0xcb, 0xe2, 0xd0, 0xf3, 0x97, 0xc5, 0xa1, 0x5f, 0x5e, 0x16, 0x87, - 0x60, 0xd1, 0x26, 0x09, 0xf1, 0xf6, 0x73, 0xef, 0x56, 0x42, 0x57, 0x7a, 0x1d, 0xa3, 0x55, 0x9b, - 0x84, 0x9e, 0xaa, 0xa7, 0xc1, 0x8f, 0x69, 0xf5, 0x71, 0xf1, 0x1b, 0xda, 0x7f, 0xfe, 0x0a, 0x00, - 0x00, 0xff, 0xff, 0x18, 0x1e, 0x88, 0x85, 0x57, 0x1c, 0x00, 0x00, + // 1766 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0x4b, 0x6f, 0xdc, 0x5e, + 0x15, 0x8f, 0xf3, 0xea, 0xcc, 0x49, 0x93, 0xf6, 0x7f, 0xf3, 0x9a, 0xb8, 0xcd, 0x64, 0x3a, 0xa1, + 0x52, 0x69, 0xc9, 0x4c, 0x12, 0x44, 0x81, 0x88, 0x05, 0x99, 0x94, 0x44, 0x5d, 0x04, 0x22, 0x97, + 0x82, 0x04, 0x0b, 0xeb, 0x8e, 0x7d, 0x3b, 0xb1, 0xe2, 0xb1, 0xa7, 0xbe, 0x37, 0x69, 0xba, 0x42, + 0x42, 0x48, 0xac, 0x90, 0xba, 0x64, 0x8b, 0x60, 0x03, 0x42, 0x02, 0x24, 0x36, 0xf0, 0x09, 0x2a, + 0xc4, 0xa2, 0x62, 0xc5, 0x0a, 0xaa, 0x76, 0xc1, 0x47, 0x60, 0x8b, 0xee, 0xc3, 0x1e, 0x7b, 0xc6, + 0x1e, 0x7b, 0x42, 0xb2, 0x4a, 0x7c, 0xef, 0x79, 0xfc, 0x7e, 0xe7, 0x9c, 0xfb, 0x38, 0x77, 0x60, + 0xa3, 0x17, 0xf8, 0x17, 0xc4, 0xc3, 0x9e, 0x45, 0x9a, 0xe4, 0xd2, 0x3a, 0xc5, 0x5e, 0x87, 0x34, + 0x2f, 0x76, 0x9a, 0xec, 0xb2, 0xd1, 0x0b, 0x7c, 0xe6, 0xa3, 0x95, 0xbe, 0x40, 0x23, 0x14, 0x68, + 0x5c, 0xec, 0xe8, 0x55, 0xcb, 0xa7, 0x5d, 0x9f, 0x36, 0xdb, 0x98, 0x72, 0x85, 0x36, 0x61, 0x78, + 0xa7, 0x69, 0xf9, 0x8e, 0x27, 0xf5, 0xf4, 0x55, 0x35, 0xdf, 0xa5, 0x1d, 0x6e, 0xaf, 0x4b, 0x3b, + 0x6a, 0x62, 0x4d, 0x4e, 0x98, 0xe2, 0xab, 0x29, 0x3f, 0xd4, 0xd4, 0x52, 0xc7, 0xef, 0xf8, 0x72, + 0x9c, 0xff, 0xa7, 0x46, 0x37, 0x33, 0x20, 0x76, 0x71, 0x70, 0x46, 0x58, 0x8e, 0x90, 0x1f, 0xd8, + 0x24, 0xa0, 0x39, 0x42, 0x3d, 0x1c, 0xe0, 0xae, 0x12, 0xaa, 0xff, 0x55, 0x83, 0xc5, 0x63, 0xda, + 0x39, 0x08, 0x08, 0x66, 0x64, 0x9f, 0x9e, 0x19, 0xe4, 0xf5, 0x39, 0xa1, 0x0c, 0x1d, 0x40, 0x19, + 0xd3, 0x33, 0x53, 0x18, 0xac, 0x68, 0x35, 0xed, 0xd1, 0xdc, 0x6e, 0xad, 0x91, 0x1e, 0x9c, 0xc6, + 0x3e, 0x3d, 0xfb, 0x1e, 0x97, 0x6b, 0x4d, 0xbf, 0xff, 0xd7, 0xc6, 0x84, 0x51, 0xc2, 0xea, 0x1b, + 0x1d, 0x01, 0x12, 0x06, 0x4c, 0x8b, 0x9b, 0x77, 0x7c, 0xcf, 0x7c, 0x45, 0x48, 0x65, 0x52, 0x58, + 0x5b, 0x6b, 0xa8, 0x60, 0xf0, 0x90, 0x36, 0x54, 0x48, 0x1b, 0x07, 0xbe, 0xe3, 0x19, 0x77, 0x85, + 0xd2, 0x81, 0xd2, 0x39, 0x24, 0x64, 0x6f, 0xf9, 0xa7, 0xff, 0xf9, 0xe3, 0xe3, 0xbb, 0x11, 0xa0, + 0x06, 0x25, 0xae, 0x4b, 0x82, 0xfa, 0x0e, 0x2c, 0x25, 0xb1, 0xd3, 0x9e, 0xef, 0x51, 0x82, 0xd6, + 0xa0, 0x24, 0xfd, 0x3a, 0xb6, 0xc0, 0x3e, 0x6d, 0xdc, 0x12, 0xdf, 0xcf, 0xed, 0xfa, 0x5f, 0xe2, + 0x7c, 0x5b, 0x8e, 0x1d, 0xe3, 0xdb, 0x76, 0xec, 0x62, 0x7c, 0x5b, 0x8e, 0x9d, 0xe0, 0xdb, 0x56, + 0xdf, 0xd7, 0xc7, 0x77, 0x89, 0xf3, 0xbd, 0x13, 0x01, 0x6a, 0xb4, 0xcf, 0xdf, 0x0e, 0xd0, 0x15, + 0xd0, 0xf3, 0xe9, 0xbe, 0x86, 0x65, 0xae, 0xc2, 0x29, 0xb8, 0x02, 0x63, 0xc8, 0x77, 0x1b, 0x66, + 0xa9, 0xd3, 0xf1, 0x14, 0xd9, 0x72, 0xab, 0xf2, 0x8f, 0x3f, 0x6f, 0x2d, 0x29, 0x84, 0xfb, 0xb6, + 0x1d, 0x10, 0x4a, 0x5f, 0xb0, 0xc0, 0xf1, 0x3a, 0x86, 0x92, 0x4b, 0x78, 0x99, 0x4c, 0x78, 0xd9, + 0x9b, 0xe3, 0x70, 0x95, 0x5c, 0xbd, 0x02, 0x2b, 0x83, 0x2e, 0x25, 0xce, 0xfa, 0xef, 0xa7, 0x00, + 0x1d, 0xd3, 0xce, 0xa1, 0xe3, 0xba, 0x2d, 0xc7, 0xa6, 0x71, 0x28, 0x22, 0x9f, 0x05, 0xa0, 0x08, + 0x39, 0x74, 0x0f, 0xca, 0x72, 0x39, 0x84, 0x58, 0xe6, 0x8d, 0x92, 0x1c, 0x78, 0x6e, 0x23, 0x0f, + 0x6e, 0x33, 0x9f, 0x61, 0xd7, 0xc4, 0x94, 0x12, 0x46, 0x2b, 0x53, 0xb5, 0xa9, 0x91, 0xe1, 0x6f, + 0x6d, 0xf3, 0x2c, 0xfe, 0xee, 0xdf, 0x1b, 0x8f, 0x3a, 0x0e, 0x3b, 0x3d, 0x6f, 0x37, 0x2c, 0xbf, + 0xab, 0x16, 0xaa, 0xfa, 0xb3, 0x45, 0xed, 0xb3, 0x26, 0x7b, 0xdb, 0x23, 0x54, 0x28, 0x50, 0x63, + 0x4e, 0x38, 0xd8, 0x17, 0xf6, 0x51, 0x1d, 0xe6, 0xa3, 0x44, 0x99, 0x8e, 0x4d, 0x2b, 0xd3, 0xb5, + 0xa9, 0x47, 0xd3, 0xc6, 0x5c, 0x58, 0x15, 0xcf, 0x6d, 0x8a, 0x7e, 0x00, 0xba, 0x84, 0x6e, 0x52, + 0xc2, 0x98, 0x4b, 0xba, 0xc4, 0x63, 0xe6, 0x2b, 0x17, 0x33, 0x51, 0x20, 0x33, 0x79, 0x05, 0xb2, + 0x2a, 0x95, 0x5f, 0x44, 0xba, 0x87, 0x2e, 0x66, 0x87, 0x84, 0xa0, 0xef, 0xc2, 0x4a, 0xb4, 0x28, + 0x92, 0x45, 0x37, 0x9b, 0x67, 0x73, 0x31, 0x5c, 0xa5, 0xf1, 0xba, 0x53, 0x89, 0x94, 0xab, 0x6b, + 0x59, 0xac, 0x94, 0x7e, 0xb6, 0x54, 0x16, 0x7f, 0xd3, 0xcf, 0xe2, 0x3e, 0x3d, 0x8b, 0xb2, 0xd8, + 0x80, 0x19, 0x51, 0xa5, 0xb9, 0x49, 0x94, 0x62, 0xa3, 0x73, 0xf8, 0x6d, 0x90, 0x21, 0x36, 0x7b, + 0x81, 0x63, 0x91, 0xca, 0x54, 0x0e, 0x19, 0xb5, 0x10, 0x41, 0xe8, 0x9c, 0x70, 0x15, 0x9e, 0x95, + 0x7e, 0x64, 0x62, 0x59, 0x09, 0x59, 0xf3, 0xac, 0xfc, 0x04, 0x96, 0x05, 0x96, 0x44, 0x52, 0x08, + 0xa1, 0x95, 0x99, 0xeb, 0x2f, 0x99, 0x45, 0xe1, 0x29, 0x96, 0x41, 0x42, 0x28, 0x4f, 0x5f, 0xbf, + 0x74, 0xc6, 0x4c, 0x5f, 0x58, 0x5e, 0xf1, 0xf4, 0x01, 0x4f, 0x9f, 0x8c, 0x6f, 0x2c, 0x7b, 0x32, + 0x4b, 0x2a, 0x7b, 0x1f, 0x35, 0xb1, 0x3c, 0x8f, 0x45, 0xa4, 0x25, 0x9c, 0x58, 0x06, 0xb1, 0xdd, + 0x75, 0xbc, 0xfc, 0x0c, 0x0a, 0xb1, 0xd1, 0x19, 0x1c, 0x8a, 0xff, 0xd4, 0x70, 0xfc, 0x8b, 0xac, + 0x9c, 0x87, 0xb0, 0x40, 0x2e, 0x7b, 0xc4, 0x62, 0x66, 0x0f, 0x07, 0xcc, 0xc1, 0xae, 0x58, 0x2d, + 0x25, 0x63, 0x5e, 0x8e, 0x9e, 0xc8, 0x41, 0xc5, 0x5c, 0xe0, 0xaa, 0xaf, 0xc1, 0xea, 0x10, 0x43, + 0xc5, 0xfe, 0xbf, 0x1a, 0x54, 0xa2, 0xb9, 0x1f, 0x3a, 0xec, 0xd4, 0x0e, 0xf0, 0x9b, 0x1b, 0xe1, + 0xbf, 0x0e, 0xc0, 0x7c, 0x13, 0x4b, 0x3d, 0x51, 0xc0, 0x65, 0xa3, 0xcc, 0x7c, 0x65, 0x08, 0x59, + 0x30, 0x8b, 0xbb, 0xfe, 0xb9, 0xc7, 0x04, 0xe7, 0x6b, 0xae, 0x35, 0x65, 0x3a, 0x11, 0x94, 0x7b, + 0xb0, 0x96, 0x42, 0x5c, 0x85, 0xe5, 0xef, 0x1a, 0xac, 0x47, 0xb3, 0x2f, 0x7b, 0x36, 0x66, 0xe4, + 0x19, 0x61, 0xd8, 0x71, 0xe9, 0x8d, 0xc4, 0xc6, 0x80, 0x05, 0x35, 0x69, 0x4b, 0x2f, 0x6a, 0x81, + 0x3f, 0xcc, 0x3a, 0x70, 0x25, 0x30, 0x05, 0x49, 0x2d, 0xf6, 0xf9, 0x6e, 0x7c, 0x30, 0xc1, 0xb5, + 0x06, 0xd5, 0x2c, 0x36, 0x8a, 0xf0, 0xaf, 0x87, 0x09, 0x7f, 0xc7, 0xc3, 0x6d, 0x97, 0xd8, 0x37, + 0x42, 0xf8, 0xcb, 0x70, 0x17, 0x5b, 0x16, 0xe9, 0x31, 0xc7, 0xeb, 0xc8, 0x72, 0x97, 0x94, 0x4b, + 0xc6, 0x9d, 0x68, 0x5c, 0x54, 0x7c, 0x1e, 0x8f, 0x08, 0xa4, 0xe2, 0xf1, 0x07, 0x0d, 0x6a, 0x03, + 0x22, 0x2f, 0x69, 0xb8, 0xcd, 0xdc, 0x08, 0x95, 0x5d, 0x58, 0xc6, 0xae, 0xeb, 0xbf, 0x31, 0xcf, + 0x69, 0x62, 0xe3, 0x54, 0x7c, 0x16, 0xc5, 0x64, 0x1f, 0x03, 0x9f, 0x4a, 0x70, 0xda, 0x84, 0x07, + 0x23, 0x00, 0x2b, 0x5a, 0xbf, 0x9d, 0x8c, 0x49, 0x1d, 0x63, 0x0f, 0x77, 0xc8, 0x09, 0x09, 0xba, + 0x0e, 0xa5, 0x8e, 0xef, 0xd1, 0x9b, 0x5a, 0xaf, 0x01, 0xb9, 0xf0, 0xcf, 0x88, 0x89, 0x5d, 0x57, + 0x6c, 0x56, 0x65, 0xa3, 0x2c, 0x47, 0xf6, 0x5d, 0x17, 0x1d, 0x42, 0x99, 0xf9, 0xa6, 0xfc, 0x56, + 0x4b, 0x76, 0x33, 0xf3, 0x3a, 0x6c, 0x59, 0x84, 0xd2, 0xa3, 0x00, 0x7b, 0x2c, 0xbc, 0x21, 0x32, + 0xdf, 0x10, 0xaa, 0xe8, 0x19, 0x94, 0x98, 0x6f, 0x76, 0xf8, 0x9c, 0x3a, 0x65, 0xc6, 0x30, 0x73, + 0x8b, 0xf9, 0xe2, 0x33, 0x11, 0xd0, 0x2f, 0x41, 0x7d, 0x54, 0xa8, 0x54, 0x44, 0xff, 0x34, 0x19, + 0xab, 0x25, 0x29, 0x66, 0x90, 0xd7, 0xfb, 0x8c, 0x05, 0xf4, 0x86, 0x2a, 0xfe, 0x0b, 0x71, 0x9e, + 0x11, 0x93, 0x9f, 0x02, 0x72, 0x27, 0x54, 0x51, 0x5d, 0xb0, 0xc2, 0xfb, 0xfa, 0xf7, 0xf9, 0x76, + 0x88, 0x9a, 0xb0, 0x94, 0x14, 0x0d, 0x48, 0xd7, 0xbf, 0x90, 0x51, 0x2e, 0x1b, 0x5f, 0xc4, 0xa4, + 0x0d, 0x31, 0x11, 0xb3, 0xcd, 0x4f, 0x0f, 0x65, 0x7b, 0x26, 0x6e, 0xbb, 0xe5, 0xd8, 0x83, 0xb6, + 0x95, 0xa8, 0xb2, 0x3d, 0x1b, 0xb7, 0x2d, 0xa4, 0xa5, 0xed, 0x44, 0x64, 0x1f, 0xc0, 0x46, 0x66, + 0xc8, 0x54, 0x58, 0x7f, 0xa5, 0x89, 0x6d, 0xf5, 0xc8, 0xbf, 0x90, 0xb7, 0x72, 0x29, 0x1c, 0x46, + 0xf4, 0x29, 0x94, 0xf1, 0x39, 0x3b, 0xf5, 0x03, 0x87, 0xbd, 0xcd, 0x8d, 0x6a, 0x5f, 0x14, 0x7d, + 0x0b, 0x66, 0x65, 0x20, 0x55, 0xeb, 0x50, 0x1d, 0xbd, 0x2f, 0xaa, 0xea, 0x50, 0x3a, 0x7b, 0x0b, + 0x9c, 0x42, 0xdf, 0x5a, 0xfd, 0x3e, 0xe8, 0x69, 0x10, 0x15, 0x83, 0xbf, 0x81, 0x38, 0x2d, 0x8f, + 0xfc, 0x0b, 0x49, 0x91, 0xdf, 0x4b, 0xfe, 0x5f, 0xfc, 0x23, 0x2b, 0xe3, 0x25, 0xac, 0x62, 0xdb, + 0xe6, 0x97, 0x1c, 0x33, 0x96, 0x76, 0x7e, 0x17, 0xce, 0xbf, 0xa9, 0x4b, 0xa2, 0x8b, 0xd8, 0xb6, + 0x0f, 0x09, 0x89, 0xda, 0x3e, 0x7e, 0x19, 0x46, 0x3f, 0x06, 0x5d, 0xe6, 0x36, 0xd5, 0xf2, 0x74, + 0x31, 0xcb, 0x2b, 0xd2, 0xc4, 0x90, 0xf1, 0x61, 0xcc, 0xbc, 0x9c, 0x84, 0xe5, 0x99, 0x2b, 0x60, + 0x6e, 0x39, 0x76, 0x36, 0xe6, 0xc8, 0xf2, 0xec, 0xd5, 0x30, 0x87, 0xc6, 0x2d, 0xa8, 0x86, 0x98, + 0xd3, 0x5b, 0x8f, 0xca, 0xad, 0x62, 0x0e, 0x74, 0x09, 0xfd, 0x45, 0x4a, 0x0b, 0x82, 0x1c, 0x78, + 0x10, 0x63, 0x90, 0xe1, 0xa7, 0x54, 0xcc, 0xcf, 0x7a, 0x44, 0x24, 0xd5, 0x95, 0x07, 0xb5, 0x6c, + 0x3e, 0x01, 0xbf, 0x02, 0xd3, 0x4a, 0x59, 0x78, 0xca, 0xec, 0xdb, 0x0f, 0x09, 0x31, 0xb8, 0xa0, + 0x72, 0x78, 0x3f, 0x9d, 0x98, 0x10, 0xa1, 0x88, 0xc1, 0xe6, 0x48, 0x6a, 0xca, 0x25, 0x8c, 0xe5, + 0x72, 0x23, 0x93, 0xa3, 0xf2, 0x8a, 0x61, 0x3d, 0x64, 0x39, 0xdc, 0x9a, 0xf0, 0x60, 0xce, 0x15, + 0x0b, 0xe6, 0x9a, 0xe4, 0xd6, 0x1a, 0x68, 0x3a, 0x78, 0x20, 0x3b, 0x50, 0x8b, 0x11, 0x4b, 0xf7, + 0x72, 0xbb, 0x98, 0x97, 0xfb, 0x11, 0x9d, 0x34, 0x47, 0x2e, 0x6c, 0x64, 0x72, 0x51, 0xd1, 0x9b, + 0x1f, 0x2b, 0x7a, 0xf7, 0x52, 0x49, 0xa9, 0xc8, 0x05, 0x50, 0x1f, 0x45, 0x4b, 0x39, 0x5c, 0x18, + 0xcb, 0x61, 0x35, 0x8b, 0x9f, 0xf4, 0x39, 0xb4, 0xd5, 0xea, 0xa2, 0xbb, 0x18, 0xd8, 0x4b, 0x87, + 0x8e, 0x0a, 0x79, 0xed, 0x39, 0x11, 0xaf, 0x70, 0xd7, 0x70, 0x54, 0xc8, 0xe7, 0xbc, 0xbc, 0xa3, + 0x42, 0xba, 0x0b, 0x8f, 0x0a, 0xa9, 0x93, 0x7d, 0x54, 0x24, 0x21, 0x4a, 0x06, 0xbb, 0xbf, 0x5c, + 0x80, 0xa9, 0x63, 0xda, 0x41, 0xaf, 0xa0, 0x1c, 0x6d, 0x8f, 0xe8, 0x49, 0xe6, 0xd9, 0x34, 0xfc, + 0xa8, 0xa8, 0x7f, 0xa5, 0x98, 0xb0, 0x7a, 0xd6, 0x8a, 0xfc, 0xb4, 0x1c, 0xbb, 0x80, 0x9f, 0xfe, + 0x63, 0x5e, 0x01, 0x3f, 0xf1, 0xe7, 0x33, 0x17, 0xe6, 0x62, 0xaf, 0x55, 0x68, 0x6b, 0x94, 0xf2, + 0xd0, 0x43, 0x9a, 0xde, 0x28, 0x2a, 0xae, 0xbc, 0x59, 0x50, 0x0a, 0x9f, 0x54, 0xd0, 0xe3, 0x11, + 0xba, 0x03, 0xaf, 0x64, 0xfa, 0x93, 0x42, 0xb2, 0x49, 0x27, 0xbc, 0xf3, 0xcf, 0x75, 0x12, 0x7b, + 0xc4, 0xc9, 0x75, 0x12, 0x7f, 0x4a, 0x40, 0x3e, 0xdc, 0x8e, 0x37, 0xd9, 0x68, 0x54, 0x24, 0x52, + 0xde, 0x1b, 0xf4, 0x66, 0x61, 0x79, 0xe5, 0xf0, 0x1c, 0x16, 0x92, 0x0d, 0x2c, 0xda, 0xce, 0x35, + 0x31, 0xd0, 0xe4, 0xeb, 0x3b, 0x63, 0x68, 0x28, 0xb7, 0x3f, 0xd3, 0x60, 0x31, 0xa5, 0x99, 0x44, + 0x5f, 0xcb, 0x35, 0x95, 0xd6, 0x4a, 0xeb, 0x4f, 0xc7, 0x55, 0xcb, 0x80, 0xa1, 0x7a, 0xc1, 0xc2, + 0x30, 0x92, 0x0d, 0x6e, 0x61, 0x18, 0x03, 0x2d, 0x27, 0xfa, 0x85, 0x06, 0x2b, 0xe9, 0xed, 0x1b, + 0xfa, 0x46, 0x41, 0x93, 0x43, 0x2d, 0xaa, 0xfe, 0xcd, 0x2b, 0x68, 0x2a, 0x3c, 0xef, 0x34, 0x58, + 0xcd, 0xe8, 0x7e, 0x50, 0xbe, 0xd9, 0xac, 0xe6, 0x52, 0xdf, 0xbb, 0x8a, 0xaa, 0x82, 0xf4, 0x73, + 0x0d, 0x96, 0xd2, 0xda, 0x06, 0xf4, 0xb4, 0xa0, 0xd1, 0x81, 0xd6, 0x4c, 0xff, 0xfa, 0xd8, 0x7a, + 0x0a, 0xc9, 0x25, 0xdc, 0x19, 0xb8, 0xf8, 0xa3, 0x51, 0x0b, 0x20, 0xbd, 0x8f, 0xd1, 0x77, 0xc7, + 0x51, 0x51, 0x9e, 0x03, 0x98, 0x4f, 0x9c, 0x83, 0xa8, 0x39, 0xda, 0xc8, 0x50, 0xf7, 0xa1, 0x6f, + 0x17, 0x57, 0x48, 0xb0, 0x8d, 0x9f, 0x5d, 0x79, 0x6c, 0x53, 0x8e, 0xe2, 0x3c, 0xb6, 0x69, 0x47, + 0x63, 0x8b, 0xbc, 0xff, 0x54, 0xd5, 0x3e, 0x7c, 0xaa, 0x6a, 0x1f, 0x3f, 0x55, 0xb5, 0x77, 0x9f, + 0xab, 0x13, 0x1f, 0x3e, 0x57, 0x27, 0xfe, 0xf9, 0xb9, 0x3a, 0x01, 0x6b, 0x8e, 0x9f, 0x61, 0xef, + 0x44, 0xfb, 0x51, 0x23, 0xf6, 0xa4, 0xd7, 0x17, 0xda, 0x72, 0xfc, 0xd8, 0x57, 0xf3, 0x32, 0xfa, + 0xf1, 0xae, 0x3d, 0x2b, 0x7e, 0xb3, 0xfb, 0xea, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x6f, 0xf0, + 0x0a, 0x36, 0xc7, 0x1c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2943,6 +2974,52 @@ func (m *MsgMarketSettleRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if m.ExpectPartial { + i-- + if m.ExpectPartial { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.BidOrderIds) > 0 { + dAtA14 := make([]byte, len(m.BidOrderIds)*10) + var j13 int + for _, num := range m.BidOrderIds { + for num >= 1<<7 { + dAtA14[j13] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j13++ + } + dAtA14[j13] = uint8(num) + j13++ + } + i -= j13 + copy(dAtA[i:], dAtA14[:j13]) + i = encodeVarintTx(dAtA, i, uint64(j13)) + i-- + dAtA[i] = 0x22 + } + if len(m.AskOrderIds) > 0 { + dAtA16 := make([]byte, len(m.AskOrderIds)*10) + var j15 int + for _, num := range m.AskOrderIds { + for num >= 1<<7 { + dAtA16[j15] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j15++ + } + dAtA16[j15] = uint8(num) + j15++ + } + i -= j15 + copy(dAtA[i:], dAtA16[:j15]) + i = encodeVarintTx(dAtA, i, uint64(j15)) + i-- + dAtA[i] = 0x1a + } if m.MarketId != 0 { i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) i-- @@ -3998,6 +4075,23 @@ func (m *MsgMarketSettleRequest) Size() (n int) { if m.MarketId != 0 { n += 1 + sovTx(uint64(m.MarketId)) } + if len(m.AskOrderIds) > 0 { + l = 0 + for _, e := range m.AskOrderIds { + l += sovTx(uint64(e)) + } + n += 1 + sovTx(uint64(l)) + l + } + if len(m.BidOrderIds) > 0 { + l = 0 + for _, e := range m.BidOrderIds { + l += sovTx(uint64(e)) + } + n += 1 + sovTx(uint64(l)) + l + } + if m.ExpectPartial { + n += 2 + } return n } @@ -5641,6 +5735,178 @@ func (m *MsgMarketSettleRequest) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AskOrderIds = append(m.AskOrderIds, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.AskOrderIds) == 0 { + m.AskOrderIds = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AskOrderIds = append(m.AskOrderIds, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field AskOrderIds", wireType) + } + case 4: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.BidOrderIds = append(m.BidOrderIds, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.BidOrderIds) == 0 { + m.BidOrderIds = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.BidOrderIds = append(m.BidOrderIds, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field BidOrderIds", wireType) + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExpectPartial", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ExpectPartial = bool(v != 0) default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) From dadbf6c66efc02dc6fbe1d5b0d51c9d376792ee0 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 11:50:56 -0600 Subject: [PATCH 140/309] [1658]: MsgMarketSettleRequest.ValidateBasic(). --- x/exchange/msg.go | 16 +++- x/exchange/msg_test.go | 180 ++++++++++++++++++++++++++++++++++++----- x/exchange/orders.go | 15 +++- 3 files changed, 189 insertions(+), 22 deletions(-) diff --git a/x/exchange/msg.go b/x/exchange/msg.go index a6030e2e3c..1bb37ccdcf 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -173,7 +173,21 @@ func (m MsgMarketSettleRequest) ValidateBasic() error { errs = append(errs, fmt.Errorf("invalid market id: cannot be zero")) } - // TODO[1658]: MsgMarketSettleRequest.ValidateBasic() + if err := ValidateOrderIDs("ask", m.AskOrderIds); err != nil { + errs = append(errs, err) + } + + if err := ValidateOrderIDs("bid", m.BidOrderIds); err != nil { + errs = append(errs, err) + } + + inBoth := IntersectionUint64(m.AskOrderIds, m.BidOrderIds) + if len(inBoth) > 0 { + errs = append(errs, fmt.Errorf("order ids duplicated as both bid and ask: %v", inBoth)) + } + + // Nothing to validate now for the ExpectPartial flag. + return errors.Join(errs...) } diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 5c0c003f7b..076ee0a7a1 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -13,7 +13,10 @@ import ( "github.com/provenance-io/provenance/testutil/assertions" ) -const emptyAddrErr = "empty address string is not allowed" +const ( + emptyAddrErr = "empty address string is not allowed" + bech32Err = "decoding bech32 failed: " +) func TestAllMsgsGetSigners(t *testing.T) { // getTypeName gets just the type name of the provided thing, e.g. "MsgGovCreateMarketRequest". @@ -28,7 +31,7 @@ func TestAllMsgsGetSigners(t *testing.T) { testAddr := sdk.AccAddress("testAddr____________") badAddrStr := "badaddr" - badAddrErr := "decoding bech32 failed: invalid bech32 string length 7" + badAddrErr := bech32Err + "invalid bech32 string length 7" msgMakers := []func(signer string) sdk.Msg{ func(signer string) sdk.Msg { @@ -263,7 +266,7 @@ func TestMsgCancelOrderRequest_ValidateBasic(t *testing.T) { Signer: "notgonnawork", OrderId: 1, }, - expErr: []string{"invalid signer: ", "decoding bech32 failed: invalid separator index -1"}, + expErr: []string{"invalid signer: ", bech32Err + "invalid separator index -1"}, }, { name: "order 0", @@ -323,7 +326,7 @@ func TestMsgFillBidsRequest_ValidateBasic(t *testing.T) { TotalAssets: sdk.Coins{*coin(3, "acorn")}, BidOrderIds: []uint64{1}, }, - expErr: []string{"invalid seller", "decoding bech32 failed"}, + expErr: []string{"invalid seller", bech32Err}, }, { name: "market id zero", @@ -508,7 +511,7 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { TotalPrice: *coin(3, "acorn"), AskOrderIds: []uint64{1}, }, - expErr: []string{"invalid buyer", "decoding bech32 failed"}, + expErr: []string{"invalid buyer", bech32Err}, }, { name: "market id zero", @@ -632,7 +635,148 @@ func TestMsgFillAsksRequest_ValidateBasic(t *testing.T) { } } -// TODO[1658]: func TestMsgMarketSettleRequest_ValidateBasic(t *testing.T) +func TestMsgMarketSettleRequest_ValidateBasic(t *testing.T) { + admin := sdk.AccAddress("admin_address_______").String() + + tests := []struct { + name string + msg MsgMarketSettleRequest + expErr []string + }{ + { + name: "control", + msg: MsgMarketSettleRequest{ + Admin: admin, + MarketId: 1, + AskOrderIds: []uint64{1, 3, 5}, + BidOrderIds: []uint64{2, 4, 6}, + ExpectPartial: true, + }, + expErr: nil, + }, + { + name: "no admin", + msg: MsgMarketSettleRequest{ + Admin: "", + MarketId: 1, + AskOrderIds: []uint64{1}, + BidOrderIds: []uint64{2}, + }, + expErr: []string{`invalid administrator ""`, emptyAddrErr}, + }, + { + name: "bad admin", + msg: MsgMarketSettleRequest{ + Admin: "badbadadmin", + MarketId: 1, + AskOrderIds: []uint64{1}, + BidOrderIds: []uint64{2}, + }, + expErr: []string{`invalid administrator "badbadadmin"`, bech32Err}, + }, + { + name: "market id zero", + msg: MsgMarketSettleRequest{ + Admin: admin, + MarketId: 0, + AskOrderIds: []uint64{1}, + BidOrderIds: []uint64{2}, + }, + expErr: []string{"invalid market id", "cannot be zero"}, + }, + { + name: "nil ask orders", + msg: MsgMarketSettleRequest{ + Admin: admin, + MarketId: 1, + AskOrderIds: nil, + BidOrderIds: []uint64{2}, + }, + expErr: []string{"no ask order ids provided"}, + }, + { + name: "ask orders has a zero", + msg: MsgMarketSettleRequest{ + Admin: admin, + MarketId: 1, + AskOrderIds: []uint64{1, 3, 0, 5}, + BidOrderIds: []uint64{2}, + }, + expErr: []string{"invalid ask order ids", "cannot contain order id zero"}, + }, + { + name: "duplicate ask orders ids", + msg: MsgMarketSettleRequest{ + Admin: admin, + MarketId: 1, + AskOrderIds: []uint64{1, 3, 1, 5, 5}, + BidOrderIds: []uint64{2}, + }, + expErr: []string{"duplicate ask order ids provided: [1 5]"}, + }, + { + name: "nil bid orders", + msg: MsgMarketSettleRequest{ + Admin: admin, + MarketId: 1, + AskOrderIds: []uint64{1}, + BidOrderIds: nil, + }, + expErr: []string{"no bid order ids provided"}, + }, + { + name: "bid orders has a zero", + msg: MsgMarketSettleRequest{ + Admin: admin, + MarketId: 1, + AskOrderIds: []uint64{1}, + BidOrderIds: []uint64{2, 0, 4, 6}, + }, + expErr: []string{"invalid bid order ids", "cannot contain order id zero"}, + }, + { + name: "duplicate bid orders ids", + msg: MsgMarketSettleRequest{ + Admin: admin, + MarketId: 1, + AskOrderIds: []uint64{1}, + BidOrderIds: []uint64{2, 4, 2, 6, 6}, + }, + expErr: []string{"duplicate bid order ids provided: [2 6]"}, + }, + { + name: "same orders in both lists", + msg: MsgMarketSettleRequest{ + Admin: admin, + MarketId: 1, + AskOrderIds: []uint64{1, 3, 5, 6}, + BidOrderIds: []uint64{2, 4, 6, 3}, + }, + expErr: []string{"order ids duplicated as both bid and ask: [3 6]"}, + }, + { + name: "multiple errors", + msg: MsgMarketSettleRequest{ + Admin: "", + MarketId: 0, + AskOrderIds: nil, + BidOrderIds: nil, + }, + expErr: []string{ + "invalid administrator", + "invalid market id", + "no ask order ids provided", + "no bid order ids provided", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { @@ -675,7 +819,7 @@ func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) { ToAddress: goodToAddr, Amount: goodCoins, }, - expErr: []string{`invalid administrator "notright"`, "decoding bech32 failed"}, + expErr: []string{`invalid administrator "notright"`, bech32Err}, }, { name: "market id zero", @@ -705,7 +849,7 @@ func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) { ToAddress: "notright", Amount: goodCoins, }, - expErr: []string{`invalid to address "notright"`, "decoding bech32 failed"}, + expErr: []string{`invalid to address "notright"`, bech32Err}, }, { name: "invalid denom in amount", @@ -807,7 +951,7 @@ func TestMsgMarketUpdateDetailsRequest_ValidateBasic(t *testing.T) { MarketId: 1, MarketDetails: MarketDetails{}, }, - expErr: []string{`invalid administrator "notvalidadmin"`, "decoding bech32 failed"}, + expErr: []string{`invalid administrator "notvalidadmin"`, bech32Err}, }, { name: "market id zero", @@ -935,7 +1079,7 @@ func TestMsgMarketUpdateEnabledRequest_ValidateBasic(t *testing.T) { MarketId: 1, }, expErr: []string{ - `invalid administrator "badadmin"`, "decoding bech32 failed", + `invalid administrator "badadmin"`, bech32Err, }, }, { @@ -1000,7 +1144,7 @@ func TestMsgMarketUpdateUserSettleRequest_ValidateBasic(t *testing.T) { MarketId: 1, }, expErr: []string{ - `invalid administrator "badadmin"`, "decoding bech32 failed", + `invalid administrator "badadmin"`, bech32Err, }, }, { @@ -1060,7 +1204,7 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { MarketId: 1, RevokeAll: []string{goodAddr1}, }, - expErr: []string{`invalid administrator "bad1admin"`, "decoding bech32 failed"}, + expErr: []string{`invalid administrator "bad1admin"`, bech32Err}, }, { name: "market id zero", @@ -1087,8 +1231,8 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { RevokeAll: []string{"bad1addr", "bad2addr"}, }, expErr: []string{ - `invalid revoke-all address "bad1addr": decoding bech32 failed`, - `invalid revoke-all address "bad2addr": decoding bech32 failed`, + `invalid revoke-all address "bad1addr": ` + bech32Err, + `invalid revoke-all address "bad2addr": ` + bech32Err, }, }, { @@ -1222,7 +1366,7 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { MarketId: 1, CreateAskToAdd: []string{"abc"}, }, - expErr: []string{"invalid administrator", "decoding bech32 failed"}, + expErr: []string{"invalid administrator", bech32Err}, }, { name: "market id zero", @@ -1423,7 +1567,7 @@ func TestMsgGovCreateMarketRequest_ValidateBasic(t *testing.T) { Authority: "bad", Market: validMarket, }, - expErr: []string{"invalid authority", "decoding bech32 failed"}, + expErr: []string{"invalid authority", bech32Err}, }, { name: "invalid market", @@ -1490,7 +1634,7 @@ func TestMsgGovManageFeesRequest_ValidateBasic(t *testing.T) { Authority: "bad", AddFeeCreateAskFlat: []sdk.Coin{coin(1, "nhash")}, }, - expErr: []string{"invalid authority", "decoding bech32 failed"}, + expErr: []string{"invalid authority", bech32Err}, }, { name: "invalid add create-ask flat", @@ -1781,7 +1925,7 @@ func TestMsgGovUpdateParamsRequest_ValidateBasic(t *testing.T) { Authority: "bad", Params: *DefaultParams(), }, - expErr: []string{"invalid authority", "decoding bech32 failed"}, + expErr: []string{"invalid authority", bech32Err}, }, { name: "bad params", diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 2a64893e15..2da9694d24 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -17,6 +17,7 @@ const ( OrderTypeByteBid = byte(0x01) ) +// findDuplicateIds returns all order ids that appear two or more times in the provided slice. func findDuplicateIds(orderIDs []uint64) []uint64 { var rv []uint64 seen := make(map[uint64]bool) @@ -31,11 +32,19 @@ func findDuplicateIds(orderIDs []uint64) []uint64 { return rv } +// equalsUint64 returns true if the two uint64 values provided are equal. +func equalsUint64(a, b uint64) bool { + return a == b +} + // ContainsUint64 returns true if the uint64 to find is in the vals slice. func ContainsUint64(vals []uint64, toFind uint64) bool { - return contains(vals, toFind, func(a, b uint64) bool { - return a == b - }) + return contains(vals, toFind, equalsUint64) +} + +// IntersectionUint64 returns each uint64 that is in both lists. +func IntersectionUint64(a, b []uint64) []uint64 { + return intersection(a, b, equalsUint64) } // ValidateOrderIDs makes sure that one or more order ids are provided, From 8d1097ab5b55e9d17e0a4e3ea3a08c9a9617b314 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 11:52:49 -0600 Subject: [PATCH 141/309] [1658]: Fix a TODO comment. --- x/exchange/module/module.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/exchange/module/module.go b/x/exchange/module/module.go index dbde64a812..08aad77917 100644 --- a/x/exchange/module/module.go +++ b/x/exchange/module/module.go @@ -116,7 +116,7 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json. // ExportGenesis returns the exported genesis state as raw bytes for the exchange module. func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { - // TODO[1658]: Create keeper.InitGenesis + // TODO[1658]: Create keeper.ExportGenesis panic("not implemented") //gs := am.keeper.ExportGenesis(ctx) //return cdc.MustMarshalJSON(gs) From a64e63e96f7954930e06d65ba3f6f751e6009281 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 12:06:09 -0600 Subject: [PATCH 142/309] [1658]: Remove the EventCreateMarketSubmitted since I got rid of the endpoint that submits the gov prop. --- docs/proto-docs.md | 19 -- proto/provenance/exchange/v1/events.proto | 11 - x/exchange/events.pb.go | 324 +++------------------- 3 files changed, 38 insertions(+), 316 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 3c8b552e23..e02f06e9ee 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -53,7 +53,6 @@ - [Msg](#provenance.attribute.v1.Msg) - [provenance/exchange/v1/events.proto](#provenance/exchange/v1/events.proto) - - [EventCreateMarketSubmitted](#provenance.exchange.v1.EventCreateMarketSubmitted) - [EventMarketCreated](#provenance.exchange.v1.EventMarketCreated) - [EventMarketDetailsUpdated](#provenance.exchange.v1.EventMarketDetailsUpdated) - [EventMarketDisabled](#provenance.exchange.v1.EventMarketDisabled) @@ -1317,24 +1316,6 @@ Msg defines the attribute module Msg service. - - -### EventCreateMarketSubmitted -EventCreateMarketSubmitted is an event emitted during CreateMarket indicating that a governance -proposal was submitted to create a market. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | -| `proposal_id` | [uint64](#uint64) | | proposal_id is the identifier of the governance proposal that was submitted to create the market. | -| `submitted_by` | [string](#string) | | submitted_by is the account that requested the creation of the market. | - - - - - - ### EventMarketCreated diff --git a/proto/provenance/exchange/v1/events.proto b/proto/provenance/exchange/v1/events.proto index 24ac27910a..3c6a0e4dba 100644 --- a/proto/provenance/exchange/v1/events.proto +++ b/proto/provenance/exchange/v1/events.proto @@ -101,17 +101,6 @@ message EventMarketReqAttrUpdated { string updated_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; } -// EventCreateMarketSubmitted is an event emitted during CreateMarket indicating that a governance -// proposal was submitted to create a market. -message EventCreateMarketSubmitted { - // market_id is the numerical identifier of the market. - uint32 market_id = 1; - // proposal_id is the identifier of the governance proposal that was submitted to create the market. - uint64 proposal_id = 2; - // submitted_by is the account that requested the creation of the market. - string submitted_by = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"]; -} - // EventMarketCreated is an event emitted when a market has been created. message EventMarketCreated { // market_id is the numerical identifier of the market. diff --git a/x/exchange/events.pb.go b/x/exchange/events.pb.go index 1243e6e281..0a4a643515 100644 --- a/x/exchange/events.pb.go +++ b/x/exchange/events.pb.go @@ -647,71 +647,6 @@ func (m *EventMarketReqAttrUpdated) GetUpdatedBy() string { return "" } -// EventCreateMarketSubmitted is an event emitted during CreateMarket indicating that a governance -// proposal was submitted to create a market. -type EventCreateMarketSubmitted struct { - // market_id is the numerical identifier of the market. - MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` - // proposal_id is the identifier of the governance proposal that was submitted to create the market. - ProposalId uint64 `protobuf:"varint,2,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty"` - // submitted_by is the account that requested the creation of the market. - SubmittedBy string `protobuf:"bytes,3,opt,name=submitted_by,json=submittedBy,proto3" json:"submitted_by,omitempty"` -} - -func (m *EventCreateMarketSubmitted) Reset() { *m = EventCreateMarketSubmitted{} } -func (m *EventCreateMarketSubmitted) String() string { return proto.CompactTextString(m) } -func (*EventCreateMarketSubmitted) ProtoMessage() {} -func (*EventCreateMarketSubmitted) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{11} -} -func (m *EventCreateMarketSubmitted) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *EventCreateMarketSubmitted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_EventCreateMarketSubmitted.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *EventCreateMarketSubmitted) XXX_Merge(src proto.Message) { - xxx_messageInfo_EventCreateMarketSubmitted.Merge(m, src) -} -func (m *EventCreateMarketSubmitted) XXX_Size() int { - return m.Size() -} -func (m *EventCreateMarketSubmitted) XXX_DiscardUnknown() { - xxx_messageInfo_EventCreateMarketSubmitted.DiscardUnknown(m) -} - -var xxx_messageInfo_EventCreateMarketSubmitted proto.InternalMessageInfo - -func (m *EventCreateMarketSubmitted) GetMarketId() uint32 { - if m != nil { - return m.MarketId - } - return 0 -} - -func (m *EventCreateMarketSubmitted) GetProposalId() uint64 { - if m != nil { - return m.ProposalId - } - return 0 -} - -func (m *EventCreateMarketSubmitted) GetSubmittedBy() string { - if m != nil { - return m.SubmittedBy - } - return "" -} - // EventMarketCreated is an event emitted when a market has been created. type EventMarketCreated struct { // market_id is the numerical identifier of the market. @@ -722,7 +657,7 @@ func (m *EventMarketCreated) Reset() { *m = EventMarketCreated{} } func (m *EventMarketCreated) String() string { return proto.CompactTextString(m) } func (*EventMarketCreated) ProtoMessage() {} func (*EventMarketCreated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{12} + return fileDescriptor_c1b69385a348cffa, []int{11} } func (m *EventMarketCreated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -768,7 +703,7 @@ func (m *EventMarketFeesUpdated) Reset() { *m = EventMarketFeesUpdated{} func (m *EventMarketFeesUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketFeesUpdated) ProtoMessage() {} func (*EventMarketFeesUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{13} + return fileDescriptor_c1b69385a348cffa, []int{12} } func (m *EventMarketFeesUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -812,7 +747,7 @@ func (m *EventParamsUpdated) Reset() { *m = EventParamsUpdated{} } func (m *EventParamsUpdated) String() string { return proto.CompactTextString(m) } func (*EventParamsUpdated) ProtoMessage() {} func (*EventParamsUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{14} + return fileDescriptor_c1b69385a348cffa, []int{13} } func (m *EventParamsUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -853,7 +788,6 @@ func init() { proto.RegisterType((*EventMarketUserSettleUpdated)(nil), "provenance.exchange.v1.EventMarketUserSettleUpdated") proto.RegisterType((*EventMarketPermissionsUpdated)(nil), "provenance.exchange.v1.EventMarketPermissionsUpdated") proto.RegisterType((*EventMarketReqAttrUpdated)(nil), "provenance.exchange.v1.EventMarketReqAttrUpdated") - proto.RegisterType((*EventCreateMarketSubmitted)(nil), "provenance.exchange.v1.EventCreateMarketSubmitted") proto.RegisterType((*EventMarketCreated)(nil), "provenance.exchange.v1.EventMarketCreated") proto.RegisterType((*EventMarketFeesUpdated)(nil), "provenance.exchange.v1.EventMarketFeesUpdated") proto.RegisterType((*EventParamsUpdated)(nil), "provenance.exchange.v1.EventParamsUpdated") @@ -864,44 +798,41 @@ func init() { } var fileDescriptor_c1b69385a348cffa = []byte{ - // 586 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xc1, 0x4e, 0xdb, 0x4c, - 0x10, 0xc7, 0x31, 0x1f, 0xfa, 0x4a, 0x26, 0xa0, 0x52, 0x17, 0x21, 0x42, 0x8b, 0x41, 0xe6, 0x42, - 0x0f, 0xd8, 0x8a, 0xaa, 0xaa, 0x52, 0x7b, 0xc2, 0x05, 0x24, 0x0e, 0xa8, 0x51, 0x52, 0x54, 0xa9, - 0x97, 0x68, 0x13, 0x0f, 0xc9, 0x16, 0x7b, 0xd7, 0xec, 0x6e, 0x02, 0x96, 0xfa, 0x10, 0xbd, 0xf5, - 0x45, 0xfa, 0x10, 0x3d, 0xa2, 0x1e, 0xaa, 0x1e, 0xab, 0xe4, 0x45, 0x2a, 0x7b, 0x6d, 0xc7, 0x91, - 0xaa, 0x84, 0x4b, 0x8e, 0x33, 0xfe, 0xcd, 0xfc, 0x67, 0x67, 0xd6, 0xb3, 0x70, 0x10, 0x09, 0x3e, - 0x44, 0x46, 0x58, 0x17, 0x5d, 0xbc, 0xeb, 0xf6, 0x09, 0xeb, 0xa1, 0x3b, 0xac, 0xbb, 0x38, 0x44, - 0xa6, 0xa4, 0x13, 0x09, 0xae, 0xb8, 0xb9, 0x35, 0x81, 0x9c, 0x1c, 0x72, 0x86, 0xf5, 0x9d, 0x5a, - 0x97, 0xcb, 0x90, 0xcb, 0x76, 0x4a, 0xb9, 0xda, 0xd0, 0x21, 0xf6, 0x05, 0x3c, 0x39, 0x4d, 0x52, - 0xbc, 0x17, 0x3e, 0x8a, 0x77, 0x02, 0x89, 0x42, 0xdf, 0xac, 0xc1, 0x2a, 0x4f, 0xec, 0x36, 0xf5, - 0xb7, 0x8d, 0x7d, 0xe3, 0x70, 0xa5, 0xf9, 0x28, 0xb5, 0xcf, 0x7d, 0x73, 0x17, 0x40, 0x7f, 0x52, - 0x71, 0x84, 0xdb, 0xcb, 0xfb, 0xc6, 0x61, 0xa5, 0x59, 0x49, 0x3d, 0x1f, 0xe2, 0x08, 0xed, 0x10, - 0x9e, 0x96, 0xd2, 0x25, 0x85, 0x04, 0xc1, 0xec, 0x84, 0x6f, 0x61, 0xad, 0x9b, 0x73, 0xed, 0x4e, - 0xac, 0x53, 0x7a, 0xdb, 0x3f, 0xbf, 0x1f, 0x6d, 0x66, 0x85, 0x1e, 0xfb, 0xbe, 0x40, 0x29, 0x5b, - 0x4a, 0x50, 0xd6, 0x6b, 0x56, 0x0b, 0xda, 0x8b, 0xed, 0x23, 0xd8, 0x98, 0xc8, 0x9d, 0xd1, 0x39, - 0x5a, 0xf6, 0x17, 0xa8, 0x4d, 0xf0, 0x06, 0x11, 0x8a, 0x92, 0x20, 0x88, 0xe7, 0xc6, 0x99, 0x07, - 0xb0, 0x4e, 0xa4, 0x44, 0x25, 0xdb, 0x57, 0x29, 0x9b, 0x9d, 0x7b, 0x4d, 0x3b, 0xb3, 0xf8, 0x3d, - 0xa8, 0x5e, 0x21, 0x16, 0xc8, 0x7f, 0x29, 0x02, 0x89, 0x4b, 0x03, 0xf6, 0x2f, 0x23, 0x6b, 0xce, - 0x05, 0x11, 0xd7, 0xa8, 0x3e, 0x52, 0xd5, 0xf7, 0x05, 0xb9, 0x35, 0x9f, 0x41, 0x25, 0x4c, 0x3d, - 0xb9, 0xf2, 0x7a, 0x73, 0x55, 0x3b, 0xce, 0x7d, 0xf3, 0x05, 0x6c, 0x90, 0x90, 0x0f, 0x98, 0x6a, - 0xdf, 0x66, 0x3c, 0xcb, 0xd4, 0x1f, 0x6b, 0x7f, 0x9e, 0x86, 0x99, 0x6f, 0xa0, 0xea, 0xa3, 0x54, - 0x94, 0x11, 0x45, 0x39, 0xd3, 0x05, 0xcc, 0x6a, 0x64, 0x09, 0x4e, 0xa6, 0x50, 0xe4, 0x4f, 0xa6, - 0xb0, 0x32, 0x2f, 0xb8, 0xa0, 0xbd, 0xd8, 0xbe, 0xc9, 0xda, 0xaa, 0xcf, 0x75, 0x82, 0x8a, 0xd0, - 0x40, 0x5e, 0x46, 0x7e, 0x7a, 0x97, 0x66, 0x9e, 0xee, 0x35, 0xc0, 0x40, 0x73, 0x0f, 0x19, 0x7d, - 0x25, 0x63, 0xbd, 0xd8, 0xfe, 0x0c, 0x66, 0x49, 0xf2, 0x94, 0x91, 0x4e, 0xb0, 0x30, 0xad, 0xeb, - 0xa9, 0xb1, 0x9d, 0x50, 0xb9, 0x48, 0x31, 0x05, 0xcf, 0x4b, 0x62, 0x97, 0x12, 0x45, 0x0b, 0x95, - 0x0a, 0x70, 0xb1, 0xed, 0x1c, 0xc0, 0x6e, 0x49, 0xb5, 0x81, 0x22, 0xa4, 0x52, 0x52, 0xce, 0x16, - 0x3c, 0xc5, 0xe9, 0x8b, 0xd3, 0xc4, 0x9b, 0x63, 0xa5, 0xc4, 0x62, 0x25, 0xbf, 0x19, 0xb0, 0x93, - 0x6a, 0xea, 0x5d, 0xa7, 0x95, 0x5b, 0x83, 0x4e, 0x48, 0xd5, 0x5c, 0xd1, 0x3d, 0xa8, 0x46, 0x82, - 0x47, 0x5c, 0x92, 0x20, 0xf9, 0xbc, 0x9c, 0x2e, 0x09, 0xc8, 0x5d, 0x7a, 0x97, 0xc9, 0x3c, 0x55, - 0x52, 0xd7, 0xdc, 0x5f, 0xb0, 0xa0, 0xbd, 0xd8, 0xae, 0x4f, 0x5d, 0xe9, 0x7c, 0x15, 0xcf, 0x2a, - 0xc8, 0x7e, 0x05, 0x5b, 0xa5, 0x90, 0x33, 0xc4, 0x07, 0xcd, 0xcb, 0xde, 0xcc, 0x94, 0x1a, 0x44, - 0x90, 0x30, 0x0f, 0xf1, 0xf0, 0xc7, 0xc8, 0x32, 0xee, 0x47, 0x96, 0xf1, 0x67, 0x64, 0x19, 0x5f, - 0xc7, 0xd6, 0xd2, 0xfd, 0xd8, 0x5a, 0xfa, 0x3d, 0xb6, 0x96, 0xa0, 0x46, 0xb9, 0xf3, 0xef, 0x97, - 0xa5, 0x61, 0x7c, 0x72, 0x7a, 0x54, 0xf5, 0x07, 0x1d, 0xa7, 0xcb, 0x43, 0x77, 0x02, 0x1d, 0x51, - 0x5e, 0xb2, 0xdc, 0xbb, 0xe2, 0xcd, 0xea, 0xfc, 0x9f, 0xbe, 0x3b, 0x2f, 0xff, 0x06, 0x00, 0x00, - 0xff, 0xff, 0x33, 0xb3, 0x39, 0x5f, 0xd1, 0x06, 0x00, 0x00, + // 540 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xc1, 0x6e, 0xd3, 0x4c, + 0x10, 0xc7, 0xe3, 0xef, 0xab, 0xa0, 0x99, 0xb4, 0xa2, 0x98, 0xaa, 0x4a, 0x80, 0x9a, 0xca, 0xbd, + 0x94, 0x43, 0x6c, 0x45, 0x08, 0x21, 0xc1, 0xa9, 0xa1, 0xad, 0xd4, 0x43, 0x45, 0x94, 0x52, 0x21, + 0x71, 0x89, 0x36, 0xd9, 0x69, 0xb2, 0xd4, 0xde, 0x4d, 0x77, 0x37, 0x69, 0x2d, 0xf1, 0x10, 0x3c, + 0x0c, 0x0f, 0xc1, 0xb1, 0xe2, 0x80, 0x38, 0xa2, 0xe4, 0x45, 0x90, 0xbd, 0x76, 0xe2, 0x48, 0x28, + 0xe9, 0xc5, 0xc7, 0x19, 0xff, 0xe6, 0xff, 0xdf, 0x9d, 0x59, 0x8d, 0x61, 0x7f, 0x28, 0xc5, 0x18, + 0x39, 0xe1, 0x3d, 0xf4, 0xf1, 0xb6, 0x37, 0x20, 0xbc, 0x8f, 0xfe, 0xb8, 0xe1, 0xe3, 0x18, 0xb9, + 0x56, 0xde, 0x50, 0x0a, 0x2d, 0xec, 0x9d, 0x39, 0xe4, 0x65, 0x90, 0x37, 0x6e, 0x3c, 0xad, 0xf5, + 0x84, 0x0a, 0x85, 0xea, 0x24, 0x94, 0x6f, 0x02, 0x53, 0xe2, 0x9e, 0xc1, 0xe3, 0xe3, 0x58, 0xe2, + 0x83, 0xa4, 0x28, 0xdf, 0x4b, 0x24, 0x1a, 0xa9, 0x5d, 0x83, 0x75, 0x11, 0xc7, 0x1d, 0x46, 0xab, + 0xd6, 0x9e, 0x75, 0xb0, 0xd6, 0x7e, 0x98, 0xc4, 0xa7, 0xd4, 0xde, 0x05, 0x30, 0x9f, 0x74, 0x34, + 0xc4, 0xea, 0x7f, 0x7b, 0xd6, 0x41, 0xb9, 0x5d, 0x4e, 0x32, 0x1f, 0xa3, 0x21, 0xba, 0x21, 0x3c, + 0xc9, 0xc9, 0xc5, 0x07, 0x09, 0x82, 0xe5, 0x82, 0xef, 0x60, 0xa3, 0x97, 0x71, 0x9d, 0x6e, 0x64, + 0x24, 0x9b, 0xd5, 0x9f, 0xdf, 0xeb, 0xdb, 0xe9, 0x41, 0x0f, 0x29, 0x95, 0xa8, 0xd4, 0xb9, 0x96, + 0x8c, 0xf7, 0xdb, 0x95, 0x19, 0xdd, 0x8c, 0xdc, 0x3a, 0x6c, 0xcd, 0xed, 0x4e, 0xd8, 0x0a, 0x2f, + 0xf7, 0x2b, 0xd4, 0xe6, 0x78, 0x8b, 0x48, 0xcd, 0x48, 0x10, 0x44, 0x2b, 0xeb, 0xec, 0x7d, 0xd8, + 0x24, 0x4a, 0xa1, 0x56, 0x9d, 0xcb, 0x84, 0x4d, 0xef, 0xbd, 0x61, 0x92, 0x69, 0xfd, 0x0b, 0xa8, + 0x5c, 0x22, 0xce, 0x90, 0xff, 0x13, 0x04, 0xe2, 0x94, 0x01, 0xdc, 0x5f, 0x56, 0xda, 0x9c, 0x33, + 0x22, 0xaf, 0x50, 0x7f, 0x62, 0x7a, 0x40, 0x25, 0xb9, 0xb1, 0x9f, 0x41, 0x39, 0x4c, 0x32, 0x99, + 0xf3, 0x66, 0x7b, 0xdd, 0x24, 0x4e, 0xa9, 0xfd, 0x12, 0xb6, 0x48, 0x28, 0x46, 0x5c, 0x77, 0x6e, + 0x52, 0x9e, 0xa7, 0xee, 0x8f, 0x4c, 0x3e, 0x93, 0xe1, 0xf6, 0x5b, 0xa8, 0x50, 0x54, 0x9a, 0x71, + 0xa2, 0x99, 0xe0, 0xe6, 0x00, 0xcb, 0x1a, 0x99, 0x83, 0xe3, 0x29, 0xcc, 0xf4, 0xe3, 0x29, 0xac, + 0xad, 0x2a, 0x9e, 0xd1, 0xcd, 0xc8, 0xbd, 0x4e, 0xdb, 0x6a, 0xee, 0x75, 0x84, 0x9a, 0xb0, 0x40, + 0x5d, 0x0c, 0x69, 0xf2, 0x96, 0x96, 0xde, 0xee, 0x0d, 0xc0, 0xc8, 0x70, 0xf7, 0x19, 0x7d, 0x39, + 0x65, 0x9b, 0x91, 0xfb, 0x05, 0xec, 0x9c, 0xe5, 0x31, 0x27, 0xdd, 0xa0, 0x30, 0xaf, 0xab, 0x85, + 0xb1, 0x1d, 0x31, 0x55, 0xa4, 0x99, 0x86, 0xe7, 0x39, 0xb3, 0x0b, 0x85, 0xf2, 0x1c, 0xb5, 0x0e, + 0xb0, 0xd8, 0x76, 0x8e, 0x60, 0x37, 0xe7, 0xda, 0x42, 0x19, 0x32, 0xa5, 0x98, 0xe0, 0x05, 0x4f, + 0x71, 0xf1, 0xe1, 0xb4, 0xf1, 0xfa, 0x50, 0x6b, 0x59, 0xac, 0x65, 0x63, 0xe1, 0xe1, 0x64, 0x0b, + 0x6f, 0x99, 0x97, 0xfb, 0x1a, 0x76, 0x72, 0x25, 0x27, 0x88, 0xf7, 0xea, 0x8a, 0xbb, 0x9d, 0x3a, + 0xb5, 0x88, 0x24, 0x61, 0x56, 0xd2, 0xc4, 0x1f, 0x13, 0xc7, 0xba, 0x9b, 0x38, 0xd6, 0x9f, 0x89, + 0x63, 0x7d, 0x9b, 0x3a, 0xa5, 0xbb, 0xa9, 0x53, 0xfa, 0x3d, 0x75, 0x4a, 0x50, 0x63, 0xc2, 0xfb, + 0xf7, 0xfe, 0x6e, 0x59, 0x9f, 0xbd, 0x3e, 0xd3, 0x83, 0x51, 0xd7, 0xeb, 0x89, 0xd0, 0x9f, 0x43, + 0x75, 0x26, 0x72, 0x91, 0x7f, 0x3b, 0xfb, 0x33, 0x74, 0x1f, 0x24, 0xdb, 0xfd, 0xd5, 0xdf, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xc4, 0xf4, 0x51, 0x87, 0x37, 0x06, 0x00, 0x00, } func (m *EventOrderCreated) Marshal() (dAtA []byte, err error) { @@ -1303,46 +1234,6 @@ func (m *EventMarketReqAttrUpdated) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } -func (m *EventCreateMarketSubmitted) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *EventCreateMarketSubmitted) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *EventCreateMarketSubmitted) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.SubmittedBy) > 0 { - i -= len(m.SubmittedBy) - copy(dAtA[i:], m.SubmittedBy) - i = encodeVarintEvents(dAtA, i, uint64(len(m.SubmittedBy))) - i-- - dAtA[i] = 0x1a - } - if m.ProposalId != 0 { - i = encodeVarintEvents(dAtA, i, uint64(m.ProposalId)) - i-- - dAtA[i] = 0x10 - } - if m.MarketId != 0 { - i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - func (m *EventMarketCreated) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1617,25 +1508,6 @@ func (m *EventMarketReqAttrUpdated) Size() (n int) { return n } -func (m *EventCreateMarketSubmitted) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.MarketId != 0 { - n += 1 + sovEvents(uint64(m.MarketId)) - } - if m.ProposalId != 0 { - n += 1 + sovEvents(uint64(m.ProposalId)) - } - l = len(m.SubmittedBy) - if l > 0 { - n += 1 + l + sovEvents(uint64(l)) - } - return n -} - func (m *EventMarketCreated) Size() (n int) { if m == nil { return 0 @@ -2850,126 +2722,6 @@ func (m *EventMarketReqAttrUpdated) Unmarshal(dAtA []byte) error { } return nil } -func (m *EventCreateMarketSubmitted) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvents - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: EventCreateMarketSubmitted: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: EventCreateMarketSubmitted: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) - } - m.MarketId = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvents - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.MarketId |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ProposalId", wireType) - } - m.ProposalId = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvents - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.ProposalId |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SubmittedBy", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvents - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthEvents - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthEvents - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.SubmittedBy = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipEvents(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthEvents - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *EventMarketCreated) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 From 8352e6ee8c35f9b6009b87b256731f70401f3347 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 12:28:31 -0600 Subject: [PATCH 143/309] [1658]: Split the EventMarketUserSettleUpdated into an Enabled and Disabled event similar to active. --- docs/proto-docs.md | 25 +- proto/provenance/exchange/v1/events.proto | 12 +- x/exchange/events.pb.go | 338 +++++++++++++++++----- 3 files changed, 304 insertions(+), 71 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index e02f06e9ee..914584505a 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -60,7 +60,8 @@ - [EventMarketFeesUpdated](#provenance.exchange.v1.EventMarketFeesUpdated) - [EventMarketPermissionsUpdated](#provenance.exchange.v1.EventMarketPermissionsUpdated) - [EventMarketReqAttrUpdated](#provenance.exchange.v1.EventMarketReqAttrUpdated) - - [EventMarketUserSettleUpdated](#provenance.exchange.v1.EventMarketUserSettleUpdated) + - [EventMarketUserSettleDisabled](#provenance.exchange.v1.EventMarketUserSettleDisabled) + - [EventMarketUserSettleEnabled](#provenance.exchange.v1.EventMarketUserSettleEnabled) - [EventMarketWithdraw](#provenance.exchange.v1.EventMarketWithdraw) - [EventOrderCancelled](#provenance.exchange.v1.EventOrderCancelled) - [EventOrderCreated](#provenance.exchange.v1.EventOrderCreated) @@ -1426,10 +1427,26 @@ EventMarketReqAttrUpdated is an event emitted when a market's required attribute - + -### EventMarketUserSettleUpdated -EventMarketUserSettleUpdated is an event emitted when a market's user_settle option is updated. +### EventMarketUserSettleDisabled +EventMarketUserSettleDisabled is an event emitted when a market's user_settle option is disabled. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | +| `updated_by` | [string](#string) | | updated_by is the account that updated the user_settle option. | + + + + + + + + +### EventMarketUserSettleEnabled +EventMarketUserSettleEnabled is an event emitted when a market's user_settle option is enabled. | Field | Type | Label | Description | diff --git a/proto/provenance/exchange/v1/events.proto b/proto/provenance/exchange/v1/events.proto index 3c6a0e4dba..52a97f0c23 100644 --- a/proto/provenance/exchange/v1/events.proto +++ b/proto/provenance/exchange/v1/events.proto @@ -77,8 +77,16 @@ message EventMarketDisabled { string updated_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; } -// EventMarketUserSettleUpdated is an event emitted when a market's user_settle option is updated. -message EventMarketUserSettleUpdated { +// EventMarketUserSettleEnabled is an event emitted when a market's user_settle option is enabled. +message EventMarketUserSettleEnabled { + // market_id is the numerical identifier of the market. + uint32 market_id = 1; + // updated_by is the account that updated the user_settle option. + string updated_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +// EventMarketUserSettleDisabled is an event emitted when a market's user_settle option is disabled. +message EventMarketUserSettleDisabled { // market_id is the numerical identifier of the market. uint32 market_id = 1; // updated_by is the account that updated the user_settle option. diff --git a/x/exchange/events.pb.go b/x/exchange/events.pb.go index 0a4a643515..9b0552a39c 100644 --- a/x/exchange/events.pb.go +++ b/x/exchange/events.pb.go @@ -482,26 +482,26 @@ func (m *EventMarketDisabled) GetUpdatedBy() string { return "" } -// EventMarketUserSettleUpdated is an event emitted when a market's user_settle option is updated. -type EventMarketUserSettleUpdated struct { +// EventMarketUserSettleEnabled is an event emitted when a market's user_settle option is enabled. +type EventMarketUserSettleEnabled struct { // market_id is the numerical identifier of the market. MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // updated_by is the account that updated the user_settle option. UpdatedBy string `protobuf:"bytes,2,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"` } -func (m *EventMarketUserSettleUpdated) Reset() { *m = EventMarketUserSettleUpdated{} } -func (m *EventMarketUserSettleUpdated) String() string { return proto.CompactTextString(m) } -func (*EventMarketUserSettleUpdated) ProtoMessage() {} -func (*EventMarketUserSettleUpdated) Descriptor() ([]byte, []int) { +func (m *EventMarketUserSettleEnabled) Reset() { *m = EventMarketUserSettleEnabled{} } +func (m *EventMarketUserSettleEnabled) String() string { return proto.CompactTextString(m) } +func (*EventMarketUserSettleEnabled) ProtoMessage() {} +func (*EventMarketUserSettleEnabled) Descriptor() ([]byte, []int) { return fileDescriptor_c1b69385a348cffa, []int{8} } -func (m *EventMarketUserSettleUpdated) XXX_Unmarshal(b []byte) error { +func (m *EventMarketUserSettleEnabled) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *EventMarketUserSettleUpdated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *EventMarketUserSettleEnabled) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_EventMarketUserSettleUpdated.Marshal(b, m, deterministic) + return xxx_messageInfo_EventMarketUserSettleEnabled.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -511,26 +511,81 @@ func (m *EventMarketUserSettleUpdated) XXX_Marshal(b []byte, deterministic bool) return b[:n], nil } } -func (m *EventMarketUserSettleUpdated) XXX_Merge(src proto.Message) { - xxx_messageInfo_EventMarketUserSettleUpdated.Merge(m, src) +func (m *EventMarketUserSettleEnabled) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventMarketUserSettleEnabled.Merge(m, src) } -func (m *EventMarketUserSettleUpdated) XXX_Size() int { +func (m *EventMarketUserSettleEnabled) XXX_Size() int { return m.Size() } -func (m *EventMarketUserSettleUpdated) XXX_DiscardUnknown() { - xxx_messageInfo_EventMarketUserSettleUpdated.DiscardUnknown(m) +func (m *EventMarketUserSettleEnabled) XXX_DiscardUnknown() { + xxx_messageInfo_EventMarketUserSettleEnabled.DiscardUnknown(m) } -var xxx_messageInfo_EventMarketUserSettleUpdated proto.InternalMessageInfo +var xxx_messageInfo_EventMarketUserSettleEnabled proto.InternalMessageInfo -func (m *EventMarketUserSettleUpdated) GetMarketId() uint32 { +func (m *EventMarketUserSettleEnabled) GetMarketId() uint32 { if m != nil { return m.MarketId } return 0 } -func (m *EventMarketUserSettleUpdated) GetUpdatedBy() string { +func (m *EventMarketUserSettleEnabled) GetUpdatedBy() string { + if m != nil { + return m.UpdatedBy + } + return "" +} + +// EventMarketUserSettleDisabled is an event emitted when a market's user_settle option is disabled. +type EventMarketUserSettleDisabled struct { + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // updated_by is the account that updated the user_settle option. + UpdatedBy string `protobuf:"bytes,2,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"` +} + +func (m *EventMarketUserSettleDisabled) Reset() { *m = EventMarketUserSettleDisabled{} } +func (m *EventMarketUserSettleDisabled) String() string { return proto.CompactTextString(m) } +func (*EventMarketUserSettleDisabled) ProtoMessage() {} +func (*EventMarketUserSettleDisabled) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{9} +} +func (m *EventMarketUserSettleDisabled) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventMarketUserSettleDisabled) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventMarketUserSettleDisabled.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventMarketUserSettleDisabled) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventMarketUserSettleDisabled.Merge(m, src) +} +func (m *EventMarketUserSettleDisabled) XXX_Size() int { + return m.Size() +} +func (m *EventMarketUserSettleDisabled) XXX_DiscardUnknown() { + xxx_messageInfo_EventMarketUserSettleDisabled.DiscardUnknown(m) +} + +var xxx_messageInfo_EventMarketUserSettleDisabled proto.InternalMessageInfo + +func (m *EventMarketUserSettleDisabled) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *EventMarketUserSettleDisabled) GetUpdatedBy() string { if m != nil { return m.UpdatedBy } @@ -549,7 +604,7 @@ func (m *EventMarketPermissionsUpdated) Reset() { *m = EventMarketPermis func (m *EventMarketPermissionsUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketPermissionsUpdated) ProtoMessage() {} func (*EventMarketPermissionsUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{9} + return fileDescriptor_c1b69385a348cffa, []int{10} } func (m *EventMarketPermissionsUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -604,7 +659,7 @@ func (m *EventMarketReqAttrUpdated) Reset() { *m = EventMarketReqAttrUpd func (m *EventMarketReqAttrUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketReqAttrUpdated) ProtoMessage() {} func (*EventMarketReqAttrUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{10} + return fileDescriptor_c1b69385a348cffa, []int{11} } func (m *EventMarketReqAttrUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -657,7 +712,7 @@ func (m *EventMarketCreated) Reset() { *m = EventMarketCreated{} } func (m *EventMarketCreated) String() string { return proto.CompactTextString(m) } func (*EventMarketCreated) ProtoMessage() {} func (*EventMarketCreated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{11} + return fileDescriptor_c1b69385a348cffa, []int{12} } func (m *EventMarketCreated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -703,7 +758,7 @@ func (m *EventMarketFeesUpdated) Reset() { *m = EventMarketFeesUpdated{} func (m *EventMarketFeesUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketFeesUpdated) ProtoMessage() {} func (*EventMarketFeesUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{12} + return fileDescriptor_c1b69385a348cffa, []int{13} } func (m *EventMarketFeesUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -747,7 +802,7 @@ func (m *EventParamsUpdated) Reset() { *m = EventParamsUpdated{} } func (m *EventParamsUpdated) String() string { return proto.CompactTextString(m) } func (*EventParamsUpdated) ProtoMessage() {} func (*EventParamsUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{13} + return fileDescriptor_c1b69385a348cffa, []int{14} } func (m *EventParamsUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -785,7 +840,8 @@ func init() { proto.RegisterType((*EventMarketDetailsUpdated)(nil), "provenance.exchange.v1.EventMarketDetailsUpdated") proto.RegisterType((*EventMarketEnabled)(nil), "provenance.exchange.v1.EventMarketEnabled") proto.RegisterType((*EventMarketDisabled)(nil), "provenance.exchange.v1.EventMarketDisabled") - proto.RegisterType((*EventMarketUserSettleUpdated)(nil), "provenance.exchange.v1.EventMarketUserSettleUpdated") + proto.RegisterType((*EventMarketUserSettleEnabled)(nil), "provenance.exchange.v1.EventMarketUserSettleEnabled") + proto.RegisterType((*EventMarketUserSettleDisabled)(nil), "provenance.exchange.v1.EventMarketUserSettleDisabled") proto.RegisterType((*EventMarketPermissionsUpdated)(nil), "provenance.exchange.v1.EventMarketPermissionsUpdated") proto.RegisterType((*EventMarketReqAttrUpdated)(nil), "provenance.exchange.v1.EventMarketReqAttrUpdated") proto.RegisterType((*EventMarketCreated)(nil), "provenance.exchange.v1.EventMarketCreated") @@ -798,41 +854,41 @@ func init() { } var fileDescriptor_c1b69385a348cffa = []byte{ - // 540 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xc1, 0x6e, 0xd3, 0x4c, - 0x10, 0xc7, 0xe3, 0xef, 0xab, 0xa0, 0x99, 0xb4, 0xa2, 0x98, 0xaa, 0x4a, 0x80, 0x9a, 0xca, 0xbd, - 0x94, 0x43, 0x6c, 0x45, 0x08, 0x21, 0xc1, 0xa9, 0xa1, 0xad, 0xd4, 0x43, 0x45, 0x94, 0x52, 0x21, - 0x71, 0x89, 0x36, 0xd9, 0x69, 0xb2, 0xd4, 0xde, 0x4d, 0x77, 0x37, 0x69, 0x2d, 0xf1, 0x10, 0x3c, - 0x0c, 0x0f, 0xc1, 0xb1, 0xe2, 0x80, 0x38, 0xa2, 0xe4, 0x45, 0x90, 0xbd, 0x76, 0xe2, 0x48, 0x28, - 0xe9, 0xc5, 0xc7, 0x19, 0xff, 0xe6, 0xff, 0xdf, 0x9d, 0x59, 0x8d, 0x61, 0x7f, 0x28, 0xc5, 0x18, - 0x39, 0xe1, 0x3d, 0xf4, 0xf1, 0xb6, 0x37, 0x20, 0xbc, 0x8f, 0xfe, 0xb8, 0xe1, 0xe3, 0x18, 0xb9, - 0x56, 0xde, 0x50, 0x0a, 0x2d, 0xec, 0x9d, 0x39, 0xe4, 0x65, 0x90, 0x37, 0x6e, 0x3c, 0xad, 0xf5, - 0x84, 0x0a, 0x85, 0xea, 0x24, 0x94, 0x6f, 0x02, 0x53, 0xe2, 0x9e, 0xc1, 0xe3, 0xe3, 0x58, 0xe2, - 0x83, 0xa4, 0x28, 0xdf, 0x4b, 0x24, 0x1a, 0xa9, 0x5d, 0x83, 0x75, 0x11, 0xc7, 0x1d, 0x46, 0xab, - 0xd6, 0x9e, 0x75, 0xb0, 0xd6, 0x7e, 0x98, 0xc4, 0xa7, 0xd4, 0xde, 0x05, 0x30, 0x9f, 0x74, 0x34, - 0xc4, 0xea, 0x7f, 0x7b, 0xd6, 0x41, 0xb9, 0x5d, 0x4e, 0x32, 0x1f, 0xa3, 0x21, 0xba, 0x21, 0x3c, - 0xc9, 0xc9, 0xc5, 0x07, 0x09, 0x82, 0xe5, 0x82, 0xef, 0x60, 0xa3, 0x97, 0x71, 0x9d, 0x6e, 0x64, - 0x24, 0x9b, 0xd5, 0x9f, 0xdf, 0xeb, 0xdb, 0xe9, 0x41, 0x0f, 0x29, 0x95, 0xa8, 0xd4, 0xb9, 0x96, - 0x8c, 0xf7, 0xdb, 0x95, 0x19, 0xdd, 0x8c, 0xdc, 0x3a, 0x6c, 0xcd, 0xed, 0x4e, 0xd8, 0x0a, 0x2f, - 0xf7, 0x2b, 0xd4, 0xe6, 0x78, 0x8b, 0x48, 0xcd, 0x48, 0x10, 0x44, 0x2b, 0xeb, 0xec, 0x7d, 0xd8, - 0x24, 0x4a, 0xa1, 0x56, 0x9d, 0xcb, 0x84, 0x4d, 0xef, 0xbd, 0x61, 0x92, 0x69, 0xfd, 0x0b, 0xa8, - 0x5c, 0x22, 0xce, 0x90, 0xff, 0x13, 0x04, 0xe2, 0x94, 0x01, 0xdc, 0x5f, 0x56, 0xda, 0x9c, 0x33, - 0x22, 0xaf, 0x50, 0x7f, 0x62, 0x7a, 0x40, 0x25, 0xb9, 0xb1, 0x9f, 0x41, 0x39, 0x4c, 0x32, 0x99, - 0xf3, 0x66, 0x7b, 0xdd, 0x24, 0x4e, 0xa9, 0xfd, 0x12, 0xb6, 0x48, 0x28, 0x46, 0x5c, 0x77, 0x6e, - 0x52, 0x9e, 0xa7, 0xee, 0x8f, 0x4c, 0x3e, 0x93, 0xe1, 0xf6, 0x5b, 0xa8, 0x50, 0x54, 0x9a, 0x71, - 0xa2, 0x99, 0xe0, 0xe6, 0x00, 0xcb, 0x1a, 0x99, 0x83, 0xe3, 0x29, 0xcc, 0xf4, 0xe3, 0x29, 0xac, - 0xad, 0x2a, 0x9e, 0xd1, 0xcd, 0xc8, 0xbd, 0x4e, 0xdb, 0x6a, 0xee, 0x75, 0x84, 0x9a, 0xb0, 0x40, - 0x5d, 0x0c, 0x69, 0xf2, 0x96, 0x96, 0xde, 0xee, 0x0d, 0xc0, 0xc8, 0x70, 0xf7, 0x19, 0x7d, 0x39, - 0x65, 0x9b, 0x91, 0xfb, 0x05, 0xec, 0x9c, 0xe5, 0x31, 0x27, 0xdd, 0xa0, 0x30, 0xaf, 0xab, 0x85, - 0xb1, 0x1d, 0x31, 0x55, 0xa4, 0x99, 0x86, 0xe7, 0x39, 0xb3, 0x0b, 0x85, 0xf2, 0x1c, 0xb5, 0x0e, - 0xb0, 0xd8, 0x76, 0x8e, 0x60, 0x37, 0xe7, 0xda, 0x42, 0x19, 0x32, 0xa5, 0x98, 0xe0, 0x05, 0x4f, - 0x71, 0xf1, 0xe1, 0xb4, 0xf1, 0xfa, 0x50, 0x6b, 0x59, 0xac, 0x65, 0x63, 0xe1, 0xe1, 0x64, 0x0b, - 0x6f, 0x99, 0x97, 0xfb, 0x1a, 0x76, 0x72, 0x25, 0x27, 0x88, 0xf7, 0xea, 0x8a, 0xbb, 0x9d, 0x3a, - 0xb5, 0x88, 0x24, 0x61, 0x56, 0xd2, 0xc4, 0x1f, 0x13, 0xc7, 0xba, 0x9b, 0x38, 0xd6, 0x9f, 0x89, - 0x63, 0x7d, 0x9b, 0x3a, 0xa5, 0xbb, 0xa9, 0x53, 0xfa, 0x3d, 0x75, 0x4a, 0x50, 0x63, 0xc2, 0xfb, - 0xf7, 0xfe, 0x6e, 0x59, 0x9f, 0xbd, 0x3e, 0xd3, 0x83, 0x51, 0xd7, 0xeb, 0x89, 0xd0, 0x9f, 0x43, - 0x75, 0x26, 0x72, 0x91, 0x7f, 0x3b, 0xfb, 0x33, 0x74, 0x1f, 0x24, 0xdb, 0xfd, 0xd5, 0xdf, 0x00, - 0x00, 0x00, 0xff, 0xff, 0xc4, 0xf4, 0x51, 0x87, 0x37, 0x06, 0x00, 0x00, + // 543 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xcd, 0x6e, 0xd3, 0x40, + 0x10, 0xc7, 0x6b, 0xa8, 0xa0, 0x99, 0xb4, 0xa2, 0x98, 0xaa, 0x4a, 0x80, 0x9a, 0xca, 0xbd, 0x94, + 0x43, 0x6c, 0x45, 0x08, 0x21, 0xc1, 0xa9, 0xa1, 0xad, 0xd4, 0x43, 0x45, 0x94, 0x52, 0x21, 0x71, + 0x89, 0x36, 0xd9, 0x69, 0xb2, 0xd4, 0xde, 0x4d, 0x77, 0x37, 0x69, 0x2d, 0xf1, 0x10, 0x3c, 0x0c, + 0x0f, 0xc1, 0xb1, 0xe2, 0x80, 0x38, 0xa2, 0xe4, 0x45, 0x90, 0xbd, 0x76, 0xe2, 0x48, 0x55, 0xd2, + 0x8b, 0x8f, 0x33, 0xfe, 0xcd, 0xfc, 0x77, 0x3e, 0xe4, 0x81, 0xbd, 0x81, 0x14, 0x23, 0xe4, 0x84, + 0x77, 0xd1, 0xc7, 0x9b, 0x6e, 0x9f, 0xf0, 0x1e, 0xfa, 0xa3, 0xba, 0x8f, 0x23, 0xe4, 0x5a, 0x79, + 0x03, 0x29, 0xb4, 0xb0, 0xb7, 0x67, 0x90, 0x97, 0x41, 0xde, 0xa8, 0xfe, 0xbc, 0xda, 0x15, 0x2a, + 0x14, 0xaa, 0x9d, 0x50, 0xbe, 0x31, 0x4c, 0x88, 0x7b, 0x0a, 0x4f, 0x8f, 0xe2, 0x14, 0x9f, 0x24, + 0x45, 0xf9, 0x51, 0x22, 0xd1, 0x48, 0xed, 0x2a, 0xac, 0x89, 0xd8, 0x6e, 0x33, 0x5a, 0xb1, 0x76, + 0xad, 0xfd, 0xd5, 0xd6, 0xe3, 0xc4, 0x3e, 0xa1, 0xf6, 0x0e, 0x80, 0xf9, 0xa4, 0xa3, 0x01, 0x56, + 0x1e, 0xec, 0x5a, 0xfb, 0xa5, 0x56, 0x29, 0xf1, 0x7c, 0x8e, 0x06, 0xe8, 0x86, 0xf0, 0x2c, 0x97, + 0x2e, 0x7e, 0x48, 0x10, 0x2c, 0x4e, 0xf8, 0x01, 0xd6, 0xbb, 0x19, 0xd7, 0xee, 0x44, 0x26, 0x65, + 0xa3, 0xf2, 0xfb, 0x67, 0x6d, 0x2b, 0x7d, 0xe8, 0x01, 0xa5, 0x12, 0x95, 0x3a, 0xd3, 0x92, 0xf1, + 0x5e, 0xab, 0x3c, 0xa5, 0x1b, 0x91, 0x5b, 0x83, 0xcd, 0x99, 0xdc, 0x31, 0x5b, 0xa2, 0xe5, 0x7e, + 0x87, 0xea, 0x0c, 0x6f, 0x12, 0xa9, 0x19, 0x09, 0x82, 0x68, 0x69, 0x9c, 0xbd, 0x07, 0x1b, 0x44, + 0x29, 0xd4, 0xaa, 0x7d, 0x91, 0xb0, 0x69, 0xdd, 0xeb, 0xc6, 0x99, 0xc6, 0xbf, 0x82, 0xf2, 0x05, + 0xe2, 0x14, 0x79, 0x98, 0x20, 0x10, 0xbb, 0x0c, 0xe0, 0xfe, 0xb1, 0xd2, 0xe6, 0x9c, 0x12, 0x79, + 0x89, 0xfa, 0x0b, 0xd3, 0x7d, 0x2a, 0xc9, 0xb5, 0xfd, 0x02, 0x4a, 0x61, 0xe2, 0xc9, 0x94, 0x37, + 0x5a, 0x6b, 0xc6, 0x71, 0x42, 0xed, 0xd7, 0xb0, 0x49, 0x42, 0x31, 0xe4, 0xba, 0x7d, 0x9d, 0xf2, + 0x3c, 0x55, 0x7f, 0x62, 0xfc, 0x59, 0x1a, 0x6e, 0xbf, 0x87, 0x32, 0x45, 0xa5, 0x19, 0x27, 0x9a, + 0x09, 0x6e, 0x1e, 0xb0, 0xa8, 0x91, 0x39, 0x38, 0x9e, 0xc2, 0x34, 0x7f, 0x3c, 0x85, 0xd5, 0x65, + 0xc1, 0x53, 0xba, 0x11, 0xb9, 0x57, 0x69, 0x5b, 0x4d, 0x5d, 0x87, 0xa8, 0x09, 0x0b, 0xd4, 0xf9, + 0x80, 0x26, 0xbb, 0xb4, 0xb0, 0xba, 0x77, 0x00, 0x43, 0xc3, 0xdd, 0x67, 0xf4, 0xa5, 0x94, 0x6d, + 0x44, 0xee, 0x37, 0xb0, 0x73, 0x92, 0x47, 0x9c, 0x74, 0x82, 0xc2, 0xb4, 0x2e, 0xe7, 0xc6, 0x76, + 0xc8, 0x54, 0x91, 0x62, 0x1a, 0x5e, 0xe6, 0xc4, 0xce, 0x15, 0xca, 0x33, 0xd4, 0x3a, 0xc0, 0x62, + 0x4b, 0x1c, 0xc2, 0xce, 0x9d, 0xaa, 0x05, 0x17, 0x3b, 0x2f, 0xdb, 0x44, 0x19, 0x32, 0xa5, 0x98, + 0xe0, 0x05, 0x2f, 0xcf, 0xfc, 0xbe, 0xb6, 0xf0, 0xea, 0x40, 0x6b, 0x59, 0xac, 0x64, 0x7d, 0x6e, + 0x5f, 0xb3, 0xff, 0xec, 0x22, 0x2d, 0xf7, 0x2d, 0x6c, 0xe7, 0x42, 0x8e, 0x11, 0xef, 0xd5, 0x15, + 0x77, 0x2b, 0x55, 0x6a, 0x12, 0x49, 0xc2, 0x2c, 0xa4, 0x81, 0xbf, 0xc6, 0x8e, 0x75, 0x3b, 0x76, + 0xac, 0x7f, 0x63, 0xc7, 0xfa, 0x31, 0x71, 0x56, 0x6e, 0x27, 0xce, 0xca, 0xdf, 0x89, 0xb3, 0x02, + 0x55, 0x26, 0xbc, 0xbb, 0xcf, 0x46, 0xd3, 0xfa, 0xea, 0xf5, 0x98, 0xee, 0x0f, 0x3b, 0x5e, 0x57, + 0x84, 0xfe, 0x0c, 0xaa, 0x31, 0x91, 0xb3, 0xfc, 0x9b, 0xe9, 0x41, 0xea, 0x3c, 0x4a, 0x8e, 0xca, + 0x9b, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xe7, 0xf8, 0x30, 0x4d, 0xae, 0x06, 0x00, 0x00, } func (m *EventOrderCreated) Marshal() (dAtA []byte, err error) { @@ -1129,7 +1185,42 @@ func (m *EventMarketDisabled) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *EventMarketUserSettleUpdated) Marshal() (dAtA []byte, err error) { +func (m *EventMarketUserSettleEnabled) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventMarketUserSettleEnabled) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventMarketUserSettleEnabled) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UpdatedBy) > 0 { + i -= len(m.UpdatedBy) + copy(dAtA[i:], m.UpdatedBy) + i = encodeVarintEvents(dAtA, i, uint64(len(m.UpdatedBy))) + i-- + dAtA[i] = 0x12 + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EventMarketUserSettleDisabled) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1139,12 +1230,12 @@ func (m *EventMarketUserSettleUpdated) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *EventMarketUserSettleUpdated) MarshalTo(dAtA []byte) (int, error) { +func (m *EventMarketUserSettleDisabled) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *EventMarketUserSettleUpdated) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *EventMarketUserSettleDisabled) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -1460,7 +1551,23 @@ func (m *EventMarketDisabled) Size() (n int) { return n } -func (m *EventMarketUserSettleUpdated) Size() (n int) { +func (m *EventMarketUserSettleEnabled) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } + l = len(m.UpdatedBy) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventMarketUserSettleDisabled) Size() (n int) { if m == nil { return 0 } @@ -2419,7 +2526,108 @@ func (m *EventMarketDisabled) Unmarshal(dAtA []byte) error { } return nil } -func (m *EventMarketUserSettleUpdated) Unmarshal(dAtA []byte) error { +func (m *EventMarketUserSettleEnabled) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventMarketUserSettleEnabled: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventMarketUserSettleEnabled: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdatedBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UpdatedBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventMarketUserSettleDisabled) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2442,10 +2650,10 @@ func (m *EventMarketUserSettleUpdated) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: EventMarketUserSettleUpdated: wiretype end group for non-group") + return fmt.Errorf("proto: EventMarketUserSettleDisabled: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: EventMarketUserSettleUpdated: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: EventMarketUserSettleDisabled: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: From cb7a1bdc0e6cb67098377c1350451b2c3d0b9d0b Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 12:48:06 -0600 Subject: [PATCH 144/309] [1658]: Constructors and test updates for recent event changes. --- x/exchange/events.go | 43 ++++-- x/exchange/events_test.go | 313 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 330 insertions(+), 26 deletions(-) diff --git a/x/exchange/events.go b/x/exchange/events.go index 056761405d..6731e440f6 100644 --- a/x/exchange/events.go +++ b/x/exchange/events.go @@ -1,6 +1,10 @@ package exchange -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + "github.com/gogo/protobuf/proto" + + sdk "github.com/cosmos/cosmos-sdk/types" +) func NewEventOrderCreated(order *Order) *EventOrderCreated { return &EventOrderCreated{ @@ -46,6 +50,15 @@ func NewEventMarketDetailsUpdated(marketID uint32, updatedBy sdk.AccAddress) *Ev } } +// NewEventMarketActiveUpdated returns a new EventMarketEnabled if isActive == true, +// or a new EventMarketDisabled if isActive == false. +func NewEventMarketActiveUpdated(marketID uint32, updatedBy sdk.AccAddress, isActive bool) proto.Message { + if isActive { + return NewEventMarketEnabled(marketID, updatedBy) + } + return NewEventMarketDisabled(marketID, updatedBy) +} + func NewEventMarketEnabled(marketID uint32, updatedBy sdk.AccAddress) *EventMarketEnabled { return &EventMarketEnabled{ MarketId: marketID, @@ -60,8 +73,24 @@ func NewEventMarketDisabled(marketID uint32, updatedBy sdk.AccAddress) *EventMar } } -func NewEventMarketUserSettleUpdated(marketID uint32, updatedBy sdk.AccAddress) *EventMarketUserSettleUpdated { - return &EventMarketUserSettleUpdated{ +// NewEventMarketUserSettleUpdated returns a new EventMarketUserSettleEnabled if isAllowed == true, +// or a new EventMarketUserSettleDisabled if isActive == false. +func NewEventMarketUserSettleUpdated(marketID uint32, updatedBy sdk.AccAddress, isAllowed bool) proto.Message { + if isAllowed { + return NewEventMarketUserSettleEnabled(marketID, updatedBy) + } + return NewEventMarketUserSettleDisabled(marketID, updatedBy) +} + +func NewEventMarketUserSettleEnabled(marketID uint32, updatedBy sdk.AccAddress) *EventMarketUserSettleEnabled { + return &EventMarketUserSettleEnabled{ + MarketId: marketID, + UpdatedBy: updatedBy.String(), + } +} + +func NewEventMarketUserSettleDisabled(marketID uint32, updatedBy sdk.AccAddress) *EventMarketUserSettleDisabled { + return &EventMarketUserSettleDisabled{ MarketId: marketID, UpdatedBy: updatedBy.String(), } @@ -81,14 +110,6 @@ func NewEventMarketReqAttrUpdated(marketID uint32, updatedBy sdk.AccAddress) *Ev } } -func NewEventCreateMarketSubmitted(marketID uint32, proposalID uint64, submittedBy sdk.AccAddress) *EventCreateMarketSubmitted { - return &EventCreateMarketSubmitted{ - MarketId: marketID, - ProposalId: proposalID, - SubmittedBy: submittedBy.String(), - } -} - func NewEventMarketCreated(marketID uint32) *EventMarketCreated { return &EventMarketCreated{ MarketId: marketID, diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go index 42d0c53b52..3d76678c03 100644 --- a/x/exchange/events_test.go +++ b/x/exchange/events_test.go @@ -1,13 +1,18 @@ package exchange import ( + "fmt" "testing" "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/provenance-io/provenance/testutil/assertions" ) // assertEverythingSet asserts that the provided proto.Message has a value for all fields. @@ -132,6 +137,41 @@ func TestNewEventMarketDetailsUpdated(t *testing.T) { assertEverythingSet(t, event, "EventMarketDetailsUpdated") } +func TestNewEventMarketActiveUpdated(t *testing.T) { + someAddr := sdk.AccAddress("some_address________") + + tests := []struct { + name string + marketID uint32 + updatedBy sdk.AccAddress + isActive bool + expected proto.Message + }{ + { + name: "enabled", + marketID: 33, + updatedBy: someAddr, + isActive: true, + expected: NewEventMarketEnabled(33, someAddr), + }, + { + name: "disabled", + marketID: 556, + updatedBy: someAddr, + isActive: false, + expected: NewEventMarketDisabled(556, someAddr), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + actual := NewEventMarketActiveUpdated(tc.marketID, tc.updatedBy, tc.isActive) + assert.Equal(t, tc.expected, actual, "NewEventMarketActiveUpdated(%d, %q, %t) result", + tc.marketID, tc.updatedBy.String(), tc.isActive) + }) + } +} + func TestNewEventMarketEnabled(t *testing.T) { marketID := uint32(919) updatedBy := sdk.AccAddress("updatedBy___________") @@ -153,13 +193,59 @@ func TestNewEventMarketDisabled(t *testing.T) { } func TestNewEventMarketUserSettleUpdated(t *testing.T) { + someAddr := sdk.AccAddress("some_address________") + + tests := []struct { + name string + marketID uint32 + updatedBy sdk.AccAddress + isAllowed bool + expected proto.Message + }{ + { + name: "enabled", + marketID: 33, + updatedBy: someAddr, + isAllowed: true, + expected: NewEventMarketUserSettleEnabled(33, someAddr), + }, + { + name: "disabled", + marketID: 556, + updatedBy: someAddr, + isAllowed: false, + expected: NewEventMarketUserSettleDisabled(556, someAddr), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + actual := NewEventMarketUserSettleUpdated(tc.marketID, tc.updatedBy, tc.isAllowed) + assert.Equal(t, tc.expected, actual, "NewEventMarketUserSettleUpdated(%d, %q, %t) result", + tc.marketID, tc.updatedBy.String(), tc.isAllowed) + }) + } +} + +func TestNewEventMarketUserSettleEnabled(t *testing.T) { + marketID := uint32(123) + updatedBy := sdk.AccAddress("updatedBy___________") + + event := NewEventMarketEnabled(marketID, updatedBy) + assert.Equal(t, marketID, event.MarketId, "MarketId") + assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") + assertEverythingSet(t, event, "EventMarketEnabled") + +} + +func TestNewEventMarketUserSettleDisabled(t *testing.T) { marketID := uint32(123) updatedBy := sdk.AccAddress("updatedBy___________") - event := NewEventMarketUserSettleUpdated(marketID, updatedBy) + event := NewEventMarketUserSettleDisabled(marketID, updatedBy) assert.Equal(t, marketID, event.MarketId, "MarketId") assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") - assertEverythingSet(t, event, "EventMarketUserSettleUpdated") + assertEverythingSet(t, event, "EventMarketUserSettleDisabled") } func TestNewEventMarketPermissionsUpdated(t *testing.T) { @@ -182,18 +268,6 @@ func TestNewEventMarketReqAttrUpdated(t *testing.T) { assertEverythingSet(t, event, "EventMarketReqAttrUpdated") } -func TestNewEventCreateMarketSubmitted(t *testing.T) { - marketID := uint32(5445) - proposalID := uint64(88888) - submittedBy := sdk.AccAddress("submittedBy_________") - - event := NewEventCreateMarketSubmitted(marketID, proposalID, submittedBy) - assert.Equal(t, marketID, event.MarketId, "MarketId") - assert.Equal(t, proposalID, event.ProposalId, "ProposalId") - assert.Equal(t, submittedBy.String(), event.SubmittedBy, "SubmittedBy") - assertEverythingSet(t, event, "EventCreateMarketSubmitted") -} - func TestNewEventMarketCreated(t *testing.T) { marketID := uint32(10111213) @@ -215,4 +289,213 @@ func TestNewEventParamsUpdated(t *testing.T) { assertEverythingSet(t, event, "EventParamsUpdated") } -// TODO[1658]: func TestTypedEventToEvent(t *testing.T) +func TestTypedEventToEvent(t *testing.T) { + quoteBz := func(str string) []byte { + return []byte(fmt.Sprintf("%q", str)) + } + cancelledBy := sdk.AccAddress("cancelledBy_________") + cancelledByQ := quoteBz(cancelledBy.String()) + destination := sdk.AccAddress("destination_________") + destinationQ := quoteBz(destination.String()) + withdrawnBy := sdk.AccAddress("withdrawnBy_________") + withdrawnByQ := quoteBz(withdrawnBy.String()) + updatedBy := sdk.AccAddress("updatedBy___________") + updatedByQ := quoteBz(updatedBy.String()) + coins1 := sdk.NewCoins(sdk.NewInt64Coin("onecoin", 1), sdk.NewInt64Coin("twocoin", 2)) + coins1Q := quoteBz(coins1.String()) + coins2 := sdk.NewCoins(sdk.NewInt64Coin("threecoin", 3), sdk.NewInt64Coin("fourcoin", 4)) + coins2Q := quoteBz(coins2.String()) + + tests := []struct { + name string + tev proto.Message + expEvent sdk.Event + }{ + { + name: "EventOrderCreated ask", + tev: NewEventOrderCreated(NewOrder(1).WithAsk(&AskOrder{})), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventOrderCreated", + Attributes: []abci.EventAttribute{ + {Key: []byte("order_id"), Value: quoteBz("1")}, + {Key: []byte("order_type"), Value: quoteBz("ask")}, + }, + }, + }, + { + name: "EventOrderCreated bid", + tev: NewEventOrderCreated(NewOrder(2).WithBid(&BidOrder{})), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventOrderCreated", + Attributes: []abci.EventAttribute{ + {Key: []byte("order_id"), Value: quoteBz("2")}, + {Key: []byte("order_type"), Value: quoteBz("bid")}, + }, + }, + }, + { + name: "EventOrderCancelled", + tev: NewEventOrderCancelled(3, cancelledBy), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventOrderCancelled", + Attributes: []abci.EventAttribute{ + {Key: []byte("cancelled_by"), Value: cancelledByQ}, + {Key: []byte("order_id"), Value: quoteBz("3")}, + }, + }, + }, + { + name: "EventOrderFilled", + tev: NewEventOrderFilled(4), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventOrderFilled", + Attributes: []abci.EventAttribute{ + {Key: []byte("order_id"), Value: quoteBz("4")}, + }, + }, + }, + { + name: "EventOrderPartiallyFilled", + tev: NewEventOrderPartiallyFilled(5, coins1, coins2), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventOrderPartiallyFilled", + Attributes: []abci.EventAttribute{ + {Key: []byte("assets_filled"), Value: coins1Q}, + {Key: []byte("fees_filled"), Value: coins2Q}, + {Key: []byte("order_id"), Value: quoteBz("5")}, + }, + }, + }, + { + name: "EventMarketWithdraw", + tev: NewEventMarketWithdraw(6, coins1, destination, withdrawnBy), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventMarketWithdraw", + Attributes: []abci.EventAttribute{ + {Key: []byte("amount_withdrawn"), Value: coins1Q}, + {Key: []byte("destination"), Value: destinationQ}, + {Key: []byte("market_id"), Value: []byte("6")}, + {Key: []byte("withdrawn_by"), Value: withdrawnByQ}, + }, + }, + }, + { + name: "EventMarketDetailsUpdated", + tev: NewEventMarketDetailsUpdated(7, updatedBy), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventMarketDetailsUpdated", + Attributes: []abci.EventAttribute{ + {Key: []byte("market_id"), Value: []byte("7")}, + {Key: []byte("updated_by"), Value: updatedByQ}, + }, + }, + }, + { + name: "EventMarketEnabled", + tev: NewEventMarketEnabled(8, updatedBy), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventMarketEnabled", + Attributes: []abci.EventAttribute{ + {Key: []byte("market_id"), Value: []byte("8")}, + {Key: []byte("updated_by"), Value: updatedByQ}, + }, + }, + }, + { + name: "EventMarketDisabled", + tev: NewEventMarketDisabled(9, updatedBy), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventMarketDisabled", + Attributes: []abci.EventAttribute{ + {Key: []byte("market_id"), Value: []byte("9")}, + {Key: []byte("updated_by"), Value: updatedByQ}, + }, + }, + }, + { + name: "EventMarketUserSettleEnabled", + tev: NewEventMarketUserSettleEnabled(10, updatedBy), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventMarketUserSettleEnabled", + Attributes: []abci.EventAttribute{ + {Key: []byte("market_id"), Value: []byte("10")}, + {Key: []byte("updated_by"), Value: updatedByQ}, + }, + }, + }, + { + name: "EventMarketUserSettleDisabled", + tev: NewEventMarketUserSettleDisabled(11, updatedBy), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventMarketUserSettleDisabled", + Attributes: []abci.EventAttribute{ + {Key: []byte("market_id"), Value: []byte("11")}, + {Key: []byte("updated_by"), Value: updatedByQ}, + }, + }, + }, + { + name: "EventMarketPermissionsUpdated", + tev: NewEventMarketPermissionsUpdated(12, updatedBy), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventMarketPermissionsUpdated", + Attributes: []abci.EventAttribute{ + {Key: []byte("market_id"), Value: []byte("12")}, + {Key: []byte("updated_by"), Value: updatedByQ}, + }, + }, + }, + { + name: "EventMarketReqAttrUpdated", + tev: NewEventMarketReqAttrUpdated(13, updatedBy), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventMarketReqAttrUpdated", + Attributes: []abci.EventAttribute{ + {Key: []byte("market_id"), Value: []byte("13")}, + {Key: []byte("updated_by"), Value: updatedByQ}, + }, + }, + }, + { + name: "EventMarketCreated", + tev: NewEventMarketCreated(14), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventMarketCreated", + Attributes: []abci.EventAttribute{ + {Key: []byte("market_id"), Value: []byte("14")}, + }, + }, + }, + { + name: "EventMarketFeesUpdated", + tev: NewEventMarketFeesUpdated(15), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventMarketFeesUpdated", + Attributes: []abci.EventAttribute{ + {Key: []byte("market_id"), Value: []byte("15")}, + }, + }, + }, + { + name: "EventParamsUpdated", + tev: NewEventParamsUpdated(), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventParamsUpdated", + Attributes: nil, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + event, err := sdk.TypedEventToEvent(tc.tev) + require.NoError(t, err, "TypedEventToEvent error") + if assert.NotNil(t, event, "TypedEventToEvent result") { + assert.Equal(t, tc.expEvent.Type, event.Type, "event type") + expAttrs := assertions.AttrsToStrings(tc.expEvent.Attributes) + actAttrs := assertions.AttrsToStrings(event.Attributes) + assert.Equal(t, expAttrs, actAttrs, "event attributes") + } + }) + } +} From ca9abe686200e9c4fc9233ec91faad527fb8d9be Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 13:08:58 -0600 Subject: [PATCH 145/309] [1658]: Rename the EventMarketWithdraw.AmountWithdrawn to just Amount. --- docs/proto-docs.md | 2 +- proto/provenance/exchange/v1/events.proto | 4 +- x/exchange/events.go | 10 +-- x/exchange/events.pb.go | 92 +++++++++++------------ x/exchange/events_test.go | 4 +- 5 files changed, 56 insertions(+), 56 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 914584505a..6342cf935d 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1468,7 +1468,7 @@ EventMarketWithdraw is an event emitted when a withdrawal of a market's collecte | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | -| `amount_withdrawn` | [string](#string) | | amount_withdrawn is the coins amount string of funds withdrawn from the market account. | +| `amount` | [string](#string) | | amount is the coins amount string of funds withdrawn from the market account. | | `destination` | [string](#string) | | destination is the account that received the funds. | | `withdrawn_by` | [string](#string) | | withdrawn_by is the account that requested the withdrawal. | diff --git a/proto/provenance/exchange/v1/events.proto b/proto/provenance/exchange/v1/events.proto index 52a97f0c23..20292363fa 100644 --- a/proto/provenance/exchange/v1/events.proto +++ b/proto/provenance/exchange/v1/events.proto @@ -45,8 +45,8 @@ message EventOrderPartiallyFilled { message EventMarketWithdraw { // market_id is the numerical identifier of the market. uint32 market_id = 1; - // amount_withdrawn is the coins amount string of funds withdrawn from the market account. - string amount_withdrawn = 2; + // amount is the coins amount string of funds withdrawn from the market account. + string amount = 2; // destination is the account that received the funds. string destination = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // withdrawn_by is the account that requested the withdrawal. diff --git a/x/exchange/events.go b/x/exchange/events.go index 6731e440f6..14b91bb81c 100644 --- a/x/exchange/events.go +++ b/x/exchange/events.go @@ -34,12 +34,12 @@ func NewEventOrderPartiallyFilled(orderID uint64, assetsFilled, feesFilled sdk.C } } -func NewEventMarketWithdraw(marketID uint32, amountWithdrawn sdk.Coins, destination, withdrawnBy sdk.AccAddress) *EventMarketWithdraw { +func NewEventMarketWithdraw(marketID uint32, amount sdk.Coins, destination, withdrawnBy sdk.AccAddress) *EventMarketWithdraw { return &EventMarketWithdraw{ - MarketId: marketID, - AmountWithdrawn: amountWithdrawn.String(), - Destination: destination.String(), - WithdrawnBy: withdrawnBy.String(), + MarketId: marketID, + Amount: amount.String(), + Destination: destination.String(), + WithdrawnBy: withdrawnBy.String(), } } diff --git a/x/exchange/events.pb.go b/x/exchange/events.pb.go index 9b0552a39c..64f8c4b3d4 100644 --- a/x/exchange/events.pb.go +++ b/x/exchange/events.pb.go @@ -248,8 +248,8 @@ func (m *EventOrderPartiallyFilled) GetFeesFilled() string { type EventMarketWithdraw struct { // market_id is the numerical identifier of the market. MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` - // amount_withdrawn is the coins amount string of funds withdrawn from the market account. - AmountWithdrawn string `protobuf:"bytes,2,opt,name=amount_withdrawn,json=amountWithdrawn,proto3" json:"amount_withdrawn,omitempty"` + // amount is the coins amount string of funds withdrawn from the market account. + Amount string `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount,omitempty"` // destination is the account that received the funds. Destination string `protobuf:"bytes,3,opt,name=destination,proto3" json:"destination,omitempty"` // withdrawn_by is the account that requested the withdrawal. @@ -296,9 +296,9 @@ func (m *EventMarketWithdraw) GetMarketId() uint32 { return 0 } -func (m *EventMarketWithdraw) GetAmountWithdrawn() string { +func (m *EventMarketWithdraw) GetAmount() string { if m != nil { - return m.AmountWithdrawn + return m.Amount } return "" } @@ -854,41 +854,41 @@ func init() { } var fileDescriptor_c1b69385a348cffa = []byte{ - // 543 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xcd, 0x6e, 0xd3, 0x40, - 0x10, 0xc7, 0x6b, 0xa8, 0xa0, 0x99, 0xb4, 0xa2, 0x98, 0xaa, 0x4a, 0x80, 0x9a, 0xca, 0xbd, 0x94, - 0x43, 0x6c, 0x45, 0x08, 0x21, 0xc1, 0xa9, 0xa1, 0xad, 0xd4, 0x43, 0x45, 0x94, 0x52, 0x21, 0x71, - 0x89, 0x36, 0xd9, 0x69, 0xb2, 0xd4, 0xde, 0x4d, 0x77, 0x37, 0x69, 0x2d, 0xf1, 0x10, 0x3c, 0x0c, - 0x0f, 0xc1, 0xb1, 0xe2, 0x80, 0x38, 0xa2, 0xe4, 0x45, 0x90, 0xbd, 0x76, 0xe2, 0x48, 0x55, 0xd2, - 0x8b, 0x8f, 0x33, 0xfe, 0xcd, 0xfc, 0x77, 0x3e, 0xe4, 0x81, 0xbd, 0x81, 0x14, 0x23, 0xe4, 0x84, - 0x77, 0xd1, 0xc7, 0x9b, 0x6e, 0x9f, 0xf0, 0x1e, 0xfa, 0xa3, 0xba, 0x8f, 0x23, 0xe4, 0x5a, 0x79, - 0x03, 0x29, 0xb4, 0xb0, 0xb7, 0x67, 0x90, 0x97, 0x41, 0xde, 0xa8, 0xfe, 0xbc, 0xda, 0x15, 0x2a, - 0x14, 0xaa, 0x9d, 0x50, 0xbe, 0x31, 0x4c, 0x88, 0x7b, 0x0a, 0x4f, 0x8f, 0xe2, 0x14, 0x9f, 0x24, - 0x45, 0xf9, 0x51, 0x22, 0xd1, 0x48, 0xed, 0x2a, 0xac, 0x89, 0xd8, 0x6e, 0x33, 0x5a, 0xb1, 0x76, - 0xad, 0xfd, 0xd5, 0xd6, 0xe3, 0xc4, 0x3e, 0xa1, 0xf6, 0x0e, 0x80, 0xf9, 0xa4, 0xa3, 0x01, 0x56, - 0x1e, 0xec, 0x5a, 0xfb, 0xa5, 0x56, 0x29, 0xf1, 0x7c, 0x8e, 0x06, 0xe8, 0x86, 0xf0, 0x2c, 0x97, - 0x2e, 0x7e, 0x48, 0x10, 0x2c, 0x4e, 0xf8, 0x01, 0xd6, 0xbb, 0x19, 0xd7, 0xee, 0x44, 0x26, 0x65, - 0xa3, 0xf2, 0xfb, 0x67, 0x6d, 0x2b, 0x7d, 0xe8, 0x01, 0xa5, 0x12, 0x95, 0x3a, 0xd3, 0x92, 0xf1, - 0x5e, 0xab, 0x3c, 0xa5, 0x1b, 0x91, 0x5b, 0x83, 0xcd, 0x99, 0xdc, 0x31, 0x5b, 0xa2, 0xe5, 0x7e, - 0x87, 0xea, 0x0c, 0x6f, 0x12, 0xa9, 0x19, 0x09, 0x82, 0x68, 0x69, 0x9c, 0xbd, 0x07, 0x1b, 0x44, - 0x29, 0xd4, 0xaa, 0x7d, 0x91, 0xb0, 0x69, 0xdd, 0xeb, 0xc6, 0x99, 0xc6, 0xbf, 0x82, 0xf2, 0x05, - 0xe2, 0x14, 0x79, 0x98, 0x20, 0x10, 0xbb, 0x0c, 0xe0, 0xfe, 0xb1, 0xd2, 0xe6, 0x9c, 0x12, 0x79, - 0x89, 0xfa, 0x0b, 0xd3, 0x7d, 0x2a, 0xc9, 0xb5, 0xfd, 0x02, 0x4a, 0x61, 0xe2, 0xc9, 0x94, 0x37, - 0x5a, 0x6b, 0xc6, 0x71, 0x42, 0xed, 0xd7, 0xb0, 0x49, 0x42, 0x31, 0xe4, 0xba, 0x7d, 0x9d, 0xf2, - 0x3c, 0x55, 0x7f, 0x62, 0xfc, 0x59, 0x1a, 0x6e, 0xbf, 0x87, 0x32, 0x45, 0xa5, 0x19, 0x27, 0x9a, - 0x09, 0x6e, 0x1e, 0xb0, 0xa8, 0x91, 0x39, 0x38, 0x9e, 0xc2, 0x34, 0x7f, 0x3c, 0x85, 0xd5, 0x65, - 0xc1, 0x53, 0xba, 0x11, 0xb9, 0x57, 0x69, 0x5b, 0x4d, 0x5d, 0x87, 0xa8, 0x09, 0x0b, 0xd4, 0xf9, - 0x80, 0x26, 0xbb, 0xb4, 0xb0, 0xba, 0x77, 0x00, 0x43, 0xc3, 0xdd, 0x67, 0xf4, 0xa5, 0x94, 0x6d, - 0x44, 0xee, 0x37, 0xb0, 0x73, 0x92, 0x47, 0x9c, 0x74, 0x82, 0xc2, 0xb4, 0x2e, 0xe7, 0xc6, 0x76, - 0xc8, 0x54, 0x91, 0x62, 0x1a, 0x5e, 0xe6, 0xc4, 0xce, 0x15, 0xca, 0x33, 0xd4, 0x3a, 0xc0, 0x62, - 0x4b, 0x1c, 0xc2, 0xce, 0x9d, 0xaa, 0x05, 0x17, 0x3b, 0x2f, 0xdb, 0x44, 0x19, 0x32, 0xa5, 0x98, - 0xe0, 0x05, 0x2f, 0xcf, 0xfc, 0xbe, 0xb6, 0xf0, 0xea, 0x40, 0x6b, 0x59, 0xac, 0x64, 0x7d, 0x6e, - 0x5f, 0xb3, 0xff, 0xec, 0x22, 0x2d, 0xf7, 0x2d, 0x6c, 0xe7, 0x42, 0x8e, 0x11, 0xef, 0xd5, 0x15, - 0x77, 0x2b, 0x55, 0x6a, 0x12, 0x49, 0xc2, 0x2c, 0xa4, 0x81, 0xbf, 0xc6, 0x8e, 0x75, 0x3b, 0x76, - 0xac, 0x7f, 0x63, 0xc7, 0xfa, 0x31, 0x71, 0x56, 0x6e, 0x27, 0xce, 0xca, 0xdf, 0x89, 0xb3, 0x02, - 0x55, 0x26, 0xbc, 0xbb, 0xcf, 0x46, 0xd3, 0xfa, 0xea, 0xf5, 0x98, 0xee, 0x0f, 0x3b, 0x5e, 0x57, - 0x84, 0xfe, 0x0c, 0xaa, 0x31, 0x91, 0xb3, 0xfc, 0x9b, 0xe9, 0x41, 0xea, 0x3c, 0x4a, 0x8e, 0xca, - 0x9b, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xe7, 0xf8, 0x30, 0x4d, 0xae, 0x06, 0x00, 0x00, + // 536 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xc1, 0x6e, 0xd3, 0x40, + 0x10, 0x40, 0x6b, 0xa8, 0x4a, 0x33, 0x69, 0x25, 0x30, 0x55, 0x94, 0x00, 0x35, 0x95, 0x7b, 0xe9, + 0x25, 0xb6, 0x22, 0x84, 0x90, 0xe0, 0xd4, 0xd0, 0x56, 0xea, 0xa1, 0x22, 0x4a, 0xa9, 0x90, 0xb8, + 0x44, 0x9b, 0x78, 0x9a, 0x2c, 0xb5, 0x77, 0xd3, 0xdd, 0x4d, 0x5a, 0x4b, 0x7c, 0x04, 0x1f, 0xc3, + 0x1f, 0x70, 0xe1, 0x58, 0x71, 0xe2, 0x88, 0x92, 0x1f, 0x41, 0xf6, 0xae, 0x13, 0x47, 0xaa, 0x92, + 0x5e, 0x7c, 0x9c, 0xc9, 0x9b, 0x7d, 0x9e, 0xd9, 0x51, 0x16, 0xf6, 0x87, 0x82, 0x8f, 0x91, 0x11, + 0xd6, 0x43, 0x1f, 0x6f, 0x7b, 0x03, 0xc2, 0xfa, 0xe8, 0x8f, 0x1b, 0x3e, 0x8e, 0x91, 0x29, 0xe9, + 0x0d, 0x05, 0x57, 0xdc, 0xae, 0xcc, 0x21, 0x2f, 0x83, 0xbc, 0x71, 0xe3, 0x45, 0xad, 0xc7, 0x65, + 0xc4, 0x65, 0x27, 0xa5, 0x7c, 0x1d, 0xe8, 0x12, 0xf7, 0x0c, 0x9e, 0x1d, 0x27, 0x47, 0x7c, 0x12, + 0x01, 0x8a, 0x8f, 0x02, 0x89, 0xc2, 0xc0, 0xae, 0xc1, 0x26, 0x4f, 0xe2, 0x0e, 0x0d, 0xaa, 0xd6, + 0x9e, 0x75, 0xb0, 0xde, 0x7e, 0x92, 0xc6, 0xa7, 0x81, 0xbd, 0x0b, 0xa0, 0x7f, 0x52, 0xf1, 0x10, + 0xab, 0x8f, 0xf6, 0xac, 0x83, 0x52, 0xbb, 0x94, 0x66, 0x3e, 0xc7, 0x43, 0x74, 0x23, 0x78, 0x9e, + 0x3b, 0x2e, 0xf9, 0x90, 0x30, 0x5c, 0x7e, 0xe0, 0x07, 0xd8, 0xea, 0x65, 0x5c, 0xa7, 0x1b, 0xeb, + 0x23, 0x9b, 0xd5, 0x3f, 0x3f, 0xeb, 0x3b, 0xe6, 0x43, 0x0f, 0x83, 0x40, 0xa0, 0x94, 0xe7, 0x4a, + 0x50, 0xd6, 0x6f, 0x97, 0x67, 0x74, 0x33, 0x76, 0xeb, 0xf0, 0x74, 0xae, 0x3b, 0xa1, 0x2b, 0x5c, + 0xee, 0x77, 0xa8, 0xcd, 0xf1, 0x16, 0x11, 0x8a, 0x92, 0x30, 0x8c, 0x57, 0xd6, 0xd9, 0xfb, 0xb0, + 0x4d, 0xa4, 0x44, 0x25, 0x3b, 0x97, 0x29, 0x6b, 0xfa, 0xde, 0xd2, 0x49, 0x53, 0xff, 0x1a, 0xca, + 0x97, 0x88, 0x33, 0xe4, 0x71, 0x8a, 0x40, 0x92, 0xd2, 0x80, 0xfb, 0xcb, 0x32, 0xc3, 0x39, 0x23, + 0xe2, 0x0a, 0xd5, 0x17, 0xaa, 0x06, 0x81, 0x20, 0x37, 0xf6, 0x4b, 0x28, 0x45, 0x69, 0x26, 0x33, + 0x6f, 0xb7, 0x37, 0x75, 0xe2, 0x34, 0xb0, 0x2b, 0xb0, 0x41, 0x22, 0x3e, 0x62, 0xca, 0x38, 0x4d, + 0x64, 0xbf, 0x87, 0x72, 0x80, 0x52, 0x51, 0x46, 0x14, 0xe5, 0x4c, 0xdb, 0x96, 0x4d, 0x2d, 0x07, + 0x27, 0x23, 0xbf, 0x31, 0x72, 0x96, 0x8c, 0x7c, 0x7d, 0x55, 0xf1, 0x8c, 0x6e, 0xc6, 0xee, 0xb5, + 0x99, 0xa1, 0x6e, 0xe2, 0x08, 0x15, 0xa1, 0xa1, 0xbc, 0x18, 0x06, 0xe9, 0xe2, 0x2c, 0x6d, 0xe5, + 0x1d, 0xc0, 0x48, 0x73, 0x0f, 0xb9, 0xe7, 0x92, 0x61, 0x9b, 0xb1, 0xfb, 0x0d, 0xec, 0x9c, 0xf2, + 0x98, 0x91, 0x6e, 0x58, 0x98, 0xeb, 0x6a, 0xe1, 0x8e, 0x8e, 0xa8, 0x2c, 0x52, 0xa6, 0xe0, 0x55, + 0x4e, 0x76, 0x21, 0x51, 0x9c, 0xa3, 0x52, 0x21, 0x16, 0xdb, 0xe2, 0x08, 0x76, 0xef, 0xb5, 0x16, + 0xdc, 0xec, 0xa2, 0xb6, 0x85, 0x22, 0xa2, 0x52, 0x52, 0xce, 0x0a, 0x5e, 0x9e, 0xc5, 0x7d, 0x6d, + 0xe3, 0xf5, 0xa1, 0x52, 0xa2, 0x58, 0x65, 0x63, 0x61, 0x5f, 0xb3, 0x3f, 0xd5, 0x65, 0x2e, 0xf7, + 0x2d, 0x54, 0x72, 0x25, 0x27, 0x88, 0x0f, 0x9a, 0x8a, 0xbb, 0x63, 0x4c, 0x2d, 0x22, 0x48, 0x94, + 0x95, 0x34, 0xf1, 0xf7, 0xc4, 0xb1, 0xee, 0x26, 0x8e, 0xf5, 0x6f, 0xe2, 0x58, 0x3f, 0xa6, 0xce, + 0xda, 0xdd, 0xd4, 0x59, 0xfb, 0x3b, 0x75, 0xd6, 0xa0, 0x46, 0xb9, 0x77, 0xff, 0x1b, 0xd1, 0xb2, + 0xbe, 0x7a, 0x7d, 0xaa, 0x06, 0xa3, 0xae, 0xd7, 0xe3, 0x91, 0x3f, 0x87, 0xea, 0x94, 0xe7, 0x22, + 0xff, 0x76, 0xf6, 0xfa, 0x74, 0x37, 0xd2, 0x17, 0xe4, 0xcd, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x02, 0x32, 0xd1, 0x78, 0x9b, 0x06, 0x00, 0x00, } func (m *EventOrderCreated) Marshal() (dAtA []byte, err error) { @@ -1065,10 +1065,10 @@ func (m *EventMarketWithdraw) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x1a } - if len(m.AmountWithdrawn) > 0 { - i -= len(m.AmountWithdrawn) - copy(dAtA[i:], m.AmountWithdrawn) - i = encodeVarintEvents(dAtA, i, uint64(len(m.AmountWithdrawn))) + if len(m.Amount) > 0 { + i -= len(m.Amount) + copy(dAtA[i:], m.Amount) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Amount))) i-- dAtA[i] = 0x12 } @@ -1488,7 +1488,7 @@ func (m *EventMarketWithdraw) Size() (n int) { if m.MarketId != 0 { n += 1 + sovEvents(uint64(m.MarketId)) } - l = len(m.AmountWithdrawn) + l = len(m.Amount) if l > 0 { n += 1 + l + sovEvents(uint64(l)) } @@ -2108,7 +2108,7 @@ func (m *EventMarketWithdraw) Unmarshal(dAtA []byte) error { } case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AmountWithdrawn", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -2136,7 +2136,7 @@ func (m *EventMarketWithdraw) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.AmountWithdrawn = string(dAtA[iNdEx:postIndex]) + m.Amount = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go index 3d76678c03..2b1fa4ed0d 100644 --- a/x/exchange/events_test.go +++ b/x/exchange/events_test.go @@ -121,7 +121,7 @@ func TestNewEventMarketWithdraw(t *testing.T) { event := NewEventMarketWithdraw(marketID, amountWithdrawn, destination, withdrawnBy) assert.Equal(t, marketID, event.MarketId, "MarketId") - assert.Equal(t, amountWithdrawn.String(), event.AmountWithdrawn, "AmountWithdrawn") + assert.Equal(t, amountWithdrawn.String(), event.Amount, "Amount") assert.Equal(t, destination.String(), event.Destination, "Destination") assert.Equal(t, withdrawnBy.String(), event.WithdrawnBy, "WithdrawnBy") assertEverythingSet(t, event, "EventMarketWithdraw") @@ -372,7 +372,7 @@ func TestTypedEventToEvent(t *testing.T) { expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventMarketWithdraw", Attributes: []abci.EventAttribute{ - {Key: []byte("amount_withdrawn"), Value: coins1Q}, + {Key: []byte("amount"), Value: coins1Q}, {Key: []byte("destination"), Value: destinationQ}, {Key: []byte("market_id"), Value: []byte("6")}, {Key: []byte("withdrawn_by"), Value: withdrawnByQ}, From 05a2ffa6343d2710defab24a64da1c537eb48ee8 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 14:22:28 -0600 Subject: [PATCH 146/309] [1658]: Correct a test that wasn't testing the right thing. --- x/exchange/events_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go index 2b1fa4ed0d..bbae876b02 100644 --- a/x/exchange/events_test.go +++ b/x/exchange/events_test.go @@ -231,11 +231,10 @@ func TestNewEventMarketUserSettleEnabled(t *testing.T) { marketID := uint32(123) updatedBy := sdk.AccAddress("updatedBy___________") - event := NewEventMarketEnabled(marketID, updatedBy) + event := NewEventMarketUserSettleEnabled(marketID, updatedBy) assert.Equal(t, marketID, event.MarketId, "MarketId") assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") - assertEverythingSet(t, event, "EventMarketEnabled") - + assertEverythingSet(t, event, "EventMarketUserSettleEnabled") } func TestNewEventMarketUserSettleDisabled(t *testing.T) { From a841230bb902cd642c8144142d0ad60d029b0654 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 14:25:34 -0600 Subject: [PATCH 147/309] [1658]: Wrap errors returned by the msg service endpoints. Emit events. --- x/exchange/keeper/market.go | 32 ++++++++++------ x/exchange/keeper/msg_server.go | 65 ++++++++++++++++++++------------- x/exchange/keeper/orders.go | 21 +++++++---- 3 files changed, 74 insertions(+), 44 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 0da88c0821..acfb31716f 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -570,7 +570,7 @@ func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Co // UpdateMarketDetails updates a market's details. It returns an error if the market account // isn't found or if there aren't any changes provided. -func (k Keeper) UpdateMarketDetails(ctx sdk.Context, marketID uint32, marketDetails *exchange.MarketDetails) error { +func (k Keeper) UpdateMarketDetails(ctx sdk.Context, marketID uint32, marketDetails *exchange.MarketDetails, updatedBy sdk.AccAddress) error { if err := marketDetails.Validate(); err != nil { return err } @@ -586,7 +586,8 @@ func (k Keeper) UpdateMarketDetails(ctx sdk.Context, marketID uint32, marketDeta marketAcc.MarketDetails = *marketDetails k.accountKeeper.SetAccount(ctx, marketAcc) - return nil + + return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketDetailsUpdated(marketID, updatedBy)) } // isMarketActive returns true if the provided market is accepting orders. @@ -617,14 +618,14 @@ func (k Keeper) SetMarketActive(ctx sdk.Context, marketID uint32, active bool) { // UpdateMarketActive updates the active flag for a market. // An error is returned if the setting is already what is provided. -func (k Keeper) UpdateMarketActive(ctx sdk.Context, marketID uint32, active bool) error { +func (k Keeper) UpdateMarketActive(ctx sdk.Context, marketID uint32, active bool, updatedBy sdk.AccAddress) error { store := k.getStore(ctx) current := isMarketActive(store, marketID) if current == active { return fmt.Errorf("market %d already has accepting-orders %t", marketID, active) } setMarketActive(store, marketID, active) - return nil + return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketActiveUpdated(marketID, updatedBy, active)) } // isUserSettlementAllowed gets whether user-settlement is allowed for a market. @@ -655,14 +656,14 @@ func (k Keeper) SetUserSettlementAllowed(ctx sdk.Context, marketID uint32, allow // UpdateUserSettlementAllowed updates the allow-user-settlement flag for a market. // An error is returned if the setting is already what is provided. -func (k Keeper) UpdateUserSettlementAllowed(ctx sdk.Context, marketID uint32, allow bool) error { +func (k Keeper) UpdateUserSettlementAllowed(ctx sdk.Context, marketID uint32, allow bool, updatedBy sdk.AccAddress) error { store := k.getStore(ctx) current := isUserSettlementAllowed(store, marketID) if current == allow { return fmt.Errorf("market %d already has allow-user-settlement %t", marketID, allow) } setUserSettlementAllowed(store, marketID, allow) - return nil + return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketUserSettleUpdated(marketID, updatedBy, allow)) } // storeHasPermission returns true if there is an entry in the store for the given market, address, and permissions. @@ -952,7 +953,7 @@ func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (marketID setReqAttrAsk(store, marketID, reqAttrCreateAsk) setReqAttrBid(store, marketID, reqAttrCreateBid) - return marketID, err + return marketID, nil } // GetMarketAccount gets a market's account from the account module. @@ -996,7 +997,7 @@ func (k Keeper) GetMarket(ctx sdk.Context, marketID uint32) *exchange.Market { } // UpdateFees updates all the fees as provided in the MsgGovManageFeesRequest. -func (k Keeper) UpdateFees(ctx sdk.Context, msg *exchange.MsgGovManageFeesRequest) { +func (k Keeper) UpdateFees(ctx sdk.Context, msg *exchange.MsgGovManageFeesRequest) error { store := k.getStore(ctx) updateCreateAskFlatFees(store, msg.MarketId, msg.RemoveFeeCreateAskFlat, msg.AddFeeCreateAskFlat) updateCreateBidFlatFees(store, msg.MarketId, msg.RemoveFeeCreateBidFlat, msg.AddFeeCreateBidFlat) @@ -1004,6 +1005,7 @@ func (k Keeper) UpdateFees(ctx sdk.Context, msg *exchange.MsgGovManageFeesReques updateSellerSettlementRatios(store, msg.MarketId, msg.RemoveFeeSellerSettlementRatios, msg.AddFeeSellerSettlementRatios) updateBuyerSettlementFlatFees(store, msg.MarketId, msg.RemoveFeeBuyerSettlementFlat, msg.AddFeeBuyerSettlementFlat) updateBuyerSettlementRatios(store, msg.MarketId, msg.RemoveFeeBuyerSettlementRatios, msg.AddFeeBuyerSettlementRatios) + return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketFeesUpdated(msg.MarketId)) } // hasReqAttrs returns true if either reqAttrs is empty or the provide address has all of them on their account. @@ -1037,18 +1039,19 @@ func (k Keeper) CanCreateBid(ctx sdk.Context, marketID uint32, addr sdk.AccAddre // WithdrawMarketFunds transfers funds from a market account to another account. // The caller is responsible for making sure this withdrawal should be allowed (e.g. by calling CanWithdrawMarketFunds first). -func (k Keeper) WithdrawMarketFunds(ctx sdk.Context, marketID uint32, toAddr sdk.AccAddress, amount sdk.Coins) error { +func (k Keeper) WithdrawMarketFunds(ctx sdk.Context, marketID uint32, toAddr sdk.AccAddress, amount sdk.Coins, withdrawnBy sdk.AccAddress) error { marketAddr := exchange.GetMarketAddress(marketID) err := k.bankKeeper.SendCoins(ctx, marketAddr, toAddr, amount) if err != nil { return fmt.Errorf("failed to withdraw %s from market %d: %w", amount, marketID, err) } - return nil + return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketWithdraw(marketID, amount, toAddr, withdrawnBy)) } // UpdatePermissions updates users permissions in the store using the provided changes. // The caller is responsible for making sure this update should be allowed (e.g. by calling CanManagePermissions first). func (k Keeper) UpdatePermissions(ctx sdk.Context, msg *exchange.MsgMarketManagePermissionsRequest) error { + admin := sdk.MustAccAddressFromBech32(msg.Admin) marketID := msg.MarketId store := k.getStore(ctx) var errs []error @@ -1093,7 +1096,7 @@ func (k Keeper) UpdatePermissions(ctx sdk.Context, msg *exchange.MsgMarketManage return errors.Join(errs...) } - return nil + return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketPermissionsUpdated(marketID, admin)) } // updateReqAttrs updates the required attributes in the store that use the provided key maker by removing then adding @@ -1146,11 +1149,16 @@ func updateReqAttrsBid(store sdk.KVStore, marketID uint32, toRemove, toAdd []str // UpdateReqAttrs updates the required attributes in the store using the provided changes. // The caller is responsible for making sure this update should be allowed (e.g. by calling CanManageReqAttrs first). func (k Keeper) UpdateReqAttrs(ctx sdk.Context, msg *exchange.MsgMarketManageReqAttrsRequest) error { + admin := sdk.MustAccAddressFromBech32(msg.Admin) marketID := msg.MarketId store := k.getStore(ctx) errs := []error{ updateReqAttrsAsk(store, marketID, msg.CreateAskToRemove, msg.CreateAskToAdd), updateReqAttrsBid(store, marketID, msg.CreateBidToRemove, msg.CreateBidToAdd), } - return errors.Join(errs...) + err := errors.Join(errs...) + if err != nil { + return err + } + return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketReqAttrUpdated(marketID, admin)) } diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index c1af8d1b50..bde1ac276d 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -2,7 +2,6 @@ package keeper import ( "context" - "fmt" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -29,7 +28,7 @@ func (k MsgServer) CreateAsk(goCtx context.Context, msg *exchange.MsgCreateAskRe ctx := sdk.UnwrapSDKContext(goCtx) orderID, err := k.CreateAskOrder(ctx, msg.AskOrder, msg.OrderCreationFee) if err != nil { - return nil, err + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } return &exchange.MsgCreateAskResponse{OrderId: orderID}, nil } @@ -39,7 +38,7 @@ func (k MsgServer) CreateBid(goCtx context.Context, msg *exchange.MsgCreateBidRe ctx := sdk.UnwrapSDKContext(goCtx) orderID, err := k.CreateBidOrder(ctx, msg.BidOrder, msg.OrderCreationFee) if err != nil { - return nil, err + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } return &exchange.MsgCreateBidResponse{OrderId: orderID}, nil } @@ -49,7 +48,7 @@ func (k MsgServer) CancelOrder(goCtx context.Context, msg *exchange.MsgCancelOrd ctx := sdk.UnwrapSDKContext(goCtx) err := k.Keeper.CancelOrder(ctx, msg.OrderId, msg.Signer) if err != nil { - return nil, err + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } return &exchange.MsgCancelOrderResponse{}, nil } @@ -59,7 +58,7 @@ func (k MsgServer) FillBids(goCtx context.Context, msg *exchange.MsgFillBidsRequ ctx := sdk.UnwrapSDKContext(goCtx) err := k.Keeper.FillBids(ctx, msg) if err != nil { - return nil, err + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } return &exchange.MsgFillBidsResponse{}, nil } @@ -69,7 +68,7 @@ func (k MsgServer) FillAsks(goCtx context.Context, msg *exchange.MsgFillAsksRequ ctx := sdk.UnwrapSDKContext(goCtx) err := k.Keeper.FillAsks(ctx, msg) if err != nil { - return nil, err + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } return &exchange.MsgFillAsksResponse{}, nil } @@ -80,16 +79,22 @@ func (k MsgServer) MarketSettle(goCtx context.Context, msg *exchange.MsgMarketSe panic("not implemented") } +// permError creates and returns an error indicating that an account does not have a needed permission. +func permError(desc string, account string, marketID uint32) error { + return sdkerrors.ErrInvalidRequest.Wrapf("account %s does not have permission to %s market %d", account, desc, marketID) +} + // MarketWithdraw is a market endpoint to withdraw fees that have been collected. func (k MsgServer) MarketWithdraw(goCtx context.Context, msg *exchange.MsgMarketWithdrawRequest) (*exchange.MsgMarketWithdrawResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) if !k.CanWithdrawMarketFunds(ctx, msg.MarketId, msg.Admin) { - return nil, fmt.Errorf("account %s does not have withdraw permission for market %d", msg.Admin, msg.MarketId) + return nil, permError("withdraw from", msg.Admin, msg.MarketId) } + admin := sdk.MustAccAddressFromBech32(msg.Admin) toAddr := sdk.MustAccAddressFromBech32(msg.ToAddress) - err := k.WithdrawMarketFunds(ctx, msg.MarketId, toAddr, msg.Amount) + err := k.WithdrawMarketFunds(ctx, msg.MarketId, toAddr, msg.Amount, admin) if err != nil { - return nil, err + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } return &exchange.MsgMarketWithdrawResponse{}, nil } @@ -98,11 +103,12 @@ func (k MsgServer) MarketWithdraw(goCtx context.Context, msg *exchange.MsgMarket func (k MsgServer) MarketUpdateDetails(goCtx context.Context, msg *exchange.MsgMarketUpdateDetailsRequest) (*exchange.MsgMarketUpdateDetailsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) if !k.CanUpdateMarket(ctx, msg.MarketId, msg.Admin) { - return nil, fmt.Errorf("account %s does not have permission to update market %d", msg.Admin, msg.MarketId) + return nil, permError("update", msg.Admin, msg.MarketId) } - err := k.UpdateMarketDetails(ctx, msg.MarketId, &msg.MarketDetails) + admin := sdk.MustAccAddressFromBech32(msg.Admin) + err := k.UpdateMarketDetails(ctx, msg.MarketId, &msg.MarketDetails, admin) if err != nil { - return nil, err + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } return &exchange.MsgMarketUpdateDetailsResponse{}, nil } @@ -111,11 +117,12 @@ func (k MsgServer) MarketUpdateDetails(goCtx context.Context, msg *exchange.MsgM func (k MsgServer) MarketUpdateEnabled(goCtx context.Context, msg *exchange.MsgMarketUpdateEnabledRequest) (*exchange.MsgMarketUpdateEnabledResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) if !k.CanUpdateMarket(ctx, msg.MarketId, msg.Admin) { - return nil, fmt.Errorf("account %s does not have permission to update market %d", msg.Admin, msg.MarketId) + return nil, permError("update", msg.Admin, msg.MarketId) } - err := k.UpdateMarketActive(ctx, msg.MarketId, msg.AcceptingOrders) + admin := sdk.MustAccAddressFromBech32(msg.Admin) + err := k.UpdateMarketActive(ctx, msg.MarketId, msg.AcceptingOrders, admin) if err != nil { - return nil, err + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } return &exchange.MsgMarketUpdateEnabledResponse{}, nil } @@ -124,11 +131,12 @@ func (k MsgServer) MarketUpdateEnabled(goCtx context.Context, msg *exchange.MsgM func (k MsgServer) MarketUpdateUserSettle(goCtx context.Context, msg *exchange.MsgMarketUpdateUserSettleRequest) (*exchange.MsgMarketUpdateUserSettleResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) if !k.CanUpdateMarket(ctx, msg.MarketId, msg.Admin) { - return nil, fmt.Errorf("account %s does not have permission to update market %d", msg.Admin, msg.MarketId) + return nil, permError("update", msg.Admin, msg.MarketId) } - err := k.UpdateUserSettlementAllowed(ctx, msg.MarketId, msg.AllowUserSettlement) + admin := sdk.MustAccAddressFromBech32(msg.Admin) + err := k.UpdateUserSettlementAllowed(ctx, msg.MarketId, msg.AllowUserSettlement, admin) if err != nil { - return nil, err + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } return &exchange.MsgMarketUpdateUserSettleResponse{}, nil } @@ -137,11 +145,11 @@ func (k MsgServer) MarketUpdateUserSettle(goCtx context.Context, msg *exchange.M func (k MsgServer) MarketManagePermissions(goCtx context.Context, msg *exchange.MsgMarketManagePermissionsRequest) (*exchange.MsgMarketManagePermissionsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) if !k.CanManagePermissions(ctx, msg.MarketId, msg.Admin) { - return nil, fmt.Errorf("account %s does not have permission to manage permissions for market %d", msg.Admin, msg.MarketId) + return nil, permError("manage permissions for", msg.Admin, msg.MarketId) } err := k.UpdatePermissions(ctx, msg) if err != nil { - return nil, err + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } return &exchange.MsgMarketManagePermissionsResponse{}, nil } @@ -150,11 +158,11 @@ func (k MsgServer) MarketManagePermissions(goCtx context.Context, msg *exchange. func (k MsgServer) MarketManageReqAttrs(goCtx context.Context, msg *exchange.MsgMarketManageReqAttrsRequest) (*exchange.MsgMarketManageReqAttrsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) if !k.CanWithdrawMarketFunds(ctx, msg.MarketId, msg.Admin) { - return nil, fmt.Errorf("account %s does not have permission to manage required attributes for market %d", msg.Admin, msg.MarketId) + return nil, permError("manage required attributes for", msg.Admin, msg.MarketId) } err := k.UpdateReqAttrs(ctx, msg) if err != nil { - return nil, err + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } return &exchange.MsgMarketManageReqAttrsResponse{}, nil } @@ -171,7 +179,11 @@ func (k MsgServer) GovCreateMarket(goCtx context.Context, msg *exchange.MsgGovCr } ctx := sdk.UnwrapSDKContext(goCtx) - _, err := k.CreateMarket(ctx, msg.Market) + marketID, err := k.CreateMarket(ctx, msg.Market) + if err != nil { + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) + } + err = ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketCreated(marketID)) if err != nil { return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } @@ -185,7 +197,10 @@ func (k MsgServer) GovManageFees(goCtx context.Context, msg *exchange.MsgGovMana } ctx := sdk.UnwrapSDKContext(goCtx) - k.UpdateFees(ctx, msg) + err := k.UpdateFees(ctx, msg) + if err != nil { + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) + } return &exchange.MsgGovManageFeesResponse{}, nil } @@ -200,7 +215,7 @@ func (k MsgServer) GovUpdateParams(goCtx context.Context, msg *exchange.MsgGovUp k.SetParams(ctx, &msg.Params) if err := ctx.EventManager().EmitTypedEvent(exchange.NewEventParamsUpdated()); err != nil { - return nil, err + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } return &exchange.MsgGovUpdateParamsResponse{}, nil } diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 242be3dc1f..a4d9405aa6 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" + "github.com/gogo/protobuf/proto" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -280,7 +282,7 @@ func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, crea return 0, fmt.Errorf("error placing hold for ask order: %w", err) } - return orderID, nil + return orderID, ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCreated(order)) } // CreateBidOrder creates a bid order, collects the creation fee, and places all needed holds. @@ -327,7 +329,7 @@ func (k Keeper) CreateBidOrder(ctx sdk.Context, bidOrder exchange.BidOrder, crea return 0, fmt.Errorf("error placing hold for bid order: %w", err) } - return orderID, nil + return orderID, ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCreated(order)) } // CancelOrder releases an order's held funds and deletes it. @@ -345,6 +347,7 @@ func (k Keeper) CancelOrder(ctx sdk.Context, orderID uint64, signer string) erro return fmt.Errorf("account %s does not have permission to cancel order %d", signer, orderID) } + signerAddr := sdk.MustAccAddressFromBech32(signer) orderOwnerAddr := sdk.MustAccAddressFromBech32(orderOwner) heldAmount := order.GetHoldAmount() err = k.holdKeeper.ReleaseHold(ctx, orderOwnerAddr, heldAmount) @@ -354,7 +357,7 @@ func (k Keeper) CancelOrder(ctx sdk.Context, orderID uint64, signer string) erro k.deleteOrder(ctx, *order) - return nil + return ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCancelled(orderID, signerAddr)) } // FillBids settles one or more bid orders for a seller. @@ -506,11 +509,13 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro } } - for _, order := range orders { + events := make([]proto.Message, len(orders)) + for i, order := range orders { deleteAndDeIndexOrder(store, *order) + events[i] = exchange.NewEventOrderFilled(order.OrderId) } - return nil + return ctx.EventManager().EmitTypedEvents(events...) } // FillAsks settles one or more ask orders for a buyer. @@ -663,11 +668,13 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro } } - for _, order := range orders { + events := make([]proto.Message, len(orders)) + for i, order := range orders { deleteAndDeIndexOrder(store, *order) + events[i] = exchange.NewEventOrderFilled(order.OrderId) } - return nil + return ctx.EventManager().EmitTypedEvents(events...) } // safeCoinsEquals returns true if the two provided coins are equal. From e9c48279f6f270361780d8690f86d8e9cf6e7ffc Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 14:30:19 -0600 Subject: [PATCH 148/309] [1658]: Fix some event field comments. --- docs/proto-docs.md | 10 +++++----- proto/provenance/exchange/v1/events.proto | 10 +++++----- x/exchange/events.pb.go | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 6342cf935d..10265b6319 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1485,7 +1485,7 @@ EventOrderCancelled is an event emitted when an order is cancelled. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order created. | +| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order cancelled. | | `cancelled_by` | [string](#string) | | cancelled_by is the account that triggered the cancellation of the order. | @@ -1518,7 +1518,7 @@ This event is also used for orders that were previously partially filled, but ha | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order created. | +| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order filled. | @@ -1533,9 +1533,9 @@ EventOrderPartiallyFilled is an event emitted when an order filled in part and s | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order created. | -| `assets_filled` | [string](#string) | | amount_filled is the coins amount string of assets that were filled (and removed from the order). | -| `fees_filled` | [string](#string) | | fees_filled is the coins amount string of fees removed from the order. | +| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order partially filled. | +| `assets_filled` | [string](#string) | | assets_filled is the coins amount string of assets that were filled and removed from the order. | +| `fees_filled` | [string](#string) | | fees_filled is the coins amount string of fees paid and removed from the order. | diff --git a/proto/provenance/exchange/v1/events.proto b/proto/provenance/exchange/v1/events.proto index 20292363fa..93c44d9eae 100644 --- a/proto/provenance/exchange/v1/events.proto +++ b/proto/provenance/exchange/v1/events.proto @@ -18,7 +18,7 @@ message EventOrderCreated { // EventOrderCancelled is an event emitted when an order is cancelled. message EventOrderCancelled { - // order_id is the numerical identifier of the order created. + // order_id is the numerical identifier of the order cancelled. uint64 order_id = 1; // cancelled_by is the account that triggered the cancellation of the order. string cancelled_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; @@ -27,17 +27,17 @@ message EventOrderCancelled { // EventOrderFilled is an event emitted when an order has been filled in full. // This event is also used for orders that were previously partially filled, but have now been filled in full. message EventOrderFilled { - // order_id is the numerical identifier of the order created. + // order_id is the numerical identifier of the order filled. uint64 order_id = 1; } // EventOrderPartiallyFilled is an event emitted when an order filled in part and still has more left to fill. message EventOrderPartiallyFilled { - // order_id is the numerical identifier of the order created. + // order_id is the numerical identifier of the order partially filled. uint64 order_id = 1; - // amount_filled is the coins amount string of assets that were filled (and removed from the order). + // assets_filled is the coins amount string of assets that were filled and removed from the order. string assets_filled = 2; - // fees_filled is the coins amount string of fees removed from the order. + // fees_filled is the coins amount string of fees paid and removed from the order. string fees_filled = 3; } diff --git a/x/exchange/events.pb.go b/x/exchange/events.pb.go index 64f8c4b3d4..6e627c95e8 100644 --- a/x/exchange/events.pb.go +++ b/x/exchange/events.pb.go @@ -80,7 +80,7 @@ func (m *EventOrderCreated) GetOrderType() string { // EventOrderCancelled is an event emitted when an order is cancelled. type EventOrderCancelled struct { - // order_id is the numerical identifier of the order created. + // order_id is the numerical identifier of the order cancelled. OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` // cancelled_by is the account that triggered the cancellation of the order. CancelledBy string `protobuf:"bytes,2,opt,name=cancelled_by,json=cancelledBy,proto3" json:"cancelled_by,omitempty"` @@ -136,7 +136,7 @@ func (m *EventOrderCancelled) GetCancelledBy() string { // EventOrderFilled is an event emitted when an order has been filled in full. // This event is also used for orders that were previously partially filled, but have now been filled in full. type EventOrderFilled struct { - // order_id is the numerical identifier of the order created. + // order_id is the numerical identifier of the order filled. OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` } @@ -182,11 +182,11 @@ func (m *EventOrderFilled) GetOrderId() uint64 { // EventOrderPartiallyFilled is an event emitted when an order filled in part and still has more left to fill. type EventOrderPartiallyFilled struct { - // order_id is the numerical identifier of the order created. + // order_id is the numerical identifier of the order partially filled. OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` - // amount_filled is the coins amount string of assets that were filled (and removed from the order). + // assets_filled is the coins amount string of assets that were filled and removed from the order. AssetsFilled string `protobuf:"bytes,2,opt,name=assets_filled,json=assetsFilled,proto3" json:"assets_filled,omitempty"` - // fees_filled is the coins amount string of fees removed from the order. + // fees_filled is the coins amount string of fees paid and removed from the order. FeesFilled string `protobuf:"bytes,3,opt,name=fees_filled,json=feesFilled,proto3" json:"fees_filled,omitempty"` } From 1a879b55ef4cb71c6c06da511c734ccc1c7f128d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 15:32:40 -0600 Subject: [PATCH 149/309] [1658]: extract getAskOrders and getBidOrders out of FillAsks and FillBids. --- x/exchange/keeper/orders.go | 227 +++++++++++++++++++++++------------- 1 file changed, 149 insertions(+), 78 deletions(-) diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index a4d9405aa6..da81e4fa00 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -235,6 +235,40 @@ func (k Keeper) IterateAssetBidOrders(ctx sdk.Context, assetDenom string, cb fun }) } +// placeHoldOnOrder places a hold on an order's funds in the owner's account. +func (k Keeper) placeHoldOnOrder(ctx sdk.Context, order *exchange.Order) error { + orderID := order.OrderId + orderType := order.GetOrderType() + owner := order.GetOwner() + ownerAddr, err := sdk.AccAddressFromBech32(owner) + if err != nil { + return fmt.Errorf("invalid %s order %d owner %q: %w", orderType, orderID, owner, err) + } + toHold := order.GetHoldAmount() + err = k.holdKeeper.AddHold(ctx, ownerAddr, toHold, fmt.Sprintf("x/exchange: order %d", orderID)) + if err != nil { + return fmt.Errorf("error placing hold for %s order %d: %w", orderType, orderID, err) + } + return nil +} + +// releaseHoldOnOrder releases a hold that was placed on an order's funds in the owner's account. +func (k Keeper) releaseHoldOnOrder(ctx sdk.Context, order *exchange.Order) error { + orderID := order.OrderId + orderType := order.GetOrderType() + owner := order.GetOwner() + ownerAddr, err := sdk.AccAddressFromBech32(owner) + if err != nil { + return fmt.Errorf("invalid %s order %d owner %q: %w", orderType, orderID, owner, err) + } + held := order.GetHoldAmount() + err = k.holdKeeper.ReleaseHold(ctx, ownerAddr, held) + if err != nil { + return fmt.Errorf("error releasing hold for %s order %d: %w", orderType, orderID, err) + } + return nil +} + // CreateAskOrder creates an ask order, collects the creation fee, and places all needed holds. func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, creationFee *sdk.Coin) (uint64, error) { if err := askOrder.Validate(); err != nil { @@ -277,9 +311,8 @@ func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, crea return 0, fmt.Errorf("error storing ask order: %w", err) } - toHold := order.GetHoldAmount() - if err := k.holdKeeper.AddHold(ctx, seller, toHold, fmt.Sprintf("x/exchange: order %d", order.OrderId)); err != nil { - return 0, fmt.Errorf("error placing hold for ask order: %w", err) + if err := k.placeHoldOnOrder(ctx, order); err != nil { + return 0, err } return orderID, ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCreated(order)) @@ -324,9 +357,8 @@ func (k Keeper) CreateBidOrder(ctx sdk.Context, bidOrder exchange.BidOrder, crea return 0, fmt.Errorf("error storing bid order: %w", err) } - toHold := order.GetHoldAmount() - if err := k.holdKeeper.AddHold(ctx, buyer, toHold, fmt.Sprintf("x/exchange: order %d", order.OrderId)); err != nil { - return 0, fmt.Errorf("error placing hold for bid order: %w", err) + if err := k.placeHoldOnOrder(ctx, order); err != nil { + return 0, err } return orderID, ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCreated(order)) @@ -360,6 +392,86 @@ func (k Keeper) CancelOrder(ctx sdk.Context, orderID uint64, signer string) erro return ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCancelled(orderID, signerAddr)) } +// getBidOrders gets orders from the store, making sure they're bid orders in the given market +// and do not have the same buyer as the provided seller. If the seller isn't yet known, just provide "" for it. +func (k Keeper) getBidOrders(store sdk.KVStore, marketID uint32, orderIDs []uint64, seller string) ([]*exchange.Order, error) { + var errs []error + orders := make([]*exchange.Order, 0, len(orderIDs)) + + for _, orderID := range orderIDs { + order, oerr := k.getOrderFromStore(store, orderID) + if oerr != nil { + errs = append(errs, oerr) + continue + } + if order == nil { + errs = append(errs, fmt.Errorf("order %d not found", orderID)) + continue + } + if !order.IsBidOrder() { + errs = append(errs, fmt.Errorf("order %d is type %s: expected bid", orderID, order.GetOrderType())) + continue + } + + bidOrder := order.GetBidOrder() + orderMarketID := bidOrder.MarketId + buyer := bidOrder.Buyer + + if orderMarketID != marketID { + errs = append(errs, fmt.Errorf("order %d market id %d does not equal requested market id %d", orderID, orderMarketID, marketID)) + continue + } + if buyer == seller { + errs = append(errs, fmt.Errorf("order %d has the same buyer %s as the requested seller", orderID, buyer)) + continue + } + + orders = append(orders, order) + } + + return orders, errors.Join(errs...) +} + +// getAskOrders gets orders from the store, making sure they're ask orders in the given market +// and do not have the same sller as the provided buyer. If the buyer isn't yet known, just provide "" for it. +func (k Keeper) getAskOrders(store sdk.KVStore, marketID uint32, orderIDs []uint64, buyer string) ([]*exchange.Order, error) { + var errs []error + orders := make([]*exchange.Order, 0, len(orderIDs)) + + for _, orderID := range orderIDs { + order, oerr := k.getOrderFromStore(store, orderID) + if oerr != nil { + errs = append(errs, oerr) + continue + } + if order == nil { + errs = append(errs, fmt.Errorf("order %d not found", orderID)) + continue + } + if !order.IsAskOrder() { + errs = append(errs, fmt.Errorf("order %d is type %s: expected ask", orderID, order.GetOrderType())) + continue + } + + askOrder := order.GetAskOrder() + orderMarketID := askOrder.MarketId + seller := askOrder.Seller + + if orderMarketID != marketID { + errs = append(errs, fmt.Errorf("order %d market id %d does not equal requested market id %d", orderID, orderMarketID, marketID)) + continue + } + if seller == buyer { + errs = append(errs, fmt.Errorf("order %d has the same seller %s as the requested buyer", orderID, seller)) + continue + } + + orders = append(orders, order) + } + + return orders, errors.Join(errs...) +} + // FillBids settles one or more bid orders for a seller. func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) error { if err := msg.ValidateBasic(); err != nil { @@ -378,7 +490,10 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro if !isUserSettlementAllowed(store, marketID) { return fmt.Errorf("market %d does not allow user settlement", marketID) } - seller := sdk.MustAccAddressFromBech32(msg.Seller) + seller, serr := sdk.AccAddressFromBech32(msg.Seller) + if serr != nil { + return fmt.Errorf("invalid seller %q: %w", msg.Seller, serr) + } if !k.CanCreateAsk(ctx, marketID, seller) { return fmt.Errorf("account %s is not allowed to create ask orders in market %d", seller, marketID) } @@ -389,61 +504,35 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro return err } + orders, oerrs := k.getBidOrders(store, marketID, msg.BidOrderIds, msg.Seller) + if oerrs != nil { + return oerrs + } + var errs []error - orders := make([]*exchange.Order, 0, len(msg.BidOrderIds)) var totalAssets, totalPrice, totalSellerFee sdk.Coins assetOutputs := make([]banktypes.Output, 0, len(msg.BidOrderIds)) priceInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)) addrIndex := make(map[string]int) feeInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)+1) feeAddrIndex := make(map[string]int) - for _, orderID := range msg.BidOrderIds { - order, oerr := k.getOrderFromStore(store, orderID) - if oerr != nil { - errs = append(errs, oerr) - continue - } - if order == nil { - errs = append(errs, fmt.Errorf("order %d not found", orderID)) - continue - } - if !order.IsBidOrder() { - errs = append(errs, fmt.Errorf("order %d is type %s: expected bid", orderID, order.GetOrderType())) - continue - } - + for _, order := range orders { bidOrder := order.GetBidOrder() - orderMarketID := bidOrder.MarketId buyer := bidOrder.Buyer assets := bidOrder.Assets price := bidOrder.Price - heldAmount := bidOrder.GetHoldAmount() buyerSettlementFees := bidOrder.BuyerSettlementFees - if orderMarketID != marketID { - errs = append(errs, fmt.Errorf("order %d market id %d does not equal requested market id %d", orderID, orderMarketID, marketID)) - continue - } - if buyer == msg.Seller { - errs = append(errs, fmt.Errorf("order %d has the same buyer %s as the requested seller", orderID, buyer)) - continue - } sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, price) if rerr != nil { - errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", orderID, rerr)) - continue - } - buyerAddr, aerr := sdk.AccAddressFromBech32(buyer) - if aerr != nil { - errs = append(errs, fmt.Errorf("invalid buyer %q in order %d: %w", buyer, orderID, aerr)) + errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", order.OrderId, rerr)) continue } - if err := k.holdKeeper.ReleaseHold(ctx, buyerAddr, heldAmount); err != nil { - errs = append(errs, fmt.Errorf("error releasing hold for order %d: %w", orderID, err)) + if err := k.releaseHoldOnOrder(ctx, order); err != nil { + errs = append(errs, err) continue } - orders = append(orders, order) totalAssets = totalAssets.Add(assets...) totalPrice = totalPrice.Add(price) if sellerRatioFee != nil { @@ -536,7 +625,10 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro if !isUserSettlementAllowed(store, marketID) { return fmt.Errorf("market %d does not allow user settlement", marketID) } - buyer := sdk.MustAccAddressFromBech32(msg.Buyer) + buyer, serr := sdk.AccAddressFromBech32(msg.Buyer) + if serr != nil { + return fmt.Errorf("invalid buyer %q: %w", msg.Buyer, serr) + } if !k.CanCreateBid(ctx, marketID, buyer) { return fmt.Errorf("account %s is not allowed to create bid orders in market %d", buyer, marketID) } @@ -547,61 +639,35 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro return err } + orders, oerrs := k.getAskOrders(store, marketID, msg.AskOrderIds, msg.Buyer) + if oerrs != nil { + return oerrs + } + var errs []error - orders := make([]*exchange.Order, 0, len(msg.AskOrderIds)) var totalAssets, totalPrice sdk.Coins assetInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)) priceOutputs := make([]banktypes.Output, 0, len(msg.AskOrderIds)) addrIndex := make(map[string]int) feeInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)+1) feeAddrIndex := make(map[string]int) - for _, orderID := range msg.AskOrderIds { - order, oerr := k.getOrderFromStore(store, orderID) - if oerr != nil { - errs = append(errs, oerr) - continue - } - if order == nil { - errs = append(errs, fmt.Errorf("order %d not found", orderID)) - continue - } - if !order.IsAskOrder() { - errs = append(errs, fmt.Errorf("order %d is type %s: expected ask", orderID, order.GetOrderType())) - continue - } - + for _, order := range orders { askOrder := order.GetAskOrder() - orderMarketID := askOrder.MarketId seller := askOrder.Seller assets := askOrder.Assets price := askOrder.Price - heldAmount := askOrder.GetHoldAmount() sellerSettlementFlatFee := askOrder.SellerSettlementFlatFee - if orderMarketID != marketID { - errs = append(errs, fmt.Errorf("order %d market id %d does not equal requested market id %d", orderID, orderMarketID, marketID)) - continue - } - if seller == msg.Buyer { - errs = append(errs, fmt.Errorf("order %d has the same seller %s as the requested buyer", orderID, buyer)) - continue - } sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, price) if rerr != nil { - errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", orderID, rerr)) - continue - } - sellerAddr, aerr := sdk.AccAddressFromBech32(seller) - if aerr != nil { - errs = append(errs, fmt.Errorf("invalid seller %q in order %d: %w", buyer, orderID, aerr)) + errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", order.OrderId, rerr)) continue } - if err := k.holdKeeper.ReleaseHold(ctx, sellerAddr, heldAmount); err != nil { - errs = append(errs, fmt.Errorf("error releasing hold for order %d: %w", orderID, err)) + if err := k.releaseHoldOnOrder(ctx, order); err != nil { + errs = append(errs, err) continue } - orders = append(orders, order) totalAssets = totalAssets.Add(assets...) totalPrice = totalPrice.Add(price) var totalSellerFee sdk.Coins @@ -677,6 +743,11 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro return ctx.EventManager().EmitTypedEvents(events...) } +func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidOrderIds []uint64, expectPartial bool) error { + // TODO[1658]: Implement SettleOrders. + panic("Not implemented") +} + // safeCoinsEquals returns true if the two provided coins are equal. // Returns false instead of panicking like sdk.Coins.IsEqual. func safeCoinsEquals(a, b sdk.Coins) (isEqual bool) { From 86c2425eadf843b99939a5371256f2eef0d268e6 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 15:33:12 -0600 Subject: [PATCH 150/309] [1658]: Fill in the MarketSettle endpoint. Still need to do the actual settlement piece, though. --- x/exchange/keeper/msg_server.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index bde1ac276d..12117997ad 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -73,17 +73,24 @@ func (k MsgServer) FillAsks(goCtx context.Context, msg *exchange.MsgFillAsksRequ return &exchange.MsgFillAsksResponse{}, nil } -// MarketSettle is a market endpoint to trigger the settlement of orders. -func (k MsgServer) MarketSettle(goCtx context.Context, msg *exchange.MsgMarketSettleRequest) (*exchange.MsgMarketSettleResponse, error) { - // TODO[1658]: Implement MarketSettle - panic("not implemented") -} - // permError creates and returns an error indicating that an account does not have a needed permission. func permError(desc string, account string, marketID uint32) error { return sdkerrors.ErrInvalidRequest.Wrapf("account %s does not have permission to %s market %d", account, desc, marketID) } +// MarketSettle is a market endpoint to trigger the settlement of orders. +func (k MsgServer) MarketSettle(goCtx context.Context, msg *exchange.MsgMarketSettleRequest) (*exchange.MsgMarketSettleResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + if !k.CanSettleOrders(ctx, msg.MarketId, msg.Admin) { + return nil, permError("settle orders for", msg.Admin, msg.MarketId) + } + err := k.SettleOrders(ctx, msg.MarketId, msg.AskOrderIds, msg.BidOrderIds, msg.ExpectPartial) + if err != nil { + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) + } + return &exchange.MsgMarketSettleResponse{}, nil +} + // MarketWithdraw is a market endpoint to withdraw fees that have been collected. func (k MsgServer) MarketWithdraw(goCtx context.Context, msg *exchange.MsgMarketWithdrawRequest) (*exchange.MsgMarketWithdrawResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) From 47ad7923b2b5f102e5e99f744a9f23df904013f3 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 17:33:28 -0600 Subject: [PATCH 151/309] [1658]: Some pre-settlement validation. --- x/exchange/keeper/orders.go | 104 ++++++++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 22 deletions(-) diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index da81e4fa00..96ff8ed557 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -392,6 +392,20 @@ func (k Keeper) CancelOrder(ctx sdk.Context, orderID uint64, signer string) erro return ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCancelled(orderID, signerAddr)) } +// safeCoinsEquals returns true if the two provided coins are equal. +// Returns false instead of panicking like sdk.Coins.IsEqual. +func safeCoinsEquals(a, b sdk.Coins) (isEqual bool) { + // The sdk.Coins.IsEqual function will panic if a and b have the same number of entries, but different denoms. + // Really, that stuff is all pretty panic happy. + // In here, we don't really care why it panics, but if it does, they're not equal. + defer func() { + if r := recover(); r != nil { + isEqual = false + } + }() + return a.IsEqual(b) +} + // getBidOrders gets orders from the store, making sure they're bid orders in the given market // and do not have the same buyer as the provided seller. If the seller isn't yet known, just provide "" for it. func (k Keeper) getBidOrders(store sdk.KVStore, marketID uint32, orderIDs []uint64, seller string) ([]*exchange.Order, error) { @@ -472,6 +486,16 @@ func (k Keeper) getAskOrders(store sdk.KVStore, marketID uint32, orderIDs []uint return orders, errors.Join(errs...) } +// sumAssetsAndPrice gets the sum of assets, and the sum of prices of the provided orders. +func sumAssetsAndPrice(orders []*exchange.Order) (sdk.Coins, sdk.Coins) { + var totalAssets, totalPrice sdk.Coins + for _, order := range orders { + totalAssets = totalAssets.Add(order.GetAssets()...) + totalPrice = totalPrice.Add(order.GetPrice()) + } + return totalAssets, totalPrice +} + // FillBids settles one or more bid orders for a seller. func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) error { if err := msg.ValidateBasic(); err != nil { @@ -510,7 +534,7 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro } var errs []error - var totalAssets, totalPrice, totalSellerFee sdk.Coins + var totalSellerFee sdk.Coins assetOutputs := make([]banktypes.Output, 0, len(msg.BidOrderIds)) priceInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)) addrIndex := make(map[string]int) @@ -525,7 +549,8 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, price) if rerr != nil { - errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", order.OrderId, rerr)) + errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", + order.OrderId, rerr)) continue } if err := k.releaseHoldOnOrder(ctx, order); err != nil { @@ -533,8 +558,6 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro continue } - totalAssets = totalAssets.Add(assets...) - totalPrice = totalPrice.Add(price) if sellerRatioFee != nil { totalSellerFee = totalSellerFee.Add(*sellerRatioFee) } @@ -564,6 +587,8 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro return errors.Join(errs...) } + totalAssets, totalPrice := sumAssetsAndPrice(orders) + if !safeCoinsEquals(totalAssets, msg.TotalAssets) { return fmt.Errorf("total assets %q does not equal sum of bid order assets %q", msg.TotalAssets, totalAssets) } @@ -645,7 +670,6 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro } var errs []error - var totalAssets, totalPrice sdk.Coins assetInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)) priceOutputs := make([]banktypes.Output, 0, len(msg.AskOrderIds)) addrIndex := make(map[string]int) @@ -660,7 +684,8 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, price) if rerr != nil { - errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", order.OrderId, rerr)) + errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", + order.OrderId, rerr)) continue } if err := k.releaseHoldOnOrder(ctx, order); err != nil { @@ -668,8 +693,6 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro continue } - totalAssets = totalAssets.Add(assets...) - totalPrice = totalPrice.Add(price) var totalSellerFee sdk.Coins if sellerSettlementFlatFee != nil && !sellerSettlementFlatFee.IsZero() { totalSellerFee = totalSellerFee.Add(*sellerSettlementFlatFee) @@ -703,6 +726,8 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro return errors.Join(errs...) } + totalAssets, totalPrice := sumAssetsAndPrice(orders) + if !safeCoinsEquals(totalPrice, sdk.Coins{msg.TotalPrice}) { return fmt.Errorf("total price %q does not equal sum of ask order prices %q", msg.TotalPrice, totalPrice) } @@ -743,21 +768,56 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro return ctx.EventManager().EmitTypedEvents(events...) } +// SettleOrders attempts to settle all the provided orders. func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidOrderIds []uint64, expectPartial bool) error { + store := k.getStore(ctx) + if err := validateMarketExists(store, marketID); err != nil { + return err + } + + askOrders, aoerr := k.getAskOrders(store, marketID, askOrderIDs, "") + bidOrders, boerr := k.getBidOrders(store, marketID, bidOrderIds, "") + if aoerr != nil || boerr != nil { + return errors.Join(aoerr, boerr) + } + + totalAssetsForSale, totalAskPrice := sumAssetsAndPrice(askOrders) + totalAssetsToBuy, totalBidPrice := sumAssetsAndPrice(bidOrders) + + var errs []error + if len(totalAssetsForSale) != 1 { + errs = append(errs, fmt.Errorf("cannot settle with multiple ask order asset denoms %q", totalAssetsForSale)) + } + if len(totalAskPrice) != 1 { + errs = append(errs, fmt.Errorf("cannot settle with multiple ask order price denoms %q", totalAskPrice)) + } + if len(totalAssetsToBuy) != 1 { + errs = append(errs, fmt.Errorf("cannot settle with multiple bid order asset denoms %q", totalAssetsToBuy)) + } + if len(totalBidPrice) != 1 { + errs = append(errs, fmt.Errorf("cannot settle with multiple bid order price denoms %q", totalBidPrice)) + } + if len(errs) > 0 { + return errors.Join(errs...) + } + + if totalAssetsForSale[0].Denom != totalAssetsToBuy[0].Denom { + errs = append(errs, fmt.Errorf("cannot settle different ask %q and bid %q asset denoms", + totalAssetsForSale, totalAssetsToBuy)) + } + if totalAskPrice[0].Denom != totalBidPrice[0].Denom { + errs = append(errs, fmt.Errorf("cannot settle different ask %q and bid %q price denoms", + totalAskPrice, totalBidPrice)) + } + if len(errs) > 0 { + return errors.Join(errs...) + } + + if !expectPartial && !safeCoinsEquals(totalAssetsForSale, totalAssetsToBuy) { + return fmt.Errorf("total assets for sale %q does not equal total assets to buy %q", + totalAssetsForSale, totalAssetsToBuy) + } + // TODO[1658]: Implement SettleOrders. panic("Not implemented") } - -// safeCoinsEquals returns true if the two provided coins are equal. -// Returns false instead of panicking like sdk.Coins.IsEqual. -func safeCoinsEquals(a, b sdk.Coins) (isEqual bool) { - // The sdk.Coins.IsEqual function will panic if a and b have the same number of entries, but different denoms. - // Really, that stuff is all pretty panic happy. - // In here, we don't really care why it panics, but if it does, they're not equal. - defer func() { - if r := recover(); r != nil { - isEqual = false - } - }() - return a.IsEqual(b) -} From 9c823c190a6cc59a685b81ed4f75f05243db7580 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 19 Sep 2023 17:51:33 -0600 Subject: [PATCH 152/309] [1658]: Start work on a Fulfillment struct. --- x/exchange/orders.go | 83 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 2da9694d24..88f8a05bc1 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -5,6 +5,7 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) // Define the type strings and bytes to use for each order type. @@ -339,3 +340,85 @@ func (b BidOrder) Validate() error { return errors.Join(errs...) } + +// IndexedInputs is a slice of bank Inputs with an index by address +type IndexedInputs struct { + inputs []banktypes.Input + indexes map[string]int +} + +func NewIndexedInputs() *IndexedInputs { + return &IndexedInputs{ + indexes: make(map[string]int), + } +} + +// Add adds the coins to the input with the given address (creating it if needed). +func (i *IndexedInputs) Add(addr string, coins sdk.Coins) { + n, known := i.indexes[addr] + if !known { + n = len(i.inputs) + i.indexes[addr] = n + i.inputs = append(i.inputs, banktypes.Input{Address: addr}) + } + i.inputs[n].Coins = i.inputs[n].Coins.Add(coins...) +} + +// Get returns all the known inputs. +func (i *IndexedInputs) Get() []banktypes.Input { + return i.inputs +} + +// IndexedOutputs is a slice of bank Outputs with an index by address +type IndexedOutputs struct { + inputs []banktypes.Output + indexes map[string]int +} + +func NewIndexedOutputs() *IndexedOutputs { + return &IndexedOutputs{ + indexes: make(map[string]int), + } +} + +// Add adds the coins to the output with the given address (creating it if needed). +func (i *IndexedOutputs) Add(addr string, coins sdk.Coins) { + n, known := i.indexes[addr] + if !known { + n = len(i.inputs) + i.indexes[addr] = n + i.inputs = append(i.inputs, banktypes.Output{Address: addr}) + } + i.inputs[n].Coins = i.inputs[n].Coins.Add(coins...) +} + +// Get returns all the known outputs. +func (i *IndexedOutputs) Get() []banktypes.Output { + return i.inputs +} + +// Fulfillment is a struct containing the bank inputs/outputs that will fulfill some orders. +type Fulfillment struct { + baseOrder *Order + fullyFilledOrders []*Order + partiallFilledOrder *Order + + assetInputs *IndexedInputs + assetOutputs *IndexedOutputs + priceInputs *IndexedInputs + priceOutputs *IndexedOutputs + feeInputs *IndexedInputs +} + +func NewFulfillment(order *Order) *Fulfillment { + rv := &Fulfillment{ + baseOrder: order, + assetInputs: NewIndexedInputs(), + assetOutputs: NewIndexedOutputs(), + priceInputs: NewIndexedInputs(), + priceOutputs: NewIndexedOutputs(), + feeInputs: NewIndexedInputs(), + } + // TODO[1658]: Finish NewFulfillment. + return rv +} From 2c674518f33f02a4f99abf8892e842136c3f7c86 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 20 Sep 2023 12:44:47 -0600 Subject: [PATCH 153/309] [1658]: Use a generic IndexedAddrAmts instead of two separate types for inputs and outputs. --- x/exchange/orders.go | 79 ++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 46 deletions(-) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 88f8a05bc1..ac36c46d28 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -341,60 +341,47 @@ func (b BidOrder) Validate() error { return errors.Join(errs...) } -// IndexedInputs is a slice of bank Inputs with an index by address -type IndexedInputs struct { - inputs []banktypes.Input +// IndexedAddrAmts is a set of addresses and amounts. +type IndexedAddrAmts struct { + addrs []string + amts []sdk.Coins indexes map[string]int } -func NewIndexedInputs() *IndexedInputs { - return &IndexedInputs{ +func NewIndexedAddrAmts() *IndexedAddrAmts { + return &IndexedAddrAmts{ indexes: make(map[string]int), } } // Add adds the coins to the input with the given address (creating it if needed). -func (i *IndexedInputs) Add(addr string, coins sdk.Coins) { +func (i *IndexedAddrAmts) Add(addr string, coins sdk.Coins) { n, known := i.indexes[addr] if !known { - n = len(i.inputs) + n = len(i.addrs) i.indexes[addr] = n - i.inputs = append(i.inputs, banktypes.Input{Address: addr}) + i.addrs = append(i.addrs, addr) + i.amts = append(i.amts, sdk.NewCoins()) } - i.inputs[n].Coins = i.inputs[n].Coins.Add(coins...) + i.amts[n] = i.amts[n].Add(coins...) } -// Get returns all the known inputs. -func (i *IndexedInputs) Get() []banktypes.Input { - return i.inputs -} - -// IndexedOutputs is a slice of bank Outputs with an index by address -type IndexedOutputs struct { - inputs []banktypes.Output - indexes map[string]int -} - -func NewIndexedOutputs() *IndexedOutputs { - return &IndexedOutputs{ - indexes: make(map[string]int), +// GetInputs returns all the entries as bank Inputs. +func (i *IndexedAddrAmts) GetInputs() []banktypes.Input { + rv := make([]banktypes.Input, len(i.addrs)) + for n, addr := range i.addrs { + rv[n] = banktypes.Input{Address: addr, Coins: i.amts[n]} } + return rv } -// Add adds the coins to the output with the given address (creating it if needed). -func (i *IndexedOutputs) Add(addr string, coins sdk.Coins) { - n, known := i.indexes[addr] - if !known { - n = len(i.inputs) - i.indexes[addr] = n - i.inputs = append(i.inputs, banktypes.Output{Address: addr}) +// GetOutputs returns all the entries as bank Outputs. +func (i *IndexedAddrAmts) GetOutputs() []banktypes.Output { + rv := make([]banktypes.Output, len(i.addrs)) + for n, addr := range i.addrs { + rv[n] = banktypes.Output{Address: addr, Coins: i.amts[n]} } - i.inputs[n].Coins = i.inputs[n].Coins.Add(coins...) -} - -// Get returns all the known outputs. -func (i *IndexedOutputs) Get() []banktypes.Output { - return i.inputs + return rv } // Fulfillment is a struct containing the bank inputs/outputs that will fulfill some orders. @@ -403,21 +390,21 @@ type Fulfillment struct { fullyFilledOrders []*Order partiallFilledOrder *Order - assetInputs *IndexedInputs - assetOutputs *IndexedOutputs - priceInputs *IndexedInputs - priceOutputs *IndexedOutputs - feeInputs *IndexedInputs + assetInputs *IndexedAddrAmts + assetOutputs *IndexedAddrAmts + priceInputs *IndexedAddrAmts + priceOutputs *IndexedAddrAmts + feeInputs *IndexedAddrAmts } func NewFulfillment(order *Order) *Fulfillment { rv := &Fulfillment{ baseOrder: order, - assetInputs: NewIndexedInputs(), - assetOutputs: NewIndexedOutputs(), - priceInputs: NewIndexedInputs(), - priceOutputs: NewIndexedOutputs(), - feeInputs: NewIndexedInputs(), + assetInputs: NewIndexedAddrAmts(), + assetOutputs: NewIndexedAddrAmts(), + priceInputs: NewIndexedAddrAmts(), + priceOutputs: NewIndexedAddrAmts(), + feeInputs: NewIndexedAddrAmts(), } // TODO[1658]: Finish NewFulfillment. return rv From faf4b2f8f49d0136b1728864ed6e56119a0ac34e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 20 Sep 2023 14:55:55 -0600 Subject: [PATCH 154/309] [1659]: Normalize the attributes when updating them. --- x/exchange/keeper/keeper.go | 15 +++++++++++++-- x/exchange/keeper/market.go | 35 +++++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 7ec5501a1d..090ea01fa7 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -15,6 +15,7 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/provenance-io/provenance/x/exchange" + nametypes "github.com/provenance-io/provenance/x/name/types" ) type Keeper struct { @@ -117,11 +118,21 @@ func (k Keeper) iterate(ctx sdk.Context, pre []byte, cb func(key, value []byte) } // NormalizeReqAttrs normalizes/validates each of the provided require attributes. +// The normalized versions of the attributes are returned regardless of whether an error is also returned. func (k Keeper) NormalizeReqAttrs(ctx sdk.Context, reqAttrs []string) ([]string, error) { rv := make([]string, len(reqAttrs)) - errs := make([]error, len(reqAttrs)) + var errs []error for i, attr := range reqAttrs { - rv[i], errs[i] = k.nameKeeper.Normalize(ctx, attr) + // Normalize will either return a normalized name or error. + // In some cases where we call NormalizeReqAttrs, we won't care about any error, though, + // So if there's an error, we still set the normalized value for returning. + val, err := k.nameKeeper.Normalize(ctx, attr) + if err != nil { + errs = append(errs, err) + rv[i] = nametypes.NormalizeName(attr) + } else { + rv[i] = val + } } return rv, errors.Join(errs...) } diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index acfb31716f..c1b1322751 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -1136,12 +1136,14 @@ func updateReqAttrs(store sdk.KVStore, marketID uint32, toRemove, toAdd []string // updateReqAttrsAsk updates the attributes required to create an ask order in the store by removing and adding // the provided entries to the existing entries. +// It is assumed that the attributes have been normalized prior to calling this. func updateReqAttrsAsk(store sdk.KVStore, marketID uint32, toRemove, toAdd []string) error { return updateReqAttrs(store, marketID, toRemove, toAdd, "create ask", MakeKeyMarketReqAttrAsk) } // updateReqAttrsBid updates the attributes required to create a bid order in the store by removing and adding // the provided entries to the existing entries. +// It is assumed that the attributes have been normalized prior to calling this. func updateReqAttrsBid(store sdk.KVStore, marketID uint32, toRemove, toAdd []string) error { return updateReqAttrs(store, marketID, toRemove, toAdd, "create bid", MakeKeyMarketReqAttrBid) } @@ -1150,15 +1152,36 @@ func updateReqAttrsBid(store sdk.KVStore, marketID uint32, toRemove, toAdd []str // The caller is responsible for making sure this update should be allowed (e.g. by calling CanManageReqAttrs first). func (k Keeper) UpdateReqAttrs(ctx sdk.Context, msg *exchange.MsgMarketManageReqAttrsRequest) error { admin := sdk.MustAccAddressFromBech32(msg.Admin) + + var errs []error + // We don't care if the attributes to remove are valid so that we + // can remove entries that are somehow now invalid. + askToRemove, _ := k.NormalizeReqAttrs(ctx, msg.CreateAskToRemove) + askToAdd, err := k.NormalizeReqAttrs(ctx, msg.CreateAskToAdd) + if err != nil { + errs = append(errs, err) + } + bidToRemove, _ := k.NormalizeReqAttrs(ctx, msg.CreateBidToRemove) + bidToAdd, err := k.NormalizeReqAttrs(ctx, msg.CreateBidToAdd) + if err != nil { + errs = append(errs, err) + } + if len(errs) > 0 { + return errors.Join(errs...) + } + marketID := msg.MarketId store := k.getStore(ctx) - errs := []error{ - updateReqAttrsAsk(store, marketID, msg.CreateAskToRemove, msg.CreateAskToAdd), - updateReqAttrsBid(store, marketID, msg.CreateBidToRemove, msg.CreateBidToAdd), + + if err = updateReqAttrsAsk(store, marketID, askToRemove, askToAdd); err != nil { + errs = append(errs, err) } - err := errors.Join(errs...) - if err != nil { - return err + if err = updateReqAttrsBid(store, marketID, bidToRemove, bidToAdd); err != nil { + errs = append(errs, err) } + if len(errs) > 0 { + return errors.Join(errs...) + } + return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketReqAttrUpdated(marketID, admin)) } From 76c8c8c0076a85cea8e83842d94b47cba6e948ba Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 20 Sep 2023 16:34:58 -0600 Subject: [PATCH 155/309] [1658]: Create ValidateName and ValidateNameSegment for better error messages. --- x/name/types/name.go | 31 ++++++--- x/name/types/name_test.go | 130 ++++++++++++++++++++++++++------------ 2 files changed, 111 insertions(+), 50 deletions(-) diff --git a/x/name/types/name.go b/x/name/types/name.go index e4640ab945..0eabb860f1 100644 --- a/x/name/types/name.go +++ b/x/name/types/name.go @@ -49,14 +49,20 @@ func NormalizeName(name string) string { } // IsValidName returns true if the provided name is valid (without consideration of length limits). -// It is assumed that the name provided has been normalized using NormalizeName. +// It is assumed that the name provided has already been normalized using NormalizeName. func IsValidName(name string) bool { + return ValidateName(name) == nil +} + +// ValidateName returns an error if the provided name is not valid (without consideration of length limits). +// It is assumed that the name provided has already been normalized using NormalizeName. +func ValidateName(name string) error { for _, segment := range strings.Split(name, ".") { - if !IsValidNameSegment(segment) { - return false + if err := ValidateNameSegment(segment); err != nil { + return fmt.Errorf("invalid name %q: %w", name, err) } } - return true + return nil } // IsValidNameSegment returns true if the provided string is a valid name segment. @@ -65,21 +71,30 @@ func IsValidName(name string) bool { // // The length of the segment is not considered here because the length limits are defined in state. func IsValidNameSegment(segment string) bool { + return ValidateNameSegment(segment) == nil +} + +// ValidateNameSegment returns an error if the provided string is not a valid name segment. +// Name segments can only contain dashes, digits, and lower-case letters. +// If it's not a uuid, it can have at most one dash. +// +// The length of the segment is not considered here because the length limits are defined in state. +func ValidateNameSegment(segment string) error { // Allow valid UUID if IsValidUUID(segment) { - return true + return nil } // Only allow a single dash if not a UUID if strings.Count(segment, "-") > 1 { - return false + return fmt.Errorf("segment %q has too many dashes", segment) } // Only allow dashes, lowercase characters and digits. for _, c := range segment { if c != '-' && !unicode.IsLower(c) && !unicode.IsDigit(c) { - return false + return fmt.Errorf("illegal character %q in name segment %q", string(c), segment) } } - return true + return nil } // IsValidUUID returns true if the provided string is a valid UUID string. diff --git a/x/name/types/name_test.go b/x/name/types/name_test.go index 0fddac52d6..adc9d28208 100644 --- a/x/name/types/name_test.go +++ b/x/name/types/name_test.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/provenance-io/provenance/testutil/assertions" ) type NameRecordTestSuite struct { @@ -113,73 +114,114 @@ func TestNormalizeName(t *testing.T) { } } -func TestIsValidName(t *testing.T) { +func dashErr(segment string) string { + return fmt.Sprintf("segment %q has too many dashes", segment) +} + +func badCharErr(badRune rune, segment string) string { + return fmt.Sprintf("illegal character %q in name segment %q", string(badRune), segment) +} + +func TestValidName(t *testing.T) { tests := []struct { name string input string - exp bool + exp string }{ - {name: "empty string", input: "", exp: true}, - {name: "two spaces", input: " ", exp: false}, - {name: "with middle space", input: " a b ", exp: false}, - {name: "upper case", input: "ABcDE", exp: false}, - {name: "space around first segment", input: " ghi .def.abc", exp: false}, - {name: "space around middle segment", input: "ghi. def .abc", exp: false}, - {name: "space around third segment", input: "ghi.def. abc ", exp: false}, - {name: "middle segment has upper case", input: "ghi.DeF.abc", exp: false}, - {name: "empty first segment", input: ".def.abc", exp: true}, - {name: "first segment is a space", input: " .def.abc", exp: false}, - {name: "empty last segment", input: "ghi.def.", exp: true}, - {name: "last segment is a space", input: "ghi.def. ", exp: false}, - {name: "empty middle segment", input: "ghi..abc", exp: true}, - {name: "middle segment is a space", input: "ghi. .abc", exp: false}, - {name: "one segment two dashes", input: "a-b-c", exp: false}, - {name: "two segments one dash each", input: "a-1.b-2", exp: true}, - {name: "comma in first segment", input: "a,1.b-2", exp: false}, - {name: "comma in second segment", input: "a-1.b,2", exp: false}, - {name: "space in middle of first segment", input: "a 1.b-2", exp: false}, - {name: "space in middle of second segment", input: "a-1.b 2", exp: false}, - {name: "newline in middle of second segment", input: "a-1.b\n2", exp: false}, - {name: "middle segment has middlespace", input: "a. b c .d", exp: false}, + {name: "empty string", input: "", exp: ""}, + {name: "two spaces", input: " ", exp: badCharErr(' ', " ")}, + {name: "with middle space", input: " a b ", exp: badCharErr(' ', " a b ")}, + {name: "upper case", input: "ABcDE", exp: badCharErr('A', "ABcDE")}, + {name: "space around first segment", input: " ghi .def.abc", exp: badCharErr(' ', " ghi ")}, + {name: "space around middle segment", input: "ghi. def .abc", exp: badCharErr(' ', " def ")}, + {name: "space around third segment", input: "ghi.def. abc ", exp: badCharErr(' ', " abc ")}, + {name: "middle segment has upper case", input: "ghi.DeF.abc", exp: badCharErr('D', "DeF")}, + {name: "empty first segment", input: ".def.abc", exp: ""}, + {name: "first segment is a space", input: " .def.abc", exp: badCharErr(' ', " ")}, + {name: "empty last segment", input: "ghi.def.", exp: ""}, + {name: "last segment is a space", input: "ghi.def. ", exp: badCharErr(' ', " ")}, + {name: "empty middle segment", input: "ghi..abc", exp: ""}, + {name: "middle segment is a space", input: "ghi. .abc", exp: badCharErr(' ', " ")}, + {name: "one segment two dashes", input: "a-b-c", exp: dashErr("a-b-c")}, + {name: "two dashes in first segment", input: "a-b-c.d.e", exp: dashErr("a-b-c")}, + {name: "two dashes in middle segment", input: "d.a-b-c.e", exp: dashErr("a-b-c")}, + {name: "two dashes in last segment", input: "d.e.a-b-c", exp: dashErr("a-b-c")}, + {name: "two segments one dash each", input: "a-1.b-2", exp: ""}, + {name: "comma in first segment", input: "a,1.b-2", exp: badCharErr(',', "a,1")}, + {name: "comma in second segment", input: "a-1.b,2", exp: badCharErr(',', "b,2")}, + {name: "space in middle of first segment", input: "a 1.b-2", exp: badCharErr(' ', "a 1")}, + {name: "space in middle of second segment", input: "a-1.b 2", exp: badCharErr(' ', "b 2")}, + {name: "newline in middle of second segment", input: "a-1.b\n2", exp: badCharErr('\n', "b\n2")}, + {name: "middle segment has middlespace", input: "a. b c .d", exp: badCharErr(' ', " b c ")}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { + expErr := "" + expOK := true + if len(tc.exp) > 0 { + expErr = fmt.Sprintf("invalid name %q: %s", tc.input, tc.exp) + expOK = false + } + err := ValidateName(tc.input) + assertions.AssertErrorValue(t, err, expErr, "ValidateName(%q)", tc.input) + ok := IsValidName(tc.input) - assert.Equal(t, tc.exp, ok, "IsValidName(%q)", tc.input) + assert.Equal(t, expOK, ok, "IsValidName(%q)", tc.input) }) } } -func TestIsValidNameSegment(t *testing.T) { +func TestValidNameSegment(t *testing.T) { + badCharErrFunc := func(badRune rune) func(segment string) string { + return func(segment string) string { + return badCharErr(badRune, segment) + } + } + tests := []struct { name string seg string - exp bool + exp func(segment string) string }{ - {name: "empty", seg: "", exp: true}, - {name: "uuid with dashes", seg: "01234567-8909-8765-4321-012345678901", exp: true}, - {name: "uuid without dashes", seg: "01234567890987654321012345678901", exp: true}, - {name: "one dash", seg: "-", exp: true}, - {name: "two dashes", seg: "--", exp: false}, - {name: "all english lower-case letters, a dash, and all arabic digits", seg: "abcdefghijklmnopqrstuvwxyz-0123456789", exp: true}, - {name: "with a newline", seg: "ab\nde", exp: false}, - {name: "with a space", seg: "ab de", exp: false}, - {name: "with an underscore", seg: "ab_de", exp: false}, - {name: "single quoted", seg: "'abcde'", exp: false}, - {name: "double quoted", seg: `"abcde"`, exp: false}, + {name: "empty", seg: "", exp: nil}, + {name: "uuid with dashes", seg: "01234567-8909-8765-4321-012345678901", exp: nil}, + {name: "uuid without dashes", seg: "01234567890987654321012345678901", exp: nil}, + {name: "one dash", seg: "-", exp: nil}, + {name: "two dashes", seg: "--", exp: dashErr}, + {name: "all english lower-case letters, a dash, and all arabic digits", seg: "abcdefghijklmnopqrstuvwxyz-0123456789", exp: nil}, + {name: "with a newline", seg: "ab\nde", exp: badCharErrFunc('\n')}, + {name: "with a space", seg: "ab de", exp: badCharErrFunc(' ')}, + {name: "with an underscore", seg: "ab_de", exp: badCharErrFunc('_')}, + {name: "single quoted", seg: "'abcde'", exp: badCharErrFunc('\'')}, + {name: "double quoted", seg: `"abcde"`, exp: badCharErrFunc('"')}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { + expErr := "" + expOK := true + if tc.exp != nil { + expErr = tc.exp(tc.seg) + expOK = false + } + err := ValidateNameSegment(tc.seg) + assertions.AssertErrorValue(t, err, expErr, "ValidateNameSegment(%q)", tc.seg) + ok := IsValidNameSegment(tc.seg) - assert.Equal(t, tc.exp, ok, "IsValidNameSegment(%q)", tc.seg) + assert.Equal(t, expOK, ok, "IsValidNameSegment(%q)", tc.seg) }) } } -func TestIsValidNameSegmentChars(t *testing.T) { +func TestNameSegmentChars(t *testing.T) { + // This test checks that all valid characters are graphic chars and are valid in a name segment, + // and that a lot of the invalid characters are not valid in a name segment. + + // testerFunc is a function that applies assertions to a rune from a rune table. type testerFunc func(t *testing.T, r rune, tableName string, lo uint32, hi uint32, stride uint32) bool + + // assertRuneIsOkay asserts that the rune is graphic and valid as a name segment. assertRuneIsOkay := func(t *testing.T, r rune, tableName string, lo uint32, hi uint32, stride uint32) bool { isGraphic := unicode.IsGraphic(r) if !assert.True(t, isGraphic, "IsGraphic(%q = %u) %s{%u, %u, %d}", r, r, tableName, lo, hi, stride) { @@ -188,10 +230,12 @@ func TestIsValidNameSegmentChars(t *testing.T) { isValid := IsValidNameSegment(string(r)) return assert.True(t, isValid, "IsValidNameSegment(%q = %u) %s{%u, %u, %d}", r, r, tableName, lo, hi, stride) } + // assertRuneIsInvalid asseerts that the rune is not valid as a name segment. assertRuneIsInvalid := func(t *testing.T, r rune, tableName string, lo uint32, hi uint32, stride uint32) bool { isValid := IsValidNameSegment(string(r)) return assert.False(t, isValid, "IsValidNameSegment(%q = %u) %s{%u, %u, %d}", r, r, tableName, lo, hi, stride) } + // containsRune returns true if the provide rune is an entry in the provided slice. containsRune := func(r rune, rz []rune) bool { for _, z := range rz { if r == z { @@ -266,8 +310,9 @@ func TestIsValidNameSegmentChars(t *testing.T) { for rv <= row.Hi { r := rune(rv) if !containsRune(r, tc.skips) { - tc.tester(t, r, tc.tableName, uint32(row.Lo), uint32(row.Hi), uint32(row.Stride)) + tc.tester(t, r, tc.tableName+".R16", uint32(row.Lo), uint32(row.Hi), uint32(row.Stride)) } + // If the next one would cause overflow, we're done. if rv+row.Stride <= rv { break } @@ -279,8 +324,9 @@ func TestIsValidNameSegmentChars(t *testing.T) { for rv <= row.Hi { r := rune(rv) if !containsRune(r, tc.skips) { - tc.tester(t, r, tc.tableName, row.Lo, row.Hi, row.Stride) + tc.tester(t, r, tc.tableName+".R32", row.Lo, row.Hi, row.Stride) } + // If the next one would cause overflow, we're done. if rv+row.Stride <= rv { break } From 477b7b9a0c67ee287701ff37cf80f74404be7e84 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 20 Sep 2023 17:22:59 -0600 Subject: [PATCH 156/309] [1658]: Remove the name keeper, and don't worry about attribute length validation (only standard name validation). --- x/exchange/expected_keepers.go | 4 ---- x/exchange/keeper/keeper.go | 16 ++++------------ 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/x/exchange/expected_keepers.go b/x/exchange/expected_keepers.go index a95d48cfa7..e435adac00 100644 --- a/x/exchange/expected_keepers.go +++ b/x/exchange/expected_keepers.go @@ -25,10 +25,6 @@ type BankKeeper interface { InputOutputCoins(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) error } -type NameKeeper interface { - Normalize(ctx sdk.Context, name string) (string, error) -} - type HoldKeeper interface { AddHold(ctx sdk.Context, addr sdk.AccAddress, funds sdk.Coins, reason string) error ReleaseHold(ctx sdk.Context, addr sdk.AccAddress, funds sdk.Coins) error diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 090ea01fa7..c642e82974 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -26,7 +26,6 @@ type Keeper struct { attrKeeper exchange.AttributeKeeper bankKeeper exchange.BankKeeper holdKeeper exchange.HoldKeeper - nameKeeper exchange.NameKeeper // TODO[1658]: Finish the Keeper struct. authority string @@ -35,7 +34,7 @@ type Keeper struct { func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, feeCollectorName string, accountKeeper exchange.AccountKeeper, attrKeeper exchange.AttributeKeeper, - bankKeeper exchange.BankKeeper, holdKeeper exchange.HoldKeeper, nameKeeper exchange.NameKeeper, + bankKeeper exchange.BankKeeper, holdKeeper exchange.HoldKeeper, ) Keeper { // TODO[1658]: Finish NewKeeper. rv := Keeper{ @@ -45,7 +44,6 @@ func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, feeCollector attrKeeper: attrKeeper, bankKeeper: bankKeeper, holdKeeper: holdKeeper, - nameKeeper: nameKeeper, authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), feeCollectorName: feeCollectorName, } @@ -123,15 +121,9 @@ func (k Keeper) NormalizeReqAttrs(ctx sdk.Context, reqAttrs []string) ([]string, rv := make([]string, len(reqAttrs)) var errs []error for i, attr := range reqAttrs { - // Normalize will either return a normalized name or error. - // In some cases where we call NormalizeReqAttrs, we won't care about any error, though, - // So if there's an error, we still set the normalized value for returning. - val, err := k.nameKeeper.Normalize(ctx, attr) - if err != nil { - errs = append(errs, err) - rv[i] = nametypes.NormalizeName(attr) - } else { - rv[i] = val + rv[i] = nametypes.NormalizeName(attr) + if !nametypes.IsValidName(rv[i]) { + errs = append(errs, fmt.Errorf("invalid attribute %q", attr)) } } return rv, errors.Join(errs...) From faa018624b17ca298514108b92d4b5614fb0a03d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 20 Sep 2023 17:25:03 -0600 Subject: [PATCH 157/309] [1658]: Create a helpers.go with some of the generic helpr funcs (that were already in a few places). Iron out most of ask order fulfillment. --- x/exchange/helpers.go | 73 ++++++++ x/exchange/helpers_test.go | 331 ++++++++++++++++++++++++++++++++++++ x/exchange/keeper/orders.go | 22 +-- x/exchange/market.go | 45 +---- x/exchange/market_test.go | 245 -------------------------- x/exchange/orders.go | 125 ++++++++++---- x/exchange/orders_test.go | 71 +------- 7 files changed, 502 insertions(+), 410 deletions(-) create mode 100644 x/exchange/helpers.go create mode 100644 x/exchange/helpers_test.go diff --git a/x/exchange/helpers.go b/x/exchange/helpers.go new file mode 100644 index 0000000000..2df4ca72a8 --- /dev/null +++ b/x/exchange/helpers.go @@ -0,0 +1,73 @@ +package exchange + +import sdk "github.com/cosmos/cosmos-sdk/types" + +// contains returns true if the provided toFind is present in the provided vals. +func contains[T any](vals []T, toFind T, equals func(T, T) bool) bool { + for _, v := range vals { + if equals(toFind, v) { + return true + } + } + return false +} + +// intersection returns each entry that is in both lists. +func intersection[T any](list1, list2 []T, equals func(T, T) bool) []T { + var rv []T + for _, a := range list1 { + if contains(list2, a, equals) && !contains(rv, a, equals) { + rv = append(rv, a) + } + } + return rv +} + +// EqualsUint64 returns true if the two uint64 values provided are equal. +func EqualsUint64(a, b uint64) bool { + return a == b +} + +// ContainsUint64 returns true if the uint64 to find is in the vals slice. +func ContainsUint64(vals []uint64, toFind uint64) bool { + return contains(vals, toFind, EqualsUint64) +} + +// IntersectionUint64 returns each uint64 that is in both lists. +func IntersectionUint64(a, b []uint64) []uint64 { + return intersection(a, b, EqualsUint64) +} + +// ContainsString returns true if the string to find is in the vals slice. +func ContainsString(vals []string, toFind string) bool { + return contains(vals, toFind, func(a, b string) bool { + return a == b + }) +} + +// CoinsEquals returns true if the two provided coins are equal. +// +// sdk.Coins.IsEqual will panic if the two have the same number of entries, but different denoms. +// This one will return false in that case instead of panicking. +func CoinsEquals(a, b sdk.Coins) (isEqual bool) { + defer func() { + if r := recover(); r != nil { + isEqual = false + } + }() + return a.IsEqual(b) +} + +// CoinEquals returns true if the two provided coin entries are equal. +// Designed for use with intersection. +// +// We can't just provide sdk.Coin.IsEqual to intersection because that PANICS if the denoms are different. +// And we can't provide sdk.Coin.Equal to intersection because it takes in an interface{} (instead of sdk.Coin). +func CoinEquals(a, b sdk.Coin) bool { + return a.Equal(b) +} + +// IntersectionOfCoin returns each sdk.Coin entry that is in both lists. +func IntersectionOfCoin(list1, list2 []sdk.Coin) []sdk.Coin { + return intersection(list1, list2, CoinEquals) +} diff --git a/x/exchange/helpers_test.go b/x/exchange/helpers_test.go new file mode 100644 index 0000000000..e88400d562 --- /dev/null +++ b/x/exchange/helpers_test.go @@ -0,0 +1,331 @@ +package exchange + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// TODO[1658]: func TestEqualsUint64(t *testing.T) + +func TestContainsUint64(t *testing.T) { + tests := []struct { + name string + vals []uint64 + toFind uint64 + exp bool + }{ + { + name: "nil vals", + vals: nil, + toFind: 0, + exp: false, + }, + { + name: "empty vals", + vals: []uint64{}, + toFind: 0, + exp: false, + }, + { + name: "one val: same", + vals: []uint64{1}, + toFind: 1, + exp: true, + }, + { + name: "one val: different", + vals: []uint64{1}, + toFind: 2, + exp: false, + }, + { + name: "three vals: not found", + vals: []uint64{1, 2, 3}, + toFind: 0, + exp: false, + }, + { + name: "three vals: first", + vals: []uint64{1, 2, 3}, + toFind: 1, + exp: true, + }, + { + name: "three vals: second", + vals: []uint64{1, 2, 3}, + toFind: 2, + exp: true, + }, + { + name: "three vals: third", + vals: []uint64{1, 2, 3}, + toFind: 3, + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = ContainsUint64(tc.vals, tc.toFind) + } + require.NotPanics(t, testFunc, "ContainsUint64(%q, %q)", tc.vals, tc.toFind) + assert.Equal(t, tc.exp, actual, "ContainsUint64(%q, %q)", tc.vals, tc.toFind) + }) + } +} + +// TODO[1658]: func TestIntersectionUint64(t *testing.T) + +func TestContainsString(t *testing.T) { + tests := []struct { + name string + vals []string + toFind string + exp bool + }{ + { + name: "nil vals", + vals: nil, + toFind: "", + exp: false, + }, + { + name: "empty vals", + vals: []string{}, + toFind: "", + exp: false, + }, + { + name: "one val: same", + vals: []string{"one"}, + toFind: "one", + exp: true, + }, + { + name: "one val: different", + vals: []string{"one"}, + toFind: "two", + exp: false, + }, + { + name: "one val: space at end of val", + vals: []string{"one "}, + toFind: "one", + exp: false, + }, + { + name: "one val: space at end of toFind", + vals: []string{"one"}, + toFind: "one ", + exp: false, + }, + { + name: "one val: space at start of val", + vals: []string{" one"}, + toFind: "one", + exp: false, + }, + { + name: "one val: space at start of toFind", + vals: []string{"one"}, + toFind: " one", + exp: false, + }, + { + name: "one val: different casing", + vals: []string{"one"}, + toFind: "oNe", + exp: false, + }, + { + name: "three vals: not found", + vals: []string{"one", "two", "three"}, + toFind: "zero", + exp: false, + }, + { + name: "three vals: first", + vals: []string{"one", "two", "three"}, + toFind: "one", + exp: true, + }, + { + name: "three vals: second", + vals: []string{"one", "two", "three"}, + toFind: "two", + exp: true, + }, + { + name: "three vals: third", + vals: []string{"one", "two", "three"}, + toFind: "three", + exp: true, + }, + { + name: "three vals: empty string", + vals: []string{"one", "two", "three"}, + toFind: "", + exp: false, + }, + { + name: "empty string in vals: finding empty string", + vals: []string{"one", "", "three"}, + toFind: "", + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = ContainsString(tc.vals, tc.toFind) + } + require.NotPanics(t, testFunc, "ContainsString(%q, %q)", tc.vals, tc.toFind) + assert.Equal(t, tc.exp, actual, "ContainsString(%q, %q)", tc.vals, tc.toFind) + }) + } +} + +// TODO[1658]: func TestCoinsEquals(t *testing.T) + +func TestCoinEquals(t *testing.T) { + tests := []struct { + name string + a sdk.Coin + b sdk.Coin + exp bool + }{ + { + name: "zero-value coins", + a: sdk.Coin{}, + b: sdk.Coin{}, + exp: true, + }, + { + name: "different amounts", + a: sdk.NewInt64Coin("pear", 2), + b: sdk.NewInt64Coin("pear", 3), + exp: false, + }, + { + name: "different denoms", + a: sdk.NewInt64Coin("pear", 2), + b: sdk.NewInt64Coin("onion", 2), + exp: false, + }, + { + name: "same denom and amount", + a: sdk.NewInt64Coin("pear", 2), + b: sdk.NewInt64Coin("pear", 2), + exp: true, + }, + { + name: "same denom zero amounts", + a: sdk.NewInt64Coin("pear", 0), + b: sdk.NewInt64Coin("pear", 0), + exp: true, + }, + { + name: "diff denom zero amounts", + a: sdk.NewInt64Coin("pear", 0), + b: sdk.NewInt64Coin("onion", 0), + exp: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = CoinEquals(tc.a, tc.b) + } + require.NotPanics(t, testFunc, "CoinEquals(%q, %q)", tc.a, tc.b) + assert.Equal(t, tc.exp, actual, "CoinEquals(%q, %q) result", tc.a, tc.b) + }) + } +} + +func TestIntersectionOfCoin(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + options1 []sdk.Coin + options2 []sdk.Coin + expected []sdk.Coin + }{ + {name: "nil nil", options1: nil, options2: nil, expected: nil}, + {name: "nil empty", options1: nil, options2: []sdk.Coin{}, expected: nil}, + {name: "empty nil", options1: []sdk.Coin{}, options2: nil, expected: nil}, + {name: "empty empty", options1: []sdk.Coin{}, options2: []sdk.Coin{}, expected: nil}, + { + name: "one nil", + options1: []sdk.Coin{coin(1, "finger")}, + options2: nil, + expected: nil, + }, + { + name: "nil one", + options1: nil, + options2: []sdk.Coin{coin(1, "finger")}, + expected: nil, + }, + { + name: "one one same", + options1: []sdk.Coin{coin(1, "finger")}, + options2: []sdk.Coin{coin(1, "finger")}, + expected: []sdk.Coin{coin(1, "finger")}, + }, + { + name: "one one different first amount", + options1: []sdk.Coin{coin(2, "finger")}, + options2: []sdk.Coin{coin(1, "finger")}, + expected: nil, + }, + { + name: "one one different first denom", + options1: []sdk.Coin{coin(1, "toe")}, + options2: []sdk.Coin{coin(1, "finger")}, + expected: nil, + }, + { + name: "one one different second amount", + options1: []sdk.Coin{coin(1, "finger")}, + options2: []sdk.Coin{coin(2, "finger")}, + expected: nil, + }, + { + name: "one one different second denom", + options1: []sdk.Coin{coin(1, "finger")}, + options2: []sdk.Coin{coin(1, "toe")}, + expected: nil, + }, + { + name: "three three two common", + options1: []sdk.Coin{coin(1, "finger"), coin(2, "toe"), coin(3, "elbow")}, + options2: []sdk.Coin{coin(5, "toe"), coin(3, "elbow"), coin(1, "finger")}, + expected: []sdk.Coin{coin(1, "finger"), coin(3, "elbow")}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual []sdk.Coin + testFunc := func() { + actual = IntersectionOfCoin(tc.options1, tc.options2) + } + require.NotPanics(t, testFunc, "IntersectionOfCoin") + assert.Equal(t, tc.expected, actual, "IntersectionOfCoin result") + }) + } +} diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 96ff8ed557..26f86a0410 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -392,20 +392,6 @@ func (k Keeper) CancelOrder(ctx sdk.Context, orderID uint64, signer string) erro return ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCancelled(orderID, signerAddr)) } -// safeCoinsEquals returns true if the two provided coins are equal. -// Returns false instead of panicking like sdk.Coins.IsEqual. -func safeCoinsEquals(a, b sdk.Coins) (isEqual bool) { - // The sdk.Coins.IsEqual function will panic if a and b have the same number of entries, but different denoms. - // Really, that stuff is all pretty panic happy. - // In here, we don't really care why it panics, but if it does, they're not equal. - defer func() { - if r := recover(); r != nil { - isEqual = false - } - }() - return a.IsEqual(b) -} - // getBidOrders gets orders from the store, making sure they're bid orders in the given market // and do not have the same buyer as the provided seller. If the seller isn't yet known, just provide "" for it. func (k Keeper) getBidOrders(store sdk.KVStore, marketID uint32, orderIDs []uint64, seller string) ([]*exchange.Order, error) { @@ -589,7 +575,7 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro totalAssets, totalPrice := sumAssetsAndPrice(orders) - if !safeCoinsEquals(totalAssets, msg.TotalAssets) { + if !exchange.CoinsEquals(totalAssets, msg.TotalAssets) { return fmt.Errorf("total assets %q does not equal sum of bid order assets %q", msg.TotalAssets, totalAssets) } @@ -728,7 +714,7 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro totalAssets, totalPrice := sumAssetsAndPrice(orders) - if !safeCoinsEquals(totalPrice, sdk.Coins{msg.TotalPrice}) { + if !exchange.CoinsEquals(totalPrice, sdk.Coins{msg.TotalPrice}) { return fmt.Errorf("total price %q does not equal sum of ask order prices %q", msg.TotalPrice, totalPrice) } @@ -784,6 +770,8 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO totalAssetsForSale, totalAskPrice := sumAssetsAndPrice(askOrders) totalAssetsToBuy, totalBidPrice := sumAssetsAndPrice(bidOrders) + // TODO[1659]: Allow for multiple asset denoms in some cases. + var errs []error if len(totalAssetsForSale) != 1 { errs = append(errs, fmt.Errorf("cannot settle with multiple ask order asset denoms %q", totalAssetsForSale)) @@ -813,7 +801,7 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO return errors.Join(errs...) } - if !expectPartial && !safeCoinsEquals(totalAssetsForSale, totalAssetsToBuy) { + if !expectPartial && !exchange.CoinsEquals(totalAssetsForSale, totalAssetsToBuy) { return fmt.Errorf("total assets for sale %q does not equal total assets to buy %q", totalAssetsForSale, totalAssetsToBuy) } diff --git a/x/exchange/market.go b/x/exchange/market.go index bae42ae3ab..60e212412b 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -198,13 +198,6 @@ func ValidateBuyerFeeRatios(ratios []FeeRatio) error { return errors.Join(errs...) } -// ContainsString returns true if the string to find is in the vals slice. -func ContainsString(vals []string, toFind string) bool { - return contains(vals, toFind, func(a, b string) bool { - return a == b - }) -} - // String returns a string representation of this FeeRatio. func (r FeeRatio) String() string { return fmt.Sprintf("%s:%s", r.Price, r.Fee) @@ -303,20 +296,6 @@ func ValidateDisjointFeeRatios(field string, toAdd, toRemove []FeeRatio) error { return nil } -// CoinEquals returns true if the two provided coin entries are equal. -// Designed for use with intersection. -// -// We can't just provide sdk.Coin.IsEqual to intersection because that PANICS if the denoms are different. -// And we can't provide sdk.Coin.Equal to intersection because it takes in an interface{} (instead of sdk.Coin). -func CoinEquals(a, b sdk.Coin) bool { - return a.Equal(b) -} - -// IntersectionOfCoins returns each sdk.Coin entry that is in both lists. -func IntersectionOfCoins(list1, list2 []sdk.Coin) []sdk.Coin { - return intersection(list1, list2, CoinEquals) -} - // ValidateAddRemoveFeeOptions returns an error if the toAdd list has an invalid // entry or if the two lists have one or more common entries. func ValidateAddRemoveFeeOptions(field string, toAdd, toRemove []sdk.Coin) error { @@ -324,7 +303,7 @@ func ValidateAddRemoveFeeOptions(field string, toAdd, toRemove []sdk.Coin) error if err := ValidateFeeOptions(field+" to add", toAdd); err != nil { errs = append(errs, err) } - shared := IntersectionOfCoins(toAdd, toRemove) + shared := IntersectionOfCoin(toAdd, toRemove) if len(shared) > 0 { errs = append(errs, fmt.Errorf("cannot add and remove the same %s options %s", field, sdk.Coins(shared))) } @@ -488,6 +467,7 @@ func ValidateReqAttrs(field string, attrs []string) error { } // IntersectionOfAttributes returns each attribute that is in both lists. +// Casing is ignored. Returned values will have the casing that the entry has in list1. func IntersectionOfAttributes(list1, list2 []string) []string { return intersection(list1, list2, strings.EqualFold) } @@ -565,24 +545,3 @@ func IsReqAttrMatch(reqAttr, accAttr string) bool { } return reqAttr == accAttr } - -// contains returns true if the provided toFind is present in the provided vals. -func contains[T any](vals []T, toFind T, equals func(T, T) bool) bool { - for _, v := range vals { - if equals(toFind, v) { - return true - } - } - return false -} - -// intersection returns each entry that is in both lists. -func intersection[T any](list1, list2 []T, equals func(T, T) bool) []T { - var rv []T - for _, a := range list1 { - if contains(list2, a, equals) && !contains(rv, a, equals) { - rv = append(rv, a) - } - } - return rv -} diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 4e6fd9231d..38fa86b563 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -728,117 +728,6 @@ func TestValidateBuyerFeeRatios(t *testing.T) { } } -func TestContainsString(t *testing.T) { - tests := []struct { - name string - vals []string - toFind string - exp bool - }{ - { - name: "nil vals", - vals: nil, - toFind: "", - exp: false, - }, - { - name: "empty vals", - vals: []string{}, - toFind: "", - exp: false, - }, - { - name: "one val: same", - vals: []string{"one"}, - toFind: "one", - exp: true, - }, - { - name: "one val: different", - vals: []string{"one"}, - toFind: "two", - exp: false, - }, - { - name: "one val: space at end of val", - vals: []string{"one "}, - toFind: "one", - exp: false, - }, - { - name: "one val: space at end of toFind", - vals: []string{"one"}, - toFind: "one ", - exp: false, - }, - { - name: "one val: space at start of val", - vals: []string{" one"}, - toFind: "one", - exp: false, - }, - { - name: "one val: space at start of toFind", - vals: []string{"one"}, - toFind: " one", - exp: false, - }, - { - name: "one val: different casing", - vals: []string{"one"}, - toFind: "oNe", - exp: false, - }, - { - name: "three vals: not found", - vals: []string{"one", "two", "three"}, - toFind: "zero", - exp: false, - }, - { - name: "three vals: first", - vals: []string{"one", "two", "three"}, - toFind: "one", - exp: true, - }, - { - name: "three vals: second", - vals: []string{"one", "two", "three"}, - toFind: "two", - exp: true, - }, - { - name: "three vals: third", - vals: []string{"one", "two", "three"}, - toFind: "three", - exp: true, - }, - { - name: "three vals: empty string", - vals: []string{"one", "two", "three"}, - toFind: "", - exp: false, - }, - { - name: "empty string in vals: finding empty string", - vals: []string{"one", "", "three"}, - toFind: "", - exp: true, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual bool - testFunc := func() { - actual = ContainsString(tc.vals, tc.toFind) - } - require.NotPanics(t, testFunc, "ContainsString(%q, %q)", tc.vals, tc.toFind) - assert.Equal(t, tc.exp, actual, "ContainsString(%q, %q)", tc.vals, tc.toFind) - }) - } -} - func TestFeeRatio_String(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} @@ -1520,140 +1409,6 @@ func TestValidateDisjointFeeRatios(t *testing.T) { } } -func TestCoinEquals(t *testing.T) { - tests := []struct { - name string - a sdk.Coin - b sdk.Coin - exp bool - }{ - { - name: "zero-value coins", - a: sdk.Coin{}, - b: sdk.Coin{}, - exp: true, - }, - { - name: "different amounts", - a: sdk.NewInt64Coin("pear", 2), - b: sdk.NewInt64Coin("pear", 3), - exp: false, - }, - { - name: "different denoms", - a: sdk.NewInt64Coin("pear", 2), - b: sdk.NewInt64Coin("onion", 2), - exp: false, - }, - { - name: "same denom and amount", - a: sdk.NewInt64Coin("pear", 2), - b: sdk.NewInt64Coin("pear", 2), - exp: true, - }, - { - name: "same denom zero amounts", - a: sdk.NewInt64Coin("pear", 0), - b: sdk.NewInt64Coin("pear", 0), - exp: true, - }, - { - name: "diff denom zero amounts", - a: sdk.NewInt64Coin("pear", 0), - b: sdk.NewInt64Coin("onion", 0), - exp: false, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual bool - testFunc := func() { - actual = CoinEquals(tc.a, tc.b) - } - require.NotPanics(t, testFunc, "CoinEquals(%q, %q)", tc.a, tc.b) - assert.Equal(t, tc.exp, actual, "CoinEquals(%q, %q) result", tc.a, tc.b) - }) - } -} - -func TestIntersectionOfCoins(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} - } - - tests := []struct { - name string - options1 []sdk.Coin - options2 []sdk.Coin - expected []sdk.Coin - }{ - {name: "nil nil", options1: nil, options2: nil, expected: nil}, - {name: "nil empty", options1: nil, options2: []sdk.Coin{}, expected: nil}, - {name: "empty nil", options1: []sdk.Coin{}, options2: nil, expected: nil}, - {name: "empty empty", options1: []sdk.Coin{}, options2: []sdk.Coin{}, expected: nil}, - { - name: "one nil", - options1: []sdk.Coin{coin(1, "finger")}, - options2: nil, - expected: nil, - }, - { - name: "nil one", - options1: nil, - options2: []sdk.Coin{coin(1, "finger")}, - expected: nil, - }, - { - name: "one one same", - options1: []sdk.Coin{coin(1, "finger")}, - options2: []sdk.Coin{coin(1, "finger")}, - expected: []sdk.Coin{coin(1, "finger")}, - }, - { - name: "one one different first amount", - options1: []sdk.Coin{coin(2, "finger")}, - options2: []sdk.Coin{coin(1, "finger")}, - expected: nil, - }, - { - name: "one one different first denom", - options1: []sdk.Coin{coin(1, "toe")}, - options2: []sdk.Coin{coin(1, "finger")}, - expected: nil, - }, - { - name: "one one different second amount", - options1: []sdk.Coin{coin(1, "finger")}, - options2: []sdk.Coin{coin(2, "finger")}, - expected: nil, - }, - { - name: "one one different second denom", - options1: []sdk.Coin{coin(1, "finger")}, - options2: []sdk.Coin{coin(1, "toe")}, - expected: nil, - }, - { - name: "three three two common", - options1: []sdk.Coin{coin(1, "finger"), coin(2, "toe"), coin(3, "elbow")}, - options2: []sdk.Coin{coin(5, "toe"), coin(3, "elbow"), coin(1, "finger")}, - expected: []sdk.Coin{coin(1, "finger"), coin(3, "elbow")}, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual []sdk.Coin - testFunc := func() { - actual = IntersectionOfCoins(tc.options1, tc.options2) - } - require.NotPanics(t, testFunc, "IntersectionOfCoins") - assert.Equal(t, tc.expected, actual, "IntersectionOfCoins result") - }) - } -} - func TestValidateAddRemoveFeeOptions(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} diff --git a/x/exchange/orders.go b/x/exchange/orders.go index ac36c46d28..2636f0894d 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -33,21 +33,6 @@ func findDuplicateIds(orderIDs []uint64) []uint64 { return rv } -// equalsUint64 returns true if the two uint64 values provided are equal. -func equalsUint64(a, b uint64) bool { - return a == b -} - -// ContainsUint64 returns true if the uint64 to find is in the vals slice. -func ContainsUint64(vals []uint64, toFind uint64) bool { - return contains(vals, toFind, equalsUint64) -} - -// IntersectionUint64 returns each uint64 that is in both lists. -func IntersectionUint64(a, b []uint64) []uint64 { - return intersection(a, b, equalsUint64) -} - // ValidateOrderIDs makes sure that one or more order ids are provided, // none of them are zero, and there aren't any duplicates. func ValidateOrderIDs(field string, orderIDs []uint64) error { @@ -384,28 +369,96 @@ func (i *IndexedAddrAmts) GetOutputs() []banktypes.Output { return rv } -// Fulfillment is a struct containing the bank inputs/outputs that will fulfill some orders. -type Fulfillment struct { - baseOrder *Order - fullyFilledOrders []*Order - partiallFilledOrder *Order - - assetInputs *IndexedAddrAmts - assetOutputs *IndexedAddrAmts - priceInputs *IndexedAddrAmts - priceOutputs *IndexedAddrAmts - feeInputs *IndexedAddrAmts +// OrderSplit contains an order, and the asset and price amounts that should come out of it. +type OrderSplit struct { + Order *Order + Assets sdk.Coins + Price sdk.Coin +} + +// AskOrderFulfillment represents how an ask order is fulfilled. +type AskOrderFulfillment struct { + Order *Order + AssetsLeft sdk.Coins + TotalPrice sdk.Coin + Splits []*OrderSplit } -func NewFulfillment(order *Order) *Fulfillment { - rv := &Fulfillment{ - baseOrder: order, - assetInputs: NewIndexedAddrAmts(), - assetOutputs: NewIndexedAddrAmts(), - priceInputs: NewIndexedAddrAmts(), - priceOutputs: NewIndexedAddrAmts(), - feeInputs: NewIndexedAddrAmts(), +func NewAskOrderFulfillment(order *Order) *AskOrderFulfillment { + if !order.IsAskOrder() { + panic(fmt.Errorf("cannot create AskOrderFulfillment for %s order %d", + order.GetOrderType(), order.OrderId)) } - // TODO[1658]: Finish NewFulfillment. - return rv + askOrder := order.GetAskOrder() + return &AskOrderFulfillment{ + Order: order, + AssetsLeft: askOrder.Assets, + TotalPrice: sdk.NewInt64Coin(askOrder.Price.Denom, 0), + } +} + +// AddSplit applies the given bid order in the amount of assets provided to this ask order. +func (f *AskOrderFulfillment) AddSplit(order *Order, assets sdk.Coins) error { + askOrderID := f.Order.OrderId + bidOrderID := order.OrderId + if !order.IsBidOrder() { + return fmt.Errorf("cannot fill ask order %d with %s order %d", + askOrderID, order.GetOrderType(), bidOrderID) + } + + askOrder := f.Order.GetAskOrder() + bidOrder := order.GetBidOrder() + + if askOrder.Price.Denom != bidOrder.Price.Denom { + return fmt.Errorf("cannot fill ask order %d having price %q with bid order %d having price %q: denom mismatch", + askOrderID, askOrder.Price, bidOrderID, bidOrder.Price) + } + + if assets.IsZero() { + return fmt.Errorf("cannot fill ask order %d with zero assets from bid order %d", askOrderID, bidOrderID) + } + if assets.IsAnyNegative() { + return fmt.Errorf("cannot fill ask order %d with negative assets %q from bid order %d", + askOrderID, assets, bidOrderID) + } + // TODO[1658]: This should be checked against the assets left to fill in the bid order. + _, hasNeg := bidOrder.Assets.SafeSub(assets...) + if hasNeg { + return fmt.Errorf("cannot fill ask order %d with assets %q from bid order %d: insufficient assets %q in bid order", + askOrderID, assets, bidOrderID, bidOrder.Assets) + } + if len(bidOrder.Assets) > 1 && !CoinsEquals(bidOrder.Assets, assets) { + return fmt.Errorf("cannot fill ask order %d with assets %q from bid order %d having assets %q: "+ + "unable to divide price for multiple asset types", + askOrderID, assets, bidOrderID, bidOrder.Assets) + } + + newAssetsLeft, hasNeg := f.AssetsLeft.SafeSub(assets...) + if hasNeg { + return fmt.Errorf("cannot fill ask order %d having %q left with assets %q from bid order %d", + askOrderID, f.AssetsLeft, assets, bidOrderID) + } + + split := &OrderSplit{ + Order: order, + Assets: assets, + } + + if CoinsEquals(assets, bidOrder.Assets) { + split.Price = bidOrder.Price + } else { + if len(assets) != 1 || len(bidOrder.Assets) != 1 { + return fmt.Errorf("cannot prorate bid order %d assets %q for ask order %d with assets %q: multiple asset denoms", + order.OrderId, assets, f.Order.OrderId, askOrder.Assets) + } + // Note, we truncate the division here. + // Later, after all fulfillments have been identified, we will handle the remainders. + priceAmt := bidOrder.Price.Amount.Mul(askOrder.Assets[0].Amount).Quo(assets[0].Amount) + split.Price = sdk.NewCoin(bidOrder.Price.Denom, priceAmt) + } + + f.Splits = append(f.Splits, split) + f.AssetsLeft = newAssetsLeft + f.TotalPrice = f.TotalPrice.Add(split.Price) + return nil } diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 372785196d..2830fa74b4 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -79,75 +79,6 @@ func TestOrderTypesAndBytes(t *testing.T) { assert.NoError(t, err, "checking for duplicate values") } -func TestContainsUint64(t *testing.T) { - tests := []struct { - name string - vals []uint64 - toFind uint64 - exp bool - }{ - { - name: "nil vals", - vals: nil, - toFind: 0, - exp: false, - }, - { - name: "empty vals", - vals: []uint64{}, - toFind: 0, - exp: false, - }, - { - name: "one val: same", - vals: []uint64{1}, - toFind: 1, - exp: true, - }, - { - name: "one val: different", - vals: []uint64{1}, - toFind: 2, - exp: false, - }, - { - name: "three vals: not found", - vals: []uint64{1, 2, 3}, - toFind: 0, - exp: false, - }, - { - name: "three vals: first", - vals: []uint64{1, 2, 3}, - toFind: 1, - exp: true, - }, - { - name: "three vals: second", - vals: []uint64{1, 2, 3}, - toFind: 2, - exp: true, - }, - { - name: "three vals: third", - vals: []uint64{1, 2, 3}, - toFind: 3, - exp: true, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual bool - testFunc := func() { - actual = ContainsUint64(tc.vals, tc.toFind) - } - require.NotPanics(t, testFunc, "ContainsUint64(%q, %q)", tc.vals, tc.toFind) - assert.Equal(t, tc.exp, actual, "ContainsUint64(%q, %q)", tc.vals, tc.toFind) - }) - } -} - func TestValidateOrderIDs(t *testing.T) { tests := []struct { name string @@ -1344,3 +1275,5 @@ func TestBidOrder_Validate(t *testing.T) { }) } } + +// TODO[1658]: Unit tests on the fulfillment stuff. From c53bda174f46bf71a14d50fa3c9c4152ca53d200 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 20 Sep 2023 17:50:42 -0600 Subject: [PATCH 158/309] [1658]: Move the fulfillment stuff to its own file and create the bid order fulfillment thingy. --- x/exchange/fulfillment.go | 224 +++++++++++++++++++++++++++++++++ x/exchange/fulfillment_test.go | 3 + x/exchange/orders.go | 138 -------------------- x/exchange/orders_test.go | 2 - 4 files changed, 227 insertions(+), 140 deletions(-) create mode 100644 x/exchange/fulfillment.go create mode 100644 x/exchange/fulfillment_test.go diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go new file mode 100644 index 0000000000..bd06fff59a --- /dev/null +++ b/x/exchange/fulfillment.go @@ -0,0 +1,224 @@ +package exchange + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// IndexedAddrAmts is a set of addresses and amounts. +type IndexedAddrAmts struct { + addrs []string + amts []sdk.Coins + indexes map[string]int +} + +func NewIndexedAddrAmts() *IndexedAddrAmts { + return &IndexedAddrAmts{ + indexes: make(map[string]int), + } +} + +// Add adds the coins to the input with the given address (creating it if needed). +func (i *IndexedAddrAmts) Add(addr string, coins sdk.Coins) { + n, known := i.indexes[addr] + if !known { + n = len(i.addrs) + i.indexes[addr] = n + i.addrs = append(i.addrs, addr) + i.amts = append(i.amts, sdk.NewCoins()) + } + i.amts[n] = i.amts[n].Add(coins...) +} + +// GetInputs returns all the entries as bank Inputs. +func (i *IndexedAddrAmts) GetInputs() []banktypes.Input { + rv := make([]banktypes.Input, len(i.addrs)) + for n, addr := range i.addrs { + rv[n] = banktypes.Input{Address: addr, Coins: i.amts[n]} + } + return rv +} + +// GetOutputs returns all the entries as bank Outputs. +func (i *IndexedAddrAmts) GetOutputs() []banktypes.Output { + rv := make([]banktypes.Output, len(i.addrs)) + for n, addr := range i.addrs { + rv[n] = banktypes.Output{Address: addr, Coins: i.amts[n]} + } + return rv +} + +// OrderSplit contains an order, and the asset and price amounts that should come out of it. +type OrderSplit struct { + Order *Order + Assets sdk.Coins + Price sdk.Coin +} + +// AskOrderFulfillment represents how an ask order is fulfilled. +type AskOrderFulfillment struct { + Order *Order + AssetsLeft sdk.Coins + TotalPrice sdk.Coin + Splits []*OrderSplit +} + +type BidOrderFulfillment struct { + Order *Order + TotalAssets sdk.Coins + PriceLeft sdk.Coin + Splits []*OrderSplit +} + +func NewAskOrderFulfillment(order *Order) *AskOrderFulfillment { + if !order.IsAskOrder() { + panic(fmt.Errorf("cannot create AskOrderFulfillment for %s order %d", + order.GetOrderType(), order.OrderId)) + } + askOrder := order.GetAskOrder() + return &AskOrderFulfillment{ + Order: order, + AssetsLeft: askOrder.Assets, + TotalPrice: sdk.NewInt64Coin(askOrder.Price.Denom, 0), + } +} + +func NewBidOrderFulfillment(order *Order) *BidOrderFulfillment { + if !order.IsBidOrder() { + panic(fmt.Errorf("cannot create BidOrderFulfillment for %s order %d", + order.GetOrderType(), order.OrderId)) + } + bidOrder := order.GetBidOrder() + return &BidOrderFulfillment{ + Order: order, + PriceLeft: bidOrder.Price, + } +} + +// AddSplit applies the given bid order in the amount of assets provided to this ask order. +func (f *AskOrderFulfillment) AddSplit(order *Order, assets sdk.Coins) error { + askOrderID := f.Order.OrderId + bidOrderID := order.OrderId + if !order.IsBidOrder() { + return fmt.Errorf("cannot fill ask order %d with %s order %d", + askOrderID, order.GetOrderType(), bidOrderID) + } + + askOrder := f.Order.GetAskOrder() + bidOrder := order.GetBidOrder() + + if askOrder.Price.Denom != bidOrder.Price.Denom { + return fmt.Errorf("cannot fill ask order %d having price %q with bid order %d having price %q: denom mismatch", + askOrderID, askOrder.Price, bidOrderID, bidOrder.Price) + } + + if assets.IsZero() { + return fmt.Errorf("cannot fill ask order %d with zero assets from bid order %d", askOrderID, bidOrderID) + } + if assets.IsAnyNegative() { + return fmt.Errorf("cannot fill ask order %d with negative assets %q from bid order %d", + askOrderID, assets, bidOrderID) + } + // TODO[1658]: This should be checked against the assets left to fill in the bid order. + _, hasNeg := bidOrder.Assets.SafeSub(assets...) + if hasNeg { + return fmt.Errorf("cannot fill ask order %d with assets %q from bid order %d: insufficient assets %q in bid order", + askOrderID, assets, bidOrderID, bidOrder.Assets) + } + if len(bidOrder.Assets) > 1 && !CoinsEquals(bidOrder.Assets, assets) { + return fmt.Errorf("cannot fill ask order %d with assets %q from bid order %d having assets %q: "+ + "unable to divide price for multiple asset types", + askOrderID, assets, bidOrderID, bidOrder.Assets) + } + + newAssetsLeft, hasNeg := f.AssetsLeft.SafeSub(assets...) + if hasNeg { + return fmt.Errorf("cannot fill ask order %d having %q left with assets %q from bid order %d", + askOrderID, f.AssetsLeft, assets, bidOrderID) + } + + split := &OrderSplit{ + Order: order, + Assets: assets, + } + + if CoinsEquals(assets, bidOrder.Assets) { + split.Price = bidOrder.Price + } else { + if len(assets) != 1 || len(bidOrder.Assets) != 1 { + return fmt.Errorf("cannot prorate bid order %d assets %q for ask order %d with assets %q: multiple asset denoms", + bidOrderID, assets, askOrderID, askOrder.Assets) + } + // Note, we truncate the division here. + // Later, after all fulfillments have been identified, we will handle the remainders. + priceAmt := bidOrder.Price.Amount.Mul(askOrder.Assets[0].Amount).Quo(assets[0].Amount) + split.Price = sdk.NewCoin(f.TotalPrice.Denom, priceAmt) + } + + f.Splits = append(f.Splits, split) + f.AssetsLeft = newAssetsLeft + f.TotalPrice = f.TotalPrice.Add(split.Price) + return nil +} + +func (f *BidOrderFulfillment) AddSplit(order *Order, assets sdk.Coins) error { + askOrderID := order.OrderId + bidOrderID := f.Order.OrderId + if !order.IsAskOrder() { + return fmt.Errorf("cannot fill bid order %d with %s order %d", + askOrderID, order.GetOrderType(), bidOrderID) + } + + askOrder := order.GetAskOrder() + bidOrder := f.Order.GetBidOrder() + + if askOrder.Price.Denom != bidOrder.Price.Denom { + return fmt.Errorf("cannot fill bid order %d having price %q with ask order %d having price %q: denom mismatch", + bidOrderID, bidOrder.Price, askOrderID, askOrder.Price) + } + + if assets.IsZero() { + return fmt.Errorf("cannot fill bid order %d with zero assets from ask order %d", bidOrderID, askOrderID) + } + if assets.IsAnyNegative() { + return fmt.Errorf("cannot fill bid order %d with negative assets %q from ask order %d", + bidOrderID, assets, askOrderID) + } + // TODO[1658]: This should be checked against the assets left to fill in the ask order. + _, hasNeg := askOrder.Assets.SafeSub(assets...) + if hasNeg { + return fmt.Errorf("cannot fill bid order %d with assets %q from ask order %d: insufficient assets %q in ask order", + bidOrderID, assets, askOrderID, askOrder.Assets) + } + + split := &OrderSplit{ + Order: order, + Assets: assets, + } + + if CoinsEquals(assets, bidOrder.Assets) { + split.Price = bidOrder.Price + } else { + if len(assets) != 1 || len(bidOrder.Assets) != 1 { + return fmt.Errorf("cannot prorate bid order %d assets %q for ask order %d with assets %q: multiple asset denoms", + bidOrderID, assets, askOrderID, askOrder.Assets) + } + // Note, we truncate the division here. + // Later, after all fulfillments have been identified, we will handle the remainders. + priceAmt := bidOrder.Price.Amount.Mul(bidOrder.Assets[0].Amount).Quo(assets[0].Amount) + split.Price = sdk.NewCoin(f.PriceLeft.Denom, priceAmt) + } + newPriceLeftAmt := f.PriceLeft.Amount.Sub(split.Price.Amount) + if newPriceLeftAmt.IsNegative() { + return fmt.Errorf("cannot fill bid order %d having assets %q with assets %q for ask order %d: "+ + "price left %q is less than price needed %q", + bidOrderID, bidOrder.Assets, assets, askOrderID, f.PriceLeft, split.Price) + } + + f.Splits = append(f.Splits, split) + f.TotalAssets = f.TotalAssets.Add(assets...) + f.PriceLeft.Amount = newPriceLeftAmt + return nil +} diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go new file mode 100644 index 0000000000..66afe0aef9 --- /dev/null +++ b/x/exchange/fulfillment_test.go @@ -0,0 +1,3 @@ +package exchange + +// TODO[1658]: Unit tests on the fulfillment stuff. diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 2636f0894d..672ad02431 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -5,7 +5,6 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) // Define the type strings and bytes to use for each order type. @@ -325,140 +324,3 @@ func (b BidOrder) Validate() error { return errors.Join(errs...) } - -// IndexedAddrAmts is a set of addresses and amounts. -type IndexedAddrAmts struct { - addrs []string - amts []sdk.Coins - indexes map[string]int -} - -func NewIndexedAddrAmts() *IndexedAddrAmts { - return &IndexedAddrAmts{ - indexes: make(map[string]int), - } -} - -// Add adds the coins to the input with the given address (creating it if needed). -func (i *IndexedAddrAmts) Add(addr string, coins sdk.Coins) { - n, known := i.indexes[addr] - if !known { - n = len(i.addrs) - i.indexes[addr] = n - i.addrs = append(i.addrs, addr) - i.amts = append(i.amts, sdk.NewCoins()) - } - i.amts[n] = i.amts[n].Add(coins...) -} - -// GetInputs returns all the entries as bank Inputs. -func (i *IndexedAddrAmts) GetInputs() []banktypes.Input { - rv := make([]banktypes.Input, len(i.addrs)) - for n, addr := range i.addrs { - rv[n] = banktypes.Input{Address: addr, Coins: i.amts[n]} - } - return rv -} - -// GetOutputs returns all the entries as bank Outputs. -func (i *IndexedAddrAmts) GetOutputs() []banktypes.Output { - rv := make([]banktypes.Output, len(i.addrs)) - for n, addr := range i.addrs { - rv[n] = banktypes.Output{Address: addr, Coins: i.amts[n]} - } - return rv -} - -// OrderSplit contains an order, and the asset and price amounts that should come out of it. -type OrderSplit struct { - Order *Order - Assets sdk.Coins - Price sdk.Coin -} - -// AskOrderFulfillment represents how an ask order is fulfilled. -type AskOrderFulfillment struct { - Order *Order - AssetsLeft sdk.Coins - TotalPrice sdk.Coin - Splits []*OrderSplit -} - -func NewAskOrderFulfillment(order *Order) *AskOrderFulfillment { - if !order.IsAskOrder() { - panic(fmt.Errorf("cannot create AskOrderFulfillment for %s order %d", - order.GetOrderType(), order.OrderId)) - } - askOrder := order.GetAskOrder() - return &AskOrderFulfillment{ - Order: order, - AssetsLeft: askOrder.Assets, - TotalPrice: sdk.NewInt64Coin(askOrder.Price.Denom, 0), - } -} - -// AddSplit applies the given bid order in the amount of assets provided to this ask order. -func (f *AskOrderFulfillment) AddSplit(order *Order, assets sdk.Coins) error { - askOrderID := f.Order.OrderId - bidOrderID := order.OrderId - if !order.IsBidOrder() { - return fmt.Errorf("cannot fill ask order %d with %s order %d", - askOrderID, order.GetOrderType(), bidOrderID) - } - - askOrder := f.Order.GetAskOrder() - bidOrder := order.GetBidOrder() - - if askOrder.Price.Denom != bidOrder.Price.Denom { - return fmt.Errorf("cannot fill ask order %d having price %q with bid order %d having price %q: denom mismatch", - askOrderID, askOrder.Price, bidOrderID, bidOrder.Price) - } - - if assets.IsZero() { - return fmt.Errorf("cannot fill ask order %d with zero assets from bid order %d", askOrderID, bidOrderID) - } - if assets.IsAnyNegative() { - return fmt.Errorf("cannot fill ask order %d with negative assets %q from bid order %d", - askOrderID, assets, bidOrderID) - } - // TODO[1658]: This should be checked against the assets left to fill in the bid order. - _, hasNeg := bidOrder.Assets.SafeSub(assets...) - if hasNeg { - return fmt.Errorf("cannot fill ask order %d with assets %q from bid order %d: insufficient assets %q in bid order", - askOrderID, assets, bidOrderID, bidOrder.Assets) - } - if len(bidOrder.Assets) > 1 && !CoinsEquals(bidOrder.Assets, assets) { - return fmt.Errorf("cannot fill ask order %d with assets %q from bid order %d having assets %q: "+ - "unable to divide price for multiple asset types", - askOrderID, assets, bidOrderID, bidOrder.Assets) - } - - newAssetsLeft, hasNeg := f.AssetsLeft.SafeSub(assets...) - if hasNeg { - return fmt.Errorf("cannot fill ask order %d having %q left with assets %q from bid order %d", - askOrderID, f.AssetsLeft, assets, bidOrderID) - } - - split := &OrderSplit{ - Order: order, - Assets: assets, - } - - if CoinsEquals(assets, bidOrder.Assets) { - split.Price = bidOrder.Price - } else { - if len(assets) != 1 || len(bidOrder.Assets) != 1 { - return fmt.Errorf("cannot prorate bid order %d assets %q for ask order %d with assets %q: multiple asset denoms", - order.OrderId, assets, f.Order.OrderId, askOrder.Assets) - } - // Note, we truncate the division here. - // Later, after all fulfillments have been identified, we will handle the remainders. - priceAmt := bidOrder.Price.Amount.Mul(askOrder.Assets[0].Amount).Quo(assets[0].Amount) - split.Price = sdk.NewCoin(bidOrder.Price.Denom, priceAmt) - } - - f.Splits = append(f.Splits, split) - f.AssetsLeft = newAssetsLeft - f.TotalPrice = f.TotalPrice.Add(split.Price) - return nil -} diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 2830fa74b4..26690fe856 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -1275,5 +1275,3 @@ func TestBidOrder_Validate(t *testing.T) { }) } } - -// TODO[1658]: Unit tests on the fulfillment stuff. From 538423b12dbc4f76919bc6378c59bcd772249238 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 21 Sep 2023 11:20:21 -0600 Subject: [PATCH 159/309] [1658]: Don't generate getters for Order, AskOrder, and BidOrder messages. --- proto/provenance/exchange/v1/orders.proto | 6 + x/exchange/orders.pb.go | 160 +++++----------------- 2 files changed, 41 insertions(+), 125 deletions(-) diff --git a/proto/provenance/exchange/v1/orders.proto b/proto/provenance/exchange/v1/orders.proto index a53d4d7606..31ba59b9cd 100644 --- a/proto/provenance/exchange/v1/orders.proto +++ b/proto/provenance/exchange/v1/orders.proto @@ -12,6 +12,8 @@ import "gogoproto/gogo.proto"; // Order associates an order id with one of the order types. message Order { + option (gogoproto.goproto_getters) = false; + // order_id is the numerical identifier for this order. uint64 order_id = 1; // order is the specifics of this order. @@ -25,6 +27,8 @@ message Order { // AskOrder represents someone's desire to sell something at a minimum price. message AskOrder { + option (gogoproto.goproto_getters) = false; + // market_id identifies the market that this order belongs to. uint32 market_id = 1; // seller is the address of the account that owns this order and has the assets to sell. @@ -48,6 +52,8 @@ message AskOrder { // BidOrder represents someone's desire to buy something at a specific price. message BidOrder { + option (gogoproto.goproto_getters) = false; + // market_id identifies the market that this order belongs to. uint32 market_id = 1; // buyer is the address of the account that owns this order and has the price to spend. diff --git a/x/exchange/orders.pb.go b/x/exchange/orders.pb.go index e01dfc4d42..053783fc93 100644 --- a/x/exchange/orders.pb.go +++ b/x/exchange/orders.pb.go @@ -94,13 +94,6 @@ func (m *Order) GetOrder() isOrder_Order { return nil } -func (m *Order) GetOrderId() uint64 { - if m != nil { - return m.OrderId - } - return 0 -} - func (m *Order) GetAskOrder() *AskOrder { if x, ok := m.GetOrder().(*Order_AskOrder); ok { return x.AskOrder @@ -178,48 +171,6 @@ func (m *AskOrder) XXX_DiscardUnknown() { var xxx_messageInfo_AskOrder proto.InternalMessageInfo -func (m *AskOrder) GetMarketId() uint32 { - if m != nil { - return m.MarketId - } - return 0 -} - -func (m *AskOrder) GetSeller() string { - if m != nil { - return m.Seller - } - return "" -} - -func (m *AskOrder) GetAssets() github_com_cosmos_cosmos_sdk_types.Coins { - if m != nil { - return m.Assets - } - return nil -} - -func (m *AskOrder) GetPrice() types.Coin { - if m != nil { - return m.Price - } - return types.Coin{} -} - -func (m *AskOrder) GetSellerSettlementFlatFee() *types.Coin { - if m != nil { - return m.SellerSettlementFlatFee - } - return nil -} - -func (m *AskOrder) GetAllowPartial() bool { - if m != nil { - return m.AllowPartial - } - return false -} - // BidOrder represents someone's desire to buy something at a specific price. type BidOrder struct { // market_id identifies the market that this order belongs to. @@ -272,48 +223,6 @@ func (m *BidOrder) XXX_DiscardUnknown() { var xxx_messageInfo_BidOrder proto.InternalMessageInfo -func (m *BidOrder) GetMarketId() uint32 { - if m != nil { - return m.MarketId - } - return 0 -} - -func (m *BidOrder) GetBuyer() string { - if m != nil { - return m.Buyer - } - return "" -} - -func (m *BidOrder) GetAssets() github_com_cosmos_cosmos_sdk_types.Coins { - if m != nil { - return m.Assets - } - return nil -} - -func (m *BidOrder) GetPrice() types.Coin { - if m != nil { - return m.Price - } - return types.Coin{} -} - -func (m *BidOrder) GetBuyerSettlementFees() github_com_cosmos_cosmos_sdk_types.Coins { - if m != nil { - return m.BuyerSettlementFees - } - return nil -} - -func (m *BidOrder) GetAllowPartial() bool { - if m != nil { - return m.AllowPartial - } - return false -} - func init() { proto.RegisterType((*Order)(nil), "provenance.exchange.v1.Order") proto.RegisterType((*AskOrder)(nil), "provenance.exchange.v1.AskOrder") @@ -325,40 +234,41 @@ func init() { } var fileDescriptor_dab7cbe63f582471 = []byte{ - // 521 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xb1, 0x6e, 0xd3, 0x40, - 0x18, 0x8e, 0x9b, 0x38, 0x75, 0xae, 0x74, 0x31, 0x05, 0x9c, 0x20, 0xb9, 0x51, 0xba, 0x78, 0xc9, - 0xb9, 0x29, 0x62, 0x46, 0x35, 0x52, 0x45, 0x27, 0x2a, 0x57, 0x62, 0x60, 0xb1, 0xce, 0xf6, 0x5f, - 0xf7, 0x14, 0xc7, 0x67, 0xf9, 0xae, 0xa1, 0x9d, 0x78, 0x05, 0x56, 0x5e, 0x01, 0x89, 0x8d, 0x87, - 0xe8, 0x58, 0x31, 0x31, 0x01, 0x4a, 0xde, 0x80, 0x27, 0x40, 0xbe, 0xbb, 0xa4, 0x41, 0x82, 0xd0, - 0x81, 0x81, 0xc9, 0xf7, 0xff, 0xf7, 0x7d, 0xdf, 0xff, 0xe9, 0xfb, 0xe5, 0x43, 0x7b, 0x65, 0xc5, - 0xa6, 0x50, 0x90, 0x22, 0x01, 0x1f, 0x2e, 0x93, 0x73, 0x52, 0x64, 0xe0, 0x4f, 0x47, 0x3e, 0xab, - 0x52, 0xa8, 0x38, 0x2e, 0x2b, 0x26, 0x98, 0xfd, 0xf0, 0x16, 0x84, 0x17, 0x20, 0x3c, 0x1d, 0xf5, - 0xdc, 0x84, 0xf1, 0x09, 0xe3, 0x7e, 0x4c, 0x78, 0x4d, 0x8a, 0x41, 0x90, 0x91, 0x9f, 0x30, 0x5a, - 0x28, 0x5e, 0xaf, 0xab, 0xee, 0x23, 0x59, 0xf9, 0xaa, 0xd0, 0x57, 0x3b, 0x19, 0xcb, 0x98, 0xea, - 0xd7, 0x27, 0xd5, 0x1d, 0x7c, 0x34, 0x90, 0xf9, 0xb2, 0x9e, 0x6c, 0x77, 0x91, 0x25, 0x2d, 0x44, - 0x34, 0x75, 0x8c, 0xbe, 0xe1, 0xb5, 0xc2, 0x4d, 0x59, 0x1f, 0xa7, 0xf6, 0x33, 0xd4, 0x21, 0x7c, - 0x1c, 0xc9, 0xd2, 0xd9, 0xe8, 0x1b, 0xde, 0xd6, 0x41, 0x1f, 0xff, 0xde, 0x21, 0x3e, 0xe4, 0x63, - 0xa9, 0xf7, 0xa2, 0x11, 0x5a, 0x44, 0x9f, 0x6b, 0x81, 0x98, 0xa6, 0x5a, 0xa0, 0xb9, 0x5e, 0x20, - 0xa0, 0xe9, 0x52, 0x20, 0xd6, 0xe7, 0x60, 0x13, 0x99, 0x92, 0x3c, 0xf8, 0xb1, 0x81, 0xac, 0xc5, - 0x08, 0xfb, 0x31, 0xea, 0x4c, 0x48, 0x35, 0x06, 0xb1, 0xf0, 0xbc, 0x1d, 0x5a, 0xaa, 0x71, 0x9c, - 0xda, 0xfb, 0xa8, 0xcd, 0x21, 0xcf, 0xb5, 0xe3, 0x4e, 0xe0, 0x7c, 0xfe, 0x34, 0xdc, 0xd1, 0x89, - 0x1c, 0xa6, 0x69, 0x05, 0x9c, 0x9f, 0x8a, 0x8a, 0x16, 0x59, 0xa8, 0x71, 0x76, 0x82, 0xda, 0x84, - 0x73, 0x10, 0xdc, 0x69, 0xf6, 0x9b, 0xde, 0xd6, 0x41, 0x17, 0x6b, 0x78, 0x9d, 0x36, 0xd6, 0x69, - 0xe3, 0xe7, 0x8c, 0x16, 0xc1, 0xfe, 0xf5, 0xd7, 0xdd, 0xc6, 0x87, 0x6f, 0xbb, 0x5e, 0x46, 0xc5, - 0xf9, 0x45, 0x8c, 0x13, 0x36, 0xd1, 0x69, 0xeb, 0xcf, 0x90, 0xa7, 0x63, 0x5f, 0x5c, 0x95, 0xc0, - 0x25, 0x81, 0x87, 0x5a, 0xda, 0x7e, 0x8a, 0xcc, 0xb2, 0xa2, 0x09, 0x38, 0x2d, 0x19, 0xc3, 0x9a, - 0x19, 0xad, 0x7a, 0x46, 0xa8, 0xd0, 0xf6, 0x2b, 0xd4, 0x53, 0x2e, 0x23, 0x0e, 0x42, 0xe4, 0x30, - 0x81, 0x42, 0x44, 0x67, 0x39, 0x11, 0xd1, 0x19, 0x80, 0x63, 0xfe, 0x45, 0x2b, 0x7c, 0xa4, 0xc8, - 0xa7, 0x4b, 0xee, 0x51, 0x4e, 0xc4, 0x11, 0x80, 0xbd, 0x87, 0xb6, 0x49, 0x9e, 0xb3, 0x37, 0x51, - 0x49, 0x2a, 0x41, 0x49, 0xee, 0xb4, 0xfb, 0x86, 0x67, 0x85, 0xf7, 0x64, 0xf3, 0x44, 0xf5, 0x06, - 0xef, 0x9b, 0xc8, 0x5a, 0xac, 0x65, 0x7d, 0xe8, 0x18, 0x99, 0xf1, 0xc5, 0xd5, 0x1d, 0x32, 0x57, - 0xb0, 0xff, 0x3a, 0xf2, 0xb7, 0xe8, 0x81, 0x34, 0xf9, 0x4b, 0xe2, 0x00, 0xdc, 0x31, 0xff, 0xbd, - 0xd5, 0xfb, 0x72, 0xd2, 0xca, 0x7a, 0x00, 0xf8, 0x9d, 0x76, 0x13, 0xc0, 0xf5, 0xcc, 0x35, 0x6e, - 0x66, 0xae, 0xf1, 0x7d, 0xe6, 0x1a, 0xef, 0xe6, 0x6e, 0xe3, 0x66, 0xee, 0x36, 0xbe, 0xcc, 0xdd, - 0x06, 0xea, 0x52, 0xf6, 0x87, 0x7f, 0xec, 0xc4, 0x78, 0x8d, 0x57, 0xac, 0xdd, 0x82, 0x86, 0x94, - 0xad, 0x54, 0xfe, 0xe5, 0xf2, 0x81, 0x8a, 0xdb, 0xf2, 0xb9, 0x78, 0xf2, 0x33, 0x00, 0x00, 0xff, - 0xff, 0x9b, 0xd7, 0x7f, 0xac, 0xbe, 0x04, 0x00, 0x00, + // 532 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xb1, 0x6f, 0xd3, 0x4e, + 0x14, 0xb6, 0x9b, 0x38, 0x75, 0xae, 0xbf, 0x2e, 0xfe, 0x15, 0x70, 0x82, 0xe4, 0x44, 0xe9, 0x92, + 0x25, 0xe7, 0xa6, 0x88, 0x85, 0x05, 0x35, 0x48, 0x15, 0x9d, 0xa8, 0x5c, 0x89, 0x81, 0xc5, 0x3a, + 0xdb, 0xaf, 0xee, 0x29, 0x8e, 0xcf, 0xf2, 0x5d, 0x43, 0x3b, 0xb1, 0x32, 0xf2, 0x1f, 0xc0, 0xcc, + 0x0a, 0x7f, 0x44, 0xc7, 0x8a, 0x89, 0x09, 0x50, 0xf2, 0x4f, 0x30, 0x22, 0xdf, 0x5d, 0xd2, 0x20, + 0x41, 0x00, 0x89, 0x81, 0xc9, 0xf7, 0xde, 0x7d, 0xef, 0xfb, 0x3e, 0x7d, 0x4f, 0x3e, 0xb4, 0x5b, + 0x94, 0x6c, 0x0a, 0x39, 0xc9, 0x63, 0xf0, 0xe1, 0x22, 0x3e, 0x23, 0x79, 0x0a, 0xfe, 0x74, 0xe8, + 0xb3, 0x32, 0x81, 0x92, 0xe3, 0xa2, 0x64, 0x82, 0x39, 0xb7, 0x6f, 0x40, 0x78, 0x01, 0xc2, 0xd3, + 0x61, 0xdb, 0x8b, 0x19, 0x9f, 0x30, 0xee, 0x47, 0x84, 0x57, 0x43, 0x11, 0x08, 0x32, 0xf4, 0x63, + 0x46, 0x73, 0x35, 0xd7, 0x6e, 0xa9, 0xfb, 0x50, 0x56, 0xbe, 0x2a, 0xf4, 0xd5, 0x4e, 0xca, 0x52, + 0xa6, 0xfa, 0xd5, 0x49, 0x75, 0x7b, 0xef, 0x4c, 0x64, 0x3d, 0xa9, 0x94, 0x9d, 0x16, 0xb2, 0xa5, + 0x85, 0x90, 0x26, 0xae, 0xd9, 0x35, 0xfb, 0xf5, 0x60, 0x53, 0xd6, 0x47, 0x89, 0xf3, 0x10, 0x35, + 0x09, 0x1f, 0x87, 0xb2, 0x74, 0x37, 0xba, 0x66, 0x7f, 0x6b, 0xbf, 0x8b, 0x7f, 0xec, 0x10, 0x1f, + 0xf0, 0xb1, 0xe4, 0x7b, 0x6c, 0x04, 0x36, 0xd1, 0xe7, 0x8a, 0x20, 0xa2, 0x89, 0x26, 0xa8, 0xad, + 0x27, 0x18, 0xd1, 0x64, 0x49, 0x10, 0xe9, 0xf3, 0x83, 0xfa, 0xcb, 0x37, 0x1d, 0x63, 0xb4, 0x89, + 0x2c, 0x49, 0xd1, 0xfb, 0xba, 0x81, 0xec, 0x85, 0x90, 0x73, 0x17, 0x35, 0x27, 0xa4, 0x1c, 0x83, + 0x58, 0x38, 0xdf, 0x0e, 0x6c, 0xd5, 0x38, 0x4a, 0x9c, 0x3d, 0xd4, 0xe0, 0x90, 0x65, 0xda, 0x77, + 0x73, 0xe4, 0x7e, 0x78, 0x3f, 0xd8, 0xd1, 0xb9, 0x1c, 0x24, 0x49, 0x09, 0x9c, 0x9f, 0x88, 0x92, + 0xe6, 0x69, 0xa0, 0x71, 0x4e, 0x8c, 0x1a, 0x84, 0x73, 0x10, 0xdc, 0xad, 0x75, 0x6b, 0xfd, 0xad, + 0xfd, 0x16, 0xd6, 0xf0, 0x2a, 0x73, 0xac, 0x33, 0xc7, 0x8f, 0x18, 0xcd, 0x47, 0x7b, 0x57, 0x9f, + 0x3a, 0xc6, 0xdb, 0xcf, 0x9d, 0x7e, 0x4a, 0xc5, 0xd9, 0x79, 0x84, 0x63, 0x36, 0xd1, 0x99, 0xeb, + 0xcf, 0x80, 0x27, 0x63, 0x5f, 0x5c, 0x16, 0xc0, 0xe5, 0x00, 0x0f, 0x34, 0xb5, 0x73, 0x1f, 0x59, + 0x45, 0x49, 0x63, 0x70, 0xeb, 0x32, 0x8c, 0x35, 0x1a, 0xf5, 0x4a, 0x23, 0x50, 0x68, 0xe7, 0x29, + 0x6a, 0x2b, 0x97, 0x21, 0x07, 0x21, 0x32, 0x98, 0x40, 0x2e, 0xc2, 0xd3, 0x8c, 0x88, 0xf0, 0x14, + 0xc0, 0xb5, 0x7e, 0xc1, 0x15, 0xdc, 0x51, 0xc3, 0x27, 0xcb, 0xd9, 0xc3, 0x8c, 0x88, 0x43, 0x00, + 0x67, 0x17, 0x6d, 0x93, 0x2c, 0x63, 0xcf, 0xc3, 0x82, 0x94, 0x82, 0x92, 0xcc, 0x6d, 0x74, 0xcd, + 0xbe, 0x1d, 0xfc, 0x27, 0x9b, 0xc7, 0xaa, 0xa7, 0x76, 0xd0, 0x7b, 0x5d, 0x43, 0xf6, 0x62, 0x45, + 0xeb, 0xa3, 0xc7, 0xc8, 0x8a, 0xce, 0x2f, 0x7f, 0x23, 0x79, 0x05, 0xfb, 0xa7, 0x83, 0x7f, 0x81, + 0x6e, 0x49, 0x93, 0xdf, 0xe5, 0x0e, 0xc0, 0x5d, 0xeb, 0xef, 0x5b, 0xfd, 0x5f, 0x2a, 0xad, 0x2c, + 0x09, 0x80, 0xff, 0xc1, 0x86, 0x46, 0x70, 0x35, 0xf3, 0xcc, 0xeb, 0x99, 0x67, 0x7e, 0x99, 0x79, + 0xe6, 0xab, 0xb9, 0x67, 0x5c, 0xcf, 0x3d, 0xe3, 0xe3, 0xdc, 0x33, 0x50, 0x8b, 0xb2, 0x9f, 0xfc, + 0x75, 0xc7, 0xe6, 0x33, 0xbc, 0x62, 0xf0, 0x06, 0x34, 0xa0, 0x6c, 0xa5, 0xf2, 0x2f, 0x96, 0x4f, + 0x56, 0xd4, 0x90, 0x0f, 0xc8, 0xbd, 0x6f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x21, 0x5d, 0xcb, 0xfa, + 0xd0, 0x04, 0x00, 0x00, } func (m *Order) Marshal() (dAtA []byte, err error) { From d994dd398a71e1ee8f1b73b9596bf12c8af1f212 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 21 Sep 2023 13:37:08 -0600 Subject: [PATCH 160/309] [1658]: Create SubOrderI and OrderI interfaces and implement all the methods for AskOrder, BidOrder, and Order. --- x/exchange/events.go | 2 +- x/exchange/keeper/orders.go | 4 +- x/exchange/orders.go | 263 ++++++++----- x/exchange/orders_test.go | 737 ++++++++++++++++++++++++++++++++---- 4 files changed, 838 insertions(+), 168 deletions(-) diff --git a/x/exchange/events.go b/x/exchange/events.go index 14b91bb81c..d4bef3a380 100644 --- a/x/exchange/events.go +++ b/x/exchange/events.go @@ -8,7 +8,7 @@ import ( func NewEventOrderCreated(order *Order) *EventOrderCreated { return &EventOrderCreated{ - OrderId: order.GetOrderId(), + OrderId: order.GetOrderID(), OrderType: order.GetOrderType(), } } diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 26f86a0410..c91248f0ea 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -77,7 +77,7 @@ func (k Keeper) parseOrderStoreValue(orderID uint64, value []byte) (*exchange.Or // createIndexEntries creates all the key/value index entries for an order. func createIndexEntries(order exchange.Order) []sdk.KVPair { marketID := order.GetMarketID() - orderID := order.GetOrderId() + orderID := order.GetOrderID() orderTypeByte := order.GetOrderTypeByte() owner := order.GetOwner() addr := sdk.MustAccAddressFromBech32(owner) @@ -119,7 +119,7 @@ func (k Keeper) getOrderFromStore(store sdk.KVStore, orderID uint64) (*exchange. func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { key, value, err := k.getOrderStoreKeyValue(order) if err != nil { - return fmt.Errorf("failed to create order %d store key/value: %w", order.GetOrderId(), err) + return fmt.Errorf("failed to create order %d store key/value: %w", order.GetOrderID(), err) } isUpdate := store.Has(key) store.Set(key, value) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 672ad02431..6cd8ad39c9 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -17,6 +17,37 @@ const ( OrderTypeByteBid = byte(0x01) ) +// SubOrderI is an interface with getters for the fields in a sub-order (i.e. AskOrder or BidOrder). +type SubOrderI interface { + GetMarketID() uint32 + GetOwner() string + GetAssets() sdk.Coins + GetPrice() sdk.Coin + GetSettlementFees() sdk.Coins + PartialFillAllowed() bool + GetOrderType() string + GetOrderTypeByte() byte + GetHoldAmount() sdk.Coins + Validate() error +} + +var ( + _ SubOrderI = (*AskOrder)(nil) + _ SubOrderI = (*BidOrder)(nil) +) + +// OrderI is an interface with getters for all the fields associated with an order and it's sub-order. +type OrderI interface { + SubOrderI + GetOrderID() uint64 + IsAskOrder() bool + IsBidOrder() bool +} + +var ( + _ OrderI = (*Order)(nil) +) + // findDuplicateIds returns all order ids that appear two or more times in the provided slice. func findDuplicateIds(orderIDs []uint64) []uint64 { var rv []uint64 @@ -76,108 +107,88 @@ func (o Order) IsBidOrder() bool { return o.GetBidOrder() != nil } -// GetOrderType returns a string indicating what type this order is. -// See: OrderTypeAsk, OrderTypeBid -// Panics if the order details are not set or are something unexpected. -func (o Order) GetOrderType() string { +// GetOrderID gets the numerical identifier for this order. +func (o Order) GetOrderID() uint64 { + return o.OrderId +} + +// GetSubOrder gets this order's sub-order as a SubOrderI. +func (o Order) GetSubOrder() (SubOrderI, error) { switch v := o.GetOrder().(type) { case *Order_AskOrder: - return OrderTypeAsk + return v.AskOrder, nil case *Order_BidOrder: - return OrderTypeBid + return v.BidOrder, nil default: - // If GetOrderType() is called without the order being set yet, it's a programming error, so panic. - // If it's a type without a case, the case needs to be added, so panic. - panic(fmt.Sprintf("GetOrderType() missing case for %T", v)) + // If this is called without the sub-order being set yet, it's a programming error, so panic. + // If it's a type that doesn't implement SubOrderI, that needs to be done, so panic. + return nil, fmt.Errorf("unknown sub-order type %T: does not implement SubOrderI", v) } } -// GetOrderTypeByte returns the type byte for this order. -// See: OrderTypeByteAsk, OrderTypeByteBid -// Panics if the order details are not set or are something unexpected. -func (o Order) GetOrderTypeByte() byte { - switch v := o.GetOrder().(type) { - case *Order_AskOrder: - return OrderTypeByteAsk - case *Order_BidOrder: - return OrderTypeByteBid - default: - // If GetOrderTypeByte() is called without the order being set yet, it's a programming error, so panic. - // If it's a type without a case, the case needs to be added, so panic. - panic(fmt.Sprintf("GetOrderTypeByte() missing case for %T", v)) +// MustGetSubOrder gets this order's sub-order as a SubOrderI. +// Panics if the sub-order is not set or is something unexpected. +func (o Order) MustGetSubOrder() SubOrderI { + rv, err := o.GetSubOrder() + if err != nil { + panic(err) } + return rv } // GetMarketID returns the market id for this order. -// Panics if the order details are not set or are something unexpected. +// Panics if the sub-order is not set or is something unexpected. func (o Order) GetMarketID() uint32 { - switch v := o.GetOrder().(type) { - case *Order_AskOrder: - return v.AskOrder.MarketId - case *Order_BidOrder: - return v.BidOrder.MarketId - default: - // If GetMarketID() is called without the order being set yet, it's a programming error, so panic. - // If it's a type without a case, the case needs to be added, so panic. - panic(fmt.Sprintf("GetMarketID() missing case for %T", v)) - } + return o.MustGetSubOrder().GetMarketID() } -// GetOwner gets the address of the owner of this order. +// GetOwner returns the owner of this order. // E.g. the seller for ask orders, or buyer for bid orders. +// Panics if the sub-order is not set or is something unexpected. func (o Order) GetOwner() string { - switch v := o.GetOrder().(type) { - case *Order_AskOrder: - return v.AskOrder.Seller - case *Order_BidOrder: - return v.BidOrder.Buyer - default: - // If GetOwner() is called without the order being set yet, it's a programming error, so panic. - // If it's a type without a case, the case needs to be added, so panic. - panic(fmt.Sprintf("GetOwner() missing case for %T", v)) - } + return o.MustGetSubOrder().GetOwner() } -// GetAssets gets the assets in this order. +// GetAssets returns the assets for this order. +// Panics if the sub-order is not set or is something unexpected. func (o Order) GetAssets() sdk.Coins { - switch v := o.GetOrder().(type) { - case *Order_AskOrder: - return v.AskOrder.Assets - case *Order_BidOrder: - return v.BidOrder.Assets - default: - // If GetAssets() is called without the order being set yet, it's a programming error, so panic. - // If it's a type without a case, the case needs to be added, so panic. - panic(fmt.Sprintf("GetAssets() missing case for %T", v)) - } + return o.MustGetSubOrder().GetAssets() } -// GetPrice gets the price in this order. +// GetPrice returns the price for this order. +// Panics if the sub-order is not set or is something unexpected. func (o Order) GetPrice() sdk.Coin { - switch v := o.GetOrder().(type) { - case *Order_AskOrder: - return v.AskOrder.Price - case *Order_BidOrder: - return v.BidOrder.Price - default: - // If GetPrice() is called without the order being set yet, it's a programming error, so panic. - // If it's a type without a case, the case needs to be added, so panic. - panic(fmt.Sprintf("GetPrice() missing case for %T", v)) - } + return o.MustGetSubOrder().GetPrice() +} + +// GetSettlementFees returns the settlement fees in this order. +func (o Order) GetSettlementFees() sdk.Coins { + return o.MustGetSubOrder().GetSettlementFees() +} + +// PartialFillAllowed returns true if this order allows partial fulfillment. +func (o Order) PartialFillAllowed() bool { + return o.MustGetSubOrder().PartialFillAllowed() +} + +// GetOrderType returns a string indicating what type this order is. +// E.g: OrderTypeAsk or OrderTypeBid +// Panics if the sub-order is not set or is something unexpected. +func (o Order) GetOrderType() string { + return o.MustGetSubOrder().GetOrderType() } -// GetHoldAmount returns the total amount that should be on hold for this order. +// GetOrderTypeByte returns the type byte for this order. +// E.g: OrderTypeByteAsk or OrderTypeByteBid +// Panics if the sub-order is not set or is something unexpected. +func (o Order) GetOrderTypeByte() byte { + return o.MustGetSubOrder().GetOrderTypeByte() +} + +// GetHoldAmount returns the amount that should be on hold for this order. +// Panics if the sub-order is not set or is something unexpected. func (o Order) GetHoldAmount() sdk.Coins { - switch v := o.GetOrder().(type) { - case *Order_AskOrder: - return v.AskOrder.GetHoldAmount() - case *Order_BidOrder: - return v.BidOrder.GetHoldAmount() - default: - // If HoldSettlementFee() is called without the order being set yet, it's a programming error, so panic. - // If it's a type without a case, the case needs to be added, so panic. - panic(fmt.Sprintf("GetHoldAmount() missing case for %T", v)) - } + return o.MustGetSubOrder().GetHoldAmount() } // Validate returns an error if anything in this order is invalid. @@ -185,17 +196,57 @@ func (o Order) Validate() error { if o.OrderId == 0 { return errors.New("invalid order id: must not be zero") } - switch v := o.GetOrder().(type) { - case *Order_AskOrder: - return v.AskOrder.Validate() - case *Order_BidOrder: - return v.BidOrder.Validate() - default: - return fmt.Errorf("unknown order type %T", v) + so, err := o.GetSubOrder() + if err != nil { + return err + } + return so.Validate() +} + +// GetMarketID returns the market id for this ask order. +func (a AskOrder) GetMarketID() uint32 { + return a.MarketId +} + +// GetOwner returns the owner of this ask order: the seller. +func (a AskOrder) GetOwner() string { + return a.Seller +} + +// GetAssets returns the assets for sale with this ask order. +func (a AskOrder) GetAssets() sdk.Coins { + return a.Assets +} + +// GetPrice returns the minimum price to accept for this ask order. +func (a AskOrder) GetPrice() sdk.Coin { + return a.Price +} + +// GetSettlementFees returns the seller settlement flat fees in this ask order. +func (a AskOrder) GetSettlementFees() sdk.Coins { + if a.SellerSettlementFlatFee == nil { + return nil } + return sdk.Coins{*a.SellerSettlementFlatFee} +} + +// PartialFillAllowed returns true if this ask order allows partial fulfillment. +func (a AskOrder) PartialFillAllowed() bool { + return a.AllowPartial +} + +// GetOrderType returns the order type string for this ask order: "ask". +func (a AskOrder) GetOrderType() string { + return OrderTypeAsk +} + +// GetOrderTypeByte returns the order type byte for this bid order: 0x00. +func (a AskOrder) GetOrderTypeByte() byte { + return OrderTypeByteAsk } -// GetHoldAmount gets the amount to put on hold for this ask order. +// GetHoldAmount returns the amount that should be on hold for this ask order. func (a AskOrder) GetHoldAmount() sdk.Coins { rv := a.Assets if a.SellerSettlementFlatFee != nil && a.SellerSettlementFlatFee.Denom != a.Price.Denom { @@ -264,7 +315,47 @@ func (a AskOrder) Validate() error { return errors.Join(errs...) } -// GetHoldAmount gets the amount to put on hold for this ask order. +// GetMarketID returns the market id for this bid order. +func (b BidOrder) GetMarketID() uint32 { + return b.MarketId +} + +// GetOwner returns the owner of this bid order: the buyer. +func (b BidOrder) GetOwner() string { + return b.Buyer +} + +// GetAssets returns the assets to buy for this bid order. +func (b BidOrder) GetAssets() sdk.Coins { + return b.Assets +} + +// GetPrice returns the price to pay for this bid order. +func (b BidOrder) GetPrice() sdk.Coin { + return b.Price +} + +// GetSettlementFees returns the buyer settlement fees in this bid order. +func (b BidOrder) GetSettlementFees() sdk.Coins { + return b.BuyerSettlementFees +} + +// PartialFillAllowed returns true if this bid order allows partial fulfillment. +func (b BidOrder) PartialFillAllowed() bool { + return b.AllowPartial +} + +// GetOrderType returns the order type string for this bid order: "bid". +func (b BidOrder) GetOrderType() string { + return OrderTypeBid +} + +// GetOrderTypeByte returns the order type byte for this bid order: 0x01. +func (b BidOrder) GetOrderTypeByte() byte { + return OrderTypeByteBid +} + +// GetHoldAmount returns the amount that should be on hold for this bid order. func (b BidOrder) GetHoldAmount() sdk.Coins { return b.BuyerSettlementFees.Add(b.Price) } diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 26690fe856..f77f256a47 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -51,7 +51,7 @@ func TestOrderTypesAndBytes(t *testing.T) { } ot := func(name string) string { - return "GetOrderType" + name + return "OrderType" + name } otb := func(name string) string { return "OrderTypeByte" + name @@ -287,8 +287,8 @@ func TestOrder_IsAskOrder(t *testing.T) { testFunc := func() { actual = tc.order.IsAskOrder() } - require.NotPanics(t, testFunc, "IsAskOrder") - assert.Equal(t, tc.exp, actual, "IsAskOrder result") + require.NotPanics(t, testFunc, "IsAskOrder()") + assert.Equal(t, tc.exp, actual, "IsAskOrder() result") }) } } @@ -327,92 +327,105 @@ func TestOrder_IsBidOrder(t *testing.T) { testFunc := func() { actual = tc.order.IsBidOrder() } - require.NotPanics(t, testFunc, "IsBidOrder") - assert.Equal(t, tc.exp, actual, "IsBidOrder result") + require.NotPanics(t, testFunc, "IsBidOrder()") + assert.Equal(t, tc.exp, actual, "IsBidOrder() result") }) } } -func TestOrder_GetOrderType(t *testing.T) { +func TestOrder_GetOrderID(t *testing.T) { tests := []struct { - name string - order *Order - expected string - expPanic string + name string + order Order + exp uint64 }{ - { - name: "AskOrder", - order: NewOrder(1).WithAsk(&AskOrder{}), - expected: OrderTypeAsk, - }, - { - name: "BidOrder", - order: NewOrder(2).WithBid(&BidOrder{}), - expected: OrderTypeBid, - }, - { - name: "nil inside order", - order: NewOrder(3), - expPanic: "GetOrderType() missing case for ", - }, - { - name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: "GetOrderType() missing case for *exchange.unknownOrderType", - }, + {name: "zero", order: Order{OrderId: 0}, exp: 0}, + {name: "one", order: Order{OrderId: 1}, exp: 1}, + {name: "twelve", order: Order{OrderId: 12}, exp: 12}, + {name: "max uint32 + 1", order: Order{OrderId: 4_294_967_296}, exp: 4_294_967_296}, + {name: "max uint64", order: Order{OrderId: 18_446_744_073_709_551_615}, exp: 18_446_744_073_709_551_615}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var actual string + var actual uint64 testFunc := func() { - actual = tc.order.GetOrderType() + actual = tc.order.GetOrderID() } - - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOrderType") - assert.Equal(t, tc.expected, actual, "GetOrderType result") + require.NotPanics(t, testFunc, "GetOrderID()") + assert.Equal(t, tc.exp, actual, "GetOrderID() result") }) } } -func TestOrder_GetOrderTypeByte(t *testing.T) { +const ( + nilSubTypeErr = "unknown sub-order type : does not implement SubOrderI" + unknownSubTypeErr = "unknown sub-order type *exchange.unknownOrderType: does not implement SubOrderI" +) + +func TestOrder_GetSubOrder(t *testing.T) { + askOrder := &AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("Seller______________").String(), + Assets: sdk.NewCoins(sdk.NewInt64Coin("assetcoin", 3)), + Price: sdk.NewInt64Coin("paycoin", 8), + SellerSettlementFlatFee: &sdk.Coin{Denom: "feecoin", Amount: sdkmath.NewInt(1)}, + AllowPartial: false, + } + bidOrder := &BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("Buyer_______________").String(), + Assets: sdk.NewCoins(sdk.NewInt64Coin("assetcoin", 33)), + Price: sdk.NewInt64Coin("paycoin", 88), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("feecoin", 11)), + AllowPartial: true, + } + tests := []struct { name string order *Order - expected byte - expPanic string + expected SubOrderI + expErr string }{ { name: "AskOrder", - order: NewOrder(1).WithAsk(&AskOrder{}), - expected: OrderTypeByteAsk, + order: NewOrder(1).WithAsk(askOrder), + expected: askOrder, }, { name: "BidOrder", - order: NewOrder(2).WithBid(&BidOrder{}), - expected: OrderTypeByteBid, + order: NewOrder(2).WithBid(bidOrder), + expected: bidOrder, }, { - name: "nil inside order", - order: NewOrder(3), - expPanic: "GetOrderTypeByte() missing case for ", + name: "nil sub-order", + order: NewOrder(3), + expErr: nilSubTypeErr, }, { - name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: "GetOrderTypeByte() missing case for *exchange.unknownOrderType", + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + expErr: unknownSubTypeErr, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var actual byte - testFunc := func() { - actual = tc.order.GetOrderTypeByte() + var getActual SubOrderI + var err error + testGetSubOrder := func() { + getActual, err = tc.order.GetSubOrder() } + require.NotPanics(t, testGetSubOrder, "GetSubOrder()") + assertions.AssertErrorValue(t, err, tc.expErr, "GetSubOrder() error") + assert.Equal(t, tc.expected, getActual, "GetSubOrder() result") - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOrderTypeByte") - assert.Equal(t, tc.expected, actual, "GetOrderTypeByte result") + var mustActual SubOrderI + testMustGetSubOrder := func() { + mustActual = tc.order.MustGetSubOrder() + } + assertions.RequirePanicEquals(t, testMustGetSubOrder, tc.expErr, "MustGetSubOrder()") + assert.Equal(t, tc.expected, mustActual, "MustGetSubOrder() result") }) } } @@ -437,12 +450,12 @@ func TestOrder_GetMarketID(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: "GetMarketID() missing case for ", + expPanic: nilSubTypeErr, }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: "GetMarketID() missing case for *exchange.unknownOrderType", + expPanic: unknownSubTypeErr, }, } @@ -452,9 +465,8 @@ func TestOrder_GetMarketID(t *testing.T) { testFunc := func() { actual = tc.order.GetMarketID() } - - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetMarketID") - assert.Equal(t, tc.expected, actual, "GetMarketID result") + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetMarketID()") + assert.Equal(t, tc.expected, actual, "GetMarketID() result") }) } } @@ -479,12 +491,12 @@ func TestOrder_GetOwner(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: "GetOwner() missing case for ", + expPanic: nilSubTypeErr, }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: "GetOwner() missing case for *exchange.unknownOrderType", + expPanic: unknownSubTypeErr, }, } @@ -494,8 +506,8 @@ func TestOrder_GetOwner(t *testing.T) { testFunc := func() { owner = tc.order.GetOwner() } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOwner") - assert.Equal(t, tc.expected, owner, "GetOwner result") + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOwner()") + assert.Equal(t, tc.expected, owner, "GetOwner() result") }) } } @@ -520,12 +532,12 @@ func TestOrder_GetAssets(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: "GetAssets() missing case for ", + expPanic: nilSubTypeErr, }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: "GetAssets() missing case for *exchange.unknownOrderType", + expPanic: unknownSubTypeErr, }, } @@ -535,8 +547,8 @@ func TestOrder_GetAssets(t *testing.T) { testFunc := func() { assets = tc.order.GetAssets() } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetAssets") - assert.Equal(t, tc.expected.String(), assets.String(), "GetAssets result") + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetAssets()") + assert.Equal(t, tc.expected.String(), assets.String(), "GetAssets() result") }) } } @@ -561,12 +573,12 @@ func TestOrder_GetPrice(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: "GetPrice() missing case for ", + expPanic: nilSubTypeErr, }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: "GetPrice() missing case for *exchange.unknownOrderType", + expPanic: unknownSubTypeErr, }, } @@ -576,8 +588,172 @@ func TestOrder_GetPrice(t *testing.T) { testFunc := func() { price = tc.order.GetPrice() } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetPrice") - assert.Equal(t, tc.expected.String(), price.String(), "GetPrice result") + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetPrice()") + assert.Equal(t, tc.expected.String(), price.String(), "GetPrice() result") + }) + } +} + +func TestOrder_GetSettlementFees(t *testing.T) { + tests := []struct { + name string + order *Order + expected sdk.Coins + expPanic string + }{ + { + name: "AskOrder", + order: NewOrder(1).WithAsk(&AskOrder{SellerSettlementFlatFee: &sdk.Coin{Denom: "askcoin", Amount: sdkmath.NewInt(3)}}), + expected: sdk.NewCoins(sdk.NewInt64Coin("askcoin", 3)), + }, + { + name: "BidOrder", + order: NewOrder(2).WithBid(&BidOrder{BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("bidcoin", 15))}), + expected: sdk.NewCoins(sdk.NewInt64Coin("bidcoin", 15)), + }, + { + name: "nil inside order", + order: NewOrder(3), + expPanic: nilSubTypeErr, + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + expPanic: unknownSubTypeErr, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coins + testFunc := func() { + actual = tc.order.GetSettlementFees() + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetSettlementFees()") + assert.Equal(t, tc.expected.String(), actual.String(), "GetSettlementFees() result") + }) + } +} + +func TestOrder_PartialFillAllowed(t *testing.T) { + tests := []struct { + name string + order *Order + expected bool + expPanic string + }{ + { + name: "AskOrder", + order: NewOrder(1).WithAsk(&AskOrder{AllowPartial: true}), + expected: true, + }, + { + name: "BidOrder", + order: NewOrder(2).WithBid(&BidOrder{AllowPartial: true}), + expected: true, + }, + { + name: "nil inside order", + order: NewOrder(3), + expPanic: nilSubTypeErr, + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + expPanic: unknownSubTypeErr, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.order.PartialFillAllowed() + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "PartialFillAllowed()") + assert.Equal(t, tc.expected, actual, "PartialFillAllowed() result") + }) + } +} + +func TestOrder_GetOrderType(t *testing.T) { + tests := []struct { + name string + order *Order + expected string + expPanic string + }{ + { + name: "AskOrder", + order: NewOrder(1).WithAsk(&AskOrder{}), + expected: OrderTypeAsk, + }, + { + name: "BidOrder", + order: NewOrder(2).WithBid(&BidOrder{}), + expected: OrderTypeBid, + }, + { + name: "nil inside order", + order: NewOrder(3), + expPanic: nilSubTypeErr, + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + expPanic: unknownSubTypeErr, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.order.GetOrderType() + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOrderType()") + assert.Equal(t, tc.expected, actual, "GetOrderType() result") + }) + } +} + +func TestOrder_GetOrderTypeByte(t *testing.T) { + tests := []struct { + name string + order *Order + expected byte + expPanic string + }{ + { + name: "AskOrder", + order: NewOrder(1).WithAsk(&AskOrder{}), + expected: OrderTypeByteAsk, + }, + { + name: "BidOrder", + order: NewOrder(2).WithBid(&BidOrder{}), + expected: OrderTypeByteBid, + }, + { + name: "nil inside order", + order: NewOrder(3), + expPanic: nilSubTypeErr, + }, + { + name: "unknown order type", + order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + expPanic: unknownSubTypeErr, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual byte + testFunc := func() { + actual = tc.order.GetOrderTypeByte() + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOrderTypeByte()") + assert.Equal(t, tc.expected, actual, "GetOrderTypeByte() result") }) } } @@ -612,12 +788,12 @@ func TestOrder_GetHoldAmount(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: "GetHoldAmount() missing case for ", + expPanic: nilSubTypeErr, }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: "GetHoldAmount() missing case for *exchange.unknownOrderType", + expPanic: unknownSubTypeErr, }, } @@ -627,8 +803,8 @@ func TestOrder_GetHoldAmount(t *testing.T) { testFunc := func() { actual = tc.order.GetHoldAmount() } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetHoldAmount") - assert.Equal(t, tc.expected.String(), actual.String(), "GetHoldAmount result") + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetHoldAmount()") + assert.Equal(t, tc.expected.String(), actual.String(), "GetHoldAmount() result") }) } } @@ -648,9 +824,14 @@ func TestOrder_Validate(t *testing.T) { exp: []string{"invalid order id: must not be zero"}, }, { - name: "unknown order type", + name: "nil sub-order", + Order: NewOrder(1), + exp: []string{nilSubTypeErr}, + }, + { + name: "unknown sub-order type", Order: &Order{OrderId: 1, Order: &unknownOrderType{}}, - exp: []string{"unknown order type *exchange.unknownOrderType"}, + exp: []string{unknownSubTypeErr}, }, { name: "ask order error", @@ -667,12 +848,207 @@ func TestOrder_Validate(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { err := tc.Order.Validate() - assertions.AssertErrorContents(t, err, tc.exp, "Validate() error") }) } } +func TestAskOrder_GetMarketID(t *testing.T) { + tests := []struct { + name string + order AskOrder + exp uint32 + }{ + {name: "zero", order: AskOrder{MarketId: 0}, exp: 0}, + {name: "one", order: AskOrder{MarketId: 1}, exp: 1}, + {name: "five", order: AskOrder{MarketId: 5}, exp: 5}, + {name: "twenty-four", order: AskOrder{MarketId: 24}, exp: 24}, + {name: "max uint16+1", order: AskOrder{MarketId: 65_536}, exp: 65_536}, + {name: "max uint32", order: AskOrder{MarketId: 4_294_967_295}, exp: 4_294_967_295}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual uint32 + testFunc := func() { + actual = tc.order.GetMarketID() + } + require.NotPanics(t, testFunc, "GetMarketID()") + assert.Equal(t, tc.exp, actual, "GetMarketID() result") + }) + } +} + +func TestAskOrder_GetOwner(t *testing.T) { + acc20 := sdk.AccAddress("seller______________").String() + acc32 := sdk.AccAddress("__seller__seller__seller__seller").String() + + tests := []struct { + name string + order AskOrder + exp string + }{ + {name: "empty", order: AskOrder{Seller: ""}, exp: ""}, + {name: "not a bech32", order: AskOrder{Seller: "nopenopenope"}, exp: "nopenopenope"}, + {name: "20-byte bech32", order: AskOrder{Seller: acc20}, exp: acc20}, + {name: "32-byte bech32", order: AskOrder{Seller: acc32}, exp: acc32}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.order.GetOwner() + } + require.NotPanics(t, testFunc, "GetOwner()") + assert.Equal(t, tc.exp, actual, "GetOwner() result") + }) + } +} + +func TestAskOrder_GetAssets(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + order AskOrder + exp sdk.Coins + }{ + {name: "nil", order: AskOrder{Assets: nil}, exp: nil}, + {name: "empty", order: AskOrder{Assets: sdk.Coins{}}, exp: sdk.Coins{}}, + {name: "one denom", order: AskOrder{Assets: sdk.NewCoins(coin(3, "the"))}, exp: sdk.NewCoins(coin(3, "the"))}, + { + name: "three denoms", + order: AskOrder{Assets: sdk.NewCoins(coin(1, "one"), coin(2, "two"), coin(3, "three"))}, + exp: sdk.NewCoins(coin(1, "one"), coin(2, "two"), coin(3, "three")), + }, + { + name: "a negative coin", + order: AskOrder{Assets: sdk.Coins{coin(-1, "neg")}}, + exp: sdk.Coins{coin(-1, "neg")}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coins + testFunc := func() { + actual = tc.order.GetAssets() + } + require.NotPanics(t, testFunc, "GetAssets()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetAssets() result") + }) + } +} + +func TestAskOrder_GetPrice(t *testing.T) { + largeAmt, ok := sdkmath.NewIntFromString("25000000000000000000000") + require.Truef(t, ok, "NewIntFromString(\"25000000000000000000000\")") + largeCoin := sdk.NewCoin("large", largeAmt) + negCoin := sdk.Coin{Denom: "neg", Amount: sdkmath.NewInt(-88)} + + tests := []struct { + name string + order AskOrder + exp sdk.Coin + }{ + {name: "one", order: AskOrder{Price: sdk.NewInt64Coin("one", 1)}, exp: sdk.NewInt64Coin("one", 1)}, + {name: "zero", order: AskOrder{Price: sdk.NewInt64Coin("zero", 0)}, exp: sdk.NewInt64Coin("zero", 0)}, + {name: "negative", order: AskOrder{Price: negCoin}, exp: negCoin}, + {name: "large amount", order: AskOrder{Price: largeCoin}, exp: largeCoin}, + {name: "zero-value", order: AskOrder{Price: sdk.Coin{}}, exp: sdk.Coin{}}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.order.GetPrice() + } + require.NotPanics(t, testFunc, "GetPrice()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetPrice() result") + }) + } +} + +func TestAskOrder_GetSettlementFees(t *testing.T) { + coin := func(amount int64, denom string) *sdk.Coin { + return &sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + largeAmt, ok := sdkmath.NewIntFromString("25000000000000000000000") + require.Truef(t, ok, "NewIntFromString(\"25000000000000000000000\")") + largeCoin := sdk.NewCoin("large", largeAmt) + + tests := []struct { + name string + order AskOrder + exp sdk.Coins + }{ + {name: "nil", order: AskOrder{SellerSettlementFlatFee: nil}, exp: nil}, + {name: "zero amount", order: AskOrder{SellerSettlementFlatFee: coin(0, "zero")}, exp: sdk.Coins{*coin(0, "zero")}}, + {name: "one amount", order: AskOrder{SellerSettlementFlatFee: coin(1, "one")}, exp: sdk.Coins{*coin(1, "one")}}, + {name: "negative amount", order: AskOrder{SellerSettlementFlatFee: coin(-51, "neg")}, exp: sdk.Coins{*coin(-51, "neg")}}, + {name: "large amount", order: AskOrder{SellerSettlementFlatFee: &largeCoin}, exp: sdk.Coins{largeCoin}}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coins + testFunc := func() { + actual = tc.order.GetSettlementFees() + } + require.NotPanics(t, testFunc, "GetSettlementFees()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetSettlementFees() result") + }) + } +} + +func TestAskOrder_PartialFillAllowed(t *testing.T) { + tests := []struct { + name string + order AskOrder + exp bool + }{ + {name: "false", order: AskOrder{AllowPartial: false}, exp: false}, + {name: "true", order: AskOrder{AllowPartial: true}, exp: true}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.order.PartialFillAllowed() + } + require.NotPanics(t, testFunc, "PartialFillAllowed()") + assert.Equal(t, tc.exp, actual, "PartialFillAllowed() result") + }) + } +} + +func TestAskOrder_GetOrderType(t *testing.T) { + expected := OrderTypeAsk + order := AskOrder{} + var actual string + testFunc := func() { + actual = order.GetOrderType() + } + require.NotPanics(t, testFunc, "GetOrderType()") + assert.Equal(t, expected, actual, "GetOrderType() result") +} + +func TestAskOrder_GetOrderTypeByte(t *testing.T) { + expected := OrderTypeByteAsk + order := AskOrder{} + var actual byte + testFunc := func() { + actual = order.GetOrderTypeByte() + } + require.NotPanics(t, testFunc, "GetOrderTypeByte()") + assert.Equal(t, expected, actual, "GetOrderTypeByte() result") +} + func TestAskOrder_GetHoldAmount(t *testing.T) { tests := []struct { name string @@ -970,12 +1346,216 @@ func TestAskOrder_Validate(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { err := tc.order.Validate() - assertions.AssertErrorContents(t, err, tc.exp, "Validate() error") }) } } +func TestBidOrder_GetMarketID(t *testing.T) { + tests := []struct { + name string + order BidOrder + exp uint32 + }{ + {name: "zero", order: BidOrder{MarketId: 0}, exp: 0}, + {name: "one", order: BidOrder{MarketId: 1}, exp: 1}, + {name: "five", order: BidOrder{MarketId: 5}, exp: 5}, + {name: "twenty-four", order: BidOrder{MarketId: 24}, exp: 24}, + {name: "max uint16+1", order: BidOrder{MarketId: 65_536}, exp: 65_536}, + {name: "max uint32", order: BidOrder{MarketId: 4_294_967_295}, exp: 4_294_967_295}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual uint32 + testFunc := func() { + actual = tc.order.GetMarketID() + } + require.NotPanics(t, testFunc, "GetMarketID()") + assert.Equal(t, tc.exp, actual, "GetMarketID() result") + }) + } +} + +func TestBidOrder_GetOwner(t *testing.T) { + acc20 := sdk.AccAddress("buyer_______________").String() + acc32 := sdk.AccAddress("___buyer___buyer___buyer___buyer").String() + + tests := []struct { + name string + order BidOrder + exp string + }{ + {name: "empty", order: BidOrder{Buyer: ""}, exp: ""}, + {name: "not a bech32", order: BidOrder{Buyer: "nopenopenope"}, exp: "nopenopenope"}, + {name: "20-byte bech32", order: BidOrder{Buyer: acc20}, exp: acc20}, + {name: "32-byte bech32", order: BidOrder{Buyer: acc32}, exp: acc32}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.order.GetOwner() + } + require.NotPanics(t, testFunc, "GetOwner()") + assert.Equal(t, tc.exp, actual, "GetOwner() result") + }) + } +} + +func TestBidOrder_GetAssets(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + order BidOrder + exp sdk.Coins + }{ + {name: "nil", order: BidOrder{Assets: nil}, exp: nil}, + {name: "empty", order: BidOrder{Assets: sdk.Coins{}}, exp: sdk.Coins{}}, + {name: "one denom", order: BidOrder{Assets: sdk.NewCoins(coin(3, "the"))}, exp: sdk.NewCoins(coin(3, "the"))}, + { + name: "three denoms", + order: BidOrder{Assets: sdk.NewCoins(coin(1, "one"), coin(2, "two"), coin(3, "three"))}, + exp: sdk.NewCoins(coin(1, "one"), coin(2, "two"), coin(3, "three")), + }, + { + name: "a negative coin", + order: BidOrder{Assets: sdk.Coins{coin(-1, "neg")}}, + exp: sdk.Coins{coin(-1, "neg")}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coins + testFunc := func() { + actual = tc.order.GetAssets() + } + require.NotPanics(t, testFunc, "GetAssets()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetAssets() result") + }) + } +} + +func TestBidOrder_GetPrice(t *testing.T) { + largeAmt, ok := sdkmath.NewIntFromString("25000000000000000000000") + require.Truef(t, ok, "NewIntFromString(\"25000000000000000000000\")") + largeCoin := sdk.NewCoin("large", largeAmt) + negCoin := sdk.Coin{Denom: "neg", Amount: sdkmath.NewInt(-88)} + + tests := []struct { + name string + order BidOrder + exp sdk.Coin + }{ + {name: "one", order: BidOrder{Price: sdk.NewInt64Coin("one", 1)}, exp: sdk.NewInt64Coin("one", 1)}, + {name: "zero", order: BidOrder{Price: sdk.NewInt64Coin("zero", 0)}, exp: sdk.NewInt64Coin("zero", 0)}, + {name: "negative", order: BidOrder{Price: negCoin}, exp: negCoin}, + {name: "large amount", order: BidOrder{Price: largeCoin}, exp: largeCoin}, + {name: "zero-value", order: BidOrder{Price: sdk.Coin{}}, exp: sdk.Coin{}}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.order.GetPrice() + } + require.NotPanics(t, testFunc, "GetPrice()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetPrice() result") + }) + } +} + +func TestBidOrder_GetSettlementFees(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + largeAmt, ok := sdkmath.NewIntFromString("25000000000000000000000") + require.Truef(t, ok, "NewIntFromString(\"25000000000000000000000\")") + largeCoin := sdk.NewCoin("large", largeAmt) + + tests := []struct { + name string + order BidOrder + exp sdk.Coins + }{ + {name: "nil", order: BidOrder{BuyerSettlementFees: nil}, exp: nil}, + {name: "empty", order: BidOrder{BuyerSettlementFees: sdk.Coins{}}, exp: sdk.Coins{}}, + {name: "zero amount", order: BidOrder{BuyerSettlementFees: sdk.Coins{coin(0, "zero")}}, exp: sdk.Coins{coin(0, "zero")}}, + {name: "one amount", order: BidOrder{BuyerSettlementFees: sdk.Coins{coin(1, "one")}}, exp: sdk.Coins{coin(1, "one")}}, + {name: "negative amount", order: BidOrder{BuyerSettlementFees: sdk.Coins{coin(-51, "neg")}}, exp: sdk.Coins{coin(-51, "neg")}}, + {name: "large amount", order: BidOrder{BuyerSettlementFees: sdk.Coins{largeCoin}}, exp: sdk.Coins{largeCoin}}, + { + name: "multiple coins", + order: BidOrder{ + BuyerSettlementFees: sdk.Coins{largeCoin, coin(1, "one"), coin(0, "zero")}, + }, + exp: sdk.Coins{largeCoin, coin(1, "one"), coin(0, "zero")}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coins + testFunc := func() { + actual = tc.order.GetSettlementFees() + } + require.NotPanics(t, testFunc, "GetSettlementFees()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetSettlementFees() result") + }) + } +} + +func TestBidOrder_PartialFillAllowed(t *testing.T) { + tests := []struct { + name string + order BidOrder + exp bool + }{ + {name: "false", order: BidOrder{AllowPartial: false}, exp: false}, + {name: "true", order: BidOrder{AllowPartial: true}, exp: true}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.order.PartialFillAllowed() + } + require.NotPanics(t, testFunc, "PartialFillAllowed()") + assert.Equal(t, tc.exp, actual, "PartialFillAllowed() result") + }) + } +} + +func TestBidOrder_GetOrderType(t *testing.T) { + expected := OrderTypeBid + order := BidOrder{} + var actual string + testFunc := func() { + actual = order.GetOrderType() + } + require.NotPanics(t, testFunc, "GetOrderType()") + assert.Equal(t, expected, actual, "GetOrderType() result") + +} + +func TestBidOrder_GetOrderTypeByte(t *testing.T) { + expected := OrderTypeByteBid + order := BidOrder{} + var actual byte + testFunc := func() { + actual = order.GetOrderTypeByte() + } + require.NotPanics(t, testFunc, "GetOrderTypeByte()") + assert.Equal(t, expected, actual, "GetOrderTypeByte() result") +} + func TestBidOrder_GetHoldAmount(t *testing.T) { tests := []struct { name string @@ -1270,7 +1850,6 @@ func TestBidOrder_Validate(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { err := tc.order.Validate() - assertions.AssertErrorContents(t, err, tc.exp, "Validate() error") }) } From 078151d46496669a737f37de7e975db5bce6e714 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 21 Sep 2023 16:46:45 -0600 Subject: [PATCH 161/309] [1658]: Make it so GetOrderType doesn't panic, but instead always returns a string of some sort since we use it a TON in error messages. --- x/exchange/orders.go | 7 +++++-- x/exchange/orders_test.go | 7 +++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 6cd8ad39c9..5f13fc6866 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -173,9 +173,12 @@ func (o Order) PartialFillAllowed() bool { // GetOrderType returns a string indicating what type this order is. // E.g: OrderTypeAsk or OrderTypeBid -// Panics if the sub-order is not set or is something unexpected. func (o Order) GetOrderType() string { - return o.MustGetSubOrder().GetOrderType() + so, err := o.GetSubOrder() + if err != nil { + return fmt.Sprintf("%T", o.Order) + } + return so.GetOrderType() } // GetOrderTypeByte returns the type byte for this order. diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index f77f256a47..8e59ed0f23 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -681,7 +681,6 @@ func TestOrder_GetOrderType(t *testing.T) { name string order *Order expected string - expPanic string }{ { name: "AskOrder", @@ -696,12 +695,12 @@ func TestOrder_GetOrderType(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: nilSubTypeErr, + expected: "", }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: unknownSubTypeErr, + expected: "*exchange.unknownOrderType", }, } @@ -711,7 +710,7 @@ func TestOrder_GetOrderType(t *testing.T) { testFunc := func() { actual = tc.order.GetOrderType() } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetOrderType()") + require.NotPanics(t, testFunc, "GetOrderType()") assert.Equal(t, tc.expected, actual, "GetOrderType() result") }) } From 4c9e3646bde032fcab53a03e417b3d539bec5ab1 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 22 Sep 2023 12:31:54 -0600 Subject: [PATCH 162/309] [1658]: Lots of work on fulfillment transfer identification and validation. --- x/exchange/fulfillment.go | 515 +++++++++++++++++++++++++++++--------- 1 file changed, 395 insertions(+), 120 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index bd06fff59a..012b49b6ba 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -1,8 +1,11 @@ package exchange import ( + "errors" "fmt" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) @@ -32,8 +35,8 @@ func (i *IndexedAddrAmts) Add(addr string, coins sdk.Coins) { i.amts[n] = i.amts[n].Add(coins...) } -// GetInputs returns all the entries as bank Inputs. -func (i *IndexedAddrAmts) GetInputs() []banktypes.Input { +// GetAsInputs returns all the entries as bank Inputs. +func (i *IndexedAddrAmts) GetAsInputs() []banktypes.Input { rv := make([]banktypes.Input, len(i.addrs)) for n, addr := range i.addrs { rv[n] = banktypes.Input{Address: addr, Coins: i.amts[n]} @@ -41,8 +44,8 @@ func (i *IndexedAddrAmts) GetInputs() []banktypes.Input { return rv } -// GetOutputs returns all the entries as bank Outputs. -func (i *IndexedAddrAmts) GetOutputs() []banktypes.Output { +// GetAsOutputs returns all the entries as bank Outputs. +func (i *IndexedAddrAmts) GetAsOutputs() []banktypes.Output { rv := make([]banktypes.Output, len(i.addrs)) for n, addr := range i.addrs { rv[n] = banktypes.Output{Address: addr, Coins: i.amts[n]} @@ -52,173 +55,445 @@ func (i *IndexedAddrAmts) GetOutputs() []banktypes.Output { // OrderSplit contains an order, and the asset and price amounts that should come out of it. type OrderSplit struct { - Order *Order + // Order fulfillment associated with this split. + Order *OrderFulfillment + // Assets is the amount of assets from the order involved in this split. Assets sdk.Coins - Price sdk.Coin + // Price is the amount of the price from the order involved in this split. + Price sdk.Coin } -// AskOrderFulfillment represents how an ask order is fulfilled. -type AskOrderFulfillment struct { - Order *Order +// OrderFulfillment is used to figure out how an order should be fulfilled. +type OrderFulfillment struct { + // Order is the original order with all its information. + Order *Order + // AssetsFilled is the total assets being fulfilled for the order. + AssetsFilled sdk.Coins + // AssetsLeft is the amount of order assets that have not yet been fulfilled for the order. AssetsLeft sdk.Coins - TotalPrice sdk.Coin - Splits []*OrderSplit + // PriceAmtFilled is the total price amount involved in this order fulfillment. + // If this is a bid order, the PriceAmtFilled is related to the order price. + // If this is an ask order, the PriceAmtFilled is related to the prices of the bid orders fulfilling this order. + PriceAmtFilled sdkmath.Int + // PriceAmtLeft is the price that has not yet been fulfilled for the order. + PriceAmtLeft sdkmath.Int + // FeesLeft is the amount fees left to pay (if this order is only partially filled). + // This is not tracked as fulfillments are applied, it is only set during Finalize(). + FeesLeft sdk.Coins + // Splits contains information on the orders being used to fulfill this order. + Splits []*OrderSplit } -type BidOrderFulfillment struct { - Order *Order - TotalAssets sdk.Coins - PriceLeft sdk.Coin - Splits []*OrderSplit -} +var _ OrderI = (*OrderFulfillment)(nil) -func NewAskOrderFulfillment(order *Order) *AskOrderFulfillment { - if !order.IsAskOrder() { - panic(fmt.Errorf("cannot create AskOrderFulfillment for %s order %d", - order.GetOrderType(), order.OrderId)) - } - askOrder := order.GetAskOrder() - return &AskOrderFulfillment{ - Order: order, - AssetsLeft: askOrder.Assets, - TotalPrice: sdk.NewInt64Coin(askOrder.Price.Denom, 0), +func NewOrderFulfillment(order *Order) *OrderFulfillment { + return &OrderFulfillment{ + Order: order, + AssetsLeft: order.GetAssets(), + PriceAmtFilled: sdkmath.ZeroInt(), + PriceAmtLeft: order.GetPrice().Amount, } } -func NewBidOrderFulfillment(order *Order) *BidOrderFulfillment { - if !order.IsBidOrder() { - panic(fmt.Errorf("cannot create BidOrderFulfillment for %s order %d", - order.GetOrderType(), order.OrderId)) +// GetOrderID gets this fulfillment's order's id. +func (f OrderFulfillment) GetOrderID() uint64 { + return f.Order.GetOrderID() +} + +// IsAskOrder returns true if this is an ask order. +func (f OrderFulfillment) IsAskOrder() bool { + return f.Order.IsAskOrder() +} + +// IsBidOrder returns true if this is an ask order. +func (f OrderFulfillment) IsBidOrder() bool { + return f.Order.IsBidOrder() +} + +// GetMarketID gets this fulfillment's order's market id. +func (f OrderFulfillment) GetMarketID() uint32 { + return f.Order.GetMarketID() +} + +// GetOwner gets this fulfillment's order's owner. +func (f OrderFulfillment) GetOwner() string { + return f.Order.GetOwner() +} + +// GetAssets gets this fulfillment's order's assets. +func (f OrderFulfillment) GetAssets() sdk.Coins { + return f.Order.GetAssets() +} + +// GetPrice gets this fulfillment's order's price. +func (f OrderFulfillment) GetPrice() sdk.Coin { + return f.Order.GetPrice() +} + +// GetSettlementFees gets this fulfillment's order's settlement fees. +func (f OrderFulfillment) GetSettlementFees() sdk.Coins { + return f.Order.GetSettlementFees() +} + +// PartialFillAllowed gets this fulfillment's order's AllowPartial flag. +func (f OrderFulfillment) PartialFillAllowed() bool { + return f.Order.PartialFillAllowed() +} + +// GetOrderType gets this fulfillment's order's type string. +func (f OrderFulfillment) GetOrderType() string { + return f.Order.GetOrderType() +} + +// GetOrderTypeByte gets this fulfillment's order's type byte. +func (f OrderFulfillment) GetOrderTypeByte() byte { + return f.Order.GetOrderTypeByte() +} + +// GetHoldAmount gets this fulfillment's order's hold amount. +func (f OrderFulfillment) GetHoldAmount() sdk.Coins { + return f.Order.GetHoldAmount() +} + +// Finalize does some final calculations and validation for this order fulfillment. +// This order fulfillment and the ones in it maybe updated during this. +func (f *OrderFulfillment) Finalize() error { + if len(f.Splits) == 0 || f.AssetsFilled.IsZero() { + return fmt.Errorf("%s order %d not even partially filled", f.GetOrderType(), f.GetOrderID()) } - bidOrder := order.GetBidOrder() - return &BidOrderFulfillment{ - Order: order, - PriceLeft: bidOrder.Price, + + if f.AssetsLeft.IsAnyNegative() { + return fmt.Errorf("%s order %d having assets %q cannot fill be filled with %q: overfill", + f.GetOrderType(), f.GetOrderID(), f.GetAssets(), f.AssetsFilled) } -} -// AddSplit applies the given bid order in the amount of assets provided to this ask order. -func (f *AskOrderFulfillment) AddSplit(order *Order, assets sdk.Coins) error { - askOrderID := f.Order.OrderId - bidOrderID := order.OrderId - if !order.IsBidOrder() { - return fmt.Errorf("cannot fill ask order %d with %s order %d", - askOrderID, order.GetOrderType(), bidOrderID) + isAskOrder, isBidOrder := f.IsAskOrder(), f.IsBidOrder() + orderAssets := f.GetAssets() + orderPrice := f.GetPrice() + targetPriceAmt := orderPrice.Amount + + if !f.AssetsLeft.IsZero() { + if !f.PartialFillAllowed() { + return fmt.Errorf("%s order %d having assets %q cannot be partially filled with %q: "+ + "order does not allow partial fulfillment", + f.GetOrderType(), f.GetOrderID(), orderAssets, f.AssetsFilled) + } + + if len(orderAssets) != 1 { + return fmt.Errorf("%s order %d having assets %q cannot be partially filled with %q: "+ + "orders with multiple asset types cannot be split", + f.GetOrderType(), f.GetOrderID(), orderAssets, f.AssetsFilled) + } + + assetsFilledAmt := f.AssetsFilled[0].Amount + orderAssetsAmt := orderAssets[0].Amount + priceAssets := orderPrice.Amount.Mul(assetsFilledAmt) + priceRem := priceAssets.Mod(orderAssetsAmt) + if !priceRem.IsZero() { + return fmt.Errorf("%s order %d having assets %q cannot be partially filled by %q: "+ + "price %q is not evenly divisible", + f.GetOrderType(), f.GetOrderID(), orderAssets, f.AssetsFilled, orderPrice) + } + targetPriceAmt = priceAssets.Quo(orderAssetsAmt) } - askOrder := f.Order.GetAskOrder() - bidOrder := order.GetBidOrder() + if isAskOrder { + // For ask orders, we need the updated price to have the same price/asset ratio + // as when originally created. Since ask orders can receive more payment than they requested, + // the PriceAmtLeft in here might be less than that, and we need to fix it. + f.PriceAmtLeft = orderPrice.Amount.Sub(targetPriceAmt) + } - if askOrder.Price.Denom != bidOrder.Price.Denom { - return fmt.Errorf("cannot fill ask order %d having price %q with bid order %d having price %q: denom mismatch", - askOrderID, askOrder.Price, bidOrderID, bidOrder.Price) + if isBidOrder { + // When adding things to f.PriceAmtFilled, we used truncation on the divisions. + // So at this point, it might be a little less than the target price. + // If that's the case, we distribute the difference weighted by assets in order of the splits. + toDistribute := targetPriceAmt.Sub(f.PriceAmtFilled) + if toDistribute.IsNegative() { + return fmt.Errorf("bid order %d having price %q cannot pay %q for %q: overfill", + f.GetOrderID(), f.GetPrice(), f.PriceAmtFilled, f.AssetsFilled) + } + if toDistribute.IsPositive() { + distLeft := toDistribute + // First pass, we won't default to 1 (if the calc comes up zero). + // This helps weight larger orders that are at the end of the list. + // But it's possible for all the calcs to come up zero, so eventually + // we might need to default to one. + minOne := false + for distLeft.IsPositive() { + // OrderFulfillment won't let a bid order with multiple assets be split. + // So we know that here, there's only one asset. + for _, askSplit := range f.Splits { + distAmt := toDistribute.Mul(askSplit.Assets[0].Amount).Quo(f.AssetsFilled[0].Amount) + if distAmt.IsZero() { + if !minOne { + continue + } + distAmt = sdkmath.OneInt() + } + if distAmt.GT(distLeft) { + distAmt = distLeft + } + f.PriceAmtFilled = f.PriceAmtFilled.Add(distAmt) + f.PriceAmtLeft = f.PriceAmtLeft.Sub(distAmt) + askSplit.Price.Amount = askSplit.Price.Amount.Add(distAmt) + askSplit.Order.PriceAmtFilled = askSplit.Order.PriceAmtFilled.Add(distAmt) + // Not updating askSplit.Order.PriceAmtLeft here since that's done specially above. + for _, bidSplit := range askSplit.Order.Splits { + if bidSplit.Order.GetOrderID() == f.GetOrderID() { + bidSplit.Price.Amount = bidSplit.Price.Amount.Add(distAmt) + break + } + } + distLeft = distLeft.Sub(distAmt) + if !distLeft.IsPositive() { + break + } + } + minOne = true + } + } } - if assets.IsZero() { - return fmt.Errorf("cannot fill ask order %d with zero assets from bid order %d", askOrderID, bidOrderID) + // TODO[1658]: Finish up Finalize(). + if !f.AssetsLeft.IsZero() { + var assetsFilledAmt, orderAssetsAmt sdkmath.Int // Temporary line so we can still compile. + orderFees := f.GetSettlementFees() + for _, orderFee := range orderFees { + feeAssets := orderFee.Amount.Mul(assetsFilledAmt) + feeRem := feeAssets.Mul(orderAssetsAmt) + if !feeRem.IsZero() { + return fmt.Errorf("%s order %d having settlement fees %q cannot be partially filled by %q: "+ + "fee %q is not evenly divisible", + f.GetOrderType(), f.GetOrderID(), orderFees, f.AssetsFilled, orderFee) + } + feeAmtLeft := feeAssets.Quo(orderAssetsAmt) + f.FeesLeft = f.FeesLeft.Add(sdk.NewCoin(orderFee.Denom, feeAmtLeft)) + } } - if assets.IsAnyNegative() { - return fmt.Errorf("cannot fill ask order %d with negative assets %q from bid order %d", - askOrderID, assets, bidOrderID) + + panic("not implemented") +} + +// Validate returns an error if there is a problem with this fulfillment. +func (f OrderFulfillment) Validate() error { + var assetsFilled sdk.Coins + pricePaid := sdk.NewInt64Coin(f.GetPrice().Denom, 0) + for _, split := range f.Splits { + assetsFilled = assetsFilled.Add(split.Assets...) + pricePaid = pricePaid.Add(split.Price) } - // TODO[1658]: This should be checked against the assets left to fill in the bid order. - _, hasNeg := bidOrder.Assets.SafeSub(assets...) + orderAssets := f.GetAssets() + unfilledAssets, hasNeg := orderAssets.SafeSub(assetsFilled...) if hasNeg { - return fmt.Errorf("cannot fill ask order %d with assets %q from bid order %d: insufficient assets %q in bid order", - askOrderID, assets, bidOrderID, bidOrder.Assets) + return fmt.Errorf("assets fulfilled %q for order %d is more than the order assets %q", assetsFilled, f.GetOrderID(), orderAssets) + } + + // If it's a bid order: + // If filled in full, the pricePaid needs to equal the order price. + // If not filled in full: + // The price * assets filled / order assets must be a whole number and equal the price paid. + // The settlement fee * assets filled / order assets must be a whole number. + // If it's an ask order: + // If filled in full, the pricePaid must be greater than or equal to the order price. + // If not filled in full: + // The price * assets filled / order assets must be a whole number and must be less than or equal to the price paid. + // The settlement fee * assets filled / order assets must be a whole number. + if unfilledAssets.IsZero() { + } - if len(bidOrder.Assets) > 1 && !CoinsEquals(bidOrder.Assets, assets) { - return fmt.Errorf("cannot fill ask order %d with assets %q from bid order %d having assets %q: "+ - "unable to divide price for multiple asset types", - askOrderID, assets, bidOrderID, bidOrder.Assets) + + if !unfilledAssets.IsZero() && !f.PartialFillAllowed() { + return fmt.Errorf("cannot partially fill order %d having assets %q with assets %q: order does not allow partial fill", + f.GetOrderID(), orderAssets, assetsFilled) } - newAssetsLeft, hasNeg := f.AssetsLeft.SafeSub(assets...) - if hasNeg { - return fmt.Errorf("cannot fill ask order %d having %q left with assets %q from bid order %d", - askOrderID, f.AssetsLeft, assets, bidOrderID) + // TODO[1658]: Implement OrderFulfillment.Validate() + panic("not implemented") +} + +// IsFilled returns true if this fulfillment's order has been fully accounted for. +func (f OrderFulfillment) IsFilled() bool { + return f.AssetsLeft.IsZero() +} + +// getAssetInputsOutputs gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. +func (f OrderFulfillment) getAssetInputsOutputs() ([]banktypes.Input, []banktypes.Output, error) { + indexedSplits := NewIndexedAddrAmts() + for _, split := range f.Splits { + indexedSplits.Add(split.Order.GetOwner(), split.Assets) } - split := &OrderSplit{ - Order: order, - Assets: assets, + if f.IsAskOrder() { + inputs := []banktypes.Input{{Address: f.GetOwner(), Coins: f.AssetsFilled}} + outputs := indexedSplits.GetAsOutputs() + return inputs, outputs, nil + } + if f.IsBidOrder() { + inputs := indexedSplits.GetAsInputs() + outputs := []banktypes.Output{{Address: f.GetOwner(), Coins: f.AssetsFilled}} + return inputs, outputs, nil } + return nil, nil, fmt.Errorf("unknown order type %T", f.Order.GetOrder()) +} - if CoinsEquals(assets, bidOrder.Assets) { - split.Price = bidOrder.Price - } else { - if len(assets) != 1 || len(bidOrder.Assets) != 1 { - return fmt.Errorf("cannot prorate bid order %d assets %q for ask order %d with assets %q: multiple asset denoms", - bidOrderID, assets, askOrderID, askOrder.Assets) +// getPriceInputsOutputs gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. +// It is assumed that getAssetInputsOutputs has already been called and did not return an error. +func (f OrderFulfillment) getPriceInputsOutputs() ([]banktypes.Input, []banktypes.Output, error) { + price := sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceAmtFilled} + if f.PriceAmtLeft.IsNegative() && f.IsBidOrder() { + return nil, nil, fmt.Errorf("bid order %d having price %q cannot pay %q: overfill", + f.GetOrderID(), f.GetPrice(), price) + } + if !f.AssetsLeft.IsZero() { + // Assuming getAssetInputsOutputs was called previously, which returns an error + // if trying to partially fill an order with multiple asset types. + // So, if we're here and there's assets left, there's only one denom. + orderPrice := f.GetPrice() + priceAssetsAmt := orderPrice.Amount.Mul(f.AssetsFilled[0].Amount) + rem := priceAssetsAmt.Mod(f.GetAssets()[0].Amount) + if !rem.IsZero() { + return nil, nil, fmt.Errorf("%s order %d having assets %q cannot be partially filled by %q: "+ + "price %q is not evenly divisible", + f.GetOrderType(), f.GetOrderID(), f.GetAssets(), f.AssetsFilled, orderPrice) } - // Note, we truncate the division here. - // Later, after all fulfillments have been identified, we will handle the remainders. - priceAmt := bidOrder.Price.Amount.Mul(askOrder.Assets[0].Amount).Quo(assets[0].Amount) - split.Price = sdk.NewCoin(f.TotalPrice.Denom, priceAmt) } - f.Splits = append(f.Splits, split) + // TODO[1658]: Finish getPriceInputsOutputs + return nil, nil, nil +} + +// Apply adjusts this order fulfillment using the provided info. +func (f *OrderFulfillment) Apply(order *OrderFulfillment, assets sdk.Coins, priceAmt sdkmath.Int) error { + newAssetsLeft, hasNeg := f.AssetsLeft.SafeSub(assets...) + if hasNeg { + return fmt.Errorf("cannot fill %s order %d having assets left %q with %q from %s order %d: overfill", + f.GetOrderType(), f.GetOrderID(), f.AssetsLeft, assets, order.GetOrderType(), order.GetOrderID()) + } f.AssetsLeft = newAssetsLeft - f.TotalPrice = f.TotalPrice.Add(split.Price) + f.AssetsFilled = f.AssetsFilled.Add(assets...) + f.PriceAmtLeft = f.PriceAmtLeft.Sub(priceAmt) + f.PriceAmtFilled = f.PriceAmtFilled.Add(priceAmt) + f.Splits = append(f.Splits, &OrderSplit{ + Order: order, + Assets: assets, + Price: sdk.NewCoin(order.GetPrice().Denom, priceAmt), + }) return nil } -func (f *BidOrderFulfillment) AddSplit(order *Order, assets sdk.Coins) error { - askOrderID := order.OrderId - bidOrderID := f.Order.OrderId - if !order.IsAskOrder() { - return fmt.Errorf("cannot fill bid order %d with %s order %d", - askOrderID, order.GetOrderType(), bidOrderID) +// Fulfill attempts to use the two provided order fulfillments to fulfill each other. +// The provided order fulfillments will be updated if everything goes okay. +func Fulfill(of1, of2 *OrderFulfillment) error { + order1Type := of1.Order.GetOrderType() + order2Type := of2.Order.GetOrderType() + if order1Type == order2Type { + return fmt.Errorf("cannot fulfill %s order %d with %s order %d", + order1Type, of1.Order.OrderId, order2Type, of2.Order.OrderId) } - askOrder := order.GetAskOrder() - bidOrder := f.Order.GetBidOrder() + var askOF, bidOF *OrderFulfillment + if order1Type == OrderTypeAsk { + askOF = of1 + bidOF = of2 + } else { + askOF = of2 + bidOF = of1 + } + + askOrderID, bidOrderID := askOF.GetOrderID(), bidOF.GetOrderID() + askOrder, bidOrder := askOF.Order.GetAskOrder(), bidOF.Order.GetBidOrder() if askOrder.Price.Denom != bidOrder.Price.Denom { return fmt.Errorf("cannot fill bid order %d having price %q with ask order %d having price %q: denom mismatch", bidOrderID, bidOrder.Price, askOrderID, askOrder.Price) } - if assets.IsZero() { - return fmt.Errorf("cannot fill bid order %d with zero assets from ask order %d", bidOrderID, askOrderID) + assets, err := getFulfillmentAssets(askOF, bidOF) + if err != nil { + return err } - if assets.IsAnyNegative() { - return fmt.Errorf("cannot fill bid order %d with negative assets %q from ask order %d", - bidOrderID, assets, askOrderID) + + // We calculate the price amount based off the original order assets (as opposed to assets left) + // for consistent truncation and remainders. Once we've identified all the fulfillment relationships, + // we'll enumerate and redistribute those remainders. + priceAmt := bidOrder.Price.Amount + if !CoinsEquals(assets, bidOrder.Assets) { + if len(bidOrder.Assets) != 1 { + return fmt.Errorf("cannot split bid order %d having multiple assets %q", bidOrderID, bidOrder.Assets) + } + if len(assets) != 1 { + return fmt.Errorf("cannot split bid order %d having assets %q by %q: overfill", + bidOrderID, bidOrder.Assets, assets) + } + priceAmt = bidOrder.Price.Amount.Mul(assets[0].Amount).Quo(bidOrder.Assets[0].Amount) } - // TODO[1658]: This should be checked against the assets left to fill in the ask order. - _, hasNeg := askOrder.Assets.SafeSub(assets...) - if hasNeg { - return fmt.Errorf("cannot fill bid order %d with assets %q from ask order %d: insufficient assets %q in ask order", - bidOrderID, assets, askOrderID, askOrder.Assets) + + askErr := askOF.Apply(bidOF, assets, priceAmt) + bidErr := bidOF.Apply(askOF, assets, priceAmt) + + return errors.Join(askErr, bidErr) +} + +// getFulfillmentAssets figures out the assets that can be fulfilled with the two provided orders. +// It's assumed that the askOF is for an ask order, and the bidOF is for a bid order. +func getFulfillmentAssets(askOF, bidOF *OrderFulfillment) (sdk.Coins, error) { + askAssets, bidAssets := askOF.AssetsLeft, bidOF.AssetsLeft + askOrderID, bidOrderID := askOF.GetOrderID(), bidOF.GetOrderID() + stdErr := func(msg string) error { + return fmt.Errorf("cannot fill ask order %d having assets left %q with bid order %d having assets left %q: %s", + askOrderID, askAssets, bidOrderID, bidAssets, msg) } - split := &OrderSplit{ - Order: order, - Assets: assets, + if askAssets.IsZero() || askAssets.IsAnyNegative() || bidAssets.IsZero() || bidAssets.IsAnyNegative() { + return nil, stdErr("zero or negative assets") } - if CoinsEquals(assets, bidOrder.Assets) { - split.Price = bidOrder.Price - } else { - if len(assets) != 1 || len(bidOrder.Assets) != 1 { - return fmt.Errorf("cannot prorate bid order %d assets %q for ask order %d with assets %q: multiple asset denoms", - bidOrderID, assets, askOrderID, askOrder.Assets) + // If they're equal, we're all good, just return one of them. + if CoinsEquals(askAssets, bidAssets) { + return askAssets, nil + } + + // Handling single denom case now since that's expected to be the most common thing, and it's easier. + if len(askAssets) == 1 && len(bidAssets) == 1 { + if askAssets[0].Denom != bidAssets[0].Denom { + return nil, stdErr("asset denom mismatch") } - // Note, we truncate the division here. - // Later, after all fulfillments have been identified, we will handle the remainders. - priceAmt := bidOrder.Price.Amount.Mul(bidOrder.Assets[0].Amount).Quo(assets[0].Amount) - split.Price = sdk.NewCoin(f.PriceLeft.Denom, priceAmt) + // Take the lesser of the two. + if askAssets[0].Amount.LT(bidAssets[0].Amount) { + return askAssets, nil + } + return bidAssets, nil } - newPriceLeftAmt := f.PriceLeft.Amount.Sub(split.Price.Amount) - if newPriceLeftAmt.IsNegative() { - return fmt.Errorf("cannot fill bid order %d having assets %q with assets %q for ask order %d: "+ - "price left %q is less than price needed %q", - bidOrderID, bidOrder.Assets, assets, askOrderID, f.PriceLeft, split.Price) + + // When splitting a bid, we distribute the price based on the assets. + // If the bid has multiple asset denoms, that division is impossible. + // So, if the bid has multiple asset denoms, ensure they're less than or equal to the ask assets and return them. + if len(bidAssets) > 1 { + _, hasNeg := askAssets.SafeSub(bidAssets...) + if hasNeg { + return nil, stdErr("bid orders with multiple assets cannot be split") + } + return bidAssets, nil } - f.Splits = append(f.Splits, split) - f.TotalAssets = f.TotalAssets.Add(assets...) - f.PriceLeft.Amount = newPriceLeftAmt - return nil + // At this point, we know there's multiple ask assets, and only one bid asset. + // All we care about are the amounts of that denom, taking the lesser of the two (but not zero). + askAmt := askAssets.AmountOf(bidAssets[0].Denom) + if !askAmt.IsPositive() { + return nil, stdErr("asset denom mismatch") + } + if bidAssets[0].Amount.LTE(askAmt) { + return bidAssets, nil + } + return sdk.NewCoins(sdk.NewCoin(bidAssets[0].Denom, askAmt)), nil +} + +type OrderTransfers struct { + AssetInputs []banktypes.Input + AssetOutputs []banktypes.Output + PriceInputs []banktypes.Input + PriceOutputs []banktypes.Output + FeeInputs []banktypes.Input + FeeTotal sdk.Coins } From 50b8a96af3a67ea9745abdc94f4fb159797f98d1 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 22 Sep 2023 13:03:38 -0600 Subject: [PATCH 163/309] [1658]: Make BidOrder.Assets a Coin (instead of Coins). --- docs/proto-docs.md | 2 +- proto/provenance/exchange/v1/orders.proto | 3 +- x/exchange/events_test.go | 21 ++- x/exchange/fulfillment.go | 7 +- x/exchange/genesis_test.go | 6 +- x/exchange/keeper/orders.go | 2 +- x/exchange/msg_test.go | 2 +- x/exchange/orders.go | 18 +-- x/exchange/orders.pb.go | 103 ++++++------- x/exchange/orders_test.go | 175 +++++++++------------- 10 files changed, 145 insertions(+), 194 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 8ee179299b..9ebabd2313 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1766,7 +1766,7 @@ BidOrder represents someone's desire to buy something at a specific price. | ----- | ---- | ----- | ----------- | | `market_id` | [uint32](#uint32) | | market_id identifies the market that this order belongs to. | | `buyer` | [string](#string) | | buyer is the address of the account that owns this order and has the price to spend. | -| `assets` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | assets are the things that the buyer wishes to buy. | +| `assets` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | assets are the things that the buyer wishes to buy. | | `price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | price is the amount that the buyer will pay for the assets. A hold is placed on this until the order is filled or cancelled. | | `buyer_settlement_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) when the order is settled. A hold is placed on this until the order is filled or cancelled. | | `allow_partial` | [bool](#bool) | | allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the order must be either filled in full or not filled at all. | diff --git a/proto/provenance/exchange/v1/orders.proto b/proto/provenance/exchange/v1/orders.proto index 31ba59b9cd..36dea50dfb 100644 --- a/proto/provenance/exchange/v1/orders.proto +++ b/proto/provenance/exchange/v1/orders.proto @@ -59,8 +59,7 @@ message BidOrder { // buyer is the address of the account that owns this order and has the price to spend. string buyer = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // assets are the things that the buyer wishes to buy. - repeated cosmos.base.v1beta1.Coin assets = 3 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + cosmos.base.v1beta1.Coin assets = 3 [(gogoproto.nullable) = false]; // price is the amount that the buyer will pay for the assets. // A hold is placed on this until the order is filled or cancelled. cosmos.base.v1beta1.Coin price = 4 [(gogoproto.nullable) = false]; diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go index bbae876b02..75832aaaf1 100644 --- a/x/exchange/events_test.go +++ b/x/exchange/events_test.go @@ -40,12 +40,14 @@ func TestNewEventOrderCreated(t *testing.T) { name string order *Order expected *EventOrderCreated - expPanic interface{} }{ { - name: "nil order", - order: NewOrder(3), - expPanic: "GetOrderType() missing case for ", + name: "nil order", + order: NewOrder(3), + expected: &EventOrderCreated{ + OrderId: 3, + OrderType: "", + }, }, { name: "order with ask", @@ -71,14 +73,9 @@ func TestNewEventOrderCreated(t *testing.T) { testFunc := func() { actual = NewEventOrderCreated(tc.order) } - - if tc.expPanic != nil { - require.PanicsWithValue(t, tc.expPanic, testFunc, "NewEventOrderCreated") - } else { - require.NotPanics(t, testFunc, "NewEventOrderCreated") - assert.Equal(t, tc.expected, actual, "NewEventOrderCreated result") - assertEverythingSet(t, actual, "EventOrderCreated") - } + require.NotPanics(t, testFunc, "NewEventOrderCreated") + assert.Equal(t, tc.expected, actual, "NewEventOrderCreated result") + assertEverythingSet(t, actual, "EventOrderCreated") }) } } diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 012b49b6ba..9c55fd2692 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -418,15 +418,12 @@ func Fulfill(of1, of2 *OrderFulfillment) error { // for consistent truncation and remainders. Once we've identified all the fulfillment relationships, // we'll enumerate and redistribute those remainders. priceAmt := bidOrder.Price.Amount - if !CoinsEquals(assets, bidOrder.Assets) { - if len(bidOrder.Assets) != 1 { - return fmt.Errorf("cannot split bid order %d having multiple assets %q", bidOrderID, bidOrder.Assets) - } + if !CoinsEquals(assets, bidOrder.GetAssets()) { if len(assets) != 1 { return fmt.Errorf("cannot split bid order %d having assets %q by %q: overfill", bidOrderID, bidOrder.Assets, assets) } - priceAmt = bidOrder.Price.Amount.Mul(assets[0].Amount).Quo(bidOrder.Assets[0].Amount) + priceAmt = bidOrder.Price.Amount.Mul(assets[0].Amount).Quo(bidOrder.Assets.Amount) } askErr := askOF.Apply(bidOF, assets, priceAmt) diff --git a/x/exchange/genesis_test.go b/x/exchange/genesis_test.go index 0e01841340..5f3508876d 100644 --- a/x/exchange/genesis_test.go +++ b/x/exchange/genesis_test.go @@ -33,11 +33,13 @@ func TestGenesisState_Validate(t *testing.T) { } bidOrder := func(orderID uint64, marketID uint32, assets string, price string) Order { priceCoin, err := sdk.ParseCoinNormalized(price) - require.NoError(t, err, "sdk.ParseCoinNormalized(%q)", price) + require.NoError(t, err, "price sdk.ParseCoinNormalized(%q)", price) + assetsCoin, err := sdk.ParseCoinNormalized(assets) + require.NoError(t, err, "assets sdk.ParseCoinNormalized(%q)", assets) return *NewOrder(orderID).WithBid(&BidOrder{ MarketId: marketID, Buyer: addr1, - Assets: coins(assets), + Assets: assetsCoin, Price: priceCoin, }) } diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index c91248f0ea..50ae794c3d 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -555,7 +555,7 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro assetOutputs = append(assetOutputs, banktypes.Output{Address: buyer}) priceInputs = append(priceInputs, banktypes.Input{Address: buyer}) } - assetOutputs[i].Coins = assetOutputs[i].Coins.Add(assets...) + assetOutputs[i].Coins = assetOutputs[i].Coins.Add(assets) priceInputs[i].Coins = priceInputs[i].Coins.Add(price) if !buyerSettlementFees.IsZero() { diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 076ee0a7a1..4af6031a26 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -222,7 +222,7 @@ func TestMsgCreateBidRequest_ValidateBasic(t *testing.T) { BidOrder: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("buyer_______________").String(), - Assets: sdk.NewCoins(sdk.NewInt64Coin("banana", 99)), + Assets: sdk.NewInt64Coin("banana", 99), Price: sdk.NewInt64Coin("acorn", 12), }, OrderCreationFee: &sdk.Coin{Denom: "cactus", Amount: sdkmath.NewInt(-3)}, diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 5f13fc6866..0671131f0f 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -330,7 +330,7 @@ func (b BidOrder) GetOwner() string { // GetAssets returns the assets to buy for this bid order. func (b BidOrder) GetAssets() sdk.Coins { - return b.Assets + return sdk.Coins{b.Assets} } // GetPrice returns the price to pay for this bid order. @@ -393,18 +393,10 @@ func (b BidOrder) Validate() error { // We also don't want to allow the price denom to also be in the assets. if err := b.Assets.Validate(); err != nil { errs = append(errs, fmt.Errorf("invalid assets: %w", err)) - } else { - switch { - case len(b.Assets) == 0: - errs = append(errs, errors.New("invalid assets: must not be empty")) - case len(priceDenom) > 0: - for _, asset := range b.Assets { - if priceDenom == asset.Denom { - errs = append(errs, fmt.Errorf("invalid assets: cannot contain price denom %s", priceDenom)) - break - } - } - } + } else if b.Assets.IsZero() { + errs = append(errs, errors.New("invalid assets: cannot be zero")) + } else if len(priceDenom) > 0 && b.Assets.Denom == priceDenom { + errs = append(errs, fmt.Errorf("invalid assets: price denom %s cannot also be the assets denom", priceDenom)) } if len(b.BuyerSettlementFees) > 0 { diff --git a/x/exchange/orders.pb.go b/x/exchange/orders.pb.go index 053783fc93..41c6ef9197 100644 --- a/x/exchange/orders.pb.go +++ b/x/exchange/orders.pb.go @@ -178,7 +178,7 @@ type BidOrder struct { // buyer is the address of the account that owns this order and has the price to spend. Buyer string `protobuf:"bytes,2,opt,name=buyer,proto3" json:"buyer,omitempty"` // assets are the things that the buyer wishes to buy. - Assets github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=assets,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"assets"` + Assets types.Coin `protobuf:"bytes,3,opt,name=assets,proto3" json:"assets"` // price is the amount that the buyer will pay for the assets. // A hold is placed on this until the order is filled or cancelled. Price types.Coin `protobuf:"bytes,4,opt,name=price,proto3" json:"price"` @@ -234,41 +234,41 @@ func init() { } var fileDescriptor_dab7cbe63f582471 = []byte{ - // 532 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xb1, 0x6f, 0xd3, 0x4e, - 0x14, 0xb6, 0x9b, 0x38, 0x75, 0xae, 0xbf, 0x2e, 0xfe, 0x15, 0x70, 0x82, 0xe4, 0x44, 0xe9, 0x92, - 0x25, 0xe7, 0xa6, 0x88, 0x85, 0x05, 0x35, 0x48, 0x15, 0x9d, 0xa8, 0x5c, 0x89, 0x81, 0xc5, 0x3a, - 0xdb, 0xaf, 0xee, 0x29, 0x8e, 0xcf, 0xf2, 0x5d, 0x43, 0x3b, 0xb1, 0x32, 0xf2, 0x1f, 0xc0, 0xcc, - 0x0a, 0x7f, 0x44, 0xc7, 0x8a, 0x89, 0x09, 0x50, 0xf2, 0x4f, 0x30, 0x22, 0xdf, 0x5d, 0xd2, 0x20, - 0x41, 0x00, 0x89, 0x81, 0xc9, 0xf7, 0xde, 0x7d, 0xef, 0xfb, 0x3e, 0x7d, 0x4f, 0x3e, 0xb4, 0x5b, - 0x94, 0x6c, 0x0a, 0x39, 0xc9, 0x63, 0xf0, 0xe1, 0x22, 0x3e, 0x23, 0x79, 0x0a, 0xfe, 0x74, 0xe8, - 0xb3, 0x32, 0x81, 0x92, 0xe3, 0xa2, 0x64, 0x82, 0x39, 0xb7, 0x6f, 0x40, 0x78, 0x01, 0xc2, 0xd3, - 0x61, 0xdb, 0x8b, 0x19, 0x9f, 0x30, 0xee, 0x47, 0x84, 0x57, 0x43, 0x11, 0x08, 0x32, 0xf4, 0x63, - 0x46, 0x73, 0x35, 0xd7, 0x6e, 0xa9, 0xfb, 0x50, 0x56, 0xbe, 0x2a, 0xf4, 0xd5, 0x4e, 0xca, 0x52, - 0xa6, 0xfa, 0xd5, 0x49, 0x75, 0x7b, 0xef, 0x4c, 0x64, 0x3d, 0xa9, 0x94, 0x9d, 0x16, 0xb2, 0xa5, - 0x85, 0x90, 0x26, 0xae, 0xd9, 0x35, 0xfb, 0xf5, 0x60, 0x53, 0xd6, 0x47, 0x89, 0xf3, 0x10, 0x35, - 0x09, 0x1f, 0x87, 0xb2, 0x74, 0x37, 0xba, 0x66, 0x7f, 0x6b, 0xbf, 0x8b, 0x7f, 0xec, 0x10, 0x1f, - 0xf0, 0xb1, 0xe4, 0x7b, 0x6c, 0x04, 0x36, 0xd1, 0xe7, 0x8a, 0x20, 0xa2, 0x89, 0x26, 0xa8, 0xad, - 0x27, 0x18, 0xd1, 0x64, 0x49, 0x10, 0xe9, 0xf3, 0x83, 0xfa, 0xcb, 0x37, 0x1d, 0x63, 0xb4, 0x89, - 0x2c, 0x49, 0xd1, 0xfb, 0xba, 0x81, 0xec, 0x85, 0x90, 0x73, 0x17, 0x35, 0x27, 0xa4, 0x1c, 0x83, - 0x58, 0x38, 0xdf, 0x0e, 0x6c, 0xd5, 0x38, 0x4a, 0x9c, 0x3d, 0xd4, 0xe0, 0x90, 0x65, 0xda, 0x77, - 0x73, 0xe4, 0x7e, 0x78, 0x3f, 0xd8, 0xd1, 0xb9, 0x1c, 0x24, 0x49, 0x09, 0x9c, 0x9f, 0x88, 0x92, - 0xe6, 0x69, 0xa0, 0x71, 0x4e, 0x8c, 0x1a, 0x84, 0x73, 0x10, 0xdc, 0xad, 0x75, 0x6b, 0xfd, 0xad, - 0xfd, 0x16, 0xd6, 0xf0, 0x2a, 0x73, 0xac, 0x33, 0xc7, 0x8f, 0x18, 0xcd, 0x47, 0x7b, 0x57, 0x9f, - 0x3a, 0xc6, 0xdb, 0xcf, 0x9d, 0x7e, 0x4a, 0xc5, 0xd9, 0x79, 0x84, 0x63, 0x36, 0xd1, 0x99, 0xeb, - 0xcf, 0x80, 0x27, 0x63, 0x5f, 0x5c, 0x16, 0xc0, 0xe5, 0x00, 0x0f, 0x34, 0xb5, 0x73, 0x1f, 0x59, - 0x45, 0x49, 0x63, 0x70, 0xeb, 0x32, 0x8c, 0x35, 0x1a, 0xf5, 0x4a, 0x23, 0x50, 0x68, 0xe7, 0x29, - 0x6a, 0x2b, 0x97, 0x21, 0x07, 0x21, 0x32, 0x98, 0x40, 0x2e, 0xc2, 0xd3, 0x8c, 0x88, 0xf0, 0x14, - 0xc0, 0xb5, 0x7e, 0xc1, 0x15, 0xdc, 0x51, 0xc3, 0x27, 0xcb, 0xd9, 0xc3, 0x8c, 0x88, 0x43, 0x00, - 0x67, 0x17, 0x6d, 0x93, 0x2c, 0x63, 0xcf, 0xc3, 0x82, 0x94, 0x82, 0x92, 0xcc, 0x6d, 0x74, 0xcd, - 0xbe, 0x1d, 0xfc, 0x27, 0x9b, 0xc7, 0xaa, 0xa7, 0x76, 0xd0, 0x7b, 0x5d, 0x43, 0xf6, 0x62, 0x45, - 0xeb, 0xa3, 0xc7, 0xc8, 0x8a, 0xce, 0x2f, 0x7f, 0x23, 0x79, 0x05, 0xfb, 0xa7, 0x83, 0x7f, 0x81, - 0x6e, 0x49, 0x93, 0xdf, 0xe5, 0x0e, 0xc0, 0x5d, 0xeb, 0xef, 0x5b, 0xfd, 0x5f, 0x2a, 0xad, 0x2c, - 0x09, 0x80, 0xff, 0xc1, 0x86, 0x46, 0x70, 0x35, 0xf3, 0xcc, 0xeb, 0x99, 0x67, 0x7e, 0x99, 0x79, - 0xe6, 0xab, 0xb9, 0x67, 0x5c, 0xcf, 0x3d, 0xe3, 0xe3, 0xdc, 0x33, 0x50, 0x8b, 0xb2, 0x9f, 0xfc, - 0x75, 0xc7, 0xe6, 0x33, 0xbc, 0x62, 0xf0, 0x06, 0x34, 0xa0, 0x6c, 0xa5, 0xf2, 0x2f, 0x96, 0x4f, - 0x56, 0xd4, 0x90, 0x0f, 0xc8, 0xbd, 0x6f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x21, 0x5d, 0xcb, 0xfa, - 0xd0, 0x04, 0x00, 0x00, + // 540 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0xb1, 0x6f, 0xd3, 0x4e, + 0x14, 0xc7, 0xed, 0x24, 0x4e, 0x9d, 0xeb, 0xaf, 0x8b, 0x7f, 0x05, 0x9c, 0x20, 0x39, 0x51, 0xba, + 0x64, 0xc9, 0xb9, 0x29, 0x42, 0x48, 0x2c, 0xa8, 0x41, 0xaa, 0xe8, 0x44, 0xe5, 0x4a, 0x0c, 0x2c, + 0xd6, 0xd9, 0x7e, 0x4d, 0x4f, 0x71, 0x7c, 0x96, 0xef, 0x1a, 0xda, 0x89, 0x95, 0x91, 0x3f, 0x81, + 0x99, 0x15, 0xfe, 0x88, 0x8e, 0x15, 0x13, 0x13, 0xa0, 0xe4, 0x4f, 0x60, 0x61, 0x44, 0xbe, 0xbb, + 0xa4, 0x41, 0x82, 0x50, 0x04, 0x93, 0xef, 0xbd, 0xfb, 0xbe, 0xef, 0x7b, 0xfa, 0x3c, 0xf9, 0xd0, + 0x4e, 0x5e, 0xb0, 0x29, 0x64, 0x24, 0x8b, 0xc1, 0x87, 0xf3, 0xf8, 0x94, 0x64, 0x23, 0xf0, 0xa7, + 0x03, 0x9f, 0x15, 0x09, 0x14, 0x1c, 0xe7, 0x05, 0x13, 0xcc, 0xb9, 0x7d, 0x2d, 0xc2, 0x0b, 0x11, + 0x9e, 0x0e, 0x5a, 0x5e, 0xcc, 0xf8, 0x84, 0x71, 0x3f, 0x22, 0xbc, 0x2c, 0x8a, 0x40, 0x90, 0x81, + 0x1f, 0x33, 0x9a, 0xa9, 0xba, 0x56, 0x53, 0xdd, 0x87, 0x32, 0xf2, 0x55, 0xa0, 0xaf, 0xb6, 0x47, + 0x6c, 0xc4, 0x54, 0xbe, 0x3c, 0xa9, 0x6c, 0xf7, 0x9d, 0x89, 0xac, 0xa7, 0x65, 0x67, 0xa7, 0x89, + 0x6c, 0x39, 0x42, 0x48, 0x13, 0xd7, 0xec, 0x98, 0xbd, 0x5a, 0xb0, 0x21, 0xe3, 0xc3, 0xc4, 0x79, + 0x84, 0x1a, 0x84, 0x8f, 0x43, 0x19, 0xba, 0x95, 0x8e, 0xd9, 0xdb, 0xdc, 0xeb, 0xe0, 0x9f, 0x4f, + 0x88, 0xf7, 0xf9, 0x58, 0xfa, 0x3d, 0x31, 0x02, 0x9b, 0xe8, 0x73, 0x69, 0x10, 0xd1, 0x44, 0x1b, + 0x54, 0xd7, 0x1b, 0x0c, 0x69, 0xb2, 0x34, 0x88, 0xf4, 0xf9, 0x61, 0xed, 0xd5, 0x9b, 0xb6, 0x31, + 0xdc, 0x40, 0x96, 0xb4, 0xe8, 0x7e, 0xab, 0x20, 0x7b, 0xd1, 0xc8, 0xb9, 0x8b, 0x1a, 0x13, 0x52, + 0x8c, 0x41, 0x2c, 0x26, 0xdf, 0x0a, 0x6c, 0x95, 0x38, 0x4c, 0x9c, 0x5d, 0x54, 0xe7, 0x90, 0xa6, + 0x7a, 0xee, 0xc6, 0xd0, 0xfd, 0xf0, 0xbe, 0xbf, 0xad, 0xb9, 0xec, 0x27, 0x49, 0x01, 0x9c, 0x1f, + 0x8b, 0x82, 0x66, 0xa3, 0x40, 0xeb, 0x9c, 0x18, 0xd5, 0x09, 0xe7, 0x20, 0xb8, 0x5b, 0xed, 0x54, + 0x7b, 0x9b, 0x7b, 0x4d, 0xac, 0xe5, 0x25, 0x73, 0xac, 0x99, 0xe3, 0xc7, 0x8c, 0x66, 0xc3, 0xdd, + 0xcb, 0x4f, 0x6d, 0xe3, 0xed, 0xe7, 0x76, 0x6f, 0x44, 0xc5, 0xe9, 0x59, 0x84, 0x63, 0x36, 0xd1, + 0xcc, 0xf5, 0xa7, 0xcf, 0x93, 0xb1, 0x2f, 0x2e, 0x72, 0xe0, 0xb2, 0x80, 0x07, 0xda, 0xda, 0xb9, + 0x8f, 0xac, 0xbc, 0xa0, 0x31, 0xb8, 0x35, 0x09, 0x63, 0x4d, 0x8f, 0x5a, 0xd9, 0x23, 0x50, 0x6a, + 0xe7, 0x19, 0x6a, 0xa9, 0x29, 0x43, 0x0e, 0x42, 0xa4, 0x30, 0x81, 0x4c, 0x84, 0x27, 0x29, 0x11, + 0xe1, 0x09, 0x80, 0x6b, 0xfd, 0xc6, 0x2b, 0xb8, 0xa3, 0x8a, 0x8f, 0x97, 0xb5, 0x07, 0x29, 0x11, + 0x07, 0x00, 0xce, 0x0e, 0xda, 0x22, 0x69, 0xca, 0x5e, 0x84, 0x39, 0x29, 0x04, 0x25, 0xa9, 0x5b, + 0xef, 0x98, 0x3d, 0x3b, 0xf8, 0x4f, 0x26, 0x8f, 0x54, 0x4e, 0xed, 0xa0, 0xfb, 0xb5, 0x82, 0xec, + 0xc5, 0x8a, 0xd6, 0xa3, 0xc7, 0xc8, 0x8a, 0xce, 0x2e, 0x6e, 0x40, 0x5e, 0xc9, 0x9c, 0x07, 0x2b, + 0xe0, 0x6f, 0x04, 0xe5, 0x2f, 0x61, 0xbe, 0x44, 0xb7, 0x64, 0xe3, 0x1f, 0x58, 0x02, 0x70, 0xd7, + 0xfa, 0xf7, 0x7b, 0xff, 0x5f, 0x76, 0x5a, 0x01, 0x0f, 0xc0, 0xff, 0x80, 0xfa, 0x10, 0x2e, 0x67, + 0x9e, 0x79, 0x35, 0xf3, 0xcc, 0x2f, 0x33, 0xcf, 0x7c, 0x3d, 0xf7, 0x8c, 0xab, 0xb9, 0x67, 0x7c, + 0x9c, 0x7b, 0x06, 0x6a, 0x52, 0xf6, 0x8b, 0x3f, 0xe9, 0xc8, 0x7c, 0x8e, 0x57, 0x06, 0xbc, 0x16, + 0xf5, 0x29, 0x5b, 0x89, 0xfc, 0xf3, 0xe5, 0x33, 0x14, 0xd5, 0xe5, 0xa3, 0x70, 0xef, 0x7b, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x0b, 0x65, 0x60, 0x61, 0xa4, 0x04, 0x00, 0x00, } func (m *Order) Marshal() (dAtA []byte, err error) { @@ -485,20 +485,16 @@ func (m *BidOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { } i-- dAtA[i] = 0x22 - if len(m.Assets) > 0 { - for iNdEx := len(m.Assets) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Assets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintOrders(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a + { + size, err := m.Assets.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x1a if len(m.Buyer) > 0 { i -= len(m.Buyer) copy(dAtA[i:], m.Buyer) @@ -608,12 +604,8 @@ func (m *BidOrder) Size() (n int) { if l > 0 { n += 1 + l + sovOrders(uint64(l)) } - if len(m.Assets) > 0 { - for _, e := range m.Assets { - l = e.Size() - n += 1 + l + sovOrders(uint64(l)) - } - } + l = m.Assets.Size() + n += 1 + l + sovOrders(uint64(l)) l = m.Price.Size() n += 1 + l + sovOrders(uint64(l)) if len(m.BuyerSettlementFees) > 0 { @@ -1106,8 +1098,7 @@ func (m *BidOrder) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Assets = append(m.Assets, types.Coin{}) - if err := m.Assets[len(m.Assets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Assets.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 8e59ed0f23..042db51ee0 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -205,7 +205,7 @@ func TestOrder_WithBid(t *testing.T) { origBid := &BidOrder{ MarketId: 11, Buyer: "some buyer", - Assets: sdk.NewCoins(sdk.NewInt64Coin("agua", 7)), + Assets: sdk.NewInt64Coin("agua", 7), Price: sdk.NewInt64Coin("dirt", 2), BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("grape", 3)), AllowPartial: true, @@ -213,7 +213,7 @@ func TestOrder_WithBid(t *testing.T) { bid := &BidOrder{ MarketId: origBid.MarketId, Buyer: origBid.Buyer, - Assets: copyCoins(origBid.Assets), + Assets: copyCoin(origBid.Assets), Price: copyCoin(origBid.Price), BuyerSettlementFees: copyCoins(origBid.BuyerSettlementFees), AllowPartial: origBid.AllowPartial, @@ -375,7 +375,7 @@ func TestOrder_GetSubOrder(t *testing.T) { bidOrder := &BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("Buyer_______________").String(), - Assets: sdk.NewCoins(sdk.NewInt64Coin("assetcoin", 33)), + Assets: sdk.NewInt64Coin("assetcoin", 33), Price: sdk.NewInt64Coin("paycoin", 88), BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("feecoin", 11)), AllowPartial: true, @@ -526,7 +526,7 @@ func TestOrder_GetAssets(t *testing.T) { }, { name: "BidOrder", - order: NewOrder(2).WithBid(&BidOrder{Assets: sdk.NewCoins(sdk.NewInt64Coin("boogers", 3))}), + order: NewOrder(2).WithBid(&BidOrder{Assets: sdk.NewInt64Coin("boogers", 3)}), expected: sdk.NewCoins(sdk.NewInt64Coin("boogers", 3)), }, { @@ -820,7 +820,7 @@ func TestOrder_Validate(t *testing.T) { { name: "order id is zero", Order: NewOrder(0), - exp: []string{"invalid order id: must not be zero"}, + exp: []string{"invalid order id", "must not be zero"}, }, { name: "nil sub-order", @@ -835,12 +835,12 @@ func TestOrder_Validate(t *testing.T) { { name: "ask order error", Order: NewOrder(1).WithAsk(&AskOrder{MarketId: 0, Price: zeroCoin}), - exp: []string{"invalid market id: must not be zero"}, + exp: []string{"invalid market id", "must not be zero"}, }, { name: "bid order error", Order: NewOrder(1).WithBid(&BidOrder{MarketId: 0, Price: zeroCoin}), - exp: []string{"invalid market id: must not be zero"}, + exp: []string{"invalid market id", "must not be zero"}, }, } @@ -1171,7 +1171,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: coins("99bender"), Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid market id: must not be zero"}, + exp: []string{"invalid market id", "must not be zero"}, }, { name: "invalid seller", @@ -1181,7 +1181,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: coins("99bender"), Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid seller: ", "invalid separator index -1"}, + exp: []string{"invalid seller", "invalid separator index -1"}, }, { name: "no seller", @@ -1191,7 +1191,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: coins("99bender"), Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid seller: ", "empty address string is not allowed"}, + exp: []string{"invalid seller", "empty address string is not allowed"}, }, { name: "zero price", @@ -1201,7 +1201,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: coins("99bender"), Price: *coin(0, "farnsworth"), }, - exp: []string{"invalid price: cannot be zero"}, + exp: []string{"invalid price", "cannot be zero"}, }, { name: "negative price", @@ -1211,7 +1211,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: coins("99bender"), Price: *coin(-24, "farnsworth"), }, - exp: []string{"invalid price: ", "negative coin amount: -24"}, + exp: []string{"invalid price", "negative coin amount: -24"}, }, { name: "invalid price denom", @@ -1221,7 +1221,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: coins("99bender"), Price: *coin(42, "7"), }, - exp: []string{"invalid price: ", "invalid denom: 7"}, + exp: []string{"invalid price", "invalid denom: 7"}, }, { name: "zero-value price", @@ -1231,7 +1231,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: coins("99bender"), Price: sdk.Coin{}, }, - exp: []string{"invalid price: invalid denom: "}, + exp: []string{"invalid price", "invalid denom"}, }, { name: "zero amount in assets", @@ -1241,7 +1241,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: sdk.Coins{*coin(99, "bender"), *coin(0, "leela"), *coin(1, "zoidberg")}, Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid assets: ", "coin leela amount is not positive"}, + exp: []string{"invalid assets", "coin leela amount is not positive"}, }, { name: "negative amount in assets", @@ -1251,7 +1251,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: sdk.Coins{*coin(99, "bender"), *coin(-1, "leela"), *coin(1, "zoidberg")}, Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid assets: ", "coin leela amount is not positive"}, + exp: []string{"invalid assets", "coin leela amount is not positive"}, }, { name: "invalid denom in assets", @@ -1261,7 +1261,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: sdk.Coins{*coin(99, "bender"), *coin(1, "x"), *coin(1, "zoidberg")}, Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid assets: ", "invalid denom: x"}, + exp: []string{"invalid assets", "invalid denom: x"}, }, { name: "nil assets", @@ -1271,7 +1271,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: nil, Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid assets: must not be empty"}, + exp: []string{"invalid assets", "must not be empty"}, }, { name: "empty assets", @@ -1281,7 +1281,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: sdk.Coins{}, Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid assets: must not be empty"}, + exp: []string{"invalid assets", "must not be empty"}, }, { name: "price denom in assets", @@ -1291,7 +1291,7 @@ func TestAskOrder_Validate(t *testing.T) { Assets: coins("99bender,2farnsworth,44amy"), Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid assets: cannot contain price denom farnsworth"}, + exp: []string{"invalid assets", "cannot contain price denom farnsworth"}, }, { name: "invalid seller settlement flat fee denom", @@ -1302,7 +1302,7 @@ func TestAskOrder_Validate(t *testing.T) { Price: *coin(42, "farnsworth"), SellerSettlementFlatFee: coin(13, "x"), }, - exp: []string{"invalid seller settlement flat fee: ", "invalid denom: x"}, + exp: []string{"invalid seller settlement flat fee", "invalid denom: x"}, }, { name: "zero seller settlement flat fee denom", @@ -1313,7 +1313,7 @@ func TestAskOrder_Validate(t *testing.T) { Price: *coin(42, "farnsworth"), SellerSettlementFlatFee: coin(0, "nibbler"), }, - exp: []string{"invalid seller settlement flat fee: ", "nibbler amount cannot be zero"}, + exp: []string{"invalid seller settlement flat fee", "nibbler amount cannot be zero"}, }, { name: "negative seller settlement flat fee", @@ -1324,7 +1324,7 @@ func TestAskOrder_Validate(t *testing.T) { Price: *coin(42, "farnsworth"), SellerSettlementFlatFee: coin(-3, "nibbler"), }, - exp: []string{"invalid seller settlement flat fee: ", "negative coin amount: -3"}, + exp: []string{"invalid seller settlement flat fee", "negative coin amount: -3"}, }, { name: "multiple problems", @@ -1333,11 +1333,11 @@ func TestAskOrder_Validate(t *testing.T) { SellerSettlementFlatFee: coin(0, ""), }, exp: []string{ - "invalid market id: ", - "invalid seller: ", - "invalid price: ", - "invalid assets: ", - "invalid seller settlement flat fee: ", + "invalid market id", + "invalid seller", + "invalid price", + "invalid assets", + "invalid seller settlement flat fee", }, }, } @@ -1404,28 +1404,21 @@ func TestBidOrder_GetOwner(t *testing.T) { } func TestBidOrder_GetAssets(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} - } + largeAmt, ok := sdkmath.NewIntFromString("25000000000000000000000") + require.Truef(t, ok, "NewIntFromString(\"25000000000000000000000\")") + largeCoin := sdk.NewCoin("large", largeAmt) + negCoin := sdk.Coin{Denom: "neg", Amount: sdkmath.NewInt(-88)} tests := []struct { name string order BidOrder exp sdk.Coins }{ - {name: "nil", order: BidOrder{Assets: nil}, exp: nil}, - {name: "empty", order: BidOrder{Assets: sdk.Coins{}}, exp: sdk.Coins{}}, - {name: "one denom", order: BidOrder{Assets: sdk.NewCoins(coin(3, "the"))}, exp: sdk.NewCoins(coin(3, "the"))}, - { - name: "three denoms", - order: BidOrder{Assets: sdk.NewCoins(coin(1, "one"), coin(2, "two"), coin(3, "three"))}, - exp: sdk.NewCoins(coin(1, "one"), coin(2, "two"), coin(3, "three")), - }, - { - name: "a negative coin", - order: BidOrder{Assets: sdk.Coins{coin(-1, "neg")}}, - exp: sdk.Coins{coin(-1, "neg")}, - }, + {name: "one", order: BidOrder{Assets: sdk.NewInt64Coin("one", 1)}, exp: sdk.Coins{sdk.NewInt64Coin("one", 1)}}, + {name: "zero", order: BidOrder{Assets: sdk.NewInt64Coin("zero", 0)}, exp: sdk.Coins{sdk.NewInt64Coin("zero", 0)}}, + {name: "negative", order: BidOrder{Assets: negCoin}, exp: sdk.Coins{negCoin}}, + {name: "large amount", order: BidOrder{Assets: largeCoin}, exp: sdk.Coins{largeCoin}}, + {name: "zero-value", order: BidOrder{Assets: sdk.Coin{}}, exp: sdk.Coins{sdk.Coin{}}}, } for _, tc := range tests { @@ -1625,7 +1618,7 @@ func TestBidOrder_Validate(t *testing.T) { order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("control_address_____").String(), - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: coin(42, "farnsworth"), BuyerSettlementFees: coins("1farnsworth"), AllowPartial: false, @@ -1637,7 +1630,7 @@ func TestBidOrder_Validate(t *testing.T) { order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("control_address_____").String(), - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: coin(42, "farnsworth"), BuyerSettlementFees: coins("1farnsworth"), AllowPartial: true, @@ -1649,7 +1642,7 @@ func TestBidOrder_Validate(t *testing.T) { order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("control_address_____").String(), - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: coin(42, "farnsworth"), BuyerSettlementFees: nil, AllowPartial: false, @@ -1661,29 +1654,19 @@ func TestBidOrder_Validate(t *testing.T) { order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("control_address_____").String(), - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: coin(42, "farnsworth"), BuyerSettlementFees: sdk.Coins{}, AllowPartial: false, }, exp: nil, }, - { - name: "multiple assets", - order: BidOrder{ - MarketId: 1, - Buyer: sdk.AccAddress("another_address_____").String(), - Assets: coins("12amy,99bender,8fry,112leela,1zoidberg"), - Price: coin(42, "farnsworth"), - }, - exp: nil, - }, { name: "market id zero", order: BidOrder{ MarketId: 0, Buyer: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: coin(42, "farnsworth"), }, exp: []string{"invalid market id: must not be zero"}, @@ -1693,142 +1676,132 @@ func TestBidOrder_Validate(t *testing.T) { order: BidOrder{ MarketId: 1, Buyer: "shady_address_______", - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: coin(42, "farnsworth"), }, - exp: []string{"invalid buyer: ", "invalid separator index -1"}, + exp: []string{"invalid buyer", "invalid separator index -1"}, }, { name: "no buyer", order: BidOrder{ MarketId: 1, Buyer: "", - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: coin(42, "farnsworth"), }, - exp: []string{"invalid buyer: ", "empty address string is not allowed"}, + exp: []string{"invalid buyer", "empty address string is not allowed"}, }, { name: "zero price", order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: coin(0, "farnsworth"), }, - exp: []string{"invalid price: cannot be zero"}, + exp: []string{"invalid price", "cannot be zero"}, }, { name: "negative price", order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: coin(-24, "farnsworth"), }, - exp: []string{"invalid price: ", "negative coin amount: -24"}, + exp: []string{"invalid price", "negative coin amount: -24"}, }, { name: "invalid price denom", order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: coin(42, "7"), }, - exp: []string{"invalid price: ", "invalid denom: 7"}, + exp: []string{"invalid price", "invalid denom: 7"}, }, { name: "zero-value price", order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("buyer_address_______").String(), - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: sdk.Coin{}, }, - exp: []string{"invalid price: invalid denom: "}, + exp: []string{"invalid price", "invalid denom"}, }, { name: "zero amount in assets", order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("another_address_____").String(), - Assets: sdk.Coins{coin(99, "bender"), coin(0, "leela"), coin(1, "zoidberg")}, + Assets: coin(0, "leela"), Price: coin(42, "farnsworth"), }, - exp: []string{"invalid assets: ", "coin leela amount is not positive"}, + exp: []string{"invalid assets", "cannot be zero"}, }, { name: "negative amount in assets", order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("another_address_____").String(), - Assets: sdk.Coins{coin(99, "bender"), coin(-1, "leela"), coin(1, "zoidberg")}, + Assets: coin(-1, "leela"), Price: coin(42, "farnsworth"), }, - exp: []string{"invalid assets: ", "coin leela amount is not positive"}, + exp: []string{"invalid assets", "negative coin amount: -1"}, }, { name: "invalid denom in assets", order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("another_address_____").String(), - Assets: sdk.Coins{coin(99, "bender"), coin(1, "x"), coin(1, "zoidberg")}, - Price: coin(42, "farnsworth"), - }, - exp: []string{"invalid assets: ", "invalid denom: x"}, - }, - { - name: "nil assets", - order: BidOrder{ - MarketId: 1, - Buyer: sdk.AccAddress("another_address_____").String(), - Assets: nil, + Assets: coin(1, "x"), Price: coin(42, "farnsworth"), }, - exp: []string{"invalid assets: must not be empty"}, + exp: []string{"invalid assets", "invalid denom: x"}, }, { - name: "empty assets", + name: "zero-value assets", order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("another_address_____").String(), - Assets: sdk.Coins{}, + Assets: sdk.Coin{}, Price: coin(42, "farnsworth"), }, - exp: []string{"invalid assets: must not be empty"}, + exp: []string{"invalid assets", "invalid denom"}, }, { name: "price denom in assets", order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender,2farnsworth,44amy"), + Assets: coin(2, "farnsworth"), Price: coin(42, "farnsworth"), }, - exp: []string{"invalid assets: cannot contain price denom farnsworth"}, + exp: []string{"invalid assets", "price denom farnsworth cannot also be the assets denom"}, }, { name: "invalid denom in buyer settlement fees", order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: coin(42, "farnsworth"), BuyerSettlementFees: sdk.Coins{coin(1, "farnsworth"), coin(2, "x")}, }, - exp: []string{"invalid buyer settlement fees: ", "invalid denom: x"}, + exp: []string{"invalid buyer settlement fees", "invalid denom: x"}, }, { name: "negative buyer settlement fees", order: BidOrder{ MarketId: 1, Buyer: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: coin(99, "bender"), Price: coin(42, "farnsworth"), BuyerSettlementFees: sdk.Coins{coin(3, "farnsworth"), coin(-3, "nibbler")}, }, - exp: []string{"invalid buyer settlement fees: ", "coin nibbler amount is not positive"}, + exp: []string{"invalid buyer settlement fees", "coin nibbler amount is not positive"}, }, { name: "multiple problems", @@ -1837,11 +1810,11 @@ func TestBidOrder_Validate(t *testing.T) { BuyerSettlementFees: sdk.Coins{coin(0, "")}, }, exp: []string{ - "invalid market id: ", - "invalid buyer: ", - "invalid price: ", - "invalid assets: ", - "invalid buyer settlement fees: ", + "invalid market id", + "invalid buyer", + "invalid price", + "invalid assets", + "invalid buyer settlement fees", }, }, } From a05332954fd1e58a2a6ce385cf9e1570b367d27e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 22 Sep 2023 13:59:44 -0600 Subject: [PATCH 164/309] [1658]: Split out some of the create order validation for easier use elsewhere. --- x/exchange/keeper/fulfillment.go | 341 ++++++++++++++++++++++++++ x/exchange/keeper/orders.go | 408 ++++--------------------------- 2 files changed, 390 insertions(+), 359 deletions(-) create mode 100644 x/exchange/keeper/fulfillment.go diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go new file mode 100644 index 0000000000..858f22147a --- /dev/null +++ b/x/exchange/keeper/fulfillment.go @@ -0,0 +1,341 @@ +package keeper + +import ( + "errors" + "fmt" + + "github.com/gogo/protobuf/proto" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/provenance-io/provenance/x/exchange" +) + +// sumAssetsAndPrice gets the sum of assets, and the sum of prices of the provided orders. +func sumAssetsAndPrice(orders []*exchange.Order) (sdk.Coins, sdk.Coins) { + var totalAssets, totalPrice sdk.Coins + for _, order := range orders { + totalAssets = totalAssets.Add(order.GetAssets()...) + totalPrice = totalPrice.Add(order.GetPrice()) + } + return totalAssets, totalPrice +} + +// validateAcceptingOrdersAndCanUserSettle returns an error if the market isn't active or doesn't allow user settlement. +func validateAcceptingOrdersAndCanUserSettle(store sdk.KVStore, marketID uint32) error { + if err := validateMarketIsAcceptingOrders(store, marketID); err != nil { + return err + } + if !isUserSettlementAllowed(store, marketID) { + return fmt.Errorf("market %d does not allow user settlement", marketID) + } + return nil +} + +// FillBids settles one or more bid orders for a seller. +func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) error { + if err := msg.ValidateBasic(); err != nil { + return err + } + + marketID := msg.MarketId + store := k.getStore(ctx) + + if err := validateAcceptingOrdersAndCanUserSettle(store, marketID); err != nil { + return err + } + seller := sdk.MustAccAddressFromBech32(msg.Seller) + if err := k.validateCanCreateAsk(ctx, marketID, seller); err != nil { + return err + } + if err := validateCreateAskFees(store, marketID, msg.AskOrderCreationFee, msg.SellerSettlementFlatFee); err != nil { + return err + } + + orders, oerrs := k.getBidOrders(store, marketID, msg.BidOrderIds, msg.Seller) + if oerrs != nil { + return oerrs + } + + var errs []error + var totalSellerFee sdk.Coins + assetOutputs := make([]banktypes.Output, 0, len(msg.BidOrderIds)) + priceInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)) + addrIndex := make(map[string]int) + feeInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)+1) + feeAddrIndex := make(map[string]int) + for _, order := range orders { + bidOrder := order.GetBidOrder() + buyer := bidOrder.Buyer + assets := bidOrder.Assets + price := bidOrder.Price + buyerSettlementFees := bidOrder.BuyerSettlementFees + + sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, price) + if rerr != nil { + errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", + order.OrderId, rerr)) + continue + } + if err := k.releaseHoldOnOrder(ctx, order); err != nil { + errs = append(errs, err) + continue + } + + if sellerRatioFee != nil { + totalSellerFee = totalSellerFee.Add(*sellerRatioFee) + } + + i, seen := addrIndex[buyer] + if !seen { + i = len(assetOutputs) + addrIndex[buyer] = i + assetOutputs = append(assetOutputs, banktypes.Output{Address: buyer}) + priceInputs = append(priceInputs, banktypes.Input{Address: buyer}) + } + assetOutputs[i].Coins = assetOutputs[i].Coins.Add(assets) + priceInputs[i].Coins = priceInputs[i].Coins.Add(price) + + if !buyerSettlementFees.IsZero() { + j, known := feeAddrIndex[buyer] + if !known { + j = len(feeInputs) + feeAddrIndex[buyer] = j + feeInputs = append(feeInputs, banktypes.Input{Address: buyer}) + } + feeInputs[j].Coins = feeInputs[j].Coins.Add(buyerSettlementFees...) + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + + totalAssets, totalPrice := sumAssetsAndPrice(orders) + + if !exchange.CoinsEquals(totalAssets, msg.TotalAssets) { + return fmt.Errorf("total assets %q does not equal sum of bid order assets %q", msg.TotalAssets, totalAssets) + } + + if msg.SellerSettlementFlatFee != nil { + totalSellerFee = totalSellerFee.Add(*msg.SellerSettlementFlatFee) + } + if !totalSellerFee.IsZero() { + feeInputs = append(feeInputs, banktypes.Input{Address: msg.Seller, Coins: totalSellerFee}) + } + + assetInputs := []banktypes.Input{{Address: msg.Seller, Coins: msg.TotalAssets}} + priceOutputs := []banktypes.Output{{Address: msg.Seller, Coins: totalPrice}} + + if err := k.bankKeeper.InputOutputCoins(ctx, assetInputs, assetOutputs); err != nil { + return fmt.Errorf("error transferring assets from seller to buyers: %w", err) + } + + if err := k.bankKeeper.InputOutputCoins(ctx, priceInputs, priceOutputs); err != nil { + return fmt.Errorf("error transferring price from buyers to seller: %w", err) + } + + if err := k.CollectFees(ctx, feeInputs, marketID); err != nil { + return fmt.Errorf("error collecting settlement fees: %w", err) + } + + // Collected last so that it's easier for a seller to fill bids without needing those funds first. + // Collected separately so it's not combined with the seller settlement fees in the events. + if msg.AskOrderCreationFee != nil { + if err := k.CollectFee(ctx, seller, marketID, sdk.Coins{*msg.AskOrderCreationFee}); err != nil { + return fmt.Errorf("error collecting create-ask fee %q: %w", msg.AskOrderCreationFee, err) + } + } + + events := make([]proto.Message, len(orders)) + for i, order := range orders { + deleteAndDeIndexOrder(store, *order) + events[i] = exchange.NewEventOrderFilled(order.OrderId) + } + + return ctx.EventManager().EmitTypedEvents(events...) +} + +// FillAsks settles one or more ask orders for a buyer. +func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) error { + if err := msg.ValidateBasic(); err != nil { + return err + } + + marketID := msg.MarketId + store := k.getStore(ctx) + + if err := validateAcceptingOrdersAndCanUserSettle(store, marketID); err != nil { + return err + } + buyer, serr := sdk.AccAddressFromBech32(msg.Buyer) + if serr != nil { + return fmt.Errorf("invalid buyer %q: %w", msg.Buyer, serr) + } + if err := k.validateCanCreateBid(ctx, marketID, buyer); err != nil { + return err + } + if err := validateCreateBidFees(store, marketID, msg.BidOrderCreationFee, msg.TotalPrice, msg.BuyerSettlementFees); err != nil { + return err + } + + orders, oerrs := k.getAskOrders(store, marketID, msg.AskOrderIds, msg.Buyer) + if oerrs != nil { + return oerrs + } + + var errs []error + assetInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)) + priceOutputs := make([]banktypes.Output, 0, len(msg.AskOrderIds)) + addrIndex := make(map[string]int) + feeInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)+1) + feeAddrIndex := make(map[string]int) + for _, order := range orders { + askOrder := order.GetAskOrder() + seller := askOrder.Seller + assets := askOrder.Assets + price := askOrder.Price + sellerSettlementFlatFee := askOrder.SellerSettlementFlatFee + + sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, price) + if rerr != nil { + errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", + order.OrderId, rerr)) + continue + } + if err := k.releaseHoldOnOrder(ctx, order); err != nil { + errs = append(errs, err) + continue + } + + var totalSellerFee sdk.Coins + if sellerSettlementFlatFee != nil && !sellerSettlementFlatFee.IsZero() { + totalSellerFee = totalSellerFee.Add(*sellerSettlementFlatFee) + } + if sellerRatioFee != nil && !sellerRatioFee.IsZero() { + totalSellerFee = totalSellerFee.Add(*sellerRatioFee) + } + + i, seen := addrIndex[seller] + if !seen { + i = len(assetInputs) + addrIndex[seller] = i + assetInputs = append(assetInputs, banktypes.Input{Address: seller}) + priceOutputs = append(priceOutputs, banktypes.Output{Address: seller}) + } + assetInputs[i].Coins = assetInputs[i].Coins.Add(assets...) + priceOutputs[i].Coins = priceOutputs[i].Coins.Add(price) + + if !totalSellerFee.IsZero() { + j, known := feeAddrIndex[seller] + if !known { + j = len(feeInputs) + feeAddrIndex[seller] = j + feeInputs = append(feeInputs, banktypes.Input{Address: seller}) + } + feeInputs[j].Coins = feeInputs[j].Coins.Add(totalSellerFee...) + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + + totalAssets, totalPrice := sumAssetsAndPrice(orders) + + if !exchange.CoinsEquals(totalPrice, sdk.Coins{msg.TotalPrice}) { + return fmt.Errorf("total price %q does not equal sum of ask order prices %q", msg.TotalPrice, totalPrice) + } + + if !msg.BuyerSettlementFees.IsZero() { + feeInputs = append(feeInputs, banktypes.Input{Address: msg.Buyer, Coins: msg.BuyerSettlementFees}) + } + + assetOutputs := []banktypes.Output{{Address: msg.Buyer, Coins: totalAssets}} + priceInputs := []banktypes.Input{{Address: msg.Buyer, Coins: sdk.Coins{msg.TotalPrice}}} + + if err := k.bankKeeper.InputOutputCoins(ctx, assetInputs, assetOutputs); err != nil { + return fmt.Errorf("error transferring assets from sellers to buyer: %w", err) + } + + if err := k.bankKeeper.InputOutputCoins(ctx, priceInputs, priceOutputs); err != nil { + return fmt.Errorf("error transferring price from buyer to sellers: %w", err) + } + + if err := k.CollectFees(ctx, feeInputs, marketID); err != nil { + return fmt.Errorf("error collecting settlement fees: %w", err) + } + + // Collected last so that it's easier for a seller to fill asks without needing those funds first. + // Collected separately so it's not combined with the buyer settlement fees in the events. + if msg.BidOrderCreationFee != nil { + if err := k.CollectFee(ctx, buyer, marketID, sdk.Coins{*msg.BidOrderCreationFee}); err != nil { + return fmt.Errorf("error collecting create-ask fee %q: %w", msg.BidOrderCreationFee, err) + } + } + + events := make([]proto.Message, len(orders)) + for i, order := range orders { + deleteAndDeIndexOrder(store, *order) + events[i] = exchange.NewEventOrderFilled(order.OrderId) + } + + return ctx.EventManager().EmitTypedEvents(events...) +} + +// SettleOrders attempts to settle all the provided orders. +func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidOrderIds []uint64, expectPartial bool) error { + store := k.getStore(ctx) + if err := validateMarketExists(store, marketID); err != nil { + return err + } + + askOrders, aoerr := k.getAskOrders(store, marketID, askOrderIDs, "") + bidOrders, boerr := k.getBidOrders(store, marketID, bidOrderIds, "") + if aoerr != nil || boerr != nil { + return errors.Join(aoerr, boerr) + } + + totalAssetsForSale, totalAskPrice := sumAssetsAndPrice(askOrders) + totalAssetsToBuy, totalBidPrice := sumAssetsAndPrice(bidOrders) + + // TODO[1659]: Allow for multiple asset denoms in some cases. + + var errs []error + if len(totalAssetsForSale) != 1 { + errs = append(errs, fmt.Errorf("cannot settle with multiple ask order asset denoms %q", totalAssetsForSale)) + } + if len(totalAskPrice) != 1 { + errs = append(errs, fmt.Errorf("cannot settle with multiple ask order price denoms %q", totalAskPrice)) + } + if len(totalAssetsToBuy) != 1 { + errs = append(errs, fmt.Errorf("cannot settle with multiple bid order asset denoms %q", totalAssetsToBuy)) + } + if len(totalBidPrice) != 1 { + errs = append(errs, fmt.Errorf("cannot settle with multiple bid order price denoms %q", totalBidPrice)) + } + if len(errs) > 0 { + return errors.Join(errs...) + } + + if totalAssetsForSale[0].Denom != totalAssetsToBuy[0].Denom { + errs = append(errs, fmt.Errorf("cannot settle different ask %q and bid %q asset denoms", + totalAssetsForSale, totalAssetsToBuy)) + } + if totalAskPrice[0].Denom != totalBidPrice[0].Denom { + errs = append(errs, fmt.Errorf("cannot settle different ask %q and bid %q price denoms", + totalAskPrice, totalBidPrice)) + } + if len(errs) > 0 { + return errors.Join(errs...) + } + + if !expectPartial && !exchange.CoinsEquals(totalAssetsForSale, totalAssetsToBuy) { + return fmt.Errorf("total assets for sale %q does not equal total assets to buy %q", + totalAssetsForSale, totalAssetsToBuy) + } + + // TODO[1658]: Implement SettleOrders. + panic("Not implemented") +} diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 50ae794c3d..96a5edd244 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -4,11 +4,8 @@ import ( "errors" "fmt" - "github.com/gogo/protobuf/proto" - "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/provenance-io/provenance/x/exchange" ) @@ -269,6 +266,33 @@ func (k Keeper) releaseHoldOnOrder(ctx sdk.Context, order *exchange.Order) error return nil } +// validateMarketIsAcceptingOrders makes sure the market exists and is accepting orders. +func validateMarketIsAcceptingOrders(store sdk.KVStore, marketID uint32) error { + if err := validateMarketExists(store, marketID); err != nil { + return err + } + if !isMarketActive(store, marketID) { + return fmt.Errorf("market %d is not accepting orders", marketID) + } + return nil +} + +// validateCanCreateAsk makes sure the user can create an ask order in the given market. +func (k Keeper) validateCanCreateAsk(ctx sdk.Context, marketID uint32, seller sdk.AccAddress) error { + if !k.CanCreateAsk(ctx, marketID, seller) { + return fmt.Errorf("account %s is not allowed to create ask orders in market %d", seller, marketID) + } + return nil +} + +// validateCreateAskFees makes sure the fees are okay for creating an ask order. +func validateCreateAskFees(store sdk.KVStore, marketID uint32, creationFee *sdk.Coin, settlementFlatFee *sdk.Coin) error { + if err := validateCreateAskFlatFee(store, marketID, creationFee); err != nil { + return err + } + return validateSellerSettlementFlatFee(store, marketID, settlementFlatFee) +} + // CreateAskOrder creates an ask order, collects the creation fee, and places all needed holds. func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, creationFee *sdk.Coin) (uint64, error) { if err := askOrder.Validate(); err != nil { @@ -278,20 +302,14 @@ func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, crea store := k.getStore(ctx) marketID := askOrder.MarketId - if err := validateMarketExists(store, marketID); err != nil { + if err := validateMarketIsAcceptingOrders(store, marketID); err != nil { return 0, err } - if !isMarketActive(store, marketID) { - return 0, fmt.Errorf("market %d is not accepting orders", marketID) - } seller := sdk.MustAccAddressFromBech32(askOrder.Seller) - if !k.CanCreateAsk(ctx, marketID, seller) { - return 0, fmt.Errorf("account %s is not allowed to create ask orders in market %d", seller, marketID) - } - if err := validateCreateAskFlatFee(store, marketID, creationFee); err != nil { + if err := k.validateCanCreateAsk(ctx, marketID, seller); err != nil { return 0, err } - if err := validateSellerSettlementFlatFee(store, marketID, askOrder.SellerSettlementFlatFee); err != nil { + if err := validateCreateAskFees(store, marketID, creationFee, askOrder.SellerSettlementFlatFee); err != nil { return 0, err } if err := validateAskPrice(store, marketID, askOrder.Price, askOrder.SellerSettlementFlatFee); err != nil { @@ -318,6 +336,22 @@ func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, crea return orderID, ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCreated(order)) } +// validateCanCreateBid makes sure the user can create a bid order in the given market. +func (k Keeper) validateCanCreateBid(ctx sdk.Context, marketID uint32, buyer sdk.AccAddress) error { + if !k.CanCreateBid(ctx, marketID, buyer) { + return fmt.Errorf("account %s is not allowed to create bid orders in market %d", buyer, marketID) + } + return nil +} + +// validateCreateBidFees makes sure the fees are okay for creating a bid order. +func validateCreateBidFees(store sdk.KVStore, marketID uint32, creationFee *sdk.Coin, price sdk.Coin, settlementFees sdk.Coins) error { + if err := validateCreateBidFlatFee(store, marketID, creationFee); err != nil { + return err + } + return validateBuyerSettlementFee(store, marketID, price, settlementFees) +} + // CreateBidOrder creates a bid order, collects the creation fee, and places all needed holds. func (k Keeper) CreateBidOrder(ctx sdk.Context, bidOrder exchange.BidOrder, creationFee *sdk.Coin) (uint64, error) { if err := bidOrder.Validate(); err != nil { @@ -327,20 +361,14 @@ func (k Keeper) CreateBidOrder(ctx sdk.Context, bidOrder exchange.BidOrder, crea store := k.getStore(ctx) marketID := bidOrder.MarketId - if err := validateMarketExists(store, marketID); err != nil { + if err := validateMarketIsAcceptingOrders(store, marketID); err != nil { return 0, err } - if !isMarketActive(store, marketID) { - return 0, fmt.Errorf("market %d is not accepting orders", marketID) - } buyer := sdk.MustAccAddressFromBech32(bidOrder.Buyer) - if !k.CanCreateBid(ctx, marketID, buyer) { - return 0, fmt.Errorf("account %s is not allowed to create bid orders in market %d", buyer, marketID) - } - if err := validateCreateBidFlatFee(store, marketID, creationFee); err != nil { + if err := k.validateCanCreateBid(ctx, marketID, buyer); err != nil { return 0, err } - if err := validateBuyerSettlementFee(store, marketID, bidOrder.Price, bidOrder.BuyerSettlementFees); err != nil { + if err := validateCreateBidFees(store, marketID, creationFee, bidOrder.Price, bidOrder.BuyerSettlementFees); err != nil { return 0, err } @@ -471,341 +499,3 @@ func (k Keeper) getAskOrders(store sdk.KVStore, marketID uint32, orderIDs []uint return orders, errors.Join(errs...) } - -// sumAssetsAndPrice gets the sum of assets, and the sum of prices of the provided orders. -func sumAssetsAndPrice(orders []*exchange.Order) (sdk.Coins, sdk.Coins) { - var totalAssets, totalPrice sdk.Coins - for _, order := range orders { - totalAssets = totalAssets.Add(order.GetAssets()...) - totalPrice = totalPrice.Add(order.GetPrice()) - } - return totalAssets, totalPrice -} - -// FillBids settles one or more bid orders for a seller. -func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) error { - if err := msg.ValidateBasic(); err != nil { - return err - } - - marketID := msg.MarketId - store := k.getStore(ctx) - - if err := validateMarketExists(store, marketID); err != nil { - return err - } - if !isMarketActive(store, marketID) { - return fmt.Errorf("market %d is not accepting orders", marketID) - } - if !isUserSettlementAllowed(store, marketID) { - return fmt.Errorf("market %d does not allow user settlement", marketID) - } - seller, serr := sdk.AccAddressFromBech32(msg.Seller) - if serr != nil { - return fmt.Errorf("invalid seller %q: %w", msg.Seller, serr) - } - if !k.CanCreateAsk(ctx, marketID, seller) { - return fmt.Errorf("account %s is not allowed to create ask orders in market %d", seller, marketID) - } - if err := validateCreateAskFlatFee(store, marketID, msg.AskOrderCreationFee); err != nil { - return err - } - if err := validateSellerSettlementFlatFee(store, marketID, msg.SellerSettlementFlatFee); err != nil { - return err - } - - orders, oerrs := k.getBidOrders(store, marketID, msg.BidOrderIds, msg.Seller) - if oerrs != nil { - return oerrs - } - - var errs []error - var totalSellerFee sdk.Coins - assetOutputs := make([]banktypes.Output, 0, len(msg.BidOrderIds)) - priceInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)) - addrIndex := make(map[string]int) - feeInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)+1) - feeAddrIndex := make(map[string]int) - for _, order := range orders { - bidOrder := order.GetBidOrder() - buyer := bidOrder.Buyer - assets := bidOrder.Assets - price := bidOrder.Price - buyerSettlementFees := bidOrder.BuyerSettlementFees - - sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, price) - if rerr != nil { - errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", - order.OrderId, rerr)) - continue - } - if err := k.releaseHoldOnOrder(ctx, order); err != nil { - errs = append(errs, err) - continue - } - - if sellerRatioFee != nil { - totalSellerFee = totalSellerFee.Add(*sellerRatioFee) - } - - i, seen := addrIndex[buyer] - if !seen { - i = len(assetOutputs) - addrIndex[buyer] = i - assetOutputs = append(assetOutputs, banktypes.Output{Address: buyer}) - priceInputs = append(priceInputs, banktypes.Input{Address: buyer}) - } - assetOutputs[i].Coins = assetOutputs[i].Coins.Add(assets) - priceInputs[i].Coins = priceInputs[i].Coins.Add(price) - - if !buyerSettlementFees.IsZero() { - j, known := feeAddrIndex[buyer] - if !known { - j = len(feeInputs) - feeAddrIndex[buyer] = j - feeInputs = append(feeInputs, banktypes.Input{Address: buyer}) - } - feeInputs[j].Coins = feeInputs[j].Coins.Add(buyerSettlementFees...) - } - } - - if len(errs) > 0 { - return errors.Join(errs...) - } - - totalAssets, totalPrice := sumAssetsAndPrice(orders) - - if !exchange.CoinsEquals(totalAssets, msg.TotalAssets) { - return fmt.Errorf("total assets %q does not equal sum of bid order assets %q", msg.TotalAssets, totalAssets) - } - - if msg.SellerSettlementFlatFee != nil { - totalSellerFee = totalSellerFee.Add(*msg.SellerSettlementFlatFee) - } - if !totalSellerFee.IsZero() { - feeInputs = append(feeInputs, banktypes.Input{Address: msg.Seller, Coins: totalSellerFee}) - } - - assetInputs := []banktypes.Input{{Address: msg.Seller, Coins: msg.TotalAssets}} - priceOutputs := []banktypes.Output{{Address: msg.Seller, Coins: totalPrice}} - - if err := k.bankKeeper.InputOutputCoins(ctx, assetInputs, assetOutputs); err != nil { - return fmt.Errorf("error transferring assets from seller to buyers: %w", err) - } - - if err := k.bankKeeper.InputOutputCoins(ctx, priceInputs, priceOutputs); err != nil { - return fmt.Errorf("error transferring price from buyers to seller: %w", err) - } - - if err := k.CollectFees(ctx, feeInputs, marketID); err != nil { - return fmt.Errorf("error collecting settlement fees: %w", err) - } - - // Collected last so that it's easier for a seller to fill bids without needing those funds first. - // Collected separately so it's not combined with the seller settlement fees in the events. - if msg.AskOrderCreationFee != nil { - if err := k.CollectFee(ctx, seller, marketID, sdk.Coins{*msg.AskOrderCreationFee}); err != nil { - return fmt.Errorf("error collecting create-ask fee %q: %w", msg.AskOrderCreationFee, err) - } - } - - events := make([]proto.Message, len(orders)) - for i, order := range orders { - deleteAndDeIndexOrder(store, *order) - events[i] = exchange.NewEventOrderFilled(order.OrderId) - } - - return ctx.EventManager().EmitTypedEvents(events...) -} - -// FillAsks settles one or more ask orders for a buyer. -func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) error { - if err := msg.ValidateBasic(); err != nil { - return err - } - - marketID := msg.MarketId - store := k.getStore(ctx) - - if err := validateMarketExists(store, marketID); err != nil { - return err - } - if !isMarketActive(store, marketID) { - return fmt.Errorf("market %d is not accepting orders", marketID) - } - if !isUserSettlementAllowed(store, marketID) { - return fmt.Errorf("market %d does not allow user settlement", marketID) - } - buyer, serr := sdk.AccAddressFromBech32(msg.Buyer) - if serr != nil { - return fmt.Errorf("invalid buyer %q: %w", msg.Buyer, serr) - } - if !k.CanCreateBid(ctx, marketID, buyer) { - return fmt.Errorf("account %s is not allowed to create bid orders in market %d", buyer, marketID) - } - if err := validateCreateBidFlatFee(store, marketID, msg.BidOrderCreationFee); err != nil { - return err - } - if err := validateBuyerSettlementFee(store, marketID, msg.TotalPrice, msg.BuyerSettlementFees); err != nil { - return err - } - - orders, oerrs := k.getAskOrders(store, marketID, msg.AskOrderIds, msg.Buyer) - if oerrs != nil { - return oerrs - } - - var errs []error - assetInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)) - priceOutputs := make([]banktypes.Output, 0, len(msg.AskOrderIds)) - addrIndex := make(map[string]int) - feeInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)+1) - feeAddrIndex := make(map[string]int) - for _, order := range orders { - askOrder := order.GetAskOrder() - seller := askOrder.Seller - assets := askOrder.Assets - price := askOrder.Price - sellerSettlementFlatFee := askOrder.SellerSettlementFlatFee - - sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, price) - if rerr != nil { - errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", - order.OrderId, rerr)) - continue - } - if err := k.releaseHoldOnOrder(ctx, order); err != nil { - errs = append(errs, err) - continue - } - - var totalSellerFee sdk.Coins - if sellerSettlementFlatFee != nil && !sellerSettlementFlatFee.IsZero() { - totalSellerFee = totalSellerFee.Add(*sellerSettlementFlatFee) - } - if sellerRatioFee != nil && !sellerRatioFee.IsZero() { - totalSellerFee = totalSellerFee.Add(*sellerRatioFee) - } - - i, seen := addrIndex[seller] - if !seen { - i = len(assetInputs) - addrIndex[seller] = i - assetInputs = append(assetInputs, banktypes.Input{Address: seller}) - priceOutputs = append(priceOutputs, banktypes.Output{Address: seller}) - } - assetInputs[i].Coins = assetInputs[i].Coins.Add(assets...) - priceOutputs[i].Coins = priceOutputs[i].Coins.Add(price) - - if !totalSellerFee.IsZero() { - j, known := feeAddrIndex[seller] - if !known { - j = len(feeInputs) - feeAddrIndex[seller] = j - feeInputs = append(feeInputs, banktypes.Input{Address: seller}) - } - feeInputs[j].Coins = feeInputs[j].Coins.Add(totalSellerFee...) - } - } - - if len(errs) > 0 { - return errors.Join(errs...) - } - - totalAssets, totalPrice := sumAssetsAndPrice(orders) - - if !exchange.CoinsEquals(totalPrice, sdk.Coins{msg.TotalPrice}) { - return fmt.Errorf("total price %q does not equal sum of ask order prices %q", msg.TotalPrice, totalPrice) - } - - if !msg.BuyerSettlementFees.IsZero() { - feeInputs = append(feeInputs, banktypes.Input{Address: msg.Buyer, Coins: msg.BuyerSettlementFees}) - } - - assetOutputs := []banktypes.Output{{Address: msg.Buyer, Coins: totalAssets}} - priceInputs := []banktypes.Input{{Address: msg.Buyer, Coins: sdk.Coins{msg.TotalPrice}}} - - if err := k.bankKeeper.InputOutputCoins(ctx, assetInputs, assetOutputs); err != nil { - return fmt.Errorf("error transferring assets from sellers to buyer: %w", err) - } - - if err := k.bankKeeper.InputOutputCoins(ctx, priceInputs, priceOutputs); err != nil { - return fmt.Errorf("error transferring price from buyer to sellers: %w", err) - } - - if err := k.CollectFees(ctx, feeInputs, marketID); err != nil { - return fmt.Errorf("error collecting settlement fees: %w", err) - } - - // Collected last so that it's easier for a seller to fill asks without needing those funds first. - // Collected separately so it's not combined with the buyer settlement fees in the events. - if msg.BidOrderCreationFee != nil { - if err := k.CollectFee(ctx, buyer, marketID, sdk.Coins{*msg.BidOrderCreationFee}); err != nil { - return fmt.Errorf("error collecting create-ask fee %q: %w", msg.BidOrderCreationFee, err) - } - } - - events := make([]proto.Message, len(orders)) - for i, order := range orders { - deleteAndDeIndexOrder(store, *order) - events[i] = exchange.NewEventOrderFilled(order.OrderId) - } - - return ctx.EventManager().EmitTypedEvents(events...) -} - -// SettleOrders attempts to settle all the provided orders. -func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidOrderIds []uint64, expectPartial bool) error { - store := k.getStore(ctx) - if err := validateMarketExists(store, marketID); err != nil { - return err - } - - askOrders, aoerr := k.getAskOrders(store, marketID, askOrderIDs, "") - bidOrders, boerr := k.getBidOrders(store, marketID, bidOrderIds, "") - if aoerr != nil || boerr != nil { - return errors.Join(aoerr, boerr) - } - - totalAssetsForSale, totalAskPrice := sumAssetsAndPrice(askOrders) - totalAssetsToBuy, totalBidPrice := sumAssetsAndPrice(bidOrders) - - // TODO[1659]: Allow for multiple asset denoms in some cases. - - var errs []error - if len(totalAssetsForSale) != 1 { - errs = append(errs, fmt.Errorf("cannot settle with multiple ask order asset denoms %q", totalAssetsForSale)) - } - if len(totalAskPrice) != 1 { - errs = append(errs, fmt.Errorf("cannot settle with multiple ask order price denoms %q", totalAskPrice)) - } - if len(totalAssetsToBuy) != 1 { - errs = append(errs, fmt.Errorf("cannot settle with multiple bid order asset denoms %q", totalAssetsToBuy)) - } - if len(totalBidPrice) != 1 { - errs = append(errs, fmt.Errorf("cannot settle with multiple bid order price denoms %q", totalBidPrice)) - } - if len(errs) > 0 { - return errors.Join(errs...) - } - - if totalAssetsForSale[0].Denom != totalAssetsToBuy[0].Denom { - errs = append(errs, fmt.Errorf("cannot settle different ask %q and bid %q asset denoms", - totalAssetsForSale, totalAssetsToBuy)) - } - if totalAskPrice[0].Denom != totalBidPrice[0].Denom { - errs = append(errs, fmt.Errorf("cannot settle different ask %q and bid %q price denoms", - totalAskPrice, totalBidPrice)) - } - if len(errs) > 0 { - return errors.Join(errs...) - } - - if !expectPartial && !exchange.CoinsEquals(totalAssetsForSale, totalAssetsToBuy) { - return fmt.Errorf("total assets for sale %q does not equal total assets to buy %q", - totalAssetsForSale, totalAssetsToBuy) - } - - // TODO[1658]: Implement SettleOrders. - panic("Not implemented") -} From 90469f7b89489d3f5a157dddc8eabba8687c7999 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 22 Sep 2023 14:20:21 -0600 Subject: [PATCH 165/309] [1658]: Tweak partial expected error messages. --- x/exchange/keeper/fulfillment.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index 858f22147a..dd276013dc 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -332,7 +332,11 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO } if !expectPartial && !exchange.CoinsEquals(totalAssetsForSale, totalAssetsToBuy) { - return fmt.Errorf("total assets for sale %q does not equal total assets to buy %q", + return fmt.Errorf("total assets for sale %q does not equal total assets to buy %q and partial settlement not expected", + totalAssetsForSale, totalAssetsToBuy) + } + if expectPartial && exchange.CoinsEquals(totalAssetsForSale, totalAssetsToBuy) { + return fmt.Errorf("total assets for sale %q equals total assets to buy %q but partial settlement is expected", totalAssetsForSale, totalAssetsToBuy) } From 0b6c80403289ad1f0a70246563fc27e93f008a3c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 22 Sep 2023 15:24:18 -0600 Subject: [PATCH 166/309] [1658]: Make AskOrder assets a single Coin too. --- docs/proto-docs.md | 2 +- proto/provenance/exchange/v1/orders.proto | 3 +- x/exchange/fulfillment.go | 277 +++++++++------------- x/exchange/genesis_test.go | 15 +- x/exchange/keeper/fulfillment.go | 4 +- x/exchange/keeper/orders.go | 13 +- x/exchange/msg_test.go | 2 +- x/exchange/orders.go | 71 +++--- x/exchange/orders.pb.go | 102 ++++---- x/exchange/orders_test.go | 148 +++++------- 10 files changed, 255 insertions(+), 382 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 9ebabd2313..85bbfa2129 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1746,7 +1746,7 @@ AskOrder represents someone's desire to sell something at a minimum price. | ----- | ---- | ----- | ----------- | | `market_id` | [uint32](#uint32) | | market_id identifies the market that this order belongs to. | | `seller` | [string](#string) | | seller is the address of the account that owns this order and has the assets to sell. | -| `assets` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | assets are the things that the seller wishes to sell. A hold is placed on this until the order is filled or cancelled. | +| `assets` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | assets are the things that the seller wishes to sell. A hold is placed on this until the order is filled or cancelled. | | `price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | price is the minimum amount that the seller is willing to accept for the assets. The seller's settlement proportional fee (and possibly the settlement flat fee) is taken out of the amount the seller receives, so it's possible that the seller will still receive less than this price. | | `seller_settlement_flat_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. If this denom is the same denom as the price, it will come out of the actual price received. If this denom is different, the amount must be in the seller's account and a hold is placed on it until the order is filled or cancelled. | | `allow_partial` | [bool](#bool) | | allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the order must be either filled in full or not filled at all. | diff --git a/proto/provenance/exchange/v1/orders.proto b/proto/provenance/exchange/v1/orders.proto index 36dea50dfb..29228ce166 100644 --- a/proto/provenance/exchange/v1/orders.proto +++ b/proto/provenance/exchange/v1/orders.proto @@ -35,8 +35,7 @@ message AskOrder { string seller = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // assets are the things that the seller wishes to sell. // A hold is placed on this until the order is filled or cancelled. - repeated cosmos.base.v1beta1.Coin assets = 3 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + cosmos.base.v1beta1.Coin assets = 3 [(gogoproto.nullable) = false]; // price is the minimum amount that the seller is willing to accept for the assets. The seller's settlement // proportional fee (and possibly the settlement flat fee) is taken out of the amount the seller receives, // so it's possible that the seller will still receive less than this price. diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 9c55fd2692..c9766ebc25 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -24,7 +24,7 @@ func NewIndexedAddrAmts() *IndexedAddrAmts { } // Add adds the coins to the input with the given address (creating it if needed). -func (i *IndexedAddrAmts) Add(addr string, coins sdk.Coins) { +func (i *IndexedAddrAmts) Add(addr string, coin sdk.Coin) { n, known := i.indexes[addr] if !known { n = len(i.addrs) @@ -32,7 +32,7 @@ func (i *IndexedAddrAmts) Add(addr string, coins sdk.Coins) { i.addrs = append(i.addrs, addr) i.amts = append(i.amts, sdk.NewCoins()) } - i.amts[n] = i.amts[n].Add(coins...) + i.amts[n] = i.amts[n].Add(coin) } // GetAsInputs returns all the entries as bank Inputs. @@ -58,7 +58,7 @@ type OrderSplit struct { // Order fulfillment associated with this split. Order *OrderFulfillment // Assets is the amount of assets from the order involved in this split. - Assets sdk.Coins + Assets sdk.Coin // Price is the amount of the price from the order involved in this split. Price sdk.Coin } @@ -67,16 +67,16 @@ type OrderSplit struct { type OrderFulfillment struct { // Order is the original order with all its information. Order *Order - // AssetsFilled is the total assets being fulfilled for the order. - AssetsFilled sdk.Coins - // AssetsLeft is the amount of order assets that have not yet been fulfilled for the order. - AssetsLeft sdk.Coins - // PriceAmtFilled is the total price amount involved in this order fulfillment. - // If this is a bid order, the PriceAmtFilled is related to the order price. - // If this is an ask order, the PriceAmtFilled is related to the prices of the bid orders fulfilling this order. - PriceAmtFilled sdkmath.Int - // PriceAmtLeft is the price that has not yet been fulfilled for the order. - PriceAmtLeft sdkmath.Int + // AssetsFilledAmt is the total amount of assets being fulfilled for the order. + AssetsFilledAmt sdkmath.Int + // AssetsLeftAmt is the amount of order assets that have not yet been fulfilled for the order. + AssetsLeftAmt sdkmath.Int + // PriceFilledAmt is the total price amount involved in this order fulfillment. + // If this is a bid order, the PriceFilledAmt is related to the order price. + // If this is an ask order, the PriceFilledAmt is related to the prices of the bid orders fulfilling this order. + PriceFilledAmt sdkmath.Int + // PriceLeftAmt is the price that has not yet been fulfilled for the order. + PriceLeftAmt sdkmath.Int // FeesLeft is the amount fees left to pay (if this order is only partially filled). // This is not tracked as fulfillments are applied, it is only set during Finalize(). FeesLeft sdk.Coins @@ -88,13 +88,34 @@ var _ OrderI = (*OrderFulfillment)(nil) func NewOrderFulfillment(order *Order) *OrderFulfillment { return &OrderFulfillment{ - Order: order, - AssetsLeft: order.GetAssets(), - PriceAmtFilled: sdkmath.ZeroInt(), - PriceAmtLeft: order.GetPrice().Amount, + Order: order, + AssetsFilledAmt: sdkmath.ZeroInt(), + AssetsLeftAmt: order.GetAssets().Amount, + PriceFilledAmt: sdkmath.ZeroInt(), + PriceLeftAmt: order.GetPrice().Amount, } } +// GetAssetsFilled gets the coin value of the assets that have been filled in this fulfillment. +func (f OrderFulfillment) GetAssetsFilled() sdk.Coin { + return sdk.Coin{Denom: f.GetAssets().Denom, Amount: f.AssetsFilledAmt} +} + +// GetAssetsLeft gets the coin value of the assets left to fill in this fulfillment. +func (f OrderFulfillment) GetAssetsLeft() sdk.Coin { + return sdk.Coin{Denom: f.GetAssets().Denom, Amount: f.AssetsLeftAmt} +} + +// GetPriceFilled gets the coin value of the price that has been filled in this fulfillment. +func (f OrderFulfillment) GetPriceFilled() sdk.Coin { + return sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceFilledAmt} +} + +// GetPriceLeft gets the coin value of the price left to fill in this fulfillment. +func (f OrderFulfillment) GetPriceLeft() sdk.Coin { + return sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceLeftAmt} +} + // GetOrderID gets this fulfillment's order's id. func (f OrderFulfillment) GetOrderID() uint64 { return f.Order.GetOrderID() @@ -121,7 +142,7 @@ func (f OrderFulfillment) GetOwner() string { } // GetAssets gets this fulfillment's order's assets. -func (f OrderFulfillment) GetAssets() sdk.Coins { +func (f OrderFulfillment) GetAssets() sdk.Coin { return f.Order.GetAssets() } @@ -155,16 +176,21 @@ func (f OrderFulfillment) GetHoldAmount() sdk.Coins { return f.Order.GetHoldAmount() } +// Validate does nothing but satisfies the OrderI interface. +func (f OrderFulfillment) Validate() error { + return nil +} + // Finalize does some final calculations and validation for this order fulfillment. // This order fulfillment and the ones in it maybe updated during this. func (f *OrderFulfillment) Finalize() error { - if len(f.Splits) == 0 || f.AssetsFilled.IsZero() { + if len(f.Splits) == 0 || f.AssetsFilledAmt.IsZero() { return fmt.Errorf("%s order %d not even partially filled", f.GetOrderType(), f.GetOrderID()) } - if f.AssetsLeft.IsAnyNegative() { + if f.AssetsLeftAmt.IsNegative() { return fmt.Errorf("%s order %d having assets %q cannot fill be filled with %q: overfill", - f.GetOrderType(), f.GetOrderID(), f.GetAssets(), f.AssetsFilled) + f.GetOrderType(), f.GetOrderID(), f.GetAssets(), f.GetAssetsFilled()) } isAskOrder, isBidOrder := f.IsAskOrder(), f.IsBidOrder() @@ -172,46 +198,38 @@ func (f *OrderFulfillment) Finalize() error { orderPrice := f.GetPrice() targetPriceAmt := orderPrice.Amount - if !f.AssetsLeft.IsZero() { + if !f.AssetsLeftAmt.IsZero() { if !f.PartialFillAllowed() { return fmt.Errorf("%s order %d having assets %q cannot be partially filled with %q: "+ "order does not allow partial fulfillment", - f.GetOrderType(), f.GetOrderID(), orderAssets, f.AssetsFilled) + f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled()) } - if len(orderAssets) != 1 { - return fmt.Errorf("%s order %d having assets %q cannot be partially filled with %q: "+ - "orders with multiple asset types cannot be split", - f.GetOrderType(), f.GetOrderID(), orderAssets, f.AssetsFilled) - } - - assetsFilledAmt := f.AssetsFilled[0].Amount - orderAssetsAmt := orderAssets[0].Amount - priceAssets := orderPrice.Amount.Mul(assetsFilledAmt) - priceRem := priceAssets.Mod(orderAssetsAmt) + priceAssets := orderPrice.Amount.Mul(f.AssetsFilledAmt) + priceRem := priceAssets.Mod(orderAssets.Amount) if !priceRem.IsZero() { return fmt.Errorf("%s order %d having assets %q cannot be partially filled by %q: "+ "price %q is not evenly divisible", - f.GetOrderType(), f.GetOrderID(), orderAssets, f.AssetsFilled, orderPrice) + f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled(), orderPrice) } - targetPriceAmt = priceAssets.Quo(orderAssetsAmt) + targetPriceAmt = priceAssets.Quo(orderAssets.Amount) } if isAskOrder { // For ask orders, we need the updated price to have the same price/asset ratio // as when originally created. Since ask orders can receive more payment than they requested, - // the PriceAmtLeft in here might be less than that, and we need to fix it. - f.PriceAmtLeft = orderPrice.Amount.Sub(targetPriceAmt) + // the PriceLeftAmt in here might be less than that, and we need to fix it. + f.PriceLeftAmt = orderPrice.Amount.Sub(targetPriceAmt) } if isBidOrder { - // When adding things to f.PriceAmtFilled, we used truncation on the divisions. + // When adding things to f.PriceFilledAmt, we used truncation on the divisions. // So at this point, it might be a little less than the target price. // If that's the case, we distribute the difference weighted by assets in order of the splits. - toDistribute := targetPriceAmt.Sub(f.PriceAmtFilled) + toDistribute := targetPriceAmt.Sub(f.PriceFilledAmt) if toDistribute.IsNegative() { return fmt.Errorf("bid order %d having price %q cannot pay %q for %q: overfill", - f.GetOrderID(), f.GetPrice(), f.PriceAmtFilled, f.AssetsFilled) + f.GetOrderID(), f.GetPrice(), f.PriceFilledAmt, f.GetAssetsFilled()) } if toDistribute.IsPositive() { distLeft := toDistribute @@ -224,7 +242,7 @@ func (f *OrderFulfillment) Finalize() error { // OrderFulfillment won't let a bid order with multiple assets be split. // So we know that here, there's only one asset. for _, askSplit := range f.Splits { - distAmt := toDistribute.Mul(askSplit.Assets[0].Amount).Quo(f.AssetsFilled[0].Amount) + distAmt := toDistribute.Mul(askSplit.Assets.Amount).Quo(f.AssetsFilledAmt) if distAmt.IsZero() { if !minOne { continue @@ -234,11 +252,11 @@ func (f *OrderFulfillment) Finalize() error { if distAmt.GT(distLeft) { distAmt = distLeft } - f.PriceAmtFilled = f.PriceAmtFilled.Add(distAmt) - f.PriceAmtLeft = f.PriceAmtLeft.Sub(distAmt) + f.PriceFilledAmt = f.PriceFilledAmt.Add(distAmt) + f.PriceLeftAmt = f.PriceLeftAmt.Sub(distAmt) askSplit.Price.Amount = askSplit.Price.Amount.Add(distAmt) - askSplit.Order.PriceAmtFilled = askSplit.Order.PriceAmtFilled.Add(distAmt) - // Not updating askSplit.Order.PriceAmtLeft here since that's done specially above. + askSplit.Order.PriceFilledAmt = askSplit.Order.PriceFilledAmt.Add(distAmt) + // Not updating askSplit.Order.PriceLeftAmt here since that's done specially above. for _, bidSplit := range askSplit.Order.Splits { if bidSplit.Order.GetOrderID() == f.GetOrderID() { bidSplit.Price.Amount = bidSplit.Price.Amount.Add(distAmt) @@ -256,7 +274,7 @@ func (f *OrderFulfillment) Finalize() error { } // TODO[1658]: Finish up Finalize(). - if !f.AssetsLeft.IsZero() { + if !f.AssetsLeftAmt.IsZero() { var assetsFilledAmt, orderAssetsAmt sdkmath.Int // Temporary line so we can still compile. orderFees := f.GetSettlementFees() for _, orderFee := range orderFees { @@ -265,7 +283,7 @@ func (f *OrderFulfillment) Finalize() error { if !feeRem.IsZero() { return fmt.Errorf("%s order %d having settlement fees %q cannot be partially filled by %q: "+ "fee %q is not evenly divisible", - f.GetOrderType(), f.GetOrderID(), orderFees, f.AssetsFilled, orderFee) + f.GetOrderType(), f.GetOrderID(), orderFees, f.GetAssetsFilled(), orderFee) } feeAmtLeft := feeAssets.Quo(orderAssetsAmt) f.FeesLeft = f.FeesLeft.Add(sdk.NewCoin(orderFee.Denom, feeAmtLeft)) @@ -275,46 +293,9 @@ func (f *OrderFulfillment) Finalize() error { panic("not implemented") } -// Validate returns an error if there is a problem with this fulfillment. -func (f OrderFulfillment) Validate() error { - var assetsFilled sdk.Coins - pricePaid := sdk.NewInt64Coin(f.GetPrice().Denom, 0) - for _, split := range f.Splits { - assetsFilled = assetsFilled.Add(split.Assets...) - pricePaid = pricePaid.Add(split.Price) - } - orderAssets := f.GetAssets() - unfilledAssets, hasNeg := orderAssets.SafeSub(assetsFilled...) - if hasNeg { - return fmt.Errorf("assets fulfilled %q for order %d is more than the order assets %q", assetsFilled, f.GetOrderID(), orderAssets) - } - - // If it's a bid order: - // If filled in full, the pricePaid needs to equal the order price. - // If not filled in full: - // The price * assets filled / order assets must be a whole number and equal the price paid. - // The settlement fee * assets filled / order assets must be a whole number. - // If it's an ask order: - // If filled in full, the pricePaid must be greater than or equal to the order price. - // If not filled in full: - // The price * assets filled / order assets must be a whole number and must be less than or equal to the price paid. - // The settlement fee * assets filled / order assets must be a whole number. - if unfilledAssets.IsZero() { - - } - - if !unfilledAssets.IsZero() && !f.PartialFillAllowed() { - return fmt.Errorf("cannot partially fill order %d having assets %q with assets %q: order does not allow partial fill", - f.GetOrderID(), orderAssets, assetsFilled) - } - - // TODO[1658]: Implement OrderFulfillment.Validate() - panic("not implemented") -} - // IsFilled returns true if this fulfillment's order has been fully accounted for. func (f OrderFulfillment) IsFilled() bool { - return f.AssetsLeft.IsZero() + return f.AssetsLeftAmt.IsZero() } // getAssetInputsOutputs gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. @@ -325,59 +306,55 @@ func (f OrderFulfillment) getAssetInputsOutputs() ([]banktypes.Input, []banktype } if f.IsAskOrder() { - inputs := []banktypes.Input{{Address: f.GetOwner(), Coins: f.AssetsFilled}} + inputs := []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.Coins{f.GetAssetsFilled()}}} outputs := indexedSplits.GetAsOutputs() return inputs, outputs, nil } if f.IsBidOrder() { inputs := indexedSplits.GetAsInputs() - outputs := []banktypes.Output{{Address: f.GetOwner(), Coins: f.AssetsFilled}} + outputs := []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.Coins{f.GetAssetsFilled()}}} return inputs, outputs, nil } return nil, nil, fmt.Errorf("unknown order type %T", f.Order.GetOrder()) } // getPriceInputsOutputs gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. -// It is assumed that getAssetInputsOutputs has already been called and did not return an error. func (f OrderFulfillment) getPriceInputsOutputs() ([]banktypes.Input, []banktypes.Output, error) { - price := sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceAmtFilled} - if f.PriceAmtLeft.IsNegative() && f.IsBidOrder() { - return nil, nil, fmt.Errorf("bid order %d having price %q cannot pay %q: overfill", - f.GetOrderID(), f.GetPrice(), price) - } - if !f.AssetsLeft.IsZero() { - // Assuming getAssetInputsOutputs was called previously, which returns an error - // if trying to partially fill an order with multiple asset types. - // So, if we're here and there's assets left, there's only one denom. - orderPrice := f.GetPrice() - priceAssetsAmt := orderPrice.Amount.Mul(f.AssetsFilled[0].Amount) - rem := priceAssetsAmt.Mod(f.GetAssets()[0].Amount) - if !rem.IsZero() { - return nil, nil, fmt.Errorf("%s order %d having assets %q cannot be partially filled by %q: "+ - "price %q is not evenly divisible", - f.GetOrderType(), f.GetOrderID(), f.GetAssets(), f.AssetsFilled, orderPrice) - } + indexedSplits := NewIndexedAddrAmts() + for _, split := range f.Splits { + indexedSplits.Add(split.Order.GetOwner(), split.Price) } - // TODO[1658]: Finish getPriceInputsOutputs - return nil, nil, nil + if f.IsAskOrder() { + inputs := indexedSplits.GetAsInputs() + outputs := []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.Coins{f.GetPriceFilled()}}} + return inputs, outputs, nil + } + if f.IsBidOrder() { + inputs := []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.Coins{f.GetPriceFilled()}}} + outputs := indexedSplits.GetAsOutputs() + return inputs, outputs, nil + } + return nil, nil, fmt.Errorf("unknown order type %T", f.Order.GetOrder()) } // Apply adjusts this order fulfillment using the provided info. -func (f *OrderFulfillment) Apply(order *OrderFulfillment, assets sdk.Coins, priceAmt sdkmath.Int) error { - newAssetsLeft, hasNeg := f.AssetsLeft.SafeSub(assets...) - if hasNeg { +func (f *OrderFulfillment) Apply(order *OrderFulfillment, assetsAmt, priceAmt sdkmath.Int) error { + assets := sdk.NewCoin(order.GetAssets().Denom, assetsAmt) + price := sdk.NewCoin(order.GetPrice().Denom, priceAmt) + newAssetsLeftAmt := f.AssetsLeftAmt.Sub(assetsAmt) + if newAssetsLeftAmt.IsNegative() { return fmt.Errorf("cannot fill %s order %d having assets left %q with %q from %s order %d: overfill", - f.GetOrderType(), f.GetOrderID(), f.AssetsLeft, assets, order.GetOrderType(), order.GetOrderID()) + f.GetOrderType(), f.GetOrderID(), f.GetAssetsLeft(), assets, order.GetOrderType(), order.GetOrderID()) } - f.AssetsLeft = newAssetsLeft - f.AssetsFilled = f.AssetsFilled.Add(assets...) - f.PriceAmtLeft = f.PriceAmtLeft.Sub(priceAmt) - f.PriceAmtFilled = f.PriceAmtFilled.Add(priceAmt) + f.AssetsLeftAmt = newAssetsLeftAmt + f.AssetsFilledAmt = f.AssetsFilledAmt.Add(assetsAmt) + f.PriceLeftAmt = f.PriceLeftAmt.Sub(priceAmt) + f.PriceFilledAmt = f.PriceFilledAmt.Add(priceAmt) f.Splits = append(f.Splits, &OrderSplit{ Order: order, Assets: assets, - Price: sdk.NewCoin(order.GetPrice().Denom, priceAmt), + Price: price, }) return nil } @@ -409,7 +386,7 @@ func Fulfill(of1, of2 *OrderFulfillment) error { bidOrderID, bidOrder.Price, askOrderID, askOrder.Price) } - assets, err := getFulfillmentAssets(askOF, bidOF) + assetsAmt, err := getFulfillmentAssetsAmt(askOF, bidOF) if err != nil { return err } @@ -418,72 +395,40 @@ func Fulfill(of1, of2 *OrderFulfillment) error { // for consistent truncation and remainders. Once we've identified all the fulfillment relationships, // we'll enumerate and redistribute those remainders. priceAmt := bidOrder.Price.Amount - if !CoinsEquals(assets, bidOrder.GetAssets()) { - if len(assets) != 1 { - return fmt.Errorf("cannot split bid order %d having assets %q by %q: overfill", - bidOrderID, bidOrder.Assets, assets) - } - priceAmt = bidOrder.Price.Amount.Mul(assets[0].Amount).Quo(bidOrder.Assets.Amount) + if !assetsAmt.Equal(bidOrder.Assets.Amount) { + priceAmt = bidOrder.Price.Amount.Mul(assetsAmt).Quo(bidOrder.Assets.Amount) } - askErr := askOF.Apply(bidOF, assets, priceAmt) - bidErr := bidOF.Apply(askOF, assets, priceAmt) + askErr := askOF.Apply(bidOF, assetsAmt, priceAmt) + bidErr := bidOF.Apply(askOF, assetsAmt, priceAmt) return errors.Join(askErr, bidErr) } -// getFulfillmentAssets figures out the assets that can be fulfilled with the two provided orders. +// getFulfillmentAssetsAmt figures out the assets that can be fulfilled with the two provided orders. // It's assumed that the askOF is for an ask order, and the bidOF is for a bid order. -func getFulfillmentAssets(askOF, bidOF *OrderFulfillment) (sdk.Coins, error) { - askAssets, bidAssets := askOF.AssetsLeft, bidOF.AssetsLeft +func getFulfillmentAssetsAmt(askOF, bidOF *OrderFulfillment) (sdkmath.Int, error) { + askAssetsLeft, bidAssetsLeft := askOF.GetAssetsLeft(), bidOF.GetAssetsLeft() askOrderID, bidOrderID := askOF.GetOrderID(), bidOF.GetOrderID() stdErr := func(msg string) error { return fmt.Errorf("cannot fill ask order %d having assets left %q with bid order %d having assets left %q: %s", - askOrderID, askAssets, bidOrderID, bidAssets, msg) + askOrderID, askAssetsLeft, bidOrderID, bidAssetsLeft, msg) } - if askAssets.IsZero() || askAssets.IsAnyNegative() || bidAssets.IsZero() || bidAssets.IsAnyNegative() { - return nil, stdErr("zero or negative assets") + askAmt, bidAmt := askOF.AssetsLeftAmt, bidOF.AssetsLeftAmt + if askAmt.IsZero() || askAmt.IsNegative() || bidAmt.IsZero() || bidAmt.IsNegative() { + return sdkmath.ZeroInt(), stdErr("zero or negative assets") } // If they're equal, we're all good, just return one of them. - if CoinsEquals(askAssets, bidAssets) { - return askAssets, nil - } - - // Handling single denom case now since that's expected to be the most common thing, and it's easier. - if len(askAssets) == 1 && len(bidAssets) == 1 { - if askAssets[0].Denom != bidAssets[0].Denom { - return nil, stdErr("asset denom mismatch") - } - // Take the lesser of the two. - if askAssets[0].Amount.LT(bidAssets[0].Amount) { - return askAssets, nil - } - return bidAssets, nil - } - - // When splitting a bid, we distribute the price based on the assets. - // If the bid has multiple asset denoms, that division is impossible. - // So, if the bid has multiple asset denoms, ensure they're less than or equal to the ask assets and return them. - if len(bidAssets) > 1 { - _, hasNeg := askAssets.SafeSub(bidAssets...) - if hasNeg { - return nil, stdErr("bid orders with multiple assets cannot be split") - } - return bidAssets, nil - } - - // At this point, we know there's multiple ask assets, and only one bid asset. - // All we care about are the amounts of that denom, taking the lesser of the two (but not zero). - askAmt := askAssets.AmountOf(bidAssets[0].Denom) - if !askAmt.IsPositive() { - return nil, stdErr("asset denom mismatch") + if askAmt.Equal(bidAmt) { + return askAmt, nil } - if bidAssets[0].Amount.LTE(askAmt) { - return bidAssets, nil + // Use the lesser of the two. + if askAmt.LT(bidAmt) { + return askAmt, nil } - return sdk.NewCoins(sdk.NewCoin(bidAssets[0].Denom, askAmt)), nil + return bidAmt, nil } type OrderTransfers struct { diff --git a/x/exchange/genesis_test.go b/x/exchange/genesis_test.go index 5f3508876d..6cff371dca 100644 --- a/x/exchange/genesis_test.go +++ b/x/exchange/genesis_test.go @@ -13,29 +13,26 @@ import ( func TestGenesisState_Validate(t *testing.T) { addr1 := sdk.AccAddress("addr1_______________").String() - coins := func(coins string) sdk.Coins { - rv, err := sdk.ParseCoinsNormalized(coins) - require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) - return rv - } coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } askOrder := func(orderID uint64, marketID uint32, assets string, price string) Order { priceCoin, err := sdk.ParseCoinNormalized(price) - require.NoError(t, err, "sdk.ParseCoinNormalized(%q)", price) + require.NoError(t, err, "ask price sdk.ParseCoinNormalized(%q)", price) + assetsCoin, err := sdk.ParseCoinNormalized(assets) + require.NoError(t, err, "ask assets sdk.ParseCoinNormalized(%q)", assets) return *NewOrder(orderID).WithAsk(&AskOrder{ MarketId: marketID, Seller: addr1, - Assets: coins(assets), + Assets: assetsCoin, Price: priceCoin, }) } bidOrder := func(orderID uint64, marketID uint32, assets string, price string) Order { priceCoin, err := sdk.ParseCoinNormalized(price) - require.NoError(t, err, "price sdk.ParseCoinNormalized(%q)", price) + require.NoError(t, err, "bid price sdk.ParseCoinNormalized(%q)", price) assetsCoin, err := sdk.ParseCoinNormalized(assets) - require.NoError(t, err, "assets sdk.ParseCoinNormalized(%q)", assets) + require.NoError(t, err, "bid assets sdk.ParseCoinNormalized(%q)", assets) return *NewOrder(orderID).WithBid(&BidOrder{ MarketId: marketID, Buyer: addr1, diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index dd276013dc..50e046706e 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -16,7 +16,7 @@ import ( func sumAssetsAndPrice(orders []*exchange.Order) (sdk.Coins, sdk.Coins) { var totalAssets, totalPrice sdk.Coins for _, order := range orders { - totalAssets = totalAssets.Add(order.GetAssets()...) + totalAssets = totalAssets.Add(order.GetAssets()) totalPrice = totalPrice.Add(order.GetPrice()) } return totalAssets, totalPrice @@ -224,7 +224,7 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro assetInputs = append(assetInputs, banktypes.Input{Address: seller}) priceOutputs = append(priceOutputs, banktypes.Output{Address: seller}) } - assetInputs[i].Coins = assetInputs[i].Coins.Add(assets...) + assetInputs[i].Coins = assetInputs[i].Coins.Add(assets) priceOutputs[i].Coins = priceOutputs[i].Coins.Add(price) if !totalSellerFee.IsZero() { diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 96a5edd244..a818a7b6b3 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -80,7 +80,7 @@ func createIndexEntries(order exchange.Order) []sdk.KVPair { addr := sdk.MustAccAddressFromBech32(owner) assets := order.GetAssets() - rv := []sdk.KVPair{ + return []sdk.KVPair{ { Key: MakeIndexKeyMarketToOrder(marketID, orderID), Value: []byte{orderTypeByte}, @@ -89,16 +89,11 @@ func createIndexEntries(order exchange.Order) []sdk.KVPair { Key: MakeIndexKeyAddressToOrder(addr, orderID), Value: []byte{orderTypeByte}, }, - } - - for _, asset := range assets { - rv = append(rv, sdk.KVPair{ - Key: MakeIndexKeyAssetToOrder(asset.Denom, orderTypeByte, orderID), + { + Key: MakeIndexKeyAssetToOrder(assets.Denom, orderTypeByte, orderID), Value: nil, - }) + }, } - - return rv } // getOrderFromStore looks up an order from the store. Returns nil, nil if the order does not exist. diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 4af6031a26..ab15f98163 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -184,7 +184,7 @@ func TestMsgCreateAskRequest_ValidateBasic(t *testing.T) { AskOrder: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("seller______________").String(), - Assets: sdk.NewCoins(sdk.NewInt64Coin("banana", 99)), + Assets: sdk.NewInt64Coin("banana", 99), Price: sdk.NewInt64Coin("acorn", 12), }, OrderCreationFee: &sdk.Coin{Denom: "cactus", Amount: sdkmath.NewInt(-3)}, diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 0671131f0f..46eac6dabd 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -21,7 +21,7 @@ const ( type SubOrderI interface { GetMarketID() uint32 GetOwner() string - GetAssets() sdk.Coins + GetAssets() sdk.Coin GetPrice() sdk.Coin GetSettlementFees() sdk.Coins PartialFillAllowed() bool @@ -63,6 +63,17 @@ func findDuplicateIds(orderIDs []uint64) []uint64 { return rv } +// validateCoin makes sure the provided coin is valid an not zero. +func validateCoin(field string, coin sdk.Coin) error { + // The Coin.Validate() method allows the coin to be zero (but not negative). + if err := coin.Validate(); err != nil { + return fmt.Errorf("invalid %s: %w", field, err) + } else if coin.IsZero() { + return fmt.Errorf("invalid %s: cannot be zero", field) + } + return nil +} + // ValidateOrderIDs makes sure that one or more order ids are provided, // none of them are zero, and there aren't any duplicates. func ValidateOrderIDs(field string, orderIDs []uint64) error { @@ -151,7 +162,7 @@ func (o Order) GetOwner() string { // GetAssets returns the assets for this order. // Panics if the sub-order is not set or is something unexpected. -func (o Order) GetAssets() sdk.Coins { +func (o Order) GetAssets() sdk.Coin { return o.MustGetSubOrder().GetAssets() } @@ -217,7 +228,7 @@ func (a AskOrder) GetOwner() string { } // GetAssets returns the assets for sale with this ask order. -func (a AskOrder) GetAssets() sdk.Coins { +func (a AskOrder) GetAssets() sdk.Coin { return a.Assets } @@ -251,7 +262,7 @@ func (a AskOrder) GetOrderTypeByte() byte { // GetHoldAmount returns the amount that should be on hold for this ask order. func (a AskOrder) GetHoldAmount() sdk.Coins { - rv := a.Assets + rv := sdk.Coins{a.Assets} if a.SellerSettlementFlatFee != nil && a.SellerSettlementFlatFee.Denom != a.Price.Denom { rv = rv.Add(*a.SellerSettlementFlatFee) } @@ -272,34 +283,17 @@ func (a AskOrder) Validate() error { errs = append(errs, fmt.Errorf("invalid seller: %w", err)) } - // The price must not be zero and must be a valid coin. - // The Coin.Validate() method allows the coin to be zero (but not negative). var priceDenom string - if err := a.Price.Validate(); err != nil { - errs = append(errs, fmt.Errorf("invalid price: %w", err)) - } else if a.Price.IsZero() { - errs = append(errs, errors.New("invalid price: cannot be zero")) + if err := validateCoin("price", a.Price); err != nil { + errs = append(errs, err) } else { priceDenom = a.Price.Denom } - // The Coins.Validate method does NOT allow any coin entries to be zero (or negative). - // It does allow there to not be any entries, though, which we don't want to allow here. - // We also don't want to allow the price denom to also be in the assets. - if err := a.Assets.Validate(); err != nil { - errs = append(errs, fmt.Errorf("invalid assets: %w", err)) - } else { - switch { - case len(a.Assets) == 0: - errs = append(errs, errors.New("invalid assets: must not be empty")) - case len(priceDenom) > 0: - for _, asset := range a.Assets { - if priceDenom == asset.Denom { - errs = append(errs, fmt.Errorf("invalid assets: cannot contain price denom %s", priceDenom)) - break - } - } - } + if err := validateCoin("assets", a.Assets); err != nil { + errs = append(errs, err) + } else if len(priceDenom) > 0 && a.Assets.Denom == priceDenom { + errs = append(errs, fmt.Errorf("invalid assets: price denom %s cannot also be the assets denom", priceDenom)) } if a.SellerSettlementFlatFee != nil { @@ -329,8 +323,8 @@ func (b BidOrder) GetOwner() string { } // GetAssets returns the assets to buy for this bid order. -func (b BidOrder) GetAssets() sdk.Coins { - return sdk.Coins{b.Assets} +func (b BidOrder) GetAssets() sdk.Coin { + return b.Assets } // GetPrice returns the price to pay for this bid order. @@ -372,29 +366,20 @@ func (b BidOrder) Validate() error { errs = append(errs, errors.New("invalid market id: must not be zero")) } - // The seller address must be valid and not empty. + // The buyer address must be valid and not empty. if _, err := sdk.AccAddressFromBech32(b.Buyer); err != nil { errs = append(errs, fmt.Errorf("invalid buyer: %w", err)) } - // The price must not be zero and must be a valid coin. - // The Coin.Validate() method allows the coin to be zero (but not negative). var priceDenom string - if err := b.Price.Validate(); err != nil { - errs = append(errs, fmt.Errorf("invalid price: %w", err)) - } else if b.Price.IsZero() { - errs = append(errs, errors.New("invalid price: cannot be zero")) + if err := validateCoin("price", b.Price); err != nil { + errs = append(errs, err) } else { priceDenom = b.Price.Denom } - // The Coins.Validate method does NOT allow any coin entries to be zero (or negative). - // It does allow there to not be any entries, though, which we don't want to allow here. - // We also don't want to allow the price denom to also be in the assets. - if err := b.Assets.Validate(); err != nil { - errs = append(errs, fmt.Errorf("invalid assets: %w", err)) - } else if b.Assets.IsZero() { - errs = append(errs, errors.New("invalid assets: cannot be zero")) + if err := validateCoin("assets", b.Assets); err != nil { + errs = append(errs, err) } else if len(priceDenom) > 0 && b.Assets.Denom == priceDenom { errs = append(errs, fmt.Errorf("invalid assets: price denom %s cannot also be the assets denom", priceDenom)) } diff --git a/x/exchange/orders.pb.go b/x/exchange/orders.pb.go index 41c6ef9197..606965dab3 100644 --- a/x/exchange/orders.pb.go +++ b/x/exchange/orders.pb.go @@ -124,7 +124,7 @@ type AskOrder struct { Seller string `protobuf:"bytes,2,opt,name=seller,proto3" json:"seller,omitempty"` // assets are the things that the seller wishes to sell. // A hold is placed on this until the order is filled or cancelled. - Assets github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=assets,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"assets"` + Assets types.Coin `protobuf:"bytes,3,opt,name=assets,proto3" json:"assets"` // price is the minimum amount that the seller is willing to accept for the assets. The seller's settlement // proportional fee (and possibly the settlement flat fee) is taken out of the amount the seller receives, // so it's possible that the seller will still receive less than this price. @@ -234,41 +234,40 @@ func init() { } var fileDescriptor_dab7cbe63f582471 = []byte{ - // 540 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0xb1, 0x6f, 0xd3, 0x4e, - 0x14, 0xc7, 0xed, 0x24, 0x4e, 0x9d, 0xeb, 0xaf, 0x8b, 0x7f, 0x05, 0x9c, 0x20, 0x39, 0x51, 0xba, - 0x64, 0xc9, 0xb9, 0x29, 0x42, 0x48, 0x2c, 0xa8, 0x41, 0xaa, 0xe8, 0x44, 0xe5, 0x4a, 0x0c, 0x2c, - 0xd6, 0xd9, 0x7e, 0x4d, 0x4f, 0x71, 0x7c, 0x96, 0xef, 0x1a, 0xda, 0x89, 0x95, 0x91, 0x3f, 0x81, - 0x99, 0x15, 0xfe, 0x88, 0x8e, 0x15, 0x13, 0x13, 0xa0, 0xe4, 0x4f, 0x60, 0x61, 0x44, 0xbe, 0xbb, - 0xa4, 0x41, 0x82, 0x50, 0x04, 0x93, 0xef, 0xbd, 0xfb, 0xbe, 0xef, 0x7b, 0xfa, 0x3c, 0xf9, 0xd0, - 0x4e, 0x5e, 0xb0, 0x29, 0x64, 0x24, 0x8b, 0xc1, 0x87, 0xf3, 0xf8, 0x94, 0x64, 0x23, 0xf0, 0xa7, - 0x03, 0x9f, 0x15, 0x09, 0x14, 0x1c, 0xe7, 0x05, 0x13, 0xcc, 0xb9, 0x7d, 0x2d, 0xc2, 0x0b, 0x11, - 0x9e, 0x0e, 0x5a, 0x5e, 0xcc, 0xf8, 0x84, 0x71, 0x3f, 0x22, 0xbc, 0x2c, 0x8a, 0x40, 0x90, 0x81, - 0x1f, 0x33, 0x9a, 0xa9, 0xba, 0x56, 0x53, 0xdd, 0x87, 0x32, 0xf2, 0x55, 0xa0, 0xaf, 0xb6, 0x47, - 0x6c, 0xc4, 0x54, 0xbe, 0x3c, 0xa9, 0x6c, 0xf7, 0x9d, 0x89, 0xac, 0xa7, 0x65, 0x67, 0xa7, 0x89, - 0x6c, 0x39, 0x42, 0x48, 0x13, 0xd7, 0xec, 0x98, 0xbd, 0x5a, 0xb0, 0x21, 0xe3, 0xc3, 0xc4, 0x79, - 0x84, 0x1a, 0x84, 0x8f, 0x43, 0x19, 0xba, 0x95, 0x8e, 0xd9, 0xdb, 0xdc, 0xeb, 0xe0, 0x9f, 0x4f, - 0x88, 0xf7, 0xf9, 0x58, 0xfa, 0x3d, 0x31, 0x02, 0x9b, 0xe8, 0x73, 0x69, 0x10, 0xd1, 0x44, 0x1b, - 0x54, 0xd7, 0x1b, 0x0c, 0x69, 0xb2, 0x34, 0x88, 0xf4, 0xf9, 0x61, 0xed, 0xd5, 0x9b, 0xb6, 0x31, - 0xdc, 0x40, 0x96, 0xb4, 0xe8, 0x7e, 0xab, 0x20, 0x7b, 0xd1, 0xc8, 0xb9, 0x8b, 0x1a, 0x13, 0x52, - 0x8c, 0x41, 0x2c, 0x26, 0xdf, 0x0a, 0x6c, 0x95, 0x38, 0x4c, 0x9c, 0x5d, 0x54, 0xe7, 0x90, 0xa6, - 0x7a, 0xee, 0xc6, 0xd0, 0xfd, 0xf0, 0xbe, 0xbf, 0xad, 0xb9, 0xec, 0x27, 0x49, 0x01, 0x9c, 0x1f, - 0x8b, 0x82, 0x66, 0xa3, 0x40, 0xeb, 0x9c, 0x18, 0xd5, 0x09, 0xe7, 0x20, 0xb8, 0x5b, 0xed, 0x54, - 0x7b, 0x9b, 0x7b, 0x4d, 0xac, 0xe5, 0x25, 0x73, 0xac, 0x99, 0xe3, 0xc7, 0x8c, 0x66, 0xc3, 0xdd, - 0xcb, 0x4f, 0x6d, 0xe3, 0xed, 0xe7, 0x76, 0x6f, 0x44, 0xc5, 0xe9, 0x59, 0x84, 0x63, 0x36, 0xd1, - 0xcc, 0xf5, 0xa7, 0xcf, 0x93, 0xb1, 0x2f, 0x2e, 0x72, 0xe0, 0xb2, 0x80, 0x07, 0xda, 0xda, 0xb9, - 0x8f, 0xac, 0xbc, 0xa0, 0x31, 0xb8, 0x35, 0x09, 0x63, 0x4d, 0x8f, 0x5a, 0xd9, 0x23, 0x50, 0x6a, - 0xe7, 0x19, 0x6a, 0xa9, 0x29, 0x43, 0x0e, 0x42, 0xa4, 0x30, 0x81, 0x4c, 0x84, 0x27, 0x29, 0x11, - 0xe1, 0x09, 0x80, 0x6b, 0xfd, 0xc6, 0x2b, 0xb8, 0xa3, 0x8a, 0x8f, 0x97, 0xb5, 0x07, 0x29, 0x11, - 0x07, 0x00, 0xce, 0x0e, 0xda, 0x22, 0x69, 0xca, 0x5e, 0x84, 0x39, 0x29, 0x04, 0x25, 0xa9, 0x5b, - 0xef, 0x98, 0x3d, 0x3b, 0xf8, 0x4f, 0x26, 0x8f, 0x54, 0x4e, 0xed, 0xa0, 0xfb, 0xb5, 0x82, 0xec, - 0xc5, 0x8a, 0xd6, 0xa3, 0xc7, 0xc8, 0x8a, 0xce, 0x2e, 0x6e, 0x40, 0x5e, 0xc9, 0x9c, 0x07, 0x2b, - 0xe0, 0x6f, 0x04, 0xe5, 0x2f, 0x61, 0xbe, 0x44, 0xb7, 0x64, 0xe3, 0x1f, 0x58, 0x02, 0x70, 0xd7, - 0xfa, 0xf7, 0x7b, 0xff, 0x5f, 0x76, 0x5a, 0x01, 0x0f, 0xc0, 0xff, 0x80, 0xfa, 0x10, 0x2e, 0x67, - 0x9e, 0x79, 0x35, 0xf3, 0xcc, 0x2f, 0x33, 0xcf, 0x7c, 0x3d, 0xf7, 0x8c, 0xab, 0xb9, 0x67, 0x7c, - 0x9c, 0x7b, 0x06, 0x6a, 0x52, 0xf6, 0x8b, 0x3f, 0xe9, 0xc8, 0x7c, 0x8e, 0x57, 0x06, 0xbc, 0x16, - 0xf5, 0x29, 0x5b, 0x89, 0xfc, 0xf3, 0xe5, 0x33, 0x14, 0xd5, 0xe5, 0xa3, 0x70, 0xef, 0x7b, 0x00, - 0x00, 0x00, 0xff, 0xff, 0x0b, 0x65, 0x60, 0x61, 0xa4, 0x04, 0x00, 0x00, + // 525 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0x31, 0x6f, 0xd3, 0x40, + 0x14, 0xb6, 0xd3, 0x38, 0x75, 0x0e, 0xba, 0x98, 0x02, 0x4e, 0x90, 0x9c, 0x28, 0x5d, 0xb2, 0xe4, + 0xdc, 0x80, 0x10, 0x12, 0x0b, 0x6a, 0x90, 0x2a, 0x3a, 0x51, 0xb9, 0x12, 0x03, 0x8b, 0x75, 0xb6, + 0x5f, 0xdd, 0x53, 0x1c, 0x9f, 0xe5, 0xbb, 0x86, 0x76, 0x62, 0x65, 0xe4, 0x27, 0x30, 0xb3, 0xc2, + 0x8f, 0xe8, 0xc0, 0x50, 0x31, 0x31, 0x01, 0x4a, 0x7e, 0x02, 0x7f, 0x00, 0xf9, 0xee, 0x92, 0x06, + 0x09, 0x42, 0x59, 0x98, 0xfc, 0xde, 0xbb, 0xef, 0xfb, 0xde, 0xa7, 0x4f, 0x7a, 0x46, 0x3b, 0x45, + 0xc9, 0xa6, 0x90, 0x93, 0x3c, 0x06, 0x1f, 0xce, 0xe2, 0x13, 0x92, 0xa7, 0xe0, 0x4f, 0x87, 0x3e, + 0x2b, 0x13, 0x28, 0x39, 0x2e, 0x4a, 0x26, 0x98, 0x73, 0xe7, 0x0a, 0x84, 0x17, 0x20, 0x3c, 0x1d, + 0xb6, 0xbd, 0x98, 0xf1, 0x09, 0xe3, 0x7e, 0x44, 0x78, 0x45, 0x8a, 0x40, 0x90, 0xa1, 0x1f, 0x33, + 0x9a, 0x2b, 0x5e, 0xbb, 0xa5, 0xde, 0x43, 0xd9, 0xf9, 0xaa, 0xd1, 0x4f, 0xdb, 0x29, 0x4b, 0x99, + 0x9a, 0x57, 0x95, 0x9a, 0xf6, 0x3e, 0x98, 0xc8, 0x7a, 0x5e, 0x6d, 0x76, 0x5a, 0xc8, 0x96, 0x16, + 0x42, 0x9a, 0xb8, 0x66, 0xd7, 0xec, 0xd7, 0x83, 0x4d, 0xd9, 0x1f, 0x24, 0xce, 0x13, 0xd4, 0x24, + 0x7c, 0x1c, 0xca, 0xd6, 0xad, 0x75, 0xcd, 0xfe, 0x8d, 0xfb, 0x5d, 0xfc, 0x7b, 0x87, 0x78, 0x8f, + 0x8f, 0xa5, 0xde, 0x33, 0x23, 0xb0, 0x89, 0xae, 0x2b, 0x81, 0x88, 0x26, 0x5a, 0x60, 0x63, 0xbd, + 0xc0, 0x88, 0x26, 0x4b, 0x81, 0x48, 0xd7, 0x8f, 0xeb, 0x6f, 0xde, 0x75, 0x8c, 0xd1, 0x26, 0xb2, + 0xa4, 0x44, 0xef, 0x53, 0x0d, 0xd9, 0x8b, 0x45, 0xce, 0x3d, 0xd4, 0x9c, 0x90, 0x72, 0x0c, 0x62, + 0xe1, 0x7c, 0x2b, 0xb0, 0xd5, 0xe0, 0x20, 0x71, 0x76, 0x51, 0x83, 0x43, 0x96, 0x69, 0xdf, 0xcd, + 0x91, 0xfb, 0xf9, 0xe3, 0x60, 0x5b, 0xe7, 0xb2, 0x97, 0x24, 0x25, 0x70, 0x7e, 0x24, 0x4a, 0x9a, + 0xa7, 0x81, 0xc6, 0x39, 0x8f, 0x50, 0x83, 0x70, 0x0e, 0x82, 0x6b, 0xa3, 0x2d, 0xac, 0xe1, 0x55, + 0xe6, 0x58, 0x67, 0x8e, 0x9f, 0x32, 0x9a, 0x8f, 0xea, 0x17, 0x5f, 0x3b, 0x46, 0xa0, 0xe1, 0xce, + 0x43, 0x64, 0x15, 0x25, 0x8d, 0xc1, 0xad, 0x5f, 0x8f, 0xa7, 0xd0, 0xce, 0x0b, 0xd4, 0x56, 0x9b, + 0x43, 0x0e, 0x42, 0x64, 0x30, 0x81, 0x5c, 0x84, 0xc7, 0x19, 0x11, 0xe1, 0x31, 0x80, 0x6b, 0xfd, + 0x45, 0x2b, 0xb8, 0xab, 0xc8, 0x47, 0x4b, 0xee, 0x7e, 0x46, 0xc4, 0x3e, 0x80, 0xb3, 0x83, 0xb6, + 0x48, 0x96, 0xb1, 0x57, 0x61, 0x41, 0x4a, 0x41, 0x49, 0xe6, 0x36, 0xba, 0x66, 0xdf, 0x0e, 0x6e, + 0xca, 0xe1, 0xa1, 0x9a, 0xa9, 0x5c, 0x7b, 0x3f, 0x6a, 0xc8, 0x5e, 0xc4, 0xbe, 0x3e, 0x4e, 0x8c, + 0xac, 0xe8, 0xf4, 0xfc, 0x1a, 0x69, 0x2a, 0xd8, 0x7f, 0x0f, 0xf3, 0x35, 0xba, 0x2d, 0x17, 0xff, + 0x92, 0x25, 0x00, 0x77, 0xad, 0xee, 0xc6, 0x7a, 0x99, 0xdd, 0x4a, 0xe6, 0xfd, 0xb7, 0x4e, 0x3f, + 0xa5, 0xe2, 0xe4, 0x34, 0xc2, 0x31, 0x9b, 0xe8, 0xfb, 0xd1, 0x9f, 0x01, 0x4f, 0xc6, 0xbe, 0x38, + 0x2f, 0x80, 0x4b, 0x02, 0x0f, 0x6e, 0xc9, 0x4d, 0x2b, 0xc1, 0x03, 0xf0, 0x7f, 0x48, 0x7d, 0x04, + 0x17, 0x33, 0xcf, 0xbc, 0x9c, 0x79, 0xe6, 0xf7, 0x99, 0x67, 0xbe, 0x9d, 0x7b, 0xc6, 0xe5, 0xdc, + 0x33, 0xbe, 0xcc, 0x3d, 0x03, 0xb5, 0x28, 0xfb, 0xc3, 0x75, 0x1c, 0x9a, 0x2f, 0xf1, 0x8a, 0xc1, + 0x2b, 0xd0, 0x80, 0xb2, 0x95, 0xce, 0x3f, 0x5b, 0xfe, 0x5a, 0xa2, 0x86, 0x3c, 0xf4, 0x07, 0x3f, + 0x03, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x3d, 0x62, 0x47, 0x78, 0x04, 0x00, 0x00, } func (m *Order) Marshal() (dAtA []byte, err error) { @@ -402,20 +401,16 @@ func (m *AskOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { } i-- dAtA[i] = 0x22 - if len(m.Assets) > 0 { - for iNdEx := len(m.Assets) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Assets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintOrders(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a + { + size, err := m.Assets.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } + i -= size + i = encodeVarintOrders(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x1a if len(m.Seller) > 0 { i -= len(m.Seller) copy(dAtA[i:], m.Seller) @@ -573,12 +568,8 @@ func (m *AskOrder) Size() (n int) { if l > 0 { n += 1 + l + sovOrders(uint64(l)) } - if len(m.Assets) > 0 { - for _, e := range m.Assets { - l = e.Size() - n += 1 + l + sovOrders(uint64(l)) - } - } + l = m.Assets.Size() + n += 1 + l + sovOrders(uint64(l)) l = m.Price.Size() n += 1 + l + sovOrders(uint64(l)) if m.SellerSettlementFlatFee != nil { @@ -874,8 +865,7 @@ func (m *AskOrder) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Assets = append(m.Assets, types.Coin{}) - if err := m.Assets[len(m.Assets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Assets.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 042db51ee0..fa2cfecc76 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -165,7 +165,7 @@ func TestOrder_WithAsk(t *testing.T) { origAsk := &AskOrder{ MarketId: 12, Seller: "some seller", - Assets: sdk.NewCoins(sdk.NewInt64Coin("water", 8)), + Assets: sdk.NewInt64Coin("water", 8), Price: sdk.NewInt64Coin("sand", 1), SellerSettlementFlatFee: &sdk.Coin{Denom: "banana", Amount: sdkmath.NewInt(3)}, AllowPartial: true, @@ -173,7 +173,7 @@ func TestOrder_WithAsk(t *testing.T) { ask := &AskOrder{ MarketId: origAsk.MarketId, Seller: origAsk.Seller, - Assets: copyCoins(origAsk.Assets), + Assets: copyCoin(origAsk.Assets), Price: copyCoin(origAsk.Price), SellerSettlementFlatFee: copyCoinP(origAsk.SellerSettlementFlatFee), AllowPartial: origAsk.AllowPartial, @@ -367,7 +367,7 @@ func TestOrder_GetSubOrder(t *testing.T) { askOrder := &AskOrder{ MarketId: 1, Seller: sdk.AccAddress("Seller______________").String(), - Assets: sdk.NewCoins(sdk.NewInt64Coin("assetcoin", 3)), + Assets: sdk.NewInt64Coin("assetcoin", 3), Price: sdk.NewInt64Coin("paycoin", 8), SellerSettlementFlatFee: &sdk.Coin{Denom: "feecoin", Amount: sdkmath.NewInt(1)}, AllowPartial: false, @@ -516,18 +516,18 @@ func TestOrder_GetAssets(t *testing.T) { tests := []struct { name string order *Order - expected sdk.Coins + expected sdk.Coin expPanic string }{ { name: "AskOrder", - order: NewOrder(1).WithAsk(&AskOrder{Assets: sdk.NewCoins(sdk.NewInt64Coin("acorns", 85))}), - expected: sdk.NewCoins(sdk.NewInt64Coin("acorns", 85)), + order: NewOrder(1).WithAsk(&AskOrder{Assets: sdk.NewInt64Coin("acorns", 85)}), + expected: sdk.NewInt64Coin("acorns", 85), }, { name: "BidOrder", order: NewOrder(2).WithBid(&BidOrder{Assets: sdk.NewInt64Coin("boogers", 3)}), - expected: sdk.NewCoins(sdk.NewInt64Coin("boogers", 3)), + expected: sdk.NewInt64Coin("boogers", 3), }, { name: "nil inside order", @@ -543,7 +543,7 @@ func TestOrder_GetAssets(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var assets sdk.Coins + var assets sdk.Coin testFunc := func() { assets = tc.order.GetAssets() } @@ -767,7 +767,7 @@ func TestOrder_GetHoldAmount(t *testing.T) { { name: "AskOrder", order: NewOrder(1).WithAsk(&AskOrder{ - Assets: sdk.NewCoins(sdk.NewInt64Coin("acorns", 85)), + Assets: sdk.NewInt64Coin("acorns", 85), SellerSettlementFlatFee: &sdk.Coin{Denom: "bananas", Amount: sdkmath.NewInt(12)}, }), expected: sdk.NewCoins(sdk.NewInt64Coin("acorns", 85), sdk.NewInt64Coin("bananas", 12)), @@ -906,33 +906,26 @@ func TestAskOrder_GetOwner(t *testing.T) { } func TestAskOrder_GetAssets(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} - } + largeAmt, ok := sdkmath.NewIntFromString("25000000000000000000000") + require.Truef(t, ok, "NewIntFromString(\"25000000000000000000000\")") + largeCoin := sdk.NewCoin("large", largeAmt) + negCoin := sdk.Coin{Denom: "neg", Amount: sdkmath.NewInt(-88)} tests := []struct { name string order AskOrder - exp sdk.Coins + exp sdk.Coin }{ - {name: "nil", order: AskOrder{Assets: nil}, exp: nil}, - {name: "empty", order: AskOrder{Assets: sdk.Coins{}}, exp: sdk.Coins{}}, - {name: "one denom", order: AskOrder{Assets: sdk.NewCoins(coin(3, "the"))}, exp: sdk.NewCoins(coin(3, "the"))}, - { - name: "three denoms", - order: AskOrder{Assets: sdk.NewCoins(coin(1, "one"), coin(2, "two"), coin(3, "three"))}, - exp: sdk.NewCoins(coin(1, "one"), coin(2, "two"), coin(3, "three")), - }, - { - name: "a negative coin", - order: AskOrder{Assets: sdk.Coins{coin(-1, "neg")}}, - exp: sdk.Coins{coin(-1, "neg")}, - }, + {name: "one", order: AskOrder{Assets: sdk.NewInt64Coin("one", 1)}, exp: sdk.NewInt64Coin("one", 1)}, + {name: "zero", order: AskOrder{Assets: sdk.NewInt64Coin("zero", 0)}, exp: sdk.NewInt64Coin("zero", 0)}, + {name: "negative", order: AskOrder{Assets: negCoin}, exp: negCoin}, + {name: "large amount", order: AskOrder{Assets: largeCoin}, exp: largeCoin}, + {name: "zero-value", order: AskOrder{Assets: sdk.Coin{}}, exp: sdk.Coin{}}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coins + var actual sdk.Coin testFunc := func() { actual = tc.order.GetAssets() } @@ -1054,39 +1047,33 @@ func TestAskOrder_GetHoldAmount(t *testing.T) { order AskOrder exp sdk.Coins }{ - { - name: "empty order", - order: AskOrder{}, - exp: nil, - }, { name: "just assets", order: AskOrder{ - Assets: sdk.NewCoins(sdk.NewInt64Coin("acorn", 12), sdk.NewInt64Coin("banana", 99)), + Assets: sdk.NewInt64Coin("acorn", 12), }, - exp: sdk.NewCoins(sdk.NewInt64Coin("acorn", 12), sdk.NewInt64Coin("banana", 99)), + exp: sdk.NewCoins(sdk.NewInt64Coin("acorn", 12)), }, { name: "settlement fee is different denom from price", order: AskOrder{ - Assets: sdk.NewCoins(sdk.NewInt64Coin("acorn", 12), sdk.NewInt64Coin("banana", 99)), + Assets: sdk.NewInt64Coin("acorn", 12), Price: sdk.NewInt64Coin("cucumber", 8), SellerSettlementFlatFee: &sdk.Coin{Denom: "durian", Amount: sdkmath.NewInt(52)}, }, exp: sdk.NewCoins( sdk.NewInt64Coin("acorn", 12), - sdk.NewInt64Coin("banana", 99), sdk.NewInt64Coin("durian", 52), ), }, { name: "settlement fee is same denom as price", order: AskOrder{ - Assets: sdk.NewCoins(sdk.NewInt64Coin("acorn", 12), sdk.NewInt64Coin("banana", 99)), + Assets: sdk.NewInt64Coin("acorn", 12), Price: sdk.NewInt64Coin("cucumber", 8), SellerSettlementFlatFee: &sdk.Coin{Denom: "cucumber", Amount: sdkmath.NewInt(52)}, }, - exp: sdk.NewCoins(sdk.NewInt64Coin("acorn", 12), sdk.NewInt64Coin("banana", 99)), + exp: sdk.NewCoins(sdk.NewInt64Coin("acorn", 12)), }, } @@ -1106,11 +1093,6 @@ func TestAskOrder_Validate(t *testing.T) { coin := func(amount int64, denom string) *sdk.Coin { return &sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } - coins := func(coins string) sdk.Coins { - rv, err := sdk.ParseCoinsNormalized(coins) - require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) - return rv - } tests := []struct { name string @@ -1122,7 +1104,7 @@ func TestAskOrder_Validate(t *testing.T) { order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("control_address_____").String(), - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: *coin(42, "farnsworth"), SellerSettlementFlatFee: coin(1, "farnsworth"), AllowPartial: false, @@ -1134,7 +1116,7 @@ func TestAskOrder_Validate(t *testing.T) { order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("control_address_____").String(), - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: *coin(42, "farnsworth"), SellerSettlementFlatFee: coin(1, "farnsworth"), AllowPartial: true, @@ -1146,29 +1128,19 @@ func TestAskOrder_Validate(t *testing.T) { order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("control_address_____").String(), - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: *coin(42, "farnsworth"), SellerSettlementFlatFee: nil, AllowPartial: false, }, exp: nil, }, - { - name: "multiple assets", - order: AskOrder{ - MarketId: 1, - Seller: sdk.AccAddress("another_address_____").String(), - Assets: coins("12amy,99bender,8fry,112leela,1zoidberg"), - Price: *coin(42, "farnsworth"), - }, - exp: nil, - }, { name: "market id zero", order: AskOrder{ MarketId: 0, Seller: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: *coin(42, "farnsworth"), }, exp: []string{"invalid market id", "must not be zero"}, @@ -1178,7 +1150,7 @@ func TestAskOrder_Validate(t *testing.T) { order: AskOrder{ MarketId: 1, Seller: "shady_address_______", - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: *coin(42, "farnsworth"), }, exp: []string{"invalid seller", "invalid separator index -1"}, @@ -1188,7 +1160,7 @@ func TestAskOrder_Validate(t *testing.T) { order: AskOrder{ MarketId: 1, Seller: "", - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: *coin(42, "farnsworth"), }, exp: []string{"invalid seller", "empty address string is not allowed"}, @@ -1198,7 +1170,7 @@ func TestAskOrder_Validate(t *testing.T) { order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: *coin(0, "farnsworth"), }, exp: []string{"invalid price", "cannot be zero"}, @@ -1208,7 +1180,7 @@ func TestAskOrder_Validate(t *testing.T) { order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: *coin(-24, "farnsworth"), }, exp: []string{"invalid price", "negative coin amount: -24"}, @@ -1218,7 +1190,7 @@ func TestAskOrder_Validate(t *testing.T) { order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: *coin(42, "7"), }, exp: []string{"invalid price", "invalid denom: 7"}, @@ -1228,7 +1200,7 @@ func TestAskOrder_Validate(t *testing.T) { order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("seller_address______").String(), - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: sdk.Coin{}, }, exp: []string{"invalid price", "invalid denom"}, @@ -1238,67 +1210,57 @@ func TestAskOrder_Validate(t *testing.T) { order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("another_address_____").String(), - Assets: sdk.Coins{*coin(99, "bender"), *coin(0, "leela"), *coin(1, "zoidberg")}, + Assets: *coin(0, "leela"), Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid assets", "coin leela amount is not positive"}, + exp: []string{"invalid assets", "cannot be zero"}, }, { name: "negative amount in assets", order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("another_address_____").String(), - Assets: sdk.Coins{*coin(99, "bender"), *coin(-1, "leela"), *coin(1, "zoidberg")}, + Assets: *coin(-1, "leela"), Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid assets", "coin leela amount is not positive"}, + exp: []string{"invalid assets", "negative coin amount: -1"}, }, { name: "invalid denom in assets", order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("another_address_____").String(), - Assets: sdk.Coins{*coin(99, "bender"), *coin(1, "x"), *coin(1, "zoidberg")}, + Assets: *coin(1, "x"), Price: *coin(42, "farnsworth"), }, exp: []string{"invalid assets", "invalid denom: x"}, }, { - name: "nil assets", - order: AskOrder{ - MarketId: 1, - Seller: sdk.AccAddress("another_address_____").String(), - Assets: nil, - Price: *coin(42, "farnsworth"), - }, - exp: []string{"invalid assets", "must not be empty"}, - }, - { - name: "empty assets", + name: "zero-value assets", order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("another_address_____").String(), - Assets: sdk.Coins{}, + Assets: sdk.Coin{}, Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid assets", "must not be empty"}, + exp: []string{"invalid assets", "invalid denom: "}, }, { name: "price denom in assets", order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender,2farnsworth,44amy"), + Assets: *coin(2, "farnsworth"), Price: *coin(42, "farnsworth"), }, - exp: []string{"invalid assets", "cannot contain price denom farnsworth"}, + exp: []string{"invalid assets", "price denom farnsworth cannot also be the assets denom"}, }, { name: "invalid seller settlement flat fee denom", order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: *coin(42, "farnsworth"), SellerSettlementFlatFee: coin(13, "x"), }, @@ -1309,7 +1271,7 @@ func TestAskOrder_Validate(t *testing.T) { order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: *coin(42, "farnsworth"), SellerSettlementFlatFee: coin(0, "nibbler"), }, @@ -1320,7 +1282,7 @@ func TestAskOrder_Validate(t *testing.T) { order: AskOrder{ MarketId: 1, Seller: sdk.AccAddress("another_address_____").String(), - Assets: coins("99bender"), + Assets: *coin(99, "bender"), Price: *coin(42, "farnsworth"), SellerSettlementFlatFee: coin(-3, "nibbler"), }, @@ -1412,18 +1374,18 @@ func TestBidOrder_GetAssets(t *testing.T) { tests := []struct { name string order BidOrder - exp sdk.Coins + exp sdk.Coin }{ - {name: "one", order: BidOrder{Assets: sdk.NewInt64Coin("one", 1)}, exp: sdk.Coins{sdk.NewInt64Coin("one", 1)}}, - {name: "zero", order: BidOrder{Assets: sdk.NewInt64Coin("zero", 0)}, exp: sdk.Coins{sdk.NewInt64Coin("zero", 0)}}, - {name: "negative", order: BidOrder{Assets: negCoin}, exp: sdk.Coins{negCoin}}, - {name: "large amount", order: BidOrder{Assets: largeCoin}, exp: sdk.Coins{largeCoin}}, - {name: "zero-value", order: BidOrder{Assets: sdk.Coin{}}, exp: sdk.Coins{sdk.Coin{}}}, + {name: "one", order: BidOrder{Assets: sdk.NewInt64Coin("one", 1)}, exp: sdk.NewInt64Coin("one", 1)}, + {name: "zero", order: BidOrder{Assets: sdk.NewInt64Coin("zero", 0)}, exp: sdk.NewInt64Coin("zero", 0)}, + {name: "negative", order: BidOrder{Assets: negCoin}, exp: negCoin}, + {name: "large amount", order: BidOrder{Assets: largeCoin}, exp: largeCoin}, + {name: "zero-value", order: BidOrder{Assets: sdk.Coin{}}, exp: sdk.Coin{}}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coins + var actual sdk.Coin testFunc := func() { actual = tc.order.GetAssets() } From 60d31da10cf9ded38ea5604f55ef89c4c6d5543e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 22 Sep 2023 19:00:11 -0600 Subject: [PATCH 167/309] [1658]: Get most of the fulfillment stuff finally coded. Still need to handle resulting partial order though. --- x/exchange/fulfillment.go | 480 ++++++++++++++++++++++--------- x/exchange/keeper/fulfillment.go | 32 ++- x/exchange/keeper/keeper.go | 16 ++ 3 files changed, 387 insertions(+), 141 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index c9766ebc25..aa03133d1b 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -5,7 +5,6 @@ import ( "fmt" sdkmath "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) @@ -24,7 +23,7 @@ func NewIndexedAddrAmts() *IndexedAddrAmts { } // Add adds the coins to the input with the given address (creating it if needed). -func (i *IndexedAddrAmts) Add(addr string, coin sdk.Coin) { +func (i *IndexedAddrAmts) Add(addr string, coins ...sdk.Coin) { n, known := i.indexes[addr] if !known { n = len(i.addrs) @@ -32,7 +31,7 @@ func (i *IndexedAddrAmts) Add(addr string, coin sdk.Coin) { i.addrs = append(i.addrs, addr) i.amts = append(i.amts, sdk.NewCoins()) } - i.amts[n] = i.amts[n].Add(coin) + i.amts[n] = i.amts[n].Add(coins...) } // GetAsInputs returns all the entries as bank Inputs. @@ -77,6 +76,9 @@ type OrderFulfillment struct { PriceFilledAmt sdkmath.Int // PriceLeftAmt is the price that has not yet been fulfilled for the order. PriceLeftAmt sdkmath.Int + // FeesToPay is the amount of fees to pay for this order. + // This is not tracked as fulfillments are applied, it is only set during Finalize(). + FeesToPay sdk.Coins // FeesLeft is the amount fees left to pay (if this order is only partially filled). // This is not tracked as fulfillments are applied, it is only set during Finalize(). FeesLeft sdk.Coins @@ -116,6 +118,16 @@ func (f OrderFulfillment) GetPriceLeft() sdk.Coin { return sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceLeftAmt} } +// IsFullyFilled returns true if this fulfillment's order has been fully accounted for. +func (f OrderFulfillment) IsFullyFilled() bool { + return f.AssetsLeftAmt.IsZero() +} + +// IsCompletelyUnfulfilled returns true if nothing in this order has been filled. +func (f OrderFulfillment) IsCompletelyUnfulfilled() bool { + return len(f.Splits) == 0 || f.AssetsFilledAmt.IsZero() +} + // GetOrderID gets this fulfillment's order's id. func (f OrderFulfillment) GetOrderID() uint64 { return f.Order.GetOrderID() @@ -176,16 +188,135 @@ func (f OrderFulfillment) GetHoldAmount() sdk.Coins { return f.Order.GetHoldAmount() } -// Validate does nothing but satisfies the OrderI interface. +// Validate does some final validation and sanity checking on this order fulfillment. +// It's assumed that Finalize has been called before calling this. func (f OrderFulfillment) Validate() error { + _ = &OrderFulfillment{ + Order: nil, + AssetsFilledAmt: sdkmath.Int{}, + AssetsLeftAmt: sdkmath.Int{}, + PriceFilledAmt: sdkmath.Int{}, + PriceLeftAmt: sdkmath.Int{}, + FeesToPay: nil, + FeesLeft: nil, + Splits: nil, + } + + var splitsAssets, splitsPrice sdk.Coins + for _, split := range f.Splits { + splitsAssets = splitsAssets.Add(split.Assets) + splitsPrice = splitsPrice.Add(split.Price) + } + + if len(splitsAssets) != 1 { + return fmt.Errorf("multiple asset denoms %q in splits applied to %s order %d", + splitsAssets, f.GetOrderType(), f.GetOrderID()) + } + orderAssets := f.GetAssets() + if splitsAssets[0].Denom != orderAssets.Denom { + return fmt.Errorf("splits asset denom %q does not match order assets %q on %s order %d", + splitsAssets, orderAssets, f.GetOrderType(), f.GetOrderID()) + } + if !splitsAssets[0].Amount.Equal(f.AssetsFilledAmt) { + return fmt.Errorf("splits asset total %q does not match filled assets %q on %s order %d", + splitsAssets, orderAssets, f.GetOrderType(), f.GetOrderID()) + } + + if len(splitsPrice) != 1 { + return fmt.Errorf("multiple price denoms %q in splits applied to %s order %d", + splitsPrice, f.GetOrderType(), f.GetOrderID()) + } + orderPrice := f.GetPrice() + if splitsPrice[0].Denom != orderPrice.Denom { + return fmt.Errorf("splits price denom %q does not match order price %q on %s order %d", + splitsPrice, orderPrice, f.GetOrderType(), f.GetOrderID()) + } + if !splitsPrice[0].Amount.Equal(f.PriceFilledAmt) { + return fmt.Errorf("splits price total %q does not match filled price %q on %s order %d", + splitsPrice, orderPrice, f.GetOrderType(), f.GetOrderID()) + } + + if f.AssetsLeftAmt.IsNegative() { + return fmt.Errorf("%s order %d having assets %q has negative assets left %q after filling %q", + f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsLeft(), f.GetAssetsFilled()) + } + if f.PriceLeftAmt.IsNegative() { + return fmt.Errorf("%s order %d having price %q has negative price left %q after filling %q", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceLeft(), f.GetPriceFilled()) + } + + isFullyFilled := f.IsFullyFilled() + switch { + case f.IsAskOrder(): + // For ask orders, if being fully filled, the price filled needs to be at least the order price. + if isFullyFilled && f.PriceFilledAmt.LT(orderPrice.Amount) { + return fmt.Errorf("%s order %d having price %q cannot be filled at price %q: unsufficient price", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceFilled()) + } + case f.IsBidOrder(): + // If filled in full, the PriceFilledAmt must be equal to the order price. + if isFullyFilled && !f.PriceFilledAmt.Equal(orderPrice.Amount) { + return fmt.Errorf("%s order %d having price %q cannot be fully filled at price %q: price mismatch", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceFilled()) + } + // otherwise, the price filled must be less than the order price. + if !isFullyFilled && f.PriceFilledAmt.GTE(orderPrice.Amount) { + return fmt.Errorf("%s order %d having price %q cannot be partially filled at price %q: price mismatch", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceFilled()) + } + default: + return fmt.Errorf("order %d has unknown type %s", f.GetOrderID(), f.GetOrderType()) + } + + if !isFullyFilled && !f.PartialFillAllowed() { + return fmt.Errorf("cannot fill %s order %d having assets %q with assets %q: order does not allow partial fill", + f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled()) + } + + return nil +} + +// Apply adjusts this order fulfillment using the provided info. +func (f *OrderFulfillment) Apply(order *OrderFulfillment, assetsAmt, priceAmt sdkmath.Int) error { + assets := sdk.NewCoin(order.GetAssets().Denom, assetsAmt) + price := sdk.NewCoin(order.GetPrice().Denom, priceAmt) + + newAssetsLeftAmt := f.AssetsLeftAmt.Sub(assetsAmt) + if newAssetsLeftAmt.IsNegative() { + return fmt.Errorf("cannot fill %s order %d having assets left %q with %q from %s order %d: overfill", + f.GetOrderType(), f.GetOrderID(), f.GetAssetsLeft(), assets, order.GetOrderType(), order.GetOrderID()) + } + + newPriceLeftAmt := f.PriceLeftAmt.Sub(priceAmt) + // ask orders are allow to go negative on price left, but bid orders are not. + if newPriceLeftAmt.IsNegative() && f.IsBidOrder() { + return fmt.Errorf("cannot apply %s order %d having price left %q to %s order %d at a price of %q: overfill", + f.GetOrderType(), f.GetOrderID(), f.GetPriceLeft(), order.GetOrderType(), order.GetOrderID(), price) + } + + f.AssetsLeftAmt = newAssetsLeftAmt + f.AssetsFilledAmt = f.AssetsFilledAmt.Add(assetsAmt) + f.PriceLeftAmt = newPriceLeftAmt + f.PriceFilledAmt = f.PriceFilledAmt.Add(priceAmt) + f.Splits = append(f.Splits, &OrderSplit{ + Order: order, + Assets: assets, + Price: price, + }) return nil } // Finalize does some final calculations and validation for this order fulfillment. // This order fulfillment and the ones in it maybe updated during this. -func (f *OrderFulfillment) Finalize() error { - if len(f.Splits) == 0 || f.AssetsFilledAmt.IsZero() { - return fmt.Errorf("%s order %d not even partially filled", f.GetOrderType(), f.GetOrderID()) +func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) error { + if len(f.Splits) == 0 { + return fmt.Errorf("no splits applied to %s order %d", f.GetOrderType(), f.GetOrderID()) + } + if f.AssetsFilledAmt.IsZero() { + return fmt.Errorf("cannot fill %s order %d with zero assets", f.GetOrderType(), f.GetOrderID()) + } + if f.PriceFilledAmt.IsZero() { + return fmt.Errorf("cannot fill %s order %d with zero price", f.GetOrderType(), f.GetOrderID()) } if f.AssetsLeftAmt.IsNegative() { @@ -196,15 +327,12 @@ func (f *OrderFulfillment) Finalize() error { isAskOrder, isBidOrder := f.IsAskOrder(), f.IsBidOrder() orderAssets := f.GetAssets() orderPrice := f.GetPrice() + orderFees := f.GetSettlementFees() targetPriceAmt := orderPrice.Amount - if !f.AssetsLeftAmt.IsZero() { - if !f.PartialFillAllowed() { - return fmt.Errorf("%s order %d having assets %q cannot be partially filled with %q: "+ - "order does not allow partial fulfillment", - f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled()) - } - + isFullyFilled := f.IsFullyFilled() + if !isFullyFilled { + // Make sure the price can be split on a whole number. priceAssets := orderPrice.Amount.Mul(f.AssetsFilledAmt) priceRem := priceAssets.Mod(orderAssets.Amount) if !priceRem.IsZero() { @@ -213,34 +341,66 @@ func (f *OrderFulfillment) Finalize() error { f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled(), orderPrice) } targetPriceAmt = priceAssets.Quo(orderAssets.Amount) - } - if isAskOrder { - // For ask orders, we need the updated price to have the same price/asset ratio - // as when originally created. Since ask orders can receive more payment than they requested, - // the PriceLeftAmt in here might be less than that, and we need to fix it. - f.PriceLeftAmt = orderPrice.Amount.Sub(targetPriceAmt) + // Make sure the fees can be split on a whole number. + for _, orderFee := range orderFees { + feeAssets := orderFee.Amount.Mul(f.AssetsFilledAmt) + feeRem := feeAssets.Mul(orderAssets.Amount) + if !feeRem.IsZero() { + return fmt.Errorf("%s order %d having settlement fees %q cannot be partially filled by %q: "+ + "fee %q is not evenly divisible", + f.GetOrderType(), f.GetOrderID(), orderFees, f.GetAssetsFilled(), orderFee) + } + feeAmtToPay := feeAssets.Quo(orderAssets.Amount) + f.FeesToPay = f.FeesToPay.Add(sdk.NewCoin(orderFee.Denom, feeAmtToPay)) + } + feesLeft, hasNeg := orderFees.SafeSub(f.FeesToPay...) + if hasNeg { + return fmt.Errorf("%s order %d having fees %q has negative fees left %q after applying %q", + f.GetOrderType(), f.GetOrderID(), orderFees, feesLeft, f.FeesToPay) + } + f.FeesLeft = feesLeft + } else { + f.FeesToPay = orderFees + f.FeesLeft = nil } - if isBidOrder { + switch { + case isAskOrder: + if !isFullyFilled { + // For partially filled ask orders, we need to maintain the same price/asset ratio. Since ask orders + // can receive more payment than requested, the PriceLeftAmt might be too low, so correct it now. + f.PriceLeftAmt = orderPrice.Amount.Sub(targetPriceAmt) + } + // For ask orders, we need to calculate and add the ratio fee to the fees to pay. + if sellerFeeRatio != nil { + feeToPay, err := sellerFeeRatio.ApplyToLoosely(f.GetPriceFilled()) + if err != nil { + return fmt.Errorf("could not calculate %s order %d ratio fee: %w", + f.GetOrderType(), f.GetOrderID(), err) + } + f.FeesToPay = f.FeesToPay.Add(feeToPay) + } + case isBidOrder: + // When adding things to f.PriceFilledAmt, we used truncation on the divisions. + // So, at this point, it might be a little less than the target price. + // If that's the case, we distribute the difference weighte by assets in order of the splits. // When adding things to f.PriceFilledAmt, we used truncation on the divisions. // So at this point, it might be a little less than the target price. // If that's the case, we distribute the difference weighted by assets in order of the splits. toDistribute := targetPriceAmt.Sub(f.PriceFilledAmt) if toDistribute.IsNegative() { - return fmt.Errorf("bid order %d having price %q cannot pay %q for %q: overfill", - f.GetOrderID(), f.GetPrice(), f.PriceFilledAmt, f.GetAssetsFilled()) + return fmt.Errorf("%s order %d having price %q cannot pay %q for %q: overfill", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.PriceFilledAmt, f.GetAssetsFilled()) } if toDistribute.IsPositive() { distLeft := toDistribute // First pass, we won't default to 1 (if the calc comes up zero). // This helps weight larger orders that are at the end of the list. - // But it's possible for all the calcs to come up zero, so eventually - // we might need to default to one. + // But it's possible for all the calcs to come up zero, so after + // the first pass, use a minimum of 1 for each distribution. minOne := false for distLeft.IsPositive() { - // OrderFulfillment won't let a bid order with multiple assets be split. - // So we know that here, there's only one asset. for _, askSplit := range f.Splits { distAmt := toDistribute.Mul(askSplit.Assets.Amount).Quo(f.AssetsFilledAmt) if distAmt.IsZero() { @@ -256,7 +416,7 @@ func (f *OrderFulfillment) Finalize() error { f.PriceLeftAmt = f.PriceLeftAmt.Sub(distAmt) askSplit.Price.Amount = askSplit.Price.Amount.Add(distAmt) askSplit.Order.PriceFilledAmt = askSplit.Order.PriceFilledAmt.Add(distAmt) - // Not updating askSplit.Order.PriceLeftAmt here since that's done specially above. + // Not updating askSplit.Order.PriceLeftAmt here since that's done more directly above. for _, bidSplit := range askSplit.Order.Splits { if bidSplit.Order.GetOrderID() == f.GetOrderID() { bidSplit.Price.Amount = bidSplit.Price.Amount.Add(distAmt) @@ -270,92 +430,17 @@ func (f *OrderFulfillment) Finalize() error { } minOne = true } - } - } - // TODO[1658]: Finish up Finalize(). - if !f.AssetsLeftAmt.IsZero() { - var assetsFilledAmt, orderAssetsAmt sdkmath.Int // Temporary line so we can still compile. - orderFees := f.GetSettlementFees() - for _, orderFee := range orderFees { - feeAssets := orderFee.Amount.Mul(assetsFilledAmt) - feeRem := feeAssets.Mul(orderAssetsAmt) - if !feeRem.IsZero() { - return fmt.Errorf("%s order %d having settlement fees %q cannot be partially filled by %q: "+ - "fee %q is not evenly divisible", - f.GetOrderType(), f.GetOrderID(), orderFees, f.GetAssetsFilled(), orderFee) + // If being partially filled, the PriceFilledAmt must now equal the target price. + if !isFullyFilled && !f.PriceFilledAmt.Equal(targetPriceAmt) { + return fmt.Errorf("%s order %d having assets %q and price %q cannot be partially filled "+ + "with %q assets at price %q: expected price %q", + f.GetOrderType(), f.GetOrderID(), orderAssets, orderPrice, + f.GetAssetsFilled(), f.GetPriceFilled(), sdk.Coin{Denom: orderPrice.Denom, Amount: targetPriceAmt}) } - feeAmtLeft := feeAssets.Quo(orderAssetsAmt) - f.FeesLeft = f.FeesLeft.Add(sdk.NewCoin(orderFee.Denom, feeAmtLeft)) } } - panic("not implemented") -} - -// IsFilled returns true if this fulfillment's order has been fully accounted for. -func (f OrderFulfillment) IsFilled() bool { - return f.AssetsLeftAmt.IsZero() -} - -// getAssetInputsOutputs gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. -func (f OrderFulfillment) getAssetInputsOutputs() ([]banktypes.Input, []banktypes.Output, error) { - indexedSplits := NewIndexedAddrAmts() - for _, split := range f.Splits { - indexedSplits.Add(split.Order.GetOwner(), split.Assets) - } - - if f.IsAskOrder() { - inputs := []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.Coins{f.GetAssetsFilled()}}} - outputs := indexedSplits.GetAsOutputs() - return inputs, outputs, nil - } - if f.IsBidOrder() { - inputs := indexedSplits.GetAsInputs() - outputs := []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.Coins{f.GetAssetsFilled()}}} - return inputs, outputs, nil - } - return nil, nil, fmt.Errorf("unknown order type %T", f.Order.GetOrder()) -} - -// getPriceInputsOutputs gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. -func (f OrderFulfillment) getPriceInputsOutputs() ([]banktypes.Input, []banktypes.Output, error) { - indexedSplits := NewIndexedAddrAmts() - for _, split := range f.Splits { - indexedSplits.Add(split.Order.GetOwner(), split.Price) - } - - if f.IsAskOrder() { - inputs := indexedSplits.GetAsInputs() - outputs := []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.Coins{f.GetPriceFilled()}}} - return inputs, outputs, nil - } - if f.IsBidOrder() { - inputs := []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.Coins{f.GetPriceFilled()}}} - outputs := indexedSplits.GetAsOutputs() - return inputs, outputs, nil - } - return nil, nil, fmt.Errorf("unknown order type %T", f.Order.GetOrder()) -} - -// Apply adjusts this order fulfillment using the provided info. -func (f *OrderFulfillment) Apply(order *OrderFulfillment, assetsAmt, priceAmt sdkmath.Int) error { - assets := sdk.NewCoin(order.GetAssets().Denom, assetsAmt) - price := sdk.NewCoin(order.GetPrice().Denom, priceAmt) - newAssetsLeftAmt := f.AssetsLeftAmt.Sub(assetsAmt) - if newAssetsLeftAmt.IsNegative() { - return fmt.Errorf("cannot fill %s order %d having assets left %q with %q from %s order %d: overfill", - f.GetOrderType(), f.GetOrderID(), f.GetAssetsLeft(), assets, order.GetOrderType(), order.GetOrderID()) - } - f.AssetsLeftAmt = newAssetsLeftAmt - f.AssetsFilledAmt = f.AssetsFilledAmt.Add(assetsAmt) - f.PriceLeftAmt = f.PriceLeftAmt.Sub(priceAmt) - f.PriceFilledAmt = f.PriceFilledAmt.Add(priceAmt) - f.Splits = append(f.Splits, &OrderSplit{ - Order: order, - Assets: assets, - Price: price, - }) return nil } @@ -378,12 +463,10 @@ func Fulfill(of1, of2 *OrderFulfillment) error { bidOF = of1 } - askOrderID, bidOrderID := askOF.GetOrderID(), bidOF.GetOrderID() askOrder, bidOrder := askOF.Order.GetAskOrder(), bidOF.Order.GetBidOrder() - if askOrder.Price.Denom != bidOrder.Price.Denom { return fmt.Errorf("cannot fill bid order %d having price %q with ask order %d having price %q: denom mismatch", - bidOrderID, bidOrder.Price, askOrderID, askOrder.Price) + bidOF.GetOrderID(), bidOrder.Price, askOF.GetOrderID(), askOrder.Price) } assetsAmt, err := getFulfillmentAssetsAmt(askOF, bidOF) @@ -391,7 +474,7 @@ func Fulfill(of1, of2 *OrderFulfillment) error { return err } - // We calculate the price amount based off the original order assets (as opposed to assets left) + // We calculate the price amount based off the original bid order assets (as opposed to assets left) // for consistent truncation and remainders. Once we've identified all the fulfillment relationships, // we'll enumerate and redistribute those remainders. priceAmt := bidOrder.Price.Amount @@ -408,34 +491,161 @@ func Fulfill(of1, of2 *OrderFulfillment) error { // getFulfillmentAssetsAmt figures out the assets that can be fulfilled with the two provided orders. // It's assumed that the askOF is for an ask order, and the bidOF is for a bid order. func getFulfillmentAssetsAmt(askOF, bidOF *OrderFulfillment) (sdkmath.Int, error) { - askAssetsLeft, bidAssetsLeft := askOF.GetAssetsLeft(), bidOF.GetAssetsLeft() - askOrderID, bidOrderID := askOF.GetOrderID(), bidOF.GetOrderID() - stdErr := func(msg string) error { - return fmt.Errorf("cannot fill ask order %d having assets left %q with bid order %d having assets left %q: %s", - askOrderID, askAssetsLeft, bidOrderID, bidAssetsLeft, msg) + askAmtLeft, bidAmtLeft := askOF.AssetsLeftAmt, bidOF.AssetsLeftAmt + if !askAmtLeft.IsPositive() || !bidAmtLeft.IsPositive() { + return sdkmath.ZeroInt(), fmt.Errorf("cannot fill ask order %d having assets left %q "+ + "with bid order %d having assets left %q: zero or negative assets left", + askOF.GetOrderID(), askOF.GetAssetsLeft(), bidOF.GetOrderID(), bidOF.GetAssetsLeft()) } - askAmt, bidAmt := askOF.AssetsLeftAmt, bidOF.AssetsLeftAmt - if askAmt.IsZero() || askAmt.IsNegative() || bidAmt.IsZero() || bidAmt.IsNegative() { - return sdkmath.ZeroInt(), stdErr("zero or negative assets") + // Return the lesser of the two. + if askAmtLeft.LTE(bidAmtLeft) { + return askAmtLeft, nil } + return bidAmtLeft, nil +} - // If they're equal, we're all good, just return one of them. - if askAmt.Equal(bidAmt) { - return askAmt, nil +// BuildFulfillments creates all of the ask and bid order fulfillments. +func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) ([]*OrderFulfillment, []*OrderFulfillment, error) { + askOFs := make([]*OrderFulfillment, len(askOrders)) + for i, askOrder := range askOrders { + askOFs[i] = NewOrderFulfillment(askOrder) } - // Use the lesser of the two. - if askAmt.LT(bidAmt) { - return askAmt, nil + bidOFs := make([]*OrderFulfillment, len(bidOrders)) + for i, bidOrder := range bidOrders { + bidOFs[i] = NewOrderFulfillment(bidOrder) } - return bidAmt, nil + + var a, b int + for a < len(askOFs) && b < len(bidOFs) { + err := Fulfill(askOFs[a], bidOFs[b]) + if err != nil { + return nil, nil, err + } + askFilled := askOFs[a].IsFullyFilled() + bidFilled := bidOFs[b].IsFullyFilled() + if !askFilled && !bidFilled { + return nil, nil, fmt.Errorf("neither ask order %d nor bid order %d could be filled in full", + askOFs[a].GetOrderID(), bidOFs[b].GetOrderID()) + } + if askFilled { + a++ + } + if bidFilled { + b++ + } + } + + // Need to finalize bid orders first due to possible extra price distribution. + for _, bidOF := range bidOFs { + if err := bidOF.Finalize(sellerFeeRatio); err != nil { + return nil, nil, err + } + } + for _, askOF := range askOFs { + if err := askOF.Finalize(sellerFeeRatio); err != nil { + return nil, nil, err + } + } + + for _, bidOF := range bidOFs { + if err := bidOF.Validate(); err != nil { + return nil, nil, err + } + } + for _, askOF := range askOFs { + if err := askOF.Validate(); err != nil { + return nil, nil, err + } + } + + return askOFs, bidOFs, nil +} + +// Transfer contains bank inputs and outputs indicating a transfer that needs to be made. +type Transfer struct { + Inputs []banktypes.Input + Outputs []banktypes.Output +} + +// SettlementTransfers has everything needed to do all the transfers for a settlement. +type SettlementTransfers struct { + OrderTransfers []*Transfer + FeeInputs []banktypes.Input +} + +// BuildSettlementTransfers creates all the order transfers needed for the provided fulfillments. +func BuildSettlementTransfers(askOFs, bidOFs []*OrderFulfillment) *SettlementTransfers { + indexedFees := NewIndexedAddrAmts() + + rv := &SettlementTransfers{} + applyOF := func(of *OrderFulfillment) { + rv.OrderTransfers = append(rv.OrderTransfers, getAssetTransfer(of), getPriceTransfer(of)) + if !of.FeesToPay.IsZero() { + // Using NewCoins in here (instead of Coins{...}) as a last-ditch negative amount panic check. + fees := sdk.NewCoins(of.FeesToPay...) + indexedFees.Add(of.GetOwner(), fees...) + } + } + + for _, askOF := range askOFs { + applyOF(askOF) + } + for _, bidOf := range bidOFs { + applyOF(bidOf) + } + + rv.FeeInputs = indexedFees.GetAsInputs() + + return rv +} + +// getAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. +func getAssetTransfer(f *OrderFulfillment) *Transfer { + indexedSplits := NewIndexedAddrAmts() + for _, split := range f.Splits { + indexedSplits.Add(split.Order.GetOwner(), split.Assets) + } + + // Using NewCoins in here (instead of Coins{...}) as a last-ditch negative amount panic check. + if f.IsAskOrder() { + return &Transfer{ + Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, + Outputs: indexedSplits.GetAsOutputs(), + } + } + if f.IsBidOrder() { + return &Transfer{ + Inputs: indexedSplits.GetAsInputs(), + Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, + } + } + + // panicking in here if there's an error since it really should have happened earlier anyway. + panic(fmt.Errorf("unknown order type %T", f.Order.GetOrder())) } -type OrderTransfers struct { - AssetInputs []banktypes.Input - AssetOutputs []banktypes.Output - PriceInputs []banktypes.Input - PriceOutputs []banktypes.Output - FeeInputs []banktypes.Input - FeeTotal sdk.Coins +// getPriceTransfer gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. +func getPriceTransfer(f *OrderFulfillment) *Transfer { + indexedSplits := NewIndexedAddrAmts() + for _, split := range f.Splits { + indexedSplits.Add(split.Order.GetOwner(), split.Price) + } + + // Using NewCoins in here (instead of Coins{...}) as a last-ditch negative amount panic check. + if f.IsAskOrder() { + return &Transfer{ + Inputs: indexedSplits.GetAsInputs(), + Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceFilled())}}, + } + } + if f.IsBidOrder() { + return &Transfer{ + Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceFilled())}}, + Outputs: indexedSplits.GetAsOutputs(), + } + } + + // panicking in here if there's an error since it really should have happened earlier anyway. + panic(fmt.Errorf("unknown order type %T", f.Order.GetOrder())) } diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index 50e046706e..1b3bddc721 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -255,11 +255,11 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro assetOutputs := []banktypes.Output{{Address: msg.Buyer, Coins: totalAssets}} priceInputs := []banktypes.Input{{Address: msg.Buyer, Coins: sdk.Coins{msg.TotalPrice}}} - if err := k.bankKeeper.InputOutputCoins(ctx, assetInputs, assetOutputs); err != nil { + if err := k.DoTransfer(ctx, assetInputs, assetOutputs); err != nil { return fmt.Errorf("error transferring assets from sellers to buyer: %w", err) } - if err := k.bankKeeper.InputOutputCoins(ctx, priceInputs, priceOutputs); err != nil { + if err := k.DoTransfer(ctx, priceInputs, priceOutputs); err != nil { return fmt.Errorf("error transferring price from buyer to sellers: %w", err) } @@ -300,8 +300,6 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO totalAssetsForSale, totalAskPrice := sumAssetsAndPrice(askOrders) totalAssetsToBuy, totalBidPrice := sumAssetsAndPrice(bidOrders) - // TODO[1659]: Allow for multiple asset denoms in some cases. - var errs []error if len(totalAssetsForSale) != 1 { errs = append(errs, fmt.Errorf("cannot settle with multiple ask order asset denoms %q", totalAssetsForSale)) @@ -340,6 +338,28 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO totalAssetsForSale, totalAssetsToBuy) } - // TODO[1658]: Implement SettleOrders. - panic("Not implemented") + sellerFeeRatio, err := getSellerSettlementRatio(store, marketID, totalAskPrice[0].Denom) + if err != nil { + return err + } + + askOFs, bidOfs, err := exchange.BuildFulfillments(askOrders, bidOrders, sellerFeeRatio) + if err != nil { + return err + } + + transfers := exchange.BuildSettlementTransfers(askOFs, bidOfs) + + for _, transfer := range transfers.OrderTransfers { + if err = k.DoTransfer(ctx, transfer.Inputs, transfer.Outputs); err != nil { + return err + } + } + + if err = k.CollectFees(ctx, transfers.FeeInputs, marketID); err != nil { + return err + } + + // TODO[1658]: Update the partial order and emit events. + panic("not finished") } diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index c642e82974..7b5a4a66ef 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -153,6 +153,21 @@ func (k Keeper) CalculateExchangeSplit(ctx sdk.Context, feeAmt sdk.Coins) sdk.Co return exchangeAmt } +// DoTransfer facilitates a transfer of things using the bank module. +func (k Keeper) DoTransfer(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) error { + if len(inputs) == 1 && len(outputs) == 1 { + // If there's only one of each, we use SendCoins for the nicer events. + if !exchange.CoinsEquals(inputs[0].Coins, outputs[0].Coins) { + return fmt.Errorf("input coins %q does not equal output coins %q", + inputs[0].Coins, outputs[0].Coins) + } + fromAddr := sdk.MustAccAddressFromBech32(inputs[0].Address) + toAddr := sdk.MustAccAddressFromBech32(outputs[0].Address) + return k.bankKeeper.SendCoins(ctx, fromAddr, toAddr, inputs[0].Coins) + } + return k.bankKeeper.InputOutputCoins(ctx, inputs, outputs) +} + // CollectFee will transfer the fee amount to the market account, // then the exchange's cut from the market to the fee collector. // If you have fees to collect from multiple payers, consider using CollectFees. @@ -183,6 +198,7 @@ func (k Keeper) CollectFees(ctx sdk.Context, inputs []banktypes.Input, marketID return nil } if len(inputs) == 1 { + // If there's only one input, just use CollectFee for the nicer events. payer, err := sdk.AccAddressFromBech32(inputs[0].Address) if err != nil { return fmt.Errorf("invalid payer address %q: %w", inputs[0].Address, err) From a9f219d14bf6a8b981e22fdeb6e544a0592ca8f5 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 22 Sep 2023 19:31:38 -0600 Subject: [PATCH 168/309] [1659]: Set the partial order and emit events during settlement. --- x/exchange/events.go | 2 +- x/exchange/events_test.go | 16 ++++--- x/exchange/fulfillment.go | 81 ++++++++++++++++++++++++++++---- x/exchange/keeper/fulfillment.go | 38 ++++++++++++--- 4 files changed, 115 insertions(+), 22 deletions(-) diff --git a/x/exchange/events.go b/x/exchange/events.go index d4bef3a380..a82c80df5f 100644 --- a/x/exchange/events.go +++ b/x/exchange/events.go @@ -26,7 +26,7 @@ func NewEventOrderFilled(orderID uint64) *EventOrderFilled { } } -func NewEventOrderPartiallyFilled(orderID uint64, assetsFilled, feesFilled sdk.Coins) *EventOrderPartiallyFilled { +func NewEventOrderPartiallyFilled(orderID uint64, assetsFilled, feesFilled sdk.Coin) *EventOrderPartiallyFilled { return &EventOrderPartiallyFilled{ OrderId: orderID, AssetsFilled: assetsFilled.String(), diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go index 75832aaaf1..d7aeda1c72 100644 --- a/x/exchange/events_test.go +++ b/x/exchange/events_test.go @@ -100,8 +100,8 @@ func TestNewEventOrderFilled(t *testing.T) { func TestNewEventOrderPartiallyFilled(t *testing.T) { orderID := uint64(18) - assetsFilled := sdk.NewCoins(sdk.NewInt64Coin("first", 111), sdk.NewInt64Coin("second", 22)) - feesFilled := sdk.NewCoins(sdk.NewInt64Coin("charge", 8), sdk.NewInt64Coin("fee", 15)) + assetsFilled := sdk.NewInt64Coin("acoin", 111) + feesFilled := sdk.NewInt64Coin("fcoin", 8) event := NewEventOrderPartiallyFilled(orderID, assetsFilled, feesFilled) assert.Equal(t, orderID, event.OrderId, "OrderId") @@ -299,8 +299,10 @@ func TestTypedEventToEvent(t *testing.T) { updatedByQ := quoteBz(updatedBy.String()) coins1 := sdk.NewCoins(sdk.NewInt64Coin("onecoin", 1), sdk.NewInt64Coin("twocoin", 2)) coins1Q := quoteBz(coins1.String()) - coins2 := sdk.NewCoins(sdk.NewInt64Coin("threecoin", 3), sdk.NewInt64Coin("fourcoin", 4)) - coins2Q := quoteBz(coins2.String()) + acoin := sdk.NewInt64Coin("acoin", 5) + acoinQ := quoteBz(acoin.String()) + fcoin := sdk.NewInt64Coin("fcoin", 5) + fcoinQ := quoteBz(fcoin.String()) tests := []struct { name string @@ -352,12 +354,12 @@ func TestTypedEventToEvent(t *testing.T) { }, { name: "EventOrderPartiallyFilled", - tev: NewEventOrderPartiallyFilled(5, coins1, coins2), + tev: NewEventOrderPartiallyFilled(5, acoin, fcoin), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderPartiallyFilled", Attributes: []abci.EventAttribute{ - {Key: []byte("assets_filled"), Value: coins1Q}, - {Key: []byte("fees_filled"), Value: coins2Q}, + {Key: []byte("assets_filled"), Value: acoinQ}, + {Key: []byte("fees_filled"), Value: fcoinQ}, {Key: []byte("order_id"), Value: quoteBz("5")}, }, }, diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index aa03133d1b..51d5d1fe3d 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -505,8 +505,63 @@ func getFulfillmentAssetsAmt(askOF, bidOF *OrderFulfillment) (sdkmath.Int, error return bidAmtLeft, nil } +type PartialFulfillment struct { + NewOrder *Order + AssetsFilled sdk.Coin + PriceFilled sdk.Coin +} + +func NewPartialFulfillment(f *OrderFulfillment) *PartialFulfillment { + order := NewOrder(f.GetOrderID()) + if f.IsAskOrder() { + askOrder := &AskOrder{ + MarketId: f.GetMarketID(), + Seller: f.GetOwner(), + Assets: f.GetAssetsLeft(), + Price: f.GetPriceLeft(), + AllowPartial: f.PartialFillAllowed(), + } + if !f.FeesLeft.IsZero() { + if len(f.FeesLeft) > 1 { + panic(fmt.Errorf("partially filled ask order %d somehow has multiple denoms in fees left %q", + order.OrderId, f.FeesLeft)) + } + askOrder.SellerSettlementFlatFee = &f.FeesLeft[0] + } + return &PartialFulfillment{ + NewOrder: order, + AssetsFilled: f.GetAssetsFilled(), + PriceFilled: f.GetPriceFilled(), + } + } + + if f.IsBidOrder() { + bidOrder := &BidOrder{ + MarketId: f.GetMarketID(), + Buyer: f.GetOwner(), + Assets: f.GetAssetsLeft(), + Price: f.GetPriceLeft(), + BuyerSettlementFees: f.FeesLeft, + AllowPartial: f.PartialFillAllowed(), + } + return &PartialFulfillment{ + NewOrder: order.WithBid(bidOrder), + AssetsFilled: f.GetAssetsFilled(), + PriceFilled: f.GetPriceFilled(), + } + } + + panic(fmt.Errorf("order %d has unknown type %q", order.OrderId, f.GetOrderType())) +} + +type Fulfillments struct { + AskOFs []*OrderFulfillment + BidOFs []*OrderFulfillment + PartialOrder *PartialFulfillment +} + // BuildFulfillments creates all of the ask and bid order fulfillments. -func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) ([]*OrderFulfillment, []*OrderFulfillment, error) { +func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) (*Fulfillments, error) { askOFs := make([]*OrderFulfillment, len(askOrders)) for i, askOrder := range askOrders { askOFs[i] = NewOrderFulfillment(askOrder) @@ -520,12 +575,12 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) for a < len(askOFs) && b < len(bidOFs) { err := Fulfill(askOFs[a], bidOFs[b]) if err != nil { - return nil, nil, err + return nil, err } askFilled := askOFs[a].IsFullyFilled() bidFilled := bidOFs[b].IsFullyFilled() if !askFilled && !bidFilled { - return nil, nil, fmt.Errorf("neither ask order %d nor bid order %d could be filled in full", + return nil, fmt.Errorf("neither ask order %d nor bid order %d could be filled in full", askOFs[a].GetOrderID(), bidOFs[b].GetOrderID()) } if askFilled { @@ -539,27 +594,37 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) // Need to finalize bid orders first due to possible extra price distribution. for _, bidOF := range bidOFs { if err := bidOF.Finalize(sellerFeeRatio); err != nil { - return nil, nil, err + return nil, err } } for _, askOF := range askOFs { if err := askOF.Finalize(sellerFeeRatio); err != nil { - return nil, nil, err + return nil, err } } for _, bidOF := range bidOFs { if err := bidOF.Validate(); err != nil { - return nil, nil, err + return nil, err } } for _, askOF := range askOFs { if err := askOF.Validate(); err != nil { - return nil, nil, err + return nil, err } } - return askOFs, bidOFs, nil + rv := &Fulfillments{ + AskOFs: askOFs, + BidOFs: bidOFs, + } + if !askOFs[len(askOFs)-1].IsFullyFilled() { + rv.PartialOrder = NewPartialFulfillment(askOFs[len(askOFs)-1]) + } else if !bidOFs[len(bidOFs)-1].IsFullyFilled() { + rv.PartialOrder = NewPartialFulfillment(bidOFs[len(bidOFs)-1]) + } + + return rv, nil } // Transfer contains bank inputs and outputs indicating a transfer that needs to be made. diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index 1b3bddc721..1aeb6ac184 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -329,11 +329,12 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO return errors.Join(errs...) } - if !expectPartial && !exchange.CoinsEquals(totalAssetsForSale, totalAssetsToBuy) { + isPartial := !exchange.CoinsEquals(totalAssetsForSale, totalAssetsToBuy) + if !expectPartial && isPartial { return fmt.Errorf("total assets for sale %q does not equal total assets to buy %q and partial settlement not expected", totalAssetsForSale, totalAssetsToBuy) } - if expectPartial && exchange.CoinsEquals(totalAssetsForSale, totalAssetsToBuy) { + if expectPartial && !isPartial { return fmt.Errorf("total assets for sale %q equals total assets to buy %q but partial settlement is expected", totalAssetsForSale, totalAssetsToBuy) } @@ -343,12 +344,12 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO return err } - askOFs, bidOfs, err := exchange.BuildFulfillments(askOrders, bidOrders, sellerFeeRatio) + fulfillments, err := exchange.BuildFulfillments(askOrders, bidOrders, sellerFeeRatio) if err != nil { return err } - transfers := exchange.BuildSettlementTransfers(askOFs, bidOfs) + transfers := exchange.BuildSettlementTransfers(fulfillments.AskOFs, fulfillments.BidOFs) for _, transfer := range transfers.OrderTransfers { if err = k.DoTransfer(ctx, transfer.Inputs, transfer.Outputs); err != nil { @@ -360,6 +361,31 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO return err } - // TODO[1658]: Update the partial order and emit events. - panic("not finished") + if fulfillments.PartialOrder != nil { + if err = k.setOrderInStore(store, *fulfillments.PartialOrder.NewOrder); err != nil { + return fmt.Errorf("could not update partial %s order %d: %w", + fulfillments.PartialOrder.NewOrder.GetOrderType(), fulfillments.PartialOrder.NewOrder.OrderId, err) + } + } + + events := make([]proto.Message, 0, len(askOrders)+len(bidOrders)) + for _, order := range askOrders { + if fulfillments.PartialOrder != nil && fulfillments.PartialOrder.NewOrder.OrderId != order.OrderId { + events = append(events, exchange.NewEventOrderFilled(order.OrderId)) + } + } + for _, order := range bidOrders { + if fulfillments.PartialOrder != nil && fulfillments.PartialOrder.NewOrder.OrderId != order.OrderId { + events = append(events, exchange.NewEventOrderFilled(order.OrderId)) + } + } + if fulfillments.PartialOrder != nil { + events = append(events, exchange.NewEventOrderPartiallyFilled( + fulfillments.PartialOrder.NewOrder.OrderId, + fulfillments.PartialOrder.AssetsFilled, + fulfillments.PartialOrder.PriceFilled, + )) + } + + return ctx.EventManager().EmitTypedEvents(events...) } From 65f71dd763bb142af5a72a523cbc41030ddaad92 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 25 Sep 2023 09:23:32 -0600 Subject: [PATCH 169/309] [1658]: Have BuildSettlementTransfers take in a Fulfillments instead of two slices of OrderFulfillment. --- x/exchange/fulfillment.go | 101 ++++++++++++++++--------------- x/exchange/keeper/fulfillment.go | 2 +- 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 51d5d1fe3d..3fec3ab96f 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -5,53 +5,11 @@ import ( "fmt" sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) -// IndexedAddrAmts is a set of addresses and amounts. -type IndexedAddrAmts struct { - addrs []string - amts []sdk.Coins - indexes map[string]int -} - -func NewIndexedAddrAmts() *IndexedAddrAmts { - return &IndexedAddrAmts{ - indexes: make(map[string]int), - } -} - -// Add adds the coins to the input with the given address (creating it if needed). -func (i *IndexedAddrAmts) Add(addr string, coins ...sdk.Coin) { - n, known := i.indexes[addr] - if !known { - n = len(i.addrs) - i.indexes[addr] = n - i.addrs = append(i.addrs, addr) - i.amts = append(i.amts, sdk.NewCoins()) - } - i.amts[n] = i.amts[n].Add(coins...) -} - -// GetAsInputs returns all the entries as bank Inputs. -func (i *IndexedAddrAmts) GetAsInputs() []banktypes.Input { - rv := make([]banktypes.Input, len(i.addrs)) - for n, addr := range i.addrs { - rv[n] = banktypes.Input{Address: addr, Coins: i.amts[n]} - } - return rv -} - -// GetAsOutputs returns all the entries as bank Outputs. -func (i *IndexedAddrAmts) GetAsOutputs() []banktypes.Output { - rv := make([]banktypes.Output, len(i.addrs)) - for n, addr := range i.addrs { - rv[n] = banktypes.Output{Address: addr, Coins: i.amts[n]} - } - return rv -} - // OrderSplit contains an order, and the asset and price amounts that should come out of it. type OrderSplit struct { // Order fulfillment associated with this split. @@ -384,7 +342,7 @@ func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) error { case isBidOrder: // When adding things to f.PriceFilledAmt, we used truncation on the divisions. // So, at this point, it might be a little less than the target price. - // If that's the case, we distribute the difference weighte by assets in order of the splits. + // If that's the case, we distribute the difference weighted by assets in order of the splits. // When adding things to f.PriceFilledAmt, we used truncation on the divisions. // So at this point, it might be a little less than the target price. // If that's the case, we distribute the difference weighted by assets in order of the splits. @@ -627,6 +585,49 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) return rv, nil } +// indexedAddrAmts is a set of addresses and amounts. +type indexedAddrAmts struct { + addrs []string + amts []sdk.Coins + indexes map[string]int +} + +func newIndexedAddrAmts() *indexedAddrAmts { + return &indexedAddrAmts{ + indexes: make(map[string]int), + } +} + +// Add adds the coins to the input with the given address (creating it if needed). +func (i *indexedAddrAmts) Add(addr string, coins ...sdk.Coin) { + n, known := i.indexes[addr] + if !known { + n = len(i.addrs) + i.indexes[addr] = n + i.addrs = append(i.addrs, addr) + i.amts = append(i.amts, sdk.NewCoins()) + } + i.amts[n] = i.amts[n].Add(coins...) +} + +// GetAsInputs returns all the entries as bank Inputs. +func (i *indexedAddrAmts) GetAsInputs() []banktypes.Input { + rv := make([]banktypes.Input, len(i.addrs)) + for n, addr := range i.addrs { + rv[n] = banktypes.Input{Address: addr, Coins: i.amts[n]} + } + return rv +} + +// GetAsOutputs returns all the entries as bank Outputs. +func (i *indexedAddrAmts) GetAsOutputs() []banktypes.Output { + rv := make([]banktypes.Output, len(i.addrs)) + for n, addr := range i.addrs { + rv[n] = banktypes.Output{Address: addr, Coins: i.amts[n]} + } + return rv +} + // Transfer contains bank inputs and outputs indicating a transfer that needs to be made. type Transfer struct { Inputs []banktypes.Input @@ -640,8 +641,8 @@ type SettlementTransfers struct { } // BuildSettlementTransfers creates all the order transfers needed for the provided fulfillments. -func BuildSettlementTransfers(askOFs, bidOFs []*OrderFulfillment) *SettlementTransfers { - indexedFees := NewIndexedAddrAmts() +func BuildSettlementTransfers(fulfillments *Fulfillments) *SettlementTransfers { + indexedFees := newIndexedAddrAmts() rv := &SettlementTransfers{} applyOF := func(of *OrderFulfillment) { @@ -653,10 +654,10 @@ func BuildSettlementTransfers(askOFs, bidOFs []*OrderFulfillment) *SettlementTra } } - for _, askOF := range askOFs { + for _, askOF := range fulfillments.AskOFs { applyOF(askOF) } - for _, bidOf := range bidOFs { + for _, bidOf := range fulfillments.BidOFs { applyOF(bidOf) } @@ -667,7 +668,7 @@ func BuildSettlementTransfers(askOFs, bidOFs []*OrderFulfillment) *SettlementTra // getAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. func getAssetTransfer(f *OrderFulfillment) *Transfer { - indexedSplits := NewIndexedAddrAmts() + indexedSplits := newIndexedAddrAmts() for _, split := range f.Splits { indexedSplits.Add(split.Order.GetOwner(), split.Assets) } @@ -692,7 +693,7 @@ func getAssetTransfer(f *OrderFulfillment) *Transfer { // getPriceTransfer gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. func getPriceTransfer(f *OrderFulfillment) *Transfer { - indexedSplits := NewIndexedAddrAmts() + indexedSplits := newIndexedAddrAmts() for _, split := range f.Splits { indexedSplits.Add(split.Order.GetOwner(), split.Price) } diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index 1aeb6ac184..768e6cd14f 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -349,7 +349,7 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO return err } - transfers := exchange.BuildSettlementTransfers(fulfillments.AskOFs, fulfillments.BidOFs) + transfers := exchange.BuildSettlementTransfers(fulfillments) for _, transfer := range transfers.OrderTransfers { if err = k.DoTransfer(ctx, transfer.Inputs, transfer.Outputs); err != nil { From 17055d429acdc3dfb1a4df1274e15282a2da138a Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 25 Sep 2023 11:28:56 -0600 Subject: [PATCH 170/309] [1658]: Add unit test for DefaultGenesisState. --- x/exchange/genesis_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/x/exchange/genesis_test.go b/x/exchange/genesis_test.go index 6cff371dca..86769835ce 100644 --- a/x/exchange/genesis_test.go +++ b/x/exchange/genesis_test.go @@ -3,6 +3,7 @@ package exchange import ( "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" sdkmath "cosmossdk.io/math" @@ -11,6 +12,24 @@ import ( "github.com/provenance-io/provenance/testutil/assertions" ) +func TestDefaultGenesisState(t *testing.T) { + expected := &GenesisState{ + Params: &Params{ + DefaultSplit: DefaultDefaultSplit, + DenomSplits: nil, + }, + Markets: nil, + Orders: nil, + LastMarketId: 0, + } + var actual *GenesisState + testFunc := func() { + actual = DefaultGenesisState() + } + require.NotPanics(t, testFunc, "DefaultGenesisState()") + assert.Equal(t, expected, actual, "DefaultGenesisState() result") +} + func TestGenesisState_Validate(t *testing.T) { addr1 := sdk.AccAddress("addr1_______________").String() coin := func(amount int64, denom string) sdk.Coin { From 7000e3ac8ad2d840f0cb8c3204786511591d5a2d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 25 Sep 2023 11:34:39 -0600 Subject: [PATCH 171/309] [1658]: Add todos for funcs to unit test. --- x/exchange/fulfillment_test.go | 66 +++++++++++++++++++++++++++++++++- x/exchange/msg_test.go | 4 +++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 66afe0aef9..16eb630e3e 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -1,3 +1,67 @@ package exchange -// TODO[1658]: Unit tests on the fulfillment stuff. +// TODO[1658]: func TestNewOrderFulfillment(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetAssetsFilled(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetAssetsLeft(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetPriceFilled(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetPriceLeft(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_IsFullyFilled(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_IsCompletelyUnfulfilled(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetOrderID(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_IsAskOrder(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_IsBidOrder(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetMarketID(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetOwner(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetAssets(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetPrice(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetSettlementFees(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_PartialFillAllowed(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetOrderType(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetOrderTypeByte(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetHoldAmount(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_Validate(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_Apply(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_Finalize(t *testing.T) + +// TODO[1658]: func TestFulfill(t *testing.T) + +// TODO[1658]: func TestGetFulfillmentAssetsAmt(t *testing.T) + +// TODO[1658]: func TestNewPartialFulfillment(t *testing.T) + +// TODO[1658]: func TestBuildFulfillments(t *testing.T) + +// TODO[1658]: func TestNewIndexedAddrAmts(t *testing.T) + +// TODO[1658]: func TestIndexedAddrAmts_Add(t *testing.T) + +// TODO[1658]: func TestIndexedAddrAmts_GetAsInputs(t *testing.T) + +// TODO[1658]: func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) + +// TODO[1658]: func TestBuildSettlementTransfers(t *testing.T) + +// TODO[1658]: func TestGetAssetTransfer(t *testing.T) + +// TODO[1658]: func TestGetPriceTransfer(t *testing.T) diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index ab15f98163..3faec9b732 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -1342,6 +1342,8 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { } } +// TODO[1658]: func TestMsgMarketManagePermissionsRequest_HasUpdates(t *testing.T) + func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { goodAdmin := sdk.AccAddress("goodAdmin___________").String() @@ -1504,6 +1506,8 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { } } +// TODO[1658]: func TestMsgMarketManageReqAttrsRequest_HasUpdates(t *testing.T) + func TestMsgGovCreateMarketRequest_ValidateBasic(t *testing.T) { authority := sdk.AccAddress("authority___________").String() From 9d7b49982063298013927f342ba4e02ef8704810 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 25 Sep 2023 11:47:13 -0600 Subject: [PATCH 172/309] [1658]: Unit tests on the HasUpdates funcs. --- x/exchange/msg_test.go | 113 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 2 deletions(-) diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 3faec9b732..1927f1ee3b 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -1342,7 +1342,58 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { } } -// TODO[1658]: func TestMsgMarketManagePermissionsRequest_HasUpdates(t *testing.T) +func TestMsgMarketManagePermissionsRequest_HasUpdates(t *testing.T) { + tests := []struct { + name string + msg MsgMarketManagePermissionsRequest + exp bool + }{ + { + name: "empty", + msg: MsgMarketManagePermissionsRequest{}, + exp: false, + }, + { + name: "empty except for admin", + msg: MsgMarketManagePermissionsRequest{ + Admin: "admin", + }, + exp: false, + }, + { + name: "one revoke all", + msg: MsgMarketManagePermissionsRequest{ + RevokeAll: []string{"revoke_all"}, + }, + exp: true, + }, + { + name: "one to revoke", + msg: MsgMarketManagePermissionsRequest{ + ToRevoke: []AccessGrant{{Address: "to_revoke"}}, + }, + exp: true, + }, + { + name: "one to grant", + msg: MsgMarketManagePermissionsRequest{ + ToGrant: []AccessGrant{{Address: "to_grant"}}, + }, + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.msg.HasUpdates() + } + require.NotPanics(t, testFunc, "%T.HasUpdates()", tc.msg) + assert.Equal(t, tc.exp, actual, "%T.HasUpdates()", tc.msg) + }) + } +} func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { goodAdmin := sdk.AccAddress("goodAdmin___________").String() @@ -1506,7 +1557,65 @@ func TestMsgMarketManageReqAttrsRequest_ValidateBasic(t *testing.T) { } } -// TODO[1658]: func TestMsgMarketManageReqAttrsRequest_HasUpdates(t *testing.T) +func TestMsgMarketManageReqAttrsRequest_HasUpdates(t *testing.T) { + tests := []struct { + name string + msg MsgMarketManageReqAttrsRequest + exp bool + }{ + { + name: "empty", + msg: MsgMarketManageReqAttrsRequest{}, + exp: false, + }, + { + name: "empty except for admin", + msg: MsgMarketManageReqAttrsRequest{ + Admin: "admin", + }, + exp: false, + }, + { + name: "one ask to add", + msg: MsgMarketManageReqAttrsRequest{ + CreateAskToAdd: []string{"ask_to_add"}, + }, + exp: true, + }, + { + name: "one ask to remove", + msg: MsgMarketManageReqAttrsRequest{ + CreateAskToRemove: []string{"ask_to_remove"}, + }, + exp: true, + }, + { + name: "one bid to add", + msg: MsgMarketManageReqAttrsRequest{ + CreateBidToAdd: []string{"bid_to_add"}, + }, + exp: true, + }, + { + name: "one bid to remove", + msg: MsgMarketManageReqAttrsRequest{ + CreateBidToRemove: []string{"bid_to_remove"}, + }, + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.msg.HasUpdates() + } + require.NotPanics(t, testFunc, "%T.HasUpdates()", tc.msg) + assert.Equal(t, tc.exp, actual, "%T.HasUpdates()", tc.msg) + }) + } +} func TestMsgGovCreateMarketRequest_ValidateBasic(t *testing.T) { authority := sdk.AccAddress("authority___________").String() From b9c21bf93fc587d7190a7a3855031bed9e6d30e4 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 25 Sep 2023 12:28:36 -0600 Subject: [PATCH 173/309] [1658]: In fulfillment.go: Make some things public that were private. Add some struct and field comments. Tweak GetFulfillmentAssetsAmt to be a little more generic. --- x/exchange/fulfillment.go | 111 ++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 3fec3ab96f..7085dd19ac 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -33,6 +33,7 @@ type OrderFulfillment struct { // If this is an ask order, the PriceFilledAmt is related to the prices of the bid orders fulfilling this order. PriceFilledAmt sdkmath.Int // PriceLeftAmt is the price that has not yet been fulfilled for the order. + // This can be negative for ask orders that are being filled at a higher price than requested. PriceLeftAmt sdkmath.Int // FeesToPay is the amount of fees to pay for this order. // This is not tracked as fulfillments are applied, it is only set during Finalize(). @@ -427,7 +428,7 @@ func Fulfill(of1, of2 *OrderFulfillment) error { bidOF.GetOrderID(), bidOrder.Price, askOF.GetOrderID(), askOrder.Price) } - assetsAmt, err := getFulfillmentAssetsAmt(askOF, bidOF) + assetsAmt, err := GetFulfillmentAssetsAmt(askOF, bidOF) if err != nil { return err } @@ -446,31 +447,46 @@ func Fulfill(of1, of2 *OrderFulfillment) error { return errors.Join(askErr, bidErr) } -// getFulfillmentAssetsAmt figures out the assets that can be fulfilled with the two provided orders. -// It's assumed that the askOF is for an ask order, and the bidOF is for a bid order. -func getFulfillmentAssetsAmt(askOF, bidOF *OrderFulfillment) (sdkmath.Int, error) { - askAmtLeft, bidAmtLeft := askOF.AssetsLeftAmt, bidOF.AssetsLeftAmt - if !askAmtLeft.IsPositive() || !bidAmtLeft.IsPositive() { - return sdkmath.ZeroInt(), fmt.Errorf("cannot fill ask order %d having assets left %q "+ - "with bid order %d having assets left %q: zero or negative assets left", - askOF.GetOrderID(), askOF.GetAssetsLeft(), bidOF.GetOrderID(), bidOF.GetAssetsLeft()) +// GetFulfillmentAssetsAmt figures out the assets that can be fulfilled with the two provided orders. +func GetFulfillmentAssetsAmt(of1, of2 *OrderFulfillment) (sdkmath.Int, error) { + of1AmtLeft, of2AmtLeft := of1.AssetsLeftAmt, of2.AssetsLeftAmt + if !of1AmtLeft.IsPositive() || !of2AmtLeft.IsPositive() { + return sdkmath.ZeroInt(), fmt.Errorf("cannot fill %s order %d having assets left %q "+ + "with %s order %d having assets left %q: zero or negative assets left", + of1.GetOrderType(), of1.GetOrderID(), of1.GetAssetsLeft(), + of2.GetOrderType(), of2.GetOrderID(), of2.GetAssetsLeft()) } // Return the lesser of the two. - if askAmtLeft.LTE(bidAmtLeft) { - return askAmtLeft, nil + if of1AmtLeft.LTE(of2AmtLeft) { + return of1AmtLeft, nil } - return bidAmtLeft, nil + return of2AmtLeft, nil } +// Fulfillments contains information on how orders are to be fulfilled. +type Fulfillments struct { + // AskOFs are all the ask orders and how they are to be filled. + AskOFs []*OrderFulfillment + // BidOFs are all the bid orders and how they are to be filled. + BidOFs []*OrderFulfillment + // PartialOrder contains info on an order that is only being partially filled. + PartialOrder *PartialFulfillment +} + +// PartialFulfillment contains the remains of a partially filled order, and info on what was filled. type PartialFulfillment struct { - NewOrder *Order + // NewOrder is an updated version of the partially filled order with reduced amounts. + NewOrder *Order + // AssetsFilled is the amount of order assets that were filled. AssetsFilled sdk.Coin - PriceFilled sdk.Coin + // PriceFilled is the amount of the order price that was filled. + PriceFilled sdk.Coin } func NewPartialFulfillment(f *OrderFulfillment) *PartialFulfillment { order := NewOrder(f.GetOrderID()) + if f.IsAskOrder() { askOrder := &AskOrder{ MarketId: f.GetMarketID(), @@ -488,8 +504,8 @@ func NewPartialFulfillment(f *OrderFulfillment) *PartialFulfillment { } return &PartialFulfillment{ NewOrder: order, - AssetsFilled: f.GetAssetsFilled(), - PriceFilled: f.GetPriceFilled(), + AssetsFilled: f.GetAssets().Sub(f.GetAssetsLeft()), + PriceFilled: f.GetPrice().Sub(f.GetPriceLeft()), } } @@ -504,20 +520,14 @@ func NewPartialFulfillment(f *OrderFulfillment) *PartialFulfillment { } return &PartialFulfillment{ NewOrder: order.WithBid(bidOrder), - AssetsFilled: f.GetAssetsFilled(), - PriceFilled: f.GetPriceFilled(), + AssetsFilled: f.GetAssets().Sub(f.GetAssetsLeft()), + PriceFilled: f.GetPrice().Sub(f.GetPriceLeft()), } } panic(fmt.Errorf("order %d has unknown type %q", order.OrderId, f.GetOrderType())) } -type Fulfillments struct { - AskOFs []*OrderFulfillment - BidOFs []*OrderFulfillment - PartialOrder *PartialFulfillment -} - // BuildFulfillments creates all of the ask and bid order fulfillments. func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) (*Fulfillments, error) { askOFs := make([]*OrderFulfillment, len(askOrders)) @@ -587,8 +597,11 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) // indexedAddrAmts is a set of addresses and amounts. type indexedAddrAmts struct { - addrs []string - amts []sdk.Coins + // addrs are a list of all addresses that have amounts. + addrs []string + // amts are a list of the coin amounts for each address (by slice index). + amts []sdk.Coins + // indexes are the index value for each address. indexes map[string]int } @@ -598,8 +611,8 @@ func newIndexedAddrAmts() *indexedAddrAmts { } } -// Add adds the coins to the input with the given address (creating it if needed). -func (i *indexedAddrAmts) Add(addr string, coins ...sdk.Coin) { +// add adds the coins to the given address. +func (i *indexedAddrAmts) add(addr string, coins ...sdk.Coin) { n, known := i.indexes[addr] if !known { n = len(i.addrs) @@ -610,8 +623,8 @@ func (i *indexedAddrAmts) Add(addr string, coins ...sdk.Coin) { i.amts[n] = i.amts[n].Add(coins...) } -// GetAsInputs returns all the entries as bank Inputs. -func (i *indexedAddrAmts) GetAsInputs() []banktypes.Input { +// getAsInputs returns all the entries as bank Inputs. +func (i *indexedAddrAmts) getAsInputs() []banktypes.Input { rv := make([]banktypes.Input, len(i.addrs)) for n, addr := range i.addrs { rv[n] = banktypes.Input{Address: addr, Coins: i.amts[n]} @@ -619,8 +632,8 @@ func (i *indexedAddrAmts) GetAsInputs() []banktypes.Input { return rv } -// GetAsOutputs returns all the entries as bank Outputs. -func (i *indexedAddrAmts) GetAsOutputs() []banktypes.Output { +// getAsOutputs returns all the entries as bank Outputs. +func (i *indexedAddrAmts) getAsOutputs() []banktypes.Output { rv := make([]banktypes.Output, len(i.addrs)) for n, addr := range i.addrs { rv[n] = banktypes.Output{Address: addr, Coins: i.amts[n]} @@ -630,14 +643,18 @@ func (i *indexedAddrAmts) GetAsOutputs() []banktypes.Output { // Transfer contains bank inputs and outputs indicating a transfer that needs to be made. type Transfer struct { - Inputs []banktypes.Input + // Inputs are the inputs that make up this transfer. + Inputs []banktypes.Input + // Outputs are the outputs that make up this transfer. Outputs []banktypes.Output } // SettlementTransfers has everything needed to do all the transfers for a settlement. type SettlementTransfers struct { + // OrderTransfers are all of the asset and price transfers needed to facilitate a settlement. OrderTransfers []*Transfer - FeeInputs []banktypes.Input + // FeeInputs are all of the inputs needed to facilitate payment of fees to a market. + FeeInputs []banktypes.Input } // BuildSettlementTransfers creates all the order transfers needed for the provided fulfillments. @@ -646,11 +663,11 @@ func BuildSettlementTransfers(fulfillments *Fulfillments) *SettlementTransfers { rv := &SettlementTransfers{} applyOF := func(of *OrderFulfillment) { - rv.OrderTransfers = append(rv.OrderTransfers, getAssetTransfer(of), getPriceTransfer(of)) + rv.OrderTransfers = append(rv.OrderTransfers, GetAssetTransfer(of), GetPriceTransfer(of)) if !of.FeesToPay.IsZero() { // Using NewCoins in here (instead of Coins{...}) as a last-ditch negative amount panic check. fees := sdk.NewCoins(of.FeesToPay...) - indexedFees.Add(of.GetOwner(), fees...) + indexedFees.add(of.GetOwner(), fees...) } } @@ -661,28 +678,28 @@ func BuildSettlementTransfers(fulfillments *Fulfillments) *SettlementTransfers { applyOF(bidOf) } - rv.FeeInputs = indexedFees.GetAsInputs() + rv.FeeInputs = indexedFees.getAsInputs() return rv } -// getAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. -func getAssetTransfer(f *OrderFulfillment) *Transfer { +// GetAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. +func GetAssetTransfer(f *OrderFulfillment) *Transfer { indexedSplits := newIndexedAddrAmts() for _, split := range f.Splits { - indexedSplits.Add(split.Order.GetOwner(), split.Assets) + indexedSplits.add(split.Order.GetOwner(), split.Assets) } // Using NewCoins in here (instead of Coins{...}) as a last-ditch negative amount panic check. if f.IsAskOrder() { return &Transfer{ Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, - Outputs: indexedSplits.GetAsOutputs(), + Outputs: indexedSplits.getAsOutputs(), } } if f.IsBidOrder() { return &Transfer{ - Inputs: indexedSplits.GetAsInputs(), + Inputs: indexedSplits.getAsInputs(), Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, } } @@ -691,24 +708,24 @@ func getAssetTransfer(f *OrderFulfillment) *Transfer { panic(fmt.Errorf("unknown order type %T", f.Order.GetOrder())) } -// getPriceTransfer gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. -func getPriceTransfer(f *OrderFulfillment) *Transfer { +// GetPriceTransfer gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. +func GetPriceTransfer(f *OrderFulfillment) *Transfer { indexedSplits := newIndexedAddrAmts() for _, split := range f.Splits { - indexedSplits.Add(split.Order.GetOwner(), split.Price) + indexedSplits.add(split.Order.GetOwner(), split.Price) } // Using NewCoins in here (instead of Coins{...}) as a last-ditch negative amount panic check. if f.IsAskOrder() { return &Transfer{ - Inputs: indexedSplits.GetAsInputs(), + Inputs: indexedSplits.getAsInputs(), Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceFilled())}}, } } if f.IsBidOrder() { return &Transfer{ Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceFilled())}}, - Outputs: indexedSplits.GetAsOutputs(), + Outputs: indexedSplits.getAsOutputs(), } } From b43a385e0b0ce8a0a0a8e296a0aa20e7d3f9a541 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 25 Sep 2023 17:30:57 -0600 Subject: [PATCH 174/309] [1658]: Refine the fulfillment stuff to simplify Finalize and put most of the validation in Validate. Standardize on unfilled/filled naming and use applied/left only for price as they're being applied. Split out the price field to separate applied/left and filled/unfilled for easier validation. --- x/exchange/fulfillment.go | 571 +++++++++++++++++++------------ x/exchange/fulfillment_test.go | 14 +- x/exchange/helpers.go | 12 +- x/exchange/helpers_test.go | 89 +++++ x/exchange/keeper/fulfillment.go | 7 + 5 files changed, 465 insertions(+), 228 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 7085dd19ac..caf9cf3cee 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -24,36 +24,49 @@ type OrderSplit struct { type OrderFulfillment struct { // Order is the original order with all its information. Order *Order + + // Splits contains information on the orders being used to fulfill this order. + Splits []*OrderSplit + // AssetsFilledAmt is the total amount of assets being fulfilled for the order. AssetsFilledAmt sdkmath.Int - // AssetsLeftAmt is the amount of order assets that have not yet been fulfilled for the order. - AssetsLeftAmt sdkmath.Int - // PriceFilledAmt is the total price amount involved in this order fulfillment. - // If this is a bid order, the PriceFilledAmt is related to the order price. - // If this is an ask order, the PriceFilledAmt is related to the prices of the bid orders fulfilling this order. - PriceFilledAmt sdkmath.Int + // AssetsUnfilledAmt is the amount of order assets that have not yet been fulfilled for the order. + AssetsUnfilledAmt sdkmath.Int + // PriceAppliedAmt is the total price amount involved in this order fulfillment. + // If this is a bid order, it's the actual amount the buyer will pay. + // If this is an ask order it's the actual amount the seller will receive. + PriceAppliedAmt sdkmath.Int // PriceLeftAmt is the price that has not yet been fulfilled for the order. // This can be negative for ask orders that are being filled at a higher price than requested. PriceLeftAmt sdkmath.Int - // FeesToPay is the amount of fees to pay for this order. - // This is not tracked as fulfillments are applied, it is only set during Finalize(). + + // IsFinalized is set to true once Finalize() is called without error. + IsFinalized bool + // FeesToPay is the amount of settlement fees the order owner should pay to settle this order. + // This is only set during Finalize(). FeesToPay sdk.Coins - // FeesLeft is the amount fees left to pay (if this order is only partially filled). - // This is not tracked as fulfillments are applied, it is only set during Finalize(). - FeesLeft sdk.Coins - // Splits contains information on the orders being used to fulfill this order. - Splits []*OrderSplit + // OrderFeesLeft is the amount fees settlement left to pay (if this order is only partially filled). + // This is only set during Finalize(). + OrderFeesLeft sdk.Coins + // PriceFilledAmt is the amount of the order price that is being filled. + // This is only set during Finalize(). + PriceFilledAmt sdkmath.Int + // PriceUnfilledAmt is the amount of the order price that is not being filled. + // This is only set during Finalize(). + PriceUnfilledAmt sdkmath.Int } var _ OrderI = (*OrderFulfillment)(nil) func NewOrderFulfillment(order *Order) *OrderFulfillment { return &OrderFulfillment{ - Order: order, - AssetsFilledAmt: sdkmath.ZeroInt(), - AssetsLeftAmt: order.GetAssets().Amount, - PriceFilledAmt: sdkmath.ZeroInt(), - PriceLeftAmt: order.GetPrice().Amount, + Order: order, + AssetsFilledAmt: sdkmath.ZeroInt(), + AssetsUnfilledAmt: order.GetAssets().Amount, + PriceAppliedAmt: sdkmath.ZeroInt(), + PriceLeftAmt: order.GetPrice().Amount, + PriceFilledAmt: sdkmath.ZeroInt(), + PriceUnfilledAmt: sdkmath.ZeroInt(), } } @@ -62,14 +75,14 @@ func (f OrderFulfillment) GetAssetsFilled() sdk.Coin { return sdk.Coin{Denom: f.GetAssets().Denom, Amount: f.AssetsFilledAmt} } -// GetAssetsLeft gets the coin value of the assets left to fill in this fulfillment. -func (f OrderFulfillment) GetAssetsLeft() sdk.Coin { - return sdk.Coin{Denom: f.GetAssets().Denom, Amount: f.AssetsLeftAmt} +// GetAssetsUnfilled gets the coin value of the assets left to fill in this fulfillment. +func (f OrderFulfillment) GetAssetsUnfilled() sdk.Coin { + return sdk.Coin{Denom: f.GetAssets().Denom, Amount: f.AssetsUnfilledAmt} } -// GetPriceFilled gets the coin value of the price that has been filled in this fulfillment. -func (f OrderFulfillment) GetPriceFilled() sdk.Coin { - return sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceFilledAmt} +// GetPriceApplied gets the coin value of the price that has been filled in this fulfillment. +func (f OrderFulfillment) GetPriceApplied() sdk.Coin { + return sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceAppliedAmt} } // GetPriceLeft gets the coin value of the price left to fill in this fulfillment. @@ -77,9 +90,19 @@ func (f OrderFulfillment) GetPriceLeft() sdk.Coin { return sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceLeftAmt} } +// GetPriceFilled gets the coin value of the price filled in this fulfillment. +func (f OrderFulfillment) GetPriceFilled() sdk.Coin { + return sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceFilledAmt} +} + +// GetPriceUnfilled gets the coin value of the price unfilled in this fulfillment. +func (f OrderFulfillment) GetPriceUnfilled() sdk.Coin { + return sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceUnfilledAmt} +} + // IsFullyFilled returns true if this fulfillment's order has been fully accounted for. func (f OrderFulfillment) IsFullyFilled() bool { - return f.AssetsLeftAmt.IsZero() + return !f.AssetsUnfilledAmt.IsPositive() } // IsCompletelyUnfulfilled returns true if nothing in this order has been filled. @@ -147,103 +170,15 @@ func (f OrderFulfillment) GetHoldAmount() sdk.Coins { return f.Order.GetHoldAmount() } -// Validate does some final validation and sanity checking on this order fulfillment. -// It's assumed that Finalize has been called before calling this. -func (f OrderFulfillment) Validate() error { - _ = &OrderFulfillment{ - Order: nil, - AssetsFilledAmt: sdkmath.Int{}, - AssetsLeftAmt: sdkmath.Int{}, - PriceFilledAmt: sdkmath.Int{}, - PriceLeftAmt: sdkmath.Int{}, - FeesToPay: nil, - FeesLeft: nil, - Splits: nil, - } - - var splitsAssets, splitsPrice sdk.Coins - for _, split := range f.Splits { - splitsAssets = splitsAssets.Add(split.Assets) - splitsPrice = splitsPrice.Add(split.Price) - } - - if len(splitsAssets) != 1 { - return fmt.Errorf("multiple asset denoms %q in splits applied to %s order %d", - splitsAssets, f.GetOrderType(), f.GetOrderID()) - } - orderAssets := f.GetAssets() - if splitsAssets[0].Denom != orderAssets.Denom { - return fmt.Errorf("splits asset denom %q does not match order assets %q on %s order %d", - splitsAssets, orderAssets, f.GetOrderType(), f.GetOrderID()) - } - if !splitsAssets[0].Amount.Equal(f.AssetsFilledAmt) { - return fmt.Errorf("splits asset total %q does not match filled assets %q on %s order %d", - splitsAssets, orderAssets, f.GetOrderType(), f.GetOrderID()) - } - - if len(splitsPrice) != 1 { - return fmt.Errorf("multiple price denoms %q in splits applied to %s order %d", - splitsPrice, f.GetOrderType(), f.GetOrderID()) - } - orderPrice := f.GetPrice() - if splitsPrice[0].Denom != orderPrice.Denom { - return fmt.Errorf("splits price denom %q does not match order price %q on %s order %d", - splitsPrice, orderPrice, f.GetOrderType(), f.GetOrderID()) - } - if !splitsPrice[0].Amount.Equal(f.PriceFilledAmt) { - return fmt.Errorf("splits price total %q does not match filled price %q on %s order %d", - splitsPrice, orderPrice, f.GetOrderType(), f.GetOrderID()) - } - - if f.AssetsLeftAmt.IsNegative() { - return fmt.Errorf("%s order %d having assets %q has negative assets left %q after filling %q", - f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsLeft(), f.GetAssetsFilled()) - } - if f.PriceLeftAmt.IsNegative() { - return fmt.Errorf("%s order %d having price %q has negative price left %q after filling %q", - f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceLeft(), f.GetPriceFilled()) - } - - isFullyFilled := f.IsFullyFilled() - switch { - case f.IsAskOrder(): - // For ask orders, if being fully filled, the price filled needs to be at least the order price. - if isFullyFilled && f.PriceFilledAmt.LT(orderPrice.Amount) { - return fmt.Errorf("%s order %d having price %q cannot be filled at price %q: unsufficient price", - f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceFilled()) - } - case f.IsBidOrder(): - // If filled in full, the PriceFilledAmt must be equal to the order price. - if isFullyFilled && !f.PriceFilledAmt.Equal(orderPrice.Amount) { - return fmt.Errorf("%s order %d having price %q cannot be fully filled at price %q: price mismatch", - f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceFilled()) - } - // otherwise, the price filled must be less than the order price. - if !isFullyFilled && f.PriceFilledAmt.GTE(orderPrice.Amount) { - return fmt.Errorf("%s order %d having price %q cannot be partially filled at price %q: price mismatch", - f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceFilled()) - } - default: - return fmt.Errorf("order %d has unknown type %s", f.GetOrderID(), f.GetOrderType()) - } - - if !isFullyFilled && !f.PartialFillAllowed() { - return fmt.Errorf("cannot fill %s order %d having assets %q with assets %q: order does not allow partial fill", - f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled()) - } - - return nil -} - // Apply adjusts this order fulfillment using the provided info. func (f *OrderFulfillment) Apply(order *OrderFulfillment, assetsAmt, priceAmt sdkmath.Int) error { assets := sdk.NewCoin(order.GetAssets().Denom, assetsAmt) price := sdk.NewCoin(order.GetPrice().Denom, priceAmt) - newAssetsLeftAmt := f.AssetsLeftAmt.Sub(assetsAmt) - if newAssetsLeftAmt.IsNegative() { + newAssetsUnfilledAmt := f.AssetsUnfilledAmt.Sub(assetsAmt) + if newAssetsUnfilledAmt.IsNegative() { return fmt.Errorf("cannot fill %s order %d having assets left %q with %q from %s order %d: overfill", - f.GetOrderType(), f.GetOrderID(), f.GetAssetsLeft(), assets, order.GetOrderType(), order.GetOrderID()) + f.GetOrderType(), f.GetOrderID(), f.GetAssetsUnfilled(), assets, order.GetOrderType(), order.GetOrderID()) } newPriceLeftAmt := f.PriceLeftAmt.Sub(priceAmt) @@ -253,10 +188,10 @@ func (f *OrderFulfillment) Apply(order *OrderFulfillment, assetsAmt, priceAmt sd f.GetOrderType(), f.GetOrderID(), f.GetPriceLeft(), order.GetOrderType(), order.GetOrderID(), price) } - f.AssetsLeftAmt = newAssetsLeftAmt + f.AssetsUnfilledAmt = newAssetsUnfilledAmt f.AssetsFilledAmt = f.AssetsFilledAmt.Add(assetsAmt) f.PriceLeftAmt = newPriceLeftAmt - f.PriceFilledAmt = f.PriceFilledAmt.Add(priceAmt) + f.PriceAppliedAmt = f.PriceAppliedAmt.Add(priceAmt) f.Splits = append(f.Splits, &OrderSplit{ Order: order, Assets: assets, @@ -265,33 +200,62 @@ func (f *OrderFulfillment) Apply(order *OrderFulfillment, assetsAmt, priceAmt sd return nil } +func (f *OrderFulfillment) ApplyLeftoverPrice(askSplit *OrderSplit, amt sdkmath.Int) { + // Update this fulfillment to indicate that the amount has been applied. + f.PriceLeftAmt = f.PriceLeftAmt.Sub(amt) + f.PriceAppliedAmt = f.PriceAppliedAmt.Add(amt) + + // Update the ask split to include the extra amount. + askSplit.Price.Amount = askSplit.Price.Amount.Add(amt) + // And update the ask split's fulfillment similarly. + askSplit.Order.PriceLeftAmt = askSplit.Order.PriceLeftAmt.Sub(amt) + askSplit.Order.PriceAppliedAmt = askSplit.Order.PriceAppliedAmt.Add(amt) + + // Update the bid split entry for this order in the splits that the ask split has + // to indicate the extra amount from this bid. + orderID := f.GetOrderID() + for _, bidSplit := range askSplit.Order.Splits { + if bidSplit.Order.GetOrderID() == orderID { + bidSplit.Price.Amount = bidSplit.Price.Amount.Add(amt) + break + } + } +} + // Finalize does some final calculations and validation for this order fulfillment. // This order fulfillment and the ones in it maybe updated during this. -func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) error { - if len(f.Splits) == 0 { - return fmt.Errorf("no splits applied to %s order %d", f.GetOrderType(), f.GetOrderID()) - } - if f.AssetsFilledAmt.IsZero() { - return fmt.Errorf("cannot fill %s order %d with zero assets", f.GetOrderType(), f.GetOrderID()) - } - if f.PriceFilledAmt.IsZero() { - return fmt.Errorf("cannot fill %s order %d with zero price", f.GetOrderType(), f.GetOrderID()) - } +func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) (err error) { + // If this is returning an error, unset all the fields that get set in here. + defer func() { + if err != nil { + f.IsFinalized = false + f.PriceFilledAmt = sdkmath.ZeroInt() + f.PriceUnfilledAmt = sdkmath.ZeroInt() + f.FeesToPay = nil + f.OrderFeesLeft = nil + } + }() - if f.AssetsLeftAmt.IsNegative() { - return fmt.Errorf("%s order %d having assets %q cannot fill be filled with %q: overfill", - f.GetOrderType(), f.GetOrderID(), f.GetAssets(), f.GetAssetsFilled()) + // AssetsFilledAmt cannot be zero here because we'll be dividing by it. + // AssetsFilledAmt cannot be negative here because we can't have negative values from the calcs. + // Checking for assets filled > zero here (instead of in Validate) because we need to divide by it in here. + if !f.AssetsFilledAmt.IsPositive() { + return fmt.Errorf("no assets filled in %s order %d", f.GetOrderType(), f.GetOrderID()) } isAskOrder, isBidOrder := f.IsAskOrder(), f.IsBidOrder() + isFullyFilled := f.IsFullyFilled() + orderFees := f.GetSettlementFees() orderAssets := f.GetAssets() orderPrice := f.GetPrice() - orderFees := f.GetSettlementFees() - targetPriceAmt := orderPrice.Amount - isFullyFilled := f.IsFullyFilled() + f.PriceFilledAmt = orderPrice.Amount + f.PriceUnfilledAmt = sdkmath.ZeroInt() + f.FeesToPay = orderFees + f.OrderFeesLeft = nil + if !isFullyFilled { - // Make sure the price can be split on a whole number. + // Make sure the price can be split on a whole number, and figure out the price being filled. priceAssets := orderPrice.Amount.Mul(f.AssetsFilledAmt) priceRem := priceAssets.Mod(orderAssets.Amount) if !priceRem.IsZero() { @@ -299,9 +263,11 @@ func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) error { "price %q is not evenly divisible", f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled(), orderPrice) } - targetPriceAmt = priceAssets.Quo(orderAssets.Amount) + f.PriceFilledAmt = priceAssets.Quo(orderAssets.Amount) + f.PriceUnfilledAmt = orderPrice.Amount.Sub(f.PriceFilledAmt) - // Make sure the fees can be split on a whole number. + // Make sure the fees can be split on a whole number, and figure out how much is actually being paid of them. + f.FeesToPay = nil for _, orderFee := range orderFees { feeAssets := orderFee.Amount.Mul(f.AssetsFilledAmt) feeRem := feeAssets.Mul(orderAssets.Amount) @@ -313,51 +279,42 @@ func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) error { feeAmtToPay := feeAssets.Quo(orderAssets.Amount) f.FeesToPay = f.FeesToPay.Add(sdk.NewCoin(orderFee.Denom, feeAmtToPay)) } - feesLeft, hasNeg := orderFees.SafeSub(f.FeesToPay...) - if hasNeg { - return fmt.Errorf("%s order %d having fees %q has negative fees left %q after applying %q", - f.GetOrderType(), f.GetOrderID(), orderFees, feesLeft, f.FeesToPay) - } - f.FeesLeft = feesLeft - } else { - f.FeesToPay = orderFees - f.FeesLeft = nil + f.OrderFeesLeft = orderFees.Sub(f.FeesToPay...) } switch { case isAskOrder: - if !isFullyFilled { - // For partially filled ask orders, we need to maintain the same price/asset ratio. Since ask orders - // can receive more payment than requested, the PriceLeftAmt might be too low, so correct it now. - f.PriceLeftAmt = orderPrice.Amount.Sub(targetPriceAmt) - } // For ask orders, we need to calculate and add the ratio fee to the fees to pay. + // This should NOT affect the order fees left. if sellerFeeRatio != nil { - feeToPay, err := sellerFeeRatio.ApplyToLoosely(f.GetPriceFilled()) - if err != nil { + ratioFeeToPay, ferr := sellerFeeRatio.ApplyToLoosely(f.GetPriceApplied()) + if ferr != nil { return fmt.Errorf("could not calculate %s order %d ratio fee: %w", - f.GetOrderType(), f.GetOrderID(), err) + f.GetOrderType(), f.GetOrderID(), ferr) } - f.FeesToPay = f.FeesToPay.Add(feeToPay) + f.FeesToPay = f.FeesToPay.Add(ratioFeeToPay) } case isBidOrder: - // When adding things to f.PriceFilledAmt, we used truncation on the divisions. - // So, at this point, it might be a little less than the target price. - // If that's the case, we distribute the difference weighted by assets in order of the splits. - // When adding things to f.PriceFilledAmt, we used truncation on the divisions. - // So at this point, it might be a little less than the target price. - // If that's the case, we distribute the difference weighted by assets in order of the splits. - toDistribute := targetPriceAmt.Sub(f.PriceFilledAmt) - if toDistribute.IsNegative() { - return fmt.Errorf("%s order %d having price %q cannot pay %q for %q: overfill", - f.GetOrderType(), f.GetOrderID(), orderPrice, f.PriceFilledAmt, f.GetAssetsFilled()) - } + // When adding things to PriceAppliedAmt (and Splits .Price), we used truncation on the divisions. + // When calculated PriceFilledAmt, we made sure it was a whole number based on total assets being distributed. + // So, at this point, PriceAppliedAmt might be a little less than the PriceFilledAmt. + // If that's the case, we'll distribute the difference among the splits. + toDistribute := f.PriceFilledAmt.Sub(f.PriceAppliedAmt) if toDistribute.IsPositive() { distLeft := toDistribute + // First, go through each split, and apply the leftovers to any asks that still have price left. + for _, askSplit := range f.Splits { + if askSplit.Order.PriceLeftAmt.IsPositive() { + toDist := MinSDKInt(askSplit.Order.PriceLeftAmt, distLeft) + f.ApplyLeftoverPrice(askSplit, toDist) + distLeft = distLeft.Sub(toDist) + } + } + + // Now try to distribute the leftovers evenly weighted by assets. // First pass, we won't default to 1 (if the calc comes up zero). - // This helps weight larger orders that are at the end of the list. - // But it's possible for all the calcs to come up zero, so after - // the first pass, use a minimum of 1 for each distribution. + // This helps weigh larger orders that are at the end of the list. + // Once they've all had a chance, use a minimum of 1. minOne := false for distLeft.IsPositive() { for _, askSplit := range f.Splits { @@ -368,20 +325,10 @@ func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) error { } distAmt = sdkmath.OneInt() } - if distAmt.GT(distLeft) { - distAmt = distLeft - } - f.PriceFilledAmt = f.PriceFilledAmt.Add(distAmt) - f.PriceLeftAmt = f.PriceLeftAmt.Sub(distAmt) - askSplit.Price.Amount = askSplit.Price.Amount.Add(distAmt) - askSplit.Order.PriceFilledAmt = askSplit.Order.PriceFilledAmt.Add(distAmt) - // Not updating askSplit.Order.PriceLeftAmt here since that's done more directly above. - for _, bidSplit := range askSplit.Order.Splits { - if bidSplit.Order.GetOrderID() == f.GetOrderID() { - bidSplit.Price.Amount = bidSplit.Price.Amount.Add(distAmt) - break - } - } + distAmt = MinSDKInt(distAmt, distLeft) + + f.ApplyLeftoverPrice(askSplit, distAmt) + distLeft = distLeft.Sub(distAmt) if !distLeft.IsPositive() { break @@ -389,15 +336,159 @@ func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) error { } minOne = true } + } + } - // If being partially filled, the PriceFilledAmt must now equal the target price. - if !isFullyFilled && !f.PriceFilledAmt.Equal(targetPriceAmt) { - return fmt.Errorf("%s order %d having assets %q and price %q cannot be partially filled "+ - "with %q assets at price %q: expected price %q", - f.GetOrderType(), f.GetOrderID(), orderAssets, orderPrice, - f.GetAssetsFilled(), f.GetPriceFilled(), sdk.Coin{Denom: orderPrice.Denom, Amount: targetPriceAmt}) - } + f.IsFinalized = true + return nil +} + +// Validate does some final validation and sanity checking on this order fulfillment. +// It's assumed that Finalize has been called before calling this. +func (f OrderFulfillment) Validate() error { + if !f.IsFinalized { + return fmt.Errorf("fulfillment for %s order %d has not been finalized", f.GetOrderType(), f.GetOrderID()) + } + + if f.PriceAppliedAmt.IsZero() { + return fmt.Errorf("no price applied to %s order %d", f.GetOrderType(), f.GetOrderID()) + } + if len(f.Splits) == 0 { + return fmt.Errorf("no splits applied to %s order %d", f.GetOrderType(), f.GetOrderID()) + } + + orderAssets := f.GetAssets() + trackedAssetsAmt := f.AssetsFilledAmt.Add(f.AssetsUnfilledAmt) + if !orderAssets.Amount.Equal(trackedAssetsAmt) { + return fmt.Errorf("tracked assets %q does not equal %s order %d assets %q", + sdk.Coin{Denom: orderAssets.Denom, Amount: trackedAssetsAmt}, f.GetOrderType(), f.GetOrderID(), orderAssets) + } + if f.AssetsUnfilledAmt.IsNegative() { + return fmt.Errorf("%s order %d having assets %q has negative assets left %q after filling %q", + f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsUnfilled(), f.GetAssetsFilled()) + } + if !f.AssetsFilledAmt.IsPositive() { + return fmt.Errorf("cannot fill non-positive assets %q on %s order %d having assets %q", + f.GetAssetsFilled(), f.GetOrderType(), f.GetOrderID(), orderAssets) + } + + orderPrice := f.GetPrice() + trackedPriceAmt := f.PriceAppliedAmt.Add(f.PriceLeftAmt) + if !orderPrice.Amount.Equal(trackedPriceAmt) { + return fmt.Errorf("tracked price %q does not equal %s order %d price %q", + sdk.Coin{Denom: orderPrice.Denom, Amount: trackedPriceAmt}, f.GetOrderType(), f.GetOrderID(), orderPrice) + } + if !f.PriceAppliedAmt.IsPositive() { + return fmt.Errorf("cannot apply non-positive price %q to %s order %d having price %q", + f.GetPriceApplied(), f.GetOrderType(), f.GetOrderID(), orderPrice) + } + totalPriceAmt := f.PriceFilledAmt.Add(f.PriceUnfilledAmt) + if !orderPrice.Amount.Equal(totalPriceAmt) { + return fmt.Errorf("filled price %q plus unfilled price %q does not equal order price %q for %s order %d", + f.GetPriceFilled(), f.GetPriceUnfilled(), orderPrice, f.GetOrderType(), f.GetOrderID()) + } + if f.PriceUnfilledAmt.IsNegative() { + return fmt.Errorf("%s order %d having price %q has negative price %q after filling %q", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceUnfilled(), f.GetPriceFilled()) + } + if !f.PriceFilledAmt.IsPositive() { + return fmt.Errorf("cannot fill %s order %d having price %q with non-positive price %q", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceFilled()) + } + + var splitsAssets, splitsPrice sdk.Coins + for _, split := range f.Splits { + splitsAssets = splitsAssets.Add(split.Assets) + splitsPrice = splitsPrice.Add(split.Price) + } + + if len(splitsAssets) != 1 { + return fmt.Errorf("multiple asset denoms %q in splits applied to %s order %d", + splitsAssets, f.GetOrderType(), f.GetOrderID()) + } + if splitsAssets[0].Denom != orderAssets.Denom { + return fmt.Errorf("splits asset denom %q does not equal order assets %q on %s order %d", + splitsAssets, orderAssets, f.GetOrderType(), f.GetOrderID()) + } + if !splitsAssets[0].Amount.Equal(f.AssetsFilledAmt) { + return fmt.Errorf("splits asset total %q does not equal filled assets %q on %s order %d", + splitsAssets, orderAssets, f.GetOrderType(), f.GetOrderID()) + } + + if len(splitsPrice) != 1 { + return fmt.Errorf("multiple price denoms %q in splits applied to %s order %d", + splitsPrice, f.GetOrderType(), f.GetOrderID()) + } + if splitsPrice[0].Denom != orderPrice.Denom { + return fmt.Errorf("splits price denom %q does not equal order price %q on %s order %d", + splitsPrice, orderPrice, f.GetOrderType(), f.GetOrderID()) + } + if !splitsPrice[0].Amount.Equal(f.PriceFilledAmt) { + return fmt.Errorf("splits price total %q does not equal applied price %q on %s order %d", + splitsPrice, f.GetPriceApplied(), f.GetOrderType(), f.GetOrderID()) + } + + isFullyFilled := f.IsFullyFilled() + if isFullyFilled { + if !f.AssetsUnfilledAmt.IsZero() { + return fmt.Errorf("fully filled %s order %q has non-zero unfilled assets %q", + f.GetOrderType(), f.GetOrderID(), f.GetAssetsUnfilled()) + } + if !f.PriceUnfilledAmt.IsZero() { + return fmt.Errorf("fully filled %s order %q has non-zero unfilled price %q", + f.GetOrderType(), f.GetOrderID(), f.GetPriceUnfilled()) + } + if !f.OrderFeesLeft.IsZero() { + return fmt.Errorf("fully filled %s order %q has non-zero settlement fees left %q", + f.GetOrderType(), f.GetOrderID(), f.OrderFeesLeft) + } + } + + orderFees := f.GetSettlementFees() + if _, hasNeg := orderFees.SafeSub(f.OrderFeesLeft...); hasNeg { + return fmt.Errorf("settlement fees left %q is greater than %s order %q settlement fees %q", + f.OrderFeesLeft, f.GetOrderType(), f.GetOrderID(), orderFees) + } + + switch { + case f.IsAskOrder(): + // For ask orders, the applied amount needs to be at least the filled amount. + if f.PriceFilledAmt.LT(f.PriceAppliedAmt) { + return fmt.Errorf("%s order %d having assets %q and price %q cannot be filled by %q at price %q: unsufficient price", + f.GetOrderType(), f.GetOrderID(), orderAssets, orderPrice, f.GetAssetsFilled(), f.GetPriceApplied()) + } + // If not being fully filled on an order that has some fees, make sure that there's at most 1 denom in the fees left. + if !isFullyFilled && len(orderFees) > 0 && len(f.OrderFeesLeft) > 1 { + return fmt.Errorf("partial fulfillment for %s order %d having seller settlement fees %q has multiple denoms in fees left %q", + f.GetOrderType(), f.GetOrderID(), orderFees, f.OrderFeesLeft) + } + case f.IsBidOrder(): + // If filled in full, the PriceAppliedAmt must be equal to the order price. + if isFullyFilled && !f.PriceAppliedAmt.Equal(orderPrice.Amount) { + return fmt.Errorf("%s order %d having price %q cannot be fully filled at price %q: price mismatch", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceApplied()) + } + // otherwise, the price filled must be less than the order price. + if !isFullyFilled && orderPrice.Amount.LT(f.PriceAppliedAmt) { + return fmt.Errorf("%s order %d having price %q cannot be partially filled at price %q: price mismatch", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceApplied()) + } + + // For bid orders, fees to pay + fees left should equal the order fees. + trackedFees := f.FeesToPay.Add(f.OrderFeesLeft...) + if !CoinsEquals(trackedFees, orderFees) { + return fmt.Errorf("tracked settlement fees %q does not equal %s order %d settlement fees %q", + trackedFees, f.GetOrderType(), f.GetOrderID(), orderFees) } + default: + return fmt.Errorf("order %d has unknown type %s", f.GetOrderID(), f.GetOrderType()) + } + + // Saving this simple check for last in the hopes that a previous error exposes why this + // order might accidentally be only partially filled. + if !isFullyFilled && !f.PartialFillAllowed() { + return fmt.Errorf("cannot fill %s order %d having assets %q with assets %q: order does not allow partial fill", + f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled()) } return nil @@ -449,12 +540,12 @@ func Fulfill(of1, of2 *OrderFulfillment) error { // GetFulfillmentAssetsAmt figures out the assets that can be fulfilled with the two provided orders. func GetFulfillmentAssetsAmt(of1, of2 *OrderFulfillment) (sdkmath.Int, error) { - of1AmtLeft, of2AmtLeft := of1.AssetsLeftAmt, of2.AssetsLeftAmt + of1AmtLeft, of2AmtLeft := of1.AssetsUnfilledAmt, of2.AssetsUnfilledAmt if !of1AmtLeft.IsPositive() || !of2AmtLeft.IsPositive() { return sdkmath.ZeroInt(), fmt.Errorf("cannot fill %s order %d having assets left %q "+ "with %s order %d having assets left %q: zero or negative assets left", - of1.GetOrderType(), of1.GetOrderID(), of1.GetAssetsLeft(), - of2.GetOrderType(), of2.GetOrderID(), of2.GetAssetsLeft()) + of1.GetOrderType(), of1.GetOrderID(), of1.GetAssetsUnfilled(), + of2.GetOrderType(), of2.GetOrderID(), of2.GetAssetsUnfilled()) } // Return the lesser of the two. @@ -485,47 +576,45 @@ type PartialFulfillment struct { } func NewPartialFulfillment(f *OrderFulfillment) *PartialFulfillment { - order := NewOrder(f.GetOrderID()) + rv := &PartialFulfillment{ + NewOrder: NewOrder(f.GetOrderID()), + AssetsFilled: f.GetAssetsFilled(), + PriceFilled: f.GetPriceFilled(), + } if f.IsAskOrder() { askOrder := &AskOrder{ MarketId: f.GetMarketID(), Seller: f.GetOwner(), - Assets: f.GetAssetsLeft(), - Price: f.GetPriceLeft(), + Assets: f.GetAssetsUnfilled(), + Price: f.GetPriceUnfilled(), AllowPartial: f.PartialFillAllowed(), } - if !f.FeesLeft.IsZero() { - if len(f.FeesLeft) > 1 { + if !f.OrderFeesLeft.IsZero() { + if len(f.OrderFeesLeft) > 1 { panic(fmt.Errorf("partially filled ask order %d somehow has multiple denoms in fees left %q", - order.OrderId, f.FeesLeft)) + f.GetOrderID(), f.OrderFeesLeft)) } - askOrder.SellerSettlementFlatFee = &f.FeesLeft[0] - } - return &PartialFulfillment{ - NewOrder: order, - AssetsFilled: f.GetAssets().Sub(f.GetAssetsLeft()), - PriceFilled: f.GetPrice().Sub(f.GetPriceLeft()), + askOrder.SellerSettlementFlatFee = &f.OrderFeesLeft[0] } + rv.NewOrder.WithAsk(askOrder) + return rv } if f.IsBidOrder() { bidOrder := &BidOrder{ MarketId: f.GetMarketID(), Buyer: f.GetOwner(), - Assets: f.GetAssetsLeft(), - Price: f.GetPriceLeft(), - BuyerSettlementFees: f.FeesLeft, + Assets: f.GetAssetsUnfilled(), + Price: f.GetPriceUnfilled(), + BuyerSettlementFees: f.OrderFeesLeft, AllowPartial: f.PartialFillAllowed(), } - return &PartialFulfillment{ - NewOrder: order.WithBid(bidOrder), - AssetsFilled: f.GetAssets().Sub(f.GetAssetsLeft()), - PriceFilled: f.GetPrice().Sub(f.GetPriceLeft()), - } + rv.NewOrder.WithBid(bidOrder) + return rv } - panic(fmt.Errorf("order %d has unknown type %q", order.OrderId, f.GetOrderType())) + panic(fmt.Errorf("order %d has unknown type %q", f.GetOrderID(), f.GetOrderType())) } // BuildFulfillments creates all of the ask and bid order fulfillments. @@ -559,6 +648,7 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) } } + // Finalize all the fulfillments. // Need to finalize bid orders first due to possible extra price distribution. for _, bidOF := range bidOFs { if err := bidOF.Finalize(sellerFeeRatio); err != nil { @@ -571,25 +661,54 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) } } + // And make sure they're all valid. + for _, askOF := range askOFs { + if err := askOF.Validate(); err != nil { + return nil, err + } + } for _, bidOF := range bidOFs { if err := bidOF.Validate(); err != nil { return nil, err } } - for _, askOF := range askOFs { - if err := askOF.Validate(); err != nil { - return nil, err + + // Make sure none of them are partially filled except possibly the last in each list. + var partialFulfillment []*OrderFulfillment + lastAskI, lastBidI := len(askOFs)-1, len(bidOFs)-1 + for i, askOF := range askOFs { + if !askOF.IsFullyFilled() { + if i != lastAskI { + return nil, fmt.Errorf("ask order %d (at index %d) is not filled in full and is not the last ask order provided", + askOF.GetOrderID(), i) + } + partialFulfillment = append(partialFulfillment, askOF) + } + } + for i, bidOF := range bidOFs { + if !bidOF.IsFullyFilled() { + if i != lastBidI { + return nil, fmt.Errorf("bid order %d (at index %d) is not filled in full and is not the last bid order provided", + bidOF.GetOrderID(), i) + } + partialFulfillment = append(partialFulfillment, bidOF) } } + // And make sure that only one order is being partially filled. + if len(partialFulfillment) > 1 { + return nil, fmt.Errorf("%s order %d and %s order %d cannot both be partially filled", + partialFulfillment[0].GetOrderType(), partialFulfillment[0].GetOrderID(), + partialFulfillment[1].GetOrderType(), partialFulfillment[1].GetOrderID()) + } + rv := &Fulfillments{ AskOFs: askOFs, BidOFs: bidOFs, } - if !askOFs[len(askOFs)-1].IsFullyFilled() { - rv.PartialOrder = NewPartialFulfillment(askOFs[len(askOFs)-1]) - } else if !bidOFs[len(bidOFs)-1].IsFullyFilled() { - rv.PartialOrder = NewPartialFulfillment(bidOFs[len(bidOFs)-1]) + + if len(partialFulfillment) > 0 { + rv.PartialOrder = NewPartialFulfillment(partialFulfillment[0]) } return rv, nil @@ -658,6 +777,8 @@ type SettlementTransfers struct { } // BuildSettlementTransfers creates all the order transfers needed for the provided fulfillments. +// Assumes that all fulfillments have passed Validate. +// Panics if any amounts are negative. func BuildSettlementTransfers(fulfillments *Fulfillments) *SettlementTransfers { indexedFees := newIndexedAddrAmts() @@ -684,6 +805,8 @@ func BuildSettlementTransfers(fulfillments *Fulfillments) *SettlementTransfers { } // GetAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. +// Assumes that the fulfillment has passed Validate already. +// Panics if any amounts are negative or if it's neither a bid nor ask order. func GetAssetTransfer(f *OrderFulfillment) *Transfer { indexedSplits := newIndexedAddrAmts() for _, split := range f.Splits { @@ -709,6 +832,8 @@ func GetAssetTransfer(f *OrderFulfillment) *Transfer { } // GetPriceTransfer gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. +// Assumes that the fulfillment has passed Validate already. +// Panics if any amounts are negative or if it's neither a bid nor ask order. func GetPriceTransfer(f *OrderFulfillment) *Transfer { indexedSplits := newIndexedAddrAmts() for _, split := range f.Splits { @@ -719,12 +844,12 @@ func GetPriceTransfer(f *OrderFulfillment) *Transfer { if f.IsAskOrder() { return &Transfer{ Inputs: indexedSplits.getAsInputs(), - Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceFilled())}}, + Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceApplied())}}, } } if f.IsBidOrder() { return &Transfer{ - Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceFilled())}}, + Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceApplied())}}, Outputs: indexedSplits.getAsOutputs(), } } diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 16eb630e3e..471f76d7c6 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -4,12 +4,16 @@ package exchange // TODO[1658]: func TestOrderFulfillment_GetAssetsFilled(t *testing.T) -// TODO[1658]: func TestOrderFulfillment_GetAssetsLeft(t *testing.T) +// TODO[1658]: func TestOrderFulfillment_GetAssetsUnfilled(t *testing.T) -// TODO[1658]: func TestOrderFulfillment_GetPriceFilled(t *testing.T) +// TODO[1658]: func TestOrderFulfillment_GetPriceApplied(t *testing.T) // TODO[1658]: func TestOrderFulfillment_GetPriceLeft(t *testing.T) +// TODO[1658]: func TestOrderFulfillment_GetPriceFilled(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_GetPriceUnfilled(t *testing.T) + // TODO[1658]: func TestOrderFulfillment_IsFullyFilled(t *testing.T) // TODO[1658]: func TestOrderFulfillment_IsCompletelyUnfulfilled(t *testing.T) @@ -38,12 +42,14 @@ package exchange // TODO[1658]: func TestOrderFulfillment_GetHoldAmount(t *testing.T) -// TODO[1658]: func TestOrderFulfillment_Validate(t *testing.T) - // TODO[1658]: func TestOrderFulfillment_Apply(t *testing.T) +// TODO[1658]: func TestOrderFulfillment_ApplyLeftoverPrice(t *testing.T) + // TODO[1658]: func TestOrderFulfillment_Finalize(t *testing.T) +// TODO[1658]: func TestOrderFulfillment_Validate(t *testing.T) + // TODO[1658]: func TestFulfill(t *testing.T) // TODO[1658]: func TestGetFulfillmentAssetsAmt(t *testing.T) diff --git a/x/exchange/helpers.go b/x/exchange/helpers.go index 2df4ca72a8..d01a08e0c9 100644 --- a/x/exchange/helpers.go +++ b/x/exchange/helpers.go @@ -1,6 +1,9 @@ package exchange -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) // contains returns true if the provided toFind is present in the provided vals. func contains[T any](vals []T, toFind T, equals func(T, T) bool) bool { @@ -71,3 +74,10 @@ func CoinEquals(a, b sdk.Coin) bool { func IntersectionOfCoin(list1, list2 []sdk.Coin) []sdk.Coin { return intersection(list1, list2, CoinEquals) } + +func MinSDKInt(a, b sdkmath.Int) sdkmath.Int { + if a.LTE(b) { + return a + } + return b +} diff --git a/x/exchange/helpers_test.go b/x/exchange/helpers_test.go index e88400d562..77d0324271 100644 --- a/x/exchange/helpers_test.go +++ b/x/exchange/helpers_test.go @@ -329,3 +329,92 @@ func TestIntersectionOfCoin(t *testing.T) { }) } } + +func TestMinSDKInt(t *testing.T) { + newInt := func(val string) sdkmath.Int { + rv, ok := sdkmath.NewIntFromString(val) + require.True(t, ok, "sdkmath.NewIntFromString(%s) resulting bool", val) + return rv + } + + posBig := newInt("123456789012345678901234567890") + negBig := posBig.Neg() + posBigger := posBig.Add(sdkmath.OneInt()) + + tests := []struct { + name string + a sdkmath.Int + b sdkmath.Int + exp sdkmath.Int + }{ + {name: "-big -big", a: negBig, b: negBig, exp: negBig}, + {name: "-big -2 ", a: negBig, b: sdkmath.NewInt(-2), exp: negBig}, + {name: "-big -1 ", a: negBig, b: sdkmath.NewInt(-1), exp: negBig}, + {name: "-big 0 ", a: negBig, b: sdkmath.NewInt(0), exp: negBig}, + {name: "-big 1 ", a: negBig, b: sdkmath.NewInt(1), exp: negBig}, + {name: "-big 5 ", a: negBig, b: sdkmath.NewInt(5), exp: negBig}, + {name: "-big big ", a: negBig, b: posBig, exp: negBig}, + + {name: "-2 -big", a: sdkmath.NewInt(-2), b: negBig, exp: negBig}, + {name: "-2 -2 ", a: sdkmath.NewInt(-2), b: sdkmath.NewInt(-2), exp: sdkmath.NewInt(-2)}, + {name: "-2 -1 ", a: sdkmath.NewInt(-2), b: sdkmath.NewInt(-1), exp: sdkmath.NewInt(-2)}, + {name: "-2 0 ", a: sdkmath.NewInt(-2), b: sdkmath.NewInt(0), exp: sdkmath.NewInt(-2)}, + {name: "-2 1 ", a: sdkmath.NewInt(-2), b: sdkmath.NewInt(1), exp: sdkmath.NewInt(-2)}, + {name: "-2 5 ", a: sdkmath.NewInt(-2), b: sdkmath.NewInt(5), exp: sdkmath.NewInt(-2)}, + {name: "-2 big ", a: sdkmath.NewInt(-2), b: posBig, exp: sdkmath.NewInt(-2)}, + + {name: "-1 -big", a: sdkmath.NewInt(-1), b: negBig, exp: negBig}, + {name: "-1 -2 ", a: sdkmath.NewInt(-1), b: sdkmath.NewInt(-2), exp: sdkmath.NewInt(-2)}, + {name: "-1 -1 ", a: sdkmath.NewInt(-1), b: sdkmath.NewInt(-1), exp: sdkmath.NewInt(-1)}, + {name: "-1 0 ", a: sdkmath.NewInt(-1), b: sdkmath.NewInt(0), exp: sdkmath.NewInt(-1)}, + {name: "-1 1 ", a: sdkmath.NewInt(-1), b: sdkmath.NewInt(1), exp: sdkmath.NewInt(-1)}, + {name: "-1 5 ", a: sdkmath.NewInt(-1), b: sdkmath.NewInt(5), exp: sdkmath.NewInt(-1)}, + {name: "-1 big ", a: sdkmath.NewInt(-1), b: posBig, exp: sdkmath.NewInt(-1)}, + + {name: "0 -big", a: sdkmath.NewInt(0), b: negBig, exp: negBig}, + {name: "0 -2 ", a: sdkmath.NewInt(0), b: sdkmath.NewInt(-2), exp: sdkmath.NewInt(-2)}, + {name: "0 -1 ", a: sdkmath.NewInt(0), b: sdkmath.NewInt(-1), exp: sdkmath.NewInt(-1)}, + {name: "0 0 ", a: sdkmath.NewInt(0), b: sdkmath.NewInt(0), exp: sdkmath.NewInt(0)}, + {name: "0 1 ", a: sdkmath.NewInt(0), b: sdkmath.NewInt(1), exp: sdkmath.NewInt(0)}, + {name: "0 5 ", a: sdkmath.NewInt(0), b: sdkmath.NewInt(5), exp: sdkmath.NewInt(0)}, + {name: "0 big ", a: sdkmath.NewInt(0), b: posBig, exp: sdkmath.NewInt(0)}, + + {name: "1 -big", a: sdkmath.NewInt(1), b: negBig, exp: negBig}, + {name: "1 -2 ", a: sdkmath.NewInt(1), b: sdkmath.NewInt(-2), exp: sdkmath.NewInt(-2)}, + {name: "1 -1 ", a: sdkmath.NewInt(1), b: sdkmath.NewInt(-1), exp: sdkmath.NewInt(-1)}, + {name: "1 0 ", a: sdkmath.NewInt(1), b: sdkmath.NewInt(0), exp: sdkmath.NewInt(0)}, + {name: "1 1 ", a: sdkmath.NewInt(1), b: sdkmath.NewInt(1), exp: sdkmath.NewInt(1)}, + {name: "1 5 ", a: sdkmath.NewInt(1), b: sdkmath.NewInt(5), exp: sdkmath.NewInt(1)}, + {name: "1 big ", a: sdkmath.NewInt(1), b: posBig, exp: sdkmath.NewInt(1)}, + + {name: "5 -big", a: sdkmath.NewInt(5), b: negBig, exp: negBig}, + {name: "5 -2 ", a: sdkmath.NewInt(5), b: sdkmath.NewInt(-2), exp: sdkmath.NewInt(-2)}, + {name: "5 -1 ", a: sdkmath.NewInt(5), b: sdkmath.NewInt(-1), exp: sdkmath.NewInt(-1)}, + {name: "5 0 ", a: sdkmath.NewInt(5), b: sdkmath.NewInt(0), exp: sdkmath.NewInt(0)}, + {name: "5 1 ", a: sdkmath.NewInt(5), b: sdkmath.NewInt(1), exp: sdkmath.NewInt(1)}, + {name: "5 5 ", a: sdkmath.NewInt(5), b: sdkmath.NewInt(5), exp: sdkmath.NewInt(5)}, + {name: "5 big ", a: sdkmath.NewInt(5), b: posBig, exp: sdkmath.NewInt(5)}, + + {name: "big -big", a: posBig, b: negBig, exp: negBig}, + {name: "big -2 ", a: posBig, b: sdkmath.NewInt(-2), exp: sdkmath.NewInt(-2)}, + {name: "big -1 ", a: posBig, b: sdkmath.NewInt(-1), exp: sdkmath.NewInt(-1)}, + {name: "big 0 ", a: posBig, b: sdkmath.NewInt(0), exp: sdkmath.NewInt(0)}, + {name: "big 1 ", a: posBig, b: sdkmath.NewInt(1), exp: sdkmath.NewInt(1)}, + {name: "big 5 ", a: posBig, b: sdkmath.NewInt(5), exp: sdkmath.NewInt(5)}, + {name: "big big ", a: posBig, b: posBig, exp: posBig}, + + {name: "big bigger", a: posBig, b: posBigger, exp: posBig}, + {name: "bigger big", a: posBigger, b: posBig, exp: posBig}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdkmath.Int + testFunc := func() { + actual = MinSDKInt(tc.a, tc.b) + } + require.NotPanics(t, testFunc, "MinSDKInt(%s, %s)", tc.a, tc.b) + assert.Equal(t, tc.exp, actual, "MinSDKInt(%s, %s)", tc.a, tc.b) + }) + } +} diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index 768e6cd14f..e9e0e159ee 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -349,6 +349,13 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO return err } + if !expectPartial && fulfillments.PartialOrder != nil { + return fmt.Errorf("settlement resulted in unexpected partial order %d", fulfillments.PartialOrder.NewOrder.GetOrderID()) + } + if expectPartial && fulfillments.PartialOrder == nil { + return errors.New("settlement unexpectedly resulted in all orders fully filled") + } + transfers := exchange.BuildSettlementTransfers(fulfillments) for _, transfer := range transfers.OrderTransfers { From 088d28b4608aa398401bc691ad6d93cc7c39dfc6 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 25 Sep 2023 18:06:24 -0600 Subject: [PATCH 175/309] [1658]: Unit tests on the rest of the helper funcs. --- x/exchange/helpers_test.go | 144 ++++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 3 deletions(-) diff --git a/x/exchange/helpers_test.go b/x/exchange/helpers_test.go index 77d0324271..df173da739 100644 --- a/x/exchange/helpers_test.go +++ b/x/exchange/helpers_test.go @@ -10,7 +10,36 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// TODO[1658]: func TestEqualsUint64(t *testing.T) +func TestEqualsUint64(t *testing.T) { + tests := []struct { + name string + a uint64 + b uint64 + exp bool + }{ + {name: "0 0", a: 0, b: 0, exp: true}, + {name: "0 1", a: 0, b: 1, exp: false}, + {name: "1 0", a: 1, b: 0, exp: false}, + {name: "1 1", a: 1, b: 1, exp: true}, + {name: "1 max uint32+1", a: 1, b: 4_294_967_296, exp: false}, + {name: "max uint32+1 1", a: 4_294_967_296, b: 1, exp: false}, + {name: "max uint32+1 max uint32+1", a: 4_294_967_296, b: 4_294_967_296, exp: true}, + {name: "1 max uint64", a: 1, b: 18_446_744_073_709_551_615, exp: false}, + {name: "max uint64 1", a: 18_446_744_073_709_551_615, b: 1, exp: false}, + {name: "max uint64 max uint64", a: 18_446_744_073_709_551_615, b: 18_446_744_073_709_551_615, exp: true}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = EqualsUint64(tc.a, tc.b) + } + require.NotPanics(t, testFunc, "EqualsUint64(%d, %d)", tc.a, tc.b) + assert.Equal(t, tc.exp, actual, "EqualsUint64(%d, %d)", tc.a, tc.b) + }) + } +} func TestContainsUint64(t *testing.T) { tests := []struct { @@ -81,7 +110,57 @@ func TestContainsUint64(t *testing.T) { } } -// TODO[1658]: func TestIntersectionUint64(t *testing.T) +func TestIntersectionUint64(t *testing.T) { + tests := []struct { + name string + a []uint64 + b []uint64 + exp []uint64 + }{ + {name: "nil nil", a: nil, b: nil, exp: nil}, + {name: "nil empty", a: nil, b: []uint64{}, exp: nil}, + {name: "empty nil", a: []uint64{}, b: nil, exp: nil}, + {name: "empty empty", a: []uint64{}, b: []uint64{}, exp: nil}, + {name: "nil one", a: nil, b: []uint64{1}, exp: nil}, + {name: "one nil", a: []uint64{1}, b: nil, exp: nil}, + {name: "one one same", a: []uint64{1}, b: []uint64{1}, exp: []uint64{1}}, + {name: "one one different", a: []uint64{1}, b: []uint64{2}, exp: nil}, + {name: "three one first", a: []uint64{1, 2, 3}, b: []uint64{1}, exp: []uint64{1}}, + {name: "three one second", a: []uint64{1, 2, 3}, b: []uint64{2}, exp: []uint64{2}}, + {name: "three one third", a: []uint64{1, 2, 3}, b: []uint64{3}, exp: []uint64{3}}, + {name: "three one none", a: []uint64{1, 2, 3}, b: []uint64{4}, exp: nil}, + + {name: "three two none", a: []uint64{1, 2, 3}, b: []uint64{4, 5}, exp: nil}, + {name: "three two first rep", a: []uint64{1, 2, 3}, b: []uint64{1, 1}, exp: []uint64{1}}, + {name: "three two only first", a: []uint64{1, 2, 3}, b: []uint64{4, 1}, exp: []uint64{1}}, + {name: "three two second rep", a: []uint64{1, 2, 3}, b: []uint64{2, 2}, exp: []uint64{2}}, + {name: "three two only second", a: []uint64{1, 2, 3}, b: []uint64{4, 2}, exp: []uint64{2}}, + {name: "three two third rep", a: []uint64{1, 2, 3}, b: []uint64{3, 3}, exp: []uint64{3}}, + {name: "three two only third", a: []uint64{1, 2, 3}, b: []uint64{4, 3}, exp: []uint64{3}}, + {name: "three two not third", a: []uint64{1, 2, 3}, b: []uint64{2, 1}, exp: []uint64{1, 2}}, + {name: "three two not second", a: []uint64{1, 2, 3}, b: []uint64{3, 1}, exp: []uint64{1, 3}}, + {name: "three two not first", a: []uint64{1, 2, 3}, b: []uint64{3, 2}, exp: []uint64{2, 3}}, + + {name: "three rep one same", a: []uint64{5, 5, 5}, b: []uint64{5}, exp: []uint64{5}}, + {name: "three rep one different", a: []uint64{5, 5, 5}, b: []uint64{6}, exp: nil}, + {name: "three rep two rep same", a: []uint64{5, 5, 5}, b: []uint64{5, 5}, exp: []uint64{5}}, + {name: "three rep two rep different", a: []uint64{5, 5, 5}, b: []uint64{6, 6}, exp: nil}, + {name: "three three one same", a: []uint64{1, 2, 3}, b: []uint64{4, 5, 1}, exp: []uint64{1}}, + {name: "three three two same", a: []uint64{1, 2, 3}, b: []uint64{3, 4, 2}, exp: []uint64{2, 3}}, + {name: "three three all same diff order", a: []uint64{1, 2, 3, 2, 1}, b: []uint64{2, 1, 1, 1, 2, 3, 1}, exp: []uint64{1, 2, 3}}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual []uint64 + testFunc := func() { + actual = IntersectionUint64(tc.a, tc.b) + } + require.NotPanics(t, testFunc, "IntersectionUint64") + assert.Equal(t, tc.exp, actual, "IntersectionUint64 result") + }) + } +} func TestContainsString(t *testing.T) { tests := []struct { @@ -194,7 +273,66 @@ func TestContainsString(t *testing.T) { } } -// TODO[1658]: func TestCoinsEquals(t *testing.T) +func TestCoinsEquals(t *testing.T) { + coins := func(coins string) sdk.Coins { + rv, err := sdk.ParseCoinsNormalized(coins) + require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) + return rv + } + + tests := []struct { + name string + a sdk.Coins + b sdk.Coins + exp bool + }{ + {name: "nil nil", a: nil, b: nil, exp: true}, + {name: "nil empty", a: nil, b: sdk.Coins{}, exp: true}, + {name: "empty nil", a: sdk.Coins{}, b: nil, exp: true}, + {name: "empty empty", a: nil, b: sdk.Coins{}, exp: true}, + {name: "nil one", a: nil, b: coins("1one"), exp: false}, + {name: "one nil", a: coins("1one"), b: nil, exp: false}, + {name: "one one same", a: coins("1one"), b: coins("1one"), exp: true}, + {name: "one one diff amount", a: coins("1one"), b: coins("2one"), exp: false}, + {name: "one one diff denom", a: coins("1one"), b: coins("1two"), exp: false}, + {name: "one one diff both", a: coins("1one"), b: coins("2two"), exp: false}, + {name: "two one first", a: coins("1one,2two"), b: coins("1one"), exp: false}, + {name: "two one second", a: coins("1one,2two"), b: coins("2two"), exp: false}, + {name: "two one neither", a: coins("1one,2two"), b: coins("3three"), exp: false}, + {name: "two two same", a: coins("1one,2two"), b: coins("1one,2two"), exp: true}, + {name: "two two diff first denom", a: coins("1one,2two"), b: coins("1three,2two"), exp: false}, + {name: "two two diff second denom", a: coins("1one,2two"), b: coins("1one,2three"), exp: false}, + { + name: "one one same negative", + a: sdk.Coins{sdk.Coin{Denom: "one", Amount: sdkmath.NewInt(-1)}}, + b: sdk.Coins{sdk.Coin{Denom: "one", Amount: sdkmath.NewInt(-1)}}, + exp: true, + }, + { + name: "one one negative first", + a: sdk.Coins{sdk.Coin{Denom: "one", Amount: sdkmath.NewInt(-1)}}, + b: sdk.Coins{sdk.Coin{Denom: "one", Amount: sdkmath.NewInt(1)}}, + exp: false, + }, + { + name: "one one negative second", + a: sdk.Coins{sdk.Coin{Denom: "one", Amount: sdkmath.NewInt(1)}}, + b: sdk.Coins{sdk.Coin{Denom: "one", Amount: sdkmath.NewInt(-1)}}, + exp: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = CoinsEquals(tc.a, tc.b) + } + require.NotPanics(t, testFunc, "CoinsEquals(%q, %q)", tc.a, tc.b) + assert.Equal(t, tc.exp, actual, "CoinsEquals(%q, %q)", tc.a, tc.b) + }) + } +} func TestCoinEquals(t *testing.T) { tests := []struct { From 91af37aab8b020eb3f5444298af8ef0761500b49 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 25 Sep 2023 19:50:41 -0600 Subject: [PATCH 176/309] [1658]: Unit tests on the indexedAddrAmts stuff. --- x/exchange/fulfillment.go | 20 ++ x/exchange/fulfillment_test.go | 601 ++++++++++++++++++++++++++++++++- x/exchange/orders_test.go | 2 +- 3 files changed, 615 insertions(+), 8 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index caf9cf3cee..4ebc30079a 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -731,7 +731,13 @@ func newIndexedAddrAmts() *indexedAddrAmts { } // add adds the coins to the given address. +// Panics if a provided coin is invalid. func (i *indexedAddrAmts) add(addr string, coins ...sdk.Coin) { + for _, coin := range coins { + if err := coin.Validate(); err != nil { + panic(fmt.Errorf("cannot index and add invalid coin amount %q", coin)) + } + } n, known := i.indexes[addr] if !known { n = len(i.addrs) @@ -743,18 +749,32 @@ func (i *indexedAddrAmts) add(addr string, coins ...sdk.Coin) { } // getAsInputs returns all the entries as bank Inputs. +// Panics if this is nil, has no addrs, or has a negative coin amount. func (i *indexedAddrAmts) getAsInputs() []banktypes.Input { + if i == nil || len(i.addrs) == 0 { + panic(errors.New("cannot get inputs from empty set")) + } rv := make([]banktypes.Input, len(i.addrs)) for n, addr := range i.addrs { + if !i.amts[n].IsAllPositive() { + panic(fmt.Errorf("invalid indexed amount %q for address %q: cannot be zero or negative", addr, i.amts[n])) + } rv[n] = banktypes.Input{Address: addr, Coins: i.amts[n]} } return rv } // getAsOutputs returns all the entries as bank Outputs. +// Panics if this is nil, has no addrs, or has a negative coin amount. func (i *indexedAddrAmts) getAsOutputs() []banktypes.Output { + if i == nil || len(i.addrs) == 0 { + panic(errors.New("cannot get inputs from empty set")) + } rv := make([]banktypes.Output, len(i.addrs)) for n, addr := range i.addrs { + if !i.amts[n].IsAllPositive() { + panic(fmt.Errorf("invalid indexed amount %q for address %q: cannot be zero or negative", addr, i.amts[n])) + } rv[n] = banktypes.Output{Address: addr, Coins: i.amts[n]} } return rv diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 471f76d7c6..395efe2609 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -1,5 +1,21 @@ package exchange +import ( + "fmt" + "sort" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/provenance-io/provenance/testutil/assertions" +) + // TODO[1658]: func TestNewOrderFulfillment(t *testing.T) // TODO[1658]: func TestOrderFulfillment_GetAssetsFilled(t *testing.T) @@ -58,13 +74,584 @@ package exchange // TODO[1658]: func TestBuildFulfillments(t *testing.T) -// TODO[1658]: func TestNewIndexedAddrAmts(t *testing.T) - -// TODO[1658]: func TestIndexedAddrAmts_Add(t *testing.T) - -// TODO[1658]: func TestIndexedAddrAmts_GetAsInputs(t *testing.T) - -// TODO[1658]: func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) +func copyIndexedAddrAmts(orig *indexedAddrAmts) *indexedAddrAmts { + if orig == nil { + return nil + } + + rv := &indexedAddrAmts{ + addrs: nil, + amts: nil, + indexes: nil, + } + + if orig.addrs != nil { + rv.addrs = make([]string, 0, len(orig.addrs)) + rv.addrs = append(rv.addrs, orig.addrs...) + } + + if orig.amts != nil { + rv.amts = make([]sdk.Coins, len(orig.amts)) + for i, amt := range orig.amts { + rv.amts[i] = copyCoins(amt) + } + } + + if orig.indexes != nil { + rv.indexes = make(map[string]int, len(orig.indexes)) + for k, v := range orig.indexes { + rv.indexes[k] = v + } + } + + return rv +} + +// String converts everything in this to a string. +// This is mostly because test failure output of sdk.Coin and sdk.Coins is impossible to understand. +func (i *indexedAddrAmts) String() string { + if i == nil { + return "nil" + } + + addrs := "nil" + if i.addrs != nil { + addrsVals := make([]string, len(i.addrs)) + for j, addr := range i.addrs { + addrsVals[j] = fmt.Sprintf("%q", addr) + } + addrs = fmt.Sprintf("%T{%s}", i.addrs, strings.Join(addrsVals, ", ")) + } + + amts := "nil" + if i.amts != nil { + amtsVals := make([]string, len(i.amts)) + for j, amt := range i.amts { + amtsVals[j] = fmt.Sprintf("%q", amt) + } + amts = fmt.Sprintf("[]%T{%s}", i.amts, strings.Join(amtsVals, ", ")) + } + + indexes := "nil" + if i.indexes != nil { + indexVals := make([]string, 0, len(i.indexes)) + for k, v := range i.indexes { + indexVals = append(indexVals, fmt.Sprintf("%q: %d", k, v)) + } + sort.Strings(indexVals) + indexes = fmt.Sprintf("%T{%s}", i.indexes, strings.Join(indexVals, ", ")) + } + + return fmt.Sprintf("%T{addrs:%s, amts:%s, indexes:%s}", i, addrs, amts, indexes) +} + +func TestNewIndexedAddrAmts(t *testing.T) { + expected := &indexedAddrAmts{ + addrs: nil, + amts: nil, + indexes: make(map[string]int), + } + actual := newIndexedAddrAmts() + assert.Equal(t, expected, actual, "newIndexedAddrAmts result") + key := "test" + require.NotPanics(t, func() { + _ = actual.indexes[key] + }, "getting value of actual.indexes[%q]", key) +} + +func TestIndexedAddrAmts_Add(t *testing.T) { + coins := func(coins string) sdk.Coins { + rv, err := sdk.ParseCoinsNormalized(coins) + require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) + return rv + } + negCoins := sdk.Coins{sdk.Coin{Denom: "neg", Amount: sdkmath.NewInt(-1)}} + + tests := []struct { + name string + receiver *indexedAddrAmts + addr string + coins []sdk.Coin + expected *indexedAddrAmts + expPanic string + }{ + { + name: "empty, add one coin", + receiver: newIndexedAddrAmts(), + addr: "addr1", + coins: coins("1one"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{"addr1": 0}, + }, + }, + { + name: "empty, add two coins", + receiver: newIndexedAddrAmts(), + addr: "addr1", + coins: coins("1one,2two"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one,2two")}, + indexes: map[string]int{"addr1": 0}, + }, + }, + { + name: "empty, add neg coins", + receiver: newIndexedAddrAmts(), + addr: "addr1", + coins: negCoins, + expPanic: "cannot index and add invalid coin amount \"-1neg\"", + }, + { + name: "one addr, add to existing new denom", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{"addr1": 0}, + }, + addr: "addr1", + coins: coins("2two"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one,2two")}, + indexes: map[string]int{"addr1": 0}, + }, + }, + { + name: "one addr, add to existing same denom", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{"addr1": 0}, + }, + addr: "addr1", + coins: coins("3one"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("4one")}, + indexes: map[string]int{"addr1": 0}, + }, + }, + { + name: "one addr, add negative to existing", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{"addr1": 0}, + }, + addr: "addr1", + coins: negCoins, + expPanic: "cannot index and add invalid coin amount \"-1neg\"", + }, + { + name: "one addr, add to new", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{"addr1": 0}, + }, + addr: "addr2", + coins: coins("2two"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2"}, + amts: []sdk.Coins{coins("1one"), coins("2two")}, + indexes: map[string]int{"addr1": 0, "addr2": 1}, + }, + }, + { + name: "one addr, add to new opposite order", + receiver: &indexedAddrAmts{ + addrs: []string{"addr2"}, + amts: []sdk.Coins{coins("2two")}, + indexes: map[string]int{"addr2": 0}, + }, + addr: "addr1", + coins: coins("1one"), + expected: &indexedAddrAmts{ + addrs: []string{"addr2", "addr1"}, + amts: []sdk.Coins{coins("2two"), coins("1one")}, + indexes: map[string]int{"addr2": 0, "addr1": 1}, + }, + }, + { + name: "one addr, add negative to new", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{"addr1": 0}, + }, + addr: "addr2", + coins: negCoins, + expPanic: "cannot index and add invalid coin amount \"-1neg\"", + }, + { + name: "three addrs, add to first", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + addr: "addr1", + coins: coins("10one"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("11one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + }, + { + name: "three addrs, add to second", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + addr: "addr2", + coins: coins("10two"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("12two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + }, + { + name: "three addrs, add to third", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + addr: "addr3", + coins: coins("10three"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("13three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + }, + { + name: "three addrs, add two coins to second", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + addr: "addr2", + coins: coins("10four,20two"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("10four,22two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + }, + { + name: "three addrs, add to new", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + addr: "good buddy", + coins: coins("10four"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3", "good buddy"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three"), coins("10four")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2, "good buddy": 3}, + }, + }, + { + name: "three addrs, add negative to second", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + addr: "addr2", + coins: negCoins, + expPanic: "cannot index and add invalid coin amount \"-1neg\"", + }, + { + name: "three addrs, add negative to new", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + addr: "addr4", + coins: negCoins, + expPanic: "cannot index and add invalid coin amount \"-1neg\"", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + orig := copyIndexedAddrAmts(tc.receiver) + defer func() { + if t.Failed() { + t.Logf("Original: %s", orig) + t.Logf(" Actual: %s", tc.receiver) + t.Logf("Expected: %s", tc.expected) + } + }() + + testFunc := func() { + tc.receiver.add(tc.addr, tc.coins...) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "add(%q, %q)", tc.addr, tc.coins) + if len(tc.expPanic) == 0 { + assert.Equal(t, tc.expected, tc.receiver, "receiver after add(%q, %q)", tc.addr, tc.coins) + } + }) + } +} + +func TestIndexedAddrAmts_GetAsInputs(t *testing.T) { + coins := func(coins string) sdk.Coins { + rv, err := sdk.ParseCoinsNormalized(coins) + require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) + return rv + } + + tests := []struct { + name string + receiver *indexedAddrAmts + expected []banktypes.Input + expPanic string + }{ + {name: "nil receiver", receiver: nil, expPanic: "cannot get inputs from empty set"}, + {name: "no addrs", receiver: newIndexedAddrAmts(), expPanic: "cannot get inputs from empty set"}, + { + name: "one addr negative amount", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{{{Denom: "neg", Amount: sdkmath.NewInt(-1)}}}, + indexes: map[string]int{ + "addr1": 0, + }, + }, + expPanic: "invalid indexed amount \"addr1\" for address \"-1neg\": cannot be zero or negative", + }, + { + name: "one addr zero amount", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{{{Denom: "zero", Amount: sdkmath.NewInt(0)}}}, + indexes: map[string]int{ + "addr1": 0, + }, + }, + expPanic: "invalid indexed amount \"addr1\" for address \"0zero\": cannot be zero or negative", + }, + { + name: "one addr positive amount", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{ + "addr1": 0, + }, + }, + expected: []banktypes.Input{ + {Address: "addr1", Coins: coins("1one")}, + }, + }, + { + name: "two addrs", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2"}, + amts: []sdk.Coins{coins("1one"), coins("2two,3three")}, + indexes: map[string]int{ + "addr1": 0, + "addr2": 1, + }, + }, + expected: []banktypes.Input{ + {Address: "addr1", Coins: coins("1one")}, + {Address: "addr2", Coins: coins("2two,3three")}, + }, + }, + { + name: "three addrs", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two,3three"), coins("4four,5five,6six")}, + indexes: map[string]int{ + "addr1": 0, + "addr2": 1, + "addr3": 2, + }, + }, + expected: []banktypes.Input{ + {Address: "addr1", Coins: coins("1one")}, + {Address: "addr2", Coins: coins("2two,3three")}, + {Address: "addr3", Coins: coins("4four,5five,6six")}, + }, + }, + { + name: "three addrs, negative in third", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{ + coins("1one"), + coins("2two,3three"), + { + {Denom: "acoin", Amount: sdkmath.NewInt(4)}, + {Denom: "bcoin", Amount: sdkmath.NewInt(5)}, + {Denom: "ncoin", Amount: sdkmath.NewInt(-6)}, + {Denom: "zcoin", Amount: sdkmath.NewInt(7)}, + }, + }, + indexes: map[string]int{ + "addr1": 0, + "addr2": 1, + "addr3": 2, + }, + }, + expPanic: "invalid indexed amount \"addr3\" for address \"4acoin,5bcoin,-6ncoin,7zcoin\": cannot be zero or negative", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + orig := copyIndexedAddrAmts(tc.receiver) + var actual []banktypes.Input + testFunc := func() { + actual = tc.receiver.getAsInputs() + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAsInputs()") + assert.Equal(t, tc.expected, actual, "getAsInputs() result") + if !assert.Equal(t, orig, tc.receiver, "receiver before and after getAsInputs()") { + t.Logf("Before: %s", orig) + t.Logf(" After: %s", tc.receiver) + } + }) + } +} + +func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { + coins := func(coins string) sdk.Coins { + rv, err := sdk.ParseCoinsNormalized(coins) + require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) + return rv + } + + tests := []struct { + name string + receiver *indexedAddrAmts + expected []banktypes.Output + expPanic string + }{ + {name: "nil receiver", receiver: nil, expPanic: "cannot get inputs from empty set"}, + {name: "no addrs", receiver: newIndexedAddrAmts(), expPanic: "cannot get inputs from empty set"}, + { + name: "one addr negative amount", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{{{Denom: "neg", Amount: sdkmath.NewInt(-1)}}}, + indexes: map[string]int{ + "addr1": 0, + }, + }, + expPanic: "invalid indexed amount \"addr1\" for address \"-1neg\": cannot be zero or negative", + }, + { + name: "one addr zero amount", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{{{Denom: "zero", Amount: sdkmath.NewInt(0)}}}, + indexes: map[string]int{ + "addr1": 0, + }, + }, + expPanic: "invalid indexed amount \"addr1\" for address \"0zero\": cannot be zero or negative", + }, + { + name: "one addr positive amount", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{ + "addr1": 0, + }, + }, + expected: []banktypes.Output{ + {Address: "addr1", Coins: coins("1one")}, + }, + }, + { + name: "two addrs", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2"}, + amts: []sdk.Coins{coins("1one"), coins("2two,3three")}, + indexes: map[string]int{ + "addr1": 0, + "addr2": 1, + }, + }, + expected: []banktypes.Output{ + {Address: "addr1", Coins: coins("1one")}, + {Address: "addr2", Coins: coins("2two,3three")}, + }, + }, + { + name: "three addrs", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two,3three"), coins("4four,5five,6six")}, + indexes: map[string]int{ + "addr1": 0, + "addr2": 1, + "addr3": 2, + }, + }, + expected: []banktypes.Output{ + {Address: "addr1", Coins: coins("1one")}, + {Address: "addr2", Coins: coins("2two,3three")}, + {Address: "addr3", Coins: coins("4four,5five,6six")}, + }, + }, + { + name: "three addrs, negative in third", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{ + coins("1one"), + coins("2two,3three"), + { + {Denom: "acoin", Amount: sdkmath.NewInt(4)}, + {Denom: "bcoin", Amount: sdkmath.NewInt(5)}, + {Denom: "ncoin", Amount: sdkmath.NewInt(-6)}, + {Denom: "zcoin", Amount: sdkmath.NewInt(7)}, + }, + }, + indexes: map[string]int{ + "addr1": 0, + "addr2": 1, + "addr3": 2, + }, + }, + expPanic: "invalid indexed amount \"addr3\" for address \"4acoin,5bcoin,-6ncoin,7zcoin\": cannot be zero or negative", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + orig := copyIndexedAddrAmts(tc.receiver) + var actual []banktypes.Output + testFunc := func() { + actual = tc.receiver.getAsOutputs() + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAsOutputs()") + assert.Equal(t, tc.expected, actual, "getAsOutputs() result") + if !assert.Equal(t, orig, tc.receiver, "receiver before and after getAsInputs()") { + t.Logf("Before: %s", orig) + t.Logf(" After: %s", tc.receiver) + } + }) + } +} // TODO[1658]: func TestBuildSettlementTransfers(t *testing.T) diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index fa2cfecc76..1a95b073de 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -28,7 +28,7 @@ func copyCoins(coins sdk.Coins) sdk.Coins { // copyCoin returns a copy of the provided coin. func copyCoin(coin sdk.Coin) sdk.Coin { - return sdk.NewInt64Coin(coin.Denom, coin.Amount.Int64()) + return sdk.Coin{Denom: coin.Denom, Amount: coin.Amount.AddRaw(0)} } // copyCoinP returns a copy of the provided *coin. From 47b591b80fe1df944936b5588516771506d57a93 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 26 Sep 2023 00:52:36 -0600 Subject: [PATCH 177/309] [1658]: Unit tests on the OrderI funcs of OrderFulfillment. --- x/exchange/fulfillment.go | 2 +- x/exchange/fulfillment_test.go | 860 +++++++++++++++++++++++++++++++-- x/exchange/orders_test.go | 83 ++++ 3 files changed, 914 insertions(+), 31 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 4ebc30079a..75e3b08079 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -107,7 +107,7 @@ func (f OrderFulfillment) IsFullyFilled() bool { // IsCompletelyUnfulfilled returns true if nothing in this order has been filled. func (f OrderFulfillment) IsCompletelyUnfulfilled() bool { - return len(f.Splits) == 0 || f.AssetsFilledAmt.IsZero() + return f.AssetsFilledAmt.IsZero() } // GetOrderID gets this fulfillment's order's id. diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 395efe2609..71ebaee48a 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -16,47 +16,847 @@ import ( "github.com/provenance-io/provenance/testutil/assertions" ) -// TODO[1658]: func TestNewOrderFulfillment(t *testing.T) +// orderSplitString is similar to %v except with easier to understand Coin and Int entries. +func orderSplitString(s *OrderSplit) string { + if s == nil { + return "nil" + } + + fields := []string{ + // Just using superficial info for the order to prevent infinite loops. + fmt.Sprintf("Order:{OrderID:%d,OrderType:%s,...}", s.Order.GetOrderID(), s.Order.GetOrderType()), + fmt.Sprintf("Assets:%q", s.Assets), + fmt.Sprintf("Price:%q", s.Price), + } + return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) +} + +// orderSplitsString is similar to %v except with easier to understand Coin and Int entries. +func orderSplitsString(splits []*OrderSplit) string { + if splits == nil { + return "nil" + } + vals := make([]string, len(splits)) + for i, s := range splits { + vals[i] = orderSplitString(s) + } + return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) +} + +// orderFulfillmentString is similar to %v except with easier to understand Coin and Int entries. +func orderFulfillmentString(f *OrderFulfillment) string { + if f == nil { + return "nil" + } -// TODO[1658]: func TestOrderFulfillment_GetAssetsFilled(t *testing.T) + fields := []string{ + fmt.Sprintf("Order:%s", orderString(f.Order)), + fmt.Sprintf("Splits:%s", orderSplitsString(f.Splits)), + fmt.Sprintf("AssetsFilledAmt:%q", f.AssetsFilledAmt), + fmt.Sprintf("AssetsUnfilledAmt:%q", f.AssetsUnfilledAmt), + fmt.Sprintf("PriceAppliedAmt:%q", f.PriceAppliedAmt), + fmt.Sprintf("PriceLeftAmt:%q", f.PriceLeftAmt), + fmt.Sprintf("IsFinalized:%t", f.IsFinalized), + fmt.Sprintf("FeesToPay:%s", coinsString(f.FeesToPay)), + fmt.Sprintf("OrderFeesLeft:%s", coinsString(f.OrderFeesLeft)), + fmt.Sprintf("PriceFilledAmt:%q", f.PriceFilledAmt), + fmt.Sprintf("PriceUnfilledAmt:%q", f.PriceUnfilledAmt), + } + return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) +} -// TODO[1658]: func TestOrderFulfillment_GetAssetsUnfilled(t *testing.T) +func TestNewOrderFulfillment(t *testing.T) { + tests := []struct { + name string + order *Order + expected *OrderFulfillment + expPanic string + }{ + { + name: "nil sub-order", + order: NewOrder(1), + expPanic: "unknown sub-order type : does not implement SubOrderI", + }, + { + name: "ask order", + order: NewOrder(2).WithAsk(&AskOrder{ + MarketId: 10, + Assets: sdk.NewInt64Coin("adolla", 92), + Price: sdk.NewInt64Coin("pdolla", 15), + }), + expected: &OrderFulfillment{ + Order: &Order{ + OrderId: 2, + Order: &Order_AskOrder{ + AskOrder: &AskOrder{ + MarketId: 10, + Assets: sdk.NewInt64Coin("adolla", 92), + Price: sdk.NewInt64Coin("pdolla", 15), + }, + }, + }, + Splits: nil, + AssetsFilledAmt: sdkmath.ZeroInt(), + AssetsUnfilledAmt: sdkmath.NewInt(92), + PriceAppliedAmt: sdkmath.ZeroInt(), + PriceLeftAmt: sdkmath.NewInt(15), + IsFinalized: false, + FeesToPay: nil, + OrderFeesLeft: nil, + PriceFilledAmt: sdkmath.ZeroInt(), + PriceUnfilledAmt: sdkmath.ZeroInt(), + }, + }, + { + name: "bid order", + order: NewOrder(3).WithBid(&BidOrder{ + MarketId: 11, + Assets: sdk.NewInt64Coin("adolla", 93), + Price: sdk.NewInt64Coin("pdolla", 16), + }), + expected: &OrderFulfillment{ + Order: &Order{ + OrderId: 3, + Order: &Order_BidOrder{ + BidOrder: &BidOrder{ + MarketId: 11, + Assets: sdk.NewInt64Coin("adolla", 93), + Price: sdk.NewInt64Coin("pdolla", 16), + }, + }, + }, + Splits: nil, + AssetsFilledAmt: sdkmath.ZeroInt(), + AssetsUnfilledAmt: sdkmath.NewInt(93), + PriceAppliedAmt: sdkmath.ZeroInt(), + PriceLeftAmt: sdkmath.NewInt(16), + IsFinalized: false, + FeesToPay: nil, + OrderFeesLeft: nil, + PriceFilledAmt: sdkmath.ZeroInt(), + PriceUnfilledAmt: sdkmath.ZeroInt(), + }, + }, + } -// TODO[1658]: func TestOrderFulfillment_GetPriceApplied(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual *OrderFulfillment + defer func() { + if t.Failed() { + t.Logf(" Actual: %s", orderFulfillmentString(actual)) + t.Logf("Expected: %s", orderFulfillmentString(tc.expected)) -// TODO[1658]: func TestOrderFulfillment_GetPriceLeft(t *testing.T) + } + }() -// TODO[1658]: func TestOrderFulfillment_GetPriceFilled(t *testing.T) + testFunc := func() { + actual = NewOrderFulfillment(tc.order) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "NewOrderFulfillment") + assert.Equal(t, tc.expected, actual, "NewOrderFulfillment result") + }) + } +} -// TODO[1658]: func TestOrderFulfillment_GetPriceUnfilled(t *testing.T) +func TestOrderFulfillment_GetAssetsFilled(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(amt)} + } + askOrder := NewOrder(444).WithAsk(&AskOrder{Assets: coin(5555)}) + bidOrder := NewOrder(666).WithBid(&BidOrder{Assets: coin(7777)}) -// TODO[1658]: func TestOrderFulfillment_IsFullyFilled(t *testing.T) + newOF := func(order *Order, amt int64) OrderFulfillment { + return OrderFulfillment{ + Order: order, + AssetsFilledAmt: sdkmath.NewInt(amt), + } + } -// TODO[1658]: func TestOrderFulfillment_IsCompletelyUnfulfilled(t *testing.T) + tests := []struct { + name string + f OrderFulfillment + exp sdk.Coin + }{ + {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, + {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, + {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, + {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, + {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, + {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, + } -// TODO[1658]: func TestOrderFulfillment_GetOrderID(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.f.GetAssetsFilled() + } + require.NotPanics(t, testFunc, "GetAssetsFilled()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetAssetsFilled() result") + }) + } +} -// TODO[1658]: func TestOrderFulfillment_IsAskOrder(t *testing.T) +func TestOrderFulfillment_GetAssetsUnfilled(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(amt)} + } + askOrder := NewOrder(444).WithAsk(&AskOrder{Assets: coin(5555)}) + bidOrder := NewOrder(666).WithBid(&BidOrder{Assets: coin(7777)}) -// TODO[1658]: func TestOrderFulfillment_IsBidOrder(t *testing.T) + newOF := func(order *Order, amt int64) OrderFulfillment { + return OrderFulfillment{ + Order: order, + AssetsUnfilledAmt: sdkmath.NewInt(amt), + } + } -// TODO[1658]: func TestOrderFulfillment_GetMarketID(t *testing.T) + tests := []struct { + name string + f OrderFulfillment + exp sdk.Coin + }{ + {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, + {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, + {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, + {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, + {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, + {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, + } -// TODO[1658]: func TestOrderFulfillment_GetOwner(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.f.GetAssetsUnfilled() + } + require.NotPanics(t, testFunc, "GetAssetsUnfilled()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetAssetsUnfilled() result") + }) + } +} -// TODO[1658]: func TestOrderFulfillment_GetAssets(t *testing.T) +func TestOrderFulfillment_GetPriceApplied(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} + } + askOrder := NewOrder(444).WithAsk(&AskOrder{Price: coin(5555)}) + bidOrder := NewOrder(666).WithBid(&BidOrder{Price: coin(7777)}) -// TODO[1658]: func TestOrderFulfillment_GetPrice(t *testing.T) + newOF := func(order *Order, amt int64) OrderFulfillment { + return OrderFulfillment{ + Order: order, + PriceAppliedAmt: sdkmath.NewInt(amt), + } + } -// TODO[1658]: func TestOrderFulfillment_GetSettlementFees(t *testing.T) + tests := []struct { + name string + f OrderFulfillment + exp sdk.Coin + }{ + {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, + {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, + {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, + {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, + {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, + {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, + } -// TODO[1658]: func TestOrderFulfillment_PartialFillAllowed(t *testing.T) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.f.GetPriceApplied() + } + require.NotPanics(t, testFunc, "GetPriceApplied()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetPriceApplied() result") + }) + } +} -// TODO[1658]: func TestOrderFulfillment_GetOrderType(t *testing.T) +func TestOrderFulfillment_GetPriceLeft(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} + } + askOrder := NewOrder(444).WithAsk(&AskOrder{Price: coin(5555)}) + bidOrder := NewOrder(666).WithBid(&BidOrder{Price: coin(7777)}) -// TODO[1658]: func TestOrderFulfillment_GetOrderTypeByte(t *testing.T) + newOF := func(order *Order, amt int64) OrderFulfillment { + return OrderFulfillment{ + Order: order, + PriceLeftAmt: sdkmath.NewInt(amt), + } + } -// TODO[1658]: func TestOrderFulfillment_GetHoldAmount(t *testing.T) + tests := []struct { + name string + f OrderFulfillment + exp sdk.Coin + }{ + {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, + {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, + {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, + {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, + {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, + {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.f.GetPriceLeft() + } + require.NotPanics(t, testFunc, "GetPriceLeft()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetPriceLeft() result") + }) + } +} + +func TestOrderFulfillment_GetPriceFilled(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} + } + askOrder := NewOrder(444).WithAsk(&AskOrder{Price: coin(5555)}) + bidOrder := NewOrder(666).WithBid(&BidOrder{Price: coin(7777)}) + + newOF := func(order *Order, amt int64) OrderFulfillment { + return OrderFulfillment{ + Order: order, + PriceFilledAmt: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + f OrderFulfillment + exp sdk.Coin + }{ + {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, + {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, + {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, + {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, + {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, + {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.f.GetPriceFilled() + } + require.NotPanics(t, testFunc, "GetPriceFilled()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetPriceFilled() result") + }) + } +} + +func TestOrderFulfillment_GetPriceUnfilled(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} + } + askOrder := NewOrder(444).WithAsk(&AskOrder{Price: coin(5555)}) + bidOrder := NewOrder(666).WithBid(&BidOrder{Price: coin(7777)}) + + newOF := func(order *Order, amt int64) OrderFulfillment { + return OrderFulfillment{ + Order: order, + PriceUnfilledAmt: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + f OrderFulfillment + exp sdk.Coin + }{ + {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, + {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, + {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, + {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, + {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, + {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.f.GetPriceUnfilled() + } + require.NotPanics(t, testFunc, "GetPriceUnfilled()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetPriceUnfilled() result") + }) + } +} + +func TestOrderFulfillment_IsFullyFilled(t *testing.T) { + newOF := func(amt int64) OrderFulfillment { + return OrderFulfillment{ + AssetsUnfilledAmt: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + f OrderFulfillment + exp bool + }{ + {name: "positive assets unfilled", f: newOF(2), exp: false}, + {name: "zero assets unfilled", f: newOF(0), exp: true}, + {name: "negative assets unfilled", f: newOF(-3), exp: true}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.f.IsFullyFilled() + } + require.NotPanics(t, testFunc, "IsFullyFilled()") + assert.Equal(t, tc.exp, actual, "IsFullyFilled() result") + }) + } +} + +func TestOrderFulfillment_IsCompletelyUnfulfilled(t *testing.T) { + newOF := func(amt int64) OrderFulfillment { + return OrderFulfillment{ + AssetsFilledAmt: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + f OrderFulfillment + exp bool + }{ + {name: "positive assets filled", f: newOF(2), exp: false}, + {name: "zero assets filled", f: newOF(0), exp: true}, + {name: "negative assets filled", f: newOF(-3), exp: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.f.IsCompletelyUnfulfilled() + } + require.NotPanics(t, testFunc, "IsCompletelyUnfulfilled()") + assert.Equal(t, tc.exp, actual, "IsCompletelyUnfulfilled() result") + }) + } +} + +func TestOrderFulfillment_GetOrderID(t *testing.T) { + newOF := func(orderID uint64) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(orderID), + } + } + + tests := []struct { + name string + f OrderFulfillment + exp uint64 + }{ + {name: "zero", f: newOF(0), exp: 0}, + {name: "one", f: newOF(1), exp: 1}, + {name: "five", f: newOF(5), exp: 5}, + {name: "max uint32+1", f: newOF(4_294_967_296), exp: 4_294_967_296}, + {name: "max uint64", f: newOF(18_446_744_073_709_551_615), exp: 18_446_744_073_709_551_615}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual uint64 + testFunc := func() { + actual = tc.f.GetOrderID() + } + require.NotPanics(t, testFunc, "GetOrderID()") + assert.Equal(t, tc.exp, actual, "GetOrderID() result") + }) + } +} + +func TestOrderFulfillment_IsAskOrder(t *testing.T) { + tests := []struct { + name string + f OrderFulfillment + exp bool + }{ + {name: "ask", f: OrderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: true}, + {name: "bid", f: OrderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: false}, + {name: "nil", f: OrderFulfillment{Order: NewOrder(888)}, exp: false}, + {name: "unknown", f: OrderFulfillment{Order: &Order{Order: &unknownOrderType{}}}, exp: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.f.IsAskOrder() + } + require.NotPanics(t, testFunc, "IsAskOrder()") + assert.Equal(t, tc.exp, actual, "IsAskOrder() result") + }) + } +} + +func TestOrderFulfillment_IsBidOrder(t *testing.T) { + tests := []struct { + name string + f OrderFulfillment + exp bool + }{ + {name: "ask", f: OrderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: false}, + {name: "bid", f: OrderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: true}, + {name: "nil", f: OrderFulfillment{Order: NewOrder(888)}, exp: false}, + {name: "unknown", f: OrderFulfillment{Order: &Order{Order: &unknownOrderType{}}}, exp: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.f.IsBidOrder() + } + require.NotPanics(t, testFunc, "IsBidOrder()") + assert.Equal(t, tc.exp, actual, "IsBidOrder() result") + }) + } +} + +func TestOrderFulfillment_GetMarketID(t *testing.T) { + askOrder := func(marketID uint32) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{MarketId: marketID}), + } + } + bidOrder := func(marketID uint32) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{MarketId: marketID}), + } + } + + tests := []struct { + name string + f OrderFulfillment + exp uint32 + }{ + {name: "ask zero", f: askOrder(0), exp: 0}, + {name: "ask one", f: askOrder(1), exp: 1}, + {name: "ask five", f: askOrder(5), exp: 5}, + {name: "ask max uint16+1", f: askOrder(65_536), exp: 65_536}, + {name: "ask max uint32", f: askOrder(4_294_967_295), exp: 4_294_967_295}, + {name: "bid zero", f: bidOrder(0), exp: 0}, + {name: "bid one", f: bidOrder(1), exp: 1}, + {name: "bid five", f: bidOrder(5), exp: 5}, + {name: "bid max uint16+1", f: bidOrder(65_536), exp: 65_536}, + {name: "bid max uint32", f: bidOrder(4_294_967_295), exp: 4_294_967_295}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual uint32 + testFunc := func() { + actual = tc.f.GetMarketID() + } + require.NotPanics(t, testFunc, "GetMarketID()") + assert.Equal(t, tc.exp, actual, "GetMarketID() result") + }) + } +} + +func TestOrderFulfillment_GetOwner(t *testing.T) { + askOrder := func(seller string) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{Seller: seller}), + } + } + bidOrder := func(buyer string) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{Buyer: buyer}), + } + } + owner := sdk.AccAddress("owner_______________").String() + + tests := []struct { + name string + f OrderFulfillment + exp string + }{ + {name: "ask empty", f: askOrder(""), exp: ""}, + {name: "ask not a bech32", f: askOrder("owner"), exp: "owner"}, + {name: "ask beche32", f: askOrder(owner), exp: owner}, + {name: "bid empty", f: bidOrder(""), exp: ""}, + {name: "bid not a bech32", f: bidOrder("owner"), exp: "owner"}, + {name: "bid beche32", f: bidOrder(owner), exp: owner}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.f.GetOwner() + } + require.NotPanics(t, testFunc, "GetOwner()") + assert.Equal(t, tc.exp, actual, "GetOwner() result") + }) + } +} + +func TestOrderFulfillment_GetAssets(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(amt)} + } + askOrder := func(amt int64) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{Assets: coin(amt)}), + } + } + bidOrder := func(amt int64) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{Assets: coin(amt)}), + } + } + + tests := []struct { + name string + f OrderFulfillment + exp sdk.Coin + }{ + {name: "ask positive", f: askOrder(123), exp: coin(123)}, + {name: "ask zero", f: askOrder(0), exp: coin(0)}, + {name: "ask negative", f: askOrder(-9), exp: coin(-9)}, + {name: "bid positive", f: bidOrder(345), exp: coin(345)}, + {name: "bid zero", f: bidOrder(0), exp: coin(0)}, + {name: "bid negative", f: bidOrder(-8), exp: coin(-8)}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.f.GetAssets() + } + require.NotPanics(t, testFunc, "GetAssets()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetAssets() result") + }) + } +} + +func TestOrderFulfillment_GetPrice(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} + } + askOrder := func(amt int64) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{Price: coin(amt)}), + } + } + bidOrder := func(amt int64) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{Price: coin(amt)}), + } + } + + tests := []struct { + name string + f OrderFulfillment + exp sdk.Coin + }{ + {name: "ask positive", f: askOrder(123), exp: coin(123)}, + {name: "ask zero", f: askOrder(0), exp: coin(0)}, + {name: "ask negative", f: askOrder(-9), exp: coin(-9)}, + {name: "bid positive", f: bidOrder(345), exp: coin(345)}, + {name: "bid zero", f: bidOrder(0), exp: coin(0)}, + {name: "bid negative", f: bidOrder(-8), exp: coin(-8)}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.f.GetPrice() + } + require.NotPanics(t, testFunc, "GetPrice()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetPrice() result") + }) + } +} + +func TestOrderFulfillment_GetSettlementFees(t *testing.T) { + coin := func(amt int64) *sdk.Coin { + return &sdk.Coin{Denom: "fees", Amount: sdkmath.NewInt(amt)} + } + askOrder := func(coin *sdk.Coin) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{SellerSettlementFlatFee: coin}), + } + } + bidOrder := func(coins sdk.Coins) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{BuyerSettlementFees: coins}), + } + } + + tests := []struct { + name string + f OrderFulfillment + exp sdk.Coins + }{ + {name: "ask nil", f: askOrder(nil), exp: nil}, + {name: "ask zero", f: askOrder(coin(0)), exp: sdk.Coins{*coin(0)}}, + {name: "ask positive", f: askOrder(coin(3)), exp: sdk.Coins{*coin(3)}}, + {name: "bid nil", f: bidOrder(nil), exp: nil}, + {name: "bid empty", f: bidOrder(sdk.Coins{}), exp: sdk.Coins{}}, + {name: "bid positive", f: bidOrder(sdk.Coins{*coin(3)}), exp: sdk.Coins{*coin(3)}}, + {name: "bid zero", f: bidOrder(sdk.Coins{*coin(0)}), exp: sdk.Coins{*coin(0)}}, + {name: "bid negative", f: bidOrder(sdk.Coins{*coin(-2)}), exp: sdk.Coins{*coin(-2)}}, + { + name: "bid multiple", + f: bidOrder(sdk.Coins{*coin(987), sdk.NewInt64Coin("six", 6), sdk.Coin{Denom: "zeg", Amount: sdkmath.NewInt(-1)}}), + exp: sdk.Coins{*coin(987), sdk.NewInt64Coin("six", 6), sdk.Coin{Denom: "zeg", Amount: sdkmath.NewInt(-1)}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coins + testFunc := func() { + actual = tc.f.GetSettlementFees() + } + require.NotPanics(t, testFunc, "GetSettlementFees()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetSettlementFees() result") + }) + } +} + +func TestOrderFulfillment_PartialFillAllowed(t *testing.T) { + askOrder := func(allowPartial bool) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{AllowPartial: allowPartial}), + } + } + bidOrder := func(allowPartial bool) OrderFulfillment { + return OrderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{AllowPartial: allowPartial}), + } + } + + tests := []struct { + name string + f OrderFulfillment + exp bool + }{ + {name: "ask true", f: askOrder(true), exp: true}, + {name: "ask false", f: askOrder(false), exp: false}, + {name: "bid true", f: bidOrder(true), exp: true}, + {name: "bid false", f: bidOrder(false), exp: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.f.PartialFillAllowed() + } + require.NotPanics(t, testFunc, "PartialFillAllowed()") + assert.Equal(t, tc.exp, actual, "PartialFillAllowed() result") + }) + } +} + +func TestOrderFulfillment_GetOrderType(t *testing.T) { + tests := []struct { + name string + f OrderFulfillment + exp string + }{ + {name: "ask", f: OrderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: OrderTypeAsk}, + {name: "bid", f: OrderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: OrderTypeBid}, + {name: "nil", f: OrderFulfillment{Order: NewOrder(888)}, exp: ""}, + {name: "unknown", f: OrderFulfillment{Order: &Order{Order: &unknownOrderType{}}}, exp: "*exchange.unknownOrderType"}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.f.GetOrderType() + } + require.NotPanics(t, testFunc, "GetOrderType()") + assert.Equal(t, tc.exp, actual, "GetOrderType() result") + }) + } +} + +func TestOrderFulfillment_GetOrderTypeByte(t *testing.T) { + tests := []struct { + name string + f OrderFulfillment + exp byte + }{ + {name: "ask", f: OrderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: OrderTypeByteAsk}, + {name: "bid", f: OrderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: OrderTypeByteBid}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual byte + testFunc := func() { + actual = tc.f.GetOrderTypeByte() + } + require.NotPanics(t, testFunc, "GetOrderTypeByte()") + assert.Equal(t, tc.exp, actual, "GetOrderTypeByte() result") + }) + } +} + +func TestOrderFulfillment_GetHoldAmount(t *testing.T) { + tests := []struct { + name string + f OrderFulfillment + }{ + { + name: "ask", + f: OrderFulfillment{ + Order: NewOrder(111).WithAsk(&AskOrder{ + Assets: sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(55)}, + SellerSettlementFlatFee: &sdk.Coin{Denom: "fee", Amount: sdkmath.NewInt(3)}, + }), + }, + }, + { + name: "bid", + f: OrderFulfillment{ + Order: NewOrder(111).WithBid(&BidOrder{ + Price: sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(55)}, + BuyerSettlementFees: sdk.Coins{ + {Denom: "feea", Amount: sdkmath.NewInt(3)}, + {Denom: "feeb", Amount: sdkmath.NewInt(4)}, + }, + }), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + expected := tc.f.GetHoldAmount() + var actual sdk.Coins + testFunc := func() { + actual = tc.f.GetHoldAmount() + } + require.NotPanics(t, testFunc, "GetHoldAmount()") + assert.Equal(t, expected, actual, "GetHoldAmount() result") + }) + } +} // TODO[1658]: func TestOrderFulfillment_Apply(t *testing.T) @@ -107,9 +907,9 @@ func copyIndexedAddrAmts(orig *indexedAddrAmts) *indexedAddrAmts { return rv } -// String converts everything in this to a string. +// String converts a indexedAddrAmtsString to a string. // This is mostly because test failure output of sdk.Coin and sdk.Coins is impossible to understand. -func (i *indexedAddrAmts) String() string { +func indexedAddrAmtsString(i *indexedAddrAmts) string { if i == nil { return "nil" } @@ -390,9 +1190,9 @@ func TestIndexedAddrAmts_Add(t *testing.T) { orig := copyIndexedAddrAmts(tc.receiver) defer func() { if t.Failed() { - t.Logf("Original: %s", orig) - t.Logf(" Actual: %s", tc.receiver) - t.Logf("Expected: %s", tc.expected) + t.Logf("Original: %s", indexedAddrAmtsString(orig)) + t.Logf(" Actual: %s", indexedAddrAmtsString(tc.receiver)) + t.Logf("Expected: %s", indexedAddrAmtsString(tc.expected)) } }() @@ -523,8 +1323,8 @@ func TestIndexedAddrAmts_GetAsInputs(t *testing.T) { assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAsInputs()") assert.Equal(t, tc.expected, actual, "getAsInputs() result") if !assert.Equal(t, orig, tc.receiver, "receiver before and after getAsInputs()") { - t.Logf("Before: %s", orig) - t.Logf(" After: %s", tc.receiver) + t.Logf("Before: %s", indexedAddrAmtsString(orig)) + t.Logf(" After: %s", indexedAddrAmtsString(tc.receiver)) } }) } @@ -646,8 +1446,8 @@ func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAsOutputs()") assert.Equal(t, tc.expected, actual, "getAsOutputs() result") if !assert.Equal(t, orig, tc.receiver, "receiver before and after getAsInputs()") { - t.Logf("Before: %s", orig) - t.Logf(" After: %s", tc.receiver) + t.Logf("Before: %s", indexedAddrAmtsString(orig)) + t.Logf(" After: %s", indexedAddrAmtsString(tc.receiver)) } }) } diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 1a95b073de..c7b2f41f0a 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -3,6 +3,7 @@ package exchange import ( "errors" "fmt" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -40,6 +41,88 @@ func copyCoinP(coin *sdk.Coin) *sdk.Coin { return &rv } +// coinPString returns either "nil" or the quoted string version of the provided coins. +func coinPString(coin *sdk.Coin) string { + if coin == nil { + return "nil" + } + return fmt.Sprintf("%q", coin) +} + +// coinsString returns either "nil" or the quoted string version of the provided coins. +func coinsString(coins sdk.Coins) string { + if coins == nil { + return "nil" + } + return fmt.Sprintf("%q", coins) +} + +// orderString is similar to %v except with easier to understand Coin and Int entries. +func orderString(order *Order) string { + if order == nil { + return "nil" + } + fields := make([]string, 1, 8) + fields[0] = fmt.Sprintf("OrderId:%d", order.OrderId) + + switch { + case order.IsAskOrder(): + fields = append(fields, fmt.Sprintf("AskOrder:%s", askOrderString(order.GetAskOrder()))) + case order.IsBidOrder(): + fields = append(fields, fmt.Sprintf("BidOrder:%s", bidOrderString(order.GetBidOrder()))) + default: + if order.GetOrder() != nil { + fields = append(fields, + fmt.Sprintf("orderType:%q", order.GetOrderType()), + fmt.Sprintf("MarketId:%d", order.GetMarketID()), + fmt.Sprintf("owner:%s", order.GetOwner()), + fmt.Sprintf("Assets:%q", order.GetAssets()), + fmt.Sprintf("Price:%q", order.GetPrice()), + fmt.Sprintf("fees:%s", coinsString(order.GetSettlementFees())), + fmt.Sprintf("AllowPartial:%t", order.PartialFillAllowed()), + ) + } else { + fields = append(fields, "Order:nil") + } + } + + return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) +} + +// askOrderString is similar to %v except with easier to understand Coin and Int entries. +func askOrderString(askOrder *AskOrder) string { + if askOrder == nil { + return "nil" + } + + fields := []string{ + fmt.Sprintf("MarketId:%d", askOrder.MarketId), + fmt.Sprintf("Seller:%q", askOrder.Seller), + fmt.Sprintf("Assets:%q", askOrder.Assets), + fmt.Sprintf("Price:%q", askOrder.Price), + fmt.Sprintf("SellerSettlementFlatFee:%s", coinPString(askOrder.SellerSettlementFlatFee)), + fmt.Sprintf("AllowPartial:%t", askOrder.AllowPartial), + } + return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) +} + +// bidOrderString is similar to %v except with easier to understand Coin and Int entries. +func bidOrderString(bidOrder *BidOrder) string { + if bidOrder == nil { + return "nil" + } + + fields := []string{ + fmt.Sprintf("MarketId:%d", bidOrder.MarketId), + fmt.Sprintf("Buyer:%q", bidOrder.Buyer), + fmt.Sprintf("Assets:%q", bidOrder.Assets), + fmt.Sprintf("Price:%q", bidOrder.Price), + fmt.Sprintf("BuyerSettlementFees:%s", coinsString(bidOrder.BuyerSettlementFees)), + fmt.Sprintf("AllowPartial:%t", bidOrder.AllowPartial), + } + return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) +} + func TestOrderTypesAndBytes(t *testing.T) { values := []struct { name string From debff298b997d3def035122eed0d9da5d0897f12 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 26 Sep 2023 12:25:20 -0600 Subject: [PATCH 178/309] [1658]: Unit tests on GetAsset/PriceTransfer. --- x/exchange/fulfillment_test.go | 485 ++++++++++++++++++++++++++++++++- x/exchange/orders_test.go | 10 +- 2 files changed, 484 insertions(+), 11 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 71ebaee48a..aa7bbbc27b 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -1455,6 +1455,487 @@ func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { // TODO[1658]: func TestBuildSettlementTransfers(t *testing.T) -// TODO[1658]: func TestGetAssetTransfer(t *testing.T) +func transferString(t *Transfer) string { + if t == nil { + return "nil" + } + inputs := "nil" + if t.Inputs != nil { + inputVals := make([]string, len(t.Inputs)) + for i, input := range t.Inputs { + inputVals[i] = bankInputString(input) + } + inputs = strings.Join(inputVals, ", ") + } + outputs := "nil" + if t.Outputs != nil { + outputVals := make([]string, len(t.Outputs)) + for i, output := range t.Outputs { + outputVals[i] = bankOutputString(output) + } + outputs = strings.Join(outputVals, ", ") + } + return fmt.Sprintf("{Inputs:%s, Outputs: %s}", inputs, outputs) +} + +// bankInputString is similar to %v except with easier to understand Coin entries. +func bankInputString(i banktypes.Input) string { + return fmt.Sprintf("I{Address:%q,Coins:%q}", i.Address, i.Coins) +} + +// bankOutputString is similar to %v except with easier to understand Coin entries. +func bankOutputString(o banktypes.Output) string { + return fmt.Sprintf("O{Address:%q,Coins:%q}", o.Address, o.Coins) +} + +func TestGetAssetTransfer(t *testing.T) { + coin := func(amt int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amt)} + } + igc := coin(2468, "ignorable") // igc => "ignorable coin" + askOrder := func(orderID uint64, seller string, assets sdk.Coin) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Seller: seller, + Assets: assets, + }) + } + bidOrder := func(orderID uint64, buyer string, assets sdk.Coin) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Buyer: buyer, + Assets: assets, + }) + } + orderSplit := func(order *Order, assets sdk.Coin) *OrderSplit { + return &OrderSplit{ + Order: &OrderFulfillment{Order: order}, + Assets: assets, + Price: igc, + } + } + input := func(addr string, coins ...sdk.Coin) banktypes.Input { + return banktypes.Input{Address: addr, Coins: coins} + } + output := func(addr string, coins ...sdk.Coin) banktypes.Output { + return banktypes.Output{Address: addr, Coins: coins} + } + + tests := []struct { + name string + f *OrderFulfillment + exp *Transfer + expPanic string + }{ + { + name: "ask, one split", + f: &OrderFulfillment{ + Order: askOrder(1, "seller", coin(25, "carrot")), + AssetsFilledAmt: sdkmath.NewInt(33), + Splits: []*OrderSplit{orderSplit(bidOrder(2, "buyer", igc), coin(88, "banana"))}, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("seller", coin(33, "carrot"))}, + Outputs: []banktypes.Output{output("buyer", coin(88, "banana"))}, + }, + }, + { + name: "ask, two splits diff addrs", + f: &OrderFulfillment{ + Order: askOrder(3, "SELLER", coin(26, "carrot")), + AssetsFilledAmt: sdkmath.NewInt(4321), + Splits: []*OrderSplit{ + orderSplit(bidOrder(4, "buyer 1", igc), coin(89, "banana")), + orderSplit(bidOrder(5, "second buyer", igc), coin(45, "apple")), + }, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("SELLER", coin(4321, "carrot"))}, + Outputs: []banktypes.Output{ + output("buyer 1", coin(89, "banana")), + output("second buyer", coin(45, "apple")), + }, + }, + }, + { + name: "ask, two splits same addr, two denoms", + f: &OrderFulfillment{ + Order: askOrder(6, "SeLleR", coin(27, "carrot")), + AssetsFilledAmt: sdkmath.NewInt(5511), + Splits: []*OrderSplit{ + orderSplit(bidOrder(7, "buyer", igc), coin(90, "banana")), + orderSplit(bidOrder(8, "buyer", igc), coin(46, "apple")), + }, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("SeLleR", coin(5511, "carrot"))}, + Outputs: []banktypes.Output{output("buyer", coin(46, "apple"), coin(90, "banana"))}, + }, + }, + { + name: "ask, two splits same addr, one denom", + f: &OrderFulfillment{ + Order: askOrder(9, "sellsell", coin(28, "carrot")), + AssetsFilledAmt: sdkmath.NewInt(42), + Splits: []*OrderSplit{ + orderSplit(bidOrder(10, "buybuy", igc), coin(55, "apple")), + orderSplit(bidOrder(11, "buybuy", igc), coin(34, "apple")), + }, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("sellsell", coin(42, "carrot"))}, + Outputs: []banktypes.Output{output("buybuy", coin(89, "apple"))}, + }, + }, + { + name: "ask, negative price in split", + f: &OrderFulfillment{ + Order: askOrder(12, "goodsell", coin(29, "carrot")), + AssetsFilledAmt: sdkmath.NewInt(91), + Splits: []*OrderSplit{orderSplit(bidOrder(13, "buygood", igc), coin(-4, "banana"))}, + }, + expPanic: "cannot index and add invalid coin amount \"-4banana\"", + }, + { + name: "ask, negative price applied", + f: &OrderFulfillment{ + Order: askOrder(14, "solong", coin(30, "carrot")), + AssetsFilledAmt: sdkmath.NewInt(-5), + Splits: []*OrderSplit{orderSplit(bidOrder(15, "hello", igc), coin(66, "banana"))}, + }, + expPanic: "invalid coin set -5carrot: coin -5carrot amount is not positive", + }, + + { + name: "bid, one split", + f: &OrderFulfillment{ + Order: bidOrder(1, "buyer", coin(25, "carrot")), + AssetsFilledAmt: sdkmath.NewInt(33), + Splits: []*OrderSplit{orderSplit(askOrder(2, "seller", igc), coin(88, "banana"))}, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("seller", coin(88, "banana"))}, + Outputs: []banktypes.Output{output("buyer", coin(33, "carrot"))}, + }, + }, + { + name: "bid, two splits diff addrs", + f: &OrderFulfillment{ + Order: bidOrder(3, "BUYER", coin(26, "carrot")), + AssetsFilledAmt: sdkmath.NewInt(4321), + Splits: []*OrderSplit{ + orderSplit(askOrder(4, "seller 1", igc), coin(89, "banana")), + orderSplit(askOrder(5, "second seller", igc), coin(45, "apple")), + }, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{ + input("seller 1", coin(89, "banana")), + input("second seller", coin(45, "apple")), + }, + Outputs: []banktypes.Output{output("BUYER", coin(4321, "carrot"))}, + }, + }, + { + name: "bid, two splits same addr, two denoms", + f: &OrderFulfillment{ + Order: bidOrder(6, "BuYeR", coin(27, "carrot")), + AssetsFilledAmt: sdkmath.NewInt(5511), + Splits: []*OrderSplit{ + orderSplit(askOrder(7, "seller", igc), coin(90, "banana")), + orderSplit(askOrder(8, "seller", igc), coin(46, "apple")), + }, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("seller", coin(46, "apple"), coin(90, "banana"))}, + Outputs: []banktypes.Output{output("BuYeR", coin(5511, "carrot"))}, + }, + }, + { + name: "bid, two splits same addr, one denom", + f: &OrderFulfillment{ + Order: bidOrder(9, "buybuy", coin(28, "carrot")), + AssetsFilledAmt: sdkmath.NewInt(42), + Splits: []*OrderSplit{ + orderSplit(bidOrder(10, "sellsell", igc), coin(55, "apple")), + orderSplit(bidOrder(11, "sellsell", igc), coin(34, "apple")), + }, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("sellsell", coin(89, "apple"))}, + Outputs: []banktypes.Output{output("buybuy", coin(42, "carrot"))}, + }, + }, + { + name: "bid, negative price in split", + f: &OrderFulfillment{ + Order: bidOrder(12, "goodbuy", coin(29, "carrot")), + AssetsFilledAmt: sdkmath.NewInt(91), + Splits: []*OrderSplit{orderSplit(askOrder(13, "sellgood", igc), coin(-4, "banana"))}, + }, + expPanic: "cannot index and add invalid coin amount \"-4banana\"", + }, + { + name: "bid, negative price applied", + f: &OrderFulfillment{ + Order: bidOrder(14, "heythere", coin(30, "carrot")), + AssetsFilledAmt: sdkmath.NewInt(-5), + Splits: []*OrderSplit{orderSplit(askOrder(15, "afterwhile", igc), coin(66, "banana"))}, + }, + expPanic: "invalid coin set -5carrot: coin -5carrot amount is not positive", + }, + + { + name: "nil inside order", + f: &OrderFulfillment{Order: NewOrder(20)}, + expPanic: "unknown order type ", + }, + { + name: "unknown inside order", + f: &OrderFulfillment{Order: &Order{OrderId: 21, Order: &unknownOrderType{}}}, + expPanic: "unknown order type *exchange.unknownOrderType", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual *Transfer + defer func() { + if t.Failed() { + t.Logf(" Actual: %s", transferString(actual)) + t.Logf("Expected: %s", transferString(tc.exp)) + t.Logf("OrderFulfillment: %s", orderFulfillmentString(tc.f)) + } + }() + testFunc := func() { + actual = GetAssetTransfer(tc.f) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetAssetTransfer") + assert.Equal(t, tc.exp, actual, "GetAssetTransfer") + }) + } +} -// TODO[1658]: func TestGetPriceTransfer(t *testing.T) +func TestGetPriceTransfer(t *testing.T) { + coin := func(amt int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amt)} + } + igc := coin(2468, "ignorable") // igc => "ignorable coin" + askOrder := func(orderID uint64, seller string, price sdk.Coin) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Seller: seller, + Price: price, + }) + } + bidOrder := func(orderID uint64, buyer string, price sdk.Coin) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Buyer: buyer, + Price: price, + }) + } + orderSplit := func(order *Order, price sdk.Coin) *OrderSplit { + return &OrderSplit{ + Order: &OrderFulfillment{Order: order}, + Price: price, + Assets: igc, + } + } + input := func(addr string, coins ...sdk.Coin) banktypes.Input { + return banktypes.Input{Address: addr, Coins: coins} + } + output := func(addr string, coins ...sdk.Coin) banktypes.Output { + return banktypes.Output{Address: addr, Coins: coins} + } + + tests := []struct { + name string + f *OrderFulfillment + exp *Transfer + expPanic string + }{ + { + name: "ask, one split", + f: &OrderFulfillment{ + Order: askOrder(1, "seller", coin(25, "carrot")), + PriceAppliedAmt: sdkmath.NewInt(33), + Splits: []*OrderSplit{orderSplit(bidOrder(2, "buyer", igc), coin(88, "banana"))}, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("buyer", coin(88, "banana"))}, + Outputs: []banktypes.Output{output("seller", coin(33, "carrot"))}, + }, + }, + { + name: "ask, two splits diff addrs", + f: &OrderFulfillment{ + Order: askOrder(3, "SELLER", coin(26, "carrot")), + PriceAppliedAmt: sdkmath.NewInt(4321), + Splits: []*OrderSplit{ + orderSplit(bidOrder(4, "buyer 1", igc), coin(89, "banana")), + orderSplit(bidOrder(5, "second buyer", igc), coin(45, "apple")), + }, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{ + input("buyer 1", coin(89, "banana")), + input("second buyer", coin(45, "apple")), + }, + Outputs: []banktypes.Output{output("SELLER", coin(4321, "carrot"))}, + }, + }, + { + name: "ask, two splits same addr, two denoms", + f: &OrderFulfillment{ + Order: askOrder(6, "SeLleR", coin(27, "carrot")), + PriceAppliedAmt: sdkmath.NewInt(5511), + Splits: []*OrderSplit{ + orderSplit(bidOrder(7, "buyer", igc), coin(90, "banana")), + orderSplit(bidOrder(8, "buyer", igc), coin(46, "apple")), + }, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("buyer", coin(46, "apple"), coin(90, "banana"))}, + Outputs: []banktypes.Output{output("SeLleR", coin(5511, "carrot"))}, + }, + }, + { + name: "ask, two splits same addr, one denom", + f: &OrderFulfillment{ + Order: askOrder(9, "sellsell", coin(28, "carrot")), + PriceAppliedAmt: sdkmath.NewInt(42), + Splits: []*OrderSplit{ + orderSplit(bidOrder(10, "buybuy", igc), coin(55, "apple")), + orderSplit(bidOrder(11, "buybuy", igc), coin(34, "apple")), + }, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("buybuy", coin(89, "apple"))}, + Outputs: []banktypes.Output{output("sellsell", coin(42, "carrot"))}, + }, + }, + { + name: "ask, negative price in split", + f: &OrderFulfillment{ + Order: askOrder(12, "goodsell", coin(29, "carrot")), + PriceAppliedAmt: sdkmath.NewInt(91), + Splits: []*OrderSplit{orderSplit(bidOrder(13, "buygood", igc), coin(-4, "banana"))}, + }, + expPanic: "cannot index and add invalid coin amount \"-4banana\"", + }, + { + name: "ask, negative price applied", + f: &OrderFulfillment{ + Order: askOrder(14, "solong", coin(30, "carrot")), + PriceAppliedAmt: sdkmath.NewInt(-5), + Splits: []*OrderSplit{orderSplit(bidOrder(15, "hello", igc), coin(66, "banana"))}, + }, + expPanic: "invalid coin set -5carrot: coin -5carrot amount is not positive", + }, + + { + name: "bid, one split", + f: &OrderFulfillment{ + Order: bidOrder(1, "buyer", coin(25, "carrot")), + PriceAppliedAmt: sdkmath.NewInt(33), + Splits: []*OrderSplit{orderSplit(askOrder(2, "seller", igc), coin(88, "banana"))}, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("buyer", coin(33, "carrot"))}, + Outputs: []banktypes.Output{output("seller", coin(88, "banana"))}, + }, + }, + { + name: "bid, two splits diff addrs", + f: &OrderFulfillment{ + Order: bidOrder(3, "BUYER", coin(26, "carrot")), + PriceAppliedAmt: sdkmath.NewInt(4321), + Splits: []*OrderSplit{ + orderSplit(askOrder(4, "seller 1", igc), coin(89, "banana")), + orderSplit(askOrder(5, "second seller", igc), coin(45, "apple")), + }, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("BUYER", coin(4321, "carrot"))}, + Outputs: []banktypes.Output{ + output("seller 1", coin(89, "banana")), + output("second seller", coin(45, "apple")), + }, + }, + }, + { + name: "bid, two splits same addr, two denoms", + f: &OrderFulfillment{ + Order: bidOrder(6, "BuYeR", coin(27, "carrot")), + PriceAppliedAmt: sdkmath.NewInt(5511), + Splits: []*OrderSplit{ + orderSplit(askOrder(7, "seller", igc), coin(90, "banana")), + orderSplit(askOrder(8, "seller", igc), coin(46, "apple")), + }, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("BuYeR", coin(5511, "carrot"))}, + Outputs: []banktypes.Output{output("seller", coin(46, "apple"), coin(90, "banana"))}, + }, + }, + { + name: "bid, two splits same addr, one denom", + f: &OrderFulfillment{ + Order: bidOrder(9, "buybuy", coin(28, "carrot")), + PriceAppliedAmt: sdkmath.NewInt(42), + Splits: []*OrderSplit{ + orderSplit(bidOrder(10, "sellsell", igc), coin(55, "apple")), + orderSplit(bidOrder(11, "sellsell", igc), coin(34, "apple")), + }, + }, + exp: &Transfer{ + Inputs: []banktypes.Input{input("buybuy", coin(42, "carrot"))}, + Outputs: []banktypes.Output{output("sellsell", coin(89, "apple"))}, + }, + }, + { + name: "bid, negative price in split", + f: &OrderFulfillment{ + Order: bidOrder(12, "goodbuy", coin(29, "carrot")), + PriceAppliedAmt: sdkmath.NewInt(91), + Splits: []*OrderSplit{orderSplit(askOrder(13, "sellgood", igc), coin(-4, "banana"))}, + }, + expPanic: "cannot index and add invalid coin amount \"-4banana\"", + }, + { + name: "bid, negative price applied", + f: &OrderFulfillment{ + Order: bidOrder(14, "heythere", coin(30, "carrot")), + PriceAppliedAmt: sdkmath.NewInt(-5), + Splits: []*OrderSplit{orderSplit(askOrder(15, "afterwhile", igc), coin(66, "banana"))}, + }, + expPanic: "invalid coin set -5carrot: coin -5carrot amount is not positive", + }, + + { + name: "nil inside order", + f: &OrderFulfillment{Order: NewOrder(20)}, + expPanic: "unknown order type ", + }, + { + name: "unknown inside order", + f: &OrderFulfillment{Order: &Order{OrderId: 21, Order: &unknownOrderType{}}}, + expPanic: "unknown order type *exchange.unknownOrderType", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual *Transfer + defer func() { + if t.Failed() { + t.Logf(" Actual: %s", transferString(actual)) + t.Logf("Expected: %s", transferString(tc.exp)) + t.Logf("OrderFulfillment: %s", orderFulfillmentString(tc.f)) + } + }() + testFunc := func() { + actual = GetPriceTransfer(tc.f) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetPriceTransfer") + assert.Equal(t, tc.exp, actual, "GetPriceTransfer") + }) + } +} diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index c7b2f41f0a..7bf29d7a00 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -72,15 +72,7 @@ func orderString(order *Order) string { fields = append(fields, fmt.Sprintf("BidOrder:%s", bidOrderString(order.GetBidOrder()))) default: if order.GetOrder() != nil { - fields = append(fields, - fmt.Sprintf("orderType:%q", order.GetOrderType()), - fmt.Sprintf("MarketId:%d", order.GetMarketID()), - fmt.Sprintf("owner:%s", order.GetOwner()), - fmt.Sprintf("Assets:%q", order.GetAssets()), - fmt.Sprintf("Price:%q", order.GetPrice()), - fmt.Sprintf("fees:%s", coinsString(order.GetSettlementFees())), - fmt.Sprintf("AllowPartial:%t", order.PartialFillAllowed()), - ) + fields = append(fields, fmt.Sprintf("orderType:%q", order.GetOrderType())) } else { fields = append(fields, "Order:nil") } From 0b586ca4dcf9193943c43e89db140dddc6a4c051 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 26 Sep 2023 14:39:50 -0600 Subject: [PATCH 179/309] [1658]: Unit tests on BuildSettlementTransfers. --- x/exchange/fulfillment.go | 26 +- x/exchange/fulfillment_test.go | 474 ++++++++++++++++++++++++++++++++- 2 files changed, 477 insertions(+), 23 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 75e3b08079..3d61631216 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -562,6 +562,7 @@ type Fulfillments struct { // BidOFs are all the bid orders and how they are to be filled. BidOFs []*OrderFulfillment // PartialOrder contains info on an order that is only being partially filled. + // The transfers For part of its funds are included in the order fulfillments. PartialOrder *PartialFulfillment } @@ -752,7 +753,7 @@ func (i *indexedAddrAmts) add(addr string, coins ...sdk.Coin) { // Panics if this is nil, has no addrs, or has a negative coin amount. func (i *indexedAddrAmts) getAsInputs() []banktypes.Input { if i == nil || len(i.addrs) == 0 { - panic(errors.New("cannot get inputs from empty set")) + return nil } rv := make([]banktypes.Input, len(i.addrs)) for n, addr := range i.addrs { @@ -768,7 +769,7 @@ func (i *indexedAddrAmts) getAsInputs() []banktypes.Input { // Panics if this is nil, has no addrs, or has a negative coin amount. func (i *indexedAddrAmts) getAsOutputs() []banktypes.Output { if i == nil || len(i.addrs) == 0 { - panic(errors.New("cannot get inputs from empty set")) + return nil } rv := make([]banktypes.Output, len(i.addrs)) for n, addr := range i.addrs { @@ -799,26 +800,25 @@ type SettlementTransfers struct { // BuildSettlementTransfers creates all the order transfers needed for the provided fulfillments. // Assumes that all fulfillments have passed Validate. // Panics if any amounts are negative. -func BuildSettlementTransfers(fulfillments *Fulfillments) *SettlementTransfers { +func BuildSettlementTransfers(f *Fulfillments) *SettlementTransfers { + allOFs := make([]*OrderFulfillment, 0, len(f.AskOFs)+len(f.BidOFs)) + allOFs = append(allOFs, f.AskOFs...) + allOFs = append(allOFs, f.BidOFs...) + indexedFees := newIndexedAddrAmts() + rv := &SettlementTransfers{ + OrderTransfers: make([]*Transfer, 0, len(allOFs)*2), + } - rv := &SettlementTransfers{} - applyOF := func(of *OrderFulfillment) { + for _, of := range allOFs { rv.OrderTransfers = append(rv.OrderTransfers, GetAssetTransfer(of), GetPriceTransfer(of)) if !of.FeesToPay.IsZero() { - // Using NewCoins in here (instead of Coins{...}) as a last-ditch negative amount panic check. + // Using NewCoins in here as a last-ditch negative amount panic check. fees := sdk.NewCoins(of.FeesToPay...) indexedFees.add(of.GetOwner(), fees...) } } - for _, askOF := range fulfillments.AskOFs { - applyOF(askOF) - } - for _, bidOf := range fulfillments.BidOFs { - applyOF(bidOf) - } - rv.FeeInputs = indexedFees.getAsInputs() return rv diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index aa7bbbc27b..cbcc2045a4 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -1220,8 +1220,8 @@ func TestIndexedAddrAmts_GetAsInputs(t *testing.T) { expected []banktypes.Input expPanic string }{ - {name: "nil receiver", receiver: nil, expPanic: "cannot get inputs from empty set"}, - {name: "no addrs", receiver: newIndexedAddrAmts(), expPanic: "cannot get inputs from empty set"}, + {name: "nil receiver", receiver: nil, expected: nil}, + {name: "no addrs", receiver: newIndexedAddrAmts(), expected: nil}, { name: "one addr negative amount", receiver: &indexedAddrAmts{ @@ -1343,8 +1343,8 @@ func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { expected []banktypes.Output expPanic string }{ - {name: "nil receiver", receiver: nil, expPanic: "cannot get inputs from empty set"}, - {name: "no addrs", receiver: newIndexedAddrAmts(), expPanic: "cannot get inputs from empty set"}, + {name: "nil receiver", receiver: nil, expected: nil}, + {name: "no addrs", receiver: newIndexedAddrAmts(), expected: nil}, { name: "one addr negative amount", receiver: &indexedAddrAmts{ @@ -1453,8 +1453,462 @@ func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { } } -// TODO[1658]: func TestBuildSettlementTransfers(t *testing.T) +// fulfillmentsString is similar to %v except with easier to understand Coin entries. +func fulfillmentsString(f *Fulfillments) string { + if f == nil { + return "nil" + } + + fields := []string{ + fmt.Sprintf("AskOFs: %s", orderFulfillmentsString(f.AskOFs)), + fmt.Sprintf("BidOFs: %s", orderFulfillmentsString(f.BidOFs)), + fmt.Sprintf("PartialOrder: %s", partialFulfillmentString(f.PartialOrder)), + } + return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) +} + +// orderFulfillmentsString is similar to %v except with easier to understand Coin entries. +func orderFulfillmentsString(ofs []*OrderFulfillment) string { + if ofs == nil { + return "nil" + } + + vals := make([]string, len(ofs)) + for i, f := range ofs { + vals[i] = orderFulfillmentString(f) + } + return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) +} + +// partialFulfillmentString is similar to %v except with easier to understand Coin entries. +func partialFulfillmentString(p *PartialFulfillment) string { + if p == nil { + return "nil" + } + + fields := []string{ + fmt.Sprintf("NewOrder:%s", orderString(p.NewOrder)), + fmt.Sprintf("AssetsFilled:%s", coinPString(&p.AssetsFilled)), + fmt.Sprintf("PriceFilled:%s", coinPString(&p.PriceFilled)), + } + return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) +} + +// transferString is similar to %v except with easier to understand Coin entries. +func settlementTransfersString(s *SettlementTransfers) string { + if s == nil { + return "nil" + } + + orderTransfers := "nil" + if s.OrderTransfers != nil { + transVals := make([]string, len(s.OrderTransfers)) + for i, trans := range s.OrderTransfers { + transVals[i] = transferString(trans) + } + orderTransfers = fmt.Sprintf("[%s]", strings.Join(transVals, ", ")) + } + + feeInputs := "nil" + if s.FeeInputs != nil { + feeVals := make([]string, len(s.FeeInputs)) + for i, input := range s.FeeInputs { + feeVals[i] = bankInputString(input) + } + feeInputs = fmt.Sprintf("[%s]", strings.Join(feeVals, ", ")) + } + + return fmt.Sprintf("{OrderTransfers:%s, FeeInputs:%s}", orderTransfers, feeInputs) +} + +func TestBuildSettlementTransfers(t *testing.T) { + coin := func(amt int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amt)} + } + igc := coin(2468, "ignorable") // igc => "ignorable coin" + askOrder := func(orderID uint64, seller string, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + MarketId: 97531, + Seller: seller, + Assets: assets, + Price: price, + }) + } + bidOrder := func(orderID uint64, buyer string, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + MarketId: 97531, + Buyer: buyer, + Assets: assets, + Price: price, + }) + } + askSplit := func(orderID uint64, seller string, assets, price sdk.Coin) *OrderSplit { + return &OrderSplit{ + Order: &OrderFulfillment{Order: askOrder(orderID, seller, igc, igc)}, + Assets: assets, + Price: price, + } + } + bidSplit := func(orderID uint64, seller string, assets, price sdk.Coin) *OrderSplit { + return &OrderSplit{ + Order: &OrderFulfillment{Order: bidOrder(orderID, seller, igc, igc)}, + Assets: assets, + Price: price, + } + } + input := func(addr string, coins ...sdk.Coin) banktypes.Input { + return banktypes.Input{Address: addr, Coins: coins} + } + output := func(addr string, coins ...sdk.Coin) banktypes.Output { + return banktypes.Output{Address: addr, Coins: coins} + } + + tests := []struct { + name string + f *Fulfillments + expected *SettlementTransfers + expPanic string + }{ + { + name: "just an ask, no fees", + f: &Fulfillments{ + AskOFs: []*OrderFulfillment{ + { + Order: askOrder(1, "seller", coin(2, "oasset"), coin(3, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(4), + PriceAppliedAmt: sdkmath.NewInt(5), + Splits: []*OrderSplit{ + bidSplit(6, "buyer", coin(7, "sasset"), coin(8, "sprice")), + bidSplit(9, "buyer", coin(10, "sasset"), coin(11, "sprice")), + }, + FeesToPay: nil, + }, + }, + }, + expected: &SettlementTransfers{ + OrderTransfers: []*Transfer{ + { + Inputs: []banktypes.Input{input("seller", coin(4, "oasset"))}, + Outputs: []banktypes.Output{output("buyer", coin(17, "sasset"))}, + }, + { + Inputs: []banktypes.Input{input("buyer", coin(19, "sprice"))}, + Outputs: []banktypes.Output{output("seller", coin(5, "oprice"))}, + }, + }, + FeeInputs: nil, + }, + }, + { + name: "just an ask, with fees", + f: &Fulfillments{ + AskOFs: []*OrderFulfillment{ + { + Order: askOrder(1, "seller", coin(2, "oasset"), coin(3, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(4), + PriceAppliedAmt: sdkmath.NewInt(5), + Splits: []*OrderSplit{ + bidSplit(6, "buyer", coin(7, "sasset"), coin(8, "sprice")), + bidSplit(9, "buyer", coin(10, "sasset"), coin(11, "sprice")), + }, + FeesToPay: sdk.NewCoins(coin(12, "feea"), coin(13, "feeb")), + }, + }, + }, + expected: &SettlementTransfers{ + OrderTransfers: []*Transfer{ + { + Inputs: []banktypes.Input{input("seller", coin(4, "oasset"))}, + Outputs: []banktypes.Output{output("buyer", coin(17, "sasset"))}, + }, + { + Inputs: []banktypes.Input{input("buyer", coin(19, "sprice"))}, + Outputs: []banktypes.Output{output("seller", coin(5, "oprice"))}, + }, + }, + FeeInputs: []banktypes.Input{input("seller", coin(12, "feea"), coin(13, "feeb"))}, + }, + }, + { + name: "just a bid, no fees", + f: &Fulfillments{ + BidOFs: []*OrderFulfillment{ + { + Order: bidOrder(1, "buyer", coin(2, "oasset"), coin(3, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(4), + PriceAppliedAmt: sdkmath.NewInt(5), + Splits: []*OrderSplit{ + askSplit(6, "seller", coin(7, "sasset"), coin(8, "sprice")), + askSplit(9, "seller", coin(10, "sasset"), coin(11, "sprice")), + }, + FeesToPay: nil, + }, + }, + }, + expected: &SettlementTransfers{ + OrderTransfers: []*Transfer{ + { + Inputs: []banktypes.Input{input("seller", coin(17, "sasset"))}, + Outputs: []banktypes.Output{output("buyer", coin(4, "oasset"))}, + }, + { + Inputs: []banktypes.Input{input("buyer", coin(5, "oprice"))}, + Outputs: []banktypes.Output{output("seller", coin(19, "sprice"))}, + }, + }, + FeeInputs: nil, + }, + }, + { + name: "just a bid, with fees", + f: &Fulfillments{ + BidOFs: []*OrderFulfillment{ + { + Order: bidOrder(1, "buyer", coin(2, "oasset"), coin(3, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(4), + PriceAppliedAmt: sdkmath.NewInt(5), + Splits: []*OrderSplit{ + askSplit(6, "seller", coin(7, "sasset"), coin(8, "sprice")), + askSplit(9, "seller", coin(10, "sasset"), coin(11, "sprice")), + }, + FeesToPay: sdk.NewCoins(coin(12, "feea"), coin(13, "feeb")), + }, + }, + }, + expected: &SettlementTransfers{ + OrderTransfers: []*Transfer{ + { + Inputs: []banktypes.Input{input("seller", coin(17, "sasset"))}, + Outputs: []banktypes.Output{output("buyer", coin(4, "oasset"))}, + }, + { + Inputs: []banktypes.Input{input("buyer", coin(5, "oprice"))}, + Outputs: []banktypes.Output{output("seller", coin(19, "sprice"))}, + }, + }, + FeeInputs: []banktypes.Input{input("buyer", coin(12, "feea"), coin(13, "feeb"))}, + }, + }, + { + name: "two asks two bids", + f: &Fulfillments{ + AskOFs: []*OrderFulfillment{ + { + Order: askOrder(1, "order seller", coin(2, "oasset"), coin(3, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(4), + PriceAppliedAmt: sdkmath.NewInt(5), + Splits: []*OrderSplit{ + bidSplit(6, "split buyer one", coin(7, "sasset"), coin(8, "sprice")), + bidSplit(9, "split buyer two", coin(10, "sasset"), coin(11, "sprice")), + }, + FeesToPay: sdk.NewCoins(coin(12, "sellfee")), + }, + { + Order: askOrder(13, "order seller", coin(14, "oasset"), coin(15, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(16), + PriceAppliedAmt: sdkmath.NewInt(17), + Splits: []*OrderSplit{ + bidSplit(18, "split buyer one", coin(19, "sasset"), coin(20, "sprice")), + }, + FeesToPay: sdk.NewCoins(coin(21, "sellfee")), + }, + }, + BidOFs: []*OrderFulfillment{ + { + Order: bidOrder(22, "order buyer one", coin(23, "oasset"), coin(24, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(25), + PriceAppliedAmt: sdkmath.NewInt(26), + Splits: []*OrderSplit{ + askSplit(27, "split seller one", coin(28, "sasset"), coin(29, "sprice")), + askSplit(30, "split seller one", coin(31, "sasset"), coin(32, "sprice")), + }, + FeesToPay: sdk.NewCoins(coin(33, "buyfee")), + }, + { + Order: bidOrder(34, "order buyer two", coin(35, "oasset"), coin(36, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(37), + PriceAppliedAmt: sdkmath.NewInt(38), + Splits: []*OrderSplit{ + askSplit(39, "split seller one", coin(40, "sasset"), coin(41, "sprice")), + }, + FeesToPay: sdk.NewCoins(coin(42, "buyfee")), + }, + }, + }, + expected: &SettlementTransfers{ + OrderTransfers: []*Transfer{ + { + Inputs: []banktypes.Input{input("order seller", coin(4, "oasset"))}, + Outputs: []banktypes.Output{ + output("split buyer one", coin(7, "sasset")), + output("split buyer two", coin(10, "sasset")), + }, + }, + { + Inputs: []banktypes.Input{ + input("split buyer one", coin(8, "sprice")), + input("split buyer two", coin(11, "sprice")), + }, + Outputs: []banktypes.Output{output("order seller", coin(5, "oprice"))}, + }, + { + Inputs: []banktypes.Input{input("order seller", coin(16, "oasset"))}, + Outputs: []banktypes.Output{output("split buyer one", coin(19, "sasset"))}, + }, + { + Inputs: []banktypes.Input{input("split buyer one", coin(20, "sprice"))}, + Outputs: []banktypes.Output{output("order seller", coin(17, "oprice"))}, + }, + { + Inputs: []banktypes.Input{input("split seller one", coin(59, "sasset"))}, + Outputs: []banktypes.Output{output("order buyer one", coin(25, "oasset"))}, + }, + { + Inputs: []banktypes.Input{input("order buyer one", coin(26, "oprice"))}, + Outputs: []banktypes.Output{output("split seller one", coin(61, "sprice"))}, + }, + { + Inputs: []banktypes.Input{input("split seller one", coin(40, "sasset"))}, + Outputs: []banktypes.Output{output("order buyer two", coin(37, "oasset"))}, + }, + { + Inputs: []banktypes.Input{input("order buyer two", coin(38, "oprice"))}, + Outputs: []banktypes.Output{output("split seller one", coin(41, "sprice"))}, + }, + }, + FeeInputs: []banktypes.Input{ + input("order seller", coin(33, "sellfee")), + input("order buyer one", coin(33, "buyfee")), + input("order buyer two", coin(42, "buyfee")), + }, + }, + }, + { + name: "negative ask asset", + f: &Fulfillments{ + AskOFs: []*OrderFulfillment{ + { + Order: askOrder(1, "seller", coin(2, "oasset"), coin(3, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(-4), + PriceAppliedAmt: sdkmath.NewInt(5), + Splits: []*OrderSplit{ + bidSplit(6, "buyer", coin(7, "sasset"), coin(8, "sprice")), + bidSplit(9, "buyer", coin(10, "sasset"), coin(11, "sprice")), + }, + }, + }, + }, + expPanic: "invalid coin set -4oasset: coin -4oasset amount is not positive", + }, + { + name: "negative ask price", + f: &Fulfillments{ + AskOFs: []*OrderFulfillment{ + { + Order: askOrder(1, "seller", coin(2, "oasset"), coin(3, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(4), + PriceAppliedAmt: sdkmath.NewInt(-5), + Splits: []*OrderSplit{ + bidSplit(6, "buyer", coin(7, "sasset"), coin(8, "sprice")), + bidSplit(9, "buyer", coin(10, "sasset"), coin(11, "sprice")), + }, + }, + }, + }, + expPanic: "invalid coin set -5oprice: coin -5oprice amount is not positive", + }, + { + name: "negative bid asset", + f: &Fulfillments{ + BidOFs: []*OrderFulfillment{ + { + Order: bidOrder(1, "buyer", coin(2, "oasset"), coin(3, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(-4), + PriceAppliedAmt: sdkmath.NewInt(5), + Splits: []*OrderSplit{ + askSplit(6, "seller", coin(7, "sasset"), coin(8, "sprice")), + askSplit(9, "seller", coin(10, "sasset"), coin(11, "sprice")), + }, + }, + }, + }, + expPanic: "invalid coin set -4oasset: coin -4oasset amount is not positive", + }, + { + name: "negative bid price", + f: &Fulfillments{ + BidOFs: []*OrderFulfillment{ + { + Order: bidOrder(1, "buyer", coin(2, "oasset"), coin(3, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(4), + PriceAppliedAmt: sdkmath.NewInt(-5), + Splits: []*OrderSplit{ + askSplit(6, "seller", coin(7, "sasset"), coin(8, "sprice")), + askSplit(9, "seller", coin(10, "sasset"), coin(11, "sprice")), + }, + }, + }, + }, + expPanic: "invalid coin set -5oprice: coin -5oprice amount is not positive", + }, + { + name: "ask with negative fees", + f: &Fulfillments{ + BidOFs: []*OrderFulfillment{ + { + Order: bidOrder(1, "buyer", coin(2, "oasset"), coin(3, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(4), + PriceAppliedAmt: sdkmath.NewInt(5), + Splits: []*OrderSplit{ + askSplit(6, "seller", coin(7, "sasset"), coin(8, "sprice")), + askSplit(9, "seller", coin(10, "sasset"), coin(11, "sprice")), + }, + FeesToPay: sdk.Coins{coin(-12, "feecoin")}, + }, + }, + }, + expPanic: "invalid coin set -12feecoin: coin -12feecoin amount is not positive", + }, + { + name: "bid with negative fees", + f: &Fulfillments{ + BidOFs: []*OrderFulfillment{ + { + Order: bidOrder(1, "buyer", coin(2, "oasset"), coin(3, "oprice")), + AssetsFilledAmt: sdkmath.NewInt(4), + PriceAppliedAmt: sdkmath.NewInt(5), + Splits: []*OrderSplit{ + askSplit(6, "seller", coin(7, "sasset"), coin(8, "sprice")), + askSplit(9, "seller", coin(10, "sasset"), coin(11, "sprice")), + }, + FeesToPay: sdk.Coins{coin(-12, "feecoin")}, + }, + }, + }, + expPanic: "invalid coin set -12feecoin: coin -12feecoin amount is not positive", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual *SettlementTransfers + defer func() { + if t.Failed() { + t.Logf(" Actual: %s", settlementTransfersString(actual)) + t.Logf("Expected: %s", settlementTransfersString(tc.expected)) + t.Logf("Fulfillments: %s", fulfillmentsString(tc.f)) + } + }() + testFunc := func() { + actual = BuildSettlementTransfers(tc.f) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "BuildSettlementTransfers") + assert.Equal(t, tc.expected, actual, "BuildSettlementTransfers result") + }) + } +} +// transferString is similar to %v except with easier to understand Coin entries. func transferString(t *Transfer) string { if t == nil { return "nil" @@ -1465,7 +1919,7 @@ func transferString(t *Transfer) string { for i, input := range t.Inputs { inputVals[i] = bankInputString(input) } - inputs = strings.Join(inputVals, ", ") + inputs = fmt.Sprintf("[%s]", strings.Join(inputVals, ", ")) } outputs := "nil" if t.Outputs != nil { @@ -1473,9 +1927,9 @@ func transferString(t *Transfer) string { for i, output := range t.Outputs { outputVals[i] = bankOutputString(output) } - outputs = strings.Join(outputVals, ", ") + outputs = fmt.Sprintf("[%s]", strings.Join(outputVals, ", ")) } - return fmt.Sprintf("{Inputs:%s, Outputs: %s}", inputs, outputs) + return fmt.Sprintf("T{Inputs:%s, Outputs: %s}", inputs, outputs) } // bankInputString is similar to %v except with easier to understand Coin entries. @@ -1709,7 +2163,7 @@ func TestGetAssetTransfer(t *testing.T) { actual = GetAssetTransfer(tc.f) } assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetAssetTransfer") - assert.Equal(t, tc.exp, actual, "GetAssetTransfer") + assert.Equal(t, tc.exp, actual, "GetAssetTransfer result") }) } } @@ -1935,7 +2389,7 @@ func TestGetPriceTransfer(t *testing.T) { actual = GetPriceTransfer(tc.f) } assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetPriceTransfer") - assert.Equal(t, tc.exp, actual, "GetPriceTransfer") + assert.Equal(t, tc.exp, actual, "GetPriceTransfer result") }) } } From bbb45a9c89ff5c958d39b2d218c7139cf2f48d58 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 27 Sep 2023 11:55:32 -0600 Subject: [PATCH 180/309] [1658]: Unit tests on the OrderFulfillment Apply and ApplyLeftoverPrice methods. --- x/exchange/fulfillment.go | 10 +- x/exchange/fulfillment_test.go | 458 ++++++++++++++++++++++++++++++++- x/exchange/orders_test.go | 67 ++++- 3 files changed, 524 insertions(+), 11 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 3d61631216..4b7dcc0880 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -184,7 +184,7 @@ func (f *OrderFulfillment) Apply(order *OrderFulfillment, assetsAmt, priceAmt sd newPriceLeftAmt := f.PriceLeftAmt.Sub(priceAmt) // ask orders are allow to go negative on price left, but bid orders are not. if newPriceLeftAmt.IsNegative() && f.IsBidOrder() { - return fmt.Errorf("cannot apply %s order %d having price left %q to %s order %d at a price of %q: overfill", + return fmt.Errorf("cannot fill %s order %d having price left %q to %s order %d at a price of %q: overfill", f.GetOrderType(), f.GetOrderID(), f.GetPriceLeft(), order.GetOrderType(), order.GetOrderID(), price) } @@ -200,6 +200,8 @@ func (f *OrderFulfillment) Apply(order *OrderFulfillment, assetsAmt, priceAmt sd return nil } +// ApplyLeftoverPrice increases this fulfillment and the provided split +// using info in the split and the provided amount. func (f *OrderFulfillment) ApplyLeftoverPrice(askSplit *OrderSplit, amt sdkmath.Int) { // Update this fulfillment to indicate that the amount has been applied. f.PriceLeftAmt = f.PriceLeftAmt.Sub(amt) @@ -217,9 +219,13 @@ func (f *OrderFulfillment) ApplyLeftoverPrice(askSplit *OrderSplit, amt sdkmath. for _, bidSplit := range askSplit.Order.Splits { if bidSplit.Order.GetOrderID() == orderID { bidSplit.Price.Amount = bidSplit.Price.Amount.Add(amt) - break + return } } + + // If we didn't find a bid split to update, something is horribly wrong. + panic(fmt.Errorf("could not apply leftover amount %s from %s order %d to %s order %d: bid split not found", + amt, f.GetOrderType(), orderID, askSplit.Order.GetOrderType(), askSplit.Order.GetOrderID())) } // Finalize does some final calculations and validation for this order fulfillment. diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index cbcc2045a4..3bf44d8a1f 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -16,6 +16,63 @@ import ( "github.com/provenance-io/provenance/testutil/assertions" ) +// Annoyingly, sdkmath.NewInt(0) and sdkmath.ZeroInt() are not internally equal to an Int that +// started with a value and was reduced to zero. +// In other words, assert.Equal(t, sdkmath.ZeroInt(), sdkmath.NewInt(1).SubRaw(1)) fails. +// With those, Int.abs = (big.nat) . +// With this, Int.abs = (big.nat){}. +// So when an object has an sdkmath.Int that should have been reduced to zero, you'll need to use this. +var ZeroAmtAfterSub = sdkmath.NewInt(1).SubRaw(1) + +// copyOrderSplit creates a copy of this order split. +// Unlike the other copiers in here, the Order is not deep copied, it will be the same reference. +func copyOrderSplit(split *OrderSplit) *OrderSplit { + if split == nil { + return nil + } + + return &OrderSplit{ + // just copying the reference here to prevent infinite recursion. + Order: split.Order, + Assets: copyCoin(split.Assets), + Price: copyCoin(split.Price), + } +} + +// copyOrderSplits copies a slice of order splits. +func copyOrderSplits(splits []*OrderSplit) []*OrderSplit { + if splits == nil { + return nil + } + + rv := make([]*OrderSplit, len(splits)) + for i, split := range splits { + rv[i] = copyOrderSplit(split) + } + return rv +} + +// copyOrderFulfillment returns a deep copy of an order fulfillement. +func copyOrderFulfillment(f *OrderFulfillment) *OrderFulfillment { + if f == nil { + return nil + } + + return &OrderFulfillment{ + Order: copyOrder(f.Order), + Splits: copyOrderSplits(f.Splits), + AssetsFilledAmt: copySDKInt(f.AssetsFilledAmt), + AssetsUnfilledAmt: copySDKInt(f.AssetsUnfilledAmt), + PriceAppliedAmt: copySDKInt(f.PriceAppliedAmt), + PriceLeftAmt: copySDKInt(f.PriceLeftAmt), + IsFinalized: f.IsFinalized, + FeesToPay: copyCoins(f.FeesToPay), + OrderFeesLeft: copyCoins(f.OrderFeesLeft), + PriceFilledAmt: copySDKInt(f.PriceFilledAmt), + PriceUnfilledAmt: copySDKInt(f.PriceFilledAmt), + } +} + // orderSplitString is similar to %v except with easier to understand Coin and Int entries. func orderSplitString(s *OrderSplit) string { if s == nil { @@ -52,15 +109,15 @@ func orderFulfillmentString(f *OrderFulfillment) string { fields := []string{ fmt.Sprintf("Order:%s", orderString(f.Order)), fmt.Sprintf("Splits:%s", orderSplitsString(f.Splits)), - fmt.Sprintf("AssetsFilledAmt:%q", f.AssetsFilledAmt), - fmt.Sprintf("AssetsUnfilledAmt:%q", f.AssetsUnfilledAmt), - fmt.Sprintf("PriceAppliedAmt:%q", f.PriceAppliedAmt), - fmt.Sprintf("PriceLeftAmt:%q", f.PriceLeftAmt), + fmt.Sprintf("AssetsFilledAmt:%s", f.AssetsFilledAmt), + fmt.Sprintf("AssetsUnfilledAmt:%s", f.AssetsUnfilledAmt), + fmt.Sprintf("PriceAppliedAmt:%s", f.PriceAppliedAmt), + fmt.Sprintf("PriceLeftAmt:%s", f.PriceLeftAmt), fmt.Sprintf("IsFinalized:%t", f.IsFinalized), fmt.Sprintf("FeesToPay:%s", coinsString(f.FeesToPay)), fmt.Sprintf("OrderFeesLeft:%s", coinsString(f.OrderFeesLeft)), - fmt.Sprintf("PriceFilledAmt:%q", f.PriceFilledAmt), - fmt.Sprintf("PriceUnfilledAmt:%q", f.PriceUnfilledAmt), + fmt.Sprintf("PriceFilledAmt:%s", f.PriceFilledAmt), + fmt.Sprintf("PriceUnfilledAmt:%s", f.PriceUnfilledAmt), } return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) } @@ -858,9 +915,393 @@ func TestOrderFulfillment_GetHoldAmount(t *testing.T) { } } -// TODO[1658]: func TestOrderFulfillment_Apply(t *testing.T) +// assertEqualOrderFulfillments asserts that the two order fulfillments are equal. +// Returns true if equal. +// If not equal, and neither are nil, equality on each field is also asserted in order to help identify the problem. +func assertEqualOrderFulfillments(t *testing.T, expected, actual *OrderFulfillment, message string, args ...interface{}) bool { + if assert.Equalf(t, expected, actual, message, args...) { + return true + } + // If either is nil, that's easy to understand in the above failure, so there's nothing more to do. + if expected == nil || actual == nil { + return false + } + + msg := func(val string) string { + if len(message) == 0 { + return val + } + return val + "\n" + message + } + + // Assert equality on each individual field so that we can more easily find the problem. + // If any of the Ints fail with a complaint about Int.abs = (big.nat) vs {}, use ZeroAmtAfterSub for the expected. + assert.Equalf(t, expected.Order, actual.Order, msg("OrderFulfillment.Order"), args...) + assert.Equalf(t, expected.Splits, actual.Splits, msg("OrderFulfillment.Splits"), args...) + assert.Equalf(t, expected.AssetsFilledAmt, actual.AssetsFilledAmt, msg("OrderFulfillment.AssetsFilledAmt"), args...) + assert.Equalf(t, expected.AssetsUnfilledAmt, actual.AssetsUnfilledAmt, msg("OrderFulfillment.AssetsUnfilledAmt"), args...) + assert.Equalf(t, expected.PriceAppliedAmt, actual.PriceAppliedAmt, msg("OrderFulfillment.PriceAppliedAmt"), args...) + assert.Equalf(t, expected.PriceLeftAmt, actual.PriceLeftAmt, msg("OrderFulfillment.PriceLeftAmt"), args...) + assert.Equalf(t, expected.IsFinalized, actual.IsFinalized, msg("OrderFulfillment.IsFinalized"), args...) + assert.Equalf(t, expected.FeesToPay, actual.FeesToPay, msg("OrderFulfillment.FeesToPay"), args...) + assert.Equalf(t, expected.OrderFeesLeft, actual.OrderFeesLeft, msg("OrderFulfillment.OrderFeesLeft"), args...) + assert.Equalf(t, expected.PriceFilledAmt, actual.PriceFilledAmt, msg("OrderFulfillment.PriceFilledAmt"), args...) + assert.Equalf(t, expected.PriceUnfilledAmt, actual.PriceUnfilledAmt, msg("OrderFulfillment.PriceUnfilledAmt"), args...) + return false +} + +func TestOrderFulfillment_Apply(t *testing.T) { + assetCoin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "acoin", Amount: sdkmath.NewInt(amt)} + } + priceCoin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "pcoin", Amount: sdkmath.NewInt(amt)} + } + askOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + MarketId: 86420, + Seller: "seller", + Assets: assetCoin(assetsAmt), + Price: priceCoin(priceAmt), + }) + } + bidOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + MarketId: 86420, + Buyer: "buyer", + Assets: assetCoin(assetsAmt), + Price: priceCoin(priceAmt), + }) + } -// TODO[1658]: func TestOrderFulfillment_ApplyLeftoverPrice(t *testing.T) + tests := []struct { + name string + receiver *OrderFulfillment + order *OrderFulfillment + assetsAmt sdkmath.Int + priceAmt sdkmath.Int + expErr string + expResult *OrderFulfillment + }{ + { + name: "fills order in full", + receiver: &OrderFulfillment{ + Order: askOrder(1, 20, 55), + Splits: nil, + AssetsFilledAmt: sdkmath.ZeroInt(), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.ZeroInt(), + PriceLeftAmt: sdkmath.NewInt(55), + }, + order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, + assetsAmt: sdkmath.NewInt(20), + priceAmt: sdkmath.NewInt(55), + expResult: &OrderFulfillment{ + Order: askOrder(1, 20, 55), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, + Assets: assetCoin(20), + Price: priceCoin(55), + }, + }, + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(55), + PriceLeftAmt: ZeroAmtAfterSub, + }, + }, + { + name: "partially fills order", + receiver: &OrderFulfillment{ + Order: askOrder(1, 20, 55), + Splits: nil, + AssetsFilledAmt: sdkmath.ZeroInt(), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.ZeroInt(), + PriceLeftAmt: sdkmath.NewInt(55), + }, + order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, + assetsAmt: sdkmath.NewInt(11), + priceAmt: sdkmath.NewInt(22), + expResult: &OrderFulfillment{ + Order: askOrder(1, 20, 55), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, + Assets: assetCoin(11), + Price: priceCoin(22), + }, + }, + AssetsFilledAmt: sdkmath.NewInt(11), + AssetsUnfilledAmt: sdkmath.NewInt(9), + PriceAppliedAmt: sdkmath.NewInt(22), + PriceLeftAmt: sdkmath.NewInt(33), + }, + }, + { + name: "already partially filled, fills rest", + receiver: &OrderFulfillment{ + Order: askOrder(1, 20, 55), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: bidOrder(3, 60, 220)}, + Assets: assetCoin(9), + Price: priceCoin(33), + }, + }, + AssetsFilledAmt: sdkmath.NewInt(9), + AssetsUnfilledAmt: sdkmath.NewInt(11), + PriceAppliedAmt: sdkmath.NewInt(33), + PriceLeftAmt: sdkmath.NewInt(22), + }, + order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, + assetsAmt: sdkmath.NewInt(11), + priceAmt: sdkmath.NewInt(22), + expResult: &OrderFulfillment{ + Order: askOrder(1, 20, 55), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: bidOrder(3, 60, 220)}, + Assets: assetCoin(9), + Price: priceCoin(33), + }, + { + Order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, + Assets: assetCoin(11), + Price: priceCoin(22), + }, + }, + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(55), + PriceLeftAmt: ZeroAmtAfterSub, + }, + }, + { + name: "ask assets overfill", + receiver: &OrderFulfillment{ + Order: askOrder(1, 20, 55), + Splits: nil, + AssetsFilledAmt: sdkmath.ZeroInt(), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.ZeroInt(), + PriceLeftAmt: sdkmath.NewInt(55), + }, + order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, + assetsAmt: sdkmath.NewInt(21), + priceAmt: sdkmath.NewInt(55), + expErr: "cannot fill ask order 1 having assets left \"20acoin\" with \"21acoin\" from bid order 2: overfill", + }, + { + name: "bid assets overfill", + receiver: &OrderFulfillment{ + Order: bidOrder(1, 20, 55), + Splits: nil, + AssetsFilledAmt: sdkmath.ZeroInt(), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.ZeroInt(), + PriceLeftAmt: sdkmath.NewInt(55), + }, + order: &OrderFulfillment{Order: askOrder(2, 40, 110)}, + assetsAmt: sdkmath.NewInt(21), + priceAmt: sdkmath.NewInt(55), + expErr: "cannot fill bid order 1 having assets left \"20acoin\" with \"21acoin\" from ask order 2: overfill", + }, + { + name: "ask price overfill", + receiver: &OrderFulfillment{ + Order: askOrder(1, 20, 55), + Splits: nil, + AssetsFilledAmt: sdkmath.ZeroInt(), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.ZeroInt(), + PriceLeftAmt: sdkmath.NewInt(55), + }, + order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, + assetsAmt: sdkmath.NewInt(20), + priceAmt: sdkmath.NewInt(56), + expResult: &OrderFulfillment{ + Order: askOrder(1, 20, 55), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, + Assets: assetCoin(20), + Price: priceCoin(56), + }, + }, + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(56), + PriceLeftAmt: sdkmath.NewInt(-1), + }, + }, + { + name: "bid price overfill", + receiver: &OrderFulfillment{ + Order: bidOrder(1, 20, 55), + Splits: nil, + AssetsFilledAmt: sdkmath.ZeroInt(), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.ZeroInt(), + PriceLeftAmt: sdkmath.NewInt(55), + }, + order: &OrderFulfillment{Order: askOrder(2, 40, 110)}, + assetsAmt: sdkmath.NewInt(20), + priceAmt: sdkmath.NewInt(56), + expErr: "cannot fill bid order 1 having price left \"55pcoin\" to ask order 2 at a price of \"56pcoin\": overfill", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + orig := copyOrderFulfillment(tc.receiver) + if tc.expResult == nil { + tc.expResult = copyOrderFulfillment(tc.receiver) + } + + var err error + testFunc := func() { + err = tc.receiver.Apply(tc.order, tc.assetsAmt, tc.priceAmt) + } + require.NotPanics(t, testFunc, "Apply") + assertions.AssertErrorValue(t, err, tc.expErr, "Apply error") + if !assertEqualOrderFulfillments(t, tc.expResult, tc.receiver, "order fulfillment after .Apply") { + t.Logf("Original: %s", orderFulfillmentString(orig)) + t.Logf(" Actual: %s", orderFulfillmentString(tc.receiver)) + t.Logf("Expected: %s", orderFulfillmentString(tc.expResult)) + } + }) + } +} + +func TestOrderFulfillment_ApplyLeftoverPrice(t *testing.T) { + type testCase struct { + name string + receiver *OrderFulfillment + askSplit *OrderSplit + amt sdkmath.Int + expFulfillment *OrderFulfillment + expAskSplit *OrderSplit + expPanic string + } + + newTestCase := func(name string, bidSplitIndexes ...int) testCase { + // Picture a bid order with 150 assets at a cost of 5555 being split among 3 ask orders evenly (50 each). + // Each ask order has 53 to sell: 50 are coming from this bid order, and 1 and 2 each from two other bids. + // During initial splitting, the bid will pay each ask 5555 * 50 / 150 = 1851. + // 1851 * 3 = 5553, so there's 2 leftover. + // The other 3 are being bought for 30 each (90 total). + + bidOrderID := uint64(200) + bidOrder := NewOrder(bidOrderID).WithBid(&BidOrder{ + Price: sdk.NewInt64Coin("pcoin", 5555), + }) + + tc := testCase{ + name: name, + receiver: &OrderFulfillment{ + Order: bidOrder, + PriceAppliedAmt: sdkmath.NewInt(5553), + PriceLeftAmt: sdkmath.NewInt(2), + }, + amt: sdkmath.NewInt(2), + expFulfillment: &OrderFulfillment{ + Order: bidOrder, + PriceAppliedAmt: sdkmath.NewInt(5555), + PriceLeftAmt: ZeroAmtAfterSub, + }, + askSplit: &OrderSplit{ + Order: &OrderFulfillment{ + Order: NewOrder(1).WithAsk(&AskOrder{ + Price: sdk.NewInt64Coin("pcoin", 1500), + }), + PriceAppliedAmt: sdkmath.NewInt(1941), // 5555 * 50 / 150 = 1851 from main bid + 90 from the others. + PriceLeftAmt: sdkmath.NewInt(-441), // = 1500 - 1941 + }, + Price: sdk.NewInt64Coin("pcoin", 1851), + }, + expAskSplit: &OrderSplit{ + Order: &OrderFulfillment{ + Order: NewOrder(1).WithAsk(&AskOrder{ + Price: sdk.NewInt64Coin("pcoin", 1500), + }), + PriceAppliedAmt: sdkmath.NewInt(1943), + PriceLeftAmt: sdkmath.NewInt(-443), + }, + Price: sdk.NewInt64Coin("pcoin", 1853), + }, + } + + bidSplits := []*OrderSplit{ + { + // This is the primary bid split that we'll be looking to update. + Order: &OrderFulfillment{Order: NewOrder(bidOrderID).WithBid(&BidOrder{})}, + Price: sdk.NewInt64Coin("pcoin", 1851), // == 5555 / 3 (truncated) + }, + { + Order: &OrderFulfillment{Order: NewOrder(bidOrderID + 1).WithBid(&BidOrder{})}, + Price: sdk.NewInt64Coin("pcoin", 30), + }, + { + Order: &OrderFulfillment{Order: NewOrder(bidOrderID + 2).WithBid(&BidOrder{})}, + Price: sdk.NewInt64Coin("pcoin", 60), + }, + { + // This one is similar to [0], but with a different order id. + // It'll be used to test the case where the bid split isn't found. + Order: &OrderFulfillment{Order: NewOrder(bidOrderID + 3).WithBid(&BidOrder{})}, + Price: sdk.NewInt64Coin("pcoin", 1851), + }, + } + + for _, i := range bidSplitIndexes { + tc.askSplit.Order.Splits = append(tc.askSplit.Order.Splits, bidSplits[i]) + if i == 0 { + tc.expAskSplit.Order.Splits = append(tc.expAskSplit.Order.Splits, &OrderSplit{ + Order: &OrderFulfillment{Order: NewOrder(bidOrderID).WithBid(&BidOrder{})}, + Price: sdk.NewInt64Coin("pcoin", 1853), // == 5555 * 50 / 150 + 2 leftover. + }) + } else { + tc.expAskSplit.Order.Splits = append(tc.expAskSplit.Order.Splits, copyOrderSplit(bidSplits[i])) + } + if i == 3 { + tc.expPanic = "could not apply leftover amount 2 from bid order 200 to ask order 1: bid split not found" + } + } + + return tc + } + + tests := []testCase{ + newTestCase("applies to first bid split", 0, 1, 2), + newTestCase("applies to second bid split", 2, 0, 1), + newTestCase("applies to third bid split", 1, 2, 0), + newTestCase("bid split not found", 1, 2, 3), + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + origFulfillment := copyOrderFulfillment(tc.receiver) + origSplit := copyOrderSplit(tc.askSplit) + + testFunc := func() { + tc.receiver.ApplyLeftoverPrice(tc.askSplit, tc.amt) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "ApplyLeftoverPrice") + if !assertEqualOrderFulfillments(t, tc.expFulfillment, tc.receiver, "OrderFulfillment after .ApplyLeftoverPrice") { + t.Logf("Original OrderFulfillment: %s", orderFulfillmentString(origFulfillment)) + t.Logf(" Actual OrderFulfillment: %s", orderFulfillmentString(tc.receiver)) + t.Logf("Expected OrderFulfillment: %s", orderFulfillmentString(tc.expFulfillment)) + } + if !assert.Equal(t, tc.expAskSplit, tc.askSplit, "askSplit after ApplyLeftoverPrice") { + t.Logf("Original askSplit: %s", orderSplitString(origSplit)) + t.Logf(" Actual askSplit: %s", orderSplitString(tc.askSplit)) + t.Logf("Expected askSplit: %s", orderSplitString(tc.expAskSplit)) + } + }) + } +} // TODO[1658]: func TestOrderFulfillment_Finalize(t *testing.T) @@ -874,6 +1315,7 @@ func TestOrderFulfillment_GetHoldAmount(t *testing.T) { // TODO[1658]: func TestBuildFulfillments(t *testing.T) +// copyIndexedAddrAmts creates a deep copy of an indexedAddrAmts. func copyIndexedAddrAmts(orig *indexedAddrAmts) *indexedAddrAmts { if orig == nil { return nil diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 7bf29d7a00..55eeef897c 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -15,6 +15,71 @@ import ( "github.com/provenance-io/provenance/testutil/assertions" ) +// copyOrder creates a copy of the provided order. +func copyOrder(order *Order) *Order { + if order == nil { + return nil + } + + rv := &Order{ + OrderId: order.OrderId, + } + if order.Order != nil { + switch v := order.Order.(type) { + case *Order_AskOrder: + rv.Order = &Order_AskOrder{AskOrder: copyAskOrder(v.AskOrder)} + case *Order_BidOrder: + rv.Order = &Order_BidOrder{BidOrder: copyBidOrder(v.BidOrder)} + case *unknownOrderType: + rv.Order = &unknownOrderType{} + default: + panic(fmt.Sprintf("unknown order type %q", order.GetOrderType())) + } + } + + return rv +} + +// copyAskOrder creates a copy of the provided ask order. +func copyAskOrder(askOrder *AskOrder) *AskOrder { + if askOrder == nil { + return nil + } + return &AskOrder{ + MarketId: askOrder.MarketId, + Seller: askOrder.Seller, + Assets: copyCoin(askOrder.Assets), + Price: copyCoin(askOrder.Price), + SellerSettlementFlatFee: copyCoinP(askOrder.SellerSettlementFlatFee), + AllowPartial: askOrder.AllowPartial, + } +} + +// copyBidOrder creates a copy of the provided bid order. +func copyBidOrder(bidOrder *BidOrder) *BidOrder { + if bidOrder == nil { + return nil + } + return &BidOrder{ + MarketId: bidOrder.MarketId, + Buyer: bidOrder.Buyer, + Assets: copyCoin(bidOrder.Assets), + Price: copyCoin(bidOrder.Price), + BuyerSettlementFees: copyCoins(bidOrder.BuyerSettlementFees), + AllowPartial: bidOrder.AllowPartial, + } +} + +// copySDKInt creates a copy of the provided sdkmath.Int +func copySDKInt(i sdkmath.Int) (copy sdkmath.Int) { + defer func() { + if r := recover(); r != nil { + copy = sdkmath.Int{} + } + }() + return i.AddRaw(0) +} + // copyCoins creates a copy of the provided coins slice with copies of each entry. func copyCoins(coins sdk.Coins) sdk.Coins { if coins == nil { @@ -29,7 +94,7 @@ func copyCoins(coins sdk.Coins) sdk.Coins { // copyCoin returns a copy of the provided coin. func copyCoin(coin sdk.Coin) sdk.Coin { - return sdk.Coin{Denom: coin.Denom, Amount: coin.Amount.AddRaw(0)} + return sdk.Coin{Denom: coin.Denom, Amount: copySDKInt(coin.Amount)} } // copyCoinP returns a copy of the provided *coin. From c3ec406a15db4739c9a8c16384ca8e8120195170 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 27 Sep 2023 13:50:56 -0600 Subject: [PATCH 181/309] [1658]: Don't use intermediate variables in GetFulfillmentAssetsAmt, and rename partialFulfillments variable (from partialFulfillment) in BuildFulfillments since it's a list. --- x/exchange/fulfillment.go | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 4b7dcc0880..3d268dceae 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -58,6 +58,7 @@ type OrderFulfillment struct { var _ OrderI = (*OrderFulfillment)(nil) +// NewOrderFulfillment creates a new OrderFulfillment wrapping the provided order. func NewOrderFulfillment(order *Order) *OrderFulfillment { return &OrderFulfillment{ Order: order, @@ -546,8 +547,7 @@ func Fulfill(of1, of2 *OrderFulfillment) error { // GetFulfillmentAssetsAmt figures out the assets that can be fulfilled with the two provided orders. func GetFulfillmentAssetsAmt(of1, of2 *OrderFulfillment) (sdkmath.Int, error) { - of1AmtLeft, of2AmtLeft := of1.AssetsUnfilledAmt, of2.AssetsUnfilledAmt - if !of1AmtLeft.IsPositive() || !of2AmtLeft.IsPositive() { + if !of1.AssetsUnfilledAmt.IsPositive() || !of2.AssetsUnfilledAmt.IsPositive() { return sdkmath.ZeroInt(), fmt.Errorf("cannot fill %s order %d having assets left %q "+ "with %s order %d having assets left %q: zero or negative assets left", of1.GetOrderType(), of1.GetOrderID(), of1.GetAssetsUnfilled(), @@ -555,10 +555,10 @@ func GetFulfillmentAssetsAmt(of1, of2 *OrderFulfillment) (sdkmath.Int, error) { } // Return the lesser of the two. - if of1AmtLeft.LTE(of2AmtLeft) { - return of1AmtLeft, nil + if of1.AssetsUnfilledAmt.LTE(of2.AssetsUnfilledAmt) { + return of1.AssetsUnfilledAmt, nil } - return of2AmtLeft, nil + return of2.AssetsUnfilledAmt, nil } // Fulfillments contains information on how orders are to be fulfilled. @@ -582,6 +582,7 @@ type PartialFulfillment struct { PriceFilled sdk.Coin } +// NewPartialFulfillment creates a new PartialFulfillment using the provided OrderFulfillment information. func NewPartialFulfillment(f *OrderFulfillment) *PartialFulfillment { rv := &PartialFulfillment{ NewOrder: NewOrder(f.GetOrderID()), @@ -621,6 +622,7 @@ func NewPartialFulfillment(f *OrderFulfillment) *PartialFulfillment { return rv } + // This is here in case another order type is created, but a case for it isn't added to this func. panic(fmt.Errorf("order %d has unknown type %q", f.GetOrderID(), f.GetOrderType())) } @@ -681,7 +683,7 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) } // Make sure none of them are partially filled except possibly the last in each list. - var partialFulfillment []*OrderFulfillment + var partialFulfillments []*OrderFulfillment lastAskI, lastBidI := len(askOFs)-1, len(bidOFs)-1 for i, askOF := range askOFs { if !askOF.IsFullyFilled() { @@ -689,7 +691,7 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) return nil, fmt.Errorf("ask order %d (at index %d) is not filled in full and is not the last ask order provided", askOF.GetOrderID(), i) } - partialFulfillment = append(partialFulfillment, askOF) + partialFulfillments = append(partialFulfillments, askOF) } } for i, bidOF := range bidOFs { @@ -698,15 +700,15 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) return nil, fmt.Errorf("bid order %d (at index %d) is not filled in full and is not the last bid order provided", bidOF.GetOrderID(), i) } - partialFulfillment = append(partialFulfillment, bidOF) + partialFulfillments = append(partialFulfillments, bidOF) } } // And make sure that only one order is being partially filled. - if len(partialFulfillment) > 1 { + if len(partialFulfillments) > 1 { return nil, fmt.Errorf("%s order %d and %s order %d cannot both be partially filled", - partialFulfillment[0].GetOrderType(), partialFulfillment[0].GetOrderID(), - partialFulfillment[1].GetOrderType(), partialFulfillment[1].GetOrderID()) + partialFulfillments[0].GetOrderType(), partialFulfillments[0].GetOrderID(), + partialFulfillments[1].GetOrderType(), partialFulfillments[1].GetOrderID()) } rv := &Fulfillments{ @@ -714,8 +716,8 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) BidOFs: bidOFs, } - if len(partialFulfillment) > 0 { - rv.PartialOrder = NewPartialFulfillment(partialFulfillment[0]) + if len(partialFulfillments) > 0 { + rv.PartialOrder = NewPartialFulfillment(partialFulfillments[0]) } return rv, nil From 805ce1837e072908b59ea2780ff4e432bf2ff692 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 27 Sep 2023 13:51:48 -0600 Subject: [PATCH 182/309] [1658]: Add the order id to the error message for non SubOrderI types. --- x/exchange/orders.go | 2 +- x/exchange/orders_test.go | 58 +++++++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 46eac6dabd..72eeafdc94 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -133,7 +133,7 @@ func (o Order) GetSubOrder() (SubOrderI, error) { default: // If this is called without the sub-order being set yet, it's a programming error, so panic. // If it's a type that doesn't implement SubOrderI, that needs to be done, so panic. - return nil, fmt.Errorf("unknown sub-order type %T: does not implement SubOrderI", v) + return nil, fmt.Errorf("order %d has unknown sub-order type %T: does not implement SubOrderI", o.OrderId, v) } } diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 55eeef897c..3812156155 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -498,10 +498,20 @@ func TestOrder_GetOrderID(t *testing.T) { } } -const ( - nilSubTypeErr = "unknown sub-order type : does not implement SubOrderI" - unknownSubTypeErr = "unknown sub-order type *exchange.unknownOrderType: does not implement SubOrderI" -) +// badSubTypeErr creates the expected error when a sub-order type is bad. +func badSubTypeErr(orderID uint64, badType string) string { + return fmt.Sprintf("order %d has unknown sub-order type %s: does not implement SubOrderI", orderID, badType) +} + +// nilSubTypeErr creates the expected error when a sub-order type is nil. +func nilSubTypeErr(orderID uint64) string { + return badSubTypeErr(orderID, "") +} + +// unknownSubTypeErr creates the expected error when a sub-order type is the unknownOrderType. +func unknownSubTypeErr(orderID uint64) string { + return badSubTypeErr(orderID, "*exchange.unknownOrderType") +} func TestOrder_GetSubOrder(t *testing.T) { askOrder := &AskOrder{ @@ -540,12 +550,12 @@ func TestOrder_GetSubOrder(t *testing.T) { { name: "nil sub-order", order: NewOrder(3), - expErr: nilSubTypeErr, + expErr: nilSubTypeErr(3), }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expErr: unknownSubTypeErr, + expErr: unknownSubTypeErr(4), }, } @@ -590,12 +600,12 @@ func TestOrder_GetMarketID(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: nilSubTypeErr, + expPanic: nilSubTypeErr(3), }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: unknownSubTypeErr, + expPanic: unknownSubTypeErr(4), }, } @@ -631,12 +641,12 @@ func TestOrder_GetOwner(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: nilSubTypeErr, + expPanic: nilSubTypeErr(3), }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: unknownSubTypeErr, + expPanic: unknownSubTypeErr(4), }, } @@ -672,12 +682,12 @@ func TestOrder_GetAssets(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: nilSubTypeErr, + expPanic: nilSubTypeErr(3), }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: unknownSubTypeErr, + expPanic: unknownSubTypeErr(4), }, } @@ -713,12 +723,12 @@ func TestOrder_GetPrice(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: nilSubTypeErr, + expPanic: nilSubTypeErr(3), }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: unknownSubTypeErr, + expPanic: unknownSubTypeErr(4), }, } @@ -754,12 +764,12 @@ func TestOrder_GetSettlementFees(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: nilSubTypeErr, + expPanic: nilSubTypeErr(3), }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: unknownSubTypeErr, + expPanic: unknownSubTypeErr(4), }, } @@ -795,12 +805,12 @@ func TestOrder_PartialFillAllowed(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: nilSubTypeErr, + expPanic: nilSubTypeErr(3), }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: unknownSubTypeErr, + expPanic: unknownSubTypeErr(4), }, } @@ -876,12 +886,12 @@ func TestOrder_GetOrderTypeByte(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: nilSubTypeErr, + expPanic: nilSubTypeErr(3), }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: unknownSubTypeErr, + expPanic: unknownSubTypeErr(4), }, } @@ -927,12 +937,12 @@ func TestOrder_GetHoldAmount(t *testing.T) { { name: "nil inside order", order: NewOrder(3), - expPanic: nilSubTypeErr, + expPanic: nilSubTypeErr(3), }, { name: "unknown order type", order: &Order{OrderId: 4, Order: &unknownOrderType{}}, - expPanic: unknownSubTypeErr, + expPanic: unknownSubTypeErr(4), }, } @@ -965,12 +975,12 @@ func TestOrder_Validate(t *testing.T) { { name: "nil sub-order", Order: NewOrder(1), - exp: []string{nilSubTypeErr}, + exp: []string{nilSubTypeErr(1)}, }, { name: "unknown sub-order type", Order: &Order{OrderId: 1, Order: &unknownOrderType{}}, - exp: []string{unknownSubTypeErr}, + exp: []string{unknownSubTypeErr(1)}, }, { name: "ask order error", From d921806d54375865bd509038c5114ef773c0c503 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 27 Sep 2023 13:52:18 -0600 Subject: [PATCH 183/309] [1658]: Unit tests on GetFulfillmentAssetsAmt and NewPartialFulfillment. --- x/exchange/fulfillment_test.go | 292 +++++++++++++++++++++++++++++++-- 1 file changed, 282 insertions(+), 10 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 3bf44d8a1f..4ca0fd9da6 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -69,7 +69,7 @@ func copyOrderFulfillment(f *OrderFulfillment) *OrderFulfillment { FeesToPay: copyCoins(f.FeesToPay), OrderFeesLeft: copyCoins(f.OrderFeesLeft), PriceFilledAmt: copySDKInt(f.PriceFilledAmt), - PriceUnfilledAmt: copySDKInt(f.PriceFilledAmt), + PriceUnfilledAmt: copySDKInt(f.PriceUnfilledAmt), } } @@ -132,7 +132,7 @@ func TestNewOrderFulfillment(t *testing.T) { { name: "nil sub-order", order: NewOrder(1), - expPanic: "unknown sub-order type : does not implement SubOrderI", + expPanic: nilSubTypeErr(1), }, { name: "ask order", @@ -203,7 +203,6 @@ func TestNewOrderFulfillment(t *testing.T) { if t.Failed() { t.Logf(" Actual: %s", orderFulfillmentString(actual)) t.Logf("Expected: %s", orderFulfillmentString(tc.expected)) - } }() @@ -947,6 +946,8 @@ func assertEqualOrderFulfillments(t *testing.T, expected, actual *OrderFulfillme assert.Equalf(t, expected.OrderFeesLeft, actual.OrderFeesLeft, msg("OrderFulfillment.OrderFeesLeft"), args...) assert.Equalf(t, expected.PriceFilledAmt, actual.PriceFilledAmt, msg("OrderFulfillment.PriceFilledAmt"), args...) assert.Equalf(t, expected.PriceUnfilledAmt, actual.PriceUnfilledAmt, msg("OrderFulfillment.PriceUnfilledAmt"), args...) + t.Logf(" Actual: %s", orderFulfillmentString(actual)) + t.Logf("Expected: %s", orderFulfillmentString(expected)) return false } @@ -1168,8 +1169,6 @@ func TestOrderFulfillment_Apply(t *testing.T) { assertions.AssertErrorValue(t, err, tc.expErr, "Apply error") if !assertEqualOrderFulfillments(t, tc.expResult, tc.receiver, "order fulfillment after .Apply") { t.Logf("Original: %s", orderFulfillmentString(orig)) - t.Logf(" Actual: %s", orderFulfillmentString(tc.receiver)) - t.Logf("Expected: %s", orderFulfillmentString(tc.expResult)) } }) } @@ -1290,9 +1289,7 @@ func TestOrderFulfillment_ApplyLeftoverPrice(t *testing.T) { } assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "ApplyLeftoverPrice") if !assertEqualOrderFulfillments(t, tc.expFulfillment, tc.receiver, "OrderFulfillment after .ApplyLeftoverPrice") { - t.Logf("Original OrderFulfillment: %s", orderFulfillmentString(origFulfillment)) - t.Logf(" Actual OrderFulfillment: %s", orderFulfillmentString(tc.receiver)) - t.Logf("Expected OrderFulfillment: %s", orderFulfillmentString(tc.expFulfillment)) + t.Logf("Original: %s", orderFulfillmentString(origFulfillment)) } if !assert.Equal(t, tc.expAskSplit, tc.askSplit, "askSplit after ApplyLeftoverPrice") { t.Logf("Original askSplit: %s", orderSplitString(origSplit)) @@ -1309,9 +1306,284 @@ func TestOrderFulfillment_ApplyLeftoverPrice(t *testing.T) { // TODO[1658]: func TestFulfill(t *testing.T) -// TODO[1658]: func TestGetFulfillmentAssetsAmt(t *testing.T) +func TestGetFulfillmentAssetsAmt(t *testing.T) { + newAskOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *OrderFulfillment { + return &OrderFulfillment{ + Order: NewOrder(orderID).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin(assetDenom, 999), + }), + AssetsUnfilledAmt: sdkmath.NewInt(assetsUnfilled), + } + } + newBidOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *OrderFulfillment { + return &OrderFulfillment{ + Order: NewOrder(orderID).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin(assetDenom, 999), + }), + AssetsUnfilledAmt: sdkmath.NewInt(assetsUnfilled), + } + } + + cases := []struct { + name string + of1Unfilled int64 + of2Unfilled int64 + expAmt int64 + }{ + {name: "of1 zero", of1Unfilled: 0, of2Unfilled: 3, expAmt: 0}, + {name: "of1 negative", of1Unfilled: -4, of2Unfilled: 3, expAmt: 0}, + {name: "of2 zero", of1Unfilled: 5, of2Unfilled: 0, expAmt: 0}, + {name: "of2 negative", of1Unfilled: 5, of2Unfilled: -6, expAmt: 0}, + {name: "equal", of1Unfilled: 8, of2Unfilled: 8, expAmt: 8}, + {name: "of1 has fewer", of1Unfilled: 9, of2Unfilled: 10, expAmt: 9}, + {name: "of2 has fewer", of1Unfilled: 12, of2Unfilled: 11, expAmt: 11}, + } + + type testCase struct { + name string + of1 *OrderFulfillment + of2 *OrderFulfillment + expAmt sdkmath.Int + expErr string + } + + tests := make([]testCase, 0, len(cases)*4) + + for _, c := range cases { + newTests := []testCase{ + { + name: "ask bid " + c.name, + of1: newAskOF(1, c.of1Unfilled, "one"), + of2: newBidOF(2, c.of2Unfilled, "two"), + expAmt: sdkmath.NewInt(c.expAmt), + }, + { + name: "bid ask " + c.name, + of1: newBidOF(1, c.of1Unfilled, "one"), + of2: newAskOF(2, c.of2Unfilled, "two"), + expAmt: sdkmath.NewInt(c.expAmt), + }, + { + name: "ask ask " + c.name, + of1: newAskOF(1, c.of1Unfilled, "one"), + of2: newAskOF(2, c.of2Unfilled, "two"), + expAmt: sdkmath.NewInt(c.expAmt), + }, + { + name: "bid bid " + c.name, + of1: newBidOF(1, c.of1Unfilled, "one"), + of2: newBidOF(2, c.of2Unfilled, "two"), + expAmt: sdkmath.NewInt(c.expAmt), + }, + } + if c.expAmt == 0 { + newTests[0].expErr = fmt.Sprintf("cannot fill ask order 1 having assets left \"%done\" "+ + "with bid order 2 having assets left \"%dtwo\": zero or negative assets left", + c.of1Unfilled, c.of2Unfilled) + newTests[1].expErr = fmt.Sprintf("cannot fill bid order 1 having assets left \"%done\" "+ + "with ask order 2 having assets left \"%dtwo\": zero or negative assets left", + c.of1Unfilled, c.of2Unfilled) + newTests[2].expErr = fmt.Sprintf("cannot fill ask order 1 having assets left \"%done\" "+ + "with ask order 2 having assets left \"%dtwo\": zero or negative assets left", + c.of1Unfilled, c.of2Unfilled) + newTests[3].expErr = fmt.Sprintf("cannot fill bid order 1 having assets left \"%done\" "+ + "with bid order 2 having assets left \"%dtwo\": zero or negative assets left", + c.of1Unfilled, c.of2Unfilled) + } + tests = append(tests, newTests...) + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if len(tc.expErr) > 0 { + tc.expAmt = sdkmath.ZeroInt() + } + origOF1 := copyOrderFulfillment(tc.of1) + origOF2 := copyOrderFulfillment(tc.of2) + + var amt sdkmath.Int + var err error + testFunc := func() { + amt, err = GetFulfillmentAssetsAmt(tc.of1, tc.of2) + } + require.NotPanics(t, testFunc, "GetFulfillmentAssetsAmt") + assertions.AssertErrorValue(t, err, tc.expErr, "GetFulfillmentAssetsAmt error") + assert.Equal(t, tc.expAmt, amt, "GetFulfillmentAssetsAmt amount") + assertEqualOrderFulfillments(t, origOF1, tc.of1, "of1 after GetFulfillmentAssetsAmt") + assertEqualOrderFulfillments(t, origOF2, tc.of2, "of2 after GetFulfillmentAssetsAmt") + }) + } +} + +func TestNewPartialFulfillment(t *testing.T) { + sdkNewInt64CoinP := func(denom string, amt int64) *sdk.Coin { + rv := sdk.NewInt64Coin(denom, amt) + return &rv + } + + tests := []struct { + name string + f *OrderFulfillment + exp *PartialFulfillment + expPanic string + }{ + { + name: "ask order fees left", + f: &OrderFulfillment{ + Order: NewOrder(54).WithAsk(&AskOrder{ + MarketId: 12, + Seller: "the seller", + Assets: sdk.NewInt64Coin("apple", 1234), + Price: sdk.NewInt64Coin("pear", 9876), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 55), + AllowPartial: true, + }), + AssetsFilledAmt: sdkmath.NewInt(234), + AssetsUnfilledAmt: sdkmath.NewInt(1000), + PriceAppliedAmt: sdkmath.NewInt(10000), + PriceLeftAmt: sdkmath.NewInt(-124), + OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 50)), + PriceFilledAmt: sdkmath.NewInt(876), + PriceUnfilledAmt: sdkmath.NewInt(9000), + }, + exp: &PartialFulfillment{ + NewOrder: NewOrder(54).WithAsk(&AskOrder{ + MarketId: 12, + Seller: "the seller", + Assets: sdk.NewInt64Coin("apple", 1000), + Price: sdk.NewInt64Coin("pear", 9000), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 50), + AllowPartial: true, + }), + AssetsFilled: sdk.NewInt64Coin("apple", 234), + PriceFilled: sdk.NewInt64Coin("pear", 876), + }, + }, + { + name: "ask order no fees left", + f: &OrderFulfillment{ + Order: NewOrder(54).WithAsk(&AskOrder{ + MarketId: 12, + Seller: "the seller", + Assets: sdk.NewInt64Coin("apple", 1234), + Price: sdk.NewInt64Coin("pear", 9876), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 55), + AllowPartial: false, + }), + AssetsFilledAmt: sdkmath.NewInt(234), + AssetsUnfilledAmt: sdkmath.NewInt(1000), + PriceAppliedAmt: sdkmath.NewInt(10000), + PriceLeftAmt: sdkmath.NewInt(-124), + OrderFeesLeft: nil, + PriceFilledAmt: sdkmath.NewInt(876), + PriceUnfilledAmt: sdkmath.NewInt(9000), + }, + exp: &PartialFulfillment{ + NewOrder: NewOrder(54).WithAsk(&AskOrder{ + MarketId: 12, + Seller: "the seller", + Assets: sdk.NewInt64Coin("apple", 1000), + Price: sdk.NewInt64Coin("pear", 9000), + SellerSettlementFlatFee: nil, + AllowPartial: false, + }), + AssetsFilled: sdk.NewInt64Coin("apple", 234), + PriceFilled: sdk.NewInt64Coin("pear", 876), + }, + expPanic: "", + }, + { + name: "ask order multiple fees left", + f: &OrderFulfillment{ + Order: NewOrder(54).WithAsk(&AskOrder{ + MarketId: 12, + Seller: "the seller", + Assets: sdk.NewInt64Coin("apple", 1234), + Price: sdk.NewInt64Coin("pear", 9876), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 55), + AllowPartial: true, + }), + AssetsFilledAmt: sdkmath.NewInt(234), + AssetsUnfilledAmt: sdkmath.NewInt(1000), + PriceAppliedAmt: sdkmath.NewInt(10000), + PriceLeftAmt: sdkmath.NewInt(-124), + OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 50), sdk.NewInt64Coin("grape", 1)), + PriceFilledAmt: sdkmath.NewInt(876), + PriceUnfilledAmt: sdkmath.NewInt(9000), + }, + expPanic: "partially filled ask order 54 somehow has multiple denoms in fees left \"50fig,1grape\"", + }, + { + name: "bid order", + f: &OrderFulfillment{ + Order: NewOrder(54).WithBid(&BidOrder{ + MarketId: 12, + Buyer: "the buyer", + Assets: sdk.NewInt64Coin("apple", 1234), + Price: sdk.NewInt64Coin("pear", 9876), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 55), sdk.NewInt64Coin("grape", 12)), + AllowPartial: true, + }), + AssetsFilledAmt: sdkmath.NewInt(234), + AssetsUnfilledAmt: sdkmath.NewInt(1000), + PriceAppliedAmt: sdkmath.NewInt(9875), + PriceLeftAmt: sdkmath.NewInt(1), + OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 50)), + PriceFilledAmt: sdkmath.NewInt(876), + PriceUnfilledAmt: sdkmath.NewInt(9000), + }, + exp: &PartialFulfillment{ + NewOrder: NewOrder(54).WithBid(&BidOrder{ + MarketId: 12, + Buyer: "the buyer", + Assets: sdk.NewInt64Coin("apple", 1000), + Price: sdk.NewInt64Coin("pear", 9000), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 50)), + AllowPartial: true, + }), + AssetsFilled: sdk.NewInt64Coin("apple", 234), + PriceFilled: sdk.NewInt64Coin("pear", 876), + }, + }, + { + name: "nil order type", + f: &OrderFulfillment{ + Order: NewOrder(57), + AssetsFilledAmt: sdkmath.NewInt(5), + PriceFilledAmt: sdkmath.NewInt(6), + }, + expPanic: nilSubTypeErr(57), + }, + { + name: "unknown order type", + f: &OrderFulfillment{ + Order: &Order{OrderId: 58, Order: &unknownOrderType{}}, + AssetsFilledAmt: sdkmath.NewInt(5), + PriceFilledAmt: sdkmath.NewInt(6), + }, + expPanic: unknownSubTypeErr(58), + }, + // I don't feel like creating a 3rd order type that implements SubOrderI which would be needed in order to + // have a test case reach the final "order %d has unknown type %q" panic at the end of the func. + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + origF := copyOrderFulfillment(tc.f) -// TODO[1658]: func TestNewPartialFulfillment(t *testing.T) + var actual *PartialFulfillment + testFunc := func() { + actual = NewPartialFulfillment(tc.f) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "NewPartialFulfillment") + if !assert.Equal(t, tc.exp, actual, "NewPartialFulfillment result") { + t.Logf(" Actual: %s", partialFulfillmentString(actual)) + t.Logf("Expected: %s", partialFulfillmentString(tc.exp)) + } + assertEqualOrderFulfillments(t, origF, tc.f, "OrderFulfillment after NewPartialFulfillment") + }) + } +} // TODO[1658]: func TestBuildFulfillments(t *testing.T) From d904dcdca04bfef5aa51a1f3d748a2113441306d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 27 Sep 2023 17:27:00 -0600 Subject: [PATCH 184/309] [1658]: Fix a calc in Finalize. --- x/exchange/fulfillment.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 3d268dceae..7bb4a2cc6a 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -277,11 +277,11 @@ func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) (err error) { f.FeesToPay = nil for _, orderFee := range orderFees { feeAssets := orderFee.Amount.Mul(f.AssetsFilledAmt) - feeRem := feeAssets.Mul(orderAssets.Amount) + feeRem := feeAssets.Mod(orderAssets.Amount) if !feeRem.IsZero() { - return fmt.Errorf("%s order %d having settlement fees %q cannot be partially filled by %q: "+ + return fmt.Errorf("%s order %d having assets %q cannot be partially filled by %q: "+ "fee %q is not evenly divisible", - f.GetOrderType(), f.GetOrderID(), orderFees, f.GetAssetsFilled(), orderFee) + f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled(), orderFee) } feeAmtToPay := feeAssets.Quo(orderAssets.Amount) f.FeesToPay = f.FeesToPay.Add(sdk.NewCoin(orderFee.Denom, feeAmtToPay)) From 1ef408407a7b266c33461e2f9a530c109d87ff9d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 27 Sep 2023 17:27:25 -0600 Subject: [PATCH 185/309] [1658]: Most of the unit tests on Finalize. --- x/exchange/fulfillment_test.go | 564 ++++++++++++++++++++++++++++++++- 1 file changed, 563 insertions(+), 1 deletion(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 4ca0fd9da6..8fb2b53bd4 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -1300,7 +1300,569 @@ func TestOrderFulfillment_ApplyLeftoverPrice(t *testing.T) { } } -// TODO[1658]: func TestOrderFulfillment_Finalize(t *testing.T) +func TestOrderFulfillment_Finalize(t *testing.T) { + sdkNewInt64CoinP := func(denom string, amount int64) *sdk.Coin { + rv := sdk.NewInt64Coin(denom, amount) + return &rv + } + + tests := []struct { + name string + receiver *OrderFulfillment + sellerFeeRatio *FeeRatio + expResult *OrderFulfillment + expErr string + }{ + { + name: "ask assets filled zero", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{}), + AssetsFilledAmt: sdkmath.ZeroInt(), + }, + expErr: "no assets filled in ask order 3", + }, + { + name: "ask assets filled negative", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{}), + AssetsFilledAmt: sdkmath.NewInt(-8), + }, + expErr: "no assets filled in ask order 3", + }, + { + name: "bid assets filled zero", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithBid(&BidOrder{}), + AssetsFilledAmt: sdkmath.ZeroInt(), + }, + expErr: "no assets filled in bid order 3", + }, + { + name: "bid assets filled negative", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithBid(&BidOrder{}), + AssetsFilledAmt: sdkmath.NewInt(-8), + }, + expErr: "no assets filled in bid order 3", + }, + + { + name: "ask partial price not evenly divisible", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 101), + }), + AssetsFilledAmt: sdkmath.NewInt(10), + AssetsUnfilledAmt: sdkmath.NewInt(40), + }, + expErr: `ask order 3 having assets "50apple" cannot be partially filled by "10apple": price "101pear" is not evenly divisible`, + }, + { + name: "bid partial price not evenly divisible", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 101), + }), + AssetsFilledAmt: sdkmath.NewInt(10), + AssetsUnfilledAmt: sdkmath.NewInt(40), + }, + expErr: `bid order 3 having assets "50apple" cannot be partially filled by "10apple": price "101pear" is not evenly divisible`, + }, + + { + name: "ask partial fees not evenly divisible", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 201), + }), + AssetsFilledAmt: sdkmath.NewInt(10), + AssetsUnfilledAmt: sdkmath.NewInt(40), + }, + expErr: `ask order 3 having assets "50apple" cannot be partially filled by "10apple": fee "201fig" is not evenly divisible`, + }, + { + name: "bid partial fees not evenly divisible", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 151)), + }), + AssetsFilledAmt: sdkmath.NewInt(10), + AssetsUnfilledAmt: sdkmath.NewInt(40), + }, + expErr: `bid order 3 having assets "50apple" cannot be partially filled by "10apple": fee "151grape" is not evenly divisible`, + }, + + { + name: "ask ratio calc error", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(10), + AssetsUnfilledAmt: sdkmath.NewInt(40), + PriceAppliedAmt: sdkmath.NewInt(29), + PriceLeftAmt: sdkmath.NewInt(71), + }, + sellerFeeRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("plum", 1), + Fee: sdk.NewInt64Coin("fig", 3), + }, + expErr: "could not calculate ask order 3 ratio fee: cannot apply ratio 1plum:3fig to price 29pear: incorrect price denom", + }, + + { + name: "ask full no ratio", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(0), + }, + sellerFeeRatio: nil, + expResult: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(0), + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(100), + PriceUnfilledAmt: sdkmath.ZeroInt(), + FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200)), + OrderFeesLeft: nil, + }, + }, + { + name: "ask full, exact ratio", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(110), + PriceLeftAmt: sdkmath.NewInt(-10), + }, + sellerFeeRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("pear", 10), + Fee: sdk.NewInt64Coin("grape", 1), + }, + expResult: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(110), + PriceLeftAmt: sdkmath.NewInt(-10), + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(100), + PriceUnfilledAmt: sdkmath.ZeroInt(), + FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 11)), + OrderFeesLeft: nil, + }, + }, + { + name: "ask full, loose ratio", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(110), + PriceLeftAmt: sdkmath.NewInt(-10), + }, + sellerFeeRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("pear", 13), + Fee: sdk.NewInt64Coin("grape", 1), + }, + expResult: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(110), + PriceLeftAmt: sdkmath.NewInt(-10), + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(100), + PriceUnfilledAmt: sdkmath.ZeroInt(), + FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 9)), + OrderFeesLeft: nil, + }, + }, + { + name: "ask partial no ratio", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(30), + }, + sellerFeeRatio: nil, + expResult: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(30), + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(40), + PriceUnfilledAmt: sdkmath.NewInt(60), + FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 80)), + OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 120)), + }, + }, + { + name: "ask partial, exact ratio", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(30), + PriceAppliedAmt: sdkmath.NewInt(110), + PriceLeftAmt: sdkmath.NewInt(-10), + }, + sellerFeeRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("pear", 10), + Fee: sdk.NewInt64Coin("grape", 1), + }, + expResult: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(30), + PriceAppliedAmt: sdkmath.NewInt(110), + PriceLeftAmt: sdkmath.NewInt(-10), + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(40), + PriceUnfilledAmt: sdkmath.NewInt(60), + FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 80), sdk.NewInt64Coin("grape", 11)), + OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 120)), + }, + }, + { + name: "ask partial, loose ratio", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(30), + PriceAppliedAmt: sdkmath.NewInt(110), + PriceLeftAmt: sdkmath.NewInt(-10), + }, + sellerFeeRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("pear", 13), + Fee: sdk.NewInt64Coin("fig", 1), + }, + expResult: &OrderFulfillment{ + Order: NewOrder(3).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(30), + PriceAppliedAmt: sdkmath.NewInt(110), + PriceLeftAmt: sdkmath.NewInt(-10), + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(40), + PriceUnfilledAmt: sdkmath.NewInt(60), + FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 89)), + OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 120)), + }, + }, + + { + name: "bid full no leftovers", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 13)), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: sdkmath.NewInt(0), + }, + expResult: &OrderFulfillment{ + Order: NewOrder(3).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 100), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 13)), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: sdkmath.NewInt(0), + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(100), + PriceUnfilledAmt: sdkmath.NewInt(0), + FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 13)), + OrderFeesLeft: nil, + }, + }, + { + name: "bid full with leftovers", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 1000), // 1000 / 50 = 20 per asset. + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 13)), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(993), + PriceLeftAmt: sdkmath.NewInt(7), + Splits: []*OrderSplit{ + { + // This one will get 1 once the loop defaults to 1. + // So, 7 * split assets / 50 filled must be 0. + Order: &OrderFulfillment{ + Order: NewOrder(101).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 5), + Price: sdk.NewInt64Coin("pear", 100), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 5), + Price: sdk.NewInt64Coin("pear", 100), + }}, + AssetsFilledAmt: sdkmath.NewInt(5), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: sdkmath.NewInt(0), + }, + Assets: sdk.NewInt64Coin("apple", 5), + Price: sdk.NewInt64Coin("pear", 100), + }, + { + // This one will not get anything more. + // It's in the same situation as the one above, but the leftover will run out first. + Order: &OrderFulfillment{ + Order: NewOrder(102).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 4), + Price: sdk.NewInt64Coin("pear", 80), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 4), + Price: sdk.NewInt64Coin("pear", 80), + }}, + AssetsFilledAmt: sdkmath.NewInt(4), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceLeftAmt: sdkmath.NewInt(0), + }, + Assets: sdk.NewInt64Coin("apple", 4), + Price: sdk.NewInt64Coin("pear", 80), + }, + { + // This one will get 4 in the first pass of the loop. + // I.e. 7 * split assets / 50 = 4. Assets 29 to 39 + Order: &OrderFulfillment{ + Order: NewOrder(103).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 35), + Price: sdk.NewInt64Coin("pear", 693), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 35), + Price: sdk.NewInt64Coin("pear", 693), + }}, + AssetsFilledAmt: sdkmath.NewInt(35), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(693), + PriceLeftAmt: sdkmath.NewInt(0), + }, + Assets: sdk.NewInt64Coin("apple", 35), + Price: sdk.NewInt64Coin("pear", 693), + }, + { + // This one will get 2 due to price left. + // I also need this one to have 7 * assets / 50 = 0, so it doesn't get 1 more on the first pass. + Order: &OrderFulfillment{ + Order: NewOrder(104).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 6), + Price: sdk.NewInt64Coin("pear", 122), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 6), + Price: sdk.NewInt64Coin("pear", 120), + }}, + AssetsFilledAmt: sdkmath.NewInt(6), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(120), + PriceLeftAmt: sdkmath.NewInt(2), + }, + Assets: sdk.NewInt64Coin("apple", 6), + Price: sdk.NewInt64Coin("pear", 120), + }, + }, + }, + expResult: &OrderFulfillment{ + Order: NewOrder(3).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("pear", 1000), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 13)), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(1000), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{ + { + // This one should get 1 once the loop defaults to 1. + Order: &OrderFulfillment{ + Order: NewOrder(101).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 5), + Price: sdk.NewInt64Coin("pear", 100), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 5), + Price: sdk.NewInt64Coin("pear", 101), + }}, + AssetsFilledAmt: sdkmath.NewInt(5), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(101), + PriceLeftAmt: sdkmath.NewInt(-1), + }, + Assets: sdk.NewInt64Coin("apple", 5), + Price: sdk.NewInt64Coin("pear", 101), + }, + { + // This one will not get anything more. + Order: &OrderFulfillment{ + Order: NewOrder(102).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 4), + Price: sdk.NewInt64Coin("pear", 80), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 4), + Price: sdk.NewInt64Coin("pear", 80), + }}, + AssetsFilledAmt: sdkmath.NewInt(4), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceLeftAmt: sdkmath.NewInt(0), + }, + Assets: sdk.NewInt64Coin("apple", 4), + Price: sdk.NewInt64Coin("pear", 80), + }, + { + // This one should get 4 in the first pass of the loop. + Order: &OrderFulfillment{ + Order: NewOrder(103).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 35), + Price: sdk.NewInt64Coin("pear", 693), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 35), + Price: sdk.NewInt64Coin("pear", 697), + }}, + AssetsFilledAmt: sdkmath.NewInt(35), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(697), + PriceLeftAmt: sdkmath.NewInt(-4), + }, + Assets: sdk.NewInt64Coin("apple", 35), + Price: sdk.NewInt64Coin("pear", 697), + }, + { + // this one will get 2 due to price left. + // I also need this one to have 7 * assets / 50 = 0, so it doesn't get 1 more on the first pass. + Order: &OrderFulfillment{ + Order: NewOrder(104).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 6), + Price: sdk.NewInt64Coin("pear", 122), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 6), + Price: sdk.NewInt64Coin("pear", 122), + }}, + AssetsFilledAmt: sdkmath.NewInt(6), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(122), + PriceLeftAmt: ZeroAmtAfterSub, + }, + Assets: sdk.NewInt64Coin("apple", 6), + Price: sdk.NewInt64Coin("pear", 122), + }, + }, + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(1000), + PriceUnfilledAmt: sdkmath.NewInt(0), + FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 13)), + OrderFeesLeft: nil, + }, + }, + // TODO[1658]: bid partial no leftovers + // TODO[1658]: bid partial with leftovers + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + orig := copyOrderFulfillment(tc.receiver) + if tc.expResult == nil { + tc.expResult = copyOrderFulfillment(tc.receiver) + tc.expResult.PriceFilledAmt = sdkmath.ZeroInt() + tc.expResult.PriceUnfilledAmt = sdkmath.ZeroInt() + } + + var err error + testFunc := func() { + err = tc.receiver.Finalize(tc.sellerFeeRatio) + } + require.NotPanics(t, testFunc, "Finalize") + assertions.AssertErrorValue(t, err, tc.expErr, "Finalize error") + if !assertEqualOrderFulfillments(t, tc.expResult, tc.receiver, "receiver after Finalize") { + t.Logf("Original: %s", orderFulfillmentString(orig)) + } + }) + } +} // TODO[1658]: func TestOrderFulfillment_Validate(t *testing.T) From 3c75dfeae76575d4d145abccce6a6e6118c48fba Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 28 Sep 2023 10:09:18 -0600 Subject: [PATCH 186/309] [1658]: The last of the Finalize unit tests. --- x/exchange/fulfillment_test.go | 231 ++++++++++++++++++++++++++++++++- 1 file changed, 229 insertions(+), 2 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 8fb2b53bd4..b49e9e7b89 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -1838,8 +1838,235 @@ func TestOrderFulfillment_Finalize(t *testing.T) { OrderFeesLeft: nil, }, }, - // TODO[1658]: bid partial no leftovers - // TODO[1658]: bid partial with leftovers + { + name: "bid partial no leftovers", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 75), + Price: sdk.NewInt64Coin("pear", 150), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 300), sdk.NewInt64Coin("grape", 12)), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(25), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: sdkmath.NewInt(50), + }, + expResult: &OrderFulfillment{ + Order: NewOrder(3).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 75), + Price: sdk.NewInt64Coin("pear", 150), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 300), sdk.NewInt64Coin("grape", 12)), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(25), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: sdkmath.NewInt(50), + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(100), + PriceUnfilledAmt: sdkmath.NewInt(50), + FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 8)), + OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 100), sdk.NewInt64Coin("grape", 4)), + }, + }, + { + name: "bid partial with leftovers", + receiver: &OrderFulfillment{ + Order: NewOrder(3).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 75), + Price: sdk.NewInt64Coin("pear", 1500), // 1020 / 51 = 20 per asset. + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 300), sdk.NewInt64Coin("grape", 12)), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(25), + PriceAppliedAmt: sdkmath.NewInt(993), + PriceLeftAmt: sdkmath.NewInt(507), + Splits: []*OrderSplit{ + { + // This one will get 1 once the loop defaults to 1. + // So, 7 * split assets / 50 filled must be 0. + Order: &OrderFulfillment{ + Order: NewOrder(101).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 5), + Price: sdk.NewInt64Coin("pear", 100), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 5), + Price: sdk.NewInt64Coin("pear", 100), + }}, + AssetsFilledAmt: sdkmath.NewInt(5), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: sdkmath.NewInt(0), + }, + Assets: sdk.NewInt64Coin("apple", 5), + Price: sdk.NewInt64Coin("pear", 100), + }, + { + // This one will not get anything more. + // It's in the same situation as the one above, but the leftover will run out first. + Order: &OrderFulfillment{ + Order: NewOrder(102).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 4), + Price: sdk.NewInt64Coin("pear", 80), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 4), + Price: sdk.NewInt64Coin("pear", 80), + }}, + AssetsFilledAmt: sdkmath.NewInt(4), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceLeftAmt: sdkmath.NewInt(0), + }, + Assets: sdk.NewInt64Coin("apple", 4), + Price: sdk.NewInt64Coin("pear", 80), + }, + { + // This one will get 4 in the first pass of the loop. + // I.e. 7 * split assets / 50 = 4. Assets 29 to 39 + Order: &OrderFulfillment{ + Order: NewOrder(103).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 35), + Price: sdk.NewInt64Coin("pear", 693), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 35), + Price: sdk.NewInt64Coin("pear", 693), + }}, + AssetsFilledAmt: sdkmath.NewInt(35), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(693), + PriceLeftAmt: sdkmath.NewInt(0), + }, + Assets: sdk.NewInt64Coin("apple", 35), + Price: sdk.NewInt64Coin("pear", 693), + }, + { + // This one will get 2 due to price left. + // I also need this one to have 7 * assets / 50 = 0, so it doesn't get 1 more on the first pass. + Order: &OrderFulfillment{ + Order: NewOrder(104).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 6), + Price: sdk.NewInt64Coin("pear", 122), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 6), + Price: sdk.NewInt64Coin("pear", 120), + }}, + AssetsFilledAmt: sdkmath.NewInt(6), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(120), + PriceLeftAmt: sdkmath.NewInt(2), + }, + Assets: sdk.NewInt64Coin("apple", 6), + Price: sdk.NewInt64Coin("pear", 120), + }, + }, + }, + expResult: &OrderFulfillment{ + Order: NewOrder(3).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 75), + Price: sdk.NewInt64Coin("pear", 1500), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 300), sdk.NewInt64Coin("grape", 12)), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(25), + PriceAppliedAmt: sdkmath.NewInt(1000), + PriceLeftAmt: sdkmath.NewInt(500), + Splits: []*OrderSplit{ + { + // This one should get 1 once the loop defaults to 1. + Order: &OrderFulfillment{ + Order: NewOrder(101).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 5), + Price: sdk.NewInt64Coin("pear", 100), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 5), + Price: sdk.NewInt64Coin("pear", 101), + }}, + AssetsFilledAmt: sdkmath.NewInt(5), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(101), + PriceLeftAmt: sdkmath.NewInt(-1), + }, + Assets: sdk.NewInt64Coin("apple", 5), + Price: sdk.NewInt64Coin("pear", 101), + }, + { + // This one will not get anything more. + Order: &OrderFulfillment{ + Order: NewOrder(102).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 4), + Price: sdk.NewInt64Coin("pear", 80), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 4), + Price: sdk.NewInt64Coin("pear", 80), + }}, + AssetsFilledAmt: sdkmath.NewInt(4), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceLeftAmt: sdkmath.NewInt(0), + }, + Assets: sdk.NewInt64Coin("apple", 4), + Price: sdk.NewInt64Coin("pear", 80), + }, + { + // This one should get 4 in the first pass of the loop. + Order: &OrderFulfillment{ + Order: NewOrder(103).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 35), + Price: sdk.NewInt64Coin("pear", 693), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 35), + Price: sdk.NewInt64Coin("pear", 697), + }}, + AssetsFilledAmt: sdkmath.NewInt(35), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(697), + PriceLeftAmt: sdkmath.NewInt(-4), + }, + Assets: sdk.NewInt64Coin("apple", 35), + Price: sdk.NewInt64Coin("pear", 697), + }, + { + // this one will get 2 due to price left. + // I also need this one to have 7 * assets / 50 = 0, so it doesn't get 1 more on the first pass. + Order: &OrderFulfillment{ + Order: NewOrder(104).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 6), + Price: sdk.NewInt64Coin("pear", 122), + }), + Splits: []*OrderSplit{{ + Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + Assets: sdk.NewInt64Coin("apple", 6), + Price: sdk.NewInt64Coin("pear", 122), + }}, + AssetsFilledAmt: sdkmath.NewInt(6), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(122), + PriceLeftAmt: ZeroAmtAfterSub, + }, + Assets: sdk.NewInt64Coin("apple", 6), + Price: sdk.NewInt64Coin("pear", 122), + }, + }, + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(1000), + PriceUnfilledAmt: sdkmath.NewInt(500), + FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 8)), + OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 100), sdk.NewInt64Coin("grape", 4)), + }, + }, } for _, tc := range tests { From 51e0e0326e999e41c16092c9994fcd3da51e106d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 28 Sep 2023 11:02:14 -0600 Subject: [PATCH 187/309] [1658]: Create newUnknownOrder and use that instead of doing it the hard way everywhere. --- x/exchange/orders_test.go | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 3812156155..dc763d7a05 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -393,6 +393,11 @@ func (o *unknownOrderType) Size() int { return 0 } +// newUnknownOrder returns a new order with the given id and an unknownOrderType. +func newUnknownOrder(orderID uint64) *Order { + return &Order{OrderId: orderID, Order: &unknownOrderType{}} +} + func TestOrder_IsAskOrder(t *testing.T) { tests := []struct { name string @@ -416,7 +421,7 @@ func TestOrder_IsAskOrder(t *testing.T) { }, { name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + order: newUnknownOrder(4), exp: false, }, } @@ -456,7 +461,7 @@ func TestOrder_IsBidOrder(t *testing.T) { }, { name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + order: newUnknownOrder(4), exp: false, }, } @@ -554,7 +559,7 @@ func TestOrder_GetSubOrder(t *testing.T) { }, { name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + order: newUnknownOrder(4), expErr: unknownSubTypeErr(4), }, } @@ -604,7 +609,7 @@ func TestOrder_GetMarketID(t *testing.T) { }, { name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + order: newUnknownOrder(4), expPanic: unknownSubTypeErr(4), }, } @@ -645,7 +650,7 @@ func TestOrder_GetOwner(t *testing.T) { }, { name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + order: newUnknownOrder(4), expPanic: unknownSubTypeErr(4), }, } @@ -686,7 +691,7 @@ func TestOrder_GetAssets(t *testing.T) { }, { name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + order: newUnknownOrder(4), expPanic: unknownSubTypeErr(4), }, } @@ -727,7 +732,7 @@ func TestOrder_GetPrice(t *testing.T) { }, { name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + order: newUnknownOrder(4), expPanic: unknownSubTypeErr(4), }, } @@ -768,7 +773,7 @@ func TestOrder_GetSettlementFees(t *testing.T) { }, { name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + order: newUnknownOrder(4), expPanic: unknownSubTypeErr(4), }, } @@ -809,7 +814,7 @@ func TestOrder_PartialFillAllowed(t *testing.T) { }, { name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + order: newUnknownOrder(4), expPanic: unknownSubTypeErr(4), }, } @@ -849,7 +854,7 @@ func TestOrder_GetOrderType(t *testing.T) { }, { name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + order: newUnknownOrder(4), expected: "*exchange.unknownOrderType", }, } @@ -890,7 +895,7 @@ func TestOrder_GetOrderTypeByte(t *testing.T) { }, { name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + order: newUnknownOrder(4), expPanic: unknownSubTypeErr(4), }, } @@ -941,7 +946,7 @@ func TestOrder_GetHoldAmount(t *testing.T) { }, { name: "unknown order type", - order: &Order{OrderId: 4, Order: &unknownOrderType{}}, + order: newUnknownOrder(4), expPanic: unknownSubTypeErr(4), }, } @@ -979,8 +984,8 @@ func TestOrder_Validate(t *testing.T) { }, { name: "unknown sub-order type", - Order: &Order{OrderId: 1, Order: &unknownOrderType{}}, - exp: []string{unknownSubTypeErr(1)}, + Order: newUnknownOrder(3), + exp: []string{unknownSubTypeErr(3)}, }, { name: "ask order error", From 08c48d45d1d863436bca93707160907eace3f674 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 28 Sep 2023 11:07:09 -0600 Subject: [PATCH 188/309] [1658]: Tweak OrderFulfillment.Validate: Check the sub-order first. Reorder the checks so they're grouped better. Make sure price left is not the order price. Make sure fees left does not have any negatives. Panic if the switch is missing an order type case. --- x/exchange/fulfillment.go | 63 ++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 7bb4a2cc6a..2f6bbb9242 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -353,23 +353,14 @@ func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) (err error) { // Validate does some final validation and sanity checking on this order fulfillment. // It's assumed that Finalize has been called before calling this. func (f OrderFulfillment) Validate() error { + if _, err := f.Order.GetSubOrder(); err != nil { + return err + } if !f.IsFinalized { return fmt.Errorf("fulfillment for %s order %d has not been finalized", f.GetOrderType(), f.GetOrderID()) } - if f.PriceAppliedAmt.IsZero() { - return fmt.Errorf("no price applied to %s order %d", f.GetOrderType(), f.GetOrderID()) - } - if len(f.Splits) == 0 { - return fmt.Errorf("no splits applied to %s order %d", f.GetOrderType(), f.GetOrderID()) - } - orderAssets := f.GetAssets() - trackedAssetsAmt := f.AssetsFilledAmt.Add(f.AssetsUnfilledAmt) - if !orderAssets.Amount.Equal(trackedAssetsAmt) { - return fmt.Errorf("tracked assets %q does not equal %s order %d assets %q", - sdk.Coin{Denom: orderAssets.Denom, Amount: trackedAssetsAmt}, f.GetOrderType(), f.GetOrderID(), orderAssets) - } if f.AssetsUnfilledAmt.IsNegative() { return fmt.Errorf("%s order %d having assets %q has negative assets left %q after filling %q", f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsUnfilled(), f.GetAssetsFilled()) @@ -378,21 +369,25 @@ func (f OrderFulfillment) Validate() error { return fmt.Errorf("cannot fill non-positive assets %q on %s order %d having assets %q", f.GetAssetsFilled(), f.GetOrderType(), f.GetOrderID(), orderAssets) } + trackedAssetsAmt := f.AssetsFilledAmt.Add(f.AssetsUnfilledAmt) + if !orderAssets.Amount.Equal(trackedAssetsAmt) { + return fmt.Errorf("tracked assets %q does not equal %s order %d assets %q", + sdk.Coin{Denom: orderAssets.Denom, Amount: trackedAssetsAmt}, f.GetOrderType(), f.GetOrderID(), orderAssets) + } orderPrice := f.GetPrice() - trackedPriceAmt := f.PriceAppliedAmt.Add(f.PriceLeftAmt) - if !orderPrice.Amount.Equal(trackedPriceAmt) { - return fmt.Errorf("tracked price %q does not equal %s order %d price %q", - sdk.Coin{Denom: orderPrice.Denom, Amount: trackedPriceAmt}, f.GetOrderType(), f.GetOrderID(), orderPrice) + if f.PriceLeftAmt.Equal(orderPrice.Amount) { + return fmt.Errorf("price left %q equals %s order %d price %q", + f.GetPriceLeft(), f.GetOrderType(), f.GetOrderID(), orderPrice) } if !f.PriceAppliedAmt.IsPositive() { return fmt.Errorf("cannot apply non-positive price %q to %s order %d having price %q", f.GetPriceApplied(), f.GetOrderType(), f.GetOrderID(), orderPrice) } - totalPriceAmt := f.PriceFilledAmt.Add(f.PriceUnfilledAmt) - if !orderPrice.Amount.Equal(totalPriceAmt) { - return fmt.Errorf("filled price %q plus unfilled price %q does not equal order price %q for %s order %d", - f.GetPriceFilled(), f.GetPriceUnfilled(), orderPrice, f.GetOrderType(), f.GetOrderID()) + trackedPriceAmt := f.PriceAppliedAmt.Add(f.PriceLeftAmt) + if !orderPrice.Amount.Equal(trackedPriceAmt) { + return fmt.Errorf("tracked price %q does not equal %s order %d price %q", + sdk.Coin{Denom: orderPrice.Denom, Amount: trackedPriceAmt}, f.GetOrderType(), f.GetOrderID(), orderPrice) } if f.PriceUnfilledAmt.IsNegative() { return fmt.Errorf("%s order %d having price %q has negative price %q after filling %q", @@ -402,6 +397,15 @@ func (f OrderFulfillment) Validate() error { return fmt.Errorf("cannot fill %s order %d having price %q with non-positive price %q", f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceFilled()) } + totalPriceAmt := f.PriceFilledAmt.Add(f.PriceUnfilledAmt) + if !orderPrice.Amount.Equal(totalPriceAmt) { + return fmt.Errorf("filled price %q plus unfilled price %q does not equal order price %q for %s order %d", + f.GetPriceFilled(), f.GetPriceUnfilled(), orderPrice, f.GetOrderType(), f.GetOrderID()) + } + + if len(f.Splits) == 0 { + return fmt.Errorf("no splits applied to %s order %d", f.GetOrderType(), f.GetOrderID()) + } var splitsAssets, splitsPrice sdk.Coins for _, split := range f.Splits { @@ -435,6 +439,16 @@ func (f OrderFulfillment) Validate() error { splitsPrice, f.GetPriceApplied(), f.GetOrderType(), f.GetOrderID()) } + orderFees := f.GetSettlementFees() + if f.OrderFeesLeft.IsAnyNegative() { + return fmt.Errorf("%s order %d settlement fees left %q is negative", + f.GetOrderType(), f.GetOrderID(), f.OrderFeesLeft) + } + if _, hasNeg := orderFees.SafeSub(f.OrderFeesLeft...); hasNeg { + return fmt.Errorf("settlement fees left %q is greater than %s order %q settlement fees %q", + f.OrderFeesLeft, f.GetOrderType(), f.GetOrderID(), orderFees) + } + isFullyFilled := f.IsFullyFilled() if isFullyFilled { if !f.AssetsUnfilledAmt.IsZero() { @@ -451,12 +465,6 @@ func (f OrderFulfillment) Validate() error { } } - orderFees := f.GetSettlementFees() - if _, hasNeg := orderFees.SafeSub(f.OrderFeesLeft...); hasNeg { - return fmt.Errorf("settlement fees left %q is greater than %s order %q settlement fees %q", - f.OrderFeesLeft, f.GetOrderType(), f.GetOrderID(), orderFees) - } - switch { case f.IsAskOrder(): // For ask orders, the applied amount needs to be at least the filled amount. @@ -488,7 +496,8 @@ func (f OrderFulfillment) Validate() error { trackedFees, f.GetOrderType(), f.GetOrderID(), orderFees) } default: - return fmt.Errorf("order %d has unknown type %s", f.GetOrderID(), f.GetOrderType()) + // The only way to trigger this would be to add a new order type but not add a case for it in this switch. + panic(fmt.Errorf("case missing for %T in Validate", f.GetOrderType())) } // Saving this simple check for last in the hopes that a previous error exposes why this From 4796a4d520f301c9e6e38f09dedaa884df919cc3 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 28 Sep 2023 14:05:48 -0600 Subject: [PATCH 189/309] [1658]: Further refinements to OrderFulfillment.Validate as I work through unit tests. --- x/exchange/fulfillment.go | 77 +++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 2f6bbb9242..21273002f3 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -376,8 +376,8 @@ func (f OrderFulfillment) Validate() error { } orderPrice := f.GetPrice() - if f.PriceLeftAmt.Equal(orderPrice.Amount) { - return fmt.Errorf("price left %q equals %s order %d price %q", + if f.PriceLeftAmt.GTE(orderPrice.Amount) { + return fmt.Errorf("price left %q is not less than %s order %d price %q", f.GetPriceLeft(), f.GetOrderType(), f.GetOrderID(), orderPrice) } if !f.PriceAppliedAmt.IsPositive() { @@ -389,10 +389,6 @@ func (f OrderFulfillment) Validate() error { return fmt.Errorf("tracked price %q does not equal %s order %d price %q", sdk.Coin{Denom: orderPrice.Denom, Amount: trackedPriceAmt}, f.GetOrderType(), f.GetOrderID(), orderPrice) } - if f.PriceUnfilledAmt.IsNegative() { - return fmt.Errorf("%s order %d having price %q has negative price %q after filling %q", - f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceUnfilled(), f.GetPriceFilled()) - } if !f.PriceFilledAmt.IsPositive() { return fmt.Errorf("cannot fill %s order %d having price %q with non-positive price %q", f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceFilled()) @@ -402,6 +398,10 @@ func (f OrderFulfillment) Validate() error { return fmt.Errorf("filled price %q plus unfilled price %q does not equal order price %q for %s order %d", f.GetPriceFilled(), f.GetPriceUnfilled(), orderPrice, f.GetOrderType(), f.GetOrderID()) } + if f.PriceUnfilledAmt.IsNegative() { + return fmt.Errorf("%s order %d having price %q has negative price %q after filling %q", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceUnfilled(), f.GetPriceFilled()) + } if len(f.Splits) == 0 { return fmt.Errorf("no splits applied to %s order %d", f.GetOrderType(), f.GetOrderID()) @@ -414,53 +414,53 @@ func (f OrderFulfillment) Validate() error { } if len(splitsAssets) != 1 { - return fmt.Errorf("multiple asset denoms %q in splits applied to %s order %d", - splitsAssets, f.GetOrderType(), f.GetOrderID()) + return fmt.Errorf("multiple asset denoms %q in splits applied to %s order %d having assets %q", + splitsAssets, f.GetOrderType(), f.GetOrderID(), orderAssets) } if splitsAssets[0].Denom != orderAssets.Denom { - return fmt.Errorf("splits asset denom %q does not equal order assets %q on %s order %d", + return fmt.Errorf("splits asset denom %q does not equal order assets denom %q on %s order %d", splitsAssets, orderAssets, f.GetOrderType(), f.GetOrderID()) } if !splitsAssets[0].Amount.Equal(f.AssetsFilledAmt) { return fmt.Errorf("splits asset total %q does not equal filled assets %q on %s order %d", - splitsAssets, orderAssets, f.GetOrderType(), f.GetOrderID()) + splitsAssets, f.GetAssetsFilled(), f.GetOrderType(), f.GetOrderID()) } if len(splitsPrice) != 1 { - return fmt.Errorf("multiple price denoms %q in splits applied to %s order %d", - splitsPrice, f.GetOrderType(), f.GetOrderID()) + return fmt.Errorf("multiple price denoms %q in splits applied to %s order %d having price %q", + splitsPrice, f.GetOrderType(), f.GetOrderID(), orderPrice) } if splitsPrice[0].Denom != orderPrice.Denom { - return fmt.Errorf("splits price denom %q does not equal order price %q on %s order %d", + return fmt.Errorf("splits price denom %q does not equal order price denom %q on %s order %d", splitsPrice, orderPrice, f.GetOrderType(), f.GetOrderID()) } - if !splitsPrice[0].Amount.Equal(f.PriceFilledAmt) { - return fmt.Errorf("splits price total %q does not equal applied price %q on %s order %d", + if !splitsPrice[0].Amount.Equal(f.PriceAppliedAmt) { + return fmt.Errorf("splits price total %q does not equal filled price %q on %s order %d", splitsPrice, f.GetPriceApplied(), f.GetOrderType(), f.GetOrderID()) } orderFees := f.GetSettlementFees() if f.OrderFeesLeft.IsAnyNegative() { - return fmt.Errorf("%s order %d settlement fees left %q is negative", - f.GetOrderType(), f.GetOrderID(), f.OrderFeesLeft) + return fmt.Errorf("settlement fees left %q is negative for %s order %d having fees %q", + f.OrderFeesLeft, f.GetOrderType(), f.GetOrderID(), orderFees) } if _, hasNeg := orderFees.SafeSub(f.OrderFeesLeft...); hasNeg { - return fmt.Errorf("settlement fees left %q is greater than %s order %q settlement fees %q", + return fmt.Errorf("settlement fees left %q is greater than %s order %d settlement fees %q", f.OrderFeesLeft, f.GetOrderType(), f.GetOrderID(), orderFees) } isFullyFilled := f.IsFullyFilled() if isFullyFilled { - if !f.AssetsUnfilledAmt.IsZero() { - return fmt.Errorf("fully filled %s order %q has non-zero unfilled assets %q", - f.GetOrderType(), f.GetOrderID(), f.GetAssetsUnfilled()) - } + // IsFullyFilled returns true if unfilled assets is zero or negative. + // We know from a previous check that unfilled is not negative. + // So here, we know it's zero, and don't need to check that again. + if !f.PriceUnfilledAmt.IsZero() { - return fmt.Errorf("fully filled %s order %q has non-zero unfilled price %q", + return fmt.Errorf("fully filled %s order %d has non-zero unfilled price %q", f.GetOrderType(), f.GetOrderID(), f.GetPriceUnfilled()) } if !f.OrderFeesLeft.IsZero() { - return fmt.Errorf("fully filled %s order %q has non-zero settlement fees left %q", + return fmt.Errorf("fully filled %s order %d has non-zero settlement fees left %q", f.GetOrderType(), f.GetOrderID(), f.OrderFeesLeft) } } @@ -468,27 +468,34 @@ func (f OrderFulfillment) Validate() error { switch { case f.IsAskOrder(): // For ask orders, the applied amount needs to be at least the filled amount. - if f.PriceFilledAmt.LT(f.PriceAppliedAmt) { + if f.PriceAppliedAmt.LT(f.PriceFilledAmt) { return fmt.Errorf("%s order %d having assets %q and price %q cannot be filled by %q at price %q: unsufficient price", f.GetOrderType(), f.GetOrderID(), orderAssets, orderPrice, f.GetAssetsFilled(), f.GetPriceApplied()) } + // If not being fully filled on an order that has some fees, make sure that there's at most 1 denom in the fees left. if !isFullyFilled && len(orderFees) > 0 && len(f.OrderFeesLeft) > 1 { return fmt.Errorf("partial fulfillment for %s order %d having seller settlement fees %q has multiple denoms in fees left %q", f.GetOrderType(), f.GetOrderID(), orderFees, f.OrderFeesLeft) } - case f.IsBidOrder(): - // If filled in full, the PriceAppliedAmt must be equal to the order price. - if isFullyFilled && !f.PriceAppliedAmt.Equal(orderPrice.Amount) { - return fmt.Errorf("%s order %d having price %q cannot be fully filled at price %q: price mismatch", - f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceApplied()) + + // For ask orders, the tracked fees must be at least the order fees. + trackedFees := f.FeesToPay.Add(f.OrderFeesLeft...) + if _, hasNeg := trackedFees.SafeSub(orderFees...); hasNeg { + return fmt.Errorf("tracked settlement fees %q is less than %s order %d settlement fees %q", + trackedFees, f.GetOrderType(), f.GetOrderID(), orderFees) } - // otherwise, the price filled must be less than the order price. - if !isFullyFilled && orderPrice.Amount.LT(f.PriceAppliedAmt) { - return fmt.Errorf("%s order %d having price %q cannot be partially filled at price %q: price mismatch", - f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceApplied()) + case f.IsBidOrder(): + if !f.PriceAppliedAmt.Equal(f.PriceFilledAmt) { + return fmt.Errorf("price applied %q does not equal price filled %q for %s order %d having price %q", + f.GetPriceApplied(), f.GetPriceFilled(), f.GetOrderType(), f.GetOrderID(), orderPrice) } + // We now know that price applied = filled, and applied + left = order = filled + unfilled, so left = unfilled too. + // We also know that applied > 0 and left < order. So 0 < applied < order. + // If fully filled, we know that unfilled = 0, so applied = order = filled, so we don't need to check that again here. + // If partially filled, we know that applied < order, so we don't need to check that either. + // For bid orders, fees to pay + fees left should equal the order fees. trackedFees := f.FeesToPay.Add(f.OrderFeesLeft...) if !CoinsEquals(trackedFees, orderFees) { @@ -503,7 +510,7 @@ func (f OrderFulfillment) Validate() error { // Saving this simple check for last in the hopes that a previous error exposes why this // order might accidentally be only partially filled. if !isFullyFilled && !f.PartialFillAllowed() { - return fmt.Errorf("cannot fill %s order %d having assets %q with assets %q: order does not allow partial fill", + return fmt.Errorf("cannot fill %s order %d having assets %q with %q: order does not allow partial fill", f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled()) } From 9fbe69a3f5c72974066653da4bfdea33b90784f4 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 28 Sep 2023 14:06:16 -0600 Subject: [PATCH 190/309] [1658]: Unit tests on OrderFulfillment.Validate. --- x/exchange/fulfillment_test.go | 1178 +++++++++++++++++++++++++++++++- 1 file changed, 1171 insertions(+), 7 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index b49e9e7b89..0e830aa19e 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -547,7 +547,7 @@ func TestOrderFulfillment_IsAskOrder(t *testing.T) { {name: "ask", f: OrderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: true}, {name: "bid", f: OrderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: false}, {name: "nil", f: OrderFulfillment{Order: NewOrder(888)}, exp: false}, - {name: "unknown", f: OrderFulfillment{Order: &Order{Order: &unknownOrderType{}}}, exp: false}, + {name: "unknown", f: OrderFulfillment{Order: newUnknownOrder(7)}, exp: false}, } for _, tc := range tests { @@ -571,7 +571,7 @@ func TestOrderFulfillment_IsBidOrder(t *testing.T) { {name: "ask", f: OrderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: false}, {name: "bid", f: OrderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: true}, {name: "nil", f: OrderFulfillment{Order: NewOrder(888)}, exp: false}, - {name: "unknown", f: OrderFulfillment{Order: &Order{Order: &unknownOrderType{}}}, exp: false}, + {name: "unknown", f: OrderFulfillment{Order: newUnknownOrder(9)}, exp: false}, } for _, tc := range tests { @@ -836,7 +836,7 @@ func TestOrderFulfillment_GetOrderType(t *testing.T) { {name: "ask", f: OrderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: OrderTypeAsk}, {name: "bid", f: OrderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: OrderTypeBid}, {name: "nil", f: OrderFulfillment{Order: NewOrder(888)}, exp: ""}, - {name: "unknown", f: OrderFulfillment{Order: &Order{Order: &unknownOrderType{}}}, exp: "*exchange.unknownOrderType"}, + {name: "unknown", f: OrderFulfillment{Order: newUnknownOrder(8)}, exp: "*exchange.unknownOrderType"}, } for _, tc := range tests { @@ -2091,7 +2091,1171 @@ func TestOrderFulfillment_Finalize(t *testing.T) { } } -// TODO[1658]: func TestOrderFulfillment_Validate(t *testing.T) +func TestOrderFulfillment_Validate(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + coinP := func(amount int64, denom string) *sdk.Coin { + return &sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + coins := func(amount int64, denom string) sdk.Coins { + return sdk.Coins{coin(amount, denom)} + } + + _, _ = coinP, coins // TODO[1658]: Delete this line. + + tests := []struct { + name string + receiver OrderFulfillment + expErr string + }{ + { + name: "nil order type", + receiver: OrderFulfillment{Order: NewOrder(2)}, + expErr: nilSubTypeErr(2), + }, + { + name: "unknown order type", + receiver: OrderFulfillment{Order: newUnknownOrder(3)}, + expErr: unknownSubTypeErr(3), + }, + { + name: "not finalized, ask", + receiver: OrderFulfillment{ + Order: NewOrder(4).WithAsk(&AskOrder{}), + IsFinalized: false, + }, + expErr: "fulfillment for ask order 4 has not been finalized", + }, + { + name: "not finalized, bid", + receiver: OrderFulfillment{ + Order: NewOrder(4).WithBid(&BidOrder{}), + IsFinalized: false, + }, + expErr: "fulfillment for bid order 4 has not been finalized", + }, + + { + name: "assets unfilled negative", + receiver: OrderFulfillment{ + Order: NewOrder(5).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(-12), + AssetsFilledAmt: sdkmath.NewInt(97), + }, + expErr: "ask order 5 having assets \"55apple\" has negative assets left \"-12apple\" after filling \"97apple\"", + }, + { + name: "assets filled zero", + receiver: OrderFulfillment{ + Order: NewOrder(6).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(55), + AssetsFilledAmt: sdkmath.NewInt(0), + }, + expErr: "cannot fill non-positive assets \"0apple\" on bid order 6 having assets \"55apple\"", + }, + { + name: "assets filled negative", + receiver: OrderFulfillment{ + Order: NewOrder(7).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(56), + AssetsFilledAmt: sdkmath.NewInt(-1), + }, + expErr: "cannot fill non-positive assets \"-1apple\" on ask order 7 having assets \"55apple\"", + }, + { + name: "assets tracked too low", + receiver: OrderFulfillment{ + Order: NewOrder(8).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(44), + AssetsFilledAmt: sdkmath.NewInt(10), + }, + expErr: "tracked assets \"54apple\" does not equal bid order 8 assets \"55apple\"", + }, + { + name: "assets tracked too high", + receiver: OrderFulfillment{ + Order: NewOrder(8).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(44), + AssetsFilledAmt: sdkmath.NewInt(12), + }, + expErr: "tracked assets \"56apple\" does not equal ask order 8 assets \"55apple\"", + }, + + { + name: "price left equals order price", + receiver: OrderFulfillment{ + Order: NewOrder(19).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98789), + }, + expErr: "price left \"98789plum\" is not less than ask order 19 price \"98789plum\"", + }, + { + name: "price left more than order price", + receiver: OrderFulfillment{ + Order: NewOrder(20).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98790), + }, + expErr: "price left \"98790plum\" is not less than bid order 20 price \"98789plum\"", + }, + { + name: "price applied zero", + receiver: OrderFulfillment{ + Order: NewOrder(21).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98788), + PriceAppliedAmt: sdkmath.NewInt(0), + }, + expErr: "cannot apply non-positive price \"0plum\" to bid order 21 having price \"98789plum\"", + }, + { + name: "price applied negative", + receiver: OrderFulfillment{ + Order: NewOrder(22).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98788), + PriceAppliedAmt: sdkmath.NewInt(-1), + }, + expErr: "cannot apply non-positive price \"-1plum\" to ask order 22 having price \"98789plum\"", + }, + { + name: "price tracked too low", + receiver: OrderFulfillment{ + Order: NewOrder(23).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(788), + }, + expErr: "tracked price \"98788plum\" does not equal ask order 23 price \"98789plum\"", + }, + { + name: "price tracked too high", + receiver: OrderFulfillment{ + Order: NewOrder(24).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98001), + PriceAppliedAmt: sdkmath.NewInt(789), + }, + expErr: "tracked price \"98790plum\" does not equal bid order 24 price \"98789plum\"", + }, + { + name: "price filled zero", + receiver: OrderFulfillment{ + Order: NewOrder(25).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(98789), + PriceFilledAmt: sdkmath.NewInt(0), + }, + expErr: "cannot fill ask order 25 having price \"98789plum\" with non-positive price \"0plum\"", + }, + { + name: "price filled negative", + receiver: OrderFulfillment{ + Order: NewOrder(26).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(98790), + PriceFilledAmt: sdkmath.NewInt(-1), + }, + expErr: "cannot fill bid order 26 having price \"98789plum\" with non-positive price \"-1plum\"", + }, + { + name: "total price too low", + receiver: OrderFulfillment{ + Order: NewOrder(27).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8788), + }, + expErr: "filled price \"8788plum\" plus unfilled price \"90000plum\" does not equal order price \"98789plum\" for ask order 27", + }, + { + name: "total price too high", + receiver: OrderFulfillment{ + Order: NewOrder(28).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90001), + PriceFilledAmt: sdkmath.NewInt(8789), + }, + expErr: "filled price \"8789plum\" plus unfilled price \"90001plum\" does not equal order price \"98789plum\" for bid order 28", + }, + { + name: "price unfilled negative", + receiver: OrderFulfillment{ + Order: NewOrder(29).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(-1), + PriceFilledAmt: sdkmath.NewInt(98790), + }, + expErr: "ask order 29 having price \"98789plum\" has negative price \"-1plum\" after filling \"98790plum\"", + }, + + { + name: "nil splits", + receiver: OrderFulfillment{ + Order: NewOrder(100).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: nil, + }, + expErr: "no splits applied to bid order 100", + }, + { + name: "empty splits", + receiver: OrderFulfillment{ + Order: NewOrder(101).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{}, + }, + expErr: "no splits applied to ask order 101", + }, + { + name: "multiple asset denoms in splits", + receiver: OrderFulfillment{ + Order: NewOrder(102).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "apple"), Price: coin(700, "plum")}, + {Assets: coin(5, "acai"), Price: coin(89, "plum")}, + }, + }, + expErr: "multiple asset denoms \"5acai,3apple\" in splits applied to bid order 102 having assets \"55apple\"", + }, + { + name: "wrong splits assets denom", + receiver: OrderFulfillment{ + Order: NewOrder(103).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "acai"), Price: coin(700, "plum")}, + {Assets: coin(5, "acai"), Price: coin(89, "plum")}, + }, + }, + expErr: "splits asset denom \"8acai\" does not equal order assets denom \"55apple\" on ask order 103", + }, + { + name: "splits assets total too low", + receiver: OrderFulfillment{ + Order: NewOrder(104).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "apple"), Price: coin(800, "plum")}, + {Assets: coin(6, "apple"), Price: coin(89, "plum")}, + }, + }, + expErr: "splits asset total \"9apple\" does not equal filled assets \"10apple\" on bid order 104", + }, + { + name: "splits assets total too high", + receiver: OrderFulfillment{ + Order: NewOrder(105).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "apple"), Price: coin(700, "plum")}, + {Assets: coin(8, "apple"), Price: coin(89, "plum")}, + }, + }, + expErr: "splits asset total \"11apple\" does not equal filled assets \"10apple\" on ask order 105", + }, + { + name: "multiple price denoms in splits", + receiver: OrderFulfillment{ + Order: NewOrder(106).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "apple"), Price: coin(700, "potato")}, + {Assets: coin(7, "apple"), Price: coin(89, "plum")}, + }, + }, + expErr: "multiple price denoms \"89plum,700potato\" in splits applied to bid order 106 having price \"98789plum\"", + }, + { + name: "wrong splits price denom", + receiver: OrderFulfillment{ + Order: NewOrder(107).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "apple"), Price: coin(700, "potato")}, + {Assets: coin(7, "apple"), Price: coin(89, "potato")}, + }, + }, + expErr: "splits price denom \"789potato\" does not equal order price denom \"98789plum\" on ask order 107", + }, + { + name: "splits price total too low", + receiver: OrderFulfillment{ + Order: NewOrder(108).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "apple"), Price: coin(700, "plum")}, + {Assets: coin(7, "apple"), Price: coin(88, "plum")}, + }, + }, + expErr: "splits price total \"788plum\" does not equal filled price \"789plum\" on bid order 108", + }, + { + name: "splits price total too high", + receiver: OrderFulfillment{ + Order: NewOrder(109).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "apple"), Price: coin(701, "plum")}, + {Assets: coin(7, "apple"), Price: coin(89, "plum")}, + }, + }, + expErr: "splits price total \"790plum\" does not equal filled price \"789plum\" on ask order 109", + }, + + { + name: "order fees left has negative", + receiver: OrderFulfillment{ + Order: NewOrder(201).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + SellerSettlementFlatFee: coinP(5, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "apple"), Price: coin(700, "plum")}, + {Assets: coin(7, "apple"), Price: coin(89, "plum")}, + }, + OrderFeesLeft: sdk.Coins{coin(2, "fig"), coin(-3, "grape"), coin(4, "honeydew")}, + }, + expErr: "settlement fees left \"2fig,-3grape,4honeydew\" is negative for ask order 201 having fees \"5fig\"", + }, + { + name: "more fees left than in ask order", + receiver: OrderFulfillment{ + Order: NewOrder(202).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + SellerSettlementFlatFee: coinP(5, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "apple"), Price: coin(700, "plum")}, + {Assets: coin(7, "apple"), Price: coin(89, "plum")}, + }, + OrderFeesLeft: coins(6, "fig"), + }, + expErr: "settlement fees left \"6fig\" is greater than ask order 202 settlement fees \"5fig\"", + }, + { + name: "fees left in ask order without fees", + receiver: OrderFulfillment{ + Order: NewOrder(203).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + SellerSettlementFlatFee: nil, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "apple"), Price: coin(700, "plum")}, + {Assets: coin(7, "apple"), Price: coin(89, "plum")}, + }, + OrderFeesLeft: coins(1, "fig"), + }, + expErr: "settlement fees left \"1fig\" is greater than ask order 203 settlement fees \"\"", + }, + { + name: "more fees left than in bid order", + receiver: OrderFulfillment{ + Order: NewOrder(204).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + BuyerSettlementFees: sdk.Coins{coin(5, "fig"), coin(6, "grape")}, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "apple"), Price: coin(700, "plum")}, + {Assets: coin(7, "apple"), Price: coin(89, "plum")}, + }, + OrderFeesLeft: coins(6, "fig"), + }, + expErr: "settlement fees left \"6fig\" is greater than bid order 204 settlement fees \"5fig,6grape\"", + }, + { + name: "fees left in bid order without fees", + receiver: OrderFulfillment{ + Order: NewOrder(205).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + BuyerSettlementFees: nil, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(45), + AssetsFilledAmt: sdkmath.NewInt(10), + PriceLeftAmt: sdkmath.NewInt(98000), + PriceAppliedAmt: sdkmath.NewInt(789), + PriceUnfilledAmt: sdkmath.NewInt(90000), + PriceFilledAmt: sdkmath.NewInt(8789), + Splits: []*OrderSplit{ + {Assets: coin(3, "apple"), Price: coin(700, "plum")}, + {Assets: coin(7, "apple"), Price: coin(89, "plum")}, + }, + OrderFeesLeft: coins(1, "fig"), + }, + expErr: "settlement fees left \"1fig\" is greater than bid order 205 settlement fees \"\"", + }, + + { + name: "fully filled, price unfilled positive", + receiver: OrderFulfillment{ + Order: NewOrder(250).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(0), + AssetsFilledAmt: sdkmath.NewInt(55), + PriceLeftAmt: sdkmath.NewInt(789), + PriceAppliedAmt: sdkmath.NewInt(98000), + PriceUnfilledAmt: sdkmath.NewInt(788), + PriceFilledAmt: sdkmath.NewInt(98001), + Splits: []*OrderSplit{{Assets: coin(55, "apple"), Price: coin(98000, "plum")}}, + }, + expErr: "fully filled ask order 250 has non-zero unfilled price \"788plum\"", + }, + { + name: "fully filled, order fees left positive", + receiver: OrderFulfillment{ + Order: NewOrder(252).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + SellerSettlementFlatFee: coinP(5, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(0), + AssetsFilledAmt: sdkmath.NewInt(55), + PriceLeftAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(98789), + PriceUnfilledAmt: sdkmath.NewInt(0), + PriceFilledAmt: sdkmath.NewInt(98789), + Splits: []*OrderSplit{{Assets: coin(55, "apple"), Price: coin(98789, "plum")}}, + OrderFeesLeft: coins(1, "fig"), + }, + expErr: "fully filled ask order 252 has non-zero settlement fees left \"1fig\"", + }, + + { + name: "ask order, price applied less than filled", + receiver: OrderFulfillment{ + Order: NewOrder(301).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(45), + PriceLeftAmt: sdkmath.NewInt(8789), + PriceAppliedAmt: sdkmath.NewInt(90000), + PriceUnfilledAmt: sdkmath.NewInt(789), + PriceFilledAmt: sdkmath.NewInt(98000), + Splits: []*OrderSplit{{Assets: coin(45, "apple"), Price: coin(90000, "plum")}}, + }, + expErr: "ask order 301 having assets \"55apple\" and price \"98789plum\" cannot be filled by \"45apple\" at price \"90000plum\": unsufficient price", + }, + { + name: "ask order, partial, multiple fees left denoms", + receiver: OrderFulfillment{ + Order: NewOrder(302).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + SellerSettlementFlatFee: coinP(3, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(45), + PriceLeftAmt: sdkmath.NewInt(789), + PriceAppliedAmt: sdkmath.NewInt(98000), + PriceUnfilledAmt: sdkmath.NewInt(8789), + PriceFilledAmt: sdkmath.NewInt(90000), + Splits: []*OrderSplit{{Assets: coin(45, "apple"), Price: coin(98000, "plum")}}, + OrderFeesLeft: sdk.Coins{coin(1, "fig"), coin(0, "grape")}, + }, + expErr: "partial fulfillment for ask order 302 having seller settlement fees \"3fig\" has multiple denoms in fees left \"1fig,0grape\"", + }, + { + name: "ask order, tracked fees less than order fees", + receiver: OrderFulfillment{ + Order: NewOrder(303).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + SellerSettlementFlatFee: coinP(123, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(45), + PriceLeftAmt: sdkmath.NewInt(789), + PriceAppliedAmt: sdkmath.NewInt(98000), + PriceUnfilledAmt: sdkmath.NewInt(8789), + PriceFilledAmt: sdkmath.NewInt(90000), + Splits: []*OrderSplit{{Assets: coin(45, "apple"), Price: coin(98000, "plum")}}, + OrderFeesLeft: coins(22, "fig"), + FeesToPay: coins(100, "fig"), + }, + expErr: "tracked settlement fees \"122fig\" is less than ask order 303 settlement fees \"123fig\"", + }, + + { + name: "bid order, price applied less than price filled", + receiver: OrderFulfillment{ + Order: NewOrder(275).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(45), + PriceLeftAmt: sdkmath.NewInt(790), + PriceAppliedAmt: sdkmath.NewInt(97999), + PriceUnfilledAmt: sdkmath.NewInt(789), + PriceFilledAmt: sdkmath.NewInt(98000), + Splits: []*OrderSplit{{Assets: coin(45, "apple"), Price: coin(97999, "plum")}}, + }, + expErr: "price applied \"97999plum\" does not equal price filled \"98000plum\" for bid order 275 having price \"98789plum\"", + }, + { + name: "bid order, price applied more than price filled", + receiver: OrderFulfillment{ + Order: NewOrder(276).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(45), + PriceLeftAmt: sdkmath.NewInt(788), + PriceAppliedAmt: sdkmath.NewInt(98001), + PriceUnfilledAmt: sdkmath.NewInt(789), + PriceFilledAmt: sdkmath.NewInt(98000), + Splits: []*OrderSplit{{Assets: coin(45, "apple"), Price: coin(98001, "plum")}}, + }, + expErr: "price applied \"98001plum\" does not equal price filled \"98000plum\" for bid order 276 having price \"98789plum\"", + }, + { + name: "bid order, tracked fees less than order fees", + receiver: OrderFulfillment{ + Order: NewOrder(277).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + BuyerSettlementFees: coins(123, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(1), + AssetsFilledAmt: sdkmath.NewInt(54), + PriceLeftAmt: sdkmath.NewInt(89), + PriceAppliedAmt: sdkmath.NewInt(98700), + PriceUnfilledAmt: sdkmath.NewInt(89), + PriceFilledAmt: sdkmath.NewInt(98700), + Splits: []*OrderSplit{{Assets: coin(54, "apple"), Price: coin(98700, "plum")}}, + OrderFeesLeft: coins(2, "fig"), + FeesToPay: coins(120, "fig"), + }, + expErr: "tracked settlement fees \"122fig\" does not equal bid order 277 settlement fees \"123fig\"", + }, + { + name: "bid order, tracked fees more than order fees", + receiver: OrderFulfillment{ + Order: NewOrder(277).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + BuyerSettlementFees: coins(123, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(1), + AssetsFilledAmt: sdkmath.NewInt(54), + PriceLeftAmt: sdkmath.NewInt(89), + PriceAppliedAmt: sdkmath.NewInt(98700), + PriceUnfilledAmt: sdkmath.NewInt(89), + PriceFilledAmt: sdkmath.NewInt(98700), + Splits: []*OrderSplit{{Assets: coin(54, "apple"), Price: coin(98700, "plum")}}, + OrderFeesLeft: coins(4, "fig"), + FeesToPay: coins(120, "fig"), + }, + expErr: "tracked settlement fees \"124fig\" does not equal bid order 277 settlement fees \"123fig\"", + }, + + { + name: "partial ask, but not allowed", + receiver: OrderFulfillment{ + Order: NewOrder(301).WithAsk(&AskOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + AllowPartial: false, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(1), + AssetsFilledAmt: sdkmath.NewInt(54), + PriceLeftAmt: sdkmath.NewInt(89), + PriceAppliedAmt: sdkmath.NewInt(98700), + PriceUnfilledAmt: sdkmath.NewInt(89), + PriceFilledAmt: sdkmath.NewInt(98700), + Splits: []*OrderSplit{{Assets: coin(54, "apple"), Price: coin(98700, "plum")}}, + }, + expErr: "cannot fill ask order 301 having assets \"55apple\" with \"54apple\": order does not allow partial fill", + }, + { + name: "partial bid, but not allowed", + receiver: OrderFulfillment{ + Order: NewOrder(302).WithBid(&BidOrder{ + Assets: coin(55, "apple"), + Price: coin(98789, "plum"), + AllowPartial: false, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(1), + AssetsFilledAmt: sdkmath.NewInt(54), + PriceLeftAmt: sdkmath.NewInt(89), + PriceAppliedAmt: sdkmath.NewInt(98700), + PriceUnfilledAmt: sdkmath.NewInt(89), + PriceFilledAmt: sdkmath.NewInt(98700), + Splits: []*OrderSplit{{Assets: coin(54, "apple"), Price: coin(98700, "plum")}}, + }, + expErr: "cannot fill bid order 302 having assets \"55apple\" with \"54apple\": order does not allow partial fill", + }, + + { + name: "ask, fully filled, exact", + receiver: OrderFulfillment{ + Order: NewOrder(501).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(0), + AssetsFilledAmt: sdkmath.NewInt(50), + PriceLeftAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceUnfilledAmt: sdkmath.NewInt(0), + PriceFilledAmt: sdkmath.NewInt(100), + Splits: []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(100, "plum")}}, + }, + expErr: "", + }, + { + name: "ask, fully filled, extra price", + receiver: OrderFulfillment{ + Order: NewOrder(502).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(0), + AssetsFilledAmt: sdkmath.NewInt(50), + PriceLeftAmt: sdkmath.NewInt(-5), + PriceAppliedAmt: sdkmath.NewInt(105), + PriceUnfilledAmt: sdkmath.NewInt(0), + PriceFilledAmt: sdkmath.NewInt(100), + Splits: []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(105, "plum")}}, + }, + expErr: "", + }, + { + name: "ask, partially filled, exact", + receiver: OrderFulfillment{ + Order: NewOrder(503).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceUnfilledAmt: sdkmath.NewInt(20), + PriceFilledAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, + }, + expErr: "", + }, + { + name: "ask, partially filled, extra price", + receiver: OrderFulfillment{ + Order: NewOrder(504).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(10), + PriceAppliedAmt: sdkmath.NewInt(90), + PriceUnfilledAmt: sdkmath.NewInt(20), + PriceFilledAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(90, "plum")}}, + }, + expErr: "", + }, + { + name: "bid, fully filled", + receiver: OrderFulfillment{ + Order: NewOrder(505).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(0), + AssetsFilledAmt: sdkmath.NewInt(50), + PriceLeftAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceUnfilledAmt: sdkmath.NewInt(0), + PriceFilledAmt: sdkmath.NewInt(100), + Splits: []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(100, "plum")}}, + }, + expErr: "", + }, + { + name: "bid, partially filled", + receiver: OrderFulfillment{ + Order: NewOrder(506).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceUnfilledAmt: sdkmath.NewInt(20), + PriceFilledAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, + }, + expErr: "", + }, + { + name: "ask, full, no fees, some to pay", + receiver: OrderFulfillment{ + Order: NewOrder(507).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + SellerSettlementFlatFee: nil, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(0), + AssetsFilledAmt: sdkmath.NewInt(50), + PriceLeftAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceUnfilledAmt: sdkmath.NewInt(0), + PriceFilledAmt: sdkmath.NewInt(100), + Splits: []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(100, "plum")}}, + OrderFeesLeft: nil, + FeesToPay: coins(20, "fig"), + }, + expErr: "", + }, + { + name: "ask, full, with fees, paying exact", + receiver: OrderFulfillment{ + Order: NewOrder(508).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + SellerSettlementFlatFee: coinP(200, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(0), + AssetsFilledAmt: sdkmath.NewInt(50), + PriceLeftAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceUnfilledAmt: sdkmath.NewInt(0), + PriceFilledAmt: sdkmath.NewInt(100), + Splits: []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(100, "plum")}}, + OrderFeesLeft: nil, + FeesToPay: coins(200, "fig"), + }, + expErr: "", + }, + { + name: "ask, full, with fees, paying more", + receiver: OrderFulfillment{ + Order: NewOrder(509).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + SellerSettlementFlatFee: coinP(200, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(0), + AssetsFilledAmt: sdkmath.NewInt(50), + PriceLeftAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceUnfilledAmt: sdkmath.NewInt(0), + PriceFilledAmt: sdkmath.NewInt(100), + Splits: []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(100, "plum")}}, + OrderFeesLeft: nil, + FeesToPay: coins(205, "fig"), + }, + expErr: "", + }, + { + name: "ask, partial, no fees, some to pay", + receiver: OrderFulfillment{ + Order: NewOrder(510).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + SellerSettlementFlatFee: nil, + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceUnfilledAmt: sdkmath.NewInt(20), + PriceFilledAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, + OrderFeesLeft: nil, + FeesToPay: coins(20, "fig"), + }, + expErr: "", + }, + { + name: "ask, partial, with fees, paying exact", + receiver: OrderFulfillment{ + Order: NewOrder(511).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + SellerSettlementFlatFee: coinP(20, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceUnfilledAmt: sdkmath.NewInt(20), + PriceFilledAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, + OrderFeesLeft: nil, + FeesToPay: coins(20, "fig"), + }, + expErr: "", + }, + { + name: "ask, partial, with fees, paying more", + receiver: OrderFulfillment{ + Order: NewOrder(512).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + SellerSettlementFlatFee: coinP(20, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceUnfilledAmt: sdkmath.NewInt(20), + PriceFilledAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, + OrderFeesLeft: coins(4, "fig"), + FeesToPay: coins(55, "fig"), + }, + expErr: "", + }, + { + name: "ask, partial, with fees, none being paid", + receiver: OrderFulfillment{ + Order: NewOrder(513).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + SellerSettlementFlatFee: coinP(200, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceUnfilledAmt: sdkmath.NewInt(20), + PriceFilledAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, + OrderFeesLeft: coins(200, "fig"), + FeesToPay: nil, + }, + expErr: "", + }, + { + name: "bid, partial, with fees, none being paid", + receiver: OrderFulfillment{ + Order: NewOrder(514).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + BuyerSettlementFees: coins(20, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceUnfilledAmt: sdkmath.NewInt(20), + PriceFilledAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, + OrderFeesLeft: coins(20, "fig"), + FeesToPay: nil, + }, + expErr: "", + }, + { + name: "bid, partial, with fees, all being paid", + receiver: OrderFulfillment{ + Order: NewOrder(515).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + BuyerSettlementFees: coins(20, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceUnfilledAmt: sdkmath.NewInt(20), + PriceFilledAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, + OrderFeesLeft: nil, + FeesToPay: coins(20, "fig"), + }, + expErr: "", + }, + { + name: "bid, partial, with fees, some being paid", + receiver: OrderFulfillment{ + Order: NewOrder(515).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + AllowPartial: true, + BuyerSettlementFees: coins(20, "fig"), + }), + IsFinalized: true, + AssetsUnfilledAmt: sdkmath.NewInt(10), + AssetsFilledAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceUnfilledAmt: sdkmath.NewInt(20), + PriceFilledAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, + OrderFeesLeft: coins(4, "fig"), + FeesToPay: coins(16, "fig"), + }, + expErr: "", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = tc.receiver.Validate() + } + require.NotPanics(t, testFunc, "Validate") + assertions.AssertErrorValue(t, err, tc.expErr, "Validate error") + }) + } +} // TODO[1658]: func TestFulfill(t *testing.T) @@ -2346,7 +3510,7 @@ func TestNewPartialFulfillment(t *testing.T) { { name: "unknown order type", f: &OrderFulfillment{ - Order: &Order{OrderId: 58, Order: &unknownOrderType{}}, + Order: newUnknownOrder(58), AssetsFilledAmt: sdkmath.NewInt(5), PriceFilledAmt: sdkmath.NewInt(6), }, @@ -3647,7 +4811,7 @@ func TestGetAssetTransfer(t *testing.T) { }, { name: "unknown inside order", - f: &OrderFulfillment{Order: &Order{OrderId: 21, Order: &unknownOrderType{}}}, + f: &OrderFulfillment{Order: newUnknownOrder(21)}, expPanic: "unknown order type *exchange.unknownOrderType", }, } @@ -3873,7 +5037,7 @@ func TestGetPriceTransfer(t *testing.T) { }, { name: "unknown inside order", - f: &OrderFulfillment{Order: &Order{OrderId: 21, Order: &unknownOrderType{}}}, + f: &OrderFulfillment{Order: newUnknownOrder(21)}, expPanic: "unknown order type *exchange.unknownOrderType", }, } From 44f5a7637f0e415ba023d96287478ea70b97afe7 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 28 Sep 2023 15:40:53 -0600 Subject: [PATCH 191/309] [1658]: Update Fulfill: add context to the order type mismatch error. Add asset denom check. --- x/exchange/fulfillment.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 21273002f3..ea3d516492 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -523,7 +523,7 @@ func Fulfill(of1, of2 *OrderFulfillment) error { order1Type := of1.Order.GetOrderType() order2Type := of2.Order.GetOrderType() if order1Type == order2Type { - return fmt.Errorf("cannot fulfill %s order %d with %s order %d", + return fmt.Errorf("cannot fulfill %s order %d with %s order %d: order type mismatch", order1Type, of1.Order.OrderId, order2Type, of2.Order.OrderId) } @@ -537,9 +537,13 @@ func Fulfill(of1, of2 *OrderFulfillment) error { } askOrder, bidOrder := askOF.Order.GetAskOrder(), bidOF.Order.GetBidOrder() + if askOrder.Assets.Denom != bidOrder.Assets.Denom { + return fmt.Errorf("cannot fill bid order %d having assets %q with ask order %d having assets %q: denom mismatch", + bidOF.GetOrderID(), bidOrder.Assets, askOF.GetOrderID(), askOrder.Assets) + } if askOrder.Price.Denom != bidOrder.Price.Denom { - return fmt.Errorf("cannot fill bid order %d having price %q with ask order %d having price %q: denom mismatch", - bidOF.GetOrderID(), bidOrder.Price, askOF.GetOrderID(), askOrder.Price) + return fmt.Errorf("cannot fill ask order %d having price %q with bid order %d having price %q: denom mismatch", + askOF.GetOrderID(), askOrder.Price, bidOF.GetOrderID(), bidOrder.Price) } assetsAmt, err := GetFulfillmentAssetsAmt(askOF, bidOF) From 3af84c9530493661f80d91d10f19a2cb51693078 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 28 Sep 2023 15:41:21 -0600 Subject: [PATCH 192/309] [1658]: Unit tests on Fulfill. --- x/exchange/fulfillment_test.go | 781 ++++++++++++++++++++++++++++++++- 1 file changed, 778 insertions(+), 3 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 0e830aa19e..6bd20aa50f 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -2102,8 +2102,6 @@ func TestOrderFulfillment_Validate(t *testing.T) { return sdk.Coins{coin(amount, denom)} } - _, _ = coinP, coins // TODO[1658]: Delete this line. - tests := []struct { name string receiver OrderFulfillment @@ -3257,7 +3255,784 @@ func TestOrderFulfillment_Validate(t *testing.T) { } } -// TODO[1658]: func TestFulfill(t *testing.T) +func TestFulfill(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + ofA *OrderFulfillment + ofB *OrderFulfillment + expA *OrderFulfillment + expB *OrderFulfillment + expErr string + swapErr string + }{ + { + name: "ask ask", + ofA: &OrderFulfillment{Order: NewOrder(1).WithAsk(&AskOrder{})}, + ofB: &OrderFulfillment{Order: NewOrder(2).WithAsk(&AskOrder{})}, + expErr: "cannot fulfill ask order 1 with ask order 2: order type mismatch", + swapErr: "cannot fulfill ask order 2 with ask order 1: order type mismatch", + }, + { + name: "bid bid", + ofA: &OrderFulfillment{Order: NewOrder(4).WithBid(&BidOrder{})}, + ofB: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, + expErr: "cannot fulfill bid order 4 with bid order 3: order type mismatch", + swapErr: "cannot fulfill bid order 3 with bid order 4: order type mismatch", + }, + { + name: "diff asset denom", + ofA: &OrderFulfillment{Order: NewOrder(5).WithAsk(&AskOrder{Assets: coin(15, "apple")})}, + ofB: &OrderFulfillment{Order: NewOrder(6).WithBid(&BidOrder{Assets: coin(16, "banana")})}, + expErr: "cannot fill bid order 6 having assets \"16banana\" with ask order 5 having assets \"15apple\": denom mismatch", + }, + { + name: "diff price denom", + ofA: &OrderFulfillment{ + Order: NewOrder(7).WithAsk(&AskOrder{ + Assets: coin(15, "apple"), + Price: coin(17, "pear"), + }), + }, + ofB: &OrderFulfillment{ + Order: NewOrder(8).WithBid(&BidOrder{ + Assets: coin(16, "apple"), + Price: coin(18, "plum"), + }), + }, + expErr: "cannot fill ask order 7 having price \"17pear\" with bid order 8 having price \"18plum\": denom mismatch", + }, + { + name: "cannot get assets left", + ofA: &OrderFulfillment{ + Order: NewOrder(9).WithAsk(&AskOrder{ + Assets: coin(15, "apple"), + Price: coin(16, "plum"), + }), + AssetsUnfilledAmt: sdkmath.NewInt(-1), + }, + ofB: &OrderFulfillment{ + Order: NewOrder(10).WithBid(&BidOrder{ + Assets: coin(17, "apple"), + Price: coin(18, "plum"), + }), + AssetsUnfilledAmt: sdkmath.NewInt(3), + }, + expErr: "cannot fill ask order 9 having assets left \"-1apple\" with bid order 10 " + + "having assets left \"3apple\": zero or negative assets left", + }, + { + name: "error from apply", + ofA: &OrderFulfillment{ + Order: NewOrder(11).WithAsk(&AskOrder{ + Assets: coin(15, "apple"), + Price: coin(90, "plum"), + }), + AssetsUnfilledAmt: sdkmath.NewInt(15), + AssetsFilledAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(90), + PriceAppliedAmt: sdkmath.NewInt(0), + }, + ofB: &OrderFulfillment{ + Order: NewOrder(12).WithBid(&BidOrder{ + Assets: coin(30, "apple"), + Price: coin(180, "plum"), + }), + AssetsUnfilledAmt: sdkmath.NewInt(30), + AssetsFilledAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(89), + PriceAppliedAmt: sdkmath.NewInt(91), + }, + expErr: "cannot fill bid order 12 having price left \"89plum\" to ask order 11 at a price of \"90plum\": overfill", + }, + { + name: "both filled in full", + ofA: &OrderFulfillment{ + Order: NewOrder(101).WithAsk(&AskOrder{ + Assets: coin(33, "apple"), + Price: coin(57, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: sdkmath.NewInt(33), + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(57), + }, + ofB: &OrderFulfillment{ + Order: NewOrder(102).WithBid(&BidOrder{ + Assets: coin(33, "apple"), + Price: coin(57, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: sdkmath.NewInt(33), + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(57), + }, + expA: &OrderFulfillment{ + Order: NewOrder(101).WithAsk(&AskOrder{ + Assets: coin(33, "apple"), + Price: coin(57, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(33), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(57), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{{Assets: coin(33, "apple"), Price: coin(57, "plum")}}, + }, + expB: &OrderFulfillment{ + Order: NewOrder(102).WithBid(&BidOrder{ + Assets: coin(33, "apple"), + Price: coin(57, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(33), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(57), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{{Assets: coin(33, "apple"), Price: coin(57, "plum")}}, + }, + }, + { + name: "ask, unfilled, gets partially filled", + ofA: &OrderFulfillment{ + Order: NewOrder(103).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: sdkmath.NewInt(50), + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(100), + }, + ofB: &OrderFulfillment{ + Order: NewOrder(104).WithBid(&BidOrder{ + Assets: coin(20, "apple"), + Price: coin(80, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(80), + }, + expA: &OrderFulfillment{ + Order: NewOrder(103).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(30), + PriceAppliedAmt: sdkmath.NewInt(80), + PriceLeftAmt: sdkmath.NewInt(20), + Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(80, "plum")}}, + }, + expB: &OrderFulfillment{ + Order: NewOrder(104).WithBid(&BidOrder{ + Assets: coin(20, "apple"), + Price: coin(80, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(80), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(80, "plum")}}, + }, + }, + { + name: "ask, partially filled, gets partially filled more", + ofA: &OrderFulfillment{ + Order: NewOrder(105).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(1), + AssetsUnfilledAmt: sdkmath.NewInt(49), + PriceAppliedAmt: sdkmath.NewInt(2), + PriceLeftAmt: sdkmath.NewInt(98), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(88).WithBid(&BidOrder{})}, + Assets: coin(1, "apple"), + Price: coin(2, "plum"), + }, + }, + }, + ofB: &OrderFulfillment{ + Order: NewOrder(106).WithBid(&BidOrder{ + Assets: coin(20, "apple"), + Price: coin(80, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(80), + }, + expA: &OrderFulfillment{ + Order: NewOrder(105).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(21), + AssetsUnfilledAmt: sdkmath.NewInt(29), + PriceAppliedAmt: sdkmath.NewInt(82), + PriceLeftAmt: sdkmath.NewInt(18), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(88).WithBid(&BidOrder{})}, + Assets: coin(1, "apple"), + Price: coin(2, "plum"), + }, + {Assets: coin(20, "apple"), Price: coin(80, "plum")}, + }, + }, + expB: &OrderFulfillment{ + Order: NewOrder(106).WithBid(&BidOrder{ + Assets: coin(20, "apple"), + Price: coin(80, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(80), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(80, "plum")}}, + }, + }, + { + name: "ask, partially filled, gets fully filled", + ofA: &OrderFulfillment{ + Order: NewOrder(107).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(30), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(20), + PriceLeftAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(86).WithBid(&BidOrder{})}, + Assets: coin(30, "apple"), + Price: coin(20, "plum"), + }, + }, + }, + ofB: &OrderFulfillment{ + Order: NewOrder(108).WithBid(&BidOrder{ + Assets: coin(20, "apple"), + Price: coin(80, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(80), + }, + expA: &OrderFulfillment{ + Order: NewOrder(107).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(86).WithBid(&BidOrder{})}, + Assets: coin(30, "apple"), + Price: coin(20, "plum"), + }, + {Assets: coin(20, "apple"), Price: coin(80, "plum")}, + }, + }, + expB: &OrderFulfillment{ + Order: NewOrder(108).WithBid(&BidOrder{ + Assets: coin(20, "apple"), + Price: coin(80, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(80), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(80, "plum")}}, + }, + }, + { + name: "bid, unfilled, gets partially filled", + ofA: &OrderFulfillment{ + Order: NewOrder(151).WithAsk(&AskOrder{ + Assets: coin(20, "apple"), + Price: coin(30, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(30), + }, + ofB: &OrderFulfillment{ + Order: NewOrder(152).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: sdkmath.NewInt(50), + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(100), + }, + expA: &OrderFulfillment{ + Order: NewOrder(151).WithAsk(&AskOrder{ + Assets: coin(20, "apple"), + Price: coin(30, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(-10), + Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(40, "plum")}}, + }, + expB: &OrderFulfillment{ + Order: NewOrder(152).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(30), + PriceAppliedAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(60), + Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(40, "plum")}}, + }, + }, + { + name: "bid, unfilled, gets partially filled with truncation", + ofA: &OrderFulfillment{ + Order: NewOrder(153).WithAsk(&AskOrder{ + Assets: coin(20, "apple"), + Price: coin(30, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(30), + }, + ofB: &OrderFulfillment{ + Order: NewOrder(154).WithBid(&BidOrder{ + Assets: coin(57, "apple"), + Price: coin(331, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: sdkmath.NewInt(57), + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(331), + }, + expA: &OrderFulfillment{ + Order: NewOrder(153).WithAsk(&AskOrder{ + Assets: coin(20, "apple"), + Price: coin(30, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(116), // 331 * 20 / 57 = 116.140350877193 + PriceLeftAmt: sdkmath.NewInt(-86), + Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(116, "plum")}}, + }, + expB: &OrderFulfillment{ + Order: NewOrder(154).WithBid(&BidOrder{ + Assets: coin(57, "apple"), + Price: coin(331, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(37), + PriceAppliedAmt: sdkmath.NewInt(116), + PriceLeftAmt: sdkmath.NewInt(215), + Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(116, "plum")}}, + }, + }, + { + name: "bid, partially filled, gets partially filled more", + ofA: &OrderFulfillment{ + Order: NewOrder(155).WithAsk(&AskOrder{ + Assets: coin(20, "apple"), + Price: coin(30, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(30), + }, + ofB: &OrderFulfillment{ + Order: NewOrder(156).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(10), + AssetsUnfilledAmt: sdkmath.NewInt(40), + PriceAppliedAmt: sdkmath.NewInt(20), + PriceLeftAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(77).WithAsk(&AskOrder{})}, + Assets: coin(10, "apple"), + Price: coin(20, "plum"), + }, + }, + }, + expA: &OrderFulfillment{ + Order: NewOrder(155).WithAsk(&AskOrder{ + Assets: coin(20, "apple"), + Price: coin(30, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(-10), + Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(40, "plum")}}, + }, + expB: &OrderFulfillment{ + Order: NewOrder(156).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(30), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(60), + PriceLeftAmt: sdkmath.NewInt(40), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(77).WithAsk(&AskOrder{})}, + Assets: coin(10, "apple"), + Price: coin(20, "plum"), + }, + {Assets: coin(20, "apple"), Price: coin(40, "plum")}, + }, + }, + }, + { + name: "bid, partially filled, gets fully filled", + ofA: &OrderFulfillment{ + Order: NewOrder(157).WithAsk(&AskOrder{ + Assets: coin(20, "apple"), + Price: coin(30, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: sdkmath.NewInt(30), + }, + ofB: &OrderFulfillment{ + Order: NewOrder(158).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(30), + AssetsUnfilledAmt: sdkmath.NewInt(20), + PriceAppliedAmt: sdkmath.NewInt(60), + PriceLeftAmt: sdkmath.NewInt(40), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(75).WithAsk(&AskOrder{})}, + Assets: coin(30, "apple"), + Price: coin(60, "plum"), + }, + }, + }, + expA: &OrderFulfillment{ + Order: NewOrder(157).WithAsk(&AskOrder{ + Assets: coin(20, "apple"), + Price: coin(30, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(-10), + Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(40, "plum")}}, + }, + expB: &OrderFulfillment{ + Order: NewOrder(158).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(75).WithAsk(&AskOrder{})}, + Assets: coin(30, "apple"), + Price: coin(60, "plum"), + }, + {Assets: coin(20, "apple"), Price: coin(40, "plum")}, + }, + }, + }, + { + name: "both partially filled, both get fully filled", + ofA: &OrderFulfillment{ + Order: NewOrder(201).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(30), + PriceAppliedAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(60), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(1002).WithBid(&BidOrder{})}, + Assets: coin(20, "apple"), + Price: coin(40, "plum"), + }, + }, + }, + ofB: &OrderFulfillment{ + Order: NewOrder(202).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(30), + PriceAppliedAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(60), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(1003).WithAsk(&AskOrder{})}, + Assets: coin(20, "apple"), + Price: coin(40, "plum"), + }, + }, + }, + expA: &OrderFulfillment{ + Order: NewOrder(201).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(1002).WithBid(&BidOrder{})}, + Assets: coin(20, "apple"), + Price: coin(40, "plum"), + }, + {Assets: coin(30, "apple"), Price: coin(60, "plum")}, + }, + }, + expB: &OrderFulfillment{ + Order: NewOrder(202).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(1003).WithAsk(&AskOrder{})}, + Assets: coin(20, "apple"), + Price: coin(40, "plum"), + }, + {Assets: coin(30, "apple"), Price: coin(60, "plum")}, + }, + }, + }, + { + name: "both partially filled, ask gets fully filled", + ofA: &OrderFulfillment{ + Order: NewOrder(203).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(30), + PriceAppliedAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(60), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(1004).WithBid(&BidOrder{})}, + Assets: coin(20, "apple"), + Price: coin(40, "plum"), + }, + }, + }, + ofB: &OrderFulfillment{ + Order: NewOrder(204).WithBid(&BidOrder{ + Assets: coin(60, "apple"), + Price: coin(120, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(40), + PriceAppliedAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(1005).WithAsk(&AskOrder{})}, + Assets: coin(20, "apple"), + Price: coin(40, "plum"), + }, + }, + }, + expA: &OrderFulfillment{ + Order: NewOrder(203).WithAsk(&AskOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(1004).WithBid(&BidOrder{})}, + Assets: coin(20, "apple"), + Price: coin(40, "plum"), + }, + {Assets: coin(30, "apple"), Price: coin(60, "plum")}, + }, + }, + expB: &OrderFulfillment{ + Order: NewOrder(204).WithBid(&BidOrder{ + Assets: coin(60, "apple"), + Price: coin(120, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(10), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: sdkmath.NewInt(20), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(1005).WithAsk(&AskOrder{})}, + Assets: coin(20, "apple"), + Price: coin(40, "plum"), + }, + {Assets: coin(30, "apple"), Price: coin(60, "plum")}, + }, + }, + }, + { + name: "both partially filled, bid gets fully filled", + ofA: &OrderFulfillment{ + Order: NewOrder(205).WithAsk(&AskOrder{ + Assets: coin(60, "apple"), + Price: coin(120, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(40), + PriceAppliedAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(80), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(1006).WithBid(&BidOrder{})}, + Assets: coin(20, "apple"), + Price: coin(40, "plum"), + }, + }, + }, + ofB: &OrderFulfillment{ + Order: NewOrder(206).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(30), + PriceAppliedAmt: sdkmath.NewInt(40), + PriceLeftAmt: sdkmath.NewInt(60), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(1007).WithAsk(&AskOrder{})}, + Assets: coin(20, "apple"), + Price: coin(40, "plum"), + }, + }, + }, + expA: &OrderFulfillment{ + Order: NewOrder(205).WithAsk(&AskOrder{ + Assets: coin(60, "apple"), + Price: coin(120, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: sdkmath.NewInt(10), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: sdkmath.NewInt(20), + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(1006).WithBid(&BidOrder{})}, + Assets: coin(20, "apple"), + Price: coin(40, "plum"), + }, + {Assets: coin(30, "apple"), Price: coin(60, "plum")}, + }, + }, + expB: &OrderFulfillment{ + Order: NewOrder(206).WithBid(&BidOrder{ + Assets: coin(50, "apple"), + Price: coin(100, "plum"), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{ + { + Order: &OrderFulfillment{Order: NewOrder(1007).WithAsk(&AskOrder{})}, + Assets: coin(20, "apple"), + Price: coin(40, "plum"), + }, + {Assets: coin(30, "apple"), Price: coin(60, "plum")}, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if len(tc.expErr) != 0 && len(tc.swapErr) == 0 { + tc.swapErr = tc.expErr + } + if len(tc.expErr) == 0 { + for _, split := range tc.expA.Splits { + if split.Order == nil { + split.Order = tc.expB + } + } + for _, split := range tc.expB.Splits { + if split.Order == nil { + split.Order = tc.expA + } + } + } + + of1, of2 := copyOrderFulfillment(tc.ofA), copyOrderFulfillment(tc.ofB) + var err error + testFunc := func() { + err = Fulfill(of1, of2) + } + require.NotPanics(t, testFunc, "Fulfill(A, B)") + assertions.AssertErrorValue(t, err, tc.expErr, "Fulfill(A, B) error") + if len(tc.expErr) == 0 { + if !assertEqualOrderFulfillments(t, tc.expA, of1, "Fulfill(A, B): A") { + t.Logf("Original: %s", orderFulfillmentString(tc.ofA)) + } + if !assertEqualOrderFulfillments(t, tc.expB, of2, "Fulfill(A, B): B") { + t.Logf("Original: %s", orderFulfillmentString(tc.ofB)) + } + } + + of1, of2 = copyOrderFulfillment(tc.ofB), copyOrderFulfillment(tc.ofA) + require.NotPanics(t, testFunc, "Fulfill(B, A)") + assertions.AssertErrorValue(t, err, tc.swapErr, "Fulfill(B, A) error") + if len(tc.expErr) == 0 { + if !assertEqualOrderFulfillments(t, tc.expB, of1, "Fulfill(B, A): B") { + t.Logf("Original: %s", orderFulfillmentString(tc.ofA)) + } + if !assertEqualOrderFulfillments(t, tc.expA, of2, "Fulfill(B, A): A") { + t.Logf("Original: %s", orderFulfillmentString(tc.ofB)) + } + } + }) + } +} func TestGetFulfillmentAssetsAmt(t *testing.T) { newAskOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *OrderFulfillment { From 6b937a5ba69f24802329fbf1b215e8ebc96030b8 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 28 Sep 2023 19:34:52 -0600 Subject: [PATCH 193/309] [1658]: Tweak some error messages in BuildFulfillments. Also make sure bids are bids and asks are asks. --- x/exchange/fulfillment.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index ea3d516492..99a74ee8db 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -469,7 +469,7 @@ func (f OrderFulfillment) Validate() error { case f.IsAskOrder(): // For ask orders, the applied amount needs to be at least the filled amount. if f.PriceAppliedAmt.LT(f.PriceFilledAmt) { - return fmt.Errorf("%s order %d having assets %q and price %q cannot be filled by %q at price %q: unsufficient price", + return fmt.Errorf("%s order %d having assets %q and price %q cannot be filled by %q at price %q: insufficient price", f.GetOrderType(), f.GetOrderID(), orderAssets, orderPrice, f.GetAssetsFilled(), f.GetPriceApplied()) } @@ -650,10 +650,18 @@ func NewPartialFulfillment(f *OrderFulfillment) *PartialFulfillment { func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) (*Fulfillments, error) { askOFs := make([]*OrderFulfillment, len(askOrders)) for i, askOrder := range askOrders { + if !askOrder.IsAskOrder() { + return nil, fmt.Errorf("%s order %d is not an ask order but is in the askOrders list", + askOrder.GetOrderType(), askOrder.GetOrderID()) + } askOFs[i] = NewOrderFulfillment(askOrder) } bidOFs := make([]*OrderFulfillment, len(bidOrders)) for i, bidOrder := range bidOrders { + if !bidOrder.IsBidOrder() { + return nil, fmt.Errorf("%s order %d is not a bid order but is in the bidOrders list", + bidOrder.GetOrderType(), bidOrder.GetOrderID()) + } bidOFs[i] = NewOrderFulfillment(bidOrder) } @@ -666,8 +674,8 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) askFilled := askOFs[a].IsFullyFilled() bidFilled := bidOFs[b].IsFullyFilled() if !askFilled && !bidFilled { - return nil, fmt.Errorf("neither ask order %d nor bid order %d could be filled in full", - askOFs[a].GetOrderID(), bidOFs[b].GetOrderID()) + return nil, fmt.Errorf("neither %s order %d nor %s order %d could be filled in full", + askOFs[a].GetOrderType(), askOFs[a].GetOrderID(), bidOFs[b].GetOrderType(), bidOFs[b].GetOrderID()) } if askFilled { a++ @@ -708,8 +716,8 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) for i, askOF := range askOFs { if !askOF.IsFullyFilled() { if i != lastAskI { - return nil, fmt.Errorf("ask order %d (at index %d) is not filled in full and is not the last ask order provided", - askOF.GetOrderID(), i) + return nil, fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last ask order provided", + askOF.GetOrderType(), askOF.GetOrderID(), i) } partialFulfillments = append(partialFulfillments, askOF) } @@ -717,8 +725,8 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) for i, bidOF := range bidOFs { if !bidOF.IsFullyFilled() { if i != lastBidI { - return nil, fmt.Errorf("bid order %d (at index %d) is not filled in full and is not the last bid order provided", - bidOF.GetOrderID(), i) + return nil, fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last bid order provided", + bidOF.GetOrderType(), bidOF.GetOrderID(), i) } partialFulfillments = append(partialFulfillments, bidOF) } From 4899347271a4d2e860ddffbf9efec183d5db5f62 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 28 Sep 2023 19:35:21 -0600 Subject: [PATCH 194/309] [1658]: Unit tests on BuildFulfillments. --- x/exchange/fulfillment_test.go | 704 ++++++++++++++++++++++++++++++++- 1 file changed, 702 insertions(+), 2 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 6bd20aa50f..1d21134c1a 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -2745,7 +2745,7 @@ func TestOrderFulfillment_Validate(t *testing.T) { PriceFilledAmt: sdkmath.NewInt(98000), Splits: []*OrderSplit{{Assets: coin(45, "apple"), Price: coin(90000, "plum")}}, }, - expErr: "ask order 301 having assets \"55apple\" and price \"98789plum\" cannot be filled by \"45apple\" at price \"90000plum\": unsufficient price", + expErr: "ask order 301 having assets \"55apple\" and price \"98789plum\" cannot be filled by \"45apple\" at price \"90000plum\": insufficient price", }, { name: "ask order, partial, multiple fees left denoms", @@ -4313,7 +4313,707 @@ func TestNewPartialFulfillment(t *testing.T) { } } -// TODO[1658]: func TestBuildFulfillments(t *testing.T) +func TestBuildFulfillments(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + coins := func(amount int64, denom string) sdk.Coins { + return sdk.Coins{coin(amount, denom)} + } + + askOrder := func(orderID uint64, assets sdk.Coin, price sdk.Coin, allowPartial bool, fees ...sdk.Coin) *Order { + ao := &AskOrder{ + Seller: "seller", + Assets: assets, + Price: price, + AllowPartial: allowPartial, + } + if len(fees) > 1 { + t.Fatalf("cannot create ask order %d with more than 1 fees %q", orderID, fees) + } + if len(fees) > 0 { + ao.SellerSettlementFlatFee = &fees[0] + } + return NewOrder(orderID).WithAsk(ao) + } + bidOrder := func(orderID uint64, assets sdk.Coin, price sdk.Coin, allowPartial bool, fees ...sdk.Coin) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Buyer: "buyer", + Assets: assets, + Price: price, + AllowPartial: allowPartial, + BuyerSettlementFees: fees, + }) + } + filledOF := func(order *Order, priceAmt int64, splits []*OrderSplit, feesToPay ...sdk.Coins) *OrderFulfillment { + rv := &OrderFulfillment{ + Order: order, + AssetsFilledAmt: order.GetAssets().Amount, + AssetsUnfilledAmt: ZeroAmtAfterSub, + Splits: splits, + IsFinalized: true, + PriceFilledAmt: order.GetPrice().Amount, + PriceUnfilledAmt: sdkmath.NewInt(0), + } + if priceAmt != 0 { + rv.PriceAppliedAmt = sdkmath.NewInt(priceAmt) + } else { + rv.PriceAppliedAmt = order.GetPrice().Amount + } + rv.PriceLeftAmt = rv.PriceFilledAmt.Sub(rv.PriceAppliedAmt) + if len(feesToPay) > 0 { + rv.FeesToPay = feesToPay[0] + } + return rv + } + + tests := []struct { + name string + askOrders []*Order + bidOrders []*Order + sellerFeeRatio *FeeRatio + expectedMaker func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments + expErr string + }{ + { + name: "one ask one bid, both fully filled", + askOrders: []*Order{askOrder(5, coin(10, "apple"), coin(55, "prune"), false, coin(8, "fig"))}, + bidOrders: []*Order{bidOrder(6, coin(10, "apple"), coin(60, "prune"), false, coin(33, "fig"))}, + sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, + expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { + rv := &Fulfillments{ + AskOFs: []*OrderFulfillment{ + filledOF(askOrders[0], 60, + []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(60, "prune")}}, // will be BidOFs[0]. + coins(20, "fig"), + ), + }, + BidOFs: []*OrderFulfillment{ + filledOF(bidOrders[0], 0, + []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(60, "prune")}}, // will be AskOFs[0]. + coins(33, "fig"), + ), + }, + PartialOrder: nil, + } + + rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] + rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] + + return rv + }, + }, + { + name: "one ask one bid, ask partially filled", + askOrders: []*Order{askOrder(7, coin(15, "apple"), coin(75, "prune"), true)}, + bidOrders: []*Order{bidOrder(8, coin(10, "apple"), coin(60, "prune"), false)}, + sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, + expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { + rv := &Fulfillments{ + AskOFs: []*OrderFulfillment{ + { + Order: askOrders[0], + AssetsFilledAmt: sdkmath.NewInt(10), + AssetsUnfilledAmt: sdkmath.NewInt(5), + PriceAppliedAmt: sdkmath.NewInt(60), + PriceLeftAmt: sdkmath.NewInt(15), + Splits: []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(60, "prune")}}, // will be BidOFs[0]. + IsFinalized: true, + FeesToPay: coins(12, "fig"), + OrderFeesLeft: nil, + PriceFilledAmt: sdkmath.NewInt(50), + PriceUnfilledAmt: sdkmath.NewInt(25), + }, + }, + BidOFs: []*OrderFulfillment{ + filledOF(bidOrders[0], 0, + []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(60, "prune")}}, // will be AskOFs[0]. + ), + }, + PartialOrder: &PartialFulfillment{ + NewOrder: askOrder(7, coin(5, "apple"), coin(25, "prune"), true), + AssetsFilled: coin(10, "apple"), + PriceFilled: coin(50, "prune"), + }, + } + + rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] + rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] + + return rv + }, + }, + { + name: "one ask one bid, ask partially filled not allowed", + askOrders: []*Order{askOrder(7, coin(15, "apple"), coin(75, "prune"), false)}, + bidOrders: []*Order{bidOrder(8, coin(10, "apple"), coin(60, "prune"), false)}, + sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, + expErr: "cannot fill ask order 7 having assets \"15apple\" with \"10apple\": order does not allow partial fill", + }, + { + name: "one ask one bid, bid partially filled", + askOrders: []*Order{askOrder(9, coin(10, "apple"), coin(50, "prune"), false)}, + bidOrders: []*Order{bidOrder(10, coin(15, "apple"), coin(90, "prune"), true)}, + sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, + expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { + rv := &Fulfillments{ + AskOFs: []*OrderFulfillment{ + filledOF(askOrders[0], 60, + []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(60, "prune")}}, // will be BidOFs[0]. + coins(12, "fig"), + ), + }, + BidOFs: []*OrderFulfillment{ + { + Order: bidOrders[0], + AssetsFilledAmt: sdkmath.NewInt(10), + AssetsUnfilledAmt: sdkmath.NewInt(5), + PriceAppliedAmt: sdkmath.NewInt(60), + PriceLeftAmt: sdkmath.NewInt(30), + Splits: []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(60, "prune")}}, // will be AskOFs[0]. + IsFinalized: true, + FeesToPay: nil, + OrderFeesLeft: nil, + PriceFilledAmt: sdkmath.NewInt(60), + PriceUnfilledAmt: sdkmath.NewInt(30), + }, + }, + PartialOrder: &PartialFulfillment{ + NewOrder: bidOrder(10, coin(5, "apple"), coin(30, "prune"), true), + AssetsFilled: coin(10, "apple"), + PriceFilled: coin(60, "prune"), + }, + } + + rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] + rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] + + return rv + }, + }, + { + name: "one ask one bid, bid partially filled not allowed", + askOrders: []*Order{askOrder(9, coin(10, "apple"), coin(50, "prune"), false)}, + bidOrders: []*Order{bidOrder(10, coin(15, "apple"), coin(90, "prune"), false)}, + sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, + expErr: "cannot fill bid order 10 having assets \"15apple\" with \"10apple\": order does not allow partial fill", + }, + { + name: "one ask filled by five bids", + askOrders: []*Order{askOrder(21, coin(12, "apple"), coin(60, "prune"), false)}, + bidOrders: []*Order{ + bidOrder(22, coin(1, "apple"), coin(10, "prune"), false), + bidOrder(22, coin(1, "apple"), coin(12, "prune"), false), + bidOrder(22, coin(2, "apple"), coin(1, "prune"), false), + bidOrder(22, coin(3, "apple"), coin(15, "prune"), false), + bidOrder(22, coin(5, "apple"), coin(25, "prune"), false), + }, + sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, + expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { + rv := &Fulfillments{ + AskOFs: []*OrderFulfillment{ + filledOF(askOrders[0], 63, + []*OrderSplit{ + {Assets: coin(1, "apple"), Price: coin(10, "prune")}, // Will be BidOFs[0]. + {Assets: coin(1, "apple"), Price: coin(12, "prune")}, // Will be BidOFs[1]. + {Assets: coin(2, "apple"), Price: coin(1, "prune")}, // Will be BidOFs[2]. + {Assets: coin(3, "apple"), Price: coin(15, "prune")}, // Will be BidOFs[3]. + {Assets: coin(5, "apple"), Price: coin(25, "prune")}, // Will be BidOFs[4]. + }, + coins(13, "fig"), + ), + }, + BidOFs: []*OrderFulfillment{ + filledOF(bidOrders[0], 0, + []*OrderSplit{{Assets: coin(1, "apple"), Price: coin(10, "prune")}}, // Will be AskOFs[0]. + ), + filledOF(bidOrders[1], 0, + []*OrderSplit{{Assets: coin(1, "apple"), Price: coin(12, "prune")}}, // Will be AskOFs[0]. + ), + filledOF(bidOrders[2], 0, + []*OrderSplit{{Assets: coin(2, "apple"), Price: coin(1, "prune")}}, // Will be AskOFs[0]. + + ), + filledOF(bidOrders[3], 0, + []*OrderSplit{{Assets: coin(3, "apple"), Price: coin(15, "prune")}}, // Will be AskOFs[0]. + ), + filledOF(bidOrders[4], 0, + []*OrderSplit{{Assets: coin(5, "apple"), Price: coin(25, "prune")}}, // Will be AskOFs[0]. + ), + }, + } + + rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] + rv.AskOFs[0].Splits[1].Order = rv.BidOFs[1] + rv.AskOFs[0].Splits[2].Order = rv.BidOFs[2] + rv.AskOFs[0].Splits[3].Order = rv.BidOFs[3] + rv.AskOFs[0].Splits[4].Order = rv.BidOFs[4] + + rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] + rv.BidOFs[1].Splits[0].Order = rv.AskOFs[0] + rv.BidOFs[2].Splits[0].Order = rv.AskOFs[0] + rv.BidOFs[3].Splits[0].Order = rv.AskOFs[0] + rv.BidOFs[4].Splits[0].Order = rv.AskOFs[0] + + return rv + }, + }, + { + name: "one indivisible bid filled by five asks", + askOrders: []*Order{ + askOrder(31, coin(1, "apple"), coin(16, "prune"), false), + askOrder(33, coin(1, "apple"), coin(16, "prune"), false), + askOrder(35, coin(1, "apple"), coin(17, "prune"), false), + askOrder(37, coin(13, "apple"), coin(209, "prune"), false), + askOrder(39, coin(15, "apple"), coin(241, "prune"), false), + }, + bidOrders: []*Order{bidOrder(30, coin(31, "apple"), coin(500, "prune"), false)}, + sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, + expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { + rv := &Fulfillments{ + AskOFs: []*OrderFulfillment{ + filledOF(askOrders[0], 17, + []*OrderSplit{{Assets: coin(1, "apple"), Price: coin(17, "prune")}}, // Will be BidOfs[0] + coins(4, "fig"), + ), + filledOF(askOrders[1], 0, + []*OrderSplit{{Assets: coin(1, "apple"), Price: coin(16, "prune")}}, // Will be BidOfs[0] + coins(4, "fig"), + ), + filledOF(askOrders[2], 0, + []*OrderSplit{{Assets: coin(1, "apple"), Price: coin(17, "prune")}}, // Will be BidOfs[0] + coins(4, "fig"), + ), + filledOF(askOrders[3], 0, + []*OrderSplit{{Assets: coin(13, "apple"), Price: coin(209, "prune")}}, // Will be BidOfs[0] + coins(42, "fig"), + ), + filledOF(askOrders[4], 0, + []*OrderSplit{{Assets: coin(15, "apple"), Price: coin(241, "prune")}}, // Will be BidOfs[0] + coins(49, "fig"), + ), + }, + BidOFs: []*OrderFulfillment{ + { + Order: bidOrders[0], + AssetsFilledAmt: sdkmath.NewInt(31), + AssetsUnfilledAmt: ZeroAmtAfterSub, + PriceAppliedAmt: sdkmath.NewInt(500), + PriceLeftAmt: ZeroAmtAfterSub, + Splits: []*OrderSplit{ + {Assets: coin(1, "apple"), Price: coin(17, "prune")}, // Will be AskOFs[0] + {Assets: coin(1, "apple"), Price: coin(16, "prune")}, // Will be AskOFs[1] + {Assets: coin(1, "apple"), Price: coin(17, "prune")}, // Will be AskOFs[2] + {Assets: coin(13, "apple"), Price: coin(209, "prune")}, // Will be AskOFs[3] + {Assets: coin(15, "apple"), Price: coin(241, "prune")}, // Will be AskOFs[4] + }, + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(500), + PriceUnfilledAmt: sdkmath.NewInt(0), + }, + }, + } + + rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] + rv.AskOFs[1].Splits[0].Order = rv.BidOFs[0] + rv.AskOFs[2].Splits[0].Order = rv.BidOFs[0] + rv.AskOFs[3].Splits[0].Order = rv.BidOFs[0] + rv.AskOFs[4].Splits[0].Order = rv.BidOFs[0] + + rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] + rv.BidOFs[0].Splits[1].Order = rv.AskOFs[1] + rv.BidOFs[0].Splits[2].Order = rv.AskOFs[2] + rv.BidOFs[0].Splits[3].Order = rv.AskOFs[3] + rv.BidOFs[0].Splits[4].Order = rv.AskOFs[4] + + return rv + }, + }, + { + name: "three asks three bids, each fully fills the other", + askOrders: []*Order{ + askOrder(51, coin(8, "apple"), coin(55, "prune"), false, coin(18, "grape")), + askOrder(53, coin(12, "apple"), coin(18, "prune"), false, coin(1, "grape")), + askOrder(55, coin(344, "apple"), coin(12345, "prune"), false, coin(99, "grape")), + }, + bidOrders: []*Order{ + bidOrder(52, coin(8, "apple"), coin(55, "prune"), false, coin(3, "fig")), + bidOrder(54, coin(12, "apple"), coin(18, "prune"), false, coin(7, "fig")), + bidOrder(56, coin(344, "apple"), coin(12345, "prune"), false, coin(2, "fig")), + }, + expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { + rv := &Fulfillments{ + AskOFs: []*OrderFulfillment{ + filledOF(askOrders[0], 0, + []*OrderSplit{{Assets: coin(8, "apple"), Price: coin(55, "prune")}}, // Will be BidOFs[0] + coins(18, "grape"), + ), + filledOF(askOrders[1], 0, + []*OrderSplit{{Assets: coin(12, "apple"), Price: coin(18, "prune")}}, // Will be BidOFs[1] + coins(1, "grape"), + ), + filledOF(askOrders[2], 0, + []*OrderSplit{{Assets: coin(344, "apple"), Price: coin(12345, "prune")}}, // Will be BidOFs[2] + coins(99, "grape"), + ), + }, + BidOFs: []*OrderFulfillment{ + filledOF(bidOrders[0], 0, + []*OrderSplit{{Assets: coin(8, "apple"), Price: coin(55, "prune")}}, // Will be AskOFs[0] + coins(3, "fig"), + ), + filledOF(bidOrders[1], 0, + []*OrderSplit{{Assets: coin(12, "apple"), Price: coin(18, "prune")}}, // Will be AskOFs[1] + coins(7, "fig"), + ), + filledOF(bidOrders[2], 0, + []*OrderSplit{{Assets: coin(344, "apple"), Price: coin(12345, "prune")}}, // Will be AskOFs[2] + coins(2, "fig"), + ), + }, + PartialOrder: nil, + } + + rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] + rv.AskOFs[1].Splits[0].Order = rv.BidOFs[1] + rv.AskOFs[2].Splits[0].Order = rv.BidOFs[2] + + rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] + rv.BidOFs[1].Splits[0].Order = rv.AskOFs[1] + rv.BidOFs[2].Splits[0].Order = rv.AskOFs[2] + + return rv + }, + }, + { + name: "three asks two bids, all fully filled", + askOrders: []*Order{ + askOrder(11, coin(10, "apple"), coin(50, "prune"), false), + askOrder(13, coin(20, "apple"), coin(100, "prune"), false), + askOrder(15, coin(50, "apple"), coin(250, "prune"), false), + }, + bidOrders: []*Order{ + bidOrder(12, coin(23, "apple"), coin(115, "prune"), false), + bidOrder(14, coin(57, "apple"), coin(285, "prune"), false), + }, + sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, + expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { + rv := &Fulfillments{ + AskOFs: []*OrderFulfillment{ + filledOF(askOrders[0], 0, + []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(50, "prune")}}, // Will be BidOFs[0] + coins(10, "fig"), + ), + filledOF(askOrders[1], 0, + []*OrderSplit{ + {Assets: coin(13, "apple"), Price: coin(65, "prune")}, // Will be BidOFs[0] + {Assets: coin(7, "apple"), Price: coin(35, "prune")}, // Will be BidOFs[1] + }, + coins(20, "fig"), + ), + filledOF(askOrders[2], 0, + []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(250, "prune")}}, // Will be BidOFs[1] + coins(50, "fig"), + ), + }, + BidOFs: []*OrderFulfillment{ + filledOF(bidOrders[0], 0, + []*OrderSplit{ + {Assets: coin(10, "apple"), Price: coin(50, "prune")}, // Will be AskOfs[0] + {Assets: coin(13, "apple"), Price: coin(65, "prune")}, // Will be AskOfs[1] + }, + ), + filledOF(bidOrders[1], 0, + []*OrderSplit{ + {Assets: coin(7, "apple"), Price: coin(35, "prune")}, // Will be AskOFs[1] + {Assets: coin(50, "apple"), Price: coin(250, "prune")}, // Will be AskOFs[2] + }, + ), + }, + } + + rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] + rv.AskOFs[1].Splits[0].Order = rv.BidOFs[0] + rv.AskOFs[1].Splits[1].Order = rv.BidOFs[1] + rv.AskOFs[2].Splits[0].Order = rv.BidOFs[1] + rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] + rv.BidOFs[0].Splits[1].Order = rv.AskOFs[1] + rv.BidOFs[1].Splits[0].Order = rv.AskOFs[1] + rv.BidOFs[1].Splits[1].Order = rv.AskOFs[2] + + return rv + }, + }, + { + name: "three asks two bids, ask partially filled", + askOrders: []*Order{ + askOrder(73, coin(10, "apple"), coin(50, "prune"), false), + askOrder(75, coin(15, "apple"), coin(75, "prune"), false), + askOrder(77, coin(25, "apple"), coin(125, "prune"), true), + }, + bidOrders: []*Order{ + bidOrder(74, coin(5, "apple"), coin(25, "prune"), false), + bidOrder(76, coin(40, "apple"), coin(200, "prune"), false), + }, + expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { + rv := &Fulfillments{ + AskOFs: []*OrderFulfillment{ + filledOF(askOrders[0], 0, + []*OrderSplit{ + {Assets: coin(5, "apple"), Price: coin(25, "prune")}, // Will be BidOFs[0] + {Assets: coin(5, "apple"), Price: coin(25, "prune")}, // Will be BidOFs[1] + }, + ), + filledOF(askOrders[1], 0, + []*OrderSplit{{Assets: coin(15, "apple"), Price: coin(75, "prune")}}, // Will be BidOFs[1] + ), + { + Order: askOrders[2], + AssetsFilledAmt: sdkmath.NewInt(20), + AssetsUnfilledAmt: sdkmath.NewInt(5), + PriceAppliedAmt: sdkmath.NewInt(100), + PriceLeftAmt: sdkmath.NewInt(25), + Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(100, "prune")}}, // Will be BidOFs[1], + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(100), + PriceUnfilledAmt: sdkmath.NewInt(25), + }, + }, + BidOFs: []*OrderFulfillment{ + filledOF(bidOrders[0], 0, + []*OrderSplit{{Assets: coin(5, "apple"), Price: coin(25, "prune")}}, // Will be AskOFs[0], + ), + filledOF(bidOrders[1], 0, + []*OrderSplit{ + {Assets: coin(5, "apple"), Price: coin(25, "prune")}, // Will be AskOFs[0], + {Assets: coin(15, "apple"), Price: coin(75, "prune")}, // Will be AskOFs[1], + {Assets: coin(20, "apple"), Price: coin(100, "prune")}, // Will be AskOFs[2], + }, + ), + }, + PartialOrder: &PartialFulfillment{ + NewOrder: askOrder(77, coin(5, "apple"), coin(25, "prune"), true), + AssetsFilled: coin(20, "apple"), + PriceFilled: coin(100, "prune"), + }, + } + + rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] + rv.AskOFs[0].Splits[1].Order = rv.BidOFs[1] + rv.AskOFs[1].Splits[0].Order = rv.BidOFs[1] + rv.AskOFs[2].Splits[0].Order = rv.BidOFs[1] + + rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] + rv.BidOFs[1].Splits[0].Order = rv.AskOFs[0] + rv.BidOFs[1].Splits[1].Order = rv.AskOFs[1] + rv.BidOFs[1].Splits[2].Order = rv.AskOFs[2] + + return rv + }, + }, + { + name: "three asks two bids, ask partially filled not allowed", + askOrders: []*Order{ + askOrder(73, coin(10, "apple"), coin(50, "prune"), false), + askOrder(75, coin(15, "apple"), coin(75, "prune"), false), + askOrder(77, coin(25, "apple"), coin(125, "prune"), false), + }, + bidOrders: []*Order{ + bidOrder(74, coin(5, "apple"), coin(25, "prune"), false), + bidOrder(76, coin(40, "apple"), coin(200, "prune"), false), + }, + expErr: "cannot fill ask order 77 having assets \"25apple\" with \"20apple\": order does not allow partial fill", + }, + { + name: "three asks two bids, bid partially filled", + askOrders: []*Order{ + askOrder(121, coin(55, "apple"), coin(275, "prune"), false), + askOrder(123, coin(12, "apple"), coin(60, "prune"), false), + askOrder(125, coin(13, "apple"), coin(65, "prune"), false), + }, + bidOrders: []*Order{ + bidOrder(124, coin(65, "apple"), coin(325, "prune"), false), + bidOrder(126, coin(20, "apple"), coin(100, "prune"), true), + }, + expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { + rv := &Fulfillments{ + AskOFs: []*OrderFulfillment{ + filledOF(askOrders[0], 0, + []*OrderSplit{{Assets: coin(55, "apple"), Price: coin(275, "prune")}}, // Will be BidOFs[0] + ), + filledOF(askOrders[1], 0, + []*OrderSplit{ + {Assets: coin(10, "apple"), Price: coin(50, "prune")}, // Will be BidOFs[0] + {Assets: coin(2, "apple"), Price: coin(10, "prune")}, // Will be BidOFs[1] + }, + ), + filledOF(askOrders[2], 0, + []*OrderSplit{{Assets: coin(13, "apple"), Price: coin(65, "prune")}}, // Will be BidOFs[1] + ), + }, + BidOFs: []*OrderFulfillment{ + filledOF(bidOrders[0], 0, + []*OrderSplit{ + {Assets: coin(55, "apple"), Price: coin(275, "prune")}, // Will be AskOFs[0] + {Assets: coin(10, "apple"), Price: coin(50, "prune")}, // Will be AskOFs[1] + }, + ), + { + Order: bidOrders[1], + AssetsFilledAmt: sdkmath.NewInt(15), + AssetsUnfilledAmt: sdkmath.NewInt(5), + PriceAppliedAmt: sdkmath.NewInt(75), + PriceLeftAmt: sdkmath.NewInt(25), + Splits: []*OrderSplit{ + {Assets: coin(2, "apple"), Price: coin(10, "prune")}, // Will be AskOFs[1] + {Assets: coin(13, "apple"), Price: coin(65, "prune")}, // Will be AskOFs[2] + }, + IsFinalized: true, + PriceFilledAmt: sdkmath.NewInt(75), + PriceUnfilledAmt: sdkmath.NewInt(25), + }, + }, + PartialOrder: &PartialFulfillment{ + NewOrder: bidOrder(126, coin(5, "apple"), coin(25, "prune"), true), + AssetsFilled: coin(15, "apple"), + PriceFilled: coin(75, "prune"), + }, + } + + rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] + rv.AskOFs[1].Splits[0].Order = rv.BidOFs[0] + rv.AskOFs[1].Splits[1].Order = rv.BidOFs[1] + rv.AskOFs[2].Splits[0].Order = rv.BidOFs[1] + + rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] + rv.BidOFs[0].Splits[1].Order = rv.AskOFs[1] + rv.BidOFs[1].Splits[0].Order = rv.AskOFs[1] + rv.BidOFs[1].Splits[1].Order = rv.AskOFs[2] + + return rv + }, + }, + { + name: "ask order in bid order list", + askOrders: []*Order{ + askOrder(1, coin(1, "apple"), coin(1, "prune"), false), + bidOrder(2, coin(1, "apple"), coin(1, "prune"), false), + askOrder(3, coin(1, "apple"), coin(1, "prune"), false), + }, + bidOrders: []*Order{bidOrder(4, coin(3, "apple"), coin(3, "prune"), false)}, + expErr: "bid order 2 is not an ask order but is in the askOrders list", + }, + { + name: "bid order in ask order list", + askOrders: []*Order{askOrder(4, coin(3, "apple"), coin(3, "prune"), false)}, + bidOrders: []*Order{ + bidOrder(1, coin(1, "apple"), coin(1, "prune"), false), + askOrder(2, coin(1, "apple"), coin(1, "prune"), false), + bidOrder(3, coin(1, "apple"), coin(1, "prune"), false), + }, + expErr: "ask order 2 is not a bid order but is in the bidOrders list", + }, + // neither filled in full - I'm not sure how I can trigger this. + { + name: "ask finalize error", + askOrders: []*Order{askOrder(15, coin(13, "apple"), coin(17, "prune"), true)}, + bidOrders: []*Order{bidOrder(16, coin(5, "apple"), coin(20, "prune"), true)}, + expErr: "ask order 15 having assets \"13apple\" cannot be partially filled by \"5apple\": price \"17prune\" is not evenly divisible", + }, + { + name: "bid finalize error", + askOrders: []*Order{askOrder(15, coin(5, "apple"), coin(5, "prune"), true)}, + bidOrders: []*Order{bidOrder(16, coin(13, "apple"), coin(17, "prune"), true)}, + expErr: "bid order 16 having assets \"13apple\" cannot be partially filled by \"5apple\": price \"17prune\" is not evenly divisible", + }, + { + name: "validate error", + askOrders: []*Order{askOrder(123, coin(5, "apple"), coin(6, "prune"), true)}, + bidOrders: []*Order{bidOrder(124, coin(5, "apple"), coin(5, "prune"), true)}, + expErr: "ask order 123 having assets \"5apple\" and price \"6prune\" cannot be filled by \"5apple\" at price \"5prune\": insufficient price", + }, + { + name: "nil askOrders", + askOrders: nil, + bidOrders: []*Order{bidOrder(124, coin(5, "apple"), coin(5, "prune"), true)}, + expErr: "no assets filled in bid order 124", + }, + { + name: "empty askOrders", + askOrders: []*Order{}, + bidOrders: []*Order{bidOrder(124, coin(5, "apple"), coin(5, "prune"), true)}, + expErr: "no assets filled in bid order 124", + }, + { + name: "nil bidOrders", + askOrders: []*Order{askOrder(123, coin(5, "apple"), coin(6, "prune"), true)}, + bidOrders: nil, + expErr: "no assets filled in ask order 123", + }, + { + name: "empty bidOrders", + askOrders: []*Order{askOrder(123, coin(5, "apple"), coin(6, "prune"), true)}, + bidOrders: []*Order{}, + expErr: "no assets filled in ask order 123", + }, + { + name: "ask not filled at all", // this gets caught by Finalize. + askOrders: []*Order{ + askOrder(123, coin(10, "apple"), coin(10, "prune"), true), + askOrder(125, coin(5, "apple"), coin(5, "prune"), true), + }, + bidOrders: []*Order{bidOrder(124, coin(5, "apple"), coin(5, "prune"), true)}, + expErr: "no assets filled in ask order 125", + }, + { + name: "bid not filled at all", // this gets caught by Finalize. + askOrders: []*Order{askOrder(123, coin(5, "apple"), coin(5, "prune"), true)}, + bidOrders: []*Order{ + bidOrder(122, coin(10, "apple"), coin(10, "prune"), true), + bidOrder(124, coin(5, "apple"), coin(5, "prune"), true), + }, + expErr: "no assets filled in bid order 124", + }, + // both ask and bid partially filled - I'm not sure how to trigger this. + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var expected *Fulfillments + if tc.expectedMaker != nil { + expected = tc.expectedMaker(t, tc.askOrders, tc.bidOrders) + } + var actual *Fulfillments + var err error + testFunc := func() { + actual, err = BuildFulfillments(tc.askOrders, tc.bidOrders, tc.sellerFeeRatio) + } + require.NotPanics(t, testFunc, "BuildFulfillments") + assertions.AssertErrorValue(t, err, tc.expErr, "BuildFulfillments error") + if !assert.Equal(t, expected, actual, "BuildFulfillments result") && expected != nil && actual != nil { + // Try to help identify the error in the failure logs. + expAskOFs := orderFulfillmentsString(expected.AskOFs) + actAskOFs := orderFulfillmentsString(actual.AskOFs) + if assert.Equal(t, expAskOFs, actAskOFs, "AskOFs") { + // Some difference don't come through in the strings, so dig even deeper. + for i := range expected.AskOFs { + assertEqualOrderFulfillments(t, expected.AskOFs[i], actual.AskOFs[i], "AskOFs[%d]", i) + } + } + expBidOFs := orderFulfillmentsString(expected.BidOFs) + actBidOFs := orderFulfillmentsString(actual.BidOFs) + if assert.Equal(t, expBidOFs, actBidOFs, "BidOFs") { + for i := range expected.BidOFs { + assertEqualOrderFulfillments(t, expected.BidOFs[i], actual.BidOFs[i], "BidOFs[%d]", i) + } + } + expPartial := partialFulfillmentString(expected.PartialOrder) + actPartial := partialFulfillmentString(actual.PartialOrder) + assert.Equal(t, expPartial, actPartial, "PartialOrder") + } + }) + } +} // copyIndexedAddrAmts creates a deep copy of an indexedAddrAmts. func copyIndexedAddrAmts(orig *indexedAddrAmts) *indexedAddrAmts { From 29b498c39173bb9425807e986455e3e879d80b3c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 10:24:45 -0600 Subject: [PATCH 195/309] [1658]: Create a unit test for 2 asks 1 bid with amounts just off that currently fails, but should probablly be allowed. --- x/exchange/fulfillment_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 1d21134c1a..a8d06a88fe 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -4893,6 +4893,35 @@ func TestBuildFulfillments(t *testing.T) { return rv }, }, + { + // TODO[1658]: Either update the process or delete this 2 asks 1 bid unit test. + name: "two asks one bid", + askOrders: []*Order{ + askOrder(91, coin(10, "apple"), coin(49, "prune"), false), + askOrder(93, coin(10, "apple"), coin(51, "prune"), false), + }, + bidOrders: []*Order{bidOrder(92, coin(20, "apple"), coin(100, "prune"), false)}, + expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { + return &Fulfillments{ + AskOFs: []*OrderFulfillment{ + filledOF(askOrders[0], 0, + []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(49, "prune")}}, // Will be BidOFs[0] + ), + filledOF(askOrders[1], 0, + []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(51, "prune")}}, // Will be BidOFs[0] + ), + }, + BidOFs: []*OrderFulfillment{ + filledOF(askOrders[1], 0, + []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(49, "prune")}}, // Will be AskOFs[0] + ), + filledOF(askOrders[1], 0, + []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(51, "prune")}}, // Will be AskOFs[1] + ), + }, + } + }, + }, { name: "ask order in bid order list", askOrders: []*Order{ From 19cd79ad1912df8e8fa0ac8df5ac0a9c9771616f Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 10:31:11 -0600 Subject: [PATCH 196/309] [1658]: Create shells of InitGenesis and ExportGenesis. --- x/exchange/keeper/genesis.go | 17 +++++++++++++++++ x/exchange/module/module.go | 10 +++------- 2 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 x/exchange/keeper/genesis.go diff --git a/x/exchange/keeper/genesis.go b/x/exchange/keeper/genesis.go new file mode 100644 index 0000000000..5c4874275f --- /dev/null +++ b/x/exchange/keeper/genesis.go @@ -0,0 +1,17 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/provenance-io/provenance/x/exchange" +) + +func (k Keeper) InitGenesis(origCtx sdk.Context, genState *exchange.GenesisState) { + // TODO[1658]: Implement InitGenesis + panic("not implemented") +} + +func (k Keeper) ExportGenesis(ctx sdk.Context) *exchange.GenesisState { + // TODO[1658]: Implement ExportGenesis + panic("not implemented") +} diff --git a/x/exchange/module/module.go b/x/exchange/module/module.go index 08aad77917..d73d6301da 100644 --- a/x/exchange/module/module.go +++ b/x/exchange/module/module.go @@ -108,18 +108,14 @@ func (am AppModule) LegacyQuerierHandler(_ *codec.LegacyAmino) sdk.Querier { ret func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { var genesisState exchange.GenesisState cdc.MustUnmarshalJSON(data, &genesisState) - // TODO[1658]: Create keeper.InitGenesis - panic("not implemented") - //am.keeper.InitGenesis(ctx, &genesisState) + am.keeper.InitGenesis(ctx, &genesisState) return []abci.ValidatorUpdate{} } // ExportGenesis returns the exported genesis state as raw bytes for the exchange module. func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { - // TODO[1658]: Create keeper.ExportGenesis - panic("not implemented") - //gs := am.keeper.ExportGenesis(ctx) - //return cdc.MustMarshalJSON(gs) + gs := am.keeper.ExportGenesis(ctx) + return cdc.MustMarshalJSON(gs) } // RegisterServices registers module services. From d2ef8df03ce136ebbcb3a522fe73e5ec91c6abe7 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 12:57:19 -0600 Subject: [PATCH 197/309] [1658]: Lint fix and some TODOs for uint parsers. --- x/exchange/helpers.go | 1 + x/exchange/keeper/keys.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/x/exchange/helpers.go b/x/exchange/helpers.go index d01a08e0c9..e940e57e71 100644 --- a/x/exchange/helpers.go +++ b/x/exchange/helpers.go @@ -2,6 +2,7 @@ package exchange import ( sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index a9022c6fe6..5712dd56ce 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -133,6 +133,7 @@ func uint16Bz(val uint16) []byte { // uint16FromBz converts the provided bytes into a uint16. func uint16FromBz(bz []byte) uint16 { + // TODO[1658]: Check the length first and return an error here. return binary.BigEndian.Uint16(bz) } @@ -145,6 +146,7 @@ func uint32Bz(val uint32) []byte { // uint32FromBz converts the provided bytes into a uint32. func uint32FromBz(bz []byte) uint32 { + // TODO[1658]: Check the length first and return an error here. return binary.BigEndian.Uint32(bz) } @@ -157,6 +159,7 @@ func uint64Bz(val uint64) []byte { // uint64FromBz converts the provided bytes into a uint64. func uint64FromBz(bz []byte) uint64 { + // TODO[1658]: Check the length first and return an error here. return binary.BigEndian.Uint64(bz) } From b10c1e7ccc7f961e181f106e3be1178336472468 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 13:22:00 -0600 Subject: [PATCH 198/309] [1658]: Implement InitGenesis and ExportGenesis. Move NormalizeReqAttrs out of the keeper. Make GetMarket less reliant on the existence of the account. --- x/exchange/keeper/genesis.go | 47 +++++++++-- x/exchange/keeper/keeper.go | 22 ++--- x/exchange/keeper/market.go | 151 +++++++++++++++++++++++------------ x/exchange/keeper/orders.go | 26 ++++++ x/exchange/market.go | 14 ++++ x/exchange/market_test.go | 55 +++++++++++++ 6 files changed, 244 insertions(+), 71 deletions(-) diff --git a/x/exchange/keeper/genesis.go b/x/exchange/keeper/genesis.go index 5c4874275f..f7d56533a5 100644 --- a/x/exchange/keeper/genesis.go +++ b/x/exchange/keeper/genesis.go @@ -1,17 +1,54 @@ package keeper import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/provenance-io/provenance/x/exchange" ) -func (k Keeper) InitGenesis(origCtx sdk.Context, genState *exchange.GenesisState) { - // TODO[1658]: Implement InitGenesis - panic("not implemented") +// InitGenesis writes the provided genesis state to the state store. +func (k Keeper) InitGenesis(ctx sdk.Context, genState *exchange.GenesisState) { + if genState == nil { + return + } + + k.SetParams(ctx, genState.Params) + + store := k.getStore(ctx) + for _, market := range genState.Markets { + k.initMarket(ctx, store, market) + } + + setLastAutoMarketID(store, genState.LastMarketId) + + for i, order := range genState.Orders { + if err := k.setOrderInStore(store, order); err != nil { + panic(fmt.Errorf("failed to store Orders[%d]: %w", i, err)) + } + } } +// ExportGenesis creates a genesis state from the current state store. func (k Keeper) ExportGenesis(ctx sdk.Context) *exchange.GenesisState { - // TODO[1658]: Implement ExportGenesis - panic("not implemented") + genState := &exchange.GenesisState{ + Params: k.GetParams(ctx), + LastMarketId: getLastAutoMarketID(k.getStore(ctx)), + } + + k.IterateMarkets(ctx, func(market *exchange.Market) bool { + genState.Markets = append(genState.Markets, *market) + return false + }) + + err := k.IterateOrders(ctx, func(order *exchange.Order) bool { + genState.Orders = append(genState.Orders, *order) + return false + }) + if err != nil { + k.logErrorf(ctx, "error (ignored) while reading orders: %v", err) + } + + return genState } diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 7b5a4a66ef..e2110472db 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -1,7 +1,6 @@ package keeper import ( - "errors" "fmt" sdkmath "cosmossdk.io/math" @@ -15,7 +14,6 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/provenance-io/provenance/x/exchange" - nametypes "github.com/provenance-io/provenance/x/name/types" ) type Keeper struct { @@ -50,6 +48,12 @@ func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, feeCollector return rv } +// logErrorf uses fmt.Sprintf to combine the msg and args, and logs the result as an error from this module. +// Note that this is different from the logging .Error(msg string, keyvals ...interface{}) syntax. +func (k Keeper) logErrorf(ctx sdk.Context, msg string, args ...interface{}) { + ctx.Logger().Error(fmt.Sprintf(msg, args...), "module", "x/"+exchange.ModuleName) +} + // GetAuthority gets the address (as bech32) that has governance authority. func (k Keeper) GetAuthority() string { return k.authority @@ -115,20 +119,6 @@ func (k Keeper) iterate(ctx sdk.Context, pre []byte, cb func(key, value []byte) iterate(k.getStore(ctx), pre, cb) } -// NormalizeReqAttrs normalizes/validates each of the provided require attributes. -// The normalized versions of the attributes are returned regardless of whether an error is also returned. -func (k Keeper) NormalizeReqAttrs(ctx sdk.Context, reqAttrs []string) ([]string, error) { - rv := make([]string, len(reqAttrs)) - var errs []error - for i, attr := range reqAttrs { - rv[i] = nametypes.NormalizeName(attr) - if !nametypes.IsValidName(rv[i]) { - errs = append(errs, fmt.Errorf("invalid attribute %q", attr)) - } - } - return rv, errors.Join(errs...) -} - // CalculateExchangeSplit calculates the amount that the exchange will keep of the provided fee. func (k Keeper) CalculateExchangeSplit(ctx sdk.Context, feeAmt sdk.Coins) sdk.Coins { exchangeAmt := make(sdk.Coins, 0, len(feeAmt)) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index c1b1322751..22817d6872 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -13,15 +13,15 @@ import ( "github.com/provenance-io/provenance/x/exchange" ) -// TODO[1658]: Recheck all the public funcs in here to make sure they're still needed. +// TODO[1658]: Recheck all the public functions in here to make sure they're still needed. -// flatFeeKeyMakers are the key and prefix maker funcs for a specific flat fee entry. +// flatFeeKeyMakers are the key and prefix maker functions for a specific flat fee entry. type flatFeeKeyMakers struct { key func(marketID uint32, denom string) []byte prefix func(marketID uint32) []byte } -// ratioKeyMakers are the key and prefix maker funcs for a specific ratio fee entry. +// ratioKeyMakers are the key and prefix maker functions for a specific ratio fee entry. type ratioKeyMakers struct { key func(marketID uint32, ratio exchange.FeeRatio) []byte prefix func(marketID uint32) []byte @@ -799,8 +799,8 @@ func setAccessGrants(store sdk.KVStore, marketID uint32, grants []exchange.Acces // reqAttrKeyMaker is a function that returns a key for required attributes. type reqAttrKeyMaker func(marketID uint32) []byte -// getReqAttr gets the required attributes for a market using the provided key maker. -func getReqAttr(store sdk.KVStore, marketID uint32, maker reqAttrKeyMaker) []string { +// getReqAttrs gets the required attributes for a market using the provided key maker. +func getReqAttrs(store sdk.KVStore, marketID uint32, maker reqAttrKeyMaker) []string { key := maker(marketID) value := store.Get(key) return ParseReqAttrStoreValue(value) @@ -819,7 +819,7 @@ func setReqAttr(store sdk.KVStore, marketID uint32, reqAttrs []string, maker req // getReqAttrAsk gets the attributes required to create an ask order. func getReqAttrAsk(store sdk.KVStore, marketID uint32) []string { - return getReqAttr(store, marketID, MakeKeyMarketReqAttrAsk) + return getReqAttrs(store, marketID, MakeKeyMarketReqAttrAsk) } // GetReqAttrAsk gets the attributes required to create an ask order. @@ -834,7 +834,7 @@ func setReqAttrAsk(store sdk.KVStore, marketID uint32, reqAttrs []string) { // getReqAttrBid gets the attributes required to create a bid order. func getReqAttrBid(store sdk.KVStore, marketID uint32) []string { - return getReqAttr(store, marketID, MakeKeyMarketReqAttrBid) + return getReqAttrs(store, marketID, MakeKeyMarketReqAttrBid) } // GetReqAttrBid gets the attributes required to create a bid order. @@ -901,64 +901,113 @@ func (k Keeper) GetAllMarketIDs(ctx sdk.Context) []uint32 { return rv } +// IterateMarkets iterates over all markets. +// The callback should return whether to stop, i.e. true = stop iterating, false = keep going. +func (k Keeper) IterateMarkets(ctx sdk.Context, cb func(market *exchange.Market) bool) { + k.iterate(ctx, GetKeyPrefixKnownMarketID(), func(key, _ []byte) bool { + marketID := ParseKeySuffixKnownMarketID(key) + market := k.GetMarket(ctx, marketID) + if market == nil { + return false + } + return cb(market) + }) +} + +// storeMarket writes all the market fields to the state store (except MarketDetails which are in the account). +func storeMarket(store sdk.KVStore, market exchange.Market) { + marketID := market.MarketId + setMarketKnown(store, marketID) + setCreateAskFlatFees(store, marketID, market.FeeCreateAskFlat) + setCreateBidFlatFees(store, marketID, market.FeeCreateBidFlat) + setSellerSettlementFlatFees(store, marketID, market.FeeSellerSettlementFlat) + setSellerSettlementRatios(store, marketID, market.FeeSellerSettlementRatios) + setBuyerSettlementFlatFees(store, marketID, market.FeeBuyerSettlementFlat) + setBuyerSettlementRatios(store, marketID, market.FeeBuyerSettlementRatios) + setMarketActive(store, marketID, market.AcceptingOrders) + setUserSettlementAllowed(store, marketID, market.AllowUserSettlement) + setAccessGrants(store, marketID, market.AccessGrants) + setReqAttrAsk(store, marketID, market.ReqAttrCreateAsk) + setReqAttrBid(store, marketID, market.ReqAttrCreateBid) +} + // CreateMarket saves a new market to the store with all the info provided. // If the marketId is zero, the next available one will be used. func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (marketID uint32, err error) { defer func() { + // TODO[1658]: Figure out why this recover is needed and either add a comment or delete this defer. if r := recover(); r != nil { - if e, ok := r.(error); ok { - err = fmt.Errorf("could not set market: %w", e) - } else { - err = fmt.Errorf("could not set market: %v", r) - } + err = fmt.Errorf("could not create market: %v", r) } }() + // Note: The Market is passed in by value, so any alterations to it here will be lost upon return. + var errAsk, errBid error + market.ReqAttrCreateAsk, errAsk = exchange.NormalizeReqAttrs(market.ReqAttrCreateAsk) + market.ReqAttrCreateBid, errBid = exchange.NormalizeReqAttrs(market.ReqAttrCreateBid) + if errAsk != nil || errBid != nil { + return 0, errors.Join(errAsk, errBid) + } + if market.MarketId == 0 { market.MarketId = k.NextMarketID(ctx) } - marketID = market.MarketId - marketAddr := exchange.GetMarketAddress(marketID) + marketAddr := exchange.GetMarketAddress(market.MarketId) if k.accountKeeper.HasAccount(ctx, marketAddr) { - return 0, fmt.Errorf("market id %d %s already exists", marketID, marketAddr) - } - - reqAttrCreateAsk, errAsk := k.NormalizeReqAttrs(ctx, market.ReqAttrCreateAsk) - reqAttrCreateBid, errBid := k.NormalizeReqAttrs(ctx, market.ReqAttrCreateAsk) - err = errors.Join(errAsk, errBid) - if err != nil { - return 0, err + return 0, fmt.Errorf("market id %d account %s already exists", market.MarketId, marketAddr) } marketAcc := &exchange.MarketAccount{ BaseAccount: &authtypes.BaseAccount{Address: marketAddr.String()}, - MarketId: marketID, + MarketId: market.MarketId, MarketDetails: market.MarketDetails, } k.accountKeeper.NewAccount(ctx, marketAcc) k.accountKeeper.SetAccount(ctx, marketAcc) - store := k.getStore(ctx) - setMarketKnown(store, marketID) - setCreateAskFlatFees(store, marketID, market.FeeCreateAskFlat) - setCreateBidFlatFees(store, marketID, market.FeeCreateBidFlat) - setSellerSettlementFlatFees(store, marketID, market.FeeSellerSettlementFlat) - setSellerSettlementRatios(store, marketID, market.FeeSellerSettlementRatios) - setBuyerSettlementFlatFees(store, marketID, market.FeeBuyerSettlementFlat) - setBuyerSettlementRatios(store, marketID, market.FeeBuyerSettlementRatios) - setMarketActive(store, marketID, market.AcceptingOrders) - setUserSettlementAllowed(store, marketID, market.AllowUserSettlement) - setAccessGrants(store, marketID, market.AccessGrants) - setReqAttrAsk(store, marketID, reqAttrCreateAsk) - setReqAttrBid(store, marketID, reqAttrCreateBid) + storeMarket(k.getStore(ctx), market) + + return market.MarketId, nil +} + +// initMarket is similar to CreateMarket but assumes the market has already been +// validated and also allows for the market account to already exist. +func (k Keeper) initMarket(ctx sdk.Context, store sdk.KVStore, market exchange.Market) { + if market.MarketId == 0 { + market.MarketId = k.NextMarketID(ctx) + } + marketID := market.MarketId - return marketID, nil + marketAddr := exchange.GetMarketAddress(marketID) + marketAcc := k.getMarketAccountByAddr(ctx, marketAddr) + if marketAcc != nil { + if !market.MarketDetails.Equal(marketAcc.MarketDetails) { + marketAcc.MarketDetails = market.MarketDetails + k.accountKeeper.SetAccount(ctx, marketAcc) + } + } else { + marketAcc = &exchange.MarketAccount{ + BaseAccount: &authtypes.BaseAccount{Address: marketAddr.String()}, + MarketId: marketID, + MarketDetails: market.MarketDetails, + } + k.accountKeeper.NewAccount(ctx, marketAcc) + k.accountKeeper.SetAccount(ctx, marketAcc) + } + + storeMarket(store, market) } // GetMarketAccount gets a market's account from the account module. func (k Keeper) GetMarketAccount(ctx sdk.Context, marketID uint32) *exchange.MarketAccount { marketAddr := exchange.GetMarketAddress(marketID) + return k.getMarketAccountByAddr(ctx, marketAddr) +} + +// getMarketAccountByAddr gets a market's account given it's address. +// This is for when you've already called exchange.GetMarketAddress(marketID) and need it for other things too. +func (k Keeper) getMarketAccountByAddr(ctx sdk.Context, marketAddr sdk.AccAddress) *exchange.MarketAccount { acc := k.accountKeeper.GetAccount(ctx, marketAddr) if acc == nil { return nil @@ -973,14 +1022,12 @@ func (k Keeper) GetMarketAccount(ctx sdk.Context, marketID uint32) *exchange.Mar // GetMarket reads all the market info from state and returns it. // Returns nil if the market account doesn't exist or it's not a market account. func (k Keeper) GetMarket(ctx sdk.Context, marketID uint32) *exchange.Market { - marketAcc := k.GetMarketAccount(ctx, marketID) - if marketAcc == nil { + store := k.getStore(ctx) + if err := validateMarketExists(store, marketID); err != nil { return nil } - store := k.getStore(ctx) market := &exchange.Market{MarketId: marketID} - market.MarketDetails = marketAcc.MarketDetails market.FeeCreateAskFlat = getCreateAskFlatFees(store, marketID) market.FeeCreateBidFlat = getCreateBidFlatFees(store, marketID) market.FeeSellerSettlementFlat = getSellerSettlementFlatFees(store, marketID) @@ -993,6 +1040,10 @@ func (k Keeper) GetMarket(ctx sdk.Context, marketID uint32) *exchange.Market { market.ReqAttrCreateAsk = getReqAttrAsk(store, marketID) market.ReqAttrCreateBid = getReqAttrBid(store, marketID) + if marketAcc := k.GetMarketAccount(ctx, marketID); marketAcc != nil { + market.MarketDetails = marketAcc.MarketDetails + } + return market } @@ -1103,7 +1154,7 @@ func (k Keeper) UpdatePermissions(ctx sdk.Context, msg *exchange.MsgMarketManage // the provided attributes to the existing entries. func updateReqAttrs(store sdk.KVStore, marketID uint32, toRemove, toAdd []string, field string, maker reqAttrKeyMaker) error { var errs []error - curAttrs := getReqAttr(store, marketID, maker) + curAttrs := getReqAttrs(store, marketID, maker) for _, attr := range toRemove { if !exchange.ContainsString(curAttrs, attr) { @@ -1111,16 +1162,16 @@ func updateReqAttrs(store sdk.KVStore, marketID uint32, toRemove, toAdd []string } } - var newAttrs []string + var updatedAttrs []string for _, attr := range curAttrs { if !exchange.ContainsString(toRemove, attr) { - newAttrs = append(newAttrs, attr) + updatedAttrs = append(updatedAttrs, attr) } } for _, attr := range toAdd { if !exchange.ContainsString(curAttrs, attr) { - newAttrs = append(newAttrs, attr) + updatedAttrs = append(updatedAttrs, attr) } else { errs = append(errs, fmt.Errorf("cannot add %s required attribute %q: attribute already required", field, attr)) } @@ -1130,7 +1181,7 @@ func updateReqAttrs(store sdk.KVStore, marketID uint32, toRemove, toAdd []string return errors.Join(errs...) } - setReqAttr(store, marketID, newAttrs, maker) + setReqAttr(store, marketID, updatedAttrs, maker) return nil } @@ -1156,13 +1207,13 @@ func (k Keeper) UpdateReqAttrs(ctx sdk.Context, msg *exchange.MsgMarketManageReq var errs []error // We don't care if the attributes to remove are valid so that we // can remove entries that are somehow now invalid. - askToRemove, _ := k.NormalizeReqAttrs(ctx, msg.CreateAskToRemove) - askToAdd, err := k.NormalizeReqAttrs(ctx, msg.CreateAskToAdd) + askToRemove, _ := exchange.NormalizeReqAttrs(msg.CreateAskToRemove) + askToAdd, err := exchange.NormalizeReqAttrs(msg.CreateAskToAdd) if err != nil { errs = append(errs, err) } - bidToRemove, _ := k.NormalizeReqAttrs(ctx, msg.CreateBidToRemove) - bidToAdd, err := k.NormalizeReqAttrs(ctx, msg.CreateBidToAdd) + bidToRemove, _ := exchange.NormalizeReqAttrs(msg.CreateBidToRemove) + bidToAdd, err := exchange.NormalizeReqAttrs(msg.CreateBidToAdd) if err != nil { errs = append(errs, err) } diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index a818a7b6b3..65a7ca139e 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -71,6 +71,16 @@ func (k Keeper) parseOrderStoreValue(orderID uint64, value []byte) (*exchange.Or } } +// parseOrderStoreKeyValue parses the provided key and value into an exchange.Order. +// The key's leading type byte is optional, only the last 8 bytes of it are used. +func (k Keeper) parseOrderStoreKeyValue(key, value []byte) (*exchange.Order, error) { + if len(key) < 8 { + return nil, fmt.Errorf("invalid order store key %v: length expected to be at least 8", key) + } + orderID := uint64FromBz(key[len(key)-8:]) + return k.parseOrderStoreValue(orderID, value) +} + // createIndexEntries creates all the key/value index entries for an order. func createIndexEntries(order exchange.Order) []sdk.KVPair { marketID := order.GetMarketID() @@ -161,6 +171,22 @@ func (k Keeper) getNextOrderID(ctx sdk.Context) uint64 { return 1 } +// IterateOrders iterates over all orders. An error is returned if there was a problem +// reading an entry along the way. Such a problem does not interrupt iteration. +// The callback should return whether to stop. I.e. false = keep going, true = stop iterating. +func (k Keeper) IterateOrders(ctx sdk.Context, cb func(order *exchange.Order) bool) error { + var errs []error + k.iterate(ctx, GetKeyPrefixOrder(), func(key, value []byte) bool { + order, err := k.parseOrderStoreKeyValue(key, value) + if err != nil { + errs = append(errs, err) + return false + } + return cb(order) + }) + return errors.Join(errs...) +} + // IterateMarketOrders iterates over all orders for a market. // The callback takes in the order id and order type byte and should return whether to stop iterating. func (k Keeper) IterateMarketOrders(ctx sdk.Context, marketID uint32, cb func(orderID uint64, orderTypeByte byte) bool) { diff --git a/x/exchange/market.go b/x/exchange/market.go index 60e212412b..78bac15c43 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -442,6 +442,20 @@ func ParsePermissions(permissions ...string) ([]Permission, error) { return rv, errors.Join(errs...) } +// NormalizeReqAttrs normalizes/validates each of the provided require attributes. +// The normalized versions of the attributes are returned regardless of whether an error is also returned. +func NormalizeReqAttrs(reqAttrs []string) ([]string, error) { + rv := make([]string, len(reqAttrs)) + var errs []error + for i, attr := range reqAttrs { + rv[i] = nametypes.NormalizeName(attr) + if !IsValidReqAttr(rv[i]) { + errs = append(errs, fmt.Errorf("invalid attribute %q", attr)) + } + } + return rv, errors.Join(errs...) +} + // ValidateReqAttrs makes sure that each provided attribute is valid and that no duplicate entries are provided. func ValidateReqAttrs(field string, attrs []string) error { var errs []error diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 38fa86b563..1d0524b568 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -2451,6 +2451,61 @@ func TestParsePermissions(t *testing.T) { } } +func TestNormalizeReqAttrs(t *testing.T) { + tests := []struct { + name string + reqAttrs []string + expAttrs []string + expErr string + }{ + { + name: "whitespace fixed", + reqAttrs: []string{" ab . cd . ef ", "cd . ef", " * . jk "}, + expAttrs: []string{"ab.cd.ef", "cd.ef", "*.jk"}, + }, + { + name: "casing fixed", + reqAttrs: []string{"AB.cD.Ef", "*.PQ"}, + expAttrs: []string{"ab.cd.ef", "*.pq"}, + }, + { + name: "some problems", + reqAttrs: []string{"ab.c d.ef", "X.*.Y", " a-B-c .d"}, + expAttrs: []string{"ab.c d.ef", "x.*.y", "a-b-c.d"}, + expErr: `invalid attribute "ab.c d.ef"` + "\n" + + `invalid attribute "X.*.Y"` + "\n" + + `invalid attribute " a-B-c .d"`, + }, + { + name: "two good one bad", + reqAttrs: []string{" AB .cd", "l,M.n.o,p", " *.x.Y.z"}, + expAttrs: []string{"ab.cd", "l,m.n.o,p", "*.x.y.z"}, + expErr: `invalid attribute "l,M.n.o,p"`, + }, + { + // Unlike ValidateReqAttrs, this one doesn't care about dups or duplicated errors. + name: "duplicated entries", + reqAttrs: []string{"*.x.y.z", "*.x.y.z", "a.b.*.d", "a.b.*.d"}, + expAttrs: []string{"*.x.y.z", "*.x.y.z", "a.b.*.d", "a.b.*.d"}, + expErr: `invalid attribute "a.b.*.d"` + "\n" + + `invalid attribute "a.b.*.d"`, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var attrs []string + var err error + testFunc := func() { + attrs, err = NormalizeReqAttrs(tc.reqAttrs) + } + require.NotPanics(t, testFunc, "NormalizeReqAttrs(%q)", tc.reqAttrs) + assertions.AssertErrorValue(t, err, tc.expErr, "NormalizeReqAttrs(%q) error", tc.reqAttrs) + assert.Equal(t, tc.expAttrs, attrs, "NormalizeReqAttrs(%q) result", tc.reqAttrs) + }) + } +} + func TestValidateReqAttrs(t *testing.T) { joinErrs := func(errs ...string) string { return strings.Join(errs, "\n") From a299620a5f6bc440d76ddb1bf311f1a8e4a2c6b1 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 14:19:43 -0600 Subject: [PATCH 199/309] [1658]: Check sizes of orders for when we're reading them. --- x/exchange/keeper/orders.go | 4 +- x/exchange/orders_test.go | 167 ++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 1 deletion(-) diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 65a7ca139e..64ecd1e292 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -13,7 +13,9 @@ import ( // getOrderStoreKeyValue creates the store key and value representing the provided order. func (k Keeper) getOrderStoreKeyValue(order exchange.Order) ([]byte, []byte, error) { // 200 chosen to hopefully be more than what's needed for 99% of orders. - // TODO[1658]: Marshal some ask and bid orders to get their actual sizes and make sure 200 is okay. + // The largest one I could make was 753 bytes for a bid order with all coins having 128 + // character denoms and 31 digits in the amounts, a 32 byte address, and max market id. + // But the more realistic ones were 130 to 160, so 200 seems like a nice size to start with. key := MakeKeyOrder(order.OrderId) value := make([]byte, 1, 200) value[0] = order.GetOrderTypeByte() diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index dc763d7a05..67fe4c7152 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -276,6 +276,173 @@ func TestValidateOrderIDs(t *testing.T) { } } +func TestOrderSizes(t *testing.T) { + // This unit test is mostly just to see the sizes of different orders and compare + // that to the initial array size used in getOrderStoreKeyValue. + expectedLength := 199 // = the 200 set in getOrderStoreKeyValue minus one for the order type byte. + + denomChars := "abcd" + bigCoins := make(sdk.Coins, len(denomChars)) + for i := range bigCoins { + str := fmt.Sprintf("%[1]d00000000000000000000000000000%[1]d", i) // i quetta + i + amount, ok := sdkmath.NewIntFromString(str) + require.Truef(t, ok, "sdkmath.NewIntFromString(%q)", str) + denom := strings.Repeat(denomChars[i:i+1], 128) + bigCoins[i] = sdk.NewCoin(denom, amount) + } + coinP := func(denom string, amount int64) *sdk.Coin { + rv := sdk.NewInt64Coin(denom, amount) + return &rv + } + + tests := []struct { + name string + order *Order + expTooLong bool + }{ + { + name: "ask, normal", + order: NewOrder(1).WithAsk(&AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("seller______________").String(), + Assets: sdk.NewInt64Coin("pm.sale.pool.sxyvff21dz5rrpamteeiin", 1), + Price: sdk.NewInt64Coin("usd", 1_000_000), + SellerSettlementFlatFee: coinP("nhash", 1_000_000_000_000), + AllowPartial: true, + }), + expTooLong: false, + }, + { + name: "ask, max market id", + order: NewOrder(1).WithAsk(&AskOrder{ + MarketId: 4_294_967_295, + Seller: sdk.AccAddress("seller______________").String(), + Assets: sdk.NewInt64Coin("pm.sale.pool.sxyvff21dz5rrpamteeiin", 1), + Price: sdk.NewInt64Coin("usd", 1_000_000), + SellerSettlementFlatFee: coinP("nhash", 1_000_000_000_000), + AllowPartial: true, + }), + expTooLong: false, + }, + { + name: "ask, 32 byte addr", + order: NewOrder(1).WithAsk(&AskOrder{ + MarketId: 1, + Seller: sdk.AccAddress("seller__________________________").String(), + Assets: sdk.NewInt64Coin("pm.sale.pool.sxyvff21dz5rrpamteeiin", 1), + Price: sdk.NewInt64Coin("usd", 1_000_000), + SellerSettlementFlatFee: coinP("nhash", 1_000_000_000_000), + AllowPartial: true, + }), + expTooLong: false, + }, + { + name: "ask, big", + order: NewOrder(1).WithAsk(&AskOrder{ + MarketId: 4_294_967_295, + Seller: sdk.AccAddress("seller__________________________").String(), + Assets: bigCoins[0], + Price: bigCoins[1], + SellerSettlementFlatFee: &bigCoins[2], + AllowPartial: true, + }), + expTooLong: true, + }, + { + name: "bid, normal", + order: NewOrder(1).WithBid(&BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("buyer_______________").String(), + Assets: sdk.NewInt64Coin("pm.sale.pool.sxyvff21dz5rrpamteeiin", 1), + Price: sdk.NewInt64Coin("usd", 1_000_000), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("nhash", 1_000_000_000_000)), + AllowPartial: true, + }), + expTooLong: false, + }, + { + name: "bid, max market id", + order: NewOrder(1).WithBid(&BidOrder{ + MarketId: 4_294_967_295, + Buyer: sdk.AccAddress("buyer_______________").String(), + Assets: sdk.NewInt64Coin("pm.sale.pool.sxyvff21dz5rrpamteeiin", 1), + Price: sdk.NewInt64Coin("usd", 1_000_000), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("nhash", 1_000_000_000_000)), + AllowPartial: true, + }), + expTooLong: false, + }, + { + name: "bid, 32 byte addr", + order: NewOrder(1).WithBid(&BidOrder{ + MarketId: 1, + Buyer: sdk.AccAddress("buyer___________________________").String(), + Assets: sdk.NewInt64Coin("pm.sale.pool.sxyvff21dz5rrpamteeiin", 1), + Price: sdk.NewInt64Coin("usd", 1_000_000), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("nhash", 1_000_000_000_000)), + AllowPartial: true, + }), + expTooLong: false, + }, + { + name: "bid, big", + order: NewOrder(1).WithBid(&BidOrder{ + MarketId: 4_294_967_295, + Buyer: sdk.AccAddress("buyer___________________________").String(), + Assets: bigCoins[0], + Price: bigCoins[1], + BuyerSettlementFees: bigCoins[2:], + AllowPartial: true, + }), + expTooLong: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var data []byte + var err error + switch { + case tc.order.IsAskOrder(): + askOrder := tc.order.GetAskOrder() + fields := []string{ + fmt.Sprintf("MarketID: %d", askOrder.MarketId), + fmt.Sprintf("Seller: %q", askOrder.Seller), + fmt.Sprintf("Assets: %q", askOrder.Assets), + fmt.Sprintf("Price: %q", askOrder.Price), + fmt.Sprintf("AllowPartial: %t", askOrder.AllowPartial), + fmt.Sprintf("SellerSettlementFlatFee: %s", coinPString(askOrder.SellerSettlementFlatFee)), + } + t.Logf("AskOrder:\n%s\n", strings.Join(fields, "\n")) + data, err = askOrder.Marshal() + case tc.order.IsBidOrder(): + bidOrder := tc.order.GetBidOrder() + fields := []string{ + fmt.Sprintf("MarketID: %d", bidOrder.MarketId), + fmt.Sprintf("Buyer: %q", bidOrder.Buyer), + fmt.Sprintf("Assets: %q", bidOrder.Assets), + fmt.Sprintf("Price: %q", bidOrder.Price), + fmt.Sprintf("AllowPartial: %t", bidOrder.AllowPartial), + fmt.Sprintf("BuyerSettlementFees: %s", coinsString(bidOrder.BuyerSettlementFees)), + } + t.Logf("BidOrder:\n%s\n", strings.Join(fields, "\n")) + data, err = bidOrder.Marshal() + default: + t.Fatalf("unknown order type %T", tc.order.GetOrder()) + } + require.NoError(t, err, "%s.Marshal()", tc.order.GetOrderType()) + + length := len(data) + t.Logf("Data Length: %d", length) + if tc.expTooLong { + assert.Greater(t, length, expectedLength, "%s.Marshal() length", tc.order.GetOrderType()) + } else { + assert.LessOrEqual(t, len(data), expectedLength, "%s.Marshal() length", tc.order.GetOrderType()) + } + }) + } +} + func TestNewOrder(t *testing.T) { tests := []struct { name string From dc7b97d805ec6da78f84bdae29de3f47cef56a48 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 14:30:43 -0600 Subject: [PATCH 200/309] [1658]: Create the query server with stubs and wire it into the module. --- x/exchange/keeper/grpc_query.go | 78 +++++++++++++++++++++++++++++++++ x/exchange/module/module.go | 3 +- 2 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 x/exchange/keeper/grpc_query.go diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go new file mode 100644 index 0000000000..01024d97e7 --- /dev/null +++ b/x/exchange/keeper/grpc_query.go @@ -0,0 +1,78 @@ +package keeper + +import ( + "context" + + "github.com/provenance-io/provenance/x/exchange" +) + +// QueryServer is an alias for a Keeper that implements the exchange.QueryServer interface. +type QueryServer struct { + Keeper +} + +func NewQueryServer(k Keeper) exchange.QueryServer { + return QueryServer{Keeper: k} +} + +var _ exchange.QueryServer = QueryServer{} + +// QueryOrderFeeCalc calculates the fees that will be associated with the provided order. +func (k QueryServer) QueryOrderFeeCalc(goCtx context.Context, req *exchange.QueryOrderFeeCalcRequest) (*exchange.QueryOrderFeeCalcResponse, error) { + // TODO[1658]: Implement QueryOrderFeeCalc query + panic("not implemented") +} + +// QuerySettlementFeeCalc calculates the fees that will be associated with the provided settlement. +func (k QueryServer) QuerySettlementFeeCalc(goCtx context.Context, req *exchange.QuerySettlementFeeCalcRequest) (*exchange.QuerySettlementFeeCalcResponse, error) { + // TODO[1658]: Implement QuerySettlementFeeCalc query + panic("not implemented") +} + +// QueryGetOrder looks up an order by id. +func (k QueryServer) QueryGetOrder(goCtx context.Context, req *exchange.QueryGetOrderRequest) (*exchange.QueryGetOrderResponse, error) { + // TODO[1658]: Implement QueryGetOrder query + panic("not implemented") +} + +// QueryGetMarketOrders looks up the orders in a market. +func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.QueryGetMarketOrdersRequest) (*exchange.QueryGetMarketOrdersResponse, error) { + // TODO[1658]: Implement XXX query + panic("not implemented") +} + +// QueryGetAddressOrders looks up the orders from the provided address. +func (k QueryServer) QueryGetAddressOrders(goCtx context.Context, req *exchange.QueryGetAddressOrdersRequest) (*exchange.QueryGetAddressOrdersResponse, error) { + // TODO[1658]: Implement XXX query + panic("not implemented") +} + +// QueryGetAllOrders gets all orders in the exchange module. +func (k QueryServer) QueryGetAllOrders(goCtx context.Context, req *exchange.QueryGetAllOrdersRequest) (*exchange.QueryGetAllOrdersResponse, error) { + // TODO[1658]: Implement XXX query + panic("not implemented") +} + +// QueryMarketInfo returns the information/details about a market. +func (k QueryServer) QueryMarketInfo(goCtx context.Context, req *exchange.QueryMarketInfoRequest) (*exchange.QueryMarketInfoResponse, error) { + // TODO[1658]: Implement XXX query + panic("not implemented") +} + +// QueryParams returns the exchange module parameters. +func (k QueryServer) QueryParams(goCtx context.Context, req *exchange.QueryParamsRequest) (*exchange.QueryParamsResponse, error) { + // TODO[1658]: Implement XXX query + panic("not implemented") +} + +// QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. +func (k QueryServer) QueryValidateCreateMarket(goCtx context.Context, req *exchange.QueryValidateCreateMarketRequest) (*exchange.QueryValidateCreateMarketResponse, error) { + // TODO[1658]: Implement XXX query + panic("not implemented") +} + +// QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. +func (k QueryServer) QueryValidateManageFees(goCtx context.Context, req *exchange.QueryValidateManageFeesRequest) (*exchange.QueryValidateManageFeesResponse, error) { + // TODO[1658]: Implement XXX query + panic("not implemented") +} diff --git a/x/exchange/module/module.go b/x/exchange/module/module.go index d73d6301da..48de88f3ec 100644 --- a/x/exchange/module/module.go +++ b/x/exchange/module/module.go @@ -121,8 +121,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw // RegisterServices registers module services. func (am AppModule) RegisterServices(cfg module.Configurator) { exchange.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServer(am.keeper)) - // TODO[1658]: Uncomment this once the keeper implements the query server. - // exchange.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServer(am.keeper)) + exchange.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServer(am.keeper)) } // ConsensusVersion implements AppModule/ConsensusVersion. From c5a7203f48902aa1b62f27bff91d9b82de092424 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 14:54:12 -0600 Subject: [PATCH 201/309] [1658]: Have uint16FromBz, uint32FromBz, and uint64FromBz return a bool instead of panicking. --- x/exchange/keeper/keys.go | 42 ++++---- x/exchange/keeper/keys_test.go | 170 +++++---------------------------- x/exchange/keeper/market.go | 13 ++- x/exchange/keeper/orders.go | 15 ++- x/exchange/keeper/params.go | 7 +- 5 files changed, 71 insertions(+), 176 deletions(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 5712dd56ce..68cea474cf 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -132,9 +132,12 @@ func uint16Bz(val uint16) []byte { } // uint16FromBz converts the provided bytes into a uint16. -func uint16FromBz(bz []byte) uint16 { - // TODO[1658]: Check the length first and return an error here. - return binary.BigEndian.Uint16(bz) +// Returned boolean indicates whether conversion was successful (true = okay). +func uint16FromBz(bz []byte) (uint16, bool) { + if len(bz) >= 2 { + return binary.BigEndian.Uint16(bz), true + } + return 0, false } // uint32Bz converts the provided uint32 value to a big-endian byte slice of length 4. @@ -145,9 +148,12 @@ func uint32Bz(val uint32) []byte { } // uint32FromBz converts the provided bytes into a uint32. -func uint32FromBz(bz []byte) uint32 { - // TODO[1658]: Check the length first and return an error here. - return binary.BigEndian.Uint32(bz) +// Returned boolean indicates whether conversion was successful (true = okay). +func uint32FromBz(bz []byte) (uint32, bool) { + if len(bz) >= 4 { + return binary.BigEndian.Uint32(bz), true + } + return 0, false } // uint64Bz converts the provided uint64 value to a big-endian byte slice of length 8. @@ -158,9 +164,11 @@ func uint64Bz(val uint64) []byte { } // uint64FromBz converts the provided bytes into a uint64. -func uint64FromBz(bz []byte) uint64 { - // TODO[1658]: Check the length first and return an error here. - return binary.BigEndian.Uint64(bz) +func uint64FromBz(bz []byte) (uint64, bool) { + if len(bz) >= 8 { + return binary.BigEndian.Uint64(bz), true + } + return 0, false } // parseLengthPrefixedAddr extracts the length-prefixed sdk.AccAddress from the front of the provided slice. @@ -224,7 +232,8 @@ func MakeKeyKnownMarketID(marketID uint32) []byte { // ParseKeySuffixKnownMarketID parses the market id out of a known market id key that doesn't have the type byte. // Input is expected to have the format . -func ParseKeySuffixKnownMarketID(suffix []byte) uint32 { +// Returned boolean indicates whether parsing was successful (true = okay). +func ParseKeySuffixKnownMarketID(suffix []byte) (uint32, bool) { return uint32FromBz(suffix) } @@ -533,11 +542,6 @@ func MakeKeyOrder(orderID uint64) []byte { return rv } -// ParseKeySuffixOrder parses the order id from the bytes part of an order store key. -func ParseKeySuffixOrder(suffix []byte) uint64 { - return uint64FromBz(suffix) -} - // indexPrefixMarketToOrder creates the prefix for the market to order prefix entries with some extra space for the rest. func indexPrefixMarketToOrder(marketID uint32, extraCap int) []byte { return prepKey(KeyTypeMarketToOrderIndex, uint32Bz(marketID), extraCap) @@ -583,9 +587,9 @@ func ParseIndexKeyMarketToOrder(key []byte) (uint32, uint64, error) { var marketID uint32 if len(marketIDBz) > 0 { - marketID = uint32FromBz(marketIDBz) + marketID, _ = uint32FromBz(marketIDBz) } - orderID := uint64FromBz(orderIDBz) + orderID, _ := uint64FromBz(orderIDBz) return marketID, orderID, nil } @@ -621,7 +625,7 @@ func ParseIndexKeyAddressToOrder(key []byte) (sdk.AccAddress, uint64, error) { return nil, 0, fmt.Errorf("cannot parse address to order index key: only has %d bytes, expected at least 8", len(key)) } pre, orderIDBz := key[:len(key)-8], key[len(key)-8:] - orderID := uint64FromBz(orderIDBz) + orderID, _ := uint64FromBz(orderIDBz) var addr sdk.AccAddress if len(pre) > 0 { // Either the first byte is a length byte, or it's the key type byte. @@ -694,7 +698,7 @@ func ParseIndexKeyAssetToOrder(key []byte) (string, byte, uint64, error) { unparsed, orderIDBz := key[:len(key)-8], key[len(key)-8:] var denom string var typeByte byte - orderID := uint64FromBz(orderIDBz) + orderID, _ := uint64FromBz(orderIDBz) if len(unparsed) > 0 { typeByte = unparsed[len(unparsed)-1] unparsed = unparsed[:len(unparsed)-1] diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index aa54b400a5..fc3372c0e1 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -344,35 +344,35 @@ func TestMakeKeyKnownMarketID(t *testing.T) { func TestParseKeySuffixKnownMarketID(t *testing.T) { tests := []struct { - name string - suffix []byte - exp uint32 - expPanic string + name string + suffix []byte + exp uint32 + expFail bool }{ { - name: "nil suffix", - suffix: nil, - expPanic: "runtime error: index out of range [3] with length 0", + name: "nil suffix", + suffix: nil, + expFail: true, }, { - name: "empty suffix", - suffix: []byte{}, - expPanic: "runtime error: index out of range [3] with length 0", + name: "empty suffix", + suffix: []byte{}, + expFail: true, }, { - name: "1 byte suffix", - suffix: []byte{1}, - expPanic: "runtime error: index out of range [3] with length 1", + name: "1 byte suffix", + suffix: []byte{1}, + expFail: true, }, { - name: "2 byte suffix", - suffix: []byte{1, 2}, - expPanic: "runtime error: index out of range [3] with length 2", + name: "2 byte suffix", + suffix: []byte{1, 2}, + expFail: true, }, { - name: "3 byte suffix", - suffix: []byte{1, 2, 3}, - expPanic: "runtime error: index out of range [3] with length 3", + name: "3 byte suffix", + suffix: []byte{1, 2, 3}, + expFail: true, }, { name: "market id 16,909,060 but with extra byte", @@ -424,10 +424,12 @@ func TestParseKeySuffixKnownMarketID(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { var marketID uint32 + var ok bool testFunc := func() { - marketID = keeper.ParseKeySuffixKnownMarketID(tc.suffix) + marketID, ok = keeper.ParseKeySuffixKnownMarketID(tc.suffix) } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "ParseKeySuffixKnownMarketID") + require.NotPanics(t, testFunc, "ParseKeySuffixKnownMarketID") + assert.Equal(t, !tc.expFail, ok, "ParseKeySuffixKnownMarketID ok bool") assert.Equal(t, tc.exp, marketID, "ParseKeySuffixKnownMarketID result") }) } @@ -2820,132 +2822,6 @@ func TestMakeKeyOrder(t *testing.T) { } } -func TestParseKeySuffixOrder(t *testing.T) { - tests := []struct { - name string - suffix []byte - exp uint64 - expPanic string - }{ - { - name: "nil suffix", - suffix: nil, - expPanic: "runtime error: index out of range [7] with length 0", - }, - { - name: "empty suffix", - suffix: []byte{}, - expPanic: "runtime error: index out of range [7] with length 0", - }, - { - name: "1 byte suffix", - suffix: []byte{1}, - expPanic: "runtime error: index out of range [7] with length 1", - }, - { - name: "2 byte suffix", - suffix: []byte{1, 2}, - expPanic: "runtime error: index out of range [7] with length 2", - }, - { - name: "3 byte suffix", - suffix: []byte{1, 2, 3}, - expPanic: "runtime error: index out of range [7] with length 3", - }, - { - name: "4 byte suffix", - suffix: []byte{1, 2, 3, 4}, - expPanic: "runtime error: index out of range [7] with length 4", - }, - { - name: "5 byte suffix", - suffix: []byte{1, 2, 3, 4, 5}, - expPanic: "runtime error: index out of range [7] with length 5", - }, - { - name: "6 byte suffix", - suffix: []byte{1, 2, 3, 4, 5, 6}, - expPanic: "runtime error: index out of range [7] with length 6", - }, - { - name: "7 byte suffix", - suffix: []byte{1, 2, 3, 4, 5, 6, 7}, - expPanic: "runtime error: index out of range [7] with length 7", - }, - { - name: "order id 72,623,859,790,382,856 with an extra byte", - suffix: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, - exp: 72_623_859_790_382_856, - }, - { - name: "order id 72,623,859,790,382,856", - suffix: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - exp: 72_623_859_790_382_856, - }, - { - name: "order id 1", - suffix: []byte{0, 0, 0, 0, 0, 0, 0, 1}, - exp: 1, - }, - { - name: "order id 256", - suffix: []byte{0, 0, 0, 0, 0, 0, 1, 0}, - exp: 256, - }, - { - name: "order id 65,536", - suffix: []byte{0, 0, 0, 0, 0, 1, 0, 0}, - exp: 65_536, - }, - { - name: "order id 16,777,216", - suffix: []byte{0, 0, 0, 0, 1, 0, 0, 0}, - exp: 16_777_216, - }, - { - name: "order id 4,294,967,296", - suffix: []byte{0, 0, 0, 1, 0, 0, 0, 0}, - exp: 4_294_967_296, - }, - { - name: "order id 1,099,511,627,776", - suffix: []byte{0, 0, 1, 0, 0, 0, 0, 0}, - exp: 1_099_511_627_776, - }, - { - name: "order id 281,474,976,710,656", - suffix: []byte{0, 1, 0, 0, 0, 0, 0, 0}, - exp: 281_474_976_710_656, - }, - { - name: "order id 72,057,594,037,927,936", - suffix: []byte{1, 0, 0, 0, 0, 0, 0, 0}, - exp: 72_057_594_037_927_936, - }, - { - name: "order id 144,680,345,676,153,346", - suffix: []byte{2, 2, 2, 2, 2, 2, 2, 2}, - exp: 144_680_345_676_153_346, - }, - { - name: "order id max uint64", - suffix: []byte{255, 255, 255, 255, 255, 255, 255, 255}, - exp: 18_446_744_073_709_551_615, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var orderID uint64 - testFunc := func() { - orderID = keeper.ParseKeySuffixOrder(tc.suffix) - } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "ParseKeySuffixOrder(%v)", tc.suffix) - assert.Equal(t, tc.exp, orderID, "ParseKeySuffixOrder(%v) result", tc.suffix) - }) - } -} - func TestGetIndexKeyPrefixMarketToOrder(t *testing.T) { tests := []struct { name string diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 22817d6872..528e66bc91 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -851,7 +851,8 @@ func setReqAttrBid(store sdk.KVStore, marketID uint32, reqAttrs []string) { func getLastAutoMarketID(store sdk.KVStore) uint32 { key := MakeKeyLastMarketID() value := store.Get(key) - return uint32FromBz(value) + rv, _ := uint32FromBz(value) + return rv } // setLastAutoMarketID sets the last auto-selected market id to the provided value. @@ -895,7 +896,10 @@ func validateMarketExists(store sdk.KVStore, marketID uint32) error { func (k Keeper) GetAllMarketIDs(ctx sdk.Context) []uint32 { var rv []uint32 k.iterate(ctx, GetKeyPrefixKnownMarketID(), func(key, _ []byte) bool { - rv = append(rv, ParseKeySuffixKnownMarketID(key)) + marketID, ok := ParseKeySuffixKnownMarketID(key) + if ok { + rv = append(rv, marketID) + } return false }) return rv @@ -905,7 +909,10 @@ func (k Keeper) GetAllMarketIDs(ctx sdk.Context) []uint32 { // The callback should return whether to stop, i.e. true = stop iterating, false = keep going. func (k Keeper) IterateMarkets(ctx sdk.Context, cb func(market *exchange.Market) bool) { k.iterate(ctx, GetKeyPrefixKnownMarketID(), func(key, _ []byte) bool { - marketID := ParseKeySuffixKnownMarketID(key) + marketID, ok := ParseKeySuffixKnownMarketID(key) + if !ok { + return false + } market := k.GetMarket(ctx, marketID) if market == nil { return false diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 64ecd1e292..fd615b93f2 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -79,7 +79,7 @@ func (k Keeper) parseOrderStoreKeyValue(key, value []byte) (*exchange.Order, err if len(key) < 8 { return nil, fmt.Errorf("invalid order store key %v: length expected to be at least 8", key) } - orderID := uint64FromBz(key[len(key)-8:]) + orderID, _ := uint64FromBz(key[len(key)-8:]) return k.parseOrderStoreValue(orderID, value) } @@ -165,12 +165,17 @@ func (k Keeper) getNextOrderID(ctx sdk.Context) uint64 { store := prefix.NewStore(k.getStore(ctx), GetKeyPrefixOrder()) iter := store.ReverseIterator(nil, nil) defer iter.Close() - if iter.Valid() { + + toAdd := uint64(1) + for ; iter.Valid(); iter.Next() { orderIDBz := iter.Key() - orderID := uint64FromBz(orderIDBz) - return orderID + 1 + orderID, ok := uint64FromBz(orderIDBz) + if ok { + return orderID + toAdd + } + toAdd++ } - return 1 + return toAdd } // IterateOrders iterates over all orders. An error is returned if there was a problem diff --git a/x/exchange/keeper/params.go b/x/exchange/keeper/params.go index 20f4d7abc3..dcaa467c6f 100644 --- a/x/exchange/keeper/params.go +++ b/x/exchange/keeper/params.go @@ -26,7 +26,7 @@ func getParamsSplit(store sdk.KVStore, denom string) (uint16, bool) { key := MakeKeyParamsSplit(denom) if store.Has(key) { value := store.Get(key) - return uint16FromBz(value), true + return uint16FromBz(value) } return 0, false } @@ -49,11 +49,14 @@ func (k Keeper) SetParams(ctx sdk.Context, params *exchange.Params) { func (k Keeper) GetParams(ctx sdk.Context) *exchange.Params { var rv *exchange.Params k.iterate(ctx, GetKeyPrefixParamsSplit(), func(key, value []byte) bool { + split, ok := uint16FromBz(value) + if !ok { + return false + } if rv == nil { rv = &exchange.Params{} } denom := string(key) - split := uint16FromBz(value) if len(denom) == 0 { rv.DefaultSplit = uint32(split) } else { From 69ab8febdb94970d168c053548c04df44605ff32 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 14:55:52 -0600 Subject: [PATCH 202/309] [1658]: Fix some query server todos. --- x/exchange/keeper/grpc_query.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 01024d97e7..436dd6d5a4 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -37,42 +37,42 @@ func (k QueryServer) QueryGetOrder(goCtx context.Context, req *exchange.QueryGet // QueryGetMarketOrders looks up the orders in a market. func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.QueryGetMarketOrdersRequest) (*exchange.QueryGetMarketOrdersResponse, error) { - // TODO[1658]: Implement XXX query + // TODO[1658]: Implement QueryGetMarketOrders query panic("not implemented") } // QueryGetAddressOrders looks up the orders from the provided address. func (k QueryServer) QueryGetAddressOrders(goCtx context.Context, req *exchange.QueryGetAddressOrdersRequest) (*exchange.QueryGetAddressOrdersResponse, error) { - // TODO[1658]: Implement XXX query + // TODO[1658]: Implement QueryGetAddressOrders query panic("not implemented") } // QueryGetAllOrders gets all orders in the exchange module. func (k QueryServer) QueryGetAllOrders(goCtx context.Context, req *exchange.QueryGetAllOrdersRequest) (*exchange.QueryGetAllOrdersResponse, error) { - // TODO[1658]: Implement XXX query + // TODO[1658]: Implement QueryGetAllOrders query panic("not implemented") } // QueryMarketInfo returns the information/details about a market. func (k QueryServer) QueryMarketInfo(goCtx context.Context, req *exchange.QueryMarketInfoRequest) (*exchange.QueryMarketInfoResponse, error) { - // TODO[1658]: Implement XXX query + // TODO[1658]: Implement QueryMarketInfo query panic("not implemented") } // QueryParams returns the exchange module parameters. func (k QueryServer) QueryParams(goCtx context.Context, req *exchange.QueryParamsRequest) (*exchange.QueryParamsResponse, error) { - // TODO[1658]: Implement XXX query + // TODO[1658]: Implement QueryParams query panic("not implemented") } // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. func (k QueryServer) QueryValidateCreateMarket(goCtx context.Context, req *exchange.QueryValidateCreateMarketRequest) (*exchange.QueryValidateCreateMarketResponse, error) { - // TODO[1658]: Implement XXX query + // TODO[1658]: Implement QueryValidateCreateMarket query panic("not implemented") } // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. func (k QueryServer) QueryValidateManageFees(goCtx context.Context, req *exchange.QueryValidateManageFeesRequest) (*exchange.QueryValidateManageFeesResponse, error) { - // TODO[1658]: Implement XXX query + // TODO[1658]: Implement QueryValidateManageFees query panic("not implemented") } From bb847ca8582e22eeb256dda9bae2c8a48466827b Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 15:15:14 -0600 Subject: [PATCH 203/309] [1658]: Populate the QueryOrderFeeCalc messages. --- docs/proto-docs.md | 18 + proto/provenance/exchange/v1/query.proto | 31 +- x/exchange/query.pb.go | 441 ++++++++++++++++++++--- x/exchange/query.pb.gw.go | 18 + 4 files changed, 455 insertions(+), 53 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 85bbfa2129..cc4819fcc7 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2015,6 +2015,13 @@ QueryMarketInfoResponse is a response message for the QueryMarketInfo endpoint. ### QueryOrderFeeCalcRequest QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint. +Exactly one of ask_order or bid_order must be provided. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `ask_order` | [AskOrder](#provenance.exchange.v1.AskOrder) | | ask_order is the ask order to calculate the fees for. | +| `bid_order` | [BidOrder](#provenance.exchange.v1.BidOrder) | | bid_order is the bid order to calculate the fees for. | @@ -2027,6 +2034,17 @@ QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint QueryOrderFeeCalcResponse is a response message for the QueryOrderFeeCalc endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `creation_fee_options` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | creation_fee_options are the order creation flat fee options available for creating the provided order. If it's empty, no order creation fee is required. When creating the order, you should include exactly one of these. | +| `settlement_flat_fee_options` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | settlement_flat_fee_options are the settlement flat fee options available for the provided order. If it's empty, no settlement flat fee is required. When creating an order, you should include exactly one of these in the settlement fees field. | +| `settlement_ratio_fee_options` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | settlement_ratio_fee_options are the settlement ratio fee options available for the provided order. If it's empty, no settlement ratio fee is required. + +If the provided order was a bid order, you should include exactly one of these in the settlement fees field. If the flat and ratio options you've chose have the same denom, a single entry should be included with their sum. + +If the provided order was an ask order, these are purely informational and represent how much will be removed from your price if it settles at that price. If it settles for more, the actual amount will probably be larger. | + + diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 19d3ca9d8c..8e6fb6b08d 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -7,7 +7,10 @@ option java_package = "io.provenance.exchange.v1"; option java_multiple_files = true; import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; import "google/api/annotations.proto"; +import "gogoproto/gogo.proto"; +import "provenance/exchange/v1/orders.proto"; // Query is the service for exchange module's query endpoints. service Query { @@ -63,12 +66,36 @@ service Query { } // QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint. +// Exactly one of ask_order or bid_order must be provided. message QueryOrderFeeCalcRequest { - // TODO[1658]: QueryOrderFeeCalcRequest + // ask_order is the ask order to calculate the fees for. + AskOrder ask_order = 2; + // bid_order is the bid order to calculate the fees for. + BidOrder bid_order = 3; } + // QueryOrderFeeCalcResponse is a response message for the QueryOrderFeeCalc endpoint. message QueryOrderFeeCalcResponse { - // TODO[1658]: QueryOrderFeeCalcResponse + // creation_fee_options are the order creation flat fee options available for creating the provided order. + // If it's empty, no order creation fee is required. + // When creating the order, you should include exactly one of these. + repeated cosmos.base.v1beta1.Coin creation_fee_options = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + // settlement_flat_fee_options are the settlement flat fee options available for the provided order. + // If it's empty, no settlement flat fee is required. + // When creating an order, you should include exactly one of these in the settlement fees field. + repeated cosmos.base.v1beta1.Coin settlement_flat_fee_options = 2 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + // settlement_ratio_fee_options are the settlement ratio fee options available for the provided order. + // If it's empty, no settlement ratio fee is required. + // + // If the provided order was a bid order, you should include exactly one of these in the settlement fees field. + // If the flat and ratio options you've chose have the same denom, a single entry should be included with their sum. + // + // If the provided order was an ask order, these are purely informational and represent how much will be removed + // from your price if it settles at that price. If it settles for more, the actual amount will probably be larger. + repeated cosmos.base.v1beta1.Coin settlement_ratio_fee_options = 5 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; } // QuerySettlementFeeCalcRequest is a request message for the QuerySettlementFeeCalc endpoint. diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index fb71b0b949..6fcb967429 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -7,6 +7,9 @@ import ( context "context" fmt "fmt" _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" _ "google.golang.org/genproto/googleapis/api/annotations" @@ -30,7 +33,12 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint. +// Exactly one of ask_order or bid_order must be provided. type QueryOrderFeeCalcRequest struct { + // ask_order is the ask order to calculate the fees for. + AskOrder *AskOrder `protobuf:"bytes,2,opt,name=ask_order,json=askOrder,proto3" json:"ask_order,omitempty"` + // bid_order is the bid order to calculate the fees for. + BidOrder *BidOrder `protobuf:"bytes,3,opt,name=bid_order,json=bidOrder,proto3" json:"bid_order,omitempty"` } func (m *QueryOrderFeeCalcRequest) Reset() { *m = QueryOrderFeeCalcRequest{} } @@ -66,8 +74,39 @@ func (m *QueryOrderFeeCalcRequest) XXX_DiscardUnknown() { var xxx_messageInfo_QueryOrderFeeCalcRequest proto.InternalMessageInfo +func (m *QueryOrderFeeCalcRequest) GetAskOrder() *AskOrder { + if m != nil { + return m.AskOrder + } + return nil +} + +func (m *QueryOrderFeeCalcRequest) GetBidOrder() *BidOrder { + if m != nil { + return m.BidOrder + } + return nil +} + // QueryOrderFeeCalcResponse is a response message for the QueryOrderFeeCalc endpoint. type QueryOrderFeeCalcResponse struct { + // creation_fee_options are the order creation flat fee options available for creating the provided order. + // If it's empty, no order creation fee is required. + // When creating the order, you should include exactly one of these. + CreationFeeOptions github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=creation_fee_options,json=creationFeeOptions,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"creation_fee_options"` + // settlement_flat_fee_options are the settlement flat fee options available for the provided order. + // If it's empty, no settlement flat fee is required. + // When creating an order, you should include exactly one of these in the settlement fees field. + SettlementFlatFeeOptions github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=settlement_flat_fee_options,json=settlementFlatFeeOptions,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"settlement_flat_fee_options"` + // settlement_ratio_fee_options are the settlement ratio fee options available for the provided order. + // If it's empty, no settlement ratio fee is required. + // + // If the provided order was a bid order, you should include exactly one of these in the settlement fees field. + // If the flat and ratio options you've chose have the same denom, a single entry should be included with their sum. + // + // If the provided order was an ask order, these are purely informational and represent how much will be removed + // from your price if it settles at that price. If it settles for more, the actual amount will probably be larger. + SettlementRatioFeeOptions github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,5,rep,name=settlement_ratio_fee_options,json=settlementRatioFeeOptions,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"settlement_ratio_fee_options"` } func (m *QueryOrderFeeCalcResponse) Reset() { *m = QueryOrderFeeCalcResponse{} } @@ -103,6 +142,27 @@ func (m *QueryOrderFeeCalcResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryOrderFeeCalcResponse proto.InternalMessageInfo +func (m *QueryOrderFeeCalcResponse) GetCreationFeeOptions() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.CreationFeeOptions + } + return nil +} + +func (m *QueryOrderFeeCalcResponse) GetSettlementFlatFeeOptions() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.SettlementFlatFeeOptions + } + return nil +} + +func (m *QueryOrderFeeCalcResponse) GetSettlementRatioFeeOptions() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.SettlementRatioFeeOptions + } + return nil +} + // QuerySettlementFeeCalcRequest is a request message for the QuerySettlementFeeCalc endpoint. type QuerySettlementFeeCalcRequest struct { } @@ -837,57 +897,70 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 794 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcd, 0x4e, 0x14, 0x4d, - 0x14, 0xa5, 0xbe, 0x7c, 0x0a, 0x94, 0x41, 0xe3, 0x95, 0xbf, 0x69, 0xb0, 0x19, 0x5a, 0xa3, 0x38, - 0x42, 0x17, 0x03, 0x82, 0x62, 0x62, 0x0c, 0x90, 0x40, 0x58, 0x10, 0x71, 0x48, 0x5c, 0xb8, 0x99, - 0x14, 0x33, 0xc5, 0xd0, 0x71, 0xa6, 0x6b, 0xe8, 0x6e, 0x26, 0x18, 0x42, 0x62, 0x7c, 0x00, 0x63, - 0xe2, 0xde, 0xb8, 0x30, 0x3e, 0x81, 0x31, 0x2e, 0x7c, 0x00, 0x96, 0x44, 0x36, 0x2e, 0x0d, 0xf8, - 0x20, 0x66, 0xaa, 0xab, 0xe7, 0xaf, 0x7f, 0xa6, 0xdb, 0x65, 0xd7, 0x3d, 0xe7, 0xde, 0x53, 0x55, - 0xf7, 0x9e, 0x6a, 0xac, 0x55, 0x2d, 0x5e, 0x63, 0x26, 0x35, 0x0b, 0x8c, 0xb0, 0xc3, 0xc2, 0x1e, - 0x35, 0x4b, 0x8c, 0xd4, 0xb2, 0x64, 0xff, 0x80, 0x59, 0xaf, 0xf5, 0xaa, 0xc5, 0x1d, 0x0e, 0xc3, - 0x4d, 0x8c, 0xee, 0x61, 0xf4, 0x5a, 0x56, 0x49, 0x15, 0xb8, 0x5d, 0xe1, 0x76, 0x5e, 0xa0, 0x88, - 0xfb, 0xe1, 0x52, 0x94, 0xf1, 0x12, 0xe7, 0xa5, 0x32, 0x23, 0xb4, 0x6a, 0x10, 0x6a, 0x9a, 0xdc, - 0xa1, 0x8e, 0xc1, 0x4d, 0x19, 0xd5, 0x14, 0x3c, 0xfa, 0xbc, 0x9e, 0xff, 0x99, 0x55, 0x64, 0xd6, - 0x1a, 0x63, 0xab, 0xb4, 0x5c, 0xc8, 0xb1, 0xfd, 0x03, 0x66, 0x3b, 0xda, 0x18, 0x4e, 0x05, 0xc4, - 0xec, 0x2a, 0x37, 0x6d, 0xa6, 0x4d, 0xe0, 0x9b, 0x22, 0xb8, 0xcd, 0x1c, 0xa7, 0xcc, 0x2a, 0xcc, - 0x74, 0x3a, 0xd8, 0x69, 0xac, 0x86, 0x01, 0x64, 0x8a, 0x2c, 0x1e, 0x14, 0x88, 0x75, 0xe6, 0x88, - 0x12, 0x92, 0x09, 0x29, 0xdc, 0xc7, 0xeb, 0xdf, 0x79, 0xa3, 0x38, 0x8a, 0xd2, 0x68, 0xea, 0xff, - 0x5c, 0xaf, 0xf8, 0xde, 0x28, 0x6a, 0x23, 0x78, 0xa8, 0x83, 0x22, 0x73, 0x3d, 0xc6, 0x63, 0x5e, - 0x60, 0x93, 0x5a, 0xaf, 0x64, 0xd8, 0xf6, 0x52, 0x8e, 0xe1, 0xfe, 0x8a, 0x58, 0xf6, 0x72, 0x0e, - 0xe4, 0xfa, 0xdc, 0x85, 0x8d, 0xa2, 0xa6, 0xe2, 0xf1, 0x60, 0xae, 0xcc, 0x9d, 0x6b, 0xc6, 0x97, - 0x8b, 0x45, 0x8b, 0xd9, 0x76, 0x7b, 0xf2, 0x39, 0xdc, 0x4b, 0xdd, 0x75, 0x91, 0xba, 0x7f, 0x65, - 0xf4, 0xe7, 0xd7, 0x99, 0x41, 0x79, 0x09, 0x92, 0xb1, 0xed, 0x58, 0x86, 0x59, 0xca, 0x79, 0xc0, - 0xc6, 0xf1, 0xf9, 0x73, 0xca, 0xa2, 0xde, 0xc5, 0xd4, 0x01, 0xe5, 0x72, 0x5b, 0xc1, 0xc6, 0xc5, - 0xb4, 0xc7, 0x24, 0x71, 0x01, 0x0f, 0x8b, 0xa0, 0xbb, 0x95, 0x0d, 0x73, 0x97, 0xc7, 0x3a, 0x84, - 0x14, 0x1e, 0xf1, 0xd1, 0x64, 0xc6, 0x41, 0x0c, 0x22, 0xb4, 0x45, 0x2d, 0x5a, 0x69, 0x88, 0x18, - 0xc2, 0x37, 0xda, 0x56, 0x25, 0x58, 0xc3, 0x69, 0xb1, 0xfc, 0x82, 0x96, 0x8d, 0x22, 0x75, 0xd8, - 0xaa, 0xc5, 0xa8, 0xc3, 0xdc, 0xac, 0x1e, 0xf5, 0x16, 0x9e, 0x8c, 0xc0, 0xc8, 0x44, 0x4f, 0x64, - 0xff, 0x78, 0xa0, 0x4d, 0x6a, 0xd2, 0x12, 0x5b, 0x63, 0x2c, 0xde, 0xa5, 0x4e, 0xe2, 0x89, 0x50, - 0xba, 0x5b, 0x61, 0xee, 0xcd, 0x55, 0x7c, 0x49, 0x60, 0xe0, 0x33, 0xc2, 0xd7, 0x7d, 0xad, 0x0e, - 0xb3, 0x7a, 0xf0, 0xb4, 0xe9, 0x61, 0x13, 0xa3, 0x64, 0x13, 0x30, 0xe4, 0x36, 0x33, 0x6f, 0xcf, - 0xfe, 0x7c, 0xf8, 0xef, 0x36, 0x68, 0x24, 0x64, 0xfc, 0x77, 0x19, 0xb3, 0x89, 0xe8, 0x7f, 0xf8, - 0x8e, 0xe4, 0xdd, 0xfa, 0x66, 0x0a, 0x16, 0x22, 0x2b, 0x87, 0x0d, 0xa9, 0xb2, 0x98, 0x94, 0x26, - 0x55, 0x13, 0xa1, 0xfa, 0x1e, 0xdc, 0x8d, 0x54, 0x6d, 0x37, 0xf8, 0xf0, 0x11, 0xe1, 0x81, 0xb6, - 0xc9, 0x85, 0xe9, 0xc8, 0xd2, 0x1d, 0x9e, 0xa0, 0xcc, 0xc4, 0x44, 0x4b, 0x7d, 0xb3, 0x42, 0x5f, - 0x06, 0xa6, 0xc2, 0xf4, 0x89, 0x03, 0x25, 0x47, 0x9e, 0xcf, 0x1c, 0xc3, 0x0f, 0xd4, 0x74, 0xa3, - 0x56, 0x17, 0x80, 0xf9, 0x6e, 0x95, 0x03, 0xfc, 0x46, 0x79, 0x90, 0x8c, 0x24, 0x55, 0x2f, 0x09, - 0xd5, 0xf3, 0x90, 0x0d, 0x53, 0xed, 0x76, 0x37, 0x39, 0x6a, 0xb4, 0xfd, 0xb1, 0xbb, 0x11, 0x1b, - 0xbe, 0xa1, 0xa6, 0x33, 0xb6, 0x19, 0x0a, 0x74, 0x95, 0x12, 0xe4, 0x69, 0xca, 0x42, 0x42, 0x56, - 0xa2, 0x73, 0xb7, 0xc9, 0x91, 0xf4, 0xc1, 0x63, 0xf8, 0xe4, 0x8d, 0x5e, 0xab, 0x99, 0x75, 0x19, - 0xbd, 0x00, 0x4f, 0xec, 0x32, 0x7a, 0x81, 0x4e, 0x79, 0x47, 0x88, 0x4d, 0x83, 0x1a, 0x2d, 0x16, - 0xbe, 0x20, 0x7c, 0xad, 0xc3, 0x1b, 0x41, 0x8f, 0x2c, 0xe7, 0xf3, 0x5e, 0x85, 0xc4, 0xc6, 0x4b, - 0x71, 0x73, 0x42, 0xdc, 0x34, 0x64, 0xe2, 0xf7, 0x02, 0xbc, 0x43, 0xf8, 0x4a, 0x8b, 0x27, 0x43, - 0x26, 0xb2, 0x68, 0x9b, 0x9d, 0x2b, 0xf7, 0x63, 0x61, 0xe3, 0x9e, 0x5c, 0xd5, 0x15, 0x70, 0x82, - 0xe4, 0x4b, 0x15, 0xe4, 0xf4, 0xf0, 0x28, 0xb2, 0x64, 0xc4, 0x03, 0xa2, 0x2c, 0xfd, 0x03, 0x53, - 0x4a, 0x5f, 0x14, 0xd2, 0x67, 0x41, 0x0f, 0x93, 0x5e, 0x93, 0x6c, 0x52, 0x10, 0xf4, 0xbc, 0x7b, - 0xbe, 0x70, 0x86, 0xe4, 0x03, 0xe9, 0x7f, 0x50, 0x60, 0x31, 0x96, 0x1c, 0xdf, 0x03, 0xa6, 0x3c, - 0x4c, 0xcc, 0x93, 0x9b, 0x58, 0x17, 0x9b, 0x58, 0x86, 0xa7, 0x09, 0x8c, 0xa2, 0xb1, 0xaf, 0x8a, - 0xc8, 0x97, 0xaf, 0x3b, 0xf4, 0x0a, 0x3b, 0x39, 0x57, 0xd1, 0xe9, 0xb9, 0x8a, 0x7e, 0x9f, 0xab, - 0xe8, 0xfd, 0x85, 0xda, 0x73, 0x7a, 0xa1, 0xf6, 0xfc, 0xba, 0x50, 0x7b, 0x70, 0xca, 0xe0, 0x21, - 0xea, 0xb6, 0xd0, 0x4b, 0xbd, 0x64, 0x38, 0x7b, 0x07, 0x3b, 0x7a, 0x81, 0x57, 0x5a, 0x14, 0xcc, - 0x18, 0xbc, 0x55, 0xcf, 0x61, 0x43, 0xd1, 0xce, 0x65, 0xf1, 0xb3, 0x39, 0xff, 0x37, 0x00, 0x00, - 0xff, 0xff, 0x28, 0x02, 0x0e, 0xa9, 0xe3, 0x0a, 0x00, 0x00, + // 1004 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x97, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0xc7, 0x33, 0x09, 0xa5, 0xc9, 0x44, 0x05, 0x31, 0xb8, 0xad, 0xbd, 0x09, 0x1b, 0x77, 0x8b, + 0xc0, 0x84, 0x66, 0x27, 0x76, 0x48, 0xa0, 0x48, 0x15, 0x4a, 0x22, 0xa5, 0xca, 0xa1, 0x6a, 0x71, + 0x25, 0x0e, 0x5c, 0xac, 0xb1, 0xf7, 0xc5, 0x5d, 0xc5, 0xde, 0x71, 0x77, 0x36, 0x51, 0xab, 0x28, + 0x12, 0xe2, 0x88, 0x10, 0x42, 0xe2, 0xc2, 0x09, 0x71, 0x40, 0x1c, 0x38, 0xa3, 0x8a, 0x03, 0x7f, + 0x40, 0x8e, 0x15, 0xbd, 0x70, 0x02, 0x94, 0xf0, 0x87, 0xa0, 0x9d, 0x7d, 0xeb, 0xf5, 0xc6, 0xde, + 0x8d, 0x8d, 0x7a, 0xf2, 0xee, 0xcc, 0xfb, 0xbe, 0xf7, 0x99, 0x5f, 0xdf, 0x59, 0x53, 0xab, 0xe7, + 0xcb, 0x43, 0xf0, 0x84, 0xd7, 0x02, 0x0e, 0x4f, 0x5a, 0x8f, 0x84, 0xd7, 0x06, 0x7e, 0x58, 0xe5, + 0x8f, 0x0f, 0xc0, 0x7f, 0x6a, 0xf7, 0x7c, 0x19, 0x48, 0x76, 0x2d, 0x89, 0xb1, 0xe3, 0x18, 0xfb, + 0xb0, 0x6a, 0x94, 0x5a, 0x52, 0x75, 0xa5, 0x6a, 0xe8, 0x28, 0x1e, 0xbd, 0x44, 0x12, 0xc3, 0x8c, + 0xde, 0x78, 0x53, 0xa8, 0x30, 0x5d, 0x13, 0x02, 0x51, 0xe5, 0x2d, 0xe9, 0x7a, 0xd8, 0xbf, 0xd8, + 0x96, 0xb2, 0xdd, 0x01, 0x2e, 0x7a, 0x2e, 0x17, 0x9e, 0x27, 0x03, 0x11, 0xb8, 0xd2, 0x8b, 0xd5, + 0x85, 0xb6, 0x6c, 0xcb, 0x28, 0x6b, 0xf8, 0x84, 0xad, 0x37, 0x33, 0x50, 0xa5, 0xef, 0x80, 0x8f, + 0x52, 0xeb, 0x7b, 0x42, 0x8b, 0x9f, 0x86, 0xec, 0xf7, 0xc3, 0xd6, 0x1d, 0x80, 0x6d, 0xd1, 0x69, + 0xd5, 0xe1, 0xf1, 0x01, 0xa8, 0x80, 0xdd, 0xa1, 0x73, 0x42, 0xed, 0x37, 0xb4, 0xa0, 0x38, 0x5d, + 0x26, 0x95, 0xf9, 0x5a, 0xd9, 0x1e, 0x3d, 0x38, 0x7b, 0x53, 0xed, 0xeb, 0x14, 0xf5, 0x59, 0x81, + 0x4f, 0xa1, 0xbc, 0xe9, 0x3a, 0x28, 0x9f, 0xc9, 0x97, 0x6f, 0xb9, 0x0e, 0xca, 0x9b, 0xf8, 0x64, + 0x3d, 0x9b, 0xa1, 0xa5, 0x11, 0x68, 0xaa, 0x27, 0x3d, 0x05, 0xec, 0x98, 0x16, 0x5a, 0x3e, 0xe8, + 0x69, 0x68, 0xec, 0x01, 0x34, 0x64, 0x4f, 0xcf, 0x48, 0x91, 0x94, 0x67, 0x2a, 0xf3, 0xb5, 0x92, + 0x8d, 0xd3, 0x1b, 0x4e, 0xa8, 0x8d, 0x13, 0x6a, 0x6f, 0x4b, 0xd7, 0xdb, 0x5a, 0x3d, 0xf9, 0x6b, + 0x69, 0xea, 0x97, 0xbf, 0x97, 0x2a, 0x6d, 0x37, 0x78, 0x74, 0xd0, 0xb4, 0x5b, 0xb2, 0x8b, 0x6b, + 0x81, 0x3f, 0x2b, 0xca, 0xd9, 0xe7, 0xc1, 0xd3, 0x1e, 0x28, 0x2d, 0x50, 0x75, 0x16, 0x17, 0xda, + 0x01, 0xb8, 0x1f, 0x95, 0x61, 0x5f, 0x11, 0xba, 0xa0, 0x20, 0x08, 0x3a, 0xd0, 0x05, 0x2f, 0x68, + 0xec, 0x75, 0x44, 0x90, 0xc2, 0x98, 0x7e, 0xf9, 0x18, 0xc5, 0xa4, 0xde, 0x4e, 0x47, 0x04, 0x03, + 0x30, 0x5f, 0x13, 0xba, 0x38, 0x00, 0xe3, 0x87, 0xb0, 0x29, 0x9a, 0x4b, 0x2f, 0x9f, 0xa6, 0x94, + 0x14, 0xac, 0x87, 0xf5, 0x12, 0x1c, 0x6b, 0x89, 0xbe, 0xa5, 0xd7, 0xed, 0x61, 0xc2, 0x9b, 0xda, + 0x57, 0x56, 0x99, 0x9a, 0x59, 0x01, 0xd1, 0xea, 0x5a, 0x55, 0x5a, 0xd0, 0x11, 0x77, 0x21, 0x88, + 0xb6, 0x05, 0xee, 0xc8, 0x12, 0x9d, 0xd5, 0xdb, 0xa9, 0xe1, 0x3a, 0x45, 0x52, 0x26, 0x95, 0x57, + 0xea, 0x97, 0xf5, 0xfb, 0xae, 0x63, 0x5d, 0xa7, 0x57, 0xcf, 0x49, 0x30, 0xd7, 0xc7, 0x74, 0x21, + 0xee, 0xb8, 0x27, 0xfc, 0x7d, 0xec, 0x56, 0x71, 0xca, 0x05, 0x3a, 0xd7, 0xd5, 0xcd, 0x71, 0xce, + 0x2b, 0xf5, 0xd9, 0xa8, 0x61, 0xd7, 0xb1, 0x4c, 0xba, 0x38, 0x5a, 0x8b, 0xb9, 0xeb, 0x49, 0xff, + 0xa6, 0xe3, 0xf8, 0xa0, 0x54, 0x3a, 0x79, 0x8d, 0x5e, 0x16, 0x51, 0xbb, 0x4e, 0x3d, 0xb7, 0x55, + 0xfc, 0xe3, 0xd7, 0x95, 0x02, 0x2e, 0x03, 0x2a, 0x1e, 0x06, 0xbe, 0xeb, 0xb5, 0xeb, 0x71, 0x60, + 0x7f, 0xfa, 0x86, 0x73, 0x62, 0x51, 0x03, 0x8f, 0x6c, 0x18, 0xd0, 0xe9, 0xa4, 0x0a, 0x5a, 0x0b, + 0x78, 0x66, 0xd2, 0x7d, 0x28, 0x5c, 0xa7, 0xd7, 0x74, 0x67, 0x34, 0x94, 0x5d, 0x6f, 0x4f, 0x8e, + 0x35, 0x09, 0x25, 0x7a, 0x7d, 0x48, 0x86, 0x19, 0x0b, 0x94, 0xe9, 0xae, 0x07, 0xc2, 0x17, 0xdd, + 0x3e, 0xc4, 0x55, 0xfa, 0x66, 0xaa, 0x15, 0x83, 0x2d, 0x5a, 0xd6, 0xcd, 0x9f, 0x89, 0x8e, 0xeb, + 0x88, 0x00, 0xb6, 0xc3, 0x63, 0x05, 0x51, 0xd6, 0x58, 0x7a, 0x93, 0xde, 0xc8, 0x89, 0xc1, 0x44, + 0x77, 0x70, 0xff, 0xc4, 0x41, 0xf7, 0x84, 0x27, 0xda, 0xb0, 0x03, 0x30, 0xde, 0xa2, 0xde, 0xa0, + 0x4b, 0x99, 0xf2, 0xa8, 0x42, 0xed, 0x8b, 0xd7, 0xe8, 0x25, 0x1d, 0xc3, 0x7e, 0x22, 0xf4, 0x8d, + 0x21, 0x17, 0x62, 0xab, 0x59, 0x3e, 0x96, 0xe5, 0xa5, 0x46, 0x75, 0x02, 0x05, 0x0e, 0x73, 0xf9, + 0xcb, 0x17, 0xff, 0x7e, 0x37, 0xfd, 0x36, 0xb3, 0x78, 0x86, 0x93, 0xef, 0x01, 0xa8, 0xc8, 0xce, + 0xd9, 0x6f, 0x04, 0xd7, 0x76, 0xe8, 0x4c, 0xb1, 0xf5, 0xdc, 0xca, 0x59, 0x87, 0xd4, 0xd8, 0x98, + 0x54, 0x86, 0xd4, 0x5c, 0x53, 0xbf, 0xc7, 0xde, 0xcd, 0xa5, 0x4e, 0xdc, 0x83, 0xfd, 0x40, 0xe8, + 0x95, 0xd4, 0xc9, 0x65, 0xb7, 0x72, 0x4b, 0x9f, 0xf3, 0x04, 0x63, 0x65, 0xcc, 0x68, 0xe4, 0x5b, + 0xd5, 0x7c, 0xcb, 0xac, 0xc2, 0xf3, 0xee, 0x47, 0x7e, 0x14, 0xfb, 0xcc, 0x31, 0xfb, 0x9d, 0x24, + 0x6e, 0x34, 0xe8, 0x02, 0x6c, 0xed, 0xa2, 0xca, 0x23, 0xfc, 0xc6, 0xf8, 0x60, 0x32, 0x11, 0x52, + 0xdf, 0xd6, 0xd4, 0x6b, 0xac, 0x9a, 0x45, 0x1d, 0xed, 0x6e, 0x7e, 0xd4, 0xdf, 0xf6, 0xc7, 0x78, + 0xd1, 0xb3, 0x67, 0x24, 0x71, 0xc6, 0x94, 0xa1, 0xb0, 0x0b, 0x51, 0x46, 0x79, 0x9a, 0xb1, 0x3e, + 0xa1, 0x6a, 0xa2, 0x79, 0x57, 0xfc, 0x08, 0x7d, 0xf0, 0x98, 0xfd, 0x18, 0x1f, 0xbd, 0x41, 0x33, + 0xbb, 0xe0, 0xe8, 0x8d, 0xf0, 0xc4, 0x0b, 0x8e, 0xde, 0x48, 0xa7, 0x7c, 0x47, 0xc3, 0x96, 0x99, + 0x99, 0x0f, 0xcb, 0x7e, 0x26, 0xf4, 0xf5, 0x73, 0xde, 0xc8, 0xec, 0xdc, 0x72, 0x43, 0xde, 0x6b, + 0xf0, 0xb1, 0xe3, 0x11, 0xae, 0xa6, 0xe1, 0x6e, 0xb1, 0xe5, 0xf1, 0xf7, 0x02, 0xfb, 0x86, 0xd0, + 0xf9, 0x01, 0x4f, 0x66, 0xcb, 0xb9, 0x45, 0x53, 0x76, 0x6e, 0xbc, 0x3f, 0x56, 0xec, 0xb8, 0x33, + 0xd7, 0x8b, 0x00, 0x4e, 0x08, 0xde, 0x54, 0xa3, 0x9c, 0x9e, 0x7d, 0x94, 0x5b, 0x32, 0xe7, 0x02, + 0x31, 0x6e, 0xff, 0x0f, 0x25, 0xa2, 0x6f, 0x68, 0xf4, 0x55, 0x66, 0x67, 0xa1, 0x1f, 0xa2, 0x9a, + 0xeb, 0x0f, 0x42, 0x68, 0x44, 0xf3, 0xcb, 0x5e, 0x10, 0xbc, 0x20, 0x87, 0x2f, 0x14, 0xb6, 0x31, + 0x16, 0xce, 0xd0, 0x05, 0x66, 0x7c, 0x38, 0xb1, 0x0e, 0x07, 0x71, 0x57, 0x0f, 0x62, 0x93, 0x7d, + 0x32, 0x81, 0x51, 0xf4, 0xc7, 0xd5, 0xd5, 0xf9, 0xc2, 0x4f, 0x47, 0xb5, 0x05, 0x27, 0xa7, 0x26, + 0x79, 0x7e, 0x6a, 0x92, 0x7f, 0x4e, 0x4d, 0xf2, 0xed, 0x99, 0x39, 0xf5, 0xfc, 0xcc, 0x9c, 0xfa, + 0xf3, 0xcc, 0x9c, 0xa2, 0x25, 0x57, 0x66, 0xd0, 0x3d, 0x20, 0x9f, 0xdb, 0x03, 0x5f, 0x90, 0x49, + 0xd0, 0x8a, 0x2b, 0x07, 0x79, 0x9e, 0xf4, 0x89, 0x9a, 0xaf, 0xea, 0xff, 0x21, 0x6b, 0xff, 0x05, + 0x00, 0x00, 0xff, 0xff, 0xc8, 0x2a, 0x52, 0xd5, 0x59, 0x0d, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1334,6 +1407,30 @@ func (m *QueryOrderFeeCalcRequest) MarshalToSizedBuffer(dAtA []byte) (int, error _ = i var l int _ = l + if m.BidOrder != nil { + { + size, err := m.BidOrder.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.AskOrder != nil { + { + size, err := m.AskOrder.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } return len(dAtA) - i, nil } @@ -1357,6 +1454,48 @@ func (m *QueryOrderFeeCalcResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro _ = i var l int _ = l + if len(m.SettlementRatioFeeOptions) > 0 { + for iNdEx := len(m.SettlementRatioFeeOptions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SettlementRatioFeeOptions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.SettlementFlatFeeOptions) > 0 { + for iNdEx := len(m.SettlementFlatFeeOptions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SettlementFlatFeeOptions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.CreationFeeOptions) > 0 { + for iNdEx := len(m.CreationFeeOptions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CreationFeeOptions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } return len(dAtA) - i, nil } @@ -1818,6 +1957,14 @@ func (m *QueryOrderFeeCalcRequest) Size() (n int) { } var l int _ = l + if m.AskOrder != nil { + l = m.AskOrder.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if m.BidOrder != nil { + l = m.BidOrder.Size() + n += 1 + l + sovQuery(uint64(l)) + } return n } @@ -1827,6 +1974,24 @@ func (m *QueryOrderFeeCalcResponse) Size() (n int) { } var l int _ = l + if len(m.CreationFeeOptions) > 0 { + for _, e := range m.CreationFeeOptions { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if len(m.SettlementFlatFeeOptions) > 0 { + for _, e := range m.SettlementFlatFeeOptions { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if len(m.SettlementRatioFeeOptions) > 0 { + for _, e := range m.SettlementRatioFeeOptions { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } return n } @@ -2043,6 +2208,78 @@ func (m *QueryOrderFeeCalcRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: QueryOrderFeeCalcRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AskOrder", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AskOrder == nil { + m.AskOrder = &AskOrder{} + } + if err := m.AskOrder.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BidOrder", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BidOrder == nil { + m.BidOrder = &BidOrder{} + } + if err := m.BidOrder.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -2093,6 +2330,108 @@ func (m *QueryOrderFeeCalcResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: QueryOrderFeeCalcResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreationFeeOptions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CreationFeeOptions = append(m.CreationFeeOptions, types.Coin{}) + if err := m.CreationFeeOptions[len(m.CreationFeeOptions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SettlementFlatFeeOptions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SettlementFlatFeeOptions = append(m.SettlementFlatFeeOptions, types.Coin{}) + if err := m.SettlementFlatFeeOptions[len(m.SettlementFlatFeeOptions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SettlementRatioFeeOptions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SettlementRatioFeeOptions = append(m.SettlementRatioFeeOptions, types.Coin{}) + if err := m.SettlementRatioFeeOptions[len(m.SettlementRatioFeeOptions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/x/exchange/query.pb.gw.go b/x/exchange/query.pb.gw.go index 44641a4c7e..dccecd1e31 100644 --- a/x/exchange/query.pb.gw.go +++ b/x/exchange/query.pb.gw.go @@ -31,10 +31,21 @@ var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage +var ( + filter_Query_QueryOrderFeeCalc_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + func request_Query_QueryOrderFeeCalc_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryOrderFeeCalcRequest var metadata runtime.ServerMetadata + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryOrderFeeCalc_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.QueryOrderFeeCalc(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -44,6 +55,13 @@ func local_request_Query_QueryOrderFeeCalc_0(ctx context.Context, marshaler runt var protoReq QueryOrderFeeCalcRequest var metadata runtime.ServerMetadata + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryOrderFeeCalc_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.QueryOrderFeeCalc(ctx, &protoReq) return msg, metadata, err From 971917e8b651c3d25ec25e49f1404d707d10f006 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 15:37:08 -0600 Subject: [PATCH 204/309] [1658]: Make the QueryOrderFeeCalcResponse []Coin insted of Coins since they're not supposed to be viewed as a whole. --- proto/provenance/exchange/v1/query.proto | 9 +- x/exchange/query.pb.go | 144 +++++++++++------------ 2 files changed, 74 insertions(+), 79 deletions(-) diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 8e6fb6b08d..1d00c9b727 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -79,13 +79,11 @@ message QueryOrderFeeCalcResponse { // creation_fee_options are the order creation flat fee options available for creating the provided order. // If it's empty, no order creation fee is required. // When creating the order, you should include exactly one of these. - repeated cosmos.base.v1beta1.Coin creation_fee_options = 1 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin creation_fee_options = 1 [(gogoproto.nullable) = false]; // settlement_flat_fee_options are the settlement flat fee options available for the provided order. // If it's empty, no settlement flat fee is required. // When creating an order, you should include exactly one of these in the settlement fees field. - repeated cosmos.base.v1beta1.Coin settlement_flat_fee_options = 2 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin settlement_flat_fee_options = 2 [(gogoproto.nullable) = false]; // settlement_ratio_fee_options are the settlement ratio fee options available for the provided order. // If it's empty, no settlement ratio fee is required. // @@ -94,8 +92,7 @@ message QueryOrderFeeCalcResponse { // // If the provided order was an ask order, these are purely informational and represent how much will be removed // from your price if it settles at that price. If it settles for more, the actual amount will probably be larger. - repeated cosmos.base.v1beta1.Coin settlement_ratio_fee_options = 5 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin settlement_ratio_fee_options = 3 [(gogoproto.nullable) = false]; } // QuerySettlementFeeCalcRequest is a request message for the QuerySettlementFeeCalc endpoint. diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index 6fcb967429..ef8fb4d0c4 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -7,7 +7,6 @@ import ( context "context" fmt "fmt" _ "github.com/cosmos/cosmos-proto" - github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types "github.com/cosmos/cosmos-sdk/types" _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" @@ -93,11 +92,11 @@ type QueryOrderFeeCalcResponse struct { // creation_fee_options are the order creation flat fee options available for creating the provided order. // If it's empty, no order creation fee is required. // When creating the order, you should include exactly one of these. - CreationFeeOptions github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=creation_fee_options,json=creationFeeOptions,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"creation_fee_options"` + CreationFeeOptions []types.Coin `protobuf:"bytes,1,rep,name=creation_fee_options,json=creationFeeOptions,proto3" json:"creation_fee_options"` // settlement_flat_fee_options are the settlement flat fee options available for the provided order. // If it's empty, no settlement flat fee is required. // When creating an order, you should include exactly one of these in the settlement fees field. - SettlementFlatFeeOptions github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=settlement_flat_fee_options,json=settlementFlatFeeOptions,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"settlement_flat_fee_options"` + SettlementFlatFeeOptions []types.Coin `protobuf:"bytes,2,rep,name=settlement_flat_fee_options,json=settlementFlatFeeOptions,proto3" json:"settlement_flat_fee_options"` // settlement_ratio_fee_options are the settlement ratio fee options available for the provided order. // If it's empty, no settlement ratio fee is required. // @@ -106,7 +105,7 @@ type QueryOrderFeeCalcResponse struct { // // If the provided order was an ask order, these are purely informational and represent how much will be removed // from your price if it settles at that price. If it settles for more, the actual amount will probably be larger. - SettlementRatioFeeOptions github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,5,rep,name=settlement_ratio_fee_options,json=settlementRatioFeeOptions,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"settlement_ratio_fee_options"` + SettlementRatioFeeOptions []types.Coin `protobuf:"bytes,3,rep,name=settlement_ratio_fee_options,json=settlementRatioFeeOptions,proto3" json:"settlement_ratio_fee_options"` } func (m *QueryOrderFeeCalcResponse) Reset() { *m = QueryOrderFeeCalcResponse{} } @@ -142,21 +141,21 @@ func (m *QueryOrderFeeCalcResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryOrderFeeCalcResponse proto.InternalMessageInfo -func (m *QueryOrderFeeCalcResponse) GetCreationFeeOptions() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *QueryOrderFeeCalcResponse) GetCreationFeeOptions() []types.Coin { if m != nil { return m.CreationFeeOptions } return nil } -func (m *QueryOrderFeeCalcResponse) GetSettlementFlatFeeOptions() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *QueryOrderFeeCalcResponse) GetSettlementFlatFeeOptions() []types.Coin { if m != nil { return m.SettlementFlatFeeOptions } return nil } -func (m *QueryOrderFeeCalcResponse) GetSettlementRatioFeeOptions() github_com_cosmos_cosmos_sdk_types.Coins { +func (m *QueryOrderFeeCalcResponse) GetSettlementRatioFeeOptions() []types.Coin { if m != nil { return m.SettlementRatioFeeOptions } @@ -897,70 +896,69 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 1004 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x97, 0xcf, 0x6f, 0x1b, 0x45, - 0x14, 0xc7, 0x33, 0x09, 0xa5, 0xc9, 0x44, 0x05, 0x31, 0xb8, 0xad, 0xbd, 0x09, 0x1b, 0x77, 0x8b, - 0xc0, 0x84, 0x66, 0x27, 0x76, 0x48, 0xa0, 0x48, 0x15, 0x4a, 0x22, 0xa5, 0xca, 0xa1, 0x6a, 0x71, - 0x25, 0x0e, 0x5c, 0xac, 0xb1, 0xf7, 0xc5, 0x5d, 0xc5, 0xde, 0x71, 0x77, 0x36, 0x51, 0xab, 0x28, - 0x12, 0xe2, 0x88, 0x10, 0x42, 0xe2, 0xc2, 0x09, 0x71, 0x40, 0x1c, 0x38, 0xa3, 0x8a, 0x03, 0x7f, - 0x40, 0x8e, 0x15, 0xbd, 0x70, 0x02, 0x94, 0xf0, 0x87, 0xa0, 0x9d, 0x7d, 0xeb, 0xf5, 0xc6, 0xde, - 0x8d, 0x8d, 0x7a, 0xf2, 0xee, 0xcc, 0xfb, 0xbe, 0xf7, 0x99, 0x5f, 0xdf, 0x59, 0x53, 0xab, 0xe7, - 0xcb, 0x43, 0xf0, 0x84, 0xd7, 0x02, 0x0e, 0x4f, 0x5a, 0x8f, 0x84, 0xd7, 0x06, 0x7e, 0x58, 0xe5, - 0x8f, 0x0f, 0xc0, 0x7f, 0x6a, 0xf7, 0x7c, 0x19, 0x48, 0x76, 0x2d, 0x89, 0xb1, 0xe3, 0x18, 0xfb, - 0xb0, 0x6a, 0x94, 0x5a, 0x52, 0x75, 0xa5, 0x6a, 0xe8, 0x28, 0x1e, 0xbd, 0x44, 0x12, 0xc3, 0x8c, - 0xde, 0x78, 0x53, 0xa8, 0x30, 0x5d, 0x13, 0x02, 0x51, 0xe5, 0x2d, 0xe9, 0x7a, 0xd8, 0xbf, 0xd8, - 0x96, 0xb2, 0xdd, 0x01, 0x2e, 0x7a, 0x2e, 0x17, 0x9e, 0x27, 0x03, 0x11, 0xb8, 0xd2, 0x8b, 0xd5, - 0x85, 0xb6, 0x6c, 0xcb, 0x28, 0x6b, 0xf8, 0x84, 0xad, 0x37, 0x33, 0x50, 0xa5, 0xef, 0x80, 0x8f, - 0x52, 0xeb, 0x7b, 0x42, 0x8b, 0x9f, 0x86, 0xec, 0xf7, 0xc3, 0xd6, 0x1d, 0x80, 0x6d, 0xd1, 0x69, - 0xd5, 0xe1, 0xf1, 0x01, 0xa8, 0x80, 0xdd, 0xa1, 0x73, 0x42, 0xed, 0x37, 0xb4, 0xa0, 0x38, 0x5d, - 0x26, 0x95, 0xf9, 0x5a, 0xd9, 0x1e, 0x3d, 0x38, 0x7b, 0x53, 0xed, 0xeb, 0x14, 0xf5, 0x59, 0x81, - 0x4f, 0xa1, 0xbc, 0xe9, 0x3a, 0x28, 0x9f, 0xc9, 0x97, 0x6f, 0xb9, 0x0e, 0xca, 0x9b, 0xf8, 0x64, - 0x3d, 0x9b, 0xa1, 0xa5, 0x11, 0x68, 0xaa, 0x27, 0x3d, 0x05, 0xec, 0x98, 0x16, 0x5a, 0x3e, 0xe8, - 0x69, 0x68, 0xec, 0x01, 0x34, 0x64, 0x4f, 0xcf, 0x48, 0x91, 0x94, 0x67, 0x2a, 0xf3, 0xb5, 0x92, - 0x8d, 0xd3, 0x1b, 0x4e, 0xa8, 0x8d, 0x13, 0x6a, 0x6f, 0x4b, 0xd7, 0xdb, 0x5a, 0x3d, 0xf9, 0x6b, - 0x69, 0xea, 0x97, 0xbf, 0x97, 0x2a, 0x6d, 0x37, 0x78, 0x74, 0xd0, 0xb4, 0x5b, 0xb2, 0x8b, 0x6b, - 0x81, 0x3f, 0x2b, 0xca, 0xd9, 0xe7, 0xc1, 0xd3, 0x1e, 0x28, 0x2d, 0x50, 0x75, 0x16, 0x17, 0xda, - 0x01, 0xb8, 0x1f, 0x95, 0x61, 0x5f, 0x11, 0xba, 0xa0, 0x20, 0x08, 0x3a, 0xd0, 0x05, 0x2f, 0x68, - 0xec, 0x75, 0x44, 0x90, 0xc2, 0x98, 0x7e, 0xf9, 0x18, 0xc5, 0xa4, 0xde, 0x4e, 0x47, 0x04, 0x03, - 0x30, 0x5f, 0x13, 0xba, 0x38, 0x00, 0xe3, 0x87, 0xb0, 0x29, 0x9a, 0x4b, 0x2f, 0x9f, 0xa6, 0x94, - 0x14, 0xac, 0x87, 0xf5, 0x12, 0x1c, 0x6b, 0x89, 0xbe, 0xa5, 0xd7, 0xed, 0x61, 0xc2, 0x9b, 0xda, - 0x57, 0x56, 0x99, 0x9a, 0x59, 0x01, 0xd1, 0xea, 0x5a, 0x55, 0x5a, 0xd0, 0x11, 0x77, 0x21, 0x88, - 0xb6, 0x05, 0xee, 0xc8, 0x12, 0x9d, 0xd5, 0xdb, 0xa9, 0xe1, 0x3a, 0x45, 0x52, 0x26, 0x95, 0x57, - 0xea, 0x97, 0xf5, 0xfb, 0xae, 0x63, 0x5d, 0xa7, 0x57, 0xcf, 0x49, 0x30, 0xd7, 0xc7, 0x74, 0x21, - 0xee, 0xb8, 0x27, 0xfc, 0x7d, 0xec, 0x56, 0x71, 0xca, 0x05, 0x3a, 0xd7, 0xd5, 0xcd, 0x71, 0xce, - 0x2b, 0xf5, 0xd9, 0xa8, 0x61, 0xd7, 0xb1, 0x4c, 0xba, 0x38, 0x5a, 0x8b, 0xb9, 0xeb, 0x49, 0xff, - 0xa6, 0xe3, 0xf8, 0xa0, 0x54, 0x3a, 0x79, 0x8d, 0x5e, 0x16, 0x51, 0xbb, 0x4e, 0x3d, 0xb7, 0x55, - 0xfc, 0xe3, 0xd7, 0x95, 0x02, 0x2e, 0x03, 0x2a, 0x1e, 0x06, 0xbe, 0xeb, 0xb5, 0xeb, 0x71, 0x60, - 0x7f, 0xfa, 0x86, 0x73, 0x62, 0x51, 0x03, 0x8f, 0x6c, 0x18, 0xd0, 0xe9, 0xa4, 0x0a, 0x5a, 0x0b, - 0x78, 0x66, 0xd2, 0x7d, 0x28, 0x5c, 0xa7, 0xd7, 0x74, 0x67, 0x34, 0x94, 0x5d, 0x6f, 0x4f, 0x8e, - 0x35, 0x09, 0x25, 0x7a, 0x7d, 0x48, 0x86, 0x19, 0x0b, 0x94, 0xe9, 0xae, 0x07, 0xc2, 0x17, 0xdd, - 0x3e, 0xc4, 0x55, 0xfa, 0x66, 0xaa, 0x15, 0x83, 0x2d, 0x5a, 0xd6, 0xcd, 0x9f, 0x89, 0x8e, 0xeb, - 0x88, 0x00, 0xb6, 0xc3, 0x63, 0x05, 0x51, 0xd6, 0x58, 0x7a, 0x93, 0xde, 0xc8, 0x89, 0xc1, 0x44, - 0x77, 0x70, 0xff, 0xc4, 0x41, 0xf7, 0x84, 0x27, 0xda, 0xb0, 0x03, 0x30, 0xde, 0xa2, 0xde, 0xa0, - 0x4b, 0x99, 0xf2, 0xa8, 0x42, 0xed, 0x8b, 0xd7, 0xe8, 0x25, 0x1d, 0xc3, 0x7e, 0x22, 0xf4, 0x8d, - 0x21, 0x17, 0x62, 0xab, 0x59, 0x3e, 0x96, 0xe5, 0xa5, 0x46, 0x75, 0x02, 0x05, 0x0e, 0x73, 0xf9, - 0xcb, 0x17, 0xff, 0x7e, 0x37, 0xfd, 0x36, 0xb3, 0x78, 0x86, 0x93, 0xef, 0x01, 0xa8, 0xc8, 0xce, - 0xd9, 0x6f, 0x04, 0xd7, 0x76, 0xe8, 0x4c, 0xb1, 0xf5, 0xdc, 0xca, 0x59, 0x87, 0xd4, 0xd8, 0x98, - 0x54, 0x86, 0xd4, 0x5c, 0x53, 0xbf, 0xc7, 0xde, 0xcd, 0xa5, 0x4e, 0xdc, 0x83, 0xfd, 0x40, 0xe8, - 0x95, 0xd4, 0xc9, 0x65, 0xb7, 0x72, 0x4b, 0x9f, 0xf3, 0x04, 0x63, 0x65, 0xcc, 0x68, 0xe4, 0x5b, - 0xd5, 0x7c, 0xcb, 0xac, 0xc2, 0xf3, 0xee, 0x47, 0x7e, 0x14, 0xfb, 0xcc, 0x31, 0xfb, 0x9d, 0x24, - 0x6e, 0x34, 0xe8, 0x02, 0x6c, 0xed, 0xa2, 0xca, 0x23, 0xfc, 0xc6, 0xf8, 0x60, 0x32, 0x11, 0x52, - 0xdf, 0xd6, 0xd4, 0x6b, 0xac, 0x9a, 0x45, 0x1d, 0xed, 0x6e, 0x7e, 0xd4, 0xdf, 0xf6, 0xc7, 0x78, - 0xd1, 0xb3, 0x67, 0x24, 0x71, 0xc6, 0x94, 0xa1, 0xb0, 0x0b, 0x51, 0x46, 0x79, 0x9a, 0xb1, 0x3e, - 0xa1, 0x6a, 0xa2, 0x79, 0x57, 0xfc, 0x08, 0x7d, 0xf0, 0x98, 0xfd, 0x18, 0x1f, 0xbd, 0x41, 0x33, - 0xbb, 0xe0, 0xe8, 0x8d, 0xf0, 0xc4, 0x0b, 0x8e, 0xde, 0x48, 0xa7, 0x7c, 0x47, 0xc3, 0x96, 0x99, - 0x99, 0x0f, 0xcb, 0x7e, 0x26, 0xf4, 0xf5, 0x73, 0xde, 0xc8, 0xec, 0xdc, 0x72, 0x43, 0xde, 0x6b, - 0xf0, 0xb1, 0xe3, 0x11, 0xae, 0xa6, 0xe1, 0x6e, 0xb1, 0xe5, 0xf1, 0xf7, 0x02, 0xfb, 0x86, 0xd0, - 0xf9, 0x01, 0x4f, 0x66, 0xcb, 0xb9, 0x45, 0x53, 0x76, 0x6e, 0xbc, 0x3f, 0x56, 0xec, 0xb8, 0x33, - 0xd7, 0x8b, 0x00, 0x4e, 0x08, 0xde, 0x54, 0xa3, 0x9c, 0x9e, 0x7d, 0x94, 0x5b, 0x32, 0xe7, 0x02, - 0x31, 0x6e, 0xff, 0x0f, 0x25, 0xa2, 0x6f, 0x68, 0xf4, 0x55, 0x66, 0x67, 0xa1, 0x1f, 0xa2, 0x9a, - 0xeb, 0x0f, 0x42, 0x68, 0x44, 0xf3, 0xcb, 0x5e, 0x10, 0xbc, 0x20, 0x87, 0x2f, 0x14, 0xb6, 0x31, - 0x16, 0xce, 0xd0, 0x05, 0x66, 0x7c, 0x38, 0xb1, 0x0e, 0x07, 0x71, 0x57, 0x0f, 0x62, 0x93, 0x7d, - 0x32, 0x81, 0x51, 0xf4, 0xc7, 0xd5, 0xd5, 0xf9, 0xc2, 0x4f, 0x47, 0xb5, 0x05, 0x27, 0xa7, 0x26, - 0x79, 0x7e, 0x6a, 0x92, 0x7f, 0x4e, 0x4d, 0xf2, 0xed, 0x99, 0x39, 0xf5, 0xfc, 0xcc, 0x9c, 0xfa, - 0xf3, 0xcc, 0x9c, 0xa2, 0x25, 0x57, 0x66, 0xd0, 0x3d, 0x20, 0x9f, 0xdb, 0x03, 0x5f, 0x90, 0x49, - 0xd0, 0x8a, 0x2b, 0x07, 0x79, 0x9e, 0xf4, 0x89, 0x9a, 0xaf, 0xea, 0xff, 0x21, 0x6b, 0xff, 0x05, - 0x00, 0x00, 0xff, 0xff, 0xc8, 0x2a, 0x52, 0xd5, 0x59, 0x0d, 0x00, 0x00, + // 978 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x41, 0x6f, 0x1b, 0x45, + 0x14, 0xce, 0x24, 0x85, 0x26, 0x13, 0x15, 0xc4, 0xc3, 0x6d, 0xed, 0x4d, 0xd8, 0xb8, 0x5b, 0x04, + 0x21, 0x34, 0xbb, 0xb1, 0x43, 0x02, 0x45, 0xaa, 0x50, 0x12, 0x29, 0x55, 0x0e, 0x55, 0x5b, 0x57, + 0xe2, 0xc0, 0x01, 0x33, 0xf6, 0xbe, 0xb8, 0xab, 0xd8, 0x3b, 0xee, 0xee, 0x26, 0x2a, 0x8a, 0x22, + 0x21, 0x7e, 0x00, 0x42, 0xe2, 0xc2, 0x09, 0x71, 0x40, 0x9c, 0x39, 0x20, 0xc4, 0x81, 0x1f, 0x90, + 0x63, 0x45, 0x2f, 0x9c, 0x10, 0x4a, 0xf8, 0x21, 0x68, 0x67, 0xdf, 0x7a, 0xbd, 0xb1, 0x77, 0xb3, + 0xe6, 0xb6, 0x33, 0xf3, 0xbe, 0xef, 0x7d, 0xf3, 0x66, 0xde, 0x37, 0x36, 0x37, 0xfa, 0x9e, 0x3c, + 0x42, 0x57, 0xb8, 0x6d, 0xb4, 0xf0, 0x79, 0xfb, 0xa9, 0x70, 0x3b, 0x68, 0x1d, 0xd5, 0xac, 0x67, + 0x87, 0xe8, 0x7d, 0x69, 0xf6, 0x3d, 0x19, 0x48, 0xb8, 0x91, 0xc4, 0x98, 0x71, 0x8c, 0x79, 0x54, + 0xd3, 0x2a, 0x6d, 0xe9, 0xf7, 0xa4, 0xdf, 0x54, 0x51, 0x56, 0x34, 0x88, 0x20, 0x9a, 0x1e, 0x8d, + 0xac, 0x96, 0xf0, 0x43, 0xba, 0x16, 0x06, 0xa2, 0x66, 0xb5, 0xa5, 0xe3, 0xd2, 0xfa, 0x62, 0x47, + 0xca, 0x4e, 0x17, 0x2d, 0xd1, 0x77, 0x2c, 0xe1, 0xba, 0x32, 0x10, 0x81, 0x23, 0xdd, 0x18, 0x5d, + 0xea, 0xc8, 0x8e, 0x8c, 0x58, 0xc3, 0x2f, 0x9a, 0xbd, 0x9d, 0x21, 0x55, 0x7a, 0x36, 0x7a, 0x04, + 0x35, 0xbe, 0x67, 0xbc, 0xfc, 0x38, 0xd4, 0xfe, 0x30, 0x9c, 0xdd, 0x45, 0xdc, 0x11, 0xdd, 0x76, + 0x03, 0x9f, 0x1d, 0xa2, 0x1f, 0xc0, 0x3d, 0x3e, 0x27, 0xfc, 0x83, 0xa6, 0x02, 0x94, 0xa7, 0xab, + 0x6c, 0x79, 0xbe, 0x5e, 0x35, 0xc7, 0x6f, 0xce, 0xdc, 0xf2, 0x0f, 0x14, 0x45, 0x63, 0x56, 0xd0, + 0x57, 0x08, 0x6f, 0x39, 0x36, 0xc1, 0x67, 0xf2, 0xe1, 0xdb, 0x8e, 0x4d, 0xf0, 0x16, 0x7d, 0x19, + 0xbf, 0x4c, 0xf3, 0xca, 0x18, 0x69, 0x7e, 0x5f, 0xba, 0x3e, 0xc2, 0x63, 0x5e, 0x6a, 0x7b, 0xa8, + 0xca, 0xd0, 0xdc, 0x47, 0x6c, 0xca, 0xbe, 0xaa, 0x48, 0x99, 0x55, 0x67, 0x96, 0xe7, 0xeb, 0x15, + 0x93, 0xca, 0x1b, 0x16, 0xd4, 0xa4, 0x82, 0x9a, 0x3b, 0xd2, 0x71, 0xb7, 0xaf, 0x9c, 0xfe, 0xbd, + 0x34, 0xd5, 0x80, 0x18, 0xbc, 0x8b, 0xf8, 0x30, 0x82, 0xc2, 0xe7, 0x7c, 0xc1, 0xc7, 0x20, 0xe8, + 0x62, 0x0f, 0xdd, 0xa0, 0xb9, 0xdf, 0x15, 0x41, 0x8a, 0x79, 0xba, 0x18, 0x73, 0x39, 0xe1, 0xd8, + 0xed, 0x8a, 0x60, 0x88, 0xff, 0x0b, 0xbe, 0x38, 0xc4, 0xef, 0x85, 0xe9, 0x53, 0x09, 0x66, 0x8a, + 0x25, 0xa8, 0x24, 0x24, 0x8d, 0x90, 0x23, 0xc9, 0x60, 0x2c, 0xf1, 0xb7, 0x54, 0xc5, 0x9e, 0x24, + 0x12, 0x52, 0x27, 0x6a, 0x54, 0xb9, 0x9e, 0x15, 0x10, 0xd5, 0xd5, 0xa8, 0xf1, 0x92, 0x8a, 0xb8, + 0x8f, 0x41, 0x74, 0x20, 0x74, 0x17, 0x2a, 0x7c, 0x56, 0x1d, 0x64, 0xd3, 0xb1, 0xcb, 0xac, 0xca, + 0x96, 0xaf, 0x34, 0xae, 0xaa, 0xf1, 0x9e, 0x6d, 0xdc, 0xe4, 0xd7, 0x2f, 0x40, 0x88, 0xeb, 0x63, + 0xbe, 0x10, 0x2f, 0x3c, 0x10, 0xde, 0x01, 0x2d, 0xfb, 0x31, 0xe5, 0x02, 0x9f, 0xeb, 0xa9, 0xe9, + 0x98, 0xf3, 0x5a, 0x63, 0x36, 0x9a, 0xd8, 0xb3, 0x0d, 0x9d, 0x2f, 0x8e, 0xc7, 0x12, 0x77, 0x23, + 0x59, 0xdf, 0xb2, 0x6d, 0x0f, 0x7d, 0x3f, 0x4d, 0x5e, 0xe7, 0x57, 0x45, 0x34, 0xaf, 0xa8, 0xe7, + 0xb6, 0xcb, 0x7f, 0xfe, 0xba, 0x5a, 0xa2, 0xd2, 0x12, 0xe2, 0x49, 0xe0, 0x39, 0x6e, 0xa7, 0x11, + 0x07, 0x0e, 0xca, 0x37, 0xca, 0x49, 0x49, 0x35, 0x6a, 0x96, 0x30, 0xa0, 0xdb, 0x4d, 0x25, 0x34, + 0x16, 0xe8, 0xb6, 0xa6, 0xd7, 0x08, 0xb8, 0xc1, 0x6f, 0xa8, 0xc5, 0x68, 0x2b, 0x7b, 0xee, 0xbe, + 0x2c, 0x54, 0x84, 0x0a, 0xbf, 0x39, 0x02, 0x23, 0xc6, 0x12, 0x07, 0xb5, 0xf4, 0x48, 0x78, 0xa2, + 0x37, 0x10, 0x71, 0x9d, 0xbf, 0x99, 0x9a, 0xa5, 0x60, 0x83, 0x57, 0xd5, 0xf4, 0xa7, 0xa2, 0xeb, + 0xd8, 0x22, 0xc0, 0x9d, 0xf0, 0xf2, 0x63, 0xc4, 0x1a, 0x43, 0x6f, 0xf3, 0x5b, 0x39, 0x31, 0x44, + 0x74, 0x8f, 0xee, 0x4f, 0x1c, 0xf4, 0x40, 0xb8, 0xa2, 0x83, 0xbb, 0x88, 0xc5, 0x0e, 0xf5, 0x16, + 0x5f, 0xca, 0x84, 0x47, 0x19, 0xea, 0x5f, 0xbd, 0xc6, 0x5f, 0x51, 0x31, 0xf0, 0x13, 0xe3, 0x6f, + 0x8c, 0xf4, 0x3f, 0xac, 0x65, 0x39, 0x48, 0x96, 0x8b, 0x69, 0xb5, 0x09, 0x10, 0xb4, 0xcd, 0x95, + 0xaf, 0x5f, 0xfe, 0xfb, 0xdd, 0xf4, 0xdb, 0x60, 0x58, 0x19, 0x1e, 0xba, 0x8f, 0xe8, 0x47, 0x46, + 0x0a, 0xbf, 0x33, 0x3a, 0xdb, 0x91, 0x9e, 0x82, 0x8d, 0xdc, 0xcc, 0x59, 0x4d, 0xaa, 0x6d, 0x4e, + 0x0a, 0x23, 0xd5, 0x96, 0x52, 0xfd, 0x1e, 0xbc, 0x9b, 0xab, 0x3a, 0x71, 0x0f, 0xf8, 0x81, 0xf1, + 0x6b, 0xa9, 0xce, 0x85, 0x3b, 0xb9, 0xa9, 0x2f, 0x78, 0x82, 0xb6, 0x5a, 0x30, 0x9a, 0xf4, 0xad, + 0x29, 0x7d, 0x2b, 0xb0, 0x6c, 0xe5, 0xbd, 0x4c, 0xd6, 0x71, 0xec, 0x33, 0x27, 0xf0, 0x07, 0x4b, + 0xdc, 0x68, 0xd8, 0x05, 0x60, 0xfd, 0xb2, 0xcc, 0x63, 0xfc, 0x46, 0xfb, 0x60, 0x32, 0x10, 0xa9, + 0xbe, 0xab, 0x54, 0xaf, 0x43, 0x2d, 0x4b, 0x75, 0x74, 0xbb, 0xad, 0xe3, 0xc1, 0xb5, 0x3f, 0xa1, + 0x27, 0x16, 0x7e, 0x63, 0x89, 0x33, 0xa6, 0x0c, 0x05, 0x2e, 0x95, 0x32, 0xce, 0xd3, 0xb4, 0x8d, + 0x09, 0x51, 0x13, 0xd5, 0xdd, 0xb7, 0x8e, 0xc9, 0x07, 0x4f, 0xe0, 0xc7, 0xb8, 0xf5, 0x86, 0xcd, + 0xec, 0x92, 0xd6, 0x1b, 0xe3, 0x89, 0x97, 0xb4, 0xde, 0x58, 0xa7, 0x7c, 0x47, 0x89, 0xad, 0x82, + 0x9e, 0x2f, 0x16, 0x7e, 0x66, 0xfc, 0xf5, 0x0b, 0xde, 0x08, 0x66, 0x6e, 0xba, 0x11, 0xef, 0xd5, + 0xac, 0xc2, 0xf1, 0x24, 0xae, 0xae, 0xc4, 0xdd, 0x81, 0x95, 0xe2, 0x77, 0x01, 0xbe, 0x61, 0x7c, + 0x7e, 0xc8, 0x93, 0x61, 0x25, 0x37, 0x69, 0xca, 0xce, 0xb5, 0xf7, 0x0b, 0xc5, 0x16, 0xad, 0x5c, + 0x3f, 0x12, 0x70, 0xca, 0xe8, 0xa5, 0x1a, 0xe7, 0xf4, 0xf0, 0x51, 0x6e, 0xca, 0x9c, 0x07, 0x44, + 0xbb, 0xfb, 0x3f, 0x90, 0x24, 0x7d, 0x53, 0x49, 0x5f, 0x03, 0x33, 0x4b, 0xfa, 0x11, 0xa1, 0x2d, + 0xf5, 0xb3, 0x0d, 0x9b, 0x51, 0x7d, 0xe1, 0x25, 0xa3, 0x07, 0x72, 0xf4, 0x41, 0x81, 0xcd, 0x42, + 0x72, 0x46, 0x1e, 0x30, 0xed, 0xc3, 0x89, 0x71, 0xb4, 0x89, 0xfb, 0x6a, 0x13, 0x5b, 0xf0, 0xc9, + 0x04, 0x46, 0x31, 0xd8, 0x57, 0x4f, 0xf1, 0x85, 0x3f, 0x07, 0xfd, 0x6d, 0x3c, 0x3d, 0xd3, 0xd9, + 0x8b, 0x33, 0x9d, 0xfd, 0x73, 0xa6, 0xb3, 0x6f, 0xcf, 0xf5, 0xa9, 0x17, 0xe7, 0xfa, 0xd4, 0x5f, + 0xe7, 0xfa, 0x14, 0xaf, 0x38, 0x32, 0x43, 0xdd, 0x23, 0xf6, 0x99, 0xd9, 0x71, 0x82, 0xa7, 0x87, + 0x2d, 0xb3, 0x2d, 0x7b, 0x43, 0x0a, 0x56, 0x1d, 0x39, 0xac, 0xe7, 0xf9, 0x40, 0x51, 0xeb, 0x55, + 0xf5, 0x0f, 0x60, 0xfd, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x68, 0xfe, 0x92, 0x8e, 0xd3, 0x0c, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1465,7 +1463,7 @@ func (m *QueryOrderFeeCalcResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro i = encodeVarintQuery(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x2a + dAtA[i] = 0x1a } } if len(m.SettlementFlatFeeOptions) > 0 { @@ -2398,7 +2396,7 @@ func (m *QueryOrderFeeCalcResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 5: + case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SettlementRatioFeeOptions", wireType) } From 7a25a327d9d258ebcdeede839ddb85a061bbae48 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 15:39:17 -0600 Subject: [PATCH 205/309] [1658]: Implement QueryOrderFeeCalc. --- x/exchange/keeper/grpc_query.go | 48 +++++++++++++++++++++++++++++++-- x/exchange/keeper/market.go | 15 +++++++---- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 436dd6d5a4..4ef7770493 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -2,6 +2,12 @@ package keeper import ( "context" + "fmt" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/provenance-io/provenance/x/exchange" ) @@ -19,8 +25,46 @@ var _ exchange.QueryServer = QueryServer{} // QueryOrderFeeCalc calculates the fees that will be associated with the provided order. func (k QueryServer) QueryOrderFeeCalc(goCtx context.Context, req *exchange.QueryOrderFeeCalcRequest) (*exchange.QueryOrderFeeCalcResponse, error) { - // TODO[1658]: Implement QueryOrderFeeCalc query - panic("not implemented") + if req == nil || (req.AskOrder == nil && req.BidOrder == nil) { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + if req.AskOrder != nil && req.BidOrder != nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + store := k.getStore(ctx) + resp := &exchange.QueryOrderFeeCalcResponse{} + + switch { + case req.AskOrder != nil: + order := req.AskOrder + ratioFee, err := calculateSellerSettlementRatioFee(store, order.MarketId, order.Price) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "failed to calculate seller ratio fee option: %v", err) + } + if ratioFee != nil { + resp.SettlementRatioFeeOptions = append(resp.SettlementRatioFeeOptions, *ratioFee) + } + resp.SettlementFlatFeeOptions = getSellerSettlementFlatFees(store, order.MarketId) + resp.CreationFeeOptions = getCreateAskFlatFees(store, order.MarketId) + case req.BidOrder != nil: + order := req.BidOrder + ratioFees, err := calcBuyerSettlementRatioFeeOptions(store, order.MarketId, order.Price) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "failed to calculate buyer ratio fee options: %v", err) + } + if len(ratioFees) > 0 { + resp.SettlementRatioFeeOptions = append(resp.SettlementRatioFeeOptions, ratioFees...) + } + resp.SettlementFlatFeeOptions = getBuyerSettlementFlatFees(store, order.MarketId) + resp.CreationFeeOptions = getCreateBidFlatFees(store, order.MarketId) + default: + // This case should have been caught right off the bat in this query. + panic(fmt.Errorf("missing QueryOrderFeeCalc case")) + } + + return resp, nil } // QuerySettlementFeeCalc calculates the fees that will be associated with the provided settlement. diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 528e66bc91..2bd529fa6f 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -366,7 +366,7 @@ func calculateSellerSettlementRatioFee(store sdk.KVStore, marketID uint32, price if ratio == nil { return nil, nil } - rv, err := ratio.ApplyTo(price) + rv, err := ratio.ApplyToLoosely(price) if err != nil { return nil, fmt.Errorf("invalid seller settlement fees: %w", err) } @@ -430,11 +430,11 @@ func getBuyerSettlementFeeRatiosForPriceDenom(store sdk.KVStore, marketID uint32 return ratios } -// CalculateBuyerSettlementRatioFeeOptions calculates the buyer settlement ratio fee options available for the given price. -func (k Keeper) CalculateBuyerSettlementRatioFeeOptions(ctx sdk.Context, marketID uint32, price sdk.Coin) ([]sdk.Coin, error) { - ratios := getBuyerSettlementFeeRatiosForPriceDenom(k.getStore(ctx), marketID, price.Denom) +// calcBuyerSettlementRatioFeeOptions calculates the buyer settlement ratio fee options available for the given price. +func calcBuyerSettlementRatioFeeOptions(store sdk.KVStore, marketID uint32, price sdk.Coin) ([]sdk.Coin, error) { + ratios := getBuyerSettlementFeeRatiosForPriceDenom(store, marketID, price.Denom) if len(ratios) == 0 { - return nil, fmt.Errorf("no buyer settlement fee ratios found with price denom %q", price.Denom) + return nil, nil } var errs []error @@ -456,6 +456,11 @@ func (k Keeper) CalculateBuyerSettlementRatioFeeOptions(ctx sdk.Context, marketI return rv, nil } +// CalculateBuyerSettlementRatioFeeOptions calculates the buyer settlement ratio fee options available for the given price. +func (k Keeper) CalculateBuyerSettlementRatioFeeOptions(ctx sdk.Context, marketID uint32, price sdk.Coin) ([]sdk.Coin, error) { + return calcBuyerSettlementRatioFeeOptions(k.getStore(ctx), marketID, price) +} + // validateBuyerSettlementFee returns an error if the provided fee is not enough to cover both the // buyer settlement flat and percent fees for the given price. func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Coin, fee sdk.Coins) error { From 24ca9d28ba50f4d6dd16c49901fabc3e97d80857 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 15:41:36 -0600 Subject: [PATCH 206/309] [1658]: Remove the QuerySettlementFeeCalc since I took out the fee amount from the settlement tx message. --- docs/proto-docs.md | 23 -- proto/provenance/exchange/v1/query.proto | 14 - x/exchange/keeper/grpc_query.go | 6 - x/exchange/query.pb.go | 431 ++++------------------- x/exchange/query.pb.gw.go | 62 ---- 5 files changed, 75 insertions(+), 461 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index cc4819fcc7..f3b3b047ab 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -105,8 +105,6 @@ - [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) - [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) - [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) - - [QuerySettlementFeeCalcRequest](#provenance.exchange.v1.QuerySettlementFeeCalcRequest) - - [QuerySettlementFeeCalcResponse](#provenance.exchange.v1.QuerySettlementFeeCalcResponse) - [QueryValidateCreateMarketRequest](#provenance.exchange.v1.QueryValidateCreateMarketRequest) - [QueryValidateCreateMarketResponse](#provenance.exchange.v1.QueryValidateCreateMarketResponse) - [QueryValidateManageFeesRequest](#provenance.exchange.v1.QueryValidateManageFeesRequest) @@ -2069,26 +2067,6 @@ QueryParamsResponse is a response message for the QueryParams endpoint. - - -### QuerySettlementFeeCalcRequest -QuerySettlementFeeCalcRequest is a request message for the QuerySettlementFeeCalc endpoint. - - - - - - - - -### QuerySettlementFeeCalcResponse -QuerySettlementFeeCalcResponse is a response message for the QuerySettlementFeeCalc endpoint. - - - - - - ### QueryValidateCreateMarketRequest @@ -2148,7 +2126,6 @@ Query is the service for exchange module's query endpoints. | Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | | ----------- | ------------ | ------------- | ------------| ------- | -------- | | `QueryOrderFeeCalc` | [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) | [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) | QueryOrderFeeCalc calculates the fees that will be associated with the provided order. | GET|/provenance/exchange/v1/fees/order| -| `QuerySettlementFeeCalc` | [QuerySettlementFeeCalcRequest](#provenance.exchange.v1.QuerySettlementFeeCalcRequest) | [QuerySettlementFeeCalcResponse](#provenance.exchange.v1.QuerySettlementFeeCalcResponse) | QuerySettlementFeeCalc calculates the fees that will be associated with the provided settlement. | GET|/provenance/exchange/v1/fees/settlement| | `QueryGetOrder` | [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) | [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) | QueryGetOrder looks up an order by id. | GET|/provenance/exchange/v1/order/{order_id}| | `QueryGetMarketOrders` | [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) | [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) | QueryGetMarketOrders looks up the orders in a market. | GET|/provenance/exchange/v1/market/{market_id}/orders| | `QueryGetAddressOrders` | [QueryGetAddressOrdersRequest](#provenance.exchange.v1.QueryGetAddressOrdersRequest) | [QueryGetAddressOrdersResponse](#provenance.exchange.v1.QueryGetAddressOrdersResponse) | QueryGetAddressOrders looks up the orders from the provided address. | GET|/provenance/exchange/v1/orders/{address}| diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 1d00c9b727..70e35fd824 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -19,11 +19,6 @@ service Query { option (google.api.http).get = "/provenance/exchange/v1/fees/order"; } - // QuerySettlementFeeCalc calculates the fees that will be associated with the provided settlement. - rpc QuerySettlementFeeCalc(QuerySettlementFeeCalcRequest) returns (QuerySettlementFeeCalcResponse) { - option (google.api.http).get = "/provenance/exchange/v1/fees/settlement"; - } - // QueryGetOrder looks up an order by id. rpc QueryGetOrder(QueryGetOrderRequest) returns (QueryGetOrderResponse) { option (google.api.http).get = "/provenance/exchange/v1/order/{order_id}"; @@ -95,15 +90,6 @@ message QueryOrderFeeCalcResponse { repeated cosmos.base.v1beta1.Coin settlement_ratio_fee_options = 3 [(gogoproto.nullable) = false]; } -// QuerySettlementFeeCalcRequest is a request message for the QuerySettlementFeeCalc endpoint. -message QuerySettlementFeeCalcRequest { - // TODO[1658]: QuerySettlementFeeCalcRequest -} -// QuerySettlementFeeCalcResponse is a response message for the QuerySettlementFeeCalc endpoint. -message QuerySettlementFeeCalcResponse { - // TODO[1658]: QuerySettlementFeeCalcResponse -} - // QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. message QueryGetOrderRequest { uint64 order_id = 1; diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 4ef7770493..12bb7deaaa 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -67,12 +67,6 @@ func (k QueryServer) QueryOrderFeeCalc(goCtx context.Context, req *exchange.Quer return resp, nil } -// QuerySettlementFeeCalc calculates the fees that will be associated with the provided settlement. -func (k QueryServer) QuerySettlementFeeCalc(goCtx context.Context, req *exchange.QuerySettlementFeeCalcRequest) (*exchange.QuerySettlementFeeCalcResponse, error) { - // TODO[1658]: Implement QuerySettlementFeeCalc query - panic("not implemented") -} - // QueryGetOrder looks up an order by id. func (k QueryServer) QueryGetOrder(goCtx context.Context, req *exchange.QueryGetOrderRequest) (*exchange.QueryGetOrderResponse, error) { // TODO[1658]: Implement QueryGetOrder query diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index ef8fb4d0c4..a6bab4249e 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -162,80 +162,6 @@ func (m *QueryOrderFeeCalcResponse) GetSettlementRatioFeeOptions() []types.Coin return nil } -// QuerySettlementFeeCalcRequest is a request message for the QuerySettlementFeeCalc endpoint. -type QuerySettlementFeeCalcRequest struct { -} - -func (m *QuerySettlementFeeCalcRequest) Reset() { *m = QuerySettlementFeeCalcRequest{} } -func (m *QuerySettlementFeeCalcRequest) String() string { return proto.CompactTextString(m) } -func (*QuerySettlementFeeCalcRequest) ProtoMessage() {} -func (*QuerySettlementFeeCalcRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{2} -} -func (m *QuerySettlementFeeCalcRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *QuerySettlementFeeCalcRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_QuerySettlementFeeCalcRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *QuerySettlementFeeCalcRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_QuerySettlementFeeCalcRequest.Merge(m, src) -} -func (m *QuerySettlementFeeCalcRequest) XXX_Size() int { - return m.Size() -} -func (m *QuerySettlementFeeCalcRequest) XXX_DiscardUnknown() { - xxx_messageInfo_QuerySettlementFeeCalcRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_QuerySettlementFeeCalcRequest proto.InternalMessageInfo - -// QuerySettlementFeeCalcResponse is a response message for the QuerySettlementFeeCalc endpoint. -type QuerySettlementFeeCalcResponse struct { -} - -func (m *QuerySettlementFeeCalcResponse) Reset() { *m = QuerySettlementFeeCalcResponse{} } -func (m *QuerySettlementFeeCalcResponse) String() string { return proto.CompactTextString(m) } -func (*QuerySettlementFeeCalcResponse) ProtoMessage() {} -func (*QuerySettlementFeeCalcResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{3} -} -func (m *QuerySettlementFeeCalcResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *QuerySettlementFeeCalcResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_QuerySettlementFeeCalcResponse.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *QuerySettlementFeeCalcResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_QuerySettlementFeeCalcResponse.Merge(m, src) -} -func (m *QuerySettlementFeeCalcResponse) XXX_Size() int { - return m.Size() -} -func (m *QuerySettlementFeeCalcResponse) XXX_DiscardUnknown() { - xxx_messageInfo_QuerySettlementFeeCalcResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_QuerySettlementFeeCalcResponse proto.InternalMessageInfo - // QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. type QueryGetOrderRequest struct { OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` @@ -245,7 +171,7 @@ func (m *QueryGetOrderRequest) Reset() { *m = QueryGetOrderRequest{} } func (m *QueryGetOrderRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetOrderRequest) ProtoMessage() {} func (*QueryGetOrderRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{4} + return fileDescriptor_00949b75b1c10bfe, []int{2} } func (m *QueryGetOrderRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -289,7 +215,7 @@ func (m *QueryGetOrderResponse) Reset() { *m = QueryGetOrderResponse{} } func (m *QueryGetOrderResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetOrderResponse) ProtoMessage() {} func (*QueryGetOrderResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{5} + return fileDescriptor_00949b75b1c10bfe, []int{3} } func (m *QueryGetOrderResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -327,7 +253,7 @@ func (m *QueryGetMarketOrdersRequest) Reset() { *m = QueryGetMarketOrder func (m *QueryGetMarketOrdersRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetMarketOrdersRequest) ProtoMessage() {} func (*QueryGetMarketOrdersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{6} + return fileDescriptor_00949b75b1c10bfe, []int{4} } func (m *QueryGetMarketOrdersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -371,7 +297,7 @@ func (m *QueryGetMarketOrdersResponse) Reset() { *m = QueryGetMarketOrde func (m *QueryGetMarketOrdersResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetMarketOrdersResponse) ProtoMessage() {} func (*QueryGetMarketOrdersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{7} + return fileDescriptor_00949b75b1c10bfe, []int{5} } func (m *QueryGetMarketOrdersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -409,7 +335,7 @@ func (m *QueryGetAddressOrdersRequest) Reset() { *m = QueryGetAddressOrd func (m *QueryGetAddressOrdersRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetAddressOrdersRequest) ProtoMessage() {} func (*QueryGetAddressOrdersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{8} + return fileDescriptor_00949b75b1c10bfe, []int{6} } func (m *QueryGetAddressOrdersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -453,7 +379,7 @@ func (m *QueryGetAddressOrdersResponse) Reset() { *m = QueryGetAddressOr func (m *QueryGetAddressOrdersResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetAddressOrdersResponse) ProtoMessage() {} func (*QueryGetAddressOrdersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{9} + return fileDescriptor_00949b75b1c10bfe, []int{7} } func (m *QueryGetAddressOrdersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -490,7 +416,7 @@ func (m *QueryGetAllOrdersRequest) Reset() { *m = QueryGetAllOrdersReque func (m *QueryGetAllOrdersRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetAllOrdersRequest) ProtoMessage() {} func (*QueryGetAllOrdersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{10} + return fileDescriptor_00949b75b1c10bfe, []int{8} } func (m *QueryGetAllOrdersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -527,7 +453,7 @@ func (m *QueryGetAllOrdersResponse) Reset() { *m = QueryGetAllOrdersResp func (m *QueryGetAllOrdersResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetAllOrdersResponse) ProtoMessage() {} func (*QueryGetAllOrdersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{11} + return fileDescriptor_00949b75b1c10bfe, []int{9} } func (m *QueryGetAllOrdersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -565,7 +491,7 @@ func (m *QueryMarketInfoRequest) Reset() { *m = QueryMarketInfoRequest{} func (m *QueryMarketInfoRequest) String() string { return proto.CompactTextString(m) } func (*QueryMarketInfoRequest) ProtoMessage() {} func (*QueryMarketInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{12} + return fileDescriptor_00949b75b1c10bfe, []int{10} } func (m *QueryMarketInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -609,7 +535,7 @@ func (m *QueryMarketInfoResponse) Reset() { *m = QueryMarketInfoResponse func (m *QueryMarketInfoResponse) String() string { return proto.CompactTextString(m) } func (*QueryMarketInfoResponse) ProtoMessage() {} func (*QueryMarketInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{13} + return fileDescriptor_00949b75b1c10bfe, []int{11} } func (m *QueryMarketInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -646,7 +572,7 @@ func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } func (*QueryParamsRequest) ProtoMessage() {} func (*QueryParamsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{14} + return fileDescriptor_00949b75b1c10bfe, []int{12} } func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -683,7 +609,7 @@ func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } func (*QueryParamsResponse) ProtoMessage() {} func (*QueryParamsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{15} + return fileDescriptor_00949b75b1c10bfe, []int{13} } func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -720,7 +646,7 @@ func (m *QueryValidateCreateMarketRequest) Reset() { *m = QueryValidateC func (m *QueryValidateCreateMarketRequest) String() string { return proto.CompactTextString(m) } func (*QueryValidateCreateMarketRequest) ProtoMessage() {} func (*QueryValidateCreateMarketRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{16} + return fileDescriptor_00949b75b1c10bfe, []int{14} } func (m *QueryValidateCreateMarketRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -757,7 +683,7 @@ func (m *QueryValidateCreateMarketResponse) Reset() { *m = QueryValidate func (m *QueryValidateCreateMarketResponse) String() string { return proto.CompactTextString(m) } func (*QueryValidateCreateMarketResponse) ProtoMessage() {} func (*QueryValidateCreateMarketResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{17} + return fileDescriptor_00949b75b1c10bfe, []int{15} } func (m *QueryValidateCreateMarketResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -795,7 +721,7 @@ func (m *QueryValidateManageFeesRequest) Reset() { *m = QueryValidateMan func (m *QueryValidateManageFeesRequest) String() string { return proto.CompactTextString(m) } func (*QueryValidateManageFeesRequest) ProtoMessage() {} func (*QueryValidateManageFeesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{18} + return fileDescriptor_00949b75b1c10bfe, []int{16} } func (m *QueryValidateManageFeesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -839,7 +765,7 @@ func (m *QueryValidateManageFeesResponse) Reset() { *m = QueryValidateMa func (m *QueryValidateManageFeesResponse) String() string { return proto.CompactTextString(m) } func (*QueryValidateManageFeesResponse) ProtoMessage() {} func (*QueryValidateManageFeesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{19} + return fileDescriptor_00949b75b1c10bfe, []int{17} } func (m *QueryValidateManageFeesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -871,8 +797,6 @@ var xxx_messageInfo_QueryValidateManageFeesResponse proto.InternalMessageInfo func init() { proto.RegisterType((*QueryOrderFeeCalcRequest)(nil), "provenance.exchange.v1.QueryOrderFeeCalcRequest") proto.RegisterType((*QueryOrderFeeCalcResponse)(nil), "provenance.exchange.v1.QueryOrderFeeCalcResponse") - proto.RegisterType((*QuerySettlementFeeCalcRequest)(nil), "provenance.exchange.v1.QuerySettlementFeeCalcRequest") - proto.RegisterType((*QuerySettlementFeeCalcResponse)(nil), "provenance.exchange.v1.QuerySettlementFeeCalcResponse") proto.RegisterType((*QueryGetOrderRequest)(nil), "provenance.exchange.v1.QueryGetOrderRequest") proto.RegisterType((*QueryGetOrderResponse)(nil), "provenance.exchange.v1.QueryGetOrderResponse") proto.RegisterType((*QueryGetMarketOrdersRequest)(nil), "provenance.exchange.v1.QueryGetMarketOrdersRequest") @@ -896,68 +820,65 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 978 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x41, 0x6f, 0x1b, 0x45, - 0x14, 0xce, 0x24, 0x85, 0x26, 0x13, 0x15, 0xc4, 0xc3, 0x6d, 0xed, 0x4d, 0xd8, 0xb8, 0x5b, 0x04, - 0x21, 0x34, 0xbb, 0xb1, 0x43, 0x02, 0x45, 0xaa, 0x50, 0x12, 0x29, 0x55, 0x0e, 0x55, 0x5b, 0x57, - 0xe2, 0xc0, 0x01, 0x33, 0xf6, 0xbe, 0xb8, 0xab, 0xd8, 0x3b, 0xee, 0xee, 0x26, 0x2a, 0x8a, 0x22, - 0x21, 0x7e, 0x00, 0x42, 0xe2, 0xc2, 0x09, 0x71, 0x40, 0x9c, 0x39, 0x20, 0xc4, 0x81, 0x1f, 0x90, - 0x63, 0x45, 0x2f, 0x9c, 0x10, 0x4a, 0xf8, 0x21, 0x68, 0x67, 0xdf, 0x7a, 0xbd, 0xb1, 0x77, 0xb3, - 0xe6, 0xb6, 0x33, 0xf3, 0xbe, 0xef, 0x7d, 0xf3, 0x66, 0xde, 0x37, 0x36, 0x37, 0xfa, 0x9e, 0x3c, - 0x42, 0x57, 0xb8, 0x6d, 0xb4, 0xf0, 0x79, 0xfb, 0xa9, 0x70, 0x3b, 0x68, 0x1d, 0xd5, 0xac, 0x67, - 0x87, 0xe8, 0x7d, 0x69, 0xf6, 0x3d, 0x19, 0x48, 0xb8, 0x91, 0xc4, 0x98, 0x71, 0x8c, 0x79, 0x54, - 0xd3, 0x2a, 0x6d, 0xe9, 0xf7, 0xa4, 0xdf, 0x54, 0x51, 0x56, 0x34, 0x88, 0x20, 0x9a, 0x1e, 0x8d, - 0xac, 0x96, 0xf0, 0x43, 0xba, 0x16, 0x06, 0xa2, 0x66, 0xb5, 0xa5, 0xe3, 0xd2, 0xfa, 0x62, 0x47, - 0xca, 0x4e, 0x17, 0x2d, 0xd1, 0x77, 0x2c, 0xe1, 0xba, 0x32, 0x10, 0x81, 0x23, 0xdd, 0x18, 0x5d, - 0xea, 0xc8, 0x8e, 0x8c, 0x58, 0xc3, 0x2f, 0x9a, 0xbd, 0x9d, 0x21, 0x55, 0x7a, 0x36, 0x7a, 0x04, - 0x35, 0xbe, 0x67, 0xbc, 0xfc, 0x38, 0xd4, 0xfe, 0x30, 0x9c, 0xdd, 0x45, 0xdc, 0x11, 0xdd, 0x76, - 0x03, 0x9f, 0x1d, 0xa2, 0x1f, 0xc0, 0x3d, 0x3e, 0x27, 0xfc, 0x83, 0xa6, 0x02, 0x94, 0xa7, 0xab, - 0x6c, 0x79, 0xbe, 0x5e, 0x35, 0xc7, 0x6f, 0xce, 0xdc, 0xf2, 0x0f, 0x14, 0x45, 0x63, 0x56, 0xd0, - 0x57, 0x08, 0x6f, 0x39, 0x36, 0xc1, 0x67, 0xf2, 0xe1, 0xdb, 0x8e, 0x4d, 0xf0, 0x16, 0x7d, 0x19, - 0xbf, 0x4c, 0xf3, 0xca, 0x18, 0x69, 0x7e, 0x5f, 0xba, 0x3e, 0xc2, 0x63, 0x5e, 0x6a, 0x7b, 0xa8, - 0xca, 0xd0, 0xdc, 0x47, 0x6c, 0xca, 0xbe, 0xaa, 0x48, 0x99, 0x55, 0x67, 0x96, 0xe7, 0xeb, 0x15, - 0x93, 0xca, 0x1b, 0x16, 0xd4, 0xa4, 0x82, 0x9a, 0x3b, 0xd2, 0x71, 0xb7, 0xaf, 0x9c, 0xfe, 0xbd, - 0x34, 0xd5, 0x80, 0x18, 0xbc, 0x8b, 0xf8, 0x30, 0x82, 0xc2, 0xe7, 0x7c, 0xc1, 0xc7, 0x20, 0xe8, - 0x62, 0x0f, 0xdd, 0xa0, 0xb9, 0xdf, 0x15, 0x41, 0x8a, 0x79, 0xba, 0x18, 0x73, 0x39, 0xe1, 0xd8, - 0xed, 0x8a, 0x60, 0x88, 0xff, 0x0b, 0xbe, 0x38, 0xc4, 0xef, 0x85, 0xe9, 0x53, 0x09, 0x66, 0x8a, - 0x25, 0xa8, 0x24, 0x24, 0x8d, 0x90, 0x23, 0xc9, 0x60, 0x2c, 0xf1, 0xb7, 0x54, 0xc5, 0x9e, 0x24, - 0x12, 0x52, 0x27, 0x6a, 0x54, 0xb9, 0x9e, 0x15, 0x10, 0xd5, 0xd5, 0xa8, 0xf1, 0x92, 0x8a, 0xb8, - 0x8f, 0x41, 0x74, 0x20, 0x74, 0x17, 0x2a, 0x7c, 0x56, 0x1d, 0x64, 0xd3, 0xb1, 0xcb, 0xac, 0xca, - 0x96, 0xaf, 0x34, 0xae, 0xaa, 0xf1, 0x9e, 0x6d, 0xdc, 0xe4, 0xd7, 0x2f, 0x40, 0x88, 0xeb, 0x63, - 0xbe, 0x10, 0x2f, 0x3c, 0x10, 0xde, 0x01, 0x2d, 0xfb, 0x31, 0xe5, 0x02, 0x9f, 0xeb, 0xa9, 0xe9, - 0x98, 0xf3, 0x5a, 0x63, 0x36, 0x9a, 0xd8, 0xb3, 0x0d, 0x9d, 0x2f, 0x8e, 0xc7, 0x12, 0x77, 0x23, - 0x59, 0xdf, 0xb2, 0x6d, 0x0f, 0x7d, 0x3f, 0x4d, 0x5e, 0xe7, 0x57, 0x45, 0x34, 0xaf, 0xa8, 0xe7, - 0xb6, 0xcb, 0x7f, 0xfe, 0xba, 0x5a, 0xa2, 0xd2, 0x12, 0xe2, 0x49, 0xe0, 0x39, 0x6e, 0xa7, 0x11, - 0x07, 0x0e, 0xca, 0x37, 0xca, 0x49, 0x49, 0x35, 0x6a, 0x96, 0x30, 0xa0, 0xdb, 0x4d, 0x25, 0x34, - 0x16, 0xe8, 0xb6, 0xa6, 0xd7, 0x08, 0xb8, 0xc1, 0x6f, 0xa8, 0xc5, 0x68, 0x2b, 0x7b, 0xee, 0xbe, - 0x2c, 0x54, 0x84, 0x0a, 0xbf, 0x39, 0x02, 0x23, 0xc6, 0x12, 0x07, 0xb5, 0xf4, 0x48, 0x78, 0xa2, - 0x37, 0x10, 0x71, 0x9d, 0xbf, 0x99, 0x9a, 0xa5, 0x60, 0x83, 0x57, 0xd5, 0xf4, 0xa7, 0xa2, 0xeb, - 0xd8, 0x22, 0xc0, 0x9d, 0xf0, 0xf2, 0x63, 0xc4, 0x1a, 0x43, 0x6f, 0xf3, 0x5b, 0x39, 0x31, 0x44, - 0x74, 0x8f, 0xee, 0x4f, 0x1c, 0xf4, 0x40, 0xb8, 0xa2, 0x83, 0xbb, 0x88, 0xc5, 0x0e, 0xf5, 0x16, - 0x5f, 0xca, 0x84, 0x47, 0x19, 0xea, 0x5f, 0xbd, 0xc6, 0x5f, 0x51, 0x31, 0xf0, 0x13, 0xe3, 0x6f, - 0x8c, 0xf4, 0x3f, 0xac, 0x65, 0x39, 0x48, 0x96, 0x8b, 0x69, 0xb5, 0x09, 0x10, 0xb4, 0xcd, 0x95, - 0xaf, 0x5f, 0xfe, 0xfb, 0xdd, 0xf4, 0xdb, 0x60, 0x58, 0x19, 0x1e, 0xba, 0x8f, 0xe8, 0x47, 0x46, - 0x0a, 0xbf, 0x33, 0x3a, 0xdb, 0x91, 0x9e, 0x82, 0x8d, 0xdc, 0xcc, 0x59, 0x4d, 0xaa, 0x6d, 0x4e, - 0x0a, 0x23, 0xd5, 0x96, 0x52, 0xfd, 0x1e, 0xbc, 0x9b, 0xab, 0x3a, 0x71, 0x0f, 0xf8, 0x81, 0xf1, - 0x6b, 0xa9, 0xce, 0x85, 0x3b, 0xb9, 0xa9, 0x2f, 0x78, 0x82, 0xb6, 0x5a, 0x30, 0x9a, 0xf4, 0xad, - 0x29, 0x7d, 0x2b, 0xb0, 0x6c, 0xe5, 0xbd, 0x4c, 0xd6, 0x71, 0xec, 0x33, 0x27, 0xf0, 0x07, 0x4b, - 0xdc, 0x68, 0xd8, 0x05, 0x60, 0xfd, 0xb2, 0xcc, 0x63, 0xfc, 0x46, 0xfb, 0x60, 0x32, 0x10, 0xa9, - 0xbe, 0xab, 0x54, 0xaf, 0x43, 0x2d, 0x4b, 0x75, 0x74, 0xbb, 0xad, 0xe3, 0xc1, 0xb5, 0x3f, 0xa1, - 0x27, 0x16, 0x7e, 0x63, 0x89, 0x33, 0xa6, 0x0c, 0x05, 0x2e, 0x95, 0x32, 0xce, 0xd3, 0xb4, 0x8d, - 0x09, 0x51, 0x13, 0xd5, 0xdd, 0xb7, 0x8e, 0xc9, 0x07, 0x4f, 0xe0, 0xc7, 0xb8, 0xf5, 0x86, 0xcd, - 0xec, 0x92, 0xd6, 0x1b, 0xe3, 0x89, 0x97, 0xb4, 0xde, 0x58, 0xa7, 0x7c, 0x47, 0x89, 0xad, 0x82, - 0x9e, 0x2f, 0x16, 0x7e, 0x66, 0xfc, 0xf5, 0x0b, 0xde, 0x08, 0x66, 0x6e, 0xba, 0x11, 0xef, 0xd5, - 0xac, 0xc2, 0xf1, 0x24, 0xae, 0xae, 0xc4, 0xdd, 0x81, 0x95, 0xe2, 0x77, 0x01, 0xbe, 0x61, 0x7c, - 0x7e, 0xc8, 0x93, 0x61, 0x25, 0x37, 0x69, 0xca, 0xce, 0xb5, 0xf7, 0x0b, 0xc5, 0x16, 0xad, 0x5c, - 0x3f, 0x12, 0x70, 0xca, 0xe8, 0xa5, 0x1a, 0xe7, 0xf4, 0xf0, 0x51, 0x6e, 0xca, 0x9c, 0x07, 0x44, - 0xbb, 0xfb, 0x3f, 0x90, 0x24, 0x7d, 0x53, 0x49, 0x5f, 0x03, 0x33, 0x4b, 0xfa, 0x11, 0xa1, 0x2d, - 0xf5, 0xb3, 0x0d, 0x9b, 0x51, 0x7d, 0xe1, 0x25, 0xa3, 0x07, 0x72, 0xf4, 0x41, 0x81, 0xcd, 0x42, - 0x72, 0x46, 0x1e, 0x30, 0xed, 0xc3, 0x89, 0x71, 0xb4, 0x89, 0xfb, 0x6a, 0x13, 0x5b, 0xf0, 0xc9, - 0x04, 0x46, 0x31, 0xd8, 0x57, 0x4f, 0xf1, 0x85, 0x3f, 0x07, 0xfd, 0x6d, 0x3c, 0x3d, 0xd3, 0xd9, - 0x8b, 0x33, 0x9d, 0xfd, 0x73, 0xa6, 0xb3, 0x6f, 0xcf, 0xf5, 0xa9, 0x17, 0xe7, 0xfa, 0xd4, 0x5f, - 0xe7, 0xfa, 0x14, 0xaf, 0x38, 0x32, 0x43, 0xdd, 0x23, 0xf6, 0x99, 0xd9, 0x71, 0x82, 0xa7, 0x87, - 0x2d, 0xb3, 0x2d, 0x7b, 0x43, 0x0a, 0x56, 0x1d, 0x39, 0xac, 0xe7, 0xf9, 0x40, 0x51, 0xeb, 0x55, - 0xf5, 0x0f, 0x60, 0xfd, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x68, 0xfe, 0x92, 0x8e, 0xd3, 0x0c, + // 930 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0x41, 0x6f, 0xdc, 0x44, + 0x14, 0xce, 0x24, 0x85, 0x26, 0x13, 0x2a, 0xc4, 0xb0, 0x6d, 0x77, 0x9d, 0xe0, 0x6c, 0x5d, 0x84, + 0xa2, 0xd0, 0xd8, 0xd9, 0x0d, 0x2d, 0x14, 0xa9, 0x42, 0x49, 0xa4, 0x54, 0x39, 0x54, 0x6d, 0x8d, + 0xc4, 0x81, 0x03, 0xcb, 0xec, 0xfa, 0xc5, 0xb5, 0xe2, 0xf5, 0x6c, 0x6d, 0x27, 0x2a, 0x8a, 0x72, + 0xe1, 0x07, 0x20, 0x24, 0x2e, 0x9c, 0x10, 0x07, 0xc4, 0x99, 0x03, 0xe2, 0xc4, 0x15, 0x29, 0xc7, + 0x8a, 0x5e, 0x38, 0x21, 0x94, 0xf0, 0x43, 0x90, 0x67, 0x9e, 0xb3, 0xeb, 0xac, 0xed, 0xf5, 0x72, + 0xb3, 0x67, 0xde, 0xf7, 0xbd, 0x6f, 0x9e, 0xdf, 0xfb, 0x3c, 0xd4, 0x18, 0x84, 0xe2, 0x08, 0x02, + 0x1e, 0xf4, 0xc0, 0x82, 0x17, 0xbd, 0x67, 0x3c, 0x70, 0xc1, 0x3a, 0x6a, 0x59, 0xcf, 0x0f, 0x21, + 0xfc, 0xca, 0x1c, 0x84, 0x22, 0x16, 0xec, 0xc6, 0x30, 0xc6, 0x4c, 0x63, 0xcc, 0xa3, 0x96, 0xd6, + 0xe8, 0x89, 0xa8, 0x2f, 0xa2, 0x8e, 0x8c, 0xb2, 0xd4, 0x8b, 0x82, 0x68, 0xba, 0x7a, 0xb3, 0xba, + 0x3c, 0x4a, 0xe8, 0xba, 0x10, 0xf3, 0x96, 0xd5, 0x13, 0x5e, 0x80, 0xfb, 0xcb, 0xae, 0x10, 0xae, + 0x0f, 0x16, 0x1f, 0x78, 0x16, 0x0f, 0x02, 0x11, 0xf3, 0xd8, 0x13, 0x41, 0x8a, 0xae, 0xb9, 0xc2, + 0x15, 0x8a, 0x35, 0x79, 0xc2, 0xd5, 0xdb, 0x05, 0x52, 0x45, 0xe8, 0x40, 0x88, 0x50, 0xe3, 0x7b, + 0x42, 0xeb, 0x4f, 0x13, 0xed, 0x8f, 0x93, 0xd5, 0x5d, 0x80, 0x1d, 0xee, 0xf7, 0x6c, 0x78, 0x7e, + 0x08, 0x51, 0xcc, 0x1e, 0xd0, 0x05, 0x1e, 0x1d, 0x74, 0x24, 0xa0, 0x3e, 0xdb, 0x24, 0xab, 0x8b, + 0xed, 0xa6, 0x99, 0x7f, 0x38, 0x73, 0x2b, 0x3a, 0x90, 0x14, 0xf6, 0x3c, 0xc7, 0xa7, 0x04, 0xde, + 0xf5, 0x1c, 0x84, 0xcf, 0x95, 0xc3, 0xb7, 0x3d, 0x07, 0xe1, 0x5d, 0x7c, 0x32, 0x7e, 0x99, 0xa5, + 0x8d, 0x1c, 0x69, 0xd1, 0x40, 0x04, 0x11, 0xb0, 0xa7, 0xb4, 0xd6, 0x0b, 0x41, 0x96, 0xa1, 0xb3, + 0x0f, 0xd0, 0x11, 0x03, 0x59, 0x91, 0x3a, 0x69, 0xce, 0xad, 0x2e, 0xb6, 0x1b, 0x26, 0x96, 0x37, + 0x29, 0xa8, 0x89, 0x05, 0x35, 0x77, 0x84, 0x17, 0x6c, 0x5f, 0x39, 0xfd, 0x7b, 0x65, 0xc6, 0x66, + 0x29, 0x78, 0x17, 0xe0, 0xb1, 0x82, 0xb2, 0x2f, 0xe8, 0x52, 0x04, 0x71, 0xec, 0x43, 0x1f, 0x82, + 0xb8, 0xb3, 0xef, 0xf3, 0x38, 0xc3, 0x3c, 0x5b, 0x8d, 0xb9, 0x3e, 0xe4, 0xd8, 0xf5, 0x79, 0x3c, + 0xc2, 0xff, 0x25, 0x5d, 0x1e, 0xe1, 0x0f, 0x93, 0xf4, 0x99, 0x04, 0x73, 0xd5, 0x12, 0x34, 0x86, + 0x24, 0x76, 0xc2, 0x31, 0xcc, 0x60, 0xb4, 0x68, 0x4d, 0x56, 0xec, 0x21, 0xc4, 0xaa, 0x9a, 0xf8, + 0x21, 0x1b, 0x74, 0x5e, 0x7e, 0x85, 0x8e, 0xe7, 0xd4, 0x49, 0x93, 0xac, 0x5e, 0xb1, 0xaf, 0xca, + 0xf7, 0x3d, 0xc7, 0xb8, 0x49, 0xaf, 0x5f, 0x82, 0xa8, 0x02, 0x1b, 0x1f, 0xd3, 0xa5, 0x74, 0xe3, + 0x11, 0x0f, 0x0f, 0x70, 0x3b, 0x4a, 0x29, 0x97, 0xe8, 0x42, 0x5f, 0x2e, 0xa7, 0x9c, 0xd7, 0xec, + 0x79, 0xb5, 0xb0, 0xe7, 0x18, 0x3a, 0x5d, 0xce, 0xc7, 0x22, 0xb7, 0x3d, 0xdc, 0xdf, 0x72, 0x9c, + 0x10, 0xa2, 0x28, 0x4b, 0xde, 0xa6, 0x57, 0xb9, 0x5a, 0x97, 0xd4, 0x0b, 0xdb, 0xf5, 0x3f, 0x7f, + 0x5d, 0xaf, 0x61, 0x5d, 0x10, 0xf1, 0x69, 0x1c, 0x7a, 0x81, 0x6b, 0xa7, 0x81, 0xc6, 0x0a, 0x7d, + 0xa7, 0x80, 0x13, 0x93, 0x6a, 0xd8, 0xe9, 0x49, 0x80, 0xef, 0x67, 0x12, 0x1a, 0x4b, 0xd8, 0x6a, + 0xd9, 0x3d, 0x04, 0xde, 0xa5, 0x37, 0xe4, 0xa6, 0x3a, 0xca, 0x5e, 0xb0, 0x2f, 0x2a, 0x15, 0xa1, + 0x41, 0x6f, 0x8e, 0xc1, 0x90, 0xb1, 0x46, 0x99, 0xdc, 0x7a, 0xc2, 0x43, 0xde, 0xbf, 0x10, 0x71, + 0x9d, 0xbe, 0x9d, 0x59, 0xc5, 0x60, 0x83, 0x36, 0xe5, 0xf2, 0x67, 0xdc, 0xf7, 0x1c, 0x1e, 0xc3, + 0x4e, 0xd2, 0xb9, 0xa0, 0x58, 0x53, 0xe8, 0x6d, 0x7a, 0xab, 0x24, 0x06, 0x89, 0x1e, 0x50, 0x3d, + 0x13, 0xf4, 0x88, 0x07, 0xdc, 0x85, 0x5d, 0x80, 0x6a, 0x1f, 0xf5, 0x16, 0x5d, 0x29, 0x84, 0xab, + 0x0c, 0xed, 0x3f, 0xde, 0xa0, 0xaf, 0xc9, 0x18, 0xf6, 0x13, 0xa1, 0x6f, 0x8d, 0x0d, 0x2f, 0xdb, + 0x28, 0x1a, 0xff, 0x22, 0x0b, 0xd2, 0x5a, 0x53, 0x20, 0xf0, 0x98, 0x6b, 0x5f, 0xbf, 0xfa, 0xf7, + 0xbb, 0xd9, 0x77, 0x99, 0x61, 0x15, 0x18, 0xe0, 0x3e, 0x40, 0xa4, 0x5c, 0x90, 0xfd, 0x40, 0xe8, + 0xb5, 0x4c, 0xfb, 0xb3, 0x3b, 0xa5, 0x09, 0x2f, 0x0d, 0x96, 0xb6, 0x5e, 0x31, 0x1a, 0xa5, 0x6d, + 0x48, 0x69, 0x6b, 0x6c, 0xd5, 0x2a, 0xf3, 0x66, 0xeb, 0x38, 0x1d, 0xd6, 0x13, 0xf6, 0x3b, 0x19, + 0x8e, 0xf4, 0xe8, 0x28, 0xb1, 0xcd, 0x49, 0x99, 0x73, 0x86, 0x56, 0xfb, 0x60, 0x3a, 0x10, 0xaa, + 0xbe, 0x2f, 0x55, 0x6f, 0xb2, 0x56, 0x91, 0x6a, 0xd5, 0x22, 0xd6, 0xf1, 0x45, 0xef, 0x9c, 0xe0, + 0x4f, 0x86, 0xfd, 0x46, 0x86, 0xf6, 0x92, 0x99, 0x4a, 0x36, 0x51, 0x4a, 0x9e, 0x31, 0x68, 0x77, + 0xa7, 0x44, 0x4d, 0x55, 0xf7, 0xc8, 0x3a, 0x46, 0x33, 0x39, 0x61, 0x3f, 0xa6, 0xfd, 0x3b, 0xea, + 0x08, 0x13, 0xfa, 0x37, 0xc7, 0x58, 0x26, 0xf4, 0x6f, 0xae, 0xdd, 0xbc, 0x27, 0xc5, 0x36, 0x99, + 0x5e, 0x2e, 0x96, 0xfd, 0x4c, 0xe8, 0x9b, 0x97, 0x0c, 0x86, 0x99, 0xa5, 0xe9, 0xc6, 0x0c, 0x4c, + 0xb3, 0x2a, 0xc7, 0xa3, 0xb8, 0xb6, 0x14, 0x77, 0x87, 0xad, 0x55, 0xef, 0x05, 0xf6, 0x0d, 0xa1, + 0x8b, 0x23, 0xc6, 0xc6, 0xd6, 0x4a, 0x93, 0x66, 0x3c, 0x51, 0x7b, 0xbf, 0x52, 0x6c, 0xd5, 0xca, + 0x0d, 0x94, 0x80, 0x53, 0x82, 0x76, 0x9f, 0x67, 0x97, 0xec, 0xa3, 0xd2, 0x94, 0x25, 0x2e, 0xac, + 0xdd, 0xff, 0x1f, 0x48, 0x94, 0x7e, 0x4f, 0x4a, 0xdf, 0x60, 0x66, 0x91, 0xf4, 0x23, 0x44, 0x5b, + 0xf2, 0xe2, 0x02, 0x1d, 0x55, 0x5f, 0xf6, 0x8a, 0xe0, 0x5f, 0x66, 0xdc, 0x95, 0xd9, 0xbd, 0x4a, + 0x72, 0xc6, 0xfe, 0x02, 0xda, 0x87, 0x53, 0xe3, 0xf0, 0x10, 0x0f, 0xe5, 0x21, 0xb6, 0xd8, 0x27, + 0x53, 0x18, 0xc5, 0xc5, 0xb9, 0xfa, 0x92, 0x2f, 0xb9, 0x10, 0x45, 0xdb, 0x70, 0x7a, 0xa6, 0x93, + 0x97, 0x67, 0x3a, 0xf9, 0xe7, 0x4c, 0x27, 0xdf, 0x9e, 0xeb, 0x33, 0x2f, 0xcf, 0xf5, 0x99, 0xbf, + 0xce, 0xf5, 0x19, 0xda, 0xf0, 0x44, 0x81, 0xba, 0x27, 0xe4, 0x73, 0xd3, 0xf5, 0xe2, 0x67, 0x87, + 0x5d, 0xb3, 0x27, 0xfa, 0x23, 0x0a, 0xd6, 0x3d, 0x31, 0xaa, 0xe7, 0xc5, 0x85, 0xa2, 0xee, 0xeb, + 0xf2, 0x0e, 0xbc, 0xf9, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd0, 0x18, 0x6f, 0x33, 0xd5, 0x0b, 0x00, 0x00, } @@ -975,8 +896,6 @@ const _ = grpc.SupportPackageIsVersion4 type QueryClient interface { // QueryOrderFeeCalc calculates the fees that will be associated with the provided order. QueryOrderFeeCalc(ctx context.Context, in *QueryOrderFeeCalcRequest, opts ...grpc.CallOption) (*QueryOrderFeeCalcResponse, error) - // QuerySettlementFeeCalc calculates the fees that will be associated with the provided settlement. - QuerySettlementFeeCalc(ctx context.Context, in *QuerySettlementFeeCalcRequest, opts ...grpc.CallOption) (*QuerySettlementFeeCalcResponse, error) // QueryGetOrder looks up an order by id. QueryGetOrder(ctx context.Context, in *QueryGetOrderRequest, opts ...grpc.CallOption) (*QueryGetOrderResponse, error) // QueryGetMarketOrders looks up the orders in a market. @@ -1012,15 +931,6 @@ func (c *queryClient) QueryOrderFeeCalc(ctx context.Context, in *QueryOrderFeeCa return out, nil } -func (c *queryClient) QuerySettlementFeeCalc(ctx context.Context, in *QuerySettlementFeeCalcRequest, opts ...grpc.CallOption) (*QuerySettlementFeeCalcResponse, error) { - out := new(QuerySettlementFeeCalcResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QuerySettlementFeeCalc", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *queryClient) QueryGetOrder(ctx context.Context, in *QueryGetOrderRequest, opts ...grpc.CallOption) (*QueryGetOrderResponse, error) { out := new(QueryGetOrderResponse) err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetOrder", in, out, opts...) @@ -1097,8 +1007,6 @@ func (c *queryClient) QueryValidateManageFees(ctx context.Context, in *QueryVali type QueryServer interface { // QueryOrderFeeCalc calculates the fees that will be associated with the provided order. QueryOrderFeeCalc(context.Context, *QueryOrderFeeCalcRequest) (*QueryOrderFeeCalcResponse, error) - // QuerySettlementFeeCalc calculates the fees that will be associated with the provided settlement. - QuerySettlementFeeCalc(context.Context, *QuerySettlementFeeCalcRequest) (*QuerySettlementFeeCalcResponse, error) // QueryGetOrder looks up an order by id. QueryGetOrder(context.Context, *QueryGetOrderRequest) (*QueryGetOrderResponse, error) // QueryGetMarketOrders looks up the orders in a market. @@ -1124,9 +1032,6 @@ type UnimplementedQueryServer struct { func (*UnimplementedQueryServer) QueryOrderFeeCalc(ctx context.Context, req *QueryOrderFeeCalcRequest) (*QueryOrderFeeCalcResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryOrderFeeCalc not implemented") } -func (*UnimplementedQueryServer) QuerySettlementFeeCalc(ctx context.Context, req *QuerySettlementFeeCalcRequest) (*QuerySettlementFeeCalcResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QuerySettlementFeeCalc not implemented") -} func (*UnimplementedQueryServer) QueryGetOrder(ctx context.Context, req *QueryGetOrderRequest) (*QueryGetOrderResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryGetOrder not implemented") } @@ -1174,24 +1079,6 @@ func _Query_QueryOrderFeeCalc_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } -func _Query_QuerySettlementFeeCalc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QuerySettlementFeeCalcRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(QueryServer).QuerySettlementFeeCalc(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QuerySettlementFeeCalc", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QuerySettlementFeeCalc(ctx, req.(*QuerySettlementFeeCalcRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _Query_QueryGetOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryGetOrderRequest) if err := dec(in); err != nil { @@ -1344,10 +1231,6 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "QueryOrderFeeCalc", Handler: _Query_QueryOrderFeeCalc_Handler, }, - { - MethodName: "QuerySettlementFeeCalc", - Handler: _Query_QuerySettlementFeeCalc_Handler, - }, { MethodName: "QueryGetOrder", Handler: _Query_QueryGetOrder_Handler, @@ -1497,52 +1380,6 @@ func (m *QueryOrderFeeCalcResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } -func (m *QuerySettlementFeeCalcRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *QuerySettlementFeeCalcRequest) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *QuerySettlementFeeCalcRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - return len(dAtA) - i, nil -} - -func (m *QuerySettlementFeeCalcResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *QuerySettlementFeeCalcResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *QuerySettlementFeeCalcResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - return len(dAtA) - i, nil -} - func (m *QueryGetOrderRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1993,24 +1830,6 @@ func (m *QueryOrderFeeCalcResponse) Size() (n int) { return n } -func (m *QuerySettlementFeeCalcRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - return n -} - -func (m *QuerySettlementFeeCalcResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - return n -} - func (m *QueryGetOrderRequest) Size() (n int) { if m == nil { return 0 @@ -2451,106 +2270,6 @@ func (m *QueryOrderFeeCalcResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QuerySettlementFeeCalcRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: QuerySettlementFeeCalcRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QuerySettlementFeeCalcRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipQuery(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *QuerySettlementFeeCalcResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: QuerySettlementFeeCalcResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QuerySettlementFeeCalcResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipQuery(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *QueryGetOrderRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/exchange/query.pb.gw.go b/x/exchange/query.pb.gw.go index dccecd1e31..4a13629453 100644 --- a/x/exchange/query.pb.gw.go +++ b/x/exchange/query.pb.gw.go @@ -67,24 +67,6 @@ func local_request_Query_QueryOrderFeeCalc_0(ctx context.Context, marshaler runt } -func request_Query_QuerySettlementFeeCalc_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QuerySettlementFeeCalcRequest - var metadata runtime.ServerMetadata - - msg, err := client.QuerySettlementFeeCalc(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_Query_QuerySettlementFeeCalc_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QuerySettlementFeeCalcRequest - var metadata runtime.ServerMetadata - - msg, err := server.QuerySettlementFeeCalc(ctx, &protoReq) - return msg, metadata, err - -} - func request_Query_QueryGetOrder_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetOrderRequest var metadata runtime.ServerMetadata @@ -435,26 +417,6 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) - mux.Handle("GET", pattern_Query_QuerySettlementFeeCalc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_Query_QuerySettlementFeeCalc_0(rctx, inboundMarshaler, server, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Query_QuerySettlementFeeCalc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("GET", pattern_Query_QueryGetOrder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -676,26 +638,6 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) - mux.Handle("GET", pattern_Query_QuerySettlementFeeCalc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_Query_QuerySettlementFeeCalc_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Query_QuerySettlementFeeCalc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("GET", pattern_Query_QueryGetOrder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -862,8 +804,6 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie var ( pattern_Query_QueryOrderFeeCalc_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "fees", "order"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QuerySettlementFeeCalc_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "fees", "settlement"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetOrder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "order", "order_id"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryGetMarketOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"provenance", "exchange", "v1", "market", "market_id", "orders"}, "", runtime.AssumeColonVerbOpt(false))) @@ -884,8 +824,6 @@ var ( var ( forward_Query_QueryOrderFeeCalc_0 = runtime.ForwardResponseMessage - forward_Query_QuerySettlementFeeCalc_0 = runtime.ForwardResponseMessage - forward_Query_QueryGetOrder_0 = runtime.ForwardResponseMessage forward_Query_QueryGetMarketOrders_0 = runtime.ForwardResponseMessage From d62324fc6a0c4285f7af03c17a5038783a1be717 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 16:13:06 -0600 Subject: [PATCH 207/309] [1658]: Change QueryGetAddressOrders to QueryGetOwnerOrders. Add a QueryGetAssetOrders. Fill in query messages for the order queries. --- docs/proto-docs.md | 101 +- proto/provenance/exchange/v1/query.proto | 80 +- x/exchange/keeper/grpc_query.go | 12 +- x/exchange/query.pb.go | 1565 +++++++++++++++++++--- x/exchange/query.pb.gw.go | 214 ++- 5 files changed, 1711 insertions(+), 261 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index f3b3b047ab..f8c363ccde 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -91,14 +91,16 @@ - [GenesisState](#provenance.exchange.v1.GenesisState) - [provenance/exchange/v1/query.proto](#provenance/exchange/v1/query.proto) - - [QueryGetAddressOrdersRequest](#provenance.exchange.v1.QueryGetAddressOrdersRequest) - - [QueryGetAddressOrdersResponse](#provenance.exchange.v1.QueryGetAddressOrdersResponse) - [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) - [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) + - [QueryGetAssetOrdersRequest](#provenance.exchange.v1.QueryGetAssetOrdersRequest) + - [QueryGetAssetOrdersResponse](#provenance.exchange.v1.QueryGetAssetOrdersResponse) - [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) - [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) - [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) - [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) + - [QueryGetOwnerOrdersRequest](#provenance.exchange.v1.QueryGetOwnerOrdersRequest) + - [QueryGetOwnerOrdersResponse](#provenance.exchange.v1.QueryGetOwnerOrdersResponse) - [QueryMarketInfoRequest](#provenance.exchange.v1.QueryMarketInfoRequest) - [QueryMarketInfoResponse](#provenance.exchange.v1.QueryMarketInfoResponse) - [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) @@ -1889,45 +1891,63 @@ GenesisState is the data that should be loaded into the exchange module during g - + -### QueryGetAddressOrdersRequest -QueryGetAddressOrdersRequest is a request message for the QueryGetAddressOrders endpoint. +### QueryGetAllOrdersRequest +QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `address` | [string](#string) | | TODO[1658]: QueryGetAddressOrdersRequest | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - + -### QueryGetAddressOrdersResponse -QueryGetAddressOrdersResponse is a response message for the QueryGetAddressOrders endpoint. +### QueryGetAllOrdersResponse +QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are a page of the all orders. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | - -### QueryGetAllOrdersRequest -QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint. + +### QueryGetAssetOrdersRequest +QueryGetAssetOrdersRequest is a request message for the QueryGetAssetOrders endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `asset` | [string](#string) | | asset is the denom of assets to get orders for. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - -### QueryGetAllOrdersResponse -QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoint. + + + + + +### QueryGetAssetOrdersResponse +QueryGetAssetOrdersResponse is a response message for the QueryGetAssetOrders endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are a page of the orders for the provided asset. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | @@ -1942,7 +1962,8 @@ QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders en | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `market_id` | [uint32](#uint32) | | TODO[1658]: QueryGetMarketOrdersRequest | +| `market_id` | [uint32](#uint32) | | market_id is the id of the market to get all the orders for. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | @@ -1955,6 +1976,12 @@ QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders en QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are a page of the orders in the provided market. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | + + @@ -1967,7 +1994,7 @@ QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `order_id` | [uint64](#uint64) | | TODO[1658]: QueryGetOrderRequest | +| `order_id` | [uint64](#uint64) | | order_id is the id of the order to look up. | @@ -1980,6 +2007,43 @@ QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order` | [Order](#provenance.exchange.v1.Order) | | order is the requested order. | + + + + + + + + +### QueryGetOwnerOrdersRequest +QueryGetOwnerOrdersRequest is a request message for the QueryGetOwnerOrders endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `owner` | [string](#string) | | owner is the bech32 address string of the owner to get the orders for. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | + + + + + + + + +### QueryGetOwnerOrdersResponse +QueryGetOwnerOrdersResponse is a response message for the QueryGetOwnerOrders endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are a page of the orders for the provided address. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | + + @@ -2128,7 +2192,8 @@ Query is the service for exchange module's query endpoints. | `QueryOrderFeeCalc` | [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) | [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) | QueryOrderFeeCalc calculates the fees that will be associated with the provided order. | GET|/provenance/exchange/v1/fees/order| | `QueryGetOrder` | [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) | [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) | QueryGetOrder looks up an order by id. | GET|/provenance/exchange/v1/order/{order_id}| | `QueryGetMarketOrders` | [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) | [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) | QueryGetMarketOrders looks up the orders in a market. | GET|/provenance/exchange/v1/market/{market_id}/orders| -| `QueryGetAddressOrders` | [QueryGetAddressOrdersRequest](#provenance.exchange.v1.QueryGetAddressOrdersRequest) | [QueryGetAddressOrdersResponse](#provenance.exchange.v1.QueryGetAddressOrdersResponse) | QueryGetAddressOrders looks up the orders from the provided address. | GET|/provenance/exchange/v1/orders/{address}| +| `QueryGetOwnerOrders` | [QueryGetOwnerOrdersRequest](#provenance.exchange.v1.QueryGetOwnerOrdersRequest) | [QueryGetOwnerOrdersResponse](#provenance.exchange.v1.QueryGetOwnerOrdersResponse) | QueryGetOwnerOrders looks up the orders from the provided owner address. | GET|/provenance/exchange/v1/orders/owner/{owner}| +| `QueryGetAssetOrders` | [QueryGetAssetOrdersRequest](#provenance.exchange.v1.QueryGetAssetOrdersRequest) | [QueryGetAssetOrdersResponse](#provenance.exchange.v1.QueryGetAssetOrdersResponse) | QueryGetAssetOrders looks up the orders for a specific asset denom. | GET|/provenance/exchange/v1/orders/asset/{asset}| | `QueryGetAllOrders` | [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) | [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) | QueryGetAllOrders gets all orders in the exchange module. | GET|/provenance/exchange/v1/orders| | `QueryMarketInfo` | [QueryMarketInfoRequest](#provenance.exchange.v1.QueryMarketInfoRequest) | [QueryMarketInfoResponse](#provenance.exchange.v1.QueryMarketInfoResponse) | QueryMarketInfo returns the information/details about a market. | GET|/provenance/exchange/v1/market/{market_id}| | `QueryParams` | [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) | [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) | QueryParams returns the exchange module parameters. | GET|/provenance/exchange/v1/params| diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 70e35fd824..6313617479 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -7,6 +7,7 @@ option java_package = "io.provenance.exchange.v1"; option java_multiple_files = true; import "cosmos_proto/cosmos.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; import "cosmos/base/v1beta1/coin.proto"; import "google/api/annotations.proto"; import "gogoproto/gogo.proto"; @@ -29,9 +30,14 @@ service Query { option (google.api.http).get = "/provenance/exchange/v1/market/{market_id}/orders"; } - // QueryGetAddressOrders looks up the orders from the provided address. - rpc QueryGetAddressOrders(QueryGetAddressOrdersRequest) returns (QueryGetAddressOrdersResponse) { - option (google.api.http).get = "/provenance/exchange/v1/orders/{address}"; + // QueryGetOwnerOrders looks up the orders from the provided owner address. + rpc QueryGetOwnerOrders(QueryGetOwnerOrdersRequest) returns (QueryGetOwnerOrdersResponse) { + option (google.api.http).get = "/provenance/exchange/v1/orders/owner/{owner}"; + } + + // QueryGetAssetOrders looks up the orders for a specific asset denom. + rpc QueryGetAssetOrders(QueryGetAssetOrdersRequest) returns (QueryGetAssetOrdersResponse) { + option (google.api.http).get = "/provenance/exchange/v1/orders/asset/{asset}"; } // QueryGetAllOrders gets all orders in the exchange module. @@ -92,41 +98,83 @@ message QueryOrderFeeCalcResponse { // QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. message QueryGetOrderRequest { + // order_id is the id of the order to look up. uint64 order_id = 1; - // TODO[1658]: QueryGetOrderRequest } + // QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. message QueryGetOrderResponse { - // TODO[1658]: QueryGetOrderResponse + // order is the requested order. + Order order = 1; } // QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. message QueryGetMarketOrdersRequest { + // market_id is the id of the market to get all the orders for. uint32 market_id = 1; - // TODO[1658]: QueryGetMarketOrdersRequest + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 99; } + // QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders endpoint. message QueryGetMarketOrdersResponse { - // TODO[1658]: QueryGetMarketOrdersResponse + // orders are a page of the orders in the provided market. + repeated Order orders = 1; + + // pagination is the resulting pagination parameters. + cosmos.base.query.v1beta1.PageResponse pagination = 99; } -// QueryGetAddressOrdersRequest is a request message for the QueryGetAddressOrders endpoint. -message QueryGetAddressOrdersRequest { - string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - // TODO[1658]: QueryGetAddressOrdersRequest +// QueryGetOwnerOrdersRequest is a request message for the QueryGetOwnerOrders endpoint. +message QueryGetOwnerOrdersRequest { + // owner is the bech32 address string of the owner to get the orders for. + string owner = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 99; } -// QueryGetAddressOrdersResponse is a response message for the QueryGetAddressOrders endpoint. -message QueryGetAddressOrdersResponse { - // TODO[1658]: QueryGetAddressOrdersResponse + +// QueryGetOwnerOrdersResponse is a response message for the QueryGetOwnerOrders endpoint. +message QueryGetOwnerOrdersResponse { + // orders are a page of the orders for the provided address. + repeated Order orders = 1; + + // pagination is the resulting pagination parameters. + cosmos.base.query.v1beta1.PageResponse pagination = 99; +} + +// QueryGetAssetOrdersRequest is a request message for the QueryGetAssetOrders endpoint. +message QueryGetAssetOrdersRequest { + // asset is the denom of assets to get orders for. + string asset = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 99; +} + +// QueryGetAssetOrdersResponse is a response message for the QueryGetAssetOrders endpoint. +message QueryGetAssetOrdersResponse { + // orders are a page of the orders for the provided asset. + repeated Order orders = 1; + + // pagination is the resulting pagination parameters. + cosmos.base.query.v1beta1.PageResponse pagination = 99; } // QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint. message QueryGetAllOrdersRequest { - // TODO[1658]: QueryGetAllOrdersRequest + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 99; } + // QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoint. message QueryGetAllOrdersResponse { - // TODO[1658]: QueryGetAllOrdersResponse + // orders are a page of the all orders. + repeated Order orders = 1; + + // pagination is the resulting pagination parameters. + cosmos.base.query.v1beta1.PageResponse pagination = 99; } // QueryMarketInfoRequest is a request message for the QueryMarketInfo endpoint. diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 12bb7deaaa..a8b7e1c39f 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -79,9 +79,15 @@ func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.Q panic("not implemented") } -// QueryGetAddressOrders looks up the orders from the provided address. -func (k QueryServer) QueryGetAddressOrders(goCtx context.Context, req *exchange.QueryGetAddressOrdersRequest) (*exchange.QueryGetAddressOrdersResponse, error) { - // TODO[1658]: Implement QueryGetAddressOrders query +// QueryGetOwnerOrders looks up the orders from the provided owner address. +func (k QueryServer) QueryGetOwnerOrders(goCtx context.Context, req *exchange.QueryGetOwnerOrdersRequest) (*exchange.QueryGetOwnerOrdersResponse, error) { + // TODO[1658]: Implement QueryGetOwnerOrders query + panic("not implemented") +} + +// QueryGetAssetOrders looks up the orders for a specific asset denom. +func (k QueryServer) QueryGetAssetOrders(goCtx context.Context, req *exchange.QueryGetAssetOrdersRequest) (*exchange.QueryGetAssetOrdersResponse, error) { + // TODO[1658]: Implement QueryGetAssetOrders query panic("not implemented") } diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index a6bab4249e..57829640ff 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -8,6 +8,7 @@ import ( fmt "fmt" _ "github.com/cosmos/cosmos-proto" types "github.com/cosmos/cosmos-sdk/types" + query "github.com/cosmos/cosmos-sdk/types/query" _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" @@ -164,6 +165,7 @@ func (m *QueryOrderFeeCalcResponse) GetSettlementRatioFeeOptions() []types.Coin // QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. type QueryGetOrderRequest struct { + // order_id is the id of the order to look up. OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` } @@ -209,6 +211,8 @@ func (m *QueryGetOrderRequest) GetOrderId() uint64 { // QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. type QueryGetOrderResponse struct { + // order is the requested order. + Order *Order `protobuf:"bytes,1,opt,name=order,proto3" json:"order,omitempty"` } func (m *QueryGetOrderResponse) Reset() { *m = QueryGetOrderResponse{} } @@ -244,9 +248,19 @@ func (m *QueryGetOrderResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryGetOrderResponse proto.InternalMessageInfo +func (m *QueryGetOrderResponse) GetOrder() *Order { + if m != nil { + return m.Order + } + return nil +} + // QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. type QueryGetMarketOrdersRequest struct { + // market_id is the id of the market to get all the orders for. MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (m *QueryGetMarketOrdersRequest) Reset() { *m = QueryGetMarketOrdersRequest{} } @@ -289,8 +303,19 @@ func (m *QueryGetMarketOrdersRequest) GetMarketId() uint32 { return 0 } +func (m *QueryGetMarketOrdersRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + // QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders endpoint. type QueryGetMarketOrdersResponse struct { + // orders are a page of the orders in the provided market. + Orders []*Order `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` + // pagination is the resulting pagination parameters. + Pagination *query.PageResponse `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (m *QueryGetMarketOrdersResponse) Reset() { *m = QueryGetMarketOrdersResponse{} } @@ -326,23 +351,40 @@ func (m *QueryGetMarketOrdersResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryGetMarketOrdersResponse proto.InternalMessageInfo -// QueryGetAddressOrdersRequest is a request message for the QueryGetAddressOrders endpoint. -type QueryGetAddressOrdersRequest struct { - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +func (m *QueryGetMarketOrdersResponse) GetOrders() []*Order { + if m != nil { + return m.Orders + } + return nil +} + +func (m *QueryGetMarketOrdersResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryGetOwnerOrdersRequest is a request message for the QueryGetOwnerOrders endpoint. +type QueryGetOwnerOrdersRequest struct { + // owner is the bech32 address string of the owner to get the orders for. + Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` } -func (m *QueryGetAddressOrdersRequest) Reset() { *m = QueryGetAddressOrdersRequest{} } -func (m *QueryGetAddressOrdersRequest) String() string { return proto.CompactTextString(m) } -func (*QueryGetAddressOrdersRequest) ProtoMessage() {} -func (*QueryGetAddressOrdersRequest) Descriptor() ([]byte, []int) { +func (m *QueryGetOwnerOrdersRequest) Reset() { *m = QueryGetOwnerOrdersRequest{} } +func (m *QueryGetOwnerOrdersRequest) String() string { return proto.CompactTextString(m) } +func (*QueryGetOwnerOrdersRequest) ProtoMessage() {} +func (*QueryGetOwnerOrdersRequest) Descriptor() ([]byte, []int) { return fileDescriptor_00949b75b1c10bfe, []int{6} } -func (m *QueryGetAddressOrdersRequest) XXX_Unmarshal(b []byte) error { +func (m *QueryGetOwnerOrdersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryGetAddressOrdersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryGetOwnerOrdersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryGetAddressOrdersRequest.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryGetOwnerOrdersRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -352,41 +394,162 @@ func (m *QueryGetAddressOrdersRequest) XXX_Marshal(b []byte, deterministic bool) return b[:n], nil } } -func (m *QueryGetAddressOrdersRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryGetAddressOrdersRequest.Merge(m, src) +func (m *QueryGetOwnerOrdersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetOwnerOrdersRequest.Merge(m, src) } -func (m *QueryGetAddressOrdersRequest) XXX_Size() int { +func (m *QueryGetOwnerOrdersRequest) XXX_Size() int { return m.Size() } -func (m *QueryGetAddressOrdersRequest) XXX_DiscardUnknown() { - xxx_messageInfo_QueryGetAddressOrdersRequest.DiscardUnknown(m) +func (m *QueryGetOwnerOrdersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetOwnerOrdersRequest.DiscardUnknown(m) } -var xxx_messageInfo_QueryGetAddressOrdersRequest proto.InternalMessageInfo +var xxx_messageInfo_QueryGetOwnerOrdersRequest proto.InternalMessageInfo -func (m *QueryGetAddressOrdersRequest) GetAddress() string { +func (m *QueryGetOwnerOrdersRequest) GetOwner() string { if m != nil { - return m.Address + return m.Owner } return "" } -// QueryGetAddressOrdersResponse is a response message for the QueryGetAddressOrders endpoint. -type QueryGetAddressOrdersResponse struct { +func (m *QueryGetOwnerOrdersRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryGetOwnerOrdersResponse is a response message for the QueryGetOwnerOrders endpoint. +type QueryGetOwnerOrdersResponse struct { + // orders are a page of the orders for the provided address. + Orders []*Order `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` + // pagination is the resulting pagination parameters. + Pagination *query.PageResponse `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` } -func (m *QueryGetAddressOrdersResponse) Reset() { *m = QueryGetAddressOrdersResponse{} } -func (m *QueryGetAddressOrdersResponse) String() string { return proto.CompactTextString(m) } -func (*QueryGetAddressOrdersResponse) ProtoMessage() {} -func (*QueryGetAddressOrdersResponse) Descriptor() ([]byte, []int) { +func (m *QueryGetOwnerOrdersResponse) Reset() { *m = QueryGetOwnerOrdersResponse{} } +func (m *QueryGetOwnerOrdersResponse) String() string { return proto.CompactTextString(m) } +func (*QueryGetOwnerOrdersResponse) ProtoMessage() {} +func (*QueryGetOwnerOrdersResponse) Descriptor() ([]byte, []int) { return fileDescriptor_00949b75b1c10bfe, []int{7} } -func (m *QueryGetAddressOrdersResponse) XXX_Unmarshal(b []byte) error { +func (m *QueryGetOwnerOrdersResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetOwnerOrdersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetOwnerOrdersResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetOwnerOrdersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetOwnerOrdersResponse.Merge(m, src) +} +func (m *QueryGetOwnerOrdersResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryGetOwnerOrdersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetOwnerOrdersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetOwnerOrdersResponse proto.InternalMessageInfo + +func (m *QueryGetOwnerOrdersResponse) GetOrders() []*Order { + if m != nil { + return m.Orders + } + return nil +} + +func (m *QueryGetOwnerOrdersResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryGetAssetOrdersRequest is a request message for the QueryGetAssetOrders endpoint. +type QueryGetAssetOrdersRequest struct { + // asset is the denom of assets to get orders for. + Asset string `protobuf:"bytes,1,opt,name=asset,proto3" json:"asset,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryGetAssetOrdersRequest) Reset() { *m = QueryGetAssetOrdersRequest{} } +func (m *QueryGetAssetOrdersRequest) String() string { return proto.CompactTextString(m) } +func (*QueryGetAssetOrdersRequest) ProtoMessage() {} +func (*QueryGetAssetOrdersRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{8} +} +func (m *QueryGetAssetOrdersRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetAssetOrdersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetAssetOrdersRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetAssetOrdersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetAssetOrdersRequest.Merge(m, src) +} +func (m *QueryGetAssetOrdersRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryGetAssetOrdersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetAssetOrdersRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetAssetOrdersRequest proto.InternalMessageInfo + +func (m *QueryGetAssetOrdersRequest) GetAsset() string { + if m != nil { + return m.Asset + } + return "" +} + +func (m *QueryGetAssetOrdersRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryGetAssetOrdersResponse is a response message for the QueryGetAssetOrders endpoint. +type QueryGetAssetOrdersResponse struct { + // orders are a page of the orders for the provided asset. + Orders []*Order `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` + // pagination is the resulting pagination parameters. + Pagination *query.PageResponse `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryGetAssetOrdersResponse) Reset() { *m = QueryGetAssetOrdersResponse{} } +func (m *QueryGetAssetOrdersResponse) String() string { return proto.CompactTextString(m) } +func (*QueryGetAssetOrdersResponse) ProtoMessage() {} +func (*QueryGetAssetOrdersResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{9} +} +func (m *QueryGetAssetOrdersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryGetAddressOrdersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryGetAssetOrdersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryGetAddressOrdersResponse.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryGetAssetOrdersResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -396,27 +559,43 @@ func (m *QueryGetAddressOrdersResponse) XXX_Marshal(b []byte, deterministic bool return b[:n], nil } } -func (m *QueryGetAddressOrdersResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryGetAddressOrdersResponse.Merge(m, src) +func (m *QueryGetAssetOrdersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetAssetOrdersResponse.Merge(m, src) } -func (m *QueryGetAddressOrdersResponse) XXX_Size() int { +func (m *QueryGetAssetOrdersResponse) XXX_Size() int { return m.Size() } -func (m *QueryGetAddressOrdersResponse) XXX_DiscardUnknown() { - xxx_messageInfo_QueryGetAddressOrdersResponse.DiscardUnknown(m) +func (m *QueryGetAssetOrdersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetAssetOrdersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetAssetOrdersResponse proto.InternalMessageInfo + +func (m *QueryGetAssetOrdersResponse) GetOrders() []*Order { + if m != nil { + return m.Orders + } + return nil } -var xxx_messageInfo_QueryGetAddressOrdersResponse proto.InternalMessageInfo +func (m *QueryGetAssetOrdersResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} // QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint. type QueryGetAllOrdersRequest struct { + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (m *QueryGetAllOrdersRequest) Reset() { *m = QueryGetAllOrdersRequest{} } func (m *QueryGetAllOrdersRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetAllOrdersRequest) ProtoMessage() {} func (*QueryGetAllOrdersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{8} + return fileDescriptor_00949b75b1c10bfe, []int{10} } func (m *QueryGetAllOrdersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -445,15 +624,26 @@ func (m *QueryGetAllOrdersRequest) XXX_DiscardUnknown() { var xxx_messageInfo_QueryGetAllOrdersRequest proto.InternalMessageInfo +func (m *QueryGetAllOrdersRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + // QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoint. type QueryGetAllOrdersResponse struct { + // orders are a page of the all orders. + Orders []*Order `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` + // pagination is the resulting pagination parameters. + Pagination *query.PageResponse `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (m *QueryGetAllOrdersResponse) Reset() { *m = QueryGetAllOrdersResponse{} } func (m *QueryGetAllOrdersResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetAllOrdersResponse) ProtoMessage() {} func (*QueryGetAllOrdersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{9} + return fileDescriptor_00949b75b1c10bfe, []int{11} } func (m *QueryGetAllOrdersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -482,6 +672,20 @@ func (m *QueryGetAllOrdersResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryGetAllOrdersResponse proto.InternalMessageInfo +func (m *QueryGetAllOrdersResponse) GetOrders() []*Order { + if m != nil { + return m.Orders + } + return nil +} + +func (m *QueryGetAllOrdersResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + // QueryMarketInfoRequest is a request message for the QueryMarketInfo endpoint. type QueryMarketInfoRequest struct { MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` @@ -491,7 +695,7 @@ func (m *QueryMarketInfoRequest) Reset() { *m = QueryMarketInfoRequest{} func (m *QueryMarketInfoRequest) String() string { return proto.CompactTextString(m) } func (*QueryMarketInfoRequest) ProtoMessage() {} func (*QueryMarketInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{10} + return fileDescriptor_00949b75b1c10bfe, []int{12} } func (m *QueryMarketInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -535,7 +739,7 @@ func (m *QueryMarketInfoResponse) Reset() { *m = QueryMarketInfoResponse func (m *QueryMarketInfoResponse) String() string { return proto.CompactTextString(m) } func (*QueryMarketInfoResponse) ProtoMessage() {} func (*QueryMarketInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{11} + return fileDescriptor_00949b75b1c10bfe, []int{13} } func (m *QueryMarketInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -572,7 +776,7 @@ func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } func (*QueryParamsRequest) ProtoMessage() {} func (*QueryParamsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{12} + return fileDescriptor_00949b75b1c10bfe, []int{14} } func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -609,7 +813,7 @@ func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } func (*QueryParamsResponse) ProtoMessage() {} func (*QueryParamsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{13} + return fileDescriptor_00949b75b1c10bfe, []int{15} } func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -646,7 +850,7 @@ func (m *QueryValidateCreateMarketRequest) Reset() { *m = QueryValidateC func (m *QueryValidateCreateMarketRequest) String() string { return proto.CompactTextString(m) } func (*QueryValidateCreateMarketRequest) ProtoMessage() {} func (*QueryValidateCreateMarketRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{14} + return fileDescriptor_00949b75b1c10bfe, []int{16} } func (m *QueryValidateCreateMarketRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -683,7 +887,7 @@ func (m *QueryValidateCreateMarketResponse) Reset() { *m = QueryValidate func (m *QueryValidateCreateMarketResponse) String() string { return proto.CompactTextString(m) } func (*QueryValidateCreateMarketResponse) ProtoMessage() {} func (*QueryValidateCreateMarketResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{15} + return fileDescriptor_00949b75b1c10bfe, []int{17} } func (m *QueryValidateCreateMarketResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -721,7 +925,7 @@ func (m *QueryValidateManageFeesRequest) Reset() { *m = QueryValidateMan func (m *QueryValidateManageFeesRequest) String() string { return proto.CompactTextString(m) } func (*QueryValidateManageFeesRequest) ProtoMessage() {} func (*QueryValidateManageFeesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{16} + return fileDescriptor_00949b75b1c10bfe, []int{18} } func (m *QueryValidateManageFeesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -765,7 +969,7 @@ func (m *QueryValidateManageFeesResponse) Reset() { *m = QueryValidateMa func (m *QueryValidateManageFeesResponse) String() string { return proto.CompactTextString(m) } func (*QueryValidateManageFeesResponse) ProtoMessage() {} func (*QueryValidateManageFeesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{17} + return fileDescriptor_00949b75b1c10bfe, []int{19} } func (m *QueryValidateManageFeesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -801,8 +1005,10 @@ func init() { proto.RegisterType((*QueryGetOrderResponse)(nil), "provenance.exchange.v1.QueryGetOrderResponse") proto.RegisterType((*QueryGetMarketOrdersRequest)(nil), "provenance.exchange.v1.QueryGetMarketOrdersRequest") proto.RegisterType((*QueryGetMarketOrdersResponse)(nil), "provenance.exchange.v1.QueryGetMarketOrdersResponse") - proto.RegisterType((*QueryGetAddressOrdersRequest)(nil), "provenance.exchange.v1.QueryGetAddressOrdersRequest") - proto.RegisterType((*QueryGetAddressOrdersResponse)(nil), "provenance.exchange.v1.QueryGetAddressOrdersResponse") + proto.RegisterType((*QueryGetOwnerOrdersRequest)(nil), "provenance.exchange.v1.QueryGetOwnerOrdersRequest") + proto.RegisterType((*QueryGetOwnerOrdersResponse)(nil), "provenance.exchange.v1.QueryGetOwnerOrdersResponse") + proto.RegisterType((*QueryGetAssetOrdersRequest)(nil), "provenance.exchange.v1.QueryGetAssetOrdersRequest") + proto.RegisterType((*QueryGetAssetOrdersResponse)(nil), "provenance.exchange.v1.QueryGetAssetOrdersResponse") proto.RegisterType((*QueryGetAllOrdersRequest)(nil), "provenance.exchange.v1.QueryGetAllOrdersRequest") proto.RegisterType((*QueryGetAllOrdersResponse)(nil), "provenance.exchange.v1.QueryGetAllOrdersResponse") proto.RegisterType((*QueryMarketInfoRequest)(nil), "provenance.exchange.v1.QueryMarketInfoRequest") @@ -820,66 +1026,76 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 930 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0x41, 0x6f, 0xdc, 0x44, - 0x14, 0xce, 0x24, 0x85, 0x26, 0x13, 0x2a, 0xc4, 0xb0, 0x6d, 0x77, 0x9d, 0xe0, 0x6c, 0x5d, 0x84, - 0xa2, 0xd0, 0xd8, 0xd9, 0x0d, 0x2d, 0x14, 0xa9, 0x42, 0x49, 0xa4, 0x54, 0x39, 0x54, 0x6d, 0x8d, - 0xc4, 0x81, 0x03, 0xcb, 0xec, 0xfa, 0xc5, 0xb5, 0xe2, 0xf5, 0x6c, 0x6d, 0x27, 0x2a, 0x8a, 0x72, - 0xe1, 0x07, 0x20, 0x24, 0x2e, 0x9c, 0x10, 0x07, 0xc4, 0x99, 0x03, 0xe2, 0xc4, 0x15, 0x29, 0xc7, - 0x8a, 0x5e, 0x38, 0x21, 0x94, 0xf0, 0x43, 0x90, 0x67, 0x9e, 0xb3, 0xeb, 0xac, 0xed, 0xf5, 0x72, - 0xb3, 0x67, 0xde, 0xf7, 0xbd, 0x6f, 0x9e, 0xdf, 0xfb, 0x3c, 0xd4, 0x18, 0x84, 0xe2, 0x08, 0x02, - 0x1e, 0xf4, 0xc0, 0x82, 0x17, 0xbd, 0x67, 0x3c, 0x70, 0xc1, 0x3a, 0x6a, 0x59, 0xcf, 0x0f, 0x21, - 0xfc, 0xca, 0x1c, 0x84, 0x22, 0x16, 0xec, 0xc6, 0x30, 0xc6, 0x4c, 0x63, 0xcc, 0xa3, 0x96, 0xd6, - 0xe8, 0x89, 0xa8, 0x2f, 0xa2, 0x8e, 0x8c, 0xb2, 0xd4, 0x8b, 0x82, 0x68, 0xba, 0x7a, 0xb3, 0xba, - 0x3c, 0x4a, 0xe8, 0xba, 0x10, 0xf3, 0x96, 0xd5, 0x13, 0x5e, 0x80, 0xfb, 0xcb, 0xae, 0x10, 0xae, - 0x0f, 0x16, 0x1f, 0x78, 0x16, 0x0f, 0x02, 0x11, 0xf3, 0xd8, 0x13, 0x41, 0x8a, 0xae, 0xb9, 0xc2, - 0x15, 0x8a, 0x35, 0x79, 0xc2, 0xd5, 0xdb, 0x05, 0x52, 0x45, 0xe8, 0x40, 0x88, 0x50, 0xe3, 0x7b, - 0x42, 0xeb, 0x4f, 0x13, 0xed, 0x8f, 0x93, 0xd5, 0x5d, 0x80, 0x1d, 0xee, 0xf7, 0x6c, 0x78, 0x7e, - 0x08, 0x51, 0xcc, 0x1e, 0xd0, 0x05, 0x1e, 0x1d, 0x74, 0x24, 0xa0, 0x3e, 0xdb, 0x24, 0xab, 0x8b, - 0xed, 0xa6, 0x99, 0x7f, 0x38, 0x73, 0x2b, 0x3a, 0x90, 0x14, 0xf6, 0x3c, 0xc7, 0xa7, 0x04, 0xde, - 0xf5, 0x1c, 0x84, 0xcf, 0x95, 0xc3, 0xb7, 0x3d, 0x07, 0xe1, 0x5d, 0x7c, 0x32, 0x7e, 0x99, 0xa5, - 0x8d, 0x1c, 0x69, 0xd1, 0x40, 0x04, 0x11, 0xb0, 0xa7, 0xb4, 0xd6, 0x0b, 0x41, 0x96, 0xa1, 0xb3, - 0x0f, 0xd0, 0x11, 0x03, 0x59, 0x91, 0x3a, 0x69, 0xce, 0xad, 0x2e, 0xb6, 0x1b, 0x26, 0x96, 0x37, - 0x29, 0xa8, 0x89, 0x05, 0x35, 0x77, 0x84, 0x17, 0x6c, 0x5f, 0x39, 0xfd, 0x7b, 0x65, 0xc6, 0x66, - 0x29, 0x78, 0x17, 0xe0, 0xb1, 0x82, 0xb2, 0x2f, 0xe8, 0x52, 0x04, 0x71, 0xec, 0x43, 0x1f, 0x82, - 0xb8, 0xb3, 0xef, 0xf3, 0x38, 0xc3, 0x3c, 0x5b, 0x8d, 0xb9, 0x3e, 0xe4, 0xd8, 0xf5, 0x79, 0x3c, - 0xc2, 0xff, 0x25, 0x5d, 0x1e, 0xe1, 0x0f, 0x93, 0xf4, 0x99, 0x04, 0x73, 0xd5, 0x12, 0x34, 0x86, - 0x24, 0x76, 0xc2, 0x31, 0xcc, 0x60, 0xb4, 0x68, 0x4d, 0x56, 0xec, 0x21, 0xc4, 0xaa, 0x9a, 0xf8, - 0x21, 0x1b, 0x74, 0x5e, 0x7e, 0x85, 0x8e, 0xe7, 0xd4, 0x49, 0x93, 0xac, 0x5e, 0xb1, 0xaf, 0xca, - 0xf7, 0x3d, 0xc7, 0xb8, 0x49, 0xaf, 0x5f, 0x82, 0xa8, 0x02, 0x1b, 0x1f, 0xd3, 0xa5, 0x74, 0xe3, - 0x11, 0x0f, 0x0f, 0x70, 0x3b, 0x4a, 0x29, 0x97, 0xe8, 0x42, 0x5f, 0x2e, 0xa7, 0x9c, 0xd7, 0xec, - 0x79, 0xb5, 0xb0, 0xe7, 0x18, 0x3a, 0x5d, 0xce, 0xc7, 0x22, 0xb7, 0x3d, 0xdc, 0xdf, 0x72, 0x9c, - 0x10, 0xa2, 0x28, 0x4b, 0xde, 0xa6, 0x57, 0xb9, 0x5a, 0x97, 0xd4, 0x0b, 0xdb, 0xf5, 0x3f, 0x7f, - 0x5d, 0xaf, 0x61, 0x5d, 0x10, 0xf1, 0x69, 0x1c, 0x7a, 0x81, 0x6b, 0xa7, 0x81, 0xc6, 0x0a, 0x7d, - 0xa7, 0x80, 0x13, 0x93, 0x6a, 0xd8, 0xe9, 0x49, 0x80, 0xef, 0x67, 0x12, 0x1a, 0x4b, 0xd8, 0x6a, - 0xd9, 0x3d, 0x04, 0xde, 0xa5, 0x37, 0xe4, 0xa6, 0x3a, 0xca, 0x5e, 0xb0, 0x2f, 0x2a, 0x15, 0xa1, - 0x41, 0x6f, 0x8e, 0xc1, 0x90, 0xb1, 0x46, 0x99, 0xdc, 0x7a, 0xc2, 0x43, 0xde, 0xbf, 0x10, 0x71, - 0x9d, 0xbe, 0x9d, 0x59, 0xc5, 0x60, 0x83, 0x36, 0xe5, 0xf2, 0x67, 0xdc, 0xf7, 0x1c, 0x1e, 0xc3, - 0x4e, 0xd2, 0xb9, 0xa0, 0x58, 0x53, 0xe8, 0x6d, 0x7a, 0xab, 0x24, 0x06, 0x89, 0x1e, 0x50, 0x3d, - 0x13, 0xf4, 0x88, 0x07, 0xdc, 0x85, 0x5d, 0x80, 0x6a, 0x1f, 0xf5, 0x16, 0x5d, 0x29, 0x84, 0xab, - 0x0c, 0xed, 0x3f, 0xde, 0xa0, 0xaf, 0xc9, 0x18, 0xf6, 0x13, 0xa1, 0x6f, 0x8d, 0x0d, 0x2f, 0xdb, - 0x28, 0x1a, 0xff, 0x22, 0x0b, 0xd2, 0x5a, 0x53, 0x20, 0xf0, 0x98, 0x6b, 0x5f, 0xbf, 0xfa, 0xf7, - 0xbb, 0xd9, 0x77, 0x99, 0x61, 0x15, 0x18, 0xe0, 0x3e, 0x40, 0xa4, 0x5c, 0x90, 0xfd, 0x40, 0xe8, - 0xb5, 0x4c, 0xfb, 0xb3, 0x3b, 0xa5, 0x09, 0x2f, 0x0d, 0x96, 0xb6, 0x5e, 0x31, 0x1a, 0xa5, 0x6d, - 0x48, 0x69, 0x6b, 0x6c, 0xd5, 0x2a, 0xf3, 0x66, 0xeb, 0x38, 0x1d, 0xd6, 0x13, 0xf6, 0x3b, 0x19, - 0x8e, 0xf4, 0xe8, 0x28, 0xb1, 0xcd, 0x49, 0x99, 0x73, 0x86, 0x56, 0xfb, 0x60, 0x3a, 0x10, 0xaa, - 0xbe, 0x2f, 0x55, 0x6f, 0xb2, 0x56, 0x91, 0x6a, 0xd5, 0x22, 0xd6, 0xf1, 0x45, 0xef, 0x9c, 0xe0, - 0x4f, 0x86, 0xfd, 0x46, 0x86, 0xf6, 0x92, 0x99, 0x4a, 0x36, 0x51, 0x4a, 0x9e, 0x31, 0x68, 0x77, - 0xa7, 0x44, 0x4d, 0x55, 0xf7, 0xc8, 0x3a, 0x46, 0x33, 0x39, 0x61, 0x3f, 0xa6, 0xfd, 0x3b, 0xea, - 0x08, 0x13, 0xfa, 0x37, 0xc7, 0x58, 0x26, 0xf4, 0x6f, 0xae, 0xdd, 0xbc, 0x27, 0xc5, 0x36, 0x99, - 0x5e, 0x2e, 0x96, 0xfd, 0x4c, 0xe8, 0x9b, 0x97, 0x0c, 0x86, 0x99, 0xa5, 0xe9, 0xc6, 0x0c, 0x4c, - 0xb3, 0x2a, 0xc7, 0xa3, 0xb8, 0xb6, 0x14, 0x77, 0x87, 0xad, 0x55, 0xef, 0x05, 0xf6, 0x0d, 0xa1, - 0x8b, 0x23, 0xc6, 0xc6, 0xd6, 0x4a, 0x93, 0x66, 0x3c, 0x51, 0x7b, 0xbf, 0x52, 0x6c, 0xd5, 0xca, - 0x0d, 0x94, 0x80, 0x53, 0x82, 0x76, 0x9f, 0x67, 0x97, 0xec, 0xa3, 0xd2, 0x94, 0x25, 0x2e, 0xac, - 0xdd, 0xff, 0x1f, 0x48, 0x94, 0x7e, 0x4f, 0x4a, 0xdf, 0x60, 0x66, 0x91, 0xf4, 0x23, 0x44, 0x5b, - 0xf2, 0xe2, 0x02, 0x1d, 0x55, 0x5f, 0xf6, 0x8a, 0xe0, 0x5f, 0x66, 0xdc, 0x95, 0xd9, 0xbd, 0x4a, - 0x72, 0xc6, 0xfe, 0x02, 0xda, 0x87, 0x53, 0xe3, 0xf0, 0x10, 0x0f, 0xe5, 0x21, 0xb6, 0xd8, 0x27, - 0x53, 0x18, 0xc5, 0xc5, 0xb9, 0xfa, 0x92, 0x2f, 0xb9, 0x10, 0x45, 0xdb, 0x70, 0x7a, 0xa6, 0x93, - 0x97, 0x67, 0x3a, 0xf9, 0xe7, 0x4c, 0x27, 0xdf, 0x9e, 0xeb, 0x33, 0x2f, 0xcf, 0xf5, 0x99, 0xbf, - 0xce, 0xf5, 0x19, 0xda, 0xf0, 0x44, 0x81, 0xba, 0x27, 0xe4, 0x73, 0xd3, 0xf5, 0xe2, 0x67, 0x87, - 0x5d, 0xb3, 0x27, 0xfa, 0x23, 0x0a, 0xd6, 0x3d, 0x31, 0xaa, 0xe7, 0xc5, 0x85, 0xa2, 0xee, 0xeb, - 0xf2, 0x0e, 0xbc, 0xf9, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd0, 0x18, 0x6f, 0x33, 0xd5, 0x0b, - 0x00, 0x00, + // 1093 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0x41, 0x6f, 0x1b, 0x45, + 0x14, 0xce, 0x24, 0x4d, 0x49, 0x26, 0x8a, 0x10, 0x83, 0x5b, 0xec, 0x4d, 0x70, 0xdd, 0x2d, 0x2a, + 0x91, 0x49, 0x76, 0x62, 0xbb, 0x2d, 0xf4, 0x50, 0xa1, 0x24, 0x92, 0xa3, 0x48, 0x54, 0x4d, 0x8d, + 0xc4, 0x81, 0x03, 0x66, 0x6c, 0xbf, 0x6c, 0x57, 0xb1, 0x77, 0xdc, 0xdd, 0x8d, 0x29, 0x44, 0xb9, + 0xf4, 0x07, 0x20, 0x24, 0x38, 0x20, 0x21, 0x2a, 0x0e, 0x88, 0x33, 0x87, 0x1e, 0xf9, 0x01, 0x39, + 0x56, 0xf4, 0xc2, 0x09, 0xa1, 0x84, 0x5f, 0xc1, 0x09, 0xed, 0xcc, 0x6c, 0xbc, 0x1b, 0xef, 0xae, + 0xd7, 0xa8, 0x48, 0x39, 0x79, 0x77, 0xfc, 0xbe, 0xf7, 0xbe, 0xf9, 0x66, 0xe6, 0x7d, 0xb3, 0x58, + 0xef, 0x3b, 0x7c, 0x00, 0x36, 0xb3, 0xdb, 0x40, 0xe1, 0x49, 0xfb, 0x11, 0xb3, 0x4d, 0xa0, 0x83, + 0x0a, 0x7d, 0x7c, 0x00, 0xce, 0x97, 0x46, 0xdf, 0xe1, 0x1e, 0x27, 0x57, 0x87, 0x31, 0x46, 0x10, + 0x63, 0x0c, 0x2a, 0x5a, 0xa1, 0xcd, 0xdd, 0x1e, 0x77, 0x9b, 0x22, 0x8a, 0xca, 0x17, 0x09, 0xd1, + 0xca, 0xf2, 0x8d, 0xb6, 0x98, 0x0b, 0x32, 0x17, 0x1d, 0x54, 0x5a, 0xe0, 0xb1, 0x0a, 0xed, 0x33, + 0xd3, 0xb2, 0x99, 0x67, 0x71, 0x5b, 0xc5, 0x16, 0xc3, 0xb1, 0x41, 0x54, 0x9b, 0x5b, 0xc1, 0xff, + 0xcb, 0x26, 0xe7, 0x66, 0x17, 0x28, 0xeb, 0x5b, 0x94, 0xd9, 0x36, 0xf7, 0x04, 0x38, 0xa8, 0x94, + 0x33, 0xb9, 0xc9, 0x25, 0x03, 0xff, 0x49, 0x8d, 0xde, 0x48, 0x98, 0x16, 0x77, 0x3a, 0xe0, 0x28, + 0xa8, 0xfe, 0x3d, 0xc2, 0xf9, 0x87, 0x3e, 0xb7, 0x07, 0xfe, 0x68, 0x1d, 0x60, 0x8b, 0x75, 0xdb, + 0x0d, 0x78, 0x7c, 0x00, 0xae, 0x47, 0xee, 0xe1, 0x79, 0xe6, 0xee, 0x37, 0x05, 0x20, 0x3f, 0x5d, + 0x42, 0x2b, 0x0b, 0xd5, 0x92, 0x11, 0x2f, 0x84, 0xb1, 0xe1, 0xee, 0x8b, 0x14, 0x8d, 0x39, 0xa6, + 0x9e, 0x7c, 0x78, 0xcb, 0xea, 0x28, 0xf8, 0x4c, 0x3a, 0x7c, 0xd3, 0xea, 0x28, 0x78, 0x4b, 0x3d, + 0xe9, 0xbf, 0x4e, 0xe3, 0x42, 0x0c, 0x35, 0xb7, 0xcf, 0x6d, 0x17, 0xc8, 0x43, 0x9c, 0x6b, 0x3b, + 0x20, 0x64, 0x68, 0xee, 0x01, 0x34, 0x79, 0x5f, 0x28, 0x92, 0x47, 0xa5, 0x99, 0x95, 0x85, 0x6a, + 0xc1, 0x50, 0x4b, 0xe1, 0x0b, 0x6a, 0x28, 0x41, 0x8d, 0x2d, 0x6e, 0xd9, 0x9b, 0x97, 0x8e, 0xff, + 0xbc, 0x36, 0xd5, 0x20, 0x01, 0xb8, 0x0e, 0xf0, 0x40, 0x42, 0xc9, 0x67, 0x78, 0xc9, 0x05, 0xcf, + 0xeb, 0x42, 0x0f, 0x6c, 0xaf, 0xb9, 0xd7, 0x65, 0x5e, 0x24, 0xf3, 0x74, 0xb6, 0xcc, 0xf9, 0x61, + 0x8e, 0x7a, 0x97, 0x79, 0xa1, 0xfc, 0x9f, 0xe3, 0xe5, 0x50, 0x7e, 0xc7, 0x2f, 0x1f, 0x29, 0x30, + 0x93, 0xad, 0x40, 0x61, 0x98, 0xa4, 0xe1, 0xe7, 0x18, 0x56, 0xd0, 0x2b, 0x38, 0x27, 0x14, 0xdb, + 0x06, 0x4f, 0xaa, 0xa9, 0x16, 0xb2, 0x80, 0xe7, 0xc4, 0x2a, 0x34, 0xad, 0x4e, 0x1e, 0x95, 0xd0, + 0xca, 0xa5, 0xc6, 0x6b, 0xe2, 0x7d, 0xa7, 0xa3, 0x7f, 0x84, 0xaf, 0x9c, 0x83, 0x28, 0x81, 0x6b, + 0x78, 0x56, 0xae, 0x1c, 0x12, 0x2b, 0xf7, 0x76, 0xd2, 0xca, 0x49, 0x94, 0x8c, 0xd5, 0x9f, 0x22, + 0xbc, 0x14, 0xa4, 0xbb, 0xcf, 0x9c, 0x7d, 0x95, 0xd4, 0x0d, 0x88, 0x2c, 0xe1, 0xf9, 0x9e, 0x18, + 0x0e, 0x98, 0x2c, 0x36, 0xe6, 0xe4, 0xc0, 0x4e, 0x87, 0xd4, 0x31, 0x1e, 0x1e, 0x8c, 0x7c, 0x5b, + 0x94, 0xbd, 0x19, 0x51, 0x43, 0x9e, 0xc8, 0x40, 0x93, 0x5d, 0x66, 0x82, 0x4a, 0xdc, 0x08, 0x21, + 0xf5, 0x67, 0x08, 0x2f, 0xc7, 0x93, 0x50, 0x53, 0xbb, 0x8d, 0x2f, 0xcb, 0x43, 0xa0, 0x76, 0xcb, + 0x98, 0xb9, 0xa9, 0x60, 0xb2, 0x1d, 0xc3, 0xef, 0xdd, 0xb1, 0xfc, 0x64, 0xcd, 0x08, 0xc1, 0xef, + 0x10, 0xd6, 0xce, 0x44, 0xff, 0xc2, 0x06, 0x27, 0x2a, 0x92, 0x81, 0x67, 0xb9, 0x3f, 0x2a, 0x04, + 0x9a, 0xdf, 0xcc, 0xff, 0xfe, 0x7c, 0x2d, 0xa7, 0xaa, 0x6c, 0x74, 0x3a, 0x0e, 0xb8, 0xee, 0xc7, + 0x9e, 0x63, 0xd9, 0x66, 0x43, 0x86, 0xbd, 0x32, 0xdd, 0x7e, 0x0c, 0x2d, 0x5e, 0x84, 0xd6, 0x05, + 0x91, 0xed, 0xab, 0xa1, 0x6a, 0x1b, 0xae, 0x7b, 0x7e, 0x6b, 0xe5, 0xf0, 0x2c, 0xf3, 0x47, 0xa5, + 0x6a, 0x0d, 0xf9, 0xf2, 0xbf, 0x68, 0x13, 0x29, 0x7e, 0x41, 0xb4, 0x69, 0xa9, 0x36, 0xee, 0xd3, + 0xeb, 0x76, 0xa3, 0xca, 0xbc, 0x2a, 0x0d, 0x7e, 0x40, 0xaa, 0x21, 0x47, 0x8b, 0x5c, 0x10, 0x05, + 0x6e, 0xe3, 0xab, 0x82, 0x9c, 0x3c, 0xf1, 0x3b, 0xf6, 0x1e, 0xcf, 0xd2, 0x74, 0xf4, 0x02, 0x7e, + 0x6b, 0x04, 0x26, 0xb3, 0xeb, 0x39, 0x4c, 0xc4, 0x5f, 0xbb, 0xcc, 0x61, 0xbd, 0x40, 0x4d, 0xfd, + 0x0a, 0x7e, 0x33, 0x32, 0xaa, 0x82, 0x75, 0x5c, 0x12, 0xc3, 0x9f, 0xb0, 0xae, 0xd5, 0x61, 0x1e, + 0x6c, 0xf9, 0xfe, 0x02, 0x32, 0x6b, 0x00, 0xbd, 0x81, 0xaf, 0xa7, 0xc4, 0xa8, 0x44, 0xf7, 0x70, + 0x31, 0x12, 0x74, 0x9f, 0xd9, 0xcc, 0x84, 0x3a, 0x40, 0xa6, 0x26, 0xaa, 0x5f, 0xc7, 0xd7, 0x12, + 0xe1, 0xb2, 0x42, 0xf5, 0x9f, 0x45, 0x3c, 0x2b, 0x62, 0xc8, 0xcf, 0x08, 0xbf, 0x31, 0x62, 0xb1, + 0x64, 0x3d, 0x69, 0xe5, 0x92, 0x2e, 0x0a, 0x5a, 0x65, 0x02, 0x84, 0x9a, 0x66, 0xf9, 0xe9, 0xcb, + 0xbf, 0xbf, 0x9d, 0x7e, 0x87, 0xe8, 0x34, 0xe1, 0x9a, 0xb2, 0x07, 0xe0, 0xca, 0xbb, 0x0a, 0x79, + 0x86, 0xf0, 0x62, 0xc4, 0xa4, 0xc8, 0x6a, 0x6a, 0xc1, 0x73, 0xf6, 0xa7, 0xad, 0x65, 0x8c, 0x56, + 0xd4, 0xd6, 0x05, 0xb5, 0x32, 0x59, 0xa1, 0x69, 0x37, 0x28, 0x7a, 0x18, 0x58, 0xea, 0x11, 0xf9, + 0x0d, 0x0d, 0x8d, 0x37, 0xec, 0x38, 0xa4, 0x36, 0xae, 0x72, 0x8c, 0x49, 0x6a, 0xb7, 0x26, 0x03, + 0x29, 0xd6, 0x77, 0x05, 0xeb, 0x1a, 0xa9, 0x24, 0xb1, 0x96, 0x5b, 0x84, 0x1e, 0x9e, 0xed, 0x9d, + 0x23, 0x75, 0x15, 0x24, 0xcf, 0x91, 0xda, 0xd3, 0xd1, 0xc6, 0x4f, 0xaa, 0x63, 0x75, 0x1b, 0x31, + 0x2f, 0xad, 0x36, 0x11, 0x46, 0x71, 0xbf, 0x25, 0xb8, 0x1b, 0x64, 0x35, 0x55, 0x71, 0x97, 0x0a, + 0xbf, 0xa3, 0x87, 0xe2, 0xe7, 0x28, 0x42, 0x3b, 0xd4, 0x93, 0xc7, 0xd3, 0x1e, 0x75, 0x8f, 0xf1, + 0xb4, 0x63, 0x9a, 0x7e, 0x66, 0xda, 0xc2, 0x8a, 0xe8, 0xa1, 0xf8, 0x39, 0x22, 0x3f, 0x05, 0x87, + 0x2e, 0xdc, 0x46, 0xc7, 0x1c, 0xba, 0x98, 0xb6, 0x3e, 0xe6, 0xd0, 0xc5, 0xf5, 0x68, 0xfd, 0xa6, + 0x20, 0x5c, 0x22, 0xc5, 0x74, 0xc2, 0xe4, 0x17, 0x84, 0x5f, 0x3f, 0xd7, 0x15, 0x89, 0x91, 0x5a, + 0x6e, 0xa4, 0xeb, 0x6a, 0x34, 0x73, 0xbc, 0x22, 0x57, 0x15, 0xe4, 0x56, 0x49, 0x39, 0xfb, 0x06, + 0x26, 0x5f, 0x23, 0xbc, 0x10, 0xea, 0xc6, 0xa4, 0x9c, 0x5a, 0x34, 0xd2, 0xc8, 0xb5, 0xf7, 0x32, + 0xc5, 0x66, 0x55, 0xae, 0x2f, 0x09, 0x1c, 0x07, 0x1e, 0x19, 0xd7, 0xe3, 0xc9, 0x07, 0xa9, 0x25, + 0x53, 0xac, 0x43, 0xbb, 0xfb, 0x1f, 0x90, 0x8a, 0xfa, 0x1d, 0x41, 0x7d, 0x9d, 0x18, 0x49, 0xd4, + 0x07, 0x0a, 0x4d, 0xc5, 0x37, 0x11, 0x34, 0xa5, 0xbe, 0xe4, 0x25, 0x52, 0xd6, 0x38, 0x6a, 0x25, + 0xe4, 0x4e, 0x26, 0x3a, 0x23, 0xd6, 0xa5, 0xbd, 0x3f, 0x31, 0x4e, 0x4d, 0x62, 0x5b, 0x4c, 0x62, + 0x83, 0x7c, 0x38, 0x41, 0x77, 0x3b, 0x9b, 0x57, 0x4f, 0xe4, 0xf3, 0xbf, 0xb5, 0xdc, 0x4d, 0x38, + 0x3e, 0x29, 0xa2, 0x17, 0x27, 0x45, 0xf4, 0xd7, 0x49, 0x11, 0x7d, 0x73, 0x5a, 0x9c, 0x7a, 0x71, + 0x5a, 0x9c, 0xfa, 0xe3, 0xb4, 0x38, 0x85, 0x0b, 0x16, 0x4f, 0x60, 0xb7, 0x8b, 0x3e, 0x35, 0x4c, + 0xcb, 0x7b, 0x74, 0xd0, 0x32, 0xda, 0xbc, 0x17, 0x62, 0xb0, 0x66, 0xf1, 0x30, 0x9f, 0x27, 0x67, + 0x8c, 0x5a, 0x97, 0xc5, 0xe7, 0x75, 0xed, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb6, 0x59, 0x55, + 0xc2, 0x5c, 0x10, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -900,8 +1116,10 @@ type QueryClient interface { QueryGetOrder(ctx context.Context, in *QueryGetOrderRequest, opts ...grpc.CallOption) (*QueryGetOrderResponse, error) // QueryGetMarketOrders looks up the orders in a market. QueryGetMarketOrders(ctx context.Context, in *QueryGetMarketOrdersRequest, opts ...grpc.CallOption) (*QueryGetMarketOrdersResponse, error) - // QueryGetAddressOrders looks up the orders from the provided address. - QueryGetAddressOrders(ctx context.Context, in *QueryGetAddressOrdersRequest, opts ...grpc.CallOption) (*QueryGetAddressOrdersResponse, error) + // QueryGetOwnerOrders looks up the orders from the provided owner address. + QueryGetOwnerOrders(ctx context.Context, in *QueryGetOwnerOrdersRequest, opts ...grpc.CallOption) (*QueryGetOwnerOrdersResponse, error) + // QueryGetAssetOrders looks up the orders for a specific asset denom. + QueryGetAssetOrders(ctx context.Context, in *QueryGetAssetOrdersRequest, opts ...grpc.CallOption) (*QueryGetAssetOrdersResponse, error) // QueryGetAllOrders gets all orders in the exchange module. QueryGetAllOrders(ctx context.Context, in *QueryGetAllOrdersRequest, opts ...grpc.CallOption) (*QueryGetAllOrdersResponse, error) // QueryMarketInfo returns the information/details about a market. @@ -949,9 +1167,18 @@ func (c *queryClient) QueryGetMarketOrders(ctx context.Context, in *QueryGetMark return out, nil } -func (c *queryClient) QueryGetAddressOrders(ctx context.Context, in *QueryGetAddressOrdersRequest, opts ...grpc.CallOption) (*QueryGetAddressOrdersResponse, error) { - out := new(QueryGetAddressOrdersResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetAddressOrders", in, out, opts...) +func (c *queryClient) QueryGetOwnerOrders(ctx context.Context, in *QueryGetOwnerOrdersRequest, opts ...grpc.CallOption) (*QueryGetOwnerOrdersResponse, error) { + out := new(QueryGetOwnerOrdersResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetOwnerOrders", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) QueryGetAssetOrders(ctx context.Context, in *QueryGetAssetOrdersRequest, opts ...grpc.CallOption) (*QueryGetAssetOrdersResponse, error) { + out := new(QueryGetAssetOrdersResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetAssetOrders", in, out, opts...) if err != nil { return nil, err } @@ -1011,8 +1238,10 @@ type QueryServer interface { QueryGetOrder(context.Context, *QueryGetOrderRequest) (*QueryGetOrderResponse, error) // QueryGetMarketOrders looks up the orders in a market. QueryGetMarketOrders(context.Context, *QueryGetMarketOrdersRequest) (*QueryGetMarketOrdersResponse, error) - // QueryGetAddressOrders looks up the orders from the provided address. - QueryGetAddressOrders(context.Context, *QueryGetAddressOrdersRequest) (*QueryGetAddressOrdersResponse, error) + // QueryGetOwnerOrders looks up the orders from the provided owner address. + QueryGetOwnerOrders(context.Context, *QueryGetOwnerOrdersRequest) (*QueryGetOwnerOrdersResponse, error) + // QueryGetAssetOrders looks up the orders for a specific asset denom. + QueryGetAssetOrders(context.Context, *QueryGetAssetOrdersRequest) (*QueryGetAssetOrdersResponse, error) // QueryGetAllOrders gets all orders in the exchange module. QueryGetAllOrders(context.Context, *QueryGetAllOrdersRequest) (*QueryGetAllOrdersResponse, error) // QueryMarketInfo returns the information/details about a market. @@ -1038,8 +1267,11 @@ func (*UnimplementedQueryServer) QueryGetOrder(ctx context.Context, req *QueryGe func (*UnimplementedQueryServer) QueryGetMarketOrders(ctx context.Context, req *QueryGetMarketOrdersRequest) (*QueryGetMarketOrdersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryGetMarketOrders not implemented") } -func (*UnimplementedQueryServer) QueryGetAddressOrders(ctx context.Context, req *QueryGetAddressOrdersRequest) (*QueryGetAddressOrdersResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryGetAddressOrders not implemented") +func (*UnimplementedQueryServer) QueryGetOwnerOrders(ctx context.Context, req *QueryGetOwnerOrdersRequest) (*QueryGetOwnerOrdersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryGetOwnerOrders not implemented") +} +func (*UnimplementedQueryServer) QueryGetAssetOrders(ctx context.Context, req *QueryGetAssetOrdersRequest) (*QueryGetAssetOrdersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryGetAssetOrders not implemented") } func (*UnimplementedQueryServer) QueryGetAllOrders(ctx context.Context, req *QueryGetAllOrdersRequest) (*QueryGetAllOrdersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryGetAllOrders not implemented") @@ -1115,20 +1347,38 @@ func _Query_QueryGetMarketOrders_Handler(srv interface{}, ctx context.Context, d return interceptor(ctx, in, info, handler) } -func _Query_QueryGetAddressOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QueryGetAddressOrdersRequest) +func _Query_QueryGetOwnerOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryGetOwnerOrdersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryGetOwnerOrders(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryGetOwnerOrders", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryGetOwnerOrders(ctx, req.(*QueryGetOwnerOrdersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_QueryGetAssetOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryGetAssetOrdersRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryGetAddressOrders(ctx, in) + return srv.(QueryServer).QueryGetAssetOrders(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryGetAddressOrders", + FullMethod: "/provenance.exchange.v1.Query/QueryGetAssetOrders", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryGetAddressOrders(ctx, req.(*QueryGetAddressOrdersRequest)) + return srv.(QueryServer).QueryGetAssetOrders(ctx, req.(*QueryGetAssetOrdersRequest)) } return interceptor(ctx, in, info, handler) } @@ -1240,8 +1490,12 @@ var _Query_serviceDesc = grpc.ServiceDesc{ Handler: _Query_QueryGetMarketOrders_Handler, }, { - MethodName: "QueryGetAddressOrders", - Handler: _Query_QueryGetAddressOrders_Handler, + MethodName: "QueryGetOwnerOrders", + Handler: _Query_QueryGetOwnerOrders_Handler, + }, + { + MethodName: "QueryGetAssetOrders", + Handler: _Query_QueryGetAssetOrders_Handler, }, { MethodName: "QueryGetAllOrders", @@ -1428,6 +1682,18 @@ func (m *QueryGetOrderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Order != nil { + { + size, err := m.Order.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -1451,6 +1717,20 @@ func (m *QueryGetMarketOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, er _ = i var l int _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0x9a + } if m.MarketId != 0 { i = encodeVarintQuery(dAtA, i, uint64(m.MarketId)) i-- @@ -1479,10 +1759,38 @@ func (m *QueryGetMarketOrdersResponse) MarshalToSizedBuffer(dAtA []byte) (int, e _ = i var l int _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0x9a + } + if len(m.Orders) > 0 { + for iNdEx := len(m.Orders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Orders[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } return len(dAtA) - i, nil } -func (m *QueryGetAddressOrdersRequest) Marshal() (dAtA []byte, err error) { +func (m *QueryGetOwnerOrdersRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1492,27 +1800,41 @@ func (m *QueryGetAddressOrdersRequest) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryGetAddressOrdersRequest) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryGetOwnerOrdersRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryGetAddressOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryGetOwnerOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if len(m.Address) > 0 { - i -= len(m.Address) - copy(dAtA[i:], m.Address) - i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0x9a + } + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Owner))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } -func (m *QueryGetAddressOrdersResponse) Marshal() (dAtA []byte, err error) { +func (m *QueryGetOwnerOrdersResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1522,20 +1844,48 @@ func (m *QueryGetAddressOrdersResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryGetAddressOrdersResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryGetOwnerOrdersResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryGetAddressOrdersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryGetOwnerOrdersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0x9a + } + if len(m.Orders) > 0 { + for iNdEx := len(m.Orders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Orders[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } return len(dAtA) - i, nil } -func (m *QueryGetAllOrdersRequest) Marshal() (dAtA []byte, err error) { +func (m *QueryGetAssetOrdersRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1545,20 +1895,41 @@ func (m *QueryGetAllOrdersRequest) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryGetAllOrdersRequest) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryGetAssetOrdersRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryGetAllOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryGetAssetOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0x9a + } + if len(m.Asset) > 0 { + i -= len(m.Asset) + copy(dAtA[i:], m.Asset) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Asset))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } -func (m *QueryGetAllOrdersResponse) Marshal() (dAtA []byte, err error) { +func (m *QueryGetAssetOrdersResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1568,27 +1939,143 @@ func (m *QueryGetAllOrdersResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryGetAllOrdersResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryGetAssetOrdersResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryGetAllOrdersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryGetAssetOrdersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - return len(dAtA) - i, nil -} - -func (m *QueryMarketInfoRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0x9a + } + if len(m.Orders) > 0 { + for iNdEx := len(m.Orders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Orders[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryGetAllOrdersRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetAllOrdersRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetAllOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0x9a + } + return len(dAtA) - i, nil +} + +func (m *QueryGetAllOrdersResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetAllOrdersResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetAllOrdersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0x9a + } + if len(m.Orders) > 0 { + for iNdEx := len(m.Orders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Orders[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryMarketInfoRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil } func (m *QueryMarketInfoRequest) MarshalTo(dAtA []byte) (int, error) { @@ -1848,6 +2335,10 @@ func (m *QueryGetOrderResponse) Size() (n int) { } var l int _ = l + if m.Order != nil { + l = m.Order.Size() + n += 1 + l + sovQuery(uint64(l)) + } return n } @@ -1860,6 +2351,10 @@ func (m *QueryGetMarketOrdersRequest) Size() (n int) { if m.MarketId != 0 { n += 1 + sovQuery(uint64(m.MarketId)) } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 2 + l + sovQuery(uint64(l)) + } return n } @@ -1869,28 +2364,88 @@ func (m *QueryGetMarketOrdersResponse) Size() (n int) { } var l int _ = l + if len(m.Orders) > 0 { + for _, e := range m.Orders { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 2 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryGetOwnerOrdersRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 2 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryGetOwnerOrdersResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Orders) > 0 { + for _, e := range m.Orders { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 2 + l + sovQuery(uint64(l)) + } return n } -func (m *QueryGetAddressOrdersRequest) Size() (n int) { +func (m *QueryGetAssetOrdersRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = len(m.Address) + l = len(m.Asset) if l > 0 { n += 1 + l + sovQuery(uint64(l)) } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 2 + l + sovQuery(uint64(l)) + } return n } -func (m *QueryGetAddressOrdersResponse) Size() (n int) { +func (m *QueryGetAssetOrdersResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l + if len(m.Orders) > 0 { + for _, e := range m.Orders { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 2 + l + sovQuery(uint64(l)) + } return n } @@ -1900,6 +2455,10 @@ func (m *QueryGetAllOrdersRequest) Size() (n int) { } var l int _ = l + if m.Pagination != nil { + l = m.Pagination.Size() + n += 2 + l + sovQuery(uint64(l)) + } return n } @@ -1909,6 +2468,16 @@ func (m *QueryGetAllOrdersResponse) Size() (n int) { } var l int _ = l + if len(m.Orders) > 0 { + for _, e := range m.Orders { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 2 + l + sovQuery(uint64(l)) + } return n } @@ -2368,6 +2937,42 @@ func (m *QueryGetOrderResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: QueryGetOrderResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Order", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Order == nil { + m.Order = &Order{} + } + if err := m.Order.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -2437,56 +3042,42 @@ func (m *QueryGetMarketOrdersRequest) Unmarshal(dAtA []byte) error { break } } - default: - iNdEx = preIndex - skippy, err := skipQuery(dAtA[iNdEx:]) - if err != nil { - return err + case 99: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthQuery + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF + if msglen < 0 { + return ErrInvalidLengthQuery } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *QueryGetMarketOrdersResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery } - if iNdEx >= l { + if postIndex > l { return io.ErrUnexpectedEOF } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: QueryGetMarketOrdersResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QueryGetMarketOrdersResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -2508,7 +3099,7 @@ func (m *QueryGetMarketOrdersResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryGetAddressOrdersRequest) Unmarshal(dAtA []byte) error { +func (m *QueryGetMarketOrdersResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2531,15 +3122,373 @@ func (m *QueryGetAddressOrdersRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryGetAddressOrdersRequest: wiretype end group for non-group") + return fmt.Errorf("proto: QueryGetMarketOrdersResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryGetAddressOrdersRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryGetMarketOrdersResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Orders", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Orders = append(m.Orders, &Order{}) + if err := m.Orders[len(m.Orders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 99: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetOwnerOrdersRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetOwnerOrdersRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetOwnerOrdersRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 99: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetOwnerOrdersResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetOwnerOrdersResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetOwnerOrdersResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Orders", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Orders = append(m.Orders, &Order{}) + if err := m.Orders[len(m.Orders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 99: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetAssetOrdersRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetAssetOrdersRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetAssetOrdersRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Asset", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -2567,7 +3516,43 @@ func (m *QueryGetAddressOrdersRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Address = string(dAtA[iNdEx:postIndex]) + m.Asset = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 99: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -2590,7 +3575,7 @@ func (m *QueryGetAddressOrdersRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryGetAddressOrdersResponse) Unmarshal(dAtA []byte) error { +func (m *QueryGetAssetOrdersResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2613,12 +3598,82 @@ func (m *QueryGetAddressOrdersResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryGetAddressOrdersResponse: wiretype end group for non-group") + return fmt.Errorf("proto: QueryGetAssetOrdersResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryGetAddressOrdersResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryGetAssetOrdersResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Orders", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Orders = append(m.Orders, &Order{}) + if err := m.Orders[len(m.Orders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 99: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -2669,6 +3724,42 @@ func (m *QueryGetAllOrdersRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: QueryGetAllOrdersRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 99: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -2719,6 +3810,76 @@ func (m *QueryGetAllOrdersResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: QueryGetAllOrdersResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Orders", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Orders = append(m.Orders, &Order{}) + if err := m.Orders[len(m.Orders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 99: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/x/exchange/query.pb.gw.go b/x/exchange/query.pb.gw.go index 4a13629453..b969e29b6f 100644 --- a/x/exchange/query.pb.gw.go +++ b/x/exchange/query.pb.gw.go @@ -121,6 +121,10 @@ func local_request_Query_QueryGetOrder_0(ctx context.Context, marshaler runtime. } +var ( + filter_Query_QueryGetMarketOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{"market_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + func request_Query_QueryGetMarketOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetMarketOrdersRequest var metadata runtime.ServerMetadata @@ -143,6 +147,13 @@ func request_Query_QueryGetMarketOrders_0(ctx context.Context, marshaler runtime return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetMarketOrders_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.QueryGetMarketOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -170,13 +181,96 @@ func local_request_Query_QueryGetMarketOrders_0(ctx context.Context, marshaler r return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetMarketOrders_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.QueryGetMarketOrders(ctx, &protoReq) return msg, metadata, err } -func request_Query_QueryGetAddressOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryGetAddressOrdersRequest +var ( + filter_Query_QueryGetOwnerOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{"owner": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_QueryGetOwnerOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetOwnerOrdersRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["owner"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "owner") + } + + protoReq.Owner, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "owner", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetOwnerOrders_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.QueryGetOwnerOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryGetOwnerOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetOwnerOrdersRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["owner"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "owner") + } + + protoReq.Owner, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "owner", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetOwnerOrders_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.QueryGetOwnerOrders(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_QueryGetAssetOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{"asset": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_QueryGetAssetOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetAssetOrdersRequest var metadata runtime.ServerMetadata var ( @@ -186,24 +280,31 @@ func request_Query_QueryGetAddressOrders_0(ctx context.Context, marshaler runtim _ = err ) - val, ok = pathParams["address"] + val, ok = pathParams["asset"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "asset") } - protoReq.Address, err = runtime.String(val) + protoReq.Asset, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "asset", err) } - msg, err := client.QueryGetAddressOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetAssetOrders_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.QueryGetAssetOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryGetAddressOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryGetAddressOrdersRequest +func local_request_Query_QueryGetAssetOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetAssetOrdersRequest var metadata runtime.ServerMetadata var ( @@ -213,26 +314,44 @@ func local_request_Query_QueryGetAddressOrders_0(ctx context.Context, marshaler _ = err ) - val, ok = pathParams["address"] + val, ok = pathParams["asset"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "asset") } - protoReq.Address, err = runtime.String(val) + protoReq.Asset, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "asset", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetAssetOrders_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.QueryGetAddressOrders(ctx, &protoReq) + msg, err := server.QueryGetAssetOrders(ctx, &protoReq) return msg, metadata, err } +var ( + filter_Query_QueryGetAllOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + func request_Query_QueryGetAllOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetAllOrdersRequest var metadata runtime.ServerMetadata + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetAllOrders_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.QueryGetAllOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -242,6 +361,13 @@ func local_request_Query_QueryGetAllOrders_0(ctx context.Context, marshaler runt var protoReq QueryGetAllOrdersRequest var metadata runtime.ServerMetadata + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetAllOrders_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.QueryGetAllOrders(ctx, &protoReq) return msg, metadata, err @@ -457,7 +583,27 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) - mux.Handle("GET", pattern_Query_QueryGetAddressOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_QueryGetOwnerOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryGetOwnerOrders_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetOwnerOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetAssetOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -466,14 +612,14 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryGetAddressOrders_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_QueryGetAssetOrders_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetAddressOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryGetAssetOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -678,7 +824,27 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) - mux.Handle("GET", pattern_Query_QueryGetAddressOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_QueryGetOwnerOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryGetOwnerOrders_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetOwnerOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetAssetOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -687,14 +853,14 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryGetAddressOrders_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_QueryGetAssetOrders_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetAddressOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryGetAssetOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -808,7 +974,9 @@ var ( pattern_Query_QueryGetMarketOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"provenance", "exchange", "v1", "market", "market_id", "orders"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetAddressOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "orders", "address"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryGetOwnerOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "orders", "owner"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryGetAssetOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "orders", "asset"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryGetAllOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "orders"}, "", runtime.AssumeColonVerbOpt(false))) @@ -828,7 +996,9 @@ var ( forward_Query_QueryGetMarketOrders_0 = runtime.ForwardResponseMessage - forward_Query_QueryGetAddressOrders_0 = runtime.ForwardResponseMessage + forward_Query_QueryGetOwnerOrders_0 = runtime.ForwardResponseMessage + + forward_Query_QueryGetAssetOrders_0 = runtime.ForwardResponseMessage forward_Query_QueryGetAllOrders_0 = runtime.ForwardResponseMessage From b2ed53bd8e831596f2c5dbe5ae87b8b1276488e5 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 16:18:46 -0600 Subject: [PATCH 208/309] [1658]: Implement QueryGetOrder. --- x/exchange/keeper/grpc_query.go | 16 ++++++++++++++-- x/exchange/keeper/orders.go | 5 ++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index a8b7e1c39f..9406f9079e 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -69,8 +69,20 @@ func (k QueryServer) QueryOrderFeeCalc(goCtx context.Context, req *exchange.Quer // QueryGetOrder looks up an order by id. func (k QueryServer) QueryGetOrder(goCtx context.Context, req *exchange.QueryGetOrderRequest) (*exchange.QueryGetOrderResponse, error) { - // TODO[1658]: Implement QueryGetOrder query - panic("not implemented") + if req == nil || req.OrderId == 0 { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + order, err := k.GetOrder(ctx, req.OrderId) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + if order == nil { + return nil, status.Errorf(codes.InvalidArgument, "order %d not found", req.OrderId) + } + + return &exchange.QueryGetOrderResponse{Order: order}, nil } // QueryGetMarketOrders looks up the orders in a market. diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index fd615b93f2..8da7802638 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -112,6 +112,9 @@ func createIndexEntries(order exchange.Order) []sdk.KVPair { func (k Keeper) getOrderFromStore(store sdk.KVStore, orderID uint64) (*exchange.Order, error) { key := MakeKeyOrder(orderID) value := store.Get(key) + if len(value) == 0 { + return nil, nil + } rv, err := k.parseOrderStoreValue(orderID, value) if err != nil { return nil, fmt.Errorf("failed to read order %d: %w", orderID, err) @@ -489,7 +492,7 @@ func (k Keeper) getBidOrders(store sdk.KVStore, marketID uint32, orderIDs []uint } // getAskOrders gets orders from the store, making sure they're ask orders in the given market -// and do not have the same sller as the provided buyer. If the buyer isn't yet known, just provide "" for it. +// and do not have the same seller as the provided buyer. If the buyer isn't yet known, just provide "" for it. func (k Keeper) getAskOrders(store sdk.KVStore, marketID uint32, orderIDs []uint64, buyer string) ([]*exchange.Order, error) { var errs []error orders := make([]*exchange.Order, 0, len(orderIDs)) From 6ef00a3976588633392fc749e70d144d800eb614 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 16:37:42 -0600 Subject: [PATCH 209/309] [1658]: Implement QueryGetMarketOrders. --- x/exchange/keeper/grpc_query.go | 35 +++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 9406f9079e..02d48c69de 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -7,7 +7,9 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" "github.com/provenance-io/provenance/x/exchange" ) @@ -87,8 +89,37 @@ func (k QueryServer) QueryGetOrder(goCtx context.Context, req *exchange.QueryGet // QueryGetMarketOrders looks up the orders in a market. func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.QueryGetMarketOrdersRequest) (*exchange.QueryGetMarketOrdersResponse, error) { - // TODO[1658]: Implement QueryGetMarketOrders query - panic("not implemented") + if req == nil || req.MarketId == 0 { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + pre := GetIndexKeyPrefixMarketToOrder(req.MarketId) + store := prefix.NewStore(k.getStore(ctx), pre) + resp := &exchange.QueryGetMarketOrdersResponse{} + var pageErr error + resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { + // If we can't get the order id from the key, just pretend like it doesn't exist. + _, orderID, perr := ParseIndexKeyMarketToOrder(key) + if perr != nil { + return false, nil + } + if accumulate { + // Only add them to the result if we can read it. + // This might result in fewer results than the limit, but at least one bad entry won't block others. + order, oerr := k.parseOrderStoreValue(orderID, value) + if oerr != nil { + resp.Orders = append(resp.Orders, order) + } + } + return true, nil + }) + + if pageErr != nil { + return nil, status.Errorf(codes.InvalidArgument, "error iterating orders: %v", pageErr) + } + + return resp, nil } // QueryGetOwnerOrders looks up the orders from the provided owner address. From 7a859a02b11dc7cd2c41c06a4623a7b7ef47e055 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 16:41:26 -0600 Subject: [PATCH 210/309] [1658]: Implement QueryGetOwnerOrders. --- x/exchange/keeper/grpc_query.go | 42 ++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 02d48c69de..77950c8b89 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -98,6 +98,7 @@ func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.Q store := prefix.NewStore(k.getStore(ctx), pre) resp := &exchange.QueryGetMarketOrdersResponse{} var pageErr error + resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { // If we can't get the order id from the key, just pretend like it doesn't exist. _, orderID, perr := ParseIndexKeyMarketToOrder(key) @@ -116,7 +117,7 @@ func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.Q }) if pageErr != nil { - return nil, status.Errorf(codes.InvalidArgument, "error iterating orders: %v", pageErr) + return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for market %d: %v", req.MarketId, pageErr) } return resp, nil @@ -124,8 +125,43 @@ func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.Q // QueryGetOwnerOrders looks up the orders from the provided owner address. func (k QueryServer) QueryGetOwnerOrders(goCtx context.Context, req *exchange.QueryGetOwnerOrdersRequest) (*exchange.QueryGetOwnerOrdersResponse, error) { - // TODO[1658]: Implement QueryGetOwnerOrders query - panic("not implemented") + if req == nil || len(req.Owner) == 0 { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + owner, aErr := sdk.AccAddressFromBech32(req.Owner) + if aErr != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid owner: %v", aErr) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + pre := GetIndexKeyPrefixAddressToOrder(owner) + store := prefix.NewStore(k.getStore(ctx), pre) + resp := &exchange.QueryGetOwnerOrdersResponse{} + var pageErr error + + resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { + // If we can't get the order id from the key, just pretend like it doesn't exist. + _, orderID, perr := ParseIndexKeyAddressToOrder(key) + if perr != nil { + return false, nil + } + if accumulate { + // Only add them to the result if we can read it. + // This might result in fewer results than the limit, but at least one bad entry won't block others. + order, oerr := k.parseOrderStoreValue(orderID, value) + if oerr != nil { + resp.Orders = append(resp.Orders, order) + } + } + return true, nil + }) + + if pageErr != nil { + return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for owner %s: %v", req.Owner, pageErr) + } + + return resp, nil } // QueryGetAssetOrders looks up the orders for a specific asset denom. From c6ef6368131112c4fd4f128dc07fab6c8c78bf97 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 16:50:32 -0600 Subject: [PATCH 211/309] [1658]: Add an order_type field to the QueryGetAssetOrdersRequest and add todos to do the same for the other indexed order queries. --- docs/proto-docs.md | 9 +- proto/provenance/exchange/v1/query.proto | 4 + x/exchange/query.pb.go | 193 +++++++++++++++-------- 3 files changed, 134 insertions(+), 72 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index f8c363ccde..dd00797ea9 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1931,6 +1931,7 @@ QueryGetAssetOrdersRequest is a request message for the QueryGetAssetOrders endp | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `asset` | [string](#string) | | asset is the denom of assets to get orders for. | +| `order_type` | [string](#string) | | order_type is optional and can limit orders to only "ask" or "bid" orders. | | `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | @@ -1962,7 +1963,9 @@ QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders en | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `market_id` | [uint32](#uint32) | | market_id is the id of the market to get all the orders for. | +| `market_id` | [uint32](#uint32) | | market_id is the id of the market to get all the orders for. + +TODO[1658]: Add an order_type field to QueryGetMarketOrdersRequest | | `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | @@ -2024,7 +2027,9 @@ QueryGetOwnerOrdersRequest is a request message for the QueryGetOwnerOrders endp | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `owner` | [string](#string) | | owner is the bech32 address string of the owner to get the orders for. | +| `owner` | [string](#string) | | owner is the bech32 address string of the owner to get the orders for. + +TODO[1658]: Add an order_type field to QueryGetOwnerOrdersRequest | | `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 6313617479..27e77b82f2 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -112,6 +112,7 @@ message QueryGetOrderResponse { message QueryGetMarketOrdersRequest { // market_id is the id of the market to get all the orders for. uint32 market_id = 1; + // TODO[1658]: Add an order_type field to QueryGetMarketOrdersRequest // pagination defines an optional pagination for the request. cosmos.base.query.v1beta1.PageRequest pagination = 99; @@ -130,6 +131,7 @@ message QueryGetMarketOrdersResponse { message QueryGetOwnerOrdersRequest { // owner is the bech32 address string of the owner to get the orders for. string owner = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // TODO[1658]: Add an order_type field to QueryGetOwnerOrdersRequest // pagination defines an optional pagination for the request. cosmos.base.query.v1beta1.PageRequest pagination = 99; @@ -148,6 +150,8 @@ message QueryGetOwnerOrdersResponse { message QueryGetAssetOrdersRequest { // asset is the denom of assets to get orders for. string asset = 1; + // order_type is optional and can limit orders to only "ask" or "bid" orders. + string order_type = 2; // pagination defines an optional pagination for the request. cosmos.base.query.v1beta1.PageRequest pagination = 99; diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index 57829640ff..2bec0ec3ec 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -479,6 +479,8 @@ func (m *QueryGetOwnerOrdersResponse) GetPagination() *query.PageResponse { type QueryGetAssetOrdersRequest struct { // asset is the denom of assets to get orders for. Asset string `protobuf:"bytes,1,opt,name=asset,proto3" json:"asset,omitempty"` + // order_type is optional and can limit orders to only "ask" or "bid" orders. + OrderType string `protobuf:"bytes,2,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` // pagination defines an optional pagination for the request. Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` } @@ -523,6 +525,13 @@ func (m *QueryGetAssetOrdersRequest) GetAsset() string { return "" } +func (m *QueryGetAssetOrdersRequest) GetOrderType() string { + if m != nil { + return m.OrderType + } + return "" +} + func (m *QueryGetAssetOrdersRequest) GetPagination() *query.PageRequest { if m != nil { return m.Pagination @@ -1026,76 +1035,77 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 1093 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0x41, 0x6f, 0x1b, 0x45, - 0x14, 0xce, 0x24, 0x4d, 0x49, 0x26, 0x8a, 0x10, 0x83, 0x5b, 0xec, 0x4d, 0x70, 0xdd, 0x2d, 0x2a, - 0x91, 0x49, 0x76, 0x62, 0xbb, 0x2d, 0xf4, 0x50, 0xa1, 0x24, 0x92, 0xa3, 0x48, 0x54, 0x4d, 0x8d, - 0xc4, 0x81, 0x03, 0x66, 0x6c, 0xbf, 0x6c, 0x57, 0xb1, 0x77, 0xdc, 0xdd, 0x8d, 0x29, 0x44, 0xb9, - 0xf4, 0x07, 0x20, 0x24, 0x38, 0x20, 0x21, 0x2a, 0x0e, 0x88, 0x33, 0x87, 0x1e, 0xf9, 0x01, 0x39, - 0x56, 0xf4, 0xc2, 0x09, 0xa1, 0x84, 0x5f, 0xc1, 0x09, 0xed, 0xcc, 0x6c, 0xbc, 0x1b, 0xef, 0xae, - 0xd7, 0xa8, 0x48, 0x39, 0x79, 0x77, 0xfc, 0xbe, 0xf7, 0xbe, 0xf9, 0x66, 0xe6, 0x7d, 0xb3, 0x58, - 0xef, 0x3b, 0x7c, 0x00, 0x36, 0xb3, 0xdb, 0x40, 0xe1, 0x49, 0xfb, 0x11, 0xb3, 0x4d, 0xa0, 0x83, - 0x0a, 0x7d, 0x7c, 0x00, 0xce, 0x97, 0x46, 0xdf, 0xe1, 0x1e, 0x27, 0x57, 0x87, 0x31, 0x46, 0x10, - 0x63, 0x0c, 0x2a, 0x5a, 0xa1, 0xcd, 0xdd, 0x1e, 0x77, 0x9b, 0x22, 0x8a, 0xca, 0x17, 0x09, 0xd1, - 0xca, 0xf2, 0x8d, 0xb6, 0x98, 0x0b, 0x32, 0x17, 0x1d, 0x54, 0x5a, 0xe0, 0xb1, 0x0a, 0xed, 0x33, - 0xd3, 0xb2, 0x99, 0x67, 0x71, 0x5b, 0xc5, 0x16, 0xc3, 0xb1, 0x41, 0x54, 0x9b, 0x5b, 0xc1, 0xff, - 0xcb, 0x26, 0xe7, 0x66, 0x17, 0x28, 0xeb, 0x5b, 0x94, 0xd9, 0x36, 0xf7, 0x04, 0x38, 0xa8, 0x94, - 0x33, 0xb9, 0xc9, 0x25, 0x03, 0xff, 0x49, 0x8d, 0xde, 0x48, 0x98, 0x16, 0x77, 0x3a, 0xe0, 0x28, - 0xa8, 0xfe, 0x3d, 0xc2, 0xf9, 0x87, 0x3e, 0xb7, 0x07, 0xfe, 0x68, 0x1d, 0x60, 0x8b, 0x75, 0xdb, - 0x0d, 0x78, 0x7c, 0x00, 0xae, 0x47, 0xee, 0xe1, 0x79, 0xe6, 0xee, 0x37, 0x05, 0x20, 0x3f, 0x5d, - 0x42, 0x2b, 0x0b, 0xd5, 0x92, 0x11, 0x2f, 0x84, 0xb1, 0xe1, 0xee, 0x8b, 0x14, 0x8d, 0x39, 0xa6, - 0x9e, 0x7c, 0x78, 0xcb, 0xea, 0x28, 0xf8, 0x4c, 0x3a, 0x7c, 0xd3, 0xea, 0x28, 0x78, 0x4b, 0x3d, - 0xe9, 0xbf, 0x4e, 0xe3, 0x42, 0x0c, 0x35, 0xb7, 0xcf, 0x6d, 0x17, 0xc8, 0x43, 0x9c, 0x6b, 0x3b, - 0x20, 0x64, 0x68, 0xee, 0x01, 0x34, 0x79, 0x5f, 0x28, 0x92, 0x47, 0xa5, 0x99, 0x95, 0x85, 0x6a, - 0xc1, 0x50, 0x4b, 0xe1, 0x0b, 0x6a, 0x28, 0x41, 0x8d, 0x2d, 0x6e, 0xd9, 0x9b, 0x97, 0x8e, 0xff, - 0xbc, 0x36, 0xd5, 0x20, 0x01, 0xb8, 0x0e, 0xf0, 0x40, 0x42, 0xc9, 0x67, 0x78, 0xc9, 0x05, 0xcf, - 0xeb, 0x42, 0x0f, 0x6c, 0xaf, 0xb9, 0xd7, 0x65, 0x5e, 0x24, 0xf3, 0x74, 0xb6, 0xcc, 0xf9, 0x61, - 0x8e, 0x7a, 0x97, 0x79, 0xa1, 0xfc, 0x9f, 0xe3, 0xe5, 0x50, 0x7e, 0xc7, 0x2f, 0x1f, 0x29, 0x30, - 0x93, 0xad, 0x40, 0x61, 0x98, 0xa4, 0xe1, 0xe7, 0x18, 0x56, 0xd0, 0x2b, 0x38, 0x27, 0x14, 0xdb, - 0x06, 0x4f, 0xaa, 0xa9, 0x16, 0xb2, 0x80, 0xe7, 0xc4, 0x2a, 0x34, 0xad, 0x4e, 0x1e, 0x95, 0xd0, - 0xca, 0xa5, 0xc6, 0x6b, 0xe2, 0x7d, 0xa7, 0xa3, 0x7f, 0x84, 0xaf, 0x9c, 0x83, 0x28, 0x81, 0x6b, - 0x78, 0x56, 0xae, 0x1c, 0x12, 0x2b, 0xf7, 0x76, 0xd2, 0xca, 0x49, 0x94, 0x8c, 0xd5, 0x9f, 0x22, - 0xbc, 0x14, 0xa4, 0xbb, 0xcf, 0x9c, 0x7d, 0x95, 0xd4, 0x0d, 0x88, 0x2c, 0xe1, 0xf9, 0x9e, 0x18, - 0x0e, 0x98, 0x2c, 0x36, 0xe6, 0xe4, 0xc0, 0x4e, 0x87, 0xd4, 0x31, 0x1e, 0x1e, 0x8c, 0x7c, 0x5b, - 0x94, 0xbd, 0x19, 0x51, 0x43, 0x9e, 0xc8, 0x40, 0x93, 0x5d, 0x66, 0x82, 0x4a, 0xdc, 0x08, 0x21, - 0xf5, 0x67, 0x08, 0x2f, 0xc7, 0x93, 0x50, 0x53, 0xbb, 0x8d, 0x2f, 0xcb, 0x43, 0xa0, 0x76, 0xcb, - 0x98, 0xb9, 0xa9, 0x60, 0xb2, 0x1d, 0xc3, 0xef, 0xdd, 0xb1, 0xfc, 0x64, 0xcd, 0x08, 0xc1, 0xef, - 0x10, 0xd6, 0xce, 0x44, 0xff, 0xc2, 0x06, 0x27, 0x2a, 0x92, 0x81, 0x67, 0xb9, 0x3f, 0x2a, 0x04, - 0x9a, 0xdf, 0xcc, 0xff, 0xfe, 0x7c, 0x2d, 0xa7, 0xaa, 0x6c, 0x74, 0x3a, 0x0e, 0xb8, 0xee, 0xc7, - 0x9e, 0x63, 0xd9, 0x66, 0x43, 0x86, 0xbd, 0x32, 0xdd, 0x7e, 0x0c, 0x2d, 0x5e, 0x84, 0xd6, 0x05, - 0x91, 0xed, 0xab, 0xa1, 0x6a, 0x1b, 0xae, 0x7b, 0x7e, 0x6b, 0xe5, 0xf0, 0x2c, 0xf3, 0x47, 0xa5, - 0x6a, 0x0d, 0xf9, 0xf2, 0xbf, 0x68, 0x13, 0x29, 0x7e, 0x41, 0xb4, 0x69, 0xa9, 0x36, 0xee, 0xd3, - 0xeb, 0x76, 0xa3, 0xca, 0xbc, 0x2a, 0x0d, 0x7e, 0x40, 0xaa, 0x21, 0x47, 0x8b, 0x5c, 0x10, 0x05, - 0x6e, 0xe3, 0xab, 0x82, 0x9c, 0x3c, 0xf1, 0x3b, 0xf6, 0x1e, 0xcf, 0xd2, 0x74, 0xf4, 0x02, 0x7e, - 0x6b, 0x04, 0x26, 0xb3, 0xeb, 0x39, 0x4c, 0xc4, 0x5f, 0xbb, 0xcc, 0x61, 0xbd, 0x40, 0x4d, 0xfd, - 0x0a, 0x7e, 0x33, 0x32, 0xaa, 0x82, 0x75, 0x5c, 0x12, 0xc3, 0x9f, 0xb0, 0xae, 0xd5, 0x61, 0x1e, - 0x6c, 0xf9, 0xfe, 0x02, 0x32, 0x6b, 0x00, 0xbd, 0x81, 0xaf, 0xa7, 0xc4, 0xa8, 0x44, 0xf7, 0x70, - 0x31, 0x12, 0x74, 0x9f, 0xd9, 0xcc, 0x84, 0x3a, 0x40, 0xa6, 0x26, 0xaa, 0x5f, 0xc7, 0xd7, 0x12, - 0xe1, 0xb2, 0x42, 0xf5, 0x9f, 0x45, 0x3c, 0x2b, 0x62, 0xc8, 0xcf, 0x08, 0xbf, 0x31, 0x62, 0xb1, - 0x64, 0x3d, 0x69, 0xe5, 0x92, 0x2e, 0x0a, 0x5a, 0x65, 0x02, 0x84, 0x9a, 0x66, 0xf9, 0xe9, 0xcb, - 0xbf, 0xbf, 0x9d, 0x7e, 0x87, 0xe8, 0x34, 0xe1, 0x9a, 0xb2, 0x07, 0xe0, 0xca, 0xbb, 0x0a, 0x79, - 0x86, 0xf0, 0x62, 0xc4, 0xa4, 0xc8, 0x6a, 0x6a, 0xc1, 0x73, 0xf6, 0xa7, 0xad, 0x65, 0x8c, 0x56, - 0xd4, 0xd6, 0x05, 0xb5, 0x32, 0x59, 0xa1, 0x69, 0x37, 0x28, 0x7a, 0x18, 0x58, 0xea, 0x11, 0xf9, - 0x0d, 0x0d, 0x8d, 0x37, 0xec, 0x38, 0xa4, 0x36, 0xae, 0x72, 0x8c, 0x49, 0x6a, 0xb7, 0x26, 0x03, - 0x29, 0xd6, 0x77, 0x05, 0xeb, 0x1a, 0xa9, 0x24, 0xb1, 0x96, 0x5b, 0x84, 0x1e, 0x9e, 0xed, 0x9d, - 0x23, 0x75, 0x15, 0x24, 0xcf, 0x91, 0xda, 0xd3, 0xd1, 0xc6, 0x4f, 0xaa, 0x63, 0x75, 0x1b, 0x31, - 0x2f, 0xad, 0x36, 0x11, 0x46, 0x71, 0xbf, 0x25, 0xb8, 0x1b, 0x64, 0x35, 0x55, 0x71, 0x97, 0x0a, - 0xbf, 0xa3, 0x87, 0xe2, 0xe7, 0x28, 0x42, 0x3b, 0xd4, 0x93, 0xc7, 0xd3, 0x1e, 0x75, 0x8f, 0xf1, - 0xb4, 0x63, 0x9a, 0x7e, 0x66, 0xda, 0xc2, 0x8a, 0xe8, 0xa1, 0xf8, 0x39, 0x22, 0x3f, 0x05, 0x87, - 0x2e, 0xdc, 0x46, 0xc7, 0x1c, 0xba, 0x98, 0xb6, 0x3e, 0xe6, 0xd0, 0xc5, 0xf5, 0x68, 0xfd, 0xa6, - 0x20, 0x5c, 0x22, 0xc5, 0x74, 0xc2, 0xe4, 0x17, 0x84, 0x5f, 0x3f, 0xd7, 0x15, 0x89, 0x91, 0x5a, - 0x6e, 0xa4, 0xeb, 0x6a, 0x34, 0x73, 0xbc, 0x22, 0x57, 0x15, 0xe4, 0x56, 0x49, 0x39, 0xfb, 0x06, - 0x26, 0x5f, 0x23, 0xbc, 0x10, 0xea, 0xc6, 0xa4, 0x9c, 0x5a, 0x34, 0xd2, 0xc8, 0xb5, 0xf7, 0x32, - 0xc5, 0x66, 0x55, 0xae, 0x2f, 0x09, 0x1c, 0x07, 0x1e, 0x19, 0xd7, 0xe3, 0xc9, 0x07, 0xa9, 0x25, - 0x53, 0xac, 0x43, 0xbb, 0xfb, 0x1f, 0x90, 0x8a, 0xfa, 0x1d, 0x41, 0x7d, 0x9d, 0x18, 0x49, 0xd4, - 0x07, 0x0a, 0x4d, 0xc5, 0x37, 0x11, 0x34, 0xa5, 0xbe, 0xe4, 0x25, 0x52, 0xd6, 0x38, 0x6a, 0x25, - 0xe4, 0x4e, 0x26, 0x3a, 0x23, 0xd6, 0xa5, 0xbd, 0x3f, 0x31, 0x4e, 0x4d, 0x62, 0x5b, 0x4c, 0x62, - 0x83, 0x7c, 0x38, 0x41, 0x77, 0x3b, 0x9b, 0x57, 0x4f, 0xe4, 0xf3, 0xbf, 0xb5, 0xdc, 0x4d, 0x38, - 0x3e, 0x29, 0xa2, 0x17, 0x27, 0x45, 0xf4, 0xd7, 0x49, 0x11, 0x7d, 0x73, 0x5a, 0x9c, 0x7a, 0x71, - 0x5a, 0x9c, 0xfa, 0xe3, 0xb4, 0x38, 0x85, 0x0b, 0x16, 0x4f, 0x60, 0xb7, 0x8b, 0x3e, 0x35, 0x4c, - 0xcb, 0x7b, 0x74, 0xd0, 0x32, 0xda, 0xbc, 0x17, 0x62, 0xb0, 0x66, 0xf1, 0x30, 0x9f, 0x27, 0x67, - 0x8c, 0x5a, 0x97, 0xc5, 0xe7, 0x75, 0xed, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb6, 0x59, 0x55, - 0xc2, 0x5c, 0x10, 0x00, 0x00, + // 1113 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0xc1, 0x6f, 0x1b, 0xc5, + 0x17, 0xce, 0x24, 0x4d, 0x7f, 0xf1, 0x44, 0xd1, 0x4f, 0x0c, 0x6e, 0xb1, 0x37, 0xa9, 0xeb, 0x6e, + 0x51, 0x89, 0x4c, 0xb2, 0x13, 0xdb, 0x6d, 0xa1, 0x87, 0x0a, 0x25, 0x91, 0x1c, 0x45, 0xa2, 0x6a, + 0xba, 0x20, 0x0e, 0x1c, 0x30, 0x63, 0xfb, 0x65, 0xbb, 0x8a, 0xbd, 0xe3, 0xee, 0x6e, 0x4c, 0xa3, + 0x28, 0x97, 0xfe, 0x01, 0x08, 0x09, 0x0e, 0x20, 0x44, 0xc5, 0x01, 0x71, 0xe6, 0xd0, 0x23, 0x7f, + 0x40, 0x8e, 0x15, 0xbd, 0x70, 0x42, 0x28, 0xe1, 0xaf, 0xe0, 0x84, 0x76, 0x66, 0x36, 0xde, 0x8d, + 0xed, 0xf5, 0x1a, 0x15, 0x29, 0x27, 0xef, 0x8e, 0xdf, 0xf7, 0xde, 0x37, 0xdf, 0xcc, 0xbc, 0x6f, + 0x16, 0xeb, 0x5d, 0x97, 0xf7, 0xc0, 0x61, 0x4e, 0x13, 0x28, 0x3c, 0x6d, 0x3e, 0x66, 0x8e, 0x05, + 0xb4, 0x57, 0xa6, 0x4f, 0xf6, 0xc1, 0x3d, 0x30, 0xba, 0x2e, 0xf7, 0x39, 0xb9, 0xda, 0x8f, 0x31, + 0xc2, 0x18, 0xa3, 0x57, 0xd6, 0xf2, 0x4d, 0xee, 0x75, 0xb8, 0x57, 0x17, 0x51, 0x54, 0xbe, 0x48, + 0x88, 0x56, 0x92, 0x6f, 0xb4, 0xc1, 0x3c, 0x90, 0xb9, 0x68, 0xaf, 0xdc, 0x00, 0x9f, 0x95, 0x69, + 0x97, 0x59, 0xb6, 0xc3, 0x7c, 0x9b, 0x3b, 0x2a, 0xb6, 0x10, 0x8d, 0x0d, 0xa3, 0x9a, 0xdc, 0x0e, + 0xff, 0x5f, 0xb2, 0x38, 0xb7, 0xda, 0x40, 0x59, 0xd7, 0xa6, 0xcc, 0x71, 0xb8, 0x2f, 0xc0, 0x61, + 0xa5, 0xac, 0xc5, 0x2d, 0x2e, 0x19, 0x04, 0x4f, 0x6a, 0xf4, 0xe6, 0x88, 0x69, 0x71, 0xb7, 0x05, + 0xae, 0x82, 0xea, 0xdf, 0x22, 0x9c, 0x7b, 0x14, 0x70, 0x7b, 0x18, 0x8c, 0xd6, 0x00, 0x36, 0x59, + 0xbb, 0x69, 0xc2, 0x93, 0x7d, 0xf0, 0x7c, 0x72, 0x1f, 0x67, 0x98, 0xb7, 0x57, 0x17, 0x80, 0xdc, + 0x74, 0x11, 0x2d, 0xcf, 0x57, 0x8a, 0xc6, 0x70, 0x21, 0x8c, 0x75, 0x6f, 0x4f, 0xa4, 0x30, 0xe7, + 0x98, 0x7a, 0x0a, 0xe0, 0x0d, 0xbb, 0xa5, 0xe0, 0x33, 0xc9, 0xf0, 0x0d, 0xbb, 0xa5, 0xe0, 0x0d, + 0xf5, 0xa4, 0xff, 0x32, 0x8d, 0xf3, 0x43, 0xa8, 0x79, 0x5d, 0xee, 0x78, 0x40, 0x1e, 0xe1, 0x6c, + 0xd3, 0x05, 0x21, 0x43, 0x7d, 0x17, 0xa0, 0xce, 0xbb, 0x42, 0x91, 0x1c, 0x2a, 0xce, 0x2c, 0xcf, + 0x57, 0xf2, 0x86, 0x5a, 0x8a, 0x40, 0x50, 0x43, 0x09, 0x6a, 0x6c, 0x72, 0xdb, 0xd9, 0xb8, 0x74, + 0xfc, 0xc7, 0xf5, 0x29, 0x93, 0x84, 0xe0, 0x1a, 0xc0, 0x43, 0x09, 0x25, 0x9f, 0xe1, 0x45, 0x0f, + 0x7c, 0xbf, 0x0d, 0x1d, 0x70, 0xfc, 0xfa, 0x6e, 0x9b, 0xf9, 0xb1, 0xcc, 0xd3, 0xe9, 0x32, 0xe7, + 0xfa, 0x39, 0x6a, 0x6d, 0xe6, 0x47, 0xf2, 0x7f, 0x8e, 0x97, 0x22, 0xf9, 0xdd, 0xa0, 0x7c, 0xac, + 0xc0, 0x4c, 0xba, 0x02, 0xf9, 0x7e, 0x12, 0x33, 0xc8, 0xd1, 0xaf, 0xa0, 0x97, 0x71, 0x56, 0x28, + 0xb6, 0x05, 0xbe, 0x54, 0x53, 0x2d, 0x64, 0x1e, 0xcf, 0x89, 0x55, 0xa8, 0xdb, 0xad, 0x1c, 0x2a, + 0xa2, 0xe5, 0x4b, 0xe6, 0xff, 0xc4, 0xfb, 0x76, 0x4b, 0xff, 0x10, 0x5f, 0x39, 0x07, 0x51, 0x02, + 0x57, 0xf1, 0xac, 0x5c, 0x39, 0x24, 0x56, 0xee, 0xda, 0xa8, 0x95, 0x93, 0x28, 0x19, 0xab, 0x3f, + 0x43, 0x78, 0x31, 0x4c, 0xf7, 0x80, 0xb9, 0x7b, 0x2a, 0xa9, 0x17, 0x12, 0x59, 0xc4, 0x99, 0x8e, + 0x18, 0x0e, 0x99, 0x2c, 0x98, 0x73, 0x72, 0x60, 0xbb, 0x45, 0x6a, 0x18, 0xf7, 0x0f, 0x46, 0xae, + 0x29, 0xca, 0xde, 0x8a, 0xa9, 0x21, 0x4f, 0x64, 0xa8, 0xc9, 0x0e, 0xb3, 0x40, 0x25, 0x36, 0x23, + 0x48, 0xfd, 0x39, 0xc2, 0x4b, 0xc3, 0x49, 0xa8, 0xa9, 0xdd, 0xc1, 0x97, 0xe5, 0x21, 0x50, 0xbb, + 0x65, 0xcc, 0xdc, 0x54, 0x30, 0xd9, 0x1a, 0xc2, 0xef, 0x9d, 0xb1, 0xfc, 0x64, 0xcd, 0x18, 0xc1, + 0x6f, 0x10, 0xd6, 0xce, 0x44, 0xff, 0xc2, 0x01, 0x37, 0x2e, 0x92, 0x81, 0x67, 0x79, 0x30, 0x2a, + 0x04, 0xca, 0x6c, 0xe4, 0x7e, 0x7b, 0xb1, 0x9a, 0x55, 0x55, 0xd6, 0x5b, 0x2d, 0x17, 0x3c, 0xef, + 0x23, 0xdf, 0xb5, 0x1d, 0xcb, 0x94, 0x61, 0xaf, 0x4d, 0xb7, 0x1f, 0x22, 0x8b, 0x17, 0xa3, 0x75, + 0x41, 0x64, 0xfb, 0x2e, 0x22, 0xdb, 0xba, 0xe7, 0x9d, 0xdf, 0x5b, 0x59, 0x3c, 0xcb, 0x82, 0x51, + 0x29, 0x9b, 0x29, 0x5f, 0xc8, 0x35, 0x8c, 0xe5, 0xd6, 0xf7, 0x0f, 0xba, 0x20, 0x9a, 0x58, 0xc6, + 0xcc, 0x88, 0x91, 0x8f, 0x0f, 0xba, 0xf0, 0x9f, 0x68, 0x17, 0xe3, 0x76, 0x41, 0xb4, 0x6b, 0xa8, + 0x36, 0x1f, 0xd0, 0x6b, 0xb7, 0xe3, 0xc2, 0xbd, 0x2e, 0x0d, 0xbe, 0x47, 0xaa, 0x61, 0xc7, 0x8b, + 0x5c, 0x10, 0x05, 0xee, 0xe0, 0xab, 0x82, 0x9c, 0xec, 0x08, 0xdb, 0xce, 0x2e, 0x4f, 0xd3, 0x94, + 0xf4, 0x3c, 0x7e, 0x6b, 0x00, 0x26, 0xb3, 0xeb, 0x59, 0x4c, 0xc4, 0x5f, 0x3b, 0xcc, 0x65, 0x9d, + 0x50, 0x4d, 0xfd, 0x0a, 0x7e, 0x33, 0x36, 0xaa, 0x82, 0x75, 0x5c, 0x14, 0xc3, 0x9f, 0xb0, 0xb6, + 0xdd, 0x62, 0x3e, 0x6c, 0x06, 0xfe, 0x03, 0x32, 0x6b, 0x08, 0xbd, 0x89, 0x6f, 0x24, 0xc4, 0xa8, + 0x44, 0xf7, 0x71, 0x21, 0x16, 0xf4, 0x80, 0x39, 0xcc, 0x82, 0x1a, 0x40, 0xaa, 0x26, 0xab, 0xdf, + 0xc0, 0xd7, 0x47, 0xc2, 0x65, 0x85, 0xca, 0xdf, 0x0b, 0x78, 0x56, 0xc4, 0x90, 0x9f, 0x10, 0x7e, + 0x63, 0xc0, 0x82, 0xc9, 0xda, 0xa8, 0x95, 0x1b, 0x75, 0x91, 0xd0, 0xca, 0x13, 0x20, 0xd4, 0x34, + 0x4b, 0xcf, 0x5e, 0xfd, 0xf5, 0xf5, 0xf4, 0xdb, 0x44, 0xa7, 0x23, 0xae, 0x31, 0xbb, 0x00, 0x9e, + 0xbc, 0xcb, 0x90, 0xe7, 0x08, 0x2f, 0xc4, 0x4c, 0x8c, 0xac, 0x24, 0x16, 0x3c, 0x67, 0x8f, 0xda, + 0x6a, 0xca, 0x68, 0x45, 0x6d, 0x4d, 0x50, 0x2b, 0x91, 0x65, 0x9a, 0x74, 0xc3, 0xa2, 0x87, 0xa1, + 0xe5, 0x1e, 0x91, 0x5f, 0x51, 0xdf, 0x98, 0xa3, 0x8e, 0x44, 0xaa, 0xe3, 0x2a, 0x0f, 0x31, 0x51, + 0xed, 0xf6, 0x64, 0x20, 0xc5, 0xfa, 0x9e, 0x60, 0x5d, 0x25, 0xe5, 0x51, 0xac, 0xe5, 0x16, 0xa1, + 0x87, 0x67, 0x7b, 0xe7, 0x48, 0x5d, 0x15, 0xc9, 0x0b, 0xa4, 0xf6, 0x74, 0xdc, 0x18, 0x48, 0x65, + 0xac, 0x6e, 0x03, 0xe6, 0xa6, 0x55, 0x27, 0xc2, 0x28, 0xee, 0xb7, 0x05, 0x77, 0x83, 0xac, 0x24, + 0x2a, 0xee, 0x51, 0xe1, 0x87, 0xf4, 0x50, 0xfc, 0x1c, 0xc5, 0x68, 0x47, 0x7a, 0xf2, 0x78, 0xda, + 0x83, 0xe6, 0x32, 0x9e, 0xf6, 0x90, 0xa6, 0x9f, 0x9a, 0xb6, 0x70, 0x2a, 0x7a, 0x28, 0x7e, 0x8e, + 0xc8, 0x8f, 0xe1, 0xa1, 0x8b, 0xb6, 0xd1, 0x31, 0x87, 0x6e, 0x48, 0x5b, 0x1f, 0x73, 0xe8, 0x86, + 0xf5, 0x68, 0xfd, 0x96, 0x20, 0x5c, 0x24, 0x85, 0x64, 0xc2, 0xe4, 0x67, 0x84, 0xff, 0x7f, 0xae, + 0x2b, 0x12, 0x23, 0xb1, 0xdc, 0x40, 0xd7, 0xd5, 0x68, 0xea, 0x78, 0x45, 0xae, 0x22, 0xc8, 0xad, + 0x90, 0x52, 0xfa, 0x0d, 0x4c, 0xbe, 0x44, 0x78, 0x3e, 0xd2, 0x8d, 0x49, 0x29, 0xb1, 0x68, 0xac, + 0x91, 0x6b, 0xef, 0xa6, 0x8a, 0x4d, 0xab, 0x5c, 0x57, 0x12, 0x38, 0x0e, 0x3d, 0x72, 0x58, 0x8f, + 0x27, 0xef, 0x27, 0x96, 0x4c, 0xb0, 0x0e, 0xed, 0xde, 0xbf, 0x40, 0x2a, 0xea, 0x77, 0x05, 0xf5, + 0x35, 0x62, 0x8c, 0xa2, 0xde, 0x53, 0x68, 0x2a, 0xbe, 0x99, 0xa0, 0x2e, 0xf5, 0x25, 0xaf, 0x90, + 0xb2, 0xc6, 0x41, 0x2b, 0x21, 0x77, 0x53, 0xd1, 0x19, 0xb0, 0x2e, 0xed, 0xbd, 0x89, 0x71, 0x6a, + 0x12, 0x5b, 0x62, 0x12, 0xeb, 0xe4, 0x83, 0x09, 0xba, 0xdb, 0xd9, 0xbc, 0x3a, 0x22, 0x5f, 0xf0, + 0x2d, 0xe6, 0x6d, 0xc0, 0xf1, 0x49, 0x01, 0xbd, 0x3c, 0x29, 0xa0, 0x3f, 0x4f, 0x0a, 0xe8, 0xab, + 0xd3, 0xc2, 0xd4, 0xcb, 0xd3, 0xc2, 0xd4, 0xef, 0xa7, 0x85, 0x29, 0x9c, 0xb7, 0xf9, 0x08, 0x76, + 0x3b, 0xe8, 0x53, 0xc3, 0xb2, 0xfd, 0xc7, 0xfb, 0x0d, 0xa3, 0xc9, 0x3b, 0x11, 0x06, 0xab, 0x36, + 0x8f, 0xf2, 0x79, 0x7a, 0xc6, 0xa8, 0x71, 0x59, 0x7c, 0x7e, 0x57, 0xff, 0x09, 0x00, 0x00, 0xff, + 0xff, 0x41, 0x5f, 0x03, 0x0a, 0x7c, 0x10, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1919,6 +1929,13 @@ func (m *QueryGetAssetOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, err i-- dAtA[i] = 0x9a } + if len(m.OrderType) > 0 { + i -= len(m.OrderType) + copy(dAtA[i:], m.OrderType) + i = encodeVarintQuery(dAtA, i, uint64(len(m.OrderType))) + i-- + dAtA[i] = 0x12 + } if len(m.Asset) > 0 { i -= len(m.Asset) copy(dAtA[i:], m.Asset) @@ -2423,6 +2440,10 @@ func (m *QueryGetAssetOrdersRequest) Size() (n int) { if l > 0 { n += 1 + l + sovQuery(uint64(l)) } + l = len(m.OrderType) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } if m.Pagination != nil { l = m.Pagination.Size() n += 2 + l + sovQuery(uint64(l)) @@ -3518,6 +3539,38 @@ func (m *QueryGetAssetOrdersRequest) Unmarshal(dAtA []byte) error { } m.Asset = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OrderType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex case 99: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) From 2741766234e1c91737d727b6002d325a5df0c130 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 29 Sep 2023 17:08:24 -0600 Subject: [PATCH 212/309] [1658]: Implement QueryGetAssetOrders and QueryGetAllOrders. --- x/exchange/keeper/grpc_query.go | 81 +++++++++++++++++++++++++++++++-- x/exchange/keeper/keys.go | 18 ++++++++ x/exchange/keeper/keys_test.go | 46 +++++++++++++++++++ 3 files changed, 141 insertions(+), 4 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 77950c8b89..3d96d86378 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -3,6 +3,7 @@ package keeper import ( "context" "fmt" + "strings" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -166,14 +167,86 @@ func (k QueryServer) QueryGetOwnerOrders(goCtx context.Context, req *exchange.Qu // QueryGetAssetOrders looks up the orders for a specific asset denom. func (k QueryServer) QueryGetAssetOrders(goCtx context.Context, req *exchange.QueryGetAssetOrdersRequest) (*exchange.QueryGetAssetOrdersResponse, error) { - // TODO[1658]: Implement QueryGetAssetOrders query - panic("not implemented") + if req == nil || len(req.Asset) == 0 { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + var pre []byte + switch strings.ToLower(req.OrderType) { + case "": + pre = GetIndexKeyPrefixAssetToOrder(req.Asset) + case exchange.OrderTypeAsk: + pre = GetIndexKeyPrefixAssetToOrderAsks(req.Asset) + case exchange.OrderTypeBid: + pre = GetIndexKeyPrefixAssetToOrderBids(req.Asset) + default: + return nil, status.Errorf(codes.InvalidArgument, "unknown order type %q", req.OrderType) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + store := prefix.NewStore(k.getStore(ctx), pre) + resp := &exchange.QueryGetAssetOrdersResponse{} + var pageErr error + + resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { + // If we can't get the order id from the key, just pretend like it doesn't exist. + _, _, orderID, perr := ParseIndexKeyAssetToOrder(key) + if perr != nil { + return false, nil + } + if accumulate { + // Only add them to the result if we can read it. + // This might result in fewer results than the limit, but at least one bad entry won't block others. + order, oerr := k.parseOrderStoreValue(orderID, value) + if oerr != nil { + resp.Orders = append(resp.Orders, order) + } + } + return true, nil + }) + + if pageErr != nil { + return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for asset %s: %v", req.Asset, pageErr) + } + + return resp, nil } // QueryGetAllOrders gets all orders in the exchange module. func (k QueryServer) QueryGetAllOrders(goCtx context.Context, req *exchange.QueryGetAllOrdersRequest) (*exchange.QueryGetAllOrdersResponse, error) { - // TODO[1658]: Implement QueryGetAllOrders query - panic("not implemented") + var pagination *query.PageRequest + if req != nil { + pagination = req.Pagination + } + + ctx := sdk.UnwrapSDKContext(goCtx) + pre := GetKeyPrefixOrder() + store := prefix.NewStore(k.getStore(ctx), pre) + resp := &exchange.QueryGetAllOrdersResponse{} + var pageErr error + + resp.Pagination, pageErr = query.FilteredPaginate(store, pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { + // If we can't get the order id from the key, just pretend like it doesn't exist. + orderID, ok := ParseKeyOrder(key) + if !ok { + return false, nil + } + if accumulate { + // Only add them to the result if we can read it. + // This might result in fewer results than the limit, but at least one bad entry won't block others. + order, oerr := k.parseOrderStoreValue(orderID, value) + if oerr != nil { + resp.Orders = append(resp.Orders, order) + } + } + return true, nil + }) + + if pageErr != nil { + return nil, status.Errorf(codes.InvalidArgument, "error iterating all orders: %v", pageErr) + } + + return resp, nil } // QueryMarketInfo returns the information/details about a market. diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 68cea474cf..796344cfe7 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -58,9 +58,11 @@ import ( // Ask Orders: 0x02 | (8 bytes) => 0x00 | protobuf(AskOrder) // Bid Orders: 0x02 | (8 bytes) => 0x01 | protobuf(BidOrder) // +// TODO[1658]: Refactor the market to order index to have the order type byte before the order id. // A market to order index is maintained with the following format: // 0x03 | (4 bytes) | (8 bytes) => // +// TODO[1658]: Refactor the address to order index to have the order type byte before the order id. // An address to order index is maintained with the following format: // 0x04 | len(
) (1 byte) |
| (8 bytes) => nil // @@ -542,6 +544,22 @@ func MakeKeyOrder(orderID uint64) []byte { return rv } +// ParseKeyOrder will extract the order id from the provided order key. +// The returned bool is whether parsing was successful (true = ok). +// +// The input can have the following formats: +// - | (8 bytes) +// - (8 bytes) +func ParseKeyOrder(key []byte) (uint64, bool) { + if len(key) < 8 || len(key) > 9 { + return 0, false + } + if len(key) == 9 && key[0] != OrderKeyTypeAsk && key[0] != OrderKeyTypeBid { + return 0, false + } + return uint64FromBz(key[len(key)-8:]) +} + // indexPrefixMarketToOrder creates the prefix for the market to order prefix entries with some extra space for the rest. func indexPrefixMarketToOrder(marketID uint32, extraCap int) []byte { return prepKey(KeyTypeMarketToOrderIndex, uint32Bz(marketID), extraCap) diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index fc3372c0e1..3154ef2fed 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -2822,6 +2822,52 @@ func TestMakeKeyOrder(t *testing.T) { } } +func TestParseKeyOrder(t *testing.T) { + tests := []struct { + name string + key []byte + expOrderID uint64 + expOK bool + }{ + {name: "nil key", key: nil, expOrderID: 0, expOK: false}, + {name: "empty key", key: []byte{}, expOrderID: 0, expOK: false}, + {name: "7 byte key", key: []byte{1, 2, 3, 4, 5, 6, 7}, expOrderID: 0, expOK: false}, + {name: "10 byte key", key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, expOrderID: 0, expOK: false}, + {name: "9 byte key unknown type", key: []byte{99, 1, 2, 3, 4, 5, 6, 7, 8}, expOrderID: 0, expOK: false}, + { + name: "8 byte key", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + expOrderID: 72_623_859_790_382_856, + expOK: true, + }, + { + name: "9 byte key ask", + key: []byte{keeper.OrderKeyTypeAsk, 1, 2, 3, 4, 5, 6, 7, 8}, + expOrderID: 72_623_859_790_382_856, + expOK: true, + }, + { + name: "9 byte key bid", + key: []byte{keeper.OrderKeyTypeBid, 1, 2, 3, 4, 5, 6, 7, 8}, + expOrderID: 72_623_859_790_382_856, + expOK: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var orderID uint64 + var ok bool + testFunc := func() { + orderID, ok = keeper.ParseKeyOrder(tc.key) + } + require.NotPanics(t, testFunc, "ParseKeyOrder") + assert.Equal(t, tc.expOK, ok, "ParseKeyOrder bool ok") + assert.Equal(t, tc.expOrderID, orderID, "ParseKeyOrder order ID") + }) + } +} + func TestGetIndexKeyPrefixMarketToOrder(t *testing.T) { tests := []struct { name string From 1cb40db96dcb6569e4b4ae914374a3bfe59dcc9e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sat, 30 Sep 2023 23:48:24 -0600 Subject: [PATCH 213/309] [1658]: Refactor the Market to order and address to order indexes to include the order type byte. --- x/exchange/keeper/grpc_query.go | 4 +- x/exchange/keeper/keys.go | 144 ++++-- x/exchange/keeper/keys_test.go | 846 +++++++++++++++++++++++++------- x/exchange/keeper/orders.go | 101 ++-- 4 files changed, 861 insertions(+), 234 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 3d96d86378..a7a2be8abf 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -102,7 +102,7 @@ func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.Q resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { // If we can't get the order id from the key, just pretend like it doesn't exist. - _, orderID, perr := ParseIndexKeyMarketToOrder(key) + _, _, orderID, perr := ParseIndexKeyMarketToOrder(key) if perr != nil { return false, nil } @@ -143,7 +143,7 @@ func (k QueryServer) QueryGetOwnerOrders(goCtx context.Context, req *exchange.Qu resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { // If we can't get the order id from the key, just pretend like it doesn't exist. - _, orderID, perr := ParseIndexKeyAddressToOrder(key) + _, _, orderID, perr := ParseIndexKeyAddressToOrder(key) if perr != nil { return false, nil } diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 796344cfe7..f8dbcff635 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -58,13 +58,11 @@ import ( // Ask Orders: 0x02 | (8 bytes) => 0x00 | protobuf(AskOrder) // Bid Orders: 0x02 | (8 bytes) => 0x01 | protobuf(BidOrder) // -// TODO[1658]: Refactor the market to order index to have the order type byte before the order id. // A market to order index is maintained with the following format: -// 0x03 | (4 bytes) | (8 bytes) => +// 0x03 | (4 bytes) | | (8 bytes) => nil // -// TODO[1658]: Refactor the address to order index to have the order type byte before the order id. // An address to order index is maintained with the following format: -// 0x04 | len(
) (1 byte) |
| (8 bytes) => nil +// 0x04 | len(
) (1 byte) |
| | (8 bytes) => nil // // An asset type to order index is maintained with the following format: // 0x05 | | (1 byte) | (8 bytes) => nil @@ -117,6 +115,8 @@ const ( RecordSeparator = byte(0x1E) ) +var OrderKeyTypeBytes = []byte{OrderKeyTypeAsk, OrderKeyTypeBid} + // prepKey creates a single byte slice consisting of the type byte and provided byte slice with some extra capacity in the underlying array. // The idea is that you can append(...) to the result of this without it needed a new underlying array. func prepKey(typeByte byte, bz []byte, extraCap int) []byte { @@ -565,42 +565,77 @@ func indexPrefixMarketToOrder(marketID uint32, extraCap int) []byte { return prepKey(KeyTypeMarketToOrderIndex, uint32Bz(marketID), extraCap) } +// indexPrefixMarketToOrderOfType creates the prefix for market to order index entries for the given asset denom and order type. +func indexPrefixMarketToOrderOfType(marketID uint32, orderTypeByte byte, extraCap int) []byte { + rv := indexPrefixMarketToOrder(marketID, 1+extraCap) + rv = append(rv, orderTypeByte) + return rv +} + // GetIndexKeyPrefixMarketToOrder creates the prefix for the market to order index limited ot the given market id. func GetIndexKeyPrefixMarketToOrder(marketID uint32) []byte { return indexPrefixMarketToOrder(marketID, 0) } +// GetIndexKeyPrefixMarketToOrderAsks creates a key prefix for the market to orders limited to the given asset and only ask orders. +func GetIndexKeyPrefixMarketToOrderAsks(marketID uint32) []byte { + return indexPrefixMarketToOrderOfType(marketID, OrderKeyTypeAsk, 0) +} + +// GetIndexKeyPrefixMarketToOrderBids creates a key prefix for the market to orders limited to the given asset and only bid orders. +func GetIndexKeyPrefixMarketToOrderBids(marketID uint32) []byte { + return indexPrefixMarketToOrderOfType(marketID, OrderKeyTypeBid, 0) +} + // MakeIndexKeyMarketToOrder creates the key to use for the market to order index with the given ids. -func MakeIndexKeyMarketToOrder(marketID uint32, orderID uint64) []byte { - rv := indexPrefixMarketToOrder(marketID, 8) - rv = append(rv, uint64Bz(orderID)...) +func MakeIndexKeyMarketToOrder(marketID uint32, orderTypeByte byte, orderID uint64) []byte { + suffix := uint64Bz(orderID) + rv := indexPrefixMarketToOrderOfType(marketID, orderTypeByte, len(suffix)) + rv = append(rv, suffix...) return rv } // ParseIndexKeyMarketToOrder will extract the market id and order id from a market to order index key. // The input can have the following formats: -// - | (4 bytes) | (8 bytes) -// - (4 bytes) | (8 bytes) +// - | (4 bytes) | | (8 bytes) +// - (4 bytes) | | (8 bytes) +// - | (8 bytes) // - (8 bytes) // -// In the case where just the is provided, the returned market id will be 0. -func ParseIndexKeyMarketToOrder(key []byte) (uint32, uint64, error) { +// In the case where just the is provided, the returned market id will be 0 and the order type byte will also be 0. +// In the case where just | is provided, the returned market id will be 0. +func ParseIndexKeyMarketToOrder(key []byte) (uint32, byte, uint64, error) { var marketIDBz, orderIDBz []byte + var orderTypeByteP *byte switch len(key) { case 8: orderIDBz = key - case 12: - marketIDBz = key[:4] - orderIDBz = key[4:] + case 9: + orderTypeByteP = &key[0] + orderIDBz = key[1:] case 13: + marketIDBz = key[:4] + orderTypeByteP = &key[4] + orderIDBz = key[5:] + case 14: if key[0] != KeyTypeMarketToOrderIndex { - return 0, 0, fmt.Errorf("cannot parse market to order key: unknown type byte %#x, expected %#x", + return 0, 0, 0, fmt.Errorf("cannot parse market to order key: unknown type byte %#x, expected %#x", key[0], KeyTypeMarketToOrderIndex) } marketIDBz = key[1:5] - orderIDBz = key[5:] + orderTypeByteP = &key[5] + orderIDBz = key[6:] default: - return 0, 0, fmt.Errorf("cannot parse market to order key: length %d, expected 8, 12, or 13", len(key)) + return 0, 0, 0, fmt.Errorf("cannot parse market to order key: length %d, expected 8, 9, 13, or 14", len(key)) + } + + var orderTypeByte byte + if orderTypeByteP != nil { + orderTypeByte = *orderTypeByteP + if !bytes.Contains(OrderKeyTypeBytes, []byte{orderTypeByte}) { + return 0, 0, 0, fmt.Errorf("cannot parse market to order key: unknown order type byte %#x, expected one of %#v", + orderTypeByte, OrderKeyTypeBytes) + } } var marketID uint32 @@ -608,7 +643,7 @@ func ParseIndexKeyMarketToOrder(key []byte) (uint32, uint64, error) { marketID, _ = uint32FromBz(marketIDBz) } orderID, _ := uint64FromBz(orderIDBz) - return marketID, orderID, nil + return marketID, orderTypeByte, orderID, nil } // indexPrefixAddressToOrder creates the prefix for the address to order index entries with some extra apace for the rest. @@ -619,38 +654,71 @@ func indexPrefixAddressToOrder(addr sdk.AccAddress, extraCap int) []byte { return prepKey(KeyTypeAddressToOrderIndex, address.MustLengthPrefix(addr), extraCap) } +// indexPrefixAddressToOrderOfType creates the prefix for address to order index entries for the given asset denom and order type. +func indexPrefixAddressToOrderOfType(addr sdk.AccAddress, orderTypeByte byte, extraCap int) []byte { + rv := indexPrefixAddressToOrder(addr, 1+extraCap) + rv = append(rv, orderTypeByte) + return rv +} + // GetIndexKeyPrefixAddressToOrder creates a key prefix for the address to order index limited to the given address. func GetIndexKeyPrefixAddressToOrder(addr sdk.AccAddress) []byte { return indexPrefixAddressToOrder(addr, 0) } +// GetIndexKeyPrefixAddressToOrderAsks creates a key prefix for the address to orders limited to the given asset and only ask orders. +func GetIndexKeyPrefixAddressToOrderAsks(addr sdk.AccAddress) []byte { + return indexPrefixAddressToOrderOfType(addr, OrderKeyTypeAsk, 0) +} + +// GetIndexKeyPrefixAddressToOrderBids creates a key prefix for the address to orders limited to the given asset and only bid orders. +func GetIndexKeyPrefixAddressToOrderBids(addr sdk.AccAddress) []byte { + return indexPrefixAddressToOrderOfType(addr, OrderKeyTypeBid, 0) +} + // MakeIndexKeyAddressToOrder creates the key to use for the address to order index with the given values. -func MakeIndexKeyAddressToOrder(addr sdk.AccAddress, orderID uint64) []byte { - rv := indexPrefixAddressToOrder(addr, 8) - rv = append(rv, uint64Bz(orderID)...) +func MakeIndexKeyAddressToOrder(addr sdk.AccAddress, orderTypeByte byte, orderID uint64) []byte { + suffix := uint64Bz(orderID) + rv := indexPrefixAddressToOrderOfType(addr, orderTypeByte, len(suffix)) + rv = append(rv, suffix...) return rv } // ParseIndexKeyAddressToOrder will extract what it can from an address to order index key. // The input can have the following formats: -// - | | | -// - | | +// - | | | | +// - | | | +// - | // - // -// In the case where just the is provided, the returned address will be empty. -func ParseIndexKeyAddressToOrder(key []byte) (sdk.AccAddress, uint64, error) { +// In the case where just the is provided, the returned address will be empty, and type byte will be 0. +// In the case where just | is provided, the returned address will be empty. +func ParseIndexKeyAddressToOrder(key []byte) (sdk.AccAddress, byte, uint64, error) { if len(key) < 8 { - return nil, 0, fmt.Errorf("cannot parse address to order index key: only has %d bytes, expected at least 8", len(key)) + return nil, 0, 0, fmt.Errorf("cannot parse address to order index key: only has %d bytes, expected at least 8", len(key)) } + pre, orderIDBz := key[:len(key)-8], key[len(key)-8:] orderID, _ := uint64FromBz(orderIDBz) + + var orderTypeByte byte + if len(pre) > 0 { + orderTypeByte = pre[len(pre)-1] + pre = pre[:len(pre)-1] + if !bytes.Contains(OrderKeyTypeBytes, []byte{orderTypeByte}) { + return nil, 0, 0, fmt.Errorf("cannot parse address to order key: unknown order type byte %#x, expected one of %#v", + orderTypeByte, OrderKeyTypeBytes) + } + } + var addr sdk.AccAddress if len(pre) > 0 { // Either the first byte is a length byte, or it's the key type byte. // First check it as a length byte, then, if that fails but it's the key type byte, check the second as the length. // Either way, there needs to be at least 2 bytes: a length byte then address byte (length 0 isn't allowed). if len(pre) == 1 { - return nil, 0, fmt.Errorf("cannot parse address to order index key: unable to determine address from single byte %#x", pre[0]) + return nil, 0, 0, fmt.Errorf("cannot parse address to order index key: unable to determine address from single byte %#x", + pre[0]) } var rest []byte var err error @@ -659,10 +727,12 @@ func ParseIndexKeyAddressToOrder(key []byte) (sdk.AccAddress, uint64, error) { addr, rest, err = parseLengthPrefixedAddr(pre[1:]) } if len(addr) == 0 || len(rest) != 0 || err != nil { - return nil, 0, fmt.Errorf("cannot parse address to order index key: unable to determine address from [%d, %d, ...(length %d)]", pre[0], pre[1], len(pre)) + return nil, 0, 0, fmt.Errorf("cannot parse address to order index key: unable to determine address from [%d, %d, ...(length %d)]", + pre[0], pre[1], len(pre)) } } - return addr, orderID, nil + + return addr, orderTypeByte, orderID, nil } // indexPrefixAssetToOrder creates the prefix for the asset to order index entries with some extra space for the rest. @@ -714,18 +784,26 @@ func ParseIndexKeyAssetToOrder(key []byte) (string, byte, uint64, error) { return "", 0, 0, fmt.Errorf("cannot parse asset to order key: only has %d bytes, expected at least 8", len(key)) } unparsed, orderIDBz := key[:len(key)-8], key[len(key)-8:] - var denom string - var typeByte byte orderID, _ := uint64FromBz(orderIDBz) + + var denom string + var orderTypeByte byte + if len(unparsed) > 0 { - typeByte = unparsed[len(unparsed)-1] + orderTypeByte = unparsed[len(unparsed)-1] unparsed = unparsed[:len(unparsed)-1] + if !bytes.Contains(OrderKeyTypeBytes, []byte{orderTypeByte}) { + return "", 0, 0, fmt.Errorf("cannot parse asset to order key: unknown order type byte %#x, expected one of %#v", + orderTypeByte, OrderKeyTypeBytes) + } } + if len(unparsed) > 0 { if unparsed[0] == KeyTypeAssetToOrderIndex { unparsed = unparsed[1:] } denom = string(unparsed) } - return denom, typeByte, orderID, nil + + return denom, orderTypeByte, orderID, nil } diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 3154ef2fed..b5d18cfc64 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -2,6 +2,7 @@ package keeper_test import ( "bytes" + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -124,6 +125,11 @@ func TestKeyTypeUniqueness(t *testing.T) { } }) } + + t.Run("OrderKeyTypeBytes", func(t *testing.T) { + expected := []byte{keeper.OrderKeyTypeAsk, keeper.OrderKeyTypeBid} + assert.Equalf(t, expected, keeper.OrderKeyTypeBytes, "OrderKeyTypeBytes") + }) } func TestParseLengthPrefixedAddr(t *testing.T) { @@ -2815,7 +2821,7 @@ func TestMakeKeyOrder(t *testing.T) { return keeper.MakeKeyOrder(tc.orderID) }, expected: tc.expected, - expPrefixes: []expectedPrefix{{name: "", value: keeper.GetKeyPrefixOrder()}}, + expPrefixes: []expectedPrefix{{name: "GetKeyPrefixOrder", value: keeper.GetKeyPrefixOrder()}}, } checkKey(t, ktc, "MakeKeyOrder(%d)", tc.orderID) }) @@ -2929,73 +2935,223 @@ func TestGetIndexKeyPrefixMarketToOrder(t *testing.T) { } } -func TestMakeIndexKeyMarketToOrder(t *testing.T) { +func TestGetIndexKeyPrefixMarketToOrderAsks(t *testing.T) { + orderTypeKey := keeper.OrderKeyTypeAsk + tests := []struct { name string marketID uint32 - orderID uint64 expected []byte }{ { - name: "market 0 order 0", + name: "market id 0", marketID: 0, - orderID: 0, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, orderTypeKey}, }, { - name: "market 0 order 1", - marketID: 0, - orderID: 1, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 1, orderTypeKey}, + }, + { + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 255, orderTypeKey}, + }, + { + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 1, 0, orderTypeKey}, + }, + { + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 1, 0, 0, orderTypeKey}, + }, + { + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 0, 0, 0, orderTypeKey}, + }, + { + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 1, 1, 1, orderTypeKey}, + }, + { + name: "market id 4,294,967,295", + marketID: 4_294_967_295, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 255, 255, 255, 255, orderTypeKey}, }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.GetIndexKeyPrefixMarketToOrderAsks(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + { + name: "GetIndexKeyPrefixMarketToOrder", + value: keeper.GetIndexKeyPrefixMarketToOrder(tc.marketID), + }, + }, + } + checkKey(t, ktc, "GetIndexKeyPrefixMarketToOrderAsks(%d)", tc.marketID) + }) + } +} + +func TestGetIndexKeyPrefixMarketToOrderBids(t *testing.T) { + orderTypeKey := keeper.OrderKeyTypeBid + + tests := []struct { + name string + marketID uint32 + expected []byte + }{ { - name: "market 0 order 72,340,172,838,076,673", + name: "market id 0", marketID: 0, - orderID: 72_340_172_838_076_673, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, orderTypeKey}, }, { - name: "market 2 order 0", - marketID: 2, - orderID: 0, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}, + name: "market id 1", + marketID: 1, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 1, orderTypeKey}, }, { - name: "market 2 order 1", - marketID: 2, - orderID: 1, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1}, + name: "market id 255", + marketID: 255, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 255, orderTypeKey}, }, { - name: "market 2 order 72,340,172,838,076,673", - marketID: 2, - orderID: 72_340_172_838_076_673, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1}, + name: "market id 256", + marketID: 256, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 1, 0, orderTypeKey}, }, { - name: "market 33,686,018 order 0", - marketID: 33_686_018, - orderID: 0, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0}, + name: "market id 65_536", + marketID: 65_536, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 1, 0, 0, orderTypeKey}, }, { - name: "market 33,686,018 order 1", - marketID: 33_686_018, - orderID: 1, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1}, + name: "market id 16,777,216", + marketID: 16_777_216, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 0, 0, 0, orderTypeKey}, }, { - name: "market 33,686,018 order 72,340,172,838,076,673", - marketID: 33_686_018, - orderID: 72_340_172_838_076_673, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1}, + name: "market id 16,843,009", + marketID: 16_843_009, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 1, 1, 1, orderTypeKey}, }, - { - name: "market max order max", + name: "market id 4,294,967,295", marketID: 4_294_967_295, - orderID: 18_446_744_073_709_551_615, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 255, 255, 255, 255, orderTypeKey}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.GetIndexKeyPrefixMarketToOrderBids(tc.marketID) + }, + expected: tc.expected, + expPrefixes: []expectedPrefix{ + { + name: "GetIndexKeyPrefixMarketToOrder", + value: keeper.GetIndexKeyPrefixMarketToOrder(tc.marketID), + }, + }, + } + checkKey(t, ktc, "GetIndexKeyPrefixMarketToOrderBids(%d)", tc.marketID) + }) + } +} + +func TestMakeIndexKeyMarketToOrder(t *testing.T) { + tests := []struct { + name string + marketID uint32 + orderTypeByte byte + orderID uint64 + expected []byte + }{ + { + name: "market 0 order 0", + marketID: 0, + orderTypeByte: 8, + orderID: 0, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "market 0 order 1", + marketID: 0, + orderTypeByte: 7, + orderID: 1, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 1}, + }, + { + name: "market 0 order 72,340,172,838,076,673", + marketID: 0, + orderTypeByte: 255, + orderID: 72_340_172_838_076_673, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 255, 1, 1, 1, 1, 1, 1, 1, 1}, + }, + { + name: "market 2 order 0", + marketID: 2, + orderTypeByte: 0, + orderID: 0, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "market 2 order 1", + marketID: 2, + orderTypeByte: 1, + orderID: 1, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1}, + }, + { + name: "market 2 order 72,340,172,838,076,673", + marketID: 2, + orderTypeByte: 10, + orderID: 72_340_172_838_076_673, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 10, 1, 1, 1, 1, 1, 1, 1, 1}, + }, + { + name: "market 33,686,018 order 0", + marketID: 33_686_018, + orderTypeByte: 55, + orderID: 0, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 55, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "market 33,686,018 order 1", + marketID: 33_686_018, + orderTypeByte: 123, + orderID: 1, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 123, 0, 0, 0, 0, 0, 0, 0, 1}, + }, + { + name: "market 33,686,018 order 72,340,172,838,076,673", + marketID: 33_686_018, + orderTypeByte: keeper.OrderKeyTypeAsk, + orderID: 72_340_172_838_076_673, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, + keeper.OrderKeyTypeAsk, 1, 1, 1, 1, 1, 1, 1, 1}, + }, + { + name: "market max order max", + marketID: 4_294_967_295, + orderTypeByte: keeper.OrderKeyTypeBid, + orderID: 18_446_744_073_709_551_615, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 255, 255, 255, 255, + keeper.OrderKeyTypeBid, 255, 255, 255, 255, 255, 255, 255, 255}, }, } @@ -3003,11 +3159,11 @@ func TestMakeIndexKeyMarketToOrder(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeIndexKeyMarketToOrder(tc.marketID, tc.orderID) + return keeper.MakeIndexKeyMarketToOrder(tc.marketID, tc.orderTypeByte, tc.orderID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "", value: keeper.GetIndexKeyPrefixMarketToOrder(tc.marketID)}, + {name: "GetIndexKeyPrefixMarketToOrder", value: keeper.GetIndexKeyPrefixMarketToOrder(tc.marketID)}, }, } checkKey(t, ktc, "MakeIndexKeyMarketToOrder(%d, %d)", tc.marketID, tc.orderID) @@ -3016,96 +3172,165 @@ func TestMakeIndexKeyMarketToOrder(t *testing.T) { } func TestParseIndexKeyMarketToOrder(t *testing.T) { + keyAsks, keyBids := keeper.OrderKeyTypeAsk, keeper.OrderKeyTypeBid + badLenErr := func(actualLength int) string { + return fmt.Sprintf("cannot parse market to order key: length %d, expected 8, 9, 13, or 14", actualLength) + } + tests := []struct { - name string - key []byte - expMarketID uint32 - expOrderID uint64 - expErr string + name string + key []byte + expMarketID uint32 + expOrderTypeByte byte + expOrderID uint64 + expErr string }{ { name: "nil key", key: nil, - expErr: "cannot parse market to order key: length 0, expected 8, 12, or 13", + expErr: badLenErr(0), }, { name: "empty key", key: []byte{}, - expErr: "cannot parse market to order key: length 0, expected 8, 12, or 13", + expErr: badLenErr(0), }, { name: "7 bytes", key: []byte{1, 2, 3, 4, 5, 6, 7}, - expErr: "cannot parse market to order key: length 7, expected 8, 12, or 13", - }, - { - name: "9 bytes", - key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, - expErr: "cannot parse market to order key: length 9, expected 8, 12, or 13", + expErr: badLenErr(7), }, { name: "10 bytes", key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - expErr: "cannot parse market to order key: length 10, expected 8, 12, or 13", + expErr: badLenErr(10), }, { - name: "11 bytes", - key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, - expErr: "cannot parse market to order key: length 11, expected 8, 12, or 13", + name: "12 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + expErr: badLenErr(12), }, { - name: "14 bytes", - key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, - expErr: "cannot parse market to order key: length 14, expected 8, 12, or 13", + name: "15 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + expErr: badLenErr(15), }, { - name: "8 bytes order id 0", + name: "order id 0", key: []byte{0, 0, 0, 0, 0, 0, 0, 0}, expOrderID: 0, }, { - name: "8 bytes order id 1", + name: "order id 1", key: []byte{0, 0, 0, 0, 0, 0, 0, 1}, expMarketID: 0, expOrderID: 1, }, { - name: "8 bytes order id 72,623,859,790,382,856", + name: "order id 72,623,859,790,382,856", key: []byte{1, 2, 3, 4, 5, 6, 7, 8}, expOrderID: 72_623_859_790_382_856, }, { - name: "12 bytes market id 1 order id 1", - key: []byte{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, - expMarketID: 1, - expOrderID: 1, + name: "ask order id 0", + key: []byte{keyAsks, 0, 0, 0, 0, 0, 0, 0, 0}, + expOrderTypeByte: keyAsks, + expOrderID: 0, }, { - name: "12 bytes market id 16,843,009 order id 144,680,345,676,153,346", - key: []byte{1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2}, - expMarketID: 16_843_009, - expOrderID: 144_680_345_676_153_346, + name: "ask order id 1", + key: []byte{keyAsks, 0, 0, 0, 0, 0, 0, 0, 1}, + expOrderTypeByte: keyAsks, + expOrderID: 1, }, { - name: "13 bytes market id 1 order id 1", - key: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, - expMarketID: 1, - expOrderID: 1, + name: "ask order id 72,623,859,790,382,856", + key: []byte{keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, + expOrderTypeByte: keyAsks, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "bid order id 0", + key: []byte{keyBids, 0, 0, 0, 0, 0, 0, 0, 0}, + expOrderTypeByte: keyBids, + expOrderID: 0, + }, + { + name: "bid order id 1", + key: []byte{keyBids, 0, 0, 0, 0, 0, 0, 0, 1}, + expOrderTypeByte: keyBids, + expOrderID: 1, + }, + { + name: "bid order id 72,623,859,790,382,856", + key: []byte{keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, + expOrderTypeByte: keyBids, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "market id 1 ask order id 1", + key: []byte{0, 0, 0, 1, keyAsks, 0, 0, 0, 0, 0, 0, 0, 1}, + expMarketID: 1, + expOrderTypeByte: keyAsks, + expOrderID: 1, + }, + { + name: "market id 16,843,009 ask order id 144,680,345,676,153,346", + key: []byte{1, 1, 1, 1, keyAsks, 2, 2, 2, 2, 2, 2, 2, 2}, + expMarketID: 16_843_009, + expOrderTypeByte: keyAsks, + expOrderID: 144_680_345_676_153_346, + }, + { + name: "market id 1 bid order id 1", + key: []byte{0, 0, 0, 1, keyBids, 0, 0, 0, 0, 0, 0, 0, 1}, + expMarketID: 1, + expOrderTypeByte: keyBids, + expOrderID: 1, + }, + { + name: "market id 16,843,009 bid order id 144,680,345,676,153,346", + key: []byte{1, 1, 1, 1, keyBids, 2, 2, 2, 2, 2, 2, 2, 2}, + expMarketID: 16_843_009, + expOrderTypeByte: keyBids, + expOrderID: 144_680_345_676_153_346, + }, + { + name: "type, market id 1 ask order id 1", + key: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 1, keyAsks, 0, 0, 0, 0, 0, 0, 0, 1}, + expMarketID: 1, + expOrderTypeByte: keyAsks, + expOrderID: 1, + }, + { + name: "type, market id 16,843,009 ask order id 144,680,345,676,153,346", + key: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 1, 1, 1, keyAsks, 2, 2, 2, 2, 2, 2, 2, 2}, + expMarketID: 16_843_009, + expOrderTypeByte: keyAsks, + expOrderID: 144_680_345_676_153_346, }, { - name: "13 bytes market id 16,843,009 order id 144,680,345,676,153,346", - key: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2}, - expMarketID: 16_843_009, - expOrderID: 144_680_345_676_153_346, + name: "type, market id 1 bid order id 1", + key: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 1, keyBids, 0, 0, 0, 0, 0, 0, 0, 1}, + expMarketID: 1, + expOrderTypeByte: keyBids, + expOrderID: 1, }, { - name: "13 bytes first byte too high", - key: []byte{0x4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, + name: "type, market id 16,843,009 bid order id 144,680,345,676,153,346", + key: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 1, 1, 1, keyBids, 2, 2, 2, 2, 2, 2, 2, 2}, + expMarketID: 16_843_009, + expOrderTypeByte: keyBids, + expOrderID: 144_680_345_676_153_346, + }, + { + name: "type byte too high", + key: []byte{0x4, 0, 0, 0, 1, keyBids, 0, 0, 0, 0, 0, 0, 0, 1}, expErr: "cannot parse market to order key: unknown type byte 0x4, expected 0x3", }, { - name: "13 bytes first byte too low", - key: []byte{0x2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, + name: "type byte too low", + key: []byte{0x2, 0, 0, 0, 1, keyAsks, 0, 0, 0, 0, 0, 0, 0, 1}, expErr: "cannot parse market to order key: unknown type byte 0x2, expected 0x3", }, } @@ -3113,14 +3338,16 @@ func TestParseIndexKeyMarketToOrder(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { var marketID uint32 + var orderTypeByte byte var orderID uint64 var err error testFunc := func() { - marketID, orderID, err = keeper.ParseIndexKeyMarketToOrder(tc.key) + marketID, orderTypeByte, orderID, err = keeper.ParseIndexKeyMarketToOrder(tc.key) } require.NotPanics(t, testFunc, "ParseIndexKeyMarketToOrder(%v)", tc.key) assertions.AssertErrorValue(t, err, tc.expErr, "ParseIndexKeyMarketToOrder(%v) error", tc.key) assert.Equal(t, tc.expMarketID, marketID, "ParseIndexKeyMarketToOrder(%v) market id", tc.key) + assert.Equal(t, tc.expOrderTypeByte, orderTypeByte, "ParseIndexKeyMarketToOrder(%v) order type byte", tc.key) assert.Equal(t, tc.expOrderID, orderID, "ParseIndexKeyMarketToOrder(%v) order id", tc.key) }) } @@ -3179,11 +3406,12 @@ func TestGetIndexKeyPrefixAddressToOrder(t *testing.T) { } } -func TestMakeIndexKeyAddressToOrder(t *testing.T) { +func TestGetIndexKeyPrefixAddressToOrderAsks(t *testing.T) { + orderTypeKey := keeper.OrderKeyTypeAsk + tests := []struct { name string addr sdk.AccAddress - orderID uint64 expected []byte expPanic string }{ @@ -3203,53 +3431,202 @@ func TestMakeIndexKeyAddressToOrder(t *testing.T) { expPanic: "address length should be max 255 bytes, got 256: unknown address", }, { - name: "5 byte addr order 1", - addr: sdk.AccAddress("abcde"), - orderID: 1, + name: "5 byte addr", + addr: sdk.AccAddress("abcde"), expected: concatBz( []byte{keeper.KeyTypeAddressToOrderIndex, 5}, []byte("abcde"), - []byte{0, 0, 0, 0, 0, 0, 0, 1}, + []byte{orderTypeKey}, ), }, { - name: "20 byte addr order 1", - addr: sdk.AccAddress("abcdefghijklmnopqrst"), - orderID: 1, + name: "20 byte addr", + addr: sdk.AccAddress("abcdefghijklmnopqrst"), expected: concatBz( []byte{keeper.KeyTypeAddressToOrderIndex, 20}, []byte("abcdefghijklmnopqrst"), - []byte{0, 0, 0, 0, 0, 0, 0, 1}, + []byte{orderTypeKey}, ), }, { - name: "32 byte addr order 1", - addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), - orderID: 1, + name: "32 byte addr", + addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), expected: concatBz( []byte{keeper.KeyTypeAddressToOrderIndex, 32}, []byte("abcdefghijklmnopqrstuvwxyzABCDEF"), - []byte{0, 0, 0, 0, 0, 0, 0, 1}, + []byte{orderTypeKey}, ), }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.GetIndexKeyPrefixAddressToOrderAsks(tc.addr) + }, + expected: tc.expected, + expPanic: tc.expPanic, + } + if len(tc.addr) > 0 && len(tc.addr) <= 255 { + ktc.expPrefixes = []expectedPrefix{ + {name: "GetIndexKeyPrefixAddressToOrder", value: keeper.GetIndexKeyPrefixAddressToOrder(tc.addr)}, + } + } + checkKey(t, ktc, "GetIndexKeyPrefixAddressToOrderAsks(%s)", string(tc.addr)) + }) + } +} + +func TestGetIndexKeyPrefixAddressToOrderBids(t *testing.T) { + orderTypeKey := keeper.OrderKeyTypeBid + + tests := []struct { + name string + addr sdk.AccAddress + expected []byte + expPanic string + }{ + { + name: "nil addr", + addr: nil, + expPanic: "empty address not allowed", + }, + { + name: "empty addr", + addr: sdk.AccAddress{}, + expPanic: "empty address not allowed", + }, + { + name: "256 byte addr", + addr: sdk.AccAddress(bytes.Repeat([]byte{'P'}, 256)), + expPanic: "address length should be max 255 bytes, got 256: unknown address", + }, + { + name: "5 byte addr", + addr: sdk.AccAddress("abcde"), + expected: concatBz( + []byte{keeper.KeyTypeAddressToOrderIndex, 5}, + []byte("abcde"), + []byte{orderTypeKey}, + ), + }, + { + name: "20 byte addr", + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + expected: concatBz( + []byte{keeper.KeyTypeAddressToOrderIndex, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{orderTypeKey}, + ), + }, + { + name: "32 byte addr", + addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), + expected: concatBz( + []byte{keeper.KeyTypeAddressToOrderIndex, 32}, + []byte("abcdefghijklmnopqrstuvwxyzABCDEF"), + []byte{orderTypeKey}, + ), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.GetIndexKeyPrefixAddressToOrderBids(tc.addr) + }, + expected: tc.expected, + expPanic: tc.expPanic, + } + if len(tc.addr) > 0 && len(tc.addr) <= 255 { + ktc.expPrefixes = []expectedPrefix{ + {name: "GetIndexKeyPrefixAddressToOrder", value: keeper.GetIndexKeyPrefixAddressToOrder(tc.addr)}, + } + } + checkKey(t, ktc, "GetIndexKeyPrefixAddressToOrderBids(%s)", string(tc.addr)) + }) + } +} + +func TestMakeIndexKeyAddressToOrder(t *testing.T) { + tests := []struct { + name string + addr sdk.AccAddress + orderTypeByte byte + orderID uint64 + expected []byte + expPanic string + }{ + { + name: "nil addr", + addr: nil, + expPanic: "empty address not allowed", + }, + { + name: "empty addr", + addr: sdk.AccAddress{}, + expPanic: "empty address not allowed", + }, { - name: "20 byte addr order 5", - addr: sdk.AccAddress("ABCDEFGHIJKLMNOPQRST"), - orderID: 5, + name: "256 byte addr", + addr: sdk.AccAddress(bytes.Repeat([]byte{'P'}, 256)), + expPanic: "address length should be max 255 bytes, got 256: unknown address", + }, + { + name: "5 byte addr order 1", + addr: sdk.AccAddress("abcde"), + orderTypeByte: keeper.OrderKeyTypeAsk, + orderID: 1, + expected: concatBz( + []byte{keeper.KeyTypeAddressToOrderIndex, 5}, + []byte("abcde"), + []byte{keeper.OrderKeyTypeAsk, 0, 0, 0, 0, 0, 0, 0, 1}, + ), + }, + { + name: "20 byte addr order 1", + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + orderTypeByte: keeper.OrderKeyTypeBid, + orderID: 1, + expected: concatBz( + []byte{keeper.KeyTypeAddressToOrderIndex, 20}, + []byte("abcdefghijklmnopqrst"), + []byte{keeper.OrderKeyTypeBid, 0, 0, 0, 0, 0, 0, 0, 1}, + ), + }, + { + name: "32 byte addr order 1", + addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), + orderTypeByte: 0, + orderID: 1, + expected: concatBz( + []byte{keeper.KeyTypeAddressToOrderIndex, 32}, + []byte("abcdefghijklmnopqrstuvwxyzABCDEF"), + []byte{0, 0, 0, 0, 0, 0, 0, 0, 1}, + ), + }, + { + name: "20 byte addr order 5", + addr: sdk.AccAddress("ABCDEFGHIJKLMNOPQRST"), + orderTypeByte: 255, + orderID: 5, expected: concatBz( []byte{keeper.KeyTypeAddressToOrderIndex, 20}, []byte("ABCDEFGHIJKLMNOPQRST"), - []byte{0, 0, 0, 0, 0, 0, 0, 5}, + []byte{255, 0, 0, 0, 0, 0, 0, 0, 5}, ), }, { - name: "20 byte addr order 72,623,859,790,382,856", - addr: sdk.AccAddress("ABCDEFGHIJKLMNOPQRST"), - orderID: 72_623_859_790_382_856, + name: "20 byte addr order 72,623,859,790,382,856", + addr: sdk.AccAddress("ABCDEFGHIJKLMNOPQRST"), + orderTypeByte: 123, + orderID: 72_623_859_790_382_856, expected: concatBz( []byte{keeper.KeyTypeAddressToOrderIndex, 20}, []byte("ABCDEFGHIJKLMNOPQRST"), - []byte{1, 2, 3, 4, 5, 6, 7, 8}, + []byte{123, 1, 2, 3, 4, 5, 6, 7, 8}, ), }, } @@ -3258,14 +3635,14 @@ func TestMakeIndexKeyAddressToOrder(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeIndexKeyAddressToOrder(tc.addr, tc.orderID) + return keeper.MakeIndexKeyAddressToOrder(tc.addr, tc.orderTypeByte, tc.orderID) }, expected: tc.expected, expPanic: tc.expPanic, } if len(tc.expPanic) == 0 { ktc.expPrefixes = []expectedPrefix{ - {name: "", value: keeper.GetIndexKeyPrefixAddressToOrder(tc.addr)}, + {name: "GetIndexKeyPrefixAddressToOrder", value: keeper.GetIndexKeyPrefixAddressToOrder(tc.addr)}, } } checkKey(t, ktc, "MakeIndexKeyAddressToOrder(%s, %d)", string(tc.addr), tc.orderID) @@ -3274,12 +3651,15 @@ func TestMakeIndexKeyAddressToOrder(t *testing.T) { } func TestParseIndexKeyAddressToOrder(t *testing.T) { + keyAsks, keyBids := keeper.OrderKeyTypeAsk, keeper.OrderKeyTypeBid + tests := []struct { - name string - key []byte - expAddr sdk.AccAddress - expOrderID uint64 - expErr string + name string + key []byte + expAddr sdk.AccAddress + expOrderTypeByte byte + expOrderID uint64 + expErr string }{ { name: "nil key", @@ -3307,83 +3687,136 @@ func TestParseIndexKeyAddressToOrder(t *testing.T) { expOrderID: 72_623_859_790_382_856, }, { - name: "9 bytes", - key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, - expErr: "cannot parse address to order index key: unable to determine address from single byte 0x1", + name: "ask order id 72,623,859,790,382,856", + key: []byte{keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, + expOrderTypeByte: keyAsks, + expOrderID: 72_623_859_790_382_856, }, { - name: "1 byte address order id 72,623,859,790,382,856", - key: []byte{1, 55, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{55}, - expOrderID: 72_623_859_790_382_856, + name: "bid order id 72,623,859,790,382,856", + key: []byte{keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, + expOrderTypeByte: keyBids, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "order type byte 99 and order 72,623,859,790,382,856", + key: []byte{99, 1, 2, 3, 4, 5, 6, 7, 8}, + expErr: "cannot parse address to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", + }, + { + name: "1 byte address ask order id 72,623,859,790,382,856", + key: []byte{1, 55, keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55}, + expOrderTypeByte: keyAsks, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "1 byte address bid order id 72,623,859,790,382,856", + key: []byte{1, 55, keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55}, + expOrderTypeByte: keyBids, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "1 byte address order type byte 99 order id 72,623,859,790,382,856", + key: []byte{1, 55, 99, 1, 2, 3, 4, 5, 6, 7, 8}, + expErr: "cannot parse address to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", }, { name: "length byte 2 but only 1 byte after it", - key: []byte{2, 55, 1, 2, 3, 4, 5, 6, 7, 8}, + key: []byte{2, 55, keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [2, 55, ...(length 2)]", }, { name: "length byte 2 but 3 bytes after it", - key: []byte{2, 55, 56, 57, 1, 2, 3, 4, 5, 6, 7, 8}, + key: []byte{2, 55, 56, 57, keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [2, 55, ...(length 4)]", }, { - name: "length byte 4 order id 72,623,859,790,382,856", - key: []byte{4, 55, 56, 57, 58, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{55, 56, 57, 58}, - expOrderID: 72_623_859_790_382_856, + name: "length byte 4 ask order id 72,623,859,790,382,856", + key: []byte{4, 55, 56, 57, 58, keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55, 56, 57, 58}, + expOrderTypeByte: keyAsks, + expOrderID: 72_623_859_790_382_856, }, { name: "length byte 20 but only 19 byte after it", key: []byte{20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, - 1, 2, 3, 4, 5, 6, 7, 8}, + keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [20, 101, ...(length 20)]", }, { name: "length byte 20 but 21 byte after it", key: []byte{20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, - 1, 2, 3, 4, 5, 6, 7, 8}, + keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [20, 101, ...(length 22)]", }, { - name: "20 byte address order id 72,623,859,790,382,856", + name: "20 byte address ask order id 72,623,859,790,382,856", key: []byte{20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, - 1, 2, 3, 4, 5, 6, 7, 8}, + keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, expAddr: sdk.AccAddress{101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120}, - expOrderID: 72_623_859_790_382_856, + expOrderTypeByte: keyAsks, + expOrderID: 72_623_859_790_382_856, }, { - name: "with type byte: 1 byte address order id 72,623,859,790,382,856", - key: []byte{keeper.KeyTypeAddressToOrderIndex, 1, 55, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{55}, - expOrderID: 72_623_859_790_382_856, + name: "20 byte address bid order id 72,623,859,790,382,856", + key: []byte{20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120}, + expOrderTypeByte: keyBids, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "with type byte: 1 byte address ask order id 72,623,859,790,382,856", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 1, 55, keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55}, + expOrderTypeByte: keyAsks, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "with type byte: 1 byte address bid order id 72,623,859,790,382,856", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 1, 55, keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55}, + expOrderTypeByte: keyBids, + expOrderID: 72_623_859_790_382_856, }, { name: "with type byte: length byte 2 but only 1 byte after it", - key: []byte{keeper.KeyTypeAddressToOrderIndex, 2, 55, 1, 2, 3, 4, 5, 6, 7, 8}, + key: []byte{keeper.KeyTypeAddressToOrderIndex, 2, 55, keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [4, 2, ...(length 3)]", }, { name: "with type byte: length byte 2 but 5 bytes after it", - key: []byte{keeper.KeyTypeAddressToOrderIndex, 2, 55, 56, 57, 58, 1, 2, 3, 4, 5, 6, 7, 8}, + key: []byte{keeper.KeyTypeAddressToOrderIndex, 2, 55, 56, 57, 58, keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [4, 2, ...(length 6)]", }, { - name: "with type byte: length byte 4 order id 72,623,859,790,382,856", - key: []byte{keeper.KeyTypeAddressToOrderIndex, 4, 55, 56, 57, 58, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{55, 56, 57, 58}, - expOrderID: 72_623_859_790_382_856, + name: "with type byte: length byte 4 ask order id 72,623,859,790,382,856", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 4, 55, 56, 57, 58, keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55, 56, 57, 58}, + expOrderTypeByte: keyAsks, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "with type byte: length byte 4 bid order id 72,623,859,790,382,856", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 4, 55, 56, 57, 58, keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55, 56, 57, 58}, + expOrderTypeByte: keyBids, + expOrderID: 72_623_859_790_382_856, }, { name: "with type byte: length byte 20 but only 19 byte after it", key: []byte{keeper.KeyTypeAddressToOrderIndex, 20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, - 1, 2, 3, 4, 5, 6, 7, 8}, + keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [4, 20, ...(length 21)]", }, { @@ -3391,32 +3824,46 @@ func TestParseIndexKeyAddressToOrder(t *testing.T) { key: []byte{keeper.KeyTypeAddressToOrderIndex, 20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, - 1, 2, 3, 4, 5, 6, 7, 8}, + keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [4, 20, ...(length 23)]", }, { - name: "with type byte: 20 byte address order id 72,623,859,790,382,856", + name: "with type byte: 20 byte address ask order id 72,623,859,790,382,856", key: []byte{keeper.KeyTypeAddressToOrderIndex, 20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, - 1, 2, 3, 4, 5, 6, 7, 8}, + keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, expAddr: sdk.AccAddress{101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120}, - expOrderID: 72_623_859_790_382_856, + expOrderTypeByte: keyAsks, + expOrderID: 72_623_859_790_382_856, + }, + { + name: "with type byte: 20 byte address ask order id 72,623,859,790,382,856", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 20, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120}, + expOrderTypeByte: keyBids, + expOrderID: 72_623_859_790_382_856, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { var addr sdk.AccAddress + var orderTypeByte byte var orderID uint64 var err error testFunc := func() { - addr, orderID, err = keeper.ParseIndexKeyAddressToOrder(tc.key) + addr, orderTypeByte, orderID, err = keeper.ParseIndexKeyAddressToOrder(tc.key) } require.NotPanics(t, testFunc, "ParseIndexKeyAddressToOrder(%v)", tc.key) assertions.AssertErrorValue(t, err, tc.expErr, "ParseIndexKeyAddressToOrder(%v) error", tc.key) assert.Equal(t, tc.expAddr, addr, "ParseIndexKeyAddressToOrder(%v) address", tc.key) + assert.Equal(t, tc.expOrderTypeByte, orderTypeByte, "ParseIndexKeyAddressToOrder(%v) order type byte", tc.key) assert.Equal(t, tc.expOrderID, orderID, "ParseIndexKeyAddressToOrder(%v) order id", tc.key) }) } @@ -3709,6 +4156,8 @@ func TestMakeIndexKeyAssetToOrder(t *testing.T) { } func TestParseIndexKeyAssetToOrder(t *testing.T) { + keyAsks, keyBids := keeper.OrderKeyTypeAsk, keeper.OrderKeyTypeBid + tests := []struct { name string key []byte @@ -3743,43 +4192,110 @@ func TestParseIndexKeyAssetToOrder(t *testing.T) { expOrderID: 578_437_695_752_307_201, }, { - name: "order type byte 99 order id 578,437,695,752,307,201", - key: []byte{99, 8, 7, 6, 5, 4, 3, 2, 1}, - expTypeByte: 99, + name: "asks order id 578,437,695,752,307,201", + key: []byte{keyAsks, 8, 7, 6, 5, 4, 3, 2, 1}, + expTypeByte: keyAsks, expOrderID: 578_437_695_752_307_201, }, { - name: "nhash order type byte 99 order id 578,437,695,752,307,201", - key: []byte{'n', 'h', 'a', 's', 'h', 99, 8, 7, 6, 5, 4, 3, 2, 1}, + name: "nhash asks order id 578,437,695,752,307,201", + key: []byte{'n', 'h', 'a', 's', 'h', keyAsks, 8, 7, 6, 5, 4, 3, 2, 1}, expDenom: "nhash", - expTypeByte: 99, + expTypeByte: keyAsks, expOrderID: 578_437_695_752_307_201, }, { - name: "hex string order type byte 99 order id 578,437,695,752,307,201", - key: append([]byte(hexString), 99, 8, 7, 6, 5, 4, 3, 2, 1), + name: "hex string asks order id 578,437,695,752,307,201", + key: append([]byte(hexString), keyAsks, 8, 7, 6, 5, 4, 3, 2, 1), expDenom: hexString, - expTypeByte: 99, + expTypeByte: keyAsks, expOrderID: 578_437_695_752_307_201, }, { - name: "with type byte nhash order type byte 99 order id 578,437,695,752,307,201", - key: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', 99, 8, 7, 6, 5, 4, 3, 2, 1}, + name: "with type byte nhash asks order id 578,437,695,752,307,201", + key: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', keyAsks, 8, 7, 6, 5, 4, 3, 2, 1}, expDenom: "nhash", - expTypeByte: 99, + expTypeByte: keyAsks, expOrderID: 578_437_695_752_307_201, }, { - name: "with type byte hex string order type byte 99 order id 578,437,695,752,307,201", + name: "with type byte hex string asks order id 578,437,695,752,307,201", key: concatBz( []byte{keeper.KeyTypeAssetToOrderIndex}, []byte(hexString), - []byte{99, 8, 7, 6, 5, 4, 3, 2, 1}, + []byte{keyAsks, 8, 7, 6, 5, 4, 3, 2, 1}, ), expDenom: hexString, - expTypeByte: 99, + expTypeByte: keyAsks, expOrderID: 578_437_695_752_307_201, }, + { + name: "bids order id 578,437,695,752,307,201", + key: []byte{keyBids, 8, 7, 6, 5, 4, 3, 2, 1}, + expTypeByte: keyBids, + expOrderID: 578_437_695_752_307_201, + }, + { + name: "nhash bids order id 578,437,695,752,307,201", + key: []byte{'n', 'h', 'a', 's', 'h', keyBids, 8, 7, 6, 5, 4, 3, 2, 1}, + expDenom: "nhash", + expTypeByte: keyBids, + expOrderID: 578_437_695_752_307_201, + }, + { + name: "hex string bids order id 578,437,695,752,307,201", + key: append([]byte(hexString), keyBids, 8, 7, 6, 5, 4, 3, 2, 1), + expDenom: hexString, + expTypeByte: keyBids, + expOrderID: 578_437_695_752_307_201, + }, + { + name: "with type byte nhash bids order id 578,437,695,752,307,201", + key: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', keyBids, 8, 7, 6, 5, 4, 3, 2, 1}, + expDenom: "nhash", + expTypeByte: keyBids, + expOrderID: 578_437_695_752_307_201, + }, + { + name: "with type byte hex string bids order id 578,437,695,752,307,201", + key: concatBz( + []byte{keeper.KeyTypeAssetToOrderIndex}, + []byte(hexString), + []byte{keyBids, 8, 7, 6, 5, 4, 3, 2, 1}, + ), + expDenom: hexString, + expTypeByte: keyBids, + expOrderID: 578_437_695_752_307_201, + }, + { + name: "order type byte 99 order id 578,437,695,752,307,201", + key: []byte{99, 8, 7, 6, 5, 4, 3, 2, 1}, + expErr: "cannot parse asset to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", + }, + { + name: "nhash order type byte 99 order id 578,437,695,752,307,201", + key: []byte{'n', 'h', 'a', 's', 'h', 99, 8, 7, 6, 5, 4, 3, 2, 1}, + expErr: "cannot parse asset to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", + }, + { + name: "hex string order type byte 99 order id 578,437,695,752,307,201", + key: append([]byte(hexString), 99, 8, 7, 6, 5, 4, 3, 2, 1), + expErr: "cannot parse asset to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", + }, + { + name: "with type byte nhash order type byte 99 order id 578,437,695,752,307,201", + key: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', 99, 8, 7, 6, 5, 4, 3, 2, 1}, + expErr: "cannot parse asset to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", + }, + { + name: "with type byte hex string order type byte 99 order id 578,437,695,752,307,201", + key: concatBz( + []byte{keeper.KeyTypeAssetToOrderIndex}, + []byte(hexString), + []byte{99, 8, 7, 6, 5, 4, 3, 2, 1}, + ), + expErr: "cannot parse asset to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", + }, } for _, tc := range tests { diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 8da7802638..c9d590a29d 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -83,8 +83,8 @@ func (k Keeper) parseOrderStoreKeyValue(key, value []byte) (*exchange.Order, err return k.parseOrderStoreValue(orderID, value) } -// createIndexEntries creates all the key/value index entries for an order. -func createIndexEntries(order exchange.Order) []sdk.KVPair { +// createIndexKeys creates all the key/value index entries for an order. +func createIndexKeys(order exchange.Order) [][]byte { marketID := order.GetMarketID() orderID := order.GetOrderID() orderTypeByte := order.GetOrderTypeByte() @@ -92,19 +92,10 @@ func createIndexEntries(order exchange.Order) []sdk.KVPair { addr := sdk.MustAccAddressFromBech32(owner) assets := order.GetAssets() - return []sdk.KVPair{ - { - Key: MakeIndexKeyMarketToOrder(marketID, orderID), - Value: []byte{orderTypeByte}, - }, - { - Key: MakeIndexKeyAddressToOrder(addr, orderID), - Value: []byte{orderTypeByte}, - }, - { - Key: MakeIndexKeyAssetToOrder(assets.Denom, orderTypeByte, orderID), - Value: nil, - }, + return [][]byte{ + MakeIndexKeyMarketToOrder(marketID, orderTypeByte, orderID), + MakeIndexKeyAddressToOrder(addr, orderTypeByte, orderID), + MakeIndexKeyAssetToOrder(assets.Denom, orderTypeByte, orderID), } } @@ -131,9 +122,9 @@ func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { isUpdate := store.Has(key) store.Set(key, value) if !isUpdate { - indexEntries := createIndexEntries(order) - for _, entry := range indexEntries { - store.Set(entry.Key, entry.Value) + indexKeys := createIndexKeys(order) + for _, entry := range indexKeys { + store.Set(entry, nil) } // It is assumed that these index entries cannot change over the life of an order. // The only change that is allowed to an order is the assets (due to partial fulfillment). @@ -147,9 +138,9 @@ func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { func deleteAndDeIndexOrder(store sdk.KVStore, order exchange.Order) { key := MakeKeyOrder(order.OrderId) store.Delete(key) - indexEntries := createIndexEntries(order) - for _, entry := range indexEntries { - store.Delete(entry.Key) + indexKeys := createIndexKeys(order) + for _, entry := range indexKeys { + store.Delete(entry) } } @@ -201,29 +192,71 @@ func (k Keeper) IterateOrders(ctx sdk.Context, cb func(order *exchange.Order) bo // The callback takes in the order id and order type byte and should return whether to stop iterating. func (k Keeper) IterateMarketOrders(ctx sdk.Context, marketID uint32, cb func(orderID uint64, orderTypeByte byte) bool) { k.iterate(ctx, GetIndexKeyPrefixMarketToOrder(marketID), func(key, value []byte) bool { - if len(value) == 0 { + _, orderTypeByte, orderID, err := ParseIndexKeyMarketToOrder(key) + if err != nil { + return false + } + return cb(orderID, orderTypeByte) + }) +} + +// IterateMarketAskOrders iterates over all ask orders for a market. +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateMarketAskOrders(ctx sdk.Context, marketID uint32, cb func(orderID uint64, orderTypeByte byte) bool) { + k.iterate(ctx, GetIndexKeyPrefixMarketToOrderAsks(marketID), func(key, _ []byte) bool { + _, _, orderID, err := ParseIndexKeyMarketToOrder(key) + if err != nil { return false } - _, orderID, err := ParseIndexKeyMarketToOrder(key) + return cb(orderID, OrderKeyTypeAsk) + }) +} + +// IterateMarketBidOrders iterates over all bid orders for a market. +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateMarketBidOrders(ctx sdk.Context, marketID uint32, cb func(orderID uint64, orderTypeByte byte) bool) { + k.iterate(ctx, GetIndexKeyPrefixMarketToOrderBids(marketID), func(key, _ []byte) bool { + _, _, orderID, err := ParseIndexKeyMarketToOrder(key) if err != nil { return false } - return cb(orderID, value[0]) + return cb(orderID, OrderKeyTypeBid) }) } // IterateAddressOrders iterates over all orders for an address. // The callback takes in the order id and order type byte and should return whether to stop iterating. func (k Keeper) IterateAddressOrders(ctx sdk.Context, addr sdk.AccAddress, cb func(orderID uint64, orderTypeByte byte) bool) { - k.iterate(ctx, GetIndexKeyPrefixAddressToOrder(addr), func(key, value []byte) bool { - if len(value) == 0 { + k.iterate(ctx, GetIndexKeyPrefixAddressToOrder(addr), func(key, _ []byte) bool { + _, orderTypeByte, orderID, err := ParseIndexKeyAddressToOrder(key) + if err != nil { return false } - _, orderID, err := ParseIndexKeyAddressToOrder(key) + return cb(orderID, orderTypeByte) + }) +} + +// IterateAddressAskOrders iterates over all ask orders for an address. +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateAddressAskOrders(ctx sdk.Context, addr sdk.AccAddress, cb func(orderID uint64, orderTypeByte byte) bool) { + k.iterate(ctx, GetIndexKeyPrefixAddressToOrderAsks(addr), func(key, _ []byte) bool { + _, _, orderID, err := ParseIndexKeyAddressToOrder(key) + if err != nil { + return false + } + return cb(orderID, OrderKeyTypeAsk) + }) +} + +// IterateAddressBidOrders iterates over all bid orders for an address. +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateAddressBidOrders(ctx sdk.Context, addr sdk.AccAddress, cb func(orderID uint64, orderTypeByte byte) bool) { + k.iterate(ctx, GetIndexKeyPrefixAddressToOrderBids(addr), func(key, _ []byte) bool { + _, _, orderID, err := ParseIndexKeyAddressToOrder(key) if err != nil { return false } - return cb(orderID, value[0]) + return cb(orderID, OrderKeyTypeBid) }) } @@ -240,26 +273,26 @@ func (k Keeper) IterateAssetOrders(ctx sdk.Context, assetDenom string, cb func(o } // IterateAssetAskOrders iterates over all ask orders for a given asset denom. -// The callback takes in the order id and should return whether to stop iterating. -func (k Keeper) IterateAssetAskOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64) bool) { +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateAssetAskOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64, orderTypeByte byte) bool) { k.iterate(ctx, GetIndexKeyPrefixAssetToOrderAsks(assetDenom), func(key, _ []byte) bool { _, _, orderID, err := ParseIndexKeyAssetToOrder(key) if err != nil { return false } - return cb(orderID) + return cb(orderID, OrderKeyTypeAsk) }) } // IterateAssetBidOrders iterates over all bid orders for a given asset denom. -// The callback takes in the order id and should return whether to stop iterating. -func (k Keeper) IterateAssetBidOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64) bool) { +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateAssetBidOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64, orderTypeByte byte) bool) { k.iterate(ctx, GetIndexKeyPrefixAssetToOrderBids(assetDenom), func(key, _ []byte) bool { _, _, orderID, err := ParseIndexKeyAssetToOrder(key) if err != nil { return false } - return cb(orderID) + return cb(orderID, OrderKeyTypeBid) }) } From 073c5ce1cf073643b8a4c9e8b01878a629be7b91 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 2 Oct 2023 10:20:28 -0600 Subject: [PATCH 214/309] Revert "[1658]: Refactor the Market to order and address to order indexes to include the order type byte." This reverts commit 1cb40db96dcb6569e4b4ae914374a3bfe59dcc9e. --- x/exchange/keeper/grpc_query.go | 4 +- x/exchange/keeper/keys.go | 144 ++---- x/exchange/keeper/keys_test.go | 846 +++++++------------------------- x/exchange/keeper/orders.go | 101 ++-- 4 files changed, 234 insertions(+), 861 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index a7a2be8abf..3d96d86378 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -102,7 +102,7 @@ func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.Q resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { // If we can't get the order id from the key, just pretend like it doesn't exist. - _, _, orderID, perr := ParseIndexKeyMarketToOrder(key) + _, orderID, perr := ParseIndexKeyMarketToOrder(key) if perr != nil { return false, nil } @@ -143,7 +143,7 @@ func (k QueryServer) QueryGetOwnerOrders(goCtx context.Context, req *exchange.Qu resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { // If we can't get the order id from the key, just pretend like it doesn't exist. - _, _, orderID, perr := ParseIndexKeyAddressToOrder(key) + _, orderID, perr := ParseIndexKeyAddressToOrder(key) if perr != nil { return false, nil } diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index f8dbcff635..796344cfe7 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -58,11 +58,13 @@ import ( // Ask Orders: 0x02 | (8 bytes) => 0x00 | protobuf(AskOrder) // Bid Orders: 0x02 | (8 bytes) => 0x01 | protobuf(BidOrder) // +// TODO[1658]: Refactor the market to order index to have the order type byte before the order id. // A market to order index is maintained with the following format: -// 0x03 | (4 bytes) | | (8 bytes) => nil +// 0x03 | (4 bytes) | (8 bytes) => // +// TODO[1658]: Refactor the address to order index to have the order type byte before the order id. // An address to order index is maintained with the following format: -// 0x04 | len(
) (1 byte) |
| | (8 bytes) => nil +// 0x04 | len(
) (1 byte) |
| (8 bytes) => nil // // An asset type to order index is maintained with the following format: // 0x05 | | (1 byte) | (8 bytes) => nil @@ -115,8 +117,6 @@ const ( RecordSeparator = byte(0x1E) ) -var OrderKeyTypeBytes = []byte{OrderKeyTypeAsk, OrderKeyTypeBid} - // prepKey creates a single byte slice consisting of the type byte and provided byte slice with some extra capacity in the underlying array. // The idea is that you can append(...) to the result of this without it needed a new underlying array. func prepKey(typeByte byte, bz []byte, extraCap int) []byte { @@ -565,77 +565,42 @@ func indexPrefixMarketToOrder(marketID uint32, extraCap int) []byte { return prepKey(KeyTypeMarketToOrderIndex, uint32Bz(marketID), extraCap) } -// indexPrefixMarketToOrderOfType creates the prefix for market to order index entries for the given asset denom and order type. -func indexPrefixMarketToOrderOfType(marketID uint32, orderTypeByte byte, extraCap int) []byte { - rv := indexPrefixMarketToOrder(marketID, 1+extraCap) - rv = append(rv, orderTypeByte) - return rv -} - // GetIndexKeyPrefixMarketToOrder creates the prefix for the market to order index limited ot the given market id. func GetIndexKeyPrefixMarketToOrder(marketID uint32) []byte { return indexPrefixMarketToOrder(marketID, 0) } -// GetIndexKeyPrefixMarketToOrderAsks creates a key prefix for the market to orders limited to the given asset and only ask orders. -func GetIndexKeyPrefixMarketToOrderAsks(marketID uint32) []byte { - return indexPrefixMarketToOrderOfType(marketID, OrderKeyTypeAsk, 0) -} - -// GetIndexKeyPrefixMarketToOrderBids creates a key prefix for the market to orders limited to the given asset and only bid orders. -func GetIndexKeyPrefixMarketToOrderBids(marketID uint32) []byte { - return indexPrefixMarketToOrderOfType(marketID, OrderKeyTypeBid, 0) -} - // MakeIndexKeyMarketToOrder creates the key to use for the market to order index with the given ids. -func MakeIndexKeyMarketToOrder(marketID uint32, orderTypeByte byte, orderID uint64) []byte { - suffix := uint64Bz(orderID) - rv := indexPrefixMarketToOrderOfType(marketID, orderTypeByte, len(suffix)) - rv = append(rv, suffix...) +func MakeIndexKeyMarketToOrder(marketID uint32, orderID uint64) []byte { + rv := indexPrefixMarketToOrder(marketID, 8) + rv = append(rv, uint64Bz(orderID)...) return rv } // ParseIndexKeyMarketToOrder will extract the market id and order id from a market to order index key. // The input can have the following formats: -// - | (4 bytes) | | (8 bytes) -// - (4 bytes) | | (8 bytes) -// - | (8 bytes) +// - | (4 bytes) | (8 bytes) +// - (4 bytes) | (8 bytes) // - (8 bytes) // -// In the case where just the is provided, the returned market id will be 0 and the order type byte will also be 0. -// In the case where just | is provided, the returned market id will be 0. -func ParseIndexKeyMarketToOrder(key []byte) (uint32, byte, uint64, error) { +// In the case where just the is provided, the returned market id will be 0. +func ParseIndexKeyMarketToOrder(key []byte) (uint32, uint64, error) { var marketIDBz, orderIDBz []byte - var orderTypeByteP *byte switch len(key) { case 8: orderIDBz = key - case 9: - orderTypeByteP = &key[0] - orderIDBz = key[1:] - case 13: + case 12: marketIDBz = key[:4] - orderTypeByteP = &key[4] - orderIDBz = key[5:] - case 14: + orderIDBz = key[4:] + case 13: if key[0] != KeyTypeMarketToOrderIndex { - return 0, 0, 0, fmt.Errorf("cannot parse market to order key: unknown type byte %#x, expected %#x", + return 0, 0, fmt.Errorf("cannot parse market to order key: unknown type byte %#x, expected %#x", key[0], KeyTypeMarketToOrderIndex) } marketIDBz = key[1:5] - orderTypeByteP = &key[5] - orderIDBz = key[6:] + orderIDBz = key[5:] default: - return 0, 0, 0, fmt.Errorf("cannot parse market to order key: length %d, expected 8, 9, 13, or 14", len(key)) - } - - var orderTypeByte byte - if orderTypeByteP != nil { - orderTypeByte = *orderTypeByteP - if !bytes.Contains(OrderKeyTypeBytes, []byte{orderTypeByte}) { - return 0, 0, 0, fmt.Errorf("cannot parse market to order key: unknown order type byte %#x, expected one of %#v", - orderTypeByte, OrderKeyTypeBytes) - } + return 0, 0, fmt.Errorf("cannot parse market to order key: length %d, expected 8, 12, or 13", len(key)) } var marketID uint32 @@ -643,7 +608,7 @@ func ParseIndexKeyMarketToOrder(key []byte) (uint32, byte, uint64, error) { marketID, _ = uint32FromBz(marketIDBz) } orderID, _ := uint64FromBz(orderIDBz) - return marketID, orderTypeByte, orderID, nil + return marketID, orderID, nil } // indexPrefixAddressToOrder creates the prefix for the address to order index entries with some extra apace for the rest. @@ -654,71 +619,38 @@ func indexPrefixAddressToOrder(addr sdk.AccAddress, extraCap int) []byte { return prepKey(KeyTypeAddressToOrderIndex, address.MustLengthPrefix(addr), extraCap) } -// indexPrefixAddressToOrderOfType creates the prefix for address to order index entries for the given asset denom and order type. -func indexPrefixAddressToOrderOfType(addr sdk.AccAddress, orderTypeByte byte, extraCap int) []byte { - rv := indexPrefixAddressToOrder(addr, 1+extraCap) - rv = append(rv, orderTypeByte) - return rv -} - // GetIndexKeyPrefixAddressToOrder creates a key prefix for the address to order index limited to the given address. func GetIndexKeyPrefixAddressToOrder(addr sdk.AccAddress) []byte { return indexPrefixAddressToOrder(addr, 0) } -// GetIndexKeyPrefixAddressToOrderAsks creates a key prefix for the address to orders limited to the given asset and only ask orders. -func GetIndexKeyPrefixAddressToOrderAsks(addr sdk.AccAddress) []byte { - return indexPrefixAddressToOrderOfType(addr, OrderKeyTypeAsk, 0) -} - -// GetIndexKeyPrefixAddressToOrderBids creates a key prefix for the address to orders limited to the given asset and only bid orders. -func GetIndexKeyPrefixAddressToOrderBids(addr sdk.AccAddress) []byte { - return indexPrefixAddressToOrderOfType(addr, OrderKeyTypeBid, 0) -} - // MakeIndexKeyAddressToOrder creates the key to use for the address to order index with the given values. -func MakeIndexKeyAddressToOrder(addr sdk.AccAddress, orderTypeByte byte, orderID uint64) []byte { - suffix := uint64Bz(orderID) - rv := indexPrefixAddressToOrderOfType(addr, orderTypeByte, len(suffix)) - rv = append(rv, suffix...) +func MakeIndexKeyAddressToOrder(addr sdk.AccAddress, orderID uint64) []byte { + rv := indexPrefixAddressToOrder(addr, 8) + rv = append(rv, uint64Bz(orderID)...) return rv } // ParseIndexKeyAddressToOrder will extract what it can from an address to order index key. // The input can have the following formats: -// - | | | | -// - | | | -// - | +// - | | | +// - | | // - // -// In the case where just the is provided, the returned address will be empty, and type byte will be 0. -// In the case where just | is provided, the returned address will be empty. -func ParseIndexKeyAddressToOrder(key []byte) (sdk.AccAddress, byte, uint64, error) { +// In the case where just the is provided, the returned address will be empty. +func ParseIndexKeyAddressToOrder(key []byte) (sdk.AccAddress, uint64, error) { if len(key) < 8 { - return nil, 0, 0, fmt.Errorf("cannot parse address to order index key: only has %d bytes, expected at least 8", len(key)) + return nil, 0, fmt.Errorf("cannot parse address to order index key: only has %d bytes, expected at least 8", len(key)) } - pre, orderIDBz := key[:len(key)-8], key[len(key)-8:] orderID, _ := uint64FromBz(orderIDBz) - - var orderTypeByte byte - if len(pre) > 0 { - orderTypeByte = pre[len(pre)-1] - pre = pre[:len(pre)-1] - if !bytes.Contains(OrderKeyTypeBytes, []byte{orderTypeByte}) { - return nil, 0, 0, fmt.Errorf("cannot parse address to order key: unknown order type byte %#x, expected one of %#v", - orderTypeByte, OrderKeyTypeBytes) - } - } - var addr sdk.AccAddress if len(pre) > 0 { // Either the first byte is a length byte, or it's the key type byte. // First check it as a length byte, then, if that fails but it's the key type byte, check the second as the length. // Either way, there needs to be at least 2 bytes: a length byte then address byte (length 0 isn't allowed). if len(pre) == 1 { - return nil, 0, 0, fmt.Errorf("cannot parse address to order index key: unable to determine address from single byte %#x", - pre[0]) + return nil, 0, fmt.Errorf("cannot parse address to order index key: unable to determine address from single byte %#x", pre[0]) } var rest []byte var err error @@ -727,12 +659,10 @@ func ParseIndexKeyAddressToOrder(key []byte) (sdk.AccAddress, byte, uint64, erro addr, rest, err = parseLengthPrefixedAddr(pre[1:]) } if len(addr) == 0 || len(rest) != 0 || err != nil { - return nil, 0, 0, fmt.Errorf("cannot parse address to order index key: unable to determine address from [%d, %d, ...(length %d)]", - pre[0], pre[1], len(pre)) + return nil, 0, fmt.Errorf("cannot parse address to order index key: unable to determine address from [%d, %d, ...(length %d)]", pre[0], pre[1], len(pre)) } } - - return addr, orderTypeByte, orderID, nil + return addr, orderID, nil } // indexPrefixAssetToOrder creates the prefix for the asset to order index entries with some extra space for the rest. @@ -784,26 +714,18 @@ func ParseIndexKeyAssetToOrder(key []byte) (string, byte, uint64, error) { return "", 0, 0, fmt.Errorf("cannot parse asset to order key: only has %d bytes, expected at least 8", len(key)) } unparsed, orderIDBz := key[:len(key)-8], key[len(key)-8:] - orderID, _ := uint64FromBz(orderIDBz) - var denom string - var orderTypeByte byte - + var typeByte byte + orderID, _ := uint64FromBz(orderIDBz) if len(unparsed) > 0 { - orderTypeByte = unparsed[len(unparsed)-1] + typeByte = unparsed[len(unparsed)-1] unparsed = unparsed[:len(unparsed)-1] - if !bytes.Contains(OrderKeyTypeBytes, []byte{orderTypeByte}) { - return "", 0, 0, fmt.Errorf("cannot parse asset to order key: unknown order type byte %#x, expected one of %#v", - orderTypeByte, OrderKeyTypeBytes) - } } - if len(unparsed) > 0 { if unparsed[0] == KeyTypeAssetToOrderIndex { unparsed = unparsed[1:] } denom = string(unparsed) } - - return denom, orderTypeByte, orderID, nil + return denom, typeByte, orderID, nil } diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index b5d18cfc64..3154ef2fed 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -2,7 +2,6 @@ package keeper_test import ( "bytes" - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -125,11 +124,6 @@ func TestKeyTypeUniqueness(t *testing.T) { } }) } - - t.Run("OrderKeyTypeBytes", func(t *testing.T) { - expected := []byte{keeper.OrderKeyTypeAsk, keeper.OrderKeyTypeBid} - assert.Equalf(t, expected, keeper.OrderKeyTypeBytes, "OrderKeyTypeBytes") - }) } func TestParseLengthPrefixedAddr(t *testing.T) { @@ -2821,7 +2815,7 @@ func TestMakeKeyOrder(t *testing.T) { return keeper.MakeKeyOrder(tc.orderID) }, expected: tc.expected, - expPrefixes: []expectedPrefix{{name: "GetKeyPrefixOrder", value: keeper.GetKeyPrefixOrder()}}, + expPrefixes: []expectedPrefix{{name: "", value: keeper.GetKeyPrefixOrder()}}, } checkKey(t, ktc, "MakeKeyOrder(%d)", tc.orderID) }) @@ -2935,223 +2929,73 @@ func TestGetIndexKeyPrefixMarketToOrder(t *testing.T) { } } -func TestGetIndexKeyPrefixMarketToOrderAsks(t *testing.T) { - orderTypeKey := keeper.OrderKeyTypeAsk - +func TestMakeIndexKeyMarketToOrder(t *testing.T) { tests := []struct { name string marketID uint32 + orderID uint64 expected []byte }{ { - name: "market id 0", + name: "market 0 order 0", marketID: 0, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, orderTypeKey}, - }, - { - name: "market id 1", - marketID: 1, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 1, orderTypeKey}, - }, - { - name: "market id 255", - marketID: 255, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 255, orderTypeKey}, - }, - { - name: "market id 256", - marketID: 256, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 1, 0, orderTypeKey}, - }, - { - name: "market id 65_536", - marketID: 65_536, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 1, 0, 0, orderTypeKey}, - }, - { - name: "market id 16,777,216", - marketID: 16_777_216, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 0, 0, 0, orderTypeKey}, - }, - { - name: "market id 16,843,009", - marketID: 16_843_009, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 1, 1, 1, orderTypeKey}, - }, - { - name: "market id 4,294,967,295", - marketID: 4_294_967_295, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 255, 255, 255, 255, orderTypeKey}, + orderID: 0, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - ktc := keyTestCase{ - maker: func() []byte { - return keeper.GetIndexKeyPrefixMarketToOrderAsks(tc.marketID) - }, - expected: tc.expected, - expPrefixes: []expectedPrefix{ - { - name: "GetIndexKeyPrefixMarketToOrder", - value: keeper.GetIndexKeyPrefixMarketToOrder(tc.marketID), - }, - }, - } - checkKey(t, ktc, "GetIndexKeyPrefixMarketToOrderAsks(%d)", tc.marketID) - }) - } -} - -func TestGetIndexKeyPrefixMarketToOrderBids(t *testing.T) { - orderTypeKey := keeper.OrderKeyTypeBid - - tests := []struct { - name string - marketID uint32 - expected []byte - }{ { - name: "market id 0", + name: "market 0 order 1", marketID: 0, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, orderTypeKey}, + orderID: 1, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, }, { - name: "market id 1", - marketID: 1, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 1, orderTypeKey}, + name: "market 0 order 72,340,172,838,076,673", + marketID: 0, + orderID: 72_340_172_838_076_673, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, }, { - name: "market id 255", - marketID: 255, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 255, orderTypeKey}, + name: "market 2 order 0", + marketID: 2, + orderID: 0, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}, }, { - name: "market id 256", - marketID: 256, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 1, 0, orderTypeKey}, + name: "market 2 order 1", + marketID: 2, + orderID: 1, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1}, }, { - name: "market id 65_536", - marketID: 65_536, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 1, 0, 0, orderTypeKey}, + name: "market 2 order 72,340,172,838,076,673", + marketID: 2, + orderID: 72_340_172_838_076_673, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1}, }, { - name: "market id 16,777,216", - marketID: 16_777_216, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 0, 0, 0, orderTypeKey}, + name: "market 33,686,018 order 0", + marketID: 33_686_018, + orderID: 0, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0}, }, { - name: "market id 16,843,009", - marketID: 16_843_009, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 1, 1, 1, orderTypeKey}, + name: "market 33,686,018 order 1", + marketID: 33_686_018, + orderID: 1, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1}, }, { - name: "market id 4,294,967,295", - marketID: 4_294_967_295, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 255, 255, 255, 255, orderTypeKey}, + name: "market 33,686,018 order 72,340,172,838,076,673", + marketID: 33_686_018, + orderID: 72_340_172_838_076_673, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1}, }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - ktc := keyTestCase{ - maker: func() []byte { - return keeper.GetIndexKeyPrefixMarketToOrderBids(tc.marketID) - }, - expected: tc.expected, - expPrefixes: []expectedPrefix{ - { - name: "GetIndexKeyPrefixMarketToOrder", - value: keeper.GetIndexKeyPrefixMarketToOrder(tc.marketID), - }, - }, - } - checkKey(t, ktc, "GetIndexKeyPrefixMarketToOrderBids(%d)", tc.marketID) - }) - } -} -func TestMakeIndexKeyMarketToOrder(t *testing.T) { - tests := []struct { - name string - marketID uint32 - orderTypeByte byte - orderID uint64 - expected []byte - }{ - { - name: "market 0 order 0", - marketID: 0, - orderTypeByte: 8, - orderID: 0, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0}, - }, - { - name: "market 0 order 1", - marketID: 0, - orderTypeByte: 7, - orderID: 1, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 1}, - }, - { - name: "market 0 order 72,340,172,838,076,673", - marketID: 0, - orderTypeByte: 255, - orderID: 72_340_172_838_076_673, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 0, 255, 1, 1, 1, 1, 1, 1, 1, 1}, - }, { - name: "market 2 order 0", - marketID: 2, - orderTypeByte: 0, - orderID: 0, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - }, - { - name: "market 2 order 1", - marketID: 2, - orderTypeByte: 1, - orderID: 1, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1}, - }, - { - name: "market 2 order 72,340,172,838,076,673", - marketID: 2, - orderTypeByte: 10, - orderID: 72_340_172_838_076_673, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 2, 10, 1, 1, 1, 1, 1, 1, 1, 1}, - }, - { - name: "market 33,686,018 order 0", - marketID: 33_686_018, - orderTypeByte: 55, - orderID: 0, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 55, 0, 0, 0, 0, 0, 0, 0, 0}, - }, - { - name: "market 33,686,018 order 1", - marketID: 33_686_018, - orderTypeByte: 123, - orderID: 1, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, 123, 0, 0, 0, 0, 0, 0, 0, 1}, - }, - { - name: "market 33,686,018 order 72,340,172,838,076,673", - marketID: 33_686_018, - orderTypeByte: keeper.OrderKeyTypeAsk, - orderID: 72_340_172_838_076_673, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 2, 2, 2, 2, - keeper.OrderKeyTypeAsk, 1, 1, 1, 1, 1, 1, 1, 1}, - }, - { - name: "market max order max", - marketID: 4_294_967_295, - orderTypeByte: keeper.OrderKeyTypeBid, - orderID: 18_446_744_073_709_551_615, - expected: []byte{keeper.KeyTypeMarketToOrderIndex, 255, 255, 255, 255, - keeper.OrderKeyTypeBid, 255, 255, 255, 255, 255, 255, 255, 255}, + name: "market max order max", + marketID: 4_294_967_295, + orderID: 18_446_744_073_709_551_615, + expected: []byte{keeper.KeyTypeMarketToOrderIndex, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, }, } @@ -3159,11 +3003,11 @@ func TestMakeIndexKeyMarketToOrder(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeIndexKeyMarketToOrder(tc.marketID, tc.orderTypeByte, tc.orderID) + return keeper.MakeIndexKeyMarketToOrder(tc.marketID, tc.orderID) }, expected: tc.expected, expPrefixes: []expectedPrefix{ - {name: "GetIndexKeyPrefixMarketToOrder", value: keeper.GetIndexKeyPrefixMarketToOrder(tc.marketID)}, + {name: "", value: keeper.GetIndexKeyPrefixMarketToOrder(tc.marketID)}, }, } checkKey(t, ktc, "MakeIndexKeyMarketToOrder(%d, %d)", tc.marketID, tc.orderID) @@ -3172,165 +3016,96 @@ func TestMakeIndexKeyMarketToOrder(t *testing.T) { } func TestParseIndexKeyMarketToOrder(t *testing.T) { - keyAsks, keyBids := keeper.OrderKeyTypeAsk, keeper.OrderKeyTypeBid - badLenErr := func(actualLength int) string { - return fmt.Sprintf("cannot parse market to order key: length %d, expected 8, 9, 13, or 14", actualLength) - } - tests := []struct { - name string - key []byte - expMarketID uint32 - expOrderTypeByte byte - expOrderID uint64 - expErr string + name string + key []byte + expMarketID uint32 + expOrderID uint64 + expErr string }{ { name: "nil key", key: nil, - expErr: badLenErr(0), + expErr: "cannot parse market to order key: length 0, expected 8, 12, or 13", }, { name: "empty key", key: []byte{}, - expErr: badLenErr(0), + expErr: "cannot parse market to order key: length 0, expected 8, 12, or 13", }, { name: "7 bytes", key: []byte{1, 2, 3, 4, 5, 6, 7}, - expErr: badLenErr(7), + expErr: "cannot parse market to order key: length 7, expected 8, 12, or 13", + }, + { + name: "9 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + expErr: "cannot parse market to order key: length 9, expected 8, 12, or 13", }, { name: "10 bytes", key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - expErr: badLenErr(10), + expErr: "cannot parse market to order key: length 10, expected 8, 12, or 13", }, { - name: "12 bytes", - key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, - expErr: badLenErr(12), + name: "11 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, + expErr: "cannot parse market to order key: length 11, expected 8, 12, or 13", }, { - name: "15 bytes", - key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - expErr: badLenErr(15), + name: "14 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + expErr: "cannot parse market to order key: length 14, expected 8, 12, or 13", }, { - name: "order id 0", + name: "8 bytes order id 0", key: []byte{0, 0, 0, 0, 0, 0, 0, 0}, expOrderID: 0, }, { - name: "order id 1", + name: "8 bytes order id 1", key: []byte{0, 0, 0, 0, 0, 0, 0, 1}, expMarketID: 0, expOrderID: 1, }, { - name: "order id 72,623,859,790,382,856", + name: "8 bytes order id 72,623,859,790,382,856", key: []byte{1, 2, 3, 4, 5, 6, 7, 8}, expOrderID: 72_623_859_790_382_856, }, { - name: "ask order id 0", - key: []byte{keyAsks, 0, 0, 0, 0, 0, 0, 0, 0}, - expOrderTypeByte: keyAsks, - expOrderID: 0, - }, - { - name: "ask order id 1", - key: []byte{keyAsks, 0, 0, 0, 0, 0, 0, 0, 1}, - expOrderTypeByte: keyAsks, - expOrderID: 1, - }, - { - name: "ask order id 72,623,859,790,382,856", - key: []byte{keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, - expOrderTypeByte: keyAsks, - expOrderID: 72_623_859_790_382_856, - }, - { - name: "bid order id 0", - key: []byte{keyBids, 0, 0, 0, 0, 0, 0, 0, 0}, - expOrderTypeByte: keyBids, - expOrderID: 0, - }, - { - name: "bid order id 1", - key: []byte{keyBids, 0, 0, 0, 0, 0, 0, 0, 1}, - expOrderTypeByte: keyBids, - expOrderID: 1, - }, - { - name: "bid order id 72,623,859,790,382,856", - key: []byte{keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, - expOrderTypeByte: keyBids, - expOrderID: 72_623_859_790_382_856, - }, - { - name: "market id 1 ask order id 1", - key: []byte{0, 0, 0, 1, keyAsks, 0, 0, 0, 0, 0, 0, 0, 1}, - expMarketID: 1, - expOrderTypeByte: keyAsks, - expOrderID: 1, - }, - { - name: "market id 16,843,009 ask order id 144,680,345,676,153,346", - key: []byte{1, 1, 1, 1, keyAsks, 2, 2, 2, 2, 2, 2, 2, 2}, - expMarketID: 16_843_009, - expOrderTypeByte: keyAsks, - expOrderID: 144_680_345_676_153_346, - }, - { - name: "market id 1 bid order id 1", - key: []byte{0, 0, 0, 1, keyBids, 0, 0, 0, 0, 0, 0, 0, 1}, - expMarketID: 1, - expOrderTypeByte: keyBids, - expOrderID: 1, - }, - { - name: "market id 16,843,009 bid order id 144,680,345,676,153,346", - key: []byte{1, 1, 1, 1, keyBids, 2, 2, 2, 2, 2, 2, 2, 2}, - expMarketID: 16_843_009, - expOrderTypeByte: keyBids, - expOrderID: 144_680_345_676_153_346, - }, - { - name: "type, market id 1 ask order id 1", - key: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 1, keyAsks, 0, 0, 0, 0, 0, 0, 0, 1}, - expMarketID: 1, - expOrderTypeByte: keyAsks, - expOrderID: 1, + name: "12 bytes market id 1 order id 1", + key: []byte{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, + expMarketID: 1, + expOrderID: 1, }, { - name: "type, market id 16,843,009 ask order id 144,680,345,676,153,346", - key: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 1, 1, 1, keyAsks, 2, 2, 2, 2, 2, 2, 2, 2}, - expMarketID: 16_843_009, - expOrderTypeByte: keyAsks, - expOrderID: 144_680_345_676_153_346, + name: "12 bytes market id 16,843,009 order id 144,680,345,676,153,346", + key: []byte{1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2}, + expMarketID: 16_843_009, + expOrderID: 144_680_345_676_153_346, }, { - name: "type, market id 1 bid order id 1", - key: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 1, keyBids, 0, 0, 0, 0, 0, 0, 0, 1}, - expMarketID: 1, - expOrderTypeByte: keyBids, - expOrderID: 1, + name: "13 bytes market id 1 order id 1", + key: []byte{keeper.KeyTypeMarketToOrderIndex, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, + expMarketID: 1, + expOrderID: 1, }, { - name: "type, market id 16,843,009 bid order id 144,680,345,676,153,346", - key: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 1, 1, 1, keyBids, 2, 2, 2, 2, 2, 2, 2, 2}, - expMarketID: 16_843_009, - expOrderTypeByte: keyBids, - expOrderID: 144_680_345_676_153_346, + name: "13 bytes market id 16,843,009 order id 144,680,345,676,153,346", + key: []byte{keeper.KeyTypeMarketToOrderIndex, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2}, + expMarketID: 16_843_009, + expOrderID: 144_680_345_676_153_346, }, { - name: "type byte too high", - key: []byte{0x4, 0, 0, 0, 1, keyBids, 0, 0, 0, 0, 0, 0, 0, 1}, + name: "13 bytes first byte too high", + key: []byte{0x4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, expErr: "cannot parse market to order key: unknown type byte 0x4, expected 0x3", }, { - name: "type byte too low", - key: []byte{0x2, 0, 0, 0, 1, keyAsks, 0, 0, 0, 0, 0, 0, 0, 1}, + name: "13 bytes first byte too low", + key: []byte{0x2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, expErr: "cannot parse market to order key: unknown type byte 0x2, expected 0x3", }, } @@ -3338,16 +3113,14 @@ func TestParseIndexKeyMarketToOrder(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { var marketID uint32 - var orderTypeByte byte var orderID uint64 var err error testFunc := func() { - marketID, orderTypeByte, orderID, err = keeper.ParseIndexKeyMarketToOrder(tc.key) + marketID, orderID, err = keeper.ParseIndexKeyMarketToOrder(tc.key) } require.NotPanics(t, testFunc, "ParseIndexKeyMarketToOrder(%v)", tc.key) assertions.AssertErrorValue(t, err, tc.expErr, "ParseIndexKeyMarketToOrder(%v) error", tc.key) assert.Equal(t, tc.expMarketID, marketID, "ParseIndexKeyMarketToOrder(%v) market id", tc.key) - assert.Equal(t, tc.expOrderTypeByte, orderTypeByte, "ParseIndexKeyMarketToOrder(%v) order type byte", tc.key) assert.Equal(t, tc.expOrderID, orderID, "ParseIndexKeyMarketToOrder(%v) order id", tc.key) }) } @@ -3406,84 +3179,11 @@ func TestGetIndexKeyPrefixAddressToOrder(t *testing.T) { } } -func TestGetIndexKeyPrefixAddressToOrderAsks(t *testing.T) { - orderTypeKey := keeper.OrderKeyTypeAsk - - tests := []struct { - name string - addr sdk.AccAddress - expected []byte - expPanic string - }{ - { - name: "nil addr", - addr: nil, - expPanic: "empty address not allowed", - }, - { - name: "empty addr", - addr: sdk.AccAddress{}, - expPanic: "empty address not allowed", - }, - { - name: "256 byte addr", - addr: sdk.AccAddress(bytes.Repeat([]byte{'P'}, 256)), - expPanic: "address length should be max 255 bytes, got 256: unknown address", - }, - { - name: "5 byte addr", - addr: sdk.AccAddress("abcde"), - expected: concatBz( - []byte{keeper.KeyTypeAddressToOrderIndex, 5}, - []byte("abcde"), - []byte{orderTypeKey}, - ), - }, - { - name: "20 byte addr", - addr: sdk.AccAddress("abcdefghijklmnopqrst"), - expected: concatBz( - []byte{keeper.KeyTypeAddressToOrderIndex, 20}, - []byte("abcdefghijklmnopqrst"), - []byte{orderTypeKey}, - ), - }, - { - name: "32 byte addr", - addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), - expected: concatBz( - []byte{keeper.KeyTypeAddressToOrderIndex, 32}, - []byte("abcdefghijklmnopqrstuvwxyzABCDEF"), - []byte{orderTypeKey}, - ), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - ktc := keyTestCase{ - maker: func() []byte { - return keeper.GetIndexKeyPrefixAddressToOrderAsks(tc.addr) - }, - expected: tc.expected, - expPanic: tc.expPanic, - } - if len(tc.addr) > 0 && len(tc.addr) <= 255 { - ktc.expPrefixes = []expectedPrefix{ - {name: "GetIndexKeyPrefixAddressToOrder", value: keeper.GetIndexKeyPrefixAddressToOrder(tc.addr)}, - } - } - checkKey(t, ktc, "GetIndexKeyPrefixAddressToOrderAsks(%s)", string(tc.addr)) - }) - } -} - -func TestGetIndexKeyPrefixAddressToOrderBids(t *testing.T) { - orderTypeKey := keeper.OrderKeyTypeBid - +func TestMakeIndexKeyAddressToOrder(t *testing.T) { tests := []struct { name string addr sdk.AccAddress + orderID uint64 expected []byte expPanic string }{ @@ -3503,130 +3203,53 @@ func TestGetIndexKeyPrefixAddressToOrderBids(t *testing.T) { expPanic: "address length should be max 255 bytes, got 256: unknown address", }, { - name: "5 byte addr", - addr: sdk.AccAddress("abcde"), + name: "5 byte addr order 1", + addr: sdk.AccAddress("abcde"), + orderID: 1, expected: concatBz( []byte{keeper.KeyTypeAddressToOrderIndex, 5}, []byte("abcde"), - []byte{orderTypeKey}, + []byte{0, 0, 0, 0, 0, 0, 0, 1}, ), }, { - name: "20 byte addr", - addr: sdk.AccAddress("abcdefghijklmnopqrst"), + name: "20 byte addr order 1", + addr: sdk.AccAddress("abcdefghijklmnopqrst"), + orderID: 1, expected: concatBz( []byte{keeper.KeyTypeAddressToOrderIndex, 20}, []byte("abcdefghijklmnopqrst"), - []byte{orderTypeKey}, + []byte{0, 0, 0, 0, 0, 0, 0, 1}, ), }, { - name: "32 byte addr", - addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), + name: "32 byte addr order 1", + addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), + orderID: 1, expected: concatBz( []byte{keeper.KeyTypeAddressToOrderIndex, 32}, []byte("abcdefghijklmnopqrstuvwxyzABCDEF"), - []byte{orderTypeKey}, - ), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - ktc := keyTestCase{ - maker: func() []byte { - return keeper.GetIndexKeyPrefixAddressToOrderBids(tc.addr) - }, - expected: tc.expected, - expPanic: tc.expPanic, - } - if len(tc.addr) > 0 && len(tc.addr) <= 255 { - ktc.expPrefixes = []expectedPrefix{ - {name: "GetIndexKeyPrefixAddressToOrder", value: keeper.GetIndexKeyPrefixAddressToOrder(tc.addr)}, - } - } - checkKey(t, ktc, "GetIndexKeyPrefixAddressToOrderBids(%s)", string(tc.addr)) - }) - } -} - -func TestMakeIndexKeyAddressToOrder(t *testing.T) { - tests := []struct { - name string - addr sdk.AccAddress - orderTypeByte byte - orderID uint64 - expected []byte - expPanic string - }{ - { - name: "nil addr", - addr: nil, - expPanic: "empty address not allowed", - }, - { - name: "empty addr", - addr: sdk.AccAddress{}, - expPanic: "empty address not allowed", - }, - { - name: "256 byte addr", - addr: sdk.AccAddress(bytes.Repeat([]byte{'P'}, 256)), - expPanic: "address length should be max 255 bytes, got 256: unknown address", - }, - { - name: "5 byte addr order 1", - addr: sdk.AccAddress("abcde"), - orderTypeByte: keeper.OrderKeyTypeAsk, - orderID: 1, - expected: concatBz( - []byte{keeper.KeyTypeAddressToOrderIndex, 5}, - []byte("abcde"), - []byte{keeper.OrderKeyTypeAsk, 0, 0, 0, 0, 0, 0, 0, 1}, + []byte{0, 0, 0, 0, 0, 0, 0, 1}, ), }, { - name: "20 byte addr order 1", - addr: sdk.AccAddress("abcdefghijklmnopqrst"), - orderTypeByte: keeper.OrderKeyTypeBid, - orderID: 1, - expected: concatBz( - []byte{keeper.KeyTypeAddressToOrderIndex, 20}, - []byte("abcdefghijklmnopqrst"), - []byte{keeper.OrderKeyTypeBid, 0, 0, 0, 0, 0, 0, 0, 1}, - ), - }, - { - name: "32 byte addr order 1", - addr: sdk.AccAddress("abcdefghijklmnopqrstuvwxyzABCDEF"), - orderTypeByte: 0, - orderID: 1, - expected: concatBz( - []byte{keeper.KeyTypeAddressToOrderIndex, 32}, - []byte("abcdefghijklmnopqrstuvwxyzABCDEF"), - []byte{0, 0, 0, 0, 0, 0, 0, 0, 1}, - ), - }, - { - name: "20 byte addr order 5", - addr: sdk.AccAddress("ABCDEFGHIJKLMNOPQRST"), - orderTypeByte: 255, - orderID: 5, + name: "20 byte addr order 5", + addr: sdk.AccAddress("ABCDEFGHIJKLMNOPQRST"), + orderID: 5, expected: concatBz( []byte{keeper.KeyTypeAddressToOrderIndex, 20}, []byte("ABCDEFGHIJKLMNOPQRST"), - []byte{255, 0, 0, 0, 0, 0, 0, 0, 5}, + []byte{0, 0, 0, 0, 0, 0, 0, 5}, ), }, { - name: "20 byte addr order 72,623,859,790,382,856", - addr: sdk.AccAddress("ABCDEFGHIJKLMNOPQRST"), - orderTypeByte: 123, - orderID: 72_623_859_790_382_856, + name: "20 byte addr order 72,623,859,790,382,856", + addr: sdk.AccAddress("ABCDEFGHIJKLMNOPQRST"), + orderID: 72_623_859_790_382_856, expected: concatBz( []byte{keeper.KeyTypeAddressToOrderIndex, 20}, []byte("ABCDEFGHIJKLMNOPQRST"), - []byte{123, 1, 2, 3, 4, 5, 6, 7, 8}, + []byte{1, 2, 3, 4, 5, 6, 7, 8}, ), }, } @@ -3635,14 +3258,14 @@ func TestMakeIndexKeyAddressToOrder(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeIndexKeyAddressToOrder(tc.addr, tc.orderTypeByte, tc.orderID) + return keeper.MakeIndexKeyAddressToOrder(tc.addr, tc.orderID) }, expected: tc.expected, expPanic: tc.expPanic, } if len(tc.expPanic) == 0 { ktc.expPrefixes = []expectedPrefix{ - {name: "GetIndexKeyPrefixAddressToOrder", value: keeper.GetIndexKeyPrefixAddressToOrder(tc.addr)}, + {name: "", value: keeper.GetIndexKeyPrefixAddressToOrder(tc.addr)}, } } checkKey(t, ktc, "MakeIndexKeyAddressToOrder(%s, %d)", string(tc.addr), tc.orderID) @@ -3651,15 +3274,12 @@ func TestMakeIndexKeyAddressToOrder(t *testing.T) { } func TestParseIndexKeyAddressToOrder(t *testing.T) { - keyAsks, keyBids := keeper.OrderKeyTypeAsk, keeper.OrderKeyTypeBid - tests := []struct { - name string - key []byte - expAddr sdk.AccAddress - expOrderTypeByte byte - expOrderID uint64 - expErr string + name string + key []byte + expAddr sdk.AccAddress + expOrderID uint64 + expErr string }{ { name: "nil key", @@ -3687,136 +3307,83 @@ func TestParseIndexKeyAddressToOrder(t *testing.T) { expOrderID: 72_623_859_790_382_856, }, { - name: "ask order id 72,623,859,790,382,856", - key: []byte{keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, - expOrderTypeByte: keyAsks, - expOrderID: 72_623_859_790_382_856, - }, - { - name: "bid order id 72,623,859,790,382,856", - key: []byte{keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, - expOrderTypeByte: keyBids, - expOrderID: 72_623_859_790_382_856, - }, - { - name: "order type byte 99 and order 72,623,859,790,382,856", - key: []byte{99, 1, 2, 3, 4, 5, 6, 7, 8}, - expErr: "cannot parse address to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", - }, - { - name: "1 byte address ask order id 72,623,859,790,382,856", - key: []byte{1, 55, keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{55}, - expOrderTypeByte: keyAsks, - expOrderID: 72_623_859_790_382_856, - }, - { - name: "1 byte address bid order id 72,623,859,790,382,856", - key: []byte{1, 55, keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{55}, - expOrderTypeByte: keyBids, - expOrderID: 72_623_859_790_382_856, + name: "9 bytes", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + expErr: "cannot parse address to order index key: unable to determine address from single byte 0x1", }, { - name: "1 byte address order type byte 99 order id 72,623,859,790,382,856", - key: []byte{1, 55, 99, 1, 2, 3, 4, 5, 6, 7, 8}, - expErr: "cannot parse address to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", + name: "1 byte address order id 72,623,859,790,382,856", + key: []byte{1, 55, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55}, + expOrderID: 72_623_859_790_382_856, }, { name: "length byte 2 but only 1 byte after it", - key: []byte{2, 55, keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, + key: []byte{2, 55, 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [2, 55, ...(length 2)]", }, { name: "length byte 2 but 3 bytes after it", - key: []byte{2, 55, 56, 57, keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, + key: []byte{2, 55, 56, 57, 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [2, 55, ...(length 4)]", }, { - name: "length byte 4 ask order id 72,623,859,790,382,856", - key: []byte{4, 55, 56, 57, 58, keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{55, 56, 57, 58}, - expOrderTypeByte: keyAsks, - expOrderID: 72_623_859_790_382_856, + name: "length byte 4 order id 72,623,859,790,382,856", + key: []byte{4, 55, 56, 57, 58, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55, 56, 57, 58}, + expOrderID: 72_623_859_790_382_856, }, { name: "length byte 20 but only 19 byte after it", key: []byte{20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, - keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, + 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [20, 101, ...(length 20)]", }, { name: "length byte 20 but 21 byte after it", key: []byte{20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, - keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, + 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [20, 101, ...(length 22)]", }, { - name: "20 byte address ask order id 72,623,859,790,382,856", - key: []byte{20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, - 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, - keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{101, 102, 103, 104, 105, 106, 107, 108, 109, 110, - 111, 112, 113, 114, 115, 116, 117, 118, 119, 120}, - expOrderTypeByte: keyAsks, - expOrderID: 72_623_859_790_382_856, - }, - { - name: "20 byte address bid order id 72,623,859,790,382,856", + name: "20 byte address order id 72,623,859,790,382,856", key: []byte{20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, - keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, + 1, 2, 3, 4, 5, 6, 7, 8}, expAddr: sdk.AccAddress{101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120}, - expOrderTypeByte: keyBids, - expOrderID: 72_623_859_790_382_856, - }, - { - name: "with type byte: 1 byte address ask order id 72,623,859,790,382,856", - key: []byte{keeper.KeyTypeAddressToOrderIndex, 1, 55, keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{55}, - expOrderTypeByte: keyAsks, - expOrderID: 72_623_859_790_382_856, + expOrderID: 72_623_859_790_382_856, }, { - name: "with type byte: 1 byte address bid order id 72,623,859,790,382,856", - key: []byte{keeper.KeyTypeAddressToOrderIndex, 1, 55, keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{55}, - expOrderTypeByte: keyBids, - expOrderID: 72_623_859_790_382_856, + name: "with type byte: 1 byte address order id 72,623,859,790,382,856", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 1, 55, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55}, + expOrderID: 72_623_859_790_382_856, }, { name: "with type byte: length byte 2 but only 1 byte after it", - key: []byte{keeper.KeyTypeAddressToOrderIndex, 2, 55, keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, + key: []byte{keeper.KeyTypeAddressToOrderIndex, 2, 55, 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [4, 2, ...(length 3)]", }, { name: "with type byte: length byte 2 but 5 bytes after it", - key: []byte{keeper.KeyTypeAddressToOrderIndex, 2, 55, 56, 57, 58, keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, + key: []byte{keeper.KeyTypeAddressToOrderIndex, 2, 55, 56, 57, 58, 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [4, 2, ...(length 6)]", }, { - name: "with type byte: length byte 4 ask order id 72,623,859,790,382,856", - key: []byte{keeper.KeyTypeAddressToOrderIndex, 4, 55, 56, 57, 58, keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{55, 56, 57, 58}, - expOrderTypeByte: keyAsks, - expOrderID: 72_623_859_790_382_856, - }, - { - name: "with type byte: length byte 4 bid order id 72,623,859,790,382,856", - key: []byte{keeper.KeyTypeAddressToOrderIndex, 4, 55, 56, 57, 58, keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{55, 56, 57, 58}, - expOrderTypeByte: keyBids, - expOrderID: 72_623_859_790_382_856, + name: "with type byte: length byte 4 order id 72,623,859,790,382,856", + key: []byte{keeper.KeyTypeAddressToOrderIndex, 4, 55, 56, 57, 58, 1, 2, 3, 4, 5, 6, 7, 8}, + expAddr: sdk.AccAddress{55, 56, 57, 58}, + expOrderID: 72_623_859_790_382_856, }, { name: "with type byte: length byte 20 but only 19 byte after it", key: []byte{keeper.KeyTypeAddressToOrderIndex, 20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, - keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, + 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [4, 20, ...(length 21)]", }, { @@ -3824,46 +3391,32 @@ func TestParseIndexKeyAddressToOrder(t *testing.T) { key: []byte{keeper.KeyTypeAddressToOrderIndex, 20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, - keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, + 1, 2, 3, 4, 5, 6, 7, 8}, expErr: "cannot parse address to order index key: unable to determine address from [4, 20, ...(length 23)]", }, { - name: "with type byte: 20 byte address ask order id 72,623,859,790,382,856", + name: "with type byte: 20 byte address order id 72,623,859,790,382,856", key: []byte{keeper.KeyTypeAddressToOrderIndex, 20, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, - keyAsks, 1, 2, 3, 4, 5, 6, 7, 8}, + 1, 2, 3, 4, 5, 6, 7, 8}, expAddr: sdk.AccAddress{101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120}, - expOrderTypeByte: keyAsks, - expOrderID: 72_623_859_790_382_856, - }, - { - name: "with type byte: 20 byte address ask order id 72,623,859,790,382,856", - key: []byte{keeper.KeyTypeAddressToOrderIndex, 20, - 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, - 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, - keyBids, 1, 2, 3, 4, 5, 6, 7, 8}, - expAddr: sdk.AccAddress{101, 102, 103, 104, 105, 106, 107, 108, 109, 110, - 111, 112, 113, 114, 115, 116, 117, 118, 119, 120}, - expOrderTypeByte: keyBids, - expOrderID: 72_623_859_790_382_856, + expOrderID: 72_623_859_790_382_856, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { var addr sdk.AccAddress - var orderTypeByte byte var orderID uint64 var err error testFunc := func() { - addr, orderTypeByte, orderID, err = keeper.ParseIndexKeyAddressToOrder(tc.key) + addr, orderID, err = keeper.ParseIndexKeyAddressToOrder(tc.key) } require.NotPanics(t, testFunc, "ParseIndexKeyAddressToOrder(%v)", tc.key) assertions.AssertErrorValue(t, err, tc.expErr, "ParseIndexKeyAddressToOrder(%v) error", tc.key) assert.Equal(t, tc.expAddr, addr, "ParseIndexKeyAddressToOrder(%v) address", tc.key) - assert.Equal(t, tc.expOrderTypeByte, orderTypeByte, "ParseIndexKeyAddressToOrder(%v) order type byte", tc.key) assert.Equal(t, tc.expOrderID, orderID, "ParseIndexKeyAddressToOrder(%v) order id", tc.key) }) } @@ -4156,8 +3709,6 @@ func TestMakeIndexKeyAssetToOrder(t *testing.T) { } func TestParseIndexKeyAssetToOrder(t *testing.T) { - keyAsks, keyBids := keeper.OrderKeyTypeAsk, keeper.OrderKeyTypeBid - tests := []struct { name string key []byte @@ -4192,101 +3743,32 @@ func TestParseIndexKeyAssetToOrder(t *testing.T) { expOrderID: 578_437_695_752_307_201, }, { - name: "asks order id 578,437,695,752,307,201", - key: []byte{keyAsks, 8, 7, 6, 5, 4, 3, 2, 1}, - expTypeByte: keyAsks, + name: "order type byte 99 order id 578,437,695,752,307,201", + key: []byte{99, 8, 7, 6, 5, 4, 3, 2, 1}, + expTypeByte: 99, expOrderID: 578_437_695_752_307_201, }, { - name: "nhash asks order id 578,437,695,752,307,201", - key: []byte{'n', 'h', 'a', 's', 'h', keyAsks, 8, 7, 6, 5, 4, 3, 2, 1}, + name: "nhash order type byte 99 order id 578,437,695,752,307,201", + key: []byte{'n', 'h', 'a', 's', 'h', 99, 8, 7, 6, 5, 4, 3, 2, 1}, expDenom: "nhash", - expTypeByte: keyAsks, + expTypeByte: 99, expOrderID: 578_437_695_752_307_201, }, { - name: "hex string asks order id 578,437,695,752,307,201", - key: append([]byte(hexString), keyAsks, 8, 7, 6, 5, 4, 3, 2, 1), + name: "hex string order type byte 99 order id 578,437,695,752,307,201", + key: append([]byte(hexString), 99, 8, 7, 6, 5, 4, 3, 2, 1), expDenom: hexString, - expTypeByte: keyAsks, + expTypeByte: 99, expOrderID: 578_437_695_752_307_201, }, { - name: "with type byte nhash asks order id 578,437,695,752,307,201", - key: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', keyAsks, 8, 7, 6, 5, 4, 3, 2, 1}, + name: "with type byte nhash order type byte 99 order id 578,437,695,752,307,201", + key: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', 99, 8, 7, 6, 5, 4, 3, 2, 1}, expDenom: "nhash", - expTypeByte: keyAsks, - expOrderID: 578_437_695_752_307_201, - }, - { - name: "with type byte hex string asks order id 578,437,695,752,307,201", - key: concatBz( - []byte{keeper.KeyTypeAssetToOrderIndex}, - []byte(hexString), - []byte{keyAsks, 8, 7, 6, 5, 4, 3, 2, 1}, - ), - expDenom: hexString, - expTypeByte: keyAsks, - expOrderID: 578_437_695_752_307_201, - }, - { - name: "bids order id 578,437,695,752,307,201", - key: []byte{keyBids, 8, 7, 6, 5, 4, 3, 2, 1}, - expTypeByte: keyBids, - expOrderID: 578_437_695_752_307_201, - }, - { - name: "nhash bids order id 578,437,695,752,307,201", - key: []byte{'n', 'h', 'a', 's', 'h', keyBids, 8, 7, 6, 5, 4, 3, 2, 1}, - expDenom: "nhash", - expTypeByte: keyBids, - expOrderID: 578_437_695_752_307_201, - }, - { - name: "hex string bids order id 578,437,695,752,307,201", - key: append([]byte(hexString), keyBids, 8, 7, 6, 5, 4, 3, 2, 1), - expDenom: hexString, - expTypeByte: keyBids, + expTypeByte: 99, expOrderID: 578_437_695_752_307_201, }, - { - name: "with type byte nhash bids order id 578,437,695,752,307,201", - key: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', keyBids, 8, 7, 6, 5, 4, 3, 2, 1}, - expDenom: "nhash", - expTypeByte: keyBids, - expOrderID: 578_437_695_752_307_201, - }, - { - name: "with type byte hex string bids order id 578,437,695,752,307,201", - key: concatBz( - []byte{keeper.KeyTypeAssetToOrderIndex}, - []byte(hexString), - []byte{keyBids, 8, 7, 6, 5, 4, 3, 2, 1}, - ), - expDenom: hexString, - expTypeByte: keyBids, - expOrderID: 578_437_695_752_307_201, - }, - { - name: "order type byte 99 order id 578,437,695,752,307,201", - key: []byte{99, 8, 7, 6, 5, 4, 3, 2, 1}, - expErr: "cannot parse asset to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", - }, - { - name: "nhash order type byte 99 order id 578,437,695,752,307,201", - key: []byte{'n', 'h', 'a', 's', 'h', 99, 8, 7, 6, 5, 4, 3, 2, 1}, - expErr: "cannot parse asset to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", - }, - { - name: "hex string order type byte 99 order id 578,437,695,752,307,201", - key: append([]byte(hexString), 99, 8, 7, 6, 5, 4, 3, 2, 1), - expErr: "cannot parse asset to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", - }, - { - name: "with type byte nhash order type byte 99 order id 578,437,695,752,307,201", - key: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', 99, 8, 7, 6, 5, 4, 3, 2, 1}, - expErr: "cannot parse asset to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", - }, { name: "with type byte hex string order type byte 99 order id 578,437,695,752,307,201", key: concatBz( @@ -4294,7 +3776,9 @@ func TestParseIndexKeyAssetToOrder(t *testing.T) { []byte(hexString), []byte{99, 8, 7, 6, 5, 4, 3, 2, 1}, ), - expErr: "cannot parse asset to order key: unknown order type byte 0x63, expected one of []byte{0x0, 0x1}", + expDenom: hexString, + expTypeByte: 99, + expOrderID: 578_437_695_752_307_201, }, } diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index c9d590a29d..8da7802638 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -83,8 +83,8 @@ func (k Keeper) parseOrderStoreKeyValue(key, value []byte) (*exchange.Order, err return k.parseOrderStoreValue(orderID, value) } -// createIndexKeys creates all the key/value index entries for an order. -func createIndexKeys(order exchange.Order) [][]byte { +// createIndexEntries creates all the key/value index entries for an order. +func createIndexEntries(order exchange.Order) []sdk.KVPair { marketID := order.GetMarketID() orderID := order.GetOrderID() orderTypeByte := order.GetOrderTypeByte() @@ -92,10 +92,19 @@ func createIndexKeys(order exchange.Order) [][]byte { addr := sdk.MustAccAddressFromBech32(owner) assets := order.GetAssets() - return [][]byte{ - MakeIndexKeyMarketToOrder(marketID, orderTypeByte, orderID), - MakeIndexKeyAddressToOrder(addr, orderTypeByte, orderID), - MakeIndexKeyAssetToOrder(assets.Denom, orderTypeByte, orderID), + return []sdk.KVPair{ + { + Key: MakeIndexKeyMarketToOrder(marketID, orderID), + Value: []byte{orderTypeByte}, + }, + { + Key: MakeIndexKeyAddressToOrder(addr, orderID), + Value: []byte{orderTypeByte}, + }, + { + Key: MakeIndexKeyAssetToOrder(assets.Denom, orderTypeByte, orderID), + Value: nil, + }, } } @@ -122,9 +131,9 @@ func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { isUpdate := store.Has(key) store.Set(key, value) if !isUpdate { - indexKeys := createIndexKeys(order) - for _, entry := range indexKeys { - store.Set(entry, nil) + indexEntries := createIndexEntries(order) + for _, entry := range indexEntries { + store.Set(entry.Key, entry.Value) } // It is assumed that these index entries cannot change over the life of an order. // The only change that is allowed to an order is the assets (due to partial fulfillment). @@ -138,9 +147,9 @@ func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { func deleteAndDeIndexOrder(store sdk.KVStore, order exchange.Order) { key := MakeKeyOrder(order.OrderId) store.Delete(key) - indexKeys := createIndexKeys(order) - for _, entry := range indexKeys { - store.Delete(entry) + indexEntries := createIndexEntries(order) + for _, entry := range indexEntries { + store.Delete(entry.Key) } } @@ -192,71 +201,29 @@ func (k Keeper) IterateOrders(ctx sdk.Context, cb func(order *exchange.Order) bo // The callback takes in the order id and order type byte and should return whether to stop iterating. func (k Keeper) IterateMarketOrders(ctx sdk.Context, marketID uint32, cb func(orderID uint64, orderTypeByte byte) bool) { k.iterate(ctx, GetIndexKeyPrefixMarketToOrder(marketID), func(key, value []byte) bool { - _, orderTypeByte, orderID, err := ParseIndexKeyMarketToOrder(key) - if err != nil { - return false - } - return cb(orderID, orderTypeByte) - }) -} - -// IterateMarketAskOrders iterates over all ask orders for a market. -// The callback takes in the order id and order type byte and should return whether to stop iterating. -func (k Keeper) IterateMarketAskOrders(ctx sdk.Context, marketID uint32, cb func(orderID uint64, orderTypeByte byte) bool) { - k.iterate(ctx, GetIndexKeyPrefixMarketToOrderAsks(marketID), func(key, _ []byte) bool { - _, _, orderID, err := ParseIndexKeyMarketToOrder(key) - if err != nil { + if len(value) == 0 { return false } - return cb(orderID, OrderKeyTypeAsk) - }) -} - -// IterateMarketBidOrders iterates over all bid orders for a market. -// The callback takes in the order id and order type byte and should return whether to stop iterating. -func (k Keeper) IterateMarketBidOrders(ctx sdk.Context, marketID uint32, cb func(orderID uint64, orderTypeByte byte) bool) { - k.iterate(ctx, GetIndexKeyPrefixMarketToOrderBids(marketID), func(key, _ []byte) bool { - _, _, orderID, err := ParseIndexKeyMarketToOrder(key) + _, orderID, err := ParseIndexKeyMarketToOrder(key) if err != nil { return false } - return cb(orderID, OrderKeyTypeBid) + return cb(orderID, value[0]) }) } // IterateAddressOrders iterates over all orders for an address. // The callback takes in the order id and order type byte and should return whether to stop iterating. func (k Keeper) IterateAddressOrders(ctx sdk.Context, addr sdk.AccAddress, cb func(orderID uint64, orderTypeByte byte) bool) { - k.iterate(ctx, GetIndexKeyPrefixAddressToOrder(addr), func(key, _ []byte) bool { - _, orderTypeByte, orderID, err := ParseIndexKeyAddressToOrder(key) - if err != nil { + k.iterate(ctx, GetIndexKeyPrefixAddressToOrder(addr), func(key, value []byte) bool { + if len(value) == 0 { return false } - return cb(orderID, orderTypeByte) - }) -} - -// IterateAddressAskOrders iterates over all ask orders for an address. -// The callback takes in the order id and order type byte and should return whether to stop iterating. -func (k Keeper) IterateAddressAskOrders(ctx sdk.Context, addr sdk.AccAddress, cb func(orderID uint64, orderTypeByte byte) bool) { - k.iterate(ctx, GetIndexKeyPrefixAddressToOrderAsks(addr), func(key, _ []byte) bool { - _, _, orderID, err := ParseIndexKeyAddressToOrder(key) - if err != nil { - return false - } - return cb(orderID, OrderKeyTypeAsk) - }) -} - -// IterateAddressBidOrders iterates over all bid orders for an address. -// The callback takes in the order id and order type byte and should return whether to stop iterating. -func (k Keeper) IterateAddressBidOrders(ctx sdk.Context, addr sdk.AccAddress, cb func(orderID uint64, orderTypeByte byte) bool) { - k.iterate(ctx, GetIndexKeyPrefixAddressToOrderBids(addr), func(key, _ []byte) bool { - _, _, orderID, err := ParseIndexKeyAddressToOrder(key) + _, orderID, err := ParseIndexKeyAddressToOrder(key) if err != nil { return false } - return cb(orderID, OrderKeyTypeBid) + return cb(orderID, value[0]) }) } @@ -273,26 +240,26 @@ func (k Keeper) IterateAssetOrders(ctx sdk.Context, assetDenom string, cb func(o } // IterateAssetAskOrders iterates over all ask orders for a given asset denom. -// The callback takes in the order id and order type byte and should return whether to stop iterating. -func (k Keeper) IterateAssetAskOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64, orderTypeByte byte) bool) { +// The callback takes in the order id and should return whether to stop iterating. +func (k Keeper) IterateAssetAskOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64) bool) { k.iterate(ctx, GetIndexKeyPrefixAssetToOrderAsks(assetDenom), func(key, _ []byte) bool { _, _, orderID, err := ParseIndexKeyAssetToOrder(key) if err != nil { return false } - return cb(orderID, OrderKeyTypeAsk) + return cb(orderID) }) } // IterateAssetBidOrders iterates over all bid orders for a given asset denom. -// The callback takes in the order id and order type byte and should return whether to stop iterating. -func (k Keeper) IterateAssetBidOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64, orderTypeByte byte) bool) { +// The callback takes in the order id and should return whether to stop iterating. +func (k Keeper) IterateAssetBidOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64) bool) { k.iterate(ctx, GetIndexKeyPrefixAssetToOrderBids(assetDenom), func(key, _ []byte) bool { _, _, orderID, err := ParseIndexKeyAssetToOrder(key) if err != nil { return false } - return cb(orderID, OrderKeyTypeBid) + return cb(orderID) }) } From 99d37a5e40fd982eac7fd3899818d680bc7e0b72 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 2 Oct 2023 10:54:58 -0600 Subject: [PATCH 215/309] [1658]: Change the asset-to-order index to match the others with the type byte in the value. --- x/exchange/keeper/grpc_query.go | 33 ++-- x/exchange/keeper/keys.go | 56 +++---- x/exchange/keeper/keys_test.go | 276 ++++++-------------------------- x/exchange/keeper/orders.go | 66 ++------ 4 files changed, 103 insertions(+), 328 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 3d96d86378..58274e2e50 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -171,26 +171,37 @@ func (k QueryServer) QueryGetAssetOrders(goCtx context.Context, req *exchange.Qu return nil, status.Error(codes.InvalidArgument, "empty request") } - var pre []byte - switch strings.ToLower(req.OrderType) { - case "": - pre = GetIndexKeyPrefixAssetToOrder(req.Asset) - case exchange.OrderTypeAsk: - pre = GetIndexKeyPrefixAssetToOrderAsks(req.Asset) - case exchange.OrderTypeBid: - pre = GetIndexKeyPrefixAssetToOrderBids(req.Asset) - default: - return nil, status.Errorf(codes.InvalidArgument, "unknown order type %q", req.OrderType) + var orderTypeByte byte + filterByType := false + if len(req.OrderType) > 0 { + orderType := strings.ToLower(req.OrderType) + // only look at the first 3 chars to handle stuff like "asks" or "bidOrders" too. + if len(orderType) > 3 { + orderType = orderType[:3] + } + switch orderType { + case exchange.OrderTypeAsk: + orderTypeByte = OrderKeyTypeAsk + case exchange.OrderTypeBid: + orderTypeByte = OrderKeyTypeBid + default: + return nil, status.Errorf(codes.InvalidArgument, "unknown order type %q", req.OrderType) + } + filterByType = true } ctx := sdk.UnwrapSDKContext(goCtx) + pre := GetIndexKeyPrefixAssetToOrder(req.Asset) store := prefix.NewStore(k.getStore(ctx), pre) resp := &exchange.QueryGetAssetOrdersResponse{} var pageErr error resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { + if filterByType && (len(value) == 0 || value[0] != orderTypeByte) { + return false, nil + } // If we can't get the order id from the key, just pretend like it doesn't exist. - _, _, orderID, perr := ParseIndexKeyAssetToOrder(key) + _, orderID, perr := ParseIndexKeyAssetToOrder(key) if perr != nil { return false, nil } diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 796344cfe7..c8cdc91d16 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -58,16 +58,14 @@ import ( // Ask Orders: 0x02 | (8 bytes) => 0x00 | protobuf(AskOrder) // Bid Orders: 0x02 | (8 bytes) => 0x01 | protobuf(BidOrder) // -// TODO[1658]: Refactor the market to order index to have the order type byte before the order id. // A market to order index is maintained with the following format: // 0x03 | (4 bytes) | (8 bytes) => // -// TODO[1658]: Refactor the address to order index to have the order type byte before the order id. // An address to order index is maintained with the following format: -// 0x04 | len(
) (1 byte) |
| (8 bytes) => nil +// 0x04 | len(
) (1 byte) |
| (8 bytes) => // // An asset type to order index is maintained with the following format: -// 0x05 | | (1 byte) | (8 bytes) => nil +// 0x05 | | (8 bytes) => const ( // KeyTypeParams is the type byte for params entries. @@ -560,6 +558,15 @@ func ParseKeyOrder(key []byte) (uint64, bool) { return uint64FromBz(key[len(key)-8:]) } +// ParseIndexKeySuffixOrderID converts the last 8 bytes of the provided key into a uint64. +// The returned bool will be false only if the key has fewer than 8 bytes. +func ParseIndexKeySuffixOrderID(key []byte) (uint64, bool) { + if len(key) < 8 { + return 0, false + } + return uint64FromBz(key[len(key)-8:]) +} + // indexPrefixMarketToOrder creates the prefix for the market to order prefix entries with some extra space for the rest. func indexPrefixMarketToOrder(marketID uint32, extraCap int) []byte { return prepKey(KeyTypeMarketToOrderIndex, uint32Bz(marketID), extraCap) @@ -670,62 +677,41 @@ func indexPrefixAssetToOrder(assetDenom string, extraCap int) []byte { return prepKey(KeyTypeAssetToOrderIndex, []byte(assetDenom), extraCap) } -// indexPrefixAssetToOrderOfType creates the prefix for asset to order index entries for the given asset denom and order type. -func indexPrefixAssetToOrderOfType(assetDenom string, orderTypeByte byte, extraCap int) []byte { - rv := indexPrefixAssetToOrder(assetDenom, 1+extraCap) - rv = append(rv, orderTypeByte) - return rv -} - // GetIndexKeyPrefixAssetToOrder creates a key prefix for the asset to order index limited to the given asset. func GetIndexKeyPrefixAssetToOrder(assetDenom string) []byte { return indexPrefixAssetToOrder(assetDenom, 0) } -// GetIndexKeyPrefixAssetToOrderAsks creates a key prefix for the asset to orders limited to the given asset and only ask orders. -func GetIndexKeyPrefixAssetToOrderAsks(assetDenom string) []byte { - return indexPrefixAssetToOrderOfType(assetDenom, OrderKeyTypeAsk, 0) -} - -// GetIndexKeyPrefixAssetToOrderBids creates a key prefix for the asset to orders limited to the given asset and only bid orders. -func GetIndexKeyPrefixAssetToOrderBids(assetDenom string) []byte { - return indexPrefixAssetToOrderOfType(assetDenom, OrderKeyTypeBid, 0) -} - // MakeIndexKeyAssetToOrder creates the key to use for the asset to order index for the provided values. -func MakeIndexKeyAssetToOrder(assetDenom string, orderTypeByte byte, orderID uint64) []byte { +func MakeIndexKeyAssetToOrder(assetDenom string, orderID uint64) []byte { suffix := uint64Bz(orderID) - rv := indexPrefixAssetToOrderOfType(assetDenom, orderTypeByte, len(suffix)) + rv := indexPrefixAssetToOrder(assetDenom, len(suffix)) rv = append(rv, suffix...) return rv } // ParseIndexKeyAssetToOrder extracts the denom, type byte, and order id from an asset to order index key. // The input can have the following formats: -// - | | | -// - | | -// - | +// - | | +// - | // - // // In the case where just the is provided, the returned denom will be "", and type byte will be 0. // In the case where just the and are provided, the returned denom will be "". -func ParseIndexKeyAssetToOrder(key []byte) (string, byte, uint64, error) { +func ParseIndexKeyAssetToOrder(key []byte) (string, uint64, error) { if len(key) < 8 { - return "", 0, 0, fmt.Errorf("cannot parse asset to order key: only has %d bytes, expected at least 8", len(key)) + return "", 0, fmt.Errorf("cannot parse asset to order key: only has %d bytes, expected at least 8", len(key)) } + unparsed, orderIDBz := key[:len(key)-8], key[len(key)-8:] - var denom string - var typeByte byte orderID, _ := uint64FromBz(orderIDBz) - if len(unparsed) > 0 { - typeByte = unparsed[len(unparsed)-1] - unparsed = unparsed[:len(unparsed)-1] - } + + var denom string if len(unparsed) > 0 { if unparsed[0] == KeyTypeAssetToOrderIndex { unparsed = unparsed[1:] } denom = string(unparsed) } - return denom, typeByte, orderID, nil + return denom, orderID, nil } diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 3154ef2fed..4bda45c0c1 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -3463,213 +3463,54 @@ func TestGetIndexKeyPrefixAssetToOrder(t *testing.T) { } } -func TestGetIndexKeyPrefixAssetToOrderAsks(t *testing.T) { - orderKeyType := keeper.OrderKeyTypeAsk +func TestMakeIndexKeyAssetToOrder(t *testing.T) { tests := []struct { name string assetDenom string + orderID uint64 expected []byte + expPanic string }{ { - name: "empty", + name: "no asset order 0", assetDenom: "", - expected: []byte{keeper.KeyTypeAssetToOrderIndex, orderKeyType}, - }, - { - name: "1 char denom", - assetDenom: "p", - expected: []byte{keeper.KeyTypeAssetToOrderIndex, 'p', orderKeyType}, + orderID: 0, + expected: []byte{keeper.KeyTypeAssetToOrderIndex, 0, 0, 0, 0, 0, 0, 0, 0}, }, { - name: "nhash", + name: "nhash order 1", assetDenom: "nhash", - expected: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', orderKeyType}, + orderID: 1, + expected: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', 0, 0, 0, 0, 0, 0, 0, 1}, }, { - name: "hex string", + name: "hex string order 5", assetDenom: hexString, + orderID: 5, expected: concatBz( []byte{keeper.KeyTypeAssetToOrderIndex}, []byte(hexString), - []byte{orderKeyType}, + []byte{0, 0, 0, 0, 0, 0, 0, 5}, ), }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - ktc := keyTestCase{ - maker: func() []byte { - return keeper.GetIndexKeyPrefixAssetToOrderAsks(tc.assetDenom) - }, - expected: tc.expected, - expPrefixes: []expectedPrefix{ - {name: "GetIndexKeyPrefixAssetToOrder", value: keeper.GetIndexKeyPrefixAssetToOrder(tc.assetDenom)}, - }, - } - - checkKey(t, ktc, "GetIndexKeyPrefixAssetToOrderAsks(%q)", tc.assetDenom) - }) - } -} - -func TestGetIndexKeyPrefixAssetToOrderBids(t *testing.T) { - orderKeyType := keeper.OrderKeyTypeBid - tests := []struct { - name string - assetDenom string - expected []byte - }{ - { - name: "empty", - assetDenom: "", - expected: []byte{keeper.KeyTypeAssetToOrderIndex, orderKeyType}, - }, { - name: "1 char denom", - assetDenom: "p", - expected: []byte{keeper.KeyTypeAssetToOrderIndex, 'p', orderKeyType}, - }, - { - name: "nhash", + name: "nhash order 4,294,967,296", assetDenom: "nhash", - expected: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', orderKeyType}, - }, - { - name: "hex string", - assetDenom: hexString, - expected: concatBz( - []byte{keeper.KeyTypeAssetToOrderIndex}, - []byte(hexString), - []byte{orderKeyType}, - ), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - ktc := keyTestCase{ - maker: func() []byte { - return keeper.GetIndexKeyPrefixAssetToOrderBids(tc.assetDenom) - }, - expected: tc.expected, - expPrefixes: []expectedPrefix{ - {name: "GetIndexKeyPrefixAssetToOrder", value: keeper.GetIndexKeyPrefixAssetToOrder(tc.assetDenom)}, - }, - } - - checkKey(t, ktc, "GetIndexKeyPrefixAssetToOrderBids(%q)", tc.assetDenom) - }) - } -} - -func TestMakeIndexKeyAssetToOrder(t *testing.T) { - tests := []struct { - name string - assetDenom string - orderTypeByte byte - orderID uint64 - expected []byte - expPanic string - }{ - { - name: "no asset order 0 ask", - assetDenom: "", - orderTypeByte: keeper.OrderKeyTypeAsk, - orderID: 0, + orderID: 4_294_967_296, expected: []byte{ keeper.KeyTypeAssetToOrderIndex, - keeper.OrderKeyTypeAsk, 0, 0, 0, 0, 0, 0, 0, 0, + 'n', 'h', 'a', 's', 'h', + 0, 0, 0, 1, 0, 0, 0, 0, }, }, { - name: "no asset order 0 bid", - assetDenom: "", - orderTypeByte: keeper.OrderKeyTypeBid, - orderID: 0, + name: "nhash order max", + assetDenom: "nhash", + orderID: 18_446_744_073_709_551_615, expected: []byte{ keeper.KeyTypeAssetToOrderIndex, - keeper.OrderKeyTypeBid, 0, 0, 0, 0, 0, 0, 0, 0, - }, - }, - { - name: "nhash order 1 ask", - assetDenom: "nhash", - orderTypeByte: keeper.OrderKeyTypeAsk, - orderID: 1, - expected: []byte{ - keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', - keeper.OrderKeyTypeAsk, 0, 0, 0, 0, 0, 0, 0, 1, - }, - }, - { - name: "nhash order 1 bid", - assetDenom: "nhash", - orderTypeByte: keeper.OrderKeyTypeBid, - orderID: 1, - expected: []byte{ - keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', - keeper.OrderKeyTypeBid, 0, 0, 0, 0, 0, 0, 0, 1, - }, - }, - { - name: "hex string order 5 ask", - assetDenom: hexString, - orderTypeByte: keeper.OrderKeyTypeAsk, - orderID: 5, - expected: concatBz( - []byte{keeper.KeyTypeAssetToOrderIndex}, []byte(hexString), - []byte{keeper.OrderKeyTypeAsk, 0, 0, 0, 0, 0, 0, 0, 5}, - ), - }, - { - name: "hex string order 5 bid", - assetDenom: hexString, - orderTypeByte: keeper.OrderKeyTypeBid, - orderID: 5, - expected: concatBz( - []byte{keeper.KeyTypeAssetToOrderIndex}, []byte(hexString), - []byte{keeper.OrderKeyTypeBid, 0, 0, 0, 0, 0, 0, 0, 5}, - ), - }, - { - name: "nhash order 4,294,967,296 ask", - assetDenom: "nhash", - orderTypeByte: keeper.OrderKeyTypeAsk, - orderID: 4_294_967_296, - expected: []byte{ - keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', - keeper.OrderKeyTypeAsk, 0, 0, 0, 1, 0, 0, 0, 0, - }, - }, - { - name: "nhash order 4,294,967,296 bid", - assetDenom: "nhash", - orderTypeByte: keeper.OrderKeyTypeBid, - orderID: 4_294_967_296, - expected: []byte{ - keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', - keeper.OrderKeyTypeBid, 0, 0, 0, 1, 0, 0, 0, 0, - }, - }, - { - name: "nhash order max ask", - assetDenom: "nhash", - orderTypeByte: keeper.OrderKeyTypeAsk, - orderID: 18_446_744_073_709_551_615, - expected: []byte{ - keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', - keeper.OrderKeyTypeAsk, 255, 255, 255, 255, 255, 255, 255, 255, - }, - }, - { - name: "nhash order max bid", - assetDenom: "nhash", - orderTypeByte: keeper.OrderKeyTypeBid, - orderID: 18_446_744_073_709_551_615, - expected: []byte{ - keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', - keeper.OrderKeyTypeBid, 255, 255, 255, 255, 255, 255, 255, 255, + 'n', 'h', 'a', 's', 'h', + 255, 255, 255, 255, 255, 255, 255, 255, }, }, } @@ -3678,7 +3519,7 @@ func TestMakeIndexKeyAssetToOrder(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ktc := keyTestCase{ maker: func() []byte { - return keeper.MakeIndexKeyAssetToOrder(tc.assetDenom, tc.orderTypeByte, tc.orderID) + return keeper.MakeIndexKeyAssetToOrder(tc.assetDenom, tc.orderID) }, expected: tc.expected, expPanic: tc.expPanic, @@ -3687,35 +3528,20 @@ func TestMakeIndexKeyAssetToOrder(t *testing.T) { ktc.expPrefixes = []expectedPrefix{ {name: "GetIndexKeyPrefixAssetToOrder", value: keeper.GetIndexKeyPrefixAssetToOrder(tc.assetDenom)}, } - switch tc.orderTypeByte { - case keeper.OrderKeyTypeAsk: - ktc.expPrefixes = append(ktc.expPrefixes, expectedPrefix{ - name: "GetIndexKeyPrefixAssetToOrderAsks", - value: keeper.GetIndexKeyPrefixAssetToOrderAsks(tc.assetDenom), - }) - case keeper.OrderKeyTypeBid: - ktc.expPrefixes = append(ktc.expPrefixes, expectedPrefix{ - name: "GetIndexKeyPrefixAssetToOrderBids", - value: keeper.GetIndexKeyPrefixAssetToOrderBids(tc.assetDenom), - }) - default: - assert.Fail(t, "no expected prefix case defined for type byte %#x", tc.orderTypeByte) - } } - checkKey(t, ktc, "MakeIndexKeyAssetToOrder(%q, %#x, %d)", tc.assetDenom, tc.orderTypeByte, tc.orderID) + checkKey(t, ktc, "MakeIndexKeyAssetToOrder(%q, %d)", tc.assetDenom, tc.orderID) }) } } func TestParseIndexKeyAssetToOrder(t *testing.T) { tests := []struct { - name string - key []byte - expDenom string - expTypeByte byte - expOrderID uint64 - expErr string + name string + key []byte + expDenom string + expOrderID uint64 + expErr string }{ { name: "nil key", @@ -3743,58 +3569,46 @@ func TestParseIndexKeyAssetToOrder(t *testing.T) { expOrderID: 578_437_695_752_307_201, }, { - name: "order type byte 99 order id 578,437,695,752,307,201", - key: []byte{99, 8, 7, 6, 5, 4, 3, 2, 1}, - expTypeByte: 99, - expOrderID: 578_437_695_752_307_201, - }, - { - name: "nhash order type byte 99 order id 578,437,695,752,307,201", - key: []byte{'n', 'h', 'a', 's', 'h', 99, 8, 7, 6, 5, 4, 3, 2, 1}, - expDenom: "nhash", - expTypeByte: 99, - expOrderID: 578_437_695_752_307_201, + name: "nhash order id 578,437,695,752,307,201", + key: []byte{'n', 'h', 'a', 's', 'h', 8, 7, 6, 5, 4, 3, 2, 1}, + expDenom: "nhash", + expOrderID: 578_437_695_752_307_201, }, { - name: "hex string order type byte 99 order id 578,437,695,752,307,201", - key: append([]byte(hexString), 99, 8, 7, 6, 5, 4, 3, 2, 1), - expDenom: hexString, - expTypeByte: 99, - expOrderID: 578_437_695_752_307_201, + name: "hex string order id 578,437,695,752,307,201", + key: append([]byte(hexString), 8, 7, 6, 5, 4, 3, 2, 1), + expDenom: hexString, + expOrderID: 578_437_695_752_307_201, }, { - name: "with type byte nhash order type byte 99 order id 578,437,695,752,307,201", - key: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', 99, 8, 7, 6, 5, 4, 3, 2, 1}, - expDenom: "nhash", - expTypeByte: 99, - expOrderID: 578_437_695_752_307_201, + name: "with type byte nhash order id 578,437,695,752,307,201", + key: []byte{keeper.KeyTypeAssetToOrderIndex, 'n', 'h', 'a', 's', 'h', 8, 7, 6, 5, 4, 3, 2, 1}, + expDenom: "nhash", + expOrderID: 578_437_695_752_307_201, }, { - name: "with type byte hex string order type byte 99 order id 578,437,695,752,307,201", + name: "with type byte hex string order id 578,437,695,752,307,201", key: concatBz( []byte{keeper.KeyTypeAssetToOrderIndex}, []byte(hexString), - []byte{99, 8, 7, 6, 5, 4, 3, 2, 1}, + []byte{8, 7, 6, 5, 4, 3, 2, 1}, ), - expDenom: hexString, - expTypeByte: 99, - expOrderID: 578_437_695_752_307_201, + expDenom: hexString, + expOrderID: 578_437_695_752_307_201, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { var denom string - var typeByte byte var orderiD uint64 var err error testFunc := func() { - denom, typeByte, orderiD, err = keeper.ParseIndexKeyAssetToOrder(tc.key) + denom, orderiD, err = keeper.ParseIndexKeyAssetToOrder(tc.key) } require.NotPanics(t, testFunc, "ParseIndexKeyAssetToOrder(%v)", tc.key) assertions.AssertErrorValue(t, err, tc.expErr, "ParseIndexKeyAssetToOrder(%v) error", tc.key) assert.Equal(t, tc.expDenom, denom, "ParseIndexKeyAssetToOrder(%v) denom", tc.key) - assert.Equal(t, tc.expTypeByte, typeByte, "ParseIndexKeyAssetToOrder(%v) type byte", tc.key) assert.Equal(t, tc.expOrderID, orderiD, "ParseIndexKeyAssetToOrder(%v) order id", tc.key) }) } diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 8da7802638..f3c498a542 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -102,8 +102,8 @@ func createIndexEntries(order exchange.Order) []sdk.KVPair { Value: []byte{orderTypeByte}, }, { - Key: MakeIndexKeyAssetToOrder(assets.Denom, orderTypeByte, orderID), - Value: nil, + Key: MakeIndexKeyAssetToOrder(assets.Denom, orderID), + Value: []byte{orderTypeByte}, }, } } @@ -197,70 +197,34 @@ func (k Keeper) IterateOrders(ctx sdk.Context, cb func(order *exchange.Order) bo return errors.Join(errs...) } -// IterateMarketOrders iterates over all orders for a market. +// iterateOrderIndex iterates over a to order index with keys that have the provided prefixBz. // The callback takes in the order id and order type byte and should return whether to stop iterating. -func (k Keeper) IterateMarketOrders(ctx sdk.Context, marketID uint32, cb func(orderID uint64, orderTypeByte byte) bool) { - k.iterate(ctx, GetIndexKeyPrefixMarketToOrder(marketID), func(key, value []byte) bool { +func (k Keeper) iterateOrderIndex(ctx sdk.Context, prefixBz []byte, cb func(orderID uint64, orderTypeByte byte) bool) { + k.iterate(ctx, prefixBz, func(key, value []byte) bool { if len(value) == 0 { return false } - _, orderID, err := ParseIndexKeyMarketToOrder(key) - if err != nil { - return false - } - return cb(orderID, value[0]) + orderID, ok := ParseIndexKeySuffixOrderID(key) + return ok && cb(orderID, value[0]) }) } +// IterateMarketOrders iterates over all orders for a market. +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateMarketOrders(ctx sdk.Context, marketID uint32, cb func(orderID uint64, orderTypeByte byte) bool) { + k.iterateOrderIndex(ctx, GetIndexKeyPrefixMarketToOrder(marketID), cb) +} + // IterateAddressOrders iterates over all orders for an address. // The callback takes in the order id and order type byte and should return whether to stop iterating. func (k Keeper) IterateAddressOrders(ctx sdk.Context, addr sdk.AccAddress, cb func(orderID uint64, orderTypeByte byte) bool) { - k.iterate(ctx, GetIndexKeyPrefixAddressToOrder(addr), func(key, value []byte) bool { - if len(value) == 0 { - return false - } - _, orderID, err := ParseIndexKeyAddressToOrder(key) - if err != nil { - return false - } - return cb(orderID, value[0]) - }) + k.iterateOrderIndex(ctx, GetIndexKeyPrefixAddressToOrder(addr), cb) } // IterateAssetOrders iterates over all orders for a given asset denom. // The callback takes in the order id and order type byte and should return whether to stop iterating. func (k Keeper) IterateAssetOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64, orderTypeByte byte) bool) { - k.iterate(ctx, GetIndexKeyPrefixAssetToOrder(assetDenom), func(key, _ []byte) bool { - _, orderTypeByte, orderID, err := ParseIndexKeyAssetToOrder(key) - if err != nil { - return false - } - return cb(orderID, orderTypeByte) - }) -} - -// IterateAssetAskOrders iterates over all ask orders for a given asset denom. -// The callback takes in the order id and should return whether to stop iterating. -func (k Keeper) IterateAssetAskOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64) bool) { - k.iterate(ctx, GetIndexKeyPrefixAssetToOrderAsks(assetDenom), func(key, _ []byte) bool { - _, _, orderID, err := ParseIndexKeyAssetToOrder(key) - if err != nil { - return false - } - return cb(orderID) - }) -} - -// IterateAssetBidOrders iterates over all bid orders for a given asset denom. -// The callback takes in the order id and should return whether to stop iterating. -func (k Keeper) IterateAssetBidOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64) bool) { - k.iterate(ctx, GetIndexKeyPrefixAssetToOrderBids(assetDenom), func(key, _ []byte) bool { - _, _, orderID, err := ParseIndexKeyAssetToOrder(key) - if err != nil { - return false - } - return cb(orderID) - }) + k.iterateOrderIndex(ctx, GetIndexKeyPrefixAssetToOrder(assetDenom), cb) } // placeHoldOnOrder places a hold on an order's funds in the owner's account. From 8924f21f1af729e8e010901b2556961a2105ab4d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 2 Oct 2023 11:09:18 -0600 Subject: [PATCH 216/309] [1658]: Extract the pagination stuff to a new GetPageOfOrdersFromIndex func (from inside QueryGetAssetOrders). --- x/exchange/keeper/grpc_query.go | 48 +++-------------------------- x/exchange/keeper/orders.go | 54 +++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 43 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 58274e2e50..bbbeb2e789 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -3,7 +3,6 @@ package keeper import ( "context" "fmt" - "strings" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -171,53 +170,16 @@ func (k QueryServer) QueryGetAssetOrders(goCtx context.Context, req *exchange.Qu return nil, status.Error(codes.InvalidArgument, "empty request") } - var orderTypeByte byte - filterByType := false - if len(req.OrderType) > 0 { - orderType := strings.ToLower(req.OrderType) - // only look at the first 3 chars to handle stuff like "asks" or "bidOrders" too. - if len(orderType) > 3 { - orderType = orderType[:3] - } - switch orderType { - case exchange.OrderTypeAsk: - orderTypeByte = OrderKeyTypeAsk - case exchange.OrderTypeBid: - orderTypeByte = OrderKeyTypeBid - default: - return nil, status.Errorf(codes.InvalidArgument, "unknown order type %q", req.OrderType) - } - filterByType = true - } - ctx := sdk.UnwrapSDKContext(goCtx) pre := GetIndexKeyPrefixAssetToOrder(req.Asset) store := prefix.NewStore(k.getStore(ctx), pre) - resp := &exchange.QueryGetAssetOrdersResponse{} - var pageErr error - resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { - if filterByType && (len(value) == 0 || value[0] != orderTypeByte) { - return false, nil - } - // If we can't get the order id from the key, just pretend like it doesn't exist. - _, orderID, perr := ParseIndexKeyAssetToOrder(key) - if perr != nil { - return false, nil - } - if accumulate { - // Only add them to the result if we can read it. - // This might result in fewer results than the limit, but at least one bad entry won't block others. - order, oerr := k.parseOrderStoreValue(orderID, value) - if oerr != nil { - resp.Orders = append(resp.Orders, order) - } - } - return true, nil - }) + resp := &exchange.QueryGetAssetOrdersResponse{} + var err error + resp.Pagination, resp.Orders, err = k.GetPageOfOrdersFromIndex(store, req.Pagination, req.OrderType) - if pageErr != nil { - return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for asset %s: %v", req.Asset, pageErr) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for asset %s: %v", req.Asset, err) } return resp, nil diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index f3c498a542..10b2fba55b 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -3,9 +3,14 @@ package keeper import ( "errors" "fmt" + "strings" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" "github.com/provenance-io/provenance/x/exchange" ) @@ -227,6 +232,55 @@ func (k Keeper) IterateAssetOrders(ctx sdk.Context, assetDenom string, cb func(o k.iterateOrderIndex(ctx, GetIndexKeyPrefixAssetToOrder(assetDenom), cb) } +// GetPageOfOrdersFromIndex gets a page of orders using a to order index. +func (k Keeper) GetPageOfOrdersFromIndex( + prefixStore sdk.KVStore, + pageReq *query.PageRequest, + orderType string, +) (*query.PageResponse, []*exchange.Order, error) { + var orderTypeByte byte + filterByType := false + if len(orderType) > 0 { + ot := strings.ToLower(orderType) + // only look at the first 3 chars to handle stuff like "asks" or "bidOrders" too. + if len(ot) > 3 { + ot = ot[:3] + } + switch ot { + case exchange.OrderTypeAsk: + orderTypeByte = OrderKeyTypeAsk + case exchange.OrderTypeBid: + orderTypeByte = OrderKeyTypeBid + default: + return nil, nil, status.Errorf(codes.InvalidArgument, "unknown order type %q", orderType) + } + filterByType = true + } + + var orders []*exchange.Order + pageResp, pageErr := query.FilteredPaginate(prefixStore, pageReq, func(key []byte, value []byte, accumulate bool) (bool, error) { + if filterByType && (len(value) == 0 || value[0] != orderTypeByte) { + return false, nil + } + // If we can't get the order id from the key, just pretend like it doesn't exist. + orderID, ok := ParseIndexKeySuffixOrderID(key) + if !ok { + return false, nil + } + if accumulate { + // Only add them to the result if we can read it. + // This might result in fewer results than the limit, but at least one bad entry won't block others. + order, oerr := k.parseOrderStoreValue(orderID, value) + if oerr != nil { + orders = append(orders, order) + } + } + return true, nil + }) + + return pageResp, orders, pageErr +} + // placeHoldOnOrder places a hold on an order's funds in the owner's account. func (k Keeper) placeHoldOnOrder(ctx sdk.Context, order *exchange.Order) error { orderID := order.OrderId From 3568831b952c470ff8e0a0ee8c5e683e1f235add Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 2 Oct 2023 11:17:57 -0600 Subject: [PATCH 217/309] [1658]: Add the order type field to QueryGetMarketOrders and QueryGetOwnerOrders. --- docs/proto-docs.md | 10 +- proto/provenance/exchange/v1/query.proto | 6 +- x/exchange/keeper/grpc_query.go | 50 +---- x/exchange/query.pb.go | 236 ++++++++++++++++------- 4 files changed, 188 insertions(+), 114 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index dd00797ea9..b3fc540a13 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1963,9 +1963,8 @@ QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders en | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `market_id` | [uint32](#uint32) | | market_id is the id of the market to get all the orders for. - -TODO[1658]: Add an order_type field to QueryGetMarketOrdersRequest | +| `market_id` | [uint32](#uint32) | | market_id is the id of the market to get all the orders for. | +| `order_type` | [string](#string) | | order_type is optional and can limit orders to only "ask" or "bid" orders. | | `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | @@ -2027,9 +2026,8 @@ QueryGetOwnerOrdersRequest is a request message for the QueryGetOwnerOrders endp | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `owner` | [string](#string) | | owner is the bech32 address string of the owner to get the orders for. - -TODO[1658]: Add an order_type field to QueryGetOwnerOrdersRequest | +| `owner` | [string](#string) | | owner is the bech32 address string of the owner to get the orders for. | +| `order_type` | [string](#string) | | order_type is optional and can limit orders to only "ask" or "bid" orders. | | `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 27e77b82f2..53c2cb45db 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -112,7 +112,8 @@ message QueryGetOrderResponse { message QueryGetMarketOrdersRequest { // market_id is the id of the market to get all the orders for. uint32 market_id = 1; - // TODO[1658]: Add an order_type field to QueryGetMarketOrdersRequest + // order_type is optional and can limit orders to only "ask" or "bid" orders. + string order_type = 2; // pagination defines an optional pagination for the request. cosmos.base.query.v1beta1.PageRequest pagination = 99; @@ -131,7 +132,8 @@ message QueryGetMarketOrdersResponse { message QueryGetOwnerOrdersRequest { // owner is the bech32 address string of the owner to get the orders for. string owner = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - // TODO[1658]: Add an order_type field to QueryGetOwnerOrdersRequest + // order_type is optional and can limit orders to only "ask" or "bid" orders. + string order_type = 2; // pagination defines an optional pagination for the request. cosmos.base.query.v1beta1.PageRequest pagination = 99; diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index bbbeb2e789..07b120cc44 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -96,28 +96,13 @@ func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.Q ctx := sdk.UnwrapSDKContext(goCtx) pre := GetIndexKeyPrefixMarketToOrder(req.MarketId) store := prefix.NewStore(k.getStore(ctx), pre) - resp := &exchange.QueryGetMarketOrdersResponse{} - var pageErr error - resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { - // If we can't get the order id from the key, just pretend like it doesn't exist. - _, orderID, perr := ParseIndexKeyMarketToOrder(key) - if perr != nil { - return false, nil - } - if accumulate { - // Only add them to the result if we can read it. - // This might result in fewer results than the limit, but at least one bad entry won't block others. - order, oerr := k.parseOrderStoreValue(orderID, value) - if oerr != nil { - resp.Orders = append(resp.Orders, order) - } - } - return true, nil - }) + resp := &exchange.QueryGetMarketOrdersResponse{} + var err error + resp.Pagination, resp.Orders, err = k.GetPageOfOrdersFromIndex(store, req.Pagination, req.OrderType) - if pageErr != nil { - return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for market %d: %v", req.MarketId, pageErr) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for market %d: %v", req.MarketId, err) } return resp, nil @@ -137,28 +122,13 @@ func (k QueryServer) QueryGetOwnerOrders(goCtx context.Context, req *exchange.Qu ctx := sdk.UnwrapSDKContext(goCtx) pre := GetIndexKeyPrefixAddressToOrder(owner) store := prefix.NewStore(k.getStore(ctx), pre) - resp := &exchange.QueryGetOwnerOrdersResponse{} - var pageErr error - resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { - // If we can't get the order id from the key, just pretend like it doesn't exist. - _, orderID, perr := ParseIndexKeyAddressToOrder(key) - if perr != nil { - return false, nil - } - if accumulate { - // Only add them to the result if we can read it. - // This might result in fewer results than the limit, but at least one bad entry won't block others. - order, oerr := k.parseOrderStoreValue(orderID, value) - if oerr != nil { - resp.Orders = append(resp.Orders, order) - } - } - return true, nil - }) + resp := &exchange.QueryGetOwnerOrdersResponse{} + var err error + resp.Pagination, resp.Orders, err = k.GetPageOfOrdersFromIndex(store, req.Pagination, req.OrderType) - if pageErr != nil { - return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for owner %s: %v", req.Owner, pageErr) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for owner %s: %v", req.Owner, err) } return resp, nil diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index 2bec0ec3ec..25520170fb 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -259,6 +259,8 @@ func (m *QueryGetOrderResponse) GetOrder() *Order { type QueryGetMarketOrdersRequest struct { // market_id is the id of the market to get all the orders for. MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // order_type is optional and can limit orders to only "ask" or "bid" orders. + OrderType string `protobuf:"bytes,2,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` // pagination defines an optional pagination for the request. Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` } @@ -303,6 +305,13 @@ func (m *QueryGetMarketOrdersRequest) GetMarketId() uint32 { return 0 } +func (m *QueryGetMarketOrdersRequest) GetOrderType() string { + if m != nil { + return m.OrderType + } + return "" +} + func (m *QueryGetMarketOrdersRequest) GetPagination() *query.PageRequest { if m != nil { return m.Pagination @@ -369,6 +378,8 @@ func (m *QueryGetMarketOrdersResponse) GetPagination() *query.PageResponse { type QueryGetOwnerOrdersRequest struct { // owner is the bech32 address string of the owner to get the orders for. Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` + // order_type is optional and can limit orders to only "ask" or "bid" orders. + OrderType string `protobuf:"bytes,2,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` // pagination defines an optional pagination for the request. Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` } @@ -413,6 +424,13 @@ func (m *QueryGetOwnerOrdersRequest) GetOwner() string { return "" } +func (m *QueryGetOwnerOrdersRequest) GetOrderType() string { + if m != nil { + return m.OrderType + } + return "" +} + func (m *QueryGetOwnerOrdersRequest) GetPagination() *query.PageRequest { if m != nil { return m.Pagination @@ -1040,72 +1058,72 @@ var fileDescriptor_00949b75b1c10bfe = []byte{ 0x17, 0xce, 0x24, 0x4d, 0x7f, 0xf1, 0x44, 0xd1, 0x4f, 0x0c, 0x6e, 0xb1, 0x37, 0xa9, 0xeb, 0x6e, 0x51, 0x89, 0x4c, 0xb2, 0x13, 0xdb, 0x6d, 0xa1, 0x87, 0x0a, 0x25, 0x91, 0x1c, 0x45, 0xa2, 0x6a, 0xba, 0x20, 0x0e, 0x1c, 0x30, 0x63, 0xfb, 0x65, 0xbb, 0x8a, 0xbd, 0xe3, 0xee, 0x6e, 0x4c, 0xa3, - 0x28, 0x97, 0xfe, 0x01, 0x08, 0x09, 0x0e, 0x20, 0x44, 0xc5, 0x01, 0x71, 0xe6, 0xd0, 0x23, 0x7f, - 0x40, 0x8e, 0x15, 0xbd, 0x70, 0x42, 0x28, 0xe1, 0xaf, 0xe0, 0x84, 0x76, 0x66, 0x36, 0xde, 0x8d, - 0xed, 0xf5, 0x1a, 0x15, 0x29, 0x27, 0xef, 0x8e, 0xdf, 0xf7, 0xde, 0x37, 0xdf, 0xcc, 0xbc, 0x6f, - 0x16, 0xeb, 0x5d, 0x97, 0xf7, 0xc0, 0x61, 0x4e, 0x13, 0x28, 0x3c, 0x6d, 0x3e, 0x66, 0x8e, 0x05, - 0xb4, 0x57, 0xa6, 0x4f, 0xf6, 0xc1, 0x3d, 0x30, 0xba, 0x2e, 0xf7, 0x39, 0xb9, 0xda, 0x8f, 0x31, - 0xc2, 0x18, 0xa3, 0x57, 0xd6, 0xf2, 0x4d, 0xee, 0x75, 0xb8, 0x57, 0x17, 0x51, 0x54, 0xbe, 0x48, - 0x88, 0x56, 0x92, 0x6f, 0xb4, 0xc1, 0x3c, 0x90, 0xb9, 0x68, 0xaf, 0xdc, 0x00, 0x9f, 0x95, 0x69, - 0x97, 0x59, 0xb6, 0xc3, 0x7c, 0x9b, 0x3b, 0x2a, 0xb6, 0x10, 0x8d, 0x0d, 0xa3, 0x9a, 0xdc, 0x0e, - 0xff, 0x5f, 0xb2, 0x38, 0xb7, 0xda, 0x40, 0x59, 0xd7, 0xa6, 0xcc, 0x71, 0xb8, 0x2f, 0xc0, 0x61, - 0xa5, 0xac, 0xc5, 0x2d, 0x2e, 0x19, 0x04, 0x4f, 0x6a, 0xf4, 0xe6, 0x88, 0x69, 0x71, 0xb7, 0x05, - 0xae, 0x82, 0xea, 0xdf, 0x22, 0x9c, 0x7b, 0x14, 0x70, 0x7b, 0x18, 0x8c, 0xd6, 0x00, 0x36, 0x59, - 0xbb, 0x69, 0xc2, 0x93, 0x7d, 0xf0, 0x7c, 0x72, 0x1f, 0x67, 0x98, 0xb7, 0x57, 0x17, 0x80, 0xdc, - 0x74, 0x11, 0x2d, 0xcf, 0x57, 0x8a, 0xc6, 0x70, 0x21, 0x8c, 0x75, 0x6f, 0x4f, 0xa4, 0x30, 0xe7, - 0x98, 0x7a, 0x0a, 0xe0, 0x0d, 0xbb, 0xa5, 0xe0, 0x33, 0xc9, 0xf0, 0x0d, 0xbb, 0xa5, 0xe0, 0x0d, - 0xf5, 0xa4, 0xff, 0x32, 0x8d, 0xf3, 0x43, 0xa8, 0x79, 0x5d, 0xee, 0x78, 0x40, 0x1e, 0xe1, 0x6c, - 0xd3, 0x05, 0x21, 0x43, 0x7d, 0x17, 0xa0, 0xce, 0xbb, 0x42, 0x91, 0x1c, 0x2a, 0xce, 0x2c, 0xcf, - 0x57, 0xf2, 0x86, 0x5a, 0x8a, 0x40, 0x50, 0x43, 0x09, 0x6a, 0x6c, 0x72, 0xdb, 0xd9, 0xb8, 0x74, - 0xfc, 0xc7, 0xf5, 0x29, 0x93, 0x84, 0xe0, 0x1a, 0xc0, 0x43, 0x09, 0x25, 0x9f, 0xe1, 0x45, 0x0f, - 0x7c, 0xbf, 0x0d, 0x1d, 0x70, 0xfc, 0xfa, 0x6e, 0x9b, 0xf9, 0xb1, 0xcc, 0xd3, 0xe9, 0x32, 0xe7, - 0xfa, 0x39, 0x6a, 0x6d, 0xe6, 0x47, 0xf2, 0x7f, 0x8e, 0x97, 0x22, 0xf9, 0xdd, 0xa0, 0x7c, 0xac, - 0xc0, 0x4c, 0xba, 0x02, 0xf9, 0x7e, 0x12, 0x33, 0xc8, 0xd1, 0xaf, 0xa0, 0x97, 0x71, 0x56, 0x28, - 0xb6, 0x05, 0xbe, 0x54, 0x53, 0x2d, 0x64, 0x1e, 0xcf, 0x89, 0x55, 0xa8, 0xdb, 0xad, 0x1c, 0x2a, - 0xa2, 0xe5, 0x4b, 0xe6, 0xff, 0xc4, 0xfb, 0x76, 0x4b, 0xff, 0x10, 0x5f, 0x39, 0x07, 0x51, 0x02, - 0x57, 0xf1, 0xac, 0x5c, 0x39, 0x24, 0x56, 0xee, 0xda, 0xa8, 0x95, 0x93, 0x28, 0x19, 0xab, 0x3f, - 0x43, 0x78, 0x31, 0x4c, 0xf7, 0x80, 0xb9, 0x7b, 0x2a, 0xa9, 0x17, 0x12, 0x59, 0xc4, 0x99, 0x8e, - 0x18, 0x0e, 0x99, 0x2c, 0x98, 0x73, 0x72, 0x60, 0xbb, 0x45, 0x6a, 0x18, 0xf7, 0x0f, 0x46, 0xae, - 0x29, 0xca, 0xde, 0x8a, 0xa9, 0x21, 0x4f, 0x64, 0xa8, 0xc9, 0x0e, 0xb3, 0x40, 0x25, 0x36, 0x23, - 0x48, 0xfd, 0x39, 0xc2, 0x4b, 0xc3, 0x49, 0xa8, 0xa9, 0xdd, 0xc1, 0x97, 0xe5, 0x21, 0x50, 0xbb, - 0x65, 0xcc, 0xdc, 0x54, 0x30, 0xd9, 0x1a, 0xc2, 0xef, 0x9d, 0xb1, 0xfc, 0x64, 0xcd, 0x18, 0xc1, - 0x6f, 0x10, 0xd6, 0xce, 0x44, 0xff, 0xc2, 0x01, 0x37, 0x2e, 0x92, 0x81, 0x67, 0x79, 0x30, 0x2a, - 0x04, 0xca, 0x6c, 0xe4, 0x7e, 0x7b, 0xb1, 0x9a, 0x55, 0x55, 0xd6, 0x5b, 0x2d, 0x17, 0x3c, 0xef, - 0x23, 0xdf, 0xb5, 0x1d, 0xcb, 0x94, 0x61, 0xaf, 0x4d, 0xb7, 0x1f, 0x22, 0x8b, 0x17, 0xa3, 0x75, - 0x41, 0x64, 0xfb, 0x2e, 0x22, 0xdb, 0xba, 0xe7, 0x9d, 0xdf, 0x5b, 0x59, 0x3c, 0xcb, 0x82, 0x51, - 0x29, 0x9b, 0x29, 0x5f, 0xc8, 0x35, 0x8c, 0xe5, 0xd6, 0xf7, 0x0f, 0xba, 0x20, 0x9a, 0x58, 0xc6, - 0xcc, 0x88, 0x91, 0x8f, 0x0f, 0xba, 0xf0, 0x9f, 0x68, 0x17, 0xe3, 0x76, 0x41, 0xb4, 0x6b, 0xa8, - 0x36, 0x1f, 0xd0, 0x6b, 0xb7, 0xe3, 0xc2, 0xbd, 0x2e, 0x0d, 0xbe, 0x47, 0xaa, 0x61, 0xc7, 0x8b, - 0x5c, 0x10, 0x05, 0xee, 0xe0, 0xab, 0x82, 0x9c, 0xec, 0x08, 0xdb, 0xce, 0x2e, 0x4f, 0xd3, 0x94, - 0xf4, 0x3c, 0x7e, 0x6b, 0x00, 0x26, 0xb3, 0xeb, 0x59, 0x4c, 0xc4, 0x5f, 0x3b, 0xcc, 0x65, 0x9d, - 0x50, 0x4d, 0xfd, 0x0a, 0x7e, 0x33, 0x36, 0xaa, 0x82, 0x75, 0x5c, 0x14, 0xc3, 0x9f, 0xb0, 0xb6, - 0xdd, 0x62, 0x3e, 0x6c, 0x06, 0xfe, 0x03, 0x32, 0x6b, 0x08, 0xbd, 0x89, 0x6f, 0x24, 0xc4, 0xa8, - 0x44, 0xf7, 0x71, 0x21, 0x16, 0xf4, 0x80, 0x39, 0xcc, 0x82, 0x1a, 0x40, 0xaa, 0x26, 0xab, 0xdf, - 0xc0, 0xd7, 0x47, 0xc2, 0x65, 0x85, 0xca, 0xdf, 0x0b, 0x78, 0x56, 0xc4, 0x90, 0x9f, 0x10, 0x7e, - 0x63, 0xc0, 0x82, 0xc9, 0xda, 0xa8, 0x95, 0x1b, 0x75, 0x91, 0xd0, 0xca, 0x13, 0x20, 0xd4, 0x34, - 0x4b, 0xcf, 0x5e, 0xfd, 0xf5, 0xf5, 0xf4, 0xdb, 0x44, 0xa7, 0x23, 0xae, 0x31, 0xbb, 0x00, 0x9e, - 0xbc, 0xcb, 0x90, 0xe7, 0x08, 0x2f, 0xc4, 0x4c, 0x8c, 0xac, 0x24, 0x16, 0x3c, 0x67, 0x8f, 0xda, - 0x6a, 0xca, 0x68, 0x45, 0x6d, 0x4d, 0x50, 0x2b, 0x91, 0x65, 0x9a, 0x74, 0xc3, 0xa2, 0x87, 0xa1, - 0xe5, 0x1e, 0x91, 0x5f, 0x51, 0xdf, 0x98, 0xa3, 0x8e, 0x44, 0xaa, 0xe3, 0x2a, 0x0f, 0x31, 0x51, - 0xed, 0xf6, 0x64, 0x20, 0xc5, 0xfa, 0x9e, 0x60, 0x5d, 0x25, 0xe5, 0x51, 0xac, 0xe5, 0x16, 0xa1, - 0x87, 0x67, 0x7b, 0xe7, 0x48, 0x5d, 0x15, 0xc9, 0x0b, 0xa4, 0xf6, 0x74, 0xdc, 0x18, 0x48, 0x65, - 0xac, 0x6e, 0x03, 0xe6, 0xa6, 0x55, 0x27, 0xc2, 0x28, 0xee, 0xb7, 0x05, 0x77, 0x83, 0xac, 0x24, - 0x2a, 0xee, 0x51, 0xe1, 0x87, 0xf4, 0x50, 0xfc, 0x1c, 0xc5, 0x68, 0x47, 0x7a, 0xf2, 0x78, 0xda, - 0x83, 0xe6, 0x32, 0x9e, 0xf6, 0x90, 0xa6, 0x9f, 0x9a, 0xb6, 0x70, 0x2a, 0x7a, 0x28, 0x7e, 0x8e, - 0xc8, 0x8f, 0xe1, 0xa1, 0x8b, 0xb6, 0xd1, 0x31, 0x87, 0x6e, 0x48, 0x5b, 0x1f, 0x73, 0xe8, 0x86, - 0xf5, 0x68, 0xfd, 0x96, 0x20, 0x5c, 0x24, 0x85, 0x64, 0xc2, 0xe4, 0x67, 0x84, 0xff, 0x7f, 0xae, - 0x2b, 0x12, 0x23, 0xb1, 0xdc, 0x40, 0xd7, 0xd5, 0x68, 0xea, 0x78, 0x45, 0xae, 0x22, 0xc8, 0xad, - 0x90, 0x52, 0xfa, 0x0d, 0x4c, 0xbe, 0x44, 0x78, 0x3e, 0xd2, 0x8d, 0x49, 0x29, 0xb1, 0x68, 0xac, - 0x91, 0x6b, 0xef, 0xa6, 0x8a, 0x4d, 0xab, 0x5c, 0x57, 0x12, 0x38, 0x0e, 0x3d, 0x72, 0x58, 0x8f, - 0x27, 0xef, 0x27, 0x96, 0x4c, 0xb0, 0x0e, 0xed, 0xde, 0xbf, 0x40, 0x2a, 0xea, 0x77, 0x05, 0xf5, - 0x35, 0x62, 0x8c, 0xa2, 0xde, 0x53, 0x68, 0x2a, 0xbe, 0x99, 0xa0, 0x2e, 0xf5, 0x25, 0xaf, 0x90, - 0xb2, 0xc6, 0x41, 0x2b, 0x21, 0x77, 0x53, 0xd1, 0x19, 0xb0, 0x2e, 0xed, 0xbd, 0x89, 0x71, 0x6a, - 0x12, 0x5b, 0x62, 0x12, 0xeb, 0xe4, 0x83, 0x09, 0xba, 0xdb, 0xd9, 0xbc, 0x3a, 0x22, 0x5f, 0xf0, - 0x2d, 0xe6, 0x6d, 0xc0, 0xf1, 0x49, 0x01, 0xbd, 0x3c, 0x29, 0xa0, 0x3f, 0x4f, 0x0a, 0xe8, 0xab, - 0xd3, 0xc2, 0xd4, 0xcb, 0xd3, 0xc2, 0xd4, 0xef, 0xa7, 0x85, 0x29, 0x9c, 0xb7, 0xf9, 0x08, 0x76, - 0x3b, 0xe8, 0x53, 0xc3, 0xb2, 0xfd, 0xc7, 0xfb, 0x0d, 0xa3, 0xc9, 0x3b, 0x11, 0x06, 0xab, 0x36, - 0x8f, 0xf2, 0x79, 0x7a, 0xc6, 0xa8, 0x71, 0x59, 0x7c, 0x7e, 0x57, 0xff, 0x09, 0x00, 0x00, 0xff, - 0xff, 0x41, 0x5f, 0x03, 0x0a, 0x7c, 0x10, 0x00, 0x00, + 0x28, 0x17, 0xfe, 0x00, 0x84, 0xc4, 0x05, 0x84, 0xa8, 0x40, 0x42, 0x9c, 0x91, 0xe8, 0x91, 0x3f, + 0x20, 0xc7, 0x8a, 0x5e, 0x38, 0x21, 0x94, 0xf0, 0x57, 0x70, 0x42, 0x3b, 0x33, 0x1b, 0xef, 0xc6, + 0xf6, 0x7a, 0x8d, 0x10, 0xe4, 0xe4, 0xdd, 0xf1, 0xfb, 0xde, 0xfb, 0xe6, 0x9b, 0x99, 0xf7, 0xcd, + 0x62, 0xbd, 0xeb, 0xf2, 0x1e, 0x38, 0xcc, 0x69, 0x02, 0x85, 0xa7, 0xcd, 0xc7, 0xcc, 0xb1, 0x80, + 0xf6, 0xca, 0xf4, 0xc9, 0x3e, 0xb8, 0x07, 0x46, 0xd7, 0xe5, 0x3e, 0x27, 0x57, 0xfb, 0x31, 0x46, + 0x18, 0x63, 0xf4, 0xca, 0x5a, 0xbe, 0xc9, 0xbd, 0x0e, 0xf7, 0xea, 0x22, 0x8a, 0xca, 0x17, 0x09, + 0xd1, 0x4a, 0xf2, 0x8d, 0x36, 0x98, 0x07, 0x32, 0x17, 0xed, 0x95, 0x1b, 0xe0, 0xb3, 0x32, 0xed, + 0x32, 0xcb, 0x76, 0x98, 0x6f, 0x73, 0x47, 0xc5, 0x16, 0xa2, 0xb1, 0x61, 0x54, 0x93, 0xdb, 0xe1, + 0xff, 0x4b, 0x16, 0xe7, 0x56, 0x1b, 0x28, 0xeb, 0xda, 0x94, 0x39, 0x0e, 0xf7, 0x05, 0x38, 0xac, + 0x94, 0xb5, 0xb8, 0xc5, 0x25, 0x83, 0xe0, 0x49, 0x8d, 0xde, 0x1c, 0x31, 0x2d, 0xee, 0xb6, 0xc0, + 0x55, 0x50, 0xfd, 0x4b, 0x84, 0x73, 0x8f, 0x02, 0x6e, 0x0f, 0x83, 0xd1, 0x1a, 0xc0, 0x26, 0x6b, + 0x37, 0x4d, 0x78, 0xb2, 0x0f, 0x9e, 0x4f, 0xee, 0xe3, 0x0c, 0xf3, 0xf6, 0xea, 0x02, 0x90, 0x9b, + 0x2e, 0xa2, 0xe5, 0xf9, 0x4a, 0xd1, 0x18, 0x2e, 0x84, 0xb1, 0xee, 0xed, 0x89, 0x14, 0xe6, 0x1c, + 0x53, 0x4f, 0x01, 0xbc, 0x61, 0xb7, 0x14, 0x7c, 0x26, 0x19, 0xbe, 0x61, 0xb7, 0x14, 0xbc, 0xa1, + 0x9e, 0xf4, 0x1f, 0xa7, 0x71, 0x7e, 0x08, 0x35, 0xaf, 0xcb, 0x1d, 0x0f, 0xc8, 0x23, 0x9c, 0x6d, + 0xba, 0x20, 0x64, 0xa8, 0xef, 0x02, 0xd4, 0x79, 0x57, 0x28, 0x92, 0x43, 0xc5, 0x99, 0xe5, 0xf9, + 0x4a, 0xde, 0x50, 0x4b, 0x11, 0x08, 0x6a, 0x28, 0x41, 0x8d, 0x4d, 0x6e, 0x3b, 0x1b, 0x97, 0x8e, + 0x7f, 0xbb, 0x3e, 0x65, 0x92, 0x10, 0x5c, 0x03, 0x78, 0x28, 0xa1, 0xe4, 0x23, 0xbc, 0xe8, 0x81, + 0xef, 0xb7, 0xa1, 0x03, 0x8e, 0x5f, 0xdf, 0x6d, 0x33, 0x3f, 0x96, 0x79, 0x3a, 0x5d, 0xe6, 0x5c, + 0x3f, 0x47, 0xad, 0xcd, 0xfc, 0x48, 0xfe, 0x8f, 0xf1, 0x52, 0x24, 0xbf, 0x1b, 0x94, 0x8f, 0x15, + 0x98, 0x49, 0x57, 0x20, 0xdf, 0x4f, 0x62, 0x06, 0x39, 0xfa, 0x15, 0xf4, 0x32, 0xce, 0x0a, 0xc5, + 0xb6, 0xc0, 0x97, 0x6a, 0xaa, 0x85, 0xcc, 0xe3, 0x39, 0xb1, 0x0a, 0x75, 0xbb, 0x95, 0x43, 0x45, + 0xb4, 0x7c, 0xc9, 0xfc, 0x9f, 0x78, 0xdf, 0x6e, 0xe9, 0xef, 0xe2, 0x2b, 0xe7, 0x20, 0x4a, 0xe0, + 0x2a, 0x9e, 0x95, 0x2b, 0x87, 0xc4, 0xca, 0x5d, 0x1b, 0xb5, 0x72, 0x12, 0x25, 0x63, 0xf5, 0xef, + 0x10, 0x5e, 0x0c, 0xd3, 0x3d, 0x60, 0xee, 0x9e, 0x4a, 0xea, 0x85, 0x44, 0x16, 0x71, 0xa6, 0x23, + 0x86, 0x43, 0x26, 0x0b, 0xe6, 0x9c, 0x1c, 0xd8, 0x6e, 0x91, 0x6b, 0x18, 0x4b, 0x96, 0xfe, 0x41, + 0x17, 0xc4, 0x7e, 0xcb, 0x98, 0x19, 0x31, 0xf2, 0xfe, 0x41, 0x17, 0x48, 0x0d, 0xe3, 0xfe, 0xb9, + 0xc9, 0x35, 0x05, 0xab, 0x5b, 0x31, 0xb1, 0xe4, 0x81, 0x0d, 0x25, 0xdb, 0x61, 0x16, 0xa8, 0xba, + 0x66, 0x04, 0xa9, 0x3f, 0x43, 0x78, 0x69, 0x38, 0x47, 0x35, 0xf3, 0x3b, 0xf8, 0xb2, 0x3c, 0x23, + 0x6a, 0x33, 0x8d, 0x99, 0xba, 0x0a, 0x26, 0x5b, 0x43, 0xf8, 0xbd, 0x31, 0x96, 0x9f, 0xac, 0x19, + 0x23, 0xf8, 0x13, 0xc2, 0xda, 0xd9, 0x9a, 0x7c, 0xe2, 0x80, 0x1b, 0xd7, 0xd0, 0xc0, 0xb3, 0x3c, + 0x18, 0x15, 0xfa, 0x65, 0x36, 0x72, 0xbf, 0x3c, 0x5f, 0xcd, 0xaa, 0x2a, 0xeb, 0xad, 0x96, 0x0b, + 0x9e, 0xf7, 0x9e, 0xef, 0xda, 0x8e, 0x65, 0xca, 0xb0, 0x7f, 0x4b, 0xd6, 0x6f, 0x22, 0x4b, 0x1f, + 0x63, 0x7d, 0x41, 0x54, 0xfd, 0x2a, 0xa2, 0xea, 0xba, 0xe7, 0x9d, 0xdf, 0x99, 0x59, 0x3c, 0xcb, + 0x82, 0x51, 0xa9, 0xaa, 0x29, 0x5f, 0xfe, 0x0b, 0xed, 0x62, 0xdc, 0x2e, 0x88, 0x76, 0x0d, 0x65, + 0x12, 0x01, 0xbd, 0x76, 0x3b, 0x2e, 0xdc, 0x3f, 0xa5, 0xc1, 0xd7, 0x48, 0xb5, 0xfb, 0x78, 0x91, + 0x0b, 0xa2, 0xc0, 0x1d, 0x7c, 0x55, 0x90, 0x93, 0x0d, 0x63, 0xdb, 0xd9, 0xe5, 0x69, 0x5a, 0x9a, + 0x9e, 0xc7, 0xaf, 0x0d, 0xc0, 0x64, 0x76, 0x3d, 0x8b, 0x89, 0xf8, 0x6b, 0x87, 0xb9, 0xac, 0x13, + 0xaa, 0xa9, 0x5f, 0xc1, 0xaf, 0xc6, 0x46, 0x55, 0xb0, 0x8e, 0x8b, 0x62, 0xf8, 0x03, 0xd6, 0xb6, + 0x5b, 0xcc, 0x87, 0xcd, 0xc0, 0xbd, 0x40, 0x66, 0x0d, 0xa1, 0x37, 0xf1, 0x8d, 0x84, 0x18, 0x95, + 0xe8, 0x3e, 0x2e, 0xc4, 0x82, 0x1e, 0x30, 0x87, 0x59, 0x50, 0x03, 0x48, 0xd5, 0xa2, 0xf5, 0x1b, + 0xf8, 0xfa, 0x48, 0xb8, 0xac, 0x50, 0xf9, 0x73, 0x01, 0xcf, 0x8a, 0x18, 0xf2, 0x3d, 0xc2, 0xaf, + 0x0c, 0x18, 0x38, 0x59, 0x1b, 0xb5, 0x72, 0xa3, 0xae, 0x21, 0x5a, 0x79, 0x02, 0x84, 0x9a, 0x66, + 0xe9, 0xd3, 0x97, 0x7f, 0x7c, 0x31, 0xfd, 0x3a, 0xd1, 0xe9, 0x88, 0x4b, 0xd0, 0x2e, 0x80, 0x27, + 0x6f, 0x42, 0xe4, 0x19, 0xc2, 0x0b, 0x31, 0x0b, 0x24, 0x2b, 0x89, 0x05, 0xcf, 0x99, 0xab, 0xb6, + 0x9a, 0x32, 0x5a, 0x51, 0x5b, 0x13, 0xd4, 0x4a, 0x64, 0x99, 0x26, 0xdd, 0xcf, 0xe8, 0x61, 0x68, + 0xd8, 0x47, 0xe4, 0x67, 0xd4, 0xb7, 0xf5, 0xa8, 0x61, 0x91, 0xea, 0xb8, 0xca, 0x43, 0x2c, 0x58, + 0xbb, 0x3d, 0x19, 0x48, 0xb1, 0xbe, 0x27, 0x58, 0x57, 0x49, 0x79, 0x14, 0x6b, 0xb9, 0x45, 0xe8, + 0xe1, 0xd9, 0xde, 0x39, 0x52, 0x17, 0x4d, 0xf2, 0x1c, 0xa9, 0x3d, 0x1d, 0x37, 0x06, 0x52, 0x19, + 0xab, 0xdb, 0x80, 0xf7, 0x69, 0xd5, 0x89, 0x30, 0x8a, 0xfb, 0x6d, 0xc1, 0xdd, 0x20, 0x2b, 0x89, + 0x8a, 0x7b, 0x54, 0xd8, 0x25, 0x3d, 0x14, 0x3f, 0x47, 0x31, 0xda, 0x91, 0x9e, 0x3c, 0x9e, 0xf6, + 0xa0, 0xb9, 0x8c, 0xa7, 0x3d, 0xa4, 0xe9, 0xa7, 0xa6, 0x2d, 0x9c, 0x8a, 0x1e, 0x8a, 0x9f, 0x23, + 0xf2, 0x6d, 0x78, 0xe8, 0xa2, 0x6d, 0x74, 0xcc, 0xa1, 0x1b, 0xd2, 0xd6, 0xc7, 0x1c, 0xba, 0x61, + 0x3d, 0x5a, 0xbf, 0x25, 0x08, 0x17, 0x49, 0x21, 0x99, 0x30, 0xf9, 0x01, 0xe1, 0xff, 0x9f, 0xeb, + 0x8a, 0xc4, 0x48, 0x2c, 0x37, 0xd0, 0x75, 0x35, 0x9a, 0x3a, 0x5e, 0x91, 0xab, 0x08, 0x72, 0x2b, + 0xa4, 0x94, 0x7e, 0x03, 0x93, 0xcf, 0x10, 0x9e, 0x8f, 0x74, 0x63, 0x52, 0x4a, 0x2c, 0x1a, 0x6b, + 0xe4, 0xda, 0x9b, 0xa9, 0x62, 0xd3, 0x2a, 0xd7, 0x95, 0x04, 0x8e, 0x43, 0x8f, 0x1c, 0xd6, 0xe3, + 0xc9, 0xdb, 0x89, 0x25, 0x13, 0xac, 0x43, 0xbb, 0xf7, 0x37, 0x90, 0x8a, 0xfa, 0x5d, 0x41, 0x7d, + 0x8d, 0x18, 0xa3, 0xa8, 0xf7, 0x14, 0x9a, 0x8a, 0x2f, 0x2e, 0xa8, 0x4b, 0x7d, 0xc9, 0x4b, 0xa4, + 0xac, 0x71, 0xd0, 0x4a, 0xc8, 0xdd, 0x54, 0x74, 0x06, 0xac, 0x4b, 0x7b, 0x6b, 0x62, 0x9c, 0x9a, + 0xc4, 0x96, 0x98, 0xc4, 0x3a, 0x79, 0x67, 0x82, 0xee, 0x76, 0x36, 0xaf, 0x8e, 0xc8, 0x17, 0x7c, + 0xc9, 0x79, 0x1b, 0x70, 0x7c, 0x52, 0x40, 0x2f, 0x4e, 0x0a, 0xe8, 0xf7, 0x93, 0x02, 0xfa, 0xfc, + 0xb4, 0x30, 0xf5, 0xe2, 0xb4, 0x30, 0xf5, 0xeb, 0x69, 0x61, 0x0a, 0xe7, 0x6d, 0x3e, 0x82, 0xdd, + 0x0e, 0xfa, 0xd0, 0xb0, 0x6c, 0xff, 0xf1, 0x7e, 0xc3, 0x68, 0xf2, 0x4e, 0x84, 0xc1, 0xaa, 0xcd, + 0xa3, 0x7c, 0x9e, 0x9e, 0x31, 0x6a, 0x5c, 0x16, 0x1f, 0xef, 0xd5, 0xbf, 0x02, 0x00, 0x00, 0xff, + 0xff, 0x1a, 0x13, 0x51, 0xab, 0xba, 0x10, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1741,6 +1759,13 @@ func (m *QueryGetMarketOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, er i-- dAtA[i] = 0x9a } + if len(m.OrderType) > 0 { + i -= len(m.OrderType) + copy(dAtA[i:], m.OrderType) + i = encodeVarintQuery(dAtA, i, uint64(len(m.OrderType))) + i-- + dAtA[i] = 0x12 + } if m.MarketId != 0 { i = encodeVarintQuery(dAtA, i, uint64(m.MarketId)) i-- @@ -1834,6 +1859,13 @@ func (m *QueryGetOwnerOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, err i-- dAtA[i] = 0x9a } + if len(m.OrderType) > 0 { + i -= len(m.OrderType) + copy(dAtA[i:], m.OrderType) + i = encodeVarintQuery(dAtA, i, uint64(len(m.OrderType))) + i-- + dAtA[i] = 0x12 + } if len(m.Owner) > 0 { i -= len(m.Owner) copy(dAtA[i:], m.Owner) @@ -2368,6 +2400,10 @@ func (m *QueryGetMarketOrdersRequest) Size() (n int) { if m.MarketId != 0 { n += 1 + sovQuery(uint64(m.MarketId)) } + l = len(m.OrderType) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } if m.Pagination != nil { l = m.Pagination.Size() n += 2 + l + sovQuery(uint64(l)) @@ -2404,6 +2440,10 @@ func (m *QueryGetOwnerOrdersRequest) Size() (n int) { if l > 0 { n += 1 + l + sovQuery(uint64(l)) } + l = len(m.OrderType) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } if m.Pagination != nil { l = m.Pagination.Size() n += 2 + l + sovQuery(uint64(l)) @@ -3063,6 +3103,38 @@ func (m *QueryGetMarketOrdersRequest) Unmarshal(dAtA []byte) error { break } } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OrderType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex case 99: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) @@ -3301,6 +3373,38 @@ func (m *QueryGetOwnerOrdersRequest) Unmarshal(dAtA []byte) error { } m.Owner = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OrderType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex case 99: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) From 0a4483856d55b49049536e38efb4e5f5da34c7e2 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 2 Oct 2023 13:19:47 -0600 Subject: [PATCH 218/309] [1658]: Allow the MarketOrders, AddressOrders, and AssetOrders queries to be limited to a minimum order id. --- docs/proto-docs.md | 3 + proto/provenance/exchange/v1/query.proto | 6 + x/exchange/keeper/grpc_query.go | 6 +- x/exchange/keeper/orders.go | 173 +++++++++++++++- x/exchange/query.pb.go | 251 ++++++++++++++++------- 5 files changed, 358 insertions(+), 81 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index b3fc540a13..0dd013c45f 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1932,6 +1932,7 @@ QueryGetAssetOrdersRequest is a request message for the QueryGetAssetOrders endp | ----- | ---- | ----- | ----------- | | `asset` | [string](#string) | | asset is the denom of assets to get orders for. | | `order_type` | [string](#string) | | order_type is optional and can limit orders to only "ask" or "bid" orders. | +| `after_order_id` | [uint64](#uint64) | | after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. | | `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | @@ -1965,6 +1966,7 @@ QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders en | ----- | ---- | ----- | ----------- | | `market_id` | [uint32](#uint32) | | market_id is the id of the market to get all the orders for. | | `order_type` | [string](#string) | | order_type is optional and can limit orders to only "ask" or "bid" orders. | +| `after_order_id` | [uint64](#uint64) | | after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. | | `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | @@ -2028,6 +2030,7 @@ QueryGetOwnerOrdersRequest is a request message for the QueryGetOwnerOrders endp | ----- | ---- | ----- | ----------- | | `owner` | [string](#string) | | owner is the bech32 address string of the owner to get the orders for. | | `order_type` | [string](#string) | | order_type is optional and can limit orders to only "ask" or "bid" orders. | +| `after_order_id` | [uint64](#uint64) | | after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. | | `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 53c2cb45db..9b84abc53a 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -114,6 +114,8 @@ message QueryGetMarketOrdersRequest { uint32 market_id = 1; // order_type is optional and can limit orders to only "ask" or "bid" orders. string order_type = 2; + // after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. + uint64 after_order_id = 3; // pagination defines an optional pagination for the request. cosmos.base.query.v1beta1.PageRequest pagination = 99; @@ -134,6 +136,8 @@ message QueryGetOwnerOrdersRequest { string owner = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // order_type is optional and can limit orders to only "ask" or "bid" orders. string order_type = 2; + // after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. + uint64 after_order_id = 3; // pagination defines an optional pagination for the request. cosmos.base.query.v1beta1.PageRequest pagination = 99; @@ -154,6 +158,8 @@ message QueryGetAssetOrdersRequest { string asset = 1; // order_type is optional and can limit orders to only "ask" or "bid" orders. string order_type = 2; + // after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. + uint64 after_order_id = 3; // pagination defines an optional pagination for the request. cosmos.base.query.v1beta1.PageRequest pagination = 99; diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 07b120cc44..50ce84829b 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -99,7 +99,7 @@ func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.Q resp := &exchange.QueryGetMarketOrdersResponse{} var err error - resp.Pagination, resp.Orders, err = k.GetPageOfOrdersFromIndex(store, req.Pagination, req.OrderType) + resp.Pagination, resp.Orders, err = k.GetPageOfOrdersFromIndex(store, req.Pagination, req.OrderType, req.AfterOrderId) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for market %d: %v", req.MarketId, err) @@ -125,7 +125,7 @@ func (k QueryServer) QueryGetOwnerOrders(goCtx context.Context, req *exchange.Qu resp := &exchange.QueryGetOwnerOrdersResponse{} var err error - resp.Pagination, resp.Orders, err = k.GetPageOfOrdersFromIndex(store, req.Pagination, req.OrderType) + resp.Pagination, resp.Orders, err = k.GetPageOfOrdersFromIndex(store, req.Pagination, req.OrderType, req.AfterOrderId) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for owner %s: %v", req.Owner, err) @@ -146,7 +146,7 @@ func (k QueryServer) QueryGetAssetOrders(goCtx context.Context, req *exchange.Qu resp := &exchange.QueryGetAssetOrdersResponse{} var err error - resp.Pagination, resp.Orders, err = k.GetPageOfOrdersFromIndex(store, req.Pagination, req.OrderType) + resp.Pagination, resp.Orders, err = k.GetPageOfOrdersFromIndex(store, req.Pagination, req.OrderType, req.AfterOrderId) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for asset %s: %v", req.Asset, err) diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 10b2fba55b..c4be03ab7f 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -8,6 +8,8 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + db "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" @@ -237,6 +239,7 @@ func (k Keeper) GetPageOfOrdersFromIndex( prefixStore sdk.KVStore, pageReq *query.PageRequest, orderType string, + afterOrderID uint64, ) (*query.PageResponse, []*exchange.Order, error) { var orderTypeByte byte filterByType := false @@ -258,7 +261,8 @@ func (k Keeper) GetPageOfOrdersFromIndex( } var orders []*exchange.Order - pageResp, pageErr := query.FilteredPaginate(prefixStore, pageReq, func(key []byte, value []byte, accumulate bool) (bool, error) { + accumulator := func(key []byte, value []byte, accumulate bool) (bool, error) { + // If filtering by type, but the order type isn't known, or is something else, this entry doesn't count, move on. if filterByType && (len(value) == 0 || value[0] != orderTypeByte) { return false, nil } @@ -268,17 +272,172 @@ func (k Keeper) GetPageOfOrdersFromIndex( return false, nil } if accumulate { - // Only add them to the result if we can read it. - // This might result in fewer results than the limit, but at least one bad entry won't block others. - order, oerr := k.parseOrderStoreValue(orderID, value) - if oerr != nil { + // Only add it to the result if we can read it. This might result in fewer results than the limit, + // but at least one bad entry won't block others by causing the whole thing to return an error. + order, err := k.parseOrderStoreValue(orderID, value) + if err == nil && order != nil { orders = append(orders, order) } } return true, nil - }) + } + pageResp, err := FilteredPaginateAfterOrder(prefixStore, pageReq, afterOrderID, accumulator) + + return pageResp, orders, err +} + +// FilteredPaginateAfterOrder is similar to query.FilteredPaginate except +// allows limiting the iterator to only entries after a certain order id. +// afterOrderID is exclusive, i.e. if it's 2, this will go over all order ids that are 3 or greater. +// +// FilteredPaginate does pagination of all the results in the PrefixStore based on the +// provided PageRequest. onResult should be used to do actual unmarshaling and filter the results. +// If key is provided, the pagination uses the optimized querying. +// If offset is used, the pagination uses lazy filtering i.e., searches through all the records. +// The accumulate parameter represents if the response is valid based on the offset given. +// It will be false for the results (filtered) < offset and true for `offset > accumulate <= end`. +// When accumulate is set to true the current result should be appended to the result set returned +// to the client. +func FilteredPaginateAfterOrder( + prefixStore sdk.KVStore, + pageRequest *query.PageRequest, + afterOrderID uint64, + onResult func(key []byte, value []byte, accumulate bool) (bool, error), +) (*query.PageResponse, error) { + // if the PageRequest is nil, use default PageRequest + if pageRequest == nil { + pageRequest = &query.PageRequest{} + } + + offset := pageRequest.Offset + key := pageRequest.Key + limit := pageRequest.Limit + countTotal := pageRequest.CountTotal + reverse := pageRequest.Reverse + + if offset > 0 && key != nil { + return nil, fmt.Errorf("invalid request, either offset or key is expected, got both") + } + + if limit == 0 { + limit = query.DefaultLimit + + // count total results when the limit is zero/not supplied + countTotal = true + } + + if len(key) != 0 { + // This line is changed from the query.FilteredPaginate version. + iterator := getOrderIterator(prefixStore, key, reverse, afterOrderID) + defer iterator.Close() + + var ( + numHits uint64 + nextKey []byte + ) + + for ; iterator.Valid(); iterator.Next() { + if numHits == limit { + nextKey = iterator.Key() + break + } + + if iterator.Error() != nil { + return nil, iterator.Error() + } + + hit, err := onResult(iterator.Key(), iterator.Value(), true) + if err != nil { + return nil, err + } + + if hit { + numHits++ + } + } + + return &query.PageResponse{ + NextKey: nextKey, + }, nil + } - return pageResp, orders, pageErr + // This line is changed from the query.FilteredPaginate version. + iterator := getOrderIterator(prefixStore, nil, reverse, afterOrderID) + defer iterator.Close() + + end := offset + limit + + var ( + numHits uint64 + nextKey []byte + ) + + for ; iterator.Valid(); iterator.Next() { + if iterator.Error() != nil { + return nil, iterator.Error() + } + + accumulate := numHits >= offset && numHits < end + hit, err := onResult(iterator.Key(), iterator.Value(), accumulate) + if err != nil { + return nil, err + } + + if hit { + numHits++ + } + + if numHits == end+1 { + if nextKey == nil { + nextKey = iterator.Key() + } + + if !countTotal { + break + } + } + } + + res := &query.PageResponse{NextKey: nextKey} + if countTotal { + res.Total = numHits + } + + return res, nil +} + +// getOrderIterator is similar to query.getIterator but allows limiting it to only entries after a certain order id. +func getOrderIterator(prefixStore sdk.KVStore, start []byte, reverse bool, afterOrderID uint64) db.Iterator { + if reverse { + var end []byte + if start != nil { + itr := prefixStore.Iterator(start, nil) + defer itr.Close() + if itr.Valid() { + itr.Next() + end = itr.Key() + } + } + // If an afterOrderID was given, use the key of it as the first key. + // This orderIDKey is a change from the query.getIterator version. + var orderIDKey []byte + if afterOrderID != 0 { + // TODO[1658]: Write a unit test that hits this in order to make sure I don't need to afterOrderID++. + orderIDKey = uint64Bz(afterOrderID) + } + return prefixStore.ReverseIterator(orderIDKey, end) + } + + // If a start ("next key") was given, use that as the first key. + // Otherwise, if an afterOrderID was given, use the key of the next order id as the first key. + // This if block is a change from the query.getIterator version. + if len(start) == 0 && afterOrderID != 0 { + if afterOrderID != 18_446_744_073_709_551_615 { + afterOrderID++ + } + start = uint64Bz(afterOrderID) + } + return prefixStore.Iterator(start, nil) } // placeHoldOnOrder places a hold on an order's funds in the owner's account. diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index 25520170fb..4c4fe65b46 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -261,6 +261,8 @@ type QueryGetMarketOrdersRequest struct { MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // order_type is optional and can limit orders to only "ask" or "bid" orders. OrderType string `protobuf:"bytes,2,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` + // after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. + AfterOrderId uint64 `protobuf:"varint,3,opt,name=after_order_id,json=afterOrderId,proto3" json:"after_order_id,omitempty"` // pagination defines an optional pagination for the request. Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` } @@ -312,6 +314,13 @@ func (m *QueryGetMarketOrdersRequest) GetOrderType() string { return "" } +func (m *QueryGetMarketOrdersRequest) GetAfterOrderId() uint64 { + if m != nil { + return m.AfterOrderId + } + return 0 +} + func (m *QueryGetMarketOrdersRequest) GetPagination() *query.PageRequest { if m != nil { return m.Pagination @@ -380,6 +389,8 @@ type QueryGetOwnerOrdersRequest struct { Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` // order_type is optional and can limit orders to only "ask" or "bid" orders. OrderType string `protobuf:"bytes,2,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` + // after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. + AfterOrderId uint64 `protobuf:"varint,3,opt,name=after_order_id,json=afterOrderId,proto3" json:"after_order_id,omitempty"` // pagination defines an optional pagination for the request. Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` } @@ -431,6 +442,13 @@ func (m *QueryGetOwnerOrdersRequest) GetOrderType() string { return "" } +func (m *QueryGetOwnerOrdersRequest) GetAfterOrderId() uint64 { + if m != nil { + return m.AfterOrderId + } + return 0 +} + func (m *QueryGetOwnerOrdersRequest) GetPagination() *query.PageRequest { if m != nil { return m.Pagination @@ -499,6 +517,8 @@ type QueryGetAssetOrdersRequest struct { Asset string `protobuf:"bytes,1,opt,name=asset,proto3" json:"asset,omitempty"` // order_type is optional and can limit orders to only "ask" or "bid" orders. OrderType string `protobuf:"bytes,2,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` + // after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. + AfterOrderId uint64 `protobuf:"varint,3,opt,name=after_order_id,json=afterOrderId,proto3" json:"after_order_id,omitempty"` // pagination defines an optional pagination for the request. Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` } @@ -550,6 +570,13 @@ func (m *QueryGetAssetOrdersRequest) GetOrderType() string { return "" } +func (m *QueryGetAssetOrdersRequest) GetAfterOrderId() uint64 { + if m != nil { + return m.AfterOrderId + } + return 0 +} + func (m *QueryGetAssetOrdersRequest) GetPagination() *query.PageRequest { if m != nil { return m.Pagination @@ -1053,77 +1080,78 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 1113 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0xc1, 0x6f, 0x1b, 0xc5, - 0x17, 0xce, 0x24, 0x4d, 0x7f, 0xf1, 0x44, 0xd1, 0x4f, 0x0c, 0x6e, 0xb1, 0x37, 0xa9, 0xeb, 0x6e, - 0x51, 0x89, 0x4c, 0xb2, 0x13, 0xdb, 0x6d, 0xa1, 0x87, 0x0a, 0x25, 0x91, 0x1c, 0x45, 0xa2, 0x6a, - 0xba, 0x20, 0x0e, 0x1c, 0x30, 0x63, 0xfb, 0x65, 0xbb, 0x8a, 0xbd, 0xe3, 0xee, 0x6e, 0x4c, 0xa3, - 0x28, 0x17, 0xfe, 0x00, 0x84, 0xc4, 0x05, 0x84, 0xa8, 0x40, 0x42, 0x9c, 0x91, 0xe8, 0x91, 0x3f, - 0x20, 0xc7, 0x8a, 0x5e, 0x38, 0x21, 0x94, 0xf0, 0x57, 0x70, 0x42, 0x3b, 0x33, 0x1b, 0xef, 0xc6, - 0xf6, 0x7a, 0x8d, 0x10, 0xe4, 0xe4, 0xdd, 0xf1, 0xfb, 0xde, 0xfb, 0xe6, 0x9b, 0x99, 0xf7, 0xcd, - 0x62, 0xbd, 0xeb, 0xf2, 0x1e, 0x38, 0xcc, 0x69, 0x02, 0x85, 0xa7, 0xcd, 0xc7, 0xcc, 0xb1, 0x80, - 0xf6, 0xca, 0xf4, 0xc9, 0x3e, 0xb8, 0x07, 0x46, 0xd7, 0xe5, 0x3e, 0x27, 0x57, 0xfb, 0x31, 0x46, - 0x18, 0x63, 0xf4, 0xca, 0x5a, 0xbe, 0xc9, 0xbd, 0x0e, 0xf7, 0xea, 0x22, 0x8a, 0xca, 0x17, 0x09, - 0xd1, 0x4a, 0xf2, 0x8d, 0x36, 0x98, 0x07, 0x32, 0x17, 0xed, 0x95, 0x1b, 0xe0, 0xb3, 0x32, 0xed, - 0x32, 0xcb, 0x76, 0x98, 0x6f, 0x73, 0x47, 0xc5, 0x16, 0xa2, 0xb1, 0x61, 0x54, 0x93, 0xdb, 0xe1, - 0xff, 0x4b, 0x16, 0xe7, 0x56, 0x1b, 0x28, 0xeb, 0xda, 0x94, 0x39, 0x0e, 0xf7, 0x05, 0x38, 0xac, - 0x94, 0xb5, 0xb8, 0xc5, 0x25, 0x83, 0xe0, 0x49, 0x8d, 0xde, 0x1c, 0x31, 0x2d, 0xee, 0xb6, 0xc0, - 0x55, 0x50, 0xfd, 0x4b, 0x84, 0x73, 0x8f, 0x02, 0x6e, 0x0f, 0x83, 0xd1, 0x1a, 0xc0, 0x26, 0x6b, - 0x37, 0x4d, 0x78, 0xb2, 0x0f, 0x9e, 0x4f, 0xee, 0xe3, 0x0c, 0xf3, 0xf6, 0xea, 0x02, 0x90, 0x9b, - 0x2e, 0xa2, 0xe5, 0xf9, 0x4a, 0xd1, 0x18, 0x2e, 0x84, 0xb1, 0xee, 0xed, 0x89, 0x14, 0xe6, 0x1c, - 0x53, 0x4f, 0x01, 0xbc, 0x61, 0xb7, 0x14, 0x7c, 0x26, 0x19, 0xbe, 0x61, 0xb7, 0x14, 0xbc, 0xa1, - 0x9e, 0xf4, 0x1f, 0xa7, 0x71, 0x7e, 0x08, 0x35, 0xaf, 0xcb, 0x1d, 0x0f, 0xc8, 0x23, 0x9c, 0x6d, - 0xba, 0x20, 0x64, 0xa8, 0xef, 0x02, 0xd4, 0x79, 0x57, 0x28, 0x92, 0x43, 0xc5, 0x99, 0xe5, 0xf9, - 0x4a, 0xde, 0x50, 0x4b, 0x11, 0x08, 0x6a, 0x28, 0x41, 0x8d, 0x4d, 0x6e, 0x3b, 0x1b, 0x97, 0x8e, - 0x7f, 0xbb, 0x3e, 0x65, 0x92, 0x10, 0x5c, 0x03, 0x78, 0x28, 0xa1, 0xe4, 0x23, 0xbc, 0xe8, 0x81, - 0xef, 0xb7, 0xa1, 0x03, 0x8e, 0x5f, 0xdf, 0x6d, 0x33, 0x3f, 0x96, 0x79, 0x3a, 0x5d, 0xe6, 0x5c, - 0x3f, 0x47, 0xad, 0xcd, 0xfc, 0x48, 0xfe, 0x8f, 0xf1, 0x52, 0x24, 0xbf, 0x1b, 0x94, 0x8f, 0x15, - 0x98, 0x49, 0x57, 0x20, 0xdf, 0x4f, 0x62, 0x06, 0x39, 0xfa, 0x15, 0xf4, 0x32, 0xce, 0x0a, 0xc5, - 0xb6, 0xc0, 0x97, 0x6a, 0xaa, 0x85, 0xcc, 0xe3, 0x39, 0xb1, 0x0a, 0x75, 0xbb, 0x95, 0x43, 0x45, - 0xb4, 0x7c, 0xc9, 0xfc, 0x9f, 0x78, 0xdf, 0x6e, 0xe9, 0xef, 0xe2, 0x2b, 0xe7, 0x20, 0x4a, 0xe0, - 0x2a, 0x9e, 0x95, 0x2b, 0x87, 0xc4, 0xca, 0x5d, 0x1b, 0xb5, 0x72, 0x12, 0x25, 0x63, 0xf5, 0xef, - 0x10, 0x5e, 0x0c, 0xd3, 0x3d, 0x60, 0xee, 0x9e, 0x4a, 0xea, 0x85, 0x44, 0x16, 0x71, 0xa6, 0x23, - 0x86, 0x43, 0x26, 0x0b, 0xe6, 0x9c, 0x1c, 0xd8, 0x6e, 0x91, 0x6b, 0x18, 0x4b, 0x96, 0xfe, 0x41, - 0x17, 0xc4, 0x7e, 0xcb, 0x98, 0x19, 0x31, 0xf2, 0xfe, 0x41, 0x17, 0x48, 0x0d, 0xe3, 0xfe, 0xb9, - 0xc9, 0x35, 0x05, 0xab, 0x5b, 0x31, 0xb1, 0xe4, 0x81, 0x0d, 0x25, 0xdb, 0x61, 0x16, 0xa8, 0xba, - 0x66, 0x04, 0xa9, 0x3f, 0x43, 0x78, 0x69, 0x38, 0x47, 0x35, 0xf3, 0x3b, 0xf8, 0xb2, 0x3c, 0x23, - 0x6a, 0x33, 0x8d, 0x99, 0xba, 0x0a, 0x26, 0x5b, 0x43, 0xf8, 0xbd, 0x31, 0x96, 0x9f, 0xac, 0x19, - 0x23, 0xf8, 0x13, 0xc2, 0xda, 0xd9, 0x9a, 0x7c, 0xe2, 0x80, 0x1b, 0xd7, 0xd0, 0xc0, 0xb3, 0x3c, - 0x18, 0x15, 0xfa, 0x65, 0x36, 0x72, 0xbf, 0x3c, 0x5f, 0xcd, 0xaa, 0x2a, 0xeb, 0xad, 0x96, 0x0b, - 0x9e, 0xf7, 0x9e, 0xef, 0xda, 0x8e, 0x65, 0xca, 0xb0, 0x7f, 0x4b, 0xd6, 0x6f, 0x22, 0x4b, 0x1f, - 0x63, 0x7d, 0x41, 0x54, 0xfd, 0x2a, 0xa2, 0xea, 0xba, 0xe7, 0x9d, 0xdf, 0x99, 0x59, 0x3c, 0xcb, - 0x82, 0x51, 0xa9, 0xaa, 0x29, 0x5f, 0xfe, 0x0b, 0xed, 0x62, 0xdc, 0x2e, 0x88, 0x76, 0x0d, 0x65, - 0x12, 0x01, 0xbd, 0x76, 0x3b, 0x2e, 0xdc, 0x3f, 0xa5, 0xc1, 0xd7, 0x48, 0xb5, 0xfb, 0x78, 0x91, - 0x0b, 0xa2, 0xc0, 0x1d, 0x7c, 0x55, 0x90, 0x93, 0x0d, 0x63, 0xdb, 0xd9, 0xe5, 0x69, 0x5a, 0x9a, - 0x9e, 0xc7, 0xaf, 0x0d, 0xc0, 0x64, 0x76, 0x3d, 0x8b, 0x89, 0xf8, 0x6b, 0x87, 0xb9, 0xac, 0x13, - 0xaa, 0xa9, 0x5f, 0xc1, 0xaf, 0xc6, 0x46, 0x55, 0xb0, 0x8e, 0x8b, 0x62, 0xf8, 0x03, 0xd6, 0xb6, - 0x5b, 0xcc, 0x87, 0xcd, 0xc0, 0xbd, 0x40, 0x66, 0x0d, 0xa1, 0x37, 0xf1, 0x8d, 0x84, 0x18, 0x95, - 0xe8, 0x3e, 0x2e, 0xc4, 0x82, 0x1e, 0x30, 0x87, 0x59, 0x50, 0x03, 0x48, 0xd5, 0xa2, 0xf5, 0x1b, - 0xf8, 0xfa, 0x48, 0xb8, 0xac, 0x50, 0xf9, 0x73, 0x01, 0xcf, 0x8a, 0x18, 0xf2, 0x3d, 0xc2, 0xaf, - 0x0c, 0x18, 0x38, 0x59, 0x1b, 0xb5, 0x72, 0xa3, 0xae, 0x21, 0x5a, 0x79, 0x02, 0x84, 0x9a, 0x66, - 0xe9, 0xd3, 0x97, 0x7f, 0x7c, 0x31, 0xfd, 0x3a, 0xd1, 0xe9, 0x88, 0x4b, 0xd0, 0x2e, 0x80, 0x27, - 0x6f, 0x42, 0xe4, 0x19, 0xc2, 0x0b, 0x31, 0x0b, 0x24, 0x2b, 0x89, 0x05, 0xcf, 0x99, 0xab, 0xb6, - 0x9a, 0x32, 0x5a, 0x51, 0x5b, 0x13, 0xd4, 0x4a, 0x64, 0x99, 0x26, 0xdd, 0xcf, 0xe8, 0x61, 0x68, - 0xd8, 0x47, 0xe4, 0x67, 0xd4, 0xb7, 0xf5, 0xa8, 0x61, 0x91, 0xea, 0xb8, 0xca, 0x43, 0x2c, 0x58, - 0xbb, 0x3d, 0x19, 0x48, 0xb1, 0xbe, 0x27, 0x58, 0x57, 0x49, 0x79, 0x14, 0x6b, 0xb9, 0x45, 0xe8, - 0xe1, 0xd9, 0xde, 0x39, 0x52, 0x17, 0x4d, 0xf2, 0x1c, 0xa9, 0x3d, 0x1d, 0x37, 0x06, 0x52, 0x19, - 0xab, 0xdb, 0x80, 0xf7, 0x69, 0xd5, 0x89, 0x30, 0x8a, 0xfb, 0x6d, 0xc1, 0xdd, 0x20, 0x2b, 0x89, - 0x8a, 0x7b, 0x54, 0xd8, 0x25, 0x3d, 0x14, 0x3f, 0x47, 0x31, 0xda, 0x91, 0x9e, 0x3c, 0x9e, 0xf6, - 0xa0, 0xb9, 0x8c, 0xa7, 0x3d, 0xa4, 0xe9, 0xa7, 0xa6, 0x2d, 0x9c, 0x8a, 0x1e, 0x8a, 0x9f, 0x23, - 0xf2, 0x6d, 0x78, 0xe8, 0xa2, 0x6d, 0x74, 0xcc, 0xa1, 0x1b, 0xd2, 0xd6, 0xc7, 0x1c, 0xba, 0x61, - 0x3d, 0x5a, 0xbf, 0x25, 0x08, 0x17, 0x49, 0x21, 0x99, 0x30, 0xf9, 0x01, 0xe1, 0xff, 0x9f, 0xeb, - 0x8a, 0xc4, 0x48, 0x2c, 0x37, 0xd0, 0x75, 0x35, 0x9a, 0x3a, 0x5e, 0x91, 0xab, 0x08, 0x72, 0x2b, - 0xa4, 0x94, 0x7e, 0x03, 0x93, 0xcf, 0x10, 0x9e, 0x8f, 0x74, 0x63, 0x52, 0x4a, 0x2c, 0x1a, 0x6b, - 0xe4, 0xda, 0x9b, 0xa9, 0x62, 0xd3, 0x2a, 0xd7, 0x95, 0x04, 0x8e, 0x43, 0x8f, 0x1c, 0xd6, 0xe3, - 0xc9, 0xdb, 0x89, 0x25, 0x13, 0xac, 0x43, 0xbb, 0xf7, 0x37, 0x90, 0x8a, 0xfa, 0x5d, 0x41, 0x7d, - 0x8d, 0x18, 0xa3, 0xa8, 0xf7, 0x14, 0x9a, 0x8a, 0x2f, 0x2e, 0xa8, 0x4b, 0x7d, 0xc9, 0x4b, 0xa4, - 0xac, 0x71, 0xd0, 0x4a, 0xc8, 0xdd, 0x54, 0x74, 0x06, 0xac, 0x4b, 0x7b, 0x6b, 0x62, 0x9c, 0x9a, - 0xc4, 0x96, 0x98, 0xc4, 0x3a, 0x79, 0x67, 0x82, 0xee, 0x76, 0x36, 0xaf, 0x8e, 0xc8, 0x17, 0x7c, - 0xc9, 0x79, 0x1b, 0x70, 0x7c, 0x52, 0x40, 0x2f, 0x4e, 0x0a, 0xe8, 0xf7, 0x93, 0x02, 0xfa, 0xfc, - 0xb4, 0x30, 0xf5, 0xe2, 0xb4, 0x30, 0xf5, 0xeb, 0x69, 0x61, 0x0a, 0xe7, 0x6d, 0x3e, 0x82, 0xdd, - 0x0e, 0xfa, 0xd0, 0xb0, 0x6c, 0xff, 0xf1, 0x7e, 0xc3, 0x68, 0xf2, 0x4e, 0x84, 0xc1, 0xaa, 0xcd, - 0xa3, 0x7c, 0x9e, 0x9e, 0x31, 0x6a, 0x5c, 0x16, 0x1f, 0xef, 0xd5, 0xbf, 0x02, 0x00, 0x00, 0xff, - 0xff, 0x1a, 0x13, 0x51, 0xab, 0xba, 0x10, 0x00, 0x00, + // 1133 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x41, 0x6f, 0x1b, 0x45, + 0x14, 0xce, 0x24, 0x4d, 0x89, 0x27, 0x04, 0xc4, 0xe0, 0x16, 0x7b, 0x93, 0xba, 0xee, 0xb6, 0x2a, + 0x91, 0x49, 0x76, 0x63, 0xbb, 0x2d, 0xf4, 0x50, 0xa1, 0x24, 0x92, 0xa3, 0x48, 0x54, 0x49, 0x17, + 0xc4, 0x81, 0x03, 0x66, 0x6c, 0x3f, 0x6f, 0x57, 0xb1, 0x77, 0xdc, 0xdd, 0x8d, 0x69, 0x14, 0xe5, + 0xc2, 0x0f, 0x40, 0x48, 0x5c, 0x90, 0x10, 0x15, 0x07, 0xc4, 0x99, 0x43, 0x8f, 0x48, 0x1c, 0xc9, + 0xb1, 0xa2, 0x17, 0xb8, 0x20, 0x94, 0xf0, 0x2b, 0x38, 0xa1, 0x9d, 0x99, 0xb5, 0x77, 0x63, 0x7b, + 0xbd, 0x46, 0x1c, 0x7c, 0xf2, 0xee, 0xf3, 0xfb, 0xde, 0xfb, 0xde, 0x37, 0x33, 0xef, 0x8d, 0x8d, + 0xd5, 0x8e, 0xc3, 0xba, 0x60, 0x53, 0xbb, 0x0e, 0x3a, 0x3c, 0xad, 0x3f, 0xa6, 0xb6, 0x09, 0x7a, + 0xb7, 0xa8, 0x3f, 0x39, 0x04, 0xe7, 0x48, 0xeb, 0x38, 0xcc, 0x63, 0xe4, 0x6a, 0xdf, 0x47, 0x0b, + 0x7c, 0xb4, 0x6e, 0x51, 0xc9, 0xd6, 0x99, 0xdb, 0x66, 0x6e, 0x95, 0x7b, 0xe9, 0xe2, 0x45, 0x40, + 0x94, 0x82, 0x78, 0xd3, 0x6b, 0xd4, 0x05, 0x11, 0x4b, 0xef, 0x16, 0x6b, 0xe0, 0xd1, 0xa2, 0xde, + 0xa1, 0xa6, 0x65, 0x53, 0xcf, 0x62, 0xb6, 0xf4, 0xcd, 0x85, 0x7d, 0x03, 0xaf, 0x3a, 0xb3, 0x82, + 0xef, 0x57, 0x4c, 0xc6, 0xcc, 0x16, 0xe8, 0xb4, 0x63, 0xe9, 0xd4, 0xb6, 0x99, 0xc7, 0xc1, 0x41, + 0xa6, 0xb4, 0xc9, 0x4c, 0x26, 0x18, 0xf8, 0x4f, 0xd2, 0x7a, 0x73, 0x44, 0x59, 0xcc, 0x69, 0x80, + 0x23, 0xa1, 0xea, 0x37, 0x08, 0x67, 0x1e, 0xf9, 0xdc, 0xf6, 0x7c, 0x6b, 0x05, 0x60, 0x9b, 0xb6, + 0xea, 0x06, 0x3c, 0x39, 0x04, 0xd7, 0x23, 0x0f, 0x70, 0x8a, 0xba, 0x07, 0x55, 0x0e, 0xc8, 0xcc, + 0xe6, 0xd1, 0xea, 0x62, 0x29, 0xaf, 0x0d, 0x17, 0x42, 0xdb, 0x74, 0x0f, 0x78, 0x08, 0x63, 0x81, + 0xca, 0x27, 0x1f, 0x5e, 0xb3, 0x1a, 0x12, 0x3e, 0x17, 0x0f, 0xdf, 0xb2, 0x1a, 0x12, 0x5e, 0x93, + 0x4f, 0xea, 0x4f, 0xb3, 0x38, 0x3b, 0x84, 0x9a, 0xdb, 0x61, 0xb6, 0x0b, 0xe4, 0x11, 0x4e, 0xd7, + 0x1d, 0xe0, 0x32, 0x54, 0x9b, 0x00, 0x55, 0xd6, 0xe1, 0x8a, 0x64, 0x50, 0x7e, 0x6e, 0x75, 0xb1, + 0x94, 0xd5, 0xe4, 0x52, 0xf8, 0x82, 0x6a, 0x52, 0x50, 0x6d, 0x9b, 0x59, 0xf6, 0xd6, 0xa5, 0xd3, + 0x3f, 0xaf, 0xcf, 0x18, 0x24, 0x00, 0x57, 0x00, 0xf6, 0x04, 0x94, 0x7c, 0x8a, 0x97, 0x5d, 0xf0, + 0xbc, 0x16, 0xb4, 0xc1, 0xf6, 0xaa, 0xcd, 0x16, 0xf5, 0x22, 0x91, 0x67, 0x93, 0x45, 0xce, 0xf4, + 0x63, 0x54, 0x5a, 0xd4, 0x0b, 0xc5, 0xff, 0x0c, 0xaf, 0x84, 0xe2, 0x3b, 0x7e, 0xfa, 0x48, 0x82, + 0xb9, 0x64, 0x09, 0xb2, 0xfd, 0x20, 0x86, 0x1f, 0xa3, 0x9f, 0x41, 0x2d, 0xe2, 0x34, 0x57, 0x6c, + 0x07, 0x3c, 0xa1, 0xa6, 0x5c, 0xc8, 0x2c, 0x5e, 0xe0, 0xab, 0x50, 0xb5, 0x1a, 0x19, 0x94, 0x47, + 0xab, 0x97, 0x8c, 0x57, 0xf8, 0xfb, 0x6e, 0x43, 0xfd, 0x00, 0x5f, 0xb9, 0x00, 0x91, 0x02, 0x97, + 0xf1, 0xbc, 0x58, 0x39, 0xc4, 0x57, 0xee, 0xda, 0xa8, 0x95, 0x13, 0x28, 0xe1, 0xab, 0xfe, 0x8a, + 0xf0, 0x72, 0x10, 0xee, 0x21, 0x75, 0x0e, 0x64, 0x50, 0x37, 0x20, 0xb2, 0x8c, 0x53, 0x6d, 0x6e, + 0x0e, 0x98, 0x2c, 0x19, 0x0b, 0xc2, 0xb0, 0xdb, 0x20, 0xd7, 0x30, 0x16, 0x2c, 0xbd, 0xa3, 0x0e, + 0xf0, 0xfd, 0x96, 0x32, 0x52, 0xdc, 0xf2, 0xd1, 0x51, 0x07, 0xc8, 0x2d, 0xfc, 0x1a, 0x6d, 0x7a, + 0xe0, 0x54, 0x7b, 0xa5, 0xcc, 0xf1, 0x52, 0x5e, 0xe5, 0xd6, 0x3d, 0x51, 0x0f, 0xa9, 0x60, 0xdc, + 0x3f, 0x5d, 0x99, 0x3a, 0xe7, 0x7e, 0x3b, 0x22, 0xa9, 0x38, 0xd6, 0x81, 0xb0, 0xfb, 0xd4, 0x04, + 0xc9, 0xce, 0x08, 0x21, 0xd5, 0x67, 0x08, 0xaf, 0x0c, 0xaf, 0x44, 0xea, 0x73, 0x17, 0x5f, 0x16, + 0x27, 0x49, 0x6e, 0xb9, 0x31, 0x02, 0x49, 0x67, 0xb2, 0x33, 0x84, 0xdf, 0xdb, 0x63, 0xf9, 0x89, + 0x9c, 0x11, 0x82, 0x7f, 0x20, 0xac, 0xf4, 0x56, 0xee, 0x73, 0x5b, 0x2a, 0xd0, 0x53, 0x5a, 0xc3, + 0xf3, 0xcc, 0xb7, 0x72, 0x95, 0x53, 0x5b, 0x99, 0xdf, 0x9e, 0xaf, 0xa7, 0x65, 0x96, 0xcd, 0x46, + 0xc3, 0x01, 0xd7, 0xfd, 0xd0, 0x73, 0x2c, 0xdb, 0x34, 0x84, 0xdb, 0x74, 0x89, 0xff, 0x5d, 0x68, + 0x1b, 0x45, 0x6a, 0x9b, 0x12, 0xed, 0x7f, 0x09, 0x69, 0xbf, 0xe9, 0xba, 0x17, 0x77, 0x79, 0x1a, + 0xcf, 0x53, 0xdf, 0x2a, 0xb4, 0x37, 0xc4, 0xcb, 0xf4, 0x2a, 0x1c, 0xa9, 0x60, 0x4a, 0x14, 0xae, + 0xc9, 0xb1, 0xe4, 0xd3, 0x6b, 0xb5, 0xa2, 0xf2, 0xfe, 0x5f, 0x1a, 0x7c, 0x8b, 0xe4, 0x80, 0x89, + 0x26, 0x99, 0x12, 0x05, 0xee, 0xe2, 0xab, 0x9c, 0x9c, 0x68, 0x3e, 0xbb, 0x76, 0x93, 0x25, 0x69, + 0xa2, 0x6a, 0x16, 0xbf, 0x35, 0x00, 0x13, 0xd1, 0xd5, 0x34, 0x26, 0xfc, 0xab, 0x7d, 0xea, 0xd0, + 0x76, 0xa0, 0xa6, 0x7a, 0x05, 0xbf, 0x19, 0xb1, 0x4a, 0x67, 0x15, 0xe7, 0xb9, 0xf9, 0x63, 0xda, + 0xb2, 0x1a, 0xd4, 0x83, 0x6d, 0x7f, 0x5e, 0x82, 0x88, 0x1a, 0x40, 0x6f, 0xe2, 0x1b, 0x31, 0x3e, + 0x32, 0xd0, 0x03, 0x9c, 0x8b, 0x38, 0x3d, 0xa4, 0x36, 0x35, 0xa1, 0x02, 0x90, 0x68, 0x28, 0xa8, + 0x37, 0xf0, 0xf5, 0x91, 0x70, 0x91, 0xa1, 0xf4, 0xcf, 0x12, 0x9e, 0xe7, 0x3e, 0xe4, 0x07, 0x84, + 0xdf, 0x18, 0xb8, 0x32, 0x90, 0x8d, 0x51, 0x2b, 0x37, 0xea, 0xe2, 0xa3, 0x14, 0x27, 0x40, 0xc8, + 0x32, 0x0b, 0x5f, 0xbc, 0xfc, 0xfb, 0xeb, 0xd9, 0x5b, 0x44, 0xd5, 0x47, 0x5c, 0xbb, 0x9a, 0x00, + 0xae, 0xb8, 0x7b, 0x91, 0x67, 0x08, 0x2f, 0x45, 0x86, 0x2e, 0x59, 0x8b, 0x4d, 0x78, 0x61, 0x9c, + 0x2b, 0xeb, 0x09, 0xbd, 0x25, 0xb5, 0x0d, 0x4e, 0xad, 0x40, 0x56, 0xf5, 0xb8, 0x1b, 0xa1, 0x7e, + 0x1c, 0x34, 0x9e, 0x13, 0xf2, 0x33, 0xea, 0x5f, 0x24, 0xc2, 0xc3, 0x8f, 0x94, 0xc7, 0x65, 0x1e, + 0x32, 0xf4, 0x95, 0x3b, 0x93, 0x81, 0x24, 0xeb, 0xfb, 0x9c, 0x75, 0x99, 0x14, 0x47, 0xb1, 0x16, + 0x5b, 0x44, 0x3f, 0xee, 0xed, 0x9d, 0x13, 0x79, 0xb5, 0x25, 0xcf, 0x91, 0xdc, 0xd3, 0xd1, 0xf1, + 0x41, 0x4a, 0x63, 0x75, 0x1b, 0x98, 0xa3, 0x4a, 0x79, 0x22, 0x8c, 0xe4, 0x7e, 0x87, 0x73, 0xd7, + 0xc8, 0x5a, 0xac, 0xe2, 0xae, 0xce, 0x47, 0xaf, 0x7e, 0xcc, 0x3f, 0x4e, 0x22, 0xb4, 0x43, 0x3d, + 0x79, 0x3c, 0xed, 0xc1, 0x11, 0x34, 0x9e, 0xf6, 0x90, 0xa6, 0x9f, 0x98, 0x36, 0x9f, 0x67, 0xfa, + 0x31, 0xff, 0x38, 0x21, 0xdf, 0x07, 0x87, 0x2e, 0xdc, 0x46, 0xc7, 0x1c, 0xba, 0x21, 0x6d, 0x7d, + 0xcc, 0xa1, 0x1b, 0xd6, 0xa3, 0xd5, 0xdb, 0x9c, 0x70, 0x9e, 0xe4, 0xe2, 0x09, 0x93, 0x1f, 0x11, + 0x7e, 0xfd, 0x42, 0x57, 0x24, 0x5a, 0x6c, 0xba, 0x81, 0xae, 0xab, 0xe8, 0x89, 0xfd, 0x25, 0xb9, + 0x12, 0x27, 0xb7, 0x46, 0x0a, 0xc9, 0x37, 0x30, 0xf9, 0x12, 0xe1, 0xc5, 0x50, 0x37, 0x26, 0x85, + 0xd8, 0xa4, 0x91, 0x46, 0xae, 0xbc, 0x93, 0xc8, 0x37, 0xa9, 0x72, 0x1d, 0x41, 0xe0, 0x34, 0x98, + 0x91, 0xc3, 0x7a, 0x3c, 0x79, 0x2f, 0x36, 0x65, 0xcc, 0xe8, 0x50, 0xee, 0xff, 0x07, 0xa4, 0xa4, + 0x7e, 0x8f, 0x53, 0xdf, 0x20, 0xda, 0x28, 0xea, 0x5d, 0x89, 0xd6, 0xf9, 0x6f, 0x3c, 0xa8, 0x0a, + 0x7d, 0xc9, 0x4b, 0x24, 0x47, 0xe3, 0xe0, 0x28, 0x21, 0xf7, 0x12, 0xd1, 0x19, 0x18, 0x5d, 0xca, + 0xbb, 0x13, 0xe3, 0x64, 0x11, 0x3b, 0xbc, 0x88, 0x4d, 0xf2, 0xfe, 0x04, 0xdd, 0xad, 0x57, 0x57, + 0x9b, 0xc7, 0xf3, 0x7f, 0x3b, 0xba, 0x5b, 0x70, 0x7a, 0x96, 0x43, 0x2f, 0xce, 0x72, 0xe8, 0xaf, + 0xb3, 0x1c, 0xfa, 0xea, 0x3c, 0x37, 0xf3, 0xe2, 0x3c, 0x37, 0xf3, 0xfb, 0x79, 0x6e, 0x06, 0x67, + 0x2d, 0x36, 0x82, 0xdd, 0x3e, 0xfa, 0x44, 0x33, 0x2d, 0xef, 0xf1, 0x61, 0x4d, 0xab, 0xb3, 0x76, + 0x88, 0xc1, 0xba, 0xc5, 0xc2, 0x7c, 0x9e, 0xf6, 0x18, 0xd5, 0x2e, 0xf3, 0xbf, 0x0b, 0xca, 0xff, + 0x06, 0x00, 0x00, 0xff, 0xff, 0x5b, 0xb6, 0xb3, 0xff, 0x2c, 0x11, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1759,6 +1787,11 @@ func (m *QueryGetMarketOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, er i-- dAtA[i] = 0x9a } + if m.AfterOrderId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.AfterOrderId)) + i-- + dAtA[i] = 0x18 + } if len(m.OrderType) > 0 { i -= len(m.OrderType) copy(dAtA[i:], m.OrderType) @@ -1859,6 +1892,11 @@ func (m *QueryGetOwnerOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, err i-- dAtA[i] = 0x9a } + if m.AfterOrderId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.AfterOrderId)) + i-- + dAtA[i] = 0x18 + } if len(m.OrderType) > 0 { i -= len(m.OrderType) copy(dAtA[i:], m.OrderType) @@ -1961,6 +1999,11 @@ func (m *QueryGetAssetOrdersRequest) MarshalToSizedBuffer(dAtA []byte) (int, err i-- dAtA[i] = 0x9a } + if m.AfterOrderId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.AfterOrderId)) + i-- + dAtA[i] = 0x18 + } if len(m.OrderType) > 0 { i -= len(m.OrderType) copy(dAtA[i:], m.OrderType) @@ -2404,6 +2447,9 @@ func (m *QueryGetMarketOrdersRequest) Size() (n int) { if l > 0 { n += 1 + l + sovQuery(uint64(l)) } + if m.AfterOrderId != 0 { + n += 1 + sovQuery(uint64(m.AfterOrderId)) + } if m.Pagination != nil { l = m.Pagination.Size() n += 2 + l + sovQuery(uint64(l)) @@ -2444,6 +2490,9 @@ func (m *QueryGetOwnerOrdersRequest) Size() (n int) { if l > 0 { n += 1 + l + sovQuery(uint64(l)) } + if m.AfterOrderId != 0 { + n += 1 + sovQuery(uint64(m.AfterOrderId)) + } if m.Pagination != nil { l = m.Pagination.Size() n += 2 + l + sovQuery(uint64(l)) @@ -2484,6 +2533,9 @@ func (m *QueryGetAssetOrdersRequest) Size() (n int) { if l > 0 { n += 1 + l + sovQuery(uint64(l)) } + if m.AfterOrderId != 0 { + n += 1 + sovQuery(uint64(m.AfterOrderId)) + } if m.Pagination != nil { l = m.Pagination.Size() n += 2 + l + sovQuery(uint64(l)) @@ -3135,6 +3187,25 @@ func (m *QueryGetMarketOrdersRequest) Unmarshal(dAtA []byte) error { } m.OrderType = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AfterOrderId", wireType) + } + m.AfterOrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AfterOrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } case 99: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) @@ -3405,6 +3476,25 @@ func (m *QueryGetOwnerOrdersRequest) Unmarshal(dAtA []byte) error { } m.OrderType = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AfterOrderId", wireType) + } + m.AfterOrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AfterOrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } case 99: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) @@ -3675,6 +3765,25 @@ func (m *QueryGetAssetOrdersRequest) Unmarshal(dAtA []byte) error { } m.OrderType = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AfterOrderId", wireType) + } + m.AfterOrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AfterOrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } case 99: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) From 696c7e54ec785ae1173cb2c0b5ef551de746b3ef Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 2 Oct 2023 13:38:53 -0600 Subject: [PATCH 219/309] [1658]: Rename QueryMarketInfo to QueryGetMarket. Add QueryGetAllMarkets. Fill in the fields for both of those. --- docs/proto-docs.md | 116 ++- proto/provenance/exchange/v1/market.proto | 10 + proto/provenance/exchange/v1/query.proto | 46 +- x/exchange/market.pb.go | 414 +++++++++-- x/exchange/query.pb.go | 869 ++++++++++++++++++---- x/exchange/query.pb.gw.go | 226 +++++- 6 files changed, 1412 insertions(+), 269 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 0dd013c45f..7e754236a3 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -74,6 +74,7 @@ - [FeeRatio](#provenance.exchange.v1.FeeRatio) - [Market](#provenance.exchange.v1.Market) - [MarketAccount](#provenance.exchange.v1.MarketAccount) + - [MarketBrief](#provenance.exchange.v1.MarketBrief) - [MarketDetails](#provenance.exchange.v1.MarketDetails) - [Permission](#provenance.exchange.v1.Permission) @@ -91,18 +92,20 @@ - [GenesisState](#provenance.exchange.v1.GenesisState) - [provenance/exchange/v1/query.proto](#provenance/exchange/v1/query.proto) + - [QueryGetAllMarketsRequest](#provenance.exchange.v1.QueryGetAllMarketsRequest) + - [QueryGetAllMarketsResponse](#provenance.exchange.v1.QueryGetAllMarketsResponse) - [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) - [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) - [QueryGetAssetOrdersRequest](#provenance.exchange.v1.QueryGetAssetOrdersRequest) - [QueryGetAssetOrdersResponse](#provenance.exchange.v1.QueryGetAssetOrdersResponse) - [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) - [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) + - [QueryGetMarketRequest](#provenance.exchange.v1.QueryGetMarketRequest) + - [QueryGetMarketResponse](#provenance.exchange.v1.QueryGetMarketResponse) - [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) - [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) - [QueryGetOwnerOrdersRequest](#provenance.exchange.v1.QueryGetOwnerOrdersRequest) - [QueryGetOwnerOrdersResponse](#provenance.exchange.v1.QueryGetOwnerOrdersResponse) - - [QueryMarketInfoRequest](#provenance.exchange.v1.QueryMarketInfoRequest) - - [QueryMarketInfoResponse](#provenance.exchange.v1.QueryMarketInfoResponse) - [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) - [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) - [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) @@ -1685,6 +1688,23 @@ MarketAccount is an account type for use with the accounts module to hold some b + + +### MarketBrief +MarketBrief is a message containing brief, superficial information about a market. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier for this market. | +| `market_address` | [string](#string) | | market_address is the bech32 address string of this market's account. | +| `market_details` | [MarketDetails](#provenance.exchange.v1.MarketDetails) | | market_details is some information about this market. | + + + + + + ### MarketDetails @@ -1891,6 +1911,37 @@ GenesisState is the data that should be loaded into the exchange module during g + + +### QueryGetAllMarketsRequest +QueryGetAllMarketsRequest is a request message for the QueryGetAllMarkets endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | + + + + + + + + +### QueryGetAllMarketsResponse +QueryGetAllMarketsResponse is a response message for the QueryGetAllMarkets endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `markets` | [MarketBrief](#provenance.exchange.v1.MarketBrief) | repeated | markets are a page of the briefs for all markets. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | + + + + + + ### QueryGetAllOrdersRequest @@ -1990,6 +2041,37 @@ QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders + + +### QueryGetMarketRequest +QueryGetMarketRequest is a request message for the QueryGetMarket endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the id of the market to look up. | + + + + + + + + +### QueryGetMarketResponse +QueryGetMarketResponse is a response message for the QueryGetMarket endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | address is the bech32 address string of this market's account. | +| `market` | [Market](#provenance.exchange.v1.Market) | | market is all information and details of the market. | + + + + + + ### QueryGetOrderRequest @@ -2054,31 +2136,6 @@ QueryGetOwnerOrdersResponse is a response message for the QueryGetOwnerOrders en - - -### QueryMarketInfoRequest -QueryMarketInfoRequest is a request message for the QueryMarketInfo endpoint. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `market_id` | [uint32](#uint32) | | TODO[1658]: QueryMarketInfoRequest | - - - - - - - - -### QueryMarketInfoResponse -QueryMarketInfoResponse is a response message for the QueryMarketInfo endpoint. - - - - - - ### QueryOrderFeeCalcRequest @@ -2197,11 +2254,12 @@ Query is the service for exchange module's query endpoints. | ----------- | ------------ | ------------- | ------------| ------- | -------- | | `QueryOrderFeeCalc` | [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) | [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) | QueryOrderFeeCalc calculates the fees that will be associated with the provided order. | GET|/provenance/exchange/v1/fees/order| | `QueryGetOrder` | [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) | [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) | QueryGetOrder looks up an order by id. | GET|/provenance/exchange/v1/order/{order_id}| -| `QueryGetMarketOrders` | [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) | [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) | QueryGetMarketOrders looks up the orders in a market. | GET|/provenance/exchange/v1/market/{market_id}/orders| +| `QueryGetMarketOrders` | [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) | [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) | QueryGetMarketOrders looks up the orders in a market. | GET|/provenance/exchange/v1/orders/market/{market_id}GET|/provenance/exchange/v1/market/{market_id}/orders| | `QueryGetOwnerOrders` | [QueryGetOwnerOrdersRequest](#provenance.exchange.v1.QueryGetOwnerOrdersRequest) | [QueryGetOwnerOrdersResponse](#provenance.exchange.v1.QueryGetOwnerOrdersResponse) | QueryGetOwnerOrders looks up the orders from the provided owner address. | GET|/provenance/exchange/v1/orders/owner/{owner}| | `QueryGetAssetOrders` | [QueryGetAssetOrdersRequest](#provenance.exchange.v1.QueryGetAssetOrdersRequest) | [QueryGetAssetOrdersResponse](#provenance.exchange.v1.QueryGetAssetOrdersResponse) | QueryGetAssetOrders looks up the orders for a specific asset denom. | GET|/provenance/exchange/v1/orders/asset/{asset}| | `QueryGetAllOrders` | [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) | [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) | QueryGetAllOrders gets all orders in the exchange module. | GET|/provenance/exchange/v1/orders| -| `QueryMarketInfo` | [QueryMarketInfoRequest](#provenance.exchange.v1.QueryMarketInfoRequest) | [QueryMarketInfoResponse](#provenance.exchange.v1.QueryMarketInfoResponse) | QueryMarketInfo returns the information/details about a market. | GET|/provenance/exchange/v1/market/{market_id}| +| `QueryGetMarket` | [QueryGetMarketRequest](#provenance.exchange.v1.QueryGetMarketRequest) | [QueryGetMarketResponse](#provenance.exchange.v1.QueryGetMarketResponse) | QueryGetMarket returns all the information and details about a market. | GET|/provenance/exchange/v1/market/{market_id}| +| `QueryGetAllMarkets` | [QueryGetAllMarketsRequest](#provenance.exchange.v1.QueryGetAllMarketsRequest) | [QueryGetAllMarketsResponse](#provenance.exchange.v1.QueryGetAllMarketsResponse) | QueryGetAllMarkets returns brief information about each market. | GET|/provenance/exchange/v1/markets| | `QueryParams` | [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) | [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) | QueryParams returns the exchange module parameters. | GET|/provenance/exchange/v1/params| | `QueryValidateCreateMarket` | [QueryValidateCreateMarketRequest](#provenance.exchange.v1.QueryValidateCreateMarketRequest) | [QueryValidateCreateMarketResponse](#provenance.exchange.v1.QueryValidateCreateMarketResponse) | QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. | GET|/provenance/exchange/v1/validate/create_market| | `QueryValidateManageFees` | [QueryValidateManageFeesRequest](#provenance.exchange.v1.QueryValidateManageFeesRequest) | [QueryValidateManageFeesResponse](#provenance.exchange.v1.QueryValidateManageFeesResponse) | QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. | GET|/provenance/exchange/v1/market/{market_id}/validate/manage_fees| diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index bef0ff2217..d7544cff33 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -42,6 +42,16 @@ message MarketDetails { string icon_uri = 4; } +// MarketBrief is a message containing brief, superficial information about a market. +message MarketBrief { + // market_id is the numerical identifier for this market. + uint32 market_id = 1; + // market_address is the bech32 address string of this market's account. + string market_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market_details is some information about this market. + MarketDetails market_details = 3 [(gogoproto.nullable) = false]; +} + // Market contains all information about a market. message Market { // market_id is the numerical identifier for this market. diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 9b84abc53a..4e913e8aae 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -12,6 +12,7 @@ import "cosmos/base/v1beta1/coin.proto"; import "google/api/annotations.proto"; import "gogoproto/gogo.proto"; import "provenance/exchange/v1/orders.proto"; +import "provenance/exchange/v1/market.proto"; // Query is the service for exchange module's query endpoints. service Query { @@ -27,7 +28,10 @@ service Query { // QueryGetMarketOrders looks up the orders in a market. rpc QueryGetMarketOrders(QueryGetMarketOrdersRequest) returns (QueryGetMarketOrdersResponse) { - option (google.api.http).get = "/provenance/exchange/v1/market/{market_id}/orders"; + option (google.api.http) = { + get: "/provenance/exchange/v1/orders/market/{market_id}" + additional_bindings: {get: "/provenance/exchange/v1/market/{market_id}/orders"} + }; } // QueryGetOwnerOrders looks up the orders from the provided owner address. @@ -45,11 +49,16 @@ service Query { option (google.api.http).get = "/provenance/exchange/v1/orders"; } - // QueryMarketInfo returns the information/details about a market. - rpc QueryMarketInfo(QueryMarketInfoRequest) returns (QueryMarketInfoResponse) { + // QueryGetMarket returns all the information and details about a market. + rpc QueryGetMarket(QueryGetMarketRequest) returns (QueryGetMarketResponse) { option (google.api.http).get = "/provenance/exchange/v1/market/{market_id}"; } + // QueryGetAllMarkets returns brief information about each market. + rpc QueryGetAllMarkets(QueryGetAllMarketsRequest) returns (QueryGetAllMarketsResponse) { + option (google.api.http).get = "/provenance/exchange/v1/markets"; + } + // QueryParams returns the exchange module parameters. rpc QueryParams(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/provenance/exchange/v1/params"; @@ -189,14 +198,33 @@ message QueryGetAllOrdersResponse { cosmos.base.query.v1beta1.PageResponse pagination = 99; } -// QueryMarketInfoRequest is a request message for the QueryMarketInfo endpoint. -message QueryMarketInfoRequest { +// QueryGetMarketRequest is a request message for the QueryGetMarket endpoint. +message QueryGetMarketRequest { + // market_id is the id of the market to look up. uint32 market_id = 1; - // TODO[1658]: QueryMarketInfoRequest } -// QueryMarketInfoResponse is a response message for the QueryMarketInfo endpoint. -message QueryMarketInfoResponse { - // TODO[1658]: QueryMarketInfoResponse + +// QueryGetMarketResponse is a response message for the QueryGetMarket endpoint. +message QueryGetMarketResponse { + // address is the bech32 address string of this market's account. + string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market is all information and details of the market. + Market market = 2; +} + +// QueryGetAllMarketsRequest is a request message for the QueryGetAllMarkets endpoint. +message QueryGetAllMarketsRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 99; +} + +// QueryGetAllMarketsResponse is a response message for the QueryGetAllMarkets endpoint. +message QueryGetAllMarketsResponse { + // markets are a page of the briefs for all markets. + repeated MarketBrief markets = 1; + + // pagination is the resulting pagination parameters. + cosmos.base.query.v1beta1.PageResponse pagination = 99; } // QueryParamsRequest is a request message for the QueryParams endpoint. diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index 81caaa17f7..3d20922a32 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -189,6 +189,70 @@ func (m *MarketDetails) GetIconUri() string { return "" } +// MarketBrief is a message containing brief, superficial information about a market. +type MarketBrief struct { + // market_id is the numerical identifier for this market. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // market_address is the bech32 address string of this market's account. + MarketAddress string `protobuf:"bytes,2,opt,name=market_address,json=marketAddress,proto3" json:"market_address,omitempty"` + // market_details is some information about this market. + MarketDetails MarketDetails `protobuf:"bytes,3,opt,name=market_details,json=marketDetails,proto3" json:"market_details"` +} + +func (m *MarketBrief) Reset() { *m = MarketBrief{} } +func (m *MarketBrief) String() string { return proto.CompactTextString(m) } +func (*MarketBrief) ProtoMessage() {} +func (*MarketBrief) Descriptor() ([]byte, []int) { + return fileDescriptor_d5cf198f1dd7e167, []int{2} +} +func (m *MarketBrief) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MarketBrief) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MarketBrief.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MarketBrief) XXX_Merge(src proto.Message) { + xxx_messageInfo_MarketBrief.Merge(m, src) +} +func (m *MarketBrief) XXX_Size() int { + return m.Size() +} +func (m *MarketBrief) XXX_DiscardUnknown() { + xxx_messageInfo_MarketBrief.DiscardUnknown(m) +} + +var xxx_messageInfo_MarketBrief proto.InternalMessageInfo + +func (m *MarketBrief) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *MarketBrief) GetMarketAddress() string { + if m != nil { + return m.MarketAddress + } + return "" +} + +func (m *MarketBrief) GetMarketDetails() MarketDetails { + if m != nil { + return m.MarketDetails + } + return MarketDetails{} +} + // Market contains all information about a market. type Market struct { // market_id is the numerical identifier for this market. @@ -250,7 +314,7 @@ func (m *Market) Reset() { *m = Market{} } func (m *Market) String() string { return proto.CompactTextString(m) } func (*Market) ProtoMessage() {} func (*Market) Descriptor() ([]byte, []int) { - return fileDescriptor_d5cf198f1dd7e167, []int{2} + return fileDescriptor_d5cf198f1dd7e167, []int{3} } func (m *Market) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -382,7 +446,7 @@ type FeeRatio struct { func (m *FeeRatio) Reset() { *m = FeeRatio{} } func (*FeeRatio) ProtoMessage() {} func (*FeeRatio) Descriptor() ([]byte, []int) { - return fileDescriptor_d5cf198f1dd7e167, []int{3} + return fileDescriptor_d5cf198f1dd7e167, []int{4} } func (m *FeeRatio) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -437,7 +501,7 @@ func (m *AccessGrant) Reset() { *m = AccessGrant{} } func (m *AccessGrant) String() string { return proto.CompactTextString(m) } func (*AccessGrant) ProtoMessage() {} func (*AccessGrant) Descriptor() ([]byte, []int) { - return fileDescriptor_d5cf198f1dd7e167, []int{4} + return fileDescriptor_d5cf198f1dd7e167, []int{5} } func (m *AccessGrant) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -484,6 +548,7 @@ func init() { proto.RegisterEnum("provenance.exchange.v1.Permission", Permission_name, Permission_value) proto.RegisterType((*MarketAccount)(nil), "provenance.exchange.v1.MarketAccount") proto.RegisterType((*MarketDetails)(nil), "provenance.exchange.v1.MarketDetails") + proto.RegisterType((*MarketBrief)(nil), "provenance.exchange.v1.MarketBrief") proto.RegisterType((*Market)(nil), "provenance.exchange.v1.Market") proto.RegisterType((*FeeRatio)(nil), "provenance.exchange.v1.FeeRatio") proto.RegisterType((*AccessGrant)(nil), "provenance.exchange.v1.AccessGrant") @@ -494,69 +559,71 @@ func init() { } var fileDescriptor_d5cf198f1dd7e167 = []byte{ - // 985 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x41, 0x4f, 0x1b, 0xc7, - 0x17, 0xf7, 0x62, 0x03, 0xf6, 0x18, 0x88, 0xff, 0x43, 0xc2, 0x7f, 0x31, 0x95, 0xbd, 0x75, 0x14, - 0xc9, 0x69, 0x85, 0x2d, 0xa8, 0x7a, 0xe1, 0x66, 0x83, 0x69, 0x2d, 0x25, 0x04, 0xad, 0x6d, 0x45, - 0x8a, 0x2a, 0x6d, 0x67, 0x77, 0x9f, 0xcd, 0x88, 0xf5, 0xae, 0x33, 0x33, 0x0b, 0x49, 0xbf, 0x40, - 0x2b, 0x4e, 0x3d, 0xf6, 0x82, 0x44, 0xbf, 0x41, 0x0f, 0xfd, 0x10, 0x39, 0xa2, 0x9e, 0x7a, 0x42, - 0x15, 0x5c, 0x7a, 0xe8, 0xa9, 0x9f, 0xa0, 0xda, 0x99, 0xc5, 0xde, 0xb8, 0x46, 0x24, 0xea, 0x6d, - 0xde, 0xfb, 0xfd, 0xe6, 0xf7, 0x7e, 0xef, 0xcd, 0x78, 0xbc, 0xe8, 0xf1, 0x88, 0x05, 0x27, 0xe0, - 0x13, 0xdf, 0x81, 0x3a, 0xbc, 0x71, 0x8e, 0x88, 0x3f, 0x80, 0xfa, 0xc9, 0x56, 0x7d, 0x48, 0xd8, - 0x31, 0x88, 0xda, 0x88, 0x05, 0x22, 0xc0, 0x6b, 0x13, 0x52, 0xed, 0x96, 0x54, 0x3b, 0xd9, 0x2a, - 0x96, 0x9c, 0x80, 0x0f, 0x03, 0x5e, 0x27, 0xa1, 0x38, 0xaa, 0x9f, 0x6c, 0xd9, 0x20, 0xc8, 0x96, - 0x0c, 0xd4, 0xbe, 0x31, 0x6e, 0x13, 0x0e, 0x63, 0xdc, 0x09, 0xa8, 0x1f, 0xe3, 0xeb, 0x0a, 0xb7, - 0x64, 0x54, 0x57, 0x41, 0x0c, 0x3d, 0x1c, 0x04, 0x83, 0x40, 0xe5, 0xa3, 0x95, 0xca, 0x56, 0xfe, - 0xd2, 0xd0, 0xf2, 0x73, 0xe9, 0xac, 0xe1, 0x38, 0x41, 0xe8, 0x0b, 0xfc, 0x2d, 0x5a, 0x8a, 0xd4, - 0x2d, 0xa2, 0x62, 0x5d, 0x33, 0xb4, 0x6a, 0x7e, 0xdb, 0xa8, 0xc5, 0x62, 0xd2, 0x4c, 0x5c, 0xb9, - 0xd6, 0x24, 0x1c, 0xe2, 0x7d, 0xcd, 0x8d, 0xcb, 0xab, 0xb2, 0xf6, 0xf7, 0x55, 0x79, 0xf5, 0x2d, - 0x19, 0x7a, 0x3b, 0x95, 0xa4, 0x46, 0xc5, 0xcc, 0xdb, 0x13, 0x26, 0xde, 0x40, 0x39, 0x35, 0x0c, - 0x8b, 0xba, 0xfa, 0x9c, 0xa1, 0x55, 0x97, 0xcd, 0xac, 0x4a, 0xb4, 0x5d, 0x6c, 0xa2, 0x95, 0x18, - 0x74, 0x41, 0x10, 0xea, 0x71, 0x3d, 0x2d, 0x0d, 0x3c, 0xa9, 0xcd, 0x1e, 0x59, 0x4d, 0xb9, 0xdf, - 0x53, 0xe4, 0x66, 0xe6, 0xdd, 0x55, 0x39, 0x65, 0x2e, 0x0f, 0x93, 0xc9, 0x9d, 0xec, 0x0f, 0x17, - 0xe5, 0xd4, 0x4f, 0x17, 0xe5, 0x54, 0xe5, 0xfb, 0x71, 0xbb, 0x31, 0x86, 0x31, 0xca, 0xf8, 0x64, - 0x08, 0xb2, 0xcd, 0x9c, 0x29, 0xd7, 0xd8, 0x40, 0x79, 0x17, 0xb8, 0xc3, 0xe8, 0x48, 0xd0, 0xc0, - 0x97, 0x16, 0x73, 0x66, 0x32, 0x85, 0xcb, 0x28, 0x7f, 0x0a, 0x36, 0xa7, 0x02, 0xac, 0x90, 0x79, - 0xd2, 0x62, 0xce, 0x44, 0x71, 0xaa, 0xc7, 0x3c, 0xbc, 0x8e, 0xb2, 0xd4, 0x09, 0x7c, 0x2b, 0x64, - 0x54, 0xcf, 0x48, 0x74, 0x31, 0x8a, 0x7b, 0x8c, 0xee, 0x64, 0xfe, 0xbc, 0x28, 0x6b, 0x95, 0x5f, - 0x16, 0xd1, 0x82, 0x72, 0xf2, 0xfe, 0x3c, 0xb4, 0x7b, 0xe7, 0x31, 0xf7, 0x5f, 0xe7, 0x81, 0x0f, - 0xd0, 0x6a, 0x1f, 0xc0, 0x72, 0x18, 0x10, 0x01, 0x16, 0xe1, 0xc7, 0x56, 0xdf, 0x23, 0x42, 0x4f, - 0x1b, 0xe9, 0x6a, 0x7e, 0x7b, 0xfd, 0xf6, 0xa4, 0xa3, 0x23, 0x1b, 0x9f, 0xf4, 0x6e, 0x40, 0xfd, - 0x58, 0xac, 0xd0, 0x07, 0xd8, 0x95, 0x5b, 0x1b, 0xfc, 0x78, 0xdf, 0x23, 0x62, 0x4a, 0xcf, 0xa6, - 0xae, 0xd2, 0xcb, 0x7c, 0xac, 0x5e, 0x93, 0xba, 0x52, 0xef, 0x1b, 0x54, 0x8c, 0xf4, 0x38, 0x78, - 0x1e, 0x30, 0x8b, 0x83, 0x10, 0x1e, 0x0c, 0xc1, 0x17, 0x4a, 0x76, 0xfe, 0xc3, 0x64, 0xff, 0xdf, - 0x07, 0xe8, 0x48, 0x85, 0xce, 0x58, 0x40, 0xaa, 0x0f, 0xd0, 0x27, 0xb3, 0xd5, 0x19, 0x11, 0x34, - 0xe0, 0xfa, 0x82, 0xd4, 0x37, 0xee, 0x9a, 0xef, 0x3e, 0x80, 0x19, 0x11, 0xe3, 0x32, 0xeb, 0x33, - 0xca, 0x48, 0x9c, 0xe3, 0x57, 0x28, 0x02, 0x2d, 0x3b, 0x7c, 0x3b, 0xa3, 0x8b, 0xc5, 0x0f, 0xeb, - 0x62, 0xad, 0x0f, 0xd0, 0x8c, 0x04, 0xa6, 0x9a, 0x00, 0xb4, 0x31, 0x53, 0x3b, 0xee, 0x21, 0xfb, - 0x51, 0x3d, 0xe8, 0xff, 0x2e, 0x12, 0xb7, 0xf0, 0x14, 0x15, 0x88, 0xe3, 0xc0, 0x48, 0x50, 0x7f, - 0x60, 0x05, 0xcc, 0x05, 0xc6, 0xf5, 0x9c, 0xa1, 0x55, 0xb3, 0xe6, 0x83, 0x71, 0xfe, 0x85, 0x4c, - 0xe3, 0x6d, 0xf4, 0x88, 0x78, 0x5e, 0x70, 0x6a, 0x85, 0xfc, 0x3d, 0x4b, 0x3a, 0x92, 0xfc, 0x55, - 0x09, 0xf6, 0x78, 0xb2, 0x08, 0x3e, 0x40, 0xcb, 0x91, 0x0c, 0xe7, 0xd6, 0x80, 0x11, 0x5f, 0x70, - 0x3d, 0x2f, 0x7d, 0x3f, 0xbe, 0xcb, 0x77, 0x43, 0x92, 0xbf, 0x8a, 0xb8, 0xb1, 0xf5, 0x25, 0x32, - 0x49, 0x71, 0xbc, 0x89, 0x56, 0x19, 0xbc, 0xb6, 0x88, 0x10, 0x2c, 0x71, 0xbb, 0xf5, 0x25, 0x23, - 0x5d, 0xcd, 0x99, 0x05, 0x06, 0xaf, 0x1b, 0x42, 0xb0, 0xf1, 0xdd, 0x9d, 0x45, 0xb7, 0xa9, 0xab, - 0x2f, 0xcf, 0xa0, 0x37, 0xa9, 0x5b, 0xf9, 0x0e, 0x65, 0x6f, 0x07, 0x87, 0xbf, 0x44, 0xf3, 0x23, - 0x46, 0x1d, 0x88, 0x9f, 0xc7, 0x7b, 0xcf, 0x51, 0xb1, 0xf1, 0x16, 0x4a, 0xf7, 0x01, 0xe2, 0x9f, - 0xf0, 0xbd, 0x9b, 0x22, 0xee, 0x4e, 0xe6, 0xf6, 0xe1, 0xca, 0x27, 0xba, 0xc7, 0xdb, 0x68, 0x91, - 0xb8, 0x2e, 0x03, 0xce, 0xd5, 0xcb, 0xd5, 0xd4, 0x7f, 0xfb, 0x75, 0xf3, 0x61, 0xac, 0xd7, 0x50, - 0x48, 0x47, 0x30, 0xea, 0x0f, 0xcc, 0x5b, 0x22, 0xde, 0x43, 0xf9, 0x11, 0xb0, 0x21, 0xe5, 0x9c, - 0x06, 0x7e, 0xf4, 0x8e, 0xa4, 0xab, 0x2b, 0xdb, 0x95, 0xbb, 0x66, 0x7d, 0x38, 0xa6, 0x9a, 0xc9, - 0x6d, 0x9f, 0xfd, 0x3c, 0x87, 0xd0, 0x04, 0xc3, 0x9f, 0xa3, 0xb5, 0xc3, 0x96, 0xf9, 0xbc, 0xdd, - 0xe9, 0xb4, 0x5f, 0x1c, 0x58, 0xbd, 0x83, 0xce, 0x61, 0x6b, 0xb7, 0xbd, 0xdf, 0x6e, 0xed, 0x15, - 0x52, 0xc5, 0x07, 0x67, 0xe7, 0x46, 0x3e, 0xf4, 0xf9, 0x08, 0x1c, 0xda, 0xa7, 0xe0, 0xe2, 0x4f, - 0xd1, 0xff, 0x12, 0xe4, 0x4e, 0xab, 0xdb, 0x7d, 0xd6, 0x2a, 0x68, 0x45, 0x74, 0x76, 0x6e, 0x2c, - 0xa8, 0x1b, 0x33, 0x45, 0xd9, 0x6d, 0x1c, 0xec, 0xb6, 0x9e, 0x15, 0xe6, 0x14, 0xc5, 0x89, 0x4c, - 0x7a, 0xf8, 0x09, 0x5a, 0x4d, 0x50, 0x5e, 0xb6, 0xbb, 0x5f, 0xef, 0x99, 0x8d, 0x97, 0x85, 0x74, - 0x71, 0xe9, 0xec, 0xdc, 0xc8, 0x9e, 0x52, 0x71, 0xe4, 0x32, 0x72, 0x3a, 0xa5, 0xd4, 0x3b, 0xdc, - 0x6b, 0x74, 0x5b, 0x85, 0x8c, 0x52, 0x0a, 0x47, 0x2e, 0x11, 0x30, 0x65, 0x7e, 0xb2, 0xec, 0x14, - 0xe6, 0x95, 0xf9, 0x44, 0xe3, 0xf8, 0x29, 0x7a, 0x94, 0x20, 0x37, 0xba, 0x5d, 0xb3, 0xdd, 0xec, - 0x75, 0x5b, 0x9d, 0xc2, 0x42, 0x71, 0xe5, 0xec, 0xdc, 0x40, 0xd1, 0x35, 0xa2, 0x76, 0x28, 0x80, - 0x37, 0xe1, 0xdd, 0x75, 0x49, 0xbb, 0xbc, 0x2e, 0x69, 0x7f, 0x5c, 0x97, 0xb4, 0x1f, 0x6f, 0x4a, - 0xa9, 0xcb, 0x9b, 0x52, 0xea, 0xf7, 0x9b, 0x52, 0x0a, 0xad, 0xd3, 0xe0, 0x8e, 0x81, 0x1f, 0x6a, - 0xaf, 0x6a, 0x03, 0x2a, 0x8e, 0x42, 0xbb, 0xe6, 0x04, 0xc3, 0xfa, 0x84, 0xb4, 0x49, 0x83, 0x44, - 0x54, 0x7f, 0x33, 0xfe, 0xaa, 0xb0, 0x17, 0xe4, 0x7f, 0xf8, 0x17, 0xff, 0x04, 0x00, 0x00, 0xff, - 0xff, 0x7a, 0x16, 0x37, 0xe4, 0x73, 0x08, 0x00, 0x00, + // 1016 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x41, 0x6f, 0x1a, 0x47, + 0x14, 0x66, 0x0d, 0xb1, 0x61, 0xb0, 0x1d, 0x3a, 0x4e, 0xdc, 0x35, 0xae, 0x60, 0x4b, 0x14, 0x89, + 0xb4, 0x32, 0xc8, 0xae, 0x7a, 0xf1, 0xa5, 0x02, 0x1b, 0xb7, 0x48, 0x89, 0x63, 0x2d, 0xa0, 0x48, + 0x51, 0xa5, 0xed, 0xb0, 0xfb, 0xc0, 0x23, 0x2f, 0xbb, 0x64, 0x66, 0xb0, 0x93, 0xfe, 0x81, 0x56, + 0x3e, 0xf5, 0xd8, 0x8b, 0x25, 0xf7, 0x1f, 0xf4, 0xd0, 0x7b, 0xaf, 0x39, 0x5a, 0x3d, 0xf5, 0x64, + 0x55, 0xf6, 0xa5, 0x87, 0x9e, 0xfa, 0x0b, 0xaa, 0x9d, 0x59, 0x96, 0x0d, 0xc1, 0x75, 0xa2, 0xaa, + 0xb7, 0x79, 0xef, 0xfb, 0xe6, 0x7b, 0xdf, 0x7b, 0x3c, 0x46, 0x8b, 0x1e, 0x0c, 0x99, 0x7f, 0x0c, + 0x1e, 0xf1, 0x6c, 0xa8, 0xc2, 0x4b, 0xfb, 0x90, 0x78, 0x7d, 0xa8, 0x1e, 0x6f, 0x56, 0x07, 0x84, + 0x1d, 0x81, 0xa8, 0x0c, 0x99, 0x2f, 0x7c, 0xbc, 0x3a, 0x21, 0x55, 0xc6, 0xa4, 0xca, 0xf1, 0x66, + 0xbe, 0x60, 0xfb, 0x7c, 0xe0, 0xf3, 0x2a, 0x19, 0x89, 0xc3, 0xea, 0xf1, 0x66, 0x17, 0x04, 0xd9, + 0x94, 0x81, 0xba, 0x17, 0xe1, 0x5d, 0xc2, 0x21, 0xc2, 0x6d, 0x9f, 0x7a, 0x21, 0xbe, 0xa6, 0x70, + 0x4b, 0x46, 0x55, 0x15, 0x84, 0xd0, 0xbd, 0xbe, 0xdf, 0xf7, 0x55, 0x3e, 0x38, 0xa9, 0x6c, 0xe9, + 0x2f, 0x0d, 0x2d, 0x3d, 0x91, 0xce, 0x6a, 0xb6, 0xed, 0x8f, 0x3c, 0x81, 0xbf, 0x41, 0x8b, 0x81, + 0xba, 0x45, 0x54, 0xac, 0x6b, 0x86, 0x56, 0xce, 0x6e, 0x19, 0x95, 0x50, 0x4c, 0x9a, 0x09, 0x2b, + 0x57, 0xea, 0x84, 0x43, 0x78, 0xaf, 0xbe, 0x7e, 0x71, 0x59, 0xd4, 0xfe, 0xbe, 0x2c, 0xae, 0xbc, + 0x22, 0x03, 0x77, 0xbb, 0x14, 0xd7, 0x28, 0x99, 0xd9, 0xee, 0x84, 0x89, 0xd7, 0x51, 0x46, 0x0d, + 0xc3, 0xa2, 0x8e, 0x3e, 0x67, 0x68, 0xe5, 0x25, 0x33, 0xad, 0x12, 0x4d, 0x07, 0x9b, 0x68, 0x39, + 0x04, 0x1d, 0x10, 0x84, 0xba, 0x5c, 0x4f, 0x4a, 0x03, 0x0f, 0x2b, 0xb3, 0x47, 0x56, 0x51, 0xee, + 0x77, 0x15, 0xb9, 0x9e, 0x7a, 0x7d, 0x59, 0x4c, 0x98, 0x4b, 0x83, 0x78, 0x72, 0x3b, 0xfd, 0xfd, + 0x79, 0x31, 0xf1, 0xe3, 0x79, 0x31, 0x51, 0xfa, 0x2e, 0x6a, 0x37, 0xc4, 0x30, 0x46, 0x29, 0x8f, + 0x0c, 0x40, 0xb6, 0x99, 0x31, 0xe5, 0x19, 0x1b, 0x28, 0xeb, 0x00, 0xb7, 0x19, 0x1d, 0x0a, 0xea, + 0x7b, 0xd2, 0x62, 0xc6, 0x8c, 0xa7, 0x70, 0x11, 0x65, 0x4f, 0xa0, 0xcb, 0xa9, 0x00, 0x6b, 0xc4, + 0x5c, 0x69, 0x31, 0x63, 0xa2, 0x30, 0xd5, 0x61, 0x2e, 0x5e, 0x43, 0x69, 0x6a, 0xfb, 0x9e, 0x35, + 0x62, 0x54, 0x4f, 0x49, 0x74, 0x21, 0x88, 0x3b, 0x8c, 0x6e, 0xa7, 0xfe, 0x3c, 0x2f, 0x6a, 0xa5, + 0x5f, 0x35, 0x94, 0x55, 0x4e, 0xea, 0x8c, 0x42, 0xef, 0xcd, 0xa1, 0x68, 0x53, 0x43, 0xf9, 0x22, + 0x1a, 0x0a, 0x71, 0x1c, 0x06, 0x9c, 0x2b, 0x4f, 0x75, 0xfd, 0xb7, 0x5f, 0x36, 0xee, 0x85, 0x3f, + 0x4c, 0x4d, 0x21, 0x2d, 0xc1, 0xa8, 0xd7, 0x1f, 0x4f, 0x20, 0x4c, 0xfe, 0x1f, 0x53, 0x2d, 0xfd, + 0xbc, 0x80, 0xe6, 0x15, 0xed, 0xdf, 0xcd, 0xbf, 0x5d, 0x7b, 0xee, 0xbf, 0xd6, 0xc6, 0xfb, 0x68, + 0xa5, 0x07, 0x60, 0xd9, 0x0c, 0x88, 0x00, 0x8b, 0xf0, 0x23, 0xab, 0xe7, 0x12, 0xa1, 0x27, 0x8d, + 0x64, 0x39, 0xbb, 0xb5, 0x36, 0xde, 0xd5, 0x60, 0xe9, 0xa2, 0x5d, 0xdd, 0xf1, 0xa9, 0x17, 0x8a, + 0xe5, 0x7a, 0x00, 0x3b, 0xf2, 0x6a, 0x8d, 0x1f, 0xed, 0xb9, 0x44, 0x4c, 0xe9, 0x75, 0xa9, 0xa3, + 0xf4, 0x52, 0xef, 0xab, 0x57, 0xa7, 0x8e, 0xd4, 0xfb, 0x1a, 0xe5, 0x03, 0x3d, 0x0e, 0xae, 0x0b, + 0xcc, 0xe2, 0x20, 0x84, 0x0b, 0x03, 0xf0, 0x84, 0x92, 0xbd, 0xf3, 0x6e, 0xb2, 0x1f, 0xf6, 0x00, + 0x5a, 0x52, 0xa1, 0x15, 0x09, 0x48, 0xf5, 0x3e, 0xfa, 0x68, 0xb6, 0x3a, 0x23, 0x82, 0xfa, 0x5c, + 0x9f, 0x97, 0xfa, 0xc6, 0x4d, 0xf3, 0xdd, 0x03, 0x30, 0x03, 0x62, 0x58, 0x66, 0x6d, 0x46, 0x19, + 0x89, 0x73, 0xfc, 0x1c, 0x05, 0xa0, 0xd5, 0x1d, 0xbd, 0x9a, 0xd1, 0xc5, 0xc2, 0xbb, 0x75, 0xb1, + 0xda, 0x03, 0xa8, 0x07, 0x02, 0x53, 0x4d, 0x00, 0x5a, 0x9f, 0xa9, 0x1d, 0xf6, 0x90, 0x7e, 0xaf, + 0x1e, 0xf4, 0xb7, 0x8b, 0x84, 0x2d, 0x3c, 0x42, 0x39, 0x62, 0xdb, 0x30, 0x14, 0xd4, 0xeb, 0x5b, + 0x3e, 0x73, 0x80, 0x71, 0x3d, 0x63, 0x68, 0xe5, 0xb4, 0x79, 0x37, 0xca, 0x3f, 0x95, 0x69, 0xbc, + 0x85, 0xee, 0x13, 0xd7, 0xf5, 0x4f, 0xac, 0x11, 0x7f, 0xc3, 0x92, 0x8e, 0x24, 0x7f, 0x45, 0x82, + 0x1d, 0x1e, 0x2f, 0x82, 0xf7, 0xd1, 0x52, 0x20, 0xc3, 0xb9, 0xd5, 0x67, 0xc4, 0x13, 0x5c, 0xcf, + 0x4a, 0xdf, 0x0f, 0x6e, 0xf2, 0x5d, 0x93, 0xe4, 0x2f, 0x03, 0x6e, 0x68, 0x7d, 0x91, 0x4c, 0x52, + 0x1c, 0x6f, 0xa0, 0x15, 0x06, 0x2f, 0x2c, 0x22, 0x04, 0x8b, 0x6d, 0xb7, 0xbe, 0x68, 0x24, 0xcb, + 0x19, 0x33, 0xc7, 0xe0, 0x45, 0x4d, 0x08, 0x16, 0xed, 0xee, 0x2c, 0x7a, 0x97, 0x3a, 0xfa, 0xd2, + 0x0c, 0x7a, 0x9d, 0x3a, 0xa5, 0x6f, 0x51, 0x7a, 0x3c, 0x38, 0xfc, 0x39, 0xba, 0x33, 0x64, 0xd4, + 0x86, 0xf0, 0x81, 0xbf, 0xf5, 0x77, 0x54, 0x6c, 0xbc, 0x89, 0x92, 0x3d, 0x80, 0xf0, 0x2f, 0x7c, + 0xeb, 0xa5, 0x80, 0xbb, 0x9d, 0x1a, 0x3f, 0xbd, 0xd9, 0x58, 0xf7, 0x78, 0x0b, 0x2d, 0x8c, 0x1f, + 0x33, 0xed, 0x96, 0xc7, 0x6c, 0x4c, 0xc4, 0xbb, 0x28, 0x3b, 0x04, 0x36, 0xa0, 0x9c, 0x53, 0xdf, + 0x0b, 0xde, 0x91, 0x64, 0x79, 0x79, 0xab, 0x74, 0xd3, 0xac, 0x0f, 0x22, 0xaa, 0x19, 0xbf, 0xf6, + 0xc9, 0x4f, 0x73, 0x08, 0x4d, 0x30, 0xfc, 0x29, 0x5a, 0x3d, 0x68, 0x98, 0x4f, 0x9a, 0xad, 0x56, + 0xf3, 0xe9, 0xbe, 0xd5, 0xd9, 0x6f, 0x1d, 0x34, 0x76, 0x9a, 0x7b, 0xcd, 0xc6, 0x6e, 0x2e, 0x91, + 0xbf, 0x7b, 0x7a, 0x66, 0x64, 0x47, 0x1e, 0x1f, 0x82, 0x4d, 0x7b, 0x14, 0x1c, 0xfc, 0x31, 0xfa, + 0x20, 0x46, 0x6e, 0x35, 0xda, 0xed, 0xc7, 0x8d, 0x9c, 0x96, 0x47, 0xa7, 0x67, 0xc6, 0xbc, 0xda, + 0x98, 0x29, 0xca, 0x4e, 0x6d, 0x7f, 0xa7, 0xf1, 0x38, 0x37, 0xa7, 0x28, 0x76, 0x60, 0xd2, 0xc5, + 0x0f, 0xd1, 0x4a, 0x8c, 0xf2, 0xac, 0xd9, 0xfe, 0x6a, 0xd7, 0xac, 0x3d, 0xcb, 0x25, 0xf3, 0x8b, + 0xa7, 0x67, 0x46, 0xfa, 0x84, 0x8a, 0x43, 0x87, 0x91, 0x93, 0x29, 0xa5, 0xce, 0xc1, 0x6e, 0xad, + 0xdd, 0xc8, 0xa5, 0x94, 0xd2, 0x68, 0xe8, 0x10, 0x01, 0x53, 0xe6, 0x27, 0xc7, 0x56, 0xee, 0x8e, + 0x32, 0x1f, 0x6b, 0x1c, 0x3f, 0x42, 0xf7, 0x63, 0xe4, 0x5a, 0xbb, 0x6d, 0x36, 0xeb, 0x9d, 0x76, + 0xa3, 0x95, 0x9b, 0xcf, 0x2f, 0x9f, 0x9e, 0x19, 0x28, 0x58, 0x23, 0xda, 0x1d, 0x09, 0xe0, 0x75, + 0x78, 0x7d, 0x55, 0xd0, 0x2e, 0xae, 0x0a, 0xda, 0x1f, 0x57, 0x05, 0xed, 0x87, 0xeb, 0x42, 0xe2, + 0xe2, 0xba, 0x90, 0xf8, 0xfd, 0xba, 0x90, 0x40, 0x6b, 0xd4, 0xbf, 0x61, 0xe0, 0x07, 0xda, 0xf3, + 0x4a, 0x9f, 0x8a, 0xc3, 0x51, 0xb7, 0x62, 0xfb, 0x83, 0xea, 0x84, 0xb4, 0x41, 0xfd, 0x58, 0x54, + 0x7d, 0x19, 0x7d, 0x17, 0x75, 0xe7, 0xe5, 0x57, 0xc8, 0x67, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, + 0xb9, 0xe4, 0xfe, 0x0f, 0x35, 0x09, 0x00, 0x00, } func (this *MarketDetails) Equal(that interface{}) bool { @@ -693,6 +760,51 @@ func (m *MarketDetails) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *MarketBrief) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MarketBrief) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MarketBrief) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.MarketDetails.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.MarketAddress) > 0 { + i -= len(m.MarketAddress) + copy(dAtA[i:], m.MarketAddress) + i = encodeVarintMarket(dAtA, i, uint64(len(m.MarketAddress))) + i-- + dAtA[i] = 0x12 + } + if m.MarketId != 0 { + i = encodeVarintMarket(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *Market) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -931,20 +1043,20 @@ func (m *AccessGrant) MarshalToSizedBuffer(dAtA []byte) (int, error) { var l int _ = l if len(m.Permissions) > 0 { - dAtA7 := make([]byte, len(m.Permissions)*10) - var j6 int + dAtA8 := make([]byte, len(m.Permissions)*10) + var j7 int for _, num := range m.Permissions { for num >= 1<<7 { - dAtA7[j6] = uint8(uint64(num)&0x7f | 0x80) + dAtA8[j7] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j6++ + j7++ } - dAtA7[j6] = uint8(num) - j6++ + dAtA8[j7] = uint8(num) + j7++ } - i -= j6 - copy(dAtA[i:], dAtA7[:j6]) - i = encodeVarintMarket(dAtA, i, uint64(j6)) + i -= j7 + copy(dAtA[i:], dAtA8[:j7]) + i = encodeVarintMarket(dAtA, i, uint64(j7)) i-- dAtA[i] = 0x12 } @@ -1012,6 +1124,24 @@ func (m *MarketDetails) Size() (n int) { return n } +func (m *MarketBrief) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovMarket(uint64(m.MarketId)) + } + l = len(m.MarketAddress) + if l > 0 { + n += 1 + l + sovMarket(uint64(l)) + } + l = m.MarketDetails.Size() + n += 1 + l + sovMarket(uint64(l)) + return n +} + func (m *Market) Size() (n int) { if m == nil { return 0 @@ -1441,6 +1571,140 @@ func (m *MarketDetails) Unmarshal(dAtA []byte) error { } return nil } +func (m *MarketBrief) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MarketBrief: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MarketBrief: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MarketAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketDetails", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MarketDetails.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMarket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMarket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *Market) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index 4c4fe65b46..a791a0c328 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -740,23 +740,24 @@ func (m *QueryGetAllOrdersResponse) GetPagination() *query.PageResponse { return nil } -// QueryMarketInfoRequest is a request message for the QueryMarketInfo endpoint. -type QueryMarketInfoRequest struct { +// QueryGetMarketRequest is a request message for the QueryGetMarket endpoint. +type QueryGetMarketRequest struct { + // market_id is the id of the market to look up. MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` } -func (m *QueryMarketInfoRequest) Reset() { *m = QueryMarketInfoRequest{} } -func (m *QueryMarketInfoRequest) String() string { return proto.CompactTextString(m) } -func (*QueryMarketInfoRequest) ProtoMessage() {} -func (*QueryMarketInfoRequest) Descriptor() ([]byte, []int) { +func (m *QueryGetMarketRequest) Reset() { *m = QueryGetMarketRequest{} } +func (m *QueryGetMarketRequest) String() string { return proto.CompactTextString(m) } +func (*QueryGetMarketRequest) ProtoMessage() {} +func (*QueryGetMarketRequest) Descriptor() ([]byte, []int) { return fileDescriptor_00949b75b1c10bfe, []int{12} } -func (m *QueryMarketInfoRequest) XXX_Unmarshal(b []byte) error { +func (m *QueryGetMarketRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryMarketInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryGetMarketRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryMarketInfoRequest.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryGetMarketRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -766,41 +767,45 @@ func (m *QueryMarketInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]by return b[:n], nil } } -func (m *QueryMarketInfoRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryMarketInfoRequest.Merge(m, src) +func (m *QueryGetMarketRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetMarketRequest.Merge(m, src) } -func (m *QueryMarketInfoRequest) XXX_Size() int { +func (m *QueryGetMarketRequest) XXX_Size() int { return m.Size() } -func (m *QueryMarketInfoRequest) XXX_DiscardUnknown() { - xxx_messageInfo_QueryMarketInfoRequest.DiscardUnknown(m) +func (m *QueryGetMarketRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetMarketRequest.DiscardUnknown(m) } -var xxx_messageInfo_QueryMarketInfoRequest proto.InternalMessageInfo +var xxx_messageInfo_QueryGetMarketRequest proto.InternalMessageInfo -func (m *QueryMarketInfoRequest) GetMarketId() uint32 { +func (m *QueryGetMarketRequest) GetMarketId() uint32 { if m != nil { return m.MarketId } return 0 } -// QueryMarketInfoResponse is a response message for the QueryMarketInfo endpoint. -type QueryMarketInfoResponse struct { +// QueryGetMarketResponse is a response message for the QueryGetMarket endpoint. +type QueryGetMarketResponse struct { + // address is the bech32 address string of this market's account. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // market is all information and details of the market. + Market *Market `protobuf:"bytes,2,opt,name=market,proto3" json:"market,omitempty"` } -func (m *QueryMarketInfoResponse) Reset() { *m = QueryMarketInfoResponse{} } -func (m *QueryMarketInfoResponse) String() string { return proto.CompactTextString(m) } -func (*QueryMarketInfoResponse) ProtoMessage() {} -func (*QueryMarketInfoResponse) Descriptor() ([]byte, []int) { +func (m *QueryGetMarketResponse) Reset() { *m = QueryGetMarketResponse{} } +func (m *QueryGetMarketResponse) String() string { return proto.CompactTextString(m) } +func (*QueryGetMarketResponse) ProtoMessage() {} +func (*QueryGetMarketResponse) Descriptor() ([]byte, []int) { return fileDescriptor_00949b75b1c10bfe, []int{13} } -func (m *QueryMarketInfoResponse) XXX_Unmarshal(b []byte) error { +func (m *QueryGetMarketResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryMarketInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryGetMarketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryMarketInfoResponse.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryGetMarketResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -810,17 +815,132 @@ func (m *QueryMarketInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]b return b[:n], nil } } -func (m *QueryMarketInfoResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryMarketInfoResponse.Merge(m, src) +func (m *QueryGetMarketResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetMarketResponse.Merge(m, src) } -func (m *QueryMarketInfoResponse) XXX_Size() int { +func (m *QueryGetMarketResponse) XXX_Size() int { return m.Size() } -func (m *QueryMarketInfoResponse) XXX_DiscardUnknown() { - xxx_messageInfo_QueryMarketInfoResponse.DiscardUnknown(m) +func (m *QueryGetMarketResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetMarketResponse.DiscardUnknown(m) } -var xxx_messageInfo_QueryMarketInfoResponse proto.InternalMessageInfo +var xxx_messageInfo_QueryGetMarketResponse proto.InternalMessageInfo + +func (m *QueryGetMarketResponse) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *QueryGetMarketResponse) GetMarket() *Market { + if m != nil { + return m.Market + } + return nil +} + +// QueryGetAllMarketsRequest is a request message for the QueryGetAllMarkets endpoint. +type QueryGetAllMarketsRequest struct { + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryGetAllMarketsRequest) Reset() { *m = QueryGetAllMarketsRequest{} } +func (m *QueryGetAllMarketsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryGetAllMarketsRequest) ProtoMessage() {} +func (*QueryGetAllMarketsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{14} +} +func (m *QueryGetAllMarketsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetAllMarketsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetAllMarketsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetAllMarketsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetAllMarketsRequest.Merge(m, src) +} +func (m *QueryGetAllMarketsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryGetAllMarketsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetAllMarketsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetAllMarketsRequest proto.InternalMessageInfo + +func (m *QueryGetAllMarketsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryGetAllMarketsResponse is a response message for the QueryGetAllMarkets endpoint. +type QueryGetAllMarketsResponse struct { + // markets are a page of the briefs for all markets. + Markets []*MarketBrief `protobuf:"bytes,1,rep,name=markets,proto3" json:"markets,omitempty"` + // pagination is the resulting pagination parameters. + Pagination *query.PageResponse `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryGetAllMarketsResponse) Reset() { *m = QueryGetAllMarketsResponse{} } +func (m *QueryGetAllMarketsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryGetAllMarketsResponse) ProtoMessage() {} +func (*QueryGetAllMarketsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{15} +} +func (m *QueryGetAllMarketsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetAllMarketsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetAllMarketsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetAllMarketsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetAllMarketsResponse.Merge(m, src) +} +func (m *QueryGetAllMarketsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryGetAllMarketsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetAllMarketsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetAllMarketsResponse proto.InternalMessageInfo + +func (m *QueryGetAllMarketsResponse) GetMarkets() []*MarketBrief { + if m != nil { + return m.Markets + } + return nil +} + +func (m *QueryGetAllMarketsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} // QueryParamsRequest is a request message for the QueryParams endpoint. type QueryParamsRequest struct { @@ -830,7 +950,7 @@ func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } func (*QueryParamsRequest) ProtoMessage() {} func (*QueryParamsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{14} + return fileDescriptor_00949b75b1c10bfe, []int{16} } func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -867,7 +987,7 @@ func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } func (*QueryParamsResponse) ProtoMessage() {} func (*QueryParamsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{15} + return fileDescriptor_00949b75b1c10bfe, []int{17} } func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -904,7 +1024,7 @@ func (m *QueryValidateCreateMarketRequest) Reset() { *m = QueryValidateC func (m *QueryValidateCreateMarketRequest) String() string { return proto.CompactTextString(m) } func (*QueryValidateCreateMarketRequest) ProtoMessage() {} func (*QueryValidateCreateMarketRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{16} + return fileDescriptor_00949b75b1c10bfe, []int{18} } func (m *QueryValidateCreateMarketRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -941,7 +1061,7 @@ func (m *QueryValidateCreateMarketResponse) Reset() { *m = QueryValidate func (m *QueryValidateCreateMarketResponse) String() string { return proto.CompactTextString(m) } func (*QueryValidateCreateMarketResponse) ProtoMessage() {} func (*QueryValidateCreateMarketResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{17} + return fileDescriptor_00949b75b1c10bfe, []int{19} } func (m *QueryValidateCreateMarketResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -979,7 +1099,7 @@ func (m *QueryValidateManageFeesRequest) Reset() { *m = QueryValidateMan func (m *QueryValidateManageFeesRequest) String() string { return proto.CompactTextString(m) } func (*QueryValidateManageFeesRequest) ProtoMessage() {} func (*QueryValidateManageFeesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{18} + return fileDescriptor_00949b75b1c10bfe, []int{20} } func (m *QueryValidateManageFeesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1023,7 +1143,7 @@ func (m *QueryValidateManageFeesResponse) Reset() { *m = QueryValidateMa func (m *QueryValidateManageFeesResponse) String() string { return proto.CompactTextString(m) } func (*QueryValidateManageFeesResponse) ProtoMessage() {} func (*QueryValidateManageFeesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{19} + return fileDescriptor_00949b75b1c10bfe, []int{21} } func (m *QueryValidateManageFeesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1065,8 +1185,10 @@ func init() { proto.RegisterType((*QueryGetAssetOrdersResponse)(nil), "provenance.exchange.v1.QueryGetAssetOrdersResponse") proto.RegisterType((*QueryGetAllOrdersRequest)(nil), "provenance.exchange.v1.QueryGetAllOrdersRequest") proto.RegisterType((*QueryGetAllOrdersResponse)(nil), "provenance.exchange.v1.QueryGetAllOrdersResponse") - proto.RegisterType((*QueryMarketInfoRequest)(nil), "provenance.exchange.v1.QueryMarketInfoRequest") - proto.RegisterType((*QueryMarketInfoResponse)(nil), "provenance.exchange.v1.QueryMarketInfoResponse") + proto.RegisterType((*QueryGetMarketRequest)(nil), "provenance.exchange.v1.QueryGetMarketRequest") + proto.RegisterType((*QueryGetMarketResponse)(nil), "provenance.exchange.v1.QueryGetMarketResponse") + proto.RegisterType((*QueryGetAllMarketsRequest)(nil), "provenance.exchange.v1.QueryGetAllMarketsRequest") + proto.RegisterType((*QueryGetAllMarketsResponse)(nil), "provenance.exchange.v1.QueryGetAllMarketsResponse") proto.RegisterType((*QueryParamsRequest)(nil), "provenance.exchange.v1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "provenance.exchange.v1.QueryParamsResponse") proto.RegisterType((*QueryValidateCreateMarketRequest)(nil), "provenance.exchange.v1.QueryValidateCreateMarketRequest") @@ -1080,78 +1202,84 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 1133 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x41, 0x6f, 0x1b, 0x45, - 0x14, 0xce, 0x24, 0x4d, 0x89, 0x27, 0x04, 0xc4, 0xe0, 0x16, 0x7b, 0x93, 0xba, 0xee, 0xb6, 0x2a, - 0x91, 0x49, 0x76, 0x63, 0xbb, 0x2d, 0xf4, 0x50, 0xa1, 0x24, 0x92, 0xa3, 0x48, 0x54, 0x49, 0x17, - 0xc4, 0x81, 0x03, 0x66, 0x6c, 0x3f, 0x6f, 0x57, 0xb1, 0x77, 0xdc, 0xdd, 0x8d, 0x69, 0x14, 0xe5, - 0xc2, 0x0f, 0x40, 0x48, 0x5c, 0x90, 0x10, 0x15, 0x07, 0xc4, 0x99, 0x43, 0x8f, 0x48, 0x1c, 0xc9, - 0xb1, 0xa2, 0x17, 0xb8, 0x20, 0x94, 0xf0, 0x2b, 0x38, 0xa1, 0x9d, 0x99, 0xb5, 0x77, 0x63, 0x7b, - 0xbd, 0x46, 0x1c, 0x7c, 0xf2, 0xee, 0xf3, 0xfb, 0xde, 0xfb, 0xde, 0x37, 0x33, 0xef, 0x8d, 0x8d, - 0xd5, 0x8e, 0xc3, 0xba, 0x60, 0x53, 0xbb, 0x0e, 0x3a, 0x3c, 0xad, 0x3f, 0xa6, 0xb6, 0x09, 0x7a, - 0xb7, 0xa8, 0x3f, 0x39, 0x04, 0xe7, 0x48, 0xeb, 0x38, 0xcc, 0x63, 0xe4, 0x6a, 0xdf, 0x47, 0x0b, - 0x7c, 0xb4, 0x6e, 0x51, 0xc9, 0xd6, 0x99, 0xdb, 0x66, 0x6e, 0x95, 0x7b, 0xe9, 0xe2, 0x45, 0x40, - 0x94, 0x82, 0x78, 0xd3, 0x6b, 0xd4, 0x05, 0x11, 0x4b, 0xef, 0x16, 0x6b, 0xe0, 0xd1, 0xa2, 0xde, - 0xa1, 0xa6, 0x65, 0x53, 0xcf, 0x62, 0xb6, 0xf4, 0xcd, 0x85, 0x7d, 0x03, 0xaf, 0x3a, 0xb3, 0x82, - 0xef, 0x57, 0x4c, 0xc6, 0xcc, 0x16, 0xe8, 0xb4, 0x63, 0xe9, 0xd4, 0xb6, 0x99, 0xc7, 0xc1, 0x41, - 0xa6, 0xb4, 0xc9, 0x4c, 0x26, 0x18, 0xf8, 0x4f, 0xd2, 0x7a, 0x73, 0x44, 0x59, 0xcc, 0x69, 0x80, - 0x23, 0xa1, 0xea, 0x37, 0x08, 0x67, 0x1e, 0xf9, 0xdc, 0xf6, 0x7c, 0x6b, 0x05, 0x60, 0x9b, 0xb6, - 0xea, 0x06, 0x3c, 0x39, 0x04, 0xd7, 0x23, 0x0f, 0x70, 0x8a, 0xba, 0x07, 0x55, 0x0e, 0xc8, 0xcc, - 0xe6, 0xd1, 0xea, 0x62, 0x29, 0xaf, 0x0d, 0x17, 0x42, 0xdb, 0x74, 0x0f, 0x78, 0x08, 0x63, 0x81, - 0xca, 0x27, 0x1f, 0x5e, 0xb3, 0x1a, 0x12, 0x3e, 0x17, 0x0f, 0xdf, 0xb2, 0x1a, 0x12, 0x5e, 0x93, - 0x4f, 0xea, 0x4f, 0xb3, 0x38, 0x3b, 0x84, 0x9a, 0xdb, 0x61, 0xb6, 0x0b, 0xe4, 0x11, 0x4e, 0xd7, - 0x1d, 0xe0, 0x32, 0x54, 0x9b, 0x00, 0x55, 0xd6, 0xe1, 0x8a, 0x64, 0x50, 0x7e, 0x6e, 0x75, 0xb1, - 0x94, 0xd5, 0xe4, 0x52, 0xf8, 0x82, 0x6a, 0x52, 0x50, 0x6d, 0x9b, 0x59, 0xf6, 0xd6, 0xa5, 0xd3, - 0x3f, 0xaf, 0xcf, 0x18, 0x24, 0x00, 0x57, 0x00, 0xf6, 0x04, 0x94, 0x7c, 0x8a, 0x97, 0x5d, 0xf0, - 0xbc, 0x16, 0xb4, 0xc1, 0xf6, 0xaa, 0xcd, 0x16, 0xf5, 0x22, 0x91, 0x67, 0x93, 0x45, 0xce, 0xf4, - 0x63, 0x54, 0x5a, 0xd4, 0x0b, 0xc5, 0xff, 0x0c, 0xaf, 0x84, 0xe2, 0x3b, 0x7e, 0xfa, 0x48, 0x82, - 0xb9, 0x64, 0x09, 0xb2, 0xfd, 0x20, 0x86, 0x1f, 0xa3, 0x9f, 0x41, 0x2d, 0xe2, 0x34, 0x57, 0x6c, - 0x07, 0x3c, 0xa1, 0xa6, 0x5c, 0xc8, 0x2c, 0x5e, 0xe0, 0xab, 0x50, 0xb5, 0x1a, 0x19, 0x94, 0x47, - 0xab, 0x97, 0x8c, 0x57, 0xf8, 0xfb, 0x6e, 0x43, 0xfd, 0x00, 0x5f, 0xb9, 0x00, 0x91, 0x02, 0x97, - 0xf1, 0xbc, 0x58, 0x39, 0xc4, 0x57, 0xee, 0xda, 0xa8, 0x95, 0x13, 0x28, 0xe1, 0xab, 0xfe, 0x8a, - 0xf0, 0x72, 0x10, 0xee, 0x21, 0x75, 0x0e, 0x64, 0x50, 0x37, 0x20, 0xb2, 0x8c, 0x53, 0x6d, 0x6e, - 0x0e, 0x98, 0x2c, 0x19, 0x0b, 0xc2, 0xb0, 0xdb, 0x20, 0xd7, 0x30, 0x16, 0x2c, 0xbd, 0xa3, 0x0e, - 0xf0, 0xfd, 0x96, 0x32, 0x52, 0xdc, 0xf2, 0xd1, 0x51, 0x07, 0xc8, 0x2d, 0xfc, 0x1a, 0x6d, 0x7a, - 0xe0, 0x54, 0x7b, 0xa5, 0xcc, 0xf1, 0x52, 0x5e, 0xe5, 0xd6, 0x3d, 0x51, 0x0f, 0xa9, 0x60, 0xdc, - 0x3f, 0x5d, 0x99, 0x3a, 0xe7, 0x7e, 0x3b, 0x22, 0xa9, 0x38, 0xd6, 0x81, 0xb0, 0xfb, 0xd4, 0x04, - 0xc9, 0xce, 0x08, 0x21, 0xd5, 0x67, 0x08, 0xaf, 0x0c, 0xaf, 0x44, 0xea, 0x73, 0x17, 0x5f, 0x16, - 0x27, 0x49, 0x6e, 0xb9, 0x31, 0x02, 0x49, 0x67, 0xb2, 0x33, 0x84, 0xdf, 0xdb, 0x63, 0xf9, 0x89, - 0x9c, 0x11, 0x82, 0x7f, 0x20, 0xac, 0xf4, 0x56, 0xee, 0x73, 0x5b, 0x2a, 0xd0, 0x53, 0x5a, 0xc3, - 0xf3, 0xcc, 0xb7, 0x72, 0x95, 0x53, 0x5b, 0x99, 0xdf, 0x9e, 0xaf, 0xa7, 0x65, 0x96, 0xcd, 0x46, - 0xc3, 0x01, 0xd7, 0xfd, 0xd0, 0x73, 0x2c, 0xdb, 0x34, 0x84, 0xdb, 0x74, 0x89, 0xff, 0x5d, 0x68, - 0x1b, 0x45, 0x6a, 0x9b, 0x12, 0xed, 0x7f, 0x09, 0x69, 0xbf, 0xe9, 0xba, 0x17, 0x77, 0x79, 0x1a, - 0xcf, 0x53, 0xdf, 0x2a, 0xb4, 0x37, 0xc4, 0xcb, 0xf4, 0x2a, 0x1c, 0xa9, 0x60, 0x4a, 0x14, 0xae, - 0xc9, 0xb1, 0xe4, 0xd3, 0x6b, 0xb5, 0xa2, 0xf2, 0xfe, 0x5f, 0x1a, 0x7c, 0x8b, 0xe4, 0x80, 0x89, - 0x26, 0x99, 0x12, 0x05, 0xee, 0xe2, 0xab, 0x9c, 0x9c, 0x68, 0x3e, 0xbb, 0x76, 0x93, 0x25, 0x69, - 0xa2, 0x6a, 0x16, 0xbf, 0x35, 0x00, 0x13, 0xd1, 0xd5, 0x34, 0x26, 0xfc, 0xab, 0x7d, 0xea, 0xd0, - 0x76, 0xa0, 0xa6, 0x7a, 0x05, 0xbf, 0x19, 0xb1, 0x4a, 0x67, 0x15, 0xe7, 0xb9, 0xf9, 0x63, 0xda, - 0xb2, 0x1a, 0xd4, 0x83, 0x6d, 0x7f, 0x5e, 0x82, 0x88, 0x1a, 0x40, 0x6f, 0xe2, 0x1b, 0x31, 0x3e, - 0x32, 0xd0, 0x03, 0x9c, 0x8b, 0x38, 0x3d, 0xa4, 0x36, 0x35, 0xa1, 0x02, 0x90, 0x68, 0x28, 0xa8, - 0x37, 0xf0, 0xf5, 0x91, 0x70, 0x91, 0xa1, 0xf4, 0xcf, 0x12, 0x9e, 0xe7, 0x3e, 0xe4, 0x07, 0x84, - 0xdf, 0x18, 0xb8, 0x32, 0x90, 0x8d, 0x51, 0x2b, 0x37, 0xea, 0xe2, 0xa3, 0x14, 0x27, 0x40, 0xc8, - 0x32, 0x0b, 0x5f, 0xbc, 0xfc, 0xfb, 0xeb, 0xd9, 0x5b, 0x44, 0xd5, 0x47, 0x5c, 0xbb, 0x9a, 0x00, - 0xae, 0xb8, 0x7b, 0x91, 0x67, 0x08, 0x2f, 0x45, 0x86, 0x2e, 0x59, 0x8b, 0x4d, 0x78, 0x61, 0x9c, - 0x2b, 0xeb, 0x09, 0xbd, 0x25, 0xb5, 0x0d, 0x4e, 0xad, 0x40, 0x56, 0xf5, 0xb8, 0x1b, 0xa1, 0x7e, - 0x1c, 0x34, 0x9e, 0x13, 0xf2, 0x33, 0xea, 0x5f, 0x24, 0xc2, 0xc3, 0x8f, 0x94, 0xc7, 0x65, 0x1e, - 0x32, 0xf4, 0x95, 0x3b, 0x93, 0x81, 0x24, 0xeb, 0xfb, 0x9c, 0x75, 0x99, 0x14, 0x47, 0xb1, 0x16, - 0x5b, 0x44, 0x3f, 0xee, 0xed, 0x9d, 0x13, 0x79, 0xb5, 0x25, 0xcf, 0x91, 0xdc, 0xd3, 0xd1, 0xf1, - 0x41, 0x4a, 0x63, 0x75, 0x1b, 0x98, 0xa3, 0x4a, 0x79, 0x22, 0x8c, 0xe4, 0x7e, 0x87, 0x73, 0xd7, - 0xc8, 0x5a, 0xac, 0xe2, 0xae, 0xce, 0x47, 0xaf, 0x7e, 0xcc, 0x3f, 0x4e, 0x22, 0xb4, 0x43, 0x3d, - 0x79, 0x3c, 0xed, 0xc1, 0x11, 0x34, 0x9e, 0xf6, 0x90, 0xa6, 0x9f, 0x98, 0x36, 0x9f, 0x67, 0xfa, - 0x31, 0xff, 0x38, 0x21, 0xdf, 0x07, 0x87, 0x2e, 0xdc, 0x46, 0xc7, 0x1c, 0xba, 0x21, 0x6d, 0x7d, - 0xcc, 0xa1, 0x1b, 0xd6, 0xa3, 0xd5, 0xdb, 0x9c, 0x70, 0x9e, 0xe4, 0xe2, 0x09, 0x93, 0x1f, 0x11, - 0x7e, 0xfd, 0x42, 0x57, 0x24, 0x5a, 0x6c, 0xba, 0x81, 0xae, 0xab, 0xe8, 0x89, 0xfd, 0x25, 0xb9, - 0x12, 0x27, 0xb7, 0x46, 0x0a, 0xc9, 0x37, 0x30, 0xf9, 0x12, 0xe1, 0xc5, 0x50, 0x37, 0x26, 0x85, - 0xd8, 0xa4, 0x91, 0x46, 0xae, 0xbc, 0x93, 0xc8, 0x37, 0xa9, 0x72, 0x1d, 0x41, 0xe0, 0x34, 0x98, - 0x91, 0xc3, 0x7a, 0x3c, 0x79, 0x2f, 0x36, 0x65, 0xcc, 0xe8, 0x50, 0xee, 0xff, 0x07, 0xa4, 0xa4, - 0x7e, 0x8f, 0x53, 0xdf, 0x20, 0xda, 0x28, 0xea, 0x5d, 0x89, 0xd6, 0xf9, 0x6f, 0x3c, 0xa8, 0x0a, - 0x7d, 0xc9, 0x4b, 0x24, 0x47, 0xe3, 0xe0, 0x28, 0x21, 0xf7, 0x12, 0xd1, 0x19, 0x18, 0x5d, 0xca, - 0xbb, 0x13, 0xe3, 0x64, 0x11, 0x3b, 0xbc, 0x88, 0x4d, 0xf2, 0xfe, 0x04, 0xdd, 0xad, 0x57, 0x57, - 0x9b, 0xc7, 0xf3, 0x7f, 0x3b, 0xba, 0x5b, 0x70, 0x7a, 0x96, 0x43, 0x2f, 0xce, 0x72, 0xe8, 0xaf, - 0xb3, 0x1c, 0xfa, 0xea, 0x3c, 0x37, 0xf3, 0xe2, 0x3c, 0x37, 0xf3, 0xfb, 0x79, 0x6e, 0x06, 0x67, - 0x2d, 0x36, 0x82, 0xdd, 0x3e, 0xfa, 0x44, 0x33, 0x2d, 0xef, 0xf1, 0x61, 0x4d, 0xab, 0xb3, 0x76, - 0x88, 0xc1, 0xba, 0xc5, 0xc2, 0x7c, 0x9e, 0xf6, 0x18, 0xd5, 0x2e, 0xf3, 0xbf, 0x0b, 0xca, 0xff, - 0x06, 0x00, 0x00, 0xff, 0xff, 0x5b, 0xb6, 0xb3, 0xff, 0x2c, 0x11, 0x00, 0x00, + // 1226 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4f, 0x4f, 0x1b, 0x47, + 0x14, 0x67, 0x20, 0x04, 0x78, 0x34, 0xa9, 0x3a, 0x75, 0x52, 0x7b, 0x21, 0xc6, 0x2c, 0x51, 0x82, + 0x28, 0xec, 0x62, 0x43, 0x69, 0x7b, 0x40, 0x15, 0x20, 0x81, 0x22, 0x35, 0x82, 0x6c, 0xab, 0x1e, + 0x72, 0xa8, 0x3b, 0xb6, 0x07, 0xb3, 0xc2, 0xde, 0x71, 0x76, 0x17, 0x1a, 0x84, 0xb8, 0x54, 0x3d, + 0x57, 0x95, 0x7a, 0xa9, 0x54, 0x35, 0xaa, 0xd4, 0x7c, 0x80, 0x1e, 0x72, 0xef, 0xb1, 0x1c, 0xa3, + 0x72, 0x69, 0x2f, 0x55, 0x05, 0xfd, 0x02, 0xfd, 0x06, 0xd5, 0xce, 0xcc, 0xda, 0xbb, 0xd8, 0xfb, + 0xc7, 0x15, 0x07, 0x4e, 0xec, 0xce, 0xbe, 0xdf, 0x7b, 0xbf, 0xf7, 0x9b, 0x37, 0xf3, 0x1e, 0x06, + 0xb5, 0x65, 0xb3, 0x43, 0x6a, 0x11, 0xab, 0x4a, 0x75, 0xfa, 0xbc, 0xba, 0x47, 0xac, 0x3a, 0xd5, + 0x0f, 0x8b, 0xfa, 0xb3, 0x03, 0x6a, 0x1f, 0x69, 0x2d, 0x9b, 0xb9, 0x0c, 0xdf, 0xed, 0xd8, 0x68, + 0xbe, 0x8d, 0x76, 0x58, 0x54, 0x72, 0x55, 0xe6, 0x34, 0x99, 0x53, 0xe6, 0x56, 0xba, 0x78, 0x11, + 0x10, 0x65, 0x4e, 0xbc, 0xe9, 0x15, 0xe2, 0x50, 0xe1, 0x4b, 0x3f, 0x2c, 0x56, 0xa8, 0x4b, 0x8a, + 0x7a, 0x8b, 0xd4, 0x4d, 0x8b, 0xb8, 0x26, 0xb3, 0xa4, 0x6d, 0x3e, 0x68, 0xeb, 0x5b, 0x55, 0x99, + 0xe9, 0x7f, 0x9f, 0xac, 0x33, 0x56, 0x6f, 0x50, 0x9d, 0xb4, 0x4c, 0x9d, 0x58, 0x16, 0x73, 0x39, + 0xd8, 0x8f, 0x94, 0xa9, 0xb3, 0x3a, 0x13, 0x0c, 0xbc, 0x27, 0xb9, 0x3a, 0x13, 0x91, 0x16, 0xb3, + 0x6b, 0xd4, 0x76, 0x12, 0x8c, 0x9a, 0xc4, 0xde, 0xa7, 0xae, 0x30, 0x52, 0xbf, 0x47, 0x90, 0x7d, + 0xe2, 0x25, 0xb0, 0xed, 0x41, 0x37, 0x29, 0xdd, 0x20, 0x8d, 0xaa, 0x41, 0x9f, 0x1d, 0x50, 0xc7, + 0xc5, 0xab, 0x30, 0x46, 0x9c, 0xfd, 0x32, 0xf7, 0x9a, 0x1d, 0x2c, 0xa0, 0xd9, 0xf1, 0x52, 0x41, + 0xeb, 0xad, 0x96, 0xb6, 0xe6, 0xec, 0x73, 0x17, 0xc6, 0x28, 0x91, 0x4f, 0x1e, 0xbc, 0x62, 0xd6, + 0x24, 0x7c, 0x28, 0x1e, 0xbe, 0x6e, 0xd6, 0x24, 0xbc, 0x22, 0x9f, 0xd4, 0x5f, 0x06, 0x21, 0xd7, + 0x83, 0x9a, 0xd3, 0x62, 0x96, 0x43, 0xf1, 0x13, 0xc8, 0x54, 0x6d, 0xca, 0xb5, 0x2a, 0xef, 0x52, + 0x5a, 0x66, 0x2d, 0x2e, 0x5b, 0x16, 0x15, 0x86, 0x66, 0xc7, 0x4b, 0x39, 0x4d, 0xee, 0x97, 0xa7, + 0xba, 0x26, 0x55, 0xd7, 0x36, 0x98, 0x69, 0xad, 0xdf, 0x38, 0xfd, 0x6b, 0x6a, 0xc0, 0xc0, 0x3e, + 0x78, 0x93, 0xd2, 0x6d, 0x01, 0xc5, 0x9f, 0xc3, 0x84, 0x43, 0x5d, 0xb7, 0x41, 0x9b, 0xd4, 0x72, + 0xcb, 0xbb, 0x0d, 0xe2, 0x86, 0x3c, 0x0f, 0xa6, 0xf3, 0x9c, 0xed, 0xf8, 0xd8, 0x6c, 0x10, 0x37, + 0xe0, 0xff, 0x0b, 0x98, 0x0c, 0xf8, 0xb7, 0xbd, 0xf0, 0xa1, 0x00, 0x43, 0xe9, 0x02, 0xe4, 0x3a, + 0x4e, 0x0c, 0xcf, 0x47, 0x27, 0x82, 0x5a, 0x84, 0x0c, 0x57, 0x6c, 0x8b, 0xba, 0x42, 0x4d, 0xb9, + 0x91, 0x39, 0x18, 0xe5, 0xbb, 0x50, 0x36, 0x6b, 0x59, 0x54, 0x40, 0xb3, 0x37, 0x8c, 0x11, 0xfe, + 0xfe, 0xa8, 0xa6, 0x7e, 0x0c, 0x77, 0x2e, 0x41, 0xa4, 0xc0, 0x4b, 0x30, 0x2c, 0x76, 0x0e, 0xf1, + 0x9d, 0xbb, 0x17, 0xb5, 0x73, 0x02, 0x25, 0x6c, 0xd5, 0xdf, 0x10, 0x4c, 0xf8, 0xee, 0x1e, 0xf3, + 0x3a, 0xe3, 0x9f, 0x1d, 0x9f, 0xc8, 0x04, 0x8c, 0x89, 0xf2, 0xf3, 0x99, 0xdc, 0x32, 0x46, 0xc5, + 0xc2, 0xa3, 0x1a, 0xbe, 0x07, 0x20, 0x58, 0xba, 0x47, 0x2d, 0xca, 0xeb, 0x6d, 0xcc, 0x18, 0xe3, + 0x2b, 0x9f, 0x1e, 0xb5, 0x28, 0xbe, 0x0f, 0xb7, 0xc9, 0xae, 0x4b, 0xed, 0x72, 0x3b, 0x95, 0x21, + 0x9e, 0xca, 0x1b, 0x7c, 0x75, 0x5b, 0xe4, 0x83, 0x37, 0x01, 0x3a, 0x47, 0x30, 0x5b, 0xe5, 0xdc, + 0x1f, 0x84, 0x24, 0x15, 0x67, 0xdf, 0x17, 0x76, 0x87, 0xd4, 0xa9, 0x64, 0x67, 0x04, 0x90, 0xea, + 0x0b, 0x04, 0x93, 0xbd, 0x33, 0x91, 0xfa, 0xbc, 0x07, 0x37, 0xc5, 0x71, 0x93, 0x25, 0x97, 0x20, + 0x90, 0x34, 0xc6, 0x5b, 0x3d, 0xf8, 0x3d, 0x4c, 0xe4, 0x27, 0x62, 0x86, 0x08, 0xfe, 0x89, 0x40, + 0x69, 0xef, 0xdc, 0x97, 0x96, 0x54, 0xa0, 0xad, 0xb4, 0x06, 0xc3, 0xcc, 0x5b, 0xe5, 0x2a, 0x8f, + 0xad, 0x67, 0x7f, 0x7f, 0xb5, 0x90, 0x91, 0x51, 0xd6, 0x6a, 0x35, 0x9b, 0x3a, 0xce, 0x27, 0xae, + 0x6d, 0x5a, 0x75, 0x43, 0x98, 0x5d, 0x2f, 0xf1, 0x7f, 0x0c, 0x94, 0x51, 0x28, 0xb7, 0x6b, 0xa2, + 0xfd, 0xaf, 0x01, 0xed, 0xd7, 0x1c, 0xe7, 0x72, 0x95, 0x67, 0x60, 0x98, 0x78, 0xab, 0x42, 0x7b, + 0x43, 0xbc, 0x5c, 0x5f, 0x85, 0x43, 0x19, 0x5c, 0x13, 0x85, 0x2b, 0xb2, 0x2d, 0x79, 0xf4, 0x1a, + 0x8d, 0xb0, 0xbc, 0x57, 0xa5, 0xc1, 0x0f, 0x48, 0x36, 0x98, 0x70, 0x90, 0x6b, 0xa2, 0xc0, 0x72, + 0xe7, 0x62, 0x16, 0xf7, 0x4f, 0x9a, 0x3b, 0x54, 0xfd, 0x1a, 0xc1, 0xdd, 0xcb, 0x30, 0x99, 0x50, + 0x09, 0x46, 0x88, 0x38, 0xf9, 0x89, 0x77, 0x82, 0x6f, 0x88, 0x57, 0xe0, 0xa6, 0x70, 0x2d, 0xdb, + 0x7f, 0x3e, 0x4a, 0x04, 0x19, 0x4b, 0x5a, 0xab, 0xd5, 0x90, 0xb2, 0xe2, 0xe3, 0x95, 0xef, 0xdf, + 0xcb, 0xe0, 0x29, 0x0c, 0x44, 0x91, 0xf9, 0xae, 0xc2, 0x88, 0x60, 0xe3, 0xef, 0xe0, 0x4c, 0x3c, + 0xf9, 0x75, 0xdb, 0xa4, 0xbb, 0x86, 0x8f, 0xb9, 0xba, 0x8d, 0xcc, 0x00, 0xe6, 0x2c, 0x77, 0x88, + 0x4d, 0x9a, 0xbe, 0x08, 0xea, 0x1d, 0x78, 0x3b, 0xb4, 0x2a, 0x80, 0xaa, 0x0a, 0x05, 0xbe, 0xfc, + 0x19, 0x69, 0x98, 0x35, 0xe2, 0xd2, 0x0d, 0x6f, 0x4c, 0xa1, 0xa1, 0x02, 0x50, 0x67, 0x60, 0x3a, + 0xc6, 0x46, 0x3a, 0x5a, 0x85, 0x7c, 0xc8, 0xe8, 0x31, 0xb1, 0x48, 0x9d, 0x6e, 0x52, 0x9a, 0xaa, + 0x17, 0xab, 0xd3, 0x30, 0x15, 0x09, 0x17, 0x11, 0x4a, 0x67, 0x6f, 0xc2, 0x30, 0xb7, 0xc1, 0x2f, + 0x11, 0xbc, 0xd5, 0x35, 0xa9, 0xe1, 0xc5, 0x28, 0xb9, 0xa3, 0xe6, 0x4d, 0xa5, 0xd8, 0x07, 0x42, + 0xa6, 0x39, 0xf7, 0xd5, 0xd9, 0x3f, 0xdf, 0x0d, 0xde, 0xc7, 0xaa, 0x1e, 0x31, 0xed, 0xee, 0x52, + 0xea, 0x88, 0xb9, 0x18, 0xbf, 0x40, 0x70, 0x2b, 0x34, 0xeb, 0xe0, 0xf9, 0xd8, 0x80, 0x97, 0xa6, + 0x28, 0x65, 0x21, 0xa5, 0xb5, 0xa4, 0xb6, 0xc8, 0xa9, 0xcd, 0xe1, 0x59, 0x3d, 0x6e, 0x5a, 0xd7, + 0x8f, 0xfd, 0xfb, 0xfe, 0x04, 0xff, 0x8b, 0x3a, 0xf3, 0x5b, 0x70, 0xe6, 0xc0, 0x4b, 0x49, 0x91, + 0x7b, 0xcc, 0x5a, 0xca, 0x72, 0x7f, 0x20, 0xc9, 0xda, 0xe2, 0xac, 0xf7, 0x70, 0x31, 0x96, 0xb5, + 0x23, 0xff, 0x8b, 0xd0, 0x8f, 0xdb, 0x25, 0x74, 0xf2, 0x74, 0x29, 0x1a, 0xd4, 0x6d, 0x2d, 0xfd, + 0xe0, 0x57, 0x48, 0x1e, 0x84, 0x70, 0xab, 0xc7, 0xa5, 0x44, 0xb1, 0xbb, 0x66, 0x1e, 0x65, 0xa9, + 0x2f, 0x8c, 0x4c, 0x78, 0x99, 0x27, 0xac, 0xe1, 0xf9, 0x84, 0x84, 0xf9, 0x98, 0xa4, 0x1f, 0xf3, + 0x3f, 0x27, 0x21, 0xda, 0x81, 0xfe, 0x99, 0x4c, 0xbb, 0x7b, 0x5c, 0x48, 0xa6, 0xdd, 0xa3, 0x41, + 0xa7, 0xa6, 0xcd, 0x67, 0x0f, 0xfd, 0x98, 0xff, 0x39, 0xc1, 0x3f, 0xf9, 0x27, 0x35, 0xd8, 0xf2, + 0x12, 0x4e, 0x6a, 0x8f, 0x16, 0x9c, 0x70, 0x52, 0x7b, 0xf5, 0x53, 0xf5, 0x01, 0x27, 0x5c, 0xc0, + 0xf9, 0x78, 0xc2, 0xf8, 0x67, 0x04, 0xb7, 0xc3, 0x15, 0x8a, 0x17, 0xd2, 0x55, 0xb2, 0x4f, 0x4e, + 0x4b, 0x6b, 0x2e, 0x99, 0x95, 0x38, 0xb3, 0x79, 0x3c, 0x97, 0xbe, 0x7a, 0xbd, 0x2b, 0x0f, 0x77, + 0xf7, 0x1e, 0x9c, 0x46, 0x97, 0x70, 0x37, 0x54, 0x4a, 0xfd, 0x40, 0x24, 0xe3, 0x87, 0x9c, 0xf1, + 0x34, 0x9e, 0x8a, 0x67, 0xec, 0xe0, 0x6f, 0x10, 0x8c, 0x07, 0xda, 0x0c, 0x9e, 0x8b, 0x0d, 0x16, + 0xea, 0x50, 0xca, 0xbb, 0xa9, 0x6c, 0xd3, 0xee, 0x6e, 0x4b, 0x10, 0x38, 0xf5, 0x67, 0xae, 0x5e, + 0xcd, 0x0b, 0x7f, 0x10, 0x1b, 0x32, 0xa6, 0x27, 0x2a, 0x1f, 0xfe, 0x0f, 0xa4, 0xa4, 0xbe, 0xc2, + 0xa9, 0x2f, 0x62, 0x2d, 0x8a, 0xfa, 0xa1, 0x44, 0xeb, 0xfc, 0x37, 0x03, 0x5a, 0x16, 0xe2, 0xe2, + 0x33, 0x04, 0xef, 0x44, 0xf4, 0x48, 0xbc, 0x92, 0x8a, 0x4e, 0x57, 0x4f, 0x56, 0xde, 0xef, 0x1b, + 0x27, 0x93, 0xd8, 0xe2, 0x49, 0xac, 0xe1, 0x8f, 0xfa, 0xb8, 0x81, 0xdb, 0x79, 0x35, 0xb9, 0xbf, + 0xb2, 0xd7, 0x2a, 0xd7, 0xe9, 0xe9, 0x79, 0x1e, 0xbd, 0x3e, 0xcf, 0xa3, 0xbf, 0xcf, 0xf3, 0xe8, + 0xdb, 0x8b, 0xfc, 0xc0, 0xeb, 0x8b, 0xfc, 0xc0, 0x1f, 0x17, 0xf9, 0x01, 0xc8, 0x99, 0x2c, 0x82, + 0xdd, 0x0e, 0x7a, 0xaa, 0xd5, 0x4d, 0x77, 0xef, 0xa0, 0xa2, 0x55, 0x59, 0x33, 0xc0, 0x60, 0xc1, + 0x64, 0x41, 0x3e, 0xcf, 0xdb, 0x8c, 0x2a, 0x37, 0xf9, 0xcf, 0x4f, 0x4b, 0xff, 0x05, 0x00, 0x00, + 0xff, 0xff, 0x6d, 0x67, 0x14, 0x44, 0xa1, 0x13, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1178,8 +1306,10 @@ type QueryClient interface { QueryGetAssetOrders(ctx context.Context, in *QueryGetAssetOrdersRequest, opts ...grpc.CallOption) (*QueryGetAssetOrdersResponse, error) // QueryGetAllOrders gets all orders in the exchange module. QueryGetAllOrders(ctx context.Context, in *QueryGetAllOrdersRequest, opts ...grpc.CallOption) (*QueryGetAllOrdersResponse, error) - // QueryMarketInfo returns the information/details about a market. - QueryMarketInfo(ctx context.Context, in *QueryMarketInfoRequest, opts ...grpc.CallOption) (*QueryMarketInfoResponse, error) + // QueryGetMarket returns all the information and details about a market. + QueryGetMarket(ctx context.Context, in *QueryGetMarketRequest, opts ...grpc.CallOption) (*QueryGetMarketResponse, error) + // QueryGetAllMarkets returns brief information about each market. + QueryGetAllMarkets(ctx context.Context, in *QueryGetAllMarketsRequest, opts ...grpc.CallOption) (*QueryGetAllMarketsResponse, error) // QueryParams returns the exchange module parameters. QueryParams(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. @@ -1250,9 +1380,18 @@ func (c *queryClient) QueryGetAllOrders(ctx context.Context, in *QueryGetAllOrde return out, nil } -func (c *queryClient) QueryMarketInfo(ctx context.Context, in *QueryMarketInfoRequest, opts ...grpc.CallOption) (*QueryMarketInfoResponse, error) { - out := new(QueryMarketInfoResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryMarketInfo", in, out, opts...) +func (c *queryClient) QueryGetMarket(ctx context.Context, in *QueryGetMarketRequest, opts ...grpc.CallOption) (*QueryGetMarketResponse, error) { + out := new(QueryGetMarketResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetMarket", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) QueryGetAllMarkets(ctx context.Context, in *QueryGetAllMarketsRequest, opts ...grpc.CallOption) (*QueryGetAllMarketsResponse, error) { + out := new(QueryGetAllMarketsResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetAllMarkets", in, out, opts...) if err != nil { return nil, err } @@ -1300,8 +1439,10 @@ type QueryServer interface { QueryGetAssetOrders(context.Context, *QueryGetAssetOrdersRequest) (*QueryGetAssetOrdersResponse, error) // QueryGetAllOrders gets all orders in the exchange module. QueryGetAllOrders(context.Context, *QueryGetAllOrdersRequest) (*QueryGetAllOrdersResponse, error) - // QueryMarketInfo returns the information/details about a market. - QueryMarketInfo(context.Context, *QueryMarketInfoRequest) (*QueryMarketInfoResponse, error) + // QueryGetMarket returns all the information and details about a market. + QueryGetMarket(context.Context, *QueryGetMarketRequest) (*QueryGetMarketResponse, error) + // QueryGetAllMarkets returns brief information about each market. + QueryGetAllMarkets(context.Context, *QueryGetAllMarketsRequest) (*QueryGetAllMarketsResponse, error) // QueryParams returns the exchange module parameters. QueryParams(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. @@ -1332,8 +1473,11 @@ func (*UnimplementedQueryServer) QueryGetAssetOrders(ctx context.Context, req *Q func (*UnimplementedQueryServer) QueryGetAllOrders(ctx context.Context, req *QueryGetAllOrdersRequest) (*QueryGetAllOrdersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryGetAllOrders not implemented") } -func (*UnimplementedQueryServer) QueryMarketInfo(ctx context.Context, req *QueryMarketInfoRequest) (*QueryMarketInfoResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryMarketInfo not implemented") +func (*UnimplementedQueryServer) QueryGetMarket(ctx context.Context, req *QueryGetMarketRequest) (*QueryGetMarketResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryGetMarket not implemented") +} +func (*UnimplementedQueryServer) QueryGetAllMarkets(ctx context.Context, req *QueryGetAllMarketsRequest) (*QueryGetAllMarketsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryGetAllMarkets not implemented") } func (*UnimplementedQueryServer) QueryParams(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryParams not implemented") @@ -1457,20 +1601,38 @@ func _Query_QueryGetAllOrders_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } -func _Query_QueryMarketInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QueryMarketInfoRequest) +func _Query_QueryGetMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryGetMarketRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryMarketInfo(ctx, in) + return srv.(QueryServer).QueryGetMarket(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryMarketInfo", + FullMethod: "/provenance.exchange.v1.Query/QueryGetMarket", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryMarketInfo(ctx, req.(*QueryMarketInfoRequest)) + return srv.(QueryServer).QueryGetMarket(ctx, req.(*QueryGetMarketRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_QueryGetAllMarkets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryGetAllMarketsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryGetAllMarkets(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryGetAllMarkets", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryGetAllMarkets(ctx, req.(*QueryGetAllMarketsRequest)) } return interceptor(ctx, in, info, handler) } @@ -1558,8 +1720,12 @@ var _Query_serviceDesc = grpc.ServiceDesc{ Handler: _Query_QueryGetAllOrders_Handler, }, { - MethodName: "QueryMarketInfo", - Handler: _Query_QueryMarketInfo_Handler, + MethodName: "QueryGetMarket", + Handler: _Query_QueryGetMarket_Handler, + }, + { + MethodName: "QueryGetAllMarkets", + Handler: _Query_QueryGetAllMarkets_Handler, }, { MethodName: "QueryParams", @@ -2160,7 +2326,7 @@ func (m *QueryGetAllOrdersResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } -func (m *QueryMarketInfoRequest) Marshal() (dAtA []byte, err error) { +func (m *QueryGetMarketRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -2170,12 +2336,12 @@ func (m *QueryMarketInfoRequest) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryMarketInfoRequest) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryGetMarketRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryMarketInfoRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryGetMarketRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -2188,7 +2354,49 @@ func (m *QueryMarketInfoRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } -func (m *QueryMarketInfoResponse) Marshal() (dAtA []byte, err error) { +func (m *QueryGetMarketResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetMarketResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetMarketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Market != nil { + { + size, err := m.Market.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryGetAllMarketsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -2198,16 +2406,81 @@ func (m *QueryMarketInfoResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryMarketInfoResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryGetAllMarketsRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryMarketInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryGetAllMarketsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0x9a + } + return len(dAtA) - i, nil +} + +func (m *QueryGetAllMarketsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetAllMarketsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetAllMarketsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0x9a + } + if len(m.Markets) > 0 { + for iNdEx := len(m.Markets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Markets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } return len(dAtA) - i, nil } @@ -2594,7 +2867,7 @@ func (m *QueryGetAllOrdersResponse) Size() (n int) { return n } -func (m *QueryMarketInfoRequest) Size() (n int) { +func (m *QueryGetMarketRequest) Size() (n int) { if m == nil { return 0 } @@ -2606,12 +2879,52 @@ func (m *QueryMarketInfoRequest) Size() (n int) { return n } -func (m *QueryMarketInfoResponse) Size() (n int) { +func (m *QueryGetMarketResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Market != nil { + l = m.Market.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryGetAllMarketsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Pagination != nil { + l = m.Pagination.Size() + n += 2 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryGetAllMarketsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Markets) > 0 { + for _, e := range m.Markets { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 2 + l + sovQuery(uint64(l)) + } return n } @@ -4167,7 +4480,7 @@ func (m *QueryGetAllOrdersResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryMarketInfoRequest) Unmarshal(dAtA []byte) error { +func (m *QueryGetMarketRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -4190,10 +4503,10 @@ func (m *QueryMarketInfoRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryMarketInfoRequest: wiretype end group for non-group") + return fmt.Errorf("proto: QueryGetMarketRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryMarketInfoRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryGetMarketRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -4236,7 +4549,7 @@ func (m *QueryMarketInfoRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryMarketInfoResponse) Unmarshal(dAtA []byte) error { +func (m *QueryGetMarketResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -4259,12 +4572,286 @@ func (m *QueryMarketInfoResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryMarketInfoResponse: wiretype end group for non-group") + return fmt.Errorf("proto: QueryGetMarketResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryMarketInfoResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryGetMarketResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Market", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Market == nil { + m.Market = &Market{} + } + if err := m.Market.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetAllMarketsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetAllMarketsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetAllMarketsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 99: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetAllMarketsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetAllMarketsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetAllMarketsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Markets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Markets = append(m.Markets, &MarketBrief{}) + if err := m.Markets[len(m.Markets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 99: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/x/exchange/query.pb.gw.go b/x/exchange/query.pb.gw.go index b969e29b6f..ece714a2dc 100644 --- a/x/exchange/query.pb.gw.go +++ b/x/exchange/query.pb.gw.go @@ -193,6 +193,78 @@ func local_request_Query_QueryGetMarketOrders_0(ctx context.Context, marshaler r } +var ( + filter_Query_QueryGetMarketOrders_1 = &utilities.DoubleArray{Encoding: map[string]int{"market_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_QueryGetMarketOrders_1(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetMarketOrdersRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetMarketOrders_1); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.QueryGetMarketOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryGetMarketOrders_1(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetMarketOrdersRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetMarketOrders_1); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.QueryGetMarketOrders(ctx, &protoReq) + return msg, metadata, err + +} + var ( filter_Query_QueryGetOwnerOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{"owner": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} ) @@ -373,8 +445,8 @@ func local_request_Query_QueryGetAllOrders_0(ctx context.Context, marshaler runt } -func request_Query_QueryMarketInfo_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryMarketInfoRequest +func request_Query_QueryGetMarket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetMarketRequest var metadata runtime.ServerMetadata var ( @@ -395,13 +467,13 @@ func request_Query_QueryMarketInfo_0(ctx context.Context, marshaler runtime.Mars return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) } - msg, err := client.QueryMarketInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.QueryGetMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryMarketInfo_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryMarketInfoRequest +func local_request_Query_QueryGetMarket_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetMarketRequest var metadata runtime.ServerMetadata var ( @@ -422,7 +494,43 @@ func local_request_Query_QueryMarketInfo_0(ctx context.Context, marshaler runtim return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) } - msg, err := server.QueryMarketInfo(ctx, &protoReq) + msg, err := server.QueryGetMarket(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_QueryGetAllMarkets_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_QueryGetAllMarkets_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetAllMarketsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetAllMarkets_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.QueryGetAllMarkets(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryGetAllMarkets_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetAllMarketsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetAllMarkets_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.QueryGetAllMarkets(ctx, &protoReq) return msg, metadata, err } @@ -583,6 +691,26 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_QueryGetMarketOrders_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryGetMarketOrders_1(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetMarketOrders_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_QueryGetOwnerOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -643,7 +771,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) - mux.Handle("GET", pattern_Query_QueryMarketInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_QueryGetMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -652,14 +780,34 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryMarketInfo_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_QueryGetMarket_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryMarketInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryGetMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetAllMarkets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryGetAllMarkets_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetAllMarkets_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -824,6 +972,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_QueryGetMarketOrders_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryGetMarketOrders_1(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetMarketOrders_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_QueryGetOwnerOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -884,7 +1052,7 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) - mux.Handle("GET", pattern_Query_QueryMarketInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_QueryGetMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -893,14 +1061,34 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryMarketInfo_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_QueryGetMarket_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryMarketInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryGetMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetAllMarkets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryGetAllMarkets_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetAllMarkets_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -972,7 +1160,9 @@ var ( pattern_Query_QueryGetOrder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "order", "order_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetMarketOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"provenance", "exchange", "v1", "market", "market_id", "orders"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryGetMarketOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"provenance", "exchange", "v1", "orders", "market", "market_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryGetMarketOrders_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"provenance", "exchange", "v1", "market", "market_id", "orders"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryGetOwnerOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "orders", "owner"}, "", runtime.AssumeColonVerbOpt(false))) @@ -980,7 +1170,9 @@ var ( pattern_Query_QueryGetAllOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "orders"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryMarketInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "market", "market_id"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryGetMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "market", "market_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryGetAllMarkets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "markets"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) @@ -996,13 +1188,17 @@ var ( forward_Query_QueryGetMarketOrders_0 = runtime.ForwardResponseMessage + forward_Query_QueryGetMarketOrders_1 = runtime.ForwardResponseMessage + forward_Query_QueryGetOwnerOrders_0 = runtime.ForwardResponseMessage forward_Query_QueryGetAssetOrders_0 = runtime.ForwardResponseMessage forward_Query_QueryGetAllOrders_0 = runtime.ForwardResponseMessage - forward_Query_QueryMarketInfo_0 = runtime.ForwardResponseMessage + forward_Query_QueryGetMarket_0 = runtime.ForwardResponseMessage + + forward_Query_QueryGetAllMarkets_0 = runtime.ForwardResponseMessage forward_Query_QueryParams_0 = runtime.ForwardResponseMessage From 9f6cb3e0af498a60c36099b0f5611f1424e263af Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 2 Oct 2023 14:02:19 -0600 Subject: [PATCH 220/309] [1658]: Implement QueryGetMarket and QueryGetAllMarkets. --- x/exchange/keeper/grpc_query.go | 58 +++++++++++++++++++++++++++++---- x/exchange/keeper/market.go | 14 ++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 50ce84829b..b56b8e0d87 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -175,8 +175,8 @@ func (k QueryServer) QueryGetAllOrders(goCtx context.Context, req *exchange.Quer return false, nil } if accumulate { - // Only add them to the result if we can read it. - // This might result in fewer results than the limit, but at least one bad entry won't block others. + // Only add it to the result if we can read it. This might result in fewer results than the limit, + // but at least one bad entry won't block others by causing the whole thing to return an error. order, oerr := k.parseOrderStoreValue(orderID, value) if oerr != nil { resp.Orders = append(resp.Orders, order) @@ -192,10 +192,56 @@ func (k QueryServer) QueryGetAllOrders(goCtx context.Context, req *exchange.Quer return resp, nil } -// QueryMarketInfo returns the information/details about a market. -func (k QueryServer) QueryMarketInfo(goCtx context.Context, req *exchange.QueryMarketInfoRequest) (*exchange.QueryMarketInfoResponse, error) { - // TODO[1658]: Implement QueryMarketInfo query - panic("not implemented") +// QueryGetMarket returns all the information and details about a market. +func (k QueryServer) QueryGetMarket(goCtx context.Context, req *exchange.QueryGetMarketRequest) (*exchange.QueryGetMarketResponse, error) { + if req == nil || req.MarketId == 0 { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + market := k.GetMarket(ctx, req.MarketId) + if market == nil { + return nil, status.Errorf(codes.InvalidArgument, "market %d not found", req.MarketId) + } + + return &exchange.QueryGetMarketResponse{Market: market}, nil +} + +// QueryGetAllMarkets returns brief information about each market. +func (k QueryServer) QueryGetAllMarkets(goCtx context.Context, req *exchange.QueryGetAllMarketsRequest) (*exchange.QueryGetAllMarketsResponse, error) { + var pagination *query.PageRequest + if req != nil { + pagination = req.Pagination + } + + ctx := sdk.UnwrapSDKContext(goCtx) + pre := GetKeyPrefixKnownMarketID() + store := prefix.NewStore(k.getStore(ctx), pre) + + resp := &exchange.QueryGetAllMarketsResponse{} + var pageErr error + resp.Pagination, pageErr = query.FilteredPaginate(store, pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { + // If we can't get the market id from the key, just pretend like it doesn't exist. + marketID, ok := ParseKeySuffixKnownMarketID(key) + if !ok { + return false, nil + } + if accumulate { + // Only add it to the result if we can read it. This might result in fewer results than the limit, + // but at least one bad entry won't block others by causing the whole thing to return an error. + brief := k.GetMarketBrief(ctx, marketID) + if brief != nil { + resp.Markets = append(resp.Markets, brief) + } + } + return true, nil + }) + + if pageErr != nil { + return nil, status.Errorf(codes.InvalidArgument, "error iterating all known markets: %v", pageErr) + } + + return resp, nil } // QueryParams returns the exchange module parameters. diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 2bd529fa6f..5c97b1d790 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -1059,6 +1059,20 @@ func (k Keeper) GetMarket(ctx sdk.Context, marketID uint32) *exchange.Market { return market } +// GetMarketBrief gets the MarketBrief for the given market id. +func (k Keeper) GetMarketBrief(ctx sdk.Context, marketID uint32) *exchange.MarketBrief { + acc := k.GetMarketAccount(ctx, marketID) + if acc == nil { + return nil + } + + return &exchange.MarketBrief{ + MarketId: marketID, + MarketAddress: acc.Address, + MarketDetails: acc.MarketDetails, + } +} + // UpdateFees updates all the fees as provided in the MsgGovManageFeesRequest. func (k Keeper) UpdateFees(ctx sdk.Context, msg *exchange.MsgGovManageFeesRequest) error { store := k.getStore(ctx) From 4af84bc56a2de03ebeb20691720793c0deba22f3 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 2 Oct 2023 14:25:16 -0600 Subject: [PATCH 221/309] [1658]: Fill in and implement QueryParams. --- docs/proto-docs.md | 7 +- proto/provenance/exchange/v1/query.proto | 13 +- x/exchange/keeper/grpc_query.go | 7 +- x/exchange/keeper/params.go | 36 +++- x/exchange/query.pb.go | 216 +++++++++++++++-------- x/exchange/query.pb.gw.go | 44 ++--- 6 files changed, 201 insertions(+), 122 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 7e754236a3..59c329d941 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2190,6 +2190,11 @@ QueryParamsRequest is a request message for the QueryParams endpoint. QueryParamsResponse is a response message for the QueryParams endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#provenance.exchange.v1.Params) | | params are the exchange module parameter values. | + + @@ -2262,7 +2267,7 @@ Query is the service for exchange module's query endpoints. | `QueryGetAllMarkets` | [QueryGetAllMarketsRequest](#provenance.exchange.v1.QueryGetAllMarketsRequest) | [QueryGetAllMarketsResponse](#provenance.exchange.v1.QueryGetAllMarketsResponse) | QueryGetAllMarkets returns brief information about each market. | GET|/provenance/exchange/v1/markets| | `QueryParams` | [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) | [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) | QueryParams returns the exchange module parameters. | GET|/provenance/exchange/v1/params| | `QueryValidateCreateMarket` | [QueryValidateCreateMarketRequest](#provenance.exchange.v1.QueryValidateCreateMarketRequest) | [QueryValidateCreateMarketResponse](#provenance.exchange.v1.QueryValidateCreateMarketResponse) | QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. | GET|/provenance/exchange/v1/validate/create_market| -| `QueryValidateManageFees` | [QueryValidateManageFeesRequest](#provenance.exchange.v1.QueryValidateManageFeesRequest) | [QueryValidateManageFeesResponse](#provenance.exchange.v1.QueryValidateManageFeesResponse) | QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. | GET|/provenance/exchange/v1/market/{market_id}/validate/manage_fees| +| `QueryValidateManageFees` | [QueryValidateManageFeesRequest](#provenance.exchange.v1.QueryValidateManageFeesRequest) | [QueryValidateManageFeesResponse](#provenance.exchange.v1.QueryValidateManageFeesResponse) | QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. | GET|/provenance/exchange/v1/validate/manage_fees| diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 4e913e8aae..75a935f452 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -11,8 +11,9 @@ import "cosmos/base/query/v1beta1/pagination.proto"; import "cosmos/base/v1beta1/coin.proto"; import "google/api/annotations.proto"; import "gogoproto/gogo.proto"; -import "provenance/exchange/v1/orders.proto"; import "provenance/exchange/v1/market.proto"; +import "provenance/exchange/v1/orders.proto"; +import "provenance/exchange/v1/params.proto"; // Query is the service for exchange module's query endpoints. service Query { @@ -71,7 +72,7 @@ service Query { // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. rpc QueryValidateManageFees(QueryValidateManageFeesRequest) returns (QueryValidateManageFeesResponse) { - option (google.api.http).get = "/provenance/exchange/v1/market/{market_id}/validate/manage_fees"; + option (google.api.http).get = "/provenance/exchange/v1/validate/manage_fees"; } } @@ -228,12 +229,12 @@ message QueryGetAllMarketsResponse { } // QueryParamsRequest is a request message for the QueryParams endpoint. -message QueryParamsRequest { - // TODO[1658]: QueryParamsRequest -} +message QueryParamsRequest {} + // QueryParamsResponse is a response message for the QueryParams endpoint. message QueryParamsResponse { - // TODO[1658]: QueryParamsResponse + // params are the exchange module parameter values. + Params params = 1; } // QueryValidateCreateMarketRequest is a request message for the QueryValidateCreateMarket endpoint. diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index b56b8e0d87..c715dd6870 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -245,9 +245,10 @@ func (k QueryServer) QueryGetAllMarkets(goCtx context.Context, req *exchange.Que } // QueryParams returns the exchange module parameters. -func (k QueryServer) QueryParams(goCtx context.Context, req *exchange.QueryParamsRequest) (*exchange.QueryParamsResponse, error) { - // TODO[1658]: Implement QueryParams query - panic("not implemented") +func (k QueryServer) QueryParams(goCtx context.Context, _ *exchange.QueryParamsRequest) (*exchange.QueryParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + resp := &exchange.QueryParamsResponse{Params: k.GetParamsOrDefaults(ctx)} + return resp, nil } // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. diff --git a/x/exchange/keeper/params.go b/x/exchange/keeper/params.go index dcaa467c6f..0ffc7e9394 100644 --- a/x/exchange/keeper/params.go +++ b/x/exchange/keeper/params.go @@ -67,15 +67,43 @@ func (k Keeper) GetParams(ctx sdk.Context) *exchange.Params { return rv } +// GetParamsOrDefaults gets the exchange module params from state if there are any. +// If state doesn't have any param info, the defaults are returned. +func (k Keeper) GetParamsOrDefaults(ctx sdk.Context) *exchange.Params { + if rv := k.GetParams(ctx); rv != nil { + return rv + } + return exchange.DefaultParams() +} + // GetExchangeSplit gets the split amount for the provided denom. // If the denom is "", the default is returned. // If there isn't a specific entry for the provided denom, the default is returned. func (k Keeper) GetExchangeSplit(ctx sdk.Context, denom string) uint16 { store := k.getStore(ctx) - split, found := getParamsSplit(store, denom) + if split, found := getParamsSplit(store, denom); found { + return split + } + // If it wasn't found, and we weren't already looking for the default, look up the default now. - if !found && len(denom) > 0 { - split, _ = getParamsSplit(store, "") + if len(denom) > 0 { + if split, found := getParamsSplit(store, ""); found { + return split + } } - return split + + // If still not found, look to the hard-coded defaults. + defaults := exchange.DefaultParams() + + // If looking for a specific denom, check the denom splits first. + if len(denom) > 0 && len(defaults.DenomSplits) > 0 { + for _, ds := range defaults.DenomSplits { + if ds.Denom == denom { + return uint16(ds.Split) + } + } + } + + // Lastly, use the default from the defaults. + return uint16(defaults.DefaultSplit) } diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index a791a0c328..f02dffa4ec 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -981,6 +981,8 @@ var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo // QueryParamsResponse is a response message for the QueryParams endpoint. type QueryParamsResponse struct { + // params are the exchange module parameter values. + Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` } func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } @@ -1016,6 +1018,13 @@ func (m *QueryParamsResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo +func (m *QueryParamsResponse) GetParams() *Params { + if m != nil { + return m.Params + } + return nil +} + // QueryValidateCreateMarketRequest is a request message for the QueryValidateCreateMarket endpoint. type QueryValidateCreateMarketRequest struct { } @@ -1202,84 +1211,85 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 1226 bytes of a gzipped FileDescriptorProto + // 1238 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4f, 0x4f, 0x1b, 0x47, - 0x14, 0x67, 0x20, 0x04, 0x78, 0x34, 0xa9, 0x3a, 0x75, 0x52, 0x7b, 0x21, 0xc6, 0x2c, 0x51, 0x82, - 0x28, 0xec, 0x62, 0x43, 0x69, 0x7b, 0x40, 0x15, 0x20, 0x81, 0x22, 0x35, 0x82, 0x6c, 0xab, 0x1e, - 0x72, 0xa8, 0x3b, 0xb6, 0x07, 0xb3, 0xc2, 0xde, 0x71, 0x76, 0x17, 0x1a, 0x84, 0xb8, 0x54, 0x3d, - 0x57, 0x95, 0x7a, 0xa9, 0x54, 0x35, 0xaa, 0xd4, 0x7c, 0x80, 0x1e, 0x72, 0xef, 0xb1, 0x1c, 0xa3, - 0x72, 0x69, 0x2f, 0x55, 0x05, 0xfd, 0x02, 0xfd, 0x06, 0xd5, 0xce, 0xcc, 0xda, 0xbb, 0xd8, 0xfb, - 0xc7, 0x15, 0x07, 0x4e, 0xec, 0xce, 0xbe, 0xdf, 0x7b, 0xbf, 0xf7, 0x9b, 0x37, 0xf3, 0x1e, 0x06, - 0xb5, 0x65, 0xb3, 0x43, 0x6a, 0x11, 0xab, 0x4a, 0x75, 0xfa, 0xbc, 0xba, 0x47, 0xac, 0x3a, 0xd5, - 0x0f, 0x8b, 0xfa, 0xb3, 0x03, 0x6a, 0x1f, 0x69, 0x2d, 0x9b, 0xb9, 0x0c, 0xdf, 0xed, 0xd8, 0x68, - 0xbe, 0x8d, 0x76, 0x58, 0x54, 0x72, 0x55, 0xe6, 0x34, 0x99, 0x53, 0xe6, 0x56, 0xba, 0x78, 0x11, - 0x10, 0x65, 0x4e, 0xbc, 0xe9, 0x15, 0xe2, 0x50, 0xe1, 0x4b, 0x3f, 0x2c, 0x56, 0xa8, 0x4b, 0x8a, - 0x7a, 0x8b, 0xd4, 0x4d, 0x8b, 0xb8, 0x26, 0xb3, 0xa4, 0x6d, 0x3e, 0x68, 0xeb, 0x5b, 0x55, 0x99, - 0xe9, 0x7f, 0x9f, 0xac, 0x33, 0x56, 0x6f, 0x50, 0x9d, 0xb4, 0x4c, 0x9d, 0x58, 0x16, 0x73, 0x39, - 0xd8, 0x8f, 0x94, 0xa9, 0xb3, 0x3a, 0x13, 0x0c, 0xbc, 0x27, 0xb9, 0x3a, 0x13, 0x91, 0x16, 0xb3, - 0x6b, 0xd4, 0x76, 0x12, 0x8c, 0x9a, 0xc4, 0xde, 0xa7, 0xae, 0x30, 0x52, 0xbf, 0x47, 0x90, 0x7d, - 0xe2, 0x25, 0xb0, 0xed, 0x41, 0x37, 0x29, 0xdd, 0x20, 0x8d, 0xaa, 0x41, 0x9f, 0x1d, 0x50, 0xc7, - 0xc5, 0xab, 0x30, 0x46, 0x9c, 0xfd, 0x32, 0xf7, 0x9a, 0x1d, 0x2c, 0xa0, 0xd9, 0xf1, 0x52, 0x41, - 0xeb, 0xad, 0x96, 0xb6, 0xe6, 0xec, 0x73, 0x17, 0xc6, 0x28, 0x91, 0x4f, 0x1e, 0xbc, 0x62, 0xd6, - 0x24, 0x7c, 0x28, 0x1e, 0xbe, 0x6e, 0xd6, 0x24, 0xbc, 0x22, 0x9f, 0xd4, 0x5f, 0x06, 0x21, 0xd7, - 0x83, 0x9a, 0xd3, 0x62, 0x96, 0x43, 0xf1, 0x13, 0xc8, 0x54, 0x6d, 0xca, 0xb5, 0x2a, 0xef, 0x52, - 0x5a, 0x66, 0x2d, 0x2e, 0x5b, 0x16, 0x15, 0x86, 0x66, 0xc7, 0x4b, 0x39, 0x4d, 0xee, 0x97, 0xa7, - 0xba, 0x26, 0x55, 0xd7, 0x36, 0x98, 0x69, 0xad, 0xdf, 0x38, 0xfd, 0x6b, 0x6a, 0xc0, 0xc0, 0x3e, - 0x78, 0x93, 0xd2, 0x6d, 0x01, 0xc5, 0x9f, 0xc3, 0x84, 0x43, 0x5d, 0xb7, 0x41, 0x9b, 0xd4, 0x72, - 0xcb, 0xbb, 0x0d, 0xe2, 0x86, 0x3c, 0x0f, 0xa6, 0xf3, 0x9c, 0xed, 0xf8, 0xd8, 0x6c, 0x10, 0x37, - 0xe0, 0xff, 0x0b, 0x98, 0x0c, 0xf8, 0xb7, 0xbd, 0xf0, 0xa1, 0x00, 0x43, 0xe9, 0x02, 0xe4, 0x3a, - 0x4e, 0x0c, 0xcf, 0x47, 0x27, 0x82, 0x5a, 0x84, 0x0c, 0x57, 0x6c, 0x8b, 0xba, 0x42, 0x4d, 0xb9, - 0x91, 0x39, 0x18, 0xe5, 0xbb, 0x50, 0x36, 0x6b, 0x59, 0x54, 0x40, 0xb3, 0x37, 0x8c, 0x11, 0xfe, - 0xfe, 0xa8, 0xa6, 0x7e, 0x0c, 0x77, 0x2e, 0x41, 0xa4, 0xc0, 0x4b, 0x30, 0x2c, 0x76, 0x0e, 0xf1, - 0x9d, 0xbb, 0x17, 0xb5, 0x73, 0x02, 0x25, 0x6c, 0xd5, 0xdf, 0x10, 0x4c, 0xf8, 0xee, 0x1e, 0xf3, - 0x3a, 0xe3, 0x9f, 0x1d, 0x9f, 0xc8, 0x04, 0x8c, 0x89, 0xf2, 0xf3, 0x99, 0xdc, 0x32, 0x46, 0xc5, - 0xc2, 0xa3, 0x1a, 0xbe, 0x07, 0x20, 0x58, 0xba, 0x47, 0x2d, 0xca, 0xeb, 0x6d, 0xcc, 0x18, 0xe3, - 0x2b, 0x9f, 0x1e, 0xb5, 0x28, 0xbe, 0x0f, 0xb7, 0xc9, 0xae, 0x4b, 0xed, 0x72, 0x3b, 0x95, 0x21, - 0x9e, 0xca, 0x1b, 0x7c, 0x75, 0x5b, 0xe4, 0x83, 0x37, 0x01, 0x3a, 0x47, 0x30, 0x5b, 0xe5, 0xdc, - 0x1f, 0x84, 0x24, 0x15, 0x67, 0xdf, 0x17, 0x76, 0x87, 0xd4, 0xa9, 0x64, 0x67, 0x04, 0x90, 0xea, - 0x0b, 0x04, 0x93, 0xbd, 0x33, 0x91, 0xfa, 0xbc, 0x07, 0x37, 0xc5, 0x71, 0x93, 0x25, 0x97, 0x20, - 0x90, 0x34, 0xc6, 0x5b, 0x3d, 0xf8, 0x3d, 0x4c, 0xe4, 0x27, 0x62, 0x86, 0x08, 0xfe, 0x89, 0x40, - 0x69, 0xef, 0xdc, 0x97, 0x96, 0x54, 0xa0, 0xad, 0xb4, 0x06, 0xc3, 0xcc, 0x5b, 0xe5, 0x2a, 0x8f, - 0xad, 0x67, 0x7f, 0x7f, 0xb5, 0x90, 0x91, 0x51, 0xd6, 0x6a, 0x35, 0x9b, 0x3a, 0xce, 0x27, 0xae, - 0x6d, 0x5a, 0x75, 0x43, 0x98, 0x5d, 0x2f, 0xf1, 0x7f, 0x0c, 0x94, 0x51, 0x28, 0xb7, 0x6b, 0xa2, - 0xfd, 0xaf, 0x01, 0xed, 0xd7, 0x1c, 0xe7, 0x72, 0x95, 0x67, 0x60, 0x98, 0x78, 0xab, 0x42, 0x7b, - 0x43, 0xbc, 0x5c, 0x5f, 0x85, 0x43, 0x19, 0x5c, 0x13, 0x85, 0x2b, 0xb2, 0x2d, 0x79, 0xf4, 0x1a, - 0x8d, 0xb0, 0xbc, 0x57, 0xa5, 0xc1, 0x0f, 0x48, 0x36, 0x98, 0x70, 0x90, 0x6b, 0xa2, 0xc0, 0x72, - 0xe7, 0x62, 0x16, 0xf7, 0x4f, 0x9a, 0x3b, 0x54, 0xfd, 0x1a, 0xc1, 0xdd, 0xcb, 0x30, 0x99, 0x50, - 0x09, 0x46, 0x88, 0x38, 0xf9, 0x89, 0x77, 0x82, 0x6f, 0x88, 0x57, 0xe0, 0xa6, 0x70, 0x2d, 0xdb, - 0x7f, 0x3e, 0x4a, 0x04, 0x19, 0x4b, 0x5a, 0xab, 0xd5, 0x90, 0xb2, 0xe2, 0xe3, 0x95, 0xef, 0xdf, - 0xcb, 0xe0, 0x29, 0x0c, 0x44, 0x91, 0xf9, 0xae, 0xc2, 0x88, 0x60, 0xe3, 0xef, 0xe0, 0x4c, 0x3c, - 0xf9, 0x75, 0xdb, 0xa4, 0xbb, 0x86, 0x8f, 0xb9, 0xba, 0x8d, 0xcc, 0x00, 0xe6, 0x2c, 0x77, 0x88, - 0x4d, 0x9a, 0xbe, 0x08, 0xea, 0x1d, 0x78, 0x3b, 0xb4, 0x2a, 0x80, 0xaa, 0x0a, 0x05, 0xbe, 0xfc, - 0x19, 0x69, 0x98, 0x35, 0xe2, 0xd2, 0x0d, 0x6f, 0x4c, 0xa1, 0xa1, 0x02, 0x50, 0x67, 0x60, 0x3a, - 0xc6, 0x46, 0x3a, 0x5a, 0x85, 0x7c, 0xc8, 0xe8, 0x31, 0xb1, 0x48, 0x9d, 0x6e, 0x52, 0x9a, 0xaa, - 0x17, 0xab, 0xd3, 0x30, 0x15, 0x09, 0x17, 0x11, 0x4a, 0x67, 0x6f, 0xc2, 0x30, 0xb7, 0xc1, 0x2f, - 0x11, 0xbc, 0xd5, 0x35, 0xa9, 0xe1, 0xc5, 0x28, 0xb9, 0xa3, 0xe6, 0x4d, 0xa5, 0xd8, 0x07, 0x42, - 0xa6, 0x39, 0xf7, 0xd5, 0xd9, 0x3f, 0xdf, 0x0d, 0xde, 0xc7, 0xaa, 0x1e, 0x31, 0xed, 0xee, 0x52, - 0xea, 0x88, 0xb9, 0x18, 0xbf, 0x40, 0x70, 0x2b, 0x34, 0xeb, 0xe0, 0xf9, 0xd8, 0x80, 0x97, 0xa6, - 0x28, 0x65, 0x21, 0xa5, 0xb5, 0xa4, 0xb6, 0xc8, 0xa9, 0xcd, 0xe1, 0x59, 0x3d, 0x6e, 0x5a, 0xd7, - 0x8f, 0xfd, 0xfb, 0xfe, 0x04, 0xff, 0x8b, 0x3a, 0xf3, 0x5b, 0x70, 0xe6, 0xc0, 0x4b, 0x49, 0x91, - 0x7b, 0xcc, 0x5a, 0xca, 0x72, 0x7f, 0x20, 0xc9, 0xda, 0xe2, 0xac, 0xf7, 0x70, 0x31, 0x96, 0xb5, - 0x23, 0xff, 0x8b, 0xd0, 0x8f, 0xdb, 0x25, 0x74, 0xf2, 0x74, 0x29, 0x1a, 0xd4, 0x6d, 0x2d, 0xfd, - 0xe0, 0x57, 0x48, 0x1e, 0x84, 0x70, 0xab, 0xc7, 0xa5, 0x44, 0xb1, 0xbb, 0x66, 0x1e, 0x65, 0xa9, - 0x2f, 0x8c, 0x4c, 0x78, 0x99, 0x27, 0xac, 0xe1, 0xf9, 0x84, 0x84, 0xf9, 0x98, 0xa4, 0x1f, 0xf3, - 0x3f, 0x27, 0x21, 0xda, 0x81, 0xfe, 0x99, 0x4c, 0xbb, 0x7b, 0x5c, 0x48, 0xa6, 0xdd, 0xa3, 0x41, - 0xa7, 0xa6, 0xcd, 0x67, 0x0f, 0xfd, 0x98, 0xff, 0x39, 0xc1, 0x3f, 0xf9, 0x27, 0x35, 0xd8, 0xf2, - 0x12, 0x4e, 0x6a, 0x8f, 0x16, 0x9c, 0x70, 0x52, 0x7b, 0xf5, 0x53, 0xf5, 0x01, 0x27, 0x5c, 0xc0, - 0xf9, 0x78, 0xc2, 0xf8, 0x67, 0x04, 0xb7, 0xc3, 0x15, 0x8a, 0x17, 0xd2, 0x55, 0xb2, 0x4f, 0x4e, - 0x4b, 0x6b, 0x2e, 0x99, 0x95, 0x38, 0xb3, 0x79, 0x3c, 0x97, 0xbe, 0x7a, 0xbd, 0x2b, 0x0f, 0x77, - 0xf7, 0x1e, 0x9c, 0x46, 0x97, 0x70, 0x37, 0x54, 0x4a, 0xfd, 0x40, 0x24, 0xe3, 0x87, 0x9c, 0xf1, - 0x34, 0x9e, 0x8a, 0x67, 0xec, 0xe0, 0x6f, 0x10, 0x8c, 0x07, 0xda, 0x0c, 0x9e, 0x8b, 0x0d, 0x16, - 0xea, 0x50, 0xca, 0xbb, 0xa9, 0x6c, 0xd3, 0xee, 0x6e, 0x4b, 0x10, 0x38, 0xf5, 0x67, 0xae, 0x5e, - 0xcd, 0x0b, 0x7f, 0x10, 0x1b, 0x32, 0xa6, 0x27, 0x2a, 0x1f, 0xfe, 0x0f, 0xa4, 0xa4, 0xbe, 0xc2, - 0xa9, 0x2f, 0x62, 0x2d, 0x8a, 0xfa, 0xa1, 0x44, 0xeb, 0xfc, 0x37, 0x03, 0x5a, 0x16, 0xe2, 0xe2, - 0x33, 0x04, 0xef, 0x44, 0xf4, 0x48, 0xbc, 0x92, 0x8a, 0x4e, 0x57, 0x4f, 0x56, 0xde, 0xef, 0x1b, - 0x27, 0x93, 0xd8, 0xe2, 0x49, 0xac, 0xe1, 0x8f, 0xfa, 0xb8, 0x81, 0xdb, 0x79, 0x35, 0xb9, 0xbf, - 0xb2, 0xd7, 0x2a, 0xd7, 0xe9, 0xe9, 0x79, 0x1e, 0xbd, 0x3e, 0xcf, 0xa3, 0xbf, 0xcf, 0xf3, 0xe8, - 0xdb, 0x8b, 0xfc, 0xc0, 0xeb, 0x8b, 0xfc, 0xc0, 0x1f, 0x17, 0xf9, 0x01, 0xc8, 0x99, 0x2c, 0x82, - 0xdd, 0x0e, 0x7a, 0xaa, 0xd5, 0x4d, 0x77, 0xef, 0xa0, 0xa2, 0x55, 0x59, 0x33, 0xc0, 0x60, 0xc1, - 0x64, 0x41, 0x3e, 0xcf, 0xdb, 0x8c, 0x2a, 0x37, 0xf9, 0xcf, 0x4f, 0x4b, 0xff, 0x05, 0x00, 0x00, - 0xff, 0xff, 0x6d, 0x67, 0x14, 0x44, 0xa1, 0x13, 0x00, 0x00, + 0x14, 0x67, 0x20, 0xfc, 0x7b, 0x34, 0xa9, 0x3a, 0x75, 0x53, 0x7b, 0x21, 0xc6, 0x2c, 0x51, 0x82, + 0x28, 0xec, 0x62, 0x43, 0x69, 0x7b, 0xe0, 0x00, 0x48, 0x44, 0x91, 0x8a, 0x20, 0xdb, 0xaa, 0x87, + 0x1c, 0xea, 0x8e, 0xed, 0xc1, 0xac, 0xb0, 0x77, 0x9c, 0xdd, 0x85, 0x06, 0x21, 0x2e, 0x55, 0xcf, + 0x55, 0xa5, 0x5e, 0x2a, 0x55, 0x8d, 0x2a, 0x35, 0x1f, 0xa0, 0x87, 0xdc, 0x9b, 0x5b, 0x39, 0x46, + 0xed, 0xa5, 0xbd, 0x54, 0x15, 0xf4, 0x0b, 0xf4, 0x1b, 0x54, 0x3b, 0x33, 0x6b, 0xef, 0x62, 0xef, + 0x1f, 0x22, 0x0e, 0x3e, 0xb1, 0x3b, 0xfb, 0x7e, 0xef, 0xfd, 0xde, 0xef, 0xcd, 0xcc, 0x7b, 0x18, + 0xd4, 0x96, 0xcd, 0x8e, 0xa8, 0x45, 0xac, 0x2a, 0xd5, 0xe9, 0xd3, 0xea, 0x3e, 0xb1, 0xea, 0x54, + 0x3f, 0x2a, 0xea, 0x4f, 0x0e, 0xa9, 0x7d, 0xac, 0xb5, 0x6c, 0xe6, 0x32, 0x7c, 0xbb, 0x63, 0xa3, + 0xf9, 0x36, 0xda, 0x51, 0x51, 0xc9, 0x55, 0x99, 0xd3, 0x64, 0x4e, 0x99, 0x5b, 0xe9, 0xe2, 0x45, + 0x40, 0x94, 0x79, 0xf1, 0xa6, 0x57, 0x88, 0x43, 0x85, 0x2f, 0xfd, 0xa8, 0x58, 0xa1, 0x2e, 0x29, + 0xea, 0x2d, 0x52, 0x37, 0x2d, 0xe2, 0x9a, 0xcc, 0x92, 0xb6, 0xf9, 0xa0, 0xad, 0x6f, 0x55, 0x65, + 0xa6, 0xff, 0x7d, 0xaa, 0xce, 0x58, 0xbd, 0x41, 0x75, 0xd2, 0x32, 0x75, 0x62, 0x59, 0xcc, 0xe5, + 0x60, 0x3f, 0x52, 0xa6, 0xce, 0xea, 0x4c, 0x30, 0xf0, 0x9e, 0xe4, 0xea, 0x6c, 0x44, 0x5a, 0x4d, + 0x62, 0x1f, 0x50, 0x37, 0xc1, 0x88, 0xd9, 0x35, 0x6a, 0x3b, 0x09, 0x46, 0x2d, 0x62, 0x93, 0xa6, + 0x34, 0x52, 0xbf, 0x47, 0x90, 0x7d, 0xe4, 0x65, 0xb9, 0xe3, 0x41, 0xb7, 0x28, 0xdd, 0x24, 0x8d, + 0xaa, 0x41, 0x9f, 0x1c, 0x52, 0xc7, 0xc5, 0x6b, 0x30, 0x4e, 0x9c, 0x83, 0x32, 0xf7, 0x9a, 0x1d, + 0x2c, 0xa0, 0xb9, 0x89, 0x52, 0x41, 0xeb, 0x2d, 0xa9, 0xb6, 0xee, 0x1c, 0x70, 0x17, 0xc6, 0x18, + 0x91, 0x4f, 0x1e, 0xbc, 0x62, 0xd6, 0x24, 0x7c, 0x28, 0x1e, 0xbe, 0x61, 0xd6, 0x24, 0xbc, 0x22, + 0x9f, 0xd4, 0x5f, 0x06, 0x21, 0xd7, 0x83, 0x9a, 0xd3, 0x62, 0x96, 0x43, 0xf1, 0x23, 0xc8, 0x54, + 0x6d, 0xca, 0x05, 0x2d, 0xef, 0x51, 0x5a, 0x66, 0x2d, 0xae, 0x6d, 0x16, 0x15, 0x86, 0xe6, 0x26, + 0x4a, 0x39, 0x4d, 0x16, 0xd5, 0x2b, 0x8d, 0x26, 0x4b, 0xa3, 0x6d, 0x32, 0xd3, 0xda, 0xb8, 0x71, + 0xf6, 0xf7, 0xf4, 0x80, 0x81, 0x7d, 0xf0, 0x16, 0xa5, 0x3b, 0x02, 0x8a, 0x3f, 0x87, 0x49, 0x87, + 0xba, 0x6e, 0x83, 0x36, 0xa9, 0xe5, 0x96, 0xf7, 0x1a, 0xc4, 0x0d, 0x79, 0x1e, 0x4c, 0xe7, 0x39, + 0xdb, 0xf1, 0xb1, 0xd5, 0x20, 0x6e, 0xc0, 0xff, 0x17, 0x30, 0x15, 0xf0, 0x6f, 0x7b, 0xe1, 0x43, + 0x01, 0x86, 0xd2, 0x05, 0xc8, 0x75, 0x9c, 0x18, 0x9e, 0x8f, 0x4e, 0x04, 0xb5, 0x08, 0x19, 0xae, + 0xd8, 0x03, 0xea, 0x0a, 0x35, 0x65, 0x21, 0x73, 0x30, 0xc6, 0xab, 0x50, 0x36, 0x6b, 0x59, 0x54, + 0x40, 0x73, 0x37, 0x8c, 0x51, 0xfe, 0xfe, 0xb0, 0xa6, 0x7e, 0x0c, 0xef, 0x5c, 0x82, 0x48, 0x81, + 0x97, 0x61, 0x58, 0x54, 0x0e, 0xf1, 0xca, 0xdd, 0x89, 0xaa, 0x9c, 0x40, 0x09, 0x5b, 0xf5, 0x37, + 0x04, 0x93, 0xbe, 0xbb, 0x6d, 0xbe, 0x63, 0xf9, 0x67, 0xc7, 0x27, 0x32, 0x09, 0xe3, 0x62, 0x23, + 0xfb, 0x4c, 0x6e, 0x1a, 0x63, 0x62, 0xe1, 0x61, 0x0d, 0xdf, 0x01, 0x10, 0x2c, 0xdd, 0xe3, 0x16, + 0xe5, 0xfb, 0x6d, 0xdc, 0x18, 0xe7, 0x2b, 0x9f, 0x1e, 0xb7, 0x28, 0xbe, 0x0b, 0xb7, 0xc8, 0x9e, + 0x4b, 0xed, 0x72, 0x3b, 0x95, 0x21, 0x9e, 0xca, 0x1b, 0x7c, 0x75, 0x47, 0xe4, 0x83, 0xb7, 0x00, + 0x3a, 0xe7, 0x34, 0x5b, 0xe5, 0xdc, 0xef, 0x85, 0x24, 0x15, 0x17, 0x84, 0x2f, 0xec, 0x2e, 0xa9, + 0x53, 0xc9, 0xce, 0x08, 0x20, 0xd5, 0x67, 0x08, 0xa6, 0x7a, 0x67, 0x22, 0xf5, 0x79, 0x1f, 0x46, + 0xc4, 0x71, 0x93, 0x5b, 0x2e, 0x41, 0x20, 0x69, 0x8c, 0x1f, 0xf4, 0xe0, 0x77, 0x3f, 0x91, 0x9f, + 0x88, 0x19, 0x22, 0xf8, 0x17, 0x02, 0xa5, 0x5d, 0xb9, 0x2f, 0x2d, 0xa9, 0x40, 0x5b, 0x69, 0x0d, + 0x86, 0x99, 0xb7, 0xca, 0x55, 0x1e, 0xdf, 0xc8, 0xfe, 0xfe, 0x62, 0x31, 0x23, 0xa3, 0xac, 0xd7, + 0x6a, 0x36, 0x75, 0x9c, 0x4f, 0x5c, 0xdb, 0xb4, 0xea, 0x86, 0x30, 0xeb, 0x2f, 0xf1, 0x7f, 0x0c, + 0x6c, 0xa3, 0x50, 0x6e, 0x7d, 0xa2, 0xfd, 0xaf, 0x01, 0xed, 0xd7, 0x1d, 0xe7, 0xf2, 0x2e, 0xcf, + 0xc0, 0x30, 0xf1, 0x56, 0x85, 0xf6, 0x86, 0x78, 0xe9, 0x5f, 0x85, 0x43, 0x19, 0xf4, 0x89, 0xc2, + 0x15, 0xd9, 0x96, 0x3c, 0x7a, 0x8d, 0x46, 0x58, 0xde, 0xeb, 0xd2, 0xe0, 0x07, 0x24, 0x1b, 0x4c, + 0x38, 0x48, 0x9f, 0x28, 0xb0, 0xd2, 0xb9, 0x98, 0xc5, 0xfd, 0x93, 0xe6, 0x0e, 0x55, 0xbf, 0x46, + 0x70, 0xfb, 0x32, 0x4c, 0x26, 0x54, 0x82, 0x51, 0x22, 0x4e, 0x7e, 0xe2, 0x9d, 0xe0, 0x1b, 0xe2, + 0x55, 0x18, 0x11, 0xae, 0x65, 0xfb, 0xcf, 0x47, 0x89, 0x20, 0x63, 0x49, 0x6b, 0xb5, 0x1a, 0x52, + 0x56, 0x7c, 0xbc, 0xf6, 0xfa, 0x3d, 0x0f, 0x9e, 0xc2, 0x40, 0x14, 0x99, 0xef, 0x1a, 0x8c, 0x0a, + 0x36, 0x7e, 0x05, 0x67, 0xe3, 0xc9, 0x6f, 0xd8, 0x26, 0xdd, 0x33, 0x7c, 0xcc, 0xf5, 0x15, 0x32, + 0x03, 0x98, 0xb3, 0xdc, 0xe5, 0x73, 0x97, 0x4c, 0x44, 0xdd, 0x86, 0xb7, 0x43, 0xab, 0x92, 0xf4, + 0x2a, 0x8c, 0x88, 0xf9, 0x4c, 0xb6, 0xdd, 0x48, 0xc1, 0x25, 0x4e, 0x5a, 0xab, 0x2a, 0x14, 0xb8, + 0xbb, 0xcf, 0x48, 0xc3, 0xac, 0x11, 0x97, 0x6e, 0x7a, 0xe3, 0x0d, 0x0d, 0x6d, 0x1c, 0x75, 0x16, + 0x66, 0x62, 0x6c, 0x04, 0x01, 0x75, 0x0d, 0xf2, 0x21, 0xa3, 0x6d, 0x62, 0x91, 0x3a, 0xdd, 0xa2, + 0x34, 0x55, 0x0f, 0x57, 0x67, 0x60, 0x3a, 0x12, 0x2e, 0x22, 0x94, 0x5e, 0xbe, 0x09, 0xc3, 0xdc, + 0x06, 0x3f, 0x47, 0xf0, 0x56, 0xd7, 0x84, 0x87, 0x97, 0xa2, 0x52, 0x8e, 0x9a, 0x53, 0x95, 0xe2, + 0x15, 0x10, 0x32, 0xcd, 0xf9, 0xaf, 0xfe, 0xf8, 0xf7, 0xbb, 0xc1, 0xbb, 0x58, 0xd5, 0x23, 0xa6, + 0xe4, 0x3d, 0x4a, 0x1d, 0x31, 0x4f, 0xe3, 0x67, 0x08, 0x6e, 0x86, 0x66, 0x24, 0xbc, 0x10, 0x1b, + 0xf0, 0xd2, 0xf4, 0xa5, 0x2c, 0xa6, 0xb4, 0x96, 0xd4, 0x96, 0x38, 0xb5, 0x79, 0x3c, 0xa7, 0xc7, + 0x4d, 0xf9, 0xfa, 0x89, 0xdf, 0x27, 0x4e, 0xf1, 0x7f, 0xa8, 0x33, 0xf7, 0x05, 0x67, 0x15, 0xbc, + 0x9c, 0x14, 0xb9, 0xc7, 0x8c, 0xa6, 0xac, 0x5c, 0x0d, 0x24, 0x59, 0x5b, 0x9c, 0xf5, 0x3e, 0x2e, + 0xc6, 0xb2, 0x76, 0xe4, 0xff, 0x31, 0xfa, 0x49, 0x7b, 0x0b, 0x9d, 0x3e, 0x5e, 0x8e, 0x06, 0x75, + 0x5b, 0x4b, 0x3f, 0xf8, 0x05, 0x92, 0x07, 0x28, 0x3c, 0x22, 0xe0, 0x52, 0xa2, 0xd8, 0x5d, 0xb3, + 0x92, 0xb2, 0x7c, 0x25, 0x8c, 0x4c, 0x78, 0x85, 0x27, 0xac, 0xe1, 0x85, 0x84, 0x84, 0xf9, 0x78, + 0xa5, 0x9f, 0xf0, 0x3f, 0xa7, 0x21, 0xda, 0x81, 0xbe, 0x9b, 0x4c, 0xbb, 0x7b, 0xcc, 0x48, 0xa6, + 0xdd, 0xa3, 0xb1, 0xa7, 0xa6, 0xcd, 0x67, 0x16, 0xfd, 0x84, 0xff, 0x39, 0xc5, 0x3f, 0xf9, 0x27, + 0x35, 0xd8, 0x2a, 0x13, 0x4e, 0x6a, 0x8f, 0xd6, 0x9d, 0x70, 0x52, 0x7b, 0xf5, 0x61, 0xf5, 0x1e, + 0x27, 0x5c, 0xc0, 0xf9, 0x78, 0xc2, 0xf8, 0x67, 0x04, 0xb7, 0xc2, 0x3b, 0x14, 0x2f, 0xa6, 0xdb, + 0xc9, 0x3e, 0x39, 0x2d, 0xad, 0xb9, 0x64, 0x56, 0xe2, 0xcc, 0x16, 0xf0, 0x7c, 0xfa, 0xdd, 0xeb, + 0x5d, 0x79, 0xb8, 0xbb, 0x67, 0xe1, 0x34, 0xba, 0x84, 0xbb, 0xa8, 0x52, 0xba, 0x0a, 0x44, 0x32, + 0xbe, 0xcf, 0x19, 0xcf, 0xe0, 0xe9, 0x78, 0xc6, 0x0e, 0xfe, 0x06, 0xc1, 0x44, 0xa0, 0x3d, 0xe1, + 0xf9, 0xd8, 0x60, 0xa1, 0xce, 0xa6, 0xbc, 0x97, 0xca, 0x36, 0x6d, 0x75, 0x45, 0x7f, 0xc3, 0x67, + 0xfe, 0xac, 0xd6, 0xab, 0x79, 0xe1, 0x0f, 0x63, 0x43, 0xc6, 0xf4, 0x44, 0xe5, 0xa3, 0xd7, 0x40, + 0x4a, 0xea, 0xab, 0x9c, 0xfa, 0x12, 0xd6, 0xa2, 0xa8, 0x1f, 0x49, 0xb4, 0xce, 0x7f, 0x6b, 0xa0, + 0x65, 0x21, 0x2e, 0x7e, 0x89, 0xe0, 0xdd, 0x88, 0x1e, 0x89, 0x57, 0x53, 0xd1, 0xe9, 0xea, 0xc9, + 0xca, 0x07, 0x57, 0xc6, 0xa5, 0xbd, 0x0e, 0xda, 0x49, 0x34, 0x39, 0xb8, 0xec, 0xf5, 0xc5, 0x0d, + 0x7a, 0x76, 0x9e, 0x47, 0xaf, 0xce, 0xf3, 0xe8, 0x9f, 0xf3, 0x3c, 0xfa, 0xf6, 0x22, 0x3f, 0xf0, + 0xea, 0x22, 0x3f, 0xf0, 0xe7, 0x45, 0x7e, 0x00, 0x72, 0x26, 0x8b, 0xa0, 0xb2, 0x8b, 0x1e, 0x6b, + 0x75, 0xd3, 0xdd, 0x3f, 0xac, 0x68, 0x55, 0xd6, 0x0c, 0x84, 0x5b, 0x34, 0x59, 0x30, 0xf8, 0xd3, + 0x76, 0xf8, 0xca, 0x08, 0xff, 0x8d, 0x6a, 0xf9, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb7, 0xc9, + 0x33, 0x53, 0xeb, 0x13, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2527,6 +2537,18 @@ func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Params != nil { + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2943,6 +2965,10 @@ func (m *QueryParamsResponse) Size() (n int) { } var l int _ = l + if m.Params != nil { + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + } return n } @@ -4952,6 +4978,42 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = &Params{} + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/x/exchange/query.pb.gw.go b/x/exchange/query.pb.gw.go index ece714a2dc..94d5ad97ec 100644 --- a/x/exchange/query.pb.gw.go +++ b/x/exchange/query.pb.gw.go @@ -571,26 +571,19 @@ func local_request_Query_QueryValidateCreateMarket_0(ctx context.Context, marsha } +var ( + filter_Query_QueryValidateManageFees_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + func request_Query_QueryValidateManageFees_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryValidateManageFeesRequest var metadata runtime.ServerMetadata - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["market_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - - protoReq.MarketId, err = runtime.Uint32(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryValidateManageFees_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.QueryValidateManageFees(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -602,22 +595,11 @@ func local_request_Query_QueryValidateManageFees_0(ctx context.Context, marshale var protoReq QueryValidateManageFeesRequest var metadata runtime.ServerMetadata - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["market_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - - protoReq.MarketId, err = runtime.Uint32(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryValidateManageFees_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.QueryValidateManageFees(ctx, &protoReq) @@ -1178,7 +1160,7 @@ var ( pattern_Query_QueryValidateCreateMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "validate", "create_market"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryValidateManageFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 2, 6}, []string{"provenance", "exchange", "v1", "market", "market_id", "validate", "manage_fees"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryValidateManageFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "validate", "manage_fees"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( From 22f71297e88f03721c0c7097771da531e91906c1 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 2 Oct 2023 14:42:30 -0600 Subject: [PATCH 222/309] [1658]: Fill in the fields of the ValidateCreateMarket and ValidateManageFees query messages. Create a QueryValidateMarket query. --- docs/proto-docs.md | 776 +++++++++++---------- proto/provenance/exchange/v1/query.proto | 48 +- x/exchange/query.pb.go | 842 ++++++++++++++++++++--- x/exchange/query.pb.gw.go | 214 ++++++ 4 files changed, 1420 insertions(+), 460 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 59c329d941..66d6bf9f5e 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -91,32 +91,6 @@ - [provenance/exchange/v1/genesis.proto](#provenance/exchange/v1/genesis.proto) - [GenesisState](#provenance.exchange.v1.GenesisState) -- [provenance/exchange/v1/query.proto](#provenance/exchange/v1/query.proto) - - [QueryGetAllMarketsRequest](#provenance.exchange.v1.QueryGetAllMarketsRequest) - - [QueryGetAllMarketsResponse](#provenance.exchange.v1.QueryGetAllMarketsResponse) - - [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) - - [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) - - [QueryGetAssetOrdersRequest](#provenance.exchange.v1.QueryGetAssetOrdersRequest) - - [QueryGetAssetOrdersResponse](#provenance.exchange.v1.QueryGetAssetOrdersResponse) - - [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) - - [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) - - [QueryGetMarketRequest](#provenance.exchange.v1.QueryGetMarketRequest) - - [QueryGetMarketResponse](#provenance.exchange.v1.QueryGetMarketResponse) - - [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) - - [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) - - [QueryGetOwnerOrdersRequest](#provenance.exchange.v1.QueryGetOwnerOrdersRequest) - - [QueryGetOwnerOrdersResponse](#provenance.exchange.v1.QueryGetOwnerOrdersResponse) - - [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) - - [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) - - [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) - - [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) - - [QueryValidateCreateMarketRequest](#provenance.exchange.v1.QueryValidateCreateMarketRequest) - - [QueryValidateCreateMarketResponse](#provenance.exchange.v1.QueryValidateCreateMarketResponse) - - [QueryValidateManageFeesRequest](#provenance.exchange.v1.QueryValidateManageFeesRequest) - - [QueryValidateManageFeesResponse](#provenance.exchange.v1.QueryValidateManageFeesResponse) - - - [Query](#provenance.exchange.v1.Query) - - [provenance/exchange/v1/tx.proto](#provenance/exchange/v1/tx.proto) - [MsgCancelOrderRequest](#provenance.exchange.v1.MsgCancelOrderRequest) - [MsgCancelOrderResponse](#provenance.exchange.v1.MsgCancelOrderResponse) @@ -151,6 +125,34 @@ - [Msg](#provenance.exchange.v1.Msg) +- [provenance/exchange/v1/query.proto](#provenance/exchange/v1/query.proto) + - [QueryGetAllMarketsRequest](#provenance.exchange.v1.QueryGetAllMarketsRequest) + - [QueryGetAllMarketsResponse](#provenance.exchange.v1.QueryGetAllMarketsResponse) + - [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) + - [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) + - [QueryGetAssetOrdersRequest](#provenance.exchange.v1.QueryGetAssetOrdersRequest) + - [QueryGetAssetOrdersResponse](#provenance.exchange.v1.QueryGetAssetOrdersResponse) + - [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) + - [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) + - [QueryGetMarketRequest](#provenance.exchange.v1.QueryGetMarketRequest) + - [QueryGetMarketResponse](#provenance.exchange.v1.QueryGetMarketResponse) + - [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) + - [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) + - [QueryGetOwnerOrdersRequest](#provenance.exchange.v1.QueryGetOwnerOrdersRequest) + - [QueryGetOwnerOrdersResponse](#provenance.exchange.v1.QueryGetOwnerOrdersResponse) + - [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) + - [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) + - [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) + - [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) + - [QueryValidateCreateMarketRequest](#provenance.exchange.v1.QueryValidateCreateMarketRequest) + - [QueryValidateCreateMarketResponse](#provenance.exchange.v1.QueryValidateCreateMarketResponse) + - [QueryValidateManageFeesRequest](#provenance.exchange.v1.QueryValidateManageFeesRequest) + - [QueryValidateManageFeesResponse](#provenance.exchange.v1.QueryValidateManageFeesResponse) + - [QueryValidateMarketRequest](#provenance.exchange.v1.QueryValidateMarketRequest) + - [QueryValidateMarketResponse](#provenance.exchange.v1.QueryValidateMarketResponse) + + - [Query](#provenance.exchange.v1.Query) + - [provenance/hold/v1/events.proto](#provenance/hold/v1/events.proto) - [EventHoldAdded](#provenance.hold.v1.EventHoldAdded) - [EventHoldReleased](#provenance.hold.v1.EventHoldReleased) @@ -1904,811 +1906,862 @@ GenesisState is the data that should be loaded into the exchange module during g - +

Top

-## provenance/exchange/v1/query.proto +## provenance/exchange/v1/tx.proto - + -### QueryGetAllMarketsRequest -QueryGetAllMarketsRequest is a request message for the QueryGetAllMarkets endpoint. +### MsgCancelOrderRequest +MsgCancelOrderRequest is a request message for the CancelOrder endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - - +| `signer` | [string](#string) | | signer is the account requesting the order cancelation. It must be either the order owner (e.g. the buyer or seller), the governance module account address, or an account with cancel permission with the market that the order is in. | +| `order_id` | [uint64](#uint64) | | order_id is the id of the order to cancel. | - -### QueryGetAllMarketsResponse -QueryGetAllMarketsResponse is a response message for the QueryGetAllMarkets endpoint. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `markets` | [MarketBrief](#provenance.exchange.v1.MarketBrief) | repeated | markets are a page of the briefs for all markets. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | +### MsgCancelOrderResponse +MsgCancelOrderResponse is a response message for the CancelOrder endpoint. - + -### QueryGetAllOrdersRequest -QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint. +### MsgCreateAskRequest +MsgCreateAskRequest is a request message for the CreateAsk endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | +| `ask_order` | [AskOrder](#provenance.exchange.v1.AskOrder) | | ask_order is the details of the order being created. | +| `order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | order_creation_fee is the fee that is being paid to create this order. | - + -### QueryGetAllOrdersResponse -QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoint. +### MsgCreateAskResponse +MsgCreateAskResponse is a response message for the CreateAsk endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are a page of the all orders. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | +| `order_id` | [uint64](#uint64) | | order_id is the id of the order created. | - + -### QueryGetAssetOrdersRequest -QueryGetAssetOrdersRequest is a request message for the QueryGetAssetOrders endpoint. +### MsgCreateBidRequest +MsgCreateBidRequest is a request message for the CreateBid endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `asset` | [string](#string) | | asset is the denom of assets to get orders for. | -| `order_type` | [string](#string) | | order_type is optional and can limit orders to only "ask" or "bid" orders. | -| `after_order_id` | [uint64](#uint64) | | after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | +| `bid_order` | [BidOrder](#provenance.exchange.v1.BidOrder) | | bid_order is the details of the order being created. | +| `order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | order_creation_fee is the fee that is being paid to create this order. | - + -### QueryGetAssetOrdersResponse -QueryGetAssetOrdersResponse is a response message for the QueryGetAssetOrders endpoint. +### MsgCreateBidResponse +MsgCreateBidResponse is a response message for the CreateBid endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are a page of the orders for the provided asset. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | +| `order_id` | [uint64](#uint64) | | order_id is the id of the order created. | - + -### QueryGetMarketOrdersRequest -QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. +### MsgFillAsksRequest +MsgFillAsksRequest is a request message for the FillAsks endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `market_id` | [uint32](#uint32) | | market_id is the id of the market to get all the orders for. | -| `order_type` | [string](#string) | | order_type is optional and can limit orders to only "ask" or "bid" orders. | -| `after_order_id` | [uint64](#uint64) | | after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - - +| `buyer` | [string](#string) | | buyer is the address of the account attempting to buy some assets. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market with the asks to fill. All ask orders being filled must be in this market. | +| `total_price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | total_price is the total amount being spent on some assets. It must be the sum of all ask order prices. | +| `ask_order_ids` | [uint64](#uint64) | repeated | ask_order_ids are the ids of the ask orders that you are trying to fill. All ids must be for ask orders, and must be in the same market as the market_id. | +| `buyer_settlement_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) for this settlement. | +| `bid_order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | bid_order_creation_fee is the fee that is being paid to create this order (which is immediately then settled). | - -### QueryGetMarketOrdersResponse -QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders endpoint. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are a page of the orders in the provided market. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | +### MsgFillAsksResponse +MsgFillAsksResponse is a response message for the FillAsks endpoint. - + -### QueryGetMarketRequest -QueryGetMarketRequest is a request message for the QueryGetMarket endpoint. +### MsgFillBidsRequest +MsgFillBidsRequest is a request message for the FillBids endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `market_id` | [uint32](#uint32) | | market_id is the id of the market to look up. | - - +| `seller` | [string](#string) | | seller is the address of the account with the assets to sell. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market with the bids to fill. All bid orders being filled must be in this market. | +| `total_assets` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | total_assets are the things that the seller wishes to sell. It must be the sum of all bid order assets. | +| `bid_order_ids` | [uint64](#uint64) | repeated | bid_order_ids are the ids of the bid orders that you are trying to fill. All ids must be for bid orders, and must be in the same market as the market_id. | +| `seller_settlement_flat_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | seller_settlement_flat_fee is the flat fee for sellers that will be charged for this settlement. | +| `ask_order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | ask_order_creation_fee is the fee that is being paid to create this order (which is immediately then settled). | - -### QueryGetMarketResponse -QueryGetMarketResponse is a response message for the QueryGetMarket endpoint. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `address` | [string](#string) | | address is the bech32 address string of this market's account. | -| `market` | [Market](#provenance.exchange.v1.Market) | | market is all information and details of the market. | +### MsgFillBidsResponse +MsgFillBidsResponse is a response message for the FillBids endpoint. - + -### QueryGetOrderRequest -QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. +### MsgGovCreateMarketRequest +MsgGovCreateMarketRequest is a request message for the GovCreateMarket endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `order_id` | [uint64](#uint64) | | order_id is the id of the order to look up. | - - +| `authority` | [string](#string) | | authority should be the governance module account address. | +| `market` | [Market](#provenance.exchange.v1.Market) | | market is the initial market configuration. If the market_id is 0, the next available market_id will be used (once voting ends). If it is not zero, it must not yet be in use when the voting period ends. | - -### QueryGetOrderResponse -QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `order` | [Order](#provenance.exchange.v1.Order) | | order is the requested order. | +### MsgGovCreateMarketResponse +MsgGovCreateMarketResponse is a response message for the GovCreateMarket endpoint. - + -### QueryGetOwnerOrdersRequest -QueryGetOwnerOrdersRequest is a request message for the QueryGetOwnerOrders endpoint. +### MsgGovManageFeesRequest +MsgGovManageFeesRequest is a request message for the GovManageFees endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `owner` | [string](#string) | | owner is the bech32 address string of the owner to get the orders for. | -| `order_type` | [string](#string) | | order_type is optional and can limit orders to only "ask" or "bid" orders. | -| `after_order_id` | [uint64](#uint64) | | after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - - +| `authority` | [string](#string) | | authority should be the governance module account address. | +| `market_id` | [uint32](#uint32) | | market_id is the market id that will get these fee updates. | +| `add_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_ask_flat are the create-ask flat fee options to add. | +| `remove_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_ask_flat are the create-ask flat fee options to remove. | +| `add_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_bid_flat are the create-bid flat fee options to add. | +| `remove_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_bid_flat are the create-bid flat fee options to remove. | +| `add_fee_seller_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_seller_settlement_flat are the seller settlement flat fee options to add. | +| `remove_fee_seller_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_seller_settlement_flat are the seller settlement flat fee options to remove. | +| `add_fee_seller_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | add_fee_seller_settlement_ratios are the seller settlement fee ratios to add. | +| `remove_fee_seller_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | remove_fee_seller_settlement_ratios are the seller settlement fee ratios to remove. | +| `add_fee_buyer_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_buyer_settlement_flat are the buyer settlement flat fee options to add. | +| `remove_fee_buyer_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_buyer_settlement_flat are the buyer settlement flat fee options to remove. | +| `add_fee_buyer_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | add_fee_buyer_settlement_ratios are the buyer settlement fee ratios to add. | +| `remove_fee_buyer_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | remove_fee_buyer_settlement_ratios are the buyer settlement fee ratios to remove. | - -### QueryGetOwnerOrdersResponse -QueryGetOwnerOrdersResponse is a response message for the QueryGetOwnerOrders endpoint. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are a page of the orders for the provided address. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | +### MsgGovManageFeesResponse +MsgGovManageFeesResponse is a response message for the GovManageFees endpoint. - + -### QueryOrderFeeCalcRequest -QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint. -Exactly one of ask_order or bid_order must be provided. +### MsgGovUpdateParamsRequest +MsgGovUpdateParamsRequest is a request message for the GovUpdateParams endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `ask_order` | [AskOrder](#provenance.exchange.v1.AskOrder) | | ask_order is the ask order to calculate the fees for. | -| `bid_order` | [BidOrder](#provenance.exchange.v1.BidOrder) | | bid_order is the bid order to calculate the fees for. | - - +| `authority` | [string](#string) | | authority should be the governance module account address. | +| `params` | [Params](#provenance.exchange.v1.Params) | | params are the new param values to set | - -### QueryOrderFeeCalcResponse -QueryOrderFeeCalcResponse is a response message for the QueryOrderFeeCalc endpoint. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `creation_fee_options` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | creation_fee_options are the order creation flat fee options available for creating the provided order. If it's empty, no order creation fee is required. When creating the order, you should include exactly one of these. | -| `settlement_flat_fee_options` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | settlement_flat_fee_options are the settlement flat fee options available for the provided order. If it's empty, no settlement flat fee is required. When creating an order, you should include exactly one of these in the settlement fees field. | -| `settlement_ratio_fee_options` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | settlement_ratio_fee_options are the settlement ratio fee options available for the provided order. If it's empty, no settlement ratio fee is required. +### MsgGovUpdateParamsResponse +MsgGovUpdateParamsResponse is a response message for the GovUpdateParams endpoint. -If the provided order was a bid order, you should include exactly one of these in the settlement fees field. If the flat and ratio options you've chose have the same denom, a single entry should be included with their sum. -If the provided order was an ask order, these are purely informational and represent how much will be removed from your price if it settles at that price. If it settles for more, the actual amount will probably be larger. | + +### MsgMarketManagePermissionsRequest +MsgMarketManagePermissionsRequest is a request message for the MarketManagePermissions endpoint. - -### QueryParamsRequest -QueryParamsRequest is a request message for the QueryParams endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `admin` | [string](#string) | | admin is the account with "permissions" permission requesting this change. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | +| `revoke_all` | [string](#string) | repeated | revoke_all are addresses that should have all their permissions revoked. | +| `to_revoke` | [AccessGrant](#provenance.exchange.v1.AccessGrant) | repeated | to_revoke are the specific permissions to remove for addresses. | +| `to_grant` | [AccessGrant](#provenance.exchange.v1.AccessGrant) | repeated | to_grant are the permissions to grant to addresses. | - + -### QueryParamsResponse -QueryParamsResponse is a response message for the QueryParams endpoint. +### MsgMarketManagePermissionsResponse +MsgMarketManagePermissionsResponse is a response message for the MarketManagePermissions endpoint. -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `params` | [Params](#provenance.exchange.v1.Params) | | params are the exchange module parameter values. | + +### MsgMarketManageReqAttrsRequest +MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs endpoint. - -### QueryValidateCreateMarketRequest -QueryValidateCreateMarketRequest is a request message for the QueryValidateCreateMarket endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `admin` | [string](#string) | | admin is the account with "attributes" permission requesting this change. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | +| `create_ask_to_add` | [string](#string) | repeated | create_ask_to_add are the attributes that should now also be required to create an ask order. | +| `create_ask_to_remove` | [string](#string) | repeated | create_ask_to_add are the attributes that should no longer be required to create an ask order. | +| `create_bid_to_add` | [string](#string) | repeated | create_ask_to_add are the attributes that should now also be required to create a bid order. | +| `create_bid_to_remove` | [string](#string) | repeated | create_ask_to_add are the attributes that should no longer be required to create a bid order. | - + -### QueryValidateCreateMarketResponse -QueryValidateCreateMarketResponse is a response message for the QueryValidateCreateMarket endpoint. +### MsgMarketManageReqAttrsResponse +MsgMarketManageReqAttrsResponse is a response message for the MarketManageReqAttrs endpoint. - + -### QueryValidateManageFeesRequest -QueryValidateManageFeesRequest is a request message for the QueryValidateManageFees endpoint. +### MsgMarketSettleRequest +MsgMarketSettleRequest is a request message for the MarketSettle endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `market_id` | [uint32](#uint32) | | TODO[1658]: QueryValidateManageFeesRequest | +| `admin` | [string](#string) | | admin is the account with "settle" permission requesting this settlement. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | +| `ask_order_ids` | [uint64](#uint64) | repeated | ask_order_ids are the ask orders being filled. | +| `bid_order_ids` | [uint64](#uint64) | repeated | bid_order_ids are the bid orders being filled. | +| `expect_partial` | [bool](#bool) | | expect_partial is whether to expect an order to only be partially filled. Set to true to indicate that either the last ask order, or last bid order will be partially filled by this settlement. Set to false to indicate that all provided orders will be filled in full during this settlement. | - + -### QueryValidateManageFeesResponse -QueryValidateManageFeesResponse is a response message for the QueryValidateManageFees endpoint. +### MsgMarketSettleResponse +MsgMarketSettleResponse is a response message for the MarketSettle endpoint. - - + - +### MsgMarketUpdateDetailsRequest +MsgMarketUpdateDetailsRequest is a request message for the MarketUpdateDetails endpoint. - +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `admin` | [string](#string) | | admin is the account with "update" permission requesting this change. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | +| `market_details` | [MarketDetails](#provenance.exchange.v1.MarketDetails) | | market_details is some information about this market. | -### Query -Query is the service for exchange module's query endpoints. -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `QueryOrderFeeCalc` | [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) | [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) | QueryOrderFeeCalc calculates the fees that will be associated with the provided order. | GET|/provenance/exchange/v1/fees/order| -| `QueryGetOrder` | [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) | [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) | QueryGetOrder looks up an order by id. | GET|/provenance/exchange/v1/order/{order_id}| -| `QueryGetMarketOrders` | [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) | [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) | QueryGetMarketOrders looks up the orders in a market. | GET|/provenance/exchange/v1/orders/market/{market_id}GET|/provenance/exchange/v1/market/{market_id}/orders| -| `QueryGetOwnerOrders` | [QueryGetOwnerOrdersRequest](#provenance.exchange.v1.QueryGetOwnerOrdersRequest) | [QueryGetOwnerOrdersResponse](#provenance.exchange.v1.QueryGetOwnerOrdersResponse) | QueryGetOwnerOrders looks up the orders from the provided owner address. | GET|/provenance/exchange/v1/orders/owner/{owner}| -| `QueryGetAssetOrders` | [QueryGetAssetOrdersRequest](#provenance.exchange.v1.QueryGetAssetOrdersRequest) | [QueryGetAssetOrdersResponse](#provenance.exchange.v1.QueryGetAssetOrdersResponse) | QueryGetAssetOrders looks up the orders for a specific asset denom. | GET|/provenance/exchange/v1/orders/asset/{asset}| -| `QueryGetAllOrders` | [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) | [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) | QueryGetAllOrders gets all orders in the exchange module. | GET|/provenance/exchange/v1/orders| -| `QueryGetMarket` | [QueryGetMarketRequest](#provenance.exchange.v1.QueryGetMarketRequest) | [QueryGetMarketResponse](#provenance.exchange.v1.QueryGetMarketResponse) | QueryGetMarket returns all the information and details about a market. | GET|/provenance/exchange/v1/market/{market_id}| -| `QueryGetAllMarkets` | [QueryGetAllMarketsRequest](#provenance.exchange.v1.QueryGetAllMarketsRequest) | [QueryGetAllMarketsResponse](#provenance.exchange.v1.QueryGetAllMarketsResponse) | QueryGetAllMarkets returns brief information about each market. | GET|/provenance/exchange/v1/markets| -| `QueryParams` | [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) | [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) | QueryParams returns the exchange module parameters. | GET|/provenance/exchange/v1/params| -| `QueryValidateCreateMarket` | [QueryValidateCreateMarketRequest](#provenance.exchange.v1.QueryValidateCreateMarketRequest) | [QueryValidateCreateMarketResponse](#provenance.exchange.v1.QueryValidateCreateMarketResponse) | QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. | GET|/provenance/exchange/v1/validate/create_market| -| `QueryValidateManageFees` | [QueryValidateManageFeesRequest](#provenance.exchange.v1.QueryValidateManageFeesRequest) | [QueryValidateManageFeesResponse](#provenance.exchange.v1.QueryValidateManageFeesResponse) | QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. | GET|/provenance/exchange/v1/validate/manage_fees| - - -

Top

+ -## provenance/exchange/v1/tx.proto +### MsgMarketUpdateDetailsResponse +MsgMarketUpdateDetailsResponse is a response message for the MarketUpdateDetails endpoint. - -### MsgCancelOrderRequest -MsgCancelOrderRequest is a request message for the CancelOrder endpoint. + + + + +### MsgMarketUpdateEnabledRequest +MsgMarketUpdateEnabledRequest is a request message for the MarketUpdateEnabled endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `signer` | [string](#string) | | signer is the account requesting the order cancelation. It must be either the order owner (e.g. the buyer or seller), the governance module account address, or an account with cancel permission with the market that the order is in. | -| `order_id` | [uint64](#uint64) | | order_id is the id of the order to cancel. | +| `admin` | [string](#string) | | admin is the account with "update" permission requesting this change. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | +| `accepting_orders` | [bool](#bool) | | accepting_orders is whether this market is allowing orders to be created for it. | - + -### MsgCancelOrderResponse -MsgCancelOrderResponse is a response message for the CancelOrder endpoint. +### MsgMarketUpdateEnabledResponse +MsgMarketUpdateEnabledResponse is a response message for the MarketUpdateEnabled endpoint. - + -### MsgCreateAskRequest -MsgCreateAskRequest is a request message for the CreateAsk endpoint. +### MsgMarketUpdateUserSettleRequest +MsgMarketUpdateUserSettleRequest is a request message for the MarketUpdateUserSettle endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `ask_order` | [AskOrder](#provenance.exchange.v1.AskOrder) | | ask_order is the details of the order being created. | -| `order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | order_creation_fee is the fee that is being paid to create this order. | +| `admin` | [string](#string) | | admin is the account with "update" permission requesting this change. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | +| `allow_user_settlement` | [bool](#bool) | | allow_user_settlement is whether this market allows users to initiate their own settlements. For example, the FillBids and FillAsks endpoints are available if and only if this is true. The MarketSettle endpoint is only available to market actors regardless of the value of this field. | - + + +### MsgMarketUpdateUserSettleResponse +MsgMarketUpdateUserSettleResponse is a response message for the MarketUpdateUserSettle endpoint. -### MsgCreateAskResponse -MsgCreateAskResponse is a response message for the CreateAsk endpoint. + + + + + + + +### MsgMarketWithdrawRequest +MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `order_id` | [uint64](#uint64) | | order_id is the id of the order created. | +| `admin` | [string](#string) | | admin is the account with withdraw permission requesting the withdrawal. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to withdraw from. | +| `to_address` | [string](#string) | | to_address is the address that will receive the funds. | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | amount is the funds to withdraw. | - + -### MsgCreateBidRequest -MsgCreateBidRequest is a request message for the CreateBid endpoint. +### MsgMarketWithdrawResponse +MsgMarketWithdrawResponse is a response message for the MarketWithdraw endpoint. -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `bid_order` | [BidOrder](#provenance.exchange.v1.BidOrder) | | bid_order is the details of the order being created. | -| `order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | order_creation_fee is the fee that is being paid to create this order. | + + + - -### MsgCreateBidResponse -MsgCreateBidResponse is a response message for the CreateBid endpoint. + +### Msg +Msg is the service for exchange module's tx endpoints. -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `order_id` | [uint64](#uint64) | | order_id is the id of the order created. | +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `CreateAsk` | [MsgCreateAskRequest](#provenance.exchange.v1.MsgCreateAskRequest) | [MsgCreateAskResponse](#provenance.exchange.v1.MsgCreateAskResponse) | CreateAsk creates an ask order (to sell something you own). | | +| `CreateBid` | [MsgCreateBidRequest](#provenance.exchange.v1.MsgCreateBidRequest) | [MsgCreateBidResponse](#provenance.exchange.v1.MsgCreateBidResponse) | CreateBid creates a bid order (to buy something you want). | | +| `CancelOrder` | [MsgCancelOrderRequest](#provenance.exchange.v1.MsgCancelOrderRequest) | [MsgCancelOrderResponse](#provenance.exchange.v1.MsgCancelOrderResponse) | CancelOrder cancels an order. | | +| `FillBids` | [MsgFillBidsRequest](#provenance.exchange.v1.MsgFillBidsRequest) | [MsgFillBidsResponse](#provenance.exchange.v1.MsgFillBidsResponse) | FillBids uses the assets in your account to fulfill one or more bids (similar to a fill-or-cancel ask). | | +| `FillAsks` | [MsgFillAsksRequest](#provenance.exchange.v1.MsgFillAsksRequest) | [MsgFillAsksResponse](#provenance.exchange.v1.MsgFillAsksResponse) | FillAsks uses the funds in your account to fulfill one or more asks (similar to a fill-or-cancel bid). | | +| `MarketSettle` | [MsgMarketSettleRequest](#provenance.exchange.v1.MsgMarketSettleRequest) | [MsgMarketSettleResponse](#provenance.exchange.v1.MsgMarketSettleResponse) | MarketSettle is a market endpoint to trigger the settlement of orders. | | +| `MarketWithdraw` | [MsgMarketWithdrawRequest](#provenance.exchange.v1.MsgMarketWithdrawRequest) | [MsgMarketWithdrawResponse](#provenance.exchange.v1.MsgMarketWithdrawResponse) | MarketWithdraw is a market endpoint to withdraw fees that have been collected. | | +| `MarketUpdateDetails` | [MsgMarketUpdateDetailsRequest](#provenance.exchange.v1.MsgMarketUpdateDetailsRequest) | [MsgMarketUpdateDetailsResponse](#provenance.exchange.v1.MsgMarketUpdateDetailsResponse) | MarketUpdateDetails is a market endpoint to update its details. | | +| `MarketUpdateEnabled` | [MsgMarketUpdateEnabledRequest](#provenance.exchange.v1.MsgMarketUpdateEnabledRequest) | [MsgMarketUpdateEnabledResponse](#provenance.exchange.v1.MsgMarketUpdateEnabledResponse) | MarketUpdateEnabled is a market endpoint to update whether its accepting orders. | | +| `MarketUpdateUserSettle` | [MsgMarketUpdateUserSettleRequest](#provenance.exchange.v1.MsgMarketUpdateUserSettleRequest) | [MsgMarketUpdateUserSettleResponse](#provenance.exchange.v1.MsgMarketUpdateUserSettleResponse) | MarketUpdateUserSettle is a market endpoint to update whether it allows user-initiated settlement. | | +| `MarketManagePermissions` | [MsgMarketManagePermissionsRequest](#provenance.exchange.v1.MsgMarketManagePermissionsRequest) | [MsgMarketManagePermissionsResponse](#provenance.exchange.v1.MsgMarketManagePermissionsResponse) | MarketManagePermissions is a market endpoint to manage a market's user permissions. | | +| `MarketManageReqAttrs` | [MsgMarketManageReqAttrsRequest](#provenance.exchange.v1.MsgMarketManageReqAttrsRequest) | [MsgMarketManageReqAttrsResponse](#provenance.exchange.v1.MsgMarketManageReqAttrsResponse) | MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. | | +| `GovCreateMarket` | [MsgGovCreateMarketRequest](#provenance.exchange.v1.MsgGovCreateMarketRequest) | [MsgGovCreateMarketResponse](#provenance.exchange.v1.MsgGovCreateMarketResponse) | GovCreateMarket is a governance proposal endpoint for creating a market. | | +| `GovManageFees` | [MsgGovManageFeesRequest](#provenance.exchange.v1.MsgGovManageFeesRequest) | [MsgGovManageFeesResponse](#provenance.exchange.v1.MsgGovManageFeesResponse) | GovManageFees is a governance proposal endpoint for updating a market's fees. | | +| `GovUpdateParams` | [MsgGovUpdateParamsRequest](#provenance.exchange.v1.MsgGovUpdateParamsRequest) | [MsgGovUpdateParamsResponse](#provenance.exchange.v1.MsgGovUpdateParamsResponse) | GovUpdateParams is a governance proposal endpoint for updating the exchange module's params. | | + + +

Top

+## provenance/exchange/v1/query.proto - -### MsgFillAsksRequest -MsgFillAsksRequest is a request message for the FillAsks endpoint. + + + +### QueryGetAllMarketsRequest +QueryGetAllMarketsRequest is a request message for the QueryGetAllMarkets endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `buyer` | [string](#string) | | buyer is the address of the account attempting to buy some assets. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market with the asks to fill. All ask orders being filled must be in this market. | -| `total_price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | total_price is the total amount being spent on some assets. It must be the sum of all ask order prices. | -| `ask_order_ids` | [uint64](#uint64) | repeated | ask_order_ids are the ids of the ask orders that you are trying to fill. All ids must be for ask orders, and must be in the same market as the market_id. | -| `buyer_settlement_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) for this settlement. | -| `bid_order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | bid_order_creation_fee is the fee that is being paid to create this order (which is immediately then settled). | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - + -### MsgFillAsksResponse -MsgFillAsksResponse is a response message for the FillAsks endpoint. +### QueryGetAllMarketsResponse +QueryGetAllMarketsResponse is a response message for the QueryGetAllMarkets endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `markets` | [MarketBrief](#provenance.exchange.v1.MarketBrief) | repeated | markets are a page of the briefs for all markets. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | - -### MsgFillBidsRequest -MsgFillBidsRequest is a request message for the FillBids endpoint. + + + +### QueryGetAllOrdersRequest +QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `seller` | [string](#string) | | seller is the address of the account with the assets to sell. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market with the bids to fill. All bid orders being filled must be in this market. | -| `total_assets` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | total_assets are the things that the seller wishes to sell. It must be the sum of all bid order assets. | -| `bid_order_ids` | [uint64](#uint64) | repeated | bid_order_ids are the ids of the bid orders that you are trying to fill. All ids must be for bid orders, and must be in the same market as the market_id. | -| `seller_settlement_flat_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | seller_settlement_flat_fee is the flat fee for sellers that will be charged for this settlement. | -| `ask_order_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | ask_order_creation_fee is the fee that is being paid to create this order (which is immediately then settled). | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - + -### MsgFillBidsResponse -MsgFillBidsResponse is a response message for the FillBids endpoint. +### QueryGetAllOrdersResponse +QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are a page of the all orders. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | - -### MsgGovCreateMarketRequest -MsgGovCreateMarketRequest is a request message for the GovCreateMarket endpoint. + + + +### QueryGetAssetOrdersRequest +QueryGetAssetOrdersRequest is a request message for the QueryGetAssetOrders endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `authority` | [string](#string) | | authority should be the governance module account address. | -| `market` | [Market](#provenance.exchange.v1.Market) | | market is the initial market configuration. If the market_id is 0, the next available market_id will be used (once voting ends). If it is not zero, it must not yet be in use when the voting period ends. | +| `asset` | [string](#string) | | asset is the denom of assets to get orders for. | +| `order_type` | [string](#string) | | order_type is optional and can limit orders to only "ask" or "bid" orders. | +| `after_order_id` | [uint64](#uint64) | | after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - + -### MsgGovCreateMarketResponse -MsgGovCreateMarketResponse is a response message for the GovCreateMarket endpoint. +### QueryGetAssetOrdersResponse +QueryGetAssetOrdersResponse is a response message for the QueryGetAssetOrders endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are a page of the orders for the provided asset. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | - -### MsgGovManageFeesRequest -MsgGovManageFeesRequest is a request message for the GovManageFees endpoint. + + + +### QueryGetMarketOrdersRequest +QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `authority` | [string](#string) | | authority should be the governance module account address. | -| `market_id` | [uint32](#uint32) | | market_id is the market id that will get these fee updates. | -| `add_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_ask_flat are the create-ask flat fee options to add. | -| `remove_fee_create_ask_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_ask_flat are the create-ask flat fee options to remove. | -| `add_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_create_bid_flat are the create-bid flat fee options to add. | -| `remove_fee_create_bid_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_create_bid_flat are the create-bid flat fee options to remove. | -| `add_fee_seller_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_seller_settlement_flat are the seller settlement flat fee options to add. | -| `remove_fee_seller_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_seller_settlement_flat are the seller settlement flat fee options to remove. | -| `add_fee_seller_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | add_fee_seller_settlement_ratios are the seller settlement fee ratios to add. | -| `remove_fee_seller_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | remove_fee_seller_settlement_ratios are the seller settlement fee ratios to remove. | -| `add_fee_buyer_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | add_fee_buyer_settlement_flat are the buyer settlement flat fee options to add. | -| `remove_fee_buyer_settlement_flat` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | remove_fee_buyer_settlement_flat are the buyer settlement flat fee options to remove. | -| `add_fee_buyer_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | add_fee_buyer_settlement_ratios are the buyer settlement fee ratios to add. | -| `remove_fee_buyer_settlement_ratios` | [FeeRatio](#provenance.exchange.v1.FeeRatio) | repeated | remove_fee_buyer_settlement_ratios are the buyer settlement fee ratios to remove. | +| `market_id` | [uint32](#uint32) | | market_id is the id of the market to get all the orders for. | +| `order_type` | [string](#string) | | order_type is optional and can limit orders to only "ask" or "bid" orders. | +| `after_order_id` | [uint64](#uint64) | | after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - + -### MsgGovManageFeesResponse -MsgGovManageFeesResponse is a response message for the GovManageFees endpoint. +### QueryGetMarketOrdersResponse +QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are a page of the orders in the provided market. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | - -### MsgGovUpdateParamsRequest -MsgGovUpdateParamsRequest is a request message for the GovUpdateParams endpoint. + + + +### QueryGetMarketRequest +QueryGetMarketRequest is a request message for the QueryGetMarket endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `authority` | [string](#string) | | authority should be the governance module account address. | -| `params` | [Params](#provenance.exchange.v1.Params) | | params are the new param values to set | +| `market_id` | [uint32](#uint32) | | market_id is the id of the market to look up. | - + -### MsgGovUpdateParamsResponse -MsgGovUpdateParamsResponse is a response message for the GovUpdateParams endpoint. +### QueryGetMarketResponse +QueryGetMarketResponse is a response message for the QueryGetMarket endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | address is the bech32 address string of this market's account. | +| `market` | [Market](#provenance.exchange.v1.Market) | | market is all information and details of the market. | - + -### MsgMarketManagePermissionsRequest -MsgMarketManagePermissionsRequest is a request message for the MarketManagePermissions endpoint. +### QueryGetOrderRequest +QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `admin` | [string](#string) | | admin is the account with "permissions" permission requesting this change. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | -| `revoke_all` | [string](#string) | repeated | revoke_all are addresses that should have all their permissions revoked. | -| `to_revoke` | [AccessGrant](#provenance.exchange.v1.AccessGrant) | repeated | to_revoke are the specific permissions to remove for addresses. | -| `to_grant` | [AccessGrant](#provenance.exchange.v1.AccessGrant) | repeated | to_grant are the permissions to grant to addresses. | +| `order_id` | [uint64](#uint64) | | order_id is the id of the order to look up. | - + -### MsgMarketManagePermissionsResponse -MsgMarketManagePermissionsResponse is a response message for the MarketManagePermissions endpoint. +### QueryGetOrderResponse +QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order` | [Order](#provenance.exchange.v1.Order) | | order is the requested order. | - -### MsgMarketManageReqAttrsRequest -MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs endpoint. + + + +### QueryGetOwnerOrdersRequest +QueryGetOwnerOrdersRequest is a request message for the QueryGetOwnerOrders endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `admin` | [string](#string) | | admin is the account with "attributes" permission requesting this change. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | -| `create_ask_to_add` | [string](#string) | repeated | create_ask_to_add are the attributes that should now also be required to create an ask order. | -| `create_ask_to_remove` | [string](#string) | repeated | create_ask_to_add are the attributes that should no longer be required to create an ask order. | -| `create_bid_to_add` | [string](#string) | repeated | create_ask_to_add are the attributes that should now also be required to create a bid order. | -| `create_bid_to_remove` | [string](#string) | repeated | create_ask_to_add are the attributes that should no longer be required to create a bid order. | +| `owner` | [string](#string) | | owner is the bech32 address string of the owner to get the orders for. | +| `order_type` | [string](#string) | | order_type is optional and can limit orders to only "ask" or "bid" orders. | +| `after_order_id` | [uint64](#uint64) | | after_order_id is a minimum (exclusive) order id. All results will be strictly greater than this. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - + -### MsgMarketManageReqAttrsResponse -MsgMarketManageReqAttrsResponse is a response message for the MarketManageReqAttrs endpoint. +### QueryGetOwnerOrdersResponse +QueryGetOwnerOrdersResponse is a response message for the QueryGetOwnerOrders endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are a page of the orders for the provided address. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination is the resulting pagination parameters. | - -### MsgMarketSettleRequest -MsgMarketSettleRequest is a request message for the MarketSettle endpoint. + + + +### QueryOrderFeeCalcRequest +QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint. +Exactly one of ask_order or bid_order must be provided. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `admin` | [string](#string) | | admin is the account with "settle" permission requesting this settlement. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | -| `ask_order_ids` | [uint64](#uint64) | repeated | ask_order_ids are the ask orders being filled. | -| `bid_order_ids` | [uint64](#uint64) | repeated | bid_order_ids are the bid orders being filled. | -| `expect_partial` | [bool](#bool) | | expect_partial is whether to expect an order to only be partially filled. Set to true to indicate that either the last ask order, or last bid order will be partially filled by this settlement. Set to false to indicate that all provided orders will be filled in full during this settlement. | +| `ask_order` | [AskOrder](#provenance.exchange.v1.AskOrder) | | ask_order is the ask order to calculate the fees for. | +| `bid_order` | [BidOrder](#provenance.exchange.v1.BidOrder) | | bid_order is the bid order to calculate the fees for. | - + -### MsgMarketSettleResponse -MsgMarketSettleResponse is a response message for the MarketSettle endpoint. +### QueryOrderFeeCalcResponse +QueryOrderFeeCalcResponse is a response message for the QueryOrderFeeCalc endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `creation_fee_options` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | creation_fee_options are the order creation flat fee options available for creating the provided order. If it's empty, no order creation fee is required. When creating the order, you should include exactly one of these. | +| `settlement_flat_fee_options` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | settlement_flat_fee_options are the settlement flat fee options available for the provided order. If it's empty, no settlement flat fee is required. When creating an order, you should include exactly one of these in the settlement fees field. | +| `settlement_ratio_fee_options` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | settlement_ratio_fee_options are the settlement ratio fee options available for the provided order. If it's empty, no settlement ratio fee is required. +If the provided order was a bid order, you should include exactly one of these in the settlement fees field. If the flat and ratio options you've chose have the same denom, a single entry should be included with their sum. +If the provided order was an ask order, these are purely informational and represent how much will be removed from your price if it settles at that price. If it settles for more, the actual amount will probably be larger. | - -### MsgMarketUpdateDetailsRequest -MsgMarketUpdateDetailsRequest is a request message for the MarketUpdateDetails endpoint. -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `admin` | [string](#string) | | admin is the account with "update" permission requesting this change. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | -| `market_details` | [MarketDetails](#provenance.exchange.v1.MarketDetails) | | market_details is some information about this market. | + +### QueryParamsRequest +QueryParamsRequest is a request message for the QueryParams endpoint. - -### MsgMarketUpdateDetailsResponse -MsgMarketUpdateDetailsResponse is a response message for the MarketUpdateDetails endpoint. + +### QueryParamsResponse +QueryParamsResponse is a response message for the QueryParams endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#provenance.exchange.v1.Params) | | params are the exchange module parameter values. | - -### MsgMarketUpdateEnabledRequest -MsgMarketUpdateEnabledRequest is a request message for the MarketUpdateEnabled endpoint. + + + + + +### QueryValidateCreateMarketRequest +QueryValidateCreateMarketRequest is a request message for the QueryValidateCreateMarket endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `admin` | [string](#string) | | admin is the account with "update" permission requesting this change. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | -| `accepting_orders` | [bool](#bool) | | accepting_orders is whether this market is allowing orders to be created for it. | +| `create_market_request` | [MsgGovCreateMarketRequest](#provenance.exchange.v1.MsgGovCreateMarketRequest) | | create_market_request is the request to run validation on. | - + -### MsgMarketUpdateEnabledResponse -MsgMarketUpdateEnabledResponse is a response message for the MarketUpdateEnabled endpoint. +### QueryValidateCreateMarketResponse +QueryValidateCreateMarketResponse is a response message for the QueryValidateCreateMarket endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `error` | [string](#string) | | error is any problems or inconsistencies in the provided gov prop msg. This goes above and beyond the validation done when actually processing the governance proposal. If an error is returned, and gov_prop_will_pass is true, it means the error is more of an inconsistency that might cause certain aspects of the market to behave unexpectedly. | +| `gov_prop_will_pass` | [bool](#bool) | | gov_prop_will_pass will be true if the the provided msg will be successfully processed at the end of it's voting period (assuming it passes). | - -### MsgMarketUpdateUserSettleRequest -MsgMarketUpdateUserSettleRequest is a request message for the MarketUpdateUserSettle endpoint. + + + +### QueryValidateManageFeesRequest +QueryValidateManageFeesRequest is a request message for the QueryValidateManageFees endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `admin` | [string](#string) | | admin is the account with "update" permission requesting this change. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | -| `allow_user_settlement` | [bool](#bool) | | allow_user_settlement is whether this market allows users to initiate their own settlements. For example, the FillBids and FillAsks endpoints are available if and only if this is true. The MarketSettle endpoint is only available to market actors regardless of the value of this field. | +| `manage_fees_request` | [MsgGovManageFeesRequest](#provenance.exchange.v1.MsgGovManageFeesRequest) | | manage_fees_request is the request to run validation on. | - + -### MsgMarketUpdateUserSettleResponse -MsgMarketUpdateUserSettleResponse is a response message for the MarketUpdateUserSettle endpoint. +### QueryValidateManageFeesResponse +QueryValidateManageFeesResponse is a response message for the QueryValidateManageFees endpoint. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `error` | [string](#string) | | error is any problems or inconsistencies in the provided gov prop msg. This goes above and beyond the validation done when actually processing the governance proposal. If an error is returned, and gov_prop_will_pass is true, it means the error is more of an inconsistency that might cause certain aspects of the market to behave unexpectedly. | +| `gov_prop_will_pass` | [bool](#bool) | | gov_prop_will_pass will be true if the the provided msg will be successfully processed at the end of it's voting period (assuming it passes). | - -### MsgMarketWithdrawRequest -MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. + + + +### QueryValidateMarketRequest +QueryValidateMarketRequest is a request message for the QueryValidateMarket endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `admin` | [string](#string) | | admin is the account with withdraw permission requesting the withdrawal. | -| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to withdraw from. | -| `to_address` | [string](#string) | | to_address is the address that will receive the funds. | -| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | amount is the funds to withdraw. | +| `market_id` | [uint32](#uint32) | | market_id is the id of the market to check. | - + -### MsgMarketWithdrawResponse -MsgMarketWithdrawResponse is a response message for the MarketWithdraw endpoint. +### QueryValidateMarketResponse +QueryValidateMarketResponse is a response message for the QueryValidateMarket endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `error` | [string](#string) | | error is any problems or inconsistencies in the provided market. | @@ -2721,28 +2774,25 @@ MsgMarketWithdrawResponse is a response message for the MarketWithdraw endpoint. - + -### Msg -Msg is the service for exchange module's tx endpoints. +### Query +Query is the service for exchange module's query endpoints. | Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | | ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `CreateAsk` | [MsgCreateAskRequest](#provenance.exchange.v1.MsgCreateAskRequest) | [MsgCreateAskResponse](#provenance.exchange.v1.MsgCreateAskResponse) | CreateAsk creates an ask order (to sell something you own). | | -| `CreateBid` | [MsgCreateBidRequest](#provenance.exchange.v1.MsgCreateBidRequest) | [MsgCreateBidResponse](#provenance.exchange.v1.MsgCreateBidResponse) | CreateBid creates a bid order (to buy something you want). | | -| `CancelOrder` | [MsgCancelOrderRequest](#provenance.exchange.v1.MsgCancelOrderRequest) | [MsgCancelOrderResponse](#provenance.exchange.v1.MsgCancelOrderResponse) | CancelOrder cancels an order. | | -| `FillBids` | [MsgFillBidsRequest](#provenance.exchange.v1.MsgFillBidsRequest) | [MsgFillBidsResponse](#provenance.exchange.v1.MsgFillBidsResponse) | FillBids uses the assets in your account to fulfill one or more bids (similar to a fill-or-cancel ask). | | -| `FillAsks` | [MsgFillAsksRequest](#provenance.exchange.v1.MsgFillAsksRequest) | [MsgFillAsksResponse](#provenance.exchange.v1.MsgFillAsksResponse) | FillAsks uses the funds in your account to fulfill one or more asks (similar to a fill-or-cancel bid). | | -| `MarketSettle` | [MsgMarketSettleRequest](#provenance.exchange.v1.MsgMarketSettleRequest) | [MsgMarketSettleResponse](#provenance.exchange.v1.MsgMarketSettleResponse) | MarketSettle is a market endpoint to trigger the settlement of orders. | | -| `MarketWithdraw` | [MsgMarketWithdrawRequest](#provenance.exchange.v1.MsgMarketWithdrawRequest) | [MsgMarketWithdrawResponse](#provenance.exchange.v1.MsgMarketWithdrawResponse) | MarketWithdraw is a market endpoint to withdraw fees that have been collected. | | -| `MarketUpdateDetails` | [MsgMarketUpdateDetailsRequest](#provenance.exchange.v1.MsgMarketUpdateDetailsRequest) | [MsgMarketUpdateDetailsResponse](#provenance.exchange.v1.MsgMarketUpdateDetailsResponse) | MarketUpdateDetails is a market endpoint to update its details. | | -| `MarketUpdateEnabled` | [MsgMarketUpdateEnabledRequest](#provenance.exchange.v1.MsgMarketUpdateEnabledRequest) | [MsgMarketUpdateEnabledResponse](#provenance.exchange.v1.MsgMarketUpdateEnabledResponse) | MarketUpdateEnabled is a market endpoint to update whether its accepting orders. | | -| `MarketUpdateUserSettle` | [MsgMarketUpdateUserSettleRequest](#provenance.exchange.v1.MsgMarketUpdateUserSettleRequest) | [MsgMarketUpdateUserSettleResponse](#provenance.exchange.v1.MsgMarketUpdateUserSettleResponse) | MarketUpdateUserSettle is a market endpoint to update whether it allows user-initiated settlement. | | -| `MarketManagePermissions` | [MsgMarketManagePermissionsRequest](#provenance.exchange.v1.MsgMarketManagePermissionsRequest) | [MsgMarketManagePermissionsResponse](#provenance.exchange.v1.MsgMarketManagePermissionsResponse) | MarketManagePermissions is a market endpoint to manage a market's user permissions. | | -| `MarketManageReqAttrs` | [MsgMarketManageReqAttrsRequest](#provenance.exchange.v1.MsgMarketManageReqAttrsRequest) | [MsgMarketManageReqAttrsResponse](#provenance.exchange.v1.MsgMarketManageReqAttrsResponse) | MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. | | -| `GovCreateMarket` | [MsgGovCreateMarketRequest](#provenance.exchange.v1.MsgGovCreateMarketRequest) | [MsgGovCreateMarketResponse](#provenance.exchange.v1.MsgGovCreateMarketResponse) | GovCreateMarket is a governance proposal endpoint for creating a market. | | -| `GovManageFees` | [MsgGovManageFeesRequest](#provenance.exchange.v1.MsgGovManageFeesRequest) | [MsgGovManageFeesResponse](#provenance.exchange.v1.MsgGovManageFeesResponse) | GovManageFees is a governance proposal endpoint for updating a market's fees. | | -| `GovUpdateParams` | [MsgGovUpdateParamsRequest](#provenance.exchange.v1.MsgGovUpdateParamsRequest) | [MsgGovUpdateParamsResponse](#provenance.exchange.v1.MsgGovUpdateParamsResponse) | GovUpdateParams is a governance proposal endpoint for updating the exchange module's params. | | +| `QueryOrderFeeCalc` | [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) | [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) | QueryOrderFeeCalc calculates the fees that will be associated with the provided order. | GET|/provenance/exchange/v1/fees/order| +| `QueryGetOrder` | [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) | [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) | QueryGetOrder looks up an order by id. | GET|/provenance/exchange/v1/order/{order_id}| +| `QueryGetMarketOrders` | [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) | [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) | QueryGetMarketOrders looks up the orders in a market. | GET|/provenance/exchange/v1/orders/market/{market_id}GET|/provenance/exchange/v1/market/{market_id}/orders| +| `QueryGetOwnerOrders` | [QueryGetOwnerOrdersRequest](#provenance.exchange.v1.QueryGetOwnerOrdersRequest) | [QueryGetOwnerOrdersResponse](#provenance.exchange.v1.QueryGetOwnerOrdersResponse) | QueryGetOwnerOrders looks up the orders from the provided owner address. | GET|/provenance/exchange/v1/orders/owner/{owner}| +| `QueryGetAssetOrders` | [QueryGetAssetOrdersRequest](#provenance.exchange.v1.QueryGetAssetOrdersRequest) | [QueryGetAssetOrdersResponse](#provenance.exchange.v1.QueryGetAssetOrdersResponse) | QueryGetAssetOrders looks up the orders for a specific asset denom. | GET|/provenance/exchange/v1/orders/asset/{asset}| +| `QueryGetAllOrders` | [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) | [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) | QueryGetAllOrders gets all orders in the exchange module. | GET|/provenance/exchange/v1/orders| +| `QueryGetMarket` | [QueryGetMarketRequest](#provenance.exchange.v1.QueryGetMarketRequest) | [QueryGetMarketResponse](#provenance.exchange.v1.QueryGetMarketResponse) | QueryGetMarket returns all the information and details about a market. | GET|/provenance/exchange/v1/market/{market_id}| +| `QueryGetAllMarkets` | [QueryGetAllMarketsRequest](#provenance.exchange.v1.QueryGetAllMarketsRequest) | [QueryGetAllMarketsResponse](#provenance.exchange.v1.QueryGetAllMarketsResponse) | QueryGetAllMarkets returns brief information about each market. | GET|/provenance/exchange/v1/markets| +| `QueryParams` | [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) | [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) | QueryParams returns the exchange module parameters. | GET|/provenance/exchange/v1/params| +| `QueryValidateCreateMarket` | [QueryValidateCreateMarketRequest](#provenance.exchange.v1.QueryValidateCreateMarketRequest) | [QueryValidateCreateMarketResponse](#provenance.exchange.v1.QueryValidateCreateMarketResponse) | QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. | GET|/provenance/exchange/v1/validate/create_market| +| `QueryValidateMarket` | [QueryValidateMarketRequest](#provenance.exchange.v1.QueryValidateMarketRequest) | [QueryValidateMarketResponse](#provenance.exchange.v1.QueryValidateMarketResponse) | QueryValidateMarket checks for any problems with a market's setup. | GET|/provenance/exchange/v1/validate/market/{market_id}GET|/provenance/exchange/v1/market/{market_id}/validate| +| `QueryValidateManageFees` | [QueryValidateManageFeesRequest](#provenance.exchange.v1.QueryValidateManageFeesRequest) | [QueryValidateManageFeesResponse](#provenance.exchange.v1.QueryValidateManageFeesResponse) | QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. | GET|/provenance/exchange/v1/validate/manage_fees| diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 75a935f452..8db771ac42 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -14,6 +14,7 @@ import "gogoproto/gogo.proto"; import "provenance/exchange/v1/market.proto"; import "provenance/exchange/v1/orders.proto"; import "provenance/exchange/v1/params.proto"; +import "provenance/exchange/v1/tx.proto"; // Query is the service for exchange module's query endpoints. service Query { @@ -70,6 +71,14 @@ service Query { option (google.api.http).get = "/provenance/exchange/v1/validate/create_market"; } + // QueryValidateMarket checks for any problems with a market's setup. + rpc QueryValidateMarket(QueryValidateMarketRequest) returns (QueryValidateMarketResponse) { + option (google.api.http) = { + get: "/provenance/exchange/v1/validate/market/{market_id}" + additional_bindings: {get: "/provenance/exchange/v1/market/{market_id}/validate"} + }; + } + // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. rpc QueryValidateManageFees(QueryValidateManageFeesRequest) returns (QueryValidateManageFeesResponse) { option (google.api.http).get = "/provenance/exchange/v1/validate/manage_fees"; @@ -239,19 +248,48 @@ message QueryParamsResponse { // QueryValidateCreateMarketRequest is a request message for the QueryValidateCreateMarket endpoint. message QueryValidateCreateMarketRequest { - // TODO[1658]: QueryValidateCreateMarketRequest + // create_market_request is the request to run validation on. + MsgGovCreateMarketRequest create_market_request = 1; } + // QueryValidateCreateMarketResponse is a response message for the QueryValidateCreateMarket endpoint. message QueryValidateCreateMarketResponse { - // TODO[1658]: QueryValidateCreateMarketResponse + // error is any problems or inconsistencies in the provided gov prop msg. + // This goes above and beyond the validation done when actually processing the governance proposal. + // If an error is returned, and gov_prop_will_pass is true, it means the error is more of an + // inconsistency that might cause certain aspects of the market to behave unexpectedly. + string error = 1; + // gov_prop_will_pass will be true if the the provided msg will be successfully processed at the end of it's voting + // period (assuming it passes). + bool gov_prop_will_pass = 2; +} + +// QueryValidateMarketRequest is a request message for the QueryValidateMarket endpoint. +message QueryValidateMarketRequest { + // market_id is the id of the market to check. + uint32 market_id = 1; +} + +// QueryValidateMarketResponse is a response message for the QueryValidateMarket endpoint. +message QueryValidateMarketResponse { + // error is any problems or inconsistencies in the provided market. + string error = 1; } // QueryValidateManageFeesRequest is a request message for the QueryValidateManageFees endpoint. message QueryValidateManageFeesRequest { - uint32 market_id = 1; - // TODO[1658]: QueryValidateManageFeesRequest + // manage_fees_request is the request to run validation on. + MsgGovManageFeesRequest manage_fees_request = 1; } + // QueryValidateManageFeesResponse is a response message for the QueryValidateManageFees endpoint. message QueryValidateManageFeesResponse { - // TODO[1658]: QueryValidateManageFeesResponse + // error is any problems or inconsistencies in the provided gov prop msg. + // This goes above and beyond the validation done when actually processing the governance proposal. + // If an error is returned, and gov_prop_will_pass is true, it means the error is more of an + // inconsistency that might cause certain aspects of the market to behave unexpectedly. + string error = 1; + // gov_prop_will_pass will be true if the the provided msg will be successfully processed at the end of it's voting + // period (assuming it passes). + bool gov_prop_will_pass = 2; } diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index f02dffa4ec..b801156ecf 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -1027,6 +1027,8 @@ func (m *QueryParamsResponse) GetParams() *Params { // QueryValidateCreateMarketRequest is a request message for the QueryValidateCreateMarket endpoint. type QueryValidateCreateMarketRequest struct { + // create_market_request is the request to run validation on. + CreateMarketRequest *MsgGovCreateMarketRequest `protobuf:"bytes,1,opt,name=create_market_request,json=createMarketRequest,proto3" json:"create_market_request,omitempty"` } func (m *QueryValidateCreateMarketRequest) Reset() { *m = QueryValidateCreateMarketRequest{} } @@ -1062,8 +1064,23 @@ func (m *QueryValidateCreateMarketRequest) XXX_DiscardUnknown() { var xxx_messageInfo_QueryValidateCreateMarketRequest proto.InternalMessageInfo +func (m *QueryValidateCreateMarketRequest) GetCreateMarketRequest() *MsgGovCreateMarketRequest { + if m != nil { + return m.CreateMarketRequest + } + return nil +} + // QueryValidateCreateMarketResponse is a response message for the QueryValidateCreateMarket endpoint. type QueryValidateCreateMarketResponse struct { + // error is any problems or inconsistencies in the provided gov prop msg. + // This goes above and beyond the validation done when actually processing the governance proposal. + // If an error is returned, and gov_prop_will_pass is true, it means the error is more of an + // inconsistency that might cause certain aspects of the market to behave unexpectedly. + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` + // gov_prop_will_pass will be true if the the provided msg will be successfully processed at the end of it's voting + // period (assuming it passes). + GovPropWillPass bool `protobuf:"varint,2,opt,name=gov_prop_will_pass,json=govPropWillPass,proto3" json:"gov_prop_will_pass,omitempty"` } func (m *QueryValidateCreateMarketResponse) Reset() { *m = QueryValidateCreateMarketResponse{} } @@ -1099,16 +1116,123 @@ func (m *QueryValidateCreateMarketResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryValidateCreateMarketResponse proto.InternalMessageInfo +func (m *QueryValidateCreateMarketResponse) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +func (m *QueryValidateCreateMarketResponse) GetGovPropWillPass() bool { + if m != nil { + return m.GovPropWillPass + } + return false +} + +// QueryValidateMarketRequest is a request message for the QueryValidateMarket endpoint. +type QueryValidateMarketRequest struct { + // market_id is the id of the market to check. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` +} + +func (m *QueryValidateMarketRequest) Reset() { *m = QueryValidateMarketRequest{} } +func (m *QueryValidateMarketRequest) String() string { return proto.CompactTextString(m) } +func (*QueryValidateMarketRequest) ProtoMessage() {} +func (*QueryValidateMarketRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{20} +} +func (m *QueryValidateMarketRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidateMarketRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidateMarketRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryValidateMarketRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidateMarketRequest.Merge(m, src) +} +func (m *QueryValidateMarketRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryValidateMarketRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidateMarketRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidateMarketRequest proto.InternalMessageInfo + +func (m *QueryValidateMarketRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +// QueryValidateMarketResponse is a response message for the QueryValidateMarket endpoint. +type QueryValidateMarketResponse struct { + // error is any problems or inconsistencies in the provided market. + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *QueryValidateMarketResponse) Reset() { *m = QueryValidateMarketResponse{} } +func (m *QueryValidateMarketResponse) String() string { return proto.CompactTextString(m) } +func (*QueryValidateMarketResponse) ProtoMessage() {} +func (*QueryValidateMarketResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{21} +} +func (m *QueryValidateMarketResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidateMarketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidateMarketResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryValidateMarketResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidateMarketResponse.Merge(m, src) +} +func (m *QueryValidateMarketResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidateMarketResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidateMarketResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidateMarketResponse proto.InternalMessageInfo + +func (m *QueryValidateMarketResponse) GetError() string { + if m != nil { + return m.Error + } + return "" +} + // QueryValidateManageFeesRequest is a request message for the QueryValidateManageFees endpoint. type QueryValidateManageFeesRequest struct { - MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // manage_fees_request is the request to run validation on. + ManageFeesRequest *MsgGovManageFeesRequest `protobuf:"bytes,1,opt,name=manage_fees_request,json=manageFeesRequest,proto3" json:"manage_fees_request,omitempty"` } func (m *QueryValidateManageFeesRequest) Reset() { *m = QueryValidateManageFeesRequest{} } func (m *QueryValidateManageFeesRequest) String() string { return proto.CompactTextString(m) } func (*QueryValidateManageFeesRequest) ProtoMessage() {} func (*QueryValidateManageFeesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{20} + return fileDescriptor_00949b75b1c10bfe, []int{22} } func (m *QueryValidateManageFeesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1137,22 +1261,30 @@ func (m *QueryValidateManageFeesRequest) XXX_DiscardUnknown() { var xxx_messageInfo_QueryValidateManageFeesRequest proto.InternalMessageInfo -func (m *QueryValidateManageFeesRequest) GetMarketId() uint32 { +func (m *QueryValidateManageFeesRequest) GetManageFeesRequest() *MsgGovManageFeesRequest { if m != nil { - return m.MarketId + return m.ManageFeesRequest } - return 0 + return nil } // QueryValidateManageFeesResponse is a response message for the QueryValidateManageFees endpoint. type QueryValidateManageFeesResponse struct { + // error is any problems or inconsistencies in the provided gov prop msg. + // This goes above and beyond the validation done when actually processing the governance proposal. + // If an error is returned, and gov_prop_will_pass is true, it means the error is more of an + // inconsistency that might cause certain aspects of the market to behave unexpectedly. + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` + // gov_prop_will_pass will be true if the the provided msg will be successfully processed at the end of it's voting + // period (assuming it passes). + GovPropWillPass bool `protobuf:"varint,2,opt,name=gov_prop_will_pass,json=govPropWillPass,proto3" json:"gov_prop_will_pass,omitempty"` } func (m *QueryValidateManageFeesResponse) Reset() { *m = QueryValidateManageFeesResponse{} } func (m *QueryValidateManageFeesResponse) String() string { return proto.CompactTextString(m) } func (*QueryValidateManageFeesResponse) ProtoMessage() {} func (*QueryValidateManageFeesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{21} + return fileDescriptor_00949b75b1c10bfe, []int{23} } func (m *QueryValidateManageFeesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1181,6 +1313,20 @@ func (m *QueryValidateManageFeesResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryValidateManageFeesResponse proto.InternalMessageInfo +func (m *QueryValidateManageFeesResponse) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +func (m *QueryValidateManageFeesResponse) GetGovPropWillPass() bool { + if m != nil { + return m.GovPropWillPass + } + return false +} + func init() { proto.RegisterType((*QueryOrderFeeCalcRequest)(nil), "provenance.exchange.v1.QueryOrderFeeCalcRequest") proto.RegisterType((*QueryOrderFeeCalcResponse)(nil), "provenance.exchange.v1.QueryOrderFeeCalcResponse") @@ -1202,6 +1348,8 @@ func init() { proto.RegisterType((*QueryParamsResponse)(nil), "provenance.exchange.v1.QueryParamsResponse") proto.RegisterType((*QueryValidateCreateMarketRequest)(nil), "provenance.exchange.v1.QueryValidateCreateMarketRequest") proto.RegisterType((*QueryValidateCreateMarketResponse)(nil), "provenance.exchange.v1.QueryValidateCreateMarketResponse") + proto.RegisterType((*QueryValidateMarketRequest)(nil), "provenance.exchange.v1.QueryValidateMarketRequest") + proto.RegisterType((*QueryValidateMarketResponse)(nil), "provenance.exchange.v1.QueryValidateMarketResponse") proto.RegisterType((*QueryValidateManageFeesRequest)(nil), "provenance.exchange.v1.QueryValidateManageFeesRequest") proto.RegisterType((*QueryValidateManageFeesResponse)(nil), "provenance.exchange.v1.QueryValidateManageFeesResponse") } @@ -1211,85 +1359,94 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 1238 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4f, 0x4f, 0x1b, 0x47, - 0x14, 0x67, 0x20, 0xfc, 0x7b, 0x34, 0xa9, 0x3a, 0x75, 0x53, 0x7b, 0x21, 0xc6, 0x2c, 0x51, 0x82, - 0x28, 0xec, 0x62, 0x43, 0x69, 0x7b, 0xe0, 0x00, 0x48, 0x44, 0x91, 0x8a, 0x20, 0xdb, 0xaa, 0x87, - 0x1c, 0xea, 0x8e, 0xed, 0xc1, 0xac, 0xb0, 0x77, 0x9c, 0xdd, 0x85, 0x06, 0x21, 0x2e, 0x55, 0xcf, - 0x55, 0xa5, 0x5e, 0x2a, 0x55, 0x8d, 0x2a, 0x35, 0x1f, 0xa0, 0x87, 0xdc, 0x9b, 0x5b, 0x39, 0x46, - 0xed, 0xa5, 0xbd, 0x54, 0x15, 0xf4, 0x0b, 0xf4, 0x1b, 0x54, 0x3b, 0x33, 0x6b, 0xef, 0x62, 0xef, - 0x1f, 0x22, 0x0e, 0x3e, 0xb1, 0x3b, 0xfb, 0x7e, 0xef, 0xfd, 0xde, 0xef, 0xcd, 0xcc, 0x7b, 0x18, - 0xd4, 0x96, 0xcd, 0x8e, 0xa8, 0x45, 0xac, 0x2a, 0xd5, 0xe9, 0xd3, 0xea, 0x3e, 0xb1, 0xea, 0x54, - 0x3f, 0x2a, 0xea, 0x4f, 0x0e, 0xa9, 0x7d, 0xac, 0xb5, 0x6c, 0xe6, 0x32, 0x7c, 0xbb, 0x63, 0xa3, - 0xf9, 0x36, 0xda, 0x51, 0x51, 0xc9, 0x55, 0x99, 0xd3, 0x64, 0x4e, 0x99, 0x5b, 0xe9, 0xe2, 0x45, - 0x40, 0x94, 0x79, 0xf1, 0xa6, 0x57, 0x88, 0x43, 0x85, 0x2f, 0xfd, 0xa8, 0x58, 0xa1, 0x2e, 0x29, - 0xea, 0x2d, 0x52, 0x37, 0x2d, 0xe2, 0x9a, 0xcc, 0x92, 0xb6, 0xf9, 0xa0, 0xad, 0x6f, 0x55, 0x65, - 0xa6, 0xff, 0x7d, 0xaa, 0xce, 0x58, 0xbd, 0x41, 0x75, 0xd2, 0x32, 0x75, 0x62, 0x59, 0xcc, 0xe5, - 0x60, 0x3f, 0x52, 0xa6, 0xce, 0xea, 0x4c, 0x30, 0xf0, 0x9e, 0xe4, 0xea, 0x6c, 0x44, 0x5a, 0x4d, - 0x62, 0x1f, 0x50, 0x37, 0xc1, 0x88, 0xd9, 0x35, 0x6a, 0x3b, 0x09, 0x46, 0x2d, 0x62, 0x93, 0xa6, - 0x34, 0x52, 0xbf, 0x47, 0x90, 0x7d, 0xe4, 0x65, 0xb9, 0xe3, 0x41, 0xb7, 0x28, 0xdd, 0x24, 0x8d, - 0xaa, 0x41, 0x9f, 0x1c, 0x52, 0xc7, 0xc5, 0x6b, 0x30, 0x4e, 0x9c, 0x83, 0x32, 0xf7, 0x9a, 0x1d, - 0x2c, 0xa0, 0xb9, 0x89, 0x52, 0x41, 0xeb, 0x2d, 0xa9, 0xb6, 0xee, 0x1c, 0x70, 0x17, 0xc6, 0x18, - 0x91, 0x4f, 0x1e, 0xbc, 0x62, 0xd6, 0x24, 0x7c, 0x28, 0x1e, 0xbe, 0x61, 0xd6, 0x24, 0xbc, 0x22, - 0x9f, 0xd4, 0x5f, 0x06, 0x21, 0xd7, 0x83, 0x9a, 0xd3, 0x62, 0x96, 0x43, 0xf1, 0x23, 0xc8, 0x54, - 0x6d, 0xca, 0x05, 0x2d, 0xef, 0x51, 0x5a, 0x66, 0x2d, 0xae, 0x6d, 0x16, 0x15, 0x86, 0xe6, 0x26, - 0x4a, 0x39, 0x4d, 0x16, 0xd5, 0x2b, 0x8d, 0x26, 0x4b, 0xa3, 0x6d, 0x32, 0xd3, 0xda, 0xb8, 0x71, - 0xf6, 0xf7, 0xf4, 0x80, 0x81, 0x7d, 0xf0, 0x16, 0xa5, 0x3b, 0x02, 0x8a, 0x3f, 0x87, 0x49, 0x87, - 0xba, 0x6e, 0x83, 0x36, 0xa9, 0xe5, 0x96, 0xf7, 0x1a, 0xc4, 0x0d, 0x79, 0x1e, 0x4c, 0xe7, 0x39, - 0xdb, 0xf1, 0xb1, 0xd5, 0x20, 0x6e, 0xc0, 0xff, 0x17, 0x30, 0x15, 0xf0, 0x6f, 0x7b, 0xe1, 0x43, - 0x01, 0x86, 0xd2, 0x05, 0xc8, 0x75, 0x9c, 0x18, 0x9e, 0x8f, 0x4e, 0x04, 0xb5, 0x08, 0x19, 0xae, - 0xd8, 0x03, 0xea, 0x0a, 0x35, 0x65, 0x21, 0x73, 0x30, 0xc6, 0xab, 0x50, 0x36, 0x6b, 0x59, 0x54, - 0x40, 0x73, 0x37, 0x8c, 0x51, 0xfe, 0xfe, 0xb0, 0xa6, 0x7e, 0x0c, 0xef, 0x5c, 0x82, 0x48, 0x81, - 0x97, 0x61, 0x58, 0x54, 0x0e, 0xf1, 0xca, 0xdd, 0x89, 0xaa, 0x9c, 0x40, 0x09, 0x5b, 0xf5, 0x37, - 0x04, 0x93, 0xbe, 0xbb, 0x6d, 0xbe, 0x63, 0xf9, 0x67, 0xc7, 0x27, 0x32, 0x09, 0xe3, 0x62, 0x23, - 0xfb, 0x4c, 0x6e, 0x1a, 0x63, 0x62, 0xe1, 0x61, 0x0d, 0xdf, 0x01, 0x10, 0x2c, 0xdd, 0xe3, 0x16, - 0xe5, 0xfb, 0x6d, 0xdc, 0x18, 0xe7, 0x2b, 0x9f, 0x1e, 0xb7, 0x28, 0xbe, 0x0b, 0xb7, 0xc8, 0x9e, - 0x4b, 0xed, 0x72, 0x3b, 0x95, 0x21, 0x9e, 0xca, 0x1b, 0x7c, 0x75, 0x47, 0xe4, 0x83, 0xb7, 0x00, - 0x3a, 0xe7, 0x34, 0x5b, 0xe5, 0xdc, 0xef, 0x85, 0x24, 0x15, 0x17, 0x84, 0x2f, 0xec, 0x2e, 0xa9, - 0x53, 0xc9, 0xce, 0x08, 0x20, 0xd5, 0x67, 0x08, 0xa6, 0x7a, 0x67, 0x22, 0xf5, 0x79, 0x1f, 0x46, - 0xc4, 0x71, 0x93, 0x5b, 0x2e, 0x41, 0x20, 0x69, 0x8c, 0x1f, 0xf4, 0xe0, 0x77, 0x3f, 0x91, 0x9f, - 0x88, 0x19, 0x22, 0xf8, 0x17, 0x02, 0xa5, 0x5d, 0xb9, 0x2f, 0x2d, 0xa9, 0x40, 0x5b, 0x69, 0x0d, - 0x86, 0x99, 0xb7, 0xca, 0x55, 0x1e, 0xdf, 0xc8, 0xfe, 0xfe, 0x62, 0x31, 0x23, 0xa3, 0xac, 0xd7, - 0x6a, 0x36, 0x75, 0x9c, 0x4f, 0x5c, 0xdb, 0xb4, 0xea, 0x86, 0x30, 0xeb, 0x2f, 0xf1, 0x7f, 0x0c, - 0x6c, 0xa3, 0x50, 0x6e, 0x7d, 0xa2, 0xfd, 0xaf, 0x01, 0xed, 0xd7, 0x1d, 0xe7, 0xf2, 0x2e, 0xcf, - 0xc0, 0x30, 0xf1, 0x56, 0x85, 0xf6, 0x86, 0x78, 0xe9, 0x5f, 0x85, 0x43, 0x19, 0xf4, 0x89, 0xc2, - 0x15, 0xd9, 0x96, 0x3c, 0x7a, 0x8d, 0x46, 0x58, 0xde, 0xeb, 0xd2, 0xe0, 0x07, 0x24, 0x1b, 0x4c, - 0x38, 0x48, 0x9f, 0x28, 0xb0, 0xd2, 0xb9, 0x98, 0xc5, 0xfd, 0x93, 0xe6, 0x0e, 0x55, 0xbf, 0x46, - 0x70, 0xfb, 0x32, 0x4c, 0x26, 0x54, 0x82, 0x51, 0x22, 0x4e, 0x7e, 0xe2, 0x9d, 0xe0, 0x1b, 0xe2, - 0x55, 0x18, 0x11, 0xae, 0x65, 0xfb, 0xcf, 0x47, 0x89, 0x20, 0x63, 0x49, 0x6b, 0xb5, 0x1a, 0x52, - 0x56, 0x7c, 0xbc, 0xf6, 0xfa, 0x3d, 0x0f, 0x9e, 0xc2, 0x40, 0x14, 0x99, 0xef, 0x1a, 0x8c, 0x0a, - 0x36, 0x7e, 0x05, 0x67, 0xe3, 0xc9, 0x6f, 0xd8, 0x26, 0xdd, 0x33, 0x7c, 0xcc, 0xf5, 0x15, 0x32, - 0x03, 0x98, 0xb3, 0xdc, 0xe5, 0x73, 0x97, 0x4c, 0x44, 0xdd, 0x86, 0xb7, 0x43, 0xab, 0x92, 0xf4, - 0x2a, 0x8c, 0x88, 0xf9, 0x4c, 0xb6, 0xdd, 0x48, 0xc1, 0x25, 0x4e, 0x5a, 0xab, 0x2a, 0x14, 0xb8, - 0xbb, 0xcf, 0x48, 0xc3, 0xac, 0x11, 0x97, 0x6e, 0x7a, 0xe3, 0x0d, 0x0d, 0x6d, 0x1c, 0x75, 0x16, - 0x66, 0x62, 0x6c, 0x04, 0x01, 0x75, 0x0d, 0xf2, 0x21, 0xa3, 0x6d, 0x62, 0x91, 0x3a, 0xdd, 0xa2, - 0x34, 0x55, 0x0f, 0x57, 0x67, 0x60, 0x3a, 0x12, 0x2e, 0x22, 0x94, 0x5e, 0xbe, 0x09, 0xc3, 0xdc, - 0x06, 0x3f, 0x47, 0xf0, 0x56, 0xd7, 0x84, 0x87, 0x97, 0xa2, 0x52, 0x8e, 0x9a, 0x53, 0x95, 0xe2, - 0x15, 0x10, 0x32, 0xcd, 0xf9, 0xaf, 0xfe, 0xf8, 0xf7, 0xbb, 0xc1, 0xbb, 0x58, 0xd5, 0x23, 0xa6, - 0xe4, 0x3d, 0x4a, 0x1d, 0x31, 0x4f, 0xe3, 0x67, 0x08, 0x6e, 0x86, 0x66, 0x24, 0xbc, 0x10, 0x1b, - 0xf0, 0xd2, 0xf4, 0xa5, 0x2c, 0xa6, 0xb4, 0x96, 0xd4, 0x96, 0x38, 0xb5, 0x79, 0x3c, 0xa7, 0xc7, - 0x4d, 0xf9, 0xfa, 0x89, 0xdf, 0x27, 0x4e, 0xf1, 0x7f, 0xa8, 0x33, 0xf7, 0x05, 0x67, 0x15, 0xbc, - 0x9c, 0x14, 0xb9, 0xc7, 0x8c, 0xa6, 0xac, 0x5c, 0x0d, 0x24, 0x59, 0x5b, 0x9c, 0xf5, 0x3e, 0x2e, - 0xc6, 0xb2, 0x76, 0xe4, 0xff, 0x31, 0xfa, 0x49, 0x7b, 0x0b, 0x9d, 0x3e, 0x5e, 0x8e, 0x06, 0x75, - 0x5b, 0x4b, 0x3f, 0xf8, 0x05, 0x92, 0x07, 0x28, 0x3c, 0x22, 0xe0, 0x52, 0xa2, 0xd8, 0x5d, 0xb3, - 0x92, 0xb2, 0x7c, 0x25, 0x8c, 0x4c, 0x78, 0x85, 0x27, 0xac, 0xe1, 0x85, 0x84, 0x84, 0xf9, 0x78, - 0xa5, 0x9f, 0xf0, 0x3f, 0xa7, 0x21, 0xda, 0x81, 0xbe, 0x9b, 0x4c, 0xbb, 0x7b, 0xcc, 0x48, 0xa6, - 0xdd, 0xa3, 0xb1, 0xa7, 0xa6, 0xcd, 0x67, 0x16, 0xfd, 0x84, 0xff, 0x39, 0xc5, 0x3f, 0xf9, 0x27, - 0x35, 0xd8, 0x2a, 0x13, 0x4e, 0x6a, 0x8f, 0xd6, 0x9d, 0x70, 0x52, 0x7b, 0xf5, 0x61, 0xf5, 0x1e, - 0x27, 0x5c, 0xc0, 0xf9, 0x78, 0xc2, 0xf8, 0x67, 0x04, 0xb7, 0xc2, 0x3b, 0x14, 0x2f, 0xa6, 0xdb, - 0xc9, 0x3e, 0x39, 0x2d, 0xad, 0xb9, 0x64, 0x56, 0xe2, 0xcc, 0x16, 0xf0, 0x7c, 0xfa, 0xdd, 0xeb, - 0x5d, 0x79, 0xb8, 0xbb, 0x67, 0xe1, 0x34, 0xba, 0x84, 0xbb, 0xa8, 0x52, 0xba, 0x0a, 0x44, 0x32, - 0xbe, 0xcf, 0x19, 0xcf, 0xe0, 0xe9, 0x78, 0xc6, 0x0e, 0xfe, 0x06, 0xc1, 0x44, 0xa0, 0x3d, 0xe1, - 0xf9, 0xd8, 0x60, 0xa1, 0xce, 0xa6, 0xbc, 0x97, 0xca, 0x36, 0x6d, 0x75, 0x45, 0x7f, 0xc3, 0x67, - 0xfe, 0xac, 0xd6, 0xab, 0x79, 0xe1, 0x0f, 0x63, 0x43, 0xc6, 0xf4, 0x44, 0xe5, 0xa3, 0xd7, 0x40, - 0x4a, 0xea, 0xab, 0x9c, 0xfa, 0x12, 0xd6, 0xa2, 0xa8, 0x1f, 0x49, 0xb4, 0xce, 0x7f, 0x6b, 0xa0, - 0x65, 0x21, 0x2e, 0x7e, 0x89, 0xe0, 0xdd, 0x88, 0x1e, 0x89, 0x57, 0x53, 0xd1, 0xe9, 0xea, 0xc9, - 0xca, 0x07, 0x57, 0xc6, 0xa5, 0xbd, 0x0e, 0xda, 0x49, 0x34, 0x39, 0xb8, 0xec, 0xf5, 0xc5, 0x0d, - 0x7a, 0x76, 0x9e, 0x47, 0xaf, 0xce, 0xf3, 0xe8, 0x9f, 0xf3, 0x3c, 0xfa, 0xf6, 0x22, 0x3f, 0xf0, - 0xea, 0x22, 0x3f, 0xf0, 0xe7, 0x45, 0x7e, 0x00, 0x72, 0x26, 0x8b, 0xa0, 0xb2, 0x8b, 0x1e, 0x6b, - 0x75, 0xd3, 0xdd, 0x3f, 0xac, 0x68, 0x55, 0xd6, 0x0c, 0x84, 0x5b, 0x34, 0x59, 0x30, 0xf8, 0xd3, - 0x76, 0xf8, 0xca, 0x08, 0xff, 0x8d, 0x6a, 0xf9, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb7, 0xc9, - 0x33, 0x53, 0xeb, 0x13, 0x00, 0x00, + // 1387 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4d, 0x6f, 0x1b, 0x45, + 0x18, 0xce, 0x24, 0x4d, 0x9a, 0x4c, 0x69, 0x51, 0xa7, 0x6e, 0xb1, 0xdd, 0xd6, 0x71, 0xb7, 0x55, + 0x1b, 0xa5, 0xcd, 0x6e, 0x6c, 0xa7, 0x81, 0x1e, 0x7a, 0x48, 0x22, 0x25, 0xaa, 0x44, 0x95, 0x74, + 0x41, 0x20, 0xf5, 0x80, 0x19, 0xdb, 0x93, 0xcd, 0x2a, 0xeb, 0x9d, 0xed, 0xee, 0xc6, 0x4d, 0x14, + 0xe5, 0x00, 0xe2, 0xc2, 0x05, 0x21, 0x71, 0x41, 0x42, 0x54, 0x48, 0xf4, 0x07, 0x70, 0x28, 0x67, + 0xb8, 0x91, 0x63, 0x05, 0x17, 0xb8, 0x20, 0x94, 0xf0, 0x07, 0xe0, 0x17, 0xa0, 0x9d, 0x99, 0xb5, + 0x77, 0xed, 0xfd, 0x32, 0xca, 0x21, 0xa7, 0x78, 0x67, 0xdf, 0x8f, 0xe7, 0x7d, 0xde, 0x99, 0x77, + 0x9e, 0x0d, 0x94, 0x2c, 0x9b, 0x76, 0x88, 0x89, 0xcd, 0x26, 0x51, 0xc8, 0x6e, 0x73, 0x0b, 0x9b, + 0x1a, 0x51, 0x3a, 0x15, 0xe5, 0xd9, 0x0e, 0xb1, 0xf7, 0x64, 0xcb, 0xa6, 0x2e, 0x45, 0x57, 0x7a, + 0x36, 0xb2, 0x6f, 0x23, 0x77, 0x2a, 0xc5, 0x42, 0x93, 0x3a, 0x6d, 0xea, 0xd4, 0x99, 0x95, 0xc2, + 0x1f, 0xb8, 0x4b, 0x71, 0x96, 0x3f, 0x29, 0x0d, 0xec, 0x10, 0x1e, 0x4b, 0xe9, 0x54, 0x1a, 0xc4, + 0xc5, 0x15, 0xc5, 0xc2, 0x9a, 0x6e, 0x62, 0x57, 0xa7, 0xa6, 0xb0, 0x2d, 0x05, 0x6d, 0x7d, 0xab, + 0x26, 0xd5, 0xfd, 0xf7, 0xd7, 0x34, 0x4a, 0x35, 0x83, 0x28, 0xd8, 0xd2, 0x15, 0x6c, 0x9a, 0xd4, + 0x65, 0xce, 0x7e, 0xa6, 0x9c, 0x46, 0x35, 0xca, 0x11, 0x78, 0xbf, 0xc4, 0xea, 0xcd, 0x98, 0xb2, + 0xda, 0xd8, 0xde, 0x26, 0x6e, 0x8a, 0x11, 0xb5, 0x5b, 0xc4, 0x76, 0x52, 0x8c, 0x2c, 0x6c, 0xe3, + 0xb6, 0x6f, 0x34, 0x1d, 0x63, 0xe4, 0xee, 0x72, 0x03, 0xe9, 0x6b, 0x00, 0xf3, 0x4f, 0x3c, 0x1a, + 0xd6, 0xbd, 0xd8, 0xab, 0x84, 0xac, 0x60, 0xa3, 0xa9, 0x92, 0x67, 0x3b, 0xc4, 0x71, 0xd1, 0x43, + 0x38, 0x85, 0x9d, 0xed, 0x3a, 0x4b, 0x9b, 0x1f, 0x2d, 0x83, 0x99, 0x73, 0xd5, 0xb2, 0x1c, 0xcd, + 0xb9, 0xbc, 0xe4, 0x6c, 0xb3, 0x10, 0xea, 0x24, 0x16, 0xbf, 0x3c, 0xf7, 0x86, 0xde, 0x12, 0xee, + 0x63, 0xc9, 0xee, 0xcb, 0x7a, 0x4b, 0xb8, 0x37, 0xc4, 0x2f, 0xe9, 0x87, 0x51, 0x58, 0x88, 0x80, + 0xe6, 0x58, 0xd4, 0x74, 0x08, 0x7a, 0x02, 0x73, 0x4d, 0x9b, 0x30, 0xc6, 0xeb, 0x9b, 0x84, 0xd4, + 0xa9, 0xc5, 0xc8, 0xcf, 0x83, 0xf2, 0xd8, 0xcc, 0xb9, 0x6a, 0x41, 0x16, 0x5d, 0xf7, 0x7a, 0x27, + 0x8b, 0xde, 0xc9, 0x2b, 0x54, 0x37, 0x97, 0xcf, 0x1c, 0xfe, 0x39, 0x3d, 0xa2, 0x22, 0xdf, 0x79, + 0x95, 0x90, 0x75, 0xee, 0x8a, 0x3e, 0x82, 0x57, 0x1d, 0xe2, 0xba, 0x06, 0x69, 0x13, 0xd3, 0xad, + 0x6f, 0x1a, 0xd8, 0x0d, 0x45, 0x1e, 0xcd, 0x16, 0x39, 0xdf, 0x8b, 0xb1, 0x6a, 0x60, 0x37, 0x10, + 0xff, 0x63, 0x78, 0x2d, 0x10, 0xdf, 0xf6, 0xd2, 0x87, 0x12, 0x8c, 0x65, 0x4b, 0x50, 0xe8, 0x05, + 0x51, 0xbd, 0x18, 0xbd, 0x0c, 0x52, 0x05, 0xe6, 0x18, 0x63, 0x6b, 0xc4, 0xe5, 0x6c, 0x8a, 0x46, + 0x16, 0xe0, 0x24, 0xeb, 0x42, 0x5d, 0x6f, 0xe5, 0x41, 0x19, 0xcc, 0x9c, 0x51, 0xcf, 0xb2, 0xe7, + 0x47, 0x2d, 0xe9, 0x5d, 0x78, 0xb9, 0xcf, 0x45, 0x10, 0x5c, 0x83, 0xe3, 0xbc, 0x73, 0x80, 0x75, + 0xee, 0x7a, 0x5c, 0xe7, 0xb8, 0x17, 0xb7, 0x95, 0x7e, 0x01, 0xf0, 0xaa, 0x1f, 0xee, 0x31, 0xdb, + 0xd2, 0xec, 0xb5, 0xe3, 0x03, 0xb9, 0x0a, 0xa7, 0xf8, 0x4e, 0xf7, 0x91, 0x9c, 0x57, 0x27, 0xf9, + 0xc2, 0xa3, 0x16, 0xba, 0x0e, 0x21, 0x47, 0xe9, 0xee, 0x59, 0x84, 0xed, 0xb7, 0x29, 0x75, 0x8a, + 0xad, 0xbc, 0xbf, 0x67, 0x11, 0x74, 0x0b, 0x5e, 0xc0, 0x9b, 0x2e, 0xb1, 0xeb, 0xdd, 0x52, 0xc6, + 0x58, 0x29, 0x6f, 0xb0, 0xd5, 0x75, 0x5e, 0x0f, 0x5a, 0x85, 0xb0, 0x77, 0x90, 0xf3, 0x4d, 0x86, + 0xfd, 0x76, 0x88, 0x52, 0x3e, 0x41, 0x7c, 0x62, 0x37, 0xb0, 0x46, 0x04, 0x3a, 0x35, 0xe0, 0x29, + 0xbd, 0x00, 0xf0, 0x5a, 0x74, 0x25, 0x82, 0x9f, 0xfb, 0x70, 0x82, 0x9f, 0x47, 0xb1, 0xe5, 0x52, + 0x08, 0x12, 0xc6, 0x68, 0x2d, 0x02, 0xdf, 0x9d, 0x54, 0x7c, 0x3c, 0x67, 0x08, 0xe0, 0x1f, 0x00, + 0x16, 0xbb, 0x9d, 0x7b, 0x6e, 0x0a, 0x06, 0xba, 0x4c, 0xcb, 0x70, 0x9c, 0x7a, 0xab, 0x8c, 0xe5, + 0xa9, 0xe5, 0xfc, 0xaf, 0xaf, 0xe6, 0x72, 0x22, 0xcb, 0x52, 0xab, 0x65, 0x13, 0xc7, 0x79, 0xcf, + 0xb5, 0x75, 0x53, 0x53, 0xb9, 0xd9, 0xe9, 0x22, 0xff, 0xdb, 0xc0, 0x36, 0x0a, 0xd5, 0x76, 0x4a, + 0xb8, 0xff, 0x29, 0xc0, 0xfd, 0x92, 0xe3, 0xf4, 0xef, 0xf2, 0x1c, 0x1c, 0xc7, 0xde, 0x2a, 0xe7, + 0x5e, 0xe5, 0x0f, 0xa7, 0x97, 0xe1, 0x50, 0x05, 0xa7, 0x84, 0xe1, 0x86, 0xb8, 0x96, 0x3c, 0x78, + 0x86, 0x11, 0xa6, 0xf7, 0xa4, 0x38, 0xf8, 0x06, 0x88, 0x0b, 0x26, 0x9c, 0xe4, 0x94, 0x30, 0xb0, + 0xd0, 0x1b, 0xcc, 0x7c, 0xfe, 0x64, 0x99, 0xa1, 0xd2, 0x67, 0x00, 0x5e, 0xe9, 0x77, 0x13, 0x05, + 0x55, 0xe1, 0x59, 0xcc, 0x4f, 0x7e, 0xea, 0x4c, 0xf0, 0x0d, 0xd1, 0x22, 0x9c, 0xe0, 0xa1, 0xc5, + 0xf5, 0x5f, 0x8a, 0x23, 0x41, 0xe4, 0x12, 0xd6, 0x52, 0x33, 0xc4, 0x2c, 0x7f, 0x79, 0xe2, 0xfd, + 0x7b, 0x19, 0x3c, 0x85, 0x81, 0x2c, 0xa2, 0xde, 0x87, 0xf0, 0x2c, 0x47, 0xe3, 0x77, 0xf0, 0x66, + 0x32, 0xf8, 0x65, 0x5b, 0x27, 0x9b, 0xaa, 0xef, 0x73, 0x72, 0x8d, 0xcc, 0x41, 0xc4, 0x50, 0x6e, + 0x30, 0x61, 0x26, 0x0a, 0x91, 0x1e, 0xc3, 0x4b, 0xa1, 0x55, 0x01, 0x7a, 0x11, 0x4e, 0x70, 0x01, + 0x27, 0xae, 0xdd, 0x58, 0xc2, 0x85, 0x9f, 0xb0, 0x96, 0x3e, 0x07, 0xb0, 0xcc, 0xe2, 0x7d, 0x80, + 0x0d, 0xbd, 0x85, 0x5d, 0xb2, 0xe2, 0xe9, 0x1b, 0x12, 0xde, 0x39, 0x04, 0x5e, 0x66, 0xb2, 0x87, + 0xd4, 0xc5, 0x06, 0xb2, 0xf9, 0x0b, 0x91, 0xab, 0x12, 0xcb, 0x8f, 0xa3, 0xad, 0xd1, 0x4e, 0x44, + 0x44, 0xf5, 0x52, 0x73, 0x70, 0x51, 0xda, 0x84, 0x37, 0x12, 0xa0, 0x88, 0x42, 0x73, 0x70, 0x9c, + 0xd8, 0x36, 0xb5, 0xfd, 0x19, 0xc9, 0x1e, 0xd0, 0x5d, 0x88, 0x34, 0xda, 0xf1, 0x84, 0xbb, 0x55, + 0x7f, 0xae, 0x1b, 0x46, 0xdd, 0xc2, 0x8e, 0xc3, 0xf6, 0xde, 0xa4, 0xfa, 0xa6, 0x46, 0x3b, 0x1b, + 0x36, 0xb5, 0x3e, 0xd4, 0x0d, 0x63, 0x03, 0x3b, 0x8e, 0xf4, 0x40, 0xb4, 0xdf, 0xcf, 0x33, 0xc4, + 0x31, 0xa9, 0x89, 0xe9, 0xd7, 0xef, 0x9a, 0x04, 0x4e, 0xfa, 0x04, 0xc0, 0x52, 0x9f, 0x97, 0x89, + 0x35, 0xb2, 0x4a, 0x48, 0x77, 0x6b, 0xd7, 0xe1, 0xa5, 0x36, 0x5b, 0xf4, 0x84, 0x9d, 0xd3, 0xc7, + 0xaf, 0x92, 0xcc, 0xef, 0x40, 0x34, 0xf5, 0x62, 0xbb, 0x7f, 0x49, 0x6a, 0xc1, 0xe9, 0x58, 0x08, + 0x27, 0xc6, 0x6c, 0xf5, 0xc7, 0x8b, 0x70, 0x9c, 0xa5, 0x41, 0x2f, 0x01, 0xbc, 0x38, 0x20, 0xc2, + 0xd1, 0x7c, 0x5c, 0x25, 0x71, 0x9f, 0x12, 0xc5, 0xca, 0x10, 0x1e, 0xbc, 0x0e, 0x69, 0xf6, 0xd3, + 0xdf, 0xfe, 0xfe, 0x6a, 0xf4, 0x16, 0x92, 0x94, 0x98, 0x8f, 0x18, 0x8f, 0x62, 0xfe, 0x4d, 0x84, + 0x5e, 0x00, 0x78, 0x3e, 0x24, 0x63, 0xd1, 0xbd, 0xc4, 0x84, 0x7d, 0x02, 0xb9, 0x38, 0x97, 0xd1, + 0x5a, 0x40, 0x9b, 0x67, 0xd0, 0x66, 0xd1, 0x8c, 0x92, 0xf4, 0xa5, 0xa6, 0xec, 0xfb, 0x57, 0xf9, + 0x01, 0xfa, 0x07, 0xf4, 0xa4, 0x79, 0x50, 0x4e, 0xa2, 0x5a, 0x5a, 0xe6, 0x08, 0x19, 0x5d, 0x5c, + 0x18, 0xce, 0x49, 0xa0, 0x36, 0x19, 0xea, 0x2d, 0x54, 0x49, 0x44, 0xed, 0x88, 0x6f, 0x51, 0x65, + 0xbf, 0x7b, 0x7c, 0x0e, 0x9e, 0xd6, 0xe2, 0x9d, 0x06, 0xad, 0x45, 0x1c, 0xf4, 0x0a, 0x88, 0x19, + 0x17, 0x56, 0x71, 0xa8, 0x9a, 0x4a, 0xf6, 0x80, 0x9c, 0x2d, 0xd6, 0x86, 0xf2, 0x11, 0x05, 0x2f, + 0xb0, 0x82, 0x65, 0x74, 0x2f, 0xa5, 0x60, 0xa6, 0x80, 0x95, 0x7d, 0xf6, 0xe7, 0x20, 0x04, 0x3b, + 0x20, 0x8d, 0xd2, 0x61, 0x0f, 0x2a, 0xc1, 0x74, 0xd8, 0x11, 0xda, 0x2b, 0x33, 0x6c, 0x26, 0x2b, + 0x95, 0x7d, 0xf6, 0xe7, 0x00, 0x7d, 0xe7, 0x9f, 0xd4, 0xa0, 0x9a, 0x49, 0x39, 0xa9, 0x11, 0xea, + 0x2a, 0xe5, 0xa4, 0x46, 0x49, 0x25, 0xe9, 0x36, 0x03, 0x5c, 0x46, 0xa5, 0x64, 0xc0, 0xe8, 0x7b, + 0x00, 0x2f, 0x84, 0x77, 0x28, 0x9a, 0xcb, 0xb6, 0x93, 0x7d, 0x70, 0x72, 0x56, 0x73, 0x81, 0xac, + 0xca, 0x90, 0xdd, 0x43, 0xb3, 0xd9, 0x77, 0xaf, 0x37, 0xf2, 0xd0, 0xa0, 0xac, 0x40, 0x59, 0x78, + 0x09, 0x0b, 0x9d, 0x62, 0x75, 0x18, 0x17, 0x81, 0xf8, 0x0e, 0x43, 0x7c, 0x03, 0x4d, 0x27, 0x23, + 0x76, 0xd0, 0x17, 0x00, 0x9e, 0x0b, 0x28, 0x08, 0x34, 0x9b, 0x98, 0x2c, 0x24, 0x3e, 0x8a, 0x77, + 0x33, 0xd9, 0x66, 0xed, 0x2e, 0x97, 0x20, 0xe8, 0xd0, 0x97, 0xd3, 0x51, 0xf7, 0x3e, 0x7a, 0x27, + 0x31, 0x65, 0x82, 0x6a, 0x29, 0x3e, 0xf8, 0x1f, 0x9e, 0x02, 0xfa, 0x22, 0x83, 0x3e, 0x8f, 0xe4, + 0x38, 0xe8, 0x1d, 0xe1, 0xad, 0x84, 0x74, 0x11, 0xfa, 0xd7, 0x1f, 0x01, 0x61, 0x7d, 0x90, 0x32, + 0x02, 0x22, 0x75, 0x48, 0xca, 0x08, 0x88, 0x16, 0x20, 0x92, 0xcd, 0x80, 0x1b, 0xa8, 0x96, 0x0a, + 0x3c, 0x62, 0x58, 0xdf, 0x8f, 0x77, 0x8b, 0x18, 0xd6, 0x7e, 0x24, 0xf4, 0x33, 0x80, 0x6f, 0xc5, + 0x68, 0x0b, 0xb4, 0x98, 0xb1, 0x88, 0x3e, 0xb9, 0x52, 0x7c, 0x7b, 0x68, 0xbf, 0xac, 0x33, 0x30, + 0x40, 0x40, 0x57, 0x6f, 0x2d, 0x93, 0xc3, 0xa3, 0x12, 0x78, 0x7d, 0x54, 0x02, 0x7f, 0x1d, 0x95, + 0xc0, 0x97, 0xc7, 0xa5, 0x91, 0xd7, 0xc7, 0xa5, 0x91, 0xdf, 0x8f, 0x4b, 0x23, 0xb0, 0xa0, 0xd3, + 0x18, 0x28, 0x1b, 0xe0, 0xa9, 0xac, 0xe9, 0xee, 0xd6, 0x4e, 0x43, 0x6e, 0xd2, 0x76, 0x20, 0xdd, + 0x9c, 0x4e, 0x83, 0xc9, 0x77, 0xbb, 0xe9, 0x1b, 0x13, 0xec, 0x7f, 0xa7, 0xb5, 0xff, 0x02, 0x00, + 0x00, 0xff, 0xff, 0x14, 0xc9, 0x3c, 0x3d, 0xa4, 0x16, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1324,6 +1481,8 @@ type QueryClient interface { QueryParams(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. QueryValidateCreateMarket(ctx context.Context, in *QueryValidateCreateMarketRequest, opts ...grpc.CallOption) (*QueryValidateCreateMarketResponse, error) + // QueryValidateMarket checks for any problems with a market's setup. + QueryValidateMarket(ctx context.Context, in *QueryValidateMarketRequest, opts ...grpc.CallOption) (*QueryValidateMarketResponse, error) // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. QueryValidateManageFees(ctx context.Context, in *QueryValidateManageFeesRequest, opts ...grpc.CallOption) (*QueryValidateManageFeesResponse, error) } @@ -1426,6 +1585,15 @@ func (c *queryClient) QueryValidateCreateMarket(ctx context.Context, in *QueryVa return out, nil } +func (c *queryClient) QueryValidateMarket(ctx context.Context, in *QueryValidateMarketRequest, opts ...grpc.CallOption) (*QueryValidateMarketResponse, error) { + out := new(QueryValidateMarketResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryValidateMarket", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *queryClient) QueryValidateManageFees(ctx context.Context, in *QueryValidateManageFeesRequest, opts ...grpc.CallOption) (*QueryValidateManageFeesResponse, error) { out := new(QueryValidateManageFeesResponse) err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryValidateManageFees", in, out, opts...) @@ -1457,6 +1625,8 @@ type QueryServer interface { QueryParams(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. QueryValidateCreateMarket(context.Context, *QueryValidateCreateMarketRequest) (*QueryValidateCreateMarketResponse, error) + // QueryValidateMarket checks for any problems with a market's setup. + QueryValidateMarket(context.Context, *QueryValidateMarketRequest) (*QueryValidateMarketResponse, error) // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. QueryValidateManageFees(context.Context, *QueryValidateManageFeesRequest) (*QueryValidateManageFeesResponse, error) } @@ -1495,6 +1665,9 @@ func (*UnimplementedQueryServer) QueryParams(ctx context.Context, req *QueryPara func (*UnimplementedQueryServer) QueryValidateCreateMarket(ctx context.Context, req *QueryValidateCreateMarketRequest) (*QueryValidateCreateMarketResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryValidateCreateMarket not implemented") } +func (*UnimplementedQueryServer) QueryValidateMarket(ctx context.Context, req *QueryValidateMarketRequest) (*QueryValidateMarketResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryValidateMarket not implemented") +} func (*UnimplementedQueryServer) QueryValidateManageFees(ctx context.Context, req *QueryValidateManageFeesRequest) (*QueryValidateManageFeesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryValidateManageFees not implemented") } @@ -1683,6 +1856,24 @@ func _Query_QueryValidateCreateMarket_Handler(srv interface{}, ctx context.Conte return interceptor(ctx, in, info, handler) } +func _Query_QueryValidateMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidateMarketRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryValidateMarket(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryValidateMarket", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryValidateMarket(ctx, req.(*QueryValidateMarketRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Query_QueryValidateManageFees_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryValidateManageFeesRequest) if err := dec(in); err != nil { @@ -1745,6 +1936,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "QueryValidateCreateMarket", Handler: _Query_QueryValidateCreateMarket_Handler, }, + { + MethodName: "QueryValidateMarket", + Handler: _Query_QueryValidateMarket_Handler, + }, { MethodName: "QueryValidateManageFees", Handler: _Query_QueryValidateManageFees_Handler, @@ -2572,6 +2767,18 @@ func (m *QueryValidateCreateMarketRequest) MarshalToSizedBuffer(dAtA []byte) (in _ = i var l int _ = l + if m.CreateMarketRequest != nil { + { + size, err := m.CreateMarketRequest.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2595,10 +2802,27 @@ func (m *QueryValidateCreateMarketResponse) MarshalToSizedBuffer(dAtA []byte) (i _ = i var l int _ = l + if m.GovPropWillPass { + i-- + if m.GovPropWillPass { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } -func (m *QueryValidateManageFeesRequest) Marshal() (dAtA []byte, err error) { +func (m *QueryValidateMarketRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -2608,12 +2832,12 @@ func (m *QueryValidateManageFeesRequest) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryValidateManageFeesRequest) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryValidateMarketRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryValidateManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryValidateMarketRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -2626,6 +2850,71 @@ func (m *QueryValidateManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, return len(dAtA) - i, nil } +func (m *QueryValidateMarketResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryValidateMarketResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidateMarketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryValidateManageFeesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryValidateManageFeesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidateManageFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ManageFeesRequest != nil { + { + size, err := m.ManageFeesRequest.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *QueryValidateManageFeesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2646,6 +2935,23 @@ func (m *QueryValidateManageFeesResponse) MarshalToSizedBuffer(dAtA []byte) (int _ = i var l int _ = l + if m.GovPropWillPass { + i-- + if m.GovPropWillPass { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2978,6 +3284,10 @@ func (m *QueryValidateCreateMarketRequest) Size() (n int) { } var l int _ = l + if m.CreateMarketRequest != nil { + l = m.CreateMarketRequest.Size() + n += 1 + l + sovQuery(uint64(l)) + } return n } @@ -2987,10 +3297,17 @@ func (m *QueryValidateCreateMarketResponse) Size() (n int) { } var l int _ = l + l = len(m.Error) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.GovPropWillPass { + n += 2 + } return n } -func (m *QueryValidateManageFeesRequest) Size() (n int) { +func (m *QueryValidateMarketRequest) Size() (n int) { if m == nil { return 0 } @@ -3002,12 +3319,45 @@ func (m *QueryValidateManageFeesRequest) Size() (n int) { return n } +func (m *QueryValidateMarketResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Error) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryValidateManageFeesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ManageFeesRequest != nil { + l = m.ManageFeesRequest.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + func (m *QueryValidateManageFeesResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l + l = len(m.Error) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.GovPropWillPass { + n += 2 + } return n } @@ -5064,6 +5414,42 @@ func (m *QueryValidateCreateMarketRequest) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: QueryValidateCreateMarketRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreateMarketRequest", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CreateMarketRequest == nil { + m.CreateMarketRequest = &MsgGovCreateMarketRequest{} + } + if err := m.CreateMarketRequest.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -5114,6 +5500,58 @@ func (m *QueryValidateCreateMarketResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: QueryValidateCreateMarketResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GovPropWillPass", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.GovPropWillPass = bool(v != 0) default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -5135,7 +5573,7 @@ func (m *QueryValidateCreateMarketResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryValidateManageFeesRequest) Unmarshal(dAtA []byte) error { +func (m *QueryValidateMarketRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -5158,10 +5596,10 @@ func (m *QueryValidateManageFeesRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryValidateManageFeesRequest: wiretype end group for non-group") + return fmt.Errorf("proto: QueryValidateMarketRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryValidateManageFeesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryValidateMarketRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -5204,6 +5642,174 @@ func (m *QueryValidateManageFeesRequest) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryValidateMarketResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryValidateMarketResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidateMarketResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryValidateManageFeesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryValidateManageFeesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidateManageFeesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ManageFeesRequest", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ManageFeesRequest == nil { + m.ManageFeesRequest = &MsgGovManageFeesRequest{} + } + if err := m.ManageFeesRequest.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *QueryValidateManageFeesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -5233,6 +5839,58 @@ func (m *QueryValidateManageFeesResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: QueryValidateManageFeesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GovPropWillPass", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.GovPropWillPass = bool(v != 0) default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/x/exchange/query.pb.gw.go b/x/exchange/query.pb.gw.go index 94d5ad97ec..33f2a941e8 100644 --- a/x/exchange/query.pb.gw.go +++ b/x/exchange/query.pb.gw.go @@ -553,10 +553,21 @@ func local_request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Ma } +var ( + filter_Query_QueryValidateCreateMarket_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + func request_Query_QueryValidateCreateMarket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryValidateCreateMarketRequest var metadata runtime.ServerMetadata + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryValidateCreateMarket_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.QueryValidateCreateMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -566,11 +577,126 @@ func local_request_Query_QueryValidateCreateMarket_0(ctx context.Context, marsha var protoReq QueryValidateCreateMarketRequest var metadata runtime.ServerMetadata + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryValidateCreateMarket_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.QueryValidateCreateMarket(ctx, &protoReq) return msg, metadata, err } +func request_Query_QueryValidateMarket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidateMarketRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + msg, err := client.QueryValidateMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryValidateMarket_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidateMarketRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + msg, err := server.QueryValidateMarket(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_QueryValidateMarket_1(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidateMarketRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + msg, err := client.QueryValidateMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryValidateMarket_1(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidateMarketRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + msg, err := server.QueryValidateMarket(ctx, &protoReq) + return msg, metadata, err + +} + var ( filter_Query_QueryValidateManageFees_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) @@ -833,6 +959,46 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_QueryValidateMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryValidateMarket_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryValidateMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryValidateMarket_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryValidateMarket_1(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryValidateMarket_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_QueryValidateManageFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1114,6 +1280,46 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_QueryValidateMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryValidateMarket_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryValidateMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryValidateMarket_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryValidateMarket_1(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryValidateMarket_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_QueryValidateManageFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1160,6 +1366,10 @@ var ( pattern_Query_QueryValidateCreateMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "validate", "create_market"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryValidateMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"provenance", "exchange", "v1", "validate", "market", "market_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryValidateMarket_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"provenance", "exchange", "v1", "market", "market_id", "validate"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryValidateManageFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "validate", "manage_fees"}, "", runtime.AssumeColonVerbOpt(false))) ) @@ -1186,5 +1396,9 @@ var ( forward_Query_QueryValidateCreateMarket_0 = runtime.ForwardResponseMessage + forward_Query_QueryValidateMarket_0 = runtime.ForwardResponseMessage + + forward_Query_QueryValidateMarket_1 = runtime.ForwardResponseMessage + forward_Query_QueryValidateManageFees_0 = runtime.ForwardResponseMessage ) From 70c7539b5872465b09de4ef62053510d8797b799 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 2 Oct 2023 16:32:50 -0600 Subject: [PATCH 223/309] [1658]: Implement QueryValidateCreateMarket and QueryValidateMarket. --- x/exchange/keeper/grpc_query.go | 64 ++++++++++++++++- x/exchange/keeper/keeper.go | 5 ++ x/exchange/keeper/market.go | 61 +++++++++++++--- x/exchange/keeper/msg_server.go | 7 -- x/exchange/market.go | 12 ++++ x/exchange/market_test.go | 121 ++++++++++++++++++++++++++++++++ 6 files changed, 251 insertions(+), 19 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index c715dd6870..4d70abf18e 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -2,6 +2,7 @@ package keeper import ( "context" + "errors" "fmt" "google.golang.org/grpc/codes" @@ -253,8 +254,67 @@ func (k QueryServer) QueryParams(goCtx context.Context, _ *exchange.QueryParamsR // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. func (k QueryServer) QueryValidateCreateMarket(goCtx context.Context, req *exchange.QueryValidateCreateMarketRequest) (*exchange.QueryValidateCreateMarketResponse, error) { - // TODO[1658]: Implement QueryValidateCreateMarket query - panic("not implemented") + if req == nil || req.CreateMarketRequest == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + msg := req.CreateMarketRequest + resp := &exchange.QueryValidateCreateMarketResponse{} + + if err := msg.ValidateBasic(); err != nil { + resp.Error = err.Error() + return resp, nil + } + + if msg.Authority != k.authority { + resp.Error = k.wrongAuthErr(msg.Authority).Error() + return resp, nil + } + + // The SDK *should* already be using a cache context for queries, but I'm doing it here too just to be on the safe side. + ctx, _ := sdk.UnwrapSDKContext(goCtx).CacheContext() + var marketID uint32 + if newMarketID, err := k.CreateMarket(ctx, msg.Market); err != nil { + resp.Error = err.Error() + return resp, nil + } else { + marketID = newMarketID + } + + resp.GovPropWillPass = true + + var errs []error + if err := exchange.ValidateReqAttrsAreNormalized("create ask", msg.Market.ReqAttrCreateAsk); err != nil { + errs = append(errs, err) + } + if err := exchange.ValidateReqAttrsAreNormalized("create bid", msg.Market.ReqAttrCreateBid); err != nil { + errs = append(errs, err) + } + + if err := k.ValidateMarket(ctx, marketID); err != nil { + errs = append(errs, err) + } + + if len(errs) > 0 { + resp.Error = errors.Join(errs...).Error() + } + + return resp, nil +} + +// QueryValidateMarket checks for any problems with a market's setup. +func (k QueryServer) QueryValidateMarket(goCtx context.Context, req *exchange.QueryValidateMarketRequest) (*exchange.QueryValidateMarketResponse, error) { + if req == nil || req.MarketId == 0 { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + resp := &exchange.QueryValidateMarketResponse{} + if err := k.ValidateMarket(ctx, req.MarketId); err != nil { + resp.Error = err.Error() + } + + return resp, nil } // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index e2110472db..44c090bb68 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -54,6 +54,11 @@ func (k Keeper) logErrorf(ctx sdk.Context, msg string, args ...interface{}) { ctx.Logger().Error(fmt.Sprintf(msg, args...), "module", "x/"+exchange.ModuleName) } +// wrongAuthErr returns the error to use when a message's authority isn't what's required. +func (k Keeper) wrongAuthErr(badAuthority string) error { + return govtypes.ErrInvalidSigner.Wrapf("expected %s got %s", k.GetAuthority(), badAuthority) +} + // GetAuthority gets the address (as bech32) that has governance authority. func (k Keeper) GetAuthority() string { return k.authority diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 5c97b1d790..aa5ee2059a 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -873,10 +873,11 @@ func (k Keeper) NextMarketID(ctx sdk.Context) uint32 { store := k.getStore(ctx) marketID := getLastAutoMarketID(store) + 1 for { - marketAddr := exchange.GetMarketAddress(marketID) - if !k.accountKeeper.HasAccount(ctx, marketAddr) { + key := MakeKeyKnownMarketID(marketID) + if !store.Has(key) { break } + marketID++ } setLastAutoMarketID(store, marketID) return marketID @@ -945,14 +946,7 @@ func storeMarket(store sdk.KVStore, market exchange.Market) { // CreateMarket saves a new market to the store with all the info provided. // If the marketId is zero, the next available one will be used. -func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (marketID uint32, err error) { - defer func() { - // TODO[1658]: Figure out why this recover is needed and either add a comment or delete this defer. - if r := recover(); r != nil { - err = fmt.Errorf("could not create market: %v", r) - } - }() - +func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (uint32, error) { // Note: The Market is passed in by value, so any alterations to it here will be lost upon return. var errAsk, errBid error market.ReqAttrCreateAsk, errAsk = exchange.NormalizeReqAttrs(market.ReqAttrCreateAsk) @@ -1262,3 +1256,50 @@ func (k Keeper) UpdateReqAttrs(ctx sdk.Context, msg *exchange.MsgMarketManageReq return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketReqAttrUpdated(marketID, admin)) } + +// ValidateMarket checks the setup of the provided market, making sure there aren't any possibly problematic settings. +func (k Keeper) ValidateMarket(ctx sdk.Context, marketID uint32) error { + store := k.getStore(ctx) + if err := validateMarketExists(store, marketID); err != nil { + return err + } + + var errs []error + + sellerRatios := getSellerSettlementRatios(store, marketID) + buyerRatios := getBuyerSettlementRatios(store, marketID) + if len(sellerRatios) > 0 && len(buyerRatios) > 0 { + // We only need to check the price denoms if *both* types have an entry. + sellerPriceDenoms := make([]string, len(sellerRatios)) + sellerPriceDenomsKnown := make(map[string]bool) + for i, ratio := range sellerRatios { + sellerPriceDenoms[i] = ratio.Price.Denom + sellerPriceDenomsKnown[ratio.Price.Denom] = true + } + + buyerPriceDenoms := make([]string, 0, len(sellerRatios)) + buyerPriceDenomsKnown := make(map[string]bool) + for _, ratio := range buyerRatios { + if !buyerPriceDenomsKnown[ratio.Price.Denom] { + buyerPriceDenoms = append(buyerPriceDenoms, ratio.Price.Denom) + buyerPriceDenomsKnown[ratio.Price.Denom] = true + } + } + + for _, denom := range sellerPriceDenoms { + if !buyerPriceDenomsKnown[denom] { + errs = append(errs, fmt.Errorf("seller settlement fee ratios have price denom %q "+ + "but there are no buyer settlement fee ratios with that price denom", denom)) + } + } + + for _, denom := range buyerPriceDenoms { + if !sellerPriceDenomsKnown[denom] { + errs = append(errs, fmt.Errorf("buyer settlement fee ratios have price denom %q "+ + "but there is not a seller settlement fee ratio with that price denom", denom)) + } + } + } + + return errors.Join(errs...) +} diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 12117997ad..75e5e499be 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -5,8 +5,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/provenance-io/provenance/x/exchange" ) @@ -174,11 +172,6 @@ func (k MsgServer) MarketManageReqAttrs(goCtx context.Context, msg *exchange.Msg return &exchange.MsgMarketManageReqAttrsResponse{}, nil } -// wrongAuthErr returns the error to use when a message's authority isn't what's required. -func (k MsgServer) wrongAuthErr(badAuthority string) error { - return govtypes.ErrInvalidSigner.Wrapf("expected %s got %s", k.GetAuthority(), badAuthority) -} - // GovCreateMarket is a governance proposal endpoint for creating a market. func (k MsgServer) GovCreateMarket(goCtx context.Context, msg *exchange.MsgGovCreateMarketRequest) (*exchange.MsgGovCreateMarketResponse, error) { if !k.IsAuthority(msg.Authority) { diff --git a/x/exchange/market.go b/x/exchange/market.go index 78bac15c43..4ce0b8aa3a 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -456,6 +456,18 @@ func NormalizeReqAttrs(reqAttrs []string) ([]string, error) { return rv, errors.Join(errs...) } +// ValidateReqAttrsAreNormalized checks that each of the provided attrs is equal to its normalized version. +func ValidateReqAttrsAreNormalized(field string, attrs []string) error { + var errs []error + for _, attr := range attrs { + norm := nametypes.NormalizeName(attr) + if attr != norm { + errs = append(errs, fmt.Errorf("%s required attribute %q is not normalized, expected %q", field, attr, norm)) + } + } + return errors.Join(errs...) +} + // ValidateReqAttrs makes sure that each provided attribute is valid and that no duplicate entries are provided. func ValidateReqAttrs(field string, attrs []string) error { var errs []error diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 1d0524b568..05f73488c5 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -2506,6 +2506,127 @@ func TestNormalizeReqAttrs(t *testing.T) { } } +func TestValidateReqAttrsAreNormalized(t *testing.T) { + joinErrs := func(errs ...string) string { + return strings.Join(errs, "\n") + } + notNormErr := func(field, attr, norm string) string { + return fmt.Sprintf("%s required attribute %q is not normalized, expected %q", field, attr, norm) + } + + tests := []struct { + name string + field string + attrs []string + expErr string + }{ + {name: "nil attrs", field: "FOILD", attrs: nil, expErr: ""}, + {name: "empty attrs", field: "FOILD", attrs: []string{}, expErr: ""}, + { + name: "one attr: normalized", + field: "TINFOILD", + attrs: []string{"abc.def"}, + expErr: "", + }, + { + name: "one attr: with whitespace", + field: "AlFOILD", + attrs: []string{" abc.def"}, + expErr: notNormErr("AlFOILD", " abc.def", "abc.def"), + }, + { + name: "one attr: with upper", + field: "AlFOILD", + attrs: []string{"aBc.def"}, + expErr: notNormErr("AlFOILD", "aBc.def", "abc.def"), + }, + { + name: "one attr: with wildcard, ok", + field: "NOFOILD", + attrs: []string{"*.abc.def"}, + expErr: "", + }, + { + name: "one attr: with wildcard, bad", + field: "AirFOILD", + attrs: []string{"*.abc. def"}, + expErr: notNormErr("AirFOILD", "*.abc. def", "*.abc.def"), + }, + { + name: "three attrs: all okay", + field: "WhaFoild", + attrs: []string{"abc.def", "*.ghi.jkl", "mno.pqr.stu.vwx.yz"}, + expErr: "", + }, + { + name: "three attrs: bad first", + field: "Uno1Foild", + attrs: []string{"abc. def", "*.ghi.jkl", "mno.pqr.stu.vwx.yz"}, + expErr: notNormErr("Uno1Foild", "abc. def", "abc.def"), + }, + { + name: "three attrs: bad second", + field: "Uno2Foild", + attrs: []string{"abc.def", "*.ghi.jkl ", "mno.pqr.stu.vwx.yz"}, + expErr: notNormErr("Uno2Foild", "*.ghi.jkl ", "*.ghi.jkl"), + }, + { + name: "three attrs: bad third", + field: "Uno3Foild", + attrs: []string{"abc.def", "*.ghi.jkl", "mnO.pqr.stu.vwX.yz"}, + expErr: notNormErr("Uno3Foild", "mnO.pqr.stu.vwX.yz", "mno.pqr.stu.vwx.yz"), + }, + { + name: "three attrs: bad first and second", + field: "TwoFold1", + attrs: []string{"abc.Def", "* .ghi.jkl", "mno.pqr.stu.vwx.yz"}, + expErr: joinErrs( + notNormErr("TwoFold1", "abc.Def", "abc.def"), + notNormErr("TwoFold1", "* .ghi.jkl", "*.ghi.jkl"), + ), + }, + { + name: "three attrs: bad first and third", + field: "TwoFold2", + attrs: []string{"abc . def", "*.ghi.jkl", "mno.pqr. stu .vwx.yz"}, + expErr: joinErrs( + notNormErr("TwoFold2", "abc . def", "abc.def"), + notNormErr("TwoFold2", "mno.pqr. stu .vwx.yz", "mno.pqr.stu.vwx.yz"), + ), + }, + { + name: "three attrs: bad second and third", + field: "TwoFold3", + attrs: []string{"abc.def", "*.ghi.JKl", "mno.pqr.sTu.vwx.yz"}, + expErr: joinErrs( + notNormErr("TwoFold3", "*.ghi.JKl", "*.ghi.jkl"), + notNormErr("TwoFold3", "mno.pqr.sTu.vwx.yz", "mno.pqr.stu.vwx.yz"), + ), + }, + { + name: "three attrs: all bad", + field: "CURSES!", + attrs: []string{" abc . def ", " * . ghi . jkl ", " mno . pqr . stu . vwx . yz "}, + expErr: joinErrs( + notNormErr("CURSES!", " abc . def ", "abc.def"), + notNormErr("CURSES!", " * . ghi . jkl ", "*.ghi.jkl"), + notNormErr("CURSES!", " mno . pqr . stu . vwx . yz ", "mno.pqr.stu.vwx.yz"), + ), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = ValidateReqAttrsAreNormalized(tc.field, tc.attrs) + } + require.NotPanics(t, testFunc, "ValidateReqAttrsAreNormalized") + assertions.AssertErrorValue(t, err, tc.expErr, "ValidateReqAttrsAreNormalized error") + }) + } +} + func TestValidateReqAttrs(t *testing.T) { joinErrs := func(errs ...string) string { return strings.Join(errs, "\n") From cfc581f24a9a533ca8b485d37c7c64fa491e6335 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 3 Oct 2023 01:15:39 -0600 Subject: [PATCH 224/309] [1658]: Implement QueryValidateManageFees. --- x/exchange/helpers.go | 13 + x/exchange/helpers_test.go | 184 +++++++++++ x/exchange/keeper/grpc_query.go | 70 +++- x/exchange/market.go | 59 ++++ x/exchange/market_test.go | 561 ++++++++++++++++++++++++++++++++ 5 files changed, 885 insertions(+), 2 deletions(-) diff --git a/x/exchange/helpers.go b/x/exchange/helpers.go index e940e57e71..4700f30c42 100644 --- a/x/exchange/helpers.go +++ b/x/exchange/helpers.go @@ -76,6 +76,19 @@ func IntersectionOfCoin(list1, list2 []sdk.Coin) []sdk.Coin { return intersection(list1, list2, CoinEquals) } +// ContainsCoin returns true if the coin to find is in the vals slice. +func ContainsCoin(vals []sdk.Coin, toFind sdk.Coin) bool { + return contains(vals, toFind, CoinEquals) +} + +// ContainsCoinWithSameDenom returns true if there's an entry in vals with the same denom as the denom to find. +func ContainsCoinWithSameDenom(vals []sdk.Coin, toFind sdk.Coin) bool { + return contains(vals, toFind, func(a, b sdk.Coin) bool { + return a.Denom == b.Denom + }) +} + +// MinSDKInt returns the lesser of the two provided ints. func MinSDKInt(a, b sdkmath.Int) sdkmath.Int { if a.LTE(b) { return a diff --git a/x/exchange/helpers_test.go b/x/exchange/helpers_test.go index df173da739..3fb976a73d 100644 --- a/x/exchange/helpers_test.go +++ b/x/exchange/helpers_test.go @@ -468,6 +468,190 @@ func TestIntersectionOfCoin(t *testing.T) { } } +func TestContainsCoin(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + vals []sdk.Coin + toFind sdk.Coin + exp bool + }{ + {name: "nil vals", vals: nil, toFind: coin(1, "banana"), exp: false}, + {name: "empty vals", vals: []sdk.Coin{}, toFind: coin(1, "banana"), exp: false}, + { + name: "one val, diff denom and amount", + vals: []sdk.Coin{coin(3, "banana")}, + toFind: coin(8, "apple"), + exp: false, + }, + { + name: "one val, same denom diff amount", + vals: []sdk.Coin{coin(3, "apple")}, + toFind: coin(8, "apple"), + exp: false, + }, + { + name: "one val, diff denom same amount", + vals: []sdk.Coin{coin(8, "banana")}, + toFind: coin(8, "apple"), + exp: false, + }, + { + name: "one val, same denom and amount", + vals: []sdk.Coin{coin(8, "apple")}, + toFind: coin(8, "apple"), + exp: true, + }, + { + name: "one neg val, same", + vals: []sdk.Coin{coin(-3, "apple")}, + toFind: coin(-3, "apple"), + exp: true, + }, + { + name: "one val without denom, same", + vals: []sdk.Coin{coin(22, "")}, + toFind: coin(22, ""), + exp: true, + }, + { + name: "one val zero, diff denom", + vals: []sdk.Coin{coin(0, "banana")}, + toFind: coin(0, "apple"), + exp: false, + }, + { + name: "one val zero, same denom", + vals: []sdk.Coin{coin(0, "banana")}, + toFind: coin(0, "banana"), + exp: true, + }, + { + name: "three same vals, not to find", + vals: []sdk.Coin{coin(1, "apple"), coin(1, "apple"), coin(1, "apple")}, + toFind: coin(2, "apple"), + exp: false, + }, + { + name: "three vals, not to find", + vals: []sdk.Coin{coin(1, "apple"), coin(2, "banana"), coin(3, "cactus")}, + toFind: coin(4, "durian"), + exp: false, + }, + { + name: "three vals, first", + vals: []sdk.Coin{coin(1, "apple"), coin(2, "banana"), coin(3, "cactus")}, + toFind: coin(1, "apple"), + exp: true, + }, + { + name: "three vals, second", + vals: []sdk.Coin{coin(1, "apple"), coin(2, "banana"), coin(3, "cactus")}, + toFind: coin(2, "banana"), + exp: true, + }, + { + name: "three vals, third", + vals: []sdk.Coin{coin(1, "apple"), coin(2, "banana"), coin(3, "cactus")}, + toFind: coin(3, "cactus"), + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = ContainsCoin(tc.vals, tc.toFind) + } + require.NotPanics(t, testFunc, "ContainsCoin(%q, %q)", sdk.Coins(tc.vals), tc.toFind) + assert.Equal(t, tc.exp, actual, "ContainsCoin(%q, %q)", sdk.Coins(tc.vals), tc.toFind) + }) + } +} + +func TestContainsCoinWithSameDenom(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + vals []sdk.Coin + toFind sdk.Coin + exp bool + }{ + { + name: "nil vals", + vals: nil, + toFind: coin(1, "apple"), + exp: false, + }, + { + name: "empty vals", + vals: []sdk.Coin{}, + toFind: coin(1, "apple"), + exp: false, + }, + { + name: "one val, same amount, diff denom", + vals: []sdk.Coin{coin(1, "apple")}, + toFind: coin(1, "banana"), + exp: false, + }, + { + name: "one val, same", + vals: []sdk.Coin{coin(1, "apple")}, + toFind: coin(1, "apple"), + exp: true, + }, + { + name: "one val, same denom, diff amount", + vals: []sdk.Coin{coin(1, "apple")}, + toFind: coin(2, "apple"), + exp: true, + }, + { + name: "three vals, not to find", + vals: []sdk.Coin{coin(1, "apple"), coin(2, "banana"), coin(3, "cactus")}, + toFind: coin(4, "durian"), + exp: false, + }, + { + name: "three vals, first", + vals: []sdk.Coin{coin(1, "apple"), coin(2, "banana"), coin(3, "cactus")}, + toFind: coin(4, "apple"), + exp: true, + }, + { + name: "three vals, second", + vals: []sdk.Coin{coin(1, "apple"), coin(2, "banana"), coin(3, "cactus")}, + toFind: coin(4, "banana"), + exp: true, + }, + { + name: "three vals, third", + vals: []sdk.Coin{coin(1, "apple"), coin(2, "banana"), coin(3, "cactus")}, + toFind: coin(4, "cactus"), + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = ContainsCoinWithSameDenom(tc.vals, tc.toFind) + } + require.NotPanics(t, testFunc, "ContainsCoinWithSameDenom(%q, %q)", sdk.Coins(tc.vals), tc.toFind) + assert.Equal(t, tc.exp, actual, "ContainsCoinWithSameDenom(%q, %q)", sdk.Coins(tc.vals), tc.toFind) + }) + } +} + func TestMinSDKInt(t *testing.T) { newInt := func(val string) sdkmath.Int { rv, ok := sdkmath.NewIntFromString(val) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 4d70abf18e..1cd0e1e499 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -319,6 +319,72 @@ func (k QueryServer) QueryValidateMarket(goCtx context.Context, req *exchange.Qu // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. func (k QueryServer) QueryValidateManageFees(goCtx context.Context, req *exchange.QueryValidateManageFeesRequest) (*exchange.QueryValidateManageFeesResponse, error) { - // TODO[1658]: Implement QueryValidateManageFees query - panic("not implemented") + if req == nil || req.ManageFeesRequest == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + msg := req.ManageFeesRequest + resp := &exchange.QueryValidateManageFeesResponse{} + + if err := msg.ValidateBasic(); err != nil { + resp.Error = err.Error() + return resp, nil + } + + if msg.Authority != k.authority { + resp.Error = k.wrongAuthErr(msg.Authority).Error() + return resp, nil + } + + ctx := sdk.UnwrapSDKContext(goCtx) + store := k.getStore(ctx) + if err := validateMarketExists(store, msg.MarketId); err != nil { + resp.Error = err.Error() + return resp, nil + } + + resp.GovPropWillPass = true + + var errs []error + if len(msg.AddFeeCreateAskFlat) > 0 || len(msg.RemoveFeeCreateAskFlat) > 0 { + createAskFlats := getCreateAskFlatFees(store, msg.MarketId) + errs = append(errs, exchange.ValidateAddRemoveFeeOptionsWithExisting("create-ask", + createAskFlats, msg.AddFeeCreateAskFlat, msg.RemoveFeeCreateAskFlat)...) + } + + if len(msg.AddFeeCreateBidFlat) > 0 || len(msg.RemoveFeeCreateBidFlat) > 0 { + createBidFlats := getCreateBidFlatFees(store, msg.MarketId) + errs = append(errs, exchange.ValidateAddRemoveFeeOptionsWithExisting("create-bid", + createBidFlats, msg.AddFeeCreateBidFlat, msg.RemoveFeeCreateBidFlat)...) + } + + if len(msg.AddFeeSellerSettlementFlat) > 0 || len(msg.RemoveFeeSellerSettlementFlat) > 0 { + sellerFlats := getSellerSettlementFlatFees(store, msg.MarketId) + errs = append(errs, exchange.ValidateAddRemoveFeeOptionsWithExisting("seller settlement", + sellerFlats, msg.AddFeeSellerSettlementFlat, msg.RemoveFeeSellerSettlementFlat)...) + } + + if len(msg.AddFeeSellerSettlementRatios) > 0 || len(msg.RemoveFeeSellerSettlementRatios) > 0 { + sellerRatios := getSellerSettlementRatios(store, msg.MarketId) + errs = append(errs, exchange.ValidateAddRemoveFeeRatiosWithExisting("seller settlement", + sellerRatios, msg.AddFeeSellerSettlementRatios, msg.RemoveFeeSellerSettlementRatios)...) + } + + if len(msg.AddFeeBuyerSettlementFlat) > 0 || len(msg.RemoveFeeBuyerSettlementFlat) > 0 { + buyerFlats := getBuyerSettlementFlatFees(store, msg.MarketId) + errs = append(errs, exchange.ValidateAddRemoveFeeOptionsWithExisting("buyer settlement", + buyerFlats, msg.AddFeeBuyerSettlementFlat, msg.RemoveFeeBuyerSettlementFlat)...) + } + + if len(msg.AddFeeBuyerSettlementRatios) > 0 || len(msg.RemoveFeeBuyerSettlementRatios) > 0 { + buyerRatios := getBuyerSettlementRatios(store, msg.MarketId) + errs = append(errs, exchange.ValidateAddRemoveFeeRatiosWithExisting("buyer settlement", + buyerRatios, msg.AddFeeBuyerSettlementRatios, msg.RemoveFeeBuyerSettlementRatios)...) + } + + if len(errs) > 0 { + resp.Error = errors.Join(errs...).Error() + } + + return resp, nil } diff --git a/x/exchange/market.go b/x/exchange/market.go index 4ce0b8aa3a..4f9f8fab78 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -287,6 +287,18 @@ func IntersectionOfFeeRatios(list1, list2 []FeeRatio) []FeeRatio { return intersection(list1, list2, FeeRatio.Equals) } +// ContainsFeeRatio returns true if the fee ratio to find is in the vals slice. +func ContainsFeeRatio(vals []FeeRatio, toFind FeeRatio) bool { + return contains(vals, toFind, FeeRatio.Equals) +} + +// ContainsSameFeeRatioDenoms returns true if any ratio in vals has the same price and fee denoms as the ratio to find. +func ContainsSameFeeRatioDenoms(vals []FeeRatio, toFind FeeRatio) bool { + return contains(vals, toFind, func(a, b FeeRatio) bool { + return a.Price.Denom == b.Price.Denom && a.Fee.Denom == b.Fee.Denom + }) +} + // ValidateDisjointFeeRatios returns an error if one or more entries appears in both lists. func ValidateDisjointFeeRatios(field string, toAdd, toRemove []FeeRatio) error { shared := IntersectionOfFeeRatios(toAdd, toRemove) @@ -296,6 +308,30 @@ func ValidateDisjointFeeRatios(field string, toAdd, toRemove []FeeRatio) error { return nil } +// ValidateAddRemoveFeeRatiosWithExisting returns errors for entries in toAdd that are +// already in existing, and entries in toRemove that are not in existing. +func ValidateAddRemoveFeeRatiosWithExisting(field string, existing, toAdd, toRemove []FeeRatio) []error { + var errs []error + for _, ratio := range toRemove { + if !ContainsFeeRatio(existing, ratio) { + errs = append(errs, fmt.Errorf("cannot remove %s ratio fee %q: no such ratio exists", field, ratio)) + } + } + newRatios := make([]FeeRatio, 0, len(existing)) + for _, ratio := range existing { + if !ContainsFeeRatio(toRemove, ratio) { + newRatios = append(newRatios, ratio) + } + } + for _, ratio := range toAdd { + if ContainsSameFeeRatioDenoms(newRatios, ratio) { + errs = append(errs, fmt.Errorf("cannot add %s ratio fee %q: ratio with those denoms already exists", + field, ratio)) + } + } + return errs +} + // ValidateAddRemoveFeeOptions returns an error if the toAdd list has an invalid // entry or if the two lists have one or more common entries. func ValidateAddRemoveFeeOptions(field string, toAdd, toRemove []sdk.Coin) error { @@ -310,6 +346,29 @@ func ValidateAddRemoveFeeOptions(field string, toAdd, toRemove []sdk.Coin) error return errors.Join(errs...) } +// ValidateAddRemoveFeeOptionsWithExisting returns errors for entries in toAdd that are +// already in existing, and entries in toRemove that are not in existing. +func ValidateAddRemoveFeeOptionsWithExisting(field string, existing, toAdd, toRemove []sdk.Coin) []error { + var errs []error + for _, coin := range toRemove { + if !ContainsCoin(existing, coin) { + errs = append(errs, fmt.Errorf("cannot remove %s flat fee %q: no such fee exists", field, coin)) + } + } + newOpts := make([]sdk.Coin, 0, len(existing)) + for _, coin := range existing { + if !ContainsCoin(toRemove, coin) { + newOpts = append(newOpts, coin) + } + } + for _, coin := range toAdd { + if ContainsCoinWithSameDenom(newOpts, coin) { + errs = append(errs, fmt.Errorf("cannot add %s flat fee %q: fee with that denom already exists", field, coin)) + } + } + return errs +} + // ValidateAccessGrantsField returns an error if any of the provided access grants are invalid. // The provided field is used in error messages. func ValidateAccessGrantsField(field string, accessGrants []AccessGrant) error { diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 05f73488c5..bb8aacb575 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -1,6 +1,7 @@ package exchange import ( + "errors" "fmt" "sort" "strings" @@ -1293,6 +1294,242 @@ func TestIntersectionOfFeeRatios(t *testing.T) { } } +func TestContainsFeeRatio(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + ratio := func(price, fee sdk.Coin) FeeRatio { + return FeeRatio{Price: price, Fee: fee} + } + + tests := []struct { + name string + vals []FeeRatio + toFind FeeRatio + exp bool + }{ + { + name: "nil vals", + vals: nil, + toFind: ratio(coin(1, "pear"), coin(2, "fig")), + exp: false, + }, + { + name: "empty vals", + vals: []FeeRatio{}, + toFind: ratio(coin(1, "pear"), coin(2, "fig")), + exp: false, + }, + { + name: "one val, same to find", + vals: []FeeRatio{ratio(coin(1, "pear"), coin(2, "fig"))}, + toFind: ratio(coin(1, "pear"), coin(2, "fig")), + exp: true, + }, + { + name: "one val, diff price amount", + vals: []FeeRatio{ratio(coin(1, "pear"), coin(2, "fig"))}, + toFind: ratio(coin(3, "pear"), coin(2, "fig")), + exp: false, + }, + { + name: "one val, diff price denom", + vals: []FeeRatio{ratio(coin(1, "pear"), coin(2, "fig"))}, + toFind: ratio(coin(1, "prune"), coin(2, "fig")), + exp: false, + }, + { + name: "one val, diff fee amount", + vals: []FeeRatio{ratio(coin(1, "pear"), coin(2, "fig"))}, + toFind: ratio(coin(1, "pear"), coin(3, "fig")), + exp: false, + }, + { + name: "one val, diff fee denom", + vals: []FeeRatio{ratio(coin(1, "pear"), coin(2, "fig"))}, + toFind: ratio(coin(1, "pear"), coin(2, "grape")), + exp: false, + }, + { + name: "one bad val, same", + vals: []FeeRatio{ratio(coin(-5, ""), coin(-6, ""))}, + toFind: ratio(coin(-5, ""), coin(-6, "")), + exp: true, + }, + { + name: "three vals, not to find", + vals: []FeeRatio{ + ratio(coin(1, "pear"), coin(2, "fig")), + ratio(coin(1, "prune"), coin(3, "fig")), + ratio(coin(1, "plum"), coin(4, "fig")), + }, + toFind: ratio(coin(1, "pineapple"), coin(5, "fig")), + exp: false, + }, + { + name: "three vals, first", + vals: []FeeRatio{ + ratio(coin(1, "pear"), coin(2, "fig")), + ratio(coin(1, "prune"), coin(3, "fig")), + ratio(coin(1, "plum"), coin(4, "fig")), + }, + toFind: ratio(coin(1, "pear"), coin(2, "fig")), + exp: true, + }, + { + name: "three vals, second", + vals: []FeeRatio{ + ratio(coin(1, "pear"), coin(2, "fig")), + ratio(coin(1, "prune"), coin(3, "fig")), + ratio(coin(1, "plum"), coin(4, "fig")), + }, + toFind: ratio(coin(1, "prune"), coin(3, "fig")), + exp: true, + }, + { + name: "three vals, third", + vals: []FeeRatio{ + ratio(coin(1, "pear"), coin(2, "fig")), + ratio(coin(1, "prune"), coin(3, "fig")), + ratio(coin(1, "plum"), coin(4, "fig")), + }, + toFind: ratio(coin(1, "plum"), coin(4, "fig")), + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + tesFunc := func() { + actual = ContainsFeeRatio(tc.vals, tc.toFind) + } + require.NotPanics(t, tesFunc, "ContainsFeeRatio(%q, %q)", FeeRatiosString(tc.vals), tc.toFind) + assert.Equal(t, tc.exp, actual, "ContainsFeeRatio(%q, %q)", FeeRatiosString(tc.vals), tc.toFind) + }) + } +} + +func TestContainsSameFeeRatioDenoms(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + ratio := func(price, fee sdk.Coin) FeeRatio { + return FeeRatio{Price: price, Fee: fee} + } + + tests := []struct { + name string + vals []FeeRatio + toFind FeeRatio + exp bool + }{ + { + name: "nil vals", + vals: nil, + toFind: ratio(coin(1, "plum"), coin(2, "fig")), + exp: false, + }, + { + name: "empty vals", + vals: []FeeRatio{}, + toFind: ratio(coin(1, "plum"), coin(2, "fig")), + exp: false, + }, + { + name: "one val, same", + vals: []FeeRatio{ratio(coin(1, "plum"), coin(2, "fig"))}, + toFind: ratio(coin(1, "plum"), coin(2, "fig")), + exp: true, + }, + { + name: "one val, diff price amount", + vals: []FeeRatio{ratio(coin(1, "plum"), coin(2, "fig"))}, + toFind: ratio(coin(3, "plum"), coin(2, "fig")), + exp: true, + }, + { + name: "one val, diff price denom", + vals: []FeeRatio{ratio(coin(1, "plum"), coin(2, "fig"))}, + toFind: ratio(coin(1, "prune"), coin(2, "fig")), + exp: false, + }, + { + name: "one val, diff fee amount", + vals: []FeeRatio{ratio(coin(1, "plum"), coin(2, "fig"))}, + toFind: ratio(coin(1, "plum"), coin(3, "fig")), + exp: true, + }, + { + name: "one val, diff fee denom", + vals: []FeeRatio{ratio(coin(1, "plum"), coin(2, "fig"))}, + toFind: ratio(coin(1, "plum"), coin(2, "grape")), + exp: false, + }, + { + name: "three vals, not to find", + vals: []FeeRatio{ + ratio(coin(1, "prune"), coin(2, "fig")), + ratio(coin(3, "plum"), coin(4, "grape")), + ratio(coin(5, "peach"), coin(6, "honeydew")), + }, + toFind: ratio(coin(7, "pineapple"), coin(8, "jackfruit")), + exp: false, + }, + { + name: "three vals, first price denom second fee denom", + vals: []FeeRatio{ + ratio(coin(1, "prune"), coin(2, "fig")), + ratio(coin(3, "plum"), coin(4, "grape")), + ratio(coin(5, "peach"), coin(6, "honeydew")), + }, + toFind: ratio(coin(7, "prune"), coin(8, "grape")), + exp: false, + }, + { + name: "three vals, first", + vals: []FeeRatio{ + ratio(coin(1, "prune"), coin(2, "fig")), + ratio(coin(3, "plum"), coin(4, "grape")), + ratio(coin(5, "peach"), coin(6, "honeydew")), + }, + toFind: ratio(coin(7, "prune"), coin(8, "fig")), + exp: true, + }, + { + name: "three vals, second", + vals: []FeeRatio{ + ratio(coin(1, "prune"), coin(2, "fig")), + ratio(coin(3, "plum"), coin(4, "grape")), + ratio(coin(5, "peach"), coin(6, "honeydew")), + }, + toFind: ratio(coin(7, "plum"), coin(8, "grape")), + exp: true, + }, + { + name: "three vals, third", + vals: []FeeRatio{ + ratio(coin(1, "prune"), coin(2, "fig")), + ratio(coin(3, "plum"), coin(4, "grape")), + ratio(coin(5, "peach"), coin(6, "honeydew")), + }, + toFind: ratio(coin(7, "peach"), coin(8, "honeydew")), + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = ContainsSameFeeRatioDenoms(tc.vals, tc.toFind) + } + require.NotPanics(t, testFunc, "ContainsSameFeeRatioDenoms(%q, %q)", FeeRatiosString(tc.vals), tc.toFind) + assert.Equal(t, tc.exp, actual, "ContainsSameFeeRatioDenoms(%q, %q)", FeeRatiosString(tc.vals), tc.toFind) + }) + } +} + func TestValidateDisjointFeeRatios(t *testing.T) { feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { return FeeRatio{ @@ -1409,6 +1646,196 @@ func TestValidateDisjointFeeRatios(t *testing.T) { } } +func TestValidateAddRemoveFeeRatiosWithExisting(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + ratio := func(price, fee sdk.Coin) FeeRatio { + return FeeRatio{Price: price, Fee: fee} + } + joinErrs := func(errs ...string) string { + return strings.Join(errs, "\n") + } + noRemErr := func(field, fee string) string { + return fmt.Sprintf("cannot remove %s ratio fee %q: no such ratio exists", field, fee) + } + noAddErr := func(field, fee string) string { + return fmt.Sprintf("cannot add %s ratio fee %q: ratio with those denoms already exists", field, fee) + } + + tests := []struct { + name string + field string + existing []FeeRatio + toAdd []FeeRatio + toRemove []FeeRatio + expErr string + }{ + { + name: "nil existing", + field: "DLEIF", + existing: nil, + toAdd: []FeeRatio{ratio(coin(1, "pineapple"), coin(2, "grape"))}, + toRemove: []FeeRatio{ratio(coin(1, "pineapple"), coin(3, "fig"))}, + expErr: noRemErr("DLEIF", "1pineapple:3fig"), + }, + { + name: "empty existing", + field: "DLEIF", + existing: []FeeRatio{}, + toAdd: []FeeRatio{ratio(coin(1, "pineapple"), coin(2, "grape"))}, + toRemove: []FeeRatio{ratio(coin(1, "pineapple"), coin(3, "fig"))}, + expErr: noRemErr("DLEIF", "1pineapple:3fig"), + }, + { + name: "nil toAdd", + field: "DLEIF", + existing: []FeeRatio{ratio(coin(1, "pineapple"), coin(3, "fig"))}, + toAdd: nil, + toRemove: []FeeRatio{ratio(coin(1, "pineapple"), coin(3, "fig"))}, + expErr: "", + }, + { + name: "empty toAdd", + field: "DLEIF", + existing: []FeeRatio{ratio(coin(1, "pineapple"), coin(3, "fig"))}, + toAdd: []FeeRatio{}, + toRemove: []FeeRatio{ratio(coin(1, "pineapple"), coin(3, "fig"))}, + expErr: "", + }, + { + name: "nil toRemove", + field: "DLEIF", + existing: []FeeRatio{ratio(coin(1, "pineapple"), coin(3, "fig"))}, + toAdd: []FeeRatio{ratio(coin(1, "pineapple"), coin(2, "grape"))}, + toRemove: nil, + expErr: "", + }, + { + name: "empty toRemove", + field: "DLEIF", + existing: []FeeRatio{ratio(coin(1, "pineapple"), coin(3, "fig"))}, + toAdd: []FeeRatio{ratio(coin(1, "pineapple"), coin(2, "grape"))}, + toRemove: []FeeRatio{}, + expErr: "", + }, + { + name: "to remove not in existing, diff price amounts", + field: "DLEIF", + existing: []FeeRatio{ + ratio(coin(111, "pineapple"), coin(2, "fig")), + ratio(coin(333, "pineapple"), coin(4, "grape")), + }, + toAdd: nil, + toRemove: []FeeRatio{ + ratio(coin(112, "pineapple"), coin(2, "fig")), + ratio(coin(334, "pineapple"), coin(4, "grape")), + }, + expErr: joinErrs( + noRemErr("DLEIF", "112pineapple:2fig"), + noRemErr("DLEIF", "334pineapple:4grape"), + ), + }, + { + name: "to remove not in existing, diff price denoms", + field: "DleiF", + existing: []FeeRatio{ + ratio(coin(111, "prune"), coin(2, "fig")), + ratio(coin(333, "plum"), coin(4, "grape")), + }, + toAdd: nil, + toRemove: []FeeRatio{ + ratio(coin(111, "plum"), coin(2, "fig")), + ratio(coin(333, "prune"), coin(4, "grape")), + }, + expErr: joinErrs( + noRemErr("DleiF", "111plum:2fig"), + noRemErr("DleiF", "333prune:4grape"), + ), + }, + { + name: "to remove not in existing, diff fee amounts", + field: "DleiF", + existing: []FeeRatio{ + ratio(coin(111, "prune"), coin(2, "fig")), + ratio(coin(333, "plum"), coin(4, "grape")), + }, + toAdd: nil, + toRemove: []FeeRatio{ + ratio(coin(111, "prune"), coin(3, "fig")), + ratio(coin(333, "plum"), coin(5, "grape")), + }, + expErr: joinErrs( + noRemErr("DleiF", "111prune:3fig"), + noRemErr("DleiF", "333plum:5grape"), + ), + }, + { + name: "to remove not in existing, diff fee denoms", + field: "DleiF", + existing: []FeeRatio{ + ratio(coin(111, "prune"), coin(2, "fig")), + ratio(coin(333, "plum"), coin(4, "grape")), + }, + toAdd: nil, + toRemove: []FeeRatio{ + ratio(coin(111, "prune"), coin(2, "grape")), + ratio(coin(333, "plum"), coin(4, "fig")), + }, + expErr: joinErrs( + noRemErr("DleiF", "111prune:2grape"), + noRemErr("DleiF", "333plum:4fig"), + ), + }, + { + name: "to add already exists", + field: "yard", + existing: []FeeRatio{ + ratio(coin(111, "prune"), coin(2, "fig")), + ratio(coin(333, "plum"), coin(4, "grape")), + ratio(coin(555, "peach"), coin(6, "honeydew")), + }, + toAdd: []FeeRatio{ + ratio(coin(321, "prune"), coin(7, "fig")), + ratio(coin(654, "peach"), coin(8, "honeydew")), + }, + toRemove: []FeeRatio{ratio(coin(333, "plum"), coin(4, "grape"))}, + expErr: joinErrs( + noAddErr("yard", "321prune:7fig"), + noAddErr("yard", "654peach:8honeydew"), + ), + }, + { + name: "remove one add another with same denoms", + field: "lawn", + existing: []FeeRatio{ + ratio(coin(111, "prune"), coin(2, "fig")), + ratio(coin(333, "plum"), coin(4, "grape")), + ratio(coin(555, "peach"), coin(6, "honeydew")), + }, + toAdd: []FeeRatio{ + ratio(coin(321, "plum"), coin(1, "grape")), + }, + toRemove: []FeeRatio{ + ratio(coin(333, "plum"), coin(4, "grape")), + }, + expErr: "", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var errs []error + testFunc := func() { + errs = ValidateAddRemoveFeeRatiosWithExisting(tc.field, tc.existing, tc.toAdd, tc.toRemove) + } + require.NotPanics(t, testFunc, "ValidateAddRemoveFeeRatiosWithExisting") + err := errors.Join(errs...) + assertions.AssertErrorValue(t, err, tc.expErr, "ValidateAddRemoveFeeRatiosWithExisting error") + }) + } +} + func TestValidateAddRemoveFeeOptions(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} @@ -1548,6 +1975,140 @@ func TestValidateAddRemoveFeeOptions(t *testing.T) { } } +func TestValidateAddRemoveFeeOptionsWithExisting(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + joinErrs := func(errs ...string) string { + return strings.Join(errs, "\n") + } + noRemErr := func(field, fee string) string { + return fmt.Sprintf("cannot remove %s flat fee %q: no such fee exists", field, fee) + } + noAddErr := func(field, fee string) string { + return fmt.Sprintf("cannot add %s flat fee %q: fee with that denom already exists", field, fee) + } + + tests := []struct { + name string + field string + existing []sdk.Coin + toAdd []sdk.Coin + toRemove []sdk.Coin + expErr string + }{ + { + name: "nil existing", + field: "tree", + existing: nil, + toAdd: []sdk.Coin{coin(1, "apple")}, + toRemove: []sdk.Coin{coin(2, "banana")}, + expErr: noRemErr("tree", "2banana"), + }, + { + name: "empty existing", + field: "tree", + existing: []sdk.Coin{}, + toAdd: []sdk.Coin{coin(1, "apple")}, + toRemove: []sdk.Coin{coin(2, "banana")}, + expErr: noRemErr("tree", "2banana"), + }, + { + name: "nil toAdd", + field: "fern", + existing: []sdk.Coin{coin(2, "banana")}, + toAdd: nil, + toRemove: []sdk.Coin{coin(2, "banana")}, + expErr: "", + }, + { + name: "empty toAdd", + field: "fern", + existing: []sdk.Coin{coin(2, "banana")}, + toAdd: []sdk.Coin{}, + toRemove: []sdk.Coin{coin(2, "banana")}, + expErr: "", + }, + { + name: "nil toRemove", + field: "yucca", + existing: []sdk.Coin{coin(2, "apple")}, + toAdd: []sdk.Coin{coin(2, "banana")}, + toRemove: nil, + expErr: "", + }, + { + name: "empty toRemove", + field: "yucca", + existing: []sdk.Coin{coin(2, "apple")}, + toAdd: []sdk.Coin{coin(2, "banana")}, + toRemove: []sdk.Coin{}, + expErr: "", + }, + { + name: "to remove not in existing, diff amounts", + field: "rose", + existing: []sdk.Coin{coin(1, "apple"), coin(2, "banana")}, + toAdd: nil, + toRemove: []sdk.Coin{coin(3, "apple"), coin(4, "banana")}, + expErr: joinErrs( + noRemErr("rose", "3apple"), + noRemErr("rose", "4banana"), + ), + }, + { + name: "to remove not in existing, diff denoms", + field: "rose", + existing: []sdk.Coin{coin(1, "apple"), coin(2, "banana")}, + toAdd: nil, + toRemove: []sdk.Coin{coin(1, "cactus"), coin(2, "durian")}, + expErr: joinErrs( + noRemErr("rose", "1cactus"), + noRemErr("rose", "2durian"), + ), + }, + { + name: "to remove, in existing", + field: "rose", + existing: []sdk.Coin{coin(1, "apple"), coin(2, "banana")}, + toAdd: nil, + toRemove: []sdk.Coin{coin(1, "apple"), coin(2, "banana")}, + expErr: "", + }, + { + name: "to add denom already exists", + field: "tuLip", + existing: []sdk.Coin{coin(1, "apple"), coin(2, "banana")}, + toAdd: []sdk.Coin{coin(3, "apple"), coin(4, "banana")}, + toRemove: nil, + expErr: joinErrs( + noAddErr("tuLip", "3apple"), + noAddErr("tuLip", "4banana"), + ), + }, + { + name: "remove and add same denom", + field: "whoops", + existing: []sdk.Coin{coin(1, "apple"), coin(2, "banana")}, + toAdd: []sdk.Coin{coin(3, "apple")}, + toRemove: []sdk.Coin{coin(1, "apple")}, + expErr: "", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var errs []error + testFunc := func() { + errs = ValidateAddRemoveFeeOptionsWithExisting(tc.field, tc.existing, tc.toAdd, tc.toRemove) + } + require.NotPanics(t, testFunc, "ValidateAddRemoveFeeOptionsWithExisting") + err := errors.Join(errs...) + assertions.AssertErrorValue(t, err, tc.expErr, "ValidateAddRemoveFeeOptionsWithExisting error") + }) + } +} + func TestValidateAccessGrantsField(t *testing.T) { joinErrs := func(errs ...string) string { return strings.Join(errs, "\n") From 1a0ef9a37e905436b1f4dbdd92c28ea4c0b17c55 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 3 Oct 2023 11:26:44 -0600 Subject: [PATCH 225/309] [1658]: Pull ratio price denoms check out into its own function. In QueryValidateManageFees, update the fees and then validate the market too. --- x/exchange/keeper/grpc_query.go | 23 ++++-- x/exchange/keeper/market.go | 36 +------- x/exchange/keeper/msg_server.go | 1 + x/exchange/market.go | 39 +++++++++ x/exchange/market_test.go | 141 ++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 43 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 1cd0e1e499..d4635d07b8 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -273,25 +273,23 @@ func (k QueryServer) QueryValidateCreateMarket(goCtx context.Context, req *excha // The SDK *should* already be using a cache context for queries, but I'm doing it here too just to be on the safe side. ctx, _ := sdk.UnwrapSDKContext(goCtx).CacheContext() - var marketID uint32 - if newMarketID, err := k.CreateMarket(ctx, msg.Market); err != nil { + marketID, err := k.CreateMarket(ctx, msg.Market) + if err != nil { resp.Error = err.Error() return resp, nil - } else { - marketID = newMarketID } resp.GovPropWillPass = true var errs []error - if err := exchange.ValidateReqAttrsAreNormalized("create ask", msg.Market.ReqAttrCreateAsk); err != nil { + if err = exchange.ValidateReqAttrsAreNormalized("create ask", msg.Market.ReqAttrCreateAsk); err != nil { errs = append(errs, err) } - if err := exchange.ValidateReqAttrsAreNormalized("create bid", msg.Market.ReqAttrCreateBid); err != nil { + if err = exchange.ValidateReqAttrsAreNormalized("create bid", msg.Market.ReqAttrCreateBid); err != nil { errs = append(errs, err) } - if err := k.ValidateMarket(ctx, marketID); err != nil { + if err = k.ValidateMarket(ctx, marketID); err != nil { errs = append(errs, err) } @@ -336,7 +334,8 @@ func (k QueryServer) QueryValidateManageFees(goCtx context.Context, req *exchang return resp, nil } - ctx := sdk.UnwrapSDKContext(goCtx) + // The SDK *should* already be using a cache context for queries, but I'm doing it here too just to be on the safe side. + ctx, _ := sdk.UnwrapSDKContext(goCtx).CacheContext() store := k.getStore(ctx) if err := validateMarketExists(store, msg.MarketId); err != nil { resp.Error = err.Error() @@ -382,6 +381,14 @@ func (k QueryServer) QueryValidateManageFees(goCtx context.Context, req *exchang buyerRatios, msg.AddFeeBuyerSettlementRatios, msg.RemoveFeeBuyerSettlementRatios)...) } + if err := k.UpdateFees(ctx, msg); err != nil { + // The only error this might be would be about event emission. + errs = append(errs, err) + } + if err := k.ValidateMarket(ctx, msg.MarketId); err != nil { + errs = append(errs, err) + } + if len(errs) > 0 { resp.Error = errors.Join(errs...).Error() } diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index aa5ee2059a..71c378b7d1 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -1264,42 +1264,8 @@ func (k Keeper) ValidateMarket(ctx sdk.Context, marketID uint32) error { return err } - var errs []error - sellerRatios := getSellerSettlementRatios(store, marketID) buyerRatios := getBuyerSettlementRatios(store, marketID) - if len(sellerRatios) > 0 && len(buyerRatios) > 0 { - // We only need to check the price denoms if *both* types have an entry. - sellerPriceDenoms := make([]string, len(sellerRatios)) - sellerPriceDenomsKnown := make(map[string]bool) - for i, ratio := range sellerRatios { - sellerPriceDenoms[i] = ratio.Price.Denom - sellerPriceDenomsKnown[ratio.Price.Denom] = true - } - - buyerPriceDenoms := make([]string, 0, len(sellerRatios)) - buyerPriceDenomsKnown := make(map[string]bool) - for _, ratio := range buyerRatios { - if !buyerPriceDenomsKnown[ratio.Price.Denom] { - buyerPriceDenoms = append(buyerPriceDenoms, ratio.Price.Denom) - buyerPriceDenomsKnown[ratio.Price.Denom] = true - } - } - - for _, denom := range sellerPriceDenoms { - if !buyerPriceDenomsKnown[denom] { - errs = append(errs, fmt.Errorf("seller settlement fee ratios have price denom %q "+ - "but there are no buyer settlement fee ratios with that price denom", denom)) - } - } - - for _, denom := range buyerPriceDenoms { - if !sellerPriceDenomsKnown[denom] { - errs = append(errs, fmt.Errorf("buyer settlement fee ratios have price denom %q "+ - "but there is not a seller settlement fee ratio with that price denom", denom)) - } - } - } - + errs := exchange.ValidateRatioDenoms(sellerRatios, buyerRatios) return errors.Join(errs...) } diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 75e5e499be..a54cfbbc70 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/provenance-io/provenance/x/exchange" ) diff --git a/x/exchange/market.go b/x/exchange/market.go index 4f9f8fab78..f1e2159dd6 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -332,6 +332,45 @@ func ValidateAddRemoveFeeRatiosWithExisting(field string, existing, toAdd, toRem return errs } +// ValidateRatioDenoms checks that the buyer and seller ratios have the same price denoms. +func ValidateRatioDenoms(sellerRatios, buyerRatios []FeeRatio) []error { + var errs []error + if len(sellerRatios) > 0 && len(buyerRatios) > 0 { + // We only need to check the price denoms if *both* types have an entry. + sellerPriceDenoms := make([]string, len(sellerRatios)) + sellerPriceDenomsKnown := make(map[string]bool) + for i, ratio := range sellerRatios { + sellerPriceDenoms[i] = ratio.Price.Denom + sellerPriceDenomsKnown[ratio.Price.Denom] = true + } + + buyerPriceDenoms := make([]string, 0, len(sellerRatios)) + buyerPriceDenomsKnown := make(map[string]bool) + for _, ratio := range buyerRatios { + if !buyerPriceDenomsKnown[ratio.Price.Denom] { + buyerPriceDenoms = append(buyerPriceDenoms, ratio.Price.Denom) + buyerPriceDenomsKnown[ratio.Price.Denom] = true + } + } + + for _, denom := range sellerPriceDenoms { + if !buyerPriceDenomsKnown[denom] { + errs = append(errs, fmt.Errorf("seller settlement fee ratios have price denom %q "+ + "but there are no buyer settlement fee ratios with that price denom", denom)) + } + } + + for _, denom := range buyerPriceDenoms { + if !sellerPriceDenomsKnown[denom] { + errs = append(errs, fmt.Errorf("buyer settlement fee ratios have price denom %q "+ + "but there is not a seller settlement fee ratio with that price denom", denom)) + } + } + } + + return errs +} + // ValidateAddRemoveFeeOptions returns an error if the toAdd list has an invalid // entry or if the two lists have one or more common entries. func ValidateAddRemoveFeeOptions(field string, toAdd, toRemove []sdk.Coin) error { diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index bb8aacb575..ebed1e4be8 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -1836,6 +1836,147 @@ func TestValidateAddRemoveFeeRatiosWithExisting(t *testing.T) { } } +func TestValidateRatioDenoms(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + ratio := func(price, fee sdk.Coin) FeeRatio { + return FeeRatio{Price: price, Fee: fee} + } + joinErrs := func(errs ...string) string { + return strings.Join(errs, "\n") + } + noBuyerDenomErr := func(denom string) string { + return fmt.Sprintf("seller settlement fee ratios have price denom %q but there "+ + "are no buyer settlement fee ratios with that price denom", denom) + } + noSellerDenomErr := func(denom string) string { + return fmt.Sprintf("buyer settlement fee ratios have price denom %q but there "+ + "is not a seller settlement fee ratio with that price denom", denom) + } + + tests := []struct { + name string + seller []FeeRatio + buyer []FeeRatio + expErr string + }{ + {name: "nil seller, nil buyer", seller: nil, buyer: nil, expErr: ""}, + {name: "empty seller, nil buyer", seller: []FeeRatio{}, buyer: nil, expErr: ""}, + {name: "nil seller, empty buyer", seller: nil, buyer: []FeeRatio{}, expErr: ""}, + {name: "empty seller, empty buyer", seller: []FeeRatio{}, buyer: []FeeRatio{}, expErr: ""}, + { + name: "nil seller, 2 buyer", + seller: nil, + buyer: []FeeRatio{ + ratio(coin(1, "apple"), coin(2, "fig")), + ratio(coin(3, "cucumber"), coin(4, "dill")), + }, + expErr: "", + }, + { + name: "empty seller, 2 buyer", + seller: []FeeRatio{}, + buyer: []FeeRatio{ + ratio(coin(1, "apple"), coin(2, "fig")), + ratio(coin(3, "cucumber"), coin(4, "dill")), + }, + expErr: "", + }, + { + name: "2 seller, nil buyer", + seller: []FeeRatio{ + ratio(coin(1, "apple"), coin(2, "fig")), + ratio(coin(3, "cucumber"), coin(4, "dill")), + }, + buyer: nil, + expErr: "", + }, + { + name: "2 seller, empty buyer", + seller: []FeeRatio{ + ratio(coin(1, "apple"), coin(2, "fig")), + ratio(coin(3, "cucumber"), coin(4, "dill")), + }, + buyer: []FeeRatio{}, + expErr: "", + }, + { + name: "1 seller, 1 buyer: different", + seller: []FeeRatio{ratio(coin(1, "apple"), coin(2, "banana"))}, + buyer: []FeeRatio{ratio(coin(3, "cucumber"), coin(4, "dill"))}, + expErr: joinErrs(noBuyerDenomErr("apple"), noSellerDenomErr("cucumber")), + }, + { + name: "2 seller, 2 buyer: all different", + seller: []FeeRatio{ + ratio(coin(1, "apple"), coin(2, "banana")), + ratio(coin(3, "cucumber"), coin(4, "dill")), + }, + buyer: []FeeRatio{ + ratio(coin(5, "eggplant"), coin(6, "fig")), + ratio(coin(7, "grape"), coin(8, "honeydew")), + }, + expErr: joinErrs( + noBuyerDenomErr("apple"), + noBuyerDenomErr("cucumber"), + noSellerDenomErr("eggplant"), + noSellerDenomErr("grape"), + ), + }, + { + name: "1 seller, 2 buyer with same: same", + seller: []FeeRatio{ratio(coin(1, "apple"), coin(2, "banana"))}, + buyer: []FeeRatio{ + ratio(coin(5, "apple"), coin(6, "fig")), + ratio(coin(7, "apple"), coin(8, "honeydew")), + }, + expErr: "", + }, + { + name: "1 seller, 2 buyer with same: different", + seller: []FeeRatio{ratio(coin(1, "eggplant"), coin(2, "banana"))}, + buyer: []FeeRatio{ + ratio(coin(5, "apple"), coin(6, "fig")), + ratio(coin(7, "apple"), coin(8, "honeydew")), + }, + expErr: joinErrs(noBuyerDenomErr("eggplant"), noSellerDenomErr("apple")), + }, + { + name: "1 seller, 2 buyer: 1 buyer missing", + seller: []FeeRatio{ratio(coin(1, "apple"), coin(2, "banana"))}, + buyer: []FeeRatio{ + ratio(coin(5, "cucumber"), coin(6, "fig")), + ratio(coin(7, "apple"), coin(8, "honeydew")), + }, + expErr: noSellerDenomErr("cucumber"), + }, + { + name: "2 seller, 1 buyer", + seller: []FeeRatio{ + ratio(coin(5, "apple"), coin(6, "banana")), + ratio(coin(7, "cucumber"), coin(8, "dill")), + }, + buyer: []FeeRatio{ratio(coin(1, "apple"), coin(2, "grape"))}, + expErr: noBuyerDenomErr("cucumber"), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var errs []error + testFunc := func() { + errs = ValidateRatioDenoms(tc.seller, tc.buyer) + } + require.NotPanics(t, testFunc, "ValidateRatioDenoms(%q, %q)", + FeeRatiosString(tc.seller), FeeRatiosString(tc.buyer)) + err := errors.Join(errs...) + assertions.AssertErrorValue(t, err, tc.expErr, "ValidateRatioDenoms(%q, %q)", + FeeRatiosString(tc.seller), FeeRatiosString(tc.buyer)) + }) + } +} + func TestValidateAddRemoveFeeOptions(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} From c922a1bea2025926c0ed802c38f021b43b544fee Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 3 Oct 2023 14:03:38 -0600 Subject: [PATCH 226/309] [1658]: Reorganize keeper/market.go and tweak a few of the function names for consistency. Also fix a couple permission checks that were checking the wrong permission. --- x/exchange/keeper/market.go | 1231 ++++++++++++++++--------------- x/exchange/keeper/msg_server.go | 4 +- x/exchange/keeper/orders.go | 2 +- 3 files changed, 630 insertions(+), 607 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 71c378b7d1..9b61a45031 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -13,7 +13,64 @@ import ( "github.com/provenance-io/provenance/x/exchange" ) -// TODO[1658]: Recheck all the public functions in here to make sure they're still needed. +// getLastAutoMarketID gets the last auto-selected market id. +func getLastAutoMarketID(store sdk.KVStore) uint32 { + key := MakeKeyLastMarketID() + value := store.Get(key) + rv, _ := uint32FromBz(value) + return rv +} + +// setLastAutoMarketID sets the last auto-selected market id to the provided value. +func setLastAutoMarketID(store sdk.KVStore, marketID uint32) { + key := MakeKeyLastMarketID() + value := uint32Bz(marketID) + store.Set(key, value) +} + +// NextMarketID finds the next available market id, updates the last auto-selected +// market id store entry, and returns the unused id it found. +func (k Keeper) NextMarketID(ctx sdk.Context) uint32 { + store := k.getStore(ctx) + marketID := getLastAutoMarketID(store) + 1 + for { + key := MakeKeyKnownMarketID(marketID) + if !store.Has(key) { + break + } + marketID++ + } + setLastAutoMarketID(store, marketID) + return marketID +} + +// isMarketKnown returns true if the provided market id is a market that exists. +func isMarketKnown(store sdk.KVStore, marketID uint32) bool { + key := MakeKeyKnownMarketID(marketID) + return store.Has(key) +} + +// setMarketKnown sets the known market id indicator in the store. +func setMarketKnown(store sdk.KVStore, marketID uint32) { + key := MakeKeyKnownMarketID(marketID) + store.Set(key, nil) +} + +// validateMarketExists returns an error if the provided marketID does not exist. +func validateMarketExists(store sdk.KVStore, marketID uint32) error { + if !isMarketKnown(store, marketID) { + return fmt.Errorf("market %d does not exist", marketID) + } + return nil +} + +// IterateKnownMarketIDs iterates over all known market ids. +func (k Keeper) IterateKnownMarketIDs(ctx sdk.Context, cb func(marketID uint32) bool) { + k.iterate(ctx, GetKeyPrefixKnownMarketID(), func(key, _ []byte) bool { + marketID, ok := ParseKeySuffixKnownMarketID(key) + return ok && cb(marketID) + }) +} // flatFeeKeyMakers are the key and prefix maker functions for a specific flat fee entry. type flatFeeKeyMakers struct { @@ -21,12 +78,6 @@ type flatFeeKeyMakers struct { prefix func(marketID uint32) []byte } -// ratioKeyMakers are the key and prefix maker functions for a specific ratio fee entry. -type ratioKeyMakers struct { - key func(marketID uint32, ratio exchange.FeeRatio) []byte - prefix func(marketID uint32) []byte -} - var ( // createAskFlatKeyMakers are the key and prefix makers for the create-ask flat fees. createAskFlatKeyMakers = flatFeeKeyMakers{ @@ -43,21 +94,11 @@ var ( key: MakeKeyMarketSellerSettlementFlatFee, prefix: GetKeyPrefixMarketSellerSettlementFlatFee, } - // sellerSettlementRatioKeyMakers are the key and prefix makers for the seller settlement fee ratios. - sellerSettlementRatioKeyMakers = ratioKeyMakers{ - key: MakeKeyMarketSellerSettlementRatio, - prefix: GetKeyPrefixMarketSellerSettlementRatio, - } // sellerSettlementFlatKeyMakers are the key and prefix makers for the buyer settlement flat fees. buyerSettlementFlatKeyMakers = flatFeeKeyMakers{ key: MakeKeyMarketBuyerSettlementFlatFee, prefix: GetKeyPrefixMarketBuyerSettlementFlatFee, } - // buyerSettlementRatioKeyMakers are the key and prefix makers for the buyer settlement fee ratios. - buyerSettlementRatioKeyMakers = ratioKeyMakers{ - key: MakeKeyMarketBuyerSettlementRatio, - prefix: GetKeyPrefixMarketBuyerSettlementRatio, - } ) // hasFlatFee returns true if this market has any flat fee for a given type. @@ -90,6 +131,26 @@ func setFlatFee(store sdk.KVStore, marketID uint32, coin sdk.Coin, maker flatFee store.Set(key, []byte(value)) } +// validateFlatFee returns an error if the provided fee is not sufficient to cover the required flat fee. +func validateFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin, name string, maker flatFeeKeyMakers) error { + if !hasFlatFee(store, marketID, maker) { + return nil + } + if fee == nil { + opts := getAllFlatFees(store, marketID, maker) + return fmt.Errorf("no %s fee provided, must be one of: %s", name, sdk.NewCoins(opts...).String()) + } + reqFee := getFlatFee(store, marketID, fee.Denom, maker) + if reqFee == nil { + opts := getAllFlatFees(store, marketID, maker) + return fmt.Errorf("invalid %s fee, must be one of: %s", name, sdk.NewCoins(opts...).String()) + } + if fee.Amount.LT(reqFee.Amount) { + return fmt.Errorf("insufficient %s fee: %q is less than required amount %q", name, fee, reqFee) + } + return nil +} + // getAllFlatFees gets all the coin entries from the store with the given prefix. // The denom comes from the part of the key after the prefix, and the amount comes from the values. func getAllFlatFees(store sdk.KVStore, marketID uint32, maker flatFeeKeyMakers) []sdk.Coin { @@ -125,25 +186,99 @@ func updateFlatFees(store sdk.KVStore, marketID uint32, toDelete, toWrite []sdk. } } -// validateFlatFee returns an error if the provided fee is not sufficient to cover the required flat fee. -func validateFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin, name string, maker flatFeeKeyMakers) error { - if !hasFlatFee(store, marketID, maker) { - return nil - } - if fee == nil { - opts := getAllFlatFees(store, marketID, maker) - return fmt.Errorf("no %s fee provided, must be one of: %s", name, sdk.NewCoins(opts...).String()) - } - reqFee := getFlatFee(store, marketID, fee.Denom, maker) - if reqFee == nil { - opts := getAllFlatFees(store, marketID, maker) - return fmt.Errorf("invalid %s fee, must be one of: %s", name, sdk.NewCoins(opts...).String()) +// validateCreateAskFlatFee returns an error if the provided fee is not a sufficient create-ask flat fee. +func validateCreateAskFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin) error { + return validateFlatFee(store, marketID, fee, "ask order creation", createAskFlatKeyMakers) +} + +// getCreateAskFlatFees gets the create-ask flat fee options for a market. +func getCreateAskFlatFees(store sdk.KVStore, marketID uint32) []sdk.Coin { + return getAllFlatFees(store, marketID, createAskFlatKeyMakers) +} + +// setCreateAskFlatFees sets the create-ask flat fees for a market. +func setCreateAskFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin) { + setAllFlatFees(store, marketID, options, createAskFlatKeyMakers) +} + +// updateCreateAskFlatFees deletes all create-ask flat fees to delete then adds the ones to add. +func updateCreateAskFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd []sdk.Coin) { + updateFlatFees(store, marketID, toDelete, toAdd, createAskFlatKeyMakers) +} + +// validateCreateBidFlatFee returns an error if the provided fee is not a sufficient create -bid flat fee. +func validateCreateBidFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin) error { + return validateFlatFee(store, marketID, fee, "bid order creation", createBidFlatKeyMakers) +} + +// getCreateBidFlatFees gets the create-bid flat fee options for a market. +func getCreateBidFlatFees(store sdk.KVStore, marketID uint32) []sdk.Coin { + return getAllFlatFees(store, marketID, createBidFlatKeyMakers) +} + +// setCreateBidFlatFees sets the create-bid flat fees for a market. +func setCreateBidFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin) { + setAllFlatFees(store, marketID, options, createBidFlatKeyMakers) +} + +// updateCreateBidFlatFees deletes all create-bid flat fees to delete then adds the ones to add. +func updateCreateBidFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd []sdk.Coin) { + updateFlatFees(store, marketID, toDelete, toAdd, createBidFlatKeyMakers) +} + +// validateSellerSettlementFlatFee returns an error if the provided fee is not a sufficient seller settlement flat fee. +func validateSellerSettlementFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin) error { + return validateFlatFee(store, marketID, fee, "seller settlement flat", createBidFlatKeyMakers) +} + +// getSellerSettlementFlatFees gets the seller settlement flat fee options for a market. +func getSellerSettlementFlatFees(store sdk.KVStore, marketID uint32) []sdk.Coin { + return getAllFlatFees(store, marketID, sellerSettlementFlatKeyMakers) +} + +// setSellerSettlementFlatFees sets the seller settlement flat fees for a market. +func setSellerSettlementFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin) { + setAllFlatFees(store, marketID, options, sellerSettlementFlatKeyMakers) +} + +// updateSellerSettlementFlatFees deletes all seller settlement flat fees to delete then adds the ones to add. +func updateSellerSettlementFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd []sdk.Coin) { + updateFlatFees(store, marketID, toDelete, toAdd, sellerSettlementFlatKeyMakers) +} + +// getBuyerSettlementFlatFees gets the buyer settlement flat fee options for a market. +func getBuyerSettlementFlatFees(store sdk.KVStore, marketID uint32) []sdk.Coin { + return getAllFlatFees(store, marketID, buyerSettlementFlatKeyMakers) +} + +// setBuyerSettlementFlatFees sets the buyer settlement flat fees for a market. +func setBuyerSettlementFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin) { + setAllFlatFees(store, marketID, options, buyerSettlementFlatKeyMakers) +} + +// updateBuyerSettlementFlatFees deletes all buyer settlement flat fees to delete then adds the ones to add. +func updateBuyerSettlementFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd []sdk.Coin) { + updateFlatFees(store, marketID, toDelete, toAdd, buyerSettlementFlatKeyMakers) +} + +// ratioKeyMakers are the key and prefix maker functions for a specific ratio fee entry. +type ratioKeyMakers struct { + key func(marketID uint32, ratio exchange.FeeRatio) []byte + prefix func(marketID uint32) []byte +} + +var ( + // sellerSettlementRatioKeyMakers are the key and prefix makers for the seller settlement fee ratios. + sellerSettlementRatioKeyMakers = ratioKeyMakers{ + key: MakeKeyMarketSellerSettlementRatio, + prefix: GetKeyPrefixMarketSellerSettlementRatio, } - if fee.Amount.LT(reqFee.Amount) { - return fmt.Errorf("insufficient %s fee: %q is less than required amount %q", name, fee, reqFee) + // buyerSettlementRatioKeyMakers are the key and prefix makers for the buyer settlement fee ratios. + buyerSettlementRatioKeyMakers = ratioKeyMakers{ + key: MakeKeyMarketBuyerSettlementRatio, + prefix: GetKeyPrefixMarketBuyerSettlementRatio, } - return nil -} +) // hasFeeRatio returns true if this market has any fee ratios for a given type. func hasFeeRatio(store sdk.KVStore, marketID uint32, maker ratioKeyMakers) bool { @@ -219,142 +354,62 @@ func updateFeeRatios(store sdk.KVStore, marketID uint32, toDelete, toWrite []exc } } -// getCreateAskFlatFees gets the create-ask flat fee options for a market. -func getCreateAskFlatFees(store sdk.KVStore, marketID uint32) []sdk.Coin { - return getAllFlatFees(store, marketID, createAskFlatKeyMakers) +// getSellerSettlementRatio gets the seller settlement fee ratio for the given market with the provided denom. +func getSellerSettlementRatio(store sdk.KVStore, marketID uint32, priceDenom string) (*exchange.FeeRatio, error) { + ratio := getFeeRatio(store, marketID, priceDenom, priceDenom, sellerSettlementRatioKeyMakers) + if ratio == nil { + if hasFeeRatio(store, marketID, sellerSettlementRatioKeyMakers) { + return nil, fmt.Errorf("no seller settlement fee ratio found for denom %q", priceDenom) + } + } + return ratio, nil } -// GetCreateAskFlatFees gets the create-ask flat fee options for a market. -func (k Keeper) GetCreateAskFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return getCreateAskFlatFees(k.getStore(ctx), marketID) +// getSellerSettlementRatios gets the seller settlement fee ratios for a market. +func getSellerSettlementRatios(store sdk.KVStore, marketID uint32) []exchange.FeeRatio { + return getAllFeeRatios(store, marketID, sellerSettlementRatioKeyMakers) } -// setCreateAskFlatFees sets the create-ask flat fees for a market. -func setCreateAskFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin) { - setAllFlatFees(store, marketID, options, createAskFlatKeyMakers) +// setSellerSettlementRatios sets the seller settlement fee ratios for a market. +func setSellerSettlementRatios(store sdk.KVStore, marketID uint32, ratios []exchange.FeeRatio) { + setAllFeeRatios(store, marketID, ratios, sellerSettlementRatioKeyMakers) } -// updateCreateAskFlatFees deletes all create-ask flat fees to delete then adds the ones to add. -func updateCreateAskFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd []sdk.Coin) { - updateFlatFees(store, marketID, toDelete, toAdd, createAskFlatKeyMakers) +// updateSellerSettlementRatios deletes all seller settlement ratio entries to delete then adds the ones to add. +func updateSellerSettlementRatios(store sdk.KVStore, marketID uint32, toDelete, toAdd []exchange.FeeRatio) { + updateFeeRatios(store, marketID, toDelete, toAdd, sellerSettlementRatioKeyMakers) } -// validateCreateAskFlatFee returns an error if the provided fee is not a sufficient create ask flat fee. -func validateCreateAskFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin) error { - return validateFlatFee(store, marketID, fee, "ask order creation", createAskFlatKeyMakers) -} - -// getCreateBidFlatFees gets the create-bid flat fee options for a market. -func getCreateBidFlatFees(store sdk.KVStore, marketID uint32) []sdk.Coin { - return getAllFlatFees(store, marketID, createBidFlatKeyMakers) -} - -// GetCreateBidFlatFees gets the create-bid flat fee options for a market. -func (k Keeper) GetCreateBidFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return getCreateBidFlatFees(k.getStore(ctx), marketID) -} - -// setCreateBidFlatFees sets the create-bid flat fees for a market. -func setCreateBidFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin) { - setAllFlatFees(store, marketID, options, createBidFlatKeyMakers) -} - -// updateCreateBidFlatFees deletes all create-bid flat fees to delete then adds the ones to add. -func updateCreateBidFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd []sdk.Coin) { - updateFlatFees(store, marketID, toDelete, toAdd, createBidFlatKeyMakers) -} - -// validateCreateBidFlatFee returns an error if the provided fee is not a sufficient create bid flat fee. -func validateCreateBidFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin) error { - return validateFlatFee(store, marketID, fee, "bid order creation", createBidFlatKeyMakers) -} - -// getSellerSettlementFlatFees gets the seller settlement flat fee options for a market. -func getSellerSettlementFlatFees(store sdk.KVStore, marketID uint32) []sdk.Coin { - return getAllFlatFees(store, marketID, sellerSettlementFlatKeyMakers) -} - -// GetSellerSettlementFlatFees gets the seller settlement flat fee options for a market. -func (k Keeper) GetSellerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return getSellerSettlementFlatFees(k.getStore(ctx), marketID) -} - -// setSellerSettlementFlatFees sets the seller settlement flat fees for a market. -func setSellerSettlementFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin) { - setAllFlatFees(store, marketID, options, sellerSettlementFlatKeyMakers) -} - -// updateSellerSettlementFlatFees deletes all seller settlement flat fees to delete then adds the ones to add. -func updateSellerSettlementFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd []sdk.Coin) { - updateFlatFees(store, marketID, toDelete, toAdd, sellerSettlementFlatKeyMakers) -} - -// validateSellerSettlementFlatFee returns an error if the provided fee is not a sufficient seller settlement flat fee. -func validateSellerSettlementFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin) error { - return validateFlatFee(store, marketID, fee, "seller settlement flat", createBidFlatKeyMakers) -} - -// getSellerSettlementRatios gets the seller settlement fee ratios for a market. -func getSellerSettlementRatios(store sdk.KVStore, marketID uint32) []exchange.FeeRatio { - return getAllFeeRatios(store, marketID, sellerSettlementRatioKeyMakers) -} - -// GetSellerSettlementRatios gets the seller settlement fee ratios for a market. -func (k Keeper) GetSellerSettlementRatios(ctx sdk.Context, marketID uint32) []exchange.FeeRatio { - return getSellerSettlementRatios(k.getStore(ctx), marketID) -} - -// setSellerSettlementRatios sets the seller settlement fee ratios for a market. -func setSellerSettlementRatios(store sdk.KVStore, marketID uint32, ratios []exchange.FeeRatio) { - setAllFeeRatios(store, marketID, ratios, sellerSettlementRatioKeyMakers) -} - -// updateSellerSettlementRatios deletes all seller settlement ratio entries to delete then adds the ones to add. -func updateSellerSettlementRatios(store sdk.KVStore, marketID uint32, toDelete, toAdd []exchange.FeeRatio) { - updateFeeRatios(store, marketID, toDelete, toAdd, sellerSettlementRatioKeyMakers) -} - -// getSellerSettlementRatio gets the seller settlement fee ratio for the given market with the provided denom. -func getSellerSettlementRatio(store sdk.KVStore, marketID uint32, priceDenom string) (*exchange.FeeRatio, error) { - ratio := getFeeRatio(store, marketID, priceDenom, priceDenom, sellerSettlementRatioKeyMakers) - if ratio == nil { - if hasFeeRatio(store, marketID, sellerSettlementRatioKeyMakers) { - return nil, fmt.Errorf("no seller settlement fee ratio found for denom %q", priceDenom) - } - } - return ratio, nil -} - -// validateAskPrice validates that the provided ask price is acceptable. -func validateAskPrice(store sdk.KVStore, marketID uint32, price sdk.Coin, settlementFlatFee *sdk.Coin) error { - ratio, err := getSellerSettlementRatio(store, marketID, price.Denom) - if err != nil { - return err - } - - // If there is a settlement flat fee with a different denom as the price, a hold is placed on it. - // If there's a settlement flat fee with the same denom as the price, it's paid out of the price along - // with the ratio amount. Assuming the ratio is less than one, the price will always cover the ratio fee amount. - // But if the flat fee is coming out of the price too, it's possible that the price might be less than the total - // fee that will need to come out of it. We want to return an error if that's the case. - if settlementFlatFee != nil && price.Denom == settlementFlatFee.Denom { - if price.Amount.LT(settlementFlatFee.Amount) { - return fmt.Errorf("price %s is less than seller settlement flat fee %s", price, settlementFlatFee) - } - if ratio != nil { - ratioFee, err := ratio.ApplyToLoosely(price) - if err != nil { - return err - } - reqPrice := settlementFlatFee.Add(ratioFee) - if price.IsLT(reqPrice) { - return fmt.Errorf("price %s is less than total required seller settlement fee of %s = %s flat + %s ratio", - price, reqPrice, settlementFlatFee, ratioFee) - } - } - } - - return nil +// validateAskPrice validates that the provided ask price is acceptable. +func validateAskPrice(store sdk.KVStore, marketID uint32, price sdk.Coin, settlementFlatFee *sdk.Coin) error { + ratio, err := getSellerSettlementRatio(store, marketID, price.Denom) + if err != nil { + return err + } + + // If there is a settlement flat fee with a different denom as the price, a hold is placed on it. + // If there's a settlement flat fee with the same denom as the price, it's paid out of the price along + // with the ratio amount. Assuming the ratio is less than one, the price will always cover the ratio fee amount. + // But if the flat fee is coming out of the price too, it's possible that the price might be less than the total + // fee that will need to come out of it. We want to return an error if that's the case. + if settlementFlatFee != nil && price.Denom == settlementFlatFee.Denom { + if price.Amount.LT(settlementFlatFee.Amount) { + return fmt.Errorf("price %s is less than seller settlement flat fee %s", price, settlementFlatFee) + } + if ratio != nil { + ratioFee, err := ratio.ApplyToLoosely(price) + if err != nil { + return err + } + reqPrice := settlementFlatFee.Add(ratioFee) + if price.IsLT(reqPrice) { + return fmt.Errorf("price %s is less than total required seller settlement fee of %s = %s flat + %s ratio", + price, reqPrice, settlementFlatFee, ratioFee) + } + } + } + + return nil } // calculateSellerSettlementRatioFee calculates the seller settlement fee required for the given price. @@ -373,36 +428,11 @@ func calculateSellerSettlementRatioFee(store sdk.KVStore, marketID uint32, price return &rv, nil } -// getBuyerSettlementFlatFees gets the buyer settlement flat fee options for a market. -func getBuyerSettlementFlatFees(store sdk.KVStore, marketID uint32) []sdk.Coin { - return getAllFlatFees(store, marketID, buyerSettlementFlatKeyMakers) -} - -// GetBuyerSettlementFlatFees gets the buyer settlement flat fee options for a market. -func (k Keeper) GetBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { - return getBuyerSettlementFlatFees(k.getStore(ctx), marketID) -} - -// setBuyerSettlementFlatFees sets the buyer settlement flat fees for a market. -func setBuyerSettlementFlatFees(store sdk.KVStore, marketID uint32, options []sdk.Coin) { - setAllFlatFees(store, marketID, options, buyerSettlementFlatKeyMakers) -} - -// updateBuyerSettlementFlatFees deletes all buyer settlement flat fees to delete then adds the ones to add. -func updateBuyerSettlementFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd []sdk.Coin) { - updateFlatFees(store, marketID, toDelete, toAdd, buyerSettlementFlatKeyMakers) -} - // getBuyerSettlementRatios gets the buyer settlement fee ratios for a market. func getBuyerSettlementRatios(store sdk.KVStore, marketID uint32) []exchange.FeeRatio { return getAllFeeRatios(store, marketID, buyerSettlementRatioKeyMakers) } -// GetBuyerSettlementRatios gets the buyer settlement fee ratios for a market. -func (k Keeper) GetBuyerSettlementRatios(ctx sdk.Context, marketID uint32) []exchange.FeeRatio { - return getBuyerSettlementRatios(k.getStore(ctx), marketID) -} - // setBuyerSettlementRatios sets the buyer settlement fee ratios for a market. func setBuyerSettlementRatios(store sdk.KVStore, marketID uint32, ratios []exchange.FeeRatio) { setAllFeeRatios(store, marketID, ratios, buyerSettlementRatioKeyMakers) @@ -456,11 +486,6 @@ func calcBuyerSettlementRatioFeeOptions(store sdk.KVStore, marketID uint32, pric return rv, nil } -// CalculateBuyerSettlementRatioFeeOptions calculates the buyer settlement ratio fee options available for the given price. -func (k Keeper) CalculateBuyerSettlementRatioFeeOptions(ctx sdk.Context, marketID uint32, price sdk.Coin) ([]sdk.Coin, error) { - return calcBuyerSettlementRatioFeeOptions(k.getStore(ctx), marketID, price) -} - // validateBuyerSettlementFee returns an error if the provided fee is not enough to cover both the // buyer settlement flat and percent fees for the given price. func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Coin, fee sdk.Coins) error { @@ -573,26 +598,82 @@ func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Co return errors.Join(errs...) } -// UpdateMarketDetails updates a market's details. It returns an error if the market account -// isn't found or if there aren't any changes provided. -func (k Keeper) UpdateMarketDetails(ctx sdk.Context, marketID uint32, marketDetails *exchange.MarketDetails, updatedBy sdk.AccAddress) error { - if err := marketDetails.Validate(); err != nil { - return err - } +// GetCreateAskFlatFees gets the create-ask flat fee options for a market. +func (k Keeper) GetCreateAskFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { + return getCreateAskFlatFees(k.getStore(ctx), marketID) +} - marketAcc := k.GetMarketAccount(ctx, marketID) - if marketAcc == nil { - return fmt.Errorf("market %d account not found", marketID) - } +// GetCreateBidFlatFees gets the create-bid flat fee options for a market. +func (k Keeper) GetCreateBidFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { + return getCreateBidFlatFees(k.getStore(ctx), marketID) +} - if marketAcc.MarketDetails.Equal(marketDetails) { - return errors.New("no changes") - } +// GetSellerSettlementFlatFees gets the seller settlement flat fee options for a market. +func (k Keeper) GetSellerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { + return getSellerSettlementFlatFees(k.getStore(ctx), marketID) +} - marketAcc.MarketDetails = *marketDetails - k.accountKeeper.SetAccount(ctx, marketAcc) +// GetSellerSettlementRatios gets the seller settlement fee ratios for a market. +func (k Keeper) GetSellerSettlementRatios(ctx sdk.Context, marketID uint32) []exchange.FeeRatio { + return getSellerSettlementRatios(k.getStore(ctx), marketID) +} - return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketDetailsUpdated(marketID, updatedBy)) +// GetBuyerSettlementFlatFees gets the buyer settlement flat fee options for a market. +func (k Keeper) GetBuyerSettlementFlatFees(ctx sdk.Context, marketID uint32) []sdk.Coin { + return getBuyerSettlementFlatFees(k.getStore(ctx), marketID) +} + +// GetBuyerSettlementRatios gets the buyer settlement fee ratios for a market. +func (k Keeper) GetBuyerSettlementRatios(ctx sdk.Context, marketID uint32) []exchange.FeeRatio { + return getBuyerSettlementRatios(k.getStore(ctx), marketID) +} + +// CalculateSellerSettlementRatioFee calculates the seller settlement fee required for the given price. +func (k Keeper) CalculateSellerSettlementRatioFee(ctx sdk.Context, marketID uint32, price sdk.Coin) (*sdk.Coin, error) { + return calculateSellerSettlementRatioFee(k.getStore(ctx), marketID, price) +} + +// CalculateBuyerSettlementRatioFeeOptions calculates the buyer settlement ratio fee options available for the given price. +func (k Keeper) CalculateBuyerSettlementRatioFeeOptions(ctx sdk.Context, marketID uint32, price sdk.Coin) ([]sdk.Coin, error) { + return calcBuyerSettlementRatioFeeOptions(k.getStore(ctx), marketID, price) +} + +// ValidateCreateAskFlatFee returns an error if the provided fee is not a sufficient create-ask flat fee. +func (k Keeper) ValidateCreateAskFlatFee(ctx sdk.Context, marketID uint32, fee *sdk.Coin) error { + return validateCreateAskFlatFee(k.getStore(ctx), marketID, fee) +} + +// ValidateCreateBidFlatFee returns an error if the provided fee is not a sufficient create-bid flat fee. +func (k Keeper) ValidateCreateBidFlatFee(ctx sdk.Context, marketID uint32, fee *sdk.Coin) error { + return validateCreateBidFlatFee(k.getStore(ctx), marketID, fee) +} + +// ValidateSellerSettlementFlatFee returns an error if the provided fee is not a sufficient seller settlement flat fee. +func (k Keeper) ValidateSellerSettlementFlatFee(ctx sdk.Context, marketID uint32, fee *sdk.Coin) error { + return validateSellerSettlementFlatFee(k.getStore(ctx), marketID, fee) +} + +// ValidateAskPrice validates that the provided ask price is acceptable. +func (k Keeper) ValidateAskPrice(ctx sdk.Context, marketID uint32, price sdk.Coin, settlementFlatFee *sdk.Coin) error { + return validateAskPrice(k.getStore(ctx), marketID, price, settlementFlatFee) +} + +// ValidateBuyerSettlementFee returns an error if the provided fee is not enough to cover both the +// buyer settlement flat and percent fees for the given price. +func (k Keeper) ValidateBuyerSettlementFee(ctx sdk.Context, marketID uint32, price sdk.Coin, fee sdk.Coins) error { + return validateBuyerSettlementFee(k.getStore(ctx), marketID, price, fee) +} + +// UpdateFees updates all the fees as provided in the MsgGovManageFeesRequest. +func (k Keeper) UpdateFees(ctx sdk.Context, msg *exchange.MsgGovManageFeesRequest) error { + store := k.getStore(ctx) + updateCreateAskFlatFees(store, msg.MarketId, msg.RemoveFeeCreateAskFlat, msg.AddFeeCreateAskFlat) + updateCreateBidFlatFees(store, msg.MarketId, msg.RemoveFeeCreateBidFlat, msg.AddFeeCreateBidFlat) + updateSellerSettlementFlatFees(store, msg.MarketId, msg.RemoveFeeSellerSettlementFlat, msg.AddFeeSellerSettlementFlat) + updateSellerSettlementRatios(store, msg.MarketId, msg.RemoveFeeSellerSettlementRatios, msg.AddFeeSellerSettlementRatios) + updateBuyerSettlementFlatFees(store, msg.MarketId, msg.RemoveFeeBuyerSettlementFlat, msg.AddFeeBuyerSettlementFlat) + updateBuyerSettlementRatios(store, msg.MarketId, msg.RemoveFeeBuyerSettlementRatios, msg.AddFeeBuyerSettlementRatios) + return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketFeesUpdated(msg.MarketId)) } // isMarketActive returns true if the provided market is accepting orders. @@ -601,11 +682,6 @@ func isMarketActive(store sdk.KVStore, marketID uint32) bool { return !store.Has(key) } -// IsMarketActive returns true if the provided market is accepting orders. -func (k Keeper) IsMarketActive(ctx sdk.Context, marketID uint32) bool { - return isMarketActive(k.getStore(ctx), marketID) -} - // setMarketActive sets whether the provided market is accepting orders. func setMarketActive(store sdk.KVStore, marketID uint32, active bool) { key := MakeKeyMarketInactive(marketID) @@ -616,9 +692,25 @@ func setMarketActive(store sdk.KVStore, marketID uint32, active bool) { } } -// SetMarketActive sets whether the provided market is accepting orders. -func (k Keeper) SetMarketActive(ctx sdk.Context, marketID uint32, active bool) { - setMarketActive(k.getStore(ctx), marketID, active) +// isUserSettlementAllowed gets whether user-settlement is allowed for a market. +func isUserSettlementAllowed(store sdk.KVStore, marketID uint32) bool { + key := MakeKeyMarketUserSettle(marketID) + return store.Has(key) +} + +// SetUserSettlementAllowed sets whether user-settlement is allowed for a market. +func setUserSettlementAllowed(store sdk.KVStore, marketID uint32, allowed bool) { + key := MakeKeyMarketUserSettle(marketID) + if allowed { + store.Set(key, nil) + } else { + store.Delete(key) + } +} + +// IsMarketActive returns true if the provided market is accepting orders. +func (k Keeper) IsMarketActive(ctx sdk.Context, marketID uint32) bool { + return isMarketActive(k.getStore(ctx), marketID) } // UpdateMarketActive updates the active flag for a market. @@ -633,32 +725,11 @@ func (k Keeper) UpdateMarketActive(ctx sdk.Context, marketID uint32, active bool return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketActiveUpdated(marketID, updatedBy, active)) } -// isUserSettlementAllowed gets whether user-settlement is allowed for a market. -func isUserSettlementAllowed(store sdk.KVStore, marketID uint32) bool { - key := MakeKeyMarketUserSettle(marketID) - return store.Has(key) -} - // IsUserSettlementAllowed gets whether user-settlement is allowed for a market. func (k Keeper) IsUserSettlementAllowed(ctx sdk.Context, marketID uint32) bool { return isUserSettlementAllowed(k.getStore(ctx), marketID) } -// SetUserSettlementAllowed sets whether user-settlement is allowed for a market. -func setUserSettlementAllowed(store sdk.KVStore, marketID uint32, allowed bool) { - key := MakeKeyMarketUserSettle(marketID) - if allowed { - store.Set(key, nil) - } else { - store.Delete(key) - } -} - -// SetUserSettlementAllowed sets whether user-settlement is allowed for a market. -func (k Keeper) SetUserSettlementAllowed(ctx sdk.Context, marketID uint32, allowed bool) { - setUserSettlementAllowed(k.getStore(ctx), marketID, allowed) -} - // UpdateUserSettlementAllowed updates the allow-user-settlement flag for a market. // An error is returned if the setting is already what is provided. func (k Keeper) UpdateUserSettlementAllowed(ctx sdk.Context, marketID uint32, allow bool, updatedBy sdk.AccAddress) error { @@ -693,75 +764,12 @@ func revokePermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress, } } -// revokeAllUserPermissions updates the store so that the given address does not have any permissions for the market. -func revokeAllUserPermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress) { +// revokeUserPermissions updates the store so that the given address does not have any permissions for the market. +func revokeUserPermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress) { key := GetKeyPrefixMarketPermissionsForAddress(marketID, addr) deleteAll(store, key) } -// revokeAllMarketPermissions clears out all permissions for a market. -func revokeAllMarketPermissions(store sdk.KVStore, marketID uint32) { - key := GetKeyPrefixMarketPermissions(marketID) - deleteAll(store, key) -} - -// setAllMarketPermissions clears out all market permissions then stores just the ones provided. -func setAllMarketPermissions(store sdk.KVStore, marketID uint32, grants []exchange.AccessGrant) { - revokeAllMarketPermissions(store, marketID) - for _, ag := range grants { - grantPermissions(store, marketID, sdk.MustAccAddressFromBech32(ag.Address), ag.Permissions) - } -} - -// HasPermission returns true if the provided address has the permission in question for a given market. -// Also returns true if the provided address is the authority address. -func (k Keeper) HasPermission(ctx sdk.Context, marketID uint32, address string, permission exchange.Permission) bool { - if k.IsAuthority(address) { - return true - } - addr, err := sdk.AccAddressFromBech32(address) - if err != nil { - return false - } - return storeHasPermission(k.getStore(ctx), marketID, addr, permission) -} - -// CanSettleOrders returns true if the provided admin bech32 address has permission to -// settle orders for a market. Also returns true if the provided address is the authority address. -func (k Keeper) CanSettleOrders(ctx sdk.Context, marketID uint32, admin string) bool { - return k.HasPermission(ctx, marketID, admin, exchange.Permission_settle) -} - -// CanCancelMarketOrders returns true if the provided admin bech32 address has permission to -// cancel orders for a market. Also returns true if the provided address is the authority address. -func (k Keeper) CanCancelMarketOrders(ctx sdk.Context, marketID uint32, admin string) bool { - return k.HasPermission(ctx, marketID, admin, exchange.Permission_cancel) -} - -// CanWithdrawMarketFunds returns true if the provided admin bech32 address has permission to -// withdraw funds from the given market's account. Also returns true if the provided address is the authority address. -func (k Keeper) CanWithdrawMarketFunds(ctx sdk.Context, marketID uint32, admin string) bool { - return k.HasPermission(ctx, marketID, admin, exchange.Permission_withdraw) -} - -// CanUpdateMarket returns true if the provided admin bech32 address has permission to -// update market details and settings. Also returns true if the provided address is the authority address. -func (k Keeper) CanUpdateMarket(ctx sdk.Context, marketID uint32, admin string) bool { - return k.HasPermission(ctx, marketID, admin, exchange.Permission_update) -} - -// CanManagePermissions returns true if the provided admin bech32 address has permission to -// manage user permissions for a given market. Also returns true if the provided address is the authority address. -func (k Keeper) CanManagePermissions(ctx sdk.Context, marketID uint32, admin string) bool { - return k.HasPermission(ctx, marketID, admin, exchange.Permission_permissions) -} - -// CanManageReqAttrs returns true if the provided admin bech32 address has permission to -// manage required attributes for a given market. Also returns true if the provided address is the authority address. -func (k Keeper) CanManageReqAttrs(ctx sdk.Context, marketID uint32, admin string) bool { - return k.HasPermission(ctx, marketID, admin, exchange.Permission_attributes) -} - // getUserPermissions gets all permissions that have been granted to a user in a market. func getUserPermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress) []exchange.Permission { var rv []exchange.Permission @@ -772,9 +780,10 @@ func getUserPermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress) return rv } -// GetUserPermissions gets all permissions that have been granted to a user in a market. -func (k Keeper) GetUserPermissions(ctx sdk.Context, marketID uint32, addr sdk.AccAddress) []exchange.Permission { - return getUserPermissions(k.getStore(ctx), marketID, addr) +// revokeAllMarketPermissions clears out all permissions for a market. +func revokeAllMarketPermissions(store sdk.KVStore, marketID uint32) { + key := GetKeyPrefixMarketPermissions(marketID) + deleteAll(store, key) } // getAccessGrants gets all the access grants for a market. @@ -798,325 +807,68 @@ func getAccessGrants(store sdk.KVStore, marketID uint32) []exchange.AccessGrant // setAccessGrants deletes all access grants on a market and sets just the ones provided. func setAccessGrants(store sdk.KVStore, marketID uint32, grants []exchange.AccessGrant) { revokeAllMarketPermissions(store, marketID) - setAllMarketPermissions(store, marketID, grants) -} - -// reqAttrKeyMaker is a function that returns a key for required attributes. -type reqAttrKeyMaker func(marketID uint32) []byte - -// getReqAttrs gets the required attributes for a market using the provided key maker. -func getReqAttrs(store sdk.KVStore, marketID uint32, maker reqAttrKeyMaker) []string { - key := maker(marketID) - value := store.Get(key) - return ParseReqAttrStoreValue(value) -} - -// setReqAttr sets the required attributes for a market using the provided key maker. -func setReqAttr(store sdk.KVStore, marketID uint32, reqAttrs []string, maker reqAttrKeyMaker) { - key := maker(marketID) - if len(reqAttrs) == 0 { - store.Delete(key) - } else { - value := []byte(strings.Join(reqAttrs, string(RecordSeparator))) - store.Set(key, value) - } -} - -// getReqAttrAsk gets the attributes required to create an ask order. -func getReqAttrAsk(store sdk.KVStore, marketID uint32) []string { - return getReqAttrs(store, marketID, MakeKeyMarketReqAttrAsk) -} - -// GetReqAttrAsk gets the attributes required to create an ask order. -func (k Keeper) GetReqAttrAsk(ctx sdk.Context, marketID uint32) []string { - return getReqAttrAsk(k.getStore(ctx), marketID) -} - -// setReqAttrAsk sets the attributes required to create an ask order. -func setReqAttrAsk(store sdk.KVStore, marketID uint32, reqAttrs []string) { - setReqAttr(store, marketID, reqAttrs, MakeKeyMarketReqAttrAsk) -} - -// getReqAttrBid gets the attributes required to create a bid order. -func getReqAttrBid(store sdk.KVStore, marketID uint32) []string { - return getReqAttrs(store, marketID, MakeKeyMarketReqAttrBid) -} - -// GetReqAttrBid gets the attributes required to create a bid order. -func (k Keeper) GetReqAttrBid(ctx sdk.Context, marketID uint32) []string { - return getReqAttrBid(k.getStore(ctx), marketID) -} - -// setReqAttrBid sets the attributes required to create a bid order. -func setReqAttrBid(store sdk.KVStore, marketID uint32, reqAttrs []string) { - setReqAttr(store, marketID, reqAttrs, MakeKeyMarketReqAttrBid) -} - -// getLastAutoMarketID gets the last auto-selected market id. -func getLastAutoMarketID(store sdk.KVStore) uint32 { - key := MakeKeyLastMarketID() - value := store.Get(key) - rv, _ := uint32FromBz(value) - return rv -} - -// setLastAutoMarketID sets the last auto-selected market id to the provided value. -func setLastAutoMarketID(store sdk.KVStore, marketID uint32) { - key := MakeKeyLastMarketID() - value := uint32Bz(marketID) - store.Set(key, value) -} - -// NextMarketID finds the next available market id, updates the last auto-selected -// market id store entry, and returns the unused id it found. -func (k Keeper) NextMarketID(ctx sdk.Context) uint32 { - store := k.getStore(ctx) - marketID := getLastAutoMarketID(store) + 1 - for { - key := MakeKeyKnownMarketID(marketID) - if !store.Has(key) { - break - } - marketID++ + for _, ag := range grants { + grantPermissions(store, marketID, sdk.MustAccAddressFromBech32(ag.Address), ag.Permissions) } - setLastAutoMarketID(store, marketID) - return marketID } -// setMarketKnown sets the known market id indicator in the store. -func setMarketKnown(store sdk.KVStore, marketID uint32) { - key := MakeKeyKnownMarketID(marketID) - store.Set(key, nil) -} - -// validateMarketExists returns an error if the provided marketID does not exist. -func validateMarketExists(store sdk.KVStore, marketID uint32) error { - key := MakeKeyKnownMarketID(marketID) - if !store.Has(key) { - return fmt.Errorf("market %d does not exist", marketID) +// HasPermission returns true if the provided address has the permission in question for a given market. +// Also returns true if the provided address is the authority address. +func (k Keeper) HasPermission(ctx sdk.Context, marketID uint32, address string, permission exchange.Permission) bool { + if k.IsAuthority(address) { + return true } - return nil -} - -// GetAllMarketIDs gets all the known market ids from the store. -func (k Keeper) GetAllMarketIDs(ctx sdk.Context) []uint32 { - var rv []uint32 - k.iterate(ctx, GetKeyPrefixKnownMarketID(), func(key, _ []byte) bool { - marketID, ok := ParseKeySuffixKnownMarketID(key) - if ok { - rv = append(rv, marketID) - } + addr, err := sdk.AccAddressFromBech32(address) + if err != nil { return false - }) - return rv -} - -// IterateMarkets iterates over all markets. -// The callback should return whether to stop, i.e. true = stop iterating, false = keep going. -func (k Keeper) IterateMarkets(ctx sdk.Context, cb func(market *exchange.Market) bool) { - k.iterate(ctx, GetKeyPrefixKnownMarketID(), func(key, _ []byte) bool { - marketID, ok := ParseKeySuffixKnownMarketID(key) - if !ok { - return false - } - market := k.GetMarket(ctx, marketID) - if market == nil { - return false - } - return cb(market) - }) -} - -// storeMarket writes all the market fields to the state store (except MarketDetails which are in the account). -func storeMarket(store sdk.KVStore, market exchange.Market) { - marketID := market.MarketId - setMarketKnown(store, marketID) - setCreateAskFlatFees(store, marketID, market.FeeCreateAskFlat) - setCreateBidFlatFees(store, marketID, market.FeeCreateBidFlat) - setSellerSettlementFlatFees(store, marketID, market.FeeSellerSettlementFlat) - setSellerSettlementRatios(store, marketID, market.FeeSellerSettlementRatios) - setBuyerSettlementFlatFees(store, marketID, market.FeeBuyerSettlementFlat) - setBuyerSettlementRatios(store, marketID, market.FeeBuyerSettlementRatios) - setMarketActive(store, marketID, market.AcceptingOrders) - setUserSettlementAllowed(store, marketID, market.AllowUserSettlement) - setAccessGrants(store, marketID, market.AccessGrants) - setReqAttrAsk(store, marketID, market.ReqAttrCreateAsk) - setReqAttrBid(store, marketID, market.ReqAttrCreateBid) -} - -// CreateMarket saves a new market to the store with all the info provided. -// If the marketId is zero, the next available one will be used. -func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (uint32, error) { - // Note: The Market is passed in by value, so any alterations to it here will be lost upon return. - var errAsk, errBid error - market.ReqAttrCreateAsk, errAsk = exchange.NormalizeReqAttrs(market.ReqAttrCreateAsk) - market.ReqAttrCreateBid, errBid = exchange.NormalizeReqAttrs(market.ReqAttrCreateBid) - if errAsk != nil || errBid != nil { - return 0, errors.Join(errAsk, errBid) - } - - if market.MarketId == 0 { - market.MarketId = k.NextMarketID(ctx) - } - - marketAddr := exchange.GetMarketAddress(market.MarketId) - if k.accountKeeper.HasAccount(ctx, marketAddr) { - return 0, fmt.Errorf("market id %d account %s already exists", market.MarketId, marketAddr) - } - - marketAcc := &exchange.MarketAccount{ - BaseAccount: &authtypes.BaseAccount{Address: marketAddr.String()}, - MarketId: market.MarketId, - MarketDetails: market.MarketDetails, - } - k.accountKeeper.NewAccount(ctx, marketAcc) - k.accountKeeper.SetAccount(ctx, marketAcc) - - storeMarket(k.getStore(ctx), market) - - return market.MarketId, nil -} - -// initMarket is similar to CreateMarket but assumes the market has already been -// validated and also allows for the market account to already exist. -func (k Keeper) initMarket(ctx sdk.Context, store sdk.KVStore, market exchange.Market) { - if market.MarketId == 0 { - market.MarketId = k.NextMarketID(ctx) - } - marketID := market.MarketId - - marketAddr := exchange.GetMarketAddress(marketID) - marketAcc := k.getMarketAccountByAddr(ctx, marketAddr) - if marketAcc != nil { - if !market.MarketDetails.Equal(marketAcc.MarketDetails) { - marketAcc.MarketDetails = market.MarketDetails - k.accountKeeper.SetAccount(ctx, marketAcc) - } - } else { - marketAcc = &exchange.MarketAccount{ - BaseAccount: &authtypes.BaseAccount{Address: marketAddr.String()}, - MarketId: marketID, - MarketDetails: market.MarketDetails, - } - k.accountKeeper.NewAccount(ctx, marketAcc) - k.accountKeeper.SetAccount(ctx, marketAcc) } - - storeMarket(store, market) -} - -// GetMarketAccount gets a market's account from the account module. -func (k Keeper) GetMarketAccount(ctx sdk.Context, marketID uint32) *exchange.MarketAccount { - marketAddr := exchange.GetMarketAddress(marketID) - return k.getMarketAccountByAddr(ctx, marketAddr) + return storeHasPermission(k.getStore(ctx), marketID, addr, permission) } -// getMarketAccountByAddr gets a market's account given it's address. -// This is for when you've already called exchange.GetMarketAddress(marketID) and need it for other things too. -func (k Keeper) getMarketAccountByAddr(ctx sdk.Context, marketAddr sdk.AccAddress) *exchange.MarketAccount { - acc := k.accountKeeper.GetAccount(ctx, marketAddr) - if acc == nil { - return nil - } - marketAcc, ok := acc.(*exchange.MarketAccount) - if !ok { - return nil - } - return marketAcc +// CanSettleOrders returns true if the provided admin bech32 address has permission to +// settle orders for a market. Also returns true if the provided address is the authority address. +func (k Keeper) CanSettleOrders(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_settle) } -// GetMarket reads all the market info from state and returns it. -// Returns nil if the market account doesn't exist or it's not a market account. -func (k Keeper) GetMarket(ctx sdk.Context, marketID uint32) *exchange.Market { - store := k.getStore(ctx) - if err := validateMarketExists(store, marketID); err != nil { - return nil - } - - market := &exchange.Market{MarketId: marketID} - market.FeeCreateAskFlat = getCreateAskFlatFees(store, marketID) - market.FeeCreateBidFlat = getCreateBidFlatFees(store, marketID) - market.FeeSellerSettlementFlat = getSellerSettlementFlatFees(store, marketID) - market.FeeSellerSettlementRatios = getSellerSettlementRatios(store, marketID) - market.FeeBuyerSettlementFlat = getBuyerSettlementFlatFees(store, marketID) - market.FeeBuyerSettlementRatios = getBuyerSettlementRatios(store, marketID) - market.AcceptingOrders = isMarketActive(store, marketID) - market.AllowUserSettlement = isUserSettlementAllowed(store, marketID) - market.AccessGrants = getAccessGrants(store, marketID) - market.ReqAttrCreateAsk = getReqAttrAsk(store, marketID) - market.ReqAttrCreateBid = getReqAttrBid(store, marketID) - - if marketAcc := k.GetMarketAccount(ctx, marketID); marketAcc != nil { - market.MarketDetails = marketAcc.MarketDetails - } - - return market +// CanCancelOrdersForMarket returns true if the provided admin bech32 address has permission to +// cancel orders for a market. Also returns true if the provided address is the authority address. +func (k Keeper) CanCancelOrdersForMarket(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_cancel) } -// GetMarketBrief gets the MarketBrief for the given market id. -func (k Keeper) GetMarketBrief(ctx sdk.Context, marketID uint32) *exchange.MarketBrief { - acc := k.GetMarketAccount(ctx, marketID) - if acc == nil { - return nil - } - - return &exchange.MarketBrief{ - MarketId: marketID, - MarketAddress: acc.Address, - MarketDetails: acc.MarketDetails, - } +// CanWithdrawMarketFunds returns true if the provided admin bech32 address has permission to +// withdraw funds from the given market's account. Also returns true if the provided address is the authority address. +func (k Keeper) CanWithdrawMarketFunds(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_withdraw) } -// UpdateFees updates all the fees as provided in the MsgGovManageFeesRequest. -func (k Keeper) UpdateFees(ctx sdk.Context, msg *exchange.MsgGovManageFeesRequest) error { - store := k.getStore(ctx) - updateCreateAskFlatFees(store, msg.MarketId, msg.RemoveFeeCreateAskFlat, msg.AddFeeCreateAskFlat) - updateCreateBidFlatFees(store, msg.MarketId, msg.RemoveFeeCreateBidFlat, msg.AddFeeCreateBidFlat) - updateSellerSettlementFlatFees(store, msg.MarketId, msg.RemoveFeeSellerSettlementFlat, msg.AddFeeSellerSettlementFlat) - updateSellerSettlementRatios(store, msg.MarketId, msg.RemoveFeeSellerSettlementRatios, msg.AddFeeSellerSettlementRatios) - updateBuyerSettlementFlatFees(store, msg.MarketId, msg.RemoveFeeBuyerSettlementFlat, msg.AddFeeBuyerSettlementFlat) - updateBuyerSettlementRatios(store, msg.MarketId, msg.RemoveFeeBuyerSettlementRatios, msg.AddFeeBuyerSettlementRatios) - return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketFeesUpdated(msg.MarketId)) +// CanUpdateMarket returns true if the provided admin bech32 address has permission to +// update market details and settings. Also returns true if the provided address is the authority address. +func (k Keeper) CanUpdateMarket(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_update) } -// hasReqAttrs returns true if either reqAttrs is empty or the provide address has all of them on their account. -func (k Keeper) hasReqAttrs(ctx sdk.Context, addr sdk.AccAddress, reqAttrs []string) bool { - if len(reqAttrs) == 0 { - return true - } - attrs, err := k.attrKeeper.GetAllAttributesAddr(ctx, addr) - if err != nil { - return false - } - accAttrs := make([]string, len(attrs)) - for i, attr := range attrs { - accAttrs[i] = attr.Name - } - missing := exchange.FindUnmatchedReqAttrs(reqAttrs, accAttrs) - return len(missing) == 0 +// CanManagePermissions returns true if the provided admin bech32 address has permission to +// manage user permissions for a given market. Also returns true if the provided address is the authority address. +func (k Keeper) CanManagePermissions(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_permissions) } -// CanCreateAsk returns true if the provided address is allowed to create an ask order in the given market. -func (k Keeper) CanCreateAsk(ctx sdk.Context, marketID uint32, addr sdk.AccAddress) bool { - reqAttrs := k.GetReqAttrAsk(ctx, marketID) - return k.hasReqAttrs(ctx, addr, reqAttrs) +// CanManageReqAttrs returns true if the provided admin bech32 address has permission to +// manage required attributes for a given market. Also returns true if the provided address is the authority address. +func (k Keeper) CanManageReqAttrs(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_attributes) } -// CanCreateBid returns true if the provided address is allowed to create a bid order in the given market. -func (k Keeper) CanCreateBid(ctx sdk.Context, marketID uint32, addr sdk.AccAddress) bool { - reqAttrs := k.GetReqAttrBid(ctx, marketID) - return k.hasReqAttrs(ctx, addr, reqAttrs) +// GetUserPermissions gets all permissions that have been granted to a user in a market. +func (k Keeper) GetUserPermissions(ctx sdk.Context, marketID uint32, addr sdk.AccAddress) []exchange.Permission { + return getUserPermissions(k.getStore(ctx), marketID, addr) } -// WithdrawMarketFunds transfers funds from a market account to another account. -// The caller is responsible for making sure this withdrawal should be allowed (e.g. by calling CanWithdrawMarketFunds first). -func (k Keeper) WithdrawMarketFunds(ctx sdk.Context, marketID uint32, toAddr sdk.AccAddress, amount sdk.Coins, withdrawnBy sdk.AccAddress) error { - marketAddr := exchange.GetMarketAddress(marketID) - err := k.bankKeeper.SendCoins(ctx, marketAddr, toAddr, amount) - if err != nil { - return fmt.Errorf("failed to withdraw %s from market %d: %w", amount, marketID, err) - } - return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketWithdraw(marketID, amount, toAddr, withdrawnBy)) +// GetAccessGrants gets all the access grants for a market. +func (k Keeper) GetAccessGrants(ctx sdk.Context, marketID uint32) []exchange.AccessGrant { + return getAccessGrants(k.getStore(ctx), marketID) } // UpdatePermissions updates users permissions in the store using the provided changes. @@ -1132,7 +884,7 @@ func (k Keeper) UpdatePermissions(ctx sdk.Context, msg *exchange.MsgMarketManage perms := getUserPermissions(store, marketID, addr) if len(perms) > 0 { if len(errs) == 0 { - revokeAllUserPermissions(store, marketID, addr) + revokeUserPermissions(store, marketID, addr) } } else { errs = append(errs, fmt.Errorf("account %s does not have any permissions for market %d", addrStr, marketID)) @@ -1170,6 +922,27 @@ func (k Keeper) UpdatePermissions(ctx sdk.Context, msg *exchange.MsgMarketManage return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketPermissionsUpdated(marketID, admin)) } +// reqAttrKeyMaker is a function that returns a key for required attributes. +type reqAttrKeyMaker func(marketID uint32) []byte + +// getReqAttrs gets the required attributes for a market using the provided key maker. +func getReqAttrs(store sdk.KVStore, marketID uint32, maker reqAttrKeyMaker) []string { + key := maker(marketID) + value := store.Get(key) + return ParseReqAttrStoreValue(value) +} + +// setReqAttrs sets the required attributes for a market using the provided key maker. +func setReqAttrs(store sdk.KVStore, marketID uint32, reqAttrs []string, maker reqAttrKeyMaker) { + key := maker(marketID) + if len(reqAttrs) == 0 { + store.Delete(key) + } else { + value := []byte(strings.Join(reqAttrs, string(RecordSeparator))) + store.Set(key, value) + } +} + // updateReqAttrs updates the required attributes in the store that use the provided key maker by removing then adding // the provided attributes to the existing entries. func updateReqAttrs(store sdk.KVStore, marketID uint32, toRemove, toAdd []string, field string, maker reqAttrKeyMaker) error { @@ -1201,10 +974,20 @@ func updateReqAttrs(store sdk.KVStore, marketID uint32, toRemove, toAdd []string return errors.Join(errs...) } - setReqAttr(store, marketID, updatedAttrs, maker) + setReqAttrs(store, marketID, updatedAttrs, maker) return nil } +// getReqAttrsAsk gets the attributes required to create an ask order. +func getReqAttrsAsk(store sdk.KVStore, marketID uint32) []string { + return getReqAttrs(store, marketID, MakeKeyMarketReqAttrAsk) +} + +// setReqAttrsAsk sets the attributes required to create an ask order. +func setReqAttrsAsk(store sdk.KVStore, marketID uint32, reqAttrs []string) { + setReqAttrs(store, marketID, reqAttrs, MakeKeyMarketReqAttrAsk) +} + // updateReqAttrsAsk updates the attributes required to create an ask order in the store by removing and adding // the provided entries to the existing entries. // It is assumed that the attributes have been normalized prior to calling this. @@ -1212,6 +995,16 @@ func updateReqAttrsAsk(store sdk.KVStore, marketID uint32, toRemove, toAdd []str return updateReqAttrs(store, marketID, toRemove, toAdd, "create ask", MakeKeyMarketReqAttrAsk) } +// getReqAttrsBid gets the attributes required to create a bid order. +func getReqAttrsBid(store sdk.KVStore, marketID uint32) []string { + return getReqAttrs(store, marketID, MakeKeyMarketReqAttrBid) +} + +// setReqAttrsBid sets the attributes required to create a bid order. +func setReqAttrsBid(store sdk.KVStore, marketID uint32, reqAttrs []string) { + setReqAttrs(store, marketID, reqAttrs, MakeKeyMarketReqAttrBid) +} + // updateReqAttrsBid updates the attributes required to create a bid order in the store by removing and adding // the provided entries to the existing entries. // It is assumed that the attributes have been normalized prior to calling this. @@ -1219,6 +1012,45 @@ func updateReqAttrsBid(store sdk.KVStore, marketID uint32, toRemove, toAdd []str return updateReqAttrs(store, marketID, toRemove, toAdd, "create bid", MakeKeyMarketReqAttrBid) } +// acctHasReqAttrs returns true if either reqAttrs is empty or the provide address has all of them on their account. +func (k Keeper) acctHasReqAttrs(ctx sdk.Context, addr sdk.AccAddress, reqAttrs []string) bool { + if len(reqAttrs) == 0 { + return true + } + attrs, err := k.attrKeeper.GetAllAttributesAddr(ctx, addr) + if err != nil { + return false + } + accAttrs := make([]string, len(attrs)) + for i, attr := range attrs { + accAttrs[i] = attr.Name + } + missing := exchange.FindUnmatchedReqAttrs(reqAttrs, accAttrs) + return len(missing) == 0 +} + +// GetReqAttrsAsk gets the attributes required to create an ask order. +func (k Keeper) GetReqAttrsAsk(ctx sdk.Context, marketID uint32) []string { + return getReqAttrsAsk(k.getStore(ctx), marketID) +} + +// GetReqAttrsBid gets the attributes required to create a bid order. +func (k Keeper) GetReqAttrsBid(ctx sdk.Context, marketID uint32) []string { + return getReqAttrsBid(k.getStore(ctx), marketID) +} + +// CanCreateAsk returns true if the provided address is allowed to create an ask order in the given market. +func (k Keeper) CanCreateAsk(ctx sdk.Context, marketID uint32, addr sdk.AccAddress) bool { + reqAttrs := k.GetReqAttrsAsk(ctx, marketID) + return k.acctHasReqAttrs(ctx, addr, reqAttrs) +} + +// CanCreateBid returns true if the provided address is allowed to create a bid order in the given market. +func (k Keeper) CanCreateBid(ctx sdk.Context, marketID uint32, addr sdk.AccAddress) bool { + reqAttrs := k.GetReqAttrsBid(ctx, marketID) + return k.acctHasReqAttrs(ctx, addr, reqAttrs) +} + // UpdateReqAttrs updates the required attributes in the store using the provided changes. // The caller is responsible for making sure this update should be allowed (e.g. by calling CanManageReqAttrs first). func (k Keeper) UpdateReqAttrs(ctx sdk.Context, msg *exchange.MsgMarketManageReqAttrsRequest) error { @@ -1257,6 +1089,197 @@ func (k Keeper) UpdateReqAttrs(ctx sdk.Context, msg *exchange.MsgMarketManageReq return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketReqAttrUpdated(marketID, admin)) } +// getMarketAccountByAddr gets a market's account given its address. +// This is for when you've already called exchange.GetMarketAddress(marketID) and need it for other things too. +func (k Keeper) getMarketAccountByAddr(ctx sdk.Context, marketAddr sdk.AccAddress) *exchange.MarketAccount { + acc := k.accountKeeper.GetAccount(ctx, marketAddr) + if acc == nil { + return nil + } + marketAcc, ok := acc.(*exchange.MarketAccount) + if !ok { + return nil + } + return marketAcc +} + +// GetMarketAccount gets a market's account from the account module. +func (k Keeper) GetMarketAccount(ctx sdk.Context, marketID uint32) *exchange.MarketAccount { + marketAddr := exchange.GetMarketAddress(marketID) + return k.getMarketAccountByAddr(ctx, marketAddr) +} + +// GetMarketDetails gets a market's details. +func (k Keeper) GetMarketDetails(ctx sdk.Context, marketID uint32) *exchange.MarketDetails { + marketAcc := k.GetMarketAccount(ctx, marketID) + if marketAcc == nil { + return nil + } + return &marketAcc.MarketDetails +} + +// UpdateMarketDetails updates a market's details. It returns an error if the market account +// isn't found or if there aren't any changes provided. +func (k Keeper) UpdateMarketDetails(ctx sdk.Context, marketID uint32, marketDetails exchange.MarketDetails, updatedBy sdk.AccAddress) error { + if err := marketDetails.Validate(); err != nil { + return err + } + + marketAcc := k.GetMarketAccount(ctx, marketID) + if marketAcc == nil { + return fmt.Errorf("market %d account not found", marketID) + } + + if marketAcc.MarketDetails.Equal(marketDetails) { + return errors.New("no changes") + } + + marketAcc.MarketDetails = marketDetails + k.accountKeeper.SetAccount(ctx, marketAcc) + + return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketDetailsUpdated(marketID, updatedBy)) +} + +// storeMarket writes all the market fields to the state store (except MarketDetails which are in the account). +func storeMarket(store sdk.KVStore, market exchange.Market) { + marketID := market.MarketId + setMarketKnown(store, marketID) + setCreateAskFlatFees(store, marketID, market.FeeCreateAskFlat) + setCreateBidFlatFees(store, marketID, market.FeeCreateBidFlat) + setSellerSettlementFlatFees(store, marketID, market.FeeSellerSettlementFlat) + setSellerSettlementRatios(store, marketID, market.FeeSellerSettlementRatios) + setBuyerSettlementFlatFees(store, marketID, market.FeeBuyerSettlementFlat) + setBuyerSettlementRatios(store, marketID, market.FeeBuyerSettlementRatios) + setMarketActive(store, marketID, market.AcceptingOrders) + setUserSettlementAllowed(store, marketID, market.AllowUserSettlement) + setAccessGrants(store, marketID, market.AccessGrants) + setReqAttrsAsk(store, marketID, market.ReqAttrCreateAsk) + setReqAttrsBid(store, marketID, market.ReqAttrCreateBid) +} + +// initMarket is similar to CreateMarket but assumes the market has already been +// validated and also allows for the market account to already exist. +func (k Keeper) initMarket(ctx sdk.Context, store sdk.KVStore, market exchange.Market) { + if market.MarketId == 0 { + market.MarketId = k.NextMarketID(ctx) + } + marketID := market.MarketId + + marketAddr := exchange.GetMarketAddress(marketID) + marketAcc := k.getMarketAccountByAddr(ctx, marketAddr) + if marketAcc != nil { + if !market.MarketDetails.Equal(marketAcc.MarketDetails) { + marketAcc.MarketDetails = market.MarketDetails + k.accountKeeper.SetAccount(ctx, marketAcc) + } + } else { + marketAcc = &exchange.MarketAccount{ + BaseAccount: &authtypes.BaseAccount{Address: marketAddr.String()}, + MarketId: marketID, + MarketDetails: market.MarketDetails, + } + k.accountKeeper.NewAccount(ctx, marketAcc) + k.accountKeeper.SetAccount(ctx, marketAcc) + } + + storeMarket(store, market) +} + +// CreateMarket saves a new market to the store with all the info provided. +// If the marketId is zero, the next available one will be used. +func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (uint32, error) { + // Note: The Market is passed in by value, so any alterations to it here will be lost upon return. + var errAsk, errBid error + market.ReqAttrCreateAsk, errAsk = exchange.NormalizeReqAttrs(market.ReqAttrCreateAsk) + market.ReqAttrCreateBid, errBid = exchange.NormalizeReqAttrs(market.ReqAttrCreateBid) + if errAsk != nil || errBid != nil { + return 0, errors.Join(errAsk, errBid) + } + + if market.MarketId == 0 { + market.MarketId = k.NextMarketID(ctx) + } + + marketAddr := exchange.GetMarketAddress(market.MarketId) + if k.accountKeeper.HasAccount(ctx, marketAddr) { + return 0, fmt.Errorf("market id %d account %s already exists", market.MarketId, marketAddr) + } + + marketAcc := &exchange.MarketAccount{ + BaseAccount: &authtypes.BaseAccount{Address: marketAddr.String()}, + MarketId: market.MarketId, + MarketDetails: market.MarketDetails, + } + k.accountKeeper.NewAccount(ctx, marketAcc) + k.accountKeeper.SetAccount(ctx, marketAcc) + + storeMarket(k.getStore(ctx), market) + + return market.MarketId, nil +} + +// GetMarket reads all the market info from state and returns it. +// Returns nil if the market account doesn't exist or it's not a market account. +func (k Keeper) GetMarket(ctx sdk.Context, marketID uint32) *exchange.Market { + store := k.getStore(ctx) + if err := validateMarketExists(store, marketID); err != nil { + return nil + } + + market := &exchange.Market{MarketId: marketID} + market.FeeCreateAskFlat = getCreateAskFlatFees(store, marketID) + market.FeeCreateBidFlat = getCreateBidFlatFees(store, marketID) + market.FeeSellerSettlementFlat = getSellerSettlementFlatFees(store, marketID) + market.FeeSellerSettlementRatios = getSellerSettlementRatios(store, marketID) + market.FeeBuyerSettlementFlat = getBuyerSettlementFlatFees(store, marketID) + market.FeeBuyerSettlementRatios = getBuyerSettlementRatios(store, marketID) + market.AcceptingOrders = isMarketActive(store, marketID) + market.AllowUserSettlement = isUserSettlementAllowed(store, marketID) + market.AccessGrants = getAccessGrants(store, marketID) + market.ReqAttrCreateAsk = getReqAttrsAsk(store, marketID) + market.ReqAttrCreateBid = getReqAttrsBid(store, marketID) + + if marketAcc := k.GetMarketAccount(ctx, marketID); marketAcc != nil { + market.MarketDetails = marketAcc.MarketDetails + } + + return market +} + +// IterateMarkets iterates over all markets. +// The callback should return whether to stop, i.e. true = stop iterating, false = keep going. +func (k Keeper) IterateMarkets(ctx sdk.Context, cb func(market *exchange.Market) bool) { + k.IterateKnownMarketIDs(ctx, func(marketID uint32) bool { + market := k.GetMarket(ctx, marketID) + return market != nil && cb(market) + }) +} + +// GetMarketBrief gets the MarketBrief for the given market id. +func (k Keeper) GetMarketBrief(ctx sdk.Context, marketID uint32) *exchange.MarketBrief { + acc := k.GetMarketAccount(ctx, marketID) + if acc == nil { + return nil + } + + return &exchange.MarketBrief{ + MarketId: marketID, + MarketAddress: acc.Address, + MarketDetails: acc.MarketDetails, + } +} + +// WithdrawMarketFunds transfers funds from a market account to another account. +// The caller is responsible for making sure this withdrawal should be allowed (e.g. by calling CanWithdrawMarketFunds first). +func (k Keeper) WithdrawMarketFunds(ctx sdk.Context, marketID uint32, toAddr sdk.AccAddress, amount sdk.Coins, withdrawnBy sdk.AccAddress) error { + marketAddr := exchange.GetMarketAddress(marketID) + err := k.bankKeeper.SendCoins(ctx, marketAddr, toAddr, amount) + if err != nil { + return fmt.Errorf("failed to withdraw %s from market %d: %w", amount, marketID, err) + } + return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketWithdraw(marketID, amount, toAddr, withdrawnBy)) +} + // ValidateMarket checks the setup of the provided market, making sure there aren't any possibly problematic settings. func (k Keeper) ValidateMarket(ctx sdk.Context, marketID uint32) error { store := k.getStore(ctx) diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index a54cfbbc70..0498cf21e8 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -112,7 +112,7 @@ func (k MsgServer) MarketUpdateDetails(goCtx context.Context, msg *exchange.MsgM return nil, permError("update", msg.Admin, msg.MarketId) } admin := sdk.MustAccAddressFromBech32(msg.Admin) - err := k.UpdateMarketDetails(ctx, msg.MarketId, &msg.MarketDetails, admin) + err := k.UpdateMarketDetails(ctx, msg.MarketId, msg.MarketDetails, admin) if err != nil { return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } @@ -163,7 +163,7 @@ func (k MsgServer) MarketManagePermissions(goCtx context.Context, msg *exchange. // MarketManageReqAttrs is a market endpoint to manage the attributes required to interact with it. func (k MsgServer) MarketManageReqAttrs(goCtx context.Context, msg *exchange.MsgMarketManageReqAttrsRequest) (*exchange.MsgMarketManageReqAttrsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if !k.CanWithdrawMarketFunds(ctx, msg.MarketId, msg.Admin) { + if !k.CanManageReqAttrs(ctx, msg.MarketId, msg.Admin) { return nil, permError("manage required attributes for", msg.Admin, msg.MarketId) } err := k.UpdateReqAttrs(ctx, msg) diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index c4be03ab7f..ba07957bbc 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -611,7 +611,7 @@ func (k Keeper) CancelOrder(ctx sdk.Context, orderID uint64, signer string) erro } orderOwner := order.GetOwner() - if signer != orderOwner && !k.CanCancelMarketOrders(ctx, order.GetMarketID(), signer) { + if signer != orderOwner && !k.CanCancelOrdersForMarket(ctx, order.GetMarketID(), signer) { return fmt.Errorf("account %s does not have permission to cancel order %d", signer, orderID) } From edb0d1a32cf8597c1fe4b58c86a20566c8eb6ca3 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 3 Oct 2023 14:12:37 -0600 Subject: [PATCH 227/309] [1658]: Make the marketID the 2nd arg for consistency in CollectFee and CollectFees. --- x/exchange/keeper/fulfillment.go | 10 ++++---- x/exchange/keeper/keeper.go | 39 ++++++++++++++++---------------- x/exchange/keeper/orders.go | 4 ++-- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index e9e0e159ee..cbc135f379 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -136,14 +136,14 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro return fmt.Errorf("error transferring price from buyers to seller: %w", err) } - if err := k.CollectFees(ctx, feeInputs, marketID); err != nil { + if err := k.CollectFees(ctx, marketID, feeInputs); err != nil { return fmt.Errorf("error collecting settlement fees: %w", err) } // Collected last so that it's easier for a seller to fill bids without needing those funds first. // Collected separately so it's not combined with the seller settlement fees in the events. if msg.AskOrderCreationFee != nil { - if err := k.CollectFee(ctx, seller, marketID, sdk.Coins{*msg.AskOrderCreationFee}); err != nil { + if err := k.CollectFee(ctx, marketID, seller, sdk.Coins{*msg.AskOrderCreationFee}); err != nil { return fmt.Errorf("error collecting create-ask fee %q: %w", msg.AskOrderCreationFee, err) } } @@ -263,14 +263,14 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro return fmt.Errorf("error transferring price from buyer to sellers: %w", err) } - if err := k.CollectFees(ctx, feeInputs, marketID); err != nil { + if err := k.CollectFees(ctx, marketID, feeInputs); err != nil { return fmt.Errorf("error collecting settlement fees: %w", err) } // Collected last so that it's easier for a seller to fill asks without needing those funds first. // Collected separately so it's not combined with the buyer settlement fees in the events. if msg.BidOrderCreationFee != nil { - if err := k.CollectFee(ctx, buyer, marketID, sdk.Coins{*msg.BidOrderCreationFee}); err != nil { + if err := k.CollectFee(ctx, marketID, buyer, sdk.Coins{*msg.BidOrderCreationFee}); err != nil { return fmt.Errorf("error collecting create-ask fee %q: %w", msg.BidOrderCreationFee, err) } } @@ -364,7 +364,7 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO } } - if err = k.CollectFees(ctx, transfers.FeeInputs, marketID); err != nil { + if err = k.CollectFees(ctx, marketID, transfers.FeeInputs); err != nil { return err } diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 44c090bb68..b412f48f1b 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -16,6 +16,7 @@ import ( "github.com/provenance-io/provenance/x/exchange" ) +// Keeper provides the exchange module's state store interactions. type Keeper struct { cdc codec.BinaryCodec storeKey storetypes.StoreKey @@ -25,7 +26,6 @@ type Keeper struct { bankKeeper exchange.BankKeeper holdKeeper exchange.HoldKeeper - // TODO[1658]: Finish the Keeper struct. authority string feeCollectorName string } @@ -34,7 +34,6 @@ func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, feeCollector accountKeeper exchange.AccountKeeper, attrKeeper exchange.AttributeKeeper, bankKeeper exchange.BankKeeper, holdKeeper exchange.HoldKeeper, ) Keeper { - // TODO[1658]: Finish NewKeeper. rv := Keeper{ cdc: cdc, storeKey: storeKey, @@ -124,6 +123,21 @@ func (k Keeper) iterate(ctx sdk.Context, pre []byte, cb func(key, value []byte) iterate(k.getStore(ctx), pre, cb) } +// DoTransfer facilitates a transfer of things using the bank module. +func (k Keeper) DoTransfer(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) error { + if len(inputs) == 1 && len(outputs) == 1 { + // If there's only one of each, we use SendCoins for the nicer events. + if !exchange.CoinsEquals(inputs[0].Coins, outputs[0].Coins) { + return fmt.Errorf("input coins %q does not equal output coins %q", + inputs[0].Coins, outputs[0].Coins) + } + fromAddr := sdk.MustAccAddressFromBech32(inputs[0].Address) + toAddr := sdk.MustAccAddressFromBech32(outputs[0].Address) + return k.bankKeeper.SendCoins(ctx, fromAddr, toAddr, inputs[0].Coins) + } + return k.bankKeeper.InputOutputCoins(ctx, inputs, outputs) +} + // CalculateExchangeSplit calculates the amount that the exchange will keep of the provided fee. func (k Keeper) CalculateExchangeSplit(ctx sdk.Context, feeAmt sdk.Coins) sdk.Coins { exchangeAmt := make(sdk.Coins, 0, len(feeAmt)) @@ -148,25 +162,10 @@ func (k Keeper) CalculateExchangeSplit(ctx sdk.Context, feeAmt sdk.Coins) sdk.Co return exchangeAmt } -// DoTransfer facilitates a transfer of things using the bank module. -func (k Keeper) DoTransfer(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) error { - if len(inputs) == 1 && len(outputs) == 1 { - // If there's only one of each, we use SendCoins for the nicer events. - if !exchange.CoinsEquals(inputs[0].Coins, outputs[0].Coins) { - return fmt.Errorf("input coins %q does not equal output coins %q", - inputs[0].Coins, outputs[0].Coins) - } - fromAddr := sdk.MustAccAddressFromBech32(inputs[0].Address) - toAddr := sdk.MustAccAddressFromBech32(outputs[0].Address) - return k.bankKeeper.SendCoins(ctx, fromAddr, toAddr, inputs[0].Coins) - } - return k.bankKeeper.InputOutputCoins(ctx, inputs, outputs) -} - // CollectFee will transfer the fee amount to the market account, // then the exchange's cut from the market to the fee collector. // If you have fees to collect from multiple payers, consider using CollectFees. -func (k Keeper) CollectFee(ctx sdk.Context, payer sdk.AccAddress, marketID uint32, feeAmt sdk.Coins) error { +func (k Keeper) CollectFee(ctx sdk.Context, marketID uint32, payer sdk.AccAddress, feeAmt sdk.Coins) error { if feeAmt.IsZero() { return nil } @@ -188,7 +187,7 @@ func (k Keeper) CollectFee(ctx sdk.Context, payer sdk.AccAddress, marketID uint3 // CollectFees will transfer the inputs to the market account, // then the exchange's cut from the market to the fee collector. // If there is only one input, CollectFee is used. -func (k Keeper) CollectFees(ctx sdk.Context, inputs []banktypes.Input, marketID uint32) error { +func (k Keeper) CollectFees(ctx sdk.Context, marketID uint32, inputs []banktypes.Input) error { if len(inputs) == 0 { return nil } @@ -198,7 +197,7 @@ func (k Keeper) CollectFees(ctx sdk.Context, inputs []banktypes.Input, marketID if err != nil { return fmt.Errorf("invalid payer address %q: %w", inputs[0].Address, err) } - return k.CollectFee(ctx, payer, marketID, inputs[0].Coins) + return k.CollectFee(ctx, marketID, payer, inputs[0].Coins) } var feeAmt sdk.Coins diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index ba07957bbc..930d9d07ef 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -525,7 +525,7 @@ func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, crea } if creationFee != nil { - err := k.CollectFee(ctx, seller, marketID, sdk.Coins{*creationFee}) + err := k.CollectFee(ctx, marketID, seller, sdk.Coins{*creationFee}) if err != nil { return 0, fmt.Errorf("error collecting ask order creation fee: %w", err) } @@ -581,7 +581,7 @@ func (k Keeper) CreateBidOrder(ctx sdk.Context, bidOrder exchange.BidOrder, crea } if creationFee != nil { - err := k.CollectFee(ctx, buyer, marketID, sdk.Coins{*creationFee}) + err := k.CollectFee(ctx, marketID, buyer, sdk.Coins{*creationFee}) if err != nil { return 0, fmt.Errorf("error collecting bid order creation fee: %w", err) } From bc422e348df85c3f5cc5096338cce649339a45ba Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 3 Oct 2023 15:17:51 -0600 Subject: [PATCH 228/309] [1658]: Reorganize keeper/orders.go. In SettleOrders, release the holds on the orders before doing the transfers, and put the hold back on the partial if there is one. --- x/exchange/keeper/fulfillment.go | 27 ++- x/exchange/keeper/grpc_query.go | 6 +- x/exchange/keeper/orders.go | 365 +++++++++++++++---------------- 3 files changed, 206 insertions(+), 192 deletions(-) diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index cbc135f379..8581175770 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -46,7 +46,7 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro return err } seller := sdk.MustAccAddressFromBech32(msg.Seller) - if err := k.validateCanCreateAsk(ctx, marketID, seller); err != nil { + if err := k.validateUserCanCreateAsk(ctx, marketID, seller); err != nil { return err } if err := validateCreateAskFees(store, marketID, msg.AskOrderCreationFee, msg.SellerSettlementFlatFee); err != nil { @@ -173,7 +173,7 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro if serr != nil { return fmt.Errorf("invalid buyer %q: %w", msg.Buyer, serr) } - if err := k.validateCanCreateBid(ctx, marketID, buyer); err != nil { + if err := k.validateUserCanCreateBid(ctx, marketID, buyer); err != nil { return err } if err := validateCreateBidFees(store, marketID, msg.BidOrderCreationFee, msg.TotalPrice, msg.BuyerSettlementFees); err != nil { @@ -356,6 +356,20 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO return errors.New("settlement unexpectedly resulted in all orders fully filled") } + for _, order := range askOrders { + if err = k.releaseHoldOnOrder(ctx, order); err != nil { + errs = append(errs, err) + } + } + for _, order := range bidOrders { + if err = k.releaseHoldOnOrder(ctx, order); err != nil { + errs = append(errs, err) + } + } + if len(errs) > 0 { + return errors.Join(errs...) + } + transfers := exchange.BuildSettlementTransfers(fulfillments) for _, transfer := range transfers.OrderTransfers { @@ -369,9 +383,12 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO } if fulfillments.PartialOrder != nil { - if err = k.setOrderInStore(store, *fulfillments.PartialOrder.NewOrder); err != nil { - return fmt.Errorf("could not update partial %s order %d: %w", - fulfillments.PartialOrder.NewOrder.GetOrderType(), fulfillments.PartialOrder.NewOrder.OrderId, err) + order := fulfillments.PartialOrder.NewOrder + if err = k.setOrderInStore(store, *order); err != nil { + return fmt.Errorf("could not update partial %s order %d: %w", order.GetOrderType(), order.OrderId, err) + } + if err = k.placeHoldOnOrder(ctx, fulfillments.PartialOrder.NewOrder); err != nil { + return fmt.Errorf("could not replace hold on partial %s order %d: %w", order.GetOrderType(), order.OrderId, err) } } diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index d4635d07b8..7f8b4edad8 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -100,7 +100,7 @@ func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.Q resp := &exchange.QueryGetMarketOrdersResponse{} var err error - resp.Pagination, resp.Orders, err = k.GetPageOfOrdersFromIndex(store, req.Pagination, req.OrderType, req.AfterOrderId) + resp.Pagination, resp.Orders, err = k.getPageOfOrdersFromIndex(store, req.Pagination, req.OrderType, req.AfterOrderId) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for market %d: %v", req.MarketId, err) @@ -126,7 +126,7 @@ func (k QueryServer) QueryGetOwnerOrders(goCtx context.Context, req *exchange.Qu resp := &exchange.QueryGetOwnerOrdersResponse{} var err error - resp.Pagination, resp.Orders, err = k.GetPageOfOrdersFromIndex(store, req.Pagination, req.OrderType, req.AfterOrderId) + resp.Pagination, resp.Orders, err = k.getPageOfOrdersFromIndex(store, req.Pagination, req.OrderType, req.AfterOrderId) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for owner %s: %v", req.Owner, err) @@ -147,7 +147,7 @@ func (k QueryServer) QueryGetAssetOrders(goCtx context.Context, req *exchange.Qu resp := &exchange.QueryGetAssetOrdersResponse{} var err error - resp.Pagination, resp.Orders, err = k.GetPageOfOrdersFromIndex(store, req.Pagination, req.OrderType, req.AfterOrderId) + resp.Pagination, resp.Orders, err = k.getPageOfOrdersFromIndex(store, req.Pagination, req.OrderType, req.AfterOrderId) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for asset %s: %v", req.Asset, err) diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 930d9d07ef..e2dc55e94d 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -17,6 +17,26 @@ import ( "github.com/provenance-io/provenance/x/exchange" ) +// TODO[1658]: Create a last-order-id store entry and use that in getNextOrderID. + +// getNextOrderID gets the next available order id from the store. +func (k Keeper) getNextOrderID(ctx sdk.Context) uint64 { + store := prefix.NewStore(k.getStore(ctx), GetKeyPrefixOrder()) + iter := store.ReverseIterator(nil, nil) + defer iter.Close() + + toAdd := uint64(1) + for ; iter.Valid(); iter.Next() { + orderIDBz := iter.Key() + orderID, ok := uint64FromBz(orderIDBz) + if ok { + return orderID + toAdd + } + toAdd++ + } + return toAdd +} + // getOrderStoreKeyValue creates the store key and value representing the provided order. func (k Keeper) getOrderStoreKeyValue(order exchange.Order) ([]byte, []byte, error) { // 200 chosen to hopefully be more than what's needed for 99% of orders. @@ -160,51 +180,7 @@ func deleteAndDeIndexOrder(store sdk.KVStore, order exchange.Order) { } } -// deleteOrder deletes an order (along with its indexes). -func (k Keeper) deleteOrder(ctx sdk.Context, order exchange.Order) { - deleteAndDeIndexOrder(k.getStore(ctx), order) -} - -// GetOrder gets an order. Returns nil, nil if the order does not exist. -func (k Keeper) GetOrder(ctx sdk.Context, orderID uint64) (*exchange.Order, error) { - return k.getOrderFromStore(k.getStore(ctx), orderID) -} - -// getNextOrderID gets the next available order id from the store. -func (k Keeper) getNextOrderID(ctx sdk.Context) uint64 { - store := prefix.NewStore(k.getStore(ctx), GetKeyPrefixOrder()) - iter := store.ReverseIterator(nil, nil) - defer iter.Close() - - toAdd := uint64(1) - for ; iter.Valid(); iter.Next() { - orderIDBz := iter.Key() - orderID, ok := uint64FromBz(orderIDBz) - if ok { - return orderID + toAdd - } - toAdd++ - } - return toAdd -} - -// IterateOrders iterates over all orders. An error is returned if there was a problem -// reading an entry along the way. Such a problem does not interrupt iteration. -// The callback should return whether to stop. I.e. false = keep going, true = stop iterating. -func (k Keeper) IterateOrders(ctx sdk.Context, cb func(order *exchange.Order) bool) error { - var errs []error - k.iterate(ctx, GetKeyPrefixOrder(), func(key, value []byte) bool { - order, err := k.parseOrderStoreKeyValue(key, value) - if err != nil { - errs = append(errs, err) - return false - } - return cb(order) - }) - return errors.Join(errs...) -} - -// iterateOrderIndex iterates over a to order index with keys that have the provided prefixBz. +// iterateOrderIndex iterates over a -to-order index with keys that have the provided prefixBz. // The callback takes in the order id and order type byte and should return whether to stop iterating. func (k Keeper) iterateOrderIndex(ctx sdk.Context, prefixBz []byte, cb func(orderID uint64, orderTypeByte byte) bool) { k.iterate(ctx, prefixBz, func(key, value []byte) bool { @@ -216,26 +192,8 @@ func (k Keeper) iterateOrderIndex(ctx sdk.Context, prefixBz []byte, cb func(orde }) } -// IterateMarketOrders iterates over all orders for a market. -// The callback takes in the order id and order type byte and should return whether to stop iterating. -func (k Keeper) IterateMarketOrders(ctx sdk.Context, marketID uint32, cb func(orderID uint64, orderTypeByte byte) bool) { - k.iterateOrderIndex(ctx, GetIndexKeyPrefixMarketToOrder(marketID), cb) -} - -// IterateAddressOrders iterates over all orders for an address. -// The callback takes in the order id and order type byte and should return whether to stop iterating. -func (k Keeper) IterateAddressOrders(ctx sdk.Context, addr sdk.AccAddress, cb func(orderID uint64, orderTypeByte byte) bool) { - k.iterateOrderIndex(ctx, GetIndexKeyPrefixAddressToOrder(addr), cb) -} - -// IterateAssetOrders iterates over all orders for a given asset denom. -// The callback takes in the order id and order type byte and should return whether to stop iterating. -func (k Keeper) IterateAssetOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64, orderTypeByte byte) bool) { - k.iterateOrderIndex(ctx, GetIndexKeyPrefixAssetToOrder(assetDenom), cb) -} - -// GetPageOfOrdersFromIndex gets a page of orders using a to order index. -func (k Keeper) GetPageOfOrdersFromIndex( +// getPageOfOrdersFromIndex gets a page of orders using a -to-order index. +func (k Keeper) getPageOfOrdersFromIndex( prefixStore sdk.KVStore, pageReq *query.PageRequest, orderType string, @@ -281,12 +239,12 @@ func (k Keeper) GetPageOfOrdersFromIndex( } return true, nil } - pageResp, err := FilteredPaginateAfterOrder(prefixStore, pageReq, afterOrderID, accumulator) + pageResp, err := filteredPaginateAfterOrder(prefixStore, pageReq, afterOrderID, accumulator) return pageResp, orders, err } -// FilteredPaginateAfterOrder is similar to query.FilteredPaginate except +// filteredPaginateAfterOrder is similar to query.FilteredPaginate except // allows limiting the iterator to only entries after a certain order id. // afterOrderID is exclusive, i.e. if it's 2, this will go over all order ids that are 3 or greater. // @@ -298,7 +256,7 @@ func (k Keeper) GetPageOfOrdersFromIndex( // It will be false for the results (filtered) < offset and true for `offset > accumulate <= end`. // When accumulate is set to true the current result should be appended to the result set returned // to the client. -func FilteredPaginateAfterOrder( +func filteredPaginateAfterOrder( prefixStore sdk.KVStore, pageRequest *query.PageRequest, afterOrderID uint64, @@ -440,6 +398,129 @@ func getOrderIterator(prefixStore sdk.KVStore, start []byte, reverse bool, after return prefixStore.Iterator(start, nil) } +// validateMarketIsAcceptingOrders makes sure the market exists and is accepting orders. +func validateMarketIsAcceptingOrders(store sdk.KVStore, marketID uint32) error { + if err := validateMarketExists(store, marketID); err != nil { + return err + } + if !isMarketActive(store, marketID) { + return fmt.Errorf("market %d is not accepting orders", marketID) + } + return nil +} + +// validateUserCanCreateAsk makes sure the user can create an ask order in the given market. +func (k Keeper) validateUserCanCreateAsk(ctx sdk.Context, marketID uint32, seller sdk.AccAddress) error { + if !k.CanCreateAsk(ctx, marketID, seller) { + return fmt.Errorf("account %s is not allowed to create ask orders in market %d", seller, marketID) + } + return nil +} + +// validateUserCanCreateBid makes sure the user can create a bid order in the given market. +func (k Keeper) validateUserCanCreateBid(ctx sdk.Context, marketID uint32, buyer sdk.AccAddress) error { + if !k.CanCreateBid(ctx, marketID, buyer) { + return fmt.Errorf("account %s is not allowed to create bid orders in market %d", buyer, marketID) + } + return nil +} + +// validateCreateAskFees makes sure the fees are okay for creating an ask order. +func validateCreateAskFees(store sdk.KVStore, marketID uint32, creationFee *sdk.Coin, settlementFlatFee *sdk.Coin) error { + if err := validateCreateAskFlatFee(store, marketID, creationFee); err != nil { + return err + } + return validateSellerSettlementFlatFee(store, marketID, settlementFlatFee) +} + +// validateCreateBidFees makes sure the fees are okay for creating a bid order. +func validateCreateBidFees(store sdk.KVStore, marketID uint32, creationFee *sdk.Coin, price sdk.Coin, settlementFees sdk.Coins) error { + if err := validateCreateBidFlatFee(store, marketID, creationFee); err != nil { + return err + } + return validateBuyerSettlementFee(store, marketID, price, settlementFees) +} + +// getAskOrders gets orders from the store, making sure they're ask orders in the given market +// and do not have the same seller as the provided buyer. If the buyer isn't yet known, just provide "" for it. +func (k Keeper) getAskOrders(store sdk.KVStore, marketID uint32, orderIDs []uint64, buyer string) ([]*exchange.Order, error) { + var errs []error + orders := make([]*exchange.Order, 0, len(orderIDs)) + + for _, orderID := range orderIDs { + order, oerr := k.getOrderFromStore(store, orderID) + if oerr != nil { + errs = append(errs, oerr) + continue + } + if order == nil { + errs = append(errs, fmt.Errorf("order %d not found", orderID)) + continue + } + if !order.IsAskOrder() { + errs = append(errs, fmt.Errorf("order %d is type %s: expected ask", orderID, order.GetOrderType())) + continue + } + + askOrder := order.GetAskOrder() + orderMarketID := askOrder.MarketId + seller := askOrder.Seller + + if orderMarketID != marketID { + errs = append(errs, fmt.Errorf("order %d market id %d does not equal requested market id %d", orderID, orderMarketID, marketID)) + continue + } + if seller == buyer { + errs = append(errs, fmt.Errorf("order %d has the same seller %s as the requested buyer", orderID, seller)) + continue + } + + orders = append(orders, order) + } + + return orders, errors.Join(errs...) +} + +// getBidOrders gets orders from the store, making sure they're bid orders in the given market +// and do not have the same buyer as the provided seller. If the seller isn't yet known, just provide "" for it. +func (k Keeper) getBidOrders(store sdk.KVStore, marketID uint32, orderIDs []uint64, seller string) ([]*exchange.Order, error) { + var errs []error + orders := make([]*exchange.Order, 0, len(orderIDs)) + + for _, orderID := range orderIDs { + order, oerr := k.getOrderFromStore(store, orderID) + if oerr != nil { + errs = append(errs, oerr) + continue + } + if order == nil { + errs = append(errs, fmt.Errorf("order %d not found", orderID)) + continue + } + if !order.IsBidOrder() { + errs = append(errs, fmt.Errorf("order %d is type %s: expected bid", orderID, order.GetOrderType())) + continue + } + + bidOrder := order.GetBidOrder() + orderMarketID := bidOrder.MarketId + buyer := bidOrder.Buyer + + if orderMarketID != marketID { + errs = append(errs, fmt.Errorf("order %d market id %d does not equal requested market id %d", orderID, orderMarketID, marketID)) + continue + } + if buyer == seller { + errs = append(errs, fmt.Errorf("order %d has the same buyer %s as the requested seller", orderID, buyer)) + continue + } + + orders = append(orders, order) + } + + return orders, errors.Join(errs...) +} + // placeHoldOnOrder places a hold on an order's funds in the owner's account. func (k Keeper) placeHoldOnOrder(ctx sdk.Context, order *exchange.Order) error { orderID := order.OrderId @@ -474,31 +555,9 @@ func (k Keeper) releaseHoldOnOrder(ctx sdk.Context, order *exchange.Order) error return nil } -// validateMarketIsAcceptingOrders makes sure the market exists and is accepting orders. -func validateMarketIsAcceptingOrders(store sdk.KVStore, marketID uint32) error { - if err := validateMarketExists(store, marketID); err != nil { - return err - } - if !isMarketActive(store, marketID) { - return fmt.Errorf("market %d is not accepting orders", marketID) - } - return nil -} - -// validateCanCreateAsk makes sure the user can create an ask order in the given market. -func (k Keeper) validateCanCreateAsk(ctx sdk.Context, marketID uint32, seller sdk.AccAddress) error { - if !k.CanCreateAsk(ctx, marketID, seller) { - return fmt.Errorf("account %s is not allowed to create ask orders in market %d", seller, marketID) - } - return nil -} - -// validateCreateAskFees makes sure the fees are okay for creating an ask order. -func validateCreateAskFees(store sdk.KVStore, marketID uint32, creationFee *sdk.Coin, settlementFlatFee *sdk.Coin) error { - if err := validateCreateAskFlatFee(store, marketID, creationFee); err != nil { - return err - } - return validateSellerSettlementFlatFee(store, marketID, settlementFlatFee) +// GetOrder gets an order. Returns nil, nil if the order does not exist. +func (k Keeper) GetOrder(ctx sdk.Context, orderID uint64) (*exchange.Order, error) { + return k.getOrderFromStore(k.getStore(ctx), orderID) } // CreateAskOrder creates an ask order, collects the creation fee, and places all needed holds. @@ -514,7 +573,7 @@ func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, crea return 0, err } seller := sdk.MustAccAddressFromBech32(askOrder.Seller) - if err := k.validateCanCreateAsk(ctx, marketID, seller); err != nil { + if err := k.validateUserCanCreateAsk(ctx, marketID, seller); err != nil { return 0, err } if err := validateCreateAskFees(store, marketID, creationFee, askOrder.SellerSettlementFlatFee); err != nil { @@ -544,22 +603,6 @@ func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, crea return orderID, ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCreated(order)) } -// validateCanCreateBid makes sure the user can create a bid order in the given market. -func (k Keeper) validateCanCreateBid(ctx sdk.Context, marketID uint32, buyer sdk.AccAddress) error { - if !k.CanCreateBid(ctx, marketID, buyer) { - return fmt.Errorf("account %s is not allowed to create bid orders in market %d", buyer, marketID) - } - return nil -} - -// validateCreateBidFees makes sure the fees are okay for creating a bid order. -func validateCreateBidFees(store sdk.KVStore, marketID uint32, creationFee *sdk.Coin, price sdk.Coin, settlementFees sdk.Coins) error { - if err := validateCreateBidFlatFee(store, marketID, creationFee); err != nil { - return err - } - return validateBuyerSettlementFee(store, marketID, price, settlementFees) -} - // CreateBidOrder creates a bid order, collects the creation fee, and places all needed holds. func (k Keeper) CreateBidOrder(ctx sdk.Context, bidOrder exchange.BidOrder, creationFee *sdk.Coin) (uint64, error) { if err := bidOrder.Validate(); err != nil { @@ -573,7 +616,7 @@ func (k Keeper) CreateBidOrder(ctx sdk.Context, bidOrder exchange.BidOrder, crea return 0, err } buyer := sdk.MustAccAddressFromBech32(bidOrder.Buyer) - if err := k.validateCanCreateBid(ctx, marketID, buyer); err != nil { + if err := k.validateUserCanCreateBid(ctx, marketID, buyer); err != nil { return 0, err } if err := validateCreateBidFees(store, marketID, creationFee, bidOrder.Price, bidOrder.BuyerSettlementFees); err != nil { @@ -623,87 +666,41 @@ func (k Keeper) CancelOrder(ctx sdk.Context, orderID uint64, signer string) erro return fmt.Errorf("unable to release hold on order %d funds: %w", orderID, err) } - k.deleteOrder(ctx, *order) + deleteAndDeIndexOrder(k.getStore(ctx), *order) return ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCancelled(orderID, signerAddr)) } -// getBidOrders gets orders from the store, making sure they're bid orders in the given market -// and do not have the same buyer as the provided seller. If the seller isn't yet known, just provide "" for it. -func (k Keeper) getBidOrders(store sdk.KVStore, marketID uint32, orderIDs []uint64, seller string) ([]*exchange.Order, error) { +// IterateOrders iterates over all orders. An error is returned if there was a problem +// reading an entry along the way. Such a problem does not interrupt iteration. +// The callback takes in the order and should return whether to stop iterating. +func (k Keeper) IterateOrders(ctx sdk.Context, cb func(order *exchange.Order) bool) error { var errs []error - orders := make([]*exchange.Order, 0, len(orderIDs)) - - for _, orderID := range orderIDs { - order, oerr := k.getOrderFromStore(store, orderID) - if oerr != nil { - errs = append(errs, oerr) - continue - } - if order == nil { - errs = append(errs, fmt.Errorf("order %d not found", orderID)) - continue - } - if !order.IsBidOrder() { - errs = append(errs, fmt.Errorf("order %d is type %s: expected bid", orderID, order.GetOrderType())) - continue - } - - bidOrder := order.GetBidOrder() - orderMarketID := bidOrder.MarketId - buyer := bidOrder.Buyer - - if orderMarketID != marketID { - errs = append(errs, fmt.Errorf("order %d market id %d does not equal requested market id %d", orderID, orderMarketID, marketID)) - continue - } - if buyer == seller { - errs = append(errs, fmt.Errorf("order %d has the same buyer %s as the requested seller", orderID, buyer)) - continue + k.iterate(ctx, GetKeyPrefixOrder(), func(key, value []byte) bool { + order, err := k.parseOrderStoreKeyValue(key, value) + if err != nil { + errs = append(errs, err) + return false } - - orders = append(orders, order) - } - - return orders, errors.Join(errs...) + return cb(order) + }) + return errors.Join(errs...) } -// getAskOrders gets orders from the store, making sure they're ask orders in the given market -// and do not have the same seller as the provided buyer. If the buyer isn't yet known, just provide "" for it. -func (k Keeper) getAskOrders(store sdk.KVStore, marketID uint32, orderIDs []uint64, buyer string) ([]*exchange.Order, error) { - var errs []error - orders := make([]*exchange.Order, 0, len(orderIDs)) - - for _, orderID := range orderIDs { - order, oerr := k.getOrderFromStore(store, orderID) - if oerr != nil { - errs = append(errs, oerr) - continue - } - if order == nil { - errs = append(errs, fmt.Errorf("order %d not found", orderID)) - continue - } - if !order.IsAskOrder() { - errs = append(errs, fmt.Errorf("order %d is type %s: expected ask", orderID, order.GetOrderType())) - continue - } - - askOrder := order.GetAskOrder() - orderMarketID := askOrder.MarketId - seller := askOrder.Seller - - if orderMarketID != marketID { - errs = append(errs, fmt.Errorf("order %d market id %d does not equal requested market id %d", orderID, orderMarketID, marketID)) - continue - } - if seller == buyer { - errs = append(errs, fmt.Errorf("order %d has the same seller %s as the requested buyer", orderID, seller)) - continue - } +// IterateMarketOrders iterates over all orders for a market. +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateMarketOrders(ctx sdk.Context, marketID uint32, cb func(orderID uint64, orderTypeByte byte) bool) { + k.iterateOrderIndex(ctx, GetIndexKeyPrefixMarketToOrder(marketID), cb) +} - orders = append(orders, order) - } +// IterateAddressOrders iterates over all orders for an address. +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateAddressOrders(ctx sdk.Context, addr sdk.AccAddress, cb func(orderID uint64, orderTypeByte byte) bool) { + k.iterateOrderIndex(ctx, GetIndexKeyPrefixAddressToOrder(addr), cb) +} - return orders, errors.Join(errs...) +// IterateAssetOrders iterates over all orders for a given asset denom. +// The callback takes in the order id and order type byte and should return whether to stop iterating. +func (k Keeper) IterateAssetOrders(ctx sdk.Context, assetDenom string, cb func(orderID uint64, orderTypeByte byte) bool) { + k.iterateOrderIndex(ctx, GetIndexKeyPrefixAssetToOrder(assetDenom), cb) } From 5f885b82a19e7329121928d4d4a8fcd3fdb7c5b6 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 3 Oct 2023 15:46:59 -0600 Subject: [PATCH 229/309] [1658]: Store the last order id and use it for getting the next order id to use. Prior to this, an id could be reused if an order were created then filled before the next order was created. And while our stuff could handle that okay, external systems probably want that to be unique. --- x/exchange/keeper/keys.go | 9 ++++++++ x/exchange/keeper/keys_test.go | 24 +++++++++++++++++--- x/exchange/keeper/market.go | 13 ++++++----- x/exchange/keeper/orders.go | 40 ++++++++++++++++++---------------- 4 files changed, 58 insertions(+), 28 deletions(-) diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index c8cdc91d16..29760a938b 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -28,6 +28,8 @@ import ( // // Known Market IDs: 0x07 | => nil // +// Last Order ID: 0x08 => uint64 +// // Markets: // Some aspects of a market are stored using the accounts module and the MarketAccount type. // Others are stored in the exchange module. @@ -74,6 +76,8 @@ const ( KeyTypeLastMarketID = byte(0x06) // KeyTypeKnownMarketID is the type byte for known market id entries. KeyTypeKnownMarketID = byte(0x07) + // KeyTypeLastOrderID is the type byte for the id of the last order created. + KeyTypeLastOrderID = byte(0x08) // KeyTypeMarket is the type byte for market entries. KeyTypeMarket = byte(0x01) // KeyTypeOrder is the type byte for order entries. @@ -237,6 +241,11 @@ func ParseKeySuffixKnownMarketID(suffix []byte) (uint32, bool) { return uint32FromBz(suffix) } +// MakeKeyLastOrderID creates the key for the id of the last order created. +func MakeKeyLastOrderID() []byte { + return []byte{KeyTypeLastOrderID} +} + // keyPrefixMarket creates the root of a market's key with extra capacity for the rest. func keyPrefixMarket(marketID uint32, extraCap int) []byte { return prepKey(KeyTypeMarket, uint32Bz(marketID), extraCap) diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 4bda45c0c1..bfa93f0a17 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -32,10 +32,17 @@ type expectedPrefix struct { value []byte } +// keyTestCase is used with checkKey to run some standardized checks on a test case for a store key. type keyTestCase struct { - maker func() []byte - expected []byte - expPanic string + // maker is a function that creates the key value to check. + // A maker is used (instead of just providing the value to check) so that + // the function in question can be checked for panics. + maker func() []byte + // expected is the expected result of the maker. + expected []byte + // expPanic is the panic message expected when the maker is called. + expPanic string + // expPrefixes are all the prefixes that the result of the maker is expected to have. expPrefixes []expectedPrefix } @@ -83,6 +90,7 @@ func TestKeyTypeUniqueness(t *testing.T) { {name: "KeyTypeParams", value: keeper.KeyTypeParams}, {name: "KeyTypeLastMarketID", value: keeper.KeyTypeLastMarketID}, {name: "KeyTypeKnownMarketID", value: keeper.KeyTypeKnownMarketID}, + {name: "KeyTypeLastOrderID", value: keeper.KeyTypeLastOrderID}, {name: "KeyTypeMarket", value: keeper.KeyTypeMarket}, {name: "KeyTypeOrder", value: keeper.KeyTypeOrder}, {name: "KeyTypeMarketToOrderIndex", value: keeper.KeyTypeMarketToOrderIndex}, @@ -435,6 +443,16 @@ func TestParseKeySuffixKnownMarketID(t *testing.T) { } } +func TestMakeKeyLastOrderID(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeKeyLastOrderID() + }, + expected: []byte{keeper.KeyTypeLastOrderID}, + } + checkKey(t, ktc, "MakeKeyLastOrderID") +} + func TestGetKeyPrefixMarket(t *testing.T) { tests := []struct { name string diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 9b61a45031..9264ebbdc1 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -28,10 +28,9 @@ func setLastAutoMarketID(store sdk.KVStore, marketID uint32) { store.Set(key, value) } -// NextMarketID finds the next available market id, updates the last auto-selected +// nextMarketID finds the next available market id, updates the last auto-selected // market id store entry, and returns the unused id it found. -func (k Keeper) NextMarketID(ctx sdk.Context) uint32 { - store := k.getStore(ctx) +func nextMarketID(store sdk.KVStore) uint32 { marketID := getLastAutoMarketID(store) + 1 for { key := MakeKeyKnownMarketID(marketID) @@ -1161,7 +1160,7 @@ func storeMarket(store sdk.KVStore, market exchange.Market) { // validated and also allows for the market account to already exist. func (k Keeper) initMarket(ctx sdk.Context, store sdk.KVStore, market exchange.Market) { if market.MarketId == 0 { - market.MarketId = k.NextMarketID(ctx) + market.MarketId = nextMarketID(store) } marketID := market.MarketId @@ -1196,8 +1195,10 @@ func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (uint32, e return 0, errors.Join(errAsk, errBid) } + store := k.getStore(ctx) + if market.MarketId == 0 { - market.MarketId = k.NextMarketID(ctx) + market.MarketId = nextMarketID(store) } marketAddr := exchange.GetMarketAddress(market.MarketId) @@ -1213,7 +1214,7 @@ func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (uint32, e k.accountKeeper.NewAccount(ctx, marketAcc) k.accountKeeper.SetAccount(ctx, marketAcc) - storeMarket(k.getStore(ctx), market) + storeMarket(store, market) return market.MarketId, nil } diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index e2dc55e94d..cce16155ab 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -10,31 +10,33 @@ import ( db "github.com/tendermint/tm-db" - "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" "github.com/provenance-io/provenance/x/exchange" ) -// TODO[1658]: Create a last-order-id store entry and use that in getNextOrderID. +// getLastOrderID gets the id of the last order created. +func getLastOrderID(store sdk.KVStore) uint64 { + key := MakeKeyLastOrderID() + value := store.Get(key) + rv, _ := uint64FromBz(value) + return rv +} -// getNextOrderID gets the next available order id from the store. -func (k Keeper) getNextOrderID(ctx sdk.Context) uint64 { - store := prefix.NewStore(k.getStore(ctx), GetKeyPrefixOrder()) - iter := store.ReverseIterator(nil, nil) - defer iter.Close() +// setLastOrderID sets the id of the last order created. +func setLastOrderID(store sdk.KVStore, orderID uint64) { + key := MakeKeyLastOrderID() + value := uint64Bz(orderID) + store.Set(key, value) +} - toAdd := uint64(1) - for ; iter.Valid(); iter.Next() { - orderIDBz := iter.Key() - orderID, ok := uint64FromBz(orderIDBz) - if ok { - return orderID + toAdd - } - toAdd++ - } - return toAdd +// nextOrderID finds the next available order id, updates the last order id +// store entry, and returns the unused id it found. +func nextOrderID(store sdk.KVStore) uint64 { + orderID := getLastOrderID(store) + 1 + setLastOrderID(store, orderID) + return orderID } // getOrderStoreKeyValue creates the store key and value representing the provided order. @@ -590,7 +592,7 @@ func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, crea } } - orderID := k.getNextOrderID(ctx) + orderID := nextOrderID(store) order := exchange.NewOrder(orderID).WithAsk(&askOrder) if err := k.setOrderInStore(store, *order); err != nil { return 0, fmt.Errorf("error storing ask order: %w", err) @@ -630,7 +632,7 @@ func (k Keeper) CreateBidOrder(ctx sdk.Context, bidOrder exchange.BidOrder, crea } } - orderID := k.getNextOrderID(ctx) + orderID := nextOrderID(store) order := exchange.NewOrder(orderID).WithBid(&bidOrder) if err := k.setOrderInStore(store, *order); err != nil { return 0, fmt.Errorf("error storing bid order: %w", err) From d10eeec8bd004c19ebc6491a0e82b45b06328b57 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 3 Oct 2023 16:30:53 -0600 Subject: [PATCH 230/309] [1658]: Create emitEvent and emitEvents that just logs any errors instead of returning them. --- x/exchange/keeper/fulfillment.go | 9 ++++++--- x/exchange/keeper/grpc_query.go | 5 +---- x/exchange/keeper/keeper.go | 20 ++++++++++++++++++++ x/exchange/keeper/market.go | 24 ++++++++++++++++-------- x/exchange/keeper/msg_server.go | 16 ++++------------ x/exchange/keeper/orders.go | 9 ++++++--- 6 files changed, 53 insertions(+), 30 deletions(-) diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index 8581175770..ed9f8f94fb 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -154,7 +154,8 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro events[i] = exchange.NewEventOrderFilled(order.OrderId) } - return ctx.EventManager().EmitTypedEvents(events...) + k.emitEvents(ctx, events) + return nil } // FillAsks settles one or more ask orders for a buyer. @@ -281,7 +282,8 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro events[i] = exchange.NewEventOrderFilled(order.OrderId) } - return ctx.EventManager().EmitTypedEvents(events...) + k.emitEvents(ctx, events) + return nil } // SettleOrders attempts to settle all the provided orders. @@ -411,5 +413,6 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO )) } - return ctx.EventManager().EmitTypedEvents(events...) + k.emitEvents(ctx, events) + return nil } diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 7f8b4edad8..d91d1cf4b8 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -381,10 +381,7 @@ func (k QueryServer) QueryValidateManageFees(goCtx context.Context, req *exchang buyerRatios, msg.AddFeeBuyerSettlementRatios, msg.RemoveFeeBuyerSettlementRatios)...) } - if err := k.UpdateFees(ctx, msg); err != nil { - // The only error this might be would be about event emission. - errs = append(errs, err) - } + k.UpdateFees(ctx, msg) if err := k.ValidateMarket(ctx, msg.MarketId); err != nil { errs = append(errs, err) } diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index b412f48f1b..5ed8cd3e53 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -3,6 +3,8 @@ package keeper import ( "fmt" + "github.com/gogo/protobuf/proto" + sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" @@ -53,6 +55,24 @@ func (k Keeper) logErrorf(ctx sdk.Context, msg string, args ...interface{}) { ctx.Logger().Error(fmt.Sprintf(msg, args...), "module", "x/"+exchange.ModuleName) } +// emitEvent emits the provided event and writes any error to the error log. +// See Also emitEvents. +func (k Keeper) emitEvent(ctx sdk.Context, event proto.Message) { + err := ctx.EventManager().EmitTypedEvent(event) + if err != nil { + k.logErrorf(ctx, "error emitting event %#v: %v", event, err) + } +} + +// emitEvents emits the provided events and writes any error to the error log. +// See Also emitEvent. +func (k Keeper) emitEvents(ctx sdk.Context, events []proto.Message) { + err := ctx.EventManager().EmitTypedEvents(events...) + if err != nil { + k.logErrorf(ctx, "error emitting events %#v: %v", events, err) + } +} + // wrongAuthErr returns the error to use when a message's authority isn't what's required. func (k Keeper) wrongAuthErr(badAuthority string) error { return govtypes.ErrInvalidSigner.Wrapf("expected %s got %s", k.GetAuthority(), badAuthority) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 9264ebbdc1..cf64aaacd0 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -664,7 +664,7 @@ func (k Keeper) ValidateBuyerSettlementFee(ctx sdk.Context, marketID uint32, pri } // UpdateFees updates all the fees as provided in the MsgGovManageFeesRequest. -func (k Keeper) UpdateFees(ctx sdk.Context, msg *exchange.MsgGovManageFeesRequest) error { +func (k Keeper) UpdateFees(ctx sdk.Context, msg *exchange.MsgGovManageFeesRequest) { store := k.getStore(ctx) updateCreateAskFlatFees(store, msg.MarketId, msg.RemoveFeeCreateAskFlat, msg.AddFeeCreateAskFlat) updateCreateBidFlatFees(store, msg.MarketId, msg.RemoveFeeCreateBidFlat, msg.AddFeeCreateBidFlat) @@ -672,7 +672,8 @@ func (k Keeper) UpdateFees(ctx sdk.Context, msg *exchange.MsgGovManageFeesReques updateSellerSettlementRatios(store, msg.MarketId, msg.RemoveFeeSellerSettlementRatios, msg.AddFeeSellerSettlementRatios) updateBuyerSettlementFlatFees(store, msg.MarketId, msg.RemoveFeeBuyerSettlementFlat, msg.AddFeeBuyerSettlementFlat) updateBuyerSettlementRatios(store, msg.MarketId, msg.RemoveFeeBuyerSettlementRatios, msg.AddFeeBuyerSettlementRatios) - return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketFeesUpdated(msg.MarketId)) + + k.emitEvent(ctx, exchange.NewEventMarketFeesUpdated(msg.MarketId)) } // isMarketActive returns true if the provided market is accepting orders. @@ -721,7 +722,8 @@ func (k Keeper) UpdateMarketActive(ctx sdk.Context, marketID uint32, active bool return fmt.Errorf("market %d already has accepting-orders %t", marketID, active) } setMarketActive(store, marketID, active) - return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketActiveUpdated(marketID, updatedBy, active)) + k.emitEvent(ctx, exchange.NewEventMarketActiveUpdated(marketID, updatedBy, active)) + return nil } // IsUserSettlementAllowed gets whether user-settlement is allowed for a market. @@ -738,7 +740,8 @@ func (k Keeper) UpdateUserSettlementAllowed(ctx sdk.Context, marketID uint32, al return fmt.Errorf("market %d already has allow-user-settlement %t", marketID, allow) } setUserSettlementAllowed(store, marketID, allow) - return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketUserSettleUpdated(marketID, updatedBy, allow)) + k.emitEvent(ctx, exchange.NewEventMarketUserSettleUpdated(marketID, updatedBy, allow)) + return nil } // storeHasPermission returns true if there is an entry in the store for the given market, address, and permissions. @@ -918,7 +921,8 @@ func (k Keeper) UpdatePermissions(ctx sdk.Context, msg *exchange.MsgMarketManage return errors.Join(errs...) } - return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketPermissionsUpdated(marketID, admin)) + k.emitEvent(ctx, exchange.NewEventMarketPermissionsUpdated(marketID, admin)) + return nil } // reqAttrKeyMaker is a function that returns a key for required attributes. @@ -1085,7 +1089,8 @@ func (k Keeper) UpdateReqAttrs(ctx sdk.Context, msg *exchange.MsgMarketManageReq return errors.Join(errs...) } - return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketReqAttrUpdated(marketID, admin)) + k.emitEvent(ctx, exchange.NewEventMarketReqAttrUpdated(marketID, admin)) + return nil } // getMarketAccountByAddr gets a market's account given its address. @@ -1135,8 +1140,9 @@ func (k Keeper) UpdateMarketDetails(ctx sdk.Context, marketID uint32, marketDeta marketAcc.MarketDetails = marketDetails k.accountKeeper.SetAccount(ctx, marketAcc) + k.emitEvent(ctx, exchange.NewEventMarketDetailsUpdated(marketID, updatedBy)) - return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketDetailsUpdated(marketID, updatedBy)) + return nil } // storeMarket writes all the market fields to the state store (except MarketDetails which are in the account). @@ -1215,6 +1221,7 @@ func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (uint32, e k.accountKeeper.SetAccount(ctx, marketAcc) storeMarket(store, market) + k.emitEvent(ctx, exchange.NewEventMarketCreated(market.MarketId)) return market.MarketId, nil } @@ -1278,7 +1285,8 @@ func (k Keeper) WithdrawMarketFunds(ctx sdk.Context, marketID uint32, toAddr sdk if err != nil { return fmt.Errorf("failed to withdraw %s from market %d: %w", amount, marketID, err) } - return ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketWithdraw(marketID, amount, toAddr, withdrawnBy)) + k.emitEvent(ctx, exchange.NewEventMarketWithdraw(marketID, amount, toAddr, withdrawnBy)) + return nil } // ValidateMarket checks the setup of the provided market, making sure there aren't any possibly problematic settings. diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 0498cf21e8..e5c2a998a9 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -180,14 +180,11 @@ func (k MsgServer) GovCreateMarket(goCtx context.Context, msg *exchange.MsgGovCr } ctx := sdk.UnwrapSDKContext(goCtx) - marketID, err := k.CreateMarket(ctx, msg.Market) - if err != nil { - return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) - } - err = ctx.EventManager().EmitTypedEvent(exchange.NewEventMarketCreated(marketID)) + _, err := k.CreateMarket(ctx, msg.Market) if err != nil { return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) } + return &exchange.MsgGovCreateMarketResponse{}, nil } @@ -198,10 +195,7 @@ func (k MsgServer) GovManageFees(goCtx context.Context, msg *exchange.MsgGovMana } ctx := sdk.UnwrapSDKContext(goCtx) - err := k.UpdateFees(ctx, msg) - if err != nil { - return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) - } + k.UpdateFees(ctx, msg) return &exchange.MsgGovManageFeesResponse{}, nil } @@ -214,9 +208,7 @@ func (k MsgServer) GovUpdateParams(goCtx context.Context, msg *exchange.MsgGovUp ctx := sdk.UnwrapSDKContext(goCtx) k.SetParams(ctx, &msg.Params) + k.emitEvent(ctx, exchange.NewEventParamsUpdated()) - if err := ctx.EventManager().EmitTypedEvent(exchange.NewEventParamsUpdated()); err != nil { - return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) - } return &exchange.MsgGovUpdateParamsResponse{}, nil } diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index cce16155ab..3197ca8a8b 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -602,7 +602,8 @@ func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, crea return 0, err } - return orderID, ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCreated(order)) + k.emitEvent(ctx, exchange.NewEventOrderCreated(order)) + return orderID, nil } // CreateBidOrder creates a bid order, collects the creation fee, and places all needed holds. @@ -642,7 +643,8 @@ func (k Keeper) CreateBidOrder(ctx sdk.Context, bidOrder exchange.BidOrder, crea return 0, err } - return orderID, ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCreated(order)) + k.emitEvent(ctx, exchange.NewEventOrderCreated(order)) + return orderID, nil } // CancelOrder releases an order's held funds and deletes it. @@ -669,8 +671,9 @@ func (k Keeper) CancelOrder(ctx sdk.Context, orderID uint64, signer string) erro } deleteAndDeIndexOrder(k.getStore(ctx), *order) + k.emitEvent(ctx, exchange.NewEventOrderCancelled(orderID, signerAddr)) - return ctx.EventManager().EmitTypedEvent(exchange.NewEventOrderCancelled(orderID, signerAddr)) + return nil } // IterateOrders iterates over all orders. An error is returned if there was a problem From b1cd73cda46f11199e19ffcc5f01ae1d462ec8f1 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 3 Oct 2023 17:19:35 -0600 Subject: [PATCH 231/309] [1658]: Begin the overhaul of fulfillment. Create a Distribution struct and add two to the fulfillment and create a couple methods for adding to them. --- x/exchange/fulfillment.go | 71 ++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 99a74ee8db..0081591660 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -5,12 +5,12 @@ import ( "fmt" sdkmath "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) // OrderSplit contains an order, and the asset and price amounts that should come out of it. +// TODO[1658]: Remove this struct. type OrderSplit struct { // Order fulfillment associated with this split. Order *OrderFulfillment @@ -20,13 +20,26 @@ type OrderSplit struct { Price sdk.Coin } +// Distribution indicates an address and an amount that will either go to, or come from that address. +type Distribution struct { + // Address is an bech32 address string + Address string + // Amount is the amount that will either go to, or come from that address. + Amount sdkmath.Int +} + // OrderFulfillment is used to figure out how an order should be fulfilled. type OrderFulfillment struct { // Order is the original order with all its information. Order *Order // Splits contains information on the orders being used to fulfill this order. - Splits []*OrderSplit + Splits []*OrderSplit // TODO[1658]: Remove this field. + + // AssetDists contains distribution info for this order's assets. + AssetDists []*Distribution + // AssetDists contains distribution info for this order's price. + PriceDists []*Distribution // AssetsFilledAmt is the total amount of assets being fulfilled for the order. AssetsFilledAmt sdkmath.Int @@ -71,34 +84,44 @@ func NewOrderFulfillment(order *Order) *OrderFulfillment { } } +// assetCoin returns a coin with the given amount and the same denom as this order's assets. +func (f OrderFulfillment) assetCoin(amt sdkmath.Int) sdk.Coin { + return sdk.Coin{Denom: f.GetAssets().Denom, Amount: amt} +} + +// priceCoin returns a coin with the given amount and the same denom as this order's price. +func (f OrderFulfillment) priceCoin(amt sdkmath.Int) sdk.Coin { + return sdk.Coin{Denom: f.GetPrice().Denom, Amount: amt} +} + // GetAssetsFilled gets the coin value of the assets that have been filled in this fulfillment. func (f OrderFulfillment) GetAssetsFilled() sdk.Coin { - return sdk.Coin{Denom: f.GetAssets().Denom, Amount: f.AssetsFilledAmt} + return f.assetCoin(f.AssetsFilledAmt) } // GetAssetsUnfilled gets the coin value of the assets left to fill in this fulfillment. func (f OrderFulfillment) GetAssetsUnfilled() sdk.Coin { - return sdk.Coin{Denom: f.GetAssets().Denom, Amount: f.AssetsUnfilledAmt} + return f.assetCoin(f.AssetsUnfilledAmt) } // GetPriceApplied gets the coin value of the price that has been filled in this fulfillment. func (f OrderFulfillment) GetPriceApplied() sdk.Coin { - return sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceAppliedAmt} + return f.priceCoin(f.PriceAppliedAmt) } // GetPriceLeft gets the coin value of the price left to fill in this fulfillment. func (f OrderFulfillment) GetPriceLeft() sdk.Coin { - return sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceLeftAmt} + return f.priceCoin(f.PriceLeftAmt) } // GetPriceFilled gets the coin value of the price filled in this fulfillment. func (f OrderFulfillment) GetPriceFilled() sdk.Coin { - return sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceFilledAmt} + return f.priceCoin(f.PriceFilledAmt) } // GetPriceUnfilled gets the coin value of the price unfilled in this fulfillment. func (f OrderFulfillment) GetPriceUnfilled() sdk.Coin { - return sdk.Coin{Denom: f.GetPrice().Denom, Amount: f.PriceUnfilledAmt} + return f.priceCoin(f.PriceUnfilledAmt) } // IsFullyFilled returns true if this fulfillment's order has been fully accounted for. @@ -171,6 +194,38 @@ func (f OrderFulfillment) GetHoldAmount() sdk.Coins { return f.Order.GetHoldAmount() } +// DistributeAssets records the distribution of assets in the provided amount to/from the given order. +func (f *OrderFulfillment) DistributeAssets(order OrderI, amount sdkmath.Int) error { + if f.AssetsUnfilledAmt.LT(amount) { + return fmt.Errorf("cannot fill %s order %d having assets left %q with %q from %s order %d: overfill", + f.GetOrderType(), f.GetOrderID(), f.GetAssetsUnfilled(), f.assetCoin(amount), order.GetOrderType(), order.GetOrderID()) + } + + f.AssetsUnfilledAmt = f.AssetsUnfilledAmt.Sub(amount) + f.AssetsFilledAmt = f.AssetsFilledAmt.Add(amount) + f.AssetDists = append(f.AssetDists, &Distribution{ + Address: order.GetOwner(), + Amount: amount, + }) + return nil +} + +// DistributePrice records the distribution of price in the provided amount to/from the given order. +func (f *OrderFulfillment) DistributePrice(order OrderI, amount sdkmath.Int) error { + if f.PriceLeftAmt.LT(amount) && f.IsBidOrder() { + return fmt.Errorf("cannot fill %s order %d having price left %q to %s order %d at a price of %q: overfill", + f.GetOrderType(), f.GetOrderID(), f.GetPriceLeft(), order.GetOrderType(), order.GetOrderID(), f.priceCoin(amount)) + } + + f.PriceLeftAmt = f.PriceLeftAmt.Sub(amount) + f.PriceAppliedAmt = f.PriceAppliedAmt.Add(amount) + f.PriceDists = append(f.PriceDists, &Distribution{ + Address: order.GetOrderType(), + Amount: amount, + }) + return nil +} + // Apply adjusts this order fulfillment using the provided info. func (f *OrderFulfillment) Apply(order *OrderFulfillment, assetsAmt, priceAmt sdkmath.Int) error { assets := sdk.NewCoin(order.GetAssets().Denom, assetsAmt) From 871108afe7e468b39a66af914dbe77543fd9cb4b Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 4 Oct 2023 17:34:41 -0600 Subject: [PATCH 232/309] [1658]: Major work on the fulfillment overhaul. Allocation done, just need to create the transfers. --- x/exchange/fulfillment.go | 467 ++++++++++++++++++++++++++++++++- x/exchange/fulfillment_test.go | 42 ++- x/exchange/helpers.go | 19 ++ x/exchange/helpers_test.go | 165 ++++++++++++ x/exchange/market.go | 5 +- x/exchange/orders.go | 103 ++++++++ x/exchange/orders_test.go | 6 + 7 files changed, 787 insertions(+), 20 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 0081591660..74cb7e201b 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -84,6 +84,15 @@ func NewOrderFulfillment(order *Order) *OrderFulfillment { } } +// NewOrderFulfillments creates a new OrderFulfillment for each of the provided orders. +func NewOrderFulfillments(orders []*Order) []*OrderFulfillment { + rv := make([]*OrderFulfillment, len(orders)) + for i, o := range orders { + rv[i] = NewOrderFulfillment(o) + } + return rv +} + // assetCoin returns a coin with the given amount and the same denom as this order's assets. func (f OrderFulfillment) assetCoin(amt sdkmath.Int) sdk.Coin { return sdk.Coin{Denom: f.GetAssets().Denom, Amount: amt} @@ -210,6 +219,15 @@ func (f *OrderFulfillment) DistributeAssets(order OrderI, amount sdkmath.Int) er return nil } +// DistributeAssets records the distribution of assets in the provided amount to/from the given fulfillments. +func DistributeAssets(of1, of2 *OrderFulfillment, amount sdkmath.Int) error { + errs := []error{ + of1.DistributeAssets(of2, amount), + of2.DistributeAssets(of1, amount), + } + return errors.Join(errs...) +} + // DistributePrice records the distribution of price in the provided amount to/from the given order. func (f *OrderFulfillment) DistributePrice(order OrderI, amount sdkmath.Int) error { if f.PriceLeftAmt.LT(amount) && f.IsBidOrder() { @@ -226,6 +244,389 @@ func (f *OrderFulfillment) DistributePrice(order OrderI, amount sdkmath.Int) err return nil } +// DistributePrice records the distribution of price in the provided amount to/from the given fulfillments. +func DistributePrice(of1, of2 *OrderFulfillment, amount sdkmath.Int) error { + errs := []error{ + of1.DistributePrice(of2, amount), + of2.DistributePrice(of1, amount), + } + return errors.Join(errs...) +} + +// SplitOrder splits this order on the amount of assets filled. +// This order fulfillment is updated to have the filled order. +func (f *OrderFulfillment) SplitOrder() (filled *Order, unfilled *Order, err error) { + filled, unfilled, err = f.Order.Split(f.AssetsFilledAmt) + f.Order = filled + f.AssetsUnfilledAmt = sdkmath.ZeroInt() + f.PriceLeftAmt = filled.GetPrice().Amount.Sub(f.PriceAppliedAmt) + return filled, unfilled, err +} + +// SumAssetsAndPrice gets the sum of assets, and the sum of prices of the provided orders. +func SumAssetsAndPrice(orders []*Order) (assets sdk.Coins, price sdk.Coins) { + for _, o := range orders { + assets = assets.Add(o.GetAssets()) + price = price.Add(o.GetPrice()) + } + return +} + +// sumPriceLeft gets the sum of price left of the provided fulfillments. +func sumPriceLeft(fulfillments []*OrderFulfillment) sdkmath.Int { + rv := sdkmath.ZeroInt() + for _, f := range fulfillments { + rv = rv.Add(f.PriceLeftAmt) + } + return rv +} + +type Settlement struct { + Transfers []*Transfer + FeeInputs []banktypes.Input + PartialOrderFilled *Order + PartialOrderLeft *Order +} + +func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) (*Settlement, error) { + if err := validateCanSettle(askOrders, bidOrders); err != nil { + return nil, err + } + + askOFs := NewOrderFulfillments(askOrders) + bidOFs := NewOrderFulfillments(bidOrders) + + // Allocate the assets first. + if err := allocateAssets(askOFs, bidOFs); err != nil { + return nil, err + } + + // Identify any partial order and update its entry in the order fulfillments. + partialyFilled, partialyUnfilled, err := splitPartial(askOFs, bidOFs) + if err != nil { + return nil, err + } + + // Now, check that the ask price is not more than the bid price. + totalAskPriceAmt := sumPriceLeft(askOFs) + totalBidPriceAmt := sumPriceLeft(bidOFs) + if totalAskPriceAmt.GT(totalBidPriceAmt) { + return nil, fmt.Errorf("total ask price %q is greater than total bid price %q", + askOFs[0].priceCoin(totalAskPriceAmt), bidOFs[0].priceCoin(totalBidPriceAmt)) + } + + // Allocate the prices. + if err = allocatePrice(askOFs, bidOFs); err != nil { + return nil, err + } + + // Set the fees in the fullfillments + if err = setFeesToPay(askOFs, bidOFs, sellerFeeRatio); err != nil { + return nil, err + } + + if err = validateFulfillments(askOFs, bidOFs); err != nil { + return nil, err + } + + // TODO[1658]: Build the transfers. + + // TODO[1658]: Build the fee inputs. + + settlements := &Settlement{ + Transfers: nil, + FeeInputs: nil, + PartialOrderFilled: partialyFilled, + PartialOrderLeft: partialyUnfilled, + } + return settlements, nil +} + +// validateCanSettle does some superficial checking of the provided orders to make sure we can try to settle them. +func validateCanSettle(askOrders, bidOrders []*Order) error { + var errs []error + if len(askOrders) == 0 { + errs = append(errs, errors.New("no ask orders provided")) + } + if len(bidOrders) == 0 { + errs = append(errs, errors.New("no bid orders provided")) + } + if len(errs) > 0 { + return errors.Join(errs...) + } + + for i, askOrder := range askOrders { + if !askOrder.IsAskOrder() { + errs = append(errs, fmt.Errorf("%s order %d is not an ask order but is in the askOrders list at index %d", + askOrder.GetOrderType(), askOrder.GetOrderID(), i)) + } + } + for i, bidOrder := range bidOrders { + if !bidOrder.IsBidOrder() { + errs = append(errs, fmt.Errorf("%s order %d is not a bid order but is in the bidOrders list at index %d", + bidOrder.GetOrderType(), bidOrder.GetOrderID(), i)) + } + } + if len(errs) > 0 { + return errors.Join(errs...) + } + + totalAssetsForSale, totalAskPrice := SumAssetsAndPrice(askOrders) + totalAssetsToBuy, totalBidPrice := SumAssetsAndPrice(bidOrders) + + if len(totalAssetsForSale) != 1 { + errs = append(errs, fmt.Errorf("cannot settle with multiple ask order asset denoms %q", totalAssetsForSale)) + } + if len(totalAskPrice) != 1 { + errs = append(errs, fmt.Errorf("cannot settle with multiple ask order price denoms %q", totalAskPrice)) + } + if len(totalAssetsToBuy) != 1 { + errs = append(errs, fmt.Errorf("cannot settle with multiple bid order asset denoms %q", totalAssetsToBuy)) + } + if len(totalBidPrice) != 1 { + errs = append(errs, fmt.Errorf("cannot settle with multiple bid order price denoms %q", totalBidPrice)) + } + if len(errs) > 0 { + return errors.Join(errs...) + } + + if totalAssetsForSale[0].Denom != totalAssetsToBuy[0].Denom { + errs = append(errs, fmt.Errorf("cannot settle different ask %q and bid %q asset denoms", + totalAssetsForSale, totalAssetsToBuy)) + } + if totalAskPrice[0].Denom != totalBidPrice[0].Denom { + errs = append(errs, fmt.Errorf("cannot settle different ask %q and bid %q price denoms", + totalAskPrice, totalBidPrice)) + } + + // Note: We don't compare the asset and price amounts because we need to know what's + // being partially filled before we can make an assertions about the amounts. + + return errors.Join(errs...) +} + +// allocateAssets distributes the assets among the fulfillments. +func allocateAssets(askOFs, bidOFs []*OrderFulfillment) error { + a, b := 0, 0 + for a < len(askOFs) && b < len(bidOFs) { + assetsFilledAmt, err := GetFulfillmentAssetsAmt(askOFs[a], bidOFs[b]) + if err != nil { + return err + } + if err = DistributeAssets(askOFs[a], askOFs[b], assetsFilledAmt); err != nil { + return err + } + + askFilled := askOFs[a].AssetsUnfilledAmt.IsZero() + bidFilled := bidOFs[b].AssetsUnfilledAmt.IsZero() + if !askFilled && !bidFilled { + return fmt.Errorf("neither %s order %d nor %s order %d could have assets filled in full", + askOFs[a].GetOrderType(), askOFs[a].GetOrderID(), bidOFs[b].GetOrderType(), bidOFs[b].GetOrderID()) + } + if askFilled { + a++ + } + if bidFilled { + b++ + } + } + + return nil +} + +// splitPartial checks the provided fulfillments for a partial order and splits it out, updating the applicable fulfillment. +func splitPartial(askOFs, bidOFs []*OrderFulfillment) (partialyFilled, partialyUnfilled *Order, err error) { + lastAskI := len(askOFs) - 1 + for i, askOF := range askOFs { + if askOF.AssetsFilledAmt.IsZero() { + return nil, nil, fmt.Errorf("%s order %d (at index %d) has no assets filled", + askOF.GetOrderType(), askOF.GetOrderID(), i) + } + if !askOF.AssetsUnfilledAmt.IsZero() { + if i != lastAskI { + return nil, nil, fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last ask order provided", + askOF.GetOrderType(), askOF.GetOrderID(), i) + } + partialyFilled, partialyUnfilled, err = askOF.SplitOrder() + if err != nil { + return nil, nil, err + } + } + } + + lastBidI := len(bidOFs) - 1 + for i, bidOF := range bidOFs { + if bidOF.AssetsFilledAmt.IsZero() { + return nil, nil, fmt.Errorf("%s order %d (at index %d) has no assets filled", + bidOF.GetOrderType(), bidOF.GetOrderID(), i) + } + if !bidOF.AssetsUnfilledAmt.IsZero() { + if i != lastBidI { + return nil, nil, fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last bid order provided", + bidOF.GetOrderType(), bidOF.GetOrderID(), i) + } + if partialyFilled != nil { + return nil, nil, fmt.Errorf("%s order %d and %s order %d cannot both be partially filled", + partialyFilled.GetOrderType(), partialyFilled.GetOrderID(), + bidOF.GetOrderType(), bidOF.GetOrderID()) + } + partialyFilled, partialyUnfilled, err = bidOF.SplitOrder() + if err != nil { + return nil, nil, err + } + } + } + + return partialyFilled, partialyUnfilled, err +} + +// allocatePrice distributes the prices among the fulfillments. +func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { + // Check that the total ask price is not more than the total bid price. + totalAskPriceAmt := sumPriceLeft(askOFs) + totalBidPriceAmt := sumPriceLeft(bidOFs) + if totalAskPriceAmt.GT(totalBidPriceAmt) { + return fmt.Errorf("total ask price %q is greater than total bid price %q", + askOFs[0].priceCoin(totalAskPriceAmt), bidOFs[0].priceCoin(totalBidPriceAmt)) + } + + // First pass at price distribution: Give all the asks their price. + b := 0 + totalPriceFilledAmt := sdkmath.ZeroInt() + for _, askOF := range askOFs { + for askOF.PriceLeftAmt.IsPositive() && bidOFs[b].PriceLeftAmt.IsPositive() { + priceFilledAmt, err := GetFulfillmentPriceAmt(askOF, bidOFs[b]) + if err != nil { + return err + } + if err = DistributePrice(askOF, bidOFs[b], priceFilledAmt); err != nil { + return err + } + totalPriceFilledAmt = totalPriceFilledAmt.Add(priceFilledAmt) + if !bidOFs[b].PriceLeftAmt.IsPositive() { + b++ + } + } + } + + // Above, we made sure that ask price <= bid price. + // So here, we can assume that total price filled = ask price. + // If it's also the total bid price, we're done! + if totalPriceFilledAmt.Equal(totalBidPriceAmt) { + return nil + } + + // We also know that total price filled <= bid price, so bid price - total price left will be positive. + totalLeftoverPriceAmt := totalBidPriceAmt.Sub(totalPriceFilledAmt) + // We need an assets total so we can allocate the leftovers by assets amounts. + totalAssetsAmt := sdkmath.ZeroInt() + for _, askOF := range askOFs { + totalAssetsAmt = totalAssetsAmt.Add(askOF.AssetsFilledAmt) + } + + // Now, distribute the leftovers per asset (truncated). + // Start over on the asks, but start where we left off on the bids (since that's the first with any price left). + a := 0 + firstPass := true + leftoverPriceAmt := totalLeftoverPriceAmt + for !leftoverPriceAmt.IsZero() { + // Loop back to the first ask if we need to. + if a == len(askOFs) { + a = 0 + firstPass = false + } + + // If there's no bids left to get this from, something's logically wrong with this process. + if b >= len(bidOFs) { + panic(fmt.Errorf("total ask price %q, total bid price %q, difference %q, left to allocate %q: "+ + "no bid orders left to allocate leftovers from", + askOFs[0].priceCoin(totalAskPriceAmt), bidOFs[0].priceCoin(totalBidPriceAmt), + askOFs[0].priceCoin(totalLeftoverPriceAmt), askOFs[0].priceCoin(leftoverPriceAmt))) + } + + // Figure out how much additional price this order should get. + addPriceAmt := totalLeftoverPriceAmt.Mul(askOFs[a].AssetsFilledAmt).Quo(totalAssetsAmt) + if addPriceAmt.IsZero() { + if firstPass { + a++ + continue + } + // If this isn't the first time through the asks, distribute at least one price to each ask. + addPriceAmt = sdkmath.OneInt() + } + // We can only add what's actually left over. + addPriceAmt = MinSDKInt(addPriceAmt, leftoverPriceAmt) + + // If it can't all come out of the current bid, use the rest of what this bid has and move to the next bid. + for b < len(bidOFs) && bidOFs[b].PriceLeftAmt.LTE(addPriceAmt) { + bidPriceLeft := bidOFs[b].PriceLeftAmt + if err := DistributePrice(askOFs[a], bidOFs[b], bidPriceLeft); err != nil { + return err + } + addPriceAmt = addPriceAmt.Sub(bidPriceLeft) + totalPriceFilledAmt = totalPriceFilledAmt.Add(bidPriceLeft) + leftoverPriceAmt = leftoverPriceAmt.Sub(bidPriceLeft) + b++ + } + + // If there's still additional price left, it can all come out of the current bid. + if !addPriceAmt.IsZero() && b < len(bidOFs) { + if err := DistributePrice(askOFs[a], bidOFs[b], addPriceAmt); err != nil { + return err + } + totalPriceFilledAmt = totalPriceFilledAmt.Add(addPriceAmt) + leftoverPriceAmt = leftoverPriceAmt.Sub(addPriceAmt) + // If that was all of it, move on to the next. + if bidOFs[b].PriceLeftAmt.IsZero() { + b++ + } + } + + // Move on to the next ask. + a++ + } + + return nil +} + +// setFeesToPay sets the FeesToPay on each fulfillment. +func setFeesToPay(askOFs, bidOFs []*OrderFulfillment, sellerFeeRatio *FeeRatio) error { + var errs []error + for _, askOF := range askOFs { + askOF.FeesToPay = askOF.GetSettlementFees() + if sellerFeeRatio != nil { + fee, err := sellerFeeRatio.ApplyToLoosely(askOF.GetPriceApplied()) + if err != nil { + errs = append(errs, fmt.Errorf("failed calculate ratio fee for %s order %d: %w", + askOF.GetOrderType(), askOF.GetOrderID(), err)) + } + askOF.FeesToPay = askOF.FeesToPay.Add(fee) + } + } + + for _, bidOF := range bidOFs { + bidOF.FeesToPay = bidOF.GetSettlementFees() + } + + return errors.Join(errs...) +} + +// validateFulfillments runs .Validate on each fulfillment, returning any problems. +func validateFulfillments(askOFs, bidOFs []*OrderFulfillment) error { + var errs []error + for _, askOF := range askOFs { + if err := askOF.Validate(); err != nil { + errs = append(errs, err) + } + } + for _, bidOF := range bidOFs { + if err := bidOF.Validate(); err != nil { + errs = append(errs, err) + } + } + return errors.Join(errs...) +} + // Apply adjusts this order fulfillment using the provided info. func (f *OrderFulfillment) Apply(order *OrderFulfillment, assetsAmt, priceAmt sdkmath.Int) error { assets := sdk.NewCoin(order.GetAssets().Denom, assetsAmt) @@ -300,7 +701,7 @@ func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) (err error) { // AssetsFilledAmt cannot be zero here because we'll be dividing by it. // AssetsFilledAmt cannot be negative here because we can't have negative values from the calcs. - // Checking for assets filled > zero here (instead of in Validate) because we need to divide by it in here. + // Checking for assets filled > zero here (instead of in Validate2) because we need to divide by it in here. if !f.AssetsFilledAmt.IsPositive() { return fmt.Errorf("no assets filled in %s order %d", f.GetOrderType(), f.GetOrderID()) } @@ -405,9 +806,41 @@ func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) (err error) { return nil } -// Validate does some final validation and sanity checking on this order fulfillment. -// It's assumed that Finalize has been called before calling this. +// Validate makes sure the assets filled and price applied are acceptable for this fulfillment. func (f OrderFulfillment) Validate() error { + // Make sure: + // * order assets = assets filled + // * order price <= price applied (ask orders) + // * order price = price applied (bid orders) + + orderPrice := f.GetPrice() + switch { + case f.IsAskOrder(): + if orderPrice.Amount.GT(f.PriceAppliedAmt) { + return fmt.Errorf("%s order %d price %q is less than price filled %q", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceApplied()) + } + case f.IsBidOrder(): + if !orderPrice.Amount.Equal(f.PriceAppliedAmt) { + return fmt.Errorf("%s order %d price %q is not equal to price filled %q", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceApplied()) + } + default: + return fmt.Errorf("%s order %d: unknown order type", f.GetOrderType(), f.GetOrderID()) + } + + orderAssets := f.GetAssets() + if orderAssets.Amount != f.AssetsFilledAmt { + return fmt.Errorf("%s order %d assets %q does not equal filled assets %q", + f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled()) + } + + return nil +} + +// Validate2 does some final validation and sanity checking on this order fulfillment. +// It's assumed that Finalize has been called before calling this. +func (f OrderFulfillment) Validate2() error { if _, err := f.Order.GetSubOrder(); err != nil { return err } @@ -559,7 +992,7 @@ func (f OrderFulfillment) Validate() error { } default: // The only way to trigger this would be to add a new order type but not add a case for it in this switch. - panic(fmt.Errorf("case missing for %T in Validate", f.GetOrderType())) + panic(fmt.Errorf("case missing for %T in Validate2", f.GetOrderType())) } // Saving this simple check for last in the hopes that a previous error exposes why this @@ -629,11 +1062,19 @@ func GetFulfillmentAssetsAmt(of1, of2 *OrderFulfillment) (sdkmath.Int, error) { of2.GetOrderType(), of2.GetOrderID(), of2.GetAssetsUnfilled()) } - // Return the lesser of the two. - if of1.AssetsUnfilledAmt.LTE(of2.AssetsUnfilledAmt) { - return of1.AssetsUnfilledAmt, nil + return MinSDKInt(of1.AssetsUnfilledAmt, of2.AssetsUnfilledAmt), nil +} + +// GetFulfillmentPriceAmt figures out the price that can be fulfilled with the two provided orders. +func GetFulfillmentPriceAmt(of1, of2 *OrderFulfillment) (sdkmath.Int, error) { + if !of1.PriceLeftAmt.IsPositive() || !of2.PriceLeftAmt.IsPositive() { + return sdkmath.ZeroInt(), fmt.Errorf("cannot fill %s order %d having price left %q "+ + "with %s order %d having price left %q: zero or negative price left", + of1.GetOrderType(), of1.GetOrderID(), of1.GetPriceLeft(), + of2.GetOrderType(), of2.GetOrderID(), of2.GetPriceLeft()) } - return of2.AssetsUnfilledAmt, nil + + return MinSDKInt(of1.PriceLeftAmt, of2.PriceLeftAmt), nil } // Fulfillments contains information on how orders are to be fulfilled. @@ -755,12 +1196,12 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) // And make sure they're all valid. for _, askOF := range askOFs { - if err := askOF.Validate(); err != nil { + if err := askOF.Validate2(); err != nil { return nil, err } } for _, bidOF := range bidOFs { - if err := bidOF.Validate(); err != nil { + if err := bidOF.Validate2(); err != nil { return nil, err } } @@ -889,7 +1330,7 @@ type SettlementTransfers struct { } // BuildSettlementTransfers creates all the order transfers needed for the provided fulfillments. -// Assumes that all fulfillments have passed Validate. +// Assumes that all fulfillments have passed Validate2. // Panics if any amounts are negative. func BuildSettlementTransfers(f *Fulfillments) *SettlementTransfers { allOFs := make([]*OrderFulfillment, 0, len(f.AskOFs)+len(f.BidOFs)) @@ -916,7 +1357,7 @@ func BuildSettlementTransfers(f *Fulfillments) *SettlementTransfers { } // GetAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. -// Assumes that the fulfillment has passed Validate already. +// Assumes that the fulfillment has passed Validate2 already. // Panics if any amounts are negative or if it's neither a bid nor ask order. func GetAssetTransfer(f *OrderFulfillment) *Transfer { indexedSplits := newIndexedAddrAmts() @@ -943,7 +1384,7 @@ func GetAssetTransfer(f *OrderFulfillment) *Transfer { } // GetPriceTransfer gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. -// Assumes that the fulfillment has passed Validate already. +// Assumes that the fulfillment has passed Validate2 already. // Panics if any amounts are negative or if it's neither a bid nor ask order. func GetPriceTransfer(f *OrderFulfillment) *Transfer { indexedSplits := newIndexedAddrAmts() diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index a8d06a88fe..77bd27cf5a 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -215,6 +215,8 @@ func TestNewOrderFulfillment(t *testing.T) { } } +// TODO[1658]: TestNewOrderFulfillments(t *testing.T) + func TestOrderFulfillment_GetAssetsFilled(t *testing.T) { coin := func(amt int64) sdk.Coin { return sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(amt)} @@ -914,6 +916,34 @@ func TestOrderFulfillment_GetHoldAmount(t *testing.T) { } } +// TODO[1658]: func TestOrderFulfillment_DistributeAssets(t *testing.T) + +// TODO[1658]: func TestDistributeAssets(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_DistributePrice(t *testing.T) + +// TODO[1658]: func TestDistributePrice(t *testing.T) + +// TODO[1658]: func TestOrderFulfillment_SplitOrder(t *testing.T) + +// TODO[1658]: func TestSumAssetsAndPrice(t *testing.T) + +// TODO[1658]: func TestSumPriceLeft(t *testing.T) + +// TODO[1658]: func TestBuildSettlement(t *testing.T) + +// TODO[1658]: func TestValidateCanSettle(t *testing.T) + +// TODO[1658]: func TestAllocateAssets(t *testing.T) + +// TODO[1658]: func TestSplitPartial(t *testing.T) + +// TODO[1658]: func TestAllocatePrice(t *testing.T) + +// TODO[1658]: func TestSetFeesToPay(t *testing.T) + +// TODO[1658]: func TestValidateFulfillments(t *testing.T) + // assertEqualOrderFulfillments asserts that the two order fulfillments are equal. // Returns true if equal. // If not equal, and neither are nil, equality on each field is also asserted in order to help identify the problem. @@ -2091,7 +2121,9 @@ func TestOrderFulfillment_Finalize(t *testing.T) { } } -func TestOrderFulfillment_Validate(t *testing.T) { +// TODO[1658]: func TestOrderFulfillment_Validate(t *testing.T) + +func TestOrderFulfillment_Validate2(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } @@ -3247,10 +3279,10 @@ func TestOrderFulfillment_Validate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var err error testFunc := func() { - err = tc.receiver.Validate() + err = tc.receiver.Validate2() } - require.NotPanics(t, testFunc, "Validate") - assertions.AssertErrorValue(t, err, tc.expErr, "Validate error") + require.NotPanics(t, testFunc, "Validate2") + assertions.AssertErrorValue(t, err, tc.expErr, "Validate2 error") }) } } @@ -4143,6 +4175,8 @@ func TestGetFulfillmentAssetsAmt(t *testing.T) { } } +// TODO[1658]: func TestGetFulfillmentPriceAmt(t *testing.T) + func TestNewPartialFulfillment(t *testing.T) { sdkNewInt64CoinP := func(denom string, amt int64) *sdk.Coin { rv := sdk.NewInt64Coin(denom, amt) diff --git a/x/exchange/helpers.go b/x/exchange/helpers.go index 4700f30c42..bf5f4330bb 100644 --- a/x/exchange/helpers.go +++ b/x/exchange/helpers.go @@ -1,6 +1,8 @@ package exchange import ( + "math/big" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" @@ -95,3 +97,20 @@ func MinSDKInt(a, b sdkmath.Int) sdkmath.Int { } return b } + +// QuoRemInt does a/b returning the integer result and remainder such that a = quo * b + rem +// If y == 0, a division-by-zero run-time panic occurs. +// +// QuoRem implements T-division and modulus (like Go): +// +// quo = x/y with the result truncated to zero +// rem = x - y*q +// +// (See Daan Leijen, “Division and Modulus for Computer Scientists”.) +func QuoRemInt(a, b sdkmath.Int) (quo sdkmath.Int, rem sdkmath.Int) { + var q, r big.Int + q.QuoRem(a.BigInt(), b.BigInt(), &r) + quo = sdkmath.NewIntFromBigInt(&q) + rem = sdkmath.NewIntFromBigInt(&r) + return +} diff --git a/x/exchange/helpers_test.go b/x/exchange/helpers_test.go index 3fb976a73d..b77932689d 100644 --- a/x/exchange/helpers_test.go +++ b/x/exchange/helpers_test.go @@ -1,6 +1,7 @@ package exchange import ( + "regexp" "testing" "github.com/stretchr/testify/assert" @@ -8,6 +9,7 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/provenance-io/provenance/testutil/assertions" ) func TestEqualsUint64(t *testing.T) { @@ -740,3 +742,166 @@ func TestMinSDKInt(t *testing.T) { }) } } + +func TestQuoRemInt(t *testing.T) { + amtRx, err := regexp.Compile(`[,_ ]`) + require.NoError(t, err, "regexp.Compile(`[,_ ]`) error") + newInt := func(amount string) sdkmath.Int { + amt := amtRx.ReplaceAllString(amount, "") + rv, ok := sdkmath.NewIntFromString(amt) + require.True(t, ok, "sdkmath.NewIntFromString(%q) ok bool", amt) + return rv + } + + tests := []struct { + name string + a sdkmath.Int + b sdkmath.Int + expQuo sdkmath.Int + expRem sdkmath.Int + expPanic string + }{ + { + name: "1/0", + a: sdkmath.NewInt(1), + b: sdkmath.NewInt(0), + expPanic: "division by zero", + }, + { + name: "0/1", + a: sdkmath.NewInt(0), + b: sdkmath.NewInt(1), + expQuo: sdkmath.NewInt(0), + expRem: sdkmath.NewInt(0), + }, + { + name: "0/-1", + a: sdkmath.NewInt(0), + b: sdkmath.NewInt(-1), + expQuo: sdkmath.NewInt(0), + expRem: sdkmath.NewInt(0), + }, + { + name: "16/2", + a: sdkmath.NewInt(16), + b: sdkmath.NewInt(2), + expQuo: sdkmath.NewInt(8), + expRem: sdkmath.NewInt(0), + }, + { + name: "-16/2", + a: sdkmath.NewInt(-16), + b: sdkmath.NewInt(2), + expQuo: sdkmath.NewInt(-8), + expRem: sdkmath.NewInt(0), + }, + { + name: "16/-2", + a: sdkmath.NewInt(16), + b: sdkmath.NewInt(-2), + expQuo: sdkmath.NewInt(-8), + expRem: sdkmath.NewInt(0), + }, + { + name: "-16/-2", + a: sdkmath.NewInt(-16), + b: sdkmath.NewInt(-2), + expQuo: sdkmath.NewInt(8), + expRem: sdkmath.NewInt(0), + }, + { + name: "17/2", + a: sdkmath.NewInt(17), + b: sdkmath.NewInt(2), + expQuo: sdkmath.NewInt(8), + expRem: sdkmath.NewInt(1), + }, + { + name: "-17/2", + a: sdkmath.NewInt(-17), + b: sdkmath.NewInt(2), + expQuo: sdkmath.NewInt(-8), + expRem: sdkmath.NewInt(-1), + }, + { + name: "17/-2", + a: sdkmath.NewInt(17), + b: sdkmath.NewInt(-2), + expQuo: sdkmath.NewInt(-8), + expRem: sdkmath.NewInt(1), + }, + { + name: "-17/-2", + a: sdkmath.NewInt(-17), + b: sdkmath.NewInt(-2), + expQuo: sdkmath.NewInt(8), + expRem: sdkmath.NewInt(-1), + }, + { + name: "54321/987", + a: sdkmath.NewInt(54321), + b: sdkmath.NewInt(987), + expQuo: sdkmath.NewInt(55), + expRem: sdkmath.NewInt(36), + }, + { + name: "-54321/987", + a: sdkmath.NewInt(-54321), + b: sdkmath.NewInt(987), + expQuo: sdkmath.NewInt(-55), + expRem: sdkmath.NewInt(-36), + }, + { + name: "54321/-987", + a: sdkmath.NewInt(54321), + b: sdkmath.NewInt(-987), + expQuo: sdkmath.NewInt(-55), + expRem: sdkmath.NewInt(36), + }, + { + name: "-54321/-987", + a: sdkmath.NewInt(-54321), + b: sdkmath.NewInt(-987), + expQuo: sdkmath.NewInt(55), + expRem: sdkmath.NewInt(-36), + }, + { + name: "(10^30+5)/(10^27)", + a: newInt("1,000,000,000,000,000,000,000,000,000,005"), + b: newInt("1,000,000,000,000,000,000,000,000,000"), + expQuo: sdkmath.NewInt(1000), + expRem: sdkmath.NewInt(5), + }, + { + name: "(2*10^30+3*10^9+7)/1,000)", + a: newInt("2,000,000,000,000,000,000,003,000,000,007"), + b: newInt("1,000"), + expQuo: newInt("2,000,000,000,000,000,000,003,000,000"), + expRem: sdkmath.NewInt(7), + }, + { + name: "(3*10^30+9*10^26)/(10^27)", + a: newInt("3,000,900,000,000,000,000,000,000,000,000"), + b: newInt("1,000,000,000,000,000,000,000,000,000"), + expQuo: sdkmath.NewInt(3000), + expRem: newInt("900,000,000,000,000,000,000,000,000"), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var quo, rem sdkmath.Int + testFunc := func() { + quo, rem = QuoRemInt(tc.a, tc.b) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "QuoRemInt(%s, %s)", tc.a, tc.b) + if len(tc.expPanic) == 0 { + assert.Equal(t, tc.expQuo.String(), quo.String(), "QuoRemInt(%s, %s) quo", tc.a, tc.b) + assert.Equal(t, tc.expRem.String(), rem.String(), "QuoRemInt(%s, %s) rem", tc.a, tc.b) + // check that a = quo * b + rem is true regardless of the test's expected values. + expA := quo.Mul(tc.b).Add(rem) + assert.Equal(t, expA.String(), tc.a.String(), "quo * b + rem vs a") + } + }) + } +} diff --git a/x/exchange/market.go b/x/exchange/market.go index f1e2159dd6..fd917f01e8 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -245,9 +245,8 @@ func (r FeeRatio) applyLooselyTo(price sdk.Coin) (sdkmath.Int, bool, error) { if r.Price.Amount.IsZero() { return sdkmath.ZeroInt(), false, fmt.Errorf("cannot apply ratio %s to price %s: division by zero", r, price) } - rv := price.Amount.Mul(r.Fee.Amount) - mustRound := !rv.Mod(r.Price.Amount).IsZero() - rv = rv.Quo(r.Price.Amount) + rv, rem := QuoRemInt(price.Amount.Mul(r.Fee.Amount), r.Price.Amount) + mustRound := !rem.IsZero() if mustRound { rv = rv.Add(sdkmath.OneInt()) } diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 72eeafdc94..2af2d93691 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -217,6 +219,83 @@ func (o Order) Validate() error { return so.Validate() } +// Split splits this order by the provided assets filled. +// If assets filled is zero, this order is returned as unfilled. +// If assets filled equals this order's assets, this order is returned as filled. +// Otherwise, two new orders are returned (copied from this order) with the appropriate assets, price, and fees. +func (o Order) Split(assetsFilledAmt sdkmath.Int) (filled *Order, unfilled *Order, err error) { + orderAssets := o.GetAssets() + orderAssetsAmt := orderAssets.Amount + assetsFilled := sdk.Coin{Denom: orderAssets.Denom, Amount: assetsFilledAmt} + + switch { + case assetsFilledAmt.Equal(orderAssetsAmt): + return &o, nil, nil + case assetsFilledAmt.IsZero(): + return nil, &o, nil + case assetsFilledAmt.IsNegative(): + return nil, nil, fmt.Errorf("cannot split %s order %d having asset %q at %q: amount filled is negative", + o.GetOrderType(), o.OrderId, orderAssets, assetsFilled) + case assetsFilledAmt.GT(orderAssetsAmt): + return nil, nil, fmt.Errorf("cannot split %s order %d having asset %q at %q: overfilled", + o.GetOrderType(), o.OrderId, orderAssets, assetsFilled) + } + + orderPrice := o.GetPrice() + priceFilledAmt, priceRem := QuoRemInt(orderPrice.Amount.Mul(assetsFilledAmt), orderAssetsAmt) + if !priceRem.IsZero() { + return nil, nil, fmt.Errorf("%s order %d having assets %q cannot be partially filled by %q: "+ + "price %q is not evenly divisible", + o.GetOrderType(), o.OrderId, orderAssets, assetsFilled, orderPrice) + } + + orderFees := o.GetSettlementFees() + var feesFilled, feesUnfilled sdk.Coins + if !orderFees.IsZero() { + for _, orderFee := range orderFees { + feeFilled, feeRem := QuoRemInt(orderFee.Amount.Mul(assetsFilledAmt), orderAssetsAmt) + if !feeRem.IsZero() { + return nil, nil, fmt.Errorf("%s order %d having assets %q cannot be partially filled by %q: "+ + "fee %q is not evenly divisible", + o.GetOrderType(), o.OrderId, orderAssets, assetsFilled, orderFee) + } + feesFilled = feesFilled.Add(sdk.NewCoin(orderFee.Denom, feeFilled)) + } + feesUnfilled = orderFees.Sub(feesFilled...) + if feesFilled.IsZero() { + feesFilled = nil + } + if feesUnfilled.IsZero() { + feesUnfilled = nil + } + } + + assetsUnfilled := sdk.NewCoin(orderAssets.Denom, orderAssetsAmt.Sub(assetsFilledAmt)) + priceFilled := sdk.NewCoin(orderPrice.Denom, priceFilledAmt) + priceUnfilled := sdk.NewCoin(orderPrice.Denom, orderPrice.Amount.Sub(priceFilledAmt)) + + switch v := o.Order.(type) { + case *Order_AskOrder: + var feeFilled, feeUnfilled *sdk.Coin + if !feesFilled.IsZero() { + feeFilled = &feesFilled[0] + } + if !feesUnfilled.IsZero() { + feeUnfilled = &feesUnfilled[0] + } + + filled = NewOrder(o.OrderId).WithAsk(v.AskOrder.CopyChange(assetsFilled, priceFilled, feeFilled)) + unfilled = NewOrder(o.OrderId).WithAsk(v.AskOrder.CopyChange(assetsUnfilled, priceUnfilled, feeUnfilled)) + return filled, unfilled, nil + case *Order_BidOrder: + filled = NewOrder(o.OrderId).WithBid(v.BidOrder.CopyChange(assetsFilled, priceFilled, feesFilled)) + unfilled = NewOrder(o.OrderId).WithBid(v.BidOrder.CopyChange(assetsUnfilled, priceUnfilled, feesUnfilled)) + return filled, unfilled, nil + default: + panic(fmt.Errorf("cannot split %s order %d: unknown order type", o.GetOrderType(), o.OrderId)) + } +} + // GetMarketID returns the market id for this ask order. func (a AskOrder) GetMarketID() uint32 { return a.MarketId @@ -312,6 +391,18 @@ func (a AskOrder) Validate() error { return errors.Join(errs...) } +// CopyChange creates a copy of this ask order with the provided assets, price and fee. +func (a AskOrder) CopyChange(newAssets, newPrice sdk.Coin, newFee *sdk.Coin) *AskOrder { + return &AskOrder{ + MarketId: a.MarketId, + Seller: a.Seller, + Assets: newAssets, + Price: newPrice, + SellerSettlementFlatFee: newFee, + AllowPartial: a.AllowPartial, + } +} + // GetMarketID returns the market id for this bid order. func (b BidOrder) GetMarketID() uint32 { return b.MarketId @@ -395,3 +486,15 @@ func (b BidOrder) Validate() error { return errors.Join(errs...) } + +// CopyChange creates a copy of this bid order with the provided assets, price and fees. +func (b BidOrder) CopyChange(newAssets, newPrice sdk.Coin, newFees sdk.Coins) *BidOrder { + return &BidOrder{ + MarketId: b.MarketId, + Buyer: b.Buyer, + Assets: newAssets, + Price: newPrice, + BuyerSettlementFees: newFees, + AllowPartial: b.AllowPartial, + } +} diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 67fe4c7152..cfe8da619c 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -1174,6 +1174,8 @@ func TestOrder_Validate(t *testing.T) { } } +// TODO[1658]: func TestOrder_Split(t *testing.T) + func TestAskOrder_GetMarketID(t *testing.T) { tests := []struct { name string @@ -1634,6 +1636,8 @@ func TestAskOrder_Validate(t *testing.T) { } } +// TODO[1658]: func TestAskOrder_CopyChange(t *testing.T) + func TestBidOrder_GetMarketID(t *testing.T) { tests := []struct { name string @@ -2110,3 +2114,5 @@ func TestBidOrder_Validate(t *testing.T) { }) } } + +// TODO[1658]: func TestBidOrder_CopyChange(t *testing.T) From 4da6be34cdb02960579bcd97e052dceb4256787a Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 4 Oct 2023 17:49:40 -0600 Subject: [PATCH 233/309] [1658]: Move GetAssetTransfer and GetPriceTransfer out of the way and create a new GetAssetTransfer. --- x/exchange/fulfillment.go | 47 +++++++++++++++++++++++++++++++--- x/exchange/fulfillment_test.go | 20 +++++++++------ 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 74cb7e201b..92bc9df023 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -1343,7 +1343,7 @@ func BuildSettlementTransfers(f *Fulfillments) *SettlementTransfers { } for _, of := range allOFs { - rv.OrderTransfers = append(rv.OrderTransfers, GetAssetTransfer(of), GetPriceTransfer(of)) + rv.OrderTransfers = append(rv.OrderTransfers, GetAssetTransfer2(of), GetPriceTransfer2(of)) if !of.FeesToPay.IsZero() { // Using NewCoins in here as a last-ditch negative amount panic check. fees := sdk.NewCoins(of.FeesToPay...) @@ -1357,9 +1357,48 @@ func BuildSettlementTransfers(f *Fulfillments) *SettlementTransfers { } // GetAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. +func GetAssetTransfer(f *OrderFulfillment) (*Transfer, error) { + if !f.AssetsFilledAmt.IsPositive() { + return nil, fmt.Errorf("%s order %d cannot be filled with %s assets: amount not positive", + f.GetOrderType(), f.GetOrderID(), f.GetAssetsFilled()) + } + + indexedDists := newIndexedAddrAmts() + sumDists := sdkmath.ZeroInt() + for _, dist := range f.AssetDists { + if !dist.Amount.IsPositive() { + return nil, fmt.Errorf("%s order %d cannot have %q assets in a transfer: amount not positive", + f.GetOrderType(), f.GetOrderID(), f.assetCoin(dist.Amount)) + } + indexedDists.add(dist.Address, f.assetCoin(dist.Amount)) + sumDists = sumDists.Add(dist.Amount) + } + + if sumDists != f.AssetsFilledAmt { + return nil, fmt.Errorf("%s order %d assets filled %q does not equal assets distributed %q", + f.GetOrderType(), f.GetOrderID(), f.GetAssetsFilled(), f.assetCoin(sumDists)) + } + + if f.IsAskOrder() { + return &Transfer{ + Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, + Outputs: indexedDists.getAsOutputs(), + }, nil + } + if f.IsBidOrder() { + return &Transfer{ + Inputs: indexedDists.getAsInputs(), + Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, + }, nil + } + + return nil, fmt.Errorf("unknown order type %T", f.Order.GetOrder()) +} + +// GetAssetTransfer2 gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. // Assumes that the fulfillment has passed Validate2 already. // Panics if any amounts are negative or if it's neither a bid nor ask order. -func GetAssetTransfer(f *OrderFulfillment) *Transfer { +func GetAssetTransfer2(f *OrderFulfillment) *Transfer { indexedSplits := newIndexedAddrAmts() for _, split := range f.Splits { indexedSplits.add(split.Order.GetOwner(), split.Assets) @@ -1383,10 +1422,10 @@ func GetAssetTransfer(f *OrderFulfillment) *Transfer { panic(fmt.Errorf("unknown order type %T", f.Order.GetOrder())) } -// GetPriceTransfer gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. +// GetPriceTransfer2 gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. // Assumes that the fulfillment has passed Validate2 already. // Panics if any amounts are negative or if it's neither a bid nor ask order. -func GetPriceTransfer(f *OrderFulfillment) *Transfer { +func GetPriceTransfer2(f *OrderFulfillment) *Transfer { indexedSplits := newIndexedAddrAmts() for _, split := range f.Splits { indexedSplits.add(split.Order.GetOwner(), split.Price) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 77bd27cf5a..ddceb52174 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -6147,7 +6147,9 @@ func bankOutputString(o banktypes.Output) string { return fmt.Sprintf("O{Address:%q,Coins:%q}", o.Address, o.Coins) } -func TestGetAssetTransfer(t *testing.T) { +// TODO[1658]: func TestGetAssetTransfer(t *testing.T) + +func TestGetAssetTransfer2(t *testing.T) { coin := func(amt int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amt)} } @@ -6365,15 +6367,17 @@ func TestGetAssetTransfer(t *testing.T) { } }() testFunc := func() { - actual = GetAssetTransfer(tc.f) + actual = GetAssetTransfer2(tc.f) } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetAssetTransfer") - assert.Equal(t, tc.exp, actual, "GetAssetTransfer result") + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetAssetTransfer2") + assert.Equal(t, tc.exp, actual, "GetAssetTransfer2 result") }) } } -func TestGetPriceTransfer(t *testing.T) { +// TODO[1658]: func TestGetPriceTransfer(t *testing.T) + +func TestGetPriceTransfer2(t *testing.T) { coin := func(amt int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amt)} } @@ -6591,10 +6595,10 @@ func TestGetPriceTransfer(t *testing.T) { } }() testFunc := func() { - actual = GetPriceTransfer(tc.f) + actual = GetPriceTransfer2(tc.f) } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetPriceTransfer") - assert.Equal(t, tc.exp, actual, "GetPriceTransfer result") + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetPriceTransfer2") + assert.Equal(t, tc.exp, actual, "GetPriceTransfer2 result") }) } } From 14ee6bf80e57b572720135d174fa7dc327e34ebc Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 5 Oct 2023 10:56:56 -0600 Subject: [PATCH 234/309] [1658]: Finish BuildSettlement (at least until I find problems as I write unit tests). --- x/exchange/fulfillment.go | 241 ++++++++++++++++++++++++++------------ 1 file changed, 166 insertions(+), 75 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 92bc9df023..1bc64e72e3 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -5,6 +5,7 @@ import ( "fmt" sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) @@ -263,8 +264,8 @@ func (f *OrderFulfillment) SplitOrder() (filled *Order, unfilled *Order, err err return filled, unfilled, err } -// SumAssetsAndPrice gets the sum of assets, and the sum of prices of the provided orders. -func SumAssetsAndPrice(orders []*Order) (assets sdk.Coins, price sdk.Coins) { +// sumAssetsAndPrice gets the sum of assets, and the sum of prices of the provided orders. +func sumAssetsAndPrice(orders []*Order) (assets sdk.Coins, price sdk.Coins) { for _, o := range orders { assets = assets.Add(o.GetAssets()) price = price.Add(o.GetPrice()) @@ -281,13 +282,21 @@ func sumPriceLeft(fulfillments []*OrderFulfillment) sdkmath.Int { return rv } +// Settlement contains information on how a set of orders is to be settled. type Settlement struct { - Transfers []*Transfer - FeeInputs []banktypes.Input + // Transfers are all of the inputs and outputs needed to facilitate movement of assets and price. + Transfers []*Transfer + // FeeInputs are the inputs needed to facilitate payment of order fees. + FeeInputs []banktypes.Input + // FullyFilledOrders are all the orders that are fully filled in this settlement. + FullyFilledOrders []*Order + // PartialOrderFilled is a partially filled order with amounts indicating how much was filled. PartialOrderFilled *Order - PartialOrderLeft *Order + // PartialOrderLeft is what's left of the partially filled order. + PartialOrderLeft *Order } +// BuildSettlement processes the provided orders, identifying how the provided orders can be settled. func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) (*Settlement, error) { if err := validateCanSettle(askOrders, bidOrders); err != nil { return nil, err @@ -302,7 +311,7 @@ func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) (* } // Identify any partial order and update its entry in the order fulfillments. - partialyFilled, partialyUnfilled, err := splitPartial(askOFs, bidOFs) + settlement, err := splitPartial(askOFs, bidOFs) if err != nil { return nil, err } @@ -329,17 +338,12 @@ func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) (* return nil, err } - // TODO[1658]: Build the transfers. - - // TODO[1658]: Build the fee inputs. - - settlements := &Settlement{ - Transfers: nil, - FeeInputs: nil, - PartialOrderFilled: partialyFilled, - PartialOrderLeft: partialyUnfilled, + settlement.Transfers, settlement.FeeInputs, err = buildTransfers(askOFs, bidOFs) + if err != nil { + return nil, err } - return settlements, nil + + return settlement, nil } // validateCanSettle does some superficial checking of the provided orders to make sure we can try to settle them. @@ -371,8 +375,8 @@ func validateCanSettle(askOrders, bidOrders []*Order) error { return errors.Join(errs...) } - totalAssetsForSale, totalAskPrice := SumAssetsAndPrice(askOrders) - totalAssetsToBuy, totalBidPrice := SumAssetsAndPrice(bidOrders) + totalAssetsForSale, totalAskPrice := sumAssetsAndPrice(askOrders) + totalAssetsToBuy, totalBidPrice := sumAssetsAndPrice(bidOrders) if len(totalAssetsForSale) != 1 { errs = append(errs, fmt.Errorf("cannot settle with multiple ask order asset denoms %q", totalAssetsForSale)) @@ -435,49 +439,57 @@ func allocateAssets(askOFs, bidOFs []*OrderFulfillment) error { } // splitPartial checks the provided fulfillments for a partial order and splits it out, updating the applicable fulfillment. -func splitPartial(askOFs, bidOFs []*OrderFulfillment) (partialyFilled, partialyUnfilled *Order, err error) { +func splitPartial(askOFs, bidOFs []*OrderFulfillment) (*Settlement, error) { + rv := &Settlement{} + lastAskI := len(askOFs) - 1 for i, askOF := range askOFs { if askOF.AssetsFilledAmt.IsZero() { - return nil, nil, fmt.Errorf("%s order %d (at index %d) has no assets filled", + return nil, fmt.Errorf("%s order %d (at index %d) has no assets filled", askOF.GetOrderType(), askOF.GetOrderID(), i) } if !askOF.AssetsUnfilledAmt.IsZero() { if i != lastAskI { - return nil, nil, fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last ask order provided", + return nil, fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last ask order provided", askOF.GetOrderType(), askOF.GetOrderID(), i) } - partialyFilled, partialyUnfilled, err = askOF.SplitOrder() + var err error + rv.PartialOrderFilled, rv.PartialOrderLeft, err = askOF.SplitOrder() if err != nil { - return nil, nil, err + return nil, err } + } else { + rv.FullyFilledOrders = append(rv.FullyFilledOrders, askOF.Order) } } lastBidI := len(bidOFs) - 1 for i, bidOF := range bidOFs { if bidOF.AssetsFilledAmt.IsZero() { - return nil, nil, fmt.Errorf("%s order %d (at index %d) has no assets filled", + return nil, fmt.Errorf("%s order %d (at index %d) has no assets filled", bidOF.GetOrderType(), bidOF.GetOrderID(), i) } if !bidOF.AssetsUnfilledAmt.IsZero() { if i != lastBidI { - return nil, nil, fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last bid order provided", + return nil, fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last bid order provided", bidOF.GetOrderType(), bidOF.GetOrderID(), i) } - if partialyFilled != nil { - return nil, nil, fmt.Errorf("%s order %d and %s order %d cannot both be partially filled", - partialyFilled.GetOrderType(), partialyFilled.GetOrderID(), + if rv.PartialOrderFilled != nil { + return nil, fmt.Errorf("%s order %d and %s order %d cannot both be partially filled", + rv.PartialOrderFilled.GetOrderType(), rv.PartialOrderFilled.GetOrderID(), bidOF.GetOrderType(), bidOF.GetOrderID()) } - partialyFilled, partialyUnfilled, err = bidOF.SplitOrder() + var err error + rv.PartialOrderFilled, rv.PartialOrderLeft, err = bidOF.SplitOrder() if err != nil { - return nil, nil, err + return nil, err } + } else { + rv.FullyFilledOrders = append(rv.FullyFilledOrders, bidOF.Order) } } - return partialyFilled, partialyUnfilled, err + return rv, nil } // allocatePrice distributes the prices among the fulfillments. @@ -627,6 +639,129 @@ func validateFulfillments(askOFs, bidOFs []*OrderFulfillment) error { return errors.Join(errs...) } +// buildTransfers creates the transfers and inputs for fee payments. +func buildTransfers(askOFs, bidOFs []*OrderFulfillment) ([]*Transfer, []banktypes.Input, error) { + allOFs := make([]*OrderFulfillment, 0, len(askOFs)+len(bidOFs)) + allOFs = append(allOFs, askOFs...) + allOFs = append(allOFs, bidOFs...) + + indexedFees := newIndexedAddrAmts() + transfers := make([]*Transfer, 0, len(allOFs)*2) + + var errs []error + for _, of := range allOFs { + assetTrans, err := getAssetTransfer(of) + if err != nil { + errs = append(errs, err) + } else { + transfers = append(transfers, assetTrans) + } + + priceTrans, err := getPriceTransfer(of) + if err != nil { + errs = append(errs, err) + } else { + transfers = append(transfers, priceTrans) + } + + if !of.FeesToPay.IsZero() { + if of.FeesToPay.IsAnyNegative() { + errs = append(errs, fmt.Errorf("%s order %d cannot pay %q in fees: negative amount", + of.GetOrderType(), of.GetOrderID(), of.FeesToPay)) + } else { + fees := sdk.NewCoins(of.FeesToPay...) + indexedFees.add(of.GetOwner(), fees...) + } + } + } + + if len(errs) > 0 { + return nil, nil, errors.Join(errs...) + } + + feeInputs := indexedFees.getAsInputs() + + return transfers, feeInputs, nil +} + +// getAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. +func getAssetTransfer(f *OrderFulfillment) (*Transfer, error) { + if !f.AssetsFilledAmt.IsPositive() { + return nil, fmt.Errorf("%s order %d cannot be filled with %s assets: amount not positive", + f.GetOrderType(), f.GetOrderID(), f.GetAssetsFilled()) + } + + indexedDists := newIndexedAddrAmts() + sumDists := sdkmath.ZeroInt() + for _, dist := range f.AssetDists { + if !dist.Amount.IsPositive() { + return nil, fmt.Errorf("%s order %d cannot have %q assets in a transfer: amount not positive", + f.GetOrderType(), f.GetOrderID(), f.assetCoin(dist.Amount)) + } + indexedDists.add(dist.Address, f.assetCoin(dist.Amount)) + sumDists = sumDists.Add(dist.Amount) + } + + if !sumDists.Equal(f.AssetsFilledAmt) { + return nil, fmt.Errorf("%s order %d assets filled %q does not equal assets distributed %q", + f.GetOrderType(), f.GetOrderID(), f.GetAssetsFilled(), f.assetCoin(sumDists)) + } + + if f.IsAskOrder() { + return &Transfer{ + Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, + Outputs: indexedDists.getAsOutputs(), + }, nil + } + if f.IsBidOrder() { + return &Transfer{ + Inputs: indexedDists.getAsInputs(), + Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, + }, nil + } + + return nil, fmt.Errorf("unknown order type %T", f.Order.GetOrder()) +} + +// getPriceTransfer gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. +func getPriceTransfer(f *OrderFulfillment) (*Transfer, error) { + if !f.PriceAppliedAmt.IsPositive() { + return nil, fmt.Errorf("%s order %d cannot be filled at price %q: amount not positive", + f.GetOrderType(), f.GetOrderID(), f.GetPriceApplied()) + } + + indexedDists := newIndexedAddrAmts() + sumDists := sdkmath.ZeroInt() + for _, dist := range f.PriceDists { + if !dist.Amount.IsPositive() { + return nil, fmt.Errorf("%s order %d cannot have price %q in a transfer: amount not positive", + f.GetOrderType(), f.GetOrderID(), f.priceCoin(dist.Amount)) + } + indexedDists.add(dist.Address, f.priceCoin(dist.Amount)) + sumDists = sumDists.Add(dist.Amount) + } + + if !sumDists.Equal(f.PriceAppliedAmt) { + return nil, fmt.Errorf("%s order %d price filled %q does not equal price distributed %q", + f.GetOrderType(), f.GetOrderID(), f.GetPriceApplied(), f.priceCoin(sumDists)) + } + + if f.IsAskOrder() { + return &Transfer{ + Inputs: indexedDists.getAsInputs(), + Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceApplied())}}, + }, nil + } + if f.IsBidOrder() { + return &Transfer{ + Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceApplied())}}, + Outputs: indexedDists.getAsOutputs(), + }, nil + } + + return nil, fmt.Errorf("unknown order type %T", f.Order.GetOrder()) +} + // Apply adjusts this order fulfillment using the provided info. func (f *OrderFulfillment) Apply(order *OrderFulfillment, assetsAmt, priceAmt sdkmath.Int) error { assets := sdk.NewCoin(order.GetAssets().Denom, assetsAmt) @@ -808,11 +943,6 @@ func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) (err error) { // Validate makes sure the assets filled and price applied are acceptable for this fulfillment. func (f OrderFulfillment) Validate() error { - // Make sure: - // * order assets = assets filled - // * order price <= price applied (ask orders) - // * order price = price applied (bid orders) - orderPrice := f.GetPrice() switch { case f.IsAskOrder(): @@ -1356,45 +1486,6 @@ func BuildSettlementTransfers(f *Fulfillments) *SettlementTransfers { return rv } -// GetAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. -func GetAssetTransfer(f *OrderFulfillment) (*Transfer, error) { - if !f.AssetsFilledAmt.IsPositive() { - return nil, fmt.Errorf("%s order %d cannot be filled with %s assets: amount not positive", - f.GetOrderType(), f.GetOrderID(), f.GetAssetsFilled()) - } - - indexedDists := newIndexedAddrAmts() - sumDists := sdkmath.ZeroInt() - for _, dist := range f.AssetDists { - if !dist.Amount.IsPositive() { - return nil, fmt.Errorf("%s order %d cannot have %q assets in a transfer: amount not positive", - f.GetOrderType(), f.GetOrderID(), f.assetCoin(dist.Amount)) - } - indexedDists.add(dist.Address, f.assetCoin(dist.Amount)) - sumDists = sumDists.Add(dist.Amount) - } - - if sumDists != f.AssetsFilledAmt { - return nil, fmt.Errorf("%s order %d assets filled %q does not equal assets distributed %q", - f.GetOrderType(), f.GetOrderID(), f.GetAssetsFilled(), f.assetCoin(sumDists)) - } - - if f.IsAskOrder() { - return &Transfer{ - Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, - Outputs: indexedDists.getAsOutputs(), - }, nil - } - if f.IsBidOrder() { - return &Transfer{ - Inputs: indexedDists.getAsInputs(), - Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, - }, nil - } - - return nil, fmt.Errorf("unknown order type %T", f.Order.GetOrder()) -} - // GetAssetTransfer2 gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. // Assumes that the fulfillment has passed Validate2 already. // Panics if any amounts are negative or if it's neither a bid nor ask order. From cea1599f754a80e9c2d80116108b65f8ed719d31 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 5 Oct 2023 11:24:00 -0600 Subject: [PATCH 235/309] [1658]: Wire the keeper's SettleOrders method to use the new BuildSettlement func. --- x/exchange/fulfillment.go | 24 ++++--- x/exchange/keeper/fulfillment.go | 105 +++++++++---------------------- 2 files changed, 43 insertions(+), 86 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 1bc64e72e3..abffa8a093 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -297,7 +297,7 @@ type Settlement struct { } // BuildSettlement processes the provided orders, identifying how the provided orders can be settled. -func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) (*Settlement, error) { +func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatioLookup func(denom string) (*FeeRatio, error)) (*Settlement, error) { if err := validateCanSettle(askOrders, bidOrders); err != nil { return nil, err } @@ -329,6 +329,11 @@ func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) (* return nil, err } + sellerFeeRatio, err := sellerFeeRatioLookup(askOFs[0].GetPrice().Denom) + if err != nil { + return nil, err + } + // Set the fees in the fullfillments if err = setFeesToPay(askOFs, bidOFs, sellerFeeRatio); err != nil { return nil, err @@ -338,7 +343,7 @@ func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) (* return nil, err } - settlement.Transfers, settlement.FeeInputs, err = buildTransfers(askOFs, bidOFs) + err = buildTransfers(askOFs, bidOFs, settlement) if err != nil { return nil, err } @@ -639,8 +644,9 @@ func validateFulfillments(askOFs, bidOFs []*OrderFulfillment) error { return errors.Join(errs...) } -// buildTransfers creates the transfers and inputs for fee payments. -func buildTransfers(askOFs, bidOFs []*OrderFulfillment) ([]*Transfer, []banktypes.Input, error) { +// buildTransfers creates the transfers, inputs for fee payments, +// and fee total and sets those fields in the provided Settlement. +func buildTransfers(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) error { allOFs := make([]*OrderFulfillment, 0, len(askOFs)+len(bidOFs)) allOFs = append(allOFs, askOFs...) allOFs = append(allOFs, bidOFs...) @@ -669,19 +675,19 @@ func buildTransfers(askOFs, bidOFs []*OrderFulfillment) ([]*Transfer, []banktype errs = append(errs, fmt.Errorf("%s order %d cannot pay %q in fees: negative amount", of.GetOrderType(), of.GetOrderID(), of.FeesToPay)) } else { - fees := sdk.NewCoins(of.FeesToPay...) - indexedFees.add(of.GetOwner(), fees...) + indexedFees.add(of.GetOwner(), of.FeesToPay...) } } } if len(errs) > 0 { - return nil, nil, errors.Join(errs...) + return errors.Join(errs...) } - feeInputs := indexedFees.getAsInputs() + settlement.Transfers = transfers + settlement.FeeInputs = indexedFees.getAsInputs() - return transfers, feeInputs, nil + return nil } // getAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index ed9f8f94fb..9828426560 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -299,72 +299,31 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO return errors.Join(aoerr, boerr) } - totalAssetsForSale, totalAskPrice := sumAssetsAndPrice(askOrders) - totalAssetsToBuy, totalBidPrice := sumAssetsAndPrice(bidOrders) - - var errs []error - if len(totalAssetsForSale) != 1 { - errs = append(errs, fmt.Errorf("cannot settle with multiple ask order asset denoms %q", totalAssetsForSale)) - } - if len(totalAskPrice) != 1 { - errs = append(errs, fmt.Errorf("cannot settle with multiple ask order price denoms %q", totalAskPrice)) - } - if len(totalAssetsToBuy) != 1 { - errs = append(errs, fmt.Errorf("cannot settle with multiple bid order asset denoms %q", totalAssetsToBuy)) - } - if len(totalBidPrice) != 1 { - errs = append(errs, fmt.Errorf("cannot settle with multiple bid order price denoms %q", totalBidPrice)) - } - if len(errs) > 0 { - return errors.Join(errs...) - } - - if totalAssetsForSale[0].Denom != totalAssetsToBuy[0].Denom { - errs = append(errs, fmt.Errorf("cannot settle different ask %q and bid %q asset denoms", - totalAssetsForSale, totalAssetsToBuy)) - } - if totalAskPrice[0].Denom != totalBidPrice[0].Denom { - errs = append(errs, fmt.Errorf("cannot settle different ask %q and bid %q price denoms", - totalAskPrice, totalBidPrice)) - } - if len(errs) > 0 { - return errors.Join(errs...) - } - - isPartial := !exchange.CoinsEquals(totalAssetsForSale, totalAssetsToBuy) - if !expectPartial && isPartial { - return fmt.Errorf("total assets for sale %q does not equal total assets to buy %q and partial settlement not expected", - totalAssetsForSale, totalAssetsToBuy) - } - if expectPartial && !isPartial { - return fmt.Errorf("total assets for sale %q equals total assets to buy %q but partial settlement is expected", - totalAssetsForSale, totalAssetsToBuy) - } - - sellerFeeRatio, err := getSellerSettlementRatio(store, marketID, totalAskPrice[0].Denom) - if err != nil { - return err + ratioGetter := func(denom string) (*exchange.FeeRatio, error) { + return getSellerSettlementRatio(store, marketID, denom) } - fulfillments, err := exchange.BuildFulfillments(askOrders, bidOrders, sellerFeeRatio) + settlement, err := exchange.BuildSettlement(askOrders, bidOrders, ratioGetter) if err != nil { return err } - if !expectPartial && fulfillments.PartialOrder != nil { - return fmt.Errorf("settlement resulted in unexpected partial order %d", fulfillments.PartialOrder.NewOrder.GetOrderID()) + if !expectPartial && settlement.PartialOrderFilled != nil { + return fmt.Errorf("settlement resulted in unexpected partial order %d", settlement.PartialOrderFilled.GetOrderID()) } - if expectPartial && fulfillments.PartialOrder == nil { + if expectPartial && settlement.PartialOrderFilled == nil { return errors.New("settlement unexpectedly resulted in all orders fully filled") } - for _, order := range askOrders { + // Release the holds!!!! + var errs []error + for _, order := range settlement.FullyFilledOrders { if err = k.releaseHoldOnOrder(ctx, order); err != nil { errs = append(errs, err) } } - for _, order := range bidOrders { - if err = k.releaseHoldOnOrder(ctx, order); err != nil { + if settlement.PartialOrderFilled != nil { + if err = k.releaseHoldOnOrder(ctx, settlement.PartialOrderFilled); err != nil { errs = append(errs, err) } } @@ -372,47 +331,39 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO return errors.Join(errs...) } - transfers := exchange.BuildSettlementTransfers(fulfillments) - - for _, transfer := range transfers.OrderTransfers { + // Transfer all the things!!!! + for _, transfer := range settlement.Transfers { if err = k.DoTransfer(ctx, transfer.Inputs, transfer.Outputs); err != nil { return err } } - if err = k.CollectFees(ctx, marketID, transfers.FeeInputs); err != nil { + // Collect all the fees (not as exciting). + if err = k.CollectFees(ctx, marketID, settlement.FeeInputs); err != nil { return err } - if fulfillments.PartialOrder != nil { - order := fulfillments.PartialOrder.NewOrder - if err = k.setOrderInStore(store, *order); err != nil { - return fmt.Errorf("could not update partial %s order %d: %w", order.GetOrderType(), order.OrderId, err) - } - if err = k.placeHoldOnOrder(ctx, fulfillments.PartialOrder.NewOrder); err != nil { - return fmt.Errorf("could not replace hold on partial %s order %d: %w", order.GetOrderType(), order.OrderId, err) + // Update the partial order if there was one. + if settlement.PartialOrderLeft != nil { + if err = k.setOrderInStore(store, *settlement.PartialOrderLeft); err != nil { + return fmt.Errorf("could not update partial %s order %d: %w", + settlement.PartialOrderLeft.GetOrderType(), settlement.PartialOrderLeft.OrderId, err) } } + // And emit all the needed events. events := make([]proto.Message, 0, len(askOrders)+len(bidOrders)) - for _, order := range askOrders { - if fulfillments.PartialOrder != nil && fulfillments.PartialOrder.NewOrder.OrderId != order.OrderId { - events = append(events, exchange.NewEventOrderFilled(order.OrderId)) - } + for _, order := range settlement.FullyFilledOrders { + events = append(events, exchange.NewEventOrderFilled(order.OrderId)) } - for _, order := range bidOrders { - if fulfillments.PartialOrder != nil && fulfillments.PartialOrder.NewOrder.OrderId != order.OrderId { - events = append(events, exchange.NewEventOrderFilled(order.OrderId)) - } - } - if fulfillments.PartialOrder != nil { + if settlement.PartialOrderFilled != nil { events = append(events, exchange.NewEventOrderPartiallyFilled( - fulfillments.PartialOrder.NewOrder.OrderId, - fulfillments.PartialOrder.AssetsFilled, - fulfillments.PartialOrder.PriceFilled, + settlement.PartialOrderFilled.OrderId, + settlement.PartialOrderFilled.GetAssets(), + settlement.PartialOrderFilled.GetPrice(), )) } - k.emitEvents(ctx, events) + return nil } From df1a9f1df06ebfd77ac498d672f86d42b341dd0b Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 5 Oct 2023 11:37:52 -0600 Subject: [PATCH 236/309] [1658]: Stub out all the new test funcs needed. --- x/exchange/fulfillment_test.go | 114 +++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 21 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index ddceb52174..17dd8e8fc7 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -215,7 +215,20 @@ func TestNewOrderFulfillment(t *testing.T) { } } -// TODO[1658]: TestNewOrderFulfillments(t *testing.T) +func TestNewOrderFulfillments(t *testing.T) { + // TODO[1658]: func TestNewOrderFulfillments(t *testing.T) + t.Fatalf("not written") +} + +func TestOrderFulfillment_AssetCoin(t *testing.T) { + // TODO[1658]: func TestOrderFulfillment_AssetCoin(t *testing.T) + t.Fatalf("not written") +} + +func TestOrderFulfillment_PriceCoin(t *testing.T) { + // TODO[1658]: func TestOrderFulfillment_PriceCoin(t *testing.T) + t.Fatalf("not written") +} func TestOrderFulfillment_GetAssetsFilled(t *testing.T) { coin := func(amt int64) sdk.Coin { @@ -916,33 +929,90 @@ func TestOrderFulfillment_GetHoldAmount(t *testing.T) { } } -// TODO[1658]: func TestOrderFulfillment_DistributeAssets(t *testing.T) +func TestOrderFulfillment_DistributeAssets(t *testing.T) { + // TODO[1658]: func TestOrderFulfillment_DistributeAssets(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestDistributeAssets(t *testing.T) +func TestDistributeAssets(t *testing.T) { + // TODO[1658]: func TestDistributeAssets(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestOrderFulfillment_DistributePrice(t *testing.T) +func TestOrderFulfillment_DistributePrice(t *testing.T) { + // TODO[1658]: func TestOrderFulfillment_DistributePrice(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestDistributePrice(t *testing.T) +func TestDistributePrice(t *testing.T) { + // TODO[1658]: func TestDistributePrice(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestOrderFulfillment_SplitOrder(t *testing.T) +func TestOrderFulfillment_SplitOrder(t *testing.T) { + // TODO[1658]: func TestOrderFulfillment_SplitOrder(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestSumAssetsAndPrice(t *testing.T) +func TestSumAssetsAndPrice(t *testing.T) { + // TODO[1658]: func TestSumAssetsAndPrice(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestSumPriceLeft(t *testing.T) +func TestSumPriceLeft(t *testing.T) { + // TODO[1658]: func TestSumPriceLeft(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestBuildSettlement(t *testing.T) +func TestBuildSettlement(t *testing.T) { + // TODO[1658]: func TestBuildSettlement(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestValidateCanSettle(t *testing.T) +func TestValidateCanSettle(t *testing.T) { + // TODO[1658]: func TestValidateCanSettle(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestAllocateAssets(t *testing.T) +func TestAllocateAssets(t *testing.T) { + // TODO[1658]: func TestAllocateAssets(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestSplitPartial(t *testing.T) +func TestSplitPartial(t *testing.T) { + // TODO[1658]: func TestSplitPartial(t *testing.T) + t.Skipf("not written") +} + +func TestAllocatePrice(t *testing.T) { + // TODO[1658]: func TestAllocatePrice(t *testing.T) + t.Skipf("not written") +} + +func TestSetFeesToPay(t *testing.T) { + // TODO[1658]: func TestSetFeesToPay(t *testing.T) + t.Skipf("not written") +} + +func TestValidateFulfillments(t *testing.T) { + // TODO[1658]: func TestValidateFulfillments(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestAllocatePrice(t *testing.T) +func TestBuildTransfers(t *testing.T) { + // TODO[1658]: func TestBuildTransfers(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestSetFeesToPay(t *testing.T) +func TestGetAssetTransfer(t *testing.T) { + // TODO[1658]: func TestGetAssetTransfer(t *testing.T) + t.Skipf("not written") +} -// TODO[1658]: func TestValidateFulfillments(t *testing.T) +func TestGetPriceTransfer(t *testing.T) { + // TODO[1658]: func TestGetPriceTransfer(t *testing.T) + t.Skipf("not written") +} // assertEqualOrderFulfillments asserts that the two order fulfillments are equal. // Returns true if equal. @@ -2121,7 +2191,10 @@ func TestOrderFulfillment_Finalize(t *testing.T) { } } -// TODO[1658]: func TestOrderFulfillment_Validate(t *testing.T) +func TestOrderFulfillment_Validate(t *testing.T) { + // TODO[1658]: func TestOrderFulfillment_Validate(t *testing.T) + t.Skipf("not written") +} func TestOrderFulfillment_Validate2(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { @@ -4175,7 +4248,10 @@ func TestGetFulfillmentAssetsAmt(t *testing.T) { } } -// TODO[1658]: func TestGetFulfillmentPriceAmt(t *testing.T) +func TestGetFulfillmentPriceAmt(t *testing.T) { + // TODO[1658]: func TestGetFulfillmentPriceAmt(t *testing.T) + t.Skipf("not written") +} func TestNewPartialFulfillment(t *testing.T) { sdkNewInt64CoinP := func(denom string, amt int64) *sdk.Coin { @@ -6147,8 +6223,6 @@ func bankOutputString(o banktypes.Output) string { return fmt.Sprintf("O{Address:%q,Coins:%q}", o.Address, o.Coins) } -// TODO[1658]: func TestGetAssetTransfer(t *testing.T) - func TestGetAssetTransfer2(t *testing.T) { coin := func(amt int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amt)} @@ -6375,8 +6449,6 @@ func TestGetAssetTransfer2(t *testing.T) { } } -// TODO[1658]: func TestGetPriceTransfer(t *testing.T) - func TestGetPriceTransfer2(t *testing.T) { coin := func(amt int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amt)} From abbf18640fe7b768fabc4c6cc70fe9c473972383 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 5 Oct 2023 12:43:06 -0600 Subject: [PATCH 237/309] [1658]: Unit tests on NewOrderFulfillments, assetCoin, and priceCoin. --- x/exchange/fulfillment_test.go | 518 ++++++++++++++++++++++++++++++--- x/exchange/helpers_test.go | 43 ++- x/exchange/market_test.go | 6 +- x/exchange/orders_test.go | 23 +- 4 files changed, 504 insertions(+), 86 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 17dd8e8fc7..9199f41902 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -122,6 +122,44 @@ func orderFulfillmentString(f *OrderFulfillment) string { return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) } +// assertEqualOrderFulfillments asserts that the two order fulfillments are equal. +// Returns true if equal. +// If not equal, and neither are nil, equality on each field is also asserted in order to help identify the problem. +func assertEqualOrderFulfillments(t *testing.T, expected, actual *OrderFulfillment, message string, args ...interface{}) bool { + t.Helper() + if assert.Equalf(t, expected, actual, message, args...) { + return true + } + // If either is nil, that's easy to understand in the above failure, so there's nothing more to do. + if expected == nil || actual == nil { + return false + } + + msg := func(val string) string { + if len(message) == 0 { + return val + } + return val + "\n" + message + } + + // Assert equality on each individual field so that we can more easily find the problem. + // If any of the Ints fail with a complaint about Int.abs = (big.nat) vs {}, use ZeroAmtAfterSub for the expected. + assert.Equalf(t, expected.Order, actual.Order, msg("OrderFulfillment.Order"), args...) + assert.Equalf(t, expected.Splits, actual.Splits, msg("OrderFulfillment.Splits"), args...) + assert.Equalf(t, expected.AssetsFilledAmt, actual.AssetsFilledAmt, msg("OrderFulfillment.AssetsFilledAmt"), args...) + assert.Equalf(t, expected.AssetsUnfilledAmt, actual.AssetsUnfilledAmt, msg("OrderFulfillment.AssetsUnfilledAmt"), args...) + assert.Equalf(t, expected.PriceAppliedAmt, actual.PriceAppliedAmt, msg("OrderFulfillment.PriceAppliedAmt"), args...) + assert.Equalf(t, expected.PriceLeftAmt, actual.PriceLeftAmt, msg("OrderFulfillment.PriceLeftAmt"), args...) + assert.Equalf(t, expected.IsFinalized, actual.IsFinalized, msg("OrderFulfillment.IsFinalized"), args...) + assert.Equalf(t, expected.FeesToPay, actual.FeesToPay, msg("OrderFulfillment.FeesToPay"), args...) + assert.Equalf(t, expected.OrderFeesLeft, actual.OrderFeesLeft, msg("OrderFulfillment.OrderFeesLeft"), args...) + assert.Equalf(t, expected.PriceFilledAmt, actual.PriceFilledAmt, msg("OrderFulfillment.PriceFilledAmt"), args...) + assert.Equalf(t, expected.PriceUnfilledAmt, actual.PriceUnfilledAmt, msg("OrderFulfillment.PriceUnfilledAmt"), args...) + t.Logf(" Actual: %s", orderFulfillmentString(actual)) + t.Logf("Expected: %s", orderFulfillmentString(expected)) + return false +} + func TestNewOrderFulfillment(t *testing.T) { tests := []struct { name string @@ -216,18 +254,449 @@ func TestNewOrderFulfillment(t *testing.T) { } func TestNewOrderFulfillments(t *testing.T) { - // TODO[1658]: func TestNewOrderFulfillments(t *testing.T) - t.Fatalf("not written") + assetCoin := func(amount int64) sdk.Coin { + return sdk.NewInt64Coin("anise", amount) + } + priceCoin := func(amount int64) sdk.Coin { + return sdk.NewInt64Coin("paprika", amount) + } + feeCoin := func(amount int64) *sdk.Coin { + rv := sdk.NewInt64Coin("fennel", amount) + return &rv + } + + askOrders := make([]*Order, 4) // ids 1, 2, 3, 4 + for j := range askOrders { + i := int64(j) + 1 + order := &AskOrder{ + MarketId: uint32(90 + i), + Seller: fmt.Sprintf("seller-%d", i), + Assets: assetCoin(1000*i + 100*i + 10*i + i), + Price: priceCoin(100*i + 10*i + i), + } + if j%2 == 0 { + order.SellerSettlementFlatFee = feeCoin(10*i + i) + } + if j >= 2 { + order.AllowPartial = true + } + askOrders[j] = NewOrder(uint64(i)).WithAsk(order) + } + + bidOrders := make([]*Order, 4) // ids 5, 6, 7, 8 + for j := range bidOrders { + i := int64(j + 5) + order := &BidOrder{ + MarketId: uint32(90 + i), + Buyer: fmt.Sprintf("buyer-%d", i), + Assets: assetCoin(1000*i + 100*i + 10*i + i), + Price: priceCoin(100*i + 10*i + i), + } + switch j { + case 0: + order.BuyerSettlementFees = sdk.Coins{*feeCoin(10*i + i)} + case 2: + order.BuyerSettlementFees = sdk.Coins{ + *feeCoin(10*i + i), + sdk.NewInt64Coin("garlic", 10000*i+1000*i+100*i+10*i+i), + } + } + if j >= 2 { + order.AllowPartial = true + } + bidOrders[j] = NewOrder(uint64(i)).WithBid(order) + } + + tests := []struct { + name string + orders []*Order + expected []*OrderFulfillment + }{ + { + name: "nil orders", + orders: nil, + expected: []*OrderFulfillment{}, + }, + { + name: "empty orders", + orders: []*Order{}, + expected: []*OrderFulfillment{}, + }, + { + name: "1 ask order", + orders: []*Order{askOrders[0]}, + expected: []*OrderFulfillment{NewOrderFulfillment(askOrders[0])}, + }, + { + name: "1 bid order", + orders: []*Order{bidOrders[0]}, + expected: []*OrderFulfillment{NewOrderFulfillment(bidOrders[0])}, + }, + { + name: "4 ask orders", + orders: []*Order{askOrders[0], askOrders[1], askOrders[2], askOrders[3]}, + expected: []*OrderFulfillment{ + NewOrderFulfillment(askOrders[0]), + NewOrderFulfillment(askOrders[1]), + NewOrderFulfillment(askOrders[2]), + NewOrderFulfillment(askOrders[3]), + }, + }, + { + name: "4 bid orders", + orders: []*Order{bidOrders[0], bidOrders[1], bidOrders[2], bidOrders[3]}, + expected: []*OrderFulfillment{ + NewOrderFulfillment(bidOrders[0]), + NewOrderFulfillment(bidOrders[1]), + NewOrderFulfillment(bidOrders[2]), + NewOrderFulfillment(bidOrders[3]), + }, + }, + { + name: "1 bid 1 ask", + orders: []*Order{askOrders[1], bidOrders[2]}, + expected: []*OrderFulfillment{ + NewOrderFulfillment(askOrders[1]), + NewOrderFulfillment(bidOrders[2]), + }, + }, + { + name: "1 ask 1 bid", + orders: []*Order{bidOrders[1], askOrders[2]}, + expected: []*OrderFulfillment{ + NewOrderFulfillment(bidOrders[1]), + NewOrderFulfillment(askOrders[2]), + }, + }, + { + name: "4 asks 4 bids", + orders: []*Order{ + askOrders[0], askOrders[1], askOrders[2], askOrders[3], + bidOrders[3], bidOrders[2], bidOrders[1], bidOrders[0], + }, + expected: []*OrderFulfillment{ + NewOrderFulfillment(askOrders[0]), + NewOrderFulfillment(askOrders[1]), + NewOrderFulfillment(askOrders[2]), + NewOrderFulfillment(askOrders[3]), + NewOrderFulfillment(bidOrders[3]), + NewOrderFulfillment(bidOrders[2]), + NewOrderFulfillment(bidOrders[1]), + NewOrderFulfillment(bidOrders[0]), + }, + }, + { + name: "4 bids 4 asks", + orders: []*Order{ + bidOrders[0], bidOrders[1], bidOrders[2], bidOrders[3], + askOrders[3], askOrders[2], askOrders[1], askOrders[0], + }, + expected: []*OrderFulfillment{ + NewOrderFulfillment(bidOrders[0]), + NewOrderFulfillment(bidOrders[1]), + NewOrderFulfillment(bidOrders[2]), + NewOrderFulfillment(bidOrders[3]), + NewOrderFulfillment(askOrders[3]), + NewOrderFulfillment(askOrders[2]), + NewOrderFulfillment(askOrders[1]), + NewOrderFulfillment(askOrders[0]), + }, + }, + { + name: "interweaved 4 asks 4 bids", + orders: []*Order{ + bidOrders[3], askOrders[0], askOrders[3], bidOrders[1], + bidOrders[0], askOrders[1], bidOrders[2], askOrders[2], + }, + expected: []*OrderFulfillment{ + NewOrderFulfillment(bidOrders[3]), + NewOrderFulfillment(askOrders[0]), + NewOrderFulfillment(askOrders[3]), + NewOrderFulfillment(bidOrders[1]), + NewOrderFulfillment(bidOrders[0]), + NewOrderFulfillment(askOrders[1]), + NewOrderFulfillment(bidOrders[2]), + NewOrderFulfillment(askOrders[2]), + }, + }, + { + name: "duplicated entries", + orders: []*Order{ + askOrders[3], bidOrders[2], askOrders[3], bidOrders[2], + }, + expected: []*OrderFulfillment{ + NewOrderFulfillment(askOrders[3]), + NewOrderFulfillment(bidOrders[2]), + NewOrderFulfillment(askOrders[3]), + NewOrderFulfillment(bidOrders[2]), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual []*OrderFulfillment + testFunc := func() { + actual = NewOrderFulfillments(tc.orders) + } + require.NotPanics(t, testFunc, "NewOrderFulfillments") + if !assert.Equal(t, tc.expected, actual, "NewOrderFulfillments result") { + expOrderIDs := make([]uint64, len(tc.expected)) + for i, f := range tc.expected { + expOrderIDs[i] = f.GetOrderID() + } + actOrderIDs := make([]uint64, len(actual)) + for i, f := range actual { + actOrderIDs[i] = f.GetOrderID() + } + if !assert.Equal(t, expOrderIDs, actOrderIDs, "NewOrderFulfillments result order ids") && len(tc.expected) == len(actual) { + for i := range tc.expected { + assertEqualOrderFulfillments(t, tc.expected[i], actual[i], "NewOrderFulfillments result[%d]", i) + } + } + } + }) + } } func TestOrderFulfillment_AssetCoin(t *testing.T) { - // TODO[1658]: func TestOrderFulfillment_AssetCoin(t *testing.T) - t.Fatalf("not written") + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + bigCoin := func(amount, denom string) sdk.Coin { + amt := newInt(t, amount) + return sdk.Coin{Denom: denom, Amount: amt} + } + askOrder := func(orderID uint64, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Assets: assets, + Price: price, + }) + } + bidOrder := func(orderID uint64, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Assets: assets, + Price: price, + }) + } + + tests := []struct { + name string + receiver OrderFulfillment + amt sdkmath.Int + expected sdk.Coin + expPanic string + }{ + { + name: "nil order", + receiver: OrderFulfillment{Order: nil}, + amt: sdkmath.NewInt(0), + expPanic: "runtime error: invalid memory address or nil pointer dereference", + }, + { + name: "nil inside order", + receiver: OrderFulfillment{Order: NewOrder(1)}, + amt: sdkmath.NewInt(0), + expPanic: "order 1 has unknown sub-order type : does not implement SubOrderI", + }, + { + name: "unknown inside order", + receiver: OrderFulfillment{Order: newUnknownOrder(2)}, + amt: sdkmath.NewInt(0), + expPanic: "order 2 has unknown sub-order type *exchange.unknownOrderType: does not implement SubOrderI", + }, + { + name: "ask order", + receiver: OrderFulfillment{Order: askOrder(3, coin(4, "apple"), coin(5, "plum"))}, + amt: sdkmath.NewInt(6), + expected: coin(6, "apple"), + }, + { + name: "ask order with negative assets", + receiver: OrderFulfillment{Order: askOrder(7, coin(-8, "apple"), coin(9, "plum"))}, + amt: sdkmath.NewInt(10), + expected: coin(10, "apple"), + }, + { + name: "ask order, negative amt", + receiver: OrderFulfillment{Order: askOrder(11, coin(12, "apple"), coin(13, "plum"))}, + amt: sdkmath.NewInt(-14), + expected: coin(-14, "apple"), + }, + { + name: "ask order with negative assets, negative amt", + receiver: OrderFulfillment{Order: askOrder(15, coin(-16, "apple"), coin(17, "plum"))}, + amt: sdkmath.NewInt(-18), + expected: coin(-18, "apple"), + }, + { + name: "ask order, big amt", + receiver: OrderFulfillment{Order: askOrder(19, coin(20, "apple"), coin(21, "plum"))}, + amt: newInt(t, "123,000,000,000,000,000,000,000,000,000,000,321"), + expected: bigCoin("123,000,000,000,000,000,000,000,000,000,000,321", "apple"), + }, + { + name: "bid order", + receiver: OrderFulfillment{Order: bidOrder(3, coin(4, "apple"), coin(5, "plum"))}, + amt: sdkmath.NewInt(6), + expected: coin(6, "apple"), + }, + { + name: "bid order with negative assets", + receiver: OrderFulfillment{Order: bidOrder(7, coin(-8, "apple"), coin(9, "plum"))}, + amt: sdkmath.NewInt(10), + expected: coin(10, "apple"), + }, + { + name: "bid order, negative amt", + receiver: OrderFulfillment{Order: bidOrder(11, coin(12, "apple"), coin(13, "plum"))}, + amt: sdkmath.NewInt(-14), + expected: coin(-14, "apple"), + }, + { + name: "bid order with negative assets, negative amt", + receiver: OrderFulfillment{Order: bidOrder(15, coin(-16, "apple"), coin(17, "plum"))}, + amt: sdkmath.NewInt(-18), + expected: coin(-18, "apple"), + }, + { + name: "bid order, big amt", + receiver: OrderFulfillment{Order: bidOrder(19, coin(20, "apple"), coin(21, "plum"))}, + amt: newInt(t, "123,000,000,000,000,000,000,000,000,000,000,321"), + expected: bigCoin("123,000,000,000,000,000,000,000,000,000,000,321", "apple"), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.receiver.assetCoin(tc.amt) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "assetCoin(%s)", tc.amt) + assert.Equal(t, tc.expected.String(), actual.String(), "assetCoin(%s) result", tc.amt) + }) + } } func TestOrderFulfillment_PriceCoin(t *testing.T) { - // TODO[1658]: func TestOrderFulfillment_PriceCoin(t *testing.T) - t.Fatalf("not written") + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + bigCoin := func(amount, denom string) sdk.Coin { + amt := newInt(t, amount) + return sdk.Coin{Denom: denom, Amount: amt} + } + askOrder := func(orderID uint64, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Assets: assets, + Price: price, + }) + } + bidOrder := func(orderID uint64, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Assets: assets, + Price: price, + }) + } + + tests := []struct { + name string + receiver OrderFulfillment + amt sdkmath.Int + expected sdk.Coin + expPanic string + }{ + { + name: "nil order", + receiver: OrderFulfillment{Order: nil}, + amt: sdkmath.NewInt(0), + expPanic: "runtime error: invalid memory address or nil pointer dereference", + }, + { + name: "nil inside order", + receiver: OrderFulfillment{Order: NewOrder(1)}, + amt: sdkmath.NewInt(0), + expPanic: "order 1 has unknown sub-order type : does not implement SubOrderI", + }, + { + name: "unknown inside order", + receiver: OrderFulfillment{Order: newUnknownOrder(2)}, + amt: sdkmath.NewInt(0), + expPanic: "order 2 has unknown sub-order type *exchange.unknownOrderType: does not implement SubOrderI", + }, + { + name: "ask order", + receiver: OrderFulfillment{Order: askOrder(3, coin(4, "apple"), coin(5, "plum"))}, + amt: sdkmath.NewInt(6), + expected: coin(6, "plum"), + }, + { + name: "ask order with negative assets", + receiver: OrderFulfillment{Order: askOrder(7, coin(-8, "apple"), coin(9, "plum"))}, + amt: sdkmath.NewInt(10), + expected: coin(10, "plum"), + }, + { + name: "ask order, negative amt", + receiver: OrderFulfillment{Order: askOrder(11, coin(12, "apple"), coin(13, "plum"))}, + amt: sdkmath.NewInt(-14), + expected: coin(-14, "plum"), + }, + { + name: "ask order with negative assets, negative amt", + receiver: OrderFulfillment{Order: askOrder(15, coin(-16, "apple"), coin(17, "plum"))}, + amt: sdkmath.NewInt(-18), + expected: coin(-18, "plum"), + }, + { + name: "ask order, big amt", + receiver: OrderFulfillment{Order: askOrder(19, coin(20, "apple"), coin(21, "plum"))}, + amt: newInt(t, "123,000,000,000,000,000,000,000,000,000,000,321"), + expected: bigCoin("123,000,000,000,000,000,000,000,000,000,000,321", "plum"), + }, + { + name: "bid order", + receiver: OrderFulfillment{Order: bidOrder(3, coin(4, "apple"), coin(5, "plum"))}, + amt: sdkmath.NewInt(6), + expected: coin(6, "plum"), + }, + { + name: "bid order with negative assets", + receiver: OrderFulfillment{Order: bidOrder(7, coin(-8, "apple"), coin(9, "plum"))}, + amt: sdkmath.NewInt(10), + expected: coin(10, "plum"), + }, + { + name: "bid order, negative amt", + receiver: OrderFulfillment{Order: bidOrder(11, coin(12, "apple"), coin(13, "plum"))}, + amt: sdkmath.NewInt(-14), + expected: coin(-14, "plum"), + }, + { + name: "bid order with negative assets, negative amt", + receiver: OrderFulfillment{Order: bidOrder(15, coin(-16, "apple"), coin(17, "plum"))}, + amt: sdkmath.NewInt(-18), + expected: coin(-18, "plum"), + }, + { + name: "bid order, big amt", + receiver: OrderFulfillment{Order: bidOrder(19, coin(20, "apple"), coin(21, "plum"))}, + amt: newInt(t, "123,000,000,000,000,000,000,000,000,000,000,321"), + expected: bigCoin("123,000,000,000,000,000,000,000,000,000,000,321", "plum"), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.receiver.priceCoin(tc.amt) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "priceCoin(%s)", tc.amt) + assert.Equal(t, tc.expected.String(), actual.String(), "priceCoin(%s) result", tc.amt) + }) + } } func TestOrderFulfillment_GetAssetsFilled(t *testing.T) { @@ -1014,43 +1483,6 @@ func TestGetPriceTransfer(t *testing.T) { t.Skipf("not written") } -// assertEqualOrderFulfillments asserts that the two order fulfillments are equal. -// Returns true if equal. -// If not equal, and neither are nil, equality on each field is also asserted in order to help identify the problem. -func assertEqualOrderFulfillments(t *testing.T, expected, actual *OrderFulfillment, message string, args ...interface{}) bool { - if assert.Equalf(t, expected, actual, message, args...) { - return true - } - // If either is nil, that's easy to understand in the above failure, so there's nothing more to do. - if expected == nil || actual == nil { - return false - } - - msg := func(val string) string { - if len(message) == 0 { - return val - } - return val + "\n" + message - } - - // Assert equality on each individual field so that we can more easily find the problem. - // If any of the Ints fail with a complaint about Int.abs = (big.nat) vs {}, use ZeroAmtAfterSub for the expected. - assert.Equalf(t, expected.Order, actual.Order, msg("OrderFulfillment.Order"), args...) - assert.Equalf(t, expected.Splits, actual.Splits, msg("OrderFulfillment.Splits"), args...) - assert.Equalf(t, expected.AssetsFilledAmt, actual.AssetsFilledAmt, msg("OrderFulfillment.AssetsFilledAmt"), args...) - assert.Equalf(t, expected.AssetsUnfilledAmt, actual.AssetsUnfilledAmt, msg("OrderFulfillment.AssetsUnfilledAmt"), args...) - assert.Equalf(t, expected.PriceAppliedAmt, actual.PriceAppliedAmt, msg("OrderFulfillment.PriceAppliedAmt"), args...) - assert.Equalf(t, expected.PriceLeftAmt, actual.PriceLeftAmt, msg("OrderFulfillment.PriceLeftAmt"), args...) - assert.Equalf(t, expected.IsFinalized, actual.IsFinalized, msg("OrderFulfillment.IsFinalized"), args...) - assert.Equalf(t, expected.FeesToPay, actual.FeesToPay, msg("OrderFulfillment.FeesToPay"), args...) - assert.Equalf(t, expected.OrderFeesLeft, actual.OrderFeesLeft, msg("OrderFulfillment.OrderFeesLeft"), args...) - assert.Equalf(t, expected.PriceFilledAmt, actual.PriceFilledAmt, msg("OrderFulfillment.PriceFilledAmt"), args...) - assert.Equalf(t, expected.PriceUnfilledAmt, actual.PriceUnfilledAmt, msg("OrderFulfillment.PriceUnfilledAmt"), args...) - t.Logf(" Actual: %s", orderFulfillmentString(actual)) - t.Logf("Expected: %s", orderFulfillmentString(expected)) - return false -} - func TestOrderFulfillment_Apply(t *testing.T) { assetCoin := func(amt int64) sdk.Coin { return sdk.Coin{Denom: "acoin", Amount: sdkmath.NewInt(amt)} diff --git a/x/exchange/helpers_test.go b/x/exchange/helpers_test.go index b77932689d..0a1f1ec224 100644 --- a/x/exchange/helpers_test.go +++ b/x/exchange/helpers_test.go @@ -12,6 +12,16 @@ import ( "github.com/provenance-io/provenance/testutil/assertions" ) +var amtRx = regexp.MustCompile(`[,_ ]`) + +// newInt converts the provided string into an Int, stipping out any commas, underscores or spaces first. +func newInt(t *testing.T, amount string) sdkmath.Int { + amt := amtRx.ReplaceAllString(amount, "") + rv, ok := sdkmath.NewIntFromString(amt) + require.True(t, ok, "sdkmath.NewIntFromString(%q) ok bool", amt) + return rv +} + func TestEqualsUint64(t *testing.T) { tests := []struct { name string @@ -655,13 +665,7 @@ func TestContainsCoinWithSameDenom(t *testing.T) { } func TestMinSDKInt(t *testing.T) { - newInt := func(val string) sdkmath.Int { - rv, ok := sdkmath.NewIntFromString(val) - require.True(t, ok, "sdkmath.NewIntFromString(%s) resulting bool", val) - return rv - } - - posBig := newInt("123456789012345678901234567890") + posBig := newInt(t, "123,456,789,012,345,678,901,234,567,890") negBig := posBig.Neg() posBigger := posBig.Add(sdkmath.OneInt()) @@ -744,15 +748,6 @@ func TestMinSDKInt(t *testing.T) { } func TestQuoRemInt(t *testing.T) { - amtRx, err := regexp.Compile(`[,_ ]`) - require.NoError(t, err, "regexp.Compile(`[,_ ]`) error") - newInt := func(amount string) sdkmath.Int { - amt := amtRx.ReplaceAllString(amount, "") - rv, ok := sdkmath.NewIntFromString(amt) - require.True(t, ok, "sdkmath.NewIntFromString(%q) ok bool", amt) - return rv - } - tests := []struct { name string a sdkmath.Int @@ -867,24 +862,24 @@ func TestQuoRemInt(t *testing.T) { }, { name: "(10^30+5)/(10^27)", - a: newInt("1,000,000,000,000,000,000,000,000,000,005"), - b: newInt("1,000,000,000,000,000,000,000,000,000"), + a: newInt(t, "1,000,000,000,000,000,000,000,000,000,005"), + b: newInt(t, "1,000,000,000,000,000,000,000,000,000"), expQuo: sdkmath.NewInt(1000), expRem: sdkmath.NewInt(5), }, { name: "(2*10^30+3*10^9+7)/1,000)", - a: newInt("2,000,000,000,000,000,000,003,000,000,007"), - b: newInt("1,000"), - expQuo: newInt("2,000,000,000,000,000,000,003,000,000"), + a: newInt(t, "2,000,000,000,000,000,000,003,000,000,007"), + b: newInt(t, "1,000"), + expQuo: newInt(t, "2,000,000,000,000,000,000,003,000,000"), expRem: sdkmath.NewInt(7), }, { name: "(3*10^30+9*10^26)/(10^27)", - a: newInt("3,000,900,000,000,000,000,000,000,000,000"), - b: newInt("1,000,000,000,000,000,000,000,000,000"), + a: newInt(t, "3,000,900,000,000,000,000,000,000,000,000"), + b: newInt(t, "1,000,000,000,000,000,000,000,000,000"), expQuo: sdkmath.NewInt(3000), - expRem: newInt("900,000,000,000,000,000,000,000,000"), + expRem: newInt(t, "900,000,000,000,000,000,000,000,000"), }, } diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index ebed1e4be8..6aba8c6435 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -1010,8 +1010,7 @@ func TestFeeRatio_ApplyTo(t *testing.T) { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } bigCoin := func(amount string, denom string) sdk.Coin { - amt, ok := sdkmath.NewIntFromString(amount) - require.True(t, ok, "sdkmath.NewIntFromString(%q) ok result boolean", amount) + amt := newInt(t, amount) return sdk.Coin{Denom: denom, Amount: amt} } feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { @@ -1094,8 +1093,7 @@ func TestFeeRatio_ApplyToLoosely(t *testing.T) { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } bigCoin := func(amount string, denom string) sdk.Coin { - amt, ok := sdkmath.NewIntFromString(amount) - require.True(t, ok, "sdkmath.NewIntFromString(%q) ok result boolean", amount) + amt := newInt(t, amount) return sdk.Coin{Denom: denom, Amount: amt} } feeRatio := func(priceAmount int64, priceDenom string, feeAmount int64, feeDenom string) FeeRatio { diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index cfe8da619c..9ccfb7832b 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -284,9 +284,8 @@ func TestOrderSizes(t *testing.T) { denomChars := "abcd" bigCoins := make(sdk.Coins, len(denomChars)) for i := range bigCoins { - str := fmt.Sprintf("%[1]d00000000000000000000000000000%[1]d", i) // i quetta + i - amount, ok := sdkmath.NewIntFromString(str) - require.Truef(t, ok, "sdkmath.NewIntFromString(%q)", str) + str := fmt.Sprintf("%[1]d,000,000,000,000,000,000,000,000,000,00%[1]d", i) // i quetta + i + amount := newInt(t, str) denom := strings.Repeat(denomChars[i:i+1], 128) bigCoins[i] = sdk.NewCoin(denom, amount) } @@ -1230,8 +1229,7 @@ func TestAskOrder_GetOwner(t *testing.T) { } func TestAskOrder_GetAssets(t *testing.T) { - largeAmt, ok := sdkmath.NewIntFromString("25000000000000000000000") - require.Truef(t, ok, "NewIntFromString(\"25000000000000000000000\")") + largeAmt := newInt(t, "25,000,000,000,000,000,000,000") largeCoin := sdk.NewCoin("large", largeAmt) negCoin := sdk.Coin{Denom: "neg", Amount: sdkmath.NewInt(-88)} @@ -1260,8 +1258,7 @@ func TestAskOrder_GetAssets(t *testing.T) { } func TestAskOrder_GetPrice(t *testing.T) { - largeAmt, ok := sdkmath.NewIntFromString("25000000000000000000000") - require.Truef(t, ok, "NewIntFromString(\"25000000000000000000000\")") + largeAmt := newInt(t, "25,000,000,000,000,000,000,000") largeCoin := sdk.NewCoin("large", largeAmt) negCoin := sdk.Coin{Denom: "neg", Amount: sdkmath.NewInt(-88)} @@ -1293,8 +1290,7 @@ func TestAskOrder_GetSettlementFees(t *testing.T) { coin := func(amount int64, denom string) *sdk.Coin { return &sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } - largeAmt, ok := sdkmath.NewIntFromString("25000000000000000000000") - require.Truef(t, ok, "NewIntFromString(\"25000000000000000000000\")") + largeAmt := newInt(t, "25,000,000,000,000,000,000,000") largeCoin := sdk.NewCoin("large", largeAmt) tests := []struct { @@ -1692,8 +1688,7 @@ func TestBidOrder_GetOwner(t *testing.T) { } func TestBidOrder_GetAssets(t *testing.T) { - largeAmt, ok := sdkmath.NewIntFromString("25000000000000000000000") - require.Truef(t, ok, "NewIntFromString(\"25000000000000000000000\")") + largeAmt := newInt(t, "25,000,000,000,000,000,000,000") largeCoin := sdk.NewCoin("large", largeAmt) negCoin := sdk.Coin{Denom: "neg", Amount: sdkmath.NewInt(-88)} @@ -1722,8 +1717,7 @@ func TestBidOrder_GetAssets(t *testing.T) { } func TestBidOrder_GetPrice(t *testing.T) { - largeAmt, ok := sdkmath.NewIntFromString("25000000000000000000000") - require.Truef(t, ok, "NewIntFromString(\"25000000000000000000000\")") + largeAmt := newInt(t, "25,000,000,000,000,000,000,000") largeCoin := sdk.NewCoin("large", largeAmt) negCoin := sdk.Coin{Denom: "neg", Amount: sdkmath.NewInt(-88)} @@ -1755,8 +1749,7 @@ func TestBidOrder_GetSettlementFees(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } - largeAmt, ok := sdkmath.NewIntFromString("25000000000000000000000") - require.Truef(t, ok, "NewIntFromString(\"25000000000000000000000\")") + largeAmt := newInt(t, "25,000,000,000,000,000,000,000") largeCoin := sdk.NewCoin("large", largeAmt) tests := []struct { From 40a4c0a97e326fd3d9de2d45324d20810e456a46 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 5 Oct 2023 12:49:16 -0600 Subject: [PATCH 238/309] [1658]: Unit tests on GetFulfillmentPriceAmt. --- x/exchange/fulfillment_test.go | 124 ++++++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 10 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 9199f41902..c55c055318 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -4642,17 +4642,17 @@ func TestGetFulfillmentAssetsAmt(t *testing.T) { }, } if c.expAmt == 0 { - newTests[0].expErr = fmt.Sprintf("cannot fill ask order 1 having assets left \"%done\" "+ - "with bid order 2 having assets left \"%dtwo\": zero or negative assets left", + newTests[0].expErr = fmt.Sprintf("cannot fill ask order 1 having assets left \"%done\" with bid "+ + "order 2 having assets left \"%dtwo\": zero or negative assets left", c.of1Unfilled, c.of2Unfilled) - newTests[1].expErr = fmt.Sprintf("cannot fill bid order 1 having assets left \"%done\" "+ - "with ask order 2 having assets left \"%dtwo\": zero or negative assets left", + newTests[1].expErr = fmt.Sprintf("cannot fill bid order 1 having assets left \"%done\" with ask "+ + "order 2 having assets left \"%dtwo\": zero or negative assets left", c.of1Unfilled, c.of2Unfilled) - newTests[2].expErr = fmt.Sprintf("cannot fill ask order 1 having assets left \"%done\" "+ - "with ask order 2 having assets left \"%dtwo\": zero or negative assets left", + newTests[2].expErr = fmt.Sprintf("cannot fill ask order 1 having assets left \"%done\" with ask "+ + "order 2 having assets left \"%dtwo\": zero or negative assets left", c.of1Unfilled, c.of2Unfilled) - newTests[3].expErr = fmt.Sprintf("cannot fill bid order 1 having assets left \"%done\" "+ - "with bid order 2 having assets left \"%dtwo\": zero or negative assets left", + newTests[3].expErr = fmt.Sprintf("cannot fill bid order 1 having assets left \"%done\" with bid "+ + "order 2 having assets left \"%dtwo\": zero or negative assets left", c.of1Unfilled, c.of2Unfilled) } tests = append(tests, newTests...) @@ -4681,8 +4681,112 @@ func TestGetFulfillmentAssetsAmt(t *testing.T) { } func TestGetFulfillmentPriceAmt(t *testing.T) { - // TODO[1658]: func TestGetFulfillmentPriceAmt(t *testing.T) - t.Skipf("not written") + newAskOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *OrderFulfillment { + return &OrderFulfillment{ + Order: NewOrder(orderID).WithAsk(&AskOrder{ + Price: sdk.NewInt64Coin(assetDenom, 999), + }), + PriceLeftAmt: sdkmath.NewInt(assetsUnfilled), + } + } + newBidOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *OrderFulfillment { + return &OrderFulfillment{ + Order: NewOrder(orderID).WithBid(&BidOrder{ + Price: sdk.NewInt64Coin(assetDenom, 999), + }), + PriceLeftAmt: sdkmath.NewInt(assetsUnfilled), + } + } + + cases := []struct { + name string + of1Unfilled int64 + of2Unfilled int64 + expAmt int64 + }{ + {name: "of1 zero", of1Unfilled: 0, of2Unfilled: 3, expAmt: 0}, + {name: "of1 negative", of1Unfilled: -4, of2Unfilled: 3, expAmt: 0}, + {name: "of2 zero", of1Unfilled: 5, of2Unfilled: 0, expAmt: 0}, + {name: "of2 negative", of1Unfilled: 5, of2Unfilled: -6, expAmt: 0}, + {name: "equal", of1Unfilled: 8, of2Unfilled: 8, expAmt: 8}, + {name: "of1 has fewer", of1Unfilled: 9, of2Unfilled: 10, expAmt: 9}, + {name: "of2 has fewer", of1Unfilled: 12, of2Unfilled: 11, expAmt: 11}, + } + + type testCase struct { + name string + of1 *OrderFulfillment + of2 *OrderFulfillment + expAmt sdkmath.Int + expErr string + } + + tests := make([]testCase, 0, len(cases)*4) + + for _, c := range cases { + newTests := []testCase{ + { + name: "ask bid " + c.name, + of1: newAskOF(1, c.of1Unfilled, "one"), + of2: newBidOF(2, c.of2Unfilled, "two"), + expAmt: sdkmath.NewInt(c.expAmt), + }, + { + name: "bid ask " + c.name, + of1: newBidOF(1, c.of1Unfilled, "one"), + of2: newAskOF(2, c.of2Unfilled, "two"), + expAmt: sdkmath.NewInt(c.expAmt), + }, + { + name: "ask ask " + c.name, + of1: newAskOF(1, c.of1Unfilled, "one"), + of2: newAskOF(2, c.of2Unfilled, "two"), + expAmt: sdkmath.NewInt(c.expAmt), + }, + { + name: "bid bid " + c.name, + of1: newBidOF(1, c.of1Unfilled, "one"), + of2: newBidOF(2, c.of2Unfilled, "two"), + expAmt: sdkmath.NewInt(c.expAmt), + }, + } + if c.expAmt == 0 { + newTests[0].expErr = fmt.Sprintf("cannot fill ask order 1 having price left \"%done\" with bid "+ + "order 2 having price left \"%dtwo\": zero or negative price left", + c.of1Unfilled, c.of2Unfilled) + newTests[1].expErr = fmt.Sprintf("cannot fill bid order 1 having price left \"%done\" with ask "+ + "order 2 having price left \"%dtwo\": zero or negative price left", + c.of1Unfilled, c.of2Unfilled) + newTests[2].expErr = fmt.Sprintf("cannot fill ask order 1 having price left \"%done\" with ask "+ + "order 2 having price left \"%dtwo\": zero or negative price left", + c.of1Unfilled, c.of2Unfilled) + newTests[3].expErr = fmt.Sprintf("cannot fill bid order 1 having price left \"%done\" with bid "+ + "order 2 having price left \"%dtwo\": zero or negative price left", + c.of1Unfilled, c.of2Unfilled) + } + tests = append(tests, newTests...) + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if len(tc.expErr) > 0 { + tc.expAmt = sdkmath.ZeroInt() + } + origOF1 := copyOrderFulfillment(tc.of1) + origOF2 := copyOrderFulfillment(tc.of2) + + var amt sdkmath.Int + var err error + testFunc := func() { + amt, err = GetFulfillmentPriceAmt(tc.of1, tc.of2) + } + require.NotPanics(t, testFunc, "GetFulfillmentPriceAmt") + assertions.AssertErrorValue(t, err, tc.expErr, "GetFulfillmentPriceAmt error") + assert.Equal(t, tc.expAmt, amt, "GetFulfillmentPriceAmt amount") + assertEqualOrderFulfillments(t, origOF1, tc.of1, "of1 after GetFulfillmentPriceAmt") + assertEqualOrderFulfillments(t, origOF2, tc.of2, "of2 after GetFulfillmentPriceAmt") + }) + } } func TestNewPartialFulfillment(t *testing.T) { From 127145b69f32d68ffad025f6330c9f4c618841f2 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 5 Oct 2023 13:10:40 -0600 Subject: [PATCH 239/309] [1658]: Unit tests on sumAssetsAndPrice and sumPriceLeft. --- x/exchange/fulfillment_test.go | 188 ++++++++++++++++++++++++++++++++- 1 file changed, 184 insertions(+), 4 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index c55c055318..a1a25de50f 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -1424,13 +1424,193 @@ func TestOrderFulfillment_SplitOrder(t *testing.T) { } func TestSumAssetsAndPrice(t *testing.T) { - // TODO[1658]: func TestSumAssetsAndPrice(t *testing.T) - t.Skipf("not written") + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + askOrder := func(orderID uint64, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Assets: assets, + Price: price, + }) + } + bidOrder := func(orderID uint64, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Assets: assets, + Price: price, + }) + } + + tests := []struct { + name string + orders []*Order + expAssets sdk.Coins + expPrice sdk.Coins + expPanic string + }{ + { + name: "nil orders", + orders: nil, + expAssets: nil, + expPrice: nil, + }, + { + name: "empty orders", + orders: []*Order{}, + expAssets: nil, + expPrice: nil, + }, + { + name: "nil inside order", + orders: []*Order{ + askOrder(1, coin(2, "apple"), coin(3, "plum")), + NewOrder(4), + askOrder(5, coin(6, "apple"), coin(7, "plum")), + }, + expPanic: "order 4 has unknown sub-order type : does not implement SubOrderI", + }, + { + name: "unknown inside order", + orders: []*Order{ + askOrder(1, coin(2, "apple"), coin(3, "plum")), + newUnknownOrder(4), + askOrder(5, coin(6, "apple"), coin(7, "plum")), + }, + expPanic: "order 4 has unknown sub-order type *exchange.unknownOrderType: does not implement SubOrderI", + }, + { + name: "one order, ask", + orders: []*Order{askOrder(1, coin(2, "apple"), coin(3, "plum"))}, + expAssets: sdk.NewCoins(coin(2, "apple")), + expPrice: sdk.NewCoins(coin(3, "plum")), + }, + { + name: "one order, bid", + orders: []*Order{bidOrder(1, coin(2, "apple"), coin(3, "plum"))}, + expAssets: sdk.NewCoins(coin(2, "apple")), + expPrice: sdk.NewCoins(coin(3, "plum")), + }, + { + name: "2 orders, same denoms", + orders: []*Order{ + askOrder(1, coin(2, "apple"), coin(3, "plum")), + bidOrder(4, coin(5, "apple"), coin(6, "plum")), + }, + expAssets: sdk.NewCoins(coin(7, "apple")), + expPrice: sdk.NewCoins(coin(9, "plum")), + }, + { + name: "2 orders, diff denoms", + orders: []*Order{ + bidOrder(1, coin(2, "avocado"), coin(3, "peach")), + askOrder(4, coin(5, "apple"), coin(6, "plum")), + }, + expAssets: sdk.NewCoins(coin(2, "avocado"), coin(5, "apple")), + expPrice: sdk.NewCoins(coin(3, "peach"), coin(6, "plum")), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var assets, price sdk.Coins + testFunc := func() { + assets, price = sumAssetsAndPrice(tc.orders) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "sumAssetsAndPrice") + assert.Equal(t, tc.expAssets.String(), assets.String(), "sumAssetsAndPrice") + assert.Equal(t, tc.expPrice.String(), price.String(), "sumAssetsAndPrice") + }) + } } func TestSumPriceLeft(t *testing.T) { - // TODO[1658]: func TestSumPriceLeft(t *testing.T) - t.Skipf("not written") + tests := []struct { + name string + fulfillments []*OrderFulfillment + expected sdkmath.Int + }{ + { + name: "nil fulfillments", + fulfillments: nil, + expected: sdkmath.NewInt(0), + }, + { + name: "empty fulfillments", + fulfillments: []*OrderFulfillment{}, + expected: sdkmath.NewInt(0), + }, + { + name: "one fulfillment, positive", + fulfillments: []*OrderFulfillment{{PriceLeftAmt: sdkmath.NewInt(8)}}, + expected: sdkmath.NewInt(8), + }, + { + name: "one fulfillment, zero", + fulfillments: []*OrderFulfillment{{PriceLeftAmt: sdkmath.NewInt(0)}}, + expected: sdkmath.NewInt(0), + }, + { + name: "one fulfillment, negative", + fulfillments: []*OrderFulfillment{{PriceLeftAmt: sdkmath.NewInt(-3)}}, + expected: sdkmath.NewInt(-3), + }, + { + name: "three fulfillments", + fulfillments: []*OrderFulfillment{ + {PriceLeftAmt: sdkmath.NewInt(10)}, + {PriceLeftAmt: sdkmath.NewInt(200)}, + {PriceLeftAmt: sdkmath.NewInt(3000)}, + }, + expected: sdkmath.NewInt(3210), + }, + { + name: "three fulfillments, one negative", + fulfillments: []*OrderFulfillment{ + {PriceLeftAmt: sdkmath.NewInt(10)}, + {PriceLeftAmt: sdkmath.NewInt(-200)}, + {PriceLeftAmt: sdkmath.NewInt(3000)}, + }, + expected: sdkmath.NewInt(2810), + }, + { + name: "three fulfillments, all negative", + fulfillments: []*OrderFulfillment{ + {PriceLeftAmt: sdkmath.NewInt(-10)}, + {PriceLeftAmt: sdkmath.NewInt(-200)}, + {PriceLeftAmt: sdkmath.NewInt(-3000)}, + }, + expected: sdkmath.NewInt(-3210), + }, + { + name: "three fulfillments, all large", + fulfillments: []*OrderFulfillment{ + {PriceLeftAmt: newInt(t, "3,000,000,000,000,000,000,000,000,000,000,300")}, + {PriceLeftAmt: newInt(t, "40,000,000,000,000,000,000,000,000,000,000,040")}, + {PriceLeftAmt: newInt(t, "500,000,000,000,000,000,000,000,000,000,000,005")}, + }, + expected: newInt(t, "543,000,000,000,000,000,000,000,000,000,000,345"), + }, + { + name: "four fullfillments, small negative zero large", + fulfillments: []*OrderFulfillment{ + {PriceLeftAmt: sdkmath.NewInt(654_789)}, + {PriceLeftAmt: sdkmath.NewInt(-789)}, + {PriceLeftAmt: sdkmath.NewInt(0)}, + {PriceLeftAmt: newInt(t, "543,000,000,000,000,000,000,000,000,000,000,345")}, + }, + expected: newInt(t, "543,000,000,000,000,000,000,000,000,000,654,345"), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdkmath.Int + testFunc := func() { + actual = sumPriceLeft(tc.fulfillments) + } + require.NotPanics(t, testFunc, "sumPriceLeft") + assert.Equal(t, tc.expected, actual, "sumPriceLeft") + }) + } } func TestBuildSettlement(t *testing.T) { From 12bf03d67c0db8e33c35c9e84e8be0303a40043a Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 5 Oct 2023 14:28:12 -0600 Subject: [PATCH 240/309] [1658]: Unit tests on the DistributeAssets and DistributePrice stuff. --- x/exchange/fulfillment.go | 2 +- x/exchange/fulfillment_test.go | 621 ++++++++++++++++++++++++++++++++- 2 files changed, 614 insertions(+), 9 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index abffa8a093..dbe27e9353 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -239,7 +239,7 @@ func (f *OrderFulfillment) DistributePrice(order OrderI, amount sdkmath.Int) err f.PriceLeftAmt = f.PriceLeftAmt.Sub(amount) f.PriceAppliedAmt = f.PriceAppliedAmt.Add(amount) f.PriceDists = append(f.PriceDists, &Distribution{ - Address: order.GetOrderType(), + Address: order.GetOwner(), Amount: amount, }) return nil diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index a1a25de50f..e39df4eeb4 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -52,6 +52,31 @@ func copyOrderSplits(splits []*OrderSplit) []*OrderSplit { return rv } +// copyDistribution copies a distribution. +func copyDistribution(dist *Distribution) *Distribution { + if dist == nil { + return nil + } + + return &Distribution{ + Address: dist.Address, + Amount: copySDKInt(dist.Amount), + } +} + +// copyDistributions copies a slice of distributions. +func copyDistributions(dists []*Distribution) []*Distribution { + if dists == nil { + return nil + } + + rv := make([]*Distribution, len(dists)) + for i, dist := range dists { + rv[i] = copyDistribution(dist) + } + return rv +} + // copyOrderFulfillment returns a deep copy of an order fulfillement. func copyOrderFulfillment(f *OrderFulfillment) *OrderFulfillment { if f == nil { @@ -61,6 +86,8 @@ func copyOrderFulfillment(f *OrderFulfillment) *OrderFulfillment { return &OrderFulfillment{ Order: copyOrder(f.Order), Splits: copyOrderSplits(f.Splits), + AssetDists: copyDistributions(f.AssetDists), + PriceDists: copyDistributions(f.PriceDists), AssetsFilledAmt: copySDKInt(f.AssetsFilledAmt), AssetsUnfilledAmt: copySDKInt(f.AssetsUnfilledAmt), PriceAppliedAmt: copySDKInt(f.PriceAppliedAmt), @@ -100,6 +127,26 @@ func orderSplitsString(splits []*OrderSplit) string { return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) } +// distributionString is similar to %v except with easier to understand Int entries. +func distributionString(dist *Distribution) string { + if dist == nil { + return "nil" + } + return fmt.Sprintf("{Address:%q, Amount:%s}", dist.Address, dist.Amount) +} + +// distributionsString is similar to %v except with easier to understand Int entries. +func distributionsString(dists []*Distribution) string { + if dists == nil { + return "nil" + } + vals := make([]string, len(dists)) + for i, d := range dists { + vals[i] = distributionString(d) + } + return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) +} + // orderFulfillmentString is similar to %v except with easier to understand Coin and Int entries. func orderFulfillmentString(f *OrderFulfillment) string { if f == nil { @@ -109,6 +156,8 @@ func orderFulfillmentString(f *OrderFulfillment) string { fields := []string{ fmt.Sprintf("Order:%s", orderString(f.Order)), fmt.Sprintf("Splits:%s", orderSplitsString(f.Splits)), + fmt.Sprintf("AssetDists:%s", distributionsString(f.AssetDists)), + fmt.Sprintf("PriceDists:%s", distributionsString(f.PriceDists)), fmt.Sprintf("AssetsFilledAmt:%s", f.AssetsFilledAmt), fmt.Sprintf("AssetsUnfilledAmt:%s", f.AssetsUnfilledAmt), fmt.Sprintf("PriceAppliedAmt:%s", f.PriceAppliedAmt), @@ -146,6 +195,8 @@ func assertEqualOrderFulfillments(t *testing.T, expected, actual *OrderFulfillme // If any of the Ints fail with a complaint about Int.abs = (big.nat) vs {}, use ZeroAmtAfterSub for the expected. assert.Equalf(t, expected.Order, actual.Order, msg("OrderFulfillment.Order"), args...) assert.Equalf(t, expected.Splits, actual.Splits, msg("OrderFulfillment.Splits"), args...) + assert.Equalf(t, expected.AssetDists, actual.AssetDists, msg("OrderFulfillment.AssetDists"), args...) + assert.Equalf(t, expected.PriceDists, actual.PriceDists, msg("OrderFulfillment.PriceDists"), args...) assert.Equalf(t, expected.AssetsFilledAmt, actual.AssetsFilledAmt, msg("OrderFulfillment.AssetsFilledAmt"), args...) assert.Equalf(t, expected.AssetsUnfilledAmt, actual.AssetsUnfilledAmt, msg("OrderFulfillment.AssetsUnfilledAmt"), args...) assert.Equalf(t, expected.PriceAppliedAmt, actual.PriceAppliedAmt, msg("OrderFulfillment.PriceAppliedAmt"), args...) @@ -1399,23 +1450,577 @@ func TestOrderFulfillment_GetHoldAmount(t *testing.T) { } func TestOrderFulfillment_DistributeAssets(t *testing.T) { - // TODO[1658]: func TestOrderFulfillment_DistributeAssets(t *testing.T) - t.Skipf("not written") + newOF := func(order *Order, assetsUnfilled, assetsFilled int64, dists ...*Distribution) *OrderFulfillment { + rv := &OrderFulfillment{ + Order: order, + AssetsUnfilledAmt: sdkmath.NewInt(assetsUnfilled), + AssetsFilledAmt: sdkmath.NewInt(assetsFilled), + } + if assetsUnfilled == 0 { + rv.AssetsUnfilledAmt = ZeroAmtAfterSub + } + if len(dists) > 0 { + rv.AssetDists = dists + } + return rv + + } + askOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*Distribution) *OrderFulfillment { + order := NewOrder(orderID).WithAsk(&AskOrder{Assets: sdk.NewInt64Coin("apple", 999)}) + return newOF(order, assetsUnfilled, assetsFilled, dists...) + } + bidOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*Distribution) *OrderFulfillment { + order := NewOrder(orderID).WithBid(&BidOrder{Assets: sdk.NewInt64Coin("apple", 999)}) + return newOF(order, assetsUnfilled, assetsFilled, dists...) + } + dist := func(addr string, amt int64) *Distribution { + return &Distribution{ + Address: addr, + Amount: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + receiver *OrderFulfillment + order OrderI + amount sdkmath.Int + expRes *OrderFulfillment + expErr string + }{ + { + name: "assets unfilled less than amount: ask, ask", + receiver: askOF(1, 5, 0), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(6), + expErr: "cannot fill ask order 1 having assets left \"5apple\" with \"6apple\" from ask order 2: overfill", + }, + { + name: "assets unfilled less than amount: ask, bid", + receiver: askOF(3, 5, 0), + order: NewOrder(4).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(6), + expErr: "cannot fill ask order 3 having assets left \"5apple\" with \"6apple\" from bid order 4: overfill", + }, + { + name: "assets unfilled less than amount: bid, ask", + receiver: bidOF(5, 5, 0), + order: NewOrder(6).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(6), + expErr: "cannot fill bid order 5 having assets left \"5apple\" with \"6apple\" from ask order 6: overfill", + }, + { + name: "assets unfilled less than amount: bid, bid", + receiver: bidOF(7, 5, 0), + order: NewOrder(8).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(6), + expErr: "cannot fill bid order 7 having assets left \"5apple\" with \"6apple\" from bid order 8: overfill", + }, + { + name: "assets unfilled equals amount: ask, bid", + receiver: askOF(1, 12345, 0), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(12345), + expRes: askOF(1, 0, 12345, dist("buYer", 12345)), + }, + { + name: "assets unfilled equals amount: bid, ask", + receiver: bidOF(1, 12345, 0), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(12345), + expRes: bidOF(1, 0, 12345, dist("seLLer", 12345)), + }, + { + name: "assets unfilled more than amount: ask, bid", + receiver: askOF(1, 12345, 0), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(300), + expRes: askOF(1, 12045, 300, dist("buYer", 300)), + }, + { + name: "assets unfilled more than amount: bid, ask", + receiver: bidOF(1, 12345, 0), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(300), + expRes: bidOF(1, 12045, 300, dist("seLLer", 300)), + }, + { + name: "already has 2 dists: ask, bid", + receiver: askOF(1, 12300, 45, dist("bbbbb", 40), dist("YYYYY", 5)), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(2000), + expRes: askOF(1, 10300, 2045, dist("bbbbb", 40), dist("YYYYY", 5), dist("buYer", 2000)), + }, + { + name: "already has 2 dists: bid, ask", + receiver: bidOF(1, 12300, 45, dist("sssss", 40), dist("LLLLL", 5)), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(2000), + expRes: bidOF(1, 10300, 2045, dist("sssss", 40), dist("LLLLL", 5), dist("seLLer", 2000)), + }, + { + name: "amt more than filled, ask, bid", + receiver: askOF(1, 45, 12300, dist("ssss", 12300)), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(45), + expRes: askOF(1, 0, 12345, dist("ssss", 12300), dist("buYer", 45)), + }, + { + name: "amt more than filled, bid, ask", + receiver: bidOF(1, 45, 12300, dist("ssss", 12300)), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(45), + expRes: bidOF(1, 0, 12345, dist("ssss", 12300), dist("seLLer", 45)), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + orig := copyOrderFulfillment(tc.receiver) + if tc.expRes == nil { + tc.expRes = copyOrderFulfillment(tc.receiver) + } + var err error + testFunc := func() { + err = tc.receiver.DistributeAssets(tc.order, tc.amount) + } + require.NotPanics(t, testFunc, "DistributeAssets") + assertions.AssertErrorValue(t, err, tc.expErr, "DistributeAssets error") + if !assertEqualOrderFulfillments(t, tc.expRes, tc.receiver, "OrderFulfillment after DistributeAssets") { + t.Logf("Original: %s", orderFulfillmentString(orig)) + t.Logf(" Amount: %s", tc.amount) + } + }) + } } func TestDistributeAssets(t *testing.T) { - // TODO[1658]: func TestDistributeAssets(t *testing.T) - t.Skipf("not written") + seller, buyer := "SelleR", "BuyeR" + newOF := func(order *Order, assetsUnfilled, assetsFilled int64, dists ...*Distribution) *OrderFulfillment { + rv := &OrderFulfillment{ + Order: order, + AssetsUnfilledAmt: sdkmath.NewInt(assetsUnfilled), + AssetsFilledAmt: sdkmath.NewInt(assetsFilled), + } + if assetsUnfilled == 0 { + rv.AssetsUnfilledAmt = ZeroAmtAfterSub + } + if len(dists) > 0 { + rv.AssetDists = dists + } + return rv + + } + askOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*Distribution) *OrderFulfillment { + order := NewOrder(orderID).WithAsk(&AskOrder{ + Seller: seller, + Assets: sdk.NewInt64Coin("apple", 999), + }) + return newOF(order, assetsUnfilled, assetsFilled, dists...) + } + bidOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*Distribution) *OrderFulfillment { + order := NewOrder(orderID).WithBid(&BidOrder{ + Buyer: buyer, + Assets: sdk.NewInt64Coin("apple", 999), + }) + return newOF(order, assetsUnfilled, assetsFilled, dists...) + } + dist := func(addr string, amt int64) *Distribution { + return &Distribution{ + Address: addr, + Amount: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + of1 *OrderFulfillment + of2 *OrderFulfillment + amount sdkmath.Int + expOF1 *OrderFulfillment + expOF2 *OrderFulfillment + expErr string + }{ + { + name: "amount more than of1 unfilled: ask bid", + of1: askOF(1, 5, 0), + of2: bidOF(2, 6, 0), + amount: sdkmath.NewInt(6), + expOF2: bidOF(2, 0, 6, dist(seller, 6)), + expErr: "cannot fill ask order 1 having assets left \"5apple\" with \"6apple\" from bid order 2: overfill", + }, + { + name: "amount more than of1 unfilled: bid ask", + of1: bidOF(1, 5, 0), + of2: askOF(2, 6, 0), + amount: sdkmath.NewInt(6), + expOF2: askOF(2, 0, 6, dist(buyer, 6)), + expErr: "cannot fill bid order 1 having assets left \"5apple\" with \"6apple\" from ask order 2: overfill", + }, + { + name: "amount more than of2 unfilled: ask, bid", + of1: askOF(1, 6, 0), + of2: bidOF(2, 5, 0), + amount: sdkmath.NewInt(6), + expOF1: askOF(1, 0, 6, dist(buyer, 6)), + expErr: "cannot fill bid order 2 having assets left \"5apple\" with \"6apple\" from ask order 1: overfill", + }, + { + name: "amount more than of2 unfilled: bid, ask", + of1: bidOF(1, 6, 0), + of2: askOF(2, 5, 0), + amount: sdkmath.NewInt(6), + expOF1: bidOF(1, 0, 6, dist(seller, 6)), + expErr: "cannot fill ask order 2 having assets left \"5apple\" with \"6apple\" from bid order 1: overfill", + }, + { + name: "amount more than both unfilled: ask, bid", + of1: askOF(1, 5, 0), + of2: bidOF(2, 4, 0), + amount: sdkmath.NewInt(6), + expErr: "cannot fill ask order 1 having assets left \"5apple\" with \"6apple\" from bid order 2: overfill" + "\n" + + "cannot fill bid order 2 having assets left \"4apple\" with \"6apple\" from ask order 1: overfill", + }, + { + name: "amount more than both unfilled: ask, bid", + of1: bidOF(1, 5, 0), + of2: askOF(2, 4, 0), + amount: sdkmath.NewInt(6), + expErr: "cannot fill bid order 1 having assets left \"5apple\" with \"6apple\" from ask order 2: overfill" + "\n" + + "cannot fill ask order 2 having assets left \"4apple\" with \"6apple\" from bid order 1: overfill", + }, + { + name: "ask bid", + of1: askOF(1, 10, 55, dist("bbb", 55)), + of2: bidOF(2, 10, 0), + amount: sdkmath.NewInt(9), + expOF1: askOF(1, 1, 64, dist("bbb", 55), dist(buyer, 9)), + expOF2: bidOF(2, 1, 9, dist(seller, 9)), + }, + { + name: "bid ask", + of1: bidOF(1, 10, 55, dist("sss", 55)), + of2: askOF(2, 10, 3, dist("bbb", 3)), + amount: sdkmath.NewInt(10), + expOF1: bidOF(1, 0, 65, dist("sss", 55), dist(seller, 10)), + expOF2: askOF(2, 0, 13, dist("bbb", 3), dist(buyer, 10)), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + origOF1 := copyOrderFulfillment(tc.of1) + origOF2 := copyOrderFulfillment(tc.of2) + if tc.expOF1 == nil { + tc.expOF1 = copyOrderFulfillment(tc.of1) + } + if tc.expOF2 == nil { + tc.expOF2 = copyOrderFulfillment(tc.of2) + } + + var err error + testFunc := func() { + err = DistributeAssets(tc.of1, tc.of2, tc.amount) + } + require.NotPanics(t, testFunc, "DistributeAssets") + assertions.AssertErrorValue(t, err, tc.expErr, "DistributeAssets error") + if !assertEqualOrderFulfillments(t, tc.expOF1, tc.of1, "of1 after DistributeAssets") { + t.Logf("Original: %s", orderFulfillmentString(origOF1)) + t.Logf(" Amount: %s", tc.amount) + } + if !assertEqualOrderFulfillments(t, tc.expOF2, tc.of2, "of2 after DistributeAssets") { + t.Logf("Original: %s", orderFulfillmentString(origOF2)) + t.Logf(" Amount: %s", tc.amount) + } + }) + } } func TestOrderFulfillment_DistributePrice(t *testing.T) { - // TODO[1658]: func TestOrderFulfillment_DistributePrice(t *testing.T) - t.Skipf("not written") + newOF := func(order *Order, priceLeft, priceApplied int64, dists ...*Distribution) *OrderFulfillment { + rv := &OrderFulfillment{ + Order: order, + PriceLeftAmt: sdkmath.NewInt(priceLeft), + PriceAppliedAmt: sdkmath.NewInt(priceApplied), + } + if priceLeft == 0 { + rv.PriceLeftAmt = ZeroAmtAfterSub + } + if len(dists) > 0 { + rv.PriceDists = dists + } + return rv + + } + askOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*Distribution) *OrderFulfillment { + order := NewOrder(orderID).WithAsk(&AskOrder{Price: sdk.NewInt64Coin("peach", 999)}) + return newOF(order, priceLeft, priceApplied, dists...) + } + bidOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*Distribution) *OrderFulfillment { + order := NewOrder(orderID).WithBid(&BidOrder{Price: sdk.NewInt64Coin("peach", 999)}) + return newOF(order, priceLeft, priceApplied, dists...) + } + dist := func(addr string, amt int64) *Distribution { + return &Distribution{ + Address: addr, + Amount: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + receiver *OrderFulfillment + order OrderI + amount sdkmath.Int + expRes *OrderFulfillment + expErr string + }{ + { + name: "assets unfilled less than amount: ask, ask", + receiver: askOF(1, 5, 0), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(6), + expRes: askOF(1, -1, 6, dist("seLLer", 6)), + }, + { + name: "assets unfilled less than amount: ask, bid", + receiver: askOF(3, 5, 0), + order: NewOrder(4).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(6), + expRes: askOF(3, -1, 6, dist("buYer", 6)), + }, + { + name: "assets unfilled less than amount: bid, ask", + receiver: bidOF(5, 5, 0), + order: NewOrder(6).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(6), + expErr: "cannot fill bid order 5 having price left \"5peach\" to ask order 6 at a price of \"6peach\": overfill", + }, + { + name: "assets unfilled less than amount: bid, bid", + receiver: bidOF(7, 5, 0), + order: NewOrder(8).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(6), + expErr: "cannot fill bid order 7 having price left \"5peach\" to bid order 8 at a price of \"6peach\": overfill", + }, + { + name: "assets unfilled equals amount: ask, bid", + receiver: askOF(1, 12345, 0), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(12345), + expRes: askOF(1, 0, 12345, dist("buYer", 12345)), + }, + { + name: "assets unfilled equals amount: bid, ask", + receiver: bidOF(1, 12345, 0), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(12345), + expRes: bidOF(1, 0, 12345, dist("seLLer", 12345)), + }, + { + name: "assets unfilled more than amount: ask, bid", + receiver: askOF(1, 12345, 0), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(300), + expRes: askOF(1, 12045, 300, dist("buYer", 300)), + }, + { + name: "assets unfilled more than amount: bid, ask", + receiver: bidOF(1, 12345, 0), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(300), + expRes: bidOF(1, 12045, 300, dist("seLLer", 300)), + }, + { + name: "already has 2 dists: ask, bid", + receiver: askOF(1, 12300, 45, dist("bbbbb", 40), dist("YYYYY", 5)), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(2000), + expRes: askOF(1, 10300, 2045, dist("bbbbb", 40), dist("YYYYY", 5), dist("buYer", 2000)), + }, + { + name: "already has 2 dists: bid, ask", + receiver: bidOF(1, 12300, 45, dist("sssss", 40), dist("LLLLL", 5)), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(2000), + expRes: bidOF(1, 10300, 2045, dist("sssss", 40), dist("LLLLL", 5), dist("seLLer", 2000)), + }, + { + name: "amt more than filled, ask, bid", + receiver: askOF(1, 45, 12300, dist("ssss", 12300)), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(45), + expRes: askOF(1, 0, 12345, dist("ssss", 12300), dist("buYer", 45)), + }, + { + name: "amt more than filled, bid, ask", + receiver: bidOF(1, 45, 12300, dist("ssss", 12300)), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(45), + expRes: bidOF(1, 0, 12345, dist("ssss", 12300), dist("seLLer", 45)), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + orig := copyOrderFulfillment(tc.receiver) + if tc.expRes == nil { + tc.expRes = copyOrderFulfillment(tc.receiver) + } + var err error + testFunc := func() { + err = tc.receiver.DistributePrice(tc.order, tc.amount) + } + require.NotPanics(t, testFunc, "DistributePrice") + assertions.AssertErrorValue(t, err, tc.expErr, "DistributePrice error") + if !assertEqualOrderFulfillments(t, tc.expRes, tc.receiver, "OrderFulfillment after DistributePrice") { + t.Logf("Original: %s", orderFulfillmentString(orig)) + t.Logf(" Amount: %s", tc.amount) + } + }) + } } func TestDistributePrice(t *testing.T) { - // TODO[1658]: func TestDistributePrice(t *testing.T) - t.Skipf("not written") + seller, buyer := "SelleR", "BuyeR" + newOF := func(order *Order, priceLeft, priceApplied int64, dists ...*Distribution) *OrderFulfillment { + rv := &OrderFulfillment{ + Order: order, + PriceLeftAmt: sdkmath.NewInt(priceLeft), + PriceAppliedAmt: sdkmath.NewInt(priceApplied), + } + if priceLeft == 0 { + rv.PriceLeftAmt = ZeroAmtAfterSub + } + if len(dists) > 0 { + rv.PriceDists = dists + } + return rv + + } + askOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*Distribution) *OrderFulfillment { + order := NewOrder(orderID).WithAsk(&AskOrder{ + Seller: seller, + Price: sdk.NewInt64Coin("peach", 999), + }) + return newOF(order, priceLeft, priceApplied, dists...) + } + bidOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*Distribution) *OrderFulfillment { + order := NewOrder(orderID).WithBid(&BidOrder{ + Buyer: buyer, + Price: sdk.NewInt64Coin("peach", 999), + }) + return newOF(order, priceLeft, priceApplied, dists...) + } + dist := func(addr string, amt int64) *Distribution { + return &Distribution{ + Address: addr, + Amount: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + of1 *OrderFulfillment + of2 *OrderFulfillment + amount sdkmath.Int + expOF1 *OrderFulfillment + expOF2 *OrderFulfillment + expErr string + }{ + { + name: "amount more than of1 unfilled: ask bid", + of1: askOF(1, 5, 0), + of2: bidOF(2, 6, 0), + amount: sdkmath.NewInt(6), + expOF1: askOF(1, -1, 6, dist(buyer, 6)), + expOF2: bidOF(2, 0, 6, dist(seller, 6)), + }, + { + name: "amount more than of1 unfilled: bid ask", + of1: bidOF(1, 5, 0), + of2: askOF(2, 6, 0), + amount: sdkmath.NewInt(6), + expOF2: askOF(2, 0, 6, dist(buyer, 6)), + expErr: "cannot fill bid order 1 having price left \"5peach\" to ask order 2 at a price of \"6peach\": overfill", + }, + { + name: "amount more than of2 unfilled: ask, bid", + of1: askOF(1, 6, 0), + of2: bidOF(2, 5, 0), + amount: sdkmath.NewInt(6), + expOF1: askOF(1, 0, 6, dist(buyer, 6)), + expErr: "cannot fill bid order 2 having price left \"5peach\" to ask order 1 at a price of \"6peach\": overfill", + }, + { + name: "amount more than of2 unfilled: bid, ask", + of1: bidOF(1, 6, 0), + of2: askOF(2, 5, 0), + amount: sdkmath.NewInt(6), + expOF1: bidOF(1, 0, 6, dist(seller, 6)), + expOF2: askOF(2, -1, 6, dist(buyer, 6)), + }, + { + name: "amount more than both unfilled: ask, bid", + of1: askOF(1, 5, 0), + of2: bidOF(2, 4, 0), + amount: sdkmath.NewInt(6), + expOF1: askOF(1, -1, 6, dist(buyer, 6)), + expErr: "cannot fill bid order 2 having price left \"4peach\" to ask order 1 at a price of \"6peach\": overfill", + }, + { + name: "amount more than both unfilled: ask, bid", + of1: bidOF(1, 5, 0), + of2: askOF(2, 4, 0), + amount: sdkmath.NewInt(6), + expOF2: askOF(2, -2, 6, dist(buyer, 6)), + expErr: "cannot fill bid order 1 having price left \"5peach\" to ask order 2 at a price of \"6peach\": overfill", + }, + { + name: "ask bid", + of1: askOF(1, 10, 55, dist("bbb", 55)), + of2: bidOF(2, 10, 0), + amount: sdkmath.NewInt(9), + expOF1: askOF(1, 1, 64, dist("bbb", 55), dist(buyer, 9)), + expOF2: bidOF(2, 1, 9, dist(seller, 9)), + }, + { + name: "bid ask", + of1: bidOF(1, 10, 55, dist("sss", 55)), + of2: askOF(2, 10, 3, dist("bbb", 3)), + amount: sdkmath.NewInt(10), + expOF1: bidOF(1, 0, 65, dist("sss", 55), dist(seller, 10)), + expOF2: askOF(2, 0, 13, dist("bbb", 3), dist(buyer, 10)), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + origOF1 := copyOrderFulfillment(tc.of1) + origOF2 := copyOrderFulfillment(tc.of2) + if tc.expOF1 == nil { + tc.expOF1 = copyOrderFulfillment(tc.of1) + } + if tc.expOF2 == nil { + tc.expOF2 = copyOrderFulfillment(tc.of2) + } + + var err error + testFunc := func() { + err = DistributePrice(tc.of1, tc.of2, tc.amount) + } + require.NotPanics(t, testFunc, "DistributePrice") + assertions.AssertErrorValue(t, err, tc.expErr, "DistributePrice error") + if !assertEqualOrderFulfillments(t, tc.expOF1, tc.of1, "of1 after DistributePrice") { + t.Logf("Original: %s", orderFulfillmentString(origOF1)) + t.Logf(" Amount: %s", tc.amount) + } + if !assertEqualOrderFulfillments(t, tc.expOF2, tc.of2, "of2 after DistributePrice") { + t.Logf("Original: %s", orderFulfillmentString(origOF2)) + t.Logf(" Amount: %s", tc.amount) + } + }) + } } func TestOrderFulfillment_SplitOrder(t *testing.T) { From 5ade7eee6adbfbaadc3ad52f097eed18ef8dfa93 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 5 Oct 2023 15:56:49 -0600 Subject: [PATCH 241/309] [1658]: Unit tests on the CopyChange funcs and Order.Split. Also fixed a bit in Order.Split. --- x/exchange/fulfillment.go | 6 +- x/exchange/fulfillment_test.go | 12 +- x/exchange/orders.go | 13 +- x/exchange/orders_test.go | 436 ++++++++++++++++++++++++++++++++- 4 files changed, 452 insertions(+), 15 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index dbe27e9353..8576dc526d 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -258,10 +258,14 @@ func DistributePrice(of1, of2 *OrderFulfillment, amount sdkmath.Int) error { // This order fulfillment is updated to have the filled order. func (f *OrderFulfillment) SplitOrder() (filled *Order, unfilled *Order, err error) { filled, unfilled, err = f.Order.Split(f.AssetsFilledAmt) + if err != nil { + return nil, nil, err + } + f.Order = filled f.AssetsUnfilledAmt = sdkmath.ZeroInt() f.PriceLeftAmt = filled.GetPrice().Amount.Sub(f.PriceAppliedAmt) - return filled, unfilled, err + return filled, unfilled, nil } // sumAssetsAndPrice gets the sum of assets, and the sum of prices of the provided orders. diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index e39df4eeb4..4633af1272 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -548,13 +548,13 @@ func TestOrderFulfillment_AssetCoin(t *testing.T) { name: "nil inside order", receiver: OrderFulfillment{Order: NewOrder(1)}, amt: sdkmath.NewInt(0), - expPanic: "order 1 has unknown sub-order type : does not implement SubOrderI", + expPanic: nilSubTypeErr(1), }, { name: "unknown inside order", receiver: OrderFulfillment{Order: newUnknownOrder(2)}, amt: sdkmath.NewInt(0), - expPanic: "order 2 has unknown sub-order type *exchange.unknownOrderType: does not implement SubOrderI", + expPanic: unknownSubTypeErr(2), }, { name: "ask order", @@ -668,13 +668,13 @@ func TestOrderFulfillment_PriceCoin(t *testing.T) { name: "nil inside order", receiver: OrderFulfillment{Order: NewOrder(1)}, amt: sdkmath.NewInt(0), - expPanic: "order 1 has unknown sub-order type : does not implement SubOrderI", + expPanic: nilSubTypeErr(1), }, { name: "unknown inside order", receiver: OrderFulfillment{Order: newUnknownOrder(2)}, amt: sdkmath.NewInt(0), - expPanic: "order 2 has unknown sub-order type *exchange.unknownOrderType: does not implement SubOrderI", + expPanic: unknownSubTypeErr(2), }, { name: "ask order", @@ -2071,7 +2071,7 @@ func TestSumAssetsAndPrice(t *testing.T) { NewOrder(4), askOrder(5, coin(6, "apple"), coin(7, "plum")), }, - expPanic: "order 4 has unknown sub-order type : does not implement SubOrderI", + expPanic: nilSubTypeErr(4), }, { name: "unknown inside order", @@ -2080,7 +2080,7 @@ func TestSumAssetsAndPrice(t *testing.T) { newUnknownOrder(4), askOrder(5, coin(6, "apple"), coin(7, "plum")), }, - expPanic: "order 4 has unknown sub-order type *exchange.unknownOrderType: does not implement SubOrderI", + expPanic: unknownSubTypeErr(4), }, { name: "one order, ask", diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 2af2d93691..0ff7c2ee18 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -229,16 +229,18 @@ func (o Order) Split(assetsFilledAmt sdkmath.Int) (filled *Order, unfilled *Orde assetsFilled := sdk.Coin{Denom: orderAssets.Denom, Amount: assetsFilledAmt} switch { + case !assetsFilledAmt.IsPositive(): + return nil, nil, fmt.Errorf("cannot split %s order %d having asset %q at %q: amount filled not positive", + o.GetOrderType(), o.OrderId, orderAssets, assetsFilled) case assetsFilledAmt.Equal(orderAssetsAmt): - return &o, nil, nil - case assetsFilledAmt.IsZero(): - return nil, &o, nil - case assetsFilledAmt.IsNegative(): - return nil, nil, fmt.Errorf("cannot split %s order %d having asset %q at %q: amount filled is negative", + return nil, nil, fmt.Errorf("cannot split %s order %d having asset %q at %q: amount filled equals order assets", o.GetOrderType(), o.OrderId, orderAssets, assetsFilled) case assetsFilledAmt.GT(orderAssetsAmt): return nil, nil, fmt.Errorf("cannot split %s order %d having asset %q at %q: overfilled", o.GetOrderType(), o.OrderId, orderAssets, assetsFilled) + case !o.PartialFillAllowed(): + return nil, nil, fmt.Errorf("cannot split %s order %d having assets %q at %q: order does not allow partial fulfillment", + o.GetOrderType(), o.OrderId, orderAssets, assetsFilled) } orderPrice := o.GetPrice() @@ -292,6 +294,7 @@ func (o Order) Split(assetsFilledAmt sdkmath.Int) (filled *Order, unfilled *Orde unfilled = NewOrder(o.OrderId).WithBid(v.BidOrder.CopyChange(assetsUnfilled, priceUnfilled, feesUnfilled)) return filled, unfilled, nil default: + // This is here in case a new order type is added (that implements OrderI), but a case isn't added here. panic(fmt.Errorf("cannot split %s order %d: unknown order type", o.GetOrderType(), o.OrderId)) } } diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 9ccfb7832b..9bd1ea04fa 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -1173,7 +1173,201 @@ func TestOrder_Validate(t *testing.T) { } } -// TODO[1658]: func TestOrder_Split(t *testing.T) +func TestOrder_Split(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + askOrder := func(orderID uint64, assetAmt, priceAmt int64, fees ...sdk.Coin) *Order { + askOrder := &AskOrder{ + MarketId: 55, + Seller: "samuel", + Assets: coin(assetAmt, "apple"), + Price: coin(priceAmt, "peach"), + AllowPartial: true, + } + if len(fees) > 1 { + t.Fatalf("a max of 1 fee can be provided to askOrder, actual: %s", sdk.Coins(fees)) + } + if len(fees) > 0 { + askOrder.SellerSettlementFlatFee = &fees[0] + } + return NewOrder(orderID).WithAsk(askOrder) + } + bidOrder := func(orderID uint64, assetAmt, priceAmt int64, fees ...sdk.Coin) *Order { + bidOrder := &BidOrder{ + MarketId: 55, + Buyer: "brian", + Assets: coin(assetAmt, "apple"), + Price: coin(priceAmt, "peach"), + AllowPartial: true, + } + if len(fees) > 0 { + bidOrder.BuyerSettlementFees = fees + } + return NewOrder(orderID).WithBid(bidOrder) + } + + tests := []struct { + name string + order *Order + assetsFilledAmt sdkmath.Int + expFilled *Order + expUnfilled *Order + expErr string + expPanic string + }{ + { + name: "nil inside order", + order: NewOrder(88), + assetsFilledAmt: sdkmath.NewInt(0), + expPanic: nilSubTypeErr(88), + }, + { + name: "unknown inside order", + order: newUnknownOrder(89), + assetsFilledAmt: sdkmath.NewInt(0), + expPanic: unknownSubTypeErr(89), + }, + { + name: "assets filled is negative: ask", + order: askOrder(3, 10, 100), + assetsFilledAmt: sdkmath.NewInt(-1), + expErr: "cannot split ask order 3 having asset \"10apple\" at \"-1apple\": amount filled not positive", + }, + { + name: "assets filled is negative: bid", + order: bidOrder(4, 10, 100), + assetsFilledAmt: sdkmath.NewInt(-1), + expErr: "cannot split bid order 4 having asset \"10apple\" at \"-1apple\": amount filled not positive", + }, + { + name: "assets filled is zero: ask", + order: askOrder(9, 10, 100), + assetsFilledAmt: sdkmath.NewInt(0), + expErr: "cannot split ask order 9 having asset \"10apple\" at \"0apple\": amount filled not positive", + }, + { + name: "assets filled is zero: bid", + order: bidOrder(10, 10, 100), + assetsFilledAmt: sdkmath.NewInt(0), + expErr: "cannot split bid order 10 having asset \"10apple\" at \"0apple\": amount filled not positive", + }, + { + name: "assets filled equals order assets: ask", + order: askOrder(7, 10, 100), + assetsFilledAmt: sdkmath.NewInt(10), + expErr: "cannot split ask order 7 having asset \"10apple\" at \"10apple\": amount filled equals order assets", + }, + { + name: "assets filled equals order assets: bid", + order: bidOrder(8, 10, 100), + assetsFilledAmt: sdkmath.NewInt(10), + expErr: "cannot split bid order 8 having asset \"10apple\" at \"10apple\": amount filled equals order assets", + }, + { + name: "assets filled is more than order assets: ask", + order: askOrder(5, 10, 100), + assetsFilledAmt: sdkmath.NewInt(11), + expErr: "cannot split ask order 5 having asset \"10apple\" at \"11apple\": overfilled", + }, + { + name: "assets filled is more than order assets: bid", + order: bidOrder(6, 10, 100), + assetsFilledAmt: sdkmath.NewInt(11), + expErr: "cannot split bid order 6 having asset \"10apple\" at \"11apple\": overfilled", + }, + { + name: "partial not allowed: ask", + order: NewOrder(1).WithAsk(&AskOrder{AllowPartial: false, Assets: coin(2, "peach")}), + assetsFilledAmt: sdkmath.NewInt(1), + expErr: "cannot split ask order 1 having assets \"2peach\" at \"1peach\": order does not allow partial fulfillment", + }, + { + name: "partial not allowed: bid", + order: NewOrder(2).WithBid(&BidOrder{AllowPartial: false, Assets: coin(2, "peach")}), + assetsFilledAmt: sdkmath.NewInt(1), + expErr: "cannot split bid order 2 having assets \"2peach\" at \"1peach\": order does not allow partial fulfillment", + }, + { + name: "price not divisible: ask", + order: askOrder(11, 70, 501), + assetsFilledAmt: sdkmath.NewInt(7), + expErr: "ask order 11 having assets \"70apple\" cannot be partially filled " + + "by \"7apple\": price \"501peach\" is not evenly divisible", + }, + { + name: "price not divisible: bid", + order: bidOrder(12, 70, 501), + assetsFilledAmt: sdkmath.NewInt(7), + expErr: "bid order 12 having assets \"70apple\" cannot be partially filled " + + "by \"7apple\": price \"501peach\" is not evenly divisible", + }, + { + name: "fee not divisible: ask", + order: askOrder(13, 70, 500, coin(23, "fig")), + assetsFilledAmt: sdkmath.NewInt(7), + expErr: "ask order 13 having assets \"70apple\" cannot be partially filled " + + "by \"7apple\": fee \"23fig\" is not evenly divisible", + }, + { + name: "fees not divisible: bid", + order: bidOrder(14, 70, 500, coin(20, "fig"), coin(34, "grape")), + assetsFilledAmt: sdkmath.NewInt(7), + expErr: "bid order 14 having assets \"70apple\" cannot be partially filled " + + "by \"7apple\": fee \"34grape\" is not evenly divisible", + }, + { + name: "no fees: ask", + order: askOrder(21, 70, 500), + assetsFilledAmt: sdkmath.NewInt(7), + expFilled: askOrder(21, 7, 50), + expUnfilled: askOrder(21, 63, 450), + }, + { + name: "no fees: bid", + order: bidOrder(22, 70, 500), + assetsFilledAmt: sdkmath.NewInt(7), + expFilled: bidOrder(22, 7, 50), + expUnfilled: bidOrder(22, 63, 450), + }, + { + name: "with fees: ask", + order: askOrder(23, 10, 500, coin(5, "fig")), + assetsFilledAmt: sdkmath.NewInt(8), + expFilled: askOrder(23, 8, 400, coin(4, "fig")), + expUnfilled: askOrder(23, 2, 100, coin(1, "fig")), + }, + { + name: "with fees: bid", + order: bidOrder(24, 10, 500, coin(5, "fig"), coin(15, "grape")), + assetsFilledAmt: sdkmath.NewInt(8), + expFilled: bidOrder(24, 8, 400, coin(4, "fig"), coin(12, "grape")), + expUnfilled: bidOrder(24, 2, 100, coin(1, "fig"), coin(3, "grape")), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var filled, unfilled *Order + var err error + testFunc := func() { + filled, unfilled, err = tc.order.Split(tc.assetsFilledAmt) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "Split(%s)", tc.assetsFilledAmt) + assertions.AssertErrorValue(t, err, tc.expErr, "Split(%s) error", tc.assetsFilledAmt) + assert.Equal(t, tc.expFilled, filled, "Split(%s) filled order", tc.assetsFilledAmt) + assert.Equal(t, tc.expUnfilled, unfilled, "Split(%s) unfilled order", tc.assetsFilledAmt) + // If the expected filled isn't null, but unfilled is, make sure that the original was returned. + if tc.expFilled != nil && tc.expUnfilled == nil { + assert.Same(t, tc.order, filled, "Split(%s) filled order address", tc.assetsFilledAmt) + } + // If the expected unfilled isn't null, but filled is, make sure that the original was returned. + if tc.expUnfilled != nil && tc.expFilled == nil { + assert.Same(t, tc.order, unfilled, "Split(%s) unfilled order address", tc.assetsFilledAmt) + } + }) + } +} func TestAskOrder_GetMarketID(t *testing.T) { tests := []struct { @@ -1632,7 +1826,127 @@ func TestAskOrder_Validate(t *testing.T) { } } -// TODO[1658]: func TestAskOrder_CopyChange(t *testing.T) +func TestAskOrder_CopyChange(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + coinP := func(amount int64, denom string) *sdk.Coin { + rv := coin(amount, denom) + return &rv + } + + tests := []struct { + name string + order AskOrder + newAssets sdk.Coin + newPrice sdk.Coin + newFee *sdk.Coin + expected *AskOrder + }{ + { + name: "new assets", + order: AskOrder{ + MarketId: 3, + Seller: "sseelleerr", + Assets: coin(8, "apple"), + Price: coin(55, "peach"), + SellerSettlementFlatFee: coinP(12, "fig"), + AllowPartial: true, + }, + newAssets: coin(14, "avocado"), + newPrice: coin(55, "peach"), + newFee: coinP(12, "fig"), + expected: &AskOrder{ + MarketId: 3, + Seller: "sseelleerr", + Assets: coin(14, "avocado"), + Price: coin(55, "peach"), + SellerSettlementFlatFee: coinP(12, "fig"), + AllowPartial: true, + }, + }, + { + name: "new price", + order: AskOrder{ + MarketId: 99, + Seller: "sseeLLeerr", + Assets: coin(8, "apple"), + Price: coin(55, "peach"), + SellerSettlementFlatFee: coinP(12, "fig"), + AllowPartial: false, + }, + newAssets: coin(8, "apple"), + newPrice: coin(38, "plum"), + newFee: coinP(12, "fig"), + expected: &AskOrder{ + MarketId: 99, + Seller: "sseeLLeerr", + Assets: coin(8, "apple"), + Price: coin(38, "plum"), + SellerSettlementFlatFee: coinP(12, "fig"), + AllowPartial: false, + }, + }, + { + name: "new fees", + order: AskOrder{ + MarketId: 33, + Seller: "SseelleerR", + Assets: coin(8, "apple"), + Price: coin(55, "peach"), + SellerSettlementFlatFee: coinP(12, "fig"), + AllowPartial: true, + }, + newAssets: coin(8, "apple"), + newPrice: coin(55, "peach"), + newFee: coinP(88, "grape"), + expected: &AskOrder{ + MarketId: 33, + Seller: "SseelleerR", + Assets: coin(8, "apple"), + Price: coin(55, "peach"), + SellerSettlementFlatFee: coinP(88, "grape"), + AllowPartial: true, + }, + }, + { + name: "new everything", + order: AskOrder{ + MarketId: 34, + Seller: "SSEELLEERR", + Assets: coin(8, "apple"), + Price: coin(55, "peach"), + SellerSettlementFlatFee: coinP(12, "fig"), + AllowPartial: false, + }, + newAssets: coin(14, "avocado"), + newPrice: coin(38, "plum"), + newFee: coinP(88, "grape"), + expected: &AskOrder{ + MarketId: 34, + Seller: "SSEELLEERR", + Assets: coin(14, "avocado"), + Price: coin(38, "plum"), + SellerSettlementFlatFee: coinP(88, "grape"), + AllowPartial: false, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual *AskOrder + testFunc := func() { + actual = tc.order.CopyChange(tc.newAssets, tc.newPrice, tc.newFee) + } + require.NotPanics(t, testFunc, "CopyChange") + if !assert.Equal(t, tc.expected, actual, "CopyChange result") { + t.Logf(" Actual: %s", askOrderString(actual)) + t.Logf("Expected: %s", askOrderString(tc.expected)) + } + }) + } +} func TestBidOrder_GetMarketID(t *testing.T) { tests := []struct { @@ -2108,4 +2422,120 @@ func TestBidOrder_Validate(t *testing.T) { } } -// TODO[1658]: func TestBidOrder_CopyChange(t *testing.T) +func TestBidOrder_CopyChange(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + order BidOrder + newAssets sdk.Coin + newPrice sdk.Coin + newFees sdk.Coins + expected *BidOrder + }{ + { + name: "new assets", + order: BidOrder{ + MarketId: 3, + Buyer: "bbuuyyeerr", + Assets: coin(8, "apple"), + Price: coin(55, "peach"), + BuyerSettlementFees: sdk.Coins{coin(12, "fig")}, + AllowPartial: true, + }, + newAssets: coin(14, "avocado"), + newPrice: coin(55, "peach"), + newFees: sdk.Coins{coin(12, "fig")}, + expected: &BidOrder{ + MarketId: 3, + Buyer: "bbuuyyeerr", + Assets: coin(14, "avocado"), + Price: coin(55, "peach"), + BuyerSettlementFees: sdk.Coins{coin(12, "fig")}, + AllowPartial: true, + }, + }, + { + name: "new price", + order: BidOrder{ + MarketId: 99, + Buyer: "bbuuyyeerr", + Assets: coin(8, "apple"), + Price: coin(55, "peach"), + BuyerSettlementFees: sdk.Coins{coin(12, "fig")}, + AllowPartial: false, + }, + newAssets: coin(8, "apple"), + newPrice: coin(38, "plum"), + newFees: sdk.Coins{coin(12, "fig")}, + expected: &BidOrder{ + MarketId: 99, + Buyer: "bbuuyyeerr", + Assets: coin(8, "apple"), + Price: coin(38, "plum"), + BuyerSettlementFees: sdk.Coins{coin(12, "fig")}, + AllowPartial: false, + }, + }, + { + name: "new fees", + order: BidOrder{ + MarketId: 33, + Buyer: "bbuuyyeerr", + Assets: coin(8, "apple"), + Price: coin(55, "peach"), + BuyerSettlementFees: sdk.Coins{coin(12, "fig")}, + AllowPartial: true, + }, + newAssets: coin(8, "apple"), + newPrice: coin(55, "peach"), + newFees: sdk.Coins{coin(88, "grape")}, + expected: &BidOrder{ + MarketId: 33, + Buyer: "bbuuyyeerr", + Assets: coin(8, "apple"), + Price: coin(55, "peach"), + BuyerSettlementFees: sdk.Coins{coin(88, "grape")}, + AllowPartial: true, + }, + }, + { + name: "new everything", + order: BidOrder{ + MarketId: 34, + Buyer: "BBUUYYEERR", + Assets: coin(8, "apple"), + Price: coin(55, "peach"), + BuyerSettlementFees: sdk.Coins{coin(12, "fig")}, + AllowPartial: false, + }, + newAssets: coin(14, "avocado"), + newPrice: coin(38, "plum"), + newFees: sdk.Coins{coin(88, "grape"), coin(123, "honeydew")}, + expected: &BidOrder{ + MarketId: 34, + Buyer: "BBUUYYEERR", + Assets: coin(14, "avocado"), + Price: coin(38, "plum"), + BuyerSettlementFees: sdk.Coins{coin(88, "grape"), coin(123, "honeydew")}, + AllowPartial: false, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual *BidOrder + testFunc := func() { + actual = tc.order.CopyChange(tc.newAssets, tc.newPrice, tc.newFees) + } + require.NotPanics(t, testFunc, "CopyChange") + if !assert.Equal(t, tc.expected, actual, "CopyChange result") { + t.Logf(" Actual: %s", bidOrderString(actual)) + t.Logf("Expected: %s", bidOrderString(tc.expected)) + } + }) + } +} From 453c417940911b66d9330538ea7d2206719f4701 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 5 Oct 2023 16:12:07 -0600 Subject: [PATCH 242/309] [1658]: Unit tests on OrderFulfillment.SplitOrder --- x/exchange/fulfillment_test.go | 120 ++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 2 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 4633af1272..b0deedd80b 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -2024,8 +2024,124 @@ func TestDistributePrice(t *testing.T) { } func TestOrderFulfillment_SplitOrder(t *testing.T) { - // TODO[1658]: func TestOrderFulfillment_SplitOrder(t *testing.T) - t.Skipf("not written") + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + askOrder := func(orderID uint64, assetAmt, priceAmt int64, fees ...sdk.Coin) *Order { + askOrder := &AskOrder{ + MarketId: 55, + Seller: "samuel", + Assets: coin(assetAmt, "apple"), + Price: coin(priceAmt, "peach"), + AllowPartial: true, + } + if len(fees) > 1 { + t.Fatalf("a max of 1 fee can be provided to askOrder, actual: %s", sdk.Coins(fees)) + } + if len(fees) > 0 { + askOrder.SellerSettlementFlatFee = &fees[0] + } + return NewOrder(orderID).WithAsk(askOrder) + } + bidOrder := func(orderID uint64, assetAmt, priceAmt int64, fees ...sdk.Coin) *Order { + bidOrder := &BidOrder{ + MarketId: 55, + Buyer: "brian", + Assets: coin(assetAmt, "apple"), + Price: coin(priceAmt, "peach"), + AllowPartial: true, + } + if len(fees) > 0 { + bidOrder.BuyerSettlementFees = fees + } + return NewOrder(orderID).WithBid(bidOrder) + } + + tests := []struct { + name string + receiver *OrderFulfillment + expFilled *Order + expUnfilled *Order + expReceiver *OrderFulfillment + expErr string + }{ + { + name: "order split error: ask", + receiver: &OrderFulfillment{ + Order: askOrder(8, 10, 100), + AssetsFilledAmt: sdkmath.NewInt(-1), + }, + expErr: "cannot split ask order 8 having asset \"10apple\" at \"-1apple\": amount filled not positive", + }, + { + name: "order split error: bid", + receiver: &OrderFulfillment{ + Order: bidOrder(9, 10, 100), + AssetsFilledAmt: sdkmath.NewInt(-1), + }, + expErr: "cannot split bid order 9 having asset \"10apple\" at \"-1apple\": amount filled not positive", + }, + { + name: "okay: ask", + receiver: &OrderFulfillment{ + Order: askOrder(17, 10, 100, coin(20, "fig")), + AssetsFilledAmt: sdkmath.NewInt(9), + AssetsUnfilledAmt: sdkmath.NewInt(1), + PriceAppliedAmt: sdkmath.NewInt(300), + PriceLeftAmt: sdkmath.NewInt(-200), + }, + expFilled: askOrder(17, 9, 90, coin(18, "fig")), + expUnfilled: askOrder(17, 1, 10, coin(2, "fig")), + expReceiver: &OrderFulfillment{ + Order: askOrder(17, 9, 90, coin(18, "fig")), + AssetsFilledAmt: sdkmath.NewInt(9), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(300), + PriceLeftAmt: sdkmath.NewInt(-210), + }, + }, + { + name: "okay: bid", + receiver: &OrderFulfillment{ + Order: bidOrder(19, 10, 100, coin(20, "fig")), + AssetsFilledAmt: sdkmath.NewInt(9), + AssetsUnfilledAmt: sdkmath.NewInt(1), + PriceAppliedAmt: sdkmath.NewInt(300), + PriceLeftAmt: sdkmath.NewInt(-200), + }, + expFilled: bidOrder(19, 9, 90, coin(18, "fig")), + expUnfilled: bidOrder(19, 1, 10, coin(2, "fig")), + expReceiver: &OrderFulfillment{ + Order: bidOrder(19, 9, 90, coin(18, "fig")), + AssetsFilledAmt: sdkmath.NewInt(9), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(300), + PriceLeftAmt: sdkmath.NewInt(-210), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + orig := copyOrderFulfillment(tc.receiver) + if tc.expReceiver == nil { + tc.expReceiver = copyOrderFulfillment(tc.receiver) + } + + var filled, unfilled *Order + var err error + testFunc := func() { + filled, unfilled, err = tc.receiver.SplitOrder() + } + require.NotPanics(t, testFunc, "SplitOrder") + assertions.AssertErrorValue(t, err, tc.expErr, "SplitOrder error") + assert.Equalf(t, tc.expFilled, filled, "SplitOrder filled order") + assert.Equalf(t, tc.expUnfilled, unfilled, "SplitOrder unfilled order") + if !assertEqualOrderFulfillments(t, tc.expReceiver, tc.receiver, "OrderFulfillment after SplitOrder") { + t.Logf("Original: %s", orderFulfillmentString(orig)) + } + }) + } } func TestSumAssetsAndPrice(t *testing.T) { From 69efe084751f2128c4107faf2239fd36357d6a16 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 5 Oct 2023 17:46:50 -0600 Subject: [PATCH 243/309] [1658]: Unit tests and fixes for OrderFulfillment.Validate, getAssetTransfer and getPriceTransfer. --- x/exchange/fulfillment.go | 53 +-- x/exchange/fulfillment_test.go | 570 ++++++++++++++++++++++++++++++++- 2 files changed, 598 insertions(+), 25 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 8576dc526d..07df517d60 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -696,9 +696,10 @@ func buildTransfers(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) // getAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. func getAssetTransfer(f *OrderFulfillment) (*Transfer, error) { - if !f.AssetsFilledAmt.IsPositive() { - return nil, fmt.Errorf("%s order %d cannot be filled with %s assets: amount not positive", - f.GetOrderType(), f.GetOrderID(), f.GetAssetsFilled()) + assetsFilled := f.GetAssetsFilled() + if !assetsFilled.Amount.IsPositive() { + return nil, fmt.Errorf("%s order %d cannot be filled with %q assets: amount not positive", + f.GetOrderType(), f.GetOrderID(), assetsFilled) } indexedDists := newIndexedAddrAmts() @@ -712,32 +713,34 @@ func getAssetTransfer(f *OrderFulfillment) (*Transfer, error) { sumDists = sumDists.Add(dist.Amount) } - if !sumDists.Equal(f.AssetsFilledAmt) { + if !sumDists.Equal(assetsFilled.Amount) { return nil, fmt.Errorf("%s order %d assets filled %q does not equal assets distributed %q", - f.GetOrderType(), f.GetOrderID(), f.GetAssetsFilled(), f.assetCoin(sumDists)) + f.GetOrderType(), f.GetOrderID(), assetsFilled, f.assetCoin(sumDists)) } if f.IsAskOrder() { return &Transfer{ - Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, + Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(assetsFilled)}}, Outputs: indexedDists.getAsOutputs(), }, nil } if f.IsBidOrder() { return &Transfer{ Inputs: indexedDists.getAsInputs(), - Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, + Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(assetsFilled)}}, }, nil } - return nil, fmt.Errorf("unknown order type %T", f.Order.GetOrder()) + // This is here in case a new SubTypeI is made that isn't accounted for in here. + panic(fmt.Errorf("%s order %d: unknown order type", f.GetOrderType(), f.GetOrderID())) } // getPriceTransfer gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. func getPriceTransfer(f *OrderFulfillment) (*Transfer, error) { - if !f.PriceAppliedAmt.IsPositive() { + priceApplied := f.GetPriceApplied() + if !priceApplied.Amount.IsPositive() { return nil, fmt.Errorf("%s order %d cannot be filled at price %q: amount not positive", - f.GetOrderType(), f.GetOrderID(), f.GetPriceApplied()) + f.GetOrderType(), f.GetOrderID(), priceApplied) } indexedDists := newIndexedAddrAmts() @@ -751,25 +754,26 @@ func getPriceTransfer(f *OrderFulfillment) (*Transfer, error) { sumDists = sumDists.Add(dist.Amount) } - if !sumDists.Equal(f.PriceAppliedAmt) { + if !sumDists.Equal(priceApplied.Amount) { return nil, fmt.Errorf("%s order %d price filled %q does not equal price distributed %q", - f.GetOrderType(), f.GetOrderID(), f.GetPriceApplied(), f.priceCoin(sumDists)) + f.GetOrderType(), f.GetOrderID(), priceApplied, f.priceCoin(sumDists)) } if f.IsAskOrder() { return &Transfer{ Inputs: indexedDists.getAsInputs(), - Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceApplied())}}, + Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(priceApplied)}}, }, nil } if f.IsBidOrder() { return &Transfer{ - Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceApplied())}}, + Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(priceApplied)}}, Outputs: indexedDists.getAsOutputs(), }, nil } - return nil, fmt.Errorf("unknown order type %T", f.Order.GetOrder()) + // This is here in case a new SubTypeI is made that isn't accounted for in here. + panic(fmt.Errorf("%s order %d: unknown order type", f.GetOrderType(), f.GetOrderID())) } // Apply adjusts this order fulfillment using the provided info. @@ -952,12 +956,22 @@ func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) (err error) { } // Validate makes sure the assets filled and price applied are acceptable for this fulfillment. -func (f OrderFulfillment) Validate() error { +func (f OrderFulfillment) Validate() (err error) { + defer func() { + if r := recover(); r != nil { + if e, ok := r.(error); ok { + err = e + } else { + err = fmt.Errorf("%v", r) + } + } + }() + orderPrice := f.GetPrice() switch { case f.IsAskOrder(): if orderPrice.Amount.GT(f.PriceAppliedAmt) { - return fmt.Errorf("%s order %d price %q is less than price filled %q", + return fmt.Errorf("%s order %d price %q is more than price filled %q", f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceApplied()) } case f.IsBidOrder(): @@ -966,11 +980,12 @@ func (f OrderFulfillment) Validate() error { f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceApplied()) } default: - return fmt.Errorf("%s order %d: unknown order type", f.GetOrderType(), f.GetOrderID()) + // This is here in case something new implements SubOrderI but a case isn't added here. + panic(fmt.Errorf("%s order %d: unknown order type", f.GetOrderType(), f.GetOrderID())) } orderAssets := f.GetAssets() - if orderAssets.Amount != f.AssetsFilledAmt { + if !orderAssets.Amount.Equal(f.AssetsFilledAmt) { return fmt.Errorf("%s order %d assets %q does not equal filled assets %q", f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled()) } diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index b0deedd80b..ff18436d5c 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -2375,13 +2375,421 @@ func TestBuildTransfers(t *testing.T) { } func TestGetAssetTransfer(t *testing.T) { - // TODO[1658]: func TestGetAssetTransfer(t *testing.T) - t.Skipf("not written") + seller, buyer := "sally", "brandon" + assetDenom, priceDenom := "apple", "peach" + newOF := func(order *Order, assetsFilledAmt int64, assetDists ...*Distribution) *OrderFulfillment { + rv := &OrderFulfillment{ + Order: order, + AssetsFilledAmt: sdkmath.NewInt(assetsFilledAmt), + } + if len(assetDists) > 0 { + rv.AssetDists = assetDists + } + return rv + } + askOrder := func(orderID uint64) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + MarketId: 5555, + Seller: seller, + Assets: sdk.Coin{Denom: assetDenom, Amount: sdkmath.NewInt(111)}, + Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(999)}, + }) + } + bidOrder := func(orderID uint64) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + MarketId: 5555, + Buyer: buyer, + Assets: sdk.Coin{Denom: assetDenom, Amount: sdkmath.NewInt(111)}, + Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(999)}, + }) + } + dist := func(addr string, amount int64) *Distribution { + return &Distribution{Address: addr, Amount: sdkmath.NewInt(amount)} + } + input := func(addr string, amount int64) banktypes.Input { + return banktypes.Input{ + Address: addr, + Coins: sdk.Coins{{Denom: assetDenom, Amount: sdkmath.NewInt(amount)}}, + } + } + output := func(addr string, amount int64) banktypes.Output { + return banktypes.Output{ + Address: addr, + Coins: sdk.Coins{{Denom: assetDenom, Amount: sdkmath.NewInt(amount)}}, + } + } + + tests := []struct { + name string + f *OrderFulfillment + expTransfer *Transfer + expErr string + expPanic string + }{ + { + name: "nil inside order", + f: newOF(NewOrder(975), 5, dist("five", 5)), + expPanic: nilSubTypeErr(975), + }, + { + name: "unknown inside order", + f: newOF(newUnknownOrder(974), 5, dist("five", 5)), + expPanic: unknownSubTypeErr(974), + }, + { + name: "assets filled negative: ask", + f: newOF(askOrder(159), -5), + expErr: "ask order 159 cannot be filled with \"-5apple\" assets: amount not positive", + }, + { + name: "assets filled negative: bid", + f: newOF(bidOrder(953), -5), + expErr: "bid order 953 cannot be filled with \"-5apple\" assets: amount not positive", + }, + { + name: "assets filled zero: ask", + f: newOF(askOrder(991), 0), + expErr: "ask order 991 cannot be filled with \"0apple\" assets: amount not positive", + }, + { + name: "assets filled zero: bid", + f: newOF(bidOrder(992), 0), + expErr: "bid order 992 cannot be filled with \"0apple\" assets: amount not positive", + }, + { + name: "asset dists has negative amount: ask", + f: newOF(askOrder(549), 10, dist("one", 1), dist("two", 2), dist("neg", -2), dist("nine", 9)), + expErr: "ask order 549 cannot have \"-2apple\" assets in a transfer: amount not positive", + }, + { + name: "asset dists has negative amount: bid", + f: newOF(bidOrder(545), 10, dist("one", 1), dist("two", 2), dist("neg", -2), dist("nine", 9)), + expErr: "bid order 545 cannot have \"-2apple\" assets in a transfer: amount not positive", + }, + { + name: "asset dists has zero: ask", + f: newOF(askOrder(683), 10, dist("one", 1), dist("two", 2), dist("zero", 0), dist("seven", 7)), + expErr: "ask order 683 cannot have \"0apple\" assets in a transfer: amount not positive", + }, + { + name: "asset dists has zero: bid", + f: newOF(bidOrder(777), 10, dist("one", 1), dist("two", 2), dist("zero", 0), dist("seven", 7)), + expErr: "bid order 777 cannot have \"0apple\" assets in a transfer: amount not positive", + }, + { + name: "asset dists sum less than assets filled: ask", + f: newOF(askOrder(8), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("three2", 3)), + expErr: "ask order 8 assets filled \"10apple\" does not equal assets distributed \"9apple\"", + }, + { + name: "asset dists sum less than assets filled: bid", + f: newOF(bidOrder(3), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("three2", 3)), + expErr: "bid order 3 assets filled \"10apple\" does not equal assets distributed \"9apple\"", + }, + { + name: "asset dists sum more than assets filled: ask", + f: newOF(askOrder(8), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("five", 5)), + expErr: "ask order 8 assets filled \"10apple\" does not equal assets distributed \"11apple\"", + }, + { + name: "asset dists sum more than assets filled: bid", + f: newOF(bidOrder(3), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("five", 5)), + expErr: "bid order 3 assets filled \"10apple\" does not equal assets distributed \"11apple\"", + }, + { + name: "one dist: ask", + f: newOF(askOrder(12), 10, dist("ten", 10)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(seller, 10)}, + Outputs: []banktypes.Output{output("ten", 10)}, + }, + }, + { + name: "one dist: bid", + f: newOF(bidOrder(13), 10, dist("ten", 10)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("ten", 10)}, + Outputs: []banktypes.Output{output(buyer, 10)}, + }, + }, + { + name: "two dists, different addresses: ask", + f: newOF(askOrder(2111), 20, dist("eleven", 11), dist("nine", 9)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(seller, 20)}, + Outputs: []banktypes.Output{output("eleven", 11), output("nine", 9)}, + }, + }, + { + name: "two dists, different addresses: bid", + f: newOF(bidOrder(1222), 20, dist("eleven", 11), dist("nine", 9)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("eleven", 11), input("nine", 9)}, + Outputs: []banktypes.Output{output(buyer, 20)}, + }, + }, + { + name: "two dists, same addresses: ask", + f: newOF(askOrder(5353), 52, dist("billy", 48), dist("billy", 4)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(seller, 52)}, + Outputs: []banktypes.Output{output("billy", 52)}, + }, + }, + { + name: "two dists, same addresses: bid", + f: newOF(bidOrder(3535), 52, dist("sol", 48), dist("sol", 4)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("sol", 52)}, + Outputs: []banktypes.Output{output(buyer, 52)}, + }, + }, + { + name: "four dists: ask", + f: newOF(askOrder(99221), 33, + dist("buddy", 10), dist("brian", 13), dist("buddy", 8), dist("bella", 2)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(seller, 33)}, + Outputs: []banktypes.Output{output("buddy", 18), output("brian", 13), output("bella", 2)}, + }, + }, + { + name: "four dists: bid", + f: newOF(bidOrder(99221), 33, + dist("sydney", 10), dist("sarah", 2), dist("sydney", 8), dist("spencer", 13)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("sydney", 18), input("sarah", 2), input("spencer", 13)}, + Outputs: []banktypes.Output{output(buyer, 33)}, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + orig := copyOrderFulfillment(tc.f) + var transfer *Transfer + var err error + testFunc := func() { + transfer, err = getAssetTransfer(tc.f) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAssetTransfer") + assertions.AssertErrorValue(t, err, tc.expErr, "getAssetTransfer error") + if !assert.Equal(t, tc.expTransfer, transfer, "getAssetTransfer transfers") { + t.Logf(" Actual: %s", transferString(transfer)) + t.Logf("Expected: %s", transferString(tc.expTransfer)) + } + assertEqualOrderFulfillments(t, orig, tc.f, "OrderFulfillment before and after getAssetTransfer") + }) + } } func TestGetPriceTransfer(t *testing.T) { - // TODO[1658]: func TestGetPriceTransfer(t *testing.T) - t.Skipf("not written") + seller, buyer := "sally", "brandon" + assetDenom, priceDenom := "apple", "peach" + newOF := func(order *Order, priceAppliedAmt int64, priceDists ...*Distribution) *OrderFulfillment { + rv := &OrderFulfillment{ + Order: order, + PriceAppliedAmt: sdkmath.NewInt(priceAppliedAmt), + } + if len(priceDists) > 0 { + rv.PriceDists = priceDists + } + return rv + } + askOrder := func(orderID uint64) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + MarketId: 5555, + Seller: seller, + Assets: sdk.Coin{Denom: assetDenom, Amount: sdkmath.NewInt(111)}, + Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(999)}, + }) + } + bidOrder := func(orderID uint64) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + MarketId: 5555, + Buyer: buyer, + Assets: sdk.Coin{Denom: assetDenom, Amount: sdkmath.NewInt(111)}, + Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(999)}, + }) + } + dist := func(addr string, amount int64) *Distribution { + return &Distribution{Address: addr, Amount: sdkmath.NewInt(amount)} + } + input := func(addr string, amount int64) banktypes.Input { + return banktypes.Input{ + Address: addr, + Coins: sdk.Coins{{Denom: priceDenom, Amount: sdkmath.NewInt(amount)}}, + } + } + output := func(addr string, amount int64) banktypes.Output { + return banktypes.Output{ + Address: addr, + Coins: sdk.Coins{{Denom: priceDenom, Amount: sdkmath.NewInt(amount)}}, + } + } + + tests := []struct { + name string + f *OrderFulfillment + expTransfer *Transfer + expErr string + expPanic string + }{ + { + name: "nil inside order", + f: newOF(NewOrder(975), 5, dist("five", 5)), + expPanic: nilSubTypeErr(975), + }, + { + name: "unknown inside order", + f: newOF(newUnknownOrder(974), 5, dist("five", 5)), + expPanic: unknownSubTypeErr(974), + }, + { + name: "price applied negative: ask", + f: newOF(askOrder(159), -5), + expErr: "ask order 159 cannot be filled at price \"-5peach\": amount not positive", + }, + { + name: "price applied negative: bid", + f: newOF(bidOrder(953), -5), + expErr: "bid order 953 cannot be filled at price \"-5peach\": amount not positive", + }, + { + name: "price applied zero: ask", + f: newOF(askOrder(991), 0), + expErr: "ask order 991 cannot be filled at price \"0peach\": amount not positive", + }, + { + name: "price applied zero: bid", + f: newOF(bidOrder(992), 0), + expErr: "bid order 992 cannot be filled at price \"0peach\": amount not positive", + }, + { + name: "price dists has negative amount: ask", + f: newOF(askOrder(549), 10, dist("one", 1), dist("two", 2), dist("neg", -2), dist("nine", 9)), + expErr: "ask order 549 cannot have price \"-2peach\" in a transfer: amount not positive", + }, + { + name: "price dists has negative amount: bid", + f: newOF(bidOrder(545), 10, dist("one", 1), dist("two", 2), dist("neg", -2), dist("nine", 9)), + expErr: "bid order 545 cannot have price \"-2peach\" in a transfer: amount not positive", + }, + { + name: "price dists has zero: ask", + f: newOF(askOrder(683), 10, dist("one", 1), dist("two", 2), dist("zero", 0), dist("seven", 7)), + expErr: "ask order 683 cannot have price \"0peach\" in a transfer: amount not positive", + }, + { + name: "price dists has zero: bid", + f: newOF(bidOrder(777), 10, dist("one", 1), dist("two", 2), dist("zero", 0), dist("seven", 7)), + expErr: "bid order 777 cannot have price \"0peach\" in a transfer: amount not positive", + }, + { + name: "price dists sum less than price applied: ask", + f: newOF(askOrder(8), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("three2", 3)), + expErr: "ask order 8 price filled \"10peach\" does not equal price distributed \"9peach\"", + }, + { + name: "price dists sum less than price applied: bid", + f: newOF(bidOrder(3), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("three2", 3)), + expErr: "bid order 3 price filled \"10peach\" does not equal price distributed \"9peach\"", + }, + { + name: "price dists sum more than price applied: ask", + f: newOF(askOrder(8), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("five", 5)), + expErr: "ask order 8 price filled \"10peach\" does not equal price distributed \"11peach\"", + }, + { + name: "price dists sum more than price applied: bid", + f: newOF(bidOrder(3), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("five", 5)), + expErr: "bid order 3 price filled \"10peach\" does not equal price distributed \"11peach\"", + }, + { + name: "one dist: ask", + f: newOF(askOrder(12), 10, dist("ten", 10)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("ten", 10)}, + Outputs: []banktypes.Output{output(seller, 10)}, + }, + }, + { + name: "one dist: bid", + f: newOF(bidOrder(13), 10, dist("ten", 10)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(buyer, 10)}, + Outputs: []banktypes.Output{output("ten", 10)}, + }, + }, + { + name: "two dists, different addresses: ask", + f: newOF(askOrder(2111), 20, dist("eleven", 11), dist("nine", 9)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("eleven", 11), input("nine", 9)}, + Outputs: []banktypes.Output{output(seller, 20)}, + }, + }, + { + name: "two dists, different addresses: bid", + f: newOF(bidOrder(1222), 20, dist("eleven", 11), dist("nine", 9)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(buyer, 20)}, + Outputs: []banktypes.Output{output("eleven", 11), output("nine", 9)}, + }, + }, + { + name: "two dists, same addresses: ask", + f: newOF(askOrder(5353), 52, dist("billy", 48), dist("billy", 4)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("billy", 52)}, + Outputs: []banktypes.Output{output(seller, 52)}, + }, + }, + { + name: "two dists, same addresses: bid", + f: newOF(bidOrder(3535), 52, dist("sol", 48), dist("sol", 4)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(buyer, 52)}, + Outputs: []banktypes.Output{output("sol", 52)}, + }, + }, + { + name: "four dists: ask", + f: newOF(askOrder(99221), 33, + dist("buddy", 10), dist("brian", 13), dist("buddy", 8), dist("bella", 2)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("buddy", 18), input("brian", 13), input("bella", 2)}, + Outputs: []banktypes.Output{output(seller, 33)}, + }, + }, + { + name: "four dists: bid", + f: newOF(bidOrder(99221), 33, + dist("sydney", 10), dist("sarah", 2), dist("sydney", 8), dist("spencer", 13)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(buyer, 33)}, + Outputs: []banktypes.Output{output("sydney", 18), output("sarah", 2), output("spencer", 13)}, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + orig := copyOrderFulfillment(tc.f) + var transfer *Transfer + var err error + testFunc := func() { + transfer, err = getPriceTransfer(tc.f) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getPriceTransfer") + assertions.AssertErrorValue(t, err, tc.expErr, "getPriceTransfer error") + if !assert.Equal(t, tc.expTransfer, transfer, "getPriceTransfer transfers") { + t.Logf(" Actual: %s", transferString(transfer)) + t.Logf("Expected: %s", transferString(tc.expTransfer)) + } + assertEqualOrderFulfillments(t, orig, tc.f, "OrderFulfillment before and after getPriceTransfer") + }) + } } func TestOrderFulfillment_Apply(t *testing.T) { @@ -3525,8 +3933,158 @@ func TestOrderFulfillment_Finalize(t *testing.T) { } func TestOrderFulfillment_Validate(t *testing.T) { - // TODO[1658]: func TestOrderFulfillment_Validate(t *testing.T) - t.Skipf("not written") + askOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + MarketId: 987, + Seller: "steve", + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, + Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(priceAmt)}, + }) + } + bidOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + MarketId: 987, + Buyer: "bruce", + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, + Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(priceAmt)}, + }) + } + + tests := []struct { + name string + f OrderFulfillment + expErr string + }{ + { + name: "nil inside order", + f: OrderFulfillment{Order: NewOrder(8)}, + expErr: nilSubTypeErr(8), + }, + { + name: "unknown inside order", + f: OrderFulfillment{Order: newUnknownOrder(12)}, + expErr: unknownSubTypeErr(12), + }, + { + name: "order price greater than price applied: ask", + f: OrderFulfillment{ + Order: askOrder(52, 10, 401), + PriceAppliedAmt: sdkmath.NewInt(400), + AssetsFilledAmt: sdkmath.NewInt(10), + }, + expErr: "ask order 52 price \"401peach\" is more than price filled \"400peach\"", + }, + { + name: "order price equal to price applied: ask", + f: OrderFulfillment{ + Order: askOrder(53, 10, 401), + PriceAppliedAmt: sdkmath.NewInt(401), + AssetsFilledAmt: sdkmath.NewInt(10), + }, + expErr: "", + }, + { + name: "order price less than price applied: ask", + f: OrderFulfillment{ + Order: askOrder(54, 10, 401), + PriceAppliedAmt: sdkmath.NewInt(402), + AssetsFilledAmt: sdkmath.NewInt(10), + }, + expErr: "", + }, + { + name: "order price greater than price applied: bid", + f: OrderFulfillment{ + Order: bidOrder(71, 17, 432), + PriceAppliedAmt: sdkmath.NewInt(431), + AssetsFilledAmt: sdkmath.NewInt(17), + }, + expErr: "bid order 71 price \"432peach\" is not equal to price filled \"431peach\"", + }, + { + name: "order price equal to price applied: bid", + f: OrderFulfillment{ + Order: bidOrder(72, 17, 432), + PriceAppliedAmt: sdkmath.NewInt(432), + AssetsFilledAmt: sdkmath.NewInt(17), + }, + expErr: "", + }, + { + name: "order price less than price applied: bid", + f: OrderFulfillment{ + Order: bidOrder(73, 17, 432), + PriceAppliedAmt: sdkmath.NewInt(433), + AssetsFilledAmt: sdkmath.NewInt(17), + }, + expErr: "bid order 73 price \"432peach\" is not equal to price filled \"433peach\"", + }, + { + name: "order assets less than assets filled: ask", + f: OrderFulfillment{ + Order: askOrder(101, 53, 12345), + PriceAppliedAmt: sdkmath.NewInt(12345), + AssetsFilledAmt: sdkmath.NewInt(54), + }, + expErr: "ask order 101 assets \"53apple\" does not equal filled assets \"54apple\"", + }, + { + name: "order assets equal to assets filled: ask", + f: OrderFulfillment{ + Order: askOrder(202, 53, 12345), + PriceAppliedAmt: sdkmath.NewInt(12345), + AssetsFilledAmt: sdkmath.NewInt(53), + }, + expErr: "", + }, + { + name: "order assets more than assets filled: ask", + f: OrderFulfillment{ + Order: askOrder(303, 53, 12345), + PriceAppliedAmt: sdkmath.NewInt(12345), + AssetsFilledAmt: sdkmath.NewInt(52), + }, + expErr: "ask order 303 assets \"53apple\" does not equal filled assets \"52apple\"", + }, + { + name: "order assets less than assets filled: bid", + f: OrderFulfillment{ + Order: bidOrder(404, 53, 12345), + PriceAppliedAmt: sdkmath.NewInt(12345), + AssetsFilledAmt: sdkmath.NewInt(54), + }, + expErr: "bid order 404 assets \"53apple\" does not equal filled assets \"54apple\"", + }, + { + name: "order assets equal to assets filled: bid", + f: OrderFulfillment{ + Order: bidOrder(505, 53, 12345), + PriceAppliedAmt: sdkmath.NewInt(12345), + AssetsFilledAmt: sdkmath.NewInt(53), + }, + expErr: "", + }, + { + name: "order assets more than assets filled: bid", + f: OrderFulfillment{ + Order: bidOrder(606, 53, 12345), + PriceAppliedAmt: sdkmath.NewInt(12345), + AssetsFilledAmt: sdkmath.NewInt(52), + }, + expErr: "bid order 606 assets \"53apple\" does not equal filled assets \"52apple\"", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = tc.f.Validate() + } + require.NotPanics(t, testFunc, "Validate") + assertions.AssertErrorValue(t, err, tc.expErr, "Validate error") + }) + } } func TestOrderFulfillment_Validate2(t *testing.T) { From 4693cef12b66f93024158c9c2f6105b23bcacec7 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 5 Oct 2023 18:17:50 -0600 Subject: [PATCH 244/309] [1658]: Unit tests on validateCanSettle. --- x/exchange/fulfillment.go | 2 +- x/exchange/fulfillment_test.go | 227 ++++++++++++++++++++++++++++++++- x/exchange/market_test.go | 33 +---- 3 files changed, 231 insertions(+), 31 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 07df517d60..0d4eec9493 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -412,7 +412,7 @@ func validateCanSettle(askOrders, bidOrders []*Order) error { totalAskPrice, totalBidPrice)) } - // Note: We don't compare the asset and price amounts because we need to know what's + // Note: We don't compare the total asset and price amounts because we need to know what's // being partially filled before we can make an assertions about the amounts. return errors.Join(errs...) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index ff18436d5c..21ab2baf49 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -2340,8 +2340,231 @@ func TestBuildSettlement(t *testing.T) { } func TestValidateCanSettle(t *testing.T) { - // TODO[1658]: func TestValidateCanSettle(t *testing.T) - t.Skipf("not written") + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + askOrder := func(orderID uint64, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Assets: assets, + Price: price, + }) + } + bidOrder := func(orderID uint64, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Assets: assets, + Price: price, + }) + } + + tests := []struct { + name string + askOrders []*Order + bidOrders []*Order + expErr string + }{ + { + name: "nil ask orders", + askOrders: nil, + bidOrders: []*Order{bidOrder(8, coin(10, "apple"), coin(11, "peach"))}, + expErr: "no ask orders provided", + }, + { + name: "no bid orders", + askOrders: []*Order{askOrder(7, coin(10, "apple"), coin(11, "peach"))}, + bidOrders: nil, + expErr: "no bid orders provided", + }, + { + name: "no orders", + askOrders: nil, + bidOrders: nil, + expErr: joinErrs("no ask orders provided", "no bid orders provided"), + }, + { + name: "bid order in asks", + askOrders: []*Order{ + askOrder(7, coin(10, "apple"), coin(11, "peach")), + bidOrder(8, coin(10, "apple"), coin(11, "peach")), + askOrder(9, coin(10, "apple"), coin(11, "peach")), + }, + bidOrders: []*Order{bidOrder(22, coin(10, "apple"), coin(11, "peach"))}, + expErr: "bid order 8 is not an ask order but is in the askOrders list at index 1", + }, + { + name: "nil inside order in asks", + askOrders: []*Order{ + askOrder(7, coin(10, "apple"), coin(11, "peach")), + NewOrder(8), + askOrder(9, coin(10, "apple"), coin(11, "peach")), + }, + bidOrders: []*Order{bidOrder(22, coin(10, "apple"), coin(11, "peach"))}, + expErr: " order 8 is not an ask order but is in the askOrders list at index 1", + }, + { + name: "unknown inside order in asks", + askOrders: []*Order{ + askOrder(7, coin(10, "apple"), coin(11, "peach")), + newUnknownOrder(8), + askOrder(9, coin(10, "apple"), coin(11, "peach")), + }, + bidOrders: []*Order{bidOrder(22, coin(10, "apple"), coin(11, "peach"))}, + expErr: "*exchange.unknownOrderType order 8 is not an ask order but is in the askOrders list at index 1", + }, + { + name: "ask order in bids", + askOrders: []*Order{askOrder(7, coin(10, "apple"), coin(11, "peach"))}, + bidOrders: []*Order{ + bidOrder(21, coin(10, "apple"), coin(11, "peach")), + askOrder(22, coin(10, "apple"), coin(11, "peach")), + bidOrder(23, coin(10, "apple"), coin(11, "peach")), + }, + expErr: "ask order 22 is not a bid order but is in the bidOrders list at index 1", + }, + { + name: "nil inside order in bids", + askOrders: []*Order{askOrder(7, coin(10, "apple"), coin(11, "peach"))}, + bidOrders: []*Order{ + bidOrder(21, coin(10, "apple"), coin(11, "peach")), + NewOrder(22), + bidOrder(23, coin(10, "apple"), coin(11, "peach")), + }, + expErr: " order 22 is not a bid order but is in the bidOrders list at index 1", + }, + { + name: "unknown inside order in bids", + askOrders: []*Order{askOrder(7, coin(10, "apple"), coin(11, "peach"))}, + bidOrders: []*Order{ + bidOrder(21, coin(10, "apple"), coin(11, "peach")), + newUnknownOrder(22), + bidOrder(23, coin(10, "apple"), coin(11, "peach")), + }, + expErr: "*exchange.unknownOrderType order 22 is not a bid order but is in the bidOrders list at index 1", + }, + { + name: "orders in wrong args", + askOrders: []*Order{ + askOrder(15, coin(10, "apple"), coin(11, "peach")), + bidOrder(16, coin(10, "apple"), coin(11, "peach")), + askOrder(17, coin(10, "apple"), coin(11, "peach")), + }, + bidOrders: []*Order{ + bidOrder(91, coin(10, "apple"), coin(11, "peach")), + askOrder(92, coin(10, "apple"), coin(11, "peach")), + bidOrder(93, coin(10, "apple"), coin(11, "peach")), + }, + expErr: joinErrs( + "bid order 16 is not an ask order but is in the askOrders list at index 1", + "ask order 92 is not a bid order but is in the bidOrders list at index 1", + ), + }, + { + name: "multiple ask asset denoms", + askOrders: []*Order{ + askOrder(55, coin(10, "apple"), coin(11, "peach")), + askOrder(56, coin(20, "avocado"), coin(22, "peach")), + }, + bidOrders: []*Order{ + bidOrder(61, coin(10, "apple"), coin(11, "peach")), + }, + expErr: "cannot settle with multiple ask order asset denoms \"10apple,20avocado\"", + }, + { + name: "multiple ask price denoms", + askOrders: []*Order{ + askOrder(55, coin(10, "apple"), coin(11, "peach")), + askOrder(56, coin(20, "apple"), coin(22, "plum")), + }, + bidOrders: []*Order{ + bidOrder(61, coin(10, "apple"), coin(11, "peach")), + }, + expErr: "cannot settle with multiple ask order price denoms \"11peach,22plum\"", + }, + { + name: "multiple bid asset denoms", + askOrders: []*Order{askOrder(88, coin(10, "apple"), coin(11, "peach"))}, + bidOrders: []*Order{ + bidOrder(12, coin(10, "apple"), coin(11, "peach")), + bidOrder(13, coin(20, "avocado"), coin(22, "peach")), + }, + expErr: "cannot settle with multiple bid order asset denoms \"10apple,20avocado\"", + }, + { + name: "multiple bid price denoms", + askOrders: []*Order{askOrder(88, coin(10, "apple"), coin(11, "peach"))}, + bidOrders: []*Order{ + bidOrder(12, coin(10, "apple"), coin(11, "peach")), + bidOrder(13, coin(20, "apple"), coin(22, "plum")), + }, + expErr: "cannot settle with multiple bid order price denoms \"11peach,22plum\"", + }, + { + name: "all different denoms", + askOrders: []*Order{ + askOrder(55, coin(10, "apple"), coin(11, "peach")), + askOrder(56, coin(20, "avocado"), coin(22, "plum")), + }, + bidOrders: []*Order{ + bidOrder(12, coin(30, "acorn"), coin(33, "prune")), + bidOrder(13, coin(40, "acai"), coin(44, "pear")), + }, + expErr: joinErrs( + "cannot settle with multiple ask order asset denoms \"10apple,20avocado\"", + "cannot settle with multiple ask order price denoms \"11peach,22plum\"", + "cannot settle with multiple bid order asset denoms \"40acai,30acorn\"", + "cannot settle with multiple bid order price denoms \"44pear,33prune\"", + ), + }, + { + name: "different ask and bid asset denoms", + askOrders: []*Order{ + askOrder(15, coin(10, "apple"), coin(11, "peach")), + askOrder(16, coin(20, "apple"), coin(22, "peach")), + }, + bidOrders: []*Order{ + bidOrder(2001, coin(30, "acorn"), coin(33, "peach")), + bidOrder(2002, coin(40, "acorn"), coin(44, "peach")), + }, + expErr: "cannot settle different ask \"30apple\" and bid \"70acorn\" asset denoms", + }, + { + name: "different ask and bid price denoms", + askOrders: []*Order{ + askOrder(15, coin(10, "apple"), coin(11, "peach")), + askOrder(16, coin(20, "apple"), coin(22, "peach")), + }, + bidOrders: []*Order{ + bidOrder(2001, coin(30, "apple"), coin(33, "plum")), + bidOrder(2002, coin(40, "apple"), coin(44, "plum")), + }, + expErr: "cannot settle different ask \"33peach\" and bid \"77plum\" price denoms", + }, + { + name: "different ask and bid denoms", + askOrders: []*Order{ + askOrder(15, coin(10, "apple"), coin(11, "peach")), + askOrder(16, coin(20, "apple"), coin(22, "peach")), + }, + bidOrders: []*Order{ + bidOrder(2001, coin(30, "acorn"), coin(33, "plum")), + bidOrder(2002, coin(40, "acorn"), coin(44, "plum")), + }, + expErr: joinErrs( + "cannot settle different ask \"30apple\" and bid \"70acorn\" asset denoms", + "cannot settle different ask \"33peach\" and bid \"77plum\" price denoms", + ), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = validateCanSettle(tc.askOrders, tc.bidOrders) + } + require.NotPanics(t, testFunc, "validateCanSettle") + assertions.AssertErrorValue(t, err, tc.expErr, "validateCanSettle error") + }) + } } func TestAllocateAssets(t *testing.T) { diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 6aba8c6435..e5372085ba 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -17,6 +17,11 @@ import ( "github.com/provenance-io/provenance/testutil/assertions" ) +// joinErrs joines the provided error strings into a single one to match what errors.Join does. +func joinErrs(errs ...string) string { + return strings.Join(errs, "\n") +} + func TestMarket_Validate(t *testing.T) { coins := func(coins string) sdk.Coins { rv, err := sdk.ParseCoinsNormalized(coins) @@ -180,9 +185,6 @@ func TestMarket_Validate(t *testing.T) { } func TestValidateFeeOptions(t *testing.T) { - joinErrs := func(errs ...string) string { - return strings.Join(errs, "\n") - } coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } @@ -395,9 +397,6 @@ func TestValidateFeeRatios(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } - joinErrs := func(errs ...string) string { - return strings.Join(errs, "\n") - } tests := []struct { name string @@ -1651,9 +1650,6 @@ func TestValidateAddRemoveFeeRatiosWithExisting(t *testing.T) { ratio := func(price, fee sdk.Coin) FeeRatio { return FeeRatio{Price: price, Fee: fee} } - joinErrs := func(errs ...string) string { - return strings.Join(errs, "\n") - } noRemErr := func(field, fee string) string { return fmt.Sprintf("cannot remove %s ratio fee %q: no such ratio exists", field, fee) } @@ -1841,9 +1837,6 @@ func TestValidateRatioDenoms(t *testing.T) { ratio := func(price, fee sdk.Coin) FeeRatio { return FeeRatio{Price: price, Fee: fee} } - joinErrs := func(errs ...string) string { - return strings.Join(errs, "\n") - } noBuyerDenomErr := func(denom string) string { return fmt.Sprintf("seller settlement fee ratios have price denom %q but there "+ "are no buyer settlement fee ratios with that price denom", denom) @@ -1979,9 +1972,6 @@ func TestValidateAddRemoveFeeOptions(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } - joinErrs := func(errs ...string) string { - return strings.Join(errs, "\n") - } tests := []struct { name string @@ -2118,9 +2108,6 @@ func TestValidateAddRemoveFeeOptionsWithExisting(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } - joinErrs := func(errs ...string) string { - return strings.Join(errs, "\n") - } noRemErr := func(field, fee string) string { return fmt.Sprintf("cannot remove %s flat fee %q: no such fee exists", field, fee) } @@ -2249,9 +2236,6 @@ func TestValidateAddRemoveFeeOptionsWithExisting(t *testing.T) { } func TestValidateAccessGrantsField(t *testing.T) { - joinErrs := func(errs ...string) string { - return strings.Join(errs, "\n") - } addrDup := sdk.AccAddress("duplicate_address___").String() addr1 := sdk.AccAddress("address_1___________").String() addr2 := sdk.AccAddress("address_2___________").String() @@ -3207,9 +3191,6 @@ func TestNormalizeReqAttrs(t *testing.T) { } func TestValidateReqAttrsAreNormalized(t *testing.T) { - joinErrs := func(errs ...string) string { - return strings.Join(errs, "\n") - } notNormErr := func(field, attr, norm string) string { return fmt.Sprintf("%s required attribute %q is not normalized, expected %q", field, attr, norm) } @@ -3328,10 +3309,6 @@ func TestValidateReqAttrsAreNormalized(t *testing.T) { } func TestValidateReqAttrs(t *testing.T) { - joinErrs := func(errs ...string) string { - return strings.Join(errs, "\n") - } - tests := []struct { name string field string From 84822ce113c2dd9fd2493d9693e5de10275a25c2 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 6 Oct 2023 01:22:02 -0600 Subject: [PATCH 245/309] [1658]: Fix allocateAssets giving the wrong thing to DistributeAssets. --- x/exchange/fulfillment.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 0d4eec9493..a4767987f1 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -426,7 +426,7 @@ func allocateAssets(askOFs, bidOFs []*OrderFulfillment) error { if err != nil { return err } - if err = DistributeAssets(askOFs[a], askOFs[b], assetsFilledAmt); err != nil { + if err = DistributeAssets(askOFs[a], bidOFs[b], assetsFilledAmt); err != nil { return err } From a60a4502591713a21722081e375a01d450e61536 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 6 Oct 2023 01:23:01 -0600 Subject: [PATCH 246/309] [1658]: Unit tests on allocateAssets and splitPartial. --- x/exchange/fulfillment_test.go | 581 +++++++++++++++++++++++++++++++-- 1 file changed, 561 insertions(+), 20 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 21ab2baf49..031d3852f8 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -77,7 +77,7 @@ func copyDistributions(dists []*Distribution) []*Distribution { return rv } -// copyOrderFulfillment returns a deep copy of an order fulfillement. +// copyOrderFulfillment returns a deep copy of an order fulfillment. func copyOrderFulfillment(f *OrderFulfillment) *OrderFulfillment { if f == nil { return nil @@ -100,6 +100,18 @@ func copyOrderFulfillment(f *OrderFulfillment) *OrderFulfillment { } } +// copyOrderFulfillments returns a deep copy of a slice of order fulfillments. +func copyOrderFulfillments(fs []*OrderFulfillment) []*OrderFulfillment { + if fs == nil { + return nil + } + rv := make([]*OrderFulfillment, len(fs)) + for i, f := range fs { + rv[i] = copyOrderFulfillment(f) + } + return rv +} + // orderSplitString is similar to %v except with easier to understand Coin and Int entries. func orderSplitString(s *OrderSplit) string { if s == nil { @@ -211,6 +223,65 @@ func assertEqualOrderFulfillments(t *testing.T, expected, actual *OrderFulfillme return false } +// assertEqualOrderFulfillmentSlices asserts that the two order fulfillments are equal. +// Returns true if equal. +// If not equal, and neither are nil, equality on each field is also asserted in order to help identify the problem. +func assertEqualOrderFulfillmentSlices(t *testing.T, expected, actual []*OrderFulfillment, message string, args ...interface{}) bool { + t.Helper() + if assert.Equalf(t, expected, actual, message, args...) { + return true + } + // If either is nil, that's easy to understand in the above failure, so there's nothing more to do. + if expected == nil || actual == nil { + return false + } + + msg := func(val string) string { + if len(message) == 0 { + return val + } + return val + "\n" + message + } + + // Check the order ids (and lengths) since that's gonna be a huge clue to a problem + expIDs := make([]string, len(expected)) + for i, exp := range expected { + expIDs[i] = fmt.Sprintf("%d", exp.GetOrderID()) + } + actIDs := make([]string, len(actual)) + for i, act := range actual { + actIDs[i] = fmt.Sprintf("%d", act.GetOrderID()) + } + if !assert.Equalf(t, expIDs, actIDs, msg("OrderIDs"), args...) { + // Wooo, should have actionable info in the failure, so we can be done. + return false + } + + // Try the comparisons as strings, one per line because that's easier with ints and coins. + expStrVals := make([]string, len(expected)) + for i, exp := range expected { + expStrVals[i] = orderFulfillmentString(exp) + } + expStrs := strings.Join(expStrVals, "\n") + actStrVals := make([]string, len(actual)) + for i, act := range actual { + actStrVals[i] = orderFulfillmentString(act) + } + actStrs := strings.Join(actStrVals, "\n") + if !assert.Equalf(t, expStrs, actStrs, msg("OrderFulfillment strings"), args...) { + // Wooo, should have actionable info in the failure, so we can be done. + return false + } + + // Alright, do it the hard way one at a time. + for i := range expected { + assertEqualOrderFulfillments(t, expected[i], actual[i], fmt.Sprintf("[%d]%s", i, message), args...) + } + t.Logf(" Actual: %s", orderFulfillmentsString(actual)) + t.Logf("Expected: %s", orderFulfillmentsString(expected)) + return false +} + func TestNewOrderFulfillment(t *testing.T) { tests := []struct { name string @@ -491,21 +562,7 @@ func TestNewOrderFulfillments(t *testing.T) { actual = NewOrderFulfillments(tc.orders) } require.NotPanics(t, testFunc, "NewOrderFulfillments") - if !assert.Equal(t, tc.expected, actual, "NewOrderFulfillments result") { - expOrderIDs := make([]uint64, len(tc.expected)) - for i, f := range tc.expected { - expOrderIDs[i] = f.GetOrderID() - } - actOrderIDs := make([]uint64, len(actual)) - for i, f := range actual { - actOrderIDs[i] = f.GetOrderID() - } - if !assert.Equal(t, expOrderIDs, actOrderIDs, "NewOrderFulfillments result order ids") && len(tc.expected) == len(actual) { - for i := range tc.expected { - assertEqualOrderFulfillments(t, tc.expected[i], actual[i], "NewOrderFulfillments result[%d]", i) - } - } - } + assertEqualOrderFulfillmentSlices(t, tc.expected, actual, "NewOrderFulfillments result") }) } } @@ -2568,13 +2625,497 @@ func TestValidateCanSettle(t *testing.T) { } func TestAllocateAssets(t *testing.T) { - // TODO[1658]: func TestAllocateAssets(t *testing.T) - t.Skipf("not written") + askOrder := func(orderID uint64, assetsAmt int64, seller string) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Seller: seller, + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, + }) + } + bidOrder := func(orderID uint64, assetsAmt int64, buyer string) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Buyer: buyer, + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, + }) + } + newOF := func(order *Order, dists ...*Distribution) *OrderFulfillment { + rv := &OrderFulfillment{ + Order: order, + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: order.GetAssets().Amount, + } + if len(dists) > 0 { + rv.AssetDists = dists + for _, d := range dists { + rv.AssetsFilledAmt = rv.AssetsFilledAmt.Add(d.Amount) + rv.AssetsUnfilledAmt = rv.AssetsUnfilledAmt.Sub(d.Amount) + } + } + return rv + } + dist := func(addr string, amount int64) *Distribution { + return &Distribution{Address: addr, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + askOFs []*OrderFulfillment + bidOFs []*OrderFulfillment + expAskOFs []*OrderFulfillment + expBidOfs []*OrderFulfillment + expErr string + }{ + { + name: "one ask, one bid: both full", + askOFs: []*OrderFulfillment{newOF(askOrder(5, 10, "seller"))}, + bidOFs: []*OrderFulfillment{newOF(bidOrder(6, 10, "buyer"))}, + expAskOFs: []*OrderFulfillment{newOF(askOrder(5, 10, "seller"), dist("buyer", 10))}, + expBidOfs: []*OrderFulfillment{newOF(bidOrder(6, 10, "buyer"), dist("seller", 10))}, + }, + { + name: "one ask, one bid: ask partial", + askOFs: []*OrderFulfillment{newOF(askOrder(5, 11, "seller"))}, + bidOFs: []*OrderFulfillment{newOF(bidOrder(16, 10, "buyer"))}, + expAskOFs: []*OrderFulfillment{newOF(askOrder(5, 11, "seller"), dist("buyer", 10))}, + expBidOfs: []*OrderFulfillment{newOF(bidOrder(16, 10, "buyer"), dist("seller", 10))}, + }, + { + name: "one ask, one bid: bid partial", + askOFs: []*OrderFulfillment{newOF(askOrder(15, 10, "seller"))}, + bidOFs: []*OrderFulfillment{newOF(bidOrder(6, 11, "buyer"))}, + expAskOFs: []*OrderFulfillment{newOF(askOrder(15, 10, "seller"), dist("buyer", 10))}, + expBidOfs: []*OrderFulfillment{newOF(bidOrder(6, 11, "buyer"), dist("seller", 10))}, + }, + { + name: "one ask, two bids: last bid not touched", + askOFs: []*OrderFulfillment{newOF(askOrder(22, 10, "seller"))}, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(64, 12, "buyer64")), + newOF(bidOrder(78, 1, "buyer78")), + }, + expAskOFs: []*OrderFulfillment{newOF(askOrder(22, 10, "seller"), dist("buyer64", 10))}, + expBidOfs: []*OrderFulfillment{ + newOF(bidOrder(64, 12, "buyer64"), dist("seller", 10)), + newOF(bidOrder(78, 1, "buyer78")), + }, + }, + { + name: "two asks, one bids: last ask not touched", + askOFs: []*OrderFulfillment{ + newOF(askOrder(888, 10, "seller888")), + newOF(askOrder(999, 10, "seller999")), + }, + bidOFs: []*OrderFulfillment{newOF(bidOrder(6, 10, "buyer"))}, + expAskOFs: []*OrderFulfillment{ + newOF(askOrder(888, 10, "seller888"), dist("buyer", 10)), + newOF(askOrder(999, 10, "seller999")), + }, + expBidOfs: []*OrderFulfillment{newOF(bidOrder(6, 10, "buyer"), dist("seller888", 10))}, + }, + { + name: "two asks, three bids: both full", + askOFs: []*OrderFulfillment{ + newOF(askOrder(101, 15, "seller101")), + newOF(askOrder(102, 25, "seller102")), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(103, 10, "buyer103")), + newOF(bidOrder(104, 8, "buyer104")), + newOF(bidOrder(105, 22, "buyer105")), + }, + expAskOFs: []*OrderFulfillment{ + newOF(askOrder(101, 15, "seller101"), dist("buyer103", 10), dist("buyer104", 5)), + newOF(askOrder(102, 25, "seller102"), dist("buyer104", 3), dist("buyer105", 22)), + }, + expBidOfs: []*OrderFulfillment{ + newOF(bidOrder(103, 10, "buyer103"), dist("seller101", 10)), + newOF(bidOrder(104, 8, "buyer104"), dist("seller101", 5), dist("seller102", 3)), + newOF(bidOrder(105, 22, "buyer105"), dist("seller102", 22)), + }, + }, + { + name: "two asks, three bids: ask partial", + askOFs: []*OrderFulfillment{ + newOF(askOrder(101, 15, "seller101")), + newOF(askOrder(102, 26, "seller102")), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(103, 10, "buyer103")), + newOF(bidOrder(104, 8, "buyer104")), + newOF(bidOrder(105, 22, "buyer105")), + }, + expAskOFs: []*OrderFulfillment{ + newOF(askOrder(101, 15, "seller101"), dist("buyer103", 10), dist("buyer104", 5)), + newOF(askOrder(102, 26, "seller102"), dist("buyer104", 3), dist("buyer105", 22)), + }, + expBidOfs: []*OrderFulfillment{ + newOF(bidOrder(103, 10, "buyer103"), dist("seller101", 10)), + newOF(bidOrder(104, 8, "buyer104"), dist("seller101", 5), dist("seller102", 3)), + newOF(bidOrder(105, 22, "buyer105"), dist("seller102", 22)), + }, + }, + { + name: "two asks, three bids: bid partial", + askOFs: []*OrderFulfillment{ + newOF(askOrder(101, 15, "seller101")), + newOF(askOrder(102, 25, "seller102")), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(103, 10, "buyer103")), + newOF(bidOrder(104, 8, "buyer104")), + newOF(bidOrder(105, 23, "buyer105")), + }, + expAskOFs: []*OrderFulfillment{ + newOF(askOrder(101, 15, "seller101"), dist("buyer103", 10), dist("buyer104", 5)), + newOF(askOrder(102, 25, "seller102"), dist("buyer104", 3), dist("buyer105", 22)), + }, + expBidOfs: []*OrderFulfillment{ + newOF(bidOrder(103, 10, "buyer103"), dist("seller101", 10)), + newOF(bidOrder(104, 8, "buyer104"), dist("seller101", 5), dist("seller102", 3)), + newOF(bidOrder(105, 23, "buyer105"), dist("seller102", 22)), + }, + }, + { + name: "negative ask assets unfilled", + askOFs: []*OrderFulfillment{newOF(askOrder(101, 10, "seller"), dist("buyerx", 11))}, + bidOFs: []*OrderFulfillment{newOF(bidOrder(102, 10, "buyer"))}, + expErr: "cannot fill ask order 101 having assets left \"-1apple\" with bid order 102 having " + + "assets left \"10apple\": zero or negative assets left", + }, + { + name: "negative bid assets unfilled", + askOFs: []*OrderFulfillment{newOF(askOrder(101, 10, "seller"))}, + bidOFs: []*OrderFulfillment{newOF(bidOrder(102, 10, "buyer"), dist("sellerx", 11))}, + expErr: "cannot fill ask order 101 having assets left \"10apple\" with bid order 102 having " + + "assets left \"-1apple\": zero or negative assets left", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + origAskOFs := copyOrderFulfillments(tc.askOFs) + origBidOFs := copyOrderFulfillments(tc.bidOFs) + + var err error + testFunc := func() { + err = allocateAssets(tc.askOFs, tc.bidOFs) + } + require.NotPanics(t, testFunc, "allocateAssets") + assertions.AssertErrorValue(t, err, tc.expErr, "allocateAssets error") + if len(tc.expErr) > 0 { + return + } + if !assertEqualOrderFulfillmentSlices(t, tc.expAskOFs, tc.askOFs, "askOFs after allocateAssets") { + t.Logf("Original: %s", orderFulfillmentsString(origAskOFs)) + } + if !assertEqualOrderFulfillmentSlices(t, tc.expBidOfs, tc.bidOFs, "bidOFs after allocateAssets") { + t.Logf("Original: %s", orderFulfillmentsString(origBidOFs)) + } + }) + } } func TestSplitPartial(t *testing.T) { - // TODO[1658]: func TestSplitPartial(t *testing.T) - t.Skipf("not written") + askOrder := func(orderID uint64, assetsAmt int64, seller string) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Seller: seller, + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, + Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(assetsAmt)}, + AllowPartial: true, + }) + } + bidOrder := func(orderID uint64, assetsAmt int64, buyer string) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Buyer: buyer, + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, + Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(assetsAmt)}, + AllowPartial: true, + }) + } + newOF := func(order *Order, dists ...*Distribution) *OrderFulfillment { + rv := &OrderFulfillment{ + Order: order, + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: order.GetAssets().Amount, + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: order.GetPrice().Amount, + } + if len(dists) > 0 { + rv.AssetDists = dists + for _, d := range dists { + rv.AssetsFilledAmt = rv.AssetsFilledAmt.Add(d.Amount) + rv.AssetsUnfilledAmt = rv.AssetsUnfilledAmt.Sub(d.Amount) + } + if rv.AssetsUnfilledAmt.IsZero() { + rv.AssetsUnfilledAmt = sdkmath.NewInt(0) + } + } + return rv + } + dist := func(addr string, amount int64) *Distribution { + return &Distribution{Address: addr, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + askOFs []*OrderFulfillment + bidOFs []*OrderFulfillment + expAskOFs []*OrderFulfillment + expBidOfs []*OrderFulfillment + expSettlement *Settlement + expErr string + }{ + { + name: "one ask: not touched", + askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"))}, + expErr: "ask order 8 (at index 0) has no assets filled", + }, + { + name: "one ask: partial", + askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer", 7))}, + expAskOFs: []*OrderFulfillment{newOF(askOrder(8, 7, "seller8"), dist("buyer", 7))}, + expSettlement: &Settlement{ + FullyFilledOrders: nil, + PartialOrderFilled: askOrder(8, 7, "seller8"), + PartialOrderLeft: askOrder(8, 3, "seller8"), + }, + }, + { + name: "one ask: partial, not allowed", + askOFs: []*OrderFulfillment{ + newOF(NewOrder(8).WithAsk(&AskOrder{ + Seller: "seller8", + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(10)}, + Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(10)}, + AllowPartial: false, + }), dist("buyer", 7))}, + expErr: "cannot split ask order 8 having assets \"10apple\" at \"7apple\": order does not allow partial fulfillment", + }, + { + name: "two asks: first partial", + askOFs: []*OrderFulfillment{ + newOF(askOrder(8, 10, "seller8"), dist("buyer", 7)), + newOF(askOrder(9, 12, "seller8")), + }, + expErr: "ask order 8 (at index 0) is not filled in full and is not the last ask order provided", + }, + { + name: "two asks: last untouched", + askOFs: []*OrderFulfillment{ + newOF(askOrder(8, 10, "seller8"), dist("buyer", 10)), + newOF(askOrder(9, 12, "seller8")), + }, + expErr: "ask order 9 (at index 1) has no assets filled", + }, + { + name: "two asks: last partial", + askOFs: []*OrderFulfillment{ + newOF(askOrder(8, 10, "seller8"), dist("buyer", 10)), + newOF(askOrder(9, 12, "seller9"), dist("buyer", 10)), + }, + expAskOFs: []*OrderFulfillment{ + newOF(askOrder(8, 10, "seller8"), dist("buyer", 10)), + newOF(askOrder(9, 10, "seller9"), dist("buyer", 10)), + }, + expSettlement: &Settlement{ + FullyFilledOrders: []*Order{askOrder(8, 10, "seller8")}, + PartialOrderFilled: askOrder(9, 10, "seller9"), + PartialOrderLeft: askOrder(9, 2, "seller9"), + }, + }, + + { + name: "one bid: not touched", + bidOFs: []*OrderFulfillment{newOF(bidOrder(8, 10, "buyer8"))}, + expErr: "bid order 8 (at index 0) has no assets filled", + }, + { + name: "one bid: partial", + bidOFs: []*OrderFulfillment{newOF(bidOrder(8, 10, "buyer8"), dist("seller", 7))}, + expBidOfs: []*OrderFulfillment{newOF(bidOrder(8, 7, "buyer8"), dist("seller", 7))}, + expSettlement: &Settlement{ + FullyFilledOrders: nil, + PartialOrderFilled: bidOrder(8, 7, "buyer8"), + PartialOrderLeft: bidOrder(8, 3, "buyer8"), + }, + }, + { + name: "one bid: partial, not allowed", + askOFs: []*OrderFulfillment{ + newOF(NewOrder(8).WithBid(&BidOrder{ + Buyer: "buyer8", + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(10)}, + Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(10)}, + AllowPartial: false, + }), dist("seller", 7))}, + expErr: "cannot split bid order 8 having assets \"10apple\" at \"7apple\": order does not allow partial fulfillment", + }, + { + name: "two bids: first partial", + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(8, 10, "buyer8"), dist("seller", 7)), + newOF(bidOrder(9, 12, "buyer9")), + }, + expErr: "bid order 8 (at index 0) is not filled in full and is not the last bid order provided", + }, + { + name: "two bids: last untouched", + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(8, 10, "buyer8"), dist("seller", 10)), + newOF(bidOrder(9, 12, "buyer9")), + }, + expErr: "bid order 9 (at index 1) has no assets filled", + }, + { + name: "two bids: last partial", + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(8, 10, "buyer8"), dist("seller", 10)), + newOF(bidOrder(9, 12, "buyer9"), dist("seller", 10)), + }, + expBidOfs: []*OrderFulfillment{ + newOF(bidOrder(8, 10, "buyer8"), dist("seller", 10)), + newOF(bidOrder(9, 10, "buyer9"), dist("seller", 10)), + }, + expSettlement: &Settlement{ + FullyFilledOrders: []*Order{bidOrder(8, 10, "buyer8")}, + PartialOrderFilled: bidOrder(9, 10, "buyer9"), + PartialOrderLeft: bidOrder(9, 2, "buyer9"), + }, + }, + { + name: "one ask, one bid: both partial", + askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer9", 7))}, + bidOFs: []*OrderFulfillment{newOF(bidOrder(9, 10, "buyer9"), dist("seller8", 7))}, + expErr: "ask order 8 and bid order 9 cannot both be partially filled", + }, + { + name: "three asks, three bids: no partial", + askOFs: []*OrderFulfillment{ + newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), + newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), + newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), + newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), + newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), + }, + expAskOFs: []*OrderFulfillment{ + newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), + newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), + newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + }, + expBidOfs: []*OrderFulfillment{ + newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), + newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), + newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), + }, + expSettlement: &Settlement{ + FullyFilledOrders: []*Order{ + askOrder(51, 10, "seller51"), + askOrder(77, 15, "seller77"), + askOrder(12, 20, "seller12"), + bidOrder(99, 20, "buyer99"), + bidOrder(8, 15, "buyer8"), + bidOrder(112, 10, "buyer112"), + }, + PartialOrderFilled: nil, + PartialOrderLeft: nil, + }, + }, + { + name: "three asks, three bids: partial ask", + askOFs: []*OrderFulfillment{ + newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), + newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), + newOF(askOrder(12, 21, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), + newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), + newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), + }, + expAskOFs: []*OrderFulfillment{ + newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), + newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), + newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + }, + expBidOfs: []*OrderFulfillment{ + newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), + newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), + newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), + }, + expSettlement: &Settlement{ + FullyFilledOrders: []*Order{ + askOrder(51, 10, "seller51"), + askOrder(77, 15, "seller77"), + bidOrder(99, 20, "buyer99"), + bidOrder(8, 15, "buyer8"), + bidOrder(112, 10, "buyer112"), + }, + PartialOrderFilled: askOrder(12, 20, "seller12"), + PartialOrderLeft: askOrder(12, 1, "seller12"), + }, + }, + { + name: "three asks, three bids: no partial", + askOFs: []*OrderFulfillment{ + newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), + newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), + newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), + newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), + newOF(bidOrder(112, 11, "buyer112"), dist("seller12", 10)), + }, + expAskOFs: []*OrderFulfillment{ + newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), + newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), + newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + }, + expBidOfs: []*OrderFulfillment{ + newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), + newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), + newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), + }, + expSettlement: &Settlement{ + FullyFilledOrders: []*Order{ + askOrder(51, 10, "seller51"), + askOrder(77, 15, "seller77"), + askOrder(12, 20, "seller12"), + bidOrder(99, 20, "buyer99"), + bidOrder(8, 15, "buyer8"), + }, + PartialOrderFilled: bidOrder(112, 10, "buyer112"), + PartialOrderLeft: bidOrder(112, 1, "buyer112"), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + origAskOFs := copyOrderFulfillments(tc.askOFs) + origBidOFs := copyOrderFulfillments(tc.bidOFs) + + var settlement *Settlement + var err error + testFunc := func() { + settlement, err = splitPartial(tc.askOFs, tc.bidOFs) + } + require.NotPanics(t, testFunc, "splitPartial") + assertions.AssertErrorValue(t, err, tc.expErr, "splitPartial error") + assert.Equalf(t, tc.expSettlement, settlement, "splitPartial settlement") + if len(tc.expErr) > 0 { + return + } + if !assertEqualOrderFulfillmentSlices(t, tc.expAskOFs, tc.askOFs, "askOFs after splitPartial") { + t.Logf("Original: %s", orderFulfillmentsString(origAskOFs)) + } + if !assertEqualOrderFulfillmentSlices(t, tc.expBidOfs, tc.bidOFs, "bidOFs after splitPartial") { + t.Logf("Original: %s", orderFulfillmentsString(origBidOFs)) + } + }) + } } func TestAllocatePrice(t *testing.T) { From c87f99fd5a5ea32540d6e85ab9f36d950518ed0e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 6 Oct 2023 10:12:22 -0600 Subject: [PATCH 247/309] [1658]: Fix buildTransfers to not double up the transfers. --- x/exchange/fulfillment.go | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index a4767987f1..5eff4c14e7 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -651,29 +651,18 @@ func validateFulfillments(askOFs, bidOFs []*OrderFulfillment) error { // buildTransfers creates the transfers, inputs for fee payments, // and fee total and sets those fields in the provided Settlement. func buildTransfers(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) error { - allOFs := make([]*OrderFulfillment, 0, len(askOFs)+len(bidOFs)) - allOFs = append(allOFs, askOFs...) - allOFs = append(allOFs, bidOFs...) - + var errs []error indexedFees := newIndexedAddrAmts() - transfers := make([]*Transfer, 0, len(allOFs)*2) + transfers := make([]*Transfer, 0, len(askOFs)+len(bidOFs)) - var errs []error - for _, of := range allOFs { - assetTrans, err := getAssetTransfer(of) + record := func(of *OrderFulfillment, getter func(fulfillment *OrderFulfillment) (*Transfer, error)) { + assetTrans, err := getter(of) if err != nil { errs = append(errs, err) } else { transfers = append(transfers, assetTrans) } - priceTrans, err := getPriceTransfer(of) - if err != nil { - errs = append(errs, err) - } else { - transfers = append(transfers, priceTrans) - } - if !of.FeesToPay.IsZero() { if of.FeesToPay.IsAnyNegative() { errs = append(errs, fmt.Errorf("%s order %d cannot pay %q in fees: negative amount", @@ -684,6 +673,22 @@ func buildTransfers(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) } } + // If we got both the asset and price transfers from all OrderFulfillments, we'd be doubling + // up on what's being traded. So we need to only get each from only one list. + // Since we need to loop through both lists to make note of the fees, though, I thought it + // would be good to get one from one list, and the other from the other. So I decided to get + // the asset transfers from the asks, and price transfers from the bids so that each transfer + // will have one input and multiple (or one) outputs. I'm not sure that it matters too much + // where we get each transfer type from, though. + + for _, of := range askOFs { + record(of, getAssetTransfer) + } + + for _, of := range bidOFs { + record(of, getPriceTransfer) + } + if len(errs) > 0 { return errors.Join(errs...) } From 8d6fd55c5260fe0d534a8adf54e29c116942022d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 6 Oct 2023 12:39:08 -0600 Subject: [PATCH 248/309] [1658]: Add some fields to EventOrderFilled and EventOrderPartiallyFilled. --- docs/proto-docs.md | 8 +- proto/provenance/exchange/v1/events.proto | 18 +- x/exchange/events.go | 16 +- x/exchange/events.pb.go | 324 ++++++++++++++++++---- x/exchange/events_test.go | 228 +++++++++++++-- 5 files changed, 505 insertions(+), 89 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 66d6bf9f5e..899b6bc907 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1556,6 +1556,9 @@ This event is also used for orders that were previously partially filled, but ha | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order filled. | +| `assets` | [string](#string) | | assets is the coins amount string of assets bought/sold for this order. | +| `price` | [string](#string) | | price is the coins amount string of the price payed/received for this order. | +| `fees` | [string](#string) | | fees is the coins amount string of settlement fees paid with this order. | @@ -1571,8 +1574,9 @@ EventOrderPartiallyFilled is an event emitted when an order filled in part and s | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order partially filled. | -| `assets_filled` | [string](#string) | | assets_filled is the coins amount string of assets that were filled and removed from the order. | -| `fees_filled` | [string](#string) | | fees_filled is the coins amount string of fees paid and removed from the order. | +| `assets` | [string](#string) | | assets is the coins amount string of assets that were filled and removed from the order. | +| `price` | [string](#string) | | price is the coins amount string of the price payed/received for this order. For ask orders, this might be more than the amount that was removed from the order's price. | +| `fees` | [string](#string) | | fees is the coins amount string of settlement fees paid with this partial order. For ask orders, this might be more than the amount that was removed from the order's settlement fees. | diff --git a/proto/provenance/exchange/v1/events.proto b/proto/provenance/exchange/v1/events.proto index 93c44d9eae..f3da3c78eb 100644 --- a/proto/provenance/exchange/v1/events.proto +++ b/proto/provenance/exchange/v1/events.proto @@ -29,16 +29,26 @@ message EventOrderCancelled { message EventOrderFilled { // order_id is the numerical identifier of the order filled. uint64 order_id = 1; + // assets is the coins amount string of assets bought/sold for this order. + string assets = 2; + // price is the coins amount string of the price payed/received for this order. + string price = 3; + // fees is the coins amount string of settlement fees paid with this order. + string fees = 4; } // EventOrderPartiallyFilled is an event emitted when an order filled in part and still has more left to fill. message EventOrderPartiallyFilled { // order_id is the numerical identifier of the order partially filled. uint64 order_id = 1; - // assets_filled is the coins amount string of assets that were filled and removed from the order. - string assets_filled = 2; - // fees_filled is the coins amount string of fees paid and removed from the order. - string fees_filled = 3; + // assets is the coins amount string of assets that were filled and removed from the order. + string assets = 2; + // price is the coins amount string of the price payed/received for this order. + // For ask orders, this might be more than the amount that was removed from the order's price. + string price = 3; + // fees is the coins amount string of settlement fees paid with this partial order. + // For ask orders, this might be more than the amount that was removed from the order's settlement fees. + string fees = 4; } // EventMarketWithdraw is an event emitted when a withdrawal of a market's collected fees is made. diff --git a/x/exchange/events.go b/x/exchange/events.go index a82c80df5f..436e81553a 100644 --- a/x/exchange/events.go +++ b/x/exchange/events.go @@ -20,17 +20,21 @@ func NewEventOrderCancelled(orderID uint64, cancelledBy sdk.AccAddress) *EventOr } } -func NewEventOrderFilled(orderID uint64) *EventOrderFilled { +func NewEventOrderFilled(order OrderI) *EventOrderFilled { return &EventOrderFilled{ - OrderId: orderID, + OrderId: order.GetOrderID(), + Assets: order.GetAssets().String(), + Price: order.GetPrice().String(), + Fees: order.GetSettlementFees().String(), } } -func NewEventOrderPartiallyFilled(orderID uint64, assetsFilled, feesFilled sdk.Coin) *EventOrderPartiallyFilled { +func NewEventOrderPartiallyFilled(order OrderI) *EventOrderPartiallyFilled { return &EventOrderPartiallyFilled{ - OrderId: orderID, - AssetsFilled: assetsFilled.String(), - FeesFilled: feesFilled.String(), + OrderId: order.GetOrderID(), + Assets: order.GetAssets().String(), + Price: order.GetPrice().String(), + Fees: order.GetSettlementFees().String(), } } diff --git a/x/exchange/events.pb.go b/x/exchange/events.pb.go index 6e627c95e8..a7700ff0f7 100644 --- a/x/exchange/events.pb.go +++ b/x/exchange/events.pb.go @@ -138,6 +138,12 @@ func (m *EventOrderCancelled) GetCancelledBy() string { type EventOrderFilled struct { // order_id is the numerical identifier of the order filled. OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + // assets is the coins amount string of assets bought/sold for this order. + Assets string `protobuf:"bytes,2,opt,name=assets,proto3" json:"assets,omitempty"` + // price is the coins amount string of the price payed/received for this order. + Price string `protobuf:"bytes,3,opt,name=price,proto3" json:"price,omitempty"` + // fees is the coins amount string of settlement fees paid with this order. + Fees string `protobuf:"bytes,4,opt,name=fees,proto3" json:"fees,omitempty"` } func (m *EventOrderFilled) Reset() { *m = EventOrderFilled{} } @@ -180,14 +186,39 @@ func (m *EventOrderFilled) GetOrderId() uint64 { return 0 } +func (m *EventOrderFilled) GetAssets() string { + if m != nil { + return m.Assets + } + return "" +} + +func (m *EventOrderFilled) GetPrice() string { + if m != nil { + return m.Price + } + return "" +} + +func (m *EventOrderFilled) GetFees() string { + if m != nil { + return m.Fees + } + return "" +} + // EventOrderPartiallyFilled is an event emitted when an order filled in part and still has more left to fill. type EventOrderPartiallyFilled struct { // order_id is the numerical identifier of the order partially filled. OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` - // assets_filled is the coins amount string of assets that were filled and removed from the order. - AssetsFilled string `protobuf:"bytes,2,opt,name=assets_filled,json=assetsFilled,proto3" json:"assets_filled,omitempty"` - // fees_filled is the coins amount string of fees paid and removed from the order. - FeesFilled string `protobuf:"bytes,3,opt,name=fees_filled,json=feesFilled,proto3" json:"fees_filled,omitempty"` + // assets is the coins amount string of assets that were filled and removed from the order. + Assets string `protobuf:"bytes,2,opt,name=assets,proto3" json:"assets,omitempty"` + // price is the coins amount string of the price payed/received for this order. + // For ask orders, this might be more than the amount that was removed from the order's price. + Price string `protobuf:"bytes,3,opt,name=price,proto3" json:"price,omitempty"` + // fees is the coins amount string of settlement fees paid with this partial order. + // For ask orders, this might be more than the amount that was removed from the order's settlement fees. + Fees string `protobuf:"bytes,4,opt,name=fees,proto3" json:"fees,omitempty"` } func (m *EventOrderPartiallyFilled) Reset() { *m = EventOrderPartiallyFilled{} } @@ -230,16 +261,23 @@ func (m *EventOrderPartiallyFilled) GetOrderId() uint64 { return 0 } -func (m *EventOrderPartiallyFilled) GetAssetsFilled() string { +func (m *EventOrderPartiallyFilled) GetAssets() string { + if m != nil { + return m.Assets + } + return "" +} + +func (m *EventOrderPartiallyFilled) GetPrice() string { if m != nil { - return m.AssetsFilled + return m.Price } return "" } -func (m *EventOrderPartiallyFilled) GetFeesFilled() string { +func (m *EventOrderPartiallyFilled) GetFees() string { if m != nil { - return m.FeesFilled + return m.Fees } return "" } @@ -854,41 +892,41 @@ func init() { } var fileDescriptor_c1b69385a348cffa = []byte{ - // 536 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xc1, 0x6e, 0xd3, 0x40, - 0x10, 0x40, 0x6b, 0xa8, 0x4a, 0x33, 0x69, 0x25, 0x30, 0x55, 0x94, 0x00, 0x35, 0x95, 0x7b, 0xe9, - 0x25, 0xb6, 0x22, 0x84, 0x90, 0xe0, 0xd4, 0xd0, 0x56, 0xea, 0xa1, 0x22, 0x4a, 0xa9, 0x90, 0xb8, - 0x44, 0x9b, 0x78, 0x9a, 0x2c, 0xb5, 0x77, 0xd3, 0xdd, 0x4d, 0x5a, 0x4b, 0x7c, 0x04, 0x1f, 0xc3, - 0x1f, 0x70, 0xe1, 0x58, 0x71, 0xe2, 0x88, 0x92, 0x1f, 0x41, 0xf6, 0xae, 0x13, 0x47, 0xaa, 0x92, - 0x5e, 0x7c, 0x9c, 0xc9, 0x9b, 0x7d, 0x9e, 0xd9, 0x51, 0x16, 0xf6, 0x87, 0x82, 0x8f, 0x91, 0x11, - 0xd6, 0x43, 0x1f, 0x6f, 0x7b, 0x03, 0xc2, 0xfa, 0xe8, 0x8f, 0x1b, 0x3e, 0x8e, 0x91, 0x29, 0xe9, - 0x0d, 0x05, 0x57, 0xdc, 0xae, 0xcc, 0x21, 0x2f, 0x83, 0xbc, 0x71, 0xe3, 0x45, 0xad, 0xc7, 0x65, - 0xc4, 0x65, 0x27, 0xa5, 0x7c, 0x1d, 0xe8, 0x12, 0xf7, 0x0c, 0x9e, 0x1d, 0x27, 0x47, 0x7c, 0x12, - 0x01, 0x8a, 0x8f, 0x02, 0x89, 0xc2, 0xc0, 0xae, 0xc1, 0x26, 0x4f, 0xe2, 0x0e, 0x0d, 0xaa, 0xd6, - 0x9e, 0x75, 0xb0, 0xde, 0x7e, 0x92, 0xc6, 0xa7, 0x81, 0xbd, 0x0b, 0xa0, 0x7f, 0x52, 0xf1, 0x10, - 0xab, 0x8f, 0xf6, 0xac, 0x83, 0x52, 0xbb, 0x94, 0x66, 0x3e, 0xc7, 0x43, 0x74, 0x23, 0x78, 0x9e, - 0x3b, 0x2e, 0xf9, 0x90, 0x30, 0x5c, 0x7e, 0xe0, 0x07, 0xd8, 0xea, 0x65, 0x5c, 0xa7, 0x1b, 0xeb, - 0x23, 0x9b, 0xd5, 0x3f, 0x3f, 0xeb, 0x3b, 0xe6, 0x43, 0x0f, 0x83, 0x40, 0xa0, 0x94, 0xe7, 0x4a, - 0x50, 0xd6, 0x6f, 0x97, 0x67, 0x74, 0x33, 0x76, 0xeb, 0xf0, 0x74, 0xae, 0x3b, 0xa1, 0x2b, 0x5c, - 0xee, 0x77, 0xa8, 0xcd, 0xf1, 0x16, 0x11, 0x8a, 0x92, 0x30, 0x8c, 0x57, 0xd6, 0xd9, 0xfb, 0xb0, - 0x4d, 0xa4, 0x44, 0x25, 0x3b, 0x97, 0x29, 0x6b, 0xfa, 0xde, 0xd2, 0x49, 0x53, 0xff, 0x1a, 0xca, - 0x97, 0x88, 0x33, 0xe4, 0x71, 0x8a, 0x40, 0x92, 0xd2, 0x80, 0xfb, 0xcb, 0x32, 0xc3, 0x39, 0x23, - 0xe2, 0x0a, 0xd5, 0x17, 0xaa, 0x06, 0x81, 0x20, 0x37, 0xf6, 0x4b, 0x28, 0x45, 0x69, 0x26, 0x33, - 0x6f, 0xb7, 0x37, 0x75, 0xe2, 0x34, 0xb0, 0x2b, 0xb0, 0x41, 0x22, 0x3e, 0x62, 0xca, 0x38, 0x4d, - 0x64, 0xbf, 0x87, 0x72, 0x80, 0x52, 0x51, 0x46, 0x14, 0xe5, 0x4c, 0xdb, 0x96, 0x4d, 0x2d, 0x07, - 0x27, 0x23, 0xbf, 0x31, 0x72, 0x96, 0x8c, 0x7c, 0x7d, 0x55, 0xf1, 0x8c, 0x6e, 0xc6, 0xee, 0xb5, - 0x99, 0xa1, 0x6e, 0xe2, 0x08, 0x15, 0xa1, 0xa1, 0xbc, 0x18, 0x06, 0xe9, 0xe2, 0x2c, 0x6d, 0xe5, - 0x1d, 0xc0, 0x48, 0x73, 0x0f, 0xb9, 0xe7, 0x92, 0x61, 0x9b, 0xb1, 0xfb, 0x0d, 0xec, 0x9c, 0xf2, - 0x98, 0x91, 0x6e, 0x58, 0x98, 0xeb, 0x6a, 0xe1, 0x8e, 0x8e, 0xa8, 0x2c, 0x52, 0xa6, 0xe0, 0x55, - 0x4e, 0x76, 0x21, 0x51, 0x9c, 0xa3, 0x52, 0x21, 0x16, 0xdb, 0xe2, 0x08, 0x76, 0xef, 0xb5, 0x16, - 0xdc, 0xec, 0xa2, 0xb6, 0x85, 0x22, 0xa2, 0x52, 0x52, 0xce, 0x0a, 0x5e, 0x9e, 0xc5, 0x7d, 0x6d, - 0xe3, 0xf5, 0xa1, 0x52, 0xa2, 0x58, 0x65, 0x63, 0x61, 0x5f, 0xb3, 0x3f, 0xd5, 0x65, 0x2e, 0xf7, - 0x2d, 0x54, 0x72, 0x25, 0x27, 0x88, 0x0f, 0x9a, 0x8a, 0xbb, 0x63, 0x4c, 0x2d, 0x22, 0x48, 0x94, - 0x95, 0x34, 0xf1, 0xf7, 0xc4, 0xb1, 0xee, 0x26, 0x8e, 0xf5, 0x6f, 0xe2, 0x58, 0x3f, 0xa6, 0xce, - 0xda, 0xdd, 0xd4, 0x59, 0xfb, 0x3b, 0x75, 0xd6, 0xa0, 0x46, 0xb9, 0x77, 0xff, 0x1b, 0xd1, 0xb2, - 0xbe, 0x7a, 0x7d, 0xaa, 0x06, 0xa3, 0xae, 0xd7, 0xe3, 0x91, 0x3f, 0x87, 0xea, 0x94, 0xe7, 0x22, - 0xff, 0x76, 0xf6, 0xfa, 0x74, 0x37, 0xd2, 0x17, 0xe4, 0xcd, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, - 0x02, 0x32, 0xd1, 0x78, 0x9b, 0x06, 0x00, 0x00, + // 535 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x95, 0xc1, 0x6e, 0xd3, 0x4c, + 0x10, 0xc7, 0xe3, 0xef, 0x0b, 0xa5, 0x99, 0x82, 0x04, 0x26, 0x8a, 0x12, 0xa0, 0x56, 0x65, 0x2e, + 0xbd, 0xd4, 0x56, 0x84, 0x10, 0x12, 0x9c, 0x1a, 0xda, 0x4a, 0x1c, 0x2a, 0x22, 0x97, 0x0a, 0x89, + 0x4b, 0xb4, 0xb1, 0x87, 0x64, 0xa9, 0xbd, 0xeb, 0xee, 0x6e, 0xd2, 0xf8, 0x2d, 0x78, 0x18, 0xde, + 0x80, 0x0b, 0xc7, 0x8a, 0x13, 0x47, 0x94, 0xbc, 0x08, 0xf2, 0xda, 0x4e, 0x1c, 0xa9, 0x4a, 0x7b, + 0x31, 0xb7, 0xfc, 0x27, 0xff, 0x99, 0xdf, 0xce, 0xec, 0xc8, 0x0b, 0x2f, 0x62, 0xc1, 0xa7, 0xc8, + 0x08, 0xf3, 0xd1, 0xc5, 0x99, 0x3f, 0x26, 0x6c, 0x84, 0xee, 0xb4, 0xeb, 0xe2, 0x14, 0x99, 0x92, + 0x4e, 0x2c, 0xb8, 0xe2, 0x66, 0x6b, 0x65, 0x72, 0x0a, 0x93, 0x33, 0xed, 0x3e, 0xed, 0xf8, 0x5c, + 0x46, 0x5c, 0x0e, 0xb4, 0xcb, 0xcd, 0x44, 0x96, 0x62, 0x9f, 0xc2, 0xe3, 0xe3, 0xb4, 0xc4, 0x07, + 0x11, 0xa0, 0x78, 0x27, 0x90, 0x28, 0x0c, 0xcc, 0x0e, 0x6c, 0xf3, 0x54, 0x0f, 0x68, 0xd0, 0x36, + 0xf6, 0x8c, 0xfd, 0xba, 0x77, 0x5f, 0xeb, 0xf7, 0x81, 0xb9, 0x0b, 0x90, 0xfd, 0xa5, 0x92, 0x18, + 0xdb, 0xff, 0xed, 0x19, 0xfb, 0x0d, 0xaf, 0xa1, 0x23, 0x1f, 0x93, 0x18, 0xed, 0x08, 0x9e, 0x94, + 0xca, 0xa5, 0x07, 0x09, 0xc3, 0xcd, 0x05, 0xdf, 0xc2, 0x03, 0xbf, 0xf0, 0x0d, 0x86, 0x49, 0x56, + 0xb2, 0xd7, 0xfe, 0xf5, 0xfd, 0xa0, 0x99, 0x1f, 0xf4, 0x30, 0x08, 0x04, 0x4a, 0x79, 0xa6, 0x04, + 0x65, 0x23, 0x6f, 0x67, 0xe9, 0xee, 0x25, 0x36, 0x87, 0x47, 0x2b, 0xdc, 0x09, 0xbd, 0x8d, 0xd5, + 0x82, 0x2d, 0x22, 0x25, 0x2a, 0x99, 0x1f, 0x3c, 0x57, 0x66, 0x13, 0xee, 0xc5, 0x82, 0xfa, 0xd8, + 0xfe, 0x5f, 0x87, 0x33, 0x61, 0x9a, 0x50, 0xff, 0x82, 0x28, 0xdb, 0x75, 0x1d, 0xd4, 0xbf, 0xed, + 0x19, 0x74, 0x56, 0xc0, 0x3e, 0x11, 0x8a, 0x92, 0x30, 0x4c, 0xfe, 0x05, 0xf9, 0x87, 0x91, 0x8f, + 0xf6, 0x94, 0x88, 0x0b, 0x54, 0x9f, 0xa8, 0x1a, 0x07, 0x82, 0x5c, 0x99, 0xcf, 0xa0, 0x11, 0xe9, + 0x48, 0x41, 0x7d, 0xe8, 0x6d, 0x67, 0x81, 0x1c, 0x1b, 0xf1, 0x09, 0x53, 0x4b, 0xac, 0x56, 0xe6, + 0x1b, 0xd8, 0x09, 0x50, 0x2a, 0xca, 0x88, 0xa2, 0x9c, 0x65, 0xf0, 0x4d, 0x33, 0x2f, 0x99, 0xd3, + 0x0b, 0xbb, 0xca, 0xe1, 0x2c, 0xbd, 0xb0, 0xfa, 0x6d, 0xc9, 0x4b, 0x77, 0x2f, 0xb1, 0x2f, 0xf3, + 0xf9, 0x65, 0x4d, 0x1c, 0xa1, 0x22, 0x34, 0x94, 0xe7, 0x71, 0xa0, 0xd7, 0x6e, 0x63, 0x2b, 0xaf, + 0x01, 0x26, 0x99, 0xef, 0x2e, 0x5b, 0xd2, 0xc8, 0xbd, 0xbd, 0xc4, 0xfe, 0x0a, 0x66, 0x09, 0x79, + 0xcc, 0xc8, 0x30, 0xac, 0x8c, 0x75, 0xb1, 0x76, 0x47, 0x47, 0x54, 0x56, 0x09, 0x53, 0xf0, 0xbc, + 0x04, 0x3b, 0x97, 0x28, 0xce, 0x50, 0xa9, 0x10, 0xab, 0x6d, 0x71, 0x02, 0xbb, 0x37, 0x52, 0x2b, + 0x6e, 0x76, 0x1d, 0xdb, 0x47, 0x11, 0x51, 0x29, 0x29, 0x67, 0x15, 0x2f, 0xcf, 0xfa, 0xbe, 0x7a, + 0x78, 0x79, 0xa8, 0x94, 0xa8, 0x16, 0xd9, 0x5d, 0xdb, 0xd7, 0xe2, 0x93, 0xbc, 0x89, 0x65, 0xbf, + 0x82, 0x56, 0x29, 0xe5, 0x04, 0xf1, 0x4e, 0x53, 0xb1, 0x9b, 0x39, 0xa9, 0x4f, 0x04, 0x89, 0x8a, + 0x94, 0x1e, 0xfe, 0x9c, 0x5b, 0xc6, 0xf5, 0xdc, 0x32, 0xfe, 0xcc, 0x2d, 0xe3, 0xdb, 0xc2, 0xaa, + 0x5d, 0x2f, 0xac, 0xda, 0xef, 0x85, 0x55, 0x83, 0x0e, 0xe5, 0xce, 0xcd, 0x2f, 0x4c, 0xdf, 0xf8, + 0xec, 0x8c, 0xa8, 0x1a, 0x4f, 0x86, 0x8e, 0xcf, 0x23, 0x77, 0x65, 0x3a, 0xa0, 0xbc, 0xa4, 0xdc, + 0xd9, 0xf2, 0xed, 0x1a, 0x6e, 0xe9, 0xf7, 0xe7, 0xe5, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x24, + 0x2e, 0x9d, 0xf5, 0xd9, 0x06, 0x00, 0x00, } func (m *EventOrderCreated) Marshal() (dAtA []byte, err error) { @@ -981,6 +1019,27 @@ func (m *EventOrderFilled) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Fees) > 0 { + i -= len(m.Fees) + copy(dAtA[i:], m.Fees) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Fees))) + i-- + dAtA[i] = 0x22 + } + if len(m.Price) > 0 { + i -= len(m.Price) + copy(dAtA[i:], m.Price) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Price))) + i-- + dAtA[i] = 0x1a + } + if len(m.Assets) > 0 { + i -= len(m.Assets) + copy(dAtA[i:], m.Assets) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Assets))) + i-- + dAtA[i] = 0x12 + } if m.OrderId != 0 { i = encodeVarintEvents(dAtA, i, uint64(m.OrderId)) i-- @@ -1009,17 +1068,24 @@ func (m *EventOrderPartiallyFilled) MarshalToSizedBuffer(dAtA []byte) (int, erro _ = i var l int _ = l - if len(m.FeesFilled) > 0 { - i -= len(m.FeesFilled) - copy(dAtA[i:], m.FeesFilled) - i = encodeVarintEvents(dAtA, i, uint64(len(m.FeesFilled))) + if len(m.Fees) > 0 { + i -= len(m.Fees) + copy(dAtA[i:], m.Fees) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Fees))) + i-- + dAtA[i] = 0x22 + } + if len(m.Price) > 0 { + i -= len(m.Price) + copy(dAtA[i:], m.Price) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Price))) i-- dAtA[i] = 0x1a } - if len(m.AssetsFilled) > 0 { - i -= len(m.AssetsFilled) - copy(dAtA[i:], m.AssetsFilled) - i = encodeVarintEvents(dAtA, i, uint64(len(m.AssetsFilled))) + if len(m.Assets) > 0 { + i -= len(m.Assets) + copy(dAtA[i:], m.Assets) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Assets))) i-- dAtA[i] = 0x12 } @@ -1456,6 +1522,18 @@ func (m *EventOrderFilled) Size() (n int) { if m.OrderId != 0 { n += 1 + sovEvents(uint64(m.OrderId)) } + l = len(m.Assets) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Price) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Fees) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } return n } @@ -1468,11 +1546,15 @@ func (m *EventOrderPartiallyFilled) Size() (n int) { if m.OrderId != 0 { n += 1 + sovEvents(uint64(m.OrderId)) } - l = len(m.AssetsFilled) + l = len(m.Assets) if l > 0 { n += 1 + l + sovEvents(uint64(l)) } - l = len(m.FeesFilled) + l = len(m.Price) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Fees) if l > 0 { n += 1 + l + sovEvents(uint64(l)) } @@ -1904,6 +1986,102 @@ func (m *EventOrderFilled) Unmarshal(dAtA []byte) error { break } } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Assets", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Assets = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Price", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Price = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Fees", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Fees = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -1975,7 +2153,7 @@ func (m *EventOrderPartiallyFilled) Unmarshal(dAtA []byte) error { } case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AssetsFilled", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Assets", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -2003,11 +2181,43 @@ func (m *EventOrderPartiallyFilled) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.AssetsFilled = string(dAtA[iNdEx:postIndex]) + m.Assets = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FeesFilled", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Price", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Price = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Fees", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -2035,7 +2245,7 @@ func (m *EventOrderPartiallyFilled) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.FeesFilled = string(dAtA[iNdEx:postIndex]) + m.Fees = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go index d7aeda1c72..6f9d178c9c 100644 --- a/x/exchange/events_test.go +++ b/x/exchange/events_test.go @@ -91,23 +91,163 @@ func TestNewEventOrderCancelled(t *testing.T) { } func TestNewEventOrderFilled(t *testing.T) { - orderID := uint64(5) + coinP := func(denom string, amount int64) *sdk.Coin { + rv := sdk.NewInt64Coin(denom, amount) + return &rv + } - event := NewEventOrderFilled(orderID) - assert.Equal(t, orderID, event.OrderId, "OrderId") - assertEverythingSet(t, event, "EventOrderFilled") + tests := []struct { + name string + order OrderI + expOrderID uint64 + expAssets string + expPrice string + expFees string + }{ + { + name: "ask", + order: NewOrder(4).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 22), + Price: sdk.NewInt64Coin("plum", 18), + SellerSettlementFlatFee: coinP("fig", 57), + }), + expOrderID: 4, + expAssets: "22apple", + expPrice: "18plum", + expFees: "57fig", + }, + { + name: "filled ask", + order: NewFilledOrder(NewOrder(4).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 22), + Price: sdk.NewInt64Coin("plum", 18), + SellerSettlementFlatFee: coinP("fig", 57), + }), sdk.NewInt64Coin("plum", 88), sdk.NewCoins(sdk.NewInt64Coin("fig", 61), sdk.NewInt64Coin("grape", 12))), + expOrderID: 4, + expAssets: "22apple", + expPrice: "88plum", + expFees: "61fig,12grape", + }, + { + name: "bid", + order: NewOrder(104).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 23), + Price: sdk.NewInt64Coin("plum", 19), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 58)), + }), + expOrderID: 104, + expAssets: "23apple", + expPrice: "19plum", + expFees: "58fig", + }, + { + name: "filled bid", + order: NewFilledOrder(NewOrder(104).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 23), + Price: sdk.NewInt64Coin("plum", 19), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 58)), + }), sdk.NewInt64Coin("plum", 89), sdk.NewCoins(sdk.NewInt64Coin("fig", 62), sdk.NewInt64Coin("grape", 13))), + expOrderID: 104, + expAssets: "23apple", + expPrice: "89plum", + expFees: "62fig,13grape", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var event *EventOrderFilled + testFunc := func() { + event = NewEventOrderFilled(tc.order) + } + require.NotPanics(t, testFunc, "NewEventOrderFilled") + assert.Equal(t, tc.expOrderID, event.OrderId, "OrderId") + assert.Equal(t, tc.expAssets, event.Assets, "Assets") + assert.Equal(t, tc.expPrice, event.Price, "Price") + assert.Equal(t, tc.expFees, event.Fees, "Fees") + assertEverythingSet(t, event, "EventOrderFilled") + }) + } } func TestNewEventOrderPartiallyFilled(t *testing.T) { - orderID := uint64(18) - assetsFilled := sdk.NewInt64Coin("acoin", 111) - feesFilled := sdk.NewInt64Coin("fcoin", 8) + coinP := func(denom string, amount int64) *sdk.Coin { + rv := sdk.NewInt64Coin(denom, amount) + return &rv + } - event := NewEventOrderPartiallyFilled(orderID, assetsFilled, feesFilled) - assert.Equal(t, orderID, event.OrderId, "OrderId") - assert.Equal(t, assetsFilled.String(), event.AssetsFilled, "AssetsFilled") - assert.Equal(t, feesFilled.String(), event.FeesFilled, "FeesFilled") - assertEverythingSet(t, event, "EventOrderPartiallyFilled") + tests := []struct { + name string + order OrderI + expOrderID uint64 + expAssets string + expPrice string + expFees string + }{ + { + name: "ask", + order: NewOrder(4).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 22), + Price: sdk.NewInt64Coin("plum", 18), + SellerSettlementFlatFee: coinP("fig", 57), + }), + expOrderID: 4, + expAssets: "22apple", + expPrice: "18plum", + expFees: "57fig", + }, + { + name: "filled ask", + order: NewFilledOrder(NewOrder(4).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 22), + Price: sdk.NewInt64Coin("plum", 18), + SellerSettlementFlatFee: coinP("fig", 57), + }), sdk.NewInt64Coin("plum", 88), sdk.NewCoins(sdk.NewInt64Coin("fig", 61), sdk.NewInt64Coin("grape", 12))), + expOrderID: 4, + expAssets: "22apple", + expPrice: "88plum", + expFees: "61fig,12grape", + }, + { + name: "bid", + order: NewOrder(104).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 23), + Price: sdk.NewInt64Coin("plum", 19), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 58)), + }), + expOrderID: 104, + expAssets: "23apple", + expPrice: "19plum", + expFees: "58fig", + }, + { + name: "filled bid", + order: NewFilledOrder(NewOrder(104).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 23), + Price: sdk.NewInt64Coin("plum", 19), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 58)), + }), sdk.NewInt64Coin("plum", 89), sdk.NewCoins(sdk.NewInt64Coin("fig", 62), sdk.NewInt64Coin("grape", 13))), + expOrderID: 104, + expAssets: "23apple", + expPrice: "89plum", + expFees: "62fig,13grape", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var event *EventOrderPartiallyFilled + testFunc := func() { + event = NewEventOrderPartiallyFilled(tc.order) + } + require.NotPanics(t, testFunc, "NewEventOrderFilled") + assert.Equal(t, tc.expOrderID, event.OrderId, "OrderId") + assert.Equal(t, tc.expAssets, event.Assets, "Assets") + assert.Equal(t, tc.expPrice, event.Price, "Price") + assert.Equal(t, tc.expFees, event.Fees, "Fees") + assertEverythingSet(t, event, "EventOrderPartiallyFilled") + }) + } } func TestNewEventMarketWithdraw(t *testing.T) { @@ -299,9 +439,11 @@ func TestTypedEventToEvent(t *testing.T) { updatedByQ := quoteBz(updatedBy.String()) coins1 := sdk.NewCoins(sdk.NewInt64Coin("onecoin", 1), sdk.NewInt64Coin("twocoin", 2)) coins1Q := quoteBz(coins1.String()) - acoin := sdk.NewInt64Coin("acoin", 5) + acoin := sdk.NewInt64Coin("acoin", 55) acoinQ := quoteBz(acoin.String()) - fcoin := sdk.NewInt64Coin("fcoin", 5) + pcoin := sdk.NewInt64Coin("pcoin", 66) + pcoinQ := quoteBz(pcoin.String()) + fcoin := sdk.NewInt64Coin("fcoin", 33) fcoinQ := quoteBz(fcoin.String()) tests := []struct { @@ -343,24 +485,70 @@ func TestTypedEventToEvent(t *testing.T) { }, }, { - name: "EventOrderFilled", - tev: NewEventOrderFilled(4), + name: "EventOrderFilled ask", + tev: NewEventOrderFilled(NewOrder(4).WithAsk(&AskOrder{ + Assets: acoin, + Price: pcoin, + SellerSettlementFlatFee: &fcoin, + })), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderFilled", Attributes: []abci.EventAttribute{ + {Key: []byte("assets"), Value: acoinQ}, + {Key: []byte("fees"), Value: fcoinQ}, {Key: []byte("order_id"), Value: quoteBz("4")}, + {Key: []byte("price"), Value: pcoinQ}, + }, + }, + }, + { + name: "EventOrderFilled bid", + tev: NewEventOrderFilled(NewOrder(104).WithBid(&BidOrder{ + Assets: acoin, + Price: pcoin, + BuyerSettlementFees: sdk.Coins{fcoin}, + })), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventOrderFilled", + Attributes: []abci.EventAttribute{ + {Key: []byte("assets"), Value: acoinQ}, + {Key: []byte("fees"), Value: fcoinQ}, + {Key: []byte("order_id"), Value: quoteBz("104")}, + {Key: []byte("price"), Value: pcoinQ}, + }, + }, + }, + { + name: "EventOrderPartiallyFilled ask", + tev: NewEventOrderPartiallyFilled(NewOrder(5).WithAsk(&AskOrder{ + Assets: acoin, + Price: pcoin, + SellerSettlementFlatFee: &fcoin, + })), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventOrderPartiallyFilled", + Attributes: []abci.EventAttribute{ + {Key: []byte("assets"), Value: acoinQ}, + {Key: []byte("fees"), Value: fcoinQ}, + {Key: []byte("order_id"), Value: quoteBz("5")}, + {Key: []byte("price"), Value: pcoinQ}, }, }, }, { - name: "EventOrderPartiallyFilled", - tev: NewEventOrderPartiallyFilled(5, acoin, fcoin), + name: "EventOrderPartiallyFilled bid", + tev: NewEventOrderPartiallyFilled(NewOrder(5).WithBid(&BidOrder{ + Assets: acoin, + Price: pcoin, + BuyerSettlementFees: sdk.Coins{fcoin}, + })), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderPartiallyFilled", Attributes: []abci.EventAttribute{ - {Key: []byte("assets_filled"), Value: acoinQ}, - {Key: []byte("fees_filled"), Value: fcoinQ}, + {Key: []byte("assets"), Value: acoinQ}, + {Key: []byte("fees"), Value: fcoinQ}, {Key: []byte("order_id"), Value: quoteBz("5")}, + {Key: []byte("price"), Value: pcoinQ}, }, }, }, From 622979c225fed08b5a91a64780316618e584f247 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 6 Oct 2023 13:34:30 -0600 Subject: [PATCH 249/309] [1658]: Create a FilledOrder struct to represent what's been filled in an order. Use those in the Settlement and for the events that are emitted. Populate the filled order and partially filled order in the settlement at the end of it all. --- x/exchange/fulfillment.go | 127 +++++++++++++----------- x/exchange/fulfillment_test.go | 156 ++++++++++++++--------------- x/exchange/keeper/fulfillment.go | 30 +++--- x/exchange/keeper/orders.go | 8 +- x/exchange/orders.go | 116 ++++++++++++++++++++-- x/exchange/orders_test.go | 162 +++++++++++++++++++++++++++++++ 6 files changed, 435 insertions(+), 164 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 5eff4c14e7..7a55e02729 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -255,17 +255,22 @@ func DistributePrice(of1, of2 *OrderFulfillment, amount sdkmath.Int) error { } // SplitOrder splits this order on the amount of assets filled. -// This order fulfillment is updated to have the filled order. -func (f *OrderFulfillment) SplitOrder() (filled *Order, unfilled *Order, err error) { - filled, unfilled, err = f.Order.Split(f.AssetsFilledAmt) +// This order fulfillment is updated to have the filled order, and the unfilled portion is returned. +func (f *OrderFulfillment) SplitOrder() (*Order, error) { + filled, unfilled, err := f.Order.Split(f.AssetsFilledAmt) if err != nil { - return nil, nil, err + return nil, err } f.Order = filled f.AssetsUnfilledAmt = sdkmath.ZeroInt() f.PriceLeftAmt = filled.GetPrice().Amount.Sub(f.PriceAppliedAmt) - return filled, unfilled, nil + return unfilled, nil +} + +// AsFilledOrder creates a FilledOrder from this order fulfillment. +func (f *OrderFulfillment) AsFilledOrder() *FilledOrder { + return NewFilledOrder(f.Order, f.GetPriceApplied(), f.FeesToPay) } // sumAssetsAndPrice gets the sum of assets, and the sum of prices of the provided orders. @@ -293,9 +298,11 @@ type Settlement struct { // FeeInputs are the inputs needed to facilitate payment of order fees. FeeInputs []banktypes.Input // FullyFilledOrders are all the orders that are fully filled in this settlement. - FullyFilledOrders []*Order + // If there is an order that's being partially filled, it will not be included in this list. + FullyFilledOrders []*FilledOrder // PartialOrderFilled is a partially filled order with amounts indicating how much was filled. - PartialOrderFilled *Order + // This is not included in FullyFilledOrders. + PartialOrderFilled *FilledOrder // PartialOrderLeft is what's left of the partially filled order. PartialOrderLeft *Order } @@ -314,9 +321,9 @@ func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatioLookup func(de return nil, err } + settlement := &Settlement{} // Identify any partial order and update its entry in the order fulfillments. - settlement, err := splitPartial(askOFs, bidOFs) - if err != nil { + if err := splitPartial(askOFs, bidOFs, settlement); err != nil { return nil, err } @@ -329,29 +336,32 @@ func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatioLookup func(de } // Allocate the prices. - if err = allocatePrice(askOFs, bidOFs); err != nil { + if err := allocatePrice(askOFs, bidOFs); err != nil { return nil, err } + // Set the fees in the fulfillments sellerFeeRatio, err := sellerFeeRatioLookup(askOFs[0].GetPrice().Denom) if err != nil { return nil, err } - - // Set the fees in the fullfillments if err = setFeesToPay(askOFs, bidOFs, sellerFeeRatio); err != nil { return nil, err } + // Make sure everything adds up. if err = validateFulfillments(askOFs, bidOFs); err != nil { return nil, err } - err = buildTransfers(askOFs, bidOFs, settlement) - if err != nil { + // Create the transfers + if err = buildTransfers(askOFs, bidOFs, settlement); err != nil { return nil, err } + // Indicate what's been filled in full and partially. + populateFilled(askOFs, bidOFs, settlement) + return settlement, nil } @@ -448,57 +458,41 @@ func allocateAssets(askOFs, bidOFs []*OrderFulfillment) error { } // splitPartial checks the provided fulfillments for a partial order and splits it out, updating the applicable fulfillment. -func splitPartial(askOFs, bidOFs []*OrderFulfillment) (*Settlement, error) { - rv := &Settlement{} - - lastAskI := len(askOFs) - 1 - for i, askOF := range askOFs { - if askOF.AssetsFilledAmt.IsZero() { - return nil, fmt.Errorf("%s order %d (at index %d) has no assets filled", - askOF.GetOrderType(), askOF.GetOrderID(), i) - } - if !askOF.AssetsUnfilledAmt.IsZero() { - if i != lastAskI { - return nil, fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last ask order provided", - askOF.GetOrderType(), askOF.GetOrderID(), i) - } - var err error - rv.PartialOrderFilled, rv.PartialOrderLeft, err = askOF.SplitOrder() - if err != nil { - return nil, err - } - } else { - rv.FullyFilledOrders = append(rv.FullyFilledOrders, askOF.Order) - } +// This will possibly populate the PartialOrderLeft in the provided Settlement. +func splitPartial(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) error { + if err := splitOrderFulfillments(askOFs, settlement); err != nil { + return err } + return splitOrderFulfillments(bidOFs, settlement) +} - lastBidI := len(bidOFs) - 1 - for i, bidOF := range bidOFs { - if bidOF.AssetsFilledAmt.IsZero() { - return nil, fmt.Errorf("%s order %d (at index %d) has no assets filled", - bidOF.GetOrderType(), bidOF.GetOrderID(), i) +// splitOrderFulfillments checks each of the OrderFulfillment for partial (or incomplete) fills. +// If an appropriate partial fill is found, its OrderFulfillment is update and Settlement.PartialOrderLeft is set. +func splitOrderFulfillments(fulfillments []*OrderFulfillment, settlement *Settlement) error { + lastI := len(fulfillments) - 1 + for i, f := range fulfillments { + if f.AssetsFilledAmt.IsZero() { + return fmt.Errorf("%s order %d (at index %d) has no assets filled", + f.GetOrderType(), f.GetOrderID(), i) } - if !bidOF.AssetsUnfilledAmt.IsZero() { - if i != lastBidI { - return nil, fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last bid order provided", - bidOF.GetOrderType(), bidOF.GetOrderID(), i) + if !f.AssetsUnfilledAmt.IsZero() { + if i != lastI { + return fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last %s order provided", + f.GetOrderType(), f.GetOrderID(), i, f.GetOrderType()) } - if rv.PartialOrderFilled != nil { - return nil, fmt.Errorf("%s order %d and %s order %d cannot both be partially filled", - rv.PartialOrderFilled.GetOrderType(), rv.PartialOrderFilled.GetOrderID(), - bidOF.GetOrderType(), bidOF.GetOrderID()) + if settlement.PartialOrderLeft != nil { + return fmt.Errorf("%s order %d and %s order %d cannot both be partially filled", + settlement.PartialOrderLeft.GetOrderType(), settlement.PartialOrderLeft.GetOrderID(), + f.GetOrderType(), f.GetOrderID()) } var err error - rv.PartialOrderFilled, rv.PartialOrderLeft, err = bidOF.SplitOrder() + settlement.PartialOrderLeft, err = f.SplitOrder() if err != nil { - return nil, err + return err } - } else { - rv.FullyFilledOrders = append(rv.FullyFilledOrders, bidOF.Order) } } - - return rv, nil + return nil } // allocatePrice distributes the prices among the fulfillments. @@ -650,6 +644,7 @@ func validateFulfillments(askOFs, bidOFs []*OrderFulfillment) error { // buildTransfers creates the transfers, inputs for fee payments, // and fee total and sets those fields in the provided Settlement. +// This will populate the Transfers and FeeInputs fields in the provided Settlement. func buildTransfers(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) error { var errs []error indexedFees := newIndexedAddrAmts() @@ -699,6 +694,28 @@ func buildTransfers(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) return nil } +// populateFilled creates all the FilledOrder entries and stores them in the provided Settlement. +// This will populate the FullyFilledOrders and PartialOrderFilled fields in the provided Settlement. +func populateFilled(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) { + settlement.FullyFilledOrders = make([]*FilledOrder, 0, len(askOFs)+len(bidOFs)) + + for _, f := range askOFs { + if settlement.PartialOrderLeft != nil && settlement.PartialOrderLeft.GetOrderID() == f.GetOrderID() { + settlement.PartialOrderFilled = f.AsFilledOrder() + } else { + settlement.FullyFilledOrders = append(settlement.FullyFilledOrders, f.AsFilledOrder()) + } + } + + for _, f := range bidOFs { + if settlement.PartialOrderLeft != nil && settlement.PartialOrderLeft.GetOrderID() == f.GetOrderID() { + settlement.PartialOrderFilled = f.AsFilledOrder() + } else { + settlement.FullyFilledOrders = append(settlement.FullyFilledOrders, f.AsFilledOrder()) + } + } +} + // getAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. func getAssetTransfer(f *OrderFulfillment) (*Transfer, error) { assetsFilled := f.GetAssetsFilled() diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 031d3852f8..b719d7dc8c 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -2117,7 +2117,6 @@ func TestOrderFulfillment_SplitOrder(t *testing.T) { tests := []struct { name string receiver *OrderFulfillment - expFilled *Order expUnfilled *Order expReceiver *OrderFulfillment expErr string @@ -2147,7 +2146,6 @@ func TestOrderFulfillment_SplitOrder(t *testing.T) { PriceAppliedAmt: sdkmath.NewInt(300), PriceLeftAmt: sdkmath.NewInt(-200), }, - expFilled: askOrder(17, 9, 90, coin(18, "fig")), expUnfilled: askOrder(17, 1, 10, coin(2, "fig")), expReceiver: &OrderFulfillment{ Order: askOrder(17, 9, 90, coin(18, "fig")), @@ -2166,7 +2164,6 @@ func TestOrderFulfillment_SplitOrder(t *testing.T) { PriceAppliedAmt: sdkmath.NewInt(300), PriceLeftAmt: sdkmath.NewInt(-200), }, - expFilled: bidOrder(19, 9, 90, coin(18, "fig")), expUnfilled: bidOrder(19, 1, 10, coin(2, "fig")), expReceiver: &OrderFulfillment{ Order: bidOrder(19, 9, 90, coin(18, "fig")), @@ -2185,14 +2182,13 @@ func TestOrderFulfillment_SplitOrder(t *testing.T) { tc.expReceiver = copyOrderFulfillment(tc.receiver) } - var filled, unfilled *Order + var unfilled *Order var err error testFunc := func() { - filled, unfilled, err = tc.receiver.SplitOrder() + unfilled, err = tc.receiver.SplitOrder() } require.NotPanics(t, testFunc, "SplitOrder") assertions.AssertErrorValue(t, err, tc.expErr, "SplitOrder error") - assert.Equalf(t, tc.expFilled, filled, "SplitOrder filled order") assert.Equalf(t, tc.expUnfilled, unfilled, "SplitOrder unfilled order") if !assertEqualOrderFulfillments(t, tc.expReceiver, tc.receiver, "OrderFulfillment after SplitOrder") { t.Logf("Original: %s", orderFulfillmentString(orig)) @@ -2201,6 +2197,11 @@ func TestOrderFulfillment_SplitOrder(t *testing.T) { } } +func TestOrderFulfillment_AsFilledOrder(t *testing.T) { + // TODO[1658]: func TestAsFilledOrder(t *testing.T) + t.Skipf("not written") +} + func TestSumAssetsAndPrice(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} @@ -2859,25 +2860,30 @@ func TestSplitPartial(t *testing.T) { name string askOFs []*OrderFulfillment bidOFs []*OrderFulfillment + settlement *Settlement expAskOFs []*OrderFulfillment expBidOfs []*OrderFulfillment expSettlement *Settlement expErr string }{ { - name: "one ask: not touched", - askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"))}, - expErr: "ask order 8 (at index 0) has no assets filled", + name: "one ask: not touched", + askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"))}, + settlement: &Settlement{}, + expErr: "ask order 8 (at index 0) has no assets filled", }, { - name: "one ask: partial", - askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer", 7))}, - expAskOFs: []*OrderFulfillment{newOF(askOrder(8, 7, "seller8"), dist("buyer", 7))}, - expSettlement: &Settlement{ - FullyFilledOrders: nil, - PartialOrderFilled: askOrder(8, 7, "seller8"), - PartialOrderLeft: askOrder(8, 3, "seller8"), - }, + name: "one ask: partial", + askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer", 7))}, + settlement: &Settlement{}, + expAskOFs: []*OrderFulfillment{newOF(askOrder(8, 7, "seller8"), dist("buyer", 7))}, + expSettlement: &Settlement{PartialOrderLeft: askOrder(8, 3, "seller8")}, + }, + { + name: "one ask: partial, settlement already has a partial", + askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer", 7))}, + settlement: &Settlement{PartialOrderLeft: bidOrder(55, 3, "buyer")}, + expErr: "bid order 55 and ask order 8 cannot both be partially filled", }, { name: "one ask: partial, not allowed", @@ -2888,7 +2894,8 @@ func TestSplitPartial(t *testing.T) { Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(10)}, AllowPartial: false, }), dist("buyer", 7))}, - expErr: "cannot split ask order 8 having assets \"10apple\" at \"7apple\": order does not allow partial fulfillment", + settlement: &Settlement{}, + expErr: "cannot split ask order 8 having assets \"10apple\" at \"7apple\": order does not allow partial fulfillment", }, { name: "two asks: first partial", @@ -2896,7 +2903,8 @@ func TestSplitPartial(t *testing.T) { newOF(askOrder(8, 10, "seller8"), dist("buyer", 7)), newOF(askOrder(9, 12, "seller8")), }, - expErr: "ask order 8 (at index 0) is not filled in full and is not the last ask order provided", + settlement: &Settlement{}, + expErr: "ask order 8 (at index 0) is not filled in full and is not the last ask order provided", }, { name: "two asks: last untouched", @@ -2904,7 +2912,8 @@ func TestSplitPartial(t *testing.T) { newOF(askOrder(8, 10, "seller8"), dist("buyer", 10)), newOF(askOrder(9, 12, "seller8")), }, - expErr: "ask order 9 (at index 1) has no assets filled", + settlement: &Settlement{}, + expErr: "ask order 9 (at index 1) has no assets filled", }, { name: "two asks: last partial", @@ -2912,31 +2921,26 @@ func TestSplitPartial(t *testing.T) { newOF(askOrder(8, 10, "seller8"), dist("buyer", 10)), newOF(askOrder(9, 12, "seller9"), dist("buyer", 10)), }, + settlement: &Settlement{}, expAskOFs: []*OrderFulfillment{ newOF(askOrder(8, 10, "seller8"), dist("buyer", 10)), newOF(askOrder(9, 10, "seller9"), dist("buyer", 10)), }, - expSettlement: &Settlement{ - FullyFilledOrders: []*Order{askOrder(8, 10, "seller8")}, - PartialOrderFilled: askOrder(9, 10, "seller9"), - PartialOrderLeft: askOrder(9, 2, "seller9"), - }, + expSettlement: &Settlement{PartialOrderLeft: askOrder(9, 2, "seller9")}, }, { - name: "one bid: not touched", - bidOFs: []*OrderFulfillment{newOF(bidOrder(8, 10, "buyer8"))}, - expErr: "bid order 8 (at index 0) has no assets filled", + name: "one bid: not touched", + bidOFs: []*OrderFulfillment{newOF(bidOrder(8, 10, "buyer8"))}, + settlement: &Settlement{}, + expErr: "bid order 8 (at index 0) has no assets filled", }, { - name: "one bid: partial", - bidOFs: []*OrderFulfillment{newOF(bidOrder(8, 10, "buyer8"), dist("seller", 7))}, - expBidOfs: []*OrderFulfillment{newOF(bidOrder(8, 7, "buyer8"), dist("seller", 7))}, - expSettlement: &Settlement{ - FullyFilledOrders: nil, - PartialOrderFilled: bidOrder(8, 7, "buyer8"), - PartialOrderLeft: bidOrder(8, 3, "buyer8"), - }, + name: "one bid: partial", + bidOFs: []*OrderFulfillment{newOF(bidOrder(8, 10, "buyer8"), dist("seller", 7))}, + settlement: &Settlement{}, + expBidOfs: []*OrderFulfillment{newOF(bidOrder(8, 7, "buyer8"), dist("seller", 7))}, + expSettlement: &Settlement{PartialOrderLeft: bidOrder(8, 3, "buyer8")}, }, { name: "one bid: partial, not allowed", @@ -2947,7 +2951,8 @@ func TestSplitPartial(t *testing.T) { Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(10)}, AllowPartial: false, }), dist("seller", 7))}, - expErr: "cannot split bid order 8 having assets \"10apple\" at \"7apple\": order does not allow partial fulfillment", + settlement: &Settlement{}, + expErr: "cannot split bid order 8 having assets \"10apple\" at \"7apple\": order does not allow partial fulfillment", }, { name: "two bids: first partial", @@ -2955,7 +2960,8 @@ func TestSplitPartial(t *testing.T) { newOF(bidOrder(8, 10, "buyer8"), dist("seller", 7)), newOF(bidOrder(9, 12, "buyer9")), }, - expErr: "bid order 8 (at index 0) is not filled in full and is not the last bid order provided", + settlement: &Settlement{}, + expErr: "bid order 8 (at index 0) is not filled in full and is not the last bid order provided", }, { name: "two bids: last untouched", @@ -2963,7 +2969,8 @@ func TestSplitPartial(t *testing.T) { newOF(bidOrder(8, 10, "buyer8"), dist("seller", 10)), newOF(bidOrder(9, 12, "buyer9")), }, - expErr: "bid order 9 (at index 1) has no assets filled", + settlement: &Settlement{}, + expErr: "bid order 9 (at index 1) has no assets filled", }, { name: "two bids: last partial", @@ -2971,21 +2978,19 @@ func TestSplitPartial(t *testing.T) { newOF(bidOrder(8, 10, "buyer8"), dist("seller", 10)), newOF(bidOrder(9, 12, "buyer9"), dist("seller", 10)), }, + settlement: &Settlement{}, expBidOfs: []*OrderFulfillment{ newOF(bidOrder(8, 10, "buyer8"), dist("seller", 10)), newOF(bidOrder(9, 10, "buyer9"), dist("seller", 10)), }, - expSettlement: &Settlement{ - FullyFilledOrders: []*Order{bidOrder(8, 10, "buyer8")}, - PartialOrderFilled: bidOrder(9, 10, "buyer9"), - PartialOrderLeft: bidOrder(9, 2, "buyer9"), - }, + expSettlement: &Settlement{PartialOrderLeft: bidOrder(9, 2, "buyer9")}, }, { - name: "one ask, one bid: both partial", - askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer9", 7))}, - bidOFs: []*OrderFulfillment{newOF(bidOrder(9, 10, "buyer9"), dist("seller8", 7))}, - expErr: "ask order 8 and bid order 9 cannot both be partially filled", + name: "one ask, one bid: both partial", + askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer9", 7))}, + bidOFs: []*OrderFulfillment{newOF(bidOrder(9, 10, "buyer9"), dist("seller8", 7))}, + settlement: &Settlement{}, + expErr: "ask order 8 and bid order 9 cannot both be partially filled", }, { name: "three asks, three bids: no partial", @@ -2999,6 +3004,7 @@ func TestSplitPartial(t *testing.T) { newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), }, + settlement: &Settlement{}, expAskOFs: []*OrderFulfillment{ newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), @@ -3009,18 +3015,7 @@ func TestSplitPartial(t *testing.T) { newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), }, - expSettlement: &Settlement{ - FullyFilledOrders: []*Order{ - askOrder(51, 10, "seller51"), - askOrder(77, 15, "seller77"), - askOrder(12, 20, "seller12"), - bidOrder(99, 20, "buyer99"), - bidOrder(8, 15, "buyer8"), - bidOrder(112, 10, "buyer112"), - }, - PartialOrderFilled: nil, - PartialOrderLeft: nil, - }, + expSettlement: &Settlement{PartialOrderLeft: nil}, }, { name: "three asks, three bids: partial ask", @@ -3034,6 +3029,7 @@ func TestSplitPartial(t *testing.T) { newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), }, + settlement: &Settlement{}, expAskOFs: []*OrderFulfillment{ newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), @@ -3044,17 +3040,7 @@ func TestSplitPartial(t *testing.T) { newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), }, - expSettlement: &Settlement{ - FullyFilledOrders: []*Order{ - askOrder(51, 10, "seller51"), - askOrder(77, 15, "seller77"), - bidOrder(99, 20, "buyer99"), - bidOrder(8, 15, "buyer8"), - bidOrder(112, 10, "buyer112"), - }, - PartialOrderFilled: askOrder(12, 20, "seller12"), - PartialOrderLeft: askOrder(12, 1, "seller12"), - }, + expSettlement: &Settlement{PartialOrderLeft: askOrder(12, 1, "seller12")}, }, { name: "three asks, three bids: no partial", @@ -3068,6 +3054,7 @@ func TestSplitPartial(t *testing.T) { newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), newOF(bidOrder(112, 11, "buyer112"), dist("seller12", 10)), }, + settlement: &Settlement{}, expAskOFs: []*OrderFulfillment{ newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), @@ -3078,17 +3065,7 @@ func TestSplitPartial(t *testing.T) { newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), }, - expSettlement: &Settlement{ - FullyFilledOrders: []*Order{ - askOrder(51, 10, "seller51"), - askOrder(77, 15, "seller77"), - askOrder(12, 20, "seller12"), - bidOrder(99, 20, "buyer99"), - bidOrder(8, 15, "buyer8"), - }, - PartialOrderFilled: bidOrder(112, 10, "buyer112"), - PartialOrderLeft: bidOrder(112, 1, "buyer112"), - }, + expSettlement: &Settlement{PartialOrderLeft: bidOrder(112, 1, "buyer112")}, }, } @@ -3097,14 +3074,12 @@ func TestSplitPartial(t *testing.T) { origAskOFs := copyOrderFulfillments(tc.askOFs) origBidOFs := copyOrderFulfillments(tc.bidOFs) - var settlement *Settlement var err error testFunc := func() { - settlement, err = splitPartial(tc.askOFs, tc.bidOFs) + err = splitPartial(tc.askOFs, tc.bidOFs, tc.settlement) } require.NotPanics(t, testFunc, "splitPartial") assertions.AssertErrorValue(t, err, tc.expErr, "splitPartial error") - assert.Equalf(t, tc.expSettlement, settlement, "splitPartial settlement") if len(tc.expErr) > 0 { return } @@ -3114,10 +3089,16 @@ func TestSplitPartial(t *testing.T) { if !assertEqualOrderFulfillmentSlices(t, tc.expBidOfs, tc.bidOFs, "bidOFs after splitPartial") { t.Logf("Original: %s", orderFulfillmentsString(origBidOFs)) } + assert.Equalf(t, tc.expSettlement, tc.settlement, "settlement after splitPartial") }) } } +func TestSplitOrderFulfillments(t *testing.T) { + // TODO[1658]: func TestSplitOrderFulfillments(t *testing.T) + t.Skipf("not written") +} + func TestAllocatePrice(t *testing.T) { // TODO[1658]: func TestAllocatePrice(t *testing.T) t.Skipf("not written") @@ -3138,6 +3119,11 @@ func TestBuildTransfers(t *testing.T) { t.Skipf("not written") } +func TestPopulateFilled(t *testing.T) { + // TODO[1658]: func TestPopulateFilled(t *testing.T) + t.Skipf("not written") +} + func TestGetAssetTransfer(t *testing.T) { seller, buyer := "sally", "brandon" assetDenom, priceDenom := "apple", "peach" diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index 9828426560..434b2b1766 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -65,6 +65,7 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro addrIndex := make(map[string]int) feeInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)+1) feeAddrIndex := make(map[string]int) + filledOrders := make([]*exchange.FilledOrder, 0, len(msg.BidOrderIds)) for _, order := range orders { bidOrder := order.GetBidOrder() buyer := bidOrder.Buyer @@ -106,6 +107,8 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro } feeInputs[j].Coins = feeInputs[j].Coins.Add(buyerSettlementFees...) } + + filledOrders = append(filledOrders, exchange.NewFilledOrder(order, price, buyerSettlementFees)) } if len(errs) > 0 { @@ -149,9 +152,9 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro } events := make([]proto.Message, len(orders)) - for i, order := range orders { - deleteAndDeIndexOrder(store, *order) - events[i] = exchange.NewEventOrderFilled(order.OrderId) + for i, order := range filledOrders { + deleteAndDeIndexOrder(store, *order.GetOriginalOrder()) + events[i] = exchange.NewEventOrderFilled(order) } k.emitEvents(ctx, events) @@ -192,6 +195,7 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro addrIndex := make(map[string]int) feeInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)+1) feeAddrIndex := make(map[string]int) + filledOrders := make([]*exchange.FilledOrder, 0, len(msg.AskOrderIds)) for _, order := range orders { askOrder := order.GetAskOrder() seller := askOrder.Seller @@ -237,6 +241,8 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro } feeInputs[j].Coins = feeInputs[j].Coins.Add(totalSellerFee...) } + + filledOrders = append(filledOrders, exchange.NewFilledOrder(order, price, totalSellerFee)) } if len(errs) > 0 { @@ -277,9 +283,9 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro } events := make([]proto.Message, len(orders)) - for i, order := range orders { - deleteAndDeIndexOrder(store, *order) - events[i] = exchange.NewEventOrderFilled(order.OrderId) + for i, order := range filledOrders { + deleteAndDeIndexOrder(store, *order.GetOriginalOrder()) + events[i] = exchange.NewEventOrderFilled(order) } k.emitEvents(ctx, events) @@ -343,6 +349,10 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO return err } + // Delete all the fully filled orders. + for _, order := range settlement.FullyFilledOrders { + deleteAndDeIndexOrder(store, *order.GetOriginalOrder()) + } // Update the partial order if there was one. if settlement.PartialOrderLeft != nil { if err = k.setOrderInStore(store, *settlement.PartialOrderLeft); err != nil { @@ -354,14 +364,10 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO // And emit all the needed events. events := make([]proto.Message, 0, len(askOrders)+len(bidOrders)) for _, order := range settlement.FullyFilledOrders { - events = append(events, exchange.NewEventOrderFilled(order.OrderId)) + events = append(events, exchange.NewEventOrderFilled(order)) } if settlement.PartialOrderFilled != nil { - events = append(events, exchange.NewEventOrderPartiallyFilled( - settlement.PartialOrderFilled.OrderId, - settlement.PartialOrderFilled.GetAssets(), - settlement.PartialOrderFilled.GetPrice(), - )) + events = append(events, exchange.NewEventOrderPartiallyFilled(settlement.PartialOrderFilled)) } k.emitEvents(ctx, events) diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index 3197ca8a8b..a85ed3b7e6 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -524,8 +524,8 @@ func (k Keeper) getBidOrders(store sdk.KVStore, marketID uint32, orderIDs []uint } // placeHoldOnOrder places a hold on an order's funds in the owner's account. -func (k Keeper) placeHoldOnOrder(ctx sdk.Context, order *exchange.Order) error { - orderID := order.OrderId +func (k Keeper) placeHoldOnOrder(ctx sdk.Context, order exchange.OrderI) error { + orderID := order.GetOrderID() orderType := order.GetOrderType() owner := order.GetOwner() ownerAddr, err := sdk.AccAddressFromBech32(owner) @@ -541,8 +541,8 @@ func (k Keeper) placeHoldOnOrder(ctx sdk.Context, order *exchange.Order) error { } // releaseHoldOnOrder releases a hold that was placed on an order's funds in the owner's account. -func (k Keeper) releaseHoldOnOrder(ctx sdk.Context, order *exchange.Order) error { - orderID := order.OrderId +func (k Keeper) releaseHoldOnOrder(ctx sdk.Context, order exchange.OrderI) error { + orderID := order.GetOrderID() orderType := order.GetOrderType() owner := order.GetOwner() ownerAddr, err := sdk.AccAddressFromBech32(owner) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 0ff7c2ee18..97fb8e3d84 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -46,9 +46,7 @@ type OrderI interface { IsBidOrder() bool } -var ( - _ OrderI = (*Order)(nil) -) +var _ OrderI = (*Order)(nil) // findDuplicateIds returns all order ids that appear two or more times in the provided slice. func findDuplicateIds(orderIDs []uint64) []uint64 { @@ -110,6 +108,11 @@ func (o *Order) WithBid(bidOrder *BidOrder) *Order { return o } +// GetOrderID gets the numerical identifier for this order. +func (o Order) GetOrderID() uint64 { + return o.OrderId +} + // IsAskOrder returns true if this order is an ask order. func (o Order) IsAskOrder() bool { return o.GetAskOrder() != nil @@ -120,11 +123,6 @@ func (o Order) IsBidOrder() bool { return o.GetBidOrder() != nil } -// GetOrderID gets the numerical identifier for this order. -func (o Order) GetOrderID() uint64 { - return o.OrderId -} - // GetSubOrder gets this order's sub-order as a SubOrderI. func (o Order) GetSubOrder() (SubOrderI, error) { switch v := o.GetOrder().(type) { @@ -501,3 +499,105 @@ func (b BidOrder) CopyChange(newAssets, newPrice sdk.Coin, newFees sdk.Coins) *B AllowPartial: b.AllowPartial, } } + +// FilledOrder holds an order that has been filled (either in full or partially). +// The price and fees will indicate the amounts actually involved in the fulfillment. +type FilledOrder struct { + order *Order + actualPrice sdk.Coin + actualFees sdk.Coins +} + +var _ OrderI = (*FilledOrder)(nil) + +func NewFilledOrder(order *Order, actualPrice sdk.Coin, actualFees sdk.Coins) *FilledOrder { + return &FilledOrder{ + order: order, + actualPrice: actualPrice, + actualFees: actualFees, + } +} + +// GetOriginalOrder gets the original order that this filled order represents. +func (o FilledOrder) GetOriginalOrder() *Order { + return o.order +} + +// GetOrderID gets the numerical identifier for this order. +func (o FilledOrder) GetOrderID() uint64 { + return o.order.GetOrderID() +} + +// IsAskOrder returns true if this order is an ask order. +func (o FilledOrder) IsAskOrder() bool { + return o.order.IsAskOrder() +} + +// IsBidOrder returns true if this order is a bid order. +func (o FilledOrder) IsBidOrder() bool { + return o.order.IsBidOrder() +} + +// GetMarketID returns the market id for this order. +func (o FilledOrder) GetMarketID() uint32 { + return o.order.GetMarketID() +} + +// GetOwner returns the owner of this order. +// E.g. the seller for ask orders, or buyer for bid orders. +func (o FilledOrder) GetOwner() string { + return o.order.GetOwner() +} + +// GetAssets returns the assets for this order. +func (o FilledOrder) GetAssets() sdk.Coin { + return o.order.GetAssets() +} + +// GetPrice returns the actual price involved in this order fulfillment. +func (o FilledOrder) GetPrice() sdk.Coin { + return o.actualPrice +} + +// GetOriginalPrice gets the original price of this order. +func (o FilledOrder) GetOriginalPrice() sdk.Coin { + return o.order.GetPrice() +} + +// GetSettlementFees returns the actual settlement fees involved in this order fulfillment. +func (o FilledOrder) GetSettlementFees() sdk.Coins { + return o.actualFees +} + +// GetOriginalSettlementFees gets the original settlement fees of this order. +func (o FilledOrder) GetOriginalSettlementFees() sdk.Coins { + return o.order.GetSettlementFees() +} + +// PartialFillAllowed returns true if this order allows partial fulfillment. +func (o FilledOrder) PartialFillAllowed() bool { + return o.order.PartialFillAllowed() +} + +// GetOrderType returns a string indicating what type this order is. +// E.g: OrderTypeAsk or OrderTypeBid +func (o FilledOrder) GetOrderType() string { + return o.order.GetOrderType() +} + +// GetOrderTypeByte returns the type byte for this order. +// E.g: OrderTypeByteAsk or OrderTypeByteBid +func (o FilledOrder) GetOrderTypeByte() byte { + return o.order.GetOrderTypeByte() +} + +// GetHoldAmount returns the amount that should be on hold for this order. +func (o FilledOrder) GetHoldAmount() sdk.Coins { + return o.order.GetHoldAmount() +} + +// Validate returns nil (because it's assumed that the order was validated long ago). +// This is just here to fulfill the OrderI interface. +func (o FilledOrder) Validate() error { + return nil +} diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 9bd1ea04fa..42d5ff5078 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -2539,3 +2539,165 @@ func TestBidOrder_CopyChange(t *testing.T) { }) } } + +func TestNewFilledOrder(t *testing.T) { + expected := &FilledOrder{ + order: NewOrder(8), + actualPrice: sdk.NewInt64Coin("prune", 123), + actualFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 999)), + } + var actual *FilledOrder + testFunc := func() { + actual = NewFilledOrder(expected.order, expected.actualPrice, expected.actualFees) + } + require.NotPanics(t, testFunc, "NewFilledOrder") + assert.Equal(t, expected, actual, "NewFilledOrder result") +} + +func TestFilledOrderMethods(t *testing.T) { + askOrder := &AskOrder{ + MarketId: 333, + Seller: "SEllER", + Assets: sdk.NewInt64Coin("apple", 55), + Price: sdk.NewInt64Coin("peach", 111), + SellerSettlementFlatFee: &sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(8)}, + AllowPartial: true, + } + ask := NewOrder(51).WithAsk(askOrder) + askActualPrice := sdk.NewInt64Coin("peach", 123) + askActualFees := sdk.NewCoins(sdk.NewInt64Coin("fig", 13)) + filledAsk := NewFilledOrder(ask, askActualPrice, askActualFees) + + bidOrder := &BidOrder{ + MarketId: 444, + Buyer: "BUyER", + Assets: sdk.NewInt64Coin("apple", 56), + Price: sdk.NewInt64Coin("peach", 112), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 9)), + AllowPartial: true, + } + bid := NewOrder(52).WithBid(bidOrder) + bidActualPrice := sdk.NewInt64Coin("peach", 124) + bidActualFees := sdk.NewCoins(sdk.NewInt64Coin("fig", 14)) + filledBid := NewFilledOrder(bid, bidActualPrice, bidActualFees) + + tests := []struct { + name string + getter func(fo *FilledOrder) interface{} + expAsk interface{} + expBid interface{} + }{ + { + name: "GetOriginalOrder", + getter: func(of *FilledOrder) interface{} { return of.GetOriginalOrder() }, + expAsk: ask, + expBid: bid, + }, + { + name: "GetOrderID", + getter: func(of *FilledOrder) interface{} { return of.GetOrderID() }, + expAsk: ask.OrderId, + expBid: bid.OrderId, + }, + { + name: "IsAskOrder", + getter: func(of *FilledOrder) interface{} { return of.IsAskOrder() }, + expAsk: true, + expBid: false, + }, + { + name: "IsBidOrder", + getter: func(of *FilledOrder) interface{} { return of.IsBidOrder() }, + expAsk: false, + expBid: true, + }, + { + name: "GetMarketID", + getter: func(of *FilledOrder) interface{} { return of.GetMarketID() }, + expAsk: askOrder.MarketId, + expBid: bidOrder.MarketId, + }, + { + name: "GetOwner", + getter: func(of *FilledOrder) interface{} { return of.GetOwner() }, + expAsk: askOrder.Seller, + expBid: bidOrder.Buyer, + }, + { + name: "GetAssets", + getter: func(of *FilledOrder) interface{} { return of.GetAssets() }, + expAsk: askOrder.Assets, + expBid: bidOrder.Assets, + }, + { + name: "GetPrice", + getter: func(of *FilledOrder) interface{} { return of.GetPrice() }, + expAsk: askActualPrice, + expBid: bidActualPrice, + }, + { + name: "GetOriginalPrice", + getter: func(of *FilledOrder) interface{} { return of.GetOriginalPrice() }, + expAsk: askOrder.Price, + expBid: bidOrder.Price, + }, + { + name: "GetSettlementFees", + getter: func(of *FilledOrder) interface{} { return of.GetSettlementFees() }, + expAsk: askActualFees, + expBid: bidActualFees, + }, + { + name: "GetOriginalSettlementFees", + getter: func(of *FilledOrder) interface{} { return of.GetOriginalSettlementFees() }, + expAsk: sdk.Coins{*askOrder.SellerSettlementFlatFee}, + expBid: bidOrder.BuyerSettlementFees, + }, + { + name: "PartialFillAllowed", + getter: func(of *FilledOrder) interface{} { return of.PartialFillAllowed() }, + expAsk: askOrder.AllowPartial, + expBid: bidOrder.AllowPartial, + }, + { + name: "GetOrderType", + getter: func(of *FilledOrder) interface{} { return of.GetOrderType() }, + expAsk: OrderTypeAsk, + expBid: OrderTypeBid, + }, + { + name: "GetOrderTypeByte", + getter: func(of *FilledOrder) interface{} { return of.GetOrderTypeByte() }, + expAsk: OrderTypeByteAsk, + expBid: OrderTypeByteBid, + }, + { + name: "GetHoldAmount", + getter: func(of *FilledOrder) interface{} { return of.GetHoldAmount() }, + expAsk: askOrder.GetHoldAmount(), + expBid: bidOrder.GetHoldAmount(), + }, + { + name: "Validate", + getter: func(of *FilledOrder) interface{} { return of.Validate() }, + expAsk: error(nil), + expBid: error(nil), + }, + } + + tester := func(of *FilledOrder, getter func(*FilledOrder) interface{}, expected interface{}) func(t *testing.T) { + return func(t *testing.T) { + var actual interface{} + testFunc := func() { + actual = getter(of) + } + require.NotPanics(t, testFunc) + assert.Equal(t, expected, actual) + } + } + + for _, tc := range tests { + t.Run(tc.name+": ask", tester(filledAsk, tc.getter, tc.expAsk)) + t.Run(tc.name+": bid", tester(filledBid, tc.getter, tc.expBid)) + } +} From 65197e170a7bdee3a7cfa6a34bfac740aa417e1a Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 6 Oct 2023 15:51:26 -0600 Subject: [PATCH 250/309] [1658]: Unit tests on splitOrderFulfillments. --- x/exchange/fulfillment.go | 2 +- x/exchange/fulfillment_test.go | 461 +++++++++++++++++++++++++++++++-- x/exchange/orders_test.go | 21 +- 3 files changed, 451 insertions(+), 33 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 7a55e02729..f9a996aaa6 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -269,7 +269,7 @@ func (f *OrderFulfillment) SplitOrder() (*Order, error) { } // AsFilledOrder creates a FilledOrder from this order fulfillment. -func (f *OrderFulfillment) AsFilledOrder() *FilledOrder { +func (f OrderFulfillment) AsFilledOrder() *FilledOrder { return NewFilledOrder(f.Order, f.GetPriceApplied(), f.FeesToPay) } diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index b719d7dc8c..1b714df978 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -24,6 +24,18 @@ import ( // So when an object has an sdkmath.Int that should have been reduced to zero, you'll need to use this. var ZeroAmtAfterSub = sdkmath.NewInt(1).SubRaw(1) +// copySlice copies a slice using the provided copier for each entry. +func copySlice[T any](vals []T, copier func(T) T) []T { + if vals == nil { + return nil + } + rv := make([]T, len(vals)) + for i, v := range vals { + rv[i] = copier(v) + } + return rv +} + // copyOrderSplit creates a copy of this order split. // Unlike the other copiers in here, the Order is not deep copied, it will be the same reference. func copyOrderSplit(split *OrderSplit) *OrderSplit { @@ -66,15 +78,7 @@ func copyDistribution(dist *Distribution) *Distribution { // copyDistributions copies a slice of distributions. func copyDistributions(dists []*Distribution) []*Distribution { - if dists == nil { - return nil - } - - rv := make([]*Distribution, len(dists)) - for i, dist := range dists { - rv[i] = copyDistribution(dist) - } - return rv + return copySlice(dists, copyDistribution) } // copyOrderFulfillment returns a deep copy of an order fulfillment. @@ -102,14 +106,66 @@ func copyOrderFulfillment(f *OrderFulfillment) *OrderFulfillment { // copyOrderFulfillments returns a deep copy of a slice of order fulfillments. func copyOrderFulfillments(fs []*OrderFulfillment) []*OrderFulfillment { - if fs == nil { + return copySlice(fs, copyOrderFulfillment) +} + +// copyInput returns a deep copy of a bank input. +func copyInput(input banktypes.Input) banktypes.Input { + return banktypes.Input{ + Address: input.Address, + Coins: copyCoins(input.Coins), + } +} + +// copyInputs returns a deep copy of a slice of bank inputs. +func copyInputs(inputs []banktypes.Input) []banktypes.Input { + return copySlice(inputs, copyInput) +} + +// copyOutput returns a deep copy of a bank output. +func copyOutput(output banktypes.Output) banktypes.Output { + return banktypes.Output{ + Address: output.Address, + Coins: copyCoins(output.Coins), + } +} + +// copyOutputs returns a deep copy of a slice of bank outputs. +func copyOutputs(outputs []banktypes.Output) []banktypes.Output { + return copySlice(outputs, copyOutput) +} + +// copyTransfer returns a deep copy of a transfer. +func copyTransfer(t *Transfer) *Transfer { + if t == nil { return nil } - rv := make([]*OrderFulfillment, len(fs)) - for i, f := range fs { - rv[i] = copyOrderFulfillment(f) + return &Transfer{ + Inputs: copyInputs(t.Inputs), + Outputs: copyOutputs(t.Outputs), } - return rv +} + +// copyTransfers returns a deep copy of a slice of transfers. +func copyTransfers(ts []*Transfer) []*Transfer { + return copySlice(ts, copyTransfer) +} + +// copyFilledOrder returns a deep copy of a filled order. +func copyFilledOrder(f *FilledOrder) *FilledOrder { + if f == nil { + return nil + } + return &FilledOrder{ + order: copyOrder(f.order), + actualPrice: copyCoin(f.actualPrice), + actualFees: copyCoins(f.actualFees), + } +} + +// copyFilledOrders returns a deep copy of a slice of filled order. +func copyFilledOrders(fs []*FilledOrder) []*FilledOrder { + return copySlice(fs, copyFilledOrder) } // orderSplitString is similar to %v except with easier to understand Coin and Int entries. @@ -2198,8 +2254,66 @@ func TestOrderFulfillment_SplitOrder(t *testing.T) { } func TestOrderFulfillment_AsFilledOrder(t *testing.T) { - // TODO[1658]: func TestAsFilledOrder(t *testing.T) - t.Skipf("not written") + askOrder := NewOrder(53).WithAsk(&AskOrder{ + MarketId: 765, + Seller: "mefirst", + Assets: sdk.NewInt64Coin("apple", 15), + Price: sdk.NewInt64Coin("peach", 88), + SellerSettlementFlatFee: &sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(6)}, + AllowPartial: true, + }) + bidOrder := NewOrder(9556).WithBid(&BidOrder{ + MarketId: 145, + Buyer: "gimmiegimmie", + Assets: sdk.NewInt64Coin("acorn", 1171), + Price: sdk.NewInt64Coin("prune", 5100), + BuyerSettlementFees: sdk.NewCoins(sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(14)}), + AllowPartial: false, + }) + + tests := []struct { + name string + receiver OrderFulfillment + expected *FilledOrder + }{ + { + name: "ask order", + receiver: OrderFulfillment{ + Order: askOrder, + PriceAppliedAmt: sdkmath.NewInt(132), + FeesToPay: sdk.NewCoins(sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(7)}), + }, + expected: &FilledOrder{ + order: askOrder, + actualPrice: sdk.NewInt64Coin("peach", 132), + actualFees: sdk.NewCoins(sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(7)}), + }, + }, + { + name: "bid order", + receiver: OrderFulfillment{ + Order: bidOrder, + PriceAppliedAmt: sdkmath.NewInt(5123), + FeesToPay: sdk.NewCoins(sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(23)}), + }, + expected: &FilledOrder{ + order: bidOrder, + actualPrice: sdk.NewInt64Coin("prune", 5123), + actualFees: sdk.NewCoins(sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(23)}), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual *FilledOrder + testFunc := func() { + actual = tc.receiver.AsFilledOrder() + } + require.NotPanics(t, testFunc, "AsFilledOrder()") + assert.Equal(t, tc.expected, actual, "AsFilledOrder() result") + }) + } } func TestSumAssetsAndPrice(t *testing.T) { @@ -3095,8 +3209,319 @@ func TestSplitPartial(t *testing.T) { } func TestSplitOrderFulfillments(t *testing.T) { - // TODO[1658]: func TestSplitOrderFulfillments(t *testing.T) - t.Skipf("not written") + acoin := func(amount int64) sdk.Coin { + return sdk.NewInt64Coin("acorn", amount) + } + pcoin := func(amount int64) sdk.Coin { + return sdk.NewInt64Coin("prune", amount) + } + askOrder := func(orderID uint64, assetsAmt int64, allowPartial bool) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + MarketId: 123, + Seller: "sEllEr", + Assets: acoin(assetsAmt), + Price: pcoin(assetsAmt), + AllowPartial: allowPartial, + }) + } + bidOrder := func(orderID uint64, assetsAmt int64, allowPartial bool) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + MarketId: 123, + Buyer: "bUyEr", + Assets: acoin(assetsAmt), + Price: pcoin(assetsAmt), + AllowPartial: allowPartial, + }) + } + newOF := func(order *Order, assetsFilledAmt int64) *OrderFulfillment { + rv := &OrderFulfillment{ + Order: order, + AssetsFilledAmt: sdkmath.NewInt(assetsFilledAmt), + AssetsUnfilledAmt: order.GetAssets().Amount.SubRaw(assetsFilledAmt), + PriceAppliedAmt: sdkmath.NewInt(assetsFilledAmt), + PriceLeftAmt: order.GetPrice().Amount.SubRaw(assetsFilledAmt), + } + // int(x).Sub(x) results in an object that is not .Equal to ZeroInt(). + // The Split function sets this to ZeroInt(). + if rv.AssetsUnfilledAmt.IsZero() { + rv.AssetsUnfilledAmt = sdkmath.ZeroInt() + } + return rv + } + + tests := []struct { + name string + fulfillments []*OrderFulfillment + settlement *Settlement + expFulfillments []*OrderFulfillment + expSettlement *Settlement + expErr string + }{ + { + name: "one order, ask: nothing filled", + fulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, false), 0)}, + settlement: &Settlement{}, + expErr: "ask order 8 (at index 0) has no assets filled", + }, + { + name: "one order, bid: nothing filled", + fulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, false), 0)}, + settlement: &Settlement{}, + expErr: "bid order 8 (at index 0) has no assets filled", + }, + { + name: "one order, ask: partially filled", + fulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, true), 13)}, + settlement: &Settlement{}, + expFulfillments: []*OrderFulfillment{newOF(askOrder(8, 13, true), 13)}, + expSettlement: &Settlement{PartialOrderLeft: askOrder(8, 40, true)}, + }, + { + name: "one order, bid: partially filled", + fulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, true), 13)}, + settlement: &Settlement{}, + expFulfillments: []*OrderFulfillment{newOF(bidOrder(8, 13, true), 13)}, + expSettlement: &Settlement{PartialOrderLeft: bidOrder(8, 40, true)}, + }, + { + name: "one order, ask: partially filled, already have a partially filled", + fulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, true), 13)}, + settlement: &Settlement{PartialOrderLeft: bidOrder(951, 357, true)}, + expErr: "bid order 951 and ask order 8 cannot both be partially filled", + }, + { + name: "one order, bid: partially filled, already have a partially filled", + fulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, true), 13)}, + settlement: &Settlement{PartialOrderLeft: askOrder(951, 357, true)}, + expErr: "ask order 951 and bid order 8 cannot both be partially filled", + }, + { + name: "one order, ask: partially filled, split not allowed", + fulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, false), 13)}, + settlement: &Settlement{}, + expErr: "cannot split ask order 8 having assets \"53acorn\" at \"13acorn\": order does not allow partial fulfillment", + }, + { + name: "one order, bid: partially filled, split not allowed", + fulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, false), 13)}, + settlement: &Settlement{}, + expErr: "cannot split bid order 8 having assets \"53acorn\" at \"13acorn\": order does not allow partial fulfillment", + }, + { + name: "one order, ask: fully filled", + fulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, false), 53)}, + settlement: &Settlement{}, + expFulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, false), 53)}, + expSettlement: &Settlement{}, + }, + { + name: "one order, bid: fully filled", + fulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, false), 53)}, + settlement: &Settlement{}, + expFulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, false), 53)}, + expSettlement: &Settlement{}, + }, + { + name: "one order, ask: fully filled, already have a partially filled", + fulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, false), 53)}, + settlement: &Settlement{PartialOrderLeft: bidOrder(951, 357, true)}, + expFulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, false), 53)}, + expSettlement: &Settlement{PartialOrderLeft: bidOrder(951, 357, true)}, + }, + { + name: "one order, bid: fully filled, already have a partially filled", + fulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, false), 53)}, + settlement: &Settlement{PartialOrderLeft: askOrder(951, 357, true)}, + expFulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, false), 53)}, + expSettlement: &Settlement{PartialOrderLeft: askOrder(951, 357, true)}, + }, + { + name: "three orders, ask: second partially filled", + fulfillments: []*OrderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, true), 16), + newOF(askOrder(10, 200, false), 0), + }, + settlement: &Settlement{}, + expErr: "ask order 9 (at index 1) is not filled in full and is not the last ask order provided", + }, + { + name: "three orders, bid: second partially filled", + fulfillments: []*OrderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, true), 16), + newOF(bidOrder(10, 200, false), 0), + }, + settlement: &Settlement{}, + expErr: "bid order 9 (at index 1) is not filled in full and is not the last bid order provided", + }, + { + name: "three orders, ask: last not touched", + fulfillments: []*OrderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 200, false), 0), + }, + settlement: &Settlement{}, + expErr: "ask order 10 (at index 2) has no assets filled", + }, + { + name: "three orders, bid: last not touched", + fulfillments: []*OrderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 200, true), 0), + }, + settlement: &Settlement{}, + expErr: "bid order 10 (at index 2) has no assets filled", + }, + { + name: "three orders, ask: last partially filled", + fulfillments: []*OrderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 200, true), 183), + }, + settlement: &Settlement{}, + expFulfillments: []*OrderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 183, true), 183), + }, + expSettlement: &Settlement{PartialOrderLeft: askOrder(10, 17, true)}, + }, + { + name: "three orders, bid: last partially filled", + fulfillments: []*OrderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 200, true), 183), + }, + settlement: &Settlement{}, + expFulfillments: []*OrderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 183, true), 183), + }, + expSettlement: &Settlement{PartialOrderLeft: bidOrder(10, 17, true)}, + }, + { + name: "three orders, ask: last partially filled, split not allowed", + fulfillments: []*OrderFulfillment{ + newOF(askOrder(8, 53, true), 53), + newOF(askOrder(9, 17, true), 17), + newOF(askOrder(10, 200, false), 183), + }, + settlement: &Settlement{}, + expErr: "cannot split ask order 10 having assets \"200acorn\" at \"183acorn\": order does not allow partial fulfillment", + }, + { + name: "three orders, bid: last partially filled, split not allowed", + fulfillments: []*OrderFulfillment{ + newOF(bidOrder(8, 53, true), 53), + newOF(bidOrder(9, 17, true), 17), + newOF(bidOrder(10, 200, false), 183), + }, + settlement: &Settlement{}, + expErr: "cannot split bid order 10 having assets \"200acorn\" at \"183acorn\": order does not allow partial fulfillment", + }, + { + name: "three orders, ask: last partially filled, already have a partially filled", + fulfillments: []*OrderFulfillment{ + newOF(askOrder(8, 53, true), 53), + newOF(askOrder(9, 17, true), 17), + newOF(askOrder(10, 200, false), 183), + }, + settlement: &Settlement{PartialOrderLeft: bidOrder(857, 43, true)}, + expErr: "bid order 857 and ask order 10 cannot both be partially filled", + }, + { + name: "three orders, bid: last partially filled, already have a partially filled", + fulfillments: []*OrderFulfillment{ + newOF(bidOrder(8, 53, true), 53), + newOF(bidOrder(9, 17, true), 17), + newOF(bidOrder(10, 200, false), 183), + }, + settlement: &Settlement{PartialOrderLeft: askOrder(857, 43, true)}, + expErr: "ask order 857 and bid order 10 cannot both be partially filled", + }, + { + name: "three orders, ask: fully filled", + fulfillments: []*OrderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 200, false), 200), + }, + settlement: &Settlement{}, + expFulfillments: []*OrderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 200, false), 200), + }, + expSettlement: &Settlement{}, + }, + { + name: "three orders, bid: fully filled", + fulfillments: []*OrderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 200, false), 200), + }, + settlement: &Settlement{}, + expFulfillments: []*OrderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 200, false), 200), + }, + expSettlement: &Settlement{}, + }, + { + name: "three orders, ask: fully filled, already have a partially filled", + fulfillments: []*OrderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 200, false), 200), + }, + settlement: &Settlement{}, + expFulfillments: []*OrderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 200, false), 200), + }, + expSettlement: &Settlement{}, + }, + { + name: "three orders, bid: fully filled, already have a partially filled", + fulfillments: []*OrderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 200, false), 200), + }, + settlement: &Settlement{PartialOrderLeft: askOrder(857, 43, true)}, + expFulfillments: []*OrderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 200, false), 200), + }, + expSettlement: &Settlement{PartialOrderLeft: askOrder(857, 43, true)}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = splitOrderFulfillments(tc.fulfillments, tc.settlement) + } + require.NotPanics(t, testFunc, "splitOrderFulfillments") + assertions.AssertErrorValue(t, err, tc.expErr, "splitOrderFulfillments error") + if len(tc.expErr) > 0 { + return + } + assertEqualOrderFulfillmentSlices(t, tc.expFulfillments, tc.fulfillments, "fulfillments after splitOrderFulfillments") + assert.Equal(t, tc.expSettlement, tc.settlement, "settlement after splitOrderFulfillments") + }) + } } func TestAllocatePrice(t *testing.T) { diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 42d5ff5078..157642924a 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -82,14 +82,7 @@ func copySDKInt(i sdkmath.Int) (copy sdkmath.Int) { // copyCoins creates a copy of the provided coins slice with copies of each entry. func copyCoins(coins sdk.Coins) sdk.Coins { - if coins == nil { - return nil - } - rv := make(sdk.Coins, len(coins)) - for i, coin := range coins { - rv[i] = copyCoin(coin) - } - return rv + return copySlice(coins, copyCoin) } // copyCoin returns a copy of the provided coin. @@ -2554,7 +2547,7 @@ func TestNewFilledOrder(t *testing.T) { assert.Equal(t, expected, actual, "NewFilledOrder result") } -func TestFilledOrderMethods(t *testing.T) { +func TestFilledOrderGetters(t *testing.T) { askOrder := &AskOrder{ MarketId: 333, Seller: "SEllER", @@ -2685,19 +2678,19 @@ func TestFilledOrderMethods(t *testing.T) { }, } - tester := func(of *FilledOrder, getter func(*FilledOrder) interface{}, expected interface{}) func(t *testing.T) { + tester := func(name string, of *FilledOrder, getter func(*FilledOrder) interface{}, expected interface{}) func(t *testing.T) { return func(t *testing.T) { var actual interface{} testFunc := func() { actual = getter(of) } - require.NotPanics(t, testFunc) - assert.Equal(t, expected, actual) + require.NotPanics(t, testFunc, "%s()") + assert.Equal(t, expected, actual, "%s() result", name) } } for _, tc := range tests { - t.Run(tc.name+": ask", tester(filledAsk, tc.getter, tc.expAsk)) - t.Run(tc.name+": bid", tester(filledBid, tc.getter, tc.expBid)) + t.Run(tc.name+": ask", tester(tc.name, filledAsk, tc.getter, tc.expAsk)) + t.Run(tc.name+": bid", tester(tc.name, filledBid, tc.getter, tc.expBid)) } } From c06b680b2f6e675194503dbdf0ff20abaf95364e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 6 Oct 2023 16:31:25 -0600 Subject: [PATCH 251/309] [1658]: Unit tests on populateFilled. --- x/exchange/fulfillment_test.go | 133 ++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 2 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 1b714df978..60bf6a41a8 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -3545,8 +3545,137 @@ func TestBuildTransfers(t *testing.T) { } func TestPopulateFilled(t *testing.T) { - // TODO[1658]: func TestPopulateFilled(t *testing.T) - t.Skipf("not written") + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + askOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Assets: coin(assetsAmt, "acorn"), + Price: coin(priceAmt, "prune"), + }) + } + bidOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Assets: coin(assetsAmt, "acorn"), + Price: coin(priceAmt, "prune"), + }) + } + newOF := func(order *Order, priceAppliedAmt int64, fees ...sdk.Coin) *OrderFulfillment { + rv := &OrderFulfillment{ + Order: order, + AssetsFilledAmt: order.GetAssets().Amount, + AssetsUnfilledAmt: sdkmath.ZeroInt(), + PriceAppliedAmt: sdkmath.NewInt(priceAppliedAmt), + PriceLeftAmt: order.GetPrice().Amount.SubRaw(priceAppliedAmt), + } + if len(fees) > 0 { + rv.FeesToPay = fees + } + return rv + } + filledOrder := func(order *Order, actualPrice int64, actualFees ...sdk.Coin) *FilledOrder { + rv := &FilledOrder{ + order: order, + actualPrice: coin(actualPrice, order.GetPrice().Denom), + } + if len(actualFees) > 0 { + rv.actualFees = actualFees + } + return rv + } + + tests := []struct { + name string + askOFs []*OrderFulfillment + bidOFs []*OrderFulfillment + settlement *Settlement + expSettlement *Settlement + }{ + { + name: "no partial", + askOFs: []*OrderFulfillment{ + newOF(askOrder(2001, 53, 87), 92, coin(12, "fig")), + newOF(askOrder(2002, 17, 33), 37), + newOF(askOrder(2003, 22, 56), 60), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(3001, 30, 40), 40), + newOF(bidOrder(3002, 27, 49), 49, coin(39, "fig")), + newOF(bidOrder(3003, 35, 100), 100), + }, + settlement: &Settlement{}, + expSettlement: &Settlement{ + FullyFilledOrders: []*FilledOrder{ + filledOrder(askOrder(2001, 53, 87), 92, coin(12, "fig")), + filledOrder(askOrder(2002, 17, 33), 37), + filledOrder(askOrder(2003, 22, 56), 60), + filledOrder(bidOrder(3001, 30, 40), 40), + filledOrder(bidOrder(3002, 27, 49), 49, coin(39, "fig")), + filledOrder(bidOrder(3003, 35, 100), 100), + }, + }, + }, + { + name: "partial ask", + askOFs: []*OrderFulfillment{ + newOF(askOrder(2001, 53, 87), 92, coin(12, "fig")), + newOF(askOrder(2002, 17, 33), 37), + newOF(askOrder(2003, 22, 56), 60), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(3001, 30, 40), 40), + newOF(bidOrder(3002, 27, 49), 49, coin(39, "fig")), + newOF(bidOrder(3003, 35, 100), 100), + }, + settlement: &Settlement{PartialOrderLeft: askOrder(2002, 15, 63)}, + expSettlement: &Settlement{ + FullyFilledOrders: []*FilledOrder{ + filledOrder(askOrder(2001, 53, 87), 92, coin(12, "fig")), + filledOrder(askOrder(2003, 22, 56), 60), + filledOrder(bidOrder(3001, 30, 40), 40), + filledOrder(bidOrder(3002, 27, 49), 49, coin(39, "fig")), + filledOrder(bidOrder(3003, 35, 100), 100), + }, + PartialOrderFilled: filledOrder(askOrder(2002, 17, 33), 37), + PartialOrderLeft: askOrder(2002, 15, 63), + }, + }, + { + name: "partial bid", + askOFs: []*OrderFulfillment{ + newOF(askOrder(2001, 53, 87), 92, coin(12, "fig")), + newOF(askOrder(2002, 17, 33), 37), + newOF(askOrder(2003, 22, 56), 60), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(3001, 30, 40), 40), + newOF(bidOrder(3002, 27, 49), 49, coin(39, "fig")), + newOF(bidOrder(3003, 35, 100), 100), + }, + settlement: &Settlement{PartialOrderLeft: bidOrder(3003, 15, 63)}, + expSettlement: &Settlement{ + FullyFilledOrders: []*FilledOrder{ + filledOrder(askOrder(2001, 53, 87), 92, coin(12, "fig")), + filledOrder(askOrder(2002, 17, 33), 37), + filledOrder(askOrder(2003, 22, 56), 60), + filledOrder(bidOrder(3001, 30, 40), 40), + filledOrder(bidOrder(3002, 27, 49), 49, coin(39, "fig")), + }, + PartialOrderFilled: filledOrder(bidOrder(3003, 35, 100), 100), + PartialOrderLeft: bidOrder(3003, 15, 63), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testFunc := func() { + populateFilled(tc.askOFs, tc.bidOFs, tc.settlement) + } + require.NotPanics(t, testFunc, "populateFilled") + assert.Equal(t, tc.expSettlement, tc.settlement, "settlement after populateFilled") + }) + } } func TestGetAssetTransfer(t *testing.T) { From 8663434446157c745cd64cf4d7d9568b173ad388 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 6 Oct 2023 17:15:48 -0600 Subject: [PATCH 252/309] [1658]: Unit tests on setFeesToPay. --- x/exchange/fulfillment.go | 6 +- x/exchange/fulfillment_test.go | 136 ++++++++++++++++++++++++++++++++- 2 files changed, 138 insertions(+), 4 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index f9a996aaa6..567366c2b1 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -608,15 +608,17 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { func setFeesToPay(askOFs, bidOFs []*OrderFulfillment, sellerFeeRatio *FeeRatio) error { var errs []error for _, askOF := range askOFs { - askOF.FeesToPay = askOF.GetSettlementFees() + feesToPay := askOF.GetSettlementFees() if sellerFeeRatio != nil { fee, err := sellerFeeRatio.ApplyToLoosely(askOF.GetPriceApplied()) if err != nil { errs = append(errs, fmt.Errorf("failed calculate ratio fee for %s order %d: %w", askOF.GetOrderType(), askOF.GetOrderID(), err)) + continue } - askOF.FeesToPay = askOF.FeesToPay.Add(fee) + feesToPay = feesToPay.Add(fee) } + askOF.FeesToPay = feesToPay } for _, bidOF := range bidOFs { diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 60bf6a41a8..f3d860695d 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -3530,8 +3530,140 @@ func TestAllocatePrice(t *testing.T) { } func TestSetFeesToPay(t *testing.T) { - // TODO[1658]: func TestSetFeesToPay(t *testing.T) - t.Skipf("not written") + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + askOF := func(orderID uint64, priceAppliedAmt int64, fees ...sdk.Coin) *OrderFulfillment { + askOrder := &AskOrder{Price: coin(50, "plum")} + if len(fees) > 1 { + t.Fatalf("cannot provide more than one fee to askOF(%d, %d, %q)", + orderID, priceAppliedAmt, fees) + } + if len(fees) > 0 { + askOrder.SellerSettlementFlatFee = &fees[0] + } + return &OrderFulfillment{ + Order: NewOrder(orderID).WithAsk(askOrder), + PriceAppliedAmt: sdkmath.NewInt(priceAppliedAmt), + } + } + bidOF := func(orderID uint64, priceAppliedAmt int64, fees ...sdk.Coin) *OrderFulfillment { + bidOrder := &BidOrder{Price: coin(50, "plum")} + if len(fees) > 0 { + bidOrder.BuyerSettlementFees = fees + } + return &OrderFulfillment{ + Order: NewOrder(orderID).WithBid(bidOrder), + PriceAppliedAmt: sdkmath.NewInt(priceAppliedAmt), + } + } + expOF := func(f *OrderFulfillment, feesToPay ...sdk.Coin) *OrderFulfillment { + if len(feesToPay) > 0 { + f.FeesToPay = sdk.NewCoins(feesToPay...) + } + return f + } + + tests := []struct { + name string + askOFs []*OrderFulfillment + bidOFs []*OrderFulfillment + ratio *FeeRatio + expAskOFs []*OrderFulfillment + expBidOFs []*OrderFulfillment + expErr string + }{ + { + name: "cannot apply ratio", + askOFs: []*OrderFulfillment{ + askOF(7777, 55, coin(20, "grape")), + askOF(5555, 71), + askOF(6666, 100), + }, + bidOFs: []*OrderFulfillment{ + bidOF(1111, 100), + bidOF(2222, 200, coin(20, "grape")), + bidOF(3333, 300), + }, + ratio: &FeeRatio{Price: coin(30, "peach"), Fee: coin(1, "fig")}, + expAskOFs: []*OrderFulfillment{ + expOF(askOF(7777, 55, coin(20, "grape"))), + expOF(askOF(5555, 71)), + expOF(askOF(6666, 100)), + }, + expBidOFs: []*OrderFulfillment{ + expOF(bidOF(1111, 100)), + expOF(bidOF(2222, 200, coin(20, "grape")), coin(20, "grape")), + expOF(bidOF(3333, 300)), + }, + expErr: joinErrs( + "failed calculate ratio fee for ask order 7777: cannot apply ratio 30peach:1fig to price 55plum: incorrect price denom", + "failed calculate ratio fee for ask order 5555: cannot apply ratio 30peach:1fig to price 71plum: incorrect price denom", + "failed calculate ratio fee for ask order 6666: cannot apply ratio 30peach:1fig to price 100plum: incorrect price denom", + ), + }, + { + name: "no ratio", + askOFs: []*OrderFulfillment{ + askOF(7777, 55, coin(20, "grape")), + askOF(5555, 71), + askOF(6666, 100), + }, + bidOFs: []*OrderFulfillment{ + bidOF(1111, 100), + bidOF(2222, 200, coin(20, "grape")), + bidOF(3333, 300), + }, + ratio: nil, + expAskOFs: []*OrderFulfillment{ + expOF(askOF(7777, 55, coin(20, "grape")), coin(20, "grape")), + expOF(askOF(5555, 71)), + expOF(askOF(6666, 100)), + }, + expBidOFs: []*OrderFulfillment{ + expOF(bidOF(1111, 100)), + expOF(bidOF(2222, 200, coin(20, "grape")), coin(20, "grape")), + expOF(bidOF(3333, 300)), + }, + }, + { + name: "with ratio", + askOFs: []*OrderFulfillment{ + askOF(7777, 55, coin(20, "grape")), + askOF(5555, 71), + askOF(6666, 100), + }, + bidOFs: []*OrderFulfillment{ + bidOF(1111, 100), + bidOF(2222, 200, coin(20, "grape")), + bidOF(3333, 300), + }, + ratio: &FeeRatio{Price: coin(30, "plum"), Fee: coin(1, "fig")}, + expAskOFs: []*OrderFulfillment{ + expOF(askOF(7777, 55, coin(20, "grape")), coin(2, "fig"), coin(20, "grape")), + expOF(askOF(5555, 71), coin(3, "fig")), + expOF(askOF(6666, 100), coin(4, "fig")), + }, + expBidOFs: []*OrderFulfillment{ + expOF(bidOF(1111, 100)), + expOF(bidOF(2222, 200, coin(20, "grape")), coin(20, "grape")), + expOF(bidOF(3333, 300)), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = setFeesToPay(tc.askOFs, tc.bidOFs, tc.ratio) + } + require.NotPanics(t, testFunc, "setFeesToPay") + assertions.AssertErrorValue(t, err, tc.expErr, "setFeesToPay error") + assertEqualOrderFulfillmentSlices(t, tc.expAskOFs, tc.askOFs, "askOFs after setFeesToPay") + assertEqualOrderFulfillmentSlices(t, tc.expBidOFs, tc.bidOFs, "bidOFs after setFeesToPay") + }) + } } func TestValidateFulfillments(t *testing.T) { From e857942251fada3354f58693dbdbeb8e233e0b82 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 6 Oct 2023 17:35:32 -0600 Subject: [PATCH 253/309] [1658]: Unit tests on validateFulfillments. --- x/exchange/fulfillment_test.go | 93 +++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index f3d860695d..3845d1998e 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -3667,8 +3667,97 @@ func TestSetFeesToPay(t *testing.T) { } func TestValidateFulfillments(t *testing.T) { - // TODO[1658]: func TestValidateFulfillments(t *testing.T) - t.Skipf("not written") + goodAskOF := func(orderID uint64) *OrderFulfillment { + return &OrderFulfillment{ + Order: NewOrder(orderID).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("peach", 123), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + PriceAppliedAmt: sdkmath.NewInt(130), + } + } + badAskOF := func(orderID uint64) *OrderFulfillment { + rv := goodAskOF(orderID) + rv.AssetsFilledAmt = sdkmath.NewInt(49) + return rv + } + badAskErr := func(orderID uint64) string { + return badAskOF(orderID).Validate().Error() + } + goodBidOF := func(orderID uint64) *OrderFulfillment { + return &OrderFulfillment{ + Order: NewOrder(orderID).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("peach", 123), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + PriceAppliedAmt: sdkmath.NewInt(123), + } + } + badBidOF := func(orderID uint64) *OrderFulfillment { + rv := goodBidOF(orderID) + rv.AssetsFilledAmt = sdkmath.NewInt(49) + return rv + } + badBidErr := func(orderID uint64) string { + return badBidOF(orderID).Validate().Error() + } + + tests := []struct { + name string + askOFs []*OrderFulfillment + bidOFs []*OrderFulfillment + expErr string + }{ + { + name: "all good", + askOFs: []*OrderFulfillment{goodAskOF(10), goodAskOF(11), goodAskOF(12)}, + bidOFs: []*OrderFulfillment{goodBidOF(20), goodBidOF(21), goodBidOF(22)}, + expErr: "", + }, + { + name: "error in one ask", + askOFs: []*OrderFulfillment{goodAskOF(10), badAskOF(11), goodAskOF(12)}, + bidOFs: []*OrderFulfillment{goodBidOF(20), goodBidOF(21), goodBidOF(22)}, + expErr: badAskErr(11), + }, + { + name: "error in one bid", + askOFs: []*OrderFulfillment{goodAskOF(10), goodAskOF(11), goodAskOF(12)}, + bidOFs: []*OrderFulfillment{goodBidOF(20), badBidOF(21), goodBidOF(22)}, + expErr: badBidErr(21), + }, + { + name: "two errors in asks", + askOFs: []*OrderFulfillment{badAskOF(10), goodAskOF(11), badAskOF(12)}, + bidOFs: []*OrderFulfillment{goodBidOF(20), goodBidOF(21), goodBidOF(22)}, + expErr: joinErrs(badAskErr(10), badAskErr(12)), + }, + { + name: "two errors in bids", + askOFs: []*OrderFulfillment{goodAskOF(10), goodAskOF(11), goodAskOF(12)}, + bidOFs: []*OrderFulfillment{badBidOF(20), goodBidOF(21), badBidOF(22)}, + expErr: joinErrs(badBidErr(20), badBidErr(22)), + }, + { + name: "error in each", + askOFs: []*OrderFulfillment{goodAskOF(10), goodAskOF(11), badAskOF(12)}, + bidOFs: []*OrderFulfillment{goodBidOF(20), badBidOF(21), goodBidOF(22)}, + expErr: joinErrs(badAskErr(12), badBidErr(21)), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = validateFulfillments(tc.askOFs, tc.bidOFs) + } + require.NotPanics(t, testFunc, "validateFulfillments") + assertions.AssertErrorValue(t, err, tc.expErr, "validateFulfillments error") + }) + } } func TestBuildTransfers(t *testing.T) { From 3b0340b979a2dc4551dc7867b2b2cbec3572cbf2 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 6 Oct 2023 18:31:17 -0600 Subject: [PATCH 254/309] [1658]: Unit tests on buildTransfers. --- x/exchange/fulfillment_test.go | 237 ++++++++++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 2 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 3845d1998e..85b306ed7e 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -3761,8 +3761,241 @@ func TestValidateFulfillments(t *testing.T) { } func TestBuildTransfers(t *testing.T) { - // TODO[1658]: func TestBuildTransfers(t *testing.T) - t.Skipf("not written") + tests := []struct { + name string + askOFs []*OrderFulfillment + bidOFs []*OrderFulfillment + expSettlement *Settlement + expErr string + }{ + { + name: "ask with negative assets filled", + askOFs: []*OrderFulfillment{ + { + Order: NewOrder(18).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 15), + Price: sdk.NewInt64Coin("plum", 42), + }), + AssetsFilledAmt: sdkmath.NewInt(-1), + PriceAppliedAmt: sdkmath.NewInt(-1), + }, + }, + expErr: "ask order 18 cannot be filled with \"-1apple\" assets: amount not positive", + }, + { + name: "bid with negative assets filled", + bidOFs: []*OrderFulfillment{ + { + Order: NewOrder(12).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 15), + Price: sdk.NewInt64Coin("plum", 42), + }), + AssetsFilledAmt: sdkmath.NewInt(-1), + PriceAppliedAmt: sdkmath.NewInt(-1), + }, + }, + expErr: "bid order 12 cannot be filled at price \"-1plum\": amount not positive", + }, + { + name: "ask with negative fees to pay", + askOFs: []*OrderFulfillment{ + { + Order: NewOrder(53).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 15), + Price: sdk.NewInt64Coin("plum", 42), + }), + AssetsFilledAmt: sdkmath.NewInt(15), + AssetDists: []*Distribution{{Address: "buyer1", Amount: sdkmath.NewInt(15)}}, + PriceAppliedAmt: sdkmath.NewInt(42), + PriceDists: []*Distribution{{Address: "seller1", Amount: sdkmath.NewInt(42)}}, + FeesToPay: sdk.Coins{sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(-1)}}, + }, + }, + expErr: "ask order 53 cannot pay \"-1fig\" in fees: negative amount", + }, + { + name: "bid with negative fees to pay", + bidOFs: []*OrderFulfillment{ + { + Order: NewOrder(35).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 15), + Price: sdk.NewInt64Coin("plum", 42), + }), + AssetsFilledAmt: sdkmath.NewInt(15), + AssetDists: []*Distribution{{Address: "seller1", Amount: sdkmath.NewInt(15)}}, + PriceAppliedAmt: sdkmath.NewInt(42), + PriceDists: []*Distribution{{Address: "seller1", Amount: sdkmath.NewInt(42)}}, + FeesToPay: sdk.Coins{sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(-1)}}, + }, + }, + expErr: "bid order 35 cannot pay \"-1fig\" in fees: negative amount", + }, + { + name: "two asks, three bids", + askOFs: []*OrderFulfillment{ + { + Order: NewOrder(77).WithAsk(&AskOrder{ + Seller: "seller77", + Assets: sdk.NewInt64Coin("apple", 15), + Price: sdk.NewInt64Coin("plum", 42), + }), + AssetsFilledAmt: sdkmath.NewInt(15), + AssetDists: []*Distribution{ + {Address: "buyer5511", Amount: sdkmath.NewInt(15)}, + }, + PriceAppliedAmt: sdkmath.NewInt(43), + PriceDists: []*Distribution{ + {Address: "buyer5511", Amount: sdkmath.NewInt(30)}, + {Address: "buyer78", Amount: sdkmath.NewInt(12)}, + {Address: "buyer9001", Amount: sdkmath.NewInt(1)}, + }, + FeesToPay: sdk.Coins{sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(11)}}, + }, + { + Order: NewOrder(3).WithAsk(&AskOrder{ + Seller: "seller3", + Assets: sdk.NewInt64Coin("apple", 43), + Price: sdk.NewInt64Coin("plum", 88), + }), + AssetsFilledAmt: sdkmath.NewInt(43), + AssetDists: []*Distribution{ + {Address: "buyer5511", Amount: sdkmath.NewInt(5)}, + {Address: "buyer78", Amount: sdkmath.NewInt(7)}, + {Address: "buyer9001", Amount: sdkmath.NewInt(31)}, + }, + PriceAppliedAmt: sdkmath.NewInt(90), + PriceDists: []*Distribution{ + {Address: "buyer78", Amount: sdkmath.NewInt(5)}, + {Address: "buyer9001", Amount: sdkmath.NewInt(83)}, + {Address: "buyer9001", Amount: sdkmath.NewInt(2)}, + }, + FeesToPay: nil, + }, + }, + bidOFs: []*OrderFulfillment{ + { + Order: NewOrder(5511).WithBid(&BidOrder{ + Buyer: "buyer5511", + Assets: sdk.NewInt64Coin("apple", 20), + Price: sdk.NewInt64Coin("plum", 30), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetDists: []*Distribution{ + {Address: "seller77", Amount: sdkmath.NewInt(15)}, + {Address: "seller3", Amount: sdkmath.NewInt(5)}, + }, + PriceAppliedAmt: sdkmath.NewInt(30), + PriceDists: []*Distribution{ + {Address: "seller77", Amount: sdkmath.NewInt(30)}, + }, + FeesToPay: sdk.Coins{sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(10)}}, + }, + { + Order: NewOrder(78).WithBid(&BidOrder{ + Buyer: "buyer78", + Assets: sdk.NewInt64Coin("apple", 7), + Price: sdk.NewInt64Coin("plum", 15), + }), + AssetsFilledAmt: sdkmath.NewInt(7), + AssetDists: []*Distribution{ + {Address: "seller3", Amount: sdkmath.NewInt(7)}, + }, + PriceAppliedAmt: sdkmath.NewInt(15), + PriceDists: []*Distribution{ + {Address: "seller77", Amount: sdkmath.NewInt(12)}, + {Address: "seller3", Amount: sdkmath.NewInt(3)}, + }, + FeesToPay: sdk.Coins{sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(4)}}, + }, + { + Order: NewOrder(9001).WithBid(&BidOrder{ + Buyer: "buyer9001", + Assets: sdk.NewInt64Coin("apple", 31), + Price: sdk.NewInt64Coin("plum", 86), + }), + AssetsFilledAmt: sdkmath.NewInt(31), + AssetDists: []*Distribution{ + {Address: "seller3", Amount: sdkmath.NewInt(31)}, + }, + PriceAppliedAmt: sdkmath.NewInt(86), + PriceDists: []*Distribution{ + {Address: "seller3", Amount: sdkmath.NewInt(83)}, + {Address: "seller77", Amount: sdkmath.NewInt(2)}, + {Address: "seller3", Amount: sdkmath.NewInt(1)}, + }, + FeesToPay: nil, + }, + }, + expSettlement: &Settlement{ + Transfers: []*Transfer{ + { + Inputs: []banktypes.Input{{Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 15))}}, + Outputs: []banktypes.Output{{Address: "buyer5511", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 15))}}, + }, + { + Inputs: []banktypes.Input{{Address: "seller3", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 43))}}, + Outputs: []banktypes.Output{ + {Address: "buyer5511", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 5))}, + {Address: "buyer78", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 7))}, + {Address: "buyer9001", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 31))}, + }, + }, + { + Inputs: []banktypes.Input{{Address: "buyer5511", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 30))}}, + Outputs: []banktypes.Output{{Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 30))}}, + }, + { + Inputs: []banktypes.Input{{Address: "buyer78", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 15))}}, + Outputs: []banktypes.Output{ + {Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 12))}, + {Address: "seller3", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 3))}, + }, + }, + { + Inputs: []banktypes.Input{{Address: "buyer9001", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 86))}}, + Outputs: []banktypes.Output{ + {Address: "seller3", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 84))}, + {Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 2))}, + }, + }, + }, + FeeInputs: []banktypes.Input{ + {Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("fig", 11))}, + {Address: "buyer5511", Coins: sdk.NewCoins(sdk.NewInt64Coin("fig", 10))}, + {Address: "buyer78", Coins: sdk.NewCoins(sdk.NewInt64Coin("grape", 4))}, + }, + }, + expErr: "", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + settlement := &Settlement{} + if tc.expSettlement == nil { + tc.expSettlement = &Settlement{} + } + var err error + testFunc := func() { + err = buildTransfers(tc.askOFs, tc.bidOFs, settlement) + } + require.NotPanics(t, testFunc, "buildTransfers") + assertions.AssertErrorValue(t, err, tc.expErr, "buildTransfers error") + if !assert.Equal(t, tc.expSettlement, settlement, "settlement after buildTransfers") { + expTransStrs := make([]string, len(tc.expSettlement.Transfers)) + for i, t := range tc.expSettlement.Transfers { + expTransStrs[i] = fmt.Sprintf("[%d]%s", i, transferString(t)) + } + expTrans := strings.Join(expTransStrs, "\n") + actTransStrs := make([]string, len(tc.expSettlement.Transfers)) + for i, t := range settlement.Transfers { + actTransStrs[i] = fmt.Sprintf("[%d]%s", i, transferString(t)) + } + actTrans := strings.Join(actTransStrs, "\n") + assert.Equal(t, expTrans, actTrans, "transfers (as strings)") + } + }) + } } func TestPopulateFilled(t *testing.T) { From ffe632930fdffdb776b50b95e4ceed3b8b60dfb3 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sun, 8 Oct 2023 02:37:37 -0600 Subject: [PATCH 255/309] [1658]: Unit tests on allocatePrice. --- x/exchange/fulfillment.go | 21 ++--- x/exchange/fulfillment_test.go | 161 +++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+), 13 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 567366c2b1..a93ecf2ab7 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -507,7 +507,7 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { // First pass at price distribution: Give all the asks their price. b := 0 - totalPriceFilledAmt := sdkmath.ZeroInt() + totalFilledFirstPass := sdkmath.ZeroInt() for _, askOF := range askOFs { for askOF.PriceLeftAmt.IsPositive() && bidOFs[b].PriceLeftAmt.IsPositive() { priceFilledAmt, err := GetFulfillmentPriceAmt(askOF, bidOFs[b]) @@ -517,7 +517,7 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { if err = DistributePrice(askOF, bidOFs[b], priceFilledAmt); err != nil { return err } - totalPriceFilledAmt = totalPriceFilledAmt.Add(priceFilledAmt) + totalFilledFirstPass = totalFilledFirstPass.Add(priceFilledAmt) if !bidOFs[b].PriceLeftAmt.IsPositive() { b++ } @@ -527,12 +527,12 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { // Above, we made sure that ask price <= bid price. // So here, we can assume that total price filled = ask price. // If it's also the total bid price, we're done! - if totalPriceFilledAmt.Equal(totalBidPriceAmt) { + if totalFilledFirstPass.Equal(totalBidPriceAmt) { return nil } // We also know that total price filled <= bid price, so bid price - total price left will be positive. - totalLeftoverPriceAmt := totalBidPriceAmt.Sub(totalPriceFilledAmt) + totalLeftoverPriceAmt := totalBidPriceAmt.Sub(totalFilledFirstPass) // We need an assets total so we can allocate the leftovers by assets amounts. totalAssetsAmt := sdkmath.ZeroInt() for _, askOF := range askOFs { @@ -541,11 +541,12 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { // Now, distribute the leftovers per asset (truncated). // Start over on the asks, but start where we left off on the bids (since that's the first with any price left). - a := 0 + a := -1 firstPass := true leftoverPriceAmt := totalLeftoverPriceAmt for !leftoverPriceAmt.IsZero() { - // Loop back to the first ask if we need to. + // Move on to the next ask, looping back to the first if we need to. + a++ if a == len(askOFs) { a = 0 firstPass = false @@ -563,7 +564,6 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { addPriceAmt := totalLeftoverPriceAmt.Mul(askOFs[a].AssetsFilledAmt).Quo(totalAssetsAmt) if addPriceAmt.IsZero() { if firstPass { - a++ continue } // If this isn't the first time through the asks, distribute at least one price to each ask. @@ -573,13 +573,12 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { addPriceAmt = MinSDKInt(addPriceAmt, leftoverPriceAmt) // If it can't all come out of the current bid, use the rest of what this bid has and move to the next bid. - for b < len(bidOFs) && bidOFs[b].PriceLeftAmt.LTE(addPriceAmt) { + for !addPriceAmt.IsZero() && b < len(bidOFs) && bidOFs[b].PriceLeftAmt.LTE(addPriceAmt) { bidPriceLeft := bidOFs[b].PriceLeftAmt if err := DistributePrice(askOFs[a], bidOFs[b], bidPriceLeft); err != nil { return err } addPriceAmt = addPriceAmt.Sub(bidPriceLeft) - totalPriceFilledAmt = totalPriceFilledAmt.Add(bidPriceLeft) leftoverPriceAmt = leftoverPriceAmt.Sub(bidPriceLeft) b++ } @@ -589,16 +588,12 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { if err := DistributePrice(askOFs[a], bidOFs[b], addPriceAmt); err != nil { return err } - totalPriceFilledAmt = totalPriceFilledAmt.Add(addPriceAmt) leftoverPriceAmt = leftoverPriceAmt.Sub(addPriceAmt) // If that was all of it, move on to the next. if bidOFs[b].PriceLeftAmt.IsZero() { b++ } } - - // Move on to the next ask. - a++ } return nil diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 85b306ed7e..17b64bd3a1 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -3525,6 +3525,167 @@ func TestSplitOrderFulfillments(t *testing.T) { } func TestAllocatePrice(t *testing.T) { + askOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + MarketId: 123, + Seller: fmt.Sprintf("seller%d", orderID), + Assets: sdk.NewInt64Coin("apple", assetsAmt), + Price: sdk.NewInt64Coin("peach", priceAmt), + }) + } + bidOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + MarketId: 123, + Buyer: fmt.Sprintf("buyer%d", orderID), + Assets: sdk.NewInt64Coin("apple", assetsAmt), + Price: sdk.NewInt64Coin("peach", priceAmt), + }) + } + newOF := func(order *Order, dists ...*Distribution) *OrderFulfillment { + rv := NewOrderFulfillment(order) + rv.AssetsFilledAmt, rv.AssetsUnfilledAmt = rv.AssetsUnfilledAmt, rv.AssetsFilledAmt + if len(dists) > 0 { + rv.PriceDists = dists + for _, dist := range dists { + rv.PriceAppliedAmt = rv.PriceAppliedAmt.Add(dist.Amount) + } + rv.PriceLeftAmt = rv.PriceLeftAmt.Sub(rv.PriceAppliedAmt) + } + return rv + } + dist := func(address string, amount int64) *Distribution { + return &Distribution{Address: address, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + askOFs []*OrderFulfillment + bidOFs []*OrderFulfillment + expAskOFs []*OrderFulfillment + expBidOFs []*OrderFulfillment + expErr string + }{ + { + name: "total ask price greater than total bid", + askOFs: []*OrderFulfillment{ + newOF(askOrder(3, 10, 20)), + newOF(askOrder(4, 10, 20)), + newOF(askOrder(5, 10, 20)), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(6, 10, 20)), + newOF(bidOrder(7, 10, 19)), + newOF(bidOrder(8, 10, 20)), + }, + expErr: "total ask price \"60peach\" is greater than total bid price \"59peach\"", + }, + { + name: "one ask, one bid: same price", + askOFs: []*OrderFulfillment{newOF(askOrder(3, 10, 60))}, + bidOFs: []*OrderFulfillment{newOF(bidOrder(6, 10, 60))}, + expAskOFs: []*OrderFulfillment{newOF(askOrder(3, 10, 60), dist("buyer6", 60))}, + expBidOFs: []*OrderFulfillment{newOF(bidOrder(6, 10, 60), dist("seller3", 60))}, + }, + { + name: "one ask, one bid: bid more", + askOFs: []*OrderFulfillment{newOF(askOrder(3, 10, 60))}, + bidOFs: []*OrderFulfillment{newOF(bidOrder(6, 10, 65))}, + expAskOFs: []*OrderFulfillment{newOF(askOrder(3, 10, 60), dist("buyer6", 60), dist("buyer6", 5))}, + expBidOFs: []*OrderFulfillment{newOF(bidOrder(6, 10, 65), dist("seller3", 60), dist("seller3", 5))}, + }, + { + name: "two asks, two bids: same total price, diff ask prices", + askOFs: []*OrderFulfillment{ + newOF(askOrder(3, 10, 21)), + newOF(askOrder(4, 10, 19)), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(6, 10, 20)), + newOF(bidOrder(7, 10, 20)), + }, + expAskOFs: []*OrderFulfillment{ + newOF(askOrder(3, 10, 21), dist("buyer6", 20), dist("buyer7", 1)), + newOF(askOrder(4, 10, 19), dist("buyer7", 19)), + }, + expBidOFs: []*OrderFulfillment{ + newOF(bidOrder(6, 10, 20), dist("seller3", 20)), + newOF(bidOrder(7, 10, 20), dist("seller3", 1), dist("seller4", 19)), + }, + }, + { + name: "three asks, three bids: same total price", + askOFs: []*OrderFulfillment{ + newOF(askOrder(3, 10, 25)), + newOF(askOrder(4, 10, 20)), + newOF(askOrder(5, 10, 15)), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(6, 10, 18)), + newOF(bidOrder(7, 10, 30)), + newOF(bidOrder(8, 10, 12)), + }, + expAskOFs: []*OrderFulfillment{ + newOF(askOrder(3, 10, 25), dist("buyer6", 18), dist("buyer7", 7)), + newOF(askOrder(4, 10, 20), dist("buyer7", 20)), + newOF(askOrder(5, 10, 15), dist("buyer7", 3), dist("buyer8", 12)), + }, + expBidOFs: []*OrderFulfillment{ + newOF(bidOrder(6, 10, 18), dist("seller3", 18)), + newOF(bidOrder(7, 10, 30), dist("seller3", 7), dist("seller4", 20), dist("seller5", 3)), + newOF(bidOrder(8, 10, 12), dist("seller5", 12)), + }, + }, + { + name: "three asks, three bids: bids more", + askOFs: []*OrderFulfillment{ + newOF(askOrder(3, 1, 10)), + newOF(askOrder(4, 7, 25)), + newOF(askOrder(5, 22, 30)), + }, + bidOFs: []*OrderFulfillment{ + newOF(bidOrder(6, 10, 20)), + newOF(bidOrder(7, 10, 27)), + newOF(bidOrder(8, 10, 30)), + }, + // assets total = 30 + // ask price total = 65 + // bid price total = 77 + // leftover = 12 + expAskOFs: []*OrderFulfillment{ + // 12 * 1 / 30 = 0.4 => 0, then 1 + newOF(askOrder(3, 1, 10), dist("buyer6", 10), + dist("buyer8", 1)), + // 12 * 7 / 30 = 2.8 => 2, then because there'll only be 1 left, 1 + newOF(askOrder(4, 7, 25), dist("buyer6", 10), dist("buyer7", 15), + dist("buyer8", 2), dist("buyer8", 1)), + // 12 * 22 / 30 = 8.8 => 8, then nothing because leftovers run out before getting back to it. + newOF(askOrder(5, 22, 30), dist("buyer7", 12), dist("buyer8", 18), + dist("buyer8", 8)), + }, + expBidOFs: []*OrderFulfillment{ + newOF(bidOrder(6, 10, 20), dist("seller3", 10), dist("seller4", 10)), + newOF(bidOrder(7, 10, 27), dist("seller4", 15), dist("seller5", 12)), + newOF(bidOrder(8, 10, 30), dist("seller5", 18), dist("seller4", 2), + dist("seller5", 8), dist("seller3", 1), dist("seller4", 1)), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = allocatePrice(tc.askOFs, tc.bidOFs) + } + require.NotPanics(t, testFunc, "allocatePrice") + assertions.AssertErrorValue(t, err, tc.expErr, "allocatePrice error") + if len(tc.expErr) > 0 { + return + } + assertEqualOrderFulfillmentSlices(t, tc.expAskOFs, tc.askOFs, "askOFs after allocatePrice") + assertEqualOrderFulfillmentSlices(t, tc.expBidOFs, tc.bidOFs, "bidOFs after allocatePrice") + }) + } // TODO[1658]: func TestAllocatePrice(t *testing.T) t.Skipf("not written") } From 73fdfcc40e5630276700b137a7b62a36527e21fe Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sun, 8 Oct 2023 06:00:53 -0600 Subject: [PATCH 256/309] [1658]: Remove duplicate total price check. --- x/exchange/fulfillment.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index a93ecf2ab7..3358729831 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -327,14 +327,6 @@ func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatioLookup func(de return nil, err } - // Now, check that the ask price is not more than the bid price. - totalAskPriceAmt := sumPriceLeft(askOFs) - totalBidPriceAmt := sumPriceLeft(bidOFs) - if totalAskPriceAmt.GT(totalBidPriceAmt) { - return nil, fmt.Errorf("total ask price %q is greater than total bid price %q", - askOFs[0].priceCoin(totalAskPriceAmt), bidOFs[0].priceCoin(totalBidPriceAmt)) - } - // Allocate the prices. if err := allocatePrice(askOFs, bidOFs); err != nil { return nil, err From 7599be32cdd344709013bfe2639aaa5f41581f67 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sun, 8 Oct 2023 06:01:22 -0600 Subject: [PATCH 257/309] [1658]: Unit tests on BuildSettlement. --- x/exchange/fulfillment_test.go | 826 +++++++++++++++++++++++++++++++-- 1 file changed, 788 insertions(+), 38 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 17b64bd3a1..3bd100b13c 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -1,6 +1,7 @@ package exchange import ( + "errors" "fmt" "sort" "strings" @@ -239,6 +240,62 @@ func orderFulfillmentString(f *OrderFulfillment) string { return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) } +// transfersStringsLines creates a string for each transfer. +func transfersStringsLines(ts []*Transfer) []string { + if ts == nil { + return nil + } + rv := make([]string, len(ts)) + for i, t := range ts { + rv[i] = transferString(t) + } + return rv +} + +// transferString is similar to %v except with easier to understand Coin entries. +func transferString(t *Transfer) string { + if t == nil { + return "nil" + } + inputs := bankInputsString(t.Inputs) + outputs := bankOutputsString(t.Outputs) + return fmt.Sprintf("T{Inputs:%s, Outputs: %s}", inputs, outputs) +} + +// bankInputString is similar to %v except with easier to understand Coin entries. +func bankInputString(i banktypes.Input) string { + return fmt.Sprintf("I{Address:%q,Coins:%q}", i.Address, i.Coins) +} + +// bankInputsString returns a string with all the provided inputs. +func bankInputsString(ins []banktypes.Input) string { + if ins == nil { + return "nil" + } + vals := make([]string, len(ins)) + for i, input := range ins { + vals[i] = bankInputString(input) + } + return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) +} + +// bankOutputString is similar to %v except with easier to understand Coin entries. +func bankOutputString(o banktypes.Output) string { + return fmt.Sprintf("O{Address:%q,Coins:%q}", o.Address, o.Coins) +} + +// bankOutputsString returns a string with all the provided outputs. +func bankOutputsString(outs []banktypes.Output) string { + if outs == nil { + return "nil" + } + vals := make([]string, len(outs)) + for i, output := range outs { + vals[i] = bankOutputString(output) + } + return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) +} + // assertEqualOrderFulfillments asserts that the two order fulfillments are equal. // Returns true if equal. // If not equal, and neither are nil, equality on each field is also asserted in order to help identify the problem. @@ -2507,8 +2564,737 @@ func TestSumPriceLeft(t *testing.T) { } func TestBuildSettlement(t *testing.T) { - // TODO[1658]: func TestBuildSettlement(t *testing.T) - t.Skipf("not written") + assetDenom, priceDenom := "apple", "peach" + feeDenoms := []string{"fig", "grape"} + feeCoins := func(tracer string, amts []int64) sdk.Coins { + if len(amts) == 0 { + return nil + } + if len(amts) > len(feeDenoms) { + t.Fatalf("cannot create %s with more than %d fees %v", tracer, len(feeDenoms), amts) + } + var rv sdk.Coins + for i, amt := range amts { + rv = rv.Add(sdk.NewInt64Coin(feeDenoms[i], amt)) + } + return rv + } + askOrder := func(orderID uint64, assets, price int64, allowPartial bool, fees ...int64) *Order { + if len(fees) > 1 { + t.Fatalf("cannot create ask order %d with more than 1 fees %v", orderID, fees) + } + var fee *sdk.Coin + if fc := feeCoins("", fees); !fc.IsZero() { + fee = &fc[0] + } + return NewOrder(orderID).WithAsk(&AskOrder{ + Seller: fmt.Sprintf("seller%d", orderID), + Assets: sdk.NewInt64Coin(assetDenom, assets), + Price: sdk.NewInt64Coin(priceDenom, price), + SellerSettlementFlatFee: fee, + AllowPartial: allowPartial, + }) + } + bidOrder := func(orderID uint64, assets, price int64, allowPartial bool, fees ...int64) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Buyer: fmt.Sprintf("buyer%d", orderID), + Assets: sdk.NewInt64Coin(assetDenom, assets), + Price: sdk.NewInt64Coin(priceDenom, price), + BuyerSettlementFees: feeCoins(fmt.Sprintf("bid order %d", orderID), fees), + AllowPartial: allowPartial, + }) + } + ratio := func(price, fee int64) func(denom string) (*FeeRatio, error) { + return func(denom string) (*FeeRatio, error) { + return &FeeRatio{Price: sdk.NewInt64Coin(priceDenom, price), Fee: sdk.NewInt64Coin(feeDenoms[0], fee)}, nil + } + } + filled := func(order *Order, price int64, fees ...int64) *FilledOrder { + return NewFilledOrder(order, + sdk.NewInt64Coin(priceDenom, price), + feeCoins(fmt.Sprintf("filled order %d", order), fees)) + } + assetsInput := func(orderID uint64, amount int64) banktypes.Input { + return banktypes.Input{ + Address: fmt.Sprintf("seller%d", orderID), + Coins: sdk.NewCoins(sdk.NewInt64Coin(assetDenom, amount)), + } + } + assetsOutput := func(orderID uint64, amount int64) banktypes.Output { + return banktypes.Output{ + Address: fmt.Sprintf("buyer%d", orderID), + Coins: sdk.NewCoins(sdk.NewInt64Coin(assetDenom, amount)), + } + } + priceInput := func(orderID uint64, amount int64) banktypes.Input { + return banktypes.Input{ + Address: fmt.Sprintf("buyer%d", orderID), + Coins: sdk.NewCoins(sdk.NewInt64Coin(priceDenom, amount)), + } + } + priceOutput := func(orderID uint64, amount int64) banktypes.Output { + return banktypes.Output{ + Address: fmt.Sprintf("seller%d", orderID), + Coins: sdk.NewCoins(sdk.NewInt64Coin(priceDenom, amount)), + } + } + feeInput := func(address string, amts ...int64) banktypes.Input { + return banktypes.Input{Address: address, Coins: feeCoins("bank input for "+address, amts)} + } + + tests := []struct { + name string + askOrders []*Order + bidOrders []*Order + sellerFeeRatioLookup func(denom string) (*FeeRatio, error) + expSettlement *Settlement + expErr string + }{ + { + // error from validateCanSettle + name: "no ask orders", + askOrders: []*Order{}, + bidOrders: []*Order{bidOrder(3, 1, 10, false)}, + expErr: "no ask orders provided", + }, + { + name: "error from ratio lookup", + askOrders: []*Order{askOrder(3, 1, 10, false)}, + bidOrders: []*Order{bidOrder(4, 1, 10, false)}, + sellerFeeRatioLookup: func(denom string) (*FeeRatio, error) { + return nil, errors.New("this is a test error") + }, + expErr: "this is a test error", + }, + { + name: "error from setFeesToPay", + askOrders: []*Order{askOrder(3, 1, 10, false)}, + bidOrders: []*Order{bidOrder(4, 1, 10, false)}, + sellerFeeRatioLookup: func(denom string) (*FeeRatio, error) { + return &FeeRatio{Price: sdk.NewInt64Coin("prune", 10), Fee: sdk.NewInt64Coin("fig", 1)}, nil + }, + expErr: "failed calculate ratio fee for ask order 3: cannot apply ratio 10prune:1fig to price 10peach: incorrect price denom", + }, + { + name: "one ask, three bids: last bid not used", + askOrders: []*Order{askOrder(3, 10, 20, false)}, + bidOrders: []*Order{ + bidOrder(4, 7, 14, false), + bidOrder(5, 3, 6, false), + bidOrder(6, 1, 2, false), + }, + expErr: "bid order 6 (at index 2) has no assets filled", + }, + { + name: "three asks, one bids: last ask not used", + askOrders: []*Order{ + askOrder(11, 7, 14, false), + askOrder(12, 3, 14, false), + askOrder(13, 1, 14, false), + }, + bidOrders: []*Order{bidOrder(14, 10, 20, false)}, + expErr: "ask order 13 (at index 2) has no assets filled", + }, + { + name: "two asks, two bids: same assets total, total bid price not enough", + askOrders: []*Order{ + askOrder(1, 10, 25, false), + askOrder(2, 10, 15, false), + }, + bidOrders: []*Order{ + bidOrder(8, 10, 20, false), + bidOrder(9, 10, 19, false), + }, + expErr: "total ask price \"40peach\" is greater than total bid price \"39peach\"", + }, + { + name: "two asks, two bids: ask partial, total bid price not enough", + askOrders: []*Order{ + askOrder(1, 10, 20, false), + askOrder(2, 10, 20, true), + }, + bidOrders: []*Order{ + bidOrder(8, 10, 20, false), + bidOrder(9, 9, 17, false), + }, + expErr: "total ask price \"38peach\" is greater than total bid price \"37peach\"", + }, + { + name: "two asks, two bids: bid partial, total bid price not enough", + askOrders: []*Order{ + askOrder(1, 10, 25, false), + askOrder(2, 10, 15, false), + }, + bidOrders: []*Order{ + bidOrder(8, 10, 19, false), + bidOrder(9, 11, 22, true), + }, + expErr: "total ask price \"40peach\" is greater than total bid price \"39peach\"", + }, + { + name: "one ask, one bid: both fully filled", + askOrders: []*Order{askOrder(52, 10, 100, false, 2)}, + bidOrders: []*Order{bidOrder(11, 10, 105, false, 3, 4)}, + sellerFeeRatioLookup: ratio(4, 1), + expSettlement: &Settlement{ + Transfers: []*Transfer{ + {Inputs: []banktypes.Input{assetsInput(52, 10)}, Outputs: []banktypes.Output{assetsOutput(11, 10)}}, + {Inputs: []banktypes.Input{priceInput(11, 105)}, Outputs: []banktypes.Output{priceOutput(52, 105)}}, + }, + FeeInputs: []banktypes.Input{ + feeInput("seller52", 29), + feeInput("buyer11", 3, 4), + }, + FullyFilledOrders: []*FilledOrder{ + filled(askOrder(52, 10, 100, false, 2), 105, 29), + filled(bidOrder(11, 10, 105, false, 3, 4), 105, 3, 4), + }, + }, + }, + { + name: "one ask, one bid: ask partially filled", + askOrders: []*Order{askOrder(99, 10, 100, true)}, + bidOrders: []*Order{bidOrder(15, 9, 90, false)}, + expSettlement: &Settlement{ + Transfers: []*Transfer{ + {Inputs: []banktypes.Input{assetsInput(99, 9)}, Outputs: []banktypes.Output{assetsOutput(15, 9)}}, + {Inputs: []banktypes.Input{priceInput(15, 90)}, Outputs: []banktypes.Output{priceOutput(99, 90)}}, + }, + FullyFilledOrders: []*FilledOrder{filled(bidOrder(15, 9, 90, false), 90)}, + PartialOrderFilled: filled(askOrder(99, 9, 90, true), 90), + PartialOrderLeft: askOrder(99, 1, 10, true), + }, + }, + { + name: "one ask, one bid: ask partially filled, not allowed", + askOrders: []*Order{askOrder(99, 10, 100, false)}, + bidOrders: []*Order{bidOrder(15, 9, 90, false)}, + expErr: "cannot split ask order 99 having assets \"10apple\" at \"9apple\": order does not allow partial fulfillment", + }, + { + name: "one ask, one bid: bid partially filled", + askOrders: []*Order{askOrder(8, 9, 85, false, 2)}, + bidOrders: []*Order{bidOrder(12, 10, 100, true)}, + expSettlement: &Settlement{ + Transfers: []*Transfer{ + {Inputs: []banktypes.Input{assetsInput(8, 9)}, Outputs: []banktypes.Output{assetsOutput(12, 9)}}, + {Inputs: []banktypes.Input{priceInput(12, 90)}, Outputs: []banktypes.Output{priceOutput(8, 90)}}, + }, + FeeInputs: []banktypes.Input{feeInput("seller8", 2)}, + FullyFilledOrders: []*FilledOrder{filled(askOrder(8, 9, 85, false, 2), 90, 2)}, + PartialOrderFilled: filled(bidOrder(12, 9, 90, true), 90), + PartialOrderLeft: bidOrder(12, 1, 10, true), + }, + }, + { + name: "one ask, one bid: bid partially filled, not allowed", + askOrders: []*Order{askOrder(8, 9, 85, false, 2)}, + bidOrders: []*Order{bidOrder(12, 10, 100, false)}, + expErr: "cannot split bid order 12 having assets \"10apple\" at \"9apple\": order does not allow partial fulfillment", + }, + { + name: "one ask, five bids: all fully filled", + askOrders: []*Order{askOrder(999, 130, 260, false)}, + bidOrders: []*Order{ + bidOrder(11, 71, 140, false), + bidOrder(12, 10, 20, false, 5), + bidOrder(13, 4, 12, false), + bidOrder(14, 11, 22, false), + bidOrder(15, 34, 68, false, 8), + }, + sellerFeeRatioLookup: ratio(65, 3), + expSettlement: &Settlement{ + Transfers: []*Transfer{ + { + Inputs: []banktypes.Input{assetsInput(999, 130)}, + Outputs: []banktypes.Output{ + assetsOutput(11, 71), + assetsOutput(12, 10), + assetsOutput(13, 4), + assetsOutput(14, 11), + assetsOutput(15, 34), + }, + }, + {Inputs: []banktypes.Input{priceInput(11, 140)}, Outputs: []banktypes.Output{priceOutput(999, 140)}}, + {Inputs: []banktypes.Input{priceInput(12, 20)}, Outputs: []banktypes.Output{priceOutput(999, 20)}}, + {Inputs: []banktypes.Input{priceInput(13, 12)}, Outputs: []banktypes.Output{priceOutput(999, 12)}}, + {Inputs: []banktypes.Input{priceInput(14, 22)}, Outputs: []banktypes.Output{priceOutput(999, 22)}}, + {Inputs: []banktypes.Input{priceInput(15, 68)}, Outputs: []banktypes.Output{priceOutput(999, 68)}}, + }, + FeeInputs: []banktypes.Input{ + feeInput("seller999", 13), + feeInput("buyer12", 5), + feeInput("buyer15", 8), + }, + FullyFilledOrders: []*FilledOrder{ + filled(askOrder(999, 130, 260, false), 262, 13), + filled(bidOrder(11, 71, 140, false), 140), + filled(bidOrder(12, 10, 20, false, 5), 20, 5), + filled(bidOrder(13, 4, 12, false), 12), + filled(bidOrder(14, 11, 22, false), 22), + filled(bidOrder(15, 34, 68, false, 8), 68, 8), + }, + }, + }, + { + name: "one ask, five bids: ask partially filled", + askOrders: []*Order{askOrder(999, 131, 262, true)}, + bidOrders: []*Order{ + bidOrder(11, 71, 140, false), + bidOrder(12, 10, 20, false), + bidOrder(13, 4, 12, false), + bidOrder(14, 11, 22, false), + bidOrder(15, 34, 68, false), + }, + expSettlement: &Settlement{ + Transfers: []*Transfer{ + { + Inputs: []banktypes.Input{assetsInput(999, 130)}, + Outputs: []banktypes.Output{ + assetsOutput(11, 71), + assetsOutput(12, 10), + assetsOutput(13, 4), + assetsOutput(14, 11), + assetsOutput(15, 34), + }, + }, + {Inputs: []banktypes.Input{priceInput(11, 140)}, Outputs: []banktypes.Output{priceOutput(999, 140)}}, + {Inputs: []banktypes.Input{priceInput(12, 20)}, Outputs: []banktypes.Output{priceOutput(999, 20)}}, + {Inputs: []banktypes.Input{priceInput(13, 12)}, Outputs: []banktypes.Output{priceOutput(999, 12)}}, + {Inputs: []banktypes.Input{priceInput(14, 22)}, Outputs: []banktypes.Output{priceOutput(999, 22)}}, + {Inputs: []banktypes.Input{priceInput(15, 68)}, Outputs: []banktypes.Output{priceOutput(999, 68)}}, + }, + FeeInputs: nil, + FullyFilledOrders: []*FilledOrder{ + filled(bidOrder(11, 71, 140, false), 140), + filled(bidOrder(12, 10, 20, false), 20), + filled(bidOrder(13, 4, 12, false), 12), + filled(bidOrder(14, 11, 22, false), 22), + filled(bidOrder(15, 34, 68, false), 68), + }, + PartialOrderFilled: filled(askOrder(999, 130, 260, true), 262), + PartialOrderLeft: askOrder(999, 1, 2, true), + }, + }, + { + name: "one ask, five bids: bid partially filled", + askOrders: []*Order{askOrder(999, 130, 260, false)}, + bidOrders: []*Order{ + bidOrder(11, 71, 140, false), + bidOrder(12, 10, 20, false), + bidOrder(13, 4, 12, false), + bidOrder(14, 11, 22, false), + bidOrder(15, 35, 70, true), + }, + expSettlement: &Settlement{ + Transfers: []*Transfer{ + { + Inputs: []banktypes.Input{assetsInput(999, 130)}, + Outputs: []banktypes.Output{ + assetsOutput(11, 71), + assetsOutput(12, 10), + assetsOutput(13, 4), + assetsOutput(14, 11), + assetsOutput(15, 34), + }, + }, + {Inputs: []banktypes.Input{priceInput(11, 140)}, Outputs: []banktypes.Output{priceOutput(999, 140)}}, + {Inputs: []banktypes.Input{priceInput(12, 20)}, Outputs: []banktypes.Output{priceOutput(999, 20)}}, + {Inputs: []banktypes.Input{priceInput(13, 12)}, Outputs: []banktypes.Output{priceOutput(999, 12)}}, + {Inputs: []banktypes.Input{priceInput(14, 22)}, Outputs: []banktypes.Output{priceOutput(999, 22)}}, + {Inputs: []banktypes.Input{priceInput(15, 68)}, Outputs: []banktypes.Output{priceOutput(999, 68)}}, + }, + FeeInputs: nil, + FullyFilledOrders: []*FilledOrder{ + filled(askOrder(999, 130, 260, false), 262), + filled(bidOrder(11, 71, 140, false), 140), + filled(bidOrder(12, 10, 20, false), 20), + filled(bidOrder(13, 4, 12, false), 12), + filled(bidOrder(14, 11, 22, false), 22), + }, + PartialOrderFilled: filled(bidOrder(15, 34, 68, true), 68), + PartialOrderLeft: bidOrder(15, 1, 2, true), + }, + }, + { + name: "five ask, one bids: all fully filled", + askOrders: []*Order{ + askOrder(51, 37, 74, false, 1), + askOrder(52, 21, 42, false, 2), + askOrder(53, 15, 30, false, 3), + askOrder(54, 9, 18, false, 4), + askOrder(55, 55, 110, false, 5), + }, + bidOrders: []*Order{bidOrder(777, 137, 280, false, 7)}, + expSettlement: &Settlement{ + Transfers: []*Transfer{ + {Inputs: []banktypes.Input{assetsInput(51, 37)}, Outputs: []banktypes.Output{assetsOutput(777, 37)}}, + {Inputs: []banktypes.Input{assetsInput(52, 21)}, Outputs: []banktypes.Output{assetsOutput(777, 21)}}, + {Inputs: []banktypes.Input{assetsInput(53, 15)}, Outputs: []banktypes.Output{assetsOutput(777, 15)}}, + {Inputs: []banktypes.Input{assetsInput(54, 9)}, Outputs: []banktypes.Output{assetsOutput(777, 9)}}, + {Inputs: []banktypes.Input{assetsInput(55, 55)}, Outputs: []banktypes.Output{assetsOutput(777, 55)}}, + { + Inputs: []banktypes.Input{priceInput(777, 280)}, + Outputs: []banktypes.Output{ + priceOutput(51, 76), + priceOutput(52, 43), + priceOutput(53, 31), + priceOutput(54, 18), + priceOutput(55, 112), + }, + }, + }, + FeeInputs: []banktypes.Input{ + feeInput("seller51", 1), + feeInput("seller52", 2), + feeInput("seller53", 3), + feeInput("seller54", 4), + feeInput("seller55", 5), + feeInput("buyer777", 7), + }, + FullyFilledOrders: []*FilledOrder{ + filled(askOrder(51, 37, 74, false, 1), 76, 1), + filled(askOrder(52, 21, 42, false, 2), 43, 2), + filled(askOrder(53, 15, 30, false, 3), 31, 3), + filled(askOrder(54, 9, 18, false, 4), 18, 4), + filled(askOrder(55, 55, 110, false, 5), 112, 5), + filled(bidOrder(777, 137, 280, false, 7), 280, 7), + }, + }, + }, + { + name: "five ask, one bids: ask partially filled", + askOrders: []*Order{ + askOrder(51, 37, 74, false), + askOrder(52, 21, 42, false), + askOrder(53, 15, 30, false), + askOrder(54, 9, 18, false), + askOrder(55, 57, 114, true), + }, + bidOrders: []*Order{bidOrder(777, 137, 280, false)}, + sellerFeeRatioLookup: ratio(50, 1), + expSettlement: &Settlement{ + Transfers: []*Transfer{ + {Inputs: []banktypes.Input{assetsInput(51, 37)}, Outputs: []banktypes.Output{assetsOutput(777, 37)}}, + {Inputs: []banktypes.Input{assetsInput(52, 21)}, Outputs: []banktypes.Output{assetsOutput(777, 21)}}, + {Inputs: []banktypes.Input{assetsInput(53, 15)}, Outputs: []banktypes.Output{assetsOutput(777, 15)}}, + {Inputs: []banktypes.Input{assetsInput(54, 9)}, Outputs: []banktypes.Output{assetsOutput(777, 9)}}, + {Inputs: []banktypes.Input{assetsInput(55, 55)}, Outputs: []banktypes.Output{assetsOutput(777, 55)}}, + { + Inputs: []banktypes.Input{priceInput(777, 280)}, + Outputs: []banktypes.Output{ + priceOutput(51, 76), + priceOutput(52, 43), + priceOutput(53, 31), + priceOutput(54, 18), + priceOutput(55, 112), + }, + }, + }, + FeeInputs: []banktypes.Input{ + feeInput("seller51", 2), + feeInput("seller52", 1), + feeInput("seller53", 1), + feeInput("seller54", 1), + feeInput("seller55", 3), + }, + FullyFilledOrders: []*FilledOrder{ + filled(askOrder(51, 37, 74, false), 76, 2), + filled(askOrder(52, 21, 42, false), 43, 1), + filled(askOrder(53, 15, 30, false), 31, 1), + filled(askOrder(54, 9, 18, false), 18, 1), + filled(bidOrder(777, 137, 280, false), 280), + }, + PartialOrderFilled: filled(askOrder(55, 55, 110, true), 112, 3), + PartialOrderLeft: askOrder(55, 2, 4, true), + }, + }, + { + name: "five ask, one bids: bid partially filled", + askOrders: []*Order{ + askOrder(51, 37, 74, false), + askOrder(52, 21, 42, false), + askOrder(53, 15, 30, false), + askOrder(54, 9, 18, false), + askOrder(55, 55, 110, false), + }, + bidOrders: []*Order{bidOrder(777, 274, 560, true)}, + expSettlement: &Settlement{ + Transfers: []*Transfer{ + {Inputs: []banktypes.Input{assetsInput(51, 37)}, Outputs: []banktypes.Output{assetsOutput(777, 37)}}, + {Inputs: []banktypes.Input{assetsInput(52, 21)}, Outputs: []banktypes.Output{assetsOutput(777, 21)}}, + {Inputs: []banktypes.Input{assetsInput(53, 15)}, Outputs: []banktypes.Output{assetsOutput(777, 15)}}, + {Inputs: []banktypes.Input{assetsInput(54, 9)}, Outputs: []banktypes.Output{assetsOutput(777, 9)}}, + {Inputs: []banktypes.Input{assetsInput(55, 55)}, Outputs: []banktypes.Output{assetsOutput(777, 55)}}, + { + Inputs: []banktypes.Input{priceInput(777, 280)}, + Outputs: []banktypes.Output{ + priceOutput(51, 76), + priceOutput(52, 43), + priceOutput(53, 31), + priceOutput(54, 18), + priceOutput(55, 112), + }, + }, + }, + FullyFilledOrders: []*FilledOrder{ + filled(askOrder(51, 37, 74, false), 76), + filled(askOrder(52, 21, 42, false), 43), + filled(askOrder(53, 15, 30, false), 31), + filled(askOrder(54, 9, 18, false), 18), + filled(askOrder(55, 55, 110, false), 112), + }, + PartialOrderFilled: filled(bidOrder(777, 137, 280, true), 280), + PartialOrderLeft: bidOrder(777, 137, 280, true), + }, + }, + { + name: "two asks, three bids: all fully filled", + askOrders: []*Order{ + askOrder(11, 100, 1000, false), + askOrder(22, 200, 2000, false), + }, + bidOrders: []*Order{ + bidOrder(33, 75, 700, false), + bidOrder(44, 130, 1302, false), + bidOrder(55, 95, 1000, false), + }, + expSettlement: &Settlement{ + Transfers: []*Transfer{ + { + Inputs: []banktypes.Input{assetsInput(11, 100)}, + Outputs: []banktypes.Output{ + assetsOutput(33, 75), + assetsOutput(44, 25), + }, + }, + { + Inputs: []banktypes.Input{assetsInput(22, 200)}, + Outputs: []banktypes.Output{ + assetsOutput(44, 105), + assetsOutput(55, 95), + }, + }, + { + Inputs: []banktypes.Input{priceInput(33, 700)}, + Outputs: []banktypes.Output{priceOutput(11, 700)}, + }, + { + Inputs: []banktypes.Input{priceInput(44, 1302)}, + Outputs: []banktypes.Output{ + priceOutput(11, 300), + priceOutput(22, 1002), + }, + }, + { + Inputs: []banktypes.Input{priceInput(55, 1000)}, + Outputs: []banktypes.Output{ + priceOutput(22, 999), + priceOutput(11, 1), + }, + }, + }, + FullyFilledOrders: []*FilledOrder{ + filled(askOrder(11, 100, 1000, false), 1001), + filled(askOrder(22, 200, 2000, false), 2001), + filled(bidOrder(33, 75, 700, false), 700), + filled(bidOrder(44, 130, 1302, false), 1302), + filled(bidOrder(55, 95, 1000, false), 1000), + }, + }, + }, + { + name: "two asks, three bids: ask partially filled", + askOrders: []*Order{ + askOrder(11, 100, 1000, false), + askOrder(22, 300, 3000, true), + }, + bidOrders: []*Order{ + bidOrder(33, 75, 700, false), + bidOrder(44, 130, 1302, false), + bidOrder(55, 95, 1000, false), + }, + sellerFeeRatioLookup: ratio(100, 1), + expSettlement: &Settlement{ + Transfers: []*Transfer{ + { + Inputs: []banktypes.Input{assetsInput(11, 100)}, + Outputs: []banktypes.Output{ + assetsOutput(33, 75), + assetsOutput(44, 25), + }, + }, + { + Inputs: []banktypes.Input{assetsInput(22, 200)}, + Outputs: []banktypes.Output{ + assetsOutput(44, 105), + assetsOutput(55, 95), + }, + }, + { + Inputs: []banktypes.Input{priceInput(33, 700)}, + Outputs: []banktypes.Output{priceOutput(11, 700)}, + }, + { + Inputs: []banktypes.Input{priceInput(44, 1302)}, + Outputs: []banktypes.Output{ + priceOutput(11, 300), + priceOutput(22, 1002), + }, + }, + { + Inputs: []banktypes.Input{priceInput(55, 1000)}, + Outputs: []banktypes.Output{ + priceOutput(22, 999), + priceOutput(11, 1), + }, + }, + }, + FeeInputs: []banktypes.Input{ + feeInput("seller11", 11), + feeInput("seller22", 21), + }, + FullyFilledOrders: []*FilledOrder{ + filled(askOrder(11, 100, 1000, false), 1001, 11), + filled(bidOrder(33, 75, 700, false), 700), + filled(bidOrder(44, 130, 1302, false), 1302), + filled(bidOrder(55, 95, 1000, false), 1000), + }, + PartialOrderFilled: filled(askOrder(22, 200, 2000, true), 2001, 21), + PartialOrderLeft: askOrder(22, 100, 1000, true), + }, + }, + { + name: "two asks, three bids: ask partially filled, not allowed", + askOrders: []*Order{ + askOrder(11, 100, 1000, true), + askOrder(22, 300, 3000, false), + }, + bidOrders: []*Order{ + bidOrder(33, 75, 700, true), + bidOrder(44, 130, 1302, true), + bidOrder(55, 95, 1000, true), + }, + expErr: "cannot split ask order 22 having assets \"300apple\" at \"200apple\": order does not allow partial fulfillment", + }, + { + name: "two asks, three bids: bid partially filled", + askOrders: []*Order{ + askOrder(11, 100, 1000, false), + askOrder(22, 200, 2000, false), + }, + bidOrders: []*Order{ + bidOrder(33, 75, 700, false), + bidOrder(44, 130, 1352, false), + bidOrder(55, 100, 1000, true, 40, 20), + }, + expSettlement: &Settlement{ + Transfers: []*Transfer{ + { + Inputs: []banktypes.Input{assetsInput(11, 100)}, + Outputs: []banktypes.Output{ + assetsOutput(33, 75), + assetsOutput(44, 25), + }, + }, + { + Inputs: []banktypes.Input{assetsInput(22, 200)}, + Outputs: []banktypes.Output{ + assetsOutput(44, 105), + assetsOutput(55, 95), + }, + }, + { + Inputs: []banktypes.Input{priceInput(33, 700)}, + Outputs: []banktypes.Output{priceOutput(11, 700)}, + }, + { + Inputs: []banktypes.Input{priceInput(44, 1352)}, + Outputs: []banktypes.Output{ + priceOutput(11, 300), + priceOutput(22, 1052), + }, + }, + { + Inputs: []banktypes.Input{priceInput(55, 950)}, + Outputs: []banktypes.Output{ + priceOutput(22, 949), + priceOutput(11, 1), + }, + }, + }, + FeeInputs: []banktypes.Input{feeInput("buyer55", 38, 19)}, + FullyFilledOrders: []*FilledOrder{ + filled(askOrder(11, 100, 1000, false), 1001), + filled(askOrder(22, 200, 2000, false), 2001), + filled(bidOrder(33, 75, 700, false), 700), + filled(bidOrder(44, 130, 1352, false), 1352), + }, + PartialOrderFilled: filled(bidOrder(55, 95, 950, true, 38, 19), 950, 38, 19), + PartialOrderLeft: bidOrder(55, 5, 50, true, 2, 1), + }, + }, + { + name: "two asks, three bids: bid partially filled, not allowed", + askOrders: []*Order{ + askOrder(11, 100, 1000, true), + askOrder(22, 200, 2000, true), + }, + bidOrders: []*Order{ + bidOrder(33, 75, 700, true), + bidOrder(44, 130, 1352, true), + bidOrder(55, 100, 1000, false, 40, 20), + }, + sellerFeeRatioLookup: nil, + expSettlement: nil, + expErr: "", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if tc.sellerFeeRatioLookup == nil { + tc.sellerFeeRatioLookup = func(denom string) (*FeeRatio, error) { + return nil, nil + } + } + var settlement *Settlement + var err error + testFunc := func() { + settlement, err = BuildSettlement(tc.askOrders, tc.bidOrders, tc.sellerFeeRatioLookup) + } + require.NotPanics(t, testFunc, "BuildSettlement") + assertions.RequireErrorValue(t, err, tc.expErr, "BuildSettlement error") + if !assert.Equal(t, tc.expSettlement, settlement, "BuildSettlement result") { + // Doing each field on its own now to try to help pinpoint the differences. + expTrans := transfersStringsLines(tc.expSettlement.Transfers) + actTrans := transfersStringsLines(settlement.Transfers) + assert.Equal(t, expTrans, actTrans, "Transfers (as strings)") + + expFeeInputs := bankInputsString(tc.expSettlement.FeeInputs) + actFeeInputs := bankInputsString(settlement.FeeInputs) + assert.Equal(t, expFeeInputs, actFeeInputs, "FeeInputs (as strings)") + + expFilledIds := make([]uint64, len(tc.expSettlement.FullyFilledOrders)) + for i, fo := range tc.expSettlement.FullyFilledOrders { + expFilledIds[i] = fo.GetOrderID() + } + actFilledIds := make([]uint64, len(settlement.FullyFilledOrders)) + for i, fo := range settlement.FullyFilledOrders { + actFilledIds[i] = fo.GetOrderID() + } + if assert.Equal(t, expFilledIds, actFilledIds, "FullyFilledOrders ids") { + // If they're the same ids, compare each individually. + for i := range tc.expSettlement.FullyFilledOrders { + assert.Equal(t, tc.expSettlement.FullyFilledOrders[i], settlement.FullyFilledOrders[i], "FullyFilledOrders[%d]", i) + } + } + + assert.Equal(t, tc.expSettlement.PartialOrderFilled, settlement.PartialOrderFilled, "PartialOrderFilled") + assert.Equal(t, tc.expSettlement.PartialOrderLeft, settlement.PartialOrderLeft, "PartialOrderLeft") + } + }) + } } func TestValidateCanSettle(t *testing.T) { @@ -3686,8 +4472,6 @@ func TestAllocatePrice(t *testing.T) { assertEqualOrderFulfillmentSlices(t, tc.expBidOFs, tc.bidOFs, "bidOFs after allocatePrice") }) } - // TODO[1658]: func TestAllocatePrice(t *testing.T) - t.Skipf("not written") } func TestSetFeesToPay(t *testing.T) { @@ -10103,40 +10887,6 @@ func TestBuildSettlementTransfers(t *testing.T) { } } -// transferString is similar to %v except with easier to understand Coin entries. -func transferString(t *Transfer) string { - if t == nil { - return "nil" - } - inputs := "nil" - if t.Inputs != nil { - inputVals := make([]string, len(t.Inputs)) - for i, input := range t.Inputs { - inputVals[i] = bankInputString(input) - } - inputs = fmt.Sprintf("[%s]", strings.Join(inputVals, ", ")) - } - outputs := "nil" - if t.Outputs != nil { - outputVals := make([]string, len(t.Outputs)) - for i, output := range t.Outputs { - outputVals[i] = bankOutputString(output) - } - outputs = fmt.Sprintf("[%s]", strings.Join(outputVals, ", ")) - } - return fmt.Sprintf("T{Inputs:%s, Outputs: %s}", inputs, outputs) -} - -// bankInputString is similar to %v except with easier to understand Coin entries. -func bankInputString(i banktypes.Input) string { - return fmt.Sprintf("I{Address:%q,Coins:%q}", i.Address, i.Coins) -} - -// bankOutputString is similar to %v except with easier to understand Coin entries. -func bankOutputString(o banktypes.Output) string { - return fmt.Sprintf("O{Address:%q,Coins:%q}", o.Address, o.Coins) -} - func TestGetAssetTransfer2(t *testing.T) { coin := func(amt int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amt)} From 1b06d49d0e22d4ede57560ed93b2f57dad8771ba Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sun, 8 Oct 2023 06:40:07 -0600 Subject: [PATCH 258/309] [1658]: Clean up the now-unneeded order fulfillment stuff. Make the OrderFulfillment type private since it's only used as an intermediate object during processing. --- x/exchange/fulfillment.go | 1302 +-- x/exchange/fulfillment_test.go | 13723 ++++++++++--------------------- 2 files changed, 4584 insertions(+), 10441 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 3358729831..c1d2b1181f 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -10,37 +10,163 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) -// OrderSplit contains an order, and the asset and price amounts that should come out of it. -// TODO[1658]: Remove this struct. -type OrderSplit struct { - // Order fulfillment associated with this split. - Order *OrderFulfillment - // Assets is the amount of assets from the order involved in this split. - Assets sdk.Coin - // Price is the amount of the price from the order involved in this split. - Price sdk.Coin +// Transfer contains bank inputs and outputs indicating a transfer that needs to be made. +type Transfer struct { + // Inputs are the inputs that make up this transfer. + Inputs []banktypes.Input + // Outputs are the outputs that make up this transfer. + Outputs []banktypes.Output +} + +// Settlement contains information on how a set of orders is to be settled. +type Settlement struct { + // Transfers are all of the inputs and outputs needed to facilitate movement of assets and price. + Transfers []*Transfer + // FeeInputs are the inputs needed to facilitate payment of order fees. + FeeInputs []banktypes.Input + // FullyFilledOrders are all the orders that are fully filled in this settlement. + // If there is an order that's being partially filled, it will not be included in this list. + FullyFilledOrders []*FilledOrder + // PartialOrderFilled is a partially filled order with amounts indicating how much was filled. + // This is not included in FullyFilledOrders. + PartialOrderFilled *FilledOrder + // PartialOrderLeft is what's left of the partially filled order. + PartialOrderLeft *Order +} + +// BuildSettlement processes the provided orders, identifying how the provided orders can be settled. +func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatioLookup func(denom string) (*FeeRatio, error)) (*Settlement, error) { + if err := validateCanSettle(askOrders, bidOrders); err != nil { + return nil, err + } + + askOFs := newOrderFulfillments(askOrders) + bidOFs := newOrderFulfillments(bidOrders) + + // Allocate the assets first. + if err := allocateAssets(askOFs, bidOFs); err != nil { + return nil, err + } + + settlement := &Settlement{} + // Identify any partial order and update its entry in the order fulfillments. + if err := splitPartial(askOFs, bidOFs, settlement); err != nil { + return nil, err + } + + // Allocate the prices. + if err := allocatePrice(askOFs, bidOFs); err != nil { + return nil, err + } + + // Set the fees in the fulfillments + sellerFeeRatio, err := sellerFeeRatioLookup(askOFs[0].GetPrice().Denom) + if err != nil { + return nil, err + } + if err = setFeesToPay(askOFs, bidOFs, sellerFeeRatio); err != nil { + return nil, err + } + + // Make sure everything adds up. + if err = validateFulfillments(askOFs, bidOFs); err != nil { + return nil, err + } + + // Create the transfers + if err = buildTransfers(askOFs, bidOFs, settlement); err != nil { + return nil, err + } + + // Indicate what's been filled in full and partially. + populateFilled(askOFs, bidOFs, settlement) + + return settlement, nil +} + +// indexedAddrAmts is a set of addresses and amounts. +type indexedAddrAmts struct { + // addrs are a list of all addresses that have amounts. + addrs []string + // amts are a list of the coin amounts for each address (by slice index). + amts []sdk.Coins + // indexes are the index value for each address. + indexes map[string]int +} + +func newIndexedAddrAmts() *indexedAddrAmts { + return &indexedAddrAmts{ + indexes: make(map[string]int), + } +} + +// add adds the coins to the given address. +// Panics if a provided coin is invalid. +func (i *indexedAddrAmts) add(addr string, coins ...sdk.Coin) { + for _, coin := range coins { + if err := coin.Validate(); err != nil { + panic(fmt.Errorf("cannot index and add invalid coin amount %q", coin)) + } + } + n, known := i.indexes[addr] + if !known { + n = len(i.addrs) + i.indexes[addr] = n + i.addrs = append(i.addrs, addr) + i.amts = append(i.amts, sdk.NewCoins()) + } + i.amts[n] = i.amts[n].Add(coins...) +} + +// getAsInputs returns all the entries as bank Inputs. +// Panics if this is nil, has no addrs, or has a negative coin amount. +func (i *indexedAddrAmts) getAsInputs() []banktypes.Input { + if i == nil || len(i.addrs) == 0 { + return nil + } + rv := make([]banktypes.Input, len(i.addrs)) + for n, addr := range i.addrs { + if !i.amts[n].IsAllPositive() { + panic(fmt.Errorf("invalid indexed amount %q for address %q: cannot be zero or negative", addr, i.amts[n])) + } + rv[n] = banktypes.Input{Address: addr, Coins: i.amts[n]} + } + return rv +} + +// getAsOutputs returns all the entries as bank Outputs. +// Panics if this is nil, has no addrs, or has a negative coin amount. +func (i *indexedAddrAmts) getAsOutputs() []banktypes.Output { + if i == nil || len(i.addrs) == 0 { + return nil + } + rv := make([]banktypes.Output, len(i.addrs)) + for n, addr := range i.addrs { + if !i.amts[n].IsAllPositive() { + panic(fmt.Errorf("invalid indexed amount %q for address %q: cannot be zero or negative", addr, i.amts[n])) + } + rv[n] = banktypes.Output{Address: addr, Coins: i.amts[n]} + } + return rv } -// Distribution indicates an address and an amount that will either go to, or come from that address. -type Distribution struct { - // Address is an bech32 address string +// distribution indicates an address and an amount that will either go to, or come from that address. +type distribution struct { + // Address is a bech32 address string Address string // Amount is the amount that will either go to, or come from that address. Amount sdkmath.Int } -// OrderFulfillment is used to figure out how an order should be fulfilled. -type OrderFulfillment struct { +// orderFulfillment is used to figure out how an order should be fulfilled. +type orderFulfillment struct { // Order is the original order with all its information. Order *Order - // Splits contains information on the orders being used to fulfill this order. - Splits []*OrderSplit // TODO[1658]: Remove this field. - // AssetDists contains distribution info for this order's assets. - AssetDists []*Distribution + AssetDists []*distribution // AssetDists contains distribution info for this order's price. - PriceDists []*Distribution + PriceDists []*distribution // AssetsFilledAmt is the total amount of assets being fulfilled for the order. AssetsFilledAmt sdkmath.Int @@ -53,175 +179,140 @@ type OrderFulfillment struct { // PriceLeftAmt is the price that has not yet been fulfilled for the order. // This can be negative for ask orders that are being filled at a higher price than requested. PriceLeftAmt sdkmath.Int - - // IsFinalized is set to true once Finalize() is called without error. - IsFinalized bool // FeesToPay is the amount of settlement fees the order owner should pay to settle this order. - // This is only set during Finalize(). FeesToPay sdk.Coins - // OrderFeesLeft is the amount fees settlement left to pay (if this order is only partially filled). - // This is only set during Finalize(). - OrderFeesLeft sdk.Coins - // PriceFilledAmt is the amount of the order price that is being filled. - // This is only set during Finalize(). - PriceFilledAmt sdkmath.Int - // PriceUnfilledAmt is the amount of the order price that is not being filled. - // This is only set during Finalize(). - PriceUnfilledAmt sdkmath.Int } -var _ OrderI = (*OrderFulfillment)(nil) +var _ OrderI = (*orderFulfillment)(nil) -// NewOrderFulfillment creates a new OrderFulfillment wrapping the provided order. -func NewOrderFulfillment(order *Order) *OrderFulfillment { - return &OrderFulfillment{ +// newOrderFulfillment creates a new orderFulfillment wrapping the provided order. +func newOrderFulfillment(order *Order) *orderFulfillment { + return &orderFulfillment{ Order: order, AssetsFilledAmt: sdkmath.ZeroInt(), AssetsUnfilledAmt: order.GetAssets().Amount, PriceAppliedAmt: sdkmath.ZeroInt(), PriceLeftAmt: order.GetPrice().Amount, - PriceFilledAmt: sdkmath.ZeroInt(), - PriceUnfilledAmt: sdkmath.ZeroInt(), } } -// NewOrderFulfillments creates a new OrderFulfillment for each of the provided orders. -func NewOrderFulfillments(orders []*Order) []*OrderFulfillment { - rv := make([]*OrderFulfillment, len(orders)) +// newOrderFulfillments creates a new orderFulfillment for each of the provided orders. +func newOrderFulfillments(orders []*Order) []*orderFulfillment { + rv := make([]*orderFulfillment, len(orders)) for i, o := range orders { - rv[i] = NewOrderFulfillment(o) + rv[i] = newOrderFulfillment(o) } return rv } -// assetCoin returns a coin with the given amount and the same denom as this order's assets. -func (f OrderFulfillment) assetCoin(amt sdkmath.Int) sdk.Coin { +// AssetCoin returns a coin with the given amount and the same denom as this order's assets. +func (f orderFulfillment) AssetCoin(amt sdkmath.Int) sdk.Coin { return sdk.Coin{Denom: f.GetAssets().Denom, Amount: amt} } -// priceCoin returns a coin with the given amount and the same denom as this order's price. -func (f OrderFulfillment) priceCoin(amt sdkmath.Int) sdk.Coin { +// PriceCoin returns a coin with the given amount and the same denom as this order's price. +func (f orderFulfillment) PriceCoin(amt sdkmath.Int) sdk.Coin { return sdk.Coin{Denom: f.GetPrice().Denom, Amount: amt} } // GetAssetsFilled gets the coin value of the assets that have been filled in this fulfillment. -func (f OrderFulfillment) GetAssetsFilled() sdk.Coin { - return f.assetCoin(f.AssetsFilledAmt) +func (f orderFulfillment) GetAssetsFilled() sdk.Coin { + return f.AssetCoin(f.AssetsFilledAmt) } // GetAssetsUnfilled gets the coin value of the assets left to fill in this fulfillment. -func (f OrderFulfillment) GetAssetsUnfilled() sdk.Coin { - return f.assetCoin(f.AssetsUnfilledAmt) +func (f orderFulfillment) GetAssetsUnfilled() sdk.Coin { + return f.AssetCoin(f.AssetsUnfilledAmt) } // GetPriceApplied gets the coin value of the price that has been filled in this fulfillment. -func (f OrderFulfillment) GetPriceApplied() sdk.Coin { - return f.priceCoin(f.PriceAppliedAmt) +func (f orderFulfillment) GetPriceApplied() sdk.Coin { + return f.PriceCoin(f.PriceAppliedAmt) } // GetPriceLeft gets the coin value of the price left to fill in this fulfillment. -func (f OrderFulfillment) GetPriceLeft() sdk.Coin { - return f.priceCoin(f.PriceLeftAmt) -} - -// GetPriceFilled gets the coin value of the price filled in this fulfillment. -func (f OrderFulfillment) GetPriceFilled() sdk.Coin { - return f.priceCoin(f.PriceFilledAmt) -} - -// GetPriceUnfilled gets the coin value of the price unfilled in this fulfillment. -func (f OrderFulfillment) GetPriceUnfilled() sdk.Coin { - return f.priceCoin(f.PriceUnfilledAmt) -} - -// IsFullyFilled returns true if this fulfillment's order has been fully accounted for. -func (f OrderFulfillment) IsFullyFilled() bool { - return !f.AssetsUnfilledAmt.IsPositive() -} - -// IsCompletelyUnfulfilled returns true if nothing in this order has been filled. -func (f OrderFulfillment) IsCompletelyUnfulfilled() bool { - return f.AssetsFilledAmt.IsZero() +func (f orderFulfillment) GetPriceLeft() sdk.Coin { + return f.PriceCoin(f.PriceLeftAmt) } // GetOrderID gets this fulfillment's order's id. -func (f OrderFulfillment) GetOrderID() uint64 { +func (f orderFulfillment) GetOrderID() uint64 { return f.Order.GetOrderID() } // IsAskOrder returns true if this is an ask order. -func (f OrderFulfillment) IsAskOrder() bool { +func (f orderFulfillment) IsAskOrder() bool { return f.Order.IsAskOrder() } // IsBidOrder returns true if this is an ask order. -func (f OrderFulfillment) IsBidOrder() bool { +func (f orderFulfillment) IsBidOrder() bool { return f.Order.IsBidOrder() } // GetMarketID gets this fulfillment's order's market id. -func (f OrderFulfillment) GetMarketID() uint32 { +func (f orderFulfillment) GetMarketID() uint32 { return f.Order.GetMarketID() } // GetOwner gets this fulfillment's order's owner. -func (f OrderFulfillment) GetOwner() string { +func (f orderFulfillment) GetOwner() string { return f.Order.GetOwner() } // GetAssets gets this fulfillment's order's assets. -func (f OrderFulfillment) GetAssets() sdk.Coin { +func (f orderFulfillment) GetAssets() sdk.Coin { return f.Order.GetAssets() } // GetPrice gets this fulfillment's order's price. -func (f OrderFulfillment) GetPrice() sdk.Coin { +func (f orderFulfillment) GetPrice() sdk.Coin { return f.Order.GetPrice() } // GetSettlementFees gets this fulfillment's order's settlement fees. -func (f OrderFulfillment) GetSettlementFees() sdk.Coins { +func (f orderFulfillment) GetSettlementFees() sdk.Coins { return f.Order.GetSettlementFees() } // PartialFillAllowed gets this fulfillment's order's AllowPartial flag. -func (f OrderFulfillment) PartialFillAllowed() bool { +func (f orderFulfillment) PartialFillAllowed() bool { return f.Order.PartialFillAllowed() } // GetOrderType gets this fulfillment's order's type string. -func (f OrderFulfillment) GetOrderType() string { +func (f orderFulfillment) GetOrderType() string { return f.Order.GetOrderType() } // GetOrderTypeByte gets this fulfillment's order's type byte. -func (f OrderFulfillment) GetOrderTypeByte() byte { +func (f orderFulfillment) GetOrderTypeByte() byte { return f.Order.GetOrderTypeByte() } // GetHoldAmount gets this fulfillment's order's hold amount. -func (f OrderFulfillment) GetHoldAmount() sdk.Coins { +func (f orderFulfillment) GetHoldAmount() sdk.Coins { return f.Order.GetHoldAmount() } // DistributeAssets records the distribution of assets in the provided amount to/from the given order. -func (f *OrderFulfillment) DistributeAssets(order OrderI, amount sdkmath.Int) error { +func (f *orderFulfillment) DistributeAssets(order OrderI, amount sdkmath.Int) error { if f.AssetsUnfilledAmt.LT(amount) { return fmt.Errorf("cannot fill %s order %d having assets left %q with %q from %s order %d: overfill", - f.GetOrderType(), f.GetOrderID(), f.GetAssetsUnfilled(), f.assetCoin(amount), order.GetOrderType(), order.GetOrderID()) + f.GetOrderType(), f.GetOrderID(), f.GetAssetsUnfilled(), f.AssetCoin(amount), order.GetOrderType(), order.GetOrderID()) } f.AssetsUnfilledAmt = f.AssetsUnfilledAmt.Sub(amount) f.AssetsFilledAmt = f.AssetsFilledAmt.Add(amount) - f.AssetDists = append(f.AssetDists, &Distribution{ + f.AssetDists = append(f.AssetDists, &distribution{ Address: order.GetOwner(), Amount: amount, }) return nil } -// DistributeAssets records the distribution of assets in the provided amount to/from the given fulfillments. -func DistributeAssets(of1, of2 *OrderFulfillment, amount sdkmath.Int) error { +// distributeAssets records the distribution of assets in the provided amount to/from the given fulfillments. +func distributeAssets(of1, of2 *orderFulfillment, amount sdkmath.Int) error { errs := []error{ of1.DistributeAssets(of2, amount), of2.DistributeAssets(of1, amount), @@ -230,23 +321,23 @@ func DistributeAssets(of1, of2 *OrderFulfillment, amount sdkmath.Int) error { } // DistributePrice records the distribution of price in the provided amount to/from the given order. -func (f *OrderFulfillment) DistributePrice(order OrderI, amount sdkmath.Int) error { +func (f *orderFulfillment) DistributePrice(order OrderI, amount sdkmath.Int) error { if f.PriceLeftAmt.LT(amount) && f.IsBidOrder() { return fmt.Errorf("cannot fill %s order %d having price left %q to %s order %d at a price of %q: overfill", - f.GetOrderType(), f.GetOrderID(), f.GetPriceLeft(), order.GetOrderType(), order.GetOrderID(), f.priceCoin(amount)) + f.GetOrderType(), f.GetOrderID(), f.GetPriceLeft(), order.GetOrderType(), order.GetOrderID(), f.PriceCoin(amount)) } f.PriceLeftAmt = f.PriceLeftAmt.Sub(amount) f.PriceAppliedAmt = f.PriceAppliedAmt.Add(amount) - f.PriceDists = append(f.PriceDists, &Distribution{ + f.PriceDists = append(f.PriceDists, &distribution{ Address: order.GetOwner(), Amount: amount, }) return nil } -// DistributePrice records the distribution of price in the provided amount to/from the given fulfillments. -func DistributePrice(of1, of2 *OrderFulfillment, amount sdkmath.Int) error { +// distributePrice records the distribution of price in the provided amount to/from the given fulfillments. +func distributePrice(of1, of2 *orderFulfillment, amount sdkmath.Int) error { errs := []error{ of1.DistributePrice(of2, amount), of2.DistributePrice(of1, amount), @@ -256,7 +347,7 @@ func DistributePrice(of1, of2 *OrderFulfillment, amount sdkmath.Int) error { // SplitOrder splits this order on the amount of assets filled. // This order fulfillment is updated to have the filled order, and the unfilled portion is returned. -func (f *OrderFulfillment) SplitOrder() (*Order, error) { +func (f *orderFulfillment) SplitOrder() (*Order, error) { filled, unfilled, err := f.Order.Split(f.AssetsFilledAmt) if err != nil { return nil, err @@ -269,7 +360,7 @@ func (f *OrderFulfillment) SplitOrder() (*Order, error) { } // AsFilledOrder creates a FilledOrder from this order fulfillment. -func (f OrderFulfillment) AsFilledOrder() *FilledOrder { +func (f orderFulfillment) AsFilledOrder() *FilledOrder { return NewFilledOrder(f.Order, f.GetPriceApplied(), f.FeesToPay) } @@ -283,7 +374,7 @@ func sumAssetsAndPrice(orders []*Order) (assets sdk.Coins, price sdk.Coins) { } // sumPriceLeft gets the sum of price left of the provided fulfillments. -func sumPriceLeft(fulfillments []*OrderFulfillment) sdkmath.Int { +func sumPriceLeft(fulfillments []*orderFulfillment) sdkmath.Int { rv := sdkmath.ZeroInt() for _, f := range fulfillments { rv = rv.Add(f.PriceLeftAmt) @@ -291,72 +382,6 @@ func sumPriceLeft(fulfillments []*OrderFulfillment) sdkmath.Int { return rv } -// Settlement contains information on how a set of orders is to be settled. -type Settlement struct { - // Transfers are all of the inputs and outputs needed to facilitate movement of assets and price. - Transfers []*Transfer - // FeeInputs are the inputs needed to facilitate payment of order fees. - FeeInputs []banktypes.Input - // FullyFilledOrders are all the orders that are fully filled in this settlement. - // If there is an order that's being partially filled, it will not be included in this list. - FullyFilledOrders []*FilledOrder - // PartialOrderFilled is a partially filled order with amounts indicating how much was filled. - // This is not included in FullyFilledOrders. - PartialOrderFilled *FilledOrder - // PartialOrderLeft is what's left of the partially filled order. - PartialOrderLeft *Order -} - -// BuildSettlement processes the provided orders, identifying how the provided orders can be settled. -func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatioLookup func(denom string) (*FeeRatio, error)) (*Settlement, error) { - if err := validateCanSettle(askOrders, bidOrders); err != nil { - return nil, err - } - - askOFs := NewOrderFulfillments(askOrders) - bidOFs := NewOrderFulfillments(bidOrders) - - // Allocate the assets first. - if err := allocateAssets(askOFs, bidOFs); err != nil { - return nil, err - } - - settlement := &Settlement{} - // Identify any partial order and update its entry in the order fulfillments. - if err := splitPartial(askOFs, bidOFs, settlement); err != nil { - return nil, err - } - - // Allocate the prices. - if err := allocatePrice(askOFs, bidOFs); err != nil { - return nil, err - } - - // Set the fees in the fulfillments - sellerFeeRatio, err := sellerFeeRatioLookup(askOFs[0].GetPrice().Denom) - if err != nil { - return nil, err - } - if err = setFeesToPay(askOFs, bidOFs, sellerFeeRatio); err != nil { - return nil, err - } - - // Make sure everything adds up. - if err = validateFulfillments(askOFs, bidOFs); err != nil { - return nil, err - } - - // Create the transfers - if err = buildTransfers(askOFs, bidOFs, settlement); err != nil { - return nil, err - } - - // Indicate what's been filled in full and partially. - populateFilled(askOFs, bidOFs, settlement) - - return settlement, nil -} - // validateCanSettle does some superficial checking of the provided orders to make sure we can try to settle them. func validateCanSettle(askOrders, bidOrders []*Order) error { var errs []error @@ -421,14 +446,14 @@ func validateCanSettle(askOrders, bidOrders []*Order) error { } // allocateAssets distributes the assets among the fulfillments. -func allocateAssets(askOFs, bidOFs []*OrderFulfillment) error { +func allocateAssets(askOFs, bidOFs []*orderFulfillment) error { a, b := 0, 0 for a < len(askOFs) && b < len(bidOFs) { - assetsFilledAmt, err := GetFulfillmentAssetsAmt(askOFs[a], bidOFs[b]) + assetsFilledAmt, err := getFulfillmentAssetsAmt(askOFs[a], bidOFs[b]) if err != nil { return err } - if err = DistributeAssets(askOFs[a], bidOFs[b], assetsFilledAmt); err != nil { + if err = distributeAssets(askOFs[a], bidOFs[b], assetsFilledAmt); err != nil { return err } @@ -449,18 +474,30 @@ func allocateAssets(askOFs, bidOFs []*OrderFulfillment) error { return nil } +// getFulfillmentAssetsAmt figures out the assets that can be fulfilled with the two provided orders. +func getFulfillmentAssetsAmt(of1, of2 *orderFulfillment) (sdkmath.Int, error) { + if !of1.AssetsUnfilledAmt.IsPositive() || !of2.AssetsUnfilledAmt.IsPositive() { + return sdkmath.ZeroInt(), fmt.Errorf("cannot fill %s order %d having assets left %q "+ + "with %s order %d having assets left %q: zero or negative assets left", + of1.GetOrderType(), of1.GetOrderID(), of1.GetAssetsUnfilled(), + of2.GetOrderType(), of2.GetOrderID(), of2.GetAssetsUnfilled()) + } + + return MinSDKInt(of1.AssetsUnfilledAmt, of2.AssetsUnfilledAmt), nil +} + // splitPartial checks the provided fulfillments for a partial order and splits it out, updating the applicable fulfillment. // This will possibly populate the PartialOrderLeft in the provided Settlement. -func splitPartial(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) error { +func splitPartial(askOFs, bidOFs []*orderFulfillment, settlement *Settlement) error { if err := splitOrderFulfillments(askOFs, settlement); err != nil { return err } return splitOrderFulfillments(bidOFs, settlement) } -// splitOrderFulfillments checks each of the OrderFulfillment for partial (or incomplete) fills. -// If an appropriate partial fill is found, its OrderFulfillment is update and Settlement.PartialOrderLeft is set. -func splitOrderFulfillments(fulfillments []*OrderFulfillment, settlement *Settlement) error { +// splitOrderFulfillments checks each of the orderFulfillment for partial (or incomplete) fills. +// If an appropriate partial fill is found, its orderFulfillment is update and Settlement.PartialOrderLeft is set. +func splitOrderFulfillments(fulfillments []*orderFulfillment, settlement *Settlement) error { lastI := len(fulfillments) - 1 for i, f := range fulfillments { if f.AssetsFilledAmt.IsZero() { @@ -488,13 +525,13 @@ func splitOrderFulfillments(fulfillments []*OrderFulfillment, settlement *Settle } // allocatePrice distributes the prices among the fulfillments. -func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { +func allocatePrice(askOFs, bidOFs []*orderFulfillment) error { // Check that the total ask price is not more than the total bid price. totalAskPriceAmt := sumPriceLeft(askOFs) totalBidPriceAmt := sumPriceLeft(bidOFs) if totalAskPriceAmt.GT(totalBidPriceAmt) { return fmt.Errorf("total ask price %q is greater than total bid price %q", - askOFs[0].priceCoin(totalAskPriceAmt), bidOFs[0].priceCoin(totalBidPriceAmt)) + askOFs[0].PriceCoin(totalAskPriceAmt), bidOFs[0].PriceCoin(totalBidPriceAmt)) } // First pass at price distribution: Give all the asks their price. @@ -502,11 +539,11 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { totalFilledFirstPass := sdkmath.ZeroInt() for _, askOF := range askOFs { for askOF.PriceLeftAmt.IsPositive() && bidOFs[b].PriceLeftAmt.IsPositive() { - priceFilledAmt, err := GetFulfillmentPriceAmt(askOF, bidOFs[b]) + priceFilledAmt, err := getFulfillmentPriceAmt(askOF, bidOFs[b]) if err != nil { return err } - if err = DistributePrice(askOF, bidOFs[b], priceFilledAmt); err != nil { + if err = distributePrice(askOF, bidOFs[b], priceFilledAmt); err != nil { return err } totalFilledFirstPass = totalFilledFirstPass.Add(priceFilledAmt) @@ -548,8 +585,8 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { if b >= len(bidOFs) { panic(fmt.Errorf("total ask price %q, total bid price %q, difference %q, left to allocate %q: "+ "no bid orders left to allocate leftovers from", - askOFs[0].priceCoin(totalAskPriceAmt), bidOFs[0].priceCoin(totalBidPriceAmt), - askOFs[0].priceCoin(totalLeftoverPriceAmt), askOFs[0].priceCoin(leftoverPriceAmt))) + askOFs[0].PriceCoin(totalAskPriceAmt), bidOFs[0].PriceCoin(totalBidPriceAmt), + askOFs[0].PriceCoin(totalLeftoverPriceAmt), askOFs[0].PriceCoin(leftoverPriceAmt))) } // Figure out how much additional price this order should get. @@ -567,7 +604,7 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { // If it can't all come out of the current bid, use the rest of what this bid has and move to the next bid. for !addPriceAmt.IsZero() && b < len(bidOFs) && bidOFs[b].PriceLeftAmt.LTE(addPriceAmt) { bidPriceLeft := bidOFs[b].PriceLeftAmt - if err := DistributePrice(askOFs[a], bidOFs[b], bidPriceLeft); err != nil { + if err := distributePrice(askOFs[a], bidOFs[b], bidPriceLeft); err != nil { return err } addPriceAmt = addPriceAmt.Sub(bidPriceLeft) @@ -577,7 +614,7 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { // If there's still additional price left, it can all come out of the current bid. if !addPriceAmt.IsZero() && b < len(bidOFs) { - if err := DistributePrice(askOFs[a], bidOFs[b], addPriceAmt); err != nil { + if err := distributePrice(askOFs[a], bidOFs[b], addPriceAmt); err != nil { return err } leftoverPriceAmt = leftoverPriceAmt.Sub(addPriceAmt) @@ -591,8 +628,20 @@ func allocatePrice(askOFs, bidOFs []*OrderFulfillment) error { return nil } +// getFulfillmentPriceAmt figures out the price that can be fulfilled with the two provided orders. +func getFulfillmentPriceAmt(of1, of2 *orderFulfillment) (sdkmath.Int, error) { + if !of1.PriceLeftAmt.IsPositive() || !of2.PriceLeftAmt.IsPositive() { + return sdkmath.ZeroInt(), fmt.Errorf("cannot fill %s order %d having price left %q "+ + "with %s order %d having price left %q: zero or negative price left", + of1.GetOrderType(), of1.GetOrderID(), of1.GetPriceLeft(), + of2.GetOrderType(), of2.GetOrderID(), of2.GetPriceLeft()) + } + + return MinSDKInt(of1.PriceLeftAmt, of2.PriceLeftAmt), nil +} + // setFeesToPay sets the FeesToPay on each fulfillment. -func setFeesToPay(askOFs, bidOFs []*OrderFulfillment, sellerFeeRatio *FeeRatio) error { +func setFeesToPay(askOFs, bidOFs []*orderFulfillment, sellerFeeRatio *FeeRatio) error { var errs []error for _, askOF := range askOFs { feesToPay := askOF.GetSettlementFees() @@ -616,7 +665,7 @@ func setFeesToPay(askOFs, bidOFs []*OrderFulfillment, sellerFeeRatio *FeeRatio) } // validateFulfillments runs .Validate on each fulfillment, returning any problems. -func validateFulfillments(askOFs, bidOFs []*OrderFulfillment) error { +func validateFulfillments(askOFs, bidOFs []*orderFulfillment) error { var errs []error for _, askOF := range askOFs { if err := askOF.Validate(); err != nil { @@ -631,31 +680,69 @@ func validateFulfillments(askOFs, bidOFs []*OrderFulfillment) error { return errors.Join(errs...) } -// buildTransfers creates the transfers, inputs for fee payments, -// and fee total and sets those fields in the provided Settlement. -// This will populate the Transfers and FeeInputs fields in the provided Settlement. -func buildTransfers(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) error { - var errs []error - indexedFees := newIndexedAddrAmts() - transfers := make([]*Transfer, 0, len(askOFs)+len(bidOFs)) - - record := func(of *OrderFulfillment, getter func(fulfillment *OrderFulfillment) (*Transfer, error)) { - assetTrans, err := getter(of) - if err != nil { - errs = append(errs, err) - } else { - transfers = append(transfers, assetTrans) - } - - if !of.FeesToPay.IsZero() { - if of.FeesToPay.IsAnyNegative() { - errs = append(errs, fmt.Errorf("%s order %d cannot pay %q in fees: negative amount", - of.GetOrderType(), of.GetOrderID(), of.FeesToPay)) +// Validate makes sure the assets filled and price applied are acceptable for this fulfillment. +func (f orderFulfillment) Validate() (err error) { + defer func() { + if r := recover(); r != nil { + if e, ok := r.(error); ok { + err = e } else { - indexedFees.add(of.GetOwner(), of.FeesToPay...) + err = fmt.Errorf("%v", r) } } - } + }() + + orderPrice := f.GetPrice() + switch { + case f.IsAskOrder(): + if orderPrice.Amount.GT(f.PriceAppliedAmt) { + return fmt.Errorf("%s order %d price %q is more than price filled %q", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceApplied()) + } + case f.IsBidOrder(): + if !orderPrice.Amount.Equal(f.PriceAppliedAmt) { + return fmt.Errorf("%s order %d price %q is not equal to price filled %q", + f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceApplied()) + } + default: + // This is here in case something new implements SubOrderI but a case isn't added here. + panic(fmt.Errorf("%s order %d: unknown order type", f.GetOrderType(), f.GetOrderID())) + } + + orderAssets := f.GetAssets() + if !orderAssets.Amount.Equal(f.AssetsFilledAmt) { + return fmt.Errorf("%s order %d assets %q does not equal filled assets %q", + f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled()) + } + + return nil +} + +// buildTransfers creates the transfers, inputs for fee payments, +// and fee total and sets those fields in the provided Settlement. +// This will populate the Transfers and FeeInputs fields in the provided Settlement. +func buildTransfers(askOFs, bidOFs []*orderFulfillment, settlement *Settlement) error { + var errs []error + indexedFees := newIndexedAddrAmts() + transfers := make([]*Transfer, 0, len(askOFs)+len(bidOFs)) + + record := func(of *orderFulfillment, getter func(fulfillment *orderFulfillment) (*Transfer, error)) { + assetTrans, err := getter(of) + if err != nil { + errs = append(errs, err) + } else { + transfers = append(transfers, assetTrans) + } + + if !of.FeesToPay.IsZero() { + if of.FeesToPay.IsAnyNegative() { + errs = append(errs, fmt.Errorf("%s order %d cannot pay %q in fees: negative amount", + of.GetOrderType(), of.GetOrderID(), of.FeesToPay)) + } else { + indexedFees.add(of.GetOwner(), of.FeesToPay...) + } + } + } // If we got both the asset and price transfers from all OrderFulfillments, we'd be doubling // up on what's being traded. So we need to only get each from only one list. @@ -685,7 +772,7 @@ func buildTransfers(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) // populateFilled creates all the FilledOrder entries and stores them in the provided Settlement. // This will populate the FullyFilledOrders and PartialOrderFilled fields in the provided Settlement. -func populateFilled(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) { +func populateFilled(askOFs, bidOFs []*orderFulfillment, settlement *Settlement) { settlement.FullyFilledOrders = make([]*FilledOrder, 0, len(askOFs)+len(bidOFs)) for _, f := range askOFs { @@ -706,7 +793,7 @@ func populateFilled(askOFs, bidOFs []*OrderFulfillment, settlement *Settlement) } // getAssetTransfer gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. -func getAssetTransfer(f *OrderFulfillment) (*Transfer, error) { +func getAssetTransfer(f *orderFulfillment) (*Transfer, error) { assetsFilled := f.GetAssetsFilled() if !assetsFilled.Amount.IsPositive() { return nil, fmt.Errorf("%s order %d cannot be filled with %q assets: amount not positive", @@ -718,15 +805,15 @@ func getAssetTransfer(f *OrderFulfillment) (*Transfer, error) { for _, dist := range f.AssetDists { if !dist.Amount.IsPositive() { return nil, fmt.Errorf("%s order %d cannot have %q assets in a transfer: amount not positive", - f.GetOrderType(), f.GetOrderID(), f.assetCoin(dist.Amount)) + f.GetOrderType(), f.GetOrderID(), f.AssetCoin(dist.Amount)) } - indexedDists.add(dist.Address, f.assetCoin(dist.Amount)) + indexedDists.add(dist.Address, f.AssetCoin(dist.Amount)) sumDists = sumDists.Add(dist.Amount) } if !sumDists.Equal(assetsFilled.Amount) { return nil, fmt.Errorf("%s order %d assets filled %q does not equal assets distributed %q", - f.GetOrderType(), f.GetOrderID(), assetsFilled, f.assetCoin(sumDists)) + f.GetOrderType(), f.GetOrderID(), assetsFilled, f.AssetCoin(sumDists)) } if f.IsAskOrder() { @@ -747,7 +834,7 @@ func getAssetTransfer(f *OrderFulfillment) (*Transfer, error) { } // getPriceTransfer gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. -func getPriceTransfer(f *OrderFulfillment) (*Transfer, error) { +func getPriceTransfer(f *orderFulfillment) (*Transfer, error) { priceApplied := f.GetPriceApplied() if !priceApplied.Amount.IsPositive() { return nil, fmt.Errorf("%s order %d cannot be filled at price %q: amount not positive", @@ -759,15 +846,15 @@ func getPriceTransfer(f *OrderFulfillment) (*Transfer, error) { for _, dist := range f.PriceDists { if !dist.Amount.IsPositive() { return nil, fmt.Errorf("%s order %d cannot have price %q in a transfer: amount not positive", - f.GetOrderType(), f.GetOrderID(), f.priceCoin(dist.Amount)) + f.GetOrderType(), f.GetOrderID(), f.PriceCoin(dist.Amount)) } - indexedDists.add(dist.Address, f.priceCoin(dist.Amount)) + indexedDists.add(dist.Address, f.PriceCoin(dist.Amount)) sumDists = sumDists.Add(dist.Amount) } if !sumDists.Equal(priceApplied.Amount) { return nil, fmt.Errorf("%s order %d price filled %q does not equal price distributed %q", - f.GetOrderType(), f.GetOrderID(), priceApplied, f.priceCoin(sumDists)) + f.GetOrderType(), f.GetOrderID(), priceApplied, f.PriceCoin(sumDists)) } if f.IsAskOrder() { @@ -786,792 +873,3 @@ func getPriceTransfer(f *OrderFulfillment) (*Transfer, error) { // This is here in case a new SubTypeI is made that isn't accounted for in here. panic(fmt.Errorf("%s order %d: unknown order type", f.GetOrderType(), f.GetOrderID())) } - -// Apply adjusts this order fulfillment using the provided info. -func (f *OrderFulfillment) Apply(order *OrderFulfillment, assetsAmt, priceAmt sdkmath.Int) error { - assets := sdk.NewCoin(order.GetAssets().Denom, assetsAmt) - price := sdk.NewCoin(order.GetPrice().Denom, priceAmt) - - newAssetsUnfilledAmt := f.AssetsUnfilledAmt.Sub(assetsAmt) - if newAssetsUnfilledAmt.IsNegative() { - return fmt.Errorf("cannot fill %s order %d having assets left %q with %q from %s order %d: overfill", - f.GetOrderType(), f.GetOrderID(), f.GetAssetsUnfilled(), assets, order.GetOrderType(), order.GetOrderID()) - } - - newPriceLeftAmt := f.PriceLeftAmt.Sub(priceAmt) - // ask orders are allow to go negative on price left, but bid orders are not. - if newPriceLeftAmt.IsNegative() && f.IsBidOrder() { - return fmt.Errorf("cannot fill %s order %d having price left %q to %s order %d at a price of %q: overfill", - f.GetOrderType(), f.GetOrderID(), f.GetPriceLeft(), order.GetOrderType(), order.GetOrderID(), price) - } - - f.AssetsUnfilledAmt = newAssetsUnfilledAmt - f.AssetsFilledAmt = f.AssetsFilledAmt.Add(assetsAmt) - f.PriceLeftAmt = newPriceLeftAmt - f.PriceAppliedAmt = f.PriceAppliedAmt.Add(priceAmt) - f.Splits = append(f.Splits, &OrderSplit{ - Order: order, - Assets: assets, - Price: price, - }) - return nil -} - -// ApplyLeftoverPrice increases this fulfillment and the provided split -// using info in the split and the provided amount. -func (f *OrderFulfillment) ApplyLeftoverPrice(askSplit *OrderSplit, amt sdkmath.Int) { - // Update this fulfillment to indicate that the amount has been applied. - f.PriceLeftAmt = f.PriceLeftAmt.Sub(amt) - f.PriceAppliedAmt = f.PriceAppliedAmt.Add(amt) - - // Update the ask split to include the extra amount. - askSplit.Price.Amount = askSplit.Price.Amount.Add(amt) - // And update the ask split's fulfillment similarly. - askSplit.Order.PriceLeftAmt = askSplit.Order.PriceLeftAmt.Sub(amt) - askSplit.Order.PriceAppliedAmt = askSplit.Order.PriceAppliedAmt.Add(amt) - - // Update the bid split entry for this order in the splits that the ask split has - // to indicate the extra amount from this bid. - orderID := f.GetOrderID() - for _, bidSplit := range askSplit.Order.Splits { - if bidSplit.Order.GetOrderID() == orderID { - bidSplit.Price.Amount = bidSplit.Price.Amount.Add(amt) - return - } - } - - // If we didn't find a bid split to update, something is horribly wrong. - panic(fmt.Errorf("could not apply leftover amount %s from %s order %d to %s order %d: bid split not found", - amt, f.GetOrderType(), orderID, askSplit.Order.GetOrderType(), askSplit.Order.GetOrderID())) -} - -// Finalize does some final calculations and validation for this order fulfillment. -// This order fulfillment and the ones in it maybe updated during this. -func (f *OrderFulfillment) Finalize(sellerFeeRatio *FeeRatio) (err error) { - // If this is returning an error, unset all the fields that get set in here. - defer func() { - if err != nil { - f.IsFinalized = false - f.PriceFilledAmt = sdkmath.ZeroInt() - f.PriceUnfilledAmt = sdkmath.ZeroInt() - f.FeesToPay = nil - f.OrderFeesLeft = nil - } - }() - - // AssetsFilledAmt cannot be zero here because we'll be dividing by it. - // AssetsFilledAmt cannot be negative here because we can't have negative values from the calcs. - // Checking for assets filled > zero here (instead of in Validate2) because we need to divide by it in here. - if !f.AssetsFilledAmt.IsPositive() { - return fmt.Errorf("no assets filled in %s order %d", f.GetOrderType(), f.GetOrderID()) - } - - isAskOrder, isBidOrder := f.IsAskOrder(), f.IsBidOrder() - isFullyFilled := f.IsFullyFilled() - orderFees := f.GetSettlementFees() - orderAssets := f.GetAssets() - orderPrice := f.GetPrice() - - f.PriceFilledAmt = orderPrice.Amount - f.PriceUnfilledAmt = sdkmath.ZeroInt() - f.FeesToPay = orderFees - f.OrderFeesLeft = nil - - if !isFullyFilled { - // Make sure the price can be split on a whole number, and figure out the price being filled. - priceAssets := orderPrice.Amount.Mul(f.AssetsFilledAmt) - priceRem := priceAssets.Mod(orderAssets.Amount) - if !priceRem.IsZero() { - return fmt.Errorf("%s order %d having assets %q cannot be partially filled by %q: "+ - "price %q is not evenly divisible", - f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled(), orderPrice) - } - f.PriceFilledAmt = priceAssets.Quo(orderAssets.Amount) - f.PriceUnfilledAmt = orderPrice.Amount.Sub(f.PriceFilledAmt) - - // Make sure the fees can be split on a whole number, and figure out how much is actually being paid of them. - f.FeesToPay = nil - for _, orderFee := range orderFees { - feeAssets := orderFee.Amount.Mul(f.AssetsFilledAmt) - feeRem := feeAssets.Mod(orderAssets.Amount) - if !feeRem.IsZero() { - return fmt.Errorf("%s order %d having assets %q cannot be partially filled by %q: "+ - "fee %q is not evenly divisible", - f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled(), orderFee) - } - feeAmtToPay := feeAssets.Quo(orderAssets.Amount) - f.FeesToPay = f.FeesToPay.Add(sdk.NewCoin(orderFee.Denom, feeAmtToPay)) - } - f.OrderFeesLeft = orderFees.Sub(f.FeesToPay...) - } - - switch { - case isAskOrder: - // For ask orders, we need to calculate and add the ratio fee to the fees to pay. - // This should NOT affect the order fees left. - if sellerFeeRatio != nil { - ratioFeeToPay, ferr := sellerFeeRatio.ApplyToLoosely(f.GetPriceApplied()) - if ferr != nil { - return fmt.Errorf("could not calculate %s order %d ratio fee: %w", - f.GetOrderType(), f.GetOrderID(), ferr) - } - f.FeesToPay = f.FeesToPay.Add(ratioFeeToPay) - } - case isBidOrder: - // When adding things to PriceAppliedAmt (and Splits .Price), we used truncation on the divisions. - // When calculated PriceFilledAmt, we made sure it was a whole number based on total assets being distributed. - // So, at this point, PriceAppliedAmt might be a little less than the PriceFilledAmt. - // If that's the case, we'll distribute the difference among the splits. - toDistribute := f.PriceFilledAmt.Sub(f.PriceAppliedAmt) - if toDistribute.IsPositive() { - distLeft := toDistribute - // First, go through each split, and apply the leftovers to any asks that still have price left. - for _, askSplit := range f.Splits { - if askSplit.Order.PriceLeftAmt.IsPositive() { - toDist := MinSDKInt(askSplit.Order.PriceLeftAmt, distLeft) - f.ApplyLeftoverPrice(askSplit, toDist) - distLeft = distLeft.Sub(toDist) - } - } - - // Now try to distribute the leftovers evenly weighted by assets. - // First pass, we won't default to 1 (if the calc comes up zero). - // This helps weigh larger orders that are at the end of the list. - // Once they've all had a chance, use a minimum of 1. - minOne := false - for distLeft.IsPositive() { - for _, askSplit := range f.Splits { - distAmt := toDistribute.Mul(askSplit.Assets.Amount).Quo(f.AssetsFilledAmt) - if distAmt.IsZero() { - if !minOne { - continue - } - distAmt = sdkmath.OneInt() - } - distAmt = MinSDKInt(distAmt, distLeft) - - f.ApplyLeftoverPrice(askSplit, distAmt) - - distLeft = distLeft.Sub(distAmt) - if !distLeft.IsPositive() { - break - } - } - minOne = true - } - } - } - - f.IsFinalized = true - return nil -} - -// Validate makes sure the assets filled and price applied are acceptable for this fulfillment. -func (f OrderFulfillment) Validate() (err error) { - defer func() { - if r := recover(); r != nil { - if e, ok := r.(error); ok { - err = e - } else { - err = fmt.Errorf("%v", r) - } - } - }() - - orderPrice := f.GetPrice() - switch { - case f.IsAskOrder(): - if orderPrice.Amount.GT(f.PriceAppliedAmt) { - return fmt.Errorf("%s order %d price %q is more than price filled %q", - f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceApplied()) - } - case f.IsBidOrder(): - if !orderPrice.Amount.Equal(f.PriceAppliedAmt) { - return fmt.Errorf("%s order %d price %q is not equal to price filled %q", - f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceApplied()) - } - default: - // This is here in case something new implements SubOrderI but a case isn't added here. - panic(fmt.Errorf("%s order %d: unknown order type", f.GetOrderType(), f.GetOrderID())) - } - - orderAssets := f.GetAssets() - if !orderAssets.Amount.Equal(f.AssetsFilledAmt) { - return fmt.Errorf("%s order %d assets %q does not equal filled assets %q", - f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled()) - } - - return nil -} - -// Validate2 does some final validation and sanity checking on this order fulfillment. -// It's assumed that Finalize has been called before calling this. -func (f OrderFulfillment) Validate2() error { - if _, err := f.Order.GetSubOrder(); err != nil { - return err - } - if !f.IsFinalized { - return fmt.Errorf("fulfillment for %s order %d has not been finalized", f.GetOrderType(), f.GetOrderID()) - } - - orderAssets := f.GetAssets() - if f.AssetsUnfilledAmt.IsNegative() { - return fmt.Errorf("%s order %d having assets %q has negative assets left %q after filling %q", - f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsUnfilled(), f.GetAssetsFilled()) - } - if !f.AssetsFilledAmt.IsPositive() { - return fmt.Errorf("cannot fill non-positive assets %q on %s order %d having assets %q", - f.GetAssetsFilled(), f.GetOrderType(), f.GetOrderID(), orderAssets) - } - trackedAssetsAmt := f.AssetsFilledAmt.Add(f.AssetsUnfilledAmt) - if !orderAssets.Amount.Equal(trackedAssetsAmt) { - return fmt.Errorf("tracked assets %q does not equal %s order %d assets %q", - sdk.Coin{Denom: orderAssets.Denom, Amount: trackedAssetsAmt}, f.GetOrderType(), f.GetOrderID(), orderAssets) - } - - orderPrice := f.GetPrice() - if f.PriceLeftAmt.GTE(orderPrice.Amount) { - return fmt.Errorf("price left %q is not less than %s order %d price %q", - f.GetPriceLeft(), f.GetOrderType(), f.GetOrderID(), orderPrice) - } - if !f.PriceAppliedAmt.IsPositive() { - return fmt.Errorf("cannot apply non-positive price %q to %s order %d having price %q", - f.GetPriceApplied(), f.GetOrderType(), f.GetOrderID(), orderPrice) - } - trackedPriceAmt := f.PriceAppliedAmt.Add(f.PriceLeftAmt) - if !orderPrice.Amount.Equal(trackedPriceAmt) { - return fmt.Errorf("tracked price %q does not equal %s order %d price %q", - sdk.Coin{Denom: orderPrice.Denom, Amount: trackedPriceAmt}, f.GetOrderType(), f.GetOrderID(), orderPrice) - } - if !f.PriceFilledAmt.IsPositive() { - return fmt.Errorf("cannot fill %s order %d having price %q with non-positive price %q", - f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceFilled()) - } - totalPriceAmt := f.PriceFilledAmt.Add(f.PriceUnfilledAmt) - if !orderPrice.Amount.Equal(totalPriceAmt) { - return fmt.Errorf("filled price %q plus unfilled price %q does not equal order price %q for %s order %d", - f.GetPriceFilled(), f.GetPriceUnfilled(), orderPrice, f.GetOrderType(), f.GetOrderID()) - } - if f.PriceUnfilledAmt.IsNegative() { - return fmt.Errorf("%s order %d having price %q has negative price %q after filling %q", - f.GetOrderType(), f.GetOrderID(), orderPrice, f.GetPriceUnfilled(), f.GetPriceFilled()) - } - - if len(f.Splits) == 0 { - return fmt.Errorf("no splits applied to %s order %d", f.GetOrderType(), f.GetOrderID()) - } - - var splitsAssets, splitsPrice sdk.Coins - for _, split := range f.Splits { - splitsAssets = splitsAssets.Add(split.Assets) - splitsPrice = splitsPrice.Add(split.Price) - } - - if len(splitsAssets) != 1 { - return fmt.Errorf("multiple asset denoms %q in splits applied to %s order %d having assets %q", - splitsAssets, f.GetOrderType(), f.GetOrderID(), orderAssets) - } - if splitsAssets[0].Denom != orderAssets.Denom { - return fmt.Errorf("splits asset denom %q does not equal order assets denom %q on %s order %d", - splitsAssets, orderAssets, f.GetOrderType(), f.GetOrderID()) - } - if !splitsAssets[0].Amount.Equal(f.AssetsFilledAmt) { - return fmt.Errorf("splits asset total %q does not equal filled assets %q on %s order %d", - splitsAssets, f.GetAssetsFilled(), f.GetOrderType(), f.GetOrderID()) - } - - if len(splitsPrice) != 1 { - return fmt.Errorf("multiple price denoms %q in splits applied to %s order %d having price %q", - splitsPrice, f.GetOrderType(), f.GetOrderID(), orderPrice) - } - if splitsPrice[0].Denom != orderPrice.Denom { - return fmt.Errorf("splits price denom %q does not equal order price denom %q on %s order %d", - splitsPrice, orderPrice, f.GetOrderType(), f.GetOrderID()) - } - if !splitsPrice[0].Amount.Equal(f.PriceAppliedAmt) { - return fmt.Errorf("splits price total %q does not equal filled price %q on %s order %d", - splitsPrice, f.GetPriceApplied(), f.GetOrderType(), f.GetOrderID()) - } - - orderFees := f.GetSettlementFees() - if f.OrderFeesLeft.IsAnyNegative() { - return fmt.Errorf("settlement fees left %q is negative for %s order %d having fees %q", - f.OrderFeesLeft, f.GetOrderType(), f.GetOrderID(), orderFees) - } - if _, hasNeg := orderFees.SafeSub(f.OrderFeesLeft...); hasNeg { - return fmt.Errorf("settlement fees left %q is greater than %s order %d settlement fees %q", - f.OrderFeesLeft, f.GetOrderType(), f.GetOrderID(), orderFees) - } - - isFullyFilled := f.IsFullyFilled() - if isFullyFilled { - // IsFullyFilled returns true if unfilled assets is zero or negative. - // We know from a previous check that unfilled is not negative. - // So here, we know it's zero, and don't need to check that again. - - if !f.PriceUnfilledAmt.IsZero() { - return fmt.Errorf("fully filled %s order %d has non-zero unfilled price %q", - f.GetOrderType(), f.GetOrderID(), f.GetPriceUnfilled()) - } - if !f.OrderFeesLeft.IsZero() { - return fmt.Errorf("fully filled %s order %d has non-zero settlement fees left %q", - f.GetOrderType(), f.GetOrderID(), f.OrderFeesLeft) - } - } - - switch { - case f.IsAskOrder(): - // For ask orders, the applied amount needs to be at least the filled amount. - if f.PriceAppliedAmt.LT(f.PriceFilledAmt) { - return fmt.Errorf("%s order %d having assets %q and price %q cannot be filled by %q at price %q: insufficient price", - f.GetOrderType(), f.GetOrderID(), orderAssets, orderPrice, f.GetAssetsFilled(), f.GetPriceApplied()) - } - - // If not being fully filled on an order that has some fees, make sure that there's at most 1 denom in the fees left. - if !isFullyFilled && len(orderFees) > 0 && len(f.OrderFeesLeft) > 1 { - return fmt.Errorf("partial fulfillment for %s order %d having seller settlement fees %q has multiple denoms in fees left %q", - f.GetOrderType(), f.GetOrderID(), orderFees, f.OrderFeesLeft) - } - - // For ask orders, the tracked fees must be at least the order fees. - trackedFees := f.FeesToPay.Add(f.OrderFeesLeft...) - if _, hasNeg := trackedFees.SafeSub(orderFees...); hasNeg { - return fmt.Errorf("tracked settlement fees %q is less than %s order %d settlement fees %q", - trackedFees, f.GetOrderType(), f.GetOrderID(), orderFees) - } - case f.IsBidOrder(): - if !f.PriceAppliedAmt.Equal(f.PriceFilledAmt) { - return fmt.Errorf("price applied %q does not equal price filled %q for %s order %d having price %q", - f.GetPriceApplied(), f.GetPriceFilled(), f.GetOrderType(), f.GetOrderID(), orderPrice) - } - - // We now know that price applied = filled, and applied + left = order = filled + unfilled, so left = unfilled too. - // We also know that applied > 0 and left < order. So 0 < applied < order. - // If fully filled, we know that unfilled = 0, so applied = order = filled, so we don't need to check that again here. - // If partially filled, we know that applied < order, so we don't need to check that either. - - // For bid orders, fees to pay + fees left should equal the order fees. - trackedFees := f.FeesToPay.Add(f.OrderFeesLeft...) - if !CoinsEquals(trackedFees, orderFees) { - return fmt.Errorf("tracked settlement fees %q does not equal %s order %d settlement fees %q", - trackedFees, f.GetOrderType(), f.GetOrderID(), orderFees) - } - default: - // The only way to trigger this would be to add a new order type but not add a case for it in this switch. - panic(fmt.Errorf("case missing for %T in Validate2", f.GetOrderType())) - } - - // Saving this simple check for last in the hopes that a previous error exposes why this - // order might accidentally be only partially filled. - if !isFullyFilled && !f.PartialFillAllowed() { - return fmt.Errorf("cannot fill %s order %d having assets %q with %q: order does not allow partial fill", - f.GetOrderType(), f.GetOrderID(), orderAssets, f.GetAssetsFilled()) - } - - return nil -} - -// Fulfill attempts to use the two provided order fulfillments to fulfill each other. -// The provided order fulfillments will be updated if everything goes okay. -func Fulfill(of1, of2 *OrderFulfillment) error { - order1Type := of1.Order.GetOrderType() - order2Type := of2.Order.GetOrderType() - if order1Type == order2Type { - return fmt.Errorf("cannot fulfill %s order %d with %s order %d: order type mismatch", - order1Type, of1.Order.OrderId, order2Type, of2.Order.OrderId) - } - - var askOF, bidOF *OrderFulfillment - if order1Type == OrderTypeAsk { - askOF = of1 - bidOF = of2 - } else { - askOF = of2 - bidOF = of1 - } - - askOrder, bidOrder := askOF.Order.GetAskOrder(), bidOF.Order.GetBidOrder() - if askOrder.Assets.Denom != bidOrder.Assets.Denom { - return fmt.Errorf("cannot fill bid order %d having assets %q with ask order %d having assets %q: denom mismatch", - bidOF.GetOrderID(), bidOrder.Assets, askOF.GetOrderID(), askOrder.Assets) - } - if askOrder.Price.Denom != bidOrder.Price.Denom { - return fmt.Errorf("cannot fill ask order %d having price %q with bid order %d having price %q: denom mismatch", - askOF.GetOrderID(), askOrder.Price, bidOF.GetOrderID(), bidOrder.Price) - } - - assetsAmt, err := GetFulfillmentAssetsAmt(askOF, bidOF) - if err != nil { - return err - } - - // We calculate the price amount based off the original bid order assets (as opposed to assets left) - // for consistent truncation and remainders. Once we've identified all the fulfillment relationships, - // we'll enumerate and redistribute those remainders. - priceAmt := bidOrder.Price.Amount - if !assetsAmt.Equal(bidOrder.Assets.Amount) { - priceAmt = bidOrder.Price.Amount.Mul(assetsAmt).Quo(bidOrder.Assets.Amount) - } - - askErr := askOF.Apply(bidOF, assetsAmt, priceAmt) - bidErr := bidOF.Apply(askOF, assetsAmt, priceAmt) - - return errors.Join(askErr, bidErr) -} - -// GetFulfillmentAssetsAmt figures out the assets that can be fulfilled with the two provided orders. -func GetFulfillmentAssetsAmt(of1, of2 *OrderFulfillment) (sdkmath.Int, error) { - if !of1.AssetsUnfilledAmt.IsPositive() || !of2.AssetsUnfilledAmt.IsPositive() { - return sdkmath.ZeroInt(), fmt.Errorf("cannot fill %s order %d having assets left %q "+ - "with %s order %d having assets left %q: zero or negative assets left", - of1.GetOrderType(), of1.GetOrderID(), of1.GetAssetsUnfilled(), - of2.GetOrderType(), of2.GetOrderID(), of2.GetAssetsUnfilled()) - } - - return MinSDKInt(of1.AssetsUnfilledAmt, of2.AssetsUnfilledAmt), nil -} - -// GetFulfillmentPriceAmt figures out the price that can be fulfilled with the two provided orders. -func GetFulfillmentPriceAmt(of1, of2 *OrderFulfillment) (sdkmath.Int, error) { - if !of1.PriceLeftAmt.IsPositive() || !of2.PriceLeftAmt.IsPositive() { - return sdkmath.ZeroInt(), fmt.Errorf("cannot fill %s order %d having price left %q "+ - "with %s order %d having price left %q: zero or negative price left", - of1.GetOrderType(), of1.GetOrderID(), of1.GetPriceLeft(), - of2.GetOrderType(), of2.GetOrderID(), of2.GetPriceLeft()) - } - - return MinSDKInt(of1.PriceLeftAmt, of2.PriceLeftAmt), nil -} - -// Fulfillments contains information on how orders are to be fulfilled. -type Fulfillments struct { - // AskOFs are all the ask orders and how they are to be filled. - AskOFs []*OrderFulfillment - // BidOFs are all the bid orders and how they are to be filled. - BidOFs []*OrderFulfillment - // PartialOrder contains info on an order that is only being partially filled. - // The transfers For part of its funds are included in the order fulfillments. - PartialOrder *PartialFulfillment -} - -// PartialFulfillment contains the remains of a partially filled order, and info on what was filled. -type PartialFulfillment struct { - // NewOrder is an updated version of the partially filled order with reduced amounts. - NewOrder *Order - // AssetsFilled is the amount of order assets that were filled. - AssetsFilled sdk.Coin - // PriceFilled is the amount of the order price that was filled. - PriceFilled sdk.Coin -} - -// NewPartialFulfillment creates a new PartialFulfillment using the provided OrderFulfillment information. -func NewPartialFulfillment(f *OrderFulfillment) *PartialFulfillment { - rv := &PartialFulfillment{ - NewOrder: NewOrder(f.GetOrderID()), - AssetsFilled: f.GetAssetsFilled(), - PriceFilled: f.GetPriceFilled(), - } - - if f.IsAskOrder() { - askOrder := &AskOrder{ - MarketId: f.GetMarketID(), - Seller: f.GetOwner(), - Assets: f.GetAssetsUnfilled(), - Price: f.GetPriceUnfilled(), - AllowPartial: f.PartialFillAllowed(), - } - if !f.OrderFeesLeft.IsZero() { - if len(f.OrderFeesLeft) > 1 { - panic(fmt.Errorf("partially filled ask order %d somehow has multiple denoms in fees left %q", - f.GetOrderID(), f.OrderFeesLeft)) - } - askOrder.SellerSettlementFlatFee = &f.OrderFeesLeft[0] - } - rv.NewOrder.WithAsk(askOrder) - return rv - } - - if f.IsBidOrder() { - bidOrder := &BidOrder{ - MarketId: f.GetMarketID(), - Buyer: f.GetOwner(), - Assets: f.GetAssetsUnfilled(), - Price: f.GetPriceUnfilled(), - BuyerSettlementFees: f.OrderFeesLeft, - AllowPartial: f.PartialFillAllowed(), - } - rv.NewOrder.WithBid(bidOrder) - return rv - } - - // This is here in case another order type is created, but a case for it isn't added to this func. - panic(fmt.Errorf("order %d has unknown type %q", f.GetOrderID(), f.GetOrderType())) -} - -// BuildFulfillments creates all of the ask and bid order fulfillments. -func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) (*Fulfillments, error) { - askOFs := make([]*OrderFulfillment, len(askOrders)) - for i, askOrder := range askOrders { - if !askOrder.IsAskOrder() { - return nil, fmt.Errorf("%s order %d is not an ask order but is in the askOrders list", - askOrder.GetOrderType(), askOrder.GetOrderID()) - } - askOFs[i] = NewOrderFulfillment(askOrder) - } - bidOFs := make([]*OrderFulfillment, len(bidOrders)) - for i, bidOrder := range bidOrders { - if !bidOrder.IsBidOrder() { - return nil, fmt.Errorf("%s order %d is not a bid order but is in the bidOrders list", - bidOrder.GetOrderType(), bidOrder.GetOrderID()) - } - bidOFs[i] = NewOrderFulfillment(bidOrder) - } - - var a, b int - for a < len(askOFs) && b < len(bidOFs) { - err := Fulfill(askOFs[a], bidOFs[b]) - if err != nil { - return nil, err - } - askFilled := askOFs[a].IsFullyFilled() - bidFilled := bidOFs[b].IsFullyFilled() - if !askFilled && !bidFilled { - return nil, fmt.Errorf("neither %s order %d nor %s order %d could be filled in full", - askOFs[a].GetOrderType(), askOFs[a].GetOrderID(), bidOFs[b].GetOrderType(), bidOFs[b].GetOrderID()) - } - if askFilled { - a++ - } - if bidFilled { - b++ - } - } - - // Finalize all the fulfillments. - // Need to finalize bid orders first due to possible extra price distribution. - for _, bidOF := range bidOFs { - if err := bidOF.Finalize(sellerFeeRatio); err != nil { - return nil, err - } - } - for _, askOF := range askOFs { - if err := askOF.Finalize(sellerFeeRatio); err != nil { - return nil, err - } - } - - // And make sure they're all valid. - for _, askOF := range askOFs { - if err := askOF.Validate2(); err != nil { - return nil, err - } - } - for _, bidOF := range bidOFs { - if err := bidOF.Validate2(); err != nil { - return nil, err - } - } - - // Make sure none of them are partially filled except possibly the last in each list. - var partialFulfillments []*OrderFulfillment - lastAskI, lastBidI := len(askOFs)-1, len(bidOFs)-1 - for i, askOF := range askOFs { - if !askOF.IsFullyFilled() { - if i != lastAskI { - return nil, fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last ask order provided", - askOF.GetOrderType(), askOF.GetOrderID(), i) - } - partialFulfillments = append(partialFulfillments, askOF) - } - } - for i, bidOF := range bidOFs { - if !bidOF.IsFullyFilled() { - if i != lastBidI { - return nil, fmt.Errorf("%s order %d (at index %d) is not filled in full and is not the last bid order provided", - bidOF.GetOrderType(), bidOF.GetOrderID(), i) - } - partialFulfillments = append(partialFulfillments, bidOF) - } - } - - // And make sure that only one order is being partially filled. - if len(partialFulfillments) > 1 { - return nil, fmt.Errorf("%s order %d and %s order %d cannot both be partially filled", - partialFulfillments[0].GetOrderType(), partialFulfillments[0].GetOrderID(), - partialFulfillments[1].GetOrderType(), partialFulfillments[1].GetOrderID()) - } - - rv := &Fulfillments{ - AskOFs: askOFs, - BidOFs: bidOFs, - } - - if len(partialFulfillments) > 0 { - rv.PartialOrder = NewPartialFulfillment(partialFulfillments[0]) - } - - return rv, nil -} - -// indexedAddrAmts is a set of addresses and amounts. -type indexedAddrAmts struct { - // addrs are a list of all addresses that have amounts. - addrs []string - // amts are a list of the coin amounts for each address (by slice index). - amts []sdk.Coins - // indexes are the index value for each address. - indexes map[string]int -} - -func newIndexedAddrAmts() *indexedAddrAmts { - return &indexedAddrAmts{ - indexes: make(map[string]int), - } -} - -// add adds the coins to the given address. -// Panics if a provided coin is invalid. -func (i *indexedAddrAmts) add(addr string, coins ...sdk.Coin) { - for _, coin := range coins { - if err := coin.Validate(); err != nil { - panic(fmt.Errorf("cannot index and add invalid coin amount %q", coin)) - } - } - n, known := i.indexes[addr] - if !known { - n = len(i.addrs) - i.indexes[addr] = n - i.addrs = append(i.addrs, addr) - i.amts = append(i.amts, sdk.NewCoins()) - } - i.amts[n] = i.amts[n].Add(coins...) -} - -// getAsInputs returns all the entries as bank Inputs. -// Panics if this is nil, has no addrs, or has a negative coin amount. -func (i *indexedAddrAmts) getAsInputs() []banktypes.Input { - if i == nil || len(i.addrs) == 0 { - return nil - } - rv := make([]banktypes.Input, len(i.addrs)) - for n, addr := range i.addrs { - if !i.amts[n].IsAllPositive() { - panic(fmt.Errorf("invalid indexed amount %q for address %q: cannot be zero or negative", addr, i.amts[n])) - } - rv[n] = banktypes.Input{Address: addr, Coins: i.amts[n]} - } - return rv -} - -// getAsOutputs returns all the entries as bank Outputs. -// Panics if this is nil, has no addrs, or has a negative coin amount. -func (i *indexedAddrAmts) getAsOutputs() []banktypes.Output { - if i == nil || len(i.addrs) == 0 { - return nil - } - rv := make([]banktypes.Output, len(i.addrs)) - for n, addr := range i.addrs { - if !i.amts[n].IsAllPositive() { - panic(fmt.Errorf("invalid indexed amount %q for address %q: cannot be zero or negative", addr, i.amts[n])) - } - rv[n] = banktypes.Output{Address: addr, Coins: i.amts[n]} - } - return rv -} - -// Transfer contains bank inputs and outputs indicating a transfer that needs to be made. -type Transfer struct { - // Inputs are the inputs that make up this transfer. - Inputs []banktypes.Input - // Outputs are the outputs that make up this transfer. - Outputs []banktypes.Output -} - -// SettlementTransfers has everything needed to do all the transfers for a settlement. -type SettlementTransfers struct { - // OrderTransfers are all of the asset and price transfers needed to facilitate a settlement. - OrderTransfers []*Transfer - // FeeInputs are all of the inputs needed to facilitate payment of fees to a market. - FeeInputs []banktypes.Input -} - -// BuildSettlementTransfers creates all the order transfers needed for the provided fulfillments. -// Assumes that all fulfillments have passed Validate2. -// Panics if any amounts are negative. -func BuildSettlementTransfers(f *Fulfillments) *SettlementTransfers { - allOFs := make([]*OrderFulfillment, 0, len(f.AskOFs)+len(f.BidOFs)) - allOFs = append(allOFs, f.AskOFs...) - allOFs = append(allOFs, f.BidOFs...) - - indexedFees := newIndexedAddrAmts() - rv := &SettlementTransfers{ - OrderTransfers: make([]*Transfer, 0, len(allOFs)*2), - } - - for _, of := range allOFs { - rv.OrderTransfers = append(rv.OrderTransfers, GetAssetTransfer2(of), GetPriceTransfer2(of)) - if !of.FeesToPay.IsZero() { - // Using NewCoins in here as a last-ditch negative amount panic check. - fees := sdk.NewCoins(of.FeesToPay...) - indexedFees.add(of.GetOwner(), fees...) - } - } - - rv.FeeInputs = indexedFees.getAsInputs() - - return rv -} - -// GetAssetTransfer2 gets the inputs and outputs to facilitate the transfers of assets for this order fulfillment. -// Assumes that the fulfillment has passed Validate2 already. -// Panics if any amounts are negative or if it's neither a bid nor ask order. -func GetAssetTransfer2(f *OrderFulfillment) *Transfer { - indexedSplits := newIndexedAddrAmts() - for _, split := range f.Splits { - indexedSplits.add(split.Order.GetOwner(), split.Assets) - } - - // Using NewCoins in here (instead of Coins{...}) as a last-ditch negative amount panic check. - if f.IsAskOrder() { - return &Transfer{ - Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, - Outputs: indexedSplits.getAsOutputs(), - } - } - if f.IsBidOrder() { - return &Transfer{ - Inputs: indexedSplits.getAsInputs(), - Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetAssetsFilled())}}, - } - } - - // panicking in here if there's an error since it really should have happened earlier anyway. - panic(fmt.Errorf("unknown order type %T", f.Order.GetOrder())) -} - -// GetPriceTransfer2 gets the inputs and outputs to facilitate the transfers for the price of this order fulfillment. -// Assumes that the fulfillment has passed Validate2 already. -// Panics if any amounts are negative or if it's neither a bid nor ask order. -func GetPriceTransfer2(f *OrderFulfillment) *Transfer { - indexedSplits := newIndexedAddrAmts() - for _, split := range f.Splits { - indexedSplits.add(split.Order.GetOwner(), split.Price) - } - - // Using NewCoins in here (instead of Coins{...}) as a last-ditch negative amount panic check. - if f.IsAskOrder() { - return &Transfer{ - Inputs: indexedSplits.getAsInputs(), - Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceApplied())}}, - } - } - if f.IsBidOrder() { - return &Transfer{ - Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(f.GetPriceApplied())}}, - Outputs: indexedSplits.getAsOutputs(), - } - } - - // panicking in here if there's an error since it really should have happened earlier anyway. - panic(fmt.Errorf("unknown order type %T", f.Order.GetOrder())) -} diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 3bd100b13c..c39087f035 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -37,167 +37,82 @@ func copySlice[T any](vals []T, copier func(T) T) []T { return rv } -// copyOrderSplit creates a copy of this order split. -// Unlike the other copiers in here, the Order is not deep copied, it will be the same reference. -func copyOrderSplit(split *OrderSplit) *OrderSplit { - if split == nil { - return nil - } - - return &OrderSplit{ - // just copying the reference here to prevent infinite recursion. - Order: split.Order, - Assets: copyCoin(split.Assets), - Price: copyCoin(split.Price), - } -} - -// copyOrderSplits copies a slice of order splits. -func copyOrderSplits(splits []*OrderSplit) []*OrderSplit { - if splits == nil { - return nil - } - - rv := make([]*OrderSplit, len(splits)) - for i, split := range splits { - rv[i] = copyOrderSplit(split) - } - return rv -} - // copyDistribution copies a distribution. -func copyDistribution(dist *Distribution) *Distribution { +func copyDistribution(dist *distribution) *distribution { if dist == nil { return nil } - return &Distribution{ + return &distribution{ Address: dist.Address, Amount: copySDKInt(dist.Amount), } } // copyDistributions copies a slice of distributions. -func copyDistributions(dists []*Distribution) []*Distribution { +func copyDistributions(dists []*distribution) []*distribution { return copySlice(dists, copyDistribution) } // copyOrderFulfillment returns a deep copy of an order fulfillment. -func copyOrderFulfillment(f *OrderFulfillment) *OrderFulfillment { +func copyOrderFulfillment(f *orderFulfillment) *orderFulfillment { if f == nil { return nil } - return &OrderFulfillment{ + return &orderFulfillment{ Order: copyOrder(f.Order), - Splits: copyOrderSplits(f.Splits), AssetDists: copyDistributions(f.AssetDists), PriceDists: copyDistributions(f.PriceDists), AssetsFilledAmt: copySDKInt(f.AssetsFilledAmt), AssetsUnfilledAmt: copySDKInt(f.AssetsUnfilledAmt), PriceAppliedAmt: copySDKInt(f.PriceAppliedAmt), PriceLeftAmt: copySDKInt(f.PriceLeftAmt), - IsFinalized: f.IsFinalized, FeesToPay: copyCoins(f.FeesToPay), - OrderFeesLeft: copyCoins(f.OrderFeesLeft), - PriceFilledAmt: copySDKInt(f.PriceFilledAmt), - PriceUnfilledAmt: copySDKInt(f.PriceUnfilledAmt), } } // copyOrderFulfillments returns a deep copy of a slice of order fulfillments. -func copyOrderFulfillments(fs []*OrderFulfillment) []*OrderFulfillment { +func copyOrderFulfillments(fs []*orderFulfillment) []*orderFulfillment { return copySlice(fs, copyOrderFulfillment) } -// copyInput returns a deep copy of a bank input. -func copyInput(input banktypes.Input) banktypes.Input { - return banktypes.Input{ - Address: input.Address, - Coins: copyCoins(input.Coins), - } -} - -// copyInputs returns a deep copy of a slice of bank inputs. -func copyInputs(inputs []banktypes.Input) []banktypes.Input { - return copySlice(inputs, copyInput) -} - -// copyOutput returns a deep copy of a bank output. -func copyOutput(output banktypes.Output) banktypes.Output { - return banktypes.Output{ - Address: output.Address, - Coins: copyCoins(output.Coins), - } -} - -// copyOutputs returns a deep copy of a slice of bank outputs. -func copyOutputs(outputs []banktypes.Output) []banktypes.Output { - return copySlice(outputs, copyOutput) -} - -// copyTransfer returns a deep copy of a transfer. -func copyTransfer(t *Transfer) *Transfer { - if t == nil { +// copyIndexedAddrAmts creates a deep copy of an indexedAddrAmts. +func copyIndexedAddrAmts(orig *indexedAddrAmts) *indexedAddrAmts { + if orig == nil { return nil } - return &Transfer{ - Inputs: copyInputs(t.Inputs), - Outputs: copyOutputs(t.Outputs), - } -} - -// copyTransfers returns a deep copy of a slice of transfers. -func copyTransfers(ts []*Transfer) []*Transfer { - return copySlice(ts, copyTransfer) -} -// copyFilledOrder returns a deep copy of a filled order. -func copyFilledOrder(f *FilledOrder) *FilledOrder { - if f == nil { - return nil - } - return &FilledOrder{ - order: copyOrder(f.order), - actualPrice: copyCoin(f.actualPrice), - actualFees: copyCoins(f.actualFees), + rv := &indexedAddrAmts{ + addrs: nil, + amts: nil, + indexes: nil, } -} - -// copyFilledOrders returns a deep copy of a slice of filled order. -func copyFilledOrders(fs []*FilledOrder) []*FilledOrder { - return copySlice(fs, copyFilledOrder) -} -// orderSplitString is similar to %v except with easier to understand Coin and Int entries. -func orderSplitString(s *OrderSplit) string { - if s == nil { - return "nil" + if orig.addrs != nil { + rv.addrs = make([]string, 0, len(orig.addrs)) + rv.addrs = append(rv.addrs, orig.addrs...) } - fields := []string{ - // Just using superficial info for the order to prevent infinite loops. - fmt.Sprintf("Order:{OrderID:%d,OrderType:%s,...}", s.Order.GetOrderID(), s.Order.GetOrderType()), - fmt.Sprintf("Assets:%q", s.Assets), - fmt.Sprintf("Price:%q", s.Price), + if orig.amts != nil { + rv.amts = make([]sdk.Coins, len(orig.amts)) + for i, amt := range orig.amts { + rv.amts[i] = copyCoins(amt) + } } - return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) -} -// orderSplitsString is similar to %v except with easier to understand Coin and Int entries. -func orderSplitsString(splits []*OrderSplit) string { - if splits == nil { - return "nil" - } - vals := make([]string, len(splits)) - for i, s := range splits { - vals[i] = orderSplitString(s) + if orig.indexes != nil { + rv.indexes = make(map[string]int, len(orig.indexes)) + for k, v := range orig.indexes { + rv.indexes[k] = v + } } - return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) + + return rv } // distributionString is similar to %v except with easier to understand Int entries. -func distributionString(dist *Distribution) string { +func distributionString(dist *distribution) string { if dist == nil { return "nil" } @@ -205,7 +120,7 @@ func distributionString(dist *Distribution) string { } // distributionsString is similar to %v except with easier to understand Int entries. -func distributionsString(dists []*Distribution) string { +func distributionsString(dists []*distribution) string { if dists == nil { return "nil" } @@ -217,49 +132,35 @@ func distributionsString(dists []*Distribution) string { } // orderFulfillmentString is similar to %v except with easier to understand Coin and Int entries. -func orderFulfillmentString(f *OrderFulfillment) string { +func orderFulfillmentString(f *orderFulfillment) string { if f == nil { return "nil" } fields := []string{ fmt.Sprintf("Order:%s", orderString(f.Order)), - fmt.Sprintf("Splits:%s", orderSplitsString(f.Splits)), fmt.Sprintf("AssetDists:%s", distributionsString(f.AssetDists)), fmt.Sprintf("PriceDists:%s", distributionsString(f.PriceDists)), fmt.Sprintf("AssetsFilledAmt:%s", f.AssetsFilledAmt), fmt.Sprintf("AssetsUnfilledAmt:%s", f.AssetsUnfilledAmt), fmt.Sprintf("PriceAppliedAmt:%s", f.PriceAppliedAmt), fmt.Sprintf("PriceLeftAmt:%s", f.PriceLeftAmt), - fmt.Sprintf("IsFinalized:%t", f.IsFinalized), fmt.Sprintf("FeesToPay:%s", coinsString(f.FeesToPay)), - fmt.Sprintf("OrderFeesLeft:%s", coinsString(f.OrderFeesLeft)), - fmt.Sprintf("PriceFilledAmt:%s", f.PriceFilledAmt), - fmt.Sprintf("PriceUnfilledAmt:%s", f.PriceUnfilledAmt), } return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) } -// transfersStringsLines creates a string for each transfer. -func transfersStringsLines(ts []*Transfer) []string { - if ts == nil { - return nil - } - rv := make([]string, len(ts)) - for i, t := range ts { - rv[i] = transferString(t) +// orderFulfillmentsString is similar to %v except with easier to understand Coin entries. +func orderFulfillmentsString(ofs []*orderFulfillment) string { + if ofs == nil { + return "nil" } - return rv -} -// transferString is similar to %v except with easier to understand Coin entries. -func transferString(t *Transfer) string { - if t == nil { - return "nil" + vals := make([]string, len(ofs)) + for i, f := range ofs { + vals[i] = orderFulfillmentString(f) } - inputs := bankInputsString(t.Inputs) - outputs := bankOutputsString(t.Outputs) - return fmt.Sprintf("T{Inputs:%s, Outputs: %s}", inputs, outputs) + return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) } // bankInputString is similar to %v except with easier to understand Coin entries. @@ -296,10 +197,70 @@ func bankOutputsString(outs []banktypes.Output) string { return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) } +// transferString is similar to %v except with easier to understand Coin entries. +func transferString(t *Transfer) string { + if t == nil { + return "nil" + } + inputs := bankInputsString(t.Inputs) + outputs := bankOutputsString(t.Outputs) + return fmt.Sprintf("T{Inputs:%s, Outputs: %s}", inputs, outputs) +} + +// transfersStringsLines creates a string for each transfer. +func transfersStringsLines(ts []*Transfer) []string { + if ts == nil { + return nil + } + rv := make([]string, len(ts)) + for i, t := range ts { + rv[i] = transferString(t) + } + return rv +} + +// String converts a indexedAddrAmtsString to a string. +// This is mostly because test failure output of sdk.Coin and sdk.Coins is impossible to understand. +func indexedAddrAmtsString(i *indexedAddrAmts) string { + if i == nil { + return "nil" + } + + addrs := "nil" + if i.addrs != nil { + addrsVals := make([]string, len(i.addrs)) + for j, addr := range i.addrs { + addrsVals[j] = fmt.Sprintf("%q", addr) + } + addrs = fmt.Sprintf("%T{%s}", i.addrs, strings.Join(addrsVals, ", ")) + } + + amts := "nil" + if i.amts != nil { + amtsVals := make([]string, len(i.amts)) + for j, amt := range i.amts { + amtsVals[j] = fmt.Sprintf("%q", amt) + } + amts = fmt.Sprintf("[]%T{%s}", i.amts, strings.Join(amtsVals, ", ")) + } + + indexes := "nil" + if i.indexes != nil { + indexVals := make([]string, 0, len(i.indexes)) + for k, v := range i.indexes { + indexVals = append(indexVals, fmt.Sprintf("%q: %d", k, v)) + } + sort.Strings(indexVals) + indexes = fmt.Sprintf("%T{%s}", i.indexes, strings.Join(indexVals, ", ")) + } + + return fmt.Sprintf("%T{addrs:%s, amts:%s, indexes:%s}", i, addrs, amts, indexes) +} + // assertEqualOrderFulfillments asserts that the two order fulfillments are equal. // Returns true if equal. // If not equal, and neither are nil, equality on each field is also asserted in order to help identify the problem. -func assertEqualOrderFulfillments(t *testing.T, expected, actual *OrderFulfillment, message string, args ...interface{}) bool { +func assertEqualOrderFulfillments(t *testing.T, expected, actual *orderFulfillment, message string, args ...interface{}) bool { t.Helper() if assert.Equalf(t, expected, actual, message, args...) { return true @@ -318,19 +279,14 @@ func assertEqualOrderFulfillments(t *testing.T, expected, actual *OrderFulfillme // Assert equality on each individual field so that we can more easily find the problem. // If any of the Ints fail with a complaint about Int.abs = (big.nat) vs {}, use ZeroAmtAfterSub for the expected. - assert.Equalf(t, expected.Order, actual.Order, msg("OrderFulfillment.Order"), args...) - assert.Equalf(t, expected.Splits, actual.Splits, msg("OrderFulfillment.Splits"), args...) - assert.Equalf(t, expected.AssetDists, actual.AssetDists, msg("OrderFulfillment.AssetDists"), args...) - assert.Equalf(t, expected.PriceDists, actual.PriceDists, msg("OrderFulfillment.PriceDists"), args...) - assert.Equalf(t, expected.AssetsFilledAmt, actual.AssetsFilledAmt, msg("OrderFulfillment.AssetsFilledAmt"), args...) - assert.Equalf(t, expected.AssetsUnfilledAmt, actual.AssetsUnfilledAmt, msg("OrderFulfillment.AssetsUnfilledAmt"), args...) - assert.Equalf(t, expected.PriceAppliedAmt, actual.PriceAppliedAmt, msg("OrderFulfillment.PriceAppliedAmt"), args...) - assert.Equalf(t, expected.PriceLeftAmt, actual.PriceLeftAmt, msg("OrderFulfillment.PriceLeftAmt"), args...) - assert.Equalf(t, expected.IsFinalized, actual.IsFinalized, msg("OrderFulfillment.IsFinalized"), args...) - assert.Equalf(t, expected.FeesToPay, actual.FeesToPay, msg("OrderFulfillment.FeesToPay"), args...) - assert.Equalf(t, expected.OrderFeesLeft, actual.OrderFeesLeft, msg("OrderFulfillment.OrderFeesLeft"), args...) - assert.Equalf(t, expected.PriceFilledAmt, actual.PriceFilledAmt, msg("OrderFulfillment.PriceFilledAmt"), args...) - assert.Equalf(t, expected.PriceUnfilledAmt, actual.PriceUnfilledAmt, msg("OrderFulfillment.PriceUnfilledAmt"), args...) + assert.Equalf(t, expected.Order, actual.Order, msg("orderFulfillment.Order"), args...) + assert.Equalf(t, expected.AssetDists, actual.AssetDists, msg("orderFulfillment.AssetDists"), args...) + assert.Equalf(t, expected.PriceDists, actual.PriceDists, msg("orderFulfillment.PriceDists"), args...) + assert.Equalf(t, expected.AssetsFilledAmt, actual.AssetsFilledAmt, msg("orderFulfillment.AssetsFilledAmt"), args...) + assert.Equalf(t, expected.AssetsUnfilledAmt, actual.AssetsUnfilledAmt, msg("orderFulfillment.AssetsUnfilledAmt"), args...) + assert.Equalf(t, expected.PriceAppliedAmt, actual.PriceAppliedAmt, msg("orderFulfillment.PriceAppliedAmt"), args...) + assert.Equalf(t, expected.PriceLeftAmt, actual.PriceLeftAmt, msg("orderFulfillment.PriceLeftAmt"), args...) + assert.Equalf(t, expected.FeesToPay, actual.FeesToPay, msg("orderFulfillment.FeesToPay"), args...) t.Logf(" Actual: %s", orderFulfillmentString(actual)) t.Logf("Expected: %s", orderFulfillmentString(expected)) return false @@ -339,7 +295,7 @@ func assertEqualOrderFulfillments(t *testing.T, expected, actual *OrderFulfillme // assertEqualOrderFulfillmentSlices asserts that the two order fulfillments are equal. // Returns true if equal. // If not equal, and neither are nil, equality on each field is also asserted in order to help identify the problem. -func assertEqualOrderFulfillmentSlices(t *testing.T, expected, actual []*OrderFulfillment, message string, args ...interface{}) bool { +func assertEqualOrderFulfillmentSlices(t *testing.T, expected, actual []*orderFulfillment, message string, args ...interface{}) bool { t.Helper() if assert.Equalf(t, expected, actual, message, args...) { return true @@ -381,7 +337,7 @@ func assertEqualOrderFulfillmentSlices(t *testing.T, expected, actual []*OrderFu actStrVals[i] = orderFulfillmentString(act) } actStrs := strings.Join(actStrVals, "\n") - if !assert.Equalf(t, expStrs, actStrs, msg("OrderFulfillment strings"), args...) { + if !assert.Equalf(t, expStrs, actStrs, msg("orderFulfillment strings"), args...) { // Wooo, should have actionable info in the failure, so we can be done. return false } @@ -395,2412 +351,244 @@ func assertEqualOrderFulfillmentSlices(t *testing.T, expected, actual []*OrderFu return false } -func TestNewOrderFulfillment(t *testing.T) { +func TestBuildSettlement(t *testing.T) { + assetDenom, priceDenom := "apple", "peach" + feeDenoms := []string{"fig", "grape"} + feeCoins := func(tracer string, amts []int64) sdk.Coins { + if len(amts) == 0 { + return nil + } + if len(amts) > len(feeDenoms) { + t.Fatalf("cannot create %s with more than %d fees %v", tracer, len(feeDenoms), amts) + } + var rv sdk.Coins + for i, amt := range amts { + rv = rv.Add(sdk.NewInt64Coin(feeDenoms[i], amt)) + } + return rv + } + askOrder := func(orderID uint64, assets, price int64, allowPartial bool, fees ...int64) *Order { + if len(fees) > 1 { + t.Fatalf("cannot create ask order %d with more than 1 fees %v", orderID, fees) + } + var fee *sdk.Coin + if fc := feeCoins("", fees); !fc.IsZero() { + fee = &fc[0] + } + return NewOrder(orderID).WithAsk(&AskOrder{ + Seller: fmt.Sprintf("seller%d", orderID), + Assets: sdk.NewInt64Coin(assetDenom, assets), + Price: sdk.NewInt64Coin(priceDenom, price), + SellerSettlementFlatFee: fee, + AllowPartial: allowPartial, + }) + } + bidOrder := func(orderID uint64, assets, price int64, allowPartial bool, fees ...int64) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Buyer: fmt.Sprintf("buyer%d", orderID), + Assets: sdk.NewInt64Coin(assetDenom, assets), + Price: sdk.NewInt64Coin(priceDenom, price), + BuyerSettlementFees: feeCoins(fmt.Sprintf("bid order %d", orderID), fees), + AllowPartial: allowPartial, + }) + } + ratio := func(price, fee int64) func(denom string) (*FeeRatio, error) { + return func(denom string) (*FeeRatio, error) { + return &FeeRatio{Price: sdk.NewInt64Coin(priceDenom, price), Fee: sdk.NewInt64Coin(feeDenoms[0], fee)}, nil + } + } + filled := func(order *Order, price int64, fees ...int64) *FilledOrder { + return NewFilledOrder(order, + sdk.NewInt64Coin(priceDenom, price), + feeCoins(fmt.Sprintf("filled order %d", order), fees)) + } + assetsInput := func(orderID uint64, amount int64) banktypes.Input { + return banktypes.Input{ + Address: fmt.Sprintf("seller%d", orderID), + Coins: sdk.NewCoins(sdk.NewInt64Coin(assetDenom, amount)), + } + } + assetsOutput := func(orderID uint64, amount int64) banktypes.Output { + return banktypes.Output{ + Address: fmt.Sprintf("buyer%d", orderID), + Coins: sdk.NewCoins(sdk.NewInt64Coin(assetDenom, amount)), + } + } + priceInput := func(orderID uint64, amount int64) banktypes.Input { + return banktypes.Input{ + Address: fmt.Sprintf("buyer%d", orderID), + Coins: sdk.NewCoins(sdk.NewInt64Coin(priceDenom, amount)), + } + } + priceOutput := func(orderID uint64, amount int64) banktypes.Output { + return banktypes.Output{ + Address: fmt.Sprintf("seller%d", orderID), + Coins: sdk.NewCoins(sdk.NewInt64Coin(priceDenom, amount)), + } + } + feeInput := func(address string, amts ...int64) banktypes.Input { + return banktypes.Input{Address: address, Coins: feeCoins("bank input for "+address, amts)} + } + tests := []struct { - name string - order *Order - expected *OrderFulfillment - expPanic string + name string + askOrders []*Order + bidOrders []*Order + sellerFeeRatioLookup func(denom string) (*FeeRatio, error) + expSettlement *Settlement + expErr string }{ { - name: "nil sub-order", - order: NewOrder(1), - expPanic: nilSubTypeErr(1), + // error from validateCanSettle + name: "no ask orders", + askOrders: []*Order{}, + bidOrders: []*Order{bidOrder(3, 1, 10, false)}, + expErr: "no ask orders provided", }, { - name: "ask order", - order: NewOrder(2).WithAsk(&AskOrder{ - MarketId: 10, - Assets: sdk.NewInt64Coin("adolla", 92), - Price: sdk.NewInt64Coin("pdolla", 15), - }), - expected: &OrderFulfillment{ - Order: &Order{ - OrderId: 2, - Order: &Order_AskOrder{ - AskOrder: &AskOrder{ - MarketId: 10, - Assets: sdk.NewInt64Coin("adolla", 92), - Price: sdk.NewInt64Coin("pdolla", 15), - }, - }, - }, - Splits: nil, - AssetsFilledAmt: sdkmath.ZeroInt(), - AssetsUnfilledAmt: sdkmath.NewInt(92), - PriceAppliedAmt: sdkmath.ZeroInt(), - PriceLeftAmt: sdkmath.NewInt(15), - IsFinalized: false, - FeesToPay: nil, - OrderFeesLeft: nil, - PriceFilledAmt: sdkmath.ZeroInt(), - PriceUnfilledAmt: sdkmath.ZeroInt(), + name: "error from ratio lookup", + askOrders: []*Order{askOrder(3, 1, 10, false)}, + bidOrders: []*Order{bidOrder(4, 1, 10, false)}, + sellerFeeRatioLookup: func(denom string) (*FeeRatio, error) { + return nil, errors.New("this is a test error") }, + expErr: "this is a test error", }, { - name: "bid order", - order: NewOrder(3).WithBid(&BidOrder{ - MarketId: 11, - Assets: sdk.NewInt64Coin("adolla", 93), - Price: sdk.NewInt64Coin("pdolla", 16), - }), - expected: &OrderFulfillment{ - Order: &Order{ - OrderId: 3, - Order: &Order_BidOrder{ - BidOrder: &BidOrder{ - MarketId: 11, - Assets: sdk.NewInt64Coin("adolla", 93), - Price: sdk.NewInt64Coin("pdolla", 16), - }, - }, - }, - Splits: nil, - AssetsFilledAmt: sdkmath.ZeroInt(), - AssetsUnfilledAmt: sdkmath.NewInt(93), - PriceAppliedAmt: sdkmath.ZeroInt(), - PriceLeftAmt: sdkmath.NewInt(16), - IsFinalized: false, - FeesToPay: nil, - OrderFeesLeft: nil, - PriceFilledAmt: sdkmath.ZeroInt(), - PriceUnfilledAmt: sdkmath.ZeroInt(), + name: "error from setFeesToPay", + askOrders: []*Order{askOrder(3, 1, 10, false)}, + bidOrders: []*Order{bidOrder(4, 1, 10, false)}, + sellerFeeRatioLookup: func(denom string) (*FeeRatio, error) { + return &FeeRatio{Price: sdk.NewInt64Coin("prune", 10), Fee: sdk.NewInt64Coin("fig", 1)}, nil }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual *OrderFulfillment - defer func() { - if t.Failed() { - t.Logf(" Actual: %s", orderFulfillmentString(actual)) - t.Logf("Expected: %s", orderFulfillmentString(tc.expected)) - } - }() - - testFunc := func() { - actual = NewOrderFulfillment(tc.order) - } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "NewOrderFulfillment") - assert.Equal(t, tc.expected, actual, "NewOrderFulfillment result") - }) - } -} - -func TestNewOrderFulfillments(t *testing.T) { - assetCoin := func(amount int64) sdk.Coin { - return sdk.NewInt64Coin("anise", amount) - } - priceCoin := func(amount int64) sdk.Coin { - return sdk.NewInt64Coin("paprika", amount) - } - feeCoin := func(amount int64) *sdk.Coin { - rv := sdk.NewInt64Coin("fennel", amount) - return &rv - } - - askOrders := make([]*Order, 4) // ids 1, 2, 3, 4 - for j := range askOrders { - i := int64(j) + 1 - order := &AskOrder{ - MarketId: uint32(90 + i), - Seller: fmt.Sprintf("seller-%d", i), - Assets: assetCoin(1000*i + 100*i + 10*i + i), - Price: priceCoin(100*i + 10*i + i), - } - if j%2 == 0 { - order.SellerSettlementFlatFee = feeCoin(10*i + i) - } - if j >= 2 { - order.AllowPartial = true - } - askOrders[j] = NewOrder(uint64(i)).WithAsk(order) - } - - bidOrders := make([]*Order, 4) // ids 5, 6, 7, 8 - for j := range bidOrders { - i := int64(j + 5) - order := &BidOrder{ - MarketId: uint32(90 + i), - Buyer: fmt.Sprintf("buyer-%d", i), - Assets: assetCoin(1000*i + 100*i + 10*i + i), - Price: priceCoin(100*i + 10*i + i), - } - switch j { - case 0: - order.BuyerSettlementFees = sdk.Coins{*feeCoin(10*i + i)} - case 2: - order.BuyerSettlementFees = sdk.Coins{ - *feeCoin(10*i + i), - sdk.NewInt64Coin("garlic", 10000*i+1000*i+100*i+10*i+i), - } - } - if j >= 2 { - order.AllowPartial = true - } - bidOrders[j] = NewOrder(uint64(i)).WithBid(order) - } - - tests := []struct { - name string - orders []*Order - expected []*OrderFulfillment - }{ - { - name: "nil orders", - orders: nil, - expected: []*OrderFulfillment{}, + expErr: "failed calculate ratio fee for ask order 3: cannot apply ratio 10prune:1fig to price 10peach: incorrect price denom", }, { - name: "empty orders", - orders: []*Order{}, - expected: []*OrderFulfillment{}, + name: "one ask, three bids: last bid not used", + askOrders: []*Order{askOrder(3, 10, 20, false)}, + bidOrders: []*Order{ + bidOrder(4, 7, 14, false), + bidOrder(5, 3, 6, false), + bidOrder(6, 1, 2, false), + }, + expErr: "bid order 6 (at index 2) has no assets filled", }, { - name: "1 ask order", - orders: []*Order{askOrders[0]}, - expected: []*OrderFulfillment{NewOrderFulfillment(askOrders[0])}, + name: "three asks, one bids: last ask not used", + askOrders: []*Order{ + askOrder(11, 7, 14, false), + askOrder(12, 3, 14, false), + askOrder(13, 1, 14, false), + }, + bidOrders: []*Order{bidOrder(14, 10, 20, false)}, + expErr: "ask order 13 (at index 2) has no assets filled", }, { - name: "1 bid order", - orders: []*Order{bidOrders[0]}, - expected: []*OrderFulfillment{NewOrderFulfillment(bidOrders[0])}, + name: "two asks, two bids: same assets total, total bid price not enough", + askOrders: []*Order{ + askOrder(1, 10, 25, false), + askOrder(2, 10, 15, false), + }, + bidOrders: []*Order{ + bidOrder(8, 10, 20, false), + bidOrder(9, 10, 19, false), + }, + expErr: "total ask price \"40peach\" is greater than total bid price \"39peach\"", }, { - name: "4 ask orders", - orders: []*Order{askOrders[0], askOrders[1], askOrders[2], askOrders[3]}, - expected: []*OrderFulfillment{ - NewOrderFulfillment(askOrders[0]), - NewOrderFulfillment(askOrders[1]), - NewOrderFulfillment(askOrders[2]), - NewOrderFulfillment(askOrders[3]), + name: "two asks, two bids: ask partial, total bid price not enough", + askOrders: []*Order{ + askOrder(1, 10, 20, false), + askOrder(2, 10, 20, true), + }, + bidOrders: []*Order{ + bidOrder(8, 10, 20, false), + bidOrder(9, 9, 17, false), }, + expErr: "total ask price \"38peach\" is greater than total bid price \"37peach\"", }, { - name: "4 bid orders", - orders: []*Order{bidOrders[0], bidOrders[1], bidOrders[2], bidOrders[3]}, - expected: []*OrderFulfillment{ - NewOrderFulfillment(bidOrders[0]), - NewOrderFulfillment(bidOrders[1]), - NewOrderFulfillment(bidOrders[2]), - NewOrderFulfillment(bidOrders[3]), + name: "two asks, two bids: bid partial, total bid price not enough", + askOrders: []*Order{ + askOrder(1, 10, 25, false), + askOrder(2, 10, 15, false), + }, + bidOrders: []*Order{ + bidOrder(8, 10, 19, false), + bidOrder(9, 11, 22, true), }, + expErr: "total ask price \"40peach\" is greater than total bid price \"39peach\"", }, { - name: "1 bid 1 ask", - orders: []*Order{askOrders[1], bidOrders[2]}, - expected: []*OrderFulfillment{ - NewOrderFulfillment(askOrders[1]), - NewOrderFulfillment(bidOrders[2]), + name: "one ask, one bid: both fully filled", + askOrders: []*Order{askOrder(52, 10, 100, false, 2)}, + bidOrders: []*Order{bidOrder(11, 10, 105, false, 3, 4)}, + sellerFeeRatioLookup: ratio(4, 1), + expSettlement: &Settlement{ + Transfers: []*Transfer{ + {Inputs: []banktypes.Input{assetsInput(52, 10)}, Outputs: []banktypes.Output{assetsOutput(11, 10)}}, + {Inputs: []banktypes.Input{priceInput(11, 105)}, Outputs: []banktypes.Output{priceOutput(52, 105)}}, + }, + FeeInputs: []banktypes.Input{ + feeInput("seller52", 29), + feeInput("buyer11", 3, 4), + }, + FullyFilledOrders: []*FilledOrder{ + filled(askOrder(52, 10, 100, false, 2), 105, 29), + filled(bidOrder(11, 10, 105, false, 3, 4), 105, 3, 4), + }, }, }, { - name: "1 ask 1 bid", - orders: []*Order{bidOrders[1], askOrders[2]}, - expected: []*OrderFulfillment{ - NewOrderFulfillment(bidOrders[1]), - NewOrderFulfillment(askOrders[2]), + name: "one ask, one bid: ask partially filled", + askOrders: []*Order{askOrder(99, 10, 100, true)}, + bidOrders: []*Order{bidOrder(15, 9, 90, false)}, + expSettlement: &Settlement{ + Transfers: []*Transfer{ + {Inputs: []banktypes.Input{assetsInput(99, 9)}, Outputs: []banktypes.Output{assetsOutput(15, 9)}}, + {Inputs: []banktypes.Input{priceInput(15, 90)}, Outputs: []banktypes.Output{priceOutput(99, 90)}}, + }, + FullyFilledOrders: []*FilledOrder{filled(bidOrder(15, 9, 90, false), 90)}, + PartialOrderFilled: filled(askOrder(99, 9, 90, true), 90), + PartialOrderLeft: askOrder(99, 1, 10, true), }, }, { - name: "4 asks 4 bids", - orders: []*Order{ - askOrders[0], askOrders[1], askOrders[2], askOrders[3], - bidOrders[3], bidOrders[2], bidOrders[1], bidOrders[0], - }, - expected: []*OrderFulfillment{ - NewOrderFulfillment(askOrders[0]), - NewOrderFulfillment(askOrders[1]), - NewOrderFulfillment(askOrders[2]), - NewOrderFulfillment(askOrders[3]), - NewOrderFulfillment(bidOrders[3]), - NewOrderFulfillment(bidOrders[2]), - NewOrderFulfillment(bidOrders[1]), - NewOrderFulfillment(bidOrders[0]), - }, + name: "one ask, one bid: ask partially filled, not allowed", + askOrders: []*Order{askOrder(99, 10, 100, false)}, + bidOrders: []*Order{bidOrder(15, 9, 90, false)}, + expErr: "cannot split ask order 99 having assets \"10apple\" at \"9apple\": order does not allow partial fulfillment", }, { - name: "4 bids 4 asks", - orders: []*Order{ - bidOrders[0], bidOrders[1], bidOrders[2], bidOrders[3], - askOrders[3], askOrders[2], askOrders[1], askOrders[0], - }, - expected: []*OrderFulfillment{ - NewOrderFulfillment(bidOrders[0]), - NewOrderFulfillment(bidOrders[1]), - NewOrderFulfillment(bidOrders[2]), - NewOrderFulfillment(bidOrders[3]), - NewOrderFulfillment(askOrders[3]), - NewOrderFulfillment(askOrders[2]), - NewOrderFulfillment(askOrders[1]), - NewOrderFulfillment(askOrders[0]), + name: "one ask, one bid: bid partially filled", + askOrders: []*Order{askOrder(8, 9, 85, false, 2)}, + bidOrders: []*Order{bidOrder(12, 10, 100, true)}, + expSettlement: &Settlement{ + Transfers: []*Transfer{ + {Inputs: []banktypes.Input{assetsInput(8, 9)}, Outputs: []banktypes.Output{assetsOutput(12, 9)}}, + {Inputs: []banktypes.Input{priceInput(12, 90)}, Outputs: []banktypes.Output{priceOutput(8, 90)}}, + }, + FeeInputs: []banktypes.Input{feeInput("seller8", 2)}, + FullyFilledOrders: []*FilledOrder{filled(askOrder(8, 9, 85, false, 2), 90, 2)}, + PartialOrderFilled: filled(bidOrder(12, 9, 90, true), 90), + PartialOrderLeft: bidOrder(12, 1, 10, true), }, }, { - name: "interweaved 4 asks 4 bids", - orders: []*Order{ - bidOrders[3], askOrders[0], askOrders[3], bidOrders[1], - bidOrders[0], askOrders[1], bidOrders[2], askOrders[2], - }, - expected: []*OrderFulfillment{ - NewOrderFulfillment(bidOrders[3]), - NewOrderFulfillment(askOrders[0]), - NewOrderFulfillment(askOrders[3]), - NewOrderFulfillment(bidOrders[1]), - NewOrderFulfillment(bidOrders[0]), - NewOrderFulfillment(askOrders[1]), - NewOrderFulfillment(bidOrders[2]), - NewOrderFulfillment(askOrders[2]), - }, + name: "one ask, one bid: bid partially filled, not allowed", + askOrders: []*Order{askOrder(8, 9, 85, false, 2)}, + bidOrders: []*Order{bidOrder(12, 10, 100, false)}, + expErr: "cannot split bid order 12 having assets \"10apple\" at \"9apple\": order does not allow partial fulfillment", }, { - name: "duplicated entries", - orders: []*Order{ - askOrders[3], bidOrders[2], askOrders[3], bidOrders[2], - }, - expected: []*OrderFulfillment{ - NewOrderFulfillment(askOrders[3]), - NewOrderFulfillment(bidOrders[2]), - NewOrderFulfillment(askOrders[3]), - NewOrderFulfillment(bidOrders[2]), - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual []*OrderFulfillment - testFunc := func() { - actual = NewOrderFulfillments(tc.orders) - } - require.NotPanics(t, testFunc, "NewOrderFulfillments") - assertEqualOrderFulfillmentSlices(t, tc.expected, actual, "NewOrderFulfillments result") - }) - } -} - -func TestOrderFulfillment_AssetCoin(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} - } - bigCoin := func(amount, denom string) sdk.Coin { - amt := newInt(t, amount) - return sdk.Coin{Denom: denom, Amount: amt} - } - askOrder := func(orderID uint64, assets, price sdk.Coin) *Order { - return NewOrder(orderID).WithAsk(&AskOrder{ - Assets: assets, - Price: price, - }) - } - bidOrder := func(orderID uint64, assets, price sdk.Coin) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - Assets: assets, - Price: price, - }) - } - - tests := []struct { - name string - receiver OrderFulfillment - amt sdkmath.Int - expected sdk.Coin - expPanic string - }{ - { - name: "nil order", - receiver: OrderFulfillment{Order: nil}, - amt: sdkmath.NewInt(0), - expPanic: "runtime error: invalid memory address or nil pointer dereference", - }, - { - name: "nil inside order", - receiver: OrderFulfillment{Order: NewOrder(1)}, - amt: sdkmath.NewInt(0), - expPanic: nilSubTypeErr(1), - }, - { - name: "unknown inside order", - receiver: OrderFulfillment{Order: newUnknownOrder(2)}, - amt: sdkmath.NewInt(0), - expPanic: unknownSubTypeErr(2), - }, - { - name: "ask order", - receiver: OrderFulfillment{Order: askOrder(3, coin(4, "apple"), coin(5, "plum"))}, - amt: sdkmath.NewInt(6), - expected: coin(6, "apple"), - }, - { - name: "ask order with negative assets", - receiver: OrderFulfillment{Order: askOrder(7, coin(-8, "apple"), coin(9, "plum"))}, - amt: sdkmath.NewInt(10), - expected: coin(10, "apple"), - }, - { - name: "ask order, negative amt", - receiver: OrderFulfillment{Order: askOrder(11, coin(12, "apple"), coin(13, "plum"))}, - amt: sdkmath.NewInt(-14), - expected: coin(-14, "apple"), - }, - { - name: "ask order with negative assets, negative amt", - receiver: OrderFulfillment{Order: askOrder(15, coin(-16, "apple"), coin(17, "plum"))}, - amt: sdkmath.NewInt(-18), - expected: coin(-18, "apple"), - }, - { - name: "ask order, big amt", - receiver: OrderFulfillment{Order: askOrder(19, coin(20, "apple"), coin(21, "plum"))}, - amt: newInt(t, "123,000,000,000,000,000,000,000,000,000,000,321"), - expected: bigCoin("123,000,000,000,000,000,000,000,000,000,000,321", "apple"), - }, - { - name: "bid order", - receiver: OrderFulfillment{Order: bidOrder(3, coin(4, "apple"), coin(5, "plum"))}, - amt: sdkmath.NewInt(6), - expected: coin(6, "apple"), - }, - { - name: "bid order with negative assets", - receiver: OrderFulfillment{Order: bidOrder(7, coin(-8, "apple"), coin(9, "plum"))}, - amt: sdkmath.NewInt(10), - expected: coin(10, "apple"), - }, - { - name: "bid order, negative amt", - receiver: OrderFulfillment{Order: bidOrder(11, coin(12, "apple"), coin(13, "plum"))}, - amt: sdkmath.NewInt(-14), - expected: coin(-14, "apple"), - }, - { - name: "bid order with negative assets, negative amt", - receiver: OrderFulfillment{Order: bidOrder(15, coin(-16, "apple"), coin(17, "plum"))}, - amt: sdkmath.NewInt(-18), - expected: coin(-18, "apple"), - }, - { - name: "bid order, big amt", - receiver: OrderFulfillment{Order: bidOrder(19, coin(20, "apple"), coin(21, "plum"))}, - amt: newInt(t, "123,000,000,000,000,000,000,000,000,000,000,321"), - expected: bigCoin("123,000,000,000,000,000,000,000,000,000,000,321", "apple"), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coin - testFunc := func() { - actual = tc.receiver.assetCoin(tc.amt) - } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "assetCoin(%s)", tc.amt) - assert.Equal(t, tc.expected.String(), actual.String(), "assetCoin(%s) result", tc.amt) - }) - } -} - -func TestOrderFulfillment_PriceCoin(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} - } - bigCoin := func(amount, denom string) sdk.Coin { - amt := newInt(t, amount) - return sdk.Coin{Denom: denom, Amount: amt} - } - askOrder := func(orderID uint64, assets, price sdk.Coin) *Order { - return NewOrder(orderID).WithAsk(&AskOrder{ - Assets: assets, - Price: price, - }) - } - bidOrder := func(orderID uint64, assets, price sdk.Coin) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - Assets: assets, - Price: price, - }) - } - - tests := []struct { - name string - receiver OrderFulfillment - amt sdkmath.Int - expected sdk.Coin - expPanic string - }{ - { - name: "nil order", - receiver: OrderFulfillment{Order: nil}, - amt: sdkmath.NewInt(0), - expPanic: "runtime error: invalid memory address or nil pointer dereference", - }, - { - name: "nil inside order", - receiver: OrderFulfillment{Order: NewOrder(1)}, - amt: sdkmath.NewInt(0), - expPanic: nilSubTypeErr(1), - }, - { - name: "unknown inside order", - receiver: OrderFulfillment{Order: newUnknownOrder(2)}, - amt: sdkmath.NewInt(0), - expPanic: unknownSubTypeErr(2), - }, - { - name: "ask order", - receiver: OrderFulfillment{Order: askOrder(3, coin(4, "apple"), coin(5, "plum"))}, - amt: sdkmath.NewInt(6), - expected: coin(6, "plum"), - }, - { - name: "ask order with negative assets", - receiver: OrderFulfillment{Order: askOrder(7, coin(-8, "apple"), coin(9, "plum"))}, - amt: sdkmath.NewInt(10), - expected: coin(10, "plum"), - }, - { - name: "ask order, negative amt", - receiver: OrderFulfillment{Order: askOrder(11, coin(12, "apple"), coin(13, "plum"))}, - amt: sdkmath.NewInt(-14), - expected: coin(-14, "plum"), - }, - { - name: "ask order with negative assets, negative amt", - receiver: OrderFulfillment{Order: askOrder(15, coin(-16, "apple"), coin(17, "plum"))}, - amt: sdkmath.NewInt(-18), - expected: coin(-18, "plum"), - }, - { - name: "ask order, big amt", - receiver: OrderFulfillment{Order: askOrder(19, coin(20, "apple"), coin(21, "plum"))}, - amt: newInt(t, "123,000,000,000,000,000,000,000,000,000,000,321"), - expected: bigCoin("123,000,000,000,000,000,000,000,000,000,000,321", "plum"), - }, - { - name: "bid order", - receiver: OrderFulfillment{Order: bidOrder(3, coin(4, "apple"), coin(5, "plum"))}, - amt: sdkmath.NewInt(6), - expected: coin(6, "plum"), - }, - { - name: "bid order with negative assets", - receiver: OrderFulfillment{Order: bidOrder(7, coin(-8, "apple"), coin(9, "plum"))}, - amt: sdkmath.NewInt(10), - expected: coin(10, "plum"), - }, - { - name: "bid order, negative amt", - receiver: OrderFulfillment{Order: bidOrder(11, coin(12, "apple"), coin(13, "plum"))}, - amt: sdkmath.NewInt(-14), - expected: coin(-14, "plum"), - }, - { - name: "bid order with negative assets, negative amt", - receiver: OrderFulfillment{Order: bidOrder(15, coin(-16, "apple"), coin(17, "plum"))}, - amt: sdkmath.NewInt(-18), - expected: coin(-18, "plum"), - }, - { - name: "bid order, big amt", - receiver: OrderFulfillment{Order: bidOrder(19, coin(20, "apple"), coin(21, "plum"))}, - amt: newInt(t, "123,000,000,000,000,000,000,000,000,000,000,321"), - expected: bigCoin("123,000,000,000,000,000,000,000,000,000,000,321", "plum"), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coin - testFunc := func() { - actual = tc.receiver.priceCoin(tc.amt) - } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "priceCoin(%s)", tc.amt) - assert.Equal(t, tc.expected.String(), actual.String(), "priceCoin(%s) result", tc.amt) - }) - } -} - -func TestOrderFulfillment_GetAssetsFilled(t *testing.T) { - coin := func(amt int64) sdk.Coin { - return sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(amt)} - } - askOrder := NewOrder(444).WithAsk(&AskOrder{Assets: coin(5555)}) - bidOrder := NewOrder(666).WithBid(&BidOrder{Assets: coin(7777)}) - - newOF := func(order *Order, amt int64) OrderFulfillment { - return OrderFulfillment{ - Order: order, - AssetsFilledAmt: sdkmath.NewInt(amt), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp sdk.Coin - }{ - {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, - {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, - {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, - {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, - {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, - {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coin - testFunc := func() { - actual = tc.f.GetAssetsFilled() - } - require.NotPanics(t, testFunc, "GetAssetsFilled()") - assert.Equal(t, tc.exp.String(), actual.String(), "GetAssetsFilled() result") - }) - } -} - -func TestOrderFulfillment_GetAssetsUnfilled(t *testing.T) { - coin := func(amt int64) sdk.Coin { - return sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(amt)} - } - askOrder := NewOrder(444).WithAsk(&AskOrder{Assets: coin(5555)}) - bidOrder := NewOrder(666).WithBid(&BidOrder{Assets: coin(7777)}) - - newOF := func(order *Order, amt int64) OrderFulfillment { - return OrderFulfillment{ - Order: order, - AssetsUnfilledAmt: sdkmath.NewInt(amt), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp sdk.Coin - }{ - {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, - {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, - {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, - {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, - {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, - {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coin - testFunc := func() { - actual = tc.f.GetAssetsUnfilled() - } - require.NotPanics(t, testFunc, "GetAssetsUnfilled()") - assert.Equal(t, tc.exp.String(), actual.String(), "GetAssetsUnfilled() result") - }) - } -} - -func TestOrderFulfillment_GetPriceApplied(t *testing.T) { - coin := func(amt int64) sdk.Coin { - return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} - } - askOrder := NewOrder(444).WithAsk(&AskOrder{Price: coin(5555)}) - bidOrder := NewOrder(666).WithBid(&BidOrder{Price: coin(7777)}) - - newOF := func(order *Order, amt int64) OrderFulfillment { - return OrderFulfillment{ - Order: order, - PriceAppliedAmt: sdkmath.NewInt(amt), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp sdk.Coin - }{ - {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, - {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, - {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, - {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, - {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, - {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coin - testFunc := func() { - actual = tc.f.GetPriceApplied() - } - require.NotPanics(t, testFunc, "GetPriceApplied()") - assert.Equal(t, tc.exp.String(), actual.String(), "GetPriceApplied() result") - }) - } -} - -func TestOrderFulfillment_GetPriceLeft(t *testing.T) { - coin := func(amt int64) sdk.Coin { - return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} - } - askOrder := NewOrder(444).WithAsk(&AskOrder{Price: coin(5555)}) - bidOrder := NewOrder(666).WithBid(&BidOrder{Price: coin(7777)}) - - newOF := func(order *Order, amt int64) OrderFulfillment { - return OrderFulfillment{ - Order: order, - PriceLeftAmt: sdkmath.NewInt(amt), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp sdk.Coin - }{ - {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, - {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, - {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, - {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, - {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, - {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coin - testFunc := func() { - actual = tc.f.GetPriceLeft() - } - require.NotPanics(t, testFunc, "GetPriceLeft()") - assert.Equal(t, tc.exp.String(), actual.String(), "GetPriceLeft() result") - }) - } -} - -func TestOrderFulfillment_GetPriceFilled(t *testing.T) { - coin := func(amt int64) sdk.Coin { - return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} - } - askOrder := NewOrder(444).WithAsk(&AskOrder{Price: coin(5555)}) - bidOrder := NewOrder(666).WithBid(&BidOrder{Price: coin(7777)}) - - newOF := func(order *Order, amt int64) OrderFulfillment { - return OrderFulfillment{ - Order: order, - PriceFilledAmt: sdkmath.NewInt(amt), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp sdk.Coin - }{ - {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, - {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, - {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, - {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, - {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, - {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coin - testFunc := func() { - actual = tc.f.GetPriceFilled() - } - require.NotPanics(t, testFunc, "GetPriceFilled()") - assert.Equal(t, tc.exp.String(), actual.String(), "GetPriceFilled() result") - }) - } -} - -func TestOrderFulfillment_GetPriceUnfilled(t *testing.T) { - coin := func(amt int64) sdk.Coin { - return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} - } - askOrder := NewOrder(444).WithAsk(&AskOrder{Price: coin(5555)}) - bidOrder := NewOrder(666).WithBid(&BidOrder{Price: coin(7777)}) - - newOF := func(order *Order, amt int64) OrderFulfillment { - return OrderFulfillment{ - Order: order, - PriceUnfilledAmt: sdkmath.NewInt(amt), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp sdk.Coin - }{ - {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, - {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, - {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, - {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, - {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, - {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coin - testFunc := func() { - actual = tc.f.GetPriceUnfilled() - } - require.NotPanics(t, testFunc, "GetPriceUnfilled()") - assert.Equal(t, tc.exp.String(), actual.String(), "GetPriceUnfilled() result") - }) - } -} - -func TestOrderFulfillment_IsFullyFilled(t *testing.T) { - newOF := func(amt int64) OrderFulfillment { - return OrderFulfillment{ - AssetsUnfilledAmt: sdkmath.NewInt(amt), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp bool - }{ - {name: "positive assets unfilled", f: newOF(2), exp: false}, - {name: "zero assets unfilled", f: newOF(0), exp: true}, - {name: "negative assets unfilled", f: newOF(-3), exp: true}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual bool - testFunc := func() { - actual = tc.f.IsFullyFilled() - } - require.NotPanics(t, testFunc, "IsFullyFilled()") - assert.Equal(t, tc.exp, actual, "IsFullyFilled() result") - }) - } -} - -func TestOrderFulfillment_IsCompletelyUnfulfilled(t *testing.T) { - newOF := func(amt int64) OrderFulfillment { - return OrderFulfillment{ - AssetsFilledAmt: sdkmath.NewInt(amt), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp bool - }{ - {name: "positive assets filled", f: newOF(2), exp: false}, - {name: "zero assets filled", f: newOF(0), exp: true}, - {name: "negative assets filled", f: newOF(-3), exp: false}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual bool - testFunc := func() { - actual = tc.f.IsCompletelyUnfulfilled() - } - require.NotPanics(t, testFunc, "IsCompletelyUnfulfilled()") - assert.Equal(t, tc.exp, actual, "IsCompletelyUnfulfilled() result") - }) - } -} - -func TestOrderFulfillment_GetOrderID(t *testing.T) { - newOF := func(orderID uint64) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(orderID), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp uint64 - }{ - {name: "zero", f: newOF(0), exp: 0}, - {name: "one", f: newOF(1), exp: 1}, - {name: "five", f: newOF(5), exp: 5}, - {name: "max uint32+1", f: newOF(4_294_967_296), exp: 4_294_967_296}, - {name: "max uint64", f: newOF(18_446_744_073_709_551_615), exp: 18_446_744_073_709_551_615}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual uint64 - testFunc := func() { - actual = tc.f.GetOrderID() - } - require.NotPanics(t, testFunc, "GetOrderID()") - assert.Equal(t, tc.exp, actual, "GetOrderID() result") - }) - } -} - -func TestOrderFulfillment_IsAskOrder(t *testing.T) { - tests := []struct { - name string - f OrderFulfillment - exp bool - }{ - {name: "ask", f: OrderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: true}, - {name: "bid", f: OrderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: false}, - {name: "nil", f: OrderFulfillment{Order: NewOrder(888)}, exp: false}, - {name: "unknown", f: OrderFulfillment{Order: newUnknownOrder(7)}, exp: false}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual bool - testFunc := func() { - actual = tc.f.IsAskOrder() - } - require.NotPanics(t, testFunc, "IsAskOrder()") - assert.Equal(t, tc.exp, actual, "IsAskOrder() result") - }) - } -} - -func TestOrderFulfillment_IsBidOrder(t *testing.T) { - tests := []struct { - name string - f OrderFulfillment - exp bool - }{ - {name: "ask", f: OrderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: false}, - {name: "bid", f: OrderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: true}, - {name: "nil", f: OrderFulfillment{Order: NewOrder(888)}, exp: false}, - {name: "unknown", f: OrderFulfillment{Order: newUnknownOrder(9)}, exp: false}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual bool - testFunc := func() { - actual = tc.f.IsBidOrder() - } - require.NotPanics(t, testFunc, "IsBidOrder()") - assert.Equal(t, tc.exp, actual, "IsBidOrder() result") - }) - } -} - -func TestOrderFulfillment_GetMarketID(t *testing.T) { - askOrder := func(marketID uint32) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(999).WithAsk(&AskOrder{MarketId: marketID}), - } - } - bidOrder := func(marketID uint32) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(999).WithBid(&BidOrder{MarketId: marketID}), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp uint32 - }{ - {name: "ask zero", f: askOrder(0), exp: 0}, - {name: "ask one", f: askOrder(1), exp: 1}, - {name: "ask five", f: askOrder(5), exp: 5}, - {name: "ask max uint16+1", f: askOrder(65_536), exp: 65_536}, - {name: "ask max uint32", f: askOrder(4_294_967_295), exp: 4_294_967_295}, - {name: "bid zero", f: bidOrder(0), exp: 0}, - {name: "bid one", f: bidOrder(1), exp: 1}, - {name: "bid five", f: bidOrder(5), exp: 5}, - {name: "bid max uint16+1", f: bidOrder(65_536), exp: 65_536}, - {name: "bid max uint32", f: bidOrder(4_294_967_295), exp: 4_294_967_295}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual uint32 - testFunc := func() { - actual = tc.f.GetMarketID() - } - require.NotPanics(t, testFunc, "GetMarketID()") - assert.Equal(t, tc.exp, actual, "GetMarketID() result") - }) - } -} - -func TestOrderFulfillment_GetOwner(t *testing.T) { - askOrder := func(seller string) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(999).WithAsk(&AskOrder{Seller: seller}), - } - } - bidOrder := func(buyer string) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(999).WithBid(&BidOrder{Buyer: buyer}), - } - } - owner := sdk.AccAddress("owner_______________").String() - - tests := []struct { - name string - f OrderFulfillment - exp string - }{ - {name: "ask empty", f: askOrder(""), exp: ""}, - {name: "ask not a bech32", f: askOrder("owner"), exp: "owner"}, - {name: "ask beche32", f: askOrder(owner), exp: owner}, - {name: "bid empty", f: bidOrder(""), exp: ""}, - {name: "bid not a bech32", f: bidOrder("owner"), exp: "owner"}, - {name: "bid beche32", f: bidOrder(owner), exp: owner}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual string - testFunc := func() { - actual = tc.f.GetOwner() - } - require.NotPanics(t, testFunc, "GetOwner()") - assert.Equal(t, tc.exp, actual, "GetOwner() result") - }) - } -} - -func TestOrderFulfillment_GetAssets(t *testing.T) { - coin := func(amt int64) sdk.Coin { - return sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(amt)} - } - askOrder := func(amt int64) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(999).WithAsk(&AskOrder{Assets: coin(amt)}), - } - } - bidOrder := func(amt int64) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(999).WithBid(&BidOrder{Assets: coin(amt)}), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp sdk.Coin - }{ - {name: "ask positive", f: askOrder(123), exp: coin(123)}, - {name: "ask zero", f: askOrder(0), exp: coin(0)}, - {name: "ask negative", f: askOrder(-9), exp: coin(-9)}, - {name: "bid positive", f: bidOrder(345), exp: coin(345)}, - {name: "bid zero", f: bidOrder(0), exp: coin(0)}, - {name: "bid negative", f: bidOrder(-8), exp: coin(-8)}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coin - testFunc := func() { - actual = tc.f.GetAssets() - } - require.NotPanics(t, testFunc, "GetAssets()") - assert.Equal(t, tc.exp.String(), actual.String(), "GetAssets() result") - }) - } -} - -func TestOrderFulfillment_GetPrice(t *testing.T) { - coin := func(amt int64) sdk.Coin { - return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} - } - askOrder := func(amt int64) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(999).WithAsk(&AskOrder{Price: coin(amt)}), - } - } - bidOrder := func(amt int64) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(999).WithBid(&BidOrder{Price: coin(amt)}), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp sdk.Coin - }{ - {name: "ask positive", f: askOrder(123), exp: coin(123)}, - {name: "ask zero", f: askOrder(0), exp: coin(0)}, - {name: "ask negative", f: askOrder(-9), exp: coin(-9)}, - {name: "bid positive", f: bidOrder(345), exp: coin(345)}, - {name: "bid zero", f: bidOrder(0), exp: coin(0)}, - {name: "bid negative", f: bidOrder(-8), exp: coin(-8)}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coin - testFunc := func() { - actual = tc.f.GetPrice() - } - require.NotPanics(t, testFunc, "GetPrice()") - assert.Equal(t, tc.exp.String(), actual.String(), "GetPrice() result") - }) - } -} - -func TestOrderFulfillment_GetSettlementFees(t *testing.T) { - coin := func(amt int64) *sdk.Coin { - return &sdk.Coin{Denom: "fees", Amount: sdkmath.NewInt(amt)} - } - askOrder := func(coin *sdk.Coin) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(999).WithAsk(&AskOrder{SellerSettlementFlatFee: coin}), - } - } - bidOrder := func(coins sdk.Coins) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(999).WithBid(&BidOrder{BuyerSettlementFees: coins}), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp sdk.Coins - }{ - {name: "ask nil", f: askOrder(nil), exp: nil}, - {name: "ask zero", f: askOrder(coin(0)), exp: sdk.Coins{*coin(0)}}, - {name: "ask positive", f: askOrder(coin(3)), exp: sdk.Coins{*coin(3)}}, - {name: "bid nil", f: bidOrder(nil), exp: nil}, - {name: "bid empty", f: bidOrder(sdk.Coins{}), exp: sdk.Coins{}}, - {name: "bid positive", f: bidOrder(sdk.Coins{*coin(3)}), exp: sdk.Coins{*coin(3)}}, - {name: "bid zero", f: bidOrder(sdk.Coins{*coin(0)}), exp: sdk.Coins{*coin(0)}}, - {name: "bid negative", f: bidOrder(sdk.Coins{*coin(-2)}), exp: sdk.Coins{*coin(-2)}}, - { - name: "bid multiple", - f: bidOrder(sdk.Coins{*coin(987), sdk.NewInt64Coin("six", 6), sdk.Coin{Denom: "zeg", Amount: sdkmath.NewInt(-1)}}), - exp: sdk.Coins{*coin(987), sdk.NewInt64Coin("six", 6), sdk.Coin{Denom: "zeg", Amount: sdkmath.NewInt(-1)}}, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual sdk.Coins - testFunc := func() { - actual = tc.f.GetSettlementFees() - } - require.NotPanics(t, testFunc, "GetSettlementFees()") - assert.Equal(t, tc.exp.String(), actual.String(), "GetSettlementFees() result") - }) - } -} - -func TestOrderFulfillment_PartialFillAllowed(t *testing.T) { - askOrder := func(allowPartial bool) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(999).WithAsk(&AskOrder{AllowPartial: allowPartial}), - } - } - bidOrder := func(allowPartial bool) OrderFulfillment { - return OrderFulfillment{ - Order: NewOrder(999).WithBid(&BidOrder{AllowPartial: allowPartial}), - } - } - - tests := []struct { - name string - f OrderFulfillment - exp bool - }{ - {name: "ask true", f: askOrder(true), exp: true}, - {name: "ask false", f: askOrder(false), exp: false}, - {name: "bid true", f: bidOrder(true), exp: true}, - {name: "bid false", f: bidOrder(false), exp: false}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual bool - testFunc := func() { - actual = tc.f.PartialFillAllowed() - } - require.NotPanics(t, testFunc, "PartialFillAllowed()") - assert.Equal(t, tc.exp, actual, "PartialFillAllowed() result") - }) - } -} - -func TestOrderFulfillment_GetOrderType(t *testing.T) { - tests := []struct { - name string - f OrderFulfillment - exp string - }{ - {name: "ask", f: OrderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: OrderTypeAsk}, - {name: "bid", f: OrderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: OrderTypeBid}, - {name: "nil", f: OrderFulfillment{Order: NewOrder(888)}, exp: ""}, - {name: "unknown", f: OrderFulfillment{Order: newUnknownOrder(8)}, exp: "*exchange.unknownOrderType"}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual string - testFunc := func() { - actual = tc.f.GetOrderType() - } - require.NotPanics(t, testFunc, "GetOrderType()") - assert.Equal(t, tc.exp, actual, "GetOrderType() result") - }) - } -} - -func TestOrderFulfillment_GetOrderTypeByte(t *testing.T) { - tests := []struct { - name string - f OrderFulfillment - exp byte - }{ - {name: "ask", f: OrderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: OrderTypeByteAsk}, - {name: "bid", f: OrderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: OrderTypeByteBid}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual byte - testFunc := func() { - actual = tc.f.GetOrderTypeByte() - } - require.NotPanics(t, testFunc, "GetOrderTypeByte()") - assert.Equal(t, tc.exp, actual, "GetOrderTypeByte() result") - }) - } -} - -func TestOrderFulfillment_GetHoldAmount(t *testing.T) { - tests := []struct { - name string - f OrderFulfillment - }{ - { - name: "ask", - f: OrderFulfillment{ - Order: NewOrder(111).WithAsk(&AskOrder{ - Assets: sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(55)}, - SellerSettlementFlatFee: &sdk.Coin{Denom: "fee", Amount: sdkmath.NewInt(3)}, - }), - }, - }, - { - name: "bid", - f: OrderFulfillment{ - Order: NewOrder(111).WithBid(&BidOrder{ - Price: sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(55)}, - BuyerSettlementFees: sdk.Coins{ - {Denom: "feea", Amount: sdkmath.NewInt(3)}, - {Denom: "feeb", Amount: sdkmath.NewInt(4)}, - }, - }), - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - expected := tc.f.GetHoldAmount() - var actual sdk.Coins - testFunc := func() { - actual = tc.f.GetHoldAmount() - } - require.NotPanics(t, testFunc, "GetHoldAmount()") - assert.Equal(t, expected, actual, "GetHoldAmount() result") - }) - } -} - -func TestOrderFulfillment_DistributeAssets(t *testing.T) { - newOF := func(order *Order, assetsUnfilled, assetsFilled int64, dists ...*Distribution) *OrderFulfillment { - rv := &OrderFulfillment{ - Order: order, - AssetsUnfilledAmt: sdkmath.NewInt(assetsUnfilled), - AssetsFilledAmt: sdkmath.NewInt(assetsFilled), - } - if assetsUnfilled == 0 { - rv.AssetsUnfilledAmt = ZeroAmtAfterSub - } - if len(dists) > 0 { - rv.AssetDists = dists - } - return rv - - } - askOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*Distribution) *OrderFulfillment { - order := NewOrder(orderID).WithAsk(&AskOrder{Assets: sdk.NewInt64Coin("apple", 999)}) - return newOF(order, assetsUnfilled, assetsFilled, dists...) - } - bidOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*Distribution) *OrderFulfillment { - order := NewOrder(orderID).WithBid(&BidOrder{Assets: sdk.NewInt64Coin("apple", 999)}) - return newOF(order, assetsUnfilled, assetsFilled, dists...) - } - dist := func(addr string, amt int64) *Distribution { - return &Distribution{ - Address: addr, - Amount: sdkmath.NewInt(amt), - } - } - - tests := []struct { - name string - receiver *OrderFulfillment - order OrderI - amount sdkmath.Int - expRes *OrderFulfillment - expErr string - }{ - { - name: "assets unfilled less than amount: ask, ask", - receiver: askOF(1, 5, 0), - order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), - amount: sdkmath.NewInt(6), - expErr: "cannot fill ask order 1 having assets left \"5apple\" with \"6apple\" from ask order 2: overfill", - }, - { - name: "assets unfilled less than amount: ask, bid", - receiver: askOF(3, 5, 0), - order: NewOrder(4).WithBid(&BidOrder{Buyer: "buYer"}), - amount: sdkmath.NewInt(6), - expErr: "cannot fill ask order 3 having assets left \"5apple\" with \"6apple\" from bid order 4: overfill", - }, - { - name: "assets unfilled less than amount: bid, ask", - receiver: bidOF(5, 5, 0), - order: NewOrder(6).WithAsk(&AskOrder{Seller: "seLLer"}), - amount: sdkmath.NewInt(6), - expErr: "cannot fill bid order 5 having assets left \"5apple\" with \"6apple\" from ask order 6: overfill", - }, - { - name: "assets unfilled less than amount: bid, bid", - receiver: bidOF(7, 5, 0), - order: NewOrder(8).WithBid(&BidOrder{Buyer: "buYer"}), - amount: sdkmath.NewInt(6), - expErr: "cannot fill bid order 7 having assets left \"5apple\" with \"6apple\" from bid order 8: overfill", - }, - { - name: "assets unfilled equals amount: ask, bid", - receiver: askOF(1, 12345, 0), - order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), - amount: sdkmath.NewInt(12345), - expRes: askOF(1, 0, 12345, dist("buYer", 12345)), - }, - { - name: "assets unfilled equals amount: bid, ask", - receiver: bidOF(1, 12345, 0), - order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), - amount: sdkmath.NewInt(12345), - expRes: bidOF(1, 0, 12345, dist("seLLer", 12345)), - }, - { - name: "assets unfilled more than amount: ask, bid", - receiver: askOF(1, 12345, 0), - order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), - amount: sdkmath.NewInt(300), - expRes: askOF(1, 12045, 300, dist("buYer", 300)), - }, - { - name: "assets unfilled more than amount: bid, ask", - receiver: bidOF(1, 12345, 0), - order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), - amount: sdkmath.NewInt(300), - expRes: bidOF(1, 12045, 300, dist("seLLer", 300)), - }, - { - name: "already has 2 dists: ask, bid", - receiver: askOF(1, 12300, 45, dist("bbbbb", 40), dist("YYYYY", 5)), - order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), - amount: sdkmath.NewInt(2000), - expRes: askOF(1, 10300, 2045, dist("bbbbb", 40), dist("YYYYY", 5), dist("buYer", 2000)), - }, - { - name: "already has 2 dists: bid, ask", - receiver: bidOF(1, 12300, 45, dist("sssss", 40), dist("LLLLL", 5)), - order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), - amount: sdkmath.NewInt(2000), - expRes: bidOF(1, 10300, 2045, dist("sssss", 40), dist("LLLLL", 5), dist("seLLer", 2000)), - }, - { - name: "amt more than filled, ask, bid", - receiver: askOF(1, 45, 12300, dist("ssss", 12300)), - order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), - amount: sdkmath.NewInt(45), - expRes: askOF(1, 0, 12345, dist("ssss", 12300), dist("buYer", 45)), - }, - { - name: "amt more than filled, bid, ask", - receiver: bidOF(1, 45, 12300, dist("ssss", 12300)), - order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), - amount: sdkmath.NewInt(45), - expRes: bidOF(1, 0, 12345, dist("ssss", 12300), dist("seLLer", 45)), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - orig := copyOrderFulfillment(tc.receiver) - if tc.expRes == nil { - tc.expRes = copyOrderFulfillment(tc.receiver) - } - var err error - testFunc := func() { - err = tc.receiver.DistributeAssets(tc.order, tc.amount) - } - require.NotPanics(t, testFunc, "DistributeAssets") - assertions.AssertErrorValue(t, err, tc.expErr, "DistributeAssets error") - if !assertEqualOrderFulfillments(t, tc.expRes, tc.receiver, "OrderFulfillment after DistributeAssets") { - t.Logf("Original: %s", orderFulfillmentString(orig)) - t.Logf(" Amount: %s", tc.amount) - } - }) - } -} - -func TestDistributeAssets(t *testing.T) { - seller, buyer := "SelleR", "BuyeR" - newOF := func(order *Order, assetsUnfilled, assetsFilled int64, dists ...*Distribution) *OrderFulfillment { - rv := &OrderFulfillment{ - Order: order, - AssetsUnfilledAmt: sdkmath.NewInt(assetsUnfilled), - AssetsFilledAmt: sdkmath.NewInt(assetsFilled), - } - if assetsUnfilled == 0 { - rv.AssetsUnfilledAmt = ZeroAmtAfterSub - } - if len(dists) > 0 { - rv.AssetDists = dists - } - return rv - - } - askOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*Distribution) *OrderFulfillment { - order := NewOrder(orderID).WithAsk(&AskOrder{ - Seller: seller, - Assets: sdk.NewInt64Coin("apple", 999), - }) - return newOF(order, assetsUnfilled, assetsFilled, dists...) - } - bidOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*Distribution) *OrderFulfillment { - order := NewOrder(orderID).WithBid(&BidOrder{ - Buyer: buyer, - Assets: sdk.NewInt64Coin("apple", 999), - }) - return newOF(order, assetsUnfilled, assetsFilled, dists...) - } - dist := func(addr string, amt int64) *Distribution { - return &Distribution{ - Address: addr, - Amount: sdkmath.NewInt(amt), - } - } - - tests := []struct { - name string - of1 *OrderFulfillment - of2 *OrderFulfillment - amount sdkmath.Int - expOF1 *OrderFulfillment - expOF2 *OrderFulfillment - expErr string - }{ - { - name: "amount more than of1 unfilled: ask bid", - of1: askOF(1, 5, 0), - of2: bidOF(2, 6, 0), - amount: sdkmath.NewInt(6), - expOF2: bidOF(2, 0, 6, dist(seller, 6)), - expErr: "cannot fill ask order 1 having assets left \"5apple\" with \"6apple\" from bid order 2: overfill", - }, - { - name: "amount more than of1 unfilled: bid ask", - of1: bidOF(1, 5, 0), - of2: askOF(2, 6, 0), - amount: sdkmath.NewInt(6), - expOF2: askOF(2, 0, 6, dist(buyer, 6)), - expErr: "cannot fill bid order 1 having assets left \"5apple\" with \"6apple\" from ask order 2: overfill", - }, - { - name: "amount more than of2 unfilled: ask, bid", - of1: askOF(1, 6, 0), - of2: bidOF(2, 5, 0), - amount: sdkmath.NewInt(6), - expOF1: askOF(1, 0, 6, dist(buyer, 6)), - expErr: "cannot fill bid order 2 having assets left \"5apple\" with \"6apple\" from ask order 1: overfill", - }, - { - name: "amount more than of2 unfilled: bid, ask", - of1: bidOF(1, 6, 0), - of2: askOF(2, 5, 0), - amount: sdkmath.NewInt(6), - expOF1: bidOF(1, 0, 6, dist(seller, 6)), - expErr: "cannot fill ask order 2 having assets left \"5apple\" with \"6apple\" from bid order 1: overfill", - }, - { - name: "amount more than both unfilled: ask, bid", - of1: askOF(1, 5, 0), - of2: bidOF(2, 4, 0), - amount: sdkmath.NewInt(6), - expErr: "cannot fill ask order 1 having assets left \"5apple\" with \"6apple\" from bid order 2: overfill" + "\n" + - "cannot fill bid order 2 having assets left \"4apple\" with \"6apple\" from ask order 1: overfill", - }, - { - name: "amount more than both unfilled: ask, bid", - of1: bidOF(1, 5, 0), - of2: askOF(2, 4, 0), - amount: sdkmath.NewInt(6), - expErr: "cannot fill bid order 1 having assets left \"5apple\" with \"6apple\" from ask order 2: overfill" + "\n" + - "cannot fill ask order 2 having assets left \"4apple\" with \"6apple\" from bid order 1: overfill", - }, - { - name: "ask bid", - of1: askOF(1, 10, 55, dist("bbb", 55)), - of2: bidOF(2, 10, 0), - amount: sdkmath.NewInt(9), - expOF1: askOF(1, 1, 64, dist("bbb", 55), dist(buyer, 9)), - expOF2: bidOF(2, 1, 9, dist(seller, 9)), - }, - { - name: "bid ask", - of1: bidOF(1, 10, 55, dist("sss", 55)), - of2: askOF(2, 10, 3, dist("bbb", 3)), - amount: sdkmath.NewInt(10), - expOF1: bidOF(1, 0, 65, dist("sss", 55), dist(seller, 10)), - expOF2: askOF(2, 0, 13, dist("bbb", 3), dist(buyer, 10)), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - origOF1 := copyOrderFulfillment(tc.of1) - origOF2 := copyOrderFulfillment(tc.of2) - if tc.expOF1 == nil { - tc.expOF1 = copyOrderFulfillment(tc.of1) - } - if tc.expOF2 == nil { - tc.expOF2 = copyOrderFulfillment(tc.of2) - } - - var err error - testFunc := func() { - err = DistributeAssets(tc.of1, tc.of2, tc.amount) - } - require.NotPanics(t, testFunc, "DistributeAssets") - assertions.AssertErrorValue(t, err, tc.expErr, "DistributeAssets error") - if !assertEqualOrderFulfillments(t, tc.expOF1, tc.of1, "of1 after DistributeAssets") { - t.Logf("Original: %s", orderFulfillmentString(origOF1)) - t.Logf(" Amount: %s", tc.amount) - } - if !assertEqualOrderFulfillments(t, tc.expOF2, tc.of2, "of2 after DistributeAssets") { - t.Logf("Original: %s", orderFulfillmentString(origOF2)) - t.Logf(" Amount: %s", tc.amount) - } - }) - } -} - -func TestOrderFulfillment_DistributePrice(t *testing.T) { - newOF := func(order *Order, priceLeft, priceApplied int64, dists ...*Distribution) *OrderFulfillment { - rv := &OrderFulfillment{ - Order: order, - PriceLeftAmt: sdkmath.NewInt(priceLeft), - PriceAppliedAmt: sdkmath.NewInt(priceApplied), - } - if priceLeft == 0 { - rv.PriceLeftAmt = ZeroAmtAfterSub - } - if len(dists) > 0 { - rv.PriceDists = dists - } - return rv - - } - askOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*Distribution) *OrderFulfillment { - order := NewOrder(orderID).WithAsk(&AskOrder{Price: sdk.NewInt64Coin("peach", 999)}) - return newOF(order, priceLeft, priceApplied, dists...) - } - bidOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*Distribution) *OrderFulfillment { - order := NewOrder(orderID).WithBid(&BidOrder{Price: sdk.NewInt64Coin("peach", 999)}) - return newOF(order, priceLeft, priceApplied, dists...) - } - dist := func(addr string, amt int64) *Distribution { - return &Distribution{ - Address: addr, - Amount: sdkmath.NewInt(amt), - } - } - - tests := []struct { - name string - receiver *OrderFulfillment - order OrderI - amount sdkmath.Int - expRes *OrderFulfillment - expErr string - }{ - { - name: "assets unfilled less than amount: ask, ask", - receiver: askOF(1, 5, 0), - order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), - amount: sdkmath.NewInt(6), - expRes: askOF(1, -1, 6, dist("seLLer", 6)), - }, - { - name: "assets unfilled less than amount: ask, bid", - receiver: askOF(3, 5, 0), - order: NewOrder(4).WithBid(&BidOrder{Buyer: "buYer"}), - amount: sdkmath.NewInt(6), - expRes: askOF(3, -1, 6, dist("buYer", 6)), - }, - { - name: "assets unfilled less than amount: bid, ask", - receiver: bidOF(5, 5, 0), - order: NewOrder(6).WithAsk(&AskOrder{Seller: "seLLer"}), - amount: sdkmath.NewInt(6), - expErr: "cannot fill bid order 5 having price left \"5peach\" to ask order 6 at a price of \"6peach\": overfill", - }, - { - name: "assets unfilled less than amount: bid, bid", - receiver: bidOF(7, 5, 0), - order: NewOrder(8).WithBid(&BidOrder{Buyer: "buYer"}), - amount: sdkmath.NewInt(6), - expErr: "cannot fill bid order 7 having price left \"5peach\" to bid order 8 at a price of \"6peach\": overfill", - }, - { - name: "assets unfilled equals amount: ask, bid", - receiver: askOF(1, 12345, 0), - order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), - amount: sdkmath.NewInt(12345), - expRes: askOF(1, 0, 12345, dist("buYer", 12345)), - }, - { - name: "assets unfilled equals amount: bid, ask", - receiver: bidOF(1, 12345, 0), - order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), - amount: sdkmath.NewInt(12345), - expRes: bidOF(1, 0, 12345, dist("seLLer", 12345)), - }, - { - name: "assets unfilled more than amount: ask, bid", - receiver: askOF(1, 12345, 0), - order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), - amount: sdkmath.NewInt(300), - expRes: askOF(1, 12045, 300, dist("buYer", 300)), - }, - { - name: "assets unfilled more than amount: bid, ask", - receiver: bidOF(1, 12345, 0), - order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), - amount: sdkmath.NewInt(300), - expRes: bidOF(1, 12045, 300, dist("seLLer", 300)), - }, - { - name: "already has 2 dists: ask, bid", - receiver: askOF(1, 12300, 45, dist("bbbbb", 40), dist("YYYYY", 5)), - order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), - amount: sdkmath.NewInt(2000), - expRes: askOF(1, 10300, 2045, dist("bbbbb", 40), dist("YYYYY", 5), dist("buYer", 2000)), - }, - { - name: "already has 2 dists: bid, ask", - receiver: bidOF(1, 12300, 45, dist("sssss", 40), dist("LLLLL", 5)), - order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), - amount: sdkmath.NewInt(2000), - expRes: bidOF(1, 10300, 2045, dist("sssss", 40), dist("LLLLL", 5), dist("seLLer", 2000)), - }, - { - name: "amt more than filled, ask, bid", - receiver: askOF(1, 45, 12300, dist("ssss", 12300)), - order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), - amount: sdkmath.NewInt(45), - expRes: askOF(1, 0, 12345, dist("ssss", 12300), dist("buYer", 45)), - }, - { - name: "amt more than filled, bid, ask", - receiver: bidOF(1, 45, 12300, dist("ssss", 12300)), - order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), - amount: sdkmath.NewInt(45), - expRes: bidOF(1, 0, 12345, dist("ssss", 12300), dist("seLLer", 45)), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - orig := copyOrderFulfillment(tc.receiver) - if tc.expRes == nil { - tc.expRes = copyOrderFulfillment(tc.receiver) - } - var err error - testFunc := func() { - err = tc.receiver.DistributePrice(tc.order, tc.amount) - } - require.NotPanics(t, testFunc, "DistributePrice") - assertions.AssertErrorValue(t, err, tc.expErr, "DistributePrice error") - if !assertEqualOrderFulfillments(t, tc.expRes, tc.receiver, "OrderFulfillment after DistributePrice") { - t.Logf("Original: %s", orderFulfillmentString(orig)) - t.Logf(" Amount: %s", tc.amount) - } - }) - } -} - -func TestDistributePrice(t *testing.T) { - seller, buyer := "SelleR", "BuyeR" - newOF := func(order *Order, priceLeft, priceApplied int64, dists ...*Distribution) *OrderFulfillment { - rv := &OrderFulfillment{ - Order: order, - PriceLeftAmt: sdkmath.NewInt(priceLeft), - PriceAppliedAmt: sdkmath.NewInt(priceApplied), - } - if priceLeft == 0 { - rv.PriceLeftAmt = ZeroAmtAfterSub - } - if len(dists) > 0 { - rv.PriceDists = dists - } - return rv - - } - askOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*Distribution) *OrderFulfillment { - order := NewOrder(orderID).WithAsk(&AskOrder{ - Seller: seller, - Price: sdk.NewInt64Coin("peach", 999), - }) - return newOF(order, priceLeft, priceApplied, dists...) - } - bidOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*Distribution) *OrderFulfillment { - order := NewOrder(orderID).WithBid(&BidOrder{ - Buyer: buyer, - Price: sdk.NewInt64Coin("peach", 999), - }) - return newOF(order, priceLeft, priceApplied, dists...) - } - dist := func(addr string, amt int64) *Distribution { - return &Distribution{ - Address: addr, - Amount: sdkmath.NewInt(amt), - } - } - - tests := []struct { - name string - of1 *OrderFulfillment - of2 *OrderFulfillment - amount sdkmath.Int - expOF1 *OrderFulfillment - expOF2 *OrderFulfillment - expErr string - }{ - { - name: "amount more than of1 unfilled: ask bid", - of1: askOF(1, 5, 0), - of2: bidOF(2, 6, 0), - amount: sdkmath.NewInt(6), - expOF1: askOF(1, -1, 6, dist(buyer, 6)), - expOF2: bidOF(2, 0, 6, dist(seller, 6)), - }, - { - name: "amount more than of1 unfilled: bid ask", - of1: bidOF(1, 5, 0), - of2: askOF(2, 6, 0), - amount: sdkmath.NewInt(6), - expOF2: askOF(2, 0, 6, dist(buyer, 6)), - expErr: "cannot fill bid order 1 having price left \"5peach\" to ask order 2 at a price of \"6peach\": overfill", - }, - { - name: "amount more than of2 unfilled: ask, bid", - of1: askOF(1, 6, 0), - of2: bidOF(2, 5, 0), - amount: sdkmath.NewInt(6), - expOF1: askOF(1, 0, 6, dist(buyer, 6)), - expErr: "cannot fill bid order 2 having price left \"5peach\" to ask order 1 at a price of \"6peach\": overfill", - }, - { - name: "amount more than of2 unfilled: bid, ask", - of1: bidOF(1, 6, 0), - of2: askOF(2, 5, 0), - amount: sdkmath.NewInt(6), - expOF1: bidOF(1, 0, 6, dist(seller, 6)), - expOF2: askOF(2, -1, 6, dist(buyer, 6)), - }, - { - name: "amount more than both unfilled: ask, bid", - of1: askOF(1, 5, 0), - of2: bidOF(2, 4, 0), - amount: sdkmath.NewInt(6), - expOF1: askOF(1, -1, 6, dist(buyer, 6)), - expErr: "cannot fill bid order 2 having price left \"4peach\" to ask order 1 at a price of \"6peach\": overfill", - }, - { - name: "amount more than both unfilled: ask, bid", - of1: bidOF(1, 5, 0), - of2: askOF(2, 4, 0), - amount: sdkmath.NewInt(6), - expOF2: askOF(2, -2, 6, dist(buyer, 6)), - expErr: "cannot fill bid order 1 having price left \"5peach\" to ask order 2 at a price of \"6peach\": overfill", - }, - { - name: "ask bid", - of1: askOF(1, 10, 55, dist("bbb", 55)), - of2: bidOF(2, 10, 0), - amount: sdkmath.NewInt(9), - expOF1: askOF(1, 1, 64, dist("bbb", 55), dist(buyer, 9)), - expOF2: bidOF(2, 1, 9, dist(seller, 9)), - }, - { - name: "bid ask", - of1: bidOF(1, 10, 55, dist("sss", 55)), - of2: askOF(2, 10, 3, dist("bbb", 3)), - amount: sdkmath.NewInt(10), - expOF1: bidOF(1, 0, 65, dist("sss", 55), dist(seller, 10)), - expOF2: askOF(2, 0, 13, dist("bbb", 3), dist(buyer, 10)), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - origOF1 := copyOrderFulfillment(tc.of1) - origOF2 := copyOrderFulfillment(tc.of2) - if tc.expOF1 == nil { - tc.expOF1 = copyOrderFulfillment(tc.of1) - } - if tc.expOF2 == nil { - tc.expOF2 = copyOrderFulfillment(tc.of2) - } - - var err error - testFunc := func() { - err = DistributePrice(tc.of1, tc.of2, tc.amount) - } - require.NotPanics(t, testFunc, "DistributePrice") - assertions.AssertErrorValue(t, err, tc.expErr, "DistributePrice error") - if !assertEqualOrderFulfillments(t, tc.expOF1, tc.of1, "of1 after DistributePrice") { - t.Logf("Original: %s", orderFulfillmentString(origOF1)) - t.Logf(" Amount: %s", tc.amount) - } - if !assertEqualOrderFulfillments(t, tc.expOF2, tc.of2, "of2 after DistributePrice") { - t.Logf("Original: %s", orderFulfillmentString(origOF2)) - t.Logf(" Amount: %s", tc.amount) - } - }) - } -} - -func TestOrderFulfillment_SplitOrder(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} - } - askOrder := func(orderID uint64, assetAmt, priceAmt int64, fees ...sdk.Coin) *Order { - askOrder := &AskOrder{ - MarketId: 55, - Seller: "samuel", - Assets: coin(assetAmt, "apple"), - Price: coin(priceAmt, "peach"), - AllowPartial: true, - } - if len(fees) > 1 { - t.Fatalf("a max of 1 fee can be provided to askOrder, actual: %s", sdk.Coins(fees)) - } - if len(fees) > 0 { - askOrder.SellerSettlementFlatFee = &fees[0] - } - return NewOrder(orderID).WithAsk(askOrder) - } - bidOrder := func(orderID uint64, assetAmt, priceAmt int64, fees ...sdk.Coin) *Order { - bidOrder := &BidOrder{ - MarketId: 55, - Buyer: "brian", - Assets: coin(assetAmt, "apple"), - Price: coin(priceAmt, "peach"), - AllowPartial: true, - } - if len(fees) > 0 { - bidOrder.BuyerSettlementFees = fees - } - return NewOrder(orderID).WithBid(bidOrder) - } - - tests := []struct { - name string - receiver *OrderFulfillment - expUnfilled *Order - expReceiver *OrderFulfillment - expErr string - }{ - { - name: "order split error: ask", - receiver: &OrderFulfillment{ - Order: askOrder(8, 10, 100), - AssetsFilledAmt: sdkmath.NewInt(-1), - }, - expErr: "cannot split ask order 8 having asset \"10apple\" at \"-1apple\": amount filled not positive", - }, - { - name: "order split error: bid", - receiver: &OrderFulfillment{ - Order: bidOrder(9, 10, 100), - AssetsFilledAmt: sdkmath.NewInt(-1), - }, - expErr: "cannot split bid order 9 having asset \"10apple\" at \"-1apple\": amount filled not positive", - }, - { - name: "okay: ask", - receiver: &OrderFulfillment{ - Order: askOrder(17, 10, 100, coin(20, "fig")), - AssetsFilledAmt: sdkmath.NewInt(9), - AssetsUnfilledAmt: sdkmath.NewInt(1), - PriceAppliedAmt: sdkmath.NewInt(300), - PriceLeftAmt: sdkmath.NewInt(-200), - }, - expUnfilled: askOrder(17, 1, 10, coin(2, "fig")), - expReceiver: &OrderFulfillment{ - Order: askOrder(17, 9, 90, coin(18, "fig")), - AssetsFilledAmt: sdkmath.NewInt(9), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(300), - PriceLeftAmt: sdkmath.NewInt(-210), - }, - }, - { - name: "okay: bid", - receiver: &OrderFulfillment{ - Order: bidOrder(19, 10, 100, coin(20, "fig")), - AssetsFilledAmt: sdkmath.NewInt(9), - AssetsUnfilledAmt: sdkmath.NewInt(1), - PriceAppliedAmt: sdkmath.NewInt(300), - PriceLeftAmt: sdkmath.NewInt(-200), - }, - expUnfilled: bidOrder(19, 1, 10, coin(2, "fig")), - expReceiver: &OrderFulfillment{ - Order: bidOrder(19, 9, 90, coin(18, "fig")), - AssetsFilledAmt: sdkmath.NewInt(9), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(300), - PriceLeftAmt: sdkmath.NewInt(-210), - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - orig := copyOrderFulfillment(tc.receiver) - if tc.expReceiver == nil { - tc.expReceiver = copyOrderFulfillment(tc.receiver) - } - - var unfilled *Order - var err error - testFunc := func() { - unfilled, err = tc.receiver.SplitOrder() - } - require.NotPanics(t, testFunc, "SplitOrder") - assertions.AssertErrorValue(t, err, tc.expErr, "SplitOrder error") - assert.Equalf(t, tc.expUnfilled, unfilled, "SplitOrder unfilled order") - if !assertEqualOrderFulfillments(t, tc.expReceiver, tc.receiver, "OrderFulfillment after SplitOrder") { - t.Logf("Original: %s", orderFulfillmentString(orig)) - } - }) - } -} - -func TestOrderFulfillment_AsFilledOrder(t *testing.T) { - askOrder := NewOrder(53).WithAsk(&AskOrder{ - MarketId: 765, - Seller: "mefirst", - Assets: sdk.NewInt64Coin("apple", 15), - Price: sdk.NewInt64Coin("peach", 88), - SellerSettlementFlatFee: &sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(6)}, - AllowPartial: true, - }) - bidOrder := NewOrder(9556).WithBid(&BidOrder{ - MarketId: 145, - Buyer: "gimmiegimmie", - Assets: sdk.NewInt64Coin("acorn", 1171), - Price: sdk.NewInt64Coin("prune", 5100), - BuyerSettlementFees: sdk.NewCoins(sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(14)}), - AllowPartial: false, - }) - - tests := []struct { - name string - receiver OrderFulfillment - expected *FilledOrder - }{ - { - name: "ask order", - receiver: OrderFulfillment{ - Order: askOrder, - PriceAppliedAmt: sdkmath.NewInt(132), - FeesToPay: sdk.NewCoins(sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(7)}), - }, - expected: &FilledOrder{ - order: askOrder, - actualPrice: sdk.NewInt64Coin("peach", 132), - actualFees: sdk.NewCoins(sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(7)}), - }, - }, - { - name: "bid order", - receiver: OrderFulfillment{ - Order: bidOrder, - PriceAppliedAmt: sdkmath.NewInt(5123), - FeesToPay: sdk.NewCoins(sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(23)}), - }, - expected: &FilledOrder{ - order: bidOrder, - actualPrice: sdk.NewInt64Coin("prune", 5123), - actualFees: sdk.NewCoins(sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(23)}), - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual *FilledOrder - testFunc := func() { - actual = tc.receiver.AsFilledOrder() - } - require.NotPanics(t, testFunc, "AsFilledOrder()") - assert.Equal(t, tc.expected, actual, "AsFilledOrder() result") - }) - } -} - -func TestSumAssetsAndPrice(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} - } - askOrder := func(orderID uint64, assets, price sdk.Coin) *Order { - return NewOrder(orderID).WithAsk(&AskOrder{ - Assets: assets, - Price: price, - }) - } - bidOrder := func(orderID uint64, assets, price sdk.Coin) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - Assets: assets, - Price: price, - }) - } - - tests := []struct { - name string - orders []*Order - expAssets sdk.Coins - expPrice sdk.Coins - expPanic string - }{ - { - name: "nil orders", - orders: nil, - expAssets: nil, - expPrice: nil, - }, - { - name: "empty orders", - orders: []*Order{}, - expAssets: nil, - expPrice: nil, - }, - { - name: "nil inside order", - orders: []*Order{ - askOrder(1, coin(2, "apple"), coin(3, "plum")), - NewOrder(4), - askOrder(5, coin(6, "apple"), coin(7, "plum")), - }, - expPanic: nilSubTypeErr(4), - }, - { - name: "unknown inside order", - orders: []*Order{ - askOrder(1, coin(2, "apple"), coin(3, "plum")), - newUnknownOrder(4), - askOrder(5, coin(6, "apple"), coin(7, "plum")), - }, - expPanic: unknownSubTypeErr(4), - }, - { - name: "one order, ask", - orders: []*Order{askOrder(1, coin(2, "apple"), coin(3, "plum"))}, - expAssets: sdk.NewCoins(coin(2, "apple")), - expPrice: sdk.NewCoins(coin(3, "plum")), - }, - { - name: "one order, bid", - orders: []*Order{bidOrder(1, coin(2, "apple"), coin(3, "plum"))}, - expAssets: sdk.NewCoins(coin(2, "apple")), - expPrice: sdk.NewCoins(coin(3, "plum")), - }, - { - name: "2 orders, same denoms", - orders: []*Order{ - askOrder(1, coin(2, "apple"), coin(3, "plum")), - bidOrder(4, coin(5, "apple"), coin(6, "plum")), - }, - expAssets: sdk.NewCoins(coin(7, "apple")), - expPrice: sdk.NewCoins(coin(9, "plum")), - }, - { - name: "2 orders, diff denoms", - orders: []*Order{ - bidOrder(1, coin(2, "avocado"), coin(3, "peach")), - askOrder(4, coin(5, "apple"), coin(6, "plum")), - }, - expAssets: sdk.NewCoins(coin(2, "avocado"), coin(5, "apple")), - expPrice: sdk.NewCoins(coin(3, "peach"), coin(6, "plum")), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var assets, price sdk.Coins - testFunc := func() { - assets, price = sumAssetsAndPrice(tc.orders) - } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "sumAssetsAndPrice") - assert.Equal(t, tc.expAssets.String(), assets.String(), "sumAssetsAndPrice") - assert.Equal(t, tc.expPrice.String(), price.String(), "sumAssetsAndPrice") - }) - } -} - -func TestSumPriceLeft(t *testing.T) { - tests := []struct { - name string - fulfillments []*OrderFulfillment - expected sdkmath.Int - }{ - { - name: "nil fulfillments", - fulfillments: nil, - expected: sdkmath.NewInt(0), - }, - { - name: "empty fulfillments", - fulfillments: []*OrderFulfillment{}, - expected: sdkmath.NewInt(0), - }, - { - name: "one fulfillment, positive", - fulfillments: []*OrderFulfillment{{PriceLeftAmt: sdkmath.NewInt(8)}}, - expected: sdkmath.NewInt(8), - }, - { - name: "one fulfillment, zero", - fulfillments: []*OrderFulfillment{{PriceLeftAmt: sdkmath.NewInt(0)}}, - expected: sdkmath.NewInt(0), - }, - { - name: "one fulfillment, negative", - fulfillments: []*OrderFulfillment{{PriceLeftAmt: sdkmath.NewInt(-3)}}, - expected: sdkmath.NewInt(-3), - }, - { - name: "three fulfillments", - fulfillments: []*OrderFulfillment{ - {PriceLeftAmt: sdkmath.NewInt(10)}, - {PriceLeftAmt: sdkmath.NewInt(200)}, - {PriceLeftAmt: sdkmath.NewInt(3000)}, - }, - expected: sdkmath.NewInt(3210), - }, - { - name: "three fulfillments, one negative", - fulfillments: []*OrderFulfillment{ - {PriceLeftAmt: sdkmath.NewInt(10)}, - {PriceLeftAmt: sdkmath.NewInt(-200)}, - {PriceLeftAmt: sdkmath.NewInt(3000)}, - }, - expected: sdkmath.NewInt(2810), - }, - { - name: "three fulfillments, all negative", - fulfillments: []*OrderFulfillment{ - {PriceLeftAmt: sdkmath.NewInt(-10)}, - {PriceLeftAmt: sdkmath.NewInt(-200)}, - {PriceLeftAmt: sdkmath.NewInt(-3000)}, - }, - expected: sdkmath.NewInt(-3210), - }, - { - name: "three fulfillments, all large", - fulfillments: []*OrderFulfillment{ - {PriceLeftAmt: newInt(t, "3,000,000,000,000,000,000,000,000,000,000,300")}, - {PriceLeftAmt: newInt(t, "40,000,000,000,000,000,000,000,000,000,000,040")}, - {PriceLeftAmt: newInt(t, "500,000,000,000,000,000,000,000,000,000,000,005")}, - }, - expected: newInt(t, "543,000,000,000,000,000,000,000,000,000,000,345"), - }, - { - name: "four fullfillments, small negative zero large", - fulfillments: []*OrderFulfillment{ - {PriceLeftAmt: sdkmath.NewInt(654_789)}, - {PriceLeftAmt: sdkmath.NewInt(-789)}, - {PriceLeftAmt: sdkmath.NewInt(0)}, - {PriceLeftAmt: newInt(t, "543,000,000,000,000,000,000,000,000,000,000,345")}, - }, - expected: newInt(t, "543,000,000,000,000,000,000,000,000,000,654,345"), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var actual sdkmath.Int - testFunc := func() { - actual = sumPriceLeft(tc.fulfillments) - } - require.NotPanics(t, testFunc, "sumPriceLeft") - assert.Equal(t, tc.expected, actual, "sumPriceLeft") - }) - } -} - -func TestBuildSettlement(t *testing.T) { - assetDenom, priceDenom := "apple", "peach" - feeDenoms := []string{"fig", "grape"} - feeCoins := func(tracer string, amts []int64) sdk.Coins { - if len(amts) == 0 { - return nil - } - if len(amts) > len(feeDenoms) { - t.Fatalf("cannot create %s with more than %d fees %v", tracer, len(feeDenoms), amts) - } - var rv sdk.Coins - for i, amt := range amts { - rv = rv.Add(sdk.NewInt64Coin(feeDenoms[i], amt)) - } - return rv - } - askOrder := func(orderID uint64, assets, price int64, allowPartial bool, fees ...int64) *Order { - if len(fees) > 1 { - t.Fatalf("cannot create ask order %d with more than 1 fees %v", orderID, fees) - } - var fee *sdk.Coin - if fc := feeCoins("", fees); !fc.IsZero() { - fee = &fc[0] - } - return NewOrder(orderID).WithAsk(&AskOrder{ - Seller: fmt.Sprintf("seller%d", orderID), - Assets: sdk.NewInt64Coin(assetDenom, assets), - Price: sdk.NewInt64Coin(priceDenom, price), - SellerSettlementFlatFee: fee, - AllowPartial: allowPartial, - }) - } - bidOrder := func(orderID uint64, assets, price int64, allowPartial bool, fees ...int64) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - Buyer: fmt.Sprintf("buyer%d", orderID), - Assets: sdk.NewInt64Coin(assetDenom, assets), - Price: sdk.NewInt64Coin(priceDenom, price), - BuyerSettlementFees: feeCoins(fmt.Sprintf("bid order %d", orderID), fees), - AllowPartial: allowPartial, - }) - } - ratio := func(price, fee int64) func(denom string) (*FeeRatio, error) { - return func(denom string) (*FeeRatio, error) { - return &FeeRatio{Price: sdk.NewInt64Coin(priceDenom, price), Fee: sdk.NewInt64Coin(feeDenoms[0], fee)}, nil - } - } - filled := func(order *Order, price int64, fees ...int64) *FilledOrder { - return NewFilledOrder(order, - sdk.NewInt64Coin(priceDenom, price), - feeCoins(fmt.Sprintf("filled order %d", order), fees)) - } - assetsInput := func(orderID uint64, amount int64) banktypes.Input { - return banktypes.Input{ - Address: fmt.Sprintf("seller%d", orderID), - Coins: sdk.NewCoins(sdk.NewInt64Coin(assetDenom, amount)), - } - } - assetsOutput := func(orderID uint64, amount int64) banktypes.Output { - return banktypes.Output{ - Address: fmt.Sprintf("buyer%d", orderID), - Coins: sdk.NewCoins(sdk.NewInt64Coin(assetDenom, amount)), - } - } - priceInput := func(orderID uint64, amount int64) banktypes.Input { - return banktypes.Input{ - Address: fmt.Sprintf("buyer%d", orderID), - Coins: sdk.NewCoins(sdk.NewInt64Coin(priceDenom, amount)), - } - } - priceOutput := func(orderID uint64, amount int64) banktypes.Output { - return banktypes.Output{ - Address: fmt.Sprintf("seller%d", orderID), - Coins: sdk.NewCoins(sdk.NewInt64Coin(priceDenom, amount)), - } - } - feeInput := func(address string, amts ...int64) banktypes.Input { - return banktypes.Input{Address: address, Coins: feeCoins("bank input for "+address, amts)} - } - - tests := []struct { - name string - askOrders []*Order - bidOrders []*Order - sellerFeeRatioLookup func(denom string) (*FeeRatio, error) - expSettlement *Settlement - expErr string - }{ - { - // error from validateCanSettle - name: "no ask orders", - askOrders: []*Order{}, - bidOrders: []*Order{bidOrder(3, 1, 10, false)}, - expErr: "no ask orders provided", - }, - { - name: "error from ratio lookup", - askOrders: []*Order{askOrder(3, 1, 10, false)}, - bidOrders: []*Order{bidOrder(4, 1, 10, false)}, - sellerFeeRatioLookup: func(denom string) (*FeeRatio, error) { - return nil, errors.New("this is a test error") - }, - expErr: "this is a test error", - }, - { - name: "error from setFeesToPay", - askOrders: []*Order{askOrder(3, 1, 10, false)}, - bidOrders: []*Order{bidOrder(4, 1, 10, false)}, - sellerFeeRatioLookup: func(denom string) (*FeeRatio, error) { - return &FeeRatio{Price: sdk.NewInt64Coin("prune", 10), Fee: sdk.NewInt64Coin("fig", 1)}, nil - }, - expErr: "failed calculate ratio fee for ask order 3: cannot apply ratio 10prune:1fig to price 10peach: incorrect price denom", - }, - { - name: "one ask, three bids: last bid not used", - askOrders: []*Order{askOrder(3, 10, 20, false)}, - bidOrders: []*Order{ - bidOrder(4, 7, 14, false), - bidOrder(5, 3, 6, false), - bidOrder(6, 1, 2, false), - }, - expErr: "bid order 6 (at index 2) has no assets filled", - }, - { - name: "three asks, one bids: last ask not used", - askOrders: []*Order{ - askOrder(11, 7, 14, false), - askOrder(12, 3, 14, false), - askOrder(13, 1, 14, false), - }, - bidOrders: []*Order{bidOrder(14, 10, 20, false)}, - expErr: "ask order 13 (at index 2) has no assets filled", - }, - { - name: "two asks, two bids: same assets total, total bid price not enough", - askOrders: []*Order{ - askOrder(1, 10, 25, false), - askOrder(2, 10, 15, false), - }, - bidOrders: []*Order{ - bidOrder(8, 10, 20, false), - bidOrder(9, 10, 19, false), - }, - expErr: "total ask price \"40peach\" is greater than total bid price \"39peach\"", - }, - { - name: "two asks, two bids: ask partial, total bid price not enough", - askOrders: []*Order{ - askOrder(1, 10, 20, false), - askOrder(2, 10, 20, true), - }, - bidOrders: []*Order{ - bidOrder(8, 10, 20, false), - bidOrder(9, 9, 17, false), - }, - expErr: "total ask price \"38peach\" is greater than total bid price \"37peach\"", - }, - { - name: "two asks, two bids: bid partial, total bid price not enough", - askOrders: []*Order{ - askOrder(1, 10, 25, false), - askOrder(2, 10, 15, false), - }, - bidOrders: []*Order{ - bidOrder(8, 10, 19, false), - bidOrder(9, 11, 22, true), - }, - expErr: "total ask price \"40peach\" is greater than total bid price \"39peach\"", - }, - { - name: "one ask, one bid: both fully filled", - askOrders: []*Order{askOrder(52, 10, 100, false, 2)}, - bidOrders: []*Order{bidOrder(11, 10, 105, false, 3, 4)}, - sellerFeeRatioLookup: ratio(4, 1), - expSettlement: &Settlement{ - Transfers: []*Transfer{ - {Inputs: []banktypes.Input{assetsInput(52, 10)}, Outputs: []banktypes.Output{assetsOutput(11, 10)}}, - {Inputs: []banktypes.Input{priceInput(11, 105)}, Outputs: []banktypes.Output{priceOutput(52, 105)}}, - }, - FeeInputs: []banktypes.Input{ - feeInput("seller52", 29), - feeInput("buyer11", 3, 4), - }, - FullyFilledOrders: []*FilledOrder{ - filled(askOrder(52, 10, 100, false, 2), 105, 29), - filled(bidOrder(11, 10, 105, false, 3, 4), 105, 3, 4), - }, - }, - }, - { - name: "one ask, one bid: ask partially filled", - askOrders: []*Order{askOrder(99, 10, 100, true)}, - bidOrders: []*Order{bidOrder(15, 9, 90, false)}, - expSettlement: &Settlement{ - Transfers: []*Transfer{ - {Inputs: []banktypes.Input{assetsInput(99, 9)}, Outputs: []banktypes.Output{assetsOutput(15, 9)}}, - {Inputs: []banktypes.Input{priceInput(15, 90)}, Outputs: []banktypes.Output{priceOutput(99, 90)}}, - }, - FullyFilledOrders: []*FilledOrder{filled(bidOrder(15, 9, 90, false), 90)}, - PartialOrderFilled: filled(askOrder(99, 9, 90, true), 90), - PartialOrderLeft: askOrder(99, 1, 10, true), - }, - }, - { - name: "one ask, one bid: ask partially filled, not allowed", - askOrders: []*Order{askOrder(99, 10, 100, false)}, - bidOrders: []*Order{bidOrder(15, 9, 90, false)}, - expErr: "cannot split ask order 99 having assets \"10apple\" at \"9apple\": order does not allow partial fulfillment", - }, - { - name: "one ask, one bid: bid partially filled", - askOrders: []*Order{askOrder(8, 9, 85, false, 2)}, - bidOrders: []*Order{bidOrder(12, 10, 100, true)}, - expSettlement: &Settlement{ - Transfers: []*Transfer{ - {Inputs: []banktypes.Input{assetsInput(8, 9)}, Outputs: []banktypes.Output{assetsOutput(12, 9)}}, - {Inputs: []banktypes.Input{priceInput(12, 90)}, Outputs: []banktypes.Output{priceOutput(8, 90)}}, - }, - FeeInputs: []banktypes.Input{feeInput("seller8", 2)}, - FullyFilledOrders: []*FilledOrder{filled(askOrder(8, 9, 85, false, 2), 90, 2)}, - PartialOrderFilled: filled(bidOrder(12, 9, 90, true), 90), - PartialOrderLeft: bidOrder(12, 1, 10, true), - }, - }, - { - name: "one ask, one bid: bid partially filled, not allowed", - askOrders: []*Order{askOrder(8, 9, 85, false, 2)}, - bidOrders: []*Order{bidOrder(12, 10, 100, false)}, - expErr: "cannot split bid order 12 having assets \"10apple\" at \"9apple\": order does not allow partial fulfillment", - }, - { - name: "one ask, five bids: all fully filled", - askOrders: []*Order{askOrder(999, 130, 260, false)}, - bidOrders: []*Order{ - bidOrder(11, 71, 140, false), - bidOrder(12, 10, 20, false, 5), - bidOrder(13, 4, 12, false), - bidOrder(14, 11, 22, false), - bidOrder(15, 34, 68, false, 8), + name: "one ask, five bids: all fully filled", + askOrders: []*Order{askOrder(999, 130, 260, false)}, + bidOrders: []*Order{ + bidOrder(11, 71, 140, false), + bidOrder(12, 10, 20, false, 5), + bidOrder(13, 4, 12, false), + bidOrder(14, 11, 22, false), + bidOrder(15, 34, 68, false, 8), }, sellerFeeRatioLookup: ratio(65, 3), expSettlement: &Settlement{ @@ -3245,9 +1033,7 @@ func TestBuildSettlement(t *testing.T) { bidOrder(44, 130, 1352, true), bidOrder(55, 100, 1000, false, 40, 20), }, - sellerFeeRatioLookup: nil, - expSettlement: nil, - expErr: "", + expErr: "cannot split bid order 55 having assets \"100apple\" at \"95apple\": order does not allow partial fulfillment", }, } @@ -3297,3484 +1083,2749 @@ func TestBuildSettlement(t *testing.T) { } } -func TestValidateCanSettle(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} - } - askOrder := func(orderID uint64, assets, price sdk.Coin) *Order { - return NewOrder(orderID).WithAsk(&AskOrder{ - Assets: assets, - Price: price, - }) +func TestNewIndexedAddrAmts(t *testing.T) { + expected := &indexedAddrAmts{ + addrs: nil, + amts: nil, + indexes: make(map[string]int), } - bidOrder := func(orderID uint64, assets, price sdk.Coin) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - Assets: assets, - Price: price, - }) + actual := newIndexedAddrAmts() + assert.Equal(t, expected, actual, "newIndexedAddrAmts result") + key := "test" + require.NotPanics(t, func() { + _ = actual.indexes[key] + }, "getting value of actual.indexes[%q]", key) +} + +func TestIndexedAddrAmts_Add(t *testing.T) { + coins := func(coins string) sdk.Coins { + rv, err := sdk.ParseCoinsNormalized(coins) + require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) + return rv } + negCoins := sdk.Coins{sdk.Coin{Denom: "neg", Amount: sdkmath.NewInt(-1)}} tests := []struct { - name string - askOrders []*Order - bidOrders []*Order - expErr string + name string + receiver *indexedAddrAmts + addr string + coins []sdk.Coin + expected *indexedAddrAmts + expPanic string }{ { - name: "nil ask orders", - askOrders: nil, - bidOrders: []*Order{bidOrder(8, coin(10, "apple"), coin(11, "peach"))}, - expErr: "no ask orders provided", + name: "empty, add one coin", + receiver: newIndexedAddrAmts(), + addr: "addr1", + coins: coins("1one"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{"addr1": 0}, + }, }, { - name: "no bid orders", - askOrders: []*Order{askOrder(7, coin(10, "apple"), coin(11, "peach"))}, - bidOrders: nil, - expErr: "no bid orders provided", + name: "empty, add two coins", + receiver: newIndexedAddrAmts(), + addr: "addr1", + coins: coins("1one,2two"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one,2two")}, + indexes: map[string]int{"addr1": 0}, + }, }, { - name: "no orders", - askOrders: nil, - bidOrders: nil, - expErr: joinErrs("no ask orders provided", "no bid orders provided"), + name: "empty, add neg coins", + receiver: newIndexedAddrAmts(), + addr: "addr1", + coins: negCoins, + expPanic: "cannot index and add invalid coin amount \"-1neg\"", }, { - name: "bid order in asks", - askOrders: []*Order{ - askOrder(7, coin(10, "apple"), coin(11, "peach")), - bidOrder(8, coin(10, "apple"), coin(11, "peach")), - askOrder(9, coin(10, "apple"), coin(11, "peach")), + name: "one addr, add to existing new denom", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{"addr1": 0}, + }, + addr: "addr1", + coins: coins("2two"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one,2two")}, + indexes: map[string]int{"addr1": 0}, }, - bidOrders: []*Order{bidOrder(22, coin(10, "apple"), coin(11, "peach"))}, - expErr: "bid order 8 is not an ask order but is in the askOrders list at index 1", }, { - name: "nil inside order in asks", - askOrders: []*Order{ - askOrder(7, coin(10, "apple"), coin(11, "peach")), - NewOrder(8), - askOrder(9, coin(10, "apple"), coin(11, "peach")), + name: "one addr, add to existing same denom", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{"addr1": 0}, + }, + addr: "addr1", + coins: coins("3one"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("4one")}, + indexes: map[string]int{"addr1": 0}, }, - bidOrders: []*Order{bidOrder(22, coin(10, "apple"), coin(11, "peach"))}, - expErr: " order 8 is not an ask order but is in the askOrders list at index 1", }, { - name: "unknown inside order in asks", - askOrders: []*Order{ - askOrder(7, coin(10, "apple"), coin(11, "peach")), - newUnknownOrder(8), - askOrder(9, coin(10, "apple"), coin(11, "peach")), + name: "one addr, add negative to existing", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{"addr1": 0}, + }, + addr: "addr1", + coins: negCoins, + expPanic: "cannot index and add invalid coin amount \"-1neg\"", + }, + { + name: "one addr, add to new", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{"addr1": 0}, + }, + addr: "addr2", + coins: coins("2two"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2"}, + amts: []sdk.Coins{coins("1one"), coins("2two")}, + indexes: map[string]int{"addr1": 0, "addr2": 1}, + }, + }, + { + name: "one addr, add to new opposite order", + receiver: &indexedAddrAmts{ + addrs: []string{"addr2"}, + amts: []sdk.Coins{coins("2two")}, + indexes: map[string]int{"addr2": 0}, + }, + addr: "addr1", + coins: coins("1one"), + expected: &indexedAddrAmts{ + addrs: []string{"addr2", "addr1"}, + amts: []sdk.Coins{coins("2two"), coins("1one")}, + indexes: map[string]int{"addr2": 0, "addr1": 1}, + }, + }, + { + name: "one addr, add negative to new", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{"addr1": 0}, + }, + addr: "addr2", + coins: negCoins, + expPanic: "cannot index and add invalid coin amount \"-1neg\"", + }, + { + name: "three addrs, add to first", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + addr: "addr1", + coins: coins("10one"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("11one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + }, + { + name: "three addrs, add to second", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + addr: "addr2", + coins: coins("10two"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("12two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, }, - bidOrders: []*Order{bidOrder(22, coin(10, "apple"), coin(11, "peach"))}, - expErr: "*exchange.unknownOrderType order 8 is not an ask order but is in the askOrders list at index 1", }, { - name: "ask order in bids", - askOrders: []*Order{askOrder(7, coin(10, "apple"), coin(11, "peach"))}, - bidOrders: []*Order{ - bidOrder(21, coin(10, "apple"), coin(11, "peach")), - askOrder(22, coin(10, "apple"), coin(11, "peach")), - bidOrder(23, coin(10, "apple"), coin(11, "peach")), + name: "three addrs, add to third", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, }, - expErr: "ask order 22 is not a bid order but is in the bidOrders list at index 1", - }, - { - name: "nil inside order in bids", - askOrders: []*Order{askOrder(7, coin(10, "apple"), coin(11, "peach"))}, - bidOrders: []*Order{ - bidOrder(21, coin(10, "apple"), coin(11, "peach")), - NewOrder(22), - bidOrder(23, coin(10, "apple"), coin(11, "peach")), + addr: "addr3", + coins: coins("10three"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("13three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, }, - expErr: " order 22 is not a bid order but is in the bidOrders list at index 1", }, { - name: "unknown inside order in bids", - askOrders: []*Order{askOrder(7, coin(10, "apple"), coin(11, "peach"))}, - bidOrders: []*Order{ - bidOrder(21, coin(10, "apple"), coin(11, "peach")), - newUnknownOrder(22), - bidOrder(23, coin(10, "apple"), coin(11, "peach")), + name: "three addrs, add two coins to second", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + addr: "addr2", + coins: coins("10four,20two"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("10four,22two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, }, - expErr: "*exchange.unknownOrderType order 22 is not a bid order but is in the bidOrders list at index 1", }, { - name: "orders in wrong args", - askOrders: []*Order{ - askOrder(15, coin(10, "apple"), coin(11, "peach")), - bidOrder(16, coin(10, "apple"), coin(11, "peach")), - askOrder(17, coin(10, "apple"), coin(11, "peach")), + name: "three addrs, add to new", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, }, - bidOrders: []*Order{ - bidOrder(91, coin(10, "apple"), coin(11, "peach")), - askOrder(92, coin(10, "apple"), coin(11, "peach")), - bidOrder(93, coin(10, "apple"), coin(11, "peach")), + addr: "good buddy", + coins: coins("10four"), + expected: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3", "good buddy"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three"), coins("10four")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2, "good buddy": 3}, }, - expErr: joinErrs( - "bid order 16 is not an ask order but is in the askOrders list at index 1", - "ask order 92 is not a bid order but is in the bidOrders list at index 1", - ), }, { - name: "multiple ask asset denoms", - askOrders: []*Order{ - askOrder(55, coin(10, "apple"), coin(11, "peach")), - askOrder(56, coin(20, "avocado"), coin(22, "peach")), - }, - bidOrders: []*Order{ - bidOrder(61, coin(10, "apple"), coin(11, "peach")), + name: "three addrs, add negative to second", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, }, - expErr: "cannot settle with multiple ask order asset denoms \"10apple,20avocado\"", + addr: "addr2", + coins: negCoins, + expPanic: "cannot index and add invalid coin amount \"-1neg\"", }, { - name: "multiple ask price denoms", - askOrders: []*Order{ - askOrder(55, coin(10, "apple"), coin(11, "peach")), - askOrder(56, coin(20, "apple"), coin(22, "plum")), - }, - bidOrders: []*Order{ - bidOrder(61, coin(10, "apple"), coin(11, "peach")), + name: "three addrs, add negative to new", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, }, - expErr: "cannot settle with multiple ask order price denoms \"11peach,22plum\"", + addr: "addr4", + coins: negCoins, + expPanic: "cannot index and add invalid coin amount \"-1neg\"", }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + orig := copyIndexedAddrAmts(tc.receiver) + defer func() { + if t.Failed() { + t.Logf("Original: %s", indexedAddrAmtsString(orig)) + t.Logf(" Actual: %s", indexedAddrAmtsString(tc.receiver)) + t.Logf("Expected: %s", indexedAddrAmtsString(tc.expected)) + } + }() + + testFunc := func() { + tc.receiver.add(tc.addr, tc.coins...) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "add(%q, %q)", tc.addr, tc.coins) + if len(tc.expPanic) == 0 { + assert.Equal(t, tc.expected, tc.receiver, "receiver after add(%q, %q)", tc.addr, tc.coins) + } + }) + } +} + +func TestIndexedAddrAmts_GetAsInputs(t *testing.T) { + coins := func(coins string) sdk.Coins { + rv, err := sdk.ParseCoinsNormalized(coins) + require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) + return rv + } + + tests := []struct { + name string + receiver *indexedAddrAmts + expected []banktypes.Input + expPanic string + }{ + {name: "nil receiver", receiver: nil, expected: nil}, + {name: "no addrs", receiver: newIndexedAddrAmts(), expected: nil}, { - name: "multiple bid asset denoms", - askOrders: []*Order{askOrder(88, coin(10, "apple"), coin(11, "peach"))}, - bidOrders: []*Order{ - bidOrder(12, coin(10, "apple"), coin(11, "peach")), - bidOrder(13, coin(20, "avocado"), coin(22, "peach")), + name: "one addr negative amount", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{{{Denom: "neg", Amount: sdkmath.NewInt(-1)}}}, + indexes: map[string]int{ + "addr1": 0, + }, }, - expErr: "cannot settle with multiple bid order asset denoms \"10apple,20avocado\"", + expPanic: "invalid indexed amount \"addr1\" for address \"-1neg\": cannot be zero or negative", }, { - name: "multiple bid price denoms", - askOrders: []*Order{askOrder(88, coin(10, "apple"), coin(11, "peach"))}, - bidOrders: []*Order{ - bidOrder(12, coin(10, "apple"), coin(11, "peach")), - bidOrder(13, coin(20, "apple"), coin(22, "plum")), + name: "one addr zero amount", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{{{Denom: "zero", Amount: sdkmath.NewInt(0)}}}, + indexes: map[string]int{ + "addr1": 0, + }, }, - expErr: "cannot settle with multiple bid order price denoms \"11peach,22plum\"", + expPanic: "invalid indexed amount \"addr1\" for address \"0zero\": cannot be zero or negative", }, { - name: "all different denoms", - askOrders: []*Order{ - askOrder(55, coin(10, "apple"), coin(11, "peach")), - askOrder(56, coin(20, "avocado"), coin(22, "plum")), + name: "one addr positive amount", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{ + "addr1": 0, + }, }, - bidOrders: []*Order{ - bidOrder(12, coin(30, "acorn"), coin(33, "prune")), - bidOrder(13, coin(40, "acai"), coin(44, "pear")), + expected: []banktypes.Input{ + {Address: "addr1", Coins: coins("1one")}, }, - expErr: joinErrs( - "cannot settle with multiple ask order asset denoms \"10apple,20avocado\"", - "cannot settle with multiple ask order price denoms \"11peach,22plum\"", - "cannot settle with multiple bid order asset denoms \"40acai,30acorn\"", - "cannot settle with multiple bid order price denoms \"44pear,33prune\"", - ), }, { - name: "different ask and bid asset denoms", - askOrders: []*Order{ - askOrder(15, coin(10, "apple"), coin(11, "peach")), - askOrder(16, coin(20, "apple"), coin(22, "peach")), + name: "two addrs", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2"}, + amts: []sdk.Coins{coins("1one"), coins("2two,3three")}, + indexes: map[string]int{ + "addr1": 0, + "addr2": 1, + }, }, - bidOrders: []*Order{ - bidOrder(2001, coin(30, "acorn"), coin(33, "peach")), - bidOrder(2002, coin(40, "acorn"), coin(44, "peach")), + expected: []banktypes.Input{ + {Address: "addr1", Coins: coins("1one")}, + {Address: "addr2", Coins: coins("2two,3three")}, }, - expErr: "cannot settle different ask \"30apple\" and bid \"70acorn\" asset denoms", }, { - name: "different ask and bid price denoms", - askOrders: []*Order{ - askOrder(15, coin(10, "apple"), coin(11, "peach")), - askOrder(16, coin(20, "apple"), coin(22, "peach")), + name: "three addrs", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two,3three"), coins("4four,5five,6six")}, + indexes: map[string]int{ + "addr1": 0, + "addr2": 1, + "addr3": 2, + }, }, - bidOrders: []*Order{ - bidOrder(2001, coin(30, "apple"), coin(33, "plum")), - bidOrder(2002, coin(40, "apple"), coin(44, "plum")), + expected: []banktypes.Input{ + {Address: "addr1", Coins: coins("1one")}, + {Address: "addr2", Coins: coins("2two,3three")}, + {Address: "addr3", Coins: coins("4four,5five,6six")}, }, - expErr: "cannot settle different ask \"33peach\" and bid \"77plum\" price denoms", }, { - name: "different ask and bid denoms", - askOrders: []*Order{ - askOrder(15, coin(10, "apple"), coin(11, "peach")), - askOrder(16, coin(20, "apple"), coin(22, "peach")), - }, - bidOrders: []*Order{ - bidOrder(2001, coin(30, "acorn"), coin(33, "plum")), - bidOrder(2002, coin(40, "acorn"), coin(44, "plum")), + name: "three addrs, negative in third", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{ + coins("1one"), + coins("2two,3three"), + { + {Denom: "acoin", Amount: sdkmath.NewInt(4)}, + {Denom: "bcoin", Amount: sdkmath.NewInt(5)}, + {Denom: "ncoin", Amount: sdkmath.NewInt(-6)}, + {Denom: "zcoin", Amount: sdkmath.NewInt(7)}, + }, + }, + indexes: map[string]int{ + "addr1": 0, + "addr2": 1, + "addr3": 2, + }, }, - expErr: joinErrs( - "cannot settle different ask \"30apple\" and bid \"70acorn\" asset denoms", - "cannot settle different ask \"33peach\" and bid \"77plum\" price denoms", - ), + expPanic: "invalid indexed amount \"addr3\" for address \"4acoin,5bcoin,-6ncoin,7zcoin\": cannot be zero or negative", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var err error + orig := copyIndexedAddrAmts(tc.receiver) + var actual []banktypes.Input testFunc := func() { - err = validateCanSettle(tc.askOrders, tc.bidOrders) + actual = tc.receiver.getAsInputs() + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAsInputs()") + assert.Equal(t, tc.expected, actual, "getAsInputs() result") + if !assert.Equal(t, orig, tc.receiver, "receiver before and after getAsInputs()") { + t.Logf("Before: %s", indexedAddrAmtsString(orig)) + t.Logf(" After: %s", indexedAddrAmtsString(tc.receiver)) } - require.NotPanics(t, testFunc, "validateCanSettle") - assertions.AssertErrorValue(t, err, tc.expErr, "validateCanSettle error") }) } } -func TestAllocateAssets(t *testing.T) { - askOrder := func(orderID uint64, assetsAmt int64, seller string) *Order { - return NewOrder(orderID).WithAsk(&AskOrder{ - Seller: seller, - Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, - }) - } - bidOrder := func(orderID uint64, assetsAmt int64, buyer string) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - Buyer: buyer, - Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, - }) - } - newOF := func(order *Order, dists ...*Distribution) *OrderFulfillment { - rv := &OrderFulfillment{ - Order: order, - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: order.GetAssets().Amount, - } - if len(dists) > 0 { - rv.AssetDists = dists - for _, d := range dists { - rv.AssetsFilledAmt = rv.AssetsFilledAmt.Add(d.Amount) - rv.AssetsUnfilledAmt = rv.AssetsUnfilledAmt.Sub(d.Amount) - } - } +func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { + coins := func(coins string) sdk.Coins { + rv, err := sdk.ParseCoinsNormalized(coins) + require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) return rv } - dist := func(addr string, amount int64) *Distribution { - return &Distribution{Address: addr, Amount: sdkmath.NewInt(amount)} - } tests := []struct { - name string - askOFs []*OrderFulfillment - bidOFs []*OrderFulfillment - expAskOFs []*OrderFulfillment - expBidOfs []*OrderFulfillment - expErr string + name string + receiver *indexedAddrAmts + expected []banktypes.Output + expPanic string }{ + {name: "nil receiver", receiver: nil, expected: nil}, + {name: "no addrs", receiver: newIndexedAddrAmts(), expected: nil}, { - name: "one ask, one bid: both full", - askOFs: []*OrderFulfillment{newOF(askOrder(5, 10, "seller"))}, - bidOFs: []*OrderFulfillment{newOF(bidOrder(6, 10, "buyer"))}, - expAskOFs: []*OrderFulfillment{newOF(askOrder(5, 10, "seller"), dist("buyer", 10))}, - expBidOfs: []*OrderFulfillment{newOF(bidOrder(6, 10, "buyer"), dist("seller", 10))}, - }, - { - name: "one ask, one bid: ask partial", - askOFs: []*OrderFulfillment{newOF(askOrder(5, 11, "seller"))}, - bidOFs: []*OrderFulfillment{newOF(bidOrder(16, 10, "buyer"))}, - expAskOFs: []*OrderFulfillment{newOF(askOrder(5, 11, "seller"), dist("buyer", 10))}, - expBidOfs: []*OrderFulfillment{newOF(bidOrder(16, 10, "buyer"), dist("seller", 10))}, + name: "one addr negative amount", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{{{Denom: "neg", Amount: sdkmath.NewInt(-1)}}}, + indexes: map[string]int{ + "addr1": 0, + }, + }, + expPanic: "invalid indexed amount \"addr1\" for address \"-1neg\": cannot be zero or negative", }, { - name: "one ask, one bid: bid partial", - askOFs: []*OrderFulfillment{newOF(askOrder(15, 10, "seller"))}, - bidOFs: []*OrderFulfillment{newOF(bidOrder(6, 11, "buyer"))}, - expAskOFs: []*OrderFulfillment{newOF(askOrder(15, 10, "seller"), dist("buyer", 10))}, - expBidOfs: []*OrderFulfillment{newOF(bidOrder(6, 11, "buyer"), dist("seller", 10))}, + name: "one addr zero amount", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{{{Denom: "zero", Amount: sdkmath.NewInt(0)}}}, + indexes: map[string]int{ + "addr1": 0, + }, + }, + expPanic: "invalid indexed amount \"addr1\" for address \"0zero\": cannot be zero or negative", }, { - name: "one ask, two bids: last bid not touched", - askOFs: []*OrderFulfillment{newOF(askOrder(22, 10, "seller"))}, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(64, 12, "buyer64")), - newOF(bidOrder(78, 1, "buyer78")), + name: "one addr positive amount", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1one")}, + indexes: map[string]int{ + "addr1": 0, + }, }, - expAskOFs: []*OrderFulfillment{newOF(askOrder(22, 10, "seller"), dist("buyer64", 10))}, - expBidOfs: []*OrderFulfillment{ - newOF(bidOrder(64, 12, "buyer64"), dist("seller", 10)), - newOF(bidOrder(78, 1, "buyer78")), + expected: []banktypes.Output{ + {Address: "addr1", Coins: coins("1one")}, }, }, { - name: "two asks, one bids: last ask not touched", - askOFs: []*OrderFulfillment{ - newOF(askOrder(888, 10, "seller888")), - newOF(askOrder(999, 10, "seller999")), + name: "two addrs", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2"}, + amts: []sdk.Coins{coins("1one"), coins("2two,3three")}, + indexes: map[string]int{ + "addr1": 0, + "addr2": 1, + }, }, - bidOFs: []*OrderFulfillment{newOF(bidOrder(6, 10, "buyer"))}, - expAskOFs: []*OrderFulfillment{ - newOF(askOrder(888, 10, "seller888"), dist("buyer", 10)), - newOF(askOrder(999, 10, "seller999")), + expected: []banktypes.Output{ + {Address: "addr1", Coins: coins("1one")}, + {Address: "addr2", Coins: coins("2two,3three")}, }, - expBidOfs: []*OrderFulfillment{newOF(bidOrder(6, 10, "buyer"), dist("seller888", 10))}, }, { - name: "two asks, three bids: both full", - askOFs: []*OrderFulfillment{ - newOF(askOrder(101, 15, "seller101")), - newOF(askOrder(102, 25, "seller102")), - }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(103, 10, "buyer103")), - newOF(bidOrder(104, 8, "buyer104")), - newOF(bidOrder(105, 22, "buyer105")), - }, - expAskOFs: []*OrderFulfillment{ - newOF(askOrder(101, 15, "seller101"), dist("buyer103", 10), dist("buyer104", 5)), - newOF(askOrder(102, 25, "seller102"), dist("buyer104", 3), dist("buyer105", 22)), + name: "three addrs", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two,3three"), coins("4four,5five,6six")}, + indexes: map[string]int{ + "addr1": 0, + "addr2": 1, + "addr3": 2, + }, }, - expBidOfs: []*OrderFulfillment{ - newOF(bidOrder(103, 10, "buyer103"), dist("seller101", 10)), - newOF(bidOrder(104, 8, "buyer104"), dist("seller101", 5), dist("seller102", 3)), - newOF(bidOrder(105, 22, "buyer105"), dist("seller102", 22)), + expected: []banktypes.Output{ + {Address: "addr1", Coins: coins("1one")}, + {Address: "addr2", Coins: coins("2two,3three")}, + {Address: "addr3", Coins: coins("4four,5five,6six")}, }, }, { - name: "two asks, three bids: ask partial", - askOFs: []*OrderFulfillment{ - newOF(askOrder(101, 15, "seller101")), - newOF(askOrder(102, 26, "seller102")), - }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(103, 10, "buyer103")), - newOF(bidOrder(104, 8, "buyer104")), - newOF(bidOrder(105, 22, "buyer105")), - }, - expAskOFs: []*OrderFulfillment{ - newOF(askOrder(101, 15, "seller101"), dist("buyer103", 10), dist("buyer104", 5)), - newOF(askOrder(102, 26, "seller102"), dist("buyer104", 3), dist("buyer105", 22)), - }, - expBidOfs: []*OrderFulfillment{ - newOF(bidOrder(103, 10, "buyer103"), dist("seller101", 10)), - newOF(bidOrder(104, 8, "buyer104"), dist("seller101", 5), dist("seller102", 3)), - newOF(bidOrder(105, 22, "buyer105"), dist("seller102", 22)), + name: "three addrs, negative in third", + receiver: &indexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{ + coins("1one"), + coins("2two,3three"), + { + {Denom: "acoin", Amount: sdkmath.NewInt(4)}, + {Denom: "bcoin", Amount: sdkmath.NewInt(5)}, + {Denom: "ncoin", Amount: sdkmath.NewInt(-6)}, + {Denom: "zcoin", Amount: sdkmath.NewInt(7)}, + }, + }, + indexes: map[string]int{ + "addr1": 0, + "addr2": 1, + "addr3": 2, + }, }, + expPanic: "invalid indexed amount \"addr3\" for address \"4acoin,5bcoin,-6ncoin,7zcoin\": cannot be zero or negative", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + orig := copyIndexedAddrAmts(tc.receiver) + var actual []banktypes.Output + testFunc := func() { + actual = tc.receiver.getAsOutputs() + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAsOutputs()") + assert.Equal(t, tc.expected, actual, "getAsOutputs() result") + if !assert.Equal(t, orig, tc.receiver, "receiver before and after getAsInputs()") { + t.Logf("Before: %s", indexedAddrAmtsString(orig)) + t.Logf(" After: %s", indexedAddrAmtsString(tc.receiver)) + } + }) + } +} + +func TestNewOrderFulfillment(t *testing.T) { + tests := []struct { + name string + order *Order + expected *orderFulfillment + expPanic string + }{ + { + name: "nil sub-order", + order: NewOrder(1), + expPanic: nilSubTypeErr(1), }, { - name: "two asks, three bids: bid partial", - askOFs: []*OrderFulfillment{ - newOF(askOrder(101, 15, "seller101")), - newOF(askOrder(102, 25, "seller102")), - }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(103, 10, "buyer103")), - newOF(bidOrder(104, 8, "buyer104")), - newOF(bidOrder(105, 23, "buyer105")), - }, - expAskOFs: []*OrderFulfillment{ - newOF(askOrder(101, 15, "seller101"), dist("buyer103", 10), dist("buyer104", 5)), - newOF(askOrder(102, 25, "seller102"), dist("buyer104", 3), dist("buyer105", 22)), + name: "ask order", + order: NewOrder(2).WithAsk(&AskOrder{ + MarketId: 10, + Assets: sdk.NewInt64Coin("adolla", 92), + Price: sdk.NewInt64Coin("pdolla", 15), + }), + expected: &orderFulfillment{ + Order: &Order{ + OrderId: 2, + Order: &Order_AskOrder{ + AskOrder: &AskOrder{ + MarketId: 10, + Assets: sdk.NewInt64Coin("adolla", 92), + Price: sdk.NewInt64Coin("pdolla", 15), + }, + }, + }, + AssetsFilledAmt: sdkmath.ZeroInt(), + AssetsUnfilledAmt: sdkmath.NewInt(92), + PriceAppliedAmt: sdkmath.ZeroInt(), + PriceLeftAmt: sdkmath.NewInt(15), + FeesToPay: nil, }, - expBidOfs: []*OrderFulfillment{ - newOF(bidOrder(103, 10, "buyer103"), dist("seller101", 10)), - newOF(bidOrder(104, 8, "buyer104"), dist("seller101", 5), dist("seller102", 3)), - newOF(bidOrder(105, 23, "buyer105"), dist("seller102", 22)), + }, + { + name: "bid order", + order: NewOrder(3).WithBid(&BidOrder{ + MarketId: 11, + Assets: sdk.NewInt64Coin("adolla", 93), + Price: sdk.NewInt64Coin("pdolla", 16), + }), + expected: &orderFulfillment{ + Order: &Order{ + OrderId: 3, + Order: &Order_BidOrder{ + BidOrder: &BidOrder{ + MarketId: 11, + Assets: sdk.NewInt64Coin("adolla", 93), + Price: sdk.NewInt64Coin("pdolla", 16), + }, + }, + }, + AssetsFilledAmt: sdkmath.ZeroInt(), + AssetsUnfilledAmt: sdkmath.NewInt(93), + PriceAppliedAmt: sdkmath.ZeroInt(), + PriceLeftAmt: sdkmath.NewInt(16), + FeesToPay: nil, }, }, - { - name: "negative ask assets unfilled", - askOFs: []*OrderFulfillment{newOF(askOrder(101, 10, "seller"), dist("buyerx", 11))}, - bidOFs: []*OrderFulfillment{newOF(bidOrder(102, 10, "buyer"))}, - expErr: "cannot fill ask order 101 having assets left \"-1apple\" with bid order 102 having " + - "assets left \"10apple\": zero or negative assets left", - }, - { - name: "negative bid assets unfilled", - askOFs: []*OrderFulfillment{newOF(askOrder(101, 10, "seller"))}, - bidOFs: []*OrderFulfillment{newOF(bidOrder(102, 10, "buyer"), dist("sellerx", 11))}, - expErr: "cannot fill ask order 101 having assets left \"10apple\" with bid order 102 having " + - "assets left \"-1apple\": zero or negative assets left", - }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - origAskOFs := copyOrderFulfillments(tc.askOFs) - origBidOFs := copyOrderFulfillments(tc.bidOFs) + var actual *orderFulfillment + defer func() { + if t.Failed() { + t.Logf(" Actual: %s", orderFulfillmentString(actual)) + t.Logf("Expected: %s", orderFulfillmentString(tc.expected)) + } + }() - var err error testFunc := func() { - err = allocateAssets(tc.askOFs, tc.bidOFs) - } - require.NotPanics(t, testFunc, "allocateAssets") - assertions.AssertErrorValue(t, err, tc.expErr, "allocateAssets error") - if len(tc.expErr) > 0 { - return - } - if !assertEqualOrderFulfillmentSlices(t, tc.expAskOFs, tc.askOFs, "askOFs after allocateAssets") { - t.Logf("Original: %s", orderFulfillmentsString(origAskOFs)) - } - if !assertEqualOrderFulfillmentSlices(t, tc.expBidOfs, tc.bidOFs, "bidOFs after allocateAssets") { - t.Logf("Original: %s", orderFulfillmentsString(origBidOFs)) + actual = newOrderFulfillment(tc.order) } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "newOrderFulfillment") + assert.Equal(t, tc.expected, actual, "newOrderFulfillment result") }) } } -func TestSplitPartial(t *testing.T) { - askOrder := func(orderID uint64, assetsAmt int64, seller string) *Order { - return NewOrder(orderID).WithAsk(&AskOrder{ - Seller: seller, - Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, - Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(assetsAmt)}, - AllowPartial: true, - }) +func TestNewOrderFulfillments(t *testing.T) { + assetCoin := func(amount int64) sdk.Coin { + return sdk.NewInt64Coin("anise", amount) } - bidOrder := func(orderID uint64, assetsAmt int64, buyer string) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - Buyer: buyer, - Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, - Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(assetsAmt)}, - AllowPartial: true, - }) + priceCoin := func(amount int64) sdk.Coin { + return sdk.NewInt64Coin("paprika", amount) } - newOF := func(order *Order, dists ...*Distribution) *OrderFulfillment { - rv := &OrderFulfillment{ - Order: order, - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: order.GetAssets().Amount, - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: order.GetPrice().Amount, + feeCoin := func(amount int64) *sdk.Coin { + rv := sdk.NewInt64Coin("fennel", amount) + return &rv + } + + askOrders := make([]*Order, 4) // ids 1, 2, 3, 4 + for j := range askOrders { + i := int64(j) + 1 + order := &AskOrder{ + MarketId: uint32(90 + i), + Seller: fmt.Sprintf("seller-%d", i), + Assets: assetCoin(1000*i + 100*i + 10*i + i), + Price: priceCoin(100*i + 10*i + i), } - if len(dists) > 0 { - rv.AssetDists = dists - for _, d := range dists { - rv.AssetsFilledAmt = rv.AssetsFilledAmt.Add(d.Amount) - rv.AssetsUnfilledAmt = rv.AssetsUnfilledAmt.Sub(d.Amount) - } - if rv.AssetsUnfilledAmt.IsZero() { - rv.AssetsUnfilledAmt = sdkmath.NewInt(0) - } + if j%2 == 0 { + order.SellerSettlementFlatFee = feeCoin(10*i + i) } - return rv + if j >= 2 { + order.AllowPartial = true + } + askOrders[j] = NewOrder(uint64(i)).WithAsk(order) } - dist := func(addr string, amount int64) *Distribution { - return &Distribution{Address: addr, Amount: sdkmath.NewInt(amount)} + + bidOrders := make([]*Order, 4) // ids 5, 6, 7, 8 + for j := range bidOrders { + i := int64(j + 5) + order := &BidOrder{ + MarketId: uint32(90 + i), + Buyer: fmt.Sprintf("buyer-%d", i), + Assets: assetCoin(1000*i + 100*i + 10*i + i), + Price: priceCoin(100*i + 10*i + i), + } + switch j { + case 0: + order.BuyerSettlementFees = sdk.Coins{*feeCoin(10*i + i)} + case 2: + order.BuyerSettlementFees = sdk.Coins{ + *feeCoin(10*i + i), + sdk.NewInt64Coin("garlic", 10000*i+1000*i+100*i+10*i+i), + } + } + if j >= 2 { + order.AllowPartial = true + } + bidOrders[j] = NewOrder(uint64(i)).WithBid(order) } tests := []struct { - name string - askOFs []*OrderFulfillment - bidOFs []*OrderFulfillment - settlement *Settlement - expAskOFs []*OrderFulfillment - expBidOfs []*OrderFulfillment - expSettlement *Settlement - expErr string + name string + orders []*Order + expected []*orderFulfillment }{ { - name: "one ask: not touched", - askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"))}, - settlement: &Settlement{}, - expErr: "ask order 8 (at index 0) has no assets filled", - }, - { - name: "one ask: partial", - askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer", 7))}, - settlement: &Settlement{}, - expAskOFs: []*OrderFulfillment{newOF(askOrder(8, 7, "seller8"), dist("buyer", 7))}, - expSettlement: &Settlement{PartialOrderLeft: askOrder(8, 3, "seller8")}, - }, - { - name: "one ask: partial, settlement already has a partial", - askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer", 7))}, - settlement: &Settlement{PartialOrderLeft: bidOrder(55, 3, "buyer")}, - expErr: "bid order 55 and ask order 8 cannot both be partially filled", + name: "nil orders", + orders: nil, + expected: []*orderFulfillment{}, }, { - name: "one ask: partial, not allowed", - askOFs: []*OrderFulfillment{ - newOF(NewOrder(8).WithAsk(&AskOrder{ - Seller: "seller8", - Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(10)}, - Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(10)}, - AllowPartial: false, - }), dist("buyer", 7))}, - settlement: &Settlement{}, - expErr: "cannot split ask order 8 having assets \"10apple\" at \"7apple\": order does not allow partial fulfillment", + name: "empty orders", + orders: []*Order{}, + expected: []*orderFulfillment{}, }, { - name: "two asks: first partial", - askOFs: []*OrderFulfillment{ - newOF(askOrder(8, 10, "seller8"), dist("buyer", 7)), - newOF(askOrder(9, 12, "seller8")), - }, - settlement: &Settlement{}, - expErr: "ask order 8 (at index 0) is not filled in full and is not the last ask order provided", + name: "1 ask order", + orders: []*Order{askOrders[0]}, + expected: []*orderFulfillment{newOrderFulfillment(askOrders[0])}, }, { - name: "two asks: last untouched", - askOFs: []*OrderFulfillment{ - newOF(askOrder(8, 10, "seller8"), dist("buyer", 10)), - newOF(askOrder(9, 12, "seller8")), - }, - settlement: &Settlement{}, - expErr: "ask order 9 (at index 1) has no assets filled", + name: "1 bid order", + orders: []*Order{bidOrders[0]}, + expected: []*orderFulfillment{newOrderFulfillment(bidOrders[0])}, }, { - name: "two asks: last partial", - askOFs: []*OrderFulfillment{ - newOF(askOrder(8, 10, "seller8"), dist("buyer", 10)), - newOF(askOrder(9, 12, "seller9"), dist("buyer", 10)), - }, - settlement: &Settlement{}, - expAskOFs: []*OrderFulfillment{ - newOF(askOrder(8, 10, "seller8"), dist("buyer", 10)), - newOF(askOrder(9, 10, "seller9"), dist("buyer", 10)), + name: "4 ask orders", + orders: []*Order{askOrders[0], askOrders[1], askOrders[2], askOrders[3]}, + expected: []*orderFulfillment{ + newOrderFulfillment(askOrders[0]), + newOrderFulfillment(askOrders[1]), + newOrderFulfillment(askOrders[2]), + newOrderFulfillment(askOrders[3]), }, - expSettlement: &Settlement{PartialOrderLeft: askOrder(9, 2, "seller9")}, - }, - - { - name: "one bid: not touched", - bidOFs: []*OrderFulfillment{newOF(bidOrder(8, 10, "buyer8"))}, - settlement: &Settlement{}, - expErr: "bid order 8 (at index 0) has no assets filled", - }, - { - name: "one bid: partial", - bidOFs: []*OrderFulfillment{newOF(bidOrder(8, 10, "buyer8"), dist("seller", 7))}, - settlement: &Settlement{}, - expBidOfs: []*OrderFulfillment{newOF(bidOrder(8, 7, "buyer8"), dist("seller", 7))}, - expSettlement: &Settlement{PartialOrderLeft: bidOrder(8, 3, "buyer8")}, - }, - { - name: "one bid: partial, not allowed", - askOFs: []*OrderFulfillment{ - newOF(NewOrder(8).WithBid(&BidOrder{ - Buyer: "buyer8", - Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(10)}, - Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(10)}, - AllowPartial: false, - }), dist("seller", 7))}, - settlement: &Settlement{}, - expErr: "cannot split bid order 8 having assets \"10apple\" at \"7apple\": order does not allow partial fulfillment", }, { - name: "two bids: first partial", - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(8, 10, "buyer8"), dist("seller", 7)), - newOF(bidOrder(9, 12, "buyer9")), + name: "4 bid orders", + orders: []*Order{bidOrders[0], bidOrders[1], bidOrders[2], bidOrders[3]}, + expected: []*orderFulfillment{ + newOrderFulfillment(bidOrders[0]), + newOrderFulfillment(bidOrders[1]), + newOrderFulfillment(bidOrders[2]), + newOrderFulfillment(bidOrders[3]), }, - settlement: &Settlement{}, - expErr: "bid order 8 (at index 0) is not filled in full and is not the last bid order provided", }, { - name: "two bids: last untouched", - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(8, 10, "buyer8"), dist("seller", 10)), - newOF(bidOrder(9, 12, "buyer9")), + name: "1 bid 1 ask", + orders: []*Order{askOrders[1], bidOrders[2]}, + expected: []*orderFulfillment{ + newOrderFulfillment(askOrders[1]), + newOrderFulfillment(bidOrders[2]), }, - settlement: &Settlement{}, - expErr: "bid order 9 (at index 1) has no assets filled", }, { - name: "two bids: last partial", - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(8, 10, "buyer8"), dist("seller", 10)), - newOF(bidOrder(9, 12, "buyer9"), dist("seller", 10)), - }, - settlement: &Settlement{}, - expBidOfs: []*OrderFulfillment{ - newOF(bidOrder(8, 10, "buyer8"), dist("seller", 10)), - newOF(bidOrder(9, 10, "buyer9"), dist("seller", 10)), + name: "1 ask 1 bid", + orders: []*Order{bidOrders[1], askOrders[2]}, + expected: []*orderFulfillment{ + newOrderFulfillment(bidOrders[1]), + newOrderFulfillment(askOrders[2]), }, - expSettlement: &Settlement{PartialOrderLeft: bidOrder(9, 2, "buyer9")}, - }, - { - name: "one ask, one bid: both partial", - askOFs: []*OrderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer9", 7))}, - bidOFs: []*OrderFulfillment{newOF(bidOrder(9, 10, "buyer9"), dist("seller8", 7))}, - settlement: &Settlement{}, - expErr: "ask order 8 and bid order 9 cannot both be partially filled", }, { - name: "three asks, three bids: no partial", - askOFs: []*OrderFulfillment{ - newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), - newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), - newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), - }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), - newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), - newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), - }, - settlement: &Settlement{}, - expAskOFs: []*OrderFulfillment{ - newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), - newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), - newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + name: "4 asks 4 bids", + orders: []*Order{ + askOrders[0], askOrders[1], askOrders[2], askOrders[3], + bidOrders[3], bidOrders[2], bidOrders[1], bidOrders[0], }, - expBidOfs: []*OrderFulfillment{ - newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), - newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), - newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), + expected: []*orderFulfillment{ + newOrderFulfillment(askOrders[0]), + newOrderFulfillment(askOrders[1]), + newOrderFulfillment(askOrders[2]), + newOrderFulfillment(askOrders[3]), + newOrderFulfillment(bidOrders[3]), + newOrderFulfillment(bidOrders[2]), + newOrderFulfillment(bidOrders[1]), + newOrderFulfillment(bidOrders[0]), }, - expSettlement: &Settlement{PartialOrderLeft: nil}, }, { - name: "three asks, three bids: partial ask", - askOFs: []*OrderFulfillment{ - newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), - newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), - newOF(askOrder(12, 21, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), - }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), - newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), - newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), - }, - settlement: &Settlement{}, - expAskOFs: []*OrderFulfillment{ - newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), - newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), - newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + name: "4 bids 4 asks", + orders: []*Order{ + bidOrders[0], bidOrders[1], bidOrders[2], bidOrders[3], + askOrders[3], askOrders[2], askOrders[1], askOrders[0], }, - expBidOfs: []*OrderFulfillment{ - newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), - newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), - newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), + expected: []*orderFulfillment{ + newOrderFulfillment(bidOrders[0]), + newOrderFulfillment(bidOrders[1]), + newOrderFulfillment(bidOrders[2]), + newOrderFulfillment(bidOrders[3]), + newOrderFulfillment(askOrders[3]), + newOrderFulfillment(askOrders[2]), + newOrderFulfillment(askOrders[1]), + newOrderFulfillment(askOrders[0]), }, - expSettlement: &Settlement{PartialOrderLeft: askOrder(12, 1, "seller12")}, }, { - name: "three asks, three bids: no partial", - askOFs: []*OrderFulfillment{ - newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), - newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), - newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + name: "interweaved 4 asks 4 bids", + orders: []*Order{ + bidOrders[3], askOrders[0], askOrders[3], bidOrders[1], + bidOrders[0], askOrders[1], bidOrders[2], askOrders[2], }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), - newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), - newOF(bidOrder(112, 11, "buyer112"), dist("seller12", 10)), + expected: []*orderFulfillment{ + newOrderFulfillment(bidOrders[3]), + newOrderFulfillment(askOrders[0]), + newOrderFulfillment(askOrders[3]), + newOrderFulfillment(bidOrders[1]), + newOrderFulfillment(bidOrders[0]), + newOrderFulfillment(askOrders[1]), + newOrderFulfillment(bidOrders[2]), + newOrderFulfillment(askOrders[2]), }, - settlement: &Settlement{}, - expAskOFs: []*OrderFulfillment{ - newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), - newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), - newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + }, + { + name: "duplicated entries", + orders: []*Order{ + askOrders[3], bidOrders[2], askOrders[3], bidOrders[2], }, - expBidOfs: []*OrderFulfillment{ - newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), - newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), - newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), + expected: []*orderFulfillment{ + newOrderFulfillment(askOrders[3]), + newOrderFulfillment(bidOrders[2]), + newOrderFulfillment(askOrders[3]), + newOrderFulfillment(bidOrders[2]), }, - expSettlement: &Settlement{PartialOrderLeft: bidOrder(112, 1, "buyer112")}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - origAskOFs := copyOrderFulfillments(tc.askOFs) - origBidOFs := copyOrderFulfillments(tc.bidOFs) - - var err error + var actual []*orderFulfillment testFunc := func() { - err = splitPartial(tc.askOFs, tc.bidOFs, tc.settlement) - } - require.NotPanics(t, testFunc, "splitPartial") - assertions.AssertErrorValue(t, err, tc.expErr, "splitPartial error") - if len(tc.expErr) > 0 { - return - } - if !assertEqualOrderFulfillmentSlices(t, tc.expAskOFs, tc.askOFs, "askOFs after splitPartial") { - t.Logf("Original: %s", orderFulfillmentsString(origAskOFs)) - } - if !assertEqualOrderFulfillmentSlices(t, tc.expBidOfs, tc.bidOFs, "bidOFs after splitPartial") { - t.Logf("Original: %s", orderFulfillmentsString(origBidOFs)) + actual = newOrderFulfillments(tc.orders) } - assert.Equalf(t, tc.expSettlement, tc.settlement, "settlement after splitPartial") + require.NotPanics(t, testFunc, "newOrderFulfillments") + assertEqualOrderFulfillmentSlices(t, tc.expected, actual, "newOrderFulfillments result") }) } } -func TestSplitOrderFulfillments(t *testing.T) { - acoin := func(amount int64) sdk.Coin { - return sdk.NewInt64Coin("acorn", amount) +func TestOrderFulfillment_AssetCoin(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } - pcoin := func(amount int64) sdk.Coin { - return sdk.NewInt64Coin("prune", amount) + bigCoin := func(amount, denom string) sdk.Coin { + amt := newInt(t, amount) + return sdk.Coin{Denom: denom, Amount: amt} } - askOrder := func(orderID uint64, assetsAmt int64, allowPartial bool) *Order { + askOrder := func(orderID uint64, assets, price sdk.Coin) *Order { return NewOrder(orderID).WithAsk(&AskOrder{ - MarketId: 123, - Seller: "sEllEr", - Assets: acoin(assetsAmt), - Price: pcoin(assetsAmt), - AllowPartial: allowPartial, + Assets: assets, + Price: price, }) } - bidOrder := func(orderID uint64, assetsAmt int64, allowPartial bool) *Order { + bidOrder := func(orderID uint64, assets, price sdk.Coin) *Order { return NewOrder(orderID).WithBid(&BidOrder{ - MarketId: 123, - Buyer: "bUyEr", - Assets: acoin(assetsAmt), - Price: pcoin(assetsAmt), - AllowPartial: allowPartial, + Assets: assets, + Price: price, }) } - newOF := func(order *Order, assetsFilledAmt int64) *OrderFulfillment { - rv := &OrderFulfillment{ - Order: order, - AssetsFilledAmt: sdkmath.NewInt(assetsFilledAmt), - AssetsUnfilledAmt: order.GetAssets().Amount.SubRaw(assetsFilledAmt), - PriceAppliedAmt: sdkmath.NewInt(assetsFilledAmt), - PriceLeftAmt: order.GetPrice().Amount.SubRaw(assetsFilledAmt), - } - // int(x).Sub(x) results in an object that is not .Equal to ZeroInt(). - // The Split function sets this to ZeroInt(). - if rv.AssetsUnfilledAmt.IsZero() { - rv.AssetsUnfilledAmt = sdkmath.ZeroInt() - } - return rv - } tests := []struct { - name string - fulfillments []*OrderFulfillment - settlement *Settlement - expFulfillments []*OrderFulfillment - expSettlement *Settlement - expErr string + name string + receiver orderFulfillment + amt sdkmath.Int + expected sdk.Coin + expPanic string }{ { - name: "one order, ask: nothing filled", - fulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, false), 0)}, - settlement: &Settlement{}, - expErr: "ask order 8 (at index 0) has no assets filled", - }, - { - name: "one order, bid: nothing filled", - fulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, false), 0)}, - settlement: &Settlement{}, - expErr: "bid order 8 (at index 0) has no assets filled", + name: "nil order", + receiver: orderFulfillment{Order: nil}, + amt: sdkmath.NewInt(0), + expPanic: "runtime error: invalid memory address or nil pointer dereference", }, { - name: "one order, ask: partially filled", - fulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, true), 13)}, - settlement: &Settlement{}, - expFulfillments: []*OrderFulfillment{newOF(askOrder(8, 13, true), 13)}, - expSettlement: &Settlement{PartialOrderLeft: askOrder(8, 40, true)}, + name: "nil inside order", + receiver: orderFulfillment{Order: NewOrder(1)}, + amt: sdkmath.NewInt(0), + expPanic: nilSubTypeErr(1), }, { - name: "one order, bid: partially filled", - fulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, true), 13)}, - settlement: &Settlement{}, - expFulfillments: []*OrderFulfillment{newOF(bidOrder(8, 13, true), 13)}, - expSettlement: &Settlement{PartialOrderLeft: bidOrder(8, 40, true)}, + name: "unknown inside order", + receiver: orderFulfillment{Order: newUnknownOrder(2)}, + amt: sdkmath.NewInt(0), + expPanic: unknownSubTypeErr(2), }, { - name: "one order, ask: partially filled, already have a partially filled", - fulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, true), 13)}, - settlement: &Settlement{PartialOrderLeft: bidOrder(951, 357, true)}, - expErr: "bid order 951 and ask order 8 cannot both be partially filled", + name: "ask order", + receiver: orderFulfillment{Order: askOrder(3, coin(4, "apple"), coin(5, "plum"))}, + amt: sdkmath.NewInt(6), + expected: coin(6, "apple"), }, { - name: "one order, bid: partially filled, already have a partially filled", - fulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, true), 13)}, - settlement: &Settlement{PartialOrderLeft: askOrder(951, 357, true)}, - expErr: "ask order 951 and bid order 8 cannot both be partially filled", + name: "ask order with negative assets", + receiver: orderFulfillment{Order: askOrder(7, coin(-8, "apple"), coin(9, "plum"))}, + amt: sdkmath.NewInt(10), + expected: coin(10, "apple"), }, { - name: "one order, ask: partially filled, split not allowed", - fulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, false), 13)}, - settlement: &Settlement{}, - expErr: "cannot split ask order 8 having assets \"53acorn\" at \"13acorn\": order does not allow partial fulfillment", + name: "ask order, negative amt", + receiver: orderFulfillment{Order: askOrder(11, coin(12, "apple"), coin(13, "plum"))}, + amt: sdkmath.NewInt(-14), + expected: coin(-14, "apple"), }, { - name: "one order, bid: partially filled, split not allowed", - fulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, false), 13)}, - settlement: &Settlement{}, - expErr: "cannot split bid order 8 having assets \"53acorn\" at \"13acorn\": order does not allow partial fulfillment", + name: "ask order with negative assets, negative amt", + receiver: orderFulfillment{Order: askOrder(15, coin(-16, "apple"), coin(17, "plum"))}, + amt: sdkmath.NewInt(-18), + expected: coin(-18, "apple"), }, { - name: "one order, ask: fully filled", - fulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, false), 53)}, - settlement: &Settlement{}, - expFulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, false), 53)}, - expSettlement: &Settlement{}, + name: "ask order, big amt", + receiver: orderFulfillment{Order: askOrder(19, coin(20, "apple"), coin(21, "plum"))}, + amt: newInt(t, "123,000,000,000,000,000,000,000,000,000,000,321"), + expected: bigCoin("123,000,000,000,000,000,000,000,000,000,000,321", "apple"), }, { - name: "one order, bid: fully filled", - fulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, false), 53)}, - settlement: &Settlement{}, - expFulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, false), 53)}, - expSettlement: &Settlement{}, + name: "bid order", + receiver: orderFulfillment{Order: bidOrder(3, coin(4, "apple"), coin(5, "plum"))}, + amt: sdkmath.NewInt(6), + expected: coin(6, "apple"), }, { - name: "one order, ask: fully filled, already have a partially filled", - fulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, false), 53)}, - settlement: &Settlement{PartialOrderLeft: bidOrder(951, 357, true)}, - expFulfillments: []*OrderFulfillment{newOF(askOrder(8, 53, false), 53)}, - expSettlement: &Settlement{PartialOrderLeft: bidOrder(951, 357, true)}, + name: "bid order with negative assets", + receiver: orderFulfillment{Order: bidOrder(7, coin(-8, "apple"), coin(9, "plum"))}, + amt: sdkmath.NewInt(10), + expected: coin(10, "apple"), }, { - name: "one order, bid: fully filled, already have a partially filled", - fulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, false), 53)}, - settlement: &Settlement{PartialOrderLeft: askOrder(951, 357, true)}, - expFulfillments: []*OrderFulfillment{newOF(bidOrder(8, 53, false), 53)}, - expSettlement: &Settlement{PartialOrderLeft: askOrder(951, 357, true)}, + name: "bid order, negative amt", + receiver: orderFulfillment{Order: bidOrder(11, coin(12, "apple"), coin(13, "plum"))}, + amt: sdkmath.NewInt(-14), + expected: coin(-14, "apple"), }, { - name: "three orders, ask: second partially filled", - fulfillments: []*OrderFulfillment{ - newOF(askOrder(8, 53, false), 53), - newOF(askOrder(9, 17, true), 16), - newOF(askOrder(10, 200, false), 0), - }, - settlement: &Settlement{}, - expErr: "ask order 9 (at index 1) is not filled in full and is not the last ask order provided", + name: "bid order with negative assets, negative amt", + receiver: orderFulfillment{Order: bidOrder(15, coin(-16, "apple"), coin(17, "plum"))}, + amt: sdkmath.NewInt(-18), + expected: coin(-18, "apple"), }, { - name: "three orders, bid: second partially filled", - fulfillments: []*OrderFulfillment{ - newOF(bidOrder(8, 53, false), 53), - newOF(bidOrder(9, 17, true), 16), - newOF(bidOrder(10, 200, false), 0), - }, - settlement: &Settlement{}, - expErr: "bid order 9 (at index 1) is not filled in full and is not the last bid order provided", + name: "bid order, big amt", + receiver: orderFulfillment{Order: bidOrder(19, coin(20, "apple"), coin(21, "plum"))}, + amt: newInt(t, "123,000,000,000,000,000,000,000,000,000,000,321"), + expected: bigCoin("123,000,000,000,000,000,000,000,000,000,000,321", "apple"), }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.receiver.AssetCoin(tc.amt) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "AssetCoin(%s)", tc.amt) + assert.Equal(t, tc.expected.String(), actual.String(), "AssetCoin(%s) result", tc.amt) + }) + } +} + +func TestOrderFulfillment_PriceCoin(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + bigCoin := func(amount, denom string) sdk.Coin { + amt := newInt(t, amount) + return sdk.Coin{Denom: denom, Amount: amt} + } + askOrder := func(orderID uint64, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Assets: assets, + Price: price, + }) + } + bidOrder := func(orderID uint64, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Assets: assets, + Price: price, + }) + } + + tests := []struct { + name string + receiver orderFulfillment + amt sdkmath.Int + expected sdk.Coin + expPanic string + }{ { - name: "three orders, ask: last not touched", - fulfillments: []*OrderFulfillment{ - newOF(askOrder(8, 53, false), 53), - newOF(askOrder(9, 17, false), 17), - newOF(askOrder(10, 200, false), 0), - }, - settlement: &Settlement{}, - expErr: "ask order 10 (at index 2) has no assets filled", + name: "nil order", + receiver: orderFulfillment{Order: nil}, + amt: sdkmath.NewInt(0), + expPanic: "runtime error: invalid memory address or nil pointer dereference", }, { - name: "three orders, bid: last not touched", - fulfillments: []*OrderFulfillment{ - newOF(bidOrder(8, 53, false), 53), - newOF(bidOrder(9, 17, false), 17), - newOF(bidOrder(10, 200, true), 0), - }, - settlement: &Settlement{}, - expErr: "bid order 10 (at index 2) has no assets filled", + name: "nil inside order", + receiver: orderFulfillment{Order: NewOrder(1)}, + amt: sdkmath.NewInt(0), + expPanic: nilSubTypeErr(1), }, { - name: "three orders, ask: last partially filled", - fulfillments: []*OrderFulfillment{ - newOF(askOrder(8, 53, false), 53), - newOF(askOrder(9, 17, false), 17), - newOF(askOrder(10, 200, true), 183), - }, - settlement: &Settlement{}, - expFulfillments: []*OrderFulfillment{ - newOF(askOrder(8, 53, false), 53), - newOF(askOrder(9, 17, false), 17), - newOF(askOrder(10, 183, true), 183), - }, - expSettlement: &Settlement{PartialOrderLeft: askOrder(10, 17, true)}, + name: "unknown inside order", + receiver: orderFulfillment{Order: newUnknownOrder(2)}, + amt: sdkmath.NewInt(0), + expPanic: unknownSubTypeErr(2), }, { - name: "three orders, bid: last partially filled", - fulfillments: []*OrderFulfillment{ - newOF(bidOrder(8, 53, false), 53), - newOF(bidOrder(9, 17, false), 17), - newOF(bidOrder(10, 200, true), 183), - }, - settlement: &Settlement{}, - expFulfillments: []*OrderFulfillment{ - newOF(bidOrder(8, 53, false), 53), - newOF(bidOrder(9, 17, false), 17), - newOF(bidOrder(10, 183, true), 183), - }, - expSettlement: &Settlement{PartialOrderLeft: bidOrder(10, 17, true)}, + name: "ask order", + receiver: orderFulfillment{Order: askOrder(3, coin(4, "apple"), coin(5, "plum"))}, + amt: sdkmath.NewInt(6), + expected: coin(6, "plum"), }, { - name: "three orders, ask: last partially filled, split not allowed", - fulfillments: []*OrderFulfillment{ - newOF(askOrder(8, 53, true), 53), - newOF(askOrder(9, 17, true), 17), - newOF(askOrder(10, 200, false), 183), - }, - settlement: &Settlement{}, - expErr: "cannot split ask order 10 having assets \"200acorn\" at \"183acorn\": order does not allow partial fulfillment", + name: "ask order with negative assets", + receiver: orderFulfillment{Order: askOrder(7, coin(-8, "apple"), coin(9, "plum"))}, + amt: sdkmath.NewInt(10), + expected: coin(10, "plum"), }, { - name: "three orders, bid: last partially filled, split not allowed", - fulfillments: []*OrderFulfillment{ - newOF(bidOrder(8, 53, true), 53), - newOF(bidOrder(9, 17, true), 17), - newOF(bidOrder(10, 200, false), 183), - }, - settlement: &Settlement{}, - expErr: "cannot split bid order 10 having assets \"200acorn\" at \"183acorn\": order does not allow partial fulfillment", + name: "ask order, negative amt", + receiver: orderFulfillment{Order: askOrder(11, coin(12, "apple"), coin(13, "plum"))}, + amt: sdkmath.NewInt(-14), + expected: coin(-14, "plum"), }, { - name: "three orders, ask: last partially filled, already have a partially filled", - fulfillments: []*OrderFulfillment{ - newOF(askOrder(8, 53, true), 53), - newOF(askOrder(9, 17, true), 17), - newOF(askOrder(10, 200, false), 183), - }, - settlement: &Settlement{PartialOrderLeft: bidOrder(857, 43, true)}, - expErr: "bid order 857 and ask order 10 cannot both be partially filled", + name: "ask order with negative assets, negative amt", + receiver: orderFulfillment{Order: askOrder(15, coin(-16, "apple"), coin(17, "plum"))}, + amt: sdkmath.NewInt(-18), + expected: coin(-18, "plum"), }, { - name: "three orders, bid: last partially filled, already have a partially filled", - fulfillments: []*OrderFulfillment{ - newOF(bidOrder(8, 53, true), 53), - newOF(bidOrder(9, 17, true), 17), - newOF(bidOrder(10, 200, false), 183), - }, - settlement: &Settlement{PartialOrderLeft: askOrder(857, 43, true)}, - expErr: "ask order 857 and bid order 10 cannot both be partially filled", + name: "ask order, big amt", + receiver: orderFulfillment{Order: askOrder(19, coin(20, "apple"), coin(21, "plum"))}, + amt: newInt(t, "123,000,000,000,000,000,000,000,000,000,000,321"), + expected: bigCoin("123,000,000,000,000,000,000,000,000,000,000,321", "plum"), }, { - name: "three orders, ask: fully filled", - fulfillments: []*OrderFulfillment{ - newOF(askOrder(8, 53, false), 53), - newOF(askOrder(9, 17, false), 17), - newOF(askOrder(10, 200, false), 200), - }, - settlement: &Settlement{}, - expFulfillments: []*OrderFulfillment{ - newOF(askOrder(8, 53, false), 53), - newOF(askOrder(9, 17, false), 17), - newOF(askOrder(10, 200, false), 200), - }, - expSettlement: &Settlement{}, + name: "bid order", + receiver: orderFulfillment{Order: bidOrder(3, coin(4, "apple"), coin(5, "plum"))}, + amt: sdkmath.NewInt(6), + expected: coin(6, "plum"), }, { - name: "three orders, bid: fully filled", - fulfillments: []*OrderFulfillment{ - newOF(bidOrder(8, 53, false), 53), - newOF(bidOrder(9, 17, false), 17), - newOF(bidOrder(10, 200, false), 200), - }, - settlement: &Settlement{}, - expFulfillments: []*OrderFulfillment{ - newOF(bidOrder(8, 53, false), 53), - newOF(bidOrder(9, 17, false), 17), - newOF(bidOrder(10, 200, false), 200), - }, - expSettlement: &Settlement{}, + name: "bid order with negative assets", + receiver: orderFulfillment{Order: bidOrder(7, coin(-8, "apple"), coin(9, "plum"))}, + amt: sdkmath.NewInt(10), + expected: coin(10, "plum"), }, { - name: "three orders, ask: fully filled, already have a partially filled", - fulfillments: []*OrderFulfillment{ - newOF(askOrder(8, 53, false), 53), - newOF(askOrder(9, 17, false), 17), - newOF(askOrder(10, 200, false), 200), - }, - settlement: &Settlement{}, - expFulfillments: []*OrderFulfillment{ - newOF(askOrder(8, 53, false), 53), - newOF(askOrder(9, 17, false), 17), - newOF(askOrder(10, 200, false), 200), - }, - expSettlement: &Settlement{}, + name: "bid order, negative amt", + receiver: orderFulfillment{Order: bidOrder(11, coin(12, "apple"), coin(13, "plum"))}, + amt: sdkmath.NewInt(-14), + expected: coin(-14, "plum"), }, { - name: "three orders, bid: fully filled, already have a partially filled", - fulfillments: []*OrderFulfillment{ - newOF(bidOrder(8, 53, false), 53), - newOF(bidOrder(9, 17, false), 17), - newOF(bidOrder(10, 200, false), 200), - }, - settlement: &Settlement{PartialOrderLeft: askOrder(857, 43, true)}, - expFulfillments: []*OrderFulfillment{ - newOF(bidOrder(8, 53, false), 53), - newOF(bidOrder(9, 17, false), 17), - newOF(bidOrder(10, 200, false), 200), - }, - expSettlement: &Settlement{PartialOrderLeft: askOrder(857, 43, true)}, + name: "bid order with negative assets, negative amt", + receiver: orderFulfillment{Order: bidOrder(15, coin(-16, "apple"), coin(17, "plum"))}, + amt: sdkmath.NewInt(-18), + expected: coin(-18, "plum"), }, + { + name: "bid order, big amt", + receiver: orderFulfillment{Order: bidOrder(19, coin(20, "apple"), coin(21, "plum"))}, + amt: newInt(t, "123,000,000,000,000,000,000,000,000,000,000,321"), + expected: bigCoin("123,000,000,000,000,000,000,000,000,000,000,321", "plum"), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.receiver.PriceCoin(tc.amt) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "PriceCoin(%s)", tc.amt) + assert.Equal(t, tc.expected.String(), actual.String(), "PriceCoin(%s) result", tc.amt) + }) + } +} + +func TestOrderFulfillment_GetAssetsFilled(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(amt)} + } + askOrder := NewOrder(444).WithAsk(&AskOrder{Assets: coin(5555)}) + bidOrder := NewOrder(666).WithBid(&BidOrder{Assets: coin(7777)}) + + newOF := func(order *Order, amt int64) orderFulfillment { + return orderFulfillment{ + Order: order, + AssetsFilledAmt: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + f orderFulfillment + exp sdk.Coin + }{ + {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, + {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, + {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, + {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, + {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, + {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.f.GetAssetsFilled() + } + require.NotPanics(t, testFunc, "GetAssetsFilled()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetAssetsFilled() result") + }) + } +} + +func TestOrderFulfillment_GetAssetsUnfilled(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(amt)} + } + askOrder := NewOrder(444).WithAsk(&AskOrder{Assets: coin(5555)}) + bidOrder := NewOrder(666).WithBid(&BidOrder{Assets: coin(7777)}) + + newOF := func(order *Order, amt int64) orderFulfillment { + return orderFulfillment{ + Order: order, + AssetsUnfilledAmt: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + f orderFulfillment + exp sdk.Coin + }{ + {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, + {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, + {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, + {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, + {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, + {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.f.GetAssetsUnfilled() + } + require.NotPanics(t, testFunc, "GetAssetsUnfilled()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetAssetsUnfilled() result") + }) + } +} + +func TestOrderFulfillment_GetPriceApplied(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} + } + askOrder := NewOrder(444).WithAsk(&AskOrder{Price: coin(5555)}) + bidOrder := NewOrder(666).WithBid(&BidOrder{Price: coin(7777)}) + + newOF := func(order *Order, amt int64) orderFulfillment { + return orderFulfillment{ + Order: order, + PriceAppliedAmt: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + f orderFulfillment + exp sdk.Coin + }{ + {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, + {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, + {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, + {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, + {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, + {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.f.GetPriceApplied() + } + require.NotPanics(t, testFunc, "GetPriceApplied()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetPriceApplied() result") + }) + } +} + +func TestOrderFulfillment_GetPriceLeft(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} + } + askOrder := NewOrder(444).WithAsk(&AskOrder{Price: coin(5555)}) + bidOrder := NewOrder(666).WithBid(&BidOrder{Price: coin(7777)}) + + newOF := func(order *Order, amt int64) orderFulfillment { + return orderFulfillment{ + Order: order, + PriceLeftAmt: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + f orderFulfillment + exp sdk.Coin + }{ + {name: "positive ask", f: newOF(askOrder, 2), exp: coin(2)}, + {name: "zero ask", f: newOF(askOrder, 0), exp: coin(0)}, + {name: "negative ask", f: newOF(askOrder, -3), exp: coin(-3)}, + {name: "positive bid", f: newOF(bidOrder, 2), exp: coin(2)}, + {name: "zero bid", f: newOF(bidOrder, 0), exp: coin(0)}, + {name: "negative bid", f: newOF(bidOrder, -3), exp: coin(-3)}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var err error + var actual sdk.Coin testFunc := func() { - err = splitOrderFulfillments(tc.fulfillments, tc.settlement) + actual = tc.f.GetPriceLeft() } - require.NotPanics(t, testFunc, "splitOrderFulfillments") - assertions.AssertErrorValue(t, err, tc.expErr, "splitOrderFulfillments error") - if len(tc.expErr) > 0 { - return + require.NotPanics(t, testFunc, "GetPriceLeft()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetPriceLeft() result") + }) + } +} + +func TestOrderFulfillment_GetOrderID(t *testing.T) { + newOF := func(orderID uint64) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(orderID), + } + } + + tests := []struct { + name string + f orderFulfillment + exp uint64 + }{ + {name: "zero", f: newOF(0), exp: 0}, + {name: "one", f: newOF(1), exp: 1}, + {name: "five", f: newOF(5), exp: 5}, + {name: "max uint32+1", f: newOF(4_294_967_296), exp: 4_294_967_296}, + {name: "max uint64", f: newOF(18_446_744_073_709_551_615), exp: 18_446_744_073_709_551_615}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual uint64 + testFunc := func() { + actual = tc.f.GetOrderID() } - assertEqualOrderFulfillmentSlices(t, tc.expFulfillments, tc.fulfillments, "fulfillments after splitOrderFulfillments") - assert.Equal(t, tc.expSettlement, tc.settlement, "settlement after splitOrderFulfillments") + require.NotPanics(t, testFunc, "GetOrderID()") + assert.Equal(t, tc.exp, actual, "GetOrderID() result") }) } } -func TestAllocatePrice(t *testing.T) { - askOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { - return NewOrder(orderID).WithAsk(&AskOrder{ - MarketId: 123, - Seller: fmt.Sprintf("seller%d", orderID), - Assets: sdk.NewInt64Coin("apple", assetsAmt), - Price: sdk.NewInt64Coin("peach", priceAmt), +func TestOrderFulfillment_IsAskOrder(t *testing.T) { + tests := []struct { + name string + f orderFulfillment + exp bool + }{ + {name: "ask", f: orderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: true}, + {name: "bid", f: orderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: false}, + {name: "nil", f: orderFulfillment{Order: NewOrder(888)}, exp: false}, + {name: "unknown", f: orderFulfillment{Order: newUnknownOrder(7)}, exp: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.f.IsAskOrder() + } + require.NotPanics(t, testFunc, "IsAskOrder()") + assert.Equal(t, tc.exp, actual, "IsAskOrder() result") }) } - bidOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - MarketId: 123, - Buyer: fmt.Sprintf("buyer%d", orderID), - Assets: sdk.NewInt64Coin("apple", assetsAmt), - Price: sdk.NewInt64Coin("peach", priceAmt), +} + +func TestOrderFulfillment_IsBidOrder(t *testing.T) { + tests := []struct { + name string + f orderFulfillment + exp bool + }{ + {name: "ask", f: orderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: false}, + {name: "bid", f: orderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: true}, + {name: "nil", f: orderFulfillment{Order: NewOrder(888)}, exp: false}, + {name: "unknown", f: orderFulfillment{Order: newUnknownOrder(9)}, exp: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.f.IsBidOrder() + } + require.NotPanics(t, testFunc, "IsBidOrder()") + assert.Equal(t, tc.exp, actual, "IsBidOrder() result") }) } - newOF := func(order *Order, dists ...*Distribution) *OrderFulfillment { - rv := NewOrderFulfillment(order) - rv.AssetsFilledAmt, rv.AssetsUnfilledAmt = rv.AssetsUnfilledAmt, rv.AssetsFilledAmt - if len(dists) > 0 { - rv.PriceDists = dists - for _, dist := range dists { - rv.PriceAppliedAmt = rv.PriceAppliedAmt.Add(dist.Amount) +} + +func TestOrderFulfillment_GetMarketID(t *testing.T) { + askOrder := func(marketID uint32) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{MarketId: marketID}), + } + } + bidOrder := func(marketID uint32) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{MarketId: marketID}), + } + } + + tests := []struct { + name string + f orderFulfillment + exp uint32 + }{ + {name: "ask zero", f: askOrder(0), exp: 0}, + {name: "ask one", f: askOrder(1), exp: 1}, + {name: "ask five", f: askOrder(5), exp: 5}, + {name: "ask max uint16+1", f: askOrder(65_536), exp: 65_536}, + {name: "ask max uint32", f: askOrder(4_294_967_295), exp: 4_294_967_295}, + {name: "bid zero", f: bidOrder(0), exp: 0}, + {name: "bid one", f: bidOrder(1), exp: 1}, + {name: "bid five", f: bidOrder(5), exp: 5}, + {name: "bid max uint16+1", f: bidOrder(65_536), exp: 65_536}, + {name: "bid max uint32", f: bidOrder(4_294_967_295), exp: 4_294_967_295}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual uint32 + testFunc := func() { + actual = tc.f.GetMarketID() } - rv.PriceLeftAmt = rv.PriceLeftAmt.Sub(rv.PriceAppliedAmt) + require.NotPanics(t, testFunc, "GetMarketID()") + assert.Equal(t, tc.exp, actual, "GetMarketID() result") + }) + } +} + +func TestOrderFulfillment_GetOwner(t *testing.T) { + askOrder := func(seller string) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{Seller: seller}), } - return rv } - dist := func(address string, amount int64) *Distribution { - return &Distribution{Address: address, Amount: sdkmath.NewInt(amount)} + bidOrder := func(buyer string) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{Buyer: buyer}), + } } + owner := sdk.AccAddress("owner_______________").String() tests := []struct { - name string - askOFs []*OrderFulfillment - bidOFs []*OrderFulfillment - expAskOFs []*OrderFulfillment - expBidOFs []*OrderFulfillment - expErr string + name string + f orderFulfillment + exp string }{ - { - name: "total ask price greater than total bid", - askOFs: []*OrderFulfillment{ - newOF(askOrder(3, 10, 20)), - newOF(askOrder(4, 10, 20)), - newOF(askOrder(5, 10, 20)), - }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(6, 10, 20)), - newOF(bidOrder(7, 10, 19)), - newOF(bidOrder(8, 10, 20)), - }, - expErr: "total ask price \"60peach\" is greater than total bid price \"59peach\"", - }, - { - name: "one ask, one bid: same price", - askOFs: []*OrderFulfillment{newOF(askOrder(3, 10, 60))}, - bidOFs: []*OrderFulfillment{newOF(bidOrder(6, 10, 60))}, - expAskOFs: []*OrderFulfillment{newOF(askOrder(3, 10, 60), dist("buyer6", 60))}, - expBidOFs: []*OrderFulfillment{newOF(bidOrder(6, 10, 60), dist("seller3", 60))}, - }, - { - name: "one ask, one bid: bid more", - askOFs: []*OrderFulfillment{newOF(askOrder(3, 10, 60))}, - bidOFs: []*OrderFulfillment{newOF(bidOrder(6, 10, 65))}, - expAskOFs: []*OrderFulfillment{newOF(askOrder(3, 10, 60), dist("buyer6", 60), dist("buyer6", 5))}, - expBidOFs: []*OrderFulfillment{newOF(bidOrder(6, 10, 65), dist("seller3", 60), dist("seller3", 5))}, - }, - { - name: "two asks, two bids: same total price, diff ask prices", - askOFs: []*OrderFulfillment{ - newOF(askOrder(3, 10, 21)), - newOF(askOrder(4, 10, 19)), - }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(6, 10, 20)), - newOF(bidOrder(7, 10, 20)), - }, - expAskOFs: []*OrderFulfillment{ - newOF(askOrder(3, 10, 21), dist("buyer6", 20), dist("buyer7", 1)), - newOF(askOrder(4, 10, 19), dist("buyer7", 19)), - }, - expBidOFs: []*OrderFulfillment{ - newOF(bidOrder(6, 10, 20), dist("seller3", 20)), - newOF(bidOrder(7, 10, 20), dist("seller3", 1), dist("seller4", 19)), - }, - }, - { - name: "three asks, three bids: same total price", - askOFs: []*OrderFulfillment{ - newOF(askOrder(3, 10, 25)), - newOF(askOrder(4, 10, 20)), - newOF(askOrder(5, 10, 15)), - }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(6, 10, 18)), - newOF(bidOrder(7, 10, 30)), - newOF(bidOrder(8, 10, 12)), - }, - expAskOFs: []*OrderFulfillment{ - newOF(askOrder(3, 10, 25), dist("buyer6", 18), dist("buyer7", 7)), - newOF(askOrder(4, 10, 20), dist("buyer7", 20)), - newOF(askOrder(5, 10, 15), dist("buyer7", 3), dist("buyer8", 12)), - }, - expBidOFs: []*OrderFulfillment{ - newOF(bidOrder(6, 10, 18), dist("seller3", 18)), - newOF(bidOrder(7, 10, 30), dist("seller3", 7), dist("seller4", 20), dist("seller5", 3)), - newOF(bidOrder(8, 10, 12), dist("seller5", 12)), - }, - }, - { - name: "three asks, three bids: bids more", - askOFs: []*OrderFulfillment{ - newOF(askOrder(3, 1, 10)), - newOF(askOrder(4, 7, 25)), - newOF(askOrder(5, 22, 30)), - }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(6, 10, 20)), - newOF(bidOrder(7, 10, 27)), - newOF(bidOrder(8, 10, 30)), - }, - // assets total = 30 - // ask price total = 65 - // bid price total = 77 - // leftover = 12 - expAskOFs: []*OrderFulfillment{ - // 12 * 1 / 30 = 0.4 => 0, then 1 - newOF(askOrder(3, 1, 10), dist("buyer6", 10), - dist("buyer8", 1)), - // 12 * 7 / 30 = 2.8 => 2, then because there'll only be 1 left, 1 - newOF(askOrder(4, 7, 25), dist("buyer6", 10), dist("buyer7", 15), - dist("buyer8", 2), dist("buyer8", 1)), - // 12 * 22 / 30 = 8.8 => 8, then nothing because leftovers run out before getting back to it. - newOF(askOrder(5, 22, 30), dist("buyer7", 12), dist("buyer8", 18), - dist("buyer8", 8)), - }, - expBidOFs: []*OrderFulfillment{ - newOF(bidOrder(6, 10, 20), dist("seller3", 10), dist("seller4", 10)), - newOF(bidOrder(7, 10, 27), dist("seller4", 15), dist("seller5", 12)), - newOF(bidOrder(8, 10, 30), dist("seller5", 18), dist("seller4", 2), - dist("seller5", 8), dist("seller3", 1), dist("seller4", 1)), - }, - }, + {name: "ask empty", f: askOrder(""), exp: ""}, + {name: "ask not a bech32", f: askOrder("owner"), exp: "owner"}, + {name: "ask beche32", f: askOrder(owner), exp: owner}, + {name: "bid empty", f: bidOrder(""), exp: ""}, + {name: "bid not a bech32", f: bidOrder("owner"), exp: "owner"}, + {name: "bid beche32", f: bidOrder(owner), exp: owner}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.f.GetOwner() + } + require.NotPanics(t, testFunc, "GetOwner()") + assert.Equal(t, tc.exp, actual, "GetOwner() result") + }) + } +} + +func TestOrderFulfillment_GetAssets(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(amt)} + } + askOrder := func(amt int64) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{Assets: coin(amt)}), + } + } + bidOrder := func(amt int64) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{Assets: coin(amt)}), + } + } + + tests := []struct { + name string + f orderFulfillment + exp sdk.Coin + }{ + {name: "ask positive", f: askOrder(123), exp: coin(123)}, + {name: "ask zero", f: askOrder(0), exp: coin(0)}, + {name: "ask negative", f: askOrder(-9), exp: coin(-9)}, + {name: "bid positive", f: bidOrder(345), exp: coin(345)}, + {name: "bid zero", f: bidOrder(0), exp: coin(0)}, + {name: "bid negative", f: bidOrder(-8), exp: coin(-8)}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var err error + var actual sdk.Coin testFunc := func() { - err = allocatePrice(tc.askOFs, tc.bidOFs) - } - require.NotPanics(t, testFunc, "allocatePrice") - assertions.AssertErrorValue(t, err, tc.expErr, "allocatePrice error") - if len(tc.expErr) > 0 { - return + actual = tc.f.GetAssets() } - assertEqualOrderFulfillmentSlices(t, tc.expAskOFs, tc.askOFs, "askOFs after allocatePrice") - assertEqualOrderFulfillmentSlices(t, tc.expBidOFs, tc.bidOFs, "bidOFs after allocatePrice") + require.NotPanics(t, testFunc, "GetAssets()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetAssets() result") }) } } -func TestSetFeesToPay(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} +func TestOrderFulfillment_GetPrice(t *testing.T) { + coin := func(amt int64) sdk.Coin { + return sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(amt)} } - askOF := func(orderID uint64, priceAppliedAmt int64, fees ...sdk.Coin) *OrderFulfillment { - askOrder := &AskOrder{Price: coin(50, "plum")} - if len(fees) > 1 { - t.Fatalf("cannot provide more than one fee to askOF(%d, %d, %q)", - orderID, priceAppliedAmt, fees) - } - if len(fees) > 0 { - askOrder.SellerSettlementFlatFee = &fees[0] - } - return &OrderFulfillment{ - Order: NewOrder(orderID).WithAsk(askOrder), - PriceAppliedAmt: sdkmath.NewInt(priceAppliedAmt), + askOrder := func(amt int64) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{Price: coin(amt)}), } } - bidOF := func(orderID uint64, priceAppliedAmt int64, fees ...sdk.Coin) *OrderFulfillment { - bidOrder := &BidOrder{Price: coin(50, "plum")} - if len(fees) > 0 { - bidOrder.BuyerSettlementFees = fees + bidOrder := func(amt int64) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{Price: coin(amt)}), } - return &OrderFulfillment{ - Order: NewOrder(orderID).WithBid(bidOrder), - PriceAppliedAmt: sdkmath.NewInt(priceAppliedAmt), + } + + tests := []struct { + name string + f orderFulfillment + exp sdk.Coin + }{ + {name: "ask positive", f: askOrder(123), exp: coin(123)}, + {name: "ask zero", f: askOrder(0), exp: coin(0)}, + {name: "ask negative", f: askOrder(-9), exp: coin(-9)}, + {name: "bid positive", f: bidOrder(345), exp: coin(345)}, + {name: "bid zero", f: bidOrder(0), exp: coin(0)}, + {name: "bid negative", f: bidOrder(-8), exp: coin(-8)}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual sdk.Coin + testFunc := func() { + actual = tc.f.GetPrice() + } + require.NotPanics(t, testFunc, "GetPrice()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetPrice() result") + }) + } +} + +func TestOrderFulfillment_GetSettlementFees(t *testing.T) { + coin := func(amt int64) *sdk.Coin { + return &sdk.Coin{Denom: "fees", Amount: sdkmath.NewInt(amt)} + } + askOrder := func(coin *sdk.Coin) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{SellerSettlementFlatFee: coin}), } } - expOF := func(f *OrderFulfillment, feesToPay ...sdk.Coin) *OrderFulfillment { - if len(feesToPay) > 0 { - f.FeesToPay = sdk.NewCoins(feesToPay...) + bidOrder := func(coins sdk.Coins) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{BuyerSettlementFees: coins}), } - return f } tests := []struct { - name string - askOFs []*OrderFulfillment - bidOFs []*OrderFulfillment - ratio *FeeRatio - expAskOFs []*OrderFulfillment - expBidOFs []*OrderFulfillment - expErr string + name string + f orderFulfillment + exp sdk.Coins }{ + {name: "ask nil", f: askOrder(nil), exp: nil}, + {name: "ask zero", f: askOrder(coin(0)), exp: sdk.Coins{*coin(0)}}, + {name: "ask positive", f: askOrder(coin(3)), exp: sdk.Coins{*coin(3)}}, + {name: "bid nil", f: bidOrder(nil), exp: nil}, + {name: "bid empty", f: bidOrder(sdk.Coins{}), exp: sdk.Coins{}}, + {name: "bid positive", f: bidOrder(sdk.Coins{*coin(3)}), exp: sdk.Coins{*coin(3)}}, + {name: "bid zero", f: bidOrder(sdk.Coins{*coin(0)}), exp: sdk.Coins{*coin(0)}}, + {name: "bid negative", f: bidOrder(sdk.Coins{*coin(-2)}), exp: sdk.Coins{*coin(-2)}}, { - name: "cannot apply ratio", - askOFs: []*OrderFulfillment{ - askOF(7777, 55, coin(20, "grape")), - askOF(5555, 71), - askOF(6666, 100), - }, - bidOFs: []*OrderFulfillment{ - bidOF(1111, 100), - bidOF(2222, 200, coin(20, "grape")), - bidOF(3333, 300), - }, - ratio: &FeeRatio{Price: coin(30, "peach"), Fee: coin(1, "fig")}, - expAskOFs: []*OrderFulfillment{ - expOF(askOF(7777, 55, coin(20, "grape"))), - expOF(askOF(5555, 71)), - expOF(askOF(6666, 100)), - }, - expBidOFs: []*OrderFulfillment{ - expOF(bidOF(1111, 100)), - expOF(bidOF(2222, 200, coin(20, "grape")), coin(20, "grape")), - expOF(bidOF(3333, 300)), - }, - expErr: joinErrs( - "failed calculate ratio fee for ask order 7777: cannot apply ratio 30peach:1fig to price 55plum: incorrect price denom", - "failed calculate ratio fee for ask order 5555: cannot apply ratio 30peach:1fig to price 71plum: incorrect price denom", - "failed calculate ratio fee for ask order 6666: cannot apply ratio 30peach:1fig to price 100plum: incorrect price denom", - ), - }, - { - name: "no ratio", - askOFs: []*OrderFulfillment{ - askOF(7777, 55, coin(20, "grape")), - askOF(5555, 71), - askOF(6666, 100), - }, - bidOFs: []*OrderFulfillment{ - bidOF(1111, 100), - bidOF(2222, 200, coin(20, "grape")), - bidOF(3333, 300), - }, - ratio: nil, - expAskOFs: []*OrderFulfillment{ - expOF(askOF(7777, 55, coin(20, "grape")), coin(20, "grape")), - expOF(askOF(5555, 71)), - expOF(askOF(6666, 100)), - }, - expBidOFs: []*OrderFulfillment{ - expOF(bidOF(1111, 100)), - expOF(bidOF(2222, 200, coin(20, "grape")), coin(20, "grape")), - expOF(bidOF(3333, 300)), - }, - }, - { - name: "with ratio", - askOFs: []*OrderFulfillment{ - askOF(7777, 55, coin(20, "grape")), - askOF(5555, 71), - askOF(6666, 100), - }, - bidOFs: []*OrderFulfillment{ - bidOF(1111, 100), - bidOF(2222, 200, coin(20, "grape")), - bidOF(3333, 300), - }, - ratio: &FeeRatio{Price: coin(30, "plum"), Fee: coin(1, "fig")}, - expAskOFs: []*OrderFulfillment{ - expOF(askOF(7777, 55, coin(20, "grape")), coin(2, "fig"), coin(20, "grape")), - expOF(askOF(5555, 71), coin(3, "fig")), - expOF(askOF(6666, 100), coin(4, "fig")), - }, - expBidOFs: []*OrderFulfillment{ - expOF(bidOF(1111, 100)), - expOF(bidOF(2222, 200, coin(20, "grape")), coin(20, "grape")), - expOF(bidOF(3333, 300)), - }, + name: "bid multiple", + f: bidOrder(sdk.Coins{*coin(987), sdk.NewInt64Coin("six", 6), sdk.Coin{Denom: "zeg", Amount: sdkmath.NewInt(-1)}}), + exp: sdk.Coins{*coin(987), sdk.NewInt64Coin("six", 6), sdk.Coin{Denom: "zeg", Amount: sdkmath.NewInt(-1)}}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var err error + var actual sdk.Coins testFunc := func() { - err = setFeesToPay(tc.askOFs, tc.bidOFs, tc.ratio) + actual = tc.f.GetSettlementFees() } - require.NotPanics(t, testFunc, "setFeesToPay") - assertions.AssertErrorValue(t, err, tc.expErr, "setFeesToPay error") - assertEqualOrderFulfillmentSlices(t, tc.expAskOFs, tc.askOFs, "askOFs after setFeesToPay") - assertEqualOrderFulfillmentSlices(t, tc.expBidOFs, tc.bidOFs, "bidOFs after setFeesToPay") + require.NotPanics(t, testFunc, "GetSettlementFees()") + assert.Equal(t, tc.exp.String(), actual.String(), "GetSettlementFees() result") }) } } -func TestValidateFulfillments(t *testing.T) { - goodAskOF := func(orderID uint64) *OrderFulfillment { - return &OrderFulfillment{ - Order: NewOrder(orderID).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("peach", 123), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - PriceAppliedAmt: sdkmath.NewInt(130), +func TestOrderFulfillment_PartialFillAllowed(t *testing.T) { + askOrder := func(allowPartial bool) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{AllowPartial: allowPartial}), } } - badAskOF := func(orderID uint64) *OrderFulfillment { - rv := goodAskOF(orderID) - rv.AssetsFilledAmt = sdkmath.NewInt(49) - return rv - } - badAskErr := func(orderID uint64) string { - return badAskOF(orderID).Validate().Error() - } - goodBidOF := func(orderID uint64) *OrderFulfillment { - return &OrderFulfillment{ - Order: NewOrder(orderID).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("peach", 123), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - PriceAppliedAmt: sdkmath.NewInt(123), + bidOrder := func(allowPartial bool) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{AllowPartial: allowPartial}), } } - badBidOF := func(orderID uint64) *OrderFulfillment { - rv := goodBidOF(orderID) - rv.AssetsFilledAmt = sdkmath.NewInt(49) - return rv + + tests := []struct { + name string + f orderFulfillment + exp bool + }{ + {name: "ask true", f: askOrder(true), exp: true}, + {name: "ask false", f: askOrder(false), exp: false}, + {name: "bid true", f: bidOrder(true), exp: true}, + {name: "bid false", f: bidOrder(false), exp: false}, } - badBidErr := func(orderID uint64) string { - return badBidOF(orderID).Validate().Error() + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual bool + testFunc := func() { + actual = tc.f.PartialFillAllowed() + } + require.NotPanics(t, testFunc, "PartialFillAllowed()") + assert.Equal(t, tc.exp, actual, "PartialFillAllowed() result") + }) } +} +func TestOrderFulfillment_GetOrderType(t *testing.T) { tests := []struct { - name string - askOFs []*OrderFulfillment - bidOFs []*OrderFulfillment - expErr string + name string + f orderFulfillment + exp string }{ - { - name: "all good", - askOFs: []*OrderFulfillment{goodAskOF(10), goodAskOF(11), goodAskOF(12)}, - bidOFs: []*OrderFulfillment{goodBidOF(20), goodBidOF(21), goodBidOF(22)}, - expErr: "", - }, - { - name: "error in one ask", - askOFs: []*OrderFulfillment{goodAskOF(10), badAskOF(11), goodAskOF(12)}, - bidOFs: []*OrderFulfillment{goodBidOF(20), goodBidOF(21), goodBidOF(22)}, - expErr: badAskErr(11), - }, - { - name: "error in one bid", - askOFs: []*OrderFulfillment{goodAskOF(10), goodAskOF(11), goodAskOF(12)}, - bidOFs: []*OrderFulfillment{goodBidOF(20), badBidOF(21), goodBidOF(22)}, - expErr: badBidErr(21), - }, - { - name: "two errors in asks", - askOFs: []*OrderFulfillment{badAskOF(10), goodAskOF(11), badAskOF(12)}, - bidOFs: []*OrderFulfillment{goodBidOF(20), goodBidOF(21), goodBidOF(22)}, - expErr: joinErrs(badAskErr(10), badAskErr(12)), - }, - { - name: "two errors in bids", - askOFs: []*OrderFulfillment{goodAskOF(10), goodAskOF(11), goodAskOF(12)}, - bidOFs: []*OrderFulfillment{badBidOF(20), goodBidOF(21), badBidOF(22)}, - expErr: joinErrs(badBidErr(20), badBidErr(22)), - }, - { - name: "error in each", - askOFs: []*OrderFulfillment{goodAskOF(10), goodAskOF(11), badAskOF(12)}, - bidOFs: []*OrderFulfillment{goodBidOF(20), badBidOF(21), goodBidOF(22)}, - expErr: joinErrs(badAskErr(12), badBidErr(21)), - }, + {name: "ask", f: orderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: OrderTypeAsk}, + {name: "bid", f: orderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: OrderTypeBid}, + {name: "nil", f: orderFulfillment{Order: NewOrder(888)}, exp: ""}, + {name: "unknown", f: orderFulfillment{Order: newUnknownOrder(8)}, exp: "*exchange.unknownOrderType"}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.f.GetOrderType() + } + require.NotPanics(t, testFunc, "GetOrderType()") + assert.Equal(t, tc.exp, actual, "GetOrderType() result") + }) + } +} + +func TestOrderFulfillment_GetOrderTypeByte(t *testing.T) { + tests := []struct { + name string + f orderFulfillment + exp byte + }{ + {name: "ask", f: orderFulfillment{Order: NewOrder(444).WithAsk(&AskOrder{})}, exp: OrderTypeByteAsk}, + {name: "bid", f: orderFulfillment{Order: NewOrder(666).WithBid(&BidOrder{})}, exp: OrderTypeByteBid}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var err error + var actual byte testFunc := func() { - err = validateFulfillments(tc.askOFs, tc.bidOFs) + actual = tc.f.GetOrderTypeByte() } - require.NotPanics(t, testFunc, "validateFulfillments") - assertions.AssertErrorValue(t, err, tc.expErr, "validateFulfillments error") + require.NotPanics(t, testFunc, "GetOrderTypeByte()") + assert.Equal(t, tc.exp, actual, "GetOrderTypeByte() result") }) } } -func TestBuildTransfers(t *testing.T) { +func TestOrderFulfillment_GetHoldAmount(t *testing.T) { tests := []struct { - name string - askOFs []*OrderFulfillment - bidOFs []*OrderFulfillment - expSettlement *Settlement - expErr string + name string + f orderFulfillment }{ { - name: "ask with negative assets filled", - askOFs: []*OrderFulfillment{ - { - Order: NewOrder(18).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 15), - Price: sdk.NewInt64Coin("plum", 42), - }), - AssetsFilledAmt: sdkmath.NewInt(-1), - PriceAppliedAmt: sdkmath.NewInt(-1), - }, - }, - expErr: "ask order 18 cannot be filled with \"-1apple\" assets: amount not positive", - }, - { - name: "bid with negative assets filled", - bidOFs: []*OrderFulfillment{ - { - Order: NewOrder(12).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 15), - Price: sdk.NewInt64Coin("plum", 42), - }), - AssetsFilledAmt: sdkmath.NewInt(-1), - PriceAppliedAmt: sdkmath.NewInt(-1), - }, + name: "ask", + f: orderFulfillment{ + Order: NewOrder(111).WithAsk(&AskOrder{ + Assets: sdk.Coin{Denom: "asset", Amount: sdkmath.NewInt(55)}, + SellerSettlementFlatFee: &sdk.Coin{Denom: "fee", Amount: sdkmath.NewInt(3)}, + }), }, - expErr: "bid order 12 cannot be filled at price \"-1plum\": amount not positive", }, { - name: "ask with negative fees to pay", - askOFs: []*OrderFulfillment{ - { - Order: NewOrder(53).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 15), - Price: sdk.NewInt64Coin("plum", 42), - }), - AssetsFilledAmt: sdkmath.NewInt(15), - AssetDists: []*Distribution{{Address: "buyer1", Amount: sdkmath.NewInt(15)}}, - PriceAppliedAmt: sdkmath.NewInt(42), - PriceDists: []*Distribution{{Address: "seller1", Amount: sdkmath.NewInt(42)}}, - FeesToPay: sdk.Coins{sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(-1)}}, - }, + name: "bid", + f: orderFulfillment{ + Order: NewOrder(111).WithBid(&BidOrder{ + Price: sdk.Coin{Denom: "price", Amount: sdkmath.NewInt(55)}, + BuyerSettlementFees: sdk.Coins{ + {Denom: "feea", Amount: sdkmath.NewInt(3)}, + {Denom: "feeb", Amount: sdkmath.NewInt(4)}, + }, + }), }, - expErr: "ask order 53 cannot pay \"-1fig\" in fees: negative amount", }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + expected := tc.f.GetHoldAmount() + var actual sdk.Coins + testFunc := func() { + actual = tc.f.GetHoldAmount() + } + require.NotPanics(t, testFunc, "GetHoldAmount()") + assert.Equal(t, expected, actual, "GetHoldAmount() result") + }) + } +} + +func TestOrderFulfillment_DistributeAssets(t *testing.T) { + newOF := func(order *Order, assetsUnfilled, assetsFilled int64, dists ...*distribution) *orderFulfillment { + rv := &orderFulfillment{ + Order: order, + AssetsUnfilledAmt: sdkmath.NewInt(assetsUnfilled), + AssetsFilledAmt: sdkmath.NewInt(assetsFilled), + } + if assetsUnfilled == 0 { + rv.AssetsUnfilledAmt = ZeroAmtAfterSub + } + if len(dists) > 0 { + rv.AssetDists = dists + } + return rv + + } + askOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*distribution) *orderFulfillment { + order := NewOrder(orderID).WithAsk(&AskOrder{Assets: sdk.NewInt64Coin("apple", 999)}) + return newOF(order, assetsUnfilled, assetsFilled, dists...) + } + bidOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*distribution) *orderFulfillment { + order := NewOrder(orderID).WithBid(&BidOrder{Assets: sdk.NewInt64Coin("apple", 999)}) + return newOF(order, assetsUnfilled, assetsFilled, dists...) + } + dist := func(addr string, amt int64) *distribution { + return &distribution{ + Address: addr, + Amount: sdkmath.NewInt(amt), + } + } + + tests := []struct { + name string + receiver *orderFulfillment + order OrderI + amount sdkmath.Int + expRes *orderFulfillment + expErr string + }{ { - name: "bid with negative fees to pay", - bidOFs: []*OrderFulfillment{ - { - Order: NewOrder(35).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 15), - Price: sdk.NewInt64Coin("plum", 42), - }), - AssetsFilledAmt: sdkmath.NewInt(15), - AssetDists: []*Distribution{{Address: "seller1", Amount: sdkmath.NewInt(15)}}, - PriceAppliedAmt: sdkmath.NewInt(42), - PriceDists: []*Distribution{{Address: "seller1", Amount: sdkmath.NewInt(42)}}, - FeesToPay: sdk.Coins{sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(-1)}}, - }, - }, - expErr: "bid order 35 cannot pay \"-1fig\" in fees: negative amount", + name: "assets unfilled less than amount: ask, ask", + receiver: askOF(1, 5, 0), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(6), + expErr: "cannot fill ask order 1 having assets left \"5apple\" with \"6apple\" from ask order 2: overfill", }, { - name: "two asks, three bids", - askOFs: []*OrderFulfillment{ - { - Order: NewOrder(77).WithAsk(&AskOrder{ - Seller: "seller77", - Assets: sdk.NewInt64Coin("apple", 15), - Price: sdk.NewInt64Coin("plum", 42), - }), - AssetsFilledAmt: sdkmath.NewInt(15), - AssetDists: []*Distribution{ - {Address: "buyer5511", Amount: sdkmath.NewInt(15)}, - }, - PriceAppliedAmt: sdkmath.NewInt(43), - PriceDists: []*Distribution{ - {Address: "buyer5511", Amount: sdkmath.NewInt(30)}, - {Address: "buyer78", Amount: sdkmath.NewInt(12)}, - {Address: "buyer9001", Amount: sdkmath.NewInt(1)}, - }, - FeesToPay: sdk.Coins{sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(11)}}, - }, - { - Order: NewOrder(3).WithAsk(&AskOrder{ - Seller: "seller3", - Assets: sdk.NewInt64Coin("apple", 43), - Price: sdk.NewInt64Coin("plum", 88), - }), - AssetsFilledAmt: sdkmath.NewInt(43), - AssetDists: []*Distribution{ - {Address: "buyer5511", Amount: sdkmath.NewInt(5)}, - {Address: "buyer78", Amount: sdkmath.NewInt(7)}, - {Address: "buyer9001", Amount: sdkmath.NewInt(31)}, - }, - PriceAppliedAmt: sdkmath.NewInt(90), - PriceDists: []*Distribution{ - {Address: "buyer78", Amount: sdkmath.NewInt(5)}, - {Address: "buyer9001", Amount: sdkmath.NewInt(83)}, - {Address: "buyer9001", Amount: sdkmath.NewInt(2)}, - }, - FeesToPay: nil, - }, - }, - bidOFs: []*OrderFulfillment{ - { - Order: NewOrder(5511).WithBid(&BidOrder{ - Buyer: "buyer5511", - Assets: sdk.NewInt64Coin("apple", 20), - Price: sdk.NewInt64Coin("plum", 30), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetDists: []*Distribution{ - {Address: "seller77", Amount: sdkmath.NewInt(15)}, - {Address: "seller3", Amount: sdkmath.NewInt(5)}, - }, - PriceAppliedAmt: sdkmath.NewInt(30), - PriceDists: []*Distribution{ - {Address: "seller77", Amount: sdkmath.NewInt(30)}, - }, - FeesToPay: sdk.Coins{sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(10)}}, - }, - { - Order: NewOrder(78).WithBid(&BidOrder{ - Buyer: "buyer78", - Assets: sdk.NewInt64Coin("apple", 7), - Price: sdk.NewInt64Coin("plum", 15), - }), - AssetsFilledAmt: sdkmath.NewInt(7), - AssetDists: []*Distribution{ - {Address: "seller3", Amount: sdkmath.NewInt(7)}, - }, - PriceAppliedAmt: sdkmath.NewInt(15), - PriceDists: []*Distribution{ - {Address: "seller77", Amount: sdkmath.NewInt(12)}, - {Address: "seller3", Amount: sdkmath.NewInt(3)}, - }, - FeesToPay: sdk.Coins{sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(4)}}, - }, - { - Order: NewOrder(9001).WithBid(&BidOrder{ - Buyer: "buyer9001", - Assets: sdk.NewInt64Coin("apple", 31), - Price: sdk.NewInt64Coin("plum", 86), - }), - AssetsFilledAmt: sdkmath.NewInt(31), - AssetDists: []*Distribution{ - {Address: "seller3", Amount: sdkmath.NewInt(31)}, - }, - PriceAppliedAmt: sdkmath.NewInt(86), - PriceDists: []*Distribution{ - {Address: "seller3", Amount: sdkmath.NewInt(83)}, - {Address: "seller77", Amount: sdkmath.NewInt(2)}, - {Address: "seller3", Amount: sdkmath.NewInt(1)}, - }, - FeesToPay: nil, - }, - }, - expSettlement: &Settlement{ - Transfers: []*Transfer{ - { - Inputs: []banktypes.Input{{Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 15))}}, - Outputs: []banktypes.Output{{Address: "buyer5511", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 15))}}, - }, - { - Inputs: []banktypes.Input{{Address: "seller3", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 43))}}, - Outputs: []banktypes.Output{ - {Address: "buyer5511", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 5))}, - {Address: "buyer78", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 7))}, - {Address: "buyer9001", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 31))}, - }, - }, - { - Inputs: []banktypes.Input{{Address: "buyer5511", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 30))}}, - Outputs: []banktypes.Output{{Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 30))}}, - }, - { - Inputs: []banktypes.Input{{Address: "buyer78", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 15))}}, - Outputs: []banktypes.Output{ - {Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 12))}, - {Address: "seller3", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 3))}, - }, - }, - { - Inputs: []banktypes.Input{{Address: "buyer9001", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 86))}}, - Outputs: []banktypes.Output{ - {Address: "seller3", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 84))}, - {Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 2))}, - }, - }, - }, - FeeInputs: []banktypes.Input{ - {Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("fig", 11))}, - {Address: "buyer5511", Coins: sdk.NewCoins(sdk.NewInt64Coin("fig", 10))}, - {Address: "buyer78", Coins: sdk.NewCoins(sdk.NewInt64Coin("grape", 4))}, - }, - }, - expErr: "", + name: "assets unfilled less than amount: ask, bid", + receiver: askOF(3, 5, 0), + order: NewOrder(4).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(6), + expErr: "cannot fill ask order 3 having assets left \"5apple\" with \"6apple\" from bid order 4: overfill", + }, + { + name: "assets unfilled less than amount: bid, ask", + receiver: bidOF(5, 5, 0), + order: NewOrder(6).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(6), + expErr: "cannot fill bid order 5 having assets left \"5apple\" with \"6apple\" from ask order 6: overfill", + }, + { + name: "assets unfilled less than amount: bid, bid", + receiver: bidOF(7, 5, 0), + order: NewOrder(8).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(6), + expErr: "cannot fill bid order 7 having assets left \"5apple\" with \"6apple\" from bid order 8: overfill", + }, + { + name: "assets unfilled equals amount: ask, bid", + receiver: askOF(1, 12345, 0), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(12345), + expRes: askOF(1, 0, 12345, dist("buYer", 12345)), + }, + { + name: "assets unfilled equals amount: bid, ask", + receiver: bidOF(1, 12345, 0), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(12345), + expRes: bidOF(1, 0, 12345, dist("seLLer", 12345)), + }, + { + name: "assets unfilled more than amount: ask, bid", + receiver: askOF(1, 12345, 0), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(300), + expRes: askOF(1, 12045, 300, dist("buYer", 300)), + }, + { + name: "assets unfilled more than amount: bid, ask", + receiver: bidOF(1, 12345, 0), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(300), + expRes: bidOF(1, 12045, 300, dist("seLLer", 300)), + }, + { + name: "already has 2 dists: ask, bid", + receiver: askOF(1, 12300, 45, dist("bbbbb", 40), dist("YYYYY", 5)), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(2000), + expRes: askOF(1, 10300, 2045, dist("bbbbb", 40), dist("YYYYY", 5), dist("buYer", 2000)), + }, + { + name: "already has 2 dists: bid, ask", + receiver: bidOF(1, 12300, 45, dist("sssss", 40), dist("LLLLL", 5)), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(2000), + expRes: bidOF(1, 10300, 2045, dist("sssss", 40), dist("LLLLL", 5), dist("seLLer", 2000)), + }, + { + name: "amt more than filled, ask, bid", + receiver: askOF(1, 45, 12300, dist("ssss", 12300)), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(45), + expRes: askOF(1, 0, 12345, dist("ssss", 12300), dist("buYer", 45)), + }, + { + name: "amt more than filled, bid, ask", + receiver: bidOF(1, 45, 12300, dist("ssss", 12300)), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(45), + expRes: bidOF(1, 0, 12345, dist("ssss", 12300), dist("seLLer", 45)), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - settlement := &Settlement{} - if tc.expSettlement == nil { - tc.expSettlement = &Settlement{} + orig := copyOrderFulfillment(tc.receiver) + if tc.expRes == nil { + tc.expRes = copyOrderFulfillment(tc.receiver) } var err error testFunc := func() { - err = buildTransfers(tc.askOFs, tc.bidOFs, settlement) + err = tc.receiver.DistributeAssets(tc.order, tc.amount) } - require.NotPanics(t, testFunc, "buildTransfers") - assertions.AssertErrorValue(t, err, tc.expErr, "buildTransfers error") - if !assert.Equal(t, tc.expSettlement, settlement, "settlement after buildTransfers") { - expTransStrs := make([]string, len(tc.expSettlement.Transfers)) - for i, t := range tc.expSettlement.Transfers { - expTransStrs[i] = fmt.Sprintf("[%d]%s", i, transferString(t)) - } - expTrans := strings.Join(expTransStrs, "\n") - actTransStrs := make([]string, len(tc.expSettlement.Transfers)) - for i, t := range settlement.Transfers { - actTransStrs[i] = fmt.Sprintf("[%d]%s", i, transferString(t)) - } - actTrans := strings.Join(actTransStrs, "\n") - assert.Equal(t, expTrans, actTrans, "transfers (as strings)") + require.NotPanics(t, testFunc, "distributeAssets") + assertions.AssertErrorValue(t, err, tc.expErr, "distributeAssets error") + if !assertEqualOrderFulfillments(t, tc.expRes, tc.receiver, "orderFulfillment after distributeAssets") { + t.Logf("Original: %s", orderFulfillmentString(orig)) + t.Logf(" Amount: %s", tc.amount) } }) } } -func TestPopulateFilled(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} - } - askOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { - return NewOrder(orderID).WithAsk(&AskOrder{ - Assets: coin(assetsAmt, "acorn"), - Price: coin(priceAmt, "prune"), - }) - } - bidOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - Assets: coin(assetsAmt, "acorn"), - Price: coin(priceAmt, "prune"), - }) - } - newOF := func(order *Order, priceAppliedAmt int64, fees ...sdk.Coin) *OrderFulfillment { - rv := &OrderFulfillment{ +func TestDistributeAssets(t *testing.T) { + seller, buyer := "SelleR", "BuyeR" + newOF := func(order *Order, assetsUnfilled, assetsFilled int64, dists ...*distribution) *orderFulfillment { + rv := &orderFulfillment{ Order: order, - AssetsFilledAmt: order.GetAssets().Amount, - AssetsUnfilledAmt: sdkmath.ZeroInt(), - PriceAppliedAmt: sdkmath.NewInt(priceAppliedAmt), - PriceLeftAmt: order.GetPrice().Amount.SubRaw(priceAppliedAmt), + AssetsUnfilledAmt: sdkmath.NewInt(assetsUnfilled), + AssetsFilledAmt: sdkmath.NewInt(assetsFilled), } - if len(fees) > 0 { - rv.FeesToPay = fees + if assetsUnfilled == 0 { + rv.AssetsUnfilledAmt = ZeroAmtAfterSub + } + if len(dists) > 0 { + rv.AssetDists = dists } return rv + } - filledOrder := func(order *Order, actualPrice int64, actualFees ...sdk.Coin) *FilledOrder { - rv := &FilledOrder{ - order: order, - actualPrice: coin(actualPrice, order.GetPrice().Denom), - } - if len(actualFees) > 0 { - rv.actualFees = actualFees + askOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*distribution) *orderFulfillment { + order := NewOrder(orderID).WithAsk(&AskOrder{ + Seller: seller, + Assets: sdk.NewInt64Coin("apple", 999), + }) + return newOF(order, assetsUnfilled, assetsFilled, dists...) + } + bidOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*distribution) *orderFulfillment { + order := NewOrder(orderID).WithBid(&BidOrder{ + Buyer: buyer, + Assets: sdk.NewInt64Coin("apple", 999), + }) + return newOF(order, assetsUnfilled, assetsFilled, dists...) + } + dist := func(addr string, amt int64) *distribution { + return &distribution{ + Address: addr, + Amount: sdkmath.NewInt(amt), } - return rv } tests := []struct { - name string - askOFs []*OrderFulfillment - bidOFs []*OrderFulfillment - settlement *Settlement - expSettlement *Settlement + name string + of1 *orderFulfillment + of2 *orderFulfillment + amount sdkmath.Int + expOF1 *orderFulfillment + expOF2 *orderFulfillment + expErr string }{ { - name: "no partial", - askOFs: []*OrderFulfillment{ - newOF(askOrder(2001, 53, 87), 92, coin(12, "fig")), - newOF(askOrder(2002, 17, 33), 37), - newOF(askOrder(2003, 22, 56), 60), - }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(3001, 30, 40), 40), - newOF(bidOrder(3002, 27, 49), 49, coin(39, "fig")), - newOF(bidOrder(3003, 35, 100), 100), - }, - settlement: &Settlement{}, - expSettlement: &Settlement{ - FullyFilledOrders: []*FilledOrder{ - filledOrder(askOrder(2001, 53, 87), 92, coin(12, "fig")), - filledOrder(askOrder(2002, 17, 33), 37), - filledOrder(askOrder(2003, 22, 56), 60), - filledOrder(bidOrder(3001, 30, 40), 40), - filledOrder(bidOrder(3002, 27, 49), 49, coin(39, "fig")), - filledOrder(bidOrder(3003, 35, 100), 100), - }, - }, + name: "amount more than of1 unfilled: ask bid", + of1: askOF(1, 5, 0), + of2: bidOF(2, 6, 0), + amount: sdkmath.NewInt(6), + expOF2: bidOF(2, 0, 6, dist(seller, 6)), + expErr: "cannot fill ask order 1 having assets left \"5apple\" with \"6apple\" from bid order 2: overfill", }, { - name: "partial ask", - askOFs: []*OrderFulfillment{ - newOF(askOrder(2001, 53, 87), 92, coin(12, "fig")), - newOF(askOrder(2002, 17, 33), 37), - newOF(askOrder(2003, 22, 56), 60), - }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(3001, 30, 40), 40), - newOF(bidOrder(3002, 27, 49), 49, coin(39, "fig")), - newOF(bidOrder(3003, 35, 100), 100), - }, - settlement: &Settlement{PartialOrderLeft: askOrder(2002, 15, 63)}, - expSettlement: &Settlement{ - FullyFilledOrders: []*FilledOrder{ - filledOrder(askOrder(2001, 53, 87), 92, coin(12, "fig")), - filledOrder(askOrder(2003, 22, 56), 60), - filledOrder(bidOrder(3001, 30, 40), 40), - filledOrder(bidOrder(3002, 27, 49), 49, coin(39, "fig")), - filledOrder(bidOrder(3003, 35, 100), 100), - }, - PartialOrderFilled: filledOrder(askOrder(2002, 17, 33), 37), - PartialOrderLeft: askOrder(2002, 15, 63), - }, + name: "amount more than of1 unfilled: bid ask", + of1: bidOF(1, 5, 0), + of2: askOF(2, 6, 0), + amount: sdkmath.NewInt(6), + expOF2: askOF(2, 0, 6, dist(buyer, 6)), + expErr: "cannot fill bid order 1 having assets left \"5apple\" with \"6apple\" from ask order 2: overfill", }, { - name: "partial bid", - askOFs: []*OrderFulfillment{ - newOF(askOrder(2001, 53, 87), 92, coin(12, "fig")), - newOF(askOrder(2002, 17, 33), 37), - newOF(askOrder(2003, 22, 56), 60), - }, - bidOFs: []*OrderFulfillment{ - newOF(bidOrder(3001, 30, 40), 40), - newOF(bidOrder(3002, 27, 49), 49, coin(39, "fig")), - newOF(bidOrder(3003, 35, 100), 100), - }, - settlement: &Settlement{PartialOrderLeft: bidOrder(3003, 15, 63)}, - expSettlement: &Settlement{ - FullyFilledOrders: []*FilledOrder{ - filledOrder(askOrder(2001, 53, 87), 92, coin(12, "fig")), - filledOrder(askOrder(2002, 17, 33), 37), - filledOrder(askOrder(2003, 22, 56), 60), - filledOrder(bidOrder(3001, 30, 40), 40), - filledOrder(bidOrder(3002, 27, 49), 49, coin(39, "fig")), - }, - PartialOrderFilled: filledOrder(bidOrder(3003, 35, 100), 100), - PartialOrderLeft: bidOrder(3003, 15, 63), - }, + name: "amount more than of2 unfilled: ask, bid", + of1: askOF(1, 6, 0), + of2: bidOF(2, 5, 0), + amount: sdkmath.NewInt(6), + expOF1: askOF(1, 0, 6, dist(buyer, 6)), + expErr: "cannot fill bid order 2 having assets left \"5apple\" with \"6apple\" from ask order 1: overfill", + }, + { + name: "amount more than of2 unfilled: bid, ask", + of1: bidOF(1, 6, 0), + of2: askOF(2, 5, 0), + amount: sdkmath.NewInt(6), + expOF1: bidOF(1, 0, 6, dist(seller, 6)), + expErr: "cannot fill ask order 2 having assets left \"5apple\" with \"6apple\" from bid order 1: overfill", + }, + { + name: "amount more than both unfilled: ask, bid", + of1: askOF(1, 5, 0), + of2: bidOF(2, 4, 0), + amount: sdkmath.NewInt(6), + expErr: "cannot fill ask order 1 having assets left \"5apple\" with \"6apple\" from bid order 2: overfill" + "\n" + + "cannot fill bid order 2 having assets left \"4apple\" with \"6apple\" from ask order 1: overfill", + }, + { + name: "amount more than both unfilled: ask, bid", + of1: bidOF(1, 5, 0), + of2: askOF(2, 4, 0), + amount: sdkmath.NewInt(6), + expErr: "cannot fill bid order 1 having assets left \"5apple\" with \"6apple\" from ask order 2: overfill" + "\n" + + "cannot fill ask order 2 having assets left \"4apple\" with \"6apple\" from bid order 1: overfill", + }, + { + name: "ask bid", + of1: askOF(1, 10, 55, dist("bbb", 55)), + of2: bidOF(2, 10, 0), + amount: sdkmath.NewInt(9), + expOF1: askOF(1, 1, 64, dist("bbb", 55), dist(buyer, 9)), + expOF2: bidOF(2, 1, 9, dist(seller, 9)), + }, + { + name: "bid ask", + of1: bidOF(1, 10, 55, dist("sss", 55)), + of2: askOF(2, 10, 3, dist("bbb", 3)), + amount: sdkmath.NewInt(10), + expOF1: bidOF(1, 0, 65, dist("sss", 55), dist(seller, 10)), + expOF2: askOF(2, 0, 13, dist("bbb", 3), dist(buyer, 10)), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { + origOF1 := copyOrderFulfillment(tc.of1) + origOF2 := copyOrderFulfillment(tc.of2) + if tc.expOF1 == nil { + tc.expOF1 = copyOrderFulfillment(tc.of1) + } + if tc.expOF2 == nil { + tc.expOF2 = copyOrderFulfillment(tc.of2) + } + + var err error testFunc := func() { - populateFilled(tc.askOFs, tc.bidOFs, tc.settlement) + err = distributeAssets(tc.of1, tc.of2, tc.amount) + } + require.NotPanics(t, testFunc, "distributeAssets") + assertions.AssertErrorValue(t, err, tc.expErr, "distributeAssets error") + if !assertEqualOrderFulfillments(t, tc.expOF1, tc.of1, "of1 after distributeAssets") { + t.Logf("Original: %s", orderFulfillmentString(origOF1)) + t.Logf(" Amount: %s", tc.amount) + } + if !assertEqualOrderFulfillments(t, tc.expOF2, tc.of2, "of2 after distributeAssets") { + t.Logf("Original: %s", orderFulfillmentString(origOF2)) + t.Logf(" Amount: %s", tc.amount) } - require.NotPanics(t, testFunc, "populateFilled") - assert.Equal(t, tc.expSettlement, tc.settlement, "settlement after populateFilled") }) } } -func TestGetAssetTransfer(t *testing.T) { - seller, buyer := "sally", "brandon" - assetDenom, priceDenom := "apple", "peach" - newOF := func(order *Order, assetsFilledAmt int64, assetDists ...*Distribution) *OrderFulfillment { - rv := &OrderFulfillment{ +func TestOrderFulfillment_DistributePrice(t *testing.T) { + newOF := func(order *Order, priceLeft, priceApplied int64, dists ...*distribution) *orderFulfillment { + rv := &orderFulfillment{ Order: order, - AssetsFilledAmt: sdkmath.NewInt(assetsFilledAmt), + PriceLeftAmt: sdkmath.NewInt(priceLeft), + PriceAppliedAmt: sdkmath.NewInt(priceApplied), } - if len(assetDists) > 0 { - rv.AssetDists = assetDists + if priceLeft == 0 { + rv.PriceLeftAmt = ZeroAmtAfterSub + } + if len(dists) > 0 { + rv.PriceDists = dists } return rv + } - askOrder := func(orderID uint64) *Order { - return NewOrder(orderID).WithAsk(&AskOrder{ - MarketId: 5555, - Seller: seller, - Assets: sdk.Coin{Denom: assetDenom, Amount: sdkmath.NewInt(111)}, - Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(999)}, - }) - } - bidOrder := func(orderID uint64) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - MarketId: 5555, - Buyer: buyer, - Assets: sdk.Coin{Denom: assetDenom, Amount: sdkmath.NewInt(111)}, - Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(999)}, - }) - } - dist := func(addr string, amount int64) *Distribution { - return &Distribution{Address: addr, Amount: sdkmath.NewInt(amount)} + askOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*distribution) *orderFulfillment { + order := NewOrder(orderID).WithAsk(&AskOrder{Price: sdk.NewInt64Coin("peach", 999)}) + return newOF(order, priceLeft, priceApplied, dists...) } - input := func(addr string, amount int64) banktypes.Input { - return banktypes.Input{ - Address: addr, - Coins: sdk.Coins{{Denom: assetDenom, Amount: sdkmath.NewInt(amount)}}, - } + bidOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*distribution) *orderFulfillment { + order := NewOrder(orderID).WithBid(&BidOrder{Price: sdk.NewInt64Coin("peach", 999)}) + return newOF(order, priceLeft, priceApplied, dists...) } - output := func(addr string, amount int64) banktypes.Output { - return banktypes.Output{ + dist := func(addr string, amt int64) *distribution { + return &distribution{ Address: addr, - Coins: sdk.Coins{{Denom: assetDenom, Amount: sdkmath.NewInt(amount)}}, + Amount: sdkmath.NewInt(amt), } } tests := []struct { - name string - f *OrderFulfillment - expTransfer *Transfer - expErr string - expPanic string + name string + receiver *orderFulfillment + order OrderI + amount sdkmath.Int + expRes *orderFulfillment + expErr string }{ { - name: "nil inside order", - f: newOF(NewOrder(975), 5, dist("five", 5)), - expPanic: nilSubTypeErr(975), - }, - { - name: "unknown inside order", - f: newOF(newUnknownOrder(974), 5, dist("five", 5)), - expPanic: unknownSubTypeErr(974), - }, - { - name: "assets filled negative: ask", - f: newOF(askOrder(159), -5), - expErr: "ask order 159 cannot be filled with \"-5apple\" assets: amount not positive", - }, - { - name: "assets filled negative: bid", - f: newOF(bidOrder(953), -5), - expErr: "bid order 953 cannot be filled with \"-5apple\" assets: amount not positive", - }, - { - name: "assets filled zero: ask", - f: newOF(askOrder(991), 0), - expErr: "ask order 991 cannot be filled with \"0apple\" assets: amount not positive", - }, - { - name: "assets filled zero: bid", - f: newOF(bidOrder(992), 0), - expErr: "bid order 992 cannot be filled with \"0apple\" assets: amount not positive", - }, - { - name: "asset dists has negative amount: ask", - f: newOF(askOrder(549), 10, dist("one", 1), dist("two", 2), dist("neg", -2), dist("nine", 9)), - expErr: "ask order 549 cannot have \"-2apple\" assets in a transfer: amount not positive", - }, - { - name: "asset dists has negative amount: bid", - f: newOF(bidOrder(545), 10, dist("one", 1), dist("two", 2), dist("neg", -2), dist("nine", 9)), - expErr: "bid order 545 cannot have \"-2apple\" assets in a transfer: amount not positive", - }, - { - name: "asset dists has zero: ask", - f: newOF(askOrder(683), 10, dist("one", 1), dist("two", 2), dist("zero", 0), dist("seven", 7)), - expErr: "ask order 683 cannot have \"0apple\" assets in a transfer: amount not positive", - }, - { - name: "asset dists has zero: bid", - f: newOF(bidOrder(777), 10, dist("one", 1), dist("two", 2), dist("zero", 0), dist("seven", 7)), - expErr: "bid order 777 cannot have \"0apple\" assets in a transfer: amount not positive", - }, - { - name: "asset dists sum less than assets filled: ask", - f: newOF(askOrder(8), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("three2", 3)), - expErr: "ask order 8 assets filled \"10apple\" does not equal assets distributed \"9apple\"", - }, - { - name: "asset dists sum less than assets filled: bid", - f: newOF(bidOrder(3), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("three2", 3)), - expErr: "bid order 3 assets filled \"10apple\" does not equal assets distributed \"9apple\"", + name: "assets unfilled less than amount: ask, ask", + receiver: askOF(1, 5, 0), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(6), + expRes: askOF(1, -1, 6, dist("seLLer", 6)), }, { - name: "asset dists sum more than assets filled: ask", - f: newOF(askOrder(8), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("five", 5)), - expErr: "ask order 8 assets filled \"10apple\" does not equal assets distributed \"11apple\"", + name: "assets unfilled less than amount: ask, bid", + receiver: askOF(3, 5, 0), + order: NewOrder(4).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(6), + expRes: askOF(3, -1, 6, dist("buYer", 6)), }, { - name: "asset dists sum more than assets filled: bid", - f: newOF(bidOrder(3), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("five", 5)), - expErr: "bid order 3 assets filled \"10apple\" does not equal assets distributed \"11apple\"", + name: "assets unfilled less than amount: bid, ask", + receiver: bidOF(5, 5, 0), + order: NewOrder(6).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(6), + expErr: "cannot fill bid order 5 having price left \"5peach\" to ask order 6 at a price of \"6peach\": overfill", }, { - name: "one dist: ask", - f: newOF(askOrder(12), 10, dist("ten", 10)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input(seller, 10)}, - Outputs: []banktypes.Output{output("ten", 10)}, - }, + name: "assets unfilled less than amount: bid, bid", + receiver: bidOF(7, 5, 0), + order: NewOrder(8).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(6), + expErr: "cannot fill bid order 7 having price left \"5peach\" to bid order 8 at a price of \"6peach\": overfill", }, { - name: "one dist: bid", - f: newOF(bidOrder(13), 10, dist("ten", 10)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input("ten", 10)}, - Outputs: []banktypes.Output{output(buyer, 10)}, - }, + name: "assets unfilled equals amount: ask, bid", + receiver: askOF(1, 12345, 0), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(12345), + expRes: askOF(1, 0, 12345, dist("buYer", 12345)), }, { - name: "two dists, different addresses: ask", - f: newOF(askOrder(2111), 20, dist("eleven", 11), dist("nine", 9)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input(seller, 20)}, - Outputs: []banktypes.Output{output("eleven", 11), output("nine", 9)}, - }, + name: "assets unfilled equals amount: bid, ask", + receiver: bidOF(1, 12345, 0), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(12345), + expRes: bidOF(1, 0, 12345, dist("seLLer", 12345)), }, { - name: "two dists, different addresses: bid", - f: newOF(bidOrder(1222), 20, dist("eleven", 11), dist("nine", 9)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input("eleven", 11), input("nine", 9)}, - Outputs: []banktypes.Output{output(buyer, 20)}, - }, + name: "assets unfilled more than amount: ask, bid", + receiver: askOF(1, 12345, 0), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(300), + expRes: askOF(1, 12045, 300, dist("buYer", 300)), }, { - name: "two dists, same addresses: ask", - f: newOF(askOrder(5353), 52, dist("billy", 48), dist("billy", 4)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input(seller, 52)}, - Outputs: []banktypes.Output{output("billy", 52)}, - }, + name: "assets unfilled more than amount: bid, ask", + receiver: bidOF(1, 12345, 0), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(300), + expRes: bidOF(1, 12045, 300, dist("seLLer", 300)), }, { - name: "two dists, same addresses: bid", - f: newOF(bidOrder(3535), 52, dist("sol", 48), dist("sol", 4)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input("sol", 52)}, - Outputs: []banktypes.Output{output(buyer, 52)}, - }, + name: "already has 2 dists: ask, bid", + receiver: askOF(1, 12300, 45, dist("bbbbb", 40), dist("YYYYY", 5)), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(2000), + expRes: askOF(1, 10300, 2045, dist("bbbbb", 40), dist("YYYYY", 5), dist("buYer", 2000)), }, { - name: "four dists: ask", - f: newOF(askOrder(99221), 33, - dist("buddy", 10), dist("brian", 13), dist("buddy", 8), dist("bella", 2)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input(seller, 33)}, - Outputs: []banktypes.Output{output("buddy", 18), output("brian", 13), output("bella", 2)}, - }, + name: "already has 2 dists: bid, ask", + receiver: bidOF(1, 12300, 45, dist("sssss", 40), dist("LLLLL", 5)), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(2000), + expRes: bidOF(1, 10300, 2045, dist("sssss", 40), dist("LLLLL", 5), dist("seLLer", 2000)), }, { - name: "four dists: bid", - f: newOF(bidOrder(99221), 33, - dist("sydney", 10), dist("sarah", 2), dist("sydney", 8), dist("spencer", 13)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input("sydney", 18), input("sarah", 2), input("spencer", 13)}, - Outputs: []banktypes.Output{output(buyer, 33)}, - }, + name: "amt more than filled, ask, bid", + receiver: askOF(1, 45, 12300, dist("ssss", 12300)), + order: NewOrder(2).WithBid(&BidOrder{Buyer: "buYer"}), + amount: sdkmath.NewInt(45), + expRes: askOF(1, 0, 12345, dist("ssss", 12300), dist("buYer", 45)), + }, + { + name: "amt more than filled, bid, ask", + receiver: bidOF(1, 45, 12300, dist("ssss", 12300)), + order: NewOrder(2).WithAsk(&AskOrder{Seller: "seLLer"}), + amount: sdkmath.NewInt(45), + expRes: bidOF(1, 0, 12345, dist("ssss", 12300), dist("seLLer", 45)), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - orig := copyOrderFulfillment(tc.f) - var transfer *Transfer + orig := copyOrderFulfillment(tc.receiver) + if tc.expRes == nil { + tc.expRes = copyOrderFulfillment(tc.receiver) + } var err error testFunc := func() { - transfer, err = getAssetTransfer(tc.f) + err = tc.receiver.DistributePrice(tc.order, tc.amount) } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAssetTransfer") - assertions.AssertErrorValue(t, err, tc.expErr, "getAssetTransfer error") - if !assert.Equal(t, tc.expTransfer, transfer, "getAssetTransfer transfers") { - t.Logf(" Actual: %s", transferString(transfer)) - t.Logf("Expected: %s", transferString(tc.expTransfer)) + require.NotPanics(t, testFunc, "distributePrice") + assertions.AssertErrorValue(t, err, tc.expErr, "distributePrice error") + if !assertEqualOrderFulfillments(t, tc.expRes, tc.receiver, "orderFulfillment after distributePrice") { + t.Logf("Original: %s", orderFulfillmentString(orig)) + t.Logf(" Amount: %s", tc.amount) } - assertEqualOrderFulfillments(t, orig, tc.f, "OrderFulfillment before and after getAssetTransfer") }) } } -func TestGetPriceTransfer(t *testing.T) { - seller, buyer := "sally", "brandon" - assetDenom, priceDenom := "apple", "peach" - newOF := func(order *Order, priceAppliedAmt int64, priceDists ...*Distribution) *OrderFulfillment { - rv := &OrderFulfillment{ +func TestDistributePrice(t *testing.T) { + seller, buyer := "SelleR", "BuyeR" + newOF := func(order *Order, priceLeft, priceApplied int64, dists ...*distribution) *orderFulfillment { + rv := &orderFulfillment{ Order: order, - PriceAppliedAmt: sdkmath.NewInt(priceAppliedAmt), + PriceLeftAmt: sdkmath.NewInt(priceLeft), + PriceAppliedAmt: sdkmath.NewInt(priceApplied), } - if len(priceDists) > 0 { - rv.PriceDists = priceDists + if priceLeft == 0 { + rv.PriceLeftAmt = ZeroAmtAfterSub + } + if len(dists) > 0 { + rv.PriceDists = dists } return rv + } - askOrder := func(orderID uint64) *Order { - return NewOrder(orderID).WithAsk(&AskOrder{ - MarketId: 5555, - Seller: seller, - Assets: sdk.Coin{Denom: assetDenom, Amount: sdkmath.NewInt(111)}, - Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(999)}, + askOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*distribution) *orderFulfillment { + order := NewOrder(orderID).WithAsk(&AskOrder{ + Seller: seller, + Price: sdk.NewInt64Coin("peach", 999), }) + return newOF(order, priceLeft, priceApplied, dists...) } - bidOrder := func(orderID uint64) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - MarketId: 5555, - Buyer: buyer, - Assets: sdk.Coin{Denom: assetDenom, Amount: sdkmath.NewInt(111)}, - Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(999)}, + bidOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*distribution) *orderFulfillment { + order := NewOrder(orderID).WithBid(&BidOrder{ + Buyer: buyer, + Price: sdk.NewInt64Coin("peach", 999), }) + return newOF(order, priceLeft, priceApplied, dists...) } - dist := func(addr string, amount int64) *Distribution { - return &Distribution{Address: addr, Amount: sdkmath.NewInt(amount)} - } - input := func(addr string, amount int64) banktypes.Input { - return banktypes.Input{ - Address: addr, - Coins: sdk.Coins{{Denom: priceDenom, Amount: sdkmath.NewInt(amount)}}, - } - } - output := func(addr string, amount int64) banktypes.Output { - return banktypes.Output{ + dist := func(addr string, amt int64) *distribution { + return &distribution{ Address: addr, - Coins: sdk.Coins{{Denom: priceDenom, Amount: sdkmath.NewInt(amount)}}, + Amount: sdkmath.NewInt(amt), } } tests := []struct { - name string - f *OrderFulfillment - expTransfer *Transfer - expErr string - expPanic string + name string + of1 *orderFulfillment + of2 *orderFulfillment + amount sdkmath.Int + expOF1 *orderFulfillment + expOF2 *orderFulfillment + expErr string }{ { - name: "nil inside order", - f: newOF(NewOrder(975), 5, dist("five", 5)), - expPanic: nilSubTypeErr(975), - }, - { - name: "unknown inside order", - f: newOF(newUnknownOrder(974), 5, dist("five", 5)), - expPanic: unknownSubTypeErr(974), - }, - { - name: "price applied negative: ask", - f: newOF(askOrder(159), -5), - expErr: "ask order 159 cannot be filled at price \"-5peach\": amount not positive", - }, - { - name: "price applied negative: bid", - f: newOF(bidOrder(953), -5), - expErr: "bid order 953 cannot be filled at price \"-5peach\": amount not positive", - }, - { - name: "price applied zero: ask", - f: newOF(askOrder(991), 0), - expErr: "ask order 991 cannot be filled at price \"0peach\": amount not positive", - }, - { - name: "price applied zero: bid", - f: newOF(bidOrder(992), 0), - expErr: "bid order 992 cannot be filled at price \"0peach\": amount not positive", - }, - { - name: "price dists has negative amount: ask", - f: newOF(askOrder(549), 10, dist("one", 1), dist("two", 2), dist("neg", -2), dist("nine", 9)), - expErr: "ask order 549 cannot have price \"-2peach\" in a transfer: amount not positive", - }, - { - name: "price dists has negative amount: bid", - f: newOF(bidOrder(545), 10, dist("one", 1), dist("two", 2), dist("neg", -2), dist("nine", 9)), - expErr: "bid order 545 cannot have price \"-2peach\" in a transfer: amount not positive", - }, - { - name: "price dists has zero: ask", - f: newOF(askOrder(683), 10, dist("one", 1), dist("two", 2), dist("zero", 0), dist("seven", 7)), - expErr: "ask order 683 cannot have price \"0peach\" in a transfer: amount not positive", - }, - { - name: "price dists has zero: bid", - f: newOF(bidOrder(777), 10, dist("one", 1), dist("two", 2), dist("zero", 0), dist("seven", 7)), - expErr: "bid order 777 cannot have price \"0peach\" in a transfer: amount not positive", - }, - { - name: "price dists sum less than price applied: ask", - f: newOF(askOrder(8), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("three2", 3)), - expErr: "ask order 8 price filled \"10peach\" does not equal price distributed \"9peach\"", - }, - { - name: "price dists sum less than price applied: bid", - f: newOF(bidOrder(3), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("three2", 3)), - expErr: "bid order 3 price filled \"10peach\" does not equal price distributed \"9peach\"", - }, - { - name: "price dists sum more than price applied: ask", - f: newOF(askOrder(8), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("five", 5)), - expErr: "ask order 8 price filled \"10peach\" does not equal price distributed \"11peach\"", - }, - { - name: "price dists sum more than price applied: bid", - f: newOF(bidOrder(3), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("five", 5)), - expErr: "bid order 3 price filled \"10peach\" does not equal price distributed \"11peach\"", - }, - { - name: "one dist: ask", - f: newOF(askOrder(12), 10, dist("ten", 10)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input("ten", 10)}, - Outputs: []banktypes.Output{output(seller, 10)}, - }, + name: "amount more than of1 unfilled: ask bid", + of1: askOF(1, 5, 0), + of2: bidOF(2, 6, 0), + amount: sdkmath.NewInt(6), + expOF1: askOF(1, -1, 6, dist(buyer, 6)), + expOF2: bidOF(2, 0, 6, dist(seller, 6)), }, { - name: "one dist: bid", - f: newOF(bidOrder(13), 10, dist("ten", 10)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input(buyer, 10)}, - Outputs: []banktypes.Output{output("ten", 10)}, - }, + name: "amount more than of1 unfilled: bid ask", + of1: bidOF(1, 5, 0), + of2: askOF(2, 6, 0), + amount: sdkmath.NewInt(6), + expOF2: askOF(2, 0, 6, dist(buyer, 6)), + expErr: "cannot fill bid order 1 having price left \"5peach\" to ask order 2 at a price of \"6peach\": overfill", }, { - name: "two dists, different addresses: ask", - f: newOF(askOrder(2111), 20, dist("eleven", 11), dist("nine", 9)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input("eleven", 11), input("nine", 9)}, - Outputs: []banktypes.Output{output(seller, 20)}, - }, + name: "amount more than of2 unfilled: ask, bid", + of1: askOF(1, 6, 0), + of2: bidOF(2, 5, 0), + amount: sdkmath.NewInt(6), + expOF1: askOF(1, 0, 6, dist(buyer, 6)), + expErr: "cannot fill bid order 2 having price left \"5peach\" to ask order 1 at a price of \"6peach\": overfill", }, { - name: "two dists, different addresses: bid", - f: newOF(bidOrder(1222), 20, dist("eleven", 11), dist("nine", 9)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input(buyer, 20)}, - Outputs: []banktypes.Output{output("eleven", 11), output("nine", 9)}, - }, + name: "amount more than of2 unfilled: bid, ask", + of1: bidOF(1, 6, 0), + of2: askOF(2, 5, 0), + amount: sdkmath.NewInt(6), + expOF1: bidOF(1, 0, 6, dist(seller, 6)), + expOF2: askOF(2, -1, 6, dist(buyer, 6)), }, { - name: "two dists, same addresses: ask", - f: newOF(askOrder(5353), 52, dist("billy", 48), dist("billy", 4)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input("billy", 52)}, - Outputs: []banktypes.Output{output(seller, 52)}, - }, + name: "amount more than both unfilled: ask, bid", + of1: askOF(1, 5, 0), + of2: bidOF(2, 4, 0), + amount: sdkmath.NewInt(6), + expOF1: askOF(1, -1, 6, dist(buyer, 6)), + expErr: "cannot fill bid order 2 having price left \"4peach\" to ask order 1 at a price of \"6peach\": overfill", }, { - name: "two dists, same addresses: bid", - f: newOF(bidOrder(3535), 52, dist("sol", 48), dist("sol", 4)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input(buyer, 52)}, - Outputs: []banktypes.Output{output("sol", 52)}, - }, + name: "amount more than both unfilled: ask, bid", + of1: bidOF(1, 5, 0), + of2: askOF(2, 4, 0), + amount: sdkmath.NewInt(6), + expOF2: askOF(2, -2, 6, dist(buyer, 6)), + expErr: "cannot fill bid order 1 having price left \"5peach\" to ask order 2 at a price of \"6peach\": overfill", }, { - name: "four dists: ask", - f: newOF(askOrder(99221), 33, - dist("buddy", 10), dist("brian", 13), dist("buddy", 8), dist("bella", 2)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input("buddy", 18), input("brian", 13), input("bella", 2)}, - Outputs: []banktypes.Output{output(seller, 33)}, - }, + name: "ask bid", + of1: askOF(1, 10, 55, dist("bbb", 55)), + of2: bidOF(2, 10, 0), + amount: sdkmath.NewInt(9), + expOF1: askOF(1, 1, 64, dist("bbb", 55), dist(buyer, 9)), + expOF2: bidOF(2, 1, 9, dist(seller, 9)), }, { - name: "four dists: bid", - f: newOF(bidOrder(99221), 33, - dist("sydney", 10), dist("sarah", 2), dist("sydney", 8), dist("spencer", 13)), - expTransfer: &Transfer{ - Inputs: []banktypes.Input{input(buyer, 33)}, - Outputs: []banktypes.Output{output("sydney", 18), output("sarah", 2), output("spencer", 13)}, - }, + name: "bid ask", + of1: bidOF(1, 10, 55, dist("sss", 55)), + of2: askOF(2, 10, 3, dist("bbb", 3)), + amount: sdkmath.NewInt(10), + expOF1: bidOF(1, 0, 65, dist("sss", 55), dist(seller, 10)), + expOF2: askOF(2, 0, 13, dist("bbb", 3), dist(buyer, 10)), }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - orig := copyOrderFulfillment(tc.f) - var transfer *Transfer + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + origOF1 := copyOrderFulfillment(tc.of1) + origOF2 := copyOrderFulfillment(tc.of2) + if tc.expOF1 == nil { + tc.expOF1 = copyOrderFulfillment(tc.of1) + } + if tc.expOF2 == nil { + tc.expOF2 = copyOrderFulfillment(tc.of2) + } + var err error testFunc := func() { - transfer, err = getPriceTransfer(tc.f) + err = distributePrice(tc.of1, tc.of2, tc.amount) } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getPriceTransfer") - assertions.AssertErrorValue(t, err, tc.expErr, "getPriceTransfer error") - if !assert.Equal(t, tc.expTransfer, transfer, "getPriceTransfer transfers") { - t.Logf(" Actual: %s", transferString(transfer)) - t.Logf("Expected: %s", transferString(tc.expTransfer)) + require.NotPanics(t, testFunc, "distributePrice") + assertions.AssertErrorValue(t, err, tc.expErr, "distributePrice error") + if !assertEqualOrderFulfillments(t, tc.expOF1, tc.of1, "of1 after distributePrice") { + t.Logf("Original: %s", orderFulfillmentString(origOF1)) + t.Logf(" Amount: %s", tc.amount) + } + if !assertEqualOrderFulfillments(t, tc.expOF2, tc.of2, "of2 after distributePrice") { + t.Logf("Original: %s", orderFulfillmentString(origOF2)) + t.Logf(" Amount: %s", tc.amount) } - assertEqualOrderFulfillments(t, orig, tc.f, "OrderFulfillment before and after getPriceTransfer") }) } } -func TestOrderFulfillment_Apply(t *testing.T) { - assetCoin := func(amt int64) sdk.Coin { - return sdk.Coin{Denom: "acoin", Amount: sdkmath.NewInt(amt)} - } - priceCoin := func(amt int64) sdk.Coin { - return sdk.Coin{Denom: "pcoin", Amount: sdkmath.NewInt(amt)} +func TestOrderFulfillment_SplitOrder(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } - askOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { - return NewOrder(orderID).WithAsk(&AskOrder{ - MarketId: 86420, - Seller: "seller", - Assets: assetCoin(assetsAmt), - Price: priceCoin(priceAmt), - }) + askOrder := func(orderID uint64, assetAmt, priceAmt int64, fees ...sdk.Coin) *Order { + askOrder := &AskOrder{ + MarketId: 55, + Seller: "samuel", + Assets: coin(assetAmt, "apple"), + Price: coin(priceAmt, "peach"), + AllowPartial: true, + } + if len(fees) > 1 { + t.Fatalf("a max of 1 fee can be provided to askOrder, actual: %s", sdk.Coins(fees)) + } + if len(fees) > 0 { + askOrder.SellerSettlementFlatFee = &fees[0] + } + return NewOrder(orderID).WithAsk(askOrder) } - bidOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - MarketId: 86420, - Buyer: "buyer", - Assets: assetCoin(assetsAmt), - Price: priceCoin(priceAmt), - }) + bidOrder := func(orderID uint64, assetAmt, priceAmt int64, fees ...sdk.Coin) *Order { + bidOrder := &BidOrder{ + MarketId: 55, + Buyer: "brian", + Assets: coin(assetAmt, "apple"), + Price: coin(priceAmt, "peach"), + AllowPartial: true, + } + if len(fees) > 0 { + bidOrder.BuyerSettlementFees = fees + } + return NewOrder(orderID).WithBid(bidOrder) } tests := []struct { - name string - receiver *OrderFulfillment - order *OrderFulfillment - assetsAmt sdkmath.Int - priceAmt sdkmath.Int - expErr string - expResult *OrderFulfillment + name string + receiver *orderFulfillment + expUnfilled *Order + expReceiver *orderFulfillment + expErr string }{ { - name: "fills order in full", - receiver: &OrderFulfillment{ - Order: askOrder(1, 20, 55), - Splits: nil, - AssetsFilledAmt: sdkmath.ZeroInt(), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.ZeroInt(), - PriceLeftAmt: sdkmath.NewInt(55), - }, - order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, - assetsAmt: sdkmath.NewInt(20), - priceAmt: sdkmath.NewInt(55), - expResult: &OrderFulfillment{ - Order: askOrder(1, 20, 55), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, - Assets: assetCoin(20), - Price: priceCoin(55), - }, - }, - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(55), - PriceLeftAmt: ZeroAmtAfterSub, + name: "order split error: ask", + receiver: &orderFulfillment{ + Order: askOrder(8, 10, 100), + AssetsFilledAmt: sdkmath.NewInt(-1), }, + expErr: "cannot split ask order 8 having asset \"10apple\" at \"-1apple\": amount filled not positive", }, { - name: "partially fills order", - receiver: &OrderFulfillment{ - Order: askOrder(1, 20, 55), - Splits: nil, - AssetsFilledAmt: sdkmath.ZeroInt(), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.ZeroInt(), - PriceLeftAmt: sdkmath.NewInt(55), - }, - order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, - assetsAmt: sdkmath.NewInt(11), - priceAmt: sdkmath.NewInt(22), - expResult: &OrderFulfillment{ - Order: askOrder(1, 20, 55), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, - Assets: assetCoin(11), - Price: priceCoin(22), - }, - }, - AssetsFilledAmt: sdkmath.NewInt(11), - AssetsUnfilledAmt: sdkmath.NewInt(9), - PriceAppliedAmt: sdkmath.NewInt(22), - PriceLeftAmt: sdkmath.NewInt(33), + name: "order split error: bid", + receiver: &orderFulfillment{ + Order: bidOrder(9, 10, 100), + AssetsFilledAmt: sdkmath.NewInt(-1), }, + expErr: "cannot split bid order 9 having asset \"10apple\" at \"-1apple\": amount filled not positive", }, { - name: "already partially filled, fills rest", - receiver: &OrderFulfillment{ - Order: askOrder(1, 20, 55), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: bidOrder(3, 60, 220)}, - Assets: assetCoin(9), - Price: priceCoin(33), - }, - }, + name: "okay: ask", + receiver: &orderFulfillment{ + Order: askOrder(17, 10, 100, coin(20, "fig")), AssetsFilledAmt: sdkmath.NewInt(9), - AssetsUnfilledAmt: sdkmath.NewInt(11), - PriceAppliedAmt: sdkmath.NewInt(33), - PriceLeftAmt: sdkmath.NewInt(22), - }, - order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, - assetsAmt: sdkmath.NewInt(11), - priceAmt: sdkmath.NewInt(22), - expResult: &OrderFulfillment{ - Order: askOrder(1, 20, 55), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: bidOrder(3, 60, 220)}, - Assets: assetCoin(9), - Price: priceCoin(33), - }, - { - Order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, - Assets: assetCoin(11), - Price: priceCoin(22), - }, - }, - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(55), - PriceLeftAmt: ZeroAmtAfterSub, - }, - }, - { - name: "ask assets overfill", - receiver: &OrderFulfillment{ - Order: askOrder(1, 20, 55), - Splits: nil, - AssetsFilledAmt: sdkmath.ZeroInt(), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.ZeroInt(), - PriceLeftAmt: sdkmath.NewInt(55), + AssetsUnfilledAmt: sdkmath.NewInt(1), + PriceAppliedAmt: sdkmath.NewInt(300), + PriceLeftAmt: sdkmath.NewInt(-200), }, - order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, - assetsAmt: sdkmath.NewInt(21), - priceAmt: sdkmath.NewInt(55), - expErr: "cannot fill ask order 1 having assets left \"20acoin\" with \"21acoin\" from bid order 2: overfill", - }, - { - name: "bid assets overfill", - receiver: &OrderFulfillment{ - Order: bidOrder(1, 20, 55), - Splits: nil, - AssetsFilledAmt: sdkmath.ZeroInt(), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.ZeroInt(), - PriceLeftAmt: sdkmath.NewInt(55), + expUnfilled: askOrder(17, 1, 10, coin(2, "fig")), + expReceiver: &orderFulfillment{ + Order: askOrder(17, 9, 90, coin(18, "fig")), + AssetsFilledAmt: sdkmath.NewInt(9), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(300), + PriceLeftAmt: sdkmath.NewInt(-210), }, - order: &OrderFulfillment{Order: askOrder(2, 40, 110)}, - assetsAmt: sdkmath.NewInt(21), - priceAmt: sdkmath.NewInt(55), - expErr: "cannot fill bid order 1 having assets left \"20acoin\" with \"21acoin\" from ask order 2: overfill", }, { - name: "ask price overfill", - receiver: &OrderFulfillment{ - Order: askOrder(1, 20, 55), - Splits: nil, - AssetsFilledAmt: sdkmath.ZeroInt(), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.ZeroInt(), - PriceLeftAmt: sdkmath.NewInt(55), - }, - order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, - assetsAmt: sdkmath.NewInt(20), - priceAmt: sdkmath.NewInt(56), - expResult: &OrderFulfillment{ - Order: askOrder(1, 20, 55), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: bidOrder(2, 40, 110)}, - Assets: assetCoin(20), - Price: priceCoin(56), - }, - }, - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(56), - PriceLeftAmt: sdkmath.NewInt(-1), + name: "okay: bid", + receiver: &orderFulfillment{ + Order: bidOrder(19, 10, 100, coin(20, "fig")), + AssetsFilledAmt: sdkmath.NewInt(9), + AssetsUnfilledAmt: sdkmath.NewInt(1), + PriceAppliedAmt: sdkmath.NewInt(300), + PriceLeftAmt: sdkmath.NewInt(-200), }, - }, - { - name: "bid price overfill", - receiver: &OrderFulfillment{ - Order: bidOrder(1, 20, 55), - Splits: nil, - AssetsFilledAmt: sdkmath.ZeroInt(), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.ZeroInt(), - PriceLeftAmt: sdkmath.NewInt(55), + expUnfilled: bidOrder(19, 1, 10, coin(2, "fig")), + expReceiver: &orderFulfillment{ + Order: bidOrder(19, 9, 90, coin(18, "fig")), + AssetsFilledAmt: sdkmath.NewInt(9), + AssetsUnfilledAmt: sdkmath.NewInt(0), + PriceAppliedAmt: sdkmath.NewInt(300), + PriceLeftAmt: sdkmath.NewInt(-210), }, - order: &OrderFulfillment{Order: askOrder(2, 40, 110)}, - assetsAmt: sdkmath.NewInt(20), - priceAmt: sdkmath.NewInt(56), - expErr: "cannot fill bid order 1 having price left \"55pcoin\" to ask order 2 at a price of \"56pcoin\": overfill", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { orig := copyOrderFulfillment(tc.receiver) - if tc.expResult == nil { - tc.expResult = copyOrderFulfillment(tc.receiver) + if tc.expReceiver == nil { + tc.expReceiver = copyOrderFulfillment(tc.receiver) } + var unfilled *Order var err error testFunc := func() { - err = tc.receiver.Apply(tc.order, tc.assetsAmt, tc.priceAmt) + unfilled, err = tc.receiver.SplitOrder() } - require.NotPanics(t, testFunc, "Apply") - assertions.AssertErrorValue(t, err, tc.expErr, "Apply error") - if !assertEqualOrderFulfillments(t, tc.expResult, tc.receiver, "order fulfillment after .Apply") { + require.NotPanics(t, testFunc, "SplitOrder") + assertions.AssertErrorValue(t, err, tc.expErr, "SplitOrder error") + assert.Equalf(t, tc.expUnfilled, unfilled, "SplitOrder unfilled order") + if !assertEqualOrderFulfillments(t, tc.expReceiver, tc.receiver, "orderFulfillment after SplitOrder") { t.Logf("Original: %s", orderFulfillmentString(orig)) } }) } } -func TestOrderFulfillment_ApplyLeftoverPrice(t *testing.T) { - type testCase struct { - name string - receiver *OrderFulfillment - askSplit *OrderSplit - amt sdkmath.Int - expFulfillment *OrderFulfillment - expAskSplit *OrderSplit - expPanic string - } - - newTestCase := func(name string, bidSplitIndexes ...int) testCase { - // Picture a bid order with 150 assets at a cost of 5555 being split among 3 ask orders evenly (50 each). - // Each ask order has 53 to sell: 50 are coming from this bid order, and 1 and 2 each from two other bids. - // During initial splitting, the bid will pay each ask 5555 * 50 / 150 = 1851. - // 1851 * 3 = 5553, so there's 2 leftover. - // The other 3 are being bought for 30 each (90 total). - - bidOrderID := uint64(200) - bidOrder := NewOrder(bidOrderID).WithBid(&BidOrder{ - Price: sdk.NewInt64Coin("pcoin", 5555), - }) +func TestOrderFulfillment_AsFilledOrder(t *testing.T) { + askOrder := NewOrder(53).WithAsk(&AskOrder{ + MarketId: 765, + Seller: "mefirst", + Assets: sdk.NewInt64Coin("apple", 15), + Price: sdk.NewInt64Coin("peach", 88), + SellerSettlementFlatFee: &sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(6)}, + AllowPartial: true, + }) + bidOrder := NewOrder(9556).WithBid(&BidOrder{ + MarketId: 145, + Buyer: "gimmiegimmie", + Assets: sdk.NewInt64Coin("acorn", 1171), + Price: sdk.NewInt64Coin("prune", 5100), + BuyerSettlementFees: sdk.NewCoins(sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(14)}), + AllowPartial: false, + }) - tc := testCase{ - name: name, - receiver: &OrderFulfillment{ - Order: bidOrder, - PriceAppliedAmt: sdkmath.NewInt(5553), - PriceLeftAmt: sdkmath.NewInt(2), + tests := []struct { + name string + receiver orderFulfillment + expected *FilledOrder + }{ + { + name: "ask order", + receiver: orderFulfillment{ + Order: askOrder, + PriceAppliedAmt: sdkmath.NewInt(132), + FeesToPay: sdk.NewCoins(sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(7)}), }, - amt: sdkmath.NewInt(2), - expFulfillment: &OrderFulfillment{ - Order: bidOrder, - PriceAppliedAmt: sdkmath.NewInt(5555), - PriceLeftAmt: ZeroAmtAfterSub, + expected: &FilledOrder{ + order: askOrder, + actualPrice: sdk.NewInt64Coin("peach", 132), + actualFees: sdk.NewCoins(sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(7)}), }, - askSplit: &OrderSplit{ - Order: &OrderFulfillment{ - Order: NewOrder(1).WithAsk(&AskOrder{ - Price: sdk.NewInt64Coin("pcoin", 1500), - }), - PriceAppliedAmt: sdkmath.NewInt(1941), // 5555 * 50 / 150 = 1851 from main bid + 90 from the others. - PriceLeftAmt: sdkmath.NewInt(-441), // = 1500 - 1941 - }, - Price: sdk.NewInt64Coin("pcoin", 1851), + }, + { + name: "bid order", + receiver: orderFulfillment{ + Order: bidOrder, + PriceAppliedAmt: sdkmath.NewInt(5123), + FeesToPay: sdk.NewCoins(sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(23)}), }, - expAskSplit: &OrderSplit{ - Order: &OrderFulfillment{ - Order: NewOrder(1).WithAsk(&AskOrder{ - Price: sdk.NewInt64Coin("pcoin", 1500), - }), - PriceAppliedAmt: sdkmath.NewInt(1943), - PriceLeftAmt: sdkmath.NewInt(-443), - }, - Price: sdk.NewInt64Coin("pcoin", 1853), + expected: &FilledOrder{ + order: bidOrder, + actualPrice: sdk.NewInt64Coin("prune", 5123), + actualFees: sdk.NewCoins(sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(23)}), }, - } + }, + } - bidSplits := []*OrderSplit{ - { - // This is the primary bid split that we'll be looking to update. - Order: &OrderFulfillment{Order: NewOrder(bidOrderID).WithBid(&BidOrder{})}, - Price: sdk.NewInt64Coin("pcoin", 1851), // == 5555 / 3 (truncated) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual *FilledOrder + testFunc := func() { + actual = tc.receiver.AsFilledOrder() + } + require.NotPanics(t, testFunc, "AsFilledOrder()") + assert.Equal(t, tc.expected, actual, "AsFilledOrder() result") + }) + } +} + +func TestSumAssetsAndPrice(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + askOrder := func(orderID uint64, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Assets: assets, + Price: price, + }) + } + bidOrder := func(orderID uint64, assets, price sdk.Coin) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Assets: assets, + Price: price, + }) + } + + tests := []struct { + name string + orders []*Order + expAssets sdk.Coins + expPrice sdk.Coins + expPanic string + }{ + { + name: "nil orders", + orders: nil, + expAssets: nil, + expPrice: nil, + }, + { + name: "empty orders", + orders: []*Order{}, + expAssets: nil, + expPrice: nil, + }, + { + name: "nil inside order", + orders: []*Order{ + askOrder(1, coin(2, "apple"), coin(3, "plum")), + NewOrder(4), + askOrder(5, coin(6, "apple"), coin(7, "plum")), }, - { - Order: &OrderFulfillment{Order: NewOrder(bidOrderID + 1).WithBid(&BidOrder{})}, - Price: sdk.NewInt64Coin("pcoin", 30), + expPanic: nilSubTypeErr(4), + }, + { + name: "unknown inside order", + orders: []*Order{ + askOrder(1, coin(2, "apple"), coin(3, "plum")), + newUnknownOrder(4), + askOrder(5, coin(6, "apple"), coin(7, "plum")), }, - { - Order: &OrderFulfillment{Order: NewOrder(bidOrderID + 2).WithBid(&BidOrder{})}, - Price: sdk.NewInt64Coin("pcoin", 60), + expPanic: unknownSubTypeErr(4), + }, + { + name: "one order, ask", + orders: []*Order{askOrder(1, coin(2, "apple"), coin(3, "plum"))}, + expAssets: sdk.NewCoins(coin(2, "apple")), + expPrice: sdk.NewCoins(coin(3, "plum")), + }, + { + name: "one order, bid", + orders: []*Order{bidOrder(1, coin(2, "apple"), coin(3, "plum"))}, + expAssets: sdk.NewCoins(coin(2, "apple")), + expPrice: sdk.NewCoins(coin(3, "plum")), + }, + { + name: "2 orders, same denoms", + orders: []*Order{ + askOrder(1, coin(2, "apple"), coin(3, "plum")), + bidOrder(4, coin(5, "apple"), coin(6, "plum")), }, - { - // This one is similar to [0], but with a different order id. - // It'll be used to test the case where the bid split isn't found. - Order: &OrderFulfillment{Order: NewOrder(bidOrderID + 3).WithBid(&BidOrder{})}, - Price: sdk.NewInt64Coin("pcoin", 1851), + expAssets: sdk.NewCoins(coin(7, "apple")), + expPrice: sdk.NewCoins(coin(9, "plum")), + }, + { + name: "2 orders, diff denoms", + orders: []*Order{ + bidOrder(1, coin(2, "avocado"), coin(3, "peach")), + askOrder(4, coin(5, "apple"), coin(6, "plum")), }, - } - - for _, i := range bidSplitIndexes { - tc.askSplit.Order.Splits = append(tc.askSplit.Order.Splits, bidSplits[i]) - if i == 0 { - tc.expAskSplit.Order.Splits = append(tc.expAskSplit.Order.Splits, &OrderSplit{ - Order: &OrderFulfillment{Order: NewOrder(bidOrderID).WithBid(&BidOrder{})}, - Price: sdk.NewInt64Coin("pcoin", 1853), // == 5555 * 50 / 150 + 2 leftover. - }) - } else { - tc.expAskSplit.Order.Splits = append(tc.expAskSplit.Order.Splits, copyOrderSplit(bidSplits[i])) - } - if i == 3 { - tc.expPanic = "could not apply leftover amount 2 from bid order 200 to ask order 1: bid split not found" - } - } - - return tc - } - - tests := []testCase{ - newTestCase("applies to first bid split", 0, 1, 2), - newTestCase("applies to second bid split", 2, 0, 1), - newTestCase("applies to third bid split", 1, 2, 0), - newTestCase("bid split not found", 1, 2, 3), + expAssets: sdk.NewCoins(coin(2, "avocado"), coin(5, "apple")), + expPrice: sdk.NewCoins(coin(3, "peach"), coin(6, "plum")), + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - origFulfillment := copyOrderFulfillment(tc.receiver) - origSplit := copyOrderSplit(tc.askSplit) - + var assets, price sdk.Coins testFunc := func() { - tc.receiver.ApplyLeftoverPrice(tc.askSplit, tc.amt) - } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "ApplyLeftoverPrice") - if !assertEqualOrderFulfillments(t, tc.expFulfillment, tc.receiver, "OrderFulfillment after .ApplyLeftoverPrice") { - t.Logf("Original: %s", orderFulfillmentString(origFulfillment)) - } - if !assert.Equal(t, tc.expAskSplit, tc.askSplit, "askSplit after ApplyLeftoverPrice") { - t.Logf("Original askSplit: %s", orderSplitString(origSplit)) - t.Logf(" Actual askSplit: %s", orderSplitString(tc.askSplit)) - t.Logf("Expected askSplit: %s", orderSplitString(tc.expAskSplit)) + assets, price = sumAssetsAndPrice(tc.orders) } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "sumAssetsAndPrice") + assert.Equal(t, tc.expAssets.String(), assets.String(), "sumAssetsAndPrice") + assert.Equal(t, tc.expPrice.String(), price.String(), "sumAssetsAndPrice") }) } } -func TestOrderFulfillment_Finalize(t *testing.T) { - sdkNewInt64CoinP := func(denom string, amount int64) *sdk.Coin { - rv := sdk.NewInt64Coin(denom, amount) - return &rv - } - +func TestSumPriceLeft(t *testing.T) { tests := []struct { - name string - receiver *OrderFulfillment - sellerFeeRatio *FeeRatio - expResult *OrderFulfillment - expErr string + name string + fulfillments []*orderFulfillment + expected sdkmath.Int }{ { - name: "ask assets filled zero", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{}), - AssetsFilledAmt: sdkmath.ZeroInt(), - }, - expErr: "no assets filled in ask order 3", - }, - { - name: "ask assets filled negative", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{}), - AssetsFilledAmt: sdkmath.NewInt(-8), - }, - expErr: "no assets filled in ask order 3", - }, - { - name: "bid assets filled zero", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithBid(&BidOrder{}), - AssetsFilledAmt: sdkmath.ZeroInt(), - }, - expErr: "no assets filled in bid order 3", - }, - { - name: "bid assets filled negative", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithBid(&BidOrder{}), - AssetsFilledAmt: sdkmath.NewInt(-8), - }, - expErr: "no assets filled in bid order 3", + name: "nil fulfillments", + fulfillments: nil, + expected: sdkmath.NewInt(0), }, - { - name: "ask partial price not evenly divisible", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 101), - }), - AssetsFilledAmt: sdkmath.NewInt(10), - AssetsUnfilledAmt: sdkmath.NewInt(40), - }, - expErr: `ask order 3 having assets "50apple" cannot be partially filled by "10apple": price "101pear" is not evenly divisible`, + name: "empty fulfillments", + fulfillments: []*orderFulfillment{}, + expected: sdkmath.NewInt(0), }, { - name: "bid partial price not evenly divisible", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 101), - }), - AssetsFilledAmt: sdkmath.NewInt(10), - AssetsUnfilledAmt: sdkmath.NewInt(40), - }, - expErr: `bid order 3 having assets "50apple" cannot be partially filled by "10apple": price "101pear" is not evenly divisible`, + name: "one fulfillment, positive", + fulfillments: []*orderFulfillment{{PriceLeftAmt: sdkmath.NewInt(8)}}, + expected: sdkmath.NewInt(8), }, - { - name: "ask partial fees not evenly divisible", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 201), - }), - AssetsFilledAmt: sdkmath.NewInt(10), - AssetsUnfilledAmt: sdkmath.NewInt(40), - }, - expErr: `ask order 3 having assets "50apple" cannot be partially filled by "10apple": fee "201fig" is not evenly divisible`, + name: "one fulfillment, zero", + fulfillments: []*orderFulfillment{{PriceLeftAmt: sdkmath.NewInt(0)}}, + expected: sdkmath.NewInt(0), }, { - name: "bid partial fees not evenly divisible", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 151)), - }), - AssetsFilledAmt: sdkmath.NewInt(10), - AssetsUnfilledAmt: sdkmath.NewInt(40), - }, - expErr: `bid order 3 having assets "50apple" cannot be partially filled by "10apple": fee "151grape" is not evenly divisible`, + name: "one fulfillment, negative", + fulfillments: []*orderFulfillment{{PriceLeftAmt: sdkmath.NewInt(-3)}}, + expected: sdkmath.NewInt(-3), }, - { - name: "ask ratio calc error", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(10), - AssetsUnfilledAmt: sdkmath.NewInt(40), - PriceAppliedAmt: sdkmath.NewInt(29), - PriceLeftAmt: sdkmath.NewInt(71), - }, - sellerFeeRatio: &FeeRatio{ - Price: sdk.NewInt64Coin("plum", 1), - Fee: sdk.NewInt64Coin("fig", 3), + name: "three fulfillments", + fulfillments: []*orderFulfillment{ + {PriceLeftAmt: sdkmath.NewInt(10)}, + {PriceLeftAmt: sdkmath.NewInt(200)}, + {PriceLeftAmt: sdkmath.NewInt(3000)}, }, - expErr: "could not calculate ask order 3 ratio fee: cannot apply ratio 1plum:3fig to price 29pear: incorrect price denom", + expected: sdkmath.NewInt(3210), }, - { - name: "ask full no ratio", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(0), - }, - sellerFeeRatio: nil, - expResult: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(0), - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(100), - PriceUnfilledAmt: sdkmath.ZeroInt(), - FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200)), - OrderFeesLeft: nil, + name: "three fulfillments, one negative", + fulfillments: []*orderFulfillment{ + {PriceLeftAmt: sdkmath.NewInt(10)}, + {PriceLeftAmt: sdkmath.NewInt(-200)}, + {PriceLeftAmt: sdkmath.NewInt(3000)}, }, + expected: sdkmath.NewInt(2810), }, { - name: "ask full, exact ratio", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(110), - PriceLeftAmt: sdkmath.NewInt(-10), - }, - sellerFeeRatio: &FeeRatio{ - Price: sdk.NewInt64Coin("pear", 10), - Fee: sdk.NewInt64Coin("grape", 1), - }, - expResult: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(110), - PriceLeftAmt: sdkmath.NewInt(-10), - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(100), - PriceUnfilledAmt: sdkmath.ZeroInt(), - FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 11)), - OrderFeesLeft: nil, - }, - }, - { - name: "ask full, loose ratio", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(110), - PriceLeftAmt: sdkmath.NewInt(-10), - }, - sellerFeeRatio: &FeeRatio{ - Price: sdk.NewInt64Coin("pear", 13), - Fee: sdk.NewInt64Coin("grape", 1), - }, - expResult: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(110), - PriceLeftAmt: sdkmath.NewInt(-10), - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(100), - PriceUnfilledAmt: sdkmath.ZeroInt(), - FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 9)), - OrderFeesLeft: nil, - }, - }, - { - name: "ask partial no ratio", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(30), - }, - sellerFeeRatio: nil, - expResult: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(30), - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(40), - PriceUnfilledAmt: sdkmath.NewInt(60), - FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 80)), - OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 120)), - }, - }, - { - name: "ask partial, exact ratio", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(30), - PriceAppliedAmt: sdkmath.NewInt(110), - PriceLeftAmt: sdkmath.NewInt(-10), - }, - sellerFeeRatio: &FeeRatio{ - Price: sdk.NewInt64Coin("pear", 10), - Fee: sdk.NewInt64Coin("grape", 1), - }, - expResult: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(30), - PriceAppliedAmt: sdkmath.NewInt(110), - PriceLeftAmt: sdkmath.NewInt(-10), - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(40), - PriceUnfilledAmt: sdkmath.NewInt(60), - FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 80), sdk.NewInt64Coin("grape", 11)), - OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 120)), - }, - }, - { - name: "ask partial, loose ratio", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(30), - PriceAppliedAmt: sdkmath.NewInt(110), - PriceLeftAmt: sdkmath.NewInt(-10), - }, - sellerFeeRatio: &FeeRatio{ - Price: sdk.NewInt64Coin("pear", 13), - Fee: sdk.NewInt64Coin("fig", 1), - }, - expResult: &OrderFulfillment{ - Order: NewOrder(3).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 200), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(30), - PriceAppliedAmt: sdkmath.NewInt(110), - PriceLeftAmt: sdkmath.NewInt(-10), - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(40), - PriceUnfilledAmt: sdkmath.NewInt(60), - FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 89)), - OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 120)), + name: "three fulfillments, all negative", + fulfillments: []*orderFulfillment{ + {PriceLeftAmt: sdkmath.NewInt(-10)}, + {PriceLeftAmt: sdkmath.NewInt(-200)}, + {PriceLeftAmt: sdkmath.NewInt(-3000)}, }, + expected: sdkmath.NewInt(-3210), }, - { - name: "bid full no leftovers", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 13)), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: sdkmath.NewInt(0), - }, - expResult: &OrderFulfillment{ - Order: NewOrder(3).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 100), - BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 13)), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: sdkmath.NewInt(0), - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(100), - PriceUnfilledAmt: sdkmath.NewInt(0), - FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 13)), - OrderFeesLeft: nil, - }, - }, - { - name: "bid full with leftovers", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 1000), // 1000 / 50 = 20 per asset. - BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 13)), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(993), - PriceLeftAmt: sdkmath.NewInt(7), - Splits: []*OrderSplit{ - { - // This one will get 1 once the loop defaults to 1. - // So, 7 * split assets / 50 filled must be 0. - Order: &OrderFulfillment{ - Order: NewOrder(101).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 5), - Price: sdk.NewInt64Coin("pear", 100), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 5), - Price: sdk.NewInt64Coin("pear", 100), - }}, - AssetsFilledAmt: sdkmath.NewInt(5), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: sdkmath.NewInt(0), - }, - Assets: sdk.NewInt64Coin("apple", 5), - Price: sdk.NewInt64Coin("pear", 100), - }, - { - // This one will not get anything more. - // It's in the same situation as the one above, but the leftover will run out first. - Order: &OrderFulfillment{ - Order: NewOrder(102).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 4), - Price: sdk.NewInt64Coin("pear", 80), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 4), - Price: sdk.NewInt64Coin("pear", 80), - }}, - AssetsFilledAmt: sdkmath.NewInt(4), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceLeftAmt: sdkmath.NewInt(0), - }, - Assets: sdk.NewInt64Coin("apple", 4), - Price: sdk.NewInt64Coin("pear", 80), - }, - { - // This one will get 4 in the first pass of the loop. - // I.e. 7 * split assets / 50 = 4. Assets 29 to 39 - Order: &OrderFulfillment{ - Order: NewOrder(103).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 35), - Price: sdk.NewInt64Coin("pear", 693), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 35), - Price: sdk.NewInt64Coin("pear", 693), - }}, - AssetsFilledAmt: sdkmath.NewInt(35), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(693), - PriceLeftAmt: sdkmath.NewInt(0), - }, - Assets: sdk.NewInt64Coin("apple", 35), - Price: sdk.NewInt64Coin("pear", 693), - }, - { - // This one will get 2 due to price left. - // I also need this one to have 7 * assets / 50 = 0, so it doesn't get 1 more on the first pass. - Order: &OrderFulfillment{ - Order: NewOrder(104).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 6), - Price: sdk.NewInt64Coin("pear", 122), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 6), - Price: sdk.NewInt64Coin("pear", 120), - }}, - AssetsFilledAmt: sdkmath.NewInt(6), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(120), - PriceLeftAmt: sdkmath.NewInt(2), - }, - Assets: sdk.NewInt64Coin("apple", 6), - Price: sdk.NewInt64Coin("pear", 120), - }, - }, - }, - expResult: &OrderFulfillment{ - Order: NewOrder(3).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 50), - Price: sdk.NewInt64Coin("pear", 1000), - BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 13)), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(1000), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{ - { - // This one should get 1 once the loop defaults to 1. - Order: &OrderFulfillment{ - Order: NewOrder(101).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 5), - Price: sdk.NewInt64Coin("pear", 100), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 5), - Price: sdk.NewInt64Coin("pear", 101), - }}, - AssetsFilledAmt: sdkmath.NewInt(5), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(101), - PriceLeftAmt: sdkmath.NewInt(-1), - }, - Assets: sdk.NewInt64Coin("apple", 5), - Price: sdk.NewInt64Coin("pear", 101), - }, - { - // This one will not get anything more. - Order: &OrderFulfillment{ - Order: NewOrder(102).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 4), - Price: sdk.NewInt64Coin("pear", 80), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 4), - Price: sdk.NewInt64Coin("pear", 80), - }}, - AssetsFilledAmt: sdkmath.NewInt(4), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceLeftAmt: sdkmath.NewInt(0), - }, - Assets: sdk.NewInt64Coin("apple", 4), - Price: sdk.NewInt64Coin("pear", 80), - }, - { - // This one should get 4 in the first pass of the loop. - Order: &OrderFulfillment{ - Order: NewOrder(103).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 35), - Price: sdk.NewInt64Coin("pear", 693), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 35), - Price: sdk.NewInt64Coin("pear", 697), - }}, - AssetsFilledAmt: sdkmath.NewInt(35), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(697), - PriceLeftAmt: sdkmath.NewInt(-4), - }, - Assets: sdk.NewInt64Coin("apple", 35), - Price: sdk.NewInt64Coin("pear", 697), - }, - { - // this one will get 2 due to price left. - // I also need this one to have 7 * assets / 50 = 0, so it doesn't get 1 more on the first pass. - Order: &OrderFulfillment{ - Order: NewOrder(104).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 6), - Price: sdk.NewInt64Coin("pear", 122), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 6), - Price: sdk.NewInt64Coin("pear", 122), - }}, - AssetsFilledAmt: sdkmath.NewInt(6), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(122), - PriceLeftAmt: ZeroAmtAfterSub, - }, - Assets: sdk.NewInt64Coin("apple", 6), - Price: sdk.NewInt64Coin("pear", 122), - }, - }, - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(1000), - PriceUnfilledAmt: sdkmath.NewInt(0), - FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 13)), - OrderFeesLeft: nil, + name: "three fulfillments, all large", + fulfillments: []*orderFulfillment{ + {PriceLeftAmt: newInt(t, "3,000,000,000,000,000,000,000,000,000,000,300")}, + {PriceLeftAmt: newInt(t, "40,000,000,000,000,000,000,000,000,000,000,040")}, + {PriceLeftAmt: newInt(t, "500,000,000,000,000,000,000,000,000,000,000,005")}, }, + expected: newInt(t, "543,000,000,000,000,000,000,000,000,000,000,345"), }, { - name: "bid partial no leftovers", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 75), - Price: sdk.NewInt64Coin("pear", 150), - BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 300), sdk.NewInt64Coin("grape", 12)), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(25), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: sdkmath.NewInt(50), - }, - expResult: &OrderFulfillment{ - Order: NewOrder(3).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 75), - Price: sdk.NewInt64Coin("pear", 150), - BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 300), sdk.NewInt64Coin("grape", 12)), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(25), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: sdkmath.NewInt(50), - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(100), - PriceUnfilledAmt: sdkmath.NewInt(50), - FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 8)), - OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 100), sdk.NewInt64Coin("grape", 4)), - }, - }, - { - name: "bid partial with leftovers", - receiver: &OrderFulfillment{ - Order: NewOrder(3).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 75), - Price: sdk.NewInt64Coin("pear", 1500), // 1020 / 51 = 20 per asset. - BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 300), sdk.NewInt64Coin("grape", 12)), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(25), - PriceAppliedAmt: sdkmath.NewInt(993), - PriceLeftAmt: sdkmath.NewInt(507), - Splits: []*OrderSplit{ - { - // This one will get 1 once the loop defaults to 1. - // So, 7 * split assets / 50 filled must be 0. - Order: &OrderFulfillment{ - Order: NewOrder(101).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 5), - Price: sdk.NewInt64Coin("pear", 100), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 5), - Price: sdk.NewInt64Coin("pear", 100), - }}, - AssetsFilledAmt: sdkmath.NewInt(5), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: sdkmath.NewInt(0), - }, - Assets: sdk.NewInt64Coin("apple", 5), - Price: sdk.NewInt64Coin("pear", 100), - }, - { - // This one will not get anything more. - // It's in the same situation as the one above, but the leftover will run out first. - Order: &OrderFulfillment{ - Order: NewOrder(102).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 4), - Price: sdk.NewInt64Coin("pear", 80), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 4), - Price: sdk.NewInt64Coin("pear", 80), - }}, - AssetsFilledAmt: sdkmath.NewInt(4), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceLeftAmt: sdkmath.NewInt(0), - }, - Assets: sdk.NewInt64Coin("apple", 4), - Price: sdk.NewInt64Coin("pear", 80), - }, - { - // This one will get 4 in the first pass of the loop. - // I.e. 7 * split assets / 50 = 4. Assets 29 to 39 - Order: &OrderFulfillment{ - Order: NewOrder(103).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 35), - Price: sdk.NewInt64Coin("pear", 693), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 35), - Price: sdk.NewInt64Coin("pear", 693), - }}, - AssetsFilledAmt: sdkmath.NewInt(35), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(693), - PriceLeftAmt: sdkmath.NewInt(0), - }, - Assets: sdk.NewInt64Coin("apple", 35), - Price: sdk.NewInt64Coin("pear", 693), - }, - { - // This one will get 2 due to price left. - // I also need this one to have 7 * assets / 50 = 0, so it doesn't get 1 more on the first pass. - Order: &OrderFulfillment{ - Order: NewOrder(104).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 6), - Price: sdk.NewInt64Coin("pear", 122), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 6), - Price: sdk.NewInt64Coin("pear", 120), - }}, - AssetsFilledAmt: sdkmath.NewInt(6), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(120), - PriceLeftAmt: sdkmath.NewInt(2), - }, - Assets: sdk.NewInt64Coin("apple", 6), - Price: sdk.NewInt64Coin("pear", 120), - }, - }, - }, - expResult: &OrderFulfillment{ - Order: NewOrder(3).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 75), - Price: sdk.NewInt64Coin("pear", 1500), - BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 300), sdk.NewInt64Coin("grape", 12)), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(25), - PriceAppliedAmt: sdkmath.NewInt(1000), - PriceLeftAmt: sdkmath.NewInt(500), - Splits: []*OrderSplit{ - { - // This one should get 1 once the loop defaults to 1. - Order: &OrderFulfillment{ - Order: NewOrder(101).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 5), - Price: sdk.NewInt64Coin("pear", 100), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 5), - Price: sdk.NewInt64Coin("pear", 101), - }}, - AssetsFilledAmt: sdkmath.NewInt(5), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(101), - PriceLeftAmt: sdkmath.NewInt(-1), - }, - Assets: sdk.NewInt64Coin("apple", 5), - Price: sdk.NewInt64Coin("pear", 101), - }, - { - // This one will not get anything more. - Order: &OrderFulfillment{ - Order: NewOrder(102).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 4), - Price: sdk.NewInt64Coin("pear", 80), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 4), - Price: sdk.NewInt64Coin("pear", 80), - }}, - AssetsFilledAmt: sdkmath.NewInt(4), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceLeftAmt: sdkmath.NewInt(0), - }, - Assets: sdk.NewInt64Coin("apple", 4), - Price: sdk.NewInt64Coin("pear", 80), - }, - { - // This one should get 4 in the first pass of the loop. - Order: &OrderFulfillment{ - Order: NewOrder(103).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 35), - Price: sdk.NewInt64Coin("pear", 693), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 35), - Price: sdk.NewInt64Coin("pear", 697), - }}, - AssetsFilledAmt: sdkmath.NewInt(35), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(697), - PriceLeftAmt: sdkmath.NewInt(-4), - }, - Assets: sdk.NewInt64Coin("apple", 35), - Price: sdk.NewInt64Coin("pear", 697), - }, - { - // this one will get 2 due to price left. - // I also need this one to have 7 * assets / 50 = 0, so it doesn't get 1 more on the first pass. - Order: &OrderFulfillment{ - Order: NewOrder(104).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin("apple", 6), - Price: sdk.NewInt64Coin("pear", 122), - }), - Splits: []*OrderSplit{{ - Order: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - Assets: sdk.NewInt64Coin("apple", 6), - Price: sdk.NewInt64Coin("pear", 122), - }}, - AssetsFilledAmt: sdkmath.NewInt(6), - AssetsUnfilledAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(122), - PriceLeftAmt: ZeroAmtAfterSub, - }, - Assets: sdk.NewInt64Coin("apple", 6), - Price: sdk.NewInt64Coin("pear", 122), - }, - }, - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(1000), - PriceUnfilledAmt: sdkmath.NewInt(500), - FeesToPay: sdk.NewCoins(sdk.NewInt64Coin("fig", 200), sdk.NewInt64Coin("grape", 8)), - OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 100), sdk.NewInt64Coin("grape", 4)), + name: "four fullfillments, small negative zero large", + fulfillments: []*orderFulfillment{ + {PriceLeftAmt: sdkmath.NewInt(654_789)}, + {PriceLeftAmt: sdkmath.NewInt(-789)}, + {PriceLeftAmt: sdkmath.NewInt(0)}, + {PriceLeftAmt: newInt(t, "543,000,000,000,000,000,000,000,000,000,000,345")}, }, + expected: newInt(t, "543,000,000,000,000,000,000,000,000,000,654,345"), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - orig := copyOrderFulfillment(tc.receiver) - if tc.expResult == nil { - tc.expResult = copyOrderFulfillment(tc.receiver) - tc.expResult.PriceFilledAmt = sdkmath.ZeroInt() - tc.expResult.PriceUnfilledAmt = sdkmath.ZeroInt() - } - - var err error + var actual sdkmath.Int testFunc := func() { - err = tc.receiver.Finalize(tc.sellerFeeRatio) - } - require.NotPanics(t, testFunc, "Finalize") - assertions.AssertErrorValue(t, err, tc.expErr, "Finalize error") - if !assertEqualOrderFulfillments(t, tc.expResult, tc.receiver, "receiver after Finalize") { - t.Logf("Original: %s", orderFulfillmentString(orig)) + actual = sumPriceLeft(tc.fulfillments) } + require.NotPanics(t, testFunc, "sumPriceLeft") + assert.Equal(t, tc.expected, actual, "sumPriceLeft") }) } } -func TestOrderFulfillment_Validate(t *testing.T) { - askOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { +func TestValidateCanSettle(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + } + askOrder := func(orderID uint64, assets, price sdk.Coin) *Order { return NewOrder(orderID).WithAsk(&AskOrder{ - MarketId: 987, - Seller: "steve", - Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, - Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(priceAmt)}, + Assets: assets, + Price: price, }) } - bidOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + bidOrder := func(orderID uint64, assets, price sdk.Coin) *Order { return NewOrder(orderID).WithBid(&BidOrder{ - MarketId: 987, - Buyer: "bruce", - Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, - Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(priceAmt)}, + Assets: assets, + Price: price, }) } tests := []struct { - name string - f OrderFulfillment - expErr string + name string + askOrders []*Order + bidOrders []*Order + expErr string }{ { - name: "nil inside order", - f: OrderFulfillment{Order: NewOrder(8)}, - expErr: nilSubTypeErr(8), + name: "nil ask orders", + askOrders: nil, + bidOrders: []*Order{bidOrder(8, coin(10, "apple"), coin(11, "peach"))}, + expErr: "no ask orders provided", + }, + { + name: "no bid orders", + askOrders: []*Order{askOrder(7, coin(10, "apple"), coin(11, "peach"))}, + bidOrders: nil, + expErr: "no bid orders provided", + }, + { + name: "no orders", + askOrders: nil, + bidOrders: nil, + expErr: joinErrs("no ask orders provided", "no bid orders provided"), + }, + { + name: "bid order in asks", + askOrders: []*Order{ + askOrder(7, coin(10, "apple"), coin(11, "peach")), + bidOrder(8, coin(10, "apple"), coin(11, "peach")), + askOrder(9, coin(10, "apple"), coin(11, "peach")), + }, + bidOrders: []*Order{bidOrder(22, coin(10, "apple"), coin(11, "peach"))}, + expErr: "bid order 8 is not an ask order but is in the askOrders list at index 1", + }, + { + name: "nil inside order in asks", + askOrders: []*Order{ + askOrder(7, coin(10, "apple"), coin(11, "peach")), + NewOrder(8), + askOrder(9, coin(10, "apple"), coin(11, "peach")), + }, + bidOrders: []*Order{bidOrder(22, coin(10, "apple"), coin(11, "peach"))}, + expErr: " order 8 is not an ask order but is in the askOrders list at index 1", }, { - name: "unknown inside order", - f: OrderFulfillment{Order: newUnknownOrder(12)}, - expErr: unknownSubTypeErr(12), + name: "unknown inside order in asks", + askOrders: []*Order{ + askOrder(7, coin(10, "apple"), coin(11, "peach")), + newUnknownOrder(8), + askOrder(9, coin(10, "apple"), coin(11, "peach")), + }, + bidOrders: []*Order{bidOrder(22, coin(10, "apple"), coin(11, "peach"))}, + expErr: "*exchange.unknownOrderType order 8 is not an ask order but is in the askOrders list at index 1", }, { - name: "order price greater than price applied: ask", - f: OrderFulfillment{ - Order: askOrder(52, 10, 401), - PriceAppliedAmt: sdkmath.NewInt(400), - AssetsFilledAmt: sdkmath.NewInt(10), + name: "ask order in bids", + askOrders: []*Order{askOrder(7, coin(10, "apple"), coin(11, "peach"))}, + bidOrders: []*Order{ + bidOrder(21, coin(10, "apple"), coin(11, "peach")), + askOrder(22, coin(10, "apple"), coin(11, "peach")), + bidOrder(23, coin(10, "apple"), coin(11, "peach")), }, - expErr: "ask order 52 price \"401peach\" is more than price filled \"400peach\"", + expErr: "ask order 22 is not a bid order but is in the bidOrders list at index 1", }, { - name: "order price equal to price applied: ask", - f: OrderFulfillment{ - Order: askOrder(53, 10, 401), - PriceAppliedAmt: sdkmath.NewInt(401), - AssetsFilledAmt: sdkmath.NewInt(10), + name: "nil inside order in bids", + askOrders: []*Order{askOrder(7, coin(10, "apple"), coin(11, "peach"))}, + bidOrders: []*Order{ + bidOrder(21, coin(10, "apple"), coin(11, "peach")), + NewOrder(22), + bidOrder(23, coin(10, "apple"), coin(11, "peach")), }, - expErr: "", + expErr: " order 22 is not a bid order but is in the bidOrders list at index 1", }, { - name: "order price less than price applied: ask", - f: OrderFulfillment{ - Order: askOrder(54, 10, 401), - PriceAppliedAmt: sdkmath.NewInt(402), - AssetsFilledAmt: sdkmath.NewInt(10), + name: "unknown inside order in bids", + askOrders: []*Order{askOrder(7, coin(10, "apple"), coin(11, "peach"))}, + bidOrders: []*Order{ + bidOrder(21, coin(10, "apple"), coin(11, "peach")), + newUnknownOrder(22), + bidOrder(23, coin(10, "apple"), coin(11, "peach")), }, - expErr: "", + expErr: "*exchange.unknownOrderType order 22 is not a bid order but is in the bidOrders list at index 1", }, { - name: "order price greater than price applied: bid", - f: OrderFulfillment{ - Order: bidOrder(71, 17, 432), - PriceAppliedAmt: sdkmath.NewInt(431), - AssetsFilledAmt: sdkmath.NewInt(17), + name: "orders in wrong args", + askOrders: []*Order{ + askOrder(15, coin(10, "apple"), coin(11, "peach")), + bidOrder(16, coin(10, "apple"), coin(11, "peach")), + askOrder(17, coin(10, "apple"), coin(11, "peach")), }, - expErr: "bid order 71 price \"432peach\" is not equal to price filled \"431peach\"", + bidOrders: []*Order{ + bidOrder(91, coin(10, "apple"), coin(11, "peach")), + askOrder(92, coin(10, "apple"), coin(11, "peach")), + bidOrder(93, coin(10, "apple"), coin(11, "peach")), + }, + expErr: joinErrs( + "bid order 16 is not an ask order but is in the askOrders list at index 1", + "ask order 92 is not a bid order but is in the bidOrders list at index 1", + ), }, { - name: "order price equal to price applied: bid", - f: OrderFulfillment{ - Order: bidOrder(72, 17, 432), - PriceAppliedAmt: sdkmath.NewInt(432), - AssetsFilledAmt: sdkmath.NewInt(17), + name: "multiple ask asset denoms", + askOrders: []*Order{ + askOrder(55, coin(10, "apple"), coin(11, "peach")), + askOrder(56, coin(20, "avocado"), coin(22, "peach")), }, - expErr: "", + bidOrders: []*Order{ + bidOrder(61, coin(10, "apple"), coin(11, "peach")), + }, + expErr: "cannot settle with multiple ask order asset denoms \"10apple,20avocado\"", }, { - name: "order price less than price applied: bid", - f: OrderFulfillment{ - Order: bidOrder(73, 17, 432), - PriceAppliedAmt: sdkmath.NewInt(433), - AssetsFilledAmt: sdkmath.NewInt(17), + name: "multiple ask price denoms", + askOrders: []*Order{ + askOrder(55, coin(10, "apple"), coin(11, "peach")), + askOrder(56, coin(20, "apple"), coin(22, "plum")), }, - expErr: "bid order 73 price \"432peach\" is not equal to price filled \"433peach\"", + bidOrders: []*Order{ + bidOrder(61, coin(10, "apple"), coin(11, "peach")), + }, + expErr: "cannot settle with multiple ask order price denoms \"11peach,22plum\"", }, { - name: "order assets less than assets filled: ask", - f: OrderFulfillment{ - Order: askOrder(101, 53, 12345), - PriceAppliedAmt: sdkmath.NewInt(12345), - AssetsFilledAmt: sdkmath.NewInt(54), + name: "multiple bid asset denoms", + askOrders: []*Order{askOrder(88, coin(10, "apple"), coin(11, "peach"))}, + bidOrders: []*Order{ + bidOrder(12, coin(10, "apple"), coin(11, "peach")), + bidOrder(13, coin(20, "avocado"), coin(22, "peach")), }, - expErr: "ask order 101 assets \"53apple\" does not equal filled assets \"54apple\"", + expErr: "cannot settle with multiple bid order asset denoms \"10apple,20avocado\"", }, { - name: "order assets equal to assets filled: ask", - f: OrderFulfillment{ - Order: askOrder(202, 53, 12345), - PriceAppliedAmt: sdkmath.NewInt(12345), - AssetsFilledAmt: sdkmath.NewInt(53), + name: "multiple bid price denoms", + askOrders: []*Order{askOrder(88, coin(10, "apple"), coin(11, "peach"))}, + bidOrders: []*Order{ + bidOrder(12, coin(10, "apple"), coin(11, "peach")), + bidOrder(13, coin(20, "apple"), coin(22, "plum")), }, - expErr: "", + expErr: "cannot settle with multiple bid order price denoms \"11peach,22plum\"", }, { - name: "order assets more than assets filled: ask", - f: OrderFulfillment{ - Order: askOrder(303, 53, 12345), - PriceAppliedAmt: sdkmath.NewInt(12345), - AssetsFilledAmt: sdkmath.NewInt(52), + name: "all different denoms", + askOrders: []*Order{ + askOrder(55, coin(10, "apple"), coin(11, "peach")), + askOrder(56, coin(20, "avocado"), coin(22, "plum")), }, - expErr: "ask order 303 assets \"53apple\" does not equal filled assets \"52apple\"", + bidOrders: []*Order{ + bidOrder(12, coin(30, "acorn"), coin(33, "prune")), + bidOrder(13, coin(40, "acai"), coin(44, "pear")), + }, + expErr: joinErrs( + "cannot settle with multiple ask order asset denoms \"10apple,20avocado\"", + "cannot settle with multiple ask order price denoms \"11peach,22plum\"", + "cannot settle with multiple bid order asset denoms \"40acai,30acorn\"", + "cannot settle with multiple bid order price denoms \"44pear,33prune\"", + ), }, { - name: "order assets less than assets filled: bid", - f: OrderFulfillment{ - Order: bidOrder(404, 53, 12345), - PriceAppliedAmt: sdkmath.NewInt(12345), - AssetsFilledAmt: sdkmath.NewInt(54), + name: "different ask and bid asset denoms", + askOrders: []*Order{ + askOrder(15, coin(10, "apple"), coin(11, "peach")), + askOrder(16, coin(20, "apple"), coin(22, "peach")), }, - expErr: "bid order 404 assets \"53apple\" does not equal filled assets \"54apple\"", + bidOrders: []*Order{ + bidOrder(2001, coin(30, "acorn"), coin(33, "peach")), + bidOrder(2002, coin(40, "acorn"), coin(44, "peach")), + }, + expErr: "cannot settle different ask \"30apple\" and bid \"70acorn\" asset denoms", }, { - name: "order assets equal to assets filled: bid", - f: OrderFulfillment{ - Order: bidOrder(505, 53, 12345), - PriceAppliedAmt: sdkmath.NewInt(12345), - AssetsFilledAmt: sdkmath.NewInt(53), + name: "different ask and bid price denoms", + askOrders: []*Order{ + askOrder(15, coin(10, "apple"), coin(11, "peach")), + askOrder(16, coin(20, "apple"), coin(22, "peach")), }, - expErr: "", + bidOrders: []*Order{ + bidOrder(2001, coin(30, "apple"), coin(33, "plum")), + bidOrder(2002, coin(40, "apple"), coin(44, "plum")), + }, + expErr: "cannot settle different ask \"33peach\" and bid \"77plum\" price denoms", }, { - name: "order assets more than assets filled: bid", - f: OrderFulfillment{ - Order: bidOrder(606, 53, 12345), - PriceAppliedAmt: sdkmath.NewInt(12345), - AssetsFilledAmt: sdkmath.NewInt(52), + name: "different ask and bid denoms", + askOrders: []*Order{ + askOrder(15, coin(10, "apple"), coin(11, "peach")), + askOrder(16, coin(20, "apple"), coin(22, "peach")), }, - expErr: "bid order 606 assets \"53apple\" does not equal filled assets \"52apple\"", + bidOrders: []*Order{ + bidOrder(2001, coin(30, "acorn"), coin(33, "plum")), + bidOrder(2002, coin(40, "acorn"), coin(44, "plum")), + }, + expErr: joinErrs( + "cannot settle different ask \"30apple\" and bid \"70acorn\" asset denoms", + "cannot settle different ask \"33peach\" and bid \"77plum\" price denoms", + ), }, } @@ -6782,2077 +3833,1083 @@ func TestOrderFulfillment_Validate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var err error testFunc := func() { - err = tc.f.Validate() + err = validateCanSettle(tc.askOrders, tc.bidOrders) } - require.NotPanics(t, testFunc, "Validate") - assertions.AssertErrorValue(t, err, tc.expErr, "Validate error") + require.NotPanics(t, testFunc, "validateCanSettle") + assertions.AssertErrorValue(t, err, tc.expErr, "validateCanSettle error") }) } } -func TestOrderFulfillment_Validate2(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} +func TestAllocateAssets(t *testing.T) { + askOrder := func(orderID uint64, assetsAmt int64, seller string) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Seller: seller, + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, + }) + } + bidOrder := func(orderID uint64, assetsAmt int64, buyer string) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Buyer: buyer, + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, + }) } - coinP := func(amount int64, denom string) *sdk.Coin { - return &sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} + newOF := func(order *Order, dists ...*distribution) *orderFulfillment { + rv := &orderFulfillment{ + Order: order, + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: order.GetAssets().Amount, + } + if len(dists) > 0 { + rv.AssetDists = dists + for _, d := range dists { + rv.AssetsFilledAmt = rv.AssetsFilledAmt.Add(d.Amount) + rv.AssetsUnfilledAmt = rv.AssetsUnfilledAmt.Sub(d.Amount) + } + } + return rv } - coins := func(amount int64, denom string) sdk.Coins { - return sdk.Coins{coin(amount, denom)} + dist := func(addr string, amount int64) *distribution { + return &distribution{Address: addr, Amount: sdkmath.NewInt(amount)} } tests := []struct { - name string - receiver OrderFulfillment - expErr string + name string + askOFs []*orderFulfillment + bidOFs []*orderFulfillment + expAskOFs []*orderFulfillment + expBidOfs []*orderFulfillment + expErr string }{ { - name: "nil order type", - receiver: OrderFulfillment{Order: NewOrder(2)}, - expErr: nilSubTypeErr(2), + name: "one ask, one bid: both full", + askOFs: []*orderFulfillment{newOF(askOrder(5, 10, "seller"))}, + bidOFs: []*orderFulfillment{newOF(bidOrder(6, 10, "buyer"))}, + expAskOFs: []*orderFulfillment{newOF(askOrder(5, 10, "seller"), dist("buyer", 10))}, + expBidOfs: []*orderFulfillment{newOF(bidOrder(6, 10, "buyer"), dist("seller", 10))}, }, { - name: "unknown order type", - receiver: OrderFulfillment{Order: newUnknownOrder(3)}, - expErr: unknownSubTypeErr(3), + name: "one ask, one bid: ask partial", + askOFs: []*orderFulfillment{newOF(askOrder(5, 11, "seller"))}, + bidOFs: []*orderFulfillment{newOF(bidOrder(16, 10, "buyer"))}, + expAskOFs: []*orderFulfillment{newOF(askOrder(5, 11, "seller"), dist("buyer", 10))}, + expBidOfs: []*orderFulfillment{newOF(bidOrder(16, 10, "buyer"), dist("seller", 10))}, }, { - name: "not finalized, ask", - receiver: OrderFulfillment{ - Order: NewOrder(4).WithAsk(&AskOrder{}), - IsFinalized: false, - }, - expErr: "fulfillment for ask order 4 has not been finalized", + name: "one ask, one bid: bid partial", + askOFs: []*orderFulfillment{newOF(askOrder(15, 10, "seller"))}, + bidOFs: []*orderFulfillment{newOF(bidOrder(6, 11, "buyer"))}, + expAskOFs: []*orderFulfillment{newOF(askOrder(15, 10, "seller"), dist("buyer", 10))}, + expBidOfs: []*orderFulfillment{newOF(bidOrder(6, 11, "buyer"), dist("seller", 10))}, }, { - name: "not finalized, bid", - receiver: OrderFulfillment{ - Order: NewOrder(4).WithBid(&BidOrder{}), - IsFinalized: false, + name: "one ask, two bids: last bid not touched", + askOFs: []*orderFulfillment{newOF(askOrder(22, 10, "seller"))}, + bidOFs: []*orderFulfillment{ + newOF(bidOrder(64, 12, "buyer64")), + newOF(bidOrder(78, 1, "buyer78")), }, - expErr: "fulfillment for bid order 4 has not been finalized", - }, - - { - name: "assets unfilled negative", - receiver: OrderFulfillment{ - Order: NewOrder(5).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(-12), - AssetsFilledAmt: sdkmath.NewInt(97), + expAskOFs: []*orderFulfillment{newOF(askOrder(22, 10, "seller"), dist("buyer64", 10))}, + expBidOfs: []*orderFulfillment{ + newOF(bidOrder(64, 12, "buyer64"), dist("seller", 10)), + newOF(bidOrder(78, 1, "buyer78")), }, - expErr: "ask order 5 having assets \"55apple\" has negative assets left \"-12apple\" after filling \"97apple\"", }, { - name: "assets filled zero", - receiver: OrderFulfillment{ - Order: NewOrder(6).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(55), - AssetsFilledAmt: sdkmath.NewInt(0), + name: "two asks, one bids: last ask not touched", + askOFs: []*orderFulfillment{ + newOF(askOrder(888, 10, "seller888")), + newOF(askOrder(999, 10, "seller999")), }, - expErr: "cannot fill non-positive assets \"0apple\" on bid order 6 having assets \"55apple\"", - }, - { - name: "assets filled negative", - receiver: OrderFulfillment{ - Order: NewOrder(7).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(56), - AssetsFilledAmt: sdkmath.NewInt(-1), + bidOFs: []*orderFulfillment{newOF(bidOrder(6, 10, "buyer"))}, + expAskOFs: []*orderFulfillment{ + newOF(askOrder(888, 10, "seller888"), dist("buyer", 10)), + newOF(askOrder(999, 10, "seller999")), }, - expErr: "cannot fill non-positive assets \"-1apple\" on ask order 7 having assets \"55apple\"", + expBidOfs: []*orderFulfillment{newOF(bidOrder(6, 10, "buyer"), dist("seller888", 10))}, }, { - name: "assets tracked too low", - receiver: OrderFulfillment{ - Order: NewOrder(8).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(44), - AssetsFilledAmt: sdkmath.NewInt(10), + name: "two asks, three bids: both full", + askOFs: []*orderFulfillment{ + newOF(askOrder(101, 15, "seller101")), + newOF(askOrder(102, 25, "seller102")), + }, + bidOFs: []*orderFulfillment{ + newOF(bidOrder(103, 10, "buyer103")), + newOF(bidOrder(104, 8, "buyer104")), + newOF(bidOrder(105, 22, "buyer105")), }, - expErr: "tracked assets \"54apple\" does not equal bid order 8 assets \"55apple\"", - }, - { - name: "assets tracked too high", - receiver: OrderFulfillment{ - Order: NewOrder(8).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(44), - AssetsFilledAmt: sdkmath.NewInt(12), + expAskOFs: []*orderFulfillment{ + newOF(askOrder(101, 15, "seller101"), dist("buyer103", 10), dist("buyer104", 5)), + newOF(askOrder(102, 25, "seller102"), dist("buyer104", 3), dist("buyer105", 22)), }, - expErr: "tracked assets \"56apple\" does not equal ask order 8 assets \"55apple\"", - }, - - { - name: "price left equals order price", - receiver: OrderFulfillment{ - Order: NewOrder(19).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98789), + expBidOfs: []*orderFulfillment{ + newOF(bidOrder(103, 10, "buyer103"), dist("seller101", 10)), + newOF(bidOrder(104, 8, "buyer104"), dist("seller101", 5), dist("seller102", 3)), + newOF(bidOrder(105, 22, "buyer105"), dist("seller102", 22)), }, - expErr: "price left \"98789plum\" is not less than ask order 19 price \"98789plum\"", }, { - name: "price left more than order price", - receiver: OrderFulfillment{ - Order: NewOrder(20).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98790), + name: "two asks, three bids: ask partial", + askOFs: []*orderFulfillment{ + newOF(askOrder(101, 15, "seller101")), + newOF(askOrder(102, 26, "seller102")), }, - expErr: "price left \"98790plum\" is not less than bid order 20 price \"98789plum\"", - }, - { - name: "price applied zero", - receiver: OrderFulfillment{ - Order: NewOrder(21).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98788), - PriceAppliedAmt: sdkmath.NewInt(0), + bidOFs: []*orderFulfillment{ + newOF(bidOrder(103, 10, "buyer103")), + newOF(bidOrder(104, 8, "buyer104")), + newOF(bidOrder(105, 22, "buyer105")), }, - expErr: "cannot apply non-positive price \"0plum\" to bid order 21 having price \"98789plum\"", - }, - { - name: "price applied negative", - receiver: OrderFulfillment{ - Order: NewOrder(22).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98788), - PriceAppliedAmt: sdkmath.NewInt(-1), + expAskOFs: []*orderFulfillment{ + newOF(askOrder(101, 15, "seller101"), dist("buyer103", 10), dist("buyer104", 5)), + newOF(askOrder(102, 26, "seller102"), dist("buyer104", 3), dist("buyer105", 22)), }, - expErr: "cannot apply non-positive price \"-1plum\" to ask order 22 having price \"98789plum\"", - }, - { - name: "price tracked too low", - receiver: OrderFulfillment{ - Order: NewOrder(23).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(788), + expBidOfs: []*orderFulfillment{ + newOF(bidOrder(103, 10, "buyer103"), dist("seller101", 10)), + newOF(bidOrder(104, 8, "buyer104"), dist("seller101", 5), dist("seller102", 3)), + newOF(bidOrder(105, 22, "buyer105"), dist("seller102", 22)), }, - expErr: "tracked price \"98788plum\" does not equal ask order 23 price \"98789plum\"", }, { - name: "price tracked too high", - receiver: OrderFulfillment{ - Order: NewOrder(24).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98001), - PriceAppliedAmt: sdkmath.NewInt(789), + name: "two asks, three bids: bid partial", + askOFs: []*orderFulfillment{ + newOF(askOrder(101, 15, "seller101")), + newOF(askOrder(102, 25, "seller102")), }, - expErr: "tracked price \"98790plum\" does not equal bid order 24 price \"98789plum\"", - }, - { - name: "price filled zero", - receiver: OrderFulfillment{ - Order: NewOrder(25).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(98789), - PriceFilledAmt: sdkmath.NewInt(0), - }, - expErr: "cannot fill ask order 25 having price \"98789plum\" with non-positive price \"0plum\"", - }, - { - name: "price filled negative", - receiver: OrderFulfillment{ - Order: NewOrder(26).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(98790), - PriceFilledAmt: sdkmath.NewInt(-1), - }, - expErr: "cannot fill bid order 26 having price \"98789plum\" with non-positive price \"-1plum\"", - }, - { - name: "total price too low", - receiver: OrderFulfillment{ - Order: NewOrder(27).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8788), - }, - expErr: "filled price \"8788plum\" plus unfilled price \"90000plum\" does not equal order price \"98789plum\" for ask order 27", - }, - { - name: "total price too high", - receiver: OrderFulfillment{ - Order: NewOrder(28).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90001), - PriceFilledAmt: sdkmath.NewInt(8789), - }, - expErr: "filled price \"8789plum\" plus unfilled price \"90001plum\" does not equal order price \"98789plum\" for bid order 28", - }, - { - name: "price unfilled negative", - receiver: OrderFulfillment{ - Order: NewOrder(29).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(-1), - PriceFilledAmt: sdkmath.NewInt(98790), + bidOFs: []*orderFulfillment{ + newOF(bidOrder(103, 10, "buyer103")), + newOF(bidOrder(104, 8, "buyer104")), + newOF(bidOrder(105, 23, "buyer105")), }, - expErr: "ask order 29 having price \"98789plum\" has negative price \"-1plum\" after filling \"98790plum\"", - }, - - { - name: "nil splits", - receiver: OrderFulfillment{ - Order: NewOrder(100).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: nil, - }, - expErr: "no splits applied to bid order 100", - }, - { - name: "empty splits", - receiver: OrderFulfillment{ - Order: NewOrder(101).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{}, - }, - expErr: "no splits applied to ask order 101", - }, - { - name: "multiple asset denoms in splits", - receiver: OrderFulfillment{ - Order: NewOrder(102).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "apple"), Price: coin(700, "plum")}, - {Assets: coin(5, "acai"), Price: coin(89, "plum")}, - }, + expAskOFs: []*orderFulfillment{ + newOF(askOrder(101, 15, "seller101"), dist("buyer103", 10), dist("buyer104", 5)), + newOF(askOrder(102, 25, "seller102"), dist("buyer104", 3), dist("buyer105", 22)), }, - expErr: "multiple asset denoms \"5acai,3apple\" in splits applied to bid order 102 having assets \"55apple\"", - }, - { - name: "wrong splits assets denom", - receiver: OrderFulfillment{ - Order: NewOrder(103).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "acai"), Price: coin(700, "plum")}, - {Assets: coin(5, "acai"), Price: coin(89, "plum")}, - }, + expBidOfs: []*orderFulfillment{ + newOF(bidOrder(103, 10, "buyer103"), dist("seller101", 10)), + newOF(bidOrder(104, 8, "buyer104"), dist("seller101", 5), dist("seller102", 3)), + newOF(bidOrder(105, 23, "buyer105"), dist("seller102", 22)), }, - expErr: "splits asset denom \"8acai\" does not equal order assets denom \"55apple\" on ask order 103", }, { - name: "splits assets total too low", - receiver: OrderFulfillment{ - Order: NewOrder(104).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "apple"), Price: coin(800, "plum")}, - {Assets: coin(6, "apple"), Price: coin(89, "plum")}, - }, - }, - expErr: "splits asset total \"9apple\" does not equal filled assets \"10apple\" on bid order 104", + name: "negative ask assets unfilled", + askOFs: []*orderFulfillment{newOF(askOrder(101, 10, "seller"), dist("buyerx", 11))}, + bidOFs: []*orderFulfillment{newOF(bidOrder(102, 10, "buyer"))}, + expErr: "cannot fill ask order 101 having assets left \"-1apple\" with bid order 102 having " + + "assets left \"10apple\": zero or negative assets left", }, { - name: "splits assets total too high", - receiver: OrderFulfillment{ - Order: NewOrder(105).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "apple"), Price: coin(700, "plum")}, - {Assets: coin(8, "apple"), Price: coin(89, "plum")}, - }, - }, - expErr: "splits asset total \"11apple\" does not equal filled assets \"10apple\" on ask order 105", + name: "negative bid assets unfilled", + askOFs: []*orderFulfillment{newOF(askOrder(101, 10, "seller"))}, + bidOFs: []*orderFulfillment{newOF(bidOrder(102, 10, "buyer"), dist("sellerx", 11))}, + expErr: "cannot fill ask order 101 having assets left \"10apple\" with bid order 102 having " + + "assets left \"-1apple\": zero or negative assets left", }, - { - name: "multiple price denoms in splits", - receiver: OrderFulfillment{ - Order: NewOrder(106).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "apple"), Price: coin(700, "potato")}, - {Assets: coin(7, "apple"), Price: coin(89, "plum")}, - }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + origAskOFs := copyOrderFulfillments(tc.askOFs) + origBidOFs := copyOrderFulfillments(tc.bidOFs) + + var err error + testFunc := func() { + err = allocateAssets(tc.askOFs, tc.bidOFs) + } + require.NotPanics(t, testFunc, "allocateAssets") + assertions.AssertErrorValue(t, err, tc.expErr, "allocateAssets error") + if len(tc.expErr) > 0 { + return + } + if !assertEqualOrderFulfillmentSlices(t, tc.expAskOFs, tc.askOFs, "askOFs after allocateAssets") { + t.Logf("Original: %s", orderFulfillmentsString(origAskOFs)) + } + if !assertEqualOrderFulfillmentSlices(t, tc.expBidOfs, tc.bidOFs, "bidOFs after allocateAssets") { + t.Logf("Original: %s", orderFulfillmentsString(origBidOFs)) + } + }) + } +} + +func TestGetFulfillmentAssetsAmt(t *testing.T) { + newAskOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *orderFulfillment { + return &orderFulfillment{ + Order: NewOrder(orderID).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin(assetDenom, 999), + }), + AssetsUnfilledAmt: sdkmath.NewInt(assetsUnfilled), + } + } + newBidOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *orderFulfillment { + return &orderFulfillment{ + Order: NewOrder(orderID).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin(assetDenom, 999), + }), + AssetsUnfilledAmt: sdkmath.NewInt(assetsUnfilled), + } + } + + cases := []struct { + name string + of1Unfilled int64 + of2Unfilled int64 + expAmt int64 + }{ + {name: "of1 zero", of1Unfilled: 0, of2Unfilled: 3, expAmt: 0}, + {name: "of1 negative", of1Unfilled: -4, of2Unfilled: 3, expAmt: 0}, + {name: "of2 zero", of1Unfilled: 5, of2Unfilled: 0, expAmt: 0}, + {name: "of2 negative", of1Unfilled: 5, of2Unfilled: -6, expAmt: 0}, + {name: "equal", of1Unfilled: 8, of2Unfilled: 8, expAmt: 8}, + {name: "of1 has fewer", of1Unfilled: 9, of2Unfilled: 10, expAmt: 9}, + {name: "of2 has fewer", of1Unfilled: 12, of2Unfilled: 11, expAmt: 11}, + } + + type testCase struct { + name string + of1 *orderFulfillment + of2 *orderFulfillment + expAmt sdkmath.Int + expErr string + } + + tests := make([]testCase, 0, len(cases)*4) + + for _, c := range cases { + newTests := []testCase{ + { + name: "ask bid " + c.name, + of1: newAskOF(1, c.of1Unfilled, "one"), + of2: newBidOF(2, c.of2Unfilled, "two"), + expAmt: sdkmath.NewInt(c.expAmt), }, - expErr: "multiple price denoms \"89plum,700potato\" in splits applied to bid order 106 having price \"98789plum\"", - }, - { - name: "wrong splits price denom", - receiver: OrderFulfillment{ - Order: NewOrder(107).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "apple"), Price: coin(700, "potato")}, - {Assets: coin(7, "apple"), Price: coin(89, "potato")}, - }, + { + name: "bid ask " + c.name, + of1: newBidOF(1, c.of1Unfilled, "one"), + of2: newAskOF(2, c.of2Unfilled, "two"), + expAmt: sdkmath.NewInt(c.expAmt), }, - expErr: "splits price denom \"789potato\" does not equal order price denom \"98789plum\" on ask order 107", - }, - { - name: "splits price total too low", - receiver: OrderFulfillment{ - Order: NewOrder(108).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "apple"), Price: coin(700, "plum")}, - {Assets: coin(7, "apple"), Price: coin(88, "plum")}, - }, + { + name: "ask ask " + c.name, + of1: newAskOF(1, c.of1Unfilled, "one"), + of2: newAskOF(2, c.of2Unfilled, "two"), + expAmt: sdkmath.NewInt(c.expAmt), }, - expErr: "splits price total \"788plum\" does not equal filled price \"789plum\" on bid order 108", - }, - { - name: "splits price total too high", - receiver: OrderFulfillment{ - Order: NewOrder(109).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "apple"), Price: coin(701, "plum")}, - {Assets: coin(7, "apple"), Price: coin(89, "plum")}, - }, + { + name: "bid bid " + c.name, + of1: newBidOF(1, c.of1Unfilled, "one"), + of2: newBidOF(2, c.of2Unfilled, "two"), + expAmt: sdkmath.NewInt(c.expAmt), }, - expErr: "splits price total \"790plum\" does not equal filled price \"789plum\" on ask order 109", + } + if c.expAmt == 0 { + newTests[0].expErr = fmt.Sprintf("cannot fill ask order 1 having assets left \"%done\" with bid "+ + "order 2 having assets left \"%dtwo\": zero or negative assets left", + c.of1Unfilled, c.of2Unfilled) + newTests[1].expErr = fmt.Sprintf("cannot fill bid order 1 having assets left \"%done\" with ask "+ + "order 2 having assets left \"%dtwo\": zero or negative assets left", + c.of1Unfilled, c.of2Unfilled) + newTests[2].expErr = fmt.Sprintf("cannot fill ask order 1 having assets left \"%done\" with ask "+ + "order 2 having assets left \"%dtwo\": zero or negative assets left", + c.of1Unfilled, c.of2Unfilled) + newTests[3].expErr = fmt.Sprintf("cannot fill bid order 1 having assets left \"%done\" with bid "+ + "order 2 having assets left \"%dtwo\": zero or negative assets left", + c.of1Unfilled, c.of2Unfilled) + } + tests = append(tests, newTests...) + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if len(tc.expErr) > 0 { + tc.expAmt = sdkmath.ZeroInt() + } + origOF1 := copyOrderFulfillment(tc.of1) + origOF2 := copyOrderFulfillment(tc.of2) + + var amt sdkmath.Int + var err error + testFunc := func() { + amt, err = getFulfillmentAssetsAmt(tc.of1, tc.of2) + } + require.NotPanics(t, testFunc, "getFulfillmentAssetsAmt") + assertions.AssertErrorValue(t, err, tc.expErr, "getFulfillmentAssetsAmt error") + assert.Equal(t, tc.expAmt, amt, "getFulfillmentAssetsAmt amount") + assertEqualOrderFulfillments(t, origOF1, tc.of1, "of1 after getFulfillmentAssetsAmt") + assertEqualOrderFulfillments(t, origOF2, tc.of2, "of2 after getFulfillmentAssetsAmt") + }) + } +} + +func TestSplitPartial(t *testing.T) { + askOrder := func(orderID uint64, assetsAmt int64, seller string) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + Seller: seller, + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, + Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(assetsAmt)}, + AllowPartial: true, + }) + } + bidOrder := func(orderID uint64, assetsAmt int64, buyer string) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + Buyer: buyer, + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, + Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(assetsAmt)}, + AllowPartial: true, + }) + } + newOF := func(order *Order, dists ...*distribution) *orderFulfillment { + rv := &orderFulfillment{ + Order: order, + AssetsFilledAmt: sdkmath.NewInt(0), + AssetsUnfilledAmt: order.GetAssets().Amount, + PriceAppliedAmt: sdkmath.NewInt(0), + PriceLeftAmt: order.GetPrice().Amount, + } + if len(dists) > 0 { + rv.AssetDists = dists + for _, d := range dists { + rv.AssetsFilledAmt = rv.AssetsFilledAmt.Add(d.Amount) + rv.AssetsUnfilledAmt = rv.AssetsUnfilledAmt.Sub(d.Amount) + } + if rv.AssetsUnfilledAmt.IsZero() { + rv.AssetsUnfilledAmt = sdkmath.NewInt(0) + } + } + return rv + } + dist := func(addr string, amount int64) *distribution { + return &distribution{Address: addr, Amount: sdkmath.NewInt(amount)} + } + + tests := []struct { + name string + askOFs []*orderFulfillment + bidOFs []*orderFulfillment + settlement *Settlement + expAskOFs []*orderFulfillment + expBidOfs []*orderFulfillment + expSettlement *Settlement + expErr string + }{ + { + name: "one ask: not touched", + askOFs: []*orderFulfillment{newOF(askOrder(8, 10, "seller8"))}, + settlement: &Settlement{}, + expErr: "ask order 8 (at index 0) has no assets filled", }, - { - name: "order fees left has negative", - receiver: OrderFulfillment{ - Order: NewOrder(201).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - SellerSettlementFlatFee: coinP(5, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "apple"), Price: coin(700, "plum")}, - {Assets: coin(7, "apple"), Price: coin(89, "plum")}, - }, - OrderFeesLeft: sdk.Coins{coin(2, "fig"), coin(-3, "grape"), coin(4, "honeydew")}, - }, - expErr: "settlement fees left \"2fig,-3grape,4honeydew\" is negative for ask order 201 having fees \"5fig\"", + name: "one ask: partial", + askOFs: []*orderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer", 7))}, + settlement: &Settlement{}, + expAskOFs: []*orderFulfillment{newOF(askOrder(8, 7, "seller8"), dist("buyer", 7))}, + expSettlement: &Settlement{PartialOrderLeft: askOrder(8, 3, "seller8")}, }, { - name: "more fees left than in ask order", - receiver: OrderFulfillment{ - Order: NewOrder(202).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - SellerSettlementFlatFee: coinP(5, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "apple"), Price: coin(700, "plum")}, - {Assets: coin(7, "apple"), Price: coin(89, "plum")}, - }, - OrderFeesLeft: coins(6, "fig"), - }, - expErr: "settlement fees left \"6fig\" is greater than ask order 202 settlement fees \"5fig\"", + name: "one ask: partial, settlement already has a partial", + askOFs: []*orderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer", 7))}, + settlement: &Settlement{PartialOrderLeft: bidOrder(55, 3, "buyer")}, + expErr: "bid order 55 and ask order 8 cannot both be partially filled", }, { - name: "fees left in ask order without fees", - receiver: OrderFulfillment{ - Order: NewOrder(203).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - SellerSettlementFlatFee: nil, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "apple"), Price: coin(700, "plum")}, - {Assets: coin(7, "apple"), Price: coin(89, "plum")}, - }, - OrderFeesLeft: coins(1, "fig"), - }, - expErr: "settlement fees left \"1fig\" is greater than ask order 203 settlement fees \"\"", + name: "one ask: partial, not allowed", + askOFs: []*orderFulfillment{ + newOF(NewOrder(8).WithAsk(&AskOrder{ + Seller: "seller8", + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(10)}, + Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(10)}, + AllowPartial: false, + }), dist("buyer", 7))}, + settlement: &Settlement{}, + expErr: "cannot split ask order 8 having assets \"10apple\" at \"7apple\": order does not allow partial fulfillment", }, { - name: "more fees left than in bid order", - receiver: OrderFulfillment{ - Order: NewOrder(204).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - BuyerSettlementFees: sdk.Coins{coin(5, "fig"), coin(6, "grape")}, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "apple"), Price: coin(700, "plum")}, - {Assets: coin(7, "apple"), Price: coin(89, "plum")}, - }, - OrderFeesLeft: coins(6, "fig"), + name: "two asks: first partial", + askOFs: []*orderFulfillment{ + newOF(askOrder(8, 10, "seller8"), dist("buyer", 7)), + newOF(askOrder(9, 12, "seller8")), }, - expErr: "settlement fees left \"6fig\" is greater than bid order 204 settlement fees \"5fig,6grape\"", + settlement: &Settlement{}, + expErr: "ask order 8 (at index 0) is not filled in full and is not the last ask order provided", }, { - name: "fees left in bid order without fees", - receiver: OrderFulfillment{ - Order: NewOrder(205).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - BuyerSettlementFees: nil, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(45), - AssetsFilledAmt: sdkmath.NewInt(10), - PriceLeftAmt: sdkmath.NewInt(98000), - PriceAppliedAmt: sdkmath.NewInt(789), - PriceUnfilledAmt: sdkmath.NewInt(90000), - PriceFilledAmt: sdkmath.NewInt(8789), - Splits: []*OrderSplit{ - {Assets: coin(3, "apple"), Price: coin(700, "plum")}, - {Assets: coin(7, "apple"), Price: coin(89, "plum")}, - }, - OrderFeesLeft: coins(1, "fig"), + name: "two asks: last untouched", + askOFs: []*orderFulfillment{ + newOF(askOrder(8, 10, "seller8"), dist("buyer", 10)), + newOF(askOrder(9, 12, "seller8")), }, - expErr: "settlement fees left \"1fig\" is greater than bid order 205 settlement fees \"\"", + settlement: &Settlement{}, + expErr: "ask order 9 (at index 1) has no assets filled", }, - { - name: "fully filled, price unfilled positive", - receiver: OrderFulfillment{ - Order: NewOrder(250).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(0), - AssetsFilledAmt: sdkmath.NewInt(55), - PriceLeftAmt: sdkmath.NewInt(789), - PriceAppliedAmt: sdkmath.NewInt(98000), - PriceUnfilledAmt: sdkmath.NewInt(788), - PriceFilledAmt: sdkmath.NewInt(98001), - Splits: []*OrderSplit{{Assets: coin(55, "apple"), Price: coin(98000, "plum")}}, - }, - expErr: "fully filled ask order 250 has non-zero unfilled price \"788plum\"", - }, - { - name: "fully filled, order fees left positive", - receiver: OrderFulfillment{ - Order: NewOrder(252).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - SellerSettlementFlatFee: coinP(5, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(0), - AssetsFilledAmt: sdkmath.NewInt(55), - PriceLeftAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(98789), - PriceUnfilledAmt: sdkmath.NewInt(0), - PriceFilledAmt: sdkmath.NewInt(98789), - Splits: []*OrderSplit{{Assets: coin(55, "apple"), Price: coin(98789, "plum")}}, - OrderFeesLeft: coins(1, "fig"), + name: "two asks: last partial", + askOFs: []*orderFulfillment{ + newOF(askOrder(8, 10, "seller8"), dist("buyer", 10)), + newOF(askOrder(9, 12, "seller9"), dist("buyer", 10)), + }, + settlement: &Settlement{}, + expAskOFs: []*orderFulfillment{ + newOF(askOrder(8, 10, "seller8"), dist("buyer", 10)), + newOF(askOrder(9, 10, "seller9"), dist("buyer", 10)), }, - expErr: "fully filled ask order 252 has non-zero settlement fees left \"1fig\"", + expSettlement: &Settlement{PartialOrderLeft: askOrder(9, 2, "seller9")}, }, { - name: "ask order, price applied less than filled", - receiver: OrderFulfillment{ - Order: NewOrder(301).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(45), - PriceLeftAmt: sdkmath.NewInt(8789), - PriceAppliedAmt: sdkmath.NewInt(90000), - PriceUnfilledAmt: sdkmath.NewInt(789), - PriceFilledAmt: sdkmath.NewInt(98000), - Splits: []*OrderSplit{{Assets: coin(45, "apple"), Price: coin(90000, "plum")}}, - }, - expErr: "ask order 301 having assets \"55apple\" and price \"98789plum\" cannot be filled by \"45apple\" at price \"90000plum\": insufficient price", - }, - { - name: "ask order, partial, multiple fees left denoms", - receiver: OrderFulfillment{ - Order: NewOrder(302).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - SellerSettlementFlatFee: coinP(3, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(45), - PriceLeftAmt: sdkmath.NewInt(789), - PriceAppliedAmt: sdkmath.NewInt(98000), - PriceUnfilledAmt: sdkmath.NewInt(8789), - PriceFilledAmt: sdkmath.NewInt(90000), - Splits: []*OrderSplit{{Assets: coin(45, "apple"), Price: coin(98000, "plum")}}, - OrderFeesLeft: sdk.Coins{coin(1, "fig"), coin(0, "grape")}, - }, - expErr: "partial fulfillment for ask order 302 having seller settlement fees \"3fig\" has multiple denoms in fees left \"1fig,0grape\"", - }, - { - name: "ask order, tracked fees less than order fees", - receiver: OrderFulfillment{ - Order: NewOrder(303).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - SellerSettlementFlatFee: coinP(123, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(45), - PriceLeftAmt: sdkmath.NewInt(789), - PriceAppliedAmt: sdkmath.NewInt(98000), - PriceUnfilledAmt: sdkmath.NewInt(8789), - PriceFilledAmt: sdkmath.NewInt(90000), - Splits: []*OrderSplit{{Assets: coin(45, "apple"), Price: coin(98000, "plum")}}, - OrderFeesLeft: coins(22, "fig"), - FeesToPay: coins(100, "fig"), - }, - expErr: "tracked settlement fees \"122fig\" is less than ask order 303 settlement fees \"123fig\"", - }, - - { - name: "bid order, price applied less than price filled", - receiver: OrderFulfillment{ - Order: NewOrder(275).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(45), - PriceLeftAmt: sdkmath.NewInt(790), - PriceAppliedAmt: sdkmath.NewInt(97999), - PriceUnfilledAmt: sdkmath.NewInt(789), - PriceFilledAmt: sdkmath.NewInt(98000), - Splits: []*OrderSplit{{Assets: coin(45, "apple"), Price: coin(97999, "plum")}}, - }, - expErr: "price applied \"97999plum\" does not equal price filled \"98000plum\" for bid order 275 having price \"98789plum\"", - }, - { - name: "bid order, price applied more than price filled", - receiver: OrderFulfillment{ - Order: NewOrder(276).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(45), - PriceLeftAmt: sdkmath.NewInt(788), - PriceAppliedAmt: sdkmath.NewInt(98001), - PriceUnfilledAmt: sdkmath.NewInt(789), - PriceFilledAmt: sdkmath.NewInt(98000), - Splits: []*OrderSplit{{Assets: coin(45, "apple"), Price: coin(98001, "plum")}}, - }, - expErr: "price applied \"98001plum\" does not equal price filled \"98000plum\" for bid order 276 having price \"98789plum\"", - }, - { - name: "bid order, tracked fees less than order fees", - receiver: OrderFulfillment{ - Order: NewOrder(277).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - BuyerSettlementFees: coins(123, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(1), - AssetsFilledAmt: sdkmath.NewInt(54), - PriceLeftAmt: sdkmath.NewInt(89), - PriceAppliedAmt: sdkmath.NewInt(98700), - PriceUnfilledAmt: sdkmath.NewInt(89), - PriceFilledAmt: sdkmath.NewInt(98700), - Splits: []*OrderSplit{{Assets: coin(54, "apple"), Price: coin(98700, "plum")}}, - OrderFeesLeft: coins(2, "fig"), - FeesToPay: coins(120, "fig"), - }, - expErr: "tracked settlement fees \"122fig\" does not equal bid order 277 settlement fees \"123fig\"", - }, - { - name: "bid order, tracked fees more than order fees", - receiver: OrderFulfillment{ - Order: NewOrder(277).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - BuyerSettlementFees: coins(123, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(1), - AssetsFilledAmt: sdkmath.NewInt(54), - PriceLeftAmt: sdkmath.NewInt(89), - PriceAppliedAmt: sdkmath.NewInt(98700), - PriceUnfilledAmt: sdkmath.NewInt(89), - PriceFilledAmt: sdkmath.NewInt(98700), - Splits: []*OrderSplit{{Assets: coin(54, "apple"), Price: coin(98700, "plum")}}, - OrderFeesLeft: coins(4, "fig"), - FeesToPay: coins(120, "fig"), - }, - expErr: "tracked settlement fees \"124fig\" does not equal bid order 277 settlement fees \"123fig\"", + name: "one bid: not touched", + bidOFs: []*orderFulfillment{newOF(bidOrder(8, 10, "buyer8"))}, + settlement: &Settlement{}, + expErr: "bid order 8 (at index 0) has no assets filled", }, - { - name: "partial ask, but not allowed", - receiver: OrderFulfillment{ - Order: NewOrder(301).WithAsk(&AskOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), - AllowPartial: false, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(1), - AssetsFilledAmt: sdkmath.NewInt(54), - PriceLeftAmt: sdkmath.NewInt(89), - PriceAppliedAmt: sdkmath.NewInt(98700), - PriceUnfilledAmt: sdkmath.NewInt(89), - PriceFilledAmt: sdkmath.NewInt(98700), - Splits: []*OrderSplit{{Assets: coin(54, "apple"), Price: coin(98700, "plum")}}, - }, - expErr: "cannot fill ask order 301 having assets \"55apple\" with \"54apple\": order does not allow partial fill", + name: "one bid: partial", + bidOFs: []*orderFulfillment{newOF(bidOrder(8, 10, "buyer8"), dist("seller", 7))}, + settlement: &Settlement{}, + expBidOfs: []*orderFulfillment{newOF(bidOrder(8, 7, "buyer8"), dist("seller", 7))}, + expSettlement: &Settlement{PartialOrderLeft: bidOrder(8, 3, "buyer8")}, }, { - name: "partial bid, but not allowed", - receiver: OrderFulfillment{ - Order: NewOrder(302).WithBid(&BidOrder{ - Assets: coin(55, "apple"), - Price: coin(98789, "plum"), + name: "one bid: partial, not allowed", + askOFs: []*orderFulfillment{ + newOF(NewOrder(8).WithBid(&BidOrder{ + Buyer: "buyer8", + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(10)}, + Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(10)}, AllowPartial: false, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(1), - AssetsFilledAmt: sdkmath.NewInt(54), - PriceLeftAmt: sdkmath.NewInt(89), - PriceAppliedAmt: sdkmath.NewInt(98700), - PriceUnfilledAmt: sdkmath.NewInt(89), - PriceFilledAmt: sdkmath.NewInt(98700), - Splits: []*OrderSplit{{Assets: coin(54, "apple"), Price: coin(98700, "plum")}}, - }, - expErr: "cannot fill bid order 302 having assets \"55apple\" with \"54apple\": order does not allow partial fill", + }), dist("seller", 7))}, + settlement: &Settlement{}, + expErr: "cannot split bid order 8 having assets \"10apple\" at \"7apple\": order does not allow partial fulfillment", }, - { - name: "ask, fully filled, exact", - receiver: OrderFulfillment{ - Order: NewOrder(501).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(0), - AssetsFilledAmt: sdkmath.NewInt(50), - PriceLeftAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceUnfilledAmt: sdkmath.NewInt(0), - PriceFilledAmt: sdkmath.NewInt(100), - Splits: []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(100, "plum")}}, + name: "two bids: first partial", + bidOFs: []*orderFulfillment{ + newOF(bidOrder(8, 10, "buyer8"), dist("seller", 7)), + newOF(bidOrder(9, 12, "buyer9")), }, - expErr: "", + settlement: &Settlement{}, + expErr: "bid order 8 (at index 0) is not filled in full and is not the last bid order provided", }, { - name: "ask, fully filled, extra price", - receiver: OrderFulfillment{ - Order: NewOrder(502).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(0), - AssetsFilledAmt: sdkmath.NewInt(50), - PriceLeftAmt: sdkmath.NewInt(-5), - PriceAppliedAmt: sdkmath.NewInt(105), - PriceUnfilledAmt: sdkmath.NewInt(0), - PriceFilledAmt: sdkmath.NewInt(100), - Splits: []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(105, "plum")}}, + name: "two bids: last untouched", + bidOFs: []*orderFulfillment{ + newOF(bidOrder(8, 10, "buyer8"), dist("seller", 10)), + newOF(bidOrder(9, 12, "buyer9")), }, - expErr: "", + settlement: &Settlement{}, + expErr: "bid order 9 (at index 1) has no assets filled", }, { - name: "ask, partially filled, exact", - receiver: OrderFulfillment{ - Order: NewOrder(503).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceUnfilledAmt: sdkmath.NewInt(20), - PriceFilledAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, + name: "two bids: last partial", + bidOFs: []*orderFulfillment{ + newOF(bidOrder(8, 10, "buyer8"), dist("seller", 10)), + newOF(bidOrder(9, 12, "buyer9"), dist("seller", 10)), }, - expErr: "", - }, - { - name: "ask, partially filled, extra price", - receiver: OrderFulfillment{ - Order: NewOrder(504).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(10), - PriceAppliedAmt: sdkmath.NewInt(90), - PriceUnfilledAmt: sdkmath.NewInt(20), - PriceFilledAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(90, "plum")}}, + settlement: &Settlement{}, + expBidOfs: []*orderFulfillment{ + newOF(bidOrder(8, 10, "buyer8"), dist("seller", 10)), + newOF(bidOrder(9, 10, "buyer9"), dist("seller", 10)), }, - expErr: "", + expSettlement: &Settlement{PartialOrderLeft: bidOrder(9, 2, "buyer9")}, }, { - name: "bid, fully filled", - receiver: OrderFulfillment{ - Order: NewOrder(505).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(0), - AssetsFilledAmt: sdkmath.NewInt(50), - PriceLeftAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceUnfilledAmt: sdkmath.NewInt(0), - PriceFilledAmt: sdkmath.NewInt(100), - Splits: []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(100, "plum")}}, - }, - expErr: "", + name: "one ask, one bid: both partial", + askOFs: []*orderFulfillment{newOF(askOrder(8, 10, "seller8"), dist("buyer9", 7))}, + bidOFs: []*orderFulfillment{newOF(bidOrder(9, 10, "buyer9"), dist("seller8", 7))}, + settlement: &Settlement{}, + expErr: "ask order 8 and bid order 9 cannot both be partially filled", }, { - name: "bid, partially filled", - receiver: OrderFulfillment{ - Order: NewOrder(506).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceUnfilledAmt: sdkmath.NewInt(20), - PriceFilledAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, + name: "three asks, three bids: no partial", + askOFs: []*orderFulfillment{ + newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), + newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), + newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), }, - expErr: "", - }, - { - name: "ask, full, no fees, some to pay", - receiver: OrderFulfillment{ - Order: NewOrder(507).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - SellerSettlementFlatFee: nil, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(0), - AssetsFilledAmt: sdkmath.NewInt(50), - PriceLeftAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceUnfilledAmt: sdkmath.NewInt(0), - PriceFilledAmt: sdkmath.NewInt(100), - Splits: []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(100, "plum")}}, - OrderFeesLeft: nil, - FeesToPay: coins(20, "fig"), + bidOFs: []*orderFulfillment{ + newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), + newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), + newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), }, - expErr: "", + settlement: &Settlement{}, + expAskOFs: []*orderFulfillment{ + newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), + newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), + newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + }, + expBidOfs: []*orderFulfillment{ + newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), + newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), + newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), + }, + expSettlement: &Settlement{PartialOrderLeft: nil}, }, { - name: "ask, full, with fees, paying exact", - receiver: OrderFulfillment{ - Order: NewOrder(508).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - SellerSettlementFlatFee: coinP(200, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(0), - AssetsFilledAmt: sdkmath.NewInt(50), - PriceLeftAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceUnfilledAmt: sdkmath.NewInt(0), - PriceFilledAmt: sdkmath.NewInt(100), - Splits: []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(100, "plum")}}, - OrderFeesLeft: nil, - FeesToPay: coins(200, "fig"), + name: "three asks, three bids: partial ask", + askOFs: []*orderFulfillment{ + newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), + newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), + newOF(askOrder(12, 21, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), }, - expErr: "", + bidOFs: []*orderFulfillment{ + newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), + newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), + newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), + }, + settlement: &Settlement{}, + expAskOFs: []*orderFulfillment{ + newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), + newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), + newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + }, + expBidOfs: []*orderFulfillment{ + newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), + newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), + newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), + }, + expSettlement: &Settlement{PartialOrderLeft: askOrder(12, 1, "seller12")}, }, { - name: "ask, full, with fees, paying more", - receiver: OrderFulfillment{ - Order: NewOrder(509).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - SellerSettlementFlatFee: coinP(200, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(0), - AssetsFilledAmt: sdkmath.NewInt(50), - PriceLeftAmt: sdkmath.NewInt(0), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceUnfilledAmt: sdkmath.NewInt(0), - PriceFilledAmt: sdkmath.NewInt(100), - Splits: []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(100, "plum")}}, - OrderFeesLeft: nil, - FeesToPay: coins(205, "fig"), + name: "three asks, three bids: no partial", + askOFs: []*orderFulfillment{ + newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), + newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), + newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), }, - expErr: "", + bidOFs: []*orderFulfillment{ + newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), + newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), + newOF(bidOrder(112, 11, "buyer112"), dist("seller12", 10)), + }, + settlement: &Settlement{}, + expAskOFs: []*orderFulfillment{ + newOF(askOrder(51, 10, "seller51"), dist("buyer99", 10)), + newOF(askOrder(77, 15, "seller77"), dist("buyer99", 10), dist("buyer8", 5)), + newOF(askOrder(12, 20, "seller12"), dist("buyer8", 10), dist("buyer112", 10)), + }, + expBidOfs: []*orderFulfillment{ + newOF(bidOrder(99, 20, "buyer99"), dist("seller51", 10), dist("seller77", 10)), + newOF(bidOrder(8, 15, "buyer8"), dist("seller77", 5), dist("seller12", 10)), + newOF(bidOrder(112, 10, "buyer112"), dist("seller12", 10)), + }, + expSettlement: &Settlement{PartialOrderLeft: bidOrder(112, 1, "buyer112")}, }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + origAskOFs := copyOrderFulfillments(tc.askOFs) + origBidOFs := copyOrderFulfillments(tc.bidOFs) + + var err error + testFunc := func() { + err = splitPartial(tc.askOFs, tc.bidOFs, tc.settlement) + } + require.NotPanics(t, testFunc, "splitPartial") + assertions.AssertErrorValue(t, err, tc.expErr, "splitPartial error") + if len(tc.expErr) > 0 { + return + } + if !assertEqualOrderFulfillmentSlices(t, tc.expAskOFs, tc.askOFs, "askOFs after splitPartial") { + t.Logf("Original: %s", orderFulfillmentsString(origAskOFs)) + } + if !assertEqualOrderFulfillmentSlices(t, tc.expBidOfs, tc.bidOFs, "bidOFs after splitPartial") { + t.Logf("Original: %s", orderFulfillmentsString(origBidOFs)) + } + assert.Equalf(t, tc.expSettlement, tc.settlement, "settlement after splitPartial") + }) + } +} + +func TestSplitOrderFulfillments(t *testing.T) { + acoin := func(amount int64) sdk.Coin { + return sdk.NewInt64Coin("acorn", amount) + } + pcoin := func(amount int64) sdk.Coin { + return sdk.NewInt64Coin("prune", amount) + } + askOrder := func(orderID uint64, assetsAmt int64, allowPartial bool) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + MarketId: 123, + Seller: "sEllEr", + Assets: acoin(assetsAmt), + Price: pcoin(assetsAmt), + AllowPartial: allowPartial, + }) + } + bidOrder := func(orderID uint64, assetsAmt int64, allowPartial bool) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + MarketId: 123, + Buyer: "bUyEr", + Assets: acoin(assetsAmt), + Price: pcoin(assetsAmt), + AllowPartial: allowPartial, + }) + } + newOF := func(order *Order, assetsFilledAmt int64) *orderFulfillment { + rv := &orderFulfillment{ + Order: order, + AssetsFilledAmt: sdkmath.NewInt(assetsFilledAmt), + AssetsUnfilledAmt: order.GetAssets().Amount.SubRaw(assetsFilledAmt), + PriceAppliedAmt: sdkmath.NewInt(assetsFilledAmt), + PriceLeftAmt: order.GetPrice().Amount.SubRaw(assetsFilledAmt), + } + // int(x).Sub(x) results in an object that is not .Equal to ZeroInt(). + // The Split function sets this to ZeroInt(). + if rv.AssetsUnfilledAmt.IsZero() { + rv.AssetsUnfilledAmt = sdkmath.ZeroInt() + } + return rv + } + + tests := []struct { + name string + fulfillments []*orderFulfillment + settlement *Settlement + expFulfillments []*orderFulfillment + expSettlement *Settlement + expErr string + }{ { - name: "ask, partial, no fees, some to pay", - receiver: OrderFulfillment{ - Order: NewOrder(510).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - SellerSettlementFlatFee: nil, - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceUnfilledAmt: sdkmath.NewInt(20), - PriceFilledAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, - OrderFeesLeft: nil, - FeesToPay: coins(20, "fig"), - }, - expErr: "", + name: "one order, ask: nothing filled", + fulfillments: []*orderFulfillment{newOF(askOrder(8, 53, false), 0)}, + settlement: &Settlement{}, + expErr: "ask order 8 (at index 0) has no assets filled", }, { - name: "ask, partial, with fees, paying exact", - receiver: OrderFulfillment{ - Order: NewOrder(511).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - SellerSettlementFlatFee: coinP(20, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceUnfilledAmt: sdkmath.NewInt(20), - PriceFilledAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, - OrderFeesLeft: nil, - FeesToPay: coins(20, "fig"), - }, - expErr: "", + name: "one order, bid: nothing filled", + fulfillments: []*orderFulfillment{newOF(bidOrder(8, 53, false), 0)}, + settlement: &Settlement{}, + expErr: "bid order 8 (at index 0) has no assets filled", }, { - name: "ask, partial, with fees, paying more", - receiver: OrderFulfillment{ - Order: NewOrder(512).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - SellerSettlementFlatFee: coinP(20, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceUnfilledAmt: sdkmath.NewInt(20), - PriceFilledAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, - OrderFeesLeft: coins(4, "fig"), - FeesToPay: coins(55, "fig"), - }, - expErr: "", + name: "one order, ask: partially filled", + fulfillments: []*orderFulfillment{newOF(askOrder(8, 53, true), 13)}, + settlement: &Settlement{}, + expFulfillments: []*orderFulfillment{newOF(askOrder(8, 13, true), 13)}, + expSettlement: &Settlement{PartialOrderLeft: askOrder(8, 40, true)}, }, { - name: "ask, partial, with fees, none being paid", - receiver: OrderFulfillment{ - Order: NewOrder(513).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - SellerSettlementFlatFee: coinP(200, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceUnfilledAmt: sdkmath.NewInt(20), - PriceFilledAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, - OrderFeesLeft: coins(200, "fig"), - FeesToPay: nil, - }, - expErr: "", + name: "one order, bid: partially filled", + fulfillments: []*orderFulfillment{newOF(bidOrder(8, 53, true), 13)}, + settlement: &Settlement{}, + expFulfillments: []*orderFulfillment{newOF(bidOrder(8, 13, true), 13)}, + expSettlement: &Settlement{PartialOrderLeft: bidOrder(8, 40, true)}, }, { - name: "bid, partial, with fees, none being paid", - receiver: OrderFulfillment{ - Order: NewOrder(514).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - BuyerSettlementFees: coins(20, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceUnfilledAmt: sdkmath.NewInt(20), - PriceFilledAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, - OrderFeesLeft: coins(20, "fig"), - FeesToPay: nil, - }, - expErr: "", + name: "one order, ask: partially filled, already have a partially filled", + fulfillments: []*orderFulfillment{newOF(askOrder(8, 53, true), 13)}, + settlement: &Settlement{PartialOrderLeft: bidOrder(951, 357, true)}, + expErr: "bid order 951 and ask order 8 cannot both be partially filled", }, { - name: "bid, partial, with fees, all being paid", - receiver: OrderFulfillment{ - Order: NewOrder(515).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - BuyerSettlementFees: coins(20, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceUnfilledAmt: sdkmath.NewInt(20), - PriceFilledAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, - OrderFeesLeft: nil, - FeesToPay: coins(20, "fig"), - }, - expErr: "", + name: "one order, bid: partially filled, already have a partially filled", + fulfillments: []*orderFulfillment{newOF(bidOrder(8, 53, true), 13)}, + settlement: &Settlement{PartialOrderLeft: askOrder(951, 357, true)}, + expErr: "ask order 951 and bid order 8 cannot both be partially filled", }, { - name: "bid, partial, with fees, some being paid", - receiver: OrderFulfillment{ - Order: NewOrder(515).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - AllowPartial: true, - BuyerSettlementFees: coins(20, "fig"), - }), - IsFinalized: true, - AssetsUnfilledAmt: sdkmath.NewInt(10), - AssetsFilledAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceUnfilledAmt: sdkmath.NewInt(20), - PriceFilledAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{{Assets: coin(40, "apple"), Price: coin(80, "plum")}}, - OrderFeesLeft: coins(4, "fig"), - FeesToPay: coins(16, "fig"), - }, - expErr: "", + name: "one order, ask: partially filled, split not allowed", + fulfillments: []*orderFulfillment{newOF(askOrder(8, 53, false), 13)}, + settlement: &Settlement{}, + expErr: "cannot split ask order 8 having assets \"53acorn\" at \"13acorn\": order does not allow partial fulfillment", }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var err error - testFunc := func() { - err = tc.receiver.Validate2() - } - require.NotPanics(t, testFunc, "Validate2") - assertions.AssertErrorValue(t, err, tc.expErr, "Validate2 error") - }) - } -} - -func TestFulfill(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} - } - - tests := []struct { - name string - ofA *OrderFulfillment - ofB *OrderFulfillment - expA *OrderFulfillment - expB *OrderFulfillment - expErr string - swapErr string - }{ { - name: "ask ask", - ofA: &OrderFulfillment{Order: NewOrder(1).WithAsk(&AskOrder{})}, - ofB: &OrderFulfillment{Order: NewOrder(2).WithAsk(&AskOrder{})}, - expErr: "cannot fulfill ask order 1 with ask order 2: order type mismatch", - swapErr: "cannot fulfill ask order 2 with ask order 1: order type mismatch", + name: "one order, bid: partially filled, split not allowed", + fulfillments: []*orderFulfillment{newOF(bidOrder(8, 53, false), 13)}, + settlement: &Settlement{}, + expErr: "cannot split bid order 8 having assets \"53acorn\" at \"13acorn\": order does not allow partial fulfillment", }, { - name: "bid bid", - ofA: &OrderFulfillment{Order: NewOrder(4).WithBid(&BidOrder{})}, - ofB: &OrderFulfillment{Order: NewOrder(3).WithBid(&BidOrder{})}, - expErr: "cannot fulfill bid order 4 with bid order 3: order type mismatch", - swapErr: "cannot fulfill bid order 3 with bid order 4: order type mismatch", + name: "one order, ask: fully filled", + fulfillments: []*orderFulfillment{newOF(askOrder(8, 53, false), 53)}, + settlement: &Settlement{}, + expFulfillments: []*orderFulfillment{newOF(askOrder(8, 53, false), 53)}, + expSettlement: &Settlement{}, }, { - name: "diff asset denom", - ofA: &OrderFulfillment{Order: NewOrder(5).WithAsk(&AskOrder{Assets: coin(15, "apple")})}, - ofB: &OrderFulfillment{Order: NewOrder(6).WithBid(&BidOrder{Assets: coin(16, "banana")})}, - expErr: "cannot fill bid order 6 having assets \"16banana\" with ask order 5 having assets \"15apple\": denom mismatch", + name: "one order, bid: fully filled", + fulfillments: []*orderFulfillment{newOF(bidOrder(8, 53, false), 53)}, + settlement: &Settlement{}, + expFulfillments: []*orderFulfillment{newOF(bidOrder(8, 53, false), 53)}, + expSettlement: &Settlement{}, }, { - name: "diff price denom", - ofA: &OrderFulfillment{ - Order: NewOrder(7).WithAsk(&AskOrder{ - Assets: coin(15, "apple"), - Price: coin(17, "pear"), - }), - }, - ofB: &OrderFulfillment{ - Order: NewOrder(8).WithBid(&BidOrder{ - Assets: coin(16, "apple"), - Price: coin(18, "plum"), - }), - }, - expErr: "cannot fill ask order 7 having price \"17pear\" with bid order 8 having price \"18plum\": denom mismatch", + name: "one order, ask: fully filled, already have a partially filled", + fulfillments: []*orderFulfillment{newOF(askOrder(8, 53, false), 53)}, + settlement: &Settlement{PartialOrderLeft: bidOrder(951, 357, true)}, + expFulfillments: []*orderFulfillment{newOF(askOrder(8, 53, false), 53)}, + expSettlement: &Settlement{PartialOrderLeft: bidOrder(951, 357, true)}, }, { - name: "cannot get assets left", - ofA: &OrderFulfillment{ - Order: NewOrder(9).WithAsk(&AskOrder{ - Assets: coin(15, "apple"), - Price: coin(16, "plum"), - }), - AssetsUnfilledAmt: sdkmath.NewInt(-1), - }, - ofB: &OrderFulfillment{ - Order: NewOrder(10).WithBid(&BidOrder{ - Assets: coin(17, "apple"), - Price: coin(18, "plum"), - }), - AssetsUnfilledAmt: sdkmath.NewInt(3), - }, - expErr: "cannot fill ask order 9 having assets left \"-1apple\" with bid order 10 " + - "having assets left \"3apple\": zero or negative assets left", + name: "one order, bid: fully filled, already have a partially filled", + fulfillments: []*orderFulfillment{newOF(bidOrder(8, 53, false), 53)}, + settlement: &Settlement{PartialOrderLeft: askOrder(951, 357, true)}, + expFulfillments: []*orderFulfillment{newOF(bidOrder(8, 53, false), 53)}, + expSettlement: &Settlement{PartialOrderLeft: askOrder(951, 357, true)}, }, { - name: "error from apply", - ofA: &OrderFulfillment{ - Order: NewOrder(11).WithAsk(&AskOrder{ - Assets: coin(15, "apple"), - Price: coin(90, "plum"), - }), - AssetsUnfilledAmt: sdkmath.NewInt(15), - AssetsFilledAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(90), - PriceAppliedAmt: sdkmath.NewInt(0), - }, - ofB: &OrderFulfillment{ - Order: NewOrder(12).WithBid(&BidOrder{ - Assets: coin(30, "apple"), - Price: coin(180, "plum"), - }), - AssetsUnfilledAmt: sdkmath.NewInt(30), - AssetsFilledAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(89), - PriceAppliedAmt: sdkmath.NewInt(91), + name: "three orders, ask: second partially filled", + fulfillments: []*orderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, true), 16), + newOF(askOrder(10, 200, false), 0), }, - expErr: "cannot fill bid order 12 having price left \"89plum\" to ask order 11 at a price of \"90plum\": overfill", + settlement: &Settlement{}, + expErr: "ask order 9 (at index 1) is not filled in full and is not the last ask order provided", }, { - name: "both filled in full", - ofA: &OrderFulfillment{ - Order: NewOrder(101).WithAsk(&AskOrder{ - Assets: coin(33, "apple"), - Price: coin(57, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: sdkmath.NewInt(33), - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(57), - }, - ofB: &OrderFulfillment{ - Order: NewOrder(102).WithBid(&BidOrder{ - Assets: coin(33, "apple"), - Price: coin(57, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: sdkmath.NewInt(33), - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(57), - }, - expA: &OrderFulfillment{ - Order: NewOrder(101).WithAsk(&AskOrder{ - Assets: coin(33, "apple"), - Price: coin(57, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(33), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(57), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{{Assets: coin(33, "apple"), Price: coin(57, "plum")}}, - }, - expB: &OrderFulfillment{ - Order: NewOrder(102).WithBid(&BidOrder{ - Assets: coin(33, "apple"), - Price: coin(57, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(33), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(57), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{{Assets: coin(33, "apple"), Price: coin(57, "plum")}}, + name: "three orders, bid: second partially filled", + fulfillments: []*orderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, true), 16), + newOF(bidOrder(10, 200, false), 0), }, + settlement: &Settlement{}, + expErr: "bid order 9 (at index 1) is not filled in full and is not the last bid order provided", }, { - name: "ask, unfilled, gets partially filled", - ofA: &OrderFulfillment{ - Order: NewOrder(103).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: sdkmath.NewInt(50), - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(100), - }, - ofB: &OrderFulfillment{ - Order: NewOrder(104).WithBid(&BidOrder{ - Assets: coin(20, "apple"), - Price: coin(80, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(80), - }, - expA: &OrderFulfillment{ - Order: NewOrder(103).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(30), - PriceAppliedAmt: sdkmath.NewInt(80), - PriceLeftAmt: sdkmath.NewInt(20), - Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(80, "plum")}}, - }, - expB: &OrderFulfillment{ - Order: NewOrder(104).WithBid(&BidOrder{ - Assets: coin(20, "apple"), - Price: coin(80, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(80), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(80, "plum")}}, + name: "three orders, ask: last not touched", + fulfillments: []*orderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 200, false), 0), }, + settlement: &Settlement{}, + expErr: "ask order 10 (at index 2) has no assets filled", }, { - name: "ask, partially filled, gets partially filled more", - ofA: &OrderFulfillment{ - Order: NewOrder(105).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(1), - AssetsUnfilledAmt: sdkmath.NewInt(49), - PriceAppliedAmt: sdkmath.NewInt(2), - PriceLeftAmt: sdkmath.NewInt(98), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(88).WithBid(&BidOrder{})}, - Assets: coin(1, "apple"), - Price: coin(2, "plum"), - }, - }, - }, - ofB: &OrderFulfillment{ - Order: NewOrder(106).WithBid(&BidOrder{ - Assets: coin(20, "apple"), - Price: coin(80, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(80), - }, - expA: &OrderFulfillment{ - Order: NewOrder(105).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(21), - AssetsUnfilledAmt: sdkmath.NewInt(29), - PriceAppliedAmt: sdkmath.NewInt(82), - PriceLeftAmt: sdkmath.NewInt(18), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(88).WithBid(&BidOrder{})}, - Assets: coin(1, "apple"), - Price: coin(2, "plum"), - }, - {Assets: coin(20, "apple"), Price: coin(80, "plum")}, - }, - }, - expB: &OrderFulfillment{ - Order: NewOrder(106).WithBid(&BidOrder{ - Assets: coin(20, "apple"), - Price: coin(80, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(80), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(80, "plum")}}, + name: "three orders, bid: last not touched", + fulfillments: []*orderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 200, true), 0), }, + settlement: &Settlement{}, + expErr: "bid order 10 (at index 2) has no assets filled", }, { - name: "ask, partially filled, gets fully filled", - ofA: &OrderFulfillment{ - Order: NewOrder(107).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(30), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(20), - PriceLeftAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(86).WithBid(&BidOrder{})}, - Assets: coin(30, "apple"), - Price: coin(20, "plum"), - }, - }, - }, - ofB: &OrderFulfillment{ - Order: NewOrder(108).WithBid(&BidOrder{ - Assets: coin(20, "apple"), - Price: coin(80, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(80), - }, - expA: &OrderFulfillment{ - Order: NewOrder(107).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(86).WithBid(&BidOrder{})}, - Assets: coin(30, "apple"), - Price: coin(20, "plum"), - }, - {Assets: coin(20, "apple"), Price: coin(80, "plum")}, - }, + name: "three orders, ask: last partially filled", + fulfillments: []*orderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 200, true), 183), }, - expB: &OrderFulfillment{ - Order: NewOrder(108).WithBid(&BidOrder{ - Assets: coin(20, "apple"), - Price: coin(80, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(80), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(80, "plum")}}, + settlement: &Settlement{}, + expFulfillments: []*orderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 183, true), 183), }, + expSettlement: &Settlement{PartialOrderLeft: askOrder(10, 17, true)}, }, { - name: "bid, unfilled, gets partially filled", - ofA: &OrderFulfillment{ - Order: NewOrder(151).WithAsk(&AskOrder{ - Assets: coin(20, "apple"), - Price: coin(30, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(30), - }, - ofB: &OrderFulfillment{ - Order: NewOrder(152).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: sdkmath.NewInt(50), - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(100), - }, - expA: &OrderFulfillment{ - Order: NewOrder(151).WithAsk(&AskOrder{ - Assets: coin(20, "apple"), - Price: coin(30, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(-10), - Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(40, "plum")}}, - }, - expB: &OrderFulfillment{ - Order: NewOrder(152).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(30), - PriceAppliedAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(60), - Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(40, "plum")}}, + name: "three orders, bid: last partially filled", + fulfillments: []*orderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 200, true), 183), + }, + settlement: &Settlement{}, + expFulfillments: []*orderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 183, true), 183), }, + expSettlement: &Settlement{PartialOrderLeft: bidOrder(10, 17, true)}, }, { - name: "bid, unfilled, gets partially filled with truncation", - ofA: &OrderFulfillment{ - Order: NewOrder(153).WithAsk(&AskOrder{ - Assets: coin(20, "apple"), - Price: coin(30, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(30), - }, - ofB: &OrderFulfillment{ - Order: NewOrder(154).WithBid(&BidOrder{ - Assets: coin(57, "apple"), - Price: coin(331, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: sdkmath.NewInt(57), - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(331), - }, - expA: &OrderFulfillment{ - Order: NewOrder(153).WithAsk(&AskOrder{ - Assets: coin(20, "apple"), - Price: coin(30, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(116), // 331 * 20 / 57 = 116.140350877193 - PriceLeftAmt: sdkmath.NewInt(-86), - Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(116, "plum")}}, - }, - expB: &OrderFulfillment{ - Order: NewOrder(154).WithBid(&BidOrder{ - Assets: coin(57, "apple"), - Price: coin(331, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(37), - PriceAppliedAmt: sdkmath.NewInt(116), - PriceLeftAmt: sdkmath.NewInt(215), - Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(116, "plum")}}, + name: "three orders, ask: last partially filled, split not allowed", + fulfillments: []*orderFulfillment{ + newOF(askOrder(8, 53, true), 53), + newOF(askOrder(9, 17, true), 17), + newOF(askOrder(10, 200, false), 183), }, + settlement: &Settlement{}, + expErr: "cannot split ask order 10 having assets \"200acorn\" at \"183acorn\": order does not allow partial fulfillment", }, { - name: "bid, partially filled, gets partially filled more", - ofA: &OrderFulfillment{ - Order: NewOrder(155).WithAsk(&AskOrder{ - Assets: coin(20, "apple"), - Price: coin(30, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(30), - }, - ofB: &OrderFulfillment{ - Order: NewOrder(156).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(10), - AssetsUnfilledAmt: sdkmath.NewInt(40), - PriceAppliedAmt: sdkmath.NewInt(20), - PriceLeftAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(77).WithAsk(&AskOrder{})}, - Assets: coin(10, "apple"), - Price: coin(20, "plum"), - }, - }, - }, - expA: &OrderFulfillment{ - Order: NewOrder(155).WithAsk(&AskOrder{ - Assets: coin(20, "apple"), - Price: coin(30, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(-10), - Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(40, "plum")}}, - }, - expB: &OrderFulfillment{ - Order: NewOrder(156).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(30), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(60), - PriceLeftAmt: sdkmath.NewInt(40), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(77).WithAsk(&AskOrder{})}, - Assets: coin(10, "apple"), - Price: coin(20, "plum"), - }, - {Assets: coin(20, "apple"), Price: coin(40, "plum")}, - }, + name: "three orders, bid: last partially filled, split not allowed", + fulfillments: []*orderFulfillment{ + newOF(bidOrder(8, 53, true), 53), + newOF(bidOrder(9, 17, true), 17), + newOF(bidOrder(10, 200, false), 183), }, + settlement: &Settlement{}, + expErr: "cannot split bid order 10 having assets \"200acorn\" at \"183acorn\": order does not allow partial fulfillment", }, { - name: "bid, partially filled, gets fully filled", - ofA: &OrderFulfillment{ - Order: NewOrder(157).WithAsk(&AskOrder{ - Assets: coin(20, "apple"), - Price: coin(30, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(0), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(0), - PriceLeftAmt: sdkmath.NewInt(30), - }, - ofB: &OrderFulfillment{ - Order: NewOrder(158).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(30), - AssetsUnfilledAmt: sdkmath.NewInt(20), - PriceAppliedAmt: sdkmath.NewInt(60), - PriceLeftAmt: sdkmath.NewInt(40), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(75).WithAsk(&AskOrder{})}, - Assets: coin(30, "apple"), - Price: coin(60, "plum"), - }, - }, - }, - expA: &OrderFulfillment{ - Order: NewOrder(157).WithAsk(&AskOrder{ - Assets: coin(20, "apple"), - Price: coin(30, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(-10), - Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(40, "plum")}}, - }, - expB: &OrderFulfillment{ - Order: NewOrder(158).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(75).WithAsk(&AskOrder{})}, - Assets: coin(30, "apple"), - Price: coin(60, "plum"), - }, - {Assets: coin(20, "apple"), Price: coin(40, "plum")}, - }, + name: "three orders, ask: last partially filled, already have a partially filled", + fulfillments: []*orderFulfillment{ + newOF(askOrder(8, 53, true), 53), + newOF(askOrder(9, 17, true), 17), + newOF(askOrder(10, 200, false), 183), }, + settlement: &Settlement{PartialOrderLeft: bidOrder(857, 43, true)}, + expErr: "bid order 857 and ask order 10 cannot both be partially filled", }, { - name: "both partially filled, both get fully filled", - ofA: &OrderFulfillment{ - Order: NewOrder(201).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(30), - PriceAppliedAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(60), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(1002).WithBid(&BidOrder{})}, - Assets: coin(20, "apple"), - Price: coin(40, "plum"), - }, - }, - }, - ofB: &OrderFulfillment{ - Order: NewOrder(202).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(30), - PriceAppliedAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(60), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(1003).WithAsk(&AskOrder{})}, - Assets: coin(20, "apple"), - Price: coin(40, "plum"), - }, - }, - }, - expA: &OrderFulfillment{ - Order: NewOrder(201).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(1002).WithBid(&BidOrder{})}, - Assets: coin(20, "apple"), - Price: coin(40, "plum"), - }, - {Assets: coin(30, "apple"), Price: coin(60, "plum")}, - }, - }, - expB: &OrderFulfillment{ - Order: NewOrder(202).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(1003).WithAsk(&AskOrder{})}, - Assets: coin(20, "apple"), - Price: coin(40, "plum"), - }, - {Assets: coin(30, "apple"), Price: coin(60, "plum")}, - }, + name: "three orders, bid: last partially filled, already have a partially filled", + fulfillments: []*orderFulfillment{ + newOF(bidOrder(8, 53, true), 53), + newOF(bidOrder(9, 17, true), 17), + newOF(bidOrder(10, 200, false), 183), }, + settlement: &Settlement{PartialOrderLeft: askOrder(857, 43, true)}, + expErr: "ask order 857 and bid order 10 cannot both be partially filled", }, { - name: "both partially filled, ask gets fully filled", - ofA: &OrderFulfillment{ - Order: NewOrder(203).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(30), - PriceAppliedAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(60), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(1004).WithBid(&BidOrder{})}, - Assets: coin(20, "apple"), - Price: coin(40, "plum"), - }, - }, + name: "three orders, ask: fully filled", + fulfillments: []*orderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 200, false), 200), }, - ofB: &OrderFulfillment{ - Order: NewOrder(204).WithBid(&BidOrder{ - Assets: coin(60, "apple"), - Price: coin(120, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(40), - PriceAppliedAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(1005).WithAsk(&AskOrder{})}, - Assets: coin(20, "apple"), - Price: coin(40, "plum"), - }, - }, + settlement: &Settlement{}, + expFulfillments: []*orderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 200, false), 200), }, - expA: &OrderFulfillment{ - Order: NewOrder(203).WithAsk(&AskOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(1004).WithBid(&BidOrder{})}, - Assets: coin(20, "apple"), - Price: coin(40, "plum"), - }, - {Assets: coin(30, "apple"), Price: coin(60, "plum")}, - }, + expSettlement: &Settlement{}, + }, + { + name: "three orders, bid: fully filled", + fulfillments: []*orderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 200, false), 200), }, - expB: &OrderFulfillment{ - Order: NewOrder(204).WithBid(&BidOrder{ - Assets: coin(60, "apple"), - Price: coin(120, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(10), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: sdkmath.NewInt(20), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(1005).WithAsk(&AskOrder{})}, - Assets: coin(20, "apple"), - Price: coin(40, "plum"), - }, - {Assets: coin(30, "apple"), Price: coin(60, "plum")}, - }, + settlement: &Settlement{}, + expFulfillments: []*orderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 200, false), 200), }, + expSettlement: &Settlement{}, }, { - name: "both partially filled, bid gets fully filled", - ofA: &OrderFulfillment{ - Order: NewOrder(205).WithAsk(&AskOrder{ - Assets: coin(60, "apple"), - Price: coin(120, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(40), - PriceAppliedAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(80), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(1006).WithBid(&BidOrder{})}, - Assets: coin(20, "apple"), - Price: coin(40, "plum"), - }, - }, + name: "three orders, ask: fully filled, already have a partially filled", + fulfillments: []*orderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 200, false), 200), }, - ofB: &OrderFulfillment{ - Order: NewOrder(206).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(30), - PriceAppliedAmt: sdkmath.NewInt(40), - PriceLeftAmt: sdkmath.NewInt(60), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(1007).WithAsk(&AskOrder{})}, - Assets: coin(20, "apple"), - Price: coin(40, "plum"), - }, - }, + settlement: &Settlement{}, + expFulfillments: []*orderFulfillment{ + newOF(askOrder(8, 53, false), 53), + newOF(askOrder(9, 17, false), 17), + newOF(askOrder(10, 200, false), 200), }, - expA: &OrderFulfillment{ - Order: NewOrder(205).WithAsk(&AskOrder{ - Assets: coin(60, "apple"), - Price: coin(120, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: sdkmath.NewInt(10), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: sdkmath.NewInt(20), - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(1006).WithBid(&BidOrder{})}, - Assets: coin(20, "apple"), - Price: coin(40, "plum"), - }, - {Assets: coin(30, "apple"), Price: coin(60, "plum")}, - }, + expSettlement: &Settlement{}, + }, + { + name: "three orders, bid: fully filled, already have a partially filled", + fulfillments: []*orderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 200, false), 200), }, - expB: &OrderFulfillment{ - Order: NewOrder(206).WithBid(&BidOrder{ - Assets: coin(50, "apple"), - Price: coin(100, "plum"), - }), - AssetsFilledAmt: sdkmath.NewInt(50), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{ - { - Order: &OrderFulfillment{Order: NewOrder(1007).WithAsk(&AskOrder{})}, - Assets: coin(20, "apple"), - Price: coin(40, "plum"), - }, - {Assets: coin(30, "apple"), Price: coin(60, "plum")}, - }, + settlement: &Settlement{PartialOrderLeft: askOrder(857, 43, true)}, + expFulfillments: []*orderFulfillment{ + newOF(bidOrder(8, 53, false), 53), + newOF(bidOrder(9, 17, false), 17), + newOF(bidOrder(10, 200, false), 200), }, + expSettlement: &Settlement{PartialOrderLeft: askOrder(857, 43, true)}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - if len(tc.expErr) != 0 && len(tc.swapErr) == 0 { - tc.swapErr = tc.expErr - } - if len(tc.expErr) == 0 { - for _, split := range tc.expA.Splits { - if split.Order == nil { - split.Order = tc.expB - } - } - for _, split := range tc.expB.Splits { - if split.Order == nil { - split.Order = tc.expA - } - } - } - - of1, of2 := copyOrderFulfillment(tc.ofA), copyOrderFulfillment(tc.ofB) var err error testFunc := func() { - err = Fulfill(of1, of2) - } - require.NotPanics(t, testFunc, "Fulfill(A, B)") - assertions.AssertErrorValue(t, err, tc.expErr, "Fulfill(A, B) error") - if len(tc.expErr) == 0 { - if !assertEqualOrderFulfillments(t, tc.expA, of1, "Fulfill(A, B): A") { - t.Logf("Original: %s", orderFulfillmentString(tc.ofA)) - } - if !assertEqualOrderFulfillments(t, tc.expB, of2, "Fulfill(A, B): B") { - t.Logf("Original: %s", orderFulfillmentString(tc.ofB)) - } + err = splitOrderFulfillments(tc.fulfillments, tc.settlement) } - - of1, of2 = copyOrderFulfillment(tc.ofB), copyOrderFulfillment(tc.ofA) - require.NotPanics(t, testFunc, "Fulfill(B, A)") - assertions.AssertErrorValue(t, err, tc.swapErr, "Fulfill(B, A) error") - if len(tc.expErr) == 0 { - if !assertEqualOrderFulfillments(t, tc.expB, of1, "Fulfill(B, A): B") { - t.Logf("Original: %s", orderFulfillmentString(tc.ofA)) - } - if !assertEqualOrderFulfillments(t, tc.expA, of2, "Fulfill(B, A): A") { - t.Logf("Original: %s", orderFulfillmentString(tc.ofB)) - } + require.NotPanics(t, testFunc, "splitOrderFulfillments") + assertions.AssertErrorValue(t, err, tc.expErr, "splitOrderFulfillments error") + if len(tc.expErr) > 0 { + return } + assertEqualOrderFulfillmentSlices(t, tc.expFulfillments, tc.fulfillments, "fulfillments after splitOrderFulfillments") + assert.Equal(t, tc.expSettlement, tc.settlement, "settlement after splitOrderFulfillments") }) } } -func TestGetFulfillmentAssetsAmt(t *testing.T) { - newAskOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *OrderFulfillment { - return &OrderFulfillment{ - Order: NewOrder(orderID).WithAsk(&AskOrder{ - Assets: sdk.NewInt64Coin(assetDenom, 999), - }), - AssetsUnfilledAmt: sdkmath.NewInt(assetsUnfilled), - } +func TestAllocatePrice(t *testing.T) { + askOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + MarketId: 123, + Seller: fmt.Sprintf("seller%d", orderID), + Assets: sdk.NewInt64Coin("apple", assetsAmt), + Price: sdk.NewInt64Coin("peach", priceAmt), + }) } - newBidOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *OrderFulfillment { - return &OrderFulfillment{ - Order: NewOrder(orderID).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin(assetDenom, 999), - }), - AssetsUnfilledAmt: sdkmath.NewInt(assetsUnfilled), - } + bidOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + MarketId: 123, + Buyer: fmt.Sprintf("buyer%d", orderID), + Assets: sdk.NewInt64Coin("apple", assetsAmt), + Price: sdk.NewInt64Coin("peach", priceAmt), + }) } - - cases := []struct { - name string - of1Unfilled int64 - of2Unfilled int64 - expAmt int64 - }{ - {name: "of1 zero", of1Unfilled: 0, of2Unfilled: 3, expAmt: 0}, - {name: "of1 negative", of1Unfilled: -4, of2Unfilled: 3, expAmt: 0}, - {name: "of2 zero", of1Unfilled: 5, of2Unfilled: 0, expAmt: 0}, - {name: "of2 negative", of1Unfilled: 5, of2Unfilled: -6, expAmt: 0}, - {name: "equal", of1Unfilled: 8, of2Unfilled: 8, expAmt: 8}, - {name: "of1 has fewer", of1Unfilled: 9, of2Unfilled: 10, expAmt: 9}, - {name: "of2 has fewer", of1Unfilled: 12, of2Unfilled: 11, expAmt: 11}, + newOF := func(order *Order, dists ...*distribution) *orderFulfillment { + rv := newOrderFulfillment(order) + rv.AssetsFilledAmt, rv.AssetsUnfilledAmt = rv.AssetsUnfilledAmt, rv.AssetsFilledAmt + if len(dists) > 0 { + rv.PriceDists = dists + for _, dist := range dists { + rv.PriceAppliedAmt = rv.PriceAppliedAmt.Add(dist.Amount) + } + rv.PriceLeftAmt = rv.PriceLeftAmt.Sub(rv.PriceAppliedAmt) + } + return rv } - - type testCase struct { - name string - of1 *OrderFulfillment - of2 *OrderFulfillment - expAmt sdkmath.Int - expErr string + dist := func(address string, amount int64) *distribution { + return &distribution{Address: address, Amount: sdkmath.NewInt(amount)} } - tests := make([]testCase, 0, len(cases)*4) - - for _, c := range cases { - newTests := []testCase{ - { - name: "ask bid " + c.name, - of1: newAskOF(1, c.of1Unfilled, "one"), - of2: newBidOF(2, c.of2Unfilled, "two"), - expAmt: sdkmath.NewInt(c.expAmt), + tests := []struct { + name string + askOFs []*orderFulfillment + bidOFs []*orderFulfillment + expAskOFs []*orderFulfillment + expBidOFs []*orderFulfillment + expErr string + }{ + { + name: "total ask price greater than total bid", + askOFs: []*orderFulfillment{ + newOF(askOrder(3, 10, 20)), + newOF(askOrder(4, 10, 20)), + newOF(askOrder(5, 10, 20)), }, - { - name: "bid ask " + c.name, - of1: newBidOF(1, c.of1Unfilled, "one"), - of2: newAskOF(2, c.of2Unfilled, "two"), - expAmt: sdkmath.NewInt(c.expAmt), + bidOFs: []*orderFulfillment{ + newOF(bidOrder(6, 10, 20)), + newOF(bidOrder(7, 10, 19)), + newOF(bidOrder(8, 10, 20)), }, - { - name: "ask ask " + c.name, - of1: newAskOF(1, c.of1Unfilled, "one"), - of2: newAskOF(2, c.of2Unfilled, "two"), - expAmt: sdkmath.NewInt(c.expAmt), + expErr: "total ask price \"60peach\" is greater than total bid price \"59peach\"", + }, + { + name: "one ask, one bid: same price", + askOFs: []*orderFulfillment{newOF(askOrder(3, 10, 60))}, + bidOFs: []*orderFulfillment{newOF(bidOrder(6, 10, 60))}, + expAskOFs: []*orderFulfillment{newOF(askOrder(3, 10, 60), dist("buyer6", 60))}, + expBidOFs: []*orderFulfillment{newOF(bidOrder(6, 10, 60), dist("seller3", 60))}, + }, + { + name: "one ask, one bid: bid more", + askOFs: []*orderFulfillment{newOF(askOrder(3, 10, 60))}, + bidOFs: []*orderFulfillment{newOF(bidOrder(6, 10, 65))}, + expAskOFs: []*orderFulfillment{newOF(askOrder(3, 10, 60), dist("buyer6", 60), dist("buyer6", 5))}, + expBidOFs: []*orderFulfillment{newOF(bidOrder(6, 10, 65), dist("seller3", 60), dist("seller3", 5))}, + }, + { + name: "two asks, two bids: same total price, diff ask prices", + askOFs: []*orderFulfillment{ + newOF(askOrder(3, 10, 21)), + newOF(askOrder(4, 10, 19)), }, - { - name: "bid bid " + c.name, - of1: newBidOF(1, c.of1Unfilled, "one"), - of2: newBidOF(2, c.of2Unfilled, "two"), - expAmt: sdkmath.NewInt(c.expAmt), + bidOFs: []*orderFulfillment{ + newOF(bidOrder(6, 10, 20)), + newOF(bidOrder(7, 10, 20)), }, - } - if c.expAmt == 0 { - newTests[0].expErr = fmt.Sprintf("cannot fill ask order 1 having assets left \"%done\" with bid "+ - "order 2 having assets left \"%dtwo\": zero or negative assets left", - c.of1Unfilled, c.of2Unfilled) - newTests[1].expErr = fmt.Sprintf("cannot fill bid order 1 having assets left \"%done\" with ask "+ - "order 2 having assets left \"%dtwo\": zero or negative assets left", - c.of1Unfilled, c.of2Unfilled) - newTests[2].expErr = fmt.Sprintf("cannot fill ask order 1 having assets left \"%done\" with ask "+ - "order 2 having assets left \"%dtwo\": zero or negative assets left", - c.of1Unfilled, c.of2Unfilled) - newTests[3].expErr = fmt.Sprintf("cannot fill bid order 1 having assets left \"%done\" with bid "+ - "order 2 having assets left \"%dtwo\": zero or negative assets left", - c.of1Unfilled, c.of2Unfilled) - } - tests = append(tests, newTests...) + expAskOFs: []*orderFulfillment{ + newOF(askOrder(3, 10, 21), dist("buyer6", 20), dist("buyer7", 1)), + newOF(askOrder(4, 10, 19), dist("buyer7", 19)), + }, + expBidOFs: []*orderFulfillment{ + newOF(bidOrder(6, 10, 20), dist("seller3", 20)), + newOF(bidOrder(7, 10, 20), dist("seller3", 1), dist("seller4", 19)), + }, + }, + { + name: "three asks, three bids: same total price", + askOFs: []*orderFulfillment{ + newOF(askOrder(3, 10, 25)), + newOF(askOrder(4, 10, 20)), + newOF(askOrder(5, 10, 15)), + }, + bidOFs: []*orderFulfillment{ + newOF(bidOrder(6, 10, 18)), + newOF(bidOrder(7, 10, 30)), + newOF(bidOrder(8, 10, 12)), + }, + expAskOFs: []*orderFulfillment{ + newOF(askOrder(3, 10, 25), dist("buyer6", 18), dist("buyer7", 7)), + newOF(askOrder(4, 10, 20), dist("buyer7", 20)), + newOF(askOrder(5, 10, 15), dist("buyer7", 3), dist("buyer8", 12)), + }, + expBidOFs: []*orderFulfillment{ + newOF(bidOrder(6, 10, 18), dist("seller3", 18)), + newOF(bidOrder(7, 10, 30), dist("seller3", 7), dist("seller4", 20), dist("seller5", 3)), + newOF(bidOrder(8, 10, 12), dist("seller5", 12)), + }, + }, + { + name: "three asks, three bids: bids more", + askOFs: []*orderFulfillment{ + newOF(askOrder(3, 1, 10)), + newOF(askOrder(4, 7, 25)), + newOF(askOrder(5, 22, 30)), + }, + bidOFs: []*orderFulfillment{ + newOF(bidOrder(6, 10, 20)), + newOF(bidOrder(7, 10, 27)), + newOF(bidOrder(8, 10, 30)), + }, + // assets total = 30 + // ask price total = 65 + // bid price total = 77 + // leftover = 12 + expAskOFs: []*orderFulfillment{ + // 12 * 1 / 30 = 0.4 => 0, then 1 + newOF(askOrder(3, 1, 10), dist("buyer6", 10), + dist("buyer8", 1)), + // 12 * 7 / 30 = 2.8 => 2, then because there'll only be 1 left, 1 + newOF(askOrder(4, 7, 25), dist("buyer6", 10), dist("buyer7", 15), + dist("buyer8", 2), dist("buyer8", 1)), + // 12 * 22 / 30 = 8.8 => 8, then nothing because leftovers run out before getting back to it. + newOF(askOrder(5, 22, 30), dist("buyer7", 12), dist("buyer8", 18), + dist("buyer8", 8)), + }, + expBidOFs: []*orderFulfillment{ + newOF(bidOrder(6, 10, 20), dist("seller3", 10), dist("seller4", 10)), + newOF(bidOrder(7, 10, 27), dist("seller4", 15), dist("seller5", 12)), + newOF(bidOrder(8, 10, 30), dist("seller5", 18), dist("seller4", 2), + dist("seller5", 8), dist("seller3", 1), dist("seller4", 1)), + }, + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - if len(tc.expErr) > 0 { - tc.expAmt = sdkmath.ZeroInt() - } - origOF1 := copyOrderFulfillment(tc.of1) - origOF2 := copyOrderFulfillment(tc.of2) - - var amt sdkmath.Int var err error testFunc := func() { - amt, err = GetFulfillmentAssetsAmt(tc.of1, tc.of2) + err = allocatePrice(tc.askOFs, tc.bidOFs) } - require.NotPanics(t, testFunc, "GetFulfillmentAssetsAmt") - assertions.AssertErrorValue(t, err, tc.expErr, "GetFulfillmentAssetsAmt error") - assert.Equal(t, tc.expAmt, amt, "GetFulfillmentAssetsAmt amount") - assertEqualOrderFulfillments(t, origOF1, tc.of1, "of1 after GetFulfillmentAssetsAmt") - assertEqualOrderFulfillments(t, origOF2, tc.of2, "of2 after GetFulfillmentAssetsAmt") + require.NotPanics(t, testFunc, "allocatePrice") + assertions.AssertErrorValue(t, err, tc.expErr, "allocatePrice error") + if len(tc.expErr) > 0 { + return + } + assertEqualOrderFulfillmentSlices(t, tc.expAskOFs, tc.askOFs, "askOFs after allocatePrice") + assertEqualOrderFulfillmentSlices(t, tc.expBidOFs, tc.bidOFs, "bidOFs after allocatePrice") }) } } func TestGetFulfillmentPriceAmt(t *testing.T) { - newAskOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *OrderFulfillment { - return &OrderFulfillment{ + newAskOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *orderFulfillment { + return &orderFulfillment{ Order: NewOrder(orderID).WithAsk(&AskOrder{ Price: sdk.NewInt64Coin(assetDenom, 999), }), PriceLeftAmt: sdkmath.NewInt(assetsUnfilled), } } - newBidOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *OrderFulfillment { - return &OrderFulfillment{ + newBidOF := func(orderID uint64, assetsUnfilled int64, assetDenom string) *orderFulfillment { + return &orderFulfillment{ Order: NewOrder(orderID).WithBid(&BidOrder{ Price: sdk.NewInt64Coin(assetDenom, 999), }), @@ -8877,8 +4934,8 @@ func TestGetFulfillmentPriceAmt(t *testing.T) { type testCase struct { name string - of1 *OrderFulfillment - of2 *OrderFulfillment + of1 *orderFulfillment + of2 *orderFulfillment expAmt sdkmath.Int expErr string } @@ -8914,2427 +4971,1215 @@ func TestGetFulfillmentPriceAmt(t *testing.T) { } if c.expAmt == 0 { newTests[0].expErr = fmt.Sprintf("cannot fill ask order 1 having price left \"%done\" with bid "+ - "order 2 having price left \"%dtwo\": zero or negative price left", - c.of1Unfilled, c.of2Unfilled) - newTests[1].expErr = fmt.Sprintf("cannot fill bid order 1 having price left \"%done\" with ask "+ - "order 2 having price left \"%dtwo\": zero or negative price left", - c.of1Unfilled, c.of2Unfilled) - newTests[2].expErr = fmt.Sprintf("cannot fill ask order 1 having price left \"%done\" with ask "+ - "order 2 having price left \"%dtwo\": zero or negative price left", - c.of1Unfilled, c.of2Unfilled) - newTests[3].expErr = fmt.Sprintf("cannot fill bid order 1 having price left \"%done\" with bid "+ - "order 2 having price left \"%dtwo\": zero or negative price left", - c.of1Unfilled, c.of2Unfilled) - } - tests = append(tests, newTests...) - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - if len(tc.expErr) > 0 { - tc.expAmt = sdkmath.ZeroInt() - } - origOF1 := copyOrderFulfillment(tc.of1) - origOF2 := copyOrderFulfillment(tc.of2) - - var amt sdkmath.Int - var err error - testFunc := func() { - amt, err = GetFulfillmentPriceAmt(tc.of1, tc.of2) - } - require.NotPanics(t, testFunc, "GetFulfillmentPriceAmt") - assertions.AssertErrorValue(t, err, tc.expErr, "GetFulfillmentPriceAmt error") - assert.Equal(t, tc.expAmt, amt, "GetFulfillmentPriceAmt amount") - assertEqualOrderFulfillments(t, origOF1, tc.of1, "of1 after GetFulfillmentPriceAmt") - assertEqualOrderFulfillments(t, origOF2, tc.of2, "of2 after GetFulfillmentPriceAmt") - }) - } -} - -func TestNewPartialFulfillment(t *testing.T) { - sdkNewInt64CoinP := func(denom string, amt int64) *sdk.Coin { - rv := sdk.NewInt64Coin(denom, amt) - return &rv - } - - tests := []struct { - name string - f *OrderFulfillment - exp *PartialFulfillment - expPanic string - }{ - { - name: "ask order fees left", - f: &OrderFulfillment{ - Order: NewOrder(54).WithAsk(&AskOrder{ - MarketId: 12, - Seller: "the seller", - Assets: sdk.NewInt64Coin("apple", 1234), - Price: sdk.NewInt64Coin("pear", 9876), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 55), - AllowPartial: true, - }), - AssetsFilledAmt: sdkmath.NewInt(234), - AssetsUnfilledAmt: sdkmath.NewInt(1000), - PriceAppliedAmt: sdkmath.NewInt(10000), - PriceLeftAmt: sdkmath.NewInt(-124), - OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 50)), - PriceFilledAmt: sdkmath.NewInt(876), - PriceUnfilledAmt: sdkmath.NewInt(9000), - }, - exp: &PartialFulfillment{ - NewOrder: NewOrder(54).WithAsk(&AskOrder{ - MarketId: 12, - Seller: "the seller", - Assets: sdk.NewInt64Coin("apple", 1000), - Price: sdk.NewInt64Coin("pear", 9000), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 50), - AllowPartial: true, - }), - AssetsFilled: sdk.NewInt64Coin("apple", 234), - PriceFilled: sdk.NewInt64Coin("pear", 876), - }, - }, - { - name: "ask order no fees left", - f: &OrderFulfillment{ - Order: NewOrder(54).WithAsk(&AskOrder{ - MarketId: 12, - Seller: "the seller", - Assets: sdk.NewInt64Coin("apple", 1234), - Price: sdk.NewInt64Coin("pear", 9876), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 55), - AllowPartial: false, - }), - AssetsFilledAmt: sdkmath.NewInt(234), - AssetsUnfilledAmt: sdkmath.NewInt(1000), - PriceAppliedAmt: sdkmath.NewInt(10000), - PriceLeftAmt: sdkmath.NewInt(-124), - OrderFeesLeft: nil, - PriceFilledAmt: sdkmath.NewInt(876), - PriceUnfilledAmt: sdkmath.NewInt(9000), - }, - exp: &PartialFulfillment{ - NewOrder: NewOrder(54).WithAsk(&AskOrder{ - MarketId: 12, - Seller: "the seller", - Assets: sdk.NewInt64Coin("apple", 1000), - Price: sdk.NewInt64Coin("pear", 9000), - SellerSettlementFlatFee: nil, - AllowPartial: false, - }), - AssetsFilled: sdk.NewInt64Coin("apple", 234), - PriceFilled: sdk.NewInt64Coin("pear", 876), - }, - expPanic: "", - }, - { - name: "ask order multiple fees left", - f: &OrderFulfillment{ - Order: NewOrder(54).WithAsk(&AskOrder{ - MarketId: 12, - Seller: "the seller", - Assets: sdk.NewInt64Coin("apple", 1234), - Price: sdk.NewInt64Coin("pear", 9876), - SellerSettlementFlatFee: sdkNewInt64CoinP("fig", 55), - AllowPartial: true, - }), - AssetsFilledAmt: sdkmath.NewInt(234), - AssetsUnfilledAmt: sdkmath.NewInt(1000), - PriceAppliedAmt: sdkmath.NewInt(10000), - PriceLeftAmt: sdkmath.NewInt(-124), - OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 50), sdk.NewInt64Coin("grape", 1)), - PriceFilledAmt: sdkmath.NewInt(876), - PriceUnfilledAmt: sdkmath.NewInt(9000), - }, - expPanic: "partially filled ask order 54 somehow has multiple denoms in fees left \"50fig,1grape\"", - }, - { - name: "bid order", - f: &OrderFulfillment{ - Order: NewOrder(54).WithBid(&BidOrder{ - MarketId: 12, - Buyer: "the buyer", - Assets: sdk.NewInt64Coin("apple", 1234), - Price: sdk.NewInt64Coin("pear", 9876), - BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 55), sdk.NewInt64Coin("grape", 12)), - AllowPartial: true, - }), - AssetsFilledAmt: sdkmath.NewInt(234), - AssetsUnfilledAmt: sdkmath.NewInt(1000), - PriceAppliedAmt: sdkmath.NewInt(9875), - PriceLeftAmt: sdkmath.NewInt(1), - OrderFeesLeft: sdk.NewCoins(sdk.NewInt64Coin("fig", 50)), - PriceFilledAmt: sdkmath.NewInt(876), - PriceUnfilledAmt: sdkmath.NewInt(9000), - }, - exp: &PartialFulfillment{ - NewOrder: NewOrder(54).WithBid(&BidOrder{ - MarketId: 12, - Buyer: "the buyer", - Assets: sdk.NewInt64Coin("apple", 1000), - Price: sdk.NewInt64Coin("pear", 9000), - BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 50)), - AllowPartial: true, - }), - AssetsFilled: sdk.NewInt64Coin("apple", 234), - PriceFilled: sdk.NewInt64Coin("pear", 876), - }, - }, - { - name: "nil order type", - f: &OrderFulfillment{ - Order: NewOrder(57), - AssetsFilledAmt: sdkmath.NewInt(5), - PriceFilledAmt: sdkmath.NewInt(6), - }, - expPanic: nilSubTypeErr(57), - }, - { - name: "unknown order type", - f: &OrderFulfillment{ - Order: newUnknownOrder(58), - AssetsFilledAmt: sdkmath.NewInt(5), - PriceFilledAmt: sdkmath.NewInt(6), - }, - expPanic: unknownSubTypeErr(58), - }, - // I don't feel like creating a 3rd order type that implements SubOrderI which would be needed in order to - // have a test case reach the final "order %d has unknown type %q" panic at the end of the func. - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - origF := copyOrderFulfillment(tc.f) - - var actual *PartialFulfillment - testFunc := func() { - actual = NewPartialFulfillment(tc.f) - } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "NewPartialFulfillment") - if !assert.Equal(t, tc.exp, actual, "NewPartialFulfillment result") { - t.Logf(" Actual: %s", partialFulfillmentString(actual)) - t.Logf("Expected: %s", partialFulfillmentString(tc.exp)) - } - assertEqualOrderFulfillments(t, origF, tc.f, "OrderFulfillment after NewPartialFulfillment") - }) - } -} - -func TestBuildFulfillments(t *testing.T) { - coin := func(amount int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} - } - coins := func(amount int64, denom string) sdk.Coins { - return sdk.Coins{coin(amount, denom)} - } - - askOrder := func(orderID uint64, assets sdk.Coin, price sdk.Coin, allowPartial bool, fees ...sdk.Coin) *Order { - ao := &AskOrder{ - Seller: "seller", - Assets: assets, - Price: price, - AllowPartial: allowPartial, - } - if len(fees) > 1 { - t.Fatalf("cannot create ask order %d with more than 1 fees %q", orderID, fees) - } - if len(fees) > 0 { - ao.SellerSettlementFlatFee = &fees[0] - } - return NewOrder(orderID).WithAsk(ao) - } - bidOrder := func(orderID uint64, assets sdk.Coin, price sdk.Coin, allowPartial bool, fees ...sdk.Coin) *Order { - return NewOrder(orderID).WithBid(&BidOrder{ - Buyer: "buyer", - Assets: assets, - Price: price, - AllowPartial: allowPartial, - BuyerSettlementFees: fees, - }) - } - filledOF := func(order *Order, priceAmt int64, splits []*OrderSplit, feesToPay ...sdk.Coins) *OrderFulfillment { - rv := &OrderFulfillment{ - Order: order, - AssetsFilledAmt: order.GetAssets().Amount, - AssetsUnfilledAmt: ZeroAmtAfterSub, - Splits: splits, - IsFinalized: true, - PriceFilledAmt: order.GetPrice().Amount, - PriceUnfilledAmt: sdkmath.NewInt(0), - } - if priceAmt != 0 { - rv.PriceAppliedAmt = sdkmath.NewInt(priceAmt) - } else { - rv.PriceAppliedAmt = order.GetPrice().Amount - } - rv.PriceLeftAmt = rv.PriceFilledAmt.Sub(rv.PriceAppliedAmt) - if len(feesToPay) > 0 { - rv.FeesToPay = feesToPay[0] - } - return rv - } - - tests := []struct { - name string - askOrders []*Order - bidOrders []*Order - sellerFeeRatio *FeeRatio - expectedMaker func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments - expErr string - }{ - { - name: "one ask one bid, both fully filled", - askOrders: []*Order{askOrder(5, coin(10, "apple"), coin(55, "prune"), false, coin(8, "fig"))}, - bidOrders: []*Order{bidOrder(6, coin(10, "apple"), coin(60, "prune"), false, coin(33, "fig"))}, - sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, - expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { - rv := &Fulfillments{ - AskOFs: []*OrderFulfillment{ - filledOF(askOrders[0], 60, - []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(60, "prune")}}, // will be BidOFs[0]. - coins(20, "fig"), - ), - }, - BidOFs: []*OrderFulfillment{ - filledOF(bidOrders[0], 0, - []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(60, "prune")}}, // will be AskOFs[0]. - coins(33, "fig"), - ), - }, - PartialOrder: nil, - } - - rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] - rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] - - return rv - }, - }, - { - name: "one ask one bid, ask partially filled", - askOrders: []*Order{askOrder(7, coin(15, "apple"), coin(75, "prune"), true)}, - bidOrders: []*Order{bidOrder(8, coin(10, "apple"), coin(60, "prune"), false)}, - sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, - expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { - rv := &Fulfillments{ - AskOFs: []*OrderFulfillment{ - { - Order: askOrders[0], - AssetsFilledAmt: sdkmath.NewInt(10), - AssetsUnfilledAmt: sdkmath.NewInt(5), - PriceAppliedAmt: sdkmath.NewInt(60), - PriceLeftAmt: sdkmath.NewInt(15), - Splits: []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(60, "prune")}}, // will be BidOFs[0]. - IsFinalized: true, - FeesToPay: coins(12, "fig"), - OrderFeesLeft: nil, - PriceFilledAmt: sdkmath.NewInt(50), - PriceUnfilledAmt: sdkmath.NewInt(25), - }, - }, - BidOFs: []*OrderFulfillment{ - filledOF(bidOrders[0], 0, - []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(60, "prune")}}, // will be AskOFs[0]. - ), - }, - PartialOrder: &PartialFulfillment{ - NewOrder: askOrder(7, coin(5, "apple"), coin(25, "prune"), true), - AssetsFilled: coin(10, "apple"), - PriceFilled: coin(50, "prune"), - }, - } - - rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] - rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] - - return rv - }, - }, - { - name: "one ask one bid, ask partially filled not allowed", - askOrders: []*Order{askOrder(7, coin(15, "apple"), coin(75, "prune"), false)}, - bidOrders: []*Order{bidOrder(8, coin(10, "apple"), coin(60, "prune"), false)}, - sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, - expErr: "cannot fill ask order 7 having assets \"15apple\" with \"10apple\": order does not allow partial fill", - }, - { - name: "one ask one bid, bid partially filled", - askOrders: []*Order{askOrder(9, coin(10, "apple"), coin(50, "prune"), false)}, - bidOrders: []*Order{bidOrder(10, coin(15, "apple"), coin(90, "prune"), true)}, - sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, - expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { - rv := &Fulfillments{ - AskOFs: []*OrderFulfillment{ - filledOF(askOrders[0], 60, - []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(60, "prune")}}, // will be BidOFs[0]. - coins(12, "fig"), - ), - }, - BidOFs: []*OrderFulfillment{ - { - Order: bidOrders[0], - AssetsFilledAmt: sdkmath.NewInt(10), - AssetsUnfilledAmt: sdkmath.NewInt(5), - PriceAppliedAmt: sdkmath.NewInt(60), - PriceLeftAmt: sdkmath.NewInt(30), - Splits: []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(60, "prune")}}, // will be AskOFs[0]. - IsFinalized: true, - FeesToPay: nil, - OrderFeesLeft: nil, - PriceFilledAmt: sdkmath.NewInt(60), - PriceUnfilledAmt: sdkmath.NewInt(30), - }, - }, - PartialOrder: &PartialFulfillment{ - NewOrder: bidOrder(10, coin(5, "apple"), coin(30, "prune"), true), - AssetsFilled: coin(10, "apple"), - PriceFilled: coin(60, "prune"), - }, - } - - rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] - rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] - - return rv - }, - }, - { - name: "one ask one bid, bid partially filled not allowed", - askOrders: []*Order{askOrder(9, coin(10, "apple"), coin(50, "prune"), false)}, - bidOrders: []*Order{bidOrder(10, coin(15, "apple"), coin(90, "prune"), false)}, - sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, - expErr: "cannot fill bid order 10 having assets \"15apple\" with \"10apple\": order does not allow partial fill", - }, - { - name: "one ask filled by five bids", - askOrders: []*Order{askOrder(21, coin(12, "apple"), coin(60, "prune"), false)}, - bidOrders: []*Order{ - bidOrder(22, coin(1, "apple"), coin(10, "prune"), false), - bidOrder(22, coin(1, "apple"), coin(12, "prune"), false), - bidOrder(22, coin(2, "apple"), coin(1, "prune"), false), - bidOrder(22, coin(3, "apple"), coin(15, "prune"), false), - bidOrder(22, coin(5, "apple"), coin(25, "prune"), false), - }, - sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, - expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { - rv := &Fulfillments{ - AskOFs: []*OrderFulfillment{ - filledOF(askOrders[0], 63, - []*OrderSplit{ - {Assets: coin(1, "apple"), Price: coin(10, "prune")}, // Will be BidOFs[0]. - {Assets: coin(1, "apple"), Price: coin(12, "prune")}, // Will be BidOFs[1]. - {Assets: coin(2, "apple"), Price: coin(1, "prune")}, // Will be BidOFs[2]. - {Assets: coin(3, "apple"), Price: coin(15, "prune")}, // Will be BidOFs[3]. - {Assets: coin(5, "apple"), Price: coin(25, "prune")}, // Will be BidOFs[4]. - }, - coins(13, "fig"), - ), - }, - BidOFs: []*OrderFulfillment{ - filledOF(bidOrders[0], 0, - []*OrderSplit{{Assets: coin(1, "apple"), Price: coin(10, "prune")}}, // Will be AskOFs[0]. - ), - filledOF(bidOrders[1], 0, - []*OrderSplit{{Assets: coin(1, "apple"), Price: coin(12, "prune")}}, // Will be AskOFs[0]. - ), - filledOF(bidOrders[2], 0, - []*OrderSplit{{Assets: coin(2, "apple"), Price: coin(1, "prune")}}, // Will be AskOFs[0]. - - ), - filledOF(bidOrders[3], 0, - []*OrderSplit{{Assets: coin(3, "apple"), Price: coin(15, "prune")}}, // Will be AskOFs[0]. - ), - filledOF(bidOrders[4], 0, - []*OrderSplit{{Assets: coin(5, "apple"), Price: coin(25, "prune")}}, // Will be AskOFs[0]. - ), - }, - } - - rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] - rv.AskOFs[0].Splits[1].Order = rv.BidOFs[1] - rv.AskOFs[0].Splits[2].Order = rv.BidOFs[2] - rv.AskOFs[0].Splits[3].Order = rv.BidOFs[3] - rv.AskOFs[0].Splits[4].Order = rv.BidOFs[4] - - rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] - rv.BidOFs[1].Splits[0].Order = rv.AskOFs[0] - rv.BidOFs[2].Splits[0].Order = rv.AskOFs[0] - rv.BidOFs[3].Splits[0].Order = rv.AskOFs[0] - rv.BidOFs[4].Splits[0].Order = rv.AskOFs[0] - - return rv - }, - }, - { - name: "one indivisible bid filled by five asks", - askOrders: []*Order{ - askOrder(31, coin(1, "apple"), coin(16, "prune"), false), - askOrder(33, coin(1, "apple"), coin(16, "prune"), false), - askOrder(35, coin(1, "apple"), coin(17, "prune"), false), - askOrder(37, coin(13, "apple"), coin(209, "prune"), false), - askOrder(39, coin(15, "apple"), coin(241, "prune"), false), - }, - bidOrders: []*Order{bidOrder(30, coin(31, "apple"), coin(500, "prune"), false)}, - sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, - expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { - rv := &Fulfillments{ - AskOFs: []*OrderFulfillment{ - filledOF(askOrders[0], 17, - []*OrderSplit{{Assets: coin(1, "apple"), Price: coin(17, "prune")}}, // Will be BidOfs[0] - coins(4, "fig"), - ), - filledOF(askOrders[1], 0, - []*OrderSplit{{Assets: coin(1, "apple"), Price: coin(16, "prune")}}, // Will be BidOfs[0] - coins(4, "fig"), - ), - filledOF(askOrders[2], 0, - []*OrderSplit{{Assets: coin(1, "apple"), Price: coin(17, "prune")}}, // Will be BidOfs[0] - coins(4, "fig"), - ), - filledOF(askOrders[3], 0, - []*OrderSplit{{Assets: coin(13, "apple"), Price: coin(209, "prune")}}, // Will be BidOfs[0] - coins(42, "fig"), - ), - filledOF(askOrders[4], 0, - []*OrderSplit{{Assets: coin(15, "apple"), Price: coin(241, "prune")}}, // Will be BidOfs[0] - coins(49, "fig"), - ), - }, - BidOFs: []*OrderFulfillment{ - { - Order: bidOrders[0], - AssetsFilledAmt: sdkmath.NewInt(31), - AssetsUnfilledAmt: ZeroAmtAfterSub, - PriceAppliedAmt: sdkmath.NewInt(500), - PriceLeftAmt: ZeroAmtAfterSub, - Splits: []*OrderSplit{ - {Assets: coin(1, "apple"), Price: coin(17, "prune")}, // Will be AskOFs[0] - {Assets: coin(1, "apple"), Price: coin(16, "prune")}, // Will be AskOFs[1] - {Assets: coin(1, "apple"), Price: coin(17, "prune")}, // Will be AskOFs[2] - {Assets: coin(13, "apple"), Price: coin(209, "prune")}, // Will be AskOFs[3] - {Assets: coin(15, "apple"), Price: coin(241, "prune")}, // Will be AskOFs[4] - }, - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(500), - PriceUnfilledAmt: sdkmath.NewInt(0), - }, - }, - } - - rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] - rv.AskOFs[1].Splits[0].Order = rv.BidOFs[0] - rv.AskOFs[2].Splits[0].Order = rv.BidOFs[0] - rv.AskOFs[3].Splits[0].Order = rv.BidOFs[0] - rv.AskOFs[4].Splits[0].Order = rv.BidOFs[0] - - rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] - rv.BidOFs[0].Splits[1].Order = rv.AskOFs[1] - rv.BidOFs[0].Splits[2].Order = rv.AskOFs[2] - rv.BidOFs[0].Splits[3].Order = rv.AskOFs[3] - rv.BidOFs[0].Splits[4].Order = rv.AskOFs[4] - - return rv - }, - }, - { - name: "three asks three bids, each fully fills the other", - askOrders: []*Order{ - askOrder(51, coin(8, "apple"), coin(55, "prune"), false, coin(18, "grape")), - askOrder(53, coin(12, "apple"), coin(18, "prune"), false, coin(1, "grape")), - askOrder(55, coin(344, "apple"), coin(12345, "prune"), false, coin(99, "grape")), - }, - bidOrders: []*Order{ - bidOrder(52, coin(8, "apple"), coin(55, "prune"), false, coin(3, "fig")), - bidOrder(54, coin(12, "apple"), coin(18, "prune"), false, coin(7, "fig")), - bidOrder(56, coin(344, "apple"), coin(12345, "prune"), false, coin(2, "fig")), - }, - expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { - rv := &Fulfillments{ - AskOFs: []*OrderFulfillment{ - filledOF(askOrders[0], 0, - []*OrderSplit{{Assets: coin(8, "apple"), Price: coin(55, "prune")}}, // Will be BidOFs[0] - coins(18, "grape"), - ), - filledOF(askOrders[1], 0, - []*OrderSplit{{Assets: coin(12, "apple"), Price: coin(18, "prune")}}, // Will be BidOFs[1] - coins(1, "grape"), - ), - filledOF(askOrders[2], 0, - []*OrderSplit{{Assets: coin(344, "apple"), Price: coin(12345, "prune")}}, // Will be BidOFs[2] - coins(99, "grape"), - ), - }, - BidOFs: []*OrderFulfillment{ - filledOF(bidOrders[0], 0, - []*OrderSplit{{Assets: coin(8, "apple"), Price: coin(55, "prune")}}, // Will be AskOFs[0] - coins(3, "fig"), - ), - filledOF(bidOrders[1], 0, - []*OrderSplit{{Assets: coin(12, "apple"), Price: coin(18, "prune")}}, // Will be AskOFs[1] - coins(7, "fig"), - ), - filledOF(bidOrders[2], 0, - []*OrderSplit{{Assets: coin(344, "apple"), Price: coin(12345, "prune")}}, // Will be AskOFs[2] - coins(2, "fig"), - ), - }, - PartialOrder: nil, - } - - rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] - rv.AskOFs[1].Splits[0].Order = rv.BidOFs[1] - rv.AskOFs[2].Splits[0].Order = rv.BidOFs[2] - - rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] - rv.BidOFs[1].Splits[0].Order = rv.AskOFs[1] - rv.BidOFs[2].Splits[0].Order = rv.AskOFs[2] - - return rv - }, - }, - { - name: "three asks two bids, all fully filled", - askOrders: []*Order{ - askOrder(11, coin(10, "apple"), coin(50, "prune"), false), - askOrder(13, coin(20, "apple"), coin(100, "prune"), false), - askOrder(15, coin(50, "apple"), coin(250, "prune"), false), - }, - bidOrders: []*Order{ - bidOrder(12, coin(23, "apple"), coin(115, "prune"), false), - bidOrder(14, coin(57, "apple"), coin(285, "prune"), false), - }, - sellerFeeRatio: &FeeRatio{Price: coin(5, "prune"), Fee: coin(1, "fig")}, - expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { - rv := &Fulfillments{ - AskOFs: []*OrderFulfillment{ - filledOF(askOrders[0], 0, - []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(50, "prune")}}, // Will be BidOFs[0] - coins(10, "fig"), - ), - filledOF(askOrders[1], 0, - []*OrderSplit{ - {Assets: coin(13, "apple"), Price: coin(65, "prune")}, // Will be BidOFs[0] - {Assets: coin(7, "apple"), Price: coin(35, "prune")}, // Will be BidOFs[1] - }, - coins(20, "fig"), - ), - filledOF(askOrders[2], 0, - []*OrderSplit{{Assets: coin(50, "apple"), Price: coin(250, "prune")}}, // Will be BidOFs[1] - coins(50, "fig"), - ), - }, - BidOFs: []*OrderFulfillment{ - filledOF(bidOrders[0], 0, - []*OrderSplit{ - {Assets: coin(10, "apple"), Price: coin(50, "prune")}, // Will be AskOfs[0] - {Assets: coin(13, "apple"), Price: coin(65, "prune")}, // Will be AskOfs[1] - }, - ), - filledOF(bidOrders[1], 0, - []*OrderSplit{ - {Assets: coin(7, "apple"), Price: coin(35, "prune")}, // Will be AskOFs[1] - {Assets: coin(50, "apple"), Price: coin(250, "prune")}, // Will be AskOFs[2] - }, - ), - }, - } - - rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] - rv.AskOFs[1].Splits[0].Order = rv.BidOFs[0] - rv.AskOFs[1].Splits[1].Order = rv.BidOFs[1] - rv.AskOFs[2].Splits[0].Order = rv.BidOFs[1] - rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] - rv.BidOFs[0].Splits[1].Order = rv.AskOFs[1] - rv.BidOFs[1].Splits[0].Order = rv.AskOFs[1] - rv.BidOFs[1].Splits[1].Order = rv.AskOFs[2] - - return rv - }, - }, - { - name: "three asks two bids, ask partially filled", - askOrders: []*Order{ - askOrder(73, coin(10, "apple"), coin(50, "prune"), false), - askOrder(75, coin(15, "apple"), coin(75, "prune"), false), - askOrder(77, coin(25, "apple"), coin(125, "prune"), true), - }, - bidOrders: []*Order{ - bidOrder(74, coin(5, "apple"), coin(25, "prune"), false), - bidOrder(76, coin(40, "apple"), coin(200, "prune"), false), - }, - expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { - rv := &Fulfillments{ - AskOFs: []*OrderFulfillment{ - filledOF(askOrders[0], 0, - []*OrderSplit{ - {Assets: coin(5, "apple"), Price: coin(25, "prune")}, // Will be BidOFs[0] - {Assets: coin(5, "apple"), Price: coin(25, "prune")}, // Will be BidOFs[1] - }, - ), - filledOF(askOrders[1], 0, - []*OrderSplit{{Assets: coin(15, "apple"), Price: coin(75, "prune")}}, // Will be BidOFs[1] - ), - { - Order: askOrders[2], - AssetsFilledAmt: sdkmath.NewInt(20), - AssetsUnfilledAmt: sdkmath.NewInt(5), - PriceAppliedAmt: sdkmath.NewInt(100), - PriceLeftAmt: sdkmath.NewInt(25), - Splits: []*OrderSplit{{Assets: coin(20, "apple"), Price: coin(100, "prune")}}, // Will be BidOFs[1], - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(100), - PriceUnfilledAmt: sdkmath.NewInt(25), - }, - }, - BidOFs: []*OrderFulfillment{ - filledOF(bidOrders[0], 0, - []*OrderSplit{{Assets: coin(5, "apple"), Price: coin(25, "prune")}}, // Will be AskOFs[0], - ), - filledOF(bidOrders[1], 0, - []*OrderSplit{ - {Assets: coin(5, "apple"), Price: coin(25, "prune")}, // Will be AskOFs[0], - {Assets: coin(15, "apple"), Price: coin(75, "prune")}, // Will be AskOFs[1], - {Assets: coin(20, "apple"), Price: coin(100, "prune")}, // Will be AskOFs[2], - }, - ), - }, - PartialOrder: &PartialFulfillment{ - NewOrder: askOrder(77, coin(5, "apple"), coin(25, "prune"), true), - AssetsFilled: coin(20, "apple"), - PriceFilled: coin(100, "prune"), - }, - } - - rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] - rv.AskOFs[0].Splits[1].Order = rv.BidOFs[1] - rv.AskOFs[1].Splits[0].Order = rv.BidOFs[1] - rv.AskOFs[2].Splits[0].Order = rv.BidOFs[1] - - rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] - rv.BidOFs[1].Splits[0].Order = rv.AskOFs[0] - rv.BidOFs[1].Splits[1].Order = rv.AskOFs[1] - rv.BidOFs[1].Splits[2].Order = rv.AskOFs[2] - - return rv - }, - }, - { - name: "three asks two bids, ask partially filled not allowed", - askOrders: []*Order{ - askOrder(73, coin(10, "apple"), coin(50, "prune"), false), - askOrder(75, coin(15, "apple"), coin(75, "prune"), false), - askOrder(77, coin(25, "apple"), coin(125, "prune"), false), - }, - bidOrders: []*Order{ - bidOrder(74, coin(5, "apple"), coin(25, "prune"), false), - bidOrder(76, coin(40, "apple"), coin(200, "prune"), false), - }, - expErr: "cannot fill ask order 77 having assets \"25apple\" with \"20apple\": order does not allow partial fill", - }, - { - name: "three asks two bids, bid partially filled", - askOrders: []*Order{ - askOrder(121, coin(55, "apple"), coin(275, "prune"), false), - askOrder(123, coin(12, "apple"), coin(60, "prune"), false), - askOrder(125, coin(13, "apple"), coin(65, "prune"), false), - }, - bidOrders: []*Order{ - bidOrder(124, coin(65, "apple"), coin(325, "prune"), false), - bidOrder(126, coin(20, "apple"), coin(100, "prune"), true), - }, - expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { - rv := &Fulfillments{ - AskOFs: []*OrderFulfillment{ - filledOF(askOrders[0], 0, - []*OrderSplit{{Assets: coin(55, "apple"), Price: coin(275, "prune")}}, // Will be BidOFs[0] - ), - filledOF(askOrders[1], 0, - []*OrderSplit{ - {Assets: coin(10, "apple"), Price: coin(50, "prune")}, // Will be BidOFs[0] - {Assets: coin(2, "apple"), Price: coin(10, "prune")}, // Will be BidOFs[1] - }, - ), - filledOF(askOrders[2], 0, - []*OrderSplit{{Assets: coin(13, "apple"), Price: coin(65, "prune")}}, // Will be BidOFs[1] - ), - }, - BidOFs: []*OrderFulfillment{ - filledOF(bidOrders[0], 0, - []*OrderSplit{ - {Assets: coin(55, "apple"), Price: coin(275, "prune")}, // Will be AskOFs[0] - {Assets: coin(10, "apple"), Price: coin(50, "prune")}, // Will be AskOFs[1] - }, - ), - { - Order: bidOrders[1], - AssetsFilledAmt: sdkmath.NewInt(15), - AssetsUnfilledAmt: sdkmath.NewInt(5), - PriceAppliedAmt: sdkmath.NewInt(75), - PriceLeftAmt: sdkmath.NewInt(25), - Splits: []*OrderSplit{ - {Assets: coin(2, "apple"), Price: coin(10, "prune")}, // Will be AskOFs[1] - {Assets: coin(13, "apple"), Price: coin(65, "prune")}, // Will be AskOFs[2] - }, - IsFinalized: true, - PriceFilledAmt: sdkmath.NewInt(75), - PriceUnfilledAmt: sdkmath.NewInt(25), - }, - }, - PartialOrder: &PartialFulfillment{ - NewOrder: bidOrder(126, coin(5, "apple"), coin(25, "prune"), true), - AssetsFilled: coin(15, "apple"), - PriceFilled: coin(75, "prune"), - }, - } - - rv.AskOFs[0].Splits[0].Order = rv.BidOFs[0] - rv.AskOFs[1].Splits[0].Order = rv.BidOFs[0] - rv.AskOFs[1].Splits[1].Order = rv.BidOFs[1] - rv.AskOFs[2].Splits[0].Order = rv.BidOFs[1] - - rv.BidOFs[0].Splits[0].Order = rv.AskOFs[0] - rv.BidOFs[0].Splits[1].Order = rv.AskOFs[1] - rv.BidOFs[1].Splits[0].Order = rv.AskOFs[1] - rv.BidOFs[1].Splits[1].Order = rv.AskOFs[2] - - return rv - }, - }, - { - // TODO[1658]: Either update the process or delete this 2 asks 1 bid unit test. - name: "two asks one bid", - askOrders: []*Order{ - askOrder(91, coin(10, "apple"), coin(49, "prune"), false), - askOrder(93, coin(10, "apple"), coin(51, "prune"), false), - }, - bidOrders: []*Order{bidOrder(92, coin(20, "apple"), coin(100, "prune"), false)}, - expectedMaker: func(t *testing.T, askOrders, bidOrders []*Order) *Fulfillments { - return &Fulfillments{ - AskOFs: []*OrderFulfillment{ - filledOF(askOrders[0], 0, - []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(49, "prune")}}, // Will be BidOFs[0] - ), - filledOF(askOrders[1], 0, - []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(51, "prune")}}, // Will be BidOFs[0] - ), - }, - BidOFs: []*OrderFulfillment{ - filledOF(askOrders[1], 0, - []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(49, "prune")}}, // Will be AskOFs[0] - ), - filledOF(askOrders[1], 0, - []*OrderSplit{{Assets: coin(10, "apple"), Price: coin(51, "prune")}}, // Will be AskOFs[1] - ), - }, - } - }, - }, - { - name: "ask order in bid order list", - askOrders: []*Order{ - askOrder(1, coin(1, "apple"), coin(1, "prune"), false), - bidOrder(2, coin(1, "apple"), coin(1, "prune"), false), - askOrder(3, coin(1, "apple"), coin(1, "prune"), false), - }, - bidOrders: []*Order{bidOrder(4, coin(3, "apple"), coin(3, "prune"), false)}, - expErr: "bid order 2 is not an ask order but is in the askOrders list", - }, - { - name: "bid order in ask order list", - askOrders: []*Order{askOrder(4, coin(3, "apple"), coin(3, "prune"), false)}, - bidOrders: []*Order{ - bidOrder(1, coin(1, "apple"), coin(1, "prune"), false), - askOrder(2, coin(1, "apple"), coin(1, "prune"), false), - bidOrder(3, coin(1, "apple"), coin(1, "prune"), false), - }, - expErr: "ask order 2 is not a bid order but is in the bidOrders list", - }, - // neither filled in full - I'm not sure how I can trigger this. - { - name: "ask finalize error", - askOrders: []*Order{askOrder(15, coin(13, "apple"), coin(17, "prune"), true)}, - bidOrders: []*Order{bidOrder(16, coin(5, "apple"), coin(20, "prune"), true)}, - expErr: "ask order 15 having assets \"13apple\" cannot be partially filled by \"5apple\": price \"17prune\" is not evenly divisible", - }, - { - name: "bid finalize error", - askOrders: []*Order{askOrder(15, coin(5, "apple"), coin(5, "prune"), true)}, - bidOrders: []*Order{bidOrder(16, coin(13, "apple"), coin(17, "prune"), true)}, - expErr: "bid order 16 having assets \"13apple\" cannot be partially filled by \"5apple\": price \"17prune\" is not evenly divisible", - }, - { - name: "validate error", - askOrders: []*Order{askOrder(123, coin(5, "apple"), coin(6, "prune"), true)}, - bidOrders: []*Order{bidOrder(124, coin(5, "apple"), coin(5, "prune"), true)}, - expErr: "ask order 123 having assets \"5apple\" and price \"6prune\" cannot be filled by \"5apple\" at price \"5prune\": insufficient price", - }, - { - name: "nil askOrders", - askOrders: nil, - bidOrders: []*Order{bidOrder(124, coin(5, "apple"), coin(5, "prune"), true)}, - expErr: "no assets filled in bid order 124", - }, - { - name: "empty askOrders", - askOrders: []*Order{}, - bidOrders: []*Order{bidOrder(124, coin(5, "apple"), coin(5, "prune"), true)}, - expErr: "no assets filled in bid order 124", - }, - { - name: "nil bidOrders", - askOrders: []*Order{askOrder(123, coin(5, "apple"), coin(6, "prune"), true)}, - bidOrders: nil, - expErr: "no assets filled in ask order 123", - }, - { - name: "empty bidOrders", - askOrders: []*Order{askOrder(123, coin(5, "apple"), coin(6, "prune"), true)}, - bidOrders: []*Order{}, - expErr: "no assets filled in ask order 123", - }, - { - name: "ask not filled at all", // this gets caught by Finalize. - askOrders: []*Order{ - askOrder(123, coin(10, "apple"), coin(10, "prune"), true), - askOrder(125, coin(5, "apple"), coin(5, "prune"), true), - }, - bidOrders: []*Order{bidOrder(124, coin(5, "apple"), coin(5, "prune"), true)}, - expErr: "no assets filled in ask order 125", - }, - { - name: "bid not filled at all", // this gets caught by Finalize. - askOrders: []*Order{askOrder(123, coin(5, "apple"), coin(5, "prune"), true)}, - bidOrders: []*Order{ - bidOrder(122, coin(10, "apple"), coin(10, "prune"), true), - bidOrder(124, coin(5, "apple"), coin(5, "prune"), true), - }, - expErr: "no assets filled in bid order 124", - }, - // both ask and bid partially filled - I'm not sure how to trigger this. + "order 2 having price left \"%dtwo\": zero or negative price left", + c.of1Unfilled, c.of2Unfilled) + newTests[1].expErr = fmt.Sprintf("cannot fill bid order 1 having price left \"%done\" with ask "+ + "order 2 having price left \"%dtwo\": zero or negative price left", + c.of1Unfilled, c.of2Unfilled) + newTests[2].expErr = fmt.Sprintf("cannot fill ask order 1 having price left \"%done\" with ask "+ + "order 2 having price left \"%dtwo\": zero or negative price left", + c.of1Unfilled, c.of2Unfilled) + newTests[3].expErr = fmt.Sprintf("cannot fill bid order 1 having price left \"%done\" with bid "+ + "order 2 having price left \"%dtwo\": zero or negative price left", + c.of1Unfilled, c.of2Unfilled) + } + tests = append(tests, newTests...) } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var expected *Fulfillments - if tc.expectedMaker != nil { - expected = tc.expectedMaker(t, tc.askOrders, tc.bidOrders) + if len(tc.expErr) > 0 { + tc.expAmt = sdkmath.ZeroInt() } - var actual *Fulfillments + origOF1 := copyOrderFulfillment(tc.of1) + origOF2 := copyOrderFulfillment(tc.of2) + + var amt sdkmath.Int var err error testFunc := func() { - actual, err = BuildFulfillments(tc.askOrders, tc.bidOrders, tc.sellerFeeRatio) - } - require.NotPanics(t, testFunc, "BuildFulfillments") - assertions.AssertErrorValue(t, err, tc.expErr, "BuildFulfillments error") - if !assert.Equal(t, expected, actual, "BuildFulfillments result") && expected != nil && actual != nil { - // Try to help identify the error in the failure logs. - expAskOFs := orderFulfillmentsString(expected.AskOFs) - actAskOFs := orderFulfillmentsString(actual.AskOFs) - if assert.Equal(t, expAskOFs, actAskOFs, "AskOFs") { - // Some difference don't come through in the strings, so dig even deeper. - for i := range expected.AskOFs { - assertEqualOrderFulfillments(t, expected.AskOFs[i], actual.AskOFs[i], "AskOFs[%d]", i) - } - } - expBidOFs := orderFulfillmentsString(expected.BidOFs) - actBidOFs := orderFulfillmentsString(actual.BidOFs) - if assert.Equal(t, expBidOFs, actBidOFs, "BidOFs") { - for i := range expected.BidOFs { - assertEqualOrderFulfillments(t, expected.BidOFs[i], actual.BidOFs[i], "BidOFs[%d]", i) - } - } - expPartial := partialFulfillmentString(expected.PartialOrder) - actPartial := partialFulfillmentString(actual.PartialOrder) - assert.Equal(t, expPartial, actPartial, "PartialOrder") + amt, err = getFulfillmentPriceAmt(tc.of1, tc.of2) } + require.NotPanics(t, testFunc, "getFulfillmentPriceAmt") + assertions.AssertErrorValue(t, err, tc.expErr, "getFulfillmentPriceAmt error") + assert.Equal(t, tc.expAmt, amt, "getFulfillmentPriceAmt amount") + assertEqualOrderFulfillments(t, origOF1, tc.of1, "of1 after getFulfillmentPriceAmt") + assertEqualOrderFulfillments(t, origOF2, tc.of2, "of2 after getFulfillmentPriceAmt") }) } } -// copyIndexedAddrAmts creates a deep copy of an indexedAddrAmts. -func copyIndexedAddrAmts(orig *indexedAddrAmts) *indexedAddrAmts { - if orig == nil { - return nil - } - - rv := &indexedAddrAmts{ - addrs: nil, - amts: nil, - indexes: nil, - } - - if orig.addrs != nil { - rv.addrs = make([]string, 0, len(orig.addrs)) - rv.addrs = append(rv.addrs, orig.addrs...) +func TestSetFeesToPay(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } - - if orig.amts != nil { - rv.amts = make([]sdk.Coins, len(orig.amts)) - for i, amt := range orig.amts { - rv.amts[i] = copyCoins(amt) + askOF := func(orderID uint64, priceAppliedAmt int64, fees ...sdk.Coin) *orderFulfillment { + askOrder := &AskOrder{Price: coin(50, "plum")} + if len(fees) > 1 { + t.Fatalf("cannot provide more than one fee to askOF(%d, %d, %q)", + orderID, priceAppliedAmt, fees) } - } - - if orig.indexes != nil { - rv.indexes = make(map[string]int, len(orig.indexes)) - for k, v := range orig.indexes { - rv.indexes[k] = v + if len(fees) > 0 { + askOrder.SellerSettlementFlatFee = &fees[0] } - } - - return rv -} - -// String converts a indexedAddrAmtsString to a string. -// This is mostly because test failure output of sdk.Coin and sdk.Coins is impossible to understand. -func indexedAddrAmtsString(i *indexedAddrAmts) string { - if i == nil { - return "nil" - } - - addrs := "nil" - if i.addrs != nil { - addrsVals := make([]string, len(i.addrs)) - for j, addr := range i.addrs { - addrsVals[j] = fmt.Sprintf("%q", addr) + return &orderFulfillment{ + Order: NewOrder(orderID).WithAsk(askOrder), + PriceAppliedAmt: sdkmath.NewInt(priceAppliedAmt), } - addrs = fmt.Sprintf("%T{%s}", i.addrs, strings.Join(addrsVals, ", ")) } - - amts := "nil" - if i.amts != nil { - amtsVals := make([]string, len(i.amts)) - for j, amt := range i.amts { - amtsVals[j] = fmt.Sprintf("%q", amt) + bidOF := func(orderID uint64, priceAppliedAmt int64, fees ...sdk.Coin) *orderFulfillment { + bidOrder := &BidOrder{Price: coin(50, "plum")} + if len(fees) > 0 { + bidOrder.BuyerSettlementFees = fees } - amts = fmt.Sprintf("[]%T{%s}", i.amts, strings.Join(amtsVals, ", ")) - } - - indexes := "nil" - if i.indexes != nil { - indexVals := make([]string, 0, len(i.indexes)) - for k, v := range i.indexes { - indexVals = append(indexVals, fmt.Sprintf("%q: %d", k, v)) + return &orderFulfillment{ + Order: NewOrder(orderID).WithBid(bidOrder), + PriceAppliedAmt: sdkmath.NewInt(priceAppliedAmt), } - sort.Strings(indexVals) - indexes = fmt.Sprintf("%T{%s}", i.indexes, strings.Join(indexVals, ", ")) - } - - return fmt.Sprintf("%T{addrs:%s, amts:%s, indexes:%s}", i, addrs, amts, indexes) -} - -func TestNewIndexedAddrAmts(t *testing.T) { - expected := &indexedAddrAmts{ - addrs: nil, - amts: nil, - indexes: make(map[string]int), } - actual := newIndexedAddrAmts() - assert.Equal(t, expected, actual, "newIndexedAddrAmts result") - key := "test" - require.NotPanics(t, func() { - _ = actual.indexes[key] - }, "getting value of actual.indexes[%q]", key) -} - -func TestIndexedAddrAmts_Add(t *testing.T) { - coins := func(coins string) sdk.Coins { - rv, err := sdk.ParseCoinsNormalized(coins) - require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) - return rv + expOF := func(f *orderFulfillment, feesToPay ...sdk.Coin) *orderFulfillment { + if len(feesToPay) > 0 { + f.FeesToPay = sdk.NewCoins(feesToPay...) + } + return f } - negCoins := sdk.Coins{sdk.Coin{Denom: "neg", Amount: sdkmath.NewInt(-1)}} tests := []struct { - name string - receiver *indexedAddrAmts - addr string - coins []sdk.Coin - expected *indexedAddrAmts - expPanic string + name string + askOFs []*orderFulfillment + bidOFs []*orderFulfillment + ratio *FeeRatio + expAskOFs []*orderFulfillment + expBidOFs []*orderFulfillment + expErr string }{ { - name: "empty, add one coin", - receiver: newIndexedAddrAmts(), - addr: "addr1", - coins: coins("1one"), - expected: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{coins("1one")}, - indexes: map[string]int{"addr1": 0}, - }, - }, - { - name: "empty, add two coins", - receiver: newIndexedAddrAmts(), - addr: "addr1", - coins: coins("1one,2two"), - expected: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{coins("1one,2two")}, - indexes: map[string]int{"addr1": 0}, - }, - }, - { - name: "empty, add neg coins", - receiver: newIndexedAddrAmts(), - addr: "addr1", - coins: negCoins, - expPanic: "cannot index and add invalid coin amount \"-1neg\"", - }, - { - name: "one addr, add to existing new denom", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{coins("1one")}, - indexes: map[string]int{"addr1": 0}, - }, - addr: "addr1", - coins: coins("2two"), - expected: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{coins("1one,2two")}, - indexes: map[string]int{"addr1": 0}, + name: "cannot apply ratio", + askOFs: []*orderFulfillment{ + askOF(7777, 55, coin(20, "grape")), + askOF(5555, 71), + askOF(6666, 100), }, - }, - { - name: "one addr, add to existing same denom", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{coins("1one")}, - indexes: map[string]int{"addr1": 0}, + bidOFs: []*orderFulfillment{ + bidOF(1111, 100), + bidOF(2222, 200, coin(20, "grape")), + bidOF(3333, 300), }, - addr: "addr1", - coins: coins("3one"), - expected: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{coins("4one")}, - indexes: map[string]int{"addr1": 0}, + ratio: &FeeRatio{Price: coin(30, "peach"), Fee: coin(1, "fig")}, + expAskOFs: []*orderFulfillment{ + expOF(askOF(7777, 55, coin(20, "grape"))), + expOF(askOF(5555, 71)), + expOF(askOF(6666, 100)), }, - }, - { - name: "one addr, add negative to existing", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{coins("1one")}, - indexes: map[string]int{"addr1": 0}, + expBidOFs: []*orderFulfillment{ + expOF(bidOF(1111, 100)), + expOF(bidOF(2222, 200, coin(20, "grape")), coin(20, "grape")), + expOF(bidOF(3333, 300)), }, - addr: "addr1", - coins: negCoins, - expPanic: "cannot index and add invalid coin amount \"-1neg\"", + expErr: joinErrs( + "failed calculate ratio fee for ask order 7777: cannot apply ratio 30peach:1fig to price 55plum: incorrect price denom", + "failed calculate ratio fee for ask order 5555: cannot apply ratio 30peach:1fig to price 71plum: incorrect price denom", + "failed calculate ratio fee for ask order 6666: cannot apply ratio 30peach:1fig to price 100plum: incorrect price denom", + ), }, { - name: "one addr, add to new", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{coins("1one")}, - indexes: map[string]int{"addr1": 0}, - }, - addr: "addr2", - coins: coins("2two"), - expected: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2"}, - amts: []sdk.Coins{coins("1one"), coins("2two")}, - indexes: map[string]int{"addr1": 0, "addr2": 1}, + name: "no ratio", + askOFs: []*orderFulfillment{ + askOF(7777, 55, coin(20, "grape")), + askOF(5555, 71), + askOF(6666, 100), }, - }, - { - name: "one addr, add to new opposite order", - receiver: &indexedAddrAmts{ - addrs: []string{"addr2"}, - amts: []sdk.Coins{coins("2two")}, - indexes: map[string]int{"addr2": 0}, + bidOFs: []*orderFulfillment{ + bidOF(1111, 100), + bidOF(2222, 200, coin(20, "grape")), + bidOF(3333, 300), }, - addr: "addr1", - coins: coins("1one"), - expected: &indexedAddrAmts{ - addrs: []string{"addr2", "addr1"}, - amts: []sdk.Coins{coins("2two"), coins("1one")}, - indexes: map[string]int{"addr2": 0, "addr1": 1}, + ratio: nil, + expAskOFs: []*orderFulfillment{ + expOF(askOF(7777, 55, coin(20, "grape")), coin(20, "grape")), + expOF(askOF(5555, 71)), + expOF(askOF(6666, 100)), }, - }, - { - name: "one addr, add negative to new", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{coins("1one")}, - indexes: map[string]int{"addr1": 0}, + expBidOFs: []*orderFulfillment{ + expOF(bidOF(1111, 100)), + expOF(bidOF(2222, 200, coin(20, "grape")), coin(20, "grape")), + expOF(bidOF(3333, 300)), }, - addr: "addr2", - coins: negCoins, - expPanic: "cannot index and add invalid coin amount \"-1neg\"", }, { - name: "three addrs, add to first", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, - indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + name: "with ratio", + askOFs: []*orderFulfillment{ + askOF(7777, 55, coin(20, "grape")), + askOF(5555, 71), + askOF(6666, 100), }, - addr: "addr1", - coins: coins("10one"), - expected: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("11one"), coins("2two"), coins("3three")}, - indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + bidOFs: []*orderFulfillment{ + bidOF(1111, 100), + bidOF(2222, 200, coin(20, "grape")), + bidOF(3333, 300), }, - }, - { - name: "three addrs, add to second", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, - indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + ratio: &FeeRatio{Price: coin(30, "plum"), Fee: coin(1, "fig")}, + expAskOFs: []*orderFulfillment{ + expOF(askOF(7777, 55, coin(20, "grape")), coin(2, "fig"), coin(20, "grape")), + expOF(askOF(5555, 71), coin(3, "fig")), + expOF(askOF(6666, 100), coin(4, "fig")), }, - addr: "addr2", - coins: coins("10two"), - expected: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("1one"), coins("12two"), coins("3three")}, - indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + expBidOFs: []*orderFulfillment{ + expOF(bidOF(1111, 100)), + expOF(bidOF(2222, 200, coin(20, "grape")), coin(20, "grape")), + expOF(bidOF(3333, 300)), }, }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = setFeesToPay(tc.askOFs, tc.bidOFs, tc.ratio) + } + require.NotPanics(t, testFunc, "setFeesToPay") + assertions.AssertErrorValue(t, err, tc.expErr, "setFeesToPay error") + assertEqualOrderFulfillmentSlices(t, tc.expAskOFs, tc.askOFs, "askOFs after setFeesToPay") + assertEqualOrderFulfillmentSlices(t, tc.expBidOFs, tc.bidOFs, "bidOFs after setFeesToPay") + }) + } +} + +func TestValidateFulfillments(t *testing.T) { + goodAskOF := func(orderID uint64) *orderFulfillment { + return &orderFulfillment{ + Order: NewOrder(orderID).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("peach", 123), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + PriceAppliedAmt: sdkmath.NewInt(130), + } + } + badAskOF := func(orderID uint64) *orderFulfillment { + rv := goodAskOF(orderID) + rv.AssetsFilledAmt = sdkmath.NewInt(49) + return rv + } + badAskErr := func(orderID uint64) string { + return badAskOF(orderID).Validate().Error() + } + goodBidOF := func(orderID uint64) *orderFulfillment { + return &orderFulfillment{ + Order: NewOrder(orderID).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 50), + Price: sdk.NewInt64Coin("peach", 123), + }), + AssetsFilledAmt: sdkmath.NewInt(50), + PriceAppliedAmt: sdkmath.NewInt(123), + } + } + badBidOF := func(orderID uint64) *orderFulfillment { + rv := goodBidOF(orderID) + rv.AssetsFilledAmt = sdkmath.NewInt(49) + return rv + } + badBidErr := func(orderID uint64) string { + return badBidOF(orderID).Validate().Error() + } + + tests := []struct { + name string + askOFs []*orderFulfillment + bidOFs []*orderFulfillment + expErr string + }{ { - name: "three addrs, add to third", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, - indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, - }, - addr: "addr3", - coins: coins("10three"), - expected: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("1one"), coins("2two"), coins("13three")}, - indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, - }, + name: "all good", + askOFs: []*orderFulfillment{goodAskOF(10), goodAskOF(11), goodAskOF(12)}, + bidOFs: []*orderFulfillment{goodBidOF(20), goodBidOF(21), goodBidOF(22)}, + expErr: "", }, { - name: "three addrs, add two coins to second", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, - indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, - }, - addr: "addr2", - coins: coins("10four,20two"), - expected: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("1one"), coins("10four,22two"), coins("3three")}, - indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, - }, + name: "error in one ask", + askOFs: []*orderFulfillment{goodAskOF(10), badAskOF(11), goodAskOF(12)}, + bidOFs: []*orderFulfillment{goodBidOF(20), goodBidOF(21), goodBidOF(22)}, + expErr: badAskErr(11), }, { - name: "three addrs, add to new", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, - indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, - }, - addr: "good buddy", - coins: coins("10four"), - expected: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3", "good buddy"}, - amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three"), coins("10four")}, - indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2, "good buddy": 3}, - }, + name: "error in one bid", + askOFs: []*orderFulfillment{goodAskOF(10), goodAskOF(11), goodAskOF(12)}, + bidOFs: []*orderFulfillment{goodBidOF(20), badBidOF(21), goodBidOF(22)}, + expErr: badBidErr(21), }, { - name: "three addrs, add negative to second", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, - indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, - }, - addr: "addr2", - coins: negCoins, - expPanic: "cannot index and add invalid coin amount \"-1neg\"", + name: "two errors in asks", + askOFs: []*orderFulfillment{badAskOF(10), goodAskOF(11), badAskOF(12)}, + bidOFs: []*orderFulfillment{goodBidOF(20), goodBidOF(21), goodBidOF(22)}, + expErr: joinErrs(badAskErr(10), badAskErr(12)), }, { - name: "three addrs, add negative to new", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, - indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, - }, - addr: "addr4", - coins: negCoins, - expPanic: "cannot index and add invalid coin amount \"-1neg\"", + name: "two errors in bids", + askOFs: []*orderFulfillment{goodAskOF(10), goodAskOF(11), goodAskOF(12)}, + bidOFs: []*orderFulfillment{badBidOF(20), goodBidOF(21), badBidOF(22)}, + expErr: joinErrs(badBidErr(20), badBidErr(22)), + }, + { + name: "error in each", + askOFs: []*orderFulfillment{goodAskOF(10), goodAskOF(11), badAskOF(12)}, + bidOFs: []*orderFulfillment{goodBidOF(20), badBidOF(21), goodBidOF(22)}, + expErr: joinErrs(badAskErr(12), badBidErr(21)), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - orig := copyIndexedAddrAmts(tc.receiver) - defer func() { - if t.Failed() { - t.Logf("Original: %s", indexedAddrAmtsString(orig)) - t.Logf(" Actual: %s", indexedAddrAmtsString(tc.receiver)) - t.Logf("Expected: %s", indexedAddrAmtsString(tc.expected)) - } - }() - + var err error testFunc := func() { - tc.receiver.add(tc.addr, tc.coins...) - } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "add(%q, %q)", tc.addr, tc.coins) - if len(tc.expPanic) == 0 { - assert.Equal(t, tc.expected, tc.receiver, "receiver after add(%q, %q)", tc.addr, tc.coins) + err = validateFulfillments(tc.askOFs, tc.bidOFs) } + require.NotPanics(t, testFunc, "validateFulfillments") + assertions.AssertErrorValue(t, err, tc.expErr, "validateFulfillments error") }) } } -func TestIndexedAddrAmts_GetAsInputs(t *testing.T) { - coins := func(coins string) sdk.Coins { - rv, err := sdk.ParseCoinsNormalized(coins) - require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) - return rv +func TestOrderFulfillment_Validate(t *testing.T) { + askOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + return NewOrder(orderID).WithAsk(&AskOrder{ + MarketId: 987, + Seller: "steve", + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, + Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(priceAmt)}, + }) + } + bidOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { + return NewOrder(orderID).WithBid(&BidOrder{ + MarketId: 987, + Buyer: "bruce", + Assets: sdk.Coin{Denom: "apple", Amount: sdkmath.NewInt(assetsAmt)}, + Price: sdk.Coin{Denom: "peach", Amount: sdkmath.NewInt(priceAmt)}, + }) } tests := []struct { - name string - receiver *indexedAddrAmts - expected []banktypes.Input - expPanic string + name string + f orderFulfillment + expErr string }{ - {name: "nil receiver", receiver: nil, expected: nil}, - {name: "no addrs", receiver: newIndexedAddrAmts(), expected: nil}, { - name: "one addr negative amount", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{{{Denom: "neg", Amount: sdkmath.NewInt(-1)}}}, - indexes: map[string]int{ - "addr1": 0, - }, + name: "nil inside order", + f: orderFulfillment{Order: NewOrder(8)}, + expErr: nilSubTypeErr(8), + }, + { + name: "unknown inside order", + f: orderFulfillment{Order: newUnknownOrder(12)}, + expErr: unknownSubTypeErr(12), + }, + { + name: "order price greater than price applied: ask", + f: orderFulfillment{ + Order: askOrder(52, 10, 401), + PriceAppliedAmt: sdkmath.NewInt(400), + AssetsFilledAmt: sdkmath.NewInt(10), + }, + expErr: "ask order 52 price \"401peach\" is more than price filled \"400peach\"", + }, + { + name: "order price equal to price applied: ask", + f: orderFulfillment{ + Order: askOrder(53, 10, 401), + PriceAppliedAmt: sdkmath.NewInt(401), + AssetsFilledAmt: sdkmath.NewInt(10), + }, + expErr: "", + }, + { + name: "order price less than price applied: ask", + f: orderFulfillment{ + Order: askOrder(54, 10, 401), + PriceAppliedAmt: sdkmath.NewInt(402), + AssetsFilledAmt: sdkmath.NewInt(10), }, - expPanic: "invalid indexed amount \"addr1\" for address \"-1neg\": cannot be zero or negative", + expErr: "", }, { - name: "one addr zero amount", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{{{Denom: "zero", Amount: sdkmath.NewInt(0)}}}, - indexes: map[string]int{ - "addr1": 0, - }, + name: "order price greater than price applied: bid", + f: orderFulfillment{ + Order: bidOrder(71, 17, 432), + PriceAppliedAmt: sdkmath.NewInt(431), + AssetsFilledAmt: sdkmath.NewInt(17), }, - expPanic: "invalid indexed amount \"addr1\" for address \"0zero\": cannot be zero or negative", + expErr: "bid order 71 price \"432peach\" is not equal to price filled \"431peach\"", }, { - name: "one addr positive amount", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{coins("1one")}, - indexes: map[string]int{ - "addr1": 0, - }, + name: "order price equal to price applied: bid", + f: orderFulfillment{ + Order: bidOrder(72, 17, 432), + PriceAppliedAmt: sdkmath.NewInt(432), + AssetsFilledAmt: sdkmath.NewInt(17), }, - expected: []banktypes.Input{ - {Address: "addr1", Coins: coins("1one")}, + expErr: "", + }, + { + name: "order price less than price applied: bid", + f: orderFulfillment{ + Order: bidOrder(73, 17, 432), + PriceAppliedAmt: sdkmath.NewInt(433), + AssetsFilledAmt: sdkmath.NewInt(17), }, + expErr: "bid order 73 price \"432peach\" is not equal to price filled \"433peach\"", }, { - name: "two addrs", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2"}, - amts: []sdk.Coins{coins("1one"), coins("2two,3three")}, - indexes: map[string]int{ - "addr1": 0, - "addr2": 1, - }, + name: "order assets less than assets filled: ask", + f: orderFulfillment{ + Order: askOrder(101, 53, 12345), + PriceAppliedAmt: sdkmath.NewInt(12345), + AssetsFilledAmt: sdkmath.NewInt(54), }, - expected: []banktypes.Input{ - {Address: "addr1", Coins: coins("1one")}, - {Address: "addr2", Coins: coins("2two,3three")}, + expErr: "ask order 101 assets \"53apple\" does not equal filled assets \"54apple\"", + }, + { + name: "order assets equal to assets filled: ask", + f: orderFulfillment{ + Order: askOrder(202, 53, 12345), + PriceAppliedAmt: sdkmath.NewInt(12345), + AssetsFilledAmt: sdkmath.NewInt(53), }, + expErr: "", }, { - name: "three addrs", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("1one"), coins("2two,3three"), coins("4four,5five,6six")}, - indexes: map[string]int{ - "addr1": 0, - "addr2": 1, - "addr3": 2, - }, + name: "order assets more than assets filled: ask", + f: orderFulfillment{ + Order: askOrder(303, 53, 12345), + PriceAppliedAmt: sdkmath.NewInt(12345), + AssetsFilledAmt: sdkmath.NewInt(52), }, - expected: []banktypes.Input{ - {Address: "addr1", Coins: coins("1one")}, - {Address: "addr2", Coins: coins("2two,3three")}, - {Address: "addr3", Coins: coins("4four,5five,6six")}, + expErr: "ask order 303 assets \"53apple\" does not equal filled assets \"52apple\"", + }, + { + name: "order assets less than assets filled: bid", + f: orderFulfillment{ + Order: bidOrder(404, 53, 12345), + PriceAppliedAmt: sdkmath.NewInt(12345), + AssetsFilledAmt: sdkmath.NewInt(54), }, + expErr: "bid order 404 assets \"53apple\" does not equal filled assets \"54apple\"", }, { - name: "three addrs, negative in third", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{ - coins("1one"), - coins("2two,3three"), - { - {Denom: "acoin", Amount: sdkmath.NewInt(4)}, - {Denom: "bcoin", Amount: sdkmath.NewInt(5)}, - {Denom: "ncoin", Amount: sdkmath.NewInt(-6)}, - {Denom: "zcoin", Amount: sdkmath.NewInt(7)}, - }, - }, - indexes: map[string]int{ - "addr1": 0, - "addr2": 1, - "addr3": 2, - }, + name: "order assets equal to assets filled: bid", + f: orderFulfillment{ + Order: bidOrder(505, 53, 12345), + PriceAppliedAmt: sdkmath.NewInt(12345), + AssetsFilledAmt: sdkmath.NewInt(53), }, - expPanic: "invalid indexed amount \"addr3\" for address \"4acoin,5bcoin,-6ncoin,7zcoin\": cannot be zero or negative", + expErr: "", + }, + { + name: "order assets more than assets filled: bid", + f: orderFulfillment{ + Order: bidOrder(606, 53, 12345), + PriceAppliedAmt: sdkmath.NewInt(12345), + AssetsFilledAmt: sdkmath.NewInt(52), + }, + expErr: "bid order 606 assets \"53apple\" does not equal filled assets \"52apple\"", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - orig := copyIndexedAddrAmts(tc.receiver) - var actual []banktypes.Input + var err error testFunc := func() { - actual = tc.receiver.getAsInputs() - } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAsInputs()") - assert.Equal(t, tc.expected, actual, "getAsInputs() result") - if !assert.Equal(t, orig, tc.receiver, "receiver before and after getAsInputs()") { - t.Logf("Before: %s", indexedAddrAmtsString(orig)) - t.Logf(" After: %s", indexedAddrAmtsString(tc.receiver)) + err = tc.f.Validate() } + require.NotPanics(t, testFunc, "Validate") + assertions.AssertErrorValue(t, err, tc.expErr, "Validate error") }) } } -func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { - coins := func(coins string) sdk.Coins { - rv, err := sdk.ParseCoinsNormalized(coins) - require.NoError(t, err, "sdk.ParseCoinsNormalized(%q)", coins) - return rv - } - +func TestBuildTransfers(t *testing.T) { tests := []struct { - name string - receiver *indexedAddrAmts - expected []banktypes.Output - expPanic string + name string + askOFs []*orderFulfillment + bidOFs []*orderFulfillment + expSettlement *Settlement + expErr string }{ - {name: "nil receiver", receiver: nil, expected: nil}, - {name: "no addrs", receiver: newIndexedAddrAmts(), expected: nil}, { - name: "one addr negative amount", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{{{Denom: "neg", Amount: sdkmath.NewInt(-1)}}}, - indexes: map[string]int{ - "addr1": 0, + name: "ask with negative assets filled", + askOFs: []*orderFulfillment{ + { + Order: NewOrder(18).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 15), + Price: sdk.NewInt64Coin("plum", 42), + }), + AssetsFilledAmt: sdkmath.NewInt(-1), + PriceAppliedAmt: sdkmath.NewInt(-1), }, }, - expPanic: "invalid indexed amount \"addr1\" for address \"-1neg\": cannot be zero or negative", + expErr: "ask order 18 cannot be filled with \"-1apple\" assets: amount not positive", }, { - name: "one addr zero amount", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{{{Denom: "zero", Amount: sdkmath.NewInt(0)}}}, - indexes: map[string]int{ - "addr1": 0, + name: "bid with negative assets filled", + bidOFs: []*orderFulfillment{ + { + Order: NewOrder(12).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 15), + Price: sdk.NewInt64Coin("plum", 42), + }), + AssetsFilledAmt: sdkmath.NewInt(-1), + PriceAppliedAmt: sdkmath.NewInt(-1), }, }, - expPanic: "invalid indexed amount \"addr1\" for address \"0zero\": cannot be zero or negative", + expErr: "bid order 12 cannot be filled at price \"-1plum\": amount not positive", }, { - name: "one addr positive amount", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1"}, - amts: []sdk.Coins{coins("1one")}, - indexes: map[string]int{ - "addr1": 0, + name: "ask with negative fees to pay", + askOFs: []*orderFulfillment{ + { + Order: NewOrder(53).WithAsk(&AskOrder{ + Assets: sdk.NewInt64Coin("apple", 15), + Price: sdk.NewInt64Coin("plum", 42), + }), + AssetsFilledAmt: sdkmath.NewInt(15), + AssetDists: []*distribution{{Address: "buyer1", Amount: sdkmath.NewInt(15)}}, + PriceAppliedAmt: sdkmath.NewInt(42), + PriceDists: []*distribution{{Address: "seller1", Amount: sdkmath.NewInt(42)}}, + FeesToPay: sdk.Coins{sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(-1)}}, }, }, - expected: []banktypes.Output{ - {Address: "addr1", Coins: coins("1one")}, - }, + expErr: "ask order 53 cannot pay \"-1fig\" in fees: negative amount", }, { - name: "two addrs", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2"}, - amts: []sdk.Coins{coins("1one"), coins("2two,3three")}, - indexes: map[string]int{ - "addr1": 0, - "addr2": 1, + name: "bid with negative fees to pay", + bidOFs: []*orderFulfillment{ + { + Order: NewOrder(35).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 15), + Price: sdk.NewInt64Coin("plum", 42), + }), + AssetsFilledAmt: sdkmath.NewInt(15), + AssetDists: []*distribution{{Address: "seller1", Amount: sdkmath.NewInt(15)}}, + PriceAppliedAmt: sdkmath.NewInt(42), + PriceDists: []*distribution{{Address: "seller1", Amount: sdkmath.NewInt(42)}}, + FeesToPay: sdk.Coins{sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(-1)}}, }, }, - expected: []banktypes.Output{ - {Address: "addr1", Coins: coins("1one")}, - {Address: "addr2", Coins: coins("2two,3three")}, - }, + expErr: "bid order 35 cannot pay \"-1fig\" in fees: negative amount", }, { - name: "three addrs", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{coins("1one"), coins("2two,3three"), coins("4four,5five,6six")}, - indexes: map[string]int{ - "addr1": 0, - "addr2": 1, - "addr3": 2, + name: "two asks, three bids", + askOFs: []*orderFulfillment{ + { + Order: NewOrder(77).WithAsk(&AskOrder{ + Seller: "seller77", + Assets: sdk.NewInt64Coin("apple", 15), + Price: sdk.NewInt64Coin("plum", 42), + }), + AssetsFilledAmt: sdkmath.NewInt(15), + AssetDists: []*distribution{ + {Address: "buyer5511", Amount: sdkmath.NewInt(15)}, + }, + PriceAppliedAmt: sdkmath.NewInt(43), + PriceDists: []*distribution{ + {Address: "buyer5511", Amount: sdkmath.NewInt(30)}, + {Address: "buyer78", Amount: sdkmath.NewInt(12)}, + {Address: "buyer9001", Amount: sdkmath.NewInt(1)}, + }, + FeesToPay: sdk.Coins{sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(11)}}, + }, + { + Order: NewOrder(3).WithAsk(&AskOrder{ + Seller: "seller3", + Assets: sdk.NewInt64Coin("apple", 43), + Price: sdk.NewInt64Coin("plum", 88), + }), + AssetsFilledAmt: sdkmath.NewInt(43), + AssetDists: []*distribution{ + {Address: "buyer5511", Amount: sdkmath.NewInt(5)}, + {Address: "buyer78", Amount: sdkmath.NewInt(7)}, + {Address: "buyer9001", Amount: sdkmath.NewInt(31)}, + }, + PriceAppliedAmt: sdkmath.NewInt(90), + PriceDists: []*distribution{ + {Address: "buyer78", Amount: sdkmath.NewInt(5)}, + {Address: "buyer9001", Amount: sdkmath.NewInt(83)}, + {Address: "buyer9001", Amount: sdkmath.NewInt(2)}, + }, + FeesToPay: nil, }, }, - expected: []banktypes.Output{ - {Address: "addr1", Coins: coins("1one")}, - {Address: "addr2", Coins: coins("2two,3three")}, - {Address: "addr3", Coins: coins("4four,5five,6six")}, + bidOFs: []*orderFulfillment{ + { + Order: NewOrder(5511).WithBid(&BidOrder{ + Buyer: "buyer5511", + Assets: sdk.NewInt64Coin("apple", 20), + Price: sdk.NewInt64Coin("plum", 30), + }), + AssetsFilledAmt: sdkmath.NewInt(20), + AssetDists: []*distribution{ + {Address: "seller77", Amount: sdkmath.NewInt(15)}, + {Address: "seller3", Amount: sdkmath.NewInt(5)}, + }, + PriceAppliedAmt: sdkmath.NewInt(30), + PriceDists: []*distribution{ + {Address: "seller77", Amount: sdkmath.NewInt(30)}, + }, + FeesToPay: sdk.Coins{sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(10)}}, + }, + { + Order: NewOrder(78).WithBid(&BidOrder{ + Buyer: "buyer78", + Assets: sdk.NewInt64Coin("apple", 7), + Price: sdk.NewInt64Coin("plum", 15), + }), + AssetsFilledAmt: sdkmath.NewInt(7), + AssetDists: []*distribution{ + {Address: "seller3", Amount: sdkmath.NewInt(7)}, + }, + PriceAppliedAmt: sdkmath.NewInt(15), + PriceDists: []*distribution{ + {Address: "seller77", Amount: sdkmath.NewInt(12)}, + {Address: "seller3", Amount: sdkmath.NewInt(3)}, + }, + FeesToPay: sdk.Coins{sdk.Coin{Denom: "grape", Amount: sdkmath.NewInt(4)}}, + }, + { + Order: NewOrder(9001).WithBid(&BidOrder{ + Buyer: "buyer9001", + Assets: sdk.NewInt64Coin("apple", 31), + Price: sdk.NewInt64Coin("plum", 86), + }), + AssetsFilledAmt: sdkmath.NewInt(31), + AssetDists: []*distribution{ + {Address: "seller3", Amount: sdkmath.NewInt(31)}, + }, + PriceAppliedAmt: sdkmath.NewInt(86), + PriceDists: []*distribution{ + {Address: "seller3", Amount: sdkmath.NewInt(83)}, + {Address: "seller77", Amount: sdkmath.NewInt(2)}, + {Address: "seller3", Amount: sdkmath.NewInt(1)}, + }, + FeesToPay: nil, + }, }, - }, - { - name: "three addrs, negative in third", - receiver: &indexedAddrAmts{ - addrs: []string{"addr1", "addr2", "addr3"}, - amts: []sdk.Coins{ - coins("1one"), - coins("2two,3three"), + expSettlement: &Settlement{ + Transfers: []*Transfer{ { - {Denom: "acoin", Amount: sdkmath.NewInt(4)}, - {Denom: "bcoin", Amount: sdkmath.NewInt(5)}, - {Denom: "ncoin", Amount: sdkmath.NewInt(-6)}, - {Denom: "zcoin", Amount: sdkmath.NewInt(7)}, + Inputs: []banktypes.Input{{Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 15))}}, + Outputs: []banktypes.Output{{Address: "buyer5511", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 15))}}, + }, + { + Inputs: []banktypes.Input{{Address: "seller3", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 43))}}, + Outputs: []banktypes.Output{ + {Address: "buyer5511", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 5))}, + {Address: "buyer78", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 7))}, + {Address: "buyer9001", Coins: sdk.NewCoins(sdk.NewInt64Coin("apple", 31))}, + }, + }, + { + Inputs: []banktypes.Input{{Address: "buyer5511", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 30))}}, + Outputs: []banktypes.Output{{Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 30))}}, + }, + { + Inputs: []banktypes.Input{{Address: "buyer78", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 15))}}, + Outputs: []banktypes.Output{ + {Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 12))}, + {Address: "seller3", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 3))}, + }, + }, + { + Inputs: []banktypes.Input{{Address: "buyer9001", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 86))}}, + Outputs: []banktypes.Output{ + {Address: "seller3", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 84))}, + {Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("plum", 2))}, + }, }, }, - indexes: map[string]int{ - "addr1": 0, - "addr2": 1, - "addr3": 2, + FeeInputs: []banktypes.Input{ + {Address: "seller77", Coins: sdk.NewCoins(sdk.NewInt64Coin("fig", 11))}, + {Address: "buyer5511", Coins: sdk.NewCoins(sdk.NewInt64Coin("fig", 10))}, + {Address: "buyer78", Coins: sdk.NewCoins(sdk.NewInt64Coin("grape", 4))}, }, }, - expPanic: "invalid indexed amount \"addr3\" for address \"4acoin,5bcoin,-6ncoin,7zcoin\": cannot be zero or negative", + expErr: "", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - orig := copyIndexedAddrAmts(tc.receiver) - var actual []banktypes.Output + settlement := &Settlement{} + if tc.expSettlement == nil { + tc.expSettlement = &Settlement{} + } + var err error testFunc := func() { - actual = tc.receiver.getAsOutputs() + err = buildTransfers(tc.askOFs, tc.bidOFs, settlement) } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAsOutputs()") - assert.Equal(t, tc.expected, actual, "getAsOutputs() result") - if !assert.Equal(t, orig, tc.receiver, "receiver before and after getAsInputs()") { - t.Logf("Before: %s", indexedAddrAmtsString(orig)) - t.Logf(" After: %s", indexedAddrAmtsString(tc.receiver)) + require.NotPanics(t, testFunc, "buildTransfers") + assertions.AssertErrorValue(t, err, tc.expErr, "buildTransfers error") + if !assert.Equal(t, tc.expSettlement, settlement, "settlement after buildTransfers") { + expTransStrs := make([]string, len(tc.expSettlement.Transfers)) + for i, t := range tc.expSettlement.Transfers { + expTransStrs[i] = fmt.Sprintf("[%d]%s", i, transferString(t)) + } + expTrans := strings.Join(expTransStrs, "\n") + actTransStrs := make([]string, len(tc.expSettlement.Transfers)) + for i, t := range settlement.Transfers { + actTransStrs[i] = fmt.Sprintf("[%d]%s", i, transferString(t)) + } + actTrans := strings.Join(actTransStrs, "\n") + assert.Equal(t, expTrans, actTrans, "transfers (as strings)") } }) } } -// fulfillmentsString is similar to %v except with easier to understand Coin entries. -func fulfillmentsString(f *Fulfillments) string { - if f == nil { - return "nil" - } - - fields := []string{ - fmt.Sprintf("AskOFs: %s", orderFulfillmentsString(f.AskOFs)), - fmt.Sprintf("BidOFs: %s", orderFulfillmentsString(f.BidOFs)), - fmt.Sprintf("PartialOrder: %s", partialFulfillmentString(f.PartialOrder)), - } - return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) -} - -// orderFulfillmentsString is similar to %v except with easier to understand Coin entries. -func orderFulfillmentsString(ofs []*OrderFulfillment) string { - if ofs == nil { - return "nil" - } - - vals := make([]string, len(ofs)) - for i, f := range ofs { - vals[i] = orderFulfillmentString(f) - } - return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) -} - -// partialFulfillmentString is similar to %v except with easier to understand Coin entries. -func partialFulfillmentString(p *PartialFulfillment) string { - if p == nil { - return "nil" - } - - fields := []string{ - fmt.Sprintf("NewOrder:%s", orderString(p.NewOrder)), - fmt.Sprintf("AssetsFilled:%s", coinPString(&p.AssetsFilled)), - fmt.Sprintf("PriceFilled:%s", coinPString(&p.PriceFilled)), - } - return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) -} - -// transferString is similar to %v except with easier to understand Coin entries. -func settlementTransfersString(s *SettlementTransfers) string { - if s == nil { - return "nil" - } - - orderTransfers := "nil" - if s.OrderTransfers != nil { - transVals := make([]string, len(s.OrderTransfers)) - for i, trans := range s.OrderTransfers { - transVals[i] = transferString(trans) - } - orderTransfers = fmt.Sprintf("[%s]", strings.Join(transVals, ", ")) - } - - feeInputs := "nil" - if s.FeeInputs != nil { - feeVals := make([]string, len(s.FeeInputs)) - for i, input := range s.FeeInputs { - feeVals[i] = bankInputString(input) - } - feeInputs = fmt.Sprintf("[%s]", strings.Join(feeVals, ", ")) - } - - return fmt.Sprintf("{OrderTransfers:%s, FeeInputs:%s}", orderTransfers, feeInputs) -} - -func TestBuildSettlementTransfers(t *testing.T) { - coin := func(amt int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amt)} +func TestPopulateFilled(t *testing.T) { + coin := func(amount int64, denom string) sdk.Coin { + return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } - igc := coin(2468, "ignorable") // igc => "ignorable coin" - askOrder := func(orderID uint64, seller string, assets, price sdk.Coin) *Order { + askOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { return NewOrder(orderID).WithAsk(&AskOrder{ - MarketId: 97531, - Seller: seller, - Assets: assets, - Price: price, + Assets: coin(assetsAmt, "acorn"), + Price: coin(priceAmt, "prune"), }) } - bidOrder := func(orderID uint64, buyer string, assets, price sdk.Coin) *Order { + bidOrder := func(orderID uint64, assetsAmt, priceAmt int64) *Order { return NewOrder(orderID).WithBid(&BidOrder{ - MarketId: 97531, - Buyer: buyer, - Assets: assets, - Price: price, + Assets: coin(assetsAmt, "acorn"), + Price: coin(priceAmt, "prune"), }) } - askSplit := func(orderID uint64, seller string, assets, price sdk.Coin) *OrderSplit { - return &OrderSplit{ - Order: &OrderFulfillment{Order: askOrder(orderID, seller, igc, igc)}, - Assets: assets, - Price: price, + newOF := func(order *Order, priceAppliedAmt int64, fees ...sdk.Coin) *orderFulfillment { + rv := &orderFulfillment{ + Order: order, + AssetsFilledAmt: order.GetAssets().Amount, + AssetsUnfilledAmt: sdkmath.ZeroInt(), + PriceAppliedAmt: sdkmath.NewInt(priceAppliedAmt), + PriceLeftAmt: order.GetPrice().Amount.SubRaw(priceAppliedAmt), } - } - bidSplit := func(orderID uint64, seller string, assets, price sdk.Coin) *OrderSplit { - return &OrderSplit{ - Order: &OrderFulfillment{Order: bidOrder(orderID, seller, igc, igc)}, - Assets: assets, - Price: price, + if len(fees) > 0 { + rv.FeesToPay = fees } + return rv } - input := func(addr string, coins ...sdk.Coin) banktypes.Input { - return banktypes.Input{Address: addr, Coins: coins} - } - output := func(addr string, coins ...sdk.Coin) banktypes.Output { - return banktypes.Output{Address: addr, Coins: coins} - } - - tests := []struct { - name string - f *Fulfillments - expected *SettlementTransfers - expPanic string - }{ - { - name: "just an ask, no fees", - f: &Fulfillments{ - AskOFs: []*OrderFulfillment{ - { - Order: askOrder(1, "seller", coin(2, "oasset"), coin(3, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(4), - PriceAppliedAmt: sdkmath.NewInt(5), - Splits: []*OrderSplit{ - bidSplit(6, "buyer", coin(7, "sasset"), coin(8, "sprice")), - bidSplit(9, "buyer", coin(10, "sasset"), coin(11, "sprice")), - }, - FeesToPay: nil, - }, - }, - }, - expected: &SettlementTransfers{ - OrderTransfers: []*Transfer{ - { - Inputs: []banktypes.Input{input("seller", coin(4, "oasset"))}, - Outputs: []banktypes.Output{output("buyer", coin(17, "sasset"))}, - }, - { - Inputs: []banktypes.Input{input("buyer", coin(19, "sprice"))}, - Outputs: []banktypes.Output{output("seller", coin(5, "oprice"))}, - }, - }, - FeeInputs: nil, - }, - }, - { - name: "just an ask, with fees", - f: &Fulfillments{ - AskOFs: []*OrderFulfillment{ - { - Order: askOrder(1, "seller", coin(2, "oasset"), coin(3, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(4), - PriceAppliedAmt: sdkmath.NewInt(5), - Splits: []*OrderSplit{ - bidSplit(6, "buyer", coin(7, "sasset"), coin(8, "sprice")), - bidSplit(9, "buyer", coin(10, "sasset"), coin(11, "sprice")), - }, - FeesToPay: sdk.NewCoins(coin(12, "feea"), coin(13, "feeb")), - }, - }, - }, - expected: &SettlementTransfers{ - OrderTransfers: []*Transfer{ - { - Inputs: []banktypes.Input{input("seller", coin(4, "oasset"))}, - Outputs: []banktypes.Output{output("buyer", coin(17, "sasset"))}, - }, - { - Inputs: []banktypes.Input{input("buyer", coin(19, "sprice"))}, - Outputs: []banktypes.Output{output("seller", coin(5, "oprice"))}, - }, - }, - FeeInputs: []banktypes.Input{input("seller", coin(12, "feea"), coin(13, "feeb"))}, - }, - }, - { - name: "just a bid, no fees", - f: &Fulfillments{ - BidOFs: []*OrderFulfillment{ - { - Order: bidOrder(1, "buyer", coin(2, "oasset"), coin(3, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(4), - PriceAppliedAmt: sdkmath.NewInt(5), - Splits: []*OrderSplit{ - askSplit(6, "seller", coin(7, "sasset"), coin(8, "sprice")), - askSplit(9, "seller", coin(10, "sasset"), coin(11, "sprice")), - }, - FeesToPay: nil, - }, - }, - }, - expected: &SettlementTransfers{ - OrderTransfers: []*Transfer{ - { - Inputs: []banktypes.Input{input("seller", coin(17, "sasset"))}, - Outputs: []banktypes.Output{output("buyer", coin(4, "oasset"))}, - }, - { - Inputs: []banktypes.Input{input("buyer", coin(5, "oprice"))}, - Outputs: []banktypes.Output{output("seller", coin(19, "sprice"))}, - }, - }, - FeeInputs: nil, - }, - }, + filledOrder := func(order *Order, actualPrice int64, actualFees ...sdk.Coin) *FilledOrder { + rv := &FilledOrder{ + order: order, + actualPrice: coin(actualPrice, order.GetPrice().Denom), + } + if len(actualFees) > 0 { + rv.actualFees = actualFees + } + return rv + } + + tests := []struct { + name string + askOFs []*orderFulfillment + bidOFs []*orderFulfillment + settlement *Settlement + expSettlement *Settlement + }{ { - name: "just a bid, with fees", - f: &Fulfillments{ - BidOFs: []*OrderFulfillment{ - { - Order: bidOrder(1, "buyer", coin(2, "oasset"), coin(3, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(4), - PriceAppliedAmt: sdkmath.NewInt(5), - Splits: []*OrderSplit{ - askSplit(6, "seller", coin(7, "sasset"), coin(8, "sprice")), - askSplit(9, "seller", coin(10, "sasset"), coin(11, "sprice")), - }, - FeesToPay: sdk.NewCoins(coin(12, "feea"), coin(13, "feeb")), - }, - }, - }, - expected: &SettlementTransfers{ - OrderTransfers: []*Transfer{ - { - Inputs: []banktypes.Input{input("seller", coin(17, "sasset"))}, - Outputs: []banktypes.Output{output("buyer", coin(4, "oasset"))}, - }, - { - Inputs: []banktypes.Input{input("buyer", coin(5, "oprice"))}, - Outputs: []banktypes.Output{output("seller", coin(19, "sprice"))}, - }, - }, - FeeInputs: []banktypes.Input{input("buyer", coin(12, "feea"), coin(13, "feeb"))}, + name: "no partial", + askOFs: []*orderFulfillment{ + newOF(askOrder(2001, 53, 87), 92, coin(12, "fig")), + newOF(askOrder(2002, 17, 33), 37), + newOF(askOrder(2003, 22, 56), 60), }, - }, - { - name: "two asks two bids", - f: &Fulfillments{ - AskOFs: []*OrderFulfillment{ - { - Order: askOrder(1, "order seller", coin(2, "oasset"), coin(3, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(4), - PriceAppliedAmt: sdkmath.NewInt(5), - Splits: []*OrderSplit{ - bidSplit(6, "split buyer one", coin(7, "sasset"), coin(8, "sprice")), - bidSplit(9, "split buyer two", coin(10, "sasset"), coin(11, "sprice")), - }, - FeesToPay: sdk.NewCoins(coin(12, "sellfee")), - }, - { - Order: askOrder(13, "order seller", coin(14, "oasset"), coin(15, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(16), - PriceAppliedAmt: sdkmath.NewInt(17), - Splits: []*OrderSplit{ - bidSplit(18, "split buyer one", coin(19, "sasset"), coin(20, "sprice")), - }, - FeesToPay: sdk.NewCoins(coin(21, "sellfee")), - }, - }, - BidOFs: []*OrderFulfillment{ - { - Order: bidOrder(22, "order buyer one", coin(23, "oasset"), coin(24, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(25), - PriceAppliedAmt: sdkmath.NewInt(26), - Splits: []*OrderSplit{ - askSplit(27, "split seller one", coin(28, "sasset"), coin(29, "sprice")), - askSplit(30, "split seller one", coin(31, "sasset"), coin(32, "sprice")), - }, - FeesToPay: sdk.NewCoins(coin(33, "buyfee")), - }, - { - Order: bidOrder(34, "order buyer two", coin(35, "oasset"), coin(36, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(37), - PriceAppliedAmt: sdkmath.NewInt(38), - Splits: []*OrderSplit{ - askSplit(39, "split seller one", coin(40, "sasset"), coin(41, "sprice")), - }, - FeesToPay: sdk.NewCoins(coin(42, "buyfee")), - }, - }, + bidOFs: []*orderFulfillment{ + newOF(bidOrder(3001, 30, 40), 40), + newOF(bidOrder(3002, 27, 49), 49, coin(39, "fig")), + newOF(bidOrder(3003, 35, 100), 100), }, - expected: &SettlementTransfers{ - OrderTransfers: []*Transfer{ - { - Inputs: []banktypes.Input{input("order seller", coin(4, "oasset"))}, - Outputs: []banktypes.Output{ - output("split buyer one", coin(7, "sasset")), - output("split buyer two", coin(10, "sasset")), - }, - }, - { - Inputs: []banktypes.Input{ - input("split buyer one", coin(8, "sprice")), - input("split buyer two", coin(11, "sprice")), - }, - Outputs: []banktypes.Output{output("order seller", coin(5, "oprice"))}, - }, - { - Inputs: []banktypes.Input{input("order seller", coin(16, "oasset"))}, - Outputs: []banktypes.Output{output("split buyer one", coin(19, "sasset"))}, - }, - { - Inputs: []banktypes.Input{input("split buyer one", coin(20, "sprice"))}, - Outputs: []banktypes.Output{output("order seller", coin(17, "oprice"))}, - }, - { - Inputs: []banktypes.Input{input("split seller one", coin(59, "sasset"))}, - Outputs: []banktypes.Output{output("order buyer one", coin(25, "oasset"))}, - }, - { - Inputs: []banktypes.Input{input("order buyer one", coin(26, "oprice"))}, - Outputs: []banktypes.Output{output("split seller one", coin(61, "sprice"))}, - }, - { - Inputs: []banktypes.Input{input("split seller one", coin(40, "sasset"))}, - Outputs: []banktypes.Output{output("order buyer two", coin(37, "oasset"))}, - }, - { - Inputs: []banktypes.Input{input("order buyer two", coin(38, "oprice"))}, - Outputs: []banktypes.Output{output("split seller one", coin(41, "sprice"))}, - }, - }, - FeeInputs: []banktypes.Input{ - input("order seller", coin(33, "sellfee")), - input("order buyer one", coin(33, "buyfee")), - input("order buyer two", coin(42, "buyfee")), + settlement: &Settlement{}, + expSettlement: &Settlement{ + FullyFilledOrders: []*FilledOrder{ + filledOrder(askOrder(2001, 53, 87), 92, coin(12, "fig")), + filledOrder(askOrder(2002, 17, 33), 37), + filledOrder(askOrder(2003, 22, 56), 60), + filledOrder(bidOrder(3001, 30, 40), 40), + filledOrder(bidOrder(3002, 27, 49), 49, coin(39, "fig")), + filledOrder(bidOrder(3003, 35, 100), 100), }, }, }, { - name: "negative ask asset", - f: &Fulfillments{ - AskOFs: []*OrderFulfillment{ - { - Order: askOrder(1, "seller", coin(2, "oasset"), coin(3, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(-4), - PriceAppliedAmt: sdkmath.NewInt(5), - Splits: []*OrderSplit{ - bidSplit(6, "buyer", coin(7, "sasset"), coin(8, "sprice")), - bidSplit(9, "buyer", coin(10, "sasset"), coin(11, "sprice")), - }, - }, - }, + name: "partial ask", + askOFs: []*orderFulfillment{ + newOF(askOrder(2001, 53, 87), 92, coin(12, "fig")), + newOF(askOrder(2002, 17, 33), 37), + newOF(askOrder(2003, 22, 56), 60), }, - expPanic: "invalid coin set -4oasset: coin -4oasset amount is not positive", - }, - { - name: "negative ask price", - f: &Fulfillments{ - AskOFs: []*OrderFulfillment{ - { - Order: askOrder(1, "seller", coin(2, "oasset"), coin(3, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(4), - PriceAppliedAmt: sdkmath.NewInt(-5), - Splits: []*OrderSplit{ - bidSplit(6, "buyer", coin(7, "sasset"), coin(8, "sprice")), - bidSplit(9, "buyer", coin(10, "sasset"), coin(11, "sprice")), - }, - }, - }, + bidOFs: []*orderFulfillment{ + newOF(bidOrder(3001, 30, 40), 40), + newOF(bidOrder(3002, 27, 49), 49, coin(39, "fig")), + newOF(bidOrder(3003, 35, 100), 100), }, - expPanic: "invalid coin set -5oprice: coin -5oprice amount is not positive", - }, - { - name: "negative bid asset", - f: &Fulfillments{ - BidOFs: []*OrderFulfillment{ - { - Order: bidOrder(1, "buyer", coin(2, "oasset"), coin(3, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(-4), - PriceAppliedAmt: sdkmath.NewInt(5), - Splits: []*OrderSplit{ - askSplit(6, "seller", coin(7, "sasset"), coin(8, "sprice")), - askSplit(9, "seller", coin(10, "sasset"), coin(11, "sprice")), - }, - }, + settlement: &Settlement{PartialOrderLeft: askOrder(2002, 15, 63)}, + expSettlement: &Settlement{ + FullyFilledOrders: []*FilledOrder{ + filledOrder(askOrder(2001, 53, 87), 92, coin(12, "fig")), + filledOrder(askOrder(2003, 22, 56), 60), + filledOrder(bidOrder(3001, 30, 40), 40), + filledOrder(bidOrder(3002, 27, 49), 49, coin(39, "fig")), + filledOrder(bidOrder(3003, 35, 100), 100), }, + PartialOrderFilled: filledOrder(askOrder(2002, 17, 33), 37), + PartialOrderLeft: askOrder(2002, 15, 63), }, - expPanic: "invalid coin set -4oasset: coin -4oasset amount is not positive", }, { - name: "negative bid price", - f: &Fulfillments{ - BidOFs: []*OrderFulfillment{ - { - Order: bidOrder(1, "buyer", coin(2, "oasset"), coin(3, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(4), - PriceAppliedAmt: sdkmath.NewInt(-5), - Splits: []*OrderSplit{ - askSplit(6, "seller", coin(7, "sasset"), coin(8, "sprice")), - askSplit(9, "seller", coin(10, "sasset"), coin(11, "sprice")), - }, - }, - }, + name: "partial bid", + askOFs: []*orderFulfillment{ + newOF(askOrder(2001, 53, 87), 92, coin(12, "fig")), + newOF(askOrder(2002, 17, 33), 37), + newOF(askOrder(2003, 22, 56), 60), }, - expPanic: "invalid coin set -5oprice: coin -5oprice amount is not positive", - }, - { - name: "ask with negative fees", - f: &Fulfillments{ - BidOFs: []*OrderFulfillment{ - { - Order: bidOrder(1, "buyer", coin(2, "oasset"), coin(3, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(4), - PriceAppliedAmt: sdkmath.NewInt(5), - Splits: []*OrderSplit{ - askSplit(6, "seller", coin(7, "sasset"), coin(8, "sprice")), - askSplit(9, "seller", coin(10, "sasset"), coin(11, "sprice")), - }, - FeesToPay: sdk.Coins{coin(-12, "feecoin")}, - }, - }, + bidOFs: []*orderFulfillment{ + newOF(bidOrder(3001, 30, 40), 40), + newOF(bidOrder(3002, 27, 49), 49, coin(39, "fig")), + newOF(bidOrder(3003, 35, 100), 100), }, - expPanic: "invalid coin set -12feecoin: coin -12feecoin amount is not positive", - }, - { - name: "bid with negative fees", - f: &Fulfillments{ - BidOFs: []*OrderFulfillment{ - { - Order: bidOrder(1, "buyer", coin(2, "oasset"), coin(3, "oprice")), - AssetsFilledAmt: sdkmath.NewInt(4), - PriceAppliedAmt: sdkmath.NewInt(5), - Splits: []*OrderSplit{ - askSplit(6, "seller", coin(7, "sasset"), coin(8, "sprice")), - askSplit(9, "seller", coin(10, "sasset"), coin(11, "sprice")), - }, - FeesToPay: sdk.Coins{coin(-12, "feecoin")}, - }, + settlement: &Settlement{PartialOrderLeft: bidOrder(3003, 15, 63)}, + expSettlement: &Settlement{ + FullyFilledOrders: []*FilledOrder{ + filledOrder(askOrder(2001, 53, 87), 92, coin(12, "fig")), + filledOrder(askOrder(2002, 17, 33), 37), + filledOrder(askOrder(2003, 22, 56), 60), + filledOrder(bidOrder(3001, 30, 40), 40), + filledOrder(bidOrder(3002, 27, 49), 49, coin(39, "fig")), }, + PartialOrderFilled: filledOrder(bidOrder(3003, 35, 100), 100), + PartialOrderLeft: bidOrder(3003, 15, 63), }, - expPanic: "invalid coin set -12feecoin: coin -12feecoin amount is not positive", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var actual *SettlementTransfers - defer func() { - if t.Failed() { - t.Logf(" Actual: %s", settlementTransfersString(actual)) - t.Logf("Expected: %s", settlementTransfersString(tc.expected)) - t.Logf("Fulfillments: %s", fulfillmentsString(tc.f)) - } - }() testFunc := func() { - actual = BuildSettlementTransfers(tc.f) + populateFilled(tc.askOFs, tc.bidOFs, tc.settlement) } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "BuildSettlementTransfers") - assert.Equal(t, tc.expected, actual, "BuildSettlementTransfers result") + require.NotPanics(t, testFunc, "populateFilled") + assert.Equal(t, tc.expSettlement, tc.settlement, "settlement after populateFilled") }) } } -func TestGetAssetTransfer2(t *testing.T) { - coin := func(amt int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amt)} +func TestGetAssetTransfer(t *testing.T) { + seller, buyer := "sally", "brandon" + assetDenom, priceDenom := "apple", "peach" + newOF := func(order *Order, assetsFilledAmt int64, assetDists ...*distribution) *orderFulfillment { + rv := &orderFulfillment{ + Order: order, + AssetsFilledAmt: sdkmath.NewInt(assetsFilledAmt), + } + if len(assetDists) > 0 { + rv.AssetDists = assetDists + } + return rv } - igc := coin(2468, "ignorable") // igc => "ignorable coin" - askOrder := func(orderID uint64, seller string, assets sdk.Coin) *Order { + askOrder := func(orderID uint64) *Order { return NewOrder(orderID).WithAsk(&AskOrder{ - Seller: seller, - Assets: assets, + MarketId: 5555, + Seller: seller, + Assets: sdk.Coin{Denom: assetDenom, Amount: sdkmath.NewInt(111)}, + Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(999)}, }) } - bidOrder := func(orderID uint64, buyer string, assets sdk.Coin) *Order { + bidOrder := func(orderID uint64) *Order { return NewOrder(orderID).WithBid(&BidOrder{ - Buyer: buyer, - Assets: assets, + MarketId: 5555, + Buyer: buyer, + Assets: sdk.Coin{Denom: assetDenom, Amount: sdkmath.NewInt(111)}, + Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(999)}, }) } - orderSplit := func(order *Order, assets sdk.Coin) *OrderSplit { - return &OrderSplit{ - Order: &OrderFulfillment{Order: order}, - Assets: assets, - Price: igc, - } + dist := func(addr string, amount int64) *distribution { + return &distribution{Address: addr, Amount: sdkmath.NewInt(amount)} } - input := func(addr string, coins ...sdk.Coin) banktypes.Input { - return banktypes.Input{Address: addr, Coins: coins} + input := func(addr string, amount int64) banktypes.Input { + return banktypes.Input{ + Address: addr, + Coins: sdk.Coins{{Denom: assetDenom, Amount: sdkmath.NewInt(amount)}}, + } } - output := func(addr string, coins ...sdk.Coin) banktypes.Output { - return banktypes.Output{Address: addr, Coins: coins} + output := func(addr string, amount int64) banktypes.Output { + return banktypes.Output{ + Address: addr, + Coins: sdk.Coins{{Denom: assetDenom, Amount: sdkmath.NewInt(amount)}}, + } } tests := []struct { - name string - f *OrderFulfillment - exp *Transfer - expPanic string + name string + f *orderFulfillment + expTransfer *Transfer + expErr string + expPanic string }{ { - name: "ask, one split", - f: &OrderFulfillment{ - Order: askOrder(1, "seller", coin(25, "carrot")), - AssetsFilledAmt: sdkmath.NewInt(33), - Splits: []*OrderSplit{orderSplit(bidOrder(2, "buyer", igc), coin(88, "banana"))}, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("seller", coin(33, "carrot"))}, - Outputs: []banktypes.Output{output("buyer", coin(88, "banana"))}, - }, + name: "nil inside order", + f: newOF(NewOrder(975), 5, dist("five", 5)), + expPanic: nilSubTypeErr(975), }, { - name: "ask, two splits diff addrs", - f: &OrderFulfillment{ - Order: askOrder(3, "SELLER", coin(26, "carrot")), - AssetsFilledAmt: sdkmath.NewInt(4321), - Splits: []*OrderSplit{ - orderSplit(bidOrder(4, "buyer 1", igc), coin(89, "banana")), - orderSplit(bidOrder(5, "second buyer", igc), coin(45, "apple")), - }, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("SELLER", coin(4321, "carrot"))}, - Outputs: []banktypes.Output{ - output("buyer 1", coin(89, "banana")), - output("second buyer", coin(45, "apple")), - }, - }, + name: "unknown inside order", + f: newOF(newUnknownOrder(974), 5, dist("five", 5)), + expPanic: unknownSubTypeErr(974), }, { - name: "ask, two splits same addr, two denoms", - f: &OrderFulfillment{ - Order: askOrder(6, "SeLleR", coin(27, "carrot")), - AssetsFilledAmt: sdkmath.NewInt(5511), - Splits: []*OrderSplit{ - orderSplit(bidOrder(7, "buyer", igc), coin(90, "banana")), - orderSplit(bidOrder(8, "buyer", igc), coin(46, "apple")), - }, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("SeLleR", coin(5511, "carrot"))}, - Outputs: []banktypes.Output{output("buyer", coin(46, "apple"), coin(90, "banana"))}, - }, + name: "assets filled negative: ask", + f: newOF(askOrder(159), -5), + expErr: "ask order 159 cannot be filled with \"-5apple\" assets: amount not positive", }, { - name: "ask, two splits same addr, one denom", - f: &OrderFulfillment{ - Order: askOrder(9, "sellsell", coin(28, "carrot")), - AssetsFilledAmt: sdkmath.NewInt(42), - Splits: []*OrderSplit{ - orderSplit(bidOrder(10, "buybuy", igc), coin(55, "apple")), - orderSplit(bidOrder(11, "buybuy", igc), coin(34, "apple")), - }, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("sellsell", coin(42, "carrot"))}, - Outputs: []banktypes.Output{output("buybuy", coin(89, "apple"))}, - }, + name: "assets filled negative: bid", + f: newOF(bidOrder(953), -5), + expErr: "bid order 953 cannot be filled with \"-5apple\" assets: amount not positive", + }, + { + name: "assets filled zero: ask", + f: newOF(askOrder(991), 0), + expErr: "ask order 991 cannot be filled with \"0apple\" assets: amount not positive", + }, + { + name: "assets filled zero: bid", + f: newOF(bidOrder(992), 0), + expErr: "bid order 992 cannot be filled with \"0apple\" assets: amount not positive", + }, + { + name: "asset dists has negative amount: ask", + f: newOF(askOrder(549), 10, dist("one", 1), dist("two", 2), dist("neg", -2), dist("nine", 9)), + expErr: "ask order 549 cannot have \"-2apple\" assets in a transfer: amount not positive", + }, + { + name: "asset dists has negative amount: bid", + f: newOF(bidOrder(545), 10, dist("one", 1), dist("two", 2), dist("neg", -2), dist("nine", 9)), + expErr: "bid order 545 cannot have \"-2apple\" assets in a transfer: amount not positive", + }, + { + name: "asset dists has zero: ask", + f: newOF(askOrder(683), 10, dist("one", 1), dist("two", 2), dist("zero", 0), dist("seven", 7)), + expErr: "ask order 683 cannot have \"0apple\" assets in a transfer: amount not positive", + }, + { + name: "asset dists has zero: bid", + f: newOF(bidOrder(777), 10, dist("one", 1), dist("two", 2), dist("zero", 0), dist("seven", 7)), + expErr: "bid order 777 cannot have \"0apple\" assets in a transfer: amount not positive", + }, + { + name: "asset dists sum less than assets filled: ask", + f: newOF(askOrder(8), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("three2", 3)), + expErr: "ask order 8 assets filled \"10apple\" does not equal assets distributed \"9apple\"", + }, + { + name: "asset dists sum less than assets filled: bid", + f: newOF(bidOrder(3), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("three2", 3)), + expErr: "bid order 3 assets filled \"10apple\" does not equal assets distributed \"9apple\"", + }, + { + name: "asset dists sum more than assets filled: ask", + f: newOF(askOrder(8), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("five", 5)), + expErr: "ask order 8 assets filled \"10apple\" does not equal assets distributed \"11apple\"", + }, + { + name: "asset dists sum more than assets filled: bid", + f: newOF(bidOrder(3), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("five", 5)), + expErr: "bid order 3 assets filled \"10apple\" does not equal assets distributed \"11apple\"", }, { - name: "ask, negative price in split", - f: &OrderFulfillment{ - Order: askOrder(12, "goodsell", coin(29, "carrot")), - AssetsFilledAmt: sdkmath.NewInt(91), - Splits: []*OrderSplit{orderSplit(bidOrder(13, "buygood", igc), coin(-4, "banana"))}, + name: "one dist: ask", + f: newOF(askOrder(12), 10, dist("ten", 10)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(seller, 10)}, + Outputs: []banktypes.Output{output("ten", 10)}, }, - expPanic: "cannot index and add invalid coin amount \"-4banana\"", }, { - name: "ask, negative price applied", - f: &OrderFulfillment{ - Order: askOrder(14, "solong", coin(30, "carrot")), - AssetsFilledAmt: sdkmath.NewInt(-5), - Splits: []*OrderSplit{orderSplit(bidOrder(15, "hello", igc), coin(66, "banana"))}, + name: "one dist: bid", + f: newOF(bidOrder(13), 10, dist("ten", 10)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("ten", 10)}, + Outputs: []banktypes.Output{output(buyer, 10)}, }, - expPanic: "invalid coin set -5carrot: coin -5carrot amount is not positive", }, - { - name: "bid, one split", - f: &OrderFulfillment{ - Order: bidOrder(1, "buyer", coin(25, "carrot")), - AssetsFilledAmt: sdkmath.NewInt(33), - Splits: []*OrderSplit{orderSplit(askOrder(2, "seller", igc), coin(88, "banana"))}, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("seller", coin(88, "banana"))}, - Outputs: []banktypes.Output{output("buyer", coin(33, "carrot"))}, + name: "two dists, different addresses: ask", + f: newOF(askOrder(2111), 20, dist("eleven", 11), dist("nine", 9)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(seller, 20)}, + Outputs: []banktypes.Output{output("eleven", 11), output("nine", 9)}, }, }, { - name: "bid, two splits diff addrs", - f: &OrderFulfillment{ - Order: bidOrder(3, "BUYER", coin(26, "carrot")), - AssetsFilledAmt: sdkmath.NewInt(4321), - Splits: []*OrderSplit{ - orderSplit(askOrder(4, "seller 1", igc), coin(89, "banana")), - orderSplit(askOrder(5, "second seller", igc), coin(45, "apple")), - }, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{ - input("seller 1", coin(89, "banana")), - input("second seller", coin(45, "apple")), - }, - Outputs: []banktypes.Output{output("BUYER", coin(4321, "carrot"))}, + name: "two dists, different addresses: bid", + f: newOF(bidOrder(1222), 20, dist("eleven", 11), dist("nine", 9)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("eleven", 11), input("nine", 9)}, + Outputs: []banktypes.Output{output(buyer, 20)}, }, }, { - name: "bid, two splits same addr, two denoms", - f: &OrderFulfillment{ - Order: bidOrder(6, "BuYeR", coin(27, "carrot")), - AssetsFilledAmt: sdkmath.NewInt(5511), - Splits: []*OrderSplit{ - orderSplit(askOrder(7, "seller", igc), coin(90, "banana")), - orderSplit(askOrder(8, "seller", igc), coin(46, "apple")), - }, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("seller", coin(46, "apple"), coin(90, "banana"))}, - Outputs: []banktypes.Output{output("BuYeR", coin(5511, "carrot"))}, + name: "two dists, same addresses: ask", + f: newOF(askOrder(5353), 52, dist("billy", 48), dist("billy", 4)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(seller, 52)}, + Outputs: []banktypes.Output{output("billy", 52)}, }, }, { - name: "bid, two splits same addr, one denom", - f: &OrderFulfillment{ - Order: bidOrder(9, "buybuy", coin(28, "carrot")), - AssetsFilledAmt: sdkmath.NewInt(42), - Splits: []*OrderSplit{ - orderSplit(bidOrder(10, "sellsell", igc), coin(55, "apple")), - orderSplit(bidOrder(11, "sellsell", igc), coin(34, "apple")), - }, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("sellsell", coin(89, "apple"))}, - Outputs: []banktypes.Output{output("buybuy", coin(42, "carrot"))}, + name: "two dists, same addresses: bid", + f: newOF(bidOrder(3535), 52, dist("sol", 48), dist("sol", 4)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("sol", 52)}, + Outputs: []banktypes.Output{output(buyer, 52)}, }, }, { - name: "bid, negative price in split", - f: &OrderFulfillment{ - Order: bidOrder(12, "goodbuy", coin(29, "carrot")), - AssetsFilledAmt: sdkmath.NewInt(91), - Splits: []*OrderSplit{orderSplit(askOrder(13, "sellgood", igc), coin(-4, "banana"))}, + name: "four dists: ask", + f: newOF(askOrder(99221), 33, + dist("buddy", 10), dist("brian", 13), dist("buddy", 8), dist("bella", 2)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(seller, 33)}, + Outputs: []banktypes.Output{output("buddy", 18), output("brian", 13), output("bella", 2)}, }, - expPanic: "cannot index and add invalid coin amount \"-4banana\"", }, { - name: "bid, negative price applied", - f: &OrderFulfillment{ - Order: bidOrder(14, "heythere", coin(30, "carrot")), - AssetsFilledAmt: sdkmath.NewInt(-5), - Splits: []*OrderSplit{orderSplit(askOrder(15, "afterwhile", igc), coin(66, "banana"))}, + name: "four dists: bid", + f: newOF(bidOrder(99221), 33, + dist("sydney", 10), dist("sarah", 2), dist("sydney", 8), dist("spencer", 13)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("sydney", 18), input("sarah", 2), input("spencer", 13)}, + Outputs: []banktypes.Output{output(buyer, 33)}, }, - expPanic: "invalid coin set -5carrot: coin -5carrot amount is not positive", - }, - - { - name: "nil inside order", - f: &OrderFulfillment{Order: NewOrder(20)}, - expPanic: "unknown order type ", - }, - { - name: "unknown inside order", - f: &OrderFulfillment{Order: newUnknownOrder(21)}, - expPanic: "unknown order type *exchange.unknownOrderType", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var actual *Transfer - defer func() { - if t.Failed() { - t.Logf(" Actual: %s", transferString(actual)) - t.Logf("Expected: %s", transferString(tc.exp)) - t.Logf("OrderFulfillment: %s", orderFulfillmentString(tc.f)) - } - }() + orig := copyOrderFulfillment(tc.f) + var transfer *Transfer + var err error testFunc := func() { - actual = GetAssetTransfer2(tc.f) + transfer, err = getAssetTransfer(tc.f) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAssetTransfer") + assertions.AssertErrorValue(t, err, tc.expErr, "getAssetTransfer error") + if !assert.Equal(t, tc.expTransfer, transfer, "getAssetTransfer transfers") { + t.Logf(" Actual: %s", transferString(transfer)) + t.Logf("Expected: %s", transferString(tc.expTransfer)) } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetAssetTransfer2") - assert.Equal(t, tc.exp, actual, "GetAssetTransfer2 result") + assertEqualOrderFulfillments(t, orig, tc.f, "orderFulfillment before and after getAssetTransfer") }) } } -func TestGetPriceTransfer2(t *testing.T) { - coin := func(amt int64, denom string) sdk.Coin { - return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amt)} +func TestGetPriceTransfer(t *testing.T) { + seller, buyer := "sally", "brandon" + assetDenom, priceDenom := "apple", "peach" + newOF := func(order *Order, priceAppliedAmt int64, priceDists ...*distribution) *orderFulfillment { + rv := &orderFulfillment{ + Order: order, + PriceAppliedAmt: sdkmath.NewInt(priceAppliedAmt), + } + if len(priceDists) > 0 { + rv.PriceDists = priceDists + } + return rv } - igc := coin(2468, "ignorable") // igc => "ignorable coin" - askOrder := func(orderID uint64, seller string, price sdk.Coin) *Order { + askOrder := func(orderID uint64) *Order { return NewOrder(orderID).WithAsk(&AskOrder{ - Seller: seller, - Price: price, + MarketId: 5555, + Seller: seller, + Assets: sdk.Coin{Denom: assetDenom, Amount: sdkmath.NewInt(111)}, + Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(999)}, }) } - bidOrder := func(orderID uint64, buyer string, price sdk.Coin) *Order { + bidOrder := func(orderID uint64) *Order { return NewOrder(orderID).WithBid(&BidOrder{ - Buyer: buyer, - Price: price, + MarketId: 5555, + Buyer: buyer, + Assets: sdk.Coin{Denom: assetDenom, Amount: sdkmath.NewInt(111)}, + Price: sdk.Coin{Denom: priceDenom, Amount: sdkmath.NewInt(999)}, }) } - orderSplit := func(order *Order, price sdk.Coin) *OrderSplit { - return &OrderSplit{ - Order: &OrderFulfillment{Order: order}, - Price: price, - Assets: igc, - } + dist := func(addr string, amount int64) *distribution { + return &distribution{Address: addr, Amount: sdkmath.NewInt(amount)} } - input := func(addr string, coins ...sdk.Coin) banktypes.Input { - return banktypes.Input{Address: addr, Coins: coins} + input := func(addr string, amount int64) banktypes.Input { + return banktypes.Input{ + Address: addr, + Coins: sdk.Coins{{Denom: priceDenom, Amount: sdkmath.NewInt(amount)}}, + } } - output := func(addr string, coins ...sdk.Coin) banktypes.Output { - return banktypes.Output{Address: addr, Coins: coins} + output := func(addr string, amount int64) banktypes.Output { + return banktypes.Output{ + Address: addr, + Coins: sdk.Coins{{Denom: priceDenom, Amount: sdkmath.NewInt(amount)}}, + } } tests := []struct { - name string - f *OrderFulfillment - exp *Transfer - expPanic string + name string + f *orderFulfillment + expTransfer *Transfer + expErr string + expPanic string }{ { - name: "ask, one split", - f: &OrderFulfillment{ - Order: askOrder(1, "seller", coin(25, "carrot")), - PriceAppliedAmt: sdkmath.NewInt(33), - Splits: []*OrderSplit{orderSplit(bidOrder(2, "buyer", igc), coin(88, "banana"))}, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("buyer", coin(88, "banana"))}, - Outputs: []banktypes.Output{output("seller", coin(33, "carrot"))}, - }, + name: "nil inside order", + f: newOF(NewOrder(975), 5, dist("five", 5)), + expPanic: nilSubTypeErr(975), }, { - name: "ask, two splits diff addrs", - f: &OrderFulfillment{ - Order: askOrder(3, "SELLER", coin(26, "carrot")), - PriceAppliedAmt: sdkmath.NewInt(4321), - Splits: []*OrderSplit{ - orderSplit(bidOrder(4, "buyer 1", igc), coin(89, "banana")), - orderSplit(bidOrder(5, "second buyer", igc), coin(45, "apple")), - }, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{ - input("buyer 1", coin(89, "banana")), - input("second buyer", coin(45, "apple")), - }, - Outputs: []banktypes.Output{output("SELLER", coin(4321, "carrot"))}, - }, + name: "unknown inside order", + f: newOF(newUnknownOrder(974), 5, dist("five", 5)), + expPanic: unknownSubTypeErr(974), }, { - name: "ask, two splits same addr, two denoms", - f: &OrderFulfillment{ - Order: askOrder(6, "SeLleR", coin(27, "carrot")), - PriceAppliedAmt: sdkmath.NewInt(5511), - Splits: []*OrderSplit{ - orderSplit(bidOrder(7, "buyer", igc), coin(90, "banana")), - orderSplit(bidOrder(8, "buyer", igc), coin(46, "apple")), - }, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("buyer", coin(46, "apple"), coin(90, "banana"))}, - Outputs: []banktypes.Output{output("SeLleR", coin(5511, "carrot"))}, - }, + name: "price applied negative: ask", + f: newOF(askOrder(159), -5), + expErr: "ask order 159 cannot be filled at price \"-5peach\": amount not positive", }, { - name: "ask, two splits same addr, one denom", - f: &OrderFulfillment{ - Order: askOrder(9, "sellsell", coin(28, "carrot")), - PriceAppliedAmt: sdkmath.NewInt(42), - Splits: []*OrderSplit{ - orderSplit(bidOrder(10, "buybuy", igc), coin(55, "apple")), - orderSplit(bidOrder(11, "buybuy", igc), coin(34, "apple")), - }, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("buybuy", coin(89, "apple"))}, - Outputs: []banktypes.Output{output("sellsell", coin(42, "carrot"))}, - }, + name: "price applied negative: bid", + f: newOF(bidOrder(953), -5), + expErr: "bid order 953 cannot be filled at price \"-5peach\": amount not positive", }, { - name: "ask, negative price in split", - f: &OrderFulfillment{ - Order: askOrder(12, "goodsell", coin(29, "carrot")), - PriceAppliedAmt: sdkmath.NewInt(91), - Splits: []*OrderSplit{orderSplit(bidOrder(13, "buygood", igc), coin(-4, "banana"))}, - }, - expPanic: "cannot index and add invalid coin amount \"-4banana\"", + name: "price applied zero: ask", + f: newOF(askOrder(991), 0), + expErr: "ask order 991 cannot be filled at price \"0peach\": amount not positive", }, { - name: "ask, negative price applied", - f: &OrderFulfillment{ - Order: askOrder(14, "solong", coin(30, "carrot")), - PriceAppliedAmt: sdkmath.NewInt(-5), - Splits: []*OrderSplit{orderSplit(bidOrder(15, "hello", igc), coin(66, "banana"))}, - }, - expPanic: "invalid coin set -5carrot: coin -5carrot amount is not positive", + name: "price applied zero: bid", + f: newOF(bidOrder(992), 0), + expErr: "bid order 992 cannot be filled at price \"0peach\": amount not positive", }, - { - name: "bid, one split", - f: &OrderFulfillment{ - Order: bidOrder(1, "buyer", coin(25, "carrot")), - PriceAppliedAmt: sdkmath.NewInt(33), - Splits: []*OrderSplit{orderSplit(askOrder(2, "seller", igc), coin(88, "banana"))}, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("buyer", coin(33, "carrot"))}, - Outputs: []banktypes.Output{output("seller", coin(88, "banana"))}, - }, + name: "price dists has negative amount: ask", + f: newOF(askOrder(549), 10, dist("one", 1), dist("two", 2), dist("neg", -2), dist("nine", 9)), + expErr: "ask order 549 cannot have price \"-2peach\" in a transfer: amount not positive", }, { - name: "bid, two splits diff addrs", - f: &OrderFulfillment{ - Order: bidOrder(3, "BUYER", coin(26, "carrot")), - PriceAppliedAmt: sdkmath.NewInt(4321), - Splits: []*OrderSplit{ - orderSplit(askOrder(4, "seller 1", igc), coin(89, "banana")), - orderSplit(askOrder(5, "second seller", igc), coin(45, "apple")), - }, - }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("BUYER", coin(4321, "carrot"))}, - Outputs: []banktypes.Output{ - output("seller 1", coin(89, "banana")), - output("second seller", coin(45, "apple")), - }, - }, + name: "price dists has negative amount: bid", + f: newOF(bidOrder(545), 10, dist("one", 1), dist("two", 2), dist("neg", -2), dist("nine", 9)), + expErr: "bid order 545 cannot have price \"-2peach\" in a transfer: amount not positive", }, { - name: "bid, two splits same addr, two denoms", - f: &OrderFulfillment{ - Order: bidOrder(6, "BuYeR", coin(27, "carrot")), - PriceAppliedAmt: sdkmath.NewInt(5511), - Splits: []*OrderSplit{ - orderSplit(askOrder(7, "seller", igc), coin(90, "banana")), - orderSplit(askOrder(8, "seller", igc), coin(46, "apple")), - }, + name: "price dists has zero: ask", + f: newOF(askOrder(683), 10, dist("one", 1), dist("two", 2), dist("zero", 0), dist("seven", 7)), + expErr: "ask order 683 cannot have price \"0peach\" in a transfer: amount not positive", + }, + { + name: "price dists has zero: bid", + f: newOF(bidOrder(777), 10, dist("one", 1), dist("two", 2), dist("zero", 0), dist("seven", 7)), + expErr: "bid order 777 cannot have price \"0peach\" in a transfer: amount not positive", + }, + { + name: "price dists sum less than price applied: ask", + f: newOF(askOrder(8), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("three2", 3)), + expErr: "ask order 8 price filled \"10peach\" does not equal price distributed \"9peach\"", + }, + { + name: "price dists sum less than price applied: bid", + f: newOF(bidOrder(3), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("three2", 3)), + expErr: "bid order 3 price filled \"10peach\" does not equal price distributed \"9peach\"", + }, + { + name: "price dists sum more than price applied: ask", + f: newOF(askOrder(8), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("five", 5)), + expErr: "ask order 8 price filled \"10peach\" does not equal price distributed \"11peach\"", + }, + { + name: "price dists sum more than price applied: bid", + f: newOF(bidOrder(3), 10, dist("one", 1), dist("two", 2), dist("three", 3), dist("five", 5)), + expErr: "bid order 3 price filled \"10peach\" does not equal price distributed \"11peach\"", + }, + { + name: "one dist: ask", + f: newOF(askOrder(12), 10, dist("ten", 10)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("ten", 10)}, + Outputs: []banktypes.Output{output(seller, 10)}, }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("BuYeR", coin(5511, "carrot"))}, - Outputs: []banktypes.Output{output("seller", coin(46, "apple"), coin(90, "banana"))}, + }, + { + name: "one dist: bid", + f: newOF(bidOrder(13), 10, dist("ten", 10)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(buyer, 10)}, + Outputs: []banktypes.Output{output("ten", 10)}, }, }, { - name: "bid, two splits same addr, one denom", - f: &OrderFulfillment{ - Order: bidOrder(9, "buybuy", coin(28, "carrot")), - PriceAppliedAmt: sdkmath.NewInt(42), - Splits: []*OrderSplit{ - orderSplit(bidOrder(10, "sellsell", igc), coin(55, "apple")), - orderSplit(bidOrder(11, "sellsell", igc), coin(34, "apple")), - }, + name: "two dists, different addresses: ask", + f: newOF(askOrder(2111), 20, dist("eleven", 11), dist("nine", 9)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("eleven", 11), input("nine", 9)}, + Outputs: []banktypes.Output{output(seller, 20)}, }, - exp: &Transfer{ - Inputs: []banktypes.Input{input("buybuy", coin(42, "carrot"))}, - Outputs: []banktypes.Output{output("sellsell", coin(89, "apple"))}, + }, + { + name: "two dists, different addresses: bid", + f: newOF(bidOrder(1222), 20, dist("eleven", 11), dist("nine", 9)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(buyer, 20)}, + Outputs: []banktypes.Output{output("eleven", 11), output("nine", 9)}, }, }, { - name: "bid, negative price in split", - f: &OrderFulfillment{ - Order: bidOrder(12, "goodbuy", coin(29, "carrot")), - PriceAppliedAmt: sdkmath.NewInt(91), - Splits: []*OrderSplit{orderSplit(askOrder(13, "sellgood", igc), coin(-4, "banana"))}, + name: "two dists, same addresses: ask", + f: newOF(askOrder(5353), 52, dist("billy", 48), dist("billy", 4)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("billy", 52)}, + Outputs: []banktypes.Output{output(seller, 52)}, }, - expPanic: "cannot index and add invalid coin amount \"-4banana\"", }, { - name: "bid, negative price applied", - f: &OrderFulfillment{ - Order: bidOrder(14, "heythere", coin(30, "carrot")), - PriceAppliedAmt: sdkmath.NewInt(-5), - Splits: []*OrderSplit{orderSplit(askOrder(15, "afterwhile", igc), coin(66, "banana"))}, + name: "two dists, same addresses: bid", + f: newOF(bidOrder(3535), 52, dist("sol", 48), dist("sol", 4)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(buyer, 52)}, + Outputs: []banktypes.Output{output("sol", 52)}, }, - expPanic: "invalid coin set -5carrot: coin -5carrot amount is not positive", }, - { - name: "nil inside order", - f: &OrderFulfillment{Order: NewOrder(20)}, - expPanic: "unknown order type ", + name: "four dists: ask", + f: newOF(askOrder(99221), 33, + dist("buddy", 10), dist("brian", 13), dist("buddy", 8), dist("bella", 2)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input("buddy", 18), input("brian", 13), input("bella", 2)}, + Outputs: []banktypes.Output{output(seller, 33)}, + }, }, { - name: "unknown inside order", - f: &OrderFulfillment{Order: newUnknownOrder(21)}, - expPanic: "unknown order type *exchange.unknownOrderType", + name: "four dists: bid", + f: newOF(bidOrder(99221), 33, + dist("sydney", 10), dist("sarah", 2), dist("sydney", 8), dist("spencer", 13)), + expTransfer: &Transfer{ + Inputs: []banktypes.Input{input(buyer, 33)}, + Outputs: []banktypes.Output{output("sydney", 18), output("sarah", 2), output("spencer", 13)}, + }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - var actual *Transfer - defer func() { - if t.Failed() { - t.Logf(" Actual: %s", transferString(actual)) - t.Logf("Expected: %s", transferString(tc.exp)) - t.Logf("OrderFulfillment: %s", orderFulfillmentString(tc.f)) - } - }() + orig := copyOrderFulfillment(tc.f) + var transfer *Transfer + var err error testFunc := func() { - actual = GetPriceTransfer2(tc.f) + transfer, err = getPriceTransfer(tc.f) + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getPriceTransfer") + assertions.AssertErrorValue(t, err, tc.expErr, "getPriceTransfer error") + if !assert.Equal(t, tc.expTransfer, transfer, "getPriceTransfer transfers") { + t.Logf(" Actual: %s", transferString(transfer)) + t.Logf("Expected: %s", transferString(tc.expTransfer)) } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetPriceTransfer2") - assert.Equal(t, tc.exp, actual, "GetPriceTransfer2 result") + assertEqualOrderFulfillments(t, orig, tc.f, "orderFulfillment before and after getPriceTransfer") }) } } From 39401cf0af670d191fca8ccad524d33e1cdef532 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sun, 8 Oct 2023 21:04:03 -0600 Subject: [PATCH 259/309] [1658]: Unit tests on ParseIndexKeySuffixOrderID. --- x/exchange/keeper/keys_test.go | 97 ++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index bfa93f0a17..51b5bc13f3 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -2886,6 +2886,103 @@ func TestParseKeyOrder(t *testing.T) { } } +func TestParseIndexKeySuffixOrderID(t *testing.T) { + tests := []struct { + name string + key []byte + expOrderID uint64 + expOK bool + }{ + {name: "nil", key: nil, expOrderID: 0, expOK: false}, + {name: "empty", key: []byte{}, expOrderID: 0, expOK: false}, + {name: "1 byte", key: []byte{1}, expOrderID: 0, expOK: false}, + {name: "7 byte", key: []byte{1, 2, 3, 4, 5, 6, 7}, expOrderID: 0, expOK: false}, + {name: "8 bytes: 0", key: []byte{0, 0, 0, 0, 0, 0, 0, 0}, expOrderID: 0, expOK: true}, + {name: "8 bytes: 1", key: []byte{0, 0, 0, 0, 0, 0, 0, 1}, expOrderID: 1, expOK: true}, + { + name: "8 bytes: 4,294,967,296", + key: []byte{0, 0, 0, 1, 0, 0, 0, 0}, + expOrderID: 4_294_967_296, + expOK: true, + }, + { + name: "8 bytes: 72,623,859,790,382,856", + key: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + expOrderID: 72_623_859_790_382_856, + expOK: true, + }, + { + name: "8 bytes: max uint64", + key: []byte{255, 255, 255, 255, 255, 255, 255, 255}, + expOrderID: 18_446_744_073_709_551_615, + expOK: true, + }, + {name: "9 bytes: 0", key: []byte{9, 0, 0, 0, 0, 0, 0, 0, 0}, expOrderID: 0, expOK: true}, + {name: "9 bytes: 1", key: []byte{9, 0, 0, 0, 0, 0, 0, 0, 1}, expOrderID: 1, expOK: true}, + { + name: "9 bytes: 4,294,967,296", + key: []byte{9, 0, 0, 0, 1, 0, 0, 0, 0}, + expOrderID: 4_294_967_296, + expOK: true, + }, + { + name: "9 bytes: 72,623,859,790,382,856", + key: []byte{9, 1, 2, 3, 4, 5, 6, 7, 8}, + expOrderID: 72_623_859_790_382_856, + expOK: true, + }, + { + name: "9 bytes: max uint64", + key: []byte{9, 255, 255, 255, 255, 255, 255, 255, 255}, + expOrderID: 18_446_744_073_709_551_615, + expOK: true, + }, + { + name: "20 bytes: 0", + key: []byte{20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 0, 0, 0, 0, 0, 0, 0, 0}, + expOrderID: 0, + expOK: true, + }, + { + name: "20 bytes: 1", + key: []byte{20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 0, 0, 0, 0, 0, 0, 0, 1}, + expOrderID: 1, + expOK: true, + }, + { + name: "20 bytes: 4,294,967,296", + key: []byte{20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 0, 0, 0, 1, 0, 0, 0, 0}, + expOrderID: 4_294_967_296, + expOK: true, + }, + { + name: "20 bytes: 72,623,859,790,382,856", + key: []byte{20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 1, 2, 3, 4, 5, 6, 7, 8}, + expOrderID: 72_623_859_790_382_856, + expOK: true, + }, + { + name: "20 bytes: max uint64", + key: []byte{20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 255, 255, 255, 255, 255, 255, 255, 255}, + expOrderID: 18_446_744_073_709_551_615, + expOK: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var orderID uint64 + var ok bool + testFunc := func() { + orderID, ok = keeper.ParseIndexKeySuffixOrderID(tc.key) + } + require.NotPanics(t, testFunc, "ParseIndexKeySuffixOrderID") + assert.Equal(t, tc.expOrderID, orderID, "ParseIndexKeySuffixOrderID orderID") + assert.Equal(t, tc.expOK, ok, "ParseIndexKeySuffixOrderID ok bool") + }) + } +} + func TestGetIndexKeyPrefixMarketToOrder(t *testing.T) { tests := []struct { name string From a127d0ece6b37310e679f8fc009bd51c88ed0ce3 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sun, 8 Oct 2023 21:26:15 -0600 Subject: [PATCH 260/309] [1658]: Stub out the remaining module pieces. --- x/exchange/client/cli/query.go | 8 ++++++++ x/exchange/client/cli/tx.go | 8 ++++++++ x/exchange/module/module.go | 21 +++++++-------------- x/exchange/simulation/decoder.go | 14 ++++++++++++++ x/exchange/simulation/genesis.go | 8 ++++++++ x/exchange/simulation/operations.go | 9 +++++++++ 6 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 x/exchange/client/cli/query.go create mode 100644 x/exchange/client/cli/tx.go create mode 100644 x/exchange/simulation/decoder.go create mode 100644 x/exchange/simulation/genesis.go create mode 100644 x/exchange/simulation/operations.go diff --git a/x/exchange/client/cli/query.go b/x/exchange/client/cli/query.go new file mode 100644 index 0000000000..3c5cb8b522 --- /dev/null +++ b/x/exchange/client/cli/query.go @@ -0,0 +1,8 @@ +package cli + +import "github.com/spf13/cobra" + +func CmdQuery() *cobra.Command { + // TODO[1658]: Write CmdQuery() + return nil +} diff --git a/x/exchange/client/cli/tx.go b/x/exchange/client/cli/tx.go new file mode 100644 index 0000000000..14b42f7b8a --- /dev/null +++ b/x/exchange/client/cli/tx.go @@ -0,0 +1,8 @@ +package cli + +import "github.com/spf13/cobra" + +func CmdTx() *cobra.Command { + // TODO[1658]: Write CmdTx() + return nil +} diff --git a/x/exchange/module/module.go b/x/exchange/module/module.go index 48de88f3ec..aec803c054 100644 --- a/x/exchange/module/module.go +++ b/x/exchange/module/module.go @@ -19,7 +19,9 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/provenance-io/provenance/x/exchange" + "github.com/provenance-io/provenance/x/exchange/client/cli" "github.com/provenance-io/provenance/x/exchange/keeper" + "github.com/provenance-io/provenance/x/exchange/simulation" ) var ( @@ -64,16 +66,12 @@ func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ sdkclient.TxEncodin // GetQueryCmd returns the cli query commands for the exchange module. func (a AppModuleBasic) GetQueryCmd() *cobra.Command { - // TODO[1658]: Create cli.QueryCmd() - panic("not implemented") - //return cli.QueryCmd() + return cli.CmdQuery() } // GetTxCmd returns the transaction commands for the exchange module. func (a AppModuleBasic) GetTxCmd() *cobra.Command { - // TODO[1658]: Create cli.TxCmd() - panic("not implemented") - //return cli.TxCmd() + return cli.CmdTx() } // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the exchange module. @@ -133,9 +131,7 @@ func (AppModule) ConsensusVersion() uint64 { return 1 } // GenerateGenesisState creates a randomized GenState of the exchange module. func (am AppModule) GenerateGenesisState(simState *module.SimulationState) { - // TODO[1658]: Create simulation.RandomizedGenState(simState) - panic("not implemented") - //simulation.RandomizedGenState(simState) + simulation.RandomizedGenState(simState) } // ProposalContents returns all the exchange content functions used to @@ -150,14 +146,11 @@ func (AppModule) RandomizedParams(_ *rand.Rand) []simtypes.ParamChange { return // RegisterStoreDecoder registers a decoder for exchange module's types func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - // TODO[1658]: Create simulation.NewDecodeStore(am.cdc) - panic("not implemented") - // sdr[exchange.StoreKey] = simulation.NewDecodeStore(am.cdc) + sdr[exchange.StoreKey] = simulation.NewDecodeStore(am.cdc) } // WeightedOperations returns the all the exchange module operations with their respective weights, // of which there are none. func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { - // TODO[1658]: Create the WeightedOperations. - panic("not implemented") + return simulation.WeightedOperations() } diff --git a/x/exchange/simulation/decoder.go b/x/exchange/simulation/decoder.go new file mode 100644 index 0000000000..c89dc0dbc2 --- /dev/null +++ b/x/exchange/simulation/decoder.go @@ -0,0 +1,14 @@ +package simulation + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/kv" +) + +// NewDecodeStore returns a new store decoder for the exchange state. +func NewDecodeStore(_ codec.Codec) func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + // TODO[1658]: Write NewDecodeStore. + return "not implemented" + } +} diff --git a/x/exchange/simulation/genesis.go b/x/exchange/simulation/genesis.go new file mode 100644 index 0000000000..14ceb2129b --- /dev/null +++ b/x/exchange/simulation/genesis.go @@ -0,0 +1,8 @@ +package simulation + +import "github.com/cosmos/cosmos-sdk/types/module" + +// RandomizedGenState generates a random GenesisState for the exchange module. +func RandomizedGenState(simState *module.SimulationState) { + // TODO[1658]: Write RandomizedGenState. +} diff --git a/x/exchange/simulation/operations.go b/x/exchange/simulation/operations.go new file mode 100644 index 0000000000..4498a72b3a --- /dev/null +++ b/x/exchange/simulation/operations.go @@ -0,0 +1,9 @@ +package simulation + +import "github.com/cosmos/cosmos-sdk/x/simulation" + +// WeightedOperations returns all the operations from the module with their respective weights +func WeightedOperations() simulation.WeightedOperations { + // TODO[1658]: Write WeightedOperations + return nil +} From 16ed92c571200394ae9ba808cc9d351bb58d9101 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sun, 8 Oct 2023 21:57:31 -0600 Subject: [PATCH 261/309] [1658]: Bypass the quarantine module when doing transfers. --- x/exchange/keeper/keeper.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 5ed8cd3e53..fa515aa5c4 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -14,6 +14,7 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/quarantine" "github.com/provenance-io/provenance/x/exchange" ) @@ -144,7 +145,10 @@ func (k Keeper) iterate(ctx sdk.Context, pre []byte, cb func(key, value []byte) } // DoTransfer facilitates a transfer of things using the bank module. -func (k Keeper) DoTransfer(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) error { +func (k Keeper) DoTransfer(ctxIn sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) error { + // We bypass the quarantine module here under the assumption that someone creating + // an order counts as acceptance of the stuff to receive (that they defined when creating the order). + ctx := quarantine.WithBypass(ctxIn) if len(inputs) == 1 && len(outputs) == 1 { // If there's only one of each, we use SendCoins for the nicer events. if !exchange.CoinsEquals(inputs[0].Coins, outputs[0].Coins) { From 6fd187afc6ecd75eefcd167e245fa8c56044f8bf Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sun, 8 Oct 2023 21:59:35 -0600 Subject: [PATCH 262/309] [1658]: During InitGenesis, make sure that there's holds on all the order funds already. --- x/exchange/expected_keepers.go | 1 + x/exchange/keeper/genesis.go | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/x/exchange/expected_keepers.go b/x/exchange/expected_keepers.go index e435adac00..d1ddb480d7 100644 --- a/x/exchange/expected_keepers.go +++ b/x/exchange/expected_keepers.go @@ -28,4 +28,5 @@ type BankKeeper interface { type HoldKeeper interface { AddHold(ctx sdk.Context, addr sdk.AccAddress, funds sdk.Coins, reason string) error ReleaseHold(ctx sdk.Context, addr sdk.AccAddress, funds sdk.Coins) error + GetHoldCoin(ctx sdk.Context, addr sdk.AccAddress, denom string) (sdk.Coin, error) } diff --git a/x/exchange/keeper/genesis.go b/x/exchange/keeper/genesis.go index f7d56533a5..cd3c5dfcda 100644 --- a/x/exchange/keeper/genesis.go +++ b/x/exchange/keeper/genesis.go @@ -23,10 +23,32 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *exchange.GenesisState) { setLastAutoMarketID(store, genState.LastMarketId) + var addrs []string + amounts := make(map[string]sdk.Coins) + for i, order := range genState.Orders { if err := k.setOrderInStore(store, order); err != nil { panic(fmt.Errorf("failed to store Orders[%d]: %w", i, err)) } + addr := order.GetOwner() + if _, known := amounts[addr]; !known { + addrs = append(addrs, addr) + amounts[addr] = nil + } + amounts[addr] = amounts[addr].Add(order.GetHoldAmount()...) + } + + // Make sure all the needed funds have holds on them. These should have been placed during initialization of the hold module. + for _, addr := range addrs { + for _, reqAmt := range amounts[addr] { + holdAmt, err := k.holdKeeper.GetHoldCoin(ctx, sdk.MustAccAddressFromBech32(addr), reqAmt.Denom) + if err != nil { + panic(fmt.Errorf("failed to look up amount of %q on hold for %s", reqAmt.Denom, addr)) + } + if holdAmt.Amount.LT(reqAmt.Amount) { + panic(fmt.Errorf("account %s should have at least %q on hold (due to exchange orders), but only has %q", addr, reqAmt, holdAmt)) + } + } } } From d4d40fab98218478433e2d5be7bd6a9e601cefc0 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sun, 8 Oct 2023 22:01:08 -0600 Subject: [PATCH 263/309] [1658]: Use DoTransfer in FillBids (instead of InputOutputCoins directoy) as I intended. --- x/exchange/keeper/fulfillment.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index 434b2b1766..b31a453bf8 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -8,7 +8,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/provenance-io/provenance/x/exchange" ) @@ -131,11 +130,11 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro assetInputs := []banktypes.Input{{Address: msg.Seller, Coins: msg.TotalAssets}} priceOutputs := []banktypes.Output{{Address: msg.Seller, Coins: totalPrice}} - if err := k.bankKeeper.InputOutputCoins(ctx, assetInputs, assetOutputs); err != nil { + if err := k.DoTransfer(ctx, assetInputs, assetOutputs); err != nil { return fmt.Errorf("error transferring assets from seller to buyers: %w", err) } - if err := k.bankKeeper.InputOutputCoins(ctx, priceInputs, priceOutputs); err != nil { + if err := k.DoTransfer(ctx, priceInputs, priceOutputs); err != nil { return fmt.Errorf("error transferring price from buyers to seller: %w", err) } From d949f9b4cc695ef28e7bdb32d0b92b82cd859642 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sun, 8 Oct 2023 22:03:45 -0600 Subject: [PATCH 264/309] [1658]: Add the exchange module to the app. --- app/app.go | 19 ++++++++++++++++++- app/upgrades.go | 5 +++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app/app.go b/app/app.go index 5912fb379f..ba8552d8c1 100644 --- a/app/app.go +++ b/app/app.go @@ -132,10 +132,13 @@ import ( attributekeeper "github.com/provenance-io/provenance/x/attribute/keeper" attributetypes "github.com/provenance-io/provenance/x/attribute/types" attributewasm "github.com/provenance-io/provenance/x/attribute/wasm" + "github.com/provenance-io/provenance/x/exchange" + exchangekeeper "github.com/provenance-io/provenance/x/exchange/keeper" + exchangemodule "github.com/provenance-io/provenance/x/exchange/module" "github.com/provenance-io/provenance/x/hold" holdkeeper "github.com/provenance-io/provenance/x/hold/keeper" holdmodule "github.com/provenance-io/provenance/x/hold/module" - ibchooks "github.com/provenance-io/provenance/x/ibchooks" + "github.com/provenance-io/provenance/x/ibchooks" ibchookskeeper "github.com/provenance-io/provenance/x/ibchooks/keeper" ibchookstypes "github.com/provenance-io/provenance/x/ibchooks/types" "github.com/provenance-io/provenance/x/marker" @@ -226,6 +229,7 @@ var ( triggermodule.AppModuleBasic{}, oraclemodule.AppModuleBasic{}, holdmodule.AppModuleBasic{}, + exchangemodule.AppModuleBasic{}, ) // module account permissions @@ -317,6 +321,7 @@ type App struct { AttributeKeeper attributekeeper.Keeper NameKeeper namekeeper.Keeper HoldKeeper holdkeeper.Keeper + ExchangeKeeper exchangekeeper.Keeper WasmKeeper *wasm.Keeper ContractKeeper *wasmkeeper.PermissionedKeeper @@ -398,6 +403,7 @@ func New( triggertypes.StoreKey, oracletypes.StoreKey, hold.StoreKey, + exchange.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) @@ -562,6 +568,11 @@ func New( appCodec, keys[hold.StoreKey], app.BankKeeper, ) + app.ExchangeKeeper = exchangekeeper.NewKeeper( + appCodec, keys[exchange.StoreKey], authtypes.FeeCollectorName, + app.AccountKeeper, app.AttributeKeeper, app.BankKeeper, app.HoldKeeper, + ) + pioMessageRouter := MessageRouterFunc(func(msg sdk.Msg) baseapp.MsgServiceHandler { return pioMsgFeesRouter.Handler(msg) }) @@ -741,6 +752,7 @@ func New( triggermodule.NewAppModule(appCodec, app.TriggerKeeper, app.AccountKeeper, app.BankKeeper), oracleModule, holdmodule.NewAppModule(appCodec, app.HoldKeeper), + exchangemodule.NewAppModule(appCodec, app.ExchangeKeeper), // IBC ibc.NewAppModule(app.IBCKeeper), @@ -791,6 +803,7 @@ func New( quarantine.ModuleName, sanction.ModuleName, hold.ModuleName, + exchange.ModuleName, ) app.mm.SetOrderEndBlockers( @@ -830,6 +843,7 @@ func New( quarantine.ModuleName, sanction.ModuleName, hold.ModuleName, + exchange.ModuleName, ) // NOTE: The genutils module must occur after staking so that pools are @@ -861,6 +875,7 @@ func New( metadatatypes.ModuleName, msgfeestypes.ModuleName, hold.ModuleName, + exchange.ModuleName, // must be after the hold module. ibchost.ModuleName, ibctransfertypes.ModuleName, @@ -901,6 +916,7 @@ func New( quarantine.ModuleName, sanction.ModuleName, hold.ModuleName, + exchange.ModuleName, ibchookstypes.ModuleName, icatypes.ModuleName, @@ -951,6 +967,7 @@ func New( triggermodule.NewAppModule(appCodec, app.TriggerKeeper, app.AccountKeeper, app.BankKeeper), oraclemodule.NewAppModule(appCodec, app.OracleKeeper, app.AccountKeeper, app.BankKeeper, app.IBCKeeper.ChannelKeeper), holdmodule.NewAppModule(appCodec, app.HoldKeeper), + exchangemodule.NewAppModule(appCodec, app.ExchangeKeeper), provwasm.NewWrapper(appCodec, app.WasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.NameKeeper), // IBC diff --git a/app/upgrades.go b/app/upgrades.go index 0e477072a1..32606cba94 100644 --- a/app/upgrades.go +++ b/app/upgrades.go @@ -17,6 +17,7 @@ import ( attributekeeper "github.com/provenance-io/provenance/x/attribute/keeper" attributetypes "github.com/provenance-io/provenance/x/attribute/types" + "github.com/provenance-io/provenance/x/exchange" "github.com/provenance-io/provenance/x/hold" ibchookstypes "github.com/provenance-io/provenance/x/ibchooks/types" msgfeetypes "github.com/provenance-io/provenance/x/msgfees/types" @@ -116,7 +117,7 @@ var upgrades = map[string]appUpgrade{ return vm, nil }, - Added: []string{icqtypes.ModuleName, oracletypes.ModuleName, ibchookstypes.ModuleName, hold.ModuleName}, + Added: []string{icqtypes.ModuleName, oracletypes.ModuleName, ibchookstypes.ModuleName, hold.ModuleName, exchange.ModuleName}, }, "saffron": { // upgrade for v1.17.0, Handler: func(ctx sdk.Context, app *App, vm module.VersionMap) (module.VersionMap, error) { @@ -135,7 +136,7 @@ var upgrades = map[string]appUpgrade{ return vm, nil }, - Added: []string{icqtypes.ModuleName, oracletypes.ModuleName, ibchookstypes.ModuleName, hold.ModuleName}, + Added: []string{icqtypes.ModuleName, oracletypes.ModuleName, ibchookstypes.ModuleName, hold.ModuleName, exchange.ModuleName}, }, // TODO - Add new upgrade definitions here. } From f0e1230b87e3d50b64bcb101ba083c5840feb897 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sun, 8 Oct 2023 22:13:23 -0600 Subject: [PATCH 265/309] [1658]: Create ValidateAuthority and use that instead of IsAuthority and wrongAuthErr. Get rid of wrongAuthErr. --- x/exchange/keeper/grpc_query.go | 8 ++++---- x/exchange/keeper/keeper.go | 13 ++++++++----- x/exchange/keeper/msg_server.go | 12 ++++++------ 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index d91d1cf4b8..7d1ba69434 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -266,8 +266,8 @@ func (k QueryServer) QueryValidateCreateMarket(goCtx context.Context, req *excha return resp, nil } - if msg.Authority != k.authority { - resp.Error = k.wrongAuthErr(msg.Authority).Error() + if err := k.ValidateAuthority(msg.Authority); err != nil { + resp.Error = err.Error() return resp, nil } @@ -329,8 +329,8 @@ func (k QueryServer) QueryValidateManageFees(goCtx context.Context, req *exchang return resp, nil } - if msg.Authority != k.authority { - resp.Error = k.wrongAuthErr(msg.Authority).Error() + if err := k.ValidateAuthority(msg.Authority); err != nil { + resp.Error = err.Error() return resp, nil } diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index fa515aa5c4..534aedd2b0 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -74,11 +74,6 @@ func (k Keeper) emitEvents(ctx sdk.Context, events []proto.Message) { } } -// wrongAuthErr returns the error to use when a message's authority isn't what's required. -func (k Keeper) wrongAuthErr(badAuthority string) error { - return govtypes.ErrInvalidSigner.Wrapf("expected %s got %s", k.GetAuthority(), badAuthority) -} - // GetAuthority gets the address (as bech32) that has governance authority. func (k Keeper) GetAuthority() string { return k.authority @@ -89,6 +84,14 @@ func (k Keeper) IsAuthority(addr string) bool { return addr == k.authority } +// ValidateAuthority returns an error if the provided address is not the authority. +func (k Keeper) ValidateAuthority(addr string) error { + if !k.IsAuthority(addr) { + return govtypes.ErrInvalidSigner.Wrapf("expected %s got %s", k.GetAuthority(), addr) + } + return nil +} + // GetFeeCollectorName gets the name of the fee collector. func (k Keeper) GetFeeCollectorName() string { return k.feeCollectorName diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index e5c2a998a9..50ac8b9096 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -175,8 +175,8 @@ func (k MsgServer) MarketManageReqAttrs(goCtx context.Context, msg *exchange.Msg // GovCreateMarket is a governance proposal endpoint for creating a market. func (k MsgServer) GovCreateMarket(goCtx context.Context, msg *exchange.MsgGovCreateMarketRequest) (*exchange.MsgGovCreateMarketResponse, error) { - if !k.IsAuthority(msg.Authority) { - return nil, k.wrongAuthErr(msg.Authority) + if err := k.ValidateAuthority(msg.Authority); err != nil { + return nil, err } ctx := sdk.UnwrapSDKContext(goCtx) @@ -190,8 +190,8 @@ func (k MsgServer) GovCreateMarket(goCtx context.Context, msg *exchange.MsgGovCr // GovManageFees is a governance proposal endpoint for updating a market's fees. func (k MsgServer) GovManageFees(goCtx context.Context, msg *exchange.MsgGovManageFeesRequest) (*exchange.MsgGovManageFeesResponse, error) { - if !k.IsAuthority(msg.Authority) { - return nil, k.wrongAuthErr(msg.Authority) + if err := k.ValidateAuthority(msg.Authority); err != nil { + return nil, err } ctx := sdk.UnwrapSDKContext(goCtx) @@ -202,8 +202,8 @@ func (k MsgServer) GovManageFees(goCtx context.Context, msg *exchange.MsgGovMana // GovUpdateParams is a governance proposal endpoint for updating the exchange module's params. func (k MsgServer) GovUpdateParams(goCtx context.Context, msg *exchange.MsgGovUpdateParamsRequest) (*exchange.MsgGovUpdateParamsResponse, error) { - if !k.IsAuthority(msg.Authority) { - return nil, k.wrongAuthErr(msg.Authority) + if err := k.ValidateAuthority(msg.Authority); err != nil { + return nil, err } ctx := sdk.UnwrapSDKContext(goCtx) From 56f89c2002eaa244ee8b1ba071c7e8f403410b61 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 9 Oct 2023 02:22:30 -0600 Subject: [PATCH 266/309] [1658]: Tweak IsAuthority to be case insensitive, and ValidateAuthority to wrap the addresses in quotes. --- x/exchange/keeper/keeper.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index 534aedd2b0..df424e1495 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -2,6 +2,7 @@ package keeper import ( "fmt" + "strings" "github.com/gogo/protobuf/proto" @@ -81,13 +82,13 @@ func (k Keeper) GetAuthority() string { // IsAuthority returns true if the provided address bech32 string is the authority address. func (k Keeper) IsAuthority(addr string) bool { - return addr == k.authority + return strings.EqualFold(k.authority, addr) } // ValidateAuthority returns an error if the provided address is not the authority. func (k Keeper) ValidateAuthority(addr string) error { if !k.IsAuthority(addr) { - return govtypes.ErrInvalidSigner.Wrapf("expected %s got %s", k.GetAuthority(), addr) + return govtypes.ErrInvalidSigner.Wrapf("expected %q got %q", k.GetAuthority(), addr) } return nil } From 9fe33ba7d1451724bb8fb74ad41a7bac8918bfc1 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 9 Oct 2023 02:23:23 -0600 Subject: [PATCH 267/309] [1658]: Create the mock keepers and create the beginnings of the keeper tests. --- x/exchange/keeper/export_test.go | 42 +++ x/exchange/keeper/keeper_test.go | 251 ++++++++++++++ x/exchange/keeper/mocks_test.go | 573 +++++++++++++++++++++++++++++++ 3 files changed, 866 insertions(+) create mode 100644 x/exchange/keeper/keeper_test.go create mode 100644 x/exchange/keeper/mocks_test.go diff --git a/x/exchange/keeper/export_test.go b/x/exchange/keeper/export_test.go index c510f05659..f27e9de834 100644 --- a/x/exchange/keeper/export_test.go +++ b/x/exchange/keeper/export_test.go @@ -1,7 +1,49 @@ package keeper +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/provenance-io/provenance/x/exchange" +) + // This file is in the keeper package (not keeper_test) so that it can expose // some private keeper stuff for unit testing. +// WithAccountKeeper is a test-only method that returns a new Keeper that uses the provided AccountKeeper. +func (k Keeper) WithAccountKeeper(accountKeeper exchange.AccountKeeper) Keeper { + k.accountKeeper = accountKeeper + return k +} + +// WithAttributeKeeper is a test-only method that returns a new Keeper that uses the provided AttributeKeeper. +func (k Keeper) WithAttributeKeeper(attrKeeper exchange.AttributeKeeper) Keeper { + k.attrKeeper = attrKeeper + return k +} + +// WithBankKeeper is a test-only method that returns a new Keeper that uses the provided BankKeeper. +func (k Keeper) WithBankKeeper(bankKeeper exchange.BankKeeper) Keeper { + k.bankKeeper = bankKeeper + return k +} + +// WithHoldKeeper is a test-only method that returns a new Keeper that uses the provided HoldKeeper. +func (k Keeper) WithHoldKeeper(holdKeeper exchange.HoldKeeper) Keeper { + k.holdKeeper = holdKeeper + return k +} + // ParseLengthPrefixedAddr is a test-only exposure of parseLengthPrefixedAddr. var ParseLengthPrefixedAddr = parseLengthPrefixedAddr + +// GetStore is a test-only exposure of getStore. +func (k Keeper) GetStore(ctx sdk.Context) sdk.KVStore { + return k.getStore(ctx) +} + +var ( + // DeleteAll is a test-only exposure of deleteAll. + DeleteAll = deleteAll + // Iterate is a test-only exposure of iterate. + Iterate = iterate +) diff --git a/x/exchange/keeper/keeper_test.go b/x/exchange/keeper/keeper_test.go new file mode 100644 index 0000000000..58da8c6a5e --- /dev/null +++ b/x/exchange/keeper/keeper_test.go @@ -0,0 +1,251 @@ +package keeper_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/bank/testutil" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/provenance-io/provenance/app" + "github.com/provenance-io/provenance/testutil/assertions" + "github.com/provenance-io/provenance/x/exchange" + "github.com/provenance-io/provenance/x/exchange/keeper" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" +) + +type TestSuite struct { + suite.Suite + + app *app.App + sdkCtx sdk.Context + stdlibCtx context.Context + + k keeper.Keeper + acctKeeper exchange.AccountKeeper + attrKeeper exchange.AttributeKeeper + bankKeeper exchange.BankKeeper + holdKeeper exchange.HoldKeeper + + bondDenom string + initBal sdk.Coins + initAmount int64 + + addr1 sdk.AccAddress + addr2 sdk.AccAddress + addr3 sdk.AccAddress + addr4 sdk.AccAddress + addr5 sdk.AccAddress +} + +func (s *TestSuite) SetupTest() { + s.app = app.Setup(s.T()) + s.sdkCtx = s.app.BaseApp.NewContext(false, tmproto.Header{}) + s.stdlibCtx = sdk.WrapSDKContext(s.sdkCtx) + s.k = s.app.ExchangeKeeper + s.acctKeeper = s.app.AccountKeeper + s.attrKeeper = s.app.AttributeKeeper + s.bankKeeper = s.app.BankKeeper + s.holdKeeper = s.app.HoldKeeper + + s.bondDenom = s.app.StakingKeeper.BondDenom(s.sdkCtx) + s.initAmount = 1_000_000_000 + s.initBal = sdk.NewCoins(sdk.NewCoin(s.bondDenom, sdk.NewInt(s.initAmount))) + + addrs := app.AddTestAddrsIncremental(s.app, s.sdkCtx, 5, sdk.NewInt(s.initAmount)) + s.addr1 = addrs[0] + s.addr2 = addrs[1] + s.addr3 = addrs[2] + s.addr4 = addrs[3] + s.addr5 = addrs[4] +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(TestSuite)) +} + +// coins creates an sdk.Coins from a string, requiring it to work. +func (s *TestSuite) coins(coins string) sdk.Coins { + s.T().Helper() + rv, err := sdk.ParseCoinsNormalized(coins) + s.Require().NoError(err, "ParseCoinsNormalized(%q)", coins) + return rv +} + +// coin creates a new coin without doing any validation on it. +func (s *TestSuite) coin(amount int64, denom string) sdk.Coin { + return sdk.Coin{ + Amount: s.int(amount), + Denom: denom, + } +} + +// int is a shorter way to call sdkmath.NewInt. +func (s *TestSuite) int(amount int64) sdkmath.Int { + return sdkmath.NewInt(amount) +} + +// intStr creates an sdkmath.Int from a string, requiring it to work. +func (s *TestSuite) intStr(amount string) sdkmath.Int { + s.T().Helper() + rv, ok := sdkmath.NewIntFromString(amount) + s.Require().True(ok, "NewIntFromString(%q) ok bool", amount) + return rv +} + +// getAddrName returns the name of the variable in this TestSuite holding the provided address. +func (s *TestSuite) getAddrName(addr sdk.AccAddress) string { + switch string(addr) { + case string(s.addr1): + return "addr1" + case string(s.addr2): + return "addr2" + case string(s.addr3): + return "addr3" + case string(s.addr4): + return "addr4" + case string(s.addr5): + return "addr5" + default: + return addr.String() + } +} + +// getAddrStrName returns the name of the variable in this TestSuite holding the provided address. +func (s *TestSuite) getAddrStrName(addrStr string) string { + addr, err := sdk.AccAddressFromBech32(addrStr) + if err != nil { + return addrStr + } + return s.getAddrName(addr) +} + +// getStore gets the exchange store. +func (s *TestSuite) getStore() sdk.KVStore { + return s.k.GetStore(s.sdkCtx) +} + +// clearExchangeState deletes everything from the exchange state store. +func (s *TestSuite) clearExchangeState() { + keeper.DeleteAll(s.getStore(), nil) +} + +// stateEntryString converts the provided key and value into a ""="" string. +func (s *TestSuite) stateEntryString(key, value []byte) string { + return fmt.Sprintf("%q=%q", key, value) +} + +// dumpHoldState creates a string for each entry in the hold state store. +// Each entry has the format `""=""`. +func (s *TestSuite) dumpHoldState() []string { + var rv []string + keeper.Iterate(s.getStore(), nil, func(key, value []byte) bool { + rv = append(rv, s.stateEntryString(key, value)) + return false + }) + return rv +} + +// requireFundAccount calls testutil.FundAccount, making sure it doesn't panic or return an error. +func (s *TestSuite) requireFundAccount(addr sdk.AccAddress, coins string) { + assertions.RequireNotPanicsNoErrorf(s.T(), func() error { + return testutil.FundAccount(s.app.BankKeeper, s.sdkCtx, addr, s.coins(coins)) + }, "FundAccount(%s, %q)", s.getAddrName(addr), coins) +} + +// assertErrorValue is a wrapper for assertions.AssertErrorValue. +func (s *TestSuite) assertErrorValue(theError error, expected string, msgAndArgs ...interface{}) bool { + return assertions.AssertErrorValue(s.T(), theError, expected, msgAndArgs...) +} + +func (s *TestSuite) TestKeeper_GetAuthority() { + expected := authtypes.NewModuleAddress(govtypes.ModuleName).String() + var actual string + testFunc := func() { + actual = s.k.GetAuthority() + } + s.Require().NotPanics(testFunc, "GetAuthority()") + s.Assert().Equal(expected, actual, "GetAuthority() result") +} + +func (s *TestSuite) TestKeeper_IsAuthority() { + tests := []struct { + name string + addr string + exp bool + }{ + {name: "empty string", addr: "", exp: false}, + {name: "whitespace", addr: strings.Repeat(" ", len(s.k.GetAuthority())), exp: false}, + {name: "authority", addr: s.k.GetAuthority(), exp: true}, + {name: "authority upper-case", addr: strings.ToUpper(s.k.GetAuthority()), exp: true}, + {name: "authority space", addr: s.k.GetAuthority() + " ", exp: false}, + {name: "space authority", addr: " " + s.k.GetAuthority(), exp: false}, + {name: "other addr", addr: s.addr1.String(), exp: false}, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + var actual bool + testFunc := func() { + actual = s.k.IsAuthority(tc.addr) + } + s.Require().NotPanics(testFunc, "IsAuthority(%q)", tc.addr) + s.Assert().Equal(tc.exp, actual, "IsAuthority(%q) result", tc.addr) + }) + } +} + +func (s *TestSuite) TestKeeper_ValidateAuthority() { + tests := []struct { + name string + addr string + expErr bool + }{ + {name: "empty string", addr: "", expErr: true}, + {name: "whitespace", addr: strings.Repeat(" ", len(s.k.GetAuthority())), expErr: true}, + {name: "authority", addr: s.k.GetAuthority(), expErr: false}, + {name: "authority upper-case", addr: strings.ToUpper(s.k.GetAuthority()), expErr: false}, + {name: "authority space", addr: s.k.GetAuthority() + " ", expErr: true}, + {name: "space authority", addr: " " + s.k.GetAuthority(), expErr: true}, + {name: "other addr", addr: s.addr1.String(), expErr: true}, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + expErr := "" + if tc.expErr { + expErr = fmt.Sprintf("expected %q got %q: expected gov account as only signer for proposal message", s.k.GetAuthority(), tc.addr) + } + var err error + testFunc := func() { + err = s.k.ValidateAuthority(tc.addr) + } + s.Require().NotPanics(testFunc, "ValidateAuthority(%q)", tc.addr) + s.assertErrorValue(err, expErr, "ValidateAuthority(%q) error", tc.addr) + }) + } +} + +func (s *TestSuite) TestKeeper_GetFeeCollectorName() { + expected := authtypes.FeeCollectorName + var actual string + testFunc := func() { + actual = s.k.GetFeeCollectorName() + } + s.Require().NotPanics(testFunc, "GetFeeCollectorName()") + s.Assert().Equal(expected, actual, "GetFeeCollectorName() result") +} + +// TODO[1658]: func (s *TestSuite) TestKeeper_DoTransfer() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CalculateExchangeSplit() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CollectFee() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CollectFees() diff --git a/x/exchange/keeper/mocks_test.go b/x/exchange/keeper/mocks_test.go new file mode 100644 index 0000000000..67abd5852a --- /dev/null +++ b/x/exchange/keeper/mocks_test.go @@ -0,0 +1,573 @@ +package keeper_test + +import ( + "errors" + "fmt" + "strings" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/quarantine" + attrtypes "github.com/provenance-io/provenance/x/attribute/types" + "github.com/provenance-io/provenance/x/exchange" +) + +// toStrings converts a slice to indexed strings using the provided stringer func. +func toStrings[T any](vals []T, stringer func(T) string) []string { + if vals == nil { + return nil + } + rv := make([]string, len(vals)) + for i, val := range vals { + rv[i] = fmt.Sprintf("[%d]:%s", i, stringer(val)) + } + return rv +} + +// assertEqualSlice asserts that expected = actual and returns true if so. +// If not, returns false and the stringer is applied to each entry and the comparison +// is redone on the strings in the hopes that it helps identify the problem. +func assertEqualSlice[T any](s *TestSuite, expected, actual []T, stringer func(T) string, msg string, args ...interface{}) bool { + if s.Assert().Equalf(expected, actual, msg, args...) { + return true + } + // compare each as strings in the hopes that makes it easier to identify the problem. + expStrs := toStrings(expected, stringer) + actStrs := toStrings(actual, stringer) + s.Assert().Equalf(expStrs, actStrs, "strings: "+msg, args...) + return false +} + +// MockAccountKeeper satisfies the exchange.AccountKeeper interface but just records the calls and allows dictation of results. +type MockAccountKeeper struct { + GetAccountCalls []sdk.AccAddress + SetAccountCalls []authtypes.AccountI + HasAccountCalls []sdk.AccAddress + NewAccountCalls []authtypes.AccountI + GetAccountResultsMap map[string]authtypes.AccountI + HasAccountResultsMap map[string]bool + NewAccountResultsMap map[string]authtypes.AccountI +} + +var _ exchange.AccountKeeper = (*MockAccountKeeper)(nil) + +// NewMockAccountKeeper creates a new empty MockAccountKeeper. +// Follow it up with WithGetAccountResult, WithHasAccountResult, +// and/or WithNewAccountResult to dictate results. +func NewMockAccountKeeper() *MockAccountKeeper { + return &MockAccountKeeper{ + GetAccountResultsMap: make(map[string]authtypes.AccountI), + HasAccountResultsMap: make(map[string]bool), + NewAccountResultsMap: make(map[string]authtypes.AccountI), + } +} + +// WithGetAccountResult associates the provided address and result for use with calls to GetAccount. +// When GetAccount is called, if the address provided has an entry here, that is returned, otherwise, nil is returned. +// This method both updates the receiver and returns it. +func (k *MockAccountKeeper) WithGetAccountResult(addr sdk.AccAddress, result authtypes.AccountI) *MockAccountKeeper { + k.GetAccountResultsMap[string(addr)] = result + return k +} + +// WithGetAccountResult associates the provided address and result for use with calls to HasAccount. +// When HasAccount is called, if the address provided has an entry here, that is returned, otherwise, false is returned. +// This method both updates the receiver and returns it. +func (k *MockAccountKeeper) WithHasAccountResult(addr sdk.AccAddress, result bool) *MockAccountKeeper { + k.HasAccountResultsMap[string(addr)] = result + return k +} + +// WithGetAccountResult associates the provided address and result for use with calls to NewAccount. +// When NewAccount is called, if the address provided has an entry here, that is returned, +// otherwise, the provided AccountI is returned. +// This method both updates the receiver and returns it. +func (k *MockAccountKeeper) WithNewAccountResult(result authtypes.AccountI) *MockAccountKeeper { + k.NewAccountResultsMap[string(result.GetAddress())] = result + return k +} + +func (k *MockAccountKeeper) GetAccount(_ sdk.Context, addr sdk.AccAddress) authtypes.AccountI { + k.GetAccountCalls = append(k.GetAccountCalls, addr) + if rv, found := k.GetAccountResultsMap[string(addr)]; found { + return rv + } + return nil +} + +func (k *MockAccountKeeper) SetAccount(_ sdk.Context, acc authtypes.AccountI) { + k.SetAccountCalls = append(k.SetAccountCalls, acc) +} + +func (k *MockAccountKeeper) HasAccount(_ sdk.Context, addr sdk.AccAddress) bool { + k.HasAccountCalls = append(k.HasAccountCalls, addr) + if rv, found := k.HasAccountResultsMap[string(addr)]; found { + return rv + } + return false +} + +func (k *MockAccountKeeper) NewAccount(_ sdk.Context, acc authtypes.AccountI) authtypes.AccountI { + k.NewAccountCalls = append(k.NewAccountCalls, acc) + if rv, found := k.NewAccountResultsMap[string(acc.GetAddress())]; found { + return rv + } + return acc +} + +// assertEqualGetAccountCalls asserts that a mock keeper's GetAccountCalls match the provided expected calls. +func (s *TestSuite) assertEqualGetAccountCalls(mk *MockAccountKeeper, expected []sdk.AccAddress, msg string, args ...interface{}) bool { + return assertEqualSlice(s, expected, mk.GetAccountCalls, s.getAddrName, + msg+" GetAccount calls", args...) +} + +// assertEqualSetAccountCalls asserts that a mock keeper's SetAccountCalls match the provided expected calls. +func (s *TestSuite) assertEqualSetAccountCalls(mk *MockAccountKeeper, expected []authtypes.AccountI, msg string, args ...interface{}) bool { + return assertEqualSlice(s, expected, mk.SetAccountCalls, authtypes.AccountI.String, + msg+" SetAccount calls", args...) +} + +// assertEqualHasAccountCalls asserts that a mock keeper's HasAccountCalls match the provided expected calls. +func (s *TestSuite) assertEqualHasAccountCalls(mk *MockAccountKeeper, expected []sdk.AccAddress, msg string, args ...interface{}) bool { + return assertEqualSlice(s, expected, mk.HasAccountCalls, s.getAddrName, + msg+" HasAccount calls", args...) +} + +// assertEqualNewAccountCalls asserts that a mock keeper's NewAccountCalls match the provided expected calls. +func (s *TestSuite) assertEqualNewAccountCalls(mk *MockAccountKeeper, expected []authtypes.AccountI, msg string, args ...interface{}) bool { + return assertEqualSlice(s, expected, mk.NewAccountCalls, authtypes.AccountI.String, + msg+" NewAccount calls", args...) +} + +type MockAttributeKeeper struct { + GetAllAttributesAddrCalls [][]byte + GetAllAttributesAddrResultsMap map[string]*GetAllAttributesAddrResult +} + +var _ exchange.AttributeKeeper = (*MockAttributeKeeper)(nil) + +func NewMockAttributeKeeper() *MockAttributeKeeper { + return &MockAttributeKeeper{ + GetAllAttributesAddrResultsMap: make(map[string]*GetAllAttributesAddrResult), + } +} + +// WithGetAllAttributesAddrResult sets up the provided address to return the given attrs +// and error from calls to GetAllAttributesAddr. An empty string means no error. +// This method both updates the receiver and returns it. +func (k *MockAttributeKeeper) WithGetAllAttributesAddrResult(addr []byte, attrs []attrtypes.Attribute, errStr string) *MockAttributeKeeper { + k.GetAllAttributesAddrResultsMap[string(addr)] = NewGetAllAttributesAddrResult(attrs, errStr) + return k +} + +func (k *MockAttributeKeeper) GetAllAttributesAddr(_ sdk.Context, addr []byte) ([]attrtypes.Attribute, error) { + k.GetAllAttributesAddrCalls = append(k.GetAllAttributesAddrCalls, addr) + if rv, found := k.GetAllAttributesAddrResultsMap[string(addr)]; found { + return rv.attrs, rv.err + } + return nil, nil +} + +// assertEqualGetAllAttributesAddrCalls asserts that a mock keeper's GetAllAttributesAddrCalls match the provided expected calls. +func (s *TestSuite) assertEqualGetAllAttributesAddrCalls(mk *MockAttributeKeeper, expected [][]byte, msg string, args ...interface{}) bool { + return assertEqualSlice(s, expected, mk.GetAllAttributesAddrCalls, + func(addr []byte) string { + return s.getAddrName(addr) + }, + msg+" NewAccount calls", args...) +} + +// GetAllAttributesAddrResult contains the result args to return for a GetAllAttributesAddr call. +type GetAllAttributesAddrResult struct { + attrs []attrtypes.Attribute + err error +} + +// NewGetAllAttributesAddrResult creates a new GetAllAttributesAddrResult from the provided stuff. +func NewGetAllAttributesAddrResult(attrs []attrtypes.Attribute, errStr string) *GetAllAttributesAddrResult { + rv := &GetAllAttributesAddrResult{attrs: attrs} + if len(errStr) > 0 { + rv.err = errors.New(errStr) + } + return rv +} + +// MockBankKeeper satisfies the exchange.BankKeeper interface but just records the calls and allows dictation of results. +type MockBankKeeper struct { + SendCoinsCalls []*SendCoinsArgs + SendCoinsResultsQueue []string + SendCoinsFromAccountToModuleCalls []*SendCoinsFromAccountToModuleArgs + SendCoinsFromAccountToModuleResultsQueue []string + InputOutputCoinsCalls []*InputOutputCoinsArgs + InputOutputCoinsResultsQueue []string +} + +var _ exchange.BankKeeper = (*MockBankKeeper)(nil) + +// NewMockBankKeeper creates a new empty MockBankKeeper. +// Follow it up with WithSendCoinsResults, WithSendCoinsFromAccountToModuleResults, +// and/or WithInputOutputCoinsResults to dictate results. +func NewMockBankKeeper() *MockBankKeeper { + return &MockBankKeeper{} +} + +// WithSendCoinsResults queues up the provided error strings to be returned from SendCoins. +// An empty string means no error. Each entry is used only once. If entries run out, nil is returned. +// This method both updates the receiver and returns it. +func (k *MockBankKeeper) WithSendCoinsResults(errs ...string) *MockBankKeeper { + k.SendCoinsResultsQueue = append(k.SendCoinsResultsQueue, errs...) + return k +} + +// WithSendCoinsFromAccountToModuleResults queues up the provided error strings to be returned from SendCoinsFromAccountToModule. +// An empty string means no error. Each entry is used only once. If entries run out, nil is returned. +// This method both updates the receiver and returns it. +func (k *MockBankKeeper) WithSendCoinsFromAccountToModuleResults(errs ...string) *MockBankKeeper { + k.SendCoinsFromAccountToModuleResultsQueue = append(k.SendCoinsFromAccountToModuleResultsQueue, errs...) + return k +} + +// WithInputOutputCoinsResults queues up the provided error strings to be returned from InputOutputCoins. +// An empty string means no error. Each entry is used only once. If entries run out, nil is returned. +// This method both updates the receiver and returns it. +func (k *MockBankKeeper) WithInputOutputCoinsResults(errs ...string) *MockBankKeeper { + k.InputOutputCoinsResultsQueue = append(k.InputOutputCoinsResultsQueue, errs...) + return k +} + +func (k MockBankKeeper) SendCoins(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error { + k.SendCoinsCalls = append(k.SendCoinsCalls, NewSendCoinsArgs(ctx, fromAddr, toAddr, amt)) + var err error + if len(k.SendCoinsResultsQueue) > 0 { + if len(k.SendCoinsResultsQueue[0]) > 0 { + err = errors.New(k.SendCoinsResultsQueue[0]) + } + k.SendCoinsResultsQueue = k.SendCoinsResultsQueue[1:] + } + return err +} + +func (k MockBankKeeper) SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error { + k.SendCoinsFromAccountToModuleCalls = append(k.SendCoinsFromAccountToModuleCalls, + NewSendCoinsFromAccountToModuleArgs(ctx, senderAddr, recipientModule, amt)) + var err error + if len(k.SendCoinsFromAccountToModuleResultsQueue) > 0 { + if len(k.SendCoinsFromAccountToModuleResultsQueue[0]) > 0 { + err = errors.New(k.SendCoinsFromAccountToModuleResultsQueue[0]) + } + k.SendCoinsFromAccountToModuleResultsQueue = k.SendCoinsFromAccountToModuleResultsQueue[1:] + } + return err +} + +func (k MockBankKeeper) InputOutputCoins(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) error { + k.InputOutputCoinsCalls = append(k.InputOutputCoinsCalls, NewInputOutputCoinsArgs(ctx, inputs, outputs)) + var err error + if len(k.InputOutputCoinsResultsQueue) > 0 { + if len(k.InputOutputCoinsResultsQueue[0]) > 0 { + err = errors.New(k.InputOutputCoinsResultsQueue[0]) + } + k.InputOutputCoinsResultsQueue = k.InputOutputCoinsResultsQueue[1:] + } + return err +} + +// assertEqualSendCoinsCalls asserts that a mock keeper's SendCoinsCalls match the provided expected calls. +func (s *TestSuite) assertEqualSendCoinsCalls(mk *MockBankKeeper, expected []*SendCoinsArgs, msg string, args ...interface{}) bool { + return assertEqualSlice(s, expected, mk.SendCoinsCalls, s.sendCoinsArgsString, + msg+" SendCoins calls", args...) +} + +// assertEqualSendCoinsFromAccountToModuleCalls asserts that a mock keeper's +// SendCoinsFromAccountToModuleCalls match the provided expected calls. +func (s *TestSuite) assertEqualSendCoinsFromAccountToModuleCalls(mk *MockBankKeeper, expected []*SendCoinsFromAccountToModuleArgs, msg string, args ...interface{}) bool { + return assertEqualSlice(s, expected, mk.SendCoinsFromAccountToModuleCalls, s.sendCoinsFromAccountToModuleArgsString, + msg+" SendCoinsFromAccountToModule calls", args...) +} + +// assertEqualInputOutputCoinsCalls asserts that a mock keeper's InputOutputCoinsCalls match the provided expected calls. +func (s *TestSuite) assertEqualInputOutputCoinsCalls(mk *MockBankKeeper, expected []*InputOutputCoinsArgs, msg string, args ...interface{}) bool { + return assertEqualSlice(s, expected, mk.InputOutputCoinsCalls, s.inputOutputCoinsArgsString, + msg+" InputOutputCoins calls", args...) +} + +// SendCoinsArgs is a record of a call that is made to SendCoins. +type SendCoinsArgs struct { + ctxHasQuarantineBypass bool + fromAddr sdk.AccAddress + toAddr sdk.AccAddress + amt sdk.Coins +} + +// NewSendCoinsArgs creates a new record of args provided to a call to SendCoins. +func NewSendCoinsArgs(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) *SendCoinsArgs { + return &SendCoinsArgs{ + ctxHasQuarantineBypass: quarantine.HasBypass(ctx), + fromAddr: fromAddr, + toAddr: toAddr, + amt: amt, + } +} + +// sendCoinsArgsString creates a string of a SendCoinsArgs +// substituting the address names as possible. +func (s *TestSuite) sendCoinsArgsString(a *SendCoinsArgs) string { + return fmt.Sprintf("{q-bypass:%t, from:%s, to:%s, amt:%s}", + a.ctxHasQuarantineBypass, s.getAddrName(a.fromAddr), s.getAddrName(a.toAddr), a.amt) +} + +// SendCoinsFromAccountToModuleArgs is a record of a call that is made to SendCoinsFromAccountToModule. +type SendCoinsFromAccountToModuleArgs struct { + ctxHasQuarantineBypass bool + senderAddr sdk.AccAddress + recipientModule string + amt sdk.Coins +} + +// NewSendCoinsFromAccountToModuleArgs creates a new record of args provided to a call to SendCoinsFromAccountToModule. +func NewSendCoinsFromAccountToModuleArgs(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) *SendCoinsFromAccountToModuleArgs { + return &SendCoinsFromAccountToModuleArgs{ + ctxHasQuarantineBypass: quarantine.HasBypass(ctx), + senderAddr: senderAddr, + recipientModule: recipientModule, + amt: amt, + } +} + +// sendCoinsFromAccountToModuleArgsString creates a string of a SendCoinsFromAccountToModuleArgs +// substituting the address names as possible. +func (s *TestSuite) sendCoinsFromAccountToModuleArgsString(a *SendCoinsFromAccountToModuleArgs) string { + return fmt.Sprintf("{q-bypass:%t, from:%s, to:%s, amt:%s}", + a.ctxHasQuarantineBypass, s.getAddrName(a.senderAddr), a.recipientModule, a.amt) +} + +// InputOutputCoinsArgs is a record of a call that is made to InputOutputCoins. +type InputOutputCoinsArgs struct { + ctxHasQuarantineBypass bool + inputs []banktypes.Input + outputs []banktypes.Output +} + +// NewInputOutputCoinsArgs creates a new record of args provided to a call to InputOutputCoins. +func NewInputOutputCoinsArgs(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) *InputOutputCoinsArgs { + return &InputOutputCoinsArgs{ + ctxHasQuarantineBypass: quarantine.HasBypass(ctx), + inputs: inputs, + outputs: outputs, + } +} + +// inputOutputCoinsArgsString creates a string of a InputOutputCoinsArgs substituting the address names as possible. +func (s *TestSuite) inputOutputCoinsArgsString(a *InputOutputCoinsArgs) string { + return fmt.Sprintf("{q-bypass:%t, inputs:%s, outputs:%s}", + a.ctxHasQuarantineBypass, s.inputsString(a.inputs), s.outputsString(a.outputs)) +} + +// inputString creates a string of a banktypes.Input substituting the address names as possible. +func (s *TestSuite) inputString(a banktypes.Input) string { + return fmt.Sprintf("I{Address:%s, Coins:%s}", s.getAddrStrName(a.Address), a.Coins) +} + +// inputsString creates a string of a slice of banktypes.Input substituting the address names as possible. +func (s *TestSuite) inputsString(vals []banktypes.Input) string { + strs := toStrings(vals, s.inputString) + return fmt.Sprintf("{%s}", strings.Join(strs, ", ")) +} + +// outputString creates a string of a banktypes.Output substituting the address names as possible. +func (s *TestSuite) outputString(a banktypes.Output) string { + return fmt.Sprintf("O{Address:%s, Coins:%s}", s.getAddrStrName(a.Address), a.Coins) +} + +// outputsString creates a string of a slice of banktypes.Output substituting the address names as possible. +func (s *TestSuite) outputsString(vals []banktypes.Output) string { + strs := toStrings(vals, s.outputString) + return fmt.Sprintf("{%s}", strings.Join(strs, ", ")) +} + +// MockHoldKeeper satisfies the exchange.HoldKeeper interface but just records the calls and allows dictation of results. +type MockHoldKeeper struct { + AddHoldCalls []*AddHoldArgs + ReleaseHoldCalls []*ReleaseHoldArgs + GetHoldCoinCalls []*GetHoldCoinArgs + AddHoldResultsQueue []string + ReleaseHoldResultsQueue []string + GetHoldCoinResultsMap map[string]map[string]*GetHoldCoinResults +} + +var _ exchange.HoldKeeper = (*MockHoldKeeper)(nil) + +// NewMockHoldKeeper creates a new empty MockHoldKeeper. +// Follow it up with WithAddHoldResults, WithReleaseHoldResults, WithGetHoldCoinResult +// and/or WithGetHoldCoinErrorResult to dictate results. +func NewMockHoldKeeper() *MockHoldKeeper { + return &MockHoldKeeper{ + GetHoldCoinResultsMap: make(map[string]map[string]*GetHoldCoinResults), + } +} + +// WithAddHoldResults queues up the provided error strings to be returned from AddHold. +// An empty string means no error. Each entry is used only once. If entries run out, nil is returned. +// This method both updates the receiver and returns it. +func (k *MockHoldKeeper) WithAddHoldResults(errs ...string) *MockHoldKeeper { + k.AddHoldResultsQueue = append(k.AddHoldResultsQueue, errs...) + return k +} + +// WithReleaseHoldResults queues up the provided error strings to be returned from ReleaseHold. +// An empty string means no error. Each entry is used only once. If entries run out, nil is returned. +// This method both updates the receiver and returns it. +func (k *MockHoldKeeper) WithReleaseHoldResults(errs ...string) *MockHoldKeeper { + k.ReleaseHoldResultsQueue = append(k.ReleaseHoldResultsQueue, errs...) + return k +} + +// WithGetHoldCoinResult sets the results of GetHoldCoin for the provided address and coins. +// If there isn't an entry for a requested address and denom, a zero-coin and nil error will be returned. +// To cause an error to be returned, use WithGetHoldCoinErrorResult. +func (k *MockHoldKeeper) WithGetHoldCoinResult(addr sdk.AccAddress, coins ...sdk.Coin) *MockHoldKeeper { + denomMap, found := k.GetHoldCoinResultsMap[string(addr)] + if !found { + denomMap = make(map[string]*GetHoldCoinResults) + k.GetHoldCoinResultsMap[string(addr)] = denomMap + } + for _, coin := range coins { + denomMap[coin.Denom] = &GetHoldCoinResults{amount: coin.Amount} + } + return k +} + +// WithGetHoldCoinErrorResult sets the result of GetHoldCoin for the provided address and denom to be the provided error. +// An empty string means no error. A zero-coin is also returned with the result. +// To return a coin value without an error, use WithGetHoldCoinResult. +func (k *MockHoldKeeper) WithGetHoldCoinErrorResult(addr sdk.AccAddress, denom string, errStr string) *MockHoldKeeper { + denomMap, found := k.GetHoldCoinResultsMap[string(addr)] + if !found { + denomMap = make(map[string]*GetHoldCoinResults) + k.GetHoldCoinResultsMap[string(addr)] = denomMap + } + denomMap[denom] = &GetHoldCoinResults{amount: sdkmath.ZeroInt()} + if len(errStr) > 0 { + denomMap[denom].err = errors.New(errStr) + } + return k +} + +func (k *MockHoldKeeper) AddHold(_ sdk.Context, addr sdk.AccAddress, funds sdk.Coins, reason string) error { + k.AddHoldCalls = append(k.AddHoldCalls, NewAddHoldArgs(addr, funds, reason)) + var err error + if len(k.AddHoldResultsQueue) > 0 { + if len(k.AddHoldResultsQueue[0]) > 0 { + err = errors.New(k.AddHoldResultsQueue[0]) + } + k.AddHoldResultsQueue = k.AddHoldResultsQueue[1:] + } + return err +} + +func (k *MockHoldKeeper) ReleaseHold(_ sdk.Context, addr sdk.AccAddress, funds sdk.Coins) error { + k.ReleaseHoldCalls = append(k.ReleaseHoldCalls, NewReleaseHoldArgs(addr, funds)) + var err error + if len(k.ReleaseHoldResultsQueue) > 0 { + if len(k.ReleaseHoldResultsQueue[0]) > 0 { + err = errors.New(k.ReleaseHoldResultsQueue[0]) + } + k.ReleaseHoldResultsQueue = k.ReleaseHoldResultsQueue[1:] + } + return err +} + +func (k *MockHoldKeeper) GetHoldCoin(_ sdk.Context, addr sdk.AccAddress, denom string) (sdk.Coin, error) { + k.GetHoldCoinCalls = append(k.GetHoldCoinCalls, NewGetHoldCoinArgs(addr, denom)) + if denomMap, aFound := k.GetHoldCoinResultsMap[string(addr)]; aFound { + if rv, dFound := denomMap[denom]; dFound { + return sdk.NewCoin(denom, rv.amount), rv.err + } + } + return sdk.NewInt64Coin(denom, 0), nil +} + +// assertEqualAddHoldCalls asserts that a mock keeper's AddHoldCalls match the provided expected calls. +func (s *TestSuite) assertEqualAddHoldCalls(mk *MockHoldKeeper, expected []*AddHoldArgs, msg string, args ...interface{}) bool { + return assertEqualSlice(s, expected, mk.AddHoldCalls, s.addHoldArgsString, + msg+" AddHoldCalls calls", args...) +} + +// assertEqualReleaseHoldCalls asserts that a mock keeper's ReleaseHoldCalls match the provided expected calls. +func (s *TestSuite) assertEqualReleaseHoldCalls(mk *MockHoldKeeper, expected []*ReleaseHoldArgs, msg string, args ...interface{}) bool { + return assertEqualSlice(s, expected, mk.ReleaseHoldCalls, s.releaseHoldArgsString, + msg+" ReleaseHoldCalls calls", args...) +} + +// assertEqualGetHoldCoinCalls asserts that a mock keeper's GetHoldCoinCalls match the provided expected calls. +func (s *TestSuite) assertEqualGetHoldCoinCalls(mk *MockHoldKeeper, expected []*GetHoldCoinArgs, msg string, args ...interface{}) bool { + return assertEqualSlice(s, expected, mk.GetHoldCoinCalls, s.getHoldCoinArgsString, + msg+" GetHoldCoinCalls calls", args...) +} + +// AddHoldArgs is a record of a call that is made to AddHold. +type AddHoldArgs struct { + addr sdk.AccAddress + funds sdk.Coins + reason string +} + +// NewAddHoldArgs creates a new record of args provided to a call to AddHold. +func NewAddHoldArgs(addr sdk.AccAddress, funds sdk.Coins, reason string) *AddHoldArgs { + return &AddHoldArgs{ + addr: addr, + funds: funds, + reason: reason, + } +} + +// addHoldArgsString creates a string of a AddHoldArgs substituting the address names as possible. +func (s *TestSuite) addHoldArgsString(a *AddHoldArgs) string { + return fmt.Sprintf("{addr:%s, funds:%s, reason:%q}", s.getAddrName(a.addr), a.funds, a.reason) +} + +// ReleaseHoldArgs is a record of a call that is made to ReleaseHold. +type ReleaseHoldArgs struct { + addr sdk.AccAddress + funds sdk.Coins +} + +// NewReleaseHoldArgs creates a new record of args provided to a call to ReleaseHold. +func NewReleaseHoldArgs(addr sdk.AccAddress, funds sdk.Coins) *ReleaseHoldArgs { + return &ReleaseHoldArgs{ + addr: addr, + funds: funds, + } +} + +// releaseHoldArgsString creates a string of a ReleaseHoldArgs substituting the address names as possible. +func (s *TestSuite) releaseHoldArgsString(a *ReleaseHoldArgs) string { + return fmt.Sprintf("{addr:%s, funds:%s}", s.getAddrName(a.addr), a.funds) +} + +// GetHoldCoinArgs is a record of a call that is made to GetHoldCoin. +type GetHoldCoinArgs struct { + addr sdk.AccAddress + denom string +} + +// NewGetHoldCoinArgs creates a new record of args provided to a call to GetHoldCoin. +func NewGetHoldCoinArgs(addr sdk.AccAddress, denom string) *GetHoldCoinArgs { + return &GetHoldCoinArgs{ + addr: addr, + denom: denom, + } +} + +// getHoldCoinArgsString creates a string of a GetHoldCoinArgs substituting the address names as possible. +func (s *TestSuite) getHoldCoinArgsString(a *GetHoldCoinArgs) string { + return fmt.Sprintf("{addr:%s, denom:%s}", s.getAddrName(a.addr), a.denom) +} + +// GetHoldCoinResults contains the result args to return for a GetHoldCoin call. +type GetHoldCoinResults struct { + amount sdkmath.Int + err error +} From 5c46ff735d9df51d16356d4c3bf2803542c89ac0 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 9 Oct 2023 14:06:26 -0600 Subject: [PATCH 268/309] [1658]: Change DoTransfer to return an error instead of panic when an address is bad. Tweak CalculateExchangeSplit to use QuoRemInt. Tweak variable names and error messages in CollectFee and CollectFees. --- x/exchange/keeper/keeper.go | 51 ++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/x/exchange/keeper/keeper.go b/x/exchange/keeper/keeper.go index df424e1495..5ba840752b 100644 --- a/x/exchange/keeper/keeper.go +++ b/x/exchange/keeper/keeper.go @@ -20,6 +20,13 @@ import ( "github.com/provenance-io/provenance/x/exchange" ) +var ( + // OneInt is an sdkmath.Int of 1. + OneInt = sdkmath.NewInt(1) + // TenKInt is an sdkmath.Int of 10,000. + TenKInt = sdkmath.NewInt(10_000) +) + // Keeper provides the exchange module's state store interactions. type Keeper struct { cdc codec.BinaryCodec @@ -159,8 +166,14 @@ func (k Keeper) DoTransfer(ctxIn sdk.Context, inputs []banktypes.Input, outputs return fmt.Errorf("input coins %q does not equal output coins %q", inputs[0].Coins, outputs[0].Coins) } - fromAddr := sdk.MustAccAddressFromBech32(inputs[0].Address) - toAddr := sdk.MustAccAddressFromBech32(outputs[0].Address) + fromAddr, err := sdk.AccAddressFromBech32(inputs[0].Address) + if err != nil { + return fmt.Errorf("invalid inputs[0] address %q: %w", inputs[0].Address, err) + } + toAddr, err := sdk.AccAddressFromBech32(outputs[0].Address) + if err != nil { + return fmt.Errorf("invalid outputs[0] address %q: %w", outputs[0].Address, err) + } return k.bankKeeper.SendCoins(ctx, fromAddr, toAddr, inputs[0].Coins) } return k.bankKeeper.InputOutputCoins(ctx, inputs, outputs) @@ -168,6 +181,9 @@ func (k Keeper) DoTransfer(ctxIn sdk.Context, inputs []banktypes.Input, outputs // CalculateExchangeSplit calculates the amount that the exchange will keep of the provided fee. func (k Keeper) CalculateExchangeSplit(ctx sdk.Context, feeAmt sdk.Coins) sdk.Coins { + if feeAmt.IsZero() { + return nil + } exchangeAmt := make(sdk.Coins, 0, len(feeAmt)) for _, coin := range feeAmt { if coin.Amount.IsZero() { @@ -179,13 +195,14 @@ func (k Keeper) CalculateExchangeSplit(ctx sdk.Context, feeAmt sdk.Coins) sdk.Co continue } - splitAmt := coin.Amount.Mul(sdkmath.NewInt(split)) - roundUp := !splitAmt.ModRaw(10_000).IsZero() - splitAmt = splitAmt.QuoRaw(10_000) - if roundUp { - splitAmt = splitAmt.Add(sdkmath.OneInt()) + splitAmt, splitRem := exchange.QuoRemInt(coin.Amount.Mul(sdkmath.NewInt(split)), TenKInt) + if !splitRem.IsZero() { + splitAmt = splitAmt.Add(OneInt) } - exchangeAmt = append(exchangeAmt, sdk.Coin{Denom: coin.Denom, Amount: splitAmt}) + exchangeAmt = append(exchangeAmt, sdk.NewCoin(coin.Denom, splitAmt)) + } + if exchangeAmt.IsZero() { + return nil } return exchangeAmt } @@ -193,19 +210,19 @@ func (k Keeper) CalculateExchangeSplit(ctx sdk.Context, feeAmt sdk.Coins) sdk.Co // CollectFee will transfer the fee amount to the market account, // then the exchange's cut from the market to the fee collector. // If you have fees to collect from multiple payers, consider using CollectFees. -func (k Keeper) CollectFee(ctx sdk.Context, marketID uint32, payer sdk.AccAddress, feeAmt sdk.Coins) error { - if feeAmt.IsZero() { +func (k Keeper) CollectFee(ctx sdk.Context, marketID uint32, payer sdk.AccAddress, fee sdk.Coins) error { + if fee.IsZero() { return nil } - exchangeAmt := k.CalculateExchangeSplit(ctx, feeAmt) + exchangeSplit := k.CalculateExchangeSplit(ctx, fee) marketAddr := exchange.GetMarketAddress(marketID) - if err := k.bankKeeper.SendCoins(ctx, payer, marketAddr, feeAmt); err != nil { - return fmt.Errorf("error transferring %s from %s to market %d: %w", feeAmt, payer, marketID, err) + if err := k.bankKeeper.SendCoins(ctx, payer, marketAddr, fee); err != nil { + return fmt.Errorf("error transferring %s from %s to market %d: %w", fee, payer, marketID, err) } - if !exchangeAmt.IsZero() { - if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, marketAddr, k.feeCollectorName, exchangeAmt); err != nil { - return fmt.Errorf("error collecting exchange fee %s (based off %s) from market %d: %w", exchangeAmt, feeAmt, marketID, err) + if !exchangeSplit.IsZero() { + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, marketAddr, k.feeCollectorName, exchangeSplit); err != nil { + return fmt.Errorf("error collecting exchange fee %s (based off %s) from market %d: %w", exchangeSplit, fee, marketID, err) } } @@ -223,7 +240,7 @@ func (k Keeper) CollectFees(ctx sdk.Context, marketID uint32, inputs []banktypes // If there's only one input, just use CollectFee for the nicer events. payer, err := sdk.AccAddressFromBech32(inputs[0].Address) if err != nil { - return fmt.Errorf("invalid payer address %q: %w", inputs[0].Address, err) + return fmt.Errorf("invalid inputs[0] address address %q: %w", inputs[0].Address, err) } return k.CollectFee(ctx, marketID, payer, inputs[0].Coins) } From 4180e8ea8113f6d98eb296c56688d3af5aa52c5f Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 9 Oct 2023 14:07:35 -0600 Subject: [PATCH 269/309] [1658]: Unit tests on DoTransfer, CalculateExchangeSplit, CollectFee, and CollectFees. --- x/exchange/keeper/keeper_test.go | 573 ++++++++++++++++++++++++++++++- x/exchange/keeper/mocks_test.go | 310 +++++++++++------ 2 files changed, 761 insertions(+), 122 deletions(-) diff --git a/x/exchange/keeper/keeper_test.go b/x/exchange/keeper/keeper_test.go index 58da8c6a5e..17c27142ff 100644 --- a/x/exchange/keeper/keeper_test.go +++ b/x/exchange/keeper/keeper_test.go @@ -12,6 +12,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/bank/testutil" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/provenance-io/provenance/app" "github.com/provenance-io/provenance/testutil/assertions" @@ -24,7 +25,7 @@ type TestSuite struct { suite.Suite app *app.App - sdkCtx sdk.Context + ctx sdk.Context stdlibCtx context.Context k keeper.Keeper @@ -42,28 +43,40 @@ type TestSuite struct { addr3 sdk.AccAddress addr4 sdk.AccAddress addr5 sdk.AccAddress + + marketAddr1 sdk.AccAddress + marketAddr2 sdk.AccAddress + marketAddr3 sdk.AccAddress + + feeCollector string } func (s *TestSuite) SetupTest() { s.app = app.Setup(s.T()) - s.sdkCtx = s.app.BaseApp.NewContext(false, tmproto.Header{}) - s.stdlibCtx = sdk.WrapSDKContext(s.sdkCtx) + s.ctx = s.app.BaseApp.NewContext(false, tmproto.Header{}) + s.stdlibCtx = sdk.WrapSDKContext(s.ctx) s.k = s.app.ExchangeKeeper s.acctKeeper = s.app.AccountKeeper s.attrKeeper = s.app.AttributeKeeper s.bankKeeper = s.app.BankKeeper s.holdKeeper = s.app.HoldKeeper - s.bondDenom = s.app.StakingKeeper.BondDenom(s.sdkCtx) + s.bondDenom = s.app.StakingKeeper.BondDenom(s.ctx) s.initAmount = 1_000_000_000 s.initBal = sdk.NewCoins(sdk.NewCoin(s.bondDenom, sdk.NewInt(s.initAmount))) - addrs := app.AddTestAddrsIncremental(s.app, s.sdkCtx, 5, sdk.NewInt(s.initAmount)) + addrs := app.AddTestAddrsIncremental(s.app, s.ctx, 5, sdk.NewInt(s.initAmount)) s.addr1 = addrs[0] s.addr2 = addrs[1] s.addr3 = addrs[2] s.addr4 = addrs[3] s.addr5 = addrs[4] + + s.marketAddr1 = exchange.GetMarketAddress(1) + s.marketAddr2 = exchange.GetMarketAddress(2) + s.marketAddr3 = exchange.GetMarketAddress(3) + + s.feeCollector = s.k.GetFeeCollectorName() } func TestKeeperTestSuite(t *testing.T) { @@ -112,6 +125,12 @@ func (s *TestSuite) getAddrName(addr sdk.AccAddress) string { return "addr4" case string(s.addr5): return "addr5" + case string(s.marketAddr1): + return "marketAddr1" + case string(s.marketAddr2): + return "marketAddr2" + case string(s.marketAddr3): + return "marketAddr3" default: return addr.String() } @@ -128,7 +147,7 @@ func (s *TestSuite) getAddrStrName(addrStr string) string { // getStore gets the exchange store. func (s *TestSuite) getStore() sdk.KVStore { - return s.k.GetStore(s.sdkCtx) + return s.k.GetStore(s.ctx) } // clearExchangeState deletes everything from the exchange state store. @@ -155,15 +174,20 @@ func (s *TestSuite) dumpHoldState() []string { // requireFundAccount calls testutil.FundAccount, making sure it doesn't panic or return an error. func (s *TestSuite) requireFundAccount(addr sdk.AccAddress, coins string) { assertions.RequireNotPanicsNoErrorf(s.T(), func() error { - return testutil.FundAccount(s.app.BankKeeper, s.sdkCtx, addr, s.coins(coins)) + return testutil.FundAccount(s.app.BankKeeper, s.ctx, addr, s.coins(coins)) }, "FundAccount(%s, %q)", s.getAddrName(addr), coins) } -// assertErrorValue is a wrapper for assertions.AssertErrorValue. +// assertErrorValue is a wrapper for assertions.AssertErrorValue for this TestSuite. func (s *TestSuite) assertErrorValue(theError error, expected string, msgAndArgs ...interface{}) bool { return assertions.AssertErrorValue(s.T(), theError, expected, msgAndArgs...) } +// assertErrorContents is a wrapper for assertions.AssertErrorContents for this TestSuite. +func (s *TestSuite) assertErrorContents(theError error, contains []string, msgAndArgs ...interface{}) bool { + return assertions.AssertErrorContents(s.T(), theError, contains, msgAndArgs...) +} + func (s *TestSuite) TestKeeper_GetAuthority() { expected := authtypes.NewModuleAddress(govtypes.ModuleName).String() var actual string @@ -242,10 +266,535 @@ func (s *TestSuite) TestKeeper_GetFeeCollectorName() { s.Assert().Equal(expected, actual, "GetFeeCollectorName() result") } -// TODO[1658]: func (s *TestSuite) TestKeeper_DoTransfer() +func (s *TestSuite) TestKeeper_DoTransfer() { + tests := []struct { + name string + bk *MockBankKeeper + inputs []banktypes.Input + outputs []banktypes.Output + expErr string + expSends []*SendCoinsArgs + expIO bool + }{ + { + name: "1 in, 1 out: different denoms", + inputs: []banktypes.Input{{Address: s.addr1.String(), Coins: s.coins("10apple")}}, + outputs: []banktypes.Output{{Address: s.addr2.String(), Coins: s.coins("10banana")}}, + expErr: "input coins \"10apple\" does not equal output coins \"10banana\"", + }, + { + name: "1 in, 1 out: different amounts", + inputs: []banktypes.Input{{Address: s.addr1.String(), Coins: s.coins("10apple")}}, + outputs: []banktypes.Output{{Address: s.addr2.String(), Coins: s.coins("11apple")}}, + expErr: "input coins \"10apple\" does not equal output coins \"11apple\"", + }, + { + name: "1 in, 1 out: bad in addr", + inputs: []banktypes.Input{{Address: "badInAddr", Coins: s.coins("10apple")}}, + outputs: []banktypes.Output{{Address: s.addr2.String(), Coins: s.coins("10apple")}}, + expErr: "invalid inputs[0] address \"badInAddr\": decoding bech32 failed: string not all lowercase or all uppercase", + }, + { + name: "1 in, 1 out: bad out addr", + inputs: []banktypes.Input{{Address: s.addr1.String(), Coins: s.coins("10apple")}}, + outputs: []banktypes.Output{{Address: "badOutAddr", Coins: s.coins("10apple")}}, + expErr: "invalid outputs[0] address \"badOutAddr\": decoding bech32 failed: string not all lowercase or all uppercase", + }, + { + name: "1 in, 1 out: err from SendCoins", + bk: NewMockBankKeeper().WithSendCoinsResults("test error X from SendCoins"), + inputs: []banktypes.Input{{Address: s.addr1.String(), Coins: s.coins("10apple")}}, + outputs: []banktypes.Output{{Address: s.addr2.String(), Coins: s.coins("10apple")}}, + expErr: "test error X from SendCoins", + expSends: []*SendCoinsArgs{ + {ctxHasQuarantineBypass: true, fromAddr: s.addr1, toAddr: s.addr2, amt: s.coins("10apple")}, + }, + }, + { + name: "1 in, 1 out: okay", + inputs: []banktypes.Input{{Address: s.addr2.String(), Coins: s.coins("15banana")}}, + outputs: []banktypes.Output{{Address: s.addr3.String(), Coins: s.coins("15banana")}}, + expSends: []*SendCoinsArgs{ + {ctxHasQuarantineBypass: true, fromAddr: s.addr2, toAddr: s.addr3, amt: s.coins("15banana")}, + }, + }, + { + name: "1 in, 3 out: err from InputOutputCoins", + bk: NewMockBankKeeper().WithInputOutputCoinsResults("test error V from InputOutputCoins"), + inputs: []banktypes.Input{{Address: s.addr5.String(), Coins: s.coins("60cactus")}}, + outputs: []banktypes.Output{ + {Address: s.addr4.String(), Coins: s.coins("18cactus")}, + {Address: s.addr3.String(), Coins: s.coins("5cactus")}, + {Address: s.addr2.String(), Coins: s.coins("37cactus")}, + }, + expErr: "test error V from InputOutputCoins", + expIO: true, + }, + { + name: "1 in, 3 out: okay", + inputs: []banktypes.Input{{Address: s.addr5.String(), Coins: s.coins("60cactus")}}, + outputs: []banktypes.Output{ + {Address: s.addr4.String(), Coins: s.coins("18cactus")}, + {Address: s.addr3.String(), Coins: s.coins("5cactus")}, + {Address: s.addr2.String(), Coins: s.coins("37cactus")}, + }, + expIO: true, + }, + { + name: "3 in, 1 out: err from InputOutputCoins", + bk: NewMockBankKeeper().WithInputOutputCoinsResults("test error P from InputOutputCoins"), + inputs: []banktypes.Input{ + {Address: s.addr1.String(), Coins: s.coins("51date")}, + {Address: s.addr2.String(), Coins: s.coins("3date")}, + {Address: s.addr3.String(), Coins: s.coins("16date")}, + }, + outputs: []banktypes.Output{ + {Address: s.addr4.String(), Coins: s.coins("70apple")}, + }, + expErr: "test error P from InputOutputCoins", + expIO: true, + }, + { + name: "3 in, 1 out: okay", + inputs: []banktypes.Input{ + {Address: s.addr1.String(), Coins: s.coins("51date")}, + {Address: s.addr2.String(), Coins: s.coins("3date")}, + {Address: s.addr3.String(), Coins: s.coins("16date")}, + }, + outputs: []banktypes.Output{{Address: s.addr4.String(), Coins: s.coins("70apple")}}, + expIO: true, + }, + } -// TODO[1658]: func (s *TestSuite) TestKeeper_CalculateExchangeSplit() + for _, tc := range tests { + s.Run(tc.name, func() { + if tc.bk == nil { + tc.bk = NewMockBankKeeper() + } + expCalls := BankCalls{ + SendCoinsCalls: tc.expSends, + SendCoinsFromAccountToModuleCalls: nil, + InputOutputCoinsCalls: nil, + } + if tc.expIO { + expCalls.InputOutputCoinsCalls = append(expCalls.InputOutputCoinsCalls, &InputOutputCoinsArgs{ + ctxHasQuarantineBypass: true, + inputs: tc.inputs, + outputs: tc.outputs, + }) + } -// TODO[1658]: func (s *TestSuite) TestKeeper_CollectFee() + kpr := s.k.WithBankKeeper(tc.bk) + var err error + testFunc := func() { + err = kpr.DoTransfer(s.ctx, tc.inputs, tc.outputs) + } + s.Require().NotPanics(testFunc, "DoTransfer") + s.assertErrorValue(err, tc.expErr, "DoTransfer error") + s.assertBankKeeperCalls(tc.bk, expCalls, "DoTransfer") + }) + } +} -// TODO[1658]: func (s *TestSuite) TestKeeper_CollectFees() +func (s *TestSuite) TestKeeper_CalculateExchangeSplit() { + tests := []struct { + name string + params *exchange.Params + feeAmt sdk.Coins + expAmt sdk.Coins + }{ + { + name: "no params in state", + params: nil, + feeAmt: s.coins("100apple,20banana"), + expAmt: s.coins("5apple,1banana"), + }, + { + name: "default params in state", + params: exchange.DefaultParams(), + feeAmt: s.coins("100apple,20banana"), + expAmt: s.coins("5apple,1banana"), + }, + { + name: "denom with a specific split: evenly divisible", + params: &exchange.Params{ + DefaultSplit: 500, + DenomSplits: []exchange.DenomSplit{{Denom: "apple", Split: 100}}, + }, + feeAmt: s.coins("500apple"), + expAmt: s.coins("5apple"), + }, + { + name: "denom with a specific split: not evenly divisible", + params: &exchange.Params{ + DefaultSplit: 500, + DenomSplits: []exchange.DenomSplit{{Denom: "apple", Split: 100}}, + }, + feeAmt: s.coins("501apple"), + expAmt: s.coins("6apple"), + }, + { + name: "denom without a specific split: evenly divisible", + params: &exchange.Params{ + DefaultSplit: 1000, + DenomSplits: []exchange.DenomSplit{{Denom: "apple", Split: 100}}, + }, + feeAmt: s.coins("30banana"), + expAmt: s.coins("3banana"), + }, + { + name: "denom without a specific split: not evenly divisible", + params: &exchange.Params{ + DefaultSplit: 1000, + DenomSplits: []exchange.DenomSplit{{Denom: "apple", Split: 100}}, + }, + feeAmt: s.coins("39banana"), + expAmt: s.coins("4banana"), + }, + { + name: "denom with a zero split", + params: &exchange.Params{ + DefaultSplit: 750, + DenomSplits: []exchange.DenomSplit{{Denom: "apple", Split: 0}}, + }, + feeAmt: s.coins("500000apple"), + expAmt: nil, + }, + { + name: "four denoms: two specific, one undefined, one zero", + params: &exchange.Params{ + DefaultSplit: 650, + DenomSplits: []exchange.DenomSplit{ + {Denom: "apple", Split: 123}, + {Denom: "banana", Split: 0}, + {Denom: "peach", Split: 55}, + }, + }, + feeAmt: s.coins("123456apple,5000000000banana,400fig,160070peach"), + // 123456 * 1.23% = 1518.5088, 400 * 6.5% = 26.0, 160070 * 0.55% = 880.385, + expAmt: s.coins("1519apple,26fig,881peach"), + }, + { + name: "zero fee", + params: &exchange.Params{ + DefaultSplit: 300, + DenomSplits: []exchange.DenomSplit{{Denom: "apple", Split: 600}}, + }, + feeAmt: sdk.Coins{sdk.NewInt64Coin("apple", 0), sdk.NewInt64Coin("banana", 0)}, + expAmt: nil, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + s.k.SetParams(s.ctx, tc.params) + + var actAmt sdk.Coins + testFunc := func() { + actAmt = s.k.CalculateExchangeSplit(s.ctx, tc.feeAmt) + } + s.Require().NotPanics(testFunc, "CalculateExchangeSplit(%q)", tc.feeAmt) + s.Assert().Equal(tc.expAmt.String(), actAmt.String(), "CalculateExchangeSplit(%q) result", tc.feeAmt) + }) + } +} + +func (s *TestSuite) TestKeeper_CollectFee() { + // define our own default params for these tests. + defaultParams := &exchange.Params{ + DefaultSplit: 250, + DenomSplits: []exchange.DenomSplit{ + {Denom: "fig", Split: 1000}, + {Denom: "zucchini", Split: 0}, + }, + } + + tests := []struct { + name string + params *exchange.Params + bk *MockBankKeeper + marketID uint32 + payer sdk.AccAddress + feeAmt sdk.Coins + expErr string + expCalls BankCalls + }{ + { + name: "zero fee", + marketID: 1, + payer: s.addr1, + feeAmt: sdk.Coins{sdk.NewInt64Coin("apple", 0)}, + expErr: "", + expCalls: BankCalls{}, + }, + { + name: "err collecting fee", + bk: NewMockBankKeeper().WithSendCoinsResults("test error F from SendCoins"), + marketID: 1, + payer: s.addr1, + feeAmt: s.coins("750apple"), + expErr: "error transferring 750apple from " + s.addr1.String() + " to market 1: test error F from SendCoins", + expCalls: BankCalls{ + SendCoinsCalls: []*SendCoinsArgs{ + {ctxHasQuarantineBypass: false, fromAddr: s.addr1, toAddr: s.marketAddr1, amt: s.coins("750apple")}, + }, + }, + }, + { + name: "err collecting exchange split", + bk: NewMockBankKeeper().WithSendCoinsFromAccountToModuleResults("test error U from SendCoinsFromAccountToModule"), + marketID: 2, + payer: s.addr4, + feeAmt: s.coins("750apple"), + expErr: "error collecting exchange fee 19apple (based off 750apple) from market 2: test error U from SendCoinsFromAccountToModule", + expCalls: BankCalls{ + SendCoinsCalls: []*SendCoinsArgs{ + {fromAddr: s.addr4, toAddr: s.marketAddr2, amt: s.coins("750apple")}, + }, + SendCoinsFromAccountToModuleCalls: []*SendCoinsFromAccountToModuleArgs{ + {senderAddr: s.marketAddr2, recipientModule: s.feeCollector, amt: s.coins("19apple")}, + }, + }, + }, + { + name: "no exchange split", + params: &exchange.Params{DefaultSplit: 0}, + marketID: 3, + payer: s.addr2, + feeAmt: s.coins("1000000apple,5000000fig"), + expErr: "", + expCalls: BankCalls{ + SendCoinsCalls: []*SendCoinsArgs{ + {fromAddr: s.addr2, toAddr: s.marketAddr3, amt: s.coins("1000000apple,5000000fig")}, + }, + }, + }, + { + name: "with exchange split", + marketID: 1, + payer: s.addr3, + feeAmt: s.coins("1005apple,5000fig,999999zucchini"), + expCalls: BankCalls{ + SendCoinsCalls: []*SendCoinsArgs{ + {fromAddr: s.addr3, toAddr: s.marketAddr1, amt: s.coins("1005apple,5000fig,999999zucchini")}, + }, + SendCoinsFromAccountToModuleCalls: []*SendCoinsFromAccountToModuleArgs{ + {senderAddr: s.marketAddr1, recipientModule: s.feeCollector, amt: s.coins("26apple,500fig")}, + }, + }, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + if tc.params == nil { + tc.params = defaultParams + } + s.k.SetParams(s.ctx, tc.params) + if tc.bk == nil { + tc.bk = NewMockBankKeeper() + } + + kpr := s.k.WithBankKeeper(tc.bk) + var err error + testFunc := func() { + err = kpr.CollectFee(s.ctx, tc.marketID, tc.payer, tc.feeAmt) + } + s.Require().NotPanics(testFunc, "CollectFee(%d, %s, %q)", tc.marketID, s.getAddrName(tc.payer), tc.feeAmt) + s.assertErrorValue(err, tc.expErr, "CollectFee(%d, %s, %q)", tc.marketID, s.getAddrName(tc.payer), tc.feeAmt) + s.assertBankKeeperCalls(tc.bk, tc.expCalls, "CollectFee(%d, %s, %q)", tc.marketID, s.getAddrName(tc.payer), tc.feeAmt) + }) + } +} + +func (s *TestSuite) TestKeeper_CollectFees() { + // define our own default params for these tests. + defaultParams := &exchange.Params{ + DefaultSplit: 250, + DenomSplits: []exchange.DenomSplit{ + {Denom: "fig", Split: 1000}, + {Denom: "zucchini", Split: 0}, + }, + } + + tests := []struct { + name string + params *exchange.Params + bk *MockBankKeeper + marketID uint32 + inputs []banktypes.Input + expErr string + expCalls BankCalls + }{ + { + name: "nil inputs", + marketID: 1, + inputs: nil, + expErr: "", + expCalls: BankCalls{}, + }, + { + name: "nil inputs", + marketID: 1, + inputs: []banktypes.Input{}, + expErr: "", + expCalls: BankCalls{}, + }, + { + name: "one input: bad address", + marketID: 2, + inputs: []banktypes.Input{{Address: "badAddr", Coins: s.coins("1000apple")}}, + expErr: "invalid inputs[0] address address \"badAddr\": decoding bech32 failed: invalid bech32 string length 7", + expCalls: BankCalls{}, + }, + { + name: "one input", + marketID: 2, + inputs: []banktypes.Input{{Address: s.addr1.String(), Coins: s.coins("1000apple")}}, + expErr: "", + expCalls: BankCalls{ + SendCoinsCalls: []*SendCoinsArgs{ + {fromAddr: s.addr1, toAddr: s.marketAddr2, amt: s.coins("1000apple")}, + }, + SendCoinsFromAccountToModuleCalls: []*SendCoinsFromAccountToModuleArgs{ + {senderAddr: s.marketAddr2, recipientModule: s.feeCollector, amt: s.coins("25apple")}, + }, + }, + }, + { + name: "three inputs: zero coins", + marketID: 3, + inputs: []banktypes.Input{ + {Address: s.addr3.String(), Coins: sdk.Coins{sdk.NewInt64Coin("apple", 0)}}, + {Address: s.addr4.String(), Coins: sdk.Coins{sdk.NewInt64Coin("fig", 0)}}, + {Address: s.addr5.String(), Coins: sdk.Coins{sdk.NewInt64Coin("zucchini", 0)}}, + }, + expErr: "", + expCalls: BankCalls{}, + }, + { + name: "three inputs: error from InputOutputCoins", + bk: NewMockBankKeeper().WithInputOutputCoinsResults("test error Z from InputOutputCoins"), + marketID: 1, + inputs: []banktypes.Input{ + {Address: s.addr1.String(), Coins: s.coins("10apple,1fig,1zucchini")}, + {Address: s.addr3.String(), Coins: s.coins("30fig")}, + {Address: s.addr5.String(), Coins: s.coins("50zucchini")}, + }, + expErr: "error collecting fees for market 1: test error Z from InputOutputCoins", + expCalls: BankCalls{ + InputOutputCoinsCalls: []*InputOutputCoinsArgs{ + { + inputs: []banktypes.Input{ + {Address: s.addr1.String(), Coins: s.coins("10apple,1fig,1zucchini")}, + {Address: s.addr3.String(), Coins: s.coins("30fig")}, + {Address: s.addr5.String(), Coins: s.coins("50zucchini")}, + }, + outputs: []banktypes.Output{ + {Address: s.marketAddr1.String(), Coins: s.coins("10apple,31fig,51zucchini")}, + }, + }, + }, + }, + }, + { + name: "three inputs: error from SendCoinsFromAccountToModule", + bk: NewMockBankKeeper().WithSendCoinsFromAccountToModuleResults("test error L from SendCoinsFromAccountToModule"), + marketID: 1, + inputs: []banktypes.Input{ + {Address: s.addr1.String(), Coins: s.coins("1000apple,1fig,10zucchini")}, + {Address: s.addr3.String(), Coins: s.coins("3000fig")}, + {Address: s.addr5.String(), Coins: s.coins("5000zucchini")}, + }, + expErr: "error collecting exchange fee 25apple,301fig (based off 1000apple,3001fig,5010zucchini) from market 1: test error L from SendCoinsFromAccountToModule", + expCalls: BankCalls{ + InputOutputCoinsCalls: []*InputOutputCoinsArgs{ + { + inputs: []banktypes.Input{ + {Address: s.addr1.String(), Coins: s.coins("1000apple,1fig,10zucchini")}, + {Address: s.addr3.String(), Coins: s.coins("3000fig")}, + {Address: s.addr5.String(), Coins: s.coins("5000zucchini")}, + }, + outputs: []banktypes.Output{ + {Address: s.marketAddr1.String(), Coins: s.coins("1000apple,3001fig,5010zucchini")}, + }, + }, + }, + SendCoinsFromAccountToModuleCalls: []*SendCoinsFromAccountToModuleArgs{ + {senderAddr: s.marketAddr1, recipientModule: s.feeCollector, amt: s.coins("25apple,301fig")}, + }, + }, + }, + { + name: "three inputs: zero split", + params: &exchange.Params{DefaultSplit: 0}, + marketID: 2, + inputs: []banktypes.Input{ + {Address: s.addr1.String(), Coins: s.coins("1000apple,1fig,10zucchini")}, + {Address: s.addr3.String(), Coins: s.coins("3000fig")}, + {Address: s.addr5.String(), Coins: s.coins("5000zucchini")}, + }, + expCalls: BankCalls{ + InputOutputCoinsCalls: []*InputOutputCoinsArgs{ + { + inputs: []banktypes.Input{ + {Address: s.addr1.String(), Coins: s.coins("1000apple,1fig,10zucchini")}, + {Address: s.addr3.String(), Coins: s.coins("3000fig")}, + {Address: s.addr5.String(), Coins: s.coins("5000zucchini")}, + }, + outputs: []banktypes.Output{ + {Address: s.marketAddr2.String(), Coins: s.coins("1000apple,3001fig,5010zucchini")}, + }, + }, + }, + }, + }, + { + name: "three inputs: with split", + marketID: 3, + inputs: []banktypes.Input{ + {Address: s.addr1.String(), Coins: s.coins("1000apple,1fig,10zucchini")}, + {Address: s.addr3.String(), Coins: s.coins("3000fig")}, + {Address: s.addr5.String(), Coins: s.coins("5000zucchini")}, + }, + expCalls: BankCalls{ + InputOutputCoinsCalls: []*InputOutputCoinsArgs{ + { + inputs: []banktypes.Input{ + {Address: s.addr1.String(), Coins: s.coins("1000apple,1fig,10zucchini")}, + {Address: s.addr3.String(), Coins: s.coins("3000fig")}, + {Address: s.addr5.String(), Coins: s.coins("5000zucchini")}, + }, + outputs: []banktypes.Output{ + {Address: s.marketAddr3.String(), Coins: s.coins("1000apple,3001fig,5010zucchini")}, + }, + }, + }, + SendCoinsFromAccountToModuleCalls: []*SendCoinsFromAccountToModuleArgs{ + {senderAddr: s.marketAddr3, recipientModule: s.feeCollector, amt: s.coins("25apple,301fig")}, + }, + }, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + if tc.params == nil { + tc.params = defaultParams + } + s.k.SetParams(s.ctx, tc.params) + if tc.bk == nil { + tc.bk = NewMockBankKeeper() + } + + kpr := s.k.WithBankKeeper(tc.bk) + var err error + testFunc := func() { + err = kpr.CollectFees(s.ctx, tc.marketID, tc.inputs) + } + s.Require().NotPanics(testFunc, "CollectFees(%d, ...)", tc.marketID) + s.assertErrorValue(err, tc.expErr, "CollectFees(%d, ...)", tc.marketID) + s.assertBankKeeperCalls(tc.bk, tc.expCalls, "CollectFees(%d, ...)", tc.marketID) + }) + } +} diff --git a/x/exchange/keeper/mocks_test.go b/x/exchange/keeper/mocks_test.go index 67abd5852a..2019b08ac6 100644 --- a/x/exchange/keeper/mocks_test.go +++ b/x/exchange/keeper/mocks_test.go @@ -30,6 +30,7 @@ func toStrings[T any](vals []T, stringer func(T) string) []string { // If not, returns false and the stringer is applied to each entry and the comparison // is redone on the strings in the hopes that it helps identify the problem. func assertEqualSlice[T any](s *TestSuite, expected, actual []T, stringer func(T) string, msg string, args ...interface{}) bool { + s.T().Helper() if s.Assert().Equalf(expected, actual, msg, args...) { return true } @@ -40,18 +41,29 @@ func assertEqualSlice[T any](s *TestSuite, expected, actual []T, stringer func(T return false } +// ############################################################################# +// ############################# ############################# +// ########################### MockAccountKeeper ########################### +// ############################# ############################# +// ############################################################################# + +var _ exchange.AccountKeeper = (*MockAccountKeeper)(nil) + // MockAccountKeeper satisfies the exchange.AccountKeeper interface but just records the calls and allows dictation of results. type MockAccountKeeper struct { - GetAccountCalls []sdk.AccAddress - SetAccountCalls []authtypes.AccountI - HasAccountCalls []sdk.AccAddress - NewAccountCalls []authtypes.AccountI + Calls AccountCalls GetAccountResultsMap map[string]authtypes.AccountI HasAccountResultsMap map[string]bool NewAccountResultsMap map[string]authtypes.AccountI } -var _ exchange.AccountKeeper = (*MockAccountKeeper)(nil) +// AccountCalls contains all the calls that the mock account keeper makes. +type AccountCalls struct { + GetAccountCalls []sdk.AccAddress + SetAccountCalls []authtypes.AccountI + HasAccountCalls []sdk.AccAddress + NewAccountCalls []authtypes.AccountI +} // NewMockAccountKeeper creates a new empty MockAccountKeeper. // Follow it up with WithGetAccountResult, WithHasAccountResult, @@ -90,7 +102,7 @@ func (k *MockAccountKeeper) WithNewAccountResult(result authtypes.AccountI) *Moc } func (k *MockAccountKeeper) GetAccount(_ sdk.Context, addr sdk.AccAddress) authtypes.AccountI { - k.GetAccountCalls = append(k.GetAccountCalls, addr) + k.Calls.GetAccountCalls = append(k.Calls.GetAccountCalls, addr) if rv, found := k.GetAccountResultsMap[string(addr)]; found { return rv } @@ -98,11 +110,11 @@ func (k *MockAccountKeeper) GetAccount(_ sdk.Context, addr sdk.AccAddress) autht } func (k *MockAccountKeeper) SetAccount(_ sdk.Context, acc authtypes.AccountI) { - k.SetAccountCalls = append(k.SetAccountCalls, acc) + k.Calls.SetAccountCalls = append(k.Calls.SetAccountCalls, acc) } func (k *MockAccountKeeper) HasAccount(_ sdk.Context, addr sdk.AccAddress) bool { - k.HasAccountCalls = append(k.HasAccountCalls, addr) + k.Calls.HasAccountCalls = append(k.Calls.HasAccountCalls, addr) if rv, found := k.HasAccountResultsMap[string(addr)]; found { return rv } @@ -110,44 +122,77 @@ func (k *MockAccountKeeper) HasAccount(_ sdk.Context, addr sdk.AccAddress) bool } func (k *MockAccountKeeper) NewAccount(_ sdk.Context, acc authtypes.AccountI) authtypes.AccountI { - k.NewAccountCalls = append(k.NewAccountCalls, acc) + k.Calls.NewAccountCalls = append(k.Calls.NewAccountCalls, acc) if rv, found := k.NewAccountResultsMap[string(acc.GetAddress())]; found { return rv } return acc } -// assertEqualGetAccountCalls asserts that a mock keeper's GetAccountCalls match the provided expected calls. -func (s *TestSuite) assertEqualGetAccountCalls(mk *MockAccountKeeper, expected []sdk.AccAddress, msg string, args ...interface{}) bool { - return assertEqualSlice(s, expected, mk.GetAccountCalls, s.getAddrName, +// assertGetAccountCalls asserts that a mock keeper's GetAccountCalls match the provided expected calls. +func (s *TestSuite) assertGetAccountCalls(mk *MockAccountKeeper, expected []sdk.AccAddress, msg string, args ...interface{}) bool { + s.T().Helper() + return assertEqualSlice(s, expected, mk.Calls.GetAccountCalls, s.getAddrName, msg+" GetAccount calls", args...) } -// assertEqualSetAccountCalls asserts that a mock keeper's SetAccountCalls match the provided expected calls. -func (s *TestSuite) assertEqualSetAccountCalls(mk *MockAccountKeeper, expected []authtypes.AccountI, msg string, args ...interface{}) bool { - return assertEqualSlice(s, expected, mk.SetAccountCalls, authtypes.AccountI.String, +// assertSetAccountCalls asserts that a mock keeper's SetAccountCalls match the provided expected calls. +func (s *TestSuite) assertSetAccountCalls(mk *MockAccountKeeper, expected []authtypes.AccountI, msg string, args ...interface{}) bool { + s.T().Helper() + return assertEqualSlice(s, expected, mk.Calls.SetAccountCalls, authtypes.AccountI.String, msg+" SetAccount calls", args...) } -// assertEqualHasAccountCalls asserts that a mock keeper's HasAccountCalls match the provided expected calls. -func (s *TestSuite) assertEqualHasAccountCalls(mk *MockAccountKeeper, expected []sdk.AccAddress, msg string, args ...interface{}) bool { - return assertEqualSlice(s, expected, mk.HasAccountCalls, s.getAddrName, +// assertHasAccountCalls asserts that a mock keeper's HasAccountCalls match the provided expected calls. +func (s *TestSuite) assertHasAccountCalls(mk *MockAccountKeeper, expected []sdk.AccAddress, msg string, args ...interface{}) bool { + s.T().Helper() + return assertEqualSlice(s, expected, mk.Calls.HasAccountCalls, s.getAddrName, msg+" HasAccount calls", args...) } -// assertEqualNewAccountCalls asserts that a mock keeper's NewAccountCalls match the provided expected calls. -func (s *TestSuite) assertEqualNewAccountCalls(mk *MockAccountKeeper, expected []authtypes.AccountI, msg string, args ...interface{}) bool { - return assertEqualSlice(s, expected, mk.NewAccountCalls, authtypes.AccountI.String, +// assertNewAccountCalls asserts that a mock keeper's NewAccountCalls match the provided expected calls. +func (s *TestSuite) assertNewAccountCalls(mk *MockAccountKeeper, expected []authtypes.AccountI, msg string, args ...interface{}) bool { + s.T().Helper() + return assertEqualSlice(s, expected, mk.Calls.NewAccountCalls, authtypes.AccountI.String, msg+" NewAccount calls", args...) } +// assertAccountKeeperCalls asserts that all the calls made to a mock account keeper match the provided expected calls. +func (s *TestSuite) assertAccountKeeperCalls(mk *MockAccountKeeper, expected AccountCalls, msg string, args ...interface{}) bool { + s.T().Helper() + rv := s.assertGetAccountCalls(mk, expected.GetAccountCalls, msg, args...) + rv = s.assertSetAccountCalls(mk, expected.SetAccountCalls, msg, args...) && rv + rv = s.assertHasAccountCalls(mk, expected.HasAccountCalls, msg, args...) && rv + return s.assertNewAccountCalls(mk, expected.NewAccountCalls, msg, args...) && rv +} + +// ############################################################################# +// ############################ ############################ +// ########################## MockAttributeKeeper ########################## +// ############################ ############################ +// ############################################################################# + +var _ exchange.AttributeKeeper = (*MockAttributeKeeper)(nil) + +// MockAttributeKeeper satisfies the exchange.AttributeKeeper interface but just records the calls and allows dictation of results. type MockAttributeKeeper struct { - GetAllAttributesAddrCalls [][]byte + Calls AttributeCalls GetAllAttributesAddrResultsMap map[string]*GetAllAttributesAddrResult } -var _ exchange.AttributeKeeper = (*MockAttributeKeeper)(nil) +// AttributeCalls contains all the calls that the mock attribute keeper makes. +type AttributeCalls struct { + GetAllAttributesAddrCalls [][]byte +} +// GetAllAttributesAddrResult contains the result args to return for a GetAllAttributesAddr call. +type GetAllAttributesAddrResult struct { + attrs []attrtypes.Attribute + err error +} + +// NewMockAttributeKeeper creates a new empty MockAttributeKeeper. +// Follow it up with WithGetAllAttributesAddrResult to dictate results. func NewMockAttributeKeeper() *MockAttributeKeeper { return &MockAttributeKeeper{ GetAllAttributesAddrResultsMap: make(map[string]*GetAllAttributesAddrResult), @@ -163,26 +208,27 @@ func (k *MockAttributeKeeper) WithGetAllAttributesAddrResult(addr []byte, attrs } func (k *MockAttributeKeeper) GetAllAttributesAddr(_ sdk.Context, addr []byte) ([]attrtypes.Attribute, error) { - k.GetAllAttributesAddrCalls = append(k.GetAllAttributesAddrCalls, addr) + k.Calls.GetAllAttributesAddrCalls = append(k.Calls.GetAllAttributesAddrCalls, addr) if rv, found := k.GetAllAttributesAddrResultsMap[string(addr)]; found { return rv.attrs, rv.err } return nil, nil } -// assertEqualGetAllAttributesAddrCalls asserts that a mock keeper's GetAllAttributesAddrCalls match the provided expected calls. -func (s *TestSuite) assertEqualGetAllAttributesAddrCalls(mk *MockAttributeKeeper, expected [][]byte, msg string, args ...interface{}) bool { - return assertEqualSlice(s, expected, mk.GetAllAttributesAddrCalls, +// assertGetAllAttributesAddrCalls asserts that a mock keeper's GetAllAttributesAddrCalls match the provided expected calls. +func (s *TestSuite) assertGetAllAttributesAddrCalls(mk *MockAttributeKeeper, expected [][]byte, msg string, args ...interface{}) bool { + s.T().Helper() + return assertEqualSlice(s, expected, mk.Calls.GetAllAttributesAddrCalls, func(addr []byte) string { return s.getAddrName(addr) }, msg+" NewAccount calls", args...) } -// GetAllAttributesAddrResult contains the result args to return for a GetAllAttributesAddr call. -type GetAllAttributesAddrResult struct { - attrs []attrtypes.Attribute - err error +// assertAttributeKeeperCalls asserts that all the calls made to a mock account keeper match the provided expected calls. +func (s *TestSuite) assertAttributeKeeperCalls(mk *MockAttributeKeeper, expected AttributeCalls, msg string, args ...interface{}) bool { + s.T().Helper() + return s.assertGetAllAttributesAddrCalls(mk, expected.GetAllAttributesAddrCalls, msg, args...) } // NewGetAllAttributesAddrResult creates a new GetAllAttributesAddrResult from the provided stuff. @@ -194,17 +240,51 @@ func NewGetAllAttributesAddrResult(attrs []attrtypes.Attribute, errStr string) * return rv } +// ############################################################################# +// ############################## ############################### +// ############################ MockBankKeeper ############################# +// ############################## ############################### +// ############################################################################# + +var _ exchange.BankKeeper = (*MockBankKeeper)(nil) + // MockBankKeeper satisfies the exchange.BankKeeper interface but just records the calls and allows dictation of results. type MockBankKeeper struct { - SendCoinsCalls []*SendCoinsArgs + Calls BankCalls SendCoinsResultsQueue []string - SendCoinsFromAccountToModuleCalls []*SendCoinsFromAccountToModuleArgs SendCoinsFromAccountToModuleResultsQueue []string - InputOutputCoinsCalls []*InputOutputCoinsArgs InputOutputCoinsResultsQueue []string } -var _ exchange.BankKeeper = (*MockBankKeeper)(nil) +// BankCalls contains all the calls that the mock bank keeper makes. +type BankCalls struct { + SendCoinsCalls []*SendCoinsArgs + SendCoinsFromAccountToModuleCalls []*SendCoinsFromAccountToModuleArgs + InputOutputCoinsCalls []*InputOutputCoinsArgs +} + +// SendCoinsArgs is a record of a call that is made to SendCoins. +type SendCoinsArgs struct { + ctxHasQuarantineBypass bool + fromAddr sdk.AccAddress + toAddr sdk.AccAddress + amt sdk.Coins +} + +// SendCoinsFromAccountToModuleArgs is a record of a call that is made to SendCoinsFromAccountToModule. +type SendCoinsFromAccountToModuleArgs struct { + ctxHasQuarantineBypass bool + senderAddr sdk.AccAddress + recipientModule string + amt sdk.Coins +} + +// InputOutputCoinsArgs is a record of a call that is made to InputOutputCoins. +type InputOutputCoinsArgs struct { + ctxHasQuarantineBypass bool + inputs []banktypes.Input + outputs []banktypes.Output +} // NewMockBankKeeper creates a new empty MockBankKeeper. // Follow it up with WithSendCoinsResults, WithSendCoinsFromAccountToModuleResults, @@ -237,8 +317,8 @@ func (k *MockBankKeeper) WithInputOutputCoinsResults(errs ...string) *MockBankKe return k } -func (k MockBankKeeper) SendCoins(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error { - k.SendCoinsCalls = append(k.SendCoinsCalls, NewSendCoinsArgs(ctx, fromAddr, toAddr, amt)) +func (k *MockBankKeeper) SendCoins(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error { + k.Calls.SendCoinsCalls = append(k.Calls.SendCoinsCalls, NewSendCoinsArgs(ctx, fromAddr, toAddr, amt)) var err error if len(k.SendCoinsResultsQueue) > 0 { if len(k.SendCoinsResultsQueue[0]) > 0 { @@ -249,8 +329,8 @@ func (k MockBankKeeper) SendCoins(ctx sdk.Context, fromAddr, toAddr sdk.AccAddre return err } -func (k MockBankKeeper) SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error { - k.SendCoinsFromAccountToModuleCalls = append(k.SendCoinsFromAccountToModuleCalls, +func (k *MockBankKeeper) SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error { + k.Calls.SendCoinsFromAccountToModuleCalls = append(k.Calls.SendCoinsFromAccountToModuleCalls, NewSendCoinsFromAccountToModuleArgs(ctx, senderAddr, recipientModule, amt)) var err error if len(k.SendCoinsFromAccountToModuleResultsQueue) > 0 { @@ -262,8 +342,8 @@ func (k MockBankKeeper) SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr return err } -func (k MockBankKeeper) InputOutputCoins(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) error { - k.InputOutputCoinsCalls = append(k.InputOutputCoinsCalls, NewInputOutputCoinsArgs(ctx, inputs, outputs)) +func (k *MockBankKeeper) InputOutputCoins(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) error { + k.Calls.InputOutputCoinsCalls = append(k.Calls.InputOutputCoinsCalls, NewInputOutputCoinsArgs(ctx, inputs, outputs)) var err error if len(k.InputOutputCoinsResultsQueue) > 0 { if len(k.InputOutputCoinsResultsQueue[0]) > 0 { @@ -274,31 +354,34 @@ func (k MockBankKeeper) InputOutputCoins(ctx sdk.Context, inputs []banktypes.Inp return err } -// assertEqualSendCoinsCalls asserts that a mock keeper's SendCoinsCalls match the provided expected calls. -func (s *TestSuite) assertEqualSendCoinsCalls(mk *MockBankKeeper, expected []*SendCoinsArgs, msg string, args ...interface{}) bool { - return assertEqualSlice(s, expected, mk.SendCoinsCalls, s.sendCoinsArgsString, +// assertSendCoinsCalls asserts that a mock keeper's SendCoinsCalls match the provided expected calls. +func (s *TestSuite) assertSendCoinsCalls(mk *MockBankKeeper, expected []*SendCoinsArgs, msg string, args ...interface{}) bool { + s.T().Helper() + return assertEqualSlice(s, expected, mk.Calls.SendCoinsCalls, s.sendCoinsArgsString, msg+" SendCoins calls", args...) } -// assertEqualSendCoinsFromAccountToModuleCalls asserts that a mock keeper's +// assertSendCoinsFromAccountToModuleCalls asserts that a mock keeper's // SendCoinsFromAccountToModuleCalls match the provided expected calls. -func (s *TestSuite) assertEqualSendCoinsFromAccountToModuleCalls(mk *MockBankKeeper, expected []*SendCoinsFromAccountToModuleArgs, msg string, args ...interface{}) bool { - return assertEqualSlice(s, expected, mk.SendCoinsFromAccountToModuleCalls, s.sendCoinsFromAccountToModuleArgsString, +func (s *TestSuite) assertSendCoinsFromAccountToModuleCalls(mk *MockBankKeeper, expected []*SendCoinsFromAccountToModuleArgs, msg string, args ...interface{}) bool { + s.T().Helper() + return assertEqualSlice(s, expected, mk.Calls.SendCoinsFromAccountToModuleCalls, s.sendCoinsFromAccountToModuleArgsString, msg+" SendCoinsFromAccountToModule calls", args...) } -// assertEqualInputOutputCoinsCalls asserts that a mock keeper's InputOutputCoinsCalls match the provided expected calls. -func (s *TestSuite) assertEqualInputOutputCoinsCalls(mk *MockBankKeeper, expected []*InputOutputCoinsArgs, msg string, args ...interface{}) bool { - return assertEqualSlice(s, expected, mk.InputOutputCoinsCalls, s.inputOutputCoinsArgsString, +// assertInputOutputCoinsCalls asserts that a mock keeper's InputOutputCoinsCalls match the provided expected calls. +func (s *TestSuite) assertInputOutputCoinsCalls(mk *MockBankKeeper, expected []*InputOutputCoinsArgs, msg string, args ...interface{}) bool { + s.T().Helper() + return assertEqualSlice(s, expected, mk.Calls.InputOutputCoinsCalls, s.inputOutputCoinsArgsString, msg+" InputOutputCoins calls", args...) } -// SendCoinsArgs is a record of a call that is made to SendCoins. -type SendCoinsArgs struct { - ctxHasQuarantineBypass bool - fromAddr sdk.AccAddress - toAddr sdk.AccAddress - amt sdk.Coins +// assertBankKeeperCalls asserts that all the calls made to a mock bank keeper match the provided expected calls. +func (s *TestSuite) assertBankKeeperCalls(mk *MockBankKeeper, expected BankCalls, msg string, args ...interface{}) bool { + s.T().Helper() + rv := s.assertSendCoinsCalls(mk, expected.SendCoinsCalls, msg, args...) + rv = s.assertSendCoinsFromAccountToModuleCalls(mk, expected.SendCoinsFromAccountToModuleCalls, msg, args...) && rv + return s.assertInputOutputCoinsCalls(mk, expected.InputOutputCoinsCalls, msg, args...) && rv } // NewSendCoinsArgs creates a new record of args provided to a call to SendCoins. @@ -318,14 +401,6 @@ func (s *TestSuite) sendCoinsArgsString(a *SendCoinsArgs) string { a.ctxHasQuarantineBypass, s.getAddrName(a.fromAddr), s.getAddrName(a.toAddr), a.amt) } -// SendCoinsFromAccountToModuleArgs is a record of a call that is made to SendCoinsFromAccountToModule. -type SendCoinsFromAccountToModuleArgs struct { - ctxHasQuarantineBypass bool - senderAddr sdk.AccAddress - recipientModule string - amt sdk.Coins -} - // NewSendCoinsFromAccountToModuleArgs creates a new record of args provided to a call to SendCoinsFromAccountToModule. func NewSendCoinsFromAccountToModuleArgs(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) *SendCoinsFromAccountToModuleArgs { return &SendCoinsFromAccountToModuleArgs{ @@ -343,13 +418,6 @@ func (s *TestSuite) sendCoinsFromAccountToModuleArgsString(a *SendCoinsFromAccou a.ctxHasQuarantineBypass, s.getAddrName(a.senderAddr), a.recipientModule, a.amt) } -// InputOutputCoinsArgs is a record of a call that is made to InputOutputCoins. -type InputOutputCoinsArgs struct { - ctxHasQuarantineBypass bool - inputs []banktypes.Input - outputs []banktypes.Output -} - // NewInputOutputCoinsArgs creates a new record of args provided to a call to InputOutputCoins. func NewInputOutputCoinsArgs(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) *InputOutputCoinsArgs { return &InputOutputCoinsArgs{ @@ -387,17 +455,53 @@ func (s *TestSuite) outputsString(vals []banktypes.Output) string { return fmt.Sprintf("{%s}", strings.Join(strs, ", ")) } +// ############################################################################# +// ############################## ############################### +// ############################ MockHoldKeeper ############################# +// ############################## ############################### +// ############################################################################# + +var _ exchange.HoldKeeper = (*MockHoldKeeper)(nil) + // MockHoldKeeper satisfies the exchange.HoldKeeper interface but just records the calls and allows dictation of results. type MockHoldKeeper struct { - AddHoldCalls []*AddHoldArgs - ReleaseHoldCalls []*ReleaseHoldArgs - GetHoldCoinCalls []*GetHoldCoinArgs + Calls HoldCalls AddHoldResultsQueue []string ReleaseHoldResultsQueue []string GetHoldCoinResultsMap map[string]map[string]*GetHoldCoinResults } -var _ exchange.HoldKeeper = (*MockHoldKeeper)(nil) +// HoldCalls contains all the calls that the mock hold keeper makes. +type HoldCalls struct { + AddHoldCalls []*AddHoldArgs + ReleaseHoldCalls []*ReleaseHoldArgs + GetHoldCoinCalls []*GetHoldCoinArgs +} + +// AddHoldArgs is a record of a call that is made to AddHold. +type AddHoldArgs struct { + addr sdk.AccAddress + funds sdk.Coins + reason string +} + +// ReleaseHoldArgs is a record of a call that is made to ReleaseHold. +type ReleaseHoldArgs struct { + addr sdk.AccAddress + funds sdk.Coins +} + +// GetHoldCoinArgs is a record of a call that is made to GetHoldCoin. +type GetHoldCoinArgs struct { + addr sdk.AccAddress + denom string +} + +// GetHoldCoinResults contains the result args to return for a GetHoldCoin call. +type GetHoldCoinResults struct { + amount sdkmath.Int + err error +} // NewMockHoldKeeper creates a new empty MockHoldKeeper. // Follow it up with WithAddHoldResults, WithReleaseHoldResults, WithGetHoldCoinResult @@ -456,7 +560,7 @@ func (k *MockHoldKeeper) WithGetHoldCoinErrorResult(addr sdk.AccAddress, denom s } func (k *MockHoldKeeper) AddHold(_ sdk.Context, addr sdk.AccAddress, funds sdk.Coins, reason string) error { - k.AddHoldCalls = append(k.AddHoldCalls, NewAddHoldArgs(addr, funds, reason)) + k.Calls.AddHoldCalls = append(k.Calls.AddHoldCalls, NewAddHoldArgs(addr, funds, reason)) var err error if len(k.AddHoldResultsQueue) > 0 { if len(k.AddHoldResultsQueue[0]) > 0 { @@ -468,7 +572,7 @@ func (k *MockHoldKeeper) AddHold(_ sdk.Context, addr sdk.AccAddress, funds sdk.C } func (k *MockHoldKeeper) ReleaseHold(_ sdk.Context, addr sdk.AccAddress, funds sdk.Coins) error { - k.ReleaseHoldCalls = append(k.ReleaseHoldCalls, NewReleaseHoldArgs(addr, funds)) + k.Calls.ReleaseHoldCalls = append(k.Calls.ReleaseHoldCalls, NewReleaseHoldArgs(addr, funds)) var err error if len(k.ReleaseHoldResultsQueue) > 0 { if len(k.ReleaseHoldResultsQueue[0]) > 0 { @@ -480,7 +584,7 @@ func (k *MockHoldKeeper) ReleaseHold(_ sdk.Context, addr sdk.AccAddress, funds s } func (k *MockHoldKeeper) GetHoldCoin(_ sdk.Context, addr sdk.AccAddress, denom string) (sdk.Coin, error) { - k.GetHoldCoinCalls = append(k.GetHoldCoinCalls, NewGetHoldCoinArgs(addr, denom)) + k.Calls.GetHoldCoinCalls = append(k.Calls.GetHoldCoinCalls, NewGetHoldCoinArgs(addr, denom)) if denomMap, aFound := k.GetHoldCoinResultsMap[string(addr)]; aFound { if rv, dFound := denomMap[denom]; dFound { return sdk.NewCoin(denom, rv.amount), rv.err @@ -489,29 +593,33 @@ func (k *MockHoldKeeper) GetHoldCoin(_ sdk.Context, addr sdk.AccAddress, denom s return sdk.NewInt64Coin(denom, 0), nil } -// assertEqualAddHoldCalls asserts that a mock keeper's AddHoldCalls match the provided expected calls. -func (s *TestSuite) assertEqualAddHoldCalls(mk *MockHoldKeeper, expected []*AddHoldArgs, msg string, args ...interface{}) bool { - return assertEqualSlice(s, expected, mk.AddHoldCalls, s.addHoldArgsString, +// assertAddHoldCalls asserts that a mock keeper's AddHoldCalls match the provided expected calls. +func (s *TestSuite) assertAddHoldCalls(mk *MockHoldKeeper, expected []*AddHoldArgs, msg string, args ...interface{}) bool { + s.T().Helper() + return assertEqualSlice(s, expected, mk.Calls.AddHoldCalls, s.addHoldArgsString, msg+" AddHoldCalls calls", args...) } -// assertEqualReleaseHoldCalls asserts that a mock keeper's ReleaseHoldCalls match the provided expected calls. -func (s *TestSuite) assertEqualReleaseHoldCalls(mk *MockHoldKeeper, expected []*ReleaseHoldArgs, msg string, args ...interface{}) bool { - return assertEqualSlice(s, expected, mk.ReleaseHoldCalls, s.releaseHoldArgsString, +// assertReleaseHoldCalls asserts that a mock keeper's ReleaseHoldCalls match the provided expected calls. +func (s *TestSuite) assertReleaseHoldCalls(mk *MockHoldKeeper, expected []*ReleaseHoldArgs, msg string, args ...interface{}) bool { + s.T().Helper() + return assertEqualSlice(s, expected, mk.Calls.ReleaseHoldCalls, s.releaseHoldArgsString, msg+" ReleaseHoldCalls calls", args...) } -// assertEqualGetHoldCoinCalls asserts that a mock keeper's GetHoldCoinCalls match the provided expected calls. -func (s *TestSuite) assertEqualGetHoldCoinCalls(mk *MockHoldKeeper, expected []*GetHoldCoinArgs, msg string, args ...interface{}) bool { - return assertEqualSlice(s, expected, mk.GetHoldCoinCalls, s.getHoldCoinArgsString, +// assertGetHoldCoinCalls asserts that a mock keeper's GetHoldCoinCalls match the provided expected calls. +func (s *TestSuite) assertGetHoldCoinCalls(mk *MockHoldKeeper, expected []*GetHoldCoinArgs, msg string, args ...interface{}) bool { + s.T().Helper() + return assertEqualSlice(s, expected, mk.Calls.GetHoldCoinCalls, s.getHoldCoinArgsString, msg+" GetHoldCoinCalls calls", args...) } -// AddHoldArgs is a record of a call that is made to AddHold. -type AddHoldArgs struct { - addr sdk.AccAddress - funds sdk.Coins - reason string +// assertHoldKeeperCalls asserts that all the calls made to a mock hold keeper match the provided expected calls. +func (s *TestSuite) assertHoldKeeperCalls(mk *MockHoldKeeper, expected HoldCalls, msg string, args ...interface{}) bool { + s.T().Helper() + rv := s.assertAddHoldCalls(mk, expected.AddHoldCalls, msg, args...) + rv = s.assertReleaseHoldCalls(mk, expected.ReleaseHoldCalls, msg, args...) && rv + return s.assertGetHoldCoinCalls(mk, expected.GetHoldCoinCalls, msg, args...) && rv } // NewAddHoldArgs creates a new record of args provided to a call to AddHold. @@ -528,12 +636,6 @@ func (s *TestSuite) addHoldArgsString(a *AddHoldArgs) string { return fmt.Sprintf("{addr:%s, funds:%s, reason:%q}", s.getAddrName(a.addr), a.funds, a.reason) } -// ReleaseHoldArgs is a record of a call that is made to ReleaseHold. -type ReleaseHoldArgs struct { - addr sdk.AccAddress - funds sdk.Coins -} - // NewReleaseHoldArgs creates a new record of args provided to a call to ReleaseHold. func NewReleaseHoldArgs(addr sdk.AccAddress, funds sdk.Coins) *ReleaseHoldArgs { return &ReleaseHoldArgs{ @@ -547,12 +649,6 @@ func (s *TestSuite) releaseHoldArgsString(a *ReleaseHoldArgs) string { return fmt.Sprintf("{addr:%s, funds:%s}", s.getAddrName(a.addr), a.funds) } -// GetHoldCoinArgs is a record of a call that is made to GetHoldCoin. -type GetHoldCoinArgs struct { - addr sdk.AccAddress - denom string -} - // NewGetHoldCoinArgs creates a new record of args provided to a call to GetHoldCoin. func NewGetHoldCoinArgs(addr sdk.AccAddress, denom string) *GetHoldCoinArgs { return &GetHoldCoinArgs{ @@ -565,9 +661,3 @@ func NewGetHoldCoinArgs(addr sdk.AccAddress, denom string) *GetHoldCoinArgs { func (s *TestSuite) getHoldCoinArgsString(a *GetHoldCoinArgs) string { return fmt.Sprintf("{addr:%s, denom:%s}", s.getAddrName(a.addr), a.denom) } - -// GetHoldCoinResults contains the result args to return for a GetHoldCoin call. -type GetHoldCoinResults struct { - amount sdkmath.Int - err error -} From fc656c9d1b03014aa46e60c371cd7cca076ff4cb Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 9 Oct 2023 14:13:18 -0600 Subject: [PATCH 270/309] [1658]: Lint fixes. --- x/exchange/keeper/fulfillment.go | 1 + x/exchange/module/module.go | 2 +- x/exchange/simulation/genesis.go | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index b31a453bf8..45f2f0cedc 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/provenance-io/provenance/x/exchange" ) diff --git a/x/exchange/module/module.go b/x/exchange/module/module.go index aec803c054..646a1a8341 100644 --- a/x/exchange/module/module.go +++ b/x/exchange/module/module.go @@ -90,7 +90,7 @@ func (AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) { func (AppModuleBasic) RegisterLegacyAminoCodec(_ *codec.LegacyAmino) {} // RegisterInvariants registers the invariants for the exchange module. -func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // Deprecated: Route returns the message routing key for the exchange module, empty. func (am AppModule) Route() sdk.Route { return sdk.Route{} } diff --git a/x/exchange/simulation/genesis.go b/x/exchange/simulation/genesis.go index 14ceb2129b..807784ce97 100644 --- a/x/exchange/simulation/genesis.go +++ b/x/exchange/simulation/genesis.go @@ -4,5 +4,6 @@ import "github.com/cosmos/cosmos-sdk/types/module" // RandomizedGenState generates a random GenesisState for the exchange module. func RandomizedGenState(simState *module.SimulationState) { + _ = simState // TODO[1658]: Write RandomizedGenState. } From 2429efa080a51bf7e538e665bb87c42ba2842b7c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 9 Oct 2023 14:21:45 -0600 Subject: [PATCH 271/309] [1658]: Fully generate the proto stuff. --- client/docs/statik/statik.go | 2 +- client/docs/swagger-ui/swagger.yaml | 23 +++++++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/client/docs/statik/statik.go b/client/docs/statik/statik.go index 805799aea4..8c63d27034 100644 --- a/client/docs/statik/statik.go +++ b/client/docs/statik/statik.go @@ -8,6 +8,6 @@ import ( ) func init() { - data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00 \x00favicon-16x16.pngUT\x05\x00\x01\x80Cm8\x00\xbd\x01B\xfe\x89PNG\x0d\n\x1a\n\x00\x00\x00\x0dIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\x00\x00\x01\x84IDATx\x01\x95S\x03Luq\x1c\xfd\x8c\xf1\xc3\xec0\xa7)\xcda\xb6k6\xb2\x9b\xf9\xb2k\xc85/\xdb\x8dqx\xc6\x94m\xcc{\xef\x7fO\xff\xf3l\xdc\xed\xf2\xe0\xfe\xf8\xc9\xffP\x14\x11/\x14[\xa3P\xc4\xa1\xbc?\xf1t>7\x12s\x13\x03\x85\xca7IR a\xb5j\x8f\xa71\xbe]\x88\xf6\xb9L\xf0\x1c\x93\xcf\xda\xe3)\x10\x93f\x8d\xe4\x06\x13\xcf\xde<\x9b\xd14\x95\x8a\x92\x81OA\xcfF\x89\xdd<\x9b M\xe6}L\xe4\x07\x15\xc5\xf5\xe3\xffI\x0c{\xd6\x8d\xffs\x994\xbasfh\xae?\xafk\x1aprw\x10 <\xb9\xdb\xc7\x86\xa6\xd1\x19I\n\xa8\xb1\xd7\x84y3g\x171T$\xb5c\x7fq\xfbbq\xbfk\x8e'\x1dQ\xb0\xc2,\x92\x0bx|;F\xe5\xf0\xef\x00\x83\xf2\xa1\x1fx|?q\xbd\xcb\xc2\x16\x80ZF\xf0\xc4J\xf3\xe3\xe4n1\xcc\x17k`:}\xcby\xe8\x98\xcbB\xc7|6z\x97r\xd14\x9d\x06\xd3\xf9\x8a\xe4\x94\x90\x8b\xb6\xd9\x0cP\xebc@\xd0|\xbe*\xc94\xc8\xa7\x98'\xcdh\x00\xe3\xd92\xa6vK}\x0cB\xa4\xf0+D\n\xc7\x81)\xb0\x10\x9a\xe3\xa9\xd8\x8bx\xe4(\xa2\xbb\x8dl\x0d\x01\xb6\x8a-\xf378\xbe\xdd\xc7\xa6\xb6\xc9\xd9\xc6d\xd8\\m\xf4\x0c\x92 uQ\x0e\xd2\xf5\xb3\xd1\xf1w\xdfQ\x16\xb34a$\xa1\xc4\xc4(V\xbcF\xd9\xdf\xa4\x91\xe9\xb0&,\x12+\xcd\x93\xcf\x1c\x1cb\xdc\xca\x00qt\xeb\xcc-\x14\x89\xfe\xfc\x0fm2j\x88\xec\xccs\x18\x00\x00\x00\x00IEND\xaeB`\x82\x01\x00\x00\xff\xffPK\x07\x08\xd4`4t\xc7\x01\x00\x00\xbd\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00 \x00favicon-32x32.pngUT\x05\x00\x01\x80Cm8\x00u\x04\x8a\xfb\x89PNG\x0d\n\x1a\n\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x04|ID\xc4\xcf\xd0@\x04&%\xad\x1e\x16\x0f\xf7\x8d\x97AR\xfa\xca\xe7l\x87\x05\xf8\xd2\xfb\x0c\x84\x1d\x0dLVY\xdc/ju\x13\x1a\x88\xd2\xa0\xaaa\x82|nzp_\xf4\x03\xc8 \xd4;^\x8a9}\xeeu\x9a\x91 `\x04\x14s\xec\xe1\x0c\xc6]\xa3\x05``\xd1w\x12*~ \x00\xf3\xae\xd3\xa0\x9cb\x82\xa2bx(\xb3n\x1fqx\xd2\xf2\xda4\x1d\x8a}\x1ck\xd4>\x9cI+\xeb\xb3\xf4k\xc8u`L\x93\xf3]4\xb5\xd0\xc3\xe33\xd9\xee\xd7\xf2\xd9\x19\xea\x18\xc9\xc1Y:\x18\xfb(-\xadN\x82\x06e\xd5\x1f0\xa2\x1dV\xf8\xbe0\xc1\x985\x01\xf8\xd2~\\\xa6\xa5\xb5)&\xf6\x98V\x80l\xe4\x03\xf8\x03\x04\x00s\x9a^\xec\x85\x00\xf4+\x0b\x00\xe1:G\xf2p\x96\x0e\xc4,\xe46\x1e5\xbbP\xdd\x15J\x80}\xce\xa4\xe2\xc8{m\xa4\xe2\xc3\xc2\x01\x07\xc0\xdb\xa4\x18-\xa1\x931\xba\x10S\xfa%\xb6P`\x10\x19v\x99#|Gg\x9b \x10W\xf6\x8dI1\xba\x92\xd66\x17E\x12\xfa\xd9\xa8\xf3UTe\n\x1b\x95\x9d\x81f\xe5\x18\xa5umc\x81\x86\xa6\xeb\xec \x804\xcbg\x17\xa19\xfa\xc6\xf7<\xa3\xbd\xf2\x0e\x7f\x02\x80\x97Y\xc7\xac\x184$h\xa3v\xba! \xcc{\xcd\xb4!\xb1\xd8\x92%h\xe3\x93\xdc\xd3_\xda1\xe6\xaei\xcf\x83\xa6p\xbc$\xf0\xb2\xda\x94\xa2q\x14B@\x13\xdb\xff\xf3\xd7\x0d\xfaA\xb9\xc5n{\x8e\xd6Y\x08\x01u\xc1'~\x16\x8e\xe9\x04\xa2\xfbA+\xc74\x0c\x98\xab\xd7:\xfc0\xd1v\xaf$\xa2#\xb7\xf1\x08\xfdm!OXh8\x10j|g\xd1\xe0a\xb2\x99\x04\x9a[y\x9a\xbdk\xf24C$\xa0\x9e#\x9f\xa3\xa8\x001\xc6\x1a\"\xc0\xe4i\xa6\xcc0\xf3\xf7\xb7\xf5XE\xb8\xe0\xa1\xc9\xc2\x0c\x90\x83\x80$\x838\xdf\xd6\xe3\xd4\x82FNG\x0f\x876\x8a\xbf1\xa8d(\xa7@\x8cQX\x90\xdb\x19\x9f\xc5YG\xe9\x9e\x00\xa5y3]\x9aJ\xe1\"\x00\x00\x00\x00IEND\xaeB`\x82\x01\x00\x00\xff\xffPK\x07\x086B\xc8\xd7\x7f\x04\x00\x00u\x04\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00 \x00index.htmlUT\x05\x00\x01\x80Cm8\x9cT]k\xdc:\x10}\xdf_1Q\x1e\x92\\\"\xfb&\x81p\xf1\xb5\xfd\x90\xa6\xa5\x81\x94\x06\x92}(\xa5\x14\xd9\x1a{\xa7\x91\xa5E\x92\xf7#!\xff\xbdX\xf6\xae\xb7\xdd\x90BYX\x8f\xe7\x9c9\x1a\x1d\x8d\x9c\x1ep\x0e\x1f\x1f>\xddBe,8/<\x95 \xc9yKE\xeb\xc9h(Z-\x15B\xd1\x92\x92\xc0y>I\x0f\xae?\xbf{\xf8r\xf7\x1ef\xbeQ\xf9$\xed\x1e\xa0\x84\xae3\x86\x9a\xe5\x13\x80t\x86Bv\x01@\xda\xa0\x17P\xce\x84u\xe836}\xf8\xc0\xffc\x03\xe4\xc9+\xcc\xef\x97\xa2\xae\xd1\xc2\xf4&\x8d\xfbL\x8f*\xd2\x8f`Qe\xcc\xf9\xb5B7C\xf4\x0c\xfcz\x8e\x19\xf3\xb8\xf2q\xe9\x1c\x83\x99\xc5*c\xae\xd7\xe0-E!\xbb'A\xa5\xd1\x9bbjD\x8d\xf1\\\xd7\x9b\xeaJ,:\x9c_\x9c\xaf.\xce\xa3\x008zB\x97\xb1\x90a\x10\xff\x9d\xde\xd9\xe5\xea\xec\xf2\x17\xbd\x90\x19\xf5\xc2\xc6\xfa\x18\x82\x9bC\xf8<<\x01\n\xb3\xe2\x8e\x9eH\xd7 \x14\xc6J\xb4\xbc0\xab\xff\xb7\xb8Y\xa0\xad\x94Y&\xc0\x1b\xf3\xc4]i\x8dR\x85\xb0\x8e/\xd0z*\x85\xda\xe7\xf2u\x02=q\x83\xbdL\x86\xe0\x9f\xd3M\x90\x14X\x19\x8b\xe3\xbb\xa8<\xda7\xfb#=CK~O\xb40r\xbdW\xd8\x08[\x93N\xfe\x1d\xdb+D\xf9X[\xd3j\x99\xc0a%\xba\xdf(\xd5\xfd\xa7\xf1\xd6\xaf4\xee'\xac\x0b;\xf9\xc1OI\x0b \xb9;\x0e,OcI\x8b|2\x18^Z\x9a{p\xb6\xdc%\xf1~\xc6\xa3\x1f\x8e\xe5\xdd*\x81\x94\xbfY\xe1\xbc\xd0R(\xa3\x91\xcf-:\xf4o\x14\xf7/K\xd2\xd2,#\xa3\x95\x11\x122\xa8Z]v\x17\xec\xf8\x04\x9e7N\xc51\\\x85{&\xc0\xad\x9d\xc7f\xc8\x97F;\x0f-A\x06\xc3m\x99\xde\\\x85\x9e\x8fGG[\xab\x12`Q\xeb\x8c\xd8v\xfb_}K7\xd3F\xfe]\xb1\xa1\x82h%q{\x8b\x9b6\x88/\xc4i }\xc07u~}\xe5\xad\xfd\xc9\x98\xe7q\xd8_}o\xf1\x92%\x9dx\x15\x9f\xd3yO\xbdX]\x1aA\xc9>t\xd6o\x93\xd3\x92\xf2\x04l\xc5\x8d\x92jz\xc1jN\xd6\xf2\xa9\x87\xfa\xb5]\x05\xcc\xf9\x1acB\xa9,\x9f\xd0\x08\x05\xb7\x962\xec\xdb\xb6\xe2\x16b\xc6\xd5\x942H\x05KfI\x06\x7f\x9c\x98\xa8\xc0\xd5\x9c\xa2\x0c\x13\xa3\xe7U\x8e\xb55;'Nk\xe6\xd0\x9d;\xd4%^\x14\xbd\xd5\xf7\x92QN\x8e.\x1c`\x079m\xe3\x9e\x8a\xfe\xed\xa2\xad\xe0y>\xe6\xe23\xdc\xf8u\xa7=\xa3\xf6\xa1\x98\xb4\x17g\xa9\xf4\x1dA\xa8Z\xe4\xf6\x88_\xfc)\xf8\xd5N\xcf,\xea\xb4\xabS\xf2\xd2\xe0v\x10\x90\x82\xbd\xb3\xe1\xc1g\xc8>\x120\x0c{\x1d\xbd\x1c\xd1\x7fd\xb4\xbf\x82|\xf7\x9f\xd0\xa7\x1e\x82\xc5`H\xc0\x94F3p0$H.\x0f]v3\xaa\x9b\x1c\x83EW}\xba4\x12O`_\xb5!H5\xd1 \x9a\x0c\xaa\xcd\x04\x8cE\xe7M:\xe1\x08\xfe\xefQ\xab\x02\xfe\xb7A\xeb\xb6k\xbb\x05{\xef\x8e\xde\x84\xcb\x9c\xb2\x8f\x04\xd7U\xf9\x9aQ:\xbe\xf51\xf1\x1a\xaaW\x97uR\xdd\xe7\xf59\x974\xb7\xfc5s\xd0\xc4P\xdf\xdd\"\xd7\x96\xc2\xdab7x\xb8;\xfc\x01\xfa'\x00\x00\xff\xffPK\x07\x08]\x12r 9\x03\x00\x00T \x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00 \x00swagger-ui-bundle.jsUT\x05\x00\x01\x80Cm8\xec\xfdyw\xdb6\xf68\x8c\xff\xffy\x15\xd7\xfa\xf6\x9b!kZ\xb1\x9d\xa5\xad\x13\xc5\x93\xc5m\xb3g\xe2\xa4\xcb\xa8\x1a\x1fZ\x82,6\x14\xa8\x90\x90m\xb5\xf2\xef\xb5\xff\x0e.\x00\x12$\x01\x10r\xdc\x99\xf9<\xcf\xc3s\xdaX\\\xb0\\\\\\\xdc\xfdn\xc1tI\xc7,\xc9h@\"`!\xfc\xf9?\x00\x00\xbd\xec\xf4w2f=\x18\x0c\x80\xad\x16$\x9b\x02\xb9\\d9+\xe0\xd6-\xd3\xd3y6Y\xa6\x04\x0e\xe5\x1f}\xf5\xf6\x00X\x10\xc2\x01\xf4T7\xfaG\x132M(\xe1-\x8a\xbf\xfa\xf1|\x02\x87\xf2G0\x1c\xe1\x80\x0e\\\x839T\x7f\xf5\x8f/\xe2\xb33\x92\x7f|\xfedI'));&\xe6'\xffs\x15\xb0YRD\xd5\xf4\xd5\xd4s\xc2\x969\xd5\xc0\xa2\x1e\xf0\xeb<\xce\x81\xc1\x00\xfe\xbcz\xf0?\xe5M\xf5*\xd0 \xd7_\xe6W2\x85\x80\x0d\xf3Q\xa8\xda\xe5?\x14t\x1e\xd4^\xe5mg|t\xc3|\xc4\xbb\xa8=\xc4\xb6\x0e \x8fZw\xd3\x03\xd8\xdak\xdf\x96]\x1c\xc0\x9fW\xb5gW\xf5N\xe5\xa8\x08\x1f\xd58N\xd3 S\x83\x8b \x8b@\xfbEC\xfe3\x85\x01l\xedj\x0f\xca\xd6\xaand\x9b\xb4?\x87\x01\x90\x08h\x7f\xcc\xa7\xc5\xff\x98\xc0\xa0\x8ep\x11\xb4@F\xfb\x99\xc4\xc5\xf5\x1a\xde\xe2\xd2\xf7\x05J\xbc\xcb\xb3\x05\xc9\xd9J~\xd9\x86\xd08\xa3\xd3\xe4l\x99\xc7\xa7)\xb1\x80\x85.\xe7D=\xdfm??#\xec\x00\xf2:\xc4\xc2j\x8e|\x0e\xb46\x87\xe6\xe8\x15\x86 Z\x93\xfe\xc9 )^\xab\xbd\xd1\xc25\xfdR+\xc1\xe7\x1a/SV\x1f\x03\x1c\xf8}\xed\xb1\xd6\xb4? X\x04\xbd\xb8\xc7\x81\x1c\x01\xabO/k.Q\xb3;\xd9\x8c\\\x99E\x9e\xb1\x8c\xef\xca\xfe,.\xde^P\xb5F\x02\x9b\xf0\xfbz\xfb\x0b\x18@\xef\xf6$)X/\x02\x1a\xd0>'\x12w\xef\xde\x13\xaf]\x05\xc3\x06~P\xbd\xff\xde\xb2 P\xb0<\x19\xb3^59\x9d\xdc\xd0\xe0\x1b\xd5T\xd4D\xb5ZS\xf5\x8f\xbe\xbdw'\x0c\xbc\xbe3\x0f\x81\xe9+-\xb6\x08S+\xd9\x05PN#\xb6\x02\x02 -XL\xc7\x9c\xbe\xb10\x046\xcb\xb3\x0b\xa0\xe4\x02>\xac\x16\xe4(\xcf\xb3<\xe8=\x8d)\xcd\x18p\xe0B\x0c\xe34.\n\x88\x0b\x88\xcb\x1ezacG\xde\xcct\xaaG\x1c\xc1\xf3\x08)\x15\x0d\xf6\xef\xef\x87\xf5M\x94\xc0\x00\x82\x1c\x06\x90\x85|\x07\xe4\xf5\x1d\x90\xc3\x81\x01y%\x9cZ\x1bO\x1f\x8f\x01\x96M8\x96t\x98\x18\xc1\x8c\xafd9\x04|\x06|\x13\xef>\x00\n\x0f\x81\xf5SB\xcf\xd8\xec\x01\xd0\xedm\xd3G\xa0f\x8d\xc4\x99\x8e\x1e\x18\xdf\xc8\xfb\x15m\x81A\xfd\xe7z\xcd\x89\x11\xe4}\x9d@I4\xe9\x9d\xc7\xe9\x92\xf4 \xa1\x90s\x88\x05y\xff\"OX\xf9F\x18A\xb0\x1bA\xa2 \x10\xf2\xc9\xe5\xfdOd\xc5igk(\x0djo\xda\xb9%\x009.\x18\x08\xb0\xf6*E*\x16h\xdb\\\x1c\x04\xb9\xbc\xcf\xbf\xd6)H\xbd\xcf+\xbf\x1d\xa5\xef\xc4\xfaHJ\xc4\xa0\xc17\xf7\xef70\xadB,N\xca\xff\x9dX\x7f\xf7\xde\x7f\x0e\xe9\xad\x04\x84\xe8\x14\xe3=\x99\x92\x9c\xd0\xb1\"\x1b\x9c\xd7\x81Y\\\xd0\xbf18%\x84BB\x13\x96\xc4iR\x90 \xec@\xb1\\\x90<\x08kop\x12C&\xbd\xd0x\x86l1\x8e\xd3%c\xb65\x18@p\x9e%\x13\xd8\x85\x01\xe7\xd2\xe0\x10zK*N\xedI\x0f\x0e\x9a(\xcc\xe9\x1bg$+\xaep\xab\xe4\xed\xf8\xc7\x04\x0e\xf4s\xe9\xaf[R\x18@\x1cp\xec\xfa6l\xaci&\x1f\xdd\xb9\xfb]\xf3Q\"\x1f\xdd\xbd\x17\x86&>0n\xb3\x05\xea|6p\x05\xc4\x8d\x1e\xc4\xb6\xb9\xae\x87'\x16\x90\xdf\xba\x05t\x99\xa6\xb8\x92\xccr\xf6\x1cs,\xe1\x8ceN\x8a\x82\xcfs\xbe,\x18\x90\x84\xcdH\x0e\xa7D4\x90\xe5\xdaa\x14\x01?\xacz\xb0\xbd1v4\xd0\x8eT\x04\x88o5d@\xab\xd7\xf9\xe8k$\xca\xc8\x19\x16,_\x8eY\x96\x9b\xa0\x0d\x88\x0f\xe9\x92\x1c\x00i3\x85\xd0d\x1c\x0d\x8c%\xbf\x14\xdd6\xb3\x96\xd0fPw[/5\xc87'\xae\xf2PPk|\x88\xd3\xcfk\xc7\x01\x13\x92\xce\xc9 \xc2\xe0\xe4\x84\x1fT\x1b\xf2\x01\xb8\x1b*\xa0\xe7\xae\x83\xd6\xbc\xd5T+|\x85\x1e\xe7y\xbc\xd2x\xc3\"M\xc6D\xdb*\xa0o\x17f=\xae\xc5\xdc\xeb\x8b/\xf9\xceqNbV;\x99\xc20\xd2\xf1\xa4\xaf-9\xe7\xc7\x1b\xdb\xc8<\x14\x03C\x0f\xd5\xee\xc5}-6\xec\x8b\x80\x84^-\xe6\xce\x16\x97U\x8b\xbf\xfa\xb6\x989[,\xaa\x16_\xfa\xb6\x98t\xcf\xfa\xd6-\xd8J\xab\xa6\x7f\xf0m\xda@\n\xb5\xa6\xb7\x82-\xc1\x1c\x91\xe1t\xe4\xd7\xe0\xd2\xb7\xc1\x85g\x83\x85o\x83\x13\xcf\x06\xd3\xee\x15_\xaf\xb1[\xaf\xe6\xc6\xbe\xe3\x9b\xb5\xc6\xa7\xffbA.X7\x16d\xea\x8fD\xfcA\xfbI\xf1\x9c\x95\x9ck,\xee\xbc$+\xc2\xc5\xf5\xa5|\x81N\xc8%\xde(\xc4\x8d\xc7E\x91\x8d\x93\x98%\xe7\xfc\xa3T\xdc|\x9bOH\x8eo\x8d\xf9\x0d\xd5\x06\xef\xba_\xb5\xc0\x07\xd0?&\xfc\xbcJ\xda\xf4c\xca\x05\xc4\xbf\xff\xfd\xe4\xe4\xf9\xeb\xd7\x1f?<~\xf2\xea\xe8\xe4\xf9\x87\xa3\xf7\xf8\xc7\xc9\xdf\xff\xdekS\xd6E\xfb\x8b\x97G\xbf\x1e=\xb3\xbc>1t\xf0\xe6\xd9\xd1/\xd6\x0ff\xed\x0f\xde\xbe\x7fv\xf4\xde\xfa\xc19\x0c\xe0^\xfb\xf6\x1c\x06\xb0\x07\x0f\x1f\xc2\xb9A\xf1\x00\x03\x98\xc3\x0e\x18\x8e\x96\x15*\x9c\xda\xf7O\x8dZ\"\xa8\x8e\xb2\xad\xbd\xd6SC3'\xd7i\xc6F\xcb/\x9c\xd8J\xfa\xd8$g\xc4\xf6\"O\x92|dn\x91\xc8\xa3\xa1lp\xd7o;]\xf2\xd3\xcc\xf6\xf0\xd8q\x12q\xbee\xbd\x86\xdd\xb6\xf4W\x13*_\xc7l\xd6\x9f\xc7\x97\xfc\x90&R\xb2\x84\x1dT\xb4\xf0c\x88\xb3Tx8\x06\xa8O\x13Rh\x06\x0f\x81>\x80\x8c\x8b\x9f\xf90\x1b\xf1\xe3j\x98\xc160\x83\xac)A\x99{\xcd\xf6\xa9s94\x9e\x8c\xf4\x8b\xe4\x0f\x05S\xfcs\x80\x0cE\xc2\xe9\x02#\xc1cq\xba\xf2'^\x1d\x7f\xb2B\x12\x99P\xba\x9c\x9f\x92\xbc\xc6\x82\xba$o\x8a\xd0\x7f\xf4\xe8\x91 \xfc\xa0\x1a\xe5|&\x15\x1c,_\xa9\xbb\xfb\xdf\xdd\xfd\xee\xfe7\xfb\xdf\xdd\xc3\x19\xd2R\x05\xfb&~cn\x85/2m\xe3\xba\x0d|\x0c\x1e\xc2.\x1c\n o\x03\xab\xc9,\xe0\x00\xcec\x97\n\xaf\xc1\x14\xda\xdaxkb\xe2\x1aM\x05rm94\xe4Zs\xe8\x08\xa1\x1e\x1e\x0e`\x87\xe2\xc9^g\xce\x0d/3x\xc4\x01\xe85\xb0w\xd6\x95\x97\xa3z-G\xee\xb9a?\xf8\xb6\xc7\xfc\xda{\xed\x018}c\xc0!P\xce]\xcb\xc5\xd6\xf77\x83m \x9c\xf5n\x087\x9cC\x12\xef%\xa8di\x9d\xf4\xfa/\x8e\xdf\xcf9\x1dhS\xe6\xdf\xf9y\xd1\xbe\xfd\x06\x06\xb0\xdf\xbe\xfd\x9e\x9fR\x95tW\x19K\x8eW\xf3\xd3,\xe5\xeb(\xfe\xea\x8bM\x9d\x19\x8c \xcf\xc4I\xa7^0\x1cm\xaf`\x00\xef9\x8e<\xb3\x1d\x01\x1f\xcd4\x87\xcd\x92\xa2O\xc9%\xf3f\xc6?\xab\x95\xb2\xe8\xa8\x94\xc1\xa4Z(\xbe\x05\xf7j\xcb6\xe4\xdf;\xa8(\x1cB^\x9e!\x19\x1c \x91v\x9e\x86\x99Y\xb2\x9bd\xd4v\xe2z\xd2\xea\xef]T\xc19$\x81~\xcequJ\x9a\x96A\xfd\xe1\xe6>\xb7~\xf4ec\x9f\xb8\x19\x83\x866H\xb3\xf4!\xcexu\xf1\x93\xb9\x0be\x91\xe1C\xb5\"\x82\xd4!\x08\xa3\x85\xdf\x8c~tw'\x0e\xd3\xf7Hk\x87\xefG|\xcb\x90\xe1\xb3\x91a\x08\x0d\xb5\xcc@?\x13\xd5\xf0\xbcF\xf4\xb3\x07\x8c\xd5\xc9\xabCXp)^]\xbcpv\x81\x1a\xa0\xe6\x91\xa3\xb6cB\xd0 \xab\x84\xe8>\xcb\x8e\xc9g\xbc\xa5Z7\xb7\x0d\x1aP\x0b\"\xc5'\x93M\x18\x95X\xe4\x02\x181\xae4(M\xa9M\xbfut\xb9 cF&\x82A\x83,\x87DIE\xa27\xc8\xa6b\xcb\x15\x11\x7f\xfa \xa5\x1b\xf1\xe8\x00\xb5\\\xb6n\x8d\xab\xc8\xaf+_d\xfb\xf5\xcb\xe0\xdeg\x19\xcab\n\xe2r\x11\x96\xed\xb5 \xfdi\x9e\xcd\x8f(\xcbW\xe5\xcb\xc4w\x94/\xbfl\x94\x86\x81\x11} |\x9cR\x8aT\xb7\x96\xdec\xfb\xc19\xb6\xe0\xcb\x07\xa7F\x13\"4\x19\xdeo\x8cL\xff\xf5QSU\xb1\xec\x98\xe5 =s)\xdd\xb4\xc1\xf6\x86\xcf\xe5\x01=\xea\xd5{\x88\xe0c\xff\xe5\xd1\xaf\xc70\x80\xe7\xfc\xef\x9f\x1e\xbf\xfax\xc4\x7f\xfd\xce\x7f\x1d\xbd\xf9\xf0\xfe9\xfe|\x13\xd5\xfaOh\xc1Q\x1f\x06\xcdQe\xcb|Le\xf2\xd9\xb3M\xd3\xd8^\\\x7fQ\x11|''%\x00{|$\x7f\xf6\"\xe8]\xf5\x9cc\x1e\xc7\xe3\x19yO\x8a\x0e\xeb\xa8\xd6\xd5\x96\xe8\x0b?\xc4sOt-e\xbd\x8f\x14\x1fL\xf0\xfc\xd2\xdf\x1c\x88\x17+\xac\xef\xb3L\xc8\xb2a$\x1eI\xc1Q\xfbH\x9e-\xf2\x05\xd74\xca\xfe\xbb\xac\x18\xdaDR\"\xbdx\x04\xa3\xd8\xd2\x01\x98{\xc8\xf2\x0d\xba\x18wv\xc1\x82_#x\x11F\xf0km\xf1\x15\xbd\xf5\\\x133\xa6\xbf\x14-\xbf\xf4\xc7\xf4\x97\x0eL\x7fY\x1b`EI=\x9b6\x0d\xf1\xe5\x0d#\xfc\x90#\xfc\xa8\x8d\xf0/o\x18S\xf6\xbcz\xf8\"Liw\xc1\x82\x1f\xc4z\xfe\xe0\xbf\x9e?8\xd6\xf3\x87\x06\xe5b_\xb6\x96/\xfaI!Z\xc8\x08\xff\xa5\xb4\xb7\x1c\xbd\xa5\xba\x96\x8f_S\xe4\xbelko\xbf\x8a\xe0\x9f\x11\xfc\x12\xc1?\xdaJ\xd3\xe3\xa3\x7f\xa0\xc2\xd4&9\x12\xe2\x10\x1dOb\xe4\xca\xd0\xa3L'6\x1b\xb1\xaf\xcc\xd2\x83\xe2/\xa5q\xe9\x13Y\x15F\x1eR\x8cDr\x83\xd5PN\xf8\x07\xc2\xc7\xadF\x077\x19\x1auN>\xa9\xf4\xf3\x96\xf9\xa3\x80\xe1\xaf\xa0\xcb\xbb\xbb\x93\x86\xb3\xa8q\xef\xa9<\x0c\x86#\xaf\x8e2KG\xea,\xaa\x0c\x18\xff\xf04\xb0 7fm\xf0+\xdeZ\xf0\x95\xd4\xb5\x12\x12\x0cG\xa1_\xbbq\x07r\x08\xa3fR\x883\x0fy@\xd9\x05 \xdb\\\xf3\x93\xea\x8d\xdc\xfc\xc6\x1f\xd5\x1b\xd4\xfc\x86Q\xca9\xac\x84\x9cR\xf5d\x16*\xbfL\xd2\x19~\x8a\xe0|\x04\xfc\xb8O6\x92x6\x92Y\x97\x1d@/\xcc\xc2\xdc\x97OO\x08r74\x8b\xc2\x8d\xe4?7\xb0\xc5\x80\x1e\x06|(W\xd7k\x08)\xf1T\x97\x11\xc9\x9a\x99\x81\x9a\xd9D\xf0\xd2\xca\x91\xf0\x03\xa2\xb2l\xecE\x10\x0b3F\x0c\x0f\x07\x90<\x80\xd8\xeeF\x07r\x1cK\xde\xc6\x90r\xd1\nv \xe6\xb2\x95\xc5\xad\x0e\xd4b\x0b\xbd\x1e\x0b\x96\xc3\xbdQ\x84\x8a\xbb\xe5pw\xc4\xbf\x8c\x80\x84\xa5\xa6$\x86mh+\xe1\xa0%~\xa9K}\xd6zhU\xfb\x936\xab\x8c\x9et~Df\xfc\x17/\x93q\x85\xac\x90\x15+\xe7\x02\x0c\xc7\xc6\x8f\x81\x93\xa5P\x97r\xfe\xf0_X\x05\xfc\xedmx\x04 \x1c:\x1a\x07?u\xa7\xba\xacjOu]\xc1\x01|F\x07F.\xcaKL\x12\xe8L\x86{\x8d\x93\xa8\xfc\xa8}\xdb\x03M\xb2\xfc\x1ax2\xb5;\xb1*\xca\xa4y\x94\x0b_L\x8eR\x11XQ\x83\xe3M\xfd\x0c\xa3\xd5\xbe\x91\xba\xcf\x0c\x9bx\x19\xd0\xb0?\x8f\x17\xd5\xba\xbb\xda\x05m\xd2\x08Q\x0c\x1d\xa06\x10:Ts\x13b\x1d\xd2\xaf\xff\x81!\xa9-\xd0^t\xb4\xeaD\xd0\xeb\x99|\xcd\xf8\xd5\xeb5=\xf7\xf0;N\xd3\x17\xde*\xab\x85\xfbT1\xf0#/9\x1b\xc1\xa1\xb4 \\:\x7f\x95\x14\"\nfB\xc4\xf3_\xeb\xcf_\xc7\x0b\xa1\xbb\xf2\x1a\xce\xc4=\x1ce=\xae\xf9]\x0d\x14O\xdd\xd4\xaa\xe9\xaf\xf9Acf\xdf\x11\x1cwHe\xbe$\xb0%\xf5\xef\x0c-\xcc%Fm\xd9\x18%\xc1\x82j/\xeem\xa0\xa6\x97N\x08o\xa7V#\x06So\xb8\xb6f \xb8y\xf9f\x10\x868\xa1\x00=\x0f\xf4\xbb\x9bN\x10\xec\x93\xf4\xa7f[f\xc7Q\xd2'\x9f\x97qZ\xa0J\xde\xf4\x02\xd3^\xd8Ro\x07\xcc\x93#?\xf7Z\xf2\xee\xe5\x8d\x03\x11M\xa4\xd9\xb5+\x87\x07\xed&+o\xca\xc7\xda\xcd\xe6\xe7''\xb3\xb8\x98\xb5\x1a\xa8n\x97\xaf\xd4\x1e\xac\xd7B\x7f\xcco.\xe5\xb0\nu\xa3\x907\xc6\xea\xc6\x18=\xa5;\x90\xb2\xe9\xc1!\x0d\xd1\xf8\xdb \x1b\xe5Z\x81\x9e}\xe6\xb6\xf9H\\\xac\x06J\x88})#\x04\x1d\xe6\x8f>9'\xf9*\xe8T\xa8\xa8K\xb1B9\xda\x00\x83P\xec\x82Nv\"\xe3@\x98\x91 CNQ8/\x06\x94\xc3\x15o\xeeb\\\xa1\xed(\x00\xf4\xdf\x97\xfdq.\xc2c\x8f\xa8q\xda\x16\xa8\xe5gc\xee\xbc\xf1\xaaZ@\x0b\xcd\xd1\xd5\xbe\x88m\xda\x0d\xdbB\x90\xb4 \x0exg\x0d\x0f\xf9\xe6\xa5xK\xc7\x12\x10\xa9\x05\x81\x01$f\x08\x1b\xa17\x15\xc10\xc6/\x16 \xb6\x8frE*\xd1\xc7\x14<\xa8_\x1c\x9e\x9c\x13\xdd\xc2\xd8\xb4\x00\x9d\xa43\xfe{\x86<\x01\xe9\x9f\x11\xf4\x8a\\\x85\xfc \xbf\xab\xddB\x1cQ\x185\x95\x1ek\x06\x8a \x885V\xf1q\xaa\x11\x13\xbe\xa8\x0b/\xba7w\xd3\xbd-T4\xea\xf1bsM\x02\xe2\x1c\xbbj\xc0\x8c\x8fB\x9f\xa3\xbc\x1e\x1a\xfa\xa4\x86/\xcb\x1e\xdc\x86\xdd\xd2\x9fE\xfa\xbd\x84\x91zC}\xe8:\xd8\xfeY\x0e\xed\x9ff\xc4\xf9\xa7\xb4\x19tl5\x1b\xb4\xce:\xa0U\x8b\x8c\x11*\x02O_\xa1\x15q9\x0b\x99\x97b\xd5X\n\xad\x0d\xf3j\x9c\x91@\xbaZE\xa0\xe2\xfb\nF\x16\x10\xc3\xfb\x98\x9e\x118]\xc1n/\x8cpo\xe19\xb4\x1b\xd5W \x0d5\xe8[z\x1bv\xc3\x08i\xba\xf6\x02\xc5e\x94K\x18\x9f\x16\xe8z\xc8\xe0\xa1\xe4\xd8\xf8\xdb;T\x99pN\n\x16\xe75\xdd&\xa1\x13M\xb5y\x82C\xc3\xc1\xeaX\xa3\xa3\x07\xfe=&I\x1a\x04\x0cv8\x01\xbe\x0d\x94\x8bV!\x97\xcd7\xc3\x9d_JX\xfeb\xc6\x9d_\xbe\x0cwN\xcd\xbaD\x81/\x9aJ\xe9\xf1i\xc1\xf2x\xcc\x9a\x96 K\xb3'\xc4\xe5fz\xe1|z$\x9f\xea\x0f53\xd6\xf0\x1f#\x15`\x1a\x10\x12\xc1K\x8e\x19z\xdc\xc3\x19\xe9\x0c\x04\x82\x86\x15\x86\x93G\x94\x0f4M\xfb\xf0\x932g\x84\xa3\xb6gc\xa3\xcf\x8dL25\x7fY\xadG\xe9![S-U\x1e\xb2\x03\xc8\x85\x8b\xac\x15W\xa4\x8a\x88\x04t\xc80\xecn\x07=\xba\xb2\x11\n\x7f\xbc\xa3jgf\x1c\x15\xadT;\xf3\x9a\xac\x9fu\xc84Q\xe3\x14Z\x937\xbe\x95\x9956\x9bikJ \xaa7\xbd\\M\xa8/\xf4\xc3CbD\xf9Z\xdf\xb3\xb8p&\x02\x80\xa6\xa5S4\xdd\x08\x93o\xa9\x02\x1a\xbd|\xe9\xc6\x12\x9d\x8a\x9dU\x99\xaa\"\xc9V\xeb;-\x11;-\xe1;-{\x00\x89;\x16:\xe6\xdf\xe3bf\xb0\x03 \x1c@b\xd1\xf35vf<\x8a n\xee\xc6\xc4\xa8\xb4\xb5\n\xa3\x89\x17\xc8\xae\xb3=%\xb8\xac\xfbS\x03\xa1uw\xe6\x9d{8\xb9\x89=\xbc\xd9*(\xc8\xa1\xa65\xfb\xf7\xed\xf9\x98\xef\xf9\xd8o\x8fk\x8b8\x9cU\x87\x1c\x95\x87\x1c5\xee\x8b\xd2[\xc5c\xad\x91\xf7\x0dk\xbb\xb2&4iB\x86\x85{V\xd8\xf2SP7\xcb\x86v\x94\xb1\xe8$\x9e\x04\xd4\"\x83\x96\xbb8{\x00[\x01F\x9cKyT\x08\xa4\x18\x8b\xb7'\xb4\x10A&d\xe2\x08\xf2\xedm\xb9\xab\x1e\xd8\xa5\x91\xbc s#L+}\xf5\x8d\x025\xcb7\x86\xaaE\x9d\xf3D\xd7\x12\x8b\xed\xf2\xbd\xa5Y\xcb\nl\xbe\xd5\x98\xb6\x0e\x1dZ\x0e\\$\xe1\x8c\x8e{@,\x8dX(\xaf\x8d\x10\xe4\x12\xe5\xf3\xff\x02\x94\xaf\x0e\x15\xfd\x14)C\x08D\xca\xa2\xb6\x83\x80~\xa0\x94\xc6\xa8\x07\x1e\xcc[6LF\x11'T\xadC\xc226\xbeK\xa8\xa6%\x12\xbb\xe4A\x17\xdd\xa4.m\x12\x9a\xd8\x86\xc9H\x84C\x96c\x8b\xeb\x03;\xcdI\xfc\xa9\xbd\xa06lk\x1d[\xc6\xe5\xfd\x8f\xed\xbe\xc6\xc2Z \x9ai\xb1\x8d/\xdf\x08\xab\x8a+\x01\x8f\xaac\xb5Ka\xd8\xbdQA\xc1\x0d\x11\xa5\x02\x9eC\xb1(\x82\xf2\xe4\x1e6\xbe\xe6\xb4.+\xf67\x1f\xfa3\xbcsI\x03\xe6\xe4\xfa.v\x0dA\x1b\x0e\xa1\xf7\x9e,H\xcc`8\xea\xc1A\xf5\x0b\xbd \x98\xa6\x16\xda\x86^u\x0f\xbf\xe5wX2'\x05\xb4\x9d\x8e\xe7\xd7g\xcaML\xb8\x18\x82\x81\x01\xaf\xf5\x93\xd0q\xba\x9c\x10o.|Ft\xc5W;*\xab\xd1<\xa6,\xf0\x99Hm\xffpPYQ^\x8b\xd9\x13S\x85\x03\xa5\xad\xab\x8d\xec\x83\xb0\x13\xc3\x8e\x08\xa6k2\n\xcd\x91\xe6\xe4\x9c\xe4\xc5&n\xda\x1dp\x9d\x90\xcb\xb7\xd3\xeb\x83\x15\x0eQc\xb8\xb3\xe7\xec&\x8d\x0b\xf6\xfc\x06\xba\xaa0\xb4\xb3\xcb\xeb\x0bS*UT\xb9\xc4\x98+\xcaJ\xb0\xca\x03\xa36\\\xda<\xd1\xa8S A\xbd\xe6\xb2\xb9\x94\xb3\x11\xab\xba\x19\xb1Vl&<\x04\xaa(N\xc5\x02Q \x89\xd0\x98\xf0F]7\"~xP\xd8\x1a4\xa5\x91\xd2\x13\x0fI]\xf5\x0e\x87m\xcc\xd4\xa6z\xde\xb6\xf7s\xfa\xbe\x92\xf4}u\xc3\xf4\x1dU\xc6\x8a\xbc\x8b\x1f\x1au\x17\xda\xddm\xe8\xf5\xfb\xfd\xea.\xa1\x13\xd8\x86@\x08\x15\xeaE\xb2\xe0\xed\xc1\xe9\xaa\xf69Y\xf0\x86{!\x9e\x07\xed\x93`u\xb3'\x81\x1an\xa5\x8b\x84\xaf\xebCi\x9d\x11\xabk\x9d\x11\x8as\x08\x08\xec\xe8}\x87p[\xeb\xcf\xba?0@zW\x18\xe452!n\xf05B\x9d\xf84\xcd\x0c\xb6\x87\xc6\x90\xbd\xcf\x9d\xc6\xa1Rv\xaa\x1d.\xe8R \x02\xb2\xcb\xa7\x91\xb0\x15\xe0\x19S\xdd\x0d\xe1\xe1\xa0\xf4-]\x91`7\x82\xddP\x1eO+\x89\xdcg\x84\x05\xbaU@\x99\x0c\xf8}f\xb8\x8f k\x9f]\xab\xeb\x1c6\xe7eTemy,\xf6-\xf8\xbf:\x92\x0c\x06|.vi@d\x17p\xaf3\x94\xf6D\xb5\xd0\xb5\xf3 4\x13mp\x89\x03\xed\xc3j\xf5\x85\xe7#\x0eGB\xd4@sV7s\x16V\xd8\x8dz\xc3J$\xe0\x90\x93\xf2`k\x03S\xf8\x1a\xf3\xe0iw\xeb*G\xeaT9\xd6%\xc4\x08\x12\xa3\x06\xd1\xbcl\x19l\x8b\x11\xed\xf0\x01\xe4\xfe\x0b\xd4\x92\xd7\x8c\x00\xdc\xfc\x00\xae\x80g\x1co\x03\xa0\x969\xf9\x02\xd9\x0c\xce\x9b8\xec\x95 \x9d9\xd5!\x0d\xe8\xf3E\x7f\x84\x16\xc9\xbf\x98\x03P\xca\x17\x94\xd7c\x1f\x91kuC\x0c\xc1\x8a4\x16F\xf8}\xc8\x1fe\xb8\x1d\x9aU\xc5\x13\xfegy_\x92,\xf9 \x9eq\xe7ed\x91\x81\x8f8%*\x9d\xd3 \x89\xe0\x94\xe0\x9f\x17\xd5\x9fG\xea\xcfSRF\xf4\x887\xb5@\x1e\xf1\xbe\x0c\xf29jH0|\xa1/\x89-\xbb\x04\x9el\xc9|\x89 &v\xf6\xab\xd3\x8e\xdf\x0b\xaa$,\x11\xec\x87*\x7f\x06\xbe~\xe0\xbfk\xee\xdf\xbbw\xe7\x1e\xdc\xe2\xe7\xd9\x9a\x13s\xfb\xc6)\xdfd\xe2M;\x92\xe3^\xd9F\xb7\xbbG\x8f\x1e\xc1\xde\xfdP\xde\xe1O\x02V\xde|\xf8\x10\xf6\xee\x8b\xdc3!\xac\x9b\xce\xf8\xb6P\xa6\xe3._Il\x1en\xc1\xde\xee7w\xbe\xb9\xbb\xf7\xed\xfe]X\xc3\x9d\xfd\xfd\xbd\xfd\xfd{w\xbf\xe1O\xfc\x9c2\x9fZ:\xd2)&\xac\xd7\x8e\xe0\xeb\x92\x86Z4\xd5\xdd>\x8f\xaa\xa3\xb6\x07\xa3\xbb\xe3\xae\x9e\xb7\x9a#4Px\xc5\x18\xa8qY\xe6P\xa5=\x18\xd8}\xce\x12\xf4)\xdc\x92C\x15\x0e;\xc2\xa7\xc21P\xd0\xf0t\x17\xd66\xe7(q\xec\x8d\xe0\xbd\x80\xf5\x1b\x993\x83`:\x1cxF0\xf1\x19>\xe7T\x1c\x1b\xe7K}\x9d,\x0bp :\xdb\x08\xc7gq1{\x9aM\x88\x06\x19u\xcb\xa4\\\xc4\x96\xaa\x90-\x1d\xa4\x9e \xb43\x9e\x1f\x9a\xbe\xaa\x08\xbfw\xc2c\x8d\x84a\x97\x1a3\xa9\x9c\x0b\xcb\xaf\xc9\xf09\x19y}\xb9\xf5\xd6:n\xb05\xceOS\xb4q?/\x8e\xaaT\xd8\xe8\x0egz\xe25\x16[g\xdd\xe0\xd5\xbf\x96\xa3\xa0\xd9\x84|X-\xf8\x96\xdb\x0d\xa1\xb8H\xd8x\x06Au\xbf\xab)~\x8d\xe3\x82\xc0\xdeA\xe7{\xa0\xd1\xfe\xfe\x92&\x9f\x97\xe4\xf93\xfb\x1c\xd5\x85\xcd\x7f\xb7a\xf3\x93l\x8c\x01\xc3G)\xe1\xff\x88\xc96n\x96cp6mVj\x83\xdcR\xdaj\x19\xdf3\x7f\xcd\x97k{\xfb5\x89\xf4\xa3\xef\x16\xbc\x16{\xff5\xee}G\x88\xc8\x07\x12r\xac/\xa4,z=G\xd7\x06\n=V6\xd5\x01\xfe@\x97\xe7\xa6\xc7`\xefMFw\xc8%#\xb4H\xaa@\xc2\x02\xe2\x9c`\x92\xe38M\xb3\x0b2\x81\xb8\x80OdU\xf4\x9b\x89\xb3\x9b\xdd\xf3\x0de-n\xf1\xdc\x98\xc3X\xbf|\xd2\x11\xab\xab\xbb*\x86~iI\x8c;\xde\x94|\xbay\xf1\x01\xcc~\xb1\xea\xc2\x15j\xac\xc3\xa6$C\xb2\xc9Z$\x89\xc6\xc1\x9b>\x08\xad\x0d\xb9\xd5m\xfa\xa5\xcb\xda\xfe=\xf7\xe3\xc5\"]I6\xde\x12\xd1\xaf_W\x91\x83L\xf23\xb0\x03\xb2\xddD\xb0\xe6\x94^\x91\xbc\x16\xde\x7f\xa4\x08!\x96AA\x18\xc4@\xf9>\xa8 \xa7\xc6\x08\x19\x95{\xc2\x89\xfa\xfc*\xe7`\x9f\xfd\x06\xf4\xc4y\xeaot\xda+\xe5kI\xd68\xc3\xa0e\xb41\xe6\x03h@\xeb'4]\xf1&\x85\xd6\x14\xd5\xa4c\xe1\xd4{J\x80s\x0fd\xd2\xf7\xf4\"\xfdd\xe1\xedKu\x0c\x13\x8c\x92f\xa1 \xf5b\x16\xfc\x85;{\xf0\xb5HU\xd8\x1f\xcf\xe2\x9c3/\x8fY@Q\x98\xb1\x8aG\xc7\xa4\xed#\xad\xff\xe2\xbd?&U\xc6\x84\xa48*ic\x9bj\xbc\xf5\xdaa,_9\xf0V\xa9;\x8d4\xf3\xcf\xab\x08z\x7f\xefE\x82]\xb4\xea\x04\xc6\xb18\xe2]{\\\xf6cs\xf57\xa0Y\xd8\x16\x97\xdf\x91\x08>XE\xe6\x9fI\xfc\xe9u\xdc\xd02\n\x06/xGd\xe6\x02\xf9\x92\xa1qqF\xb6\xa1\xfc\x1c;<9I\xe6\xf3%\x92p\x8em''\x8d\x14\xed\x1d)\"\x03lE\xfc\x0e\x9e\x93&\xd2\xf3\xfe\x7f\xe7o\xec\xdd7$\xa6\xe4\x0f\xf6\xef\x192\x1f\xbf\xb7\x0cY\xb2\xf86)\xfa\x95e\x03\x9c\x91@\xc4f\xa1tV\xb9\xcd/H>\xcd\xf2\xb9P\x7f\xc7\xa2\x8d\x8b\x84\xcd \xa6\x90\xd0iB\x13F\xa0H\xfe \xbe;\xf0\xa3[\x8cw&\x0d\xfbE$\x0d\xfb\x8cMp\xfeb\x1c\x94\xf9\xd3\xf9\xb3>\x1f\xd9\xeb%\x8byO\x85\x16\xd6\xd2\xa5\xab\xce\xad\xe9\xed^\x91\x80*-?\xedO\xb3\xfc(\x1e\xcfj\xf1V\xc6@\x06u)R\x8a\xdc\x15m\xa9\x9b\xd4e\x8a\x82\xf6\x03\xe7g\xef\\ \x7f\x90\x8el\xe6\x1fI\x04'|\x9e\x1f\x89G2\x9d\xd2| B\x8a\xcb\x038r\xa9\x88\\\x8bd%!\x1d\x15\x86`{\x00\xfb]\xa2\x14\xda\x85\xe1Q\x95@\xc6p,\xbfN\x8a\"\xa1g\x82 \xc3^?\x91\x95\xc8f\xc1\x86\xd4\x94fR]\x82y\xe6/E\xfcU\xde\x97-\xdc\xbds\x9d\x11\xfc\xd76_\n\x85\xa7\x96\x01\xeau\xbc\xb0\xa6<\xfb\xf8\x85\x96\xc5\x93<\xcb*\x959\xff\x81\xa2s\x19K#\xf26\x85&\x93b\xad\xebb\xa3\xae\xff\xa1'\x85r\xcf\xa9 \xec9\xdd\xa0i\x9c\xc8r1\x89\x19y\x8e/\xaf\x0c\xd5\x0cm\xdfn\xba\xb29\x99g\xe7\xa4S\xd26\xccz\xe5nxBR\xc2'\xe0\xdbtk\xd6\xbeS^m:e\xd1IsA\xdc\x89\xa3\x85\x08Y\x92\x17\xa5G;\x94\xae \xa12\xce\x94\x13\x18\x92\x91l\xd4c,m\xf4\xb0\x8c\x06\x83]\xd1)R\xc6b\n\x14w\xf8\xc8\x96$\xda'\x91\xc4\xb9\x8c\x03\x15\xa6\x8d\x95]'\x1aw\xfa\xe2qr\x17K?<;Q<\x97)c\x12YM\xcbb\xd6RW\x01\x03\xc8\x82\xa5\x83\x06\xca\xe5*p\x02K\xe9\xac\xdb\x8e!\x03\xab\xd4qF\x82\x04cH\xd0p\xc3\xf7n\x04\xbd\x84\x9e\xc7i2\xe1\x94\xf8]\xccf69\x88\xcf&\x85\x01\xc4.\x0fT\xfe\xd2XNy\xc5\xa7\x8c\xd4*\xe5\xfb\xc9\xfe\x01?\x07I0\xae\x16\xd0\xa9(\x9d\xe2\xec\xc7r\xf6\xe2\xd7\x8a\xff\x92\xbb=H9\xbe\x06I\xc5\xcb\xb0\x10\xcf\x8e4\x82\xa9\x81\x07\x90{\x9eR\xd4\xe9Z\"\x1ee\xdfy\xd9\x9b\xe4\x9aZu\xd0\x1a;`\x9c\x92\xd8Y\x94Hk\xbc\xed\x16\xc3\x84?\x84Ym\xc0:\xea\x8d\xb3\xee\xf6k2P\xe7\x04J\x8b,_\xa9\xb8x-t\x11&\x06@\x8e\x86 b\xb1\xfeE\\<\x16\xf44@\x1f\xb6\xfe\xc9 \xa1\xc52'o9\xbd\x0e\xea\xc4[\xb1R\xce\x81\x97\xbd{\xee\xc1\xd6\xf9P?7\xf4\xd1pQ\xec\xd2\x0d\xb6\xb8x\xae41\x9b\xf5\xaf\xf7\xd3\xb12%\xc86\xebA\x9e[\xce\xb67spR\x1a\x11r\x01/\xfde\x9e\x8d\xbc\xd0\xbe\xd4\x89Y;\xdcKo\x1b\x94\x03\xdb\x99E:\x88\x08\xba3\x93\x80a\x82\x19\x86\x19eL6\xf7H\x94}\xea\x80\x80\xb6\xda\x9d{K\xed\x98\x8a\xc11`+?\xd2\xfeI*\xd6Fgk\xa2*\xaf\x03\xb24\xc8\xe15\x1a\xd2r?\xe8\x0c\xce\x9edp\x0c\xd3I\n.\xb9\x0f\xe0\xb3\xc1s\xe8{\x12\x01\xb2W\x8dd\xc0\xaf\x1f\xbf\xb3TO{\xc2\xdf\xd6\x81dS\x0f\xfedO\xfc\x81\xc3oOH&*j\x19\x1f\xac5>\x9c @,\x9d\x9c&l\x8e\xe0PN\xb14\x13.\xc8\xd4\xab\xcf\x9f\xaf\xd3\xe78[Rv\xed._\\\xa7\xcbOd\xf5\xa3`\x8aY\x0b\xba~\xdd\xfezs\xdd\xae\xbc;}\xd9\xdd\xe9 \x13\xa5FK\xa7\xe6*\xc2\x86V\xbe\xcd\xf1\xf8\x93H\xd3\xa9(\xcaW$\x90\xbf\xfc\xb4\xa1?t\xa6x\x14\x15\x90D\xc6\xaaVRJ[\xb3_u6k\xa6m\x1ce\xac\xe5o\xd1\xab\xf8\xc0\xe6\x8eyr\xb2\xc8\xc9\xb9\xc9\x14\xec\x97\x85\xe5\x9f\xbeIQ\xeb\xc5_\x9f8\xf2\xf6fJ\xaa#\x11d\xa5H\xc7\xf0\x87F\xe9\xa8\xb8!\xa5\xbb\\\xfc\xaa\x13\xbd\xcck\n\xbf8\x93R\x7f\x8fz\xed\xe0{>\xa0\x7f\x92`\xd73\xff\xdd?\x9c\xb8z.k\x92\x9b\x8d\x9c\n\x15-\xab\xadt8\x17\xc1\xa9\xc5\x9d\x12d~\xd8\x8b\xe0\xc4\xa1\xbc\xc1\x04pL\xf5\x86\x91/\n\xbc\x11h\xcaU\xb1\xb8I\x04q\x18\xc1\x96T}T~U\xe6\x0eD\x1e\\\x19~\x18$\xb2P\xd7!\xe7\x02\xa4\xf6`g\x0fK~\x1d4\xab\xc9\xf1\xeb\xcae\n\x17zvl\xc6g\x14{U\xf9\xc6\x9fp\x9bW\x93\x1cZ\xa1'\x8a\x8f\x19\x1f\x9b\x82@m\xc8C\xea*\x8b\xb2>c\x16\x95\xd4\x07Q\x97\xb4\xd5\x14\xa4\xa5\xa3@O\xb8\\p\x08\x19\xee6\x93\xbe\xc2\x82\x8f\xd2\xe9\xa6\xd4/\x89\x05\x8d`\xe9\xe4U\xb8D%$\xb6\xc0\xf8\xe9\x01GD\xb9\x9e\x84\xf3#G\xc12\x8c\xe0(\x881\xeb\xc3\x05?'D\x0e\xd7!\xff\xcc7\x9d;cn\x1e\xaa\x95\xa8\xf4W\xe1\xf6\xd9\xba\xff\xc2\xcf\x13\x976\x80c\xea[l\xcc\xf2\x08\x1b\x0c\xf8\x02h\xac\xf3\x8br\xa6\xb2\xbaP\x04\x99\xc9\x96\x83\xbbW$\xde\x0e\xaa$_U\xcb\x07\xda\xdf\x8f\x1e=\xe2\xf4\xe3\x16\x9c\x99\xf7\xf9\xb2\xde\x08\xba\xe9k\x1fY),\x1f\xef\x8f8^\xaci\x1b\xc3Z\xfc\xb1\xc4qI\xbd\xea\xb0\x82\nl\xc3\xb9\x84\xccH\xe8\x15\x07\xf5\xd5\xcdB\xfe\xe5C\xf1\x1d\xe1+\x0d\x070L\" \xbeK\x9e3\x17\xbd\xac\x12k`\xf5\x82Z\x86\x02Z\x9a\xe8:\x12\xdfph\xd1a2\xb2\xd3\xcc\x02M\xb46\xeds\x1c,\xd1-:\xe0\xaf\x15\xf5\x8c\xc6>~ \xd3V4\xa1\xba\xae\xc2\x90\x1f_\x8be1\x0b\x0c\x9eEV\xf2\x12+\xa0e~@\xce\x9c@.w=zmUj\x95[\xb7\x00\xb3\xb0\xd6\xd4+\"'c\x99\xd8Wl\x7f?\xce\x12\xc1S\x82\xc9h\x87\xbc\xa3QX\xe3\xc8\x98\x0fG\xa6.\xe5l\xc0\x86\xb6\x04x\xea\xca\x10\xab%\xf9'5\x115FEKl\xad\xfe\x01F.J]\n\xd9\xcd\xb4\x99wU8\x8d\xf2|\n\x0b\x90\xd1a\x9a\x82W\xc9\x99\xd6\x8e\xb9d\xb7\xe0\xb8\x85\x14\xa9\xe8\xb2\xf9\x1f\"\x7f\x9dJ\xdb\xff\x0e\xec\xc1!L\xfa\x8bLT\x82\x98\x0cSN\x8dZ7\x86|\xe4\x9c\x1f\x9f\x08\x06S\xfc\x0e#\xec9hh\xff&\x95)\\ \xcc\x11L\xbaX\xd2\xab\x08~\xbc693F\x97!vY6+\n\xf5\\\\ \x82z\xfdp\x11\xf9IP\xf6\xb1hF\x12EC\x84\xa6\xd7J\xd8x\xc3\\\xce\xb9%\xb8\xbb24\x1b\x95\xb3\xc3%\x13\x8f03\xf2H\xc4q \x19\x89\x99\xd8\x89&x\xaeM\x17k\x99\xa1U\x02\xe8\xa7$\xc8m\xa0\xd2\x04D&Y\x1e\x8a@b\x0e\xa9\xb2P\xf0]\x9a\x9f\xa7u\x18\x9a_\x1acL\xe5\xd6\x00\x82\x14n\x81 \xb5\x91\xae!\xa1\xce\x1a\xca\x1c3AUtz\xc9D\x93\x08|s\xe7\x0b5B\\.\xf3;|\xef\x8d\xe1\x10\x16\xc3\xe9\x08\xdc!\xeb3\xa1(\x9b\x08\x0b\x8cX\xe8\xfaZ\x99g'\xd4\x04\x13\x8f\x83B\xc0\x01E\x97\x85F\xde\xc7N\xf2\xeep\xf3\xaaU\xfc\x92\x0c\x01\xdf\xcf\xa2\xde\xcc<\x8c\x103v\x1fHV\x9f>\x80%\xa6\xf9\xe1\xb81\x80\xbd\x10\xe2\xe1r\x84hp\x0b5\x0bl\x98lo\x8f\x1c5\xeb@\x13J\x87\xf9H\xa8\xb8\x84/|\x80 \x05\xb7\xb1\xda\x98\x81\x90\xf0\xc7\x8b\x08\xd2\x08\x96\x11\xcc,\x90\x94\xe79\xff\xbf\x08S/\xa1\xc4\xe5?\x16,\x86{\xf0/\x98j\x9c\x8b\xba\xe3h\x0f?\xde357\xab\xda\x99\x99\x11\xf1tSr\x7f\"\xd1m\x86\x14\xfc\x00R\xf8\x17\x92\xfd\x14\xd6`\xc1\xd0\x0b\xed\x93\x82\x05\x8b\x08\xa6\x11\xcc\"8\x0d\x9b\x01\xf8\x1d\xe2\xc7yY\xed\xa3\xf2\x80\xb0\x1f\xb5B\xbdZ\xa6\xbf\xc9\xb5\x08Z!\xc5P\x80O\xb9\xa7\x1eb\x99=Q\xf3\xacslz\x97\x88\xf6\xf5\x0e\xdd*\x8d\xa4\xfa\xcc1\x06\xb7\xa2#\xe9\x92\x16\xf0%\xb5L5\x00\xa8\xbbn\x19\xa2\x81_0\x80\xafH\x90X\xed\xe7\xe0\x14\x17\xc6\x19e \xdd\xa8\xf8C\xbb\x7f\xedW_\xf8\xccv\xecj\xa8\xb6\xa7mct\xe6J\xb5\xe6Im\x10\x90:0\xf9*\xa7|\x06s\xb8\x0dw\xdb-\x8f\xd5\xb3\xfd\xf6\xb3i\xf9\x9d\xcds\x7fa\xf1\x188\x97\xb1CG\xc6\x80a\xe4\x9b\xbb\xf3XZ\xe4\xea \xe6\xc9+\xa9\x9d\x99/\xa4\x18:\xec\xaa\xe7D\xdd5\x1e\xc4`r\xa9\x03\n^\x89\xe3:\x87G\"kt\x0e\x0fa\x0e\x87p\x81\x99\x07\xf2\x08U\x0c\x18g\x8a\x85 X@\xfb,\x13\xf2w\x88ei\xd9\xc6n1\xe8'r\x9c\xfc!z6\xa4\x01\xe9\xd2\xf4\x96\x9a\xda\x0e\x7f\x13\x93\x17\x89\x9f\xa7\xc5\xc4\xed0\xa2\xe5\x01\x99\xb1\x8e< \x0b\x16\xc1\x05\xe1l2\xf3\xc8\x03\xa2 \x1f\x81=\xc6r\xc1\xb4#\xeeKsZ\xbcJ\n\x06\xc3^\x04\xbdQ;\xa9E\xad'\xcf\xa4\x16\x89\xaa\x15_%\xc5\x0f\xcb\xac\xe4\xa4\x9e\x95\xdcq\x9ar\x01\xb6d-1I3\x8e<\xcb\x93\xb3\xc4\xe6\xd9\xa6d.\xde\x13\xed\x8b2\xa1\x04n\xc1\x99!\x14\xd2\n '\x0c6\xcb\xae\xe1k\xbf@\x901\x04\x99d\xabjU\xf3\x1dE\xa00\xb1\x7f\xe5\xc4\xc6\xe0\xa1\x96\x0dvs\x975\xc0c\xe1!\xec\xc2!|\x92\x19\x0cq\x9b\xed\xca\x08SqsW\xa8\x1f\xf7\xc43f\x8c.\x03\xb0'\xd8c\xe8\xfb\xa4\x16\xd3\xfcNe\xcf9aq\x92\xba\x19*\xe5\xdeo})q\x06\n \x14\xdfb\x94\xc08^\xc4\xe3\x84\xad\x84A|\x00\x97Xo\xbb\x195 \xe4A\x14\xb12\xf1R\xd6x\x89\xf4ORrN\xd2\xea]\xfb\"n%~\xe1\x06\x89\x08\x9b\xa8BL\xcbuV^\xf6b\x14\x1c^\x9b\xb8\xdc;7\xd3\x05\x82E\xac\x14~\xad \xa4\xcf13z\x17^\xb9\xe2,k\xdbj\xb3\xf4-H \xcaJ\x1c\x9aU\x03 \xcb,\x992T\\h2\xaf\xcah\xaf^R\xba\x0d\xf1p\x91&c\xe4\xdb\xf6lQ\xbb\xb5\xc1&\xb4 \xf9&d\xa0\xd1\xcbn'8\xfe\x0d\xc9$tjZ\xfeTK\xab'\x9b\xc0\x15\xe6\xf8\xd3\xc8>!%%\x81j\xd7NE\xc1\x19)'(\x16\xcbb\xd6\x05 %\xbcU\x11\xfa\x96]\xae\xc1\xc9\xca \xe1\x1b\x16\xbai%\xe0\x9f\x90\x11\x91dQ\xd9R-;\xbe\xe6\x16\xbc\x8b2\xbb\x96\x16\x11%w*\xe8*l\xe3\x1e\x1e\xe6^%\xd9\xea`\xcb|\xf3:|R\x87\xecn\x04;{\xeeV\x97\x14wWW\xcb\xad\xf5\xb8\x16\xb0\xad\xa1a\x9f\xf0\xc8\xd9\xf1\x05\xb3#\xfbd\x99HnH7\x07\xb1\x17(\x9a@\xee\x00\xf0&\x89W\x1e\xfb'^i\xf7\xe1\x95\x90\xa3\xd9\x91o\xe2\x95vw\x1b\xe4\x19y\xec\x97g\xc4\xdc\x87\xd7\xb4\xce\xaf\x93\xd7\xe3qg\x9e\x91&\x9fx,\x08\xad\xd7\x89\xa6o\xc2v\x11\x8dz\xcb\xbe\xf5\x97\xce\xbf\xa8\xee_9\"Y\xe2\xaf\xac\xfa\xe7\x1e\xddfI\x19\xca\xedi\x17gOJ\xe4\xb3\xaf\xcd\x06\x05a0\x14\xb1\xabB.\x9e\xa8\xa7\xec\xdfW\x04\x86b\xd1\xd6\x8d)\xd0F\xd9)\x9aur\xa5\xfe\xd8 _\xbc\x02\xa1s@\xa1\x04\xc1\xa2\xd7w\xa6\xd7\xad\xec\xdc\x98\xc8_\x92d\xe2\x82\x05:\x9b\x135\xb8\x9c\x1a\x87\xa3s7\x91\xc6\xdcl\x94\x90\xc2\xb4\\I\x81\x12\xf6\x00&\xac\xad\xc1\x9a\xb1v\xe2\x89W\xcf\x8f?X2O\x9c\xa3\x05]\x83\x9cM\x7f5gV<\xc0\xb1\xa3h\xac%-\xa8f\xd2\x8cn\xd3\x7f\x9d\xb3\xe1\x8c\xa9`\x90sV\x05\x83\x9c\xb32\x18\xe4\x9c\x95\x89\"\x9f\xc8\x9c\x91\xda\xbbx\xbf|[\xbd\xa5~\xe1\x8b\xa5\xfd\xed\x89\xb2\xc5i\xb7\xd5\x17\xea\x17>\xaaR{=)\xf3|U\x0f\xcadOOj\xd9\x9f\xf0\x85f\xe2\xa0'\x0d\x89\x19_\xd2\x93\xf4<\xd1r\xf6\xc8\x87z\x0e\x9d'\xb5\xa4:\xa2\x0b=\x03\xce\x13=#N\x04\xf3\xb6\x08\xf4\x84L\xb3\xdcd}\xb4iZh\xe9\xd0\x84\xde\xcc\x0c#\xdb\xca\x8d\x81\xeb\\\x86^hL\x97Y\xbb\x88\xfaC\xe1\x13e\x0e\xad\x15\x0e\x80\x8f\\\xadK=\xe1p\xc4O2s7\x99\xf4\xbb\x10\xaaHs/LT\xbd\xb0S\xf2\x18\xf4Q\x0c]\x06,,R\x1fs\xba\x15\xd7\xc0\x8c\xb0\x85\x1d\xd4q\x86!\x8e\x06\xdfJj\xa0jSe\xe3\x80\x85\x95,\xf3\x80\xf2\x12\x06p\\\xe5\xce2\xcf\x7f+1\xabTj\x8e\x13\xbb\x0f\xa0\x10.\xa6\x05\xfaIJX\x14\xa3R\xfc\xb2\x12\xe4\x0c\xddD\x96%\xf48\x8d\x0f#X6)\x98\x01G\x1fO\x19i\x1d\xef\x9d(\x1a\xd4q\x14\x83\x8c\xbf\x00S\xa5\xf5\x13\x85\xfa\x0e\x84\xcd\xdc\x08k\xee\xc4\x0b\x07\x93:\x0e\xda,J\x88\x839&\xcb\xe4\xd8\xa5\x83\xd1\x80\x82\xf8Rf\x86\x0c\x1a\xbf6DN\xb5Y\x9c('\x9b\x8ceoRY\x91\xa1\x92/\x92~mq9M\xceD\x85\x11\xc4udi\x1fog,\x82\x15\x8b8\xd3\xe0J\xa3~b?\xad*^]\x1d\xe2F\x08KEay\xb2\x1b_\xc2\x04-,\xc8\x1dQ3Ryf\x87O-\x91\x88d\x1cv\xc3\xc6\xc4\xa0\x16\xf7\xcc\xe7\xb6\x8c\xc0jc\xad\xe9q\x96\xb5rV\x16O\x13u)b\x12K\xff\xa5C\x85`\xe2x?PQ\xee\xf8\xd3\xce\xa3\x82\xf4K\x89e\xe5\xc3]\xf4\x8c\xdd\x81\xd8\xfd \xaa\x18\xf9k\x16\xbe\x11_y\x04s\xc4\x1d\xfe\xf2\xdca\x0f\x95@\xe8\xe4\xe1\xd5\x95\xa0\xe3,\x9fvZ\xee\x87SG\xd1\x11\xd0\xd4\x12X\xedq'\x85\x03N5\xdd\x9f\xc8\x96\xd1\xb3k9$\xe6\\)`\xdcvx\x97/a\xd1t\xcb\xcfPs\xdc\xb1\xac\xc2\xa9\xd5\x7f\x01S$/\xf5\x05L\xe0\xd1#\xc8\xdc\xdf\x8d1\x00f\x9b\x1f\xeb\xea\x03\xc72\x8d\xcb\x05\x1d\xdf\xf0\x82\xe2\xb9\xf6\xc0\xea`\xa1_|\xed\x8d\x19]L\x97Z\xf4\xa5M\xe8k^\x89,\xb2\xc7E\x9d.\x85|\xf3ZJUh\xe7\xcbv;\xbe\xba\xf80\xd2\x86/a\x17\x82\x83.\xf5#\x92\x8f\xe1\x00\xd2.$\x079\xf2X\xb8\xa2\x17\x98y?\x13\x87R\xc2Q\x83\xf2S;\x0b\xedn \xe0\x9c\x92co ]l=\xf6K(qaL\xf6c;D\x96\xad\xec\\\xe7\x0e\x8d\xc2\xb2T\x93\xc3\x0e\x17\x92\x96\x9a\xaa\\\xfc\xd4T\xe5\x0co(=9\xc5_U\xd6\xa3e\xa9$\xcf\xf0\x87&5&\xe2\x86\xd4\x97\xc7\xe2W=\xb9\xd7\xd2\x0b\x14G\xcc\xa5Q;c\x18\x06}\xc6\x07$\xec\xfa\\|\xf34\x85_\xb6\xa1l\x03q,\xfc\xf1er\x1ewL\x05\x11N\xf3\x0f\x15qS\x8a\xd9\xd6\x07\xc8\x0b#^j\xbe\x14\x99kc\n\x96\xb3\x83sK\x1b\xc4u\xb8td\xcc\x19\x0b\x13\x9f\xb4\xe5\x89\x8d\xa1`\xe1\xd4$\x8d\xc5 \xa5\xf2F\x05\x92\x0d\x136\xde\xb2c\x18\xc0\xd8\x1c6h[\xd1\xa2>\xf2\xf2\xf8'\x95[\xa6\xdeUT\x83\x9d\x80<\n;-\xde\x12\x0e\xcb\x9b\xcaD\x16\xeb\xe3l\xc7 \xd8\xf0\xe6\xd8\xce\xd3\x95j6\xf4\x07(c\xf0\x88\xe6\x99J\xa4\x07\xea\x9c\x05\"?\x97dK\x91+\xe5\xa3\xe2\xe2\xa5g\x1a\xc3\xa7\xf6\x91\x94\x16\xf4\x86\xedW\xb7\xac\x9a\xf9A\xf1\xe5C!\xd0(V\x10\xb6\xe1\xdc\x86t5sD\xc9DJ\xbe\x15\xbf~ \xfc\x16\xd0\x15\x07\x0b\xab\x0eJ\x1f\x06\x11\xaa\x95\xa3'\x03\xffhg\x00\xe7N\xc4\xeb*\xf3n\xad\xe8\xe5L\xd2\xa3\x05\xbd\xa8\xa83Q\xeeX\x7f\xa2\xe2\x0f,\xe5\x8d5\xb3\xbe\x9en\x07\xf33\xd8\xd9\xf6\x0e\xf6?\xf1a\xff1\xc6\x03\xb6m\xc5\x19\x96\xa5\xcc\x8c\xd8H\x91\x9b>@\xb3\xd1.\xfe\xbd\x8d!c\xbc\x05\x83\xc7\x02\xc7\x87\xb8\xb9\xbf\x92.2\x15s\xdc[j\xd8\x86\x86_\x13\xa7R\x13\xfb+\xd1#\xd5\x91i\xac\x82N\xb7a\xccG\xfd \xc4\xe7r\x1fa\xf5\xac\xb4\xbe\xe3\x0fa\xa8\x8cG\xe9H\xee*.\xd8\x8da[e\x1f(\xf8\x9f\xe7\x86\x11\x8d\x85L\xc8\x1f\x8f#QF}\xcc\x0f\x00\xf1o\x82\xff\xba&2\x15\xd2X\x82\x11\x04\xf8\xe72|\x00\x0b\x0e\x11\xec\xb9\xe0\xbb\xc9k\n\xb5\xa1\x8b\xf1\x9a\xf1n\xd2\xe5N2\xc3 \x8a\x87\x18#!\xc8\xc6RH\xdc\x07|`x[Soat\xe3\xc4\xbc\xb2X0]|s\xeb\x16\xc6\x01\xa3h6i\xa8 :h\xc5\x1c#X\x90\x90\xa7bz\x9c\xdf(\x1e\xc0\n\x1e\xc19\xff\x87S\x82.Y\xe2\x14\x060E\n\xb22+I\xd4\xc5\xbb\x9bK\x92s:\x12\xfdV\xbf\xad \xa4\xcc\xfc\x9d\xfaP\xf4|\x8e\xb4\x0b\x060\xe9\xa0L\xa0\x18|\x05\xb2\x80/\n\xc6\xac\xcfj\x8a\x93\x1c\xd9\x98e\x88g\xdd\xa3\x01,B\x8898\x16\xb8h\xf8o!\xdc\x16*\x07\x85VSR\x0f(\xda2\x85O\x96\xee\xc8\\8\xce8\xa5B\xfcp\xae\x9c\xdc\x87\xa9S\x98\xe1\x0bs\"\x84\xeeG\x8f\xf8\x81\xeeZ\x18>\x80\x13\xa4\xae\x8b\xea\xf5\x10Ns\x12\x7f\xb2\x7fu\"\x05\xb5\xed\x01\x04bK\x85\xf05\x9c\xe0&\xd9)!#\xf7\xd3\xf0\xc4,\xdc\x9a\x177\x15X\xfdH\xaa\x11E;M\x90\x16|ev`\xcc\x97(\x15\xfb\xe1\xa1\xd8\x0f\xb5\x0f\xca\xe5,8%\x90\xef+\xea\xb2#\xa9\xca\x8e1\x8ar\xe3\x94\xa4KTkT\xc7\x89`\xbbI\x8d\x9d_V\xba\x1d\xc08\xce\xca\xbd*\xd5\xdd\xabf\xbe\xeeU\x9cL\\\xb0 \x16\xe2\x0eFj6\xa3\x1b-\xc7\xf1c\xbf|\x91\xb9\x9e/\xb2\x16A_eY[\xba#B0)\xb6\x93 F \xc6\x9a\xbe'\x15\x10~$\xf7l\x82\xeb++\xfd\xc5A!RJ\x8aU\xbf\xe9\x94\x92\xb9\x88GK7@\x8f\x04\x1e)\xa7\xc9[\xb7D\x82\xa8\xca+9A\x92\xa2 \xdf\xccrcY\xa9\xb7])\xe6\x84[\xf5.*\xe5\x94\xce\xfa\x9co\xcas\xaf\xf6\xdf\xb9\xdbw\x16z|.\xdc\xe1>\xb0\xaa\xbe#\xbf\xb5\xb1\xdf\xcd\xf9\xff\xfa\xfa\x8e\x1f\xdcP,Ka\x8e\x9b\x08gk\xf0\xb5oJ\xbe\xba\xea\xe1\x9dfT\xb1+!\xaa\x14\xe1(\x02\xe1\x8f\x03\xb4\xdb\xf7OD\xea \x91;<\x15\xf6e\x8f\xdc\xe1^sz\xeeT&\xac\x842a\xc5{|\xcd\x02Q\xdd\xe6\x88\x05\xadP?K\xeb\xbf\xbb%\x0ci\xda\x89\x14KoM\xbd\x14K>8)\x1c\xfc\xbcHI\xc1,\n\xff\xa2\xe2\xf8\xf9\xd1\xba\xb4\xa9\x12\x06\"o\x93\x19o\x85~\xa2KQ\x18K\xf28\x10\xda\xd3\xea\xe7>|\x0d\x89r\xdcD\x1b\x910V\xb6\x93\x9fZDXu\xc9\xfe\xb5\xf9H\x15\x0bJk\x96}\x14\xf6Y\xf6\x92\xac\xc8\xe4\x98|\x0e\xc2\xcd)3\x19\xeeZ\xb8\x86\xb0?M\x93E\xc0;x\x1d\x8b|:\x1anr\xa2\x9b\xd7p\xb5\x8e\xb9\xba\x933:\\\xa0\xf1L\x95}c\xa10\xfe)%\x86\xe6\xdc\x1bkj\x0bND\x96J45(/\xb5X3\xabm\xa6B\x80\x18Qi\x19\x0e\xf7F]\x8b\x9d\x0b\xd5\x9eXG9\n\x91j\xdd:\x081?\xe9L\x1f+\x12Z\xb5\x10\xcbB)\xb2\x19+\xc9\xb0\xf1=\xb9\xfc\x9e(\xca!|\xc3%\xe5\xc8\xcc\x9c\x0c\x07\xe3kt\x7f\xf7\xcc\xbc\xfc\xa6\xc3\xeb\x04\xdd\x954\xaf\x93\x93eA^\x92U\x01U)\x0bE\xf1\xdaI|m\x9d\xbe\xb7\xd0tc\x8f\x9b7\xff\xec\xafm\xfe\xd5_\xdb\xfc\xc7\x8e8\xb6\x7f0W\x8aXV\x1bA\xbd{~\x83o\xf1.\xafN\xad9CR\xe6\x08\x8b9\xaa\xe2%\x9d\x0d\x9d\x97e\x92\xe5G\xb2\xfe\x19\xfa^9\x15b\xfe\x83\x05}7\xc9n\x02\x0b#\x12\x99*\x8a\xf09\xcd\xe2\xa2\xd3\x0d\x15\xf4\x8e\x12:N\x97\x13R4\xab\xda\x97-\xaa\x176kv\x16\xdb[\x1c\xc7\xe3\x19yO\x8a%\x86Q\x12\x1aaE3\xe9Q\xf8\x91\xe2\xe3Z\xd9.W\x04\x93\x12C\xcc\xce\x14P\xa7P\xadzV\x9e\x8c\xa1\xf4:\x14\xbc\xa1]\x1da-v\xa5y\xa7n:?\xa1\xef\xe5\x07\xc1\x9b.\xa9^i7UW\xa2]\xbb\x98\xaeXx?'Vu)\xbbf\xee,_\xab.\xe4RHg\x1d[uU\xfb\x0c\xdd\\\x87\xbb\x1d\xd9\x90\x00\xc3:\xd5\xbb\xda\x87{\xa3H\xfb\xbb\xe5^\xd8\xbc\xdcfQ+\x19Q\x97-\x8b\xb9\x1f>\xf2\x95\xc2\x15\xfe\x9d\xcbLp\x00\xbf[\x11\xa9v\xd3F{?ws\xba\x9d\x148o\x12\xdd|s\xd2b\xa7\x01y3\xa4\xd3\xa7\xa82\xc6\x81bbz7\xc5\xadj\xa6d\x18&\x8c\xbe\xf6\xa2\xc4Nn\x14\xedp@N\x02\xe43\xbck\x13\xa0\xac\xc3\xd9\xa6N\x83\xf2\xa0\x9a\x91\xfaXZ\x04mD)\xeb\x98\xb2\x99(\xf9\xcc\xb9\x86\xc3o:\xeb*o@i\x94\xf8\x9atR\x19t\xb4\x93\x04F\xc9\xaf\xf6\xb7\xcf\xa5OZ&h\x83\xdbE\x05}\x13\x9c4H\xc9\xef\x1cZ\xcbHC\xb6\x18)\xd0\x92\xe3\x9bq\x01\xc0\xa2NhUE\xb4\xec\xf1\xef\xbb=\xd7\xdc\x1b\x9c\xea,\x16m\xeev\xba s\xe4\xe2\xb2\x88`\x7f\xd02\xe7\xcd \xa9S\xe0\xa3y\x06\xa0sW\x1b\x8c\x13\xf4\xbd(\xa4D\xdb\x961pW\xa8Yj\x90-W:\xc1\xb2'\xd4\x04\xc8\xbc\x8f;{\xb0cHa\x0d\x92{h\xd2X+WP\xa7\xb1\xb5\xc6--_\x8f\x8d\xeb\xe0\x0e\xa9\x81\x97\xa3\xe6\xe8\x90\xff8\x0f\xd7Q\x8c\xe4*\x82-\x1b\xec\xcc\xb1E\xae\x19\x19\xcfx{\x0f^[\xfe\x0f_\x95_\xc7\xc9\x8e\x9b1k\xa2\x9a\x15\x8f\xcf\xcbD\xbd~\xc7o\x86\xc7\xd4\x8a\xf7\xb2\xb5U\x11\xc4\xccq\xfaf\x7f-;P\x8e\xa7\xcd\x0bH[\xbb\xa1\xb4P(t\x98\x0e\xa6\xc0\xe5My\xae\xc5 \xd8\xcf\x98\xa5\xb9*/t#|\xe2p\xeb\x05%5\xe8|\x02~P%R\xdc\xde\x8e \xe3\x0d\xe5\x12\x02hn\xb6\xe7\xf9\xe4Sm\xfa\x84\x81Z<7\x1f\xe1\x03\xa6&\x1f\x918*/v\x03m\x036\xc3\xd3\xf9S\xe1\\\xdc\xc9\x8d\x80\n\xca\xa8s$\x89\xfb\x0be\x08K|\xb8\x12\x906\xb1b\xb8\xeb\xb0\x9a\xa9\x0b\xb3Y\x1a\x13\x83\xeaW\x1d_\xc6h*\xd4r\x02}\xc6\x8a\x882\xb7:\"\xcf\xd8\xcap\x82U\xf01\xf3;~\xb6\x81'\xbe\xc4\x8fX\"N\xf9\x0c7r#\xe2B\xc4\x1e\xdcF\x1f\x1c\x0cDD\x9f\x1c\xf9\xfe[Y\xc1,\xeb\xcc\x9b\xc4\xd1\xe6\x9d\xa8cf\xb7'|@\ni \xc8\xe1\x04\x0c\x12X\xaf!\xe6\x7f\xc5e\x8f\x1c&}\x96 \x15\xbav\x10\x07a\x05)\xf3\xa0\xa4\x93w\x0c;&\xcc,`0\x10\x9e~\x01\xdfl\x85tD\xda\x85\x03c\xa5\x89s\xe9\xd5\xe8>vR\xc5bV\xe1\x06K\xac\xac\xa5\x8c\xa1\xcb\xca\x80\x18\xc1\x16\x9eR\x992\x8b-\xcb4>A\xda<+<\x8ea\x99\xe1\x86\xc9p\xd3*)\x10\x93E\x15\x15\x93\xb6\xcd\xe9$\xa6\x9b1\xf8\xb1\x85\x11\xa4_\xa6\xa7\xca\x9c\xe09\x96!\xda\xa4\xc2\xbcf!F\x11\xb4\xdd\xe5\xaf\xf45\xbe\x9e\xb2N\xda\xf4x\xff^K\xe4\xd6\xd3)\xb4\xd1Zm\xab\xf8\xec\xeb\xe3\xb1\xbc7|\x96\xaa\xb5z\x10B\xd6yZrxmo\x17\xf0HC\xf9\xae\x93\xd8+\xfa\x1d\xba\"\xe0\xf9u\xe5V\x13\x10T\x13tM\xa1\xe4\xaa1 \x96\xd2\xe2\x11\x0c\xb0g\x91\xa8\xa3\x13\xc9'\xcfU\x92\\\xf4\xc6\xd05\x95\x9b(\x08\xeaXk;0\x7f\xf2=0\xddd\xfb\x86x`;\x19K|\xf6\x08 \x1c.\xef\xe72\xc8\xc2E\xa7\xba\x11\xdd\xc1i\xa7\x9d\xa4J\xa4\xe4\xc6\xd3\xb2\xc9u\xa7aE\xb5\x8a\x16\xdb]\xb8\xd9\xee0\x02C\xa0\xe5\xcd\xf0\xdc7\xb0,Y\xee\xb3.\x9b0\xf7_~\xdel@\xb0p\x93\xe3\"\x19\x12\xb5\xabk\x92uP\xa4De\x1d\\JZ\x11\xd6Y\x7f\xa4\x0cY\x832d\x918\xc2\xb2.\xba\xd0-7L+\xabG\x07\x8f\xcf1\x04+\xf9\x8d\xf1/\xde\x81\xe0\xf2\x8a\x1a\xde\x8ee<\x93\x83\xbd\x87\x8bY\x92\x12\xb0:\xe5\x81\xae\x0e@\xdb\x95>\xf3\x04\xfb\xd8\x88\xe6\xf9 ?\xde\x88\xe1\xe3\x8b-\x01\x0e\xfcE:e\xa9s$\x07P\xce\x86\x04E\x07\xed9WUC\xac[\x99_\x85\x89\xb2e\x1d\n\x04\xd0\xb8\xe7-\xf4\xbcJ\xe1!\x16\xac\xb9\x05q\x80U\xfb\x90(\xa7\x18\xa8\x0d\x07*M7R\x04*\xcb\x01$()\x86\xa5$\xb1\xb5\x8b\xc59\xedxeW\x95\xf3\x85\xe5_\xb7K(\xfd\x15\xa6\x8c\xdc.\xae\x81\\\xc5aG\xa1\xf3\x1b\xa3R\x92\xadJ\xbc\x94\x14\xc4\xcbd\x02\xea\xdc\x92\xa9\xe672\xcf\xa6\xbe\xf4\x06d/\xb9\xa4\x00\xa5\xfb\xf5po\xc4%T\xd4\x10\x06K\x15O\x81\xd8\xc5\x8f\xd18H\xab#\x93\x96\x84#\x8f\xc4\xf9\x99v\x93E~-\x85sn\"K\xa3\xa5\xad\xe5u\xb6\xa0\\\xb4\x90\xac\xa3g\x97\x1di\xbb(`\xd7\xaa\xdd C\xbb\x01E\xf533\xfd\xec\xa4\xa8\xc2#\x13]@M\xf2\x8b\"\xb8Kk\xda\xe8\xccN-\xc5\x9eT\xda\x8d\x9a\x83 \xeb(\xe2$\xe1>\xccq\xe4\x99(\xbdx\x08\xe2C\xe9^\xc6\xac\xee\x83e\x96i\xeb\x11\x91\xf4\x8b,g~\xd2\xacb\xa2\x022\xbc3\x8a\x80\x0e\xef\x8c\x10\xcb\xc9p\x7f\x04;@\x87\xfb\x86\x0c\xc1aU\x90\xbc\x91\x95\xc1j\xb1I\x86l\xa4v\xd2\x00\xf6\xdbm6+\xf4\xb9\x1a\xe2\xa0\x1f\xee\x99\x06&8\xd7_e\x8d\x0f\xe1\xd6\xfdR\xfc\xfa!h(\x04m8\xf5\xc2\x89S\xc2\xdfE\xc3+\x0f\xbb\xd1\x17\xe2 \x1fJ\x89\x1bV\xbc\xc8\xc9d9\xde@\x87![\xff\x15=+\x05;G\xd1\x87S(*,\xf9\xf2\xdd\xb6\x0c\xd4\x8a\xe5&\xdfWG@\xca&\x03\xaf\x0f:\x12\x89\xf9\xcc\xc3\xf5\xf4|\xff\xd5\x8b'\x13\xf5s\xec[N%\x8f\xbfu\x0b\xa8\xa6\xbf\xad\x85M\xae\xd7U4\x82\xf8\x05[\x03\xde\xedz-b[\xbd\xc6\xfb\xb2\x8a\xbf\xf8\x02\xa1Y\xea:\xf91OH\x90\xfbz8\x97k\xd6\xf2\xb3\x04\x81\x84\xf3\x84\x06u\xcb\x14\x0c\xfc\xf6u3\x0b\x9f\xf0\xf3\xac\xce\xc4\xdfE\xbcv&Bx\xb6T\xfd\x0bM\xa2\x81Z\xfa=i\xa9\x10\xe4\x95\xd9\x92\xf0\x81\x06\x94\xf6|\xba\x05Y\xe2\xc1\xb9\xe5\x9e\xc0U\x97\x022_\x1f~2\xc1O\x01\x86\xb0W>\x97\x1c\xdf\x1d\x07\xfe\xf5\xf5m\x1e\xec\xff\x06\x9c!\xaef\xa7\x00\x86\xba \\\xce\xe4\x9a\x80\x92X\xe0\x02\x88H@\xd2/\xb29\xb9N\x07\x1c\xbd\x1c\xcd\xcb\xfaR\xffFFJ\xe5\xc7\x8c\x11\xbb\xa5\xb3\xaf,Gq](\xe2\x00]\xb3\xbcy\x81\xf8\x87\xce\\\x08\xc2\xc4\"jr\x90\xfe8\xa3\x05\xcb\x97c\xd4,\xfb\xd1\xf7\xaf,\x8e\xdeI\x99\xcdFD a\x89\x116\xcb\xb3\x0bD\xf1\x0f\xab\x059\xca\xf3,\x0fzG\x97\x0b2fd\x02\xc3\x97\x11\xfc4\x02\xb6\\\xa4\xe4\x00z\xb0\xdd\xcaHk\x19\xc3?\xdd\xd1U\xaf\x88\x8cG\x08#x\xea\x1b`\xf5\x8b\xbb\xcd\xa5\x00[^\xb1A\x19\x17x\xbd\x9a\xfe\x87\xbb\xe9z\xc4V {\xfaUc\xb88\xb7\x15j\x81\\^\xbd\x12\x8f\xea\x1c\x9c\x14\xd7\\zT\xee\xf6\xd6\x13\xb41\xce\x9aY\xdd\xf1-\xe9\xa4/\xf3\xac\xbf\xd0\xb3\xcbW\xdf\x0bm\x13k\xa7.\xb5\x8c\x9eu\xe6\xba'\xf0Hf\xa3<\x10\xc5>\xe0\x10v\xf8\x0f\xbfs\x9fZ\xb6\xf2\xb9\xf4E\xfb\xc9x\xe0\xa3\x14m\xe7\xa5\xf9\xd3\x9f=0\x1f\x8f\xc0\xd3\x94@\x96\x03\x06E\xef\xa4\xc9\xa7r\x0f\x98I\xbc\x18\x14\x1f\xb5\x81@X\x97\xd9\x0b\x16yG\xe2d\xc1A\x94$\xd0\x99SLX\xb0\x13Z\xb0\x98\x8eI6\xd5*\x9e;\x9c\"\x10r\x88\x1e\xf5Ok\xc9>\xf3\xc0\xa6z.\x9bpr\xe8\xfc\xa2\xa8\x96\xea\xd6\xb2\xc6U(\xe5'\xb2*\xac~\x89\xea\xda\xf2\xe3\xca\xf4\x8b\xe5+\x8f\xb7\xf8\xc5\x8c\x11\xae^\x9d\xa8K\xceeB\xa6 %\xef\xf2lAr\xb6\x92\x9c\xaf\x7f+\xfc:#L\x13-7\x19\x83\xbat\x12$\xc2&7j\xe2\xaa\xdb F\xbf\x8a\xdax;\x8fo\xd3uF\x1a\x89\x98#\xe8=\x8d)\xcd\x18o\x1d2\n1\x85\xa4L\xcf\x9b\x93q\x96O\xfa\xbd\x92d\x8ah;\x07\x8bi\xba\xba3\xb7\xa9\xcb\x12\x8d\xd0\xbc\xae\xfa\xa7 \x9d\x04U\xd4]\xf7gW0\x8e\xd9x\x06\x086\xf7\x80\xae\x02\xe5\x9a\xae\x8e\x88X\xea'\x90\xeb\xa7\xf1\x9c\x94\xa1\xc3\x9fD(^\x8c?&d\x1a/S\xf6\x13\xe7\x960\xe7\x8c\xb5\x1b\xfb\x00\xc4\xea\x88\x80\xc3\x8f\xa4\xa9\x98P\x97\x05q2\x94)\xcaS\xab\x15C\x9d\x99t]\xa5\xe4\xa7\xb1P\"\xda\xb1\xa9h\xd3\x7f\xb1\xe0\x1d\x8b\xe0#gL\xde\xdd\\\x95\xaew7Y\xa5\xebm>!9\x99\xbc\x8e\x17\xf0g/\x82\xdeU\xbbV\xd7\xbbk\xd4\xea:\xd7k\x04\xf0\x95\x125\xfc\xed\x90\xadyh\xc9b:\x18F\x8a\x1f\xd2PT\xa6m\xd5\xd0z\xf7o\xaenS\x96\x9d\xe1S\x92I\x95\"}\xb4\xb5{\xa1\xcc\x88\xe0\x1c\xf5f\x95\xbf~g\xae\xdaG\xef\xae_\xfbHo\xb8]\x06\xb5\xd6p-\xf5\xb8\x0f\xb0+\x90U\x9f\x06\xa8\xb8\xd1 \xa7?rv\xbf\x91nDGD+\xf2i\xa30\xd8\xd2\xba\xdc\xe8E\xbe\xb9\x80\xa1\x0e\x90\xa1\x05\xd6\x12\xde\xe57/\xbf\x12\x17\xed\xa1O\xf3l~DY\xbe\x12\xbaRM\xf9\xd3\x8d+\x9b\x15J\x10\xc2\xdf\xa0U%\xc1#\xbf6\xab\x11\x85Z\xb7V3BEH\xe4\x12\xd5?\xb2.+\xdf\xd5\xaf\x99t\xe5$\xfe\xd5\x16\xd4\xd1\xc2\xf4\x9d-\xf2^\x18$\x1a\x84dRh\x84t\x00\x1fX\x1d\xbe\xc3\x99\xaanP\x83zY\xe7\xc0\xb0o#`\xc1\x1b\x16\xc1\xafa\x04o\xaeA\x81\xdb\x82\x1fR`\x13&\xd4\x9ao\xc4\x0dt\x96K\x13m\x8b\xa2i\xce\x86Q?rL>oD3\xb0q\xf5e\x9b.\xbc\xa9\xc3\xcd+T\xe8\\\xab\xc8l\xc67\x0e\xdf\xef\x159\xdc2%\x1b\xac\x8dQ%\x1b@\xa3\x86\xf74A\xd7\x1d\x89y*+\x87=8\xfc*l\x05\x896\x80 0\xb7\x13;t\xb2h\x06\x02\xa7\x02\x9fk\x87\xcd\x06`\xc8\xaf\x03\x06\xda\x00\xc3<^\x18\xf0\x15$\x18Z\x85_\xde|\xd9\x19\x119B\x94\xda(\xa99\xe0\xd6&\xaf\x99\xf3<\x1c\x97I\xc0l1KW\x9c@\xa9|\xcb\xff\x14\xeb\x10\x8a,=e\x0fV\xd5y\xd9|\x16\xc9|\xcd\x14\x0eD1 SWa'Q\xd8\xechB\x1b\x9f\x0e\x96\xd0\x01Au<\x99\x8f\x0bZ\xd7=\xb5\x0c\x1aV\xd4m\x82\xcd\xba\xa8\x9e\nye\x19\xa2N\xef\x8bRL@\x83\x8aP\x1a\xa2\xa2Y\xac\x02\x16\xc4G\xbf\xb0\xd2\xbcbZ\x0e\xd7RT' \x0b\xde\xb3\x08^\x86\x11\xbc\xd7\x97\xca\x14\x08\xe8I\xc4\xcbh\xc06%\x7f\xffe\x9b\xab\x93\xd2\xd8\xd7\xc7\xb8\xe9\xbcy3\xdca\x08r_\x96\xcc8S?\xbc\xff\"\x84\xbd\x11\x0ce\xbe\x18\xca\x14\x862\x85\xa1\xa2\xda\x96\xc2K\xaf\x9aa,x\xc6\"\xf8!\x8c\xe0\xd9\x97s\x10\x0e\xe4{v#\xc8\xf7Wb\x18\xf3\xc7/\xe3dn\x0c\xbf\xfe\xc3HT\xe1\xcf\x86\x88\xf4Jr\xba\xaft\xe8\x10)\xcct\xf1\x10\xedu\x94,D\xb3\x9fW\xff\x95\x88\x84\xc7\xa5\xed!\xbf\xbeb\x81\xb5\x88\x9e\xe6d\x11;\xdf*\xd1\x15K\xf4\xa30 \xaa\x12\xa3\xd8Z\xdd\xdc\x157-R,\xbf\xdaz9#\xa2\x1b\x81\xfd_\x83\xe8\x1e\x91\xa1~{\x01\xca\xf0\xca\x9a[\xb8\xa3\xa2\x86Z/\xd6\xe5e\x89\xde\x95\xae\x11\x82@\x0eS\x18\xa0~)\xde%\xee|S\x0e\x1e\xf7r\x06\x87\"\x91\x8b@\x89\x1cQ\xa2\xba\xb9'n\xee\xb5\xf3\xe5\xeb\x97\xc5e\xd1\x83&\xd4\xce\xe1z\x1a\x827\xf6G\xcf\xec\x8f^\xd9\x1fa\x8e\xaa \xa7\x11\x9c\x10.ZP\xed\xcd/T\xb0.\xa9\xe4A\xb7\xa1g\xd5\xb0\xd6:\xdc\xf8\xf8\xaci\xd4\xf9\xe7o/he\xf2qw\xe6\xa9L\x10v\xd0YY\x1d\xdd\x85\xe6\xf5\xcd[\x1b\xdc\x90\x18\xe2\x94ks\xe1\xe2\xeba\xf5\xb7\xd2Y\x18b6\x9b3\xf1R\xfeV\x92\x89Qe%\xfa\xbfuK\x1b@M\x9fk\x9eli\x1f\xd7l\x03v\x9dT\xff\x84\xcc\x17l\x85br\xf9c\x001\x95\xa2\xf6/\xa4\x9d\xf2\xb41UO\x8dq{\xd1*+\xb5\xb0P\xffM\xb3j-\xe9'\x9a]P\xf8DV\xd0\xfb\x1bl\x03\x81m\xf8[\x0f2\n\xfc\x97\xc2c\x8b\x91\xbc\x06\xbd\xad\n|\xb2\x98~Y\x8b\xc3\x8c\x14\x1ez\xc3\x9a1\xa1\xbeD\x85\xd2ku\xe0V\xad,\x846\x9a\n\xe7\xe0\xa0Z\x87v\x1d\xe6\xda\x1ax*\xd7\xed\x1b\xc7OCZ\x9f\xa9\xccS\xea\xca\xac\xd8\x9a)\xeb\x9ci\xfb\xe8\xae\xcd\xf4\x86\xb4\xfd\xce>\xae\xcf\x1eX!\x91\x07\x06\\k:jZ:\x00])e1Y_uk\xd8\x8dS\xbc9v\xf3\xdf8C\xe25\xc1\xff\x84 \xa1\xbeA62\x0dT\x1b@\x06\x0d\xf8\x1a\x04\x1ap\xa8w\x82\xcc\x16z\xd7j\xc0\xb1\x15\xa8\x8c\xc5\nuxO\xd7\xed\xd3\xf2\xd7\x19a\xefT\xf3o\xa7\x9c\xb4\xd8\x11E\x1b\x7f\xde\xcc\xe4\xed\x17(\xb2\xec(\x99--\xfe\xebu\xdd\xcb\xb0\xaf\xee\xf6\xde\xa3\x93D\xcf\xab\xb3\xc2\xdd\x993'\xfd9E\xff\xde\x94\xcacgk\x1c\x94\xc9\xe9\xf9\xb3k'\xa7O\xae\x9d\x9c\xde\xc5\xc1\x97\x92t<\x99\xd8\x8b\x11\x18\xb6\xa6\x17 S7 \xb7\x82-\x04\xe1\x16\x19N\x9b9\xa4\xeb,zF+[UFK\x0bUy\x1b\xeb`\x97\x0f\xda\xe5\xb73*Jdk\xd5\xb2\xab\x9b?'\x18\xd4\xa2\x1e\xf0\x9f\xd5\xc3V\xf9m\xf5\xe0\x19!\x8bF\xf1\xed\xfa\xc3F\xb3\xeaV\xfd%c\x01\xef\x8c\x1aJ\x8dg\xd4XA\xbc\xbc\xdd\xae \x9eQ\x8f:\xe0\x19\xed\xdb\xeb\x80\xe3CW\x1dp\x16\x144\x82#\x8ey\x05\xbd1\x07\x93\x82\xa2-Yf\xd0\xf6\x96D\x02Nq\xfb\x9f\x88\xb0?\x9bZ\xbd1\xa9\xaawL\x98U\x9a*\xbeH\x9a\xaa\xb8Vg\xbb\xf1d\xe2\xdb\xee\xa4\xc0\x9aq\xac\xac\xbcC\xb7\xb7CH\x026\xa4\xa3\xb0}\xec85\x8a\xe5\xb1\xcd\x8f\x1d\x8b\xfa\xc6x\xec(\x07\xa9Z$\xc1p\xb7yx4\x96>\xa1\x8c\xe4\x05\x19\xb3\x9b]\xfe*\xa3\x12\xf3\xab\xbd.0\xc4/\xbeC6\x94\x98NeS\x18\x9f\x17\xcb~-,0\xf0\x14N\xbfg\xd6'\xe7$_y\xb4\xac\xae\x12\x1dJ#\x8cE\xf5\x0b\x02 \x90\xcd\x93\xa4\xc5\xa6$\xeefZ\x1aHR,OY\x1e\xff\x7f8\xf2o\xc2\x91\xeb\xc6ry\xa2\x08&\xb2\xbai\x14Q<\xa4\xcf1\x85`\xc43G\xab\xe5\x10\x81\x93\xebi\xf4$9H7I=/K\xaf6\xd1q\xafCM\xd3\x1e\\[\xe7T\xdf!Y\xce|y\x819\x0d~.\xbdw:Nf\xde\xee\x93\x95\x8f^\xc2\xd08\xebn\xff/\xd2 \x15\x7f\xadz\x85iZ\x85\xb61\xcf#3t\x90c\xcc\xb9\xafa\xd88\x1d?\x85Xk\xc4\x9b\xea\x80L\xf9\xb0;\xd5[\xc5\x7f^\xfb\xb3\x99\xc2G\xf65\x8f?\x91\xe0\x0bu>8\xfb\xa48FM|J\xdb*\xa01\x8d`\xcaq\xac\xf7\xf7\xbf\x9f\x9c<\x7f\xfd\xfa\xe3\x87\xc7O^\x1d\x9d\x1c\x1f}89\xf9\xfb\xdf{mG\x90\x05\x7f\xbb\xf0P\x1aM:\x11\x81X\xaa5\xb1f\xb5&\x05\x05U([j\x88\xb1\x1c\x9c<4\xa5w<\xae\xf0|\xc1V\"|\xba\x04\xa3\x9f\"b\xd6\xbd\x17\xebJ\xae\x85#\x08\xa3\xcaf\xdf(_G\xd5\xb4\x88\xc8\xea]\xad)\xf3M\xc2}\xee\xa4Kc\xcc;\x10\x8c\xf9xg40\x99j,\xed\xce\xbf@\xa5u!TZg\xb4\xd2d]\xfc\xbfM\x93u\xe6\x86_\xa9\xee3\x14X\xd4\x7f-\xe8pJ\x95\x03\xddBSj-*\xa5\xd6\xa2\xae`R?\xeb\x0f$k\xb0\xa0\xba\xcej\xe1\xa3\xf0Y\xb8\x14>\x8b.\x85\xcf\x82\xaa}\x08\x038\xa7\xf2\x06\xdf\x8a\x88\x92\x11\xb0`N9q\n#\x98\xdf\x9cFh\xfe\x97h\x84\xe67\xa9\x11\x92\xfe\xf7.\xc5\xd0\x9cV~\xfa\x82r\x9f\x19(\xf7\x8aFp\xca\xf7\xc9\xdc\x83\x16\x9flJ\xd8N\xffC\x84\xed\xc2 \xcd\x95 l+>\xde\x13\x1a<\xf7/\xbby\xf4\x05\x84\xed\xad l\x97\x1aa\xe3\xb7\xfaKZ\xcc\x92){\x9c\xa6\xbe\xd1\xfc\x97\xde\x8a\xee\xa7nE\xf7)\xad\x1clO\xf5\xbdvA\xe5\x0d\xb9\xd7Np\xaf\x1d\xd1\x08.8\xb5<\xba\xb9\xbdvt\x93\xbb\xe2\x98\xc5\xe3O0\xe4\x1bb\xd4\xde\x10G\xd7p\x05\xa9\x1b\xe3g$6\x14\xaaG\xbd\x15\xd1\x92r\x93\xf0\x81H\xbcNvv\x1e\x84\xf8\xbd\xf0\xaa\xb2\xef\x058\x04\x99\x84\xc6\x14\xf7W\x1b\xf9\x82\x90O\x1b\x01\x88\x8f\xba2\x1c\xf2_\x86\xec\x1d\xad^\x96\xc5\xac\xab\x97J\xdbP\xae\xaf\x9f\xd6\xa1\xd4\xf4\x95\xce$\xb8\xfb\xb7[\xedD\x1a\x03\xcc\x07\x1e!0\x9bo\xc1\x0e\xecq\x88?\x12j\xc3\x9d\x9d\x10?\xb3\xf1\x05\x98Y\xa5lcH-\xb9\x0f\xf9\x825\xd7\x82_\x86D\xcbu|\xb4\x04S\x96\x9c6\xae\x87\x16o\xd5\xac\x18*\xef\xd6\xcb\x9f3\xe9\xda\xff\x98\x9a\xc5\x93\xd6\xe2=\xe6\xa4\xc8C0\x91\xead\xb4u\x05$\x0c\x05G\xe4^\xbf*\x07I\x87\xd4\x82\x0c\xb8\x19\xba\x1d\x9b\xaa\xe4\xed\xcb\xf0\xa0\x0d84&\xb2\xe4\xd9P\x00*4pT\xa7\x10\xeb\xdfN\x9d\x0f-2\x8aw\xca\xc0X\xdb\xfa\xb3\xc6\xfa\xd3\xeb\xae\x7f\xdb\xfd\xba\xb5\xfeYge*\x1de\x8b4\x19\x93`\xcf\xdd\xa6<\xa66i\x97\xa3\xa1\xa7:\xca\xd4\x95\x0f\x067\xbb3\x9d\xa2\x8d\xd67\x9fF\xb6\xb8\xce,6\xb12}i|\xb6D\xa9\x06\x06m\x82W\x9c\x15q\x83\x8d#\x89\xcf\x91\xc9\x89\xca[\xe9\xe8Q\x0e\xd6\xc7\x15\x8cbq\x11\xa2\x7fe\xd6p\x7f\x08jM\xd7-TeG\x17\xa49\xfa*M\x8f5\xc6\xaf<\x99\xf2\xda\xc9\x84e\xce\xb2:\xc9\xe2\x07\xcd\x83\x10\xeff\xee\xd3\xdd\xbd\x88yc\x11\xb3k\xad\xdfcj\xaa0\xddX\xc3\xcd\xd4V\xa5.\xa9\xad\xb9\xaa\x10\x94\xe3\xeacZMH\x9f\xcc\x86a\xc8\xfa\xcc\xf6,z\xa8\xa3kkAe\xdc\x81\xbe$\xd5\xd1\xa2y~\xb9\x90\x82\x8a=\x977\x10!\xaf%\x13\xccU0\x08\xd5\x92 \xe27y\x07\x13\xe85Y?\x1d\xa9\xd7l3\xb3\x0e\xb1\x9a\xa9\xf1\xec\xcb\xfdNn\xcf\xc8\x84N\xaf\x7f\xc5O\xe4]\xf1\x03\xb2\xdf\n\xd0\x91\xf0\xec\x17\xcb`Q\xd1\x98g(Z\xead\x1e\xba\xb2\xf393\xf3\xf9D\x05\x1c\xa1\xd6\x15\x85\x9a\x01\\\x1a\xa4\xf7c\x1a\xc1S\x93\xde\xf5\xc3\xe3\xa7/-\x9a\xd7O\xfc\xfd#\x0fi\xffq\xe9\xae\xd7\x91?\xb4.\xf3\x7frf\x94\xa9\x98\xe1L\xe7\x84\xb3\xa6\xa3^V\xd1\xbf\\\xfc\xaaS\x07\xbf\x94\x81o\x9d\xa7\xee\xb1\xd0\x03\x1cs\x80<\xa6A\xcb=\xc5\xd2\xe8\xbbnq\xb1D{\xabYR;\x9c\x86\xa8\xa3cCjH\x84k\x85\xa4\x9e\xbe\x8bU\xbc1\x0d#\xa8\\&\xb5\xd0\x88\xe3\xd5\xfc4K\xb1B\x82\xeby\xb3\xadf}|\xfd\xd7':|Z\xaa\x17?\xf9h\x03?\xb9\xb4\x81\x9f\xba\xb4\x81\xbc\x0b\xdd\xb6\xf6D\xb7\xb5E@\xfb\xcf+\x02\xf91\xe2\xcbDM\xe9\xbfdJl\x8f4_\xafH\xe0bE@.8\x91\xb9qE\xa6\xed\xeah_\xaf\x8d6zh0\x06U\xbe\x07\x8b\xe9\xcdi\xdaV\xd8c\xa61\xad\x15\xc4\xbbm\x9a\xc0\xb2\xe7tB.\xc9\xe4\x98|\xf6\x00\x8cF\xe2\xdf\xcb\xa8s\xbf^^\x1c\xfb\xb7\x8e\xc01\xa6\xc2\xf6\xd1\xccc\x82\xdf\x9e\xfa\xa4\x07\x9c\x85Y-H6\xc5\xfc\xda/\x8eQ\xe7\xc8\xff\x10\x16\x1e\x0b\xf8P\xbb\xc4\xdf\xf1\x9d\xde\xdb7\xff-\x13|\xfb\xa6\x9c\xe2\xdb779\xc9\x97du\x0dAC\xf8\x13\xd8\xfa\xa4\x93F\x8f\x1eU\xa3\x10\x98\xfcS\xcc\x89\x1aX\xcc\x1b\xa0\xebI\x0f1\xa1\x89\xb9<\xb8aXB+\xb4\x19,j\xc8\x125W\x9c\xa1\x84\x8ay\xbbYh.Sc\x18\x08\xe7@|6o\xa3oRZR\x04=\x84C\xe8aE\x028\x80^\xd4\xb3c2\x83\x01\xf4\x0czTu} \xa6\xbbp\x9c\xcaR\xfd[{\xe8\xb2\xba-,%\xfc_t3\xdaR%\xa4\xb4I\xe1\x9a\x96^4x\xe6\xf4\xda\x9c%\xc8\x1d\xe0\xc5\xb7}\"\xab/ ?\xcf\xbdVt^\x93C=\xd0\xaa\xdcb\xf5\x94\x9d^\x9d\x89\xb3t\xc3\x0d\x16A\xe6\\\xe0\x06\xae\xb5\x1cT\x1e\xc2>\xe6G\xe4\x98\x02\x07b\xc3\xb6\xb6\x83\xae\x06\xc0\x9a\xb5\x0e\xe4\xc8\xe0\x10\x82LR9l.\x94\xed\x92\xb2\xf4\xad\xa8\x18\x988\x0b2\xe7\xfe {\x9f\x9c\xcd\xd8\x86pS\x84Ig\x84*C\x94\x9b>I\xaeG\x9a\xdes\xab\xdd\x1dl\x83\xc6^\xfcq\xb7D*=\x19\xaeWWh\\\xbe&\x06?\xb9\xde!\xc1\xb9\x91\xcdz\x14yYD\xac\xdc\x1b\x8a\xa5\xc2LY0L]\xe5^5&\x9a3\xb3\x06\xe4\x80\xb9\x1f\x94\xba\xbf\x80\xd6\xfc\xee\xd5\xcb\xe9\x92\xbd\x8a7Q\x0f\x88}\x8d\x1e2\xbb\x11\xec\xecy\xf5\x92\x14G\xf3\x05\xf3\xb11\xc8^4\"\xae\xcb\xe9M\xc9\xfd@.c\x9d\x19\xf5\xe0EmFH\xaf\xd9\x8c\xb3%m\xee\xfc\x8e\xf9<\x0dH\xa5J\x12\xdb^\n\xb0\xe2\xe3\x0d\xf4*\xd8\xfb\x13_\xf6T\xf6\xefK\xa5@\xa3T\x1fI\x10V\x06)W\x06<%\xe5\x98\x88w\x17\xeb\x8a\xdf\xcb\xbc AU\xa7\\T\x12\xe7\xbbR\xcfy\xec%\xb5i2\x97\x99\xddU\x97\xa3\x94\n\x9e\x05\xba\xb9\xcdR!\xefJ?o}V\x8f|^\xc6\xe9&\xc2\xd69)\xc9\x86W\xfb2k\xa6\xc7V\xd3\x1dN\xcdk\x8b\x81Z\xfd\x13L\x97W+\xceDHu\xdf\xcd)\xd6\xab\xb7\xfeN\xc3\x86\xaa\xd5\xcd'\xd6\xaa\x1at\xf9\x8e5>&\xc6<\xa0\xea\xba\xf2\xe4\xf7\xc4.}\x93m\xb8\xdf\xa5\xf8\x81;|\xa3\xd3\xa5\x14Y6\xe7,,\xd5\";xn\xea']V\xc2%m\n\x97\xbc\xefa\x16\x01\x1d9\x05L/\xd6\x8aO\xff%\xf1%n5o\xf4M\x84=T\x8dQc\xa9]\xf3\x98\x1agd\xc7\x8a\xe8 7\xb3z8\xda\xb2\x99MF\xb1!rx\x0e\xa5\x02\xdc\xa6\xe3\xf1_-\xcf\xa1\xbc$r\x05\xfdF\x91o\xcc\xbc \xe8\x1f\xfb5\x9f\xc6\xec\xf5\xb5\xa51\xdf5\x02m\x13\xffb\xae\x93\xa4\xae&m\xabk\xea\xbb6\xb2\xd6Bn8k]\xc7\xa1\xae\x895o\xf1\x8d%O\xd9\xe2\x06ga \xd9\x1f5)\xc1WD\xd0\x8f\x12\x7f\x8c\xe1\xa7\xdd\xab\x0d\xcc\x90\xf5\x82y\x1e\xd8R\xa1\xa4.\xef\xfa\x14\x1f\x9fa]m\x9b>5\xaa\xfcd}\x07\xfe\x9cz\x0e\xddTnZ\xf8\x03c\xa1MUa:\xabU\x98\xee\xcc\xb6\x9c`\\\x90GV\xe4\x00}\x1a\xb1Z:\xc6-\xa9\xa4\xc4I\x04+\xceJ\xafB\x14\x13V\x95\xbf\xa7\x19D\xaee\xf1:\xad\xce\xf2l\xb9\xf8w\xb0\xe2~6\xbc@f\xbb{\xc7P\xd5\xc5\xf9wO\x06\xde\xc8\xb9w\xe9\\\xf8\x95\xb59w\xfe\x99\xe0\xdc\xbb\xf7\xb5~I\xf0\x04\"\x04r\xbd\x86\xe1(\xc4\x18\x06\xccY>\x8c#HFp\x00\x89\x87q\xd0A\xc7\xec0P(\xe8G\x81\xb3:\xe5\xed4?U\x14\x8cD\x90\x04&\x12\xa9.\xcb\xf87\x165f\xf1&r\x06\xd2!\x99py%b\x08V\x9e\xbd<\xdf\x84\x86\xab~\x9e\xd3M{J\x8a\xe3\xe5\xa9g\x81\xcfR\x06\x1c\xd8|\xc2\xcaJ)\xc2\xea,y\xf4J'\xe4\xb7\xb4\xe5y\\&\xc6\xd9 \x9f\x96y\x8a\x0b\xce\x0bm2\xc9\xc05K 3m\x96ay\xd3\xffT\xfbDVo\xa7\x1b\x0c\xa9<\xd483\xb7\x11$o\xc0H(\"\xce\xfd\x8f\xf8\x9aV\x86\xef\xea\xe7-)\xd5\xa7\xdbts5Z\xab\xe4W\x1f\xf9Y\xff\xfe^^g],\xbc7\xae\xb11\x97U\xbb\xefy|\xb9A\xaf/\xd8F*\x8cy|\xb9\xe9\x99\xfa\xa2\x96\x8f\xc8\xab\x13?\xa3Yk\x06p\x08\xef\xa9pa\xf9\xe8'(\xcd\x13z\xfd\xe9\x88\xee\x98\xe8\xcewn9\xd9\x18\x13\x8d!\x8f`n\xbe\xf8\x94,6\x80\x9d\xd6\xfe\xeb\x98\xcd\xfa\xf3\xf82\xb0T$\xb6t\xd6\x14\xbe}\xa5\x04\xcb\x1e\xe3M\x06D\xbb\xe3=\x90\x9fgI\xba\xa1\x99\xa1\x1c\xccO\xd74l|J\x16\x1f)K\xd2\xcd\xba\x15@WC\xdeL\x05%\x12\x82m\xd6_\xdb\xcaa\xc8\x0c\x06\xe6\xfeX\xfc\x89l\xb0\xbc\xacf\x80\xb8\x06J\xf1\xfen\x18\xa5x\x93\x9b\xa3\x14\xff\xeaKP\xea:\x92\xc4?\xbc\xb8[\xad\x84\xd1G\x8aj\xdeZ\xf26\x8c\xac\xec`x\x15;\xcd\xac\xdaeuq\x91.\xab\xc7\xe6i\x05Zja \xd8\xb1\xbb\xb5sY\xcf\xbf\xa3\xec\x7f\xc9\xb8\x19\x04\x1f\x82*\x91e\xd7\x0c\xb5f*\xe9\xa7\xfc\xf6\xd6-\xd8\xde\x8eQH\x95\x0dZ\n\x95\xab\xeb*\x8c \xb6\xbeq\x15\x81^\x06\xe9\xbfhU\xb2|\x93e!5o,\xfe\x9d[\xae\xe5\xd7\xd2\xe1Q\xa2.9N\xcf(K\xfdB\xdf\xa9e9\xd3\xee\x0f\xc0?\xe2Q\xbf\x9c\xd1\x8f\xfae\x89\x95\xd0/e\xba\x89;\x8bS\xa9K\xe8\xf0kE\xaa<\x1c\x1aUD\xa3\xac\xdf\xeb7\xd1B:\xab\xfa\xbd\x9d\xe2\xdb{\x1d\xae\xad`\xdaki\x04\x05j<\x0f9i\x1b\x0c\xe0\x8d\x14s>s\x8c,\xf0\x05\x91\xe6o)=C\xfe\x0b\x16\xb7\x8b\x088)\x80\xf1\xe1\xe6\x9aW~\xf0\\\x97\xa9(\x0f\xad\xcd\x98\n\x15C\xb0!_\xba\xb9\x186\x8b\x8b\xd9\xd3l\xb2\x81\xa3\x0b\x9bU\xd9\x05\xb0\x8a\xf3L\xcf6\xd0\xcd#@\xb9\xbd\x84\x83\xf2`\x00{p\x1bv\xcb\x8d\xe6 ]\xcaL:\xeeT\xf0\xf9\xb9\xf2\xa36\x16\x0ea\xcf\\\xf5\xb6|M\x0c\xcck\xf1\x1b\xdf\xf0\xd1^\xa2\x90~\xe7\xee\x9d\xfd\xef\xf6\xbe\xbds\xefN\x18\x95\xb7\xe1\xe1C\xd8\xbb\x07k`\xf0\xe8\xd1#\xd8\xd9\xbb\x17\xc1\xdd\xfb{\xdf\xde\xbd\xf7\xdd\xee7\xcd\xf7\xeeh\xef\xdd\x89\xe0^\xf5\x1c\xd3\xb9\x07\x0c\xb6\xe1\xce\xb7\xf7\xef\xee\x7f\xb7\xbf\xf7\xdd}Xs\x98\xfe\x8bo\xe9\x7f\xc9\xcf\xf6\xeeG\xb0\xbf\x7f\xf7\xfe\xb7\xfb\xfb\xf7\xca\xe6\x8f\xe5\xe7\xd8M\xf9\xe6\x9d\x08\xee\xec\xdf\xbf\x7f\xf7\xdb\xef\xbe\xdb\xfd.\xd4\x9bpl\xb9@\xe7\x0f(\xd6\xba<\xdc\x10j0\x80;{\xf05\xe4\xb0\x0d\x9fi\xf0\x94\xe0\xa6yJ\x02\x16\x86|F\xf6\xce\xc1sw\xaaKh\xc5\xaf\xd1K}R>\xdd\x943\xc2\x8e:;\xd8\xacq\xcfvCc9k( \xa2\x89\x14\xd6\xee4\x95\xc1|/~\x10\xc9\xc9\xb4\\\x00\xfa\x1b\x1f\xe8p\xaa\x02\xbc?\xd0\xe1+\xfe\xf7\x07i\xb2(\xf8-\x19:*n\xcb\xc0\xea\xf2\xbe\x1e8\x04\x03xF\xf1IB\x8b\x85\xc8\x8d\x8f\x9f\x1cg\xcb\xbc\x9eW\xc6\x04\xb2\x86\x12I\xba\xb7\xd6g\x87\xad\x8fgqBE\xdb\xd2\x96)ng\x94\xc5 F\xa5\xe3\x10\x84\xee\x12c\xc4s\xd3)9M\x93\x0dB#K\x01\xe5#\xb3\xae\x84I\xed\xb38j\xb9\xf7\xfbZ\xff\xedT1\xb7\xcb\x02N\xe1n#\xc3j)M('\x89a\x12A6\xb2\x17\x9f\x06\x10FU\xcd&\xe9)4\xce\xe3\xc5\xcb\xba\x0f\xb2/\x8c\xae\x01\x04\xbe\xeeMXt\x89\x19-X\x88h\x04\x07\x10\xb0\x93\xeb\xec\xd6\xd7\x14\x93\x9btf\xeexn\x07\x92\xdaI\xf5\xbe,\xed\xfc\xde\xd9\xce\x90E@F^\x8d\xbd\xb1\x90\xc3\xe6\xd9\xdc\xb1\xd9\xb6\x88O2.h\xc3\xd32\xac\xf773\xac\x9d\x1b\x1e\xd63\xf7\xb0z\x05\xd2\xc0\x9a\xf1\x03\x0e\xe1\xc5\xf1\xdb7}\xf1(\x99\xae\x84\xdaVRK\xcf\xdc\xa2\xaf\x9c\x04\xf8\xd8\x9a\xc9\xd3\xd2\xdc\xc7N\x0c\"\xf0\xb0\xe4\xe0\x08<\xc2\xbfw\x90\x9d\xf3\xea\xe0\xb3G\x07\x9c\xf5\xd9\x86\xfd\xfb\xf7\xee\xde\xbds\xef\x9b\xfb\xdf\xc16\x04\x843d\xf7C\xf1\xe7\xa3G\xb0\xdf>}\xeb\x0b%[{M\x87\x0bu$\xbe\xae\x8eD\x19\xa8\xc5\xef5\xceD\x91^\xa0|\xd08\x14;\x89\x9a\xec\xb6\xb1\xb0\x0c\xa3o\x0f0\xfc\x161\xa5>p<\xd82s\xf2\x93/M\xdf\xe0\xa73\xbf\xd1\xc0\xa9=\xbf\x93b\x9a\xd0 JO\x9e\xdd~\x817\xdd!:\xd3\xc1\x01\xec\xb4\xfd\xffLfN>*?\xc3\xd5\xb9\x9e>S\x99\xa8\x9c\xa3\xd1\xd2\x0c\x97{\xc7\xcb\xd53\x8d\x0b\xf6\xfc\x9a#+\x8dq\x7f\xd9\xe8n\"~\xc3\x13qn2~\xc3\xb7\xcb\xc5\x06}*Dm\x86\x15\xd9\x9d\x98\xf9:U\x96\x02.u\x8a\xa0Z\xb1\x10\x98\xf6j_\xfe\x89\x15\x8c;\xb23\xf2\x8b\xa8\xec\x8c\x9c`\xef*\xe7~t\xce\xafRDt\x04\x85VI\x15\x959\xa3\x03{J0\xef\xc9\xd1\x1eB\x0e\x07\x90\xab\xd0\xfdc=\x02x_94\x88\xd61\xc7\x81gP\xb0r\xee\xfc\"\xf2Qz\xab\xfe\x15$\xe4:\x8e\x9f\xa2\x9a\xbdW\xeb7\xe4\x9a\xe8\x89\xfd\x1b;\x0d6\xd2k\x87\x88\x82\xaa\x14]]\x0b\xa5e^\xafG\xd3\xdc\xba%\xf8\x8b\x99\x96dU\xe1\xed\xb5\xfc\x11EUmKV\xa5M\xdd\x117s^j\xc1\xe3\xd1\x00v1\x07\x85%\x90\xc8\x02(d\xbefUt\xd1\xce^\xf5\xa5<\xb4Z\xd5\x14\xc1v\xc61\x92/\xb2b\x13\xd3\xe6\xf5\x93|\xf8\x99\xf5\xaa\x12\x03%\n\xec\xc3\xd7\xea\xd7\x0e\xec\x89\x02\x03\x0e\xcb\x9f-\xf5\xa1~)\xa3\x01s\xca\xe5\xeaJ\xbe\xd8V\xd79 \xad\x8d`+\xc1R\x00b]Eh)\x17\xd1\xb30\xd4\x92\x96b\xb3\xf2\xbe\xb3\xe5+\xde{\xe4\xca\xa3\xa1C\xd4l\xb6\xf3\x06i\x84\xb0\xaa\x19\xd0~\xc7\xfe;'\xefo\x0f\xbd\x86\xfd\xac\x84l\xc6!\x1b\xc3\xff\xe5\xb2\x03\xdfz\x1c\x07\x92\x9a\x0b0\xc6\xfc\x1e\x88w\xe0\x10>\xf3\xb9\xc7\"\x1d)Zm\xd4\xcfL\xa5\x8c\xed\x02\xbf\xd3ZbIU^Q \xefm\x9c\x92\xf8\xdc\x87\xf3Rf\xb9!\xefbd8\x94C\xc7bq\x1e\xe5\xa5 \x00J\xff\x12\xc1\xcb~6EgZ\xebg\"?\x89\xe6\x9d\xef}\\\xc3\xbf\x8e\x1f\xf8\x9e\x11\xaa7\xed\xde\xe3y\xf2\xffq-\xbd\xeaK\xf5\xc7+\x1a\xb9\x90\xcd{\x0c?'l\xe6sN)\x99G\xef\xc5\x8do\x9c\xa7S\x01\x02\xed\xf1\xdbL\x96\xb5;W!\xa7\x08Uz\xd8\x89\xd27\xe87\xcb\xba-\xef\xd0q\xbd=\xfc\x8dy,\xc4 Q\x0bZ\x9a\x95\xbd\xe4\xb4\xeb\xe6\xd31T\x9d\x86\x9b\xd9l\xd8|\x95\xc3\xcd\x03\xda\x89\x96g[\x94\xd0\xaeY \xf4\xc7\x9a%A\xbf]3)\xfc\x1a\xe9J\xda\x10\xef\xbd\xac-\x9f\xb8\xf7C\xadiq\xef\x84\x18>\xbe \x86\xaf\x8fH\xf3\xf36TT~\xb9\x03\xa0m\xb8\"P_\xb4\xef?\xcd\xd2\x94 \xa4\x0f\xe0\xd4\xe0\x03\x81\x01b\x1f\x0d\x0f\xf4\xb4\x92\xefX\xfb\xb9\xc8\xcb\xb70<\x91\xa9\x02\x8f\x8c\xa3d\x07P\x18\x1e\xe8Y%\xe7\x86\xe7\xef\xc98\xcb'\x07\x90\x9b\x9e\xc5\xf4\x8c\x1c\xc0\xca0\x89\xf7dAb\xde\xa4\xe1YR\x1c\xc0\xccp\x7f\x9agsLmkK\x97|\x15\x01\xe9\x93\xcbE\x96\xb3\x02\x93\xc4 \xac\xbcr\xfb\xb4\xf5\x96\x05\x81\x82\xe5\xc9\x98i\xf9i\x94 ]\xdbn\x9a\x0f\x8d\xdeQ\xb3u\x15\xfb\x16G\xb0\x8c\xa0hn$L\xc6\x1e\xb00\x82-\xe3\x1e\xe6]\xa7m\xfa\xa7\xa5\x01C=OX&L;\xca\xf3,\x0fz\xaf\x13\x9aL\x132\x01r9&\x0b> \xc8\xc6\xe3e\x9e\x93\xc9\x03\xe0\x93d3\x024\xa3;s\xf5\xe2\x84\x9c\x03\xa1\xe7I\x9eQNu1\x02\x8b\xbf4]\xa6)\x10\xde*\xccIQ\xc4g\x04b:\x81x2Ix\xb3q\n3\x92.\xa6\xcb\x14.\xe2\x9c&\xf4\xac\xe8\xf7\x0c\x14\x9b\xa4\x05q\x90\xfc1\xe7i\x9a\xc0r\xf8\xf7L\xed\xfcfP\x07\x05\xeb\xe7d\x91\xc6c\x12\xdc\xfe\xbf\xc5\xed\xb3\xa8\x9b\xa8AE\xd8\xc6\xc3\xe9\xf6v;\x84\x17\x90\x8a\x85a\x9f\xc6s\x0c\x8dxN\xcf\xe3<\x89)\x83\x9f\x92,\xc5\xe4\xdb\x86\xfc\x92\xad;l\x96g\x17\x90\xf6\xa7y<'\xc5\x87\xec\x1dV\x91\xd9k\xa6b\xd3\xb0\xfa\xcb\x91\x98\x06w\xee\x86f\xdc\xcd\xaf\xdf\xba#K\xa2L~>!\xd3\x84\x12\x95\xfc\x9c\x8bE\xbd\x93\x13R\xbc\xce&\xcb\x94\xf4L\xa4T:I5\\\x9e0\x8f\x12\xe7\xbb\x9ef\xf3yF\x8f.\x19\xa1\x85\xcc\x7f\x8e\xf7\x1bwH1\x8e\x17XS\xf1UB?\xbd\x8b\xb1\xae\xa2J\x9d\xdf\xba]\xcc\xe24\xcd.\x8e>/\xe3TV#d\xfd\xd3e\x92N\xbe\xcf\xf2\xf9\xb3\x98\xc5\xe2\xb5,g$\x97OY&o\x92<\x89\xd3\xe4\x0frL\xe2|,\xda[\xc4y\xa1\xff>#\xec8\x9e/Rr<\x9e\x91\xb9\xf8\xee\xaf\x17\xc7o\xdf\x88\x9d\xd1\xe9\x01\xc6\xf2U\x07\xb3\x8c\xb6*D5\xab\x8eF\xe8\xa8o\xdd\x82^\x86\xbd\xf6D\x11\xb2\x86\xb1\xa0\xb7\xa4b\x9fNzp\x00\\\x82*\xf8\xc6\x8d\x97)\x0b\x03\x16\x86\x8ex\xd7+\x18\xc7l<\x03q8\xb6\x1e\xcb\xef\x1a\xd9\x1b\xae\xf8^\x16\x03J\xa6\xabNH\xc8F\x8e\x05\xc3|$\xf9f-\xa9<\x1c4\xfb\xc6\x1e\xe2<\x8fW\x1bt@d\xb3\xe8]\xa3\xff-\xeaI\n+\xefp\xd4\xeeH\xb0%\x92O\xd2z\x03b\x0eM\xe3\xabr\x84\x1eT\n\xae\xe6\xb3\x9eAB\x0b\x16\xd31\xc9\xa6\xb0RK\xd2\xe7[\xd2\xf5i /\xc6\x01U\xcf\x86\x8b\xb7\xd2\xb2)\xce\xb8\xcb\xb4\xbc$\xec\x8b\x8c\xce8\xdb\xea\x95\x8a\xd9\xac\xde4\xd5Nd\x98`\xf0Cv\xcc<\x0b\x05)\x15\xa3)\x87\xbb\xd2\xfd\xecF\xb0\xacP\x91\xb4\xb3\xf3v [\xe6\xf0\xc5!3$\xe80\x14\xbe\xeb*\xc6N\x879\x17\x0f\xc90\x1f\x89\xf4\x8at\x99\xa6fMt+\x13&\x82\x8cf\xf9\x1c\x0f\x0f\x81s\x03\xb8\x8c\x90N|O}\x91\xd6<\xc1vOIQ\xd2\x9dc\xd9\xc7\x92\x8eo\xbe\x175\x11\xaff\x9b\x99\x9a\x8dT\xe2u\xbc\xf0A'+\xca4\x93\xfa\xba\xf4\xa2\xf5ue\x01_Y\xa1\x8a5\xe5\xee\x84?\xdb\xa5\x84p\xc8\xef\xb1\xcb\x7f\xdb\xa8K\xc5x9^\xa7\xee$s\x1e\x08Y\xd7\x81 U\xda\xfcn\\\xdd\xa5\x18r\xb1\x01\x98\x8aU\xc1\xc8\xfc\xc3lI?\xbdN&\x93\x94\\\xc49\xf1E\x9c\xee\xfd\xcf\xfa\x93\xa4X\xf0\xb3I2\x8eH\x97\x9cp\xe9n\xd4\xf4\xb2\xd3\x82\x05\x1d[\x08\xcd\x93\x01 0\x959\x0b,\xbel`\x14#\xccw\x0d\xe7\xa0\\#\x0e\x80e\xf14\x9btC\xf9\xbcL\xb2\xa5\xaal[I4+55\xc1\x05?[.\xf8D\xfc\x93\xa8+\xe0\xec\xf7Ty\xd4m\xe8\xf5Bc\x06\xa5\x10\x19pK0\xf3\x95\\f~\x82\xf9l<\x8c\xce\xa9N9\xa5\xc0\xe1\xbc\xa7\xfc3\xd0\x8a)V/\x8a\x13\xb2\x0d\x0eu\x9a\x11\x99\x83\xc0p\xec2\xce>\xb0\x91\x1d\x96\xf5^\xfaI\x81\x9dQ\x91\xf8\xfe\xa05\x88\xf6\xfcg\xc9\xd9,M\xcef\xdd\xdc\xa5Z\xe1I6Fu\xab\x99\x01\xd9\xaa\xf8\x8c\x9e!s\xaf\x08N`\xe4\x92=\xcd(#\x94\xa94\xac\x8f\xe0\x1e\xb9S\xc5\x03\xe9\xafX'\xdf\x8d+\xb5\xec0\xba\xd2@\xa4\x83\xab\xfa\x88\x90\x0b\xdf\x8dP=\xb2\x1c\xee\x8e\"\xd44\xecE\xa8@ \xfd\x84R\x92\xff\xf8\xe1\xf5+\x91q\x18\x16\xa8V\x10r\xb2\xa8g\xbb\x80\x87\xf0\x0d\x92\xc9\xdf~\xc3\xfdJ\xa5\xe7\xdc\xd8\x99m\x86\x03\x84\xf7\x94\xaa\xae\xb7\xb7\x8b\x910\xafM+\xd8\xecE\xb05\x86\xf5\x1a\x16\xf0\x08\xbe\x15\xbd\x08\xaa\x80w\x87\xb7\x7f;\xbe\xddg\xa4`\xc18\x8c\xf8\xdb\xfc\x83\xdb\xc3\xaf~\xbb\x18i\xf7\x83\xdem9\xb2\xf5\xbal\x80\"iN\"\xf8[\xefo\xa0\xdcN\x92\x08z\x7f\xeb\xe9?\x97\xc3\x02v\xe0\xee\x08\xb6\xd1)\x9e\xf2g\xbd\x9d\x9d\xdf.\xefp\x99\xbc\xba\xf5\xf5\xed\xdeh\xb8\x18\xb9\x8de\xb8,SQ\x98\xa1\x1f/\x16\x84N\x9e\xce\x92t\x12\xc4\x9a\xc8}\x94\x12\x8efA\xafX\xc4\xb4\x17\x86\xfd\x82\xb0\xc7\x8c\xe5\xc9\xe9\x92\x91\xa0W\xb0\x15\xaa\x03\x86\xbdq\x96f\xf9\x01\xfc\x9f{\xf7\xee=\x80iF\xd9\xce\x05\x11 qO\xb3t\xf2\xa0\x17\xe1\x8a\xe1\x7f\xfa\xabxo4\\\xc0!\xae\xdd\x1d8\x84}8@\x08\xdf\x87C\xb8+\xff\xe6\xf7\xef\xc0\x01l\xdf\xfeW\x10\x07\xa7\x05\xcb\xe31[\xa7I\\\xac\xe9d\xadL\x0fk\xbeg\xd7E0_\x17$g\xe1\xe1z\xc9\xb2p}\x1a\xc4\x05Y\x93\xb3\x84\xae\xb3,\x0dHL\xc3\xc3uN\xe2O\xeb\x15#\xe1z\x8c\x8f\xf9\x81\xb3\x9e\xc5\xf9\x1aE\xdb\xc9:\x8d\x8bb\x9df\x94\xac\xb3\xf9\"]g\xb4`\xeb\x8c\xb2\x84.I\xb8\x9e\x90\xe0tyvF\xf2\xf58\x99\xc7\xe9z\x9c\xc69YO\x03\xbe\xc7\xd7$\x0f\x0f\xd7 M\xd8:\x0d\xc8Y\xcc\xc8\x9a0\x12\x1e\x86\xebI\xb6\x9ed\xcb\xd3\x94\xacI0\x9ee\xeb\xb48L\xa6\xeb\xb4 A2\x0d\x0f\xf9<\xb0\xf6\xe8\x9a.\xe7\xebsB\xd9\xfa2\x18\x93\x05[\x93\xf1z\x11\xa4\xc98a\xeb,g\xe1\x9a\x91\x80N\x8a5*M\xd69\x0d\xc3\x90w\x9d\xa6l\x96g\xcb\xb3\xd9:N\x0b\xb2Nh\x9c\x06\xe9\x8a\x0f\xe5\x92O'\x8b\xf9\xd7\x01\x89\xc73>\xfb\x84p\xb0e\xf3\xf5\x92\x8e\x03\xbe{\xf9\x00\xcf\xd2\xec4N\xd7g\x19\xcb\xd6g\xcb8\x9f\xac\x93`\xba\x9e/\x02\x81\x03\xc5Z\x1b\x04\x0d\x12\xb6F\x95~p\x92\xd11 \x0f\xd7i\xc2\xa1\xb5dk%\xfa\xacY@\xf2i<&k\x92\xd38\x0d\x0f\xc3\xc3u\x11\xae\xd3 \x9e\x9fN\xe25a\xebl\xfci\x9d\xd1\xb3p=\x0f\x92q\x9e! \\\xa3\x8ai-\xd4\x08\xe1\xfaM\xfcfM\x83xN\x8a\x05o)f\xc99Y\x93K\xb6&\x17\xeb$]gl\xbdL\xd3p\x9d\x05\xc8\x16\xad\x17\xc2\x10\xbe\xce\xd7K\xb6>'y\x9eLH\xb8^\x04\xf1\xf8S|F\xd6q\x1e\xcf\x8bu\x9e\x9c\xf3u\xc93F\xc6\x8cp@\xb0l\x9c\xa5\xeb\xe5i\x9a\x8c\xc3u\x1e\xc4 \xc7\x98 \x9ed4]\xf1\x85\x9b\xae\xcf\x92\x82\x91|\xbd 1[\x7f^&y5\xefb\xbc$k\xa1b[\xb3|\xb5\xe6T1\x0c\xd7Ep\xba\xe2\x8b\x1f\xa7d\xb2&\xe9t=\xcbr\xb6N\xce(\x99\xac\x93?\x10<1K\xc6kT\xe7\xacY\xbe\x1c\xb3\xf5\xf2\xb4\x18\xe7\xc9\x82\xad\x97\x0b\x92\xafWt<\xcb3\x9a\xfcA&\xeb\x8b\x84\x8dg!\x87\xe8|\x91\xf2\xc1\xcf\x08]\xcf\x92b=\xcb\xb3\x8b\xe2p\x9d\xc7\xb4H8\xd2\xe4K\xb2\xceW\xeb\xd5\x82\x041\xee\x8f \x99\xae\x93\xc9\x9a\xc6s\xb2\xce\xa6a\xb8^\x064\x18K4\x9f\x90i\xc0\xd9E\x8e'\x19]\xa7\xa4(\xd6\x85\x18#K\xd2p]\x90u\x91\xf0\x05:\x0f\xe2|\x9d\xe4l\x19\xa7\xeb,\x99\xacQm\xca\xd7\xe7\"\x18\xcf\xe2\xfc\x84\x89\x01\x91\x9c\xacgIJ\xd6 \x9b\x85\xeb\xcb,_\xaf\x12\x92N\xc2\xaf$\x01\x9cr~iw\x14r\x16T'9\x8a\xdc| \x97\xecM6!\xc14\x0cC\x91Al\xc1)\x94\xa0\xeb\x9cF\x1c\xf0\xf3c\xaa\x1d\x00{{\x0f`k\xb8\x17\xc1\xed\xe1o\xb7\xff\xbc\x1a\x06\xbf\xedl\x7f=x\xf8\xe8\xe0\xc1\xfa\xb7\xdf\xfa\xd1\xe1\xd6\xad\xbf\xff\xfft\xfa{{\xf8\xdb(\xac\xdfhPhI\xa0\xc7\xbc\xe3\x0cS\x93sR\xff\xb0\x07[x\xceH\x12=.\xa9\xf3\x98\x1fS\xdb\x90\xc26\x12\xe8m\xd8\x1b\x95\x7f\xee\x8f\x90 \xffvyg\xbc\xb5\xb3\xd3So\xf2{\xb7\xbf\xae\xff\xbc\xcdi\xe1\xff\x11-\x8e\x86;;\x8b\xd1\x03\x87\x07\xcf\x14\xb6\x070\xf6e.\x8d2\xda<^|\xc8\x1a|\x97M\xf5as\xb1\xe4\xc7b#\xc9~\xf9\xcapo\x04\x87\xf5\x9f\x07\xd0\xfbDV\x06\x96D)\x06\x0d\xed\xef[\xdb\xdf\xaf\xb7\xbf?\xaa1[\xaf\xe3\x85\x89\xe1k0\x90\xaf\xe3E?)\x84\x96\x04=\x81\x84\xf7\xc3\x06\x1cd\x9dc\xa4\xa2\x82\x0dE\x0b\x89\x89g\xe4\xfd\xd3*\xef\xfd^\xa5\x11\xea\xcfI~F\x02\x93\x14x.\xa3\xe5\xbbG\xc3\xdf\xe4\x8c\x155V\x07\xe2O\x0bK\xf4\xbc2\xecl\xed\x99\x9fM-:]p*=K\xe6o\x11\xc1\x04\x06(~&\x9a\x96RE\x06\x04!\xa6 \xe4\x83\x0b\xf8\xb6\x9e\xd4\x1c\x85\xc2\x07r\xd8..\x8e\xf72\xe3\x14\xc3'8\xfd\\\x8e%\xab\xc62C\x17Y\xe7Ws\x0e\x83\xceP\xf63|k\xaf\xe3\xad\x15\xe7i\x83\xb3\x08h\x99m'\x82\x9c3X\xc12\x82yS\x0d\xad_mTPB\xc7\x8a\x0b\x1d\xb1r\xfe\xc0\xec\x87\xb1H\x9a\xb72s\x83\x06b\xa1\xab\x86\x8d\xdf\x8c\xa5k\x05r\xe5\x86\xef\xa7\x9c\xfbHm\x18a\xc7\x15~ma \xdeI_n\n\xedo[\xe2\xe6\x8e\xee@\xf1\xf7\xa14\xe0M}\xe1\xd0\xba#\xc7\x14\xb7I)\xb9D\x8e\xf4\xfb$%o\xe29\xf9>\xcf\xe6R\xa6y\x96\x14\x8b\xac@\xe3\xeb\x8f$\x9ex\x94\x95W\"\xde\xedi\x92\x12~l\x0fz\xc1\xf0_\x0fF_\x87\x0f\x0e{\xb7\x93>\xb9$c\xa3\xe1\x00\xcb\x9e\x08\xdb\x00g\xea\xebm\x94MT-\xd8\x88\x93\xaa\x9e\x82\xcdh\xb2\xa1F\xaa\x8c\xf9\x19\x94\x12n\x99\xa6m\x08-\xe2b\x1c\xa7O\xe3\x82\xc0\x00\x9e\xd6\xef|/\x07\xd9 \x1a\xd9\xc3\xd3\x80Tf\xe2\xdf\xfa\xc3\x7f\xf5o\x8f\xbe\xfe\xea6\x17%B\x93\xc6*\xa6 K\xfe \x1f\xf3\xb4\xb3\x07\x0e\x802vlK\x8b\x1d\xe3\xc2\x9a\xd0u\xb8ekM18\xd6{\x0e\x8dG\xf0\x19a\x8f\xc7\x9c\xcb\xe7\xd8\x92gi\x9a\xd0\xb3\xf7\xa4Xd\xb4\xe8\x86F\xe3$\xab\x14\xfe\xfd\xa4\xd0\xb4\xff\x9a:\x84/\x8dMcP?\xf6\xccoV\xfa\xa5\xbaCx\x97Wry\xc2\x15,\xceY\xf1s\xc2fAo\xbfW\xea#u\x15*:\xe9\xf5\xc6b\xf7\xf4\xf04\xfd\xf3*\xac\xb0\xd0V\xa8\xc1LlK\xd5N\xd0\x93]\x88&\x8dv\x12K\x1b|\xcb\x06\xd40.s#a\xa9|\x93\xa6.5v\xa1\x0d2CVA\x887\x9b\xb7\xf1dB\xc8\"]\x1d\xb3\x8e\xbaLmJ\xf3\xdeP\x86\xffye\x0eLi\xe0hf09\xd9\x15\xdaU\x1cQ\x1edC6\xc2\xbdr\x08\x13\x92\x12F\x80\xdf\xe1B\x0d\xff\x87\xf3\x03\xe2\x0dj\xcce`\xcaV\xabl\x03\x06\xb2\xa7\xa2!\xbd\x08\x89)`\xd6\x95\x19HV We=\x95Y\xd7r\xa6X\xad\x16\xa4k\xc1\x89\xb0Z\x94\x87\x12 \x1d\x0c\x84F|s\xad\x89\x08\x84}o\xdf\x00R\xc5\xect\x19$\xcdQ\xc2\xe0\xe2\x13\x88#\x15\x03\xebS\xf4\xbd\xf8\x90\x95\xfe\x1c\x1ek$\xbe\xb1\xac\x91\xd6\x9b\x15M\x1a\xa6\xbf\xfa{\xe7\xb2\x92\xe7I@\x83oL>\x12ctH\xba\xf7\xcd\x9e\xe1\xd9T~x\xef\x1b\xa3{\xc5B\xb9f|\xbbkz<)\x1f\xdf5=\x9e\x95\x8f\x8d\xe3:\x97\x8f\xef\xdf36>W.%\xbb\xf7L\x8f\xcfpV{\xdf\x99x\xff\x95\xfc\xf4\x8eqR\xa7\nX\xfbw8\xe2\xd7\x9e\x97\x04\xfa\xa4\xc3w\xe1\xd6-\x0c\xe1P\xbeU\xd2\xb5\xd8\x8c\x8b\x12\xa5M\xa5\xea\x9bQ\xf3\xfa/\xbe\xb0\x170\x80\xf2\x08lO\xe5\xc8\xe0\xc0\xd3\xad\xd9o\xc9\xc8fsL{\xb06`]ndv\xae\n\x047&on\xfc\xd8\xd9\xf8\xd6\x16q\xdaW}(\x95c\x0dtO\xa9\x89\xfa\xc8\x06\x86\xa7\xce\x91\xf2~\x17U\xbf\xfc\xe7\xd4\x7f\x18u\x07\xaeN\x16\xce\xa1\xf8\xd9\x8c\x8b\x18Z\xc4a\x0b\x8br\xc7\xda\xf8\x9dz\xe3wD\xe3NN\xbcn\xa2\x97} \xefQ\x7f\xc8\xca\x87\xeb5 `\xcfk\xc7\x88\x0e-\xab\xfd\x18\x9d\x84\xab\xfc\xdf\xb4b\xbfM\x9a\x15\xd0\xfd\x00\x86\xd4\x92\xf6\xces\xa3\xc1!h\x02AR\x04\x182\xc5Q\xd5\xcaq\xf9\xa05\n?\xb6\x06|\xfc\x0e\xf0\x08'\xf8i\xd6&\x06\x82{k\xd4l\xeb*`\xb3\xc5{\x99k\xc3\x1cR\xceY\x0d\xa9\xc1\xeau\xd5\xdc\x12\xeds\xef\x93\xc5\xe1\xb1s\x7f\x80\xb2\xa7\xc2#\xa8\xc2\xc4{?\xc5\xe9\x92\xc0|Y08%\x90\x92\xa2\x006\x8b)\xc8\x96\xbd\xca\xd9?\xb68fn0\xa6\x87\xf61\x9d\xa1\xc2=\x97\xc3\x12\x8d{\x0d\xeb\xad\xd9\x85\xb4\xfb\xb4@9\xf3\xf6\xbfv\x0e\x7f\x9bl\x07\xbf\xf5\xf9?\xe1\xa1\xb2\x0chRjc\xa01H\xb6\xc7gp\xef,>\xaf\x9b\x8d\xcecP\x14#\x01\xcf<\x87\xf5\xc1\xe4\x9b\xeb7&<\x95\xb6\x02\xe2\xf0)\xb4Cn\x9a\xa4\xc4k\x80\xaf-\x0e\xc5~c\xec\xb1|Iz\xb2n0?D\xa7qZ\xe87\xb6v\xb5\xbf\xf7\x14#o\x1b\xf5\xa9\xe8\xdek\xe0\xcf\xcd\xce\xd1~\xe3\x16\x835\xa8{\xecc\x93/\xfb\x0c\xedw\x9b3\xb7\xdf\xe0\x92\xe2M\xfc&\xe0\x9f\x95\xce\xc2\x8e\x95V\xcd{\x8d\xec\x8d\xc9\xef\xdcoTJ\xd8S\xa2F\x9fe\xaf\xb2\x0b\x92?\x8d\x0b\x12\x84\x11l\xdd\xfe\xd7\xf0\xcf`t8\xdc\xdd\xf9.\xde\x99\x8e\xfe\xfc\xf6j\xa7\xfc\xfb\xae\xc7\xdf{\xfbW\xc3\xf0j\xe4E\x18\xf8\xc8\xbd&\xfc\xde\xea~\xefOL+\xde\xc4\x8f\xce\x8b.\xbc\x86\xf7\xcc\x1a3\xb0\xf9\xf06 \xf9\x1b\x8c\xf0\x95%\xd2\xc1{|[\x94\\\xc0{rvt\x89\xfe\xc8\xae\xa5\x9dfi\x9a]\xc0Bv\xd2\x83m\x93\x03{\xfd\x0co\xc7et\x8e\xec\xba\x9c\xed\xad[\xb5\xdfv\xae\xd6\xc6\xf1\"\xab\x87\x94\xe74\x9b\xac\xa4RY\xa8\x17\x13\xda\x13N\xf2\xf8\x0b\xcdX'\x97\xf3\xb4\x87\xee\xf2\xda\xcd\x9eEU\x99T\xea\xce\x9c\xa0\x9b\xc2\xc4\xf6j\x0c\xc2;J\xbe^`\x84\x8b\xe8\xc8\xa2\"\x8e\xcb\xd5\xca\xedv\xc7X47\x97|\x8e\xa5\xf3\xb1\xf6\xa6d=,oN\xab79q\xb6\xbd\xb6\xa8^\x9bf\xf9\x8f\xe0,\x82\xd3\x08N\"\xb8\x88\xe0(\x82\xcb\x08\x8eG\x0d\xe1\xd59\xf6J\xdfd|\xc5V\x92\x0eYB\xe4\x9f\x9f\x86\xcd\xb9\xbf\x97\xb4\x1e\xa6 I'\x90\x14@3\x06\x8b<;O&x\x02\x98(\xb6j\xf4\xdc5X>\xf1\x8f0\x80WA\x16\xc1\xb9\xc3%\xe1#\x1a8\xc4x>\xfa\xba\x1a\x80\x1c\xc2\xa4\xda:\x93\xae\xd1|\x86\x01\xbc\xe7\xa3\x998F\xf3Y\x1b\xcd\xe7MG3\xeb\x1a\xc2\xf70\x80g|\x083\xc7\x10\xbe\xd7\x86\xf0\xfd\xa6CXV\x00q\x96\x1d\xe1\xa3\xf9\x03S]a\x91\x11\xfbh\xfe\xd0F\xf3\xc7\xa6\xa3\x19W\xa3\x19w\x8d\xe6 \x0c\xe01\x1f\xcd\xd81\x9a'\xdah\x9el:\x9a\xfa\x91\xd85\x9e\x9f\x1c^K\xeaB\xee&\xf8 5\xe41#;\x8c\xcbQ\xd8\xfc\x02\x0e\xe1\xf7\x00Uh\xbd%\x176\xca\xbbo\xc4\xdd\xe7\x82\x88\xda\xf9\"u\xc9\xd9\xfedsb\xa9\xc8l\xfd`\xeb\x9a\xdf\x8f0\x80\xd7\x81\xab\xda\n\xce\xee\xc7\x0d\xc6\xf8c\xf7\x18k\x87g\xd7\x10\x7f\x86\x01\xbc\xed\x1e\xe2\xcf\x1b\x0c\xf1\xe7\xee!\xd6O\xe8\xae1\xbe\xc0\xec\x8d\x9dc|\xb1\xc1\x18_t\x8fQg\xb0\xbaF\xf8k\xc7\xd0N\x91\xf9)\xd90\x9f\x81\xfe\xaax\xd6\xe74\x18\xf6\x12F\xe6E/\x02\xc1g\x8f0\xc9N\xcb\xcc\xdd\xe5\xe9\x01\x9a`\xd5\xb5\xed\xf8U\xc3\xa4_\xd1E\x82#\x0b\x86\xaa\xd6\x97P=|'\x1f\xeaT\xe0Wd\xc0\xf8\xd3\xe7\\\xa8\x8c\xa4\xb9]\xac\x83{\xb0\xfcJDVKC\xde\x95\xe6\x85\x995\x0e,\x99\xc4\xd4\xe5\xac7\xdb\x89\x13\x1a\x83\xdc\x85\x12/a\x00\x1f\xba\x91\xf6\xa5\x0f.H`\xbd\xf4\xa5\xc6V\xab\xb7\xc1{\xa5\x9dF\xc1\xcd))7\xa3/w66X:Az\x05m*\xf6\xb7\x0cZ\xa6\xf8g\x0e\xef\xdb\x97\xf3T\xea\xae\x98U\xbeK\x84\xcf\xd5\xe5<\xc5m\x8b\x7fa~\x12\xd7\x9a\x0b=\x0f\xff\x86K\xf9\xf2\xdb?\xaf\"\xfe\xfdW_\xe5d\xaa;\x03\xac\x16\xe8\xb4F\xfa\xb8\xaf\xc5\x9f\x0b\x91\xcf#!\xf2w\x95\x16\xe6]\xf5\xe4\x10\xfe\xf6\xf0\x907~N\xf2\"\xc9\xe8\xa0\xb7\xd7\xdf\xed\x01\xa1\xe3l\x92\xd0\xb3A\xef\xe3\x87\xefw\xbe\xed\x1d>\xfa\x8dJ\xb7v\xf8\xe5\xf5+ \x97\xb8\xc40\x8e)g>O \x9c\x11\x8a\xc9\x19' B\x94\xfef\xf5~R\xd7yY^\n\xa7\xd3\x9fsQ \xb8\xfd\xdb\xf1\xd7\xbf\xdd\x0e~;\xde\x0e\xbf\xba\xed@\xf6\n\x88\xb2\x84\x94'*C\xddXx\xa6,\xb5\x93\xa7\xa8/\xfb\xe5\xf5\xab#17\xe1J\xe2\xe3\x01r.\xcb\xaa\xd5\xdb\x13\x9b\xe0\xfb<\x9b\x8b\x8d \xdbk\xcfH)\xc5l\x92]\xd2%\xd9%a\x08\x87M?\x98\xa4\xf2\x83\x81\x83F\x8eJ\xe9\xa3\xa9\xa7?q\xba}\x9d\xcb\xcc\x86\x7f\x1at\x85 \x93\x17V\xe2|\x9a\x8d1\xcbN\xbf\xc0\xc6-\xfa\xa5Joi\xdbZ=\xa1\xa4w)MD\x16\x94byZ\xb0<\xd8\x0b\xfb\xc5\"MX\xd0\xbbe\xd2\xc6\x80\xee\x9f\x9eCB\x81\x86@\xfb\xb3\xb8x{A\xcb\xdc7\xb9pS\xc4(\xc3a>R-\x0e\xb8XE\x86\x132\xce&\xe4\xe3\xfb\xe7O\xb3\xf9\"\xa3\x84\xb2 \x1f\xee\x8e\xc2\x11\x0c \xe7T\xe8\xd6-0\xbe\xb37\x12v\xd5\x9e\x0f>\xa9m\xdd^\xb3v\x1a\x1b7m\xb5Z\xc5\xfd\xca\x97\xab\x81\xd0\xd6\x8cD\xca\xfdA\x0f\xb6MO\xc9\x90\x19\x0d\xb3\xfd\xdf\xb3\x84\xe2\xf2\xb4\xa7&S\xf5\xb8\x07\xa5\xe6S\xcb\xb9\xa1r\x17Sr\x01$`\x9a\xb9\"\x82\xde\x92Mw\xbe\xed\x85au\xb7w\x1a\x17\xe4\xfe]\xd3\x18\xaa\xd4A\xed\xae3\x0c6K2Z\x1c\xe3[6\xaf\x9d8]\xccb\xcf\\\x83\xa0\xbb\x8f)m\xe2\xac\x17\xe2\x16J \x07h\x9c\xf3)i\xcf,G\xb6yc\xce \x9be\x93k\x8fF|n\x1b\x8fz\xea\xcdD\xb4\xc7\xc8\xe2\xb3\xbf\n\x9c\x8d!{\x0f\xd2\x80\x99\x8d\x14S~\xec\x8c\xc9I\xa5\x8a\x8d\xe6\xe4\xc7z\xfa+_^b\xf5\x10\xd1\xd8\x96\x1c5\x88\xbd\xeao&x\xbb!\x8d\xf8\x06\x8dL\xfb3\x0f\xb5\xc4k\xfb\xbb\xb7\xcf\"\xe8m\xf7\xc2\x91\xdc\x9f\xa6%\xb5R)\xe6\xda\xd4\x86\x94]\xb5\x95\xb48\xd6\x94J3N\xb8f\x15\xe1\xa2\x9aSN\x97\xcb\xc8F\x1e#\xf5\x91\xd7a\xae\x94b\x96\xbcd^\x04\xd8X\xa0\x063\x8ektL\x9a\xb31\xa5Q\x9e\xcc\x03m\x91~\xc3\xecx\xbd\x13\xb4\xd8\xf4z\xae\xe1Z\xb2\xaay\x0d\x93\xc3\xec\xb4\x82\xd9\xc7\xb6{Yd\xc8\xe3\xe6\xd54ig\x9b\xe8N\xc2z\xfb_\x97;%s\xdd\xb9l\x915\xf7\xdc_9Bi\xffY\x97\xf6\xa5ui=ZK\xbb\xd8ZZ\xbd\xfc\xa7\xf2?\xd5\x83\xb2\x90\x16\x0d\xee\xdd\x0d\xfbO\x96\xd3)\x91\xde\xe2\xd7\xca\x06hN\x88\xd9\x9cfI\xa9\x8c\x92\x99\xc8\x15\x0f\xff\x7f\xf2\xde\xbc\xbbm\x1cK\x14\xff\xbf?\xc55\xa7_\x8a,\xd3\xb4$\xaf\x91\xedx\xb28\xdd\x99\xc9\xf6b\xa7\xea\xd7\xa3\xf2xh\n\x92\xd8\xa1H\x15\x17;\xae\xb2\xe7\xb3\xff\x0e.\x00\x12\x04\x01\x92rR\xd3\xfd\xde\xe3\xc9\x89E\x12\xc4r\x01\\\xdc\xfd\x9e@\x15\xcb\xf2\x13\xf1\x83\x9c\xc7\xa2\xfc\x17$\x0b(\x81p\x047a\x16\xe6\xb0\xc8\xf3\xd5x{{\xe6\x07\xe4:I\xbex\xf30_\x14\xd7^\x98l\xa7\xf4\xbb\xedi\x12d\xdb\xf8\xf1\x16#\x9fRo\x91/\xa3\xd3P\xc4nd\x94\x86\xcb\xf3\xb9A\n\xc7\x90\x1fA\xba\xb9\xe9@\x0c\x9b'`=\xf1\xd3y6\xb94Q$\x157\x97\xa2\xcb\xaeB\x1f\xb2:\xeaq5ED\xcd$\xed\x1f\x94\xb3\n\xc8\x99uG\xe2l\xa2\x99\xa4\x16\x1dS\xe5\x15\x98C[\xd2\x1a\xd8\x12\xc58j\xc4\xca\xca\n\xef\xbb\xc4\xa8'\x14\xd8\xe7\xa4\x1f\xac\x932\x1a\xf1#\x9a\xacB\x19\xcbcf\x1d\xa8nz\xf5#\xcb\xfd\xe0\xcb#\xba\x80\x11\x98\xd9\xb8\xe9/:r\xfa\xb7W\x9b!\xb7\xd0}D\xb3\xc2\xb8\x17[\xd6\x18\xfd\xf6j?\xc5H\xcfk\xb5^\xd4\xb3\xbd\x88\xa8=\xad\xca\xa8\xf2\x84\xc84'\x04\x8b\xac\xc3\x8c\x102x\x06{p\n\x19l\xc1\x1e\x8c1\xf3R\x00'\xb0w\x04\x01\x1cCv\x04\x01E\xe3\xd1$\xa0\x05.\xe5\xda&AKb\xf0\x1b\xee\xa5n\xb6\xa3\x86R\xdb3\x93\xe9\xac\xd4c\xc1\xb0\x8d\xe2:q\xd1\x16\xd0\xd4\xc4\x9eux\x8a\x03\xb75 \xdb\xe5\xdf\x1c\xdcR,h\x8a\xc3\xa3p\x8afOSzb\xc2\x7f\xd1\x9f\x05\xfd\xf9_\x90\xcc\x90Zd\xcfV\xecYV\xacV\x11=\x7f\xf2\x84=O\xf0\xb9\x0b\xe4\xeb\n\x03\x9c\x80\x1fC\xe9\xd8\xe1\xfd=\xe3\xa1\xbf=\x8d\xe8A\\z)\x19\xc8\xb3\xbch\xe5X\xc4EK\xde \xe7\xb2\xe8H\xe9\xde\xa9\x8b\x16\x97\xb0\x8d\x99\x95\xd9\x03\xdb\xacN\xe4\x0b\x1d\xf3y\x1eJ\x91~h\xb2taQ\xaeo\n9\x8f\xc2pQfP\x88\xda<\xf1\xc5E;?/\xe5W\xf3\xd6\xf2f\xd8\x1a\x82\xc5\xf5\xda\xe4\xd9\xc2_\x911\xac\x9aoD\xa07\xed\xcb\xa5\xbfzY\xbe\xef\x8d\x1ef\x88\x9c\x1ew\x06F\x18\xe5>\xb3\xf5\xe7\xb6\xb6\x87X\xbc\xd9Z\xdb\xf9\x8a\x9f\xf4<+\xb5'#V\xd0<\xeb\xdaN6\xb9\xcd\xae\xb3\xcap2\xb1V\x0dg\x8d\xae\x9f\xbf\xf2~\xfe\xca\xfb\xf9+\xf6\xf3WM\xd9\x94\xc7\xfb\xcfl\x8b\xed\x7f\xcb\xed?\xe1D\x87.\x9b\xb3\xadi6,S,d\xf6\x9a\xc7\x99\xec&&z\n~\xb3\xaf\x82+\x11|t}\xbb\xf2\x11h\x9c\xc7\x84\xfeu\\\x1f\x1e\xb3R\xa5\xef\x85\xfc}\xac\x8e_\xf4\x97\x16\xaa0+r\x1ae\xcen\xbb\x14>\x03\x06F\xac\x05\xdf}\xd0\x8c\xac\xd00]\xe2]\xce\x8f\xe1\xb4\x0c\x9e\xa7\x9b\xb0\xb5N\xe0}~\x02\xefK'\xf0\xbe\xee\x04\xde\xef>\x81\x05\xd5\x00'\x80\xa6+)\x0b\x9e\xc7\x8c\x1c]\xe1\xbd\xcb\xe2\xb3\x9e\x02QQpm`2\xe2\xe5\xc9\xe8\xa5\xe3\xb14u\xa2\xc0\xf6\x1b\xe7\xe3\xad\xcfl\x9f\xb2\x15 \x18S\x16\xc6\xac@\x88\x05<\x94\x97\xb0\x86\xebk\xad\xb1\xa2\x98&A\n\x0f\xbc1t\xb4++\xf6\xc2\xac\xec\x96\xfa\xcd\xa0\x16\\U7\xed\x99\x96\xfco\xd2ar\xf4D\xed\xec\x8b\x89\xa7P6\xa9X\xec\xac\xd5\xe44B\xda\xa6#\x87\x8f\x81X \xdb\x89\x95\xa8/\xb1\xf2_\xa5\xac\xe0\xbft\x14\x8aQ\xec\xd8\x8c;\xe2\xb4\xc2=2\xc9\x1b\x9b\xa0\xaf\xe0\xaeI\n\x02\xf2\xc6\x8b\xb4\x1b/(7^\xc4I\xdfH\"}g\x8c\xf4\x9d\xc11DG0\xa3\x1b/\x98\xcc\x9a\xa4\xef\xcc\x10\xd0i\x85\xaa\xa6\xc44\xe7\xb1\xbdj\x9ds\xbaf\x0b3\xfd\x84F\xd0\xf6\xeaQKB\xa2_3\xcd\x92X\x18\x96D\xd8E\xbf\xa2K\x00#\xd5\xfa,\x10fW\xc1'S\xef\xe7\xa3\x19\x00-#\x1ce\x0d]\xc4y_\xa5\xc9\xea\xa2\x1cS\xd6\xe8{\xb9\xe2\xb4\x99V\xca\x95s\x83\x91\xab\xca\xc8\xf5.\x92\xb8\x03\x97\xd3\xac<\xa1-,\xe1\x18\xe6G\xb0\xa4\x8b\xc4<\xa5\x18ZJE\xb27.,\xcbEL{9\xa1\xfd]\xd2_\x97V\x89t\x03\x13\xb5K\x81x'\x9f\x82\x08\xae\x12\x80w\x1d\xf3\xd0\xb1\x19\x85xC\x17.\xbb\xb9\x1f[\xb7`\xa2\xdd\x82a\xb9\x05\x13\xc7\xe5 \x10\xc1\x87cH\x8e\xc0\xa7\xd0\x0c'~}\xbb\xf9\xe6s\x0eQ\x07vU\x01r\x88:]\x16\x7f \xf3\x8d\xb8r\xb7\xab!\xa2[\xae~\xfe\xcaq\x84\xdaq\xf8\xe58B\x8eJB \x95\x14\x0c\x95\x14p\x0c\xe1\x11\x14t\\\xfe\xa4h\xa2\x92\xc2\xa4E\xe2(\x8cLrC \xe3^\xca\xda\xf6\xd2\x17r\x97]H\xfb\xc9NV\\\x08\x9a\x91 \x89\xa7e\xd7\x9c\xe6V\x8bM[\xad\xc9\xe6\xb6o5\x90\xa1\x8b\xe1~\xe5H=\xe5\xbe\x9b\xb1}G\xb1jP\xee;\x8a\x9cW\x1c9\x9b9T\x81N3u\xef\x05.\xcc\xca\x99G\xa4\xb8\xf5\x8c\x02\xc5\xa6\xe3\x08&\xb3K\xfa\xcc\xa9v\xa1\xdf\xc6s2\x8bi\xe3Nl\x92\xe5\xa0\xc5\x8a\x0fNs\xf5\xea\x0f\x98l\x9d\x9d<3\xd3\xe7\x92\x05\x8bb\xb7U1\x060\xae\xbdk\x9eK\xb1\xa9\"\xb4\xd1\xd2r\x15\xb5:G\x97Z\"\xee\xff\xa5\xd3\xfe\xb1\xc7y\xd1~\x9cO\xff\x87\x8e\xf3\x9b2\xcec%\xffi=X\xbb4\xebK\xc4x7-\x18o\xd9\xb5\xeb\xe9)\xbdTw\xfd\xc2\x85\x9b\xda\x89\x8b\x1c\xe2M\xf7Y\x0b=%J\x9d\xc6\n\xed[u\xd5\xdc\xaa\x95|G\xfeT\xfc\x925\x85\xcc~\xecQ\x8a\xa3\xed\x1f\xcb\x9f\x8c\xc3\xde\xf2\xb3,\x9cWl\x92\x1d8p\x1e\xc6\xd3\x94\xc0y\x92.\x8a\n\x01\xfdk\x14\x06$\xce\x08\xbc{sQ>\xfcq\xbb\xfc)tR<\x8d\xd9\x9c\xe4\x92)\xd7\xf9\xdd\xf2:\x89\xb2\xa6\xae\x8a\x97\xae%\xb9\x94\xbek\xea\xae\x1a\x1fp\xcb\xca\xbb7\xd9Y\\,\x19\xda9\xd2\xc2\xcdH\xc4\xe8=\xa9pS\xf3\xe6\x18\x94Z\xc3\x89\xdcp\xbb<\xba\x83\x85u\x93\x7f\x1d\x98|\x11\xc9\x04\xb1\x8e5%\x96\x0b\xd6\x1e\xb34\xd4\xc2\xee\xbd\xbf$\x99M\x9c\xc9\xe0\xb2\xb5\x0355\xf1\xef\x0fL)<8\x82\x18\x8eaH\xffR\x84\x97O\xac+\xba\x15X\x0f1\x0f\xd3\xcb\x85\x9f\xbeL\xa6\xc4\x8e\xd1t.\xd6\xf7\xd7\x1a\x0cG;\xbb{\xfb\x07\x87O\x99}KK_s\xc5\xa6\xadK\xc4\x95\xabq\x84\x00$\x0b5\xab=\x8c\x8bXw-I\x91\xe8\xc9p3\xb4\xb6\xb2\xd2\xb6\xc2\x94\xd7\xc4\xbb\x9aE\xfe<\x83'PPZ\xe5\xa5\x1f,\x08K\xa5@[\xd1\xcbxo\xcaLG\x154\xe8\x17)\xd1$\x80\x06\x11\xa7\x82%m\xc2\x82M\x9c@\xc6\xb2\xb8\x02\xed\xe7\xb55!zV\xed\xea\xc3Vm\xfb\x0d\x8fx\x1fO\xc2\x8e8\xea\x19\x02\xddw\xbc\xabi\xb2|\xf3\xaa\x9d\xa2f\x16\xb2Z\xaeN\xbepTGU\xd4\xd1\xe4\x08\xa1\x91`P\xfa\xf3\xf0:\n\xe3\xb9Yy..\xda`d'\x94\x8b\xecjP\\3\xdbw\xa1\xcd\xa3K\xbe\x02\x9e\x91FC\x08\xa8\x97Y\xe7L\xaf\xd4\xb6vF\x16\xed\xa7\xb1\x98A5\xdd\\\x12bi\xde\x9f\xe8\xd7\xe6\x9f\xf4\xdf\xeb\xb6\xc0\xb4\xb9\xb5\x19\xd1\x9aU4(\xbd92\xec~&qa\x96\xd7\xb0\x81%M\xc4\x03w\x7f#\x98\xda\xdb[\xf9)\x89q\xc3:\xb2vA\xb3\x01p?U\xc5\x0d\x83\x83jI\x91\xd2U\x11\x87q\x84U\xa4\xde*Y\xd9\x8e\x83\xd8\x8a\xf6Y\x98U>y\x02+z\x96\xaa(E\x90\xac\x7fj\xb6%\xb8\xe3\xfa8\xe7$\x7f\x19%\x19\xc9rq\xc6\xbcN\x93%\xed\xf2\x18\xa6\xaeZ\xb4Y\xa6\x9d\xfc\x12\xf4\xfeT\x1b\x97^\x82 \xca\x0b\x99I\xba\x84\x13y\x18\xc2\x9c\xfb\x87\xd5\x81\xd8\xe8\x1c\xfd\x86vLt\xb2\xabsa=\xfb:\x91Z\xc6\x98\xcc\xd6\xce\x0e\xba\xf2T\xcf%7\xba\xf2Y\x07\xa7\xc3V\x98T\xdc\x11V\xf7\xa4\xaa\xfb#\xae\x13\xd4\x8f\xda\xd6\xce.\xb6\n'\xf5\xb7\x86v\x8e\xca@\xfcl\xc5\xe4b\xc5\xe01!\xf7\xdd\x08\x7f\xa9P\x1b\x84W) \xe8\x96\xadvl\xc3nD\x14\xe1KC!ub\xf9]\xafe\xd3\nf&L\xe7\xd1\xb2\xe9\xc9Y\x1b.\xdd/E\x14\x19\x8d\xa5\xf5<\xf8\x02\x9f\xaa\x04\xa4\xdc\xc5\xea\xb0\xac\xbeR\xce{\xe6\x1d9\x06k\xe4\xedy{\x96\xaeMM\xc0\xe6\xab+\x86\x01\xe8\xdf\x13q^~+);\xd0\x19\xe0N\xac/a<\xa5|}J\xb2$\xba!,\xf7Z\x9ca\xae)z#D\xc8\x1ff\xf4n\x95\x92i\x18\xf89a\x9f\xacR\x92\x91\x18\xcbq\xf3\xffs\x9e\xec\x8de}{\x1e\x85~F2\xeb\xb2I.O\xac,\xf0#?\xc5\xb2\xe4\xd7\x82\xc4\x01~\xb7\xf4W\xab0\x9e[\x97\x1d\x92\x11#y\xe5\x82__ \xe1\x8c\xe5\xb9\xc8\x85'\xac\xcc\xe1\xe6}\xc3\xb4\xd3Z\xb6x\xd8 \x0f\x9d\xc1?\xcc\xd0w\xb7b\x1bS\xfb\x87\xcf\xf1\x978\xb9\x8d\x81\xa9.\xc0\xfa\x81\x13\xa8?X\x10f\xb0$9%\x80\x90KD\x03oHf\xac\x0cae\xfe\xf6\xfc\xdd[\\\x04\xde\x0f\xcaju\\\xc8\x17a\xe6\xe5\xfe\x9c\xae8~G'\x0f7:\xfe\xe0\xf1\xed\xf9;>\xa1\xf8Z\xfc\xbe\xbf7\x8b\x96@b\xd3\x15\xb3\x07^c\xb9.\x98[Ky'\xd7\xda\xea*\xa1\xad\xb5Z`,\xbctu[\x1fO\xb9\xf4\x18f+\xef\xd4Q\xf35\xc9\xc7-\xee\xea\xa5\xe4\xc5\x8a\x05k\x0f\xeae\xe5\x85\x8c\xec\x1cs\x1e\x95\x9f\x96\x1f\xf8B\x9e%hB\x8c1 \xaf\xb7\xb8\xaf\x08'\x9e\x90\xcb\x9eK\x93^\xfe\xa4d\xc6LR\x9f\xc6\x82\xf2\x1d\x17\xf8\x92\x0e\xab%-\xd6\x95ii\xe3Rc\x0b\xbb\\\x82b\x81W\x165\xf4@\xea\\\xd9\xbdx\xf4\n\x85\x8dvG\x8em\xdd~\xc9\xd4\xf8j\x8c+\x1f\xee\x1b\xd8\xf2\x1d\xc7cR\xdd&s\xaeM\xdc+\x99\xe3\xda\xfd\xfc^\xf8\x02G\x91\xdb\xfd=\xd8\\\xf6\xe6\xd3\xd9\x0f\xc5C\x1f\xf5\xb0cH\x1c\xdbb\xfda\xc6`\x92\xb3\xd4\x83\xe3ey\x82\xa9\x92\xd3>\xb0\xd1#\xfd\\\x0e\x15_\x0f\xdc%\x80\x19\xda\xb1\xbd\xb7\x7f\xa8\x06\xacO\xf8\xab\xa7CG+7\x08\x8dC\xef\x1f\xa3\xde\x10\x9f\xfe\xe1O\xcd_\xe5\xbel\x13\x89\x0bmD\xdb\xc1\x00\x1c\x81\xab\xf6}\x15\x11\xa7\x17\x81)\xce\xf1\xa5\xf0\xae\xfa\xb0\xb3Y\x90\x08\x05S\xb0Gz\xa5,_\x96\xf1}\x88!\xe1\xcc\xef\xfd\x8e`*\xed1\xd8J:\xb5`bH%\xeb\x19\xc1\xbck\x98\xe3\xa6@\xd5u-\xef\x1a\xe3V\x18%[\xb0\xbcj\x94EbHW\x8e\xa4\x9e;G|\x9c\x06\xe6\xb5_`\xb7\x90\xa7\x16\xf3\xb5\x88\x0e\xa0_\xbe\xaf\xee\xa0t\x1b\xe8\x18\x9bIi\xc6\xb2\xf64c\xd0\xb3i\xe0\xcb+\x14(\xd67W\xa7\x1f\x9f\xf6\xa9\xe0\xa1\x1a/\x1f\xd8\xea\xd4\xd0\xcd:\x91\xb7\xd0\xe6\xfayN\x96\xab\x1c\xf2\x04\xa6\x84\x1d\xf5E\xca\xbc\xd9\x84\xbdni`\xa0*\x03\xaa\xcdl\xf7\xa2^%:u\xbf\x1d\xc9\x0f\xf7\xb5H~4\xfc\xbf\x16\xc9K\x07\xa0^\x1c=\xdc\xd3\x82d\xf7\xa9F\x1a\x1d\xdb\x0d!u\xc1\x1e\xab\xa9M\xfaz]\xa3\xf2\xc1\x05f\xbd\xb2\x02\x0c\xe0\x0d\x99\xf7Z\x8f\xaa\xa6e\x81\xbf\xe8\x0b,\xca\x02\xe7\xfa\x027e\x81\x8f\xfa\x02\xcb\xb2\xc0\x0b}\x81yY\xe0g}\x81;8\x81)\x9cB\"\x92.\xd1\x99\xe5\xd9\x97~7e\x11\xbb\xc6h&\xa5\xb6W_\xe8\x8a\xd7\x9c\xc2\x18\x16\xf4/\xcb\xecd\xa7\xbc\x95\xdf\x1f\x9c\xaa\n\x03\x9b\x8f\x9a\x9ei)\"\xca\x1d:1\x98\x9a|\x03\xf3\xe0^)\x11\x8a\xae&\x11\xd3\xb1\x14\xf6\x1d\xaa\x7f\xe8h(\xb1\x1d\xc0)\xbe\x841\xaa\x81\\\xb8c:!\xac[k\xbf\x85\xa5O\xb14\x8caI\xcb\xd1JB{\x86&yc\x98c\x07\xb0\x9a\x13\x98\xc1i\x07c\x00\x12\x83_\xd1\xb8z\x0b?\xf9B\x96n\x11f\xb5x\x1e]\xe2\xd3\x0c\xf3#\x83\xad\xea\xd6\xba\xbe\xa3W\xe0g\x04\x06\xe3\xcerP\xb7\x8f\xd1L\xa1za\xcd\xc3\xf5k\xb6u\xf8\\\xbd\xb0\xf2\xd1c*\xd7\xc60\x92\xaf\x0ea\xb1Z\x996W\x99\xb8\xccu\x95b)f5C\xe7\xdc\xad\x94\xa3\xfa\x1a5\xdau\x90\xc4\xa1\xd5\xfebr\xd9r\xc3\xea\x02\x88\xb3d\xd47\xca\x86\xa8N\x91\x19\xae\xfe\xd7\xfc\x0d\xaa5]\xc0of.\xfb\xcc\xb6\xef\xbc\x1b\x96\x14\x1b7^u\x87\xb8\xc4a[n\xe6r\x8c\xf4\x89~sM\xff\xdb\xb8\xa6\xaf\x9e<\x01\xdf\xbev\x01\xab5\xa7(\xc9\xbc\xd7\xcci;\xf3\xfe\x02'0\xa2?\xce\xe1\x04v\xe9\x8f\x8fp\x02\x87\xf4\xc7\x0bZf\x9f\xfe\xfa\x19N`\x07K}\x86\x13\xd8\xc7b\x9f\xe8\xdb\xd1\xa1[\x93\xb70Q\xfc\xbaR09\xeeT\x85=n\xc3x\x9a\xdc\xd2!\xb1_\xde;\x0c2q\x82ZL8\x15\xef\xc7\x86\xcf3\x12a\x10e\xfaW\xfd\x14\xdf\x8dAL\x84m\x89\xd9^\x84\x99\xe5\xc8\xa6_Zq\xdb\x9c\x8b\xdb\xe6\xdf(n\xeb\xe2\xbc\\~b\x8f\xf6\xd5\xd3\x16\x03\x81\xd1S\x9eE\xcaN\xeb\x9cT\xda\xceI\xa5\xa6e\xa1e\xa0\xda=\x1aPBEx`\xb0\xb0\x96\xd9(w\xb5\xc7\x7fT\x901h\xd4\x83\xa44r\x1ak9\x9b \x89g\xe1\xbch)q\x9b\x86\xb9x[\x1f\"\x86\xa0g\x07r\xec\xd6T\xb1\xd0=wfym \xd1\xd8\xde\xdb\xd9Q\xa6\xa8\x9a\x91Z\x7f\xf4M\xeavH\x8d\xfb\xd4\x8b7\xe3>\xfd\xff\xc6\xb5\xa7\x8e\xeb\x8f_z\xe52j\x17\x15\xd6\x94%\xc3#\xc8\xb5\x860\xb9\xde\x10\xe6F\xcd\xd4\xa0\xb5NoDr\xeb\xb0\xea+\x0dUx\x8072I/\xb9\xf7\x94\x89\xe3\x01\xbd\x89\x00=\xa8\xde\xef\xef\x0d\x06\x07\xec\xfd\xfe\xde\xde\xce\x1e]I\xfc\xd7\x13`\xf2&z\xb7\xaby.*\x1c\x94\x95\x1d\xb2\xe7\xc3a\x95]J\x14\x1a\xee\x96\xa5v\x86\xb5\xcf\x87\xa3\x83\xf2\xd5p\xef\xa9\x03<\xbf\xd63\x18\x0e\x87\xbb\xc3\xe1\xd0a\x97\x04\xd3&T4\xbe\xba!\xcf\x02\x87\x9d6\xa11\x8a\xfe\x18\xc06\xc1\xb6 l\x9d`\xf9}\x07\x9e=\x83\xa1\xca\xbe\x8b\x8b\"\xbf\xbd\xfd\x9d\xd1\x80~5\x1c\x8cv\x10&FM\xaf\xce\xac\xb6I\xf5k\xd1\x9a\xeeS\xad)\xf8\x0dw6\xdd~bO\xfc\xad\xdf\xfe\xe5\x92\xfe?\xd8zz\xf9\xfb\xd0\xdd\x19>8G\xdbs\xc5\xe0\x8dR\xc5\xdb\xff\xf9/\xb6}:\xfe:\xf1\xb7f\xbc\xf0\xe1\xc3\xfd\xa4\xfc\xe98\xdb\xcaW,\xe7\xec\xeep_+\xb4n7\xc5R\xc4\xa5|\x88\x89\x1d\xf0\x14\xcc\x01\xe3\xd0w\xf6PO\x92{\x01\x1f\xf1\xf3\xdc\x1e\xe0\xb2\x88Dx.F\xabc|\xab\xaf\xcc\x946\x9f\x0c/\xeb\xb9\xaf\xe0\x140\x80\xea\x9b8\xb7\xf3\xd2D\xcf\x85\xe1>\xa5h\x1a\xaf\x86\xf4\xd5\x00\xe3\xb4\x16v\x8cD\x8f\x01\xcc+\n\xb8\xc9\x93\xe3g\xd6\xe5v\x1d8S\xe9\xcd\xbc\xfe\xaai\x02B/\xeb\x895\x06\xeb\x89\xbf\\\x1diB#[\xc7\xf86\xca\xb5/\x9f\xe1\xcb\xb9\xf6\xe5\x0f\xd6\x0f\xf4\xe5\xafE\x92\x1f5b\xd15\xa7\xed\xc6\x88S\x16\xb2\x11\xb6\xac-\xe0V\xba=\x84x\x93K\x06a\x86\x1eK\x9a\xc1\x85\xe1:\xfa\xe0\xd6dVR2Lq\x0c\xe6z#c\xb4`\x149H\xf8W\x06\xe6\xbeKum\x0coH/2\x89/y\xe4\x1bm\x19]\x0c\x91\xfa<95Z\xdb\xc5l\xc0=\xd2\xe9q\xa0[\x1368\x8e@.y\x04\xf3V \x11\xff\xb4q<\nSW~\xbe5\xcd\xa9\xeb\xdd\\\xf8xN\xd3\x9fE\xcc\"\x1d\xbek\xcfgWJ\x1e\x84b\xd4\xfa\xe5\x17\xcb\x81c\x18p\xcd\x16)\xe3,\x86.X\x7f\x1eZ\x8e\n\x99\x9f\xfc(\x9c\x9e\xc5y\x98\xdf\xbddf(>}\x81x3\x99\x92\x8fI\x88j\xea\xc2e\x9ajZ\x17\x96\x0eI/A\xb4\xd4\xb5'\x86\x9ee\xae\x9c\x18\x08\xbb\xc5\x06\xff\xd7\x1c\x03\x84w\xb6\xb1\x12I\xd80\"\x83\xa8v\xea\xc2\x8d\x0e\x19\xb51Ak\xc9\xd8\xa5\xa0\xd6U\xe0\xcbS)\xc1;\x8c\xf5\xf2\x98\xae\x1e\x19E\xeb\x0dn\x8f1K\xfb\xeai\xcbD\xeb{\x87Z\xd1\xfa\x81Z \x13\xad\x0fGj-\x8f\x93\xad\xbb\x92\xf4\xdc ^_t\x89\xd7o\xba\xc4\xeb\xcb.\xf1\xfa\xbcK\xbc~\x07'L\xb6\x8d\x923.\xe3f\n\x13!A7\x8a\xbc\xcd\xa2\xf5\xc5\xba\xf2\xf8+8\x81kI\xd8G\xbf\xb9\xae \xff~\xd7\xa5Q\xaaD\xechY)\x89\xd8\xd1+\xd3f\x82v\x14\x91\xdfA]\xd0~\x87\x82\xf6S\xb8\x831\xc4\x0eJ\xd4\xe9\xb1\x8c\xc2\xa5\x00\x8fp!&G\xc9\xb9Q\xa0X\x98\x04\x8aw\x8c\xc4\xb8c\xe2@!2\xfc\xec\xb8\x80\xb2\xc2\x0d\x9ee,\xe4\x02\xc3\x15\x06\x08\x10\x02y\xf1\xd6\xbe\xe2\"G\xa301\xf5\x02\xa6\x9eJ\xdc\xffi\xc1\xa2Y\xf5\xa5*\xb3\xb8\xeak\xa0\xaa\xc4\xf8\x06Uw\"\xdd\xa0\xdb\x96J\x00\x15\x9a}hP=\xdc\xf0\xa8\x01\xdc\xcc&\xc4\x1c\"\xda\x85W``KtM0R\xdf<\xf22*\x95\xed\x82\x85\x11\x15~\xec?\x9c\xa0\xe1\x0coH\n\xba\xec\xbb%\xf9\xe4\xa0U\xcd\x0f\x0e\x8fF\xf6\xactu?\xde.}\"\x9e\x19\x03\xfe\xaegP\xa7\xf1X\x8b\x99\xea3\xb7\x0b\xc7\x85\xd4N\xbd\x8f\xb0 \xa9\xf7\x1a~\x84\xa4=\x02\x83\xe0o,\x0b&\xe4\xd2\xa6c0\x02)gF\x03\n\x05}\x7f\x0f9w\x88\xa3_K\xd9\xe0\xeb\xc3u0 #\xc6O\xae\xb15\xddG\x15\x8e\xba\xeaU\xdc\xc3\xfa$_\x84\x95\xd1\xfa\x83,on\x9a\x19\xd0\xfab:\x0c\xa3\xb4\x1aq\xd5\xc0\x05r\xe3G\x8em\xb1\xc7U\xf5F# \xcd\xb1Y\xc9\xdc\x11\x93\xb1[\x1d\xaf\xf6\x9d\xa4\x905Q\xe3S\xdd\xe6\xfc\xfe\xa2\xc6^\x9e\xb37\"\x19E\xa3\x01\x91xb\xacMT\xb1\x08\xb3SV\x160\xf1\xf0j\xb9\xd0\x84\xe7C\x91\xd89\xf6\xb2\x15 \xceIDh/2\xcd#\xbc\xfb\xb7,i\x15\xf7\x89\xa3\xcc\xf4\xad. \x8e\xb8x\xa7}\xbb\xa0\x0cmi \\\xd7\x1e\xd25\xa8XH\xff\xfe\x80\xb1lb\x9d\xa5\x80|}H\xc3\xb1\xc6\xdeF\\\x0f\x18\xd5\xd3\xd4l\xeeB\xd8\xf7x\x85j0\xe2\xd4\xb8\xf5\xd3\xd8\xb6p\x95\xde\xa6\xfejE\xd21\x04I\x11M\xe3\x1fr\x98\x13\x16\x17\xd4r\xdc\xa6\x9fa\xb3 \xad\x17\x99@dt{\x0c\xfe\xa1\x86\xf4\xcd\x86[\"\xe3\xf2\xcdGiZ\x7f\x15\xaa\x9bO0\xae\xcd\x944\xcc\xf9\xae\xbe\xc9v\xbc\x81g!\x8d\x9fW\x0c\xdan\x17\x13f\xe6\xfe\x0f\x9d.\xeeU\x1d\x15:\xc1\xa7h\xe3\xcf\x08\x91J\xde\x8eqCE\x02l?\xe6\"\xf7\x0d\xc3\x88\x1f-R\x1c\x1d\xa8RBLy\xd1\xe4\xd1d*\xa0\xa4\x06\x18\xda\x96\"\xb2\x887M\x8e*\xa5\xfcb\xd2\xcaQ\xea\xa1\xa7\x0f\xcf$\x8f\xa6\x1f\xaco\xfa\xc4V\x16\xae\xbdL\x03[\x03\x03\xed\xba\"\x0d[s\xa9tx?\xd6\xfc\xb2\xdb\xcc\x7f\xae\x8b\xf9E\x92D2\xb3\xd9\xab}I\x90\xac\xda\xa7\x0b\xab\x1bu1\x84\xdcv[uZ\xf2+k\x80\xfa\x99-\x9f\xb23\xa6\xf1\xdc\x95\xa2\xe6\xd4\x0b\xab\xd1s4\x87\x13\xba\xb4\xa3\xeb1\xda\xe8P\xb4\x8a\xe4Qj\xc7\x8ekN\xdb_\x1e\x0d\xa2\xdaZ\x89\x1a\xe1\xfe\xd0h\xcf\x9a\x93\xdcb\x91j\xe8\x9cg\xe2\xae\xb9I\xad\xe7A@\xb2\x8c\x9e\x7f\x18\xab\xb9X\xd19#S\xd36\xb5\x90d\xe1u3\x86\x8c\x99\x87\x95\x0e)kn\xe4~Vb\x0dw\x84\xb5\xac\xc4\x1e\xd7\xa4\xbab\xbe\xa5\xc9N\xb7a\x83\xcb\x81\xce\x88,\xb6w\xf6v\xb5\x8a\x91}Uz[\xf0\xe2\xaa\xe7\x02J\x9f\xecCu\xafD\xac\xd1]u\xe4L\xf1\xaf\x96\x9ei\\\xadV\x18\xb0\xb3\x0eS\xb4L\x9b\x93\xfcc\x92Dd\xaa\xe6\x87Xh\xe4\x1a7%2)\x1f\x97'\xeb\xb2\xc1\x1d\x9cy\x98\xde\xea\x13 \x928\x08#r\x91\xfaq\xe6\xb3\xd2O\x9e\xc0\x0d0'\xff\xe1h\xc72YOP\xeem\xa2l\xdb8\xccY6\xcfq;\xe3\xc5<]\xc34\xbf+i\xdb\x8ce\x18\xc3\xbc\x18\xecX\xae}\xa5\x88\xa54\x82\xabu\x1a\xd98\xa9\x9a\x81S\xb0g(\xb5\x0d\x08%\x19\xcd\x9f9.\xdc\xdaH\xfe\x95\xdf\x9e\x18\xc3\xb0?\xa8t\xe6z\xc0 \xfc(\xba\xf6\x83/\xff\xbb \x05\xf1R\x92\x91\\\x11{<\x16\"\xf5\x9a\xe3$\x0fgw\xcf\xa3H\xad\xbd\x1a\xc8\xa5nI\xdd5\xe3\xff1\x1f\xe7j\x98\xd2\x9a\xb2\x9d6\xb8\xf2\x95\xebj\xfa\xd7\xd8\x07\xa2\x19\xcd\xba=i[\xd5R%\x1b\x83v\xdb\xa8\xeb6\xe35\xe2]-\x93\"\xce1\x15\x06lA.\xdf\xb7V{\xd5F\xdej\xe1\xa2\x88G\xeb\xab\x96\xc5\xfe\x18\x8ev-\xc4\x9c\xe2\xb9C\x7ffI\x9a\xdb\xd7\x8e\x0b\xab\xcd\xcdz%Ud\xba*\xaca\xce\xa3\x1a6\xd7\x0b\x17tR\x04:\x9b\xc4\x06\x0fQ\x1f\xe7\xe8jE\xe2i\x18\xcf_\xf2\xd9\xcb\x9a\x0c\x1c\xba\x156\x0b\x96\xb3_xQ2\xbfHVo\xc9\x0d\x89>a\x88'c\xa0\xa3\x1b\x1e\xbd\xd6\x90\x9e(\xf4\xae\x82\"MI\x9cs\xc6\x0c\xf3\x89c\x9e\x03?\xc8E\x1b?3\x16\x0b\x8f\xe4\x88\x8d\xa2\x11g\xcba\n\x03\x8be\x03,VS?',\xb8WD\x97\xd4{\x7fI\xe8\xaa\x14\x0c\\\x1e.\x89\x9dt\x19\xab\x00\x87F\xe6\xadH:K\xd2\xe5g\xac\xf7\xcd\xec=\xa1\x84\x85\x9f\xde\xd9\xa1\x8bF\x0d\xcd\x85\xcct\xa7 *n\xa5F\xcf\xe2)\x8b\x0c\xae\xe7>{D\xbe#\nf \xf1\xaf\xf4\xaf\xedO\x82K\x97\xef\xc2\xe2:\n\x03\x11\xb8\xc6V}>\xfe\xd4\xfc\x95\xd8\xb2\xdf\x19D*R\x9c\x93\\\x1a\x1b\x9f\x90\xac\x03\x8d\xf1\xad8oC\x87\xc2-4I\xfb\xe0\xc4v\xb4\x14z)\x89\x88\x9f\x11\xbb\x89\xa0\x1c\x03\xd6b_\xb6!\xa4Z\x9d\xba\x99\xee@v]\xa1\x86\xf8\xd2\xea&\xb6\xa1\x02i$\x16$\xcf\xd1\x89>M\xc6N\x88\xc2-E\\\xd0\x93\xe2\xd5R\xa1k\xd6\xf3\xa7S\x8a\x9c\xc3x~\x91\xd8w\x8a8\xef\xb6M\xcc\xc9\xa3\x0b\x95h\xf1\xfe\x1e\x16\xc6(Y\xb3\x0e\xb7:\xa1\x88\xbb\x93\x8f\x1c=\x86!b\xf0\xf6\x95HKO\xd7\xc2]9\xad\xba\xd4v\xdaN\x19{\xc3\xa8<}\xf3\xe2\xe4\xd0\x04\xb5\x03-\xfd\x08\xb9|\xd4\xd7\xd6tWG\x8d\x82\xa4\xb3\x06/`\\\xed,2V}\x81^Sn\x8cL\x19\xee\xcb\x9a\xeb\xb4\xcc\x17\xd3\xb2`\x97t,7^\xbd\xaaf\x05m\xfb\x84\xe3\xb9\xcf\x1c\xb5\x97\xe75\xd1\xdbP\xf2\x16\xc3\xec\x05m3\x8c\xe7\xbcQFFb\xa0\x81\x9c\x0b\xe8PZ\xe0]\xb1C\x03\x8b\xbfGm\x08\x17Ji^\x9c`N\xbc!\xd2\x98\xdaQ\xb5\x8ed\x16\x15\xd9\xe2\x85\x02\xd5[\x85\x19\x8a)G\xceT\xca\xcd\xe5\x88/\xf5\xf3g\x16\xb1\x88\x8b\x94L\xc3\xbe\xe5\xb4\xe2>\xbd\xb6\xb0I^\xb0\xfe\x08@\x9f\xe7\xa9\x9f\x93\xf9\xddz}9\xa0}\xd1gOQ\x00\\\x92T\x87\xf8\xc95\xdd:\xbe\xf2Es\xda\xc5GO\xe9G7\xfa\x91\xb5M\x9a\x9f\xf9\xab\x1e\xa9T\x03[\xb3\xe6\\N\x97\xf0[\x8f\xd5\xf5\xd2\x8f\x7f\xc8\xc5\xb2\x06?\xc6&@\x1cP\x10\xc6\xe0c\xe8E\xf25\x87\xdb\x05II\xc1\x87\xe2c\x08\x85\x1c\xaeI\x18\xcf\xc5\xf6\xf4\xe8\xb8\xa6%5\x80\xfds\x19n2\xb2>z\x81\xd6\x19>]C\xce\xb0\x11\xdb{C\xc7l\xb4\xc3q\xc0\x01\x9d!\xbd*\xe9\xf7\x07\x17,\xbf\xa1B\x02FytP\x06r\x13]s\xeaxU\x9c\x8c\x87G\xa84\xc5\xd3.O9\xcc~@\xc1\xf2T\x17\x1f\x07_\x8d\x86\xea\xab\xd0\x14h\xa2\xd4b\xa0\xcd_\x861!\xe4\xf7\xa5\xf6\xa4\xd3[^\xc8tUSWz=@\xd7\x8e\x95\xf5\x0b\xdd\x1d%U|\xaf$\xe5Q\xcf\xe4\xd7,\xe2i\xa9\xa0\xa9\xcc*O\xab1\x8e\x0d]]\xcf\x83\xe8\xbb*D\xc4/\xd9;\xb1\x1b\x18\xd2\xac\x9d@hW\xfa\xae\xd6)\xe3\xfd\x97\xc3JR\xe8H\x86\x00c\xd4\x03U\xddk\x9d\xc3\x7f\xc4\xfc\xad\xd1\xf7\xc7oG\xb3\xd4\x93\xb3\x97J\xc4O}S&\xfc\xd6 \xd0\x9a^Bgx\xfe=\xc6( T\x0d\x86\xe6\xaa\x84\x94\x0bTu\xf2T;\xb6\x9f:.L\xaci\x98\xad\xe8\x01\xf2\x12=\xa9-\x17\xac\xab\xdcOylVz\x1b\xfbyx\xc3\xfc+1\x96c\xf6\x8a\xcd\xf7\xc7\x94\xd0gd\xca\x9eRT\xee\xcf\xd1\x08\xee\xa5\xa94B\x1f\xca\xdd%j\xd8p\xdf\x18K\xdb\x10\x1d\xad4\xfb\xd3ft\x03\\\xd4\xa7\xd8i\x96\x01\x8e{\xe3Y\x0c\x00\xec`\xf0y \x8f=D\xc5\xecX\xfa&\x9e\xf8\x9a\xdc!\x0d\xe8\x08Y\x1d\xe6B\xf5\xd4Y\x87S\xdd\xc31l\xb08\x8e1\xb7\xde\xfb\xa9i\xbc(i\x84\xbd&\"\x80\x13\xa0\xdcU\xd8\xb0\x9aR\xf6\x1bZY\x89\xc8\x9d\x1a\xc4\x81<\xb1\xbe\xfc\x9f\x9acN\xedL\x96\\\xd5\xa7l\xc5\xfa\xf6J\x9c\xea=$L\xcdAmh&\\H \xd4\xd5\xda,\xc9t\xd5\xc4\xabw\x05}\xa1\xea\x8fl\x87\xd9\xf8a\x88\xcc:7#M\x08\xafM~r\x02h\xadf\x9e\x95\xc6\x8c\xb4r\xa7Y\x9e\xac\xa4I\xe9\x00\xda\xfa\x80P\xeaGH(\xcfZ@\xc1\xb0\xea\x0bD\xbd\xbc\xc2\xda\xa3\x13\xa6\x80\xee\xbd\xb8:\xc1\xb1\"i\x86\x99\xc4\xbb\xd7N\x98}d\x85\x19\xdaj\xb4\xd3\xd6\x8c\xfc\xadv\xbf\xd4J\xf7\x96\x9a\xd6\xa6\xa7\x07\xae\x84z\x0c\x0d\x96\xd1\x0c\xf1\x0f\xd3\x84k\xa3\xd3\xeb\x94\x15\x95\xd0\x9aebB\x146\x89//\xb5\x12\xd1j_;.dU\xe7\x98kc\xe6\xf9\xc5|I\xe2\xfce\xe4g\xbd\x1dNd\xb8\xa8\xbe'5\x1f.\x84\x8d!b\xda\x0d\x8fn\x10\x93[\xf5\x18J\x99\xec\xbf\xfc\xd0\xa9\xdda\"\x16\xf9A\x9d\x98\x06\x8c\xa6.\x8f3E&\x18\xfbR>f<\x9e\x8b\x98\xa4\x19\x908H\xa6a<\xafgD\xc8\x17$\xc6\x8d\x87\xc9\xd2\xca\xc3\x0fD\xe0\x17\x1fx\x03\x06e\xb88c\xb9\xc1@/\xd57\xffF\x18\x19\x18\xcc\x04\xf4S\x13\xb5\x88\x85\xc0\x0cCC\x8c\x9b\x1f\x84}n}\xdc<\x9b\xa6\x0f\xac\xa2\x16gp\xbd\x03\x1d\xae\xdb\x17\x0c\xdb=y\x82LO\xb9\x1e\xe4w\xcdC\xbe\x85P\xc3\xd0>\xde\xf5]N\xde\xf2l\xdd1FWA\xcf\xf3\xea1\x1cWv\xcb\xeaV\xfd!\x99\xcd2\x92\xff@\x97@R\xe4\x90\xcc\xe0:)\xe2if\x9a]\xb5MZ9l\x82\x8d\xb6\xfd\x03\xc7\xd8\x0e\xdbs\xfd\xdb\xc9\xeb\x99\xd1\x99!juO!\xd5@\nuE\x80\xae\x08n\xe0\xb1\xee1\x05\xb3\xbe'\xad\x88)oCD\xb4\x00\xcf|\xd8\xbaU4J\xe2\xda\xec\x8f\xf5\xde,\xdd\x04\xa1\xb84\x9f#@\xcb\xe8\x0e\xf7\xf7\xcc\xed\xde*\xf2\xd9a\xdb\xd4od^\x98\x9dq\xbca\xc7\x8ei\x13 \xd4bIh\x83\x1d\n\xac+%\xee\xd1\xed$\x90\xce\xd3\x01\xdc\xc3\x82M\x9c\xde\xe2\x10\xf8\xe1\x8a\xd3\x81\xc7V\xea8\xdem\x1a\xe63/HX\xa7\xdcL\x8d\xe1\x98\x11\x91\x84rZ$\xb9)\x1bUJi\x08\xfag\xf3\x04\x86t`\x18\xbax\xb4\xb7\x07O \x9f\xa4\x1a=\xd7Z#\xd4$^\x85r\xdd<;\xa1\xbc\x95\x89jy^e\x96\xf1#\x0c\xbfB\xf8\xce\x82\xc8O\xe7\x842\xa8~\x0cK\xffk\xb8,\x96\x90\xa1;\xc7\xe0+\xe5\xb3}9\xcd\xf5p\xdfAWNJ6i)\x9e\x12a\xdf\xf7\x1c\xd4\xa2u%J'\x8b\x9c;JH\xcb\xf5\xdb\xb4\x0f\x92\xd6\xdasHe\xbc0\xfb)$,\xd0H\xf31\x9d\x88\xfb{ \x06\x14/\xf7\xb4\"0\x9b\xbd\xd5\xb8\xd6W\x8c\x9e\xa5\x13r\x80\xb4\x9c\xdb\xa1\xc0\xa9\xcd\xb2'\x9a\xedU[\xbe\x1b\xc3\xa3#\xa7\x14\x0d\x1bOB\x14\x88Z~\x16\x84\xa1\xa5\x17\x8b\xb2\x12\x91\x9f\x87\xf1\xb0\xb5\xc8u\x18\xfb\xe9\x9d\xa1\x08H\x12(\xfdq\xc2*A2\xaf\xad\x95\"\x9fm\xb5\x96`\x84vg/^\xdb\xc41\x02\x1c\xaa\xe6\x82l\xd4\xde\x9f \xdb\xea(\x91\xcf\x86\xfb\x11\xe9*\xb3\xd5R\x08\xaa~\x8f\xe0\xc7v\x08.\xc8\xd7\xeeZbx\xf6\xec\x19\x18\xac\xb6\xf9t\xfa\x19\xd9\xdf\xed\xae\xea\xb7.@\n\xa32cE\xa8\xedpzO\x0cp&\xcc\xc6\x1d\x95;\xf5\xe8f.\xcf\x8f\xd6\xf8T\x95\xbe\xeb\xd1\xd7M\x1b\xc7\"\xf6\x16\xd1F\xc6\xe7riz\xfc\xb9\xe2\x10L{5\xba\x94\x98*\x83\xc6\xa1B\x01\xa4\xa4\x189\xc0\xb64\xd3h\x10\xb7\xc4\x94;L\x99\xf0\x1cOn\xe49\xe1\x99,\x91;\xc575\x11\x1d=\xdd\xb7\xca'\x87 b\xa1I\xcf\x1cV\xe1f\xecB\x98\xbd\xf7\xdf\xdb\xb1S\x16K\xf8\xe1\\\xca\xb7\xb6`\xe8\x08\x91\x80(T\xbe\xdcDZ?\xa6\x07 \xe9p\x84@\xcb\x95V8\x00\x8f\xfe$7\xdd\\\x19@\xa2\x8c`m1\xa3\xd7\xcc\xcdm\xf4k\xafk\xf9A\x8bH\x8c\xd9\xdd#\xcf>K\x93%\xe5\x15S\x07\x15\xc35\xae\xac\xc6J\xe5\x15\xfb\xb45\x841\xcc\x95\x15eX!Z\xe1\x13\xaf8\x87'H\xeb\xb8\x069\x83\xe9\xd0\xad\xc4\x17\x92\xf6\x97\xc7\xd9\xc5\x08\xa4\xa7\xadE*\xf5\x04\xe7Z\xb5\x85#?\xcb\xdf\x18>\xc0\xb1O\xf2\xcb\xb6\xd1ky\x97\x1b?* {\xc1\xae0\x08Q\xce\x843Z\xfd\xe8q\x15\xfe\x06d\x12\xb2\xf0l\x86\xd8o\x85\xb4p\xf5%2\x89\n\xd6O\xb1\x14\\\x95\x89\x14\xd8\x89\xc6\xf8\xef\xb4\x8a\xc6\x99*h\x14\xe9!~\xb8q\xa1\x15>\xe0gY\xfd\xd1\x96\xf4\xcc(/@\xb2\xb6\xa2\xd8GL\x18X\xddw\xee+\x9fEO-`\x9bEQ\xe5\x7fc\xfc\xab\xd9o\x8dG\x8a`\xd6\xd4Q\xde\x8dai\x92FX\x00{\xe2\xa5\xc4\x9f~~\x13\xe7\xc3\xfd\x17gv\x0e?\xea\xdc\x18\xf5\xfb\xdc\xa8E\x16\xce\x8e\xa6A#M\x87j\x98#\x08\xe1\x18\x8a#\x0877\xf5L\x19\xf0\xc6px\xa1\x83\xfdG\xad4OQ\x1cp<\x1c\xc2\x16\x04\xadr\x1dQS\xf9!]9\xb4\x9b\xa1\xe3\xb2\xcfa\x93\x03(+\xe7-\xa0\x001V\xc9\x91\xec\x16K\"\xc1j\x0ca\xeb\x84\xf7\xc6\xe5P0 g3lb\xd8\x84\x0c\x9eAQ\x9e$\x05lA\xe60\x7f`\x84\xda3d\xe6\xc2\xad\xad\xb6!\x97\xc4\xf3\x8c\x07\x0b\\1\x1ep\x05\xc7\x90\x1d\xc1\xaa\x0d\xe8P\x03[{>\x1cCz\x04\x9b\x9b~\x1b\xfa\xa0\xc7\x84\x9c\xf7\xa2\xb8\xce\xf2\xd4\xa6|\x82\xef\x02O\x8d\xa1_X8H\xa4\xd6\x8a\x8a\xa0\xf0\xf5e\xc9\x84\xee4f\xba\xdb\x03\xe9\x89\xcaz-\x9a\xeb\x8eE\xc3+{a\xbf\xa6\x1bJ^\x16\x0e\xaa\xe4\x9a&@\xa6\x96\xae\xfa\xb6d6\x18(\xeb\x94smM.]Y\x14V\xb2\xf2L\"\x963\x87K&8\"r\x02\x94\xb8C\xa2\xafK\xa8\x98\xaf;\xe8\xdb~\x83\xae\xc1\xa6W\xc5g\xfd*~a\xff\xb6~\xa7\xbf\xf6\xad\xbb\x97V\xa3\x92W\x96\xde\xb6|\xd6\xa4\xadF\xa4\xa0\x15\x1b\xb6\x9d\xd3\xd3i\x84i!\x1c\xbe \x19+!\xcd\x9f\xcf\xf9M\xcaO\xc3!\x8f\xdaL\xd1\xc6\xde\xbe\x0b!\x9b\xf6\xc4)\x7f\x9a4yF\x94\xfc\xf0\xad\x0b\xfe\xbc\x8d\x9f\xad\xb3\x10t\xd8q\x8d\xc5\x84SH\x91\x07yq\x97\x13\x91\xf1\x9dbU\xf5!WQ\xe5u\x9b\xae\xb6~\xbdl\xeb\x17\x05\xf3;?_x\xcb0.i\xc6\x1e\"[:\x9f\xe8\x1aq\x04 \x8an\xdb\xd0&\xa5\xbd]\xb4\xafu1F\x07\x99$-\xc9\xe5\x03\x11,\xc1X\x82\x9e\xe0\x11e\xa5w\x9e\xc2)\xec\xc2\x98\xdd\x8dv\xe0\x14v\xf8\xdd\xf0\xe9\x10Na\x04c\x93\xe8\x05iE\xd8\x84\x19\x1c\xa3\xb0O\xc8\xeffm4D\x9f\x04\xb8\x11\x1c\xc3ptX\x12rQ\x8b^ \x04\x9da.\xd2'-.m\x8er\x19\xc3\xa7#x\xc2\x88X2\xa1\x83\x1b^:L8@\xd9\x17{g\x08O r\xe0\xf8\x18\xf6\xe1\x1e\xf6w\xe0 %^\x9f\x89\x0cb\xd8\xdd\xec;t\xd7`\xf6).\xb9\x7f<3>\xde\x8d.]e(!\xf6\xbe\xfe\xcc\x97F4\xdc+G4\x1c\xc1=\xd8bL\xf2\x10}:\xc4\xd1`\xf7\x80\x7fw\xcc\x13\x96\xdd\xdf#9+%x\xfb^\xe3\xdf}\xfc\xf8\x8b\xf2ng\x0dh\xd4\x9f\x15\x06\x08\x1d*\x10\x92@\xe6\xd7AV8\"\xef\x1b\xad\x89\x82\x8c\xa5\x92\x1bI`\xd2\x0eQO\x12\x97\xc6X\x94/\xc2\xcfi\xdd;.\xee\xe4!\xc5s\x81\xdc\x9e\x1d\x94i\xe4\\H\x19>\x0f\x98\x18u\x00O\x00\xf3\xc5\xdd\xb3I\xe4\xdc\x0c\xcb%w\x0f<\x95\x1cer\xc4w\x18\x1bg\xf3\x04fM\x8co\xc2\xd2\xdd\x14\xc9M\x19\xa7\xa9M|\x8a\x8aq\x8a^\xbe\x94$\x9f&\x1d\x1d\xb71>\xe7b\x10\x9d\xde\x02$\xdd\x85\xa5\xc9V&\xaeT\xaf\x0c\x04(\xc3\xa2\xa4\xa8=\xa4\xc7\xeb\xe6I\x9f\xce\xf0\xe3&u\x99j\xeeK\x07\x11\x157\x81l7\x8eO\xf9.\xf7\xb8b\xe9\x84\x1e\x0e\xb9w\x1e%\xb7\xe5\x93\xf6y\xd8$U\x84N\x82\x12V\x0dC\xc0\xba\x95y\xa8\xba\xb37\x1b\x1e8\x90{o\xde\x9f\x7f<{yq\xf5\xee\xf9\xffw\xf5\xe2o\x17g\xe7t=\x0dL\xb2\xb8\x139\x89\x0e1\x98\x05\xe9\x9fwy\xf6\x18\x83\xdf\x0b\xdf\x1a\xc5di\xd8a\xa2R\xb3J2\x9fie)\xbd\x00\xb0\xe5\x18N\x92\x1e\x01\x13\xc4\xc5{\xb5\xdb\x94\x1f\x89K\x8f;\x1e\\\xd8\x1dqZi\x96$\xb6c\x14\x87\x12\xca\x901K\xd3'O\x84'x\xf9\xcc\x1eb\xc2\xbcJ\xa9\xd8\\\xaa\x9d\xd9\x0d\xf8\x1864\xb2\x93\xfa\xbab\xf1u\xbe\xbc\xf3\xbf\x96\x91\xa3|\x1b\x05\xcb\xab$\x89\xce\xc3\xdf\xe8t\x1e\x0e\x9fb\xf2\xa1+\xeea\xd3\xb9\xe2\xb5\x13[sJT=\xbf\xb8`\xbb\x87\x1f\x8cT\x7fd\xf3\xf0EZ\x0b\xcc\x16!\xb5\xec Y\xeb\xa3v]\xd1\x91k\xcb\xb8\x06\xfb\xc9st\xf5\xa7\x0d\xb1_\x18\x1cJ+!\x13\xdetY\xa9Xa_hmM\x98\xe1K\xdd\xd5\xad\xcd\xccAV\xec16\x08\x02ZGc\xdf\xd43\xd0\xc9\xb5\xd5\\j\xb5\xd0B\x0c\x933\x0c\xd2\"\xd5\xa5\xbc\x07\x99\xc4\x97FvK\xc8\xa5j\xc7\x83\xad\xcb\xb3\x0f\xdcV\xdc\x84\xee\xcc\xbd0\x13\xe7>7F1\xb3\x812\n\xf7\xff\xa0\xf9\xa3\x97\xcf\x8c\xb9Q\x13\xce\x19_\xe1 \xdf\xb1\x16\xa1Z\xb7is\x91J\xce\x1e'\xb0p\xa1F\xe9I\xc7\xe7\xc6\xa0\xfe.\xbb\xf5W\xc3\xfd\xb6x\x9d\xa0\x06\x0fh\xd3\x13\x11\xad\x9eH6\xd7\xe4=\xc9(\x89]\x99\x0e/\x8b(\x0fW\x11\xa1\x10\x1c\xeeo]\x87\xb9\xf6X\xac)\x1a\x06Gh\xbeK\x8e\xd8\xf2\x1b9p#\xe2\x9f\xba\x98\xb4R\xc7\x7f e\x82\x1cB\x04\x04\x10\xeb`\xd9\x19}W\xb0\xec~#XvF\x8f\x02\xcbn\x03,;\x8e[=\xa2`b\x7ftZ\xb85\xa0\xb5\xbf\xfb]\xa1u\xf8\x8d\xd0\xda\xdf}\x14\xb4\x0e\x1b\xd0:\xd0Ck_y\x9d\xe8\xda\xf9\x83F0\xcc\xe6LX}a\xfc\x16x&\x8f\xa7\xf2(\xb1\xfa\xd5\x8b~S\xb1Z\x890\x90\x90\x1f\xa2\x19\x1e.\xba>M\xa0\xd9(\x96>>\xa1\xbd\xe5w\x9d\x1f\xe3\xeac \xa4\x89\xe4\xcc%\x19(\x1b\xa5\x1b\xd0\x83\xee\x14\x17\xef\xc5\xc7j1\x9b\x9c\xac\xa0\x0f\xb5\n\xbd(Vq\xf1\xc6_\xae\xd3x\x1b\x9d+.^\xef\xf3u\xeam\xa5\x8e\xa1\x1f\x85,.\xde\xfe\x87u\xda\xef\xb4\x1d\x86\xaa\xe2\xf3u*n\xa1\xc6\xa1\x17E\x0e=\xa9rX\x872\x87j4\x17\xfdF\xd3I\xac\x03\x94v\xd1Z\xc6\xfa3\x8b\x0eUz+\x8e\xb51\x14\xd4\x8b0w\xc4M\xb0\xac\xbef\xd3\xa0\xa5\xc9\x1eD\x0c\x12\x1c\xac)\x0cI\x1d\xa9\x93_\x0b?j\x8f\x1f\x01ZiC\x87lA:\x0c\x85\x8df\xeb\xc1\xc3\xcf\x80\xfb{\x8e,KY\x88\xde/\\\x19E\x18g+L+\xd6\xefd2)F\x98\xffRC\xca\xdf\xdaqq>=\xe3f\xd3%]Q\xba\xf3 \x8e\xe4\xfe\x92\xde\xd2\xcf\x83\x85\xbd\xed\xfd>z\xd8\x9e;\xde\xdf\x930\xb6-\xb0Dx\xb0\xb22\x9e\xec\x89\xa5P\xf7<\x0f,\xc7q\xc1:\xe6\xf4\x06\xae+]6\xf4:\\\x0c\xf2\xa4N\xa3\xf6\xef?\xd5*\x8fW;YU\xcfmf{\x8e\xda\x11\x0e\x90\xb1Z.-\xed\xb6\x94\x17\xcc\xd6,i\x9c\xa8\xb9\xf0u\xa7'pY\xef\xfd=\np\x06,\xd5\x9cr4\xeb)>\xee\x8f\x9e\xd2G\x80\xf6\xd1\xa6\xf1\xa6\xf0\x8c\xf7'\xa7\xbfZ\xdd\x84\xaa\xf2\x9d.\x04Je\xe6RH\x07\xb8\x10\x97\xbf\xd2\xf2WR\xfe\xaa6_/\xf1^\x88\xae\x03[t\xf5`\x0e,\xd8\xa2\xcb\xa9\x90%z\xa1\x0b\xbe\xc3\xcc7\x10\x9c\xa5^0\xe1*\xd8\x9ae\n\xd3\xec\x0e\x8e`\xc6\x0ci77gf `4\x991 `0\x99\xb5J\x00i7ia\xd6KZ\xda\x8c\x83\x1f!\x01\x0c\xe1\x18\x8d\x90Q\x02\xe8\xc31\x84f \xa0\x8c\xa5\x82\xa8\x98\x92>\xb1\xc6\xa4\xb6\xb8q.\x82\x92\x9b\xe3\xdbf z\xd3\xba\x7f\xad\xc6\x96\xf5\x90\x1a\x98:\xaf\xad\x11\xc9\xe4\xff[\x1b\x1a\xb66\x84\x1e\xfaz\x0cf=\xbdp\xdf\xd4E\x10\x86\x1cm}\xa5\x10?X\xac\x0f\xda0@\\X\"\xe2\x87\x984\xd99\xba\xa8\xf1\xe5\x1f\x1a\x03\x03\xa9\x91\xfe\xd4\xd8t\xa6\xeacz&IB\x07s\x1c\xcc)\xf9\n\xb2x\xa1'D\xff\xde\xc1\x0c\xe5\xa5O\x7f\xce\xed\xa9\xf7p\xc2\xf5z\xc9\xda\xeeU\xadud\xaf\x17\x17Fu\xc3\x1d\xee\x8e\x96\\\x02\xea!\x9e`P\x9e\xe3c8\x84\x1f)\xfd{\n \x8ca\x08[\x908\x0e\xdahk^\xf4\x1a\xf0\xfb\xb5\x06\xbc;z\xba\xfbt\xff`\xf4\xf4;\x8dz\xd7<\xea\xbc9\xac\x1d\x1c\x16\x03F\xaf\xc1}\xea\xbd?\xbeea\x99\x96j\x0b>y\xf4\xfa|U\x1bQ[J\xc6\x90\xeeB\x04\xc0\xc0e\xa0v!\xe1<\xae\\\xc7h\x87\xbd\xa3\x10\xd8\xed\xd5\x87\xb7\x8f\xee\xc3\xa1\xa1\x0f{#\xf6\x8e\xf6\xe1P\xe9\x83|\x97\xa9t]\x1f\xfb\x1d\xe1\x15\xd7OI}\x02\xff\xfd\xdf\xc4U\x83`\xe6p\x8a\xa9Z\xfe\xfb\xbfs\x97\x9d\x14,\x0c\xe5&=\xb5\xcb\x1dBD\xc4\x11B\x0f\xf6\xf2Q\xeaT!\xc9\xec\\\xf9&\x17\xdf\xe4\xe57\xb9\xf4\x0d)\x9f\x10\xc7`\x03\xecT:\xcf\xd2\xea\x1aaa\x0c\x90\xb9\x96\xfc\xa4\xa4\xc0`K\x8d\xcb/\xae\xb8\x0c\xf3\x9b\x08q\x86\x81\xbb\xa81\xe7\x9cNH8\x19\x13S\"\x80\x0d\x04)\x00\xd2\x95\n\x07\xaa\x85V\xf7\x80P\xd8\x0f\x11\xd5\xe0\xedYO\xb9\x1a\xe1\x92\x19!\xb8A\xaaM\x90\x13\xb2|\xa3\x05\xf7\x89\xe56!\xdcgoX\x12G\x9b\x9bt\xd89\x17\xae\xffxB\xe9\x1e\xe7\x88\x13\xb5\xec\x1b\xd8\x84\xf0\x12~\xd4\xb9v\xebIY\xfd\x88_\xfccF\x0c\x9b\xb0\xb5\x95\x8bq\x1f\xe1\xd2\x1et\x0c\x97~\xf0\xed\x03>\xec\x83\x10\x84\xc6\xa9\x1c\xe3\xd0U\x15\x1cl\xe2\xfa\xb48\xdco.\xab^\x8d\x8e\x0c\x8drK\x0f\x04\xca\xf0\x12\xcf\xfc~\xfdhN\xf6\xb7\xf5\x03\xa9\x8dZg\xfa\xf4cg\xf4Hx\xec\xaa\xfd\xb0\xcd\x00\x91\x1f\x8d\xf0\x11\x8b\xf37\xdc?88\x18\x0d)\x17Q\xbe\xdf\xe9\xd9\xedG\x82\xaf\xd1\xedF\x1f(gc+#\x18\xee7\x87P\x1b\xd5\xcee\xab\x08\x9fv\xfb\xff:\x8c\x06\xcfN\xf8\xe7\xc3\xd1\xa1\xc3E\xe1[\x9cv\\%\xb76\xa5\x12(X\x1d\xc7\xedF\x07\xff\x10\xf4W\x03\x8c\x84\xdb\xd2\xcb#$/\x9bX0T\xb0`\xda\x0e\xa4P\x03\xa4\xd0\x08\xa4\xb0\x07\x90\xbe\x13\xcaD\xdf\xebr\xc5\xa3:\xefG\xc0\x88\x10[\xd2>@\xaf\xd3\x9e\xd8u\x0d\xe4j\xc4fM8\xde\x88\xd8\xaaF\xe4b\x84\xfd\xce\xe8`\x9f\x0e2\x86S\xc6\x08\x0d\x86\x07\xfb\x03\xb8\x87\x18\xc6\xdd\x14\xc8\x1a8\xfa\xd1\xc3a\x83\xb8\xaf\xa1\xf0?n8\xdf\x0f\xd5\xaf\x87\xe9\xebx\x92>\x1b\xed\xf6\xean?\xe8\xf7\xef.\xb6\xdc\xect\x0f\xe4\xde\xd5\xdd\xd7Q\xe2k\xb0\xfb\xe3\xba\x9b`\x95\x95\xa2ac \xb8\xbe^\xdd\xf8^Pktc\xd8\xb7\x1b\xaf\x92\xe2:\"\x8f\x04\xc7ag?\x06\x82\x01\xed\xd7\x8fG\xc2\xa3\xbb\x1f\xc3>\xfd@\xe6\xd9\xc8\xcd\x18\x848\xc8\x86n\x92\xda\x01\xc7\xacXPm\xfbF5 P\x0f\x93\xd8\x81-\x8a\xf2M\x8e(\x899\xc6_\xd8\xe2\xf4\x81\x1b\"\xafBN\x13AI\xc4\x8dc\x92\x15eD\xc4 \x10\xd8\x86\x84\xc9\x81\x8c\xe8\x8d\x16n\xc5b%$\xb5d\xc2?\x10\x921\x161BSc\xa4$AS\x88\xcfJ\x88nm%\x18 \x8e\x93\n\x1a\x90&\x02\xa4\xe1w\x03i\x83\xa8h\xb7`\xd1\x00U\x85%E\x16{{.\xeaQ\x8c\xf9~pv\x10\xe4\xb3(IP\xd2\xcd\xb1\xb5\xbc\xca\xb8\xc9\x7f\xaf\x81\xe8(\x90o\x1e\xcb\xc8e\x92\xe3\xb6\xd1\x9cj\xb6\x87[\xcd\xd9\x90\xcd\x19\x8aH)M\xf5\xf7Z\x03,G*=|z\x0e\xb27\xa5\xfc\x07\x0e\x92\x8fF\x1d$\x1f\xbbf\x90\xc3\xb5\x06\xa9\xa3V\xbey\x90\xbb\xae$\x12\xef5RF\xb3\x88\xd1\x8ev\xa5\xe1\x8e\xaa\xe7\xc3}\xc3\\k\x963\x85\xcc{\xfd\xf4\xb7\x92E\x12d\xfe\x80\xe9_\x1f2\x06\xa8\x0c\x0dP\x19\xe9\xd7\xccN;d\x86\xbd!\xb3\xe6\x11+\xa4\xc72X6\x8c\x06G\x02\xd57\x8e\x07\x0c\x1d\xad\x97\x9d6\xce\x96\x84\x1d%[\x1a7o\xbd=\x18\x9e\xc5\xfa\x83\xa5#J\xef#Op_:n\x88\x10y3\x89z\xc1~\nsLv\xb6\xd3\x01]\xe2\x97\x05\x86(r\x95s\xdf\xa6\xa7\x94\x0f\xcf\x9e\xc1\x80\x9e\xa3\xc5w9\xaf\xd6\xa4\x00\xfeO\x99\xe8\x16*\xe2\x9b&[\xcc\x85D`\x84\x15\x81\xb1\xf6\x8co\xfecf\xfc\x0f!P\x86\xa3\x03\x17\xb6\x86\xa3\xc3\xb5i\x14R\xd3!Q\xd02\x9f\x84\xe1\xb7\xd0/\x7f \xf9\xb23:\xd8\xa7cE\x19B?\xd4\xfe\x07\xd20\x7f \xf3\x88\x81\xfe\x81t\xcc\x1fH\xc6T\xf9\x10\\%\xedA\x8f!\xb7\xcfm\x0f\x12\xa7F\x12}\x13A\xf3\x07\xd23f\x10\xd5\xb7o\xcdHB\xec\xe2\x1eP\xfc'\"~\x0c\xf2\xa7v(\xbeR\xe6\xac\xcb\xab\xa2ji\xdd\xf9RZ\x1a\xf6j\xc9$Ejo\xea\xedc\x06e\x12\x14\xad\xd5T\xe7\xa8\x82du\xb7\x1e\xddR\xa5\x9b\x1c\xa0Cd\xe9\"X\xd9\xd5\xe7\x8a\xa7\x97\x94\xa5\xa42E\x90\x0b\xd0\x0f\xf3\xb2F\xae\xe2HK\x12\x10\x9d\x17\x98\xf7eWz\xa7\xb0\x11 \xa5\xea\xa0\xdc\xad\x8e*\xf26\xc3\x9b\xdcO\xe7$?\xcf\xfd4\xef\xce\x86Z\x9a\xf1\x003\xd6T\xba\xa1o!K\x8a4 k\xb4\x90\xb6\xf5\x97\xd5v\x16O\xbb\xebJ\xeb\xce\x17%\xf4\xeb3*\xd9_\xe5\x18{iK\x9a\xa8\xda\xcbM\xadU.\x12\xb4L\xbf\x95\xea\xe3\xd6\xe3\x1cTn\xa8\x18t\x99+\x07\xb1\xc5\x96\x904 \xb0t \xc3#HxV\x83\xad-4\x0bK`\x13\x10I\"\xae\xa3w\xba\xb8/\xa5\x93\x11eA\x86d\x07X\x18\xaf\xf5\xb2\xfe\xb105\x8aY\xda\x1a\xedk\xf3\xb9d$\xaf\xf2\xb8\xd4Lubf\xf6\x14:\xfa\\\x98B\xef\xd7\x86\x08fa\x14\xad\x87\x084NWkg\xb6\x16\xe9 0\xa4\x06?6\x95\x1d\xa2M\x9f+\xe1\x85\xe6'.\xcf\xba\xd1\x95\x19 $\xde\xaa\x16\xb0\xdcdy\x04\x18\x80\xe8\x18m\x8c\xc5Am\x88\x8ff\xce\xb7\xaa&\x9b\xd1\xe4\xc33\xf9\xb3\x97\x19\xbf\xfb&\xf36\x80\x1d\xdb\xad\xe7\x02NM^\xc5&\xcf\x8fF{\x95\x12`:-\xc9\x9b)\xcb-\xe2T\xe9\x17a9\x00n\xab\x87>\xca\xb5A\x08\xbc\xe8OB\xf8_P\xaca\xb3\x977b\xe4\xd4\xfb@\x07\xfb\x19N`{\xf2\x9f\x9b\xbfl\x0f\xb6\x9e>\xdf\xfa\x0f\x7f\xeb\xb7\xad\xab\xcb\xed\xb9\xc9\xf5\xe6\xd7\xf6\x10\xae\x80\xca\xd9S\xb0\x06\xe8\xf4_O\x13:V\x1e\xd4\xfbfh\xf0\xb5Q\x01x\xa3\x0f\xd0\x96\x03\x8f\x8a3\x84\xed\xce\x1c\x97\x95\x83L\"\xc2\xf3\xeb\xf2:\xb4\xa7P Y`\x9bFb\x07\x07\x9ea4\xef=qD\xef\x1d\xec\xec\xee\xb6!\xdc\x90\xe7\x873\x97\x80r\x93>\x83\xbd\xfd\x9d\xe1\xd3\xae\xc2\xf4b\x89(vh\x7f\xb6\x86\xb43<\x99\xc4h\xe7\xa9\x0b\xc3\xa7C\x17\x86\x87O[\xd0\xba\xb8\x82$\xce\xc3\xb8\xd0\xe7R\x12\x979{\x10\xf0\xbe\xfb R?\x19\xa5z\xf2\xf5O\xd4{\\$\xed-u\xb6\xd2\x9e] \x97\xc9\xfe\xce\xc8\x98BP\\\xfd\xa0\xe2\xfe\xc1]\x8e\xb9\x8f\xc6>lR\xban\x8b\xa7 8>\x86!3t\xd9\xe2\xa3\xd1\xd6\xc0O\xc5\x84\xf3==\xc6c>\xc9\xab\xfd\x1b\xb3D\x15]\xfb\x8c58d\xd9Y\xba\xd2\x1f\xf0\xce\xc4\xad\xe3\x10\xf37\x1a\xec\xf6l}\xb4^\xeb\xf0\xec\x19\xe62\xc0\x00\xdb\x98\xd0 \xa6w\xa3\xc3^\xdd\xc2y\xea\xd7\xaf\x9d\xf5\xfb\x85I\x17F\xa3]\x16\xc2\x03\xf6\xe1 \xed!\xf6n\x8d\xbev\xa0F\x1c\x07O\xd9\xa0\x8b3 \xd2i\x05\xc9\x94\xc0*1x\x91\xc9U\xb2\xf1\xee>b\xbc\x87t\xbc\xbb\xe4\xeb*I\xf3\x0cN\xe0\xf7\x07\x89v,\xc1\x106<\xd2\x1b\x9b7#\xf9E\xb8$I\x91\xc3\xc2g~\xa0\xd7\x84\xc4 B\xe6W\xf0~\xd04\xe0w7\x10D\xc4O\xbf\xa1\x89\xa2\xb9\xe0\x19n\xc5\x18`e\xef\xab\xe8\xc2\xe5#\n>\x95o\x16T\xe3\xc9 \xf3\xe2\xda`\xf9\x8e5\xf5\xd0C\xb6z\xecv\xd4\xab\xcf\xb7!\xaab_\xd4\x97\x81\xc8\x0f\xa17\x955\xa6\xef\x10U\xb2\xa5SF\xcb\xd79\xfc\xb7\xb6\xd0\xac\xab\x94\xd2v\x07\x0f\xa8&l\xa3Z\xac\x8d\x95\xa0\x1d\x03f\x9d\x11\xdf\xc8\xbc\xa6\xb4\x10O\xe5\x9b\xb1\x8av[\x13k\xd0\xeaU4-\xdf\x19\xe6\xc9\xd4\xa9\xda\xe2=\xad\xdf\x8e\xd5,\x89\xad\x1d\xa3M\xa8Y\x15\xcb_\xb6\xb4\x9a\xe8\x1e\xe7\xa9\xcd&Jb\xb3\x00C\xbf\xd4\x9f\xcdx\x12\xda\xe6\xc6Y5f\x04\xb3\xb7b\x1a\x0b\x9bW\x05\xa5X\xe0\x14[\x14\x01\xc4\xed\x08\xc3\xa7b\xdd.D\x92\xecuj;\xed\xfbu\xdah\x16\x89\x88\xc0\xc4L\xd2\xb3\xad\xb0W\x1a\x8a\x01\xfb\xd8\xc6KR\xa6S\xf4\xed\x083\x11\xe9\xd79~@\xb1d$\xe0\x8aA\xc4x\xf6\"\x9e\xf2cv\xe9\xa5El\x9b<\xfc8(\xe4&;v \xf0D\xcfl\x8f\xea\xe6N\\\xfd\x8ev&T\xa7\x98K^\x86U\x1a_\xe9\xa1\xdd\x16P\x12Q \xab\xc8G\x14\xc8b5h+\xa5\xabV~\xe1\xf6o\xc6\x8c\xc2\xc4\x95\xda\x06\xf9\x12\xf4\xc2^\xe2\xean\x08d\xf2K\xc6\x9b\xe6\xe6a\xad.@\xa3\x01\x8eL;\x1a0\x8f^\xfb\xe6A\x05\xd8C\xebN\\h\x858(\x0b\x9c\x15(9\xe1B{\x96\xe6\xe8D\xcaZ\xaa\xab\xee\x86n\xec\xaa\xc5\xc4\x8b\xc9\xd7\xfc\"\x0c\xbe\xb4\x12\xa7b\x9fR\x8a\x80\xd1\xbc\x8d\xb8\xcdM\x93!\x94W\xa8\xc5\x9e\xc1\xb0 \xce\x12\x17\xc4\xcc'\x93\xb2*\xea\x97G\x10onRr-f\x86XR\xe8\xe8F\x98\xfd\x883\x1b\xe4V\x80\x0fe\xf7\x98\x15Z\xa2\x07\x03\xfa_aO%T\xe8\xc2B\xb6\xabG\x00\x9b\xcfF> <\x1c+[\x8e\xd5\\\xd4\xaaM\xbc<\xcc#\x0cJz\x9d&\xb7\x19I-\xfa\x90\xff\xe6a\xf2\x13\x8f\xc47H\x07\xd2\xdf~:\xbf\x11y5\xbd\x1b\x92ft\xfeX$\x93\xf2>+K\xe3\xbb\x1b\xfcn:}\x1bf9\x89\xb1\xde\x1b\xf6\x12\xdd\xd1\xd9\xef\xd9L\xfcL\xc92\xb9!ja\xf6\xf4y\x14\x89\x17\x99xC\x96a.~\xafR\xb2\"q\xa3%\xfe\xf8C\x1c4\xea\x8d\xa4\xea\xccK\x8d\xef\xc0\xc9e\x1dz\xd7a\xdc\x99\\\xa5A\xb5\xae\xd2$ YV~\xccC\xa4HA\xf1\xea\x8d\x04\xb7\xd3\xb6\xf9\x16\xac\xd2\xb6\xa5|\xb6\x98\x86\xe9\xe3z\xc6>\xed\xeaW\xb1\xf4\xb3/=z6\x90\xb6>h\xb8\x10E\xc5o\x15\x19AEO\x90KL\x9c\xcc\x90\x98G\x84\x1a\xa0\x8a\xd8\xda\x90Uu:}\x0f\x06\xb1\x15\x03\xf5\xcb\x8aU\x19C\x83k|\xc4@\x9aH/\xd5\xe2\xd0\xca\xbe\xe6\xa4\x0bk&f\x94\xd8\xc0p\xc7'0\xa4\x88E\xd2\xdeT\x98jx\xc9\x835\xc8\x8f\x9a\xf4DlLx+duZ\xb0\x19\xd7\x07\xa8\xc2{\xb5\xd7Lt\xcfP{\xea\xa8\x02|\x9fb\xdep\xe2\xd7\xb1\xaeof\x961\x17\xd6\x86\x88\xa2\x19\x0b\xd0 \xc3&\x91\xa1\xa1GnHzW\xcb\"\xdd\x95\xda\x0c\x19\xb7x\x92^j\xf8\x1bts\xb1\x19W\xcdp2\x9b\x04\x17B\xc7a:\xb5\xd05s\xf2Z\xde\xbb1\xf15\xc2\xb5 \xc7\xb8\x84cN\x0f;8\xc5\xe0\x14C\x1e\xd98e\x07\x1c\xcb\xb9 )\x85k3\xa9\x9d\xe4-\xa0\x16\x97\x00]\xfb\xa6\xef\x03}6\xc4Y\x9a,[Yv;4\xcc\xc3\x83\xf1\xb8\x8f\xbc\x94dE\x94\xbf.\xe2\x80\xae%\x17\x9f\x04\xc9rU\xe4~\xce\xd9\x94\xce\xcd&6Z\xe3\xe5\x03\xab/#\xf9\xa7GWJgH[q\xed\xa1L\x0c\x88_\xb9wuE\xb2w\xc9\xb4@\xf6\x8d\xf2i\x98:\xd6/\xa2\xfc\x1dY&,soB\x9f\"\xda$\x02\x8b\xbedH\x94\x11\x1d\xe5\xcb<-\x82\xbcH\xc9\xb4D\xb6}\x18\xefGP\x99\xbeBe6\x99s+\xc1<\xb8F\xea]\xc8\xfeM\x1dg\x87C\x06\xb30\xcd\xf2*^\";\x18\xfc\x18X\xf5p\xbb )\x01\xe2\x07\x0bX\xf1\\\xbb\x94\x11\xf0A\x9c%\x9a\xa3\xc3Gk\xb0\xb2SG\x0d\xa0\xd0\xbd\xc6\xd3\xf8~!wYC\x88UR\x8bq\x1dU\xb5\xf9\xc3\xd3\x0dY_\x0e\x8e\xdb\x93\xe4\"Z\x84\x9cW\x08\x81\xd3~\x03F\xfb\x11N\xfb\xe5\x93\xb4\x9d\xee\x03i(^J\xa6E@l\x85\x13\xea\"\x98\xc9\x84R\xcb\x97\xcc\x18R\xa3\x8es\xe1\xf7\x07E %\xb1\x9fu\x91\xb6\x8f\x04L}\x99\xd3\xf5m'z\xb5\x97\xc2\xa7 \xee#\xb6\x87\xc3\x03\xe5@D\xc6\xc6\x1e\xed\xee8zV4\xb6\x87\x83\x01\xa5\xfc\xda\x1a\x00Y\x84'\xd2'$6Z\xabK\x83\xea\x91TLZ\x12\xcc\x18tM\x96\xb4\x1a\xea\xc1\xaeaD\xed\xcc\xf5\x86\x1c\x0b\xd5\xc4G\x8b=\xb6\xf1H>Z\xedq\xac*$\xeb\xfb\x8e\xc9\x9c\xc6`\x8d\xbc=o\xcf\xd2\xad\x12\x8d\xfd\xe1\xd5\x153\xd4\xa4\x7fO\x84\xdb@o\xf0\x8d\x0e\x0e\xd6\x86\x9f\xcc\x85\xca)\xe7j\xb2\xeau\xa7Q\xbf`\xf7\x0ev\x95\xe7!\x7f\xbe\xa7<\xa7{\xc7\x9ap\x9c\xf8\xbe\x88\xa2K%Tx!\x17\xf8,\xd2\x9d\xab\xa524n?E\x13\x04f\x0fx\xe1\xcf\xcb\xcc\xde\xdf\x01R\xd2\x89Bo\x0b\xcc|2\xe6\n\x16\x08c\x8ev\x99q'\nF\xc6\xc8&?\x16\xb0{OGz\xc8>\xdd\xeb\x9cx\x0d\xbd,\x96q\xc2\xdej\xb7E\xca\xb2\\\xc4%\xd8\x1e\xdb\xf7\xd1Su\x96Y\xdf\xf7w\xd41\xb1Uqp\xd89$\xc3\x0c\x85\x0c\xde)\x83w\xb26\xbc\xf5\xb2> !\xef\x0e4#\x91NXJl\xb4\x93\xd4\x82V\x99h\xce0\x89s c\xa42\x84U\x98\xf9\xbc\xab\xbdx0\xc0\xad>\x96\x90\x1f\x14\xfbR\xb5\xa1\x17\xc6\x0b\x92\x86\xfc\x149\x1c:\xcd3-\xb6w\x06\xeaL\x16\xac\xae\xda*\xac\xea\xb2g.\xf8\xd2\x9br\x80\x19\xae\xbd\xa2\xd2\"\xf0\x14I\x83#\x88\xe0\x18*uFD \x80\xe6\xda\xa5\x04t6\x89\x14\x18\xce\xaa\xfa&\xc1%\x8a\xb9\x94G\x94)\x93\x1f\xb4\xebwg\x86C\x879\xc7\x88@\xda\xc9\x0cfU~IJ\x12\xce\x1a\x84\x96_W\x95\xb9P\xa8\x0f\x10\xfbo\x08\xd7\x89\x94\xf8S\xff:\xe2\xb1c\x17aV=9a^\x80\xf5\xf2\xb7i\x98\xd7\xcb\x97Oxy\xa6q\x89\xa2\xe4\xf6\xaf~4\xfb\xb0\"1'\xd3\xeb\x15\xd5K\x94\xb55>,\xabL\xe2\x80\xd8\x16\x89\xa7\x96\x0b\xabvp6\xb5\xf4\x9a\xba\x85\xc3\xc1\x95\x18\xc0y\xee\xe7\xc4#\xf1\x94L\xe9\xcb\xb4\xd4\xc5\xd9S\xd6\x85.\x1d}c\x0e\xb16[E\x0d\xf4\xe2;\x99\x1d*\x1f9\x19.\xaf!\x17,\xd1\xaf\xbf\x86\xf3\xc5\xcf~N\xd2w~\xfa\xc5r\xd56\xe2bIRZn\xdc\xd0\x85\xcfI>n\xa7\x98\xc5\xe6\xd6\x00b!7[\xdf\xfc\xd5\x80\x1c\xb7\xd7P\xa6$\xcb\xd3\xe4\x8eL\x1b\xdd\xef\xddE\xc9\x9f\x86\xf5V\xacS\xec-]@\x8d\x12\xb5\xf1TK\xac\xfe\xa5W\xf6\x0d\xbd\xce4\x80(\x0b(d\xb9B\x08\xd4\x06\xa2\xc7\xc8\x7f\xfc\x10*\xfd\xb3i\x10\xb4\x88Q\xe1M\x19,I\xe1z\xc5\xbf\xea:\xe4\xb1Av\x80\x14Q$6,\xae}W\xdeGyM{\xff]\x0e\xca\x9d\xe1\xc8\xb1\x1f{\x8a\x93\xca=\xabT\x91t\xd1\xe8k\xf6o\xff@w\x90\xb3\x10\xf7\xfe\xd7G\xf6;\xb1\x07.\xd2\x1e\xdf\x00\xccu\xcbk\xa9\x94\xa1flvl\x1f:]\xf2\xbe\x90;~z\xe2l\xfb\x98$\xc2\x16\xc0\xc4@\x0b\x82\xa6\xf9\x1d*8\xf4\xb2;\x19\xc1 \xc3Pz\n6\x05\xd6F\x0bez\xd0\xd2\xef\x1b\x86\"\x1a\x9a\xb2}\xd4D>\xca\xf1h\xa7\xe7\x8cm\x8d\xf6,t\xb7\xc5\xedVP.\xde\x16\x9bH\x03\x1f8\xe6\x1b.I\xa2\xf3\xf07R\xe2\xad:L\xe8vl\xa4o\xad\xdd\xfa((\xab=*\x1a\\&\x16\x9cNi\x9d\x94\xb9I\xc6\xed\xa8@\\%\xfb\xda:-q\xad\xcf\xdc\xba\"\xf6\xe6$\xa7\xf7\x88\xac\xd0\x01\xca\xa7O\xcb\xf1\xa2czu{\x02\xc3\x81C\x0b\xa4$\"~F\x98\x84\xaf)\xa1}\xd0\xa8oc\"\xd2\xa9b\x83\xe9X\x05\x08\xbd\xf2\xdbD-\xd5\x0b\x06\x8fY\xe4 \xeb\xa6\xd6Y\xe8\xa0[\xec1\x8b\x10\xe0\xe8\xc0\x01\xda5\x0f\xbauO\xab\xe8\x03\xce|\x91\x92\x06@\xbbD;\xe2\xfa\x16h\xa5\xdf\x05Zi\x19G\xa9\x114Z\\\xfd\x01\xd6\x88\xc8\x00z\x98\xcd\x92\"\xed\x02Y\x8bT\xf1[\xa0\x96|\x17\xa8%R\xf4\xa9\xd4Q\xf5\xf9\xe2Z\x0bp\xae\xd6\xf1\xb8\x8e\xca\xf4Gg\x81O\xdb\xe4ju\x03\x7fmq\xb3\x98tO\x95.%\xfcy\xb7l\xc4p\x94\xa7v\xb2\xfe9.\xf7\xe8\xd1-s\xb9\xd1#\xc8\x08\x89\xfa\xda\xd1\xcb\x8a\x0e\xb5\xe2\x96\xe1P}\xce\x98\xfd\xe1\xfe\x81c[Y\x1aX\x1a\x9e\xff5\xefH)_k\xca\xdfX\xfe\xc1\xc2\xf1\xb2U\x14\xe6\xb6%J\xcaR\xd8\xd8\xde\x1f8\"a\xf99F\xca\xe8\x03$\xce=\x93\x9a\x05\x98m\x94~\xe1\xda-tr\x84\xc8d\x0d\xafx4FH\xe4\x87\x14s[\xb1\xbf$\x16\x1a\xd1$\xd5=7\x9fDIxi\xd2cK\x9f\xf9\xd5\x17>/\x87\xf2\xd6M\xf6{\x0c\x19\xb3H\xe0\xde\xcb\xb9\xe3\xb0\xa8b,\xb6\xcbi)c\x871\x14\xe2\xb6\xf64\xa9\xd6\xc4\x18\xec)\x89HN\xf0\xbd+\xbd\x92\xd7\x94c\x97\x93(3\x85\xe54\xb5hu\xf84h!\x87\x04\x14\xa7}&>Ja$a\x87\xdc\xfeZH\xa1sM\x94z:9\xf4\xc1\xa9\xc4A\xc0\xb8\xcb^\xa5\xd76\xeb\xa4\xbe\xf5\x9bo\xb4o\x10\x81\xef\xeckw\xdf\xde\xaeJ\xc53Q\xdb\x81Z<\xe3\xc5UYj\xc4\x9f\xab\x12\xbb\x80?W\xeb\x99\xf1\xe7*2X\xa1\xd0\x8ci\xb3\xce\"B\x0f\xc4z\x81\xa9T\xe0\xb5O\xc9\xe4\xbbz\x81\x05+\x10%\xb1\xbe\x82\x1b8\x81\xb4\xfeh\xd9I\xb47t7\xd0<\xc8\xe7Z\xb2\xf9\xe5\"\x8c\xa6)\x89\xc7\x86sx\xe9\xaf\xc6\x10zK\x7f\xd5$\x0b\x80 1\xcf\xfc`A\xcb\xf0\x9f\xfarAR\xc49-\x85?\xf4e\xf2\x045\x9f\xb4\x14\xff\xa9/\x97\xc4\xd1\xdd\x18f\x8dw\x1a\xca\xe5e\xb2\\%1\xa1M'^y\xd3,\xf7\xb1HI\xadl\xedA\xb3|m\x05\x8cA\x03\x1cy\x86\xc7\xa0\x81J\x98\xfd\xe4G\xe1\xb4,Rx\xf5'\x9aN\xa6\xc9\xea\x82\x99De\xa6.\xbd\x8c\xfc,\x1bC`z\xcf\xd7\xe4\x18\xa6\xa6\x12\xef\xc2\xafa<\x86e\xf3\xfd\xab\x0f\xef\xc6\xe07\x9f\x97J>\x8d\xf1\xe9\xd5U\xb6J\x89?\x1d\xc3M}q\xea)\x829>\xfdc\x90Nc\x93\x87L\x12\xf0\x94\xb2\x1e\xf6h\x7f\xbf\x12\x14V\xe2\xa5\x85\x9f}\xb8\x8d\x85\xc8P\x8b\x9cF\xfb\xaa\x9eO\xcf\xa1~!wc\xd8\xd0XA\xa6d\xa6\x7fqu\x95\x91\xc8\xfc\x0e)\x84\xb1\x9a\xbeX\xeb\x10\x9a\x19O\nI\x9cG\xbc\x94T\xbbJ'?\x8e\xfaU\xf3\x85\xdcI\xd5\x88_BU\xa1\xe1\x1cX2C\x03Y\xd2\xd4*\xd3\xeb\xcf\x7ff'\x96vE\xe6\x98^\x994_\xe0\x1ch\xb6\x16NA\xdc|\xbeJ\x93U6\x86B\x03\xff\xe46\xa6|PhZ\xd6P\x01\xa7\x8a\x0b#\xbd\x0f\xea\xc7\x88\x060:`\xa4\xcc\xd0\xfaw\x1d\x97\x06&\x0b\xf0\x15\xe8,\xc0\xd1\x9b\x96\x11\x04:\xde\x19\xd5S)\x84t\xf1\xe4,3\xcf\nm9R2s\\\x88\xc4\xc3\x19:\x98\xc0&\xa0\xd2\xcfqky\x06=\xb6\x84\x05\xe91.\x9f4\x8b1z\xb7^\x10\x9f!\x1d\x14\x96\x921\xe6\xb5\xb6Q([\xd3\xe6\x99\x87}f\x1f\x93OR5\xe3.\x05\xdfTg\x18\xb5\x05\xa3&d\x98\x0eh\xea\x80\xef\x05\xfc\x8c\x84Fl\x8f2\xe2\xc3\x14\xbd\x944\xcb\xb4T\xf2-J\xc3\x9e)\x85\x11S\xef\xdd\xc01L\x8f\xe0fs\xd3\x81\xc5\xe4\xa6n\xd8s\x83\x811\x9b\\\xee\xc0\xad\xf7\xa9\xee\x8f\xf8\xd0\x18 \n\xdf\x88\xb0?\xa3\xf0\xcat=\xa5\x9d\\\xa21\x87\\\xb2\xd9|\xb5.\x96N\xcd\x96\x8c\x02^\x9a\x81e\xc3\xe0\xfeA\xb77\x02\xba\xdag.\xac0\xa9&z4\x05E\x9a\xd2\x03\x10\xfc\x1aK\x13\xd4\xc9\xaa^Fp\xca&C\xb7\x9e\xd2 P\xbbWs\x8f\"\x0f\xae\xa4P\x9a\xa7G\xfa\xf3x\xfa\x89\xc5F\xf8w\xd2\xa9t\xa8\xc6\xe81\x86\"w\x19\x96\xa5\x7f\xf8>\xa0?\xf8:'\x1e\xc3*\xf4\x17b\x1eu\xfc\x12M\xd1\x13_\xf8\x0c\xb8\x94\xa8\xb4\x7f\x7f\xa8*n\" \xd4\xba\xd0-\xdc|\xb5\x00~8h\xce~\x0cj\xdd2\x16\x8d\x87_\x17\xd2\xf1kHg!\x90\x0e\xdb5\xe5\xf2\x90q\xd0T\xc5A\x0c\xdel\xe1\xe39.\xaf\xe9\x12mi\xde9\n\xb6\xf1\x0d\xd8\x86=\xb7e$F\xf9\xbb\xba~\x8c\xe2\xbd\x15\xf3\x81\x99\xd1?cqG\xcbj\xb0\xd3rM\xec\xb4t`\xd5\x07;-;\xb1\xd3\xbc\xc4NK\xc7\x85;\x86\x9d\xee\xe0\x18\x96GpG\xb1\xd3|rW\xc7Nw\x06\xecT\xeb\xd0\xbc\xd7\xfe\xe7{c\xea\xc2B \x81\x9b\xba\xfe\x9c.\xfe:u\xfch&\xb8\xa6Gc\x0bD\x90\x12\x0c\x8d\xc9\xad\xca\xa4i\xf0'\xe8&M%\xb1\xd3\x81\xe3\x9d\xdf-\xaf\x93HO\xe9\xa6\xebU7:\xd4\x9b\x0d\x0d\x0f\xbf\xcd\xd6m\x83C!\xa9\x0c\xd0q\xc1\x7f\x8b\xdd\xdb\xc8 \x81|\xaa\xaa\x19\x19\xd3\xbf\xdf\xb0#bt\xf5\xfe\xb0sdf\x94+E\x12\xe4f]p\n\x13r\x89\x96g\xfe\xb7\xc8\x131\x1e~cxJ\xf8\xbb~\x13\x11\x1aB\x972\x95\x1b\xa9\xechH\x13W`\xe0b\xd8lD\xe1\x11k\x7f\xc0j\xa4\x93I\xfbF\xe8\xddV\x02\xa7`m\x0d,J_u\x8c\xbf\xc6p\xe9$E\x9cUb\xe7+F\x1c\xea9C\xc4\xcb\x8a\x15I\xaf\xb8yq\xc5lU\xd6c\xacR;\x97eqM\xec\x15$\xb1\xd0E\x9a\xc4\x17\x98\x98_\xcb @\x87]\x8a\xb8\x84\x89\x82\x9e\x0b\x03\xd6\x8dY8/D=\x1a\x9f\x81\xda\x93\x87\xbaU\xf1\xa3\xc0\xd6\\\x0e\xaa\xd7\xb9\xc2\x88\xc45(\xd7\xe0Z\x9f\x80\x98\xdc\xa2\xe9r-.w f\xf8\xfe\xb6\x07\xfb\x9d\x9b\\\xb7kj\xa6\xceJ\x98\xd8\x97~\x1c'9\xd0\x86\x11\xc5%)\x14q\x19sH\xbb[\xbe\xcb\xa0\x1a^\x1f\xcaxyt@\xfb\xa0\x81@P\x10\x91b\x04_\xba_S\xb9\"\xe6\xfb\xdb\\\xdd\x9ch\x19\xab\x99c\xe5\xfe\xf02\x9d\xd0\xec\xe3\xc9\xf4\x87x.\x89\x93\xa8>\x04\xdd\x0c\xd9\x03\x17B1 g\xed\xc3\xa9\xe7\x8c\xb9\x06\xa0\xb5\x18\x0d\xab;M\xf2\x99\x16f\xab\x18\xff\xf7\xc3\x8cr\xa8\x98X\xe6\xfe\xbeK\xceT\xc6\xd6\xe6Lm\xccX*\xd2dj\x1b\x10|\x048\xca\xc7\xa5\x9c'\xed\x92\xf30S\xef\xfb{a\x06\xde\xc4\x0b \xefg/\xcc\xde'\xf9\x82EcH\xdd\xda\x0b\x06\x8a>\x04K7=W\xf5An\x83\x0b\x93\xfb4\xa1\xee\x04NBpjbB\xc9\x079\xd5o\xad\x99\x94\xac\x88\xdfo\xdd0\xcf\x1e\xf5\xe8\xc6\xa5\x133\xda;f^\xd61lb\xd4L\xccP\x85\xc5\\\xefL\xcf\xc1\xe6F\xf4[e\x81\x1a\xcby1\x18/\x8c\x83\xa8\x98\x12\xa1\x95\xe9p\x1fG\xef\xe0\xb2\xad\xda\xeb\x07\xae\xc9\xed[S\xb3\\\x9bEM\xee\xe5\xfe\x9c\x9b[\xd3_O\x9eP\x1e>\xa4\x8b\x88\x89\x92\xe9O<\x13M!a\x1f\xd0\xaeJkJ\x86ofa\x94\x93\xd4n]\x91PAn\x8b\xc7J.\xb1v\xaeV*\xad\x93\xe6\x84i\xa2\x16r\xf3\x15\x9c\x0e\x14:\x88\xdf\xf7\xf7hK\xc6\xde/WQ\x18\x84,\x1dIy#\x97 _\xa5\x12\xe5\x8d\xae\x8e\x9e3\x85\xb2A/J\xfc\xe9\xbfs [Y\xe0G~jq1\xbex%\xd3Y\x89m]\xa0s&\xbac\xc6I\xbc\xc5\xbeA\x84LO\xbc|A\xa0\xec\x7f\x14f\x18\x07\xdf\x87,X\x90\xa5\xef\xc1\x1b\xf1*%Y\x12\xdd\xd0\x13!\x99AV\x04\x0b\xe6\xed\xdf\x08l\xe3Y\xcdIe\x86=\xc9r\x15Fd\xfa\xa6\x82\x9c\xcf]\x08,\xd1\x01\xcb\x85\xc9\xa5\xfa\xc1\xd9\xd7\xe6\x07\x02\x9e\xda\x0f(m\xf9\xce_)\x14v\x03\x9etK\xf2\x1d\xa4\xd5X\xd0\x8b\x01k\xac\x95\xdf\xe3{\xf2kA\xe2\x80\x98K,\xfd\xd5\ns\x1f\x98\n\xcc\xfc(\xba\xf6\x83/c9h\x97\xb8\x1e\x94H\xf3\xd0q\xea\x8b+\x9e\xb0\xadx9\xc1m\x8af\x16\x9eh\xa9z\xa6\xf1\x15m6GQ9a\xa8\\\xe7\xa7|\x84q\xed\xf3#\x16,v\xe8H2'R!!U\xae\x08Fj\xd2\xd6\xae\x16\xc3\x9aP\xc9Jz\x15\xde\xab\xb3\xd7\xcf?\xbf\xbd\x10\xfa\x95R\xc1\xdf\xb6\"\xc4j\xa8w3\xbb\x0d1\xb2\x9c:h\x1d\xdc\x03?#0\x1ck\xe7\x03\x83'\x8a~)p\x9c\x0c\x0c1\x02\x0c\xf1\x96\xb1\x9d\x91\xb9\x1d\xb9b\xb5)\xd5G\\\\\x86\xa6\x04\xd3\xa2\xfd\xa6\x86d~N\x93x\x0e\xcc3\x141\x88h\x12\xd7\xcf9\xc3&|\x16J\xe9D\x9b\xba!\xe4y.SA\x0e\xa2\x83u^{\x92;.l\x90^\xf1_\xc49+[K\x17\n\xa2R\xf0\xe6\xf9\x8a\x04\xe1,$\xd3\x12-\"C\xcfQc\x06v\x92RD\x19\xc6\xf3\x88\xf0\x11r_]\x07\x83\xc6\xfba,pn\xed\xad\xa72\xb5k\x84\xb1\xd1\x0d#\\w\x18\x7f{\xfe\xee-\xc7\xde\xb51P\xbci\x1a\x81\xf4\xae\xd1\x7f\xb1\x8f\xc9-\x14\xb6\xe6\xdcb\xc7\xa7V\xaa#\xf0\xf8X\xf5\x05\xac \x93\xbb\xad1\xd7$\xf6\x86\xc3\x9a\x19\xdf\xa1\x96\x96K\xda\xe4\x956\x81'\xf4\xa5\x1aXLn+\xd4\x1e+\xef>\x9f_\\}>?\xbb\xfa\xf8\xe9\xc3\xc7\xb3O\x17\x7f\x1b\xeb\x92\xa1\xfe\xf5\xf9\xf9\xd5\x8b\x0f\x1f\xde\x9e=\x7f\x7f\xf5\xd3\xf3\xb7\x9f\xcf\xc6\xb0\xab/\xf5\xfe\xf3\xbb\xb3Oo^\x8aR\x87\xfaR\x1f?\x9c\xbfA\xd6@)>2\xd4\xfa\xe1\xa7\xb3Oo?<\x7fu\xf6J\xed\xc6\xce\xa8\xf9E\x18\xd3\x85\xf1\xea\xc3;\xc1\x10\xbfD\x19[\x97\xf3\x12H\xb2\xd1P\x7f:\x02'v\x89\xc7\xab\x0e z8\x98NS\xe0\xe2h\xe2\xbd\xfa\xf0\xeey\x9e\xa7\xe1u\x91\x93\xf7\xfe\x92d+?\xe8\xfe6\xd3\x7f\xdb\xf5Y$>\x13\x00\xe8\xf5U \xbez\xc7\xe3\x9d\xbc#\xf9\"\x99\xf2\xef\xf4\x98\xba\x94W\xccP^\xe1\x85\xd9\xcb\"\xcb\x93e\xd9_J\x18\x16\xdeU\xe3\xb9\xb0\x97\xe4^U\x9a/\x9d\x16\xba\x1f\xf0`]\x95s\xa0\xea\xd7fL\x12f[\xbb\x87\x96\x0b\xb3\x16co\xdaw\xa4\xcd\xbc&Y\x98\x877\xc4X\xa7\x1e\xcb\xf5\xab\xfc\xc3\x0dI)\x07E\xa6\xc6\xe1\x9b\x90b\x93\xc9\x95/\xc3F\x06~\xf2/<\x05\xe2\xb0 \xf8L\x1e\xa5x\xa6\xefd\x19*(\xb5\xad\xbd\x01\xee?\x174[\xb4ms\x03\xdf\x9a7\xe8\x9c>\xeb\x08[\xb5\xf0j{\x02N\x14sA\xf9\xd2\xbbi\x00:\x96k\xb1\x88\xad\xd4\x8e;\x0es|\xcd(\xaf\x17\x19\xbf\x92w\x1b\x9c@\xc4\xca\x07\xc6\xf2\xf5\xcd\x06'\x10\xb0/dD7\x99]6lv\xc4\xa5\xe1\xd7jO4\xbeq\xd6\xf8\xf9\xd6\x7f\\\xf9[\xbf\xfd\xf2K1\x18\xbc\x1cl\xe1\xdfW\xfb\xec\xcf!\xbb}\xcdn_\xb3\xdb\xd1\xeb\xd7\xf4\xcf\xce\x01+\xbcs\xf0\x8a\xfdyMo\x87\xaf\xf1\xedh0x\xb9\xc5\xfe\xbe\xc2?\xac\xf0hx\x88o_\x0e\xd8\xed\xeb3z\xbb3\x18\x0c\xe9\xed\xab\x03\xfc\xf6\xf5S\xf6\xf6\xf5\xab\x97x\xfb\xea5\xbb}\xfd\xfa\x95&|Is\x05\xbdyu\xf5\xfc\xe2\xe2\xd3\x9b\x17\x9f/\xce\xae\xde?\x7fw6\x06k\xea\xe7\xfeVJ\xfc \x0f\xa7Vs\xfb}\xfa\xf0\xe1\xa2\xed\xa34Ir\xcdg\xf5/\xae\xce/\x9e\x7f\xba\xb8z\xf9\xd7\xe7\x9f\xb4F\x85Ji^\x0e6\xc1\xfa\xe5\x97-o\xb0\xf5\x14\x81\xfc\xe2\x00\xa19\xe0\xc0\xddg\xd0\xdcy\xcd\xa0\xb9;\xd0t\xa3Z\x1cz\xae\x1e]\x0d\xb3,d\x8e\xd2\xf1\xd4O\xa7\x0c\xff\xeb\x91y\xcbQ=n\xa4\x16\x00\xb4DV\xca\xf7\xa1\xb3\xea\xfa \xa6\xfai'\x13jj!3\xe2\xc00\xf5\x03\xb7\xbd\xb2I~\xe9\xc8\nr\x8d\xd6\x15\x8c\xa8B|3ln7\x13)\x8a\xe6\xcdFS\xcf\xef\xceO\x1c\x1c\xee\xd4\x18\x8a\x1df\xa3\xfc\xd4\xc0W4x\n\x8a\xef\xfc`\xf1\x89\xcc2.\xe1Bi\xc7\x157\x9d\xe264:a\x87\x9e\xcfX&E\x9cK\xf6\xf1\xea\xd8P\x98\x1f\xa2\xb5\x94^.V eZ\xaf\xc6\xae\x7fi\x94\xe7\x10\xb5\xdf\x92\xce\xa7\xf9\xd2K\xc9\x8cI\x91\xe7$\xffD7\xff;\xda\xea'\xe2O\xefl\xc7#\xf1\xaf\x05)\x08z\x04R\xcc\xdc\x86_\xe7$\xffk\x92\xe5\xef\x93i\xe7\x8e(\xbb*}c\xb7:6\x17q+P\xb5\x8dxSRN+3\xb1S&\x94>S+n\x08\xb0\xeb\xfd\xe0\xf1\xf3Z'74M+\xe3\x8c\x94^4'\x12\x95:(T\xc6\xc4\x13!\x97/_\x05I\x9c\x93\xafF\xdfdM\n\x10\x90\xd6S\xeae\x8b\xa4\x88\xa6\x9fWS?'\x08\x14_\x9ft\x18\xf0\xacA-B\x1d\x82\xbe\xc3\xec1\xeb \xb0\xc5\xa8]\xf6\xd5\xe3\x16`\xdcc\x016\x11P\xdbT\xadH:K\xd2%\x1b\xef\x9b\xd9{\x12\x90,\xf3\xd3\xbb~\xfe\xcb\xc4\xbb*\xf0\xcb\x17~\x1e,\x98\x86\x8f'\x8a\xc51\x9ajo\xac\x9f\nk\xe81`\xf8=0\xe0\xc8\x10\xedo\xb8\xfbT\xab?\x1b\x19\xfc6w\xf6\xd4\xf2\x183\xad2\x08\x91\"YN\x93\xa0\x10\xd3\xab J'^{\xe2\xc7\xbb\x84)q\xf4\xb5\xc5\xfeM8\xc7h\x9erf\xe5\x93\xe6{\xaf\xc8H\xfa|\xce\x1b\xde\xfe\xe5\xfal:'\xbfl\xff2\xdd\xf6r\x92\xe5\xb6\xa6\xa0\xf6\x1c\xd0\xf8x\xd0\x8d\xd7\xf0\xa9\x00\xd9\x82\xcc\x8b\x93\xa9\xc1:*\xe69V\x995\xa7~W\x8b8\xedz\x8e\xa5\x16?\x9e\xc7\xb1\x8cK:\x00\xc3Y\xb2,h\x93\xf4\xd2\xc5\x1d\xa5\xd9\xbch\xc5Z\xed\xb6E\xbe\x8c0\x8a\x1c\xda\x8e\xd1;\x07\xc6\xd2{\x8aP(\x1c}V\x00\xf1\x8bi\xfd\xd6\xd6]\x84Q)\xbbv\xd2p\xc8=\x16(\xdc\xf0?\x94db\x02\\\xdd\x0b:\xf7\x95\xd9B\xed=\xa5\xe1\xea2\x0bf\xeb\xc1\x03\xeb\x89\x92\x82a\xf9\xfc\xe9\x0d\xc6\x83\xd2C\xe1\x1c+\x10\x85\x84\xd2\x94A\x8e\xb7\xaf>\xbc\x93\x7f\xb3\xca\xc5\xddE\xf2\x85\xc4\xec\xc6\xcf\xfd\x8b\xd4\x8f\xb3\x19I\xdf\xe4d\x89\x0f_\x87\xbcQ\xba\x9d\x9fG\xd1\xcb$\x8a\x18\xc7\x8bO\x94\xdb\xd7I\xba\x14\x0e\xca\xf4\x9e\x85t\x16O\xde\x91i\xe8ce\xef\xc2%\x1e\x80\xcc\x8d\x9b\x9e\x03S\x8a\xce\xde\xf9+\x97\xfe\xc52\x1f\xfd\x90\x8e\xe1\xd7\x82d\xac\xeb\x1f\xa3b\x1e\xc6\xfc\x0f\xfb\xf2\xfc\xa7\xbf\xbc\xc5\xb5\x8e\x05\xce\x7f\xfa\x0b#\\\xc5\xddG?_\x9c\x93yy\x9b\x84q.n$(\x9c\xff\xf4\x176\xee$e\x83f\xd15^\x14\xb3\x99\xa8\x8b\x82\xfb|A\x08\xfb\x9c\xa2\xa1\x8b\xd4\x0f\xbe\xbc\xe4\x00/\x1f\xb0\xbb\xa4\x08\xb0G\x96\x88\xe7\xe1\xd2y\xcc\x18\x99\x93\xa1(Dl\xd1L\x1f\xb4\x93\xee\xccb\x92iv&\xddK)\xdd\x89\x8d73\xe0\xfb-\xa8,G\x15t\x81\xce\x1b3\xee\x8a\x94`\xc8Q\x17\"\xba\x10'\xd1%\xdd\xee\x1e\xc2\xb5c\xcd\xab8\x91\xa1\xa62\xbcI\x17\x024\x1c\xe9\xb1\x08T\xe2eQ\x18\x10\xfb\xd0\x85\xada\x97!\xafi\xbb\x9b[\xeb\xce3\xd5\x99c\xea{\x04\xc7\xeem\xd8o$xj\xee \xf6\x10\x9e\xd0s\xbf\xb9\\\xea\xee\x07\xf6\xc8PNrd\xb0w\x0de\xb8\xbb\x84\xa2;_\x0fAJ\xb8pG\xe5\xbd8\x0f\xb7o\x8a\xd8\xde;xp\xe5\xe5\xe3B\xd2\xb5\x84\x8c\x1d\xdc\x1d8\xdeL\xd7\xc3=},\xe6&\xee\xee\xda z&\x82E\x99M\xd0\x1e%\xe6&\xc6D\xf6\xc9\x08\xb9\xf6\x93\xa0l\xac\xb92T\x97\x93\xbe3\xb9&\xa4\xba\x98\xf4\xdd\xbd=\xc7\xde\x18\xd4D\x95\xa3\x9d\x03\x87\xc7\xedq\xc1jF\xcf\xd1\x9bG^QR\x8eG\xfb!\xc2\xfe\xee\xaa\x9e\x82\xe3\xa1%\x06\x8f\xb0\xb6\x12\xd1\xc2\xae4>\xfee\xb8\xba\xabPooRK\xfe}\xaa\xa5\xa8\x10\xa8<]L\xe3\xf54\x895\xe1\x18\x90\xdbB\xff\xdb\x9c\xf1Wbl\x9b'\xa5\xaf\x84n\x8e\xcd\xaeK\xbc\x9d\xa1qn\x1d\xed\xe4\xfe\x13!\xf5\x162n#\xb6\x87\x83\xa1c\x1b\xa7\x9a\xb7{@\x11\xbb>\xae\xef\xef\x0f.X~#\x8c/\xf4\n\xe5+7\xd1x\xa9\x88\xe7\x1c\xcf_\x07\xe8\xfd\xe0\xda\x9aQ|c\xa3!Vn\xcf>\xadU\x8ftat#\x89\xddk6e\xb3(\xdd\x01\xc0\x02\xcb\x86\xf1#\x17\x1c\x81g0@\x1e#ET\xf1t08\x18>}:\xda\xdb=\xd8\x1d<}:\xa4,\xc7\x9a4\xfd\xb7d\xb5lM\xa1\x07[0d\xe6\xc0\xd6\xbb0fVs(\x12\x06B\xc9\x0f\xf8\x17\x0cyFi\x90#\xb8 \xb30\x87E\x9e\xaf\xc6\xdb\xdb3? \xd7I\xf2\xc5\x9b\x87\xf9\xa2\xb8\xf6\xc2d\x1b\x15\x99\xdb\xd3$\xc8\xb6\xf1\xe3\xad) \x92)ar\x9f\xd30\xbe\xf1\xd3\xd0\x8f\xf3\x13\xac\xb2\x96:\xa6L\x1bHQ\x8e\xf5\xc4O\xe7\xd9\xe4\x92\x95\x8bi\x15\x9f?\xbd\xa9d\xdfRb\x19\xd8\x84\xa1\xeao\xc4\xea\xc0Qc\xae\xb6\"\x8a`I\xb2\xcc\x9f\x13t\xb4\xcb\x08>\x8f\x93xk)F<%7@\xe2\x9b0Mb\x14\xaf\xd2\x8f\xf1C\x1cG\x06~<\x05\x7f:\x0d)\x80\xfd\x08\x16$Z\xcd\x8a\x08n\xfd4\x0e\xe3y\xe6)n27<,d\x95oHM \xc0\xa8\xbc\x04\x85d\x14\xf6o\x04p\xe0\xa70\x89\x90\x9d\xc2\x8c\xb8\xb3\xd4_\x92\xec\"\xf9\x98\xac\xe0\x84\xceT\xf2\xc8\x8d\xd1\x87\xbe\xe3IC)]CJ\xb7\xeb\x1c\xc9\xd3\xf5Vk\x8bI\xa7x\x03\xedj\xaa\x86\xf7\x998\x03\x1a\x91\x04\xa1\x81\xf4r\xe1\x1d\xd5\xba+\xa4\xc6j.Up\xdat\xb1\x1aW)L\xf0\xd9%\x93\x94\xc6\xcd\xc8\xc0\xd887T\xe9\xdb\xbcu\xcd\xca\x9b\x932\xf2z\xdf\xa3\xdc\xb5_\xa5\x1a\xaf7\xa5\xa6\x0fi\x99\x8ee\xcdJMu2}M\xbf\xaa4\xda\x0bm\xadl\xd6{\xd7\xaaqU\xd7\xd6\x8aa\x0f\xfa\xd7\x8a\xc5;k]\x1b\x9e\xb2\xab\xa2\xae\xc2Od~\xf6u\xd5\xb7\xb6r\x8d\xb2\xcf:\x16i\x0f\xa7F\xb9\xee\xfe\x8e\x8dR\x1b\xaf\x14\x0f\x84^\xbd\xa7\x1fu\xf4\x1dq\xea\xda\x15\xe3WR\xcd\x0c\xcfIf\xe5X@\xd7\x9e0\xea\xe8\xdd\xa4(\xd5\xb9d>\xa6\xe1\x12\x0d\xfc\xfaV]\xedk\xd4\xeb\xe9P\x07\xbe\xd0l/|n\x88\xe5\xa0[\xe2P\xcf\xc4\xa7\xed?\x93O1\x970~S\x16{p\xca\x185\xb1\xbd\xb7\xebx\xec\xbd\x9e\n]\xdf\xfdWs\x8e\xe1\x04J\xc1K9'#\x0e\xd9\xbf=\x7f\xf7\xf6\xeck@V\xfcx\xc5\x97)\xf13\x9cY\xc2\x1f,\xfd\xf4\x0b\x0b\xfc\xc0n9\xe9pR%v\xa1\xe5)\xcc\xec\"\xfe\x12'\xb71\xb0g\x8e\xe5\xc0&/\x85\x95\x9c\x82\xc52\xfe\x89'\xe5)f\xe3\x99b9n\xd9\xe5U^\xa4\xe4<\xf7\x83/\x17\xa9\x8fQ\xc6\x0codk\x19)\xee\x01\xad\x10\x9fe\xb4$\x86\x0d\x14\xc4\x87\xc3\x9f\xd1.K\xe9\xcd\xca_iK|\x0b\xd6 9\xedOj\x8c\xbb\x90\xd6_\x8a\xb1\xb6\xae\xec\x1b9\x1b\x01\xce\xd3&Xc\xd0G\x0c\xc9)e\xd79 .lT\xc1\xfcq\x1e0\xe1\x07\xa3\nM\xd3\xe1(\xa1\xb4\xd6\x8e\x83\xd3%\x8884E\x91\xa0\xd3\x94*>$\xa5\xff\xc8$\xb6wv\x07\x8e\"h\x15\xbe\x83\xf8\xfe`o\x88\x96W\x07{#\xb5\\\xe5j\x82\xe5vx\xb9]\xfew\x8f\xff\xddw$w\xf1G\xecN\xf1T\xe6\xaat\xe9:b{\xd4Hu\x11r\x13\x08\xf5\xb90\x8dP\xa5\\E\x15\x103\xf5\xe6L\x14NX\x0c\xaf&\x92\xc8L\xd2-\xd1\xd3\xb61\xaaeso\x1af+\xca\xc82O\x0fo\xb5\xf032\xfdD\xe6a\x963\x05\x08Z\xeeNbs\x14\x89\xc2&\x8d\xa0\xec\x0f\xf4Y\xdc\xb4\nJ\x99\xaa\xdd\xbb\x12\xcd\x8a\xa1\xa2\x01\x8b\xf6\x05\x8b\x1c/\xbdy\xc3\xcf\xb6\xc6'\xe5\x0b\x17\xeaq\x86\x9a@\xd4\x04\xd4\x14\xe1\xfaz\xc1\x03\xa5\xfc^\x9e\xfa7$\xcd\xc8\xc5m\xf2\x91\x96\xb3\x89w\x95\xfb\xe9\x9c\xe4\xb4+.dJN\x9bf?\x02\xbd\x18}\xad\xbe\x98\xe6\x97\xd9\x99\xc8\x1dj\x14\x03!\x9e\xa3|=\xa6\xd6@\x05\xb8\x00$\xd3M7#X\xd2K3\xfaX\x1d1@]\xe6\xd1\x1c\xff\xcc\xb4H\xd1\xc8\x85\x99s)PH\x95\xf1\xb7-\xef\xce\x8f\xf5 \xa1\xfb\x9a\xafj\xcd\xc0\x1f\xb3\x84\x93o[\xc2\xd0 \xc8U\xdf\x05\xadB\x80\x16\x9a\xa9\x0bw\xa0I\xc6\x04\x1c\xae\xd3\x86\xce\xd7\x0f\x82bYD~^.\x85W\xbcM\x92u\x19pb\xf0\x83\xa8\xd5R\xb2\xad\xfa\xf3/\xe1\xea\x02;\xde\xab!U\x15nj\xe8U\x98\x92 _s\x14\xab\x9e\x95\x9f\xc59I\xdf\x12\xff\xc6\x00\xa6\xd2\xb4W\xd7R\xb5\xed\xaajlf\xcd;\xe3 ]L\xabF\x7fRO\xf1\xe97\x1f\x8d\x86\x93Q\x1fy\xaeyb\xf2\x88\xceC\xdd\xc9\xa8;I3\xc3I\x1aUI\xa6~Ws0a\xcc\xf9\x86\xc9\xd1\xacK\x8c\x04b+\xd9\xa1G\xbe\x92\xa0\xc8\xa5y{\x13\x7fH\xa7\x84\xd3\xedh\xfb\x95}$i\x86\x1b?\xb7\x193&\x13\x94\"\x0f\x91\xdd\xd8\xdd\xf5^\xf5f\x8f\x11\x81n\x0cZ+\xeb\xcd\xb9\xb3\xca\x86\xad\x95-\xfaVfy(\xe9\xf4\xae\xd2$A\x93\xaa7\xaf\xea\xf5\xd6\x17\xd2M\x03\xadH\x1e\x00\xcdF\xd8\xcb\xb3\x1b\x12\xe7\xccl\x01\xe7a\x0c\x89\xa7\x7f\xd3D\xf4\x8dr\xd9\x0b\xee\xde\xa7\xa9\x83\xbfk\x9d\xb2\xa2\xa4\xdb\xfa\x19\x06ku\xe51S@ZOw-\xfcR<\xd6\x1cD7\xdce`\xd1H\xf4I/;\x9a\xe4,\xfbh\xc4\"\x81\xfd\xfe\xe08\x93\x10#H\xe8\xeb\xc2\x94_\x8d\xf3\x81\xd9\xebd\xda0b>\x1a|z\xd3p\xfa\xb1\x1a\xbc\xeeY \x866\x00J\x84o\x0f\xa3|\xa1I\x8b\xb4=\xa3\xe4C\x9f9\x00)6\x84v1\x8b\x0b\x835XI\xfc2\n\x83/\x96>\x90B\xa3\xdcK\xc6\xe6\xf6(\xfe*)\xae#\xd2\xb7r\xa9t\xff&\xde%EF^%\xb7\xf1:e\xd7\xac\xfe]r\xb3V\xd95\xab\xff\xbc\xea_\xb2\xbbj\x90\xf4t\xf6\x06\x92\x8a\xfeu\xc4\x12\xbcbT\xc0\xdc\x05\xeb\xba\xc8s\xb6Cy2H+\x8cWE.?\xc8\xd0\x14K~\x92\x93\xaf\xb9\x9f\x12\x9f?sZ\xbc\xa8[#s\x88K\xf4\xb2\xe98\x05\xa0\xea \xc4\x85\x87s\xe3\xcd\x03\xb3\xceV]'DDJ\xf59\x8bY\xed\xc8b:=\xeeH\x8dx\xa8T\xf2SZ~\x92^\xb6a\x00\x96/\xe8\x11H`=\xb4\xc5\xf9\x8a\xdb0\x8a^\xd5Z4=g\xed\x9bG\xae\xc7AX\x1dO\x81\x94N(tz\x0c\xfey\x14\x95lC\x17\xd5)\x98<=\xe0\xeby\xbc\x15\x12[\\\x14O6\xfcpc\xb4\x82\x89&\xf1\xe5$\xbflC\x8ab\xfcf\xf0\xeb\xc4\x06\xe2B\xf8\xa4\x86i\xd0=\xb7\xb9\xa1<\x87)\xef`\x8f=\xf1\xa0J\x90\xf2\xd4\xe7\xc7{\x7f\xca\xbb\x84g\xe8\xf2\xa3r\xc5H\x83\x9a\xfd\xa1\xdff\x7f(.a\x87\xe8O2\x03|p^\xba@O \xda\xc8\xab\x8dF\x1e\x83\x19\xf2\xccv8D.7\xa4\\\x91~q4\x11K\xf3 \xdf\xdea+\xbc\x99\xebU\x13\xdefR;\xc0\xbe\x05\x1a.X!\xba\xd2$ Y\x86U\xffo\xdaHW\xf5b\xcf\x04M\xe8\x94\xfc\x01d\x88%\xe1\x14V0\x86\xa9\xe32\x80Q\xaa\x0c\x93\xb1\xfa^JP\xd5\xfd\xd2/\xe6\x8b\x9c\xe9\xc2[\xbbyu\xb5*\xd29\xe90\x81\x89*S\x0fc=\x12\x91\xf4\xc2\x8f\xbf\xf4\xcb\x8f\x1d\xd5\xeb,\xef\x0c,!\x0b\x01\xf0\x8d,a#\x85\x97` \xd5$A\xfa\xe8:7!\xb9\xed\x9aK(\x83\xe9\xd1\xd2U\xd0n\xbc\xd5\xaf~1\xfd\x89\x16e\x82\xf0\x99\xf4n\xc3x\x9a\xdc2\xcb\x81\xb2b\x8d\x87%H\x87P\xeea\xe2\x85W\xdcKM_\xb8<\x0eO!\x16!o\x7f\n\xc9-\xc6t\xe5\xfe'?\xb3\xc6\xc7\xc0z\xd1\xdc\x85MffJr?\x8c\xfa\x00\xac\x04\x12\xfb\x84\xb6\xdb\x199\xbb5B\xa6\x0b\x89\xda\x16oCRZIy@\x1bf\xa3\xf8\x85\xe7\x17s\n5\xcc\xa3e\xfb\xcc\x0bT^\x94\xfe\xb7/J\xb5\x93\xcb\xe4\xa6\x13_\x10\xcc\xa7\x1e\xe4o\xe2\x9c\xa4\xb1\x1f \x01\x1d\xdd&\xa8El\xdb\xae=\xc4R\xe5t\xe8\x9bi\xab}\xe1w\"\xd3\xbaF\x9e{\xff\xae\xdd\x90\x92\xbe\xde$#1C\xcah\xd7\xac\xc7?\xbdTS8\xa9\xd5\xf7\xdb?nH\x8d\xbcLVwi8_\xe4`\x07\x0e\x8c\x06\xc3}\xf872\x85\x9f\xfd\xdcT\xec\xefdz\xcb\xea\xabl\xc5\x02\xbaz\xd1E\xb0,\xff\xe3\xf6\xffQ}\xdc0\x1f(\xfa\xcd\x05u\xab\xd6:)\xa9D\xbd,\x91G3t\x02\xc8\x14\x16\xe1\xd9\xbe\xa5\x10\x17\xcdh\x95-\xe1,\xc4\x86\xafl\xeat\xf49plo\xcc\x9f\x0c\x92\x90\x85\xcbaR3Q\xa5$\x958\x81P1Y8\x81\xd0\x01\xc2\x9c\xfe\xda\xa8\xb32}L\xddb+u\xca\xaf\x13\xcf_\xad\xa2;\x9eP\xa9\x95\xbf,+\xaby\xc3\x86z\x82O\\\xe5D`F\xa0\xd4\x11\xc6\xc6\xa9\xc8\xcb\x93rG\x17\xde\x1f\xff\x9b\xe9G\xc2\xf2\xceZ\xd0\x1aKR\xc6c\xacy\x814\xeai0\x92\xd2\x85\x0eGk\xd7\xb4\xa2-x\xb2\x9e\x9e\xfa\x81C9\xc7\xd8\xb4(\xcb\xade\xf7\x95T\x9e\x0f\xf6zV\xc8\xdc.\xb8\x0f\x8a\xe3\x9e\x1b:\xd5\xf3?\x81A\xaf\xda]\x16*\xbc\xde\x9a\xe8i\xea\xc7\xd3diw\xfan\x18\xbak1\xf36\xdb\xf2\x82$\x0e\xfc\xdc\xae\x85\xc4\xc74\xc6cJeX\xce\x95\xe5\x82\xbd\xb9\x19\xc3&\xa4Ne\x0e\xb1\xb3\xff\xf8\xe43\x8dh\x06<\xb5e\xe39Sp\xec6\xe6\xcb\x07\x83\xd5|\x05\x8d\xdcc\xd9o\x87\x83\x81\x03\xa7\xfa\xd2\xd0-ZF\x94V\x06Y\x0d\xe9\xf2\xdd\x188.\xa46\xe5\x9d\x13\xa7\xdd\xd0\xdd\x14\x8c\\\xb6v\x7fh\xb4g\xcdInQ\\\xc1\xacW2q\xd7t\xfc\xb2\x9e\x07\x94aKR%\xdc\xb4\xc9\xf3\xcbBw\x0c^7\xe5\x0cE\xb2i\x0f_P\"\xf1\x11KTsP\x89\"\xeb\x9a\x17\xc7e\xce\x88F\\\x9f>=\xc1\x9d\x11\x9002l\x9aY\x94$iW\xef\x0c]\x0b\xb3\xf7\xfe{\xf4\x81\xd9\xc44\n\x03\xe6\x12\xc3v}\nc\x88\xd7O\xe8!\xe1\xa4Q\xaf\x87J\xe3>\xc3\x99\xa6\x91\x1b\xb4\xc4qn\xf4\xc1 \\R\xcaK\xddh\x98\xd6\x88\xcb\xd4\x93\x9d\xfe=\xd1\xb0n\x9aO\xea\x9d\xa91p\xf2\xa5\xf0\x8c\xba\x05\xd9\xe7\x0c&\xd5\xa9[\x92ofC\x08X\xe3\xd05\xef\x97\x7f\xa0\xe7\xaa\xd9Gr_\x9f\xc8b\xcf\xe4\xc3\xd9\x89\x0eR;Y?\xffZ\x97\x98gO/\xe69\xd0Iy\x98\x87Y\xf3\\\xc4A\xd5\x1f3\xbd\xff\xb0;\xc7\x9e\xd9\x14.cF<\x1ao[\x96\x94\xdeGk%\xcb\x82 \xb9\xd4\xb9\xf7\xa2\\\x7f`\xf0\x06\x8f\x1a\x11\xd8C\xb3\xe7\x1cH\x82']8`!^\x9ad\x97]\x84\xaaT\\\xe3%\xe72\xef<6\xa6f\x02\x0ds\xc21X\x1f,\xd8\x84\xcdMM\xf2oq\xddj\x93l@\xe3\xdc\xc1'\xad\x92\xf9\x99H\xeb\xa2\x8dfB\xaf\x7f?\xfb\xdb\x184\xf6#\xef\xcf\xce^\xe9\xd3\x17\xce\xfc,\xffw\xa2\x86\x873mg\xcc\x1a\x90\xc8A5\xb5n\x0b\xcc[]\x9f\xb6\xf2\x14\xacs\xca\xfdX\x1f\xd1X\x9f\x98e\x1d\x1b!NOk\x04a,\x97\xd5:\xf4\xdaj\x97{lT\xd4\x9bu\xd6R6P]_\xc4\xa5\x9fLq\x86N\xd2K/lNl\x13\xf2s\x92\xffL\xfc/\xeb@\xfeQ\x00\xd90\x84H\x84&<6\x86\x7f\x088zi\x05\x92\xf8uJ\xc8o\x9dBn\xa8*\x8f\xd0\x1e\xd4\xa3\x8b\x9b\xfe\xc2\xd8vO\x9e\x80\x00\x13\xfd\x1d\xd8u\xb6K\\:\x02\xb0\x8d6c\xfc\xee\xef\x0fe\xb8\xe77\xd9Y\x19yC\xfb\xf5Z\xb4\xc9\xef\xdf\"]\xd6W\xadw{\xcf]\xb0\xaa\xc8F\x0d\xf7w\x8e\xf2\xe4xG\x947\xf7^\xbe={\xfe\xe9\xea\xc5\xdfPs\x847\xf8\xeb\xfd\xd9\xcfW\xcf?_\xfc\xf5\xea\xecS\xf5\xe0\xfc\xe3\xd9K\xfa\xe0\xea\xc5\xf3\x8b\x97\x7fm<.\x1f\\\xfc\xf5\xd3\x87\x9f\xdfkJV/J\xc5\x05\xedCLn/(}\x1b\x9f\xa5\xed\x9eg|u4\x97\x0e\xc5A\xda\xa8\xcd+\xff.J\xfc\xe9\xb8%\x83$\xd4\x89y\xb5C\x18/\xf3[z\xa59@\xca^\x91\x8e^\x9c\xafH\xf0\x8d@\xc9\xbe\xbd\xf9o\x06\x81&\xbe^\xef>\xbf\xba\xa6;\xd7j2\x01\x0d\xc4]~\x9c\xadH\xa0i92\x1f\x02\x8dO\xb5\xad\x06\xbac\xa5\xfc\xd4/\xf2\x85\xa6\xd5Y\xedT\xc2\xd2\xb8\x80\x95b\xab\xaa\x18;\xc9\xaa\x92W\xd7w\xcc-\xb37_\xb6\xaf2X\\\xc6\xaeK\xdcY\xba?3\xa5\xc0\xe5\xda\xe1C\xdaH\xed\xfb{\xb4\x0fa6?\xc4\xa1\xef*\xeasMfs\x7f\xc7\xe1\xec\x96\x0b\x16s?5E\xaf\xeaE\x98H5\x0f\xf4\xee\x88\xfb\x0d\x19\x0bO\xf7?\xd03\xb0\xfb\x03\xbd\xf0e\x7f\xb0\xdb7\xdc\xb1\x10nli\x98\xa1\x98[U\x01W\xd3\x0c0\xe6\x16W\xe2\xd6\xd7\\\x92r?c\\@\xb6s\x04\x9b\x9b9\x1cCl\x0c\xb3\x99\x1a3\\3\xafa\x92\xdb)f\xcfK'\xc3\xcbv)\"\xbd2\xd9\x0b\x98\x9f@\xa9[{\xccm\x0fO \xa9?\x9f\x13\x96\xfc\xaa\xf6p\xe1\xa3\xe5J\xfda\x86%\x8b\xbauK\xb6\xde\xdc\x0f\x07{}$c*\xd8$\x93\xd0\x13)_x\xbc\xb5u\xd4\xe4C\xb8\x94~\x12_\xb2\xfc\x83\x92\x19\xb0\xf6\xac\xd8\x1a>z\x8f\x0c\xba\x93\xd1kFS\x0d\xe4\xeaj\xea\xe7\xfe\xd5\x95\xb6_\xa9\x9d;p\n\xf1D\xc3:\xe7\x94u\x16\x8f\xc7`-\xfcla\xd1\x134\xf6\x96\xfe\xea\xd1\xe31\xb8C\xed7\xe2\xf2\x89\xf0v\x06w\xa8]\xfd\xc6\xec\x11\n\xd7\x84\xeeD \x9dlA\xde\xa5!\x85\x86.:\xc6)\xf86*\x93\x12\x9b\xe0\xba tg\x89T\xddc\x94\xb8v\xc0M\xee\xdbZ\xbd'\xde-\xb9^\xf9\xc1\x97\x8fIt7\x0b\xa3\x88\xab\xe4\xa7d\x95\x92\xa0\x99\x17\x14=\xdeW~\xbe\xc8\xb8=I\x15z\x99\x7fY\xde\x9e\xb0\xf4\xb3z\x06\x8f\xb8`\xb1dM\xda\xd8f\xb5p\x91\x9a\xf0tk\xc5>#^\xd4x\xad0\xd6\xad\xfd\x0c\xffG\xfa\xa8\x11\xc64\xfa\xd8\x9c\xad\x13\x18>R_\xab\x9a&\xd4\x07@w\xdd\xf6\x7f\xda\xa7\xe3\xc1\xfdd\xb8\xf5\xf4\xf2\x97\xe9\x8f\xce\x9f\xb7\xbb\xb6\x88\x01\xa3$\x95\xb1\x8f>\xef\xfb\xc6\x86\xfd\xff\xb3\xf7\xef}q\xe3\xc8\xe20\xfe\xff\xbe\x8a\xc2\xe7\x9c\xac=\x18\x03I&\x97\xce\xb0,\x03\x9d\x1d\xce\x06\xc8\x0f\xc8\xcc\xce\xaf\xc3\x971\xb6\xba\xdb\x1b\xb7\xddk\xab\x9b\xb0\x9b<\xaf\xfd\xf9\xa8$\xd9\xb2,\xd9\x86\xb0{.\xcf\xd7\x7f@[\xd6]\xa5RU\xa9.T9\xd3\x18\n\xc9`\xc4*{\xf2\x04\\\xd5EI\xde\xf0A\xb2\xb1\xc7M\x87\x0b\x1e]\x80xX\x80\xc0\x1f`k\x97\xff\xfa\x0f\xf4e\xcfi}\x8c\xc5\xfb\x80\x99\xd2]L\xf5\xcd\x82\xed(\x17\xfa5\x8a\xe9\xa2\xf9z\x8b+\xd8\x18\xf1\n\x86\x03P\xba\x82*\xae}\xc8\xa1\x83\x90\xd2\xb1\xa1`\x1f^Y\xc8\x9dg\xfa\xfd\x99 w\x9e\xe9\x0e\xc6\x05V}\xa6\xd3\x99\xa5\x99*M\xc5%\x81^\x0d^\x18\xb9\x85\xd7&\xa4S7\xf7\xdats\xea&Zj\x8c\xa9\xa1\x96:\xc7\xd4\x95\x96\x8a\xe1\xdd\xea%q\xb9\xe1\x91\xe2m(\xfc9!\xb7W\x08vk\x97\xbb\xe3`\x7fQ\x97\x8c\xbb\xacqw=\xae\xd5\x947\xca\x9e\x84K\xb5X\xee\xf1\xd01j\x96\xf7E\xbeHJ\"\xb3%\x01\x0f*N\\^_\xd8\xc8|A\xa8Z_\x88YV\x8d,\xbf\x90\xf0\x93\xd6\xec\x8ao\x0fw=\x08ZK\xe3=_\xa62\n|c\\9r\xcf6\xfd\xbc\xd8\x9d\x8b\"\xf4\xc1>\xa4n\xc6\xdd\xdbh\xd7~\\\x81P*)\x18/\xf7\xf1Z>\xea\xbc\x967\xac\\\x9b\xa6\xc5z\xa6\xc3\xea\xc1\xe9\xb4T\xb1\x1cVE\xb5\xca\x96j\xe2a\xd5\xe0\xfa[\xaa\x98\x0f\xab\xa2\x82\x8fFn\xa3\x8a\x81\x8235\x05\xf2AV\x0d\n\x89\xfd\xecu/\x95e\xbf|\xce5\xaeG\x88nF`\xb4%\x13}W\xb4arq\xaa\xf49F\xb4v\xbf%T\xe1\xd8\xf2\xd5\xce\x90Au\xf2\x0d;\xdc\xb9>\x1e\x82\xe8[\x97x^\xcdJ\xc8x0l\xf3f\xf0\x03$o<\x94i\x91I\xee\xd2I\xb6\xb9y\xe5]\x19\x07\xcf\x8d\xf2\x90\xd7\x16\xf4\xa8\xa6_?h\x02\xccr\xfb\xfaZ\xb45\xb4\x0d\x1a\xacIQ&\xdc\xef\x92PE\x92IA\x92\xc5\xe4\xf3\xd9\xd4u\xd6;\x81\xe3u\xe7\xd8e9\x9e<\x11\x02:s\x8eW,\xcf~\xcf\x85cF>\xd3\xcb$\xd2n\xb1z\xf4u\xfaUX\x18V\xad\xd5X~\xefDa\x9a\xde\x84\xd1'\xa7\x92\x1eb\xf8Y\xb8!\x8aZ\xcb\xef-\xaa\xc5ka\x07\xc7c(\xb4\x94\xb3\x8de$\x8e4\x06F\x92\x0f\xa2\x85\x9d\x1e+_\x8b\xc2\x97|$*\x08\xe4LZ\x8d}\xa0G}K>\xed\x1a{ie\xf5\x11\x1aT\\]\xdb\xa2X&\x1f=\x10\x89\xfat\xe9w\xc9\xe7Q\xbbjU>\x93Ooo\x9f\xffk{k\xd5N\x93OW\x87\x07\xd9b#.D\x12SRS\xee\n\xb6\x90\xb3 \xb9\xb9B\xc8\xd0\x9e\xdc \x1e$\x93ps\xf3\xaaa\x8d\x10\xf6D\xe5\xfd\xe6YQ\xcd\x03zt\xfd\xbf\x0e\xbd\x81\xd68<\x14\xe3\xd5hL=wU\x07\x89\xdf{f\xcdx\xbb\xa6\xb5\x89\xcc/\x84\x97E\x93<2\xe9;\xb2\x92\x0c\x91\xe0$\xbb\xc2s(S\xfc\xc2u\xd9\xb5Y\x84\x10y\xf5]\xa9F\xfe\xca\x83i\x91/\x00\x9d\x83\x85i\x9aG\xca\xcf\x0fY\x19NI+\xe1\"\xcdo\xb5#\x81\x91\xa3n\xe2\x16\xdc\xa7\x0c\x0d*w\x94\xa1\xe7C\xe2\xe6<~b\xc8\xdb\xea\xa7G\xf0h0x\xce4\x1f\x0c\xceA\xe34\xc8rq\"\x88\n\xcc\x94\x8biRX\x0f\xf9\x1c\xdc\xb3\x8b\xbdg\x97\xd6\xc5\x8e\xeeI\xb0j\x9b{6I\xae\x0d\xc1\x14\x98\xc2\x05\xc2>\x14\xc14\x91Z\xc1\x8c\x86\x13\xaf\xcaoT\xb07\x8c],z\xaf\xf2\xe9?a\xec\xf5\xd2\x98\x16E\x01\xbe\xff\xc2\xce\x15\x01\xeb\x81`G{\x05\x87\x83h=u#e\xee\x8b\x97\xdf{\xae3\xcd\x8bq\x18\xcd\x9dA\xa8\xa8O\xe3\xf5\xd9\xaeY\x10\xf1\xcc\xe2\x06r\xf7\xb5.)\x10\x82\x88W\xaa\x18\xd7\x1dL\x8c#R\xc3\xf8$+T\xcfL\x8d3\xdb\xbaC\xfe\x01\x9e6\\\xe5n4\x84\xban)\x9c\xc3r\x97\xb1D\xb0/\x0c\xc2\xcb\xc6\xd1\xf5T\x04\x8c\x94\x8c\x0dFO[\xa1I\x13\xe7\x0b6\xd0n\x08\x93\xc3J\x7f\xd3\x89\x1c\x11\x93KI#2\x04\x97\x92v\xebx\x9e\xcf\x0d\xe1\x1b\xa3\x82Z\x91\xc6\xe0\xc6\xb0\x19\x96%kgP\xc5\x9fI\xfbs\x1d\xa2G\x8fK\x0c%\xdb\xfen\xee\x96\xac[ld\xb5x\xf6\xab\x17\xcc\x86\xf2\x83b\xa9|\xdd\xef@u\x0di^\x15\x945\xf1@\x06\xe6\xc5I\x1b\x8b\xf3LY\x1c\x86\xceh\xa5\xec\x03#H\xc4=\x88\xf8\x8e\x16\xe8\xcd\xef\x19\xb7qS\x1a\xe5\x1fqA\xd3\xba\x0f\xca\x17\x0d\x18$ \x945 \xac\x0c\x80P\xb6\x00\x01},\x98\x16\x1d\x05\xd3\x86%G\x9bd\xc3J7A\xc1\xa0\x01\xa4\x82B\xa9\xafv*V;\xf5D\x0c\xbd\xe8~(\xa9\xc6\x12\xadp\xb9\x02I<5_\x01={f2\x18\xcb\\\x8b\xb0rwW\x17nrt\xb7\xfbB\xc7M\xdc\xa7D[R\xa9\xaa\xbd\xb8TS\x82\xd5\x87\x88\xbe\x05\x97&\xb8\x8e}\x98\xfb\xb0\xf6a\xe1\xc3\x0c\xf6`\xa9\xaa\x89\xdbhU);n}dD\xa5Y\x94w\x87\xc2\x06\xde\x11\x06\xd9Oa\x04:\xbae\xcf\x0d\x92\xe0\xcd \xb6q\xc6\xb3\x1e\xe3\x8e\x84r8i\x99v\xb0\x1a\x13wf\xd4\x19E\xba3\xe6\xa6\x072F\xef\x1b\x88\xe1\x0fp\xf3\x06n67\xcd\xd46\xab\xd1]\x08G\xacwn\xe8\xce\x91T\xbd\xb9\xf2\xf0\x8em.\xee\xd8\xee\\L\xf3P\x06\x81\xb7_\x0b\x1e\x0b\xb2\xba\x9a]4!\x1a\xcd\x7f\xcd}\\\xc3\x1eTq'\xde\xc0\x066\xb9F\x8e\xc3\xf5\xbc \xce3b\xb8\x14\x06\xb5\xb3\xb9\xbb\xf6\xe1\xce\x879\xb7\xc5\xe3w\xc4\x03\xba\xf6\xd5\x0b~<\x1f\x1f\xfc\x99\xc7j\xa5\xc1\xf9\xf8\xf2\xc3\xf9)\xec\x89\xdd\xf6\x8d\xe7\xb3\xd5'u\x11\x1c\x8d\xdf\x1e|xw \xfd\xfe\xa9ww^\xf5\xf8\x9d~)\xfcL\xbf\x12\xff_\xdf\xdb\xdf\xb4BR<\xb7\xdcm\xec\xe8\xdb<1\\\xf1\xdc\xdf\x94\xd1rH\x85Fm\x8aD1pD\xee\xc5\x0d\xb1\x18\xddd\x83\x00\xad6a&\x1f\xec\x96\xd6+W\xa8\x869O_\xeaGCU\xcchc]}\xb5-\xdc\x0e\xa7}\xd9\x7f\xdep\x05\xa7\x07\x82\xc9\x8cxp\xf8\xda \xb39FQ\xde\xe2(\x10\xa6I\x16\xa6ig\xd7:;\x0eP\xb9&\xeb\xcf\x08r\xa4Q\x9a\x97b\x00\x9d\x05\x9aF\xe6\xdcu\xc5\xe0\n\x86\x0c\x0e\xba\xe6\xde\x93\xa8\x15{\x1a@\xba\xd2\xb0\xd9)\x81d-\xb0\x11s\x03a\xdbu\x8b|V\xed\xab\x05\x90\xd8\x81\xfb\x83GM?\xae\xff\x93U\xbcNh\xe7u*\xcffA$\xa0\xf8\x80\xbaa\xa7+\n\xae\x01\xd6\xa3T\xc5\x88,\xe7\xc9\xdfV9}\xd3\xe1\x8b\x83=7\x05 ?\xd9\xb3\xf0\xd6^\x0di-\\,\x1f\xa5\xb1\xd7C\x1a\xfb\xb7\xcfO_>Fk/:\x14\x0d\xa1j-}\x94i|\xd1\xa3b\xc8\xdb\x9a}k[\x83t\xd8\xa2<\xa3I\xb6j\xdf\x0c\x81\x95\xc5\xe3|0j\xf6\xbb l2\xfcX\xaen\xf8\xb5\xb5\xbb\xf2!\xf4\xe4e>\xe3@\x19+\xbc\xa9#:s\xe5b\xaf\xca\xfa\xf7Y\xc9v\xe50\xd2C\x0c<\x92\xbaH\x83\xea2\xfa\xa67\x851\x0b\x852\xb5\xd9@\xaf\xcd\\\x96\"\xbf\xce@ [\x92\x96FId\xb8\xb5\x9d\xa2p\xa1\x99\xb6l\xa3\xabvx>\xf6\xd0|yp\x93\x17t\x04N\xc8\xfe\x1b\xd0\x1f\xcb\x92%\x0b\x0c\xe11\xce\xe2\x11\x94\xae\x13\xca\x04\x92\xc5\\\xff\xb9\x99\xd4]\xcb1%<\"H\xb3\xaeD&\xeb5\xd6\x1f\xba\xeb\xbd\xa0!\x1b\x89Zg\xc9\x92\xf4\xfax\xa2\xb1\xae\x1f\xd3U1\x02\xe7&]\xe9&\xed\"\xc3a\x98\xbdO\xc3\xbb\x118Q\x98-\xd3\xf0\xae3\xdb\xe5\xbc\xc8W\xb3y\x9d\x9b\xf2\x04K\xa1y\x98\xcd\x08\xcb\x8c?,\x99RT\x01w\"\x8c e\xce\x92/\x96y\x99T\x0b\xe6Du\x82uu\x94Bb\x1e\xd5b\x1dS\xa6\x14\xfc\xb0\x8cQ&\xa0\x96\\a\x9a\xadhF\xc9gzB\xb2\x15\x16\xc2\xb7\x05\xc9V\xb6\xecK\x9c\xf8|i\x9b\xf5\x15v{e\xe9\xa9\x12\x1ek\x04N|\x93v\xcc\xe1Q\x11\xceX\xa6\"\x9c\xd93\xf0\xd9ey\xac\xd3\xca\xb3QRT\x19)\xb1\x80\x16f\xfd\x9cP\x99\xf3sb\x1bG\x11\xce0\xc0\xa3\xc8\x99\xb2\xdf\xf6\xacg\xeb\xaa\xf5|\xdd\xd5\xb8\\w\x96\xb3c\xc1\x8f\x8a|\x89\xb9\xf2\xa5%\xc3\x8ao\xd7\n\x9ec\x91\xd0\x05\xd7\xe3\xc5\x92&\x84\xcd'\xe1\xbf,\xd9\xb2\xa8\xb8[R\x9eQ\xfe\xb6e\x8dE\xb6\xd8\x9a\xa5(r67\x84\xfd7gy\x9bG\xabr\x04\xce\x94\xfd7g9\xce\x96\x08x<\x02\x981\xcb\x9f\xc9\xddQ~\x9b\x8d\xc0\xf9D\xee\xe2\xfc\xd6\x82\xca\xfeL\xee\xde\x17\xa4,y\xbe%\xfbi\xcd\xf8a\xc9s\xad,\xab\xf0\x0e-\x93\x19\x0f2\x92f\xca\x8cs\xe9\xca|Bh\x18\xab\x05\x16\"\xc1^H\xc2\x0c\xcb\xdf\x013U\xe0\xb8\x118\x0b\xf6\xdb>\x07U\x108\x99\x95qW\x1dY\xcfp\xee1gn\x9b~\x9e\x91\xef\x03\x9e\xd3\xba\x11D\x988\x99\xd16\xbb\xef\xc3\x121\xdd\x92\xfd\xb7eY\x95<\xcb\xaa\xb4e\xe1G\x89\xfd\x1ca\x19\x92l&\xf2$\x99\x05\x19\xbd/\xf2\x99\x80\x9b\xa5\xf8i\xcex\x1eRRm\xcb\"\xa4\xa4kKr \xdb\x08\x9c\x12\x7fX2\x11\xf2 \xb7Y\x89?\xec\x99\xf80J\xfe\xcb\x96-\xe5\x91=\xab.\x962\xa5\xb3\x9f4LS\xde\x07\xfe\xcb\x92mU. b\xec\x92\xff2g\xbb$\x9f\xa9\xdc\xd1T\xfe\xb6dM\x16\xa4:\xf3h\xb2 ]\x87\xdde\xbe\x8a\xe6\x87a\x16\x116\xa5\x94\xbdE\xf8\xd6\x91\x9d\x1f0\x98\xd7\xde_\xf6U\xec\x17\xcci\xdf/\x98U\xeeX\xcc\xdb\xb1e\xf1\xda/Q\xa9>Z\xa5\xd4d_3\xcdX\xd1\xcfy\xbaZ\xd4P\xb7\xc6\xd7\xae\xf5\xfc%L(\x87\x96[\xfe\xcb\x92mNp*o\xd9\x7f\xcd\x04\xb4Y`\xcex(\x1e\x85\xa6\n\xa2w|\xe4\xc0\xa6\x90\x18\xb9\x8d8\x04^P\xa6ID\xdc\xa7^\x93\x1dX\xa3j\xdb?\xbe\xa2VE\x93\x94>'2\xd2Z\x1d\xa4\xb0}\x990 p\xad\xa9\xa2~\xf99:\x8f\xf9)\xcc\xe2\x94\\\xe6\xcbwdMRw\x1d\xcc\x1b \x9e\x0f\xeb\xa0]=\xec\xf5{ll\x8e\xa2$t\x9ca@\xcc\xbe\xae\x19\xdb{\xf2\xc4\x98\x1e\xd4\xd5\xb6\\\x01j\xb3X\xb6\x9b7\xb5.5\x88\xdc\x0dc?\xbe|\x01\xe3\x87\xa0\xaa\xdf\xed\x0e1\x97b\x81\xcb|\x80S\xd1\x86\xa4\x98\xfa\xd0\xed;O>b\x00=j}\x95\x16\xde\\D\"\x99\xcc\xaf`\x0f\x96\x9b\x9b>D\x13\xf6&\x82\xfcV\xaf\xed\xe5\xe6\x11 `\x0f\x92V\xc0\xc6#\xc20%\xc9\xa2\x84\x94\x13r\xd50f\xcb\x87\x08\xb3P\xcb\x9d\xed\x1c\xabu[\xa1\xc7\x99\\\x89X2+\x1e\xa7\xd8\x91{\x9d\xcb\x86Wht/v\xbd\x07\xfbfp\xa2E\xb8\xfcqu\xc3\xd6\x11?(\xb5\xf8\x12e\x08\xb3\x9d\xd4\xe5G\xfd7\xd5\xa8\xd4 \xaa}@%Gg'H~\\\x88\xf3\x96W\xe4TGqc\x02\xe4\xa1\x0c\x1b;\x9d}\x16\x01o\x95\xf6\xaa\xea\xeb:\xee\xd9cC\x0d\xc6\xc2\xbf\x1c\x9f\x1e\x9d\xfdr\xfd\xd3\xc1\xe9\xd1\xbb\xb1\x1c\x0bR\xd4r(x\x86p\xbe\xbb\x1e\x9d\x9b\xba\x92\xde\x16\xa3s\xef1\xbc\xb7\xa2dUEf\xc1}\x96\xf2\xd8\x17_\n\x01 \xf3\x04\x90`uI\xe6\x08\x15\xd7\xc1\x93\xd5\xecO\x92\xf5\xf5\xa8U\x81\xec\x10\x96G\x1a\x97u\xca\x87\"\x10\x1f\x85N\n\xbeck\x98\xc0\xba\x1d\x9b\xf7\xd6\xb0\xb6W>\xc4\x93\xd5\x15\xef.n\xc7\xbdVHy\xe8;.\xf4Z\xfb\x03\xd5\x80b\x867\xa8\x9f-\x85bK7\x1aK\xfd8\xfdhB\xcf\x90\x8e\x88\xc86<4\xe9\xfbpF\xfe\xf2k\xcfA\x86\xb7\x17\xfa\xad\x1e+\xdd\xe9Kz-\x9c\x86\x9a\n\xba\x0e\xa2\x19\xfcm\xd2\xe3\x92\xf7$\xaa\xd3\x06UQ\xa0k|$+W\x85\xc0`?\x87\xe9\x8a\x9c\xe4YB\xf3\x02 \xba\xdeq*\xae.\x90T\xc0K\xdcu`\x984\x97\xed\x80\x0d\xcc\xb41\xed:|\xd8$\xac\x82\x82L\x0bR\xce\x95~\x95\x96\xfb@\xd3R/\xf8\x18\x94\xd2\xe8\xebzZ\x87\xecR\x1fm?To_-\x06\x08\x83<\x904\xc5\xd4Ur\xa5\xd1P\xb4\xe6\x94k\xb4^\x17\xab\x94\x94\xd7\xd7\x0d\xdd\xf0\xeb(\x8c\xe6\x04\x13-\xd7\x8b\x85Bp\\_O\x93,\xc6\xdcv\xaa\xa5\xad\xf7W5-\xc8\x04~\x8d\xb7\xb5\xfb\x06\xa8\xd5\xb1`\xb3\xe0ds3\xbbB\x85\x01\xae*s\x0fO\x83\xbe6\x82(_,\x93\x944\x07a\xbaB'\xa2\xfb\x06\x96\x83M\xa1\xe3hT\x0cQ\xc6)\xecI\xddn\xda\x8e\x04\x84\x13\x98\xfc~\xe3\xf5\x18\x07\xa8\x95\xa2\xae\xfe?\xd0\x07q\xaby[ OY\x92\xc7\xda\xe2\xae\xf3:\x86oD\xa9\xec\xc9\xd4)p\xd1!X\x86\x13!\x07G\xf9\xe0\xbe|\xd1Z\xe5#\xcd\x82if\x88M\xdd\x1a\xad\x0d\x1cB:\xd0\xf2\xa5\xa8a\x99o\x01\xa3\x11\x1a^\x12\xb1\xbe\xea>\xa3\x19Doq\xb5\x81B\xb5\x8c\x16V\xd1\xef\xc3\xa2$\x05\xb0\xe9C\xc3\xb2i\xbeB~\x1f6A7K\xd7\xf6Eq\x15L\xa5\xf1g\xebK\x98b$c\xfc\xff\xe5\xcb\x90]\xdf\x9c\x9d\x1b2\xcd\x0bb4\xf7k\xb9\xb1ZK\xcfx\xbd\x93\x94Hm\x9c\x8eI\xca\x1fs\x92\x82r\x89l|\xee\xc3\x8e\xc9\xf5!C+F\x13R\"\xd9K\x93C\xc4if4/\x0dS:\x82\xa4\x9e\xf2\xd6\xb6\xbb\xd7\n\x84SJ\x8a\xff=\x0b\xc0o~\xff\xa7-\x02\xc34\xf7@\x13F\x04\xa0M\x08\"/\xdb$\x18T[z'\xc10q8 \xc5cM\x02\xefA\x9f\xf2\x17\xcb\xd0\x0cJ\x8b\xae` \x8c\x00e\x06\xdc\xe3cs.\x86\x1dy\xf5Y\xd9\xd2\xa0\xe7\x87\xd9\xb0j4\xba\xa4\xda%fU!\xca\xce\x1e\xc3N8g]\x87E\x98\x853R\x8c \xc9\xd6a\x9a\xc4bg0\"\xc5\xb4'\xa0\x8d\xbd\xe9\x95:*=\x84\x13\xe6\xbe\xef:\xc5I\xd9Z(}\"\xdc\xeee\xf2\xfe\x17\xcc\xe5\xeec\xcc\xe5\x8cP\xde\xbb\x01jo\xc2\xcb\xc1\x9e\xdeB\x0d\xef\x15\xe1\xe9\xb6\xfa1!W\xda\x1e\xfd\xea\xdf\xdf\xf3{\xbf\xbb\x93\xce\xbd\xbb\xe6nC\nn1hq\xd6\x8e\x16\xc0\xc12/O\xc2\xcf\xed\xaf+\xf9\xb5\xfd\xa9\xc4OIy\x9c\xbd\x0boH\xda>x\x94\x8f^M\xc7\x9b\xf2\xa5,\xcf\x87l\x11\xd2hN\xe2\x8b(_\x92\xb2\x8e\x0dj\xfc\xbc\xb5\xe5\xb7*C>\x05{\x8bf\xf5x4)\x9d\x10\xa2\x14F\\\xed\xbe\xe1\xa3\x82\x1f 4z\x9ag\xfdz\xcd\x0fN7\x07\xa1\xca\xaf\xea\xecaq\xcf\xf3 \xdb\xdclCr\x15\x82\xfb\xf53\xe1\xdb\x11\xbd\x04\xb2\x9f[[V\xd2\x99\x0b{\xcc\xbc+\xea\x80\xb5\xbe\xb4u\xabP)\xb7$EP~J\x96\x97\xf9'\x92\xd9\xc3\xef\x80\xa2\x11\x0f\xfb\xdc\xc5\x19_l\xcb\xa4\xc3\x1e\xf7\x0cb\xfd\x9a\xc1\x16\x9ft\xbe\x06+}\xfeK\xff\xe1a\x15^\xdb\xa2`r)\xba\xeb\xfc\xdd\xf1\x8cq\xa5\\%\xb6r\xa7V\xaa\xd4w\xbd\xa8=B\x15\x02\x8f\"\xc1C]\xc7a\xc3\x17\x0d\xf6j\xa3\xa9\xf5\x0f\xd3\xb8m\xc8IL\xa1H\x9d\xc30\xfb=\x85(LSX\x10:\xcfc\xc830b\xd4\x96\xcb\x8d{\xcew+&\xa20S\xd8\xf5\x02)x\xd2no\xd0a\x87\x08\xe0\xe2\xe6M%\xf5^\x1f\xa4\x96\xc5H`\x1f\xb4\xaa\\\xf4:\xaf\xd8\xb1\xdd\x7f`}\x9d1 S\x14\xd5\x15jD8\xcdW\xb8\xc0\xb6y\x1b\xc1!\x8dd\xf2\x97\xedr\xedt\x19\xae\x9c\x87]+\x10\xe1\xc8\x18\xd3^\xdd\x9e\xa1\xe6\x8eJ\xd1?\xc7\xd9\xf4\xfeun\xfcs\xbak\x83\xe4<[\x93\x82\x82p\xfbKsX\x16\xc9\"\xa1\xc9\x9ap\xefON\xdf.\xd3\xd6\xb9\xe9\x0c\xec\xfb\x9d\xfb\xfa\xe5\xd0\xadpd\xd4w\xdd'\xb8\xf0\xf4\xf5B\xd7\x1f\x0dE\xfa\xae\xe7:\xc7\xe3\xeb\xf7\xe7g\x97gz\xd0\xd1U+jA\xe3s\xd9%\xc8\x02)\xcc\x12\x8e\x99\xdc\xdd\xef_x\xae\x93L\x8bpA\xf4\x86\xe4S\xe0\x05\xa0\xcdS+\x8f\xc2\x12\xa0I\x10#7\x97ix\x07{\xe0dyF\x1c\x1f\xa3R\xecx\x0d;\x17\xee\xa4\xb0,\"\x96\xed\xaf\xe1:\xe4VE#\xc7\xe7\xa4(\x0dP\xe3/\xa3\xbf$Y\x9c\xdfV\x08\xc3\x0b\xf2%\xc9\\\x1e*\xa0H(q\x9d\x1fx\xd1?T\xc2\xec\xb7{\x1c\xbf\xfe\xf0q[|r0?\x1a\xbc\xba\xc2\x95\x14 \xde\xbe\x81bk\xeb\x8d\x07\"<\x8b\x12oe\x92L\x8a+\xc3\x8d\xa4\x00\xcc\xd2\xd5\x0e\xc4\xaecE\xa0\x1eP\xa3\xb6Zi-#\x02\x16\xa2v\xe9.Kq\x8e\xcf\x8f\x17N\x91\xa0\x03t\x1f\x9a\x9f\x85\x93\xd3I\x88n,\xd1\xfe\x04=\x9fka\xd4\xa5\xe3h7\xfb\xff^D\xfa\x17O=\xd7\xf9D\xeeJs`\xdf\xdd\xdd\xfe83\x96\x8e\x17\x82\x86w\xf1\x07w(\xf9\xe0~>5\xd9$\x17\x13\x871\x11\x05\xd9\xfaky]\xce\xc3\x82\xc4\xd7\xd7\x8el\xd4\xfc\x0d\xef\xfb\x1f8\xa2\\\x8e(\xe7#\xfa\xc7\xd7\xbe\xf1\xd8\x10\xab\xa38\xd2\xf7\x9b\xd7\x90~R\xbe\x97 |6\xf5M\x04\x99O\xf3wy\x14\xa6\x84\x9f#\xbe\xe4\x9e'\xb0u\x82~\x07\xd1\xa1\xacsVG]B\xbb\xb2\x02\xcd\"-T\x18;\\\xc34%8be\xe9F\xc2\x12\x19\x1e\x008\xde5#8773\xd8\x84\xc2\xab\x18\x13F\xc4\xf7\x9dl\xd6\xbd\xf0\xd2\xe2\xea\xf7\xd9\xffx\xb6\xf7y\x0f\xa9\xf4\xe2\xe5C{\xfb\xa8\xa4\xd2\xee\xeeK/\x98\x9a\x899\x93\x07\x17\x13\x9e\xea\x1b\x87\xf9\xbe\x07\x95a6r$V3!='5A\xeeC\"\x03\x84\xa2\x03\xb6\xf6foz\xa25\xdd\xecH\x87\xc6\xcd\x8d~\xcf\xb9\xea\xf5\x80\xf3t\xd74\x03\x18{\xbdw-\x19#b\xcf\x04\n\xcem3X(\x03_\xf2\x18B\x82\xa7!\x0d\xdf\x11\xc6XI\xa0\x13L\x8c\xa5\xf9\xf2Eu\xd4\x9e\x19$a?\x86\xb1\x8cW\x04\n9ju\xcf\xc7=)g\x95\xec]}\xaa\xcb3\x11\xd5J\xa0\xd1*\x11e\x13\xe8\x8eVc\x1d\xbf\x81uy\xfa\xbdY\xd4\xf0\xbdM\xce\xd9\x07\xbe F\xefd\xc8\xbf5W|k\xfc\x9b\x03\x9b\x90\xa1\xbf\xdb8'e\xf6{\na\x14\x91%\x85\x82\xcc\xc8\xe7\x96\xd3[\x01\x11\x02\xa9~\xdb\xa6f[\x14\xa5\xc5\xfd\x9b\xd3x\xc6\xc3\x1el\x07\xdb\x9aH\xc9x\xe2:\xdb\xc1\xb6\x03\x13r\xe5jnu\xaa\xa3\xd6(\x80\xef=\xbe\xe9\xa4\xb8\xe2\xf6\xb8\xb0am\x03z\x8et\xd3\xfcn\xdc3\xe0\x11\xc5\x8d\x8c\xb4\xfd\x90\xec=L(\xb27F\xac\xda2Q\x16\xa2\xad\xd6 \xc9M\xa0\x9f\xefx\xc1\xf4\xa1k\x9b\x07\xfc\xcc\xe7\xec\xa9|\xe1\x81\xa1\xfe\xf1\x15\x83.\xd4\x19\xfe\xa1Gtq\xae\x91\xc4!xAs@\xdd\x1d\xd4\x97'\x90d\x1c\x93\xac0f\x95 c\x0b|\x1c\x06\xd3\xd65I\x1f\xac\xb7\x97DH\x8cf\x84*\xfc0\xef\xb6\xd9\x8d\x07\x0fXz\x7fT\xdf\xa1\xcd\xb5\xfd\xddFs\x90\xdf\xc1\x1fc\xc2\x05iI\x9e\xc19\x89VE\x99\xac\x89\x94\xb8\x92\xcf\x94dq\x92\xcdZ\xc5\xc2\x15\x9d\xe7\x05\xfc\x9c\x84\xd1\x9c\x94i\xb8\x86w9-\x17a\x96\xaf\xe1\x87T\xfe|\xf5\xfa\x8f\xb3E\x98\xa4A\x94/\xfe\xd0\xaa#M\"\x92\x95\x04N\x8e/\xb5oz\xd6\xcb9\xe6\x82w\xa2\x84{r|\xe9\xf5\x949\xcc\x97wE2\x9bSp#\x0f\x9e\xee\xec>\xdbz\xba\xb3\xfb\xca\xd8\xe5\x9e\xaa\xde\x93b\x91\x94\x18\x14,)aN\nrs\x07\xb3\"\xcc(\x89}\x98\x16\x84@>\x05\x06_3\xb6L9\x84\xd9\x1d,IQ\xe6\x19\xe474L\xb2$\x9bA\x08Q\xbe\xbc\x83|\xaaW\xcf\xce\x11(\xf3)\xbd\x0d\x0b\x02a\x16CX\x96y\x94\x84\x94\xc4\x95\x1e/Zf\xc04II .\x9d\x13p.D \xc7\xc36c\x12\xa6\x90d\xed\xca \xc8\x9cp\x9b\xd0y\xbeb(\x9d\x83M\x92g\xbe\xf0s\xcdz(?\xa7\xc9\"\x11\x0d\xb2\xe28\x8b%\xd0\\\xaf{U\x12\x1f\x07\xe5\xc3\"\x8f\x93)\xfbOp\x0e\x96\xab\x9b4)\xe7>\xc4 k\xe9fE\x89\x0f%K\xc4\x05\xf4\xd9(\xb7\xf3\x02J\x92\xa6\xac\x86\x84\x94\xc6\x89\xa9\xfb\x8eE\xf0\n\x80-\x06\x15\xd3\xcbz\x05\xb7\xf3|\xd1\x1cgR\xc2tUdI9'X&\xce\xa1\xcc}\xbd\xfarU\xdd+\xb0\xd2\xd3>\x1a\x1f\x81sp\x01\xc7\x17\x8e\x0f\xbf\x1c_\xfet\xf6\xe1\x12~98??8\xbd\xfc\x15\xce\xde\xc2\xc1\xe9\xaf\xf0\xe7\xe3\xd3#\x1f\xc6\x7fy\x7f>\xbe\xb8\x80\xb3s\xbd\xe6\xe3\x93\xf7\xef\x8e\xc7G>\x1c\x9f\x1e\xbe\xfbpt|\xfa'\xf8\xf1\xc3%\x9c\x9e]\xc2\xbb\xe3\x93\xe3\xcb\xf1\x11\\\x9ea\xfb\xa2\xe6\xe3\xf1\x05\xab\xfbd|~\xf8\xd3\xc1\xe9\xe5\xc1\x8f\xc7\xef\x8e/\x7f\xf5\xe1\xed\xf1\xe5\xe9\xf8\xe2B\xaf\xff\xed\xd99\x1c\xc0\xfb\x83\xf3\xcb\xe3\xc3\x0f\xef\x0e\xce\xe1\xfd\x87\xf3\xf7g\x17c88=\x82\xd3\xb3\xd3\xe3\xd3\xb7\xe7\xc7\xa7\x7f\x1a\x9f\x8cO/\x038>\x85\xd33\x18\xff<>\xbd\x84\x8b\x9f\x0e\xde\xbd\xc3\x96\x0f>\\\xfetvn\xea\xfd\xe1\xd9\xfb_\xcf\x8f\xff\xf4\xd3%\xfct\xf6\xeeh|~\x01?\x8e\xe1\xdd\xf1\xc1\x8f\xef\xc6\xbc\xe5\xd3_\xe1\xf0\xdd\xc1\xf1\x89\x0fG\x07'\x07\x7fb}?\x87\xb3\xcb\x9f\xc6\xe7\x98M\xf4\xfd\x97\x9f\xc6,\xa957\xa7pp\n\x07\x87\x97\xc7g\xa7l\xcc\x87g\xa7\x97\xe7\x07\x87\x97>\\\x9e\x9d_V5\xfdr|1\xf6\xe1\xe0\xfc\xf8\x82\xcd\xde\xdb\xf3\xb3\x13\x1f\xd8R\x9c\xbdeY\x8eO\xdb\x9d>=\x1d\xf3J\xd9\xaa5\x17\xf7\xec\x1c\xdf?\\\x8c\xeb\x9e\x1e\x8d\x0f\xde\x1d\x9f\xfe\xe9\x82uH\xcd\xacC\xcdv\xe3]\x9e%`!\xf7\xa5\xf4\x02\x92\x8c\xc1g\xc4\xe3\xfc\x8a\xf3\xb5J9\x12\x97$\x8d\xc4s2\x1b\x7fn:\xf1S\xe2oAS\xc7\xdd\xd88\xea\x874Z\xb6q\x10R&AE\x04\xaa}\xf9\xab\x0e\xca\x00#dI\xa8\x12\xa6\xc1XU\xa5x\xc26<\x1a\xd0\x19\xbc\x92\xf7w\x95M\x89\xa7\xb2U,\xc1E%\xa4\xcbdA\x1a\xd2.k%|\n\x1b\xd5\xf0$\xa3ZVK\x17\xebCF>/I\xc4N\x992\xa1+\xe1\x83e\xd0\x8a\xe4VI\x97\x14\xd3\\_#o|}\xedT\xf7PUh\x99\x96\xb0\xab9ak\xe1\x94\xcbH%\xda\x00\xc1\x10\xe0h\x17\xad\xccd\xd4\xfa:\xd0G\x1d g\xe7\xaa\xd3\x96\xc6R\xefS\xaf%\xab\x9c\xec\x18\xae\x14\xe5M,7\x9e\xec\xce+*\xe4jz\xb5N\x1aZ$\xf3\xeb\xf3\xaa\xbc\x0f\xbb\x06\x9d=k\x14M\xc3\x04\xa0\xf9]%\xe0\xc4\xb7\xa6~\xe0\nidA\xb2~\"w\xa5\xbb24iu\xa1\x0f\nc\x84\x12\x9f\x90\xfb\xa2G\xe1I\xee\xa2gz\x1e\x19$T\xc1\xc2\xd0S\xd2\xe8\xa9\x8c\x9c\xeb\x86\x93\xb2\xba\xf54h6\xaay*\x90%f\xeb\x06\xf5Y\x0b\xa5\xea\xc9\xd0x\x8cm\x03\ntN\xd5\xdd\n\xa8\x8b\xa2\x85G\xaf\xee\x83\xd9~i\x8e\x0c\xa35\xe5\xe2\xba\x97\x8bw\xb3F\xa2\x90\xf9\x8a\xb7\x04-\xd6\xd5\x94\xb6\xf7-\xf5\xf9\xea\xf9\x90[s|E\xdd\x96\x11?\x06\x9a\x13\\\x88O\x86\xd5\xa3\x8d\xd5\xa3m8\xa3ze\xbc\xd7\xbc\xc2f:\x0f,l\xec\xa0!d%\x1bMhA1\xcd\x80\x94\xcf=\x11Oq\x10\xbf|\x1f\xa5K\x9b\x00\xbb\xbd\xf4D\x89\x92\xc4\xd6\xd6b\x94\x88\xcc\xba\x01u\xb4\xd4{qZ'W(\x11n\xe7\xcf\xb8>\xba\x1et\x9a=\xea\x8e\xa7\x86\x1do\x0d7,Q6\x9d\xe4\x96\xbdc\x0c\xb9\x94\x08\xffqO\x9e\x98\xa6\x85\xf1\xf7[\xbb\\\xc6W[\x08M\xf2+6\xbcb\x92_a<\xf7\xc3\xa4\x88ViX\\90\x92\xa9\x04\xb3\xf9\x90 \x97\x0e;\x08P\xe2\xa3!\x00\xaa)\n\xac!\xf6#\xe56ih\x9f(\xcc\xd3D\xda\xd0\xf2\x0bR\x96\xe1LV!\xdf\xf6\xea/C+*i\x18}\x12\xd5\xf0\xdf{2\xd5P\x85\x14\xc57w\x04\x03\xf0 \x06\x922\xde\x06\xe1m\xca\xe4\xad\xf8\xc2-?\x84\x1f_\xe0~\xd5\xf2\xecn\x91\xafJ\xc7\x83Mpp\xfe\x1f\xacP\xf8\xfd+\xf35\xe3\x0bc\xc8#\x96n\xf2|\xcc\xd2\xf5k\x80\x95H\x7f\xed\x99\xcc'K\xbb\xd8\xc9\xa4\x10\x8d\xda8J\x84\xbb\x1d\xae\xf0j\xd0\x9d\xe2zS\xdc\x19? \x0b\xd7{\x03\x9b\x9b\x14~\x80\xcc\xa8S,g\xa2\x1do \xa4\xec\xbc$\xd4-0\xfeW1\xd9\xbd\xb2\xe9\xed\xd6\xbf\x14\xa5'\xde\x07\x86\xac\xfdF\xb2P\x8f\xc2`\x1ceS\x15\x9em\x94f\xe2{\xe9\xf9\xe0\x9c\x84K\x9b\x10x\x90V\xbc\"Un\x85\xd0\x13\x10e\xf1\xea\xf8\xc2\"\xd2|\xd1\x12\x81\n\x88\xda\xd5E\xf4\xa5H\x7fi\x84\xb4\xd4\x0ei\xc2< \x0ei\xc8\xad\x140\x1a\x99\xd1\xca\xaaL\xfe\xce\xf1\x05\xfbaX\xf4\xd4\xb0\xe8\xb9\xdfH\xae\x16=i\xa6\xf3E\x0f\x9b\x89|\xd1W\xcdD\xbe\xe8es\xd1S\xe3\xf2\xa8C\x1e\xacN\xdb\xf0\x9b\xb2\xb5\xcb\x1d\xa7\xd0\xca\x9c\x98\xeb\xdcK\x1f$\x9b\x9b\x19\xfc\x00\xc5\x1b\x0f\xc8$\x87M\xc0\xf81\xed\xb05\x92o\xd3\xe6l08\xbdx\xaa#\x1c\xa1\xf2\xfcZ\x07\x1bcL6\xa3\xaaS\x0b\xda\xba\x84\xc4m\x18\x0c\xd5\xe0\x8a]\xec\xb9\x8a\xb1\x90,@B\\Q\x1e(\xdc\x90\x1b\xb6[E\xc7Z\x8dj\x10\xb8V\xbe\xaf\xba\x03\x1dF\x83\x9a\xf7\xf4\xea\xbe\x8b`>%\x9e\xebkcZ\x83\xf6t'\x9a\x97\x8c\xf6\x14'\x03\x16\x0eq\xd37\xaa\xb6\x08u\xc7A\xab\x99\xb3\xaf<\xe8L\x15E\x15\xd56\xb8\x87\x92\x8dU;\xbd\xd9\x9ey)\x06!\xed\x0e\x1b\xb1z\x95\x9e\xe9\xab\x015\xf2m!e\x90\xbaB\x16\x8e\x08\xffl\xd0 \xcbcry\xb7D\xd2\xc9d\xfe\x88\xf7Af:\x92;\xa4\xc7zH\xa3\x1e\x83\xe9%\xdfW8\xbb\xd5\xd4\xec\xf1\xab&\x19t^\xb0&&\xbf\xe0l\x1e\xdd\x15\xec\xc3*HJ-7\xb2\xd4\x9a\xde{{\xfeAgPv\x9f=\xf7\xaa\xcb\xd5!z7\xafwv^\xee\xbe~\xfd\xf4\xfb\xe7/\x9f\xef\xbc~\xbd\xfbP6\xc5\xe4\xbf\x1d\xe7\xf1\x0f\x8c(\xc7_\xff\x81\xbe\xf1\xb93\x02\x02?\xec)\xa2\xb0\xfek\xb1{\xf5\xa6\x1b1I\xdc\xde\xba\xd4\xed\xe9\xceC\x80\xfb\xe9K\x9d\xc0\x04\x01\xdd\xdf\x08\xc1l\x13\xe4\x8f\x00\xc1\xd5NH\x1a\x10\x8cU\xa3\xb9cDJ\x83\xc5\x9env\xd0\xca\x00\x9d\xf7\xe0 \xe5]u\xeb\x05\xf9\xdb*)H\xe3\xc5uV4I\x1d/`\x03\xb3xb\x01U\xae\xfc\xe5\x8b\xdc\x8e7 \xdeD6^du\xc6zz\x02[}u=\xfbf\\=`3v(W\x99\xaf\xd6[FT\x0c\x04\xb6?\x06_>N\xdc\xfd\xd1\xe4\xffL>^]}\xf7\xc5\x9d8\xbf\xbf\xf2\xdc\xfd\x91\xbb\xbf\xf1q\xd7\x9b\xfc\x9f\x8f\x1f\xaf\xbe|\xfc\x18x\xdf\xed\x7f\xdc\xf5>\xea\x81Yx\x00\x98\x8f\xb7\xdf\xfd{oH\x07\x8b!S\xc3\x8eI\x17\x8bV\x92t\x01\x98F\"k\xc3\xad\xb0\xc7\xc6\x1ed\x08\xd4%R1JB\x158B\xa64\xdc\x0em\xa0F .?\x8f\x05\xc2\xa3\xc8n$\xea\x9b,A\xf9\xf6H\xa4\xd3<\xf7^\x86\x0e\xf7BD\xf7\xa4\x1f\xcd\xf2\"A\x99pm\xd3\xcaE\x17\xf5\xc1\xb9\xbe&\xe5I\x1e\xafR\xe2\xe8\x1a B\x1bAU\x08AC\x9b\x05Y\xe4\xc9\xdfI|\x11.\x96)y[\xe4\x8b\x8bhN\x16\xa1\x90*\xf0\x8f\x87\xa8,\xf8\x97\x93w\xe3\xcf\x98\x8d\xb3\x10\xf8\xf3/\x8bT+\x94dSR(\xefe\xbbfq\x00\x824\x81i\xd4\xac(z(\xec\x98\x89\x1b\x0b\xdd\xcc}\xf1\xfd\x0b\xcf\xb0\x0f\xf0\xd3\x8b\xd7\x9e\x91\x97\n\xed\xeb\x83\xa0\x10\xd4\xf3(T\xf5\xdaXKFF\xd0\xddZ\xfd\xae\xfdk-|\x19\xb6+\xe1\xa2\x99\xe1qm\xa5,\xa7\x95\xc7\x10F\x8bg\xbd&\x8b0I\xef\xd1\xc2\xaa$\xc5\x1f _\x8c \xca\x17\x83\xda\x12\xfdb,(\xd9\xa2\xc9\x828\xc3[t\xe5\xf5\x95\x17\xd0\xfc\xf8\xe2L\xa8\x84\x19\xf8\x02\x83<\x05\xd1\xc4\xf0\xb6\x06\xc5u\xe3\x95^O\xd3<\xa4\x8f\\u\x92Q2{\xf4\x0e\x0bT\xd8G\xff\x83\xb2\xca*\xf6\x94\xb88\x10 \x8dW\xad\xf2\xa5\xdd~\x13\xdc\xdb\xbcLw'\xa4\xcc\x82mt\x17\x9d\x0frr%\x99\xdeyF\xff3 \xc4f4h3a\xf2AO6\xc14/\x16\xa1\x812\x02\x81\x12V\x13\xd4O\xbcv`\x13\xb8\xa9\xcc\xca\x18\xd5S\xc2%\xf6.)\xdf\xae\xb2\xc8s\x13\xc6c%\\O\xda\xf9\x90}\xca\xf2\xdb\x0c\xb5 \x85K\x1b\xec]\xd7\xd4\xa46\\Xa%\xcb\x0d\x93<2[7\x89\x7f\x00\xa4\xa3\x15U\xd6\xfa\x8ep\xf7\n\xf6\x9b\xaf\xa3\x96)\xa8|r\xd3RP\xcbR \x99\xd9\xb1\x14\xca\x97\"P\xe1\x8035V\xb3Vg\xaa9\xef\x1c[\x16\x00m\xce\xb26\x844\x93\xcf\xa2\xe3\xdb\x0c\xc9\xb0\xcf\x0bC\xc0f\xf60\x1c6\xc3;j\xf3\xf7\x1b\xfc\xbe,\xc841x\xb4b\xcfuU\x03F\xab5g\xba\xe5S\x9b\xad\x16\xe6\xef\xe3\x8aG\xb6\x1c\xe0a\xc7\x01\xceN\x90\xd4C\xa8\xfa\x97\x9c\xe2a\xdf)\xee\xb2Y\xbd\xc3K\xff,\xa7\xe1\x8cM\x8e\xc3\xcd\xa5\xdc\x1b\xd8\x87\x1bF\x96\x8f\xd0>\x16u\x01\xee|\xb8\xe6\xde\xd2\x17\x13\xf6\xdd\xf9\xbcH\xb3r\xc4\xce\x8e\x1b\x96 _\xd1_\xc1\xb5\x85\xc0Q\x0f\x05\xc48\x91\x0d\xf9\xb2\xdc\x11\x83\x07\xd8\x03\xfe\xff\xcb\x17\x98qK\x10\x9f\xa7HU\x0d\xe5\x85\xe5\xe1P\x023\x11\xa9>\xae\x88\xbf\xf5$\x93nn\x9b'\x04\x9e\x0d\xd3\x81ns\xe5\x13\xc9\x1d\xc8\xfd\xb6\xb2\xca\x85\xdf^v\"\xe4V\x9d\xa6\xd6\xf94g\xad\xcf\xef\xdd\xba|\xb6\xac\x8b\xfb\x8d\x0bs\xaf\xf6E\xaeV\xa6\x01\xe4\xb6U;\x91M\xfd\x85\x99\xdc\xee!\xa7\x0f\x199\xad\xec\x19\xb4$\x95\x1b\xf0\xc2N\x9d\xb2\xbe]\xe8q\n\x0e9\xde\xd8\xb8\x98\x1c*\x84\xf7\x97/\xb0T?\xd4$7#\xc6-\xd3\xd5h\x87\x95\xe2H\xa2\xfa){(\xde\x03\x06\xb3h\xa9\xd2\xb5l\xf2a\x03\xff\xd4R\xbc\xc3\xba\x90Jc\x9d\xad\xde&;Wv\x96E}\x0ed\xff:\x0fm\xfd9\x93\xa5\x04D\xd91\xbd|\x16\x93j\xd4\x12\x1d\x1e^UG\x16\x92M\x07l\x04\x07\xd04\xb5\x9dN\x0e\x91\xef\xc1\xff\xcdOg,\xfd\x8c%~b\x7fJ\x9c\x8b\xee\x85\xf9\xdaw\x80\xc9\xa7\xd9\xd9=hw\xbe\xe1\xf3H\x9dA\x8d\x18\x94\x03p\x1byx\xba\x05\xce\xd5\x87\xad\xfa{d\x99.\x86\x15h\x82\xc7{Tw\xe5;\x05\xd1\xa8pa\xf0^\xa2[\x8e\x04\xde\xf7L[\x17j\x94\xcc\xa4h\xa8\x0fQ7\xa9\xcd\x118\x07\xd9\x1d\x9d\xa3\x0dT\x98\xc1\x0dAc7\x0bU\x80\xe1Q\x86\x9e\x08zC\xa5\x8doeH\xee\x11\xcf\x99\x018R\xcc\xdc\xb8 \xffSv\xd4W,\x15&\xcd\xd9\xf9\xdbB\xff\xb7lQo9WV\xa2]\xb8Xa\xc6\xe1M\xcc}\xb7\xf6\xfb\xab\x0fcV\xd1X\xef\xfaW\xe3=\xc8\xd4x\x89'\x05\x8e\x11\xff\xda\x84R\x86\x0d\xb3\x86\x9c+\x97x\xc3s3\x93\x19lL\xa24\x94\x81{M~\x0b\x92,\xc6\xc0*\xceG\xaa\x85c\xd3\xaf\xe1\x00\xcda;.\xa5X\x7f\x92\xba?\xd3\xbe\x1b.-\x7f\xda\xaf&Q\xcd][t\xcf\xd5\xf0\xc8\x9aq\x87\x95V\x9ex\x15\x87\x05O[\x84\x9f\xabxrU\xc6Fb\x85\x1b\x95 hw\xc1`\xd7$\x85\"2OCl\xd8YY~?\x8ds\xd5\xd8\xa0\xbb\xe2\xc4Z\xb1\xeaz\xc5\xb0\xd2\x0dGY>d\x01\x06W\x19/\x12\xca\xdd\xdcc\x9a\x12\xac\xa3\x9ayy\xbb\xd8\xf8\xaaMz\x9dG\xac\xfeI\xf3\xfb\xaeV\xbe$z\x0e\xbb\xd4\x03\xa9&\xe5\x06\x9b*\xc6(D\x06\xa8\x10\xbe\xebL\x1e\x152X\xacJ\xca\xd0g\x08<\x1e\xf2\x9a\x88[)\x8b\x1b\x05#\\\x11\x0eo\xf5\xcc6GD\x16 \xed\xb7\x9f\xe7\xfe\x8f|X\xf9P\xfa`\xf0\xc4\xac\x83\xb9\xabm\x03\x0c!'\"\xe5\n+\x1c$\xc4\xd4l\x01~F\x05'\xb7\x9d\xce\xd5\xd2\xda\xe9\xd2\xd0\xceDo\xb1\x9e\xa1\x8b#U^\xe3\xa9\xc6oc^5\x9f|\x03\xcd\xc3F\x1f eZ\xbe.\xbf\xff\x90E\xe1j6\xa7>\xac\xb2rI\xa2d\x9a\x90\xb8\x1a\x1bv-\x00\xf7\xf7\xb0\x89\x0e\xa2\x1d\xcf\xe4.\x84\xb7\x17\x05\"j5\xa7\xde\xa3&\xdak\xcdq\x82^\xa2\xd4\x19\x98\x90+\xbb\x92\x05\xd7\xc2\xc8<\x0f\xca\xdb\x04UXt9\x97i\xca\xa2\xb0$\xb0k\x8e\xf4/\\\xb0\xa2[t3\xd5\x82>\xa4\xdb\x9f\xb0\xd2\xa7\xbd\x95\xfa\xcdu\xba\x7f\x13\xcf\xee\xd9\x84\xfa\xf6\xf4\x9e\x0d\xca\x9b\x7fc\x99UE\xd4\xf7[\xe1\xb1\xfd\x18.\x97\xe9\x9d\xe8\xe0J\xd7{\xad\x84\xf4\xb9k\n\\\x83,\xd4\xfd\x1a\xc4C/\xc5\xeb-n\xda\xe2y\x95^t\xc9C4r\xc7\xe5Pnnz\x90N\xca+\xad\x8bF\xfc\xa3j\x954\xb1L\x18\xc7J\xcc\xd0N\xe5!\xb6\xe3\xc26$oX\xfc\xce\xa4\xb2\xda\x1aYV\xa7^\x17\x96\xecAU\x0d<\x93\x91[5\x02)~cx\xd3u\x94/\x0e\xfa\xff(\\\x1a\xc8.y(\x90\xaf:8\x02\xaaU\x94\x04\x08/\xa5\x9f\xf6\xae\x074\x87$\x8b\n\xc2\x90\x0d\xfa\xb7\x08\x9c\xd6\x92J\xe4\xea\x9b\xe9/\xd9\x7fZ\x84\x11\x1e\x82\x8d\x04\x0cL\xd7u^\xe7h\xe6\x00\x1b`\x15\xb9&<\xfa\x8du5\xd9\xc3\x03\x88d\x12\x83\xee\x83[\xfd\xdec\x8c\x8dyU\xd0\x08[F\xd8J8M\xf0\xad\xeb\xd4\xbf\x13\xfb\xb7\xdaA\x9a\x0e\xe3\xad\xd6F\x07\x81\xad\xed\xd1\xb3\x156:\xc6\\\x15\xe5\x9ci\xeb\x8ax_g\xf4\xd1\x87\x98~\xe6>y\xd2\xb9/\xda]2\xb7f\x05t\x8a\x0e\xc8\x1a#\xd6\x97G8\x02\x90K\xd8\x9eh\xa3\x0d\xb7J+\x19\x8a\xe8\x8dh\xf0#cC\xaa\x0b\x0eF\x9e\xa6\xb0\xf04\x96\x93!\xb3\xa1\x03\x83\xc6\x04N\xd0\x9bjo\xbc\xb1W:\xa9\xf6\xcc\x16\xb4\xf8\x0e1\x13]\xcbh\x03\xeat\x10,\x9b\xc8\xd26\x8d\xc4\xdd\xf1\xea\xdbx\xbfE\xfc\x19(?I\xe3\xc3H\x8b\x16e\xea\xeba\xbe\xca\xba\x05\x02:\xbboS\xae\xa0\xed\x85m\xc3YRy\x94\x14\xd3`q\xa0R\x87+\x96\x16\x9c\xfd\xf8F\xe3F\xec#4\x1c\xe6\x95\xbaJ\xa3T\xbfI\x80n\x0cD5\x0f4\x99\xfbl\xe7{\xcf\x0b.hA\xc2\x85\xa0H\x82s\x12\xc6\"\x02\x1b\xbe\xffR$T\xbcg\xee\xee\xeb\xefQ\x80y\xb4Z\xa6\xe437\x80\xe3)\x97E\x98\x95\xd3\xbcX\xf0\x8aww0\xf5}X\x96\x97\xf3\"_\xcd\xe6<\xf3\x8b\xe7\x83LMz\x1d\x01\xf28_&T,\xdc9>\xdf\xf1l\xf4\x9fA\xd7\x1e481II\x12\xc6|\xa1|\x84\x07\xaa\xe0\xa7PF\x8b\xbbf\xd24\xc9\x92f\xc0E\xdb9\xbd\xd19\x07\xfa#-\x0f\x08o\xd4~\xb6\x93F\xaf\xec\xf9\x04R*\x8c\xe6\xfb\xea\xb3\x16^d\nd\xe0o\xc2\xc8 \x82P\x1f\x1a,\xb9\x93\xc5\xe8fk\x8b\xf1y\x18v\x1d+`3h-k\xbe\x07\x02\xac1\xca\x8bO$>'\x7f[\x91\x92\x96o\x0b\xf4\xe9mJ\x96\x8bDP/\xcdPlO\xd3\xdb\x92\xcfW\xee\x91\xa5\xf5\xedk\xc7\xeeV\xb7\xd3]\x9b\x0fYq\x11\xc6\x06\x0dn\x8a\xfc\xb6\xe4\xd4\xcb\xc4Y\xef\x04\xbb;\x8e\x0f\xec\xc7\xeb\xc0\xb9\xaa]\x81\x04kR\x94I^y\xf9\xf0\xe1{\x8fk\xd2\n{\xda\x04\x87w\x99\xe8KpW\xed\xd3\x0b\x1a\xa2-\xfc\xac\xdd\x9dT\xd8\xad\xbc\xd0\x8e\x954H\xb29)\x12\x81\x15^\xed\x1aX\xaa\xc8h-\x02(|\x12z\xa6#\xdc\xe0\xcf\x06\x99IL\x05\xfe\xd1=\x0e\x80\xd4uvw\x9f\xefJG6\xed,\\u\xebC\x92\xd1W(i\x025`\x8d\xd7R1e\x03\x98\xfb\xa8\xa1\xc5\x1a}iE\x0d\x0b,l\xf983bg\x10\"6\xee\x82\x8a\xa3C\x0420\x84Q\x05e\x1fSU\xf6k \xd5\x11\x99\xf0\x8b\x8e\x93\xd9\x15\xfc\xeaz\x7f\xea/\x10\x19z\xb7\x0f\xbb/`\x04\xbb/\x9e\xbdzn\x99\x85FW\xd0\xaa\xf4\xcb\x17A\x0c\xe7\xb0\x0f9\x8c\xc4\\\xa4\xf5\x87\x94Q$)\x8c \xf2\xcd\x95\xd4\xb1~\xdc\xf6w\xafF\xe6az\x18\xa62,\xa7/\x0f\x02\x12\x1f\x15a\x92\xa9\x89\x1c\xe7i)\xcdr\xfclh\xa6\xc5\xa4\xa4E~'\x12\xcd+\x82\xf1\xf99\x7fE\x82\x98Dy,\xa2\xc9\xd8N\xaaF\x1eVxZ\xb5\x86B\xb2q\x16\xe5\xa2\xb7\xa4\x95\xf6\xe5\x0b8+:}%\xe5I*\x13\x87 l\xc5\xb5\xa1rD\xab\xe4)\xef\xb2HJL\xd8\xfb\x0dn\xe5\xf7\xdcZW+\x9cg\xa8\xff\xd2\xab\xb8\x0b\xedC\xb3\xef\xc4\xe4A\xdc\xaeoU\xec\xd8\xad\x84RpY\xf4]\x16u\xe7\xe3\x81\xe0\xb0\xe3\xd1\x8d\xfd@d\x14c\xff\xa8\xe4C\xb4\xb9%\xb2\x81\x8a\xc6 \x15\x7f \xf7\x1eII\xe6+\xbf\xd9\"X\x1b\xf9\x8a\x871\xf5\x0c\xc4\x87\x99\xa6\xd2\x9f\xad-\xe5x\xf71r\x80[\x9fJn\xeeC\xe1\xf9\xca9\xe5^\x08\xa6\xdco\xad\x03\x97\x9br\xb9\xa8\x14\xa9\x12\xc1\xd8\xf3+,V\x19\xe3\x15\xdc\xdc-\x1e\\\x81\x0f\x17\x1cT\xecZ(\xe89\x8aO\x00es\xd0A\\\xf5+\xf8\xe0\xad\x01\xec\xc1\xd8\xd5YD\xfd \xf1\xcc\x90{\x07\x7f\xb7\xb6 C\xde2\xb9\xa2dX\xea-gB}\x8cfZ\xba\xd78\xcd\xfcj4gsv\xed*\xef\xf6\x91\x1b\xbfXi!\x05\x01\xa8@Y'\n\xf8kl\xfa\xba\xdb\x8d\xfciX\xd2\x1f\xbb2T`\xa6\xd4\x88\x8a\xcem$\xaa\x03\xc2\xae\xb9\x03\x92\xdf\xdai`-\x8d<\xcc\xc8-\x84\xfcf\xb11\x016\xba\xe0\xce\xbc\xad\xb9\xe6s\x930\xd8p\xe7\xfc\x12\xec\x8ew\x00\x8d\xbe\xd9\x8f\x06-\xe05\x1c\xa0\xdeY|\x9f2n\xf6V#\xfaX~N\xa6(\xe1\xa2ok\x0e\x0e7\x08\x9e\x94f}\x0c\xbe\x86\xca\xc5\x87\xc4\xcb\xe2\x8b\xed\"A|^\xeb%\xd7u\xd1\xb5\xbd\xac8\x01\x95\xc22e\xaf\xfej/\x8eg\xb4R\x98\xbf\xef\xc9/\x9e\xe7\xc3T\xb9-\x1e\xb4\xa67M\xa4\xc8E\xe9\xc6k\x03\x15\xec\x19\xfaP\xf6F(_\x05>\xc7\xcb\x03\xe5\\\xc4\xa8+r\xa6\x18\xe6\xa4\xf2$\xe4a\x87\xf9\x17\x97\xb7^\x7fSk\xd9\x1d4\x9ake4\xa6Ad\xd0\x17\xf0Q>\"\x06\xa3<\x83\x9e<\x01\xaa\x10C\xb8\x06-\xe2Hb\xe4\x98\xa59\x06,\xfc\xd5\x15\x07\x84\xc68\x16n\x8d\xbb\x07\x8d\xf3\xd6\xdawj\xa4?\x0c\xb6\x0c\xeb\xca\xb1\xb2\x86:\xcc\xb2\xa0j\xf9PD\xcfo#\xd8\xc9g\x9b\xbf\x8a\xf87b&;\xc1\x91\x8b\xcd\xcd5\xf4\x8a\x0e\x83AtZi@l\xe6\x93(\xa9e\x05\xe6\x0c\x95R\xf4\x8a\xa3\xcd\x92\xcf\x1b:\xfd\xcb\xf1\xc6\x82k=\xa1w \xbc'\xc3\x1c\xbb2\xd0'\xce\x86\x0f+\xd8\xdc3\xc9\xd3\xd8\x93\x07a\x9a\xf2\x83\xa0\xe4^\xd8\xe4\xee\xe3;\xa6\xf2\x92\xe6\x83\xe30\xd2\x82\x1f\x00Mx\xd9\xdc\xc4\xac\x1dG\n'I\x18\xb9b\x11\x0b$\xa2\xaf\x89*\xe7\xf1\xecb\x04qN`?l\xe7L\x1b\xd6\xbb(\x08)&\xee\x94\xc8T\x9c|\x10\xcdW\x99\x85\xd1\x92\x0f\xea\x0b\x05DP\xf6\xddy\xb99r\xbf\x88\x87\xc1}\xb5B\xbb\x88\x99\x1a\xdc\x1c\x8c \xad\x16-\xf5\x19\x036\xd5\xc0\xc1\x0b\xae\n\xb9\xa3\x81S\xdau\xf4\xca\x83\xbd\xa6\xb9\xf9\x1e\xb2\xd4ZW\xa9\x87\x0bhn\xa4Z\xb4\xc8H^\x86\x06fM\x07\x9d\xc2\xa7\\\x8f\xb4\xbc:\x85*\xf1\x96\xb6\x07xx\xf0\xc9\xd5\x1b o<6\x0c\xb4=\x92\xa28\x9c6\xebJk\xe1\xe9\x0c\xc2\xca>A~\xb7\x171\xb3s$e\x1e|p\xf8pZ.\x92\xf4gF\xe8\x08\x0d\xad\x84\xc8\xb5\xdbI\xa3\xfe\xa8\xb7{\xd5\xd4\x1b\xdc\xda\xa8\xcfW\x1f\x1c\x8d\xe9\xe6}\x85\xa4\xacE\xbfBYI\xcbX//\xe3nH\x18\x07\x8e\x0f\xce\xd1\xf8\xfd\xce\xce\xce3\x8b\x8f3ho\xf0*\xb9\xd7\xfd\x99\x85E\x10\xb1\xb4\x9e<\x11\xbf\x82yX\x1e\x0b~\x0bl\xa1C\xa5\x9b\xe8z\x99&\xed\xd2Wh(\x07{\x03s\xfb\x16X\xb8\xf3\x0d=\xeb\x08\xe0\xd5/O\x92Z\x90\x1bsU\xdf\x94\xd4\xfc&\xdb\xed\x9c\xe3\x92\x0e\xa6\x9a\xbc\xa4\xc2\x8f\xce\xfaN\xcb\xaf\x88\x85\xe6\xbd\xe2;y\xce5\"\x9c\xb4\xee\xe5}P\x15G\x97\xc9\x92\xf4a\x07.\x01h\x1e4uP\x90\xc30\xcbr\n\xac\"\x1f\xd8\xafB\xdcp\xea\xac\x88\xd6r[$i\xbf\xa3C\xb2\x9e\x1b\xf0\x1b\x18s\xbb\x8d\xfd\x86\xc1#7\x88\x0b\x85\x8d\\\xa5\xab\xd01:W\xa1_V\xae8\xdd\x02\x17\xb4P'4\xb6\x1fi+$\x0d\x94\xe2\xdc\xed\xaa;L\xf0**Y\x06\xd3\"_\xe8\xf1\xe3\x00DH\x05\xcb\x16D\"\x85\xebWpT\x8dT\x18\xe3\x0b\xf6\xf1U\"@FmsEX\xbc\xe1\xd1$\xd3\xcd\xdak;\x86\xac\xaa}\xe1\xf9\x90\x0b\xb9\xfb\xfe\xb0\xb3[R\x03\n\xc8\xf0\xa5\x0f\xa7\x94\x14@\xb2\xd8\x16d\xd3D\xdd(G\xb4\xc5y\x86\xd8\x8b\x19\x9e\xdc\xab\x16\xe7m\xe7\xd2A\xb9\x9e1Y-\xc9'\xb4\\$\x80B\xdc\xd4\xa4\xf2>\xf7\nN\x1az\x80'\xe1\x1dn\x15>\x11\x98\x1bQ\x0fF'+Q_\xc0\xf1\x8c\xd1\xa3\xb9,A\xb1\xa3\xc989\xd4\xbc\x8er\x0dm\x1eg\xeb0Mb\xc8\xf2l\x8bW\xbb-N\x1a\xe4s\x1c\x0f\x95\xc5\xb9/\x8e\xe6\xbc\x87\xcdy/xJ.\xf9\xd0v\x10\x10\xb9\x069\x97\x99\xf2\x00\xd2n\xde$\xc0B\xc3\xde\xaf\xa4A\xb6\xf5AU\xae\xdek|S\xd5}\x078\xd1o\xf4\x8c\xd7Axw#\x17E\x8b[\x82{Jl_\xda\xe1\xc2G>F\xf2H}\xbeVz\x18\xf6\x8a\n\xee\xb2\xa4\xda\xa0\x8c\x88\xcc\x95\x0d\xcf\x15\x03,\xce#\xcc|\x9e\x94F\x18\xf8\xce\xc2\x18\xb9@>\x95\xd8j\xd3\xaa\x1b\xc9\xeaF\x0b\xb8:8\x12m\xde\x0c\x9a\xcb \xed\xfd\xa6\xeck\xa7\xc3GR-\x18\xc4\xed\xc1\x05\x0c}p\xc3=\xb6\x19\xd8Z\xfb\xfc\xdb\xb8\xe0n`\xc3\x1d7\x02\xc3\xcd\xbb\xfaH\xb1\xc2\x08\xf4P\x84\xda\x83\x07\xce\x08\xb2\x1eY\x85\x90<\x8c \xe9\xce\xc8v:\x8fgo\x07M\x1f-\x86S)\xca1O\xc3\xc8\xc8\xe4\x1b\xf3Z\x85<\x9b{\xd0vs\x06\xb5\xa4G\x95\x94\xacj\xfc\xd1\x89\x9e\xcb.\x8c\xb5\xf2A\xa2\x8cvL\xa0& \xc3\xa0j\x10\xf1\xa4\x11\xee\x1c\x1a77\xbb\xea^eCjo\xf0l\xcdV\xda3 \x1b\x16H\x9e\xbflm\xf9\xca\xad(:\x82\xac\xef\xcb\x14\xa9\x07\xbe\x19o\xcf\xda\x02\x13\xbc=\x93$q'\x11X\x12z\xd4\xba1\xef\xa6\x95\xd0\xd6\xd2\xe2\"O\xb8\x99\xa2\xf9\xbb\xfc\x96\x14\x87a\xc9\x8d,6\xdc\x893'\x9f\x19w$\xee\xdd\xd9\xff-\xfc\x11\x96Q\x92\xb0\x1f7I\x16\x16w\xf8+,\xc9\x8b\xe7\x98+*\x9f\x8a\xff[OE\xb1\xdd\x17\xe8k\x17k\x90\xbf\x8b\xf0VQ3r l\x82\xe3xZ?P\xcf\xa8\xb2\n\xd0Ng\xe9`\xb2\xde\xf3\xe8d\xb2G]W\x83+\x83\xf2\x81I3\xd7\xca&5X\xe6[\x93\xda\x89\x91\x83&U\x9c\x83\x91\x91\xe2F\xae\xba\x97\x93\xee\x18W\xe3\x80h\xef\xdd\xe6\xe8\xbc&\x84]\xdf\x87\xcf\xc8\\\x85J\x15\xd7C\x1e\xe3\xc4\x19\xb1\x96,\x96)Y\x90\x8c\x92\xb8\x87\xb5\xa9/\xe7\xb8h\\\xfdF\xb2x`g\xaa\xbb\x8c!{\xdb\x1a\x90 \xa9\x02\xc2\x055\xe2\xeeW\x11\xbd\xdf\x8b\x99\xa8\xcd\xbf\xa1\xe9$\x83{\xa8\xaf\xee\xa8\xa5\xcc\xabP\xf1MQ\xab\xb0\xc8\xcbc\x8e\xe2p\x87\x16R6\xcb\xd8\xad\x06\xd2\x192S\x80\x07q\xad\x1f\xb4S 7\xfdJX]\xd5\xb9\xaf\xd2\xb2\x19\xbf \xcc\xb3\x88TB\xb7\x0e\xd2\x8d\xd6*G;\xbe\xa2\x9a\xd5\x16Q\x83r\xa8\x14-Fe\xe0\x16\xacT\x97\x8c\xdb\xee^\xdbJY-\xd3\xd5v\xa5\x84\xae#\x14\xd1\x81\xf6\xd8\xda\xdb\xbcl\xf4\xc7\xca\xe7Z\x9aw;\xdb\xc7\xd8\x8d\xf7\xdc\xf9\xf5%\xf7Z\xfe\xd6\xb6\xe9*S\xf3ToZ\xae:O/\xbf\xcb%%Y\xecz>\xd0V\x0c\xf8\xdf\xd5=U\x03\n~\xcf\xa0\xd4}\xb6\xf3\xcac\xc7\xe1\xf1bA\xe2$\xa4\x04\x13w\x87\x85\x0ex\x8c(\x83F\x04\xf2\xbbf\xe7\xbf\xb9\x1b\x99\xfb\xe2\xf5\x8e\xe7z\x95\xdbN\xc6-a\x98\xc8\x17\xafw\xbfa\xa8\xeb\xcam\xfc\xcb\x1ds\xf0\x84\x17\xa6\x88?\x99\xfb\xea\xa9!\x86\x97n]-\x0e\xf6f\xc6\x95)jSWx\xa0R*E\x867\x9a\xff\xc5\xb4\xa1.y\xdf\x05\\W^\x1b\"_u\xa5\x0f\xb51\xa2\x12\x9f!\xb4\x98W6\xcb\xe1\x85@\x86\xc1W\xb9A\xb0W\x9b\xbaF\x9a\x93\x05~F\xa0sI\xf4p\x11y\"\xce]\x04\x7f\xd8\x83\x1d\xc6&\xb0\xb4\x914H\x96vN[\x90\xba\xa5\x1by\xde\x1b\xe0a\xee`s\xd3p\x1d\x85z>\xaa\x94\x95rq\xc2T\x1c\x8d\x13z\xe5C\xe1N\xbdz\x8c\x1a\xbf&R\x15w\xc9\xdf\x00\xcd\x0d#\x89\xd6i$\x05\x95Z\x07\x86\x11\xb5&\xd1\x1b1\xd3\x8bHaJ\xc2\xc4nD\n\x8aT\xb8\xf1\xe1+\x97\x12tw\xaa\x06,\x967\xce#\\r\x11\xc0\xe1\x92|\xa6\xa7yL\\\xc7\xe9p\x1cn\xd0\x00QT\xaf\x06\xdc\xaf \x83\xd3\xc1\xe6{\xf2\x80\xe7\x97\xeb\xdc=\x16\xb5\x9d\xdfC\xfc_f\xfd\xfe/\xb11\xe3W\xb3D\x05\xad\xd6\x9a\xe4\x94E\x8e[;Z\"B\xf3\xa3\xca\x8f'8\xd1c\xd0\xc8\x077l\x1e\xc4!\xe5\xe1|\xf6`s3\x81\xff\x80\xa7\\\xdd\x01k\x0b\xcay2\xa5.z\xa1\x10\xe2\x17ix-(\\6\x82 \xad\x96qH\xc9\xbb\xf0\x8e\xcd\xf3\x00*\xd7@\xb2cD\x0f\x83\x80u\x19\xde\xa5y\x18w\x84\xfb\xa9;\xf06I)\xe9>\xe5{:`\x10\xc9\x0e\xeb@9\xcfo\xfb\xc9C\xc6\xa0\xb6|B\xf5\xf8>\xe7\xc1\xb4\x94\x04#UE*\x17\xb0\xba\xfby\x06\xc5\xb6\xe1\xae:\x86ke\x1b\xb3\xd9\xc8\x14\xbf\x8e=l\x16\xb2\x91\xe1.\xc5f]\x88s\x17\xcd\xc3lF\x84UW\xff\x0c\xdes\xfe\xda\xbe\xe3\x1d\xe7\x11\xa70|\xe4)\\\xe41\xb9\xd7\x0c\x9a\xb8/c\xd0\xae\xf6\x06vR\xdc\xb1\xd7|\xf7\\\xf37\xa7\xcd\x9f\xb5\x91\x81Vr\x8a\x1b\xcfi\xb3p:Z\xd1\xca\xb1\xc1:m~\xae\xc2J2;\x83+\xee\xa2\xf2\xbf\x1ea\xe2\xf5mH\xc9\x8fd\x9a\x17d\xfc\x99D+\x14l\xd2 \n3\xf1\x8a~.y\"k\x0cOR%m\x1e\x96?\xe5\xe2\x12\xa6\xfa\xfeKB\xe7'\x84\xf2Y[\x86E\xb8 \x94\x14\xe6\xd4\xe3,JW%\xab\x94P\x9ad\xb3\xb7ya.\xf6\xe3\xddqL2\x9a\xd0;\xfc\x1e\xa6i~{Y\xdc\x1d\xd3\xb3\x15\x95\x85\x16\xec\xa8\xafn\x0ddj\xa1\xbf\x96\xcb<+\x89\xb9P\xa9\x16)\x1b\x05\xf8\x1b\x0dg3\x12\x9f\xc9\xb1\x96\xcd\xa1\x97\xac\xbb\x97\xe1\xac\xca{Dh\x98\xa4\xd5\xab)\xfby\x9e\xd3c\xaet\x87r)\xca\xa3Z\x88\xf6\xe6rzo\xc2\x92\xbc\x0f\xd1\xacO\x00@Rw`\x9ad\xf1Q\x95\xc6+!\xd1\xaaH\xe8\xdd\x91\x96U\xa6\xf3i.\xf2x\x15\x89\xa6\xa2<+W\xb2\xdd\xbc9\xc2eH\xe7\xb2\xfcb\xcd\xfd!I\xe3g\xfcM>SRdaz\x94G<_\x92M\xf9^M\xca\xb3\x83\x8bg\xbc\xec\x92D\xd5\x8f\xff,9\xa8\x9c\x932O\xd7$\xbeX\xdd\xd0\x82\x88\xe6Y\x06\xedC+\xbdQS\xf5r\x91\xaf\x8a\xa8\xce|Ay_WE}\x19\x8b,\xaf!>\x82\xa2\x15\x94\xb9\xafLA\xdaQ\xa5'GyA\xd1\x0c\xf1Wt\x87\xf8+\x9aH\xafn\x13cm\xbf\x97\xd0nVa\xb0\x1c\xfd\x08\x17\xecL\x9d\\1\x96bF\xe8q\xe6N\x9c\x05\xa1\xa1\xe3\x83\x83K\xe6T.\x9e5G\xb5\xd4\xf3a\xe2T\xdb\xact\xae<\x1f\x0f\x8d\x12Eh\xffy\xe1\xb9\x93+\xcfC\xc8\xea\xb1\x87\x94\x97\xa0\xc1I\xb8\x0c\x92\xf2$\\\nE%\xec\x93\xeb`\xb0\x06\xaf\xd6\xf4\x16\xc9I&\x12\xb5\xb9A2\x81\xf7\xe4$\\z*9\xea\xab\x98\xe1g\xae\xe0\xd2\x7f\xf7a\x9a\xae\xf7Bj%)\xbf \xb1O\x94\xe7\xf1\x0e+\x93%\xa7\xea]RR\xcf\xf5\xbc\xa0 l\x1f\xb9\x8d\xaet\xdd\xc1\xc8\x08\xa4\xb1\x081A\x959\xd9\x97o\x88\xb8\xaf?/R\x87[5\xd4\x89]r\x19F\x9c\xbbj}\x9b\xe0\x04\x0el\xca\n\xf8r0\xb0j\xce\xbb\xbe\xfc\xffP\xa3\xa87\xa7\xbe<\xe6AX\x8e\xb3\xff\x1a:\x87\xf1\x84|\xf2\x83\xa4d\xffT\x81$ \xca|A\xbe\x11f+\xe0\xd4\x94\x8d\xfbf\xe4\x92\x07\x1d\xba\xf49>\xa5$\xa3,\xc9\x0c\xabz\xc7\x14\x08}\xd3\x9aH6\xd5\xb1K\xbcj\x9f\xf7\xed\xef\xd6~f\x0b\xda&\xd5\xb8\x8b\x92\xfb\"\x8f\x81\x953Tz\"n\xceZ\x1fQ\xa7\xac\xb5\xb5x\\]r+vW\xbb\xd8\n\x1d\x93`1yb]\x8bM\x811\xd2\xcd_Fp\x89\xd1\xf30j\x15\xcb\xe8,V)M\x96aA\xb7\xa7y\xb1\xd8\x8aC\x1a:u\xb6\xbcX\x1c\xb1\x14\xcc\xcapE\x12\xe1q\xb8\xfdy\xeb\xf6\xf6v\x0b\x8b\xac\x8a\x14\xaf\xd7I\xecT~\xda\x8d\x04\xb96U\x06h\x14\n*\x15\xc0\x189\x1aI\x894\xf2\xe5\x9d\x00Z\x1d\xe3\x87\xf5\xe1\xde \x83&dy/\xb0c\xc7\x8a\x9c}\xc3\xa1\xd2\xc6*\xd1\xaa(HF\xdf\x0bR\x84\xd3e'\xcdS\x19A\xc5\xfd^\xbfrY\x99y\x04~1\xf4\xd2k\xd6\xc1\xce\xff\x893#\x14\xe1{\xc5\xff\xe5%\xfe\xe7\x1e\xba\xd8\xaf|\x89D\x0f\xfb9'a,\xf6B4g?\xd0\xcb\xa6\xa3E\xd2\x88z\xc5\xde\x15Wf;\xd7\x00Z\xf7\x9fS\x1e%M\xa5VX\xd1P\x08\xcb/HJ\"\x9a\x17\x9e\x1b\xf5\x05\x82\xac\xb0\"\xee\x8b\xaaBM\x9d\x9fs\x04\x9cHz\x94\x86V\x85\x1e\x15\x9d7Q\xd3d\x8f\xd2\x0c\xab\x8e\xa3\x0cG\xf7\xfc\xef\xeb\x04\xe1\xa35\xc8k\x14\xcdf9\xdd\"qB\xf3\xc2\xd6\x01A\x9e>J\xf3\x7f-\xf3\xac\xa2>8\x18\xe9\xb3\xacm\x86%\x87$\x8dp~\x94\xce\x14\xa2\xbe\x9e\x0e\xf9Vz\xbe\x97\\R\xdbC\xecSh\xccB\xf7\x11\xc5Qr\x8b\xce\x91\xcd\xca\x80\x89\xc3\xe8\x03~M\xa8\xa6d\xdc\x8f1\xce\x05\x8f\xca\x8a \"~b\x19\x9c\x151)H\xccg%X\x90bF\x18\xc3S\xd3\xa9#\xdd\x16K[\xbbx\x08\xb3\xf4mK\xd9\xdd\xd3\xa5\xdf\x00<\xcf\xd7\x97\xbeZ\x87\xf6\xaa7\xde\xe7*\xff7\xa8c\xd3\x96\xbaC\xb3\xc6\xb5\x88#)\xb9K\xf34\xcc\xfd\xee\x0b\x16\xd1\x98n\x0f\x8a0+8\xd8\xfe\x8a\xbb\x86\xf1Wi\xaf#\xc8\xcai\xde\x9e*m\xae\x16|d\x1aG\xfd\x98\xddP\xab6\xac\\\x83\xb57\xb7\xbb\x1e\xd8\xae\xda\xaa\xa8\xb3u,h\xc3\x9f \x84%\xe5\x0c\xe6\x0e,\x06v`{\xbd\xefNv\xb6^_}\xe7}\x0c\xda\xbf\xb6\x93\x80|&\x11#p\xb8\x0b\xb7]\xd3lH\xe9\x87\xb9+\xf1\xc0\xae\x10I\xeb2\x02\xaag\x12\xee\xdaB\x18s\xe3\xb3\xbe\xc6\xf1\x0e\x9a\x07\x0e \xca\xe4\xef\x04~\x80]\xaf\xb9\xfb\x05\x17\xdbf)%\x03\xd7\x93\xad\xb9\xd6\"\n\x1d\xec\x83K\xda!\xe9H\x87\xca]\xdd\xd5\x8d\xaad\xd5Uk\x18bc\x1bV\x83\x1c\x10F\xae\\\xb3\xb6\xf0d0\x15\x97K\xd9\xf0\x9a\xb7\x8f\\W\x1f\xb6\x9a\xbd\x9a\xf2\x0bB\xe7y\xdc\xab\x9f_-\xb7U\xa6.\x9f\x84U\xc6\x18\xfb-\xc6\xd8\x9bU\x07\x80\xc3\x95\xe5J\xdat/\x8f\x87\xf0\xa8\xb9\xda\xfanh\xbc\xdf\xe8r\xc3oCR\xbc\xe1\x0bB=\x974\xd9\xb8\xbe\xe3\xe5Z\x97f>vGd\xd5}\x1d\xb9\x95\xc8\xab\x12\xb2~[O$\xd5)\xeak \x9e\x0c\xc8\xca,\xf8}\xd4n(U\x1b\x89\xfc\x968\xba\x97\xd0\xab]\xbfY)=d\xd3\xeav}\xa0W\xbe\xd031\x82xS\xb0!\x08g[\x15v\xb5\"\xd4 F\x99D\xeb\xa6\xdcoI\xe2\x1fe\x96\xd5.\xda\x85\xa1P\xcd\xb6r3\xf0(\xed\xcb\xfa\x8cK+\xee#\x1e\xa5!V\x97\x99I\xac.@\x1e\xa5\x1dQ\xdd\x006\xa5\xfbf\xc6\xdc\x99;\x1fn|\xb8\xee\xbe\xceku\xac\x11\xd8\xdd\xaa\xc5Qe\xe7\xd7\x8c\xaeSu\xd0\xe9\x9b\x02\xf9\xa0\xd7\xa3\xae\x0c2\xd3FS\x18\xda\xaf\xb5\x06j\x07o\x13:\x97\xaa6\xe5\x80\x91\x19+\xd1p>'Z\xe4\xd0\xab\xf4\xa1#W\x1f\x03b\x17|\x8ekP\x11\xd5\x9f\xaf5\xe3S\x1f\x04\xcd\xdeU\xe9\x8f\xdc;\x83E\xb2\xfe|m\x85\xb6o\xe7\xb0~\xb6\xfbpnt\xca\x80|\xe4c$%\xb4\xbd\xa5\xa1h\xae\x97#\xeeC\x1fe\x8b\xb3\xbaz\x0f\xc7\xc6\xfbg\xd9\x87\xfa\x8a\xb6\xf7\x94\x92S\x82~\x81*\xc4\\]\x02q\xe5\x01W\xd9G\x83\xee\xcf\xa05\x1a\xe5\xc6\xcc\xa0?\xd1\x89\xc6\x9a\x83\xbc\xd0\xd8\x08\xe5z\xda<\xed\xb7>\x8c\xfd\xc1\x13A\x06\xdf{\x81r\xc6+`N\xab\xf3YEl|5\xaflJ\xb7\xf2d\x0e\"\xf4\xab\xcfH\xf8]\xf4\xcc'\xf7\xa2\x10\x02\xe9\xf0\xd0\x07QZ\xfdD\x06\xce\xb2@=\xc6A1\x8c\xbf\xd32\\G\xe8\xd9\x03\xfb\x08C\xfb \xf6\xed\xff\xd5\xea2\xf4^\xcbZuC\xb9w\x94w\x8c\x1d\xfb\x11TPn\xc8\x9fz6\xee!'\xb1\x0d\x8a\x18\x83\x10F\x95i\x10\x9c\xe2x\x0e\xf3l\x9a\xccJ\xb6<\xf6\x85\xc5\xcb,\x06\xb8\x17yAM>\xd0\xe5\xc3\xfd\x10\xd7{\x92\xe7\xef\x04\xf5\x0b\x94O\xe4\x05\xfd\xf1n\xd8\x9a(e\xcd\xee\x00\xba\x02\xd4\xea\x8f\x9c\x0f\xa3\xdej!t\x1fV\xd8?R\x94\xca\x1cL\nK\x14}P\xe9\xeb}\x90]\xe8\xb0\x11\xff\xea5)\xa6>\x0f\x0c\xf2\x9e\xdd\xd8g\xe9\x83\xbc\xee\xb3\xbe\x1a\x93\xbc'^z\x02{8t\x8aU\xb8\x05^\xd0\xf7\x0eV\xc1\xdb\xdd[\xbb>\x96F\xdc\xd9[\xd6\x01z\xa0\x8a\x0e\xca\x11$\xf7F\x04\x86\x9d\xd9\xdc\x82\xbe\xa6\x07e><\x86\xca\x9ck\x192\xaf\xf0~\x17\x1a\x9f\xf0LST\xb4\x1e\xa93\xbc\xbe>&\xa1\xf1~\x80]ik\x90=J\x8f\xb4j\xef\xd5\xb13\x8e#\x9b\xban\xf7\xe0O\x0e\x95\x1b_\x96U\xb2\xc9&\xa8P\xb4\xeb\xee\xd1\xc2\xa7\xc1-\x98\xb4\xfa\xee\xd1\xd0\xc1\xe0\x86\x0c:\x85U;\x1d\x0dh\xc6)M\xbd\x10\xa3\xfa\xe2\x90\xdeK\x04v\xef\xbbw\xa3JW\xf3|5\xa3\x92\xfcA\x8a \x03\x9b\xb4\xcaW\x8a\x81\x9c\xb0\x14E\xe7\xb89\xb2\x06\x9d,\x15\x9c2y\xc9\xe2\xd8\xc6\x08\xe2\xa4\x1eX\x0b\xa6\xcd\xc3r\xce\xc5\xac\xf8\xf30\x8f\x89q@\xa0\xe3y\xc3\xa5\x9aXq\x93\x11\xca\x03Y\x85JQI\xed\xb6Y\xf7NMi\xb7o^\xb7N,\xf3\x9ec\x99\x1ee^\x1d\xda-\xc2y\xe9)+\xab\x16\xc2@\x13\xa9c\x7f8\x98^'\xb2\xa3\x0c\xab\xe6\x0cf7\xf4{\x1f\xe3.\xbe\xffh\xfe\x19\xdb\xf7\x1b\x01\xa5\xb0\x80\xc7P\x90\xb0\xae\xca\x99\x98\x93\xdc0\x95&\xe5\xf0oD\x83\xbc\xd0\xd5c\xa1\xb8\x07T\x97\xd4\x9ah]\xba\xa1\x0d\x04\xd7y1\xa5N\xa4<\xac\x0c\xb8\x02p/Z\xd7\xc1\x8e}\xd0\xf7\x17\xf2i\xcd\x0e'\xfa>W\xf5\x93k\x1d\xff\x07Hj$\xdanH|\x8d:r\x06\x17<\xdc\xcc\xb1V\x1a\xc5\xf8\xcf\xce\xb6\x08K9\xd9Q\x02\x12\xaa\x11\xa2do\xe0\xd2\xde\x9f\xff\x81*\xa9lRz\x95R\x0d\xb3p\xf2\xaf\xd155\\\xa3\xa0\x99\xb2\xf4\xf1\xd2\xb9\xbd\x1f\x88\xd0\x85\xccU(y^y\x9d\xf7A\xb9T7\xe5#\xaa\xe5\xb5;\xbd\x97@x\xff\x83A\xac\x1a\xaa\xa0x\xa7\xd4\\\x8a\xdf\xb5\x7f\xb11\x1e7\xe5p\x95\x05M\x1f\nl\xcc\x8fP\xaa\x0b\x16!\x8d\xe6\xee\xf6\xffq'\xe1\xd6\xdf\xaf\xd8\x9f\x9d\xad\xd7\x9b\x1f\xb7\x82\xab\xef\xbc\xd1\xb6E\x0b\x97\xbb\xa0HJ\x19\x90\x80\xb1\xed\x1c\x92\xb3V\xd0\xc1\xd6)\xcb/P$\x8a\x14\x92\xef\xd6G\xe7Z\xac\x0f\x1f\x9e\xc33\xe6\x9ar^\xc3\xf6\xc1`h\xd47%\xa2s\x13gN\xe9\x12\xd54)]\x96\x8a\xb7\xac\xe3\xaa$\xf7\x90U\xb7\xdce\xf4\xd4)\x0d\xe9\xdd,zd\x8a\xc7\xa1S\xecF\x19-\x8d\x07\xdb\xe6Rp/z\xdf,M\x96\x03\x02\xcfJqj\xe5\xfa\xd1\xa0\x0b\x93\xa9\xeb\xd8\xc65\x7fm\xf7\xc4\x8c\xd6\xf61\xde#W\xf3> \x97\xda\xb6\xf9\xaf\xb7\x8d#\x8a5\x9c\xf8\xddp8\x98\xcf\xd4\xd7\x92p3\xf3\xa6W\xc2\x92\xd0\xd6+\xe7\xc7\xb9E\x12J\x80\xc7\x8b%\xbdC\xfb\x9f\x8az\xc6\xaf\x12N\xf1\x93\xb4\xa8\x92\x89\x9a\x16\xe0a\x18\xcd\xd5:M\x86S\x82O7\x7f\xc2\xb4\x0bi\x9c\xb5\x0c\x8b\x92\\\xe6\x95U\xd5\xc5\xf8\xf2\xfa\xe2\xf0\xa7\xf1I\xc3\x9c\xfa||q\xf6\xee\xe7\xf1\xd1\xf5\xc5\x87\x1f/\xcf\xc7\xc6oj\xda\xd9\xfb\xf1\xf9\xc1\xe5\xf1\xd9\xe9\xf5\xc9\xf8\xf2\xe0\xfa\xe7\x83w\x1fx\x99\xc3w\xe3\x83s\xf6~\x8c\xf9\xde\x1f\x9c\x1f\x9c\\(_\xce\xc7\xff\xbf\x0f\xe3\x8b\xcbF\xca\xc5\xfb\xb3\xd3\x0b^\xfc\xdd\xd9\x9f\x1aYXoO>\\\x1e\\\x8e\x8fZ\xe9\xedw\xa5\"S\x0fD\xdf\xc7'\xef/\x7f\xe5\xe9\xd7\xc7\xa7\x87\xef>\\\x1c\x9f\x9d\xaa\x19\xf0\x93\x9a\xf0\x9f\x17\xcd\x0c\x1f\xce\xdf\xa9\xaf\x17\xef\xc7\x876\x034\xd8\x83\x1b7s\x9f~\xaf\x93\x9d\xb9\xf8\xf2\xea\xb9\xfe%\x91e\x9e\xe9_B\xf1\xe5\xf9S\xfd\xcbJ\x96\xd9i\x15*\xc5\xa7g\xcf^\xe9\x9f\xd2\xea\xd3k\xfdS$\x9b\xfa\xdek\xd0\x8f\x1c&/\xfaT?%\xb6z\xc7\xe8\x8e\x82,\xd30\"\xee\xf6G\xba=\xf3\xc1\x01\xd0\xf1\x96\xcdkc\xad/\xd6Fsh/q\xdd>\x1f+3g\x8d\xaej\x9e\x1c\xcd\xbd\xf5-\xb6\xf9\xa7\x1d]\x18\xe0\x1c\xe0\x03j\xe9?\xb8\xf5\xdbok\x9d\xa1\x85\xde\xc5\xec\xe9\xc2\xf8\xa1]\xe0\x06\xf6\x88\x13\xcd\xbc\xb8! bO_>w\xf4\xc5\xcc\xa9q\x95?\x8b\x86\x9e8P,\xf7?x\xb4\x9f\x86\x0b2\x02K\xf0\xa8%?\n\xac*\x85I\xf9\x97E\xaa[\xfd\x00\x0crL\x80\xf3\xd6)\x89\xb4\x1b\x9b\xfe\x8b\xa6\x0f\x87o\x9d\x1c1\xb9\xddSS\xdcsjR\x12\x16?\xeb\xa7\xed\x83A\xfb\xf8A\xf3q\"\x14D\xdbj\x1c\x03\x96U\x9av\xa1\x91a\x1f)\xdb\xd3\xfd\xbf>\xa8\xfb}\xbb\xc1\xb2\x9c\x9f\xc8\xdd\x08tS\xbd\x87\xcc\x80\xb4\x1d\xfb\x1f:\x03\x1a\x1f{\xcf\x19`\xf0\xab\x10\x96\xdf2\xf6\xcb\xc7\x1d\xbbT{\xbe\x87\x0f\x10eD\x92r\xfe\x96\x01\x9d\xfc\xb7\x18PI\xe8}\xd9[\xdb\x80\x8e\xee= \xce\x9ew \\6^\x0bx\xca\xf1\x1ad\xc3\xb6\xf16\x89\xd9iEd\xbe4\xd9\xa5e\xaen\xd1\x19W\x05Z\xf4\xe5\\|\xda}\xd9\xfa\xb4\x96Ti\x9b\xcc]\x88O/_\xb4\xc8\xdcY\xf5\xa9Ej\xdfI\xc3R\x13\x93{c=\x14dh\x1e\xd51\x04\xe9v\x0ca%w\x1a\xf3xm`\x1e\xd0\x14Q\xfa\x9fA;\xc8\xe6\x18n\xdb\xfcG\xa3\xc8\xaaH\xb5\x12c\x03\x07\xd3(\xc2\x95\xa8\x1be>\x9b\xd8\xa0F!<\xd2\xb5R\x83\xb8\xabF-\x84\xf1\xc9\xbc\xae\xfa\xfaF\xab\xf5\xd0\xc2\xc7\xf1\x8a$\xf3l\xec\xd0'\x13O\xc8\xcb\x95\x84^\xcb\x8bt\xad\xd4\x81\x81\xb3T\x0b!\n\xd3\xca\x9cup\xa9uYq\xe9m\xa9\xe3\xbd\x81\xf3\xe5e\xd3|f)ca\xa0y1D\xb9\xb6Q\x9e\x18\x99\xf1fAS\x8b\xc7\x9d\xec\xbdZ\xbesi\xfe:@\x8a\xd0\x00\x95J\xccz\xbd 4\x14\x87j\xb3\xceS\x8b\xb4\xa2QOm\xde\xda({\xde#\x051\xd6q]r\x81\x8bV\xd7Q\x05\x0c\x95\x80\xc5a\xcb/e\xaa\x8d\xcc\xef\x86\xaa\xb8\xb9;>\xba\xa8\x16R\xc5J\xdc\xa6\x9bH\xab\\zS\xe8\xd3K\xfeV\x19:\xad9\xb8\xc5\xe7\x01\xe6,\xcdGLQe\x937J\x96\x8c\xdc\x99\x10)\x8a\xce\xea\xf8\x95\x9c027g \x85{R\x83\x1c\xd4\x1a\x16\x10\xc3@\xc0\x97/\x90\xb8\x18\xb0\n\xc1\xb6C\x87\xabD\x0bqF\xda\xb1i-\xda$\x1d{\xbez\"h\x91\\\xaa\xa0\x0c\xa7\xe4]\x1e\xc6\xc6h]j4=\xf3T\xf2\xa5a\xf4t\x9e\x8aX\xfb\xe8\xf1-\x0f2r\xcbx\xf6qq\x9fN\x9b\xa7\x8f=)Y\x93t\x042\xa0\x935\xdf\x82\x94e8c\xc4GP\x90\xb0\xcc;\xcc\xe4\xd2$\xc3|\x8b\xb0\xf8\xc4OQ\xf6+`\xc9\xa8\xdb[\xbfmb\xe4 .:\xb3\xcck{\xf2l[\x05\x03\x1d)\xde6\xf7\xc0Uba\x85\xb0\x0f\xce*\xe3\"et\xf2\xc1\xb6VTo\xad\xd0\xe3&\xe0M\xd1\x88\x1bz\xec\xd0\x1fH#}0\xc4\x95\xfb[\xa5\xbf\xa5Hf; a0\xecM\xab\x86d\xe5\x85\xa8\x7f\x7fBus6`\x8f\x82t\x83\xde\xbbO\xa1\xf2\xff2\xed\x00\x8a\x15\xecA\x18L \x8d\xe6\xf6L%f\x12S\xd5\x01`\x98\xed\xe0\xc2\xc0\xe3\xc8'\xaaD\xb2\xb8\xfa)\xec\xc3?\xbe\xc2\x08R{\x91\xa9\xbcT\x14:\xc2f\xb5\xa0\x0fh, 7\xe6mXd\xdc\x91\x84\x98\xa2\xc6:7\xc2tB\x99d\x11\x81\xf5\xb3`w'\xd8\x810\x8b\xe16IS\xb8!P\x90E\xbe&1$\x19\xac\x9f\x07;\xc1\xce\x1bX\x95\x04,r~\x11\xd0s\xc3\xf1|\x0ep\xb6XW\x0c4\x18i>\xedRv\x8e10\xd9\"\x8fI*/ZN\xc2\xa8\xe8\x88*5\xc7\x12\xd5\xcdVO\xee5\xe6\x16C9\xce()\"\xb2\xa4y\x87R\xf5B\x94\xe0\x04\x8cR\xc42\xcaz\x95\xeb8?y\xe5i\xc1\xad\x9dG\xf0\xfb\xf6\xca%x\x1e\xac\x8a\xd4\xaa\xfe\xc5&\x8fq\x15\x11\x83\x88wIFNW\x8b\x1bR\xbc\xcd\x0b\xb4\xcf\xdb\xb7}h\x86\xdd0\x84\xc2\x90\xcf]\xd5\xcd\x0bZ\xd8\\w\xcb\x1b\xb7\x0eT\x8f[\xca\xe8cH>\xac\x8dN3\xe4\x9b\xb0$Gyd\xe5\x1dA\xb8\x00mB\xc8\x08b{\xf6&x\x8c\xa0c\xd3\xb7ac\x04\xeb\xae\xec-\xc0\x18\xc1\xc2\x98\xfd\xab\x17\xd09\xc9\x06\xe8WA\xe3\x8e\x95M\x98\xbd\x03\xec\xe1\xf6\xad\xfc\x1a\xd6\xae*\x9eL\xc1Mz \x0c\xa8$\x02\x0e\xba\xf3\xcf\xcc$\x06\x082\xa3y\xfb\x9f\xe1\x1do\xa6(\xd6t\x0d\x11T\xe5\xbc\x81\xda\x9a\xeac%K\x08?\xcf\xd9\xa4LWi*\xb6\xc8\xcc\xbd\xf3\x95\x14i\x15\xc0\xd2\x96\xdc\xc8\xb5\x91\xbd~ \xfe\x9a'\x99\xeb\x04\x8eZ\x04)\x15FU\xcb\xd8\x93$\xa0\xdcE\x9b\x9c7\x1f\xb5s\x84\x8b iu\xccr\x9a\xef\x93\x89\x0f\x8e kz\xa3?\xcb\xa7\x11\xcf\xaa#\x10\xa8\xfa\x08\xb9! Dc\xbd\x85\x86X\x01\xda\xa1\x8e= #\x13/qV\xc6E\xf1#j\x99\xe4\xdf`9XhWfvS\xaaVr\xcb\xfc`r\xa5\x1dGo\x85>\xda\xa2&\xc6\xd8kZ\xbf\x96\x15Y\xcdh\xc7\nh\x81X\x03\xdfQ5b\xa8\x0f!\x0f\x80\xe2C\xec\xc3\xdc\x87\xb5\x0f\x0b\x1f*k\xdf[\x1f\xc6V\x85\xa1\xba\xed\xdbb\xd0\x86\xc1p\x0bo\xdexP\xde&\x9c\xca\x0f\x96\x05F\xfc\xe2\xc1\xd0\xbb6Z\x14\x96\x04vF\xddk;\xe5\xe7\xd7\xdf\x82\xf2\xae\xa4d1d\xe3\x12\x19\x8c\xf1y7\xdc\xb0\xe7\xa6 a;\x92\x9a\xfa\xd8\xc1\x05lH\xc2\x89\xc9\x8d\x00\x1e\xe9\x05`\x04q\x9e\xfd\x9e\xc2<\\\x13\x08\x81\x0f\x06h.\x0c`\x08\xe4\x99\x0f\xe1M^\xd0$\x9b\x05\xdcaQxS\xac\x96h\xe2\xc1\xda\xb0\x05\x07\x069\x93\xcf\xfbg2\xd3yQ\xc1\xc6\x92\xa2\xa8)d\xc1\xb1N3\x1fi\xe2\xbc\xa2\xf2\xf8P8\xef\x97#E\xaaS\x9e\xa1\xa4\xfc\xade\xee9\x04\x94\xd6\"R\xe8`\xacK\x0dw\xf3\xb6\x87U\x1eb\xe8\xd4\x14\x91\xf0\x12\x91\xf0\xa2\x1fh\xe1\x1bp\xb0\xe9\xf9\x16\xbclz\x86\xe0j\xd3S)\x14\x8au{\xeaw\x99\x1b\x9a\x1el\xf9\xe9\x83[\x0e9\x91K2\xea\x0b\xb6\xbc \xe5*\xa5'\xe1\xd2\x17\xbc5\x83\xf2_\x12:?\xe4\x0e=%\xcaV\xa0\xed\xa5\x0f\x89\x9b\xe2\xf9z\xbfi\x93O\xc5tL9\x1f6\x8c\x96\xd2\x1f\x13[r\xf7\xb0\xaat\x96\xe5\xe6a\xd5\x98\xd8\x19\x83\xa2\xd2\x90\xc7\xc8\xea\xdc\xde\xbb\xaa>bQ\x7f\x10\xbc^>\x18\xbc\"\x05\xbc\x96\x88x9\x9f\xc4\x8f\xba\x88sWP\x04a\x9a\xe2 R\xba\x1e\xf7f\x86\x8c\xcc\x10n\xc9\xf6\x0c\xe4\xa2lO\x9b\xbbZ\"w\xb5\xd4\xcc\x16\\.\xa1\xb8?\xfbdz*l`b\xa0\xe6\xee\xfa\x7f\x1b\x03ez\x1e\xc2T\x99\x9e{3Z\xa6\xa7\x9f\xf92=\xa8Pm`\xba\x16\xd2\xbd\xf6\xac>WW\x885\xe3\xf6\x87\xb4\xfa\xd0\xa2\x83\x1e:\xbd\x15f\xef\x94\x10u=\x96\xa3`\x04\xf6\x08\xf0\xb6\xe7A\x88h\xf7\xfb\xfba\",\xe4\x90,v\xeeW\x0e\xd4\xcdX\xd2|i\xf1\x91cz\xba\xa9g\xf9|\xc5\xe8\xf1&G\xb6\xc6\xdc6\xc9\xa4\xfa\xb4\xae\xf0z|)\xa8O5Xs\xd0\xcf\xde:\xba\x07\xfd\x95Q\xc3\xab\x8an\x13\xb8d\x00bW \xd6\x9d\x9a\x9c\x0d\xbb\x93\xab\xcac\xcfR\x9a\xd0\x074\xff\xcf\x8b!D\x84\x15\x9c\xa7\x8a\xc8X\xd4\xd6=\xc0\xae\xf5\xe1\x90\xdb\xc3~\x8e\x95\x83\x92{-\xafxz\x1f\xaf\x8dx0\x10I&>\xed\x06\x07\xe4\xf1\xfaz\xf4\xba\xbbG5c\xf1\x1aO\x87\x1d\xec!^V\xba\xbb\xbb\x9e\xafK\xfe\x02j\xbb{\x80\x8aL\xed\xa1Sc\xb3\xa1\x83\xcb\xc6>\xae \xd3\xdef\x9e\xd9\x9b\x19\x8a\x11\x86\xec\xfe6\xd0\xab\xbb\xda\x87\x89\xb1\xd4\x841j\xbb\xaf\xafZ\x1f\xaf\xda\x0e2\xe0\xd9\xf7\x0d\x9d{\xab\xb5\xc77^\xec\xffM\xc6\xc1\xf4+\xa8\x03\x0cC\xfaV\xf7LX\xbd}m\xdb\x02\xdc\xd3\x11x\x8fJ\xdcy{\xff~\x8b\x8e\x9fT\xd8l\xaf\x99m\x80\xfe\x10\xdb\x1c+o\xfdO\x1a\xdd\xc4\xe2\xc0F\x0cO\xc5\x83\xf7\x1bi\xcb0\xe9[\xd6\xee\xf0A\xa3\xab\xb4\xa5\xcdC\xe4.\xc1\xef\xbd\x84]\xf6X\xdf\xae'\x7f\xf1\xcf\x18\xe9#\x98\x13\xf0\xb058\xea\x9f\x85\xe9\xc2\xf0iS\xb7v\xd3\xbc\xed\xc1j\xae\x03&\xa5_=\xd7\xfc\xb9`'\xb6\xc9\xcd\x81e\xc9>uAK\xc3\xb8\xef\xbf\xe7h\xffv\xaf\xd1\x1e\xf4\x8c\xb6e\xe0\xf8\xbfa\xd0g]\x83n\x18y\xf6\x1e\x9c\x1d\xe34\x8c\x857\xff\xbe\xab\xf9\x96\xd9io\x17\x86*\xe5\xd9Tn\x8aa*{\xf9P\x95\xbd\x95&\xeb6\xe7\x12\xf1\x06\xc3\xf2YOu)\x12\x96\x0c<\x18\xca3\xe7\xe1r$qW`\xcc1\xc5\x1c\x95\x8e\xa8\x05m\xc2\x1e\xacl\x9c\xc1\xfd\xb4S\xac\x9a)\xe6\xec3\xbc0\xe0\xacD\x9b|M\xa6\xe0\xce\xe0\xc9\x13\x98)\xa1\xc7\xf4w)y\xd2\x93\x85{\xd2~\xf1\x93\xa4iY\x0d\x1bBK\x86{\xc7\xaa\xcf\x89\xf6\x1e3\x98\xa5w\xc6\x0b\xcf;\x1d\x07\xb9\x93\xd4\x87\xe8\x8am\x84\x8c\xad6\xd2X^\x17\x9bJ\xd4)\xd9k\xbe~\xf9b\x8d\x1f\x00\xca\xd6P\xcbLx\xc3\x1d\x1e\x0c\xdd\x0dt\x0e\x8e\xa1\xfcv\x84\x8b\xa52\xf9;w\xda\xe1\x9a\xea\x82=p\x0c\xbe\x97\xc0\xcc#\xa0H\x07\x83\xc8}\xa6\x1f\xaa\xc8Lq-\xfa\x91\xcaH\x01\xcd/\xd0\x12\x96\xb1\xcf\x02<*\x00?\x8eQ\xc8\xa7\xbe\xefi\xdfG\xbcP\xca\xfeD\xa2\xf3\xcd\xfcY\x90/\x8fcw\xc6\xefc<\xd4)\xe5d\x96k]\x136\xa97\xb0\x07)l\x823r`\x13\"\xf3\\2v\xb6\xe0\xb1>\xca\xa0D\x1c@\xe2\x0bLro\x90ko%w\xe8_]\x8bjX\xbe\x9f\xc3\" oR\xd2\xa5\n\x05\x18,\x9d\xe5\x1eU=\xe9\x96\x08\xb0\xa5,\x97aDFpc\xcd\xf8\xb5_\xbap\xfb\x08=\xedo\xbf{\xce\xabv+\xf7>\x15t]{\x12\x91\xec\xc35\x8c\xe0\xd6G5^=R\x1d\x0e\xa2\x9d\xec\"\xa0\xf0\"\xad\xa8u\xa2L+\x9d\x17B\x87!\xdfm\x7f\xe7\xd8\x17y\xac\xb6\xfac\x1es\x9c\xc4\x8b\x9bK\xb1\xc1\xdd\x05I\xf9\x9f\x17g\xa7\\0\xed\xb9cT\x8cW\xab\x81=`\x19\xb86\xbc;\xf6F0f\xfba\x8csi\xc8<\x16\x93\x0c\xa3\xf6\xa7\xf6\x86n\xa5\xb0\xa1|\x163\xaf\xb8\x01\xf9\x07z\xe6m\x8f\xe33\xee\xc4\x9bU\x92J2\xcc\xfd\xec\xf9P(\xc4\xa8\xab\x1c\x90\xf5A\x08\x9f\x0d\xb5\x11\xc3\x11\xa6R\x19\xbd\xfeq\xd7\x0d!\xe0\x84\xea*:\xea\x93\x9bG\x99u\xab0\x16m\xc2\xd32\xc0\xbc\xe1\x9bD>_U\xf8k\x0e\xd3p\x97\xcc\xc6u\x01{p\x14R\x12d\xf9mG\xa8\x9bLRg.\xd1\xd5\x05\xad\xd3F\x83x\xc5Qj\xa3\x0d\xd8\x82\x8bj\x0dyO-c4\xa8O}\xf5\x84\xa0\xad\xbfyuJ{\x1a\xea8c\xb9\xf6F\xd7}\x0b)\n.^\x98\xab~m\xccg\x9ei@\x8d$\x0b\xafI\xdan{\xf4aK\xf5\x04\x83\xa3\xaf\x1d\xab\xa3\xaf\x9d\xa6\xa3\xaf\x9d+T\xe37P\xef\x15%\xda\xfe\x96uR\xa0\x89\xd8\x07\xb9b\x9e\xc3}\xfeP\x0c1\xc9\xcb9Wf\x1fi\xdd\xa4\x9bT\xd2$\xc14\xebR\x9a\x0f+}\xd5\x01\xf4;\xe9\xe7\x07\xca\xea\xf6\xdf\x16\xa5\xce\xed>\x0c\xb9\xfa\x80\xe6\x1d\x8b_K\xd8\xa9\xfc\xb0\x1d_W8x\xednl\x8a\xf7\xc9\xed\x03\xcb\xce\x08D\xa6\xa3\xca\x9c\x9d\xd1J\xdb\x9f\x17\xe9v\x12P\x86\xac\xa6\x96N\xccq\x00\x15\x81\xd8\xe8\xbe\x0f\xb1\xfd\xec\x16\x80\xb0\xd2\xb8C\xd4},\x9a\xb85\xb1md\xa1\xfcm\xd1\xbf\xe7\x8a\xdf\x96\xa5\x96\xd8\xa2\xdfb\xd8V^\x92\xc4V\xednS,\xdc\xa9\xa5\xab\xc2\xb4\xd9b\x9fa\x0c\x97\xbb4\xa0\x1c+\xce\xc1_=\xce\xa8H@>/\xf3\x02\xfd>7\xe7\xbb\xb2\xf1\xcd\xdc\x97\xcf\x9ej\x90P\xdb\x087\xbdO\x19\x9b\xb4\xb57@,\x89\x91]\\n\x00\x12f\x11\xbaUD\nKA\x80\xe8\x11\xb4\x80$\x03\xe2\x01\xde\xea\x03\x9b,T\xb4p\xd1\x1f\xeb\x08\x92,\xca\x8b\x82D\x14\x92l\x9ds\x07x\x1b\x16W\x8e\xe4~3hv\xe7U\xd9(\xb9\xaf\x9f+\xcdT\xc3\x0f\xa6CD\"\x19\xb9\x1d\x805Y\x8f\xda{\x8d\xd15\xc1\xb2\xc8\x17 \x8a4YUdX\x9096\xe9\xca\xfcRm\xbe\xb3\xf6,;?\x861\xbc\x17mEyV\xd2b\xc50\xb3M\x97\x11O \x1f\x0f\x1b\x83\xbc\xd6\xf3y\xe7\xc5\x05*\xcb\x84\xbe\xe5D\"\xa3~1M\x0b.\xf3U\xb5;\x1c\xb4t\xf5\"}\xbfcZ\xa4\x01bB\xd4\xb0\xe3GW\x921\xd8D~\x9aLrv\x16\xe3\xbf=\xa0\xec\xdf\x08\nVG\xee\xe3\xeb\xbf\x04\xf2^>\xdf\xb5\x8c\xaax\x8c\xea_\xbd\xb0\xd4\xce@M\xd7g\"\x9f\x97i\x12%t\x04\x13\xd6\xb1\xe7\x8c\xe0u_>\xff^\xfc\x7f\xe1\xa9\xdeP\x1f\xde\xbb\x0eJR\x99\x97\x17\xbb\x167\x93\xec\x9b\x8e\xea@\xd0=\x9a\xc7\xca`s\xeb\xea\xbb\x91\xb7\xef~\xdc\xfe\xb8\xed\xed\xbb\x93\x8f\x17\x1fK\x0c\xc9\xd9.\x1eb\xf1\xc9\xc1\xd6\xff\x1f+\xe0\xffw\xb6^on\x05W\xdf\x8dX\x05\xdb\xedB\x8c|\xb1\\\xad:\xff\x86\x9e#\xc3r\xae\x87\xf3\xae\xb3\xec\xb3,\x7f[\x91\xe2\xce\x9eg[\xfatDG\xca\xd6l\x7fd\xd9\xc2\x15\x92x\xbb\xb6\\\xa7\xe1)\xeb\x13\x8fH.\xaf\x86w;\nl\x8f\xdc\x8f\xf1\xa6\xf7\xef\xdb\x18\xc8\xbch\x14\xebo\x04{\xac5\xd4*c\xa8\xa6}\xce\xc9\x87M\xe7\x08v\xcd-\xe3D\x8e`\xb7\xf5Q\xf5# \xaa\x9b\x8d\xd4\x8e\xaf3\xaepo\xb3\x94C\x015\xfa\x83s+\xc3m\x1a\xa4\xe2\xd4\xe2\xc2@\x8bp\xd5\xb9I\xf3\x9b\x91#d\x9e\xcb\"\xa7y\x94\xa7\x1e\x87{v\x96\xb8\xab\x8c\x94Q\xb8\x94\xbc\x13\x9bF\xcf7WH\xd2\x92\xe8\x8e\xea\xf6t\xf7\xd8\xf2A<\x981\x1cX\xb7E\xb0b\x1fJO\xeaz\x14\x93\xcc \x91\xac\x1bR-\x99\xad\xda\xd6uS\x84\xa1\xdb$\x03\x94\x90\xba\xacr6_\x93LG\xaf\xf2Ql\x14\x8a\xa0L\xc3rNP\xfc\xec\xd6o\x8c\xb0\xa5\x9cQ\x9f\x17dj\x8a\xfa\xd3J\x91\xbc\xe9\xef\x9a\xd9\xccp\x11u{;\xad\x02\xfaZ\x89g\xf3\xa4\xc8\xb5\x1e\x01\xe5\x0e\x9f\xd9\xbf\x80\xe6\xef\xf2[R\x1c\x86%A)\x8fc\xb1v\x17\xa3\x1f\xc1\xc6\x06\x9d<\xb5\xec\xbe\x82\x94\x94U\xff\xac\xbd\xd1\xf4+V\xf3\xd0\xa7\xb6C\x14*J\x8f\x1d\xf1*\xb17\xad\xbdPW0E\xcd\x82\x176\x83\xdc\xec\xa9\x94\x1a\xf7sn\xc1\xb0\x12\xc1\x91-\xdc\xcc\x02j\x97\xdd\xe6\x1c3\x96c\x9eX\xb8\x8a;\xd8\x83\x9dv\x7f\x10L+\x88f\x84\xd3\x02\xad\xf5\xe5f\xaaR\xb8=\x8e\x8f\xcb\xcf\x1d@s\"B \xfe\xb3Q\xf50\xabJ\xe4\\\xcc\xe7\xf1\x82)RH\xec\x9c\xdap\xd9q\x13\xb9\x84{.\xf6\xbc\n\x0f\xe0\x85H(A\xdd\x87Y\x03\xea\xe5\xef/_ \xe1\x1eu\x95\x8cU\x15\xc8\xf8\xc9\x17DL\xea\x9b\xe3\xf8\\l\xc1h7\xea7ku\xd7\x93\xa7l\x83N\xb6\xdd\xe0;o\xbbq\xf4xo\xe0\x0e~\x80\xb5\x10s\xbc\x81\xbb\xcdM\x0f\x91\xb5\xcbx\xd8\xf5\xe4\xee\xca\x9b\xec\\\xf9\xdc\x12{\xb2{\xe5C\xc9f\xa5\x84}\x98M\xe6\xb8\xef\x19|\xb7]j\xb2\x1c\xff\x8f\x1b\xa3,@\xfaX.=~\xc9\xe1dh\xfe\xa2f_\xb2>\xee\x83++\x15\xa0\xb3#tT\x95\xa4\x1861\xb7\x87A\x87\xb5\xfczf,\xcfs\xc6(\xfc\x15\xbb\x9c\xf7C\x14\x8eq\\z1\xdek\xcf\xf3\xe5@\xf1\x9f\\\xa5\xe5\xe4\xd9\x15\xae\x96Hd+\xb0\x9c<\xbfR\xebe\xff\x9a\xa8\xc0\xb0}8`\xcd\x02<\xe9\x90\x14\x12\xbf=\x84+\x15 @\xf1c?\xab\x8e\x91 \x9a\x87\xc5\x01uw\xc4\xdc\xea\xdfy\xef8GQ\x9f=\xa2\xd5*\xd3\x00?\x11\xa0\x92\xdd\x18\xe9\x0c9\x14g\xdb\xf1\x82r\x99&\xd4\xe5?\xe5\x0cn\xedz\xd2a5Q2x\xbep\"\xc1A\x8e\x1b\xbce\x93\x02\xb6\x18\xfd\xc1\xb7\xd2.7s\xdby\x03\xc5\xd6\xd6\x1b\x0f#{\xe0M\xd9\xa4\xb8B\xcf\x19\xac\xba\x08#\x13\xec\"~\x0d\x9a\x19\xdcf\x0e\x1fB\x06\xd6#\xee\xb7\xc3\xdd\xa9\x03Z\xb8 \xf7j\xe0C\xab\xc4\xd6V\xb7\x94\x19\xd7&\x0bVY9O\xa6\xd4u\x1c\xcf\xc7~\xb2\x89\xceq\xa9\x82\xea\xed\xcb\x17\xc8\xb8\x0e\x1cf\xcb\x84\xce\xfc\xb6)\xa2\x8a\xb2*\xbe\xbabl\xde\xd8\xb7\xbc\xa0*f\xe0\xfa\xa93\x19a\x97\xff\xe0\x85yf~{\xc8\xdeV%)\xc4b\xb36\xca\xf26/b\xfc\xcc\xbe2B\x13\xa7d\x89\xdf\xd9\xab\\\xb5Q\xab\xfcr\xb2S\x81}\xa3.\x86#\x04\x02d_\xf2\"\x99%\x19oP\xc1\x86\xa2\xbb\x88l\x93\x94\x8c*\x98\x95y\xf6\xd5\x97Mp\xb6\xb7\x1d\xd8\x94\xc5F\xe00|\x8dM3b\x01\xab\xaf/3\xb53Q}\x9b\xf2J\x85)B\x1b\xc4KBG\xbd\xac\xa7|\xf0\xe0\x13'\x94\x19R*\xeb\xaf\xae\x0bh\xae2\xca9\x86n\xa5\xd1\xdeX\x17\xd2\xdd\x84\x8b\xd4\xaa<\xa8x\xa0\x85d\x82\x17\xc9=\xe6_C4{9\xd7\xd0c\xee*Zc0K}H\x14p\xdd\x17~1\x12 \xb2I\x05\xb2\xd5\x95/\x0f(o\xc8Q\x8d\xc3\xe92\xd7\x84\xa1#\xa98\x9a\xa1\xa3I\xf8\x96\xe2\x13\xbd\xb9'\xba\xcbS\xd9$\xcb\x1e?\xc64#O7\xb4c\xdb\xa3\x8f\xf1\xe6\xbfos\x1a\x9a\xb2Yv\x85\xffxe\x0b'\x12!\xd0`\x99/\xdd\xaa\xc3bSS\x81\x96F\x8e\xa7\xcc\xbf\xfc\xa8\x14\x7f\x9c\xc9\x97 \xd17F\x95\x08\xa2\xcd\xf3\x94\xf5\xa9\xa6\xa56z\xa2N\x0f\xeb\x95\xa4\x8d\xfa\x94\xbcQ\x0c\xd0o\xf4=\xc8\xd6\x13\x0dW\xd9\xc4V\xad\x0b'3\xfbx\xe0\x8f\xc0\xf97\xcb\xb5\xb6\xfaHhP(\x82\x0da\x16\x1e\xb2M\x05&\xe5V\xf5\xf9*X\xc2\xc7@\x15R\x8c=\x08~\x8d\x99\xccF\x1f\x15\x05Rr\x02\xa1\x84\x1f`U\x91\xaf%;\xe7\xed\xf3\xcd\xca10ZM\xca\x0e\x0d\x9dT\xd2q\xc9$\x9d\xec^\xb1\x1e\x8a_\x1a5w\x8fnK\xa2\xa1>\x11\x93\xc6\x89\x98\x18O\xc4D=\x11\x13\xc3\x89\x98\xe8'b\"O\xc4\xa4\xa1\xde\xd3\x0e\xeei\xba\x9f\x14\x05F=\xb2o@\xd7vMNI\xf1\xa5\x8f\x04\x89\xf0\x8c\x84\xf5%\xd3\xbb\x0e\xcd\x1b\xca\xe5\xd1v>\x0f@\xc6\xc9\x95\xe3\xb7\xd0e\xd8%1s\x85\xdc\x04\x85<\x1c\xb7\x18\xa9\x88B\x07\x81\xb8;\xfa\xc4\xe3\xb4n\"\x1d)\xd0\xcb>\x9f\xf2\x91\x1d\xf9U\x97\xfc\x15\x9d\xc4 \xcc\xcd=%\x8d\x11\x7f\x15\xb9T}\xe7\xc7H\xfd\x05I\x7f\x96\xfeGG\xfe\xcc\xf8J\xf3\\\x92\x10\xcf\x87\x8d4X\xa6\xabY\x92\x95\x93\xec\xaa\x0biR\xb9\x86\xe35\xc9h)\xeby)\xeaQ\xab\xe9>5\xe4)G\x03\xb2\x167\xab\x1d\x1e\xad\x14D\x9fd\x10z\xb0r\xc3Iy\x85\xeb\\z\xb2\x17\xaf\x1c\x94;\x19<_\x82\x11\x17\xab\xd7\xb4\xed\x95\\\xd9h\xfe\x94w\xf94\\\x90\xa3\xa4\\\x864\x9a\x0b\xedd\xb6\x19\xcen\xb3\xcaP\x99{\xc9b]{\xed\xa0*BGY!8m\xceA\xad\x8f\xb1\x9c\x87%\x89\xcf\xc9,))\xd7q`uhS\xc6A\xcd\xb0|\xd5\xfc%l\xfe\xacR]\xaeS\xab\x0d\"\xf1<(\xdd|\x92\\\x89\xe9\xe8\xd9\xe9P\xa3?=\xae\xed\xefLy6HPh\xc3B\xfcR\xba\xed\x0f\xa2\x07>c\xd3;\x17\xaf\xb4/\x9e^'\xbfB/\x19\xf5\xc1\x17kwg\xa7\x02\xe7\x8e\xccH\x06\xb7s\x1c\x91%\xc9b\x92EI\x95M\x01\xf1Iv\x15\xc4J\x0ee\x10\xf2\x97\xa4K\x9a\xfd\x16\xfb\xaam\x95e\x83\xa7\xb6\xda\x91e,\xfd\x19\xd5!\xb5s/\xf3\xb2LnR\xd2\x82M\xe1\x01\xa0 \xa1\x19;\x9e\x10y\xbc\xc7\x11a\x8c\xc9>\"#\xafVf\x97\x9d\x81u0\xba\x8a\x83\xe7\x92&~0\xb0\x95\x0bu\xd6\xbf\xa7\x1b\xe5\x8fw\\)e\xc0M?\n\xa5,\xb2f.\x0e\xc3k\x11\xeb\x0e#m4\xd1G\xa7\xe6\xe2N\xc5\x8e!\x133\xeeI\x10\xadH\xb9\x93\x8b\xafr.\x9f\n\x9c\xc4\xf3\xe0\xad8\x17\x80\x0dD\x9fH\xa1\xf6L\xf4\x8c\x88 \xe6\xc0\xf66/p\xd2\x87\xce3 \xe2\x06T\xb7\xc7\x8flUk\x13V\x17\x16\xf6\x1d\xdc.\x84\xb2*\xb3[g]\x1b\xc3\x86\x8e\xbbNqn83\x08\x8f\xcb\xa7\x02)\xd4\xac1`^\xf9\xe0\xc9\xaeC@\xd1 V\xa0\x80\x96}\x96\xb2Iq\xd5\x01uP\x1f:b\xc2\xdbQ\x85\xe4\xd3u\xfe\xcaG\x92\xcd\xab4\xed\x82\xaa\xeb\x82\x94\xa4\xb1}Gv5Nh\x11[\xb9\xb8\xe4A\x8fg\xad\x8d\xc3\xe5\xe1\xe2\xb2\x94\x91]\xed\xe1Wd\x8e\xe4'\x8c\x97O\x12\x88\xedg~\x1f\x12\xa1\x1e\x0f\x9e\xdb\xde\xd7\xa2{\xd4\x88\x13$Yk]\xd6\x8evC\xbc>\xf6\xa0\xd0\xdb\x0d\xd5v\x8bI\xd8\xbc\x804j\xd9\xaa\xf4;_\xcf\x87S\xe9\xdc\xa3\xa2\x99VG/\xd0\xee\xd3\xdd\xa7\n\xdd+Hw\xf7\xb51\xfe\xc6\xaaC\xdd\xad\xa6\xb9P4\xfc\xe5\x0b8\xab\xecS\x96\xdff[\xb8\x8e\x9a\xf0\x85\x04\x11w\xe9p\x19\x163B\xf1biF\xe8i\x1e\x93\xb7E\xbe8\x16\xf7\xa8n\x81\x97\x84\xfb\x10\x06I\xb6\xce?\x91?\xad\xc2\"&\xf1a\x98\xa67a\xf4 }Cp\x7f\x99\xd8-\x82W\x14\xe6\xbcU\x16\xdf\xd0zc\xef4\xa9\x8a\xb6\xdeER\x8e\xb38)\xe7}\xf8X\xecK\x87\xe6\xcb\x93|U\x92\x0fK)\x94b\xd3C\xf3\xe5e\xbe\x8a\xe6\xe3,6%\x1f\xb2\xf1\xa7\xe2K\xd7\xb6N\xca\x93|M\x1e\xd0\x1dV\xcc\xd4\xb2\x92\xde\xdd\xee\x05\x0d\x0b\xfa\x80\x86\x8f\xf2\xdb\xcc\xd40\xd67\xa0e\xa1\x82{\x94\x14$\xa2\x129\xf4u\xa2>\x1c\xaf\xe5\xe9\xf8.))\xc9\x88M\x0b;k\xe6\x960i\xc0\x03M?T\x94\xd3\x10\x8cXx\xe6\x18\xa1\x8dA\xb4\x19\xde3\xcf\x18\x18\x18\x14\xfc\xc4\nS\x97\xd83J\x95<#\x90\xfb\xc6 0}\xac\xc6[},\x06-\n/M\xca\xe36\x95j\xb9\x16]WV\x80C\x97\xa6\x18\xbc4\xec\x9c\xd5\x9d0w\xe8\x01I4\xb6\xf3\x06r\xf8\xa1v\xd5\xfc\xe4 l\x90 )\x19b\x0fg\\[\x9e\xe6\xcb%\x89]\xef\x0d\xe4\x9b\x9b^\x8d\x1d'\xf9\x95\x0fE[U\x12\xa4\xc2\x10^X7\x90\xa9!\xe3\x03W\xe9!K\xc4Fr@/\x8b\xd5`J\xbe_\xbay\xff\xed\x06\xf7\xdar`\\[\xdaI\xbc)\x84!\xbf\x19\x87\x1f\x1a7\x7f\x1d+\\lnv;\x18B\x8azR\\\xb1Ue\xe4\x9f\xa2\xfd3)\xdajG\xa0\xdc\x15\xa0\x87\xe0'O\xd8\xa6\xe6\xc1\xb3e\xc1n!\xa9\xbe\xd8Xe\x97\xfaU\xe7\xde\xee\x847\xda\x05U\xf3\xb0\xac!\xaa\x0f\x80\x14\xf1E\xbb\xbd\xaeV0\x9e7\xef4C\x98\x0cq\x0el\xab\x08\x0ce\xf5@/\xed\xd6t\xd4|\x9f\xd6Zh\xbd\xbb\xb5\xa4<`k\x81\x0e#{\x91\xa5\xe4\x18\x82\xba\x14\xcf\xdb3\x9ew\xf9-Zw,\x16y\xf6\x90\xe6,U\x0cj\xfb}\xc8\xce\xa1{\xce$6\xd9,\xd93\x8f\xb4\x08\xd7\xa4(\xc9\xe5m\xfe\x9e1\x8c\xc3\x14\x11\xaa\xe6\xf4\xe2U\xa1!m\x8e3J\x8aw$\\\x1bZE\xd7\xe6FYu\xab\xed\xba\x1a\xadp'\xfc\xa0\\&\xc93\x93g\x0f\xfe\xf10_,\xf3\x8c\x11\x03\x05\xe9]\x00\x90'l\x1b\xbf\xb4Q7\xaf\x9fU{\xc9\xc7\x10\xa6C\xea\xcf\xcd\xf5\xff\xce\xfcfa\x8f8\xc6x8{\x042 U\x95\\\xf1:\xb9\x0dd\xcc\xb1\xaah\xcb\xa4\xa33j\x14kUQ\xa1\xc2\xc9\xee6\x86\x02\xe5^M\xe3FL\xccN\xcb\xca\xac\x9b}je/\x08\x1a\xca\x1c\x86\xab\xd9\x9c\n\xd7\xe1\x9d\xb2\x02v\x8aY\xcdr\xd6\xc2&\xd4\x12\x14\x86\xdb\xe4\x14\xf5Y\xf4\xadp\x91<\x1c.\xcc\x164&n\x97S7\x94\x13\xd7_\xbe\x00 \xca\"\x1a\xa7dA2|\xbfM\xb28\xbf}\xa3O+\xdb\xef4@\x9b\xaer\x99gq\x92\xcd>\x94D\x96\x93\xfaG\xd6\x1c\x9e\x0f\xcfxh\x9c \xcbc\x82F\xfd\xfb<\x8c\x1c\xc9\xf0\xe0i\xe8(|\xab5\x8e\xd0-t\x9f\xaa\x163y\x10\x85\xd9\x87\x92\x1c\x9d\x9dT\xe0\x1b\xe7\x11\x1a\xef\x06\xc9b\xc9{\xca/'\x9f<\xb1}\n\xe6a\xf9\x96\x84tUH\x7f'\x1b{\xd6z\x94\xcc\xae\xe3\xf8\xa8\x1d\xdc\x98\xd9\xed\xef\xbekB\xcdwp8'\xd1\xa7\x92Af\x98q\x81?$%\x94\xab%[_\x1e\xc0\x89\xce \x08.IP\xc7\xe82=['E\x9ea7\xb4J\xf56N\xcf.\xc7#\xb8\x9c'%\x8f\x0f\x95\xe5\x14n\xf3\xe2\x13\x08\xa3\xbd\xf4\x0e\xa9\xce,\xcf\xb6f\x8c\xc6I\"\xde\x13\xd6\x8fh\x0ea \xbf\xf1H\xca\xbf\xf9z\xd5\xbf\xa1\xb8\xee7\x1f~K\xf30f\xff\xd1\x08\xfc7\x1f\xa3Q\xfd\xc6\x1ds\xfc\xd6\xd7\xc1\x1f\xf3\xa2\xc8oK\x98\x16\xf9\x02N\xf2\x98\x14Y\xf2\xf7\xa2\xaf\xd4\x1f\xd1^\x14\xfe\xc1\xb5\x0f\xbe\xd6\xd7%\x17\xab\xe94\xf9\x0c(D\x84L\x98\xaf\xcf\x02p\xa24\x89>9z\xbdUE\xfb7y\x9e\x920chq\x89K\x8e\xab\xc3\x16\x07\xd7@$\xa2\x9c\xb7\xb1J\xed\x1a\xa51AU#c\\dE\xedenW\x90\xb036\x0b\xd3\xd6\x874\x89HV\x92z\x9a\xe0Y\xb0\x13\xec,\x0b\x02\xee\xe1\xaa\xa4\xf9\x02~\\%i\xec\xc1\x1789\xbe\xd4\xcao7\xde}\xbb-\x9e\x8eL\xd0~@\xddS_\xbe\xf0[\x82\x0d\xd7 \xe3\x18\xe7Z\xd2\xc8\x0e\x83Z\xb9GjVA\xbfY\x91\x1c\xb5\x93g\x0el\x9a\xfc`\xa1PP\xad\xecM\xbbOF\x92e-\xae\xa0\xab\x8d\x1a\x15$\xa4\x12=\xb9N\x9c\xacM\xea\x1daP\x12z@i\x91\xdc\xac(q3\x1f\x84\xb3\xe47\x8e\xd0\xfe7\xaa\xc2\x84\x93\xcc&2\x05\x85\x9d@Mb\xae\xbdr;'\x95\xd8\x0c\xa4~\xf2\x10\xac\xc2\xef\xe6\x03^\xde\x07\xe7Y\xb0\x83\xaa\xd6\xc9\xa3!\xd3\xd6\xd1}\x90\xd2\x118aJ\xffL\xee\xf4\x90\xbayF\x8b<\x1d\x81\x13\xd1\"m\x7f?!4\x1c\xa1\xdb\x82\xb0\xfd\xf1b\x9eLY\xcd\xa8W\xcd>\xd7C\xb0\xd0:\xb6\x03\x0e\x0dW\xb3\x90&k\x82\xf3\xd3\x86\x12\xf43v\x92\xc7\xc94!\xc5\x05\x0di}\x8d\xd4\xfe\xd4bO%\xa0\x16\xad\x1b\x83\x8aS\xc43dc\x83\xaa\x90PC\xc1\xb0\xf3\xbau\xcd\xf2\x08K\x99\xb9\xaf^\x1b\xd4_2\xf7e+=\xe1j1\xbb\xdcv\xf4\xd9k\xfc\xf7t\xf7\x95\x1e\xfd\x9a\x8b\xe4w\x9f\xeb\xe5W\x98\xfe\xec{\xb3X\xbe4b\x151d\x93h\x92S\x18\x93\xdd+!\\\xa7\xe8\xb5\xf8\"\xb9I\x93l\x86\x1eu\xa6IQ\xd2\xc3y\x92\xc6\x86)_\x8b\xab\xf6\xc4\xedc\xafH\x90d%)\xe8\x8fd\x9a\x17\xc2\xb1D]\xa1q0\x91\xad\xaeB\xd4\xc58\x0dQ_\x8b?3\xe94XM\xb7Z3\xb3ob\xdcl(07+\xeaTaK\xec\x840\x8fI\xa4\xcc\xb8]\xb8\x95\xba\xdc\xee\xba\xe0\xd7\xf7\xdc\x82\xbdCk4\xafh_\xf5\xd1\x88g\x1c\x1cZ$Q\xb4\xdaA\x91s:l2\x97\xd6\x03l\x88\x1c\xae\xba\xcf\x9d\xec\x1a\xee\xdfb\xac\x1b?\xef\\\xf1;v\x12\xf0`\x9b\x08\x89-\x0eK\x0355+\xed\x1eFl\x83\x89\x8e\xe5\xab\xc4\xef\xddK\x87|P\xcfR5\xfbZ\x0cc\xfc\xe6\x0861\xa3\x15\x8b|U\xa6w\xe7d\x99\x86\x11a$?\xe3\xe3N\xc2\xe2\xd3j\xd9DS\xeb\xb6k\x8c\x9e\xf2-\xef \x05\xcfuD\xd2d\x91P\x12_\x92\xcf\x03\x0d<\xe4\x84\x11\x8571K~\xf9\xbda\xe7\xb4\xe6\"\x1c\xe8>\x17\x9e\xa7n\xe1\xeb\x14\x08\xeb\x19\x8a\xf6\x18\xe4\xe4x=\x02\xfb\xe0\xae\xf0\xde\xcf\xf3!v\xf9u(E\xd5||\xeb\x95]-\x8b<\"e\xf9\x01=\x14\x97\x03\xc4e\x0d\xeb\xae\x9d7\x90)\"\xe67\x90\xd9u\xab+\xf0\xb2\xea\xabHS\x98\x02oXm\xf5@\xa5]\x7f|z1>\xbf\xbc>98\xff\xf3\x87\xf7=j\xf6\x88u\x0b\xe9\xd8\xc7\xe7GJ\x11\x84SJ\n6\xa7}\xd1\x0d\x06\xd9\x05\x9c\x9c\xfd<\xbe\x1e\xff\xe5\xf8\xe2\xf2\xf8\xf4O=\x1d\x9a\xf2\x0eL\x85\xb8\xf6\x9f\xd4\xa3\x8b\xf1\xc0\xf9 \x1b\xf3\xf3\x18M_\x8e\xffry}xvz9>\xbd\xeci|\xf5\xe8\x8d\x9f\x8fq-N\xcf\x8e\xc6=m/\x9b\xeb0T\xc9\xe9\x9e\xf2\x9a5\xa6>\x88\x1a\xb3{\x01\x9a\xd3\x05#\x9f\xe7\x94.G\xdb\xdb\xb7\xb7\xb7\xc1\xed\xb3 /f\xdb\xbb\xaf_\xbf\xde\xfe\xcc>kd\xf3\"\xa4s{\x99W\xdb'!\x9d\xe3\x9f\x93wZ\xc9r=3\x16{\xba\xb3\xb3\xb3]\xaeg\n\x01\xfe8C\xed%u\xd5\xe8\xe9\xb5\x0d\xf6\xc9\xc5\xc1r\xc9\x10(\xfe@S\xde\x0f\x19\x0f~\x1f\x85\xe9[y>*\x94P%\x826\xaa\xbfvV\xd3\x1f\xd6N^L\xa9\xad\xb4aI\x17\xac\x8e\x1e\xdb\xdb\x8cQ\x8d=s_\xed\xbc4\xd0\xf1\x99\xfb\xf4\xc5+\xcf\xcd\xdc\x97\xdf{AR\xfe\x1c\xa6I\\\xc9\xe6\x1a\xb9CE\x19\xdee4\x7f{\x12nV\x94\xe6\x99\xd9\xaf_4'\xd1\xa7\x9b\xfc\xb3\xf9k\xb2\xc0\xf8\xfe\xa6O\xf3$\x8e\x89\xa5\xd2\"\x8c\x93\xdc\xf2\x89\xa0\xed\xa6\xe9S\xb9\xbaY$t\xd4\xd2L\xb6i \xe9\xeb\x8d\xe2\xee\x0dv\xc8\xe3\xa0H\xfc.\xc9>10\xac?`x\x04\x99\\\xb8\xce\xab\x97N\xaf\xae\xb2\xde\xcc\n\x95X]\xadR\xa9\x9f\xc8\x93\xf2\xec\x10\xe5mR\xc7\xfc\xd5\xab\x9ev\x0c\xdePZ\xed\x88Q\xf5\xb4\xf4\xba\xd1\x92\xfc\xc5\xc002\x9a\xd2\x8a\x88\x11Ch-P\x18f2\xa1\xa8\x93\x19N\xb8.\xd6\x15\x17N\xcb\xee\xf0\xb7\x82\x84\xf1Y\x96\xde\xf1\xb78)\xc3\x9b\x94\xc4\x8c\xbcb\xfd\x1f\xa1\xcb\n\xe1 \xeb\xd7|%\xc3\x83\xc6\x10\xc2o\xd8\xad\xdfX\xd2\x12h\x0e!\xa3y\x160MH\x1a\xc3mB\xe7\xf9\x8aB\x98\xc1o\xb2\xc1\xdf`\x1efqJ\x8a@\x91\x93\x16$\x8bI\x01!\xb0\x8el\xe5\xac'XC\x00\xc7\\\x90\xc7\xeb+\xe7\xf9*\x8d\xe1\x86\xc0bEY\x171\xd4\xfeo\xc22\x0e\xbd\xf7\xfd\x16\xc0\x19\x9d\x93\xe26)\x19\x99@(\x90\x84\xbd\xab\x1d\xc8\x0b\xf8M\x8e\xf8\xb7\xc0d2n\xd9~$~\xf8\xfc?\xe2\x94\x8b\xbe\xfc\xb7\x98\xf4C\xd1\x97\x7f\xd2\xb4\xcb\xd2#H\x026\xf3\xbf\xeb\xc8?\xb5\xda\x13-\xdb\x9b\x16u\xc8m|\n\xbf\xcb\x99\x11\x94q\xdb\xfc\xbf\xd3J\xb0\xe5\x08\xe95\x9b31\xa9\xdc\xff\"\xe4S\xf8\x8d[~m\x82\xf3[\xd0\x0ckh\x94]::m\x00\xa2Oq\x0b) \x18\xbc/\xf2%\x1aE\x0c\x83\xcc\xa62td\x03^6\xbe\xc8\xa4\n-%\x16\xd1\xa4\xb8b\xc74\xe7\x9a\x1c\x06\x88\x8e/\xee\xeb\xf2\x0e\xcb\xa9D\xf5\x89\x83\xe0\xcd%\xdb\x89\x0c\xfb\xc7\xba5\xedV\xdb\x99T\x99\xafP\xd5\xdeN\xde.u!\x81|zI\xd4&d\xcd\x08\xfdY\xc7\xbe\xa6.V\x9a5\xf5\xf1\xb5\x8f68(\xbc\xa8\x12\xff_\xf6\xfew\xbdm\x1cY\x18\xc4\xbf\xf7U\x94\xf9;\xa7\x0f9\xa6\x15\xc9v\x9cD\x89\xe3\xe3v\xdc\xd3\x997\x89sbg\xfa\x9d\x9f\xc6G\x0f-A\x16'\x12\xa9CRv<\x93\x9c\xeb\xd8o{\x0d{\x01\xfb\xec%\xed^\xc2>(\x00$\x08\x14H\xcaq\xf7\xf4\xec;\xfc\x90X\x04\x88?\x85B\xa1\xaaP\x7f\xc4_\"X\xf5\x8d\x15\xc4\xdf\xee\xfb\xc4\xa6=\x8d\xbd\xeb\xa7\xea\x11\xaa\x8d\x84\xd9a\xf5Z\x1f\x81|\xdd4\x06i)vVn\xc6V\xc1\xb7+$T\x94Ql\xd7/\xe4\xfd\xa9\x1c^m|M\xb3q\xb4\"\xab\xc8vJ\xf2{\xa4\xfd\x10\xce.*\xf8\x1aFI\x10?\x1c;\xd5!\xb1\x08\xe8\xfd\x12|\xa7\xe4\x18\xb7\xcc2\xfb\xe2\x1f*\xf5\x8c\xa9\xc4\xb1]\x88\xa0\xd2f\xa0\xda)cI\xa9\xd5\xa0k7Z\x95T\x15N\xab\xcb\xd26|UO\xe5\x98\xb4/b*\x90\xb3@\x92L\x96\xc8h\x18\xc4\\@\x06\x8f#\x8a\xc4M\xb6\xc1\xc1\xaa\xa7\x95<\xd0X\xf0\x0dv\x06\n\x0bd\xae\xd6\xca%\xabN\x83\xdd\xa6)\x0e\xb9\x8f\x95\x8a2q\x9f\x8e\xcc\x87\x16\x0du\x00\x8f\xb0\x0e\xfeQ\xf0}\x82\xdc*\xda\x1f\xa2\xa0Xa>9\xe5FB\x80N-\xa2\xa4\xba\x9a\xec\xdbwFZl\xb1\x9a\xcf{i\x16#\xec\xc2\xedZE\xadV\xd1z\xff)\xa1\xfb\x89\xdd!%\xb2q\xdc\xa8cjW\x84\x87\x90\xb4\x10\x15\xe1\x04\xc4\x0fg\xcf\x9aK\x08*\x00#\xcd\x8a\xf89\x06Q\xb2\x071\x03\x7f+\xab\xdc\xb3G\x91H\x99\xb9\x95\xfal\xc4\x7f\xa1\xaa\x1e\xffp\xdf\xf8\x96\xd06\xd6\xef^\xc8\xd9y\xc1\x15\x9c\xeb\x0b\xb75\x10\x7f\x132\xa6^\xb7\xd0\xea\x12\x17\x8b\x18\x81'\xab\xaca\x85\xbd\x94\xbd\xceU\xd0I\xd7=\xb7B\x1e\x12b\xf5\x10\x91\x88wUl5\xfe\xe6\xa8^%\xb6\xaa\xc40\x84Z\xfcG\xbc\x8dV\xe9\x9a\xd1T\x07\xff\xc4\x97\x9f\xd8\x9d|\xf7\x89\xdd=\xc4Z\xd17\xcb\"Tf\x1bAV\xac/M\xaa\xbdCo\x08\xdea\xdf\x11y\xd1\x1bb\xf1\xae\x9d\xba\x9bH\xf8\xa3\x80\xfd/\x9c9\xf6=4J\x08\x14u\xf7\x1f\x8d\x0e\x87\x97\x8f\xae\xc3\x0e\xe7\x87\xbaZ\x1e1\"\x96c\xa3._\xc5\x0f\xfdV\xa0\xf4q\xda.\xa0\x1c\xee\xf2\xe2\xe1&@\x11\xe0\xf0U\x8466\xea\xa3\xb7)\x87\x95\xf8\x8dQ1Y/__ D\xf4w\x05\x83S\xbd\x18\x04\x81\x06M\xff\xb0\xff\xe5p7xx\x80V\xf8J\xd3\x8a\x07 \xce\xec\xe2\x8a\xf6\x0fP\x916\x18\xec\x9a\xd7\xe6\xf2z]\xde\xab\xef\xef\x05\x9d=\xda\"BN\xec\xb1\xe4\xbf\xd6l\xcd\x04\xdfP\x8f\xccm\xb7@h\xbbJ\xdb I\x94\x1a\xcf?\xfd\x14+\xe8C\x0csQ\xa9\xb8\xe4\x82\x8ah/z*B!\x11\x014\xb3\x8e@\x92\x04fF\x8a\x8e\xf2\xf7\x0b\xd8\xed\xe3\x95\xdb6x\xe0\xf3&\x86\xc0q5\x93a\xaeB\xf0\x02^\x16x\xa0g\xffs\x87\x16p\x9d\x1fh\xeb\xed\x1a^\xa2\x0e}\xad\x03\xbd\x01\xdb\xed?\xce\xdf\xa6\xeb\xa4h\x97\xa0\xd4R\xd1\xfd\x83n\x86RH3\x94\xdeXH\xfclZ\xdaT\xd77\x89!I d\xaa\xecr\xbb\x08\xed\x8b2\xd9k\xe9\xbc\x88U\xed\xe1\xa9mc\xaf-\x94\x9cEu\x84\xd2\xeeb\xbd\xf1\x8a\xa1\x95\xa9\xea,\x87#\xea\xad\x08\xbf\x88\"\x13\xf5\xcd!\x8c\x8a\xcb\x10\"\xebB\xbb\x11 \xaf\xa51^\x07\x11\x93\x91\x03%\xdej\x03\xa5\xbe)\x07\xda\xecM \x07\xfac\x9aM$-\xe8\x8aM\xf4bH\xe3\xder@Z\xc3(\x98\xf0\x11\x15fJ\x0crH\xf2\xe6\x1e-\xaa\xba!T3\x9aH#\xf4rd\xd8\xf0\x7f\xf0\x9e\x14\xac\xaa2\xbdo9l=\xc1\x82\xa6\xd4\x97\xbf|\x02\x99\x85\xf5_\xd5\x90\x17\x84\x9b\xa2a\xd2\x80\x86\xc9e \xf0\xb0\x0b0\xcfYA\x01\xd2\x05\xc5\xc4 E1[?\xa1\xc0\xf8\xe5\x0b\xd0\x05\x870\xba\x0c\x02\x85\xb0|\xd4\xa6{\"=jy\xe3\xe4\xd8=\x0e,\xa86\x8327\xc7h,\xac7\x96\xc9\x0e\xf9\xf9\xdb\xbe1\xcc\xe5\xec\x0093\xd6\x99.\xf7I]\xc0\xee\xae\x87#\xe7\x07\xea\x86l\xc77x\xc9'\xfe`/\xa0\xb8\x90\xbd}\x9a\x0b\xe1<\x86\xee\xaf\xa9\x8f#\xbd\xff8\xba\xdd\xed\xdeT\xc1\xdeP\x928I\xa7\x8c\x16j&\xf3(\xe3\xa5h/\xccP\x1b\xc0yI_(\xbaU)^M\x0d\x84?ARZ\x06\x0e\xf6\xf8\xde\x92\xc8P\xc0\xcbC\xd8\xdbE\xd5\xc1^\xa9[(`\x08\x1bJ\x9a\x15h\xad<\x15\xd2\xc5`\xf7)y\xdd\xbao\xde\xc2b\x98\xc7\x91`\xa1${si\xb0\xe3k8\x04u\x0d]\xe9V\xeaurB\xfbR\xaf\x81q\x0e\xcb \x80\xf5\xb2 \x86,\xa8+k\xec\xdb\x89\x85\x90\xeae\xde\xc3M\x97[\x18a\xf3\xf7\x18\xaa\x8b\x05|\xdfD\x8dJ\x0fdf,\xf2\x84\xe24\xa15\xe9\xd3\x0c\xe7\xa4\xd4Ex\xb5\x8c8\xa8$\xd2yO\x1a\xf7\xaam~X\x0f\xfe\x9e\xe8w\x01\xc2\x8eK\xf4\x94\x04\xbc\xea\xec\xbe\x08\xb5\xfb\xecI a\x8c>\x83j5\xcff!4\x82\xbe\x93\xbc\xa2\xf7\xe3\xcaJ\xd3\xb2eA&1\xd2a\xe7\xb3\xde\xd5]\xc1\xde\x08u\x12\xcd\xf8b6\x9a\"\xe8\xe5\xac\xf0\xc5\x0f\x0cb\xdd\xe6\xdec\x8e^\x05\x87\xc4\xf5\x9b\xc7yo*\xe6\xa5R \x0e!\xe2EJmm\x16\xba\xc1\xa0\x00\xaam\xfc\x01n\xf2G\xfa\xc6\xff\xef\xbe\xd8\xf8\xfa\xbeG\x94\xc4\xa8\x0b\xc5\xfc\x03\x9b\xac\xb3<\xc6$\x86\xebP\xf8r\xf1\xf7mWB\xb8w\x8d\x8dk\xedX\xc5\x95H\xaabs\xab\x9e\xa7|(\x84s\xb8f\x1c%\xe84z\xda\xce\xd2u\x82~\xbcY\x9a\x16\x8e\x9c\x98\xe6~\xc6I\xce\xa3\xfc\xa3BhmB\xc0\xec`\xf3q\x15\xc4\xb0\x99{\x16&B$fuq\x8e\x01\xcb{ \x94\xfe&u\xec\xc5c\x90\xfc\x1a\x14\xf4}\xe4\xc0\x02\x02\xd9\xd4\xf3\x95\xcc\\V^\x94\xb9\xc6\xa7\xae\xdbb\xdf\xb4u\xd5\x9f\x08\x15\xaar\xd4\xeeyjg|\xd4qV\xe9(\xb9l\x99\x18\xb9\xdb\xaa\xe4w_\xeb\xb2~3\xef^\xa2E\xa1\x19(;\"yH\xc3\x12\x91\x92\xbdL\xf9\xa9l\x9cD\x96,\xe1K\x89\xb9 \x12\xf9\x13\x0fl.\x89\xc8\xdfe.fyh\xf0wE\xc6\x98\xe5\xd8EN\x14\xcd\xb5Y]B\xf0q\xdbh{\xa3\xe8!w)l\xb1:\xc6\xd0\xa8d \xcb7Q\x08\xef\x83\xc7\xa6\xbeD\x08\xefOLY_\xba8\x0e\x1e\x93.\x8e\xcf\x06OZ%\xac\x86k\x04\xce\x06Q\x97\xc0\xbc\x81]G\x19\x17\xf2\xf7\x1ce\\\xc8\xdfw\x94q\xf1\xfe\xc0Q\xb6\x82Cx\x0c\xea:\x9cH\xa2<\x05y\xfd\xbd&iV9\xd9\"\xe4\xb4w\xde\xc8D\xdf\x84\xb0\x0c1\xd1\x1bnKL\xea\x96\xfa\xd7A\x08W\x98kv\x8d\xd9\xe4\xf6\x82\x10\xc6\xfcL\xf1\xef*6\xfbV\x90\x99S\xf4\x05?\x82)\xefo\xccE\xa4\\\xfd\xeaW\x06R\xcfa\x0c/\xe1\xf69\xdc\xba\xb6*\xdf\xa6\xfe\nc_p\xa2,\xa3\xe4/\xe1\x10\xae\xfc\x1b8\x84\xbb\xd1\xede\x08\xb7!\xf0\xc1\x99Z>\xb3\xa1$\x80\xd3\xd1-\xe7\xf5\x974\x11\xe1OI\xc5\x96A\xb7TA\xa0\x18\x9a\xbdf\xbf\x17\xd0\xcfjw\xff\xa0\x9a{\xdc\xb9\xb9\x9b\x0e\xad\x1dtn\xed\xb6Ck\xbb\xed\xad\x9d\ny\xe5\xc6\xbd$\xda\x891i\xe4\x7f\x14\n\xc3\x11\x17K\x86\x80\xd9\xf5&p\x04\x13\x18\xc2i\xad\xba\xe9\xeax/\xcd\xa9\x14\xdb\xc4a^j$\x8a\x10\xbc*\xd3\xb7g\xfa^H\xd3z\x9d\x0d\xe3T\x13Sv\xa5Y\xfcW\x95\xde\x1d\xcf\xdf\xf2\xe5\xf1\x04\xed\xca\xa4-\xda\x0fQ\x1eO\x8e\xd7\xc5\x9c%E\\\xa6bpV\xff1\xcd\x96\xef\xa3,Z\xe6F\xad\xd5jA~\xfe\xbeJ V\xf4V\x19;V\x05\xaf\x97\"!1\x16\x9c\x9c\xbd\xfb\xf1\xf5\xef?~8\x1d\x1f\x7f\xbc\xf8 _\xfd\xf1\xf8\xcd\xebW\xc7\x17\xa7\xf8\x83\xbf=\xfb\xf0\xfa\xff\x7f:>\xe3\x7f\xee\xe2\xcb\xf7\xb2\xbaU\xf0\xe6\xec\xf7g\x1f/\xea\x1f\xe2\xaf\xf3\x9f\xce~\xc6O\xc6\xef\xcf\xde\x7f|\x0f\x87\x8a(|W\x81T\x86\xcf\xf5\x13\x7f\xff\xb1yE\x9f\xca\x92\xdd=\xea\xf2\x1e\xbf\x19\x04\xb5C*\x9f\xa7\xb7\xaf\xf8\xa2\xc6\x1c4\x9d|\x9e\xecm_`\xea\xf9 A\xa1\xa3\xbbE\x1aM\x87\xcdbG\xb9\x16\xdf\xd2;A\xfe\xbb\xf5\xbeH\xaf\xd3u'V\xdf\xd5\xf5\xea\xbe]\x97\x13?\xe3\x7f\xed~\xcb\x18\xa6\xf7\x1d\xc3\x04\xa3=\xaf\x05\xe2\x7f\xcb\x08\xe6\xf7\x19A\x1d\xb1#\x85\xbe\xfdg&\xfe\xaee\xd1\x9ee\x96\x92\x0bV\xa7OZ\x9e\x10nEJn\x13&\x1e\x15\xf5\x92\x8a\x1c{zJ\xacv\xcf\xa26\x89\x89c'{|\xab\x8dW\xe9j\xbd\xf2\xec+\x8c:%\xf0J\xcc0\xaa\xae\xea\xf4\xc3\x13\xc8kT\x9ab\xcaK\x17\xf9\xf1V\x19\x1b\x97\xed\x8fSD=/\xa4\x89\x98gU4\xa0?\x17}i\xc4\xd0S\x17\x97\xd8\xa6E8\xbd\x12\xe1p\x10^\x8d\x1a9\xe8o+NV\x9c\x1c\xc5\x95\x94\xcay\xdcp\xc7X\xb3!\xe2m\xd1cY\xd6XKx\xd2\xf3\xc6\xe8\xf2H\xc4,K?\xb1\x84\xae ,\xa8\xa5[#]e!\xf2RM\xe6l\x19\xd15&\"\xc2E\xb4t\xf8\xfb\x8b\x9b\xb1kV\xf8\xdel\x91\xdeR\xe1\x82d\xc4\xf4uO\xe2x/\xbf\x8d\xae\xafY\xf6\xf1\xf5\x076\xc5\xb8\xcf\x822\x85\xe0E\xe51+t\x063\xcep\x88\x1c;\xbd\x84\xdd\xf2e;\xcd\xcc\xa4\xfe\xea\xe1\x8d\xbc\x9e\x92G\x04\x7f\xf2t\x9dM\xd8P\xe5\x90\xa7\xe1\xc1n\xd8b\x08\xdem\x94%qr\xed\xa8%%\xc1!x\n\x8f\xc4\x91\xbf\x8c\xee\xe0\x8a\xc1\x1a\xddgCXEy\xce\xa6\x90\xa3y\xc5m\x94\x83\x88\x0e\x86J\x8e\x9ce7,\x83\xf7F\x95\xe4\xdf\n\x89ml*\xc2|a\x1eRQ\x9b\xb0C\x0cB\x88z\x18J\x0c\xed+~M\x10a\xafm\x00\xf2\xfb!\xc4j\xdd\x03?\xa2<\x821\x13\x97qH5\x0c\xdf\no\xa8\x1e\xdc C\x88\x88.\\$U\xa7\n\x14\xaf\xf6\xeb\x92\x04\xd6\xb8\x11c\x11X\xc3\xb9\x11\x059(\x13\xab\x91u\xd62\x84\x87\x98\xa0\x9b$Tu.\xac\x8bt\xf5L\x84zu\x11\xb3\xa4x\xedhk\xa6\xd59g\x93\x8c92\x9b\xaf\x9c&\xba\xfc\xb9\xce\xa2\xa4\x18\x8b\xf3\xdfS\x03s`\x1e\x7f\xf2I\xca\xabrp\xa6+\x96K\xfbF |\x16\x01\xac+A\xf5\xa0\xc7\x9e\xa3l.}\x15\xcd\xf7JKy\xc5\xa5 A\xc0\x16p\x04\xf3^\x9dL\x1c\x82\x87\xf2\x06\x9a_\xf2\x1d\x92\xf7\xae\x8a4\n\xfc\xa8\xcc\xf8\xba\xc6\xbbM^\x96V\xbbgEy\x9d\xf3G-:\x89\xfc\xae\x8f\x14 \x87\xb0&\xe9\x8a\xcc\xc1[\xce\xc2\x9f\xa0\x06`*\x97s\x1cs\x08M\x82\x10f\xf5\xf79\xae3\xdf<\xe8\xba\xd5y\xf2\x93r\xf2\xb3\x00\xd3\xec\x99\xf2\x9b\x83&\\\xa5\xd3\xbb\xa1ji\x1d/\xa6\\8{\x15\x15Q\xe0\xaf\x1c\x8a\xcdu\xb6\x18\x8a\xe0\xce\xbe\x87T\xe3c\xb60Y\x0e\xf5\x08\xb8\xc6\x0eD`\xd1\x94e9\xc9\x96\xf2\x07AH\xb2\xcdPR3\xe2N\xdcI\xafB\xb7\xb0\xf9[\"U\xa9\xac\xc1w\xdf\xb7\x10\xb3f\xe2\xb2\xeeH\\l\x93b\xfd\xa9a\xe7\xb0\xcb\xce\xdc\x84\x8a\xd0\xc1\x00\xd4S#lr\xfbL26eI\x11G\x8b\xbc\x9d\xc4\xa5m\xb4\xcdI\xa3\x1eb{M\xee\xb3e6\xd9{r\x83\xb4\xec=\"r~\xc7\x0d\xe4\xd6\xe9\xb4\xdb\x00\xb98\xf3D\xba:\n\xc6\xf6c\xb6hV\n;m\x8f\xb3\xb2\x8fV!\xa1h\xe5\x1b\x8a\x96\xadVt\xd8j\xc57o\xb5\x1a\xbaG\xfa\xbe\x1bO8\xc7\xefF\xf7 f\x08(z\x13g\xd81\xac\xa5\x0e\xa6!8`\xa1\xd5\x12\xc7\xd4\x10\xd6\xee\x9aj\x11\xc7\xeb,\x1e\x12V\x04\xd0\xb8\xc3\xb2\x07\xd8af\xd2U\xf5\xb4\xef\xb0t\x93\x1df'\x9c\xbe\xd7\x0e\xa2\x95\xa8\xff\xdcJ\xb5\xe7a\xb6\xd2o\xe6\xd4\xfa\xbbm\xe3\xbf\xff\xe6\xbc\xff\xf1\xb7\xd9\xe6\xfc\xa5\x8e\xbf\xeaZ\xe4\xc1x\xc7\x99C\x13%\x90\xfe\x9a\x152\xeb\x1f]+\xef\xc6\x7f.:i\xcf\x84\x824\x8d\xf2\xbds\x0c\xae\x9e\xbaR\x15 \xbdh\xbeb\x93\x96\x8a\xabrx-\x15\xa7Ho8\xe68\x96\x0e\xcbQ6\xa0+\xdc\x94W2(}\xcd\xe1\x08\xfe\xf6\x15\x9cR\xc6\x12\xdb\x93\x08AW\xb9\xae\xb7\xb8T-.\xe9\xeaw-\xec\xf9\x95\xd05dD\xa4 \xfe\x8c[4\x97\xb7p\x08\xfeJ\xc3\x07\x1f\xad\xe2\xff\xf65\xe8E\xd3)\xde\x11E\x8b\xff\xe0\xf0\x11\xd6\xfa\x82-\xa3\xdb:%\xae\xaf\xf4\xb2Y/\xce\xcf\x8e\xcf\xf7\xfc\x80\xcb\xb0\xfd\x10\xa2J\xa0\xbe\na\xd2\x13\xb1\xf7\xd9\xf4\x1cul\xbe\xc8\xac\x0cC\xa2\xee\x8c\xcfXV\x08\xeb^\xe2\xbaU\xd1-\x1c\xd5\"\xf6\x89\xa6\xb2\xaa\xa9\xdb@\\\xa6\x9f\xca\xb4\xf4\x87`\x08\xfa\x7f\xfb\x1a\x82,\x0c\xe1\x96\xb2\xe3\xe3[\xee3\x1c\xc2i\xe9\xd1\xe0;\x88\xc89\xd1\xbc\x93\xa8\xf2\xf3|\x85a\xcc+\xd9\xf2\xd1_\xf24 \xa1`\x9f\x8bG\xabE\x14'!\xfc\xee\xd1\xef\x1a\xa8\xbcw\"\x82[\xee\\\xdc\xad\x98g4\xf6y\xe7\xf6\xf6vg\x96f\xcb\x9du\xb6` ?\n\xa6\xb6b\x13\x04\xb5\xba\xa6\\\xb3z3VL\xe6\x8eY }\xfd\xec\xd8'\x18\xd6i\x08\xde*\xcd\xcd\xdb\x0c\xf5\x94d\xf5\x9c.\x97\x12\xfd\x8dc_\xe0i\xe18\xf9e\x9c\x1bt\xf3\xe2`N\xb3!\xac\xfd\xa0g\xbfw}\x9f\xaf\xd2$gD\x03V\x81\xd5\xc0\xd7\xa0\xc7\xf92\xbf\x99[\x02\x8d+\xd3,KYo\xcaO<\xf7\x92#\xf5\x97.\x91B\x1b\xfd\xe5\x0bx\xaes\x0d\xd4\x15\x88\xfc\x02;9\xd5>\xa3\xed X/\xfd\x84\x0e\xcc_\xbe@\x06G\xb0hWw\x83\xa6\xf2v\xd0Z\xe8\xa8\xd2\x86\x8e\xeaqhP\x7f\x13\x16\x85\xa0T\xe0yG\x158\x94\x8c\xc1\xd8=\x00\xa9\n\xb7\xf9zP\xdd\xfd\x03\x00\x8f\xf5\xf2\"*\xd6\xf9\x05\xfb\xec\x9a\x08\x85\xe6\x98\xaai\x03<\xaf\xacQY\xa0l\xfch\x04D\xcb\xc5r\xb7\x89\x9b]\xf5K\xec\x90\x06\xae\xf9\xa6\x0c\x00P\xfb\xc4m\xf2C\xe7\xa6\xd2\x1f%\xdbh!M*\x17\xad#}\x03\x8bL\xa4\xcd\xe6E\x99\xdc\xb9\xc2sp\xfb\x10\xbc\x10\x98H\x16%\xc2\x04\xe0\x0ft\xee\xc5\xbf\xc6S\x96O\xb2x\x85b\x9e\xfe\x91\xf6\xbe\xf6\xa9\xfeA\x93m\x92\x96k\xcb\xf6\x0e\x02\xa0|\x86\x00\xfd\xec\x7f\xf3\x18\xbd\x01\x1a\xd7^\xfd\xf6l\xab\x10\xad\xfe\x14-\x17\x82\x81s\x99\x10\x95\x19\xa7\xc8\xe8\xbb\x98k*\x15!U\xeb&\x12Y\xb3\x89\x84\x91\xbb\xb6v\xb7o\x0d\xac\xd1\xd8\x94\xdedR\xea\x89\xab\x0bk\x0c\x87\x1cM-g\xea\xc6\xc4p\xb2\x19\x91\x0fT\x13X8\xa2^\xcc\xb3\xf46\xe1\xa8\xaa\xd3\x9f 4q\xfe\xb7\xb7\xf4\x8b4\x9a2a\xc8vq\xf6\xfb\xdf\xbf9\x1d\x0b\xeb\x8bs|\xf5\xf1\xfd\xab\xe3\x0b\xfdU3^\x98\x16\xc5\xbf\x14Z\xacUh\x86Flh\xb1=\"\xb4\x11\xa5\xed\x91q\xd2s\x0e\x9e\xd9 *PrH\x16\xe9\xf5\xf5\xe2\x9b\xcc\xd1\x08\xe5\xe5}\xac\xa1\x88e\x93\x064\xf9X@\x8ep\xc9&\x96\xbf\xfcH\xcc\xcc\xd3W\xa0D\x9br\xb2m\xba\x86\x1a\xfd\xbf\x07\xf6\x97\xafK;\xadL}D\x07AG\x03\xfd<\xc3\x8bmi\xae\xcf\x92\x9b\x9aA\x7f!\xcd\x17\x95\xc9?\x92\x1b\xe4e\x95}?\xe7\xbcr\xcd\xe0\x7f\x95\xe6\xc20[\xfdz\x1bq\xc1M\xf5%\xed\xb7e1\x9e\x9e\xd6Z\x90j\xe3\xf1U:\xbd\x1b#\xf6y\xb6,e5&\xb3T\x8d/\xfe\xf4\x9enN2Vx\xbfk4\x18\xd5\x1b<\x7f\x7f\xf6\xee\xfc\xb4\xa9E\xb1\xd3\x9b\x9a\\\xd7\xe1\xc5\xc14\xfe\xe3\xf1\x87\xd7\xc7?\xbc9%\xe6,\xa06\xbe\x91\x08/\xa7\x8d-\xde\xeb\xd8\xbf\xd1\x02\x95R1\xc2\x12\x7f\xb7O\xba\xc2\x0e\x1e\x9b\xf1\xad\x84/\xecc\xb3\xbap\x85}b\xbe\x16\xee$\xfb\x8f\xcd\xf0\xa8\x0b\xe19kjK&b,\xfbf\xf5\x99\x18\xcc\xb3\xc0\xf7\xe2\x82e\x11Fv\xaaWYq\xfe\xdf\x1f]b,\x14\x8c\x9c\x91p\x8e\x1a\xe2\x04\xe4K\xdf\xf4ui\x94\xd2@Sl\xcc\xe3\xbc\xbe-*\xc8:\xdd}Q\xfa\x9a\x87\xca\xd3\xd5l>\xf7\x13\xacdFQ\xe2+u\x17\xc2U\x08c\xe1\xea\xda\xae\xe0\xc50\x10\x98 \x0b\xf3R\x9c\x94\x9e\x8e'V~Z\xf5tr;\x15148\xe4\x1a\xf2\xad\x89J\x88\x9fM\xd5\x80\x96{\x1b\xebk\xdf$\xec\x16\x12\xe9\xa7\xee\xc8\xe7\xa6\x9eMT\xa9\x9b\x8c\xa8\xfbH\xec\xbe\x08\xf3\x13\xf4P\xc4\x10\xb5\xaf\x15B\xdb\x95>K\x07 \x0e[8<\xa4n\xe3\xce\x85\xd8k\xbd?\x11\xdc\x02\x1d#\x8e?\x9f\xe0\x10NF3\xcc\xfas2\xf2\xfe\xfd\xdf\xcb\x8d\x85\xafn8>\x9d\x8cn.\xed/\x8f\xe1\x10>\xa1\xc3\xb4\x7fC\xdc|\x9d\xc1!\xdc\xc0\x11|\x86#\xb8\xf5=\x96\x14Y\xccr/\x80!\x1c\x97~\xd9\xf6g\xe8\xd4\x85\xb1&\x84~\x1f\xfb\xef\xc9\xafyoF\x82@\x8e\xf5\xefQ\x1f?\x86C\x98\xf8\xefeT6v\x0b,\x08\x02\x8c\xe5i\x86\xbc\xe2\xd5\xc7\x98\xb3\x13?\\\xf8\xe3\x10N\xe55\xb7\xb8\x93S\xa8\xa0\xdf1\x8c%\x94\"^}\x16\xc24\x08B\xf8\xcc[\xc0\xbc_\xe5\x02\xf1\x1e?\x89X \xbc\xf5s\x19i\xf4\xb8#\x95\xf9T\x05c0\xb4i8\xba\xef\xbf\x87\xadk\x0c>\x8f[}\xeb\\,\x90\x1a\xda \x0e\xed8\x08a=*\xb8\xa8z\xcc\xff:\xe5\x7fMC |\xa49\xfc\xee\x9c\xf6ObNC\\D\xbej\xb7\xbe\x9a\xa6\xe3\xaeS\xc4Y^V\xd5\x91n8*\xcbU\x1d\xc2\x19\xb1U\xe0\x9a\xdeV(\xd8_I\x1f}\xfc\xff\x84O=\xe6S\xbf\n\xe1ntuI\\\xa8\xa2\x03x\xea\xa7\xbd\xf7\xb0\x0di\xefG\xf8\x1d\x08o\xff\xf3\x00\xe9\xef\x1d\x1d\x80e\xc3(\xf7\xfa)\xb0\x95\xf8\xfb\xfb\xa8\xd5\xddJ\xfc\xc7\x83\xc0\x9dQP\xf6\xf5\x04\xb6\x0e\x1d\x829?\x80\x0f\x02\x99\x9f>\x04/\xb2ds\x10\xc9w\x86\xedDL\xf5f\x83\xdc\xc0\xb6^\xe5\\!\xefg:\x07\xdaxLG\xc9|B\xe5\x85\xe1l\xc1^\xe0[9cd\xb0\x8d\x83A\xe0{\xafO\xc7\xef?\x9c]\x9cy\xf7\x0e\xb0\x11\"g\x92\x92\x894\x84\xc2\xd2z\xbdp\xc5M\xc3P\x82\xeb\x00\x12\x0ci\x89z{\x7f\x8d\xb0\xc0\xa8\x902\xc4/\xf1\xe1\xf32 \x0e\xbc\x84\xfcy \xbf\xe3G\xc0(\xdf\xde\xbe\x14f2\xff\x1d\xfb\x0bl\xed\xcb\x97\xaa5\x1a=\xcd\xa8\xe2\x9d\x17hw\x10\xf4T\nb\x1a\xa4\x99\xb8\x8fP\x95d\xd0\xdd\xcdzq\xa1\x01u\x0bb/\xb5\x8d\x0e&\x1d\xa7GN\x06\xd3\xac\x07\x8btj\xe4$\x8a\x08\xcdy\x8ca\xe8F\xf1%\x0c\xe9\x13\xc1\x0en\xaf\x07 \xad\x97\x1e\x19\x91\xef\xab\xc3hX\xffL\x86\x88:\x82\x08\x86T\xe4\xf8\xce\xd0\xdf\xdb#\xa0\x9f\x8d\xbc\xf1x\x92fl\xe7/\xf98\x9fG\x19\x9b\x8e\xc7\xe2\xa8\xf7]e\x87\xf0\xb7\xaf\xad\x1b\xcf\x01\xd2t$r8\xfa\xa9\xd0\x9c\xfe\xedk\xd02\x1f\x17=\xbd\x9fF\x91%\xeb%\xcb\xb8\xf04\x84-\x7f\x00\xdf\x03E\x01\x94\xf7\xb4\xaa\xb7\xeb\xa8w\x9b\xc5\x85\xaa\xb3\xef\xa8\xa3\x14#\xb5\x82o\xba\xd8\xa9Z.\xb7\xef\xfe\xe3\xc0\xdf\xd2\xb5\xd4\xfc\xddA\xe0\xcbh\xbf\xe0\x89?\xbc\xa6$\x1a\xa8g\x1e\x17p\x08\xd2\xa2\xaeT\xca\x8f\xe3\xfa\xcdG\xe8>U\xf8\x98\x98L}/\xda\xb3!Rj\xe0\xc71I\xc5\x12xyXQ\xc6#b\x15%L]<\xe34M\x98\x9d\xe0\x15\x86\x18\xcc\x0d2\x91\x7f\xa0\x9a\xdb\xf6a\x19V\x8f:Feg\x04\xaf,\xfb\x19\xd4\xfb\xd1\x10z\xc3cr0\xa0\x03R=\xde\xbb\xefv++4\x05\xd3\x8fC\x88\xc4y(\x17>\xf5\x0bS&V\x0f\x1e\x05~\xe2(\x15A\xa6]\xd1\xd2\xe4\x98rx\x01}\xe1\xd7\xfeR\xb8V28\x02\xcf+\x85\x00\xbeP1\xb6\xa4\x05/\xcc\x83\x00^\xc0\xe3\xc7\xbb\xcf\x0e\x90\xbd\x83\x97\xf0\xf8`o\xf0L4\xb4\x0d\x03\xe9\xa8\xc9iKd}\xcc+\x88\x06\x0e\xf6v\xb1\xf3\x887\xf0do\x7fO\xf6/\xeacG0\xc44H\xe2m\xbe\x88'\xcc\xcfC\xec\x04s\xd5D\xb0#\x9b\xd9\xe6\xe3\xdc\x91\x83z\xf1\x02\x06\xfd\x00\xb6\xe1\xe0\xf1\xe3\xbd\x83_v\xb7\x9b\xfa\x11\xa9\xab1\xb1G\x86-3\xe9\xbeT\xd5\x98\x1a\x9c\xb5\x0c\xf1a\x9e\xc6RWs@\xebj\x06\x96ng\"\xeb\x9b\x83\x94\xca\x9a'\xffT\xd6\x10\xcf?\x955\xfa\xf3Oe\x0d>\xffT\xd6\xfcSY\xf3Oe\xcd/\xa6\xacqjj\x06duw\x18\xd1\x03\xc7\xdd\xc9\xe3\xbe\x83o\xd3\xc2\xb3w\x12DQ\xfcL\xdb$\xa5\x0d\xf9\xca\xb7Q1\xef-\xa3\xcf6\xcf J\xe2\xa4\xc3 \xe9\x18\xb0d\xb4\x19\xf2\\}8\xe2b4l\x83\n\xc2\x19\xfb\xcc\x88\xc9\x0f\x1b\xac\x8f\x9e\xc8#4\xb2\x96\xc4\xb9\x9e1c%_\xbf\xceOK\xb9/,\xd27\xe9$Z0)\x1b\x95)Qpo\x9c\xcd\xbc^\xbeZ\xc4\x85\xef\x85\xde\x86\xec\xfb\xde\xde\xaf\xa2Dq\x04\xad\xdd\xa5\x95i\xc8o\xe5+6A\xfa}\x8f\x15\x95\xea\xb2H.hk\xca\x14\xcd\x13,\xc2CH\xfd\x16Q\x923?\nF\xf1e \x13\xef\xa4z\x92\xf3\xeeh-b\x17\x87J)h\xddR\n^v\xff\x89 \xab\\nL\x07/{`\xf2\xc4\x13Zs\xc2Y\xd9\x89\xca\xcdl\xb3\xb0\x93^\xce\x8a\xd7\xcb%\x9b\xc6Q\xc1l~u\xd2\x9b,X\x949j\xcc\xb1\xc6[a4\x7f2\x8f\x92\x84\x19~\x867X\xe3U\x9c\xaf\xa2bb\x98},m\xe5\xe55\x11\xca\xe7\xae\xed@CA\x1e\x0ea\x9b\x9fe6I\xe6'\xcf\xb5\x99:\x85\xce\x90\x01\x9a\xe1\xc5\xb5\x93\x9b\x95A\xd2x\x85\x10\n\x9f\xf0 \xa8\xbd1\xa6s\xd5\xcad\xdf\xc9\\ \xc2Q\xa5\xdeV5\"<\x96\xa7(D\xae\x1a\x9b\xac\xa5\xfd\x18]\n\xad\xed\xe09D\xd95n\xed\xbcR\xec&\xcf\x03\x95C\xa3,\x1d%\xdb\xdb\xe6I'\xf7\xcf\xf5h{{y\xd9\xb6\xd0\x02(\x7f\xe5\x0c&_\x87\x9b^\x92\xde\xb6\xb6\x86\xb5\x9c\x0d\xcd\xe1H(\x13|$\x93\xec\x16\xe6A\x8f\xd3\xbd\xdd\x10R\xfcc\xd0K\x93*\xb4\xf9\x95\x08T\x1f\xf9qo\x95\xe6\x85\xdc\x85Hk\x06\x18\xcfi\xd2\x8b\xa6\xd3\xd3\x1b\x96\x14o\xe2\xbc` C\x9aN.\x86\xd6\x00r{\x93^\xbc\xe4=\x9e\xa3\x17P\xceG\xd6<\xb5\x89>\x06<@=/\x04\xefw\xf54\x07\xf6\x88|ON\xc8C\xaejK\x8c\x1c]\xa5\xd2$c\xd1\xf4\x0e\x03\xee\x89p|(]/|O\xf8&a\xaa\x15\xf7\x88\xf2^\xb4Z\xb1d\x8a\xf9\xe8}\xed\xab\xa0g\xb7\xdc\x86\xc3y/c\xcb\xf4\x86\x89\xc6\x90g\x0e\xcb}\xea\xf4\x1c\x80\xa6\xcc\x959+.\xe2%K\xd7\x85\x86\x11\x9c\xe9\xa8\xbe\x0f\xeaF\xb3\xd6\xf7V\xa4Y\xa4\xd5C\x98VM\xe0_]\xb9\x15\xf7`\x1b\x9doh:\x8a\xeaF\x9a\x1f\xbf\x19\x02k'\x9b]\x1cv\xdc]\x13\"\x1f\xc8\xae\xdb:n\x81\xde\xa6\xec\xce\x13:D\xff\xe0I{V3G\x9e\x8f\x0cie\xea\x17vj8\x91\x90\xa8-\xb5q\xdc\x9b\xb9\xb2\xfe\xfa\xfd\x10\x92^\xc6\xf2tq\xc3\x02\x8cl\x8f\xa9\xfc\x96\xb1\x96\xdfjC\xc0X\x10\x10\x80yF+\x01\x91\x0dDg\x86v&\x90\xe2\x00\xe9|\xf3\x98\xc7\x8f\xcb\xc9Z\xdaT\x91wF\xb2x[[\x9c\xc9\xf3>\xb0\xeb\xd3\xcf+\xa4\x8di-%\xe6\x86s\xb6\xf8<\x95\xb0\x81\x9c\xf3\xe3{\xe1\x82ZN?\xed\xc9\xab7\x11\x9aA^\\\x89w\x9cK\xb10>\"\xc2\"F\xd2A\xc0O\xf0\x161\xeb\x9d\xa3C(\x17ac\xb7\x05\x00\x88l\x9e\xb6\nA&\x8c\xf1B\x88\xee\x0d\xc4g\xae\xdb\x84Zf\x97Nr\xa9\xa6\xeb\xc9\xea\xc9\xc57\x1a\xd1\xee\x9eC\xa69\xd8Cyc\x12\x15\xbe'\xf8)O0\x1dB\xc2\xab\x875\x9e\xd5\xeez5\xbe\xf4]\xb4d\xbf\x8e\x9c\xbdk\"\xa2\xdc\x934~Z\xe6\x0fR\x9aylj\xce\x854c\xdd\x9eKaf\xcf\x14Z\x16.@\xbc\x92\x0e\xc8\xba\xe4&\xe0&lS\x8e`\x01- peF$\xcc\x98'\xae\xf9\"\xbf\x90\xda\xb7\xd2\xccL|`\x1eH_\xad\xaedN\xa5\x92\xf4\xa6\xfeV\xd6\x9bii\xfdB`\xa3\xe2\xb2m\xc5\xcc\xe5Jp\xa7\x96\xb1C\x1el;\xa8D\xae\xf8\xc9\xa5\xe0\x8a-~\xa6\x13R\xb9Y\x94\xd2\xdd3\xf1\x1f\xef\x99\x18Ty\xeb\xd4\xfdr\xbat\xd9v\xed\xf4\xec\x80\xde\xa4O\xcc\xf7\xb1c3\x08\xf4\xb6\xac=\xe4\xbd\x93\x95tGS\x94Ey\x1e_;\xd4Q[\xb8\xb5[L\xaa\x944KE\xb4-\x1c\xef9\x92\x9c\xdf-\xaf\xd2\x05\x15[\x06\xb9\xe9\xe8j2e\xb3\xeby\xfc\x97O\x8be\x92\xae\xfe+\xcb\x0b\x8f<)e:\xd1'!dJ\xbf\xe4\x05\xbdY\x9a\x9dF\xad\xd1\x1a\nq\x86\x18\x0e\xadA(,\xc4r\xe1l\x1b\xf0\x0e\xca\xf3I\xdc\x95\x89\xa2\"\x08d\x98L\x0f\x93\xeeVn\x16_\xeb\xcc~\x9b\xd7\\\x84{\x9e\xc3\xdc\x94rC\xa49\x83PFK\x9f\x85\xa8!\x89{\xb3\xe7\x90\xc3KX<\xb7\xf9\xd2\xb2\xe5\x95\x90=\xd7\x9ap\xbc\xe0\xc2q(\x14!\\\xfe\xf3\xa7\xe510\xf1\xa7B\x98\xf1\xa7A\x88\x8a\x90y9\x86\xa5H\xc2u\x03/a\xf9<\x00I&\xa6!\xead\xe6\xa3eiQ\x95\x8cV\xa8S\x1f\xad\x1c2\xb8\x96a\x0d\x86\xdd\xb2J\xb5\xed\x9eA\x9f\xe6\xd7\x06\xa6nI\xec\x9e\xdd\x03j\xf7\xf8\xbc\xe0\x80s\x8f\xfe`\xf7 \xa8\xd9{<\xc5\xd7\x8f\xf7\x1e\x93)\x1a\xd6\xd4\x98\xa1t\xd7\xcc\xd2U\xae\xb9\xfdV)\xd4\x95_o\xc6f\xb9\xcc\xe2\xc7\x7f\n\xafh\x9c\x19\xea\xef5Jc\xf7\x9d\xff\x1d\xfb^\xd4\xdd\xa8\xd7\x9aof\x9c\x7f`\xd1\xa4\xd0\xf3\x10\xf2\xed\xa2W\xc9e>\xfd6\x9e\xb1\x8c\x85e\xe4\x82wg\x89\xc7\xbc\xbe[\x87e\xca\xf8\xa7\x8f\xbd\xa0>\xbf\x9e\x91\xd3\xbf\xbc\xaf\x0ceD\x05\xa2\xae\xcab\xafR\xb7\x85\xe0\xa9)\xd4u\x06\xfa$gi6a\x1f\xed\x00\x01\xe4j\x19\x1d\xfeX}\xab\x04x\xd6qp,\x04O\xeb\xba>\xbeE-\xab\xf1Z\xcfj\x9c\xd7\xf3#\xb3[X\xd4^\x1a)\x97s.\xd3\xe5z\x03ZkA\xfd\xcb8\x7f\xbf\xce\x98\x85\x15[\xfd&\x95AY\xd3r\xe5\xe2\x8di\xa5\xb9\x86\xa8p_\x82\x92\xf8\xcf\x02\x9b\xbc\x18\x0bc\xf5l\xfe\x90\xae\xafa\x861\x0c\xba\xfe\x07\x91\xcb\x13q\xb5k\x1fjk\x10\xf5+X;nb\xee\xbf\x04\n\xe8z\xc2\xb0\x07n\x9aT'\n^\x84\xef.\xf1\x17\xdf\xb8\xf5_\xbe\x97q\xdc\xed1q\xaf\xe4\xa1\xc9\xf0A\x7f\xd0\xdf\xfb\xc5F\x9a\xf8\x8f\xf7\xefm\x9d\x86\xe2\xd6\xd6`C\xd6\x98\x1eP\xed\x82\xf0\xfc\xf4\xe4\xc3\xe9\xc5\xf8\xd5\xd9\xf8\xdd\xd9\xc5\xf8\xfd\xf1\xf9\xf9\xf8\xe2\xa7\xd7\xe7\xe3\xb3\x0f\xe3?\x9d}\x1c\xff\xfc\xfa\xcd\x9b\xf1\x0f\xa7\xe3\x1f_\x7f8}\xf5\x0d\xees\x0f\xe65O\xc1u\xd7\x12\x0f\xa51\xe0\x01\xed\x92\xf7\xd82\xd0\x92v^\x074\xc3\xbd\xfb\xe4q\xdd^\xf4\xc9\xbe\xfe\xbb\x87)\x13=\x91k\xfe\xbcH3\xe65\x98}\xaa\x05\xed]i\xb3\n\xabV\xd2\xe5U\x9c\xb0\x0fl\xba\x9e\xa0\xd7gkKi\xcd\xdb\xa0j\xe9*N\xa6\"\x8c\xd0 \x1fY\xda\xa9\xb1\xd8\xd1X\xb4Z-\xee\xde\xc6\xd3\xe9\x82\xddF\x9d&\x189Z\x9ap2\x9fwia\xbd\xb1\x1b\x85\xe3 Ps\xe8\xd0g\\\x1bs\xd1\xd3o\xcb\x80\xc9|\xb0V\xf46\x8e\x8aFJO\x92.a\xf4\xb3\xda\xad/\xe7\xb1\x11\xf9\xc4\xb5\x98(38m-\x15\xf1\x16\xff\x88:\x9f0\xa5/\xc5BED*\xe5\xd3\xcf+\x8c\xf9\x00\xc5\x9c\x01K\xe6Q2a\x19\x14)\\1\x88\xca\xe9\xf6\xa8\xe8\x8ajq}\x16\x08C\xd9Z\x0d[+A\x8e\xa9h\x1bS&\xb0\xbf}H72\x99/\xa1g\xc6{j\xfb\xf5\x84pM\xe1\xef\xf1\x9e\xda~\xbd\x92\xa7W\xad\xa0D\x88)\xa9\x8e\x9c\xe1\xda\x8a\x1c(\xe2\xfa[X\xc6\x06&\xb0\xe8F\xe7MVS\x8bNM\xdc\xd0L\x8csAX\xd3\x82,\xd4\xe5]\xebj\x80v}M\xa5O\x95s\x98\xfaA\x08\xb32\x9a\x8dU\x0d\xb4\xa94\xda(\x8a\xd4\xdb\x0d\x15@\xea,\xb6\x06!\xef\xd5\x1e\x91\xfe(\xd9}&\xb23\x9f\xd9W\x14\xe63C\xfd\xc4\x84\xf9I\x08\x03\xda\x8a\x0b\xac]A\xbfu\xad\xe4\xd2\xbd\x92[Y/B;\x02k\xe9d\xf08X\xae\xf3\x82/\x19\xc6\xe2\x05!x\xe5=\xf8\x983\x98\xac\xf3\"]\xc2\xb2\xa4\xe8\xa8e\x88\xf2\xbbd\x02\x91\xf8\x9c\\^#-:\xeb\xa1l`\x0d\xe1\xdf\xca!Dw\x98\xb2}\x1e\xdd0\x88\x12(\x83\x1d\x83\x87jiPvG=\xf8\x89W\xb9K\xd7\xb0\x8c\xf3|\xc5\x16\x0b6\x85\x08PD\x89\x92\xe2\xe8\xdf\x1c\xa3Y\x11\x00P\xa7g\xd9\xfdT\x1a\x804\xce\xcd\x1dFs%E\x1bNSr\x7fA\x9a\xc2~\x85Y\x9cD\x8bEc\x1b\x03\xfb3\x9b|\xe8\xf6\x12\x9c\\\xcd\xc4\xd9 \x93\xa6k\x89\xe1\xb7\xb7]\xc8\x7f#3\xb6\x17\xa3\xc4aD\x92\xb6^\x80\x82\xa6\x92\xfb\xce]m\xe9\x0c\xc8\x15\xf7^\xbf{}Q\xff\x94V\"\xadI\xc3L\xb5hd\xec\xf1|}\x95O\xb2\xf8\x8a\x91\x11\x96\xafKq\x87\n\xf5\"\xe4'\x89$m\x92\x1f\xdc\x9bp\xf2\x93,a\x9f\x8b\x0f]O3\xf5H\x1d\x0f\x05Y\xf58!\xac\x1e*Th})BX\x8f\xd2^\xd4j?sS\xf9)\x11I\xacu+Fz\xb8\xdaJ\xb5C\x1a\x14\xb4 5\x91\x0e\xeb\x8b\xbb\x15\xa3\xe0\x9d^\xc9t\x89\x12\xd8\x8a\xec!\xac\x9d=\x96\xe4\xb6\xddJ\x9f\x95\xf6\xd4\xe2/\x7fn\x9e\xeb\xfaC\x93~@)\xa2\xe1pQ\xa2Ma9\xc3\xeaO\xa3\x0d\x82z\xd6\x89\x06\x7f;l\x90z\xba\x9cQ\xf8&\xe8\x843P\x0d\xcf\xf2&\x01\x81|\xcc\xc2\xc6\xf2\x05\x11)\x87\x0b]\xb4K\xecc\xeb\x0e0&Q\x91\xef\x94!x\xff\xfe\xef\x9c\xb9\xfc\xfc\x88\xff\xac\x07\x93\xff\x06\x89Z\x17\xf1\x1d~i\xd6\x9d\x8d\x14E\x1f\x9bWB\\\x1a(o\xc7\x84\xd8|I\x84\xc2Qfk.\x9f\x87\x9cp\xfa\xad\xd7\x10\x1eh\xa5Mo\xad\x8c\x1f;\xb9a\xb3X\xaf!\x92\xb9\xe2\xb5\x81\xe8\xa6v\xc1\x1c5\xea4\x90{\x89\x91{\x01\xcc\xd7\x8a\x7fm\xa1hS*\xdal^\xbc\xc0\x1b\x93\xc8b\xcbxs\xa8$\xe6\x1cIQ5\xd1\xb7\x9bH\x90\x1d\x17\x8e\x07a\xcd:\xda\xb3mY\xc8\xa3\xca-\xd7%\xba+2\xbe\x91\xf0I\x02^uV\xa1\xf7\x83 \xda\xe3~\xd0\x8bzB\xa3e\x82~cm\xd5\xa6\xf5\x9dkm.u\xc9\xcc0\xf2.\xacP\x97\xc7x_\xa6q9exIq\x19\xa8Y\x83^\xda\x8b/xQ\xc5\x18\x95\x08\xd0|\xda\xd0\xac\x8d\xdd\xf8\x80n\xbc\x18\xf5/I\x04)zBz\xf5k\xb0l\x18AWB\xca\xfc\xa2\x87j\x18\xc9\x80\x87\x15T\x88\x13\xc88\xec\x1fDq\xf8`J\xbc\x10\n\x15\x00\xb9\x8b\xf2S\\\x10\xd5(\xb7&}\xc0\x11xq\x12\x17q\xb4\x107P\n,*\xabr\x91\x82\xae\x9b\x83!\xa6\x1c\xbf\x89\xd3u.\xd3)gl\xc2\xe2\x1b6\x85\xab;]\xffP\x8b\xec\xaakM\xcb\xd1w\x81e\xb5g\x9f8\x9cQ-\xdb{y\xb1i\x1e\x19\xca\x84\x9frG\x1d\xc0#\xd3\x98]\xb8Q\x1cA=b\x02\xe5\x90\x86r\x0d\x1cA^\x1e\x07e\xc5j\xf5)}5GJ\x8a\xba\x13y\x06\n\x97Q \xaf\x1f\xfb5\xcb\x95\x82KXh\xc3kW\x8d\xf4\xaa\x0bL\xee!\xe8y\xc0\x17\xd6\xa3i~A4\xa6\x08z_\x18\x9fp\x1c\xe3@,\xf8\xaf\x9d5\xc7\xaa\x9d>G\x96d\xb3\xadS\xed{\xa7\xbd\x9c\x96\x0f\xa8\x84\x0e\x9e>\xe2\x08\x92\xb6t\x87\xa5G\x1f\xbe\xae\x0f^_\x0cm\x80Ay\xb6%\xfe\x9e2\xf0\xde\xdc\xfc\xb6\xcd\xbcag l\xbf\xe5\xa9\x8b\xb6\xf4}\x18j\xb1\x01\xd2\x92\xb0g\xc1s\xd8\xde\xe64={\x1e@*\xe8y\xe1\xb3Qr\x89\xcaT\x87\x1dh\xba\x19\xd4\xb5\x83\xf1\xc9A\xe0{E\xfaq\xb5b\xd9I\x943\x97\x15'}Hv\x02\x0eqA\xaf\x06\xb0C\xd8\x1c\x8bh\x97\x94\xaf\x7f\x81>_\"%\xc6!\xec\x14\xf0\x12R \xcb\x14\xb6\xd1h\x0b]\x81\x12Y\x90r|\x0c\xca\x8f\x12\xd8>\x844\x10\xe0\xe6\x1f'\xf2\xe3\x04v\xf8\xef\x97/1v7\xff\xe3\xd0\xcczU.h\\.U\x8aK\x95\xc1\x0bH\x9f\x07\x10\x8f2\xb4\xa5\x19e|$\xf4a\x17\xb7\xac\x92\xb9D|.\xc2\xc2\xd5\xf7F\x7f\xfe\xf3z\xb7\xdf\x9f\xfe\xf9\xcf\xeb\xe9\xd3~\x7f\x87\xff?\x9b\xcd\xfe\xfc\xe7u\x7fO\xfc\xec\xef\x1d\xf0\x9f3\xb6\x8b?glw\x86\xdfL\xf1\xe7n\x7f&J\xfbL\xfc7\xbb\xdc\xdc`W\xce#\xe9\x15,/\xdaM\xcf\xbabG\x08\x19\x85 \xa9\x03A\xe2\x86\xbdD\xac\x1a\xdee\xc6\x12\x03\xf8\nmo\xa7\x97\xb8v)\xbc\x80\xf8y h\x9e\xcfw\xd7(\xbdD\x0f0\xc76\xdb\x90\xb8U\xdbl\xf0\x9420\xae\x84\xf1J\xcdA\xc6\xd7\x8fI\"\xe3\xd6\xb3\xa0\xe1\x9a4\x04)\x9c\xf6\"\x05\xad\"H\x89[\x83\xa4M\x84US-\x99,ZQ-v\xde\x11(\xdeLXldhx5\xea\x13\xa6\xcf\xa0\xd6[\x04*\xb7\xc5{<\x0f\xb9\xec\xe5\xa7\xd5A\x17c\x1eHs\" \xc7)r`\xd7\x07`\xd7,q]e\x00\x88{9o\x14/\xb4\xbe|A'\xc1\xdaG_i\x94)\xbfO\xd8\xad\x1f\xf7N\xf0\x17\x97\xe38\x0bo\xe0\x13\x7fT\x15\xcc\x8e\xa0\xef\x9ax3\x94\xb3ng\x05\xfbd\x19\xf5\xc6\xba\x04}\x9c\xdf%\x13%,\x9b\x82tM\xd6vUZ\xeb\x95~\xcf\x12\x116\xc0U;\xd7k\xbf\xcf\xd2\xcfw\x97\x8e\xab\xf7\x16\xf9\x18\xad\xff\xdb\xc4\xe1\xcc\xe5F\x81\\\x0c:\x95\xe2_\xeb\xf2\xaf\xb8\xfc\xab\xcd\xc8\x86\xa2\xdd\xb6\xd6\xa1\xc52\xb8y\x92\xa5i\x17\xb5\x01\xdd\xeax\x0d\x11m\xff'\xfe\xb4d\x86jmY\xf8\x8fm\xd2\xecWj\x11\xf4\xd4\x10\x1b\xa2\xfa\xa0\x1f\xf8\x89\x7f\xb0\xff$\xd8\x88{ih\xd0\xdc%b\xf3\xec?i92\xcbKo\x19\xfa\xc8q\x80\nv\x15\xad\x0c\x95.\x06\x8a\x92h\xab\xa2-\xe53\xb4\x95\xfa\x89\xf0kV\xf4\x1c#\x02&h\xae\xaa\xf7\xc7x\x97m\xa7r\xc3\xacim\xdc\xee3\xda0\xe4\xc0\xca2\x14\xa1\xb1n\xed\x15\xa7\x07\xbbm\xd8\xae\xd8\x80<\x84E\x08\x13\x8a\x19@g\x02\xf8\x9e\x0c \xaf1\x8cv\xa9\xc8\xa8Dq\x07x\x1f\xc6\x019E \xfb3@\x1f\xdd\x97\xb0j&%\xc2\x8f\x9a\x9f0\x94nm\xce[\x11\xc5\x9a\xe85\xc7%\xb6\xdb\xbaq\xf08Kq\x87f\xbd\xbf\x96`\xe0\x12\x17?\xb63B\xf4\x04\xc5\xf9\xa0\xbb\xb8\xa0N\"!k!dE\xce\xfb\xdc\xc0\x0bX=w\x1d\xe5\x98\xa7\x96\x8c\xef\x02\xd2)\xba\x18\xdd\x10we\x1c\x00y\x80M\x8c\xf9\ns)\xd9\xbf\n\xe1\x0eC\x1d\x15\x88\xa1\x13\xcc\xca\xe8\x8b8F7\"\x9d\x13\x7fK\xb7\xa6\x99r\x8c]*\x1f^o\x1c`\xea\x9a8Y;\x92\x0c.\x0d\xcb:\xfd\xb9\xcaX\xf4\xc9*\xb1I!:\xa77\x8db\x0b\xa5\xf1V]V\xed\x93\xd8\xbf\xc6j\x9cA\xbd\x13\x9a\x1a\xbe\xfb\x17\xd2\xcdTl\x8bIP\xe1\xd2\xb50\x06p&\xbdl\xea\xb1 \n\xe0\x84\x04\x90\xd0\xf8*\xe2\xa7\xc4\x18+\x86/\xd0\x15\xee\xa3\x85\\\xdar\xe0\x8e\xe1|\xeb\x82\x90\x87\xc8\xa4'<\xcaQCZ\xfe(\xeaN\xe9\xf8\xd7\xbd\x84\x95o\x92\xf35\xc9\x9e\xc4\xac\x9a\x98\xefT\xcc\x97\x84\xa9e>N2\xbf\xf7$\xe8}\x8c\x93\xe2)\x8a\xb1\x0fr^\xee>\xa3B\x80r\xb1\x87\xbe\xc79\xd8\xbf\xaf\xe8)\xe2\xa5~\x93/\xddSz\xac\xbb\xedcr\xeb2b\xa1\xa5q(g\xf8l\x8e0\xf4_\xe6\xc7!$\x1dp\xa4D8x\xfc8\xf03\xc7\xd6M7\xebc\xd0\xa7\xa3RqN\xcd\xbf\n!'&v\x0d\x870\xf2X\x96\xa5\x99\x17\x827Y\x08\x7f5o\xca\xf2\"K\xef0\xb0N\xb4\x16\xef2\x96\xaf\x97\xcc\xbbt\xb9\x08\xdd9\x11&\x06y\x1b\xc3a\x88\xde\xe0ROf\xce\x154\x1aU\xe8F\x86\xb1]\x0f\xbd\xc9\xc5\xed\xd3\xdbt\xca\x9b\xdc\xdab\xda\x0b\x19Z\xd9\xb7\xeb\x99o\xbe|\xc1O3\xb9\x7f\xce\xca\x12\xc7\x1d\xa40r\x98\xc7\xd7\xf3\x9f\xa3\x82eo\xa3\xec\x93\xbd& id\xd5\xeeO\xed\x1f\xac\x89\xd1\x1d\xc1\xe0\x00\x8608\xd8{\xba\xef\x80Bm(\xfc,\xe0S\x12'\xa42\xa5\x10\xb0\x88\xaa\x82(\x90\xd9c\xd6!\xdd\x08\xc6\xfb\x9d-\xd24\xf3\xedr\x15\x96@\x08\x8a \\\xeeo\xca\x84\xed\x18\xe4R\xcb\xd8\x1e\x8b<\xe9\x9c\x8f\xd5_\x9d\xa4k\xf4\xa5W\xf5f\x8b\xf4V\xa4\x1a\xd7j\xb2D\xa4\xc8/\xf3\xb5\xb3d*\xe8W\xed-\x87\xb2\xf8\xb6|\x85.>\xc2\x9d\x05\x7f'\x8cM\x15\x91\xac5(Z\xa3\x8a\xd4\xda\x89 \x8aF\xfbbC\x9cO\xe6l\xba^\xd4G#\xf7\x8f\xf9\x12-\xe9N\x93I*\x87\xca\xacw\\\xae^\x17\xb3\xa7*\xe3|t\x1b\xc5\xc5\xab,\x8a\x13\x0dNr\xaeo\xd3\x8c\xd5\xdb\x9f\xa4S\x96\x99\xe0+{\x13oY\xf5\x8a\xa3\xc4\x1c/\xb2\xe6\x92\x82<\x0bzBE\xf1J\xb4\x15\xd8M\xb3[\x98\xfbU#\x81\xdd\x8fVX\xc3W\x97\xe7\xd7\x95\xdb\xf3\xcb\xa4\x1c[\x88\x8b:e\xb8\xaa8\x08>\xb4+\xd2\x95\x0dG8\xce\x8c\x03\x92\xd7\x17DK\x04\xa9\xa8\xad\xb8\n\xf1 \x14\"4\x03\xcc\xebV4\x06\xdb/w|\x10\xba\xd8f\x89\x1b\xda\x87\xea\xcdaU\x1a`\x14\nW\xdcx\x07 \xc7\xd5m\\\x16B\xeab\xe9%\x17\xc1\x0c\x88\xd8`\xabL\xcd\xe1\x08\xfc\xc8\xd8c\x9d\xf8\x04\xd4\x8d\x8b=\xac\xd6\xc9\xee\xa7\xaa(\xf1\xcc\xd5\x1ah\x9c{Y\x99\xb7\xde\xe4b\"\x94\x01\x8a*!\xd4%\xddRy\xd3\xc2*\xb1\xd06o\xb8N}aX\xb1\x91d'\xf6\xed\n\xa0\xb9xI\xb9\xfa!\x9c\x93\x97\xf7\x1ct\x11\x86.\xf2\x91f#\xbew\x82+B\x81\x9es&\xa2\xe4,zq.\xd8'?\x13\xce\x07\xfa\xb6A\xcd%e\xbb\nztn\xa5*1NKa\xa8W\xf7Mz\x9d\xdcD\x8bx\nI\x9a\xec\x88f\x1f\xc9\xc3a2_'\x9f<39\x9dz\xf0\xb8wLDnk\x02n\x11F\xb0\n!F\xe1\x93\x13p\xbf\xe4bb\xcc\xc7c\x0cY\x1a\x9c\x96\xf1\x97\xfb\x1c\xa3]\xf37?&\x93\xc5qi\x16\xb3\x0bi6\xc7\x1c6\xcdv\xde\xc6\xdc\x16\xbdY\x96.i\xdc\xc0 f\xfc\x94\xd6\x8f<{\xbe\x9aC\x9e\xe0({\xeb$\x9f\xc7\xb3\xc2\x0f \x9a\x15,\x03\x96L\x81\xdd`\xf0\x8f\x00s80\xb48\x10!\xfa\x10X\x02U\xbb\xb4\x8d[F5|z\xf6\xa3h\xd2\"\x0eQyd`nK\x0em\x8c\x0bXn\xda\xdb,\x96\x97{&\xb4\xa5\x8e\xaeJ\xf5\xa5\x8fw\xc0{\xfbT\xed\x9bz\x99\x0ci\x8c\xe9\x9ej\x03\xa2\xb0\xcfT,\xb6\xad\xd5\x16\x93`\xe2$\x84\xd5\xb9 \xdc$r\xc0/L\xe6\xb0b\xba\x98\x93\x8e|\xf5\xcd\xf8\xe3\x0e\x1a\x7f\xab\xd1xj\xc0E\xc9E}\xff=\xd4\xddEp)\n\xc1\x16\x1d\xf1)\x88\xb5\x9eFE\xc4\x97\x1ac s\xa0\xf9}\xb1\xa6\x1d\x89\xa2@\xd2\x92\xa6*\xe4Kx\x1b\x14\xa5\xad\x01\xee\xfb\xef\x914\x06\xa1XT3\x10d\xed\x17\xed\x94q\xa5\x87q\xf2J\xc6\xeb\xdb\x93\x9f\xea\nc\x82\x7fP\x01\xad\xea\xaf+\xce\xcf^bB\n\xae\x8d\xc7\x89\x80\x8e\xee\xfd\xc6\xfe\xf9 \xdf\xee,\x13\x82\x06\xbf^\xc5\x88,\xd5\xdf\xf5\n\xe3u\xa2\xd7)\x7f\x19\xb5\xaa:\xad\x87\x99\x90\x06\x10;\xd6\x8b\x05G\x10+\xccw\xbdq^\xb7K\xc37\"EE\x06\xe4\xf29\xc9AVG\xf4\x04\xcfoC{Th1\xdb|\xa4kxld&7/r\x15eu\x86\x9b\xa1;\xa1 \xfb\xc2\xba\x07U\xac\x9e\xf4\n\xc3\xa0\xa9\xe3*\x1c\x1a\x126;\xfcH\x1d&r\xcf\xb5\x9e\xe4\x97/_\xc2\xa0\xf6k\xb7\xf6k\xbf\xf6\xebi\xfd\xbb\x83\x10\xd8\xf6v`:]\x83\xe0\xb6\x03T>\xbd\xa8q\x17\x0c\xe7\xab\xa0\xa9\xcf\xbc\xb04\x06\xfd\x10\xfa\x1dc\xdb\x9c\xd3PPW*\xed\xc2\x97\xdd;\x97\xf3-e\x05\xc7\xfa\xa9\xef\xf1\xd7\xea\x9d\x17V\x8b\x1eP\xdfH\x9d\x88\xe2\x04\xd2*\xf5\xc6 \xba\xa3\x0d\xe1\xa4f\xe6\x02\x0d\xf3<\xa1\xe7)\x87\x04j\x92\x9e\xc8\xb0\x80\x0c\x87\xfe\xee\xc2N\xea@\xf7\xf3\xc9}\x82\xd4\xf4!\xc8\x82\x9b\x1a\x92~\xa8O\xf2X\x10\xd6\x8e\x13\xbb\xca!\x864\"\x01\x0bXV\x9c\x16\x17\x10\xce\x9c\xab\\\xeaK8x\x8bx\xf2\x89\x1ag\xa7>\xde\xb7\xaf\xb0\xc2v\xa1y\xa3zB|w(\xe6,eZ\x85\x90\xa8\xd9\x96\xe8\x18\x82\xb9d\xdarn6\xa5\x8bo%\x02\x88bS\xdf\xe3\xe3\xa9m\xeb\xe7\xf5AJ\x0b\x01\xa5|\xf2\x83\xe7\x86\xc0\xe3\x1a\xe1\xdb\xb6C\xc88z\x8eDWH\x1d-F\xa9{\xaf\xe3\x98\xdeu\x13I\xfaB\xfbU\xb9\xb0\x08\x07\x16\x0c7D\xe2\x15_$\x91\x93\xa4\x16^\x8a\xb8g\x92%;\xa6\xf4\xa0\xff\xd2\x15:\x99\xd8\x93\xcd\x1a\x02)Mx\xe2\xecd\x9a\x91$\x9f\xef\xc0\xb4\x95\x02\x0d\x01 \xa5\x0dM 1\x8a\x00\x8d\x9er\xfd\xa4r\x832\n(\xa9\x9b\xd0\xfeZ\x9al\x0d\xc3\x0f-\x99\xee\xcb\x17\xa5f\xa8n\xac\xe5\x8c\x87`\x89\xef\xa2\x9d\xb0\xfc$l\xd4\x01\xbd\x16\x97\xc40\x84s\x95q\x81\x13D\xd7<%\x81>T*\xa8@k-p0\xfe\xdf\x7f\xafzq\xb5\x8d|\xb2\x0c\xd0Q\x03\x8d\x13}\xa6\xbe\xc7\xebUJ\x82\x10C|\x18Q\xae\x04\xe4\xaa\x93\xc6\x96\x97q\xfcS\xe5\xf6\x00\x0b\x96\xe7P\xcc\xa3\x04ny\x8de\x94}\xf2\xc4\xb8P\xb9\xaa\xc0\x86\xcd*\xd1\xeeH\xad\x05\xff\x91\xe2\x95\x19\xde!\xa4b\xe1\x91\xbf\x93R\xf94\xc5\x01{A\xa8}_S\xa9HM\x91\x05@J\xa3T\xd38\x9aJ\xb5@or\x10\x1a\x82\xb0X\xc1\x04WP\xae\x8aX\xdaL\x1e\xf1}8*\x05\xbc\xa1<\"\x8f\x1cz-\xfe\x7f?\xd0u\x7f;\xa8\xec$gQ\x02\xd01\xa3\xa4\xdaJ\x9a\xc2C\xe2\x8f\x1a*\xea\xc6\xcbk\x94\xda]\x14?\xb0\xea\xa7\x9b\xa1 \x1ew\"(Z\xc3\xc4\x85\xa6\x80x\x00q\x8e\x81s\xe3\xe5JdH`6\x1d6n b\xcc2\xd2\xca\x8c\x96\x82\xd6\xf7B\xb8#\x8b\xa7Y\x14'^\x083\xb2T\xed\xcf%Y*g\x17\xc2\"\x109S\x8d\x8f\x13N\xaa'\x0deWd\x99\xa467AX\xc6\xbd\xde\x8au-!^\xeb\x8fo\xb3\xb8\xa8]\xbcn\x99/\x91\x08\x96\x9f\xcc\xa88\xb9_\x1b\xd6w\xe2\xbc\x8a\xc6\xb5E\xceP\x18\xeeM;\xc5\xb2\x8e\xeb\x06#\x1a\xef\x8b\x04\xf2\x8c\xab\x8cQ9^\\X\x17\"\xea!|\xeb\xc9X\xc6\x02\xc6\xd5.\xa0A\xac\xb20Pes 24\x00\xd4\xb2!8O\x05\xc4$1\xc1P\xb6\x14*j\xc5Jk\x1c\x8e\xbeBt\x91\xd1@k\xe4\x12\x1d&%qW\xa1\x0ej\x15^\xc2\x80W\xda\x11\xcd\xbe\xf3+\xfa/x\xcc\xad\x95b\xa2f\xd1\"g\x80\xddB\xc6\xf2U\x9a\xe4,\x04ek\x9e\x98\x17\xb0\xb5%n(\xdd\xde\x96\x93\xeb\x8bl\xca\xbc\xbdMw\xe3\xb2\x05\x88\x8aT\x15A\x08W~+5\x13\x08'\x10L\xbc\x17\xe7\x82\xc1\x98\x10\x11!\x9a\x06y\xed\xdcV-\x84\xf9\x8a\xa4 \xee\x8e\xee\x9ai\x93l\xbb\xf5\xb8\xd8\xb4\xdb\xab\xa6n\xab\xc3.\xe9\x89\xbf\xbb\x9d\xfdJ\x9e\x15;\xb1$\xfed7]o\x07\x00\xac`n\xba\xb1\xef*c+\x96L\x15P*/=\xb3D\xe4\x98iP\xa1\xf7\xc6h\xc2\x97\x0b\xe4\x91?F\xc5%\x1cA\xe4\xeb/\x02\xb4\xe3\xab~\xd7-\xb2j\x9f\x1e\xc2( k\xaf.\xb1\x8a\xf0\\J\x1c\x04OCeu`\x8b\x03\xa5\xce\x1f\x88w\x06W \x90^\x9e3|3\xc7%\xa1\x95w{\xc8\x8aU7r\x89\xbc\xcd\xf3\x03\xebR\xdf2\x82\xb1\x18\xf3&\x9d\xd5F*\x03\xf7\xdaWL\xd4\x90Jz\xc1\x1f\xc2\xc9%\xd6b9\xeb\x1c\xbdR\x11\xce\xe3\x9c\xfeh\xe0\xfe\x88U\xcc\xa5,\x87#lIXq(\x89Q\x96\xe1Qi8f\xd8^\x19\xfa)8\x90\xd6\xf0j\x11KvA\x18\x13%R\x92%p\x18\x9d\xfd\x9c\xfcB\xe9\xf0#\x0f\x0b'\xa8S\xa8\xcf\x9c\xde,\x9b\xce\x8an\xa5\x163\xb4\xff\x1cb\x0c\x15\n\xf1\xf6v\x00\xd9(\xbet\xc1\xa0Qak\x19\x0e\x01I\xa6nd\x9c\xc3w~Q\x9d\x9f\x0d:8D\x89H[l\xf9\x99\xca\xd9\x13\x850\x08\x0c@\xec\xa0\xe4cc\x93d~\x14\x08\xe5_\xa3\xfe\xa5\xb6{]\x0b\xdf\xb49S\xeb\xc6\xb5Ib\xcek_Vn\x10\xd2p\x83\xc60A\xd1\x05g\x12\x94\x82\x98\xdb\x00\xadT=(\x02C\xf0l*FRe\xb3\xa2\xdao\xc1\xe5.B=\xe0]Q]\x89\x9c\x11.G|\xe7R\xef\xc5\x85\x88\xa5\xc9\xc9\x1c\x0eM\x99\xa6\xec\xca4}\xcey\xa9<\xd4\x04\x853\xb9\xa6\x9b\x1c\xabM\xeb\x1fM\xcb\x93\x0e\x0e\x0d\xcc\x08\x0dU1\xdav\xb4\x98\x19\xde\xc8@\xfb\x9d\x00]\x9e\xb9\xc6QS\x9d2\xcc`\xf7[1\x15\xa4YJ\xdd\xd0D\x19\x1fY\xe6'\xf5\x1b\x88\xf7\xa4\x01\x12\xe0\xd9*\xd1<\x08(;CC\x0f\xc5\xb9\xdb6@U\xaaV\xbe\x8b\x04\x87\x0dr\xb2B\xc7\xd1\xb0E\x82\xb0\xe3>\xc2\x83\x1b\x99w\x87\x05e\xfd\x1c\xd1\x14s\xf2\xab\x0e\xd3\xbd\xcd\xa2\xd5F\xa7\xbb\xfb8\xef|\xf6g\x8e#\xa2<\x1eR\x8c\xc7\x83\x0c\xa5\x10\xa7[\xc5^NN\xa6\xbe\xc7g\xb3bS\x90\xc2}R\xf7\x97P\xba\xf8f\xc9\x99 \xcb\x87nnP\xf2\xec\xd6\xaf\x0f\\Z3p^c\x16\x9a\xa9\xb6\x8d\xbc\xa5&A\xf2\xd6%,HW4\xfe\xe8\x90P\xc2i\x0d\x14~Z\x9b\xa3\x90SS\x8e.[\x89\xe17R*\x95QS\xafY\xef\xa7B\xa4\xf7\xcd\x0f\xb0\x9e\xb2JQb?\xce/\x0d\x04\xd1U\xba\xf1R\x90\xa4\xb6l\x806\x93\xba\xcf\xd4<\xceG\xe9%\xd4c7kR\x81,\xf4UE\x0d\xa9\xdb\x1c\xee[\xd1K\xab\xcb8\xf3/B%3=\x85F\xc7\xf5\xfe\xca\xe1\xdc\x80\xfa\x1agt]^1\"\x83\x84Hp=\x8a/\xb5\x9d\xde\xbb\x8a\x93\xa9\xa4n\xbc\xa8\xc1#\xa7\xd0\xbd)\xdb!\xa3\xa1\xd0X\xde\x1f\x16\x81\xf2\xfe\xce\x14\xe7Z\x89\x11\xf6Di\xda\xd3\xc5\xddD\x91\x90\x9ao7\xe9z\xc2\x92\xf5\x92e\xbc.\x97\x13lj\xb3\x91k\nEak\x17G\xf6\x1c\xeb\xb3C\xbf\x8f\xf1,K\x97\xfcT\x86Cx\xfb]UV\xcf\xac\x10b\n\x1eG\x82\x05C0\xae\xe5j\xb0\xe3Mti\xa2-\x1b\x90\x88\x99Q\x16\x94\n\x83\x94<\xaa\x1b\xb4,_\xc9Q\xd7?\x97~,\x1d\x0c\x8f\xee}\xd7\x03m~D\xee\xd0\x02\xe23K;M\xbc\xaeZsn:\xf4\xb2\x8e\x84\x9f\xde\x11:\xe1\x94\xd6\x9b\x1b\xf4\x83p\xae\xb1\xb3%\xd3\x93*yA9Y\x08s\x9d{\xba6i\x17\xa7\xd6\xc0\xfcF\x08\xd4?\x96\xaf\xfd\xf2\x04 ;h\xb8\xb7\xe4=\xce\x11\xe7\xcb\xf5 &bv 5(\xf3e\x1dV8(\xbc~E\xd0\x92\xfa,\x87\x9cU\xfbYzd\xb5\x10\x93{\xc3}@\xf3w\x99\x1d~\xc1\xf2\xa1\x996\xb6`\x84u\xf8\x96\xe5\x1d\x90\xdf\x12#\xb0\xca\xcd)\xd4+\x08]Vs\x1b\xc6\xa2\x9aNU\x06\xf9\xe9\x9ca\x87\x0c\xc8\x96\x95\xa1g\xaa\xfbvDd\xafL>\xabG\xcf\xca\xd9B\x04\xb5\xe4\xff\x7f\xf9\x02\xb7q2Mom\xfa\x92\xd2\xe1\xef\x91\x93p93\xd1Y.\xa0\xc4\xb4xZ\xf9N\xf5\xc6h\x89\xfd#\xd2K\x07x\xf0\xcb^\xce\x8a\x8bx\xc9\xd2u\xd1Q\xccI\xd8-\xc4~*N\xb0\xeak\x8c\x87P1@!\xe0\x00d\xa1\xa5\xb7\xc0~_'\x05\xcbn\xa2\xc5=;V\x9f\xd3=\xabR\xa2k}d\xa8\x80\xa9}\xd0*\xffH.\x1f5\xb1\xbe\xd5|\\S\x97fl\x86\xb6\x91\xba\xec=3\xe6k|\x84\xed\xb6\x81\xa4\xb6\xc6\x02\"YX\xe2\x011g\x96d\xe9b\xd1EA\xa4C\xc7g\xbc\xb9\x05\x93?_OQ\xfc\xd0_\xd9\xf8\xc5{['D\x7f\x0f\xd2\x99i\x0e\xc7{\x1b#\x9c\x8f'E|#\xb4\xaf\x91\xfa\xf3[:\xa7/\x08\xe5M\xaaV\xd5\xaeW\xc0\xcbC\x99S\xc9l\x15\x0e\xa1\xda2~+/\xcaz\xe34Q\x93\x17\x97\x12\xe5o\xea\xb6\x87p\xb9\n1\xa4\xd5n\xa0\xf6\xdcr\xc9\xa6\xb1\x08\xce\xd2N\xc2\xea_Ta+*Rh\xd5\xe08X\xb2.za\xb9\xf36\x1c\x82\xf1\x0d9\x08\xbbNm\x18\xf5\xe2\xea|\xe8\x94\xe0lc\xe6\xd9\x11S-Eeb\x9c\xebq\x88\x9a\xf1SY$\xe1\x9d\x82\xe7\xc16\x17\x82q\xbeE\xfa&\xbd\x15 \xc9|\xa7\xfd7\x1a\x11ys\xf6\xd9\xa3\x8d{D9FBj\xa9\xb0\xd3\\#\xca'q\xdcX\xe3*N\xa2\xec\xae\xb9J\x94\xb3\x83\xfd\xe6\x91L\xf2\xdd\xb6\n;-5\x8a\xd9\xe0`\xc1\xda\xea\xec\xb4V\xca\xa2[G9h\x1e\xda\xfd{\xda\\\x95\x1e\xde\xf6\x16\xaf\xefnG6,\x8a\x931\x08\x95B.\xdc \xac\xab'\xb8\"\x81\xed\x0c\xbc\xba\x90\x92S\x11x\xd6r\x11T<\x7f\x1e\x94\x03s\xb6\x0c]p\x17:\xe1\xafz:\x0c\x12\xba\xa0!tBE\xe8\x88\x8e\xd0\x15%\xd5\xa3M\x03k\xb7\xcdd\x11\x15q2h\xed\xbdq\xf7\xaaG\xf5-\xdbl\xeb\xbaq\xbbC'\xd2\x02\x1dh\x9cz\x94\xba\xae\xc1\xe8\xa9mO\x82r\xb1h\x0e\xb2\xa5\x1eN\xb3}I\xb4\xeb\xf4ZD\xa3\xd0R\xd8\xea\x0f\xa5#\xa4n&\x1d\xd1{\xc5\xe5b\xed\x989<\x94\xd1\nE\x120\xdb+\xc4\xfb\x98|J\xd2\xdb\x04\x14\x15\x18\x82\x18\xb6[{\x88V{uJT\x05v(#\xd3Q,W\x07\xb4\xc7F\n\xf6\x99C)/\xdb\xe4\xac\xd3B\x80\x8e\x88\xd1\x08n#\xd7VR\x81\x1d\xcc\xe2\xc5\xe2M\x84z\xba\xf5\xfd{i\xc4j}^\x93\xda\xbcf\xa2\xc7\xbd\x8dzlDX]\x89),\xc0\x0ea\x15\"\xe7\xe4k\x1d\x9b\x92B\xed\x17\xd6[Dy\xf1\x8e\xa1\xa0\xadB#\xf2W\x17i\x81\x92\x92\xfe\xeed\x1e \x9f:\xdd\x1f\xb0\xa6\x0d,\xff,\xcf\xaa\xc8&\xf3\xa5\xa9\xc5\x8bC\x18\xec>QIb\xe0\xe5Kx\x0c\x87\x87p #B\xe3\x9b}\xfef\xb0\x0fG\xb0\xa7^\xed\xf1W{}8\x82}\xf5\xea\x80\xbf\xda\x85#\xd8\x19\xc0\x10vv\x1b\x87\xb4v\x1c\x9fJ\x1bXM\x7f\xa7\x0e\"[\xca\xdf\xc4\x05\x1a-Ov\x9f\xf2\xbd\xec\x0f\x9e\xed\xc2\xf7\x98\x14<\xd0\xac\x99\xeaK\xe1\xfd\xdf\xff\xd7\xff\xe9\xa0\xb2\xe8cTU\x97\x16\x83\x9ak\xd8\xa0\xe9h\xa5\x062p\x0dd\xd08\x10\xa0\x06\xb3k\x0c\x06\x7f\x9b\x1d\xee\xba:\xdc\x95\x1dv&\x9e\x85T\x88>\xa7\x90L\x93$\x12t\xb0\x1f\x1aX\xffB\xf36\xc3x^\xe8\x97YCy\\V}\x1f\xf0\x0f\x03c_\x94\x89\x0d\xeb\xfcVho*\x11\x17\xac\xa9\xa32\xc2\x99\xbe\x9f\xcb\x11\xefh!\xd0\x9a\xf7^N\xaa\x00\xf8z\x95\xd9T8\x8a\x07\xf0\xaf\xb0\xcb7P\xbfI)_\xa5n\xf4K\xf2\xee\xb6#i\x0e\x04\x80\xd7\x91\x93y\x94\x9d\xa4Sv\\\xf8\x9a\x0f\xac\x199Z=\x18b\x9f\x8b\xdd\x8f\x1f\xef>;\x004\xcc\x7fq\x08\x8f\x0f\xf6\x06\xcfj&_\x06.Y\x04m\xdfX\xb8Q_\xa4-\xd6 \xb2{i\xd6\x19Xu\x06\x97!$\x95\xa3\xfa\xce\xe0\xfeF\x1e\x14\xde\x9a3\x19\x103\xd9m\x9f \x1f\xa5c\xe1*4C\xa87\"\xd2\xc2M1\xeb7\xe2G\xda\x81$n?\xa8\x9c\xec\xf5\x8d\xd4r\x11\xe4&\xc7\x0d\xdc\xcb\xb6ksj\x10\xe8\xdb\x01\xc1\xc8\x95h\x84\xcc\x84\xdcbj\xfc\xd66\xdb#\x89T_z\x9b\x1c\xd5\xd6J\xb2\x1a\xd2\xf1\xcc71b\x0fv !\xb0bOY\xa4%j5\x1a\xf1\xa3\xd6\xf47\xed\x87 t\x0c\xbf\x86iI\x0b\xcd\x9a=\x1c\xaa\x91[\xe9\xa8\x11;\xcaA\xf7C\x04\xb0\x81\xa9\xc3\x16lX\xb9\x99\x1d\xc7\xf9\xd0\x0c\x8ci\x03\xf3\xd4\x06\x0b\xada\xf5WQ\x8f\xe7\x06\x87\x10\xd75\xd3\x8a\x91t\x0b\xff\x95\xcdmy\x06\x95\x82\xa1\x01~\\\xb6\xd0t|\xee\xb4\xff\xe3*\xef%\xfab\x96\xac\x99b\xe2\x85\x9c\xe3\xe8\x18t\x03%\xd5Mhs\xbb\xf5\xbd/\xec\x14\xd1\xe5\x9bD\xa3\x04c\x92V\x00\xd71\x89\xf3\xfc\x9c\x10$\x81\xe2/\xeao\xf0:I[\x91:\xd4\xa5\x88\xd0xK\xf5\xc0\xf8\x8f\x1cV\x1d\x9d\xebc\x92RL\xe3]\xc2\x8d\x99\x17\xbd\x81\x01\xae\xec\x93+\x8aAs\x0e\x19\xbc\xe0M(\xd2hW\xba\x91\xd9\x03\"\xbf\x18e\x97\x0e\xfe#E\x0d}\xd9L\x8a\x8e\xbcB_\xaf\xa1@\x8aG_\x08)\xdd\xc8\xce\x0e\x0e\x86\xaf\xde\xce\xae\x10\xb3\x9b\x06\x86\x8c\x956\xb2\xa0\xf3\x18v\x7f\xfd1\xc8\xb60\xf8\xce\xa1\xca\xd2Y\x1f\xd5\x1e=*\xd5y}\xfb\xb8M\x8bQOhly\x9b*\x96\x01\xfb\x8d\xaf\xad\xf3-\xb1\xa9\x8c\x1e\xa0\x01v\xc0O,\xcaMn\x0c\x9a\x05\xef\x0b\xcfijh\xf5|a\xf5\x0d\xa3\xa9\x17\x9a\xa9g};\xbe \x08\xa9C4h\xe4\x85\x1eT@\xa9C\xeb\xde\xc3\xd1\xc4\x98\xfa\xa45 \xc68\xa5\xeeu5\xa3\x9b\x1ei9Nn\xb4\\Pt\xa63LcS\x164\xa9\xd7\x11\x87\x11\x04\xb5\x84*\xf5\xb4 \xb1\x9d\x01\xabfu_Zc\x14Y\x94\xe4\xb34[\ns\x0c\xca3\x06C\x83_\xa8z\x1dl\xa7\xc0d\x9b\x8d^h\xa9*\xe9\x95\xb5\x9a]9*\xb1\x0d\x0f\x9c\xc9\x95[J\xdb\xca\xea\xf2\x983v\x80\xe068\x84\xae\xa2\xc9'\x15\xaaf\xb9^\x14\xf1j\xc1\xa0\x88\x97,w\x86\xbcW\x03\x99\xaf\x93O\xa5\x9bJ9\xba\xea\x8d\xcc\xfaW\x94W\x852ut\x88Y\xf8\xdc\x93M\xbb\xda\xc5\xf3'5Lw\xfc\xd4\x8al\xaeLd\xe1\x05\xa4D\xe0\x8d\xaa+\xdf,\xb6z\xfcZ\x99\x81Ri\x04\x19\x9bj\x88C\x99I\xeakN\xd7\x90`\x14\xf1.\\\xc5\x1c\xf4\x8d5*u3\xafT?/h\xfb%\xc2\x13\x83\xaa\xa6E\xf3h\xcc-RNT3y\xaa\xde\x1d\xea5\xdc\xa9Ff\x8bu>\xd7\x1a\x10\xbf\x0fU\x89\xb2\xbaG\x9b\xedU\xc6J_\xbd\xa8M1J\xf1S\xca\x1d\xa3\x8eg\xe4\xc8\xf4\xd1\x1c\xe9\xbfj\x99\xd3Hnl]\x12\xd7\xfa\xa2p.r-\xc9U\xb5\x7f\x9a\xe7\xb1v\xb1}\xb5\xab\x14\xc2\x88\xd4\xe6\x12j\x99GY\x15\xee\xde\x8a\x14\xa0\x0eL\xeb\xa2\xe3$Z,\xf86\xac\x16y\x9a&\x0cn\xe7,\x81\xdb2\xa9\xd2\xd6!\xf4\xcd\\\x86B\x8bi\x10\xcd\x1au\xdc\xb0\xbb\xbc\x88\x17\x8b\xdaV3\xbb,!C\xb8\x03TB[j\xa5V\x0b\xb5w~,\xd8\x95x\xc3\xe0\xee:\x816']\xa3 \xa5\xdfS\xbd}\xcb\x9d\xac\x1ay}0\xb5\xfd\xd6&)X\x00\xae\xbev\xc4\x98qvk\x8b\xb2t\x97ug\xb3\xa63\x13\x85\x13\xfd\x80\xe1P\xa9\x1dB\xac|\xa3]\xb7\x17!le\x06\"\xd1\xf2Q\xe7#\xc7\xcf\x8c5\xc2\xf3\xe5\x17:q\xbe:Al:\x174\xdf\xaa4\xc2\xb6t;)t\x88\xe25\x82\x02\xb8\x88\"\\cW0\x0c\x93\xc9\xc0\xf4-.\xcb\xd7\x1b\x0dU\x93\x15\x03\\\xf4\xea\xdc\x960!\xb6\xb7A\xdf \x89\x8e\xa9\x1at\xfe\xccd\x14\xed\xd6\x8c-\xd6l\x90Q\xf8\xc2fZ\x10Y\xe1Cn\x12w\x83\xb8\xdc\x8b\xd7\xd6\x98j3\xeb$G_\xcc#\xa9KEiv\x1aM\xe6\xf5\x8aq\x95\xdf~\x92\xb1\x1a.tK\xdf\xab\xf0*\x16D\x93\xa4\xaa\xd2\x8a\xb4\xb4\x1am\x03 \xe7\x069\x8eug\xb4iV\x10M]\x12\x99`\xbe\xc08\x80\xc0F\xc9\xa5U\xf9\xab/\xf3f\xa3\\`\xaeUX\xd34\xc2}\x97\x8b\x84g\x00\x7f\xfb\x86&5\x0c\xd0Sen\x92\xb7\x16\x89\x1d\xb9jq\xfe.z\xe7c\xfa_\xd4b\x14B\x7f\x817w\xdf\x7f/\xd5\x15;\x98\x9b!\xc5\xe8\xd6\xc32\xfc\n^ \xb5\xa7O\xef4\xc7\xba\x0b\xce\xc1\x93\xa7\x81\xcf\x87$\x916\xca\xf3\xf8:\x81!\x16=\xfbV\x9b\xc2\x10\xd2\x10\xb3\xc9\x85\xb0\x0eA\xf5h\xec\xadNv\xbd\xd6\x85\x05\x7f\xb4\xb8 Evg|E{g-B\x90Q\x00I'\xacI\x9a\xcc\xe2\xeb\xb5r\xc3\xea\xd3\xcc\x7f\xe4t\xd2js\xe2\xc2,\xd8C0\xcc\x80\xb5u\x85IT\xda\x8fU\xa7\x93\xb8\xf4Xhw\xb9\x99%Y7\x0f\xdd=\xec\xfa\x90\xab\x91\x88\xd0\x86$\x14\xc3\x8d\x13\xd4\xa35\x0cJ\xa6\xa5.\x0b\x1d!ez\x0d?\x13\xf9\xc1\x05K\x81\x9eZ\xd5*e\xfa\xad\n^\x17\xc9\xd4\xd2\x83\x83 \xc4\x8c\xa8\xa3\xcb\x10\xe2v\xaa\x1aR\x1ap\xce\xf9\xacG\xec\xb2d\xe6\xf9\x8fz\x15${\x05\xf6\xf3\x1c\xd8\xce\xce\xf3@\xb9\xb9z\x91\x07\xdb\xe0oo'A\xa5\x82\xda;0\xe5zM\x8f\xa2\xdc&|o\x96\x88\x9c\xb9XTJ\x1c>o\xb0\x90Q\xeeC\xf0\x02\xd8\xe6\xff\xfcM\xb51K\xa4\xc3\xa68;+\xc7\x81\xe7\xf0\xf5y\x9de\xec\xbcF\x04\xc5G\xf9\xc6\xb1f\xaeD\xf2 \x9eZE`\xa9\x1e\xec\xbd\xc9\x9f\xc8OB3\x01\x95\x03\xfd\x81\xba^\xfe\xfa\xad\xc4I\x88\x1cT&u\x1a\xe9\xeb\x00\xaa\xaa]\xb3\xe2\xec6Q\xd5^\xb1|\x92\xc5\xab\"5\x0c\xa8#\xd7\x07\xef\xa2\xa5\x19\xd3d\xed\xaa{~\xb7\xbcJ\x17y\x87\x93\x89\\cA\x82\xe5\xd1\x9c\xf9\x85\x89\xa7('\xea50\xca@\xe4\xe7\x81bv*\xf1\x9b\xce-G\xae4\x7fpOg\xa1H\xba\x9eQ>\xb6\xfa\xd2\x93M\xa0\xa1\x86\xfd]\x1d\x81\\\xaa\x0e\xcc\xe7\xbe\xfe\x07\x9b\x89n\xe0SJ\xe8\xb4\x9c\xfd]\xbd\x95o\xdc\x15\x8f)\xfe7\xf1\x07\xfb\xe6n\x89iO0\xce\x9e\xde\x17I\xf9\xc1Fd\xc2\xe3\xfb\xa7\xa4v\xa3\xddK\x12\x0c\x19\x92+\\!\xbd#\xc1\x87\xac\xa9\xe5HF\xd9%\xfa8)_\x8a\x08\x05\x12\xf5\x85\xb5$I\x0b\xa0\xf5>\xba1\xfcr\xe8[[R\xdb'B\x10\xd4\xd3\xc8}\xf9\xe2P\xe0![\xefR\x10\xceY\xdbh;\xa1\x05\xcdH\x15!x\xe31\xcb\xdf\xa6\xd35\x9a\x9c\x98K\x89\x8c\x8e.W\x06\"\xde<\xda}v\x81\x88\xbdX9\x17\xae\xdf/\xd6\xd7q\x92\x0f\x1d{\x8be\x99\xab\x08\xb0\xed\xe9z\xc2\xb2|\x08~\x9f\x0b\xbar\xe9\xcd\xe2E\xc1\xb2\xee\xc4\x80\xf5>\xb1\xbbs\xf6_~\xd0c7,\xd3\xc8\xb4\x13\xb4`u_\xb4d\x0bD\xa9mT4d6Q\xb2?z\xb8f\"\x16aw\xb2\xefDg\xd6[\xb2\xec\x9a\xf9N \x19\xc5T\";\xdc\x06X0\xfe\xe1O\x0f\x8d\x08\x9a\x1e\xa3\xf2 N~\x0dtH\xe8pZ\xbf\x06\x805)\xb2.\xc2\xc5B\xe5\xb6k^\x97\x89\xcb\x0f\xf3m%\x94\x0f:\x0b\xe5j2\xa6\\./e\xec\xc9\x95\xaa\x03\xc3{\xfa;\xfb/>\x83\x85uG\xc5\x19\x9b!\x18WS\x0bv\xc3\x16\xc32`|\xadl\xc9\xf2<\xba\xe6Go\xe9\xe6\x8d\xb5\x8c\x1e\xff\xbe\xa2\xb7K\xaf\xd5\xa4\xe1\xb4`\xfb\x97\xfc|\xc5&C(z\x9c\xc98W\xda$\xfc\xf5\x87\x04\xd6\x91\xb28f\xf35\xe8\xc0\xb1\xaaok\xa2\x80\xd8\xa1\xf8b\x15 \xbe\xc4l\xba\xc2G\x87\xf6\xf0\xc9\xae\xa9\xd4\x7fH\xed!Er\x08\xf7\xf8\xff\x15\xf4\x80 \x87\x8e7\xd3\x11\xd2\xe4]q\x8f\xc6\xff\xdc\xab\xfe\xdc\x0f\x02a:\xf3\xf7'_\xb4!\xa3\xeb\xc0\xe8\x80\xc67e\xb41\xc4ZI\xc7\xbd\xa0\x17'S\xf6\xf9l\xe6{\xd2\xe21\x9dA\x84g\xbd\x9f\x07\xa6\x11)\x947\xd1/a\xc7\xe9\xf6\x7fS:q\x1b] \x07ft \xa3:S\x96\xb6\x98\x05\xa1\xf0\xbd\x90\xea\x1e\xf4i\xe7z\xfb\xa1\xab\xc3>\x92\xd8\xed\x0ebB\xadqq3\xe1\x9b\x88\xd0\x90\xd7\xcdh\"\x91i\xdc*'4\xb1\xab\xe5\xef\x970\xc0\x83}\x1b\xbc4\xc3\x18)\x05\x0c!\x1b%\xb0\x0d\x83K\xa3\xea\xae\xac\x8a\xc0\x0b\xc1\xd3kj%X\x80\xbf\x9c\x03\xfc\x1a\x82\x97\xcf\xd3\xf5b\nW\x0c\"\x97Z\xc3O6\xc9$\xe0&~\xbf\xe9\xfdD\x9c\xbdEO\x1c\xfc$\xa1\xd1nu\x1dD}\xb0\xf7TCZ\x071\x0f\x91_\xfcMC\xe6\x1b(\x8dkw\xfa\x14\xf9\x11&@\x9e\xf2s\xeay\"e\xeaj\x11M\x98\x9f\xb0[\xf8\xc0\xaeO?\xaf\xfc$\x04\xef\x9aW\xf7\xbc\x80\xd2\x1b({\xa2\xdf:\x1e.\xa2\xbc@ss\x11Yr\xb1\xc0\x1fy\x19\x16\xd6@+R\xb4\x10\x98\xf6\xd8|\x1d[M\n\xa5\x8b0{U\x0cl\xd0q\xf5\xea\x80l\xd3\xb1\x94k\xae\x8b}JXU\x9a\x16cm\xaa\xa9\xd6\xc1B\x8f:n\x1aB\xd9=oG\xe3\xc8\xbf\xc5$\xe9A\x97\x9d\x90F\x1cs\xb0a\xdb\xe5\x92}\x11\xdd\xa5\xeb\xa2\xdb={)\x88\xfc\x03\xdc\xafS8\xfeP\x1c2}\xbf\xbe\xdb\xef\xbb\xef\xd7\x9fv\x16\xe5\xffW\xe0\xab\xff\xbe\xdb\xca\xc6\x99P\xaahvM\xa3\xa8HaM\xfc\xd0X\xb3& \xb4\xb0\xab\xe6\x98\xa4\xd3\xb8\n\x96hm\xaen\xe7\xa3J/\x90\x86\x90\xf7>\xbe\x7fu|q:~s\xfc\xa7\xb3\x8f\x17-\x8a\x82\xfaQ+\x88\x00\x9e\xa0R\xb9\xa7S\xc2\xc6\xde~|\xfd\xe6\xe2\xb4M\x91\\\xefM\x08\xde\x9b\xf5v\xfe\xd3\xd9\xcf-\x9dX\n\xca^>Oo\x13\x9b\x0e\xa9\xa3b]j\xed\xabO\x8ay\x9c\\\xbb\x1c\xe0\x94\x16\x1f\xdb\x95\x87T\xd5\xc8\xdf\xf8\xd8;\x1ev\x1c\x0e\x19\xe1\xd8\xd8\n\x07 \xf5\xb7g\xafN7\x06\x07\xce\x8d\x06GUi\x99N\x99c\xfa\x18\xea\xdc\x1fy\xbcJ\xee]\xaa\xfb\xab\x84\x0f5\x13\xb1C\xd0\xc6\xd9\xabO#\xfd\xad\x1c\xa5|\xd9\xce\xd7\xcbe\x94\xdd\xe1\x94o\xe7\x91\xc8\x0f\xc4\x7f\xc4\xf99_U\x11\x86}\x9de,)~D<\xd5\xdf\xb8\x98-u\xec<\xdd\xfbUO\x1d\x82\x95\x13de`Z\x97\xe5\x92\xda\xe8T\xa5\x9aS\x07\xf6\xe8Z#\x13\xda\xf2\x86\x04\xb4\xba\xb6&\xc9\x80S\xdd\xb50\xd6\xa5 {\xb4\xd6\x8brw'i\xb6\x8c\x16\xf1_\x19\xba{\x05\xd2\xfe\x1d\xfb\xd6wp\xae\xef\xe0\x00\xcb\xeb\xaf\xf9w 9\xcc\x1a\x0eu\xda\x8d\xa5\xdd\xab.\xa0\xd7SX\xe9\xa6\xb1pT\xff\xe9\x8e\x9e\xd3>kj\xef\x1a\xea\xe5\"0\xa6jo\x1bA\x94\xbaK\x06\xb6\xfc\xdb\x81\x1d\xdfBf\xc3c\xd3\xb8Hk\x18\xd2\x89\x94T\xf2\xcf\xdeAG\xd7/N\xa5\x8c\xa1\xd0jt9\xc0\x14\xf3\xe6d~\x12\x8c\xfa\x97!$\xa3\xc1%zc\xfa&EoTm\xab\xbb!\xd6\x13\xcd\xda\xc2\xa90\x14\xd7\x90#\x16\xfec\xd2\xc8Y\xa4\x0e\xac\xf7\xf8]\xfd\xaf\xce\xb0zb\xd2\x0c\xa9\x96x\x16\xf8^\\\xb0,\xc2\xa5\xb0\xc9\x9b\xe1K\xd9\x06o\xc7\x8a\x9b\xa1\xf4\xfd\xac\x87\x0dk\xc9\xc71{\xdaa\x8d\x9f\xddp\x8a\x8dsI\x8d\xb0\"\xf6\xfa\xab\xe5\x1a=\xb9\x1ce\x97f\xfe\xbdX.b\x93\xa4\x06\xaa\x1f#*Q(\xa1\xc8)NM^\xa5\x1a\x108\xb1[oA\x83 \xedx\xd3\xd9r_\xc4AB?\xe6*\x84\x93\x19oE\x913\xf3=\xbdi4\xc0\xd1R!?\xccb\x02\xa6X\x86Y\x97\xda\xa0\nMr\xb0z\xa6i\xc2\x86b\xdc\x9d\x83^\x878\xb0\x0d\xba\x8f\xa86\x98\x1f;\x08\x03\xeb\xe0\x1e\xd5\x05\xcb\x7f\x05\xfe\xe9\x97VE\xe4xk\xea^\xbe\xdb,Z\x1d+\xfdBC\xee\xe8\x7fH\x85\xc5\xde\xaf\xcb:.Paa\x99\x94\xaf\xcb\xa2\x81Y\x94\xcb\xa2\xbd\xfd\x03Z\x97AD_\xfd\xa7.\xe3\x97\xde\x97$:\xadHw\x81X\x95\xec\x99%\x91,yj\x954i),!c!\x9b\xd9\xb3\xba\x9eH\xb5\xc6\xc0x?\x93\xefwI\x84j\x08S\xfaK\xd8\xb9\xd4\xf4,\x99\xa6g\xd1\xac\x0f\xb3\x10fJ\x06?\x7f\x7fz\xd2M\xefQ\xe6G\xd0\xa2\")\x81\x1b\xa3\xe9\xa2Z\x04-Ru\xa5\x08\xe8\xa3V\n\x01\xc7`>~x\xd3m,\xb2\xb3u\xb6\xd0\xfb\"\xc4\xf6\x86\xce\xfep~\xf6n\xa3\xde\xfe\x92\xa7\xa6\xb4u\x96MY\xc6\xa6\x9a\xee%\xe8\xdc\xff\x87\xd3\xf3\xb37\x7f<}\xb5\xc1\x18P\xf8\xc9X\x9e.n\xd8\xd4\xbb|\xf8\xb1\x8c\xcf?\xfep\xf1\xe1tc\xad\x0c\xad\x8fI\x84\x13\xbd]\x98J\x13\xdab\xde\xa2\xa4Qs=__\x15\x193e>]\xad\x14\x04\x0ehd\xdd\xa1\xf0\xfe\xf8\xc3\xf1\xdb\x87\x9a:\x9f\x9d{\xe6Y\xb4|\x17- \xd0\xc4U\x85\xd7\x84\xd6o]\x15\xdb\x85y\x13\xcc1\x9cg/\xce\xff\xe7\x92\x88 7!tB\xea\xbd\xf0T\xe6\xe7\xcf\xfc$\x9d\"\xd1\xda\x8a\x05g\x0dG\xb0\x16\xaa\x88$Z2\xa17\xeby\xb0\xad\xde\xc6\x89|\xc7?\xde\x11\x05\xaa\x1d\x1f\xf3\xf7\x97_\xc4\xf61\xca\xe9\xea\x02\x8e\xc0\xc3\x19\x8d?/\x17\x1e\x0c\xe5/Z\x7f\xa0i\xf7\x18\xe6\xf3F\xeb$7\xd6dA\x08#\x0f\xa1\xc9\n\x86Wv\x93\x10f\x97A\x08yg\xac9}\xfb\xfe\xe2O\x02w\xc6\xaf\xdf\x9d\xbc\xf9x\xfe\xba\x95\xb0l\x84EoY1O\x89\x1a\x0f\x83Kq2Y\xac\xa7\xect\xb9*\xee\xfe\xc8Ak\xf3-\xc2\x1cx+.y\x1ee\xc2v\x1be\x89\xef\xfd\x1ce \x06\x1el\x02\x08L\xd0\xe4\"I\x0b\xb8f \x17^\x19D\x80c\xfb\x1f\xec\xae\x87\x16d6\n\xe4\x18\x1d\xd7\x81#\x0f\xb3\xe8c\x04@\xce\xd9g/\x84\x9c\xaf\xfd\xba}\xed\xffx\xfc\xe6uE3\xce\x7f\xbd\xe5\x8e\xf3\xb3\xe3\xf3=z\xad5\x05YGH\x04\x84\xfa\x9f0\"\xe7\xb4\xe3\xd1\xe7\xe5\xe2Q\xdc+X^\xf8\xb1\xd8\xde\x1c\x0d\xd6K\x96\x8f\xc5\x96\xa4\xbe\xe4{x\xd2\xe3\x9ca\xc4\xa1\xf3s\x8c\xf3\x8bd\xcc\x10ArB\x18\xb1\x86!6\xdfcl4]c\xb7_R\xd3\xefx\xfb1S\xd6\x8f\x1a\xed\x10m\x95\x8e\x15\x94\x01\x95K\xecV\x18\"\x8e\xb0\x9bh\x11\xf3\xc9\xbd\xe7\xad\xa3\x91\xfb\"\x84\xb4\x835\x18\x87FAR\xe4\xa2\xa2\xc8!(\x0b\x85Ks\xfe\xa4\xd1\x93\x1d\x15\xa5}\x7f\x08\x93\xfco\xdc%\xdavx(\x1cH\xdaq`t\xd9\x15\x07\xbaX\x03\x81\xc5F\xd6\xacCj\xdd\x12\xb0\xdf\x18\xf0\xe7\xa7\x17\x9c\x9b{\x7f\xf6\xee\xfc\xc1\xb8\xb8\xcc\x8c\x07\x035\x1e\xce.\xc3k\x9d\xde\xd2A\xc8\xd6\x0ef\xc3_\xa3\x13\x1d\xc2\x07\x8e\xc0\xd0\xea\xdb\xa0\x15\xd6\xd2dP,\x8e\xfcC\xd1V/!\xcf\xc6\xd2\x90_T\x92? \x9e\xaa\x88\x8au\xce\x19\x16U\xb5zS_\x9bP\x96g,_\xa5I\x8eY\x02\xb2\xa07g\xd1\x94\xa19\xd2\xba\xfc\xfb\xcb\x17K?\xc0\x17c\x824\\\xe3}\xb1\x1d\x8e*i\x08\x91\x8b\xdd_;(\xe4B\xc1\xae\xf7\xc3\"\xbd\x12\xda\x97iTDzPm\xbb\x8e?A\x8a\xed\x1aD\x08^\xc1>\x17\x9cr\x88\xd6\xf8\x112\xe9\x88\x95\xff\xf1\xf1\xf4\xbc\xedJ\x7f\x03\xa4\xfc\xaf\xcd\x902\xd6\x90\xb2U\xec\xf8\xaf5\xcb\x0b9\xe9\xd8\x05\xf9.\xa2\x05\x9f\xf9\xdb\x8f\x17\xc7\x17\xa7\xaf\xfe\x91 \xb0\\\x17Q\xc1\xa6\x1f\x1e\x0e\x10\x929<{\x7f\xfa\xe1\xf8\xe2\xf5\xd9\xbb\xf1\xdb\xd3\x8bc~B||0:\xd5$r9\xa4\"\x01\x92O\xec\x8e\x96\xa6F\xad,\x85\x83[\xeaz\x1eYN\xa0\xe5J(V\x0e\xb5\x0e\xae\xcf\xf3 \x080{dY\xbd\xd2\x0el\xfcI\xab\x90\x8d\x9f\x1eUX\xe2\xaa\xb7\xe0\x87ll\x9f\xaci\xd0M\x1b$\x98\x87\x87>\xc5\x9a\xb0\xa3qOL\xd9\x82I&C'\x87Y\x08\xe9e;\xde\xab\xc9<\xe8\xd6\x7f\x98\xb9\x94{\xbb\xe3T8-;?\xf9\xe9\xf4\xed\x83\xadI>\x993\xeat\xfe&*\x96\xf2s,\xd6\x11\xd5\x13\xfdTT,\x13\xca\x87/_\xb0\x9e\xbc\xb6\x1dR\x1fxc \x83s\xf1\xe6\xb2\x9e\x97$(\x7fv\xbe\xbf\xdd\xa3c\x99=\xdb'4\xdd\xf2\xb67_\xb1I\xccr\xaf\x8b\x1d\x00\xb9\x16!\xb2d\x99\xcf\xd0_?/\xb2\xf5\xa4H3\x12zZ*\xa8HK\x0f\x7fx\x08~\x82mD\x01\xdf\xdb\x98\xdbh\x08\xa9n+\xd0\xe9*\xe1\xa6\x16\x87\x15\xe7\xb8\xff\x8cV\xd8\xef\x99 \x91\x86\x85\xfb\x94\xce>\xf1\x07V\x948\xa9\xb1\xa7\x14\xf6\x93\xde*K',78\xdbU\xc9\xfd\x94\x89\xf6k\xe5S,\xafg\xc0\xaf\xd7\x98c\x8d\xb7\x82\x9f<\x99GI\xc2\x0c\x85\xdb\x0d\xd6x\x15\xe7\xab\xa80\xc35/1\x1di\xed\xd55\x11\x80\xee\xae\xed*\xf7F\xa67\xd8\xb6\xc3_\x83\xd4\xea\\\x1bWJ>s\xe6\xbeW\x97Z\xd7V(R\xf5\x08\xba\x82\x15B(|B\x92\xa9\xbd1\xa6s\xd5h\\\xc1\x1fu\xe1%x\xcez[\xd5\x88V|\xe7O1\xc6\xc1\xaa\xb1\xc9*G\xba\x8c\xd6\xcaQ{\xf0\x9c2lJ\xaa\xe8\xaa\x95\x11S\xb2\xbd\xed\xb8g\xbb\x1emo/[o\xda\xd7\x8e$\x1a\xf2\x06\xe8\xc7j\xe0\xa1\x15\xae:\x84\xcc_\x06!,\xbf\xd3^5\xc7\x86\xd7VG\xff\xc8\x93[\x00\x87\x90\xf8\xcf\xf6\x02\x7f\x16\xe0\xb5l#\xec\xd0\x94\xe1\"\x9e|\xf2#\xff\x0e\xe3\x94\x0ct\xfe\x0f\x86p\x83\xc6`\xbd$\xbdmm\x0dk9\x1b\xc2\xd0\xc2\xb12\x19N\xd8-\xcc\x83\x1e'{\xbb\xfct\xe2\x7f\x0czi\"\x8578\x84\xab\x10\xbb\x8b\xfc\xb8\xb7J\xf3B\xeeB$5\x03d>&\xbdh:=\xbdaI\xf1&\xce\x0b\x96\xb0\x0c\\\x01\x0b\xb5\x06P\xdb=\xe9\xc5K\xde\xe39\x86S\xcdU\xd0c\xf7\xd4&\xfa\x18|tt\xe3\x07\xca\xef\xea\xa6\x87\xf6\x88t\xa7\xa1\xab\x10\xb6\xc4\xc8y_^\x9ad,\x9a\xde\xa1\x1d\xc2d\x1e%\xd7\xcc\x838\x81\x85\xef\x89 \xaf\x1e_>\xf7\x88\xf2^\xb4Z\xb1dz2\x8f\x17S_\xfb*\xe8\xd9-\xb7\xe1p\xde\xcb\xd82\xbda\xa21\x91 \xa7\xdc\xa7\x06\xce\xd6\x16\xb5a|\xac\xb8\x88\x97,]\x17\x1aF\x84\xd0\xaf\x1f\xb8\xfa\xd1g}?\x84\x95q\x06pZ=\x84i\xd5\x04\xfe\xf5\xedq2\x1bM\xebh:\xea\x08\xc2\xcd\x9f\x9b!\xb0v\xb2\xd9\x18\xc9\xb5\xb5kBQ\x02\xb2\xeb\xb6\x8e[\xa0\xb7)\xb3\xb3\xfb\x94dvv\xfb\x8f\xef\xc3\xe2`\xb2\x10\xa4\x95\xa9_\x88|\x1b:\x9b#\xed\xedJK\x08[\xf1\x82\x91\xa2{3;\xa5\x98\xf8\x82\xf3\xc2\xa8\x05\xe3b\x92\xb4\xa4\xe5\xec\xc32\xce7\x8cs[\x8fu\xffd\xef[\x02\xda\x17\xba\xe5\xc0!l\xb9\xcc\xb9w\xfb\xbf\xa4Q\x8e>\x1eY\xa7\x8b\xa5d+\xf3\"\x9c%\x1d\xa1\xc5]\xa8\x8f\x89\xe1\xd40j\x8aw2\x9a\x13\xd8\xe3\x81\xccOC\x88\\\xb5\xa112\x85zn\xa4\xb3}1J/\xfd\x88\xd0\x10\x98\x8f\xd0\x0e\xa2\x8a\xc2Y\xb7=\x8a\xb3ztF\x9e\x0c$\xa3\x1e\xdb\xe0K=x\xeb\xb7\xeeM\xd3\xa4\xda7%`\xd5N\xf0\xf3\x00c\xfav\xd0\x80\xab'\xf3=\xce\x15\xcb\xc8\x1b\x89\x88\xd7 \xd2'\\\xb6exq\x918\xc2^\nM\xc0\xb7R_\x84\xc9\x8e\xe5\xff\x98\x0d\x87\x8b\xdb\x9b\xa1Q5\xe9\xc1>}\xca>1\xe5j\xa9R\xd83St\xca\xfc\x15\xe6\xa1,\xc4\xf0\xa7\xfd.g2\xba\x1f\xe4\xd4\xc9\xbc\x15\xa1d\xa9TP\xf5\x8dX\nb\\\x84\xdf\x19\x84(\xb2\xa3\xa7|\x8aQ\xe2\x82@Jb\xa1\x90\xdaa\x07\x06!J\xe9\xecy\x99o\x12\xc5\xbe\xed\xed\x05\xbc\x80\xc9s\xd7\x81\xc2%\xa4\xb5_\x8c\x16\x97\x0e\x82\xcc\x05w\xc2y\x81O\x01{\x995I\xc7\\\xa6_\x8d\xa6\x0e\xe9XO\xaf\xcd\xbb\xe1\xc2C\xee\xdf\x840\x0da\xc5\x99{QA\x98r\xceQ\x80\xb9\xe1\x9c\xfc\x0d\x0c!\xe6c\xc6@\x17\xfc\xcd\xe8\x92\x9f\xceT\xf8!\xebM\xe6\xaf\xb0\x83y \x00\xc6\x87\xf7\x9d\xfb\x13\xb5>\xf7E\xc2\xbd\xfdN\xbc\x1bq\x14{\xe31\x9a\xb9\x8e\xc7b\xaf\xe0\x9e\xe0\x8c\x88\xfc\xc0\x86z{V\x9cZ\x12\x19\xa2\\Z\xa1\x12V1Zb\x1a\xc3\xbf\x01\x95\xd7\xa3\x82\x0b\xf7\x1b\x9a\xb5k\xf4\xc9\xe4\xc5\xd261\xab9\x10\x16C\x95\x9c0\xc4\x0d\xc1\xab\x9b\xe2\xb6\xc5\x8f\xc10\x94\\&E\xb3\x07B\x06p\x9b\xf7\x7f\xf5\x1d\x8b\x9dv\x81\xc7/lN\x1cBQ7\xa1\xc8Q\x17\xcd>\xb3\xc9\xba`\xf2N\x0b_ \xfb\x81?\xe4ir\xbeb\x13\xed\x95\xfc\xe9\nJ\x11\xfb\x89\xbfO\x862\xe7%\x83=\x87\xa3<\x91\xecX\xad\xc5/c\x0b\\\x9bL\xa3\x0cU\xa9\xec\xf3\x15\x9bH\x07\x05R\x1aj\xc4VfX\xf6TL{(L\xd1rv\x91rx\xcbz\x89^\xc55\xa1\x90Z\xa9_c655\xa1\xa9\x1b\x0c+\xc71\x14 #\xcc\xe5\x04\x11\xbc\x80\xe29D\xdb\xdb\x01\xc4\xa3\xe8\xb2\x96&$\"\x0e\x08\x13d1\x82*N\x14\x06\x7f\xa8_\xcf\x9dD\x939\xa3\\\x8c\x94\xd4\x11\x8f\xfa\x0e\x07\xa5\xdc\x0eP\xbf\x0e\xab;\xce\x80\xb2K\xe0\x8f_\x8f\xb9I\xe5\xacq\xf2\xe9F\x7f9\x1a{\x05\xbd\x7f\xc9\xd8\x8c\xa3<\xdeb\xf3\xedh\xcc\xd2W\xa3\n\x81]n\xc2\x80\x87\xd4F\x7fh\\!\xcd\xb8\x94\x0c\xda[\xa4\xd7\xb2k\xe1\xb6\xea\x9b\x1a\xdc\xfah-J\xb5\xc1h\xcb\xb0\x8c\xf7\x1f/\xc3`\xc7\xd2\xae\xd0\x8aRcP\x95\xbf?]\xef\xa2c\xb8\xd1c\xbd\x9d\xa4\xcbU\x9a`VJ\x0b\x04e\x94\xb6\xf3\"\xcd\x1c\xd6\x01Z\xa0b\xbb\x02\xde\xaa\xd5z\xb1\xeb\x08\xab\xa6\x8c%S\x96\xd9\xa5\xb9\x0c\x1c\xfe\x89\xbd\x8dV+6=I\x93\"\x8a\x13\xaa\xea\xa2\xdc\xbeK\xb6L\xe3\xbf\xb2\xc0\x8fDvr\x91>:F\x1e\xdcJ\xa2\xe5T\x0bfiZ\xbcN\xf8\xda8\x9d\xd9\xf4\x99\x0d\x810\x1c\xe7\x0f1\xf8\xa19\xd0\xdc\x1e\xe8\x02\xc7J7)\xa05\x84\xb5\xfdYd\xdd\x88\x80\xc5\xcb\xba=\xd5Z/\x9a6r\xf6\x02\x0d\xd9(\xc2\xd9\xe2\xf4\x05\xbf\xa8\xe3\x17Tk\xeft\xfe\x02d\xe58\xf3\xfe\x94bf\xd0=\xea7\xb2\xf1uTD\xfa'p\x04\xff$0\xb0\x81y\xbb\xe6\xcc\xdbcj\xbe\xd7$[\x17\xcb\x12\xda\xe5\x0cK\xac\xd6\xd6\xaa5\xca\x01\x11?1\x0b\x16\xb2\xc0\xead\"\x0b\xac>f\xb2\xe0\xc0,X\xe1\xd2\x99\x97\xe4S\xac\xbe2\xde\xcee#O\x9eXC\xbd\x11\xe2\xffc\xf3\xfa|)?y\xfa\xf8\x19\xcd\xe6^\xff\xbal._W+\x1d\xb4C\xe5k\x13\x81\x06\xa3l \x8eR\xa7\"Y=\x9a&\xb9\xad*\xd4\xaf\x18\xf2\x8aM\x12\x1a\xefL\xda\xe1L\xcc\x02?\xeb\x952\xb3\x8a\xe8\xbf\xae\x19\x9594\xe7n\x0d)\x90:\x04\xfd\xd1F:\xab\x19\x06%r\x98\x8b\xda\xdbQ\xfb\xdc?\xb1\xbb!xb\x1f{\xf4A\xa0?\x9224r\xec\xd4#\x07>-\xf5\xd7\"\xee\xc7\xa9Hl\xcf\xe9\x91a\xbf\xf67\xf4u\x0fdn\xf3U\x96\xaer\xf9\xf7$M\n\xf6\xb9h\x81#\xb4\xc2\xf2\xebe\x10\x12\xe1\xd8\xcbb\x7f\xd5+\x89\x9dK9\x8d\x98KC-\x95\x9c\xc2\x0d\x1fp\xc2&\x85\x16\xdb\xa4-\x80\xeb\x8dL\x8eo\x9a_\x7fE31\xe6S\xd1'\xd5\xa3PD?\xbe\x96\xd1\ns\xd0_\xa4\xfc\x04@\xdb\xe7v\xa9\xc1h\xb0}\x9d\xf1\xde\x9a\xba\xc7\xd4\x1f\xf7\x9a|\x0d\xfc\xa4\x8c\xf1D\x146d\xf6Ij7\xee\x0d\xd4d#J\xb2\x01\x15\xf9\xadP\x107t\x1f\x96rl@5\xeeC1Z\xa8\xc5M\xef}\x96\xde\xc4\x9c\x97\xef\xd0\x18 j\xa6Y+j\x82\xe0\xb16\xa3Qn\xf2t_:\xdf@\x97Zh\xd2W\xb1\x81`h$\x0ci\xb4\xf4j\x8c(]r\xc6)\xe7\x8c\x1b=\xa7by\xd9JS&\xd2\xba'\x1670\xc9(\xbd\x0c!\xc3\x7f\x19\x99\x88\xa6i6c\xbc\xacp\xb0\x9f\xc44\x85\xcdc\x830\xde,\xb1C\x9d0\xb8x\x1c\xf58(\x82\x9b|\xeb\xa4\xff>\x14C\xa4\xac\xc5\xda8\xb6\xf6\x93\xe2\x8a\x03'\x12Z~\x8c\xb2G\xa3^\x13=\xb5\xa9J\xb1)U\x11\x14e\xa2\x90\xfb\xe7x\xb1\xf8\xc0&,\xbeA\xa1%o 2&\x81id%\xf9\xa3M\xb8\xda\xbd\x9b\xd2\xd4\xafM\xa4\xa7#y\xdc\x944\xaa\xcb\x06\x0e\xd8e\x1d7\x14 \x8a\xa4\xd3\x96\xa6\xee\x8b8A\x18\xb9n\xdc\xf4\xa7@a#\x0e\xc1\xcb\xd2\xb4p\xdd\\\xa8\xa7\x9d\xa5\xdb\xd8\xec\xc1A\xfa\x1a\xc8\xde\xd7P\x97B\xc9\xedn\xc5c\x03\x8db\xa9\xaaY\x08\xde\xf1j\xe55\xcc}\xde\xabl/x\x7f\xbek\xe6q\x88\xb7\xa2\x81\xc5\xcc\xb4\x1aUTJ\xb3$Z\x12z\x8e\x16\x90{\xd3\xf8\xc6\x92\xe5\xd5\x93\x17w\x0b\xd6\x14\x14i\x15M\xa7\xe8B\xee\x0d\xd8\xb2\x01k'\xe9\"\xcd\x86\xe0\xfd\xff\xa2(r\xe4\xbd\xb3W0\x04\xef\xff\xf9\xdf\xff\xb7\xff\x03<\xf7\xf9\xea\xc5\x9e\x00\\\x08\xdeI\xe9\xa8.\xd7\x96/\x0c\xe6\xbf>\x84\x02\x8e\xc0\xe38\x0f%\xb5\xf0`\xc8\x17\xd1\x0b!g\x0c\x8a9+\xbd\xe3=+\xe4w}b\xb7\xad\xca(\xb5&\xdd\x18f\xb9B[>\xab\xd8o!oW\xdcx\x9c\x7f`\xd1\xa4h\x17.\x9a\x0dI\xf5\xa7\xf3\xd1\xa5\x9e\xf2\x08k\xa7:\xd0\xc2\xdf&N\xfe6i<\xad\x92{\xf0\xb7\xd0*\xd5\xd1'RB\x9eHI+\x9f\x0b\xdd\x89\xb9z6%\xea\xea\xa9\xae\x02:\x9cI\xea\xe9 \xe1&n\x1a\xdcI\xc2\xc5\x1bwz\xda\xd2\xbd\xa8Dl\x01\xa3\x06\x0d\xa8Y\xb5\xed\xde\x1dZM\xfdJ\x06\x95\x91\xb7\x83Yy;\x88\x96\xa9\xe2v0\x85\x17\xc0\x9eC\xba\xbd\x1d \xd7Y\xbb\x1dt1\xb0\xa0\xdf.\xe9h\x9b9 \xd7\xc9TP\xb6XOG\xc5\x87\xea\"\x92\xe36\x89G:d;VL=\xc27\xbb\xc0c\xc6\x8d\x1f\x8e\x99Q\xd4\xddPgW0\xb4\x94\xc6\xf6\x19\x9d\x86\x10\x9b@\x8ag\xe0\x97\xc6[U\xe2\xbf4\x90A+\x13v\x0b\x17w+v*\x12x\xbdcl\n\x11\x88\x0fB(R\x981\x0e\xfd\xa8:#z\xf0s\x94\xc3u|\xc3\x12\x880\xd5\x8d\xaf\x99\x04\xa5\xfcPY'BM>\xe5\xe7\x89q\xe1\x9aZA08\xd6 \xa3-3*\x84\\U\xce\x8b\xc5\xbc]\xe4(\xb0\x1b\xfe\xf3N\xb1\x9f>\xfa\x14\xe0\xcf[?\xc2\x1f\xb7\x82[\xf3\x99\x1f\xf4\x16\xe9\xb5\x0c\xeeR\x9d\x86\xb38\x99j\xc7\x1e\xe70$\xb3Q\x0e\xa0\xd3%\xa1\xdb|_Nx\x08\x89\xff\xe4\x89i\xc8W\xe9\x8c\xeb\x97\x03]\xba\xa4\xaf'\xdc\x03\x99G9^\xb3\x0bG\x89w\xe9\x94\xe5C\x18\xddX\x12\xc2:\x04\xe1V\xa4\x90\xd5w\x10T4\xdb\x16\xb1\x93\x1c'\x838\x94\xd7x\n$x\np\xc4Jz\xf2,\x80\xa1\x8a_\x87\xb1\x89\x9d:\xee\x05\xca\x11\x92\xfd\xec)\xa4\xc6hl[\xfd\xc6\x03\xd0\x81\x8e\x8dwR4,\x0b\xa1U\xd1\x1b4\xb8@\xd26[g\xd0\x84\x1b\xec7\xf1\\\xf5Q\xcbKC\x93\xceO\xd1b\x8cz[\xc4K\xa2\xc4SE;\x8bt\x12-<\xbb\x06[F\xf1\xc2~\xbdL\x93bn\xbfN\xd6\xcb+F\x8ck\x15\xe5\xf9m\x9aM\xed\x92\x8c\xef\x07\xfbu\xce\xa2lBtP0b0\x9c\xef'\xde\x923^gD\x03\xb7\x8c}\xaak`\xdb\x94tN.W\\N*v\xb6\xfe\xab\xce\xb5\x92\xac\xae\xce\xe5\x16p\x04[[\xd9Hp\xce\x98b\x8e\xcf4\xcaX$+T\xe3}p\xfc\x12\xa9\x03\xcf'\\\x8c|\xc3f\xc5\xd0\x0c\xe1U\xabq\x91\xae\xac\n\x19\x9be,\x9f\x8b\n\xb8m\xf3\xb6}\x98\xf5\xac~Q:\xf8\x1c\x9aE\x17)\xfaK\xf7\xeejm\xb4\xee\xc3\xec\xdb\xe1\xe4R\x83\xfa\x83\xc7\xa6u\xbatM\xb7B\xc1E]\xd4W\x9c\x82\xb7\x86\xd6f\xbdY\x9c\xe5\x05\xaa\xf4\xddZ\x1b\x94\x9f\x12\x112\x06\xd3ic}\xferO\x8aS\x1cC/\xeeV\xd5\x89s\x93\xc6S_\xbc\xc7\xa5\x83\xc3v\x0f\x15@`k\xeaX\x8bU\xd2V\xc5T\xfbvW\xf9r\xae\xba\x15\x82{\"a]918\xe2\xc4]\x04\xd3AMy}j\x15\xde\x04F0\xa6o\xa0\xdc\xdd(\x07}\x1f\xcbz\xb3t\xb2\xce\xcds\x86v^~\xf0\xdd\x1f%\xf1\x12c\xdb\xbf.d\x90\xfb\x93t\x9d\x104\xf6*\xcd\xa6,{\xbd\x8c\xae\xd9\xd9\xba@\x06\xbf\xa1\xca\xf9\"\x9e\x10$Y\xab\xf1s<\xa5\x8e\x95\xab\xf4\xf3\x8f\x0b\xf6\xd9Y\xf0\xfb,]\xaf\xc8\xd2\xb3l\x1a'\xd1\xc2Qa\x92.\xd6K\xd7\xdcDan\x17\xcc\xc8\xa1\xcc\xc48n\xe9\x92\xf7i\x1e\x17\xf1\x0d1{^z>\xcf\xe2\xe4\x13]\xf6\x8e]G\xee/1\\\xb1]t\x9d\xc5\xd3\x0f\xd4Xd\xc1iB\x1c\xc5\xb2\xec|\x15%\xee\xc2\"\xca\x08X\xf1\xd2\x13\x84WS\x99\xb3WQ\xec\xeeX\x96\xd3}\xcf\xd2\xa4\xf8\x99\xc5\xd7s\xa2l\x11'\xecd\x11-\x89\xb5\xe7E?9>KW\xd1$.\xee\x88\x02\x1a\xdci\xb6\x9aG\x14\xaa\x14\xd1\xd5y\xfcWb\xedn\xe3izK|\xf0\xd7\xd7\xc9\x94\xc2\xae\xbf\xa6\xe9\x92\x98z\xbcX\x9c\xb9\xc6:[\xa4\xe9\xd4Y\xca\xb9\xd9\x86\xc2,\xfd\xc4^E\xf9<\xca\xb2\xa8\xb1B:\x9b\x91\xdb^\xd4x\x1b\x17,[\xc4\xcb\xd8Y\xa3e\x0c%A(\xcb\xbe\xda\x17p#\xefgv\xf5).\xbc\x10\xbce\xce\xff}\x9b\xfe\x95\xffw\xe6i\x9a\x1e\xa9\x89\xf9\xc4\xeer?\xeb\xe2\xee\x9d\xdauh\xa7\xe3Q\xeba\x0e\x9a:\x11\x13WL\xe6Qv\\\xf8\xfd\xa0W\xa4\x1f\xb90+5\x99\xbc,__ \xc3\x0b\x7f@\xd9\xa4\xa3!\xe8%gf\xf4\xd0\x97X\xa6\xa98\x8d{\xca\xd8\xa2\xf1q\xfe1\x89\x8b\x05\xcb\xf3w\x92i7\xdcs\xf3y\x9a\x15\xf3(\x99*\xad\xd5\xe9\xe7U\x94\xe4\"'\xa3=\xc5\xabh\xf2\xe9:K\xd7|\x8f\xd3\x00\xa8j\x1c\x17E4\x99/\x19Ev\xed\xda'\xb4\xaccW\xc4#\xa4KEA\x8d\xd3\xe4\x7fnR\xf9O]*\x7f`+\x16\x15C*\x8d)\xa1:\xb1;i\x87\xdd\xfd\xc7\xdeiD\x92\xc29F\x81\xa5\x8eC\xba^\xe9\\\x98\xc76W*W\xb6\xfb\xd0~H\x8b\x82\x93\xc2\xa6\x01\x8a:\x9d\x86)\xaav\x1a\xac\xa8z\x8f!\x0b\xf1\xa9i\xc0\xbcF\xa7\xe1\xf2\x8a\x9d\x06\xcb+\xdec\xa8\x1f\xc4y\xd84V\xac\xd2i\xb0X\xb3\xd3h\xb1\xe6=\x86\x8bbg\xd3`/\xd2U\xa7\xa1^\xa4\xabN\x03\xbdHW\x1b\x0d\x93\xf3&\xae\x11\xf2\xb2\x96Ny\x95?FY\x1c5\x11\xca&\xfeG\xafC3\"\xeaib\x87\xd4\xc3[\xf91Z\xc6\x8b\xbb\xae\xf3O\xd7\x05o\xd8\x05\x02Y\xdc\xb2D\xb2V\x0b\xacd\xad\x86\xe5\xf9\x8e\xfe\xe5P\x15\xc4\xf8\xf6\x9b\x84\xaa\xc4\x7fj\x06\xe3K\x85a\xd0`\x1f\xe3\x02\xee\x89\xf0\x80O\xfb\x96\x83\xbc4 \xc2rv\x0b\x1f\xd8\xf5\xe9\xe7\x95\xef\xfd\xe7\xc8\x83m\xc8z\xc7\x17\x17\x1f^\xff\xf0\xf1\xe2t\xfc\xee\xf8\xed\xe9\xf8\xfc\xe2\xf8\xc3\xc5\xf8\xe4\xa7\xe3\x0f\xb0\x0d\xde%]\xa9,\xfe\xdd\xbfXi\xcd\"\"\x1e\xfbZ\x06\x80(_\x96w\xa5\xb9\xf3\xaetkkmG`\xc7\x00\x81\x11\xf1\x9e\xcb\xfd2\xfb\x1a\x1a\xb4\xf9\xeb\x11\xbb\xc4\xb0\xaf\xa8\xdd\x85!\xf8\x91\xf6\xa6\x16H\x9bNs\xdc\xc5\x9e\x10\xf3\x84\xcc\xa3\xfc\x874]\xb0(\x11:\x80\xef\xbf\x87\xad\xaa\xe8\xddz\xc9\xb2xR\x16\xc5\xf9\xbb\xe8\x1dg\xfeT\x05%\xce\x99\x15\x0bx\x01\x83\xb2\xd6\xd9\x0d\xcb\x16i4eS\xab\xaf\x01\xa9\xc0\x03\x89<\x13[\x1f\x87V\xcbo\xa3\xec\xd3z\xf5c\x9a\xbd~\xd5\xaaJ\x13\xd3\xcez\xaf_\x8d\xeb\x88\xc0q\xe0\x90cHj\x85\xb4\xae#@\xce\x8a\xe3\xa2\xc8\xe2\xabu\xc1\xac>\x1d\x8c.f\x9b(\xbf\xf2\x89\xee\x89\xe0\xefM3\xfd\x90\xa6m\xd7\x95\xe5T?\x9c\x9d]\xd8\x93\xfd\xb7C\xcf\xfb\xb7\x0d\xe6i\xf4HB\xd7\x9a&\xd1uXK\xdcK\xf4k\xccT\xed\x8c\x0ePV\xea?\xbc\xfc\xe6\x1f\xc5,'\xf6\xd7Q\xad\xc2\x08U\xc8\xb4Q\x15j ]\x82\x0bF\x8b\x14.\x1f\xa5~\xd0\xf3huc\xe9\x07\xd6\x8b\x14tl\xb3\x0e\xf5\x94\xf6\xff\xe6n\xfc\xf2E\xbcl\xd8@\xfdRE\x1e\xab5\x86!\xfe\xad\x90\xbb\x93\xbe\xb2\xc4\x9d8?Y\xe7E\xba\xac\x16\x15\x01X\x91\x0d\xbc\xc1\x1a\xa2\xf8V\xf5 \x01\xba\xc1*\x1b\xbdtXl9\xc4\\RL\x15{\xa7\xc00#\xc6`<\xaf\x05\xd1\x11\x80ndk\x880\x92\xb6\xe0[a\xe1[\xd1\x8co\xa4\x1f!h8\x94\xf60cW\x9c&T\xbeD\xf5\xf0\xa6\xe2@hw]\x06~l\x913GgP\"x\x8a\xee\xbd\xba\x02\\\x98}\x89\xabb\x13pb\xb9\xe8\xeeT\x9b|\x02y\xf11/\xed>\xd0$Q\x81\xe8\x8eo\x8cK:@\xabzZ\x06\x0e\x9a\xbdQZ\xdfq4\x93\xa4?k\xfb\xa3|\x15M\x1c{\xb5\xfa\xea\xc8\xa0~\xef\xce\xfd\xb5\xc8\xa2\x877\xbc\xe8.O\xed\xe8\xb4\xd3\x8eN\xac\xf6}l:P\xa9\x8c\x8c\xf7\xd8\xa5s\xc4\x8e+|\x9b0\x08Hc\xd0}\x82\x14\x14\x06^Lz\xdaV\xd2(\x86\xdcA\x1d\xf7\xa0\x8b\x0886a.\xf3\x00\xf8\x8a& P\x89\x84\x15\xfaXmH\x15%\xa4\x1a\xc7V\xc7\xf4Mh\x145\x8c\xee==\xf0\xc9\xb71%r\x9e|\xa5\x85\x7fgJ\x94\x06\x9c\xad\nU\xf0\xe3\x06r\x84\x1d\xdb\x04\xc2\xbd\xd9\xab\xa3U' \xee\xddj\x1f\xabG\xc0F1\xb2\xd3\x03\x0c\xfb\x8b\x7f{\x0e\x9fc1J{a\x8d\x93\x9d8d\xc5\x97\xf4>\x12\x17\xe2m\xc8R\xfer\xc8f\"9\xe77\xcaf\x03*lq\xe2\xef\x0e\x1c\x11\xc6\xcdp\xeb2\xcf\x97\xd9\xca\xba\x92\xdc\xb6\x06\xa4\x91lnq\xb1x\xd7\x8bV\xccY\x9a\xa25\xcd\xebW\x95\x0dv\xcd\xdci\xc5\x92i\x9c\\\x7fD\xa3\"\n]\xda\xbe\xc1\xe5\xb7\xb1\xc6\xf0.\x10w\xed\xf2\xcaU\x06C \xf1\x04\xc3\x9aW\xf6B\x94\xfdL\xc5\xb1|\xff=(\x03>\x89\x98>\xeb-\xd7\x8b\"^-\xa8\xb4P\x15\x1e8\xc5=\x82X\xde\x94\xd9\xd8\"\xcc\x81B\x1b(\xf5\xd2UaGEu\xde\xba\xa3\xbbA&\xc4d\xdd\xe5 \xa9\xbb\x1cd#AhG\xe9\xe5\xff\xcb\xde\xbbv\xc7\x8d\x1b\x0d\xc2\xdf\xf3+J\xcc\xacCF4\xad\x8b\xc7c\xb7G\xd1\xeb\xb1\xe5\x8d\xb3\xe3\xcbZ\x9e\xe4\xeci+Z\xaa\x1b\xdd\xcd\x11\x9bdH\xb6de\xac\xe7\xb7\xbf\x07\x85\x0bA\x12 \xc0\xb6<\x93d\x1f|\xb0\xd5$\x88K\xa1P\xa8*\xd4\xe5\xac\x93\xc0\xa4\xd5\x92\xd2B\xdcn\xc1L\x89X\xd0\xcd\x0e\xb1\x8b\xa7\xf9\x197\xa4\xd2\x93\x02\xacPaLU2\xc7[\xf1\x0d\x9e\"\xed\xe7Gj\x82xQ:\x1a\x13\x137\"A\xc3\xa6\xde\x02O{r\xda\x01R\x907\xb3@&\xa0l\xdb!t\x87\xba\xa3#\xac\xb1\xe2k\xe2\xc7\xd3\xbd\xee\x17F\xcc\x12\x7f\xe9\x05\xef%\xa9\xff\x9cW5\x06Mq8\x9f\x84<\xc1b\x19\x99\xecA\xf3\x8c\xd9\x01Nz\xd6\x8c\xe2\x8d~\xb3q_xv\xb8\xf4\x97k\xf0\xc8]\xe7\x9b\xac\xfe\x1b\xeb\xcba\"\xe2\xa0U\xf6\xb6\x8e\xdd\xed\x8c\xbf\x07>QZ$\xc8\x9c1*\xc9\x92:\x89Sn\xb9*\x08\x07et2\x984!?\xf1\xbdI\x8f\xc9\x12\x8eU\xecs\x83\xaeP\xc2\x7fX\xcc\x17EXw\x8d%\x8e\xa20@\xf2\x10\xceoy\xe7\xec\"\xcf|~\xeb\x0e\x04\xdf\x85\xba\x9b\xd8\x0eP\xcd\xb9\xe3*.|\x1ec\xcb\x18\xd5\xe0\x96\x85\xaa5\xd9\xf9_\xc7\xd5kN\xbc'\x92\xa0\xd7\x0dA\xefch\xa8\xa6\x8d\xa8\xf9\x8eW\x13r\x1eu\x16\x99\xbe\xdc\xa0\xc9\xcfF\xb7\x8d\xc3\xee^e\xc1\xa3\xf1\xd3\xe7\xcc!\xc8\xb6\xc6\x06/\x0f\x15\x13\x87\xfa,\xf2\xaaf\xa0\xd7\xec-\xd3\xc6bVmZD\xb2n\xb1\xd6\xc8\x0cY\xe7\xa1e\"\xd6\xfe\\Y4{_Je8\xd2-\xb1\xbe\xdf\xd2N8\xc4\xde.\x99\x7f\xb6\x8da \xd9q\xaf\x19A\x08%Ztex\xb6i*42\xd3N\x0f\xbb\x8e\x07\x9amW\xa5]\x0c\xd5\x15?D>\x13\xaf\x17)G\xfe\xfa\xaaLm7\xb0m\xae\xe7u\x19O\xfbx\xbf\x1b\x91\x80g\xcdy\xd45q\xdc\xf0\xe7\xdd\xfb\x8c\x8a;:\xd3\x0e\x809<3\xdewx\x13 \x19\x93N<==\xb4\x96m\xd6\xab\xf7\x11\xcd\xfb<\x1c\x97\x91\x8fxz\xa2}\x91/\x8f\xee\x88\x98\xc7\x00\xf1\xd3\x0e^J\xb9\xccc\xd9\x92Zi\x8e\x86\xf4b\x86\xb3\x88)\xb1h\x03z\xb9S\xeb:\x84A\xfc4\xa1:z!=D\x11|\x8bI%\xbb\x17\xc2\x0cv]\xbc@Ax\xf9\x0eU\x80\x16\x0d\xa3\xbcu\xbc\xd6\xe6nP\x0bg\xab\x85\xf2\x18\x9e\xaf\xc8\xec\x12\x03K\xf1\xc05,\xf55\xe4\x0b\xf8\xbf\xe8\xa3\x05\xbb\xe0\xfd\xdfH/\x9a\x82Q\xb1\x03\x8a!\xb5A\xac\xf5\xf3\xe8<\xbf\xceHI \x87\xef\xed\x1f\xeeyMX\x89\x04\xd5\xc9\x13 \xf2\x10f6\xae\x98\x16MV,\xb6\xec\xc8\xb7\x1c\xc1\x86#\xdc\xab\xac&e\x16\xa72|\x8b\x8f\xc1%]<`\xc4\xac\x1a\x8cQ3p\xdd\xbb'NPf\xf5\xda\n\x95\xa5\xffF\x8dfK9\xc3&\xa4\x8c\xcb'%\x0b%(?\xea\x03\xc9g\x10\x088\x082r\x0d\x15\x9b\xae/~\xb3\x1a~\x1e\x04\x11\xe7\xb2)\xa3\x83\x87}\xd6zr\x04\x19C4\xbcr\xcb\xe7]r\xc16\xae)7\x99\xc7\x9c\x12\xba9\x89\xdb\x0b\xc3\x9d+s\x0c\x1c\xe1#\xb5G\xec\xd8\xf7\xc2\x86\x02\xb4q\\\xde^\x9c#\x00\xd1p\x8fy\x8f\xcbGk\x96\xc1\x97\xb9)w\xf3+\xd1\x92\xfb\x95\xea\xbf\x98t\x05\x86s\x16\xc9\xa1N0g\x8a\x1a\xe4l\x02\xcd\xadC7\x81,{\xf3uN\x92\xef\xbay\xd6\x94P\x17}\xd4\xfd\xf3\xdb\xd3\x0f=\xc7\x00Z\x9e\xbf}\xfd\xee\xed\xe9\xab\x0f'\x13\xd0\x88\x02'\xaf\xdf}\xf8?\x138\xe8\xbfY\x92\xfa\xc3M\xe1\xc4\xb8\xb7/~;'\x01\xdd\xe8\x11v\x83\xea\xea\xa4\xfak\x9c&s\x11\x15\n\xd1\xd6\xb0 \xf8\xbeN\"9\x05\x98@\x12\xd1\x99\x8a\xa4g\xa5\xef\x1d<\xd2'o\xec\x88\xd4\x067\xf1/\xb5=`\"x\x1f, f\xc68Y\x17\xf5\x8dD\xa4\x97\xf1\xac\xce\xcb\x1b'\x88R\x92o\x9bR\x1f;\xfa\x8d\xb1]\xe7\xd4\xa5\x90\xa7\xed\xb0l`\x90Dl\xa2\x94k8\x82<\xbcS\xd8\x9a7\x07\xdf\x05,Ve\x0f\nm\xf5\xf3\x95\xd6K\xdcpL\xd8\x00\xc5\x81\x94S\x04\xa7Tk\x9fR-\x86\xa9\xdc~\xc4v\xd5\xaf%\x83\x8e\xddb\x82ZK\xfbI\xf5\x01\xdd;\xc6M\xa8\x15\xc8&\x19l_\xac\xb7\xce\xd2\x88\xbd\xfc\x9f$#e2\x93cx\x9e\xc6\x95\xd5! \xf8\xd2j\xb0\xbeO\x9bX?\xad\x89:w\x92\xb8l-\xf9\xeb\xeby\x19\x9aQ\xfb\xe1#\xc6\xe1\xef\xf7rj\x08YB\x97\x81S\xec \xff\xa0\x9fiD\xd1\x94{\x91\xa7\x11,\xbc\x89\xe7.\x08H\x9c\xa1\xfc\x8b\x86\x7fW\xef\xceItIn\xe0\x18\xe2\x88T\xb3\xb8 >>\x08P\xc5T\xe7,G\xaa\x7f\xf8H57\x12\x7f\x8d\x89\xd9\xd51=\xa2\xc7\xc6\x9e\x92+\x9e\xa7\xa9\na\x16\xea\x13q\xd2E)BLr\xc2gQ\x1b\x04 %\xd2\x1e\xe5\x00\xd1\xb7\xcb\xbb`\x92\xaaxD\xf9\xaa\x9a\x13\xa2&\x94\x9a\x88\x94\xd10O\xbc\xae\xc26\x89'\x0dTy\x17u\xf4\xcd7|d\x18\xf4Or\xf83\x7f\x81 \xf1\x85p\xa2\x07\x8b\xc6\x0e\xa3\xf7\x84\x13\x94U\xeb\x05\x86\xda\xf0\xbc\xae\xb9\xc5\x97\xfaA\xb2\xd0\xa9h\xcb\xb2 \xa1\xc2tn3v(\xeeuo\x7f\x17\xec\xf6\xf7Q'\xe0%S\x7f\xe9N\xad\xc2\xec4\xfe\x92\xd7Q\x04lq\n\xf5\x177k\x02\xe4\x98\xf2\xa9\xf5?\xa2G\xbb\xb4!\xf6\x98\x07\x12\x06\x89\x0c\xa2\x92\x14i<#\xfe\x83\xe9\xc7\x8f\x7f\xff&\xfa\xe3\xee\xb1\x1fL?\x9e\xfdr\xfb\xf9\xec\xc12\x04\xef\xe3\xc7o\xeeyJ\xb5vW\x9f\xa5oT\x10\xfd\xf1\xd8?>\xfa\xf8\xf1\xa3\x1f|\xc6m\x1b\xed\xf2\x07g\x01\xb6\xf4\xcd~\xf4\xc7c\x86\x18\xdft\x03\xc2\xeb\xbd`\x85~\x8d\x8fV\xa7n\x96\x06|hF\xdc\x0d\x10?\x184X\xd8,\xef\xb7\xbf\xf9]\xff\xaf\x8e\xb2\xae\xe1*\xd8\x11\xb3(\xf3\xb5Qm\xf2:\xc6T\xde\x85\xff:.Z\x06|\xaf\xe3\xc2AQ\xd3\xaa\x85\xdbL\xb6\xd6y\x1e\x18\xdb8%5\xfb\xe8\x94\xd4\xad!\x9c\x92\xdaa\x08\xadZ\xca\x10\xfa\xcf{q\xa4\xaex\x92r*#\xbc\x8e\x8b>b\xae\xf8\xcbS\xd2am\x9c\x12\x9a\xcd\xa3\x8a\xd4\xecm{\x0d\xc3v\x0e\xea\xa1\xe5\x9fGK\xd2\xd7@\xb3D\xb8\xc3\x0d\xcc\xb9i\xa0\xe6\xe3\xd8\x16T\x8ew\xde\xe0\x8f?g4\xb4g\xa1\x85l\xf2\xf0@VQ<\x9fkF1\xecx\x0e<\x07\x83a\n\xd6\x98\x94\xfd)\xac\xf4Sh6\x94\x8e)\xba\xe2\x99\xe6\xbb\xee\x07\xc0\xb3\xf2\xe9\x9e/\xad\x13\x03Eg\x1a\xe9C\x1ai\xda\xbd\x19\xd3.&~~\x95\xd5>\xe1\x1e\x9b\xfe>ej\xf74\x8a\x8a-P[\\\xdf-\xb5T\xef\x8ae\xc8\xac\xc7c\xbd8s\xf4\xed\n\xab\x8bi}6~?\x0c7\xcd#.\xe9\x9av\xdd-*\xafq\x15D\xeb\xb8\xf0o\xb6\xd8.\xc3\xe3\\\xb3l\xf8\xddD\xf9.\xbb\xc9 \x00k\x0d\x00\\\xf7\x9a\n\x80\xb5\x1e\x00\xbf\xeb\xffE\x87E\x05\x85\xe9\x99\x8e/97\xf3%yo\x1eF\xf3\xa8+\x99\xc2y\xb6J\xd2\xf9\xab\x17:\x99\x0c\xc3Oe\xd2\xab\xfa|\x8c\xb5\xd7\xb5E\xc8\xf6>f\xd8G\xc6B\xd13\xcd\xffO\xd9e\x96_g\xc8s\xf8h\xc2\x0f~\\\x03c\x80\x16I\xca\xa2\xf2H\xd6\xe6\xef\xd1\x1f\xa7\x1f?~|p\xf6\x80Y\x1c\xef\x827au\xd3$#\xccM\x9a>\x0c<\x14<\xb19\xa69\x9b\xc3\xc5\x0d6\x9b\xc9\xf7\xaa\xf3\x87nB'}\xb8k\xf4\x05\xde\xef\xc9\xba\xa8o\xb0\xc1q\xf7\x1b\xde\xefk\xf2\xa96}(\xd4\xd8\xfc\x8f \xff#\x9a'U\x91\xc6hY\xca\xdc\x98\xf0i\xc6\x7fJ\x80\x0e\xce\xec\x93\x01\xa3B\xc4\x90Sz\xde\xbeh\xba\xd1Z\x97\x94\xa2b\xa3\x91\xefW\xcaE\xa5\xb7\xd7\x19)_\xbd\xe8a\xab\xd4\x8b\xa2\xe5\x8c\xae\xef<\x08B\xb8\xc6\xfc\x91\x80\xb1\xc8\xcf\xab|S\xce\xda\x1cE{'\x9d\xf6\xb4\xb6yvJXH\x9d\x92dcL\xab\xf4\xd6\x92\x14\xd03\xdf\xdb\x7f\x88\xd1\x923\xb9\xa1\xe8\xee\xeaW\x97\x92z\xc9$\xf5\xb2\xa5\xbe(\x87-\nY\x8e\xb9\xd2\x90Z\x1f\xb8\x0e/\xf7\x13\x93m\xa1\x1ck+:\x7f\xdc\x8cY\xaf\x8c\x8b#\xc2\x83\xf9(\xcch\xeb!6\xbaO\x1b\x8d\xa3\xa4z\x9do2\xba\xc9Xo\xdf\xed\xb7;+\xe2\x92d57\x90R~\x1ea\x8cr\xe5\x01^\x8e\xca\xd6\x0f<&\xec\xc9\xf7.\x176\x1d\xd5h\xf6\x03Y\xe4%y\xdd\xbaAu3\xe7/}c\xb8H\x0e\x87 h2\xaf\x03FSc\x03\x9e@\xa6\xaf\xc0\xec\x9e\xcc\xf6oby&05\xac\xbd\x84\xb9\xd9V\x8f\xc55\xe4\xc1s\xc6Z#\n\xc8\xfd\xc4\x1b\xd1\x83n\x9b\xddC1JA\x194\xfe\x91\x98\xd5\x8bb\xd5\x1b\x96y)\x87N|\xfd`\xea\xf6V\xae\x95a1\x97Va\xf1\xa6b\xf0\xc6r\x95\x92g\x030\xdbf\x8c\xa8\xc7m\x01\xac\x8e\x94\xb5\xdd\xdd\xb5\x8c&[\xdf)\xc8X\xa4\xc7\x16\xa4\xf6\xf5\x90\xaa|\xa2K\xc7x!\x82\xf7\x0f\x8d\xbb\xd8\x94K\xc2\x87N\xe6r\xf0\x95\xc5\xd5\x14\xc3j\x9eF\xe7EI\xaeHV\xbf\xdb\x94\xcb$3*j[\xc9\x94\xf6\x9e\x02\x81\xef\xe1B\xd2fb\xa6\xcd\xb4\x9c\xfb\x17Sr\xe6\xaa8\x03\x9c\xf8@\xd0\xfa\xe1[\xdaf\xb7\x7f\xc9\xe2 \x85\xcaN\x17\xa9\x86\xfa^\x92\xfa9\x8f\xecW\xc7\xb3\xcbg\xf39\xc9\xe6\x9b\xb5\xebHtVO\x836L\x82~\x9c\x0c\x86\xaf.\x99\xe5$Z\n\xe9\xcf\xbe\x1av\x8f\x18\xeb@\x1a\xae\x81s\x11\xd2*\xcav\x9e\x80\xa2\xe4Z\x88\x08\x87\x06\x8aL\xc1N\x9b\xcf\xa3\xf39\xb9\xd8,_\xbd0\xae\x00\x8e\x0d\x99\x9d\x16L\x7f\xb8y\xf5B\xc4\x9c\x17EcB\xdb\xfd\xc4\xb6\x14\x12\xcd\xf9z\x00y\x1a\xb0!|B\x8e\x9f\x08\xce\xeb\x1d\xdf\xbcC\xc8\xd3\x15i{\xb8\"\x8f.7\xfc\x18\xc4T*\x124\x12\x0b\xa6\xf5\xb4t\xaf0\x8f\xae#\xe8\xf0\xb1\x83\x839q\xf3)n\x1at\x1d\x84\x03\x18\xc4\x19\xe9\xd4=g\xb9]\xbbw\x87\x01\x12\x0e\xb6\xefpT\xecO\x89\xf2n\xa3{'\x19$\xb7\xe19@G\x1e\xcfk$Gi\xff\x15Y&UMJ\xc2\xe8U\xdc\xe5@\xaa\xd5\x9b<;\xad\xe3l\x1e\x97\xf3\xbf\xc5e\x96dK$\xbe\x0e\\\xb0\xf1FB\xa4>,I(\xf2\xc2N\xaat\xd8\xecH\xa2N2\x94;\xb5/\xc6\x86\xda?\xc5\xa7\xdb\x1b\x010G\x97\xeeu\xbf\xde\x9e\x969\x1b\xba\xe9{\xa09gH\x14\xcf\xe7'T\x80\xfc\x91{+2'\xa8\xeeSn\x1e\xb6\xb3\xaf\xb5\xadn\x1a]\xe7Wc\xd2\x8a\x08\xff{C_c1\x90\xc5\x9b\x881\xa4'6\xc9'\xd3<\xf0=\x8a\x00\xbb\x0c4w<\x959\xd1w\xb3\xcd,L~\xb5\xfd\xed?\x8b\x8bzS:\x06\xee\x80\xedW~\xef\xae\xc15\xb0\xf2\x9a\x8bKQ\x06`f\x1f]\xa9\xff\xd8\x05\xcc%\xe7\xa0^\x88$\xba\xeaL\x8d\xe6\xdf\xad\x84kwA\x0d\x1e\x1f\xe8\xc2\xf8\xd1\xe7\xfaP\x11\x87\x8f\xba\x99\x00\xb8[\xddw\x07A\xbb\xfd\x8d.M/\xf3aM\xf2\xecy\\\xc4\x17I\x9a\xd4\x89=u\xc2\xd5\x97&\xa0\x80\x8e\x14\xe6\xb7SQ\xdc\xbb\xc7\xb2Ox<\x8d\x00^\x1b}\xfe\xdcKI\xc1\x9e\x95\x1b\"*\xceXL\xff\x93yR\xc7\x17]\xa7`\x93\x03o\x92g\xaf\xb2E^\xb2(\xf4\x16\x0c\x17\x1a\xb6x`Jz4\xc5\x18\xfb\x04\xdd>\x8c)\xbe+1\xa0\xf7\xccc\x1c\x03\x1cj\x97\xc8G\xb7\x91M\xa4\xce\xc2'Zy\x1el'nI\xaa:/\x89l\xc7i\xf9\xd9\x05[lJ\xda\xc3tZ\xca\x9c\x0d\x13\xc6j\xedi\xeb\x14\xed;G\x9c\xe9\xc7\xab\xb52\x84\xdc7\xe5l`\xa1\xe30!\x90\x19z%\xd6\xd8D\x95\n\xbe2\x84*\x08!\xf1\xcb\xe1\xd0E*\xcc\x9d`\xa5\xd7\x1azr\xda\x18l\x1e\x13Q\x90\x007\x96\x1e\x83*\x16\x93^\x81\x17~\xa8\x87,\xc9\xe6\xad\xaa'\xd9\xbc\x8f\x15\xfd\x81I\xebP ^\xd9B\x7f\xb3\xab\xbb\xd6\xb4\xf1m\x12a\xbf\x1f\xee'\x87\xb8`\xf2\xf5\xcc\xb8\x8eD\x08*\x01\xf7\xb4\x12\x18b>)8\x10\xefg\x11=1\x10\x80\xbe7[\xc5e<\xabI\xe9\x85p\x9f\xa7\xf9\xe2\n\xee\x01\xb1\x04A\xcc\x1b\xa2\xcc\xe3`3\xdaV4Y\xfa\xb9\xddR-\xd2]\xbd\xc5\x98\xf7\xd5\xb0*\xe1\xf3\xe7a\x941\x98\xb8\xe3\x04F\xaa\xef+\x03\xf2[n\xd0\xea\xa82\xe3*3\xbb$\x99&\xd6\x15E\xc5V\xaa\x7f\x91\xb6\x9b2w\x86\x1d\xd4\xdd \xb4v\xd8\xd9\x0bp\x04\xaf\xe3z\x15\xad\x93\xccG\xa7\xad\xd6b\xfd\xc6\xfb\x02\x1dt\xf86\xf8@>\xd5\x83[!\x89fy\x9a\xc6EE|d\xe1\x12\x13bg\xf2e\x0fYs\xb8\xcf_\xb3Y\xe9\x12\xcf\x8aH[\x95\x82\x93CQ\x94\xf4<\x12\xcb/\xb8\x15\x8f\xe4\x96\xe2\xa6\x830>\x01\xee\x8d\xd9q\\\x11\x02\xa2XO8n\xfe\x14\xdcy\xd0\x84\xe2\xeb+B\xf5\xea\xa5\x86\xf7\x9e\xd5\xc9\x15Q\xf2\x08\x91\xe8\"\x9fwRH \x81z(\xbc\x8f\xee\xbb\xdf\xb5\xff\xda\n\x9cW6\xef\xdb\xc7z\x86\xb3\x17f:\xd6\xfb\xea\xb2(\x0e\xfb\xdfv\x1b\xafZ.^}\x0f\xaf\x94\xf5\xf2\xb0+\x15\xcf\xf8\xf3n?\xcc8\xfe\xf0\xdb\xee\xf3\x82\xcf\xad\x1bub\xce\xfa\x17\xe1\xb0\x1f>\xea\x0e`\xc5:z\xdcy|\x85\x8f\x0f\x0e\xba\xe3Z\x8364\xdb\x92u\xdf\xcb\xdfu\xc3\xb9\xf6n3\x17\xaa\x03\xdb\xfe\xc3'\xddQ\x9d\xf3\xee\xbb\xd3\xb9n\x1c\xdb\x92~\x00\xe4N\xe5\x13\x8cQ\xa6\x8b\x1f\xdc\xaa\xf6 \x8e\xba\x9e\xd2\xa7p\x04O\xda\x8f\x9e\xd3Z\x9dj\x97\xc68\xde\xcf\x8c&h\xcc4L&\xcf\xa2\xbb\xf6\x14\x1fu\x93qMZ)\xc8\xba\xac\xae\xce:\xec\xad\xb9Sz\xb6\xca\xa0\x80\x8c\x84\xabO\xfck\x96\x8ew\xd8\xfa\xec\x9d\xd8n!\xf2\xa4\xdd\xbe\x90\x96\xb7\xa9\x06%O\x8b\xa8\x9f5\xdbtv\xc6\xe6\xe8=\xec.\xd1\x14\xf2\x03\x8e\xc0C/~\x16\x8ck\xc2L\x155w$1\x1cC\x0c\x13\x88\xbb\xf6x1\x9a\xe2\x05\xa1T\x95\xd5\xc9\x9a\xf4\xaet{\x13\xa6\xfb~\xd5\x89\xf3@\xc1\x94\x85<6\x01w\xa9D\x07\x98n\xf8\xa8DU\xcd\xd1\xfe\xe8Q\x95`\xc8\x81s\x16\xbdC1\xa0\x88\xcek\x0eD\x1e\x0e\x89e\x87\xffQ\x8d\x88\xf0*\xabsLa\xbd\xc1\x85\"\xb8P\xd9\xb0\xb5\xe4\x07eUuKJ\xc9\xe3:B\xe0\xbe'\xb3<\x9b%)\xf9P\xc6Y\x153\xfeuI\xeawy\x9e\x92\xb9\xbf\x83\xcc\xc1,\xdaT\xe49\x9e\xe6|\x01;\xb3\xce\xa3\x82\x94T\x02\xf5\xdf \xb1\x11\xe4|\x10\xe1`\x7f%I \xe5)\xf2\xe1i\xbd6\xe9\x8d\xf0*d/\x84U\xb4\xc94\xeb\x86\xd6D\x9d\xed)\xf8\xec\x9e\xf4\x15<\x85\xbaI\xfb\xf74\x80\x9a\xab\x81\xf0\xb7\xaf\xbc\x1b\x1e\xec+\xb3\xa5\xf0\xb3\xf1\x96\xc2U\xa4\xcbj\xae\xf3Q\x13f%t\xe9>\x7f\x86\x9d,:_\xe5\x15\xbf\xdb\x18cC\xfc\xb3\x91\xf4\xec\xf8;\xdc\xdeU\x02u\x07\xfd\xde$\x1f)\x9f\x9dj\x9e=\x1f\x06\xdc\x1b3\xe0\x1c$U\x0e^=\x9b\xce.\x88\xef\xdd\x1b\x0fN\xdc\x06mX\xf20{\xfd\x9bW\x93e-\xbb\xf6\xc2\x16\x9e\xe7Y\x1d'\x19)_e\x8b\xbcO\x05z\x07\x83\xf8\x8bN\xf1}\xffl{a\xb3\x88\xc7\x08R%^\xbe\xc2\x11\xbc\xefZ\xa95\xc3}\xa1\xf8(%U;\x88\n\x0f\xe7\xf9\xa2\x15\xd9\x06\xe3\x11\x0d\xf4.\xe6N\x07\xa0\x10\xfdfn\xb4A\xde\xd3\x87\x1e1T#\x82\xd2\xb9\xff\xd8\x93\x8c;\xdfL\xe0E\x87\xeb\x10A\x11\xaa\x1fn\x18\x01B(L\xe0\xb2\xc3\xd4a\xa2\xd4\xd7y\x96\xd4\xb9K\xc4\xc7\xae\x84\xd1\x112\xcf\xd9\xbd8\xedl\xc0\xd2U\x7f\xe8B\x03\xb6\x1f\xa3\xd6\xb8\xfc2\xb4\xab\xaf\xaf\"\x92\xfdcC6\x82T\x8b\x00\x19\x92x\x86L\x08\x95\xf5\x9e\xc7iz\x11\xcf.\xd5\x8a\xb9F~\xa2\x87\xd8\xe0\x9c\x196\xbc!\xd7\xd6ik\xe7\xfc3\xcf\x19R\xfa\xde\xe1w^\x10\xc2&\"Y\xb5)\x89\x92\x14\x97\x03\x02\x93J\xf77\xab\x10=1\xde<\xc6\x13\xee\xd6XG\x17T`!sf\x0dQ\xf9\x1f\xd0\xacY\x8cJ\xdf$\x0b\x8c+1\x89o$#\xad\xb8\x9c\xc6g\xf4\x8bp8\n\x07\x83\xd6\xe9\xe6\xa2. \x9e\xf2\x92(8C\xacc\xc6\x82\\`\x11\xadbT\xaerH>\xa6\x90\xfcQ0\x1f\xba\xee\xd4N\x1c\xd6\xf7\x8bF|\x15]\xc5i\x82&#\x1c\xeb\xfc<\xe4|\xde\x8b\xb7\xaf9A\x11\x96\xec\xad0C\x0dr<\xf1B\x93\xad\x8c\x07\x94\xaa\x93\x18\x83\xa3\x15qU%\xd9\x12b`\x95!M. \xfca\x9e\\\xfd!\xc4\x97\x80\xfdr=\x85\xe8\x07\xdf\x07\x90\x97\xf0\xfd<\xb9\x82\x07\x7f\x8a\xd0-DL\xd0\xb1\xc7YJ\xdb\xc7\x0e_\xe6\xf9@w/\xf3\x9cu\xf62\xcfEg\x99\x1a\x03Z\x89U\xc6\xf9f\xec\xf5\xc3*\xa9`\x1d\xdf\xc0\x05\x81Y\xbc\xa9\x98W\xcd&K\xf0\x02!\xc9\xb38Mo \xcd\xe39\x1dP}\x9dC\x92\xcdIA\xe1\x9b\xd50\xcb\x8b\x84Tt\xc8lL\xdc\x07\xc7\xb0\xa5\x98\x9fX\xdc\x19\xf9\x0b\xd3m\x1bR\xf8 h\xe2\x9ci:\xb0\x9a\x9fRq\xbb\xe0n\xa7\x06\x05\x122H\xe7E\x99\xcfHU!o\xc6\xc3\x99\xfaUt>c\x7f\x1a\x15B\xf4\xeb\xa5~\xe2T\x92\x7f\xe3\xeb\xf2d`\x12\x8c\xa1QSa?\x1d\x12{\x0cSY\x80\x7f\xee\xcf\xd8\x15\x80Y\x07L{X\xb0\x1e\xfaB\x05\xe5\xde7\x17i2\x93\xf1\xbb-\x96)sa,k=[\xd4\x9237\xf3\x85\xf9\"\x14@\xab\xa1\x17E\x9eq\xba\xc3\xd2O1\xac@\x82\xa4d\x1e\x84\xb0\xd0\xb6\xa3\xbfk\xfd\xb1'\x07<\xc3\xd8xvS\x0e\xe0\xc0]!\x1f\x99\x19\x00\xb7\xa6\x12\"r\x84;o}\x93\x82\xfd\x06\x8e\xe0\x95\xb1\x89\x0b*\x82a\x13)\xfe\xab C\x00\\9\"\x89w\xf7d\xa5\"a\x16\xc2E\x08I\xe0\x88\x08\xc6C\x8b\x1bK\xe3\x92^\x07!\\\xdb\x8f.\xb7\xfb\xfcf\x95\x07N Ud\x1c\xce\x08\xa2_X\xdb%\xd6\xcf\xcd\x81\xf8p\xcfD\xe6j\xdc\xed:\"\x83\x8e\x0c\xc6T\xb5\xaf\xd0n{_Q\x96\x7f\xe0\x01\x020\xd4D\xa3\x9191\xd0/!V\xed; '\xaf\xcb\xddc/\xa7u\x8f/9\x0b\xfb\\\xcek\xa1;@\xeb\x98\x9e\xb7n\xeb\xa7F\xf7\xa0;\xde\x93\x10b\x1dD(\xac\x14N\x8e\xb9\xa5\x0d\x86c\xdd\xe0^\x1b\n\xee3\x8ffq\xf6\x9el*\x9e\x19\x8a\x8eb\xd3\xc92C\xc5\x0b2\x8bg+\xc2v:\xad\xa1oQP\xf6M[_6\x8f\x9e\xff\xf9\xe4\xf9\xff:\xfd\xe95\xaa\x16\x99\xf6Q\xdf\xc2\xa6\x97\x93c\xc4\xc7\xe2t\xd8D\xf9\xa6&\xe5\x9f?\xbc\xfe\xd1\xd4Ke\x1b_\x08\xdd\xa8\xbc\xa2\x88\x13b \xb5Q\xe1\xe2Y\xaf\x16\xe9\xba\x90\xa9\x97O\xe2\xce)\x94\x9e\x94A\xa8\xfaWf\xcc\xb1r\xb0e\x10\x8c\x80H\xf5\\\x06\x9c\xe1\x91\xbf\xe5j\x1b\x1c\xec\x85P\xc0.\x1c\xec\xa1S\xf4\xc7\x0c\xfc\x8a\x94W\xa4d\xd5g\xe6\xea\xfa\x99\xe9tWtg\x1dx!h\xaee\xfb4\x03\xb5K\x86F\x0e\x19\xaf\xdd\xd3\xef\x19P\x81\x07\x98r\xd5\x90\xe9'\x94GIV\x91\xb2\xfeP\x12\xc2\x1c\x1b}F\x9d\xe81`\xe4\xd3.X\n\x80P\xb3\xd3kE\xab>\xf2:\xefG|\xfa\x85\xf7O\x87\x8f\xbe\x0d\xf4\xcd\x9b\x8f\xa5\xc6\x0fH\x03$TM*\x1a\xe37|\xed\x98\x95@\xd9DS}\x1a\xa01\x8fN\xb9l\xd0A\xb1\x060\x00\xeb\xb1\xf6;\x98\xc8Z,\xe4+\xcf\xeb\xd7\xb3\xf8\xfb\x82\xab\xbb::?'\xd5\xeb|\xbeI\x89F\xcd\xc3C\xb2f~\xf7\xea\x0d\xc3\xe7b\xbc|4\x7f)\xd5f\x8e\xa1\xd4Z\xd8\xcd\x859\\\xdb\xb4\xeeV\x1d\x0d\xaf\x83r>\xff;\xaaVqA:f\xd3t\xe7\xce\xca\xe4\x82L\x94\x8at\xfa\xa8\xc2\xfa\xc7&)\xc9\xbc=\xe2yR\x15\xf4,v\xfe\x80\xf9\x94\xd5C=4+\x10\xdc\xe1\x12\x84-8\x98\x11W\x7f\x0b\xcd\xaf<\xc0\x14\x16I\\\x89\x90\xb2\xccK\xf5\x8e\x04\x1f\xf4\xb8.\xfd\xddt\xbd*\xf3k\x8c\x80t\xc2\xbfj/\xa9\xde\xbc\xdb O\x95\xcb\xe4\xc7\xdd\x1bJ~\x9b\xdc\xb3S\x14\xa9\xae\xba7\xa41\xaf\xdf\xc5\xde\x0d\x7f\xdem\xbf\xe2\xcf\xbb\x17\xc0\xfc\"\xb9\x97^\x80_$\xf7\xd2\x0b,\xf8\xf3\xee\xc5/\xbbH>x\xa2\xbbH\xce\xfc\xc3\xc7\xddy\xb1\xfb\xe3\xfd\xc3n\xfbW\xbc\xfd\xee\xb5\xfa\x9a_\xabw\xdbY\xf2\xe7\xddy\xb1\x1b\xe4\xde=\xf4\x05\x07\x7fw\xba\xe7\xbc\x99\xeep\xae\xf9\xf05W\xc4\xb4zw\x94\x9f\xf0y\xef\xda\xfa\xb4\xafN\x7f\x0eG\xddh\xda\x97p\x04\x0f\xdb\x8f\x9eQN@\x04\x00|V.\xf1\x12\xa9:\xebD\x18|\xab\xd6\x12\xa1\xeb\xba\x95\xde\xa9\x950\xf4n\\\xe7\xa5\xa9\xf6\x07\xb5\xb6\x88<\xd8\xae\xf2\x9a\xdfb\xcb\xdf\xd3gg\x94g\x9b*\x03.\xe3\x9b3O\xf7\xf4\x87\xcdbA\xca\xde\xbb\x17q\x1d\xff5!\xd7\xbd\x17<\xc7\x87\xee\x03\xd2{\xf82\xcd\xe3\xfa\xf0@\xdf=\xbe|\xf4P\xff\xf2UV?6\xbe\xd9\x7fd|e\xea\xecu\\\xf4\x9e1\x17\x14\xf1\xf8C\xe7-\x8b \xd8\xfb\xe8\x94\xd4\xfdg\xc8\xdf\xf5\x1f\xdf\xac/\xf2\xb4\xf7\xf8\xa7\xc487|\xf5<\x8d\xd7\x05\x99\x9bk\x98\xa6O\xdf\xb5\xe6O\xc9\xbc\xf2\x1e\xc9\xa8\xf8\xeam\xe7\xe3\xbf\x91\xf8R\x02ig?\xd4262,\xef\xab\x10~\x0e\xe1M\x08\xefu\xb7w/B\xbc\xbb\xc9\xe0\x1e\x9c\xf6\x99\xeb\x9f\xf8\xab\xe7\xfdW\xff\xe0\xaf.\xdb\xe7\x03ei_\xe1%\xee\x0b*\xb5\xc31\xbc\xa2\xe3\x90#\x98\xd0\xdfA\x10\xaa\xda\xd3\x17R\x84x\xd1ol\xe7Z\xcd[\xdaa\x9e\xe8\x0c^\xe2\xbdBWJ\xa5\x9f\xbe4\x89\xc1thW~M%\xee\x1fe\xd3\x18\xd5\xf7E\xf7\xe02\xc4\xbf\xa5\x1d\xff\x13\x8e`E[\xe9\xbd\xa5\xe5\x078\xa25\x8e\xe0-\x15\xb8\xf1\xafwz\x05\xc6\x85:\xc1\x8a\x8e\xe2G\x83\xaa\x03[\xf9 \xdb{F\xff\xfa\x01\xb5ToLr\x81\x98\xeeO\xac\xee1\xfcr\x0b\x13Xv'\xff\x13\x1c\xc3\x82v\xbd\xf1_0\x1d\xe7\x04f\xf4w\xcc\x7f\xf7\x1a7\x82F\xf4\xba\xf3z\xfa\xcf3\xd9\xc1\x1b\xee/\xfb\x8bA\xefH\xc7\xb8\xa6\x1d\xfe\x93N\xbf\xdf\xdb\xef\xcc\xbf\xde\xa3\x0d\xde{`!\x18\xcb\xa0\x8f\"\x7f\x85#x\x8f\x9aj\x1d\x9a\xfcU\x0e\xf2\xaf\xfd\x97\xef16#bF\x88~\xed\x0d*\xca\x08`\x92}\xe9\xd9t\x00\xde\xdcbXC\xbf\x14\xbb\xb1D&\xe7}\xd7\x12<\x08u\xe8\x7fn\xeb\xd2p\x9f\xf3\x02\xc7\x9d\x87\xa0t\x9c\xbbvLa\xf6g8\x82\x7f\xc01b\xc6\x1c&P\xc0\x04\xff\xbe$7\xd5\xab\x0c\x03\xe2\xf6:\xfd\x1b\x1c\xc1K8\x16{{\x02\x7f\xee\x01\\h5\xfd\xbf\xd1U\xab\x15\xde\xcf4\x93\xbf!5)1\xc6\x13z\xe8\x9e\xa1%\xfd\x0b\x9c\x8f\xdb\xec\xe4\x93\x91\x1c\xe7\xc1\x93.\x87$8N}\"\xaa\xef\x1e\x8f\x9669<\x12\xe6u\x81W~;\x18Z\xbc\x95\xeb`\xe4\xb8\xf7\x1f\x1b\x92\xc2\x1ety2\xce)?\xd6g\x85=x\xd2}\xbei\xc2\xf62\x0f[\x11A\x97\x1d\xa0\x15%#\x83\n\xdfV\x94\x8d\xe9\x19\x8b\xb2\x81\xce[\x14\x04<\xcc\xc6\xb0{{{}a\x02\xb1\x1e\xe8N\x06\xc1\xeab\xeb\x81v\xd8cX\xb9{\xd4\xf6\xab\x8d\xcb\x9c\xb4\xaeuG\xae\xf0\xe3\xc7z\xcc<\xec\xc9H|\xb0\x8f\x0f\xb7\x1dl\xe2+\xa9\xa0\x99\xc9\x18&\xec\xf7\xbe`\xf0]4\xcc\xa5\xde2\xfed\x1b\xa6\xfeF\xa3Q\xa3@\xaeZi\xd7\xa8L\xe1Z\xc6\xfb\xb0\x0f\x13\xc0\xe0\xfd}\xe2e\xbdc\x93\xa8KA\x1a\x0b\xb9\x82\xc5\xfd\xbc\xbf\xcf\xaebs?i:c\x1d\xa1\x14\xc9\x82\xf7o\x82\xa7\xb0\xbb\x1b\xc3\xf7\xb0y\x1a@\xc5\xcd\x11\xa65\xecB|\xa6?\x17Y\xe3\xfawr@\xa9\xec\x816\xb5/{\xa9\x9f\x06\x90\x8a^L=\x08\xf6\x87\x05\x0c\xcd\xfc\nS\x8a\x11\x96S3\x04\x9d\xdeo\xfb\x85\xefn%a\x0f\xbe\x1f\xf8\xa5\x01A\xbf\xc0\xf7\x91S*\xa6\x15i\x12\xab\x87\xe05*\x16\xaf{Y\xce\xb3\xd3*w1\xb7\x81A\x05c@B\x0d\xd5\xcbzZ\xae\xa6\xf5\xa7=H\x99\xf7$\xea\xe2\xd9\x0dV3\x05\xc9\x1f\x90\xfe1^w\x04N\xd1\x884M\xe9/\xafr\x9b\xc0\xbc^,q\xdayTs\\\x11\xb4\xdedQ}\xc94;3\xd8\xdb)\xb0\xa4k\xd9\x80\xc2\xcf\xfc\xfd'\x07\xc1\x17h\xcf\xbe\xf6\x92\x1bM \xf54\x03\xc3\x88\x18\xbd\xa4\x92l\x91k3\x87\xd1\x92\xe6Km\xee0\xc0\x94\xb5e6\x81C\xfdKT\xdcM\xe0a\xef\xa5\xc659\xb3\x1ao\x82\xb2nSrF\xb9\xb6\xfb\x9a\xfb\xd0~\xd3\xccOs\x96g\x8bdYEi\xbeDs\xc0~=F\x02J5\xdb\x00\xa8f\xa7\x89\x8d\x91`\x97Z\x92 \xcb[\xafDR\xc5\x12\xfe\x04\xfb\xa8\x87f'\x00\xa5\xca\x94\xb0\xee?\x05J&\xcb\xa7\x10\xef\xee\x06\x94F\xd2\ngjkZ\xb2\x89\xa0\xfa\xd3\x91\x12\x92\x95+M\x83)9\x8b\xe2\xa2H\x11\xe5\x06\x0d\xda\xc5\xe9\x1a\xd1\xb5D\xfd6&)f\x17\xee\x1e}\x88\xf7\xb3\\/\xdb}\x8fOY\x05\x8aD\xbd\xf7\xf4!{\x8d\x18\xd8{\x8fO=\xad[>^Vc\x0e\xa8\xca\xe4\x17\x8f\xa8\x99\xf4\x91\xc00]\xa7S\xc2\x9a\x07\x8e21]M\xe3\xd7\xb9vpc\x8f\xc4\xc6\x978\xae\xa5u\xfa\xb3\xc0\xc0`\x90\xce}\xc4:\xbe$\x7f\xae\xeb\xc2\xa7\xc4\x97\xbc\xa4\xaf)Y*\xf2\xaa\xc6\x1f\x06\xd5\xc3\xc5&I\xe7\xef\xc9?6\xa4\xaa\xd5\xe6\xd4\xe7\x06\xd2\xc1r{\xab\x1f\xf1G\xfa\xfa%\xa9\xf2\xf4\xaaU\x9f?\x1a\xac\xcfMM4\x9f\xf17\xfa\xaf+R&q\x9a\xfc\x93\xbc'\x95\xfa\xad\xfa\\\xffe^\xbc\x9a\xab_\xacHZ\x90\xb2\x8a\xe8\xf3\xbbEc7\xdc\x91\xc4\xad\xd6\xeb\x0c\xf0\x84\x9e\x96\x8d\xfa\x84\xfe\x10-\xf7\xe9\xd1\x15w\x1d\xa1\xb5\x8cGQ2\x81\xd2p\xd2\x98\xa3\xe3\xf2.'\xba\xa8<\x1aM\x8e\xe0C\xe8h\x91+\xc8\xc5\xa0Q>W~\xa1\x97N\x94r\xcd\xa7|a\x00=\xf0If\x1anF2\x15k\xceNDx\x0d\x83\xe7wGp\xd0\xb9\xdd\x00^\xb9\xe5\x9c\x7f\xf9\xfc\xd9\xc0A\xb0\xaf\xf5\x90e\xfb\x7fS\xc6\x17)\x19\x00e\xb6Y\x13Q\xc7\xc0\x10,I\x8f.\x01h\x82\x10C\x1d\xd9On\x01\xb0\x1e\xbf\xa8\n\xe9\x96#\x9f\x88-\xd3\x1f\x138Dl\x11\xad\x8c\xc0\x9d:\x9a\xfbY\x08^\xcc\xfd\x8a\xb3\xfe\xd4s\x17\xfb\x18\xde\x9c+\xef\xdaO\xbdRG\x05KL\x05\xb5_Gt?\x1f\x1c*\"\xaf?\x1d\x1c\x82J\x072\xff\xe1\x81\xf2e8<\xf8\xce\x97\xdfn\xfbek\xb4\xe3\xbe\xdc\xba\xcf\xc3\xc3\xc7\xe6O5R{\xfb\xd0o\xbd\x92$\xb2\xd4c\xb7@-\x0dr\x13c@\x1fy\xf6\xdb\x93T\xea\x07\x93\x1b\xf1M\xec\xb6.\x1f\n\x7f\x82\x83\x8e\xb5x\xc3\\\x1e\x9c\xc1q\xfb\xe7\xc4\x98\n\x8d\xb29\xbe\xa6\xf5Cc\xeb\x87\xed\xd6\x0f\xcfP\xff\x1eDW\x07o\x0bRbL\x9aWh^\x12\xd7 \xc6/\xb9y\x9d\xcf5\x1e\x9f*\xa8[\xa9\xddTE\x0b&kP,\x10&\xe8\xf87\x13\xf4#\xf0I\x10\xb0(Qy\xd39s\x84U\xd2r}\xac0\xc7\x96\x174\x86a\xab\xf6'\x01L \xe1W[\xfaE\x1e\x9e\x9e\x9e\xbej\xfd\xc5\xcc\x02\xc9@8K\xdd\x12\x8dC\x00\xfb\x12\x99\xc8\xad\xc0A\xbfnG\x84\x80]\xf0\xce1}P+QZ\xb5\xf3\xff\xfd\xfe\x9b\xff\xf1\xf7{\x7f\xf4\x83\xf3\xdd\xa3\xe9/\x1f\xcfn\x9fN\xbe\xff\xd3\xe7\xe8\xe3\x83\xe3\xf0\xe3\xc7?x\xde}\x96<\xed\\g\x99\x0b\x0df\xb0\\\xe8\xcc\xf3\xb0\xb1\xa1\xdbo\xfa\xad\x95~}\xff<\xf8\xe5 \xbc\x0dD\xd3J\xe6\x12\xff<\xf8\xa3@\x80\xe6\x83\xe9\xf9Y\xf0\xc7o\xf8s\xcb\xc6UF\x851X\xe7~M\x87\xd1\x0f\xa4nX\xdc\xd8v\xa0\xf0\x06\xbd\xfb\xfdtL\xa667\xb66+N\x1fw\xf6\x90\x03q\xc6\xc4\xcaDWA\xdc\xc1\xb1\xe0Vb\xcf\xeel\xb3g?\x7f\x86\x1d\x12\x15q\xbd\xaa\xfa\x8du\xaa\xb3jC\xb1-@Qs\xf1\xea\xfd\nR\xb6\xcf!\xc9\xa0\xd4\x9b\xa8*\xeaXZi\x9a\x1b\xa2\xcc\x03\x87\x85\xf7\xee\xd9\xfbg\xafO>\x9c\xbc?e\x83O\xa2:\xff\xa9(laSD\xb9\xe2\x0eg\xb4\xa7ibP\xa6\x8aB;\x8c\x07\xe9el\x83}\x1cX\x87\x04\xd0\x18j\xdbk\x8aR\x15df\x8c\x13\xa6+t\x95XX\xd1\xdc\xfd\xa35\xa9W9\n]-(\xbb7 i\xfed \x9c\xa8Z4:(]\xc1\x0c4\xbe\xc9\x06]-(\x85\xa1W\xb2D\xe8\xcd\xe0Gz\xa7\x97\xfe\x9b\xf6\xaf\xadT\x96\xa0U[b\xe3\x9a\x0bp*g\x95~\xe6\xef?\xee\x06\xff\x00n\xb6\x86o\xbby(\xea(\xa9\xde>;=t\x125\x98.$/H\x16\x17\x89\x91\x89\xe0Y\x15(\xae\x17\x0d\xae\xd3\xc9\x1ez\x1a\x16<\xa9N\xaf\xe3\xe5\x92\x94\x07#\xc6P\xb1O\xb6\x18\xc3\x81n\x0cy\xf1j\xce\x12\xf0\xd7Q2\x7fY\xe6\xebwq\xbdz\x8d\xf8\xcd\xdcI\xeb(%\xcbxv\xf3\xaa\xff6\xa6o\x97\xa4\x96\xc7\xf9\xfb\xf8z\x84\xf8\xc2\xd9[F}\x8f\xd9Ib\xd7\xd7J\xc9/\x12[\xd7\xbc5\x18!f\xbb\xd5\\+\x11\x8b\xcb&\xa1\xdf;x\xe2$\x83'Nb\xa3z\x89\x12\x19i\xc7p\xef%H^\xa2\xf2\x85\x83\x0c\xca4\xf7\x13\x19\xf0\"\xf6\xf9\x1f\x9b\xb3\xa8\xca\xd7\xc4\xb7\x03\x14\xba+\xc2\xee\x16\xb5uu\x91\xd7\x0c\xd9\x10\xd0>>\x9bK\xdc\x80#\xd8\xd0\x87$\x9e\xad\xd4\x87\x15\x8b\x93Q\xaeQ\xcb\xc5w\xc4\x98\x0dQ\x90\x99~mY\x005D/\xb3\xd4\xa1\xb3\xd9\xc1\xb5F\x96\xaf\x8e\xbe\xf9F\x8emn\xba\x8b\x82\xde\x89m\x0c2+\x0e\xda\xccx\xca\"\x9f\xbd\x17\xc2\xa2uZ\x0e\xac\x9d\xc0\x18\xcc\x92\x15\xafIMJ\x0d\xdb!\x8a\x1cgE\xc7\x19\x07\xb0\xe3\xb0\xe7D\x91r\xe0\x948\xf0\x08;\x9did\x0d\xf6{\xb3<\xab\x93lC4\xa9a\xd4r\xc5]qs\x9f9\x7f\x99\x9cqE\xa1\xddj\x83\x02uK9\xad\xa8tB\xffc\x91\xca3\x8a\xc6\xf8\xf4\x08\xa6\x99ev\xc0\x87\x86\x87\xcb\xb4r\xa8M\x076k\x84\xa6\xfd\x00f}{'\x13\xbd\xd4\x15\x12\x9d\x9f\xe7e\xb2L\xb28U\xc4)\xe6\x96\xa1}\x83\x12\x8cBT\xc2\xf6O\x96\xb7\x9f%L\xe7W\xed\xd6\x81\xe8\\\xab\xbbE\x86\x00Td\xc4\xac-\xf4\xba\xcd\x98\x02\xbc\x80#\x98M\xf7\x1c\x00NKa\x84\x91\xe9\x0d\x15P\xda0*:0\xaa\xac=\x9b\x19%\xfb[\xe4\xe5\x9bm\xcc\xce\x18\xeb\xb6\x04\x0e\x9d\xb9%U\x84ZV\x06\xda\xd7-\x92^\\QzQ\x07\xe0\x15e>\xdf\xcc\x08\x1f\xdc\x15\n\x02\xb3<\xab6\xeb\xf6\xb3\x8a\xcc6eR\xdf\x88g\x9f?\x83\xbf\x9a^\x9d\xa1\xb1\xdb\xd5Y\x08s\xb6\xf3V\xba\x0ca\xddB\x01\xb3A\xc6f\xa5\x909v\xa64\xed\xd0\xbf\xb97\xa0\x03\xc8\x80\x83m\xcd\x14\xf5N\xf5\x81{\x18\x98\x14\xe1\xbar\x03G\\Ab\x9f'X3pt\x8b\\\xa0\x8b\x10\x9d\x16(\xd1M\x1b\xa2;\x0f\x9e\xc2\x8eO\xa7\xe8_\xc0\x11\x9cG\x19\xf9T\xfbA\x10\xcd\xf3\x8c\x04O\xf9\xe4]\xc1%\n\xed\x8f\xb2z\x17,\x00\xa8\xdb\xbcD\x91#>\xa1(um'3\xdd\xc2n\x90N\xce\xc6\x8eZ\x94\xde.\xa3\x0c\xcf\xc9\xb6\xad\x01\x87\xc7\xa7\x91h\xa4+\xa7#QKW\x9e\x8fD7]\x19\x87\x82\xba\"\x17\xf92D\xa7\x95\x0eZ^\xd3\xe5\xa3\x98I\xa1\xe6_\xc2\x11<\xebb\xe6'\x8e\x99;\xf6\xab\x981\xe5\x8a\x87\"\xbf\xdc\x06uu\x85bb\x87\xd7v>\xc5mE\xde\x1be\x1e\x81\xb7\x19*p\xc4\\\n\xc4\xbcq\xfe\xd4q\x9d\xac\xb5\xb6\x150n\xfdJ\x0f\x1b\x8d\xf9K\xef\x89<\x89T\x85\x08G\x8e\xceMQ_E\xbb\xe0J\xd8\x87\xdf\xe9T\xb4\x85P\xd1\xf6\x82Z\x03\xf7\x17\xb6k(\xf8\xf0\x98\x07\xa4b\x11\xa1\\\x15rs\x08\x8d\x06\xab\xdf\xe9jL\xa7D\xb9w\xfc\xfb\xc7\xeb\xb3\x07\xcb\x84]\xfe\x0d\x80u\x9c\xe9\xc1\xe3'\x036\x16\xffo\x98\x1e\xdc\xcd\xd5s\x9a\xc7\xf3S\xa3\xc2\xb0\x94\x9c3\xd3R\xd0\xe6\x0d\xe9\xdb\xf5\xc9\xc6\xe4\xdb\xcb \x90(\xbf43\xf2\x9b2\xa5U6e\xca\\\xc5\x8c\x15\xab:\xae7\x15\xe6$\xc1\xbfl5Y\x8aPQ\x9b\xfe2\x7f\xb1\"\xf1\x9c\x94\xd5\x04\x12\x9fD\xfc\x87\x81B\xe8\x1b\x89\xe1\x08r\xf1\xe5\xd4\xe3y\x84\xee\xd3\x9d\xe7\x19\xf4\x10\x1b\xccC\xf9\xf93\x9c\xfb\xb1\xd9\x0f\xca\xdf\xa0kKM>\xb1\xf8\xe5\x17i~\xc1\x14X\x17\xe8'\x1e\x88\xcd\x1c\xd5+\x929(\xb9)\xc9\xceY{hH\x97G\xf3\xb8\x8e\xd9\xdf\x9b\xc0r\x00]\xf5\"\x01;(\xea\x84\xa63.\x8a4\x99\xa1\x02\xe9\xc1\xcf\x15\x8bO\xc1\\w\xfer\xfa\xf6MT\xc4eE|LA\xb4l\x8c>\xe3\x05\xf91\x8f\xe7C\x0c\xf4-\x1d\x85\x0e\x84\xa2\xe4\x98\x01\x01\x8e(\x85\xc8\xa3\xfc\xe2g0j\xf5\x9dX\x83\x9c\x8d\xf5\x84\xdbl\xeb\xb9\x01\xfd\xe9\xc3a\x91\xf7\xa9\x83\x9b\xe1B2\x9cT\xaaO\x19\xf6\x8c\x94a\xafM\x19\xf6\x18e\xd0\xe3\xaa\xce\xbf\x04\x94\xa5\x15\xe3SC\x8e\x10\xa1\xd6e\xf6@:\x1d\xaf\xf9r@ \xba9\xcd\xe8@\x85\xbf \x9a\xfaGI\xc5\x1d\xa1\xa6\xd9Y\x00\xc7\xac\xd2\x04\xa6\xf4\xff\xb3\x10\x7f\n\xb9\x8b\xe2\x93\xf0U\xd1@\x1d\xf1\xb7\x1b,s\xc0ld\xe0\xa4\xd0Gfy\x99\xf0#C\xc4\x89\x13\xcfd\x9c\xd1\xa3\xadl\xaeVm\xfb\x0dS\xe0\x17\x12\x15I\xf1\xa5\x06,\xcdM\xe3,Oy\xd6\x9a\x97\x98\xf0\xcc||\x90(N\xd3\xfc\xfad]\xd47\x18;\xd8|||\xd9\xcc\x8fE\xf2\x1dJ\x1f\xf5WX\xdd\x04@es\xfdb\xc8\xc8\x1f\xfb9\xcb\xdfp\xc1\xa2k\xa8 \xcd\xe5\xd7y\xff\xe3+\x91~'\x9b\xe5s\xf2\xd3\xfbW\x86\x80P\xa0p\x92\xa8\xcdM\xb8j\xe8\xa6\x99]\x1eX\x1dma\xd0\xfc\x16l\x81\x19\x95\xcf;\xf7\xe4:\xee0\x08\xcdW\xbe\xb9m\xa9rfd\xd4\xde\xbf8C\x97G\x18\xfe\x1d\x8e!\x8f\xd6q\xe1'A\xf4s\x9ed\xbe\x17zt\xf3z\xebMZ'\x0c}\xd4J0\xe9\xd4\xd7\x03`V]M\xc0\x0b\x0d\x06\x99\x15\xbe\xfd\x1f\x07{\x86\xf75{\xbf\xf7\xc4\xf0\x9en\xbfj\x02\xdeg\xaf\x0fP\xa4^\x94\xe9\xc0\x14\xd0\x9e\xe7\xb4M\xab\xe1{\xe0\xceU#\xda\x02\xce73U'7Dx\x85\xd1\xd64\x1b\xb8>\xa1\x9bvg\xa7\x8c\xaa\xcb\xa48\xa1\x88\x9ed\xcba\xab\x82\x9c\x87\xeb\xefo\x0bc\x88V\xe0l\x95\x1d\x83EQ9\xf6/\xa2)\xc6^ny\xe2\xbf\x9d6\x82v\xa3Q\x88\"6\xf84\xa1\xc7\xcf\xc6\x8f\x8d\xeeJ\xa2pc\x1fC\x1a\xd2\x10\xf2 \xd4\x05v\x0e)Oo$0\xeb\x86\x9dB\xa90Y\xa0\xe1\x91~\x14l\x85\xcc\x0e\x0eI6Of\x14\xa3u\xf1R\xbb9o`\x00\x8f\xd3\xdf\x8e\x95Aq\xc3*\xf9\x08\xee\xd4\xf3\xd0\x9d\\[=\xc7\xd6\xfe\xb1!\xa5!\x8203\xa9Y\xe4\xe5Z\x7f\xd0\x0c\x86fM\xfb\xfb9 \xc6X\xb3@\x83\x04\xb1\x9fL\xc9\x19;)\x07\x10|`3\x168\x15\x83\x8c\xc3d\x12\xf9\xf29\x7f\xf9\x01_\x9a\xed;P\xe8{\x80\xf4\xbb\x88\xcb\xfa\xe3\x03\n\xa9\xfbT\"y\x90D5\xa9j\xbf\xb0\x9a|\xf08j\xa6\xf8\x9d\x80J\x04.\x01d\xe4\x1a\xe6\xa1\x06\xa8=\xf6\xd4*\xd6\xb06\xa3\xb8(H6gAu\x92i}\x86\xf6\xbdC\x00\xd6om\xa6\xf4\x94\xe3\xac\xfc\xc40\x1d\x1ez\x98\xe1T\x7f\x07j\x91L\x1bq\x058\xf8V\x98)\xb2*\xd2\xa4\xf6\xbdco\x00\x01\xae\xa0g\x0b\xbc\n\xa1\x1b\x8aB-K\xba\x9b\xa6{\x03G ^ O\xf7\x07j\\\xa0=\x86\x19\x85nl\xf8q\x8e\xe9\x96\x04 db\xe6\xcd\x00\xb2t\x90#\xd7 \x87\xeb\xa6\xe3\x8bu>%f%6e\xab.ZCl\xa8\xf4\xf9PFmP\xa9u?\x0b\xa7(&\x8c3\"\xc4\xb5-\x9d\x8d(\xf2fSG\xb0C\x96\x0c\x08\xcfG\x12\xb0l\xbf{O!\x83\xef\x81<\x85lw7\x10bYC\xb8\x87\xac\x8d\x04gRG\x8b$\xadI9~1\xccZ\xfb[\xc1O\xde3\xb9@@\xd3LI\x8f\x84c\x0fv\xf1(\xf7\xfal\x1d \xa3p\x11BE\x99^}{L\xe1u\x04K\xd8\x85\xeb\xb0\xd9\xd4x\x928\xecj\xed\x94\xbe\xb2\xc1q\x08uT\xad\xf2M:\x7f\x91_gi\x1e\xcf\x9f\xa1Z\x8deg%\xe9\xc2p\xdd.\xed\xc3\xfc\xcc?\xe8eK\xa4Eh\xc5\xf7\x86\x94\xe2Z\xa3\xe6\xb9\xd0\xa7\xeb^\xae\x1a\x8b\xe7\xfe\xcb+\xf1Rc\x0f\xad\xba\x1a\x0b\x9b`\xf9\xec\xcf\xec\x8c\x136\xc1l\x07Ri\xf8m\xf9\xbf\xe9\xea K\xce5)\x97\xe4U\x86\xcf\xde\x96\xb4\x02\x1cA\x8ao\xb8\xc3\xb7C\xc0\x1bh\xd6Zz\xdf\xd8\x11\xdf,\x11\xb2]Y\x7fq3\xda\xfa\xb2E\xad\xfb\xad(B\xf2\xeeg\x90a \xbaK\xab\x9b\x03\xaa\x8c\xf5,2\x08\x82\xaa\x01\xbf_\xf2\xc8\xe85\xfe\x95\xf9\xa4\x97\xa8[6\xd1F}Z\xf9\xe0;\x8d\xc5\xfdZ\xa0\xb5\x169\x97\x02\xc5\xbe\xd5\xbd\xbd\x11\xdf\xf6Ru\x02?\xf5\xe4\xae\xd2\x83\xa3\xed(op\xda\xe8\x83a\x02\x9a\xf4\xee\xdd\x1d\xc0\x8f\"\xdbI \x88?=2\xaf\x14S+y\x94\xad\xe3\xf2RRj f\xae\nUL,!\x17Kn\xa0\x97\x01\xf6\x8d2\xc0~[\x06\xd8?\x1b\x08C(Ng9\xcc\xeb2.\x1c\x0f\x14\x16\x82\xfdi\x00\xd5u\xc2T\xc5QQ\x92+\xe4\x8d3\xf2\xc9\xca6\xce\xe2\x8a\xc0\xded\xb0\x0e\x08\xd3,\x93\x10[\xdb\x84X\x91\xc2\x1e5\x02\x14\x96u@O\x1c\x0c6\xbf\x92\x04\xac\xf9\xfb\xf3gL.\xa7\xdd6q\x10\xc2N\x1c\x95,\xa4\x04\xa6)\x9b\x91\xa2\xce\x07w\xb9Z\x18`\xe0\x08\xf6\x1d\x0d\xb1.J\x12_Zk\xda\xef\x87\xe5\xb5$\xef\xff\x11\x9d~\x7f\x1e\xda\xfb\x17\xb5\xe0\x9a=r[3\x12\xd5{\xcc\x1c\x9fdu\x08\xf4\xe7h8=\xf9u\xc1\xc4\x87\x1c;\x00\xe1\x89\x1d\x08,\xe3lmYjlm\xdfa\x1f(\xa7_<$|\xc6&\xe13\x1c\x96/y8+\xce\x81\x19\xbb\x90<\x9a\xb1\x1f~\xb8\x88\x08z\x92,\xec\x1f\x86\xca\x0ex\x14\x82\x8f\xf9\x1eJ\x8c\xed\x82\x071\x06y\xa1O\xcbt\xf8\"\x0b$\xe0\x1c\x90Q\xb2\xab*2\x8aa<\xa1{]=@|\x16\xaf\xd4\xadw\x07,\xa0[A\xed\x1a HU\xe4YE\xbe\x84\x82\x1c|\xf7\xebn\x8d.\x0598d$\xa47\x13\xa3\x0eP\x14\x84\xdc\xc1\xa1\x1b\xe4HT\xef\xb7\x89\xc8\xfexP=\xfauA\xc5\xc7l\xc9\x0f\xc3\xc0\xe0\x82\xbe\x8c\x8c\x18\x9c\xc3Da\xcd}goN\x82\xe5\xd0\x01\x83\x10$.\x1d;n\x04I\x0b\x0e\x9e\xe0b\x1e\xb0\xbb\xb4\xb8\x9e\xad\xfc\xfd\xc3\xc0\x10\xafFW\x9ai\x1c\xda\xa7\x01w\xb8\xba\xcc\xc4\x8b\x8e\xdd\x01.\x87\x0eh\xce\x1a\xf4s\xae\x94c\x19%J\xc5Z#\x08\xf8\x8f\xe7\xf9\x1c\xc3\xc5\xf2\x9fL]\xc5L@ \x97{Q\xde\xc6G\xf5A\xa8\xbb\x99S\x0b\x1b\xa5\x03\xda \x19\x8b\xf2\xcb\xd1\xeb\xf3\xd0\x02'Q\xeev}\xf0\x16\xd1\x0d\x9c\x89\x0e\x9c\x89\x04'}\x1cv\x93\xcfw\x0b\x82\xf1\xe1\x81\x1d\x8c\x92\x8c\xc6\x17\xe5\xa6\xa8}\x8f=\xf0\xc2^ \xefna]X\xf0 +y$\x9b{#\x86R\xd5y1`\"\xa9\x07\xf9-K\x93\x871S\xa7\xc6o\xa7\xf4\xcc?x\xa2\xd7\xf9i\x02\x18\xdc\xea\xd4D|\xa0v\x85t\x03\\\x16\x92\x10\x07'%![(\x8d\xdbnVB\xa125*{\x06%B>\x98\x07\xfe\xcfU\x9e}\xfe\xb4N?\xdf\xc4\xeb\xf43\xa6\x00\xfdx\xf1\x80\xf1\\_|\xb9\xd3\x8d\x10\xb2\xad9\xe1\xc3\xfd\xffxk\xc2\x81\xc1\xb4/1I\xa0\x06Q\xfe\x1eCi\xe2\xd5\x97\xf7\x00\x83\xa0\xe0M\xba]F\x16\xe6\x04\x99`\x02\xddkTS\xe3\xb3\x01\x13)#\xa3\x85\xbaR\xba9\xd8\xbc\x9b\x00\xcfti\xce\x95\xa5\x19GZ5S\x991+g\x9d9\xaa#i]\x0c3\x19\xeeW\xa4\xfc\x0b\x85\xf1\xd2\x8d\xcaiL\x85\x9d\xf1\x19i\x94ua6\xca2\x0db\xee0\x08Q\xb9e&\xeb\xd4\xfaJ\xdf:zAY\xf6\xb8\x88\x9b4x!\xe1\xc5\xf3\xb9\xb0\x8a\xff\xfc\x99\xb2#\xeb\xfc\x8a\xb4\x9f0\x06\xc5\x10\x99\xc6\xb8/;\xc6Z\xa6 ^\x0d\x82\x0f\xa7\xff\xf93\xd0\xb9\"$\xd7\x9b:\x16\x90D\xc9\xfb\xc6\xd1\xd4x=\xd8\xcf\x15o\xdfo\xe0AA\xd7\x07\x80|\x8a\xb7\x16\xbag/\x08)\x9a\xe7n8\xb4t\xc0\xa1\xaf\x8e\xc87Fcl\xb3\x87\x06\x1f\xe1\xa9\xbc\xd6Z\x92\x1aM\xaf\x7f\xb8y\x97'\x19\xa5\x08\xfd\x18\xb8\x00.n\x0f\x82\xbcw\xb2\x86\x86\xda\x88\xd1\xbf3\xff\xbas\xa3\x84\xbe\xecz1t\xeb\x7f\xce_\x1ej\x0d\x06\xae\x87\xec\x10N\xc4\xa7\xda\xdb\xdcO\xe26W\xf7\xf2T|\xaa\xb5~x>d\xc3p)>\xd5:\x0c>\x13o\x1f\xf7\x8d\x18\x9a+\xdc>4\xe3\xf9|2,'\x8b2(3\x81\x90\x9b\xe8>\x1d0\x1c\x1c\x92\x9b@\x91\x9d\xb4\x154\x08\xd6o\x89\x93\x85 $\xbaw\x94\x8a\xde\xe9|9a\xb6Ny\xfb !\xf5\xba\xab1S\xba\xe8\x1a'\x8a8\x899\x19\xca\x86\xa3\xe5\xdc\x06\xdd %\xad\xb7!L\x87\xb6\xa3\x89\x9a\x9b\x0e\x1ae=\xdb\x8a\x0b\xdd\x9a\xdaV\xf1\xaa!\xb6\xe6\x11f\xcc\xeb\xf85\xa9c\x1c\x1d\xa9\x00\x83}\xadI\x8d\xaa\xcd\xb5_3\xd5B\xc7\x8f\\\xd0\xfc\xcf\x9f[xEk^\xe9)\xd7U\xc8\x9b\x15\xe9l\xafl00\x9e\x85\xf5Y\x10\xde\xf1\xc8m\xc0\\v\x0e\xc7a<\xbb\xd0\x83`)A0\x1ee\x14\x06\xe0\xc2\xc8\x00h\x9f\x8a\xdd\xd7{\xa9a\xcf\x8a\xb8$Y\x8d\xa1\xba5<\xda\x10\x83\xd6\xf1\xf0\xac\xed\xf1\xaa\x95\x84\x9aG\x98B\x17\xf1\x95]\x9b0\xbf\x97\x92\xf9\xbd\x18aE\xfbE\x9f\x18\xd4\xc3\xa2s\xb0\xa5O\xf1\xba\xef\xfd\xa3\x01\xc6\"\x8d\xeb\x9ad\x13\xd0\x04}Yl\xd2\xf4\xe6\x8d\x08g\x84s\x1e\xe1;\xbe\xf0g~\xea\x93\xae\xf6\x1a\xf4\xe3\xc8:\xddh<1\x93\xea]\x99\xaf\x93\x8a\x8c\x18D\xc1\xb5\x86s\x9f`,\x14\xa7\xb1p\xcf\xae7\xe4\xda\x117\x86\xe3\xa3\xf0\xa1\xe0}m\xa5U\xb5\x01\xb8\xa8\xdb`\x08\xcf\xc1U\xc4j&\xf7\xaeL\xd6I\x9d8kA\xdcg\xb9\xf9\xcdg\x99T\x7f\xa9\xf2\x8c\xcb`+\xdd\xfb\xe7L\xde\xed\x89i\x16\x84\x92jn!/\x9b\xb4\xdc`\x1a\x18\xefQ\xe3\x1b\x9fT\xaf\xb9&b\x02W\xba\xd7\xcf\xe6s\\\xb0\xa6\xdaZW\xed\x7f\x92\x8c\x94q\x9d\x97#\xe6\xf5\\\x92d\xe5\xfb\x97\xcd\xd7ns\x13\x1fL@\x93P \xa9\x18\xdb=\x81B\xf7\xf2\x84\xe5\xaeu\x1eq+x\n~\xdc\x1fc\xeb \x95\xdf\x15C\x1f\xa9\x0c\xfd\x9dRap#t\xa3\x8e}A\xae\xb4'\xdb~\xba?\x94fm\xf8\xd3'{\x03\x86M\xb6O\xb7\xcebw\xb0\xf7\x9d\xf9\xd3\xff`s*q\xbfw\x07\xfeJz>\x8c\xe5o\xe8;\xae\xe8k\x97\xbcv\xcfF]_\x9d\x850\xb8N\xea\xd5\xf3\x92\xccIV'qZ\xc11xI6K7s\x82&`U\xbc&\xf7Y\x9cx\x8d+\xb6`\x03\xc4z\xdb\x14yd@hB\xe7\xbe\x81Pm\"p\x9d9\xbd&`G]XML\x01\xecX\xf5\x1e\xb0\x8cyTA\x8d\x177,\xfc=\x9b\xd1\xb6&\x9a\xd0g\xc6\xcf\x06\xd2\x1b\xcd\x9a\xe5\x99h\"\x88\x01\x8aw\xaea\xe0@\x95c/\xf2\xb9>x\xa7.\xcb\xc9\xef\xcc\xbf~\x85\xdb\xbdd\xe8\xb2,\x1e\xf0\xe9]\xc7\x97,\xb7\xf2_N\xdf\xbe\x11N\xbd\xb3\x94\xc4\xe5\xf3x\xb6\"6\xbb\xd6**\xd2\xcd2\xc9\xaa\xa8$\x8bJ\xf9\xb0cB|\xeb\x9aQ\x1eT\xc2R\x9b\x17J\x10\x97z\x95\x18\x92\x99\x9c\xa0X\xd8\x19\xe0<\x9f\xe1\xf0X\x14]\x12\x84\xdd\x19,TX\xf8\xd7C\xeae\xddf2\x84;\x01\xd3f\xba0\xe0\x97~JB\x8c\x9a\xb6\x07m\xd0i\n\xeb \x01N\xd5\xb0cI\x81\x931MM\xd3X\x13\xf2>\x08\xf5\xdf\xad\xf5\xdf1\x9cN\x08~\xc7\x8f.$\xec\x85\xb6~\x9c\xa6o\x17A\xd8\x8d\xf9n\x06\xb55k\x9b\xbc\x11\x1a\xa6<\x17qE^\xe4\xb3 \x9clCi\xf8\xf0\x07IfW[\xa1\xe5\xbdE\xa1\x82\xfe\x8b\xa4\x9aQ1$c\xec\xaa\x86\xebmj\xf3\xd5y\x1d\xcf\xca\\\xcb?\x8b\xb2\xce\xe7$\x15\x94\x86W\xefGE\x01\x854\x9e\xbb\xe4E\x86\x8eos\xdc\xac]b\xf4mv\xd5\x1b&\xdb\xb8\x1d\x8b\xf2\xa5\xee\xc7\xa2\xb8\xba!\x8b\"\xcf\x8a\x9e\x07\x87\xc9\x16\xb4[\x98\xeb\xa0[\x8fc\x1c:D\x91#\xb48v\x882\xac\xf2\xe6\x8e\x1e\xe6f\xb4>\x1b\xa283D\x9d\x0f\x9c}8D1(\xd2\xfd\x00&0\xeb%\x13\xb3\x9d\xe6\xa0\x90^\xc2N\x083\x8b9\x94pl1\x1cd\x8bE\x92\xa2{W\xff~\xde\xc4\x8fT(\x8c\xbe\xee\xaa\x1d\xb0\x0b3\x17\x19R\xdc\xb1]\xd2\xa3E\xfa\xcak9\xc66}\xd1\xd7^\xf2\xa6U\xc2\xa5\xaf\x89\xf1\xe3\x9dy\xf9\x0b^\xdb\x91\x97?g\xebr\x99\x14B\x97\x87<\xa7\xbe\xf25\x8b\xe7U\xd7\x1a\x19\x1d\xb8\xc1\x13\x89\xf8Ibd\xfai\xad\x13tc\x0e\xb1E\xbc\xd5\xbe\xa6\xffl\x04\x9d\x0b1fN\xed\x97\x18\x91\xd1\xcck\x8c\xe03\x1cy\x8c\xdb\xc0?\xe1t\xbf\x9b\xfa\xbd\xcfZn8\xf7\xa8\xb5\xb4\xe2\xd2\xfc\xbe\xe6\x15K\xbbY\x19Rnf\xfe\xd6\xba\x83\x83\xbd\xad\x93\xbb?\xd9Z\xfe\xdfZ\xfa\x1f\x18\xabU\xf6W\xdf\xdc\xb9\x10a\xe2\xc8\x0d\xfaOy\xa2\x9b\xd9\x03TAE\xb3\xb8\xa87%9\xad\xe3\xd9\xe5\x872\x9e\x1186\xbd\xe1\x04\x9d\xfe\x1b\xcd\xf2\xac\xaa\xcb\xcd\x0c\xdd\xdf'\xecYEkR^C\xfan\x06\xec\x99\xe5\xaaA\x1fx+k\x05\xde*Y\xe0\xad\x92\x05\xde*ww\x03\xc8\xa6e;\xf0Vi\xe0\xacqpkRU\xf1\x92`\xae\xc6\xbd\xb3\x90\x99\xd0\xd4\xad\x93J\xa7l7\x11\x8c\xac\xb9\x8bW\x9dUC\xf5\x05\xcf\xedC\x8f`\xf5\xa9\x02:\xfai\xd8q\xa8\x1a\xad\xf5\xfb\xed\xf12\xa9^\x96\x84\xa47o\xe25\xb1\xe7w\x90\x86\xe4S\xd2\xf2\xc7\xd1\xae\x1d;\xc4\xa5\x0b\x9d\x91\x80\x97Q\x92\xcd\xc9\xa7\xb7\x0b\xca\xa5\xfc \xee\xefS\xda\x9d\xcb\x87Y\xf30q\x0d=)WZ4BX#}$\xb1\x12e\xf4i\xf2\x1a\xb9K\x17M?\xc7:\xb80 \x1dX\xe5\x85\xa0f5\x0b\xc1\x13\xe7\x05\xfe\x10\xf9\xf8^\xb4\xbf\x98\x89\x90\xb4\xd5\x83j\xb6\"\xeb\xb8\xfb\xb4\xd5\x88\xf2\xbc\xdd\x95\xda\x0c\xef\xe8\x946\xa7\x1f{\x82cg\xfd= \x9f\xe2u\x91\x12\xefl\x0c\xc6v\xc8\xf7\xc3/ \xc3\xadW\xff\x96*X$G\xc6\xedp\x07\n\xda\xfe6B\xf3\x86~03\n\x87\x8cG\xf9\xc3`\xef\x8c\x9c\xed \xc5T\xef3r%\x91>\xb9F\xab\x8f~'\x1d!TP\xdd~E\xb1g\x90r\x97\xa4\xca\xd3+\xe2w\xb5\x82\x96}[G\xf3\xa4\x8a/R\xc6]-\xe2\x19\xc1\x00Q\xdd1\x84\x18]\xfb\x92<+\x92\xeaC\xbc\x94\xd9C\xfd:\xd0G)\x1e\xa2A\xb34!\x99\\\xc1Nt\xb7\xdfL\xcbxh\xd62\xfah\xed\xffm\x80\x91\xe4\x1e\x05\xba\x8a\x82\xa1\xd4\xa7\xf3\xa9\xc4[\xad\xb7A\x8a\xbb\xf9;\x03SY\xfa\xa9!\x8cb\xe6\xef?2\x06Q\\\x0cEP\xd4\x86\xb0[17\xf9'\x86\x00\x8a\x99\xff\xad\x8e#^s\xbe\xb7\x0d\xd8\x1ce\x0d48\x94\x82A\xae\x06CL\xe5\x8f\xe8\"\xc9\xe6~\xb6I\xd3\x90\x7f\x16\xf0X\x1f\x14\x9f1m\xad\xd2\x04\x7f|\xba\xb9\xa8KB\xdf\xce\xd5\xb7\xe4\x13\x99mj\xb4\xd0\x11\x7f\xd3\xc7\x9d\x18\x8fi\xebA\xabB\x13\xf01\xed=\xa4\x15\xdbJd\xe5g\xc82\x85\xb0\xb3\xe1\x87M\x92\xf2f\xae\xa2w\xcf\xde?{}\xf2\xe1\xe4\xfd\xf9\x0f?\xbd\xfa\xf1\xc5\xc9\xfbS\xd3f\x82#Xi_\xd0\x0f.h\x9b\xef\x99\xd4\x84\xed\xaa\x0f\x10r$-X\x9f\xfd\xdd\x90\x17\xaf\xe6\x13Xc\xe2\xfb\xf6\x86\xc0q+-\xc8\xac\xd1\xe2\xf1\xffY\xd8\x17\xfe\x00\x9d\xfc\x98 \xc5\xfe4\x99\x8e\xdao [\x14\xa5\xbd\xcbm\x17o*n\x0d \x84`\x1d(.\xe8y4\x96fe/l\xf4R\xc8\xc3xt\xef{\x83\xbe\xbb\x94\x08WRi\xcf\x02\x88\xd7\x06\xed/\x89Vy\x85\xbe\xba>\xff\xf3\x082\xfc#@ 3I\x80\xbf\x17\xbf\x8e`\xca\xc5\xdcY\x9e\xca\xe8(\xde\x84\x8a\x13^p\x86_^\xc4\x15y\x17\xd7+\xfe\xa9\xfcy\x04T\xba\xb3/\x80\xaa\x03\xc9\xc7\n\xca\x16e\xd3\xde\x80\xd01\xfc\xe9\xfe\x17\x98\xb8l\xadW{\xb2\xf7h\xdbO\x0f\x1fn\xad\x1f{\xb27` \xf4\xef%\x9a\xa9\xbf\xee\x9c\x1bG\x9bdv\x01\x89\xb8I \xd5\xeb\xb8\x18\x08.\x9e\xc3@\x84\xf0d\xc8\x1dX\x1a\x0chu\xbe\x9b![\x83j\xc8W8\x15\xedj\x87$\x82\xa1\x1fj\x9d\x85\x17C\x9e\xc42C\xa86h\xb4\xe0\xe5\x0f\xf6\x86\xdc\x81\x87Y2E\x14\xbd\xf6I@E\xc1\x02\x8d\xb6\xad\xaa\x1a\x11n\xfdP+5\x89x\xeb\xda\x81\x8b8\xda\x87\xda\xb7\"\x8e\xf6Cm\xc3\"\x8e\xf6C\xed2 o\xf0\x87Z\xafm\xe1\x0e\xfeP\xeb\x98\xed\x94\x08A\xb9\x00\x1e<\x80;\xf9\xb5\x98\x98K\x82^.\x12\xf6b\x98\xcdd,\x92g\xf1'\x99\x93\x8b\xcd\xf2GrE(\xe7\x98d\x8b\xdcR_\xde\xfaO-\xael\xac\xe2\x9f\x93\xaa\xce\xcb\x1b\xb3\xd5\x9a(\x8cy\xb07+|s\x1d\xaa\x16\xcc:|.Y:\xdb\x07U\x1dSi\xc46\xd4\xc2\xb5\xbd\xc6\x0c\xc3\xd2\"\xaf\xf8\xa1$d\x82\x9b\xea\xdc,4\xa9\xa5Z\xe5\xd7/\xe8\x02\x9a31\x89\x12\xa7\xa93\x1c\xd8\xd2Q2M\xa5 FY-h\x91&\x17\xafI\xbd\xca\xe7\xd5\xa4\x8b\xab\x9dd0\x14u\x035\x10\xbcu\xdc\x1d\xc6\\\x93RJ\x14\xca\xc1\x04\xfc\x06eI$\xb7w\xbe$5S\x16\xf0\xceE\x05n\xf3\xad\xd6\xe3\x8f\xfa\xd5Wq\xf5~\x93\xc9\xaa\xecg\xbf\xdau\x19\x17\x05\x99\xbfk\xce&\xfaT\x98\xfa\xac\xe3\xc2\x97\xd5X\x1d\xa5\x89@\x84\xe4\x91\xc0\x89\x1a\x13j\xd1\x01\xc7>fD\xd4T\x8c\xe7s\x7fz\x166\x1cp`\xf9\x80\xe3\\\xf3\x11\x7f \xbf\xdb\x14\xf3\xb8&\x1c\xec\xbe\xda\x94\xde\xd2`\xd0\x11\x87\"\xc1\xbcA\x02\x12\xc2\xd4L\xbd.\xc9\xcd\x04<\xa4L\x03h\xc7Y\x03\xbb\xee@\x14\xe4\xef\xe94\x1a\x9a\xc7\x8c\xf5m\x1f\x82z\x9bV\x87Z-1\xbbBc\x17j\x19\xaa\x8c\x8f!\x83\xfb\xb0\x0f\x13\xd8\x0bBd?\xf6\x9fB\x0e\xdfC\xf6\x14\xf2\xdd\xdd\x00\xcai\x8e73\xadK\xb6\xdc\xc1%\x17\xdd\xbfy\x94\x95 J\xf3e\x13\x86Jc\xbd\xa1\x16\xb39\x8b\xc1Fd\xe8\x90a\xcbtE\xca\x8b\xbc\x1a\x8a\x04\xb1\xd5B\xc9v\x99\xf3_{\xd9l\x0d\xc0\xbf\xcf\x82M\xbd)\x06\xce\x84]\xf0\xce(C\x7ff\x8b\xca&\xcaWX\xcb\x86*\x8dYNKx\x05P\x04dAE\\lk\xd4\x827\xb9\x83*\x13Qr\x83\x08\xd0-B\xfa\x99*\xf4\x99\x9ex\x98F\xb8d\xd70h\xf4\xde\xab\x10\xc0\x04t\x04\xda\xc7\xb0m9\xbf\xc9Qk0\xe9G\xc4\xab\xca\xad\xdcu\xb7\\m\x93P[\x14>\xd1\x9d^\x889\xcc\xc5G\xaeHy3\xce\xb1Y-R\x86<\xe2I\x98\x9d\xbe4$\x1bkU\xb1o*\xde\xb7T\xd4tL-K?\x0f\xc1\x988\xb1[0\x16D\x08\xb3\x10\x16!\x14\xe8\x14\xbf\na\x8d\xee\xab7\xf6\xb1\x80n\x85p\x1a\xc2\xf3\x10.Cx\x16\xc2\xdb\x10\xde\xb9A\xbe[,+\x11o;~\xd0\xadL,V&\xdeje\xbae\xdb\x95\xea\x16\xcch\xdd\xa7A\xf9\xa8\x00\x16C%\x96\xf9r\xb6[\xa4nq\x0fk1T\xec!*l\x85\xa5b\xb8$7x\xd3\xbf\x98.T#\x9a;\x07\xde\xc3\xff,\xe0\xf1\x9d\xd7L\x0f\xe3D\xe3\xd9\xe9\xa3>\xf9\x92\xdc \x0d1%.u-,\xe2\xff\x97o\x93f\xa4\x8f\xbfl@\xe0\x96\x11\xc4V\\\x93H\xd9\n\x9a\x89)\x98\x1b\xa2\xe2m1\x9d\x9f\x85\xa8G[H\xab+\xd5l*\x08Q\x8d\xa6>\xc2\x93\x1dC\xa9\xcc\xf1\xcfu\x88\x87B\xa2\x0dD1\x9b\xe6\xd17\xdf\x94dq\xc6\xb2\x95\xee\xec\x85\xa8=\xdb\xd9gf\xbf\"\xed\x91\xa4\x99\xfb\x0fC\xb4\x0d\xee\xb8\xbe\xd0\x9fU\xf3\xd3\x98 \xd3\xb58\xa7C\xb2\x15J\x1c0\xce\xc5'8\x82\x13\xc4\x1d?\x08\xa2y\x9e91r.Eb\xe4\xe1\x7f\x18m\xc0\xe8&p\x04\x9fD\x10\xf9\xe7p\x04\xf9\xf4\xf4,\xc4\xf8\x95\x0b!\xf7\x9c\x06!\x86\xac\xd4\x9c^\xcf\x83\x10\xdeb\x96\x17\xc4\xb2\x10\x06\xd3\xfa\x8e)\xf1\xd8\x84H\xb6\xf2\xaf\x04\xf5\x9dg\xff\x0d&K\x91^W:\xb2\xf6\x16\xe5\xb6\xd9\xf4\xed\x19\xd2\xb4\x80Y\xb8\xa5d\x19\xd7\xe4\xff$$\x9d\xfb\xa5\xcf\xd8\xd6\"\x08\xc1\xab\xf7\xbc\x10\x0e\x1e\xdd\x05\xcdr\xc9\x81e+\x18x\x9aJ{\xa7,d\x0c=\x83\xef\x1c\x1f\x0e-)\xb8\\\xcb\xbf\n>P\xa0\xbd\xc3\xcc\x06\x19\x8b\xd0\x96a$\xbbw\xff\x0d8K\xe9r\x80\x87\xfb\n\x0b\xf8\x1c%\xbcK\xcc\xddZ\xdc\xc5\xfe8tt\x15\x1c*\x82Q\x89\x9b\xf4\x8b_62\xb8CV\xf0\xf0Ny\\\xc7\xcc\xaaC\xe5\xce&v\x07\x94M\xb2\x91\x87\x98\xb3\x153\x0b\xc6\"c\xde\xc3\x80\xf3\x9e{\x8c\xf7\x8c\xadi\x02m\x85\xc9\x1cw \x9b\xcbq?Ty\xe1\x87\xfb!\xec\\P2s\x12\xf1]\xa4\xfc\xddM\xc05\xb68\xa5Hs)\x9426c>\x0ca\xe7\xfc\xce\x89\xe2\xc3;\xd8\x81\xf0/D\x14Y\xde\xbd\xeb/\x9b\x14[\xc1;\xd86\x92D/\x92,\xa9V\xfe\xc3\xc3;\xc1-\x87D\x89\xb6\xd2\x1b\xd9\xde\x9d\x8c\xec\xf1\x97\x8dl\x1b?sS\x913t\xf4?7\x95\xedp\xf26\x84\xd8\x9e\x98\xd0V\xa6Tj\xa7$\x97\x92\xaf\x87\x8f\x1dB\x1a\x9b\xca\x94\xd2\xbc\x10\xa9\xc8\xc3\xef\xdc\xee\x0e\xba\xc5\x10\x15r\xa8\xdc\xb2\xc4\xf1\x9d\x8b\x83\x9b D\x9b+\x0c\xc9\xcb\xcf\x8d\x82\xeb.\xe6\x8a\xeeBj\xe2\x1f\x852f\xac\xa2\xba\xc8uw\xf8\xdd8mc\xf5\x19\x88\x81[`1\xa5\xd5\x18\x84x\x8d\x1e\x02w\xa1\xae(%\x97\xb4\xa5zb;\x9a<\x1e\xdf\xf9N[\xc2\x11\xac\x85\xc6\xa1\xec\x88m7\xfeR\xbcZ\xf28\xa3K)\xc1\xed\xefo\xb3J\xfb[p\xa4\x02\xdd$l\xb7\xd0En\xc1\x97\xb1\xf1n\xc1`\xcaq\x1el\xc1Pn=\xd0-N>\xb9W\xf7\x1fQ\xe8\xb2\xd4\xd3\x9cA|\x14\xf0\xfd\xbd\xc7\xf6w9\x9a?d\x12\xfa\x16\xfc\xa0\x1c\xd6\x81JO\x0e(\xff\xb7\xa0<\xdfJ\xe1\xffV[\xf2\x7f\xce\x99\xc4\xbb\x85%3\x16c\xa2\xfc\xdd\xd6\xf7}\xe5\x97j\x8b~-Z\xc1\xf8\xb3\xf9\xb8An\xad\xa0\x91\xee\x8c\x9c\xcb9\x18\xcb\x7f9\xe73\xef\x96^\xcfc\xf9+\xd6\xf3\xc8\x93\xe8K\xf8'9\xe2\x91\xfc\x92\x1b\x0e\xdc\x86P\x8e\xe7\x87\xa6\x8fB$(t\xf7\x1e\x8ca\x7f\xa6\x07\xc8\xee\xd0Mu\xe0\xc8\xee8\xb07\x16k\x8a[\x9f\x04}\x03\xe2\x9c\x99\x1d\x96\x81\xcd\x8a\x18\xa4=\xe8\x9bxM&\xc0\xa3.|\xfe<\x14~Q\x94V\xe8Y\x95!\x92\x8f\xfd\xdc2\xfa\xd1Q\x8d\xecVN\x94(\x8d\xb6r\xb2\xd1@\xbbw\x9b(\x8aE\xe4\xaam\x16\xdb1\x1eU\xbc?\x9c\xcc\n\xa4\xf7\xd6\x92\xd4\x82\xd3\xac^\xe6%k\xce\xaf\xd5\x8c\xae\xbf\x0d\xd0U\x83\xec;\x84\xbd4\xec\xecX|\xb72\xd8J\xc9K`\xa1\x0c\xb9\xd2\xfb\xcc-u\xa7Z$\xe8q\xe8\x16\xe0~\x05\xe8. \xc7hno?\x02\xb8\xd6\xf9\xa9Q\x13\"\xd9\x11\xa5\x06>\xb1\x1c\x1f\xaa\xd7n\xcb\x1f`Z\xf3\xfc3_\x11\x14\xef7\xd9\xf3|\x93\x0de\xb0\x1a\x0d\x0buB]\x98\xfbDl\xb0\xaf8)\xde\xd7\x87d\xc8 \x7f\xf4\xb4\xf4K\xdc\xcc\xcbm\x951\xe2\xcf\xb4V\xedeX\xf2\xaa\xaf\x08\x0fA\xe7^es\xf2\xe9W\x03\xc9\x87\xa4\xc0\xe4\xcbj\xe7N0\xf2\xb2\xcd\xfa\x82\x94\x1e\xec4\xbe\xd9p\x0c\xf7\xf7\xc1\x94&\x0d\xee\x04Lt\xb7\xde%t$\xbdkX\x83\xbb\x1f=w@\xd8\x96\xae9\xd8\xc8\xb6\xcc\x92\xc7\x916_C\xd4\xb2\xb3\xb6\xbf\x87\xf2\x9c\xa7TG\x1f\x8c\xa1x\x91_\x08+v\x80}E(\x0d\x03\xa5a\xf1\xda\xe9;\xe8f\xe1y&F\x1e\xach\x8d\xd7\x0b\xec\x1f@\xc6\xbd\xcd\x19Dm\x8bE\x0bf\xd8\x19NY\xa1\x16\xb4\x9b\xd0\x1aqKV\x025\x82\x19sK\xf0\xbb+\x00\xde\xff\xcck\x88!\xcb\xb3\xfb,\x0f0\xf3\x1b\xf3Bp\x19-\xf0!d\x91\xf4\xf1b\xb1\x83\x1b?.1\xf5\xb0\xc5Ys\x1e\xcb'2=\x91\xf0\xd5\xec\xb19\xcd\xf7l\"\xad\xf7\x1fV$s\x82+h\x8cM\xd5\\\x1a\x1a\x88U\xd2\xcd\xca'\\\xed&\x86\xbb]\x7f\xe2\x14\xd0\xf4\xc5\x96E\xb2\xc3\xba\xcc\x15\xdd\xe2\x96\x93D-\xfd\x8c\xc7]\xfc\xb463,\xb0~\x0d\x8e\xbc\x03\x991D\xc3\x06\x97v\xe6\xebvL\x16\xb1\xd2hO\xd1qJP^!\x19\xd5\x19\xe3\x88Z\\\xf5\xae\xc8\xb4\xbf\xdc6xdA$q\xba+\xfesM\xe2)\xe6BW\xc75\xc1\xf0\xbev\x14p\x0c\x1ebY\xe1\xe1\x11\xb3\xc0\x14\xd8\xaet\x81mvp3dJ\xa7\xbf\x02\xb2\xb0\\\xc6\xdb\npV\x84iq[]:\xd5\xc4\x07\xb4\x81\xe8{\xd8\x13!n8U\xfeP&d\x0eu\xce\xf3;C\xdc\xf6\n\x86z\x15\xd7\x90T\xd9\x1fj\xa8W\xa4$;\x9e\x0c\xb7\xd9\x1dFU\xa4 \x95\x18C\xd8\xff\n\x00\xee\x11\xdf\xaf\x05^'>\xb5\xd9c\xfc\xafN\x14\x19''!\x11eN\xb7M]\xb6\x154S\xcd\xac\x95m\xfb\x070\xbe\x81\x06\x8d\xd9\xfe\xe9x\xbb\xda\xdc(\x03~\x890\x0e \xee\xfdkB\xa5\xaa\xe5k\x1c\x07\xaa\xd2h\x0c\xee90\x90\x8d\x97\x18\xa0\xe6p/\xd4\x0bBH\xe1\x04\x15h\xa8\x1c\x93'\x05\x95k\x9eW\xb8\x1f-\x01\xd8\xbf\x00\x1c\xcf7eI\xb2\xad\xa0\xe2\x08\x11!w\xe8\xb4u\xfc\x15\x1f\x04\x7f\xfa\x95tG\xfd\xfeG\xccu\x14\xf5\x89\xf4\x92\xbb\x95\xb6\x9b\x00\xe6\xd7\xb0\xfbU\xe8q\x17\xf4#\x00b\x83\x87:\x97\x99\xda\xc7W\x99\x05')o\x17\x1fn\x8aQ:\x80\x11\x1b[\xd8<|\xa5\x8d\xf8cr1b\xe0\x8e\x83F\xf07a+\xee~\xe0\xe7K\xf25t\x8f\x0d\xcb\x8a\xc9\xf1\xdb\xdc\xeaW\x80\xbf\x12\x14\xe3+\xcc\x86m\x82&\xfc \x9d\xd4\x90\xb8\xb4\xf54\xaa\xadf\xe1\xbe\x07z\x13\xa9\xe8D\xbe\xce\xd9\xc4\x83\x8f\x8c\x99\xc8\x98Y\xf44\xe8\xc6\xc3\x08\xfe\x04>;\xd1\xbf\xc6,gi\x9e\x8d\xa2X\x8e\x93\xfc\xcb\xe9\xdb7<@\x1feMsE6\xfd\x1a\xe7\xab\x88\x8d5b&\xb6\x89H\x97lb\x9f4-\x84 \xce-\x81W\x93\xcc\x97k.\xda\xac( a\xfbH\x14\xd09\xfe\xedW\xc6\x99sM\x19\xc0\xba\xb9\xcf\xb5\x19\xc9\xa0R\xcf\xc9\x11_D\x8ck:h\xf1\xec\x0e\xc2\x06\xed+\x97\xda\xa8\xdc1\xb8v\xb7\x88}i\x8a\xb0\xa6+}\xe9\xe4\xeb\xf6f\x87\x85\x88\x96\xed6\n5\xb6+\x9ekN_\x89\x00b\xf8\x1d\xfba\xfd\xce=\xca\x04\x1b\x8d\xaa\x8a\xf5\x13\x11\x0eI\xa0I\xa3\x9a\x0dB\xf5\x9e\x99\x07\xb3M\xbed\x131]0\xbbV@\x9a\x8c\x11C\xd5\xdfx\xd3\x16\xb6\x1f\xb2\x0c\x1e~\xef\x19Rl\xca8k\xea\xff \xf6\xf7\xb4\xd7\xe5\xd6\x98\xbc\xa2\xb0\xf5\xcb\\\x17O,\x9cT\x99r?P\x99\xf4\xc3\xf7\xfeF\xfepE\xa0$\xf1lE\xe6\x10\xc3*.\xe7\x90&\xeb\xa4\x86|A\xc7\xcbMT\xa0\xdcd\x95g\xa3V\x0eD\xa2DW\xb9>\x87.5\x93zK\x03\x97}&\x92\x08i\x9b\x19oy\x00\xe3\xac\x0f\xc0\x01\x00\x00\xd0_\xfe8M\xfd\xcd\x97\x8e\x0fi\xa0\x88\x97\x13\x82\x0cmfm\xe56p\xcdN\xd0-\xdb\x91\xb4/\xd8\xa9\xbc\xc3Q\x03\xcd:Xv\x04\xa5}\x89\xc4\xb9\x9aE\x1a]\x85o \xab'J\x8e\x0dtu-p\x1f\x1cla\xc7]\xa6\x95\xaa\xd9\x97\x0bPD\x11\x87\xc7P&_]\x89\x99\xf1\xfe\xa8o6\x8e\xd1\xa3\xd4\xe2\x0e\x06Qdh\xb2\x8a\x99 w\\\x08J\xbf\x0e\xd9\xaa\xfe\x98\\\xf8A\x10<\x85\x1d\x9fB\xc0\xaf0\xa9A\xcb\x8c\xff)\x87M\x00\xc4\xaf\xf8\xe5\x87\xf3`\xc6\xdft\x89\x12s\xcbi\n0;\xc5\x11\xe5\x16\x16I\x16\xa7\xe9X\x80\x8d\x071-; %\xd7\x85bL]Hc\xeaQ\x8dm;l\x10\xeer\x01\xb70\xde\x8c\xfa\xdc\xcd\x86\x15\x9ck\xde\xb2;p\xd2G0\xeb\xe7\x12Q\xac\xe2\xb0(\xed+Q\x8ck\xeeO-\x91A\x9d\x8cQEa'\xfe\x04\xfaY\xfeu\xe56p\xb1\xa4\x1d\xb9\xceRTj\x99K\x95cf\xd12!2%\xec\xee\x16\x97\xf8i\xd6\x1a\xd2,\xc0\xf1`\xbc\x1dxo\x90\x8d1&}\xef\xd5\xad\xeel:1J\x07%YT\x13X\x0b4\xd1\xd3sL\xa1<\x81\xe5p\xad&\x05\xd7\x04n,Ue\x04\x9c \\\x88\xaa\xfd\xa9\xb4O 5\x0c\xf9u;By\x93ay\\<\xf8\xc3\x87\x03\xf1\xe0\x87?=x\xfc\xdd\xb6\x9f>\xde:\xa5\xe4\xc1\xf6\x91\xef\xf7\xf7\xb6\xfdt\xff\xbb\xed\x13\x04\xec\x7fIF\xca\xd6+\xa9\x94\xf9\x8d\xe2\xed\xeb\x07\x93\x1b\x95\x98,2LT\x93\x8aY5\xe9\x07\x80\xb5jq\x80Q\x99\xecm\xebV\x9d\xe5Z\x8a\xa1$i\\'W\x04~z\xffc\x08\xd7I\xbd\xca75\xac\xe2\xab$[B\x0c\"\x13E\x84Y\xbe'\xf0\x07\x19\xf4\xf4\x0f\xf2\x1d\x7fZ\xe3S].Bh\xa0\xf8\xa9'\x97\xd6Z\xf5w\x9f2\x89ep\x82^b\x84\x9e \x9f\x0c \xcf\xf3M:\x87,\xaf%DJ\xb2 %\xc9f\x04.\xc8,\xa6X\x93/&\x80\xb3\x16\xb92\x11\xc3:c6\x0d$\x1e\xc4)\x1f!\xe9\x05h\xa3P\xfb\xde\xef=\xb7V7\xc6\xe9 \x9b\xbfwS\xa2\x89o\x8b\xda\x084\xe09\xd5\x98\x9eeA0\xc0\xb1 \xab\x80\x14\x99\x90\xe1U\xa6\x0c\xc2E\xc3 ,{\x8b>\xec\xbfr~\xce\x15\xabz\x1eA\x97\x91\xc6\xca\x10\xf3\x91\xa9C\xe1v\x81\xee\xb8W\xf9\xa4+\xce\xda\xfaKM\xf8\xed\xb6\xd0\x95\xbe\x03!B\xeaWY\x88\xcep\x0c\xbae\xae\x038\x86\x1a&\xd0_\x96:\x80 \xf8\xb4U8\x82W,G\xf8_N\xdf\xbe\xe9\xcf\xdb\xc8O\xf2\xcey\x1b\xb5>U`\x88\xef\xdd@\x90Zq}\xa6\xbd\x85f\x9a7.\x17\x7f\x0f\xfbR5V\xf7\xeb\n\xdc>\xed\xde\xd1\xe91\x1d\xcd\x18\x9b\xac\xe4e\x87\xca\xf6\x89J\x91'YMJNG\xe8\x9e\x87yN*\xacC>%U\x0dI\x06\xf3|\x86\xa1\xa9\xb5\xf9Th\x91\xadh\xce\x14\xcd(\xf9t\xbb\xc9\x16\xf5P\x9e\xe9\x11\xad\x95\xfe\xb21\xf9 \xea\x8c?\xdc\x14\x84\xeb\xfbN>\x15dV\xa3\xaa\x8f}\x14\xc2\x12\xadi\xe9\xbcU\x90\xd1\xc3\xd3\xdbd,\xaf\xcc\xdc\x03\x96|\xe0\xaau\xa3c\x9e\x92\xf7\x80Y(\x92\xe9\xde\x99\xbc!!Q\xb5\xb9\xa8\xea\x12s\xc1\x80\xe7\xc9~\xa6g0\xc1\x0cXHb\x1fx\x01\xd3\x86\xb9a\xdfb\x90~\xeb@\xc3\xd9\x82\x13\x89J\x9b\x8cT\xb3\xb8 >\x91\xc9\x9f\x1e\xfc\xd7\xfe\x83e\x88\xb9\x9d\x94g{\xf8\xec\xbf\xbazP\xd3\xd0\x8a\xc1\xa15\xfdkzg\x1d\xed\xa9\xbd\x7f|\xc0\x1e\xee\xbbv?\x1fdP~\xf6\xeb\xc6\xa4wG\xa3\x95\x11\x9b\x97D\xb3U\\>\xab\xfdZ\xda\x0b\xe9\xe9\n\xcb^\x86\xa6C\xf7u\x1e\xfe\xbc/\x8e_j\xdac\x8a!;\x98\xb9^ \x0e\xfb\xf1{\xfe\x03k\xd0_;t3;M~%\xf8\xcc\x10\xb4:1q\x0d\xf5\x01\xef\xc5K\xcdpsL\xf5\x95\xf3\xc0\x15\x1f\xf0\xda\xb9\x0cA\x1b2Sh\xd2\xec\xa7\x0e\xf4\x01\xc1)\xe01\xdd\x12\x13\x84\x00\xb22q\xe1\x17A\x93@Z\xdb\xda\xad\x9f\x19V#\x86#\xf0\xf1\xee\xc2\xfb\xbe*\xc8l\x1d\x17\xf7);\xf8'/\xa0\xd4\xed\xf7\xd8\x89\x9ep\xd6p\x84\xce\xfc\x1d\xdb\x81\xe9Y\x80i\xcf^\xe43\x0cZ\xea'\x98\xca\xd0\x86B\x1b8\x02\xcf3Q\xffq\x19\xadi[\x1b:|\x84Q\x81\xb7\xaa\xf9t\x83$\x86\xfe\xef\xda\x9c\xd2$n\x92\x18c\xb6\xcf\xfd\xd8h\xe8\xa1\xe3h\x86\xe7\x9eO\x13\xbc\"\xc2\xff\xb9\x93\n\xbf\x7f\x89\xbb\xfbW\xfdu\xe7 \xbd\xdaC\xa3Kr5\x94\x93k=\x94Xk9\x98\xb0K\xa6\x82\xd2~{1\x94X\xeb\x9c%\xba\xd5e\xb3\xbd\x16}jSH\x9d\x88>\xb5\xcd~\x1aL\xf2{:\x94\x13\xeb\xb9\x18\xae\x16J\x97B&\xef\xbfz\xc6\xd3\xea\xbf'\xcb\x93O\x85\xef\xfd\xdd\x9f\xc6\xf7\xffy\xb6;y\xf0\xe0\xf3\x83\x07\x81\x17\x82\x97x\x9a\xef\xder}\xf5\xf3\xe6\x8c\xf5(k\xf7\x9e,\xf0\xf0\xf6\xec2\xb4(x\x03&2M\xe2\xc7,_\x7f\x87\xebGk\x00\xe0\x17\x9c:\x04\xef\x0f\xf2\x1d#\x87\xbd\xe7\x1f\xf8\xa4\x07\x94?\xaf\x8d\x8a(f\xcd\xf1MI\x16\x06K\x0e\xa1\x91\xec\xce\xdf@\xdbE\xc1\x8b\x00\xbc\x86a\xa7\xd2^\x08\xda\x83I\x14\x94\xc8i\xad\xcb(\xa9^\x96\x84\xa47o\xe25\x99\x07~e\x0d\xeeN\xfb\xc2\xb4sJ\xf6#?\x93\x14\xd3~1\xaag\xe2\xda\xc20\x05\xd1\x04\xd6\x9b\xaa\x86\x0b\"Y8\xf0)\x9a\xdc\x7fO\x16\x81\x913U\x0bk\xc5\xe1\xfe\x98\x8f}\x02\x0e\xd9A\x16\x1b\xbc\xa3_\xd9,\xcamW\xa4\x14\x8e\x0b8B\xb1\xdc\xdek\x81\xa1\xb7\xf7\x1c\"E`\xd8\xee)\xf3\x9b\xb5en\xa3\xe5\xca\xf1\xbe\xca\xed\x02\x85\xb6\x96\xd2\xae\x0b8\x86\xdc/BH\xa9 gL.+\xca\xb8\xdb\x01\x8e, =-\xec\xb5A\x15X\xe6v\x88\xc0\x18\xd4\x01\x8e>\x0c%\xae\xdc>p\xc5!\xd0\x1f\xc8\xad\xd7V$[6\x91\xc7\xac\x9d\xdd8\"\x03\x12\x90\x95?\x0f\xe1*\x84\n\xcd\xbb\x1c\x16\x029\xa1M\x9aR\xb6\xeb\n\x8e\xc1\xbfA\x91y.\xfc\x07\x19\x9f\xe8/\x05u\xf1o\x02\xc62/9\xd1\x1dV\x93q\x99\xf6_\x06%\\)\n\x8c\xc6\x88\x80\xee\xa9%OhD\xe9(Bh\xe3_\x850\x0f\x82\x88+\xad\xe0\x18\x96\xf2\xef ,\xbb&]N[\x0ddl\xa3\x11\xbb\x0d\xb6\x00/\x8c\x051l\x01f\x18 j\xb0o@\xe0j\xa4\xa5\xc6\xc5\x98\xd3\xa9\xe9\xa9\xa2\xdeZ\xe7W\x84\n3\xb0t\xc8\xfaE\xf7\xefEK\x1b$\xa4\xe4\n\xd3\xdf\xb8-\xc77\x1c\xae\xd6\xca\xb63\x0b\x84\xc6\x89\xee\xca+\x14R\xd3f\x96\x17\xa12N\x91\x1b\xd0\x9acT\x14\xb9\x94W\xd6\xea\xb7\x81\x03\xe8\xdc\xce+\x10\xc4l\x9c\xc5\xb6Z\x84\xfa@\xab\x005\x15iST\xc4\xf5**\xc9|3#\xfe\xd6C\x00\xf52\x96ytNk\xbc:\x9d\xd6nA\xa2h\xc1\x8c\xfd\xee\xfb\x08F$\xa55\x15>hU7\xcc\x9d\xe4\xb9\xb2$S\xb5'\x7f:\x82=\xd4U\xec\x85\xcdmn\xe0\xd7AG\x1cv\xf2\xa4\xd3\x15q\xb1\xe3\xd7\xd3\xcc\xe1\xb2\xbf[\x86\xe2\xf2\xe8\xca\xad_\x8f1\xb7\xb9\xf5K\xe1\xa5q\xd1\x88\xe4\x17\xd6o\xed7\x12\xdd\"p\xc9\xc6\xb5\x81\x95\x011\xbf5\\\xf8\xf7\x9ejd\xb0W\\\x80T$\xbc\xd7&23\xcfg\xcf\xe3\xd9\x8aL\xe0\x9d\x1e\xb5\xe3\x8b*O75I\x167\x13\xc8\xf5uf)\x89K\xde\x8c\x9b\xd2\x85\xf33;\\\xf1;')\xa9 \xbb\x8a\x98t\xf1\xf7\xdd6\x91-\x94\x16\xcd 6\xa8x\xf4\x93TE\xf0 \xbc\xd5W\xba.\xe3\x82\xd7H\xf45\x96\xa4F2n0\xbfG\xdd\xf7\x04b\xfd[\xf2\xa9.\xe3Y\xfd\xb2\xcc\xd7\xd8\xc8F_M\xde\x06\xb9.\x87r\x19x\xce\xee\x920\x81\xec0\x88W$\x9e\xa3\xa1\x87}\xd3<\x9b\xcdHQO\xc0\x8b\x8b\"Mfh\x8f\xf3\xe0\xe7*\xcfBP\x9f\xdc\xc4\xeb\xd4\x1b\xde/\xc3\xf47\xcd\xe3\xf9)\xdaF\xef\x98\xe3\xaf\xdd:\xdf\x0c\x8a\"\xe8^\x84G\xf6\x80\x91\xce\xb6-_K\x02_\xc5\x0b\xf2c\x1e\xcf\x07=\xb4F\xe1-\xc7\x19#\x0fH\x97\xe1\x1dcF?\xe4\xe8\xa42\x81\x99\xbe\xaa\xb8\x1f\xf9\x8b\xfa\xc9%\xc9&\xb0\xe8\xd3\xa5\xa0k\xb9\xc3\xa7\x08G\xf0\xaa\xaf\x8a\xfc\xd9\xaa4\x17*V\xa2^\x0f\x10\xf5z\xa0cp\xd0\xeeD5J\xa9{\xe6FcMZ\x1enm\x0ds\xf0\xed\xf6\x9f>\xfa\x02C\x1a\xf5\xcd\xaf\xa0Z.\xad\xeb \xdb\x1a\xec\xc0\xb0\xd1\x0e\xe8\x8fI\x93\xc29\x17\n\\3\xba\xf6\x87\xc1\x14\x95h\x12\xa7Q!\x99\xb5\x94 ^1\xe8\xa7\x85lv\x1c\xadI\x1dS\xa4\xe6\x7f\xb24\\6\xe5\xe6f\x1b\xe5f\xdeUnn\xacZ\nf\xd0\xd4Isk\xfb\x08T\x0dl\xfb\x16\x1a!\xd8\xe813\x88i\x9b&\xc3$\xb5\x08;\x8fH\x88\xabL\xb1m\x89\x003\xf8Vhn],\xdag\x98\xee\x04\xb7\xc3\xf0X7[\xf0.\x80\x1d`B,8\x82Y\xcf\xfe\xa2[\xa8x\xcd\xf8\x1d\xfc\xc0\xdfca\xd89\xfb\xf4\xcbm\x08\xb3 \x88\x10\xd6n:\xd7i\"\xe5\xe8M\x08\xbf\xdc\x062c6\xe9\xf8\xa78\nb\x887I;\xc4\x97\xfd+\xe0_624\xe5\xb8\xed\xb8A\x0b.\xa4\xa3\x8b\x81\xa0W]\x13\x89\x94`\xfeqH2h#*\x8b\xbdT\xb9\xe0)(\xe6\x1d\x1d\\\xb5\x9bU;\x9b\x18'\xd1\x9a\x94K\xf2\x82\x90\x82\xae\x98E`\xba\xb5\xc5n\xe2\xad.\x98\xac\xdci|\x16\x04!\xcc\x18]\xa2\x84J\xd6\xe2\xba\x9b\xa9D\x96M\x08\x1eV\xf3\x02\xfaM\x9fG\x10\xc5Y\xd6i=\xc1XTc\x0eu\xeb\x19\xd9z%e\xf7\xdf\xc8\xd8T\xfd\xf5+\x1c\xd8\xf9\xd0\xadl\xd2\\\x90\x8e?&\x1b\x9b\xf0Qgei9+{\xd9\xd6q\x1d\xec^\x82\xe2\xbc\xec8\xa6O\xcf\xec\xea\x9d\xfe\x1d\xa2E\x1c\xe9wC\xa9q\xd2\xb1]+\xa3\xaa \xb3\x10\xaa\xa1})e\x90\xfey\xe2@\x84\xdd\xb4}\x9bi}\xa6,h\x19\xc9\xa5{\x1d\xcf\xca\xdcO\xed\xa4e\x94.E\xe0]\xe3\x87j\x0bR\x03\x0d$\xf2\x0e9\x1dv\xec\x18P\xb4\x04\xea\x8a\x88s/\x0bac\x10\xb3\xb4O%!\xd64d5\\\xfdoJ\xf6oB\xc9\x9a\xa4\xcd\xa3(\x99i/\xd0\xd1\xc6z\x1aa\xda\x08\xd2\xb1qC\xd9\x122d\x06NK<\xdd\xb4w\xf4:\x9f\x93T\xc0\x9d\xedjZ\xc7\x80\xeaN\xbbY\xe5\xed\xed\xbbx\x14\xe3>~\xaf\xc5\xff\x8f\xef5\xfd`\xcc.*\xd2T@\xdf\xf3l\x95\xa4\xf3\x92d\x13]\x8cq\x16e\xb0v3BM\x86l\x95\xe4\xe1&b\"\xca`\x0b$*\xca\xbc\xce\xff\xca\x9fgp\x8c\xbbe\xd3\xde-\x99R\xab\x89P\x8a\xc6\xc4W\xec\x99\xbf\xa7\x04\x8c\x08|\x12\x89\x99i\x94\xcb\xc6\xd3T\xb5\x84e_Ok\xc3\xa5V\xab\n\x1cAB\x913\x13\xa3\xd1\xba\x19t=\xf9~u\xc2\x19\x0fY\xfcm\xf8\xcbC\xdd\xcbJ\x98\xd7i-\xe8RA\x90\xb5\x0d\xcfTM\x91 \xf2\xae\x17i\x9d\xb4\xf6\xcc\xb0M\x86o-\xf3\x9cR\xc1\xdc7\x9a\xba\x81\x8d\xe8t\x1c\xc9I\x08S\xf3hd\\\xac\x11\x81\x89\\\xb8\xb9\xabnP\xf5\xb8$\x19\xc6\xc2\xda\xb1\xa5\x1bB\x1b\x13[\xfb\xa0\x08\xc5dJ\xd4t\x03v\xd5\x08p\xa3\xe3L\xee\x00;K\x17O\xcb38\x86\xc4\xa7\x7f\x0821a\x8fq\xbd\xe8\x83\xc1V\xb8\xe7u\xe2\xcb\x85f\xcdl\xd2t@\x91\xae_\x7f{\xc0\xa9;\x8e;G\x17\xc5\x97\xb1;\xa7g\x81\xd6\x19FL\xccE\xed$\xd9\x04\x19\x15\x92\x81$S\xd3,*\x7fS\x9ei\xef)\xe4\xf0}c\x87~\xef\x1e\xf8\x0c\x03\xf2\xb3\x10|D\xb8\x86lN\xcb\xb3\xe0)\xe4\xbb\xbb\x01\x0b\x911--\xd7\xfbb\x1a\x18\xe0E\xa1\xd7_eu\xd8\x8e\x18\xb3F\x0e\xdb\xaeu\x03A\x945\x82cfi4Q\x9f\x1e\x888\xc9Hu\xd0\xafE\x11\x1cu6\x0dN\xfb\x12Ui\x8dA\xa8\x05\x0f@\xdd\xc9#6\xa4\x98j9\xcd\xd0\xa8\x9eE\x8e-Y\xfe\x85\x1c\xad\xd4\xd0\xe8?\x04\xfalxg*\xc4w\xf4V4\xfa\xb7\x9b\x99\xf7\xd9X\x06o\xf8\xd6\xe5p\xc0\xf1\xf9\xdf\x8b5T\x7f\xfd\n\xdc\x84\x10\xc3\x1e\x0e\x89aZnB\xf0!\xfbZ\x8b{\xc1\x88\xeck\xe5;\xc9\x89<2q\"\x99\xff\xed\x00\xf6\x0cr\"W<\x03Y\x87\x99\x94\xa2\x1bKs\xab\xf2*\x03\x9b\x1a\xb7%f\x0b\x9e\x85\xb0\x08\xa1\x08a\x1e\xc2\nMF\xd7h\xbdv\x03G\x10\x97Kt5T2m\x1d\xa0uYc@!\xabL\x0f\xe8!\xda\xfaI\xf9v\xfdn\x97Z\x141\xf6\xeb\xd29\xf2\x14\x9e.O\x9f\x06P]'L>\x14\xd9, \x86\xce\xb1\xd11LW\xe8\x90\xd5S(\xce\xe1\x08nx\\\x99\x93\xacNJ\xf2\xa1$\x84\xa5\x18\xbe\x11\x86\xf5,\xb50\xad\xf6\x8f\x0d\xa9\xeaWYM\xca\x19)\xea\xbcd\xc9\x86\xe9\x9b\xaa\xc8\xb3\x8a\xb4^\x15\xf8\xaa\xad\xe7b\xd9Jo4\xb22\xcbGl'\xd2\x80\xa10\xea\xd5\x8b\xa4\x9a\x95\xc9:\xc9X~\xbe\xcc\x8d{\x92\xa6~\x06+\x90n\xe9O\xd9x\x83\xdf-\x1a\x98L`\xe1\xf6m\x1bh\x13(\xdc>\xebCu\x02s\xeb\x97\xb7!\xda\xce3\xf6[\xa6\xbe9\xbd\x8e\x97KR\x06\x0e!\xf3\xa0 {h\xadKe\xb15\x86\xf2d\x8aY\"\xb2\xac~\x1bv%\x8cN\xea\x0d*\x8c\xael\x863\xa2\xb0\xe1\xac\xdd\xc0\xd6\xcf\x80\xe1\x1a\xad\xab\xbaL\n\x11\x85\x14\xedl\x06\xadcD\xb1^\x12\xe1&\xfe\xd6y\x13/\x99\xe3/\xc9\xea\x10vJJ\xc2\xda\n|\xe6\xdb\x99\xa9\xcc\xe7\x12\xc1\xcfW]\x91\xf8\x97|Y2\xf4\xd6C\x16\x9f\xaeQ|Qn\x8a\xda\xf7X\x87^\x08K\x97\x19X2\xad\x8e\xc9\xac*\xb5\x18\x96L\xaaF\xc6\x960VI\xebb\xd8\x9f\x8a\xb8\xa5\x93j\x8b\x81\xc3F\x0e\x0d\x93\xb0p\xb9X\x9e\x14V\x9d\x99\x1f\x8ce\xaa\xfe\xbdX#\xfd`\xf2A&@s2\xef\x19O\xe6\xbd\xf6\xc9\xbcg:\x99{kjSE1\x0b\xe97\xf1z\xc0+\x809d\xaf1\n\xbb\xb9\x16\xc6\xe2\x8d(Yf\xe1\xb2\x0c\xb9\x9a\x9dG\x08|\x94\x89\x1eV\xfbFX\xed\xb7a\xb5?\xc4\xc5\x80\x8a\xdb\xe4\x13\x99mj\x16rZa\xcf\x86\x891#\xc2\x04I\x8ay\xc7\x86]\x1aDB\xf0\xfa\xe7\xae\x87O{G*}\xbc\xa9H\xf9\x92\xd4\xb3\x95g\x8d\xc1&V\xd4\xca0\xb0%\x9d@9\\M\x0d\xcaeI)\xac,\xffP\xa8\xb4\xdb\x10\x12\x831\xb7\xf5\xd6\xde\xac\x1f6\xed\xb6\x9a\x1d\x1d\x94\xe6k\xbb\xe4*\xd9\x0b\xfd\xdbF\xcd\xc1\x03\n\x1c\x03\x95\xd4\x0d\xa0\xcd\xb1-\xbe\xcc\x1f\xe2\xa5\xbeV\xd2n3\x87c\xf0\xf87\x1e\x18\xcd\xa4c\x96\xec\xe7\xe0m\x03\xe4\xe7\xf9\xba\x88\xeb\xe4\"I\x93\xfa\xe6u>7\xec\xe2\x8d\xc1\xdb\x96\x96\x05\xbe3\x92\x12\xc6\xaf\x90x\xb6\x92\xdd\x06\xf4\xa8\xb0s\xfa\x8d\xb6\xdbNb\x18\xd8l$&\xc5Z\x12\xc7\xf4[\xdaO\xa3:^Vp\x0c3\xfeg\x00\x13\x98&gc\xcd\xc0[\xce\xb4G\xaa3\xad]\xbb\x8a1\x1cX`\x1c\xfc\x8f\xddF\x0c~\x06\\\x97\xcd\x00\x9e\x17\xaf\xe6\x81\x9f\xe2\xfd_n\xdb\xf0\xa2\x0c\xa3\xc6\x04bk+:W\xedn)PDv\x1b\x11\xe7\x98\xed\x8d\xc2\x18\xba%\x8a\xa0_\x86\xfd\xd2-\x12q\x9c\xfd\xd9Z\xe4\xccL\xdeE\xb1\xf9wQ\x8c\xdaLgg\x01\xd0\x7fwwCH\xa6\x9e\x07\xbb0\x83]|D\xf1\xa5\x18n\x83\xa9\xa9\x9b\xb0D\xf4\xecK\xb0M\xfb\x8aP\xcc\xa4\xa2)\xed\x8a\xa2\xa4C\x04a\xacz\x04s\x16\x8a|\xfcp\x81wK\xe5^:L{m\xeeyA+\xb7:\x9c\xd3\xde\xcc\x89\x9bAQ\xe2\xb31\x17\xc6\xba\x06\x06Z\x7f\xa9\xd66;\xfb\xcaj\xb0\x10\xea\xa8\"\xe9\xc2\xe0'\xac\xde\xb2\x1d\xf6-\x10\xd6\xf1%9aL\x0c\x1cQ\xb2\xc1\x1e=+\x92\xeaC\xbc\x94\xb4\xa1\x92\x7f5\x95\x9d\xf4Vw\xc0\xb2\xea\xf7\x1dj\xce\xd4\xe1\x1b\x9d\xf63^\xb3hMh\x80\x1a\xd9h\xe2v\x07*t8?s\xad\xd9\x85Ic`\xa2\xb5\xa5\xe1@\x96w29$\x99\xe9>KVJh\xa5r\x9a\x9f\x0d*\x9c$\x81\xab\xb47\xf4\xc0x\xb5l\x9a\x9f\x05\xd8Xs\xf8V,,\x8d\xb9i\xceMO\xf0\xebi\xa2W\xf2\x9b\xf9\x0e}\xc3q\x91T\xba`\x81=\x1b\x0d=\xe6\xffK\"\xfaV \xf8\x8f\xd9\x03nK\xd9\x9e*=K\xfa\x84Q(\xf6\xbf\xd5\x9a T\\u\xdf\x7f\x93\xda\xb0\x02\x9a%\xd1\xbalj\xd6z6\xc6}\xa5g\x89\xca\xb4\x12:\xd7CMW\x0b\x16.\x8d\x1d\x1a\xfa~\xba\xf03:\x17*\x88\xa9\x13\xdf\x9a\xa5\x19w\x07\xf6\xe4` \xce\xf1\x7f\x86\xa6\xe7\x0b\x85O\x85\xd14\x1f\n>\x89*2\xdb\x94I\x9d\x90*\x04\"\xee*0JPV\x7f\xb8)\x08{\xca\x14\x08\xcac\xc3I\xc3\xa4\xaej\xb6\"&\xd9\x8c\x89\x9c\x9a;\x11m\xed\x8a\xd7\xee\xdf\x93h\xab\xcf\x98\xdc\xcd\"\x19\xfcT\x1ax\xf2\x05\xd6\x92\xea\x0f}\xa5\x82\x81\x87\x0f\xf4\x87|~\x13\xa2\xb6\xb8\xbc\"\xa5a\xf2s\xaeP\xa6U\xfe\x1a\x97I|\x91\x12\x83S\xed\n\xab\xae\xea\xdapE\xb1\xe4R\xaeP\x93\xe8k\xdd\xb4k\xfd\xb0I\xd2\xb9\xb1\xb2\x08\xe2\xf5)J\xaa\xb7\xcfN\x0f\x03\xbf\xd6\x1c\x147\xe8\xaeO\x1b~\x0b\xc7p.\xef!\x95\x88\xe8\x86 \x83\xef\x8c\xc4bS\xa6\x13cd\xa3YI\xe6$\xab\x938\xad&\x80Z\xf6Ut\x9d\xd4\xab\xe7\xcds8\x06/\xc9f\xe9fN0\x0ca\x15\xaf\xc9}\x16C\xcc\xd0h\xe3\x08l85gy~\x89q\xdeuF\x84\xfd\xf9\xc5\xa8\xfd\x7f\xa7A[z\xb4\x07!T\xb2B\x0fS\xe1\x08*\xca\xf4\xf3\x1a\x12\xed(=7\x80\xf2\x83\\\xaa%\xa9%\x91}\x1f_\x07CQew>\xa8\x91U\x9f\xfb^\xc3\xa4P\x89'\xc3\xd0\xb1Y^\xc3\"\xdfds\x9d\xab\x10\xed\xfb5F\x9e\x94\xd4C\x0f\xbeWmm\xd3k8\x86_na\x02\xaf\xf5\xd5\x7f\xc66\x87t1o\xb0\x86\x10\xd7\xf5\xf3{\x17m\xca\x14v\x8f\x8c\xa6\xa1\x83\xaa\x01F\x93\xcc\x01\x03$\xcd0\xdeT\xb2\x8dm\xbcU\xec\xec{c\x18\x9dF'\xf1\xc6pdr\x1d\xc4\xcf}\xcc\x0cB\xd8\xc9\xa4\xa5\x8d\x88(\x10ql\x0e\xe1]\x1fr\x12joBx\xc7\xd7\x80\xa2\x17J\xc1?\x07Q\x9d\xffT\x14\xa4|\x1eW\xc4\xc7\xa08G\xb0d\xca%=~\xbc\x97*\xfej\xfa\xe6\xccT\xb3\xe4\xd8\xce7b\x14\xa3\xbb=e\xa7\x0ch\xf7\x02\x8e\xe0\x99\xe2\xa9u\xea\xbfR\xc8_\x104\xcf\xdf\xb7\x9ek\x9a{1B+'4\x8a7S\x12%\xd9\x80-ai\x89\xb3\x85\xaa\xbd\x8b|~\xe3\xc9\x18\xb2\x8ca@\xbc\x8b\xd5\xbf\xa3\xc6h_Z\xb4-;\x11\xb5\xd0:\x8a}\x94\xc5k\xfck9e\x7f\x9fQn\xce\xf0>\xc1M\x1e\xb10\xadX\x19&p\xe9\xb3\xbfCx\x11tn;D\xc2\x96\xeb\xb8\xcc|\xef\x9d\x80+\x8f\xd4\xcf\x9a\xc6p\xfdI\x05\xf1\xfa\"Yn\xf2M%\x83\xdb\xd7+\x02<\n3\xee=X\xc5\x15\xac\xf3\x92\xbe\x893\xc83\xd2(\xfa1;\x00~\x91!\xee\xf7z\x88\xb39\xbe.\xe2\xaa\"\xf3\xfbI\xa6|\x8b\xba\x8d\n\xe6 \x8b#\xc6\xfa\x848\x83?$\xd9\x1f\xd8\xdb\xc8\x0bB\x11\\\xebh8\xf6bG\xd5%u\xeb\x8a8\x86\x91\xb9\x1bsCy\xf2\x85\xbd\n\x8cCHJ2\xa7\xbfvH\x84\xb7\xe2'\xeb\xa2\xbe\xf9+3\xf9nH2\xf7\xe2|/>h&\xd8\x06\x06\x856\x9dgQ\xe6W\xc9\x9chI\xb5:\x99\xb7]L\xf3\x98;\xa8@E\x8ev\xf5M\x81\x88\xa2\xd1@\x976\xaf\x0d\xe0[@I\xa3:\x90.\xdf\xcdK\x03d\xa02\x058M\xb48\xec\x85;\xb6vqA\x84\x97\x8c+\x1c\x91!\x041\x18\x15s\x80l\xf2\xbd{\x90Y\xb4\xce%\xf9\x871\x0e\x8d(rl\xd6@h\"3\xc1p-E\xa9\xfcj\xb8\xa6\xcdz\xc4\xd9\x9c\\\xa7f\xa6\xa4\xf1\xc7\xbe\xa9\xc3/\xcc*@\x0f6u\xe8N\x9d\xa0\x9d\xf1;\xcem\xd2\x9e\xae\x9b\x9e~\x0c\xe1]\xc0\x83\xef\x9ct\x1e\x07\xe2\xcc\xc3M\xda\xb6\x80\x97\xe7a`\xf1\xbd\xa43\xfc\xa9\x9f\x8aM\xf9~l\x98/q\x9c\xc8&\x8c\xde\x18\xa0J\x96\xbb\xe0cP\xfb{\xc8\xdeb\x18\xec&goE\xca\x04M\x8b\x06l\xceoC\xfa\x99\xbe\xa7\xe6\x10~\x8ec\x82#\xf8\xa9\xbf6\xfd\x13\x9c\x0d\xee\x9d\n\xe8>\xc3\xc1\x02#\xa17\xf6\xab\xec\x7foHy\xf3\xb6|\x99\x97\xeb\xc0\x7f\x17\x84\xf0\xeew\xed>Z?m\xf7\xac\xcama#\xb20\xb9\x97\x9e\x80ng\xbbMV\x06)/\xdbo\x14K\xa7\x1b\xc5\\\x11\x02\xcd\xb5\x12'A\x15\xa4\xbc\xec$TB+\x99!\x12\xffXp\xe6\x03\x86{\x15\xdf\x02J\x92\xb6:\x84\xa9\x87<\x9e\x87\xf7\x85~\xc9\x82\xd3Rv\xf1\xc7\xfc\xbaa\x17=6\xb0\xca;\x0bD\x9c\xb7\x81f\x1cj75\xcc\x03N1n\xbb\xf9\xfd\x8c\xc7\xd94sj9\xc5fDi\x97,\xae\x14\x91\n*\xc6\x8dL\x85*\xcd@6\xa59*\xdb\xd0\x0d_!c\xe9\xe5\x01\xfc \xee#\xcf\xe6\xa7\xec&\x86\xce\xb2\x9a\xaaUL>\x93;io\xba\xb2\xa1j\xbawF\xc7'\xda\xdb;\x0b(1\x14\x8dz\xbfxM\xcfn3o9zL\xcf\x98\x87\xc7\x83_\xfc\xe9\xdfo\xcfv\x83\xdb\x07K\xd5\xcf\xe3)\x0bs\x81\x862> \x9e\x06T\xb6\xd8T+\xbf\x9c\xee\x9f\xd9}6\x0d*`?\xdd\xe6f~\x16]\x89\xfd\x85\xbcq\xf3sJ\xac\x97\xa1b\xc2\xed\xaf\x86\x8fo\xe0\xc4g\xc3\xef\xf3\xa5\x0d\x9b\xfd\xb3\xb2\x13\xc9\xfd\x17\x99\x1c\xe6\xd6\x0b\xc1[\xda\x02\x81\xd0\xa5O\xa5\x97j9\xe8\xccd\xba\xdb\xd4\xf7\xd0\xb5\xc6\xb2m\xac;\xb9\x1c\xb1\x85\xcd\xae\xef\xc2\xe2\xcb\xd6 ]\xca\x95<\xb6\x19\x93l\x8b\xdfPj\xbe\xa9-\xdf\xd0\x13\xe6\x9d\xcf\x1dLgy\x8a\xb4\xf4\x9d_\xb6\x1f\xd8F\x9b\xe0\xbe[\xe5\x15z\x1e\x96\xf8\xd7\xf0\x17\xcc\x85\x8e\x92s\x14T\x1c\xfap\xc9\xac\xcb\xf1E\x84O\xf3\xe97H\x9e\x138\x86\x9cb\xf4\xe4\x01\xe6\xd4\xf0\x13\xd8\x85\x18\x9d\xf0\x82\xe9F\xf5\x00\x84c\xd8\xb4\\\x99`b\xc8\xbaz\xeb\xa7!hr\xb2\xdf\xfa\xe8\x9bk\xa7\x15\xe3x\x8a!=8H\x8e\xc2\x85\x0b\xc8\xdb\xc7z)R\xb2XX\x8c.j\xe5\x03\xa8E\x97\xb7}oT\xf3 T\x98\xf4K\xfc`;\x0e\xfd\xad\x8cma\xf4/\x8a!1\xc3\xcd\xa4\x83\x9b\xab\xba.\x06p\x87\x19\xf4\n\xdcL\xe4_C\xf8\x96\xe27\"\xb0\xbb\xad\xf6\xcc\x82\x99]\xac\x9caz\x17>\xc9\xae\x99+\x96\xf6\x89\xf0\x1b\x17&\xc6\xf2\xbfy\xf80E\xdd\xc4n\x98e\x8di&i\xa2\xe6nU\x03\x82\x7flH\xf9\x95V\xc86{ &\xb3\x8e\xbd\x8ep|\x08\x03\xf6\x17\x87\xc0\xce>w{\xbbw\x0f\xbc\x8b'?\xbd\x7f\xf5<_\x17yF\xb2\xda\xcf4\xbe\xa7:\xcb\xea\xbc\\\xbf\x88\xeb\xf8_\x12\x00~\xc64\xc1=\x0b\x16F\xa5\xe8\xd8\x11<\xf8\x87D\x13\xfa\xcbiC\x89-a\x1ee\xa7\xe3I\x7f,\xe6o]\xb6\xab\x1ei\x1d\xfc\x05\xfe\x93\x03\x0d\xa8\xbf\xee\x9c\xc5\xe8\xcb\xf9\xf9\x90\x12P\xc4`\xd2\x8a\xc8B-\xf9\xed\xe3q\x81r\xff\x05\x08\x8e\xb9bC\xa9\xcdu\x10*QU\xdf\xa4\x03\x95P/K\xd14\x1d\xf6\xae\xe9\xabr\x86%\x18\x8c_g\x1b!8moZp\x16\x13HP?_%\xeb\x82\"\xd4\xe0\x17|J\x13\xd8\xd0ol\x990X6\xa0 \xec\xec\x1b\xab\x99$\xcb!\xfa\x9f\x0b\xd2\xaf\x0bL\xf2\x1f\xc9\x98\x99\x19\xb06K5\xcc\x88l\xfa\x91\x0e\xbcM\xc6mF=n\xdb\xa5\x04+\xd2\x99\xb6\x8b\xe2\xcd )\xde*\x86\x8d|Op\xc3\xb1\\me\xa4\xb4\x0f\nq\xca\xacY!\xdb\\$\xc5\x8c\xa9\xbc}?\xf3\x86\x0fAQ\xf8n\x19\xb5\x15E\xc1-\xe9\x98r\x95\xf7\xe3\xe8\xce\xcew\xa7\ni\xb7\x0f\xc5\xb6\xe3\x07\xf6{\x82f\xb4\xf0\xd0IP\xcd\xc6\x1dJ\xee;e\xf4\xa1\xd0\xdf\x1e\xad'\xb7}U\x0b]\xdf\xa9\xc7S(K\xe6\x8c\x12\x9e\x9a\xbf\xec\x9ad\x11\x14\xbb\xa6g\xae\xdd\x81\xeat!\xc1\xb0\xff\xa8\xe3\xe5\xac\xdf`[t\xe2\xfd\x0f\x14\xfcM\xed\xfd\x9c'\x99\xefi\x9c\x13\x95w\xd0E\xd8_]#\x9b\x0cid\xe3F#\xdb\xd5\xb9\xb2[\x90\x17I\x85\\!\x99S\xfc\x88g5;\x01\xf3P\x1f\xc3\xdeb\xb8i8_\xb5VF\xf5X/\xb0Krcc\x04\x9cTl\x16M,3\xfd\xb42D\xcc\xafk\x88\x1e\x00W\xeb\xda\xe7(\n\x87\x13\xe6\xd6\xb2Ku\xe2(\x1c\x8e\xe1h8\x8f\xa0\x7f\xe6\x88\xc2\xa2\\2\xa6\x92\xb15M\xb6\xdc\xf1{lc\xca;/7Qhrv\xc1\x81\xa4\xf1\x05I\xbb\xe3`.\xf2_e4\xd1\xe0h\xd6q]&\x9f\xbe2X\xc6&r\xe1M\xb2,2 \x1c\xd3\x83\x84\xb9\xfbQ\x06\xef)\x05U\xcdX=\x0c#2a\xaa\xce\x10\x7f\xe9\xc70\xe0\x8e\x8a``\x8a\xb4#\x9b\xa7\xbe\x90`\x13\xee\x1c\xdb\x8ccB\xfb73\x9e[\xc0\x15\x1c`\x0b\xcaBkn\x02\xc0(\xed\xb3-Q\xc43\xf2\x82\xa4\xc9:\xa9)\x93\xee4\xfd\x94O_\x99\xf8o;o\x0f\x83\x15\x18RX\x0d\xcc\xbeH\x8a\xd1\x93\x9f\xfd\xcbM\xfe3\xc6\x0eu\x9dh\xde\x0d H\xeb\xa1AE\xc7\x1d\x92\xbe}\xc2\x1c\x92\x1e\xe9\x1d\x92\x985\xf9#]~\xff\xd4i%\x05\xec&\x0f\x8e\x7f?=\xfb\xffv\xbe\xb9\xf7\x07?\xf8\xe3n\xf8\xf4\xc8\x93\xf7\x19\xdcp\xb6?\x15\x8d&~L\xa7\x0f\xfe>\x8d\xef\xffs\xef\xfe\x93\x8f\xf7\xa3\xf3\xff:\xdb\xfd\xe6A\x12\xd5\xa4\xaau,\xd7\xb6~\x01O\x0e\xf7\xb7\xb7\xd1?\xd8\xfe\xd3\xc3/0\xefo\xbd\xfa\xb7\xd4\x8a\xca\x00\xa9f\x95\xa6\xdd5\xb5\xec[ a\xcc\x9a\xc1\x84(\x96\x08\x95\x9a|(\xd8\xe6`\"\x14\xb3\xdb\xef\xa2\xef=\x8bw\xa3\x86\xcbbtR\x8c\x84\xc2\x9d\x18\xdc{\xe7\xed1\x16b\x8c\x06\xdfeLx \x80\x89F[q\xeb\xd7\xd4\x10n\xe4\n\xb3-\xdc\xbb\x07;;\x1d\xfd\xea\\D\xc8\xd2\x7f\xb8\xee\xc7\xc6\x8aC\x98z3a\xf6\xac:\xfd\xde\x9c\xb2\xf0\x00<\xb6\xcfP*)\xe5\xa6l\xd1\xbd\\]H\xe3\xb4E\xdb8\xad3\xf42P\x14\xd8W\xf4\x1f\x16\xd3\xa6s}\xd5\xc0\x0bG\xd5\xfc\x94a\x7f\x8e\xc1_il4\x06X\x13\x19\xe0&\x83$\x1bN\xde\"8\x98\xf9t(\xb6$p\xa4^O\xb3\x01{\x0f\xb4\x07\xb0\x9d\xd3R\xa1\xcb\xf3\xd6\x7f\xfel\xbb\x10\x03\x8e\xfd9zN\x0c\x9b\x9b\xb0!X\x9bCy?.\x92\xffEx4\xcc8\x00\x0f\x17\x93\xdf3\xf2\xe0\x98\xfeB8\x19\xc8\xeb\xf0$\x08\xc1c(\xd1\xab+.\xcf;\xb5\xd9\x9dp\xaf\xb6\x08\xc0\xa6\xd6\x1e\x9e\x1d\xa8>\x18\xcc/^\x8c\xde\xce\xf2\x80\x8c\x01\x1aW\xc9L\x8c\x86\x85\xccp\xfd\x1e\x14\xae \xc1@\xc1\xf6[\xcfnAuYT\xc4Uu\x9d\x97\x03a\xcatE\xc8\xb3\x8a\x7f,\x0buA\xd9\xa3\xca\x01z\xa2\xc8\xb5\x8a\x9e\xa9w\x8ep\x04\xde\x0f\x14\xfcN\xf1\xbf\xbc\xe5\x81*-R\xae>R\xa1\xe0r\xf9\xb9\x87a\xdf\xe9\x06\x8eVq\xf5\xf6:\x13'`{x\xb9-_\xb2d\xb3 \xcf)Bi\xfa\xdeS\xa8\xe1{8\xf8\xf6\xd1S\xd8\xdd\xad\x03 ,\xda&\xf3\xca\xa1t\xff{\xd8\x7fD\xb9\xb1=\xc5\xf2\xb1\xe5\x17\xd4q\x0c2\xab\xef:>:\xbeR\xb3\x8ebJ:?\xe4l\xca\xb6\xb3V\x91\x18\x8e\x00s\xce\xd5Q\x91\xc6I\xc6>\xa7\x9c\x1a\x87\xdd\xac$qM\xfcl\x93b|y\xca\x0b\x96l\xda%|/\x1d\xb8\xe8\xdc\xcb@UV\x91iy\x86\xf8\x98\xd1?\xd8\xef\xee\x92sS\xe9f\xcd1)6)\x97\xa43\xfe,\xec;\x92\xa2\xba\xb6IC\xd9\xe1\xc3\xd9\x0d\x99T\x7f \x9d\x9b\xd6\x03\x81\xd6\xed\xc6\x0e\x96\xeb\xa8\xb3\xa5E*gVDk\xfa%r\x9cS:\x1d\x83\xe8\xe5\xe7\xedE\xf8\xfc\x99\x8a(i\x9a_\xbf\x13\x18\x8c\x0fw\xcah\x16\xa7\xa9\xdfEo\xba7\x18\x11 S\x0cv\xbb\xb37b\xc3\x0fy\x809LK&\xcd\xecBLp\x87D\xbb\xfa\xbd\xa0\xcd}\xef\xdf\x8c\xcd)A'\xd0\x16\x9aS\xdc@m\xa7\xae\x95^#\xc7\xe0g}\xc1:\x0b!\xd1*\xc0\x18\x8c \xbe>\x062M\x10\x9f\x15\xad\xb6\x84\x02}\xc5k\xfc\xff\xec\xbdk\x97\x1c\xc7\x95 \xf6]\xbf\"P3KU\x0d\n\x8d\xee\x06@\x11MAt\xa3\xbb\x014\xd4\xe8n\xf6\x03 \x00a\xa0\xac\xcc\xa8\xaaDge&\xf2Q\xdd\x8d\x11\xe6\x90#\x8a\xc2\x83;\xb3\xde\x91\xa8\x91=cy\xd6$H\x00\xb3^\xdb\xeb\xb5\xd7\xf6\x8e\xf7\x1c>\xd6>Gs\xa8\x99\xbf\x80?\xb0\xfe >\x117\"2\xf3\xde\xc8\xac\x02 R\x9c\x1d\xd59\x12\x1by\xe3\x1d7\xee+\xee\xbdqFcp[\xfcSc\xeeB\x81M\xe2o(X%\xf9B\x8e\x97\xbe\x9cjS\xf7\xf8a\xda\x0e\xada4\xd6\xe1j\xd2\x1b^\xf7\xebc6ms\xc2#v\xf4\x88\x01\xe8t1bT\xde.\x01\xbe\x90\xa6\xfe \x9cDs\xd4\x18\xca\xf3\xcb\xa6\x0f\x13\xd2H\n\x88\x9d]\x0foX\x06\xc6\xd1\xc0<.$\x95F'A\xfb\x8b\x93\xaa7\xa8_\xc9\xb1X\xce.|Tf\x17f-\x946\xc0<e\xbe\x9e\x9e5_O\x7f\xc7|\x9d\x9b\x9f\x97q\xc5G\xf5\xc0\xe4\xa0\xd8\x82\x80\xb2\xb9\xf9W40\x12\xd8\x0e_\xe7gO\x96>\xcf\x9d\x9eg\xb2\xd9\xef\xb1\x97o\xb0\xa3\xe2\xcb\xfc+\xecG\xec\xe5\x13\xec%f\xea\x9c:5\x7f\xfae\xd3\xff\xa9\xef\x9c8y\xb2hb~\xfe\xa4nbn\xbe\xdc\x06\xb4\xca^b/\x9f\xb07\xddND\x0bs]\xb9\xb0/\x9f:u\xe2e)S\xcc\xcd\xce\xcb\"\x1d\xf6\xdd\xef\xb2\xb9Y\xf6#\xa6\xbe\xa0\xb5\x97; C89k\x86\xf0\n\x19\xc2\xdc<\x19C\xf3\xd0:\x0d\xac\xc2\xce\xd5\xddh\x14;ns\x14n\xf5\xcd6\x8aaQ\xefV\xdd\xc5Cd\xbdr\xa0\xe2g\x9cD\xf1\x02kE\xd5\x0c{\x96fI\xeef\x91zH\xbb\xf4\xa1\xe8\xab\x16\"4\x85b|\xdfb_VaU3/\x16C \x1bTS=\xfe\xcf\xe6g\x8f\x0f\x8a\x16\xca\xf7\xc4\xd5\xc50\x97\xb2\xad\xadsK'N\xbf\xf22J\x1f\xd3\x97i\x89\xe1m \x8a\xbd[\xe7\x96\xe6\xbes\xe2\x95ib\x8c\x88\x90\x19uY\xeb\xa8-\xf3\x04\xa5\x13jh\xcf\xd1\xcd\xc4+\xe6j'f\x1e-\xf5W\x8b\xc0a\x00f\x95\x9eo_\xf5\x0e\x02E(6P\xbe\xbdF\xb7/l\x9f\x9e\xc3a4\xbe\xfa>\x8f\xbe\x9b0W\xb5\xbd\x93n\xfdY\xe9\x04H\xef\xc8P\xbf{\x02O\xb9H\xc7\xac6/;\x9b,;\x99<\x13\x19\xf9\xf8\x1a\xe33\x03\x9e\xed\xf8#\xde\xee@\xf5\xd2\xbf\x17T\xbc\xfe\x11x\x19\xcf\xa2!Vt\xa6\xe2\xbb\xcc\xf62\x03\xe7@\xca\x9f0\xb0\x05\xf9\x97\xfcc\x9aY2\xb5\xf0A\x97\xb9\xf5t;oC\n\x97\\\x12h\xb52G,~f\xba\x02/\xf6\x0fhp\xf1\xef\xa9\xea\xfb\xd2\x80\xa0\x0b\x1e\xf1\x85\"\xa03\xe3\xe8\xd3\xd1\x01\xf3\x91\xfag\xd6\xe92\xc7\xcc\xb4\x81\x07\xa5\xb2\xe9z&#\xad\"\xe94\x13ef\xb2\xca\xbc\x083E\xbaDSm\xc9\xd0\x02`bA\xc5\x18\x14\x1c=\xda|\xe7);\xbe\x1e\xdcP,.\xb81U\x87\xba\xc8\xb4\xe9\xfeX\xad~\xa7\x7fc\xf5\xe8W4\xf1\x8d\xd4X\x96\xcaj\\\xf6\xb4\xc67M\xd2\x8c\xba\xe4s\xb5{\xde/v\x88\xc5\xd3n\x90\xdc\x9c\xfeL\x1a%Y\xbb\xd3e\xb1\xf9K\x06\xea\x95\x9e\x88\x14{\xf7=\xd8\xc3c\xc7\xeawM\x0e\x04v\x8c\xc5\xd3l\x98\xc1\x8e/\xd8\x99\x8c\xed\xbb\x1e\xdc\xe8\xb2#N\x9b_wotY&\xff?\x9c\x8c\xdbZx\xd14\xa8\x90yi\xfa\xfd\xbb\xc5\xb1\xab\xc0\xee\x96\x1c\xa6\x8c\x7fR\xde,kHu\x9c\x15Y\x17\xcfT\x1e\xce\xbaki0\xadm\xf0H\x1bH\xab\x95\xa8\x8a\xef:\xffV\xe9\xbbA\x0e\xe9\xcc\xa9;\xa9(\xfb3n\x14\xcb\xb7\xf8j\xc0\x92_I\xf1\xa8\xa0\x0c\xea!d[\x8f\xd7go<\xaf\x04\xa49%=(\xc0\x0e\xe8u\xb3\x8d}\x9e8=ka\x9f\x13/\x98\xd5\xe2Fj`H\xad\xbbK\x19o\xd8\x9e?1[1\xb4_L\xa3pS\x1cw\xfd\xa0\x9b3S\xfc\x13\xacN<^\n\xa2P>*=s\xd3\xfc\xb3*\xee\xe5\xd6%p#\xfe[G\xc8s\xa9+\xd4\x11\xa2\\&O\xa9;\xdc\xf9\x8c\xf8o\xf5@\xd9\x14\xaa\xc0*\xa9Kw\x03\xd0K\xean5\xb5\xd5\x9e.\xa7d\x02\xa2w\x0b\x17P\xd4\x1f\x8f\xab\xfcO\xc3i\xe4Mt\x97\x85\xb0q\xa6\x8cM\x8bs\x95\x93JR\xe3\xa7R ~\xd3\xd2\xcf\x91\xb9\"\xbc\xeb\x8cN|.\x1f\x98?2\xdb\xe9\xaa\x82V--a\xaf\xb1Dp\xc2\xd9.\xe3\xf2\xeeDH[l\x81\xc5\xf2\xa3\xcc\xb8\xdcR\x179\x00\xa2\xab4V\x99\x0d\xed\xe8XAE\x8b\xa5\x95\"=x\xb0{\x9e\xee7\x8a\xcd\xce\xb93\xa5\xe6\xe4\x1d\x8a:\n\x16\x9b\x9dlF\x9d\xc7\xe7jJ\x8bl\xe2T\xd6\xb7,\xa5C\xd3\xacT\xa3\x05\x8eO\xd1\x93D\xd4\x10D\x94.\xc3\x0d\x89\xad\xaa\x0c\xa1S?\x06ql\xca\x1d\xdaw@\x9a@\xe4\x11cg\x04\xf75\x88\xd81Od\x01\xb8\xc3\xb2a\x12\xed\x8b-#\xcai\xbb\xb5#\x1a0\xce\xc1\xac\xef\xf8\x01\xf7Z]\xd6\xdaY\xd9\xde\xb9\xb9\xb1\xb9\xb2\xb5\xb8\xb3\xba\xb1~\xf3\xdc\xe2\xea\xda\xcarK\xa2T\xd8e|\x82\x18\x86\x16G\xac8E\x92\xba\xcd\xad\xae]i\xc5\xab[\x88\xb7:\x0f\xecf^\xd9\xaa<\xef\xb4\xcd\xb0\x90\x18j\xeb&\xcd+h\x1e\x81g?\x8c\xe2\x1f\xca\x8bL\x9ed\x87\xccOY\x18eL\xa8\xf9Q\xbfX\xe2\x94\xa9\xa8J\xe6\x87l\xeb\xdc\xd2\xb1\x97O\xcf\xce\x8b\x05/\xd6zc\xf3\xe6\xea\xfa\xe5\xc5\xb5\xd5\xe6\xf5\xd6\xcbR%V\x95\x7fE\xca\x92\x8fT)\x8eU)m\xe6l\x03=`\x90WW2\xd0\xac\xdd:\xde\xb2\xd8>a\x17\xc8\xe7!;\xc3,\x8f\x16\x8cKv>\x0b\xb31!b\x146h\x80\x1d\xd6\x84\xe3J\xd3\xe2\xa1|\x1a\xae\x8e:\nb\xf8\xaa\xf5\xcaWl\xf9@\xda\x16\x877\x14\x95-\x11a\x08\xde.\xc7\xb3]\x1f\xdc`\xaf\xc9)\xf4\xc18\xd6\x9e\xed\xb2\xa1N\xc5z\\f\xe7\x1b\x8a\xee\xc7\xec\x18\xe4\xe2o\x8f\x98\xa1\xbc\x95\x00^\xd9\xf8aA\xb8G\x82R\x0f\x8f\x1e\xc5\xf7\xc8^\xad\x89_\xe2\xfa1@\xf4AG.\x9e\xa7\xad\xee\xd6\n\x0d\xae\x8aL\xe3\xbf\xb4\xf6\x95\xa5\xd2A\xa7\xf9H\xac\x1c\xc4\xdc\xcd\xb8\xc7\x9c\x90\xe5a\xea\x0f\x04\xba\xf7\x9c\x94\x1f\x9b\x9be\xea9d\xa6\x08\xf3\xc8\xd9\xf3\xc3\x01\xcb\x86\\6\x96\xf0>Ox\xe8r\x0f\nH\x80\xf4\xe9c<\xe0\xf2\xa8\xef\xfb\xd9P~\xbe\xc3\x93\xe8\x98h\xd6\x03\x81\xb5z\x8a6\x17w.\xdc\\][[9\xbf\xb8vsqkk\xf1\xea\xcd\xd5\xf5\xe5\x957\xd4\x99\x02\xed\x8e5\xbd\xe5W\x9d\xb2\xdc9\xb1\xa0\x7f\xfc\xc7\x83iu\x1b\xa6\x96p\xc8\xbew\x86\x8d'\xdd\xcb\xc8\x85\xae\xf2H\xf1e\xc0\xbeg6q\x021\x1fr\x19\xc6\xe1\xf7}\xbd&\xec\xd2\xee\xf6\x0e[\xdf\xd8a=\xce\x06\xd2W7a\xd9\xd0 a\xc5\xa5\xc1V\xd0'\xb5\xb8\xa9\xa0Jf\xc9\xab\x0bzyqmw\xe5\xe6\xc6\xee\xce\xcd\x8ds7\xcfn\xec\xae/oO\xbf\x96\xf2\xde \xd8\x92\xb4\xdc\xa7\xd7\xc5\xf4n\xc0\xedV\xd8e^\x97\x0d\x04\x99\xeb|\xfd<\x8b\xd5\xd1R\xfd\xb3\x08\xccE \xc3@\xb9\xc5\x1c9\xc3\x06E\xaa\x83?n\x15\xf8\xe2\xcc\xe4!\xe4\x9a\xdct\xb2a\xe1)8\x90\xa7\xbb\x113\xf0\xaa\xe5\xdf\x9cU\xab]1\xbaZ\x1e\x032Y\xc3\xa8l\x02s\x7fz\x81\xd9&\x16\x13\x07\xe1\xe6\xa5\x91\x7f\xb3\x94\xdf\xce\x05\xe5a\xa3<\xcd\xc4qq\xc2\xe2\x18l\xaf\xbc\xbe\xbb\xb2\xbe\xb4rs}c\xe7\xe6\xe2:\x10\x14\x1c\xe12-\xbb5\x9e>\xf2F\x9f\xef3\x1d\xd6\xa4\x0e\xb9\xf2\x00\xebB>Msk\x9a\xb3\xef\xb2\xf4U\x96\x1f=\xdaa\xfe\xf5\\\x86`\xcau\xba\x9e\x0bN\x05\xf7\xf7\x12R\x16\x8d\xac\xda\x8bO\x054\xbfqC\xe2 \x1bRw\x0bU\xbd\xf6\xa2^\xf4\xd3IVJ\x96rB\xa6\xba\xa9\x10&\xb5%\x1bg/\xae,\xed\xb4\x00k\xc5z\xbcJFy$\xbf\xce\xc5\x01\x9a\xb6\xdf\xafD\xa2\xab\x1f\x9eq\xbe-_\xd9\x81\x826\xe5xEa:b\x87\xa9\x86-\x0cr\x8aa)\x9f(9\x92\x82\xc4\x1d\x07\x12\xa7>\x177\x81\x8dc\xfdv\xfdX\xe5\xa9K3'Q\x1c\xbeu\xbc\xf5\xed/6\xde\xb2\x1a\xc7\xa9\x1a\xc7\xa5\x02 X\xadm\xb9\xa5\x027\xedr\x8b\xc2t\xb9\xe3\x84\xa7\xe2X\xb5U\x88\\/\xe0\x025~(F\xf5C\xe6\x84\x1e\xfb\xa1\x18\xcd\x0fK(\xd4\xa9n\xcd\xb9\xad\x8dK7\xb7V^\xdf]\xddZ\x994W#/\x98\xa9V\xd4c\xf3\xb5P+\xcd\x02\x94o\xa1\xb5Eq\xca\x99\xcb\xd2\xd3O\xdd\xf1\xbc\x1fv\xd9\x0f\xd5\xc8\xd4\"\x88\x115,\x02\xc8\x1b_\xfd*83C'\xdd\xd5\xc9n\xdaz%\xbeyK\xb1\xb4\xb8.H\xdd\xd2\xc6\xfa\xce\xe2\xea\xfa\xcd\xdd\xf5\xe5\x95s\xab\xeb\x13\x96\xc6r%Q6\xc5\xa8e\xa87cB\xa0\xb4<\xe3\x85:\xd8\x98_\x83)kxD+\xd8E 1\x1e_\xd2\x98\x94\x1d\x05\x15I\xfd\xb3y\x0f\x96\x9cP.4OdT\xb2\xa3\x16\xb7$\xe48\x99\x14f=\x9e\xfa \xf7\xa4u\xcfB\x03\xd5\xba..\x97W\xb2I\xe6\xab\xc1\xad\xb2\xe5\xc2|,\x0c\x0fM+\xed\x83W\x99\xa3\xdc\xac\xa2\xe7\x9a\xb8\x98be\xce\x8e\x9c\xa9\x10\xf33\xe6E\x1c\xf0\x91\x1f\xf8if\x99\xfd\xee\xfa\xd6\xca\xf6\xc6\xda\xe5\xc5\xb3k+\xd3\xce\x7f\n\xfaZ\x8fQ\x81\x10\x07\xdb\x16\xff}\xfdk2\xd0\xea\x1f\x18j\x81\\O\xbc\xa3\xab\xc9}.~wo\xd0c\xa3\x7fb\xaa\xd2\xeb\xbdq\xc9\xe4\x9c\x03\x99\xf9\xe2K\xec\x9a\x98\xc7\xd4\xfb&\xd9\xc3\xd4\xfb\xd6(\xd7yZ\xae\xc3;f\xf7\x8b\x93B\xd4\xf3Iq/J\xb8\xd6\xdd\x87\x1d\xd6oW\xe4\xeb\xb0\xd3\xc5\x02\xb7\xd0\x03~\xf4#\xa1\x11\xd0F\x1aL\x1e\x89L\x19\xf6\xa3\x1f\xd5\xe5\x01\xac\x84t(\xd7\xfc\xc2\xab1\x12\x82y\xd2\xe6\xd7\xa3\x1b\xd2\xb79\xd4\xc6\x9dI1\x0b\xcd\xee\x81\x926\x94\xfdn\xf1\x1a\xd7]\x81\x88\x1f\xecLm0\x99\xf9K:\xed\xca\xf7\x92\xcf\x1enF~\x98I\x0f\xfa\xc0Du\x17\xfc\xee\x0cs\xcdW\xd8\xdb3\xaco\xbel\xc9p\xbd\x04\xc7\xe7\xe2y\xe9\x0b2u\x8bb\x91\xd4A\xebM\xbe>\xc5V\xadaR\xd6\x8c\x8a\x85\x12\x13\x1c;\x81\xef9\x99\xf4\xe9\x8aK\x1f\x84\xd6\xe5}K\x15\x9b\xc6\xb3-l\xcf\xbfR\xea\xbd\xd6w\xdb\xa6h\x1dI\x94\xb72\x9f\xb9\x99\x81{\xac^\x9e\x9d\xc3\x98\xab5Y\x0de@U\xe6\x0b\xa9#\xe1.\xf7\xc7<\xe92\xf3\x96\x84L)\"x\xe2\x11|\xcc4*!\x1c\xf9BQ\x0b_(\xad\x0cM)SN'Sr\ni\xcf\xcfw*\x8ew\x96<25\xbe\x93\xf4\x909\xfd\x8c'k\x91\xe3M\x13a \xafk\x93(\xcaVC\x08\xc4>C?\xe9w\xc9\xd1\xf7\x19?\xf4\xb3\x8d\xc5<\x1bB\xb2\x98<\x1b.\xca\xde\xd2\x197\n\xfb\xfe O\xb8\x80Zj\xc6 7)\xdc\x16e*(is\xee\xf9\xa1\xd7\x86\xcb\x0f\xe94\xdeT\x0d\xf2\x1a\x9dan\xb5\x16%O\x94\xa5\xa6\x99\x93\xf1\xcd \x1f\xf8\xa15\x0eD\xfcD?u0&W_\x12\x87t\x81Ez\xb3\xeay\xb7\x03\xcb\xd2\x185\x96\xf2\x80\xbbY$Z\xb4\xbf\x0fY\x93\x95\x16r\xdd\xd4\x0ft?q\xe2E\xdd\xbf\xfdQ\xae\x89\xee!U\xdaa\xdd\x05\x0c(v\xb5\x8a\xf0\x91B\xf8\x13\xa7O\xe2\x9c\x19>\xbc<\xd4\x9e?A\xb2M:\nt\xe2\xf4)\x0c\xca\x0dH\xe6\xd90\xb0&\xb7c`C(\xdbc\xd3\xed{&\xa3J(iWQW6\xbc#\x89\xea&$\xe80\x91D*\x05@\x06\xd1\xdf\xfczX\x93K\xa2L$x9\xff\xa7M6\nj}\xaf\xa7\xcfzY\x93\xf1\xb2Y(s5\x89\xb5\x18\xdb\n\x9d\xacL;\x0c\nQ|/\x1e\x0d\xd9\xd6\xa7\x85\x16\xca\xa5\xcdR\x14\x12\xdc\xd5r\xfaMz5?\xddX\xdc>\xd1\x91 \xcd&>\xb2\xc1\x16\xd8\xf5\x96%\xd3b\xcb\x12\xa6*\xd4\x82\xbc\xdd\x11r\xc8j\xd8\xben\xd2E\xa4]v=\xbbA\xd2\xc1\xc0F\x04\xec5\xe6\xcb\x07\x99\x13\x94\n\xb3![\x99\xfd\xdc\xebdq\xb5\xae5:u\x9c\xcd\xcf\xd2F0\xc5\"8\x0b,\x98\xc9\xa2\x8b\xdb\xe8=gHS+NB#\"\xf4\xeb\x1c\x8d4U\x98\x1a\x0b\xfci\xb0\xc0\x81\xb7[j\xb1 7O ~eX \xc3\x98-X\x907aA\xca^c\xd1\xf3b\x81\x0d\xcb\xd5\x96\xa5So\x19\xfb\xa6\x89F]\xed\n-\xa5#\xca+$\x84d^r\x14d\x8e<\x00\x90Kq\xf5;\xe8+$\x1b\x9e\xc3\x11\x16\x81\x8a\x87\x98\xb7\xf2\x14\xf7\xeb!\xa7\xfa\xaf2\xa9\x97\xfeT:'kT\xca\xc9\xdae\xc1\xcc\xf6\x85\x8d+7\x17ww.\xdc\xdc\xdc\xd8\xdc\xdd\x9c\x90oY\xfb\x95e3\xb1-\x9f\x9f\x9e\xd1L\xca\xb3v+\x1dF\xfbe\x84\x17\xa8Q\xda;\xfbx\xc4P6\xb6V\xaf\xad<\xefH(B'&Op?\x89F\x17\xb7;BW&\xa5\x80\x90\x0c\xc4\x80\x8b\x1c\xc1-x8CV\xbe\xe4\xc4\x1d\x1c\xf8n\xd4%\x1ef\xc9\xe16\xbf\xdd\x9e6\xe3\xba\x96\x0dP\xbaN\xdee8\xb0U\xff\xe4,\xaf\xcf\xd6\xe46H$t\xae\x06\nIe\x159i\xc1 \x17T*\x939\xcfjl\x0c\x95T\xab2\xc7H\xe9\xa5\x1d\xbf#W,\x92[\x1c\xda\xcdG\x85\xa9\xac\x94\xdf\xd4\x9a\x97\x87\x95\xc2}\x8aq\xca\x93.\x86\xa9\xb9R\xebFC\xfca`\xaf\xab\x19\x96u\x9aLm|\xdb\xccET\x0e\xbbL\xd5ot\x9f.xe^?*H3\xb7P\xce\xa6\n\x8f\x93\xf5\xb2\xc8)?\xdaS\xf7Ls\xa7S\x1e\x96\xda\xba\x1b]\x98j[\x7f\x98\x98\x11B\x066\xc3y,\xa1\xb7\x10\xad\xa6?\x8a77\xc4\x9f\xf3/\xe6D\x86\x92Q\xdb\xcfaX\x97,\xd9\xa9\xf1u2\xe7\x10\xde\xeb!o\xfd\n\xaa\x17u \xcfH\x95\x14$z]$\xd6T\x96\xc6\x81\x15\x96\x88\xd7\xb9\xd1-\xe7\x05\xac[\xaa\xb5\x8d\xf3\x1b\xbb;/f\x81,\xc4hf\xdf\xcf\x86\x97\xf2\x0c\xaeG\xa6\xc8\xa8h\xc9\xe4\xd5\xf8\x8c+\x9f\x81\xc0\xb2\xda\x10^\x0b\x9a\xd5\x98N,\xb8\x96L^\xc0\xa5\x8d\xf5s\xab\xe7w\xb7V$/z\xde\x85l\x1a \x18\x16,\xdcG\x8d\xea\xb7+\xc0t\xc1\xf6\xb8\x04\x83\x94s\xf2\xd3E\xb3x\x90\xd4\xad\xfaO\xaf`\xa9\xe7\xa2d\x0bLY\xe0\xbe\xa4\xd2\x0f\x94\x98\xee\xd9\xc3ug\xc4S\\q'2}H\x90`\xd5a\xa9\x9a\xe5\xb8i\xdbS\xde\x0e\xdb'\x89t\x15)\x08\x95\xa1 o\xc3),D9J\xb4z\xbe8\xe2\xafDV\x1a\xab\x04B\xf5\xc7\x8a\x9a\x05\xcb\x967\xcb\xe2\x01\x19\x82\xec\x90Z\xe5\xe8\x08enr\x1f\x8a\xbc#\xd9\xa9\x83p\xa6v/'\xf7\\\xd3\xf1tb\x0b\xd2\xa2l\x0f \xb4\x8d\xec\xe4\x80\xecT\xfb\xcaQh\xe4\xa05?\xcd\x88\x90\xc5\xca\x96\x8b\xe7\x16\xb4\x18\x12\xb6\xa2\xa9\x84-fD\xaa:\x81\x8b)\x9c\xae\x17\xbaXIYt\xac\xe2c\xb9T.\xc9T\xd2\x95/%\x86\xe0\x1b\x9b\xa7\xc3vn#\xb9]\x9c\x17\x91\x92\x12\xeb\xe1o$\xa7S#@H\x11\x80\xce\xcb\x8d\xc24\n\xf8\xcc\xbe\x93\x84\xed\xd6\x95\xc5\xad\xf5\xd5\xf5\xf3\x0b\xcc>2?e\x1e\x8f\x13\xee:\xe00\xeb\xb1}?\x08X\x8f\xeb0\x1e\xed\x91\x19\xf2\x83\x8c\x8d\x9c[Q\xc2\xc6\\g\x9aB7\xe2;\xd3\x04\xbb\x11\xe7\x99\xce`,I\x98?\xa1W\x1b\x8f\xc1\xbf\xca\x9b\x039PF\xa9\xba(\xd7\x95T\xd0\xbc\x97^b\xed6\xbcp\xa1$\xe3(\xe6i\xab\xd3\x99\xd9\xe3_h%\x99\xf4~v\xa30s\xfc0U\x17N\xb2\x87T\x8bI\xdc\"w\xeb\xdf]\xe5\xc1\x98+I(\x08\xa2}\xeem\xc3\xa8\xba,\xed\xa8\xe46\x99\x84\xfb]f9\xe9\xba\x1d\x1f\x9e\n\x95\xb9\xcd\xec\xf4\xc0\xaf\xa3\x07\xddI\xa2B\xfdbh|u\x92\x81\xbc\x08L\x0b\x07\xb79V\xcd\x15f\x8a\\\x9f\xbb\xc1^\xab\xfes\xa1\xe9TMEtT\xa16\x18\xfa\n\xaec\xe7~e\xc6\xa3\xfa\xecL\x9f\x84\xdc\x1c\xf14\x1a\xf1)\xc5fSG \x1e/\xe1\x9b\x9f\xa4Y\xbb\x06G\xac\xb2t\xd3.V\xe4\xbf\xc9\xfc}\x82da3rh\xa2\x84\xb8 \x92D_$\x13\xa9\xeeg1\xa6\x06\xe2\x0b\x9b:\xe3\xa7\xe2?\x10\x1b|\xe4H\xa6\x8c\x95\xcf\xbd\xcf*\x97#2\x9b\xf2\xce\xcc\xc8\x89\xa7h\xa5\xd4\xd2\x91#!\xec\x7f\xddv\x1b\xaf\xd1#s\xb6\xad\xd7\x87\x0b\x99W\x19E\x84\x8a\xa2\xf0\xa5\x11A+F\xe5]\xff\x16\xfbFhD\xfc\x80\xbb\xb9\xf4,\xb0j!]\x95\xe5f\xfe\x94E\xd7\x90\xd6\xceH2\x88\xa4\xaa($\xcd\x8aB5^\xb8\"\xe1\x17\xe3\x99R/\xad\xa0\xb7]\xcd\xcf\x9a\x04)|\x9aj\x9f\x83\x89\x94\x1a\\\xe7\x8e\xe8\xa8\x0c\xd6\xd90\xaayr,\x97%\xa6x\xc1M,C\x968\x0d\xcf\xc9\xd6\x1f\x95\xe2\x80/(\x03\x90>\xeeb\x9f\xaa_\xd4\x89\xae\x97\x1eJ\xd4\x7f\x81%5*\x88\xdc~+hb\xfb\xe5W\xdd\xca\x1d\xe0VMS\xf6s_K\xc8x\x1b[\xa9\xac\x0d\x80\x93_\xcd\x1by\xb0\xa3\x0b\xcc\xb1\x83K\x0f\xde\xd4\xd8(\xcb\xaf\xe6X^\xbf\x95rJ\x1d-\xfa\x86P\x89/\xe3\xf1\xd2\x0f\xebnB\xd3\xa1\x94\xd8Vn\xe7N\xf0}~\x08(\x86\xbe\xd1\xf5\xaa[*j?\x917G\xdf\x80\x15\xa4#K\xdba\xfb$y\xe7:2>\x16\x13\xfd\x8dj\x05I>\xd3\xb7\x10\x16{\x82\x02\xf1\xf3\xa2\xfd0\x98\xd2\x1d\x89Y\xc8emj\n\xfd+\xf4D\x9e$\xea\x02\xb9Y]aZQ\x9at\x8d\x8c\x7f\x8e\xa94u?\x10\xf8Tp\xfb\xc95\x02I\x9f\xfb\xa0\xc4v\xcc\xddv6\x93 ~'\xf4\x8a< \xda\x9d\"\x93\xbf.\xb6\x9b\x04u6\n\xfdk\x1e\xbbL\x14#8\xac\xea\xa2[7\xc6\x00\xfe ,\xdc\x0d\xb8\x934\xbc\x8d\xa1\x7f\xcf\x83dB\xfe\x0f\xa6h3O\x82\x05[\x9e\x16\xfc\x13\x03\xde\x96^\xd1G\x1a\x1e<\xd4?\xf5 \xe9j\x98\xf1\xc4\xe5q\x16%\x0b2=\x0f\xfe*\x96j:\xf9\xb5\xfc#w\x8du\xbf\x1a\xef\xee\xf2/\xe1i\x1c\x85)'C%\x9f\x7f\xfbcu\x13\xee\xf10\xf3\x9d ]`\xad\xd4\x19qEg\x1b\xe2\xe0\xf4O\x91\xb7&\xa7\xf6\xf2OP\xc98[\xa8\xbe\xe2y+\x8d\xc2\xee\x1f\x1c\xff\x83\xc9\xe4\xad\xf9\x94\xdc\xed\xccdC\x1e\xb6\xfb]\xd6o\xb8$\xb0Bj\x96\xc9r\xc8\xa6\xd5\x8c\xb4@x\x1d\xa2\x1d\xcc\xd1\xec\xb2V\x11*\xa4i\x8a\xf9\x08zG\xab\xe1\x0d\xf4\xaa\x1553&Nx\\N\xdf\x01r\x95\x11G\xfcg\x01\xc4p)\x90Ws h\xdf\xa8\x92\x1d6\xebLdT\xd9a,\xa8\x85\x90\xb5n\xc2\x02\xddT\x93\xbb B\xf8\x04\xbcQ\xae#\xb6\x04n\xfaW\xb3I\xe4\xab\xcd\xff\xb9V\xb7\x0d\xaa\xdbh7\xe3N\xb7\xb9\xc6)\xa2\xce\x8c_\xfe\xddm\xb2\x0c\x97\x7fU+qe\xb8pc@\xcc\xd4\xfag\xbb\xd9\xb0\xda5i\xe7\xd3\x04\xd8L\x8a[113\x8d\xd9!u\x10N3v\xd5\xa3\xd5B\xb3\x0d\xd8\xf6S\xb3\xb6\xbc.g<\x98 \xd1)]\xf0nQD\xe6;m&=\xf5\x98\xdc`\xed,\xa2\x88j\x1e\xa0\xa2\x9b\xfa-\xfb\xbf\x90\xb5k\x82\xe7O\xf5\xab \xca\x99\x9f:&\xe7\xab\xf2 \xfa\xed\xda\xe5\xbe\xace\xf3\x85\x9e\xa4\x1a\xf32\xab\xe2M\xdf\x8e7\xf6\xba\xea\xdai\xbaH\xb9t\xe6EG\xca}\xe9x6j7u\xdba\xfb\xf4 \x12\x9c\xa6\xee\xa8N\x9c\xb0\\R\xc9\x00NZ\xc5Q\xa0\x93\xb3\xb3\xb6P\x04\x00\x11\x0bm\xaa\xc6pr\xb6\xe6\xecXB\xb9\xfe\xe9\xc5\xb3}\xcd\x01\x18c\x95T\xb2\xda\xc8\x80gk\x91\xeb\x04 `-4\x9b\x03\xb5\xf7\x834K\xc4N\x92\xf2\xab\xceHU\xed\xb4\x0bi\xa9q,\xbf}bf\xec\xd8g\x0fw\x130Tk\xfb>|op6\x85\xf3S\xb9v\xc0U'^w7_\xa2\x96\x169\x9b\xe9\x87`C\xef`E\xb9\xee\"^O\xe9\xb9\\#\xac\x06*}\x99[\xb9*\xa0\xf2\xb7<\xb7\xe6\x9cFh9\xda\\)\x1f~\x97\xf96\x03\xbf9\x0d~\xfd\x1dIh5\xe2\x87U#>{\x8d\xb5\xa3&\xfb\xbdR!:\x02w\x9f\xab\xd8n\x12\xb4[\xe2CU\x89\x08KV\xfd\xc2\xa8?\x93'\x81@2x\x81]HH\x99\x8a\x84#\xe7%\x04\x03\x89ED\xfd\x06\x9f\x9f2\xe6\x0fx6%\xa6q\x15\x0d\x83\xdf\xdf\x94\xf6\xfc\x05\x19J\xf8\x0d\x9d\xa5v\xef\xe8*\xe1q\xde\xf6\xda\x9f\xf4\xf0\xf0\xbf\xbc\x87\x07e\xb0u\xb1~\x82U\xdb\xef>e\x00\x91\x8e\xad+\xc5sE]\x96\xce\xecn./\xee\xac\xdc\x84\xd8\x86\xed A\x0df\xef\xe0\xb9\xf1j\xb4J\xa1\x04\xd0P\n\xdc\xeb\xce\xc6\xf9\xf3k\xd3\xf6\xfa\\1)8U\x89\x19\xb2\x8a\x05;\x82\x02=\xa2o\xc2=\xf7\xf3\xc9\xd3\xd7\x0d[\xb5\xd9\x1f\xa6\x91\xad\xa7\x90o+ \x16\xea\x8b1e-\xe0\xf8\x15\x8d\xe7\xd09\x9f\xfb\xbe\x91C&\x1b\x95c\xb4[xtNa\xb2f%\x84\xda\xf7C/\xda/.3\x86NZ\x93\x00\x0d\xff\xb2\x99\xc09\x8c\xf2L\xc7uKJ\xbe\xccy\xbc\xe6\x87{\x17\x9ct8\xcd\xfd\xd2\x04\x1b]-\xf4K\x98|\xc4\xae\x9a\xfc\xb6\xb5\x1b[\xf2\xcc\x99\x90\x06\xc4$\x1d\xdaq\x06\x0b\x85\xbb\x10\x1dJ\xe5\xcb\xdd\"\xd1\xacEUq\xa4\x9a`UU\x00\xf4\xb2-|\x07@\xdf\xb1+\x17\xce\xd7'W\xff\xf6 \x89\xbc\xcc\xd8v\x93(\x08v\xc0\xf5.U\xffPw\xe0\xf2[\xc2\x1d\xefp'\x82r\x8a\xb8\"\x1c\xae\xd45!X\xcd\x0e\x8f\xfd\xda\xb8\xf6\xbe5\xf2\n\x0c-'g\xb1\x97d\xaej\x9c>AR\xa34\x86\xb6c\xde(\xdf\xa0l\x07V\xac\xe8\x7f}X\xc1\xd4*\xc5\xe5e\x9cH/\x0b\xc67\xc9\xcf\x06\x9c5\x81&5\xc4\xbdLKp+\xef\xf8c\x0f{\xd8h-\xafU\xde\xc2\xcfT\xee\xe3\x08r\x1f\x17\x9e\xf6y\x8d\x99\x1e\xb2*V\xa9y\xd4\xe9\xb2\xb0\xdd\x91\x8f0\nT\xf4\xc3Ag\x8aG`\xc5\xfeG\x13#D\\Yj\xae\xe1\xd6 0O@k\xa14\x10Bi \x84\xd2\xa0\xa1\x9eV\xa6\x13!\xef\x8b\xe3#+\x9fK\xa2\xd1j\xba=\x8c\xf6\xc3\xef\xf3C\x89\x88u\x0d\xc8\xdca}\xf4:ls\x7f1\x8d&\xeeO\x8e\xa5\xf1\xd8\x19\x16O\\\xa9\xa1,\xd5\xb4Rr\xc0n\xa7\xac\x9e:B\xcc\x12\x93\xef\xc8\xa4\xa2\xf5u\xe7\xe5\x9d\x8cyX\xf65\\\xbb-\xe3\xd0\xe1\xcaA\xd3\xa4M'\x83v\xd9Q\xe6Iw\x16\xf1\xd7P\xaaTs\xd5\xf6^z\xe9\xb9\x1b\xac\x8b\x84\x98\xea.\xbe\xaa\x07N\xff\xb2Z\x95hT7\xc4\xc3\xf4\xb7\xf9j\xa4\xd6\xd8\xca\x8a\x8b( \x107\xa1\xcd\x9bYTs\xfdd\xae\x9dp\x1eIE\x06\xafs\xfaTW\xe3T\x86\xb5\x0cf\xaa95[GX\x85RV\xe4\xb2z\x0c\x9f\x92`2\x85\xe6`z)\xa8p\xa7J\x9f$\xbbh\xc2\x8f\xb1\xc9\x06\x04\x0f\x90\xcc5\x1c\x8d\xd6\x11\xf08\x13\xc4\x8c\xe9\xcc\xf9\x91\xa9\xd8\xe9J\xc4o*\xd1L4|\x9c\xf9w\xfah\x12\xfd\xd3'\x9e\xebwhT\xba\xdd\xf6\xf1\x9b\xc7\x07]\xd6b\xad >\x1c\x13(\x94#\xe9\xa8o\xe8\xa6\xa0\xa2\xbb%\xaa\xda\xf6\x1b\xe6\x18J\xfe\xdav\xba\xf0\xdc@h\x8eP\xdby!\xe7rl\x95\x9f&2\xf3\xa9,l\xac\xe2\xf7\x8b\xd0S\xe0\x9f\x96\xeb\x043\xa9Y\x03\xd7xi\xf9i;\x01\xfd;0Z:\xef\x80\xe1:D\x1a\x0c\x92\x11%g\xc7e*\x92\xa5-t\xacq\xddF5\xb2\xe8\x8b[\xb9f!A\xca\xbd`&\xec\x87\xc5Zn:\x89\x98/\x17\x92\x8cY9u\xd7-\x0b\xc8G\x1eg\xb2\xa8\x96\xac\xff\xd68\xc4@\xae(\x96\xf7\xa7\xb1\xd7O\xc3%d\xbb\x8aWP\x87\x1340\xbb\xe5\xa9\xda\x8d=\x9e\x01m\xc4\x94f\x04M\xf0\x8d\x97\xaf\xfeC\xe1U3\xe5\x97\x84|\x14\xe7\x19\xf7\xb6\xb3\xc3@\xe6#\xae\xad \xd6\xb4\xe5\xf4\xd2(\xc83\x95S;\x99\x89\xa3T\xc6\xea\xd4W\x93\xf1\xf7\xec5v\xbc\xed\xe4Y\xf4#X\xc7\x1f\x0d}\xcf\xe3a\xe78[\xa8\x02:\xc7\xeb\x99O\xab\xef\x1fp\x0f\xf7\\\xbc\x90f\xafidx\x99^\xf0U\xf9\x1fG\xf0\xe0b\x91^\xad\xa7\xd221\xbdm\xa5\x9cN\x97\xb5\x8f\xc8wTZi\xe6d\xbe\x0b\xae\xd3\xe5\x81\xbd\xf4\x12\xf3eZ\xe0v2\x13\x8dy\xd2\x0f\xa2}v\x94\x15\xff\xb8Z\xf9\xd7\x1b\x9d\xc2\xdd\xde>\x17=\xd3IX\x88\x14\xc5 \x960\xc0\xf3\xdaT\xa9\x93\x8d_\x88\x96-\xb0\x86D\xe7\xba\xec\x02\xab\x89q\x13\xbf\xcaQ^`\x83\x06,.\xb3\x9f\x056\xae/I\xa4\xae\x056\xb4\x13\x1f{\x1b\xa5{\xe9\xfa\x95\xa8r\xa6i\x1d\xbf\x18\xc3\x9e\xccM\xef$\xf5UZ\xac\xed\x01\xb4_\xd4{\xa44\x8b&\xa9\x1e^;\xf1\xbb,\xb7SgDX\xb2\xa1\x9fvY\x9d]\xd5\x08\xc1\xa9\xd5\x90\xed\x1aCv\xda\xe9J\xeb\xed\xec\xab\xac\x0f\x8f\xf8\xf5\x8f\x1e\xed0\xf7z\xbfj\xc8\xee7\xbf\x16/\xd8\x9cO3\xa7\xc2 \xe5\xbb\x83\xc1\xcc\xcd\x9b\xd2\xb9\xec\xe6M\xed\x12]\xf2)\x0f:\x1d\xe9a\xa6L\xe2\xbc\xcb\xae\x8b\xba&\xc9\xb2\xdb\xe9\xc8\xf0\x99(\\\x8b\x1co\xa2\xfdL\xff4\x07\xf6g\xe2$\x8a\xd3\"\x93\xc2L\x16\xc1\xc1j\xca5\xc0\x14\x17F\x92G8\x939\x83\xae|\x04U}]\xf5\x1a8*\xbe2\xadH\xb0\x82?\xd4\xe9\xc4p\xc3\x10\x12G\x02{V\"J\x96K\xe6\xe9\xbc\xb4\xd2\xf06<\x92I\x82.\xaby\xf6hO\x88=\xad\x84\x87\x1eOj\xcc\xa6\x8a\xdaL\xbc]a\xc5\xa0Rdq0Q\xaai\xec\x84\x84\x9c\xd1F\xfa\x0b\xf0\x9c\x04\xe0Cm\xe1\xbb\xdd\xda\x9e\xb8z\x90B\"F\x1d?\xa7\xab|\xa3\xd3E)\x19\xee\xb6\x8b.\xcc\x15\xf37\xda\x87\xe7\x1bG\xfaCi\x176\xff\xfc\x1d\xd9/\xfd~G\xf6\xbf8\xd9\xb7\xe8\x85\x9a\x13d\xce\xe0\x0b\xd3\xec\xf0w4\xfbw4\xfb\xab\xa6\xd9\xcf\xe7\x1ag!?\xb5It\xa28='\x13\xb2=\x87\xe3R10\xc4Kt\xba\xaf\x93\xb3\xa7-L\xe3E\xe5\xfb\xfa\xe6\xeeG\xa3\xb7(\xc9{gy/\xa5TA\xbe\xd5~\x86\x85&`\x13\x87\x0f\xfc\x97\x85\xa1\x93\xcc\xd4l\x8a`\xa8)\xed\x19\xcc\x04\xeaB$\xf9tlD\xff\xa6\xf5\x1e\xc2?U/\x91\x0f\xc0w\x1b\xbc7'\xb6f7\x9a\x19h\xb3\n\x03\x13\xbf\x98F!\x9e\xfc\x146L\xf6%\xe6os\xe3jwf\xa2P\x90\xdc\x80g\x96G!m?\xb3\x8c/\xbd\xc4Zz\x10\xe5@\xcdP^\xec\xa6<\xdb\xf1G<\xca\xa5\xbb3<\xb8\x7f\x86\x1d\x99\xeb|\x95+_\x0b\xad1s\x92\xaf\xd3\xd2Y9\x15\xeb\xa1/\xefF\xf9\xbd\xc6\x96\xe7d\xce\x82?r\x06\xfcx:\x1e\x1c=\x18\x05\xaf\xf6\x9c\x94\xbf|\xb2\xbbya}\xfe\xda\xe1\xd9\x13\xce\x95\xadYgy\xd6\xbftkq\xdf\xbd0\xf0W\x97\xceF\xd7\xae\x04\xa1s\xe1\xf5\xd3\xab\xb7V\xf7/]8{r\xd5_\x1c\xf0\xf3si/\xbctzu4\x9c\xf5.,\xbe\xbcvx\xfa\x84w\xc2\xcd\xbd;\x97\xf2\xde\x89\x8b\xe1\xda\x9d\xd5\xfdK\xcb\x8bc\xf7\xc4\xb5p\xd5?;\xef\\\xb9|\xe2\xf5\xd1\xe9\x93\x9b\xdb\xab\xfb\xab\xcb\x8b\x83K;\x8b\xfb\xab\xcb+\xfb\x97\x96V\x07\xee\x85\x8b\x81;\x7f\xf9\xd0\x1b]>\xeb\x9e8\x1b\\=\xb1\xb5}\xf5\x8d\xad\xb8wg\xd6\xe7+s\xf1\xb5s\xc1\xbas\xe5u\x7f\xf5\xfczz\xf5\x8d\xf5;\x9b\xdb\x17\xd3k\x17.e\xee\xe8t\xda;\x1f\xe4\xd7\x0eW\x07\xee\x89\xadS\xbd\xf3\xbb\xa7WG\x17\x87W\xe7\xb3\xd0\x1d\x9d\x9e\xeb\x8d^\xcf\x9c+s\xc3k\xf3\xbb/\xaf\x9e?5\xee\x8dv\xbf\xb3z\xbe\nw\xcf\x9f\xbe\xe3\x88\xbe\xe6O\xbe\xbcz>\xc8\xc5\xdfW\xaf\xec\x0f\x9c+\xa7b\xef|0\xec-\xa7\x83\xab\xa3s\xb7\x9cy\xef\xb0w\xe2r~mi\xee\xf0\xda\x1bg\x83\xabo\xbc^W\xde\xdf\xbcup\xcby\xe3\xe2\xad\xde\xf9\xdd\xc1\xd5\x13\x83\xd3\xab\xb7v\xf7W\xfd\xb3\xb7\xf8\xce\xac\xbf\xbe\xb3\xe8\xaf\x9e\xbf\x16\xf7\xce\xef\x9f^\x1d\xc91\xf9\xab\xe7O\x85kW\xce\xcdz\x17V3\xf7\xc4\xd6ao>\x0b6\xb7/~\x87\xcf\xaf\x8f{\xa3k\xf1\xb5\xc3S\xb7z\xf3\x07c7\x9c;\xbd\xea\x9f\xcd\xaf\x1d\xce\x0d\xbd\x0b[\x87ko\xac\xcf\xba\xa3\xd3\xc9\xb5\xed9\xb3o\xfcDv\xab7\x7fj\xe4\\qso>\xd8\xf3\xce\x0fO\xf7\xb7W\x07\xbd\x91\x9b]}ck\xd6\xf5\xe7\x0eQ\xdb\x87W\xafl\xc5\xde\x1b\xeb\xb8\xdc\x1d\xef\xc2\xc5\xb13\xbf\x9b];\x7f\xee\x8es\xfe\xdc\xa1;:w\n\xd5\xdd\xbb\xfa\xc6zt\xf5\x8d\x8b\x87W\xdf\x08d\xfdb\xfc\xab\xb7\xd6wv\xe7\xc4\xffV\xfd\xb3\xa6-\x18\x93X\x93\x15\xb1&\x87\x9b\xdb\xabw\xd6K\xf5\xd6\xael\x0d\xdd\xf9\xe1\xd0\x0d/\x0e\xc5z]\xda\xb9:\xbbvk\xef\xce\xa5;W\x0f\xd6\x97/\x1d\\\xba\xf3\xfa\xfc\xfa\xf2\xca\xdc\xea\xf2\xee\xfc\xda\xad\xbd\x13\xebw\x06'.\xed\xbc~g\xfd\xce\xe0\xf0\xd2\xce\xa5\x93\xab\xb7N\xber\xf5\xca\xa9\xb8w\xe5\xdc\xec\xb5\xcb[\x87W\xaf\x9c\xbasmt\xfa\xb0\xb7}V\xae\x99s\xe5\xe2\x9cw\xfe\xf2\xc6\xd5+sb\x8dg\xdd\xd1\xb9\xdc\x9d\xbf6vG\xb3\xfe\xea\x85\xadS\xae\xc0\xa1\xf0\xe2\xd8;\x7fn\xf6\xda\xf6\xea\xe0\xea\xfc\xb9\xf4\xea\xec\xdc\xf8\x9a\xc4\xad\x83\xb87\xbau\xf9|\x90]{\xe3\xd2\xe9\xd5[\x8b\xdf\xb9\xb4\xbd:\xb8v\xe1\xb2\x98\xf3\x81{\xb8:\xb8:\xba\x1c:WN\x9e^\xbdu\xf6\x8eX\x0b\xc0\xab\xade\x81g\xde\xf2\xac\xef\\9\xb5w\xed\xca\xb5\xb87\n\xc4X\x8en.\x9d\x1e\xf6F\x81\xd8\x9f\xe0\xf2\x85\x8b\xc3^\xb8>\xea\x9d\xb8\x98m\xde\xda\x1f_\x9d\x0f\x0e\xaf\xce\x1f\x04\xe2oq\xe66\x07\xd1\x99\xd67D\"X\x8a\x82\xc0\x89Sx\xbab\xcd\x0f\xf7\xe4\x1f\xe0\xcb#\xff\\\x0d\xe3\x1c\xfe\xda\xe1\x07\xd9b\xc2!\x0d\xea\xd9<\xcb\"\xe0\x16[\xd2KX6\xa5\xfe+\xb3}\xcb\xb7{\xeb\x82\x11\xa5\xff51Ch\xcf\xecW\xac\xafS\xf6mF\x10G7f3i\xf4mF\x90T\x01H\xef\x81\x02\x10#\x88\xab\x00\x15#\x88\xf4\x13\xb7\x9b\xbf\xbf&\x87m\xdaqLx\xbd\xb10p\xab\x85!3\x16\x06\xae^L\x98}\x95\x85\xec\xbb\x8c\xbf\xca\xc2\xa3G;L\xc5\x0d\x17\x16\x86\x10\xa9\xe1jb\xd9tI\xa3U\xe9#G\xd0\xac:3\xb7\"?l\xb7X\xab3\x93%\xfe\xa8\x8dEg&\xb5\xfc2f\xd5wd\x96#\x9b\x14\nLl \x99R\xdbSb\x1c\xc9\xa8a\xa4|G\xdc\xe9(\x99\x05\x8a\x17\x12K]\xec+\x1aIPj\x0b\x9e\xdfE6\x85\xccj=\x98`9\x98\xd6j\xa0\x11\xa4\xd0\xd6\xebET\x95\x834\x0f\x82\xd4M\xb8\xed\x81)\xfd\x0bM\xc9\xfa2\x96\\q\xbc\xcb\xae\xb7\x8a\xf6e&\x9d<\x08j\xdf\x1e\x93\xc9\xec\x8cg\x8e[k\xf5\xe0 \x88B4\xaf\xad!\xed\x84\xd4J\xf7\x9d\xc1\x80'\xc7\\\x8dn2\xabN\xc8^c\xadcr(l\x81\xb5\xea\xbc\xc6\xa7\x1fG\x9b>3\xe97\x99e\xdc\xc0I\xd3u\xf9XZ\xdc\xf6g\xcc?+\xafj\x95\x7fw'\xbb>\xde\xe8Tb\xfd\xdb\xae\xc5\xceR\xa5\xde\x1e\xf1\x97\x1bE=?\xe0bI\xaa\xfb\x9c9\xbd\x80g\x0b\xacu\x0c\xfeB`\x8f\xa7{Y\x14\x0b\xb8\xfa\x13\x15\x08\x9cd \x9a=6\xf4JW\xb3\xafV\xe8A\xf0;J\x00\xbf\xdf\x1a%\x18\xfa^CV8\xa0\x01{\x9c\xc7K\x90\x8d\xb3\xa1=I\x0b\xf8\x0c\xa0\x93\xd0\x02\x01m\xba\xd2\x9bB\"\x88\xf8Sb\x05\xf1\xdb\x90DC\x0cE\x90\x8brw\xe2\xdf\xd0\xa2|\xabQ!\"k\x19\x94c-\xd9b\x8b< k\x86%\x93\xf1\xbe\xf4\x12;\x12NAe\xc0\xb6*C\xe8\x9b\xa9\xcc\xf5\x1a{\xb6\xe1\xd89\xf3C\xe65\xbb>z(\xedG;\xefL\xd2\xf6\xf5u\x83W\x1b\xec\xa4\x7f\xa2\x83\x1c\x1e\x0d2F\xdc)L :\xc8\xa9\xa85\xb1'\xa6z\x0b\xd8w\xd9\xdc4}0\x99\xd4Q\xbe\xe5\xd2\n\xa3\x90\x0b\x02=mT\xad\xa0\xea~\x98O\x91hob =\x84^\x10\xb9{0\x86\xae\xf9\xe8F\xc11\xf9(\xa5\xfc\xde\xd8\xd6\xf3\xda%t\x0cW\x8c\x0c%\xd7K\\\xc1\\\xca8u\x88=\x11\x97\xbf0\xa7J\xb3\xc3\xa0\xf6yl\xfd\xf3\xfc4\x0e\x9c\xc3\x05\xe9}\xacv\xd1\xf2nG\xf9\xd7`9+1\xc7\x9a\x14J/\x86\x19v\x8d\xc2\xf3;\xb6\xf3\xe2\xd8\xce$T\xf4\xfc\xb1\x1d\x0dK|jZ\xc9\xa9\xa8R\x16\xa1Z\xfb\x89\x13\xc7<\xa9u\xd2{!\xd8S\x1c\xc4vI\x85\xfe\x1d&}}\x98\xd4\x93\x8b\xfeU#\x93\xea\xe5+\xc5\xa5\x8e\xfe&\x98?\xcd\x91Y\x1af\xabF|.\x19t\xeaQp\xd2\x82f\xfc s\x12\xee\xb4*\xb7\xec2\xb5\x936\x1d}\xf1\xc6}\xd1\x02j\xb9r\x86\x8c\xa1j\xaa3Tw\xa1Ws\x80(\xdb\xd4\xe6\xab/z\xb0dV6(-\xc7b\xe9b\x08\x85lo\x81\xeb\xe8\xcc\xba\x17 \xd4jB\x00\xa7<02\x15&\xfc\xb5\xc0\xf8\xcc(\x0f2?\x96V\xa7\xeb\xad\x96\xf4\x0bo\x89S \xaf\xf6j\xb3\xac\xaa\xa3\x17Q\xa4\xedZ/~\xf5\xef\x1bC\x13\x9e_\xa9Q\x0f\x0d^\x16\x1d4\x14\x06\xedF\xafj}\xb9\xa4hte\x14g\x87\xb2\xdd\xfa\xe2\x91\x1e\xab\xdc\x17\xd8?\xf9<\x12{\xcd\xfe\xbd-\xb3u!\xc8\x17\x15\xfa\xc4\x81jt\x0f)Q\x16+\xf9\xab\xad\xa8\x17\xaa1\xab\xac\xc6\xb6\x86\xe5 \x97\x86N8\xe0\xc6?\x05\xfei-/P\x94\xbdV?\xdd(V\"n\xfdt\xd5\x80Z\xf6d\xd6w\xbb\xacu\xecX\xab\xa3DWA\xf6\xaaq\xca\xd3\x054|\x99\x012}R\x1a\xa2 Y1\x91m\x999\xb7)}\xfd\xddnQ\xe8\xb7\xc9\xc2\n|92\x87\xac\xfe\xd5\xa3T\xbd\xd7\xa8\xda\xab\x86\x93BM\xcb\xd4\x81\x9e\x99\n\x8a\x95\x9b\x9a\x18\xf2\xc9'\x91\x1a\x08\x9e\xd6m7\x93\x83p\n*\xe3K\xab\x02\x84\xd7+N3\x939\xc9\x80g3\x80Ei\x83\xf3\xb43\xe1\xa5\x1b\x01\x8f\xd8k\xcc\x9f\xce\xd0\xaf\x7f\xc6\xb7\x06\xe8\n\xb7\xfb\x91\xdd}\x9e\xe0~\xd3\xa4\xc4\xe7\x9a\xf6\x04=\xd4\x93\x97\xe5\xba\x103\x04\x81!\x13\x0f\xbbS\xd3l\x17\xdc\x1a\x12[\x88>\xc2\xff\xeaR\x8f\x85\xd0`.\xd8\x9a':A\xe8g\xbfe\xc1\x9f\x91\xb9\xb2\x17\xc2\xec\xd9d\x86\xcf\x9e\x83\xe9\xb3)\x88\xab\xf3e\xf4\x00\xe8 X`\xad0\x8ab\x1e\xf2\x84\x85Q\xc2\xfb\x9fCe\xd5e\xb0\xce\xb6\xd1\x8c\x98c\xf3\x04\x9d;\xf4\x03/\xe1\x96\x90\xeeIK\x0e\x9a\xbc}U'\x9a\x8d\x86\xdc\x1f\x0c\xe5c\x13ymR\x18\xf1\xebE\x89\xc7\x93\x05eUj\x10H\x9cd\xe0\x87\x0b\xac\xe1\xa1\x92\xd8\xf1\x95\xfa\xf2O\xc9\x04\xb0\x1ee\x8b\xa1?r2\xee} \xc9_\xdfN\x17'\xccO7\xc4Y\xf5\x1a\x84\xc2\xb1\x8e\x19,\x1fL\x85\xf0\x82\xb1\xd4\xe2v\x18\xa5n\xe2\xc7\x99\xbe\x00\x98@6\xef\xda\xce\xc1oO\xe5Q\xab=I\xdb\xd1\x0b8I\xdb\xa9'\x11\xac\xb41\xec5p:\x0e\x95\x8f1,\xfc\xc4\x9dI:F\xe3!\xe8by\xb3\xe3\xc5\x8b\xa6z\x15,\xa2\xa9\x1a\xc6\x82v\x00d\xec\x9b\xe1\xffK\x9dp\xbcZ'\x1c\xcf\xe6j\xe3\xeb*6\x1f\x1c\xcf\xe6j\x93+\x8057\xa2gs\xb5 \x14\x80\xe4\xecw\x15\xe0\xf4+\xa71\xa8\xaf@sd`\xb1\x86\xd8\xfdt\xbc\xaf\xc7OG\xffE\xb4\x91\xe7\xa5\xf5E\xfcQ\xd2\xb5\xa5 \xc1d\xbc\xd6\x8c5!\xee(\xa8\xc4\x1d\xb9\xe0\x15\xe4B\xdc\x91{\xf4h\x87\x05\xd7\xdd\xaaW\x90k\xb9\xe0SK)\xa8\x866\x99\xe5\x84\x11\x81\xdf\x19aF\x115\x9b\xd5\xc5\x1c\x052\xe6(\x99\x19\xf0\xecR\xe4\xf1@HO\x13E\xec\xd2\xf8\x94\x17?7^\xfc\xad\xdf;^z\x15\xfbxKf\x93+2\x87\xfd\xe1\xcc\x1f\xfc\xde\x0f\xca%~p\xfcx\x97\xb5\xa4\x05\xc0\xd6\x96k\xd2\xd8\x1eO\xdd!\x1f9\xa4\xc9\x9aB\xbaQ\xd0\xca\xc8\x14\xee\xaaIo\xf1\xfe\xb6\xac\xf2<\x93N\x14[\xab\xbc\xbf;\xd3\xf7C\xafx\xde\xdbf!\xb8\xdb\x85\x9c\x14\x84\xa1'\xc4 \xa5V8H\xad\xc2\x81\xf3<\xc2\xc1\xd7\xca\x18Uj!\xb9=\xcdJ:\x9f\x98\xff\x94)2\xca\xa7}\xf9\xd8\x81\xc2r\x83\xebK\xe5\xb2T\xc2o\xe7~\xd2\xc4\x99SY.l4\xd2\xb9\x8a\xcbo\xf1~}\xa1\xbe\x99\xc3f\xeds\xf9L\x11`>\xa3nz\x9b\x8d\x832\x8dd\xbb\x05\xecN\x9e\xe4V\x83\xb9b\x08\xa5%\x95\x9aXx\x0c\x857\x13\x7f\xe4g\xfe\x98O\xac0bgX+\x92#i\xd0\x1e\x06\x82\x04\xc2\xab\x902)\xd0\xef\xff~\xc2\xfbuna2 \xa9|\xccx\x00\xe1\x0f\x1a\x07\xcbt\xab=\x10\xb4\xec\x88S\x14sJ\xc5\xccIo\xa7P\xcc\xb8\xa3\x04\xb5\xd6\xdcI\xa1~\xe5[\xa2\x91\x18\x06\x93\xff\x7f,\xf3\xb3\x80\xd7Z<_`\x7f\xd0\xd3\xcd\x9b\x19?\xc8j\xfb\x8b\x05_\x10\xbc\xa8\xb6c\x7f4h\xec7M\xdc\x05\x16\xb6O\xce\xcd5!\x95V/\xe7g\xe3\x83\x86\x8d\xdf\xf7\xbdl8\xb9\xd8Du\x96\x19\x15t\x8d\xf7E\xbfs|4\xe9\xa5=\x95\xbcL\x92\xc2\xc0\x11\xd8<\xa1F/\xca\xb2h\xb4\xc0Zb\xb0\xb5%k\xe2_\xea\\G\x04\x15=\x94\x89\x1a\xfctcq\xfbD\xbbS:\x07\x1e\x8f\x13\xeeJ\xcd\xad\xa6z\xba\xef\xcbL\x84\xae1:J\xbe\xe9\n\xa5\x8c-\xb0#G\x06]y\x06\xcb\xa7+;\x8c9\xbc\x997j2\xf9\xb8N\xca\xcd\xd9]h\\\x99 \x87\xc7\xa3\xb6\xa1\xc6\xe6\x18Bo5\x86\xc6:\xcfelb*\xc0N\x90\xdc\x05\xd6@\x9d\xf5\xaf\xe0F\x8d\xf7)\xfa\x07\\\xa6\xf1\xa12\xfd\x0b\xe5\x14\xa7xL\xbf\xc0\x85\x05v8\xb9\xb8d;\x0b\xccm^\xb4\xa6\xcc\xb1\xb0\xff\x8e\xe0\x0b_n\xfb\x87_r\xfba\x08/v\xf7\xff\xf1m\xa8\x96I\xea\x1e\x8b\xd3\xbf)\xf6T\xbd\xf8X\xbf\xa9P,\xccG=\x9eL,\xe6\x87\x19\x1fLQ\xae\x17E\x01w\xc2\x86rZ\x03\xfc2\xc86\xfe\x92vh\xa6\x91C\xc9\xa9\x13\xef\x02\xd9\x7f\xe9\xd8d\x85O\x8c\xe7\xac\xb5\x0c\x95\xb0s(\xb7d\xe70\xe6\xd4,\xa4\xd7\xa8o\xf6YZ\xa2\xb9w\xc9\x89\xa5Lm\x93\xd0\xab\x1b\x17\x9b\xaaB\x97i\xae\xa46o\xca*\x15\x95\xa3\\\x0b8Um=\xd8\xcd\xa28\x1c\xc4j\x99\x92\x88?\xa9\xa8\xa2\xf1E!q\xc4\xaaE\x8a}n*\xc5\x0fbG(\xac\xb1`\x87EA \x00hx\xd3\x14*\xf1VS.\xf0\xd3\xf2\xc2\x14\xa8Q\x8d\xa6\x87L\xa5\xbf]\xfb\x9e\x18Q\xea\x08\xdd\xfd\x8e\x0c\x90\n\xa8\xc1/\xb7Y\xd6\x84\xe6\xda\xce\xc1J\xd6\x95EN\xce\x9d\xea\xd8\x8c\x7f\xb2\xd0\xec)\xab\xfdO\xc2\xe6N\xd8\x0dm\xf9\xd7kh36\xb0\x19\xc7\xf3.D\xd1^\xbb\xd5\xe3\xfd(\xe1\xdbjy\x14\xd9M\x1b\xd3:\x9a{\xe6a\xc2\xfb0\xcc\x94g\x8bY\x96\xf8\xbd<\xe3m!\x80\xb7\xba\xf6\xdb\xbfN\xb74LlzM\xa7q\x89;\xfe\x87\xd7\x17\x8f]\xfbA:{\xec\xf4\x91\xd7~0s\xe3\xe8\xef\x1f\x1f\xa8d\xc5Ug8\xba\xda\xf5i\x98\x8a\x85\xd1\x88\"\xf0\x94\xae\xf5\xe2\xf2\xf2\xcd\xc5\x9d\x9d\xad\x05v\xbd\x05\x97\xe8\xadj\x86P\x92\xda\x82\xd5\xe6c\xc2C).\x11\xd3(O\\\x8bE\x00\xee\x19\x1a\xfc\x89\xfcBm8s\x06\xee\x0eZ\xd2w\xbc*B\x08\x95;mgE\xd6\xe6\xa4N{\xac\xbb\x94\xach\xabN\xb2\xe7E\xfbaU\xa4\xbbK\x0d\xac\x10\xbbq\x86\x85|\xbf\xb0c\xd6\x08\x8f\xc3l\x14\x88clg}\xd9a\x1c\x0d\x12'\x1e\xf2\xa4\xbeP/\xe1\xce^Z\x0f\x0f\xfcp\xcf\xef\x1f6\x17\xd8\x91\x9b\xbc\xc0Z7{\x81\x13\xeeY\xd2\xa8w\xd4EK;\xb3(\xd0\xae\xcc\x12\x96\xa3\x850w\xff\xafI\x15\x05\xf8\x9fq\x8d\x91\xe3\x8aa\x7fJ\x86\xa6\x01\x04\xb1FN \xd6\xeb\xd9Gx\xd7\x17/m.\xb0\xd6K\xa4|l\xf9\xba\x18J\xccy\xfc\xe7\xb84|\xbf\xf7!\xfd\xae@\x8f\x7fNA\x00\xf8K\nH\x83H>)\xf1\xec\xf1_P\xe0X\x02\xfe\x1b\x02\x90\xb3\xbbGvDz\xa6\xb6\x9e=z\x9f\x02d\x94\xac\xb5\xca(\x85\xf9`,\x02\x90\xe3\xc8\x16?\xb2\x03{\x12\xf8\xd8\x0e\x94\x07\xf2\xd1\x13;P\xf6\xf9\xe8\xa9\x1d\x08\xb3\xf8\x1b;P\xe2\xfc\xa3\x7fm\x07\xca\x85y\xf4?\xda\x81\x12#\x1f\xfd\x1b\nL2\xb9\x02\xbf\xb2A\xc6r\x8e\x0f\x08]\x01\x18L\xe3\xaf(0\x05\xfc\xbfGhE8HEo\x9f\xfc\x84\x02\xee8\x89\xc0\xe7g\xff\xfc?`T\x8c\x06\xd2\xee\xfa)9\xd0\x1a\x80[[\x8c\xe2>\x1c\xf5\x7fO\xaa(\xc8\xcf\xff%\x86\x88S\xf0\xec\xfe=\xf2Y\x10>\x89\x88d\xe9bID\x1fcJ\xe6\x00F\xdf\x7f@\xbe\xfbr\xc1\xee?$\x80(]`\xado\xe3Y\xc4qpxN1#+\xa9s\xe28\x89\x0ej\xc6-@\xfc\xb6u$\x8b\x89\xf4\xac\xb2l\x83\x06|\x80k\xa4.\x10\xcf\x7fI\x0e\xb1\x81\xfco\xa4N\xea\x0f\xe4\xc0\xef\xff\x8cT\x12X\xf0\x07\xe4\xeb\xe1\xa8f\x17\x04DM\xe6\x9f\xe3n2?\xf0$\x8d&L\xd1@\xfe\x07\\'\x17\x02G\xeb\x13\x82Q\xea;!!\xfbn\x14\xfa!\x1c\x14\xcc2\x9d}\x05\xf9\x08S\xf5\x9e\xe3\xee\xb9\x11\xd0\xab\xfb\xefZ\x80Z\xcf\xee\xbdG\xa0\x89\xa4\xbaO1}\xef9\xc9\x98\xcb\xb1<\xc0\xfd\x9du\x92}.1\xfb]\xcc\xbb{\x05\x08\xa3\x1a\x80\x80dS`/\xd9\x13\x80?%\xf3\xee%{\x99\x06\x92%\xab]\xeb\xb3 s\x90\xfd\x81\xcf\x98\xe7\xf6\xbc\xdby$\x97\x1dK\n=\xee:y*W\x0e\x8f\xec\xac\x04q+\xac\xd7\x08\x1b\xc5\xd9\xa1\\\xf4G\x98\x92\xf4\x04~X\x91\x83'a\x94\x8b:oc>qV\x82\x82\xc0Ok\xc0\x99\x9430\xf9\xeb\xa9\xef\xff\x0b\xfd\x0e\xa2\x0c\x1dB\xb6\xcf9\x1co\xd2\x89\x96\xb4\xc8\xbej\x00f6=\x7f\xe0\x02\x05~\x88\x05O\x01\x02\xd1\xf3\xd9/0 \x16\xb0\x1c\xaa\xe1\xc3\xdf\xf3\x07\x91\x17\xc1\xb9\xc4\xb2\x93\x80\xc5\x01l\xe4GX~\x12\xc0\xcc\x1fq\x80ZF\x93\xdeV}~D\xd0\xdd\x1f\xa4\x99#\xb9\xc5_\x90\xa9\xfb\x83,\xf1\xa5,\"\xf4&Q\xe6=rr\x8b2\xd0\xc3{\x98\xd6\xf4\xfcAnF\x8e\xa9W\xcf\x1f\xa83\xfa\xd02)s\xda\x1e\x92\xe5\xd8s\x92h_\x80\xde\xc7\xd4\xa2\x178\xee^\x10\xdd\xe1J\xb8\xfa\x10\xcb,\xb2@z;w\x12 \x7f\x0f\x0b<\x12\xae'%K`5\xa1R\xc2,\x0d\x968*\xa5\x02\xb8\xb5}\xf6\x0b\xb2;\xe5R\x89\xbaT~\xf6\x1e\x96\x02\xa4\xae- \xff\x023\x86^\xb077/\xeb\x90\x03\x12\xec\xcd\x9d\x94\x10BE\x82\xbd\x13\x00\xc1\xc2\xb2LO !\x98\xa1\xf5B\xb1\x18g\x9e\xfd\x183\xda^\xc8o\xe7\xbe$\x07\xf7\xff\xda\x02^\x07\x94~\x8a%\xc0^\x08\x80w\xb1\xbau\xd6\xc8B\xff\x07\xaebd!2nh\xeb\x01\xe9]_i\xdb@\xfb\x99\x0f\xe8E\xe6\x1a\x1d\xf4@J\xf9\xf0>\x05-\xaf \xc8\xcf\x7fa\x81\x04\x12\x82YT/:\xf0\xa0\x0eV4\x04D\xd6\xf9\x19^\x04\xd1\xda\x96\xac\x83%\x11\x01\x91\x07\xd6\xb2\x08\x07\x1e\xd4!\xa8\x10\x1dx\xb2\xce\xcf\x08O\x8f\x0e.\xc8*\x96\x01H2\xfa3r\xf6\xa2\x83\x0b\xcb\xb2\nVo\x05D\xb2\xce\x9fciD4\x06u\xe8.\x1c\x0ce\x9d\x9fa\x92,Z\xdb\x95u\xb0\xbe\" \x92\x95\xfc\x9c\xf0\xfc\xe8`\x08u\xb0\x02$ \xb2\xce\xcf\xc8i\x8e\x0eF~\x08\x04\xea\x01\xa1\xf2\xd1\x81&^\x0f\x08k\x8d\x0e\x0c\xd5}\x80\x15\xb5^t\xb0\x0b{\x8e\x95\x0d\x01\x01<\xc1\x82i/:\xc8\xa1\xce\x7fk\x81\x00\x9e`\xa5S\xb4\x06{\x8e\xb5N\x01\x01<\xf9\xa5\xa55\xa8ci-\x07<\xb1`\xddeY\x85\xd0\x92\xe8@\x9e\xfd\x9f\x11\xca\x16\x1d\\\x06\xd4\xb2\xec\xece\x89[?'\xb49:\x18C\x1dB\x95\xa3\x831\xe0#V\xb6Dk\xb0j\x844F\x07\x97a\xa5\xb1V'Z\x83:XA\x11\x10Xi\x0b\x0e_\x86U\xb3\xec\xf5eXi\x0b\xfa\x8c\xa1\x8e\x05y\xc6\xb0\xd2\x04\x0b\xeae\xe8\xb3\xca\x98\xf6k\xb2o\xf5\x80qO\xb2\xf7\x8f\xf1a=\x0bZ\x10\x95\xb7zF=\xfa\xdf \x84\x8f\x84p\xf7\xec\xad?#\x90:\xc9>Us!R}/\x8d\xc4:\xff\xe0\x07\x96\xefR\x85\xff\x90\xc8#i\x14\x0c\xd3\\\x02\x7fEHv\x1e\xc8m{\x93lu\x1e@j1\x1bH)o\x7fj\x01HM\xf9 \xb6L\x08\x08\xe8\xcax \xce\xe6F\xdf\xb35\xa7@\xb8\xd6\x92\xb6E~\x8a%3\xd7@~J\xea\x80\xfc\x88\x89\xbc\x12G\xefar\xe9:\xb16ta\xf9\xcbu\xe2^\xa2d\xc3\xc7\x98\xd5\xb9N\xac\x9a|\x8c\xf5\x7f\x01R\xb5\xf0\xe8\\'VB\xecc\xcc9\x96\x9c\xd8\xcf\x9c`\xd9\xef\xf7y\xc2\xc3\xccw\x02\xc9\x14~\x82w\xdaubPY\x1e\xff\xe7\x7f\x8f\x1bq\x9d\x04\xb6\xf3-,1\xbaN\"\x15\xd3_\xd3\x05;\x0c\xf8!h\x17X\nqu_\x8f1\x82.\xe9\xf6>\xc5<\xd35\x10Z\x87{\xbe\xd4\xc7\xc9\xb2\x18\x08\xe6YKJW\xf8\x14\xa3\xb4\xab\x01xc\x96J\xaa=V\xc0\\7W\xf3\xa1\xa3\xce\xe34\x95\xc7\xf41f\xf6K\xb0e\x9fb\xb3\x8b\xab\xbe\x93\xfdW\x93\xf9\x18\xcb\xa9K\x02\x1086\x90[R\x1b\xb1\xce\xe6J\x7f\x86\xd6\xc7\xf8\x84.\xf10\xe3\xc9\xb2\x1c\xc4\xc7\x98\x1c\xb9\x12\xe8\xd9\x81K\xfd\xc4\xbe\xdfZ\x9f\xc3D|\xe9\x02\xa8\xd6x{\xdc\xa1\xfc\xfe\x0fdC\x87\x1c$\xe5\xbf\xc4b\x98\x84\x8c\x9c\xc4\x0e]\x1a\n\x12\xfa9\xedF\xaa\xcd\xa4\x17\xb0\xe4\xfd\x82l\x00\xa0\xc6\xaf \xd5\xf0\x13W\x91\x1a,\x9f\nP\xc0\x9d$\x89\xf6\xb56\xf2\xce\xffY_\xc6\xe8\"\xef\xfc_\xd6B\x1eX\xc4\x9e=\xc0\xb2\x8a\x02k\x0d\xf8\x01\x96K\x14\xdcS\x06\x9d\x07X>Z\x92\xf0e%\xd0c\xd9E\xd5\x16L\xf5cL\x9c\x15l[T\xfcs|\x9a\xa0\xd9KF\xd2\xc3B:\xc07\xb5\xb0\x87%u\x00\xef\x18y\xcf\xb2\xba\x92c|\x88\xb5z\xd7\x07=\xd3\xb6\x1f}}\x8c?\xc2\x07\xd2\xf5\x93\x11\xd8^\x9fb\x0b\x82\xeb'\xa9B\x8b\x0f\xb1\xcc\xb5$\xd4\xb7}?\xe5KQ\x98Ey\xb2\x1af|\x908\x923\xde\xc3\x87n)\x88R\xbe\x94'\xc1\xe1r\x94\xf7\x02\xfez\x1ee w\x90-1%\x8b2dc\x82\xbc'\x97\xe6\x97X\x0c\x93\x90\xdc\xcf\xac\xc0\xa5\x08\xac\x89\xcf\xee\x91\xe3\xad \x0b\xb6\x1ap\x03\x83Ey\xd7\x80\x88\xfd\x16@\xb7k`\xa3\x91 Y]\xdbw1\xec\xff\x8a\x02\x80\xd5\x12\x16\x14\x8d\xe2>L\x07Kb\xae|\x19a\xc4\x15\xdd\xb6\xd5\x0c\xf8\x01`\xd7\xdbx_\x8d\x99\x90p\xca(\x1chv\x8bI\xddR\x14\x0e\x92\\ux\x1f\x0b\xbaK\x05\x0f!\x18V\x80\xf0\x11\xb3\xe1\x15-#\xb5t\xdb,\xb4\xfaNw N\"\xb8\xd6\"\xacI\x82r7\xb3C76\xaf\nR@d\x9e(>\xac\xfb\x9e\x02g\xc0\xe7q)\xca\x05?i%\xa2e\xa6\x90\xec!\x99M\xee9I\"W\xe7}26 \x93\xeb\xf3>^\x1f7\xe7\xb1\x84<$s\xcdy*9\xc7C\xacM\xb9y\xa0\x97\x1b\xdbv\x01$\xa7\xf5>\xd6A\x96\x94\xbd\x95\xf0i\xf8~\x0f\xab\x9an.\x84b%\xf9\x126\x92\xc7J\xfe&\xd7:nn\xe4e\xc2\x96s#/\x13\x11+\xd7\xf2\xf2\x03K\x83\x11\\\xe4\x91c\xaf\x84\xbc{O,\x02rn\x90\x92\x90T \x92\"\xe0\xfbX\x8dv\x05y\xe7\xb7\xe3\x84\xbb5\xdb\"\xe1i\xee\xd6mN\x12\x1cjc.\xd6\x80$\xb00\xe7\x12\\\xcd\x93D\x1a\xe6?\xc6J\xb7\x9b'c$\xb3\xd0\xad\xd7E\n\x91\x85N\xbc~d\xea\xba\x87\x0e\xaa|\x83F\x04V}\x83v\x0f_\xc5\xb8\x87\x81\x9b \xda\xf3\xec]L\x90\x97e\xaep\x01z\x13Sc\xaf\x00a\xc1\xd4s\x02}\xa3\x81\x0f\xd8\xb2\xdeh\xd2\xdc\"\x00~\x8aq\xde\xd35(\x00\xc4\xb171QXv\xd2!\\\xb0\xe1\xbd\xf14\xe4\x01f\xea^\xc9>\x8f\x97\xd5\xeb\x05\xd2\xd3\xe0\xd7X\xc8X6Z\x15\xde#\xcf@pc\xcb \xb3cv\xe2\xc1g,\x1e,\xdb\xb5M\xf0\xf5\xf8 >\xb3\x9e\xd7\xb0]z\x1d\x7f\x8a\x8f\xf3\xf2r\x94%\x0e\x984\xdf\xc7\x94\xd7\xf3\xa2,\x05!\xe41FQ\x8f\x0b\x0e\xff1\xd6\xe7\x969p\x1e\xac\x18,\xf3\x00\xae\xbf\xc8\xdc5\x00\xcf\xde+\xe9_\x18i\xbd\xbe\x9f\xc2\xd1\xf9\x00\xbb\xe0,k\x85 \x8f\xc0\xd3\x00\xb28\x17\xe0B\xe9\x03l\xeb\xf5\x86\x0ep\x8a\x9fb!Y@`=\xb1\xcc\xb0\xec;n\xe2g\xbe\xeb\x04\x8bun[\xa52\xa06\xfc\x1a\x0b\xa7\x95\x12B\xd6\xd5mQ,,J\x9eW\x9eT?\xac/\xb2\xa3\xae\xeb\x7f\x8d\x8dx\x9e\xefH2\xfb\x10[\\\x96}g\x14\x815\x86\xc0\xbc\xc90#Gcs\x9e\x80\xa75\x10\xb9h\xd8 N\xad0\xe4\x00\xf8\x03\x07\x04\xe3\xdf\xe0U\xf2\xfc\xd4\x97b\xeeCL\x18=y\x13\xf4 \xc1n\x7f\xec\x83c\x83\x1d\x12\x85\xc6\x94\xfe\x90 \x9a?\x8e\xc2\x03+h\xf9\"\x9ct\x8c5\xde-P\xda\xb1\x1c\xe3\x05n\x94\xc8\x81\xbf\x8b\xf9\x9b\x17\xb8\x89|b\xe0\xd9\xbb\x98\x0f{Q\x10H\x94\xfe}\xdc\xbd\xb9\xa9\xc2:\xb2gD]\xacH*c\x06\xde\x0e\xaf\x06q\xa3Li\xc2?&(\x16eJ\x9f\xc1$[B\x94Pq\x1f\xd3\xa0\xe5([\xb9\x9d\x83>8+:f\x01S\x0c\xae\x01\xd8Z\xc1\xb5\x9d\xf4\xd9}\x8c\x1f+\xb0hX\x0d\xe5\xb0fX\xca\xe1\xcbJ\xd2 \xaa\xc9\x8a\xba\x05\xc2\x83\xd5Fz\"cpU\x01\x1fR8\x9f?\xc1R\x1c\xef\xeb\x860cZ\xd1:\x066\xc3p\x0d\xc07FR\x8bz\xf6\x04o\xc5\x8a \x8b -\x19\x08fy| \x89\xf7\x132\xedA\xaa\x8e\xca\x13l\xe4\x05e\xed \x96\xe2VJ\x86_\xd2\x7f\xe0\x87\x19OdW\x7f\x86 \x13\x87K\xed\xb71\x93\xe2\x01\x0c\x0d\xef8\x0f\xcc\xd0\xf0\xda\xaf\xe8\xe8\x0b\xbc\xc6\\\x03H'B_\x94c\xc6\x04IBR\xb8\x86%@\x99ky{\xe4\x04\xc1\xb6\x91\x08\x7f\x81\xe5\xe3B\x17\xb5\xd7\xbf\xcc\x13\xdc\xc6{\xd8Y\x84\x8fRI{\xdf\xc4\x9cS\x00\xe6NH\x10V\xa3$H\xba\xbe\xbdI\xfa]?\xbf\xc0Z\x9f\x91\x83'-\xef\x9f\xe1\x0b8\x1e\xaa\xce1G^\xd1.\xfe\x0474\x80`\x87\xd1\"\xb0M\x8e\x1b-\x82\xe0`\x0cT\xf4!\xc1\x80\xd8IR\xe0\n\xd8*\xc3\xb5\xf4\xfe\x18Sx\xe5\xb4\xfb9&\xd6+\xc6\xd9\xfbs\xda\x8f\x01\xe1Z\x02$\xb6\xf67\x04p[_\n\x12\xba\xc7o\xd7\x931~[y\x97\xdc\xc7k\xcdo\xa7\x81\x13f\x83,\xb1\x1fT\x00\x07<\xb5\x9f\x16\xa3\x07=\xa6#\xcd\x1dy\xc4\xce\xd8\xaah\xad\xdf6\xa0\x9c\xc3\xb5\xe8}\xcc\x92Vn\xe7~\xe0\xf7\x12?\x97s\xf9)\x16\x18JN\x946\x08\xd8\xae\x1ec\xa5\x81\xdf\x1e\x17\x1b\x8e\xa5h\xaeY\xe0\x07d\xc3\x13Mq\xf1\xa1_\xd1nA\xd8\x10\xc55\x00\xf3m\xaeI\x0e\xd1&W\xd4\xbe=\xc6\xd7&\xbcnCW\xc0tE\xf8\x06|&|i\xe7\x82\xa0\xdb\xb8[\xb0\x96~\x82'\xb0\xa2\"%\xc8IV\xdf y\xc9\x13\xe9R\xff'\xd8A\x8a\x1f\xb8\xa2\xc2\x11\xf2\xd9\x87\xad\xbf\x87\xe9\xd1\x8a\x80\xa4V\x10?\x88\xb9\x9b9:^\x86\xac\xfa\xca\x01${\xf0\x9d@^/S\xdeY\x14\xb03\xd7\xbe\x13\x04\xbe\xbc$T\x96G\xc2d\xcf\x81\x98\x80\xa5\xe6>\x88 \x98\x82\xf6\xf9Hu\xf5K|\xf3\xd0\xef\xfb\x10\xf8\xf8\x9f\xff\x06\xcf\xb3\xdf\xd7\x10Z)\xd0 \xdc\xd59\xcd\xe4\xb1\x9c\xd6\xd7\x00L\xe2\x8a\x01`5\xe2\x9c\x1f\x04\xdc\xc3l \x13\\(ec>X\xec\xea\xdf\x82\x9e\xfa\xb70 p\xc0B\x87\xc5\xaeb\x9e\x18\xeb\xfbA\x16J\xf4x\x0f\x9f\xd3~\x18 \x06\xf0\x9f\xc8\x96\x19\x96\x81\xf5\xb3\xbea\x19\xf8\x10\x9d\x8b\x92E\x10'\xee\x91=\x88\x12\xa7\x1e$\xfdX\x1eb\xc3\x87\x00\xc0\xbd\x00\xe6g\xe7\xa2<\xf1y\x92%p\x0bL\xe6\x14;I\xa6\xfd\x1e\xb0\x10\xdaO\x1cW\xba\xb3\x7fL&& \x92\xa9\xff\x04\xd3, \x12L\xfdc\xbc\x9f\x12rJV\xc2\xc4_\x82^\x96 <\x01 zE\x82\xb0\xe0.@\xf30\n\xb2 \x02\x04}aF$@\xd2\xe1\xfec\xac(I\x08T\xc2\xfb%A0\nl\xfa\x13\xa0\x93P\x0bK\x19\x02t\n\xa6\x85e` \x82\x06\xb1=W\x80\xbe\x03 l\x13\xe8'\x0e\xb0\x97\xb7\x08%HT\xe8\xc3\xbbX\x08?\xa7y\x05\xd9{\xa3\xfbb\x81p\xa0U\xaf\xff\x07\xf3\xe2\xf3\xca\x08\xfd9\xdevm\x9d\xfe\x1c\xb3\x17Y\xc3\x13\x12\x08^\xb8\x81\x81\xe0\x15\x18\xc0\xcd\xed\x13l\x970\xa2\xc9\x13L\xd6\x00$\xf9\xfb\x13L\x8e\x15\x0c\xe6\x8a\x91~\xc0S5Yz\xf3.`0\xc8'\x988\x9c\xd7\x1c\x0b\xab\x17\x03\x0d\xc0\xec\xf7\xbcTd\x1fb\xda4\x00? ,\xac\x0c\x065\xc5\xfd\x11l\xce\xdbXx:\xaf\xaeN0\xa7\x1e\xa8\xab\x13\x82qpc\x80\x9b\x19Hg\xcfgO\xc8\x1e\x83\xbc\xf2\x04s\xaeApK~\xc7\xd3\x1d\x84\xea\x00\x92\x05\n\x8b\x98a\x0b\x10\x10\x98\xec\xc5\x9ckud]\x96U}\xaf\x82\xcf\xb4\xaf\x01X\xc6\xf0G\x0eh^\xb6\xb6\x06~\xe8$\x87\xab\xf6\xd5\x199\x83@\x9d\xe8\xb71j\x0b`\xec@\xca$\xbaw#\x99\xc5\xb4\xf5)\xd6\xd4\xfd\x91\xb4<={\x80Y\xb8?\x8a\xa5\xc3\xec\x7f\xc2\xf8\xb4:\x8a\x03\x1f\xd4\x1f\xe2`\xe2\x87l\xc1v\xf9\xe5\x87\xae2\xb0\xbd\x8d\xafc\xcc\xde\xdd\xc3\x8a\xb7\x84\xa8\xd0\xfd\x0f\xb1\xbe\xec\x87*\x87\x06\x99\xd1\xaa\xc2\x12\x82q\xea;\xd9\x8d0s\x81\xc6<\xc0B\x9c\xca\x08\x0d\xb1\x1a\x98\x81V\x9c\x97,\x8d\xf2\xa4\xae\xd9Uy\x11\xc8M\xf6$\x92X\xc4\x0f\xb3\xc0I\x86\xd2 \xf7\x11\x16\xda\xfc0\xd3A\x14\x1fa!q5\x1c\xfb\xa9/\x1d\xac\xc0fb![\xba\x88\x89qz\x0bK\xe5\xab\x1b@I\xb0m\xd5\x8f@\xf4!X\xabo\xbc0\xc1\xf35\x00\xdf%\xac\x1a\xae\x86\xf9\x92o \xd8\xac\xb5\n'\xf9s\xcc\x07\xd5 \xff\x1c\x0b\x16~\xed*\xf9Z\xca\xfe\x18\xb3\xf9U\xcd\x15\xc9\xe12\\\x11k?\xdaC\x92\xe2|\xea\x87Z\xf0&49\xf5A\xc8}HF\x9d\xfa`#~\x88\xbd_%DZb\x1fb\xca$@c\xfb 2\xfb\x0e\xeb\xfcS\x9f\xe2\xcbp\xdf@\x08\xc1\xcc\xf7\x00-\xb0\xee\xe1+\xc0?`s\xe8\xaa\xbaq\xc1\xac\xdbW\xdf1V\\\xd4\")\x9e\xfa-\x0d\xc0\xeb\xa8l\x1b\x18%\xc0\xb4\xf1\xf7xm/j\x06\x86y\xff-\x0d\xc02\xca-E6\xff_L\x1d/\x1a4\xc5\x87\xe4\x96\x81`}\xea\xa2\xc1!,\x94\xde2\x10\x8c\x90\x17S\x9e\xc0d\xf0\xce\xde\xd2\x90\x7f\xc0\xf2\xc4E\xbdQ\xd8\xa6uKo\x14\xe6\xf8\xdfw\xe2X\x9e!|\xe6\xf64\x00\x930 \x90\x97\xbfX<\xf9\xbe1\x8abo\xa5=\x03\xc1\xab\xf9}\x18/\xe9\x1d>\xe3\xbe\xbf\xafw\x0b\x0b^{\x1a\x80\x91zo\x90@B\xa8O\xb1\x90\xf5}\x15\x0d\x8cwdOE\x03cn\xf5}\x85qX8\xd9S\xd64,\x7f|\xdf`\x03\xa6\xf1{\x06B\xea\x18l\xc0\x82\xd6\x9e\x86\xfc9&\x9b\xc1\xa2\xd6\\\xf0\"\xae\x99\xfc\x02\xf88\x04\x06\x82W8pJ1\x04\xf80\x06\xce q\xe0\x16\x13\xb3\xff5g\xd4\xf3$\xbe`\xdc\x0f\x0c\x04\xabOk*k\xe6\xaf\xb0\xf8\x14h\x00\xdeM\x01\x80\xfc\x8e\x98\x11\x05\xc6\xb3\xccR \xcc\x8exC\xd7\x1c\xf9\xe2\x9a\xbe\xc4\xc23\n\x1cH\xb8\xf61f\xf0kZ\xab\xc7RK\xa0\xed\x00\x98\x85\x98\x986\x1b@\xc6\xf6\xfd\x14\x8b\x18\x12\xd2\x97\xec\xe0}|\xf9 `\n\x84e#\x01\x02\xe1\x81\xa8\xa2\x02\x14\xc8\x95x\x07\xcfH\x06\xd6I\x81\xe5}\x8a)\x89\xb6\xe7|\x80y\x8f\x80e\xb2\xda;\x98\xcb\xa8\x1b\xd2'\xa4\xa7\xc5\xcc\xf1\xa1'\x8a'\x06\x84\x89z\xe0@D\xf2\x13,\xfe\x0b\x00\x98\xa8\xfe5\xb5\x18\x05g\xd5\xb2\xbf\x8f\xa9E\xd0\xd3\x10|\x98\x03\x9d\xe4\xef\xaf\xb0n\x10\xf4\x12\xb0:\xfc\x91\x0d \xea\\\xa7\x80=9\xecGX\xd1\x16\x904\x00D\xc6\x1c\x12`2\x8f\xd1#\xcc\xac\xd6\x8c\xb7!V\xd0\x03\x03\xc1B\xca\x9a!\xbd\xf8\xf8\x05\x06\x82\xa5\xa4\xc0\xe5\xb0\x13\xefb\xd6\x13\xb82\x16\x15\xaf\xc1\x1a\x90F\xb2\xa5\xf0\x99t\xec\xb9R@}\x1f\xb3\x89\xc0\xe48\xc4\x84QB\xc0\xe2AN\x9d\x97x\xda\xe1\x143\xf1\xc0K\xf2T\x03\xc9.x`\xd2x\x87l5\x18!1 \x06\xf2r\x1f\x9fT\xe9\xf2/\x88\xcfY\x81\x07\xe01GhP%.\x80\x90\x81\xb5\xb2\x0d\x89R\x8f\x8a\x85\xc9V\xb7\xec\xedN(\x89)\x80\"\x04\xb0,g\xba\xd1\xc7\x90\x1cj\xd1\xd2\x12\xf7\x03H\xc7J\x91C\xc0\xc1\xf9\xbf\xbc\x14x\x19\xa1\x94t\xd7.\xf9\x8dc\x0b\x85.Ur\x1b\xc7\xb6\x9ej\x11\xed5\x8ei\x87(u.\x88\xa0\x8dw\xb1\xe9VLZy\xe0\xeb,\x7f\xc4\x1f\xbeT\x06\x02|\xdf!\xe7\x85\xf73\xb3|\xa0\x1ec+5\x0d\xf8 FaQ\xa4j+$\xf6\x99\x80\x14!\xadT\x8b\xa4\xb5[-\xcb\xa8iA)r>t\xa9\xf4v\xee\x0f\x8a\x1e1\x11\xb6\x05'`\x8a[\x8a\x9e!\xa1\xa4\nV,\x8c\x0d\x83\xab\xd8\x82%\x1d1\xd4l\x98p^\x84\x98\xe1\xd9\xc8FJ)\x1f\x1f\xe0S_.\xa0\x90\xe9CL\x9c\xcbe\x8c}\xf2\x01\x16\x93D)\x08\x92)\x0d\x19\x0b,P\xa8:-|\xa7\x0feJ\xa1\x1aXG(\x17\xd0\x07\x00\xeb\x04(\xda\x03\xe3.\x8d\xf4 \x82\xd0\n8\\S\xfc\x80\x0bi\xba\x19p\xc1CD\x1a}\xf3C k\xc9'\x80\x9e\xbe\xb4\xee\xbb\xba\x99#\xf2\x9e\xf1 x\x8c\xd7+(\xf9\x04`\xedM\xc1\xe4\x1a<\xc1\xb4&\xe0\xa9\x9a\xacE\xce\xe0\xa9r\\x\x82o\xd4\x03\x9e\xa6\xa5\xab;,\x81\n\xb0\xb6\x13`\x0dZ\xc0\xf8m\xe5\xf7jYc\x01\xd5`\xb25kO\xaa*\x14\xa1U\xa2\x08\x12\xb0 \xe1\x8a\xeeHrA\x94\x80\"\x95\xb8\x0d&\xcdC$\xc7x\x00k\xd9\xb6|\x06\xd7\x92GD\x18\xd0~:T\x1eOJ\x04\x92X{\x12\xa5\xc0R\x01=1\xb4\x91\xec\x00\xa4\x00z\x93X>\x12E3\x1f\x10\xca\x98:Z\xf9\xc6\xf8\xb9\xa6\xafF\x88dh\x8c\x92X\x98ZS\xaa5\xa1\x95\xb5\xdfk\xa4\x81\xc08}ac\x88\x80\x80`J8vz\xbbg\xb3\xc7\xa4z\x82\x041Rc] B\x92vb\xf8\x8c\xc8\x8b\x06\x82\xed\xbbk;\x0b\xac\xf5]\xfcQ\"\x05\xe5\x9a\x99\xa5l\xa0\x9d\xce\x08\xdd6Ng\x84\x86d\xb5\x82\xa4T\x8c\x16l:QP\xa8K\x84=e\x9a\x9d\x7f@hQ\xc9U\x8d\x98v4K&t$K\xe0:\x97hK\x81\x0e1&\x89\xf3\x83,\xd1\xeerdRy\xe2\x19\xc3\x0e9\xb3ybB\x90\xc9\nV|\xd0>\xb2H\xf3\xda\x07\xcd\x02S\xb7\xfa\x1f\xe3\xdb+\x13.\x83g0r\x80\x16\xfc%\xd6\xec\x04\x80\xc3\xe3\x1b\x04v \xc4\x89\xf71\x91\x1e\xc1\xf7w\xf0\x94\n\xfeT\x032\x96\x0dl\x1e\x03\xb0a)Xa\x03\xb0\xb2y\xe0k\x92\x91\x93\xec\x01\xc5z\x0f\xdf\xfd\x8et\xb6\xc5g\x1fa\x99\xf9\x12H\xa0\xd8\xbc7\x82\xcf\x98\xbd\x8eL\xca*l\xe5\x18\xe9H\xe6{\x98\xb1\x8f\xb8\x93\xe6 \xf7\x8a\x07\xb6\xb0\xf2q\x89{~>2Ndoa\x82{\x89\x07\x81\x1f\xeak\x01l\xf4\xbe\xa4\xd5\x01l\x88\x1bi\x00>\xe2\xa3\xa1\xdc\x9c\xb7\xc9\xea\xfb\xae\x0c?\xfb\x18K:*-\xe8=l(\x19\xf9\x9e\xfd\x8d\xa2\x91\xef)\xba\xf0\x14\x13\xd6\x91\xef\xd5\xa4\xcf-\xb2\xc0`\xb2.!\xf0\xc6\x16^\x1b \x82\xd1a \x0e@R]\xf9\x08/\x81\xcc\xc9\xaa\x13\xaf\xde\xc3\x8cq\x14\xb8\x90\xad\x10\xdb\x8fG\x01\xb3\xb4g\x1e\x1a\xa3\xb0\x0c\x1e9\xf8%\xa6M\x12\x02f\x85:\x18\xf8\xfc`\x1f\xbb\xb0'\x9d\x8c?\xc6\xd4:,R\xcc\xd3\xb1\x97r\xc9S\xa0\xce$\x89\x97}]\xdf\xe5|\x86\xb7*4\x10lz_\xd7w9\x9fa\xae\x11\x1a\x08\x96:C\x93r\x96\xf6S\xce9k\x19\xb9Jt\x89Q|\x1d\xc88\xd6\x14B\xf8\x8c\x15\xca\xd0Pw|\xbaT\x82_\xb2\xd4\\{F\xbd\x8fYU\xc8\xf5\xdd+V*D% y\xc7\nQ\xaa\x02\x85\x99\x88g2\xfdu>p2\x7f\xcc\x11\x1fy\x13KW\xba\xdc\xce\xd0w\xf7\xa6*\x16N.u\x99'\x87\xcd%Ko\xf5`KS\xc8S\xaer\"a[AX\x04l[&\x9cf\xdc\xa3A%$\x82\x02\n\x96-\x7fD\xde]\xe7\xfb\xca1\xf9\x07!\x19\x82 \xaf&\xf4\x86\x17\xf1\xd5\x18\xb6\xae\xf9.6\xb8\x85\x1a\x80\x87\x19\xea\x988\x8a\xd9*,\x0e;\x16\x86:\xce\xcd\x06\xb8]\xdfX9\xd6\xcd\x06O\xeb@:4\xccRI\xef\x13\x96\x1aB\x1d\xd6b!\xc9\x03\x00a\xb95\xd4\xc6[\x028\x9f\x01\x06=\xa5\x030\xd1\x0eX\xb7\x0cM\xb8\x03!\xacCexx\x8a\xd5\xbbPj\x0b\xf7\x08\x0e\xc3Cq\x0f1\xf3\x0b}\x10>\x1eb\xa9/\x04\x8c'\x0d\xad+\x93'V\x11Be\xf2\xc4\xea^h|8\xb0\xba\x19\x1a'\x0eZGI)XD\x0e\xf5E2]Du\x97\x8c\xa5\xb5\xb0z\x13L\xc7P\xb9\n&\x03\xb1\xdc \x92M\xb2\\!\x92\xed\xd278dx\xc5\x15\x8emJ\xe5[\x1c\x1b\x19jM\xdbr\x0e@\x1b\xa3\xddh\xb5\xf5!&W\xa1\xd1[\x1fbkZ\xb8\xa6\xce\xc8\x13:8-\xc1c6\xb5\x1e\x9dM\xb8#Y\xd8[\x98\xbb\xadG\xa1\x04\xfa\xe1@\x13w\"l\xac\xebX\x11\"\x9d\x18\x01\x16K\xec\xfam62|\xd0\n\xf0\xe7\xf5(\xab&\x95\xc7\x86\xc9_\x01.\x06\x81)\x7fQ\x06\xc5b\xda\x86b\xe3\x9d\x0d\xe5\x0c\xf7\xc4V\x9e\xa2\x08\x0e\xcclh\xadX&\xcc2\xd6\xa3\x8c\x86\xe2\xd8ZB\xf18\x14\xe1\xa3L\xb9B\x13I\\@\x8c/\xb4\xbd\xa2r\x87\xb6\x03\xc7N}\xbb\xf0\x10\xf4C\xac\xd9\x02\x0cr\x98c\xe3\xd5z\x94aO\x00r\xe8Q\x19\xe3\x0c`[\x19\xabG\x00\xa1\x15\xb2`\x0d\x8dS\xb0by1\xd5U\x05\xca\xc8c\x1dHY\xea\xb2\x0f\x95^\xac\xd6\x95+p\x06\x93\xd7\xf5(\xab\x93\x07\x9f\xfc+[sT(|\xf2\xd7\xb6\xadV\xa2\x00\xf6\xc8\x93\x10\x85\x04v\x18 \x01\xd6\xa9\x01\x06H\x805\x8f\xf5(\xdbL\xb8\xcb=\xf5\xd2\x0b\xb6\xf3\x95\xe0f\xad\x9e\xfc\x1b\xdb\xe4t\xb1\xea\xba>\xb4P\xac->\xe6I\xca\xcbD\x0fOG\x94\x92\x195\xcb\xc8IdlTHc\xa7EOA%\x8b\xe1Y\xa86\xe4\xc1\xd9\xce{*\xe7\xdb\x03+\xb6\x97K\x15\xcdYX\x84.\x18\x8b9C\x83\xd6\x01V\xcb\x15Mb\xd3\x97(Z\x8c\xedO(k7\x05\n\xb7\x1c\xa2#\x8b\"\xae\xcb\xb9\x07\xbb\x8e\x0d\xfa%x\xb1\xeb\xd4XQ*\x86v\x1d\x1b\x1aK%\x8b\xf3\xf4\x1f\xed\x0d\x96\x16\xea\xc75\xb3Ck\xf4\xc0\xc23\x8bn,\x93\x93\xc0\x82\xccXx\xa2,Qeg\xc4Z\xa4J\x15=Y\x86\x81\x99?\xd1\xd6\xe3\x1a\xa9@\x00\x9c P \xf1mPH\xcd\xf1\xf4o\xe9+\xb4\xa1\x8e\x80\xbbG\xa5\x810\x8e\x02\x1d\\\x88M\xc9!?}\xc7Z &Id\xcc4\x8f\x1b\x88\xb2\x02\xabI\xd6T\xd6\x93\xb4\xf4\x9b\xa9|;D\xc8\xd7qx\x9f\x10\x8b\x96\x81\x10;T\xa6\xbc\xd1h/\xe8yr\xaa\xe2\x96K\xc0d\xa8\xaeK\x9e/\xa7\x07\xbfRD\xb5C\x04\x0dy\xa5A\xec\xc3\xf2+1\x0f\xcb,\x9a\xbfG\xbfrH\xda\xf86\xbe\x13\x0es\x9d-\x96\xd8\xb3\xc7\xfa='\xcb.^^\xd6\xcf\x14\x12+\xd8e\xf3\x82!\xb1\x18\x8cM-B\xe6\xc6\xa6\x16Y\xc6\xb1N\xbbe\x19\xc7\x18\xf2\xcf\xd8 \x17t\xb8\n9\xbc\xe3\"\xfe\x1d\xdf\\\x85cm\xcbz\x1f\xdb\xe9\xc3\xb1\x8ee\xb0\xf5\x06. v\x88\xb9\xc4\xb7\x815\x0b{\x9f\xd0\xdd\xb1\xe1\n\x0f\xfe\x9d\xad\xa6~[\xf8?X\x80\xfb\xc6\xe8Oh\xda\xbe\xe6\x99\x04\x15\xf65\xcf\xb4B\x14W\xa3\xb0P\x9b\xc7\xf1\xd5\xe1\x86I\x11\x81\xef*\"\x03\xc1W\x81Q\xdd\xf3\x99\x91\xba\xac%\xeffn\xe8\xf4\x11XF\x894\x00kc*\\\x1b\xef=Dk\xff=\xd6\x89\xa2\xda\x1797\xf4\x9bM\x9f\xe1k\xed\xc8@05\x8a\xe0!\x98g\x1fa\x9a\x13\xe9\xd7\xce\xb0\x93V\xe4\xa5\x91\n{\xc2\x96\xdd\x8d\x15H\xbd\xf0\x19\xde\xff\x88+\x00Y\xf8\xbeZ\xc6G\xd8\x95iC\x1b\xfeI[\x1a\x80\x0f\xa6\nV\xff5\xde\xa9\x0d\x93\xc4\x824e \xd8\xa4\x1d\x81\xb1\xfdC\xcc\xba\"\x9d\xa8\xe7\x116\xc3DC\x81\xfd\x9fc9&\xaa{\xa112\xa6hl\x06\x8f\x02\xbd&d\xeb\x03\xf3(\xe1#\xec\xb4\x13\xe9\xc4\x12o\xd2Z0\x17,\xcbn(O\x98\xcf\xb0\n\x1bi\x006]o\x8c\xf8\xc0\xb1\xceR\x01~\x83\x19\xe8\x86\xf4\x8f\x90\xe9\xa7\xb1M3*@x\xef#%R=\xc2\x86\x9fhT\xfb.\xec\x861\x9e\xe2+\xd2\xc8@\xb0\n`\\)\xb1\xf1i#\xe6\xa1\xf5\xc5U|\xbdo\n\x16E\xb0_Z\x14sx\xf0\xf0\x11\x96\x11\x8c\xef%y\xc5vC\x0e\xeb1\xa1 N\xe2k\xbf\xc8(\x17\x04)\xc0\xb3\xf01\xa6\x14Q\xe2\x81\xb5\xe7mL\x8b$\x04R\x8a\xd8`2\x13\x17\x16>\xa2\xc4\x13\xb8\xff1A\xe4\xc4\x1f\xa8\xec$d#\x13\xf5b\"\xde\xc6(I\x83\x08D\xb9\xc7\xf8>7J$\xa9zLH\xb1\xfd%\xe1\x0d\xa3\\\x90\x01k\xc7\x0fB\x89u\x8a\xa4O\xc8.\x1a\x08!\x94\xeau\x8f\x07\xb8\xca\x86\x11\xf4\xf0\xf6F\x06\x82\xa9\xc8F\xe1s\x8bq\xb2p\xc7%\x8f\x1a\x03\xc8\x81zx\xa97T\xb6\x06\xb2\xd2\xea;\xd9\x9a\xb1\"q\xefbanc\xccu|\x11!2\x12\xa6\x82k\x9f\xfd\x19fe\x1a\xaa\xc2 \xff\x94\xac\xfb\x98'\x9bN\xc2\xc3l\xc8S\xb86\xfc3|\xd4\xb42\x85M\x06B\xd7\x13\xd8\x87\xe7Q\xd1\x01-\x95\x94\xb8\xf2\x14s\xfc\x92}\x82B\x94m\x02\x016\x9d\xc4<\xcfF\x81\xc0\xc61\xf9\x8b\xe13&}1O\\\xc91\xfe\x19\x05\xf82\x1f\xca\x0c\x05\x8c \xd6\xf3Mlt\xd6\x94\xe7\x01\x99>O2\x1eJ\x81\xecM\xac\x85lj\xfe\x8ayu\xac\x01XX\xde\x84\xa7\xd2\xb1\x96\x1b\xc3S\xe9\x98\x1c\xc7Cxu\x00\x1f\x8ax\xa8^q\xa6\xfeX\xf1P=\x17\xfd\x17\xf8&tS\xf6\x8c\xe9z,;\xc6\xfc.\xf63wX\x9b';\x86Q\xe1S\x12\x07N\x08\xef\xc7\x93\xa4i\x00\x82\x84jx\\\x02\x06i\xb7-\xd5$\xd1?j\xf9\xec(\xc6\xff\x11\x16\x92\x05\x104\x7f|\xb2\x04D\xd7\xc2\xa6\x04\x01\xf3\xa4\x9aE\xde\x81\x93 p\xf3#\xb8\x11\xe4\xe0\xd3\xfa\x18\x0bE\x9bA\x9e\xea\x87\xd9?\xc6h#\xaa\x8d\xc2:\x88:l\x1f\x11\x1c \xf24\xdb\x97c\xfc\x08\x8b\xeb\xf1\xc8\xd6\xdaf\x04\xc9\xa8\xc4\n\xcba\x92\xcc\x83\xb1\x90\xb9\xb4\xa1\x10c\xd9\xa6\xbe|\xc5bml\xa4\x04l\xbf\x8a\xa3\\>\xf6\xf81\xde\x95M\xb9\xecO0\xd3\x05S\xe4}\xcc\x0d\xe3DE\x18a\xc2nL\x94\xf7\xb1<\x1d\xc3[\xf5O\xc8y\xd0\x96K\xfa\xdd\xad\xe9\x9b\xbb\xa50&:\x02\xee\xaaw\x83\xad\xe3(\xdf\xb3\x90\xb6-\x97,5%\xaa\x96\xf6\xda^\n\xab4f2e\xe3\xab\x05T\x8e\xd4\xc2\xb2\x96\x84+;\xce\x13\xccu%P\x87Ya\xe9J\x00\xb5\xc5\x10\x0fh3Q\x16\xc37\xe9\x16i\x08>E\x12\x92\xdaq0\xd1Qht\xf8p\xc1j\x19z\xc3\xc0\xd5S\xed\x98\x02m\x96\x1ej'\xd4)\x89\xfaN\xa0\x04\x00\xac\xb3\x08\xa0V3\xde\xc5\xca\x94\x00\xa698\\\xbfKx\x87z\x7f\xed\x1e\x96D7\x93(\x8e\x12\x9dI\xed\x1e\xc6\xcc\x02\xac\x12\xb5\xe1\xfa\xa2a\xf0\x9b\xb7\x80\xea\xb6-N\xf2\x04\x04\x83\x07\x98en\x1a\xa1\x11\xdb\xc6bc\x91\xc6\x86\xc9Mx\x95\x87\xac\xbf\xfc\xfc\x1b,\x96\xc6y\xe8*\x13\x17\x06\xbd\xae9,&\xd7\xb75\x00\xef\xc8\xed\xbal\x8b\xafk:\x87\xcd\x13\xb7\x0d\x9d\xc3\xec\xe2\xb6\xc1\xd9\xb7\xb0\x80\xf9\xbaY\x15\xact\xdf6\xab\x82\xf9\xfc\xed\xdc\xc9x\x12\xfa*3\x01\xc9\x8c*\xe0z\xf4\x98\xeb\xea\xd8\x94\xd7l\xdf\x15\x91\xc2\x02\xd5\xeb\xbb\x1b;\x0b\xec\xdb\xado\xe3*Qf\xf9\x9c\x98\x84KX\x9b\xd0B\xec\xbd\xbf\xfd;\xcc{\xb6\x8c/5\xde\xa0\xc4@0\xc3I\x1c\x0f\x12\x90\xde\xc3;\x91\x94\xb34a\xfa\xb1\xa5c;1\x1a&\x1a\x80u\xf0\xc4\xa4U\xc2'S@\xe4\x94\x1ea^\x9f\x14 \x97hs*s\x12fo[Z\xd9\xc4R\x97\xb9\xfc\xa2\xfd\xab\x1a6\x00\x10\xbc\x0f0]KLR%:\xe6\"\xa9\x12\x19Bq\x97f\x81\xa8JX\x84J\x8atKXQL\x8atK\x18\xf1\x13\x93n\xe9\x03L\x0f\x92R\xba%\xac\xe9l\x99tK\xefc\xa4O\x8aLLX\xd2(]\x03\x92E7 \x97\xb0\xc2\x94\x14\xb9\x98(\xeae>\x10M\xac5IH\xa8\xfd\xe7q\xbd-\x93\x8d [\x18\x13\x03\xc1\x1c%1y\x9a0\x05HL\x9e&\xb2[:O\xd3]\x1b@\xd4\xb9A\x01*O\x13\xa6\x84I)O\x13\x16\xd3\x93R\x9e&<\xa3-\xe3\xa7\x8f\x15\xfb\xc4@0\x03\xdf2~\xfads\x0d\x04\xd3\xd6\xc4\xe4i\xc2\xc6\xb3\x04\xf24\xe15\xd8\x02\xcd\x91\xe0>8\xc3b\xad'\xd1y\x9a0kM\xbc\xc0\xa4\\\"\x87\xdf\xe4p\"\xf8V\xe4p\xa2 \x15\x17Jh\x19\xc8\xe9\x04?9\xf0t+@g\xc9%\xd4\x99;\x81\xc9\x92k\xab\x08\x88K\xc6\xc6A\xdey\x0f\xeb\xae[+\xe7\x05\x91\xc3|5\x81W\xfe\xf1g\x8b\xff\x0fvV\xd6E\xd03r5\xc5vcT\x90<\xb7\x9a\x14\x890\xb0=\")\x12a\x90\xe6U\x0eh\xb2BZ\x90 \xdd\xe8\xc4\x16\xf8\x16\xdb\x84'\x93\x17\x7f\x13\x9d\xd8\xe2\xa7\x04\xe7\x8a\xc4\x16\x98ln\xc98\xba\xcf\xb1\x8e\x95\xc8\xcf\xbf\xa1]DR+'\x8cX\xc6\x88\xe3|]\x18\x8bQ$9\xe6>\xc8}\x820\xa7\xaa\xf7\x84\xb5v%g\x17fTE\x89J\xd4\xfbO\xf1\xfd_\xd1\x91I\xda\x85\xe9\xbfl\xaa\x9c\xb5\x0b\x93\nY\x80\xa6\xed\xc2*\xb5*\x86\xf3v\xe1\xd3b\x8a\x95\x12wa\xb3\x16*\xa3\xf3\x0ea\xf1G\x16;W\x8b\xa7\xe5\x04V:\xc2\x95\"Z\xa9\x10\xf8\x06P\x8c\x13EP\xf6.\xeb:\x97\xf2\x80A)\xc2.D)\x9c{\x8bPf\x9ff\xd4\xb2.\xa2N\x97\x85em\x0d,\xb0\x13[F,\xcfr\x13Z(\x8a\xa0\x8cYx:\xc4\x17\xf1\x01\xa1\xceVG\xc4\xa6B\x85\xf7\x1a\x96\xdad1\x925\x0bK\x04\xaaTur\x98R\xa9B\xa5\xa4WX\x8b\xab\x94\xd0\xf8\x87\x05s\x94\xd3\x8c N \xae\x9b\xc0\xbak\x02\x87\xee\xd7D\x88\xf2\xd3\xea\x83\x8d\xa4\xa2I\xa6CP1\xd0\xe9 \x08\xfa\x05\x90\xf3\x81HQEf\x1bL\x0c\x93jf\x1b\x02\xd6\x81\x0cO \x933 d0WLL\x02\x19\xbc\xe8\x89I \x83iKbn\xd3\xb0&\xb8\xa5uQ\xc2\x95\x8d.J\x04\xde\"/ \x1duqGB\xf0/\xcaC\xaf\x94\xe0\xfe\x03\xac\xde'0\xc6\x8e\xe53\xdc\xf8>\"\x9a]\\r;$<\xc2d\x03!\x04\x19\x85\xf0\x90\xb3[d\xea\xc0\x06\xb5-};E\xebh]\x1b\xfb\xc6l)\xc9\x8b\xec}\xedw\x99\\\x83\x08\xd1&\xb9\x06\x16l\x93\"\xb9\x06\x01\x15\xa9)\x082\x17t \xc7ni\xdf\xc3\xf7\xb0\xa5\xab\xe4db\x81H\xc2zE:\xe2\xc5\x93\xf7d\xbc\xb5\xe8:\xf2a0\xefR\x88\xdc\xc9'd'G*\xaf<65\x08\x00\x84\xaa\xfd\x0d\xcd\x02\xb5\xbdqn\x07\xce*\xa9\x16\xf538\xadX\x9c\x01G\x9f\xe3\xf4\xab$\xe3\x1fb!_\x00\xd4E\x1aa!F\xf0\xc5rQj d\xc9bG]\xc1\xfe\x92\xa0\x99\x04\xe9w\xfd,\xd0\xc4z\xf0\xd3\xdbJ\x96x@\x98\x9f\x80\x80\xaf\xd1\x9f\xd3\xb5Ko\xab\xdc!\x0f\xb0\xb0,!P\xefg\x965\xbf\xad\xfcg\x88\xd4t[\x076`\xb5\xa7\x08\x94x@(\xce\xedR\xf8\x82\xb5^\xe1\xd7o\xab\x0b3 \xb4\xd4D_<\xc04P\x82L \\\x0dPuH\xebJK\xd9{\x98\xd5\x97^\xae'R@=\x08j\xe1g\xa8\xc8.\xd2p\xc0\x86\x02\x85R\x8f\x17\xcb\x16\x06\xd8X\xa4h\x8a\xb0\x11Yn7\xd4#\xa6\xf8\x93;p\x83L\x1e\xf2Oo\xe75\x80\xda\xeb\xa5msk\x89u\xc8\xd4hR\x98#\xa7\x0d\x02I\x03mJ35\xee\x87\x98jogp\xfa\x08 U\x80\xbf\xb0\x01d[\x7fAD\xc6,q\x04\x9f\xe6q\xea\x07r \x7f\x83\x95$]D9_as\\\x9a%\xd2\xeeE\xb2\xdfm\xc3\x01|H\xf0Z\x1dL\xc2r\xf3\x9e~\xb3\x9b\xa8\x0e&\x16\x89\x02\xe0d\x91\x19\xe7=\x9d\xaa\xe7)\xe1\xbayo\x94\x83\x07\xf3S\"[\xe7=\x90\xfa\x9fb\xbb\xa2\x80@_\x84\xc0\xe6=\xcdE\x9f`\xb2\x9c\xe6=\xc3E\xb1^Z\x1c#\xdb\x1a\x990*+H\x11\x05\xcb\xb4\xcb\x11T\xd6\x0e\x8b\xb3d\xaf\xad\x12\n\xdb\xa6 \xd0\xdbu\xeb\xa3\xfd\x1f\xb1-A\x80`\xd3\x9f\x12\xec\x11 \xc8\xf2F8\x86\n\xf6\xa2\xfaj\xee\x96]\x8f\xb0\xd6*\xc0e\xd7#\x8cL\xe5`_\xd2\xb6%\xd2\xb7\xa6\x04r=\xaa\xeb\xa5\x14\xe1k\x19\xa7\x0eY\xb3\x80\xca\xaeGD5\x15p\xedzD\xd4S\x01\xacUPs\xb7^\x0b\xcd\xdd\xe1\xce\xd0\xb1_Bm\xc3e\xd2=\xc2\xf7j\xbf\x83!\xf0\x97\x98\xb8n\xc3v?\xa4\x15\x80}\xd2\xd3\x1a\xcf \xf2\x82OO\x9a\xc7\xf3\xe2;\x91M\xf3\xf8\x84\xf8N\x84\xc7<\xd6\xe4\x05[ \x05H#(\x11XM\x84 \x05\x009\xa0\xd8\x1e\x1b\xd2\x83\x05\xb8j@w\x0d\xb08\xa0\x96\xa6\x87\xca7\xfcWXQ\x9405 |!\x9c\xe6\xb1I\xdbJOSl\xa8!\xa55\xb1\xa2\x86Dp\xcdcE\x0d)\x1d\x8855|J\xc45#\xed\xd8\xb6\xbfn]*b\x90eI\xca\xe1\x94V\xa8\xa6h\x96\xa1\x96)\x9ae\x8e\x9a\xa2\x11\x9e\x9e\xc7z\xad\x89\xc0!@@\xd1\x08\xbb/b\xd6\x88\x19\xc6\xc4\xacachjb\xd6\xac\x90\x9a\xbc\xd7\xe9~\xa8\x8d'D\xba\xb9\x03\x91S\x9f`=q\xc7\x113\xfaA\x86>gN2\x80\x9dy\x17Oh\xc7\x91!\x9aX\xaf\xc8\xe4\xe7\xdf`\xe4\xcf\x94\x9d\x9f\xf8\xea\xef\x18k\"i\xc9@\xb0\xa6\xb1cl\x80\xd8\xfe\x92\x19\x08\x96\xa9\x94zF+H\xdd\x0c#\xbf\xce\x9c\xfcclw\xcdx\xa0\xbcb\xdf\xc5\xeclG\xdb\x8b\xf0 \xcc4\x00\xdb\xcd\xb3!O\xf8I\xd1\xd8=\xb2,\x02\xd4\x8f@b'\xd0\xac\x11\xba3\xe4\xf0\x06*\xa6g\x99\x06`\xb6)\x01\xe9\xa1\xc0\xf7\xdf\xe0\xc3)ac;\xc4w\xf7J\x197\xf1A\x91\xf0:cJ5\x03\xe2[\xbf\xa2/\xf5gC?T\x9e\x8d\x98\xdeU\xb3\x1dbh6\xdcS\xb1\xbdtD\xf5\xe3\xb9\xb0\xb1\xb5.N\x066\xc7d\xc3(\x11X\xf8 \xe6\x1c\x86\xbb\x93\xb6t<\xce\xaf\xb1%\x1a\xa5\xdb\xc0\xc4\xce\x92k\x03\x8bq(\xd1\x06\x99\xa0\xba!\xf9\x84\xe0\xa0\x00\x80\xec\x8d\x15z\x00\x01\xc1\xf8\x88\xa0\xa8\x00\xc2\xbb\xb9XP\xc9\xea\x1e\xe0\xce\"\x0e>B\xd8n\x99\x81\xd7\xee\x03r\xd2\xa3\xb8\x07\xe7\xed],\xd0dQ\xac\xd3\x18\xe3\xa1\xed\x18\xdb\x06\xa6\xed\x99\x81`\xca! *d\xe3)6\x1bdQ\n\xc3\xc6rSVx_\x93\xa3\xb6\xb5\xb8,\x99\xe4\xdb\x84\xb0$\x0e\xec\x91\x05R\\\x9f\xbf\x87\x15.\x0d\xd4\xde\x0b\xefaA\x0d\xc7\xee\x93\xac\xea4t\x9f\xa4W\xd7E@F\xc6HJ\xe2\xfa\xc9\xa5\x9a%\xac\x9f\\\xafe\x89zU\xe5\xd9/\xb0IL_\xc9\xd9z6\xb6\xc1\x8f\xb0\xdc\xbb\x93\xf8q\xc0\x97\xeb\xe8\xb2\x80\xaa\x9a\x96\xe1\x02\xea\x7f\x88]\x06\xb3\xc4\xcf\xd4\xd6~\x84e\xa3,\x89\xf9\x1d\xe5F\xf5gx\x0fw\x8c-\x00k\xbe\x99\xb1\x05\x10\xa2\xa5nz0\xfb\xcf\xd4U\x0f\x96_v\xb4\xf9\x9f\xa0\xb7\xb6\xff\xe3E\xd81\xcf\x0f\xd0>4\x04_\xc0d\xfb>\\\x8c\xdc'\xdb\xb4\x1f\x0d\xb9\xe3U\xf3K\x12\xea\x08\x85\x90w\x13&1\xbb& \x1e\x1f\xba\xdc@\xf0~\xefj\xd1\x07\x8b*\xb9\x96\x960?\xcau\x0d\x0c\x10M\xe9\x00\xfb\x0f\xf0\xb6\xec\xf6\xd4\x93\xca\xf8\xa67W\x80\x7f\xc0s\xde\xed%\\\xc6y\x7f\x86\x97,7\x10L\x13wu\xb4>\xde\xb3\\\x030\xfe\xed\xc2\xa8\xb0\x1c\x93\xc3\x98\xf0\xa9\xcf=\xed:\x809\xc6\xae \xd6\xc7\x04<7\x10LZs\xe3\xca\x89M]y\xe1?\x88\xf9\xe1\xae\x16s\xb0\xd8\x91k\x00V\xd7vM\xc0<\x16as\x03\xc1\x879\xd7\x9e\x85da\x86N\x02\xeen\x98d\xe6& -\x1ern\xde\xc5\xc2\xdaJ.\xdf\xa7\x12\xa0w1\x95\xca\xcbOWY\x80*6\xe5]l\x1e\xcd\xcdC\x18X\xfc\xda\xd5\x11\xf2X\\\xcf5\x00\xbb\xedC\xb0\xed\xc7\x98\xc1\xee\x86\x9e\x8e\xa9\xc5\xef\xe5\x00\xc8\x84\xd4\xe2Ce\xc0:\xa6\x16\xd3sY\x00\x07\xd5\xe2{(c\x8a}\x88\xf1SBt\xb6\xff\x07\xf8\xa8\xed\xaad\x0b\x9fa\x0c\xc95\x00k\xf4\xbb\x86\xc5c\xcd-7\x10L\x04\x9b.\x1cw\xe3\xc2\xb9\x86\xd0\x95\x02f\xa9Wv\xda|\x1f\xdb\x8c\x15\xb8r'KOh\\\xbd\xb3\xc5\x8a\xc5n,\xa4\x81b|\x18\x9eW\xe1\x96\xfa\xd8+\x98\x9c\xeaX91\x9aw?\xc8\x19\xd2%\x8a\xa7\xa4\xc8a\x8ak\xb77\x8e\xf1[MX\x9b\x94E\xd0\xad1\x96awU\x08\x14^\xe4\\}\xc7\xeb*\xbe\x0fm\x15v\x8d\xc1\xfbs, \xe6\x85-\x9cn\x93v\xbf\xc4\x95$\xa4\x187mSa\x10x\x7fb\x99=O\x0c\xa9\xc1\xe7)/?\x02e\x01jRC\x16\\9\x19~F6Z\x03\xb0\xd8\x92k\x0f\xaa_`\x82\xbbkD\x1d\xc2?\x8c\xa8\x83U\xb7\xdc\xbc<\x84\xeb\xecj\xdd\xe83L\xbbr\x03\xc1\xf2w\xae\x9d\xbb0M\xca\x8d\x0b\x17\x96ps-\x0b\x90\xd5\xdeUy\n\x08\xe1V\xdf\xb1.\x97\xef\x1ba\xfd\x11\x96\x9d\xc6N8\x80;\xc8G\xb8\xb9\xb1\x934\\\xab\x8c\x9dD(\xce\xd2c\x01\xaf\xd0\xd8I\xc2H\xe8\xbe\xf0\x9a\x06\xc6\xc2\xb1\x93\xd4\\\xc6\x08\x88o\x0b:\x17\x80\xfa\xb8\xc6\xb1\x16\xa7,\xed%Vz\"\x00\xe0`\x8f\xe5\x86\xb1\x93\x18O\x0clR\x11\xb0\xea\x1d\x03\xbd\xd2-\x97Q7\x0d5\x85*\xa6\xbd\xe62\xca\xc0g-\xa4-\"\xc4\xb6!`H\xd3\"\xaf\x03\x97\xca\x18\xaaH\xfc\xa1/+\xcd\xfa)f\xe1c\xc53\x9e\xe2\x83 \x002\x8a\xef)>\x08\x97A$\xc4\xe4l\x0c\x9f\xf1\xf0\x8a$f\xb8\xeb\"\x87\x19\xee\xa1HaFFe\xea`]H\xb6&%\xaf\xa7\x98\xe3^V\x9e\x9c\xf8\xa6m\x0c\xdfI\xea\x991\xe7j\xb9\x1e`qx\xcc\xb9\xd2W\xb1\n1\xe6A\xe0\xc3\xbd\x02&w\x97y\xa2\xda{\x93\x1c\n\x0d\xfa\x11\xad\x93\xd5\xd5\xc8j\xca\x97\x13\x9bb\xb9T\xc3\xd5\x13\x17u\xd5\xb7y\xec$\x8e\xf2+\xff+,B\xebR\x85\xe5\x07#3}\x04\x04\x13\xe5\xcbZ\x0c\xc7\xc2\xf6X\x030\xee\x8e\xb5\xc4JQ\xdf\xe4\x8e\xb4dz\x1c\x9b\x9c\x8b\x96\x0c\x89\x97\x8dx\x86\x95\xf1\xb1\x81\x10:[\x1b\xef=6o\x17\x92sg\xd8\x16!R\x86ma\xc5z\\\xba\x01\xb6\x90\x8b\xd2-\xb0\x15j\xeeKj\xa0\xbc\x8eZ].\x0e\x17\xd6\x00\xc6w\xfc\xc1\x1dG\xb2\x82G\x18\xf1\xafh\xbfV\xcc\xfd\xf65\x00\xf3\x9d}\xee\xa9\xf3\xf0\x18+\x00W\xb8\x07Q\xbd\x0f\xf1\xe8\xf65\xe4\x1e\xde\x17 \x81C\x89qj\x9f\xfb*[\xcc\xdb\x18\x97\xafht\xc3\xf3\xd9\xd7\x00<\x9f+\x063\xb0\xa0\xb3o \x98\x94\xec\xdb;\xdfO\xac\xa7g?\xe1N6\xb4\x82\xae\x18D\xc2\x87`\xdf \x12\xd6A\x0e\x94'\xd4C\xcc\x04\x0f\xd4\xce<\xfb\x05\x16\xc0\x0e\x94\x13\x14\xd1\x9c\x0e<-\xfe\xe0k\xe67\xf4za\x9b\xc2\x81\x06\xe0\xfd?\xd0\x0f\xb5\x90\xb7o\x0f\xb4\x8eL\x9e\xbb}Cf#\xc06\x90\x03\xf9\x15\xab\x00\x07:\xbd$y\xcb\xf7@\xdfA\x927|\x0f\xd4\xf3d\xe4!\xdd\x03\xfd\xe2\x0bf\x05\x07:\x99\xe0Gx\xaf\xde0\xe8\x80\x95\xef\x03\x03\xc1,\xef\xa0\x88\x0d\xc1l\xea 2\xd6A\xb2\x91:<\x9d\xbc\xdc{\xa0}>\xc8\x83\xbdo\x18L\xc2\xc4\xea\xc0`\x12&\x8a\x07\xc6;\xee#l\x1f<0\n\xd7G\xf8\xb6\xed\xc0\x88\xcc\xa4\xa7q\x0dK>\xd8\xaf%\x00W\x8d\x8d\x0e\x93\xdfC\x03\xc1\xb8yu\x11\x84\x12\x8c\xe6\x87\x0e\xd8\xaf\xf0\xfe\\\xd5$\x0b/\xda\xa1\x06`\xbc\xbc\n\x1d`\xd9\xe6\x10\xda\xc7\xa4\xfd\x90\xcbdBX5\xbb\xaaO\n\x96\xdf\x0f5\x00\x8f\xe7\xea*\xf4\x8b\xef\xa2\x0f}\xe8\x18+\xadW\x0d\xe2a?\x9fC\x03\xc1D\xff\xaaA\x14L \x0f\x0d\xa2`JxU\xd9\x0b\xb1\x08t\xa8\x0c\x86\xa4<\xe8;\x9f\xe1\x83z\xa8\xf4 l\x00\xb8fBQ0\xc2\xdf1\x10LT\xae\x99\x1b\\\x8c\x1ew\x0c\x04\x93\x90k0\x0d\xbc\x8cw\xe03F\x82k\xea\xe5vL\"\xee\xa8\xef\x98\xa6\xdc\xe1\\?\xe2\x89\x19\xc65\x9eDW|/\x1b\xd6?\xa3vM]\x9fb\xc9\xf0\x8e\xfa\x8eq\xe5\x9a\n\x9b\xc6]\xdd\xd1\xc8E\xa6\xa3,\xfe\xa4\x030\xf8\xff=\xee\xe0\x8e?0!c\xf8l^\xd3ar\xf8\xb6\xed\x8e\xc1;|v\xae\x19\xbc\xc3D\xfa\x8e\xc1;|p\xef\xec\xdf\x92k\x85 \xd7\x9d\xfd\x10\x00\xef\xb6\xcc\xf7\xbb\xf2\xaf\xbb]\xd6\xcfC\xe9g\xda\xe6]\x96uY\xd8a\x7fd\n\xb5\xf2\x94\xb34K|7k\xbdj\xbe\x8e\x9d\x84%\xec\x0c\x0b\xdb'\xe7^\xe9T\xbb\x8a\xe4\xf7\xf9\xeftf\xf2\x90\xa7\xae\x13\xf3K^Q\x93\xcf\xf0\x838J\xb2\x94\x9d\xa9\xf6[\xeeTw\x11v\x99\xdfeN\x97\xe5\xec\x0c\xcb\xaa\xdd\x88\x9fh\x84\xcf\xc4Qz\xc99x\xb5\x02\xf5\xfb\xac\xfd\xf2,;sF\x14H\x13w\xc6\x1d:\xc9R\xe4\xf1\xc5\xac\x9dup_\xe2\xd7\x8f\x12\xd6\xce\x8e\x1e}\x95e\xec\xbb,}\xd5VF\xb7<\x07-\xb7Cfo\xbe\xc3\x12\x9e\xe5I\xc8\x8e\xcc\xbdZ\xdb\xc8\xcb\xf3\xb2\x91\xd0\x14v\xd8\x19\x96\xb4\xa36\xb4\x98\x06\xbe\xcb\xdb9;\xca\xe6\xc4\xeat:]v\xe4\x08\x9f\x89\x9d$\xe5\xc9\xcc\xd8 |\xcf\xc9\xf8\x9a\x1f\xee\xb5\x9d\x0e{\xe9%\xd6\x96+!\x16\n\xea\xf0\x99\xc0\x0f\xf7\x96\xa20\xe3a\xc6\xce\x88e<2\xdb\xb1\x8f\xe7\xb4\x1a\x8bhGV\x17K\xc0^\x13\x7f\x9fa\xf3l\x81eG\x8f\x92\x8aw\xc9\x173\xebo\xd5\x97\x93\xeb\xec\xb33lV\xad\xb4\xe8\xf3\xc4<;\xd2\xb4\xa0\xa2\xcc\x91v\xc8\xbe\xc7^\x11\x7f\x86\xec\xbbl\xeed\xe7\xd5\x0e\x19\x81XX\xebd:j.t\xfe\xfe\x83\xf4\xe8\xf1A\x97\xb5X\xab3\x93E\xf2\x0eg\xc9Iy\xfb\x85\xe0\xf0F\xef\x16w\xb3\x19\x8f\xf7\xfd\x90o&Q\xcc\x93\xec\xb0\x9duY\xeb\xe6M\x9e^\x8a\xbc<\xe0\xad.\xc1\xd6 \xe7\x0b\xec\xc8l1\x82N\x97\xc9V\x9c<\xc8\xca\xd3\xac\x99%\xc5\x147\x1a\xc5Q\xc8\xc3,]`\x8en\x89\"\xfb~\xe2\xc4K\xa5\xa2y}\xd14s2\xbe\x19\xe4\x03?L\x17jXA\x1as\xb7\x0e\xc6Tw\xdb<\x90\xb9&\xd2\x05\x96\xd0^\xf4/-J\xf9\xd6Bw\xedu\x9d<\x1b>\xc7\x08\xa2\xe7i;r\xd2\x13Mm;r\x8f\xd2\x05\x96\xd6\xcf+\xe1^\xeer\xd1\xb5[\xbf\xd4\xfaWZ\x84\xc0>P\xf2\xf5n\xcd)\xbcK\xe9l\xdc\x0e\xdb'\xe7\xe7;\x16\xc9\x14@'0\xc87\xa0\x93\x18$\x88W_\x82NaP\xaeA'H\xadT58\x7f\xe2e\x0c\nt_'\xc9\x08]\xdd\xe0\xc9\x13\x9d\xce\xab\xdf20}JX\xbf\x9e\x1c\x08\x02\xc6g\x8a\xc3\xc8^c\x9c\xd96Um\xce\x02\xe3u+j\xe98\xa6\x1d\x0b\x92Mz-\x88t\x95\xd4j\x0e\xfeGw)\xbb \xf3 `G\xce0N\xe59\xc9P$\xcfc~\xc8xG\x93\xa18\x89\xb2(;\x8c\xf9\xcc\xd0I7\xf6CM\x90f\\'\x08\x04Q\x0bA\xd6\xc9\xae\x877\x04S\xb9\x1e\xde@|N\x0d\xb3L\x8b\x04-,-\x02\xfbF\x90J?\xdd\xdew\x06\x03\x9e\xcc\x0b\x8e7\xe3\xa7\x1b\x8b\xdb'\xe4\x9f)O\xc6\xb7\x1b(\x82\x103y\x91\x942\xc5#KtY.\xddJ\xa4\xec\xaa\x93\xe6\xc7\x03&\"\x99\xb0\x90\x00\n\x17^l\xb1\x97{fz\xaek\xcd\x03\xcc\x9f9o0\xefp\xde\xa4=/2+vD\x00\x01 \"\x80$)Y\xd5}\xb0\x96\xad$\"\x10\xd7\x1d;\xf6}'a\x00\x9b*\xfaf\xe7\xbe\x92\x1bl\xbf\x0d\xf1\xed\xd6\x8e\x12\xc6}-\x8cW[\xd1\xde\x07]=\x1d\x13W\x0d\xd8;#\xc5\xe1U^\x10z\x91R\x1c_aP\xfc\xeb\xbb\x9c6\xa2&\xday_\xf6\xa6\x0b!\xdf\x16\xc7\xce\x1cz\xec\xcb\x85\xcdc\xa7\x851\xd5\xf8\xec\xa3\xcc\x94\xf7t\xc8\xb0/\x9fq\x03\xf4\xc5L\xd94s\xb7\x89\x85\xf1o E\xe3\xdf\x12\xfe\xc6\xbfk\xdc\xce\xfe\xac\xd0\xfe\xddLI,e\xffvUw\x8f\x91C\x1d\x82\x83)\x84\x13\xbcXn\x86\x7f\x95\xb8\x17\x87\xed\x85\xf9K\x1f\x89\x15F\xfe\x18\xcee=\xbd\xce=\xfb\xb9MP\x0c\xed6\x93\xc4_\xbf?=#\xe1\x9f\xa3\xe4IY,\x92,\xfc\x99\x18\x88\x8a\x9cR\xd1JZ\x9e\x96\x8c\x1e\xa8Hy\x05!\xe2+ \x91\xd2D\x88\xe4\x9f\x86\xd8\x16\xbf\xe8\x84#\x0d\xaan.\x95-\xee\xceP\x7f7k\x87.\x83}\x7f\xed6\xccvq\xab\x8c'\xdc\x01\xc2+>t\xdf{\x11\xe6\x85\xd3\x06\xfe\xeav#q\x91]\x1d\x92\xbf\xdb\x8e7O\xb2\x03\x7f\xb60\xcc\x0d\xa4[\x93\x1d\x06\xbe\xee\x0e\x1d\xc7\xd8Q3\xa2\x14R\x8a\xe9\xe6\xb1\xba\x14u\x0e\xd3\x91\xa6\x94\xe2\xdf\x92Q\x01\x94\x0d\xb1\x14g\xd8J(\xcb>\xb6P\xbe\x84bn\xfe\xc1c\x7f\xf6}D\xf7|\xd2\x04\x00m\xfdk\x0d\x03\x11#\x03\x92\x96\xf9\xc2\x8e\xc9\x05\xf8\x14\x81\xf3\x1b\xbd\xda\xd6_\xaeQ\x056\xf3\xe6aT\x90l\x00|@}\x88\x18FE\x91-Q\xd6\xbdv\x1cG\xc1v8.X\x8b\xa2H-\xfc\x14!\xd7\xf2\xd3\xf0\xcf\xe4J\xbc\xa1\x84\xc2\n\xc3/;\xfd\xd0>\xe2?\xc8\x7f\xadt\xe5*\x99\xbfJV@o\x8d\x8a\xad\xf2\"\x12\x9f\x15\x0b&2\x7f\x92e\xfe\x95\x9d\xc1c\x18\xc1>d\xb0\x01#\x98\xc0\xa6\xe3\".\x18=\x82\x10\xbe\x82\xec\x11\x84\xeb\xeb\x0e$\xd3\x90V8\x96[\x9b\x86\xc7\xdd\xcd\xa4}\xfaws\xd9\x97\x155\xe3\xd3\xcb=j1\x8b\xd3\xe2\x98\x92\x8b3\xbf\xb0\x13\x87r\x93mV3\xd1^\xff\xac\xe0\xf7\xbf\xff[\xf2\x8c\x9a\x9a\xbdK\xa1\x82\xdc\x06W\x1f\x0f\xe3\xebVe\x91\xef\x84\x8d\\\x99\x81\xbd3\xd6y \x03+\x13%\xf5\x86\xa1Z\xa7GB\xa0\xd5\xe4E\x1d\xde\xd6\xc8\xd7\xe6m\xbev\x18\xf1\xb2\x12\x8f\xe3\xf6*#\xccK[\xe1\x9fB\x89\x7f\xe2\n\xff\x14\x1c\xff\x14\x12\xfe\xc9\x18\xfe\xc9\xe0+(\x1eAF\xf1O<\xcd\xba\xf8'\xd3\xe0\x9f\x04Ug\xb7\xc6?\x127E\xf1\x8f\xdfB/1\xc59]\xd1\x8e\xe9\x88\xaf\x84\xd7?)+E>gV\xa9\x8b\x07\x99\x0e\xa2\xa3MH\xaa\xa2\xfb*N\x88\x15u\x98\xa4Z\xa9\xf1P\xaf\xd4\xd8T)5X\xd1H%\xcdcEz\xa5\xc6\xd6\xef\xab\xd4\x10\xbfd\x91\x7f\xb3\xa1\xa7~\x14\x9d\xfa\xb3\xf7\xf9\xa4&b\x9as\xf9\xb6(\xd2'\xa8\x88\x8b\xd4\x15\xde\x12Lc\xf5u\x12\\Mj\xfa\xbcY\xe7\x90a#\xad\xfa\x92\x97?M\xe2\xc2\x0f\xd1\xdfL\xa3\xbc\x94:;\x08B\xf4V\xc8\xd55_\xa7\x84%\xff\xa9\xfa\xd6(\xe9\x12Q\xf1E\x18\xbf\x9f@(j}\xe6\x87\xc3\xb7c\xbb\xab\x9fKxI\x07\x90C\xbc\xbe\xec\xd8\xa6p\x8cUF\x14l\x91\xa8XQ'\xf1\xd1A\xb4\xff.%\xa8\xf5B\xc0\xedr-\xb1\xb8\x18*ex\xb7\x0e7\x0cI\xc9\xec\x8d_,\xba\xe5LJbU@TA\xa6\xa5\xb0)\x0b\xe7`\xaf\x15\x95\x1e\xb0:\x03\x9cH\xe0\xe9ul+O}J\xf5\xd0\xdb\xc4\x05\xebU\x02\xd5$\xda\xcc4\x9d'SI-\xfd\xb4\xa6-z\x94@\xda\x8e\x83\xf0\xbc\x03e\xe2yO\xae&\x12c\"\x9ekW\xdf\xdcb\\\xcd\"\xc6\xeb\xaf=\xc8\\\xc7\xaa\xf1\x81Z_|\x91\x91\xb9\x10\x13\xecc[0\xb9\xd9\xf8A\xcc!W\x16_\xab\xc6\x17\x99XI\xba\x9b\xf2\x00\xa3jc\xe90\xd5\x8c-\xf0=\x9bUR\xaaa\x02\x83\n\xf7LZ\n\x0c\xf9\xd1q\xd3\xd0\xbf\xf3\xa5\x0b\n\xfe\x94\x98\xd6\x12pX\x13\x98\x99\xc5\x01\xb8\xe4Q\x8f\xc8\x00\xfd\x86,s\xa5%)\x16I\xd0\xdbV\x8a\xee1=\xa2\x15q\x9e\xe9=\xc3\xd8t\x17r\xba\xdd=\x12\x99(J.\x8e\xb2\xab\xe7\xc5\xeb\xb2\x98\xb4\x8d9\xe5\xe7Z!<\xd0\xbdo\xbfko\xe3\xb0C\xcb\x8eY\xfey\x194uo\xa3Pu\xe7\xd0\xcb\xc8\x0e\xc5\x9d\x13\xf6\xdf9\xe1\xe7}\xe7d5\xf1\xa1\xbbu\xa4*\xdf\xd3\x85\xeb\xd6\x0b\x07\xdfNX'\x9e\x87g\n\xa8/\xab\xfb\xabb \xba\x95\x98\xb1\xf8<\xee\x96D\xec\x0ee\x06\x84GW\xa9b\x9c3\xac\x12\xe6\x07\x97dV\x16\x8a\n\xf3\x9e+4\xc5\xf2$~\xba\xf0\xe33\xc5\xf7\x01\x82\x8d\xf5\xd2\xcf\xde\x07\xc9E\xac\x92?.X\x95e\x12\x90\xe8\xe0\xd2_\xa6\x11QU;g\xd5:\xb4\xa1\xaa\xee\x12\xb85q\xc1\xe4\x01\x01\xc9gY\x98\xd2\xad\xb7*]f\xf7\xb3\xb3\xd6g|\xe9\xf8'\xe4\x02\x12\xefu\x16\x90\x8c\x04/\xfd\xb4y\xce\xe9ZG\xb4\xda\x99\xf7\x9e\x08\xe1w\x98\xe5E\x9bu\xa3\x80v\x05{p\x86]\xa8\x90\xd6)\xec\x81\x95\xe0)fw\xd3U\xcd\xef\xa3\n\xdar\x81\xc9f\xdb\xb6?H\xa2\\\x19n2\xbc\xf5(\xeb\x1b\xce\xf0B\xba\x97\xcc\nRl\xe4EF\xfc%\xbf\x08\xe9$\x98\x91k\xe4\x85q@._\xcfm+\\\xfag\xe4\x1e[\x88N\xa1_\x06a\xa2+<\x0f\x03B\x0bu,\xf0 \xdb\xd6\xe7qZ\x16*m\x03\x9f\xcb\x0c\xf6\xeb\x0b\xae\x85DOt7\x1d\x93f[\xf3\x90b\xecK\xf3;\xc1\x0e\xa1\x82V\x98t\n\xb5\xa3)\\lL;(.'\xd0\x8f*/\xae\"b\xb2^\x07\xf4\x1a\x880\x98\x07\x1d\x9d\xb6b\xf72\x026F\xeb\xdf\xfe\xf5\x8f\x96\x90}\xdf\x14\x07\x81\x0e:NN\xf0p\xea:/]\x88(\xc0\xdf|\x85\x1a\xbdfI\xba\xc1O\xb8v\xba\xf6\x17\xfc^p,\xe7#L7 iFf~\xa1\xdb\x0b\xca\x95\x0b\xbcQ\xd5\xa4\x97\x82\xfc\xb7\xd8\x0d\xd3\xf8nw\x88dj\xb8w\x9c\x12\xe1\xec\x1a\xa9\xb0\x06+\xab\xabta\x1a\xf6<6\xf2\xfeA\x98\xa7~1[<\x8f\xc3\"\xf4\xa3\xef9\xcb\xaa`J\xc4\xc3n\xff (\xf8\x12\xf1H\x13\x9c\xa0\x9f\x94\x05\x1b`\xc1\xbaz\x01\xb4\xcd\xc8\x9c\xde\x04B}E\xcehs\x13\x06\x8a\xcf\xe7\xb0\x0f\x01L`\xae\xffhU*\x15\x18\xa5\x8azu\x83\xfd\x86z\xef\x9d\n\x1f(\xa5\x1dZC<\x18p\x07\xc9 \xb24\x9d\xfd@\x05'yRf32\x81es\x04\x86\x83\xb2P5\xd3\xbbW5K>\x01_\xc1p\xcb\xfc\xf8\x04\xcan\x0dr\x99\xfaq\xf0\x8c\xa4\xc5b\x02#\x85t@\xf0\xdbJ\x01\x9c\x80\xda+a\xb8\x83$\xac\x02\xf8jA\xd8\x9c \xc2d\xe2WQ\x9f\x13&z.\xe4\\w:3Y\xfb\xa3!\x12j M\xd5\x15\x90\xd58B\x96L#\x06\xec\xdd\x19\xe8]\xe9 \xefz\x8c\xa7\x15\xe9\xa2\xad\xd2\x90\xbc\xc5\x14\xeb\x95\xb0\xaf\xad\x9e\x18g\xcc\x89\x9d\xee\xed\x05B\x98\xc8\x996\xedh\xd2L\x12\x03VJn\xf8\x17\x0b\x8dW-\xfa\xaf~\xb2\x19\xff\xd4\xd4\x81\\\xc9zS\x818X=f\xaf\xf2\x83\"i!\x04Y\xdbCQd2\x87Z\xd1nY\xbd\x8a\xd1\xc2\xcb\xd3(,l\xeb\xc7\xd8r\x86)\xd3\x15\xad\xc4\xf0\x186a\x9f\x1b\xb3\x11X\x87\x91\xe3\xfd\x94\x84\xb1m\x81\xe5\xc0:\x14`V\xe0\xf2\xcat\x10\xeaM\xa3\xb8\xaa\xa5\xa9\xf5\xc5\x06\x8d\x1d&/\xfa\xe5z\xd8\xb6\xa8\xa8\xf3\xe6=q\xdc4,\xb4#\xafF\x91\xb2\xe5#\xef\n\xf6 \xc5\xb7\x9f\x1b\xf13S\x918 /\xe8\x908!/\xe8\x908>/Pz\xbb\xcfT$N\xce\x0b:*\xcf\x88\xdb\xe9\xd6c\x9d *gf\xa0rf\x9f\x9e\xca1;e\xf6P9x\xa5\xbb=\xc2\x90U\xa1'L\xce\x18\xd3\xd3k\x88M\x9f\xd0\xcbI\xc1\xbe\xaa\xd5Hx\x06\x14gY\xee\xe3{?\x0b\xfd\xd3\x88\xa0\xc8c\x85\x0e\x85R;\xec#\xc8bn\xb3^(\xfa\xd3\x7f\x951O\xfc2\xcbH\xcc\xbf4\xd3j\xd5\xa4\xcfH\xf1\xa4(\xb2\xf0\xb4,\x88m\x05~\xe1o\x9c\xf3>\xfb\xe8\xac\xe6\xc2\xa9\xaf\x06K,\x8d\x05{\xd5\x8d\x82\x91pb\x83\xa9\x0e3\xa66\xc68AZ9\xd1\x97\x9f\xfb\xd1\x04|e\xf1\xb5f\x8f\xabE\x1f\xb4\xa3\x8c\xe3\xc0\xddd_R.\x97\x04\xac\x85\x8e\xe9/\xef\x04\xcd\xdc:\xdc\x00\xfa\xafh\x90\x08\xb4\xbd7T\x9cE8\x8c\xb3\xa8\\\x8b\x9f\x85\xc1\xcb\xa4\x8c\xdb\xc9\xff\xe0\xa32\x19\xdcB^\x0d'\xa4 \xbcH\xf9\xd3\x96\xebcZ\x08%>#\xc7\xcb,\xb2\xfa/^\x15Y\xd7Z\x8b\x1f\xc2(zKf$<\xc7\xcb2\x1f\xb0&\xbd\xa7|\xc8\xa2\xc4\xb2sJ\xdf\xc9^\x15\x1f$\x955{\xe3+\xf5\xdaS\xba\xaf\x1eqk#\xd0\xb5\xab\xf9\xceD\xc4\xd1\x15@/\x19o\x1e\xc6\x81D\xfc\x0d\xa4\xfc\niwyl\xc5F\xdf\xda6LF{h\x8c\x11Vdl\x0b\xb0b\x15`\xe9\x1b\xb3CVO`\xc9\xdc\xaa<>\xa2\x96:zu\xfa7\xb1[\xf3\xc5o>|\x80\xac\xc7\xb0\x11$\xac\xd9n\xa2\xf7Cf\x92\xda_\x0fqj\xa1P\xb7Zz\xe6\x0e\xd4\x08\xb7\xa7Ha\xb31\xf4`\xdf\xa9\xf8\xc4\x8c\xd3\xee\xfc\x98\x0f\xdc7\xcd\xe9\x1e `9\x98\xcf\xc9\xac\x08\xcf\x89\xf8\xd2\x88E\xd0\xfb\xaa}\x92{\xd5\x1d\xb2k\x94|\x92MgW{\x82\x06\x1e5\xb3\x04\x87\xc7\x14\xf4\xf2\xf0g\x0d\n\xe4c\xceo*\x14\x91\xd5|\xc2\x13L\x0d\xd8\xae\xbe\x93\xc8?%\x91\xb1\x9bE\xb1\x8c\xbeA%\xf3\x8d;aa\xd1\x8c\xbd\xd4\xea\x03\x04\xf0&y\xad\xeb0fT 3\xb7k\xda\xa2\x98\x00\xa6o\xe1\x13&p\xeb3\xa0\xe6g[\x8693:C\\!W\xd7\x03\xa7\xdb\xa8\xa7\xb3G\xf6\x8a\x841N\x8e\x905\xf5\x00\x1374\xbe\x0b\x88\xa3\xb4LY\x90`\x83\x8eP\xb7A\xd6S^\x0b\xde\xbd}1\xb1\x0c]7Dg\xa1\x9d\xe1\x8c\xb4\xb5\x17\xdb\xb5d\x8b\xd3\x0c\xd2y5|\xd8\xb4s\xd2Wk\xd89\xf9\xab\xdd\xa9}\xe0\xd5c\x89\x03z\x7f\x0d\xf1\x98\xce\x1a\xda\x06\xd4~\x1bC\xea\xf1\xdb\x95\xc4\xe5\x12\xcd\x11ns\x8e\xe9\xd3\xe2\xe8z\xaf\xf9\xfa\xec\x13\x13\xcfkZ\x8e\xc6\x14V@\x050`\xbf\x06\xa2\x03\xa8\xe2?\x92`B/\xf3\xbd=Hl$\xa6\xfa\xa9\x1c\x86\x1a\xfa\xeb \x9cc\xacH\xb1\x87\x89\xfaq`\xa2\x9fm\x88\x96\xb8}\x93\xe5\xa6\xb5\x05\xb9T\xf1s\xf2\xc3G\xccW\xa2\xcf&\x0e\x86\x83\x83\xb9\x91.\x0c\x9a\x16D\xeb\xf0Q[Ctj\xf4\x88[\xeb\x05\xee\x13\xbb\xce\xf1\xed\xe7&v\x8dtb\xd7H'v\x8dtb\xd7H'v\x8dtb\xd7\x88\x89]\xebQEL\xc0\xaa\x12\xabF\x9f^\xac:\xbb\x8dXU\x12\xac(\xa4\xa7]\xad\xadVy\xdc\x92Z\xdeJy|+\x11\xcf\x9dr?}\xbcM1\xc4)F\x19\xe9\xa3\xa6Q4\xb7\xa5\xeb\xb5\x10\xb2\xa5\x98\x81I\xdbMk\x1f\xa1w\xee1+\xa4p~\xe5\xd8\xed:\x15\xd2\x17\xb0>GI8\x962\x0fE4\xe5a\xf3\xe8\xe3\x9d\xb9\x8b\xdb\x0fYX\x90\xd7qt\xd5\xc0\xbc\xedG\xa7\xabp%\xb0\x1f\x0c\x08\x83\xa1\xb7W\xcc\xc0\x80\x96\xe9\xee\xaa\xd3g\x02\xd9\x85\x1f\x07\x11y\xbd\xea\x88[\xa0;\x14\xd0(\x10\xdf\xfb)O\xe2{\xa1W\x90\xbc\xb0\x0b\x16\xc0^\xb6\x1d\xe0yf`2\xc8\xa6\x00VY\xbe\xf6\xe17m\xaf\xbc\x91vlX\xc1\"9;\x8b\xc8\xf3\xfc \x08\x8b\xaf\x93K0$\x99\x91\x1f\x19\xbf\xb2\xb1\x0f[y\xe9\xdb~\xb9W(F5\x815\x8c'\xc0\xfe2~\xa7\xb6\xc0\x84\x1e\x98\xc7\xa46\x9d\x08W\xf2#\x8fE\xe1|!\x9e\x0e\x82\xd6W\xe5\xa7A\xa3p\xa4\xc3\xea\x14t'w{f\x1bV\xb2\xa9\x80\x15\xf8o\xfa\x08\x05u\xe3\x16\xaa/\xf1\xc1*S\x1d\xf6[\xdd\x02\x02V\xb1\x82\x001\x85\x16\x9e\xe0\xb6\x04\xf5\xdf_~\xa9\x9e\xaa-Ur\\X\x93\x1a\xab\\N\x18\x11\xd8\xf8\xb3\xd2\xeb\x0f@\x0b2d\xae\x8e\xf1o\xbc\xd4\xcf\xc2\xe0]\x1a\xf8\x85.\x08\xc2M\xd7X\xa2\x11\xf8*\xcbo\xb4\xeb\xac\xda\xa5;\x9a\xb2V\x10\x05+\x1e\x86a\xeaxXA%\x0f\x15ie\x88\xb6\"?\x99P\x9f\x0f\x101A\xa5\x9f\x1fx?\x86\x98O\xce\xfa\xba,\n\xb3c#p\xba+\xb3\xad#rY<\xc9\x88\xd2\x15M~JV}\x11\x9e-\xa2\xf0lQ0\xb0\x9a\xf4T\xe1\xee\xab\x97\x9ef\\zz\x13W\xe0\x81\xd2\xd3\x94U\xcc\x0c\xa3@\xf2\xad\x8f\"\x1f\xaa\xf0\xd5SK\x91M\xcer!9\xee\xd9'\xc7\x85s\x13\xa3a-vk\xab\xe7*o^`\x19XS\xbfo\x99fC\xe6%b\x11\xa8\x82R\xf4\xcf\xe9\xc6c\xab|\x13\xf8\x94\xdfqH\x9bX\xb8Rz\xfe\xb4\x15\x01\x15,\x17\xce\xf1_\n\xa2\x06 \x83y8\xbd|\x1e\xacd\x17\x0b\x9ck 3\x12\xe0\xed&\"b\xf6~\xc5\x08\xa2\xfa\xe0\xf5\x7f\xd1q\xae\xe8\x91\xc7\x00\xdb\xbb\xbb\xdc\xbc7~\x9e_$Y\xb0\xf2\xe6\xfd\x11\x9fO\xb1w7\xdb\x0d\xbf,\x12z\xddG\xa4\xa0\xbb\x12\x93\x8b\x8d\x94\xcfu\xc0\xd7\xb1\x08\"8\xf8\x0b\x0ea+|q\xf3\xdd_\xe8\xfdkz\xc2z\x88\xa7\x07\xdd\xe7C\xf6\x85>\x84^\x9e\x83,\xe4\xa1\nf\xda[\xd5\xe0\"\xc8\x8a\x0dF\xf4\xda\x12\x11\xb6\xe4\x94\xf8\x19\xc9\xf8\xbdj\x82\xf7\xdf\xe9\xc6\xc3\xe1\xdd\xea\xca\xbb\xf1u\x87\xd7B\xf0\xd9]u7\xba\xe6\xee\xf6\x8ac\x16\x89\x16.\xcf\xe7\x86\"\x87_m\xab\"\x9c\xbb@6w\x81h\x86#\x99\x01\x08\xc6\xe8\x7fl\xda\xa9a\x08\x81,\xfb\xeb\xd4\x11\xab\x12\x0c\xf6\xfe\xed\xd1\xd1\x1b\xccLK\xe2\x82\xcbR'P\xc6y\x99\xa6IV\x90\x80IR\x08\xa5\x97\xac\xffh\xc1:\xa4\xb0N\x7f\xddN\xfc[\x0f\xaf\x16\x017W8\xed\xb3e\x919\xf6.{\xd1\x002\xb9)c4r\xc6\xab7-\x98\xf4\x1b\xcf\xb4\xab\xccLH_+D\x0b\xb5\x1e\xd5$3c33\xf1e\x95\x82\x92\xaf\x1d\xcf\xe9\xc3\xc4e\xfd\x02$w\xb3\x00\x9d\x99\xa8\xb2\x92\x1b\xb3\xbe\xd1;'O}J\xe3\xd6\xab\xa7\x96\x1e*s\x9d\xd1\x01\x9d\x99\x00\xca\xb4\x9cd\xc8r2Q\xbby9\xd9\xc5=h9\xd9\xeau\x86l\x17\xd5\xec\x15\x06\xb7\xf54\xe5\x15\x87\x9e\x94\xbf\xe2\x11\xa4E\xefT3\x96g\xbe\x17r\xe2\x95\xa7*\x0f\xdbp\xdbK\xd0\x90\xd5\xd0\xa0\x1fL\x15\xe9G\x0d0tM\xb4k\xa9r\xbc\xfa\xf4\x07q\x05LT-\xa7j\xe4\x03\x82\xc8\x19h;\xe5)T\xc7\xa9Q\x07\x8d\xcb\xebxn\xd2\xd5\xe17\x12\x08B\x87\xa0\xba\xbd\xfa\xf2ws\xf6MZY~\xfbp\x03\x85\x82\xde\xaaYGW\xa7\x06 \x96\xf7\x95R>k\xf1\x80$\xa1\xe7\xbc\x8d+u\xe5;pKo\xea\xa2\x11[p\xb8;t\xdb\xa1\xba\x9eT6(\xc2\x9b\xd6\xa3Z4\xa4*U\xef\xfe\x8d\xe2Yw\xe5J\xffhB\x83\xed-\xbd\xd4`\xab\xc3\xd3\x87UQ\xc7\xad\xd9\xaf\x8a\x1e\xe8d\x07\xdb[\x0fu\xd2\x83\xedme\x8ckV\xf4yX\xf2\xc9\xfb\xd9lHX\x8dHym\x9aSyR\x16\x8b\xe7\x05YJ\xb9\xc7\x9b\x15\xea\xec\x0c\x93ZR\xd0\xacR\xa7\xa26\xa6<%3\x1e\xb6\xd0\x9ba?\x98\x90\xeb\xeb\xab\xe7\x01\x89\x8b\xb0\xc0\xa06b\x08\x7f&W\xa8*\xc2\xbe;\x8db`mQ\xf5i\x12\xe7\xe5\x92\xe4?0\x01\xd1JB\xfb\xdea\x17\x8aa\x8b\x0eQX\xe0\xd8Ek\xd0\x9a\xe12_\xcf#\xfft\xd0\x00\x05\n\x97\xd2\xf2\xb1\xbc\x0f\xb0\x8f\xd1\xe0z-%\xea\x0f\xbf\x0f\xf3\x10\x85'k\x9bj*\x8d>\x14FN\xfd\xd9\xfb\xba\xb2:\x1c\x14\xa2QK\xd4^uP\xdd^\x0cCR\xcd\xc00(FO\xab\xd7\xde\xec\xc2\xa5\x98\xbbzT\xca5U\xf6\xa8A\x1f\xf0\xb9j9\xf4\xbb04z\x04\xd3n%\xf1Qv\x95\x94\x05:\x07\xeb+'\xbc2\xf3g\xee\xa9\x1cr\xbd\x99X{}M\x96\xe5\xd2\x8f\xa2\xe4\xe2(\xbbz^\xbc.\x0d\x96P,\x87e\xc1\xeb\x1d\xc4\xfei\xa4\"\xd5\xc4\x83\xf1\x1f\xbc\xb9A\x0b\x12\xad\x10\x0e#\xa8\xebb\x1ag}\xcd\x05\xd6\x1c\x18L\xf6\xbc\xaa\xdc\x1b\x1fv\xc9\xb6`H(\xd9\xb3\xaa\xea\x80!\\UZ\xce\x97\xa8\xc5\xd4\xd7<\xad\x06\xfb\xc6\xa8\x13=a\xdd\x0b\xad\x8e\xbe\xe2\x05\x86e\xaeQf\x8f\xc3\xd8\x01\xab. \xa5?\xd2\xc8%\xfb\x80\x07\x85;BZZ_\xfb\x90\xd5~Z\xa1\xca\x1e\x0f\xb0\xa7\xac\xfe\xdb\xdaM\xbc\xef\x8b\xf7\xb0\x07%\xa5m\x0c>\x7fO(Q\xe5\x859e\xbe\xf4\xb5^\xc3\x1e\x9c0\x16ArS7\xcd\xee\x0d\xec\xc1\xa9\x97G\xe1\x8cP\x9c\xb51rx\x82\xef\xc6\xf7F\xe5\xdf\x8dS\xad\x1a\xb4oZ\xcd\xcd\xc7\xe8\xacO\x05w'}\x0eP\xf5\xdd\xb8\x9f\xd5\x838T>~\x155\xd3\xcc\x1c\xac\xfdX# \x02\xc5l\xc3\x82,\xc1\x82u\x9e}\x8b\xd9\x93v\xae^\n\xf7\x96\x8f\xaa\x1b]2S\xc3\xca\xac\xa0\x13\x1c\xa6\x04\xd5\xf6\xc4#2W>F\xf5ZQv\x86\x1f\xba\x9a\x9er\x0c\xd9x?\xd1~J\x83\xf9h\xdb\xd9\"\xb9\xfe17\xb3F\xedR\xcce\x17\xcd\x9bu-\x1c\x98\x06J\x18\x0d\xa2\x14\x8b\x88\xa7A3\x193=6H1]r 9K\xb3\xf1\xb4\xdd\x02*\xe5\xf5\xaf\x1b\x1e\x10r=\xf4fI\x19\x17\xf6\xad\xceD\x0b\x1c#2\xa0cmg\"7\xcf\xb0\xee$\xc4\xb8zO\x14\xe7W\xa0\xa6\xaf\x96\x0d\xa8\xb3\x18<\xe2Y\x12\xc1,\x89N\xd8\x85\x03\x8d\xdd\x8aN\xd0IK7\x13\xeb\x15\xbap}\x8aq\xc8nO\xda\xe1<\x93}\xa3\x1c\xe3\xb8\x1a\x99\x94\x06\x99P\x82\x8c:%\x9f \xee7\x9fV]\xbd\xf4S/\xcc_\xfa)\xf3\x17R\xd8\x1f\xd2\xe7\xda\x0e\xa5\x8e\x07&o\xd2\xcd\xe7\xa2\xcf\x8fh\x1e\x1bc\x95@G\xcaj\x88ZB\x1fA\xc1O\xe0\x94\xd1\x80}\xd9\x84j\xb6g\x02\x06\xfe\x80>\x99\x7f\x81W\xe6\x04z\xe2T\xa4\xac\xd6\xa2F]?\x84\xc8\x82\xf8\xb5|\xc9\xbe\xc2\xf4%\xc6v\x98\xdb\x94\xec\x94h\xae\xdf\xcc\x04\xd4\xe7\xa3#\x7f!\xa4H\xf2\x97-QV\xff\xbaK\xb2t\x03\x07%jsNo\x02\xe7}\x8b)\xb8\xb7 \xf4\x04\xd7\xaeBEN\xe0\xbd\xb6\xa2.^h#;\x1c\x06\xd8\xbb\x0b,\x7f\x13\xe31m\xc7i}\xdd\xbfJ m\x90o0\x01\xcbj\xdc\x9bm\xb2\xe6\x8e\xee\xad\x8a\"\xab\xef.\xb8\xcbY\x1e\x1a\x07\":\x9f\xf0\xb0\xe2\x98Z\xb2K\xb8\x1a\x0e\x8a\x8c!\x14,c\x1f\xc1y]-\xf5\x13\xdb\xa1\xa4\xe2\xeb:t\xab\x9e9\xb8\x93\x95\xff\x87d/oJ\x0f\xd7\xe0}\x82w=\xa3\xda_\xd7r\x01\x8c7\x80; \xfd\xa9\xbd\x81\xb9$\x03#%\x1a \x83\xa6\x87\xb1\xae\xda\xa5iN\\\xe6y&\xe2\xfb>\xade4\xdc\xff\xe8\xccmk\x8a\xafL + y\xf2 \xf05\x10\xe9\x00\x1c\xef=\xb9\xc2\x1b\xdfH\xa8\xf3\x8b\xa1_\xd8/\x9e\xa5\x97\x93\xe2mg\x06\x03r\x1c\x8bh\xf8fd\x0dm\xdcn\xacmr\x0f\x1e\xc6\xfeI\xd1<\xf9\xd2m\xa0\x06Zw\xcaM@r\x93\x83t\x17\xb8\xf1\xa9\xd1,\xb7Blo\xf4+\xd2\x08\xfc\xf8zP\xbd\xef[\xe0\\\xbd3\x01s\x9d\xf8\xa1/\xf9\xaf|i\xaf\x06\xc1\x03\xdc\xdc\xb5\xa6T\xedG\xa85W\x9be?\x84\x03W0\xcck\xea\xdb\x8e)\x0f\x19C\xe3\n3D\x9d\x12\x0f'\xb5\xe5sY\x0dr\xc0\xa9\x84\xd5h)\xf1\xf0\xc3\x9c\xd0^\x9f\xc7L5\xd4\xfba_\xa4\x90\xc1\x88g\x95 ~Fh\xa7F\x97\xab_\x03Z|t\x03\x8bo\x95\xa5\xf7\xb9\xe8M\x1dD\xb6%\xa9\xe9\xcb\xb5\xd4\x12\x01\xf5Uoi\xb8\xba\xda\xcd\x86\xbe\xac\xab\x92\x95\x94\xdb\x13\x98\xd6!SZ\xf1h\xe9\xaa\x06\x06\x1b\xaf\xf3\xcf\xd0\xa8\xc6e\xa6\x0b\x1d\x03\x16\xcc)\x95\xc1\x1e$H\xecdM\xd3\x91\xccl:\xd2\xf4\x93k\x81\xac_[\xe8\x89W\xab\x98)\x0e4\x94SZ\x83\x85\x83\x84\x9a\xbaZ\\?\xadod\xe9G\xea$\xedyq\x15\x11\x9de)%\xfb\xcf\xb2\xa4\x8c\x83\xa7I\x84\x19\xdc\xff\x7f\x0f\x1e\x9e\xce7\xb7\xbb\xf7t\xeb\xe4\x19\xc6\x92fj\x19\x9dL\"\x9c3\x1bx\xab\xdd\xa8E\x17\xdf\x92O\xfegj\x0d\xd6\x03E\xd9\x10(\xd2\xd8K5\x0dj?\xcf\xe9\x07\xdax\x16\x81\xce\x18.\xd0\x19\xc3\x05:c\xb8@g\x0c\x17\xacf\x0c\x17\xa8\x8d\xe1\x82\xda\x18\xae\xebd\x93r\x0f\x81-\xa5\xb1[\xf0\xe9\x8d\xdd\xcc)\xfe$c7\x15\xed'\x19\xbd(L\xde:\x9e\xc2\x83M\xdbn\x95Q\xf8\xf31\xbf\xe93\xae)jO\xe0\x1es\x11JPO-t\xde\xd98M.\xadc\x03}O!L\xeb%\xcc\xd7i\x8d\xf9M\x88\xe0\xc2\"\xeeX\x9a\x91\x99_\x08i\x80\x1dsI\x8e\\\xc0.\xd7>U\xda0\x86\x8e\xcd\xa7n}\xe3\xc2\xcf\xe20>3\x89\xffE\xdd\x89uW|e\xec\xfd\x94\x84\xb1m\x81^\xe8\x91\xe8{J\xbd\x97t\x16\x1d\xfa\xf3\x97kW\x86\x01\xc3Pd\xb9\xb9\xc9\xb6\x88\xa4\x94#5d\x0b#\x97\xa9\x1f\x07\xcfX\xbd\xbaoOzO\xcf\x9b:\x01\xd4\xcd\x1c!\xfb\x1c \x19_\xa6\xbf\xb3\x16\x9f\xe75\xf4\xef\x0e\x1a\x9f\xad\x83\x86\xc15C\xaf\xa8\x890\x91c\x97\x89\x02~\x93\x87\xde<\xc9\x96\xbe\xa2_\xee\x92\xc1\x03\x9a\xab\xfd1\x84K\xd7\xda\xde\x1eD\x18\xd9\xfb4\x8c\xfd\xec\x8a\xbd\xc1\xecB\xd6\xa9\x9f\x93\xddm\xf1F\xef\xa9\xc1@_\xef\xd2\xa0\xf4\xe4\xe0\x01\x12\xe7\xa12\xdd\x90\x84\xeaJ\x1eS\n\xf6\xc1\n\xe3s?\n\x03\x8b\xc9\xe0\xbbm\x86E\xd4\xfc\xa2\xd4\xd4\\E$\x9a\xdbU\xcaK:\xda|\xba\xa9\x08\xd2\xaf\x90\x07\x04a\xce\xd9\xdc\xc2\x0b\xf3g\xfc\xaf\xe6a\xf8\xcch{\xb7\xca\xbd\xdfL\xef\x0duR~\xe1\xe8\x9e+\xde\xd5u3\x92\xa7I\x9c\x13I\xea\x01R\xa6\\\xcd\xebJ\xde\xc3\xdbnEN\xd2\xb9\xcb\xc6\xf6}\x05\xd6\xd3\"\xb7P\x8b\xdc\x8c\x84R\x15\xf0\xacP\x06<\x8b\xab\x80g\x94\x88\xccX\xc0\xb3\x0c\xbe\x82\xe2\x11d\xeb\xeb\x0e\xc4\xd3\xac\x19\xf0,\xd3\x07<\xab\x15\xf0&\x92\xadJzwx\x95\x17di;M\xdb\\\xfc\xeb\xbb\x9cN\xc7HW1Z\x96\xd9e:v\xc6r\xbf2j\x96\xad8?\xde\x0d^L<\xad\xdb\xf6\x0f\xdd_\x8a\x8d\x0c\xcd\xd1J\x854\xb6\x80}\xc0\xd4\x18\xcd\x06\xacc`\x81t\x9b/\x95x\x0e)\xd5\xe7\xb1\x1d\xf3\xec\x05-XW\xc0]kl\n\x03\x88V\xd3Sag\xfa\xcc/|\x8b}\xe22\x85\x03\xcbZr\x8c}\xb78YWw\x18\xee\xaa\xffn\xe3\xa6\x81\xa8N\xeb\xdd\x8d\xa4\xd3\xba~(j\x84\xd2?\x14q\x1eT\xae\xcc\x98\xb8\xa1\xbe\xf0\x84\x0f\xb3\xd6\xc9:\x91P\x9b\x9are~\x00Ul*\xc59\xc6\x80\xa2\xfb0\x0d\x11|;s\xc2\x98\xcf.\xc4\x02\x94\xf5\x15\x9a\xe7\x0bH\x94\x13\x15S\x8b\xbc\x96\xa6\x9d\xa2\xdb\x8ei\x1b\xb3a{\x93\x0f?\xc8\x9f\xc9\xa6\xc4C6\xc5\xbc#\x03\xb7#6n\xc7\n{\x11W\xaa\xb4\xcc{\x9dq\x17\xf5\xd4\xb1\x1d\xe5\xd6t.\xed!\xfb\xe3Br\xbb\x9d {w\xc6\xef\xdb\x99\x84\xc5\xddeq>\xf7k\x84\xe2\x9b6\x8a%#\x17\xa8G_M\xb5e\x08Mn\x9d\x82\xa8\xa7\x89G\x9de\xa3\xb4}\xa2\xbcrl\xdah\xac\xd9\xb6\x81\xb1\xbai\xeb\xa5\x97\x914\xf2g\xc4\x8e\xc9\x05\xbc%g\x07\x97\xa9m\xfdb\xc1:`D\xc6k\xcb\x05\xeb\xccr:*9\n\x11\xa5\x04\x1f\xf8\xf3\xf7\xa5+\x95\xca\x8e\xd2\x8e\xedqG\n\x1a\xf2\x92Q'4\x0fSX\x8c\xb7v\x95T]\xf9;\xb2\xac\x14\xfb\xfer\xed\xb6\xa5\x82\x99\x0b\xbe\xf7\xee\xcd\xb3'G\x07'\x87\x07/\x0e\x9e\x1e\x1d<;9}\xfd\xea\xe8\xe0\xd5\xd1\xc9\xd1\xdf\xde\xfc\xfbZ\xaa\x88\xe0\xd5\x16\xf5\xf0\xcd\xebW\x87\x07\xbf\xcf\xaa\xeadR\xaa\x98\xac=\xeb\x91\xb8\x10\xeaH\xf1U\x16\x84a\xaf\x93\xef\x9f\xbc}\xfe\xe4\xeb\x17\x07w{du$\xc4 \x0c\x16{\xef\x89\xc2\xa8\xc5\x17K\xad\x069 \xef)\xef\xfe\xcc\x85\xd0H\x11b\x05\xe3V\x94.\xf8\xcd\xf5\xcdnq%\xd72\x8fQ[\xbd\x97\xf0\xd7;\x0f\xa4\xfb6\xa1\xcb\x82y\xf4\x92\xec\xc0\x9f-l\xbdh\x01\xe9>\xef^\x18\x07\xe4\xd2\xfb)gr?-\xd5Gw4\xb1U1\"\x88G.\xd3$+\xf2)#\x80R?\x9f\xf9\xd1S?'\xdf\x84\x11\xa1\xdb\xe8\xd8\x85s\x8c\x1b#.\xd1}\xe9w\xdbAH\xba~\x07-\\loo\xefR\xb2H\x8c\x03\xd7eg\xb43\xe8k\xc3\xb2\x0b\x1b\x8d\xad\xb1L\xd0\xd4\x11\xbd\xecU\x0c5*Z#\x93\xa6W P\xdfd\xc92\xcc\x91r\x89\xed\xed\x9d\xfb\x8e\x0b\x87H\x91\xd7\xa65^^\xf8Y\x91\xff\x102\x0dIlo?\xd8\x1d4\xc3\xd8~8FM\xef\xc3\x07\x9dU\xda\xde\x19\xd6F\x1fpno?TB\xe7\xf6\x8e\xca\xc0%\xb6\xef\xb7_3b\xef\xfeHZ\xe9\xe6H\xc7[\xf7\x1d\x1b\x05n.X\xf8\xaf\xd5\x83\x87P\xbbt\x82\xd2;\x9b\x08'\xb3\x13\xda\xff\xa6\xf8\xe3=ES\xf5~\x18\x92x4T\xa6'\n!|\x15\xac\xe0Da\xd7\x18W\x85\xe1\xfa\xba\x12{\xac\x11\xdcTxL\x19\x94J\x9cm\xd7s\x10\xa2\xb9\xc4\x1e\xa1MzB\x0f\x9bE\x0f;\x8b\xd3\xc6\x8d\x0cYZ\xd9\xfa\x1d\x992\x99C\xec\xe2O\x89;\xbav\xab\xcah]\xf3D\x08*Q\xd7\xc0W:\xb3Y\x17\x0e\xfe\xac\xabg\xb6E\xe2\"\x0b\x890\x9co\xc3\x8f\xbc~\xf2F\xca\x0b\xac\x8e\xd0\xd8\xfb\xa5j\xaf\xf9*\xaaP\x17\x8b\xb9\xda\xdd\x93 \x89)\xdb\xb2f\xa6\xfdoy.F;\xeas\xf1\xb0\x1d\x95\x91\x1d\x8b\x87m\xc1\xb6\x8f\x9c\xc6#\xe9,\xeflb4\xf3\xd8\x1e=tl+,H\xe6\x17\x98CV\x0f\xbb|q(,\xd5\xb3k\xa1\x82>y\x1b\xa9\x11\x11\xc6\xef\xf6U:\x9e\x98\\\x16\x142Gn;u\x00\xed.\xc4\xb6)+\x0b\xcf\xaba\xaf\xb6\xdc\x12\xc2Q\xdf\x86[\xbb\xeau\xdd\xd5\xe2\x95\xedm\x07\xf6\x95\x9coHr\xe81@N\xecv\xa2\xa1Jk\x10\xbb\xb8y!\xaa\x07\x90\xda\xadT\x079S\x16\x94\xf0\x18\xf2G\x0ed\xde\xdc&\\\x182\xcd\xd7\xd7\x8f](\xa6q[\x08!\xa8\x8c\x9b.\xd8\xfd\x91\x9a|\x18\xa9!q{g[\xb3duw\x1a8\xab)\x0e\x96wFGQ\x94l%\xf4q-#$9\x84\xcaES U\xa3\x14\x1c#\x05iBI\x1cv\xa9\xc2\xda\x9e\xde\xb5\x117\xed\x11D\xf0\x18f\x8f\xf46\xc0\xb45\x9bne>\x9d\xad\xaf\x1f;\xb4\xcd\xd2\xa9\xcdU:\x1f2\xe1S\x7f\x970[_\xef\xe9\x16\xaf\x87\x19\x841\xe4Ho\xe4\xd3\xd91\x0b+\xea\xd4r\x0f\xac\xf2\xe1\x03j\xa2\xaak\xe5\xcb/a\xa3\x19\xbbhE\x1c'a\xb3]\xd5\xa9{\xe9\x17\x0bo\xe9_v\xc1\x88\x95\x84q\x1f \xe9\x11\xba\xcd\xb0\x0dq\x1c\xf8\n6a\x9f\x9e8X\xa7C\xdc\xa4\x97 C)7F\"\xea\xf9P\xac\xbds'\xc0\xaf\x83\xfc\x10\x83\xb8SHbD\x9eM k\x0d|\xb3#\xa2\xf3k\x8dPp\xc8\x0e\x88B+\xc1\xc6\x94\xe3\xda}\xf8\x009%/\"\x14\x87\xf1X\xb4\x9c\x9a\x9d\x80\x8dr8o\xb6\xf0\xb3\xa7I@\x9e\x14v\x8ek\xbe\xb33~\xb8K\xbf\x0d\xe11\xec\xecn\x8d\x1e\xb2\x86\xd6a\x84\xe0\x87\xb6\x04\xb6\xdf\xf9\x98V`\x0d\xecn\x8d\xb1s\x9f6p\x7fk{\x8b\xf7\xcf\xeacGt'a\xc2\xdf2/\xbd\xdc\xc5N\xc6\xb4\xcc\x87\x0d\xde\xcc:\x1d\xe7\x06\x1f\xd4W_\xc1h\xd3\x81u\xd8\xdd\xd9\xd9\xda\xbd\x1b\x08\xef\xdc\x1f\x1c vu\xd8\x90\x02\x8b\x83\x12e~\xa5\x0d\x8a*\xdc\xbd7\x90\x19\x13\x1f\xb6\xc4\xf0\xc5\"K.\x802\xef\x98%\x1dO\x80\x05a\x0eqR\x00R\x00\xa7\x11Y\xd3X~dv\xc1\xa2\xf0\x11g\xc5sB/\x81\x07\xc88\x8c\xb7\xb7\xf1\xdf\xed\xdd\x87\xec\xdf\xfb[\xec\xdf\x07\xfc\xfd\x83\x9d\x0eg\xb1\xbb\xe9\x08\xaefHg\xbd\x84\xd4\xaejgd\xd2(\x99\xc6\xf6\xe8\xbec[E\xc2N\xd5\x91\x7ff!\xdbi\xfdlQVn\x9d\x82\xfc\xda\x1eX\xd3\x04o{\xf8\xf9\xd8b\x0c\xd7\xfd-\xc7\xe6\x14@\xed\xc9\x00UCV?mU\xb5\x89\xe9j\x90l\xa7\x90i\x1dK\x1ah\x0c\xa94d-\xe4\x85\\\xa3\x1c\xfe\xa6\xc32\xac\xd8\xa3\xcdQ\xbf\x0d\xf5}:I\xb5(\x9f\xae\xe3\x03\x87Y\x1e:.X\xbe\xd2\xfe\x10\x83ik{i\xf7\xd6)l\x99\x088\x9e_\xaf\xc1\xa0\xf9KDK?\x11\xa2\xb8;0)\x0d\xbb4\xc4\xd5\xf8\xa8s\x0c\xd5z0Le#\x9d\xc3*\x02\xb6\xcdTG\x02$\xd8\x86d6\x13U\x89\xf3U\xf5\xa7\xd2\xb0\xe9\x1bE\x1e\xe5\xf5|\xf56\xd7>\xcep\xdb\xf8\xc6z\xea\xc7\xff\xb1\x80Y\x12\x9f\x93\xac\x00\x0e\xe9E\x02i\x16.\xc3\"<'\x8c\xcdZ\x95\x9a\xef;\xf3\xdb\xbbm\xc91\xc3\xc6\xe3\xed-%\xcd:RJ\x15Z\xec\xd3\x03\xc1>\xdd\xff\xef\x99}\xd2\xb0\xa5\xdb\xbb\xea\x95\x1dw\xc48>\xc7\xca\x94 }~p\xf2\xe6\xed\xeb\xa3\xd7\xed\x80\x15e\x9b\xdfo\x16\xb7\xc5\x01\x9d\xf58g\xb9+\x0b\xde\x15E\\\xe1<3D\xc6@+\x0c-5\x84$w\xe1\xa1S\x90\x17\x84y\x1a\xf9W\xf4v\x88\x93\x18\xf3E\xdb\xe3\x9d\x11\x9a\xf5\x938x\xba\x08\xa3\x00Y\xb7\xc2\xcb3\xcacX?\xf9\xe7>\xf3\xe9\x9dXU\x16J\xee\xfb\xf7C\x18\x07\xc9\x85\x17$3\x14\xa18^\x92\x92\xd8F\x18\xb9\xc8\xc2\x82\xd8\xd6W\xec\xd3\xc7\xa2\x8a\xf7\xcd\x1eC\xd1_\xfdx\x8f\x17\xa1j\xd7\x9bEI\x8e\xe9\x0ds<\xc1\xdf<\x82lc\xe3\x91\x03\x01\x89HA \xaf\x01i\x1aN\xb3c\xbdMYn\xb7`H\x8dI\xf9E\xc1,8)\x9dfD\xad\x889\x95tF\\F\x11J\x90)\x15g\x97-x'\x0ecpcrA\xf9\xbef1s\xff\x8aYZ^\x82\xa6g\x98\xd5\xc2qei\xab\x90p%v|+\x9a\x7f\xa46\x1e\xec\x9c\x08\x0e\xf9\xdb\x0f\xf4\x94\x1f\xbd\x98\xff{\x90\x1d\x8cF\x0f\xd4d\xf1\xb8\x8d\xa0\xb9\xf0`w\xd7\xb1\xd7\xda\x02\x075\xca\xb8\xc1\xfd\xce\x97\xa8\xe4\x84t\x17\x17\xe0\"u_Sfiz\xacX\xf3\x98\xf2\xd5\xa5\xc3\xa4\x04>\x8a\xf31%<^\x9b\x91\x88,\xa4\xf8\xf0\x11\x14BX\xcb\xf7\x03\xbf\xa3\xa8\x01w\x83\xb9\xa8\xfc\xa7\xd0\x8e\xb0\xb5\x0f\x1f\xea\xd6\xd4[\x14\xddt\x8b\x1e>\xd4\xac$\x83N\xdb\xfa\xd9r\xd0\xd5\x82\xd2\x81\xcf\xf3\x83\xb8\\2\xbe\xc1\x96`\x18L\xe6\xd1\x82\xd2=\xac\x93\x83\xd0s\x8d\xe6;y\x1a\x85\x85ma\x8e}\xde!\xb9\xf9 \xed@\x95\xd0ti.\xa7m\xdd\xdc{'\xd3\xe0\xd6\xff]T\xf5\xdf\x92\xa8J\x83\xb2\xb6w\xdb\xef\xc3\x01\x94\x8c__\x94\xd5\xc5e\xbcN\xcfH\xf1FT|=o^\xab\x1aX$\x02\x9d\x01fp\x0e\xf1dMQ\x1b\xad\xa2\xf0)\xa9\x90\xc4y\x91\x95\xb3\"\xc9\xd0\xe4 \xc28/\xfcx\xd6-\xddo\xfe-\xdd\xbe\x93\xe6g\x1c\x0f\xec\x83\xdf6\x00_q\xfdw\xb6nz&9\xfe\xc8V\x17XT\xf7'g\x1f(;P\xb1\x0c\x0f( \xcd\x98\xca-\xc7\x15\xde\xf0[\xfc\x82E\xc6\x80'\x8f\xb5G\x9bc\xc7\xe5>\xb5\x94Z\xc0\x83\x1b\xb5\xb8\x05\xf6\xaa!kp\xd1s6\x17\xba\xb3\xa0\x13m\xe1\xe9\xe1\xe1\xdb2\"/\xc2\\\x11\xec\xe0\xe9\xe1\xe1!%M\x9f\x91Y\xe4\xb3x\xd3\xdd\x80 O\x0f\x0f\xd1\x14\x817\xd1.\x8dB\x12\x17o\xc9\xacP\x97?{\xfd\xd2X\xc8\xe6\xa2->J\xde\x93X=\xf8g~\xe1\x1fe~\x9c\xcfI\xf6\xbc Ku\x1b\xdf\x84\x91f\xe4\xdf\x1e\xbd|\xf1$\x8a\x9e&Q\xc4\"P\xa9\xab\xf4\x95\x7f\x93dK\xee\x85\xa4\xae\xc0\x9c%\xb4U^\x92 \xf4\xd53|\x19. e\x89qs\xbb_\xbe\xf2\x97$x\x95\x04\xe4\xa5\x9f*J\x93@\xb3\xebo\xfc0\x16\xe1O\xd4K\xf3&*\xcfB\xc5|\xd9{\xcdp\x0e\xbf\xff\xd3\x0b\xbc\x8a\xd4m\x1e~\xff\xa7W\xe5\xf2\x94d\xda\xe27\x98%X\x03\x0b\xb4< c\xcd\x80\x0f\xbf\xff\x93 \x90\x0e\xbf\xff\x13\x83\x94$\xd3\x80\xc9!f\\\xfb\xba\x9c\xcf\xb5\x03\xa4\x07\xe5pAH\xa1^\xd5#rY\x1ce\xfe\xec\xfdS\xddQ\xa9jh\x8a\x93rV\xad]Ur\xed\xa2+zb\x07\x945a\x94\xf89|\x05\x0b\xc1s\xc2\xf9\xfa\xba\x8aZ]\xba\x18\xc9~1=W\x18\xbcQ&4\x98\x9e)JN\x91\xacW\x95\x9c\xc0\x1e\x9cR\xa4\x7f\xaa\xba\x90\x80_\xc5'H~\x9e\xd0\xfb\xf7\xc3\x07(\xed\x13\x17f.\xa4\x8e\x0b'\xd3y\xfdn\xee\xc2\x19E~\xd33\xca\x80\xa5.\xa8\xe2\xd2 r]\xd2[=s\xe0d\xba\xc4\xcfC\xfa\xf9\xd2\x85l\xba<\xae\xc5\x9b0\x14a\xf7\n\x804J\xcb\xed\xfbj\xbe\x03\x11w\xe3\xbd_Q\x94:&n\xbc\xbd\xfb\xefv%\xff8v%z\x82\xef\xbec[e\x9c\xcf\x92\x14\xbdU\xda$\\\"\xfc\xf5T\x07\xa6\x123@2\xcd\x8e\x99R`\xe7\x01\x1a\xaff.\xfc\xa2\x97\xf6u\x98\xfaiv<%\xf4\x18\xc9\xf6\xf0\xca\x99\xe8$\xfeF\xd8\xfb\x0c\xed\\\x84\xb1\xa9/(\xa9\xf1v[\xc2\x92W\xc4V\xe35\xa7\xb0\xc6\xaa\xb8%*\x8d\xcf\x9c5\xdf\x16\xd4\xb0p%\xf7\xb7[\xaf\x03\xdez\x1b\x85,8\ni\xd7?\xe7\xef\xdb\xf6\x10K\xd6\xebN\x1b\xb5\x9c\xf1\xf7[\x8e\x97\x93\xd6\xba_\xb1\xb6\x1elvb\xe1\x9dr`m\x8f\xea\x84\xb7\xd6\x1e\xd5\x05\x7f\xdf\x1e\xd5\x01R\x9a\x95\x8c\xbeYx\x89\x85i\x96\xccH\xde\xf2D?\xc4\"\xae\x98k\x16=\x85=\xb0\xf8Gx\xceg\xf6e\xab\xd7\xf7f\x89\xee\x13\xb4\xb0\xdd\x83So\xde,xM\x0f\xc4\x9aY\xda[dW\x1a\x9eW\xe0\xc8C/#y\x12\x9d\x13\xbb\xbdz\xf2\x83\x1e\x1aM\xf6g\x8f\x1ea\xa1\x1e\xccS2C\xfcr<(\x1b\x96x\x88\xfd\xde\x85\xf7z\xd6\xf7\xba\xcb\xd2\x83d\xc7\xf0\x14\xfdQU|\x1c\xdf\x8b\xb7\xe4'F\xd9\x1e\x9c\x93\xb8p\x98\x0fK\xb1 \xb1\xfd\xde\x919\xb4\xa2\xd3\xcd5\xcc\xfcb\xb6\x00\x9cCK\xf9\xd6\x06\xbf7\xbdsF\x15\xb5V\xa8\xbcf\xaf\xa5\xf4\xbb\xe6d*m\xb5\xcd\xe21\xd0a;8\x85\xe6h[\xe0r\xd4\x87\xed@\xe8\xb9\x88w\xa2\x95\x88\xd02\xc4\xb7\xea\x0d8\xe7\xb6\xcb\xc4;\x99\xa9k\\\xe95\xaa\xf2\xd3\xe0.\x89wr\xcex\xcb\x11`\x8c\x9a\x93\x9c\xb1\x97\x9b\x8c\xb5\xac\x05K}p\xc5\x85\x995\x02M`\x1f\n/y\x0f\x13(\xbc\xb9\x1f\xf6\x84@\x87*A\x14?\x1c\xfd\xd5#^\x9d\x02\\\x7fm\x9649H\x96~\x18\xab\x17P<\xfa\x13,?%\xa5?\x124\x1b\x19\xf3\xb5[PP\xf9 \x89)\xfck\x0fF\x8e+\xe2\xff\x94H\x81\xec\xa1I\xb5\x8d\x81*f\x1e\x89\x0b\x92\xd9\\\xa7P\xda\x19\xf2\xe8\x98\xa1\xd8#\x97aas\x06\x7fm\xd3au\xf6\xd0\x1b\x81\xdbX\xefCd\x1f\xd8\x16?w\x1b\xb3\x85\x1f\xc60\xbb\x9aE\xc4B\n\x08Ma\xde\xd8\x14\x82\xf7!d\xda\xd2\x18\xfdK\"Z\x9cc\xc9\x04\"[\x91\x1dP~\x1a\xe7\xb2wYp\xfck>\x9f\x1f\x9fDd\xf7\x84\xdf\xbc6\xe0#\x88k\xd9t\xf8\xc8\x01\xdf\x8e\xa7\xe1\xfaz[9 ?\xf4\x90\xa0\x90\xdc\xad\x8e\xd5\xc8\x05\xd42\xaf\x89}z\xa9\x1b\x93\"z\xe6\xb5\xe9\xf8\xbf\xec\xc5Egl\xf1s\x03\xfd,\x1eD[(\xc4\xe5f\xfbxB\xb5\x13\xa5[\xfc\xbc\xa3\x80\xa9J\xe7\x14\x08(|\xc0C\xe0\xf0\xa3c\xea\xed\xa7\xde\xdeV\x85_54\xca\x80U-\xfa\xb7l7,\x01S\x05\x87\xa9\xaa\x02\xdf.v\x0b\x9b\x92u\x0e\x00'\x01J\xf4L\x0d>\xfa\xc6\x9dz\xd5\xbbv\xc2T\x8er\xaa\xddu)\xbc\x93\x00\xaf\x10\xfcA1\xbd\xcb\xd6\xa0\xf0N.hA\xe1x'\x94\xa2\xa7d\x85wB/\xc81\xfe\xf2\xc5W\xccG\xfdd\xc6\xed\x0d\xe9Eqd\x17(\xc40\x8e\xfc\xed\xb0\x91\xbb\x15o\xaeV\xf5\xac\xc5\xdeI\xa0\x03\x86\xb8\x9e\x14*\xcd\xf9\x9c4\xd7\xaf\xf9\xda\xa5\x9d\xb1\x1b\xb0:X\xf5\xe5\x073\xb4\xec9\xa5\xa7\x19\x89\x87\x00\xc2\"'\xd1\\\x97?\x8f>\xb8\xceo\xd0\xbcj\x7f(\xf1\x04\x12\xaf\xde\x7f\x17\x9e\\L\xc0\x90l\xb1\xaa\x16h\xd3\xb2\x8aGC\x95\x8bg\x18\xc5\"\x0c(\xe9}\xfc\x16/\x98\x11\xde\xcd\xaf\xf8\xef\xbb$\x03^\xb1\xbe\xb2\xde\xc0\xdb\x86\x9b\xdf\xa1wL\x05\xfe1\x03\xff\x11\x85\xef\xd8\x855\xddx\x87\x8d\x93\x8f\xcf<\x91\x01\xfb\xd7\xb3w\xd7\xda\xf9w\xe7\xdd\"2\xea\x1d\x7f\x8dg\xfd\xd0x`\x17<\x82\xe7\xa1\x0b\xe2PX.X'\x0b\xcbq1\xd4\xa9\x0bY\x9d\xc5\xbau*\xd4\xe0Cl\x04\x13\xd6n\x05)\xe2\xcf\x16r1.\xfa\xabf\xfe\xec\xe6\x97\xd5_\xd7.\xbb\xc4\xf5\x93d\xd2>A\xd9\xb1\xbf\xe4\x9b\x97\xbd\xc9e f h?\xfc\xeb\xbcSy!Wf\x84b= \xa7i\xdeco?\x189\xf6\xa1l[\xdb\x1e\x1f\x89\x07\x84\xfa\x17\xac\xdc\x13{)v\xcd\x9cS\xfc=\xec)\xd9T\xa6\x7f\xc6\xb3A\x19\xacf\xad\x9a3G\xba\x97br\xce\xfd \x19C\xefb\xfe\xe7\xa4\xb5&\xb3*\x07U\xb5\xc6\"Y\xcc\x89\xdf.\xcbi\xd9\x11\x9f\xc7\x1a\x05\x93Xp(\xcd}n\x9e#\x04\x97\xbe(v\x92\xc5\"\x13!\x88q\xeaa\x88kG{\xe5\xd41\xb9\x80\xecQ\x17\xba\x04U\xc8n\\\xfa\x86\xdf(\xa8'}\x8b \xd5GNU\x84Z\xe6=v2\xb0D\x86\xe6SoNwy\x88\xb2\x98\xe0\xcdv\x88\xdb\x89?}JA\x93\x0b\x16\xf4m\x82\n\xf5\xc6$\xe7\xf6\xdc\xfb\x13\xac\xc3\xdc\xfb\x01\xff\xff\x0d\xfc\x11\xd6^\xb7\x01\xf2\x8d \x8a\x0e\x1b\x1f3\x13S[\xc6\x15\xdc\xfe}\xec\xd8\xf2+\xa6v\x90L\xe0Y\xc7\x87\x8d.%|\xd3\x9e\x1b]\x9e\xbeM\x16\x04\xd2\x13\x15f\x02I\xf4\xb4\xe9V\xdc\xbe\xc3\x14\x16j@\xeb\xacS=\\\xbb\xa4+\xbc\xf6\xda1\x8e\x1a\xf7\xbbo\xd8|T\x17v)\x0eG\xb5o\x870\x81>\\\xd7\x19\xda\x9a\xfd\x9a\xc9\xeb\xb7\x1fl\x99\xa2\x85\x1ez\xcc\xea\xd9\xc3\x13d\xbf\x97\xc1\xc24-?\x8a\xfa\xa6$\x93\xaa\xea[\x8fa-\x9d\xf1\x10\x8b\x86`\x14\xdf$\xbc\x8a^d\x13\x0e\xe7T\x05\x1e\x9d\x1a\"4\x03o\xd2\x90$\x1f\xb8~m\xa4\xa7\xb1\xce).\xa7\xd7\xc8p9\xeb9\x0f\xb6\x14\xae\xaf\xf7S\x80\xe8!a\xe8\x1f\x90\x98F\xcc\xcbP =\x9b\xeb\xebn--\xa3\x10\x81(r\xf8\x08\x01;\xa6\xa4E.\x88\xf4iy\xcc0\xdf\xc6\x062\x18\x99\x1d\xf7Q\x85Z\xa6\x198\x98KM)\xeb]\xeb\x8f|\xe8\xa1-Ub\x87\xde\xf9\xd0\x8b%\xf3g\xbdg\xf7\xae\x00]\x0f\xc5\xc9\nP\xbc:luw\xbd>v`\x90\xe6i\x93\x08jw a;\x90\xd9\x89i\x07$\x14\x84?o\xa4\"dB\xaf\xf6\xd4\x91\xc7\xb4\x1b\xb6]\x05\x8a\xed\xb9\xaasmo\x0f\x98\x84\x07\xc2\xb8f\x0dk\xa7\x8f\x18\xd6\xc1\x9a@\x18\xcf\x92,\xa3\xb7u\x18\x9f'34K\xd2\xb9\x9a\xdd\xdc\xbe\xb8\xa3\x02\x14z~\xb5;\xf7\xf6}\x95\x9f\xbc\xc2\x86\xbb\xe4f\x01m\xcdc\xce\x9bi\xdb\x02F,\xb0W\xe3\xdd\xac\xe5C\xc2u\x1c\xa6\xdd\x98\xbb\x90\xaa\x08\xa8\xc0\x85\x85\x0b\xe7\xae\xb0\x07Ia\xbf_2\xd4Y\\\xf1\\\xa30Ze\xff|\xc5|Fq E-p\xeb\xd4;E\x13\x96\x0e\xdc(I\xe6\xb3\x9b\xfa!\xa20\xd5>sT\xf3C\x9dJ\x802|a\x9d\xe0<\x82\x00\x1e\xc3\xe9#8\xd5Y\x9a\xa2\x95\xe9\x92\x07\x8c\xbd\xb2}\x9b2#dzz\xecL7\x8f]XLG\x18+\xf0\xca\xc6wN\xed\xa7\xba\xc4\x9f\xb3\xca\x0cu\xd9<\x8ej\x13X\xa6\xf7\xc1da\xdcq\xea\x11\xaca\x97\xe7^L.\x0b\xdbq\xbc \x89\x89\xc6\x1a\xb7\x1alb\x9f\xbbp\xe5\xc2\x82\x07\x82\x82b\xd8\xd0\xae\x1d\xef\xeb\xb7\x07O\xfeL\xc9ezq\xbd=8z\xf7\xf6\x15\xec\xc1l\xb5C\xb6\xd3o%-\xe07\xe90\x90JFW\xe0:\xd8\x87\xc2\xa6\xf7\x14.\x7f\xcc\x97\xbfh_\\\x15\xafk\x8c,I<\xd6\xacB\xe6\x87\xe0'\xe1\xaf\x90\xa1\xd8\xb0rhs\xdb\xfa\xc6?4\x7f\x0d^\xab\xae!QR\x1b\x99Hf\xa0M@7Y\x98\x0c3\x1f\xe1+*\xcd\x11\xaf\x11;cv3L\x8c\x87\x86W\xd3\xe4\x98\x0b\xf5n&:\x8d\x1c/a\x98\xc3NuY\xa1f\x0b?\xf3g\x05\xc9\x9e\xf9\x85?Q\xba\x94q\xfb\x9c\xde\x85H\xbd\xc0/\xd0j\x8aNe\xde\x03\xdfJ$\\\xf5\xa1\x9a\x85'\xde\xdc.\xd0TOA\xf0a\x82\xb4\x12\xb9\xe0\xaeK\n\xac\x1aX\xa5\x90\xe3M\x88\xa7u\x14nLo\x18\x89\xfc\xa4%U\xed\xde\x7f\x82Y\x9b\xde?\x9ef\xc7m,\x1br\x16\xae\xef\xec'M3y`\x13`,\xd4\xac\xd3q H\x04\xe3\xaaB:\x1d\x1c\xc5\xd3\x12t\xfc\x01\xb8\xf3C#t\\fg\xde\x1bX\x87\xcc{kP1\xcd\xc3\xd8\x8f\xa2\xab\xa1\xd2w\x9f+\x8d\x93*j0\xe5\x88\xc5\x1f\x1a\xd1{\xacSr\xab\x92\xd9\xb4\xd5\xc7\xb1,\xa7\xd4\x1ab\xf3\xcfJ\xcchj;m\xbd\x8a\x89\xcc\xeal\xb4\xfc\xa8\x8c\xcb(\xebF\xa9\x8b\x8f<.\x86`V\x1b\x96^u\xf9\x11\x81\xb7\xebP\"\x02\xf7l\xb7\xc0\xf1\xd0\x00\x88E6\x18\x08\xf1\"\\\x84\xb9\x01\xdcB\xa5}\xad\xd0J\xc7\x1eACwn\x0b0\xa9\x953\x8e\x1d\xa3\xd2\xa4_M=dAc{\xfb\xc1}\xae\xa5\x7f\xc0\xff}\xd8\x8cj\xc7\xc3co?\xe4Q\xed\x1e\x8a\xf7;\xfc_\xfe\xfdC\xfe\xfdC\xf6\xfd\x0e%G\xf0\xdf\x11\xffw\xcc\xff\xdd\xe2\xffn\xf3\x7fw\xf8\xbf\xbb\xfc\xdf\xfb\xfc\xdf\x07\xfc_\xde\xde\x88\xb77\xe2\xed\x8dx{#\xde\xdeh[\x19e\x8f9\xdb\x0eY\x8b^0\x1aw\xc2x\x87U\x90J\xbc\x92\x9f\xf2\x10\x8f]\x94(WJ\x02\x82\xfe\xc1-\xc8CD\x88\xe6\x04k\xcc\xd0}\x84\xf1V\xaa\xa0\x19Ul\x91\x0e\x82\x94\x1b\xed\x83\xd0:o\x9f+\xb4\xdc8\xe9n\n?_$\xed{\x0c\xbeVL\xc0\xa2\xc2\xed\xc1z\x9d\xc8\xcf\xc78; \xc5'\xa3\xd1h{4\x1a9\"v>C\x18o\xfd\xf8\x8c\xebH\nYG\xe2\x03\xa6\xb3\x84Y\x12\x10H\xe9dtv\x96\\i]\xc0W,\xba%\xecc4 \x0cy\xca\xa2_\xae\x83m\x17\xb0\xb1\xc7\xca\x1dx\xfc\x18\x10~\n\xf8\x0f0\xda\x1co\xc3:\x8b\x99\xd9\x9b1\x17$\xfc\xcb\xb3\x0c[\xb7\xc3a\xbd`\xa6\x8b\x1b4\xda\xdcR`+\x0dPd\xfe\xc5pP`\xb15\xbc\xcc\xbf\xe0LiX\xcbnM\xe0A\x81\xa7d`\x12\xc3c(\x1f9\xc0-\xb9x\xe4\xd6bZ\xae\xaf\x1f;\x18F\xe2+&kiV\xa8\xc1\xa6<6X\xab\xf9w\xb3\xf4\xea\xeb\x83\xe2\xacM\xc7\xb6\x8a,\\Z&\x85y\x9b\x9bV-\xaa`\x059\x15\xb2u\xbb\x01\xf7\xc2\xca\x8e&\xd6\xdf\xa6:\xbc\xd4\xf6\xc3\xf6{\xba}\xd6\xd4\x82u\xf0YD\xce\xaeXS$\xdb\xfa\xff\xd3Z%\xff\xcf\xfac\x9b/\x8a\xea\xaau\xa5/\xda\xb5f\x03\xb8o\x90\x85\x12\x8aT\xb2\xc0\xc7\x1d\x0e#S\x04k\xb2\xe6O\xc9\xb1\xcd\xbc\xf3~\xfb\xf5\xff\xf8\xb7\xff\xc2\xe2\x9d\xf2\x9fX\xa6l\xe3Zs\x8b\xd3\xb5I\x98;s\x89J\xbe9\x86\xe3\xed0\xca\x807\xfe\x97_\x82\x9dLcZ;GWnA\xfbR\x94_\xca\x07\xb9e\xf9\xd2Z\x809\xec\xc1\xcc\xa3\xb0\xda\xc7\xa0\x81\x04\x8er0eT\x05\x8e\x803\xef6\xe1jE\x96]-w\xc1\xc2\xbc\xeccM\x85HTh\x11\x1ej\xc1\x82Z\x0b+\x8fT\xaem\xfdX\xfc\x18\xffx\xfe\xe3\xfc\xc7\x0c\xfe\xed_\xff\xeb\xff\xf5\xeb\x7f\xfd\xd7\xff\xf3\xb7_\x7f\xfd\xed\xd7\xff\xfc\xdb\xaf\xff\xc3o\xbf\xfe\x8f\xbf\xfd\xfa?\xfd\xf6\xeb\x7f\xf9\xed\xd7\xff\xf9\xb7_\xff\x97\xdf~\xfd_\x7f\xfb\xf5\x7f\xfb\xed\xd7\xff\xfd\xb7_\xff\x9f\xdf\xfe\xf3\xff\xfd\xff\xfe\xfa\xeb\x8f\xe5xs\xfc\x00\xff\xff\xf0\xc7rN\xe6sk\xc8\x19\xbb!M9\xde\xde\xc1(n-vF\x8f\x91g\xe2\x8a~\xd2{I\x0b\xd5q\xafm\xf3 $r\xc3 \xea\x02\x8a\x8d:\xe1%(n\xb1,\x8f\xc4\x01\xe6_Q1x\x14\xc8\xe9\xa7[\x8em\x89z\x96\x81\xa6\x11u\xfaVJ\\_\xa1X*\x17\xe4\xf6\x95\xe76V\xdcg\xf0\x18F\xb0/\xa5#\x1e\x1d\xd7\x06\xcc\xcaV2\x96\xf1\xc7\x1c\xd3\xacl\xe9Iy\xee\x1b\x11\xf9\xddN\xd0\xe493 \x18~j\x0d\xbc\x82O\xc7\xcdM\xe1\xd1\x0f\xb3DM \xf7\xdc)a\x03\xeaK\xbbd6\x15\xf9\xef\x02O\xf7\xc7J\xde_\x06\x8d0\x9eEe\xc0\x82]\xe8@C\xd4\xe9\x03\x8d\n\xed\xff\xa7D\x02\x8e\xba\x07\x0fS;\xbd\xc6\x08\x91\xab\x80\xc3\xed\x0ecc\x99\x06\xe3\x8e\x8c\xa4\xc4/&x\x83\xef:+v\xd9\xb7_\xa3\x91\x96\xb6\xb8\xa9\xb4\xb8\x0e\xdcO\x99`\x05x\xa3\xc0E\x91\x89>\xe4\xf1P[\"S\xf48\xe5a\xfaC\xd8\xdb\x83\x11\xdc\x83M\x05Ca=M\xca\xb8\xa8\x1d\xb7br\xe6\x17\xe19is\x12\x0f/\xc9\xdd\x0f\xbd(>\xc9\xd8\x93\xb8\x98%\xd1\xc78\xb2\xb4i:|\xd1\xfc\xc7<\xb6\xb4\xaf<\xfc\x99|\xbcY\xf0\xd6?\xe6$\xc2\xc2\x8f\xc2Y\xbe\xd2\x1c\x86L!\xfc\x14\x80\xb42\xf2\x19\xb4\xfa\x88\xf6\x17\x19\x99\x7f\xe4\xa5\xcf\x97~\x14\xad4\xfc!\xa3\x17\xad~\xf4\xc5\xa7\xef\xdf\xaf\x06\xfc\x83\xc6/\x9a\xfd\xf8\x13(O\xef~\xf4\xe5'\xc1\xfey\x99~\x84\xa1\xa7w4\xf4\xd8\x1e\x8d)\xb9\xbc\xf4\x8b\xd9\xc2rad\xae.\x0dfZ\xd5S\x8a?\xd5k\"\x1e\xc1\x19\x10\x93\x921\x91e\x0f(z\xa8\xd2\x99\xc5\xd3B\x9f\x19C2\xafO`_\xd8\xe11/\xaa \x9a\xc0q)o\xecL\x8bc!\xc8\xcf:qA >\xbe\xe1jrQ\xa3\xe5\xc2\xf8\x06\xeb\x99)<4`\xd0\x92\x86}K\xea7\x964\x93\x974\x1b\xb8\xa4\x12?\x91a\\\xb3\x04W\x95\xbd\xe1k\x19:,N\xd3\xdd\xadhN\xfc\xec\xdf\x01\xf4\xee\x963\x8d\xc2B \x9e\x1d\x03K\xfd: \x0dGl\x8fw\xda\xbe& D!\xdd\xd7L\xef\x86J\xb4\xae\x90\xc4\x9a\xa1\xf1\x8a\xe5\x9f\x9e\xce,\x9ew\xe2\x9e}\xea\xfc\xf1\x9eC\x99\xe3\x0f\x1f`\x1bu\x1e\x05\xc9\x8b\xba|\x7f\xe2\xdcsac$\xc2:\xd1zc\xac\xe7\x9f\xca\xb5|lH\xaa\xc4\x1a\xf3\xea:\xde\xbeC\xffkT\x92\xcb\x1d[*\xa3\xdc;-\xaf\x8a\xbd\xfd\xaaP\x05r\xe7\xdc\xf7Y\x12\xa8\xde\xb3\x9d\xfd\xfd{\x1e\xb9$3\xdb\xb2\xe8\x1c\x15P3DO\x02\x92\xad\x9a\xd0]\xaa\xe3\x06@\xd3'gOx!\xf14<\x95%\\;\x95\x8a\xfc\xedZ\"\xa7_\xab\x83\xe8\xe1\xe8\xd4\x9f\x9d3K\xff\xdc\x85\x08\xc3T\xcfY8}\x93\x93z\xc0B}\x86gq\x92\x91\xa7>\xc6\xf6\xb3B\x0b&\xf4\xda\x83uZ\xb6,\xa3\"\x8c\xc2\x18\x8b\x96\x8d\xa22\x0eQ\x11\xbf\x0fV\xd9(\xc8\x8bp\xf6\xfe\x8a\xbe\xbf\xe2\xef\xf5CX\x98}\xe4\xcf\x9b\xbbY\xc0>l\x8f\x1fn?\xdc\xbd?~\xb8\x83\xe6\xfe\x8f\x1f?65\x80\xd1g\xeb\x03O\xbc\x1c\x83\xa3\xbb\x10\xc0:Xg:\xfb\x01\x94\xfea\xd0\x06t\x8e\x90Z`J\xce%o\x876\xf2\x85\xbd\xbf\xf6\xe3\x8f\xb9c\xb9\x10\xa84\xd4\xd5\x83\xfe\xeeK\x06\x8b<\xbe\xe7\x9amG\x18y\x0cE\xcd\xb0\x0e\xf9t\xf3\xb8\x82\xf0\xc7\x80\xf1\xd5\xec\x94\x07?\xe12\xa5\x85+>p\x1c\x17\xd6\xd0\xb6\xbf!\xf1\xc2\xa4!\x9b\xc7\x95F.s\xcd\xe4O\xe3\xc1\xa9\xcf1.\x01\xcc\xe1\xab\xae\xe4{\x03\xc6\x8f`\xbe\xbe\xee\xc8;S\x8b\xd8\xe6h\xe8k\xe3\x8f=\xa5D\xbc\xf1\\;nw\xf0|9\xbe\xaaC0\xa2]\x00s\x14J\xe9\x07l%F\x0e\xcf.!-\x1b\x8b1\x1f\xb9\x90V\xad\xee\xc1\xb9\xe3|\x00\xbec,\xa3O{\xfb\xe8\xa0\xeb\xc1\xc19\xecC\xca\xcb6]8\xc7O:#hY.3\x8f\x06kS\xa0F!\xd3\xdct\xa4\x15\xb3\x07a\xb6\xe6\xa5\xd9FW\xb0\x0f\xd3c\x98\x08\x1cT g\xdb\xdc\xa0Z\xcc-\xd1\x08\x1a\xa2\xeb\x06d\xd5\x8d\x08\x01\x89\xac\x8ak\xb2*\xeb\x90U\xb1\x8a\xac\xcaV\xa5\x03\xcc\xf2\xfa\xd4\x8e\xed\xedQ[\xec\x9c\x88\x92q\xbb$\x14%;\xed\x12\x9f\x97\x8c\xee?h\x17\x95\xbchgk\xb3]\x94\xf3\xa2\xadNO\x11/\xb9?\xden\x17\xcdz\x03\xf7U)\x98\x88wrB\xf2\x97IPFD\x97C\x14$\x99\xff/\nW\x10\x8c\xbb\xc7r\xe2\xe9B\x99\xd5\xf9\xdex\x0c\x86v\x8a!o\xe1\xe7\xaf/b\x91\xbe\xb5\nC\x17s\x95\x0d3\xb6 \xdd\x84oP\x83\x10&\xa6\xf3\xcb\xa8\xe0\xa1\x99\x9a\xa0A7e\xbb\xb3Ts\xae|q\x1e\xfd\xa1z/\x96\x0eR-\x8b\xdaY;\xcc\xf4<\x18Y\xa3.E\x92\xd6Y0\xde\xdd\xd9\xdd\x1c\x05-E\x1b\xbdv\xad-o\xf4\xc0\x1b\xb7J\xe8}j\x9d\xfa\xf1OI\xab\xe0\x8c\x16\x1c\xfa\x85\x0b\xe3\x1dxR\x9e\xc1xs\xf4\x006\xefOv\xc6\x93\xf1.\xfc\xe9\xe5\x91t\x10\x86\xe9\ns\xb1\xf4\xde9\xc9\xf20\x89s\xbc*;/?|\x80_\xae]E\x89\x97_\xf8gg${\x17*\x9d\x97x\xb5 (\x02\xdd\x9e\x85\xc5[r\x1e\xb2\xf2\x85\xb2\xfcY\x98\x15W\x13\x08\xba\x85\xa7e\x18\x05G\xe1\x92\xe4\x85\xbfL'p\xd6\xad\xb2\xf4g\x8b0&\x93v\x0c\x85.\x07Ph\x1d\xaf\x82dy\x12\x06,\xcf\x94\x1ao\x06\xc9\xf2U\x12\x10S\x95<%\xb3\x89\xde\x88*\x8b&J5,/\xccMMG\xfeUR\x16\x13\xb0\xbe\xf6s\xf2\x02\xff\xd0\xb4\x14$\xb3\x83\xcb\xd4\x8f\xd9r[Q\x98\xebj.\xfd\xcbg,\xf5( \x8e\xfc3c\xff\xf30*Hf\xaa\x81\xe6\xa4~\x91d\xefp\x9e\x8b\xa2H\xf3\xc9\xbd{IL)^\x01=^\x98\xdc\xab*j\x86\xc5|\x97r\xfdB\xce\xca\xbcH\x96\xfar\x9eO\xf5uJX\xea\xaa\xe7A7\xa9N\xab.\xcfz\xf4\xac\xd4%\xbb\xaa\xea\x13\x92\xbe\x08\xe3\xf7a|\xa6\xaf\x94\xb1\xd6\x9e\xc7\x05\xc9f$-\x92\xacOc[\x7f\xc9\xb0\x97\xb2\x82f\xba\x19\xc9\xd3$\xce\xc9'\xea._$\x17\xe8\xd3M\x02\xbejj\x073\xa8q\xeb\xcb$ \xd1[\x12\x07$\xc3u\xb3\xc8\xa5\xbfL#\xa2\x83`\xe9+\x04\xe5\xe0\x19I\x8b\xc5\x04\xb4{R\xd7\xcf\x87|@\xa7ppY\x10<#\xb9~\x1fi\xbd\xa7\xc9r\x99\xc4\x83j\x97)\xc5\xc3$8,O\x97a\xc1\xa2M\xe4\x13\x98Zg\x04\xd5.i\xc9\xfeIr\xfc\x97e\xd1\xa5\xbf\x92\x94nU\x8e\xfa\x01\xe2\x07X\x89\xcb8\xad\"\xf3g\xc4\xd20\x9eiFrR\xd0>\"\x81\xb0u51C\x17\xad\xa9\xa9\x10\xc6a\x11\xfa\xd1!\xddX\xfd\xd1\x9a\xc7\x86c\x99,\xd3$\xa6|\xcb\xa4\xed<\x05jp\xa2\xfc?%\xd3\xe7^\xeag99D\xb9Y'M p\x82\x89x\x1c\x057\xf1:OF\xac)\xa5X?\xe5\xdd\xf8b\x8d\x1c\x9b\xdeq\x05\xd2\xde\xb1\xa2\xb7+\xed5\x91_\xe5\x05Y\xaa\xc8\x08\xf1T\xd8+\xf5\xf8\xcfU\x0eW\xb5M\xa9\xc7\xf7V\x03kl\x9b\xda\xb3\xd2\x8eJ\\\x1ff~U\xd4J=\xf6K\xdd\xb7x\xc4\x95\x90z\xec\x97\xb6\xb2f\xaeP\xdf\x98\xc6~X\x1d\xdd\xc5)\x1e\xbc]S\xaf\xcc\"\xfd84;\x01\xa9'C\x7f\x97@V\xc4&\xe8\xfb\xa4\xa2\xa7O)=\xdd\xaa\xdd\xfa\xbbEZ\xdb\xa7HRK\xfdS\x15\x9a\x078`\xb2\xdc#\xa5\xc0\x86\xb0\x073\xc7\x85\x13/'\x05\x1bCn\x97\x8e\x0b\x17\x02;=\xc1\x99\xe7^\x94\xf8\x01 0\x8fI\x9d=\x9d6\xb5\x16\xd3CE\x7fZ \xf2\x84\x16KQ\xb0\xe9BX\x8f\xb2\xc4y3^p\xd3\x85\xa4S\"%|ck$:.\xd3\xc0/\xc8\xbb,\xb2-\x0b\x07\xd6-|\x91\xf8A\x18\x9fQ\xe8/s\xdb\xca\xcb\x19\x06~\xd1\xd4>L\xc9\xcc\xa6\x83\xc8:\x83\xc0d)\xcdo\x82\xe4\"\xa6s\x07\x0c\xea\xc1g\xaa\x1d\"\xd6\xe8\xf4+\xda\xe0\xc5\xe8\x81#6\xc0\x81\x0b/C\xd2\xa7\xde\x14\x17\xac'i\xaa\x93\x97V\x91J\xb0\xfeI\xa8\x0d\xcd\x0f\x1c0s9\xb2\xc6\xdfK\x92] \xf8\xab\x9b\xd0\x8bR\xab\xe1\xe5bXj4\xc9\xa3\x89P\xe0\xc0T8\xbceL\x06\xd0x\x89`\xf7\xe1\x03\xf04\x1e\"k\xc7\xe1\xfb0MI\x00YM\x07\xc6 \xfc\x0bk\xe5_ \xc9\xf07\xfd\xf8_\xe0\xc2\xcf\x11\xed\x87\xf3\x90\x04\xbau\xe2x\xe8\xa2\x8b\x18\xba\xe7\xeb\x92bB\x0e\xf2L\xa6\xc8~\xbf\xcb\"\xa5\xac\x0d\xe5\x98\x8dM\xee\xbc\xa0G\x9b\x9d\xa8\xaf\xaf\xdeq\xb0Y3\xd6\xf8\xf0\xc1\xd8\x82\xe2\xfa\xc6K\xed\xb2;\x1d\nlo\xc92)\x08\xfb^M\x81\xab\xd8\x90\xd4\xeb\xbeU}\xa9`)\xe8\xa7\x9d\xd7M\x1c\xec\xc2\x01fb\xb0\x8d\xf3\xbc\xa4\xd5\\\xb8\xa0\x87\xf1@r\x03\xba\x96\x91,\xe9\xa5E\x1c2\xe1\xd8\xde\x19=\xe88\xf0\x8ev\x1c\x8f\x8b\xfd\xde\x93\xab|HC\xf5\xcau\xac\xa0\x99\xb6\xf5\xe1\xae4\xe1\xd8\x1e\xef\xdcwx\xbaM\x03\x95\xd1631\xbb\xed4\xb3s\x03\xacnX\"/C\xb3\xa3J8\x18\xdb;\x9d\xc0\xb0\xb5pq\xd2\x9fb\xb3\xb3\x03\xdc\x83\x1b\x1d\xbe[\xfbp\x7f\xdb\xf1\xe6rL\x94!-\x0e\x9cD{\x9bn7\x89\x9d1\xf3\x07\x1f\xdd\xe7~\xe4c\xeeW>\xbe\xaf\x04\xaf\xc3\xab\xe5i\x12\x0di\xbb\xd7J_\x9d\x8e\xb7\x13\n\x83G\xe9m\xe7\xb2\xe4\x913\xda[\xca\x83\xf4\xee\xb4\x83\xf1\xf2\x19\x8c\xb7\x1d\xef\xcf\x07\x7fk\x96\xb1\xd4\xa1;\xed\xf1\x88\xcc\xa1\xed\x011\x81\xf6\xc3vX\xa1\x94{\x87\xb4\x8d\x13x\xea\xd0\xb6O\xc2\xa2\x82\x94\xe6\xfbs\xfe^\x9d9tg\xdc\xae/2\x87\xb6'\xcc\xb2\x86n\xb5G\xc3R\x86\x8e\xdb\xb5Y\xc6\xd0N\xdc\x87\x0b\xbe\x9a\xed\xb9\x1e\xb0%h\x8f\xf1\x92Wo\xcf\xf5\x90\x8f\xbd]\xff)\x1bL'X\xca{\xb6\xe5\xed\xd7O\x04Bj\xbe~\x0d{\xf0\xb4\x9d$\xf4\x0d\xec\xc1\xfb\xf6\xcb#\xcc\xfb\xd9z\xf9\x12/\x08\x06\xd7\xcd\x92\xe7\xd5\xd5\xd1|\xff\x13\xec\xc1sJ.<\xafQz\xb3\x06\xbd`\x02\xdb:Y\x84A@\xe2\xb6\xca\xff-+-\x927Y\xb8\x0c\x99\xbfM\xb3\xc63\xd4\x03y)g(\x9f\xe7\x07q\xb9d!\x91\x9b\x15_\xd0\x1b\xd2\xb6r\x1c\xfd\x06c\x05\xb3\xabvs\xef\xe4Z\x9dd\xc6\x7fg\xa5I\xba\xa1\xa9\xf0\x0d\xecu\xb4I\xcd\x1a?\xeb\x02\xc2\xbcl\xd6\xfb\x1aW\xf4/\xac\xb1f\xd1\xf7\xb0\x07k_cf\x88\xaf\xa5\x8c/\xad\xbf\xbdy\x18\x07O\x17a\xd4R4|\x0b<\x82odvr\xe6w\xce}X\xdb\x83K\xfb\x0d\xf2fh\xd7\xab&\xd0\x87\xc5\xd8\x82\xba\xe17\xb2\xad\xb0Y*\xc2\x93,\xdf\xd7V\xbav\xbcn\xd0#P\x8aA\xae\x9dv\xddkG\x0eg\xa3\xb1]\x03 !\xbf\xb6\xbfQ\x9b\xd3d\x92\xac\xe2\x9biq\xec\xc2\x9b\xaa=\x1e\x10\x92 \xb7\xf9\x0d\xfd\xf9\x06\x9b\xe9\x04\xc0\xbf\x86 \xbcin\xd9\x0f\xbd|\xbb\xe0\xd9\xdf1\xaf\xf1K\xfbe\x0d\x08&\x1d%fL\xef\xaa'\x9b\xdd\x7f\x07{\xf032\xc5\x0c\xea\x1bP\xeb\x89\x9b\xbb\xb1\x88\x06\x80R4B:\x0b0\xa8\xa5F\x94\xfd\x97\xa6\x19\xfcm`l\x80\xaa\xe1=\xb1I\x7f\xb3\xff^m\xe0\x15\xcb\xe2\x02{p\xc13\xd6\xd1w\xb4$\xb1\xdf\xa1\x91\xc4>\xc6\xd7\xa9\x10\x10f\\\xa5\xfd\xbdby\x85\xa7\xaf\x8e\xa7\x053s\x11\xbf\xf7x\x0e\"\xdc\xb4Xw\x10\xea&)\x17\xb1\x89\x89\x8bT\x90\x0d\x93\xba\xc3\x0f\x1f\x18\xf4\xbdr\xe1\xc0\x1ea6uJ\xa6\xd4\xfd\xd2\xe1\x7f[\xad\x06\xfd\xb6\x86V\xd3b\xfey\x88q\xc8\x95\xd2\xf5\xad\xd6\xbc\xb3\xe0\x1fK\x9e\xe8\xb3\xa0CKXj+\x16e\x97IP\x98\x1fe\xf2\xc8\x81\xbf\xa1\xfe\x1d\xc3\x05&\x18\x06\xa60j\xdf\x8d)7\xfe4\xf88=k\x18\xaf\xe0\xc6\x13\x96\xaaP\xdb\xf3\x1a\xd6\xae\x01\x08A\x83\xe5\xf7\\K(0\x11f\xc1e\xaf\xd9\x05\xa2\xec\xda\x17\x9f\xff\xf9N\xfc\x16%\x0cz\xe8o\xbay\xe4\x18\x0b\xdbv4\xcd)~1d\x8f\x98\xdd\x05]\xff.\\\x0b)\x11\x89\xa9\x9e\x94\xff\xc8\x11{\x82\x87\xcd\x17\xb3\x8a9\x04\x7f#v+dSz7-\x0c\xe70l\xce\xaa\xae\xf73nmi\xdb/M\x81\x0d1\x08\x14=N2\xa2\xef&\xc4\xb0\x18IZ\x87{\x92\x92\xd0w\xf2b\x9c\xf3\x8cj\xa9\xca\xebw\xb3\xe1\xf5\xbb)\xf9\xe6\xbb\x9d)6\"B*\xaf\x13\xe0Y\xdajl\xc0SZ\xfe\x9d](\xcd\x03\xce\xfe\x9a\xbe:\x16\xf8\xc2\xae\x8f\xbc\xb8'\xbe\xad\x0d\xe9\x10\xa9\xab\xd2\x1d]+\xa5|H\xf2}O\xff\xf7-\xdd\xc3N.@\x18\x14I5\xa7T^\x8bXp\\\xf8\xa1\x99\xeeM\xce8h\x15I\xe5\xe3\xdd'\x04)0C\xdf\xfb?\xc8M?\xc5\xa4t_\xb8\x94E\x81=\xf8\x1bF\x90\xdby\xe8\xe0_\x87\xf8\xff\x7fF\xae|\xbc\xc3\xde\xfd\x89\xf1\xe8\xbb\xec\xaf\xbf\xf2\xfc\xc6k\x94\xdf\xdc\xc6e-\xe9\xfc-\x15\xc3`\xb9\xf4kD0\x0b\xfc\xbaWR\xf5\x83\x1d4$2t\xc4\xbe\xedc\xaa;\x1fS\xdd\xf9,[\xda\xcf\xed\xf5f ;\x91\xe8\x16Y\\V\x1d\xe7\xbfPva\xe1\xe7\xcf\xf9\x01p\xc3\xfci\x12\xcf\xfc\xe20\xcd\x88\x1f \x9b#(0\x17\x9d\x85\\n\xbd\xeb2\xd7\x0c\x97\x07\xe8u\xd1\xde\xd3\x958)W\xec\xcc\x91\x7f\xe6\x96q>KR\xda\\.LC-\xd7\xa2\x17\x01a8\xe2/\xf5!!\xe4\x91\x03\x81\xfd\x97)!\xcd\xb4\xe65\x12\"\x98\x8f*\xf0\xf2\"\xc9\xe8\xe5\x12\xf3V\nR7\x13\xd3f\xce\xed\x82L\xe3V;t\x05\x0f\x1bk\xc7Ox7B]\xbf\xfdG%;{Ao\xb5\xf5=\xb47\xdf\x87\x17\xf4TM\xd8?{\xdd\xe4\xea-\x04\xfc\x9e\\}\xd3\xdf\x15Z\xe0\x7f\x87\x16\xf8\xc6\x9c=>0\x1a\xb8\x83\x9b\xa0\x19<-\x8c\xe1\x85ZCA{z\x81t\xdc\x9e\x9c\xba\xc3H\xc6\x9799$\x05\xaa\xb1\x8d|\xda\xf7\xaa\xf0\xc0\x9d\x96\xc2e\x1a\x91!-5\x93\xcd^w\x8eJk\xa3\x19\xc3\xdb\x8dq\x84A\xd4\x07$+\xedZ%\x17\xb0\x0f\x976\xa6\xa5\xfc\xb3}\xc9h\x1d\xe3f\x07d\x1e\xc6D\xa8\xa8'\xf07CqH\xf2 \xfc\xb9Y\xe1\x8c\x14\x92\x8a\xfb\x19\xc9gY\xc8\xd4\n_\x98*\xbe\xf2\x97\xb4\xb1\x7f6\xd5a\xc7 \x9f\xc0_\x1b\xeb\x88\"\x96\xe6b\xdakx\xc5\x1a\x98|q\x11\xbel\xc7<\x16\x8c\xda4.\xa3\xe8\x18c\x99\xfdd\x0b\xba\xd3\xfa\xe5\x9a\xbf\xe9\xae\xbd\xdf1,m}\xc26\xb7\x851\x1d\x17\xac\xef\x0e_\xbfR\x04\x01\xa9\xb4\x0c+\x10?\x9cd#\xc7\x8c\xa3\x18=R\xc5\xe0\xa1,\x05\xa7\xc9\xea\xeb>ib!\xf1\xf0L\xde\x9c \x1a\x1d\xbb`\x9f\xda\x9d\xa4n\x9c\xc4\xffN\xf6\xbf9\xe3\xd5\xecb\x089.\xfaRJ\x87X\x987\xa44;\x06\xf5\x8eK\xfb-\x1c\x0d\x1a\x00\x0e$t\xect\x1a.\xfc\xc4\xb5*\xcf\xbb\xc2\x87\x06XIB\x84\xe9[$\xc6c{g\xd3\x91\x85\x0b.\xbcm\xd4cI\xb6^\xcf1_\xe8\xcb\x1aq\xb3\xbf\xfdb\xe1\x82E\xff\xb1\xf8=;\xe7j\xa6\x1a\x06\xd66\x07\xa9\x00j\xe9xG\xca)\xa2B\xa9\x93\xd8QBaU\xbd\x94\xe0\x073e\xda\xb7\x98\xc5\xe5\xed\x1a\xce(2HV\xa0\xea\xbb\\\x00O\xf1\x11\xed=\xf4\xe6,/\xcb\xe6#(kH\x8d\x1e9\x90W\x16\xe8\x94`/\xa7\x11\x12\xe5HN2\x10V\x1f`Ia\xb8\xda\x8av\x84\xdb\xc2\x9b\x90\x92]\xdd5\xfd\xe5\xda\x13\xa4D\xb3\x10\x83\x03\xd5\x86\x14\x02\x96/\xc28H.P\xc9\\\xfd\xe2BS\x05F\x84}C\xa1\xcdZ\xa0\xb8]v\x8b\xab\xb5\xa3\x83\xa88\x0c\x8akM\xd9H\xe1\x07l\xf2\x18G\\\xe58\xeb\x95n\xe9\x93\xd5T\x04\x88\xca\xda\xaa7\xf9\xbb\x18\"w\xf4Q4\xd1<\xc06\xcf\xbf\xdc\xd4\x14\x0e\x02\x00\xa6K\xb1-?\xbf\x8ag\xcfWR\xc8\x89OY\xfa\x12\xa4\xa5\x07}\xa7\xd6|\x15\xde\xe9UA^\xb0#0\xe4\\F\xdas\x89\xe9\xa5:%\x19\x96\xb4}:\xf9Ro\xd1\xdb\x13\x83/9p\x0f\xb6aC\xe2\xcd\xaf](\xbc\"\xf9\xfa\xaa <3\x9catm\x9e\xfd\xa4\xb0\xe7\xce1|\xf5\x15\x8c\x1e\xc0\x87N\x11\xac\xc3\x88\x17\x8f\xd5\xc5cV\xbc\xab.\xddr\xe8JL\xf3\xf5u\xbc\xa60\xb2\xf2.| \xe3\x9d\x9d\xf6\xfb\x07\x9d\xd7\xe3\x9d\x1d\xf8\x12Z\x89\xa4\xc6<\xc5\xb5\xb8:\xd5\x93\xd1\x0c\x96\xce\xe5\xf1c\xd8\xeev\xd2\xc2\xb6\xa3A\xbd\x8c6\x8dK\xb6\xad_\xb1\xc7\x8fa\xa6\x87wZ\xb0u\xfd\x12v\xb7\xe8\x0bko\xcfB)\xf7\x98\xb7\"\xf6\xcbf\xed\x8cq\x1f\x1e8\xb0\xaemx\xb4)Z\xa6\x80Q\xb5\xcc\xbb\x1aK]Y\xed\xa1\x0b)L7\xdc\xf4\xb5\x82\x7f\x16B\xc7D\x12>Ze\xcc8\x8f@N\x0f\xfb.\x8c\x8b\x07l\x1f\xf7\xe5?&,\x9f\x0b\xdb\x14\xeb\xc9\xd7O\x9f\x1d|\xf3\xa7o\x9f\x7f\xf7\xe7\x17/_\xbd~\xf3\x97\xb7\x87G\xef\xbe\xff\xe1\xaf\x7f\xfbg\xfft\x16\x90\xf9\xd9\"\xfc\xe9}\xb4\x8c\x93\xf4\xefY^\x94\xe7\x17\x97W?o\x8e\xc6[\xdb;\xbb\xf7\x1f<\\\xbfg\xf1h\xdc\x0c\x8f\xf8\x95t\xbe\x84\xaf \x7f\x04\xeb\xeb\xa5\x03\x19K\xc6\xedOK:\xf0\xa9/\x83r\xe9`,c\x95[[\xa4\xc7\xea\x02\xd8\xba\x84U\x01\xff\x01\xb6)\x1a\x13\x8c6E\x9e\\\x16\xf8\xc1vn\xc2\x84!f:^9mfw\x1df:\x8c_g\x8cB\xf7S9:z\xc1v \xa6\xff\xac\xef\xc1\x96\x83\x00c\x13\xba\x13\x14\xe5P\xec9\xda\xbd?\x1a\xed>\xd8d>\xf6\xd3\x92\x9e-\x06\xe9\x14\\w\xc6\xbc\x84\xa1\x0fV>>\xa6\xac\xb9\x80|;\xc4\x8cZ\x08\xff\x0f$\x98\x0f\xf1\xcd\xb8\xfdfWz\xb1\xbb\x05_B\xd8\xe6\xa9*\x8a\xa6{\x14\xaa_\xc9\xd4\xda\xb0d\x08\xdaD\x08\xda\x1dS\xd0\xb2NTE[JzC^\xcd\xc2\xcb\x88\x1f(T\x81<(\x8a\x02\x0cCW\x10\xea\x0f\xe0\x8f\x90PZ\x80b\x06\x85`\x94.\xfc\x88\xaek\xe9\xa8k\xa0\xbf>\xaeY\xb7\x8c^\xcb\x1b\xf7\xbb\xef\xd1~\x06\xf6\xb1\xe3\x11LT\x01\x0bR^e\x83\x96+\x9a\x0e\x10QR2a\xde\"w\xb8\xc3\xfe\xfa\x1e\xa4\x0c\xc3\x04\xf0%\x9f\xc3\xc6\x8cM\x02\x02x\xfcx\x0f6f\x94rX\xa7'\x18f\x18\xd8\x14\xeb\x8fwv\xe1\x8f\x10\"\xc2d\x1d\xb8 \xda\x9b\xc1\xc6\x1e\xcc_\xf9\xaf\xb8\x8c\xa7\xc0\xb6\x18x\xec\x83\x8dY\x04D1o\x92!\xef\x19j\xe9}\xd1\xd6R5\xcf?\x85\x0dX\x1c\xc3\x87=\x18\x8d\xe9\xc1:o\xddp7b\x8a\xb9\x10\xa4)\x9c\xb6\x0b\x17\xac\xda\xac\xb5#B\xe5\x96S\xb2\xb1\xab4bAj^)\xa3G$\xbcd\xac\x8c+\x81%[\xaa\xb8\x12X\xa2\x8a*A\x0b:_\xe4\xbc\xa0\x13l\x82\x99\x9a\x8e\xef\xb7U\xaf\xcc\xd6\xb4mf9\xc7ff\xad\xb7)o\\\x11\xe6\x82\xd9\x9a\xee\xec\xb6\x03]/\xaaO\x1e\xb6?\xe1\xf6\xa6\xe3v\xdfK1\xb7\xce\xac\x99\xc5\xa9&\xa0\xc3\xd5\xa7\x0f\xe8p:D\x1a&%\x1bm\x82\xca\x89IU_M\x8b(UA\x92t\x9e\xb15J\xe5{\xed\n\xb8\xd6\x88\x0d\xb4y\xdc\xd5\xcb\xab\x82\x7f\xb4\xdc\xc9\x84a\x8d\x8b\x05i\xbb@-p\xcb\xcd^\xc1\xbd\xce\xc5+\xb8\xcd\x9a\xbc\xe3L\xde\xc7\xd0\xf1@\xd6\xd7\xcb\x92\xa4x\x1eS\xd4\xd1S\x11\xe7\xfdF\xccN\xe1\xd4\x0c]M\x99xN\x932\x0e\x0e\xc5\xc45\x95\x8a$\x89N\x93K\x8d\xc34bz4\x00\xa8\\\x18\xe9\x1d\x81\x16\x01\xd5\x1b\xef4\x8c\x03\x1e\xf0\x87\x95\xa1\x82\x99\xdd<{p\xeaVn\xd63\x14r|w\xc8\xf6\x9ayUr\xe1[\xb3\x93\xfe\xb0\x85\xe2\xa9\x18s\xda\xfe\x99\xc7\xf6\xf9hQ\xc6\xef_\x86A\x10\x91\x0b?#\x8e\x1d;\x86\xc0i \x06\xf2\x12\xe1FNN\xde\x1e<{\xf7\xd7\x93g\x07\xdf\x1f\xbd~\xfd\xe2\xf0\xe4\xe0\xafG\x07\xaf\x0e\x9f\xbf~u\xf2\xf4\xf5\xcb7\xaf\x0f\x0fNNP\x87\xc7\xbcGsE$\x1c\x90\xc8\xc6M\x97\xd6D=\xe9!\xaa\xdd\xf9\x84\x12;b\xfa\x9ez\x98\\\xffS\xa5*wTf$6?\xaf\x8eXk\x0cO\xc2\xbdK\xd1\x1a\x05\xdfVN\xb5\xf8\x17?\x1e:\xadRk\xbce}$\x89\x0b\xd3\xee\xba\xbf'W\x13\xb0\xe8f\xd1\x19)\xdc\xa2\xf9\x05gTCC\xcb\xc2\x04a\xa6;\xdf\xe6\x90U\xe8\x81\x8dFLx\xc0hz}l\xd7\xd4\xa9\x07txp\xc4t\xb0\xf2\x0b=\xb0\xc9y\x80\x81\xd8&\xd0\x16\x0f\xe5}\x18t\x879\xa37\x1cJ\x91b\xc09\xfe\x1a\xc5JNC\xdb\xa8\x06KU\x9b\xdf\x94\xf1\xac\xf1-\xb1\x0b4\xa0\xd5y\xf9\xaa\x1aQ\x8c\xc0[\xfai-:\xd7jW\xe5\xa7\x1e@\xc7\xde\xb5\xfd\\;^F\x82rF\xec\x0b4\xa35\x0f\x957\xacA\xa0\xc0t4mTg\xeb\x02\x00^p\xfc\xc5qU\x8c,\x01\xb7\x06m\x1cH\x85\xfe\x03\x9a\xd7r\x1f\x00\x08\xfcF\x9b\xd6O\xf1\x9c\x07\x17U\xc0\xedX\x0b\xb7\xe3\xe6\xfd=>\xeeq\x0d\x07Nd&\xde\xc2\xcf_\xa0\xb7\xb6yD(T\xd0W\x19\n\xd3\xa8\x07T\xa9\xdf\x0b\xcf\x9f\x17${\xc1\x9d\xa7\x91\x83X\xdbt\xe1\xc0\x96J\x1cY3\x1f\x9bB:\x9a\xcf\x84\xdc\x0c?\x1e}\x1e\x12\xd52M\x14\xd9\x9f\xc5c\x82\xdc\xbb=`\xcd\x99dB\x18\xd1\x7f*\x07\xcd\x03\x00TY\x80\xeb\"\xfd4\x85\x95\x18\xb0z\xd3\xc5\xbb\xa1\xad\xf0\x18T\xba\xe3\xd13\x02\xceG\x16\x82K\xe2o\x06u\xfe|9\x81\xb9XZ}\xb5\xb7\xc4\x9f\x15\x93:H\xa2\x1as\nn\x8cqi\x12\xcf \x18\xc6\xe5\x96p\xce\xa7u{p\x92\x07\xa9\x8bX5xdw9\xb0\x01\xc2\x82!c\x87\xce\xf8\xbbo\x0c3\xcaW\x99\x91\x96\xb7Q\x0c\x14\xf6\x14q\xf7\x06\x0f\xab\x894\x07\x0c\xcdxE2b\xc4p\xef {(b`\x0bLmW\x97\x18\x9f\x99,.a\xbea\x8c|JN\x7fz\xe9\xa7\x0e\xbdA\xfa\x97\ndZ\x89\xf1\x18\x99fW\xb9\x87V+\xd6\x0f\xa9X\x93\x9a8\x1bB\xe6\xf7RH<\xc6-F\x82&\xd3\xf8x\x85H\xe0\x82\x10Y\x91\x0c\xe9J\xf8br\x013\xef\xa5\x9f\x9a\x19\x05\xe0\x84\x89\xcc\x15\xf7s\x93k\x99)\xc2\xb0\xfc\x08\x93\x80lZx\x94\x1d\x18\xd0x/\xa3\x0d\x12'u`\xc7\x8e\xc9_N~\xf8\x88\xab D \x97\x0c'\xc6/\xf5\xac(\xa8\xc4\xbe\xed\x07aO\x0d\x95\xc8\x0f\xbbm\xa8,\xe4\x08X\x9b.\x04\xde,Y\x9e\x86\xb18M\xb9\xc3r\xea\x9f\xf6&\xc97\xa3\xdf\xa3\xabt\x88L\xa8W\nC\xa6\x9b\xc7^\x91\xbcKS\x92=\xf5sb\xa3\x11P\x15+\xbeW\xec\x86\xa7\x9e\xcd\xcd\xb1\xf5H\xa2\x1aP\xacH\xe7!?\xe7<\xb6y\xac\xcc\xf8-\x1eTT;\xf28\x92&}\x9c\xc1:\xc5u\xa1\x9aU\xba\xcd\xa5L\xc9\x13A+\x0f\xd8\x80!\xb72\xdfN\xdb\xca\xab\x86o7@N\xef\xdfbx\x02\x915\xc7\xe7\xf3v\x07\x82\x05^\x06d\xc5\xcb\xa0\x03T\xc4`\xd6\xa2z\x1a\x02\x06\x8a^\x1c\x13\xa0\x14\x9dL\xe0\xf2\xa3a\xb5o ?j\xeel\xc0n\xf5\x9ef\xba]\xc3\x98\xd1\x06_\xa8\xf2W\x07\xdd\x86\xc6\xcd\xfd\xe8\xbfpi\xaf*\xac0\x8d\xeb\x0c\x0e\x1b\xf7\x9dc\xef\"\xf3S>\xa4\xdeK:\xe3\xf8U\x03h\x03\x04\xbe\xe2\x0e\xca\xa6q\xcf\xb5\xc6\xbbD\xe3K\x14\x10 A\x91\x9d0\x1f\x17\xb4UL\x8e\x1d\n]m\x9ad\xc8P@Z\xaa\xde\xa3\xd9~\xc4\xbd\x88\x87\xa3!\xaci\xa9:\x14Q\xc4t\x8fB\xbf\xd8~\x90\x90\x90\xcfY\xe6\xc8\x16\x89\x92\x87\xb2\xb4\xad\x10\x13\x12\xe4P$\x954\xaa\x96\xd2\x16\x0b\xbf\xe0\xafs\xf0\xb1\x91\xaa\xcc\x0e \x14\x0b\x02\x17\xec\xe4\x00CD\x8e\x0e\x11\xc9\x0f\xef\xe8\xc0\xcez$\xdd<\xf0\xe67\xbcO)\x88\x08\xbd\xafM$\x82\xb6\xf8n\xf1\xc4*\xd7\x8e Q\n\xa2\xce\x8c,\xb26\xb2\xa8%D\xfd\x01\x0e\x9a'S\xce\xa5\xa3J\xe7%?\xe2TN3 9<4)\x16A\xb87)qL\xc2\xd0J5\xf8^\xc4\x12v\x10K\xb1\xc2\xf0A\x16\xcaO\xb3a\x88\xc5\xef\"\x16\x9f!\x16\xb4x\xf5\x99M\xaa\x82\xd9\xe9\x1d\nH\x14\xd5\xca\x88\xa5\xb2\xbe\x0d\x15\x1c\x0d3Mb\x83\x0d\x1dn#\xcdlr\xc3GP\xae\xaf;h\x0e\xdd\xe0M\xca\x9e\xe5\x10\x8f@\xf1\xc8\xcf\x990\xda\x94\xcb\x8b\x9e\xc7v\xe2\x1cS\x8e{\xe6\x17\xb6\xaf \xad\xdb\xcfM\x10\\hBp\x02\xc0~?\x0c\x17\xf6\xa1\xb7\xc2\x80\xde\xd4<\x0e\x08\xf4\xa6a\x81n\x87\xdeP\xca7\x08\x99\x0d\x90\x94fM\x0b\x17\x15.X]^\xd0\x14\x08\x10\njL\xec\xad^\x0e\xf7v\xe2\xbe\xa6|\xfd\x1fg]\x06#\x16\xc1m\xb3C\xabr\x11\x15\xcf\xf5G\\\xe3o\xe2\x01K{c\x99\xe5\xc4+\x93\xc7z\xeaV\x83\x92\xaa\xb05<\xb6\xf9\xbe~\xf4\xd0\x96,\x8b\xb2[m\xce\x9d\xd2jJz\xaa\xd2\x98T\x14\x99\xb3\xa2\x84EEa\xf5RFz6\xb0\x97\xc1\xe1-\xf4\x1e/\xf9ix\x84u\xc9\x8f\xb0\"?2\xa7\x8a\xe6\xe4\xc3W\x90=\x02\x9f\x92\x1f\xe1\xd4o\x92\x1f\xfe\x00\xf2\xe3\x9c\xa7C=\xb0cAl`*$\x0d\xa9\x11\x1a\x93W\xf2\x87O^i\\\x81\x89(m\xd6c\xe9\xd8\x85\xcd\xa2\xca\x1b\xdb4X\xd7|\x14q\xc5] )\x08\xc6\xe6\xfa\xf0\xa1\xa3\xf1\x13jt\xf5R\xcah\xca\xab\x85[\xed\xc8\x1d\xe2Q\x9f\x18\x99\x84\x1f\x80nl4(<\x0d\xc5\xbc\x9ff\xc4\xa7\x07\xcd\xa9\x10\x17\x90\xc1\xa6 \xd2\xc6\xd7\xce\x8b\x85\x99\xcd\xe8k\x1a\xe4\xeb\xb4\xe8\xb3\xe1\x82\x017\x9b\xfc\x08\xe9\x1f\x05\xfd~\xf8\xd6\xbb\xff\xb7\x1f\x94(\xdeB*!\"\x06\x0cZ\x1e\xe0\x1d\x0e\xabI\x1f\xba5\x138\xf7^\x1d\xfcpr\xf4\xed\xdb\xd7?\xbc:9x\xfb\xb6_\x03#\x1e\xcc\x80\xa0\xcf\x92\xa5zR\xff*J\xfc\x80\xa5\xf8Y\xc8j\x84AM\x98\xb5\x1bX\x03\xe6a\xecG\xd1\xd0-\x12@\xd5[\xd9\xdc\xb5\xc9\x02\xb0p\xb42\xd7[b\xaa\x97~\xca(\xe8\xe4M\x96\xa4C\x90\xd5\x10\xf9\xb7\x11\xcf\xf4\xb6\x04M\xac\xd2\xb2\xe3!\x03H\x9a\xdb.\xc93\x8e^\x87\xaf\xca \x92q\xd8\xb2\x0c!\xee\xec\xa6\x87\x02\x8a\xe5\x0dVL\xc8\x81\xd5VG:P\xea[\xb6c\xfam\xf5\xea\xdaV:\xaa\\hCG\xddZ\xc5\xab2\x02-\xd4\x0d\x9b\xac\xa2\x1b\x0d\x8fT\xde!\x0dA\x860\x03\x95\xb4\"\x83\xea\xcbF\x9a\xcd\xea\x05\n\xd8j\x96\x04)\x9a\xd6\xd5\xd6\xaa2\x80Z\x15T*\x91\xc8r\xe6\x1a$\x91\xf0*\xf9\x1a\x067\xe8H\xe9\xf7\xc1n}\x89&\xb6\x9c\x8c\x9b\xc6\x14\x18x\xf4\xea\xf6`\xa7\xd91\x86\x95\xc1yu\x1b\x99&.\xc4\xc7\xc6\xaf\x9bp\xa7\xd0\x19\xb7\xbe\x91\x13\xfdk\x9a\xd5\xba\xee\xcb\x8c}w[\xdb\xbb\xaa\x8a\xa1Y;\xddC\x18\x9b]B\x98\xa261$\xe5ow\x18V\xa9\xa3\x1aoe\xd5\x8f6\xc2.\xc8\xb2\xd5a\xca\xa2j.%\x9d\x8b\xdfG6\x9c\xf3,K~\xaf\xa8\xb2 `9\x93\xd6\xd2O\xa7\xf9\xb1+$\x9fye\xb1\xde\xd8\x96\xee\x9bir\xac|)O\xb2\xb7\x02\xed\x13\xe3z\xf4Ub\xf3\x13\xb0\xdfW\xdd LU_\xf2}\x88W\x8d\xf4I#2\xa1*J\xc4\x81>Z\xc6\xaa\x9e$*\x9c\xe9xQr\x86\x02]\x850$\x96\x93\xa9\xef1Ij\xcb\xf7\xc3D\xec\x0b'F#\xb1\xa0'\xa3\xa5\xb0\x98*N8\xab8\xe1B\x84\x12\x7f\x04 |\x05\xc5#H('\x9cQ\xf8\x92W@wb\x05\x82GcpN\xa7\x13\x17\xa6\xf4\xba\xaf\x00&SY\xae\x0c\x8d\xe5\x85\x11C\x9a\x19\xc3\x08\xcfE\xd7\x036\xd7\x7f\xe8\xfe\x92\x13\x8d\x9f\xe0\xdb\xdeX];[c\x85\x17\xb0\x9c\x14\xa9.U\x07\xc8S{\xca \x9dE\xdbI\x99\xb4\xa3\xca_\x0f\x19g=\xae\xf1\xa64\xdc\xcc\xce0\xcce\xc6b\x86\xb2|7\xda\xb8\xa1\xedX\x9e\x98+\xc5\x9b\xd7#q\x86\x0c\x85.\xd9\xb6)\x87\x94\x9f\xe7\xe1Y<\xa4\xa9\xfeY\xe9'\xc3z\x99`\"\x98-g\xc59\x98\x93\x0c\xc9\xa7\xf2Z\xbd\xfb\xd9\xed{\xa1\xeb\xd8\xf6\x9ef\xb1\x055\xc1\x1a\xb7\xd4\xb9\x8cv\xb6\xdaYyJ\xcc\x1aP\\$O\xf8\x01\x7f\x93$\x11i\xa5{\xc3Yx\xf3\xa4\xccL\xb5\"\xd8\x83{?\xde[\xbfw\xa6\"\x86gZ\xbfi\xdb\xb2`\x1d\xd0\"\x13MG\xed\xc8\x05\xeb\x8b/\xefYf\x94>W\xca>Q\xd0C\xeb\xf0\xfc\x1c\xf4\xcfY\x12\x17\xe4\xb2`1<\xf9\x9b2\xa6\x7fo\x1a{Hu\xe7Ul\x0b\xc1\x9e\xba\x18_\xd0\x9e\xd8m\x0b\xd33_\x99\x84\x19\x0f\xb1\x81\xac\xaf\x9bg\x1aHaI\x94\xf3\xcdH\xce\xf0\x98\x98\xf1{r\xf5&#\xf3\xf0R\x9a3_\x94\xb8\xb3(\xd9J\x8b\xb2\xe8_\x146\x9c\xee\xb2\xf8XZ\x8d\xad[\xa14\xaci.\xafi\xb7\x98\x02_\xc9\xd66o\xadms\x03\x9a\xc4WD\xa9\xfbs\nq\x19\xaeo\xe8\x15\x0b\xbfx\xcb\xd4\xac\x02\xd8)\x05\xcf\x13\x9e\x02\xcb\xe1\x98xa\xfe\xbd\x1f\x85\xc1ADh\x0d\xda\x0e}\x1f1\xc6 Jb\xf2$\x0e\xde2x\xfe3\xb9\xa2\x1d\xf8\xb0\x0e\xf6ZD\xe7\xcf\xe2\x9e MF\xff\xa2T\x01{\xbf\x0f\x96\x05\x13\x98\xd9\xf8\xa7\x03\xeb`\xdd\xb3\x1c\x0cU\xe8\xb8\"\xf0n\xe4\x98\xc1\xe5\xdc\xee\x0f\xcf\x04{`Y\xcd\x85\x113dq\xb9h\x8d\x19e\xc0\xd9\x10\xba\x1c\x03\xdd\xab\x802\xd2\x88\n\x02\xbb\xc0([\xd8a\xb3\xb2O\x87\xb3p\xa1\xa4\\\x92\x97\x91\x88\xf89\xb1K\xf3\x1c\x96=We\xe3\xce\xaf\xef\xf4\xb9\x14P7 \"\x95\x81I\xcd\xd88\x1a(\xaco\x9d\x8e\xc6\xcb\xce\x01\xa1\x9b\xe2\x07\x01]\x830>;J\xec\xb9\x98\xe8\x8d\x06R\x1dd\xa9W\xf9,K\xaf\xefp\xcc\x81\x0by\x8b\xae9\xeb\xc8>\xe7Iv\xe0\xcf\x16\x93^b\x06\x84-7\xb3\xb5\x96\xa2\xac+\xec\xc5\xabk\xb4 I*\xb7f\x84\xa3\x94\x85\x84\x9aWp\xd4\x8e\xc3\xdc\xc4\x0cK?\xfdH\x03\x9e*\xa8`\xfe\x15\x9e\xbf\xcc\x15\xbb\xc0\x9c\x8f\x8diJ\x96~\xfa<.\x92\x1f\xc2b\xf1g\xb1\xdb\x98?5\xf6\xa3 \x9c7+\xe3\x8e\x0e\xd0\x00\xf2\xd1\xe0\xb2-\xd9h\x8ckU$\x88\x12\xfb$y\x82\x95\xe8[\x80B,\x80\x1a\xa5vRg\xd5\xf0\xa9\xa6\xa2\xce\xf0\xed-\xa9\xa8\xd1f\x9b.\xc2\xc0\x7f\xb1\xfd\xc0\xe9\xb34\x16)U<\x91R\x85B+g\xa3\x86H<\x9b\xdf\xa5I\xda\xa3\x83b\xa7\x17\xfdjY(\x16Epr\xdd\x06\xc4\xe4\x02\xbf\xef$gP\xd0\x8a\xe6Y7R\x85\xd1&1)\x8fm\x8dw0\xc7\x85\x84\xdb*\x1fN\xc5\xfaPv\x92\x16\xa5I\x12\x1d\x86?\xd7n\x9d\xcd5\xa1\x97\x9b9\x9d\x04\xa5 \x92.\x01\xdb\x1d\xb7\x8c\xdf\x06\x9c\x15\x90\xc5`\xc6m\x89\x1bc\xe61%\xe3\x1a{\x01g\xf0}\xfa\xb6\x9a/K\xc7T\xfd\xb9\x07#L\xc6$\xb0\x18\xec\xd1\xbbS\x91\x9bIAZ\xc6\xa4I\x83O\xda\x0bB\x9f\x0e=?p\x0dn\x02\xe4 \xad\xddJ\x80\x0e*`\x8fyl~\xd5r\x80\x12\xe6A\x05\xf7\x9dT\x15\xa0^\xceb\x91\x91\xce\x82\x0e\xb90\xe0\x96\xab\x95\xdd\xc9je\xae\xf0\xcb\xeb\\1\xe2\x19\xbe`\xcax\x1e\x8a5\xeb\xf2\x81\xdd%3\x98\x91\xdcf\xd5\x92;Y\xb5\xa4Z5FM\xa8\x9d\xc0VZ\xb8NB\x88n\x0b\x9a{\x8d\x99k|\xac{m\x9b\xa5Z\x1e\xef\xdeW\xc5\xa2\x8b\xed\x9d\xadv\"]\xbf\xbe\x10c{g\xbb\x13^\xaed\xe5\x0f\x1d\x17,\xaf\x9d\xc6\x95N\xc8\x9aX\x9ax\xc5\n\xc4#\x08-\x0c \xd2\xcdx\x80\xef\x05cB8\x8b\xe4{$\x9f\xf9)\xb1 c\x92&\x18Z\x9e\xe5Q\xb0\xb7v\xdb\xd22\xb8\x990\xae\xa2\x06y\xdc\xccj\"\x84\xc7w\x9a\xb90\xd7\x11H\xa9\x8bq\xf2\x84\xb9F\x1761_I#05\x86\x91\xfd\x12\xacSz\xa2\xfcX\xbc\x12YP\x90|sk\x07F\xbcd,\x16\xab\xd9\xc27X\xd7\x8a\xcb\xe5)\xc9\xe47\xf5\xaa\xf2.\n\xef\x8b/\xf8\xc8\xd0\x15\xb2\"wg\x94{)\\\xca\x83\xb2\x00\xcd\xfbP\xc2: \x05\xb2\x89L\xb0\xe3\xc2HM\x13/0\xc6\xa5\xf2\xc8\x9c#\xb3)59\x81\x18\xd6A\xa1y\xa1\xab\xd2\xe4\xcf\x0b\x8d\x06\xa1\x92j/\x99\xc4zII\x8c*\xbc\xf6r}\xdd\x81\x05\xac\xef\x01\xb1S\xba\x0f\xd3\xe5\xb1\x0b\xe78\x97\xd4\x85\xa5\xc3w\xaf;\x02Ml[\x90\xd8\xa2P\x99\x8d\x10\xf8\xf0\xcf\xfaP\xd8\x95\x8b\xd1\x04\xcf8m\xd7\x13Z\xe6\x0c\xc1\xa0\xf0H\\d!\xe91s\xa9\x16\xe5\x84-\xca\x9a}\x05{p\xea\xc5\xe4\xb2\xb0\x1d\xc7\x0b\x12L\x1d&-\xcc\x15K;#\xad\xcd\xc9\xfa\xba~u\xc4CW\xa9\x7f$\xda\x01\xe8\x17H\x91i\xd2\x8e\xe1\xae\xcdSU(\x92P\xdd\xc1\xca4\xc7\xca\x0e\xc2P\x0e_\x0d\xc6\xd6\x9e5\x01koS\x03\xc1\xd6\x04\x8b\xc7V\x17J\xb4\xf2\x02\xeb\x0b\n\x93\x1d5\xc0\xbd\xe9\xde\xe4\xf8\xdeY\x1fc.5TL\xc9q\xb7_#GY\xc6w\xb3(\x9b8m\xdd\xa2\xec\x8di\xf1d\x95Ea\xcba[\x1e;\xccd\xba\x89\x1az\xbaV\xeco\xd4D\x13//O\x19\x15`\x8f\xd1\x97Pz1r\x1ci5\xed\xbd\xcd\x0f{c\xe7\xee\x17\xb4\x86W\xf5\xd9\xb9\x13\xfd\xd7\xfd]\x87\xc7\xe8\xfc\xc6\x9f\x15Iv\xd5=\xc5\n)\xc0\x84\xa2H\xbfM\xa5b\xd1\xe9i\xc6JOO3e\x85 \xc8H\x9e\xb3:\xec\xb7\xb2ZFx/\x19Qw\x94\x15\xe1,\"\xbc\x0e\xfeVV\xcb\xc3\x80W\xa2\xbf\x94U\xca LX\x15\xfaKU\xe5\x14\x8bO\x95E~\xce\xda\xa7?\x94\x15\x82\x90\x95\x07\xa1\xba8\xe1\xc5\xea\x9e\xc33V\x1c\x9e)\x8b\xa3d\xf6\xfe\xefeR\xf01T\x7f*+'\xc1\x15\xab\x96\x04W\xca\nl\xeb\xd4\x1bwZ\x16E\x12\xb3\n\xf8SUi\xe6\xc7\xe7>\xdb\\\xf6S])\xa5\xe0\xcak\xe1oe\xb5\x90\xcf\x8a\xfePVH\xf8\xd6\xd2\x1f\xea\n\x11/\x8f4\xc5gYR\xa6\xa2\x0e\xfe\xa1\xaa\x18\xf8\x05\x03F\xfaCW!\n\xf3\xa2\xaaD\xffPV\x0cX\x95@YH\xd8p\x03\xa2\x1cn@\n?\x8cr^\x05\x7f+\xab\xcd\xd9\xca\x06s\xe5\xaa\x06\xa1\x1f%\x0c\xa6\xd8Ou\xa5s^\xe3\\Y\xcc\xc7\xa9\x1e&_\x05\xe5\xfc\xc9\x12\x0b\xc9R]xJ\x02^~J\x94K4\x0fI\x14`\xd2\xe7\xcc\xb6\xc4\x1f\xea\x8ag2\x98\xd5\x7fj*\x97\x19\x11\x15\xcbL L\xf3$\xc1\\\xb5\xff\x1f{o\xda\x1d7\x92$\x08\xbe\xdd\x8f\xf5+\x9c\xf1\xaa% \x03\x0c1H\x89\x94B\xa2\xd8J%\xb3[\xdd\x99\x92FRVMw0\x8a Fx0PB\x00Q8xdQ\xef\xf5\xcc\xec\xdc\xf7\xee\\=\xf7\xd9\xb3;\xf7\xb1\xc7\xec\xce\xf4\xf4\x87\xce\xfc#\xf3\x07\xf6/\xecs3w\xc0\x017\x07\x10$\x95U\xbbo\xf1\x81D\xf8\x05wssss3s3Q\x08^\xe9B\xc9R\x16I\xc81.\x86\x90\xbd\x18\x92\x99\xdb\x98\xb9Mf\xee`\xe6\x0e\x99y\x1f3\xef\x93\x99\x0f0\xf3\x01\x99\xb9\x8b\x99\xbbd&\xf7qB\xc4\x8b\xad\x80\x04\n\xbe\x92\x85\xcaU\xb6\xb0\xae\xb1\x85l\x85n![\"\xca\x89\x17\xaa\x00\x92X\x92\xc0\x06\xf3\xc4_\xe2\xe4\xe2+Yh\x89K\"X\x92\xeb!\x88V9\xe2\x1c\xbc\xd1ERY\x80\\\x95\xefO\x10\x90\xefOH8\xbe\xe7\x97\xa7\x1cQ\x15_\xa9B\xa1\x7f\")\x04\xbc\x91E\xf8)\x8f\xf0K\xf8J\x16Bh\x85$\xb8\xc2 z/\xb3\xa3\xf7T\x81\xa5\x1f`G\xc5\x0b]`%\xf3\xc9\x89^\xfa\xc9{\x99\x9f\xd0\x1f\xe0Q\x8e\x05x\x94\xdb\n\x04\x99$%\xea\x07]P\xd2m\xf1b) \xb1\x17\xde\xa8\"\x91\x8f\xa40\xf2IR\x18\xc5\x18M\x19\xcb\xc8\x1fTA<0B1y\xac\xa5\n\xe1\xf4\xd2\xdbU\xbc\xca\xca\x85\xa4~X\n*\xba\x17[i^\x9cg\n\xa7\xf1\x95*\x84\xdf\"?\xb2\xf2\x13\x1fg\x00\xde\xc8\"\xc14StU\xbe\x93\xc5T\x11[v|Zp\x8c\xea\x07U\xf0gP\xe2gTV\x82\x03I\xc8\x91$\x08\x85\x84\x84@\x92\x9f \xcf$^\xa8\x02\xd8/\xb2C\xa9\xbf\xc4\xef\x8a\x17\xb2@\x89:v\xc4I\xf9\xb4\x98N\xf9N\x17\x0b\x15~\xe1+Yh\xe9\x87\x88b\xf0F\x16\x89\xf3d\x8a\x13\x82\xafd\xa1\x95/;\xb4\xf2\xe9\xdedI\x1c!I\xc5W\xba\xd0\xa5d\xe0\xe1\x8d,\x92#\xeb\x9d\xe6$\xf3\x9d\xe6\xcb\xa5\x9f\\\xca\"\xf0N\x17\x93\xf3@\xaf\x97\xcc?\x91\xfd\xc80R,Q\xa4\xe0\x9d3\x1b\xf3\x9c!\xd9\xcdH\x92\x9b\xf1\x8b\xac8\xd2\xa8\x1fdA\xc1[`)\xf1F\x16Y`\xfe\x82\xceT[vf\xdb\xb3\xb3@n\x87\xe2\x85.\x90)x\x887\xb2\x08R\xcd\x8c$\x99Y\xe2O\xdf\xcb|\x7fJ\xd2x$\xf0$u\xcf\x11As\x12;\xcf|\xfc\xf0\x99O~\xf9,\x98qW\xfc\xfa\x9c$\x11<\x0c\x83\x95<@\xcaw\xaa\x18\xae$\x9a5Y\xfa\xa7\x92\xbb\x11oT\x910\x88\xb0\x84x\xb1\x15\xf0\x93_K\xfcY\xc0\xa3\xac(Z&Q\x95\x96~\xaa\xf6\xf1\x94\x9c\xe3\x95\x82\xd0\xca\x02\x9d\x95\x9fe<\x89T\x19\xf1N\x16\x8b\xc3\xcbSI\x00\xe5\xbb\xadX1R\xf5\x83*(\xc6\xe4\x87\x95\xd1V\x93\xc8J\x8a\xb8&6\xd2\x9a\xc5\x92\xc8d1M\xec\xcf$=<#\xe7Q\x10\x85\x82:\x90\x05\n\xa2\x9b!\xd5\xad\x94\xb0\xc8\x88P\x05{\x0b2\xa2\xaa]f\xb5w2\x1a\xfb\xae\x1e|\xac\xd2 eMv\xc3~\x18\xc6\xd7\xf8\xe1\xba\xe95j`)\xfdk\xe4\x0c\xeb\xe1\xb5r\xd9\xf7zq\xb4\xa8\x7fp\xff\xbeeL\x8df\x1f\xcal\xe3&\xf2s&\x8doi\x19\xba\xfa\xcaT\x94x\xf2\xc4\x8f\xe2\xe8r\x19\xe7\xe9\xd3\xa7\x84\xa8tn\x95\xaf\xfah\x99v\xe6\xf4\xe0\x8dB;\x06\x82#\xc1\x98\x9e9\x85\x12\xd5RN\x0c\x17\xca\x15\xe3\xb6\x14Dm*\x14\x95\x8aUKA\xc55\x9f5q\xcd\x0c\x19\x8e@0\x1cg\x8eR\xde\xda\n\x02\xd0\xb1 \xbc\xda\n\xfa\xd1\xe5\x88-\x9cD7\xb3{ \xdab;(_\xcd\xdb\xe4\xdd\xeaQ\x9a\x9c\xaa\x7f\x1fk|\xcc\xfaS\xd3wh\xb7\x9a\\\xdd\x94b\xe6\xf4\xd4U\x13\xf6u\x8f\xf5!8j\xefk\x16\xcf\xcbx]\x98\x91`\xc6\xc2OY \x03\x16\x8b\x9a\xef.W\x9cEq\xe6\x83\x8a>\x88\xd2`\xc6\xd5P\x07m~\xb0\xce\xe4\xbd\xc0\xac\xd5\x99#\xdcn\xad;[k\x83\x01\x93\x9f\x00+F\xc7\xef\xee\xf4CBF\x05f\x16\xc3\x8f\xc5\xf0\xeb \x12 \xc5\xb4\x14\xd3\xd2|\xb5\n\x03>cY\xacC\xcdc\xfcb\xc5\xa7\x19\x9f1?B\xe8\x0c\x08g\xb1\xfa\xd3|Q\xbfP8\x87\xa8p\x0e\xd9\x13-\xc8u\xd8\xefw\x05\x0d\xdc\xd6p|\x8f\x85\x05f\x89\x1e\x8fE\xdfC\xf16\xe9y,\xef\x0091AS\xddf\x11.\xe5\x95\x16\x0e7\x18,ey^\x7fl>T\xe8\xa5\xc8q\x93\xea\xe0Q\x80\xdd|%\xae\x89\xe4|\x0d\xc4\xce?>b\xe7\x9d\x11\x9b\xa5At\x1ar\x8c\xbf \xd9\x80\x9ba\xf9M&\xde\x16^Ja\xe8\xf7J\x887\x1cp\xba\xa6\xad\x0e\xdey\x8e\xf1\xeeN\xe4/\xc1\x98\x95\xb8\x9fC=y\xab}\xb1\xedA\x1c\x1cL\xe3\xa8\xb8;qu\xc5\xaa)\xd0\x9bri\xb7c\x9fz\x94\xd1\x99\xd1X\xa7\x16>\x00\x14\x7f)\x90]\xcd\xa4\xa8\x0e%|(\xf1\x8bCw\x0b\x17\x05\xfa\xafk\x12\xb9\xc6\xbbL\xf5\x07\xd0f\xe9\xf0q6q\xeb\x0c\x86>\x01I9\x01\xb1\x05\xd8\x91IY\x80\xa4\xbc\x8cg\xbc\x95\xa3\xb8 \x0cm$\x03\xf9\xca\xef\x95`\xfc\xc2875\xd6V@\xeb\xbbZ;M\xea\xc6\x81UL\xba6*\xf1\xec\xd7_\xcb\xebpd\xf8\xcd\xd61k\\\x17\xf8\xa5h\x1d\xb6\x18\x90?X\xf8\xe9\xab\xf3\xa8\xb8[\x1ev\"\xfd\xac\x99A\x1b\x00\x83\xd6\x8d5c7e\xcf\xd8/\x80t\xc5\xd1\x1a[4q:\xd0<\xe5\x18\x07\xb4\x06\xbb\xbe\x9b-\xdd\x02A\x8a\x95\xa1{X\xe6\x05\x83\x9e\xeb\x17\x8fm\x8f\x18\xd4J\xcc<\x07\x7f\x1e:\x8c\xdb\x97\xa6Xp\xbf\xf1\xf6\xd5\xcb\x01\x9eu\x83\xf9\xa55\\\x80z\xd6\\i`\x1f\xaao~\x1d\x96Z\x1c\xc1\x8eY,\xcf\xa6\xfd\xf2\x1a\xe8\xf2\xee\xb2\xdd\x9cL=\xb7\x862\x157\x1f[\x8fYV\x99\xe9\xac\xfd(\xa6dAb\xef\xec@\x1f\xa9\x9d!*:\x1e8\x1bC\x8f\x15\xb3\xa7\x9c\x87T\xe6\xa6\x80\xd5\x80\x1d\xd6\x8f\xa5\xb0},\xf8\xf4}\x01\xc6\xd4c'y\xc6\x12>\xe5\xc1\x19\x9f\xb1_I\x99\x9f\xb1 \x9a\xf1\x0b\xf6+\xe9\xa0\xe7\xb1\x13\xf4\xed\x05\xf7\xa4k`\xb3\xcf\xee\xf7\xb2\x04\xa5o\xd1r:\xfc\xf6\xe9`\xda\n\xe2\x9d\xbc\x8f\xeaWX\xd3jo\x05\x81v;QG\xd6\x99\xc6vY\x9f\x96\xa5x{\xeb-]t0\xddT\xcf\x0d\xa7\xf4\xff;\xac\xc6\xd7\xf8\xc5\xaf\xd7\xe44:\x1d\xe0\nfa\x1cv\xc4\xd9i\x97f\x99lz\x0en n\x85\x0f\x99\x17\xa0\x9e\xb7\xd6i^\x12\xdd\x16\xcc\xed1%\xfc\x02BK~oX\x9fv\xc6\xfa\x10\xb0\xbe\xee`\xae\xfe\x18X\x1f\xde\x00\xeb\xc3[\xc7z\x85\xc2>:\x93\x04\xfe\xa9\x8dk)V\xca\\\xac\x94N(-J\xaf`\xa5\xcc;\xae\x94\x8d\xd5zpz\xcf\xe5\x99l\xdeL\x8e\x8f\xa2O\xfdY\xa1\xc2\x10\x195\x9e\x0da\x80\xd7\xf9{L^\x139\x8a@\xd3\x06\xb7J\xc8Z\xfa%\x13\xe5\xa7K\xd6\xef\xb0L\xcf\xe4\xa5\xb2\x95\x93zln\xae\xf6y\xb7\xd5.\xe0\xb6(\xc0\xb6\xf8\x05\xadc#\xf5\x83vE\x92\x99>\x87(\xfcQR+y\xfd\xef\xa0pR\x7fu\xc5\x86\xec\x1ed\xc0K\xc6F\x8c\xc3\x85I\xb8\xed\x07\x0cZ\xa5\xb5\x0f\x96o\xcfhJ\x02\x17g\x97J\"\x81\xe8\x84\xe2=\xf0\xd8\x1c`\x92\xa37\x1ep\xb1\x13#+\xfa\xdc\x0f\xc3 :-D\x0e)\x83\x95\x03\x8e\xb9\xd9,H\xf84\x0b/Y\x90\xb2(F65N\x04\xd18\xb9\x84\xc0*_\xaf\x92x\xb5)\x88N\xfa5[\xf9\xd3\xf7\xfe)\x1f\xb0\xafR\xce\xbe.\x1a\x1c\x00\xc3Z\xfct\xdc\xaf\xc5:\x9b\xfaa(\x9aX\x0e\xd8\x1b\xee\xcf\xd82N\xb8\xe0\\\x17Y\xb6\x1a\xdd\xbb7?\x19,\xf9\xbd<\xe5\x9bP{\xb3\xfc\x8eu\x91hx(f<\x19\x07\x13v\x007+\x8b\xcb\xa1*\x0d\x89\xc4\xbb\x05/\xcf:\x15\xa2\x19\xa4`\xe5(\x18\xef\x94%\xfcgy\x90\x80TQ?O!\xdf\x1dd\xa9$\x067b\xdc\xa9\xe0H\xdb\xa5k\xa6+\xe61\xbc3\x92\xa1\x0d*\xb4^\xba\xd6B\x1co\x10\xd7\xdd\xd5#\xc6\x10c,\x91\xa4\xdbm\xee\xa4v\x9b\xbb\x8b\x10\xe11\xdb\x80\x10\x91A\xed\x16ucMV\xeaBb\xbcB\xadM\xe4\xd0\x0e\x9a5nvS}\xea\xc8\xf5\x82\x17\x9f\xae7\xbbAx-\xf0cc\xe9\xf8\xe3\xe1\xa4\xd3@X\x17\xd9\x8e\x0d\xa3\xa5[\xd8\xf6\x05k~\xbf\xeeu\x96&s\xa7\xcdWL\x95\x9e\xc5\xba?\xd5\xe5\x85\xec\x80I\xbb(\xe0\xfc4\xf1\xfa\x1b~zx\xb1*\xef\x81\xf7XGG@\xf2K\xca\xf4\x08\xaf\x9c\x82;\x89\xb7ZJ6\xee\xfd\xea\xaf*\xd7\x1b\xef\xfc\xd3\x1e,\xe0\x16k\xb2L\xef &\x9bpD\xa7W\xa2\xe3\xaa\x07\xf58r6\xe0^\xda\xddwiN\x98a,\x05\xb5+UZx\x07\xd9\x84\xbc\x9a\x9bSR~m8\x01ht\xb0T\x99\xa1\xcf\xfcL\xfb\xfa\xcc\xcfx\x8f\xc6J\xa3&\xcemY7\xe1\xa7\xfcbE\\1\xb6\xa1Q7x\x9e4#+-\xd0/v\xec\xe6\xad\x1a\x91\xb6i\x1bn\xdd\xf6\xd4\xe8\xfd\x088\x9b\xc6=\xb4y+\xc620\x03M\x05$\x98;\xf4\xa8\xa9C]iL\x9b\xd3\xb7\xea/YIs>\xc9\xf6Q\xc5V\xa6xl^;\xa9\xb0}\xc1J\xcf\x07z\xc2\xdc\xd3\xa4b7\xf0C\xd0\xe4x\xa7P\xe9\xdfR\xfb\xbd\xe1\x83\xc1\xee@z\x1e\xb8Vkg\xa5\x8f\xe9\xdd\xfb\xee\xa0\x88\x98@Y\xf3\xb6\x19\x1b\x07\xb2\x9d\x07\xa4}\xef\x83\xfb{\x16\x83]\xdfQ\x92\xb9\xdb\x18\x87aG\x8c\x9d\x1fn\xd3n\xa3\xeb&\xca\xa2\xb3\xbdep\x11Di\xc7I\xad/xuf\x19\x13\xd2\xc3\xd4j\xef\x8b\x9f\x1c\xb1\xdeg\x87\x9f\xbfxyx\xfc\xe5\xb3\x97\xbfe\xf1\xad\x90f~\x16L\xbb\x95])\x0c\xefTZ\xfaS]\xa3\xc2\"\x08g\xcf\xd7\xadu\xca\xb3\xcf\x90\x1a@\x84\x9dj\x9d\xe3/\x0f\xdf\xfc\xda\xe1g\xf6\xaa/\xa2 \x0b\xfc\x10\"\x17\xadY\xf5\xb9\xd6\xddu\xaa&<\x82\xbb\xb4\xaa\xc6\xab\x97\xcf\x0f\xad \x94+\xe8\xc7A\x18~\x89\x8eK;\x80\xa4\xa8\xf6Y0\xbbF-\xf1\xb17\xa8($@j\xc3\xa3E\x9c\x0bp\xc86\xbeZ\xcd*\x10\xed:\xc8z\xbd.\xfd\xfd,\x98]\xa7\x1a|.Zv\x86\xcfW/\xdf>\xfb\xfc\xf0\xf8\x9asB\xd5^\x1b\xc8T#k\x0c=\x87\xa2\xc5\x1c\x8dX\xef\xd5\x8f\x0e\xdf\xbcy\xf1\xd9\xe1\xf1\xa7\xcf\xde\x1e\x12\xbc\x8f\xd9Nh%:\xb0\x10\x93\xe0\x8c\xcf`5}\x9e\xc4\xcb\x86\x15\xd9\xe5[S\xeb\xb7fA\xba\n\xfd\xcb\x97p\xe3\xbb\x13G\xce\x80\xf0j\xf5X]\xac\xab\x1e\x8b\xd6H\xd1\xd4\xce_\x13\x1cgK(\xb9B\xed\x11\xa1\x9a;\xaa\xb8a\x8b\xfa}W\n\xb4\xc7\xd1d-\x15\x17AJ;\xf7\x9b\x0f\x8c\xda\xe2\x88.C\xa6\x19y\xa4\xabP\xd6\xd0\xb5k\xf7\xca\xd2\xa1\x1b\xf4\xc5\xd8;\xd6\xe8N\xad.8\x13\xaa\xa7\xed\xb3\x85c\xa4B\xcb#\xb2\xf4Z\x08\xa9\xed\xc6kt{\xa5q\xa9\n\x84E\xda\xba\xf0+\x98\x87\xce\x1d\xd8\xe8^\x94u[C\xac\xba\x8e\x82\xa8\xbdU\xf5(>\xaf\xdd\xa6_=\xd0\x9f\xba)`\xd4\xd9\x14\x90)\xb1\x97\xe0\x16A\xd3\xd9\xed\xb3\xe2 \x9c\x8d\xd8cw\xc1\x88\xf6y\xe8\xa7\xe9\x88\xfdV\x9c3\x1f\xf4!\x19_\xae\xb2 :eY,C\xcf0\x9f%<\xe5\xc9\x19\x9f\x01\xa6\x88\x9ez\xec\xeb_I\xbf\xf60\x16>n\xd8\xd1\xd1\xdd\x8c\x9dp\x06\x11\xf2A\xb4\x0b3\xdac\xef\xf9\xe5\x80}\x86M\x05\x19\xf3S\xe6G\xa5\xc1\xb4j\x11R\xb8?{,\xca\x9c\x07a\xc8\xd2L\xfc=\xe1\xcc\x9fNy\x9a\x06'a\xd1\xb8n.~\x97vRo{\x94\xd8\x0b\x80\xd6A\xea\xa5\x1e\x90~\xad3;L\xe3\xb9Cs\xa2\xd9\x01\x0b\xc7\xd1D\xca\xe9\xbb\xf7\x83\x95\xa7\xcb\xc0\xa1\xb6C\x10{\xe4\x1e\xebu\x9e_1\x95\x02\xb2\x97q\x9eh\xb6\xc2\xa0 \xcb\x16~\xc4\xe2h\xca\x07\xec\xdd\"H\x05\xe4\xe7a0\xcd\xd8\xd2\xbf\x14s3\xcb\xb9h\xc9\xc7Mm\xd0C\x07\xc8gq0s8\xc6\x95_\xc0\x8b\xc7\xa8\x80S\xb6\xa7Y\xff\xab?\xf2#\xb4\xc7\xe5\xfa\xd3\xde\xac\xbd\xc4\x07\xa42\xeb\xd04?\xcf\xe2\x93 \x9aU-\xee\xd7PA\xd3\x81u\x98f#\x98\xd6\x11+\x13\x88\x95\x8e3;b\x9d\x10U\xee\xdc\x11\xc8Te\xe1\xd0Ml\x05\x8f \x12\xc2\xdc\x9fr\x1bB\xc5g`\x87Q\x9a#\x86eXj\xc9\xb3ENDg\x9f\xe5Y\xfci\x10\xcd^\xfbAb\x89TY\x8dR\x19\xd5\x97\x99\x0f\xcbl:@\xee\x1f\xa6T\xbe\xbb\xa4\xbfw\xf5\xc0\x1c\xd7\x1bC\xbb\x8a\x1cC\"\xb6\xedJg\xf2^h4\xce;X\x8e\xad`\xd8\xc6\xf7\xda\xf5\x80sg\x85!w\xa6fm\x97M\xc7\xf9D\x0c:li\xa9\xc1\xef\xb3\xfe\x881\xcd(\x02\xd8\xd6S\xd6d7\x0d\xc6+\xe0\xac{\x05\xb7\xdc\x86H*\x06\x8a\x92w\xdb\xc1\xc0P\xbfmR\xf4\xe7L\xba\xcfN[\x03\x96\xeaO\xe0\x80\x13q;\x13\xb0\xac\x13@\x99\\_\x81_E\x85\x11\x81 \xd1l\x15\x87\xc1\xf4\x92\xfdJ\n(\xfd\x9e\xc3\xeb\xf9\x82G\xb8\x02O\x81\xdd,\x96\xa6\xa8\x02\xc4x\x89\xb3\xdf\xd0\x9d\x03\x96`\xe4\xd2\x85#^\x042\xb0\x11\xd5C\xf4\xe0\x8be\xcf\x8a\xb2\xdd\xa0/\xddA\xcb\xda\x1d8+(\x1ec\xd0\x93\\|\xc7+*7\xd6m\xe0\x15\xcc-\xbe\x13\xa1\x9fY\xf7\xfb\xea\xb1$p\xa4AY\x83\xaf~\"=\xf3Xo\xc9\x93S\xaeB\x1c\xbd\x8c?\xcbW\xa1\xd8\x90\xf9o\xf2\xcb\xd4qG\xec\xb9\x1f\x89m\x17\x8a\xb1(\x8e6\xb1\x99\x14\x08x\xe62\xe2\xc8\x82Q\xca*:=`\xf8Z\xbf\xf5.\x91\x06-\xf8\xb5\xec<\x96\xf4;\xc5\xed^p\xfa\xa9\xbf\xe4\x18\x06]l\xbd\x9dv\xd6\xc7\x02D+\xf0\xf0*\xf6\x044\x92SE\xa7~\x9eJk\xb2\xf3\xb8.\xb6u\\\xb1\xc5\xd5\x0e\xd3\x8e\xab8\x0e\xc9w\x8b\x15P\xe9\xa7\xd8\x1c\x17\"\xf5=\xbfL\x15\x0b,\x19S\xcb\x0dUeB\xd8 -\x16m\x96\x88:{i\xdd\xf70\xb04F\x83\x15\x10\xf1\xcaH\xb2\x96{\x8e\xe2\x81C\xad\xa5\x96]=\xaaL\xe2\xca{(I{\xe1\xd2\xd6#\xb2\xef\xde\xe0^\x98\xf0\xd5\xcc4\xa5\x9b\x13\xe3\x14\xc0\x0b\x1dV\xa4\xdbz<\xbb1\xe0\xad\x00\xb7\x02\xf5\x9a]]\xb6\x1e\x1524\x9e\xa3\x94\xc4\n\xec\xb5/\xd5[1C\xd1\xa9\x87P\x13\xb4\x82\x86)\x83\xd6\xe3\xe3 \x85J`\xe3\xb7\xb1E\x96&H\xaa\x89\xb4\x97\xed\x1d\xac\x88\xea\xaf\xddG\xda\xde\xa5S\x1fO\xac}\x94\xfe\xc1\xa5\x02\xa9\xb3p\x0b\xfa\x87\xf2\xf8d\xc0\xa3\x9f\xe5<\xe7o\xb4\xa6$\x86\xad}z-\x06\xdc\x11N\xca\x16g\xa3\x0e\xb0\xeb\xc3\xea\xd8\x1e\xd6\x97iF\xa2\xce\xb1\xaeT\xd7y{vB\x90\xb6\x12\xb2M\xe42\xab\xa9T\x93\x06sPV\xa2\x89yXP\x91\xd7\xee\xdc\xe9\xf0e\xf5T.\x11r\xb2]\xcf\"\xeag\xfd}\xb6\xdd\xd6>\xab\xc9,\xdb\x8f\x05L\x9e\x88\xb2q\xc4\xfal\xd8\x81O\x85\xe0\x0b\xfbH\x99\xe2\xeb\xfaA\xf8\x00\xe8\xab\"\xda\xad\xa4t\x9b[C\xe7&|\x0e\x0e\xc4\xbc\xca\xbaP6\xeaQi1\x9fq\x19\xcb\xc7>\x90\xc2\xcaWT\xa9\xb1\n\xec\x80Lv\xdcV\x81^\xe0\x10\xacY\x0evuUs2`\xa6\x7f\x85\xf8\xc4\x88-\xc5\xc9W\xa2\x7fq]]\xf0.\xe2\xd3=\xb1\xb9\xe8\xea)q\n@~_P\xc14\xd0\x14w=\xb7\x06\x91\x9c^\xad-'\xde\x04\x84\xe5\x15c\x97\x88\x9f\xb3cOO\xac\xf8\x10\xc1h\xc8Z&\x85\xe22\xa8_>\x90!O\x9d\x95n\x00\x9e\xb9\xae\xc7VN\xe6\xb1S\xf5\xc2\xd5\xcb%\xec\xb0u\xb5\x08\\EP\xc1\xe6\x0bMI\xbd\x98\xe3\x82\xacB\xef\x1c*\xda=\xd6\xc3\xc0\x07pnr\x06\x83\x81`\x98M\xd1\x16NO\xb0\\\xa15\n\xf3\xd9\xd7\xd8\xc0\xd7\x92\x93\x04f:u\xf5\xf1\xcb@%N-I\x86\x9bj\xe4w\x9a,\x93n`\xd0s\xd6\x12\xd3\x0c\x0co\xca\xe2\x91cs\xe6g\xa7zr\x00F\x0cg\xee\xca\xe0\x96\xc3\xfb;\x10\xdd\xf2v\xc7\xb3\xbdG\xdb\xe2)\x1b\x00\xb1\xd5\xc5.Ek\xfd\x12*5Z\x0b\xc1X\x1f\xeby\x96#$\x8f\xf2%O\xd0\x01\xfe\x86%\xd0\xe8)\xef*]Q[\xf3\x80\x96\xb5\x13b\x82\xc6\xbe\x07\xdf{\xbf\x83[\xe9\xb7D\x93\x8e\x9d'\x1b\xcf\xea\x08\xc4\xf6\xd9\xd0Bv\x18uz\xb8\xc1\xfao\xa3E\x80\xb7\x9e\x14A\xe3M\xa3*\xca\x927\x95\xe0&\xf5 >Iyr&\x86.\xce\xdcp\x0bXK\x1a\xc9\xa0\xbc\xe2P\xad\x12{\x10\xd1]+\xb4\x8fvr\x19:\xc7\xd6\n\x92;\xf0\xf7\x02\x91\x8a\x80\xc7\xf0\xcf\x00Bn\xa4\x98[\x8fYP\x11\xf0\x04\xb4\xcb\xa2\xb3\xc2)N@\xc8f\xb6<\x1a\xc4|\xecO\xf0\xe2\xa7xA\x07G\xb6\xbd\x8ai\"\x11\xbd\xc7u\xeb\xab-\x93\xd8\xa6\x16F\x8a\xe6\xbc6:\x08\xca\xaa +\x04\x04E\xc5F\x91\xe9\x99\xe6a\xabY\xf2\x85\x07C\xec\xbamm\xeaO\x06\x1e\xc7\x04;\xfb\xe2\xe5\x8bw\x8d\xc5?\xb4\\Q\xd5No\xb1\xcb\xb2E\x12\x9f\x83P\x05n\x119w\xdf\xf0Y>\xe5 \xeb\xdde}\x96\x81\x1b\x90\x9e\xc4`>c\xc5V\xc9fy\x82*[\x90 \x05\xdfH\xe3\x9b\x17sT\xaf\x81\xd8g\xe5\xa7)j\xe2DZ\"[\x0e\xd2\xb2\x19\x8f]\xc69\xca5\xf8\xc5*\x0c\xa6A\x16^\x16\x0bf\xc1U\xfb\xd8\xe0\x80\xbd\xab'\x81\xfe-\x8a\xc1B\xb0h\x15\xba!\x1a\x9e\xc5\xd1\xdd\x8c\x9d\xfbQ&:\x91\xf2\x8c\xf9\xd2\x01\x81X'\xa0\xbf\x93\xbd\xc2\x8eL\xfd\x08\x0c?\x80\xb9\x91\x86\x83,\x9ek-7\xb9\x96\x11\xd3\x1f -\x10\xad^\xdc{\xfd\xe6\xd5\xa7\x87\xc7_\xbd\xfc\xcd\x97\xaf~\xfc\xf2\xf8\xd9\xf3w/^\xbd<\xee\xb1>\xfb\xd2\xcf\x16\x83\xc4\x8ff\xf1\xd2q+\xa1\xcd\xb5\xe0\x9e{\xee ]\x85A\xe6\xf4z*\x80o\xe3\xe7k\x93\xdb\x15\xbd\x10\xb5\xe8\xed\x86\x01>\xdd\x00K@\xbb\xbfJ\xe2\x13\xf1\x1ed\x0b\xe63\x1c6|v\xc0>\x83 \x12\xcb5\x8b\xd9\xc2\x8ff!z\x99P\x98\xce\xfa\xec.\x8b\x13\x16g\x0b\x9e0\x1f\xd6 \x88\x18z\x08\xe1Ozh\xd6\xb5\xf2\xd1<\x8a_\x82\x8d\xd54\x06/\xa3 X\x96\x06g\x80:\x85yO\x81q\x1a\x9aM\xf3$\x01\xa3\x03\xc0)\x81\x1c~t\xc9\xf2\xe8}\x14\x9fG\xea\xbb\x1e\xcb\xa3\x90\xa7)\x0b\xb2\x1a\x12\x07\x11;_\x04\xd3\x05\xde \xa4>PAZ\x8f%\xfc\xd4Of\xd0X\x8c+\x06\xbf!\xc1\xd2\x0d\xcd\xd1\xa9\x86\xc0\xd9\x13D\xd9\xc1]\x8b&\x86\xd0\xfe95\xd3\xa0\xca\x01\xd3(\x0e\xc2\xf1\x06\xfa\xddEo)\x96\x87\xd83\x0b\x9d\xa4\xd2`\xc6\xb2\x12\x14\xc9\x80\x8f\xb2\xf8*/\xbd\xbc\x88\xceb4\xdcz\xed'>\x84u\xff\xb2\xf0\xb1\x9b\x15\xac\x84\xf4\xf4@\x124\xf0\x16$\xb6\xae]\x97\xd8\xbbD\xd6\x83]#+(\xb2\xf6\\\xf2X\xeb[\x95\xba\xd2v\xa4\xb2\xfey\xf3\xfa\xb7\x1e\xc0\xb5\x05_\x1bj\xa2\xe6\xd8[\x0bd\xb1^\x8d\x82\xff/1\xe9\x15\xbds\x04\xe5%\xa61P3L\xcdU\xf0}\xcf\x15E\x9c\xed\x8e\x9f\x82\x1a\x89\xa6\x0e\xb5\x1b\x81\xa4\xb9\xa5'\xbb\xb7Y\x9cp6\x8b9zc^\xf8g\x1c%\xf3\xc1L\xc9\x1c\x06\xecK\xff=g\xf2*//#\x8c\x94J\x85\xfa\xe6\x1b\xa4\xday\xf7|\x11\xa7\x1c\xa7&\x05\x99\xb0l7\x1d\x10\xc1k}I'\x0b\x14s\x0d\xed\x13\xba\x0d-\xb6\x84\x17\x19\xaaM\x07A\xaa^\xf5\xb8.\x85\xbbd\x1f$\xd8A\x8aB\x91\xe2\\\x9e\xd5\xa2\xa2\xa8\xc1e18&\x88*\x81\xdf^,\x979\xc4\x83/\xbeZ\xdec\x9a\xc7a\x18\x9f\x07\xd1\xa9rx\x10\x80S\xaa\xbb\xac\xcf\x02T\x1a\xdc\xedy\xacw\x17eL\x83\xbb\xe6\xd8\xe1\xc0%f\xef-\xff\x19(#\xf0\\\xe8\x0e\xe6A\x98\xf1\xa4\xe5\xa8 \xc7\xbba\xdc\xdf\xaa\x1da\xeaZ)Y/\xd7e\xc0\x07\xac\xa7]\x19\x04\x81\x04^\x94,J\x1d\xb0\x9e\xf2\xeb\xd0c\xa3\xe2G\xc0S\x14\x97\xe1\xc0ss\xe0l\x1e\xe7\x118\xa5\xbe\xab&E\x03\x7f\x16\xb3y\x10\x15a\x83\x04\\Q\xf0\xaf\xe4_\x853 \xbcC.\xc5\x1a\x0dp\xd6\xef>\x96\x9dD\xff\x13'\\J\xeaf\x83\xbbuw\xca\xb7\xbf\x1b\xde\x1aE\xf3\xd6\"\x0euo\x9c]tH\xa4d\x13UH\xa0\x1a\x12X\xaed\xa7\x97+)\x0bEQ\xe7\xad\xc8?\xeb\x02(M\xb6y+\x13\xa4W\xacB\xab\xa0\xd0b\xd7\xae\x07\x00/\xe7\xa9:#]>\x199\x8fP\xc4\xfd\xe8\xa1[\xedy\xe4<\xd8\xdb\xead\xe0Y\x1e\xa1\x87\x86\xafC\xe9l\xf0\x91\xeb\xf4\x8a\xd8\xe0\xa4\xad\xf3\xde\x96\xc5\x8a;r\x86\x0f\\\x8d\x8a\xaeq*\xb0\x1d\x084ER6\x8e\xd1c\xad\x16\xbb\x1c\xee\x14@4\x81:\xcdJ\x1c]~\xd7 \xc0\xcdV\x86\xf7~\xe2\xfc\xca\xf6\xd6\xd5Q\xea~\xe2\xfc\xd4?\xf3\xd3i\x12\xac\xb2\xab\x99\x9f\xf9\xee\xbd`i\xc2\xf2\xde\xf8'G\x17\xdb[\x9bG\x17{\x87\x93{\xa7\xf5\"\x01\xb69\xfe\xc9h\xd2wG\xf7N\x97\xe6qk\xdc\x1b\x08Bt\xaf7\xa1\xe1]\x05h\xeaGA\x16|\xc3\xbfJ\xc26a\xd5\x99\xb4\xb5\xf1\xe4\x8e!\xaf\x95\x89cA\x8fRKw\x12\x10j\x05\xfd\x010\xec\xaf\xe6\x0e\x1foM\\\xf6\x94m\x12\xee\x97\x9d\xdc\x95&\xe7N\x04\x12\xc0\xa5\x9fM\x17N\xe0\x8ad4\xd9\x11\x873\x96\x0c2\x9ef\xe8\xb6\xa4\xe7\x9f\xc4y6: \xfd\xe8\xbd\xd86r\xb8\x1d\xae'V\xbe\xb3\xa6\x15e\xb9<\x1e\xd8\xec\xff\x1f\x0e]#\xdci\xc3f\n.\xa2\x07Y\xfcE|\xce\x93\xe7~\xca\x1dpG\x02\xfa\xa3\x03&\x90\x94\x8d\x0c\x1f\x1f\x96\xe5\x15\xaf7\x84]\xca\x9e>r\xb6\x1f\xda\x96\xaf}z\x95\xb0\xdbI\x1c\xeeVG\xb3\xe6\x1a+\xbb\xb7W\x17]|/\xa6\xe4`H\xdelF\xde\x0d$g\xff\xbf1y1\xc7\xf5 \x8e\xba\xd9\x8cw\x03t!d\xb9\x96\xe5\xb8\xbe\xa2)\x84\x13\xeb\xc1r\xa3g\x8f\xf2\xaf\x0b\xcb\xea\x9aCh\x96\xf5\x80\xc5\x03\x19\x94@\x814F\x12\x18 \xd1\x90\xe2y\xa34\x93\xa8\x0e\x96\x91hd\x91\x0d\xa6\x0b?y\x969[\x16%L*\xcb'N\xe4\xb1\xa1\xb2P\x82\x08!\xd9 \x0d\x83)w\x1a\"\xb0\xe4c>\x01\xc5wU\xd8\x7fm\xda\xbb\xfd\xb0\x1d\xc4\xf6cl\x0c;\x9a\x14\xdf\x93\x98T,2\xe9\x02\xea\x80\xc5\x82w\xf7\xd8\x06\x98\x01D\xec\xe9>\x8b\x95Ux\xf1\xa9\xeb\x8e\xe6\xc1^\x9d l\xc1\xbb\x9b\xd0g\x8e\x08\x02\x97\xb4\x92\xf6\xc5b\xe3h[\xbf\xc4Ks\xb65>\xa1\x10\xb97>:\xcag\x0f\xb7\xb66\xc5\xff\xf9|^\xbf\xf4\x96\xa8B[;Xhkgw~t\x94\xcf\xf96\xfc\x9c\xf3m\xf1s{k\x06?\xb7\xb7\xcc&\xe0\xc6\x00|fg:\xc6\xcf\x9c\xd8>\x07\x86~\xe3\x9f\xb4t\n.\xf49\x07#\xbd\xd1\x19\xdf\x85\xe2\xb3\xf9|\xe2\xfe|\xfb\x03y\xc5Oo\xf7d>\x9f@\xc2\xd4\xfe\xa1T~\xa8\x08\xe1sU\x84\x01r\xc5[\xef\xa0V!T\x9f\x99\xf3-\x8e\xff\xe6\x93\x03\x15\xe1\xc9\x91\x9d\xde\xde\xda\x9a\xc9V\xc7\x18\x93)\x9f\xc8\x95~\x85A\xe2\\k\x1b=\xf7\x93\xfaY`\xaa\xf5r\x1c\xa8\xae\x1e\xf4\xf0\x1a<(\x08\xa3z\xfb\xb5~\xcf\xd9\xbe\x0c\x8c\xe0\xc0\xe8\x9c\x83\xfdr\xa40\xe8)F\x8a\xec\x9d\xf6\xae\xbb&\xb8\xe4*\xe7p_t<\xb9\xee2\xde~hc\x08m\xcb\x98\xf2%/G\xdb\x1b\xdf\xfdo\xbf\xf3\xbb\x93\xde\x8dF\xd6\xbc\x9d\xa8\xdd\xdd \x1c\xb1o\x14,\xbe\x0f,\xbe\x0b\xce\x1ez\xbd\x1b\xdd9\xd2h\x9c\x058\x06\x0b\n\x87\x9e\xf1\xd1\xc5T\x1c\x8bf\xbbG\x17\xb3\x87\x9bG\x17\xf3\xdd\xa3\x8b9\xbc\xcc\x8f\xf2\xad\xa1X\x19\xf9\xd6po>\xb9w\xda\x00\xc2u\xc9\xc3M`\xed\x80\xd0\x1a\xa4\x82 \xa9U\xd0\x0c<\x96\xd4a{} \xdew\x9d\xea\xd7{\x7f\xf8;\xbd\x11\xeb=\xab\xad\x9b\xde\x1f\xfe1:\xf9\x8f\xd3\xc9\x7f\x82N\xfe\x1f\xe8\xe4?I'\xffC\x91\xec\x1b\xc9\xff\x88N\xfe\xc7t\xf2?\xa1\x93\xff)\x9d\xfc\xcf\xe8\xe4?-\x92\x9f\x1b\xc9\xff\\$O\x8d\xe4\xbf\"\x92\xeb\xde\xf1{\x7f\xf8\xefD\xf2\xccH\xfe3\"\xb9\xee;\xbe\xf7\x87\x7f\x96N\xfest\xf2\x9f\xa7\x93\xffg\x91\xcc\x8d\xe4\xff\x85N\xfe\x17t\xf2\xbf\xa4\x93\xff\x82H~a$\xffE:\xf9/\xd1\xc9\x7f\x99N\xfeW\"90\x92\xff5\x9d\xfco\xe8\xe4\x7fK'\xffU\x91\xfc\xd2H\xfe\xf7\"92\x92\xffG\x91\xfc\xcaH\xfe\x9f\xe8\xe4\xbfF'\xffu:\xf9o\xd0\xc9\x7f\x8bN\xfe\x0f\"96\x92\xff#\x9d\xfc\xbf\xd2\xc9\xff\x1b\x9d\xfc\xbf\xd3\xc9\xff\x89N\xfe]\x91\xfc\x95\x91\xfc\xb7\xe9\xe4\xbfC'\xff]:\xf9\xff\x14\xc9\xb9\x91\xfc\x7f\xd1\xc9\xff\x99N\xfe/t\xf2\xdf\x13\xc9\xf5\xd8\x01\xbd?\xfc}\x91|i$\xff\x01\x9d\xfc\xa7D\xf23s9\xfc\x9eH\xf7\xcd\xf4\xbf/\xd2\xdf-\x8c\xf4\xff*\xd233\xfd\x1f\x88\xf44\xad\xa7\x7fK\x93\xe5oi\xfa\xfb-Mh\xbf\x05\"n\x90\xb7o\xff\x04\x9d\xfc'\xe9d\x80\x80A\x0c\xbf\xfd3t\xf2\x9f\xa3\x93\xff\x02\x9d\x0c\x84\xd6\xa0\xa8\xdf\xfeY:\xf9\xcf\xd3\xc9\x7f\x91N\x06\x12d\x90\xe5oij\xfd-P&\x83Z\x7f\xfbW\xe9d \x13\x06\xfd\xfd\xf6\xaf\xd1\xc9\x7f\x83N\xfe[t\xf2\xdf\xa6\x93\x81\x04\x19\xf8\xf6\xed_\xa7\x93\xff&\x9d\xfc\xbbt\xf2\xdf\xa1\x93a\xcd\xfe\x9a\x91\xfc\xf7\xe9\xe4\x7fH'\xffc:\x19\x16\xe7\xa9\x91\xfc\x0f\xe8\xe4\x7fD'\xff\x13:\x196\xfb_7\x92\x7f\x8fN\x06\x1e\xc0X\x98\xdf\xfes:\x19\xb6Xc\x07\xfb\xf6_\xd0\xc9\xff\x8aN\xfe7t\xf2\xbf\xa3\x93a\xfb66\xb6o\xff%\x9dLo\x9a\xdf\xd2\xbb\xe3\xb7\xff\x9eN\x86\xed\xe47\x8cd\xd8N~j$\xc3v\xf2\x9bF\xf2\xff!\x92\xdf\x1b\xc9\xff\x89N\x86\x9d\xe0\x0b#\xf9?\xd3\xc9\xbfO'\xff\x01\x99\xfc\xdd\x1f\xa3K\xc3.\x13\x1a\xc9\xff\x85N\xfe\xafd\xf2w\xbfC'\xffq:\x19H\xaf\xc1\x8d|\xf7'\xe9\xe4?M'\xff9:\x196\x01\x83\xa5\xf9\xeeO\xd1\xc9\x7f\x86N\xfe\xf3t2\xd0o\x83I\xf9\xee/\xd1\xc9\x7f\x85N\x06Bm\xf0\x17\xdf\xfde:\xf9\xaf\xd2\xc9@c\xdf\x18\xc9\x7f\x83N\xfe[t2P\xcd\xc4H\xfe\x9bt\xf2\xef\xd2\xc9@\xa8\xdf\x1a\xc9\x7f\x97N\xfe\xfbt\xf2?\xa4\x93\x81\"\x1b\\\xc1w\x7f\x8fN\xfe\x07t\xf2?\xa2\x93\x81\"\xbf3\x92\xff)\x9d\xfc{t2\x90\xde\xccH\xfegt\xf2?\xa7\x93\x81\x98\x1aL\xe1w\xff\x82N\xfeWt\xf2\xbf\xa1\x93\xff\x1d\x9d\xfc\x1f\xe8d\xa0\xb1\x06\x0b\xf9\xdd\xbf\xa4\x93\xff5\x9d\xfco\xe9\xe4\x7fO'\xffG:\x19H\xef\x8f\x8dd \xbd\xe7F2\x90^\x83\xc7\xfd\x0eH\xaf\xc1\xcc~\xf7\x9f\xe8\xd2@z\x7f\xdbH\xfe\xcft\xf2\xef\xd3\xc9@L\xbf1\x92\xff\x0b\x9d\xfc_\xc9\xe4oav^\x98\x1b\x0f\xc0*0v\x9e\xef\xf0\xb8fp.\xdf\x01\xb3\x14\x9b\xe9\xc0X\xde5\xc9\x1b\xec\x1bi\xa9\xd9\xb5)Hi\x8f>\xd7\x16rw\x12\xb0\x11\xce\xd4F`\xa3[\xa9p\x03\xc9Z=\xf6\xa3\x12;R\x96\xdf\x84\xc4M\x9am?l\xf7\xbcG\xabT\n\x0b\xc5}\xd0+x\xba\xea\x04u\xf4\xfa\xc0AA%\xd5\x10~\xa9\x86\x80\x00T(\x87\xcd\xba\xc9a)\xb5\x01\x18Tlmm\x1e]l\xcf\x8f.v\xfc\xcd\xa3\x8b\xfb[G\x17\x0fN6\x8f.v\xb7\x8e.\xf6\xc4\xcb\xde|\xd2\xbfw]%\xa3\xeadt\x93N\xfa\x9b\xdfL\xc6\xcf6\x7f{r\x05\x7f\x7f\xbe\xed}\x80\xb4\xab\xf1\xd6\xe6\xa3\x89x\xc5L\xf9\x02\xa9W\xe3\x9f\xe0\xcf\xad\xcdGlr\xef\x9a\xdd\x8f\xd0Pb-\xb5O\xa1\x939:\xba\xf0\xa7GG\x17'\xc3\xa3\xa3\x8b\xd9\xde\xd1\xd1\xc5\\\xfc\x01\x01\xab\x008B\x1c@\x8e0\x07\xa0#\xd4\x8f.NP\xe0\xba%\x05\xae\xbbsvt\x94\x89\xea'GG\xa2\xae\xbf\x05r\xd9\xf9\xfc\xe8(::J\xa0\xd0\xf6C\xfc\xf7\xe8\xe8(\x1f\xee>\x14%\x86\x0fA\xf9 \x1a\xc2\x7fC\xfc\xb7\x8d\xffv\xf0\xdf}\xfc\xf7\x00\xff\xed\xe2\xbf=\xfc\x87mn=\xc2\x7f>~\x01;\xf7@\xfc\xdb\xd9\xda\xda\xaa\x11\x18\xd46\xf5X\x9fE\xac\xcfz\x16M\xd2\xac\xdf3\x17\x1cH\xa1\xb7\xf7\xe4\xb0\xf7Nh\xa5\x91\x98j\x01\xd4\xb9\x80\xd4|\xf7\x08\xa5\xddG\x17\xa6\xea''5Q\xaak\xa0\x18\xa9}\xd0\xda\xf4\xb3\xcd\xdf>BA;H\xdaQ\xd4~t1\xe36u\xd3\x1az\xad\xf0Zz-\xd0\x18\x8d;\xf7k\xae)\x98\xfcB\x0d\x96S\x8a\xa4\x95Vt\xda\\t&\x8b\xae\xa9>\xb8\xb2\xa9\x12\xdd\xba2naU\xc6\xcd,\xca8R\xf5\xc8R\x8f\x85\x9d\xf4s3Z?wV\xd1\xcf\xd1\xed\x89\xbc\xda}\xcbe\xa9b\x19OQ\xa3\xa7\xe0\xdf\x17`\x03\xc5\x95s0\x9a]\x85\xe1\xd5\xf2*\xe1W\xe9Uvu\xc6]\xf7@\xaa\xef\xc6\x89\xc7\xa6\x1e\xeb\xfd\xb0g\xaa\xff\xd8\xcah\xe8\xb3\xab/\xbe\xb8\xfa\xf2\xea\xcd\xe1\xd5\xdb\xabwW?:\xac5\xc4\xfalnk\xac\xec\xdf\xbcK\xffT\x8d\xb6\xcf\xf79\xc0\x1d\xeb\x87\xd7\xa6\xec\x1b\xce\x06\xd8t \xea\xa6l\x10\xc0\x14\x97\x1d\xb0\x15\x18A#\xe3\xef\x17\x0eG\xd9Z\xa8S\xdc\xb5~d\xbdk}o\xfc\x93\xc1\xa4\xff\xc3{\x03~\xc1\xa7N,z\x10\xc35\xb1\xf2m\xf0\xe2\xf0\xf8\xf5\x9bW\xef^\x81\x91~\x0f\xac\xb8{\xe8\xc8\xd1I\x93\xa9{<\x1c\xa0E\xd3\x88\xf5z\xd7\x85\xc4F >\x18@`\xd6k\x8c\x14\x91~\xcf\x1d\xf7\x8e\x8f\xa7q\xc27\x7f\x9a\x1e\xa7\x0b?\xe1\xb3\xe3c\x9b\x95\xfdu\xa5\nv\xdf6\xed2\x83\xf6s[7\xb0\xa9\xad\x01\x88\xcb\xc2\x87\xcd\xe3\xce\x1de\xde[!JcN{\x05)\xe9\xd2\xe6>\xcb\xd8\x01\x1b\xb2\x11l\xda\xd7\x05\xbf\xa0\x9e\xc4 \xeb\xf88\x8cg~\xba8\x16{\xfdqqg\xe8\xf8\x988v\xb5\xb8OX\x17\xb9*PR\xf0\xa8\x02#\x983\xc7pZ\xcc\xb4\xf3sf\xc0\x8fULN\xf7\xd1\xa6\xb4\x98\xee\xa6@J\xb2VPx\x15\x86\x95.\xbeP\xd8\xfd\xde.\xf0\xbf\x7fx\x16\xc6\xe7\x07\xd5+>0\xc4X\x1b\xf8\xed\x0e\xb4\x01\xcb\xda\x06\xd9\xe4=\xacu\x9c\xe5\"\xeaW\x17#rdC\x8fEb\xe8\xfbh\x8d\xaf\x89\xd82i\x9d\x9c!\x83pS\x02\xd1\xc6\x96\x8c'\xb7\xc4\x88\x0cw(\xf6\x18\x83\xd7h\xcc\xd8*\x0c\xa6\xbc\x0d\xf2\x9d\xd0\x8bf}\x13D\"rN6\x9c\x88=A\xc7\x11N\x04\x9e\xa0\xd4\xd5\xd4M6\x14\xebm\xb0\x8a\xd1WD\x89\x8f`\x1e\xef\xb1\xcd\xcd\x02H\x1e\xdb\xba\xd6\x9e[@\xe9\x174z\x1c\xbb.\xba\x1dG\x93\xf1\xb0m\x0b\xba\xd5\xa1\x146\xaa\xd5\xb1\x08rW\xb91\xf6\x11\xba\xd2u5\x9b\x80\x8d\x01\xb0\x91\x15\xb0\xb1\x04\xac\xd3\xefkH\x12a\xec\xd0\xb1\xf8\xf0\xc4\x85\x08P\xe3X\xc0[F9j_\xdb\x0d\xc3\xddn\x1d\xae\x0d\x89\x12\x15\xf9\xcd\x95G+\xdb-\xa1\xebr\x01\xad\x14\xc9\x8e\xdf\xd2S\x1d\xd9\x9d\x1e\x9e\xe8\xd1\x81\x1b\xf0\x9bQ\xbe<\xe1\x89\x96\x90\x02\xe7\xa9%\x9c\xc4q\xc8}\xe9\xf4M\xf0\xa6\xc7\xc7@\x89\x8e\x8f{2\x10\xc0Hs\xce\xf7}\xceFe\x1d\xc0d\x9c\xf2\x0eb\xfc\x8f\xdc\x07\xdc\xa1>f\x1f\x1a\x16a\xd9\x0fz\x05F\x80\x8c4e\x03\xc1\x034\xeeU7\xdeHnk\xc8\x8a\xc9\x8d\xf7fK\x8f\xb6{7\xae\x8eI\xe5\xdc\xfdV\x90X\xa6\xa5(\x80{\x10\xe9u\xef\xac\xe2w\x9d\xbcI\x06\x8e/b's\xa9\xfa\xaa\x8dT\x11\xb8\x1d\xa2\x05&o\xaa\x05\xe0{(j\xec\xbb\xfe\xc8q\xa4N>\xe6\x13\xb8|\x90wu3k\xa6\x9cI\x8f\xbc\xbc\x00\x87\x95\xf3\x0ea'a\x07,\x1f\xa7\xc0C\x87\x82\xc1\x0c F\x9a\xb1\x1bH\x03w\x87\xf5[ \xf2\x02\x84!`AL\xd8~\xd4*A\xb2\x12\xc6\xd8F\xa3\x87\x15&\xe6\xce\x1d\x96\x8d\xb7&\xe3\xed \xde\x19\x14\xef[\x82\xbd\x13/\xc3\x89\xd8\x82\x8ao5\xdd`\x8e\xa4\x13Q\x88\xb6\x16QAB\xaf\x0d\xb5\xa1qwF]\x8d\xa3\xa064%U\xdbm0\xc4\xaf\x0bd#\x80\x99\x02\x1d\x91n4\x8d\xe1\x0b\x04K\xcd\xe4)\xdbg\x1b\xb9y8,\xce\xf4\x85\xdf\x98\x8dZ\xfc\n\x10\xb0\xf2\x8a\xc7\x03\x96nnZ\xa5\xabs\xd1\xbdqjq}=\x85`\xa18\xbbs\xc1G\xc0\x166\x9e\x8f\xb7&\x02\xb97\x1c\xf1\x06b\x92\xd2\x93\xcdFS\xac\x0f\xe8\xdec\xd6\xef\xa7\xec \x0b\xad\xbdZ\xb1}\xe6\xa8\xae\xb9V\xe7i3\x10\x0d\xaf,\xb9\x0b1IV\xaf\xde\xc5\xd0l\x04\xa5\xe6\x90\x04B\xdco8\xab\xe6\xd1\x8aG\xc6}\xb7\xd3\xbe3\x86Q)\x1bBQ\xe7.\x94\\\xb2}\x96;3\x8f-<\xb6\xc2U\xe1\xb13\x0b\xc5\x04\xba\xabwy f\x12\x0b\x8f\xcd<\x16\xb0+y_\xeeL,\xcae\xf3\x08\x1afP\xd5\xba\xc1\xa1\xad\xf5\xeai}J\xea\x07HT\xd1\xacu\x86\xbc\x01\x8b\xd8~\x04\xca:\xf3\xb5\xa2\xac\xe4\xd5o\xbd\xc3\xfa\xc7T\x7f\xbb\xf1x\xb7\xf4\xad\x9b\xf2r\x16\x8d\xe0C\xea~\x9fH\xaf\x97\x07b\xbd\xd5\xead\xa1\xeb\xa9\x8c \xbfLy\xd9\x8a\xe7ft1\xa6\xb1G\x91\xa5\x15V\xf0Gb\xab+\xdcT=a>\xdbd\xc3bM\xe6\x95\x83\\\x15\xd3\xfb\xfdH\xa2\x90H5\x9b7\xc6!\x17L\xe0\xe4\x1d\\M[\xf8Z\xc5\xd6\xde\x90\x93\xb5n\xc5u1\x9ade\xb7\xa9x\xa7\"\x9d\xd2\x1c \x14\xaa\xab?Sl\xbf\xaeq\x08ew\xea\xcdL%\xdfTO\x9f\x9b\x9c\xc1J\x0f\xac\xfaLy\xf0\xac\x9b\x97\xcc\xaa\xa5\x12\xff\xb2^b\xa1\x97\xc0M\xbb^\xe4\xec\xe6\xc2S\xc5\xa2,=v\xea\xb1K\n\xffO\x04+\xe2PG\xa1c\xc8\xc9\x88\x9cs\xb6\xcfN\xd8\x01\x9b\xb1\x11\xcb\xc9\xba\x87l\x9f\x1d\x17%\xa86.\xc4^/\x1a:\x17\x9c\xcd\x8a\x1d\xb0\x05\x1b\xb1sW\xfc\"8\xa6\xb7\xa2\xb8h\xf5P/~h+\xfe\\5|h.\xe7\xe7bK\x0fA\xd7e\xaedX\xa5!\x9cb\x8a\x8d\xd2\\l'\xe0+\xc5\x83A42>\xc5\xf76.\x8a\x06/A*x\xa964\xd7c'\"e\x8a\"\xdb\x98\x98\xb5\x11\x0bd\xeay%\xc3\x1c\xdb\x86\x13\xb1;lN\x0eM\xcc\xf6{\xb6\xcf.@\x0c\\\xb8\x96\xe9\x1d\x1f\x9f'\xfej\x05\x82jb\xa2\xc4\xf3\x8c\xed\xb3\xb7Z\xb5\xac^\x8d&w\xef\xc5\xb8\x9e5\x9d\x07_\xb1}\xf6\x9e\x1d0>\x00Wr \x11mp\x9a\xfe\x9a\xed\xb3g >-\x8bg4[d\x05\xf6\xa9\xf3\xcac\xaf\x15\x1c/\xdb|^\xd3l\xd0\x06L\xaac\xb6\xee\x9b\xd3w\xfd\xad\xd1\xd8\xea\xe4\xc1o\x9b6\x96\xd9\xdd\x1ev\xf5\xe3zv\xcbf\x1du.M\xb7\xef\x80\x02\xfel\xe6\x80w\xe1\x1a0\xc4\xe3k\xf4\xcd\x9f\xcd\xc0\xabP\x99\"\xb6D4\xca\xf0\x0d\xfb\x8b\xa0jj\xe1\x93\xf0\xad\x037\xba\x99\xae\xa6\x13O$w\xd3\xc8\xed\xb4s~\x9f\x8cX\xfb\xb7\xec\xbae\x00\xbb\x93\xb5}\xc2\x8a\xd06/I\x86\xb9\x93d\xf5\xb6(7\x17\x14\xdf\x90K\xfc\xafo\xf8\xa9L\xaf\xb7\x13\x9a\x1b\xbb\xe0\x01\xb6\xcd\xed\xbf\xd8\xa3?E o}\x93\xae\xf0\x03\x9f\xf9\x99aiZa\x05\xc0\xa3e#+\xf0\xa5\xbf\xa2\xf8\x00-\xd8\xfb\xf2\x84\x1bM,\xf5\"h\x97R/r\xaa\x17y\xcb\x0dn\xe3\xb2\x92\x0f\x12\xf0z\x91\x93J\x11\x10\x81\xd7\x8b\x1c\x1b\x8c\xcf\xa7\xf9|nv\xf8\xbc\x066\xffG\x01?\xaf\x17:,\x9c\xaa\x15\xeb\xde\xe2\x9b\xea\x02\x18\x83\x03v\x88\xfb\xc2\xabyg\xd7k\x8aX'\x1e;\xf4\xd8[\x8f=\xaf\xe3~z\x1e\x80\x0f4R\x8e\x05q\xdc\xceGF:\x93; \x1f\x9c\\f\xfc\x0bd\xf77\xc41P\xfb}u\xc50\xff\xd5|\x9e\xf2\xac\xcc\xc7\xdf\x8d\x1c\x88x8x\xa3:\x01\x00{\xd2\x1b \xfe2\xcbCG\x8f\xe9\x8e\x16:\xcb\xb6\xden\xbcu\x04u\x8f1\x18\x0c\xbce\xaeKl\xfe\xf0\xb5\xb9\xf95H_Y\xd2\xcf\x1a{\x178}\xee\xb1>%y\x86\xda\xb3\xc6\xda|\x10\x81Oq1&x\x03O+K\xe53\x1c\xc2\x9d\xe0\x0fK\xf3KK\xa7/\x9b?\x8b\xfa\xa0~\xc5(\xa9R\x7fA\xd7W\xbcZn\xa9vj\xaf\xf6\x0c5\xfd,\xb4\x8b\x8b\x80/sD\xfb)x{\x85\xb3\xde\x86\x12R\x00\xbb\xfa\xac\x15\xfb\x14\xfb\xf6\\\n\x1b\xec\x9f{U\xb4\xf5\n\xe0aa\xd8\xd8\xd5>\x9bz\xecyy\x14\xb5\x7f\xf858\xb4{\x0f\x88\xf8\x1eC\x15\x94\x0b\xb8\x91!|^\nm<\xf6\xda\x02\xde\x13\xfb\x8a.\xf9\xf8\x0b\xe55P\x0cJ\xfe\xb0J\xaf\x99\xb6\xce\xda\x94\xcf\xed[\xf4\xba\xec\x9c\x0c\xe1\x04\xd3K\xcb\xaa\xb8\x195\x82\n\xa5\x0e\x0d\x8e\xfb\xfdl\xc2\xf6\xc1\x86\x9e\xd7\xee\xa2\xb9\x1fC\xc4\xf5q\x86\xd786\xbe\xf6\xb0\xecv\xb3\x8f(\xf1\xc7\xd0\xe4xn\xe9\xb0\x8f\xf2\xde\x94\x02\"\x08@\xd8\x1d\x16\x9bp\x9c\x82f\x8e:\xcb\x0b6hJ\xf2\xffb=\xcc\x05\xe1H\x9c\xcc\xd5tC\x1b\xa1\x95z\x14\xd1\x8a\x04\xe34\x7f\xccV\x0dJ\n\xc1:M\xc7+\x8b$\x7f\xc3 A\xc0\x00^\x9aG\x9aA\xdb\xcc\xed\xa8\x95\x10\xdfX\x80\x190E\xc1\xc47`4\xa9\x0c\x87R4\xba \xa8\x98\x12\xf0o\xd4\xbc\xab\xa6\xba`-U\xf1P\xea\xdf*\xa0\"\x18\xb9P\x1c\x9eV\xec \x9b[!s\n\x1a\x10\x05\x1f\x8b\"\xe4\x12,\x07g\x16\xf0\xf9n!\xfe \xe1B\xe5%\x1cWg\x80E\x1c\xf0g\xc4|G\x9c`!\x15\xd1+\xb5)~u\x05\xc4 ;\x10=\xdc\xdf\xc7\xd3w.\x1bA\xd4\x84vO\xecJb\x90\xa8\xd0\x14\xfc$\xe1\xfe{#\xc7T\xe1.a{\x03\x9exZ\x1a\x92\x83m\xc6\xac\x89>\x83\xea\x07\xf0wi\x03\xfc1\xb0\\Z\xab4\xe8\xcf\x81\x17\xd3\x8a\x99\x03:\x16\xeb\xe6\\|\xad\xda\xc9@F\xec0R3\xd4D\x91\x01\x06\x8fE\xde\xb1.\xa6\x86\x14\xb2,|\xf3\\/{\x8eF\xdf\x08\xfa\x0e\x1bX\xaao\xa1\xc5\x0f\x81\xe0g?\xa8V\\\x9f\xf4\x13\x87\xcfJ|\xc7\xcd!F\x83\xb5 (\xd0\xdc|\x0b\x03>\x8e'b)E\xec K\xacK\xc9\x87\xa5T\x8fZ(\x9e\xcc\xf1\x01i\xd1\xac\xd9 \xc6q\xbf\x0f\xb1\x0e;\x80(\xf8\xde\x00\xa1\xa23\xaa\x91\xf2\xc7.K0(cf\x04'\x91\xbdKZzg7E\xa0\x05\xf9\xf7\xa9\xfb\xe2\x94\x94\xbcm\x0b\xb3\xc8\x1dbiZ\x9eHf\xeb\xc6\xd0\xb5|\xa7\x953[\x170C\xcbMz\x03`>\x84)-\xc1\xe3\x8f\x0b\xf0}\x1e\xc6~\xb6\xb3-\xb5\x08\x80\x80\xb5\xcc\xdd\xfbt\xe6\x8b({h\xcd\x19\xeeZ\xb3l\x1f\xfb*\xb06\x08Y\xcfC\x7f\xb9\xe23{ \xdb7E^\xe5\xa3\x1b[\x9e\x9e\xafaP\xad&\xdd^E\xf0P\xcb+\xe48\xb5\xf4R\x08afp#Q\nr\xea\xb3!q\xc5\xc8\x00\xa9N-MIrj\xc9J\x17TKVB\x9dZ2\x08r\xeaiRxSK\xfe1\xf7\xdf\x17\xfd\xd8\x18z\xeb-\xc1@.\xc1\xd8\xe1E\x94&\xb1\x1fm\xf8c\xb1*o`\xdaK\xfb\xa0\xd85\xac\xdfn\x81C\xae\x8f\x0dc5\xe9\xf1\x98L\xfb'u\xf6\x18O,,[$6\xe7\xc2\xec\xc6\xd5\x9c\xf6G\xae\xb9\x91o\x00\x03~\x87e\xa8\xea\xb5\x10\xe86\xcb\xd7\x86\xb3\xc6\x9e\xebh\x81\xb6<\xd93\x8b\xe9\x05}\xfd\xc8N\xe5v\\\x07\xae8y\xac\xa7\xd6\x8b\xed\xe2\xd9\x0d\x9a~\x9d\xc4\xcb \xe5\x1f\xa1\xe5\xb7<\xfb\x08\xad\xca\x95uK-o\x1b\x97v\xe5\x8aX\xdf\xc0\xb3\x12\x856.B8gE\x00\xda\xa8\xe1\xf4\x15\xc0\xf1!\xb2\x1c.\x90m\n(\xb6 \x99\x0f\xe9\x06\x96\x95\xd2E0\xcf\x9c\x06D\xd5.\xfe\x03k\xd1\xb64E\xf9\xc0\x89\x8b\xbd\xcb\xde\xb2x\x00\xf8q\xc3\xa2\xa2)-\x99\x8aS\xe1$\xec\xa9\xf4%\xa6\xf6\xbc\x91\xd8\xc0Y\x9f9\xd2\xc8\xfd\x80\xf5\x9e\xdc\x13TM\xfe\xee\xb3\xde\xd3\x9e^Jn\xa0\x82\xa1\x8aD\xe9\xa3Hf\x83\xa6\x10\xe4\xa0\xd4\xc2\xb3\xcfb`\xdf\xc2\xd4)kC\xc7\x138J\x96\xbf\x07\xfej\xc5#\xf0\xef\xe0\xe9\xf84\xc0\xc4\xb8\x92\xa8\xcc\x18\x9c\x0dq\x06\xdd\xd8\xeaB\"\xe0N\x06br\x01\xb5*\xbc4pi\x80*W\xbf2s=`=\x86e\xb5\x072\x0e\xd6\xabN/\x8a3\xe6\xa7ip\x1a\xf1\x19\xcbb\xe6\xb3\x95\x9f\xf0(\xdb\xa0\xf8\x07\xf5\x9ci\xfe\x91\xe8^\xaa\xa7\xf4H\xa3 f\xec\x0d\xe7\x8e\xd6[IT#\xaf\xd2\x02\x8a\x80\xfa\x82\xc1P\x94\xd6\xf5\x9agE\x7f\x14{\xe9P\xbc\xa2zlT\xca\xc2f\x08\x9a\xd7uJ\xb4\x0d\x17\x0d<\xc4\xd0\xe0\x84\xcb\x95\xd7\x1d\xc1\xe7\xaa\x1c\xd1\xd3\xce$\xd3*\xfa\xac]d+~}pK\xc7\xc3\xce\x83\x07\xf2\x80\xdd$\xe8W\xdbyu\x80\xbd;\xbd\x11\xeb\xdd\xf1\x97\xab\xc75\xa2x\xb7wW\xe4\xfc,\x8f\xb3zV\xef.VZ\xc5\xa9\x91\xf5\x04\xb2B\xb3\xceS\xc88\xcd\x1ek\xc1\xfa\xda\x04\xe3\x16\xa9\xb8$^\x92\xb2\x01\xf1*\xc4=\xce\xf8N\xef\xc9\xd3\xbb\x18c\xa1U\xd8\xa6\x04\xccFP>\xe0\xd9\xca\x8e\x92\xd0\xad\x91G}\x08\xf1\xe3\n\xdc\xa5\x19\xc1\xa3\x1dwpx\xc6\xa3\xecp\x19d\x19O(o\x1f\xe6A:\x913\xbd\x08\x0cu\xb5x\"\xe7\xe1\xd0ub\x0f\xfc\x97\xc4\x837%\xc5\x14_\xbc\x0f\x89?N\x82\xacH\xdc\xdd}\x00\x89\x9f\xe5\xab\x90_\xc8\xa4]Hz\x97\xf8Q:\x8f\x93\xa5L\xdd\x83\xd4\xd7~\x9a\xbe[$q~\xba\x90\xe9\x0f!\x1de\xe2x\xb0\x8bu\x97\x1f\xc1\x8a\xb7\xe97\xce4\xdf]6\xc9yL\x9fF\xf9\xe0\\\x0d\x07U \xb8\xd5\x88D.j\x80\xd5\xd8\xca\xcfS\xae\xbd\x1a\xc7&\xfa\x93\x01I\x85\xa2r\x1f\x82\x16\x13\x9e\xe6\xcb\xca{\xe3\xa9,\x1a\xc4Q\xc1\x92\xc5`,\x08 \x89\x1fD=\x8f\x05\x90r\x1c\xa4o\xb3Y\x00r\xfcL\x1b\x18\x1e\x9e\xc1\x119\xd4\x12l\x9c\xc7r`\x88\xc4od\xdb<\x96\xd6\xa5xg\xd2Ztch\x83oN\x0e\xd6\x87\x8f\xf9r\xc7\xe5H\xc7\xbaA/\xed\xd0 y\xa9\x8d\x0ff<\xcd\x92\xf8\x12\x17\xb6\xfc\xd1\xf5\xb3!M\xb7\xc5\x16:u\\OZ\x02$\x830H3\x1e\xf1\xe4\xb9\xd8\x87\xa4\x13\xe1\x1e\x17\x9bi\xcfU\xfbk\x9d\xde\xd2_\x9cZ\xd1d\x19\x9f\xf1/\xe4wjsndj\xf3oV\xd5\xe7\xb9\x9eW\xce9Y\x13F$\x98%\xea\xabz\xae\xed\xab\xd3\xc6\xafN\xc9v\xcb\xdc\x86\x95\xa0\xc8-br\xa5\x9f\xf5\x14\x1d\xdb\xa7\x06\xb6O\x8b:\xd5\x14<\xca\x08\x02\x04gL\xaf\x95\x86\xbb\x10`\xa9\x89\xac\xf7\x04!I\xb3$\x98f=\x92\xaa\xdf\x1f\xba\x03\xbc\xadDZ\x08\xec\xb6z\x9c\xaf\xe3R\x81f\x9cD\xb3\x8d\xf6m\x8d\x15\xa6\x91\x9ci7E3Wg#\xdf]\xae\xb8d%\x9f\xfb\x91\xe0&\xc5>\xc3|6\x0d\xfd4e~\xca\xfc\xe2K\xc4\xb9\xf0C\xe9\x86\x1b\x19\x9e\x05\xf7g\xd2LK\xa6d~\x10VS\xe4y`\xdf\xea\\\x99i\xbb\xbc\xe9E\xaa\x99QS\xbc\xad\xe5h\xe9g\xbe\xd5;Y\xc4/2\x94G\x99\xe34y3}(O\xc1\x16\xa9\x18.\x88}@Q>\xaa@%\xab\x82$\xf3\x98\x8c\x01\x80\xcdT\xa1\xe1U\xc6\x9eG \xfc\xfe\xf8\xc3/\xfa\xdb\x05\x062\x06\x89\x06 \x10\x06\xebc\xac!\xc6:c6Fl#\xf0R\x00V\xb6\xdat`\xe5\xeaH#z4\x10\x10\xa1\xcf3\x12\x01\x87\xc6\x10\x0f\xaa\x03\xaa\xe1x}\xca\x8b/ \xf0\x16\x91A\x949\x05a\xce\xde\x04\x11\x15\xf5\xae\x11\"M\xbdkY\x81\xd5\xaf\xfd4\x0e\xda\x1d\xb8#\xfc\xf7\xeb\xf0\x97\xd0\xa3|\xe6Tn4\x15\x9d\xc5kM=\x14\xc7\xc3\xacHoH\x02n\x8f]\x16\xb1\xfe>\xe8\xc03\xcb\x9c\xd1f\"5\xf8\xc5\xd1\xd4o_D\xcdcJ\x06~\x18\xc6Sg\xcbb\x8an`LQ\xb3\x0d\xedJ\xc8\xc0\xb19F\xb3)\xf9\xbd\xaf\xa2\xd4\x9fs\x87\xb3\xa7O\x9f\x82x\xd2\xaf\x82/\x17\xd3\xf9\x98\xf9\x8f]\x00\x9c\x0f\xdf@\xa8\x06x\xa3>\xf7@\x97\xb6\xbaD\x9b\x1fQ\xa5\xaf\nV\x0c||\x04\xba\x0d\xc4\x81\x01\xe2\"\xe1\x83`\xb5d\xf4\xb7 JW|\x9aU~\x0c\xa6y\x9a\xc5K \x13\xa5t\xa6\x98\xa0q\xbd\xe0\xa4 \xd9\xd5j.*\x11r5\x1c\xd6\x88YI\x8e\xe5\xf2\xa6(\xae]\xfa,to\xa0/\xd2\xc6k=rw6H\xa2\xb6\xef\xea\xeeN+nH\x8eD=\xb0\xefC0\xcb\x17\xcb%\x9f\x05~f\x95jH\x05\x0d\x1a\x19I\xbf3\xe6}7\xfd \xe1\xa2\xbb=\x7f\xda\xa0\x9baRw\xc3\x07\xb3x\n\x922{\xb9Uitt\xca\xb3\xd7\nI^\x81R\x83\xcc\xb0\xba\xb0\x12M\xad\xc0\x92D\xc0\xe4]\xb0\xe4q\x9e\xc9\xe8\x88\xdc+\xfd\x1c\xac\x92x\xca\xd3t\xd2\x835\xfc\xf3\x0fEpIy!x \x0b\xa0\xb1m\x1b\x1dQ\x8f\xa6\x07j\xa4\xdc\xfa\xb3p\x88\x0b_\xea\xb1 \xb8\xd8HG\x9d\xa6O\x80\x12u\xb0\x8a\xd3\xecK\xe9@M\x9c6\xf9 X\x8a%\xf9v\x9a\x04\xab\xccj\xef\xa3\x1eE\xc47\xb6\x9a\xa5\x88LJ\x12\x05\xb3nu\xd1\xa6?\x05\xf3W\x94o\xdb\xf4\xeaOF\xeb\x10\xf4\x07\xf7\x86\x12\x02N\xaf\xe7\xb1\xde'=y\xaa(?\x1c\xd5o\xd9UZ\xa1g\xc2qA\"%\x9b~\xbe\xf0\xa3\x88\x838\xdb\x01{J~\xce\xaaY\xee@\xc0}H\x0f\xb8\x11\xb9\x16\x0e\x07\nn\x93y\xae\x81\xa7\x01tb\xbb\x02\x14\x0b\x16\x82l\x0c\x16b/\x8e\x12\xee\xcf.\xd3\xcc\xcf\xf8t\xe1G\xa7\x1c|\xdd\xcc\x07\xd3\x84\xfb\x19\x97\xa2w\xa7\x97\x02R\xf5\x04`\xc0\x8eq^\x90\x00Yd\x9d\xae*\xd4\xb3~\xc5\x8e`\xd9\xc0\xec\xf1:\xe8%E\xbdt+\xc8d\xc5\xf2d\xfc|\x11\x8430s\xced\x9e\x1d\x8fD-\x94m\xabZv\xc0w\x87SI\xed\x9c\x85\xc7\xb6\x8c\x1bF\xea\x11\xa4\x03\xc43=}\xcf\xf8\xa1\xd8\xed\xe0\x16P\xe2G\xb3x\xe9\xc8@\xb5\xc8m\x14=h4a\xcc\x06i\x9c'S.ob\x08\x8c\xd1\x83sI\x1b\xa5\x812\xe9\x93|\x172%A4\xe3\x17\xaf\xe6\x8e\x0f\x02\xbd\x85\xd3\x97\xe9\xa0pq\x14\xd3b3q\x14\xeb\xd8\x9f\xcd@\xd8\xaad\x14\xb0*\xeb\x89NO.\xba\x1el\x7f\x1bC\x10\xfc\x0e\xfc,\xf3\xa7\x0b(\xe9\xf4\x8a\x85)\x052Ig\x00T\x89\x8c/XX\xa43\x96\xf9\xf5p\x93*&\xa1\xf3\\kR\xb5\x8d\x9a\x19/\x97DGy7q\x80\xd1\xe6MF\x7f\x156\xbd48.\x14\\\xea\x10\xb1 \x11\x0f#\xe4>#\xf6DwM\xd0\xef\xbb\xca\x97@Qo\x0c\xaaA\x8b\xdd>\xd3\xec\xbe\x9aW\xa1\xd8\x8fO\xfc\xe9\xfbF_\xe3\xe2\xf1\x93\xd3\x942\xb8S\x0fq\xacU\x8f\xdc\x86\xc2q:A\x01w\xe2\xa4\xae\xc7\xd2~\xdf\x86p+<\xa2\xe9sG\x1c\xa4\x1b\x8c\x08f\x0d\x16%\x18\x947\xac\xdfhd-M6\x18\xa9\x80t\xd4\xa5\x88\x04\x0d\x94\x86\xe88L#\xca!\x19\xebV=p\x85\xad\x8d\xc8N ?|\xf5'K.;p\x02\x1b\x1dW\x8f\xfe\xa8\x81\xa0RW\xa0Y;\x83\xa3\x9e\x04\xea \xack\xee\xbdz\x94\x91u\xd2\"\xbb\xa0\x1e0\xbc\xde\xb2\x1b\xdfRO\xa3\x01%\xf5\xb4\x98i\xd7\x1f\xe8\xd3p\xdd>%\xe3-\xeajw\xd3s\x9d~m_\xa7_\x1eK\xc6\xc3\xef\xa3w;\xd7\xef\x9d\xf8\xbb\xfd\x91\xfb\xd8j\xebM=\xa0\xb0\x0fA\xe4@\xd8{P\x0f\xcdQWJ\xd8\x98\xa3\xa2\x00\x9b\x07\x91\x1f\x86]\xe8\xc3\x0c\xd8\xb9i\x87\xf3\x825\xb7\xab\xe1oM\xb6\xe7\xf4\x8a\x98\x05:/\x94\xf2p^^aW\xf7W\xb3E\x90\xc2\x0d\xd7\x11\x14\xd0\x94\xc0\xba\x11\xc0\x0e\xec\xc5v[\x80\xee\xd7\xa2\x8a\xed\xc3B6\xed\xc4\x17\xadV\x06a<\xf5\xc3\xb7Y\x9c\xf8\xa7\xbc9\xe6\xda\xd4\x07\x02\xd8\xe6\x15\xa45\xda\x19\xd3U\xca\x95\xef7\xc6^\x97>#\xc0\x9c\xac\x97%9\xc7\xc3?\x9e\xfb\x9d\xc8\x1dd\xf1\x17\xf19O\x9e\xfb\x84\x06Y\xff\xd5\xf9^\x1fS\x97a\x9c^\x14\x7f\xc6W \x9f\x82\xe9ZO\xbb\x97g\xf6Wi\x9b(\xd7\xaa\xf5\x9b\x82M\x1b\xfe\x06ycS/\x119=\xd0\x10\xd5\xbaV7>\xb29\xf7f`\x90\xd0\xcb\x12\x7f\xca+M\xb0\x036\x8d\xa34\x0e\xf9\x002\x1d\xf0w\xa4\x92\xce\xfd$B7\xe0\xb0\xf7w\\SL\x17\x17 \xa9\xc9@%UZb\xb5\xadC\xebR\xea\xb4\x86hA\\\xc5\xf9N\x99\\j\x0cw\x86\x96+\xe5[\xbbd\x00\x98\xc0\\\x1f\xa8\xdc\x03\xc2\xa0\xe9\xf7\x82\x12\x890v\x98\xe1N\xbb4%!\x02\xe8\x8b'\x1e\x04\xd1\x82'A&\x1d\xc1\x0c\xc1\xd2C\xa59\x01\x9a\x99\x04\x9a`\xfd8\xd3\x8cF\x9a\xa0\xc5\x007\xf0\x94\xdc\xea/\xa4\xc1\xb6&r\x86\x8f\x1et\x9a\x9fj\xad\xdd\xebT\x1a>\xba\xef\x96f1\xd7\xac\xaf\x19\xd0ti\xa1M\xe3\xbc3\xa4\x02\xe8\x8bt\x8bK\x82\xbd\xf6[\xea\xf5\x89\x92\xaa\x08\xbc\xac]\x1e\xe0\x0c^H\xa2\x9b?\x88\xe2d\xe9\x87\xc17<\x81k\xa9\xa0\x96s2\xed\x8678.+\x95\x0d\xa5G\x0c\x7f\xe0\xa7\x97\xd1\xd4E\xcf\x04\xfe`\x95\x04\xcb \x0b\xce\xc4\xd6\xa7\x8c`\xd8A\xf5\x13p\xb1z\x0b\x0e\xeb\x19\\\xb3\xc0\xaaF\x89m\x17<\x7f\x8f\xea\xb5\xb5vE\xb1\x1d\x17bQU\x13\xf70Q\xbc>\x84f\x8a\xae\x82\xe5\x8f\xb3\xb7\xf5\xc8\x95Q\x8d\x96\x8146r\xf6\x86\xa0\x9f\x19\xcc\x82t\x15\x97\x89\xbb\x90\xb8\xf4/\x9e\x9d\x16i{*M&lc\xcd\x84\xcf\xc1@\x85'*}[\xac8\x81(\xfe\x9a\xab\xa6\x0d\x91v\xf7(D\x02\xa1\x8f\x7f\x92\x9a\xa8\x049\xf30\xd6\x1dbwC'\xa5>J_\xfa/\xd1_\x05\xba\xe8\x00,\x11Get\xa7\nN?\xee\xdcaA\xfay\x10\x05\xe0\xa2\x1a\x1c\x0dq\xf0\xf2\xe1\xc4\xd2\xdfP\x9bQG'0\xd4\x88\xc3\xde\xb6\x0b\x82[\x18c\x1a\x9cF0\xf5\xbb{;\x9d\x88F\xfb'\xac\xfb\xb3Re\x15\x1f&\x17\x18m6\x05h/\x0d\xe0\x9c!z\xa5\xdbT\xbf7\xb7\xb7\xd6u\xe7\xb1\xc60\xec\xb6\x99\xdadz\xe5\x8c\x03Q\xd0=\xb2pi:\x81>pn\xa3\x9f%b?\xa0\xbd\xd2\x0e\xef\xd7\xfd\xdaH\x02Y\xf7\x98$\x03V\xee\xd1\x01+\x05\x9dm\x86\x0e\xe3\xb4\xb3\x81\x08oCUgX\xec\xe5\xe8\x10\x03n^I\x97\n\x15\x9a\xebjtG\xd1\x1b\xc2\"\xfc\xd5J|\x1d\xf3 l\xe8\xca\x9f\xf4\xb4\xe6\xce\xa8\xe5\xcc\x9bbEt\xd8z\xa0\xda =6\xf7X4\xe6\x13\x88\xe9\x81Nx\xc8K\xe5\xb6\xe3\xea\xad\xe0\xf2\xae%\x16\xe0\xce\x90\xf6K9\xbco\x89 \xfcp\xcf\x1d,y\xb6\x88g)Ejw\x0d\xff\xc0\xa9\xe4\xec\xeaG\xa8\x90^\x0cp,\xac\x96\x9cv]6\xf3re\xa0\xa6\xb1\x9a\xad\xd9(\xa0(G\x12\xcb\x80\xd7\x86\x82!1\xe3\x9a\xdf\x80\x05\xa4\xf2e\x90uXX\xc4Q\n\xec\xbb=vVD*\xf5\xd8\x89\xc7\x8e!\xc8\xec\xa1\xc7.0\x9a\x96\xc7\xde{\xec\x99\xc7^y\x10tk\x0e\xe7/\x9a\xe2c\x00\x11y\xa1\x14i\xb9\xdc\xbd\x0b\xf14\xee\xd6\\#\xe8\x1aW-\x10\xff\x02\x9cu\xea\xc9\xae\x07Qq.\x06\xa7<\xf3 \xf2\xcd\xc5 \x15\xaf\x97\xf0\x8a\x9a\x0d\x0f\x02\xd9\\\xa0\x06\xc5\xf5J\xc1\xcc \xe1i\x1c\x9e\xf1$\x85\xe6_\xc9\xad\xa5H\x15\x8b\xfa\x19SA\xf3\xed\"-Vn\xc0\xd2\xb4\xaa\xa0 &\xf9\x10\x1b\xf2+\xf8\x1e\xf8\xbeq\x02\xb7\xec\xd2>n\xd2K\x91\x08\x8aIb\x9b|-f\xab8\x89C\xe0]_Z&\x9f\xf2\xac\x07\xab6@s<\xd7c\xaf\xc9\xe8%\xa2\x0f\xe8tO\xf0LAi\x808-\xe8 \x9e\xe2\x83\xf1\xd6DP\x80\xb0\x9e\xae\xfa\xbc\x8f\x9e\xa1\xecB!bd\x8a\xb7H\x9c\xde\xf3 \x99\xe6\xa1\x9f\xb0 :\x8b\xa54\xc7c\xbd\xe7/\xde<\xff\xea\x8bgo\x8e_\xbc\xfc\xd1\xab\xe7\xcf\xde\xbdx\xf5\xd2\xa6x\x17\xad\x9e:\x01!\x8bA\xa5\x92\xe8C\x03\x18o\xa9'r6^\xa3J2\xf6\xd8s}^R5/R\x89/\xf8\x90*\xfd\xf4\xd8\x99[x\x15\x14\xeb\xa3Q\xe0\x06\xc7gzV-C\xc5\xbb\x02\x8dh\xa3\xae\x13\x14\xa8[\xe2\x90\xc5\xaa\x10\xf4m:\xb2\x97xT\xc7\x97Rf\xc6F5$s=\x1b\x9a\x17\x9d\xbe\xe5IB\x93\x000\x19&\xa6\xa9\xb8C\x8eV\xad\xa6'l\xdd\x93\xfa\xed\x92\x02\xfd\x8e'lyRT\x0c\xab\xd0\n\xa6\xb8qZ\xe3*5\xa0\xfc\xda\xc12\xbd)5h\xe8\xdc-O\xdf8\x16k,\"'/V\xf3\x16U\x82\xf21\\c>\xa9\xfc\x8f\x93\xe04\x88\xfc\x90T\xf8+n}\xc4\x9e\x99\x99\x92\xd5\x7f \xde\x83`\xb7W?\xcd\xb2\xa7<\xebr\x15T\x0e\xf2U\xc1\xe8\xbdr\xb8\x0b\xbb\xdc\x01[\xa2\xb3\x07\x89\x14\\L\x86I\xf5\xcc//\xfct\x8d/[\xe6\x91r\x12o~\n\xf7\xdb._\xb3\x900\x86\xfd\xa5{\xc00\xaa\xfa\x9d;\xec\x12-\xa5\xd8>{\x0d\xbc\xaa\xb4`\xc0\x1f\xefu\xb4\xc0\x9c\x1e\x86\xa8\xa3\x1cE\x99\x83\x006a\xd4\xae\xf2P\xa2\x15\"N(\x83\x80\xc8w\xee\xb0\x13q\xe6\xd3X#\xaf\xe8\x18|\xa5\xd7\x15\xb0q4j?\xb52M\xa0#\x16\x7f!\x10y\x0bz\x0f6\x02\x1b\xac2\xf9y\x91,\xa1TZRA\xfcW\xf0\xe41\xab\x08\xf5i\xdf\x15f\x7f\xc5\x18Glaf\x14\x87\xe1\x0e\x00\xe6\xc8\xd9\xca\xe5i~\xb6\xbe\xbc\x8fMV\xcd~\x95\x05-\x8b\x1a\x883.A8\xe5\xe1\xf1\xae\xe4d2\xe0d\"\xe4\xd1\xfc2\xc6]\xbdC\xeb\xec\xe9\x85\xa8[\xb6&7\xbfj\x93\xacmi\x11\xe4\xa3\xdcTp\x17\xf1\xcb\x00}\xf5\xfe\x9e\x83\x14\xbd\x95\xf5\xe0\xad\xb0\x93\xdd(\x87.\xf7\xdc\x91\xda\xef4\xb0r9k\x02\xa0%u\x8b\xb0\xb3bE\x9b\x82\x97\xc3\x8f\xd6O\x1f\x82\xd8K\xd8\x93\xdd-\xb1\xa0\xa1\xe3\x1210\xe6\xbe\xd9\xff\x95\xf3\xcc#\xfa\xac\x0b\xbfF,\x00\xd7UV\x12\x1b8\xc7D\xae\xa4]\x81\xe3\xab\xd3\x8e\xf9\x15\xd8\x89\x02\xe7\x9c\xca\x83\xbd\"p\x0e\xcd>\xfbE\xca\xad\x1c\xf1w\x86T \x10q$\xb7h\x99\xea\xe2-\xb1\x97\x83`r0\xf5WY\x9e\xf0\xb7\x99?}\xff.\xf1\xa7\x9a(\xa9\xe2\xab\xa3U#\x15I{D\x94wR\xd1n\xf3\x8aphH\x88\x90\xd2\x9a\x90\x89<\x0b\x07N*\xddm\xe5\xb8\xa9I\x8f\xa4\xca\xa9=hdR\x19\xd50\xc2\x9b\xb8\x81*\x1b\x0d\xa6\xf1L\xe0^\x0eWu \x08D\x84\x8c\xea\x9a\x0e\xa8\xd7\x90\xc7\x93j\x05\xdc\x81\xa5\x90\x02}\x85t\xd7.H\xf7n\x0e\xed\x15e\x1e\xc7#\xd6K\xfcozu\x1ae\x96=\x11\x18\xdf\x9b\x9d\xfb\x1d\xcaf\xc97\x97#\xd6\x13\xffz\x06\x8a\xf3\xc1<\x8eY\x9f\xf1\xc1\x89\x9f\xc0\x7fQ\x0eh\x83\xe8\xca\xec\xdc\x87z\xb7,\xb8\xdd5\xa2B5Hn\xd7\x08\x9c`\xd1\x10\x94\x17q\x02\xc3\xe4\xd6c\xdb5\xbe\x1blu\xb9.\xe9\x04n\xb4b\xa4M\x8a\x1a\xedV<|\x9c@\xfc\xd1qBX\x9b\xb6\x9a\xecD\xe8\xac@\xac\xebV\xf3\x0bd\xf8\x87\x8f\x99\xcf\x9e\xb0\xf41\xeb\xf7}y\x85\xadX\xa0\xfe\xc4\xc3\xf8\xd4\xca=Q\xee\x9a\xea\x13\xcd5KT\xe8EHL\xff\x18\xaa\xc3\x87CT\x1dj\"vT\x1e>\xdc\xfe\xd8\xcaCz\x12\x15\x8f\xa1\xf9\x96\xed\x15Z\xf5\x1ex[\xac\xceC\xe3\xa4\xd26X\xb7-P\xa6\x94#\xda\x00\xda\x96S\xbd\xe3\xb2\xd31x\xc3-\xe6\x06\x8fg\xeb\x1a\x9f\\\xab\xef\x04\xc5\x94\x9f\x18\x91\x97\xa6\xf0\x16\xda\xc8\x98\x9ak\x0e\x1c\x86}\xe7\x0e\x8b\xc7J11\x11\xebr\xdd\x10\xb9\xed\xa8)\xd0\xfc\x01\xe2\xbf\xbc.W\xb9s\x9b\xf9A\xa4V\xc3\xee\x0dV\x83\x82\xb6N\xe6\xd7\\+M{]R\xf6Ulz\x1b\xcae\x88Ju`\xf7R\xbe\xeb\xeby\xf38\xee\xdd\x8e\xaa]\x0d\xd3\x00\xa5\xbc\x0es]l\xa8\x1d\x11+\xcae\xf6\xf46\xf5\xef\xb5\xeb\xa4\x9er\xc8N\xe9\x80\xe6\xb4^t\xd5Y\x953\xeb\xaa\xcaY4\xabr\xce,\xaa\x9c\xda\xe7\x96]5>\xa7\xed\xc1n\xab\x15.I\x8a1\x8d\xa3yp\x9a\x83\xf6\x95\xa6\x1a\xbc\xd0\xce\xd2\xae\xaf\x95\xa7\xa4&\xba\x92\x1b\xdf\x164*i\xe3V\x98\xe2X\xac\x87\xb69\x185\x9c\xea\xb8\xd7;>\xe6\x1c\x0c\x07\x0e4\x07s\x90&\xcer\"\xe9rp\xe6\x87\xb9\xe0h\x16J\"sV\xab\xed\xb1K\xd7\xd3\n\xcab\xd1\x98O\xd8\x01\xe5t]\xe6\x88\x7f\xe8\xb1\x0d\xacO!u\x9f\x8dQ\x9b\x9aM\xca$\xe9\xad\xa3\n\xb1\x1a\x8d\x8f\xa6|\x04\x94\xbe\x1b\x94<\xdd'\x98z*\x80\x8a\x95[>c\xb9F]\xee(J5u\x8c5\xe0*\x992\xdah\xb7\x8a\x05\x07;\x02\xba\xaf\xa2i\xe1\xd4\xe7\xf8\xb8#(\xe6\xf3\x11\xf0\xbe]!!\x89\x04-\xe7F`l\xd0hS\xf1\xa7@\xd7\x97q\x80J\xc4r\xc7|\xd2\xa1\x9e\x896\xe8`T\xd46!\xc6\x14\xeb\x1d\xe0\xed71y\xc98\x98\x08\x1e6pY\\\xfa\xe5\x8d)\xb8b\xae`\x94\xb7\x95s*%\xd2\x97(\x98\x8c\x03i%7\x14\x88\x99\x0c\xd2\x15\xdc|\x0c<6\xa4\xee\xee\x81*-)?\x9b4~V\x8ac\xa3&\xeb\xf8\xb6iG \xa2\xdfzG\xf1\xac\xf0j\xd18\xef\x16:!\xb6\xe3\xb8:\xa1\xf6\x19\xa1\xe7\xb1\xd9\x19<\xccbD(\xc9d\xac6-\xde\n\xdew\xcc\xf0\xc8\x92\xb1',\x12\xd3\x9d\xb9,\x18g\"\xb3z\xd91k\xb8\x08\x07\x1f\x8d\xc1\x81\x05^h\x95\xedn=\x06\xc2\x1b\x8b\xca\xd8\xb4\\\xc5I\xa9\xc9!\x1b\x95\xbaTu\xa3\xac>\x96&\x00t\xb9\xb55+\x88\x0b\xe8\xa9\xec\x03c\xedw\x8b\xba\xdc\xc6\xaa~\xaf\xc6\xb0\xdc\xfc\xeb-\xb7\xad\x9a\xbe\xeeU\x84G7\xebK\xa7[U\xbf\x10\xfc\x14\xcf\xaa\x06\x05\x1b\xe6\xfd\x80\xfe\xf5\x81\xf2\xc6,8\x8b\xa9S\x17z\xe2^:u\xe2z\xba\xd8X\xa6N\xe0R\x84g\xea\xe8\xe6\xd0hG\xb8t~\xfe\x01\x85q:{\xdc\xec\xf5G\x19\x8bi\xa1*\x17N\x88\xce\x88\x8bSc5T\xa4\xc72e\xb4\xc4\xf6Y\xfe\x03vS\x8eY\x9e\xa3\xea\xb1~\x1b\x04\xab\x04\xdb,\xf88\xd2=q\xf9\xbdf\xe7\x01\x1a\xdd\x1f,\xfdU\xbb#hU\x81\x1d\xb0\xcc\xe1\xe3\x08T\xcf\xe2\x7f\x15%\\\xe9|\xc9\xc9+Zi\xf3\n\xff\x07o\xbdc\x0d\xc8\xbd@\xe0\xd516O O\xc5\xbe\xa1Zq\x05\xd7u\x12D\xb3\xf6P\xb6\xddg\x16\x8f=\x8f(S9\x9c\xa8 \x85\xff\xd7<\xd5\xc5(\xda\xe0\x10\xce\xfdv\xba\xdd\xe9 \xadD\xcb\xc8\x98\xe2H\xe6I\\\x0b\xc8\xd5t\xdcF\xff\xed\xe0]\x00\xe6p\x0c\x82d\x0fe\xc4\x13\xd7c\x9f\xc6q\xc8\xfd\xc8\x01V&+}.C\x01\xd4\x05\x81]\xf4m\x8cY\x13\xe4<\xdav\x07A\xc6\x13?\x8big\x8e\xc6\\\xca%\xfa\xc8fAN\x1a\x90\x1bK7\xa5\xe5\xc9!\xbd\xfe\xa7\xf2\x9bur1\xaf\xe3U\xa7c\xb5yX\x9e\xdd\xc6a\x94\xc8\xd7\x0f\xa3f.\x1c\xe6\x08\x1f\x8c\x1f\xac'\xf9\xeaQ}\xddET\xb2\xa5V\x13\xcaV]\xd2\xdbF]\x128Z*%\xf3)J\xe6C\xe7B\x06\x08\xbf\x90\x0e\x12\x99t\x19\x0eh\x0e\x13'R\x02\xf4\xf8\xec\x16\xbe\xf2\xaa\x8d[\xfc1\xc0 \xe8\xc2zF\x9c3y\x89F\xaeN4\xf7tN\xb5\x10\xc5\x82\xa4 \x16\xc9\xdb\xdb\xf2\xc2\x9e8\x9f;\xcb\n\xc71t!b\xd9>\xe3p\x19}i\xe1\x86\xf0T'\xbe\xda\xc2\x85W[\xaft\xaa\xe2f\xe4T\xb05\x91\xcb\x96h\xcc\xc7I\x0bJ\xf5\xc8\x91.\xc9\x02\xe6\xa5R3e !\x03\x7f`/\x040\x9f\x1bzdf*'\x9cs\xe8n2\xb1\xc2\x02\xe0p\x02f\xae\xe7\xf2J*\x1a\xd2\x08\x82\xa9\xe0#\x0e\xc8\xe2l~\x02\xce\xc5\x9c\x128\x1b\xc7\x83Y\x1c\xf1\xc7.(\xe0/\xd8\x81b\xe2\xd0\x1a\xf8\x18%&\xd2\x90\xbd\xf8%\xf6ogVHS\x0e=\xb6p\x96\xb02fp\xddJ\x82\xf9\xb0\xfe\xd1~\xdf\x125K\xcc\x1c\x11\"\xa84\xf7\x9c6`\x03@\xe0\xb4\x123\xdb\x1c=\x8c\xd7\x03\xb9]\x0d'\x0e%B\xc8Py\"GZ%\xed\xb3\xc3\xc1t\xe1'\xcf\xe3\x19\x7f\x969[\xae\xcb\x9e\xee\xb3\x07\x0f\xb6\x1f\xed\x82\xc5\x12{\xb2\xcf\x1e\xec\xee\x0c\x1fA\xf9Cp:9\xee\xf7\xa3\x89\xb4g0\xc0y(\xedG\x0e\xad <+Ax&A\xd8\xef\x9f\xd9\x81v\xd6\x82\x8e\x1a:\x89=\xf0\xd4D\xb8\x02z\xbe\xa3\xad\x9d\x1a\x00\x9dS\x97^P\xe40%4\x15o\xd7\x1d_H~\x00\xbb2\xab\xc8\xee<\xb6,/\x89B\x8c\x90\xa2\xe6\x0d\xf6\xf5\x9a\x96\xe2\xd1\x8e\xd4R\\.O\xe2\x10U\x12\x8f\xee\xdf\x82J\xa2v\xc2)\xf48\xb5-\x1e>[\x91\xc3\xb6\xe9vH\xbe\xcb\xdcb\xc8{(8J\xcd\xf9Bm\xf7`\xfb\xb2\x88\xd3\xcbx\x9a\xc9\xee\xd5\x8d:i\xf5\xa22o\xac\x9b>\xddD\x89\xa8\x97\xd9H\xc6\x95Q\x14,\xd9\x04\x953F~\x16\xbfV\xdaM(B\x95\xc0N\xbf\xf3O'\xb7\xc74\xea\xba\x0e\x8b\x8aC!_\xfdZL\xd8\xac\x90\x98v\xd54\xcc\xbbi.V\x84B\xc2d\xfa\xc2\xfa\xed\x90\x1az\xed\x1b\xe8U;\x97\x14X\xb5\x06\x1a%\x8e+=\xda6i\xa5\xeb\xeaf&\xe7`\x81\x9b\x80\xb3(\xbb\xef50}57\xbb \x92\xc0\xc5\x98c\xac?\x8c\xa1q-wF\xe3\xca)\xb4z\x98\x8f\xbb\\\x8f5\x89[\xbd\xb3\xfc\xd6:\xeb\xc3\xcdrP\x04\x01\xf4CG\xf3j!\xc5h\xda^\x0b\x01\x1a{\xa5\x15\xa1\xe0B\xa6ND[ \xce8\xfa\xa2\x0c\xe2\xe8\xf8x\xc4r\xf0/\x9aQ\xe6|\xc7\x91\xbf\xe4e\x993\xa7n\x02\xfd\xa1*\x1f\x99:q\xfd\x93\xf38\x11\xd5\x9b\xb1L\x0ez\x86\x8a0\xf87\xc2\x7f\xfb,v\n\x8anHE*\xbf\xdf\xf3\xcb\xcf\xbb|\xccb:\x0e\x8b/cA\xc4R`jgv!\xfel\x9cM\xd0\xd6\xb9\xd4\xdc4vm\xe1\xa7/$\x96(X&\xa8\x06\xd1r\xd0\xa2\xaf\xa7\xa5\x18\x01\xd3\x83\xf49\xc8\xaa\xde\xaeT\xc8\x97Zsf\x01\xd9\xaa\x99a6.\xf7\xb1z\x932Y5$\x7f\x1a\xd5\x97\x82\x1c\xd6\xeaB\x9a\xac\x08\xefF-\x19\x19\xa9VO\xc5N\xc2\x9a\xf2\x97Q7\xe5~b|\x12\x13eM\xfcaV\\\xf1i\xc0\xd3zMLUU\xf1\x17Q7\x0c2\xa3f\x18dE\xbd0\xc8\x8cZ\x1a\x0fP\xab\xab\xe5\xc8\x16\xb4\x14\xa2\x9d\x82S0\xda)r\x8av\x8a\x14\xa3\x9dW\xddS\xdfoT!\xeb\xc2_E\x95j+\xae\xd6\xb1\xd8\xde1\xfd\xcb]\xbe\xaa\xc8\xb7\x031\xdcQ\xf01\xa8\x91Q\xd6g=\xd70 \xad\xfc\x863\xc5\xaby\xd7\xaf\xa6\xb5\x98Z\xcc\x1c\xe5\xbc:\xcaXG&\xaf\x0d\xac\xea\xfa\x89\xfc\x0e-\x1e\x95\x8cw-B<8\xc8(0\xce\xd1;E\xf7\xaa@D\xe8\xd5\xb4\xe7)\x98\xf6\xb0B\xd0^!\xae8\xe3\xafp\xcct\x13UHPM\x94l\xf9M\x1cj\xe9\x02\xda\xdd\xb5=\x19\xa1\xdf3\x108P\x9c\x03\xba\xf6/\xf8\x06\xfa\x1c$'\xeb\xd6\x8dG[E\xfc\x1b\x1bx\xd9\x87D\x93\xab+\x91\xaf\xc7*\xc0\xb2o\x8b\xb2\xe0\xc6\xb4\x1e\xca\xe0\xce\x1dV-2\xae\x16\xaa\xce\xfcm\x0cYM\xa0a\x12\xa5>U]\xc6`K\x81\x12\x88.\xcb\xb8\x10\xc0V\x17\xb2\xe3\xae\x8d*Uk9\xee\x02x\xe2_,\x04\"gg\xb8}\xed\xa1\xd8\xdd\x06\xfdR\x0d\xb2\x12\xf2|\xbd\x01\xa6\x86CqX\x18\x88\xe6\xa6)\x88\xf2\xcf\xa1\x1d)\xb0o\xa2R\x0d&\xee\xedY\xcc\x9e\xe9^`\xd6\x1d*\xc1N7O\xef\x01\xb1XR\x9e\x91\xd7g\xe1\xaeQ-\xea\x9d8\x12\xd1\x91\xa4\xa0t\xe2\xf0\xc1)'.\xd3i\x01R\x07)\x071a\x06/\xfbP'\xe5\x10\x9d\\\xdenC\x15\xa0\xfa\x81%\xf0\x07\xdc9\x93\x01\x8f\xb0\x90\n~$\xca\xe0\xad)\x88\xd1\x0d\xfd\x94\x1f\xc8\xd0\xc1Dv;\x14k\x8d\x89)\x04 J\xdej\x1eb\xb5\xa0\xff\xbd\xff\xbeW\xcd\x97\x87\xa2\xfd\xf2\xd20\xc8e'\xeec\xb6\xb9\x99@D\x9f\xfe>\xeb\xfdw V\x00q4\x89 \xd9\xf77j\xb5\x19\xea\xf7%Ik\xbfB\xd8\x12\x95\xc3\xcb\xf0\xd6`\x82\xf2{A\x02\xb8\x18h\xac\xc2<\xe1@\xb3q\xbf\x9f48\xf61\xd0\xb5\xcb>Q\x8b'\x7f\xcb\x17\x18\x86\x86\n8\xae\x8b\xf8Z\x00mc\x1f ]i\x06*)3=\x82\xd3\xbc\xdd\xc5\x8beA7\x9f\xe6\x99f\xc2JwG=\x01\xd8\x8bZ\xb3}\xeb\"QOPD\xdf\xf2\x8b\x15\x13\x8c}\xb8\xba Fe\xaf%>-J\xda\x06\xc0\x14>>f1{\xc2|\xb6\xc9\x86\x8f\x9b\n3\xd9\xb0t\xa7\x07\"\"\xb9?\x04\xa0\xed\xe4\xe3x\xe2j\x0eW\xad\xdd+Z\x83.\x0e'\xa0C\xe9\xf7ckaS\x05\xa9\x1e\xf9\xad\x96>\xb1\x03\x15\x8eN~N\x81\x8fl\x97\xfe\x9a6*#\x9f\xb8M\x9eV\xd0\xc8jo)\xd0(@ao\x03\x1a\xe5\xcdh\x04\xd2\xc4\x8eh\x94\xba,\xc7\x10\x0e\xfd\xbe%\xf0PK`\x03@\x1ah\xe3\xeaJ\xbe\xec\xb3q\xe3DS+\xb3\x9ao\xcd\x9e\xc8\xab{\xe2;\xf2V\x9c\xc4\xd4M\xe9\xfc\xc3 \xcaI\xcfa\xd2c\x81\xf6h(\x1b@\xd5-i\xe4\x0e\x19\xa2\xa2\xc7\xf2\xf1P&~\xc4\xae\x17}\x1fN\xc6\x01\xe0\xb8\xff\xf8F\xfdv=\xd5\x18N\xe05\xf0WJ8\xc9p\x8b\xe6P\xd7\xf3\x8e!\xdd\xc74`\xb2\xdf\x8c\xc9\xb9\xb4/o\xc6\xf5\\\xe9\xc1\xad\xa5B\xd8\x0e:\xac\x05\xc9l\xf9\x02\xbb\xec\x8bAT\x81X\x80\xe3\xb4\x0b=\x0d4,\xedNO5\xee\xdf\x07t\xc8\xc7\x81FO\x9bIi\x88\x88\xe2\xa3\xa7&\xec\xebp2\x8e\x01\xe9\x82k\x10\xd6[\xe9Yq\x15\xb7\xe8\x8c\xa8\xaf\x0c\xf7c\x0f\x10Z\xe4U\x92\x1e\xb3\x0d(&\x15\xe0w\xee\xb0P\x117\x176\xdcp\xb0\x8aW\x8e\xeb\xe1\xa4\xc8_n\x87\x96\xd7X.\xda}\x80.\xeb\xa4\xab\x03\x16\xc9\xa7\xe8|\x89\xd9\xfc\x0f\xe8_7\xe0\xca\xaa\x9a\xff\xbd-y?\x11\xdd\xd2\x0e\xc0\xa9\x9dt\xec|\x93+\x89k1q\xfa\xb7\xd79\xca\x81\xc2\x9b;?\xff\x00\x84\x92;/\xfd\x97x\x0b\x91;;\xf7\xbf\xcf\xb3N\xc1\xf5o\xec\xdf\x8e\x1c\xac\xca:_\x13\xack\xf2\xc6u\"y\x1bl\xb1F.2\x0f,\xe1,fpU\xe6-.\xb9\xb4h\x1cwZuU&\xab\xcd\x7fh\x8642\xc1\x03W\x84\xbf\xfa}\xee~\x9c\xbdP\x93XA\x10)\xd8\xf87`\xa0x\x86\xaf\x12\xab\xa8\xf2\x9b\xa0\n\xb7Ct\x08~\xe5#\xd0\x9b\xdb<\x05\xd2B\x06\x1a\xd5#++j\xe3\xe3\x08x\x10%\x83\x1b\x1e#\xad\xbe\xaf\n\x89@\xc1:\xa1\xa142\x11\xbc\x95\x89h\xdc\xa6\xb3\xca6\xddr \xeb\xc434\xb2\x96-\xfd(\x97\xb7\xfc\x8c\xf5\x10\xd6\xba\xd2\xad\xc7\xa9\x02\x9c\xd2\x00i\x0b\xaf\xdcD\x8fY\xae\x81\xb3\xe0\xc0\xfd\xb2\xa7\xa9\xe4\xc0s\xc5\x81\x8b\xbcT\xe3\xc0surH;\x9c\x1c\x9aN\x0d\x96\x13\x03\x9c\x16R\xf8\xe8p\x02N>\xfa\xfd\xbc\x0b\xdd\xbc\xce(\\O}\x06\xce\x11\x99\xc7\x02\xb0/\x10hHxN\xee@\x0b;a8\x1es\x91\xcb\xc7\xc1\n\xb2\x14\x82\x18 \x93\xc7\xbbk\xe3<\x9e\xa1B8C\xb5\xb3\xa6)B$W\xc1\xbf\xe5)\x0d\x91\xdf_\x03\xf9eo6\x1a{\xd3rd\xc8\xf4\xcf\xe7&#\x9b\x13,r^e\x91\xd3*\x8b\x9c\x16,r^\xfe\"Xd\xb3ekO%G,f\xaa#xn\xb0e\xd9 9\xbb\xe6\xf2\xf2t\"nv\xf5\x07\xf4\xaf[\xda\x03m\xbe\xc1\xe9\xcb3;C\xfa\x82\x9b\xe9K\\\x1aY\x1a\x17_R\xdb\xcd\xb7j\xb1\xf5\\\x84[6m\x88\x16!\xe3\x18\xb4\xdcx\x97B\xd3\xb9\xc7V\x1e\xd8WN\xa5\x81\xa21\x1f\x8b\xa6\xcc3\xd0n(\xc7sf\xfe\x12\xf2\x95\x13\xc6*F\x97\xf5\xc0$\xbc\x99\x97S\x9cF\xe9_\x98\xc4\xad\x04|C\xa9\xa8\x0ep\xaf\xd4*\xa9\xa7\x9d\xad0\xe5\xb1/A3\xbb\xb4`\x9f\xb7<\xb69\x14[\xc3\x99\xbc}2/\x9c\"\xac\xc4\x9b\xa9s\xead\xb1\x1c8\x1a\x00\xd9Y\x83\xe1\xf2\x87\x1a\xf8\xe2H\xb9\xe9m\x87]\xe3\xf5v\xf2\x02%+\xcc\xdd4\x17\x05$\xcct\xc3\xbd}6\x9e\x81\xcb\x8aH\x19\xf1!u\x8f\\\xd4\xc1\x01h \xeeM= nH`\x91\x89tb%}L@\xa8|e\x93\xdfbD\xa3\x1e\xe0?\xect\x94\xf2\x15\xbb\x901\x0d`\xbf^\xa0\xf7\x8d\xd2%2\xac-\xf4\x07\x1b\xe0~%\xbd\x19'\x10M!\x8e2~\x91A,\xa6\xe44u\x0b\xfb\xcd\x04\xe3G\xc4\x88)A\x89BbNlq\xa2[I#\x86\xfb\x96k\xab\xcd\x0d\xc7\x19^\x8c\x94F\xe1\xd6E\x11\x89\xa1\xf3jd-\xe9\xffC5\xcf\xb8\x1da\x14\xff\x8c,\x05\x1f\x043\xbb\xe4O\xfa\xc2d\x8d\xf1\xfc\x01\x03q\xbb\x13\xadaOf\xe3\xb4t\xdb\x8b?\xe2R'ct>\x03W\x9a\xa9t\x80\xc8\x0e\x98\xd2\xec:\xe0P\xdcY\xa0\xe0\xdc\xde \x86\xf6lbnG\xb8\xe2\x1b\x8bbh\xe7\x06Q_\x89Ri\x89R\xa9G\xaf\xaeXF6\x88\x8b;\xc9nCI\x14\xc3\xd5/\xc7C\xf5n\xd7\x90\xf5Gk\x8c\xb7\xdc\xb4gr\\\xe8)\xdc\xc2\xb5\xa1\x087wBy\x9b\xd9\xf4\xfeB\x1d\xb6q+\xa6\xa8\x00\x97\xbc\xb4\x94\xb3\xca\xae.U\xb3\x1c\xe2\x03NOp\xc9E\xb8\x00}\xcd\x05\xf9\xb2\xc5\xfd\xcc\x07OR\xd9\xb4\x03\x95\x85\x95#I\xe1\x1adr0=\xa9Q\xca\xc1\xf4\xc4-\x0d\xa0\xc5\xcf\x02\xd7\xf1G4\x08\xc4\x96)\x9d\xef\x001e\xa3\x12\xa9\x89\xeb\xe38\x8a\xc2\x9bu\xfbvA\xb0\xeb\x14\xb1\x9c\x01\xb1\xbc\xba\x02BY\xec\x9c\x0b\xdd\xabv\x95\x84b\xa2FEU$\x19 \x98 n\xb1\xf5^\xb9\xbcn\xa7r\xa2\x0bD\xff5>\xa6\xe8\x0f4\xaa\xba\x13\x0b\x8cl_\x1d\x92\xce\xc8\x9e\xf3\xa2\xe7&\xea\x1ac)~\xde\n3k2\xad\xc8\xcc\xee\x191\x18\x03\x99^\xbf\xc4\xed\xcb\xf4\xba7]\x15K\x8c\x0epc2\xb9\x1dn\x0c\xc5N/[p\xf0\xd8/\xfe\x8fd$d\xb8X\x1fG\\\xfd/\xd2\xdd:[\xabB\x19val\xb5\x0b7\xc6\xac\xc4M\x99s\xea\xa6\x11S\xa62[\xca\xec_]\x0e\xac\x96)\x14T\x1c\xfc\xa3\n\xf2\xb3\x01\x91\x96\xe8k!w{\xac\x0f\xde\x1eX\x9f\xf5\xee*3\xcf3?\x0cfL\x0dv\x19\xcf\xb8q\xf1\x8d\"I \xee\xeb\xb65\x11Z\x02\xf4\xc2\xb0r\xc7/ES1:X\xf5\xa5\xc9\x14\xb1Q%\xf4\xe14\xc2\x8aC\x8f\xcde\x13f\x19\xd1\x95i\xabS&\xbd4`\xee\x98\xb2\xb7Q\x8f\x18BH\x04\x9c\xfb\x12yj\xce\xb8\xf8=b\x9f\xf1\x8cO3>cy\x14'3\x9e\xf0\x19\x13\x88x%\xb0\x8e\xdd)\"sC\xf8\x9e\\t\xcec\xe7\x8b`\xba`A\xc4\x002K\xff=O\x19F\x1fc3hMpC\xf1\x9c\xa5\xf9t\xca\xd3\xf4\xde\xdc\x0f\xc2<\xe1,X\xae\xe24\x0dNB\xce\x9c\xf3\x05\x8fD\x13wu\xec\xbe\x0b\x13\xeb\x1eE\xcf\xe3(\x0df\x80N\x04m3*?\x1c7\x1f\x1b\xc6 \x15\xbd\xc8\x02\x89\xb5N\x0e\x84'T\x9dc\xac\xf0\x96:\xbbh9S$k\x9d)H\x13\x97\x8fz\x8a\xa8\x8b\xa6\xa5\x90\xe0#\xe9\x89\x9b\x14\xb7JOY\x06\x90k\x06[\x86\xe7\xe3\xfa\xc5\xfc\xea\xe5\xf3\x9b\x03\x88p}\xa5NYm\x91\x96\xad\x86*\xe8\xf9\xfdV\xe7Q\x9c\xca\xd6\xbf\xbd\xd1\xe8\xa2\x1f\xaf\xe28\xe5\x15\x19p\xe8\xa6]\xfc\xd3\xa2\x895H\xad\xcd\x89\xa3\x0eC\xaf\xfd4\xe5\xb3B\x10\xa3\x05\x84\xc6K4\xc1\x9c\xcf\xea\xf1\x8cn\x17~{\x86JG\xcc\xf3\xbd\xf1Qt\x94\x1c\xe5\xdb[\xdb\x0f\xe1\xef\xa3\xc9\xbd\xd3u\xc1\xac\xd0_\xcc:\x89\xfb\x85\xc2\xe2)\x1bnm1\xe5\x80.\x93\x0eX\xb7<\xf6\xe8\x11\x1c\x13\xff\xdb\xef\xfc^O\xde\xff\xcf\xd4=iAq\x9b\x97\x8a\xfc\xcao\xbc}\xf5r\xa0\xc0y\xe9pW6?\x04\xc5Fm\x19\xdd.p\xff_\x83\x9cJ\xcf1~\x19G\x9b\xd3\x98'S<\xc6e\xb1DD\x17o\xf2N>\xea\x85\x8d\xdb\x88\x11o\xd3&\x96\xdf\x0b\x06\xb3 ]\xc5\xa6L\x85p\xa9)\xfaV\xb3\x81\x08 6\xa5\xa2\x9dg\xa7]W\xe0\xcc\x03\xa7B\x1e\xab\xf93\x05\x89#\xf8\xe4AY\x0b\xdbg+\xc5\x96.@\x89P,\xd0\xd4\xb2@\xd3\xe2\xc7\x01\xeb\xe1za#\x06\xbea\ny#\xeb\x8b\xcf\x17\x1d%\xf1u\x86\x0e\xd6R\x9e\xbd\x0b\x96<\xce\xb3\xf6sO!\x00\x8aH\xe1\n\xb7\xe9\xbb\xc4\xa7\x06y\x94\xf0\xb9\x18@\xf9\xcb\x81\x88\xa7\xe0UNt\xe6\xce\x1d\xd6\x8b\xf8E\xf6.\x98\xbe\xef\x81u\x90J\x86\x05\xa4\xba)\x12E\xc5\xf5\xfb/\x8f,\xcb\xbasa\xd9\xff3[\xff\x97\x95\xfe/\xb5\xfe\xb7hpj\xf3@.\xfb\xca\xd8f\x18\xef\xbf\xd0\x98\x8a\xb3\x15B\xc8\x80\x0c\xa7 \xa3\xd7^\x92A\x15\x05.\xf1\xcf\xb9\xd8XE\xb3g\x18\x1ct\x7f\x7f_\xcf\xb9\xba\x92Q\xdb\xcb4\xb1m\x0fvvv\xd8\x88M\x9d\xb9\x83\xa6\xe8z>\x1aGmI\xcc^\xb2}\xf6\xf3\x0f\xd2\xaf\xd6\x90m\xb23\x97}\x82\xd2M%\xaa\xa8\x03\x07t\xde9\x05\"\x18\xec\xd5\x15\x83\x01\xb2}\x0dK<\x16\xb4O\xbbE\xda!\x1e\x0d\xaa\xfb\x1aT\x1d\x0d\x84\x9e\xae\xb0\xabl\xa1h\xbb\xe6\xc4\xae\x8b\nA\x08\xe8W\xb1\xb3\x91\xc6\x03\xd2b\xae\xb2\x8c}'@Hu\x12O\x84\x1e\x0b5 \x05\xfc\xa4$\x9c\xa6\xdf\xa7\xea\x1eT\x839\xbd\x0d\xcd\xdaP\x96\xd5\xd1\x96\xdc\x8b\xd0\\I \x01bp\xec,\xbb4\\Ctn`\xb9\xe5c\x88q\xc6\xf8\x8b\xdf\xb7\xb2\x05\x1a\xbe\x98\xd5\x11\xf3\xd1\xda\\\xb3\xe0\xca\xa4\x01\x87\xd8\x0e\x9e\xb2\xb8\xc9\xb7\x08\xbf\x98r>K\xd9\xd2\xbf\x08\x96\xf9\x92\x15z\x8b\x0c\xa1\xf2}9\x1b\xd9\x1e\xde\xdf\xbb\xffpg\xf7\xfe\xde\xf5\xdbk\x07\xe76\xad\x17\xdd\xd5\xafx\x04bG\xee\xb8\x1d\xcb8R\xc4^\x9c\x14{q.\xdd\xc0Kk\xf258\xe5\xe6\x8d\xd8G\x13\x9bf\xc4\xd7\xdd\xfb\x02\x8b0X\x04\x99\xeaZ\xbb\xc1\xc0i\xf9)b\x0b\x12\xa3W^\x11\x0cr\x00\x99\xd2\x1d\xc2m K\xcb\xe46(\x9f\x83\xf6xW\xeb\xae\xb1\xb32\x044q\xf3\x01\xc2F\x9a\xc9y)\xff23\xd3\xa6\xcc\x10\xda*R\x1f\xed\x15\xa9\xc3\xedm\xb8\x0f\np\x02\x18 \n\x8e]\xae&\x02\xdcz\xff\xf7\x1f\xfc~\xafq\x1d\x9av\xef\x84\x1d\x85\x8e\xb1 \x82\xc178j{\x15D\x96a>\xabK\xb5\xea\xbe;\xd1\x05\x87\x1f\xdc\xe2\xc2N\xe4\xec\x0co\xe2\xdb\x93\xf4]/\x1a\xee\x1d\x1f\xf3\xf4\xcbx\x96\x87\xbcW\xa7\xda2T\x90\x1eJ\xc1EY\x0f\xc4\xd3k\xb2UQF\x00\x89*\xec\xb1X\xbd\x96\x1b\xd0\x07\x93\xdd\x08\x1cq\xb8}Pw\xf3\x1b\xcb\xac\xfb\xdb\x10\x95\xb3\xc8S\x1d\xc0\x90cd\x1f8\x12\x99r\x9c\xd2\xef+\xb5Ca\x9c\xc0\xba\x9f\xbe\xf5\x88\xe9/\xc7\x04\xa8}\x87&\x8b\xd3x\xb9\x8a#A\x0e)8\xa8\xe7\xd9j5b\x97\xc5\x0cZ\xcb\xf9y\xb6\x88\x93\xe0\x1b_\xf4\xe4u\xbc\xcaW#v\xd2\xbd\x1a\xff4\x8bF\xecx\x8d\n\xafV<\x81\x8fA\xcd\xf3n5\xd3\x11;l/\xf9,\xcf\x16/2\xbe\x1c\xb1\x8b\xf6\xc2\xa2\xd9C4{{\xdb^:\x16\xc5\xb7G\xecY{Q\x7f\x15\xfc&\xbf\x14}\x19\xb1\xe7\xed\xc5O\xfc4\x98b\xe9\xf7\xed\xa5\xe5\x91\xe4U{\xc908\xe3ox\xba\x8a\xa3\x94\x8f\xd8\xeb\xf6\nA4\x8fG\xec\x8f\xb4\x17|\x11\xcd\xe3\xe7\x18\xd8\x9d'#\xc6y{\x95\xdf\xc8\x97\xabw\xf1k_\x8c2\xebP>\x8e\xc2 \xe2?\xf2\xc3`\xe6gq\xf2\xa9?;\xe5#\xf6\xaeCE\x85]\xe9\x88}\xb9F\xf1\x11\xfbi{\xe9\x02u\xdf\xe6\xcb\xa5\x9f\\\x8e\xd8\xcb\xf5+} A1G\xec\xcd\xfaU\x11~\x9f\xb5W\\\x04\xa7\x8b08]d\x82\xe1\x18\xb1\x9f\xb5\xd7H$\xa6\xa4#\xf6y\xf7\xd2#\xf6M\xf7\xc2\x9f\xc6\xb3\xcb\x11\xfb\xb4\xbd\xc2\xcaO\xfc%\xcfx\x92\x8e\xd8\x8f\xd6(\xfe&>\x1f\xb1\xdfh\xaf\xc0/\xf84\xcf\xf8\x88\xfdV{\xd9\x05\xf7g\xd0\x91\xdfl/\x0bF\xb4\xe9\x88\xfdZ{Q\xb8\xc5\x17e\x82y\x1d\xb1\x1f\xb6\x97\x8f\xcfxr\x16\xf0\xf3\x11\xfb\xed\xf6\xc2\xf38\xce\xc4\xc2\x8c:,\xb4\xcf\x830\xe3\x89\xb6\x9a\x93\x0e\x95^\x0b\x88\xe3t\xc6\x1d\x8aO\xf3$\x1c\xb1\xa0C\xc9t\xba\xe0K\x81\x83~\x87\xc2o\xb1\xb0\xd6\xf7\xbcC\xade<\xe3\xe1\xe1\x85\xbf\\\x85|\xc4\xc2\x0e5\xbe\x145~\x9c\xf8\xab\x95\xf8\xc6\xb4k\x8d\xe7q\x18\xfa+\xb1F\xd2\xaeUFl\xde\xb5h:b\xab\x0ee\x0f\xa3|)\x9b\x9eu(\x8e\x8c\x8e\xac\xb0\xe8P\x01\xcc6e\xf9\xb3\x0e\xe5\x0bg\xf7\xb2\xce\xb2S\x1dd\xb8F\xec\xb4C\xe9w\xc9\xe5\x8b\xecU\x9e}\x9ag\x99 \xeb\x97\x1d\xea|\xe9'\xefg\xf1y4b\x17\x1dJ\x7f\xea\xa7\xfc\x0b\xff2\xce\xb3\x11{\xdb\xa1\xfc\x8fx\x92\n\xde*\xf1O\x97>\xae\xb7\x11;\xe9^\xf1m\xe6/W#v\xdc\xa1F\xb1a\x1c^d#\xf6\xc5z\x15\x80|~\xd5^\xe7\xb5\xa2\xb7\xf0\x91__\xa3\xc2\x8bh\x1a\xe63~\xb8\\\x89\xd9\xfcq{\xcd\xa2{\x10i\xe4\xc5\x1a\x154\xaap\xda^\xed3\xceW_\x04\xd1\xfb\x11;\xef\x00e\xc1\xff|%H\xda\x1f\x1d\xc8\xd7\xe6\xb2\x02ap\xeb\xc6\n\xeaw\x03i;;}\x96\xa6\\p\xf8\x87E\x87\xc8\xd2\x9d\xe4\xd8\xb4\x9frV;K<\xef\xa4F\x88:\xb5\xf5\x9eh\x8b\xd4\x1c\x8dg\x05\xbc\xd9\xbc|M\xcbW\xbf|\x0d\xcaW\xeal\x8az@\xf9\x8a\x87\xbb\xb0L\x88<6-\x7f\xad\xca\xd7E\xf9zV\xbe.\xd5k\xe3\x89\xf7\x15\x87\xe0\x03\x8f\xa8#/\xe6m\xef\x1a\x11\x8e\x8a\xbc\x9d\xedz\x9e_\xe4\xdd\xdf3\xa2\xe5\x14y\x0f\xef\x1b\xf1\x80\xca<\xe3\xf8\x1d\x96yF_\xa6E\xde\xa3\x9dz\xde\xbc\xcc3\xfa\xb2*\xf3\x1e\xd6\xf3fe\x9e\x01\x97\x85\xca\xbb\xbfe|\xef\xac\xcc3\xda\\\x16y\xc3\xadz\xde\xa9\xca{\xb4c\x8c\xef\xb2\xcc3\xc6pR\xe6\x19\xdf;.\xf3\x8c1\x9c\x17y\xf7\x8d\xbe\x1c\x96y\xc3z\xdeE\x99g\xcc\xfb\xdb2\xcf\x80\xcb\xf32\xcf\x98\xf7\xf7e\x9e1\xef\xcf\xca<\x03.\xaf\xca\xdaq\x07\xdc\xebv\x11G\xab6\xcd5\xd9\x1amW\xc7\xceQzs\xa8\xc5\xe8=}\x10\xa0\xad\x1a\x04D\x10\xa0\xadj3b\x1a5w\xc9\x807\xbfU5\xb2\xf5x\xfd]ugDN48\x81\x1eD\x837\xf0\x03tX7#\xd7\x12\x8e\xa3\x00X)\x8d\xb3\xdb\x87.>\xaa\xdd\x02\xb2\xaaM\xf1\xc1\xaf\xf3\x14Y\x11\x8f\x84)\xc3\xf6\xd4j\x82\x10\xaf\xb4F\xf5\x98\x06z\xc2\xff\x8c\xf9H\xf5-\\j6\xaf\xbe&\x13\xc9\xd0\x19\x14&\xc5\x1b\xd3\xd1\x0c\xc6\xc2\x82D\xff\xda\xaalar\xad\xaf\xb54\xe7\x05ab\x9b\xe7\xac5\xd6\x1a\xec\xe4Y\xe5\xae\x1d\xb1s\xdd\xc7\x01n\x96\x06\xb8\xa9\x0c\x106]\xb7_$\xa9\x86;\xb8\xbfg0\x14.\xe7\xac\xa9\xcc\xb93D|\xc1\x83\x0c\x83\x9b\xd1\x1b\x98\xa3!G\xe2\xac\xf3\x00x\xcf!\x85\x97\xb0|\x0e\xcb^\xcf\x05\x8c\xea\xbe\xec\xc3\n&p\xed\xac\xa7\xcbY\x1f\x96\x8c\x8c\xb0\xaf\x86\x10+\xe6^\x99\xf4-\x0e\xc6\xb5p\xf7\xc7A<\x87\x0e:f,\x06!\xbdM\x1d\xd7E\x0f\n\xcd\x10\x88\xb3@\x17\xadi4\xc0\xab\xe8>\xb0\x01q\x8b)Q\xa4\x19\x944b\x924}\x9f5W\xc9%\xa6\xe0\xfd7!\x1b\xd5\x8d\xcd\xc9\xc6\xb3\x9d/<\xc10{6;\xc9\xe3\xc1B\xd4\x89\x9c!\xab\xc8\xa6NyT\xeb\x07\x12\xef\xd0\x19\xed\xed!)\x15\x14\xf5\xd9\xa6 \xac[\xe2\xef\x9e\xf8\xfbTKh?p\xf3\xc46]Y\xc0\x95\x87\xcd\xec\xcb0\xbf\xb5\x88i\xbc\xcb\x9a\x83A\xa0'\xd0\x92$VI\xe8BO\xb8\xd7\x82u\xa9\x14\xcf\xf9zU\x87r)\x1a\xa9\x96_\xf3N\xb7\xab\xe5+A\xe7\xab\xe5KQ\xbe\xe3\x0e\x12ZQ\xcb\xde Z\xbf\xe3:U^_\xf4^\x9d\xda\xb9h\xad*Y\xde\x88\xf2*;u\x88\xb1ws+\xb3\xf2\xc3[\x1eI;\x8e<\x9aT\x82q\x9e\xe0#\xb1\xee\xe5G\xaf\x18\x05\x17/!\x01\xf7\x9c\xdb*w_1\x0f\xa9(b\x0f`\x1fw\xc9\xc5`Q~p\xcc\xd8\x97\x8e\xdd\x04T\xef\xcf\x0e\x8a\xdd\xc9\xc9\x00\xa3\x8f]S\xa7\x8aG\xea\x87QC\xa7\x9cZ\x17\xed\xa6\xa6\xa13z\xe6*\xb9\xcbg\xad\xac\xfd\xe4\x87:W}\xb82\x1b\xc3\x1b\xa2\xe1\x08\xc2\xe5\xbcb\xf4]{>\x8a\xb5\xf8H\xff\xe0\x11\xd3\x0e\xafi\xc8M\xdb(w;\xbbr\xd5\x94\xa7\x9a\xa0\xf7\xe6 \xc8\x9f\xab\xe8\xf7\xa1q\xce\xd7\xf5\x8c\xa5P\xcc\xa3\xe3t\xd6\x0e\x8fi\xa9\x8b\xea\x84G\x11\x1f\xb6p\xa2)\x0f\xa7<\x98\xd3\xa6`\x85 M\xf0\xe9\xe0\\\xebM\x0bH\x83\xcfCt\xa7\xd4/\xc0\xb5\x08xH\x07\xe7\x9e\xbe\xc6]\xb3\xc5-\xa8\xd2#O\x18z~\xcd\xcd.\xd1\xd0\x91\x0e\xce\x93RZ\x8c\xbcE\xa37\xb9\xfc\x08c\xd8\x82|F\x18\x817\xba\xc2\x98\xa5\x0b\xe2[nq\xe4'\x11\xf1.ps4W\x0fDu\x86p\xcd\xb5=\xac=\x8fV\xc4oH\xede\xde\xc1\xea'c\xf2\x0c\x1at:\x9b\x02v\xe8\x14\xfb\x07\xda\xb5\xe2\xaf}tj\x15\x0e\xb2\xac>\x97\x83\xc6\xe0\xa0\xb9\xbd7\xa0aJcG\xf0\x1f\x19\xba\xbap\xdfPo@o\xfd\xd4\x11\xeed\x9d\xa1\xcb\xeb\xb0\xdd\xa6\xd8\xe2\x07\xce\xa1\xd3\x15\xfbn\xc3\xbb$~\x08\xde\x9d\x17\xd0.\x0fI\xcd\xd6\xf1\x83\x13rk\xd8<1N\"\x9cA\x13\x87\x9f\xd8\x81\x13\x9b\xa9\x01T\xf7e#Xp\xfc\x1d\"\xe6'&\x11\xe8\xdc.\xd5\x8f\xde\x95\x07\x9f\xd4\xf8\x8d\xc8\xb7\x08\xaf\xec\x89 O\xec\xa08uR\x94D\xad#\xff\xd8n\xe4\xfch\xd2\x0f\x9e{\x15\x0e\xce\x8d\x01=\xc3bR(`\x8b9\x19\x8e_\xfb\xb1\x8b:q\x19\x98\x99o\xac\xe2\xf0\x03\x8f\x84\x8f1\x8c\x98`\x1e\xe6\xe0\xa7 \x0d\x16\xb60\xba\x08\xe7\x0f\xe8&=i\xcb<\x81\"Z7\x9f\x85\xe77c\x08\x9b9\x93\xf3\xf9X\xcd\xf1\xaf\xfb\x18\xb8r\xf9i\xc7\xb1\xa4\xf9E@\xe0|\x14\x01\x9e\xd9\xf7#\xf1\xfd[\xb2\x01Gy\xbe\x8c/?\xf9]v\xc6\xe4\xe8\x1fr\xf4\x1f1\xfc\x0e\xfb\xd01\x8d\xb7\xdd8\xc5\xf8\xec\x13i\xb1~\x0dk\xf7\xd98\x7f\x8deQy\xbb*\xfe\x11\xb8\xd7O\xac\x1b\xf6RD.>\xe9\x83\xdc\x14\xdd>t\xcf/\xbbn\x1f\xe6\xdc\xd5Jx\xcc\\\xfaU\x17;=\xfaP\x07\xd1\x84\xb7\x9bc\x8a\xfcY!.V\xa0\x1f\x15=\xd7\xe0\xa1\xa8\xbb\xfa\xfc\x107O\x925Ppv\xfc\x97z\xf2\xf2\x92\x84\x8b/\xfc\xc7\\\xf2~\xf8\xeb\xbaV\xf9R\xad\xcc\x19\xc5b@nq\xa5&\xd4\x1d\xbb\xaes\xa2\xc4\x8c\xaa\x8d\x8f\x86\xe3fQP\x8ar\x07\xceJ\xae\x9ak\xd3\x15FWe\x9dtGI\xce\xca\xcey\xb67\x98\x80e\xd4\\\xe3\xd9\xc9jq\xe9\x07\xd9\x18v\x16\x8b\x9f\xe3\nL\xbc\"\x97\x8f\x841k\x80\x7f\xad>K\xd8\xb3S1\x8f\xceH\x0dTS^\xe7\xf2>Bti\xd2\xdc\xcb\xebH\xd6\x11\xaa\x10\xe48\xcd8$\x82\xe8\x18\x89\xb9\xd4\xc1\x84\xf4\xa6\xea\xb8\x89\xdd\x14\xe9\x07\xa8\x98\xa18Q0\x04\xecG\xbc\xaf\x1a\xb9\xf9#\xc6\xa4\xe0\x93#\xf1D\xc5\xe6\x8b\xc1\x82\xad\xb2\x15\xa5\x8b\x08\x0f\xfb\xfb\x80>r\xfc+a\x1c4\xbd\xe1\xbe[c\x0c-R\x9a\xe4\xc2Y\x0c~\x82\x1e,\x06\xbf\xe1\xffx\xbfr\\E\xc8\x0f\x92):)\xbd\x1c:\xcf\xf6\\G%\x15B\xbb\xba\xeb:j\x11\xa9*Xy\xbf'\xa5\x1e\x15rS\x9d\x1a\x83N\xd3\x1aK\xfe\xe8@G\x98@\xd1<1\xf4\x14\x10w\x1d\x1e\x8aD\x8bg50\x15\xc3u2\x06\xe0\xce\xb1k\x1d5.w\xd3\xb0\xc5\xa8n\x9cL\xee\x8d|\xd9Nro_+\x9aV \xe9\x1c\xb3\x86\x1ao\xc8N\x06x\x84\xbb\x03\xdc@\xce\x95\x8a\x15\xb6i\x91 h\x9a\x92\xca\xa9\xea\x0f=N\xb4R\x83\xd2\x92\xbb\xf2Z\xb57\x91\xa8b\xd6\xd8\xf8\xed\x05UIFm\xb9 A4iI\x90\x0f2\x96\x8b\x99\xc5\xbaf\xa4\x9c\x9d\"\xed\xd5\xac\x18|\x01\xf6\xc1\xef\xf5\x9a\x19\xc0\xc4\x90\xb6C\xfd\x88\xec\xc9\x9c\x02\xb2\xbd\xd9\xeb\xf5\x0be\x19\xc3\x88\x96\xa9\x0e\xd4O\x82\x9cE\x92'q\xc8D\x12\x89\x8d\x0d\x94/b'lb\n\x8d23\x084W\x9a\xd2\xd6\xd3eG\x90.\xc6\x03\x1e}\xc2\xf1\x07\xd7m\xcf\x95\x98x\x8d{\xf7[!\xba\x19\x8b\xa3\x07`\xf1\xc3q\xab\xbe\xea\xc5\xb6\x03\x8b2O#\xdd\x82}\x05\xa2\x81\x08\xc0\x1b\xd9V@!A\xf8\xf5KmMtgu\\\xdcuc\x94\xc1\xf2P\x93\x1b\x1f\xb9\xce4\x8f\\P\x87\x9cG\x12\n\xc3\xb1~%e\xb8\xa1 P\x8c%L\x85\x9aT\x03\x12lg\xd4\xa2\x9dt:\x9c\xa9m\xf5!\xd5gd\xc7\x167[\xb6\xc8Z\x19i\xda\x15\xe5\x86\xd6\xb7\x1e\xd4:\xfb\x7f\xd3\xd8\x87xj\xe8i\xfb\x0bzb\xffo5\xf4'\xea\x180N\xe9B\xc4=\xc66\x94SQ\x8b\x91f\xbb\xb1\xea\x8d\\d\xb9\x1d\xc5\x14\x84\x83\xf7Y\x8a.1\xc7\x17 \x8d\xaf)\x06v\x88\x07\xbf\xd1\x8b_\xfc\xb4\xfa\xac\xfc>O#\xad\xbd\xde\xcc\xf0\x91\xf6z3\xa9^o\x86\xce\xb3-\xd7!M\xd7\xf9ZNX\x1ay\xb5\xca+\x19\xf7ui\x13\xf0> \xa5\x00\x94\xde\x88\x90*\xa4\x06\x16o\x00\x9e\x035&\x98\xe6J\xeeE\xd8G\xbe\x9c\xa2\xdd\xc5\x97(\x88\"M\xd2\x0cPEScl4\xc8\xa3\xd5cl\x1c$\x04\xa9\")\xb6\x8d>V/)\xb5\"\x00\xc2\xaf|\xca\xf8\\\x9e\xaf\xbf\x00'qy\"D\xdb\x9a\x90\x81\x0cv\xe9\x04\xd6\x06\xf3D\x1e\x1d\x9fcgH\xae\xfd%I\xa5n<\xff9HR\x12\xceI\x10\x85\x1a\xad\x05\xc6\x7fC\x83\x1ey\xda\x98\x00z-\xf2\x7f\xe5\x15\x1d\x83\x1a\xaeq\x8a\xf2\xe3\x89\xc8\xa5\xadu)|\xce\xad\xda\x8frU\x95.M\xb5\x06\x92\xfa\xdd\xb1\xe0\\\x94\xb6\x8b5\xec\xc3<\xf2x\x94\x1c\x1e\xff\xeb\x94\xde\xa6G\xd1\x9c:]\x9d\x8e\x92\x8b~\x81;\x888\xe5p\xd6\xba\xb0Q\xec\xe3]\x92\x98x)\x8d_\x93\x94\x8c\xaby2@J|m\x00\xb1\x1e\xccI\x8a\xb7\xbel*\x8b\x06\xfc\xd6\x12\xe1\xbc\x0f\xedf\xbb\x16A\x08\xf5\xdd/\xc21\xc4\x06~\x0cS\xb2\xf2\x9d\xd4\xb4D\x80\xfb\x8e\xc7\xb2b\xef\xc1>\x86\xcf\xa5<\xfe\x0c\xcf\x0e\x1a\xa2\x9e\x1c\x1f\x19\xe6\xd4\xea\xdch2\xbd2\x9c&5\x93J_o\xa8\xc5\xc5\xef\x9a!\x8fLA\xae\xda\x804\xd0\xfe\xdaN\x95,\xb0>\xc1,\x8f\xa8\x15\xf1\x88Zq-D!W\x07\xe1ej\xcaD\x06\x8cf\xbapR\x0c\x93\xaaa\xc0\xa2p\xe1/\xb3\x98\\p#\xdb\xfa\x12/i\xda\"\x0c\xa0\xa2\x0djB\xcd\x07\x9e\xff\x8d\xeb\xa87\xa13\xaccm\xd5\x89\xc1\xf2*\xcbm\xa2\x8aNc'\x1e|\x80\x1e\xc4\x83\x8f\x16i^\xa4\xf7j+\xe8\x10\xa1\x9e\x8b$G\xc1\xf6\x82/\x7f\x18\xa4\x9c\xd0\x84\x1e\x9a\xa0c5E]\x08\x93blF\x93\x17\xf1\x1aOH\xe0\xb8U\x11\xd6v H\xe5\xa8\xb6\x82\xee\x1a\x8f1\x99}\xf8\xee\xe3\x12\x91\xd3\x1e4,\xb3\x96\xe8;\"o\xddt\xcf\xcfM\xf7\xca\xe8xbA\xc44n\x8d\x84\x11#\x11\x987\xda\x88n\xbe\xd6\x92A*\x00\xc3\x01E\x93\"\xa1u\x1d\x17r\xb0\xeb\x84(\x9f6k\x04\xdb\x00T\x82\xce\xba\xde&b\xf4\xd9A\xa32\x99_\xc2\xe9*\x15\xbb5+J\x0c\x01?\x88\xe9\x92\x864f\x0c\xd8\xc7,L\xfd\x15\n\xdd\xc2\xa9gIS\xc5\x95\xe7\x88\xach\xe2\xc4\xee\xc0\x0f\xe7\xf4\xf6x\xc1\xda\xaf\xbe\xdcu\xe1eM\xe3\xe5\x83\x08c\xa7\xeb\xae\x809&{\xd1\x0d\xa8\xe0c\xcb\xd6\xb7{\xec\xd4\xc2\xb4\xec\xfa\xb7\x94\xc8\xf9\xc8;\xd5yx\x11}S\xf7~\xb1p\xc6\xeb%\xeb`\x8b\xf7\xb5\xeb\xae\xb6\xa5\x18u\xd6\xeel\xf4;\x0c\n\xa37tU\xaf\xf8`\xd5\xb1\x9c/v\xd95\xab^\xcb7\x91\xdd\x93\xbb\xd5E\x14\xc0D~\x19\xd7\xccVA\x9c5\xfe\xc0O9@\xd0\xbe\xf1?\xffS\xfe\xec\xd6\xeb\xa3\x8e\x92\x87}}[~\xa9T\xa6y3\xc17e\xb0\xc3S\xb2\x14\xef)%\x9a\xb7\xf0\x92*BX\x95\xce\x94zMOX\xf7\x99\x91\x15\x04\xc2z.\x04\xc8\xf0\xa9\xa8\xe9\xb9\xad8w\xc7\xd4\x0d\xecC\x80\xb9\xa6d\x93\x0c\xde\xee\xe0&&\x8c\x99?\xaf\x93))\x03t\x93,Y\xd3pN\xe7')\x89S\x0d\x0c@H\x04E\xcd\xbf\xfa4\x98\x1bj\xa2C\n\x8f\xa9\xe4\x87:\x90\x820\x06\xefz\xd1j\xcd\xf6\x92\xa9\xa5k\x9ePA\xfbl\xa5qC\xc4\xf2)\x995\xd1Bhb\xce\xf4\xc0Z\x16\xbbfI\xd3\x0fr\xe3\x1c/\xf4#\xbc\x83}X\xb2e^:K\xe7\xbd3\x9d\xb9\xbaKS\xf48\xb9C\xb3(\x14n\x85pw\x87I\xb3ej\x91;\xcd\x8blD\x17h\x9c\xad\xde\xf9\x1e\x96~\x95\x028;+M+\xb7\xa5\xfa\x17\x15\xeb\xed\x93>\x9cT\x8an\xfbp2M\x18\x88o1MW@\x90\xc6\xb3\xe5\xfcIb\xa4(\xbf\xf8\xa5\xcf\xd7mp6\xc3\x83\xd2\x19\xb2\x0fW8m\x8c'\xaeu+\xb5!j$n\xe8\xaf\x9cs\xf5\x0d{dh\xed\xde`\xa7\xf9\x04\"t\xca\xe2\x1e]\x0f\xb9'\xcbU\xcb\"\x9f\x0e\xe5\x8e]Jk\xfa%\xd0\"\xf7+\xc4\x8f\x8b*vuY\xd97 \xb2}\xb8\xc8O\xe3\x074\xd6\x9d\xf2\xd3\x18\xf2\x01Ur\x1e\x82\\\xe0+z\xd7\x9c\x8a\x04\x14R35\xa46\xa8\xf9\xaf\xa7\xd2\xa8\xc4\xba\xbe\xec\x94\xbe\xa6qB\xab\\\xb4\xfa\x91\xa3\x83f;>\x91\xd9@\xde\x1d\x19\x15\xd4\xeaG\xca\x06\xe9`\x1d\xadMZM\xf5\x83\x0c\xb5\x98fn\xd0\xc3\x91\x08\xd3h\x84\x1c\xb5\xb8\x91\x92^l\x94\x1f\xb3\xa5\x1c(\x02q\xde\xde\xd0\xd6\x9e\x96Hx|`l\x91\xdf\xf7\xe1\xb4D\xe8\xf4\xa0Q\x0e\x8c1\x9c\xeaW%\xa6 m\xb4\x02\x91\x1f\xccz\xc1\xedp\xe8\xb5b\x9a%\x14y\xf2gBCy\x81;8\x17?B\xf1L\x81'\xffM\x03\xba$\x18\xa5\x84'\x92\xc4\xd2\x15\x86 \x95\xd9\xc0\xba\xa2\x94\xc4K\xa5\xa54\xbe;\x0c\xd3\xd8\xa7\x89\xcc\x97\xec|p\xfb\xd0i\xb0h,\xa2\x9d\xb3uG\x91\x17\xbaiWxo\x88P\xdbCW\xe1N\xb8v\x86;Kux\xea\xb4\x9eL\n;\x12 \x86X\x1d\xe1[i :z\xf0'i\xb4n\xa1\\\x03i\x00\x95\xa3\x8f\x19\xb7\xa5\x0dU\x05H\xd3\xe1l XP?\xb2\xb8\xd8`*}\xd4\x93p\x98\xd0\x01\x1eJ\xf2\n\x86-\x82\xf9eU\xd3\x14_\x93zb\x020\x83\x821\"L\x8c<\xbc\xf5\xe8:\xc5\xa8\xb4\x0f\xc4J\x06\x9c|\xa0v\x00\x156\xdf\xcd\xb4*vL\xa9\xf6\xd5\x8f\xd4J\x0d\xc4\x96\x140\xecC&\xf0\x16m\xc4\xc5NA\xef\x11\xae\x04\xaf\xa3\xba\xc4s\x86\xcc\x1d\x8b_\x85y\xe4\x12\xc5\xfd:\x1aHg\x9d\x0d\x18=\x07\x1fU\x11\xcfacC\x1b\x17B\xfd\\\x8b\x1c\xffU\xac\xf2\x1b\xcc{@H\xb1\xa4\x15\xf2\x81D\xc08\x8a\xc4\x9e$\xac\xb7w\x91\x97\x13\xe8\xd8\xe9\xd2pn3\x1d\x97\xad\xc8W\xe1\xc5>\xe4d\xabi\xa2 &\x8b\xb9kD6\xf4>tQ\xc3\xf1.\xf2\xba\x96\xd3M\xfd\x04\xe5\xd7\x85J\x18\x1bhw,\xe1\x9dm\xd0f\xb4P\xa3\xcc/0=/\x1f\xb0\x02\xb7\xa2\x10\x1d\x10\x9a\xc7\x01\xda\x96\x8b\xb9\x94\xdaV\x8a\x1b\x1b\xfe\\\\z&\xdfs\x8a\x8d\x0d\x7f6i\x1et\x1f\xbc\xa3\x0d\xd4\xfc\x1b\"\xf7F\x1a\xdfA\x92\x92\x94b\xd6\xf4\x1b?\xbd\x8c\xb2T(\xc5\xa2X\xde\x07\xb4Yy\xf8n\x10\xb7\xd6\xb0\x98\xf9?\x84\x84\x93\x8b8[\xa7-l\xac\xe5G\xe15\xed\x94*\xcc)\x95\xf1Z@~r&\xb0B\xa9B\x03\xbf+?\\\xb9\xaa\xa1\x18\n+\x10W\xb6rny-\x96*.-U3VI\"m\x10\xe8\xd5\xcfEL\xc9\xd57]D@}&\xa6)\xc5\xc6\xc5y\x8f\xfa\x02\x99>\xac+}z\xf0\x16Q\x01\x0e\xc8\xd4%\xbe2el\xcc\x17\xac\x9c\x05\xdb\xe5a\xe2s\xd7\xd7\xfc`@-^#wA\xe4\x11K\xfb@\xc4a\x99\xf6\xb11\xc7\xc2=\x8a\xa3W\x1do\x1f\xae]a\x0e,GA\x1d\xf2 \x06N\xbe\xf6\x00\xa4\xff\x16\x1cVi\xc58<4\xcb\xc6\x1fLJ\xf3\xc7\xf6a\x0c\xe2\xea\xa3R\xd3\xc9Y7\xb9\x83\x04\xf3\xc2\xfe\xd6\x98s\xd1D\x19\xc0\xfctf=\x84Q\xbc\"A\xa9\x07y5\xed\xa8o\xa4n\x1f\x0c\x1e\x7fz\xa0/\xfc\xd0O\x1a\xfd\x13\xf2\xda\x05\xc7o'2iNd\xda\xf9\xd3k\x88L\xda\x82\xc8\x84\xea\x8e\x11\xdbKe\x9csL\x0c\x95\xad\x81\xc9\x89\x17)\x8d\x19e\xe9\xa3\xe3\xb8 h\xf0P\xb2\xdd\xca\xdbC~\xfe\xfd\xa0)\xa8\x92\x80d;\xa2\xcb\x8d\x84\xdb\xb2\xa4\xa0\xd9\xb5\xb1\xd8\xb5\xcd\xfd\x81\xa26\x8b\xed\xbb[\xfd|0\xd9d\xab\x1f\xfb\xb1\x0e\x05\xc10\xcb\x11\xf0\x85GG\x8d\x0b\xf2\x03&\xca\x07\x82\xef!iJW\xeb\xb4\xfb j*\xb5\x01x\xe32\xae\xea%\xad&\x82\xea\x0eR\x94\n\xf6\xe5\x91Woc\x8c7`\xe7\xecc\x9adAzDVt\x0c\x0d\x01-\x18]{\x17yc\x83m\"p\x85\x0e?\x9d\xb8\xe2A\xa1\xab9u,\xc4@\x03q\xac\x95VM\xc0J?sy\xf6\xbcA\xcd+q\x95\x9f\xf1\x8a\x9eI\x89\x0fs(\xf2\xe6\x1d\xea\x01Q\xcb\xa7\xe9D\xaa\x82[\xfb\x0e\x11Z\xe5S\x07\xef8\xa7:[f\xb1\xc8\xfe\xe0\xdc\x0f\xaf#\x8c\x02j\xb3\x15P?\xb9\xdd\x80U\x8b\x99\xb7f\x8a\x95(?\\s\xc8\xd6n\xae\x11\x08rm-\xf8 \x90 \xa6d~\x07q\x16\x86~\xb8\xb4\x89\x01E\xabZc\xf9jU\x95\x1e\xe5\x19\xc6\x0d\xd9\xf0\xe5GL\xf4\xadA9\x0e\xcd\x9a\x85\xb0\xe0\x00\"<\x96\x10O\xfd\xe7\x8d*Z\xc9\xf6\x85\xf9\x06m&\xef\xa4\xa9Q\x10\x0dg\xe8\x14B\x18\x064\xd3W4\x96m\xd32\xc8\xca\x08\xe3\xeb\"\xafns\x1f\xa0(\x85\x1a+\x7f\xa9x\x06\x12\x13\nZ\"\x97\xc7\x85Pjb\xc3B\x0d\xdb|\xfe\xe4\x92\xb9\x8a]E\xa3\xcd0+\x90x!q\x92m\xbc\xcb~\x9b\xde\x01\x9d\xa9j\xba@\x07_m\xf0v\xe2C/1\xb6\xa1BU\xc3\x01\x97O\x9d\x82o\xe5\xad6l\x18\xd8\x87\xb9\xbd\x8a\xd4\x17\xdd\xe4D\xa8\x19\xb1K\xdcq\xd2\x9a\x99\x10\xc0\x957 \x13\xb8\x841\xac\xfb \x8e\x8b\x87\"i\xe3u\xa6\xfa\x11I\xfd\xb0\xabvZ06\xc6\xb1\x18k\xe3\x0b_\xb3\x07T\\MrQ\xc3\xc9\xf1\xae\x90Y\xa4ZV\xd2\xad\xc4\x8eX\x06F\xbaV\xfa\x99-}\xd8\x07\xe2\xf6+\xc97M\xc7\xf0\x8d\xed\xc42;S4\xaeX\x8ai\xb5$z\x99\xd7\x89\xc4\xcb\xdc\xb3\x07\x87\xd1v\xa6\x8d\x11\x1c\xda\x0eQ,E\xc3\x08\xdb\x0e\xab\x15\xd0\x0f1\x9e\xa0\xe1\xe1\xad\xed\xe1\x89\xed\xe1+=0\xa6R\x01\x91c\x9d$=\xb3\xfc\xce\xcal\xd8&?\"hg;\xf1Le\x83\x05\x93\x84v\xb2\xadW\xb7j\xee\xaa\x9f\xf0\x95\xc5\x9a\xb4Nu\xd4\xd1\xa83\xb1\x19\x1a\xe4]\xf9\xad,\x8d\xe9\x8dt\xa7W \xda\xc0\xc3A\xc9\xb2\x90\x07\xbc\x8ey\x90\xbc\xa6\xd7@\xe1:n\x1c:\x0dg\x18a n\xc9{Hr\xd5\xd9\xdf\x177Fm:\x04\xe5\xa8\xc9\xda\x13a\x10\xd7\x11 \xbf@n\x1e!\x14pE\xcb=\x8dE`\xa0(E\x03L\x05\x8bV/]\x17&r\x1dr\xef\xa2` \x9e>\xc8\xb8\xa3\xfaI\x1d\xb9\x99\xa8X\xa2V\xaf~~\x88\xeb\xae\xfaI\x9d|\xd3>\xacC\x17\xc6u\x10|\xd5\xd4\x93\xdc$\x01C\xc9'-\x07\xd2j\xc8\xcd\n\x04\xe2d-x/\xb1w\xd2Z\xb0\xf8R\xad\xb6T\x08\x14J\x06\"K;\x87\xa0\x8f{z\xcc\xa8B\x9dv\xb5\"]\x07\xd6\xc8/<\xec\xa6\xd4\x0bL\xe5\xfd\xacF\x11U\xb0\xb9F\x99\x13or\xea&\x0e*\xb3\x92\xb6`\xac}L:/\xc74\x10\x80\xa9^\x1f\x17\xca\xd8\xc2PB\xcc\xd5\xd0e\xaev\xbc6\xd3\x84T\xc3:\xe5\x1d\x943\xd0\x9f^\xd2\\\xa1\x02\xf3\x88&\x10F)\xac\xe3\xe8\xda\x9fS \xf0\x18\xdf\x7f\x0c\xbcA\x93b\xc8\x86\x0b\x9aH}\xdaE\x8c\x90*\xc7}e%\xc5\xa85\xf4\xb9&H\x0bz,\xf1\xcf\x02\x80Hh\xc5\xebK\xac\x81\xa8\xbc\xeb\x89\xf4B\x90Tm\xe0\x95\x88\xe0\xed\x9dt\x8a4D\xe8\x9dfx}!\xe2\x99\xa7\x85B_\xa8\x9b\n\xee\x02\xcf\x95\xb4\xa4P\xb2\xdb\x19\xe8f\xc0\xb3\xcd\x8f\xcb\xef6\xa0@\xbe\xfc|\xd0\xe0s\x1c !\x88#\xc4\xd4W\xab\x9d{lwa\xd1o \xae\x1d\x1e\x03\x9d\x0egu\xf4\xa9\xaf\xc3\x88\x9b\x9ar\xa0\xc9\xcbd\xcc\xc72\x9a\xb9}\xd8T\x1f\xabz|\xa0\xdc\x1d>\xd7\xd2c\xd1\xd6\xcc\xad\x9b+\xa19]\xdan\xce\x1f\xecs\xa6\xea\xed\xd9\xfd\xbd\xf6\xfa,\xcdMR\xa4L \xbd:R\x8e\xbf\xa5F\xf6\xab\xd1\x94\x0d\x03;\xd5\x0f\xac2W\xd8\x87\xa9}]\xb8\xa9G}e08\xacd\x92\x8f9\x10\x8b\xc8N M\x9d\xea\xfd\xbei\xa4\xef\xf5#E\xaaj\xd3\x16\"|\xa7\xc4p\x07\x81\xb4]\xa1\x12|\x7f R\x9fom\x8fJ\xcf_\x1d\x7f<,?/eU\x1a\xbc>|s\xf0\xe9\xdd\xe9y\xb5\x9fQ\xa5\x1fY\xef\xcd\xa7w\xefJ\xf5\xb6wJ\xf5\x82\x88\xcc\xf1\xc2\x94}\xa9>8\x08\x82\xfc\xd9\x01\xe3 \x8a\xc7 Y\xd0w\xf2]\xf9CWA\xb6\xa1\xfcV\xab\xcd\xb3\xd5\x1a\xb95\xf6\xa5\xfa\xfek\xf9P\xfeP+\xfc\xf5\xe0\xfd\xbb\\q-`\xb0W\x9a\xdb\xfb\xb7Go\xdf\x1f\xbc\xb3-G[0Z \x98x\x84\xbb\xedv\xd9\xb7n\xe9\xd9\x9a\xc4\x18F\xd1w\xba\xf8\xb5\xfc\x14\x93\x19\xcb\xe7\xe2G\xb9\x06\x99\xcf_\x95<\xa5|\xa7[.\xeb~\x93M\xfc\xb4\xea\x06\x1d\x15\x00-\x95\x8b\xb4Z\xdb\xfaDq\x08\xbdRyV\x80\xacT\x9eh\x9cE\xad^\xa1\x01F\xbd-\x15y\x18\x07\xbaL\xaba\x1f\xb6\xcaE\x0c\x81\xb6\xcbE\xf3z[\x97\xf5\xb6\xae\xebm\xad`\x1f\x9eL\xcfn\x87\xc3\x8d\xb3\xdb\xe1\xd3\xb3\xdb\xe1\x8fg\xb7\xc3Wg\xb7\xc3\xc3\x8d\xb3\xdb\xd1\x9b\xb3\xdb\xbd7\x1bg\xb7O\xb7\xcfn\x9f\xeen\x9c\xdd>{s\x96\xbdy\xf3\xe6\x10\xff\x7f3\xbb\x9f\x9ee\xaf\x9f\xb2\x97\xb3\xd7?\xbey3s&\x1dV\xf2\x8a\x97\xb0\x1a\xee\xbd3\x19O\x7f/W\xbb\xff\xdd\xadT{R\x1e\xd6R\x0c\xeb\xe9\xceY\xb69\xdc|\x8a\xff?\xab\xd6\xba\xc3Z\xfd\xb3\xe9\xd9\xec\xec\x1fg\x9f\xab\x8f/\xd8\xe3\xdf\x9d\xc9\xb8s\xdf\xe9\xdcw\xa6d\xe3\xefg\x1b\xb3^\xc7\xfd\xf3\x13\xbf\\\xf3\xbc\xa89\xfd\xbdh\xcfu&\xe3\xff\x98\x0e7\x9e\x91\x8d\xc5\xec\x1f\x9b\x9f\xef\xf9\xf7\xbf\x9fm\xfc_\xcf\xcf\x9e\x9cM\xc6\xff\xf9h\xff\xacw\xf6\xe7\xfe\xf9\xd9\xa0\xf3?g?<>s\xce\\\xf6\xf6\xcc\xfd\xe1\xcfO|\xddYqc<+F\xc3\xc2\x8an\xb4\xc5\xbf+\xd4\xbc\xde\xd4\xa1\xb1\xa9gEK[\x9b-Z\xba}HK8\xbe\x87\x8e\xf5\xc4\xd8\xc3\xf6v\xd1\xd4\xb3\x91\xf2}K\xe9b\xb3\xf4c\xa7E\x87\x1a\xbd\xbaF\xc5,\xc7\xf0\x14^\xec\x0bgI\xf6mg\x0f\x13Zn\xb0\x07cx\xb6\xc7\xca0\xaa\xf8\xd6&\xdc\x0b\x9bF4a\x1c\x0d7\xd1\x9ca\x83U\xea1\xb0\x8cacd\x1d\x98F\xff]\x8c\x82Or\x02\xdd\xb3a\x97\xf7\x9c\x97\xfc\xff\xb0@\xadr\xc1JF\xa3]\xa5(\xc5J\xd5\x82Q\xbe\\\xac(\xe4EjK\xd7X4\xdcT\x8a\x16\xbc\xd6\xb6R\x14\xf3Z\xa3\xa2\xe8\xff\xcfJ\xb6\x94\xd7\x00\x0b\x8a\x97\x1ew\x1f\xc3\x18\xb6\x95i<\xc1\x11\xaa=\x9d\xb1\x92=e8\xff\xe7\x7fc\x9d\x1d\xa5\xe4\xff\xc6:\xeaL\x91*\xb0\xd2\xa7\xc3J\xe93V\xda\xedZ\x17\xe1\xc0\xb8\x08\xb8\xfe\xbb;;[;0\x01\xeet\x87y\x0b_]\x92\xf8U4\xc7\x9c\xa8c\xed\x83\x9d\x9d\xcdg\xbb\xd0\x03\x87!\x0eka\x17^\xbe\x84\x11\xe3uvv\xb76\x87\xe5G\x8f\x18\xbc\xb7\x14o\xd9\x82_\xcb\xed\xe4\x8e\x85\x9a\x043\xee9\x9b;\x8c5\xfb\xa0);\x054\x97;\x85\x17\xb0\xb9\xb3\xfb\x1cN{=\x17\x8e\xa7\xa73\xd8\x87+\xe7\xd4\x85 \x8c`\x0c\xc3>|(\nu\xc4\xe9\xbdV\xc1\xa9\\\x94Dx\xdf\xc7\xc3\x17\x0f\x16~@C\xb2\xa2\xa8,\x0b\xd7Y\x8aN\xb4Q\xe2\xa7huH\x07\x81\x1fR\xb5\x0c6D!:\xd0\x97\xe6^\x1f\xcb[\xedX8\xcf,\xc6i}\xff\x0f\xed\xfbt\x10\x85\xbf\x918\xf4\xc3%w\x8d\xce\x7f\x8a@\x85\xa8U\x12\xed\xeb\x16\x87\xad\xcbQMe\xc4\x18\xb7\x9a\xd1\x99V\xb9{]$\xa4\xab\xcb\x8e\"7\xf0>\xd0\xc15\x8d\x136\x8dG\x8f8$\xba\xf3l\x1d\xf8\x1eF\x1d\x84h\x01\xff\xc1\xba\x84\xb9\x1fS/\xf5\xaf\x91\xc7\xe2IC\xf2\xa4:\xf9\x9b\xe5\x9a@<\xc6`&@o\x89\x97\x06w\xc0d]\x99\x03\x12\xe3E\xb3A\xb0-\x85w\xe0O~w\xd8\xa17\xeb\xb9g\x03\xf9\xed\xcfO\x06\xf4\x96zN8\x1d\xce\xb8\x17\x1b\xef\xc8\x0f\x82\x8dE\x14\xaf\x98\xa4\"\x1a\x04L\xb0I\xa1>Z\xc6\x8e!\x03\xf96L\x9d\x18\xc3B\xe2^\xf1\xcb\xe5\x9b\xb2\x9c\xcf.*z\xcbB>\x13r\x11\x88\xf6%\xccD\x9f20\x1b\xe7?\xe5\xc3}\x081\x12%\x1dx\x97\xd4\xbbz\xe7\x87\xf4\xc7\x98\x92+\x0c{\xc1v\x90\xec\n\x0d\xdc7\x8b\xaf\x7f\x88^\x93l\xcd8Y:o\xe8\xb4\xb4\xba\xd5\xccb\x07?=\x0c]\xea\xb8\xb2iX\xed\xd3\x83\x9f,\x8b\x9d\xdeDE\xc2O\x06\x988\x07\x08\xf2\xc7\xb8\x0e\x17\x83\x94&\xa9\x13\xa3\xa8][\xda\x94,\x81'o\x01g\xe1\xc7I\x9a7\xe8J \x94\xc6\xc0zI\x84\xeef\x90\x92\xe5{\xb2\xc6\xcb[9\xe2\xc7\xe9%\x8d)\x9a\xbb\xc1:\xa6\xd7~\x94%\xc1\x1d\xcc\xa9\x17\x90\x98\xce!\xc9\x16\x0b\xff\x16\xa9b\xf71\xf4 \x86\x1e<\xee*\xc3x\xec\xf6\xe1\x9c\x0f92\x0fy\x1dS\xd6\x8c\x93P/\n\xe7-\xc6,\x07;\x8dg\xb6xr::\xfa\xd1b'\x89\xb7\x0cy>\xb5\xf2\xba\xa2f\x10^\xe8QA\x18\x93Ib+\xdcH\x11q\x8c\xd1\x81\xf1(\x89\xb8\x83\xad\x8fw\xbfB\xed\x06\x11\xbc\x00\x9f\xfd\xe9\xed\xc3\xc8\x15<\x83C\xb0\x8e'\x8e\xb4\x03\x06PW\xf0~/\xf6y|8\x82|\xcfh\xb4=\x1a\x8d\n`\xd3\xdb5\xf5\xd8\x9e\xb8&\x81?\x87\xbf\x9c\x1c\x1f\x15\x11\x0cuv\x8bhp\xb5\xe2\xab\x96)4\x84-E\x92\xc6\x94\xac\xd0\x16\x89\xf8a\x02a\x14n\xacc?\xe4[=o6\xd1\xb6+n=\xd8\xbc2\xd3\x9ai\x96\xecu\xb1d5\x87M\xbc\x7f\xe1\xeb\xd5\x87\xa0\xdc'B8\x1e\xf8 \x17\xfd\x9cP\xc1@\xa1\xaaY\xd1xIaE\xd6k?\\&\xcf\x11\xdb\xc4\xdd\xd6\x1c\x92(\x8b=*.9\xd8&P\xc9\x1aC\xc3\x8c\xaf\x1e\x13\x16\x1d\xc58\xf6\x8a\xdea\xa2\xb7|A3x\x01\x01\xfb\xc3\x17\x14\x9dd\xa6\xd9,\xdf{)\xda&`r!\x1e\x95 \x9c\x12\xb6\xeb\xf9\x0fU#\xae\x03\xcf;\x05\xa3\xd5t\xaa:P\x05}\xf0\xeax\xcd\xb0\x90\xb3MN\xa4\x9e2y\xc4\x11\xf8\x07\xe6\x83N\xc9r|GV\xc1 \x8a\x97\xfd\xcd\xe1ps\x8c\xf0\x13\xa6\xf3u4gm\xf3\xf4\xd2~\xc2\x99\"\xdf\x96\x958\xe0\xe0\xf4\xf0BL\xc2.\x80\x17\xe0\xb1?\x1cv\x12\x17\xfci0\xd3\x9b\xe4!\xf6\xe6\xd5\xeau\xf09\x1d\xfc\x91\xf0\xbb\x95$\x8f\x82\xcc T\xa7X\x13^\xe0p\xbe\x08\xd8\x1e\xc3\x0c_5\xd6i\x1f2\xfe\xa4`\xb0\xca|\x01\x9dK\x14\x83+z\x87!M\xd2i\x84\x17\x7f\xf9\xadM8\x8dfZ\x01(\xb5.\xfe\xa7V\xb2\x94\x102D\x8aMN\xa3\x14JR\x8c\x1c\xf32\x15?{=&Vl d\x98\x80\xa3>\xea\xe7\xa2\xa6\xb5E\xce\xcb\x15\xaf1\x1e\x9d\x83\x87\x00\x02\x16\x9d\x9e\xd8\xf6\x92\x84\x8aSx|\xd6\xc3\xe4C\ng\x8a\x13\x90\x8dY!\xf37\xd3\xd9]J\xc69\x94\x19\xfflSx.\xb2~GZchqyr\xe8D\xees\xd7\xd4Z\xaf\xa7\xb6\xa7\xdd)\xb8\xdb\xb6\xb8he\x08\xf0?\x8f,\x979mz\xd6\xbe\xfc\x19n.}\xc62\x8c\x86\x05#7\xda*\xbe\x8bb\xc3\xb8;7x\x14\xe12\xd6k t>a\xf2\x90f@\xf7!fx\xc5\xd7\xfbm8\xe7\xe6\xcd\xc3\xe7R\x90e\x0b\xa0>d\x95\x1f<\xed\xcf\xba]\xb6!8\xf4b\xba1G\\e$/\xf8c\xcel\xce\xe9\xc2\xf7|V\xec\xe3S\xe4\xfe\x91k\xb3b\xe5\x1b\xc3~\xed\x8bD\xb3r\xc8ZR\xd0q\xb6wpl\xa6\x8d,2\xe7n\xefr[\x01\x0c\xfd$\x84\x96z]\xe81\x82\xdaTe\x93\x13\xc1\x90m\xc5\xad\xbe\x80MC\xff\x9d['u\x1bd\xc8\xbfke\xc0QNjTf\x81\xeb.R\xcc\xda\xcfc\xce\x15\xcf\xe2AL\xd7\x94\xa4N\xf7\x0c\xcdd`\xa3\x94(K\xd7\xf5\x8f\xda\xae\xafE\\A\x89Q)\xd1X\xe2\xf9\xdck2\xf4.\xaby\xb3A\xa8\xa5u\x99Q2M\xae\x11\xeetQ\x08\x95\xbcM1=\xfe\x831\xb8\xf2;;,\x88\x90 \xda\x11+lk\x9b\x93\x13\xfc~\xebX_Dtp5\x97\xbe\x92\xb9\xed\x0c\xfbP\xa6\xffHbY\xf1\xc6\xc8\xad\xef\x96}\x06c\x99\xbb*\x0b\x82v\xa3\xafu\x9f{.\xf0\x0d\xc2O\xdf\xdf\x04q_\xf0<\x1e\x1d\xcc\xce\xc2\xbb\x92\xc8\xe1\x96\xc7\xd7\xa6\xf3~q\xd8#-\xc8\x8f{1\xa5\x97\"^\x8c\x00\xb0+\xce\xb1\x0b2W\x89\x00\x93Z\x08$\xf4o\x19\x0d=\n4Lcm\x94\x80|b\x15\"\x93ji\xa9$\x01\x9dL\xe0\x08\x13\x9c\xd0W'\xc7\x1dd'\xe8\xe0\xca\x0f\xd1\xaaG\x8e\xa0\xdb/6\xd3>\xe3\x0c\x9b\x18\xca_\xcd4*g1\xf95\xbev\x07T1\x9dMq\x8b\x9f&N\xf3\x11P\xd8\x0f\xe8\xdaQ6\x0c\x9b\xbfI\x03C\x84X\xc9\xafv\x18U\xde\x15\x1cP\x9b\xd3\x82\xf1@\xc8\xcfw\xcc\xdcA\xe5\x851lq.)b\xef\x12%\x01g\xb7\xd3\xe9\xb6o\x85\xbf\xd1\xedC\x99\xd11\x98<\x1b\xd9\x816\xdd\xd5^\xcc\xd9\x00\x85\x0b\xd8\xdd4\x1e\xfd\n\xe5(lF\xd8\xecc\x9d \\\xdaem\x86W\xb0\x89Y\x98K\xb04\x9cK\x9d\x80\x10Do\xfc\xf4\xd2\x0f\x81\xc05\x8d/H\xea\xaf\xd8\xcaW\x15<\xa6p \x82sS\xe6\xdb\xb9\xe5\\\\\xbe\x9al\xaf\x11\x98H \x98,\xa5\xceC\x08\x90B\x10\x06z\xeb\x05d\xc5\x11pE\xe2\xab\xa4\x9b\xa7k\xae\xc0\x82\x1dP%\xf1\xa1\x87\xc9\xed\x84bG\x95QCR\xd1\xe9T\xfaL2\xef\xb2$r\xcb\xcc\xe5U\xf4\xe1\xa4\xbd\x1d\xdc\xeb\x0b\xdd\xbc\x9ew\xb9R\xaa\xd0\x15\x18!\xb5\x08\xa2\x1bF.\xd9v\x8d\xe2\xd2\xf8\xcb\xab\xa6#\x7fx\x90u\xce\xf5\xfd1x5\xc0h\x8c\xf6\x1b\xb1\xcb\x03KH\"\x1a\xc3\xb8\xae\x06\x0b]\xa5F\xaep\ng\xa8\xe6\x1a\xb3]*N\x89\xa2\x16+\x93Ou\x8f\xeb\xf2\xb3\xac\xcf\xb5mY\x98k\xd6\x94UG\xcdZ\x88\x9a\xb5\xc7\x98\xda\xdeJ\xbc\x7f6\x13o\x0dY~\xca\xc9r\xf8\x15d\xd9\xcc\xc8\xe8Is\x08\xa2\x86J\x9e\x0d\x03(af\x15\xab\xe5\xc6\x0d\xc5\xc6\xe5\xa2f\xe7\xc4 \xd9\x0en\xd3\xa2\xf6\x84U\xb6M\xae\x03)\xf6cy\na4\xa7\xb0\xca\x92\x02\xdfH\n\x01%I\x8a\xaa{E\xcbV:\xa6\xed\xbb\xa9a\x81\x7fS\xb4a\x9as\x01\xddqQ\x1b\xb6\xea\xc3\xb2\x0fw}\xb8\xe8\xc3y\x1f\xae\xf8e\x94\xe6\xd0~o8\xcc\xff0\x1c\xe6\xcab\x07~\x92\xd2\x90\xe6\xb2\x12\xff\xe5t\xa35\x0d1\xbfx?\xc7~~}\xa3@A\x16\x08~E\xfe\xcc9\x15^\x80jO\xd8Gc\x88u\xc1\x97-\xf8W\x11q\xad\xca\x88:\xefs~\xb5\xcc\xbe\xc1\x84\x03\x01\xd3_\xa9B\xa6\x90:\xf0\xba\xae\xfa\xf0\x85P\x84\x9d\xa2\xf1\xa5\x8b\x17\x1e\xec\x85\xd3\xfa\x19*N\x14\xe4\xa0\xee\xefq3>w\xcb\xc3\x9b\x14\xa3[q~\xec\xbb\x0c\x12\xc6\xd8\xbcn\xfdV \x832\xbfg\x83\xf4\xf3\x1b\x9cS\xf6`-6\x15\x93\xfa\xce1\"w\x0et/'i\x98\n\x80\x1d+}\xb8*\x1f5\xa5{\xc4\x1cR0\x01\xde+\xca^W\x08\x9c\x87\xdc\xb1\xf4\x0b%ob\x96\xce@X\xee\x98%4\xf6YXBr\xcf-\xcf.%Nj\x9f^[\x9f\xae\xacO\x97\x86\x0d\x08\xc2\x8eF\x97\xa7\xf2\x0b\xe4\xc7\x85PY\xb7\x93\x1f3\xa3\xe7\xbf\xf4Vn\x16'\xfbB`\xe6B\x1b\xa9\xf0\xb4\xbb\\(@\x81f\xe7\xa9\xf8~\x7f\xcfhyl\xb5\x84F\xad\x13\xd2\xc1\xb0\x0f^.\x02\x1auP\xea{\x8a\x80\xd7\xe8F\x880n\x03\xb1C'c\xfb\xdcP\xb5\x81\xbfR?l\x84;\xdc\xde\"s\xe1\xd6\xd4y\x85S\xce9F\xc2X\xf8\x94&k\xe2)\xa7\x8f\xaa[\x05td@\x0e\xfa\x8a\xdemp\xd3\xea\x84\xae \xf7\xf0\xc8\xd9\xe9\x8b \xf2\xae\xa4\xd6\x9a\x1d_(l9x\xd7\xb0\xe8\xc3\xbc\x0f\x97}\xb8\xe6w\x05n\x1f\xf7\xc6\xb5\xa0\xd2\xa2\xe8N\x109\x81\xdc\xc8|\xb2\xbf\x97\xf9\xfe\xc57$\xc1\xb7\xc3\xa5e\xf2+\xa6\x04\x88\x97vF\xe9\xba\x91Q2\xe5'a\x80\x17\xe6\xa0\xce\xba\x19\x17\xf8\x9d\xd8\xb3\xad\xbe\xd0\x83sM\xac.P\xbd\x85\xf2\xb1>G\x9b\x9caX\x1beQ\xf9a\x1d\x8e6wD\x8fC\xde\xe3?\xda8\xf4|\x01[\x15\xbb}0\x80\xa1|\xf2\x0b\xfc_[\x19\xab|\xab\xb1\xbd\xda\x06\xbc\xe2\xbe\xb0.\xbe\xf2\x9b4\x8e\xbb\x97%\xdc\xbdVp\x97\xd1\xdb\x1c\x7falR\x1b\xc7\xe6\xc3d^\xf0\x1f\x9c>\x82\x17\xadV\x04.hzC\xa9P\xf8xQ\x10P.\xc0R\xeeD\xc8H\xa3\xc7\xb6\x95H~\xc9\xc5=\x1f\xef\xd99\x9a\x88\x13a\x0dm//@F*%\xf6\xeb\x8a\xd4\xcdU\x0e\xe5\xeb\x84@\xb9N\xf0\n>%Q(h\xa9\x19\xe3\xc2\x97\x05z\x02\xf9\xe5H!\\ \x8ew\x8d\xe4Xj\x9b\xdb\xe0Qe\x04\xba\xb1/\xca$\x9f\xad1\xd2\xb8\x18\xe9\xbc\x874d\xc1]\x81'\x10\xf3{\x13\xac\xc0\x17A\xa9\xc3*\x89\nI\xb5ga\x1e\xde\nI'\xe0\xcc\x1f0G\xd6-\xd6\x1f\xb5\xd8\xb3\x0fQ\x13W\x90\xb1\xaasd-\x9d\xb3\xd1\xa2\xee\x83 \xd9<\xfdn[R]\x15T\xe7f!\xd5$\xf0y\x96g\x0b\x0c\x8a\xab}\xb4\x86Z\xfe9\xf9\xd1\xe9\x01 \xa7\xa9b\x11I\xf3\"\xba\x82\x87\x7f0\xe1\x16\xb7\x08\xa4\x15\xddntP\x04I\xa6\x95\xab.\x8f\x04$.S\xacnW\x12\\b\xf0deC\xdb\xde\xb2N\xbf.h\x89\x1bU\xe22\xfc\xdcg\xe4k\x82+-\x1a\"\xc8\x7f\x8d1\x80\x17\xc7K~=\xcd\x99\x1b\xef2Z!w\xb3B\x86\x92q-\xfe\xc2\xd7[\xe1A\xb3\xd8\x83b\x80\x83\xc4\x83\xbbI\xa0\xbc\xc8\x93ne\xb9\xb3D&\x9d%6F\xbfF\xf1`\xdf\x18\x11\xbe\x8e5\x0c^\x87\x0e1\xea\x16\xac\xe65m0D?\x0ey\xaf\x86]\x9b\xf9\xfe-\x89Y\xc6!X\xc7\x07_3FP\xc7\xd9\xb9q\x88r\xcf\xad\x19\x90aC*\x1b\xce0P\xc5\x1a\xa8j\xe4\xd37\x8d\xbe\x9d\xf2\xc4\xe9x5Y\xe9\x05;\xe4\x1e=\x92\xd6CDc=\xd4\x06b\xe6%\xebxP5{x \x0bdC\x169{\xc1\x1f\xb8}\xb8A\xd4[\xf7z_\xbc\xd9\xeb\xb3\xb3\xe3C\x82\xf3\xbe\xae\x98\xd3TLf\x02\xf4A\xe9\xc1\x1a\xc6\x8c\xb5\x1e\x8b\xb70\xc4\x88\xcc\xf1\xa8\xd8\xe2\x9c\x85M)\x0f\xecA\xed\xcd\xaa\x0fa\x11=\x01\xb6Q\x18\xc7\xb0\xca\xd9\xb8\x96\x83\xe7Zo\xf9\xe6\xc8\xfa\xe6Z\xf0\x8ccA\xed\xd60\xd1M\x17\x90\xee\xd8\xdaix^\x1e!\xb7\x16\xee\x0c%\xe9\xea\x8b\x83\xbbj\xfe\x05\xd5M\xf8\xdc\xfd\n\\e\x9f\x8fB_\xaaj`;\xa3\xb6\xa4\xd3(@W\x8ek\xc9A=P\xbc\xd53'[\xcf\xbe\xfez\x12\xdar\x0bUi!\xc6\xec\xbd\xfb\x9a\x0b\xc76\xe3\xb1\xb0\x1c[\xdc\xa0\xdf\x9a\xf2\x82\xd5\xfb(8\xf6\xd2\x821\xee\xbe\x01,e\x9e\xa5\x00\x8cE\x17\x18\x97\xe6Y\x85D\x19\n\x863\x0e\xa9\xd7\x8d\x83\xb7\xe6\xf9\xd0#1b4\xf6\xe3\xb2\xc3H\x88_u\xf0\xf2}\x94Kt\xfb\xfb\xfb%\xc3\xdfG\x8f\xb8\xf1\xe4\xc4\xca\xefK\x1f\x9f\x82\xe3O\xfcp\x19P\xf8[\x16\xb1\xaab\xedEBJ\xf3,5\x1b\xe9!b\x86\xbe\xd3o\xb1ST\x01\xc3\xb0k\xb69z\xb4P\xd3}\xfb]\x13\xa29\x85v\xd7\xb4\x18\x8fU3\"|W\xb3|\xd0Z\x8a6t\xabC2!>\xaa\xb16e\x9b-\xf6\xa2\xae\xab\x9bvW4\xae\x8a\xfd\xe6}\x98\xeb53\xee/\xca\x90\xfex\x9a\xcd\xdc\xd2\x01\xf3\x01}G\xd4I\xb6h\x11%\x9c\xd1\xa60\x83\xc3`\x93l/m\xa2+\xf1^.\xcal\xc3\x18\x9e\xee\xe4?\x99\xd80t\xe1%\xfb\xaf\xc5]Y\xc4/\xb4}n\xb4\x1d\xb1\xf7\x9eC\xb4\xb1\xe1b\xef\xaf\xda\xc2\x8a )0\xc1f\x1c\x1f^\xbc\x80m\x17z@r\x91*\xdf\x81\x97\xf4\x96\xcc\xa9\xe7\xafH`wiR?*(\x0f\x1c\xbf\x82/f\xbe\x85\xc3RR\x81\xab0\xba \x81&\x1eY\xd3\xdc\xd8\xd3\xd6u}g\xd8)iVPR\xbe\xf5M\x94\xb4\xde\xf0w\xa2\xa4\xf3(\xbbhCI+\x83i\xc1K<\x84\xb4\xeaG\xa1%\xad\x8a\x1aG\xc95o\x0e\xbd\xc6!\xad\xa7\xaa\xdb\\\x87\xd1|\xf1\xdd\x86\xaa\x1a\x1aie\xee\xc4M\xe0n\x85\xf5[\xe7\xc4\x89\x19\xd9l\xd3b}0\x0f2y\n|\x92<\xc8\xe2Ic\xfc\xd8/\x9b:)*\xf5J8\x16\xd5\x10\xf2q\x16\xe6j\x80\xb9\x18G\xc5(N9\x93T5}8\xab\xde]\xd5\xd9U\x86&_j\x8a\x82ZWO\xea[\xd9IiV\xce\x99/\xba\x19z\xdd:^3b1\x88\x9c8\x1ew\xfb\xe4D\x1a\x85\xde\xad\xa7\xc5\xf7\xedM\xa5|\xab\xf8.\x15}\xf8cW\xad\xf4L\xf9\xae\xd4\xd9\xdaS\xea+\xe5\xcfx\xa8\x07\xcf\x8a\xe5x\xe2\xec*\xdd\x0b\xb5\x99\xc7u\xf4\xb7\xcd\xdbHHg\xf7\xf7\xdc\xbe\x8f\xa1y\x8b\x8d\xd5\xcc\xaeD\xe8K^fw\x85\xd5\xba\xd8`\x9e\x95\x0b\x11\xd6\x19\xd6Dp|A\xbfh\x8a\x16\xe1YI\xaf\xb8\xb5\xd3v\x10\xf6\x01\xa0\xafL\x8b>\x9b\xb4\x12\x8dGM1G\xafY\xfb\xc8\xda\xbc\xc1\x8a\xcdV\x10Y\xaef\x91\xd74\x8a\xf1Y\x90\x17p\x95\x89rrn\x8cjw\xd4\xfb\xf6\x04o\xf2C\x14\xf9\xfd\x8b\xb5U\xe2#S:X+\xda\x839\xab\xc0\xe7\xfe\x1f\xdcx\x80\xd1'u%\xc4\xfduI\xe7\x16|{=\x8e\xbe\x14/\xc08/\xc3\xe9gg$y\x191\xde\x0d\xc8\\\xdb\xe6t\xfbp((\x9fS\xae!\x0c\xcd\x0c\xcb\xd1\xe0\xf2`:\x11\xabC\xedtr2\xc2]\x82\x05\x99Y\x94\xe8\xcb\xba\xaeQ\xe1\xacH_ZQr\xf2\xf7\x87@\xa1\xdc\xd1:\xf7f\xc9\x8d\x0d\xba\x93.\xea\xa6,u\x95\x12q\xb3[\xd8\x81\x15gur\x19e\xc1\x1cmu.\xc95\x05\x12\xdeI\xcbk\xbc\x84\x95\xfe\xde\xad\xaf\xbb\xf3{\xc5Buv\x9a\xcf\n\x8d<\x85\x8dg\xa5i1\xean\xa7[\x14\xe8\x9d\xcd\xba\x93n1S\xab&y\xc9ugw|\xed\x85\x11\xd2\xe9\xdd:OZ\xf7\x1c\x96\xf0\x02\xee\xd8\x1f\xf4\x1f\xb7\xd2\x1c\xe7\xa2\xde\xcet9s\x072\xe0\xbb2u;\x9dPp\xe2b\x90'lW]\xd3\xe4:_\xf0\x1b\xe6/\\\x82o\xbb\x7f\x05\xb1/\xb1t\xe7\xb6`T\x0b\x86N\x19\x13\xbfw\x16\xc7\xdb\x91\xf0\xf0;\x9a\x863\xa9cc\xf4\xf4\x0f\xa1q\xe0\xf44W\x82\x15hZ\xd2<\xfc\xc9\xdcy\x99\x1e\x0c\x15\xd1H\xec\xf7\xc2=\xdfN(\xdaV\xe4\xf1\x1c\xdaW\xdet\xcb\x11]D\x84\x07u\xdc\x0c D\xb3W\x13T\xd0\xadH\\\x8b\xdb\xf2[\xc1\xd3\x8bi\xa2\x9d\xc6Z1N+\x03\xa6N\xa4\x1f=\x82%w\xf0,\xaf\xbd_^{\xc8Cq\x84Q\xb8qp\xf2\xea\xed[%\x9eL\x02$\xa6\xe0\x87)\x8d\xd71E\xc7\x87\x04\xc5\xad<\xe8\x9c\\\xda\xa4\x166\xa0\x85<;\x81\xed\xddf \xbb\x82\x15h\x80\xb0RA\xf1\xa4\xdeP\xa9d]\x1f\x1a\xc5\xa8\x0b\x15\xe8Yxp\x94\xd6\xc3z\x18\xff\xd5\xd1Fa,bAQqv\xa0\xcc\xc3\xce\xc8\xa1\xe4\x17\xf2\xb8v2d\x0c-\x03\xa0\x98\x02\x82@\xc4\x92\xb1Wrhn^\xd0\x87\xdd\x9d\xcd=\x11+U}i(k\xb2r\x8e\x15#\xb7J\xfb\xaeE\xde\xe9\x90\xde4\xdf\xaca\xe6 \\B\xc0DL\xf8[F\xcfds/~\x08\x96G\xd4Id\\\xf6T~\xbd\xbfg27>,\x02Y\xb2\xe7\xc5\xafr\x13\x9c\x13\xc1*\xe2\xeb\xfd=W\xeb\xb3\xa7\x18\xa0\x8a=\x93\x91\xaa\xf2'9\xbb\x86o\xca\x1f\xe5\xb6KB\x8cL\xc2\xcd\x07\x8a\x81\xc0\xfd\x80\xce\xdf\x8a:2\x97 \xe7\xdf\x0d\x95O\xf9\xd3|\xe8\xb8v\x052\x88rE\x171\xccG\x8b\xea\x08\xf5\xa7\xd4H\xa8e\xaa!\x10O\xf7,\xf7'\xf2\x17eB\xcb\x97S\xc3\x04\x86b-\x11\x93\x86\xdd\xaev\xe5\x97s\x93t\xf2\xdc$EZ\x12_3#%$V\x11\x82-\x86\x17\x10\xb1?<\x04[\xea\xf8\xd3xf\xa7-?i7\x9c\xdc\x99\x7f\xd5\xad\x1f\x1b\xb1p\xe8\x96\xd9P4\xfb\x95\xd5\x1a\x89%\x95\xb5$X\xa7C\x8dOA\x91\xc9!r\x8a\x8b\xc3\xfc\x86>\xa7\xa0~\xa8P\xd7>\\d),\xa2\x8c\x9drQL\x1f\x94\xc9\xa1He\xf0K\xbf\x9e\xfa\xe0\xa7\xbe1kA\xd3-D\x8b5E\x94\x89\x07\xf46\xa5\xe1\xdc\xa9\x83\x8fo\xea1\x90\xf2|Xg\x95\xe5\x90\xc8\xf7\x85\x8d\xfdI\xf9\xa9M\xe3`\xa5\xccb6?}\xe9l\xea\xf1\x81\xbf>c\x81.\x98h\xe4\x94B/V\xa7\x81tL\x1c$\xf2l\xb9\xc8\x16\x0bN\xba\xeb$3,\x93\xccX\xfc\xf4\xa2 [\x85\xa5@\xa7\x05\xde))\xd8\x07K\x9a\x9e\x84\xfezM\xd3&\x00\xd7\xcc\xd5\xeb{\xb1\xa3\x0c\xd7U\x95\x06:\xd9\x1bD\x00\xf8m\x85c\xd8\xdb\x11\x11p\xc4\xadKi\xb6\xc2:\x80\x1d\xe7\x1b|?w\xcf\x86g\xf1Y\xf8\x7f\xfe\xb7\x9aU\xa0;\xf0\xc39\xbd=^8\xcah\x90\x8a\x1f\xa4N\xc4\xef/\x0c!\xab\"\xd8@2^\x06\xf2\x06\xf6\x9b\xc2\x13\xd8\xe4\x9c\x87^X\xc3q\xc3`0\x00\x1c|o\x1fv\xf4RJ\x1bw3\x04\x91/ A\xea\x90 \xf0B\xc5\x0d\x85\xbd\xfab\xd0\x10#X\x1c\"\xc8\xf8F\x052-\xa0\xe2\xabP!\x0c\xbe_\x01\x15\x81Q\x99\x84\x87\x98\x00\xe7\xea\"\xee\x8aX\x98R\x02\xaa\xa1\x84\xe4\x95\xa1\x01x\x8f\x07\xcc\xefUkAO\xb3\xe6=\xe5\xbc\xe8A\xf7\xf7\xaeJ\xa0\xd4=\x94F\x9c\xfb\xb5\xe6\xe6UB\xf6u\xbb\xda3\xbe\xd8\xfa\x8caE\x0e\xe2\xb1\x1fr\xe1\xb1x\x86\xd1\x92\x1f\xe3U9\xe3XH\xca%\x186)\xa7\xa0\x04(\xd7\xf5\xd8\xdc\x04%(\x9e\x8b\x02~\x05\x82;\x10\x85r|VP\x03G\xa8\xa8x/c\x0e5\xd4]j\xc9tNi\xbe\x92h\x8ev\x953Em\x9d\x9d\xc6\xb1\xa3 \x87\x93\xa4q\xb7_\x81\xf5\x95\x1f\xce\xc7\xc5}n\xe9Y\xae\x90\x1d7\x98w\xd4t\x9e\x98D\xa2\x94\x8b\x00\xca\x07\xbb\xfb/\x82\x00\xfd\x9b\x11\x02\xb9c\xde\xb7\x85A\x95\xb9\xfe\x97\xc3`E\xd6&\x18\xe4\x8e\xb6\xdf\x16\x04\x15\xd7\xd0\x7f=\x08\xd8\x08\x1f\xb4\x13\xc4\xedA\x13\x00|\x19\xbe\x07Ek\xabm\xf0u\x9e\x8cR\xc8\x01&h\xca\x98\x9d\x8f\x1eA\xf7\x7f\xc4\xcd\x1d\xf2\x02E\xb9\xd3\xc5 \x15\xcf\xbaG\xd5\xdf\x9f\xde\xbd\x13\xbf+\xbcv\xf3R7\xac\xb4\xad\xb9uL1\x10Y#\xe0T\xcc\xc1Q\xdaZ\x8d\xe9:\xa6 \x0d\xd3\xb1\xa6%\x8f\x84Q\xe8{$h\x98\x01\x14\xbdv\xffG\x93J\xb3~5\x12D74\xf6HB\x1f\xd02\xaeK\x9b\xc6\xb3\xf5\xfa\xc1\x8d\xe3\xa2\xb6i\xdc#+\x1a<\xb4q\xfd\xc8m\xeb2\xa7\x0b\x92\x05\xe9Iz\x17\xd01tsxu\xff\xe5\xfb\xfd\"\x8a\xfe\xa9\xfb]c?\xd5z\xbf\x97\xf6u\x1agT\xdd\xc7\xa7\xd5\xdf\x1f?\x1d\xca}\xcd\nv\xd4\x97\x17$HJ\xb5\xdf\xd4\n\x0e\xde\x9d\x1c~)]\xb0m\xe4\x87\x0c\xfc[\x12\x90\xeeT\xa4\x13\xf81\x8a\x02J\xc2\x19\xef\xa3\x96\x9cN\xb2\xa12\x03\xed\x17\x93\x1b\x1dQ0&\xc8\x95\xf6\xa00\x91\x00\x1a\x83X\xa56\xdbXG#Z\xf5\xc5\x81=\x96\xeb\xdd\xa6/\x1d\xc9h\xd7\x97\x9c\xd7\x1b\xc3\xbc\xfe\x1d(\x88)C\xe2\xee\x03\x93\x9c\xd6\xb2\xa7\xed\x14\x03\xd54D\xda7\xb4\xa74$\xbfUI]\xa4#u~\x98\xfe;P:\xae\xb4Q5\xd8Z\xcc\x89\xccn\xf5\xba\xa8\xde \x95'q\xa3ylw\x83\x1bB\xf1[\xd4i4C\x19\xad\xdb\x13y\xdesY\x8eN{\xbdh\xe6\xf6\xa1;\x14\x99\xfe\x8d\xe29j=z\x82!\x8b\x1b=\xbfp\x14\x17\xbcQ\xb5+S\xfb\x90\xbby\xf4z\xa4\x9fb\xe6\xb7\x959\x8ev\xddA\x1a}b\x02\xe9+\x92PG@\xa2\xb1\x9a\x0526\x1c\xab\xc8\x85b*\x15I&aO\x0f\x02\x9f$4\xb1\xe1\xe2t\xb3\x0f\xdd\x0b?\xecjR \xe4\x98>\xedC7\xf2R]\x95\x1c\x8e\xd3\xd1\x10\x13Uy\xbaZ%\x88OG\xbb}\xe8^\xd2\xdb\xee\xf7\xbd\x0b0\x8b\xb5\xe5b_\x08\x90\x1f\xe9\xf2\xf0v\xedt\x7fw&\xe3\xe9Fo6q&\xe3\xe1\xfdt\xb4\xf1l\xc6\x8e\xd8\xf3\xd9\x0f\xae3\x19\x9f\x9d\x0d\xe4/VaJ\x0fgXY\xa4\xc4\x9d\xdc\xe7\x15z\xda\xc7\xc5/\xd1\x8c3\x19\x97\x0f\xf2\xa2\x07^\xf9\xecl\xe0L\xc6~\xb8\xb8\x7f\xcb\xfe\x1d\xbdq\xefyQH\xc2\xfb#rt\x7ftp\xe4\xba\x7fV-\xef1.?&\xedU:\xa7O\xcczB\xad\xf0\xbc\x08\"\xf2]\xc4gU\xbf\xcdoF\x18\xa5u:\xbe\xe0`\\\x95\xf9\xa1S\xd5zo\xf6\xcdy\x1am@\x189B\xd8\x07\xc9G\x08\x03\xe4\x1a;2H\xa3w\xd1\x8d\xdc\xd2\x8c\x97\x80 ;\xc8\xc7 b\x00Og}\xe8\xf66\x94+tdX^\x8a\x13\x86\xdf\xa1\x16\xccH\x1fX\xcdE\xc1{\x08\x0b$\x98\x88\xc3l\xf0\xe1\xf8\xe4\xed\xe9\xdb_\x0f\xcf\xdf\x1e\xbdy{\xf4\xf6\xf4\xaf0\x96\x8f\x8e\x0e\x7f:\xa8>\xea\x0eB\x12\x16\xcd\x1d\x91#\x18CZf1\x04is\xd2/\xe33\xa22\x9f\xf1\x86!\x8e\x95\xd3\x10\xb6w1\xe74\xa2\x07t\x95JN#f\xaf\x9b9\x8d\x10~`|\xf3\x18\xbf(\xa3J\xff\x9dx\x0d\x873\x1b\x9d}\xee\x8d\xa1\xe15\xda2\x1b%Bi\xc2\xf8P\xaf\x1c\xf2\x93#r\xc4\xfa\x82\xe4\xc6O\xbdKp\x8c\xca\x03\x8f$T\xd5D\x8e\xb5\xb5@\x01\x0e\"\x9f^<\xe2\x8d\xe5z\xdc6\x8d\x1d\x1d\x1cY\x1b\xcb\x15\xb5\xad\x1a#G\x1a\x8dl\xe1\xf8l\xdcnB\xeb\xf7=\xa0\xc5v\xfe7\x83\xd6\xdb\xa37\xdf\x0eZo\xc3E\x1bh\xd5)\xd0\xf7\x83\xd6\xc67\x05\xd7\xc67\x85\xd7F#\xc0t\xbb\xbdx}8\x18j\xc6\xa2\x9cKe\xbe\xb7\x0f$\xcf\xe95\x810?\xa6\xba\xb4\xcb\x0e\x14\x1e\x083\xb4\x11\x93\x7f\xd6mC\x8d\xff\x8aj\xfcW\xce\x1e)\xff\xb9\x1b\x8e\xe9\xc7\x9f\xbb\x8d\x1c]c\x8b\x93\xca/\xc6\xbb\x9d\xa6\xb3\xfb)\x9c\x9d\xa5\xb3\x9e[z8V{/\xfd\xe0\x0c\"/\xf9\xc1\xe5\x1c\"\xb6\xf0\x83\xf3\xdf\xf7\x0ec\xc6\xdcj7\xa5\xf7\xdd\x89\xebNJ\xac\\\xab\x1b\xdd\xd4_\xd1$%+\xa3)\xcb7\xe7\xd6\x8a\xb0\xe5\xd1\x80\xdeRO0my\xa9/K\xbf\x03\xbf\xa6\x89\x87b\xb85Y\x0b\xf7L\xfd\xb9\x97\xdf\xe0 \x0b\x96\xcf\xc3\xcd\xb9\xb2b\x12j\x9erW1\xf3>\x8c\xe3(v\xba\xafIJs\x9fZ\xca\xcat\xc1\x99|\x91W\xb4\x97NG3\xce\xfc\xf4\xd2\xe9\xe6\x8c{-\x11\xfesk\xd6\x87N:\xdd\x9e\x15f\xb0\xf4\x06X\x07\x0e\xfbo\xf0\xe9\xf4\x95#\xc0\xa0\xf3\xc3\xf3E\x98\x8a\x1ek\x82G\xa9\xe8\xa5\xd3\x9d\x19\x8fO\xd1K\xa7\xbb\xb3>\xa4\xd3\xbd\x99\x89\n\xa3\xca\x15\x03\xdfN\xf7f\x82+\x1d\xf6a\xcb}\x0e\x8b\xc2\xa7r\xeb\xb9\x0b\x0b4\xf0\xd3Q)l\x87u\xb7\xa8\xd3?\x13z\xa5\xd3g3\x04<[\xb3]\xba\x0d?\x80\xb3;\x84\x1f\x10Z\xc3\x19\xf4\xa0\xe7\xa4\xd3\xd1h\xc6\xd0l(\x95\x80\xb8 \xea\x9b\x1bkW\xc4g0\x82M\xc1\x9e\x85\x8bQ\xd5\x1f=\x02o\x90\xd0\xf4\xd4_Q\xc7\x1b,\xc57\x1760\x88\xa6gCa?LR\x12z\xf4x1\xc6\xeeZph\x96M\xc6\x88\xfa\xdb\x93cA\xd7\x8d\x8e\x00\xdf\x8a\x10?\x90\xcc\xf0\x04\xfc\xdf\x8f\xc4t_\xbcP\xac\"L\xe6O\xdf\x0e\x0c\xc5\xcf4\xbe\xab\x0c\x8b\xc3hg\xdb\x1d\xfc\x88\xb6\xc2E\xaf\xe0\x11dd\xd8L>\x97\x1a\xb4(\x18\xba\x07?\xbez}\xf8\xe6\xa7\x9f\xdf\xfe\xe5\x97w\xef\x8f\x8e?\xfc\xd7\xc7\x93\xd3O\xbf\xfe\xf6\xbf\xfe\xfa\xdf\xe4\xc2\x9b\xd3\xc5\xf2\xd2\xff\xe3*X\x85\xd1\xfaoq\x92f\xd77\xb7w\x7f\x1f\x8e6\xb7\xb6wv\xf7\x9e>\xeb=\xd9?\x0b\xcf\xe2\xee\x03%x\xae\xe4\xf9\x1e+\xf6\xc57\xe0\x06J\x1d5^\x8e3\xfa\xe8\x1b\xae\x88B\x1e\x030\xe4\xbeC\xa1\xed\x9e\xa8\xe3 i'\xb9\xfcK\xa5\x19;\x8f\x06\x08\xbb\xdb\x8d7G)\xbc\x80a\xab\xdb\x1f\xd4\x8b\xefj\x1f\x1b)a\x0c\xff\x01OQ\x01]\xc6\xfb\xaf>:\xa3\xb2\x02cz\x16\x9f\x85\xfb3\xa1\xc60\x03=\xb2.K\x86\x91\x80\xb4\x8f\x12\xf3r\x07\x86;\xa1\xdc\xd3{\xf8\x1c\x18\x94\xc9sH{=\x17R\xf8\x0f4\x05\xe3*\x13~\xa5\x13\x88L\x11\xf0\xf2%\x8cv\xe1\x11l\xee\xec\xb8}P\x8b\x9fVK7wv\xe0\x11$\x8c\xec'\x98\x0e\xe4\xc5\x0b\xd8\x85{\xc8rt\x88$:\xa4\xba\xe3U,\xd1\x10dH\\\x82\x03\xfb\x01v\xf1\x9a\xe6\xab\x86\x04c\x18=\xcdu=\xe5\xb6\x86\xda\xb66E)\xbe*|\x0f\x19h\xd4:\xdb\xf9\x9b1\xa6\xdfX\xc4\xd1*\xff\xe2\x04(\x16 \xbd\xc7\xaf\xdf\xd4~\x15C|0)\x87S\xd0\xf67'm\x11:\xe6n.F\x82b@>\xd2Hk2\x0b\xad1`\xe7V\x05;q\xe7g\xd3\x08\x97\x8f-\xfa\xee\x16\xf2|J\xe9\xa6\xaet\xb7R\xb8\xbb\x05\x8f\x00Mr\xd8\x8c\x9c\x88a\xecS\x17z@\xa7\xa9\xf9R\xb5\x8c\xa0[\xfc\x0e\xf1\x1b\x8f\x08\xc6\xb0Y\xa0k\xa9\x9d\xa1\xae\x9d\xedZ\xe1\x8b\x17P\xedqw\x1b\x1b\x1e\x15\xc8\\j\xb9>\xc0\x17/j\x0d\xefn\x97\xdb\xebC\\F\xbc\xfc\xd7Ws\x10f\x89\xb6\xa6\xff+\x87\x9c\xacs\x08F\x85\xe1\x03\x99\xb4\xc8\xe2\xd1`\xf0\xea\xf8\xca3\xdfd\xcf_\x91\xd7\xb8*\xdcx\x1cP\xdb~\xe3\x97\xd2A\xee%\xccv_\xf8\x9c+\x83\xcd\x1ed\"uh0MgE>\xb0\\]\xcb\x01>\xeb\ny\x15\xd5\xb2q\xb3Q\x87\x88\x89\xe3\x87\x10\xdb\xadx\"\xd1$Jj\x16\x8eB\xd6\xcf\x1a\xbb\x96\x9f/\xb2\xd6A\xe6\xa7\xb9\x0fVM\x98!$\xf9\xa1H\x9a\xc1\"\"[\xb4\xca\xdf\x91#Ny[~!\x83S\xd7O\xfc\xb3\\\x8dZ\xec\xfa/\xdc\xc4k\xe2\xc7\xc9\xbf\xd7.\x16\xbe\xbb\x96\x9dJ\xc4\x8c\x0e\xe2\x98\xdc9\x99t\x81\xcco{\xd8\x16\xce\xbel\x0bg\xb8\x85\xf5[7j\xbdu}\xf4\xe7G\xc3!\x85\xe2^\xd1\xbb\x84\xbd]u\xf17\xb5B\xa6\xe9\x8c\xd12\x7f:d\xe7\x0c\xfe\x9d\xcd\xfe\xe9hoXG\x1dW}]\x0d{&R\xd1\x18\xd6\xd1/\xad#\xd1\xae#1\xad#[-\x82\xab\x15\xd5@\xdc\x07_\xc0.\x12\xb0\x8b\x10vF6\xc6\xff7\xd8\xc1\xe5s\xfb\x81\xfb8\xa1\xc6\x0bt\xbdw\xe1\xf7\xdb\xc4\xd6#\xd6\x0f\xc1\x10\x08L9\xc9\xc2\xbe\xb0D\xccIm8Mg\xd6\xfd\xf2mQ\xdeD\xe9\xff\xed<*\xffH\x9ed\xe1\x9c.\xfc\x90\xce\xbfR\xfbb\x81\xc3\xc3\xa1\xea\xd6\xf2\xcd?T\xa6\xbb\x8e\xfc\xb9\x8c/f\xeb]'\xcd\xd94\x7f\xffn\xae\xd1\x7f$Ob\xba\xa4\xb7\xdf\xe5F\xe5\x01\xca3\x1f\x03\xd5`\xbd6\xe7S\xeeW\xa7\xe7\xb3\x19\x11xr\xf6\xc4\x99.\xfd\xd5\xec\x07\xf7\xcfO\xe4\x05\x87\xbez\xac 9\x00\xd2z\xfa\x89\xd4\xbe\x0f\x8dw \xfc\xc2C\x9a\xf2\x86\xd3\x11\xcab\xf2\x16\xe1%\x93K[\x9c\xd8\xac'4\xeb\x9d\xa6\x85!P\\\xb2 *\x9a\xa9\xb5\xf2\xbd\x8f\xe1\x7f\x0e\xc4\xe56Q\x80\xceo\xe1\xaa\xd0-\x19\x13\xf5\xc1\x001\xbc\xd0*.H\xd3~U\x96\xf9J*\x913j\xbc\x83\xb6&1\x0f%(\xd6\x05a\xb0\xea\x01\x1d$Q\x16{\x14z\xac\xc0\x08X:X\x06\xd1\x05 \xc4\xd5_o\x1f\xbaK\x1e\xb9\xaf\xc8D_\x11\xf5\x9fV\xca3\x9b\xd2\xaf\\5i\xd6.\x94_\x08`\x1f\x9eU\xc8 \xec\xc3\xa8r\xad\xb5\x80}\xd8\xda\xac`\x03+\xdb*\x97\xcdY\xd9v\xb9\xec\x92\x95\xed\x94\xcb\xaeY\xd9^\xb9l\xc5\xca\x9e\x96\xcb\x96\xac\xac2\xbe;\xd8\x87\xed\xcaX.XY\xa5\xdfsVV\xe9\xf7\x06\xf6a\xa7\xd2\xc7!\xec\xc3n\xa5\xbd[VV\x99\xdb +\xab\xf4\xf1\x8a\x81\xaf\xe2\x93x\xc5\xca*\xef\x1e\xb0\xb2\xddr\xd91\xe6/\xacT\xfc\x80\x85\x95^N\xb1\xb02\x95\xf7\xb0\xafA\xfa\xe1\x18\xbaggC\xcdQ\xb4\x87O\x88\xe6\xc9S|r\xa1y\xf2\x0c\x9f\xa4\x9a'#\xdeQ\xa8{4\xc2G\xd7\xbaG\x9b\xf8h\xa1{\xb4\x85\x8f\xaa\x0c\x1d\xfbl\xf2\xa1Wu\xd1\xec\xb3\xb5=\x86\xc7gg\xdd\xc7\x9a\xb1\xf3\xbe\xce\xce\xb4\x9d\xf1\xde\x8et\xcfv\xf9\xd4\xceu\x90\xda\xdc\xe2\xad\xbe\xd3?\xe4\xad~\xa8(\x1a\xcaU\xdf\xb2\xf3\xba{\xd7\xedC\xf7\xaf\xec\xbf;\x9a\xe0w\xf1\xe7\xf0\x84\xfdA\xb6\xb7{\xcc\xff?b\xff\xe3W\xfe-\xc2\xaf\xfc\xffc\xac\xbdX`E\xf1\xe7\xcd\x9b\xeeL\x17U\xe3\x8f:\x9d,\xb4\xb6\x95\xabhn\x82\xb2ou-\xeb\xf3\xc8\x19\x9b;;.\xe7\x85n\xbb<\x80\xeff\xb9\xad\xdc\x1a\x19\xab\xef\xee\xecl\xc9\x172\xf1\xc2\xb6\xe6\x05=\xd7\xde\xe1\x8dlo>\xdb~\xb6\xbb\xb7\xf9l\xc7u\xcb\x11q\xbdhNa\x1d\xf9\xa5\x8c\xb9<\x00\xe2\x8a\xdc\xc9L\x0c\xcb\x98\x92\x94\xc6<\x19\xc3\xf0\xf6\x8d\xf8\xe8X\x07\x1c\xe8'1\xd0\xa7\xe5\x95-\xfd\x92\x87\xde\xd9YW\x84u,\xe28\x0e\xf1\xfd\x8d\\Vv\xa1\xa7\x08p\xba\xc8%G\xf5\xc5R\xa2X\xf3x\xe1y\x98n_\x06\xc9\x961\xa7\xdf\x93\xf4r\xb0\"\xb7\x0e\xa6\x0c\x17\xc5\xf7\xf7\xb0\xe9\xcah\xdfW\xfe\xfamxM\x02\x7f\xce\xdbR~\xab\xa1\xb9\x17At\xf3\x8e^\xd3\x00\x99X?9\x8a\x18L\x97\x0e-\x9e\xb8\xd2\x17I)\x93\xbd\xa4w\x81\x08\xc1]:YMLu=%p\x93Ym\xe1\xdb\xff\x8f\xcf\x06\xcds(\x12\xa2pk\x0d\x9e\x845\xae\xdc\x1b\xa4\xf9\xd5\x0c\x8f\x04\xe0?\xe7ARG\x90\x89\x86X?\xac=\x91\xe4!\x18\xa8>\x97}\xc8xg\x19^\\\xab\x8f\xa6\x19\x1b_8%3\xd8\xaf\x06\xc3\x05E\xcd]\xc6gGA1\x868\xd8b\"\x0d%s\xdc\x89\xe2\xf4\x17z\xc7\xb3\xcf\xe4?\xca\x01\xddC\xfa\x9b?\x97\x01\xd5\xf3_\xf7\xf7\xf0T\x86C\x0f\xa3\x8ft\xc1\xdb\x10_\xd5\x16\xc2\xe8U\xb4Z\x93\xf4=\xdb\xce\xbc\x8eR\xa0\xd6\xf4\"\x86\xdd\xe8zu#@\xa9\x14\xa85\xbf \x84\xbcLOd{\xe5\xf0\xb6\x1cu\x1e\xd3`\x85E\xe4\xfaR\xb6F,\x99g\xec\x0d\x92Ra\xaf\xc0K\xb3\x84\xce_\xabOJ\xb1\xfet4\xe2\xa3v3!\xd2\x8b\xdd\x14\xc1~%\x9al\xea\x8at\xc6\xfc~nc\xc4\xf1\x9a\x8d-Q\x83\xa5\x81\x0f/ y\xeeb\xda\x064`\x97\xd9\xfa\x85K\x1f;\xfb\xc1w\xd1\xec\x87\xfb\x8a\x88\xac\x16\xa2\x83\x04\xb3\xbd\x95\x9e\xb0.ydW\x1f\xad\x86\xf8\xf7P\xd5C\x9c Q0\x14x\xdd\xdb\x87\xc8eC\xec\xedW]\xcb\x04\ngV\x10\xbd\xb6\x85\xe3\xd6\x87\xdb\x95\xe4\xf2\x07H]k\xdb\xef\xea$Z\xca\x1c\x08\xb1\x05\xc3>\xfe\xd5\xbe\x8e\x9f\x8c\x0dmm\x96\xa3T\x8d6wQ~\xdf\x1dU\xc3`m>\xdba\xbf\x18\x87RxP0\x96D\xfc\xba\xbf\x87\x9d\xbd\xad\xed\xed\xf2{\xec0\xdeb\xbfx~\x8a\xbc*+\xdf\xadt=\x1am\x8fF#\xebD\xfef\x9c\x08N\xb1\xd2\x0f\xb6\xcc\xbe^\x14__\x15_\xaf\x8a\xaf\xc7\xc5\xd7\xd3\xe2\xebM\xf1\xf5\xd2:\xac7\xc6a=\xf9\xfd,\xfc\x01dT\x13u\xb9\xe57\xb6\x91\xfe^\x0f<\xf2#cs\xcaE\xbf2Y\xa5\\\xf43\xe3m\xcaE\xbf\x01\x06\x99\xae\x0f\xf2/\xf6\xd0\xebl\x1c\xbej\xe7\xd4\xd1\x84B \x0c\xe5\x0b\xdc\xe9<\xeeG\xfd\xe9{N\x07j\xe5\x8cS\xfd$\x12\x92\x96r\x96TV\x12\x83\xf3t\xde9\xfc0\xca\xb0\xec\xbc\xf8z[|\xbd)\xbe^\x14__\x15_\xaf\x8a\xaf\xc7\xc5\xd7\xd3\xe2\xebe\xf1uU|\xbd+\xbe\xae\x8b\xaf\x1f\x8a\xaf\x87\xc5\xd7e\xf1u^|\xbd.\xbe\x9e\x14_\x0f\xc4\xcc\xcc\x89^49\x1f\xd2\xbaJ(7y\x18r\xba\xaaP\xd9^\xcfv\xb3\xd5\xf9$\xc8\xae\xd2\xbf\xafD\x05\xfaM\xaf\x04f+\xf7\x96\x8d\xfdoZc)\x13\x83\xfd\xc5\xc3\xd4\x0e\x12 \x9f\xe7rd\x1d\xf6a\x01hQ\xcdX\x15\xe4Ya\x03\xde\xe3\xe9\xf2\x92[\xf1vA$\xd2\x9c\xbeg'\xc3\xac\x8f\x88\xe9\x1b\xf4\xdc\xb9P\xc1@\xf4\xb5\x00\xd1n$\x1c%\x0e\xbaq\xa8\x7f2\xb7&\xc6\x85\xdcM\x00\x13\x08\xe1%<\x83\"\xed\xd2o0\xc6\xf2\x9fa\x0c\xbf\xc2\x98\x8f\xb2\x13\xf1\x87\x7f\x871\xfch%m\x7fU\xa8Fu\x85\xe8`\x9e\xadJ\xbc\xb7\xe9.\x84\xdf\xfe\xa6\xd5\xdb\xdf\xee\xe3\xc7\x86\x9b\xd9N\x85!\xe3\xa1\xfd\x19H\xde\x16!\x08\x14W\xd3\xc7\x18\xa0\x1dz\xec\x9b\xfeF\xd9\xcf\xb9\x0b;\xe9\x94\xfc\x17'\xed\xf3$\xc6\xbeH\xdeL\x14\x85\xa3\xd1eY\x80\xb0Q~\x92\x1f)G\xe97\x02\x94\xdcYd\xc0H}\xa6\xd9\x90\x87D\xe3\xd9\x82\xccv\xa8 p\xa2\x9ah6\x9c\xe5\x19H\x15T0\xc5n\x04\xeb\xbd\x0d@\x9e$\xa9\xbe{\x8d\x96\xaf\xe8Q\xfd\xf7F?jM\x06{\x90o\xff\xd8\xf8\xb6\xc0\xed\xc2\xe7\xe51z\xbb<~\xdcuM\xf8\x0e\xb2\xf5_\x9b[\xbfg\xad\xff\xc2\xf3\x04r\xbca\xcd\xfe\xe4|dE\xbe)M\"\xb6\xfess\xeb/\x8d\xad\xb7\xc67(\xcb\xee\xb0\x0fO\x9c\xb3\xb0\xe7:\xd3\xdf\xcf\xc2\xd9\x0f\xee\x93\xa5~W\xa9\x1f\x94\xc9\xb3\x9a|\xe1r\xd9DP\x96\x0c&\x90\xa1\x9aA\xb8U@4\x08H\x92\xbeeo\xf0\xfc\xe0\x7f\xce#\xd3\x0d\xfb\x98\x7f;u\x0d{Z\xfd\xa0\xa8~\x16\xcaP0Ct\xffd$^\xfe6c,\x88\xc9k$l\xf5#b\x0c\xc6\xaa\x0b\xb01\xc1\xa7\xfaam'\xc0\xc3\xbc5O\x04\xc4\xc9\x15O7\x1b\xc6\x0cyJ\x18>\xcb\x00o\x80|\xb6\xd3\x13\xe81Y\x0f\x13\xdc38\x88\n0a_\xc7<\x9f\x1d\xf4\xe0\xcfN\xc0\x85I\xbc\xb5\xb0vf\x8ey \x05*\xfa\xc6J\x9f\x19z\x12\xb7 \xdb\x7fk\xc4\xf6\xc7\x98\xac\xa4\xf9~O~rA\xba\xe0\xca\x85\xa4l\xe4\x91\x84\xce\xb4\xc2\x08\xbd\xe4\x02\xda.\xa0\xe7\x0e\x13\xd7v\xb7F\xc8\x04\xd4\x83\x95\xfa(\x15\xf3wv\xb76\x87PD.\xdd\xda\xdeb\xc26*\xa6\xfepF\xc3Mt`Na\x83\xb7\xce\x93\xc9l\x88\xd7z\\\x86c`c\xbc\xdb\x98\xeb\xbc\xde\x0b\xab\xd9\xde>t\x90\x93\xf9\xe4`Zh:\xf5g0\xe6\xa7\xdc\x1fz\xb74\xf5#\xafSmk\xe6\xf2\x8c\xa2\xfa\x86D \x08\xf3\x92\x95t\xba\xfej\x1d%\x89\x7f\x11\x08\xc7\xf71\xf8BU\xc9\x8d@x \xb2n\x13c\xf7\xd9\xb1\xcb\xf3\xbf\x983K\xc1\xbe\xe4\xd7\xa4\x02\x10\xe3\xafin\x01\xe221)\xc5\x95\xd2\xea/B\xb6\xdfx\x8em\xfd{\x9b\x9c\x1e\xe5\xcf\xd8(\xba\xbd..\x97\xdc\x94\x1b\xfc\xb09\x0b\xbb\xd6\x19\xfed\x14\x84MCf\xb8Q\x90\xd4\x8d\x11\xa6\xf7\xb4\xf6\xf1g-\x14\xd1\x1aAq\xbcV\xc9k\xce\x1bTl\x87UE\x96\xe2CY+:\xae2\x90\x85*\x9d\xc0\x0b\x08\xd8\x1f=\x07\x89\xa2\xa3\xe31)oJf\xee\xa0\x88s\xc0P\xc4\x1b\xe4\xf6\x06\\\xcb\xdd\xf1*5\xba\xdc\xbc\x80aR\x9e9\x90\xd3XY/Z\x80\xfaR\xdeN\xder\xa5#F\xfal\x82.\x95\xea]\x98\x80\x87\xdf\xc7\xd0\x9dt\xfb\xe0\x0dr\xbb\x04\xdb\xb1\xc2\xdaXp\x95\xa8\xb8\x1a\x99b33>\x0e5>N\xdfh>\x91\xf1\xbb\x00\xb5K\xee\x13\xa1\x94\xb03sa\xa1\xe2\x06\x0d\x80\xfaA9/\xa9\xf5\x85\x11-\xca\xf4\x99'\xe8\xf7D\x82\xfe\xc7/1k\xbf\xe0\xfdc \x9eG\xd7i\x82Wo\xfc\x04\xe6i\xc2\x10\x02\x8f\x9bN\x9a\xf2\xb4\xa6\x8b\x19\x9f\x99\xf9\xe41OY\x8a\xc3\xb1\xb6\x8a5\xfe\xb4\xc6&K+\xe6w\xec\xfa\xd1\xffU\xd2\xf1\xf1M_\x95\xd9\xd5\xfb\x83|\xc8a\x9fo\xe5\xb0\x0f\x9d\x11F\xc1\xc9\x7f\x0e5\xd9\x82\x13\xc8\xb1\x847Q\xcd\xdb\x9a\x13?U\xa4}\xc1#\xc4\x95\xa5\xdcjVS\xd6|\xd0\x87E\x1f\xed?\xea\xdeR\x0cAQ\xd9\x91?B\x17\x1f\xf9\xa4\xae.C\x85\x9d\xa3h(\xc5\x8dXqI\x92\xcb\x04\xa1\x8b7f\x85o\x06\x02\xeb\xd1#\xb6\x05\x95\x02T\xdb\xdc\xdf\x83P\x84K\xa5\x02\x12\x86\x97 R.\xfb\xa8*u\x85Z\x8aVn_\xa6\xc1\xcc-\xa0\xdf\xfd!\xa6\x8bs\x86\xe3\x15\xf1\xderQ\x8d\xd3\xc2\xb6;\x9a\xc6q\x08\xba\xf2}\x9eR\xdc\x00W\x97\xaf\x1c\xcf*\xab\xde_\x8aU\x96\xc7\xcd\x04\x9cN\xcd\x96I\xa3!\x92\x9f\xb2r\xb9\xaf.\xb0\xc5\xa2\x95\xdf\x1c\xa7\xc4\"\xe0]V\xeeYM\xb9\xf1\x91\xd6H\x1f\x04y\xa5\xe8\xc2%~w\x9aT\x80J\x0e\xd9\xe2$\xd0\xb4\xa3\x145\xb4\xa8\xbe\\\"u\xf9u\xe7*K\xd0\x92\x80\xc0\x05O|\xc3\x13\x98\xdb\x8c\x10\xa1\xa4b\xe5,\xc4e\xe9\xbe\x8d<\xe72\xd8\xc8E\x95=\x135\xc4\x823\xc8\xf8\x0c\xa9\x1d\x0c\x89$\xae\xb5D\x88\x89p\xca\x18\x9c\xcb\xa9?\x9b\xf5\x05\x8d\xe1\x96\x80\x19O\xcb\xce\xffq\xbc\xc7\xdd\xd5b\x07 \xe4\xc7\xbd\xc1\xbe\x15\x1e\x15L\xf0\x90\x89\xe0e\x1dO,\x1d\xd6,\xe77\x9f\x88 N\x13\xc6\xa8\x8a\xaf\xd0\xc5\x8d\xd7\x93\xaf0\x0e\x83S\x81\xd2\xdc\xd4\xa9$|\x1a\xc1\x17\xf4<.z\x1eC\x97\xe1uo_\xed\xdd$\xedHZk\xa2\xee\x89}&g\xe4K\xda\xe2\x14t\xe4QNG\x90\xc9\xe3\x9d3\xd9\xac\xbe[m[\xb5b#\x914\xec\xd3\xa0y\x9fz-\xf7i5\xa7\xb6\x97\xa3o%\xa7vV\xbf\x8a\x9f\xa0\x00\x8eR\x93\xa0`\xfc\x18\xc2\xbb\xddn\x1fq\x02\x95 S\xb6?\xbci\\`3N\xb63\xe2\x87_\x01\xd22N*\x8dq\x04\xcb\x8a%f2\x96q8\xc8x\xa3eF\xbd\x0e\x17\xaf\xb099\x14R\x1e\n\xb2\xe6Y{lR\x8f\xf5\xee?X\xaf \xeb\xbf\x11\xa3\x9a\xd0\xa9\x0b]\x05\xa9\xeac(\xa8\xa5\xf6`.\x1d-e\xf0~\xc9iRx\x00\xdb03\x93\x98i\xc16\xc5l'4\xd9\xe8\xa8\x84\"D[\x1d\x95\xe4)$4B\x12J\xcad\xa6%1\xc1\xb7\xba\x1b\x0c!\xc4W\x9e5\xb8Xy\xfb\xc2g\xca\xc2\x13\xce!\xcd\x9a\x16\xfd\x9fAF\x1a\xd6\x88\xb4X#\x85\"\x84&\x8a\x90\xf3\xbe\xd3xV\xdeA*1\xf091h\xd8\x8c\xae\xd0U\xb6\x82;Q7\xdc\xb4+S-7\xc2\xbe \xf0\xad6\x9cY\x94\xcc\xb7!\xd7(\x89@\x03I\x93\xf4X2\xd5k\xf4m\x84\xaa*-\x0b\xb98F.\x02\x8a\x9eT\x10-\x801/|,i\x048W$Kz!K/'\x95\xf9\x87G\x8f\xf8\xc5\xa4DbT\xe0\xd6\xc1]+i\xe2K\xca\xab\xc1\xc5N*\xc4\xce\xeeKu=\xfed\xee\xa8.\xd2\xe9D\xb5\xff2+\x03sm\x94.\xd4\x8c\xce\x1d\x87\xc7\xbb\x94-\xa3\xfb\x97\x89~*\xb4\xb3\xbe\xa2\xb9\xe5c'O \xa6\xd1\x80\x98}\xec7\x94\xc0\x14\xa1zO[Xy\x15ia|\xdc\x9c1\xf7ui\xbc\x85\x0fy\xbd\xd4\xed\xf3ce\xe0'<\xb4C\xaa\x89\xce.?Uf851\xc3\xd4I\xa7\xfeL@\xcd<\x12{G\xd5X\x11\x15K\xb8\xc8\xd6y\xc4y\xeb\xb0\xee\xc4\xca\xd0$\xe2dZ\xb9R\xf5\x0d\x97\xa8\x90\xaar-\x82,\x9a\xfa\xd3p6\xabL+\xd5\x98\x03\xe6\xe12b\xbb\xd2\x8fR\xab\"\x9b\xb5s\xc43\x02\xb0S\xe8\x1fUOB\xa9\x97V\xcc2q3\x84\xc8\x03\x85}6GZ\x9c\xb0\x13\x08%\x8b\x85\xda\xcbR\x0e\xf2b\xe7\xe5n\x9fr\xfbR\xaadh\x1f$dA_W\xac\x15,\x96{|\x8a\xf1\x80\xde\xa64\x9c;\xf5}\xc4m4\xc7@\xca\xab\x85'~et_\xe4\xf6\xa3z\xb1Z\x07,\x0d\xe9\xd5\xac\x07x\xd9\xd6q(\xecC\x8f\x9aC\xcaX\xa3\x99\xf3h\xe1\x97i\xba\xd6\x04\n\xe7\x0fo\x12C\x0cq\xd1\xdfS\xc1\xec\xd57T\xd1\xb8\xae \xd9zC\xf3\xdb\xdb[\xf6\xf6\x17\xda\xb1+-l\x8e\xec\x0d,\xa3\xf5%\x8d\xedm\xec5Lr\xe1\x07\xa6P\xebzs\x04\xeda\":\xf9\x16\x98%\x1d\xca\x1a\x83\xc4\xd47~d\xbc\xde\x99S/\x9a\xd3O\x1f\xdf\xbe\x8aV\xeb(\xa4a\xea(Q:\xcfzh\xb2\xc0\x18+\xcd\xceM\x07\xdc\x7f\xc2_\xdc5!{NT\xaa\xf1\x05$\xed\xd1\x9e\x8c\xdcQ\xdc\x0f\xa1\xcb;R\x9d\xcd\xf95\x0dZOO\xd0#\xde\x85X(6\xd1H\xf2\xd1#\x10G\x0f\x0dkS\x8cP\xb2\xdbG\xb6\xa0\xfe\x94'\xf03\xd0\xbe\\\xf4I\xd1O\xf2\x8f\xc8\x0f\x9d\xee\xa3\xae[!o}H\xb9go 2U\xb0\x94.\x92\xd1@b\xfa\xfb\xfe\xe4\xd1\xac\xe7\xeeO\x9c\xe9\xef\x8f\xb8\x95\x04\xae\xfa?>?G(\x86V3\x01i0\x159\xe8\xb4i6\x8fb\x156\xabg\x0b \x9b\xe2\x87\xfc\xba\xd7\x89\xa7\xfe\x8c\xb1\xc9-x\xa6\xf8a\x08^\xf8FnU}\x1a\xb9o\xe4\xde\xee\xb6\xd67rk\xb8\xa9\xf1\x8d\xec\x1e\xde\xae\xa9\x97\xd2\xb9\xaag+W\xcb\x14\xdf\x97\xf2\x93$\x7f\xe2\x87-\xc8\xb8\xe1\xcaL\xdc\x94\xf5a\xdd\x87y\x1f.\xfb\xe8\xc9\xa8\x89\x01\xba2X\xe2.\x0d\xe5w\xa8\xf9-\xafSE\xb5Yl\x8a\x92?\xf4\xe9\xdd\x9ar\x9fh\xa2\xe6R\x06\x950\\\xe8\xcf\x10\xb9+\x03=\x02\xe1\xddK\x1du\x04.\x04\xec)\xec\x8bh=\x1c\x10)W\x1a\xd3\x01Y\xaf\x83;'\xeeW#>}6\x0c\xf0\xdc\xech\x8f\x16\x12\xb0\x01\xe6\xfc\xedJ\xbc\xa0Kn\xb7\xf2R\x90\xa1P\xdei\xa0\xe8\xc0Z\xb9f\xcf\x16\xad\xc6t\xa35\x97dC\xa2\xb8\xb3t\xbbj\x01\xce\xb9\x9ac\xe3\x90\xed\xe0Z\xb59\xec\x83\x08\x05\x1fe\xa9s\xd3oa\x94\"A\x91\xc2\x068\x08\x0f{\x00\x88%L a\xdc\xdaB\xbep\xed\xd6\xf3s\x00ga\xabn\xdf\x06\x88\x1cZ\x1d\xad\xe7\n2\xa0Av\x00\x13\xb8`\xaf\x8c\xf9\x9d\x8e\x8a-5 M\xdf\xe3m\xd3\x1a\xe81\x97\x01\xea\\\x0bz\xb6Bl,$^f+\x1a\xa6 \x0f\xe4\x9f^\xfaI\x1fo+\xa8Ei\xc2^V\x90\xad\x10\xbf\x9b\x97\x0f\x14t\xe5\xbd\xd4\x91\x80 $\xab\x02fkmC\x9f\x1d\xd3\xc2\xb3\xd1-]u5\xea\xcd_8\x97m\xe4\xf0\xfa\xc6BSyG\xd7\xa8\xdb\xaf\x8cT{r`\xaa\x0bF\x85\xee\xefQFrB\xae\xfbA:\xd9a\xe7-\x99\xfb\xe1\x92g\xdap\x18\x95\xec\xae\xc8\xedo\xc4O\xbbty\xbb\xb5PS\xe5~p\xa2{#\x97u\xff@ *\xdd\xeb9\xe1-]B\x0f\xab\xac\x05\x82\xe43\xa1\xaf\x0f\x9d\xd8\xa9\xc4\xcd\xccs\x08\x15\x0c\":`\x8c\xc1#\xe1\xe3\x94\xcd\x0dH\x02\xb9|\xd9\xa9\xd8O~\xd6\xef\xd0\x1a\x80\xc6\xa0]\x14\x14-\xba\xe7\xe7\xd8\xfe\xf99R\xe4\x7f|\x86I\x15LZ-\xa89\xe8\x16\x8fC\xe7l?s\x1di\x15\x85\xe2`\x9f\x81vw\xe8\x0e\x16NUp\xee\x832\x0c\\\xbc>l\xba.\xeb\x7f*\xc3\xd9u\x1c\xaa\xda\x8c\xa1\x9aM\xe78\xd5\x14y*\xd5G\xcd6\x9e\xb0*0\x8cl\x87\xa8\xebK%\\\x8aFx\xf9\x9c\xd0\x1cM\xd0@\xf6\xb8\xae\x06\xad\x9a\xc1\xfe\xe33\xbf|\x19\x8b\x83\xa6\x82z\xde%\xf5\xae\xc6\x8aEv\xebM\xab\x92\xf5\x02\xe5\x8b\x8d\xdb\x82\xe8\x1b\x8f\x1d\x0fC6\xf0:\x0f\x1b\xd9\x97\xed}\xde\xdf\x18\xc7\xff\xcc}\xe0~oV\x1a2p\xed|E[\nx\xab2\xb4\x90\xad\xf7\xb4I\x88\x9d\xad\xbd-m\xdc\xa1\xa7\xba\xb0C\xa1\xb3]\xad\xcd\x07\xfft\xbbZ=\x10\xe5\xd5\x83\xc0\x13\xbdVG\xb9\xe0\xf5w\x86\xa5\xd3\xf0\x99\xf2+\x1a\xf8![\x1a\xa7\x82U\xeb\x1a\x19Z\xf8\xe1\xfc\xf5\xf1\xfb\xa3hN\xc7Ui6\xa6\xe1\x9c\xc6c\xf0\x07\xfc[e\x92\xe1*\xca\xc24\xd7\n\x1d\xa4\xbc\x11\x7f\xa0\x7fR~\xfb\x9a\xc6\x89\x1f\x85cH\xaa\xad&x\xc3v~\xc1\xe8\x05\x9d\x7fZ\xcfIJ\x931d\x83r\x89\xe15>\xd2\x93\xec\"\x8d)}\x1b\xa6\xd1\xab(L\x89\x1f\xb2y\x14\xc2\xabB\xa1\xf5\x91\x1a\xcf\xcf?\x1e\x1e\xbc:=\x7f}\xf8\xeb\xe9\xf1\xf1\xbb\x93\xf3\x9f\xde\x1d\xffx\xf0\xee\xfc\xe7\xe3\xe3_\xce\xd1CWk9e\x7fM,\n{\xbbU\xc5\x8ar>\x87\xe7iL\xa9.i\xf8\x92\xa6\xaf\x82(\xa1I\xfaV\x10\xe47q\xb4\xe2\xab\x12\x0f\xccO5\xba\x16\x8aK\xc6*\xc8\xcaM1\xc3@\xb9b\x18\x88e\xa0\xf3|\xcc\xfc\x02\x921\xfbR/\n=?`\xcb_\\h|\xaepH\xeboAL\xf6\xf6\xaa\xd1\xca$5\xa9\xeewNM\xf6\x9e\xea4u\xac\xbc\x1a\xdd,\x13\xe5U\xaa$\x88\xe1\xd3j\xbf\x81(\xaf\xf6\xcb\xe9\xc9\xde3==\xa9\x11\xc35'3\xa3*Y\x9a\xf3\xf2\xcd\xea\xe1w)\xcaG\x95\xf2kQ^\x9d\xeeJ\x94W\xc9\xe4R\x94W\xc1p'\xca\xab`\xb8\xe0\xe5[\xd5\xf6\xcfEy\xb5\xfd\x1bQ^\x9d\xef!*\x18\xdb\xf0n|{6\xc4\xce>D>\xeeP\xb8p/\x07\x87\xd74L\x0fW~\x9a\xd2Xl\xf0\x8f\x94x)\x96\xbf\xf3\x93\x94\x864vVn^\xf7C\x90-\xfd\xf0\xe7\xecB\xd4V\n\x8f\xe39\x8d\x1dR\xad\xfb)\xf5\x83D\xd4.Q\x0bga\xab\xcaj\x9c\xc6\x84\x91d\x12\xa0\x80\xde<\x82\xe4\xc7\xbb#\xb2\xa2\x9a\xfbC\xf69\xf1W\xeb\x80*\xd5\xc7pS\xa72\xecs\x18\xa64~G\xc9u\xb9v\xa6\xaf\xfd\xea\x92\x84\xcbrMCv\xb3\x13\x1a\x94\x07<\x86s}\xcd\x1f\xe9\"\x8a\xe9\xdbp\x9d\x95\xab\xd7]\xb4>#d~\x8e\x92\x02\xb8\x020?\xb1\xb5\xf3\xbd\xbc\xf8U@\x92\xc4\xf1\x8c\xf5O\xe9mZ\xa9|\x89\x95_\x1f\xbf\x97\xd7T\xa2\xaaR\xf2*\n\x17\xfe\x1235\xb4\xab\x99\xb4\xaey\xc1\x17}\xb5f%\xe5\xb1\x96\x0b\xdf\x10/\x8d\xe2\xbb\x16\xb1>\xa5\xc2\x81\xde\xc0\xba\x1a\x98\xb2\x80\xa68\xcd\xf3\x0d!\xc8\xf5iL\xc2\x84\xf0\x1e\xee4\x15\x7fd\xbc\x80\x1f.O\xd2\x98\xa4ty\xe7\\c\xa5\xda\xd8\xc3k?\x8e\xc2\x15\x0dS'0K\xf3\xf8\xed\x8b\xc8\xbf\x99F\x08\x00\xfb\x8cw\xa9\x03\xa8Kb\x9flxY\x1c\xd30\xed\x8eu\xf7 \xbc\xca\x9c\xa6\xc4\x0f\x12k\x15?a\xac\xcf\xdcV\xe7\xd2\x9f\xcfih\xab!\xfc\x02mU\xae\xe8]r\x19\xc5\xa9\x97\xa5\xd6\x01\x05\xe4\x82\x06\xb6\nq\x14\xd09M\xbc\xd8_#\x07e\xa9J\xb24\xf2\"FMRj\xab\x87\x92\x97\x1d\x06\xf4vM\xc2y\x03\x9cH\xb2\x8e\xd6\xd9\xda:=zm\x9f\xde*\x9a\x13{\x05\x19\xb5\xbc\xb1R\x82d\x8c-\xaf\xadj\x14\xfb4LI\x13,\xf1\xce\xfa2\n\xe64\xb6V\x8bi\x92\xd8\xc1\x14S2\x8f\xc2\xe0\xce^\xe7o\x99\x1f\xdb\xdb\xe1\xd3k\xa8\x13\xc5\xd6\x1drM\x82\x8c\xae\xc8ms\x1d\xdf\n\x1d\xac\x13F7\x8duRzk\x1d\x10I\xa3\x95\xef\xd9j\\d\x89\x15t\x81\x7fm]\xef\x98\x06\xf4\x9a4\x10\x0eF\x7f\x16\x0b&\x9f[j-crqa\x87?\xa3\xc2\xd7\xb8]i8o\xe8\xd4\x8b\x02\x8f\xf1\xe1\x0du\xd0P\xae\xa1N\xb2&\xd6\xe5\xf2\xa20\x8d\xa3\x06\xca\x884\xe6\x82\xce/\xac\xe0F\xcf\xe8\x15M\x12\xb2\xb4\x82}\x11D7id]8F\xf9\x82\xa6\xfe\xa2\x9b\xd0:\xecu\x94\xf8aB\xadP\x8c\xa3\x9bFH\xc7\xd1M#\xa4\xe3\xe8\xa6 \xd2 M\x13\xff\xef\x08\x99R\x8d\x8a\x00\xf6\xfa\xf8\xfdA\x9a\xc6\xfeE\x96R\xc6\x1a\xb2s\xaf^E\xf2\x1dy\x8d\xbc\xc2W\x9c\xc2\x8aFgX\x95V\xc4\xd5\x81^\xa3\xb3\xb7W\xad.e\xb0\xaap#e\xb0\xaap\x83q\x08\x9f\xf5a\xb4\xd5\x87\xcd\xbd>lmV,[\x990\xb6\xb9\xa9 \x14\x1d\x0d<\x12~J\xe8\xeb\xe3\xf7\xa8O@\xde%\xf1\xd9\xcc\x91\x0fE\xbd/O\x11Q~\x19\xc5\xb5R\xda\xfcjS\xf3\xc8\xc3+\xda\xf7\xd1\x9cb3\xb2\x00\xa4\xc3\xa0,\x18\xa8U\xab\xca\"~\xd3Zm\x9c\xf1\xae\xd5\x01\xb2\x07\x1d\xee\xb2\xe7\xd4\x0dk1\xf5\xbbHv\xc1V\x9f\xb8F\x05\xcaz \x14C\xac\x06\x9a\x07\xbd\x0dS'/u\xdc>\x8c\x86.\x8f\xe7\xa7\x11?+cu:\x1e\xc8HT\x0b\xc0\xec\xbe\xec\x0b\x86\xe4\xabL\xf6Z\x13\xa6{\x95G-\xc5t\xbc\xaf\x84W\x03\xe35K\xf5\x96\xdax\xd2\x17\x85\\\xa1\xe3\x00\xd9g}I\x12:\xffH\x97~\xc2\xf8X?\n\xe5\xb6\xd0Vg\x9f\x8b\xec\x82\xf1zc\xe8F\xa1\"\xb9X\xbc\x10<\xb2N\xb3\xb8\xfe\xca+^^\xb7\xe5\x87\xfa\xde\x96\x9f9]\xd3pNC\x0f\xd9\xdai7\x8d\xd6*\xda\x86\xf3n\x1fX\xe1/\xf4\xee\x03\xe3\"\xc4O\x862b\x98\xf8\xfb\x03IR\xda\xd5$\xe5\xab\xf7\xea\x95\x9a\xffN\x80\xac\xce\xa1\x1d,\xcbo}#p\xfe\x18d\xb1\x80\x92 \xb2\xaf\xa3\x9bP\x0f\xe7_\xe8\xdd\xa7\xb5\xf8\xfe>\xca\x12\x8aU\x1f\n\xe7\x93\x94\xc4\xdf\x0be_U\xba\xf9\x02X\xe3{\xdf\x15\xdabd\xff,xs\xc9\xf6\xfb\x03\x9c\xf7\xf3\x05\x10\xe7/~W\x90\xcb\xb1}C\x98\x97J*\xe3\xbb\x13\xaa\xbe\xbc07\x9b\xba\xd0^\xa5I{r\xad\xb2\x83[C\xe7C\xb3ZD\xd7r\xf7\xa2G\xc5\xab\xf2\xe1\xabk\x18gim:o {\xd0D\xd3S\x9b\xe3\x105\x19\xa8\x97@k\xa9\x84ki\xb7\x00\xd7\xc4\xac\xb3F0j\xb2\x1c\xd7ymhL \xafe\xde\xb7\x01W\xa0\x94G!:1\x05A\xe9\xceIJ\x90\xbbIa\x02\xe9\x80\xfd\xac\xdeI\x14#b]\xdd\xe4,Y}t\x87\x92\x8f5\x84\xa6\xcd\xfa\xba\xd8\x0e\x1e\x86l\xb3\x99FC\x13^\x82\xbaT5\xf2\xd6\x18\xf3k9\xa8\x9e z\xe39]\x17\xec\xbczX\x07\x87\xe1\xbc}\xf3\x82Z<\xac\x07\xfeR\x13\x9d\xe0\xd7O7\xdc\x96\x10\x85\x8fG\"J|u\xb8h=\xd7df\"1M\xd9\xc4\"\x92\xd3\xa3G\xca\x8e-\x07\xba\x16\x031\xf7\x8e\xab\xe1\xf6AI\x18^\x16\x08\x00\xf9a\xf6.\xc6q\x17\xe1{kMp\x1c\xab>:\x0c\xd1j\x8f\xe7\xa9c\xf2\xcd\xcd`I\xd3\xd7$%\x8e\xcb\x81\xb3\x0f>\xdawEQ@\xe7NTu\x05`X\xbd\xc0,\xc4E\xa5\xac\xd8\x03udO\\X\xf0]V\x8bsbp\x05\x95\x97\xd9\xe7Z\x7f\xfb\xdc\x92GDH\x91m\xb7qn\x8c\x07\xc4\xf3\xb2U\x16\x90\x94\x9e\xdeD\x1f\xd8\xf1\xfb\xdaO\xd6x\xf9\x9c\xe0E\xca\xc2J\x8dn\x1b\xf6;\xa9\xcf\xbf\x83\xd1\xa2\xe6U\x13\x9fo\xb6\xe3[m\xc7s\xa7\x1a\xb0F~\xda\x1c\x1c\xf2\x93\x1fF7\x97\xbew\x89\x8bp\x0d\x13\xbe\"cp\xee\xc4u\xd8\xaa\xa9\xabBd0\xf7\x95\x1bv\xe3\xfa\xea\x1b\x04\xe5&\x02Q\x1dc_\xdf\x15C\n\xf5\xef5\x86\xd9S\xf6]3M\xc1\xad\xdc\x82\\0d\xb81\xad,:5\xd4\x17\xb6\x88\x0c\xd7\xf1\xd8\xdc\x04\x07cj\x05\x14\xc0)\x1b\xbb\x11z\xfe \xa6\x01% un\xdc~~\xe0\xf5\x0d\x01,\xf5\xae\xce\xeda\x06\x0fBu.O\xb6Z\xabo\x8e\xe1\x8f\x1eA\xa7\x85iD\xe5m\x87\x0e\xbc4\x0e~\xa1w\xb8\x1ayJ~\xd8\xd0\xd1\xa2\xcf\xd1s\x80\xf2\x83\xf7\xba\xf9\xbe\xb9t<]XD\xa8\xb1\xa8\xf8*\x1b \xba1\x8b\xdcQ\x1a\xda\xd6HX\x01J\x810\xc1\xaa\xac\x96\xbc\x0d\x1d\x9c\xdf\xc4d\xbd\xa6\xf1I*\xb2~\xa4\xe5\"\xf3\xd5\x01gT0\xd0\x980\xd7\x0d8\xaf\xd3\x0d\xb3\xd5\x05\x8d\xf3\x95c\x0b`\x19\x0b(\xacw\x97\xe7\x8c\xc3\x03\xcc\xdc3`\xf4\xb5%Ms\x93TG\x9cyn\x112\x17\x1d\xefk\x15\xb4+\"?\xfa{\x8dz)\x9eB\x81\xd1\xe1D\xafp}\x8f\xa5_)*\xef=\xd595\xab)\xde#q\xa4\x8a$\xe2V\xb4i\x197\xd5@\xe0\xf8\xe5\\L\x17\xf5\x85\x928\x18\xd60\xd7\xe2\xce\xaf\xcfV\x00\x13\xa0\x0e\x0f8\x92]\x04\xbe\x97SMd\x02\xe2\x01\x99\x17n\xa8\x07\xc9G\xba8\x8d0m_\xbf\x1ab\x0bp\xe1B.\xc8\x0d\xce\xa3\x9b\x90Vc\x96\x16K\xc8\xc4\xb7\xe42\xca\x02!\x06\xb5\x81\xa6\x84I]r\x03\xa9\xae\xac]a\xe4\xd0\xa7\x06\xe8c\xb9\xc8\x86\x16\xd3\x85LL)\x86_\xbf\x0f\x89\x8c\x03\xf0\xb5\x03P.W\xecX\x90\x13\xcb\x94\x8f\xc3\xc7\xafb\x1c}\x08\xf1m\x0c#\x9eG+,\xde\x8e\x90\xc0\xf1\xbdY\x062g\x89\xdb\x80\xf7\xff5\xc8\x8a<;\xe2fLW\xd15-\xa3';\xf9\xbf \x82~\x075\\)\xe2\x80Q\x03iP\x8a\xfc\xe6\xc1^\x0b\x13G\xedR\xa7\x91Xh\xf3\xfb\x1e\xe6\\\x9a@d\x89\xfc\xe2\xac\x8d\xc1V\xd8\xe73_\x81 W8z\xe6!\x8b\xf0\xa0\xfb\xfb\xe0\xb5\xc4\x94\xb9h\x16D\x92\xe4\x04\xc6|\xb05\xf5G`\xb8\x96\x07\x19uD\xb4\xe2Y[\xf1,\xad\\WlZ\xc9\xa0 P\x88\xd0\xb8S\x0ds\xc9ov\xf0\x9d\x80S'V\xcc\x17\x0c\xd3`]WVq_\x17\x95\x17\x04dV\xfa\xd1 \x81\xc60\xca\x96\xd1\x08\xd0\xaf\xca\x83\xa2\x9c\xb6\xb3\xe2\xbc\x7f\xf6\xab:\xa8y\xd9\xce\xa98D\x95{\xa9\xeb>\xac\xf8&w\xfb0e\xbf\x1a \xa9\xfe\x8c\xcf\xb0\xf4+\x0f\xd2Z\xf4\x1bv\x8e\xca\x00+~\x14\x0e\xde\x7f:9=\xfftrx\xfe\xe1\xe3\xf1\x87\xc3\x8f\xa7\x7f\xad\x9f\xafj\xf5\x9f\x0fN\xce\x7f<>~wxpt\xfe\xeb\xc1\xbbO\x87\xf5c\xb7Z\xfd\xe8\xd3\xfb\xc3\x8fo_\xe9\xaag\x9a\xea\x1f\x8eO\xde\x9e\xbe\xfd\xf5\xd0\xf6^\xa2y\xef\xf8\xd7\xc3\x8f\xef\x8e\x0f^\x1f\xbe\xb6\x0d0\xd0\x9eR~\xf2*K\xd2h\x95k;\xc6\xf0\x91.\x0fo\xd7J\x94\xfc\x94&\xe9\xe0\xc2\x0f\xe7NHo\xc4c\xa7\xfb\xbb3')\xb9'\xb1O\xdc\x0d\xcc\x01\x14\x0f\x0eNO?\xbe\xfd\xf1\xd3\xe9\xe1\xf9\xd1\xc1\xfb\xc3\xf3W?\x1f|\xc4\xbc@?\xfc\xb9\xab\xcb\x1ao\x0f\x85\xc1><\xb3\x8e\xd6\x07\xb9x\xfc\xea\x92\xc4\x185\xd1R+I~\xa1w\x96\x1a)\xc6\x1c3=\x0e\x82\xe8\xe6M\x16\x04'^L\xa99\xb6\x0c\xd6\xc3\x08%xjx\x96\x0e\x03\xcbp\x13\xcb\xa3\xbb\xd03w\x9f\xa5\xd1+\x11\x12\xc3\xdcD\x96F\x1f\x02rglE\\\xec\x9b\x9f\xd3 \xf8@\xe6s?\\\x1a;auN\xd6\xc4\xb3\xd6\xb9$\xf1\x89e\xd5\xbcK\x12\x04\x14-\x1c\x8c50\xb4\xc7\x18\"\xb87\x8e\xd6\xb7\xc0\xc2\x0bH\x92\xbc}m\x7f\xceYLS\x8d(H\x8cA\x89\xbc\x88\x01\xc1\x8cV^\x14\xa64\xb4@\x80??\x9c\xfb\x18\xe8\xc3^\xef6}O\xc3\xccZ'\xc6\xc1\x9a\x00%*\xbc\xf3\x13\xdb\x88\xa2xnFO/\x8e\x92\xe48\xf61L\x92\xa1\x0e\xb7\x0c2?\xa4\xa7\xbe\x05\xdey|\\\xc3,\xe6t\x81\x81 \x0dO\xfd\xd8\xdc\xb2\x08\x96c~9\xba \x83\x88\xcck\x91 \xf3\n1Y.\xad\x0bEC\x8f \x04\xc6\xe7\x8b(^Y\x1f\x1e\xd8\xe9\x14\xabr\xd8\xa2\x8f\xf74\xbd\x8c\xe6\xd6*G\xd1\xaf$\xf0\xb9\xff\xa9\x01 \xac\x1a\xe7\x0f\xcc-\xc5dE\x7f\x8cb\x8c\x16i\xa8sI\xc9\x9c\xc6f\xa4\xba\xa4\xfe\xf2\xd2\xdc\x05\x0f`d\x1c\xe4\xa5\xbf\xbc4\xbf\x1b\xd3\x85\xf5\xe1;b!`\x97\xe9*x\x13Y&\x96\xa6\xeb\xc3\xbfe\xfe\xb5\xb1\x86\xefY\x16\xd37/\x10\xden\xbd\xc7\xf0\x8d\xc6\x1a)]\xc6~j>\x81|3\xc4\xaf\xe8\xdd\x07\x12\x93\x95\xb5\x86\x15\xc9\xae\xfc\xd0d\xeet83ov*nd\xd9$e\xba]D(4\x7f2\xec\"~]\x19\x95\xea3\x08a\x08|\xda\xd7\xed\xbe\xca>3$WK\xbe\x052\xd5\xd0C\xe4\x87xVE2\x11\x9b\xf4\x99>?\x84.\xd9L\xac\xac\xe8\xa40\x9d\xe7\x89x\x04\x85r\xbas\xff\xfa\xffa\xefM\xdb\xdb\xc6\x91E\xe1\xef\xf3+`\xde9ij,)\x96\x9d\xc5Q\xe2\xf6u;\xce\xe9\xdc\xc9\xf6\xc6N/\xa3\xf6\xf8\xc0$$\xf1\x84\"8\\d\xbb;\xf9\xef\xef\x83\x02@\x82d\x81\xa4lgf\xeey.?\xd8\"P\x00\xb1\x16\xaa\n\xb58\xfa\xbe\xb7\xb9\xf2\x1e\xfe\xfd\xb7\xf4//\xdc\xdf\xae\xb6\x07\x0f\xf1Q\xe8\xa5\xdbX\xbb\xca\xcf\xc5\x9a\xa2\xee\xd6\x04\xd1DL:\xfd[\x91\x8ab\xf8\x8af\xde\xd2M\xdb/>\x01Ug\xb3\xc9yU\x1f\xbc9\xf1\xa8yVH\x94np\xe0\xd6u'\xe1\x82\x1bkd4\x0e\xa2\x88%b\xbb\x08\x9c<\x9b\x9c\x93m\xc2\xc86 g\xbb\xc8\n/B\x1a{\x00\xbds\xfe\x9cx\xa3\xd1\xf3\x81\xd4\x0c\x1d\x874\xcd`\xe1V\x17\xa6\\\xda\xd5O\xb1\xe6\x90\xce\xb5B\x98\x9a\xf4\xf4\x87\x9b3\xba\x80H\x0d\x8e\xf4\xb7^?a\xe7:`\xb3\x8c\x16\xadgkH\xb8;\x1f\x8c\xe7<9\xa1\xde\xd2\xcd\xeaF\x80E/br \x83~\x81\xfa\x89\x1b\x8d=\xd1x\xb1m\xd3\xc1s\xb3?\xa2\x87Z\xdfQn\xe42\x0f7\x99,\xf1\xfc\xd7\xfb\xd8\x7f\xfb\x96\xcdm_\x82\xaa\x1d\xedkT+7nI\xcd\x1cTC\xb7\xaa\xd0x`\x86#~\xf0\x808r\x06\xc05\x03T\xb2\xe5:)\xcb^G\x19K\xd64\x94\xe9\x83\x8a\xde\xbc\xa9\x13)p\xb3 \xcd\xe1\xf3r*\x82\x14\xfe\x8b\x06\x8bO{4\x0c\x19S\xf5\x83\xa9G\xc6V\xaa\xda\xea2\x13%\x0eI\xa3\x12 \xa2\xc0\xf6\xbf\xdb\x98\xa3\xdc\xaf6\x7f b'\xe1\x0d\xd5c\xb7U\xd5n\xb6\x85r\x86\xc3\x08\x16+20\x99\x91\xad\x0c.\xc1x\x81\x8c\xc8\xa4\x18 ]\x1c\x9d\x9c\xb1\x1c7\xa3\x9ez(\xf9AK\xbc=\xb5.d?\xcb[v\x18F\x15\x87\x1d\xc1Jf\x9c\xbc&UX\xec\xbaH\xef:7\x13[U\xfa\x9e\xe0\xe4\x05\xc9\x9e\x13\xbe\xbd= \xd1\x8c\x9f\x8bI\x98q\x04\x05i\xf5\x9c\xe6\xdcO\xc9\x8c\x9d\xdf\xef\xb6\xb3\x1c{XP\xa4\xbb\x1ec\xa0\x13\x89h\xed\xcd&C\xf2\xdd\x0b\xc9\x1f\x16\x02\xec\x03'Kr\xe6|\xff\xdd\x908/\x1e\xca\xcc\xef\x9d\xf3\xe6\xc1(J;/\x80\xb1\xfc\xde\x01`\xf5\x1b\xf1\xf4=\xdb+a_d\x97\xdc\xbf\xf9\xfeE\x96\xe8b\xc9\xf7/\x1e\xaaDK\x1d^\xd9\xda\xf5\x82\\\xaf\xc2(=\x00\x8eo\xfa\xf0\xe1\xd5\xd5\xd5\xf8jo\xcc\x93\xc5\xc3\xdd\x9d\x9d\x9d\x87\xe9zQ\xb4~\xbdhT5G\xa9x\xe7/\xceT\xf6\xe8\xf0\x85\x1f\xacU\xcb\xe0\xd7y\xf38\xa4 \xa3\n\xfc\xc5\x8a\xc6\n\x1a~!\xd0\x1e\x0f\xa7d\xb6\xdb\x1c\x01\xddi\x8f\x87\x8b\x84\xe7\xba\x9e\xe2\xd56\x1a\xe2 \xd9\x82E\xben\xc4<`\xa1\x9f\xb2L\xd5P\xbe\"%c\x9a\xd0\x95.(1\x8b*\xa6_\x90BY\x82vAM`\xeb\xdc\x11y\xb7\xb0\x90\"wDn\xcacy\xad\x8bdyT\xe5!l\x92\x1e&4\x13\x9a\x84\xe7\xcc9\xcf\xf0\x9c%\xb3\xdcog~#\x08\xa0,0\xad\xbb\xa7,w\xfa\xcc\xf1\x82\xc4\x0b\x81\xc5\xf5\xc2 \xfe@\xb3\xa5\xf8\xed\xb39\xb8n`a\x18\xc4)d/\xc4\x9f`E\xa5\xaf\x07\x08\x80\xa2\xfe\xd3\xe4?\x13\xea\x07,\x02-\xdd\x15M\xc1\x03D\xac\xaaR72\xf0\x93\x877\x0b^\xfc\xd4u\x88\xc244\xebHddJ'\xcd\xb8\xf4\x0d\xc1\xae\xa5\x060\x84;8/(\x1b\xfba6\x07\x0f>\xc4\x1b\x12*\x7f\x99\xc1xk^N:i\x88@\x9c6\\\x9e\"\xf3\xda)\xa2N?p!\xe4\xfcEpV\xd4\x02\x11T\xe8?\xe7/\xa5m\xb5\xf3\"\x0c\xa2\xcf\xe4\xe1\xf7\x0e\x99\x12\xe7\x85\xa3HP\xe7\xfb\x17\x0f\xcb\xdfN\xd9\x95`<\x0f\x12M}\xa9\xe4C\xd9e\xd4\xd3\xed]\x0f\x01T\xc8`Qwoe~q\xe1BO\xeeW\x1f\x9d\xb8\x82(\xe6\x83\x99\x80\xab\n%\xfb\xd0\x0e/\xa2>\xac$Nl\xde\xc1<\xa2S,\xd1p@\xa3\x19\xc9z$=-\x97\xa8\xcfI\x8eK7R5\x85x\x9c\xc1\x86\x02\xa6\n[\xfa\xa4\xce\xbe\xaa0\x83\x0dW>\xb1\xaa\xbe\x9e.\xe3\x0cN\x1e\xd7;+\xe3\x0c\xee=\xae\xc3\xaf\xf1\x15\xa5\xc2\x0c\xee\xd4;\xab\xc2\x0c\xee\xd4 \x91\x1b\xd5\xfc\xfa`\xaa0\x83\x0d\xbb\x8d\x0b)\xb5\xd9{6\x18B\xb8\xc4\x9d\xba\n\xa4\x8a7\xd8\x18\xbe\x13U\xf0\x11\x14\x9c\xf8\xeb\xebB\xa2`r\x0b\xa2\x85\x16{\xf7\xa8\x10\xf9;\xe4l\x19\xa4D\xd0\xf6\x82c%W4%:L,\xb9\xbc!\xff%\xce\xa9H\x9cS\xff5Fn6\xfed\x7f\xd3\x1f(Ka./\xde\xa1'\x83\xb4Z\xfd?36\xbe\xc8\xe8\xe2\\\x1a\xd7(s\xcfl\xac\x97\x85\x1e)\x99jY\x0c\x8a\x1fu&{O\x1dA\x1d\x88\n\x87\xf6\xc1?$\x0e\x81\x0btA\x8f\xa9\x91P\xaa;\x84\xcf \x9c\xda\x96\xb2\xe5\xc0\x8b\xe1\x1a\xc3\x91\x0f\xf6\x89]M\xb4uO6\xfc\xc9\x0eHu\x11\x9b\xd9\xb6\xfa\xce\xc0\xa3\xa4\x15B\x8a\x94\x9fL\x9cA\xa5\x81p\xcf^1\xd158\xf72W\x14\xddu\x86\xb0\xec\x07\xed.M>\xb6x\xdc\x90N\xb6\x133P\xfd\x15\xea!\x19\xf1\x88\xa8m\xa6\xd9\xf8b \xa1!\xda[\xe4\x05\xac\xf2\x07\x0f\xf4\xcfRN#h\xb6\xd7`\x99#a\xa6\xe2W\x87 \xd3\x91\x9b\x0dI\x00>\xb2\x16L\x06\x8e\x85\x88\xc7\x1f\x19\xf5o\xdc\x81v\xa6\xe5\xbe\xc4\xee\x0e\xa0QQ\x9aM \x12\xeb\x99\xa0\xb6v\x16\x97\x9a\xa1:3\xa6\x88\xdf\xe7\xafVKQd\xb6^6\\ \xcd\xc7q^\xc6\xc1\x05\xe7\x92\xa2\xcd\xca\xcfd\xbd\x85*Y\xb7\xa7}i\xbci|l5\x8ey*G\xf0g\xe9\xca\x02\xbe\xd8^\xcd\xa7F5\x97\xb7\xa9\xe6\x1f\x8dj\x16\xdd\xd5\xe8_b5\xbej\x1ca\x19\x8f\x8f.y\x02w\xd3\xe2\x7f\xed\xcc\xcbx|L#i\x0e\xe0x4\x8aCzc\x05)\xfc\xe1h\xc8L&4\x0b\xbc\xcc\xe5|\x1c+\x0f\x85\x8e\xaf\x12<\xcc\xab`\xc6\xe3\x93U\x9c\x05\xe0K\x90\xc9_\x08H\xe4%7q&\x81\xf4o\x0c\xccW >\x9a\x9d$p\xa3\x0e\x91\xfd\x9a\xd9o8\xf5\x99/\xfd\xd6:!\xbc@\xc8\x0f\x0b\xe0[\x96Q\xdf\x04^\xa9\x04\xbc\x80\x8a\x9f\x04\xb0)\x12\xe4\x08\x1c\x96\xe7\xa9\x18\xb0X\xfcG\xb2\xe5L\xe1\xd3$2\x81\x88\x80\xfc Z _$\xa0X\xe6\xc4\xeag\x13\xe8#\xcdX1s \xcd\x98m\xd6N\x19\x03\xf3\x0b'\x85\x1f8\x80lQ*\x7f! \x19\x0d\xa5\xcf\xc9T\xfeB@\xf24\x06I\x8f\x93\xca_M\x90\xb3`\xc5t\xb4$'\x0bV,\xc7B\x1ae<\xfe\x89\x87\xf9\xaa\xec\xdd\x1a^m\xfd\xfb\x99\x06\x99l\xfe\x95\xfce\xd0\x11\x18 \xf6{c\xff^\x8f\xb3\x84z\x9f{\xec\xfd\x1f\x1aeK_\xcb\x82\xe0~\xfdR\x1f\x98{\xf5\x8b\x1a\xb1\xf3\x199 \xea3\xd5\xcc\xc2W\xbe.\xfe\xc8)<\xf4ft\x81\x1du\xd2\xd3{\x00\xba\xfb\xd6 ?\xeap\xc6\xdd\xb5\xcb\xeaMW@\x05>\x06\xb9\xa9/\x86%\xfeA\xba\x1bU\x0e\xdc\xd4\x1e\x01\xb9\x8f\xfc\xcf\x06\x96k\xe0\xcb\x84\xd1\xcf\xcd,\xd9\xb0u\xe03nm6\xcd\xfd\x00\xcb%\xa6\x0c=+]a\xdb\xfbp>$\xaf\x06\xe4U]\x1e\x93\x01\xb1\xd7Vx\x1c\xe7\xe9\xd2E\x86 \x1b\x92W\xb3\xec\\t\xdcB7\xb7v\\j\xac\xdd\xef\x8c\x9cH4Y\xe0\xcb[\xceI\xb0Z|\xf3v\x0d\xc9\xb7\\Us\x9e\xac\xee\xb7\x0b\x1f\x19h\x88\x11'Q?Z\xbap\x9a_\xae\x02)\xb4\xd4\xbfn\xd7\x8d\xc0\x128E\xad \xe9*\xce\x1a\xd7\x8b]g4a\xf4~\xc7\xe1\xb5\n/>\x14\xad\xd3?\x99=$\x01\x82;\x7fj\xe0\xce\x1b\xa0\x9b\xe4\x89\xd0\x87p\xfa\x11\xe5\xfd\xe5%\x07&k\xb8\xa4\xe2\x94Fs\x12<\x1d\xae@\xb0\x0c\xb6\xba\x14\xc7\x1f\x96\xb5\xb4\xd4\x15\xac,\"\x90@\xc6\x14\xc5\xb2>\xb3\x9b\x05\x8b\xf0\xbc0\x88>\xe39\x82\x9e\xc1s\xd4\x1d\n\x96\xa5Ug\xb1<8\x0e\xf1\xac\xab\xcbN\xe1\xcd\xcf\xe84\x89Uf\x95\n\xc5\x89\xad%j5w}\xf3\xff\x80\xff\xbe\xe6WW,\xca\x83\x8c\xad\x90\xf2\xe4\xc7\x9ap\xedW\xd0\xa2\x99\xd1\xd1\xefG\xa3\xbf\x9d\xab\xff\xd3\x8b\xdf\xc6\xbf\x8d~\xf3\xcf\xff\xf2\xe7\x87U\xf0\xbf\"\xb7\x95\xff i\xb5\xd3\x06#B\xfe\x8cJ3\n\xedJ\x1d^\xd0\x199\x03\xf2\xfd\x01\xd9\xa9J0\x02[\xa4\x92\xbfA\xb0\x01\xe4{\xbf\xb4\xc5\xd8\x13|{\x15\x17u\x85\xc4\xf9Oy\x03\xfeW\xf03\xfb\xe5\x0bq\x7f\x05\xf3su\xcf!\x08\x98\xc7\nW\xfeU\xdf\xbd4\xdc\xbc\x16\x04NUFb\x86\x03\xc9\xe8\x824\\C\xea\xcc\x88\xaeX\x1aS\x8f}\xfa\xf8\x9aT\xe3ph\xb9\x94\xbee\xa8e\xc7 [\x07r\x9e\xb9e\x9dRZ[\x1a\xa4\x05,u%\xa99\x17\xb4\xbe\xa5\x9d*\xbcv\xee\xc6\x16\x08\xd5s\x18\x92\xd7Q\x90\x054\xd4t\xbb\xa0%\xe7C\x92\x0c\xc9\xd5@\xfa\xd8o\xfa\xf4\xfb\xda\xe6fP|\xfd\xa4\\\x98\xf0\x8d\xf71\x8b\xce\xe8B\x9a\xdd\x1cE\xfe\x87\xf2\xda*\x85\x0f\xb6,\xf6\xebZ]JA@\xd6\xa5[k\xe9\xa7h\xfe\xd6\xb5@)?\xce\x8a]yN\x0e\xc9\x89X\xdeR\xf3\xebD\xaet\xb2M\xae\xc5/\xb9\xfc\xadKC\x02\xf7@\xe0\x1b\x92\xaf]\x14O\xc7\xc9\xf2\xa68\x82\xe6c\x9ag\x1c\xc2\x88H\xd3\xba\xd6r\xc1x. M\xfe\xe3\x9fr\x14w4\xeb\xd3\xbfSwZ\xa9\" r\x99gY+-\xf7o\xd0\x8dNz\xb3\xa3Q\xff\xe8O\xbc(\x99J\xab\xbeN\x0f\xcc\xd0CCQ+\xd6\xc8\x03l\x83\xb3\xb0\xb8\xd2H\xe0J\x03?\xc7@\xa7\xa7~\x8f\x91t\xc6\x89\x06/\xee\xb3\xa4\xc5T\xcf\x0c)\x11\xd8\xcfP\x0d\xfa\x1ek\x03x\xa7\xfe\xa8N\xa1\x04\xe2\xa2\xd8\x0e\x04\xfdt8\x87\xd5\x8f\x03\xba$\x92\x96\x01\xcb.7P\x7f5&\xc6$6\xdc\xfd\xe3\xebP+\xa2\x08\xa2-\x80x\xf6r\x9a\xe5\xfc\xbe\xe2 \x94H\xdd@-\xa6\x8e\x06\x135\xa29\xc1\xdc\xeccOA'\x9b\xf4\xe4\x9fK,\x0c\xeb\xe8\x90\xbcm\x8e(\xc8\xd4\xc4\x87\xbcz\x9bk~ ]1\xd8\x10(\x01\x85.\xab\x94\xda'\xb9\xd4 \"\xdb\x07\xc4\x01\x15\xa5\xbc}\xc2\xfb\xc6\xcb0\xcc\xc2#\x9f%g\\\xf0\xf9\x81'\xdbA\x0eID\xa6\xfa\xf4\xa9\xd2\x1cf[\x1a\xad\x07\xfa\x03\xf4\x8eZ\x80^\xbfT\x15\x83\xech\xd0\xea\xd3\x1d;\xb5\xfb\xf9s_\x17\xe1Kp\xe2\x80\x93\x16\xb5\xad\xe6J1\xf7\x1c\x1f\x14\x0b\x85\x8f\xa5\xce#\xccRB\xca\x04divP=b\xc1\x7f\x98\x15\x1aYZUL\xd0\x1b\x86\xe2\x98M\x01R?T\xadu\xc0\x0df\x84p]\x83\x9d_)Q\n\x0c\xdc\x89\x1b\xb4\xd1\xc5f \xda\x86\xd3\x12\xbd\xef\xa5\xfcQ\x13\x8aT\xc5[\x18\xff7\x0f\"\xd7qng\xa7O\xca\xa5\xfc\xb3I\xa3 \xce\xf37\x15\x02,\x19{K\x9a\x1ce\xee\x8e\xd8\xbb\x90\xbcM\x1225\xe2^\x10\xeb\xca\xab\xd1\xb7\xbd\xa5\xa6Z\x89\xed~\x97X>\x86\xd3T\x94\x17\x08\xe2\x7f\xc6bs\xa4\x83\x89\xc0\xe8 \x84\x86\x06\x0c\xd8{\x05Z\x1bY\x9c\xd5i\xfbB\x94\xec\xca\xces\x12\x92\x17$\xd5\xb6\x94$\xdc\xde\x1e\xe8fI\x0e6\x19\x92t\x16\x9ew\x912\x8d\xe8\x14\x1e\x0b\x8c\xf0\x14\x9ba1\x8c6i\x0e\x0d\x06e\xdc\xceHv\xb0h\x81\x9b\xc1\xc9\xdf\x8czR7\xe8\xab\x16\xbb\xc5\x16\x00\x19=\xbe\x8c\x82o+\xd7\xefb\x8c\xb8M\xdc\xcb\x15 \x82f\xda\x96%\xb9\x17J\x9a\xdb\xa4\xb3\xbaMh\xe6\x9d\xda\xd4)\xba\xe56\xf1\xacn\x13\x9ay\xa76\xf5\xe0\x03\xb9M\xec\xaa[\x85f\"$\xb3\x9d\x01\x7fW\x14j\x13\xaapE@7`\n,\xa3 \xc4V\x19v\x8b\xf8\xfa-\xde\x95\xda\xd1\x15M\x8c!\xb9\xc6\x83\xe3\xde\x95\x03\xec1\x1f\x97X\x83\xee\xf0\xc9\xcee\xd9\xc1t\xfe\xd4\x8f\xe9\xac\x9f\xfc\xc8\x0co\x80\xade\x8cI\x0b\xcf\x98 >\x00\xf4\x03:\xf3\x08\xc3(Y~4Y\x1f\x7fl\x96 \xe7\x91Yq\x85+\xeb#YN\xed\xecZ;\x1f\x05\xfd\x0cD?\xd3\x01I\xeb\xed\x0e\xa4\xec\x1fX%pU\xf2\xc7\xd7\xc1,8\x07B\xbd\x83\x9d\xb33\x8f\xedW\x8e\x92Z@\xb8`r\x08\x03G L\xad\xdc\xe6\x89`\xcc*\x0c\x1fka\xf8f\xd8A\xecB\x11\xd1\xed9\x90\x81q\xc5dfn\xaa\xd1\xc4\x83M\xd6x\xebZ\x12\xe0\x10\x98\xa6\x87Pb.\xa6\xb0}\xf1\x0dI\xdc\xb5\xa7Hek\xc4\x03\xb2\x15#{\xe3\xcb\x172\x87\xb1\xc0\xf3n\xb5o\xaa_\x9e\x0f\xd0\xca\x1f< \xb1\xa8OL\xc1\\\xfc\xb0\xecR\x91\xd7!\x81\x90\xfbM\x14E\"\xfb\xe9\xa7\xa0\xe0Q\xe9\x94\x98\x1aC85\x07|;\x95k\xa3\xdc\xaa=j\xaf\xc9n\x06\xf6\x9d\x9c\xb2\xacm\x1b\xb7\xdf\x8d\x17\xdf\xdb`\xa3w\xa3`\xdf\xa6|^\x7f\xca\xddrX\xedI\xd1K_u\x81L\xed\xd8\xc5\xdf0\x10k3\x05\x84U\xd4l\x80\x12\xd8\x15\xe3\x98c'\xb2\xf5\xfc\xbd5\xd7]\xb0\xb6\xac\xc2\xda\xb2~\xac\xed\xdd\x99c\nZz-6|\xd6L\xc5\xd1\xe3\xd5\xe6m\x02\x05\xd0\x8f\xbfU\xb5\xa9\xc1\xc6\xf3\x92\x8d/G\x0b/\x16vq\xffx1\xaf\xf25\x03\xbd[\xbc\x07\xcf+\x9f1\xe0\x11\x1aKg\xa5\x05q\xa4B]e\x06\xff\xabIr\x89\xb8#uF{\xa2\xc8\x16 _\x03\xf8\x8c]gJ\xf8\xe8V,>\x03PF(\xe4\x16\xd6\"d\x9b\x04\x03\xe3\x98\xcc\xc9!\xa1P.\xaf\x95SW\x92\x8e\x14\xf2\x1aE\xc2\x1a`\xd1\x81\x10\x0bg]\xdbL\x8a\xffy\x07\x0e\x85\x8b]\x84\xed\x1d%F\xab\x1b\xd5 u\xe6\x91]\x95\x10\xabyC\x9e\xfd\xff\xe9\xe2\x19\x8f\xd6\xf9\x95c\x87[\x01\xd8\x0f\x07iV\xdezvT<\\\xed<'\x11yA\xb2B\xfa\x15mo\x0fH6\x8b\xce\x95\x0e\x87\xcd\xf2\x9c\xf4a\xe7\xda\xf8\xd9\xde<\xe6\xf58\xcdx|\x96P\xefs\x10-\xbaN\xc7\xce6\x81\xc3\x82\xb6&-\x19\xf5\xdboo\xb9\x7f\xd3\xd2\xde\xc4u\x9e6\x1f\xe93\\\xf6\xd9i*C\xea\xa7\x8f&\x8bA6\xe0\x07\xa2\xf4h|\xc7\x03\xf1\xe9\xb3\xba\xcb2\x0e\x86\x87\xa3U:\xea\xf4\xdc]_\xeaj\xeb&n\xe1e\xdd\xe5C\xe2\xac\xd2\x913\xa8\xe3\xda;\xb5\xfb\xe1\xc8\x1d\x0f\x1e.n\xd9\xbe\xb2u\xc9\xb0\x1b\x85kW\xe0\xe3\x8c\x7f\x12\x14$\xe2\x02\xfc\xeb\xbdv\xceF\xa5(\xaa!\x19\x07\xe9\xa7(\xc8B\x96\xa6\xef\xc0\x7f\xd9\xa0k\x1cZ]\x19iQ\x02h@9\x97\x9c\x87\x8cV\\\x17\xcb\x0c\xa5\xc0_z\xe0\xaa\xed\x04\xady\x11\xa4\xef\xe8;7\xab\xa1\x07\xbd2DU \xe80\x9c(s\xc4?\xe5\x83\x07\x84K/\x922\xd2\x05\x99\x82\x08\xbc\x11!\x80HG\xe3`\x96\x99\x04+\xd0\xcf\xca\xc4y\x13_7N\xf7;N\xca\xfe\x0e6)\x0f\xff~\xb7\x8d2\xa8\xec\x94\x11l\x95\xfbl\xf7Cwv4\xfa\xdb\xf9=m\x16g\xf4\xe7\x893\xb08\xc3\xbfCk\xfb\xb5H\xcb\x0b\xfe\xf8\x8a.\xae\xa2 z\xe6\x17\xdb\xb8\xb6\xd8\"y\xf9\x90\xcd\"pq-M\x89\xa5\x14>\x82\xd54\x8b\xec~\x05\xc8m;lpg\x8fw:\xf7\xafej\xbes\xbe#\xdb\xb0\x88\xc8\xb6x\xb9\xe7\x86M\xcc\x86i\x92\xa9\xda\x10q\x08\x87\xecL\xd9\xfcb\xa2l\x8e\xcdE\x97A7\x01?\xa9\xea\xa6\x1b\xdc>\xa4 !(|\xa7B\xda\xff\x07\xf7\xe0[\x13\x84\x9ft\x931\xbb\xce\x12\xeae\xbat\xd9\x1e+s\x8e\xcf\xc2\xbd\x84~\xd9}2\xc0\xec\xe09z\xe8h\x9e\xc1\xb2\xcc\xa3\x19\xabn\xc0s\xcc*=\x9a9?\xb3\xcb\xcfA\x06\xae\xff\x80\x1c\xb9*\xde3\xc8\x7f\xcb\x7f/3W\xf2E\xe6\xac\xd22\xe3\xedi\x99\xfe\xbeL\xe6\x90\xda\xf8jm \x12\xe3`hN3\x8d\x82\x15\xb8\xf8\x02OM\xdcu\x8et\x823$\xe5\xcbI\xe4c|KQ:\xc8\x98\xf4\x14\xd6R\xc7k\x0d\xd3Z\x93\n\xf5g\xad\x05\x9cqa5d\x89\xa0?\xcd\xae\x9c\x15)\xa2\x86\xf2\x0d:S]\x81My\x02\xe6v\xde\\\x0d\xa6k{q\x00\xe6\xfd\x18\xf6\xca\xa0\x8a}\x01Q\x1b\xae\x82\xc8\xe7W\x80\x04\xa5\xa8\x8d\x04csf\xca\x97!i\x02\x14\x83\xdf\x0e\x06#[\xbe\x0e\xaac\x82\xb4\xa5\xa8\xa22\xb4\xc6[o\x9f\xd9\x82\xc6\xa13v^P.\xe2\xe5y\x03d+0a\x90h(\xe2\xe4 \x1aE\x0d\x113\xce)\xa2\\b$5\\D\x91\xbc\xd2.P`\x88\xce\xd1\x8d_qIJ\xee\x8e\x946s\xfc\xdct\xc1,%_\xbb\x93\xba\x0f\xe3\x1c\x97:J\xc7\xcf\x8f\xf6\x8cCE\xbb#~\x86b\xc7\xb0\xdb\xbd\x19h\x13 zY\xc6@5\xeb\xf5\xac\x07\xaa\xe3-\x99\xf7\xf9\x92_\xebHU:,\x1c\xb8\x84\xe7\x95\xd4\xc3R;d\x0c\xc5\x98oj\x8c\x8c!R\x9b\x05\x1d6\xa3)\x98\xaa|\x1b\x88\x95\xe8x\xa1$ nf\x11\xed$\x1a\xecX6\xb2A\x9a\x93\xb2\xff\x98\xcf\x1a\xf1\xc8\xb0\x9aR\xe8f\xb9f\x850\xa8m\x10\x10(\xba\x15\x80^k\x80F\xfeWX\xddx\xe3Tx\x7f\xd5\xbd\xf6o(\xd8\x9fd\xd8\xc16H\x15\x99P\xcfg\xa4\xccFX\xed\x9e*\x90*\xf4P!^\x91\xa7\xdb\xa5\xabJ\xc8!h\xe8[\xaaR\xfd\xc0++\xddc\xd6K\xeb\x9c\xe6\xd0\xb5\x9e6\xa6\xd9\xff\x06\xeb.\x1b\x9b#\xd9\\O\xac\xa7\x8b\x8dj\x9f\xcb1\xca\x8a-uh\xfc\x9e\x96\xdfm\x1d%sR\xcc:aN\xa1F\xf9kJl\xb7\xffU\x8f\x1f]s\xd1M\xcc\x92\xc6m'\xa6\x11\xde.\x9b\x95\xfb\x9d]3/\xcf\xd8{\xf5q7k\xb7mK\xc74\xa5\xb1\x1bv\x1aI\xae\x0b\x85\xf6\x88\xaeZ,\xe4Azh`Ce\xfbk\xe8k\xa2\x14\xbf\xf9\x14G\xa68Xr\xfb=\xd1\x10\xee0\x82\xe7\xc43\xc2\xf7=\x1f@j%\xa9\xdf\xd7\xe6P\xec\x1f9KnNA\xf7\x96'Ga\xe8\xca\x9b\xdb\x99\xe8\xf5\x81\xa0i\xff\xcf\xe9\xfbwc)i\x08\xe67Re\x01D\xd8\xdf\x9d\x83\xda\xcc\x81\xea\xfd\xf9w\x03\xe9\x02`\xe79\x89\xc9\x8b\"\xf4\xd9s\x12oow\x0d\x01Q#\xee\x83\xd6Y\xdc!\xb3$j\xdc\xfdR'\xc3\x1f\xcfy\xb2\x82\x19\x08\xe0g\x9f/\x12\xf5\xd5\xa5\x1ew=\xdeb\xec\xe1\xd2\xb5\x1e;\xcd\xf6,\x95c\xadg\xe0\xe4\xbb\\d\xcbn\xc9*.\xfa\xec\xce\xb5\xe7\xa0\x01\xa8\xf4\xf3u|\x19D>\x1a\x9eO<\x1e\x8f\xb2\x84Ko\xb2\x1e\xa6N\xd0\xaaM]\xa1<\xba\xf0\xc0\xda\xea@\xbfe\xf3Kd\xab\x10`sn\xca\xe3\xe9\xc1\x03\x12\xa0\xdaq\xf8\x06\x13\xdc\xb4\xa3\xaa\x85;\x1b\x88}\x8b\xcc\xbe&\x17\xad\xd5\xe0\xb8\xb1N\x9b4+\xaeZ\x84\xe1x|N\\)'\xe4pG\xa1M\xde\x00{\x0f\xf4\x0f\xc1\x8d\xeeX\xc4\xf2\xc5MD\x11\xd2\xad\xc4Y]\xb8\x1aD\xec4I\xe5]\xa1\xab\xbe6$\x93\x1d\x90\x18\xb5\xdc\xc9\xb8\\\xeai)\x8f1RcK\xb7VbH0\xa9,\xdb/\x91\x0c\xbe\x80e'\xca\xe2\x1a\x1c\xaf\x039\x8b!\xd6\xa3\x16\xf2*x\x03_W\xcfr\xd9\xd4JJ\xf1\xc9&\xa4[\x03E\x01\xb5f\xd9\x81y\xaec\x0d8.\xf3\xca\x8au\xe2\x01\xd9\xda\xaaC\xb6\x926u/\xe8\xdfl\x7f\xda\xb6Fs*\ne\xb1\xd6\x05\xa8\xf4\xab\xa4\xd7\xd66\xed\x1c\xe9\x05\xb6\xc5d\xa5KA\x08\x02\xbd\xb7~\x02\x9a\x06\x1a\x85\xdc\xa3\xed*I+\x1ee\xcbv=\xaa\xae\xaf]1f\xd3n#\x10a\xb5\xdc2C\xe3-\xea\xa0i\xf5\xd32\xaa\xaa\x82>\xdf\x8ej\x0c\xa2~\x9a\xc7\\\xc1\xb0[(3eb*\xdd\x11H \xa99?,\xbbdl\xa2zZ_(\xfc3u\x05\xcd\xe2\xcd\"M\x9dC\xea\xad\x04\x17f5\xce\xe9\xc9\xf1\xc7\x93\xb3\x8b\x97\xef/\xde\xbd?\xbb\xf8ptzzq\xf6\xe3\xeb\xd3\x8b\xf7\x1f/~}\xff\xe9\xe2\xe7\xd7o\xde\\\xfcpr\xf1\xea\xf5\xc7\x93\x97\xce\xed\xbfi\x08K\xeaR\x11\x15o\xb9\x1e\x0d+\xc0\x85\x1f\x94\xe0q\xa0\xf2\xf2^\x0f\x8e\xdf\"\xb3\x90V\xa4\xf6{\x90\xfa\x15\x9c\xe6\xe2\xc7Z\xad\xae\x88K\xc7\x86\x1d\xc8\xaf\x90[\x10\xe9\x9f\xacq\xd3&\xc5 \xe5)Z\xa6\x1f\x92\x8cl\x8b\x92SiN\x01\xd2\xc8\xad\x9d\xba\x9c}0$Y\xb9:*#\x1c\xe2\xee\xd9\xb8\xe9K\xc2\xd0\xa5\x96\x94\x8b2\xf6\xab\x17,d3\x92!\x01\xc4\x03\xea\xd5\xd7\x99[\xbf\xa8 V\xe4\x10\x0c[\xbc\x80\x98=\xb7X@\x08\x90\xc0PDo2\xca\xdbb\xf7OI\xea\x96\xfa\xef\x03\xf9\xd1\xad\xc9\xb0\x16\xe0\xb7]7\xa9\xe0\xc6\x0c{\xf4\xa4b\x8fn-J4\xf7 .\x0ef\xe1\xb9\xe4~\xfa0>rEv\xb36\x80\xda[\xa1,\x8a\x1b\xa5Y\x90l\x9dl\xda\xed\xe5\"r\xbd\x08\xa6$\xefX\x04\xdf\x96\xe8\xb1s\x1c\x06!\x19X\xe8\x9f\x8a\x037\xd7\x01xg\xa8K\xb6\xd2n\xb7\x14\x87&\x16\xf9e9\x9cm\"\xbf2l[\x8b\x14\x12\xa1\xeaJ\x99oU$\xa7\xbf\xaaN\xcc\xe2\xd5\x0ei\xe1\xbf\xc0\xe7\xa3\xb9\xf7\xec\x02\\\xf5-\xaft5\xcd+\xd7r\xa4\xcf!-U\xee\xeez`nt\xbb\xd0\xbcE\xa0\xf8A\x9aoz\x8b\x90\xf6\xbaE\x08;n\x11\xf4/\xfc\xb8\xdap\xb9j\x81E\xc9\xff\xd8\xad\x9e\x12\xd7y6q \x82\xfe\x1fmRp%\xaf\xbe\x1f\xe1w\xb9\x13\x1c\x159nC\xa1\xf7\xbf\x8b\x9c:\xe8\xbe\x1f\xb1\x9c\xf8\xa6fT+\xc5@\x1b\xe2p\xbb\x187$\x07\x9d\x0ed*\x96QnE\xd7V\xac\x85]\xb1\x16\xaa'n(\xc5 \xa1:F\xc9\x8b\x032\xd1\xf2\xb9=G\xf9~ g;\xe7\x03\xe9\xdc\x16\xe644\xb8r\xa9\xc8K5\xd7\x00\xc2\x9b\xe6\xfc4R\xfa\x1efUq\xbc\x94S\xfc_&w\x0f6\x95\xbb\xab-\x9eK\xc9hZ8m\xec\x10Rv\x8c\xfa\xbfD\xfcH7\x92\xfc%\xf5]\xd7E\x92v\x10\xe3\x92\x9e\xc2\x07Z\xda(F%%\xe2\x96\xfc5\xafH\x9d\x1ar\xab\xa8.\xb7B\xa4o\xcd\x15o\x17\x995+\xac\xc9\xc0\xda\xe6\xf1\xb6D\xdbf3#E\xc9Yi\xc1\x89P2\xea\x82\xdb\x8e\xee\xa1\xafY)\xc5\xd8\x90\xfd\xff\x96\x94\xc5\xee.f\xcf\xe4\n\xf8]\x19\xe4X\xda\xf2l\xaeg\xa3A\x9f*v\xc3\xa85\xfd\x90\xf0\xa1\x9dQ\x04Y\xbfv\x90\xd6\xd6\xec\x14\x1cGgC8;i\xdd`\x99\x0dE-\xc5\xe7\xa4\x06\xa9\xbd\x86\xf28B\x17V\xc7\xaa\xe0bU\xd0\x86\x05q\x04\x12T\xd8\x0fQ}M\xf0\"\x9a\xf6d\xdffg\xa5\x95\xbeg\xaer+h_DR\x1d\xca9;\xf9\xe5\xec\xe2\xf8\xfd\xbb\xb3\x93wg\x16G\xacD]1\xc3\xd0X\xa2 \x8bg\x0e\x07\xb8\xcf\xae\xbb\xbcR\xce\xd5M}\x17\\\xc6{UG\xe7\x19K\xca\xfaP\xb8\xaf\x03\xcc\x1d\xa4m14\xdd\xd8\xfe\x8f_\x07\xa7'g\x17o\x8f>\xfe\xf5\xd3\x87\xff\xb7\nH\xdeq\x1c\xdbVCf\xf8\x16\xbc\x1dIp\xdb/\xd7\xcf\xc9\xea\"\xb4\x8f\x1aG\x14\xb5\xcd\x87v\x9c\x809r6W\x89\x19Wz0\xa5\x92\xa0\xb0\x9f\xcf\xe2\x1c\x84\xab\x97V\xe7wp\x0c\x0d\x0b\x973\xed'\x1f(6\xb5\x83\xf8\xdd \xcbn\x90\xb5\xf5\xe6B?\xb0\xe1=\xa9*\xddZ\x15\x0cC}\xcb{\x9d\xe4\x00Qc\xb3\"\xeav3\x99y=\xe8\x02\xf1,\x04E8\xf3z\xa8oIU\xad\x059$\xee\x1c\xa4\xb9su\xe4\x97\xc1cVC\xb2\x1eB$\x9e\xc1@\x86\xe3yK\xb3\xe5xE\xaf\xdd\x95y\xc0\x0b\x80!Y\xd5\xce\xfc\x18|\xf1\xad\x80\xb1h/\xabB:\x95M\xb8(\x11\xe8\x91\x04s\x17CBg\xcbs\xdd\xa2L\xd9B-\xb7\xb7\x07C\x12\x0b\xf2b\xad\xf9|\xed\x81\xc7E\x9c\x7f\x98\x8f]\x7f\xab\x9c`>h\x1a\x03zR\xbaUk\xb2\x89\xf5]\x980\xc2g\xde\xf9\xa0\xcdm>\xf8?\xd2\xe8}^\xfa\x0fi\xd2\xb5\xcdK\x17\x82\xf6\x00\xc3\x7f\x91\x95\\o=\x087<\x05\x9b\xe7^f\xfah\xb5\x84\x9c\xec\xd3\x81bA\xf6vLF\n7\x05\xe6\x92|!\x80\xeb\x96y\x1d\xa8\x98\x94\xf4g\xfb\x9eU'\xef\xdb\xf7?\x9d\\\x9c\xfc\xf2\xfa\xf4\xec\xf5\xbb\xffl9|\x89y\x00w#?\xe3\x1c\xae\xf4\xa9\xbb\x94{\xcd\xae\x11\xaf\xac\xc7E\n\xb1L\xed}\xcd\xeb\xc7\x13\xd8\xc3\xef\xde\xbf<\xe9;\xab\xdd\xe3\x7f\xd7\xfd\xdbB\xa2\x93\xfeT5\xe9IY\x93\x8em\xdbkV\x9bg\xf8-$a\x85\xc5w\x95\xb4H\xd4\xa9b\xe0\x05Qe\xd4\xbbm\xe6Q\xd5s\xcd\xe9\x0b<\xf8\xb0\x19b\x8f\xe1w\xf0\xc4\xde\xfcH\xbaBl\xb6\xf4O\xf8\x9bEt\xedA\xea\xadD\xd7\xa5\x9b'\xd4\xd6W\xb9\x17\xa8\xfb\xe1 \x86\xa7\xae\xfa-8)\xa5\xdb\xbb\xbb{ \x97\xde\xdd\xdd\xad\x0b\xb4\x89\xa1x\xb6_\x1b\xb4\xdau91\x85\xccy\xc7\x81\xbfV\xb6\x1b\x86\x17&\xd60Z$\xe6} \xa8\x89H\xa1\xb7\xb4\xb3\xe7\x82^i*\x89U\xc7FV\xbfu\xa0*x\x0fN \x11\x15\x0f\x81=.N\xde\xfd4%N\x9cp?\x87^ \xe8\xe4\xe7\x93\x1f>\x1c\x1d\xff\xf5\xe2\xf5\xbb7\xaf\xdf\x9d\\\x9c\x9e\xfd\xfa\xe6\xe4tJ\xb6&\xd5F\xd4FJ\x8b\x0b\x9b\xdfE\xa4\xd8\x1b\x13M\xfa\x8e\x8a\x0dL\xb5\x80v\xb9j\xdd0\\?Z\xbc.>\x9d\xcb@\x01\x1b\x88\xf1\xda\xba@\xa1\xc2\x14\xa2U{\xe0k\xd7\xde#\xf0\xe9\xd1y#+\xf8\x9c\x0e\x9e/n\xf1\xbd\xa4\x1f\xd4\xba6\xee\xcd\xf3 \x06\x15\xd8%\xb8\xd8b\xb3\xf8\x1c\xb8\x0d\xbf~G\xda\x8f\x1d\\\x83\xf5n_k\x1e\xbd9@?(p\x97C\xb2\x1e\x0cH2\xae\x07Sq}`\xc3\xf2!\xf8b\xca\xa4\x1f\xa2\x96\xb1\xd3O\x0f\xbfJ\xfa\x91*JTV\x9dT\xa8W\x1f\xdc.\xd4\xbd\xa2\x8a6mM\xfa\xc4(#\x06w\xcd\xdd5l\xfa~\xa5TOW\xfd\xa0\xc57\x16\xd0\xfaZKW\xf5\xa5\xdb\xaf\xbeH\x8a\xcf;\x98Z\xd2\xca\xd8\xb6\xe7\x96k\x9c\x0d\xc8V\xc3\xc7[\x0cV&\x80\xf8\x90\x05.\xcd\xf5\xc1[[|.\x98\xf5\x8d\xa7\x0em\xd7]Y\xdc\x96\x13\xbdj(o\xf1vG\x88\xc5\xe3]\xd4\xb9\xa55r\xc4O\"\xf3A\xc6\x84\xa3\xb4\x8c~\x90Q\xa9\xa4\xd4\xd0\xb1I5\x94\x17|_\x07\xca\xb5\x8c8\xac\x1f?V\x13p+z\xa2\xf3*\xdc\xa2d\xd7PV\xa7\x96\x8bs\xa5dW\xf7\x89\x99*U\xbd\xba#\x80P\xb5\xa5\x9e\xeeU|h\xee=y\\'P\xe68\xe5\x13\xcb\xfa\x1a>9}Y\xdf\xbe\xa2w&\xf5\xea\x96\xaa;\xf5v\xacK%\xfbzO\x05Z\xaa9\xce\x14Xd\x17\xbb\xd2\x07\xc7T\x7f`\xb7\xf2\x97\xe8\xca/\x15H\xcb\xe5rS:\x7fU\xd1 M\xdf\x15\x18u\xc8\xc8\x01 \xc5\xbe\x96:\x89xX\xe8\xc6\x02\x85\xbb\x0b\xe9\x94Z\xaa\xf7(\x12^*\x97Wbf\xd5c\x0d(*B\xf5\xa9\xa2\xb5_]\x82\x17\xcd\xb1\xbbB\xe9$\x8fGi\x96\xe4^\xaf\xebALM\xcb\x88\xf3eq\xf7\xeb\x89\xad\x9c\x06\x19;\xbb\x89YA\xf4\xcb\xbc@i\xc6\xd4\x92\x8d\xd0\x8f\xcd\x8c\xca%l-_\x0e\xdb\x0f4\xf3\x96\xd2\xffZ-?f\x91\x1fD\x8b\xb2\xedH&h\xd6\x80\x03#<\xff\xa3\xf4\xb9\xa5\x15\xeb\xb6&\xb5\xfcW<\xf1\x98\xbc-\xa8dk\xc1\x9f\x18!d(\n\xb9\xa0\xc6|\xb5|\xb5>j\xa9\x80,\xdf'r\xb1\x16C\x9e)\xafOJi \xef\xc71\x0d\xc3K\xea}N\xeb\x1f\xa2ah4\xe3\xe7 \x0c?I\xa4\x0c\xddi\xac\x0c\xabZD[\xe46\xab%z\xbd\xb3\x1c\xed\xe9\xc5\xf66\xbaV\xb2\xd6\x85b'\xdd\xe9\xd0\xb8\xf3\xe9\xaf\x83G\x14\xe6U\xe3\xaa\x14}\n+\x11{!\xcf\xf61\x1ce\xe8g\x0eJ\x82\x0b\x96\xc9\xe5%\xbdl\xb5|\xc6o\xf5\xbeS\x7f\x14v\xd9r\xb7X\x89\n\xc1\xfa\xd8x\x1f\x07)\x04\xbe*f\xb7\xe5lv\xbd\x96\xb6-\xcb!\xd08\xa8B\x08I\xca\xd0F\x13\xfafD\x86%1LV\x97\x1ay\x1f\xf6\xf2eF6\xe8\xf8\x87\x9d\xe9\xb3tl\xb2\xeb\xb6N\x05\xd2\xb8!\x91\x1e\x06b\x1eD\x99-\xa0\x07\xee\xaa^?E\xd4Vl\xa5V\x9b\x83#f\xed\xda>o=\x0e\xc6 \x97\xa4\x91K\x07u\x1c\x86\xee=7o\xd9\xf9\xa0\x96]\xadC#\xa7\n\xdd\xf0\xc1(Y/`2\ne\xaa\xc2\xc2\x83\x016\xbeV\xba\xb2\xc9bo\xed\x808\xa2\xd2\xeb;\x0fu\xdbZ\x0dn\xb9\x1ao\xb5\xf8\x8aq\xd6\xe3f\xa7IZ4_\x83\x12\x83 \x8a\xb8@|.\x96\xe1v,\x87\xa0\xc7\n\x08\xf4\xa4\x07\xe5<\x0f\x86\x15\xc1~\xa1\xaan\xce4\x90\x0543&\xdc\xb5 \x03\xd7\xca\xe5\xbd'\x90\xb78\xecQ\xcf\x18\xa4\xa1flp0H0,b\x08\xe6\xcd\x81\x07a|\x95|\x02i8\xdc\"x\xe3\x93\xb7\x1f\xce~m\xbf>\xb2,hI\x85\xcc\x11\x15\xdeD/\x92*\x81\xbe\x0cB\xdf\xa0\xd2\xb1(\xde\xc8z\xec\x1f\xd2\x8a\x187\xb3\x15\xb1\x9f\xa5\x03\xbd>\xbfi\xf4+\xa2E\xf0\x96ov\\\x02d\x8dmc\x97\xdcII\xbf\x87q\x8c\x0f\x1e\x90\xad\xac\x8d\xa7\xecs\x87\xd0\xc1\x92\xee\x0c\xdb\xef4\xf4S\xb9\xb8, \xbam\xe2\xa0mw\x07\x1d\x01\x05\x08\xe8w\x07\xd1\x9a\x7ff\xff\x99\xd3\xc4g\xbe\xe6\xa9A\x05\x00\xadU\x9a\x93e-!E )\xac\xd6\xf1*\xda\x82a\xd9\xb6\x08\xe8i51\xbf\x05\x1c\xd3W\xba\xa5\xd8\xa2&\xe1\xf9\xf6\x14r%\xdb&\xe3h\x95\x03\xe1\x92\x16\\\xb8e\x93\xb4\x84:p\x99\x8dE\xec\xb3\xe5/V4\xfd\xac\x10U\x9f\xed\xben3\xa7\x04\x1eVuM\xcc\xa3%\xec\x07\xf8\xdb-C \xc4v\xfc\x8e\xf9\xc1\xd6O5~N6 \xd1,9o\x0d`c\xf5\x14\x87\x8dKU\xd2\xb2\xf9\xd0\x18\xe3j=\xf2\xf4\x99\xb3Q\x83\x8c\x93\xa5w\xabL=\xfb\x8d\xa4AM\xca\xc6>\xa5\x81t3[6\x8f\xe8\xe8\x0c\x8d\x1c\x19\xa8\xa1\x0d\xa1VC\xf0 \\\xb5\xf2rpl\xac\xb6\x82\xa5~\xba9K=\x90\x1f\xc2j\xd5B\x8f\xfd\xcdj\x15g\xbe\x1d\x89\x96.w\xbf\x02\xdf\xdb{\x0f\x13\x83\x1d\xeb\xb5n\x80`7;\xd4_\xab\x0f\xf3\x81\xd1H\xaa_X\xf7\xaf~]Q\xbd\xef{\xe5\xceM\xa1\x9e\xe8T\x1b9\xd9\x86\x84\x95\xdeCyP\x011\xc7@I\xaa\x9f\xaa\xa4b\x1f\xe4\xd9\xf0z\xfe\x8e\x89\x0dJ\x93\x9b>\xfb\xb2P\x8e\xc1\xdayH\xe6ME\x80\xcc\xb0\x14\xab\xc2\x0f\xcb\xfb\x11M\xc7\x97\xce\xa8\x0f\xac\xa7\xe1\x97/\xf6\x83\xee\x10\x1f\xa3\xf2;\xd5\xd9jO\xad\\;\x99M\x94 \xb6\x1b\x95>SPk z\x0f\xd0a\xfdI{\xe2\xb8\xc8\xf4\x97 0\xc2\xde\xa6\xa2\xbb\x16\x16i\x08\xbc\xcc\xd6\xa4m1\x17D\xc3\x81\x0c\xd2\x9b\x83\x11\xb8N\x9dJ\xd7[jF\xab\xf7\x04\xc1@\xd5o\xd3\xbeX+\xc7&\x9dW\x11\x10\xe2\xd8\xe6\x1d\x88\xc0\xd5#X\xe5\x03\xeeW\x9f\x1cJ\x17\x98\xb4Ji~\x94\xeb\x1b\xbc\xa6td\xbb\x9e=\xa6\xd9Z\x07\xfe7\xfb]\xe1r\xa1\xb0\xbdGq\x8bw(\xeb\xf6\x80\xf8h\xe3t\xc9\xf3\xb0$K\x8b\xad\x13\xc3\xc4\xa0\xb9\xa25\xf3\xa1\x8c\x82\xacg\xb5\"\n?8 \xd2\x8c\x03\xda\xe5\xbb\xe1\x90x\xb0\xac\xb6|\xf1E\xd1\xa3!\x99\x03\x9f\xde\xbe{\x86$&\x87\x9a7\xeb$e\x01\x91\xd5\xdb\x1aI\x9d\x19\xb8(ab\x17\x81\x95 \xb6\xd5\xc57\x9b\xb4m0$\xb4\x10\xea{\xe2E\xcb$\xe6Cc\xe5\x1e`\xa6=-$\x909\xbb=\xd5O*|Y\x0f)My,5\xd0f\x1fb \xe1,\xect\x93\xb5\x08\xc6m \xcc\xccVii\x11\xb5]dHGo\x0f\x1e\x90\x89r\xa4+\x1d\xc6\x14\x85\x93\xd9\x8e\x85p6\x88\xb1\x03E\xb2\x08\xfc#\n\x88sF~T\xb9\x84\x13\x19\x132%;\xcfI^\xf1\xee\x96\xb7\xfb\xc5^\x1bf\xd9v\xb2\x89\xbbtH\x1c=\xe5\xa6'\xc2\x94\x1c\x92T\xea\xd8H\x8dE\xb9\x1c\xa6$\xbd\x05e\x85\xf8\xbf\xc1\x96#\xbakn\xa1y\xad\xaf\x87\x87\xda\x13A\xdfe*\xb0\xf1\x0f2d\x9b\x1bV\xee?d[,8\xd3#\xda\xe3O\xa8%\x809\xbc(\xf4\x02\xbe:\n\x91\xe0\x90\x845\x19\x81D \xe07\x0b\xc9(\xee\x03p\xaa\xc0\xd4\xe6\xa8\xa0\x8a\xb0@\x15\xd9P\xb7E\xe2\x95\xd0@\x15I\x15\xef}\xac\xcb\x06\\\x18\xe8\xa1\xec#o\xbf2\xc2\x86L\nO\xc2B\xe9Ut\xbf\x1fv\xb24\xe8V\x18\xaa).iEU\xd1m\xc8g\xbb,\xb7\x1d\xc5\xd9\xa4\xd7s\xe2.]\x10\x95\x0f0\xf2URb\xacMP\x9a\xd9\xa4\xc8\x1d\xca\xac\x1a5U%\xa16{Y\xf1 r\xaah\x88\xbb@\xd7OS\x92\x8d\xb9\xdb\xd6Ou\x1a\xbb\xa5\xd9d\x03\x896\xef'\xd1&-\xb2\xba\xd6\x90\xac\x9a\x18\xc4\xc4\xdd\xc5\xfc\x95:1fJ\xcd{E\xdbT\x8bm\xda\xddp8\x0d\xc5\xf0\xfd\x1cdK\xe9]@\x1c\x01!\xca\xa2\x91\xdeR/\xb4\xe2\xfe\x9c+\x1d\xe3-c\x1b\xd8\xd9Y\xf7\x9fy\xb9\xfb>i\x8az\xda0\x08\xeb\xc9\xcb\x14\xc62\xb2\x11\xee\xddZ\xdc\xb7q]4P\x95\x14\x16+|\xd1F2\xe4c\x85\xf4T\xa7[VS\xeb\x95\xafx\xba\xaf\xb8\xd0iA\x06N?_\xc9<\x88h\x18v}\xd9\xec\x05\xca\xf5\xea\xa7\xd5\xf9\xec\xad\xdb\xdf.*\xd5\xdaA\xcc\xd0\x0eb\xa8v\x10+\xb5\x83\x9em\xc8\x16\x0f\xfbI\xb2h\x96Qo\xf9\x91\xcdos\xa2.X\xf6!\xbf\x0c\x03\xafp\x94f\xe9\xb9\xe6\xf2#\xcd\xe5Ov\xda\x18w\x194\xa7w\xedn\xa4\x14\x99\x0e\x0e\x80=\xd3\xaf\xe4\x8f\xaf@I\x8b\xb7\x81\x0c\x04\xd7\xcbv\xc7g\xc8\x98\xd8\x06D\x05\xd5\xb3\x8d\x07||\xc6\xce\xfb|W\xcdl\xdf\x8d\x7f;\xe1s\xf3~\x10\xcc!*)\xe3B9\x86[\xdcQ\x15\xa8\xae\xa6\xae\xa6l+j\xa9\xacPbS\xf9\xfa\xb5\xaf@\xaa1\xb0\x1b\x8fQ/\xcc\x8d!L\xedc\x02\x96\xf0\xb4\xdf\xa6\xb2\x93\x19\x88\xcd\xaa\xc56R*X\xdd\xc9\x96a\x82\xd7l\x1d9\xcd\xb2no\x17\xc9_\xef\xde\n\x94\xb1<\xbdY]rp\xc7*\x7f\x8d\x057\\ys\x9dD\x8c\xdc\x98\xc9U\xed\x00\xba{\xb23\xd9\xd9\xc3{\x95\xfc\xb3Z*\xa3s\xf2\xa4:\xed\xe0W\xf3\x7f\xffo\x9dy\xeb8\xcc*\x04\x0c\xa8\xe6\xcd\x92s\xd8=3~^\xc3|\xe0\xb3\x1dkmy\x01X\x0f\x0cp\xab\x91i\xb1\xb2\x95V\xb2\xcf\x1b\x9d\x90F4\x9b\x19\xc7\xf2\x0e%;0_\x12CR\\Y\x19\xc1\x12\xda\xf6?\x18/\xb53^\x86^\x0e\xb7\x9a9\xed\x0c\xa5\xa9md\x1a\xdf\xba\\\xda\xddvG\xb8\xaa\x0e\xd2\xbf\xca\x04\xd7\x16\xdc\xd5r\xda\xe3\x96\xb4\x08\x02m\xbbS\xd6(\xc5\xd57@-\x8e\xd3\xbf\x891\x17\x1eb\xe4I\xdd3\xba\x0e1\xf2\x14\xb1\xe6*\xcd\xad\xf6'\x0d\x07\xa79x\xa4\xaa~\xbai\xd9\xacd#\xd5S\xabb\x1e_\xfc.6E\xd8D\x12p>%L9\x8f\x0d~g\x10\xef\x97\xaa\x1a\x87:_\x90\xaag\xfc4\xa3Y\xe0I\x1e\xca\x10\x0f\xe5);6\xa3\x19\x9b\xf2\xd0\xbc\xb4NP\xea\xe5\xb4\xd5k{\xd3\xdd\xa9\xe0\xe2\xcb6)\xe5\x8a\xb4\xe3\xb4V\x8b\xa4\xea!\xa8v\xac6EN\xfd*M;*5\x0c2\xfaUX\x1f\xa8\xb6\xfa}\xa6\xa9\xa8\xda\xccW\xc1J\xed\xcfV0\xad\xe6\xd9\xb2\x8a\nP7,\x0d \xc03\xaa7\x18\x12>\xa6\xbe\xff\x81\xf30\x88\x16g\xdc\x0dk\x18\xe1^\x1c \xef\xee>2\x10\xbfD\xfa&\x14o#@\x8a\xb5\xcf\x9a\xe7\x0d\xa9\xc5\xb8o\xe1Q@\x15\xc6eD\xd3|p.\x0eH\xb6L\xf8\x15\xacjA\xd8I\xfd_\xe7\x98F\x11\xcf\x88\xc0<\x84\x12/\xa4iJhJh\xf1%\x07\xc1\xee\xea\xd6\xb8\xd0\xb5\xca\xca%/\xce\x83\xea\x92\xa8\xce\xa1\xa6\x9bM\xf3\x14X\xd3\xac\xdb\xe6G\x9b\xbb\xd4\x10\xfb\xb0R\x9dB5Z\x81\xaa\x8e\xe9-\xf2\x97z7\xc6A\xfa:\xaa`\x17\xe0\xdc\xea\xb5\xe3\xb2\x19\xbcE\xd5k\xb2\xf6\x9en\xd8\x1c\xa3\xea\xba\xc3w\xbc-\xb5\x0b\xa1\xceU\xb5a{\xcc\xea\xdd\xa6\x1e\n\xde\xa6S\x96}\xab\xf6\xe8\xaa-m)1\x88\xc9a\x9b\xa8\x81\xdf\x07j\xb0\x9c\xc5\xfb\xb6\xb3\x189\x8a{\xac\x1a\xe4\x0e\xb5f\x87\xfa\x8e\xfbu\xa5\xc5[\xdb\xad\xfa|%\xf5\n\xab\x83jbbjb\xe2j\xa3\xbb\xcd-\xad\xbeb\xa8\xbc\xa0\x08\xfcc@\x1e\xc9\xf6v\x93\xf8\xaa6\x91\xa2\x9d\xdd\xd4\xf0R\x0b\xec\x1d\x02\xec\xd9\x88\xad\xe2\xecfJ B\xa5\xf1\xb9m\xe2\x10D\x0bW\xfa!\xa8\x93 m\x14|*\xfb\xc9\xaf\"\x96\xbc\xe4^\x0e\x12\x0e\xe55\x89\xaf@HfSb\xd06\x0b\xe38a\x1e\xf5\x96\xacP\xe5\x967P\xdcEn1\x9b\xf2\xc0\x9aT\xb7FX\x1d\xca0^\xceo\xd7{\xde\xd6h$\xc6!\x17\xbd\x1f\x8d~\xbb\xdecNm\xaf\xd5\xce\x02\xab\x8eW\xf3\xf0\xef\xaf\xc4^t\xdb\x1a\x04\xba\xadQ-\xda\xea(\x930\xce\xa3\xea\xd8\xd6j/qK\x8d\xda\xa0\xf7\x82R&\x15b\x03\x0f\x1b\xc0Q4\xea\x14\xb8\xc0\x01\xe7\x19J\xd0\xba\x07\xd1]j\x99\x99\x91Y]k\x86\x07\x0eP.\x06\x86\xf39\xe1\xcfI3\x80\x1d\x89\xea\x9b\xb4\x12\xb5{G\x1a\x03e\xcf }\x0e\xbfh\xb5t\x80\x96~N\"2\"\x01\xf9\x9e\xec<\x1f\x80\xbc\x8bU\xaf\x91\xa2\xd1\x08-\x16\x90\x11\x89T1@\x04\xd5b\x01ZL\xef\xfe\xe89\xc9G\xa3\xe7v^\x1dB\x02\xb71\x8dHK\x1b\xad\xb0\xac$R\x15\xa5\xff\xa9 a\xae\xb3j\x0b\x83\xf4(\xf2XZ\xa5\xc8m\xa7\xacm\x89$\xc9lr\xbe\x89\x96W\xdb\xdc\xf5gIk\xea\n\x06\xea\xb5\x88\x08\xda8\x07i\xe8\x88\xec\x0e\xbcS\x05\xd1\x01*\xf1v\xa6x\x1c\xb1\xeb\xec4\xb8\x0c\x83h\xf1\xdcJ\xa7\x93\xda\xc5X\xa6\x14Z\x9e\x14\xd6q\x12\xe9\x0e\x86d_2A\xe3H\xab)>x@j\xf8\xcc\x80\x90\x11\x0d[\xbeJ\xcaE\\\xc7 \x16c-\xfd\xb4G\xe0\xb6;\xd3\x94\x04\x981,=\x17\x8d\x9e:A\xe1U\x0fx\x1c\xab\x9d[\xcedVWa\xba\x9b\xa8\xe2vD\x81\xc0\xd0\xb7\x15q\xdc\xcb\x85\x8aEj\xfa\x08'\x07\xf1\x1bL\x19h\xb1:x\x16\xef\xcb\xfafqJh\xf3\xb0\x15\x83\xd7\xb5\xd7 (\x02\x07)\xd8\xce\x04\xd1B\x85M\xb4\xb8\xa0k\x9b_Qfv\xdb6\xf2\xf1<\xcc\xd3%\xb4\x82)-\xf4T\xaa\xa1\xf3\x86\x04Gv%+\xbb!e0\xc9`\x08\x85A\x17m\xee\xd6<\x91}%W\xcb d\xc4\xadKT\x8cX\x82 \x97\xe1\xe4E\xa5n-b\xe1 \xa1\x81\xc5Qd\xce\xf8\xf9\x90,\xc7\xcaC\xd7\x99\x9a\x03\x97U\xa6C:\xb53\x87j\xd8\x18;\x1c\x17\xc7v.\xde\xa6\xa9\xd1\x18&lu\x18$Du\x81\x18\x19\xf5\x01h\xde\x19\x96M\x06n\xb1\xa2\xaa!\xf8\xc5qv\xc5\x8f\x92\x05\xf0\xb5\"\xa7\xe2dx\xad\x1c\xefW\x1b|\xc1\"z\x192\x7f*0d5\xa7:\xc4X\xdc\x95\x9f_\xbf{\xf9\xfe\xe7\x8b\x1f\x8f\xde\xbd|s2%\xc1\xd8\xa3\xd1\xa7\x94\xbd|\xff\x96\x1c\x92\xab \xf2\xf9\x15\xc1\xca\xa5,\xfb\xb1Vy\xbb\xe4\xa81\xe1bQT\xc7\xa6\xf1\x85\x13\xdd\xb1\xce\xaa\xd5\x10\x88Sb\xab\xb5\xd6 mV\xdar\xfc\x96U\xb7U\x9a%4\xfeAJ\x1faQ\xf4\x13V\xeb\xdb\x0drH\xf8X\x06\xf0W\xb1\x89\x96\xa0Z-\x0e@\xa8N\x124r\x99\xb1\x81\x16\xd7v5\xe8X\x892o\xdb\"%\n\xbd\xaf&\xadx\x14d<9\xf5\x12\x1e\xca\x88\xe8]\xd3\xaaQf;\x94x\x98\xeb\xb9r\xad\"\x8e\x9b\xbeV\xdb\xda$<\x8a\xc1\x97U\x0c\x89\x93B#\x1dD\x8d\xa2\x8aN\xcc\x11\xe9)\xd3(\x17T\x1b\xd1$0f\x0c\x86\x06\x02\x05\xb4\xc6\xeei\xb7\xcfI\xc7U\"\xce\xf5\xedr\x81\x1eF7\xf18a!\xa3)so+\\(\xde,$\xd7\x12RoEr\xf5S\xc1.\xc4`?K\xe4\x067\x1d\x86\x0eY\x91q\x88\x8c\x03\xc4\xc5\x8a\xe9\x82\xfd\xf2~>O\x99\x0c\xd82\xf6\xb5\xc6\x82\xfe\xa1m4\xe4:z\xc3\xe6\x88\x00\xf5FW\xf5\xeb\x06U\x9d\xf1\xaaX\xf0+\xc1\x82\xceC+;\xbfm\xa9\xf1O\xd5_\xb7\x9a\x89\x92\xf8\xdd\xaf3\xaa\xea\x9acb!~\x1b\xd7\"\xed\x81\x16\xf6\x9e\xe0\x91\x16&\x8f\xeb\xf5\x84\n\xbe\xde\x1e\x0f\xa7\x97q\xbe\xc9\x10B\xd0q\x10\xfd7\x83qi\x8e\xef\xcb\xf7ou\xfc\x8d)I\xda OVqvcT\x9b\xb7\x02\x0b<\xf3!\xcc\x17A\xf4c~)\xb8\xdf~\xc0\x9f\xb2 L\xc5\xd9\xde\x05~\xb2\n\xb2\x8c%S\xf0\x9bg\x05\xfd\x11t\x88\x8a&\x87m\xb0\x05\xef\xe8\x95P\xd5\xf5\xf6/\xe0\xbc\x1e\xd7\x99\xa6\x00g\xb1\xa8e-\xa9\xb5\xf7\xb4\x9e\x9eV\xd4\xc8'\x8f\x9e\xd6\xd5\xc8\x15\x17\xb6[\xff\xbe\xd7-\x03\x01\x8e\xe0\x94\x85r\x08_G\x82\xd9\xa5\xf8\x98+\xd9H>N\x80\x16eE\xa9\xea\xc0c\xf1\xb9\xcd/v\xca\x7f\xb4\xbc\x97\x8e\x0b\xa2\xaa\xc3&\x92\x8eK\xa2\xce\x85X\xe3\xbd\x0c\xad\xea\x02)+\x1dP\xa9\x1f \x94S\x17D\xddu\x04\x94\xa4\xa8\xa2\xb0.F\x9da\xc6\xad=:\xb6\xd1w\"\x9e\x05\xf3\x9b\xa30\xc4\xbeU\xed(*\xf8B\x98\xfbv\xc9W\xbb\xe5Aa^Pk'\xa8Q\x94\x94Ldx\x99D\x8c\x14\x0c-\xd5\xca\x86\x8e\xef\xd5\x06\xc1\xab\xad\x83z\xc5\xb7\xb2A\xc0:\xdf\xf1\x9d\x8d\xcd\x12Z)l\x9b\x81\xc1&\x0d\xae\xf8\xa8n\xfb\x18b\xa6`W\x18hl\x11\xed\xca\xba\xa1\xc6]y\xed\xcd\xae\xf3\x82,\xc5>7\xb0.\xcc&\xcfR.\xbf\x12\x91%\xee\xdc\x14)\xa4C\x12\x0f\x86$\xa8\xf2\xee\xf3\xba\xe1\x15\x14\xbf\xe3\x01\xd6\x90\x05*]\xea\xddz\xdc\xa7@\x1dl{\xa8\x18\x8f\xb6h)\x94\xd78\xdap[*\xa8%\x96\x8d\x98KO\xe6\x85\x90\xe0\xc1\x03\xe2\xa4\xfa\x80\x01\x85/M\xb9\x8a\xac-\xd71\x8f-\xc8W\x8cZ\xf3\xe8l\xce\xeb\x82e\x928N\xa7$'\x87=N\x00\xcd3\x16tt\xd16u}\xff\x91F\x8b\xd6\xa0,`\xdb1\xce\xd8u\xa6d8vP\xb8\xb3\x1d\xfby\x1c\x06\x1e\xcd\xac\xd7\xb5 \x84\xaa?\xe3\n\xcb\x9dI\xb7\xa6C\x92\xc8\xd3\xca\xff\x00\xbb\xcd9\x89|@\xaaI\xe6\xd8\xb9=-rK\xcc\x16\xb6\x9e\xb9-\xbc\xa1\xf8VC\xed\xcf|X\xe4OA\x03\xa5\xe9\xf7\x95\xe0\xcc\x1e\xe9\xc2\x07\xc4\x98$\xb9\x12*\x84\x8dX4H\xb2mh\xe5-\xb1`\x9dv\xd4-k\"\xe6\x174mz\x86\x05\x95\xf3M#o\xc9!\xdep\xd7tKH\xb9,\xed\xb0\xd2\xb7\xc1\x9c{y\xda^iP\x02v\xd5\x99k\x7f \xb0\x86\x8f2\xd7\xe6\x91\xb0]$\x90\x8fa\xe2\x0b+\x80\xe2\xeazH\xf21\x8b\xfcf\x06>\xf9:XC\x9f\xd8=\xa8\x07\x00\x82.!b\x98\x04P\xb723\xf5\xd1\xaf\x8cpu\x14\x07\xe4\x90\xec\x10A\x04g\xfc\x14\xd40\xdcA\xe7~\x0eA\xf2\xee\x85<\xd2h\x02\x1f\xdfPa\x15\xf1]p\x06\x12e)\xec\xe8P\xedh\xb7>\xc6C=\xea\xaau\xf6\xe5\xe8)\x0d\xa7z\xf9\xd0,/^\xcd\x99R\xef\xd5\xae\x87\x9bt]\xf0\xbb\x1e\xd9&-\xee+c\x13\xadV\x90)\xde\x9bX\x0c\x06\xe03W\xb94\x8b\xf5\xf0p\xbb\x03#\xad\xd2\x14\x8f=\x1e\x864N\x99%`k_\xf4\xe6\x8bs\x83L\x89\xd7\x81\xe6\x04\x9c'\xd0W\xcfu\x8a\x90\xf3\xa9\xf5\xb8\xear\xb52\xd4\n\xcb]\xe7V\xf7icX\xbagbQ\x90CIL\x00\xf2\x801!\xd3\xe2\xd7\xf7\x05\x8c+\x01X\xe4\x0f\x15\xa2\x03\x08\xf0Zi\x94\xd5\x99,\xf2\xc1\xd4\x14?\xd9d\xba\x9c{\xc7[\xd2\x84z\x19K\x1ci\x19\xce[\x8e=^\x14\x16\xcb\xa4R4!\xa3\xa2\xb8\x18\x1a\x8c\xeb!=\x84\xb0D\x1d\x1b\xc8)\xd3\x86\xc8\xf4Q\x81\x1eN\xf6\xa5E\xd4\xb9\xc1f\x81;8\xef\xdc\x86DI\x1d\xde\xd2l9^\x05\x91[\x0e{\xc7G\xf2\xaa\x93\x03=\xad\x94L\xcd\xca\xe4\xf4\xb6\xa9\x95\x89\x035\x1a\xb3\xebL\x94\x7f\xf0\x80P\xf2=i\x0d\xc7C\x0c|\xdd\xe2\xa0\x8d\xa86Ri\xff\x92Z\x01\xed\x9aJZ9\x15\xb4\xd6i\xc7xx\x1a\xd0f7FTo\xc1\xe9\x87\xd7\xa7\x87\xf3\x0d\x11\xa0~\xe6%\"\x0c\xe1L\x15\xe8\x9aK\\=\x04\xc7Eb\xc1\x1f\x85!\xd4\x96\xba\x10/\xe8{\xc0 n$\xb8\x0c\xf9\x959\x00\xcb\x99q=U\x91\xa7+\x82\x8d:\xd7\x08\xb6\x91-\x8a\x1a5\xe1\xc2{b\x1d\xfeN\xb1>.\xc5\x93\xb3\xbc\x11\x13T$\x17\xdcKbWB\x00\xe1\xfdx\x1e$\xa9t\x91_(\"\x18I\x95\x82\x9a\xdb)\x12\xb1\xdb{n\xff\xa0\xdd\x16\xca\xd4\xa0+\xf5\x1a+\xea\x86\x8d\x82\xb2\xad\xa5\xeaCuH\xff\xd4\xfc\xd5\xdb\xb3G\xc5`-\x01\x9cl\x18\x9f\xed<'\x91\xb5'{\x92\x13,\x88\xbf6\x1cJ\xc1i\xed6\x89\x80\x1bQ\xa4\x90Fr$ /\x94\xea$%\xdf\x9b\x86b\xf6\xad\x16\x81\x96)\"\xd3\xd4\x8f\\\xceS\x92\x91\x11\x12\xa6\x8a\x90FHi\xfd\x04\x851b\x05\xb8\x91\"\x07\x8c\xbb\xd1\xe0\x9b\x9a\x7f\xec\xef\xedX\x8c\xb0\x8be(\xd5\x9c,\xfc\xfa\x96b{\xb6\"\xb0\x01WVe\x11$%n&\x13\x137\x1a\x14\xfaR\xc6:\x13\xb8\xc2\xf1$\xf1\x98*\xbb\xb6C\x88f#\x93D\xb1)\xd9\xda\x92\xf1mhR(\xda\x7f\xe0i\xa0\xb9\xb4\xad-w\xf2\x84< V 1\x84\x0d\x15\x8d;\x0f\xdb\xa4c\xd8\xac\x17~\x80F\x1e< {\xe0\xe9\xa6\xc9\xdb\xdc\xa1}\xfd\xda\xa1\xb9^\x97\x899\x19W\xec+\xe0\xf2\x8fL\x8b\xe3e0\xf6\xd9\x9c\xe6a\xf6S\xc0\xaeD\xa6$;Pd\xb6\xe5nI\x17\x83\x16_Qc0\xba9\xac\xder\xaa\xd4)\xeak \x84:\x118D\xaf\xa4W\x95\x9c\xa5v{\x13\xe0\x1d]\xb1\xfb\x9dwg\x99e\xf1\xf4\xe1\xc3\xab\xab\xab\xf1\xd5\xde\x98'\x8b\x87\x93g\xcf\x9e=\xbc\x0e\x83\xe8\xb3\xd3\x94\x90!\xf0\xbf\xbc}#\xca\xec?\x8c\xe8\x8a\xa51\xf5\x98\xd3\x94\xa05\xf1\x12\xf5<\x16e?\xb2`\xb1\xcc\xa6\xc4\x91\xaf\xa3%\xbc#>\x9a\xa8\xe7\xe5\xab<\x04O\xd6;H\xb6\xef\x07Y\xb0\xb6d\x86\xc1\"\x12s\xff\x03MY\x18DL|O\xa7\x8d.U\"\xf6\xd10\xe4W\x1f\x19O|\x96@\x99\xf2\x15\x85\x8e\x97\xf4\x92e\x81\x87\xb7b\x15\x87A\x96\xfb\x966&\xf42\xf0^\xf1d%>\x04/\xa39OV\xd8wR\x0fn\x07\xb1Z\xb2, .\xf3\x8cI7\x88N\xe5\x1d\xabJ\xe7\x8b\xa5g\xc2\x8bw\x0c>\xcf\xf8G\x06\xc6\x92\x02\xba|\xc3`\x7f\x0fVy\xb6D\xdb)\xc6\xfcU\xc2\xfe\x91\xb3\xc8\xbb\x99\x12\xa7\xf2\x8e\xd4%\xf2?$|\x1e\x84LA\xab7\x0b\xac\x98\xcf\xd3e0\xcf\x14\xb4x\x1f\xa5\"\x01+p\xc9\xaf\xf1V\xb2E\x10\xe19\x01M\xf1\x8c\x1b4\xd9\xa3\xa1\xf7\x16\x0e`G\xffD\x1a\xe2\xd1\xb8\xd8\x0f\x1e\x8d\xed\x9b\xc1\x0b\x83\x18\xffN\x18\xc4\x1f\xa8\x18tG\xfc\x1c\xc54[Z\xca\x7f\xcca,\x01,\xc9\xd1\x91\xd4\xb5}\x8a\x02\xc1w;\x95w\x0c\x9e\x87\xb3#\x1b?\x98\xcf\xf3\x94\x1ds\xe9\xabsJ\x9cZ\n\xd2\x1b?H$go\xa9\x11\xbc\x9eZ\xf2\xd6\x81m |\xbe\n\"Z\xc1\xef:\xa9\x0d\xbd\xfb\xb9\xa5:|\\}\xbca\xcc_0\xb5\xb7\xf5O\xe4[,dkj\xed\xb8\xd4[\xfb\x81z\x9f\x17 \xcf#_\xd4\x05I\xa3\xcb\"\x0d\xab4\xc2'U\xd0L\x91m\xda\x04\x9b\x9bD4\xfc\xc8R\x9e'\x1eK?\xb2\x7f\xe4A\xc2\xe0\xa3\xb6<\xe4\xe3\xf3 \x0c\xd1\x0f\x88\x8c\xf71\xf5\x02\xf0k#\xdeF\\\xbeZjQ\xa8\x08 -\xa8H\xeew\xdb\xe72\x96|d\xa9\xacB\xfe\xb6V\xa1q\x99\xf1\x86\xc1\x86\x9c\xfb\xc7\x02\x13\x08P\xf12\x02\xbc`\x035\xba\x0b\xc0-\xfd\xe5^\x9e\x8a\x99\xc5\xfb\xc2\xa3\xec\x15]\x05!T\xc5\xa3l4\x877\xb4\xa2(;\x05]\n \x98\x06\xbf\xa3\x03\xa7\xc0\x8e\xfc\xff\xce\xd3\xcc\x04\x1eQH\xb2\x95\xc9\x12\x96y\xcb\xa2\x80|\xb5\x02\xdf\x84eC\xc4\x8b\x05\xf0'\x9a\x04\x12U\x00\xe8Z\xbeZ\x80\x7f\xd6g!\xc0^\xd9\x0eC\xa9\xae\x83\x0fg\xc2Wx\x06\xbe\xc3\xe7\xf8\x0e_L\xf0\xe4]<9\xbc\x89\x97\x8a\xfe\x82\xdf\xa3\x08'\xbe \xf3}\x12\xb0(\x03\xcc\xf0#O\x82\xdf\x05\x9f\x18\x16%y\x99;Z\x16\xd9=\xea\xfa\x89%Y\xe0YjZ\xabL[=\xe0\xb8\xdb\xd1?1\xa8\x84\xfa\xa2:\xd0\x12\x99K\x9a\xb5\x91\xd6RNo\xc2\xca;\x02\xbf\xa4\xd1\x02Ned\x98a8\x8e\xfc\xf5/S\xe2\xc0\xef\x11\xf5\xd7\xa3k\xac\x16\x91\xfb> \x16AT\x02sxG\xe1\x03\x9f\xf1EB\xe3\xa5\x85\x90\x0fVt\xc1L\x92\x01\x12ZI\x86 \"xU\x11\xbe\x86\x80\xd8\xf1X\x8c/\xeb\xcfx*\xbeJ?\xe3_\xf8\xbc\x87'?\xc2\x93Y\x12\xb1\xf0-\xcd\x92\xe0zJ\x1c\xf3\x15\xe9\xad\xcc\x16\x93\xfa\x06\xe4UE\x892\xc9R\xca6\xd9\x9f\xd9\x0d\xdci\xa4P\x95\xfa\x8d\xd6qs\x1a\x8b\xd3^\x01\xaa\x17\x1c\xf2,Xi8\xf8\x89@Iy[\x81;\xcdW\x14:\xcbXr*p?\xac\x0b\xf9>Je\x02V@\xa040\xa6\x95'\x8d~\xb7\x1e6`\x8f\x0e\x05\"v\x14-\x00\xe96\xd2\xb0r\x1cp\x012\xb2+\x9a|f\xc9 \x90\x1c\xf2\xf7\x88\xa1\xb4\x86\xcc|\x1b\x18\x80\xab\xc0\x0ex*\xaf\x085h*o\xa1,\xc0\x05\xd7c\xbeZ\xa15\xf60\xde\xac\xb0?\x07>\xac?\xe3\x0d\x85M\xf1=U\x84\xcb-qV=\xc9R\x9d n\x87\xcb\x96lE\x15\xa2\xc6>\xcf-\xd2\x82(_\xbd\xf72\xba\x86\xf5[\xbe \xdf\xd0R]\xa4\x12\xae\x89\x164O\xbaa\xc73\xa5<\x04\xcd ld\xa7q\x00\xd9\xf2m\xdc6_\xb3d\x1e\xf2+k\xa6\xd8\xe4Z6:%\x8eN\x1a\xc5*\x0d\x1b\x17\x05s\xb6\x0c\xbc\xcf\x11KS\xb3\\\xa6\x13\x91\x821\x0d\xa2\xec\xbd\x92\x08\xc1\xcb\xc8&\x10\x8ai\xc4S6\x018\xf1k4A\x81\xb2e\x81&\xcb\x17\x1cRP\xe7\xb5\xf5\x88\xa4\xda\xcb\x9a\x07v=\xc9^\xaa\xf6)\xeb78\x1c[\xa0\xee\x0e\xe0\xf2}\xc4 \xc1V\x00\x97\xa3\xc8\xac\xa3\xec\x17]\x8f\xf8m\xad\xe2(\xfb\xd5\x80\xfb\xb5\x05\xeeo\x06\xdc\xdf0\xb8\x84\xa5,Y\xb3\xa30^R\xf0\x1bo\xbc\xb7\xc1\xa71\xf3\xb2\x8fby\x9b\xa5\xcaT\xb4,`\xee5+\xc6\xb7\x92\x80\x94\xc07\x9d \xa2r|\x18\x136\x17#(\xfea\xd5\xb1\xf9\xaf2\x17\x1b\xb2\x82\x9ey\x0d+\x0b\x00U\n\x08cP\xba=a1\xa3\x19(\x89A\x81\xe2\xcd\n\xfbR0\xe1N\xf1\x1b\x85\x93<\xe8\xc9u\xc6\xa24\xe0Q\n\x05\xea\x89-%_1\x9a\xe5 3\xcb\xe9$\xb4\x94\xd2oA\x074\xcdCK\x16\xcflR\x94\x04g7\x12\x1c\xf7\xa6\x1e\xb5\xb0\x87)c8\xc3\x9f.i\\!I!\xa1\x95$MC\x1e[\xbe\xa2 \x184\x8fyyH\x13C\xe8SO\xc2\xbe\xa5@N\n\xb9\x84SO\xc2K\xd9\xba\x1b'\x8c\xfaoY\xb6\xe4>\xd4U\xbeb\xf5\x94\xda]\x02\xb8|Ca\xfd\x97l\x1dh\xe1\xa5\xf9\x8aB\xb3\x15.\xe0\x169kKN\x90y\xcb\xb3 \x84\xe5h\xbc\xa1\xf5\xf3X\xd3\x86\xe2\xb7\x95.\x14\x99\xa5\x0c\x02@\xed\"\x884K\x82\xcf,[&<_,\x8dc\xb3\x92\xdevvV\x00\xcd\x03\xb4ZC\xdb)*o\xb8,\x03\x94\xf0\xcf\x96\x95 Y/i\xba\xa4IBeWE\xca\xc8\xd7I\xf8\xa7T!^\xae\x81\xa2\x14\xb7\xaf\x04\x01\xf3&\x88\x98G\xe3\xb2L(\x13Z\x0b\xfc7\x0f\xa2j \x91b-\xf26\xc8\x04\xdd\xb1\n\x8c\xa6\xad\x8a4k1s\xbe\xa1L\xeb\x8c\xf3\xcfL\xd3\xc2\n\xfc\xcaB\x0c\xa7y2\xa7\x1e;\x95X\xc81_1\xe8\x1b\xb1\xd4\xdf\xd0h\x91\xd3\x05\xc0W\x12\x90\x12\x19\xbd\x0c\xa5\xb7&\xb1d\x8c7\x146Y0 \x02\xd4/+\xcc\xaf\x05\x0cv\x96e\xec:;\x02\xfdV\x01\xc6\xae\xb3\x91\xd4v\xb5\x80\xbed\x1eO4\x0e\x00p\xbfH\xb1\x141\x91/\x94h\xc3\xbd\x02\xa0\xa0\xf9\xca\x17\x0c\x92\xa3\x1b!+\xe98$7\xc7%\x019. \xc8E;k\x14t\x91\xd6\x86\x06\n \x13\x05\x94%\xdb\xb6\x7f\x1e\x05\x9e\x8d\xb7Qy?\x04~\x00\xf5\xc1\xdb\xe82\xf0\x03{E\xa0|e@\x83\xaa:\x0e\x9e\xa5\x1fXr\xb2\x92\xc0Y:\x8a\x05\x85\x8a\x11\xbf\xeb#\xe3>\xd7Y\x8f\xca\xeb]\x0c\xf8G-\xaar\xd6#%\xb6\xc2\xc0^\x9b\xb2%g=2dM\x18\xf8\xdb\n\x87\xe8\xacG&\xcb\x88\x15P\xdb\n\x19\xd65\xf32\x9e\x9c\xcc\xe7\xcc\x13xF\xbe\x8e\x18\xbcc5\xb1$\xb5\xb1jk\x96dG\xfe\xfaW\xa8&\xc9@\xf0\x86\xa1\x1d\x91Y\xca\xdd\x00\xb4E\xecVB\xffZ\x83F\xeb\x0e\xd8\xd5\x0f\xfcZ@\xca_\x16\x983\xc0 \nL\xbe\xa0\x90ip\x19\x846n\x18P%>\xacW<\xf1K\x89\x8fxk\x91\xf7\\% \xa9Q\xb7E\xeam\xb4\xc2o\x8cp\x9a\xf1\xba\x90\x95\\\xdb\xef\x87\xafq\x04p\x8d#\x80\xeb\xe3%\x8d\"\x16J\xad[@\x91\xf5$\xec\x1ba\x10}>\xf2\xb2\x1c\x88^\x07^\xa7T\xbe[\xc1\x13/\xe1\xa1\x01.\xdfm\xe0?& \x88\x96\xb0\xcb\x04\x15EC\xe6G\xb3\xd2\xb6\x1aO\x97\xfc\xaa\x00L\x97\xfc\xca\x06x\x16dF\x95\x99x\xb3\x82\xca\xab\\\x05\x89_\xe2^\xaf\xc2\x1f\xc0\xd3\xb6s\xbd\n\xa7\x97\x14U\x98\xb8^\x85\x11\xbe\xc8 \xe7\x17\xf8\x00\xd4\x10\xa5SLAG\x81\x8a\xb3W})\xa4\xe8:\xbc^\x85b\xcd\xea\xf6`J;D\xfa2@\x1as\x83/\xae\x1b|q\xdd4\x17W= \xf9\xf2\xefh]\xbfs\xbe:\x8a\xfc\x0fT\x1cQ\xe5K\xab\x7fT\x8a*\x1f)\x17\x02\x81\xc0\x95\xf5@\x11Dz\x1982Ug`\x84R\xcc!\x04il\x85\xa4Y\x1dil\x806 \xb9\xec\xdb >v\xd6!\x17z\x1b\x84Z\xe1\xad \xb0\xb2m\x10zI[\x8c\xdc\x8a\x85h\xcfWk\xb0WH\xd9\xc6\x8cL\xcd\xc8]\xa4\xaa\x9d*#\x02\x8e?\xb3\x9b\xd4\x0d\x06\xe39ON\xa8\xb7t\xed\n\x84t\\\xae\x08\x19\xe7vgH\x02\xf1\xeb\xc1\x03\xe2\xd2q\xe3\xeb\x12H@\x18\xeax\xdf$@\xc7N\xddu\x02\xc7\xedW[\x82\xfe`\x0e\x15\xa4\xa3\x85Guk\xd7T\x81\xef\xe2>>\x1e\xe3>>vw\xeb\xd5\xcf\xc16\xbdj\xcb\xaa50\xdf\xea\xf8\x05\xa69k\xc3;\x8b\x80\"/\x0e\xc8\xa4\xe6=\xb1i\xaeN@2\x12\x02]\x83o\xd0xIS\xe6\x7fd\x8b \xcd$\x15\xaf\x97\x10\n.\x1e\xe5\xf1~J\x1c\x1eID\x85\xa0)\xfdh\xd7\xf6\x06\xb4r\x11\xe5\xa0e\x90\xf5M@\xd9&\x16LC\xe4\x01^\x9a9\x19\x8f\x7f\x08\xf3\xc4\x19\x12\x07\x04\x01\x10\x1b\xfb-\x8br\x95\xf2\x8a{y\xaa~\xff\x95\xdd\xbc\xe4WQ\xf9\xf6)V\xbf\xdf\xf2\x06\xe8I\xe47'\xab\xa9\xa2\xbf\xa1EV\x8b\x05q\x87\x0b\x12\xfbf*\x0dM\xa7=\x0d\x82Mc\xd4io\xd3\xe0\xc2du\xda\xcfB\xd8\xb0j\x9dV\x8d\\\xf1m\xdb\xb17\x88\x1a\xed\xa6\xa5a\xab\x85b\x0f\xdb\xc4[\x8e\xbb\xb4KP&\x84\xd3\xc2PA\x07\xc7o\xb1\xf3\x92Q\x12\xa4\xf1I\x0b\x14\x8f\x05\xd0%\xcf#\x1f|5\xc4v\xd8\x90\xcd3\x13\xf8\x0d\x9b\xdfn\x94\xbf\xba~m<\xc0\xb2n\x0d\x8a\xfa\x9e\xbb\x16\x07,6\xde\x80~\x9a\x03\xa9\xcd\xfes\xc3\x93J\xac\xe6aH\x96Cbq\x10\xa7\x06\x9fC\xb4xr\xa0]58C\x91\x04|\xa6\x98\xd7!I\xc6\xa5\xea\xba\x8e\xb8\xf3Ry\xb7c\xa9\x0bf\x99\xd5\xfe\xfd \xf9\x8c%N\x93h\xfce3X\xee\x9aE\xa0\x84\x9aNImF\xd8u\x96P/\xd3wtu\xca\xa4%|\xf4\xd6\xa2\xc3\xea_\x0fdF\x0em\xb1\xd3\x06d\x8a\x9a[\x88'\xbd\n\xdam\xde=\x9a2\xe3\xd8\x9bZW\x9a\x1b\xba\x1c\x82\x9d;Y\x923\xe9#\x9e\x8f\x95\xaa\xed\x89\x1f\x80\xc8Q\x9a\xf1\xf82\xb6\xc7R\xfa\xa2\xd5\x07T\x8b\xd1!\xb8\x82\xc7\xb3\x8b\xf6\xc1\x99mo^qd\x96\xc7d\xf1\xe5\xbb}\xb8<\xe9\xed_\x87\xe3\xd6\x12\x17\x8b\xf4\xfc\x8eI\x89\xe0_\xaa6\xe9S\xdc\xd2 \xb5\xa6\x14\x19@n\xa4E{G\x0b\xeaT\x8b\xbdz\xb1t\xe7\x83^\xdd\xd2$TG\x97$m\xd5\xd9!\xd5\x91\x0edFZ\x1c94\\b\xfa\x1f\xf2\xec\x0d\xf8\xd3d\xf5\xe8k\x16\xaf\xa3%\xf1*M\x97a\xd1\x03u\xb5c\xb5\xc1\xc3\x8d\xaf.!\xf5\xae\xcc\x0c\x1e\x99\xc9\xe6\xaf\xbb\xc9\xfbP\x9c\xc9\xc9\x95\x05\xdbc\x94\x9b\xd9\xdf\xab\xf3J!\xce\xfc(\x8f\xdd{u&g\xae\xd2\xeb\xf0\xb1jM=\xdd\x97\xf0\x8f\xea\xbdZ\xaa\xf4\xfa(\xacUz\x9d\xe9Z\xa9A\xab\xc3/\x14|\xdd\x07\xdf\x8d\x1c\xcd\xfa\xe8\\*\x1e\xad>\n\x17e\x84\xaa?\xbe\xd6\xf2\xaej\xe1\xe8g\x0e\xbd\xe4\xe0G\xc0\xa1Q \xdd\xe3\x9dD~\xe5\xfdu\xc6\xf4\x15\x89\x91\xaa\xfd\x0f8\x97\x8a\x95\xf1h\xf4!\xa47\xc6\xcf3ya\x08)a\xe0}\x86\x1fUn\xc7\xe3\xb1,\x91C]>\xcf/Cv\xac\x81\xfd\x84.\xf4\x7f\xd5*\xf9S\xfa7\x90/\xd7A\xa6\x7fC\x8c7\xfd\xf2~]\x02\x15\x8d\xf5\x13\x0e\x1c\x92\x9f\xcb.)<3$\x0e[\xc5Y\x00Q\xcc\x1c\x16y\xc9M\x9c\xe9\x17_\xfdH\x12\x0e\x15\xce5{\x16D\xb1lv\x10\xadi\x18\x00\xd4\xe7\x92_\xfb\xccn>$pO\x02\xbf%k\x16r\xea\xeb\xff\xcc\x7fI3Z\xbe\xbde\x19\xf5\x8d\x94\xa2\xd5+\x93\xd5\x83\x97\xb7\\v\x14^\xde\xe7%\x94\xee\xf5\xaa\xe4\x06c\x9afL\xfe\xc8S\xf9C\xcd\x93\xf8\x0f\x12m\xe2\xc4 _\xe8\xc6&4c\xe5\xc0\x80s>\xc7t\xf1\xeb\xa4\x8c}\x96\x83\"~\xa9\x1a\xd2\x8c\x86\xa1J\xcd/WrV\xd2<\x8d\x99\x9c\xb9,X\xa9P\xd4\xf0\xc6soy,\xc8\x87\xb0xUS\x0c\xbfu\x07\xe1\xa5\x18\x08\xb8\x1f\x0b\x8cE\xba\xe6a\xbe2\x1a{EA\xf6\x0e?\x97\x8c\x85\xcey\x0f)\x91f\x8d\xd8l\xe7|\x9c\xf1Oq\xcc\x92c\x9a2w@\xb6\x05c\x16\x06\x1es\xeb\x9b\x95(\xcbg\x87G\x10\xe3\xb7\x99\x0bv\x98\x19\x8f-\xd9\x1c\x15x\x90;\x8a5Z\x0c\xc1KiFD\xb6\x89s\x0f\x92\x8c\x04\x91*T\x0f\xe3\x0b)P\xe3Cr5K\xce\x8b\x80\xd9\x00Y\xf3\xd2~\xa2PS\x91X\x08\x07\xae\xad\x16\xca\xce\x18\xe2P\x8d/\x12\xce\x81.}\xfd\xb2\xac\x1f\xa9\xe9\xd4^\xd3e\x9ee\xd2\x0c\xf8@\x06\xe0T\xdb\xdbHH\x8d#W\xa6\x08TF\x13FU\x9a\xf1m\xfdK\xf4\xec\xb8\x95\x92\xbf\xd8\x90\x92\xe7(\x13D\x13B\x87pR\\\xcd\xd89.-\xd8\xba\xe9 \xf5\xfb\xd3\xeaGpjtPT\xc7\xeaD\xe8\x07\xa6O\x8b\x0e\xe8\x97U\xcc\xdd\x01}\xa2\xb0z\x17X\x81\xf1;\x01\xfd\x1e@pRt\x00\xbd\x86\xd5\xd5 $\x0f\x96\x0e\xb07\xe2P\xe9\x01\xa3\x0e\x9c^\x90\xc5a\xd4\x03Z\xe2\xe7\x0e\xc0\x0fp\xfat\x01\xf5X/\x1f\xd4\xa9\xd5\x05\xa6O\xb4\x0e\xb8\x8f\xe5i\xd7\x05 'a\x07\xd0\xa9<\x1b{@\xf5\xe8\xc3\xa9:S\xbb\xc0\xe4y\xdb %\xcf\xe2\x0e\xb0\xb3\xf2\x9c\xee\x80\xfc\xc9<|;`\x7fV\x07\xb3\x9d\xbf\x12<\xc0\x1d\x19\xe5\xbfj\x8a\xab\x9do\x94\xfe\x9e.\xdd\xa8M\x82\xac\x9f\xfbf#!\xb8\xd3\xdd\xba\xd9\"\x88(`\xba\x84)\xa2\x19\xde\xdd\x9a!\xc9\xf4\xf6\xa1\xdeU\xaeq\xe4\xe9\xba\xc9p\xbf4X\x81\x8e\xbev\xc9G\xaa\x80@Y\xf6\x01\xb4Nc\x15\xec}7\x1a\x7f[P\xe6\x1d\x80\xdd\x12\x18\xa2\xe6.\xbe\xdb\xdc\xbd\x14\x9cUGc^*\xae\xab\x17X\xd6\xdd\xb9\x97\x9a[\xeb\x01'9\xb9\x1e\x80}F\xf5e\xc1\x01v\x02\xf2\xae\xadkq\xadHz\x8e\xfb\x99\xc1\xf6t\xe1a\xcd\x12\xf5\x81\xeb\xb3\xa8\xcfJV\xaa\xbd\x8f\x16\xef\xb8\xa4g\x1f\x8fLABG\x9b\x8e\x9aB\x86\xbe%\xfa\xf4\xa4\xc5\xbb^\x9f\x9e\x9cU\xd8\xcd\xf6O\xad\xef\xf6)\x19\xe4\xa7\xe3\x1b\xab\xbb}\xe3g\xe0\x88\xdb?\x81\xf8\\\xd3O\x9fO\x1c\xf3\xb8\x93~;\xeeF\x98\x1f@d\xd1\xde\xd2\xa6?\xc4\xa6\x08\x96\n.-q\x9d\xfd'\x0e\x1e\xc8H\xf0M\x17\x10\x90\xa1\xbc%\xba)9\xadf\x01u\x80\x05\xed\xb7?\x17\x83!\xb9\xa8\x94\xbd\x07\xa1/\xdcV\xf3H\x1e\x89\xa5\xdcw\xeb\xd4e\xe3\x8b\x8c.\xd0\xdb1b\x08j\x05\x1fm\x17\x0f\x04z\x18\x90`\x83\xf8\xac\x9f\x08\x96\xfe\xcb\x17\xe2\x9e(\xde^G\x85\n\x0c\x89\xdf\x0d\x16_\xaamh\xae\x820|\xc9B\x961\xcb\xf0\xdc\xfb\xd8Djll\xbd\x8c\xce\x95\xc3Iw0$>4\x0dR\xbb\xfaU\xbcYd\xef\xc7\x90zG\xd9\xfb\xa3}\xd4\x81=o\x11\x18h\xf7nc\x8f\x86\xa1\x8a\xacn@\x97\xcd.~%c\x9aC\xbc\xf8\xe3\x90\xa6\xa9\xcb\xeba@\n\xa9\xb0\xf4\x8f\xd0\xd4\x06a\xd2/\xb1\xe0-\xb0\xec8e\xb9\xcf\xcb\x0b\xed\xca\xadhM\xfd\x8a\xdf\xd3\xa85o,\x9a+\xc4\x0b\x83\xf8\x92\xd3\x04\xf8\xe6>~\xda\xb54\xa9RP\xe9\x94\x1c\x126\xae\xa4\x17\xb7\xa6\xd5\xe4\xaee\x85Mw\xf0-\xa7;\x90^\x86\xcdI\x08\xeec\x12&\x93\xc9\xbf\xc1\xdaM\x98@\xe2\xbeV(\xff\xf6k\xafy\xf1\xc3-79\xb8\x87\xbd\xcf\xecf\n\xf7V\xf5[4\xa2<\x02d\xa0\xe0\xdf\xdce\xe2\xf1\xb2$\xfc+T\x80f\x83/\xb5\x96|\x1a\xb6\xe5\xaeXF[\xb2\xa51\xa8-\x17|\x19\xa0\xd8\x81\xc8\xb8\x16o\xb9\x1f\xcc\x03pA\x90 8wwR\xbf\x18\x14\x8f\xb7\xa4\xc9q5\xf4~\xe7v\xfd\xccnb\x10\x1cH9\xae\xd4\xfd8\x94nm\xa7\xb5x\xa4\x04\x17\x8f\x7ff7\xb7\xf8\xaa/\xb8V\xf3\xa3_\xbe@z\x1e\xd7\x9a\xc2\xc6\xea\x03}\xdbs\xb5\x0c\xbc\xe5\x86\xadi\x19\x83\xfbll%\x05Eg\xf4[b\x00:$\xc1\xb7P\xe9m\xee_\xfcP9I\xbd)qNR\x8f\xa26\x05\xa0=}I\x93)q\x08\x92\xfd\x06\xf4\xad\x9c\xa3$\xe1W\xe27\x02\xf2)\xd6\x00\x9f0\x83\xc6\x8f\xca\xd0\x04 >ZLM^\xf2\xabH\xc3\xc8\x9b\xc7&\x08\x0b\xa7\xc4\x91\xa4\x1a\x92\xfd3\x18K\xbe?E\xb2\xde\xb2(\x9f\x12\xa7\xa2\xf9\xda\x00:\x8a\xe3\xb4\x13H\xb2MS\xe2\xc8\x1fo\xb8\x87\x19O\xbc\xe5\xbf\x7fH\x82\x08\x14\x84\x00?9\x9f\xa2\xc0gQ&\xf0\x89\xdfjg\x80\xa3\xe0\xfd)q~\xa0\xdeg\x9b\x85\xc5\xb3)q\xce\xe8%\x923\xd9\x15}\n\x19\xc5\xcc#&{ba\xc8\xdb\xedf\xe6\x13\xd1M\x8b\xaf\xcb\xc9S5T \xc7\xec\xc7&\xa2\xc1G!ZR\xb4U\xca\xe6\x9b\x99\xbb;S\xb8(L-\x03\xbb\xfb\xb4m%\xef\xedZ\xd6\xf0\xde\x1e|s\xc1\xd0\xf5\xb9\xf7H\xe5Z\xd6\xdd\xdec\x18%\xcc$|O\x8c\xd1\x8f\x1cu\xcb\xb5\xf7\xb4c\xdb\xec\xed\xb7n\x9b\xbdg]{\xe6\xd1N\xc7\x8ey$Z\xfe:J\x19\xea3\xe7\xd1\x93\xb6\xed4\x81\x95\xf3\ns52\x81u\xf3j\x17\xcd\x12\x83\xf9j\x0f\xcd\x12\xady\xf5\x08\xcd\x12My\xf5\x18\xcd\x12\xc3\xf8\xea \x9a%\x06\xf0\xd5S4K\x0c\xde\xab}tC\x88Q{\xf5\x0c\xcd\x9a@\x97w\xd0<9\x1c\xe8x\xec\xc2xL\xd0\x01y$\x06\xe4]\xbe\xb2\xac\xe8 \xccQ+6\xd9\xdd\x15U\xbce\x19\xada\x0e\x9c\xcb\xb3\x9f\xc0\xd2\x0b\xfegvc\xbb\xd1\xcd\x04\xc99\x03\x90s\x19\xec\xf63\xbbir\xa9\xc0\xfcV0\x1ah\xc8\x97\xde\xe3\xab\n\xb9_\x1b\x8d@\xcf~[\xa3\xb4\x7f|\xabld\xa2\xfc\xe1\x93C\x8d\xcc\xc8\x94\xc8\xb0:\xe3y\xc2W\xc7\x8a@\xab\x07DF\x15d7\xa2;\x82YAy\xc0x\xd5\x06eJ\x9cr\xc6\xee\xc1\xc9\xb6\xd4\x11\xfb\xd7s0>\xcd\xa8t\xf7\xc3\x92\x7f\x1d\x03\xd3\\-\xa0\xbb\xc3R\x1bI/\xb5\xa9\xcf\xda\x81<\xb8]\xf4;\xa0\xee\xc4\x96\xdc\x91%\xb2q&\xd5\xb5\xfd?\x86i\xff\xb7X\xf1\xb1\n\x15\xfd\x7f\x8b\xb8\xe9\xdf\x04O\xb00\xa3\xbft\xf1\x84\x1a\xf1JhCv%\x13\x04\x16\x05\xd5\xba\x97\xd5\xfc\x11\x1b\x1b\xc9\x0d\xc6\xaf\x11\xa74\xcc\xe8\xaf\x1b5\xe5\xd7zS~\xad6\xe5W\xbc)5(\x1c\xa8Ws\xff\x86-%\xc8\x91\x86\xff\xdfj\x19 \xce\xf2\xf1\xa0\xb9\xac\x9eu\xd1\x1b\x88\xac\\\x1f\xe0\xcd\xb1\xbe\xc8x\xfc\x86\xadY\xa8\xe2\x02O b`u\x11\xf8\xe0\xf5KdO\x90\xecJ\x84\x8e\xa9\x8a\x91R\x84\xc0\x80 \xa9\" \xc2\xa9U\xa3y\xd8\xb0\xeb\x85\x8co\x83\xe8O^dta~B\xe0\x82q\xc6\xdf\xf0\xabB{\xd3^\xa9\xb6\xfd\xfe\xf4\xf1uQ\x87\x91F\xa6\x88\xda\xfesl{F\xb5}x\xab\x196\xa7\xaf:3\xf5x\xcfS\xb2U3\xa0\xcfS\xf6*\xb8\x14\x13\xb25\xb9\x8f\xb6\x18\x91c\x1e\xd5\x15\xe6\xc51\xff\xf0\xb7\x87\x87\xdf?\xac\xa6\x0b&\xf9\xe1\xdf_\xfc\xb6\xf5\xdb\xe8\xb7Q-\x0f7\xd4?\xfe\xf1\xe4\xf8\xaf\xa7\x9f\xde^\x1c\x9d\x9d}\xbcxw\xf4\xf6dJ\x1cA\xc7\x8c \xe4\xf0\x08b*\xa79\x1a&\xc3\xf7\x8fU\xee\x19\x97\xb1\xb4\xbb\xf0\x081\xe8i\x9ct%\xe6\xd5^\xc6\xd2LTt\x08\x01f\xd88aqH=&\x10\xaaC\x1c\xb2M\xe8\xb8\xd9~\xb2M\xbe;p\xbe#\xdb$\x13?\x9d??\xf8\xae_@s\x1a}dy\xca\x9a=\xe9\x8a\x80\xa8c\x9b\x16\x16\xec.\xd6\xae\xf6\xce\x8aJ 6QL\x93\x94\xbd\x8e \xf0\xe4dg0\x94\xc1\x7f\x80\x8eo\xf6\xc2\xb6/\xeeY\xa4\xf6\xe4\xf1\xe3\xddI\x17\x92\xab\x0fQ\x11\xc7KL\xf6d\x08=\xdc\x91\x91\"wdH/V\x84\xdb\x12ks\xf4\x88< \xc1s\xc2\xc9\x0bB\xd1\x10_E\x8d\xb9\x19f\x90\x93m\xf2h\xe7\xd9\x93!\xa1\x03Y:\x17\xff\xb6\x0f\xc8\xa3\x01\x89\xc4\x7f7\x13\x7f\xd9X\x0b\xa4\x8f2\x97\x0f\x06d\x1b\xcd \xdbd\xd2\x96\xb9\xdb\x96\xb97@f9#\xffq@\x121\x00\xffa\xc6\xa6&\x8d T\x91\xdaD\x17\xc48lo\xab\xf6c\xcdGq\xa0+?5 _\x88\x1b\xa9\x9f/^\x90\xc9\x93\xfb\xc0G\xe6\xac;\x93\xc7\xe3'\xe3]\xe7\xf6\xb5u\xd8,\xb9\x91\xfb\xe8\xc9`(m\x91p\xdb\xa5I\xdd\x9aG{bx40\x8f\xec}\xa8\xe5\xd9\xc6\xa1\xb7\x04;\x1e)kw\xd6\xa2/'\xe0&\x8a\xfb-\xe3\xce)pV\x85\xd5\xbb\x01\xac7\x1b\xe8O\xd4T\x8a\n\xdcL\x06\x11\x1e\x08\xf4\xc7\xed\xe6\x9e\xcd\x16\xa1\xa1\xb4\x04\xf2\x8c|&N\xfd\xc4u\x1e=rDY\xf1\xeb\xb13\xac\xb8\xf3\xb8\xe7\xf8WbB\xf6,\x83\x9f\xa86\x9d\xe6\x97Y\xc2\x04\xd2\xe3EX\xe0\xdb\x7f9\x1b_\\\xb0\xf4-\xf7\xf3\x90\x81!\xdeP\x86\x87\x8b\x98\x97\x01\xa6\xfe\x90\xf0u \x86BG\x1dm\xb6:p#w\xff\xf1n}\xe5\xf1\"\xeb\xd1\x00e#\x02\xabY\x83\x8a\xf7h4M\x1ejM,\xa7\xa2\xa7MIwL\xc5J_\x12\x1dw\xad\xda_\xae\x93\xefyDU\xad-\x83\x18\xb9u\xfb<\x0eK:r'\xd8\x96\x16\x19{O\x1f\x9b\x18T&=\xc1\xc7\x9a\xfes\xc7Z\x9f;-\x07\x9en\x99\n\x1a\x8d|o\xab\x1fU\x016\"n5\xe8\xdd`@\xb2e\xc2\xafH\xc4\xae\x88@2`\xdc\xe0:\xc74\x8axF\x04oJ(\xf1\x04\xc3IhJh\xf1%\x07\xa1~\x14\x17\x8b\x99\xdd\xaf\x95\x95y\xff\x862\xb3e\x1f\xd9\x9c%,\xf2t\xf3\xc4\x87\xc8\x92\xa6\xd1w\x19\xb9d,\"A\x14d\x01\x0d\x83\x94\xf9dD\xd2\xd3\x05\x1b\x93O)+\xeb\x1b\x83\xb4\xa2xu\x07$\xe3\xf2d\xcc\x96l5&\x1f\x19\xf5\xc9J`m\x9a\x11\x15hu~9^\xb1\x87y\xca\xa4\xa8cT~\xc5\xa9\xdf\x8a\xe1\xa3\x91\xb5-~\x1b]A`\xd0\xcb\x95 \xb8\xe1&\xaf\x80\x0b\x08\x95kn\x04C^r\x1e\xa2\x19\xa2\xb1h\x86\x8c\x94\x8bf\xc9\xa3\x15\xcd\xd2\xce\xc5\xb1\xac\x9b\xd5\xa5\xa5\x114\xc2[\x0d\xfdy?Ge\x8bLK\xdb\x90r\x9a:\xb2\x14\x95\xf2Jk\xc7,\xa5xd\xab\x0fr\xa4\xc7F$\x17\xe2\x01\xe0]\xb8\xa6b\x18kW\xbf(\xff\x1e\xd5\x160\x91r\x83\xb1\x99 \x0e\xec\xa2\xec\x1d\xf0F\x83\xa8o\xa2\x14u\x82\xd14\x0d\x16\x10\x9e\xbb\xaf\xb0\xe79\xc9\xc8\x0bB\x93\x05\x88\x94S%\xe6yN\xb2\xedml\xaf\xe8\xa5^\x14\x98e\x88\xe1t\xf1\x89\x84\x04\x91\xe8\xa1j^y,-i\xfa\xfe*R\x8e&o$-')qqN3\xa9\x1b\x1f\xcd\x92\xf3\x1e\xd7\xdd\x86 9~\xe8\xb4\x8d8Q\x9d\xf2\xccN\xa9Q \xdf\x93=\xd1\x1e\xc95\x01\x8e,\xfb\xbdwN\x0e\xab\xaf\xb8\xfb\xd4\x159 ?p\x1e2\x1a\xa1\xa6\x04\x0b\xa2\x0c\xe3\xe7\xcd\xbc\x1b\x84e\xd3\xe9x\x14n}S@\x0e\x89\xbb#\x0e=5\n\x03)\x81\x88\x9b\x88\x0b<\xa2\x80\x8b\xc0\xe6\xf7\x05\xbd\xe3\x8d\xe3H\xf2z\x1dNb\xdc\x99^u\xcd]Y\x8a\xe6\xd58\x00\xe5\xdb\xbdp\xd4\xeeJ\xcb\xd3\xe8\xcb\x17\xb2%\xe8oZ\xd2\xdf\xba\xce\x12j e$\xf5\xb2\x07\x82\x0d\xa8\xbb\xb2\xd5\x0f: \x95\x11\xbd\x8f1\xa9N\xd1\x1d\x87\xc5\xaf\xe0\xad\x96\x91\xa9\x00\x9a\x83\xe3\xd70\xdf\xa6\xe3\xf3\x96%\x0b\xe6\xdfit\xba$OX9\xb1_/\x8b\x02\xed\xacf\x8b\xf3j\xd2\x85\xa1H\xc1N\x1a\xcb\x08\x1b\xd3\xcd\xa6oKV\xb9*\x07O\xcc\xc8)L\x0b>\x81\x06\xa89}f\x0d\x9bL^\x90\x9e\xe6\x97\xa9\x97\x04\x97\xfd\xe7K\xb5\x1d\x97\xa9\x89\xc6\xe4Q\xaa+\xed\xd3\x86,\xb9)\x1a\xd1\xb7\x0d+p\xbeQ\xffZ9\x1ef\xe2\x81q\x1f8.\x92%\xdc\x92F~\xa8\xa8\xe2\xf1e\x10\xf9\x90<\x18\x0cI#\xdbE\xfc\x8c\x10\xb47\x9f*\x1f\xef\xd5\x9f^=qu\xb3\xaa\xbd\x13\xecd\xaf\xa6\x15\x92\x83\x97\x81\xff\x96\xe7Q\xe7]\xab~\xe0\xa3\xe64\xb9\x9b}\xef\xe7 \x0c?2\x8f\x05k\x84\x93h\xfb\xf0U\xcbN\x90[\x0c\xdc\xc3\xa8\xb9j\xf2@M\x7f\xe5\xfaik\xea\xa7hu\x9b\xd1\xf9\x84\xcc\x94)\xb3\xe8\xd5\x8e\x02~\xa3\xaf\xd7\xb17h\xa5\xd7\xcf\xc2jz\x15c\x18\x19\xb6q,\xb2\x9b\xecd5\x7fm\x9c\xf7?0\x16}H\x98GC\x0f\\\x19\xf9\xca[\x7f\xadi\x06H\xc0#\x10\xa3T\x1b%o\xe6\x99\xaf\xb4\xd4\xab\x99v\xa2\x0b\x01\xaa\xf1%\x0d-|\xfd\xd4&\xc6\xc4\x04}\xa7\x06\x14\x1fk\xfb\xb5\xcf\xa1VCY}\xf9[\x02:\xb9\x07\xc6\xd8\x8eK\xe9Z\xfb\xd9\x07\xec\x8b\x14'\x00\xd1\xd9\xd9L]\xe8\xaa\xc4\xc3m\x1c]\x9f\xea\x08&\xcd\xef\xa2\xf2\xebO\x96\xdcl\x00M\xcc\xab \x1a\xc7\xe1\x8dk\x11\xe2`\xcfW\xe2\xd1vo\xc6\xb6G}s9\x06y\x9a<\xb0\x97\xbdk\xb0\xcb\xb3\xccGQ+6r^\xee\x8a\x0e\x8aI?\xb0<\n\xe7\x9a\xfd\xcaDp\xd3\xb5\xc4\xc8o|\xb7\xab\xd1\x18\xf4\xc7#\xedb?\xd2k\xa8z\xe1\xb4T\xef\xc0~\xd3l\xca\xb4q\n\xc8|\xbe\xb6\xaf\xb8\x16\xe9e\x1f\xbc\xb5`\x99\xb4\xb7\xf2\xb5zu_\xec\xa59\x8c\xea\x15\xc7\xf5\x908g\x9cP\xcfci\n\x97\x12W\xb2\xfa\xe2\xf6kHnxN\"\xc6|\x92q\x88\xe0\x1f\xcco\xc8\x1fD]kNI\x96\xe4\x8c|%T\x16\x9f\xf3<\xc9\x96\xc5\xe50\x01\"\x12\xeeF\xe0~q\x00\xf7HcgP\x1c\x04\xf3t|U\xedQ\x9fq\xe8\xa7\xda\xa5\x1f}\xcdi;\x10\xdb\x11qT\x96l\xae\xab\xf6\xa2\x81\xf9\xd1\x96\xe5\xdf^\x0b\xad\x9c\x02\xb6=\xd7^G\xae\xeb\xa8\x1d\xbd\xf6\xdd_\x1cw\x16\nb\xd2AAL\xfa\xef\xfc\xcd(\x08\xaa\xefih\xbb`-\x95{\xbeuX\xc2\x8e0Hp \xe6\x80\xf5R\xad, /e\xba\xce\xc8!\xd4m\xc2\xb6\n\x88:\x84\x84\x1e\x12\x1d\xb1\xfe\xccU\xb4D[~@\x0ee=;dJ\x803u=\xbd*l\xe7\x8a+x\xa7\x10`\xe7UXT\x82\xe2\xb6]\xc5\x16L\xf2\xd6\x96\xeb\x81\xd6\x07\x8c\xe6\xa0\x18\"\xab\xe8\xc1\x95\xbcqN\x0eIN\xa6jY6i\xc8k\xa5\xf9\xc1\xd5\xf5\x99\xca\x01\x1e#q\xff\xf8\xda$\x95\xbb\xee\xd3d\xe0\xe9\x1a~\xc2#`\x10\xc0\xfd\x03\xd1\x88TX\xc7j\xc5\xd5U\xb4l\xac^um^\xb5\xdf\xaf\x16Z\x93\x03\xe5!\xe0~\xb4\x1e\x87v\xa5\xbez'\xc1K\x90ti[\xdcR\xd5\x8f8\xcd\x98U-\xea\x9a\xc7KR\x83\xa9#\x19\xb0>\xd4\x1a\x83\x82\xd3L\xd4K\xf9\xe5\xda\x81T\xa8G\xf2\xb2j\x9bj\xa44\xbf\xddyN\x02\xf2\x82D\x85zf\xb0\xbd\xdd\xc4\x91\xc0\xd3p\xa5\x194$\xd1,8\x07a\x12\x9b\x89\x9f\xe7\xf2\xeeE\xfe\xb6\xb6\xad\x18\xac\xda\x0e\xf9\xb6Sh\xd9\xe7\x05\x00\xca0\x1b\xd4|\x02\x82\xce#\x00\x06\xdb\x7f\x9e\xa4\xf2\xbc\xe9\x89&\x957\xc2\xa7J\xb4\xd6\xd1[(QV\xd0J\x83\xe3#C\x0c\xb9\x08\x8e\x04\x1a\xd6\nv5\x12\xaf\x17\x94\x1aw8v[\xa0\xcaS\xd2\x0e\xb4`\xd9\xcb^\xb5\x01`\x12\xac\x99\x0fd\xd5\xab\x84\xaf:J\xac\x82\xeb j\xc9/\xceS;H\x06\x8a\xdf\x08+\x8dh\xe7f\xd6\xf1\x8fZG@\xee\xc3\xd6f\xca\xed\xdc2k4\x0c\xc1\x05E[~K\xf9B\xf7\xb8\x0d$\xc8n\xfa\x0e\x85\x81\x0b}6\x0f\"V\xa0\xa0\xe6\xce+A\x17,3\xb0\x15\xc4\\k\xc2s\x1b\xfc)\x98 %\x02[\x89\x97,\xf5\x92 \xce0^\x8fV\n\x19\xdaMMPA\xcaPAEP\xa5'\x85[\xe9\x17\xb4H\xea\x86C\xe2\x0d\xc9\x1cCD\xa0['\x0d-L\xcd:\xcf\xc6\x8e\x0bx\xd4\x0eG?\x023\xc4`g\xeb\xb5\xf0\x12\xb1h\x7f\x0cX\x1d\xb83hc,\xda\x88\x16\xc1e+\xe2S>\xb8\xf8\xb0}\x8a\x13\x1d\x1d\xd8\x17\x84\xb1G3\x97\xbb\xde\xc0\xc6\xe5\x14\x87\xdbR\x9e[K\xf2\x82\xf8\xc5\xb9\xb5\xbd\xbd\xec\xea\xb8 \x1b\xfc\xd9\x121+\xd0\x8fRN\x9e\xad\xc1a]\xa6\xfe\xcfE;\xe7\xb3\xf5\xb9\xd5o\xbd~\xc4WV`\x1f\xee\x0d\xc9\xbaC`\xd8O\xfc\x1a\x89\xb1_\x0f\xc9\xaaC\xf2e\xcaW7\x16\x83\xa1\xa9j\xa56%\xfeMp\x14\xd48\x12\xab\xde\x97\x12\xb7\xd7Y\xd8\xed\x81\xa2^\x1aL\xd1\xf8\x90\x04\xb8A\x9a\xd6\xdcn\x0e:\x084\x9a\xb3%\n\x18\x96\x08\xd9@\xc6\xbaeWD)\xaf\xbe\x0d\"\xf0fH\xd8\xb5\xc7b\xd8\xcf\xdc\xf3\xf2$a\xfes\"\x9a\x9f-\x19\x89x4Zi@\x9f\xad \x8b\xd6A\xc2#\xe0\xab\xc5\xa2\x06\xc9^\x1e\x86\x04\x82\x9a\x92\x15KS\xba`\x84F>\xa1\xbe\x0f\x11OhH\x96,\x8c\xe7yH\xaeh\x12\x05\xd1\"\x1dc\xda\xe2,L\x99eQ\x89>\n\xcehV\x1f\xa6s\xbb\xe0\xc3\x83\x9d\x86f\xbb\xd5\xa1\xc8\n\xbf<\x0f\xff#}\xb8\x18\xf6\x13\x1d\xeau3\xf3\xb6\xb7\x9b\x01\x1c\x88d\xfa\x07\xd2\xee\xe1\x808\xaf\xa35M\x02\x1ae\xe4\xa7\x80K\xe1\x15b\x00\xd1H\x91\xf2\xact\xd2\xec\xcc\x1f_\xf1\x1d\x828Hi\x02\xea\xd5\x87\x89\xd0\xa4#\xa8l\xd8A\x95\x13C}L\xbaE\x91\xf6\xd1!\\k\x83<\xb04\xaf\x9a\x0c\x86\x98\x8d\xff`Hr\xd1QO0d\xa0h,\xc5o\xa2\x7f\xdc\x8d\x86\xe4\xe9\x90\xa4\xd8\x01T\x1c>s\xe3;\xcf\xc9|4z> \x01\xa8\xfc\xcd\xe6\xe7-R\xa2\xeaR\xb3\x99\xdd\xa2\x0b\xcf\x1c\x8c\xde\xbe\xe5\x8a\x06\x8b\xae\x8d&C\xa2E\xbc0U\xe4\x90\xec\x80Nvy|F\xe4\x05I\xe0\x86R\xe9\xd2\xb9l\x16\x9dK.~\xf0\x1c\xa7b\xea1V{o\x99\xc6\x9a\x96;\xe6\xc9\xa3.{d\xac\xab\xa6\xec\x06\xd6\x11w\xb3AE\x90u?\xad\xdb{\xba\xffo\xd1\xbcF\x88t\xd9\xbcI#\x02\xbbB7O\xea\x88\x82vK\x07\xba\xfa\x89\x9e\xad\x89\xcb\xca \x8eA\xc3\xb7\x91\xbe(\xe2\xa84D\xac\xd3\xd9\xb9E\x9e\x91\x835\xd0\xc0u\x0c\x1b\x0c\xa0\x88sP\xe0\x83\x8b\x00*\xe5\x13L\x9c\xfc \xd1\x8e\xc6q\x9e.\xdd\x1c_\xbb]\x06\xb4\xdd\xbb\xae>\x06\xba\x7f\xf5^\x14Hr\xeb\xa0.]%\xd5\x9d\x1aDj^` 3\xd9\xfe\xba\xaa\x9e\xc6\x81\x9b-\x9f\x8e\x88\xdb\xdaM\x1321\x1c\xe2j+c\xb3\x83\xaay\x8f\x8c\xebdx\x95\x14i8\xd3\x05\xd4>R\x8f\x14\xb9B=\xacR\x0ff%N\x943\x81\xa0\x9c\x90\x03Q\xf5!I\xc6?\xe4\xf39K\xc8T\x99}\xdaX\xb3CB\xc74\x0c\xb9\xf7)J\xe9\x9c\x15\xf0\xd5A\xee\xbd\xbb \xa9;\xed\xd21\xca\x91\xc3`]h\xa4+e\xe4\x06\x04QL0\xdc\xc6\xb8\x11h\"\xb3+\x02z\xdez\xe1\xa3\xba\xe3\xc5\xc7=\x1e\xdf\xb8\xc9`h\xf52\xf7uP\n\xf2\xdc\xc9\xde\xa3A\xe1\xeek\xf3-\x80\x0c\x88q\xe64\x1bi\xf4\x1d\xd9\xe9\x99TP#\x07\xe4(I\xa8\xe8\xc5\xa08\x99\x9e\x0fH6\x8b\xce!0|t~\x1f;\xa2\x13\xdfO\xf6\xefr\x1c%\"\x13P\x9d)+\xbc\x9f\x96\xed=\xedt\xdcqO-\xab7+\xba\xff\xa3C\xa3M\xfb\xa6H\x14\xabQ\xdd\x05\x16\xc9\x8a4\x82\xd5B\x13\x03\xcf\xccv\xce\xe5\xa9\xa0\x8f '\x88|v\xedH\xcd\xe0d\x0co\xd0\x0e\xf85$\")\xce3\x95\x14\xe7YeSm8\x93\xbb\xbb8\x93\xb0\xff\xb4N\xae\xabS\xfb)\xee\xdap\xff\xe9\x1e\xca%\xec?\xad\x9f\xf2b\xd4\x9d\x99D\xb8\xdaQ\xc0\xb9\xd3d\x19\n\x98\x974cu\x00\xcf\x04xK\xe3z\xfe\xdc\xcc\x7f\x07\x8eD\xea \xb1 \xf2\x91-N\xae\x1b\xb5\xf8&\xc8)\xcb\xea\xf9\xcbJ>Lm\x1dd]\x01\x01\xe9_\x1dde\x82\x00\x86\x91GF\x1dnQ\x1b\x14\xfaS\xc0\xae\xea@7&\xd0\xab\x90\xd3lo\x17\xea\xac\x03^6\x00\x9f\x01\xd4\xb1\xbbA\x1d\xe2\xef\xc4Z\xd3\xde\xc65\x89\xbf\xbb\xbd\xbc\xe7j+a1\xd6\xb7]\xa9\xfb\xb6\x1b\x90G\xf8R\x9d<\xc3tk\x04\x1b\xdbzH\x90\x9aL\xcd\xc9\xb8\x143;-\x91\x0c*^\xf5\x9aHH<}<\xfb)\x83\x07\xc1~\xe0\x00\xa6\xbb\xbf\x06@\xcd\"V\xb0i\x01\xbe\xf3\xf0\x18`\xdd\xbb\xc5\xb2O[93\xbd\x04,\xab\xa4{\xe3j\xd6h\x7f\xa76\xb2bYL\x9e4\x97\xc4K\x9a\xb1q\xc4\xaf6\xc5:\x9a\xdeA&0hj\xbf\xf5\xe9\xfbZ;\x02\xb5\xf9 \xc8\x01{\x8e\x88K\xc9\x08\xf5O+\x98L\x88\x86#\x0e\xa7\xef\xc9\x0e\xf6\x15\x0d\xb7\xbd\x9d\x91\xef\x0fHapnx\x8e\xdei\xaa\xd4}\x95\x1a\x82\x19\xae\xd7W\xdb\xb8\x9a\xcd,j\xbc'\x89\xe1\xe4\x11.\xe3hluEn?\xc3\xc9\xed\x06S\x9a\x93\x03T\x0d&\x85\xf4\x86\x16L\xd8}\x95Y-\xe0\x011\xde\x89G@ \xdb\xcd\xe0\xf0\x92\xb1\xbb\x80\xc6L\x95\xd6Os\xd8\xc5\x94\xa0\xf3[\xd5\x0c\xc9\x06$,\xf1\xb1\xe6|\x80D\xcafQ\x1d#[\xa8+o\xb3\xa9\xda\x7f\x86\xc7\x93\xd8\xdb\xe9\xbe\x1a\xb7R\xbc\x05\x08v\n\x13\xe3\xfb\x18iG\xf4\xbahU\xa1\x90\xfc\xaf$\xbf\xa2YPeL\xec\xbbR\x14\xd9\x85\"\xbb\xe7\x16\xc5\x10\xa2\xe7\x85\x1aW\xd6\xda\x9f;\xea\xe6Ip\xdan0\x1a\x81mu\xd1\x06\xa9Y\xcf]\xf3`\xcd\xe5U\xb4l\xfc\x0b\xb2g2\x06T\xdak\x81^c\xb1p\x05\x95A\xb6\xb7\x13\x08\x16h\xc3\x12\x9aP\x8ef\x89E\xf5\x1d\xcc\x95\x81\xdcNe4\x8f\xa6\x92\x92U\xb8V\x0bip\xeb\x83\xbeyp\xab\x95fa\xc2\xf7\xf6m\x11\xe5\xfap\x83\x81\xab\x83='bS\x92m\xe28\x1b6\xbd+\x12\xcb\xfe3\x1c\xcb\xed?{j \x1bWo+\xd8/\x03j\xf2xH\xaa\x8e\x8aB\x9a.e(\x882\x91\xe6\xd9\xb2\x9a\xb2\xe4i\xcd\xfd\x8f\x18\xa4&\x8cR\xb0\xae86Jku\xa5\x8c&^-\xed\x1f9Knj\x1f\xa0\xd9\xb2Y\x9dH\xad} asRs)T.\xb2l\x0c!P\xc9\x01\xb9\x1c\x92l\x9c\xb0\x94\x87\xebN\x97\xaejr\xc1\xc7\xdd\xd6\x04\xfc\xba\xe9\xa2\xa6\xaf\x9a\xafF\x95r\x1f\xf5\xac\x98\x91C\xb4\xf2b3V<\xac\xc3g\xe6\x0eRIl*y\x16H}.\xad\xd7D\x15\xdf\xf9\x01D\xe0\x96_\x81\x18\xcb\xa6\x1f\x0f\x99\xac\xafZ\xaa\x0d\xfb\x94\x88%\x15TW.\x85\xd0\xc1\xee\x8c\x8e~\xdf\x19=\x1bo\x8f\xce\xb7\xa7\x83\x87A\xf3\x98}8\x9d\xed\x8c\x9e\x9d\xff\xe5\xcf\x0f\x9bG\xed\xc3\xbf\xbb\xbf=\xfc\xed\xe1\xa1{\xb8\xf5\xdb\xc3\xc1\xec\xef\xbf\x1d\xfe\x96\x9e\xffe\xe0\xfev8\xfb;\xfc:\xac\x97\x02\xb3\x04\xe7\x0fgH\x9c\xaf\xe2\xcf\x17\xf1\xe7\xb7\xdf\xc4\xdf\xbf\x8b?\xff\xe5\x9ck\x03\xa1\x99\xf3B\xa4|\xef\x0c\xc9w\xcew\x90\x07q\x80E\x81\x04\xfeF\xf07s\xce\x07\xcd\xd3{\xe6|WV\x15\xd6\x00\xe6\x00\xf0\x1f\xa2\xf8C\xf1\xe7P\xfcy.\xfe\xfc\xaf\xb2\x90W+\x14C\xa1\x12\xfe\x7f95s\n\x1fFd\xb6-\x87\xf4h\xf4\xb7\x8b\xd1\xf9\x1f;\xc3'{_\xeb\xa3\xb0T\x83\x8f\x80\x0e\xdc\xf1_\x06u\xf85ja\xf8\xdftM\xa5!\x1b\xce\x958\x06\x80\xd3\xe0(j\xd6{\xabo\xff\x89\x05\xfa \x88\xcb\x84V.r,\x86\x89s[\x99\x05\x8f\x976\x83\xc8y`\xe3\xdf\x1ch\x84\xd3\x92\x99Zs\xe7-%Uk\xacEE\x83:\x87\xedF\x9d%\xfb\xe8Yri\x93q\xfc\xff\xec\xbd\xeb~\xdbF\x928\xfa}\x9e\xa2\x84\xec8@\x08R\xa4\xe4+mZ\xeb\xc8\xcaF3\x89\xedc\xd93\xbb\x87V\xf4\x87\xc8&\x89\x18\x048\x00\xa8K\xc6\xdeg9\xcfr\x9e\xec\xff\xeb\xaa\xeeF\x03\xe8\x06@\xdb\xc9dv\x07\x1fl\x11\xe8{\xd7\xbd\xab\xab\xe8\xfa:\x17<\x06a\xa6\\\x8d\xc9\xbc\xa2S\x95\xa6\xe4\xb5\xd2\x1b/4R\xa7\x94(\xb7\x1a@\xdde\x0e\xc7\xa1Q)I\xe9\xdb\xec3\xe2\x12\xbaF,-)\x05^\x05i\xb0f9K\xe1\xebm\x1a}M\x19\x05.\x19\x04\"gU-\x81\x80\xc9Q=,<\x01_.\\\xe7\xc81(s[\x94Q\x8b\x14g\\h\xd3\xea|\xe5xp\xc4\xe9\x02\x8c9a\xa8\xd7\x8f(S\xc6&\n\xf3\x9a\x97z4\x1d\x9e\xc3\x04\xff+\xaeV\xbd{\xb7\xbfD\xf2d\x18\xf0%\xa6\xfb\x99@4\xf89 \xe3Z{|\xf5x\x91\xcbA\x9e\x86k\xd7\xf3a\x0fS\x8d\xcb\xb4\xc54\n>\xe6\x06\xf3\x17\xef\xe7\x02&\x90\x91#\xc3\xa5Ew\xbd(\x07\xf0\x16\xcc\xff\xb2\xcc\xf9/\xeb\x02\xc3\x05J\xc1\x17\\\xf8>\x92\x81\xd0\xa4\xd4\xc1\xdfV\xa4\x8e\x1c\x8e\xe0V\x80\x9bV\x18\xc3\x96\xe6\xa9;\xf2T\x10n\xe3\x07(\xa2\xad\xc9N\x1c\xa7\xd2\xc5\xdf?\x8a82e\\\xac-\xfe5\xd7\xd6\xcd\x8b\x82\x91\xffl\x8by\x02\x13py\xe5\xeb\xe9\xf0\xdc\x1b\xe4\xc9\x0f\xc95K\x8f\x83\xcc\xe8>^\x15\x08O|\xa0-\x15\x13\xbb\xaey\x1f@m\xb4x\x19\x81\xab\xa6\x18\xc1\xf0r\xb0\xc6H\xea\xfb?q\x96=\xfd\xe9\xdf\xdf\xed\x9f\xf7\xfe]\xfc\xbfo\xbc\xef\xca\x87\x8dn\x83\xfb\xfb\x0e\xc2\x8e\xea~\xe8\xc3\x81a\xd4{7\xd4\xdd\x9d;\xb0\x9e^\xe3\x8dZ\xb74\xec\x03\xaf&\xd5V#\x91\xd6\xe7\xb0\x87m\xf1-,\x9a\xdf[N\xaf\xcd\x97t\x95&}\xe6\xc3\xb1\x8f\x9e\x87\xfd\x91\x8f\xde\x82\xc3\xc7\xf0\x0c\x9e\xc0F]\x85zfNP\xc6\x1f\x81\xec\xeeK\x1c\xbeD\xf4\xcd\xf4\xd9\xb9\x88/\xdc'tz\xcf\x87\xf4\x12\x9e\xc0{z\xcd\xfb{iP\xaa\xb8^J-\x1e\x13)\xa1\xcaGpY8\xffpJ\xf2\xef\x98\xa9\xbb\xf6\xd2\x87\xf7\xa2\xdf3ZO\xbcw0\xf4\xe1\xd8S\x90\x81\xaf\x8e1\xa1}YM\x98\xb3Y2go_\x9f\xaa E\xee\x99\xe7\xc9\xb5\xb1(\xbd\xda\x82-\xba,\x18_\xf2\x97\x8f\x8bi\x96\x17n\xf1y\x0bG\x15d\xb1K \xfce\xddG[\x95\xf7\x95Uy\xef)\x12\x94f\xec\xfb$\xcb]\xaf\xae\x14\x95\x7f\x7f\xf8\x00\x8e%\xb3\xd6+<\xd7&\x9c(U\x12\x8e\xe7\xce\xb9\xe9[\xe9\x974'\xf4adP\xd5\x11\xec_\x99\xef\x81+\x00\x7fS\x1d\xb2\xa0\xec\xfb\xef\x06\xfb\x9e\x0f?r\x82\x83\xbb\xe8\xc3\x1b\xb9b\xb4\xa1?6\xee$\x88Y\x9e\xc2\x04\xdeL\x9f\xb5\\\xa2?Et<\x15\xd4e\xdezq^\x0d\xffgA\x85_\xd0\x10_\xc3\x04N\x15\xa0\xbd\x80'\xf0\xfa1\xbc\xe0\xa3<\x1d\xccVAz\x9c\xcc\xd9\xb3\xdc}\xe1\xc1S\x18\x1d<\x80#\xf8\x19z\x13pn8\xcf\xc5?O\xa7/\x1a\xc6\nrY\x7f\xee\x97\x8b~ \x19\xc2\x198\x1e\xf4\xe0\xd2\x80\x15\xcf\x8b\x12\xedc\xb9LY\xf0\xbe\xb1T\xdd\xbc\xd4\xfc\xa5\xfe\xd6\x88GO\xe1\xe0\xde=\x99\xeeA\x1b\xbd\xe3H\xc9\xc0\x86\xe8eV\xec\xc3+-vvQ%\x1d\xe4\xc9\xb3\xb3\xe3\xd3\xd3\xf2\x17\xd3\x05b\x0e2\x7f\x93\xbd\xa0\x15\xe6\x08\x9c1\n\xa1\xea\xcd\x98\x83\xbeq\xbe\xdfu%D:\xe9\xfb\x0ez\xf07]\xe8\xeai\x8d\xf0))\x01\xc8\xba\nRb\xf2\xcd\xeb\xdb\x07\xce\xbb9\xccp\xea~)\x08\x9d\x06H\x97^+\x1f\xbf\x9a\x9e\x9c[.E\n:\xc5i\xd6\xac\xe06\xad\xa4\x8a/\xf5/\xbc\x8e\x95L\xf1\x8e\x05//\xb8\xd1/\x8d\xa8\xcf\x1b\xfd\x96\x8b\xd8q\x8dm\xfe\xd2\x80\x02\xdf\"\xc9\xff\x05\x97\x05\xabg\xb3`\xc3x_\x8a\x17!y\xfe\xc5#\x84\xfa\xd6L\xde\xeb\xf0^\x97A\xffR\xe2\xad\\\x92/\x18\xef_\xb4\xbd&\xcb\x9e\x92\xbe\xfeR\xe1\x8aC\x1f\xfeR\x05`\xde\xfc\xf7\xe5\xe6\x8f\xaa\x88\xaf\xad\xe9\xf7u\xf1]u\xf7\xbdW\x11\xb1\x8b/RH)\xc6*\xcb\x94\xa4||\xe9\xd5G\xfd\xfd\x8eb\xfdeQR\xd3A8\xb1[NO\x10\x90\xcb\xb8\xa1\x82w\xab\xd2\xa6\xfa\\9\xabj62\xbb\x18\x0d\xc8\x04e\x05e\xd0\xea\xd8\x04\x8d\xbf\xaa\x88\xb54\xc1&R t\xaf\xbfA\x0f\xfe\xda\x80\x89\xba\xba&\xf43\xfc[\x1a\x16+JP%^p\xdd\xc8i:eU\xd4\x05\x05P\xc3\xa0\x992~\xe2?\x06Lc\x9e\xa7\xc5\x199|\xb6\x1f\xfa\x9c\x88\x92 \x7f\x02\\N\xae\x03\xae\x8aM\xac4'\xec\xbbNhc\xf3&\xd4\x0b\xa6Z\xcc\xe2\x95\xadPh *\x1b @\x96\x87YP\xed#2\xcb\xdd!\xf5\x14+\xe6\x18#\xc1*\x9c\xd1\xb0.\x86\xe0p\xberD\xc0\xc7r]\x0ex\xfc[\x0f\x8f\xad\xb6r\xe2\x18\xa8\xabR\x94/\x14-\xca\x16ij\x0fB>Ht7/phz\xf4\xd5y)ZOSLQ#B\x96\x89\x8a\xc7\xe5E\xec{\xab:q\xber|p\xfexp\xe8\xe0\xd7\xd4FEL\x87<\x96\x83\x18\xdc\xa2\xf2\xe1\x8b~.\xe3)\xba\xd5\xd2\x97\xe1\xf4\xc7du\xac\x18\x1d\xcd6\x91\xdcl\x16\x85\xe24K\x1b\xa1O\xd4\xb0\x81\"\x97\xe2\xb7`\xbb\x14\xc2\xa5\x8aQ\x9e\x8f\x14e\xf8\x18\x02x\xa2\"\x84>\x86\xc0\x9ef\x1d\xfdO\xa6\x81\xc9\x83q\xba=\x17\x086\xdd\x9e7\x8c\x8eB\x93\nQ\x02\xbd&V>\x97\xaa\xc9\x96\xc89H\x11\x0cH\x1d\xf5i\xdc$\xae\xcb\x0eL\xe1\x1c\x85\x82\x90\xd4\xba\xd1\x9c\x93\xd5\xc3\xac\xa2Uu\xf8\x18\"x\x02E\xd6\xf9\xa8Y\\\x9c\xc1\x04\xb2id\x11\x17\x1d9\x16B\xb5\x19\xe1\xf1tF\xd1\x08f\x06\xf1\xd5z\\\xbe\x9c\xc6jf\xe2:zI\xc0\x88\xcb\xd2E\xacNN\xeb2\x86ya[6\xadXW@g_\xf5\x8bHU\xd3\xa2\xa3\xb4\xbe\x9c\x16u\xcem+Z\n\x96T\xdd\x9e\x0dm\xcf\xa6dB\xda\xb4\x1b\x1e0\x04\xf1t\xd3\xa0\xcc\xc7\xd39\xed\xc8\xdc\x12K\xcc\xf8\xb6\x11L;l,\xa1\x82f\x95-\x16\xc8\xe7\xb8\xc09\xf8\x87\x0f\xb0./\\i?\x99\xfaQ\x9f\\CD\xb7R@D\x97U\xc4\x16O\x9a\xf4\xf7\xb9\"\xb0\xd2X\xee\x9e\xcb\xa4\x8a\xb8\x1a\x90=\xc0\xabEx\x92O1\x83\xa2\x162*V\xd2E]V\xd6\xaf=$\x07\x1c\xa8VB+\\)\xe3\x03~]\xe9\xfe\xf8\xf5\xcf\xa5\xf5Y c\xc3\xbe!\xdf\xbbmC\x94\xf0\xcf\xc4\x9f\xbcM)\xff3\xfa\xcb\x17\xd8G4LL\x93+\x0b\xb14\x922\xfc\xc3\xd7\xb1tR\x999\x13\xeat,}+\x18\xfeQ\x9a\xc2\x87\x0f\x107H\xff @\xfc\xaa\x8c\xe8\x16\xc1R>x\x04\xd8\xa2\x03\xf0G\xd1\x90+\xe8\xc1m\x87\x05T\x18\xa1y\x99\xe8\x02\x91\xa2\xd4\x9f@\x83\xe4IU\x99\xce9\xe2(\xa1x[H3\xf5\x05\xb8(\xed\x173\xb6\xc4:\xb5t\x0d\x13\xb8\xe0\x8d\\\xd2\x16a\x9bD\x17E\xedz\x9d\x13\x98\xc0u\xfd\xf5MmR\xdad\nL\xe4\xfdL\x0d\x11\x17\xcf8\n\xafJ\xb4\xa0<\x90z\x1b\x1a\xb9\x06:\xfc\xd0X\x8bA9?\x13\x1c\xa5\x84\xa7\x1a\xdc\x92sN\xb1\x08\xae\xe0\xe77\x1c\x81\x8f\xe8\xbf\x89\xfc>\x86\x1b\x85\xb0\xf4\xca\xf34t\xe2\x0d\x97YM\x99@P_\xac\xdc5\xabu\xbd\xa2\xaeW\xd45\x93]\x17\xb4\x82\xa9\xae\x15q\xc2\x0c\x7f>n\xedu\xad-D\x135+^\xef\xc23\x13\x01)\xca\x90R\xa6\xba\x8e\x15\xb6[ B\xa9.\xbe<\xd2\x7f\x8c\xb5\xba>t%T\x1c\xbc*WY\x903\xf0\x8d]\xa9\x13[<\nso\xe8*\x8b\x0f7\x83M\xb2\xe1\x18\xc9\xdf\xdcH\x17\x96\x95\xd7\xb5[K\x7fx\x08\xffb\x1bE/\xd3\xb71Et\x9e\xbb\xb2\x19\xa3|\x8c\xe0\xe7\x95\x17M\xad\xfa\x8d\xe4A>\xb8\xaf\xb8\xd2\xbc\xe7\x16@H\x7f\x15\n\xed\xbf;\x1eyD\x17\xdf\x04b\xfc\xbb#\x8e\x92\x14\xf1~U4\xac:+\x0d\xe1U\xc1\xfd\x1a\x88`\x87\x85\xf2A.\x89[`=\x8eF{/\xe9?\xdf\"E\x93\xb5\xf2p\xa4\x13\x901g\xa2\xa8\xb1\xc9\x11\x1c\x15\x83\xc1\x8f\x9f*\x02\xee\xdd(xQ\x93\xdcT\xbd\xf6J\xbd\x8a\xb1\n\xad\xb5\x18D!\x9dJ\xd2\xd1*\xe9+\x99\xe5\x98v\x1e\x8dw\xfd\x91\x87^\xb0\xefiA\n\xca.\xff\xba)\x0c\xfaB_w\x06\x84e\xc7\x88q\x03\xf9\xcb\xd3\x10\xf0X\x9c\xef\xfa\xf0\x12\xfb\x92\xb2\xe6Kx\x8a\x12\xe8\xcb~\xdf\x03\xd9\x0e\x1e\xc0\xdeL_\x9e{\x9c\xd4!L\xcd\x98\xfbR\xdc\x7f+:\xe0J\x7f\xf9\xb3O\xa6\xe81<\xc3\x81\xd5>\xf6\xfb\x06Z\xbcG\xe7\xd5'\x16\xc3\xf7c^\xed1<\xf34*\xcb\xc7Pi\x89\xb2\x10\xead\x9a\xaf\x95\xb8\xfb\xf0\xf0\xfe\xdd\x07fM\x8ck\xfc\x87\xf7\xcd\xdff\x18f\xdc\xf8\x89\x83\xf9\x81\xa5\xda\x867\xf9\xd0\xfcm\x0e\x13xP\xbd\x13'\x1f\x8ez\x0f\x0e\xcc\xdf\xb8n9:\xb0\xb4\x8a\x91\xf1\xfa\x16]s\x89~\xc97q\xbf\xbfo.\xc0\x05\xa1\xfd\xe9O\xefn\x0e\x86\xfdw7\x0fN\xce-\xe5.\xb1\xdc\xbb\x9b\x83\x93w\xdb\xc3\xe1\xf0\xe0\xdd\xf6\xbb\xef\x86'\xfc\xdf\xfb\xa3\xf3\xfd\xa5\xb9\xd2\x855\x8f\n\x7f\x92+\x96.\xa2\xe4z\x0c\xceK\xf5'Em\x8c\x19\x9bgp\x1d\xceY\na\x9c\xb3%K3\xc8\x13\xd8\xa4\xc9\x8ceY\x83b\xed\xc4I\xde\xbf\x0c\xb2p\xe6\x8c\xc19\x8d\"\xb6\x0c\"\xd1*\x17\x1dn\x1e\x0e\xc1\x8d\x93\x1c\x02\xc0R\x80h\xb4I\xc28\xf7\x9a\x9a\x0d\xe3\xab \n\xe7}l \x9b\xa6\x17\xd4\xb49\xf1\x9d!\x9d\n\x08\xc55\x82>\xcc\xcc\x9f\xb9\x8e\xfac\x90\xaf\x06\x8b(\xb1\xe5\xae\xe4:\x01\x19\xb5\x07\x8b4Y\x1f\x0bo\x1a\xcd\x9dX>\xca\xad\xf8\xcc|<\x00*\xc6\xfe\xeb ^\n/\xdc\x8b)3\xdaE\xed\xad\x1f[o\xd4A\xd5\x1e\xaeB\x85\xa2I|z\xfe\x18b\x0c\xc4\x9eR\x84X\n]n1hI?\xe5\x9d\xc6\xf6\xbeql\xc5\xb0\n\x89\xc2\x0e\x07\xa9\xe1\x00P}\x93\x02y!\xef\x82<\xf8\x89\xb98\xd5\x03\xf4\xfbC\xceON=)\xf4\xe0\xd8\xa5\x13Su\xe6r\xe9s\xc9\xd6S6@\xca \xeb\x15N;;\xcd\xfe\x99}\xdf\xd5\xb6P\xac\x06\xda\x0e\x1f\xaf:\x0d}\xe1D-\x05\xef\x84\xae\xa9\xb9\xa4jk\xee[I\xaf\xe7y\x1c\xb5\xee\xdd;xt\x9f8\xc7\x93 \xdc\xbb\x7f8z\x84R\x0b\xaf\x08G\xfc\xc5\xc1\x10\xe3\xa2\xdc\xbf{ot\x00\xe24\xad\xde\x96G\x01\xce\xb8\xbc\xea\xba\xa3\xe1\xc1!\xdc\xe1\xbb\xf7\xe4 \x8c\x86(\xc5\x88w1\xffq\xff\xde\xbd\xc3\xfb(X\x89*9\x17\xa0\xb8r0\x06\xf5\xe6\x0b\xc2\xd2K\xfbj\x8a\xf6\x10\x13\x9a\x8f\xe4\xe4#O\x9el\x00\x05\xfa\xbd\xa1\xa78\xd7{\xa0\x0e}\n\xa3!\xdc\x01\\\x9e\x0f\xb4\x1dB\xa0\xa1\xb5\xff\x00b\xe5\x18\x1d*\xf2&\x0c!\xcd\x01\xcf\x02\x05\xb4\xed\x08l\xaf\x1aQM\xcd\xa5\x07\x07\x07\xd0\x83\x07\xf7\xe0\x1bp\x19<\x81\x83\xfb\x1e\xf4\xc1u\x87\x18\xcd\x0c7\xfb\xden=\xbf\xb1\xdd<\x90\xcf\x95\xb8\xfd`I\x89\x82\xb8\x80\x98 Gp\xe22\xd8\x879\x06\x95\x03\xbe\xae\xc2G\x81\xde\xe7\xdec\xdc\x8fk\xf8\x06\x16\xf8\xf91G\xe4 D\x1e\xae6\x95\xban\x06\xbb\x13\x97\xe3\xbe{\x8d~3\xf0\x0d\xf0*._\x99\x8d\xb7\xdb\xc4\x7f\xb4\xc3\x98\x86\xdaz\xce\x18L\x075\xf7a\xe9\xc3-9\xe2\x98\x8c\x9a\xf2\xb9\xd0I\xb6\xb5\xd4\xb5\xf9\x16\xbe|8\xbf\xba\xb2\x7f>\xae\x1b\xc8\xe4\x83\xfb\"(\x85\xeeA\xbd\xf6f\x82\x82\xd0\xf3\xe1\xc4\xbdF<\x86\xa7\xc0'xc\xe8\xea\x86\xf0\x9d\xca\xf1\x89\xfe\x11\xb3\x03_J\x0b\xd1u\xaf\x87\xa1\xa7n\xba\xfa\xfcA\x81\xfb/\xdd\xcb\xddp\xfc\xf4sq\xdc\x87\x0b\x9fC\x9b\xb8>QMr!\x1f\x04\xccK\xe9\xc3\xf5\x0c]\xb6\xa4\xb0\x96#\n\xa3\xa8$\x84\x83U\xc9{\xe1\x92c\\\xe0\x11tN\x83s\x8e\x9e\x02\xd5\xde\x13j\xdd\xb85\xaf\xa0R\xc7)\x06{\x99\xc0{\xd5g\xa2\xd5^{\x84\xd9\x97\xed\xa8\xc5\x91)k\x19\xdcS\x91\x81\xfc\x16\x9e\x88,\xe6\xbc\xd6m\x837\xa8h\xba\x0fy\x81\x1a1G\x0d\xf7\x02c\x82pBn\x02\xda\x98C\x12U\xe4\x84\xfe\x82\x96rk\x1a\x9f\xb5o\x10\xa6\xc7\xd2\xea\xe2\xf8{\xbd\x18\xa1\xb8\xde\xef-P\xda3\xfbb\xc9\x07g\xc6IK\xec\xa3\x8e\x1a=\x96\xc8\xcc\xd1q\xce\x919\x14\xc8<\xe7\x0b\x17j\xc8<\xc70(\xdec\x98\x0bd\xe68\xb8\x81>\x87<\xa9\xe8,\xfd\x02\x04^\xb9K.\xf3\xc2\x1f98\x0e=O8\x15\x9c\xb8\xc7\x0dF(O\xf9\xb4\x13OAj\xafW\x97\xf0\xf4\xe7c\xaf\x17\xf3R\xf5\x84S\xd0\x86\xc7\xef\x9b\x84\xa4\xea\x9b\xadU\x17\xbebi\x16&\xf1\x18\x1c4\xe6X\xb4\xd0\xed,;0\xe5\xb2\x96\x0f] \x1a\xc33;\x9b%\x1f\xb01\xbc4O\xd5b\xb4\x10\xed\xfeh\xfe,\xdb<5\x7f\x16.\xf6\xe3\x8e\x12\xb1\\\xd8\xee2\xb4V\xebv\x90\xb3,\xa7\x98|\xceM\xdc\xef;\xd0#\xd2iJ\x99-\x9f\x8f\x16\x02n\x9b\xcf\xdb8\xa4\x19w\x1b\xdfg\xcdh\xa9\xcd\xe8GW\xe6\xa6\xb9[\xb9k\xf8i\xf3\xab\x83\xac\x0fZ\xbeD\x94n\xac\xa6Y\xf9\x88qn\xeb\x8d\x15\xc1nP,g\x14\x02\xd3\xd5c}$\x15\xffC\xdd\xe3\xcf\x90\xe6\x86\xffy8\xb2d\xbb\xe9\x14\xdfC\xef\xbc<\x1f\xe9\"\xd8\xb6\xabb\xbe\xa6\x0c%\xe5\xb9\xf8\x95\xe6\xc9\x91\xaak\xf3\x16K\xab\x88\xf58i\xeb\xec\xc56\x8a:v%\"\x85vjR;1\xde\xad\xf5\x1dC\x89u\xda\xcb|@\x84 \x0d\xf8\xf2\x16z\xec>|\xf4\x88+\xb7\x03\"Kd\xdd\x97\xde\xc9@q\xaa\xba%\xf3.\xf7\xaa^+\x91,m\x8a5\xd2\x12\x99J%\xb1\xa9e\xf0\x81\x96\xb0\x87>\xd4l\xf8x\x84\x81G\x89w\x1cbzxC\xd8\x99\x18\xf2\x8a\x07\x86L\x90\xa19M1zC\x0c\x853D\xe5\xc89\xa8\xb7\x8cqE\xde\xf5\xf6+\xc29\xd3\x0ckU;\x8ct\x01\x1d\xb1\xc3\xca\x888\xac;1\xe6\xa3\xd1q \x1c\xac\x83\x9b?\xb3[\x14v0\x85\xa9zch:\xd2\xcdW\xa5\xaf\x99\x0c\xf5\x19I\xc9 \x13PV\x1bQ\xd61J\xa4\n3\x8c,\n\xbd\x9e1\x833zLJ\xa9{\xe5\xa3\xc9\x9eMg\xc5\xfd\xff-\xfaQ\x0fm\xc6\xc55\x17\xaf\xd5\x81\xa7)5\xc6\x1a\xed\xd7p\x04\xee\x02\xcb\x16gTk!D\xa9wk!\x8c\x8eEY\xfa\x8c\xc7\x94s\xf3\xed\xe1\x85\xe7\x83\xe5b\xf1\x86k\xd6n\xe0\xc3\xdc\xa3\xb0\xd3\xd39\x1e\xb4\xf3\xffI\x16[a\x1cTr\xe0\x9c\xf2\xff}X\x9d\x17\xafV\x16\xec\x87\x02a\x82\x02\x0f\x8a\x89\xe3\xf9\x97\xcc'6\x083\xfc\x9f\x83e\xab\x8by9Q\x90\xb8\xba[CJ\x19&\xb2\x1ecgw\x02\xa1\x8f9m\xf4IWYld\xf8\n\x030atO\x89\x94\xcdA>\xebpB\x95/)gTKm.)\xe5\xe9\x96a\x94\x8bE\x10e\xcc`\x8a\xa4\x06\x05>6\xe7B\xc9\xbe\x0b\xe30g$\xb1\xd0\xc1s\xbd\xbd9[\x04\xdb(ol\xc9q,@\xf3\xd1\xcc\xce\xeb\x84\xb2\x16sX\xb4l\xa7\x97\xbe\xc6\x0dA\xdef\"\x91\xc8\xb3\x1c\x7f\x1eA\xe8\x06(\x9b\xa8\x01\x046\xea\xc0I\xa4\xe1\x16F\xea\x06x\xb5\xc2\x90wW\x8c8qI\xe3\xe3\x9d\xf1\xbf\xba\x08\x92R0\x83\x9e\xb9Of\xb22\n\xa3/\x86\xc2\xb2\xd7\xe4c\xa9\xde\x1c)U<2W\xdc\xd24\x1bF\x84\xf0\xf2\xfb\xa2\x04\xe6`o&\xd6O\x0e\xfa\xeb`\xa3\xe5\x92\\\x07\x9b\x1a\xdb+\x9d\x85M\xcfKV\xcb\xe2\xb8%\xed\xf5<\x99\x035w\xd94\xe5\x05-\xfe*\xd5d\xa8\xa0q{\xcd\x81\xbfy\xbd\xae,\xf9O\xcba,\x99\xd7Y\xb6\xa1 \x97\xbfR\x1a\xd4\xda\xea\xef5\xeb*fb-\x9fn!0\xe5#\xc6\xee\x96\x82.\xe5\x82\xde\xc5\xec\x1ar\xb7\x80(\x97S\x8e\xcb0\x0e\xd2[\xc7\xf3\x8a\xd7\xcee\x90\xb1\xfbw[-\x07V\xa5\xe8\xde]O$M\xed$\xce^iY)\xcdA\xdd\x0f, \xcf\x0f\x87\xe6\x84\xe7\xf7;\x05\xf47\x1c\xc8(\xde3\x01\"\x9d1\x14\x19\x0bb\x91\xb1 uC7\xf6\xd0\xc2\xaa\xc4O_$ \xc6P\xacB\x17\x8e\xd1\xbeV\xb8\xe6 un\x81*}@\x9f6p\xc9 \x84\xbe\x8c\xd7o\x14\xc7`\xf0\x84\xe6\x81\xf0\xe0)\xad\x1a\xaf.j\xa5\x9eN\x14\xd4\x90\x13\xf4n\xc8p\xa5%\xfe5E\x84\x1f\xd57\xf3n\xdb\x86YfL\xb9\x16\xe0\x03\x84m2\x92\xde\xc0^C\xc3\x16\xed\nt2\x9b\x9bQ\xd0\xaa\xaf\xc8\x95-.\xfb\xf9\xb0?\xfd\x89\x02\xf2\xbd\xeb\x7f\xf5o\x7f\xbc\xf3\xf57\xbd\xc1\xbb\x9f.\xfe\xcf\x87\xff>\xdf\x0f\xa5m\xc5\x12\x88L\xfaw\xccVA\x1a\xccrtD\x81\x15\x0b\xe6,\x85E\xc8\xa29\xc4\xc1\x9a\x99\"h(\xf2_\xb2\xd2\x94\xd1\xda2\xe7\x8ef\x87\xb6iW\xf5msg\xa9\xb93\xc9 \xcc\xd4/f7\xba\x19\xc3F$Ak\x88I\x7fK\xbbqWL\xd0\xde\x16\x7f\xe6I\xcc\xc6\xba\x8d\xca\xe0\x10\xa8?\"6\xbb\xd9\xb0\x0b5Rk\x7fkH'%\x06\xbc\x1a\x849\x85\x88\xa7s\xf9)%/\xa5\xb7y\x92\x9e\xef`D\xab\x8f\x13\xe3\x97u\xda\xca\xc4\xbc\x95\xe8\x9f\xb8\x0e6\xa8\xf6\xfb\xe50\x81\x89\x0c>z\x12\xccV\xed\x81\xb1Us\xc1f\xc3\xe29%\xbb\xa9\x8f\x98n`\xa3G\xb5.\xab \x85\xc0\xd0]\x97\xbe\x18:\x98\xb3\xe9\xc8\xe4\x94T\xf4\x88{ \xc4\x93%\xcb5\xa1\xe4E\xb0f\x99\xcb\xbcz\xff\x9d\xe7:\xcd\x1b:\xef\xb4G\xa1\x9d\x9e\xb1\xc1e2\xbf}\x9b\xb1\xb9\x12\x1e_\xa5\xc9:\xcc\xd8 exC\xbaB\x9c\x9eE)\x0b\xe6\xb7\xc0\xffuL\x87jE\x8b\x18\x90\xad\xd3\x00\x83f[\x1e\xbb\x96\x83j\x0f\x02\x0e8\x84$\x8e\x92`\xde\x05\x05\xf8\xc3\xc5\xa6\x94e\xdb(\xb7Y\xe4\xb1I\xc6W\xa0k\x9b\xb1\xcb\x06X\xa1\xb3\x11\xbc\xdb^n\x9bI'_\xab\xef\xc2\x88\xbdFva\xa6R1\xca?&\xe7$I\x0f\x06|w\x9feZ\xb2c\x12\x97:\x8d0k\x826\x94\x9dj9\xef\xabn\xfdP\x99Q\x91b\xd8-\xa5\xe9l\x98A\xc6\x08t\xf5\xaa\x18\x82B\xa4j\xec4\x95\xa8)K\x05\xe2\xa9\x0e\xeb2\xdc\xd1E\x18\x87\xf9\xb7\xc9\xfc\xb6\x93P\xcf\xd7\x85\xaa\xf1\xb6N\xe3\x10\x19\x97\x91\xc6\xe9UL\x07\x01\x1e\x14\x0d\xbda7\xd8\x90\x9d\xf3i\x17\xc1.\xa3\x04\xc3\xda|\x1b%\x97\x9a~\x15f\xaf\xe4\xdf/\x17B^\x91\xed\xf3\xa2\x9d\xdb_$\xe9\xfay\x90\xa3\xf3\xf4w\xe2\xef\x8e\xfd\xc8\xe2\x9d\xfb\xa2\xcb\x05\x18\xcc\x15-\xaco_\xffp\xa6\xbd\xea\xd8\xad\\>M\x9d\xea\xd4{P\xa0\x0c\xe0\xf5d\xb9\xb4\xebJ\x07\x1an\xc1\x84\xe3\x8cL'\xeaC\x0d\x1a8\x1c\xf3\xf5v\xa7\xc6\xfa6\x97Uh\xbe\x07.\x1f\xbcXT\x1e\xf9\x87\x0f\xb0\xa7u\xd0\xb0f\x80WH+\xb2\xac`\x15\xdb8\xdbn\xb8\xa8\xcf\xe6\xf0\xad\x9c\x0d\xaf\xd9\x16\xfc\xada\x95\xecH!s\x94T\xb7\xd0\xe6\xe2H7(\x90Lf\x9ci\xbb\xce,\x89s\x16\xe7}\x1a\"\x1e\x1a\x9a\xb0LE\xc6\x11u\xb3Z]\x1f\x9c\x9c\xdd\xe4\xfb\x9b(\x08\xe3\xc7\\\x8c\xcfX>y\xfb\xe6\xbb\xfeCG\x05\x97-\xb0H\x86\x8cRo\x06\xbc\x95.\xdd\x18\xaayx\xd1\xf5\xd3\x91@\x8d\xa6qz\xc1f\x13\x85\xb3\x80S\xb6\xfd\x9b\xfe\xf5\xf5u\x9f\xa3x\x7f\x9bFda\x9bWgm\x94`\n\xec \nxI4\xa5\x95\xbf\xca\xeb9!\x8521\xef/\xf2\x1b[@j\xbdPy\x11\x0db\x90\xc8\x04P.\xd6\xa5=\x0dz\xad\xcd\xb6\xe2v\xa7\x9e$\x954`\xe1,\xd9r\x8d1\xc9QdS\xe4\x17x5\x082\xe0\x8bnC\xc8\x1d\xc6\xcc\xb1\xadj\x9d\x85BP-\x91\x97\x0e[\xac\xf3\xd8\x1a%8\x92;\xcfq\xd4\xbeO\xa5\xe5\x17X\xc7g\xebz\x83|\xc5bwk2D\x8b\xe1\xe6D\xfeZh\xd2m \x8ak\x05\x06\xc1Q\xda\xfb\xd85i\x88n^\x98\xf74Kx^\xb1\x84OQ\x956\\yq\xf3i#\xeb\x95\xda\x8b\xddU\x0b*+\xa6/D\xa7\x95\xfb\x0c\xb4\xe7\x00\xbe#\xda\x97\x91\xddB\xd1uQ\x8fj,\n \xae\x15\x9dt\xb4\xe7#\x94\xa8\xbah@\xd5\x9f\xb3$\xfe\x9c\xb6\xfft\xf6\xf2\x05\xf9qX\xa9W\xe9\xbdMY\x98Y-\x18\xf2\xcc\xc5U'\x80\x7f\xff\xe8\xa1\xeaP_\x7f\xa4\x15\xba\xb5\xc4x\xe6\x0f\x06\xf5\xddhK,\xab\xeb\x0d\x92\xd06%\xb7\x85m*S\xed\xccR6gq\x1e\x06QFn\xdf\xc5o\xaeF \xf9\x00\x8a\x00\xb7\xe2\x05\xa1X\xe22\xf9FE\xfe[\xb3|\x95\xcc\xb11\xfaS\xbe'\x87\x19\x86\x7f\xf8t*\xaa\x1cx4I\x18\xef\x1cC\xe9\x9d_\xb57\x18\xf6P\x13\x0ci\x96\xca`i^~\xc3\xec\xf3\xd2o\x19\x98\xb3\xf2\xceI\xd6a\xee\xf8\xb0W,NE\x98\xb2/Vn_\xacv\xd2W\x98;\xf3\xe4\xedfc\xcf\x04\x00\x05\x1a\xdc*\x8f\x0ftF\xef\x8f\xb8\xbcit\xe7\xfb\xe8\xe6r0r\xe2\xc5O\xe7?N\xde\xa8\xe8\x87k\xe9\xf8\x84\x7f\xa8\xc2\xe2\x87\x96\xc5)e\x0b\x96\xa6( \xd0[\x17\xdb)BRj\x1d|\x7f\xf2\xecy\xed\x0b]\xc7\xb7\xc0<\xaa\xdex\xd12\x8a\x92k6G\xb6\xf0\x1f'o I\x81\xb7\x06)\xfb\xdb\x96eyfB\x08\"rR\x83w\xe3nV\x99E\x07\xab\x8c \x83MV{L\xb1!/\xdf\xddq\x0cV\xc3F3B\xabxP\xbam8i\xbam\xc8\x9f\x94.\xdd\x93\x05]\xcb&\xd2\xc3l\"\xd0V\x1d\x0f\xf7\x04\xf3\x9b8\xc6\x06\xec\xcc3\x97\x16P\x83[\x10\xd7\x91\x0d\xaf\x13\x83\xf4 \x16S[W\xeb\xf6\xa6}_\x93\x86\x0d\x951\xf4\xd3\xa3w\xf1\xfe.\xbbY\xdb\xacq\xdb\xd5\xd0b\xa3\x08\x8a\xec\xe2C\xed\xb6\xbf\xfeH\x7f\x07\xb9qc\xa7\xb9A\xd0\xf7*\xf5\xab\x9e\xb5\xf2\xf9\x9c=\x98[\xf9*q\x84\\O\xb8B\xaa\xf3\x04\x1c\xe1\xea#\x95\xe4,\x0f\xf2-'\xb7\x0e\xfd\xe5`jLN\xf3\xe4\xa71\x1c\x0c\x87\xa2t\xf2^\xc5\x8b\xa5\x8fO'\xfc\xab\"\xe7\xe2\xed\x138TU\xe8\x95\xb49\x14\xbfj\x1da\x9118/\xff,\xc7f\xe7\x05\xbe\xce\xb5r\xfc_\x84\x9a\xab\x90\xa9j@\xd5\xd2/4\xf0\xb0\xc1\x82\xe5\xe68rW\"\x16\xa0\x19*tS\xc2\x18\x9c\x8a%\x01\xa7g\x08w\xc6\x1fy@5\x06\x87\x0e\xa7\xa80\xfaX\xcac*|E_\xcd\x8dp\x85m\x0cN\xa1\xd0h\x8dp\x0d\xa3\xf8\xd9*\x00\xf2'Oo[\xcca\xda\xa1\x03o\xdf7eO\x96\xcfG\x98\x05\xe8R\xd7\xd5\xad~odo\xcb\x8c8\xb6l\xc0R\xaa\xe6k#\xfel\xda\x0bM\xfd\x1e\x83\xa3)\x1aT\xa9\x8e\x9ef\xd1\xa8d&\xf4\x10r\xae0\x95\x9dtv:\x95\xfa\xd6\xb9\xe3\x17.P\x85\x1aV\x7f}\x1c\x05\xeb\x0d\x9b\xd7\xbf\x9e\xc6\xf9\xe8\xbe\xb9\x92\xe9\xfdi\x9c\x1f\x1e\x98\x8b\x9b\xde\x7f\x17%\x81\xfd\xc3\xfd\xbb\xe2\x83\xe5z\xea\xba\x93\\\x06\xba\xeb\xc6\x9d;\xc07\xe9/!\xbbn0\xbf\x99\x81\xc0<\x88\xa5\xf4K\x13V\xda0\xe3\x8d7;[\xe9\x8f>\xb4\xc2\x01\xb8\xd5E\x8d\xc4E\xf3@\xebP\x93h-\x11\x9b\xa8\xf8\xbbX\xd9\x11\xa3\x90\x0cB;\x8f\xdd\xd4\xc2\x82$\xcb\"\xf10\xd8L\x99\xe5\x8e\xa1V@$wO\xa0\x07\x8e\x8f\x81\xb1al\xba\x8f\xef\x97\xc6?g\x11\xcbY\xa7\xad\x17EU\x97|\"\x86\xbc\xda\xe5\xf6\x97,\xef\xd4\xb8\xda8\xb9@\xc4F\x82\x8c\x0e\xbb\xf5y\x8e\xcb\xa9R-\x1d\xaf\x82\x9d\x1c\xd0d\x07\x15\x07<77;w\x96\xfb\xca*\x93l\x80\x80\xf2\xea hk_\x08Ym\xb9Y\xe5SI\x96-z\xf4\xacs$\xe7B\xa6\xfc\xe1\xd4\x18\xe3s\xbaqT;\x957\x8c\x11\x9d\";\x98,\xa4u\xd1vkV\xdf\x8f\xba\x83A\xc3 9\xe0)\xb9p\x904\xa32\xfa\xde\x9bM\"\xfaT\xd0\xd5\xe57\x98L\x87\x99\xd8N\xef;\xce\x84\xc5y\x1a\xfe\x16S\xe9\xb6/S\x0eL\x06\xcf\x0fh\x99R\xc51H\x9b\xa1\xc9E\xc8\xb0\x00\x96\xb3\xf8[\xe4\xf3\xcfO~8ys\xc2\xf9%W\xd8}\xa1\x9e\xfb\xe0\xbc|\xf5\xe6\xf4\xe5\x8b3\xfe\xe7\xab\x97g\xf8\xe9\xd5\xdb7\x8ea\x81fZ\x97\xb3(\x89Y\x97\x15\xd7\xa4\xb2\x19ZP\xfc\x86\x15\xbcL\xe6\xb7\xfa)\xdbi\x1cZ\xee\xd8\x1aWP\xa4\xcb\xd7\xc6\xe9\xa9\x97\xf3\xd2\xcb\xf9gNe^9\xf9o\x9a\x14i\x0fc]\xdb\xb0k\x84\x85\xaa1\xae\xaa'\xf6JB\xeb\x18K5D\xd3M\x1a\x94\xcfm\x1a\x8d\x95\x9a\xb2\xc3*\xcf\x07\x9d\xfdi$\xba\xd1\x92\x91\xc5\xa8}\xa1\x1a\x82\x82\xe8\xcb\xe3X\"h5\x9b\xcf\x98R4q\x16N\xd5\xf3\x11\xcc\xd2\xd0\x95\x88c==\x1c\x8e|8\x1c\x1e\xf0\x7f\x0e\xf9?\x0f\xf8?\x0f\x0d\xe82\x1f\xa4l\x1e\xa6\x1d\xd2\x8d\xcb'\\\xa8\xfc.\x97\x9a\x95O\xb7\x96i\x11\xb7\x94\xbb\xa9Pjg\xc9\xdcz@_\x02\xdd\xae\xfb\xd0\x05\xe2\x9a\x95\xa7(\xa1\xa3\xe6\xc6\xcb\xc6;\x80\x1e\x1b|\xafT\xee\x84\xff|M\x06A\x98\xc0\x8c~f\x9b$\xc6{\x9ds\xfe\x1b5\xe7\xae\xab\xaf\xadQ\xcdi-n\x10v@\xb7\xbe \x99\xc3^\x9aml\xa1(\xfc\x9f?\xfe\xf0}\x9eo\xc4<\xec\xa6\x9apG\xcf8\xd0\xb0\xaf\xb9\x14h;\x1e\xb6\xd2\xa7r\x0dB\xc4\xb0\x13\x91\x92\x8f\x02\x9d\x8d\x1br\xc1\xf9Y\x14\xc9m\x13\x9b\xeb\x8a\xa8\xbev\x97\x110#\xa9\xfe0a|qR\xd1\xf8\xdb\xd7?\xa0\xca\x1c\xc2\x11\x84\x03\xed-\x8c\x81\x95\xfdI\xfe\xb3/\xf6\xa3\xcf+\xb5\xf8\xbcH\x93\xa2\xea\xc8\xd0\x0b\xe6\xe9\x97?\xf8257\x19\xbb\x82\xc7\xe0%x;\xe6\xf8\x08\x16\x9d\xa9\xb1|\xd2\xaak\xe8\x0b\x96_'\xe9{i^\x87E\x10Fln\xf2\xfd\x90\x8f\xe8:\x0f\xd7,\xd9v:o\x97\xcf\x17\xeb|\xc3b7Q\xc7Q \x9d\x7fa\xaa\x1d'\x8cg\xd1v\xce\xe8\xf0!)\x9d\xf6p\xc9*\x1c\\\x87\xf9\xea\xb8tND\x15\xd5\x16\xddn\xe46\x96|\xc1\\m\x17\x05\x17!/\x0c>\x00 B;\xf9G\xcb'\xe4\xea\x95\x80:B\x03\x8b\xbb\xb4|\xb8$\xc9+\xc5sWsoO\xb4C\xb7#:\x8a\x1b\xeb/mR\xa9\x99\xd8\"\xf9\x1cl\x92\xe8v\x11F\x91\xc9+X\xfd\xe5:[y\xd1_\xbfk\x90\xb1h\x01G\xf4\xdfXS\xb1>\xeb\xa2l\xec>\x1a\x9a\xae\xaf\xf0\xf7\x0f\xcd\x17\x92\x1e>\xb2\xdc<*\xef\n\x85!\xe6\x84\xb0\xdc\n\x1e2\x8f!)\xbfUQ\x02\xc6\xb5\x9c\xf7\x9f9\xbf\xc3\x87\xd5y$j\x1e\xf5\xf9\xd5!\xeb2\x0df\xef\x19\x9fHg\xd3\x00f\x84\x9b\x9e\xd7e*\x83\x0d+\x8c\xe7\xe1\x8c\x95Zo\xe7\xab\xd4\x01f\x96\xa3\xe4s]zJ\xd9\x86\x05\xad10@\xeb\xa5\xdej\x19d\xeb\xf7\xd2\x9e\x079+Y\xcdN\xcf^\x92\xe1\xac\\\xd6\x1c\x8dg\xce\xa2p\xcd\x15\xb31\xde\x0e\xae}\x97\xc1n\xf6\x0cR-}K\xc7\x90\x8a\xe0\x13\xb6\"\x7fA]\xfde\x1c\xdd\x8e\x8d9\x063\x96\x86A\x14\xfe\xc2\xf8\\vX\xad\xa0v{U>\x86\xbd\xc8\xde\x87\x9b\x17\xdb(\xca,c@p\xe6\x05\xbe\x0f\xe2y\x84\x91Q*V\xf3J\xa3\xba\xc6\x0eL\x04~Q\xf1\xc82\x1f\"\x9f\x8buE\x88\x04\xd3l\xa4%\xdb\xc0R\xd1\xdbZv\xa0{\x82F\x1eV\x89\xb8Xwe\xba !\xdd\x82\xaft\x7f\x0e\xbe\xb6Tq\xe36\xd6RW\xc2\xaf\x9a\x04\xfdP\xb9LQ\x06\xb4\x15\xa7\x93|D[\x01\x0c\xe8\xfbf\xb8\xe2\xcd\x9f+\xf4\x8fm\x81u\xb0{\x9c_\xa1\x84U\x8f\x97A\xefe \x80\xea\x87t\x10f\xe2V\xc1\x95\xa7\x0d\xff\x08\xa6s\x17#\xc4\xc3\xb8:\x07\x8f#\xfb\x84\xa3\xfd\xdc\xcd\xdc\xab\xd2\xa7s\x18\xf3\x9a\xb1^F\xb8x\\y\x9eA\xa5\xe2\x9b\xbd\xf6\xd1~n\xb2\xe0\xe0\x96\x15\xcc\xf0J\x0d\xd1\x10\xff\x8f\x97-\xdf7\x8a<\x0f\x8f\x07\"\xcb\xd6\xdaU\xdc\xdbJ\xda3\x13t\x808|\x98\xc1\x11\xdc\x0e\xb2$\xcd\xdd\x19\xdf\xe0. \x9a\x94\xa9\xf3\x92\xbc\xdd.\xe1 \xac\x95\xb7[\xafw\xd9\xa4\x7f_\xc0\x04\xd6\xd3K\x8b\xc1\x0b\xdd\xbd\n\x80\x9d^`&\x07wY\xbd9\xef^yp\x04K\x99S\x86\xb9\xbc\xa8\x0f FP\xf3Z\xd0\x96\xcf\xb3V5\x86\x1e\xb8\\8p\x06|\xe7/T\x9e\xd2\x0b\x95\x9b\xb4\xb9Q\x03\xd1\xaa\xbd\x91\xfb_&CfQ\xa0\x91\x99\xa9s\xfd:\xe1\x0b\x80n\xe5\xa6\x83 \xcb\xc2e\xec\xfe\xfd#606\xc6\xcdQ\x01\x99\x02\x89\x07x\x8aS\xdc\xf7-\xbd\xd7\xc8W!T\x05\x05\x810\xba\xd1\x9c\x88\xfa\xab\x00\x03\xa0_2\x08\xd4\xe4j9E\xaeD\xdc\x1b\x0do\x82\x81bjp\x04[\xed\xd7X\xffV_\x89\x19\n\xc4u\xe2\x11\x0c\xea\xcc\x01\x8e\xcc\xaf\xc7\xb05\xbc\xae\xf7\xb5\xb0\xf7%\xf9\x14u\xa1~a\xcb\xf2W\xbd\xc1\x8d\xb5A\x11\x18\xea\xa8\xf8s\xac\xa8X\xbd\x1d\xae\xa2\x1b\xb9N\xb1\xb1G\xda\xdfES\x86\x05]\xd9\xdb\xca(\xa5\xbc\xf8\x83N\x8b\xea\x0d\\\x15;K\xb0\x85\x9eU\xcf\x93\x1cy\x8e\xf6\xb3^u\xdd\xd0\xb7.n\xd0 Jop\xa5\xf57\xf5\xd6\x97-\xab]H<\xdaji/\x8be+^\xd6\x91\xad\x04\xd4$\xdc{\xea/4\xa2\x0bo\x93r\xd5\"\xf3U\xa7\xc8\x15\x89h0gi\xe6\x17\x1dY\xb0\xf3m\xfc>N\xaec\xa1k@\xb2A\xf1g\x93&W\xe1\x9c\xcd\x8d\xf8)\xc2\xb1\xe2\x80\x8b\xae\xa6\xb2\xa7\ni\xb7l\xda\"\x8c\x08\xa1\xd1\xa1\x95s\x12\xf9\xces1/\\\xfd\x06\xae*\x80\xba/&o\xd7\xab\xd5\x07z\xedc*\x82*oF!D\xc6\xc2)\xe8\x98\xee.:\xe1\xfd\x0bj]\xbd\xf8s\x8d\x9d\xa2\xff\xc2w\xb4h\xc2\xc0R~9\xe6\x8a?*&\xa8\xba\x07X@\xbc\xe1lF}\x1csE\x9f\xeb\x15\x8e^\xa7>\x9b\x1b\x98@8\xbd\xaeL\x06\x83\xc8\xb8U\x96\x1f{\x18\x0d\xeb\xce\x1d\xc9\xdc\xabw\x1c\x15\x0f?#\x1e~\x06O\xe0V\xe3\xe1g6\xe1\xf6\x18&p;=3\xf0\xefE\x89w\xc7\xd3c\xe2\xdd|\x07N$\xb7\xcd\\\xfe\x1e\xa3\xf8\xde(\x0e\nG0\x97$\x83C\xd6\xca\x87+\x9f\x0bV\x17>,\xab\x8c\xf5cm]\xdec\x07\xe8f\x16\x19\xcc\x9c\xcf\xd0P \x90.\x98\xcf\xff\x9f-Ko_\xa5l\x11\xde\xf0m8r\x0c1\x9e\xc4\xce\xbf/\xf2 \x0c\xe1\x08\x9eA\x0f\xdeW\"\xfc\xe0_\xbf\x8az\xdd\x82\xeb]\xf4nEN\xcd*\x12~Vn#\xb6B\x1c\xa4\x7f\xe0,v\x0c\x07\x06\xa5\x91\x1c(Qi\xa4?ME\x9au\xd29\xdb\xe4\xab1\xdc30\xc1 \x0d\xd6,g\xa9\x18\xc0\x88\x1d\x1a\nEA\x18\xd3j}1]0\xe8\x10L\x05\xda\xbce\xd5\x0ekl\xeeH\xcb\x92\xb1\xffn\xe0N\x7f\x1aL\xcf{\x1e:\xb2N\xffmt\x8e\xf7\xfa,\xbeW 6z\xdf}7\x9d\xfe4}w~\xfe\xcd\xb9gK\\\x03b\x16\xe5\xc2\x94h*m:\x86\xe3\xd4\x0d\xc5Gq\xa5\xda'\xb2\xc5n0!\x85\xbdb\xd6p\x8e\xcd\x97\xa9\x16\xcd\xacZ`/\x1e\xe8[ \x98/\x0c9Z\x15\x1504\x1a\xa5\xab\xae\xc0\xb0$\xdav\x83vF\xa7\xe2\x86;[`=\xfdQ\xc4R\xe4\xf6VB\xb3\x1b`\x08G\xb1\xa88\xa6\x08\x9e@<@\x90n\x0c\xf3\xcdg\x1cA\x0fC\xe7\xef2\xf3`::\x17[3\xf2\xa1/\x02v\x7f\xc6J\x04\xc6\xa0\x14`]\x0ci\xab\xe1\xdd\x8a&HQ\x92\x10\xa3\xc0E\xe8M\xd6\x01tA\xb0Ry\xb9\x0d\x1c\xa9r\xca\xf2\xa2%7\x1b\x89\xe4\x03\xc3\xc7\xd0\xef'm\x8d\x81@\xd0\x90\xa2\x98\xb3i\xd2\x90\xda[>(9LE\x0c\xb6\xc0Cl\xc44\x08\xd3sO\xb28\x9b{\x99\xfet\xb8M-\x1f\xb4\x18\x97\xc1\xe3H\xf2\x86Y\xca\x82\x9c\xa1\x0eg\xd2\xefl\xcf\x95\x08\xe5\xc7\xb7\x8d\xd8b\x91\x9f\x91+y\xe7\x95\xd7\x81\xb6\xc6\x1e\xc9\xd7\x1a\xfcq-\xcc\xbe\xc7\xd5\x87S 4_\x9f\xc6\xb9\xbb\xf5ad\n\xd9`z\xf6\xc2\xecE\xf0\xc2\xcdp\x88\x01b\x1f\x06\xbd\x17\x06\x9a\xcc\xc31\xe3\xab\x8c\xc2\x8c\x8a\x1c\xc8i\xc6P|\xcc\xe8\xd3\x13\xa4\xc7\x8a\xa9\xc1\x91\xda\xc0iv\x8eQ\xf0\xc7\x10N\xb7\xf8g\xeb\xc0\xcc\x18\xa2?\x1cT\xc3\xc6R\xcdm\x08l\xb3\x0f\xe5\xa3\x9b \xec\xa9\x15\xa9\x98\x9a?\xc3\xcc\xf0 \xf6\x84X\x88\x03U{B\xe9\xbd\xd1\x9e\xa0JX4\x96\xe7l\x07{\x02\x8ei\x10.\xe3$e\xba\xe4\xa7dB\xc3G\x1f\x87 \x8d\x0c\x13S\xacl\xbd\x80\xb0D\xbef\xcb\x93\x9b\x8d\xab}\xf10I\xa5n\xae\x085s\x85\xe4\x12\xbc\x83\xba\xe5S~\xc3?eI\x8c\x83=\x11\x9eZ\xc1\xa0\xf8\xe9#f\xb1\xcd\xb1\xf0B\x0e\x06\x17\xea'f\xa5\xc8f\xc1\x86\xbd\n\xf2\x95\xba0\x8b\xa5\x0c\xefy\xf1ml\xab`\xfcR\x1e\xfe\xd6\x90\xd7\xaf\xd5\xad^\xc0c\xbb\xcf\x01]\xd0\xbc\xccXzE\x1e\x9c\xd3syk\xf3\xf2g\xa8f\xfc\x80\xba<]\xbdQ\x17\xed<\xb4\xb6@\x95\x9cv]\x06\xb3\xf7\x14\xc8\xad4`\x98\x98\xa2mV\x07h\x8a\xfd=\xab/I)\x8b*\xe5\x9cJ1-\xb9\xa471<\x81\xf41\xc4\xbd^]\xcb@\xdb\xce4>\xa7e\xc3H\x0bd[\xb7N\x0d\x19VlQ\xb7/S\x16\xbco\x99\xd9\xc2\xcd\xe9\xbe\x88\xaf:\xe3\x7fm8\x14s\x11\x0b\xd3D\xa8\xdfR{E\xabJ\x81\xaaz\x1b\xa2\xa4\xe1\x08\x81R\xc8\x8a\xefF#q\xa8\x1b\x891\x94\xad,.`\x8a\x15\xfb\xa8n\xfc\xf0_n\x88\x89\xbf4jY\xdf\xac\x85\xab\xb2\x01\xd4,\x1a\x18b\x82\x92\xe9\x98\x96\xda(\xa4\xe7\x83<\xf9\xd3\xd9\xcb\x17@9X'\xea\x85k\n\x14\xa3\xe0\"D\x9epAK\xfdg\xce\x9ar\x8f\x84\xa1\xf2[\xe6\x91\x98\xb37\"\xde\x17\x94\xac3\x99\xb0\xced\xfd~\xa3X\x83\xe6\x18\xe4T\xd3\xec\xbc\xc1\xa2\xb8\x97\xd6.\x8e\xf9\xb0\xf1*\xd2g>\xdd\x9cWt\xd0\x08Mf$\xc0\x94\x8f\x98rO\xc5\xac\xb7\x9bg\x92\x0d\x1e\xd9\xac\x93+\xd6\x90o{\x13\xe4\xab1\xdd\x0c\xdc'\xf3\x98\x81\xe0\xb9\x1b\xfb\xc5\x1c\\HK\xae\xd7\x16\x03\xd2\x95\xc8\xf9\xc2\xe7n7\xaf\x18\xf2ADP$i\xa2\x1f\x86B3\xbd\xd0\x8c\x0b\x89.\x89\xa2\x1cJ[\xe7\xcb\x85\x1d2\x11`;\xee\xde\xd0o_r(\x96\x1d\x05\xf3\x86u\x87\x1d\xd6\xbe\xb9\x15\x11}9\xd5X\xa0;kr\x81\xedjF5\xfbEm9\xe0*j\xb2W`\x8f\xb9YDNMm\x08\x15\xb5\xcez\xbd&\xeb'\x07\x8e\x0d\x9e%f\x0d\xc0Q\xc3-f\xc3-\xae\xfau\xde\xbf`>\xff\x87\xed\x1d\x1fm\xd3\xf6u\xd8=\xcd\xc5X\xfd\xc5\xa5\x1c\xc1\x96\xdb\xeciZQ=+\x02\x97\x94:\xb6\x80\n,\x99\xbe\x9bE\x9cR\x08\xb3!\xf1\xf5\x82\xa1\xe7\x94`871tPL=\xd7\x98\xba\xd2\xe1\xf9\xeb\xf2\x9a\xd4\x02 \xf1\xda\x898\xdao\x95vJz\xb9\x90?\xb9bq\xfeC\x98\xe5,F\xfb\xa3\xed\x93\xeb\xac\x93m\xc6\xb6\x1b\x87\xac.\xd6b\xef\xd9m{!lk\x9e\\\xc7m\x05\xdf\xb3\xdb.\xc5f\xab ^2,\x85\x807Of\xdb5\x8b\xf3\x81\xfc\xe3$b\xf8;\xc8\xf3`\xb6\xc2\xda\xae\x93\xc4\xe59u\xad\xa5O\xb1k\x9d\xea\x8c\xbb\xd6+/@\xd7Z\xfazt0A\xc4\x15\xb9;\x16\xaa\x01iO\xb1\x99J\x9b\x80z\x86y[\x8c m\x84\xddV\x12\xa7\n~!R'\x1f\x03\xc9+\xf4\xc3\x12\xc9C\x9e\xadw%r\x80\xc7>\x8c,\x08\xc9 _\x87\xaehH\x02\xb1\x0d\x13\x0d_-\xc8h,i\xc0G{\x8bu\\\xb3\xb5\xa9J6\xe3\xdb\x9c}\n\xbeUju\xc27SO]0\xa7\xdeW1\xb5\n\xeap\x8eT\xc0\x01\x85n`\xd7@I\x99\xbcRD\xd6\x8fd\xad\x8aYJ&\xa8\x19\xff\x8dv\xbe\xb4\x9b\xa0bp \x91F\x90B\xb1Em\xbd\x9a\x01\xac\xc9h\xa8\xb4\xe3\xcfI\x02\xd69\xadW)\xe1\xafQ\xa9\xd63\x94\x1d\x95~\x8d!\xf6\x06\xd9*\\s\xf6\xdd:/\xb9dZ\xc6\xb7%\xeer\x86'\xf2v\xa2%\x06\xdd\x12q'\x90\xadi\x92\xa7\xd9DdH\xab#}!-Ck\x0d\xf6\xa3mo\xbd?C\xee\x17uK\xcb\xac\x82\xd2\xfb\xfa\xb1\x19\xd3\x8c=\x9d\x9ce\x99\x0f\x0e\xff\x831\x87\x1cij\xb56\xa2\xfciv\x12o\xd7\x14\x11\xc3P\xf7\xc3\x07\xdd\xa5\xec\xa3Kq4\x0b\xc8\x89\xe1\x08}\x0b\x12oPD\xb3\x9f@JVR\xfdUb\x04\x94\x9d|\n\x8d`JQ;p\xe12\x11F\xad\xfaQ\x85\xf4(\x1d\xa8Y\xf6F.y1ih\xba\xebU\xda\xd1\xe6\xf1\xb1\xc1,\x89\xb3<\xdd\xce\xd0\xc0=\x99\xe8\xdf\xd0t \x86\xabv \x8e\x8aI\x8d\x0d#3A\xb9\x1d\xea\xb4\x93\xcc#\x0ee\x11\xb6\xaa\x9fh\xf2\xf7\x1a_\x1c\xeb0:)9z\xd7\x8bR\xa2\xc8#Sz!\x07\xcf\xe5K\xed\xb5\xf4\x9b\xb6\xe1\x96!g\x8f\xc4e}\xc8 \x0d\x00\xb3\xc2\x8c\xd58\xb4/\x81[\xc9Bo\xea\xcc\x90\x7fG\xe9\\\xeb`\xe3\x86\xcdc5\xe4\xa4\x91\xf4\xdcz$,\xe9y\x15\xbdE\x80%7\x9f\xc6\xe7\x18W\x9dM\xe3Z\x10\xfc:\xb57\x8c\xca\x90\x87\xa6\xa4\\+\xbaZ\x18\x82G\x15\x83\xa3*2\x1d\x9d\xf3\xb5\xd4\x7f\x8eIX5;\xf0bT6\xb6\n\xae\xc2d\x9b\x8e\xc15\xf4u`\xed\xeb\xa0\xdc\xd7\xc19\x1e3z\x83r\xabx\xc5N\x9a\xd5J#Pg\xe4|\xeb\x9a\xad\x0d\n\xb91&u\xb9\x15\xcf'+:}\xf3\xa5\x13e\xc4\x85\\%\xf2F&Y\xb7\x94\xbf:\x9dF\xe7t\xda\xad\x1f\x91\xceD\xe2\xe8\xe1c\xd8\xc0\x13X\xa8\x067v#\x18o\x11#WL7\x0d\xa7\xe6+.\xf0L\xe7\x0d%\xae0\x97\xe3\xaa\xc1\x12\xb5\xc6\x12\xe1tn\x8b\xef^\xba\x8a\x80W\xde\xec\x12?\x96- \xe3\x13X7\xa9\x1b \xe6\x8a\x0e z'k8\x02>\xa8\x0e>\x83!%\xc0\xce\xd0\xebk\xba\xf4a\xeb\xae\xbcs\xa3\xbb\x99|D\x9clQs[\xbbz \x1fu\xadE\xa76m\xf3\xd7\x8av\x9a\xfb-\x1ex\xdb\x86 \x1f1V\x07O\xbd\x1d\xe1\x17VA\x13Z2\xe9+pk\xbe,)\x9f\xf2\x1a\xd8\x07\xa0\x97Z\xd5J\x18\xd5\\\xfd\xc0H5\xd3)\x17f#\xd5\"\x12$NA\x90\x84\x1dA\x8en\x1ecL\x1e\xcd)\xc1Hd6(R\x1a\xf0\x02\xe7zk\xd3\xd4,\xefg\xe4\x16Q\x8c\xdd/\x06=\x88\x93\x1f\xb7y\x907*\xe6j\xf0\xcc8\xf8\\\x0d^\xe6g\x18\x92\x1e\xcdH\x8f\x06\xc1\x07\x8a\x81V\x0f \xd5@\xc9\xbf\xd1<\xd2\xeb0_\xbd\xc4+R5\xdfI{\xba\xd5L}\xafl]\x8b\x8cg\x0f\x0c!\xf3\x8fC\xec>\x1a\xdd\xab\x10\xa0\x8b\x0b\x96\xfd\x98\xcc\xb7\x11^\xf3\xdf\xad\xcb\xd8\x1d=x\xc0\x17\xd0}t@\xff\x8d\xee\x8b\x9f#\xf1\xff\xa1\xe7\x97\x05[wt\xcf\x1b\xfc\x95\x05\xef\x7f\x0c6]\xfah\x10]}\x99\xc9\xf7p\xe4\xb9U?\x8ePtV\xbd,C^\x0e\xa3\x83\xbb\x95\xf7[j\xea~5Y0\x0d\xfa\xd1\xa8\x1a\xbb\"\xa2\xf2\xd5\xe6g\xf8\xfa^\xd5{d!\xbcG\x0e*\xef\xf1\xdcr\xb0d9_\x91\xf2\xa7y\xc1\xbb\xc2\xec\xe4&gq\x16^F\x95\xcb\x1e\x9c\xedd\x83\xed\"\xcb\x93\xb4\xf2\xe9\x8a,\xca\xa5w\xed\x01d\xab^\x076\xaa)Y\xb8\x88\x8ag\x904\x86%qbx\xaed\xd3V\xd7\xe3\xf2\x98\x97FYg\xc9:\x05\xd6\xc0{\x13(A\xdb\x89\xbf\xa4q\x1bcj\x06\xf9\x88 \x0b?\xe0\x1c\x8e`\xe5.\xc4\xec\x1d\x01\xcf\x8e\xe7a\x0c&\x94}1\xfa\xb6HU\x14\x16\xb37v`8\xf4\xab\x8b Yy\xca\xedAK\xb2\xc1\x9c-\x0c\x83\xf4\xd1?d\xc7m\xb8\xadj\xa8\xee\xa3\x83\xa1\xe7\xaaV\xf1\n\xde\x12o\xbb\xef\x0d1\x96Q\xb1\x963\xb7\xcd\x18\xf1\x00\xf6&\x80\x96\xa5[\x0fs\x7f\xc9\xbb,\x8b\x94\xb1_P\x18\xa4\x17\x9e{\xe5\xf9\xf0\x80\xd6Yc\xff\x1fI~\xdf\xba.\xa6l\xe3\x9f\x8f\x0b\xad\xd0]\x977I\xbb!\xb3\xf4|\x08\x06/NN\x9e\xe3\x01\xba\x0f\x89;u(\x8e\xae\xe3\x83\xb3\n2\xfe\xdf\x92\xe5\xfc\xbf\x8c\xe5\xce\xb9\xdf\x00w\x12\x96n\xb5.j\xeb\x8c>\xf2\xb5x\xc1!\xc6L\xd2\x1a\xcf\x0d^\x1c\xa0`:'\x03\xc4\x1c\x9d\x10\xcc`@\xb0\xb7(\xd2\x7f\\,\xc4\xe1TSP\xe3P\x065\xbeXL\xd99\x8d\xc2\\Zj\x86|U@\xe8\x9b\xbc&\x8c\x0d\x97\x18\xec\x0e\x91\"\xa8-\x02i^\x8b\xe5\xffQ\xdfc\xfa\xbbs\xa2\xf0G\xa3\x87\x96\xc8I\x8dh$\x07\xc6\xae]\xd4\xbe\xf5\x10\xaf\x9d\xf8b1\x82\x1a\x7f\x10\x1c\xab\xc6\x96\x04\xbbz\xe4\xb9N\xb6a\xb3\x90\x95\xd2\x84t\x93\xd8\x10\xf8\x8cb\nj\xe5\x1c?LW(\x84\xf1I3\xa2\xa0}\x8a\x9c\x85PJBHK\\\xcd\xce\xe5\xa9\x1c\x08\x82\xa6\xfb\x90\n\x90T\xe6\x10\xf2\x18\x9a\x86\xe7\x9e\xf2\x1f\x12\x85\x8b\x1c\xf1\x92\x96R7\xe3\xd6T\xf6\xdd\x85\x03Z\xe7\xe1}\xe3\xfas\xf6o\xe6\xba\xc2\xcd\xb3Z-0\xef\xa6\x10\x1a\x86UaBH:w\xab\xef#%\xaf\x18\xa5\x86\xaat\xd0$5DnU\x92\x9b\xe3\xdb\xea\xc8WxxT\x86\x93\xaeR\x00\x1b\\`\xea\x07\x17\xff \xd2\xb1\xae\x1e\x10\x94~\xae\xdbN\xcb\x90\xb2\x04hrojg\xd9\x86\xa3P\x8cr\xe3\xb2A\xd0D\x94+\xe5\x19\x17F\x10\xf0j\xa5\xaa\xd9\x90\x0b\x98Zk\xd6\xc3\xaa<\xd2A\x16\x91|a)\xe8\x9c5 \x94:\x83\xcb\xa7\xa3\xc6\x15Z\x05\xad\x01\xd2\xa4\xc8\xb2W\xf4\xda\xd4b7\xf9B\x1e;4\xcd$F\xe7yT\xf5r\x99\x021\x10\xf1\xa5Y=\xbete\x1c\xc4|\xdb&'WT\x043\xd6\x01\xa0M.\xca%\x00\x18\x9cv\x0d\xb3\x11\xb5\xfe;\x07\x99\x88%\x90\x07\xa2\xb9\x8f\x97\x08\xf6\xf6\xfe\xbb\x9aTF\xfd\xe57(fe!e\\#u>\x84\xb6\xa9\xa3\xdbc)J\xa35\xc4\xeb\x96\x7f\x8d\xb0E\xe7\"$g\xd7\x8b\x9c\xdcE\xd8\xe0\x82S\xbcU\xaf\xe7\x83@r\xa2\xcc~a$\x04\xbc|\x97\xb9)\x8e\x88M\xc3ss*|\xfb\xd2\xa5n\xa4\x8b\\\xe6av\xdbv\xf9\xa0Gg\x80\x92\xbd\x04\xf3\x91]x\x97@\x9b\xec \xe2s \xbeR\xd2s\xeey\"\x11\x03I\xf71_\x93\x99\x1b\xab\x9c8\xc8\xe4D\xfe\x85X\x89\xfd\xc6\xbe,\xee3\x1d0Z>\xff\x88\xd9\x8bD\x0f\xa6\xa9\x9bgi\x80\x10\x1f\xa2f\xcc_\xd4\x91\xc0\x86\x01)YK\xd1\xb7x\xcft/\xb8<\xa1\x14'\xc4H\xbb\xc8\xc5\xa5\x9bt\xcaP9\x9b d7\x0dM\xa8\xd8c\xb8*P\xfb\x0f\xf0\x05$\x94\xaa( \x04D\x8b9\xa3f\xb6\x08\xcc\xf6\x06\x12L\xeeU[\xc9,RQd\x91Wf\x16\xf9fa\x16\x876$uW\xc3\x9b\xce\xf1\xf5\xdd\xa17X\xd4e\x13\x8b\xf9\xe6\x8a\xea\xdcm\x15\x82%\xa5$\xed\xf3\xd6$\x13_\xe2y\x003\xd8\xe6/`\x02\x97\xf5\xd7\xd7\x9c\xbf\xe1!!\xa30;f?\xd4\x13\x98\xc0\x05G\x86\x8b&m\xef\xc6p\x1e%@\xf3\xcaz\xba\x89\xcd\xba\x18\xad\xe7D\xe5\x16\xe1Rx`W\xa5\xf9\x83*\xf4\x85'\x93*\xb8\x1ez\"\xb9U\x95\xca\x83#p/0\x91\x8b\xaen\x1aqm\xc6\xbf\\\xa0j\xea\\\xcc0\xeb\xe2\xe0b&\xa4\xc1K\x9dO a\xc0\xebsK\x1f\xf2\xe9\xf5y\xcd\xca\xc0)\xc0\xca\xe5\xcb\xe9\xa3\xc3\x94O\x04\xd3\x173\xf4\x97,\xf7WA\xe6g,\xf7\xdf\xb3\xdb\xcc\xa7<\x1f\xbe\x98\x8eO\xb7\x0f\x1c\x99\x9e\xce\xe7\xa3\xe9&&\xe0\x16\x82\xbcnZ\xa8\xacu\xb2\xc1 \x8c\xe1\x84\x9c\xcdq\x03\x1c\x1c**L\xa4Em]}\xc3K:{S\xa8uN\xb4e\x16 \xbe\x9e\x9cn\xa1LA\xfa\xd5\xc2\x8d\x0br\x8e\x06\x07\x1a\xae:\xaf\xb3\xab\xec*\x0f\xd1\xc5\x8c\xab\xec\x05\x05\x1frr\xed[\xd5})\x0f\x15z{R+W\x15\x89=\x9f\x82H\xcd\xcb\x8b\xe0d\xe1/\xcc1\xf1\xf6\xb2t\xdc&\x9a\xd1,\x06\xbc\xb5\xfaPjP<&(^W\xcd=dIY\xfap\xed\xf9\x90\x95G\x1a\xe3\xadOe\xf0\xf1|\xd8\xb8b;n(G\xd3\x85\x0f\x89\x9b\x0c\xfe\x03z\x90\x0c\xfe\x8a\xff~\xe7\xc3\x8d\x9c\xf9\x9a\xb3\x90\xb3\xc9J\x98\xa4\xcd\xb0\x16\xa1\x1eTy\xaf\xec#\xe72=O\xb5\xe7\xc3\xfe\xf4\xa7\xa0\xff\xcb\xb0\xff\xe8]\xff\xab\x7f\xfb\xe3\x9d\xaf\xbf\xe9\x0d\xde\xfdt\xf1\x7f>\xfc\xf7\xf9~8\xc8Y\x86\xb9\xd7\xcc\x81Wd\x82\x97\xd9*H\x83Y\xceR\xceW)\xcd\x00,B\x16\xcd!\x0e\xd6\xc6\x9c/\xca\xfa\x94'?$\xd72\xaftyq-sn\xb6\x84t\x9e6\xeb\xd4\x99\xc1\xf1\x11t'$#p\xc5\x98u\xa4\x95\xac\x82\xd6\x10\x93Iv[\x957{[\xfc\x99'1+9\x88\xb5$<\x11\xb7\xa2\xccI\xac\xc0\xa8\xe2\x99\xdf\x1a\xbcF\xc4\x80+i\xc3rS\xb2\xb0\xd6\xb5\x92\xb2C\xbd\xdf\xce\xd9~\x0d\xde}\xa0\xa5\x02\x14\x97sJ\x19\xf2\x13\x0c\xfd\xb1S\xbe\x0c2\x1eQ\xd6bs\x82\x0c\x91\xf9\xbf\x1e\xcd\x14\xbd\xeaL\xddu\xe9\x8bM\x87\xe7>0c\xe86\xadG\xdc\x03q\xee\xb6d\xb9\xe6\x1e\xf7\"X3\xae\xfd\xef\x90!\xaf:\xd7\xa9)\xab\xdcGS\xe6B\xdb\x1e\x19|\x13A]k\x90\xd9\xf8\x95\x04-\xb2 \x0dR\xc6\xe7S\xcd\xdb\xf2,JY0\xbf\x05\xfe\xafc\xba\xcc\\\xc9\xef\xdfi\x80\x06\x7fF(K0\xb5\xd4LM\x81\xec\xd8\x8eY\x93r\x97\xcf6\xdbF\xb6D)x\xff}\xb7\x8c;\xb1\xcb(aZw\x1bO\xa7\xa52\xf8n\x82F\xf1\xf8Z\x15\xb9\x97\xcdT*FW\xa9\xdc\xce?\xf2\x01\xdf\xddg\x99\x96\xac\x96\xdc}:\x8d\xd0\xe0\xc7 \n\xda0\x86\x8cvCP\x04\x9f1\x8cE\x9fQ\x91\x8f\x98\x03\xecm\xce~\xa0\x0b\xbb\x0d3\xc8\x18\x81\xae^\xd5C\x15\xfc\x12'\xd4i*QS| \xc4S\x1d\xd6G\xd54\xdf\xad\xa7E \x0f/JY\x05\xe9\"UC\x12\xa0\xd0\x9c\xdd\x81yZ\x0eE\x91\xd9\xdc\xa0\xa6\xcbG\xf9\x05\x16\x89\x8e\xbe\x8d\x92K\xcd%\xbf\x9a\xecXo\x9f\x17\xed\xdc\xbeL~\xcd\xfb\x90\xe1g:\xf6#\x8bw\xeeK\xcf\x7f\xce\xfb\xab$@\xef\xd8\xad\\>u\xc1\xa2I\x86\xd0z\xd7\xd2mC)\x87\xd4\xba\xd2\x81\x86[\xe8\xf7\xc9\x04\\\xca\xec\xc0:4\xc4\"\xb7\xb9;5\xd6\xb79\xbdB{\x00\x03\x90&\xf1\xf2\xc8?|\x80==S\xb5}\xcd\xd0\x00\xb3\xac\xc8\xb2\x82U\xe8\xd7-\xbe\x95\xb3\xe15\xdbr\xab5\xac\x92\x1d)\x84+hm\x0b\xab1\xa7\xe5\x83\x05K\xf9\xdffI\x9c\xb38\xef\xd3\x10\xf1\xf8\xd6\x12\x04\xadT7\xab\xd5\xf5\xc1\xc9\xd9M\xbe\x8f\x01\xa9\x1es1>c\xf9\xe4\xed\x9b\xef\xfa\x0f1\x04W\x05\x8b\xe4\xe1\x98z3\x10W-Z\xbb1T\xe3\xed\x7f\x0e\x12\xa8\xd14N/\xd8l\xa2\x90\x92<\xee\xdf\xf4\xaf\xaf\xaf\xfb\x1c\xc5\xfb\xdb4\xa2\xe8\xfc\xf3\xea\xac\x8d\x12\x8c\x96a\x8d\x88)\xd1\x94V\xfe*\x8d&!i\xcc\xe6\xfd\x0d)d\xb4\xe44\xf6B\xe5E4\x88AY\x12]\xb1j\xb1.\xedi\xd0km\xb6\x15\xb7;\xf5$\xa9\xa4\x01\x0bg\xc9\x96k\x8cI\x8e\"\x9b\"\xbf\x98t\x17\x82\x0c(\x93]\xa3e\xa2\xcb\x989\xb6\x9d\x9b\xb7\x99\x04\xda\x12&\xb7nq\xc9\xaaY\xa5\x04Gr\xe79\x8e\xda\xf7\xa9\xb4\xfc\x02\xeb\xf8l]o\x90\xafXl\x8aM\xfdQ\x92\xdf\x9c\x88G\xeb8\x7f\x13Pl\x17\"`G\x11P>vQP>\x15\x91\x90o\xb3A\x16\x94\xcf\xc7_\x0bM\xba-A\xc9\xf3\xbe&\xfd\x91\xbfzaS\xcde\xdc\x17\xf2\xba\x1f\n\xaf{u\xb5E:\xdf\x9f+\x1b\xc7`\x91&\xeb\xe3U\x90\x1e's\xe6\xe6\xd3F\xd6+\xb5\x17J\x99`\xcbk\xfa\xd1\xb2\x10\x9dV\xee3\xd0\x9e\x03\xf8\x8eh_Fv\x0bE\xd7E=\xaa\xb1($\xb8Vt\xd2\xd1>\xc7\xf37B\xd5E\x03\xaa\xfe\x9c%\xf1\xe7\xb4\xfd\xa7\xb3\x97/(\x06\xaf\x95z\x95\xde\xdb\x94\x85Y\xab\xe7\x0f\xf9\xf5\xd1\xfd,\x0fU\x87\xfa\xfa#\xad\xd0\xad%\xc6\x08\x94`P\xdf\x8d\xb6\xc4\xb2\xba\xde Q\xda\\F\xf9T\xf1\xcd\xac\x94)\x95\xe9\xbf\xb9\x1a%\xe4\x83\xc2Gv\xa5r4\xc7\x98\x8f\\e\xd7\xf5\xe4NQ\xd6VlL&p\xa5\xf7\xc9\x9c\xd1\xdbd\xce\xfcR\x82\x18`\x9a$\xcc\xbb\xc2l\\z\x06\xf6\x8a\xbd\xc1\xb0\x87\x9a`H\xb3T\x06K\xf3\xf2\x1bf\x9f\x97~\x7f\xf8P_\xa1\x0f\x1f\xc0I\xd6a\xee\xf8\xb0W,NE\x98\xb2/Vn_\xacv\xd2W\x98;\xf3\xe4\xedf#\xed\xbe\x8d\xc8}\xabe\x1a\x87\xa7\xd0\xa7{H\xa6\x8c\xdd\x1f\xdd\\\x0eFN\xbc\xf8\xe9\xfc\xc7\xc9\x1b\xc7+\xefcN\x7f\xa8\xc2\xe2\x07\xe5\x9d\xc1W)[\xb04EI\x80\xde\xba\xd8\x0e\x99V+\x1d|\x7f\xf2\xecy\xed\x0b\xf9\xcbZ`\x1eUoN\xf90&4\x9b#[\xf8\x8f\x937\x90\xa4\xc0[\x939\x873\x13B\x10\x91\x93\x1a|5\x8e\x8f\x0d\xf7\x17\x1d\xac2\x82\x0c6Y\xed\xd3p\xedz\xf2\x8c\xfe\x8ec\xb0\x1a6\x9a\x11Z\xc5\x03B\x1e\xd1~cxb\xfe\xe0\xf6H\x0b\xba\x96M\xa5\x87YT\xa0\xad:\x1e\xdc \xe67q\x8c\x0d\xd8\x99g.-\xa0\x14d\xf8\xed\xeb\xd3\"&\x19\xd7\x91\x0d\xaf\x93\xeeq\xe1:[\xb77\xed\xfb\x9a4l(\xad\xf4\xfe\xbb\xf4\xe8]\xbc\xbf\xcbn\xd66k\xdc\xb4\xda\xe5\x8d\"(\xb2\x8b\x0f\xdd2\xda\x8b\x8d\x1b;\xcd\x0d\x82\xbeWi\xed\x0e\x82|>g\x0f\xe6V\xbe\x9a+_\xfa\xbf\x17\x82\xbbH\xd0-\xae\xeeI%\x99R\xd5SXs\xfe\x17\xe6\nC\xf7\x0d\xf9i\x0c\x07\xc3\xa1\x8c\xfe\xfa^\xfa\x85\x88\x8fO'\xfc\xab\"\xe7\xe2\xed\x138TU\x8a\\\xf8E'\xfcW\xad#,2\x06\xe7\xe5\x9f\xe5\xd8\xec\xbc\xc0\xd7\xb9V\x8e\xffc\x8a\xfc\xaa\xa1\xb1j\x17)/7\x1axDZo\x1b4\xaf\xac\xc7n\xba)a\x0cN\xc5\x92\x80\xd3\xb3\xe4Q\x92\x07Tcp\xceD\xcc\x88P\x06\xa6\x90\xc7T\xf8\x8a\xbe\x9a\x1b\xe1\n\xdb\x18\x9cB\xa1\xd1\x1a\xe1\x1aF\xf1\xb3U\x00\xe4O\x9e\xde\xb6\x98\xc3\xb4C\x07\xde\xbe_=\xc3\xd0\x9f\x8f0\xc3\xe0\xd4\xcd\x94\x174\x97\xca\x91\xbd-3\xe2T\xa3\x1f\xcbGJ\xd5|m\xc4\x9fM{\xa1\xa9\xdfcp4E\x83*\xd5\xd1\xd3,\x1a\x95\xcc\x84\x1eB\xce\x15L`\xaa\xe2\xd5\x9cJ}\xeb\xdc\xf1\x8b(6\x85\x1aV\x7f}\x1c\x05\xeb\x0d\x9b\xd7\xbf\x9e\xc6\xf9\xe8\xbe\xb9\x92\xe9\xfdi\x9c\x1f\x1e\x98\x8b\x9b\xde\x7f\x17%\x81\xfd\xc3\xfd\xbb\xe2\x83%,A\xfbuP\xf9H^\xc0!\x94o\xd2_Bv\xdd`~3\x03\x81y\x10*[\xaf\xb0\xd2\x86\x19o\x9cS\x88\xdd\x87v\xa5\xc4\xc1\xd6\x10C$.\x9a\x07Z\x87\x9aDk\x89\xd8D\xc5 \xd5\xca\x8eP\x94D\xb5\x9d<\x83\x9a\xae\xde)?\xbeu\xb0\xb1:Di\x05`\x82\xa7\xd0\x18\xfd\xd4\xc7\xe8\xa706$\xff\xc1 ^\xc5\xf8\x85\x93z\x97\xad\x17EU\x97|\"u\x9f\xf6J\xfbK\x96wj\\m\x9c\\ b#\xe4f~T\x9a'\xa5{l\xebx\x154\xfbFU:\x96\x1d\xd4\xc2Bs\xe8h\xeb+\xabL\xb2\x01\x02\xca\xab'\x80\xa0\xad}\xe9\xf3\xdb\xe1\x1a\x14\xd4\x02\xdc\xc8\x1e=\xeb\x1c)\xdc\x8d\x88L\x95\xfb\xc5\x18\xe3st\xfc\xcak\xa7\xf2\x861b\xd0\xb2\x0e&\x0bi]\xb4\xe5\xfb\xd3\xf7\xa3\xee`\xd0\x92\xea\x8d\xc9\xc8lfT\xc6\x8b\x89f\x93\x88>\x15\xf23\xfe\xf5'\xd3a&\xb6\xd3\xfb\x8e3\x11\xae\xd2\xbf\xfeT\xba\xed\xcb4\xae\xdf\xf7\x92O\xd3\x94*\x8eA\xda\x0cM.B\x86\x05\xb0\x9c\xc5\xdf\"\x9f\x7f~\xf2\xc3\xc9\x9b\x13\xce/\xb9\xc2\xee\x0b\xf5\xdc\x07\xe7\xe5\xab7\xa7/_\x9c\xf1?_\xbd<\xc3O\xaf\xde\xbeq\x0c\x0b4\xd3\xba\x9c\x89\xf4\x17\xad+\xaeIe\xd2\x13\xdc\xbe\x82\x97\xc9\xfcV?e;\x8dC\xb3+\x96!\x16\xf5G\x1f\"Bnm\x9c\x9ez9/\xbd\x9c\x7f\xe6T\xe6\x95\x93\xff\xa6I\x91\xf60\xd6\xb5\x0d\xbbFX\xa8\x1a\xe3\xaazb\xaf$\xb4\x8e\xb1TC4\xdd\xa4A\xf9\xdc\xa6\xd1X\xa9);\xac\xf2|\xd0\xd9\x9fF\xa2\x1b-\x19Y\x8c\xda\x17\xca\x90D\xb7\\\x84\x96\xc7q,\x83nDm\xa6\x14M\x9c\x85S\xf5|\x04\xb34$/\xd5L\x0f\x87#\x1f\x0e\x87\x07\xfc\x9fC\xfe\xcf\x03\xfe\xcfC\x03\xba\xcc\x07)\x9b\x87)\x05\xd8\xed\xc4\xd2\xb8\xa0.RK]jV>\xddZ\xf6:\x88\x97UwS\xa1\xd4\xce\x92\xb9\xf5\x80\xbe\x04\xba]\xf7\xa1\x0b\xc45+OQBG\xcd&\xeb\xa4|,\xea\x93\x11\xf4\xd8\xe0{\xa5r'\xfc\xe7k2\x08\x02\x86^\xe5?\xb3M\x12g|{\xe7\xfc7j\xce]W_[\xa3\x9a\xd3Z\xd3%\x17\xd0\xad/H\xe6\xb0\x97f\x1b[(\n\xff\xe7\x8f?|\x9f\xe7\x1b1\x0f\xbb\xa9&\xdc\xd13\x0e4\xeck.\x05\xda\x8e\x87\xad\xf4\xa9\\\x83\x101\xecD\xa4\xe4\xa3@g\xe3bN\xa7gQ$\xb7Ml\xae\xeb\x91\xb1\xc4\xee2\x02f$\xd5\x1f&\x8c/N*\x1a\x7f\xfb\xfa\x07G&\xa2\x0f\x07\xda[\x18\x03+\xfb\x93\xfcg_\xecG\x9fWj\xf1y\x91&E\xd5\x91\xa1\x17L\x0f(\x7f\xf0ejn2v\x05\x8f\xf1\xc1$\x97\xcb\xe7\xa3\x8f`\xd1\x99\x1a\xcb'\xad\xba\x86\xbe`\xf9u\x92\xbe\x97\xe6uX\x04a\xc4\xe6&\xdf\x0f\xf9\x88\xaes\x8a\xfe\xfd\x0f\xe9|\xc3b7Q\xc7Q \x9d\x7f\xe1\xe5&'\x8cg\xd1v.\xe2\xd4%\xa5\xd3\x1e.Y\x85\x18\xa5\xec\xb8tND\x15\xd5\x16\xddn\xe46\x96|\xc1\\m\x17\x05\x17!/\x0c>\x00 B;\xf9G\xcb'\xe4\xea\x95\x80:B\x03\x8b\xbb\xb4|0j\xe4 c\xf1\\\x0f\xa6\x9ah\x87n*}\xa0\xf6\xd2&\x95\x9a\x89-\x92\xcf\xc1&\x89n\x17a\x14\x99\xbc\x82\xd5_\xae\x9e\xc1\x163[\x90lQ\x8d\x85\xf6\x07\xd1xiqv\xbai\x94\x9bn\x19\xdd\xbb\xeb\x0d\xc8\x98b\nd\x1b\x1a\xb7\xc0lQ\x14\\\xc0pLQ5\xd5J\x13\xa2Q'\x10\xcd\xa4*\x8d\x9b\xf4\xc6\xe5\x03\xd1|\x13m\xeb\xa9\xfe\xaa\xb6\xd0\xc6\xcd\n\xb5\x18\xef2\x89\xec\xdd\xf2`W\xf9Ml\xe9\x9eQF\xffE*KN\x910\xdc\x9a&\xe7J\xc4\x1b\xcd\xe0I\x11N\xfa\x88k\xd6\xc2\xbf\xe2Y\xee\xa2s\xfd\x8b\xe0E\x9d\xcee\xd7!\xae\x9a5\xdb\xfd,\xc8\x18\x0c\xc7V\xc0\x97\x0dX\x8f\xd7\xe5\x83\x0d\x1d>\xb0\xb7$\x1f-\xd9\x80\xb8z\xd5\x10Y@>\x98\x86\xad\xb9\x18\x0e\xe0\xeea\xfb\x00\xf0J\xac\xcb\xd7\xf4\xf0\xa0\x85\xdb\xc8\xc0\x86\xadm\x06\xd3\xa8\xd73'\xea\x94\x8fY\xf2\x82\xe6\xc9\xe1\xa4F\xf6\xfe\xb9\x0c\x1b\x92<6\x83\xa7\x13\xb8\xfb\x90On\xc6!\xeb\xde\x03\x0f\xd7z\x06}\xb8\xfb\xd0>O\xe5\x95\x8b\x0d\xdc\xbf\xa7\x1ax0,\x1a\xb8\x7f\x0fz0\xb2\xdc\x10\x86\x1d\x1ch\xa9\x97G\x0fT/\xa3\xe1Ac\xf0<\xf9\xa8\x15>|\xe0k\xcb-p\xab#\x045\x96\xb2o\x10\x08\xb0\xe5+\xf1\xe8\x01\xae\xc4'l3\x1f\xe8\x81}\xa0mPp\xd0\x0c\x05\x82\xc4\x98\xa0 \xfd\\(H\x7f\xe7P\x10\xea\x10\xf1\xeb\x83B\xfa\xd9\xa0\xa0F;\xba\x0f\xdf@\x0c=\x93Q\xfd\x0f\xf6_\x82\xdf\x05ER\xe2\x08\xfaz\xea\x94\x8f\xbe\xc6\xca\xf8\n\x15\xab\xa2XVP\xf2\xf2;\xb8w_2\xaa\xc7\xb0\x85'pp\xef\xfec\xe8\xf5\xb6\x1e\x04\xd3-\x86#\xfe\xa3\x03=p]\xfeqt\x1f\x8e\xc0\x19:\"]r\x0f\xb6\x05\x97\x1d\xdd\xf7<\x9b\x87\x8d\xcc\x9e\xd6hFo\xb8E\xd9\x9b\xf0\xfe\xca[\\\xf2ft\x9cR\xceP\xe1\xac\xc8\xb4T\xc5F\xcdRj\x94%\xb6j:I!\xf0=<$\xf9\x8fkNw\xefi\x7f\xdf/\xfe~\xa4\xbd\x1f\x1dh\x1f\x12\x0e\xfb\x87\x8f\xf8\x8c\x12\x0e\xfbw\x0f\xd4[B\xdc\x84\x10W\xbd%l\xc4\xb7\x8f\x86\xea-a\x0f\xbe\x1d\x1d\x1cX\x04xtd\x80>\xc4*\x1dh\xce\xd7P^(BE\x9b\x8b\xd3|K\x0f\x1e\x12\xbdO9T\xfb\x80\x05\x83ib\xb1\xdd*\x82\xc1\xeb\x1e\x0c\xef\x1a+\x8f\x1e\x1d\x00\x0e\xf7)\xdc?\x87\x1e\x7fs\xf0\x10>\xc0\xfdC\xb8\x03\x9dZ\xbew\xef\xe0\xd1}5\xe7{\x0f\x0e\xef\xde5utppWv4:\xd0{\xa2\xbe\xe1\x0e\xdc?\xdcm\x00\xcd\xd6\x87\xb0\xc1v\x80\x10\xd2\xeb\xe9pW2*\xbd}}*\x94\xb1\xb7\xafOa\x1dD\x8b$]3\xab\xdb!\x08\xfb\xc5hx\xc0\x07]\x81P\xdf\xb4\x18w\x87\xf0\x81\x12\xc5\xdd\xbfw\xef\xf0>b\xad\xa8\x9ex\xf0\xe4 \x8cx\x81\xd0\xf3p\xbd\x1e\xd6\xd6ktP[\xb0\xe6u4\x0e\xbc\x03\x01+\x02\x890\x8c\xfbT\x12qs\xe8\x15\x80\xea\x95c7\x96\x15\x95\x96\x88\x05\xd4\x97\xe5\x8e\n\xef\xd8\x94\xb9\x85#K\x98}\x17\xc6!E\xe4:\x02\x87\x93?,~\x99$\x11\x0b\xe2zSG\xe0\xe4\xe9\x96!Y\\\x04QF\x7f9\xfa\xb8\x0b:,\xf5\xa5hw}\xc9\xae\x1e5\xc51,8\x02F\x1e\x18vQ\x87h\xd1\xc2\xc5-&\x0c\xa4[+U\xa5\xc8\x9c\x0fX9\xf1:w\x04MF\x87UgR\xb9ht\xa5\x12\xfa\xd2\xd8\xca_\x89\x0e\xd8\xa2\x18%bD\xba\xe6H\x96\x03<\xb3\xa9\x7f\xe4\xf8B\x99b'\xf6d>\xa6%,qM=\xe3\x83\xcc1\x1c\xa8\x88$\\\xbd\xdbrvL\xd9\xf29GZ\x10+Z\xc0\x13\xd8r\x1e\xb4h2\xe1S\xaa\xe1EC\xa6\x879\xa5$n\xc9\x16\x11\xba\x19\xe6\xb7\xedU\xd3A\xca\x87\xafm\xf9\x12\xf8\xbcQ\x08Skp\x05\x13\x98\xab\xf9\xaea\x02W4\xdf%\xcds O\xe0\x8a\xcfs\xe9\xc1\x8c\xd3\xa4\x15\xf4p8\xf3\xe9\xf2\x9c\xf3\x1b^`-\xd4\xb0\xde\x04\x9a.V`\x08+\xbep\x91^\xdeLp\x88r\x97{\xe4\xdd\xb5W\xaf\x8bj\x02gf\xedDL\xc7o.v\xa1\x8f<\x024\x995\xbe<\xba\x04\x86\x88_\xa1-\xea\xc6\x87\x0f2[\x8fdFJ|,\xb7`\xa8\x9d\x17\"CM\xec\xba\x12)\xf1c \x08\xb5%$\x8fp\xdbW\x8e\x1b#vXn\x94P\xbdN\x8e\x93\xc1:\xb8\xf93\xbb\xcd\x94\xee\xae\xde\x18\x86\xc5\xd1m\x04\xfbU\xb5p\xa6\x84 ^`f\xa8\xb8\xc1m\x93T\xd2443\x15\xaa\xdb\xaf\xb0\x9b\x0d\x8e\xb3\xfe\xd1&\xc0r\xbc\xde m\n}D\xe1\xe9\xb9\x8f\xc86$,\x1b\n\x0c\xf3\xf1\x94\x99\x13\x96K\xf1\xff\x05\x9d\xc1\\\xd3\x7f'T\xe8\x86\xb0\xf1\xa6\"\x00\xdf\xd8\x04\xe0\xb3\xaa\x00|c\x11\x80\xcfp\x8c\xb9^tm\xa5\x1c\xbc\x82\x18<:]\xb9\x87\x0f\x10\x1c\xcf\xe0\x08\x07:\x821\x9c\xa8\x9d9+\xc4\xe0\xb3B\x0c>+\xc4\xe03RJ\xd5[\x12\x83\xcf\xa4\x12 G\xc0es\xe8\xf5(\xc2\xda5Y\x9b\xb1\x8f \x86\x91\xe6\xb4\xc7j\x0e\x035CJ\xba\xa2\xcdp\xd9\xaa\xa0\xf2\x8a\xbd\xde\x12\xabn=\xb8\x82'\xe0\xbe\x87 \xdc@\x1f\x96\\B\xa38\xd5\xb7\xba\x04~\xe5\xc3{N\xa2\xc4\x96]a\xf1^\x9bIl\x96\xc4y\x18ow=\xe6\x03\xe1\x0d7\xe4\x00\xf3\x9bo\xc5Ee+\xcc4\xdc\xf8\xf6\xee\xa1\x18'o\x077\x10\x8e\xc0\xe5\xebz\xa5\x86[]\xd6\x1b\x0f\xe3\xa9q\xd2\xf5\xc7\x83\xa1\xc0\x11\xea\xbfR\xf3\xd2T\xf3R\xaby-\x8f,\xd4\xf6\x188H\xa1\xb7\xf4zk\x1cn\xd6\xc4\xe5\x8f}\x90\xb0\xb1\xb6o8oN\xce\x97\xc3\xd3{\x1b\x04\xc1X\xfb^\x9d\x10B\x98\x8c\xf88\x81\xc8\xbd\xf5a\xc3\xdf]\x8b\xe2\xfc\xdd\xa5x'\x8e\xc4W\xeaH\xfc\xd6\xf3 \x98\xde\x9ec(KXMW\x82\x96\xf0\x17\x86\x9bY 4(\xf7\x18\xe5\x98\xdbsO\xbf\xa6\x85r\x06\x1c\xc1\xf1\xf4Xk\xe6\x12\xc6\xb2\x8b\xe9\xb1\x0f\x97\x16\xc5\x8c\xaf\x06\x06\xf5\xea\xf7\x17^\x93\xc1\x8cou\x99\x16\xdeb/D,\xd5.\x12UE\x8c\xa8\xef\xe7\x1f\xec\xbf\x16\nt\xaet\x95\xe5\xc3\x07X\xf2/^\xfd\x93\x0e\xb7\xe5\xdd\xe3;\xb7\x86'\x90\x19v\xce\xfb\xcc}\xe3Hb\xdd9D\x84\xcf\xd9\xa3\ns\x90B\xc5\x1f\xcak\xd69\x93\xc1#K*\x83\xc3\x87#\xaf\xfdtO\xba\x13\xc8\xebpp\x04\x7f\xffH \x0dAB\x8b\x91\xeb\xc7e\x9d2]\xea\x03\xaeF\xd5\x13\x03\x1e\xb6GI\xb4'\x85HE\xa7\xad~p\xa2|\xe2\xb2Z\xfa\xb3\xd6\xc8p\xd69\x8d\x0e-s\xba[M[D\x81\x05\x1f<\xea2U\xc3\x0cJ\xfaT\x7fD:\x94\x12\x16Qt\xfc\xfbG.\xad\x04\xa83\xd9D\x16\xbc\xf01\x0d,\x9a\x10\xe6\xe9\xe3#\x88\x0c\x82L\xec\xce\xf8\x07\xa0\x98\x81>\x84nDA:g6\xbd\x18\x8aU\xcfv[`\xf3\x19\xeb\xfe7{E\xdb\xdf\xc0,I\xde\x87L\x7fs\x9cln\xd3p\xb9\xca\xdd\x99\x07\x07\xc3\xd1A\xff`8\xba\x0b\xaf\x93u\x10\xc3\xd9*\xbf\x8d\xd6A\xdcT\xe1\x1e\x1d\x9e#\x0f\x99\xa3*O\xfcf\xc4\x99H)w\n\xc4\xd3\x0d\x95\xc3?&\xb0u\xe7>d\xed\xa1)M8SI\xe4\x9d\xb14\x0c\xa2\xf0\x17\x93~\\],E\xa0\xc4v\xd7WZ7O}\xf8P\xbdm\x88pY\xa8n\x05d\x86\x16\xc8L0\xa9\x1e\x88\x06\xc3\x0cB\xf2\xfe\xab\xee2\xeep\xd0\x12\xa8R\x81y\x1c\xac\x9b\x1a\x93\x1auX\x8b4A\x07|\x18\x9e\x9b\xfa\xda\xb6\xf6u\x15D-]\xe1uu\xe8\x813q\xa0\x07\xdbz\x8f\xc2R\x06)W\xb5\x9f-\xadW<#(\xca@\xdft\x18\x8b\xc7\xd4\xd9\x8b\xe0\x85\x1b\x99\" \x89\xaa\xd9\n\x831 \x0dxA&\x00\x03\x14g(\x98?\x86\x1f\x83\x9b\xfe\xb3%\xc3\xc1\xff\x18\xe4\xab\xc1\"J\x92\xd4\x8d\x9a\xa87\x1e\x87\x0c\xe6\xc9:\x08\x8d=\xe8o\xb0\xd7\xe4\x15$'(\xfa\x98\x9cUe\x9b\xea\xd3\xe6\xdd\xe0D\xc1\x8d\xb3C\x87?\x047\x9f\xd3\x9b\x90\xc5v\xe8\xf0sf\xd8\xeaF\xd4\x04\xf4j\xbfu\xa8\xaf\xb5\xd4\x81\xffj2k1L\xc9Y\xebF\xca\xba\x1aP?N\xa9\xab\x04\xfb\x8f\xe1\x9b\xfd\xf2k.\x9a\xed\xff4}\xb7\x1d\x0e\x87\x8f\xf8\xbf\x07\xc3>\xff\xef\x01\xe3\xff>\xa4\x1f\x8b\xc5y\xef\xdf\xf6M\xc7c\xdb\xdf\xeax\xac\x1a\x93\xb9\xfc\xd7'I\xf8\x1dC\xaa\x8b\xfek\xcb\xeb2-\x1c\xc4t\xefk\xd7\xfb\xe6|\x7f\xd9\x16\x8b\\\x1eK\xa0\xbbF\xc9\x9e;\xf4J^\x1ae'\x8d\xf2\xec\xdb4H\xbd\xe3n\xb3,\xb9i\xc8\x1c\xf32+\xb2\x92\xc7c\xbb<\x9eV\xcd\xd3\xb1E\xe4N\xd1U\x00\x1d\x07\xee\xdc\x81\x14m\x97\xf7\x0fG\xe8q\x11C\x0fF\xfa\xc9|\x83X^s\x08\xc1\xca\x16\xc1\x9a\x0e*\x9fbW\x07h\x1c\x12n\x1c\\un0\x1c\xcb\xe3\xcf\xd1\xf0\xe0.|C\xde\x1a8v\x0fz\x90\xf0\x1f\xd8^\x8f\x8e\xf2\xed\xe4'\xa7\xebp\x07w\x87ey(\x84}\xb8\x7f\xb7\xf8\xc7\xf3at\xf0\xd0Z\xc6\x83?\xc2\xfd\xbb\xd62\xe5\xcf!\xfeB\x1f\x84^\xa3\x1bg\xa3\xbd\xban\xf25\x9c\xc6Qh\x89\xbb\x0f1B\x04\xcd\xf4\xe0ny\x84i\xf3$S\xc3\x04R\x9a\x00\xe7\x97\xbc\x03\xfeR\xb5?zt`l\xa0^WTH;\xd8\x0d\xda\xd2O\xea\x90\xb2gw\xf3\xe7@\xc3la\xf9\xedF\xb2J\x91\x86\x0b\x96(\\\xa6z\xfe/\xcb\x19\xb2\xc4\x93\x86[d\xa1\xddAs\x9e\xb4`F\x80V!v\xc3f\x8d\xa9\xc5\x94\xb62\x99L h4\x0d\x83\xd2\xcbCx\x02\\\xbao)\x9c\x90S\xcd\xf0\\\x19\xa7\xc2^\xcf\x0c\xc8p\xbd\n#\xa6\x14'>\x14s\xbb\xd2v\xc7\x81N\xf3x\xe9\x8f\xcc\x19r\xfe`\xdfIK\x8a\x00\xd0\x9d\x04\x85v\xbaS\xbb\xc2\xach\xa3\x8eZz\x8d;\"\xbd\xc1\xd4\x99\xfet\xee\x9c\x97\xcd\x07d;\xe0\xa2l\xcd\x9e\xa3\xda\x12\xa4\xbd\xed\x92\xf0\x0ea\x81\xb0\x1a!%\x1bd\xc96\x9d\xd9\"Fx\xbe,\x18\xca\x82\xe48\x98\x0efI<\x0bD\x10Gv\x0d\xaf\xd9\xf2\xe4f\xe3\xa6\"\xe0\xcf\x07\xc7\xab\x99]\xc1H\xba\xd8`\x11\xc6\xf3\xe3U\x90\x9e\xc6sv\xd3fB\x93\x0f\x87\xd1\\\x87\x0f\x85\x89\xfd\x86\xb3\xa22\xceZ.>\x95,i\x89\xeb\xf9\x02E\x0b\xd7\x98X\xa2\x1c\xda\x1c\xdcx\x10\x05YN\xc3\x7f\n\xb9\xf7\xd8\xe38\xd0\xb8]\x86\xfc\xcc\xbeX\x8aoos\xb6\xd3R\xc8\xd9\xf0\xd5\xc0\x1b\xb4\xb4 \xe4\x95\x858\x83\xf5q&\xe6x\x8b\xc4\xc5\x9fu\xbe\x1a*\x17\x87n\xa6\xebc\xa6j\xf6\x0d\xe0\xd2\x0c\x9e\x88\xc6\xc6\xbd\xb3EY.\xe4\x1b\xe5\x98\xc9\x85\x8d\xea\x89\x88\xfe$\xe8t\x84\xfb\xd4\x92~KQ\xc6\x84\xeb\x8c\x94)?\x99\x0e\x8dq6tyg\x97\xd5j\xbd)\xa3?r\\Hc\n\xdc\x92(\xe8#\xb50\xee%\x7f>\xb6\xedA\x8a\x06W\xd9\x8b\xf1^\x0c\xd8D\xbc\x96\xa5$\xa9\xf2\xc9\x84\xbcA\x92B\xb4+\xcd\x89\x8f\x15}?\x87\x9e\xafdN\xe95\xca<\xa7\xd0=\xa8\x07\xee\xa2Q\xe0\x10\xde$\x9c\xf4\xbdJ\xc2\xb8\xc5\xe6!\x9f.\xb6\x0f\\\xdb\x99lW\xae\xb1\xc6=DjIU\xc4\x13\xd6\x12\xa1~j\xef\x1b\xa7o\xe1\xfajBo\x84\x85\xe8\x8bM\xac?\xb9\xcf\xd7\xf2\xf9w\xdf\x9d\x1b_\xeek\xbb\xfeQ\x1c\x16t=\x13\xf8\xba\xdf\xef\xbf\x8b1\x00\x96\xb3\xca\xf3M6\xde\xdf\xdf\xb0\x1c\xf3\xdd\x0f\xb2\xeb`\xb9d\xe9 L\xf6\xaf\x0e\xf6\xe5\xaf\x9f\xb3$v\xde\xc5\xf3d}\x11\xce\xc7\xe0|%>\xf4\xb7\xa1\xf3\x8e\x0e\xc1\x82\xd2>\xab\xa60\xf2\xc15-\x07\xf4a\xe6\xc1>$\x1dg\xa5?ie{\xb4\xa3\xc0\x0cz\x10\xc17d\xee\x1d\xdc\x83#8\xc08\x0e\xdf`$&\xfe\xbf{\x17\xfa\xf4\xd2C\x95\xd2\xa6\xe0\xd8\x9e\x02Py\x17#\x0e\xac\x08\\\xdf3t\xef\xf5\xf0\x00\xf2 \x10`\x0f\x88L\xd37.\xb1\xa0\x0b\x90\xbe\xd2\x81\x0f\x8f\x1eiPo\xc7\xce\xea\xf3\xd1\x87G\x1d\x8b\x7ft\x9b\xcb\xd9/%5\x90\x84h\x07S\x85|2wK\xf1\x9e\x8dG4\xf2\xb1\x84\xb4\x93\x8c\xc8N\xa4X\xbe\xdd\x8c\xbb[\xbb\xa1h\xd4\x1571\x91*y\xeap\x8c\x8fU|B\x87\xe6\xdcS\xc6\x9d\xdck\x8a\x1d)\x1f\xe1`\xf4|\x9b\x8a\x00\x90q;\xb8\xb3\xf9\x92\xbd\\,2\x96\x9bBz\xeb\xcf'\xed[\x9e\x8c\xc1\x92\xab\x80>\xff\xd7\xb8\x89\xd6\x85q\x9e\xfc%d\xd7\xe5u6]\x9c\xad>\x92Wc\x9c\xf0o\x93m<\x0f\xe3\xe5q\x14\xb28\x7f\xcdf\xb9\xeb\x0dV\x88'\xed+\x14H\x8a\xae\xf8Z\x0f\xc2\xf6j3YM\xe2j{\x95\xc5N\xbcc\xc3Q\x02zm\xa1n0\x05\xf2\x13Xp\x88\n\x91^<\x85\x19\x1cQ\xbc\x01Z\xc91\x04\xe2\xc3\x06\x8e s\x03N/\xf9\x9b\xa2\x00\xb1\xd2\x06\xccn\x80\x81\x19\x8bs\x96\xd6\xb60\xed\xb0\x8b\x99\xdb$]\x94I\xe1>\x1c@\x8f\xa3\x0b\xc7\xaa\x96]\xe7\x85=OL\xefS\xe6\x94\xe5\xc9f\x0c\x81\xbd\xc0:\xb9\n\xe3e\xc7\x0c\xfcP\xd0\x86\xbd\xbd\xfa!\x90|\x1a\xc6\xc3\x81f,\x80\xa7\xb1\x14.\xdfX[Jca\x833N\xbdUN\xb3\xa4\x14?\x90\x7f\x9cDl]s \x04\xc1G[\x17C,\x82\xd0E\x88\x9f\xfd\x17\x1a\x91\xc5\x8f7\xc9\xa6\xcb\xd0\xd0j\xef\x9a\xfb\xa0x\xd7j\xe0\xd4n\x18/\xc5\xc8yo\xea#/k^N\xa4\\\xddd\xe5\xd2l\xde$\x1c\x92wL]\x81\x9bkIN\xa9P\xa0#\xac\x95\x978\x8cc\x96\n\x89\x01\x97y\x86\xc8Bov\x1c\xa3\x00\xadn\x8b\"\xf5T+\xa2\xe6\xc9\x86\x93 \x14\xde\xe2A\x82,\xca\xb4\xfb`\x06W\x83\xb75\x06%\x0drv\x86\x1bQ\x8b\xeah\xa3G\xd2N\xd5\x08N\x96D2e(i \xcb\xaf \x9c\x03\xef\x8ek\xff_\xbb\xed>k@'h\xec\xe8S`M\xc9\xe7\xac\x04^~' \xdc\x15S>\x0d\nw\x86/\x01/\x7f\xa8\xbct\x82\xf9\xfc\xe4\x8a\xc5\xf9\x0fa\x96\xb3Xd\x0c*L.{b\xcaq\xf2\xff\xb2\x98\xcc/\xf8\x9a\xb9%\x9ac\xbc'&E\x1ag\x15fy\x92\xdeV\xad9\x9bm\xb6:\xcb\x83\x9c\xcc<\xa2\x90y\x9d\xb8L\x13\x92 \x08\xe1\xe05\xe3\x85Qj\xd4+\xd7%\x0b\xcaT*>\x0fj\x95\xf9\xe8\x82m\x9e8\x9e\xda\xdc\xea\x82\xb8N\x94\x04s\xc7o\x87 \xeakWE\xb1ql\xeb \xde\x06\x91%\x86=Wq\x1a\x86\xbdI6\x19\xaen\x9b\xe7\xb5|\x18\x86\xe8&K\xdc/,\x16\xdc\x8cRH\x15\x9f\x12T\xf1\xc4\x8bAQ\xce\x06\xf7\xb0\x87\x97\xf3\xc40e\xb0\xf7\xc1*\xc8\x10\x92v].iUL\x06\xa8\xd0\xb8\xde\xa0\xd0\x08\x9aO\x0dZ\xedC\xd2h\xa7 {\xc9\xa4x\xf0\xed\xed\xe9\xdc\xadM!e\x0b\x99\xc1\xef+\xc7\x9b\x8e\x9a\xf2\x05\x83t\x8ek\x1b\x05\xd4\x0c\x05$L&\x850\x99s\x1e\xc3:\x88\xdc \xe4\x98D\x08\xe9\x9c5\xb5+\xf4Cx2\x81\x14\xc8 \x1d\xd0\xff\xdc \x124\xa8\xa8\xd0\xac}\xd9\xa1\xd9D\xb6\xf6L\xae\xebW2\x8aO\xe1\x86\xe5\xb8?}x\xf7.\xf34J\xe5\xbe{\x97}\xf87\xcf\xe4\xc2i\xc5\x9aY\x14\xce\xdewB\x99\xd2\xb1!\x1b\xe4A\xbad\xf9c:\x89q\x9e9\"\xd8L\x1e,_\x04k\xf6\xd8\x13G\x9f\x9b eq\xfe\"\x997$\n\xdfs\xf7\x90\xb1\x8c(\xe0\xd7\xe0z\x15\xceV\xa4&`\x1a\xc8?\xb3[\xfa\xb5fy\xa0~\xcc\xf24R?\x82\x88\x97j\x8c\xfd\x82\x16\xc86h\x94\x90\xa8\xa8\x94\xa2\x10\xf5\x08d\xe52G\x95\xdf\xe3\x9a\x91\xbc\xfa\xc4\x1a5\xd1\x80\xb6\xb9R{\xca?\xd0\x88\xac\xb8\x96\x82\\\xc7\x8d\xeb\xe7k\xd5\xa7\x94\x02pW\x90\x06\xdd\xc5\x0b\xb3\x18\xe4y\x1a^ns\xe6:\x9cv8\"\x85A3\xd9\x12\xc6\xfe\xe2\xce\xf6W\x0e\xf9\xb7n\xc9C:\x1f\xcc\xa2 \xcb8\x90\xb5\x86\xfa\x91\x06\xdf\x06\xb7w\xf9D\x0d\x840-\xdcZ\xdcQ\x9b\x89\x10\x8fW\xber\xc4\xd1j\x87\xbdB\x0c\x88\xe4\xd1J;\xb9\xca$\xac\x10q\x8c>\x95.\x01egJ\x19'\x08\xcf\xc94\xd5\x06}W\xe2\xcac'\xd6\xa5?\x15^\x02\x93\x16c\x164\xab\xd3\xf2Y\xec\xcc\x19\xa9\x16]\xff,3\x9c\x0c\xfa\xb0@/\xeb;\"x\xd9N\xb3\x94(\xa7\xa4<\xf7\xef\\\xdet\x8c>^\xfa\xf3\x11C\xbb\xa2\x94\x91\xf9\"\x83\xf4\xac\xc1\xe8af'\x16V\xf2\x07{!\xe9\x07\xa7^~t\xcb\xdea\x18\x9e\xd1\x18J-\xc5[\xad\xc1f\x13\xdd\x92\xa7 \x8c9\xac\x7f\xf8\x00\xae~\xa2\x1c\x9a\x0f\xa0;\xdd\xc9\x13\xc1\x1b\xe9\x94\xb2\xc8\xc9\xe7\x83sq\xc1\xb2\x1f\x93\xf96\xe2\x92^y_0}\xdbX\xcf\xc8\xa0\xeb\x99\x926m\xdc\xd8\xbd\xeb\x19\x02\xa8\xf0\x0f\x07\xd5\x0f\xa1\xf8pX\xfd\x10\x88\x0f\xf7\xaa\x1f\xb6\xe2\xc3\xfd\xea\x07L\xf6\xe0\x0e+o#,^MJ\x85'G\xbc\x15\x94&\xf1\x0f\xb2\x88\xb9\x87\x0f\x1fT\x1b^P\x94\x17\xcft1\xd3\x90\xf4Y?\x83f\x83b=E\x9c\xd5:\xac\xcb\x9b\xb1-\x97/A,2E\xbdX\xb1h\xc3\xd2l\x90lN\xe7\xe5\xe1\xb6;\x02\xaa\xd1\x0b\x7f:\x0b\xfe\x91\x9c(F\xe7\x89Lj6\xcf:\xa9\x9e\xf1JA\xb5\x92\x9b\x0f..0\xfd\xd9\x05\xc5\\\x1b\xfa\x18\x19R\x16\xf2<\x91#\x11K\x93{g\xe3\xc1D8\xc8\x93\xe52bg\xab\xe4:\xeeJK\xa4\xb0\x1f\x0e6i\xb2i9c\xcc\x85\xd3\xeem\xb2\xcd\x9fa\xdb-\x15b!\xb7-\x9b\x8b\x91\x97\x1cG8$\xd5\xd5\xcd\xab>\xc25;\xc3\x896\x17E\xad\x96s\xae\xd7,K\xa2+6?\xdb^\xe6)k<\x0f\xc53P\xcd?'@;\xf9@$\xc6\xa95\x84!KV\xc9\xb5;u\xd4\x0c2\x87\xec\xd9\xe7>\xec\xd9\x9c\x9a)u\xcfq\x10\xcfXt\xccE\xe2\xae[\x869j\x04\xbdo\xde\xae\xf4\xf64\x7f\xb9\xcdO\xe2\xe02b\xf31\xec\x85B\xa7\xac|\xb1\xb6b\xc8H\x03\xc5\xd8\xdf\xa4\x1c\x10v\x1a\xfb'\x80[\xb6a\xb3\x1d\x80m\x13\x98b\x8a\xea\x0fA\x1be,j\x10\x0c\x7f\xcbU\xe60\x84.\x1b\x7f!\xbf$F\xc9\xc11\x87ejs\xab\xa3M8\xb9a\xb3m\xde)q\"\xec2-F\xed\x9e\xc6\xaf\xd2d\x99\xb2,\x1b7&\xf2n\x18c\x1d\xfb\xba\x0e\xf6\x13\xa1\xe5\x8cEl\x96'\xe9\xaf\x00/]\x08\x13\x1f\xc2\xab _\xd9aK\xdd\x07\xc0\xac\xf6\x1b6\xab\x12\x15.\x9b\xfd\xe9\xcc\xf5\xe8\x12\xb1\xa9\xc4\xd4\xe1\x03Wt\xa6a\xf9\xcdt\xebW\xde\x82_\x0da\x7f\x85\x0d\xb0\x10\xf6\xf2\x1eX\nu\xdf\x06R\xd1\x9b\xb2\x00\xd6 \xc9\xc8>[\x13zZr\x8a\xfb\xa6;\x97\xb57\xca\x11\xc1\x87\xad&\x85\xf8\xc2\x07\x81OA\x7f;5\xcf\xe3=\xbb\x1d\x83\xb3\x0e6Hb\xde$\\\x8c\xce\x1c\xf34\x84\xe8\xdc\xd9]B\x1aJ\xf2A\xb2i\x07\x98\\\xc8)\x1d\x89A\"\xc4\xb4\x9c\xdc\x1d\xe3E\xb8\xcc\xbc\xb63w\n&?Of'7\x9b \xce\xc2\xa4\x834\xc2\x85G\xb6\xf9!\x8c\xdf\x87q\x8bX\xb4\xa5\xe2a\xb6\x89\x82\xdb\x97]\xa5\xa3L\xaf%R\xd9I\xff\x8f\xe6\x9a\x11\xa9\xb6\xdb\x0d\xd7\xa6\x10\xc6\xd7a\xfe#\xa2]\xcb\xeaa'OO\x16\x83\x1f\x83M\xab\xd2\xfe\xb3\xd0\xf4\x17x\x13\xfcOg^\x0b\x8b\x03T4\xc6p\xda\xdc,\x7f\xf2`\xd9\xe9\x86\x05\xa7\xdfV\xef]\xfd\xc9\xa4\xee\x91[\x14-\xfa.\xf4,\xc7\xc2\xdd\xf4g\xce6)\x9b\x059\x17\xf1OI\xf3-^9B]3\xf6\xa5\x15\xa3\xee\x9a\xccS\xf2!\x0e4\x86\xa4\xbdh\xa1\xa7t\xb8JQ\xd6UZTi\xa8\xaa\x8a-j\x19\x96\xaf\xdb \xc4\x82u\xb7X\xb4\xf7R\xd2/;\\\xf0SzU\x8b.\ne\x15\xaaE\xf6\x80\xbaN\xd9B\xf2AW\x81Z\xf4O\xb0\xe8\xc6-\xda(4\xe8\xc7-B\x12X\xd5\xfd\x16\xce\x0ff\x89\x96\x04b<\xd2\xa9}mo\xb0f\xd6\xd5\x9a\xebzB\x04P\xf7_\xd7\x1fa-\x89\xa4\x89V\xb8\xb5\x0b\x8f\"\xf7\xc7\xb6\xabb\n\x9c\xc7\xf0s\xf3\x8c\nm\xba\xcdh\xdf\x11<\xba\x82\xb4v\xb6-\x96P{\xd3\\\xb5tR)*\x97\xde\xb5U\xd7\x0eiUu\xed][uqD\xa7\xaa\x8a\xdf\xcd\xd5\xa4<5\x86\xcb\xf6\x82\x82\x95\x8f\xe1\xba\xbd\xac\xe2\xe3c\xb8h\x19y!$\x8c\xe1e{Y\xad\xe5W\xcd\xa5K\xf2\xd0\x18\x8e\xbb\x94\xd6Z?k.\xaf Och\xd9\x9d\x92\xe44\x86g\xcd\xa5u\xc1r\x0c'\x1d\n\xa3T9\x86\x9b\xe6\xa2\x8bx\x0co\xac%l\x87\xab\xb5\xb7\x1f\xcf=\xbfrO\xe4\xa3\x9b\x0d^mSfJ1\xb9\x92\xe4\x02-\x1d\xb5\xb3\xa9\x12s\xda\xab84\x16t\x00\xdd\xc7J\xdf*\xbc\xa4Z\xd5\xc4\x0c\xaa\xb2\x84\x8d\xf2k\xc6\x05\xcc\x15#&\x00\x13\xa0\\\x14\xbf7\xc7\xaf\xc8\xe6\xf8\x15\xd9\x1c\xbf\"\x9b\xe3Wds\xfc\x8al\x8e_\xfc\xc3Pw\x1a\x8a\xc8\xb9\xcb\x92k\xfa\xb7\xf6\xd9\x9a5\xfadi\xfeX&k\x8cv\\ip\xc7\xf2?\xd9\xe5Jx\x18bq\x992\xa7\x9a\xd6\xc8\xe8\xd4\xf8\x19\x07\xa7d\xa0Z\xb2\xfc\x07$t\x06)\xbe\xab}j\x17\xdbT\xbe\x83\xaa\x1c\x9b\x14\xdf\xc1l\x9b\xa6\\\xbch\x10t\xd1>\xe9\xc6\x98T\xbc\xd1y\x0d\xef\xe8\xb6\xceO\xab\x90Yd\x1dg5r\xa4O\xeb\xd7\xf0\"\x11\xdc\x03D\xf0\x19\xbcS\xe0|\x8d\xe7\xf5_;\xf0ug\xd2Z\x86\x00\x93@\xd5bg\xfc\xa4=T@a\xb3\xe6\xb6\xac\x06\xa3\xa50\\\xfb(\xcf\xa7\xcc88\xd3\x90\xed\x99\x18\x87Nwg>\xccj|\x84Z\xff\x171\x16\xcf\xfftb\x8c \x8b(\x15\xfa\xd5|a\xb0\x8b\xd3\xac\xba\xf0\xc3WL\x91_\x15_?\x82 \xe5 u3\x8fr\xe8\x0f\x1f\xc3\x0c\x9e@\xf6\x18f\xbd\x9e\x07\xd1tv\xae\xd7\x9c\xce\x0ca\x01\xc5R\xc6x\xe1\xd1\xe6\x9c\x8b\x18\xd8\xca-fA\x14 \x96\xc1|\x98\xf2\xba\xe72\xf4b\x84IZ\xc3\xc1,J\xb2N\xeeV\xc2\xc5J\xb7\xfd\xa11\xfc9G\x85\x10\x7f\xbbU\xffz 4\xc3\x8bZ5\xa6\xc77\xe3\xb7\xe0\\_\x96\xe4ub[\x1d\x0d\x9eqwcj\xba\x03;\xa4\xd3\x15\x96\xa6\x1d\x86\x10\xeeb\xf1\x0e\x84\xf1t\xf0\xec\xec\x8d\xbd\x14\xdfm\xed\x04-\x90)m\x1b\xcc`\x98\x0e\x15\xa1)\xd6\xc1\xa9\x81sS\x8aT\x87\xaf]f\xcb\xd0\xd0\xc6\x8a\xe7\xe1U\x8dT\xeb\x8f\xbaV5\x06g\x1e\x06Q\xb2\xecoo\xacWq\xbfH7\x97\xc1\xec\xfd\x1f\xea\xe57Z<9\xa5>^\xcf\xff\x8d\xfaZ\xb1`\xfe)\x9d\xad\x0e\x95\x1c\xe8<\xbb\n\xc2(\xb8\x8c\x18\xea\xfbI\x1a\xfe\"\\\xb8\x9a6\xfbr\x9b\xe7h\xe0\xb5\x0f8\xbf\xdd P\x89\x92\x9d&\x86\xfc\xa0\x8f\xd3k\xa8\x91\xc4\xba\xb9 \xeb\xec\xbc\x02\xd9\xd5\xb2q\xf4\xd7\xe1<_\x8d\xc19\x186\x0cd%\xa2;\xf0R;\x8f`\x9b\xd5e5\xfdY\xa5l1\x06\xe7+\x9c_\xc3 n\xa20~\xff}\xa9\xb0\x05y\x91\xe9~Y\x00\x9c%q\xce\xe2\xdc:\xfbh\x80|\xee\x8c\xfd\xcd\xf5\x06\xeb`S\xcaI\xdex\xfd\xb7\x85~\xce\xda\xcc\xb6\xc8~[\x0e?\x9e\x9d\xbdi=\xf0\x98\x17,\xc1\x1a\xb7D>e\x13X\xcb\x19\x96\xce\"[\x0f\x81*\xa6\xb8\x96\x93\xdb\x92\x91\xaf\xc5\x00\\1{\xd6\xdd\xa1\xe5c\xb3\xb4y\xf8\xd4\xbe}9%\n\xdf\xfeK_\x12\xcf\xbf\xf4\xa5\xff\xc5\xfa\x92\xe0|]4\xa6\xce\x97S\xf2\xeez@\\\xd7/\x06\x1a}|\x93\xa8\x83g\x9bI&\xafim\xe6\xd4\x15\xffR\xda\xccO,\x80\xac\xac\x8dy\xa4\x8b(\xd9\xedU\xb2\xd9n\x1c4,6+u{{\xbb)>\x89\xa8\x13\x14\xee\xce\xde \x0b\x7f\xb1D\x13\xf9\x92:\x10\xef\xb2\x7f\x9d\x06\x9b\xcd\xa7\x08\xbc\x1d\xe4U\xad\xb3\x04\x8e\xc0\xb9\xccc%\x113\x88\x92\xd9{6w`\\\xfd\xb0\x8d\xc5\xa7\xae\xf2\xaa\xf8\xb5\xf3\x14\xb2M\x10kR\xbb\x1c@\xa3\x98\xfe\xcf\"\xe5\xe2\x82\x7f\xa5\xad\xf1W\x1d\x96U\x13|\x1b\xea\x9bG\x8c\xf4\x14\xddkm#\x8f\x85u\xf8_\x92\x0d\xfcK\xb2\x81\x7fI6\xbf\xbddc\xbd7\xc0\x06Y\x9el8\xd4\x07\xcb\x80\xf8\xb0\x99\xff\xc8\xcb\x05\xd2z,:\xb1\x88&\xe8lop\xa9\xff\x9f(\x8e\x94\x1c\xd5?\x8dy\xef\xc6R9\n\x96\x85\x94\x8b\x0b\xceH5\x9am\xf8\xda\x81\x0b8A\x1a\x06\xfd(\xb8d\x91c\xea\x06h\x9c\xd6\x8e\xe4\xf7\x0e]}!>\xfeO\xc2\x93\xd9g\xf2\xe4\x86\xfa\xe6\x11\xff/\xb4\"\xcc8K\xad\xf1\xd4D|\xa9q\xe1PV11\xdb\x99\x89\x0bo\xc5\x87\x1a\x17\xce\xc4\x87\x1a\x17\x8e\xc4\x87\x12\x17\x9e\xc9\xc8G3\x11\xf9\xc8\xc4\x8fg\xbf=?^t\xe5\xc7\xb6\xb0EU*l\xe5\xb9W\"\xafz\x95\x98[}g\x92:\x0fl W$\x16+\x18$1\xa7\xcd\xc7\xab ^\xb6g0\x02\x8d\xcf\xb1A\x1c\xac-\xbaXP\\[\xab\xb0\xe8\xbf\x7fDL`&\xf4\xe3\xfc.\xc3\xbb\xee|H\x9d\x06S\x0fb\xc7\x1b\xa9\x1f\xdf*\x15\xca\x0d\xc8\xe3\xd7\xd2}\x94,M\x91tv\xe8\xbfY8\x08\xda\x14t\x8a\xab\xd0\xc9@B\xc1\x154\x93H\xcd\xe6\xdd\x1a\x80U@\x819\xa25 \x1d\x19\xe4 \xc9w\x96\x99\xc5b\xcd\\s:\xd3\xa0~\xec\xbe\xc3b\x9a7\xb3\xe3Y|P\x84\xfa\xe0\xbf,8\x0ee\xd9)3\xcaN\xc1?@vj6\xe2t1\xf6\xc4U\x00i\x83\xa5\xee\x87\xeeyW\x1bR\x88\x85\xbb\x9d\xd0\x07t\xd2\xcd\x91\xff4g\xeb\xa6\xabH[*Jy\xe0\xda\x8cO\x19\x15\xfe\x96d\xc8\x96\xa3\xf6\xa4do\xb2\x97\xa5\xc0\x19\x8b0\xcaY\xfaIH\xb7\xb77\xc3k?\x96(\xea\x80\xd8g\xef\x7fc\xee\xbfc\xe7r\xe5D\xd4]\xbc~\x94\xdfnXC\x8c\xd8\xa6\xc1\xcc\xbf\xcc`&;\x0c\xa6Q\x8f\xb0\xdd\xbf\xd8\xdd\x088K\xe2<\x08\x9b\x0e\xd9\xf7\xf66h\x95\xe4b\x87\xb5\xdfE\x92\xae\x1b;Nb\x8a\xf2\"o\xa5(6h\xebvS\xa6\xf6mI\x97Z\x16&\xe8t\xc2\xd9v\xba7[\xb1u\xd0z`\x18\xe3\xf2\xb6\xb4\xb5\xd3\xe9\xa6.\xc3\x8c\x81\x95d\x9a\xe6\x9a\x81vy\xad\xe5\xdeK\xf9\x08\xf5\x13\x8e.\x0bN\xea\x7fA\x00\xbd\xcc\xe3VK\xb5\x00P\x8e^\x0b\xfa\xf3\xc8:\x82\xack\xef\\e\xa6\xa3yi\xa3\xee\xac\xcdjR\x96m\xc8\xce\x0fX\xc6\xf1`\xfciC\x15\x1e!\x84H\x1d=B\xeaS*\x00\xc4\xba\xb8e\xeb\xf8'\x8d\xb5e\x0c|\x8b\xe7I\xdc\xe4\x97\xb1\x83\x97\x8as\x8cn\x1bh\n\x9bs\xa25o\x03 \x01\x94t\x18\xf0E 7\x9b%\x1b\xd6\x9f\xb3E\x83/\x87\xa5\x9bMq,q\xc6[\xc9 H\x19l36\x87<\x81e\x1a\xc49\x041\x04\x9bM\x14\x8a\x80\xd3\xf3p\xb1`)\x8bs\x88\xd8\x15\x8b2H\x16\x10\xccf,\xcbx\x95y\x90\x07\x90\xc4p\xc9VA\xb4\xe0\xdf\xf2\x15\x03\x16\xcfy\xa3\xe9\x00N\x82\xd9\n\x9e\xbd:\x85up\x0bs6\x8bx\x7fI\xcc Ia\x9d\xa4\x0cp2\xd9\xa0i\xf7\xf5Q\xf3\xa6R\xf6\xb7m\x98\xb2\x0c\xbbZ$Q\x94\\\x87\xf1R\xb6\x04Dg\x80b\xe1'1\xcb\xe06\xd9\xc25\x9f\x9a\x9ac\x9e\xc0\x19\xa5\xd1\x85\xb7\xa7\x03\x07\xe3\x03\xef\xc6\x81?\x8d\xfb~\xac\xbb\xd64J<\x9f\xcb\x91A2\x9f\x06%\xc5\xbe\xf0\xdb\xb6\xa6w`\x00\x92\xbd\xb5\x05\x8dA\x10oR\xa9\xda\x19\x04\xa7z\x9ft] \xeal\xa3\xa2\xe4b\xbf7\x1b\xd5\xef\xf2<\xc8\xa7?,\x96\xa8\x7f\xb6\x93\xa1\xffy\x17\xb6\xbe\xa8\xda\xdd\xa6T\x8b\xd0\xaaH\x0b\x9aUo2\x905\xeb\xdc\xbb9\xbaw\x93kC\xe5\xe3\xd1\x16\x1a(\xd8\xc1}^h\xdc\xc1&\xfc3\xbb\xe5\xc3hR\xa4#*|\x19d\xe1\xac\xad\xecL9\xd17+\xdb\xb9\xce\x9a\xcc\xda_v\x1db\x06\x93E\x13C\x9a\x05\x19\x031\x0fgl-\x06bh\xb6\x83\x8dV\xce\x02\x1d\xb5&\xe8\xae9AW\xed j\xfaJ\x87\xc8\x1c:+\xec\x10\xf9c'\x0d\x0dHF\x15\x1a\x9a=\x8d&4\xe8\xf6\xf2\xb9LY`9V\x05\xb5\xbf\x08z\x9f\xb1\xbd\xd1\xbf\xb6\xf7\xf7\xb9\xbd\x92U~\xf2\xcev\x928A\xedn\xf3\\|p\xde\xc6\xef\xe3\xe4:Vas4'nTB\xc1\xf1a\xd1\xf5v+t8\x0bo\x1b?\x8d\x1bz\xe0\xf4\x7f\xde\xae7V\x15\xcb\x90h\xe6\x7f\xf8 \xe8\xefR\xba\xfc\x97L\xf9\xbfD\xa6\xe4\x82V\xd2@HU\x1c\x00\xd7A;E\x93\xd0\x14\x17e\xd7,\xcb\x82%k*\x9d\x16\xa5\xb3d\x9b\xce\xac\x02\xd4\xe7\x92\x1e\xdd\xc6\x83\xb3\xb5\x85m\x05\xcc\xd3}\x1b1\x13\xe4\xea\xcfe0{\xbfL\x93m\xd4)\xd5\xe7\xfbm\x80\x1e\xf5\x07\x97\xe7\x1f\x16\x98\xbay\xa7\xa1t#\xaa\xc9\x95\x16t\x7f\xea;w\x8a\xd4\x10\x9c\xe0\xe14\x1c[z\x9c\xfa\x92\xdbX\xd8\xef\"\x94w\x1b\xdc\x83.(u0\xb2\x81\x12\x95\xba\x99\xc4@\x19\xe6\xda\xf7.\xc44\x8d\xcei\xbc\xd9\xe6m1v\x03*\xfb:\xb9n+\xb9\xa5\x92\xc7I\xa3\xb0\x08*\xff$\x1e\x19\x9fp\xc1\xac\xad\xfc\x8c\xca\xff\x18\xa4\xef\xe7\xc9ukX`\xcaB\xe9\xfc C\x9d\xbe\n\xf2U\x9bO\x0e\x08\x17\x96\\\x04W\x12\xa4\xa9\xb9\xc2\x1c Y\x10E8\x85\xcc\xf5v;\xf0\x92\x8fdo$\x11\xf3%9\x9d;\x1e\x9e\x7f}\xba\xe9\xa2\xdb9W\xcb\x19\xea\xean{\x99Y2g\xaaT\xa2\xe2\x04\xbb\x0e\x07B<\x07t\xfe\xff\xff\x0f\\2pz\x8e\xbd\xa5E\x9b\x11\x84\xa2#OU\x16\x19\xcd\xe7\xce\xf1!9\xb7V\xc6\xb4\xb6\x9bF\x87\x98\xd5}\xc3\xf5\xb2y\xd3\x19j\xd0\xb62\xad\xb7\xf4I\xf7\x19\xcb\xf5\x9a\xb3l\x96\x86\x9b\x1c\xa3^7\xcf\xe5\x93\xc7\xa4\x1f\xfc\n\xbd\xa8\xeb\xd6\x96w\xf5\x8b\x8d\xe24\xde}\x0ca\xfc\xd9#\xa0;\x13j\x14\x88\xeec\x07\xc1\xa4\xc1\xf1\xa04\x18\x07\xbe\xc1\x07\x1a\x9dB\xb6mC \xdb\xc0Dx\x8ep\xe5\xabE\xcd*L\x9e\xf2\x92\x06\xfel\x82%\xcf\x87yS\x98\x8a\xae\xde\x83\x9f\xe4g\"\x1fT\xcd[\x0f\xb2\xa1\xfd\xe4\x1d\xc0\xea\xefD\x9f:\x0b\x1a\xa6\x80\xa9\xa6\xc3\xec\xf2\x907m\x97\xd3u\xc1\xa2N\xbbK\xbb\xa67e\xdd\x85+\x91\xfa\x8e\x15\x97\xbcZN\xe3\xc8[6\x0f\xd2%\xcbi\xe3\xede\xe5\xdd\xb7\x8a\xbf<#\x91\xbcmg\x85\xc0ega6\xf6\xc5\no\xfd\x10\xd3L\x87\xadz\xfc\xbf|\n\x8a\xe7\x93\xac\xbe\xffd>\x05\xb0\x9bN\xde\xe9f)\x88\x9e\x7f\x83\xc4\xdc\x0b*\x186\x8cb\xdb%|\x05\xdf\xd1m\xab\xde\x11a\xa9f\x9d`&\xf3a\x0b\xc1w\xb0\xcdXj\xbfP#v\xbfK\xf6RR\xce\x1b4o\xa9\x9c7\xccS*\xe7p\xd4Bs\xe4\xa8m\x8a<\x7f>r\xf0\xb4\x9a\x19\x7f\xeb\x94\xa8\xffp=\xbf\x8bc\x06\x94\\HZ\x95\x0e\xbaM,\xf5\xfcX\xd3\xf39\xda\xd8\xd6\xbe\xbe\xf0\xffK\xb5\xfdv\xed}\x978\x93\xf0;\xd0\xf6\xa3O\xd3\xf6wS\xdf\x17\xbb\x99\x08\x0c\xda\xbe\"z\xedj\x7f\xf2\xab\xaa\xfduc\xa3\xfetP\xfb[N\xccH#\xb1GH,\xd4~\xe7\xdb \x0bg\xe5\xe8\x88\x8e\xbdj\xab\xce\xdb\xac\xc3\xa7]tx\xfb\xb0\xad:\xbc\xadJ\xd0\xb6\x14\xad6\x89O\xd7\xe1?yLU\xdd\xf5\xad\xe4yR}\xb5V\xac\xa8\xaf\x8e\x0f\x1b\xfc\x9f\xeb\xaf\x0d~e\xcd\xc3\xf9\x82\xfa\xabpC\x9f#q\xa7?[j\x10\xafw$\xde\xfe*\xfa\xf1\x17\xdb\xa8WA\x96]'\xe9|\xe7\x8d\xd2\xed\x0c\xbf\xde>\xed\xbe\xfa\xc16O8g\x8bX\xcew!f\xd7\xfd\x8d\x98c\xb7}\xebXZ@P\xc7\xd2\x9f\xb6\xcb_\xc4\n\xf2Y\xde{\xff$V\x10\xd3\x11yy\xc8\x8b\xdf\xbf\x15$\xd5\xac \xf6R \xda\xf7;\x18I\xd2\x16\x99\x8d\x1c\x9b)\xb5\x176gf\xe0\xc14<\xe7\xb2\x85\xaf\x9b@\x9a\xe4V\x94q\x03\xf3n\xa2\xe5\x84Y\xa3\x0b\x94w\xf5\x9f\xc9\xc7aa\x8d\x1b\xb2\xb0\xf98,l>\x0e\x0b\x9b\x8f\xc3\xc2\xe6\xe3\xb0\xb0\xf98,\xc8\xb2R\xfe\xc0\x05Yw!M,\xfc\x8fGw\x1fxf#\xcb\xe2\xb77\xb2l\xbe\xa4\x91\xe5\xf7\xe6\xf80\xff]:>\x04\x9d\x14\xee\x85*\xd9A\xc3\xe3\xbb8\xe3 B\x17\xf8\xb3\x06\xc5\x07\xa3\x98\x0c\x8a\x04d\xae\xd0\xc8\xed5\xae`Bb\xf7\x86$\\%j\xb5f\x16]Wj\xce\xa2\x90\xc5\xf9\xa9H&\xba\x1a\xc8\xdfm\xed,\x8d\xed\x9c\xb1Y\xca\xf2r[\xf4\xae\xad\xbd\xdbJ{R\xacx\x8379\xb0\xb6\xc8Q\xd8\xbfL\xe6\xb7\xceg\xbb\xa7\x04\x9b\x0d\x9d\xb5\xad\x06\xe2O\xfb\xe0\xbe\x84+\x0b]\xdb\x1c\xc3\xf4\xbc\x01\x14\xc5\xe27\xa6\xdb\xd4W\xb51\xb9favkH\xea(\xd7y\xdc\xb8;\xfan\x8c\xe1\xd6X\xee\x1f\xe0\x8e\xf3\xab\x18\x9b\x9a%\xbd\xaeaU@\x85Vi\xa3?\x00\xbbEV\x81]\xa3\xab\xc0\x8e\x11V@\xb0\xe1\xbc\x83\xcdkKS\xec\x96/\x05\x8a0+\x9d\x8c^\"\xa9I\x07\xa3\xd7\x82Jv0zm\xba\x86y\x01\xe9J\xb2\x83\x85lE\xe5w\xb3\x90]Q\xa5\xae\x16\xb25\x9e\x1b\x84\xd9\xcbgg\x87\xcd%9\x89^\xbb^-\xfe\xe01\xd7c1\xea ^o\xc7\x9f\xcd-\xdd\x16-\x11\xf59N\xd9\x9c\xc5y\x18D\x19\xb5T\\\xa4oi\xea\xff\xb2\xf7\xef\xebm\x1b\xc9\xa28\xfa\xffz\x8a\x12fN\x06\x1c\x93\xb0(\xdf\x99(>\x89-\xef8c\xc7\xde\x96\x9d\xcc\xda\x1ao} \xd0$\x11\x83\x00\x02\x80\x944\x89\xdfe?\xcbz\xb2\xdf\xd7\xd5\xdd\xb8\xf6\x0d\x94l\xcb\x19c\xd6r(\xa0\x80\xbeUW\xd7\xbd\xe6\x98\x04\x06I\xfc\"6/\xeci\x0d\x8eu*I\xc8\xe2\xf9\xd9\x91\xc0\x9f\x14\xfc\x96\xfeSg\x98)\xba\x9d\xb9\x07\xdf\xf7\x0d/\x1e\xa1\x15\xe6Cj\x16\xe5\xc2\x82\xb8t9u\x80W\xc5\xdf;\xbaT\xa7\x9c\xad\x1fG![\xbff\x88\xbf\x08\x040\xf4\x0fsC\xe8;y\\/dK\x1dgT\x9a^\x99\xaf\x94?\x06\x07\xdc\x17\xdfm\xca\xd5\xc1\x18\xe8\xed\x16\x1a\x823\xd2\xb9\xbc\xacL\xca\x02\xbd\x0e\xd57\xe8P\xcb\xba\xca4\xe7Ft\x1e/\xab;\x0d\x9dj\xbd\xf5\xd0g\xa7\xff\xa5J\x9b\xc8\xde8\xd6\xb9\\mM\xc3\x14\xaaU\xd9Zj\x868\x86\xb3\x1d=\xbd\\'Z\xd3\x11F%\xc3\xcc9\xdd\xf8s\xfc\xb9\x1ci\xbf\x99\xf5?\xc9R}\xbcy\xf5l\x80{SRo\xd8\xea\x13o\xf2\x98\xe5F\xa9\x19\xd5~\xef\xea\x9f\x17\xd6\x1d}\x9d\xbe#\xac\x83\xd6\xfds\x1a\xb8\\\xd2\xd7\xab\xcei\x1b\xd4/s3F\x077\x88zm\xc7\xe0<\x89\xd3\xb3\xe13\xca6\x1e\xfa\"\xd6\x93\xb8\x87\x93\xf8\x10!5\x0e\\\x81i\xe7\x1b\x01*=\xb0~\"V\xe5:~\x82AB\x98\x01\xe5\xb4\x92\xb4\xb4\x13\xb2ij\xff\xcf\x068\xaf\xb57pe\xf9\x12;X\xf5\x19\xa3E\xa4\xf4\xe71\x15\x17\xa6\x9a\xf8y@UE\xf1\xaeL3\n\xa8\x1b\xa0r8\x11\xf2u\xa6\xdeDa\x7f>\x0dl\xb7\xb5\xb9\xc2 \xfd\xd2\x9f\xe0'/a\x83@\xfe\xd4JE\xfd\xb1\x11\xb0\xda*Z\x04\xcc\x9aV\x8d!\x08h\xe3=\xf9\xf9b\x9b\xa5\xb1b\x98i\xa3\x8dq\x96/}\x16\x18'\xc6r\x8a\xf94\xb4\x08\x87S6\x14\xd9\xda\xd4\xae\xa9d\xf8|(^\x81r\xafqR\x11 \xdb\xf3\xb9\x0bV\xbd6\xbf\xb8\x1bfiF\x98f\xdc\xbf@?B\xaeoi\xab\xe9\xb48\xf3\x8aA\x02B\xea\xf8\x95\x81=`i=\xb4M\xd7\x0e\x14W\xd9\xf0o\x1b\x92\x1b\xc6\xfc\xbf)\x08d~\xee\xafII\xf2\x02}\xe6)#\xc99E\xd4t\xaa9^|\xdce9\xbf\xfaJ\x8c\x19\xd9'\xc5\x96B\x1e\xd4\xdd;\xa3\x9f@f\xbc\x01'\x14\x8fZ>\xf5\xea\xe9\x0bk\xf642\x1cf\x15\xd8`\x02\xf3g=\xcd\xea\x89\xb3:\xc8,\xd8\xa6\x86\x9fA\x07\xbd\x0c\xda+\x86\xfa\x12\\\x1aB\xde*+\xc4\x87 m\xbd\xfduE{\xe9\xa3\xef\x93\x82YWl\xf6\n\x03\xfd\xb2_\xda\xfb\x85O\xe0n\x18\xcd,.W\xb5\xdfd\xf8\x7fl\xd3\xbdK\xec\x81=$\xfb\xa7\xf8\x8fe:W{-\x01W\xc2\xee\xb4\x92\x98\x9d\x9d\xe3 \xd3\xef\"\xe6\x9e\x0e\xcb^\x0df\xa5\xa1\xd1\x13\x12\xacS:]j\xe2\xa03y\xc1\x8a\x04\xef\xe6\xa9\xa2 \xb8\xb84\xadZEt1\x9cc^\xdfV\xe9\xc3\xe8\xdea9\xa2\x1c\xb8\x01s\xfc%\xba\x8a\xb7\x84\xfb\x8c\xd9PD\xaf0*(i\x08gpf\x06\xe6[\xa9\x9a\x19\xf3\x1b\xf5\xce ^\x9a \x1e\x19\xb6\x05p\xdd\xe4% 54\x89\xb5\xf5|\xed\xba\xd4\"\x9d\x8a\xb9OM\x0c\x8bJ]~\x170M\xc4.H\x8dTp\xe7Q\x9au\x94\xd0iO\xaf\x96\x03\xd6^r9\xbd(t\xdal\xea\xbfMM\x97\xf2\xb2\xd4\x15\x84$\xb5\xef\x18\x8e\xae\xc2\x03R5\xe0\xd0f\xb8\x1f\xcf\x03\xf2\x92\xf87<\xeb=\xb0\x859G\xc9H\xc7'eC\xda\xd6&\x887\x1e\xee\xbd\x0c\xf8\xba\x9e\xdb$\xc0\xff4}\xaf\xde\xd2v\xbf\x91\x15_\xb3\xfa\x97\x1d\x81Ej|\x18\x90\x1e\x1fx\xe7\xab\x14\xf9R(K\xc7\xddz\xcc*\xc7\xdd\xf0\n\x1cw{\xe5\x95\x94\x94\xa3\x94\x94W\"\xbb\x97Wj\xe3\x82i$\xc0GS\xd6n\xc3\xea%\x1b\\\x04\x8b\xe4\xb9\x112\xad\x1dq\xd0\x15O\x0d\x19\x0dq\xc1\xf1\xe1\x10R]\xe2\x92\x8d\x88\xf4\xac\\\x00\x15\x0en^\x10\x13?\xd7\xf8\x1f3\xc7\x82\x19\xe8Y2\xce]\xf9\xfa\x82\x1c\xc2\xd8\xcb\xe0\xe4h\xce\xbd\xb6\x02\x81\xc7#C\xdffU\xa4\xba\x16\x8c\xaf\x94\x96M\xad\x17T\x9b{6`S\xaa\xcd\x7fK\x9b|$\xe06\x8a\x91*\x11\xbc\xc5mZm3\xe1\x1covw\xcf\xd1q\x02\xb9H\x9doj\x8a`\x94\xc1/D\n\x019\x06E\x0bp\xb1\xcc\xf4d\xca==\x18K\xca\xcbJDIH\xce_,\xdctd\xf2\x97\x8b\xa0\xf72\xaf\xa0{\x92\xbe\xd5\xf8uXy\xd1C\xc3crx\x15\x1d qA`/g\x1e\xda\x8a\xf1\xc1\xb7t\n\x18\x84\xb9C\xa23\x9d\xcf\x0dv\xba\xa9\x9c\xc7\xf7\xb4\x89\x84\x94\xf5\x8148\xd8P\x04\\1\x0e\xb6\x91KOY0\xaa\xd5\x14\x9e\xe1\xcbsX\xa4cPE\xdf7\x16\xc9WO\x02\xe3\x98\xacF\xdf?\xe8\xd4\x1e\xe9\x89\xcdy\xc46\xaa\xd5y\xc4\xe6\xd3\xe6_\xfb\xe7\xca\xbf\xbe\xf2\xb2M\xb1r\x9d\x9c\x14Y\x9a\x14\x04\xed\xca\x87\xa8\xd3WP3E\xde|\xd6^ev\x1c\xd2\x1a\xba\x9c\xed\xd4\\\xdf\x95\xf8C\xcca\xcf\xf3y\xc8\xe0\xd8T\xb6^hS0\x87R\xa0d\xe9\xc0\xe1!\x92\xd1t\xc1\xa2X\xc4\xe7*C\xdd!\xaa\xff\x12\xfa\xc17\xaf\x9eV\xb2\x9e\x9bu\x03\xa5(A\xd9b.\x03Vr\xeb\x15 \xa3\x9c\x04\xe5\x9bZ\x9f\xd1\x13\xe8t\x0c+\xfe\xd1\xaf\x9c\xd1[\xf6\x93\x8bS\xa7\x95\x84\xe1\x8b\"9\xa6@\xb09\x8b\xe5\xd4\x19\x89\xba\x06\xa2y\x99Lp\xee \xcd\xe6q\x1a\xbc\xc3\x12\xeey\x1a\x9f\x9e\xceK]\x08c\xdbF\xc4\xff\x92B3\x0b\x11\xf1sI\\\x94\xb1\xde\x89\xa9\xce\xc9\xf5\xcc\xa1\x8aD_\x9a\x03\xe4Z\xd69\x19\xb3\x1f\x07X\x15\xd9\xbd\xf7y\x9c\x05\xd0\xd29\xad\x88\x1f\x92\\b\xf53\xed\x19\xbb\xe0\xc9F\x98\xa1\xa0=\xc0\x9b\xd4\x17\xb2\xce\x1b\xd9\xc1\xbb\x12L{\x81\xcc\xc9N\xea\xd1\x86\\d\xfc(\xc3e\xae\xe9\xa2I\xfb\xe1\x8e\xc1\x81u\xe1\xe8G\x1d\x1aGm8\xf3\xa1M\xa0%Y^\xc6;gr\xb1\xa9\xa7\x06=*\x06W\x9c\xdb\xa1X\xa5\x9b8\xac\x08\xe1\x9b,\xf4K\xdb|\xac6\x15\xcd\xeb$\x0e\x9e\xd0\xf9\xa0tI\xea?\xff\xf8\xa3 E\x0fq\x0e\x81?\xdbO\xd9\xf1\xcd\x9f\xf3?\xda\x10aTd\xb1\x7f\xc11\xeb\xb1P\x7f\xb07\xe4\x0f\xa5c\xf8\xdcR\xb2\x8a\xe9\xd4\xc3\x0eM\xca\x9a\xd6\xf0\x06C=T\xd5\x8e\xe5\x93\xac\x7f\xd3\xafx=\x0b3?T\xcax=\xc7\x07\xfc\xc8\x12\x98\xa2\x87\x0c\x98\xf3\x00\xba\\<\xdfPi8\x14\xe4\xe9!\xf8\xde\xbau\xebI\x9a\xbb\x9b1\x14#\x98\x81\xef\xe5\x9d\x9b\xfa\x86B\xa8\n(S\xa1{cL\xa9\xb0\xa2\xa7+\xcf@$\xd7\x974\xafm\xfd\xf9\xea\x10\xf1\xca\xf4\xc7cSE\x97u\xfdb\x92\x96\x8f\xd3\x00I\x12\x86\x87k\xdf[\xd6\xef\x11\x9b\xf4\x1d\x175<\xfa.\x1a\xc0\xe75x\xe3\x98\xd0\xber\xda\xb7{n-\xd2VlO\x1c\xca\x9f\x92\xa4\x9c`\xe4\xd8[JZ\xb6'\xce#~\x13\xa3\xc24y\x85\x80\xeb\x94\x12\xd7 ,\x16\xea\x9c\x81\x8a\x8d\xfb=\x0b\xcf\xd2\xber\x0c\x87]wm\xa3)\x1c,\x0enk_W\xe8p\xf9\x0c\xc3\xe2\xc8\xe8\xf5%.\xa4\x95z\xa7\\\xe0l=8\x98\xe3\xcc\xc1\x90\xf7\xed y\xcb\xa2\x15\xb5\xef\x9a\x92x<\xa2\xe24\x1e\x06\xc7\\\xe0\x96\x8b\x82`1iMn'\xd0E\xaa\x1c\x99f\x96\xd3\x0fm\xe2\xf6\xd1\x18V\xda\xf4\x06v\xcc\xd7\xed>\xf3\xf5\xe6\xd53-\xdf5\xd4)TD&\xd2-\xa0\x1e\x8f%\xa3\xb7\xd2\xa7Xh\x8e\xe7\x98\xe4[\x92\x83\xd8O\xda1a\xf0\xcc\xc0Q\xb1\xcf\x16\x13\xf6\xeeN#+\xe9~1\xafR\x99\xef\xd85\xb6\x1dw\xec[8\xa8\xd1 \x8d!H\xe3S\xd6d5\xeb\x13z\x8f\x1fk\xban8h$\xd4.\xd1\xd5\xf5\xc7\xca}\x9cv\xea1)\xfd(.\x0cy=J\x8c\xa4\xfdP\xab\xf8\xd1Vo\xe8\x92\x85cX_e(S\xd5\xfe& kfc\xa7\xd1G\x8d\xe0\xba7\x8d\xaf\x81S\xf9\xf8_1\xaa\xed\x84_K\xdd\xf4\xb5\xca\xf7\xb6\n\x8e\xc1\x0d<\x04\xe1\x86\xb8]\x95\x99\xae\x03\x18.4\x9f>7\x0e\x8e183\xb80\xb0\xc8\x0c\x8e\xa5'4\x04\x17m\xf2x\x06\x06\xe6\x9c\xf3\xa7\xda\xcc\x89\xf4j\xca+\xba\x98\xb1\xf7\xf5|<\xd2\xcc\x871\xb4\xb2\xea\xd7\xb1MS\x11=\x96\xe7\x97 k\x10|\xed\x0c\xe6\xe6\x06\xd5\xe1-\x97\xf0\x85\x97\xeb?C\xbc{\xdd\xf4\x9f+\xa5\xfe\x13\x9f\xf4\xb4\x96\x91x\"S\x80\xaed\x9a\xd1\x0d\x7f\xd0\xd3\x8c\x16\xfcA\xaf\x8d\x98?\xe8iF\x03\xfe\xa0\x97\x1dy!\x1a\xdf\x7f\xd0}\x94Q\xf1e%\xb4\xa7h}\xec@\x84\xa2\x83\x8a\x9aU\xab\x8f\xafO\xdd\xda\xda\xd6T\xa9\x94\xa5&*\x99\xfd\xac\x99B\xb9\xb0Q\xbcEm\xc5\x9bE\ne\xac\xd0\\\xc7]\xbc\xc9\xe3!\x96-\x9eU\xb9\xad\xce\x90\xcb\x19\xc2LG\xce`!z\xe9\x12o\x93\xc7.\xe6\xe5\x17;5N\x99\xa3\x00\x95\xe4\x99;\x87+\xd1\x14\xca\xe7*\xe5s\xd5\xd4\xe3\x8c\xdc\x91\xc7\x1d\x8f\xd2\xbc\xe7\xf3\x04`\x9d\xe3\x17\xc9|\x7f\xbaT\xba\x86f\x9b\xb3\xa6\xabd\n\x0f\xc1Y\x95eV\xccn\xdeL\x13*Q\n\xbf\x06/JoV\xef9 \xab\xaa\xd7K\x8a\xab\xb4\xb1\xc5\x0d\\\xa8\x15\xa6m\xcb\x9b\xd2\xc6\x16\x08z\xf9K\x14\xc7\xafH@\xa2-\xd2\xb6\xc2\xc2\xec\xa6\x94\xd3\x85\xe2}\xf8\x12\x81\x88;\xb2p\xac\xc7uB`\xdb\xa5\x02\xddr\x95\x03\x96K\x1eZ'\xf3\xb1o/\xa1\xec\xd4\xbc\"[\xa7\xd8\xa9t\xce\x1b\xba\xe3\xf6\xe4\xd3\xed\xab\x9e\x1a\xb1d\x99W\xf8t.\xffM\xde\xe41\xa3Bu\xb1\x83j\xf2TqF^\xb0\xc9s\x92\x94OXj\x08s\x85\x93-%I{\xcc\xf9\x03\x7f\xbb\x1b,4\x97f\x05\xff\xc6f\x0c\x18\x9f\x88~\x16{Q\xf1\x93\xff\x93\xbbB\xfd\xca\x8a)0\xc4K\x1b\xaf\x88\xa3\x80\xd0M\xb2\xd2U\xc9m\xf9dlzy\xc5|\x13\x9fDw\xc3F \x87\xeb\xa4\xd5:\xea\n\xba@=dU\xbf\xac\x12\x92\xb1\x9d]\xb5\x89\x89\xf5\x0c\xf5\xb5\x00\xb5 \xcb\x17\xf3_\xad\x12\x99\x95\xfeR\x9b-F\\\x9d\xdd\xa7\xcdB\xd3~\xa7\xca[\x93\x9a\xdf\xa8\xf7\x9f6\x8bC\x0b\xdc\xc2& \x8c\xe7\xe8\xae\xbei\xe9\xa1!,\xf0\xe5\xcf|L\xa3m|\x0d*\xb2\xc5\x8d\xc5\xe5*5:\xf1\x89+\xc5@M\x816\xcf\xa2\x82\x9e\x8b\xb4ez\x98&c\xc8u9g\xc4\xc5\xd1\x8f\xc7j\xba%\xaf\xa3\x85\xa5\xad2\x98\xc1bTi \xf3Q\xad\x16\xdc\xb9\xb0\xba\xb8XJ\xd1*3\xa4\x05\x9a\xd0\x8b\x9e\x1e/\xb1\xac\x90\x05\x96\xd0+\xcd\xac\xd0\x1b\xaarE\x169@\x01\x83\xb9\xe9JY\xa17T\xdb\xc7\x08\xaa\x91\x8c\xd8\xe3F>D%d\x13\x8a\"3\xa6\xb5\xfd\x06\xa6\xbaB\xde\xab[\x0d\xaf\x8c\x9fR\xa8\xc9\x17p\x856D \xce\xfe^]8\xe9R\x96mYy\xe6\xcf\xc9\xb2-\xad\xe1\x9b\xaaj\xf8F\xaa\x1a\xbe\xbe\xaa\x86\xefFU\xc3\xb7P\xd5\xf0\x8d{5|Y \xcf\x82K\x05m\xe8@\x04\xcb~\x16%~\x0d\\\xfb\xa7\xe4\xd8\xafi\x88\xe0\x10\xee\x9cq\xe6\x8c\x1bPC%\x02J\x0d\xc2\x8e\xb2`\x15\xc5aN4\x944\x1d\xc6\xa9GC\xb8t\xdf\x9aC\xdf\x0c\x90/\xb0p\xb2\x8e%_\xb0\xc38\x0d\x8e\xce3?)\xb4Q\x14\x19?\xb8I\xf6,J\xdeE\x89fFCQ\x04\xd8Y\xf8qAX\n\xfeL\x0dO\xb9\xf4\x0d\x96\xfd\x8c\xfd\x0c\x1dk\x95\xa0[\x06jSes\xcd@\x1f\xf3\x1e\xeb@\x97\x0c\xd4\x04V\x05\x164\xa1\x1aJ1\x9cb\xab\xb7\x15\xb5r\xc8\xe7yz\xa6\x19\xdcY\x14R\xd2\xe0\x1c\xec\xeb\xbccH\xb4\\\x95\x0cjpo7\x85>\x14\x88\xed\x08\\\xab\xbf\xc4\x14\xcf&\xd8\xe7 r8t\xa9\x9aw5\x9d<\x8f\xa3\xe4\xdd\x0f\x83>\xa6\"6:\xad\xa3\xb6\x86rT\xbc\xc8HB \xf6\x91j\x9er\xa3\xf9@\x92JC'xg\xe2)\x1a\xe6{\xce'BcX\xab\x9d\x16y\xba\xfe\xf1\xd8\xfd\xbd\x1b\xcd\x87\x1a\x0f\xa7\x9e\x94\xf7\xe3k\x97\xd0\xb4/\xd4g*\xa1>S \xf5\x99J\xa8\xcfTB}6,GS\xe6vc\x94\xa9\xe4\xeef:\x97\xf3\x05~\xed^sY\xb96@&\xecg\x1f_\xd8\xd7\x9b\xe9\xbe\x08\xfb\xe2\xfap\xc2\xbeP\xa4\xaa\xe1r\xcbT\x05)\x87\xc3@R\x0dc\xc9\xb4\x07\xe9r\x19\x13d1\xd5\xa0L\x82O\x93\xd79\x15\xf8\xf1\xb8T\x03o8\xf0#? Hl\x00.8\xf0\xd19 6\xba|\xfb\x0b\xa3\xe1.\x1b\xa0<\x08\xadU\x12\xabjq\x8cz\x8e\xed\x10s\xea\x1a\x81\xad2q/+P\x8b\xef^\xb0 \xf5\x8b[\xc6\xef\xce+P\x8b\xef\x9e\xb6\xdd\xce*\xc6J\xc3z`\xb8\xbd)w\x02\x15\x9f\xcf\xbc\x90d9 \xfcRW=\xe0\x1c!\xb98\xa4\x06;F0}n\x8bG\x08c\xcak\xf1\x0e\xa1R\x8dn\xe7;\x84\xd0*\xe0^\xf0\x8f\xf0\xe9\xd2\x95\x9c|\x89\xa0~\x1c\xa7g\xaf\xf3\x8b\xa7\xe5\x8b\x8d\x06\x83_\xb3y\x1b\x98-\xe49\xeb0\xff\xfa\x11\x13?\xd5\xe0O\x11\x9c\xb0\xbd\xf94y\x99\xa7\xcb\x9c\x14\x1a,\xf9\x15\x0e\xe1\x9d\xd7P\xea\xa8A\x7fB\xd0\xa6\xeeF\x0d\xfb\na1\xdd\xb7,\xa3\xb7\xb8\x1e#\xc6 %Q\x9ai\xb5@\xcf\xe0\x10\x1e3#_\x15\x02\xae\xd3\x8f\xbd\xa9\xe1\xb3<\x0d7\x81\x1e\xfc7\xee\x8f\x8c\xa9G\x9eEE9r\x1f\x8f\xe1\xc4iT\xd5\xd5\xf5\xee \x1c\xc2\xb6F\x9bc\x1c\xba{<\x86G\x9a\x97\xfe\xddQl9c\xf8n\x0c/4\xca\xab\xef\x9b\xbd<:/ \xeaI\x8b\x91\xfbX\xd3\xcc\xcf\xc8\x04\xd9\xcd\xda\x0f\x0c\xb6YKX\x0d\xfc\x0b\x03\xe6\xf8\xa6\x83\xfc\x91A\x06,w\x9d\x1a\xee\xbf\x19\x9c\x8d\xf2\xf5\x1f\x0c\xd4F\xf9\xfa\xbf\x18(\xc7G\x1d\xe4_\x19d\xe5\xd5\xc1\xb2,h_\xf9?\x9dW\x8e\xf4I^\xfe\xd9ma\xb3^\xfb\xb96\x17\xca\xfff\xaf\x98\x14\xc2\x84\xf2/!\xcf\xe9S\xe3\x86\xda\xa5\xf7\x19f\x8fe)d\xd1\xc4\xf9-\xec\x9b\xdc\x95\xd0\x9d~\xef\x19\xee+\x1e\x9a\x97{\xad\xec>,F\x87\x838\x9c{\xd3\xb9p\xe4\xe8\xe0R\xf43\xf1\x8c\xa1$\xb6\x16R\x10\x1e\x04\xb4\x7f't\xdfI\xd2\x84\x02\xd8\xe69\xb1\x12\xe6\x9b\xaa\xdb*\xe7c}2R\xf9\xf6\\\x06\xe2\xc0\x0dx\x047\xc0\x91\xe9x\xdbP\xea\xd5\x8e\xc2\x99F\x03\xfe\xefZ\x01\xaa\xd4\x80\xaa\xa6\xe0\x9fZ-\xb1\xc0[\x94ngp\xaa\xeea\x83S\xd5\xfa\x98\xb4}K4\xa7w\xab\x84\xd3Z\x0f\xd7\xf0\x9f\xd1\x1c\xf6\xb53\x84\xca!W=M\xffm\xa7x8\x1f:\xfdC0\xb0R\x8d\xab\xeb\xe2\xbf\x1f\xc3c\xba!\x1f\xb3-\xfe\xc7\x1f\xcc\xff\xe4\xf0\xf0\x10\x1e\xd7\xce(\xea\\\x13\x06?\xe8J\x15u\xeb \xd3\xd5S\x15z-\x03\x18\xbaU'\xee\xed\xe9TC\xe8d\x13\x10\xa7~\x18%\xcb\x89\x9fDk_c\x1f\x19\x8d\xe1H\x9bX\xc8`%\x91\xb5\x8d\xea\xcd\xd3$\xcd\xd7\xbe\"\x07\x10&x\xfa\xc5\xcf\x93(Y\xce\xe0qM\"Fc\xf8\xd5\"\xcf\xd1\xb0\xfe4\xd89}\xa9\xca\xab\xc6Bcf\x10M\x83\xff\xb01G\xfc\xaaX\xd4\xd1h\x0c?\xd1y\xfc \xc3=/\x91\xb6E6,\xc1\xf3N\xc24(v\x9f\xd1\x0f\x86YO\xa2$\x84u\x9a\x13\x08EF\x9f+^\xd8\xd6\x0c\x0c\x1f\xb91\xd0\xd5\xd8\xe6\xa99\xeb\xcceq\xeb\xa7\xa6\x18\xa4\xc23u\x1b\xff[\xd7\x86}\xb0\xac\xc5L\xc4\x91\xf6\x0bJ\x8b\xd6O\xda\xe8X\xf6\xb4\x91c\xa7yj\xa87\xd4\x0f\xbaa\xd7R\xc4\x0c~\xb3:\x85yA\x10;\xf1\xa3\xe2Ef\xf0X\x03\xc5+x\xff\x03\xdd%uj\xb8\xa6\xbaL\xeb\xaa\xdb\xd2\x95I\xeb]\x89\xab#\xb9\xcf\xe0\xb9\x86mi*\x12f\xf0R\x0d\xb9H\xa4Ev\xc4e\xcdP5\xb4d\xda\xecE-\x15\x996\x7fQ\xe6\x97\xab\xe7\xdc\xb1\x93q\xe1\x86nr\x17\xe4P\xb1\xe1*l|\xae\xc1\xc1\xbf\xeap\xd0z2\x98M\xfeX\x0d \x1cV5Ly\xda\x91\x1bgB\x03Q\x98\xe5H\xda~\xf5\xda\x16\x15b\x85;\x12\xda\x91\xe31T\x1f\xd1\xe9!\x96\x84\xbb\x83\x91\x90}l\x06s\xafh\xdd\xd1\xacs\xff\xe5\x0b\xafw\xd3\xf0>\x05\xf9\xd9\xcf#\x8a\xf0?3\xed;\xffH\xef\x89a\x18Mx6\x8ca_8Z,HPF[\">\x85\x9d\x11\xdf\xa9\x9e\xe2}3\xfe}\xf5\x15\xbc\xa4\xff\xbc\xc2\x7fLtq\xa7cV((T4Z\xd5\xd8\xff\xd2\x9eo\xec\xa33x\xf5aq\xdf\x96\x98\xf0H\x16\xa6!\x9b\xc1\x13\xc5\xcc\xd7S\x7f\x15S\xfc\xbcRu\xbc\xa4\x12\xf9\xbcL&\xcb<\xddd(ys\xfd\x95\x91\xb3{.\xdeW\xf5\xe8\x17+\xc9Y{Z\xd9\xce\xe20\x92|\xd9\xb5\xad\xec=3(\xacvJn\x9a\xaa\x1f\xb5(k9 \xf6C\xd3wz4\x86\xa7W\xb5\x97\x85 \x1aT\xc1dCw\xf3.\xcd)]'\xaaey\xa6\x19\xe0\xcf\xba\xd6*\xb5\xf1\x0c\x9e\xa9g\xbaJ\xea\xab\x89*\x11\xcc\x90(\xfb\xa0\x8d\xfd\xb0>\xb7[l\xc4Ul\x98\x86-N\x9b#\xd2\x1aK\xb9\xf5a\x06o\xcc@\xfc\x90\xda\x8a\x80\xbf\x97\xfc\xfe\x934w\x19C\xa59\xfc\xfb\x8c\xb4\x95\xce\xdf~\x1b\xa9A\xe4\x86\xad\x19\xbcV\xbf\x82\\\xac\x89\x9a\x10\xf4\xa0\xf8\xdet\xdc\xfe\x1f\x1d\x06\x93J\x17>\x83\xef\xad1\xce@2vq\x1bz\xb9\xc9\x89\xcce\xa8\xca|'w\x19j\x9c\x1c8)\xad\x87y\xb5\x99d\xcf\xf8\xa6\xec?\xaaQ\x85J\x8a\x0b\x8fY\xbc\xba>5\xcc6\xa1\xf3B\xfa\x12Z\xd4\x9e1\xa5\x17\xd2B\xee\x85\xb4\xa8\xbd\x90\xee5S\x19-4\xeeF_b\x8b\xfe\x03\xdd\x8d\xac\xfc~\x86\xc4\xfb\xe7\xf6\x0e-\xe9\x10\x87\x16\xe6\xa6\xd4\xb6\x13\xa9\xa1}K_\xaa\x0d\xd6\xd039\xa7\x14,\\\x9d\x91-5X\x80`QQ\x95=\xd5\xf0\x0d\x0b\x845\xb9\x9ed\x08\xa5s= Y\xd7V\xe9\xd9\xb1\xa9{+\xfe1\x0b\x17\x94-\x03\xcd\xa3e\x94\xf8\xf1\x0b\x9bW0\x12I8\xa2X\xbd\xb1\x84C\xc8\xcc\xb3z\x81K\xc4\xd5\x1d\xc1&\x8fJ\xadU{\xce\x12(Tu`\xab\xae|_j\x8d\xf9\xa7\x9d\xc4\x0b|:\x9f\x1b\x03\xbf\xcf\xe4/\xbe4\x04\x9a\xf3\x1a'?n\xd6\xd9\xeb\x14\x811;\xc4\x07\xb7.\xd7Z\x01\xd6O\xe8\xfc\x8d\x06b\x8d\x16\xb0\xae*(\x05\xd1\x08 \xa7\xba\x1e\n^P\xc5\xb9\xa9?{f\xaf\xa6\xd3\x05>v\x0c\xd0\x1a\xc3r\xcd\xe3\xc8\xe3\xc6ig\xc3\xab\x92\xfb\xba\xabcc\xafX\xd2\x83\xad\xa8\x99],\x8a\xedn\xe9\xdd\xd5\xc8\"{\xfen=\xab\x93\\D\x8a\x02\x04\xef\xc7 :Qg\xdc\xff\xea+\xb8\xf0\x82t\x93\x94\xae\xaeos\xbdY\xbc&\xb93\xd0d\xcc\x1a\x1e\xe3!N\xd4\x941\x94\x98\xef\x97JMT\"\x89r\xec[\xe1^\x982\x89 \x81\xae\x13\x06\x17\xae\xc2\x01\x05z\xacEu\xd7\xac\xb8\xd2V\xc8\xc9\xb4\x08{\x85B\x87!N\xa1\xbb\xcfL\"D\xb0\xb3\x08q=\x03\x19>i\xa6\xb2\x01\xc5\xa6?\xa32\xa3_\xc4\x04q\xed.&hK:\x9b\xb8\x8fK\x1d\x1b<\xb3\x8e\xf4\xdd\xf7c\x94P\xded\x19\xc9\x1f\xf9\x05\x91%W\xd9\x99P-\x86\x13\xaa\xfa\xbb\xe3\xcf\xa0\xc4\xf1g\xaa\xad\x10\x91S_\x94\x16\xff\xb1\xd4H\xcd\xc0\x95\x034\x11\x89Dc`\x14\xf5\xe9\xc6I\xac\xe2PR\x844\xc6\xa1D\x08\xa6\x8fC\xf1\x11F\x1b?\x82u\xf1\xed\x84\xf7\x82w\xecq\x9d\xc6\xc4\x18\xe1AO\xd8\xb2\x99G\xe4\xc3\x9f\x04y3'\x838\x0d\xe8<\x9d\x9e\xb6\x9d\x9d\xa5@\x83\xcd_\xdazUU\x02\x06\x9d\x02J$`\xd0\x98\xa2\xb2\x06\xdf\xca\x9ao\xfbO\xfbXy\x80J\xd8\x1b\x0d\x0e\xb2,\x0d\x91|\x84Wy\x04^7v\x99\x9e\xaa\xcd\x80\x078\xe4\xe5R\xfa\x87[D\xcf\x84\xfb\xb2\xd3-\xea\x96\xd0\x8f\xd8\xe9\";=\xa2\x8f\x7fz\xf8\x98\xc1\xa63J\xf5q\xb2\xad*\xca\xd7\xe6\xa6>\xe6$\xed\xd27b\xa5\xdb\xe1#\xaf\xd2\xb3\xee\xbe\xe6\x83M\x87j*\xa4\x0c\x9d,\x81\xcc\xfb\xf1\x95~\\Z\x9bS\xd7F\xb3\xb4i\x1d\xbb\xe2P^\xe3R\xfd\xc2\xf2\xa5*c\xbc\xaeC\xa2f*\xeb\x93\x1a\xacU\xe3T\x0d\x96[\xc0\xc8\xeb2\xaa\xcb~\xf6\x06\xe3<\x89H\x8cN\xe5\x1f\xb2\x114Q\xb3\xa2\xa1\xeafZECK\x8f$e~qL~\xc3\xec\xb7\xa6\xcc\xa0\xdbF\x8d\xa8f\x9d\x9f1\x1c(\x881=\xbb\xcb\x93}\x85\xb3!\xee\xe4\x93\xa9$ \xc8\xb0\xad\x12\xd5Q\x84\x0cUT\xa5\xdeT\xb8\x8a\x9e\xa3\xcb\xa9BAy\xfe\xb3\x1f\xcb\xf4<\x9d\x04\x96\xef\xdb\x05\x10\xdf\xcb\xcf\x04\xf6\x99\xebu&\xbcJ\xcf\x0c\xc7\xc2\xed\xe9\x9f\xe2X`\x03\xb59\x19(B\xc8\xcf\x04\xe2Q|\xe8?C\xa6\x14\x1eR\xa63\xfd\xf1\xb8\xfa\xe1\xa2\x92\x91+\x1a\x87\x9d\x14\xd6\x94\x88o]#1ap\x9d\xbd\x1a}&H\xdbG\xcc?Q\x02\x13\n\xf0\xe0\xee\xfe\x9f#g \n\x9f\x98\x949\x1a\xc3\xa6O\xca\x15\x82z\x1fp\x91\xe6\xe0\xd2\xaf\xd1 \xaf$p^Bn\x8c\x13\xceR\xff\x16\xa31N\xf4\xfe\xd7\x10\xc07P|\x0d\xc1\x8d\x1b#\x88O\x82\xb7\xcd7O\x02\xf5\xc1B\xb7v\xc4O\xb2\xbe\xb2\x00ei\xa3\xc2 \xf0\xe3\x98k\x0d\xc8\x18N\xe8\xbboE\x11\x87\x18O\xe1\xc8Cs\x85\x1fG\xff\xae\xa5\x07c\x19\x07zE\x1e\xa1\xe3\xed{?\xbfG\xadBz\x865y^\x936\xef\xab\xfa\x1a\xf3$\xaai\x00\xd7X\xe2\xbe\xa3\xdfc\x7f.\xa2\x98PN\x03S-\n\xef%\xaf|\x0b)Z\x0dY E\xac\xce\x9c\xc07\xacVa\n7 \x82o\x0f\x99;n\xc2\xe2\xbbqs\xf39}\xcc\xd6JV]u\xcc4\x19=E\x17\xdd}\x1fC[u\x95\xb5\xcf\x98\x9c\xbf\x8a\x96\xab\x98\xce9\xaf[I$\xc1P\x1d ]\xc6\xff\xf5\xbb\xf7&\x0b\xfd\x92\\\xaf\xfe}\x02e\xdfV\x1f\x90\xc1vV%h\xe87\x14\xa9\x88\x0f\x15\xc3\xb4:.,0\x86\xc4\xc4\xb9\"\x9f\xeaj!&A\x1a\xaa\xca2\x8eQ/v%\xed\x89\xa1Nx\xc5yY57q\xd5^\x1dt]\x9a\x14Z\xd5M\xe71\x07r\xcc\x96i'\xcb\xf5\xc9\x01YYN\xda\xb4\xe4\xc8\xd1\xf5\xfa\x97\x15!qU\x04KG\xd0\xd5_i\xcc\x19\x96=\x80uD\xbf\xa0\xae{\xfa\x9er\x00\xc6M\xd4W\xc3\x99Tpr\xa7\xd7\xe6N\"\x1e9\xcf\xd2\xbc,Z\xc7S\x9f\xbd\x85\x06\xe7\x99\x903\xf8>N\xe7\xee y+[\x83\xf2\"\xc3\x91ST\xa7\xfc@\xc4\x8ad\xdfL\x83\x92\x94\x93\xa2\xcc\x89\xbf\xeeH\xeb\x1d\xf6'ZT\xf5v\xf7\x0e\x0f\xe1,J\xc2\xf4\xccK\xfcm\xb4\xf4\xcb4\xf7\xd6\xc5\xb1\xbf%\xb4\x0f#\xddC7\xefsV$.\x88\x82k\xa3\x87\x1e\xff\xda\x9bW\xcf8\xc61\x0e\xfe\xcd\xabgn\xae\x91\xe9C\x9e\x0c\xa4\x8b\xa6\xbeL\xef\x1dyX/W\xb8\xb6\xc1!8I\x9aP|\x8e\xbcUN(G\x9c\xd2\xdf\x05)\xbf+\xcb<\x9aoJ\xe2V\x9b\xcfa\xb2N\xa3\x1cq\xcd\x00\xd13\xb3\xfb\x1ec$\x9cq\x15\xd3;\x1a\xd7\xdd\x9d\xa7\xe1\x05\xe5\xd9H\x12>ZEq\xe8F\xc8\xa6\x05t\xeb\xba=\xc0\x9c\xac\xd3-\xa9\x01\x1b\x93\x95\x93m\xfa\xae1Y\xa9\xea\xe8}/E\xc9\xeb L\xc9\x95\xbfR1+R\x89Y\xbeJ\xcc\xda\xa8\xc4\xacB%f\xc5\xfcAOb\nx\xca\xc7\xbe\x1cUKZYU\x12B\x98>+\xe0?\x81`\x95\x8f\xc1\x97\x0bV\xd1u\x14\xacr.Xml\x05\xabt\xa8`\x95{\"x\\\x84\xe1\xfc\xc2B\x04\xad\x84\x0e\xde\xd5\\T\x88\xac\xc3\x85\xbc\xa0\xf5QT\xa8\xba'\x02\x10M\x90\xd5k\xcc\xed\xe2-\xe5\x9f{\xad\xbcg]\x14\xf1T\x8f\x18\xfb\xf0\xfa\"#\xac\xd7V\xdd\xace#\xca~\xe4i\\|\x17\x04$+\x7f@\xf5\xaf\x89\x9f30})\xe6v2\xb0\x8f\x11\xba\xedY\xa5@\xf4\x11To\xa4\xdd \x8c\xceO\xa6\xac\x08\xbad\xea4EZ9\xd1\xd3\xe5\xb4d\xde{j\x00\xe1>\xbb\x91BH\xaa\x17\xbd\x1f3\xabs\xafp4\xdd\xad\x96\x82X!\x15\xc4|;A\xacX\xa5\x9b8\xacX\"ka\xc7\xb4/\x1a>M\xdd\xc0@\xe4NH\xff\xb6(\xbf\xcf\xde\xaab\xdb8x\xfdw\x1bN\x84\xd6q\xb0\xeaO9\x14n\xc6\x0e(\xbb\xd7\x86\x97\x07\xbc\xf1\x17\x15\x0f;-\xfa\xe5J4D\x7f\xb6\x9f2D\xe1\xcf\xd9\x1f}\xdch/\xffG\x92\x06\xf5$\xc1F^d\x1e\x19\xd5z\xe9)C\xd2\xc3\x03=yH,\xbdN65\xac!\xa5,\xf3\xd3\xb0\xcc\x13\x8bl\x841\xefm\xd2\xc6-5p\xc8\xdc\\\x06\xa6\x0d]U=\xd6G\xd5l\xf9\x11Zi\xed\x8e1\x89\xdf\xa34$#7\xd5x>\xac\xb1\x98\x8f\x13\xd4d\xd3T\xd1\xc6w\x9d8\xda\x12\xb1\x86\xa6\xca6~\x1d\xbbj\n\"\x91m\xf5\xaf\xbe\x92\xdd\x16Q\xa4\xb27f\xb5\x84\xf7\xb2\xf5D\xdd\xf8)\x1cB\xd1\xac\xf6\xc7\xa6rIJv\x82>b\xe7)\x95p\xc5\xb0\xe9\xacJ\xcd6\xe229\xee\x0c\xd1+T\x1b\xcc\x98\xd9\xe0J\x9a\xb3q\x01\x10\x971O\x16w\x05x\xd5\x88_n\xcf\xb5)q]\xec\xcfI]3\xc4\xe4\x08\xd5i\x0e8b\xa3\xcc\xad\xcb\xa6\xa5\xad\x16\xc3\x89\xab&(L\xb0\x97\\1\xa2\xe065\xc4\xa6\xde\x7f\xc5\x0c\xe6\x1a\xc0\xc6:\x89t\x17\xfc\xe5 \x8eQ\xbeJ#]\xc6\xabA\xc8Q\xe3b\x94\xe8\x92\"Df\xa5\x9a~E\xb5\xd5^\xea`i\xeb|\x94\x1a^\xae\x99y@\x93\x03\xaa\x93y@CP\x18\xf7\xd8a\x11\xcc\xbcd\x8fk\xd0\x1c'\x8a0}U\xfe\xa5\xe1\xdb\xd4B\xc9(\\k\x86b\x0e{o0=i\xbb\xe8\xa8\xc1\xf2\x1d\xba\xb4+\x8dS\xb8\xe1\x88K\xed\x8eS\xa1\xf0\x84\xde\xe39wU\xcd;\xf4 \xd7&\x03\xbc\xa2~\xd8\x04\xbb9\x8f\x1b@]j\xfe\xa1;\x18G\xc9;\xcd<=\xc3\xc7un\x07\xdd\x8c\xb5<\x9bR\xa5gS\xa9b\xa5\x81\xb3\xd3I\xdf\xc3\xa9T{8\x89\x0bYg\xa5\xa7\x93\xb8\xb0|\xc9\xc9\xd4\x00\x15\x027\x18F\xed\x0c\xcepx\x08)<\xac\xf1\xfc\x94'#A'_G\xce\xb8\x80\x99y\xb9\xd0\xad$\x08a\xc5P\x96\xb8\x8e:[\xb1\x1c':6\x15\xd0\x1d\xf8\xb1\xd0\xa6mQ\xafkh`\x91h#\x13\xa1\x8du\x1aZ\x8b\x90iH\x8cC\xaaO%M8/\x0c:I\x803\x07]u\xce\x8c\xa2\xc6\xe1\xa1.m30\xbe\xa4\xabK\x9aa\xd9\x0f\xa5\xaa\xc9\xdc\x15\x0e\xae\xe5\x87\xc0\xfeT\x85\xfeI\xad\x84U\x14\x85n\x15\x83\xde!\xa1K\x8d\xe7;$u\xe9'C\xeaGX\xd6\x99\x83\x98\x85\x98U\x8a\x1a\xb9'-\xfb\xcf\xaf\x85\xa4\x16\xa7\xea\xa0\xdf\x9b\xd6\x03\xf8\x1c2\xb9\x84*w\xacP\xe5\x8e\x15\xaa\xdc\xb1B\x95;V\xa8r\xc7\n\xa5\xe6\x8b\x98?\x91Z\x10\xdcP\xd8\n\xc2\xcaV\x80\xbf\xa6\xb7z\x05\xa4\x17R\x8b\x03\xaa\x07Te\xa5\xc3\x8fo\\X\xd9\x1a\x17\x88\xc4\xb6 C<\xb3hkjo);O)\x0e\x8d}\x914\xc1'+\xf2N%$n\x90\xba<2)\xb9\x12\xe6\xeb\xd3oF\xfd\ns%\x92\xd1m\xf9\x99\x8b*\xec\xe3\xd2/uJ\xeb\xbcO\xb2\xbbK/\xae\xf7h\xd82\n\xb4\x9a\x11\xc8\xcf\x9c\\\xd1Z\xef6\xfa{Q6\x84\xf4\xe8\xa5\xb8\xa4\xc3q\xfa\xac\x1d\xfd\x94\x02\xbf\xe1\n\xdd\x94\xaeF\xb3\xca\x08-Z\xe0RK\x1d*3\x9aP\xfeB\x0d\xc3\xac%\xe6\x02d\xccbb\xe1\x9a\x13\"\xa0Y\xaf\xb8B8\x9d\x12t\x8b\x10v\x9a\xdau\x0dk\xd0\xd4.\xab\xfeYhj/\xf8\x0cVx\xa4\x06\x9dW\xa0\xf6\xf6\xb1S8\x84\x95\x17%\x0b\x92c\xaeS\x8d\"\xe1\x0c\x0ea\xc9\xc5!5\xd4\x11\x1c\x82\xcf8u&\xe2h\x93\xfa\x9d\xd7\xd0\xe4\xdc_g\xb1>\x07\xe0q\x0d\xced%\x0d\xec#8\x84\xadU'\xdeqH\xe1P\xc5\xe5Q%\xfcw\x0c~\x9d\x86$>b\xbd\xd6\x81\xbf`\xe06%\x80^2\xd0*.\xd3TL\xe75\x83\xb7Tp?\x17\x9b\x16i\x97'\xa1Q\xf4\xc8\xbaPP\xf1\x05\xb8g\xee\xc8$/>\x15+\x84\xc5\xb2x\xc7\x9c1<\x7f;\xe6\x8a\xe7\xe7~6r\x7f\x7f\xdfe3\xba\xd7\xafp\x08O\xb9\xc4\x87\x88\xe9\xf4>\xa0\x16\xf1\xeaP?4M=ma\x98#\x94\xe0\x99W`m\xa0hq1r\xbb0T\xccf@KR\x1e\xe3M\xb6AF\xee\xaf\"\xec\xd70\x9b&A2J\x82x\x13\x92W\xc4\x0f_$\xf1E\x8b\xcb\xec^\xf4\xd0\xa3\xc7\xcd\xaf\xf0\x10\xcaJy\x95\xf0;\xa7U\x9fj\xc5V\xce\x9f\xb9\x8d\xcc\x89\xcd\x151\xf5]L\xfb[\xfaI\x85\xe6\x8d9T\xd1^\x9c\xba\xbe\xe8\x01k\xda\xf7V~Q\xad\x1d\x9d\xf2\x90g\xfb\xacnQ\xb9\x14\x07\x95T\x0b\xd2\x9b\xebd\x0c\xcfu\xf3(\x99C\xcdi\xc4\x80\x7f\xc9\xa3\x92hg\xfc\xbd\xde\xfcq\x8e\xbe\xcc\x94v\x9d[\x04\x8a\x89K\xb0\xc0\x94\x1d\xa2l/+&\xf5\xd7\xbf\xe6d\xe1\x08\x97.\xda\xae\x8a\xebQ\xe0;\xddu?Y8\xf05/a\xdcF\x0bTeo\x1a\x16\xff\xd6\xbc\x9a\xb1p\x0d3\xbe&\x16\xaey\xe5\xda\xb8\xb8\xe6\x95\xf2\x1893\xa4\xe0\xd0[{<5%V\xba\xa4YK\\\xc8t\xc9\xd9IqiMKw*\xcd]\xaeQ\xf2)\xe3\xfe\x9aW\xdb\xa4\xc2h\x9by\xf68[(\x8f\x19\x17\x97,v\xbc~V+-(J_\xd6^b\x1c\xeb\xf0q\n1A3\x06A\x05\xe4\x1b\x92\xa2\xf7\xf9\x18\xde\xed\x98\xdc`\x07M>8p\x03\xdc\x0ds#\xd7l,'\xf4K\x9f\xb9\x85+\x03\xff\xafN\xdd>D\xd7\x1f]\xa1\x9a\x7f\xb0n\x7f\xe7}-[\x8bn\xab\xa7\xa7z\x93\xa1\xaa\xf1\x17\xba\x86E\xd5\x1f_\x94)l\xd8&T\xa7\xc4\x18\xce\xcc\xbb\xcdj\xacL\x9dWQ\xf3\xe6\xd0\x1b6Y\xd3\xcet\x84@2\xf1Q\"\x11\xd6\xa8\x19\xcc5[o\xe84\xbe\xb60q\x1b8\x1e\xf5\x94\xb4\xec\xd7|-\x04#E9\x9b\xee-\xef\x1da\xc7(\x88\xc4\xd5\xc7\xe4\xb7^\xd2\xb9\xe6\xd51\xb1\xcb\xf4>\x8a\xf5\x1e\xc3\\\x9b\x83q\xed\xc7\xb5\x83\x81\xc3\x9d=\n\xd0E\xa1 \xe1\xa8^ar\xa43\x1a\x83\x03l\xe9\xbc\xda\x06Uq\x9b?i:\xf1\x9d\x16\xc5+K\x89u\x9a}MV\xfc\xa6Z^S{\xb1c\xa2\xd0\xd5^D>T\x88\x02L\xb5\xfd\"\x0fIN\xc2\x91\x9bhV\x94\x1fB3\xf8I\xb1p\xd5\xd4\x1di\xa6\xee\x91n\xea\xb8h;\x83#\xeb\x99\xd3\xf7e4\xae\x04\xfc+\xb5w\x0e0r\x1e\xc3C8\xf6\xcaT\xc6\x85v\xa2W\xba\x97\xe1\xc0}i\"T\xc8\xb5i\x14<\xf4JpP\x06 :B\xad\xfe\x11,\x17\x064\xa4p\xa4\xad\x87Yo\xdf\x9fR\xe0\xaa\x92j\x95{\x1f\xbc\x94\x05i\xa5\xb7 \xd5fCF \x85u\xe8\xf7\xf7]s\x89\xcc\x9a\xd7TL6T\xffm\x9b\xd0\xea\xbf\xf8\xcdke\x13Z)sG\xacTQ%+UT\xc9J\x15U\xb2RE\x95\xacTQ%+\xa5Mh%lB+\x8c\xc8\xbf-\xb5\x04\xb1g\xbd/W\xe6\xa0\xf6\xedP\xf4]\x91no\xf5\xf1\x0dE[[C\xd1\x97(\x94\x8e\xd1\xca\x14\x85\xa2\xb7\x88d~^\x90\x90oq\x85X\x85\x91\"\x1bt\xdd\x7f\xd9\x04\x1fd\xf2\x12!)\x9c\x1bSk3\x99\xff|\xa9\x16b)\x10S\x91@\x94\x14\xa5\x9f\x04$]\x00\x0b<4\xebC\x12\x1e,\xf9$\x8aQ=\xa52\x8f\x89+\xf1R\x16\xc6g\x91\xc3\xa0y\xe56\xe6\xb5\xe6\xd5] \xca\x0cobydn\xf3R\x9cD\xd5\xe31~\xca\x0f\xbf+^\x93\xf3\xd2\xd5L,\xd7\x1bZ\xf7\xbc\xd3\xe3\x92\xf2\x07\xac\xaa\xbbN\x03!C\xafO\x1b\xa4r\x95\xd9\x02PN\x90\xec\x15\xd7\xea\x88W\x07a\xec\x942@\xb9)\x95\xbd$b\x7f^\xa2\xabWc\xd5\xb4\xb4d\xd6\xc1g\x16YB\xad\xccu\xac^\xc9&\x97$T\x12\x17\xabR\xc2\xf9|5\x98_\x9b;Xz\x8d\x87\xf0\xfb{\xd0\xba\x0fo\x06d>-\xdav\xa3\xd6nT\xbf\x85\xf5A\x06X\xd5\xe8\xc1\\\xfb\xf2\xa1\xa6\x8b\x92\xcf\xc7~I\xb0\xbe\xe8\xebhMt\"\xf4\xba\x9a\x04\x8d4$\xc9\xf5\xd5\xbc(\xc5\xa7\xcb\x92\x8aL\x0d7\xffo\xc3\x87\xe9_\xad \xf6\x9b\x91W\x92\xa2t\x93\x11\x05\xf6O\x1c>#\x93\xc7Q\x91\xa5\x05f\xe6w\xde\xd2\xe3\xe3\xa6_\x96~\xb0\xa2\x07\xb5xI\x05.\xbe%4,\xa1\xdd\xb7\xa4\xe0\xbd~5\xb4G\xec[\xf4h\x82\xd7\xb9\x9f\x14\x0b\x92\xcb\xba\xd6|\xa3\xd75\xeb\xcfI\xdf\xd0(\x8f\xe9*8\xf4\x98u Jx\x9c\xb9\xe9$\xa4[\xf9\xa2\xca\xb1Q\x92\xf3\xf2\xe6\xaa\\\xc7\x16\xban\x0c\xce\xe9\x1e\xf0\xc2\xcaV%;(\xa5\xc9\x0ed\x17K\x80pa\x84\xed\xca?\xb2\xebT\x9f\x94`n\xf1\x8938\x84\x93\x0b\xca\xd0\x15\x9byQ\xe6n\xea\xc5~Q>MBr\xfeb\xe1:7\x9d\x11\xdc\x80\xe9h\x0c\xa7o\xbd_\xd3(q\x9d\x99n\x9b\x8a\x0b\xed\xfc*D\xd5l\x08=\x13\xd4\xc9\xfdpdZv\xe0K\x7f^\x99{\xc8y\x99\xfbA\xf9\x84\xe7oz\x92\xa7k\xde\x8fF7\x98W\xc4\xc8=2\x18\x84\xe8\x85!<\xb43\xcc\xeaG\xe7\xf3\xdc\xc0 i\x9fR\x1aTy]\xd6\x99+\xe8\xc7%\xb7yB\x8b\x17\xf9\x8b\x8c$\x1c3/eIq|\xa3\xc6\x16\xaa\xfa\xec\x06\x07\\\xd8\xa9\x06\x8a\xb88We3hw>\x863\xfd\xa4\x83q\xe2\x9bYf`\x11 #\xff\xb5\x9aM\x91\xcbc\x06g\x83\xc7\xa2|\x81\xb3\xdb\x14\xf1\x94\xe3`)u\xb8\xce\xa8\xfa2\xe7< $%\x96\xd6\x86\xf9\xa6\x84\x8bt\x93\xc3\xd7r/\xda\x99f\x96k\xda\xe7\x06'\x84\xa2\x81\xdbN~\xc8x\xd7\x9b\x14\xe8_7\xb3\xd8\x8f\x92\x9b\x8d\xd9\xff\xc8\x036\xf0k\xc2\x88\xa7\x181\xcc\xe0\xe6\xff\x8d\xd6\xfe\x92\xfc\xebf\x0b\x87\x12\x8f\xbb\xfd\x14\xaeSl\x97\x8e\xd6\xb0\xd1\xa4\xf9\x0e8\xa8Fv\xc0\xd1+\xdb\xd7K\xed!\x80\xf9\x9ed\x9a\xcb\xe6\xb5\xf6\xcf\x7f\x89\xc2r5\x03g\xba\xbf\xff\xff\x93c\" \xe5W7\x94\x073\x1d\xbb\xa8\xd0\xc8\xf0\xb9\xf37a\x94v\xe6\xce\xea\xb8P\x9f\x8d\xf4\x8bzC\x117G\xaa\x1d\xb1tA\xd1h\x1c\xd7O=\x9d\x11]\xado\x96\xacL\xb5\x89\xe8\xc48\xcc\x7f\x88n\x1f\x04O\x17P~\xfc\xbdQ\x9e\xcbtE\xe22o\x0d\xee\xe4\xf5-\xec\xc3C(lw\x80z\xf9\xad\xcd\x7f\x91:\x9c\xf1M\x92\x93 ]&\xd1\xbfIX\x99\x89p\x8e\xbf\x16\x81A\x94\x89\x10A\xee~\x81\xd4\xdd\xd3E\x8a~\xca\xd9/4\xa4\xf8\xd3M\xe4\x06K\x91@\x99\x8a)\xad\x8d\xf7Z\xb7\xa5\xe5\xa5q\xa4\xe1\xc5Vg,\xc0\xb0Tz\x9e*]\xab\xacm\x916UH\x98Yu'\xcb`\x95\xef\xd0}p\xf7\x8e\xc4\x88\xa7\xd7}\xd6\xbe\x9eY\x1c\x95\xeeM\xf7\x9b\x7f\xdd|x\xf2\x7f\xbf}{\xe3\xdb\xd1\xcd\xe5\xc8[DqIr\x0b\x0fK\xfe!\xc7\xa9\xb2\x0dEkY\"\xdc\x8e\xfa\xba\xdd\xdf\xc8\xb6\xbf7\xbf\xf9\xd7\xcd\x1b\xac\x9b\x9c\x11 \xda\x0f\xfb\xf6\x1f\xc6\xaf\xfe\xeb\xa6\xddw7\xb6\xdf\xb5\x9e@\xec\xc0\x9er\\\x80\xc8E0\xef\xf0^$~\xf8\xbdn\xd6\xf8!\xcf\x9d\xd9\xed\x850JuM|\xf0-Li\x13\x0d]Gm\xcb\x9b\xbe\x85\x87\xed?g\xf0\xbb\xe4\xdcg\xb1[\x82\x83\xed?G\xbd\xad'a\x89\xfb\xa01\x1c\xca\xf4\xa6\x01\x1c\xc2IGeSg\xb2\xa5\x7fu\xe2\xac\xe9x\x17c4\x07\xbb\x0b8\x042\x86\xd4]\xd8\xb8\x13\xf3uR)\xeau!]\xec\x14wK\xd6^\xe4\x96\x94uq\x1e\xc5i\x11%\xcb\xd7\xfe\xd2\x81\x19l\xf8\xdd\x17\x19I\xea\xbb>\xbf{L\xe2E\x1b\xdeyM\xe4\xb9\xbe\xe5\x01\x81\xed\xa3\xf7\xfdH\xe2\xba2\x86TeR\x8eLI\xeaX\xfdq\xa4\xe8\xbd\xe7\xad\x81R\x1e\xdf\xa7\x88\x15O&\xf2\x9e\xd2\xad\x95\xbb\xc9\x18b\x85\x92\x0fK\x89\xc3\x0d\x88\xfa\xef\xa3b\xb69\x83us7n\x8c\xa1\xd0\xd9Y(J\xa4'%L@\xe7\xbe\x1dVP\x07\nM\xa1|\xb8l\xb9\xf0\xef\x0c\xe7 ov\xbb\x1aV\x8f\x109\x1d\xac\x9c\x057 ds\x0f7 \xab~ET\xe8\xc4\x80\x05\xec\xcd\x18\xb0\xeb\xc6\xf0kh\xd0\xa6\x0eN\xb4\xc7\xc3\x81\x02o\x91\xe6G~\xb0\xb2\xdb\x1e\xd9 yK\xf7_\xf7\xe4\xa42jfw\xaa\xf0/\xed\xedu\xfc%F\\\xfb\xfb\xaf\xa6o\xe9%\x12\xb6\xde\xfc\xfb^\xdd\xc0\xdf!'\x19\xf1\xd1vB\x99\xbaoVe\x99\x15\xb3\x9b7\x97Q\xb9\xda\xcc\xbd ]\xdf\xfc5M\x8a`\x15G\xc9;\x92\x977[\xf0\xdf6\xbe\xd4\xfc\xe8\xa34\xbb\xc8\xa3\xe5\xaa\x047\x18\xc1\xc1\xfe\xf4\xf6\xe4`\x7fzg\x0c?\xa6 \x1cW\x1f\xf3\x9a\xef<\x8b\x02\x92\x14$\x84M\x12\x92\x1c\xca\x15\x81\xe7O_\x8b\xdbM\xd0\x9b\xd5od\x06X\xd4c3\xb3\x842\x7frw\xdeq\xe3\x08Ab\xaf\x12$\xc8\x08\xcaU\x9e\x9e\xa1\x9d\xe1\xf5EF\x8e\xf2<\xcd]\x87\x9cgL\xdd\xe6\x03\x7fI\x92\"y\x8a(]\x8e*^\xa3\x0fr\xd0\x05\x81\x1b]0\xe1\xa9@\xc4\xc1\xf4w(\xfb\x1f\xca\x19\xf7A\xa9~\xc3\xce\x98\x8fX\x16\xf4\xfe\xc4@S\x9d\x97Vg\xde!\xc5\x1b\xde\x97\xca\x1e\xb1O\xb1\xa9\xfd*z\xc7|\x8d\xa5\x00\xaa\x97\xd1\x0d\xe3[\x98~=\xa2''\x0b]qS\xb8q\x88F\xf8\x12\xbe\xfd\xf6\x10\xa6c:\xc4\xc3\xee\x18E\x8b\xf4P\xe2o\xb4\x1a\x1f\x86\xed5cxw:2\xe1\x82\xc2\xbb)w\xc9\xc8+\xd3g\xe9\x99\xa8D;\xac\x0f\x1f\xdd\x99\xed3,\xfe\xba\xa82\x1b\xd0_\xf7F\x7f\x8e\x82\xaf\xdb/\x05f\xd4\x05f\x84\x17\xfd\x80h8\x81\xe0\xb9\xaa\x8a\xf6\xa8\xe2\xa8\x8e\xceKM1\xef\xb4[\xb2;U\x97\xecN?\xbeZ\x88 t\x9d\xb1\x98-\x8b\xe6z\xddReh>t\xb7Jy\xa7\xd3Sr^\x92\xa4\xe8\x1d\xf6\xef\x99\xe7\xd4\x0c\x9c1\xf0\xa3)1\xd7\xda\x8e\xae\x1bB=e\x9ecG\xeb\xac\xbc0\x94\x89\xef\xc5\xd4\x8a*\xf1\x98S\xb5~'\x12\xfa\xc9\x88\xeb'\xafU\xc5x\xd5\xc8m\xf0\x10\xb1B\x85\x88Q\xc1\xbf(9\xea\x98\xf9S}\x02\xfb\xfc\x0b\x8f\xa3\x02)\x9d\x14\xa1\xf9\xb9\x8f4\x0f{\x8d\xda-\xf4\xf6\xbb\x0c\xaew\xf4\xa9-\xd4\xa7\xad\x9c\"\x0e\x9d\x96\xe9r\xa9\x11>B\xdesY\xfa\xe7\x9e\xeb\x86\xba\xbfQ\x92mJi#\xcc\x04\xee\x04+\x12\xbc\x9b\xa7\xe7\x12MY\xa3\x0b\xfd\x87\xf8\x1e\x1e!\xa8t\x90(tj^\xc9\xac\x9c\x8c\\Q\xc1\xda\xe3\x1f6\x1e\xb7\xa318\xc7$ \x01'\x95mL\xa7\xe7#\xf4Y\x95\xe8\xff\xa49\xa1\xe5&\x93Pj2Q\x94\x93T\xa4\x88\xbeu\xd0\xcb\x0b\xf0%\x17\xb4\xdc\xb0ag\xd4\xb0\xcd\x05-v\xe0.f\x82\xa1\xeeG_}\xd5\xfa[-F$&\x1bD\xc3\x02\x90TC\x18\xb9\x89'$\xc618\xcc9\x03\xad\xcb\x88\x13\xcc\xbaLD^\xc2\x84\xd5PB\x91\xbfOG\x9a\x96\x14\xebCK\\\xdbai\xb2\xad\x94\xc8y\xad\xc2W\x03\xa5\xd6\x9af\x1fS\x1aX\xc9\xb4\x9b\x1a\x94\x8a\xc4\xda\x05IxT6\xce\x15.\x04N\x1e\xe5\xe4\xdct\x0c\xfe\x186*S\x10\xe6\xf3\xe6\xd5*X\xcdA\x8b\x8c\x05\xc2\x00c\x9ci\xc6KX\xea\xf6\x13\x10u M\xd3\xc8\xca\xb5WHg\\\x18\xb5r\"\x19C\xae\x98\xdbF\xf4\"\x96\xf0`k!\x0e\xb3\xaf\xbe\x02\x07\xb5Y\xb8\xdf\xd2z\xa1t\xfa$\xc1\x9a\xe9\xa2\x96\x01\xcf\xc3\xa88>\xf3\x97K\x92\x1f\xa0N\xd6\x87\xaa\x8d\xf3I\x9d\xf9\xf6\x8f?\xd8]L\xcf\xcbi\x11\x8f\xed\xad\xefW w\xabT\x8aj\x88\xc67f\xd8\x0b\x9e=\xea\xab\xaf\xc0m\xf4A\xd1\x83\xddZ\xaa+`\xef \x07\xb0\x1e}tY8h\xb2Y\xcfI\xfe\x9a\xeb\xc7F\xae\xaf\x88\x93\xeb{q\xc90\xdd\x1d}\x9c|\xedU\x12\x86_\xa28~E\x02\x12m\x91;\x91\xd5\xdc\xb7\xce\xc5Ps\xea\x9fxw\x99R\x88G\x97\xda\x83Hd\xa2\x02 \x1b\xee\x84\x1cf*3\x9a\xcd\xeeJ\xab\xed\xe4F\xad|\xd4#q\xa8\x07,%\xf5h\xc4Q=\xd9\xac\x91w\xf5\x81\xe5b\x88:\xf7u\xad \x17\xcd\xc6{53lJoP\x18\x86\xd2\xd84\x1b\x8c\x03\xa1\xff\x9d\x893#'\xbfm\xa2\x9c\x84\x8cT\xe1\xae\xf2\xd9\x19L\xf72\xba\x89x\x8b(/J\xb7\xb3\x01\xb1\x90e\xc1?+jZ\xdam\xc7bTe\xd1\xee\xee\xb4\xfe\x86lo<\x99\x18\xf4\x01\xbc\x05\xec\xce+\xc3q\x9fX\xee\x8f|@V\x8e\xb4\x865\x98\xcb#.?sm\xaf\x9e\xd7 Z{\xfe\xa6%\xaa\x0b\x95\xb7\x1e#\xad\xe9M`Mo\xc2\xea\xb3\xe6\n\x0f\x85\x91\xde`\x95\x07cj\x11\xafX\xa5gGB\xdde(\xef\xc0\xa0\x1f\xa5\xebu\x9a\xd8\xbcs\x81^\xd9\xce\x8fE\x9a\xb0\xcc\xe7O\xd2|m*)\x9b\xbb\xcc\x98\xfc=\x0b\xaaQ\xc2\x9e\n\xc7\n\xc6n\xa8\x01\xcf\xe0\xb0\xc9\xa2\x9c\x9a\x0b\x98\xceM\xf6\xac\xb6\xc1\xc9`\x15Y$Zk6\xd4\xf6#\x83\x95)\xa8\xec3\x85W\x15S\x10\xd8\xea\x06\x06\xbbP\xd0\xf4\x8f\xa2\x9fh\xa4\xf3\xc1{\xf4\x135\xcd$E\xd9\xc8\\hot\x92\x91I\xbbwk\xf3\x93\xa1\xf4X\xc3\xc2\xa3\xc9\x05\x04\x83\x8b\xb65\x8dL\x81\x12R\x97\xe1\xe4\x88\xe1\xafm\x0d\x8ds\x06nSC\xe3\xb8\xb13\xb8\"\xddT&\xa4 \xde\x94!MEC\n-\x93\x12P\x89^\xfd\x81\xef\xea]\xb9H\xf3\xb5\xaf\xed\xe5\x0b8\x04\xf4\x81^!7Rv\x18\x11\xed\x86x \x87\xf0\x82\xbdP\x1a\x10\xf45%\x00\xb47\x8f\xfd\xd2wL5\xf8\x9eS\xe8'\x15t\x94\xd4\xa1\xe5\xea\x97\x9e\xd6\xc3\xae\x19\x0e5\xf8\xaf\xa2\xf3(\x0cD%Y\x17T\x16\xc0\x81t\xab\xc95\xaf\x9f\xe0\x10\xde\xc1Cx\xd7\xe5\xa1\x1cM$\xe7+8\xc4\xc0GW\xd4\xa2\xe8\x12\xf0\x91[Vy{\x95_y\x0c\x87\xb0n~e\xe0\xfb\xcf,\x12Y\xbd\xb1\x80\xf9\xcd\x02\xe6 \x1c\xc2\xdeT\xab)h0z\xcc\xe9\xfeY\x8dOl=:\xec\xe03:v\xda\xc1gM\xbew\x8c\xfd\xe1\xb7\x84(\x87\x86\xe37\xf5\xf7\x04h\xe3koh\x9bo\xea\xf0e\xda\x03\xec\xf5~\x1b\x8e\xf5\xed\xb7\xfa[U\x1b\xe3f\xccB\xd9\x15G\xb1\x02FWL\xd6z\xa4\xe8\xf3\xf6\xb3\xdc\xfbH\x17&\xa8\xb0\x99\xd9\xba$4\xdf\x8c\x12\xa7\xe5\xde }\xe9\ns\xf8\x0fq&\xba\nC\xffSx\xd82#\xd2\x06\xa1\xa2\x070\xeb=T\xf6\xa6=\xb9\xf8au\xc6\x00VF]\xddC\xabT\x0dA\x1ac\xbe\x10\xdaS\xf5\xd9\xa7\xea\xaf\xf3?\xff\xef\xefN\xc3\x8f\xee*f\xb39Y\x9a:\xe9cx9\x86_Q\x0fu\xe2\xc0\x0d\xf8\x15n\x80\xf3\xd6\x19\xc3w\x18\xc2\xb7\xf3\xac\xb5z\x92\xa7\xd9\x84\x9fg\xca)p\xffJ\x1b\x1d\x833\xd2o\xb5\x1d\xa7 $YN\x02\xbfT\xad\xcf\xfbq}\x96\xd6\xdb\xbf\xf1\x16\xc6\x846\xfe\xfep\xab\x15i\x9c\xe4\\g\xdcb\xdbq\xba\xc6\xb0\xa4}~%\x94\xe3\xaf\xae4G\xfa\xb1\x89\x9dgnW\x14o&\x14\x83\x0c\xeeR\xe7\xff\xb0H\xa9~\xfe\xb3\x1f\xeb\xcb\xb0\xc8g\xa8N\xa0\xbf\xa63\xf2X\xcc\xc8\xe3\xff\xf8\x19\xb9\xc2\x1a+;8wV\xdb\xa9\xe1\xe2\xa9!\xca\xe7Zz\xcc\xeb\x9f\xc8\xbei\xc2\x8a\xbd3\xd4\x0b\xc3\x1f\x7f\xc0\xde\x13\xb3$\xab\xed\x87\xca\xf9\x85\xb2+\xea\xb5\x14\xbdw\xbe\x89\xbe\xfdn\xebG1\xa6\xe2@V\xb4\xf8\xe6f\xf4-=\xe6\xe0\x06\xbc\xb1\x88\x8eo^\xc2|\xaa\xc1\x8f\xda7\x8f\x07\xf5\x8eU\xc9\xcd\xde\x8fZ3\xd5\xe0\x94~\xfb0s&\xd82\xbbi\xe3*A6i\x8d9\xfbM9\x98\xd7t,{\xcf\xb5'Z+\xcb\x13\xc6\xdc\xce\x0cY\xed*)\x07\xcb\xebP\x94\x8a\xcc\xd3\xa3\xad$o\xd0uX\xebM\xb8N\xf3'5\x84`\xabf\xf0T\x0d\xd4\xd8Z\xf2\xedVK\x9d\x8c\xd5\xa2\x14\x0f&\xd0p\xb9m\x83\xcfXx\xbd%\xef\xbb\xabV\x84\xd0\xc5+fB\xccc\x7f\xea\x1a\x12\xf5\\^(\x11\x087\xc3\x0b\x0d\xc5:\xd2-\xab\xf5\xba\xd5\x0e\x96\xdd\xba\x88\x06\xa4\xe0\x0e\xd9\x9a\xacVvZ\x1f{\x8d\x8f\x98\xb3\x8e\xd6A\xb3*\xa2\xf6\x8d<\x89\xa5\x84H\xefX\x01G\x816M\x1d\x8en\x9a\x84K\xda\xac\xa9\xc9\xa9\xec\xe0\xc7\xa4,\xa3d\xf9$\xcd\xdd\xa0'g4\x183\xcdD\xd4>k3\xf8\x89\xb96PY\xf5'\xe4U\xd4\xaf %\xa7~\xf6\xae\xca\x89\xf9\xfa\x97R T\xaeT\x81\xca\x95*P\xb9R\x05*W\xaa`\x98+U\xe0\x16\x8d\x8e\x06jO\xe2\xe0\xe3\xfb?-l\xfd\x9f\xbe\x04\x98\x0b@\xfb\x00\xf38\n\xde}j\x87\x17k?$R[?4goevS\xc30\xcb\xe0\x1aU\xferma\xe2m\xfd8\xe2\x85\x1e\xfcu\xe1\x9e\xa4c\xf0\x91\x02UO\xbe'\x8b4'\xfcp\x12\x00\xa8\xb7\xe3\xb3\xe4\xa5 \x7f\xca|::7\xdd\xd1\x18\x12\x8f\xf0?4\xc7\x82\x18\xb4\xf6\x04\xce\xf0\xf4\xd5\x9c\xa3kn\xe1\xe8\xfb\xec\x02\x12*\x837\xda\xcb<\x0d7\xc1\xb0\xb8\xfe\xca\xdb\x8f\x8d\\\x92r\x80\x7f\x94\x19\xc9O\x04 \xae^\xf5\x1a\xeb\xf8\xdb?i,\xbf)\xf6y\xce\xa2\xabme\x93y\x99\x00G)\x10\xe1G\xfc\xd8f\xa9\xa6\xae\xdb\xb1\x8d\x19X\xee\xab\xb2\xc6H+\xa0I\xd3\xc9\xf8\xaat2\x1bU:\x99B\x95N&\xe6\x0f\xe4\x15\xd0Z\xb9c\xaeY\xc6\x98\xfeG\x84\x1e\xfa/\x0f\x1e<\x90 \xe9\"M\xcac\xa6\xcfv\xa2\xd2\x8f\xa3\xa0\x1b\xa2\xd3\xfa34\xd2'\x03\xe3\x00m\x1a!)\x83\xd6\xab\xbb\xa4\xf6\x93\xee\x94\x1fc\xc72\x03\xaf\x18\x02#\xff\xdb\xe9\xd1\x8e\xa5\x9b\xc0L\xb9`\x00\xf5\x82\x81\xfeEP\xb1\x08\xc62@\xc0\x19\x04:\xac\xb6\x17\xd1\xc8u\xc4\xd6V\xf9\x05C#\x94\x06\x9ae\xe1wVyC\x87\xd0\xf2\xfe\xeb\xe39\x01\xf46&C>\x06\x90\xb7yz\xaaI\xca\x00\x9c>\xff\xc0\xcb\xa9\xea\xe3\xe4\x8dI\x06@\xde\x85\xdd\x86;$\xd3\xc0\xd0.M\xf2\xf4l\xd7^\xed\xd2\\\x90\xc6\xfa\x05\xb8l\x92\x02\xd8\xb1\xddV6\x82\x8f\xdf<\xf3\x1a\x1a\x90\x05\xa1\xf4HR\xe6\x17\xb2\x12\xb9&\xdd\xb1\xf0\x01\xee\xc8?d\x0c\x07\x06\xbf%\x10\xee\xbb'\xfb\x9ax\x10q\xa1\x0b\xef\xc9\xd4\xa2\xda\xcf\x9e$\x1f\x83\x1b\x8d\xaa<\x81\xeaL\xd5\xe2\x12N\xbc\x91\xd7\xf1\x19\x7f;\x12N\xb4\x1dOr\xee=\x02\xb3\xc6S\xa3G\x89\xb86\xb2\xa6Z\x0e\xec\xfa\xee\x9a\xd8W\x8b\xbd\x0c\xe2HJ\xb5`\x97\xf0\x0f\x10\xd7P|\x06\xd6lz \x13\x94\xb8vl:\x92(\xa3?]o|^Fb\xa39H\x13\x9b\xf6)\x97\x80\xb6CGx\xcb\x991\x95\xbe\x83\xa6D\x83\x97\xa0\x80\xe5\xdcb\xa6\x1f\x94F\xfdX\xc3t\x93CHS\xbd\x83\x94c\xeb\x88?x\xcbP\x82\xba)\n\x85x\xf7\xba\x89B\x9fT\x83\x19\xc8\x04\x1e* \xb9\x81\x10xP\xdc\xf93\xa8/\x1b\xfc\xbeDK\xd9g\xf9m#5m$\x90k\xaa/\x19\"m0I\x83\x84Q\x99\xe6F\x0d#SF\x92<\xb7P\\2md\xec_\xa4\x9b\xd2\x02\xbf\xb3p\xb9#\xcc \x884\xdcH\x18\xe55\xf8\xf3\xd5\x07\x84\xcaL\x04\x82gv\x8a\x8c\x04\xe6\xe1\x84W9\x9c+\xeb<\xf3\x0b\x93#\xc8h\xa7tj\xb6\xfc\xfc\xa2\xcdL\xeb\x93\xa7C+\xcc\x19gA>\x05\x0c?u\xc7;\x9e\x95\xa5\xe1h\x14\xec}\xd9<\xa2\x94V\xea\x9d\xf6jo\x9f\xaa\x8f\x9f\xf7c,Mgh\x86\xe9\x90\xf4\xa7\x87\xd031\x7f\x1fVg\xaf\xe9+\xcd\x99\x0fx\x08+\xb7\x03\xc5\x1c\xc3\x1a\xae_\x02\x16Co\xc4\xcd\xcc/W\xf8\xbe\xb2\x1f\xc5\xda\x8f\xe3F-F\xbf\x84\xee\xeb\x0d\x7fW\xf5gt\xce\xebFw\xff\xb3UT\x92\xe3\xcc\x0f\x98k;\x99\xe0\n\xabw\x95U\x15Gi\xaa\x01>\xb05)\n\x7fI\xb4\x07\x8b\x16]\x8cC\xc2\x8a\xa0\x93\x90\x04)3\x91;3p\xb0\x12\x8aah\xc1&/\xd0\xdc\x94\xa5QR*\xb9\x1f\xd9\xd8\xb0\xb6\xb5\x8e\xe6i\xaa(W\x07\x7f\xe2\xcd\xa3$t\x19:\xe4R\xbb\xb6\xf3\xe3f\x9dA\x99\x02\x1d\n\xc5\x96\xbc\xd6U\x88\x1fm\xb24\xd4\x04\xb6\x13m\x91C\xe5\xbc\x8c\x8f\x92ZtwJ\x8e%h\x9fEE\xe9E\x05\xfd\x8f\xdb\xd9\x0c\xf6\x9bI\xb2\x97\xb8\x9f\xb0\xc7v\xd5%>\xc4\xd2\x804\xc8!\xfa\xe3&\xe8\xe5\x91c\xcc\xa4\xdd\xa7\xd3\xa4Z\xc6\xd6\xe7v\xde\x19\x9f\x90\x90Z\x13I\x0c\x0fB\xc4\xfd\xc8$\xcd~3\xff\x99 \xd5\x95\xd2\xa86\xd6Z\xd1\xab\xf6+\x06\xda%\xd3\xd6\xad\x94\xda:\x17\xd3k9\xce\x88W\xa4t\xc0\xb1\xb1\x1d \x11\xfcd\xff\xadW\xa6o\xe8va\xf5\x8a\xe0\x06\x10\xaf\x88\xa3\x80\xb8\xd3N\xc7\x04-\x81^\x1d10\xa7\xccm\xf2\xa4-\xa51\xfb\xc2\x17\xbd.\xbf,\xf5\xbaA\x95\xbb\xefO\xa3\xe1\xfd\xe2\xa0jQ\x01\xe9\x12>\x87\xe2\x13u\x12O\xdc\n\xd7\xd0\x93\xb0\xca\x92\xf58\n\x9f\xa7\x9bD\x16Td\xab$\xaf\x95\xe3\xcdl\x1fE\x95\xce\xa837\n\xf0*?R\x7f\xb2\xda\xf3!;J>`\xea/\xd2\x1bT\xfbN\x9d\xe6\xa9s\xbf*\x9d\xcf+)0\x9dH\x13G\xa4\xc3\xbf\xc4\xf8?\x81\xb9\xa39\x04\x93\xb5\xa3\xe2\"M\xa6\x0e\xec\xaeV%\xddv\xb3\xda\x89\x89\x82^\xc8&\x8edR^dD\xb0\xb7\xc8f\xba ?\xfe\xa5\x9f\xd1\xe9\x11\x0b4\xd6\xec\xd4\x03s\xcd\xf4\x9c\xf5J\xab\xf7\xd5\xc4\x85\xa9\x06SZp6\xe22\xe9fR\xe6C`\xa5\x953\xe8\xdb\xf8\xa05\x81\x9bR\x8fm\x80\xaeE}\xc7\xda\xe9z\xa5\xdbB\xcf\x98I\x12@\x8fzU\xa9\xf9\x08\x93^~\x93\xe6\x16cI\xb5co\x91\xa7\xeb\x1f\x8fG\xee\x89C\x0f\xb5(@.\xff\xe6\xafE\x9a8o\x1b\x9c\xe3\xf8\xday:\xd3\x1e\xbd\x10!\x06\xcf\xa2\xe4\x9d&5\xfcug\x10\x13\xf7\xb6* \xfdg\xc9\x18^\x05?\x98H\xf9\xc1\xa8\xe2\x07\x93\x11\xe3|\xf6\xbf\x86\x0d|\x03\xc9\xd7\xb0\xa1\xfc`t\xb2i\xf3\x83\x1b ?(\xf8\xcd\x0f\xc5\x08F#M\x12i\xcc\xb2\xf8\xda_\xa2\x05\x17u1\xa7\x8d\x1bLx\xa5\xccn\xa1X,\xb8B\xe6\xad\xd9\xb2\xc5i\xaf3:5\x98\xb1\x96\xc7\x003\xfd)\xf2F\xb7\x87\xa8\xe6G\xe87^d\xd7\xb9\x87\x9f\x80c\x1a\x14\xadf\xed\xf4\x91\x0fq\xfaH\x07\xa4\xcad eK\x7f\xb9$aE\xb8\x0b]\xc6G\xcc\\lv 11\x0f\xf6\x8aB;\xee*\xdd\x92|\x1b\x913S\x8d\xc1\x17\x1c\xceA\xa1p\xb0\xf56\xad\xad\xb7U(\x9d6\xaa\x1e\xf8$\x9f4z\xe8/\x0bg\x0c\xa5\xc1Y\x98y\xcf\x08\xa7\x92\x08\x1dI\x8c\xb6\xe2\x9dye\xa86M\xd5OT\xc2*_\xb8\x84\x9f\x05\xec\xe4\xb6\x00\xf5(sF\x1d\xe8\x9cl\xd4\xee\n\x00=;F\xf7jbPL\xd9\x95\xe6\"\xe9}\xd3\x85\xef\xaa3A\xa7\x87\x1b\x0e\xf3\xa2S\xcd\x89o\x9a\x90\xda\xef\xc1\xe0\x93j\xf4}\x00\xd6\xc3t\x00\xab\x0f-\x0bN\x992\x86PG\x06\xc4U\xa7\xeb7\xc32b\xb36d\xb0\x15\x17\xf33\x8b, \xe9N1$G\x05\xce\xde%\x0d/\xad\xc6\x06\x1e\xc3\xc6\xd29}g_\x0b\x10\x1b\xcc\xa2\xa7\xc6\xf8[q\x898\\C\nSzE\xe1\x0c\xd2*\x19\x93\xc5\x0bt\x8b%Z/\x9c&\xe4\x8b\xec\xa9\x19u\x9b\xc0/s\xb2\x88\xce\xb1\xb0]\xbd\x0c\xc6\xb7W9Y\xcc\xc0\xf9K\xf5\x12\x8e\xc6\xa2\xd9\x8a\xde0\xda\xa1'\x1a\xb6\xfe\xdbR\xb0&\x08&\xca\x8f\xfeM\xe0\x1bVUDM1o5\x0c\xfa?\xa5u\x9cv\x01L*\x0b!J01\xc9\x1eHm&\xad;\x03\xe5[\x83SI_\xa4\xb3\x12D\xa4\x04\xc7Z\xe4\x10\xd2\xc6\xae^\xc9\xcd\xfa1\x1a\xbe?i$.H\xbcS\xfe\x077VQ!\xb0=\xaf\xff%\xf9\xc4\xe5\xf9}\xde\xea\xc7\xe5S\xf964\xb1\xa8\xed\xed*'\x91\xcc\xc3\x98\x8fb\xe4\x9e$\xc8\xdc\xc0\x1e{[V\xe4\xbf=\xab\xd7\x8a\x81\xd7\x1d8I#\xd7\x83\x89Y\xc7\xa1\x9b\x98tJ\xcev\xe2\x9fc\x8fnE\xdd\x99\xc3(\xa5\xe6\x0c1\x9a\x99\x81\x87J\xffB\xa2\xe5\xaa\x9cAN\xb9\x9dy\x1a\xb3,\xa4I\x9a\xaf}m\xfc\x9ez\xec\xb2\xe4\x00j\xf0\x96wl\x9c\x06\xef\xaad\x04\x94e\x1b\xee\x05l%z\x08\x9f\x0b;\xe9\x83\xce\xca$\xf6\xe7$\xc6\xf3HQ#|\x0cI\xdbT\xbc\xb3/\x03(\xdbW'\x1f\xb4\xb0=\xd8\x1c\x1b\xff\x05\xd7B\xcb\xf84Y\xa4o\xf2\x18\x8f'\xfa\xfb{\xbf /\xfdr\xa5Q8JS+\xa4\xaa\xd4\n\x91*\xb5\x82\xafJ\xad\xb0Q\xa5V(T\xa9\x15\xe2Vj\x05\xb4C\xb7\x01\xea\xdc\x0b\xdcR=\xdd\xbf\x16\xa9\x17zsn\xc5\x11h\xdc(\xbeD%5\xe1\x86\x9eY\xab\xb4\xd0\xe8x\xd8\xa95\xe7\x8b\xb5\xd3q3(\x16\x84\xb64\xd9\xe4jR\xe4\x9c\x00E\x1dx\xf3\xea\x19\x96\xc1-\xd1g\xc1\x81\xb7\xbb$\x80\xd11\xb6vn\xd1\x06\x0c\x85O\x8c\xa5\xd0\x9b\x05\xb8\x12l\x053\xc6\xc2\x00\xac\x85\x81\x98\x0b\x15\xf6\x86~i\x90\x89\x93\x01\x1aM\x00h:\x9e\xf3\x94\x9c\x7f\xfc\x01N\xb9\"\x10\x92-\x89\xe9\xc9c\x905\xd3\xfa\x0b\x14\x93-\x14|\x1c\x9a\xac\xfd\xc8\x08\xefc\xf2<\x87\xb2p\x16\xf1\x1fV\x8cL\xaa\x15/mX\x1e\xa3\x86\x8aq\x94.\x96\xf5*\xfc$*\xa3\x7f\x937y\x99%r\x90\xfb\xbb\x9d8\xc5\x14\x9e\x945\xd4\xb1\xf3L\xb5\xb9\xc9c\x1d\x10\xb3\xd3\x08\xee\xc4\xe4\xe5^\xa2\x0c\xa9\x83bR[S\xca\xd3A\xc7\xcc\xea\x83L\xee\x15x\xcdc\xee\x98\xbc\xcaV\xa8\xa6\xe1\xb1\x8e\x86\xd3\xdeh\xf99\xe4\x984\x829c\x085\x06\xbc\x9a\x19\xd4\x9cZ\xcd9\xd4\xba\x91\xb6\xcfA\x85\xa3\x8d\xfa\xa4\xb8\x949\xb9y8\xb0\xda\xfe\xd7\xedp(T\x87C\xa1:\x1c\n\xd5\xe1P\xa8\x0e\x87\x82\x1d\x0e2\x92_||\x92\xaf\xd7\xa0\x7f!\xf9\xe2\xb2%\xf9\xc2/v\x97 Z\xc6\x1cXo\xa1\xf8Zn\xa1\xeb\xc1_\xf5\xf7\xd6\x17v\xea\xcf\xb2\xb7v\xd6/4u\x0b\x8b4Ugp\xfa\x8f;\xf7\xae\xc7\xa6\x157\xffDB\xd1\x97\x94B\xda\x94BO0\x9f9K\xff`4\xe5\x03\x9fO\x1ed\xd7\xc8 $\x17\x06\"i\\\xf4&\x0b\xfd\x92\xb0\x86e\xc6\xdbO\x9e{\xe8\xd2d\xf2\x03K\x9d\x83\x82\xae\xa5\x96\xfdG\xa9\xd6\x90B\xe9\x8e\x13\xa7~\x18%K\x96\xd5\xb8\xf4\xf8\x9f\xc7\xa5_n\xb4B\"\xc5[g\xe1G1 \x07\xbf\x8bn\x85^\xb0\xc9s\x92\x94\x1cC\x0c\xd2\xeb\xef\xef\xb5\x82(\xba\xde\xb9\x1b\x0f\x0b\xea\xd1\x9e\xe5$tF\xdc\xdb\xb0y\xff/\xbe\xefk\xb3\xa07%W\xfa/\x8e\x0dmw{S\xfe\xbb\xaa\x1a\x7f5\x07$\x8e\x1f\xebU\xfaQ\xb2CN\xfa|XK rf\xaa'|\x9d\xce\xa3\x98\xcc`z0\xb4/N\x94d\x1b\xfbTCut$\x9f\x05\xfe\xba\xf2\xe5,\xf6\x03\xb2J\xe3\x90\xe43p\x18\xea\xc0\xfc\x02J\x7f\xa9y\xab\xbc\xc8\xd0\xbeE\xceu\xdf\xee%*j\x12M\xf5k\xd5\xc1_c\x8aS\xe6\x1b\xe2T\xd8\xe28\xa0U<\x84U\x81qs\x14\x94\xdcn\xf6\x81\x13x_O^*S\xf1R\x99\x8a\x97\xcaT\xbcT\xa6\xe2\xa5\xb2a%\xc53\xca\x15\xb4\xeeb`L\xa6\x89\x9cY\xe0\xc7\xa6\xfbR.,\xfb\xf8\\X\x08\x87\xf0\x84\xb7\xef!\xebAwO\xbb\xcf\xfa@\x1a\xe8\x84\xd7v\xf0\xa4yYse\xc0{\xa7\xe6\x96\xec8%\x11iK\xfb\xa4Wmn\x19|\xc4B\xa3K\xbf$\xd2\n\xae\xe2\x8a\x8a\xa30*\xbfO\xcfg\xb075\x12\x0bGI\xe4#\xc3.\x86+a\x80`P\x02F\x18\xc0\x13\x81H\x95\xc3\xd8?\xacq]4\xa7\xbef\x96\xac\xcdc\xaa\xd3dx\xb6E\x90\x8cD\x9boB;\x14U\xa2\xb7\xa1#\xf8d\xfel\x8c\xcf\x14\xe7\xde\xa34)6k]\xfeD\xa8\x9c\xd62?\xf7\xd7z@\xe6\xb5\x16\x15\xbcf\xb6\x1e8\x1a\xc2\x1eC\xe5\xb7\x96\xf9\xe5\xea\xb9E\x9a\x8e\xcd\x003\x0ep\n\xbfq\x9d\xefYE\x1c\x0dk\n\x9c\x82o\\\xe759/\xbf\xcb\x89o\x02\xcf\x18\xf8*Z\xae\xe2h\xb9*\x1f\xa5\xa1\xd1\x81,d\xef4R\xf0\x99\xde@\xef\xed\x08\x8bg\xe2Z\x91\x92\xe4\xbfD8[\xfe\xf7\x17OC\x92\x94Qy\xe1\xfa\xdc\xe7<\x1fyu\xd9\x94\xc2\x19s\xd3\xf7\xb3\xa8(Gn\xf7\xc8\xea^[,\xa7\xd9\xe8\x1c\xdb*\xae\xcf?\x9a\x93\xdf6\xa4(\x1f\xd9\xf7~\xddBb\xfai\xc4\xccN*Wq[\xf8,\xc8\xde\x98\xd5\x8c\x0c%\n\xd5\x03}\xfbK\xd1>\x12~=\xec\x05\x1c\xc2\x92\x89\xc7z\xc09\x02V\x07\x85\xd1[\xed\xca\xaa6\xcf\xd3\xf0b\x82X`\xf0zpB\xbf\xf4\x19\xe4\x04c6f\x907#8\xec\xdf\x8e\x92\xfa\xdd(\xd1\xd5\xfc\x1a\xc3\x9c.k\xaa\xa9\xae\xb9\xd8m\xb0\xa7\xa7\xc8\xf0\xc3\x0dpW\x0d\xeb\xa3\x03Q\xb2\xf5\xe3\x88e\x070\x0d\x8a\x93\xdf\x0b\x03\xadk\x8b\x0e+? c\xf2\x82\xdfT\x8f\x9d\xee\xbc\x0b:z\xd5\xc8\x8d\xce@\xaa\x91\x13\xab\n\xa3bp\x9a\x1ej\xca\xae\xee\x8e\x86\x13\x96\x91U_P[\x87\x11\x97i\x9b\x84Q\xa9mX\xd5h1\xa0\xc19\xa6\xa0(\x13\x08\xfc$ 1H\xd6\x86u\x04D%\xb50*\xd5PF\xeck\xa4\xa9(\xd3\xe52&O\x05\x99\xd1\xef\xbc\x87\xe0<\xc2\x1ebG\xe8+u\xd5\x02\xcd\xd2\xb3\x0c\x0e\xa6\xf9X\x95\xeb\xf8 \xd6q\xd8i\xbe\xdb\xf1N\xceKq\x8c\x89L\xb4\xc0\xca\x92\xa9?`\xf4U\xe3\xf8\xbf\xd5Oo;\xf1\xad\x89\xeb\xa9(\x81\xc1\xf9Z\x81\x9d\xad\xe4\xcb\x9a}\xa9L\xea\xd4\xbb\xab\xf0.k\xc7\x9c\xd4\x87\xd1\xaay\\\xf6D\x1eq|\n\xdf8m\x02\xe0\xf6\x04\xe0\xf8\xba\xef\xfd\xfe\xbe+\xbfW\xf3\x17\xca\x1f<\xaaz\x10V\xcf\xdf\xb7\x95\x03\xdb\xa6x\xda\xe5\x97\x9b\x98y\x05\x89\xd9\xfdY\xcdLDU\xde\x10T/\xa5B\xbd\xa4\xd0\x1cQ6\xf9\xe6\xf9:\xbe\x19y%)J*\xceJ\xe1(\x83\x8c\xcbf\x02D\xab\x08<\x84\x84\xc7\x80\xd0\x9e\x9e\x9e\xafYu\xb0\xe6M\x99\xe7P\xb4\x00\x97w~\xef\xf0\x10\n\x9db=\x86C\xd8C\x8e\x0f\x93\x17\xfe\xfe\x9e\x8e\xb2\x903M\xc4+HyLY5W'\x1c\xe1fW\xd4\xb0\x1e\x8d\x9b9\xf1\xf5\x9eH\xc5?\xd7\xb1V\xa1\xd7P\x06(\x12\x9cK\x94u@\xe2\x82\xe0\xdc\xb6\x92\xf3\x17x\x0c\xb8\x0e\xce\xb1\xaa[\xfa.i\xbb\x83L\x88\xacEMc\xda\xcf\xb5)\x0d\x17\xf8\xd97\xad7\x14\xd1I\xafXvK\xb7\xe3R\xae$J\xbcE\xe2E\xc9\x82\xe4\xc7X\xe2\x7f\xe4\xe6<\xdaF\x9dg\x8d\xbe\xb7\xa0h|\x8c=\x16/\xa6\xa8\xefT\xcc\x07+\xb0\xf0K\x1e\x95\xe4E\x12_H\xf3]*\xe6EL{kf\x14\n3\xa1\xf7Lj\x19B=~\n\xf4\xcf\xb5\xa44\x99q\xaf\xf0}\xa2\x90\x90\x0d\x8bOw\xd1i]bc\x0c\xa9|\xdc\xa7C\x06\xee\x92N\xed\x0e\xf8\xe3\x0f\x08G\x0c^\xfa\xf96\x03>\x14\xedl\xe8p\xde%\x98\x89\x82`\xa6\x1d\n\xac\x82\xa3\x84=\xa7Bl\xcb\xe0\xea\x95y\xb4vYA6\xbd!\xb6\xb1\x85\x95ek9\x99\xe8\xc7\xba(\xb0\xb3\xc3J\xea\x8eUh\xa8\xa6k\x0c3+\xd9\xf8;v\x8aURc\xbe\x14^\xc2\xfc\xa8\x0c\xc9\xef\xe5\x96\x8e\xeb\xe9J\x7f\xdd+\x10\xd0\x1f\x0f\xee\xdf\x1a\xfd9\x8a\x10\xfc\xf9\x1c\xc2\x189|\x92\x06\x9bK\x96 \xe2$\x88\x15\x94\xa1\x1cB\x98\x068\x0e\x8f\x9c\x93\xe0Q\xba^\xfbI\xe8:A\x9a]\x98Sd\xc9\xa8\xd4\x07\xf3\xcc\xf0\xb8\x12R\xcd\xb4\x95\x9ck\x88\xeb9%W\xe0\xfd\xae\x0e\xce\xac\x8bK:\x8fX\xee&\xd3\x17\xd5T\xb2]\xbf'\xa3\xd2dQ\xaa\xb3\xcb+\xdb)\xc9y\xe9\xe7D](\x11P\x14CTj)\xbb\xf0\x8ezrs\xe2\x87\x8c7b\xb6q5dk$tZ\xd4\xa0V\x89A[\xc52/\x91\x0bT\xb0E\xf2)\xfd\xa0\xe6\xf7\xebP0\xa7\x7f(m\xe8\xa14\x95\x9dJ\xf4\xc9\xf4\xbe\xecX\xa2O\x1eLUqlj\n$\xbc\xd1N$\xa5\x08(\xe3&\xab?U\xd9|\\gE\xfc\x90\xe4EW$\xa5\xe2h\xe9e\x9bb\xe52T\xc3\x84\x9d\xec\xef\xc9?\x9d\xb1x\x9d\xe5\xd1\xc5\x18N\xfe\xf8o\xce\xdf\xb0zf\x9d\xa1\x08n\xc0\xdf\x9c\xbf\x8dx|\xf4\x06M\x12*V\x93\x9e\xaa{\xfbrTC\xb1Wa@\x0e$9C\xc5U\xe6\x17\x8a\x8dP94.\xc6h{\xea\x9c\x1b\xdd)\xf2HR\xe6\x11)\xa8\x90\x04{.\x16\xba\xa1\xc7i\xe6%\xe4\xbctG#/L\x132\xfa\x9a\x8f\xc2d\x8e\xc4L`6\xd6\x91\x15\xefZ\xe3\xc8\x0d\xc7p`R\xcfS\x9e\xedd\xdfP\xa1b\x8dPS\x89#\xa6\xb8(\x12\xad\x1b\xab\xff\x038\xdd\xd5\xde\xc2\x0dpf\x98?m\xcdW[N\x0b\xfa\x84\x00\x02\xbf\x0cV\xa0>Yc\x86\x11\xb8\xc2}{\xc1{XD\x89\x1f\xc7\xaa\x15V\xaf=\xbd\x98\x12%\xf3\xf8\xa1\xd5\xf8\xed*\x06`h\x0e\xf8\xd6\x89GP\xae\xf2\xf4\x8c\xbb\x07u/\xc9<\xfc\x97\xfa/\xfaA\x8e\x8a\xf34\xbc\x90\xa5\xd6\xa1 \xcez\x13\x97Q\xe6\xe7\xe5\xcdE\x9a\xaf'\xa1_\xfa\xcc\xd1\nG\xe6\xbc|q\xfc\x9a\xfd\xdd\xdd\xbb\x1aNa\xa9\xd9\x8f\xc0-|:\xa7\x8e\xb9f_\x82q}\xaa\xfdy:\xc6\x8c\x1c\xf2\xfd\xc9&\x057\xe7\xc51\xf9\x8d\xefN\xdas\xf7\x14\x0e\xe1\xac\xbb;\x97\xc6\xdd |\xf4G\xfd\x8dw\xca7\xacq\xfb\x01\xcf\xf5qd\xdc\x82\xc0\xb7\xe1\x91v\x1b\x02\x9e\x08|\x0f>q0h>J\x8a\xd2O\x02\x92.j\xae\xdb{\x12\xa1\xb0\xd0\xda\xa0\xe7t\x83\x1e\xfe\xffq\x83z\x89\xbf&\xf4\xef\xaf\xcb\x8b\x8c\x1c\xb2{\xf4'\xdf\xb9(P\xf7\xde5\xeem\x90\xe25X\xedq\x10\x98\xb4?F\x8c\x91\xdb\x05m6\x9f\x1e\x9f\xe8\xb5\x87\xc1\xfcg\x8d=\x7f\xa6\xdf\xf3`\xd94\xf0}x!\xf6\xfe|\xe8\xabe\x0f\x1b\x94\xb7#E\xb5 \x84\x97\x13t\x07uo\xfe\xeb_\xc9\xcd\xe5\x18\x1c\xa7\xab\xd8\xe3\xe3/e\xe5\xac\xdb\x1c\x8d\xcf\xb9\x93[\x8aJz\x9b\x8f'\xc4^7F\xefK\xcc\xca\x97\x98\x95O\x11\xb32 Z%B\x95c\xb0\"k\xab\x9a\xd7\x0dp\xab\xcf\x0b\xf1#29\xd5 c\xa0.K\x1b\xb3\x072\xbeD\xc1/\xa0#\\U_\xb0\x1e\x19\xe2J~\x0dCiZ>\x98\x97\xad\xe3-Q\xde\x148\x01\n\xeb\x1f305\xd6\xff\x9aV\xf0n\xba\xa7\xb1\xd0\x17\x8e\x82H\x9b\xf8\x10\xebr\xdd*p\xcc\xa3\xdb\x1b\xb3x\xfd\xf2c\xff\x00\xca7\xbd\xd2\xad\xea\xbc~_\x91\xf64\xec\xa6\x993;\xae\xd4N+\xbcW\xc3\x95h\xc6\x94\xa3M\x1d\x17o\xc5T\x0e\xf2\x98wF[\x89\xc5\\\xe7[Q\x8c\xdb\xa8\xf6R\x16\x8a\xe1d\x16E\x92\x01u\xfcL\xebdY\xb2\x9b\xf7\xce\xa0Z`\x85\xbd\x95 \xb6%\xbbM[jw\x05\xdf\xf5\x8c\xaf\xf9\xc2\xf7} \xbe\xef\xcfg`\xfa\x14gF\xcd\"\x99\xce\x0d\xcb\xb0\x82|@\x90\x00s\xb1\xa8\xc2\x17\xf91\xac\xd1\x96D\xf8\x02'\xf6\xe6\xd8\xd8\x82\x04\x9b<*/\x1e\xd3}\x1d\x95\xa6Z\xc7t+\xe5\xc6x\xdf\x98A\xf9\x9br\x95\xe6\xd1\xbf\xc9\xf7%\xa5\xb0{\xdd@\xb6\xe6\x15\xb0W\xc4Qx\x05\xf60\x8c\xd4\xe5\xc5&\xff\xf8\x03\xfd\x9d\xae\xc4\xea\xc5\xbax\x890\xda\xcd\xb0\x96\x8a+\x89\xa3m\xce\x86z\"\x02m\xd7\x9a\\\x91>\x84\x94u\\\x9b\xdf\xaa\xb1\xad\xd4\xc6\xae\xcaAX\xb7z<~\xbaJq\xf5\x1f\x9b\xeb\xea\x93zo\xc8\xe3T\x03\xb7ht4P\x1f\xad\xd7\xd9wC\x15Xj\xad6\xd9~\xf8\x80\xd2\x88\xfbP\x89*\xf4\xa1\xc9\x87\n\x1a\xf94\xd2\xe45\xbe\xcchD\xfb\x9e+n\xac\xd3\x90\xc4\x942\x8da\x8f\x07\xaaz\xe4<\xf3\x93\x90\x84#\xa1\xea0\xb8\xc6\n\xf8Y\xff\x13\n\n\xd0\xdf\xc3\xf2\xe9\xdd\x98\xb4&\x18iW\xb5&\x87\x89\x11&\x10S\xc8\xe3\xc8\x94\x1a*S\xb8n=ZE\x9f\xba-\xcd F\x99[\xac\xfeK\xee$\xd8\x86\xeaOI7\x9a\xf7\xc3\xf0^6\x11\xbc\x1f\x8e\x0d[E!9&\xf1\xe2Er\x84\xd3j\xe2\xc5\xf4+\x0d\x15\x1bV\xa1\xb5B\xe7C\xf7D\xd2\x89\x07\xac\xf6F\xdes\x0c\x85!\x1a\x90\x0f\xad\xfd\x11s\x80N\xf0\xf5\x94T\xa3\x19\xb4cw\xd8\xaa\xb6\xf3\xf0 \xb8z\xd4\x82\x98p\x08\x991\x956P\x98|\xaa\xe8\xcd\xfe\xfc\xb2U\xe8b\xae.\xdcl\x88F'\xc1\x0c \xea\xf2\xb6\x0d\xb5\xde*\x8a\xc3\x9c$\x943\xfa(M\xebB\x0d\xcd\x0d\xc9\xc2\xcc\xaasM\xc3Q\xdaxi\x05\x9b\xbc@\xa5[\x96F\x892_\x1c\xf4\xb0\xb7\xba\xcb$\xe7?\xed\xe0v\x1fX\xab\x92\x04%\xaa\x1368\x8c\x8b\x95\xed\x12\x1eP\xe4\xd4\xc7\xa0\"|\x17S\xf6\xcb\xbf Ar\x985a\xbb\x87\xa7\x91J\xf5\x85\x02\x990\xb0h\x1d\xd1\x92\xe8\xb5\xee\xc1\xee\xfc\xeey\xde\xfb\x0e\x89k\xb0C\x1d\xaf\x0f$O\\\xf8i=\x10GO\x9b(v\xdc \xbb\x14\x87~\xbf\x1e\xd2\xf83\xf0\xf9\xbb\x96*\xc11\xfb\xa10\xdc_g\xe5\xe0\xe7!\xc1\xf8A\x19m\xc9k\x7f>\xc8VZ\x99aC\xbf\xf4\x0bR\xa2G\x8e\xfc\xc8\xb6\x92Q\xaa^\xa8\xd5\x12\xbd\xdb\x97\x13JP\x13\x98,\xa2\xa5\x02\x8a\x89%\x86\xc0\xce\x00\x13QW\xb9\x86\x9fS\n\xfc\n\xf9\xaa(Y*E\x18G\xc4\xef#\x8b\x18\xa0k\x1b\x12\xef\xc6\x0d\x97~\xba\x02\xb4HS\xd4\x98\xc1\x98R\xf9\xaa\x8d\x99\xc4\x83\xefc\x0b/W\xc9j7\xb2\xce\xb0-^\xffIg\xafq8\xb5\xe0ly\xef\xc6XG\xee\xc4\xd1\x90\xefG%Y#\x9fY\xd3\x9a\xc3\xc3ff\x9d\xc6\xd9\xf2\x10\x1c\xbe\xb3x^\x96\xc1}\xd3\x07\xadt\xba\x16G\xc9;U\x860\xa8\x92\xd9\xf0$8\x8e9\x9dJ[~\xa8\x86\xa5\x1aDD\xc7{\x14F%`\x8c)\xcb\xbe\xc1\x1a\xe1wX\x154\x8dqd\xd7\xa5\xe0\xe7\xc8\xf5Z\x08\xda\xb3\x88'\xe7i5n\xbbBlTW\xb6>l\xc7\xd6\xb9P\xcc\xb1Y<\x92\xcb\x8c\xe8_}\x05\xe9\x18\x8c\xcb\xa0\xa9\x84\xa65\x071b\xab\xad\x94\xd2.M\xa2\xa1\xf55 \xd5\xa6;h\x1d\x06\xda\xc4'\xa4\xa6\x993\xd0\x14\xb3\x14\x14Y\x97\xef\xb4\xf7\xc0(1~\xdef\xa4\x05\x15\xb1z\x12S\xca\x9f\xf4\xa4\xb2H\xbc\"\x13\xbe\x162\xa9l\xc3\x1f\xf4\xda(\xf8\x83\x9eT\x16K\x0dL(\xfe\xb8qS,W\x1b\x98\x16\x1f_<\xcbl\xc53\xbd\xcfn>\x06\xbf\x7f\x92wy\xdfk\xe3\xb3+\x92\x84ozb\xa2\xc2g7\xed\x8b\x8az\x9f\xdd\xbc6X\x1d\xb6\xb7\x8e\x8aG\xcde\x89\xe3\x01\xabE\xc92\xca\x17\xab\xf4\xcc=a\x94\xb3p\xc6@\xde\xd2o\xf7\xe9\xc0\x989Q\x8c\xbb\xe3\xa5+f\xe9\x0dSH\x85\x1a\xdfN\xa8\xb9\xe6\xbc\xbb\x0dc\x9c6\xf8V\xdd!\x1c\x19B\x9f\x9a\xda\xf8\xe6\x92V\xc7\x05J\xb2Q\xdb\xdb\xb7\x03\xe2E\xc5\xf1*=K\x9aK\xdf\x80\xa6\x1c\xc0[\xccB\xa0?\xa0\xed8\x12\xa6\"\x9d\xa7\xe7J\xdeX\xd5L:\xeejX~o\xa9\xfbu=h\x1e\xb4\xc6\xe3\x93\x84Z\x0f\x8e\x90\x9d\xae\x9ax\xb5ZYY2'P\xf6\xa7\xa9]~l\x97]C\x16\xde\xa7T\xa3\x9f\xf5\x06v<\xabc\xe3\x19\x9d\xe1]\xc3\x19\xed\xea\x1e\x82\xf2\x10\x07\xbe\xad\xd0^\xe2\xf06)g\n%\xc6\x9c\x89^\xcc\xa0c\x84\x16G5\xe7\x02\xfc\xa2\x88\x96h\x931\xeb,\xaa\xe3\x806<\xfd\x1aJ\xf8\xa6w*|\x0d%\xa5\xfcj4\xda\xf2<6\xf5\xa1Pj\x82\xed\xaa&s:\xb4d$\xba]%\xfd\xf6V~\xf1\xe2,\x11l\x0c\xd3\x16b\x04\x02\xeeZr\x92\xd3\x13(9\xc9\xdf\xdaF\xc2B\xe3x\xef\xe3D\x1f\x01S\x1bw\x89\xea\xc4&\xda\xc3\x06\x9aCN\xd8\x81\x9a\xc07PV\xb3\x9b\xe8g\x17\x1a+\\\x9e$\x860\xc6\xdc#\xc9fMr\x7f\x8e\xe7a\xebO,&1\xc6\x9a\x88t\xd3o\x04\xd0\xde\xfe\x18x\xf64\xba$X8\xd1\xcd\xbd\xb3<*+\x88\xd1X\xc1d\x12\xfa\xc1w\xe4B\x1a!\".\xdb\xa0<\xa8\x17\xaa\x9a\xff\x92\x87\x9fh\xa6\xa8\xe27(\xeb\xe66P\x89\xee=^ \x12\xd3B\xe5\xbd\x9c\x84\xe2\xea\xf7\xe5\xbd;\xeao\xb3\xc8\xa8\x8c\xae\xd0\"2\xd5\xb9\xb2\xe2U\x80G>\xee\xb9\xa4\x19\x92Z\x8eD$dB\xce\xe0\xf5EF\x8e\xf2<\xcd]\xe7\x91\x9f$i t\xcf\x80\xcf\x8e\x18\xf0\x0b\xf0\xab\xd6T\x825g\xcbT \xf8\xa014c\x87At\x9a4{\xf9\x8a,HN\x92@t\x956\x08+\xbfH\xfeV\xc2\x9c\x90\x04\xd0\xe5\xd4\x8f\xa3\x82\x840\x81b\x93\x91\xdc\x1d\xb5 \xe8\xb0H\xa8+\xb9\x0f\xf5\xfc\xee\x95h\x97N\x11m\x1d\xd8;\xc4\xcc\x9dt\xf2\x90\xc0V\x13\xd2z\xc2\x98}9\x8e@c\x9e\xdc\xa8\xcd\xba\xf2\xcd\xb1$\xe5K\x81|/\x16nd\xe9\x1e\x0dR\x0c\x1c\x82'\x18\xa5.\x1f\xd2W_\xb1\xc21\xa8\x84V\xa0\xcd1\x9dlz\xe0\xe6\xa4((\xf6\xae7E $*W$\x879a\x1fH\xf3\x06\x1e\x8d\x81\xe2\x99\x037\xaa\x86\x14\xabB\xea\xedX\x9fQ\x8c\x87q\xb1s\xad\xfd\xaaa\x97\xd2\xa4(\xf3\x0d\xe5\xcdL\x96o\xbb\xf8\x8c\x9a2\xea\x8b'\xd0K\xd0\xc2\x996b\x1fX7+\xda*M\xc9'.\x05M\x1cq\x87 \x97\xcfT\xd1\xc2(x\x08\xd2\xfb\x1c7f(\xb9\n\xb4<\x94\x8a)n4\x86\xa62b\x0c)\xbd\xa5-\xd7P\xac\xd2M\x1cV\xef\xbc\xc1l\xa5\x96\x95\x03\xb4\x019\x82\xf5\xc0\xed\xa1\x9d\xd7T\"\xaf\xc2\xb70\xa5s\xd5H\xeeY\xf3 \xd3\xb7\xf0\xb0\xfd\xe7\xacg\x1a\xef^Q+\x01;\xdd\xd7\xaa\x02P\xd0\xa03\xcc\x9f\x81\xa5p}\x910\x1f\x80\x9a$\xbc#\x17\x85\x9b#WNZu(F#\x8flI~Q\xb3\x8b\xdaC\xae\xd1b\xe2E\x05\xf2Ac\xb6y\xb2B\xc9\x0c\x01\xe2\x14\x1e\xfd\xedn\xa2\xb9I\xd1\xcf\x94\x9e\x03\xfd\xeeiW\x12:\xddKO\xa8\x9c\x1c\x9d\x10m\xc7\xe4{\xa0\x8f\xb4\x94S\xef\x18\x06\xbb\xc73\xf1\x9e\xae\xd7\x1b\xdc\xa5\xad$\xc3p\x08\xd1\x18H\x83\x89\x8f4\xbc\x8cNa\x06R\xa5\x19\xb4\x07\xf2\x9e%\x88t\xf7E\xdd\x1d|r\xdd\xb4z\xa14WR\xca\x9f\xdc\xef)\xe9\"\xfe\xa4\xa7\xef\xf3\xf9\x83\x9e\xbeo\xc3\x1f\xf4>U\xf0\x07=}_\xcc\x1f\xf4\xf4}\x81T\xdf\xb7@\xf0\xa0s7\xe3\x1f\xb9\xd7t*\x08\xd5\x8a\xc0\xf0\xe3+\x02\xf5e\x8c\x86(\x02\x15\xc1\xfb=\x97\x0c\xad\"0\x96*\x02\x83J\x11\x18\x8f\xc68\xd7\xfb_\xc3\x02\xbe\x81\xf8kXP\x81%8Y\xb4\x15\x81\x0b;E`a\xab\x08\x8c\xec\x15\x81\x01W\x04.yd\xb2\xff=\xaf\xa9n#\xc7\xf1>\n\xdd_\xcb\xaa\xe0E\xc5\x8b\xef\x8eoa\x01\x87\x93\xdak\xa0p\xc6<\x1e\xc7/\x1cz\xae\x9c8a\x1d1\xe5\xbc\xed\xb5\xf3\x9e\xf7\xeeQ\xc7\x13l@\xff\x1c\xe8\xab\x86\xf0\xb3,\x11\xde\x15h@\x15\x8aN\xce\x8f4\xe7G\xbc\xc0\x93\x1b\xbe\"E\x1aoIx\xbc\x99\x979!\xeeI\xb50\x1d\x85\xaed\x85\\\xbar\xf4\x900\xa5\x17(Z\nU\xdb\xf4\x02\xb1T\xa1\xba\xf9\x04\nU\xbd*\xd5F\xe5\xca\xb2\x1d:\xfaa3<\xcf\xfd\x80\xa0\x8d\x18\xb8#\xb9\xaa=F\xb8,\xa9\x90\x1dE\xb4\xebb\x94$$\x9f\x18z\xa7l\n\x1d&\xad\xdb\xda\x0d\xe1\x9c\x12k' z}\xa4\x99#\xa7\xcc\xb5\x9d\xb1\xcb|\x96\xc6\x98\xf8\xec/w\xef\xde5h\\\x17iR\x1e\xb3o:Q\xe9\xc7Q\xb0C\x9a4\xf5`\xc2\xfa\x90jp\x893GG\x99\x1a/\xa9`^h\xa7(\xdd\xe4\x01\x99\xc1\x91\xbc\xbb\xa3Q\x8d\x80\xe7\x94H\x9f\x8b<\xd0\xe7J\xc3\xb4\x95\x0fw\xc7i\xcf\xa2\x8e\x1b\x0bi2\xd9\xae\xd1=\xe9dj\x80\xa2\xf2\xe4\xa9\x8b\xa7\x8e/\xd8\xf2,'\x81_\xea\x99X\xe0\x02\xe6\nm\xa9^T\xa0I\xf5\x1d~\xe8\x9d\xc7\xad&\x85\x9b\x1b>\x91)\xf3\x1f5\xaf-\xe5\xdc\x03?\xfe.\x8e\x96\xc9\x0c\x9c2\xcd\x0c\xf8I\xaf\x8cr\xff\xc9\xf2\x15\xf7\x9c\xd8\xf7\x0e\xc8\xda\xc03\x1amQ,\x026\xf3(\xfe\xff\x82>\x19p\x08\xce<\x8dC=n\xeaw'\x08\xad\x84&\x0d\x04\xb4I\xca\x86G;Vk\xa5\xde~\xa6=\xa3\xef\x17\xa7\x1c\x99\xee\xfb9\xe7dv'\xcc`K\xa3\xa0A\xa7r\xdd\xb0AIy\x80\x1f<\x7f\xd7s:\xf6sc\xee\xb1\x0c\x81w\xef\xb9\xaa\xcb/\xc7\xddT\x00\x16(\xc7\x03\xbd\xd0V\x99\xc0\x0dp\xf0WN\x7f\x9d\xd2_\xbe\xae'F7\x07!\x0f\x1b-\xf1m\xbf\x00\x83\xd5\xab!\x9b\xf1:\x84\x0d\xcd\x00\x86+\x9a\xdb\xe2\x0e\x02\x81\xa1%\xeeIa\xf0 \xe0Q\xdc\x0b\xb8\xa1\xb3\xa8\x8dd\xd62\xf6\xa46\xa8U\x87\xcc\x99\xf1\xb8\xe7'\xe4\xff\xfc?\xa7\xfdV\xf9\xb1\x0f\xa4\xc4\xea@J\xf9\x81\xa4&\xb2\x18\x8dw>\xe1%b\xbd\"\x8e\x02B{s\xa0,\x08+\xae-/\n\x99\xc2CH\xbd2\xfd\xf1\xb8\xfa\x81S\x9a\xf2 \xb2\x8a\x80\xbc\x0c\x19\x07\xb1\xaf,\x1cU\xac\xc9\x074\x99\xb3{\xf7\xee\xe9i\x07h\xe9\x07\xd8\x1c \x0c\x97\x92K\x92G\x18:\xc6\xc1d\x12l\x86\xda\xf1\xfc\xf3U\xbb\x10\xd4\xbc\xaal\x7f\x1e\xd3\x13\xefX0\x816;\xd5f\xce\x9do\xe0\xef\xf0\xed\xa59]\xc9Q`\"\xd75\xa9\xd6EuZ\xd3\xe9>\x8d\x1e\xaa\x8c\xb5$\xd3\x82D\x1f\xabA\x8c\xe4\x19Is\xb5\xb2\xbf^\xe5z\xa2\x0e\x0c&\xdf\xda\xae\xe8\xaf\x1d\x8am\x88\x197\x91,\x1b\x1f)\xa4W\x9a\xd8\xed+E3\xb0F5\x18\x82n G9T@\xa2\x89\xd2\xdc\x8c\x19\xd5\xa0\x81n\x06\xa7 #\xca\x01(\x92\xad@W\xda\xfc\xe9*\xd1\x11U\xaa\x03\xd0\xf1\xa7/\xe8\xd8\xb8.\x89\x8eL\x9f\xfd\x99\xa3\xe3\xab\xabD\xc7$-\x07 \xa3\x01\xad>\xbf#\x11\x0d\x14Wv\x02\xbe\xba\xec XW\xff\xba\x94 \xa0\xaf\x08\x0e\xe2\xb4\xd0\x94K}\xef\xec\xe0G\x98\x19\xfd\x08\x99\xe1\xee\xba9Pe\xca\xcc\x90\x99\xd4M*\xe2O\xa41\xe4\x99*\x86^z\x971\xa8\xdc\xbc\xac\xdc\xc6\xa0\xf2\xf42\xbbR\x01W\xe1G\x83E\xffd&\xf4\xb7^\x94\x84\xe4\xfc\xc5\xc2\x95\xa4\x12j^\xa6\xd8\xa0%\xcf\xeci\xe1\xfa\x03\xdci\xac\x1c\xe0\xd6\x03\xdcw\xcc&y(p\xe7\xb1\xd2u\xc4\x81h\x02?\x83C\xd8R\xd2~\xb98\x17\xd8\xc5\xbb\x02\xe0\n\"l`wg\x06`\xedo/\x13\xe0d\xd5GK;3\xe8\xe7C\x1b\x9d\x0b\xb5\xeb\x82!\xc4\xaf\xf6L\xf0\xe1\x9bC\xd8\x18\xc8L\xbf\xc2\xd3\x89\xe7yo\xb5#pN\x9c1\xac\x85\xdem\xbd\x9b\xae\x1b:\xfa\xeef\x90\xa9Y\xdf\x0d\xd6:o\xa8\xcc\xb5:\xbd7\x98q\xc1\x18\x97\x05\x95\xe2\xb96\xe2\x98\xfbF\x8f\xd0\x7fX\xaa\xab)\xec\xcf~l\xb4R\nX\xceB\xc9+\x1d\x8aK\x91\xcb\x8a=\xaad\xce\x0c\x1e\xee\x1ej+\x0c\xfb\x1a\x13&m\xa9B\xa9K\xc5\x1b\xb6v\xa3\xa0\xda6C4\x11\x01=\xd4\xfc\x12\xe9\x8c\xc1>\xa51\xb4\xa4\xd8\x80K\xb1V\x078\x0bvN\xb4\x9ex\xd0\x10f\x0d\\\x87\x9dh\x0e\xb5\xe8\xeb\x1bU\x1fcpZ\xf17\xad\xe7\xbd\xbb\x1dy\x14o}\xb6\xb1mr\xc93UI\x9e\x91J\xf2\xf4U\x92\xe7F%y\x16*\xc9S]\xad \xeb\xc5qRy\xd4\xcd\xea0\x9c\xe9\xfe\xe7\"\x80\xde\x9d\xd3\xff]?\x19TR\x14\xa1/\xf4)e\xd0\xf4\x03\xc8\xa0;\xe6\xf8\x87\xeb\"\x83\xdaH\x89\xc9@i5\xddAZ5\xcb\x8a\xfe0Yqc+\xda\x16\x18D\xdb\x0d\x15\xd1{\x03\xb0d\xc4{\xe8\x9f\\E\xa4\x18J\x07\xa0\x06S\x9f\x0d$n\xc4yP\x81\xce\xc2K\x8d\x83/\xd2|\xedk\x95\xb6\xc0\xb7#\x7f\xe1|m\x94\xaa\xb654F\xaa\x1a\xc0\xd7\xd2 \x15\x9f\xfec\xc8\xa7\xb1\x1c\x1c|\x03\\\xa8d\xe1vKR\xd6\x0bG\xf7\xb6\xfeE\x94,\xafL\xf2\xc6\xa9\x19C%\x81\xf3\x95\xb8\x02\x11\x9cw\xf1\xa7\xb4\xdc\xb9\x97\x17\xde\xca/\xcc-\xe9\xe7\xeb\x14\x8fe\x18\x83i.)Y<_\xc7\xe8\xfa\xb7\xfa\x0f\xd9\x13vS\x07;m\x0c\xe3\x84\x83\x81\xf1h\xae\xbd\xf3?\xff\x8f\xfe\xcf\xc1\x14\xe2\xce\x0c\x9c1\x1c\x97y\x94,\xddT\xe7M\xdaL\x94T!\xe8Vw\xe6\x9e\x99&\x83K\xaa[\x03\xa7\xdf\xf2II4=\xbc\x9c\xc2\xcb\\\xfa\xeb:(\xbc\xc6Pz\xe2}I <}\x86\xa7k\x91\xe0I\x14Qj\x8d\xc3&\xd3\x13?\x1e\xfa\xd8\x92T\x8f\x7f\xf6%*\xd9\xb4z\x8c\x87\xc0\x15ef\xe2{\xb2\x97\x0d\xc9*\x05S\xd9\xd9yI3W\x92\x1c\xf9\xa2k\x80|}<\x8be:\xd5\x94?\xe8\xe9T#\xfe\xa0\xa7S\xf5\xf9\x83\x9eNu\xc3\x1f\xf4t\xaa\x05\x7f\xd0B\xf2X\x8d\xe4\xf1\xc7G\xf2\xe0\x8a\xb2\x14\xa5*\x05f\xcf\xbbF\xa6\xc0\xcc\x87+0\x95Y\x8a6R\xc5edR\\~\xb2,Ei\xf2:\xbfH7%\xa6\xdfV\x03'\x1c\xf8\x91\x9f\x04$6\x00\xe7\xcc\xab%\xf1\xe71 \xb5\x01\xfe\x86\xba\xdd\xea\xb3\xb1U\xa8<\xbf\x98\xa4\x1buT\xb7\xb6R\xfb|S\x96\xf6Y\xd1\x9dy\x99\x00o\xef\xf4\x94\xfe\x11\xe0\x84\xd8\x147\x97\x1f\xcb\x94\x0fd\x93\x8aa]\x1f\xaa\x9f6\x1dT\xd4\xfc\x1b\x83\xf3:\xbf\x80\xa8\x84tS\x82\xccdfp\xdd\xd4\x17\xf7\xaeX#V\x12\xaak?i\xe1\xe7\x0c\x9e\xf0\x1d\xd0\xa8\x86\xd6\x01o`\xa8\x19\x9c\xe3\xe8\x0c\xf6jc!&\xc8\xa8\x0f\x95\xebYp\xfc\xcb\xa1\xf2\xe5P\xb9\xbe\x87\xca\xfc\"\xf3\x0bC\x91\x16\xe2E\xc5\xf1\x99\xbf\\\x92\xfc\xc0t\x94\xb0\\?\x1a\x12\x86P~\\\xa4\xc7\xab\xf4L{\xe2\x94\xba\xc3\xa0\x19XP\x8f\xd6\x0bVQ\x1c\xe6$A\xa1\x0e\xcb\xfc\x98?bG\xa6\xb7$/\xa24\x99d\xb9\xbf\\\xfb\xca\x13,\x1d\x7f\x88\xe6NO\xd7\xa4(\xfc%\x01\xc5\xfd\xc9\xc4_\xcf\xa3\xe5&\xdd\xa8\x0b~X\xcd\xa5\x12hu\xab\x0e\x0ey\x83\xb4\x18\xca\x14\x18\xc6\xe2\n@]\xea\x06\x13\xc7\xa8>\x94\x99\xdb\n\xd2\x90\xd4\xad\x15\x0c\xf5X\"V? \xa9\xa4a\xf9j\x9a\x91\xc4\xcf\"\xf6\xea\"\"qXP6 IK\x98\x13\xc8rR\x90\xa4\xc4\x8a\xd4+\x02\x85\xbf&\xc0\xf1\x1c\xd2\x1c^d$\xf9\xee\xe5\xd3\xc6\xb8\xeeY\x8e\xdc9\xdedY\x9a\x97$\x14\x0b*z\xe7\xe7d\xc0\xf8\xf8\xd4\xa0\xf0\xf57\xe7\xc0\xdbw\xfeV\xcdR\xb9J\x0b\x02\xe5\xca/a\xed\x97\xc1j\xc0g\xf9\xb4\xcd\xe0\x96\xb7\xef%l\xf6\xdcE\x9a\x039\xf7\xd7YL\xc6\xbb~k\x1f\xbf5\xf2\x1c\x11\xd3BI\xb0\xc5\x16\xd5\xee\xf3\x0f\xb0\xdf\xae\xdf\xf6^GE\x11%\xcb\xcfgs;\xafWt\x87\xa5\xdb($a\xe3u\x08SR`\xad\xdd\"#A\xb4\xb8\x00\x9f\x1eoQg'X\xef$\xbe#\xa3$\x8c\x02\xbf$\xd5\xd7$\x1b\xb9\xdd\x00|\xd9\x83\x97\x11\x10Z5I\xed\x85\x04q\xf2\xcb<\x0e\xc5\xa6\x96=c|\xca\xe7\xc7\xfd_c\xd5\xe5\xe0\xdc\xf4l\x97\x0c\xd48\xae\xfd8\xae0Q \x96\xe5\xf2\x9cm\x12\x9a\xd9u\xb7\x03\x07\x13\xb6\xe3\x7f\xafY\x92v\x8a\xa0\x8f \xc9\x9eE\xc9\xbb\xcf]\xbd\xdd\x18\x87\x0d\xb2pq]\xa9\xde\x96F/1\xe1\xa0$\xe7\xe50$\xf3\x8d\xb8\x93\xa4\xa8\xe1\x96\x88V\xb5N\x05\x1e\x1a<5\xa11\xd9^\x96\x93-I\xca\xc7\xacG\xae\x84\x92*\xf3\x9b\xae\xb0\xa2[\x89\x15\xddn\xb2\xf4N\x0c\xb4\x8b\xd9&=>\xdbT\xe9g\xa9n\x1f\xe3j\xf7\x1d\x89)\xb6\xb9\xb8+F\xacLk\x0b\xa1s=B\xe7\xed\x19\x94O\x86R\x8a\xe6k\x1b\xd9\xb0RJ UU\xc1\xf3u\x9c\x143pVe\x99\xcdn\xde<;;\xf3\xcenyi\xbe\xbcy\xb0\xbf\xbf\x7f\x13_\x93\xbf\xf4\xcf8J\xdeI\xdf\x9c>x\xf0\xe0&\x16 \x94\xbc\xabM\xf0\x93\xa5\x05rc3p\xfcy\x91\xc6\x1be\xf9{^\x05QQ\xbcF\x94?\xdc\xef\xa3\x7f\x17\x99\xd5\xd3J\x16\x85\xc5\xbc^\xac\xe7i,\x9d\xdamD\xce\xbeO\xcfg\xe0\xec\xc3>\x1c\xd0\xff\x93\x0c\x06\x0bNm\x928\x0d\xdeu\xd3\xd3\xe9z\x97\xb1<\xe0\x12\xa4\x9b\x81\xf3|z\xc7\xbb\x0f\xf7\x7f\x98\xde\xfe\xf9\x8ew\xf7\xd1\xf46\x1cx\xf7\xf6o\xc1\xf4\xc0\xbb{\xf7\x0eLa\xba\x0fS\xb8\xe7\xdd\xbau\x1b\xa6p\x97?\xbd\x0bw\xbc\xbb?\xdf]\x1dl'\xde\xfd\xfd\xe9\xa3\xfbp\xcb\xbbw\xe76\xdc\xf7\xee=\xb8\x07\xb7\xe8K\xb7\x82\xa9w\xb0\x7f\x8b\x0e\x07\xf0\xd9\x01\x1cx\xd3\x07\x0f~\xbe\xff\xc3\xed`\xe2\xdd\xb9s\x0b\xf6'S\xf0\xee\xde\xbe;\x99\xc2\x14\x1fM\xef\x05\xfb\xe0\xdd\xb9\xfd\xc0\xbb}p\x9f\xde\xbb\xf5\xc0{p\x87>\xbd\xb5\x7f/\xa60\xf7\xbc[\xf7\xef=\xba\xe3\xdd\xbdw\x00\xd3\xfb\xde\xfd\xbbS\xb8\xeb\xdd\xb9\x03\xd3\x07p\xcf\x9b\xc2\xf4\xc1\xea\x8ew?\xa0\x9f\x80}\x98\xd2\xcfL\xe8W`J\xbf3\xa9>swB\xbf\x13xw\x0enO\xbc\xe9\xdd{\xde\x83;\xb7&\xde\xbd;\xec\x07m\xee\xee\xcf\x0fh\x97\x1eM\xef\xc1}\xdaG\x98\xde\xf5n\xdd9\x80\xfb\xc0&\xec\xdf\x9d\xf9\x1f\x8d>\xf8\xca_\x9bu\xff\x93\xac\xe0\xf3\xe9\x01\xdc\xff\xe1\xfe\xcfw\x10l\x10\n\x7f\x82\xd5\x97\xe4\xb9\xb8\xc4\xe2\xdf\xf6n\xdd\xbe\x0f\xd3\xdb\xde\xfd\xdb\x0f\x82\x89w\xfb\xee\x03\xfa\xff\x93\xa9wp ~\xdd}p\x0f\xf6\x9fQ4\x98z\xf7\xa7\x0f\xe2\xc9\x81w\xf7\xce\x94\n`\x07\xdaW\xf0Q\xe3\x1f\x04\xa0\x98B\x1f\xc7\x07\xde\xbd;\xf7'\xb7\xbc\xe9\x9d \xfd\xf9\x00\x7f\x1e\x04\xb2\x97\xee\x8b\x97\xaa\xdb\x80\xb7\xc5\xcf\xaa\x83\xf7\xbd\xe9\xfd[1vor\xcb\xdb\xbf5\x0dto\x80\xe8z\xf5\x9ca\x1a\xed\x1d\xf6\x89b\xc2\xf4\x0e]k\xf1;P\xbe\xf2)0AY,\xf7\x12\xf8p\xcb;\xb8\x03\xd3\xfdgw\xbd\xe9\xfe\x038\xf0\xee\xdc\x0f&\xde\xc1\xdd\xfb\x13\xef\xe0\x1e\xffqo\x1f\x17\xf7\xc1\xbd\x07\xe2\x81wo\x7f\x8a\xff}p\xf7\x01\xec\xc7\xf7\xbc\xfb\xb7\xe0\x9e\xf7`\xff~@!\xbc\x83{S\xfc\xef\xbd}:[\xf4\xc5x\xd2\x80\x99\x08 \xfa\xe9)\xb6\x83\xdf\x11\xed\xd2\x15\xec4\xfcL\xf4\xf3\xd3\xce\xfa\xa4\x1fyy\x89\xa9\xbf\xe7\xdd\x9e\xde\x07\x9c\xf8\xc0;\xb8w0\x11\x93\xc6~<\xb8\xf7\x00\xf6\x0b\x9c\xcc{\xfbS\x9c\xc8\xbb8\x91\x0f\xf6\xef\x03\x9d\xce\x00\x97@\xcc\x14\xfb\x81/q\xa0I\x05\xd4XQ\xfc\x14N8[\x81~\x93\xb8\xf3\xe9t\xc7\xd8\xc1\xc9=oz{\xfa\x81\xe6\xfd6\x1c\xdcV\xcd;/\xcbqe\xd3\xfd\x00\xeemo\xffp\xc7\xbb\x7f+\xbe\xe5!)\xba\xf3\xe0\xd9}\xb8\x1bO\xee\x02\xfb\xdf\xd4\xbb=\x9d\xd0\x7f\x9eQ(\x98\xde\xfa\xe1`\xfa\xf3\xbdO0t\x16\xf1~e#\xdf\x87\xe9\xfd\xd5\xed\xed\xe4`5\xb9\xbd=\xf8\xf7\xf3[pw{\xb0\x9a\xde\xff\xf9\xee\x0f\xb7\xfe\xbd\xbe\x05\xf7V\xd3\x83\xed\xe4\xe0\x87\xbb\xdb\xff\x8f\xbdw[r\xe4F\x16\x04\xdf\xfb+\x90l\x9d*\xb2x\xc9d\xd6E\x123\xb3\xb2\xd5j\xe9\xb4\xd6T\xdd2\xa9\xfa\xcc\xce\x90\xacj0\x08\x92\xa1\x8c\x9b\x10\x08ff 5\xd6\x0fk\xfb\x03\xbb\x0f;f\xbb/\xfb0k\xf3\xb2f\xfb\x0b\xf3)\xfd%kp\x07\x107D0\x98U\xea\xd3\xe7LS\xb2\xca\x08\x04.\x0e\xc0\xe1\xeep8\xdc\xcf\xeb\x9d\x1d|\x1c\xc5\x84Q\x18D\xfd\xf3O\x07\x13\x9a\xa6\xfe6\xaa\x9f+G\xfd\xe9\xd9Y\xd5\xa6\xd47\x1f\x9e9\xce\x95\xd5\x87\xe9s\xc7\xb9\xb2\xfa\xf0\xb4\xbaCK\xf1\xc3\xf3j\x13\x81\xf3F\xa5\xdd\x9b\xa9\xba\x9e}\xee0u\xdddA\x80\x9f\x9f\xbb\x82\xedxq\x18\xc6QH\xf9\x8d\xce4\xad\x1c\xc5\xba\xd4$\x9ekP\xd5\x0f\xce\x10R\xee\x91+\xf5\x19\xdeX\x04\xd1\xbb\xf5[\x0c\xd7\x95\xd0}\x8b~\xd6_D|\xc3\xe0\xc3|\xa9S\xfc(\xf0#\xf6*^3rEN\xa6\xa5T<\x0d\x85G\x9d\xbeR\"(\x1e\xba\xaa'\x9d\x8aJv\x86\xa7\xa7\xe6\xc5\xb4x\x9f\xc4[N\x93\x9d\xfe\\x/\xa0S\xbd\xf7\x1b\xe7-\xa9^\n\xe6y=rrE\xc4}\xc2\xe2\x0d\xea\x8c\xfa\xa0\xb1\x19\xc1\xc1qOOWoP\xedL\xc4nIV\xe9\x89J\xa3:\xcd\x8b\xb9\xc9\xe6\xd7\xbb\xa6\x92c\x93\x9c\x056-\xad\x8d\xba\xbd\x1e\xef\xc1\xd5\xc9\x8c\xb3~0gK\x03O\xcaD\x1f\xae\x1e\xfe\xfc\xbe\xba\xa4`\x08r\xf3\x11\x95\xb5UY\xc5\xfb\xc5\xa6G\x84\x15*\x1c\x95j\xb2\xa0tR~\xa9Z\xcb\xfa+\xb80\xc9\x06D\xecx|\x0b\xfd\xfe\x8a\xf3\x98\xf7{\xff\x81\xc7\xd1\x96\xfc\x993\x85\xdet\x15\xb0?\xe3\xa1\xa4\x18\x11o\xc7\xbc\x1b\xb8\x9c\x7f\xea\xa1\x13\x8e\xea\xbd0\x8b\x9f\x18\xabF\x8d\x8cM\x1a\x8c\x88\x02[\xab\xe7!\x87V\xe4\xdc\xb0\xfb\xb4_\xfc6\x98lb\xfe\x15\xf5v\xb9-{m\xd5`sy\x99y\xb4\x84i\xc4\xa6\xcd\x1b\xd7Z\xbf\xbe3+\xc4\xd2\xaa\x10\xc6\xa6\x01W\xd4\xef\x8a\xb4\xde\xf93\x8a\xb8\x82\xc1\x87zj\xaa1\xa1\xfcp\x9dj\x06#\x8d\x99\x9e\xae\x18\xf29\xd5\x91\x16\xedU3\x1eK\xd3~4\x18\x91H\xd3\x89&@\xf4\xa1Z\xb7\xde\x01:!\xb6W\xd6\x94~@\x14\x86\xcea=\xe5\xf5\xa4RZG\xe4\x1b\xb3\xbc?\xe2\xb8D\x15\xbax6\xfa\xa0\xa1\xea\x06\xe2\x03\x06\x0c+\xee2l\xe0\xf7+\xe6B\xd1\xa7M\xe1u\x92 ?H\x0dC\xfe\x15\xf9(|\xbd\x81\xa1?u\x1e\x07\xf85%\xa6%\xb1)D\xfeE!\x01\x9c\x8e\xc4\xa6\x97[&~\xcb\x19U\x14<\xb6/\x0ebZ\xec\xb6\xaf$\xa7nS\xe3\xe0\xba\x9b\x98\x93\xbe\xe9e\x0e\xe1Hk\xfc\x03\x16m\xc5n\x04B\xca\xd9\x08D\x92^\xef\x82\xc4\xe3\xf1\xc5\x80P2\xbc\"|\xce\xe6\xfeR1@\xb6T\x8d\xf8\xc3!\xb6\x84]r#\"-\xcea\x1d\xfa\x8f\x0b\xf7x\x9a\x03>\x1c\xfa\xe4\x92\xc4\x17\x03\xd2\xc3\xa5\x80\x8e\xf3m\x17\xc85\xf6\xaa\x80\xa0\x06\x19U\x16s\x0ej`\x9a5\x8c\xc1Q#\xf0\x91\xb0s\xb2\xa3\xa9\x0bC\xd5\xa7,b\xa9G\x13\xf6j\xed\x92=U\x0e\xce\x92\x80z\xec\xabH\xf8\xc2g\xa9K\x12U\xd9\xb0\x9a\xdf\x8b0\xa8\x8b\xa4?\x17\xb4\xfa\x19J\"?e\xb1`o!\xa6\xd5a\xed~\xef2/\xf3rQ\xd8\x88\xbe\x1f\x95\xeb\x03\x95QG\xb2\xd3\xbb<-\xd4\xda#C\x92b\xf6r\xed\x1eR\xc4.5\xb2\xb9Xj9\xeb\x9a\xf4.\x13\xce^^\xaa\xe2P9\xed\xc3g-\x17\xc0u\xe6\xcbS\xf8zy\xaar\x16\x00 3\xd2\xebR\xb02\x0e\x1b\x16y\xae\x85=R2`\xe0\xe2\x0f\xdeH\x91F\x08\x1d;\x17\x8ekjkX\x1b\x8e\xc305\xeb\x93\x80F\xdb\xef8\xdb\xf8wu\xc9)Q\xe4\x9a\x86\xa9K(Q\xdf\xc1\xc9\x0c\xf8\x9f\xd1\x19'i\x12\xf8\xa2\x7f\xbaH\x87\xa7\xdb\xc1@\x87\xf2\x86H\xde\xbc\x1f\xe0\x12\xc6\x1e\xbe\xf5\xb2T\xc4\xe1\x88x\xf3\xb3\xe5\xc0\xfa\xb1p\xe5\x99\xab,\xcb\xca8\xd4\xed\x17U7\x1f\xe3\xd1\xe3U\xef1\x19\x92\x1d\x0c\xbb\xdf\x8f\xfb\x9b\xc1@\x8d\xf8\xe3\xde\xe3R)\xa7)ia\xc6\xd5\xbc\xad\xd5L\xc1\x0c\xf6\xa3\xc9\xce\xdf\xee\x02\x88p\xf4\xe8\x11)\xbcj\xc3\xd5B\xca\x88\xcc\x133\xd90\xeb\x1e\x15}o\x80n)\xfa\xf6\xd3\xa0\x15\x83\x1c\x88\xa1\x87DK\xeb\xd9d\xc7\xe8\xda\x8f\xb6\xb5%\xd8\xbabv\xaa\x0d@\xc7\xdd\xb7l\xcf\x02\xecb\xb95S\xf1\x91k\xd1Yum\xad\xef\xbap\x00c\xda\x1bM\xeev\"\x0c\xfe\x98\xc1\xb1\xed\xe5\x8e\x93\xd3\x97=X\\;\xfe\x12<\n8\x87k\x95\x05\x01\x13o\x03?\x15\xdd T\x168\x08S\xa1\xa2#G#\x0b\x9a\xa7\x13\xea\xf3\x05\x0b\xbbC\x17\xf8\xd5Y\xca+\xa9A\xd6\x0cU\xe0\xd7;\x19s%\xaa\xad\xdd\xc3\xd5&\x98\xaa\xb9v2\xc0\xdee\x1c\xe8e\x03\x95\x93\x97dJ\xae\xc9c\x92\n\xca\x05\xaeP\xf3 \x96&FTu#L \xbc#'!n\x99\x04E\xb5`[\xdf\xa9\xcfE\x06!\x80\x0c\\\x93\x1e\xa2bR\x9d\x99\xbc\xe6N\xe0\x9a\xe1<\xe9\x17jW;\xe659\x07\xe1\xf1%\x05\x1b\x10\x03\x07R*\xce6\x06\x06\x0c\xf3\x15\xbb(\"\x8c\xc1\x11\xcb\x8cV+\xf0C\xba\xed\"\xb2\x9b\x01|LR\xee\x95 M\xb9\xa7\x01\xad\x8fS\xf6\xd0!oX\xbd~\xb85Q\xcf\xfa\x8f \x0d\xf4hc-4P\xf3\x80\xcc\xd5$\xa0]1.\xe1\xc7\xbd\xc7\xeaO\x86\xeb\xbfH\xbf\xc9i\xaf\xb0\xd0+#\x04\x11D\xbb\xd3C\xc8^'\x16X\xcb\x113\xd5T\x8f\xe2\x81G@\xa3\xb27\xd5r\x0c4\x0d\xf5\xac\xe2\xf5\xfd\x11\xd0\xa8\xecM\xb5\x1c\x03MC=\xfc\x08Pxm\x9e\xf9Q p\xd7\xa8v\xa2\xd8\x1d\xb8\x94\xd8i.E\x03\x7f\x1bi\x0eu\xaf\xd6\x8d`wb\x0c\xa93\xa43\x98\xa3\xca\xac\xea\x90\x1d\xd3\xb7]\xad|\x1d\xe5\x1e\xda\xb3\xf5G\xee\xd9qh\xbc\xae\x96O\x05\x8f\x1d\xa2jc\x15\x98\xbf\xa1\x96# q\xd7s\x8c\xe0\xc5BG\xe9# \xa8\x97_\xb3\xa0{\xf3k\x16\xb8\xca\x1f\x01\x80\xa3\x06?J\xbbC\xe0G\xa9\xab\xfc\x11\x108j\x08)\xaf\x0b\x15\x8d5\xa8\xdc\xce\x1a\x8e\x00\xc2UG\x9a\xad\x0e\xad\xb5\x1c#\xb3U\xf3f\x1e>V\xebN\x8e\xa8;i\xab\xbb&`\xee(_\xaf\xb4.\xf1\x90D\xa1\x1b\xa9\xec\xa4Vj'\xb5\x88P\x12\\9\x88l\x1ao\xc4\xd1M@\x81\x94\\whM=\xd6);\xbb\x13\x1d\x07\xad2T\x95\xf1\x11a`N\xcb\xbaTV\xac\xaa^\x93\xa0\xdb\x0f\xae\x87\xaeVu\xae\xd9R\xd3\xe3KU\xe2\xa0\x14\xf7\xf2\xb1\xa3\x99#\x16\x85\xca_SB\xc5\xb1\x88b\xc1\xder\xb69\x04\xad\xe1D\x7f\xc8\xc2\x15\xe3\x08\x9f\xbf&C2\x1dLD\xac\x1d\x938N\x97\x95\x88\xdb\xdbD\x9cm\xc0\x10\xdb\xc9\xc4P\xea\xcdV\xdf\xac\xc9Kr\x06G\xa6\x9c\x0c\xafHof\xf5\x0c\xf0u0\"\x8f\xd5\n2\xea\x1f\x03\xffX\xd5\xfe\xd2\n\xfd\xbf\xdeD\x8fuL\xdf\xc7=\xe2\xaf\xaf\xac\xc4\xff\xb8\xf7rn>\xf5\x96Jxw.:;.\x80Y]wD\xba3eI\xf8\xf1\xe5\x8eW\xc1M\xc7)Kz\xb0N\x14\x1fn\xce\xa22\xc0\xec_\xa6\x0c\x9a\xaeeSY.\xe3\xa0^\\m\xa1\xa1|k\xcf\x8e\xc0\x9f8PM\x9dj@\xeaT\xc4\xd6|\x14\xea\x07>\xcc\x0fNX;j\xe1l\xd6\xa6\xde\x17,\xac-\x0e\x0b\xcc\x11\x1dt\xe9Kl=4\xf2v\xf1\xc1CE\xb3Fr|o\xefR\xd7\xc5\x105-\x06\x92\xe3|\x01\xe3\xabC\xb4\xa2\xde\x0d\xac\x90\xbf\xfe\xaf\xffM\xe1|e\xb0\xd6\xc7\xc8(\x0e\xcd\xd9\xfa\x08\xcd\xdbZ\xd4D\x9c#\xf6^\xeb\x9a\xb0\xb9>N>rC\x7fL\x0d\xc2Q\xc3Q\x02\xf3\xba\xb2\xe9+\x1f\x03\xa5\xe4\x8ad\xc5\xf3\xc3.\xcb\xa8_\xe4\xa4\x84\xf5]\xc4\xa9\x90}8\x8c\xc8\xcb+\"\xf4\xe9\x1a\x19\x93s\xc5\xc7\x15\x9b.+\xcaP\x13\x05\xd6\x07F\x0b\x85/FmU\xd2X\x89\xb9B\xbf\x82\xc6\xea\xac\x9c\xac\x99\xa5iU\x15\xafh\xcf\x8a\xf5\x9c\x97\xda\xd4 Z\xab\x85=Tip\xc5\xb9\xd4\xcf\xf78P\x03ri\x8f\x0f\xa1\xa9\x8a\n\xd5*\xd9\xecya\xaf.\xa7\xe4SS<\xa8\xcd \xf5\x03\x0f\xfa\xea\xc6]1\xb9\"\xf3\xda\x94\xcd{@\xa8{\xe8\xdb\xff\xec\xf9\xc0q\xf03\xef)\xden\xb2\xbcpg\xe1l\xc38\x8b<\x08\x13\x0f\x19?ug\xd4S\xaa3}\xe6\xced\xe9\xa2\xa0~`\xf2~\xde\x0c\xdc\xb9\xce3=k\x82\x0e\x8e-C\x16 \x03\xdft\xea\xce\x9a\x86\x94\x0b8\x06\xb49\xcf\xdd9\x03?\xba\xf17\xf7&\xd7\xd3\xc1\xb2\x94iy\xc4q\xbf\xc3z\xaahd\xc5\xcb\x84\xdc\x1ej+\x92pvA\x18\xb9$\xb1F\xc6\x0b\xc2\x86\xc3A\xa1\n\x8c$\x12\xcf\xd9r~\xb6\x1c\x11x\x98.]\xa6W\xc5\x03vm\xe5Q\"\x10.n\x84Gi.\xf8\x04\x9a\x02D\xe66X\x01\xa2-\x13\xdfg\x01K\xfb\xbd\xde``\xe1\x16\xe4\x92D\x17D(\xf0\xf9\\,\xfb\xac\xd1\x84\xe3\x03n\xc3\x95,A\x1a\xbb\xc6\x8a\x160\xd7\x84i;\x17\x1c\xcb:\xe1SC6\xb3\xd4\xcae\x01\xa9\x830\xb1I\xca=s\x88\xde?]D\xa7[\xbc\xf6:\x11\xdc\x0f]\xe2m\xc0\xf6,p\xde\xdeRm\xa532?\x1b\x91\xa9\x03?\xf3\xbb\xd8\xf32^\x82CWm\xc2h\x0c\x8f\x14X\xa3\xa2\xbd$\x9b\xb0h?\xb2\x1d\xff\xd8\xc6\xafO\xab\xb6\xaa\xdaJ\xe6y\x93\x91\x0c3\xa7\xb6\xbe\x0b\x0b)\x9c\xe6\xa6#\x12\x8c\xe0\x18\xbb~\x04\xfd\xec\x9c\x9c(\x82<\xf1v\x94\x7f\x19\xaf\xd9\x17\xa2\x7f\x96\x9f\x17\x8f\xa7\xf5\"\x9fO\xebE\xa6\xedE\xb4G}f\x1d\xe4\xf7\x96\xb3^{\x11j\x96x\xa1\x8b#2_\x0eF\xa4\x9f\xc1\xd5b:\"S\xe07gDJ\xf2\xfc\xb3:T\x19\xc8}\x8d\xcd\xc0r\x0c\xc8\x15\xa1\x93$N_\xd1\xbb\x11\x8a\x01\x8a\xc1]\x90\x94\\\x92@\xb1\xb0\xe9\x19\xd4L\x01E\x0b\xb5\xa7\x83\x0b\x92\x0e\x87naR\x873\x0c|\x8f\xf5\xcfG$\x1b\x8c4[\x86C}\xf3\x05\x9a\x1a\x91\xd4\xa0\xb9Y\xf4\xe4\x9a\x8c\xa7dF\xfa>l7\xd9\xde\xa7H\x07\xa5\xac\xa7)\xda8\x18\xe9;\xd8\xd0F%\xc7\x1c%Xo 2m\xe3\xc7+\xb2\x19(X\x1c\x14\xb0\x1bq(\xd0=\xf0'\x82Q=p\xa1\xb8\xccF\x0b\xb4\xa4~\xc9\xd8\xd2\xca)\xd2J\x9aKM\xd3\x12M\xac\x954\x0d8\x85*Z=\xde+\x89R\xd4\xca%\x8dR\x92\xaa\xc0J[.a\xcf\xfc\xa0\x03jY\xd3\x82\xc6\xe2\x82\xf0\x82pt\xd2\xef\xab\xf5\xed\xf7\xf9\xa8`R]\xa56\x88\xe3\x83\x8b\x01\x10 \xaeQ'68S\xb7\xd40\xbfb\xc3\xaa\xe4(o\\\xe1Q>\x14 \xde\xa1=c\xde=\x9bx\xc8[\xef/N\xf9\\6W\xcf\xa6U{B\xaa\xd3\xab\x86\xf8h\xed\xff\xec\xfc\xccIA\xd3\x9c\xbc\xd4\xccp\x14t\x9apB\xe4\x80\xf5\x88\xecFd?\"\xe1\x88l\xbb\xd1\xc5\x03\xa4\xf4\x01t1\xa8\xd3\xc5\xd4\xd0E\x0f\xe8b0\"g\xedt\xd1\xeb@\x17\x13rE\x02K\x17\x15\xd1\xf2\x90.n\xc8%\xc6p\xe8?=G\x8a\xb6\x86\xac\x15\xea\xb8Ac\x9c)R\xa4\xf5\xe0\x82lj\xb4\x12\xc8\x80\xaf\x00\xde\x1c\x80f\x0fM(\xc1R\xc7m\x1ca\xfc)\x03\xa4\x82px\xa5(\xc3G\x04\x0fZ\xb6\xf5\xed`\x1c7\xea\x91\"\xc8\xe4\x9a\xf4\xc3:`\x16(%O@\x86^\x0fSw\x83\x02|\x1a<\x07d\x17\x03\x05\x8c\x93\xad\xd8\xd2\x9a)9J[\xde\xb1U\xbc\xacoX\xcdtD\xbcA\x99M\xa4\x93|s2\xdf\"w\xa8\xa6\xb9.\xbe\xe8\xb8\x9c\xa1\xc3\xe4\x0d\xfc?\xecK\xe9\x8a7m>\x1eS\xf1[\x99\n\x10\xccB\x17\xb4\xc7\x8eR\x92\xb6\xa1>\x92\xff\xf8\xc7\xf3\x9f\"g\xf1\x1b8K\xce\x99\xfc\x1agr\xf2\x1f\xffh\xfe\xe3\x1f\xe2?\xe9/\xc4\x7f\xfcv\xfe\xe3\xbb\xf8\x8f\xff7\xe5?\x0fA\xc1F\xfc\x83\x01\x8fpw\x07n>\xec\x0e.\"\x97\x84_\x90H\xed\xe0JX\x01\x08\x16\xcf\xa3\xe5\xc0\xce\xba\x99\x07\xbd\x03\x11f\x00]\xbb\x10\x91{\x8b\xfb\xd7\x1a\x0d\x90\xcaK\xdb\x0c\x18\x80\xfar\xc2{d\xb5\xf4\xa4b\xf8LJ\x0b\xd9\xaa\xd5\x816\xb1\xfc\xa2\x9a\xddx\xd6B}\xb5\xe8\xdfz\xc5c\x17\xa4\x06\x85\xf5\xc7\x8cB\n$t\x85\x8b\xe6F\x1cF2\x0f\xe8\x8a\x05#r2\x053\x1cGUE\xfdV\xb9\xae\xe9\x88$Z\xce\x0e\x14IMM5}`'z\xfb\xcc\x06#r\xb2\xa9^$\xd2\x93\x9d\x0f\x05\x18%\x0e\\\xdd\x04\x04\xa4\x96\xe4\x95K\x8c\x0en\xd6I\xbeaw\x9c\xc348Q\xd1\xdbpo8\xac}\x06/Q\xb9\xb2\x83:\x15\x1an0\xa0']\xe0%\x0e\x98[\xa0%\xfa\nmK\x90\xc3\x96\x0e\x11\xdd)\xdc% *^\x93>lG\xe7\xcbAG8+\xb4\xbf\x19\x12\x81\x0eh\xda\x82\xcdv\x006\xeb\x08V\xa3\x8e\xc6\xfc\xac\xae\xc6eEh~\x06\xa0\x96j\xac\xfa\xa50\x8c\x1f\x0c}\x95U~\x8cQ\x1d\x8f\xbd\x06\xb8\xe0\xe2\x8a\x82\x1eh\x02\xd0&\x886\xab\xd7x\xfei9\xc8\x97]\x91ji\x83\xf5l\x80\xf2\x8c\x9b\xd3\x9b\xdcs[,\x97@\xac\xf6<_$q\xd2\xcf\x03\xbe\xc4\xf9\xbe3\x8b\x04\x9cg]\x17\x13fJ\xac\xe1\xa8%\xe5p\xa3\x87p\xb5\x1c\x1f\xba\xe6\xf0\x98\xee\xe1\xab\x0e\x0e\xd6Z\xc3|\x1b\xccj\x98\x12\xb7\x14\xe2#G-\xf6\xc9\x1ft\xa3\x84\xc4\xd1\xcbC\xb8u\x10q\xea4\xb2\x96\xd2\x0567\x95n\x83\xae\x05\xb2\nT\x1f$W\xd9d\xbb\xbf\xe6\xcd^\xfdruo\x7f>\xee\x0f\x16\xf3\xc5\xf2\xe7\xf7\xc3\xeb'\x93O\x16o\xe4h\xf6\xeb\xcb\x93\xc5b9\x00E\xf0b\xf1\xc9\xb4\xf71\xf6\x10\x0ey\xa5\xb8\xbb\xef\xb0\xb7()\xcf\x1a\xb6\x0dy\xce\xef\xd9\xf6\xab\xbb\x04\xc4]\xb8&\xd4\x7f#\xe7=\x08\xd2\xb8\x88\xfa\x83\xf9\xf2\xf1\xa27\x19\x9d\\\x8f{\xfafO\xaf\x87\xc1\xb7\xb8\xb9\xdb\x83\xa6\x82\xcbA_\x95*_t\xaeC\xd31n\x97\x9d\x804[\xa5\x82\xf7\xa7\x0e\xbc\x1cL\xd2\x98w\x0cN\xaa\xeb+\x9ck\x9a\x13@W\xbd\xa5\xeeI\xec\xdf\xa0\xff\xc9\x03\xc7\xa5g\xe4\xa3\xc2h\xa3\x82\x04_\xfa\xeb\x11\xe9m{j\xe7\xbb\xb1\x92Q\x9e\x17E\x933$\x98\xbb\x92\xc0\x1e\xa3\xc0\xee\xa6+\xd5\xed\xdd\xce\x9c\xd5\xba\xf3\x93\xe2\x86\xb2\xafH>\x14\xb0\xd2{eo\xf9\x12\xe8\xb2\x18\x8f\x9bk#\x06\n\xc1\xee\x84\xdeLP\xbd\xd9\x1b\x1c\xdc\x1b\x9a\x9f\xd5\x80\x9f\x8d@OF\xf3\xdd\xc6f\x12\xd0T|\x13\xad\xd9\x1d~\xf7\xb4\x0c\xb7g\x81\x11\x8d/@|\xdfL\xd8\x1d\xf3\xfa\x19\xe8-\n\xa5^\xa2\xfa\xfc \x95-\xfe4e\x83N5\xd3\xd9\xe2\xcf\x8a%\x99\xde\x98\x06#\x92\xa0>\x8d\x0cI2\x9f.\xf5\xe0v\x08EG\x0e\xf1\x99\xe2\xef=\xb8q>\xbeo\xd6L\xadc\x07\xb5\xb6\xc5\xb1\xde\xb5\xb8\x91\xcc\xcf\x97\x1d\xa2\xe7\x91\xc3\xf2b\xf1\xf7\xd0\xee=d\xeaT\x0f\xba\x15\xf9\xdb\xcc\xce!>_\xfc\x1d\xe0\xf9\xc5\x9f\x82)\x80\x05\x93/\x921I\xe6O\x0d\x8a6\xabR\xcc/-ho\xfa\x01\xb9$Y!\xe1!\xfd}\xc8t\xd9\x95\xf6K,\xa9\x12aT\x04\x0d(\x8d\x91\x98}\xdd\xf4\xd9\x08\\\x1b\xa4#bR\x04\xea\xb4\xdb)\xe6\x07 7&\xd5\x1cZ\x9c.\x86c\xb9\x98,&rq\x8d\xff\xc9\x93\x93\x93\x139\x1a\xc9\xf1\xf8\xb4~\x98q\xba\xe8\xf7=)B\xc9e2X\x0cN\xb7~\xfd`\xa3>w\xde\x8c\xf4\xfe\xfb\x7fsL\x11W\x1f\xfe_\xc7\x87D}\xf8\x7f\x1c\x1fD8#\xbd\xbf\xfe/\xffw\xaf\xf4\xa5\xc1\xda\xa6\x8b4\x95\xcbQ.iIk\xab\x8a\xbe}\x1a\xe4\xa5\xd2\xde\xa8\xc8\nS\xcd\n\xd3&VXc\xc4v\xd3\x94v\xe7\xc7\x19)\x97;\xcc\x96I\x91\xed*,\xcd,\xdb\x85\x95 gQ9/U\xafx\xd0<\xc8Oz\xfa=<\xa3\xb9&\x01\x99\x91\xc0J\xc3\xf1\xa8\xdd\xf6\xac\xfa\xd3\xd2\x97?\x17\x13\x11\x7f\x1b\xdf2\xfe%MY\xbfbtS\xfc\xa9e\xc6'\x82\xa5\xa2O\x07\x16^Z0\xbf\x18\x8eA\xec\xfe\xef\xff_oPH\x9d\xfc|>z\x0f\x1f\xfe\xfa\x97\xffZ\xfc\xd2\x9f_\x9f,\x07\x7f\xfd\xcb\x7f\x85\x8f\x9fL'\x93\xfa\xd7\x9f\x9f\xe9\xb2\x9fL\xd5\x7f\xc5\x0c#[\xef\xa8T\xee\x8d\x9c\xbf\x19/\x07\xe3\xf1\xb8\xaf\x1e\xe4'\x83\xd3m\x085\xfc\xf5/\xff\xfb'\xe7\x95\xbc\x8bt0\x1e\xf7\x17i)\xdb\xffV\xcb6\x7f3^\xa4\xaa\xd2>>\xd5\xb3\x83\xff\x96\\mM?\x8an\xd5\x12\x8d\xf9\xe3\xde\xd2E\x1c }[\xa7\x08\xa7\xf3\xf1\"\xc5\xdd\xd1\xf2\xd4\xb5\xc3\xa2m\x16\x8a'}a\x0e\x02\x01\x7f\x8d`\x0e\xd3~\xe2#\x120\x85\xbc\x85N\xd6\xdb\xc8\x0e\x98^\xdb\xad\x04\xd0em\x10k\x13\x914WF\x91<\x80\xde\xf8\xceM\x9b=\x92\x1d\x91\xfb\x11Y\x8d\xc8\xdb\x11\xb9\xfd0\x82t\xab5\xbf\xab&\xc2\xb4\xd2\xc4`u.\xc5\x9a\xccFaK\xaer\x88a\xe8\xb60tx\xfct;\xdf\xea\x9c\xe4\xf2\x8al\x06\x17d;\x1e\xb7\x9c(\x99_a\x0c\xb6\n\xb9P\xae\xd2\x9b\x14\xd8_\xd9\x15<\xe8,[\xb1\x19v\xe1\x82(\xc1\xca\x03\xc2\x18\x97vAz\xe3\x13\xe3\x86\xc7\x1f\x0c.\xda\x87\xd9\xfc\xc0\xd7\x07\xb9\"'\xb4\xafPX\xefN\xc6d\xaa\x05\xc2\xd4\xeeW\xa6#rO\xaeH\xef1NL\n\xa6\x89\xa0:\xc0\xb2\x01\x1e[']\xe6\xc3\xfcT\xeb{U\xc3zDB\xf57\xe9\x06\xb5\xf9\xc1\xa0\xb4\xcdc_\xcd\x83\x9a\xcaQeJ\xc9f\xa0\xa7\xf4\xa8\x06\x89\x06z7I\xfdh\x1b0\x18\x8a{\xd5R\xa1r\x95\xb69f\x18\x8a\xbf\x1c\xe0{rM\xfao\xe7;\\j\xc5\xe3\xca\xcc\x91<\";\xb46\xc8\x89 Z\xc4\xce\xcf\x97\x15\xb6\x91\xf5\x0b\x02\x80\x9e`G\xb9\xa7K\xd0&\x7f\x0c\x10\xce\x1e\x08\xc2t\xa9X^qI\x1d^+\xae\x9fj\xca\x8f2V \xbe\xd1\xe5WW\x836\xfd\xf6\xe4\x9a\xdc\x1e\xb3\xcf1?\x18\xc5V\x1d\xb4\xeb\x97\xc4\xe9\xcc\x0e\xddQ%\x11ug\xc4\x11\x07\xbb\xed\xa7\xf7J\x9b\xce\x85\xc0j5T\x8b\x03VH\xff0\x02\xf4\xfe\xfa\x97\xff\xe2\x8a\xa0\xea\xfa\xbd',H\xd9G\xad\xfa\xa3\xee\xc1\xc0\xc0\xbc\xea\xf8\x15\xe4\xa9\xdb\xdb[\xf9\x1b\xb9\x98-N\x17\xa7N\xb9\xc9o\xd4L\x9f\xbe\xb9\\\x9c\xd2E\xfa\xe4\xe5\xa9\x91\x90\xda\xc5#Z3^7F\xe8s\x87^CX\x0b.7\x06\xab\xce&\xe82\xaa\xf9\x9c*\xe3\xc1\x8c\x9c4\xc4\xae`!\xf5[>\x8b[_\x08\xc6\x9b+\xd7\xf2\xf2\xd7Q!0g\xd3\xdd\x16\xf3Ko}\xe1\xed\x14\x92l\x99x}\x9f\xb0\xfeA\xa1\xc1\xa3)#\xbd\x8c\x07\xbd\xd9Add\xc7\xacy%\xb2\xccH4\x81\xc8dl\xfd\x9a\xddu\\\xf60\xaa\xd0\x83?\xf1\xc0\x11\xf9\xa6\xfak:w*\xfe\xe0\xc2n{6\x1c\x08\x98\xb5\xbf\xaf\xa1\xe8)\x90D\x0cjF\x18\x96\xafTB\xbf\xb0\xa3z\xa3s\x9c\xfa\xa3\x92[\x9b\xa6\x9f\xe3\x0c\xcc~j\xfcb63Sg\x8ez\xb9\xea\xb4\xe8\xf2\xf5\x11\x0b\xfc\xe8&\x9d\x11V\x1f\x12\x9a\x89X}U\xcb\xa4\x1c\x93\xda\x15L\xea\xd8\x8d\x0co:\x80*\xeee\n;\x80:|jg\x12eA\xab\xe2E\xdf\xc3i\xd8\xe3\x14,\x95\xee]\x96J\xce\xb1\xaemk\xee;\x1e|\x14\xb6+\xa0o\xb9\xffX\xe7\x1f\xb9\xdb\xa0\x1eXD\x822);\xea\x14\x04\xea\xd1\xb7\xd0\xb5\xdc\x9d\xabr\xb6 \x9f[Vw\xfa\xe6\x92\xce_.\xd2\xa5a\x0d\xdb\x01\x1a\x87\xea+\xa3\xbb\xf1xD\xfc~\x9a;\x18P\x89\xc3\xe1@\xc9\xc6\x90\x0bR\n\x9b\xaf\xbc\xad\x18k\xcc\xcbv\x01\x9e\xe8\x0e\xac\xe0\x90Q\xc9\xf9}\x85\x1b\x14.\x13(\xf4F\xa1\x7f5\xc91\xda\xee:l\xaf\xf6\xa5=e\x08\x05\xfb\x81\x82yo\x15\x06F\xbc;L\xf1\x88\x99tOo\xa3\xd7\xd0\x9a\xde\x11np\xc7\xba!\x97\xb6Y4\xbe\xcdM\xdf \xce%\x15\xec[\x05\xc6~\xbeYN2\x1e\xa0\xa6J\xdb%\x1b-\x1a|\xd4;T\xf5Y\xb5\xb4\x1e\x11\xef\x18\x12I\x1e\xa4\x0d'E\x8dx\x90\xab\xa5\x93\x8eJq\x92\x0b{\xebN\x05 \xb2\xc0C;f\x1d\x8c\x1d\xd1;m\xcc\xab\x87\xbf{9}`\xd5f&T\xfd\x99\x81\xe8p.E\xb4\x02\xf3\xa1#\xf1\xd0)\xb6\x98\xd6\xbd\xec\x91\xd3\xfb\xf0>\x15h\xe0\xd1\xd0\x8d\xc7\xdd\xe1\x0b\xd0\x92\x1eP=!\xc3|L\x0c\x91\xe8 \x0e\xa9_P8\xb4zh\x9f\x1f:\x8fG \xf2\xd1\xf3w_9\xbb\xcaJgWY\xf9\xec\xca\x1b\xd9\x834}vu\xb0\x9d\xf6m2\xee\xd5\x0eV\x82\xe7\x1e\xe3\xf1\x05pI\xadM9\xb9\xb2\x14\x9a\xe0\xadmC/\xe0Sf\xac\xd7/\x06\x8a-\xdb6:\xed\xe0\xf6:(\xe2\x88\xf89z\xc4\xfa\xe6+\x1a\xc0\xd9\xe2U\x8ew\xfa\xe4\xa4\xdc\xa1'\xe4\x0b\xcb\xc7&?\xa6\xd5\x8fg\x93\xe9\xf3\xc9\xd3Jj5\xd3\x97qr\xcf\xfd\xedN\xf4\xbd\x019?\x9b>'\xff\xcc\xd96\xe6\xf7\xe4\x7f\xa2^\xbcJ\xc9\xe5\x96\xb3\xedo\xd4?\xe3\x1f!e\xe2\xc5\xe1\xcbj5\xaf\xbeyM\xbe\xf5=\x16\xa5l=!\x85\x18\x86j\xdc\xd28\xe3\x1e\x83X\x86\x01\xe6IOC_\x8c\xf5\xcb$\xd9%\x07\xa0T\x15\xa6\xb3\xd3\xd3\xad/v\xd9JAp\xaa B\x80N\xdbF\xe1\xb4\xf4\x0e[\xd1Q\xd9\x80\xbd\xddF(\x9e\xfcI\xf8\x81q\xb0\xae\x9d\xe2W\xac\xc4\x9c\x02v\x9c_\x94v\x9fe\xc6Q*x\xe6\x89\x98\xcfH\\_\x88\x19\x0fR\xf7\xb6\xb5eG\x9b\xeff\x1d\x1f#v\xfb\x1f\xfch\x1d\xdf\xba?\x97\xb7\xda\xae\xcay\xa6\xd6.\x9b\xe9{3\xf5\x1c\xc5X\xac.'\xd0\"\x0c\xbe\xa3\x14\x9d\xf8\xe9\x97A\x9c\xa2\x13\x9ck\x18\x89WT\xec&!\xbd\xebGj\xaf2R\xd2\xfc\x0cvK#\xa2\x1d\nT\xfd\xd5\x17\x7f\xa0KC0\"\xe1\x8b{\x0b\xc51e\xf1\xeeV\xab.\x86\x98\xcb\x8bfz\xf5N\xf0\x07\xc1[\xdbP?\x0dJ\xd0\xb2OGX,\xcc\xce\x8cnV\xa5\xe9\x04\xb7F|\xb5\\\xef\xddX\x8d\xc0w\xc1mc\x8c\xa8\xb1\xfaU\xbe\xb6\nj\x0bf\x02w@\xa0,\xc8\xf3=\x94\xfb\x17\x1a\xe8\xa8\x03] s\x15\xef\x02#,=\xf74\x14\xc1\xb7j8bb\x19\x95\x93'\x1e\x0d\x02\x13%FS\xe9\xc1(\x8f\x86te\xa3! rM\x04\x99\x91\x13\xbco\n\xbe\\\xec\xe8\xa0V\x08\x8c\xc7\x05\xf1\xa3T\xd0\xc8S\x85\xe2\x89\" \xaf\xe9V\x15.\xfa\x83\x9a\xd9\xd1}m\x89R\x7f0Y\xa9\xa7>+\xfaY\xea2\x88%\xd23k\x16\x05\xcc\xcf\xa8V\x01\x86\x9c\xbc\xb6\x0e'\x83\xcd\xb1\xa3\x94 \xe0TH\x9a\xe4\xd0\x0cF\x8e\xb3\x0cw\x17^\x15i\xf8q}(\x90\xffc:Q(f{QH\x9b\x141\xbf\x99T \xcb\x85\n\xd5c3\xa9\xd5\x1c\x18r\xc2ssV\xcb\x91!\xb3~k\xce^b\xc2P\xa4\x90\xe2&.\x83#f\xe6u\x81q\x1e719\xcb=f^\xf2RvZ\xbe\x80\xdb\x11\x85\xc5\xd2<\x1f\x05\x81\x05j\xb3\xef-\xc3me\x14l_\xbf6\x17(\x88,H\x05\xcd\xfbQ\x83]Jy?\"1p\x99C\x9e\xb3H>n06}\x81j\xaa~U\xc0\x1c\x19t\xd6\xbe\x7f\xe2\xf2\xaa\xfd9\xcfPIS\xb2\xabS\xfa\xa4\xabTp\xea\x89WL\xec\xe2u\x07d\xc0\xa0f=S\xae\xd7\x05\xe1Ph\x9e\x1d\x1e\x04R\x94\xc3\"\xe2G*\x9b\x98\xech\xfa\xc7\xdb\xc8F\xa3\x8fP\x14a\xf3hI\xd0#X\x03\xfb6\xb8\xd8\x05Fv'X\xb4\xee\x08#\x80\x87\xf2\x1f\xcb\xc5\xfbf\xe4\xaan\xe7\xde7\xdc\xcc)m\x15\x1a\x16\x98\x91\x18AW]\x1b\x9b^a;\xd1\x1b\x00\x93*\xa4\x90\x0e\x13L@\xde)\x14\xd2\x81F\x90\x99R\xbe\xcd\xc01V\x83\x843(u\x01\xc2\x03\xb6\xce\x0d-\x81\x07q\x19\xe9$\xcd\x12\xc6a\x01\xe2\x0d\xe95\x0b\x98`\xe5\xae\x8c*;2\x8a\n\x84\xa8\xd3\\\x07\x81\x9f\xa4~:k\xdd\xa2\x17\x7f\xd6\xa4K\xebh^b\x90\x04\x98\x83(\x0b\x02%VD\xe4\x9a\xf4&\x93\x9e\x12~1\xbc\xa21\xf6Rl\x1f\xf4\xfcc\x12Y\xd5\xf1\x90D] \xb6V\xecvDN%\x0f\x7f\xc19\xbd/x\xe8\xd25\x0c\xf2\x8e\x18eq5r\x83\xf9\x15\x96\xa1\xdd\xeb\xb0\xceG\"\xc4\x9c\xbb\xc0\x1aU\xd2\x95m:j\xc5\x87q\xfd8\xcb1 p\xff\xe5\x8bh\xfd%MD\xc6\xd9\x11\x03s\"&\xdb ^\xd1\xc0\x11\x9e\xf1\xcfP\xed\xf7l\xcb\xee\xfeL\xc2,\x15dG\xf7\x8c\x88\x1d#\x8f\xb7\x8f\xc9&\xa0[\x92\xb2Z`F\xf3\xcbG\xac\xb23\xbc \xb8T\xc1@\x8a\x81\xcf\x00}\xb9\xb9\x80\x1f\xf1\x08\"\xe9\xad\xd9\xdd \xdf7Eh\xbf\x82\xe1(\x8c9\x94Jl\xb5\xdf\xb2\x1b\x8az#Pw}\x84\xeb\\\xc6H\xb9Wf\x99!}\xec\xe3m+W\xdc\xdc\xdb\x9d/X\x9aP\x8f\xc1\x08\xce\x08\x04dr\xec\x0f\x8a\xfa\x8e\xc3\xdb\x02\xb7\xde\xc5\x86+\x8d\x18W\xa0\x1a9#O\x90\xb2\x98\xf2\xfa\xd5\xb7\x9d\xf0\xcanw\xbb\x80V\xdc\x96\x08,\x86\xa1UE12\xa5\xf95\nb\x95\xe6\x8eiMJ\xd2\xeb\xc4\x81S&\xbe\x10\xe5\xbdb\x87\xbbkzC\xa3J\xa6\xfd\xc1\x9c-\xf30\xba]\x1a\xdd\xd6\x1b=\xba\xc5.\xed\xe8\xce\xa5]\x1a\xaa*xtK\xad\x0b\xa9\x82\x829\xfeu\x01n[\x07\xae\xcb PU\x06d\xe8\xc2\xebU)\x0c\xae\xf9\xb9G\xe4K\xc5>\xbb\x8cH\xb1U=\x92\xfd\x1e0\xdf^M\xc3I\x1a\xe4\xbb\xf5\xbass\xb9\x9a\x0d\xd5hf\"\xa0\x82\xfe`\x94\xc7^\xac\x10\x14\xd4\xaf\xe9\xb9\xd0\xdc\x0bo\x11D\xe0\xf8\x1d\xefDr\xb5\x13W\x94\x17\xef/\x98\xc4\x0b\x98\xf4l\x92\xee\xfc\x8d\xe8+\x12<&\xb8\xed\xf7QrP\xdc\x9c\"\xc1l\xe2\x88n\x1c\x9d\x189\x85\x16\x03\xcfu\xc5\x0e\xce\xc2x\xcf\xfe\xee\x07\x8f\x16oX\x95FR\x0de\xbbv\x13\\p\xe2 _\xc0\xa8\xc3\xb1\n\x8e\xb7j\xc1c\xfdtD\x1c\xd7m\xc9!\x8d\xd9G\x9d\x89m}\xc9tY1\xb5\xe6;\x93\xe4\x1dM;\xcf\xbb\x15\x8e\xd0\x9a\xa3GzdX\x9d|\xb8(\xdc+\xdc\xa5\x81LL'w\x81(e\xe2\x1b\xc3?\x8f\x80\xaa\xc6\x89\x8f\xe3\x80\xae&\x8fk\xb1\xf3\x90\x1b\x1d\\\x87\x96J:\x8f\xa2\x16\xbcE\xe5`\xb2\x83\xce\x0f\xb0\xe2\x07\xc1\x0f\xf0\x96y\xef\xb2\x87\xd1\x95 \xaa \xf5\xdcb`2\xd2{\xd9\xcb\xa3\xf8\xda\x91R+\xbdwy\x8a\x05{/{\xcb\xa3T\xc7%\xf0:\x0c\x05\x8a\xcd\x96\x0bYA\xbe\x1a\xc5\xcb\xfc\xaaC\xa7\xd7G\xfb\xc0\xcd\x97\x87\x84j\xe2G\x84\x0d\x08sk\x03\x84\x16\x98\xc9\x90<\xc6\x08\x0b\xb0\xf5\xc0\xa8`\xed\xf4<\xa7\x16\xf5\xd1+\xa5\xbcW\xa2xMou\x84\x88\xfcQD\xdf\xceS\xdc\xa5\x89\xa2\xd6\xc9\xc8\xfcm\xbe?\x8c\xb4\xda\xa3-f\x06\x14\xe5\x1d\x98\x7f<\x0d@\x14`\x85\xd3+T\xb5\xe3X\xfe\x9e\xb3M\x7f\xd0\x82 ~N\"\xa0R\xedoZ\xcf\x04\xbb\x13\xfdBm\xa8\xb7oROt\x19\xbd\x02\xcc\x1d\x05f\xb3On\x1e9bm\x87Dc\x1e\x07(\xe6g\xf9:\xc2\xf6e\x8a\xbcC\xed&\xdb\xe6\x95\x1b\x13u\xa3K1\x1b'\xabA\xd5\x190\xb6!\xb9\"\xbd\xb7\xab\x80F7\xbd\xae\xaa\x942<]P\xae$\x81[-k\xfb\x12\x85\x93\x9a\xa1\xa5\x8dC\xd2\x1b#s\x9bu\xa4\xfc5\x8c\xe9\x02\xa9Uek`\xd7\xf1k\xadF\xae*f\x89\xbb\xd5\xbc\xc0\x11\xcd\x19b\xa2uT\xf6X\xce\xa8\xb0\x15\xbb\xc3@\x1e\x93\xef\xfe\xf8\xc37\xaf\xbf\xf9\x97\xaf\xde~\xf3\x87\xaf\xbf\xf9\xc37\xaf\xffc7\n\xe6<\xd69\x82\x8c\xa9\xf2z\x8f\x0f\x1a\xfe\xd3\xfe\xf5\xac7\x7f\xd3[>\xb9\xee\xc9\xc7\xf37\x8f\x97O\xae\x1f\xcb\xf9\x9b\xc7\xbd\xab\xcb\x97\x7f^\xa4\xcb\xe1\xe0\x14\x19\xdc\xe9\xfc\xcd\"]\x9c\xf5\x1e\xbf\\\x9c^-\xee\xce\xa6\xe3\xc5\xdd\xf4\xeb\xc5\xdd\xa7_/\x87\xa7\x134\x0fQ\xb3\xdb\xbf\x9e-\x16\xe9\x93+\xf5O\x0foM\xdao\x83\xeb\xde\xa8\xe8\xcbd\xaer+Vy\xd9?\xf9\xdd\x1f\xbf|\xfd\x1f\xbf\xfbj\xa0^u\xeab\x91\x0e\xf3W1\"= \xeeQ\n\x15\xaa\xcf\x83'\x86\xdb\xe2\xbb,Tq\xd9?\x85F{\xe0o\xe6t~6\xfe\x9c\x8e\xdf}1\xfeO\xcb\xfcq\xb6|rZ\xad\xb3\x0c\x81\xb0\xad\xa8^\x9d^\x17\xda\xcb\xf9\xf7\x88\xf4\xb6~\xcfE\x0b\xd5\xa0\x7f\xb9\xa3\x9cz\x82q\x13Q\xddhZ\xfa\x8f\xa2U\x9a\\\xc8G\xbf\x9e\xbe8\xbb\x90\x8f\x02\xa1\x9e\xe1q\x8b\x8f\xe7\x17\xf2\xd1OY\x0c/O\x9f\xc1\xbf\x9f_\xd4\xaf\xdb\xab\x1f\x989tA\xd8\xd2n\xa4\xb0\xf7\xb0\xf8Q\xb2\x8c\x98//PUzb|]\x82\xf2g\xfe\xf4@nE\x10ON\xc4A7\x1bAE\x93\x1b\x8f\x88\xd0\x9a\xbaf\xab\x81\xc0\xaa\x87\x91c\xa91Ut\xe7\x8bh\x0d\x93w\xff\x87x\xcdR0'\xf6At\xd1Zv\x7fD\xa2\x81M\xec\x17h\xfeWh\xa4\xa1\xca\xf5\xb5\x8f\x81\x81\xd6\x0d\n\xab\x1b\xa4M>\x86H\xe3fJ\x89wq!@\xc9\xa1\xa9\xf0\xaa\xc3\xd12\n^\xb7Q\xf0\xdc\xa3pD'4\xed\xf4\xbbP\xe5\x06(\x8e\xc3x\xad\xdf\x8dr\xb2Y\xd1I[\xba\xdd\xbcp\xf5~]\xaf\x8f\xc8*\xd79Z\x0eA\xd0\xb1\xf3C\xd3\x01{\xf89\xef\xb02\xa29\x07/\xb2\xcd\xd3E\x0b\x92t\x01\xf3\xd4X!\xda)\x84\xcb\xdc\x99\xf2\x91\xecg\x0f\x99\xba\xbaX\xd4(m\x14V\xc2\xd1'85\xc3\x86\xe2\xb2j\x11|Adh9\xe1\xb3\x92q\xc5\xe1Ds \x0f\xad\xa8\xaa!\x83\xcc\xef\x18Q5\x1f\xfb.H\xdc8\x12\xf9\x0c\x1e\x1c\x88\x0f\x06\xd9\xe0\xd4\x87\x00l\xf1\xf2\xe3\x81\xfb\xabr\x06\x87\xb4\xa4\x1a^\x9e\x8e\xb4S\xb0I\xffz\xe6G\x82\xf1\x08\xbc\xf4\xd1@Z\xf2\xe7\xc7\x91z\x01\x92\x14\xf3T2\x95-\xe1~\xcaR\x99\xecb\x81^i\xeee\xc2\xe35fO\xe5&\xce\xa25\xd4$\xfd0\x8cW~\xe0\xb3H\xfa\xd1:S}`\xa9\x0ciD\xb7\xb0VU\xb9\x84q%tI\xc1\xbc]\x14\x07\xf1\xf6^z;\xee\xa7\"\xa4\xa9\xf4\xe20\xcc\"_\xdc\xcb\xb5\xcf\x99\x82\xe1^\xb2u\xe6a\xf5\xec\xa7\xccO\xa0\x1e?J\x85/2\xc1dH\xf9\x0d\x13~\xb4\x95i\x1cd\x08\xd1\x9eb\x81T\xae(\xdfR_=\xc4\x99\xf0\x7f\xca\x98\\\xa1\xa20\x95j\xfb\xaedf\xe9\x05\x8cF\xf8\x10\x8b\x1d<\xc4a\x92 \xc6\xe5\x9a\x85\xb1\xc7\xa9\x90k\x9f\x86q\xb4N%\xf4\xdf\xf7R\xb9\x8b\x83\xb5\x1fmS\x19\xf8\xdb\x1d\xb4\x9fP.\"Us\x12d\xe1\n \xca\x92$\x80\xber\xeaC\x13{\x16)y4\x95\xd4\xa3k\x16\xdeK\x8fr\x06\xd0\xc4aB\xa3{\xe9\xf1\x0c\x06{\x1d\x87\x007\xbbK\xe2\x94\xad\xe5\x06\x9aI\xe5&\x88\xd5X\xc9-\x0d\x02\xc6\xef\xe56\xf3\x05\xe5\x00\x8e\xbf\xa6\xf7\xf2\xc6WX\x11\xc9\x88e\xa9\xa0\\\xc67~Do\xa9\xe4\xcc\xf3\x13\x96J\xce\"A\x03\xf5w\xef\xb3\xdbT\xa6;\xff&\xddQ\x89\xce R\x009\xe6B\xa6\xf7\xa9`a*\xe9\x96E\xde\xbd\\1\x1e\xf8\x91\xf4h\xc88\x95\x1e\xa0\x85\xf4\xe2\xcd\x861\x85/\xeb8\x95\n\x05\xa2\xadd\xa9\xa0\x82I\xa6z\n\xe03.\xe4&\x13\xab8\x9074\xdb\xb0H\x06\xd9]\xc6\xefeH\xfd4\x8ed\x18G4\xdd\xc90KY\x16\xca\x88n\xe3{\x8a\xb8\xa6\xa0L\xa8\xcf\xd5\x1f\x80)\xf6|\x1a\xe0\xa8\xdeKA\x85\x88c)|\x16\xad\xa9\x1a\xe1=\x0b\xe4\xde\xa7?\xb2T\xee\xfd \xa0\xeaO\xaa\xd0f\x1f\x03d\xfb\xf8\x9en\x99\x04\xccF4P\xa3\xbfN\xa5\xb7c4\x91\x9e\xdaw\xc85\x8d<&a\xd1\xcam@S5\xb2Y\xaa\xd0,\xda\xc62\xf2\xa3\x1f)L\xb4^\x0e2\xdd\xc5j\xd4\xe2\x80r)b5\x03\"\xbe\xb9\x8f\xa5\x88\xe3 \x95\xb7j\x8d\xca\xdb\x98\xdf\xa4\x922\x1eK\xca\x13*i\xeaS\xb9b\xa9\x90+\xff\x86\xc9U\x00h\xf9\xee\x9d\x1a\xdeDzA\xb6\x92^\x1c\xabU\x19'rCy(7~\xba\x93[\x7f#\xe46\xe3\x99\xf4\xa3M,\x7f\x8cW\xa9\xbc\xf1o}y\xc3\xd9Z\x064Z\xcb\xc0\x0fc\x19\xf8\xd1\x8d\x0cY\x94I\xb5\x18e\x18\xaf\xa9\x8ch\xc8d\xa2\xf06Q_\x938\x15\xf2\xa7$\x8e$\xf7\xbd\x9d\xe4\xd9\x8e\xcb\x94\xdd\xddK\xe1'\xa9\x1a/\xa6\xfe\x89\xe5-\x8d\xb6\xf2V-\xe7[\xff\xc6\x97\xef\xe2\x88\xa9%%W\xfeZ\xae|\x05\xf0J\xad#\xe9\xb1Xa\xb0Z\xaar\x1b\xef\xa5\x1f y\xe3\x872\xf4\x03\x191!\xe3(\x901\xdf\xaa\xe5/\x93l%\x15\xc0\x82\x052\x8bby\xcb\xd6\xf2\xee\xeeN\xde\xdd\xbf\x93\xd4\x93t-)\x93t#\xe9VR_\xd2@\xd2P\xd2H\xd2X\xd2\x9f$\xe5\x92\xa6\x92\nI3Io%\xbd\x93\xf4\x9d\\Q\xb9Z\xc9\xd5Z\xae\x98\\m\xe4j+W;\xb9\xf2\xe5\xeaG\xb9\n\xe5*\x92\xabX\xae\xb8\\\xa5r%\xe4j/W\xb7ru/W\n|\xe9y\xd2[Ko#\xbd\xad\xf4v\xd2\xf3\xa5w#\xbd@z\xa1\xf4\x14)\x94\x1e\x97^&\xbd\xbd\xf4n\xa5w'\xbd{\xe9\xbd\x93k&\xd7?\xca\xf5\x8d\\\x87r\x1d\xcb\xf5;\xc9<\xc9\x98d[\xc9\xb8d\xa9dB\xb2Ln|\xb9\xf9Qnn\xe4&\x94\x9bXn\xb8\xdcR\xb9]\xc9\xedZn\x99\xdcn\xe4v+\xb7jb\xe56\x90\xdbPn#\xb9M\xe4\xf6'\xb9\xe5r\x9b\xca\xad\x9an\xb9\xbd\x95\xdb{\xb9\xbb\x91\xbbP\xee\"\xb9\xe3r'\xe4.\x93\xfeZ\xfaL\xfa\x81\xf4C\xe9G\xd2\x8f\xa5\xff\x93\xf4\xb9\xf4S\xe9\x0b\xf9#\x93?\x86\xf2\xc7X\xfe\x98\xc8\x1b&o\xb6\xf2f'o|y\x13\xca\x9bH\xde$\xf2\x86\xcb\x9b[ys/o\xde\xc9\x80\xca`%\x03O\x06\xbe\x0cnd\xc0e\x90\xca@\xc8 \x93\xc1^\x06j\xa9\xca\xd0\x93\xe1Z\x86L\x86[\x19\xeedx#\xc3@\x86\xa1\x0c\xd5\n\x96a\"\xc3\x9fd\xc8e\x98\xcaP\xc80\x93\xe1^\x86\xb72\xbc\x93\xe1\xbd\x0c\xdf\xc9\x88\xca\xc8\x93\x11\x93\xd1FF[\x19\xf92\nd\x14\xcb(\x91\x11\x97Q&\xa3w2\x0eeBe\xc2d\xb2\x91\xc9V&;\x99\xdc\xc8$\x90I(\x93H&\\&\xa9L\x84Lner/\x7fR4M\xf2X\xf2T\xf2L\xf2[\x99R\x99\xaed\xea\xc9t-S&\xd3\xadLw2\xf5e\xfa\xa3Lod\x1a\xc84\x94i$\xd3X\xa6\\\xa6B\xa6\x99L\xf72\xbd\x93\xe9\xbdL\xdfI\xe1I\xb1\x96b#\xc5V\x8a\x9d\x14?Jq#E E(E$E,E\"\x05\x97BH\xb1\x97\xe2V\x8aw2\xa32\xdb\xca\xecFf\xa9\xcc\xeee\xf6N\xee\xa9\xdc{r\xcf\xe4~+\xf7\xbe\xdcGr\x9f\xc9\xdb\x8d\xbcM\xe5=\x93\xf7B\xbe\xa3\xf2](\xdf\xdd\x0e\x16\xab\xd3\xaa\xe6\xb47\"\xe8\xffoq\xbb\x1c\xfc\xa6\xbf\xb8\xfdy:\x9a>\x7f?0\xba\xcc\xb2:\x14r_\xcf\xe6\x8b\xf1\xc5\xec\xd1\xd5b\xb8\xf8d\xb4\xb8]L\x96\xc3\xdf\x14\nD\xf6\x897Ub4\xa3\xb6B\x94\x19\x96\xf3\xf1dh\xc5\x87\xe5p\xd6\xbf>i\xfa\xb48]\x9c\x0e\xfa\xd7'\x8b\xf5pqz=\xe8_c\xca\xb5\x13\x90\xbaJ\xb7?\xb9>E\xa5\xaej\xff\xf6\xf6v19\xbadsG\xad\xf6\x17\xd4\xc5\x8b\xb1\x05|\xf8\xe87\xbf^\x9c\xfe\xd3\xd5\x7f~\xdb\x1f\xc8\xc7\x9f\x80@Tg\xe1O\xbc\x0du\xc8\x11\xb3@\x8c\x0f\xaf\x03y\x12=\x1a\x7f\xe2\x81&-''Y\xb7\"\xdf\xb3\x80\n\x7f\xcfl\xb9\xcd\x81S\xc8\xa3/\xfa\x117\x99$\x87NX\x9a\x87\xd0\xd2\xf7\x19I\x9a\xa1\xb54\x7fF\x1cZc\xf3\x0b\xb1\xdf\x0d\xc1~\xba\x10\xf7vj\xd4E\x08\x81\xdb\xe4\x03\xe3bX!\xf9\x17\xa2_\"W\x87\xf8\xb4\x00$\xc6\x95r\xba\xe8\x9fn\x0f\xdc\xb7\x8fJ\xf9\x07\xa7\xdb\x03<\x1b\xb9\x80\x0d\x0e#%9\x1b\x90K\xd2\x07\xf2\x14\x95\x92-!?9\xeb8\xa6$\x9fs\x87w8\x976\xf2UU0\xeb\xaa\x84\xf4#pK\xd5(X\xce\x17\xb7\xcb\x06\xc1rG\xd3\xaf\xb3 \xc8\x8b\x9a\"-\x12\xbf\xa3\x9a\x8c\xfb?x;\x16\xb2\x83\x15\xb8a\xf8\x0f1_\x7f\xa90d#\x18\xaf\x023\x9b\xbfY\xa4\xcb'\xd7\xa6JG\x15E\xe6\xdb]\x1e5\xd3S\x94\x06tM\x7f2\x1dR\xec\xca\xdcb\xc94!\xfa]\xcc\xd2?\xc4\xe2\xf7to)\xf6\x1f\xf9\xefb\xa1\xad\xd3Z\xb2\x7f!\xbee4\x15\x7f\x8c\x98\xe9q\xa5\x8c\x9f~S\x9b\xcc\x9c\x92\xf5]\xe7\xf1\xce\x13\x89r'\xba,\xd7\xea\x82\xd3](\xce\xeb`~\xb6,\x1f\xac\xb6J\xf1\xbd\x1f\xe9\x9e\xa6\x1e\xf7\x131Cg=0\xce\xbd\xfd\xaa\x9c\xd8\xa5G\x87\x86\xbe\xa3\x89\xa0\x9d\xf1\x13\x86\x8e\xe7\xd5\xfa\x07\xfb\x00\xc7:@\x9fw89c\x13A\xdb\x1avO\\\xded\xbbA^\xc7\x82\x87\x81\x7f\x827&NL\x0f\x9aWQ\xcdW\xac\xf99\x91\xa7\x0d\x05\xbb\xa0\x92\x01\xf3\x84\xd9\xf1m#Q\xcd\xc09\x88$\n#P\xf8\x08\n\xf9Q\xf6\xcf]\x06\xef\x01\xc7\xbc\xaf\x8abS\xd7C\xae\xc2\xbe\x18Jv\x84-7\xf5=\x06\xc2\xa2\xc1\xa6\xb3T\xe3<\xc1\x8e\xc3q\xf6W\x98\xc5\x8fs\xe6\x87\x1ej;\x8e\xc2W\xb8\x7f\xe9Zy\xbe\x1f\xecX\x7fq\x94\xbb6R\xf4g\xfb\xc0\x06\x1f\x80A\x0d\x8d4\xce\xa7\xde\x8a\xfd-fT\xef\xd5\xba\xce\xe9\xeb\xf2\xd6\xaek3E\x0d\x00\x96\xed\xd8\xde\x83\xe6\xd88N\xd3\x0d\x82\xe74;\xe1\x0f\x87\xe2\xb8\x89\xef\xfd\xa6k\x93\x8dh\xf0'\xfe\x80E\x9d\xf1\x00\xf7S\xb9\xc2\x13\xc6\xc3(\x8d\xfb\xa8\x00\xbe>uY\xc3VX\x91\xad\xa2A\x1e5\xf9\xbf\xe3,a\xd1\x9a\xad?\x96\xedI\xc6;S\x99?\xf1.4\xa6tO'\xe3\x0dJ\xa2\"\xb6:\xf7\xb8V\x80\xacn\x9ak\x1f\xec\x90\x94}\xc3d0\xa5=\xed+\x10\xcc\xbdGM\x05!\xf4}G\xaf \x0f\\*\xd0\xb2qv\x9e\xfb\xf4~D\xc3\xe4\x02\xe21=\xeav\xcd\xea\xd85R\xbd6\x05\xed?tN\x8c\xbe\xae\xa8P(\xe7\xc3\x05\xd1\x07\xe7XU\xb5\x83\xa3\xf8\x9f\xcc\x12\xc2\x12\xf6#^`}\xcd\xa9\x1f\xf8\xd1\xf6\x87\x80B\xcc\xf6.\xe3S\xae\xb6\x8bl\xe4V\xd1\x97\x17\xb7\xdb\xe1zS\xf3\xeeAy8,Nb\xd1\x19$\xc7X\x1e\x01J\xef\xb4M\xe1Q\xd4\xe0\x1a\x87\xab\xe3i'/F\x8a\xfa\xda\x94\xf7#\xedh\x11c$\xf16?\xa5\x1a\xb0x\x92\xfb\xe5\x84\xbb\xc0\xf9`\xbc7\xbeeFd\xbe\xc4(>\xfd\xa2\xdbx\x1d\x8a\xeaC\xa3a\x1b\x8c\xc8<\x0fa\xde\x1b\x91\x1e\x04\xa4\x86\xf02\xea-\xf0S\xd1s\x85(\x9d\x973Bm\x9f\x7f@m;\xaek9?\xfb\x80Z\xe0\x93\xaeg\xdaZ\x8f\xbb\xbc \xcbm\xea8\xaf\xd4\xd1\x00;\xa3k?\xda\x9aBO\x1f\xd0pP\xa9\xe3\x99{\xf6v\"\x0c\xa0.\x93\xef\xf9\x03\xda\x12t\x15\xd8\x1e~\xda\xa9\x87k\xb6)\x0em\x15m\xdc\x85\x8aPA\xb1\xcf+\x81\x0d\x97\xee\x98x\xd5\x05\x8a\x14<\x0b\xacW\xb6\x8a\xcb){\xdd\x81\xa1\x1b\x1bF.\x89o\xaf)\xb0\xe1pP\xa8BG\x92\x9f\xb3%\xc4\xe7\x82\x87\xe9\xd2%\x8e\xd1@\xcc\x08\xe6<\x87\xf3\x85\xf9r\xa0\xa9\xd2\xa0BzrJa\x9fh\xc1\xad\x11\x04\x82\xf0\xdf\xb1\xaa\x835\x87\xe6\xcd\xf6E{\xfb-\x00\xbee\xe2\xfb,`)\x1e\xa3\xa3\xa3\x04\xec$\xbaH\x10\xe8\x10\xe1dzA(\xb9\xd4GHl\x12\xf8\x91j\x98\"Q\xbd\xf1\x93\xaf\xc2D\xdc\x7f\xebG,\xedS\x08m@\xc9\xcb+\x12\xa1\x17\xfe\x93>\x9b\x88\x1fv\xfeF\xcc\xe9\x12\xae\xdb\xac\x82\x9bo\xa25\x8b\x84\xfb\xfa\x13\x00\xccq\xe0\xe1F\x08\xd4\x12\xcf\xf9Ru\x91\xc2\xf1\xe6\xc9tpA\xf8p\xe8\x90\x130\xea\x85\xf0\xb7;\xa1`\xcfF\x84M\xfc\x14@4\xb0[\xbe\x90\x19\xb9\xaa\x8f\x9dQ_\x07\xa6\xa7y1\xda\xa86W\x8da%#2\x1c\xdaAB\xaa\xa1\xb9RB9\x8b@\xe8\xad\xd7\xda\x12\x0e&\x1f\xe7\xda\xe7\n\x9f\xcaq\xa5\xcc\x0420S]D\x0bQ\x8b%\x99\x82q*W\x1f\xb3\xb3\xb3\xcf\x9e/\xe5|\x91\x9d?;\x7f\xb6\xc8\xce\xcf\xce?\xd3\x89\xd5R\x01\x94\xca\xce\xce\xe8\xd9i!,X\x111\xe1\x8e\x91\x03+G\x84W\xc7P\x81\xe8#\xa2\xb9<)\x03\x02\x94\x92\xe1>>\xb3\xc7\x02\xd5\x9b\xf3\xc0\xe55\xab7\xc2I0\x02'\x10\xb98\x9b\x8eHo\x11\xa9\x14\xabU\\\x88\xde \x8f^W.\x9f\x15\x18p\x93Z\x1b\xd6V}\x0e5\x94\xd3\xb3\x82p\xf2e\xbcf_\x88~4 \xd7:,,F\xf9\xf3t<\x14\x08\xfe\xa6P\xbf\xa7j\xe8i\xda\x00\xee\x85)\x19\x13o@\xfe\x89<3\xc7\xb5\x90\x08\xc5y\x95z\xe8\xd5\x8c>\x15\x99\xf1\x07k\xe6\xc1\xdc\xab\xd54\xa4\xef\x8f\x14q\xf3#f\xfe\xbe\xa2w\x05\x024*\x05\xb4Al\x1fz\x1epZ\x86U?@e\x18kM\x9a\xeb\xae\xae\x96\xab\xdf\x8a\x00\x9c\x0dj\xa8X\xac;\xdf7\xfd\xaa\x0e\x08/\xbaUD\x1e\xd6\x1a<\xa0\xb8Y\xc7\xfa\xe7li\xd5`(\x11\xb0\xa5\xa2\xbc\x85.\x14=\x9f\xbd\x1f\x95\xda,K\x1a\xadM\xd7]\xda\xeb\xfe\xa2(\x87g\x8f\xfdC\x90]V\x00\x1b\xa0\xe8w\xe1\xea%k\x83\xfa\x87\x84zGC\x9cr/\x978\x0d\xd0z\x15\xd9\x0c\x85%\xc8\x1e\x0c\xde\x97;\xca\xd3C\xaezKn1\x9d\x00F\xf6\xe4\xa9\x06\x19\x02\xfdA\xf0\xfd\x96z5w\xc2\x0e\x86\x0c\xd2\x1f\xb9\x04\x97\xf8\xa6n\x07\xdfP\x10\xbf$\x91#b/Z\xaa\x9d4\x0c\xf2x\xccr\xbb\x04\xa6\x96\xedq\xdd\xd92Q\xc7\xdeV \xa9j\x19\xa98]],b\xb0\x8c\x1a=\x14\xa9,\x81\x82\xb6\xe2\x92\xd4/\xaf\xffy\xa0V\x01F5\xf0\xf1\x10\xce,\x87`9\x02\xb7\xad\x8acpr]Z\x19Pjj\x1c\xc1\xdb\xc4Q>\x82(\xc7\xa8~\x0c\x1c\x93\x91iQ\x05|\xb7\xf6\x05\x19\x83\xe1\xac\xf6 \x1a(\xd4\xbf \x81\xa2\xbc\xf1p8\x80\x88ne\xc8\x06j*Ax\x03&?\x18\x01\x07;\xb3)gZ\x1c\xaa\xf54\xc5\xfe\xe0\xc8\xa8\x15&e\xf7\xcee\xf3xY\\\n\x8d}\xd4c\x9d\xd5}UUD+\xb4\x8d;J\xb42\xa9\xee\x90\x83\xee%b\xf6\x82\x0e,2c*\x96j\x12\n\"\xcd%y\x96\x9b\xe3L\x1ds\x18\x03^\\\x81\x8f\x9a)\xee\xdb\x9aVW\xbe\x03\xe2j-\xb9x~\x8b\xdd\x1fl\x02rHy\x15\xd2\x97W\xe4Y\xfb\xc6J\x81:\x1c\x1er\x06k\xf5\x9cZ\x86\xe3\xa3<\xf6{C\x8c*\x1d\x8b\nUf\xb5\xaf6\xe6TN\x05\xd4\x96\"\x1e\x91g\xe0\xe8\xc5va\x04[\xd2ZyP\xc2\xb8\xaf'*\x10\xd3\x19\x99\x8b\x91\x86\xd7\xa1<\xd1\xe1\xab\x18\xca\x8c\xa5\xcf\xef\x95\xf0\x96\x8bI\xef\x7f\x194\xecN\xdf\\\xc7F\xe8|C/^\xb1\x84\x11\xb3\xc8Z\xcf\xbe\x81\xec\xccd\xaf\xa3\xbaG\x86\xe4)yI6\x8dh\xadrM\xcf_\xa0\xd7\x96\x18u\x1def\xe0\xa1\x82\xe3s\xcc\x13\xb7\xd6\x04\x92\xf7\x08%\xe7\xbeg5'\xc0\xda\xfa\x9e\xda\x03\x0d\xc8\x98\xa4\x03rI\x9e\xb6V\xa45\x159\xc5\x01C\xf9\x89\xe0~\xd8/\xeej\xff\xac7\xb5\xad\x95\xf1\x82\x8d]\x03a\x16\x17\xe4\xa4?\x1cf\xa8\xd1A\xc1 :\x90\x16g$+\xcdH\xb6\x04\x9b\xbe\xd2$\xa84P\x7f\xd8<5]P\x03\xb5\xa8\x8d:0\xb1\xb8\xa2[\xca\\\x84\x00\x04\xf8\xe6\xd1\x06\xe5R9\x0b\x8aj0\xb5\x10\xb0\xbe\x81\n\x01\x9a\x9e\xb9\xe9\x0b\x90\x9en\xd4\xc5\x87vs<\xce\xc9MF\x86\x8ae_\x03\xeb\x81\x93\xbfn\xc4\x07\x94\xf1\x0e\xea\x93PN\xc3tFhG\xc2\x84\x8a\x85\x0c\x16\xa7\x93\x1c\xfd{\xa29\xf5\xb0\xbb\xc7Q\x9b\xf0\x10\xb5\xd9\x93\x97$l]\x89/\xce\xb5\xb1[\x05\xdb\xf7\xc3\xe1\xa0\xb5\xa0\x1e\\\x85\xeey\xac\xdf\x90\xde\xfd\x81\xa5\xc2\x8f\xb6\x1f\xb2\xfc\xf5f\xa3\x0e\x13\xac\xe4\xbd\x92\xc84\x11\xc8Y\x17\xab\xeaA \xeaaa,\x01\xc9\xf3\x91\xbd\"{\x14\xce X\xed\x9e\\\x92\x10\xc2\x11\x15\xd6\xe2~@fd\x0f\xd4,D\x81m^\x98\x0d\xa8/\x17[T\x1d\xe3b\x0b#\xcd\x0bP-TS|\x17\x8e6\x8cO)\x94`b\xb3\xa39\xe9\xf7K\xe8\x10\x97\xd0!^\x02`\xfd\x12\n\xc4\xcb\xc1\x00\x03\xa09IZ\xfb\\7\x8b=~\xabXc\x03+\x9fLGpW\xe7\x0c\xaf\xa6l\xec&-!\x97d}A\x92C\xb1\x0b6\xf3d\xa9/eE\xb0\xfa\xdbt6\x04\xaeA4SC\xf3sSE\xf3k\xf6\xd0\xb5k\xedtf\\\xfd\xdb\xc9Q{\x14\x93\x98\xcf\xd1\xa88c\xa0A{\xfa\xf4\xd3:\x8dF\xc1\xb3\x03\xde;\xdb-\xa2\xc8\xf1x}\x18\xe8\x12f\xc7K\xc7\x8a\x0dH\xf9\xc0aT>~\xb8\xaa\x9c{v\xe4)y\x99\xa6\xa0\xc1\x9a\x19@\x84g1\".wue^P \xed\xfb~0\xca\x97\xa8\xd5K#\x11\x8f\xbb3\xbf\x02\xa0M\xf1om\x9c\xdb&\xa6T\x190\xc5\x1b\xe6\xd3\xa5=\x1d\xd2K\x0b\x17\x13\xcd\x97\x16F\xac\xd6s\x93\x90!\x01Z\x94\xcd\x93\"}\xb2\xe9t\x9e,\xdd\x8a\x83\x12\xf9L\xff.xd\x99\x17:\x0cJ\x0eq\xbf~F\x86%9Gm\xd8\xd3V\xce\xf4\xec\xbcE\xee\xce\x80N>zD\x9e=G\xc9\x1b\xa4\xf0\xe7\x07\xa4pX jEN/HF.I\xea<|\xac\x88\xd8\xb5Vm{O\x11B\xda\xd8\x1e\x01\xbfrVT\xf5\xab(\xef\x9a\xfe\x93\xbe\x8f\x1b\x80G\x8fH\xff\xe4\x84k\xbb\x10-\x13j\xa1\xac\xe3b\xd8\xf1\xe6\x85\xfaaR\xdb\xa0z:}\x14N\xda\xe4\xcai\x90\x0b \xf5\xf9\x90s\xa9\xf4y\x9b\x90\x86\\9.\xa3\xe6\x80\\\x93\xb1\x12\xa8\x0dzE\xae\x89\xe6\x15\xf4\x02)\xe0\xd9S\xfd\xack\xe0\xe4\xb2\x84\x07\xf5Zlc\xbc0Z\xf5\xce\xc7\xad\x9d?N\x0e\x8d\x0f\xadD\xf0\x83\xa8F&_&c\xd7\x1e\xb3e\\.\xc9\xb3\xcf\x14ZF\xe4%y\xfeic5\xa8em\\b\xbc\x1d\x08b\x15=m\xa0\xa8\x1d\xdegj\x0e\"ry\xa5\x80i\x13\x9e\x9e\xa1\xee3R\xb0?{a\xa2\xa6\xb6\x88\x16\x16\xb4\xda\xd7\xa6\xe3\xf7B\xa9\x07\xa2\x87yj\xa7\xd7\xb534p\x87\xd9\xb2\x9b\x19)\x01c;\"\xf7#\xb2\x1a\x91\xb7#r;\"_\x8d\xc8\xdd\x88\xfc0\"_\x8e\xc8\xcd\x88|\xe1\x10\xe1\x00\x15\x94\x08\xa9q\xd4(\x14\xb6\x8e\xbc\x0d\x1a;=\x89\xaa\x12^\xaa\xa4\x95lB\x03\xd3\x96Q\xfe\xd0\x8dO\xe8B\xaa\xb5\xbe\xcf\xed\xb7\xef\x8aV\xb8gG\x12l\xace\xb6\xe4\x1a\xef\x017\xafV\xd8T\xa2\xffj\xad\xd4\xd07\xca\xd5<\x911I\xf0~fg\xfa\x1e\xf35\xe3l\xfd6\xf0S\xd1$\x97A\x9e\x19\xd972\x82\xdb\x87KlJz\xed\x08\xea*\x0b\x02&Z!\xfdpx\xac\xc9\xd2[\xbd\x07\xbak\xdb\xf7\x81\x81\xce\xe0\x82\x9c\xf4O\xfa`\xb6\x836\x98\xb0\x81\xea\xdfW\xd5AkD[K[\xe9Rkf\xee\xc9\x98\xac\x958\xf3\x0cX\xb6*\xadPhG.\xc9\xb4\x94\xa2\xa4\xa8uQ~\xa7\n?v\x9dg\x1b\xc6\xce\x17,<0\x80_}\xc8\x00\x06\xd5\xdd<\xea\xc5\xc0H\xc1\xec\xf5\x0b\x08\xbdq\xec6\x8a;\xf1\xfb\xeaN\xbc,\xdd\x82e\x965\x808\xab\xefU\xb4}`\xd3\xc6\x00\xf7\xa6y%j\xaf\xfe\x16f\x11\x88\x99\x1a\xf5\xb7Vn'c\"\xc8K\x9c\x14\xa7=X\x15\xba\xa0\xda\x9b\xb4\x08\xaeW\x83v\xf3\x80\xa9|\xf0&\x050\xbd\xb0'\xf9\n\xb7(tD\xee+\xd2:\xd1\xa6xj\\\x8a\xa6g\xf8~\xbc]\xde\x8d^\\?\xa0\x82\xe1KrE\xee\xec.\xe8\x07rI\xbe\xbc ?4)\x18\x14\xe9\xbd\x9b\xffP\xb4\xe3kW.\xdc\x1cP,4+\x15\xea\n\x05\xd5\xf8M#\xc7W_\xb7m\xf2C\xce\x08)HAg\x83&Eo\xeev#\xe7{\xe52\xee\xe6C\xb7\xa4\xb0\xd6\xf7\xf6\xeb\xad5\x1cXuAB\xc5\xaf\xca\x1c\x04q\x91T\xa8\xf5\x831\xf4\xd6bdn\xc7\xa8\xa4\x8cG\x8f\xda\xcd\x0cHY\xf2G\x1c\x07>?$\xe7\xf5q\x03\x9c\x8c\xf4\xde\xe8\xdc\x08\xcc%\xe6L\xc6\xe4\xbc\x14\xb7\xd3f\x98GKcAevi\xb9\x851\xd2Y\xad\x08\xca\xf3\x0bm\xc6\xd9\xcf\x13U\xcb\xcb\n!+\x14(\xa4G\xe8\xd8\xbc1k\x97\x82\xa1\x7fO\x9b\x8bv$\x08\x99\xb6g\x1b\x92sT+\xf43\xb3\x0b\xf4\x14\x17x\xfe\x99{\x08\x87\xc3lPVDd\xc3\xa1\xc2m\x16\xed'\xe6VCjn\xae\x94\xd2 \\c-\xeb\x84\xb3\x8d3?~\xd0\x85R+\x9a\xe3\xf1f\x80\x0b;S\xcb\xb8\xa1\xcey\x0f\xae\xf0\xa6Km\x1a\xd9\x8d\x04\xda\x9b\x19o9\xdb0\xce\"\xafY\xbdIW\x8a\xda9\xe2\xe1\x1f\x14\xa9\xe2*?\xae\x1d\xf9\xd1\x03RTI\x10\xcd\x06d\x8c\x82S\xf1\x08%+\x0b/\xc3+\xf2\xac.M\x15.\xa2\x14\x1b(1~C\xd9\xec\xd7\xe1U\xedx\xc7\xb6;.}k\xd1\xe0\xe6\x82Z \"Z\x86z\xac\xa1.\xf6\xdd\xaf\xf64\xfe\x90\xd9}03SR\xca\x07\xe9\xbcL\xea\x07Q\xe7\xe3\xe8\xf2A\xad,\x9c\xe8\xb7ka\x9f>o\xd3\xc2\xe2\xb5\xb5\x03\xd5\xe4ZW\xb3\x16\x1cd\xe6\x82<}\x9e\xf3`P\xce\x82\xca\x94\\^\x91\x17\x17\x03\xe2\x83\xf1Wci\x17\xd5;\xe9\xfb\xe4%y\x81\x10\xea\xfa\xb4.&.S\xb5\xd4\xae1kg\xd8OG\xe4\xa9\":\xf9\xcd\x90\xfa\xf7\xe7\xea\xbb\xda\xfae$7\xcc\xac\x01H\xf3\xcb&`=?(\x08DG\xeas\xf1:W\x13\x8d\xda}\x8bX\xec\xb8\xc9\xfd\x11\x94\xbev\x0c;\x02\xebG\xaa\x9dv+\xa8\x9c\xc6CH\x1fm\xc2r\x084\x18\xb3\x07u\xd1\xdb\xf9\xc1\x1a\x1ci\xcd\x97\xb5\x0ev\xec\x97\x99\x84&R\xd26\x0b\xbf\xacZ\xdd\xa4>\xc4\x12pd\xee\xe1\x88F\x8bV{\xa7K\xcb\x10\xcd{GG\x86\x8aa\x8e=\xe0\xe8\xf7K\xec\x91\x96\x88\x1a\xd5:|\xbfH\xc8\xe8R\xcb$\xfdg\xcf\xf3\x8b\xb8\xb5U\x17#mz\x81:_\x8eE\xe2\xf2B\xee\xc7x\x17\xc6BQ`\xb31l\xd7\xfcb\xb9F\xb5^\xe1>\xdc/\xb0\x9cM\x17\xb4\xbe\xe9\xfca\xa8\x7f\x00\xf7:\x82|\xdc\xa2\x06V\x9d\x1f\xbd|\xdc\xe5\xad\xa8\xea\xbf\xf2\x12\xef03\x87W\xfc\xe0# \x16\x85;\xdfg\xe7\xd5\xbb\xdd\n\x81O\xdf\\\xf6\xe7:x\x9fvu=_\xa4\x8b\xd3\x97U\xd7n>f^\x9c:\xb2\xbf\\\x9ev#4#B]\xb4&?\xa0\xa8H\xc5\xb5\xa1\xab\xd8o\xd63$e1\xba.\xbbxJvMF\xe4$\xdf\xdc\xedD\x18\xb4\xca;\x89\xa2M\x8apx\xb0[zyu\xc0<\xf4\xc5\x99{\xeb\xe4\xb5\xef<\x9f\xe2\xa6\xae\x9f\xb9H\x97\xa7w\xae\x8a|a\xbe\xaci_Y8{._rz\xdfv\x1c\xf3\xecS\x00\x1a\xa4\x96\x93\x96\x1b)\xe6g.\xa5<='\xb2z\xf5\xc0\xfc4\x18`t\xf9\xf9\xa7\xaaf\xa1d\xb7\xe9\xf9y-\xfb\xfb.\xdb\xdeg\x9f6\xf7\x9c\xd8c\xa5\xeaV\x11-a\xd1\x95\x9e?(\xb6R\x87\"W\xd2\xb5\xd7\x13\x0f\x0eC{\x82h\xc0\xe7\xe9|Zq\xd6\xb7o\x0b\xd5m\xfcm\xc6\xa1U\xb5\xb3e\x1c\x9fx\xa8\xfe\xee\xa6\xf0\xef9\xfc\xfb\x14\xfe}\x06\xff>\x87\x7f_\xc0\xbf\x8c\xae\xb1\xd4\xce\xc2\x03\x1e2z\xfe\x86\xd3P\xbb\xc1P\xff\x86\x14>\xc6\xe0\xd9\x0f\x9e\x00\xd28\x13I\x06\xef\xf09A`\x12\x1eo9K\xa1\xf3\xe8b\x12\x9e\x98g\xe0N\xc5=\x8e\xa6\xf1\x11\xd1\x13f\xd8\x04tY\xb0;A9\xa3\xf0\xbc\xc1\x0b\xaf=\x01~'\x04\xc7gF!g\x06p\xec\xfd5\x8b{\xcb\xc9&\xe6_Qo\xd7o\xb9\x808g\xcb\xf2\x0dP\xad\x95\xfa\x90\x1b76\xb9\x8b\xf9\x8aCr\xcc\x95)\xb5u\xc0\xdb\xb6\xecv\xf9\x16N\x8e\xc1BdL\"\x97\xb7\x88v\xf6\xdc\xf5\xcau\xd1\x8a\xa0\xce\xc8\x04\xb2\xc9\xc2];\x17\xbb\x0bJ[]\xe4\xd8Am\xd7\xd0RA\xbf\xa4\xfa\x08J\x12x\xb0,\x9f\xcc\x06\xcd\x14\xd7\x87\x0b\x1d\xa80\xd6\xbb\n\x87J#\xb7\xfb\x81\x1b\xbfZ;\xea\xb7\xd6J\xady\x030\xef\x1199}3\x1f\xcf$Y\x0e?9EW\x9b\xb4]$\x80\x1b\x08\x14C\xa9\xf6{\xb2\xa7\xf6\x1f\x10\x03\xb5M\xad\x92\xe8\xeb\xe7)Z$\xa6\xe4\x92\xe472[no\x9f\xc0\xb9\x947O\x97\xe6\xdaH\x1b\x9fE\xff\x05\xa0\xb8M\xe1\xd1+\xb9W2\xd7\xb2[\x05\x83\x83\xde\x98\x89\x01\xed\xf4\xcd\xecz<\x9c]\x9bq[\xb7\xb3\xdf\xe7\x9f\x01H\xeb\xd2\x81Y \xbek\x92 {se=S\xdf{\x18b\x0b\xce\xbe\xb8\xbf\xdd\x89\xde\x80\xcc\x9c5\x9f\x15\xaa\xeb\x05l\x839MB\xaf\xed\x06\xb7\xea\xdc\x18w\x0c\x05tq\xdc\xdb\x81\xb9o\xc1\x14D\x14\xeb\x9d\xed\xcdB\xca\x85\xfc\x04\xfc\xb3\xf5\x06\x05\x04\x1a\x91\xc4\x8c\xc3Ia\xd2Z\xeb\x8e\xdb-_:\x8a\x0b@\xe8\x0f\x98)\xec>\xc4L\xa1+\x1c\x8ao\x1c\x80C\xc1\x00\x8b\xf6\x97\x84\x83\xff\x92@4/\xfe\xae\xe0\xed\x9a\xc0\xa3\x81\xbf\x8df$\x99\xa7.\xc0>\x02\xec\x1d!<\xacw(\xd0\xb2\x8f\x00\xe9/\xa3W\x10\xbb\x87\x1e@|\xc0R\xe4\x0fm\xf3\x88n\xa9U\xf6\x8b\xb7\xa2d\xc6\x03\xcbh\x0f4\x05\x8f\x0b\x1fDW\x8c\xa0r\x8e\xdb+}\xfb\xa7Efy\xf4\xc88)\xcfiz\xe0\xa6\xe9p\x83\xbd\xd1\xaa\xa6;Q?4^\xa4\x0b\xdd!\x87F\x83|0q!\x058\x1a\x8909DdHW@7F\xa0\xc9\xc3\xf3+Q\x0f\xc4\x15\x95\\e\xe2p\xabrD\x9a\xf2\xc0{Y\x8a\xa8$\x91Y1\xc5j7\x8f\x19\x97F\xb2F\x8a\xa4\xad!\x8a\xca!\x8aE\xda\xa8\x16\xe9\xb8\xf8Hi\x12\x9b\xd689\xb4\xce\x89\x83\x8a\x11\xd8\xa2to\xbe\x99\x90\x91n\xcd\x97W{\xe9\xcdn\xad\x8e E\xbf8\xc1\x03!\xea\xc1\xad\xec\xd0\xfcj\x8f\x7f\x82QI\xed\xf3a\xea\x13\x9b\xdce\x03\\\xb0\xe2\xea|r\xedw\xd8\x06\xc7j\xd3\xe7\x1b\x13z{M\xdf}\x18d\xees\xe8\xbd\x1c7\xc5b\x14\xc7#\xd7\xe9\x8f\xce\x12\x95\xda\x89*\xe3F~\x91}\xb6\xb5\xd6o\x15\xd0\xfb,\xf7\x08\x06\x96\x85\x8f\x1e\xd9\x89x\xe9t\x9d\xb7)\xee\xc3\x8d\xaep\x03\x05\x87\xc3\xcd\xc1m\xbc\x9d\xb3\xcdQ{w\xdf0\xc6\x8d1\x81lm\x03\xd0\xf9h\x9b,m\xa7\\4\xfb\xeb\xbc\xd2\xd6\xc1\x01\xb9\"\xf8\x90\xbdJ\x866\xe9J<\xa8\xf8\xafc\xb3\xb6K2\xf0\xe9^\xdb\x0dn\xb5\xd1\xed\xa1\x1e\x91B\xaf\x1a-\xedIA$\xceF$\xfb\x10\xb6{\x04@\xdd\xb8]A\x03\xac`3\xd8Z\xf4\x8d2m>J$\x1d\x8f\x13I\xb7!\xf8\x98\xfcs\xddlKK\x0e\x11t\x82\xfc\xd3\x89'$_\x9d\x07A!\x05pZe2\x92\x8f\x8f\"k\xf3\x8d\x1b\xf9m\xd6C\xa8B\xf4x\xe1\xb5\x1b}\x9d`\x0d/\x86\x86\x8d\xf4\x89^a\xa6\xf7\xc5#>\xba\x1c\x81\xd2\xa0j)W4\xd9gE\x1f\x89E\xfb\x03\xd8\x12\x14\x13\x14M/\xdd\xc5\x18\x91\xf6\xab\x08\xb9\xb7b\xa7\x91\x1bu\xdfF\xd8\x82\x81\xd1\xbd\xb9\x8d\xb0\x05\xb0\xf4\xf15=x\x1b\xa1\x08\xee\xbe\x08`X\x83oW\x1d\x8adT\x1e\x8du7d%%\x0ciCX\xd2\x05i\x89\xd9F\xa0\x18\xb2\xb1\xfdW\x02\xfb\xcb\xfc\x02^\xd3\xb1\xe2\x01\xb6s\xb0\xac\x83\xf9\xb4\\\xf8\x03\x1a]_x\xb5\x14\xe4\xa5/\xdb\xee\x0f\xfa\xda-\xf0\xa6\xc8j\xb3f\xb7T\xa5\x8e\xd6<\xe3\xb4\x95\x82\x8d'\xd0\xc9\xc1a\x90J\x17@\x1e=\"t8\xcc/\x88t\x01\xadn\xec\xd3\x06\x9a\xef\xbe\xfdP\xca\xfc!\x92\xf8:x\xb8\x80\x1ch\x94,H\xc6\x9b\x11\xb9\xff\xc7\xfd\x04\xe7\xfd\x04\xef\xa3\x1d\xba6\x8a\xcb-\xdb\x87\xe2\xfd\x04\xb7\x91\x9a\x0f\x1e\xb6.\x8d,\xaf\x8f\xc5\x07\x95s\xf1\xd4\x11=\xceZ\xf37\xde\x14\xcc}\xce\x0fP\x13\x12\xd5\xaaE\x9dH#\x19*\xe8\x90R\x971\\\xdb\x0d(\xeb\\O\xc9\x7f>^\xba\x82%o\xd51>\xb9$\xf4\x82\xf8m^]\x88\xa1Is\x1f._\xa5]._\x99_\xdc\xc1\xbb\x0b9\xe8\xe1\x858i\xa9\xf9\xe9\xcdM\xd7\xfb\\\x9aN\xe0j*\xda\x0c\xa4\xcd\xd2b\xbe\xd0\xd3\x11\xe1f\xf1\x15\x97\xca\x01rSYzu\xa2\x03K\xc9\x1d\xf5\xa8\x8b\x19DY\x8c\xaaQ\xac\x8eP\x1eV\x96\xf3CMw\xb4\xc1\xfb\x85\xec\xef\xf2an\"\xeem\xe3\xdc6\x86\x1f\x8d\x88\x1d\x8e\xb0r\xfe\xf4\xb9#\xc0J\xd4?\xff\xb4\x92L\x1b\xe2\xae\x08vgbc<\x9d\xba#wD\xec\x16\xa7\x1as\x9d\xbbs\xb1\xd4\xa3\x89\xcd\xf4\xd4\x9diE\xbd\x1b\xe1{7&\x8a\xcb\xd3\x86`!k\x16\x98\x1c\xcf\xdd9\xfc\xc8\xd6\xf1\xc2\x9d#\xa4\xdc\xc4\x1ay\xda\x10Q\x86\x85\xc9\x8e\xa6\xbe\xad\xe93w\xb64[\x99\x1c\x9f7\xe5Ht\x8egg\xee\x1c\x81\x1f\xd9^?k\x18h{\x95\xc4\xac-\xcc\xdd0\xe0\xc5\x8b'&k\xc3\xb0S\x1d\x1e\xc8dk \xd1\"\xa8 \xe4\xf2\xaca\\Y$|qo2}\xd6%0J\xf6Q\x02\xa3\xe4^\x90\x9c\x81Q\xa8 \x8cB10JE\x11\x0c\xd9\xf7\x18\x81\x99}\xebG7\x8a@\x17\x16i\x1d\xea\xb4n\xe9\xb3\xb7\x81t\x91\xd8\xb7E\xcc\xd5\xbc\xc3\x1c\xc6\xabb\xbe9z\xf9J\x8d\xa1\xafXI\xf1\xf8f\xd63\xf1hU\x89\xb9\x0d\xa6\xdb\x1b\x15\xe3\xed\xf6\xc0H\x0bM\x9c\xd6T\xd0\xde\xd2\xd6 \xcc\x11\xce\xac7\x98\x9f-]\xe6:Y\xc5\xe7\xf5kE*[=\x86C\x9fG\xc6KLa\xd4KQ]j\x88\x02\x8ez\x8d\x8e\xac\xf6\x15u\xafI\x9c:4y([y\xd4\xdb\xb1\x7ff\xa2\xef\xc3\xe5\x97\xb3\x01\xe6W\xe8R\xd1o\xb9MP1l\x03b\x8f \x97$\xbe \xa2Mx\xe2s\x01\"\xcbI\xc1g\x08\x04\xe2\xd2\xa0\xfc\xa0@\x19!\x10\xce3\x86$N\xf1\xdeb={)w>\x17\xefG\xa5\xe90\x1b\xfd\x8e\xfe\xdb\x0fNIy\n\xf2!G\xf7\xf40\x98\x97\xc4o\xd6\nF8x\x91q1s\x02\xc3\xc9\xe7\x11\x8e\xd3t0\xc0}\x84{W\xd6\x18\xe8\x187z\xaa\xf5\x97`\xef\xd4z\xbb\x9dM\x12\x16\xad\xfdh\x8b7\x04S\xee\xcd\xf5H/\x1b\x06\x95\xe0d\xe8R\xa0\xf7P\xe4\xe1;L\xe8\x0f\x9aF\xff\xd8\x802\xcdaO\x1ct\xc7\xeap\xfcF\xa7\xdc\xd9\xaf\xc8\xb1bB\x9dd\xf1:\xc2\xa4\xb7\xbe\xf0v\xc4mw\xed\xd1\x94\x91\xe9\xd9\xcc\xfd\xe1\xf3\xf3\xa6\x0f/\x1a>m\x1a\xad\xa7\x9f65\xdf4(\xd3\xf3\xc6\x91o\x82\xebE\xd38>w\x8c\n)\x98\xd29vbk\xb6\xa1Y \xda\xcb5\xf9S\xeap\x94\xd5H\xec\"\xcb.\x80\x1c\x192\x06T\x89\xd7]7G\x83\xc1\xc5@\xd1&'G\x8e\xf4e\nE\x82\xd4\xb6L\xe8\xbb\xe2UJ\xa3\xad\xf4!\xa3Z\x87\x83Q\xce\x82\xca\xf6\xe2\x1f \xe2w\x1e\x8b\xaa2\xc8\xc9;\xa7\x0d\x17E\xe2v[?=\xbc\xd8\xff\x82\xf1\x81\xd1#\xe1h\x8f\xc8\x89p;\x9a\x85\xd3\xcb\xb3\xd2\xf5TSYyV\x9c\x88ck\x98\x1e\xacA\xbb(9\xa0\xc6\xb0\xf4\x19U^>\x9eS\x12\x7f<>\xac\xb9\xb0~\xd4\x1c\xcd\xfb\x9d\xd4\x189\"\x15\xab\xc9\xedE\xce\x14+\x1e\x92iC\xe8\xd9\xe2\xefC4\x1d\xec\x90\xfe\x9d\xe4[\xe1\x1d\xe5kh\xabE O\xdaw\xbd\xc5\xdf{\xf70\xd7Xzi|\n1SG\x87\x81\xd7\x80\xa7\xf1F\x1c\x02\xbc\x03\xd0N\xa3\x11\x0d\xeb\xc1\x13\xb7C0\x1ch\xdfiv\x17\x0f\x87\xe8\x19\x9a\x93\x96;\xdf\xb1\xa2rq\xe3\xfd\x1b$U\xf1\xc7RF\xd8\xa5\xc5\xb59\xb8\x0e\x9c\xa2\xc0<\x7f\xfe\x02\xfdP\x13\xbd\x19;+\xf4\xaa\xb7X\x9c,z\xbf\xfe\xe4\x9f\x1e=\xee\x0f\x9e\x0cG\x93\xd3\xd9\xc5\xe5\xd5\xcb\xeb\xdf\xcc\x97o\xde\xfe\xf9g\xf9\xfe?\x8f{f\xe3\xd2\x1bt\xbboQ6\xb4Z\x92\xabb$\xa9\xca\xe5\x8b.d\xd5\xd2\xd4\x96\xad\x8a\x92\x9bk\xa4\xf3\xf3\x06\xbf\x8b\x07(\xeep\x18\xe3\xc5\xdf:j\xf9\x8d\x8e1\xf1\xb6\xf0\xf9\xf3\x17\n)\xcc]\xb0(\xbf\x88\xd0\xc4\xc8\x8c\x8fg\x85\x10\xc3+r>r2w\xcd?\xb4\xc3J7\xca\xebM\x15\xf8\xf4\xea\xb6B\xbb\x90\x96N+\x14\xa2\xf2 \xb6\xf9\xc7/\n\xf3k]\x1c\xb6\xb1_5\xbf5\x0fuo\xb1\xe8\x99aV\x1b\xc1\x8f\xb3\xea\x8eE\xe4\xd29F\xb3\xa0\xa0c\x89\x1c\xe3*\xc8\xee \xb3\x11\x01\x0f=\xbc\xb4\xa1\xcc\x0c\xb5\xfa\xfcE\x93+\xa1\x8b\x81*\xe8\"w\xa4,rE\xe8\x12\xc3\xd7\xc1_\xb3\x0b\xb0\x84\xac\xdc\xa7)D \x81\x93\xbf\xe6\x8d,\x85sx\xb8\xceH\x0fAIU=\xd4\x85>>\\\xc0\x19+\xa8\xae\xf2\x00\xb6\xe5\xc5\xd7\x85_4\x84\xed!\xa4\xd9i\x85_\x08\x93?'\x8bh9\x04\x93]\xd2k7Q1\x91|\x9a,S\x0e1\xa6\\\xde\xa5\xb5u\xd2uU\xc4E\xca\x93G\xfd\xfd;Z\x1cJ\xb2\xadu>m\x91\xb1\xcf\x1b\xd6N\xdaN\xf2\xdb\xed\xd7R\xf4^\x06w\x91[\xb257\xfe\xcb9\"\xf3u \xce\x94\xbc$g\x18\\\xa0\xda6\xd8.\xcf\xc0)\x96\xd3\xa7\xb9\x82\xee|0\x02\x03\xca\xab\x83\xd7\xdcL\xaef\x9f\xe7~\xee\xed\x8c*\x9c\xd3|\xab\xb9\x00\xd0\x01\xaeC`\x9ec\xdc0\xb8\x99n\xda\xaa\x81\xcc\x15!\xa8\x05\x0d\xf3\xd1\xa74T\x93\xc7O\xb2\x08\xce\xc9\x98\xa4\xa3FF\xacWt:\"\x1c\x0f\x89\x1c@\x9a%\x97\xe2A~\x8c\x8e\xe4u\x0b\x10>.k\xf4v\xdd\xd8\x19TC\xb6\xf6\xd7\xb6\x80\xceH\x9c\xf7\x161\x0f\xda\x0dY[Xj\x96\n\\\xd2T\xc3\xea@\x11\x9b\x01\xd1\xc4\x82b\xef?\x9a\x8d\x17\xbc\xd8P\xa8\xd7$\x1e\x8f\xc9\xcc:\xc1/|\x84\xe7\x18\x1d6]\x82\xa7\xe7&\xa1%\xfa\xc0\x18J\x04wSxjou\xe6}\xd6\xc1\xd4;\"\xd7zF1\x06\xaa\xd6%T\xe6\xd8\xa2K\xbb\x15\nk6 m3\x8c{\xef\xf6\x98\xd6\xb6\xcb*\xb4\xf8@\xc3\x97\x02\xef\xb0\xdd\xd7\xd6qv02P\xa2\x90Y\x01\xe7A\xad\xfco\x963h\xdf\xfd\xff*\x8c\xa1\xb1\xed\x7f\x13|\xe1\xd9\xd3\x0elAg\xfa[p\x85g\x0d\xee0\xdb\x98\xc2\xc9\x95\xae\xe7\xef\x8e-4\xf5&\xe7\n\xad9\x8e`\n\x1a\x0b\x1f\xce\x13t\x05\xff` \x9dX\x82\x1f\xa5\x7fc\x96\xa0Z\xfc\x07K\xa8\xfcZX\xc2\x8b\x06w\xc3\x7f\x0b\x96\xd0\xd8\xf6\xbf \x96\xa0\xdd\x9e\xb5\xb3\x04\x9d\xe9o\xc1\x12tS\xffNXBSor\x96\xd0\x9a\xe3\x08\x96\xf0b\xfa\x81,AW\xf0\x0f\x96\xd0\x89%\x84\x94\xdf\xfc\x8dy\x024\xf9o\x8c)\xd8\xe46\xd3 \xb3f\x89\x0d\x00\xc50\x00\x14\xa8\xfaT\xea\x8b\xe76\xf5\xf33\x9b\x8a\x9e\xe9X\xd53\xdd\xd1Q\xb9\n\xfeR\xeb\x03\x9b\xa1-}-=mH\x0fZY\x98\xe7Z\xc6\xc2u4\x85\x97\x0c\x1a\xc8\xbb\xc8\xc9;\xeaZ\x03\x18\x89j6\x8a\xa1\x95=\x97\xaaU\x0f:\xdc\x16\x81\xd2`5\x0f\xf7\x9a\xfa\xa8\x10\x1e\xeb\xab\xa7\xcf\xc85\x8c\x02\xf4x\xaa\xf0\xe3i!\x9a\x1f\xb6\xee\x80\x91\x16U\x10H%bt;o\xda\xd1\xd5D\x85\x1c\x91u\xe1\x0c9>G\xa7\xb0\x1e\xc0\xc7\xfb\xda[\xad\xad\x80\xf7\xe3\xdc\x15\xf3\xc9t\xa0\xd0\xbc\xbe|<\x1a\xc1J\x9d\x91\xcc1!4\xc25\xe5t\x07\xbff\x81\x1f\xa63\xe27\x10\x97\x07\xd8Z\xe4RO\xf5\xdap+\xe2l\x9a\x0f\xce\x12\x17Nm\x06uF\xa9C*&\xb0\x01\xc0\xb1O>@\\\xfb\xbb\xdcW>z\x84\xfd\xd3s\xa4\xbax]7\xb7\xb0\x01\x05\x90\xad\xa3C\xea\xd3\xfe\x1b9\x7f\xb3X,\x07\xfd\xc5b\xb1\x18\x00\x83>9\xcc\xf9U\xb6(?K\xd5\xb1\xf8\x80\xcc\x18s\x08\xe3\xdc\xd4\xde\x07}p\xfc\xe1\xc0O\x9du\xe0\x87+2_\x0e\xcc\xee\xac\xfe\xbd\xe0V\xd4E\x0e\xe2\xc3\xe8Xv\x0cR\xa7\xcb\xeb\x87\x84\x8d\xac\xac\x1b\xdc=\xd6\x1c\xa1\xba\x17S\xbd\x93s\x7f\xa9\x06\xaf\xde\x03\xa8p\x96W\x9d&\xb8\x9d\xa9H\xfe\x95%ZXCqm\x07\x90\xd9\x08x\x1fc1\x1d\xbbhJa/\x9b\x17M\xcbU\x1d\xc5\xba\x9e\x92\x97\x07\x8c\\N\x1c\xf8ZM\x83 \xd6\xad\xb54EGo\xb9\x16\xd4\xa60\xc8~9K#k\xa7\x93\xe5v:\xf4\x82\xf0\xe3\xa3\xa3\xf3\xc3\x81\xd7\xa6\x0d\x02}\x87\xa2M\x81\xd5y\xf7\xc0\xeahG\x04\xfd\xd4\xe4\x8e\xab\xe1B\xd7\x8a}\xae\x96cT\x11k2\xe3\x05\x10\x05#-\x12\xe1\x1c5\xc65\x8f\x96\xcd\xe4\xaf\x1bMk\xaf\xfc\x12D9\xad\xaah%|\x0e\x82\x11\xbb \x86\x8e\x98\x1e\xb9\xb4\x08Y$f\xe4\xacN8\xda`\x84\xa8\xcd3\xe2\x82\xb1\x94\xb1\x99~\xcf\xe3\xe5\x04\xdan\xec\x08~\xd6\xd2\xc7\x87R\xf2\xd8\xc1\x80\xb3\xd57\x0f\xa0\xf1\x05\"\xcaK\x04\x94~\xc4\xc0\xe4\x05Y\xe4\xecY\xd5u\x99\xd1\x99|\xe6\xd0\x99\x14\xe2\x8a\x9e\x8d?\x9f\x9c\x80\xf2\xf4\xc9pqzum\x15\xa6\xc3\xdf\xe49\x96\xfd\xebY\xfe6^\xfe|6z1}_\xf8>\xb8\xee_\xcf\x16\x93\xa3J\x0c\x9e\x0c^\x9e\xd6\xf56\x05\xd8&\x8b\xf1\xf2\xe7\xe9\xe8\xfc\xf9\xfb\xc1\xac?\x7fs\xf9rqwv6^\xdc\x9d\x9f-U\xd9\x87\xf3\x91\x92n\xa7U\xc2z\xd1\xa8}\xd0\xd4\xa3_\xa5\x16\x9b\xa2\x13\xaa\x97\xbd\x82(\x04\xaa\x90H\xab\x0f)\xb8\xab?\xe9s\x9b9\xab\xc5\xa1,\x94U\xbb\xa1l~\xb6\xd4\x8dL\xf5\xd5~\x0f\xac\x08\x02\xb5\xe7:\xb1\x02C\xd1/W?(\x8ba\x1dd\xef\xd6\xfd\xc3\xc1]Be\x1d\x1c^\x96\x02|\xe69(\x8e\xd6[\xba\xc2S\xb2\xaa\xe3\xc3\xa3[\xed\xb2\xcb8\xb0\xb2\x87zF\xf2[\x98\x03E\xedN04i\x94\x874\xb5\x13\x986M`/\xa4~ b \x87m\x93\xe9\xfdc2K\xbf\x8f:\x99iu2?\x0e\x91.\xd2\xa6y\xcf\x8b1N\xe7:\xf6\xeb\x8e\xe8(\xa5\xfa\x0fD\xe6\xa4\xab\x18CwR\x0f\x0b\x99?>\x04\xd6\xf48\xfe\x05\xb7u\xf0\x17#\x94\xfa\x18\xffs\x0d>\x1d\xads\xbb\x8d\x80\xb2[\x16\xc3\x1f\xfdo\xb2\xd3\xd1E\x9f\x9ec\x04R\x81\xd9\xd4_(\xee\xd3;\xf8\xa3\x9b\xf6C\xfcW\xbfE\x1b\xa8\xc7O\xf0\x95\xfb\xa9\xf9;Y1f\x13'w\x89W|\xces\x05\xb7\xef\xd4s\xb0\xc6\nq\x19\xc0\x13\xf6-Lyb\xfeB\xa9P\xfc\x84 Y\xa2V\x85z\x8c\xd8-|\x8a6\xf8\xc7\xc7\x7f!\x16i\x14a\x7f\xe2\x84\xfe\x94\xb1 \xf6n`+\xa4\x92\x92\xd8DD\x85b\\\xa4\xf0\x9e2\xbe\xf7=\x86\x8fij\xe2\xa1\x9a\x81I}\xb6\xc7\x8f\xbe~G\xb8\xd2\x10\xffD!&\xc74\xb1C`_ \x0b\xfa\x84\xec p\xca\xa9\xfeD\x188V\xe8\x19\x12;?\x0dY\x9a\x82\x06\x8a\xf4D\xf4\xf4\xfc\xd33x\xc2\x16\x05\xccr\xc6\x01\xae=\x0bC\xe8/\x0e\xc1-\x86t\xbd\xf3\x10j\xf5w\x9c\xa5L#\xca]\x18\xf0\xc4\xb3`\x15^\xb1T\x88\xd3\xf8\xee\xe9\xe7\x93\xe7g<\x7fDd\\\xfbYx'8b\xe8&\xc1?\xf8 \xb1\x82j$\x16\x82z\xbb\x90E\xf8v\xab\xfe]\xb1tG1\xf4\xec\xca\x17^\xeccX\xde8\x80\xb9\xf6h\xa0g\xdd\xdb\xf1\x18\x83\xda\xe2\xd3\x98\xdd \x16\xa566o8f{\x16\x89\x15\xf7\x05\x1bS!X\xb4f\x98\x1d \x0c<\xee\x01\xa8u\x10\xd1q\x12\xd0\xfb\xd4\x8f\xb6\xda\xbf\xa3IR\xb9\xa9\x1f!\xea\xaf\x05T\xbe\xde\xaf\xd4\x1f\xb6>\xbfQ\x7f7\xd4c\xc2GX6\xcc\x84\xf9\x8d\xb6:\x84\xaf\x9f\x02zma*\xb7\xbe\xc0?\xef\xc28\xe1\xb1 \xc0\xbb\x154\x80\xbav\x1e\xae\x04=+~\x82\x7f\xb8^\x13\xde\x0b\xfd\x17\x97\x85@L\xfa\x91BK?\xe2\xdb\x0d\xbbO(\x16\x08h*60\xe0j\xd5\xe0\xa2\xa0[\x8dD\xa1M\xe17:%G\xa5\x10\xeb\n\xd3\xf1\x8e\x05zYE8wa\x16\xea8\xbf\xe1\x1e\xa0\x03\x19[=\xc4\x88; \x0dB\xfc\x9bPN\xdf\xbd\x03\xa4K\x02*L4\xe3\x84\xc7w\x10\x1f8I\xef\x01\xce\x9f2\xc6!\xc1,0\x96\xc6\x19\xc7\x95\xc5\x11iyz\x1fA^.\xf4\xb2a^\x1c\xad\x03\x7f\x83KL\xaf\x88t\x8bk\xf0\xe6>\xc1\xf4\x10\xa6*\x8d\x835\xc5\xc0\xc5I,\xfc\x0d4\x96\xe2\xc4\xa4\x82Q\x00+\xc5\xee\xa8\xd74\x01\xc7)\xb0\xc2\xa2-\xc0\x94\xad\xa1\x81,\xe2\x8c\xc2r\xcc\xc4\xf9\xd9\x19DaVx\xc6}D\xd0\xbd\xcfn\xc79\xf4\xb7l\xe5a\xf6[Aq\xf5\xdd{\xfe\xed= \xc3\xdd\xc6GD\xbf\xe3\xf0\xe9>L\xb7\xbc\xb7|8\xff( \xf9\x9f\x0e&\xbf\x7f\xfd\xea\xdb\xb7\xaf\xbf\xf8\xe7\xb7\xdf\x7f\xf5p\x01\xb8\xa2Eq+\x17+A\xf8I~CE+^\xc8Ic0}\n\xc7\x1aE3\x05\x14\x97\x9f\xea;\x8dN\x97\x0e\x06\x17\xa7\x15\x8d\\\x8a\xe5@u\x04\x98\xac3?\x9d\xbeW\x99\x1f\xce*\x8b\x97v\x1c\x04\xab\xc0\x0f\xeb\xfa\xf8\xa7\x9f\xb9\xb9\xa3w(Z8\xde8\xdd\xb8/\xa9<}\xee\xd6Iy\x9a}\xbai\xa6\xbf1f(9\x93\xf1\x0c'+\x1cI\xa0rA\xf1\xe7\xde\x1dF\xaa \xe6\xd3\xa5b %\xdd\x14\xb9&\xa0\xa1\xf8&\x12}\x95\xc1\xe85\x06#2}\x01\x01\xd6\x8b_Gd\x8aa\xb6\n\x97\x81\xfc~\xa4j\xa1}\xa0\xcc\xb4\xff\xe2\xf9\xf3\xa7OK;\xf2\xa0\xcc\xb6\xea\xc4\x1am6\xc0p\xa8\xb1k)2\xe9X\xf1\x01\x05J\xb5\xa7%\x98\xf8\\eY\xb6\x00\xe1\x14\x95\\\x0e\xec\x1e\xfd\xc2\xfe\xeb\xca\xb3\xac\x05\xb5\x99c\xf2\x95\xe0\xe1\xf6[v\xa7>\xfd1k\x88\xca\x01\x07*iC\xc4\x0e\x1am\xbf\xe3l\xe3\xdf\xcd\xd4\x8e$\xdaft\xcb\xc6.\xed\x8b\x1f\xdd\xf8\x9b\xfb\xc6\xf8*7\xaf)\xdf21sJ\x03\xe2>\x89!\xa8\x08\xe3\xee\n\x809\xa63\xd2\xfb\xeb_\xfe\xcf\xbf\xfe\xe5\xff\xfa\xeb_\xfe\x8f\xbf\xfe\xe5\xbf\xb8\xd4]\xfev\x17`\xfc\x91(\x0b\x1cJ\xa8\xfc\x8clF\xce\xab\xa7\x1c\xa5W/\x0e\x938b\x91p\x8e\xb5\x17s\xe6JW?\x9e\x05\x10\x8a\xa5\x07\x9e\xe4z\xa3<\xea\x8b\xda\x1c\x19+\x19|\x03\xc9E1\"x\xd7\x83\x88{\x1f\xca\x05v\xbb^\x8e\xaeV\xfc\\=\xd8\xa3\x0eA\xfd\xa0\xe7\x08\x83\xe8\x98mto\xd7\x05th\xbe72\xce\xf7\xd4\x06\xd9@`\x1aV\xcf;F\xd7\xc8 {;T2\x890\xb0}\x0f\n\x9fu\x90\xbeB\xd0\xa6\x91\x8e\xa5\xdb\x0dv\x1c\xc7\x83\xc0\x17\x02w\x94b\xa7\xe8\x00)\xc5\x00&y\\\x8e<\x14K5FH!\xc2\x87\x0dHR\x08\xef\x82\xbaP\x07\xfc\xbfr\xbf\xfd\x83,\x14?\xfe\xbb$\x0b-\xcb\xae\x0d\xab\xff\xce0\xc6q\x1d\xbe\x801\x8e\xaf\xff\xc0\x18\xf8=\x04cj\xe9\xe4(F\x82\x0c\xa1\x13\x0d\xfd8\xf4\xffCh~'0?\x94\xd4\x1f\xa2\xf1\xff\n4\x1d\xb6]\xf9\xd2\xe4\xc5}IU\x98w\xaffS\x0b\x83#&jf\x1e\xfez<\x8e\xeeQ?\xbf^s\x86\x07\x04\x943\xcc\xc5\x85\xef\xa1\xde\x97\xa6>N&\xcd\xd6>h=A\xc9\xbaZ\xfb\xf8\x07\x93|\x18\x99\x95\x1d\xda\x12:\xac\xe25\x8c&\xb6\xbc\xca\x84\xd0z{\x1a\xed\xf1D\xcb\xa3\x890\xca|\x16 T\xa6{~\x19\x9b\xbc8\xd0\x7f\xb6<\xce\xf0\xc4+W\xef\xe7\xa7]\x82\x1a\x1cZ\xe39\x18\xf3bNE\x8cZ}d\xe9k\xa6$ d\xf2\x1b\xd4\xf3\xfb\xf8\xdd\xc7\xc32\xcc\x05\xb5\xb0\x80\x99S\x0b\x06\x03\xb6\xf1Y\xb0N\x99\x8e\x11\xb5-\x00\xbf\xf1\xb7\x19\xd72\x01\x96P\xb2\x81>\x1b\xd0\n\xf1\xdd\x14\xfe\x05yl\x87\x87k\xa0X\xde=\x87\x7fA\xe9\xaf\xd6\x83\xf9\xab\x0f\xe2l\x9f\xf3\xf5\xa3\xfe\xc2,\xf8!\x0c\xbf\x1f%x.\x88a\xdbz7+\xa8\x04\xacw\xe0\x81mY\x84IP,\xa4x\xde\x12\x9aC6\x08\xe5\xa6\xfe\xfe\x94\xe1\xf1I\xc8\xa2\xcc\xfc\xf5\x05\xf6>d\xbaC\x11\x9e+F1\xce+\xceN\x9c\x08\x0bil\xc7%\xce\x84\x06\xcd\x9c\xad\xe1\x9fxk0\xef'\xf5\x0f\x9e\xe9q\xc8\xc8\xb3\x15\n\xb6\xf0\x0f\xb5\xe7\x00\xa6\xca\x94\x05\xfa<%\xdd\xd1u\x0c\xc7IiH\x03\x80\"\xd7\xc9\xa7 \xf5\x10\xdc4\xa1XPp\xff\x86\xe9\xa7\x18\x89N*\xee\x11\xdb1\x08]/\xcd\xc2\x90\xe2)\x05\x06\x9d\xd3R\xa7z0\xd8,`$\x05\x0b\x93@\x1f8*\"`V\x90P\x13\x0f\x0f(\xb4\x9a\x195gG\x82\xe3\xbf\x14)\xa0\x80\xbc0\xd6\x19\xf4`\x8f\xc7<{\x7f\x8d\x07\xb3\xb7+\xdes\x04\x8a\x03\xa3\xb0^\xba\x87^\xe0\xd2\x0d\xc46\xb8GQ\xd9<\xafQ.5\xaff&i\xe4\x87T0/\x0epm\xe8\xf706c\xac\x13\x04\xa7Qj\xd0\xd7\x92\x81\xc2\xea\xf5\xb9&\x16^\xe0' \xc5.\xaf\xd9F\x0b\xd1)\x9c\xe5\xb0 \xf0\x93\x14\x17\x87\x1f\xd8E\x81\xcb\x04\xcf\xcb\x0c\xdc\xf0`\x84\xe9\x1b\x86G\x9a\xda\xf6\x1e\xe8\xaf\xfdK\xf9\x96\xd3\xb5\xaf\x97'\x9cnq|J\x11\x97\x99\xa0\x862\x84\x06\xb2\xc2_\xa1+O\xe2\xe0~\x1b\xdbG\xcb5\xe9\xda\xa7A\xb1 n\x90N\xe01q\x8e9\x10\x01\n\x9e\xee\xc3U\xac\x0fq\xef\x84\xf9k\x1a\x05\xabzx\xd0\x1d\x14\x061\xed\\\xef}\x06\xe8\xbc\x87\xae;f=\x82Y\xdf\xb0\xdf\x06z=o\xd8\x97j\x12_Q\xc1\xfd;\x93\xa0\xc5\x88\xd70{z\xb819\xd5\x94U\xbdF\xfb8\xd8\xb3b\xc9\xdf\xf9\x9bM\x96\xb2o\x958\xa3\x99\xb2JL\xed\xde\xf3\x15\xd2\x0bH\x144\x12\x90\x13S\xbe\x0e\xe2XC\xf4u\x16y_\xe4\x8f\xbf\xcd\x1f\xff9\x7f\xfc\x1e\x1f\xff\x99fi\xea\xd3\xe8\xb7A\xa6\xe1|\xc5\xf8\x96\x15\x1e\xff`E\x8aW1Ovq\x10o\xef\xf1\xfd\x8f\x9b\x8d\xa1\xc5\xa87,\x80\xf3C\xc2\xbc,\xa0\xbc\xdc\x97\x1f\x92\xb8\x98\xe9\xb5\xb1\x84`\xaf3\xbe\xca\x02%\xb4\xb8F\x1d\"r\xf4B=\x8f!\x8b\xb4e\x89z\xe6\x1c\x97P\x08\"\x0f\x9a(l8\x05\xc4\x0f-^\xe3\xe9f\x08\x04\x99\xad\x91\x04\x84a\x16\xf8h\xea\x81\xa7\xb0H\x92\xd1\xd8!\xdektN\xe8z\xad\xabMv4\x121\x92b\xae\x89L\xc8\x91\x00\xea\x83\xdc\x04\xa8\x1e&\xfc\x84\xe44\xbc\xb7\x98\x1aj\"\x17j\xd2\xa6\xde\xcd\xa3%s!\x92\xb7\xd0\xa0p\xa8\xa1\xcd\"\xcd\x90\xf0 \x00t\x8cU\x0cc\xf5k\x14\x8b\x1c\xd2\x1a\n$\x9e\xc7\xb4m\x80%\xeb4\xf0\xb7\xfa\x01\xbfd\"V\x12q\xc0\xb4,A\xbd\x1b\xc5`\x10\xefW[K\xbcV1\xd7\x90y,\x08\xd4x\xe9\xf9V\xafj<\xcc\xeb\x8ey78\x94V\xc0\x08(2!/`Hvm\xad^\x8cB\x82\xfa\xab\x97\xa9\x17\xc7|\x8d\x89\x9a:A3\x8a!\x8cW4e\x86g\xd2\xd436>\xe6L\xcf \x84M00\xd3w~\x98!`\xaa\x8a\x8d\x9a \x16y\xf7&A\xd59Nw\xfe\x06\xea[1\xbd\xd2V>\n\x1e(!\x16\x96/ZB\xa9\xbfc\xc3o\xe1E\xed\xffz\x95u\x1d\xf3\xb1Z <\x89\x03j7\x1f\xf5\xe41\n+i\xfe9\xe1\xb11\x9e\xc3\x04\xce\x14)4\xf4\x05f\x07\xbb\x80\x8b\x1d\x12Pf\\#k\xf5\xe2\x08\x18'&\xf1\\\xa8]\x03\x97\xd5Y\xf7~\xaa\xf7,\xc8\x14\xd9z\xcbB\xcd\x06Y\xc0\xf6\x16j#\x04\xf8(\xfc\xaa\xbf\xe3XQ<\\\xf9\xf0nF\xa0 z)V=\xb6#\x82\xaf\xc5bq$\xc6\x1b\x1a\xfaA\xfejP\xdb\xbe\x8c\xe9\xfa\xc7,\x15y\x9a\xe0L\x8bA\xfa]c1\xbc\xed)\xf7i\x94\xe7\xbe\xb5h\xb6A\xd9\x03Z\xda\xc2\x06i\x0b\x1b$`\x9dc\x83?E\xb9\xd0\x08eY\xe4#\xe34 %i\xb5@8u9M\x1a\x950Y\x9e8D-?\x82va\x99\xdf\x00 7\x98\x00;\xb5\x1b\xd8\xa9)\xb1L\x17\xbaa\xf7\x89\x929R\xfd\x92&\x10X]\xbf)n\x00\xcf\x96\xd4\x02%\xcd\xc7,`\x8a\xd6\x8d\x0b\xecI\xd5\xcd\x82\xd0\x8ac\xf8\xae:\x99S\xe1@K3\xf9\xe4\x05\xb16P\x1c\xb3\x84\xef\xbc\x1d\x8d\"\x16\xa0\x00\x84=\xbdw\xa4Asw\xd0\x8f;\xe8\x07\xca\x1f*7\xfc\x03_\xee\xe1\x0b\x18|\xbf\x8b\xe3\x90Fk%09d\x94\xac \xa3\xf4P8\x81U\xaa\x97\xb4\x15{Vl\xcf\x02-k\xdbM\x9a\x17\x07Y\x18\xa56\x13\xbe[r\xad?kQm\xcd\xa28\xb4Y\xd7,\xd1:\x0d+\xcb\xe7l\x1a\x1es>\x07\xbbG\xf5\xc05ykbA\x81\xc2\x1f-q\x17H{\xc4\xc4\xce\xf7n\"\xad\x17\x0b\xecV.\xb0\xfaT\xb5\x05-\xef\x83T\x8a]g\xea\xc50j\xf5\\\xe0\xba!\xbd\xb3_\xfc\xc8>\xc6{\xb55\x81U\x03\x8dFqNL\xa3,\x1f\x07#\xad\xf3\xf8\xd6\xa6\xf1\xf8\xd6\x8e!\n\xcc\x06w\n\xe23\xb7\xbd\xe0\xb6\x17\xb8\xe7\x05\x03\xc5\xfc\xb5\x00\x95\xde\x13\xfb\xef\x98\xde[\xf8Z\x8f\x07\xe8e\xb5\x80 \xb5L\xc2\xbeh\xe2\x03\xa2\x88V\xe2\xe9 \xffV\x96L\xb3\xa4\x9ar\x1f\x86Lp\x1f\xe4\xf1}N}\x0e\x8b\xcex\x83\xe3.\xf0\xa3\x9b\x99\x99\xe3\xbb0\x98i\xebzH\xb7\xe2\xba\xfa`G\x03\xaa\x9cA\x8e\xde\xb2`?I\x8a&\x8f\x81\xd3\n\x89T#7\x9b\xab\x9d\x17$\x1a\x8f/\x06\xa8\xe8\x8c\xb6=ru\x05\xa6\xa6\xf1\x86\x88\xb9\xb9}:\x87[\x98\xeaO\xe5f\xd9\x88\xb0\xb9J^6x\xdf2\xa6\x9b\x95\x83\x0d7\xe4^\xbb-\xae\xebp\x93h\xf5\x16^\xa6\xad\xb7\xaf\xbdc\xfb\x11a\x03\xf2\xc7\xd5\x8f\xcc\x13\x85\xf0\xf2;\x9a\xfe\xf16\xfa\x8e+\xd1A\xdcO<\x1a\xc0\xe0i\xcf\xd1\xba\xd7l\x1e-\x1d\x9eT\x8c\xc9N\xc3\x91\x0d\xd1\x80o\xc0\xbb\xdc\xcf\x8b\x9f\xe7\x8bt\xf1\xc3\xf2\x89\xd4\x7f\x17\xef\x17\xefO\xb7a\xbdG\x89*p\xf9O\x95\xec\xff\xf4\xd2\x99y\x0d\xd6jk*\xe8x\xbe\x18/n'\x8b\xec\xec\xec\xb7\x9f\x8e\x17\xd9\xd7_\x7f\xfd\xf5\xf2\xd4q\xf2\x08%\xd4\x12\xc7\x12\xcb\xe1'\x8e\\{\xc8\xd5\xbf\x9e\xe1\xff\x1b\xb9\x13\x03\x91\xa4\xd7\x12o\xd6H\xc1\x02\x89\xd7-\xa4\xe7\xaf\xe5]\x98$\x83\x99\x9c\xbf\xa1\xe3wK9\xa7\xe3w\xc3\xc9b\xbc\x1c\xf6\xafg\x90\xa6\xdefK\xf9\xc9`P5\xb7#\xda\xb3\x154\xb6\xb8\x1d\xe2\"\x93`\x829se\xde\xaa\xccs\xd5\xcd\xb3\xb3\xb1\xfas~\xa6\xfe\xfd\xe2l\x91M_|\xa6\xfe\xfd\xec\xec\xabEv\x8e\x9f\xcf\xcf\xce?W\xff>\xdf,\xb2\xa7ggg\xcb\xd3m\xbd\xca{rEz\x06 \x8b\xf8\xff\x03hf\x15.\x18%m\xed\xe3D\xc9\x0f\x8a\x86\x90\xeb\x03\x16\xe5\xa4\x803XC\xdd\xa9\xee{2\xeb^\x0b\x03\xc0\xda\xe1f\x13\x10\xd1x\xa6\x18,\x18\xe1\x15\xbe\x81M\xa1\xee\x86]\x13\xe4:\xef\xec\xac\x05\xd2&\xea\xb3r\xc3\xedoH\xff\x0b%\xb5M\xfc\x14\xfe\xf6Y\xa3\x85\xa1%Sj\xd1\x9f\xe1=z]\xc6\x98\xb0_\x10\x01\x11\xe7\x0d \x13\xc3\xe1\x80Ds\x81\xebU,\xeb\xcb\x95\x14\xdc\xf5\xd5{\xd3\xb4\xba\x11\xe4\x0d\x8f\xc3vG\x80\n\xda\xb7m\x07\xae\x85:{J\x00\xd9\xf8\x11[\x17\xe7\xec\xd6\x8f\xd6\xf1-\xb9\x06{\x002\xd3\xef\xe5&\x9d6\x83v\xe4o\x9d\x8d*\xc8\xbe\"W\x84\xf2m\x06\x86`&\x92\xfcK\x8c\x0d_\xf0B`\xb3\xcc\xcf\x96\xe4\xba\xfc:#o\x9b\x02\x9a\xde\x95\x0c`\x9b&\x95\xe4\x10\xdfV\xc7\xd2\xfc\xde\xbb\xbd5\xdcM\xf6\x8c\xa7\xaa\x8bW\xa47\x9d\x9cM\xd4\xae\xfan\xc2Y\x18\xef\xd9Z\xc7\xbd>\xf9\n\x9ck|5Y\xc7\x1e\x80\xad^?\x87~\xe5i\x93(^\xb3\xd7\xf7 \xb3\xb6\x9bw\x13?\xfd!K\x92\x98\x0b\xa8\xead:\"wu0\xd4(\xfe@\x8aU\xb9\xc7\xe2\xcb\x06\xbf~\xeaw\xd3\xf2\xed\x8b\x0eu\xff\x11\xf2\xfcN\xe7\xf9\x9a\xd3ms\xde\xef \xef\xef_\xbf\xfa\xf6\xb5>p\xfc\nO\xa5\xdd\xd9_C\xf6?\xd4,\xad\xcd\xef\x95\xfd\xfe5\xe8\x83\xdc\xb9\xbe\xc1\\4dk\x95\xf5\x15M\xdc\xf9~\xb4\xfc\x1a(\xd27\xe4\xbaRLM\xddW\x93W\xf1;H\xfcB\x08\xae\x12g\xe4\x1bw}\x7f\x80v_\xb3\xbb\x86\xde}\x0f\xdf\xbfD\x8b|w\x96\xdf\xe1\xd8\xfe\xf1\xd5wp[\xda\x9d\xe9[\xc8\xf4?\xbf\xfa\xf6\xf7B$\xdf\xb3\x9f2\x966T\xf7\xa7r\x0f\xbf\x85\x1e\x96\x0b\x92\x19\xf9\xd6]\xf8'h\x86Ej\xff\xf6\xa7\xef\x1b\xfa\xfcu\xb9\x85\x9f\xa0\x05[\x86\xcc\xc8O\xee\xb5\xe4\xe4\x17\xdf5-Z\x85\xf6\xef\x14\xf5\xfd\xff\xd9\xfb\xda\xae\xb8m%\xe0\xef\xf7W\x0c~zR\xfb\xe05\x90\xa4\xb7\xed\x06\xc2!\xb0ii\x03\xe4\x02i\xdaK\xf3p\xcc\xaev\xd7\xc1k\xed\xe3\x17^z\xcb\x7f\x7f\x8eF\x92-\xdb\x92\xec%iz?\\\x7fHXk$K\xa3\x91\xe6E\xa3\x99`\x9c\x92\x8a\x88\xdc\xea\x18\xdb\x10\xc4\xff\x8f@\x98D\xd8\x16S\xfe\x08\xe8mBRI\xc1(c1\xc27\x94\xdb.\xd5\xc8\x87u\xf0\x15\xeb\xa0\x1eK\xbf\xc0\x0e\xbc\n\xa2\xc5\x92\xf7\x1b\x95\x14=\xe4\x8f\x08\xc9G\xc9\xa8\xf0P\xb0u=\xf4{\x84\x9e\x91\\ ${u\x7f\x1e\xce\x18\xb5\xea\xe1\x7fRZ\xef\xb7\x80\x7f\x83\x1d8c=\xa7in^\x97?\xa3T\xdc\x9e\x82\xe6\xae\xf6Kc\xa7\xffE\xf4\x85m\x10\xeat\xf0\xfdr\xaf\xdc\x88\x8e\xe8Ds\xf7\x8d!\xfd\x07\x8c\x8c\xa6\xed\xd4W\xb0\x03\x86\x95\xffo\xd8\x81\x89\xbe\xe8W\xd8\x81\xb9\xbe\xe8_\x18wM[D\x08\xec\x80F\xa4cON0(\xa0\xb6,aez\xcf;@F\x05;\x10\xbb\xffy\xf0\xe1\xe2\x03\xa3\xceq\x98\xbbW\x188\xeb\xca\xcd\xf1\xdf\x04\xffM\xf1_\xeay\x06\xdeH\xed\xdf\x89\xf4\xdf\x89\xb0\xd5\x10\xff-\xf0\xdf\xcc\xf8\x85\xd0\xfe\x85\xc2^\x9c\x11Cb\"\xc0[\x81\x96\xc21\xb1\xb0\xb3\xa9\xadpi+\x9c\xd8\n\xe7\xb6\xc2\x1b[\xe1\xc2V8\xb3\x15\xde\xdb\n\xafl\x18\xba\xb4\x15\xde\x12\x8bB;R\xc8\xa2r\xa0\x91.A\xd2\xa3\xa0\x8a\xf7PZ\x93T\xef\"\xe1\xe4\xc3\xbdD>\x98d7\xed\x97J\xcf\x12\xe1(V\xb9Gq\xa7\x1aSkg\xb5\xd6\xb8a\xb99}uh\xf8\x98R\xc6*\xb1\x97\x85ZI\xfb)\xa5LVB\xfaw\xde\x9d\x8d.\xdf\x9e\x9e\xbc>|3\x92\x9fz\xf2\x04\xa6\x81\xfa\xde\x17\x9b\x14\x0f\x82'\xfa}\xb9wz\xb8\x87\x0d\xfab\x9b\xaa\x17\x1f\xec\x9d\xcbb\xdc\xa8\xe4\xfbw\xc7?\x1f\x9f\xbc?f\x8d\x9f\x9f\xec\x9f\xbc9C\xa5a\xcb\xe7;\xd648\xdb{=\xba|}rz\xf9\xd3\xbf\xde\x8dN\x7f\x93\xa5\xcbF\xe9\xf9\xe8\xe8\xed\x9b\xbd\xf3QY}\xc2\x01\xde\xffx\xf2ftyp\xb2\xff\xeeht|.\x0b\x17\xbc\xf0tt\xfe\xee\xf4\xf8\xf2\xe0\xe4H\x16\xcc\x9a\x05\x97\xafO\xf7~P\xab\xde\xb7 \x0e\x8f\xde\x9e\x9c\x96\xe57\xbc\xfc\xf5\xc9\xe9\xfe\xe8\xf2\xd5\xc9A\xd9\xe3\xab\x1aR\xce\xf6\x8e\x0f\xcf\x0f\xff\xcd\xbav\xe4\x8b\x8dI\x96\xfd<\x1a\xbd\xbd\xdc?9>\x1f\x1d\x9f\xfb\x9ciV\xc4\xf1\xee\xf4\xf0\xf2t\xf4\xc3\xe8\xd7\xb7\xac\xe1\x9c *0\x0c\x11\x91i\xd5f\xfc\x05\xdfa7=\x9cZ\x0c\xecI\xb4\xbc\x0dy%\xa7OT\xdb\xf8Z\xb8%Uh\x80\xd8M\x88\x0f\x8c\xd7\xc6.%>D<\xb3\x97\x84\xcbnf\nX^\x82\x85\xe5_Y\xab\x02\xd7Z2\xa5^\xd2]\x8f\xed\xb3Gj\x97\xd2\x12\xb2P\xebx\xb8\x9a\x0e\xf8\xa2(\x87\xbe\xb3\xc3\xa4\x88\x12\x11c7!\x1e\xd6b-U\xf0UmF\xad\x08Oy\xed\x88\x94\xbf`\xecRQ\x9b\x12\x15\xbe\xaa\xcd&\n\xc9S6\x13\xbbgD[\xe8!\x01\xf0\x8e\x95.Wr\xee\xb8\x85\x94\x1b\x96RB\xfe \xb8*\xab\xb7\xc2\x82\xca\xcb\xdc\xa9\xe7\xf3\xadu\xaa\xdd\xfd\x0c\xdc\xed\x84\xf46\x18\x94J\xbe)&\x82\xfa\x08\xbf\xeb\xa1\xc6Z%\x9f\x07K\xce\xb1<\xbd\xb7\xf4\x04dv\x08\x92\xa0<.:\xb6?\x8f\xe2\x89\xc9\x9c\x01h\xd1\x1b\x87\xf9x\x8ey8\xbaZ\xa7ENR&\x92c\xe8rs\x93\xab \xfb-\xe9\xba\x9e\xac>\xdd8XiF\xd8S\xfa\xf0\x0c!g\x1a\xd3\x9e\xfc\xcd\xb0\xc8$\xea\xce\x16\xa6)]\x0c\x1bv\xf6\xe6\xf3\xd0c\x06\xac\x94\x06\x9f86\xb3p\xa1>\x9f:\x14\xf3\xc4\x89\xae\x97\xd85\x9a\xd8\xf4\x9d<\xef\xbf&\xa5a\x96K2\xf61\xdbNf\xe4\x13M\xc1\xbd\xe1\x1b\x12\xca\x04\xdb|$/\xb77\xc4\x1f\x0e\xac#7\xb8\xee\x9a\xbfn\xeae\x0f\xfb\xc8k\xdb\x92\x85&\xd1\x98\xd1\x0ej\xb4\x03r\x0b\xef\xcc\xc3dO\x1a\xa4$[\xd2$C\x1b$\x1b\xacT\xb4\x1d\x1f\xd2\x80.I\xe2:?\x8c\xce\x1dq/e\xc86\xe7\x0d\xc6\x18_\x8c\xe7a\x9a\x91|\xa7\xc8\xa7\x83\xef|D\x89/\xd2\x9a\x06\x19I&.#@\x8fGE\xa9>\xf3\x08Jb\xd3\xb1\xef\xf5\xc0%\xfb\x92\xcb\x06}\xe0\xf1\x18\x83\xafS\xba8\xc33D\xb6\xcf8e\xdf\x9d\x9ek\xd3\xdc\xa7\xf2v\xfc\x93'\x90\x97\xc6 !\xa8\xe3\x95y\x9e^\x94uIg\xdap\x1d\xc7\xf3\x82+:\xb9\xf7L[x\xa2\x16L\xa34\x93\xcdc1\x13\xc4k\xdb3\xa3\xc7\xf7\xfc\xbc0G\xe9oW\\\xb1\x81\xa1\xb8\xbf\xe4]l\xb6\xefw\x81\xde\xc8]7\xd70 \xd8v\x8c\x00\xca-\xads\xe2~\xbd\x9d\xdd\xcc^n\xcf\x80\xa2\x8f\xf0\x0e\x06~k\x0f\xd3\xf5\x9c\x97\xdb\x1b\xb3\x97\xdb\x1b\x0c\xfck\x03#$\x01\x86\xdb:\x13.\x19.j\x91\x18\x82\xc9\xbd\xe62\x82\xbe\x9e\x9d\\\xdczW\x97/\xb7Qo{\xb9\x1d-f\x90\xa5\xe3\x1dg{\xa3\xf1\xe6\x0eh\x82^\xf2;aL\xd2\xdc\xdd\xf266\x9c\x97_{\x9e\xa6\x83\xc0\xd4T\xae7\xed\xf3N\xea\x11o'\xb6\x07W36\x86\xe7\xa3\xfe{\xa3 \xd4\x1f\xc5Ir\xc3\xde\xf9\xe7\x9fl\xd1\x12\x1f\x8e\x82\xb3\x1fO\xde_\x8e\xde\x8c\xb8\xac/_\xec\x9f\x1c\xd5_\x9c\x8f~=\xf7\xbb\xa9\xa1\xf1\xf9\xa3\xe0\xf5\xe1\x9b\xf3\xd1\xe9\xe5\xde\xfe\xfe\xe8\xed\xb9y\xf5\xd5s.\xd5\x8b\xb4\xaf\x0fWFE\xa9\xfd\xee4\xb4\xdfs\x8d\xf6{\x8e\xb1l D\xe8U6&t\n\xe70\x14\x07\x9d\xa6\x86\x88\xa6!\xc2\xd5h')\x16W$UM\xdd\xa4<\x02\xe2\xc7\xba-\x9f\x07\x0ep\x1c.\x0c)O\xf5\x88\xf9\xd8\x12\xb3\x1a\x973\x9b\xcf\xcf\x17\x04]+\xd8\xff\xc1\x94\xa6\xa3pN<\x95\x0c\x8eQ\xfdT\xdf\x9cb\xe8/\x8d\xcfJ9\x7f\x86 \xce\x03\xc6\x99\xf6\xab\xe3 \xed\x91H\xaer\x07\xcewJi/S\xfb\xf1\xb1\xb3\x89R&\xb3@f\x8a`\\\x05\x969\xe1\xb9\x1al\xf9\x7f\xa5\xf4Q\x91m\xddA\xa7{J\x8a%M\x1a\x13\xc2\xe7\xa3\x83\xfd\xf3\xf3\x8e!\x18\x8eH\xe4\x13\xc61\xbd%\x93\xf3p\x96\x0d!\xb1\xa9f>\xac%\xe4\"\xfd\x80\x01\xff\xd8\x1f]\x8b\x80\x8d\x80\xab\xb2k#\xach\xc2/ \xa2$#i\xbe7\xf9\x18\x8eI\x923&\xdeG\xc4\x01\\i\xed\xba\xae\xb37\xcdI:Bg:\x06\x90p\xc1\xe0\xb3\xc9\x94\xcd\xf97c\xadk\xff]\x9b\x12\x1eT\xb0%\xd3\xf0\xd7\xca1]\xf9C\x0f\xbb\xb6\xb1\xbd1\x0br\x92\xe5.Q\x97\x10\x97\x0eV\xd2\x9d*M=\x18\xc74\xe1\xaa\xa0m\x03\xaba\x99'9\xa9:P\x06\xe8c\x1d\xf4\xc1y\x12\xe7/\x1c\xcf\x93\xa6*\x99\xeaA\xdd\xf7\xb9\xb8X\xfeS\x1fO\xd9\xde\x0f>8\xc0$G\xf9\xe2+\xfe\xc2\xafW\xa8\x82J~\x01,\xa8\xdf\xdd\x81\x84\x0d\x93-\xe2\x90\xd1\xa3}[\xddZ\x85\x0b\x9c\xae\xc8\x05V\xd6\x07\xedpiO8\xda\x13.\xea \x17\xf6\x84+\x1e\xcd\xf2\xca]\xbe>;<\x82j\xc5a\xba\xb6>\x86\xf4v\xcc\x15\xdd\xc3\xda\xe4\x1b\xb5.\xa0\x89\x0e\xfa\x970.z\x82_\x13\xb2d#\xd2\xc7ki>\x82\x15T(\x18\x0253\x04\xd0\xebJ\xea\x83\x8ebl.\xc2\xd2\x11\xac@_\xd6n\xb4\xc8\xec\x92(k\x84\x17\xc5\x07/H\xc2\x05\xf1\x91\xf4\xf2\x00\x0f\x98\x82<\x8d\x16\xae\xe7\xf3\xa0\x85u\xbe\xeaC\x16H\xd4\xf2\x04P\xfc7\"\x8f'\xeb\xc8\x02\x89\x1e\x91J\xb3\xc9m\xf7\x94\x18\x96hJ\xe6_W\x1a\x92\x07d\xb8\x85Q\xe4o\x87G?8\xca\x8e&\x05\x9d0\x88&\x1e\xd29\xfb\x8b\x13\x14w^\xab\xbc]1\xa0]\x10.\x97\xf1=\x1e.\xbf%.?\x8e#\xfcG\xc2\xff\n\xcbL\x12\x91\x07/\xa1\xe0\xbcA\x95PD\xb5\x88\xa3\xc9\"c\xc8\xc7\x90\x12Q\xf7\xa0\x93\xca\xe1\xf1\xdbw\xe7\xbaa\xf2\xbb\x0e\n:\xf0f\x1d\xb7\xb6\x0bs\xf9\x05E b\xad`\x7fy\x1eF\xc5\x8d\x92B\xe3\xc7\xa0{\xd8\xc8\xb0\xb9D3\xec\xc4\x07\xc7Qp\xd5\xd9\xa2\x9d\xcb\x83\x18\xaeB(\x18)\xf8\nY6\xf6d\xad\x1c(\xa7\x03\xfe\x9b\x0d\xcfM!J`\x8f\xfd\x8d\x7f]\x13\xcf\xe8P\xd9|\xd8G\x05#d\x04\x87\xff\xa4\x9dl\xcf\xc3\xa3\xb6'O\xe0\xdf\\\n\xa0^\x8f\x99\x079\xfb8P\xac\xfe\xebc\xaa\xf7\x1b\x18\x88\xc1\xad\x95d\xc0\xa9`E\"\x00\xd1\xcc\x19V\xee_\xa7\x1chN\xf8\x18+\xa4\x12\x82\xb4\xd3w\xcc\xa0\xb6\x86\x97~\x15RPn\x0eT\x04\xc1\x1d{\xaa,0\xdc\x80\xc8m7kw\xe4\xc2\xa4 |\xe8\xa6b\xf5\xc1\xb0\xa2\\\xe6\xfe\xd7g\x18#\xa8\xe3L\xaby\xea\xd5@\xf7\xea\x82N\xd3T\xf3i\xaf\xf8\xd4\xf3\xd5\x93\x01\xba\xb4\xc8h\xea\xb3\x82\xb8\x0f\x9d\x83\xb1\x97\xb6$@\xad\x94alb\xa5\x03\xa5\x03U2\x04b?\xd7\x92wM\xfa\xc8Tl\x13:b\xed\x99\xa9\x07\xf9}[\xa6:\xc3\x80>\x07'G\x0e7\x87\xb0\xc1\xbe\xc0\xef\xa6AB\xeer.X\xbf\xf0Z\x0c\x98W\x14\xa1B\x92R\x18;&n\xc2\xb5\x9a\xa4\xd4\x8f\x14\x8d\xff\x049CU\xe6\xf9p\xcajX:\xde\x9a ]\x97\xf5\xb3`\xbcxr\x17d\xa2\xb1\xbe'|}g\xa3\x8f\xf4\xddG\xf2\xee#u\x87\x1d\x924f#\xe4Qqa\x07\x9c\xdf\xef\x9e\x8d\xd7\x06\x83\xdf\xef\x9e\x11\xc6\x88K\xf3\xceZ\xa5\xeb\xe3\xdetH,\xf7\x0b\xa0\xed\x0b\xab\xd4\x0fr\xcaO1<\xc8\xe7)\xbd\xc5\x83\x1d\xa68\x8e\xd2\x94\xa6\xae#\x8b!\xca \xa19\x84%\xf2M\xce\xb0\xe5\xf7Z\xbd\xc5AU_t\x19\x0b\xd7~t\x12\xa5\xf9}\xf5E\xde\x90\x0f\xe1\x15M1N\x8d\x81x\x8c(]\xab\x1d9t\"J\xb5\xbd\xde\xbb#\xecp\x98GcnHa\xc2\x8a\xce\xec\xd2\x84\xeb\xb6\xe6\xe8\xec\xb1\xa55\xac\xde\x9c\xdb%w\xb2\xf6\x04\x19\x18\x1a\xa8NtV\xdd\x1b\xc1t\xb3M>f\xcc\xcf\x91\x9a\xf7\x08\xba\x916/1\xd4M\xdf\x1e\xf0,\xbb\\HK\xf8\x19J} x\xf5#\x06\xc5a\x98\xed\x04k\x9b\x9eW\xb7w\xbf:9\xf8M\x88\xcb\x95\\\xbd\xcb\xf7J\x18B\xc2\xb4\x03\x92L\xf8\x99Xj:$\xb2\x0bdH_\\\\_\x9b\xe0\x7f\x03\x99-\xb8\x14N\xb6\x1d%\x7f\xb7}\xd5\xac\xc9\x91\xa3\x80+\xea\xf0^\xf3\x9b2\x06W \xfd\x14\xf0\x93\xe6\x13\xb6}\xa3\x95\x8b\x1f\xef\xe9{P\xdeC*8kJ\xbc\x17\xb8\xef\x15u\xae\xc2\x0dL\xb4\x86h\xca]x\xd8T\x1f\x13\x97rnB\x8d\xdc\xe4\x80T\x85\x9c\x9dP\x91\x8c\x98\x1a\xfa\xc60\xb3\xb0\xdae\x18\xc4\xacCG\xc1\x11\xb2-\xf8'~\x9e\x904<\xf0_\x80\x8a\xa6\x17\x1e\x845\x02\xe9\x81C\x90\xf4\x82A\xfb\xcd0b^\xef\xb9V\xc2\x80\x7f\xe3]:\xf3e\xaaK\x1f\xc2\x15&Z4\x88G\xb3\xea\xd9-#\xf2\xd2\x94\xd8\xaa\xf9\xc0\xd6dF\xf2}\x9aL\xa3Y/\x1b\xd8\x1e7\xd2r\xdfdMly\xd6\"\x06\x8aj\xb7ij\xb2rW\x95.\xcf\xfaf\xc3\xc9\xe4GJ\xaf\xfb\xf2\x7f\xfd\xd9\x03\"\x1c\x8f\xa3v\xf8\xa9\xd4\x9f\x7f\xe2^\x84'Sh\xc6\xcc=\xcdU\x8cj\xf3ju\xc1\xf4\xfd\xda\x99\x97^\x90n4\x9b\xad\xd4\xae\x1c\xc5\x85F\xa7Q\x1a\xde\x8b\xe3V\xdb\xc6\xa6\xd1\x0fW\xdbZ\xed\xe5\x832\x16\x9e\xce\xb6\x0c\x8b\x9c\x8a\xa2G\xc5W\x16\xfev\xfcpS\xdeSvs\x1f\x9c\xcbK\x92\x1d\xd1 \x0f\xd3S\xef\xfc\x0d7\xe0\xa9\xa9\x02\x94\xd5)O\x8cb7q\x9f7o\x15PQ\xf0\xb4Y\x10\x89\x82g\xcd\x82P\x14|\xd3,(D\xc1?\x9b\x05\x99(\xd8T%f\xf6b\x8b\xbd(\xdf\x94:F\xdc\x9ey\xf5\x06, *T\xe0\xe9\xb1.\xa8\xaf\x88\xaf\xd6\xf4\x0dlF\xd8\x05\x81\x9f\xb1\x95\xee\xca\x9e\xe5\xb6k\x9e\xee\xa6\x0f4\x10\x1f\xf6\xdc|\x1ee\xdc]\x95\x15\x84\xcd\x027\x0f./\xd1Twy\x89\xccb\xd3\x87T\x01\xf2;\xd3\x88P\xd0%\xbb>\xba\xaf\xab\xe0\xc5\x82\x93\xb4\xb4\x88\x99 \"[/\xaa\x8554]\xc3\xe4`lM\x0dM7<\x01\x0f\x0e3z6\xa7\xb7f\x92[Zmh\xe6\x01,;\x87\x18\xf7Et\x94Li\xba\xe01 ;\x88\xc2\xd2\xa1\xb1\xeds\x0bz\x15\xc5d\x08[OWm\x96\x8aqz\x96\x91N:q1\xed\x84\x98wB\xc4rg\xf8D\x0cXx\x08\xc9\xaes\xba|\x0c\x9a\xc2\x1eh\xfa\xaf\x1e@Q\x0e@\xa7\xb3\xd5\xde<|\xf0|\xe5*\xc2\x83[\xb5Y\nS\n\xa3\xcbe)\xec\xc0\x18\xdf\xfe\xbd\n\x8d\x0fy\xf0SF\x13\x14\x15\xc2Kn\xa1D&\xad\xbc\xbd\xa24&a\xd2|\x8d\xe1\x03\x9b/\xb9\xe9\xb1\xf1\xf65M\x17\x1a.-u\xa8{\xa6*\xb5T\"*KZ:Q$JZzW(\xab\xe8\xb4\xa8{\x9d\xde\x95\x89\x82\xd67bQ\xd0\xd2\xbb\xb8\x94\xd7\x14\x88\xa6\x08>n\xbc]\x8aF\xb6\x9a\x8dp\x01\xed\xdb\xc6\xdb\xb9\x04\xdfj\xf5\xf3F\x16\xb5\x86\xb6\x90%\x9b\xdf\xb4\x061\x13\x89\x8a\xb5\n\xe1\xfd\x97U\x08\x97\xe5\xba`=\x08\xa2\xecT\x84\x85\xf6\x95\xa20\xb9\xf7\x1b\x90\x96bN\xad\x86\xa6x\xa1\x0f7\xe5\x9b8\xcar\x15\x82\x91\xb5\xedw\x98\xdc\xd7i\xf5\xaa\xe5*t\xa3w\xf2\xa1\xc9\xfe\xf9\x86\xb6]\xcd:\xff\x1c:\x7fK\xb5\x97:\x7f\xd6,\xd0\xe9\xfc\xaaF\xfe\xa9:\x7f\xac\xb4U\xe9\xfcuK\x80Q\xe7/\xd3J\x1dD\x93#\x1eG\xb6\x05\xf9\xd7\xa9\xff\x93([\x86\xf9x~\xc8t\x860\xe6\xceP\xc6:\xdc\npc\x07\xe2^\xd2\x92\xc0\xf5\x1a\x17\x1aCS7\xe9\xe4\x9d:\x16\xff\xf7\xd9J\x90\x84\xbb\xd0\xc3\x97Z\x17~:\x90\x18\xd5\x90h\x91\xd8W\xb0\xcb\x14\x08;5\x1c\x0e\xe4AN\x7f\xe2\xd7\xaa9{g?]\xd3a\xbb\xf4\x8b\xb4|.F\x17\xbb\xfc~i\xe9\xfe\x18a\xb8\x9a\xbf\xe0\xa6\x80>*\xa9\x0f\xb4=\xe3\x06\xc6\xd3\x06\xac\x9di6c\x02\xfa\xb88x\xa8\xc5\xc2\xe3\xf9\xaa7_\xc0\x18\xb6\xa1x\x01\xe3\xf5u\x0f\xe2\x8b\xf1\x07\xb5\xe6\xc5X\x13kQ\xc6Y\xc4S\xe5\x1d\x03\xf3\xc3=\xae\x93\x01\x8e\xc38\x16\\\x90\xf8p\xc1\xea\x96\xc1$\xb8\x9e\x96\x96\xdbQ\xaf\xc3\"\xe9\xae\xaez\x8er\x92\x17\xfbh \xa2`\x92\x80G\xec\x0e\x18\xa0\x88\x81X\xbeC\xba4,<\xd1\x9a\xec\x15\xe3\xb2\xf2\x9d\x90\x90\xb4\xc7Sl\x1c\xa3\xa4X\xac0\x16\x81\xe7\xd6\x17\xf5\x1f@\x9bvK\x14a\xf4\xf4%\xe4\x89\xbf\x81/\xf6c?+\x08\x0f]\x8c\x96\xf6b\xb4\x9c\x87J\x99\xb8\x8b\x87N\x08\x8f\xf3d\x8c\\\x07\x82\x85\xa6\x01I\x8a\x85\xd92\xcd:G93\xdd\x15\x7f\xb8\x1e\x0c\xf1\xac\xb7\xe82U#Ou\x1d~\"c\xf3s\xea`;V\xbe\x02u\x8b\x1a\x95\x91Jw\xc1\x89\x12\xcc\x07\x84\xd7\xab;\xee%`\x90\xa8Zm\xda\xa3\x96\xb8\x9b\x80\x82ff\xe5]P\xd1\xaceF@\xb69Z,\xf3{q\xa5b\xcd\xc2\xa2\xa0\xc6\xcb\x90\xc8\xd5\xfd\xc0X\xcft\xbb\xd3\xb8\x86b\xdc\xfch\xba8\x08\xf3Pn\x80\x11\xba\xbb\xaf\xb9\xce\xeb\xb2 JD\x0c\xda\x8e\x83\xa3\xdcu\x0e1\x91\xa4]\x10\xa9\xed\xb7b\x8b5Q\x89\xd5\x82\xc6\xea\x0eEs\x96\x9e}\x12\x1d\xadNC\xad\xa9\xeb\x92\x90e~\xaf!\xc4\xfa dk\xd3\x84\xa0\x85|\xdf\x03Q\xcb0\xcbni:\x91\xb8\xe7R-CFU2\x94\xb9\x07\xffk\xf0\xd9\xbd\xc2\x16Q\xf2\x06[\x1b\xda\xfcK'\xe4\x8a\x16\xc9\x98\x9cG\x0bB\x8b|\x08\xcf\xbe\xb1@+\xa1\xe7\xacb\xe9_0\xdb\xad\xd7\x9fU\x02\x95\x16\xcf^\x02(1\xdc]\xef-dJ\xf3\xe8c\xad\x1e<\xae\x06Bc_\xcc\xd1\xf7\xf5\xc2\xdf\xaa\xf2R\x1ady\x98\x0b!\xc0(\x9c\x1d\xe6D'\x9cY\x1c\xae\xd2 #\xf9\x19k\xba\xba\xdao\x8d\n :hg\x91ri\x88Kj\x19\xc9\xb98f\xacd\xf2\xefW\xb0g\x184w\x98b\x03\xef'\x8fj\xc6k\xbd\x1f\xb0\xcax\xe5\xa5<\x11\xce\xe4/\x19o8\x994\x07\xbb\xcaX\xfb\x04\xc4\x10T\x06;p\xe9J\x8a\xeb\x12\x8a\x04\x06\x048w\xcaslau\x1e\x8d\x80\xd5U\x10\x0d\x1az`\xa1\xdfx\xff\x82\x01\xe2B7^\x9c\x15\x1f\xaefF\xdbH\xed\xe5_\xa3-\x95\xd6\xd7\xf7Q\x1c\x9f\x921\x89n\xf0\xb4,\xeb\xa1@\x19\xe7J\x92\xde\xda\x8e\xd0\xa2\x94]\x8f\x89\x7f\xfc\x9d\x9cN\x9bB\xa0\x92\xa3~*:\xf9\xd9\x17\xb2\xa0\xdau\xc4>\xba$?=\xec\xa7KR\x84\xedV\xed\"\x84\xebR'C\x84\xeaR'\x0b\x842\x99OC\xbc\x11,\xb4\xbeP\xd5\xfa\xec\x06\xd4\"\x88\x92)I\xb9\xf8\xe0FA\x94\x93E\xd6\xedhV?Q\xe9\xe1s\xf6\x8ag\xf7\xef\xf0\x1f\xcbP\xb7\xb5\x88W\xd0\xa6h\xb3&\xbc\xec\xd2v\xe7\xd2\xd3\xed\x13\xb5\xddy\xd7\xc6\xaeH\xd5\xe1\xeaR5T\x92\xb5R;\xecQKf\xdf\xed\xbe\xb7/\xd6\x9c\x85\x96\xa1\xad=\x1b\xa2\xbf\xd7\xa0kz1\xfd\x9b\xf5\xe2\x8ey\x14\x0eW\xdc\xedc\x8dGC\x99\x04\x98]\x91\xfd-\xfet=\xd8\x86\xad\xea^\xca$X\x84KE\x10\xf2\x81v\x11^$\x84\xe6\xb4n\x96\xcf:.\x96\xc9\xd9\xb75\x0f\xe2\x13K\xdc\x10xZ\xd7\x9e\x92\x8b|J \x06\xaf\xf1\xf0[/\xd6J\xb6p\xab\x80'\xeb\x82j\xe5\x9d\x8f\x8b\xe5\xc5\xe6\x07\xbe\xe3\xc1:P\xcb\xdd\xe4\xce{Y\x1dsi\x1f-2\xa2\x0e\xa2T}\xbf>f4\x19\xf0\xed|\xc0\xf4\xeb\x01\xdb.\xad\x0e\x81\xa6\xeeY\xdd\xcd\xa0\xfbd\x05Z\xa7+\x1dF*)]\xf7]\x81\xfd\x04{\xf9\x94$\xa3\xaaO|)\xd8)\xc7\xde\x1dy\x9e\x13Y\x96\xbf\x19\xc7V\xf3\x124\xa6\xf6*O\xe0*O\x06\xd9\x02\xb4\xb3<\xe0\xfaH\xc7\x86K\x93\xfd8\x1a_\xf7\x10^\xd4\xa7\xc4^\xa5\x87\xb9]\x88\xb3\x11\x9d\x03\x03pL\x9e\xa8^\x90S~\xf4\xf3X\xd4\xad\x84\xb6p2\x01\x07\xd6\xab\xcd\xab\xc1\xf8\xb8\x1b\xa1\xf1[%B\x91#\x08\xbdM?06\xee\xbd\xc9\x04\xd8g\xb5\xc3\xef\xb4\xb4\xbc-R\xb2\x8a\xb5\xa5r;\xebeo\xf9\xdf\x81\xdf\xca\x07~\xabj\xa9\xff;(\xd3?\x7f\xd1AY\x97\xceB{\x1d\xa7\xd5\x0f\xca\x0c\xa7\x0bx\xf2%\xf4\x9b\xb4\x9f~\x13\xf69\xcc\xea\x10#\xc2\x9e\x1ba\xba\xbaX/Dz\xa5f\xda\xcfX.\x82\x08$\xb6\xdbFuA\x9d\xbb\xc6MS\xba\xf8\xe9\xccs)jYx\xff\xd3\xc9S\x9e`e\x1a\xc6\x999\xe1\x0b\xe8\xa5\xf9\xb2\x1d\xdb\x81\xd7\xaaB}\xb7I\xe1\xd3L\xe4\xa5\x07\xf1\xa3\xf7\xec\xde{\xb2\\\xa1\x9fl\x1f\xb7X\xc6\xd9\xc2\xc9H\x8esrN\xcf\xc2\xc52\xeee#\xaf\xbc\xbb\\\xf6\xe5\x19\xdb\x1cxm\x8e'\xcf%5w \xfd\xdd`\xa2\xb5\xcb\x1bEF\xd2\xf2\x990\xb4:\x0f\x93ILNVi\xfb\xa6\xccw\xdc\xed\xbb\xa1\x0c^\xe7\x03\xe8\x1b\xbd\x85\xe132\x80\xcf\xe9y\xb9V1\x81\x86\x9dO\x9d\xc3\xf2e\x9bdtw\xb4\xeb8\xf8B\x86\xbc\xffbN\x96\xbb\xce9\xb9\xcb\xf7R\x12>\x92\x9b\xd4\x0c\x0c& \xda\x93\xe50R\x9b+\x06\x04c\x1d\xf6\x08\x9e\xc4\xd8M\x16\xfda\x0d\xcfkF\xbddX\xac\x05d\xc3\x1fi\x94\xb8\x8c}x\xfd8\x97EGm\xb0\x89\xfa\x06\xa0\xad\xf5(w\xbe.\x11\x1f\x81\x1fu\xe3E\x1e\x86\xe2E\x87\x7fz\xc1\x818\x91F\xa7\x89\n,\xad\x17\xf0\x10\x92\xb58\x02\x8f\xef\xc2g\xbdt\xd3\xec\xa6\xe9n\x8c\xf8h\x98e\xd1,a\x8c\xcc.\xa6\xd7\x92>o\xf1\xfc\xceMuE\xe4y\xb6\xef\xf3\x95\xa6bJ\x03]~\n\x03'&=\xf3\xc2c(8\xb4Ta\xac\xe9\x1dH.R]\xa0\x89\xd6\x1b\xc9\x90\xeb$X\xa7x\xda\xc5\x9aK\xd1\x83XO\x9ck\x19\xfe7_@\x02\xdbj\xa2\x7f3\xf6@\x99\xb9\xfc\"1`\x0e\x90P\x99tG\xd2\xf0\n\x05\x8a\xdaO\x91|,e\n\xdb4\x9a\x15\x12hm\xb3L\xda\xc7P\xce\xe3\\\xa6\xc1m\x1a\xe5%D\x99}\xaaI\xa7\x845xM\xee\x19\xfe\xf5\x0b\xbe\xff$\xa8\xd6X>\xa1V\x85\x91\x07\x01u\x15\xd2\xe0\x99\xc3R\xf1\x9eG\x07l{\x157\xb6\x9b\xe6\xc5r\xa6\xd8\x14<\x02F\xbd \x14\x05[\x9b\xdf|\xab\x0f\x86Q|\x91\xbbOn{\x99\xf7\x92\x8a\xb5+{\xad\x9f\xb3\x04\x8f\xf5T\x8b\x80\x95\x9b\xc2\xa1\xed\x87IBs`\xeb\x12B\xce\xfb \xccj\xa1\xd8\xdas\xd2!\x90'}\xbd:\xb0\xa3D\xed\xd9)\x99\x92\x94$\xe32D\xdc<\xca`\x1ef\xc9\xd79\\\x11\x92@\xc4\xaf\xb1D\x19\x99\xc0\x00\xb2bIR\xd7\xabA\xb0\xa1\x90I\x87\xf8\xb0\x86\xc7\x0dJB\xc9Z\x10\x1fm8\xbb\\P\x81\x86F\x0d\xfa\x86X\x843\xc2\x98\x1f'\xfa\x93i\xcb-\xc7\xa2y$\xab9d\x93`I\xd2,\xcarSX\x05\xc9\x14\x92\xee\xd3\xbdd\xa5\xe3kU\x1f\xd0o,=s\xaf\xb0\x1e\xd2~=dO\xe9\x06\xf7\x92U\xe1\x82x\xe9\xcd\x86\xe1\xaa\x12\x9aGS\xbc\xe68,\xb7oxYU|\xf2\xa4\x02J\xf1\x88\xa8G\xbe\x066\xd8!\x08p1\xf8\xaeZP\xe1\xcb\x92\x91\x0e\xf4\xeayUd29\xb7\x89\x12\x13-%?\x93\xfb\x03zk7\xa0\xca\xa7\"\x0f\xa9C\x8a\xda\xfa pFI\xceS\xc20\xf1\xfe\x9a\xdcsdNi:&\xc7\x12\xed\xbe\xc85e0\x10\xb2.\xbe\x8a\x8b\xf4\x91\xfdcUM\xf4\xbbb?\xb8\x86\x80\xf0\x11\xe9\xd7\x1f\x1eQs\x1b6\xbd\x92\x86\xba\x84\x0f\xf9\xc8\x05^\xc4\x06/F\x83V-\x03\xfc\x8a\x84=\xb5\x0f'\xc1\x84\xf2\xf1Z*\xdb\x97^.L)\x8a\xed\xa5\x1b\x0d\xf2I\x82(\x13\xbc\x8e\xdf\xd1a\x02L\xd5)\xab\x9f\x19\xdb\x07\xcd\xcb\\\x87\xddGtg\xd3\xd7\xcf\xbf|\x90\x0e\xa6q\x91\xcd\xfbN#TS\x99\xf3\x9a\xb6\xb4\x13Hf\x8c!\xc7\xab\xb4\xafEk.\x1a\xb2}NOXz\xea\x97\x93\xd4\xa7cI\xc3\xc4$\xce\x18D|Z\xe5r\xad\xfeS\xca\xba\xec5\x9f\x98_\xa0\x86\x03\x1b\xc6J\x0c\xe3^$\x91d&--K\xec8\x81\x04\x0d\xb31\x7f!Wx\x14E\x9e\xa4\xac\x08\x0c\xa2X\xfe\xfeR\x0c\xe8\xf1i3{\x07\xdf\xc1\xa9\xee\xe5\"(\xdd\xe6\x98<\xd6f\x8c\xd8\x8en_\xa9Aj\xcd\x87\x9d\"\xa81r1\xb2\n\xf4=A\x07?\x83\xe8|\xc6\x84O w\xcb\x94d\x19\x93\xda\x17E\x96\x03\x89\xf29I\xe1\x8a\xf0\x06h\xaa\xc8\xd2>\x06\x1dv`\xbd\xfc\x90\x862I\xa5\"U\xba?\xe7N\xae\xc8\xdb\xa8\xe8Pz\xd4\x8ei\x92\xe5i1\xcei\xaaS[\xe4#g\xc0L\xef\x95F\xda\x8e8\xa0>R\xff\xb4\xbbA\xa9\xba\xec\xd0\x94\x8cICK\x92{\xbb\x02\x1bYM\xa2\x86]\xd0\xbe\x17\xf3>DUN\x8a\xe5l:\xeb\xa4\xc3t\xcf\xf2T\xa0a\xbd\xf2\x81\xf630\xbf\x8f\xe2\xf8S-\xcch\x95\xab\x8b!\xaeb`n\xdc\xbf\xe8\xb2\x97X\xac\xc9\x7f\x89K\xac\xdcH;\xb7\xd0D\\\xc6\xab\x8dF\xbf}\xe2\xe8k\x8b\xff\xcf?\xcb\x8c\x85\xb84+g[\xc5\x01\xb7Q\xd2[\x8f1\xddi\xf6!\xa9<}\xb5\x93Q~\xac1}I\xb7\x01\xb5\xe74\xbdK\x16\x9f\x83\xbc\xb8t#{k\x92Xzw\xf1o8\x97\x10\xb9\xbe\xec\xf4\xe5*\x91\x15J\x8a\x04R\xb1k\xbfM\x82\xec\x95\"\x9b\xbc\xbaG\xf5\xc6\xe68\xc3\xa3-TUNP\x1f\xb1\x9c\xef\x8a\x90\x0fB\xab2\x03\x16\x02\xd0\xde\\\x86PQ\xb2,\xf2S25\xc3\xc5}\xcd1\xf2\x916\x9c\xff\xf4I\x1aUZ\x7f\x89\x07y\x19\x96<\xf5\x98\xb8\xb3\xa9XA\xec&aR\x9a\x84\x13n\x12\xc6\xac\x85\xf6\xcfK\x1d\xca\x08\xf4\x80~/\x8e\xa0\x18\xc7\x07G\x12\x85S\x1aQ}pJ\xa2\xc0d\xd1u\xa2\xc0\x83\xfb\x16Q4\xde\xf2y\xe7\xed\x8b\xb9\xe5?\xe4k9G\xd6\xd3\xffqG\x0cKt\xf3\x86]\xcb\xdc\x95_/\x1d\x01\xc4o\xfd\xbe\x06C\x08\xfb\xb6g\x88\x17\x0eC#\x910\xba\x98v\x0c\x89\x95\xd3\x8e.0\x1c\x96\xe3a?\x8c=)z\xb5T\xadB\x99\xba\xb4(r\xaeueb\xe8\xba\"\xf3=\xd8\xd6\xdd\xd7\xad\xcd\x06D{\x93h\x8b\xc2\xad-\xa3\x0d\"w\n\xd9\xc1\n\x97\xf8W\xc7\x99\xa5\xe5\xae\xa0\xdc\xd3\x9d\xd1\xdd\x92\x8cs2QM\xfcmBIa\x07\x8e\xc3\xe3v\x01cz\xce\x85\xf0\xf09\xbb_\\\xd1\xf8\x83\xa6~\x04;\xb0\xf1\x7f\x7f\xcf\xd6\xff\xfc=[\xffjc\xd6\x86\x08\x11\xe2b\xb0\xfea\xf3\xeebs\xf0}8\x98~X\xffjC\xe3\xe6T \xe4\xe6\xd5\xc5\xe6\x96\x01\"\xe3\x10\xf4bs\xf0\xad\x01\x841A\xcc\xad\x7f\xa8\x93\x1d\xd8\xde\xaa\xa4f\xa9\xe9\x81B\xe7:\x11NM;R'\xc3\xd7\xed\xa6\xa6\xfa\xa62\x12OY\x0d\xf5\x7f}\x9b\xac\xa4\xdd,\xdb\x80\xc6x\xf6\xcb\xfey-\xe7\xd9\x91\xd6\xa7y\x949\x9e.\xec\xf2\xa4R\"+\x16,\xd3\xe4\xb4\xc1\xe7\xb0\x03Ga>\x0f\x16\xe1\x9dF\xac+K#\x8d\xf8\xd2\xef\xb6'\xef\xf028`\xdbNBou\xf2\xa7r^\x07\xea\xb9\xd8L\xaf\x7fH\xddC&\xba1\x1e\xa8\xac\xad\xf1\xac\x18\xb5 \xd2d\xddiz\xa7\xea{\xa3\x89\x9e\x08\xd2\xac\xa0\xc9\x97nK\xd3\xc2\xeat\xebX\xa2\xbe\x93\xe1\xba\xab5\xde\xed\x16\xd0hD\xa0BC\xaa\x066\xc0Z}\xf2\x04&B`\xf3@{i\xe5AM\x13\xa4\xb1\xcdc.\x15KF\xa9\x9b2\xa8PmBdF)\xdc\xbdQ\xe5/\xffF'U\x93\x17\x1a\xec\xc0\x8cm\x86\xbb\x90\xc3:\x8f)\xd6u\xc6\x0c\xcd\x0cJk\x9a)\xac\x12\xe6\x13\x18\xc2\xba\xe6\xf3D\xb8\xdc\xf2\x84~\x11\xe6\xf33\x1f\x97\x16\"\x1d\xb4\xe5,\x90\xcdp&\xc1`\x17bW\xe4!u\x9f\xa2\x86\xba\x0bOa\x08\xdf1l\x84\nX\x8a\xfdk\xd0\xb3\xfaK\xf5\x8ci0\x17\xed\xa1>\x1e\xd1\xf9\x10a6\x99\xc2\x87\x0c\x85\x13\xf4w\xd7\x0b\x1cSn\xb2\xd3\x96--e\x13\xb4\xd9\xebIH\x9fpLo\xa8K\xbc\xc6v\x02\xea\"\xbe\xea\xf6w\xb4\\_b|2\xb2Jv\x8ca*\xe9\xdbx\xa0\x17_\xa8x\xdcr\x9e26\xae\xa1Js\xa75\x91;\xe5#;M`\x00\xb1\xb5gJ\xc0\xbd\x98\x11W\xc2T\xb6\x9c\xff\xb5\xcdu\xb7%zB\xc0\x00\xc6\xac\xac\xad\x04\xd8\xfax\xdb\xa9\xf4/l\xe1\xff/k\xf9\xc6\x8c9\xca\x18\xd5f$\x17\x82\x99{\xeb\xf7\xdc\x05K_V\x18\x80\x8b\xb8\xea\xbe\x9c\xba\x84]\xb8q\x13\x1fBYi\xec\xa1\x05\xdf\xb8a\xae6\xab\xa3\xce\x9d?S\x08i\x02\x98\x1dk\x17\xae\xf89\x82\xdb\xa4\xb4b\xb5\xaf\xdf\xf5\x99/\xf3JHx\x1c\x06\xcb\x8cR\xd5\xa5\x8c\xe7\xe4\xe2.\x10L63EJQ\x1bP\x086\xf3\xdaV\xfe.\xb3\x86\xa80\xe6_k\x13N\xee\xf90\xad\xf0\xa9W\x14\x01g\xd6F,\xe2^\xb42c\xed\xcf\\\xb9\xa6\x00\xfb=\x17l\x86b\x8c\xaeq\xcf\xd7\xf4\xdc\xe8\xc5\x95c\xe4\xe8\x1ccbn\xfa0s\x85\x15\x06\xf7\xec\xb54\x88 \xe6f\xe0Y\xb0]\xb6[;\x8b\xf0\xee}\x18\xe5\xdc\xfd\x8cq\x98\xb9{\xef\xa6\x81x-[B\xc3{\xe8\xe3&\xee\xe4i\x18\xc5\xc8K\xd1em\x17\x9b\x96/a\x08\x13L\xe0\xd7\xffhT\xb1\x00#\"0)\x98\xc4B&o_\xf1\xebG\xb1X\x15\xd5\xd2ic\x87}\xbd\xf7\xb9\xafn2v\xa1\x80!\x8c\xdc\x85kH\xf0U{\xa9\xb8\x87IW \x1f\x12\xf7\xd9\x96\xa8\xdc\xa1\xe5I\xe7\xc2z\xf7\x9c`#\x8c\xe3\xe0c\xe6\x0c\xe1\xf9\xf3\xe7~\xab\xb0\xc8\xe7\x1b!6\x9aq\xa8\xa7\xcf\x9e\xea\xa1\xd0\x88\xc7a\x9e}\xffL\x0f\x93\x92I1&i&\xc1\x0c\x1f\xccd\xe2! \xf7\x8d\x01nI\xc6\x83\xdb4\\\x0ej]|\xf6\xfd?[\xf0\xfc\x10)k\x8e\xa5\xdd\x01 8'\xf1\xb2\xec\xe9\xd3g\xed\x01I\xc0\xda\xb8\xbf7\x82\xd5\x87\xfe|\xb3\x8dE \xd9\x18\xfd\xf3\xcd-3(C@mH\xcf\x9b&\x06'\xd8\x98\x10\xb2\x1c\xc4Qr\x1d%\xb3\xfa\xb8\x9eo\xb61[\x83V\x06\xf7|\xb3\x8d\x83\x1al\x1c\xde\xd3\"\x97\xc0m\xcc\xd6\x80\xcb|K\x83<\x9c\xe1\x1c.I\x1a|\xcc\xee\xb0\xf2\xb7}+7+\xb6'~Bo\x93\x98\x86\x93A\x91\xc6r\x96\xbekA\x914\xad\x93\xc6\xd6\xd3v\x1f\x18\x10\xdeG\x18\xe4i\x98dS\x9a.H\x9am\xcc)\xbd\x16-?mO\x95\xa1R\xedGB\xf3\x01\x9d\x0eP\xc9\x16\x0d\xb5\xc9\xa3OC\xcb0\x0d\x17$'\xe9\x80&\x84Nec\xed\x89\xeb\xd3\x18\xd3d\x96\x03\xe9\x0e*\xdbj\xcf+kK]\x04[\xedE\xc0@\x1ak\xffi\x9bN\x19Ts\xe9?m\x13(\x8f\x9dP'\xcd\xf6\x8c\n(\xba\xccxV* \xd9\xee\x1c\xa7\xdb\xc6\xce\xa0YF\x02N\x1d\xea\xd36\xbd \xa8\xe6h\xdb\xd4$\x00[\x03n\x0f%\xa6\x8dm\xe6\xbb6Rh\x98=knn\xed\xceq\xa8\"\x9f\x0f\xc8]N\x92\x8cAo\xe0\x06\xda\xdct44\x83\x95\xcb\xe3\xc5l\x83\xf1\xa0\xabp|\x9d\xc9\xd5\xa7\xc1F\xb3\xce<\xcf\x97\x03\xd6\x01YG\xc3M\x9au\xd4\x89\xd6\x90C\x13\xbc\xda\x1c\xd8vQ\xf6\xad\x8dVs\xc5\x8c\xa7X+\xfb\xd8\x8d\x8b\x94\xfc\xbf\x82d\xf9\xe0\x8aN\xee\x07d\x12\xe5\xb4\xdc\x93\x9e\xb5\xf7\x04[\xed\xb2\xc3m\x8aiV\x13\xdd\xac\xb2\x1d\x95\x9fl\x13\xaf\xa1n\xf9\xb5\xf6\xb2\xc0\x1a5n\xf1\xcc\x80\xfc\xda\x04\x19F\xdb`\x7f\xcf\x0d(m\x92\xe1s\x03y \xe3Sh\xb8E\xbe\xedmJ[OO\xfb\x86\x8f\"\xb0\x82C\\HQN\x16%\xde\x0d\x0b\xa0YQE\x98F\x04\xd1\xd6Q\xa38p\x1b\x93D\x91\x01\xe3\xcd\x06\x16az\xcd\x98\xa1\xfc\xaea2[\xd5\xe8\x84\xc4r\x80\xcf\x0d\x84\xd5\xacD\x938J\xc8\x00\xaf\xb6\x859M\x07W\xe1dF\xe4\x97\x0d\xb4\xd6l\xa4df\xd5B4\xac\x89f\xcd\x1b\x9e\x02r\x90\xe5\xe1bYV\xd6\xec\x00 \xd6\x8aINjs\xb2\xd5\x1ef\x86\xb71\xb3\x8d\xa9\xc0\xdf\xd6\xf7m\"\x910\xb5\xad\xba=\xbd\x8c\x06\x9b\xdcF\xd3\x18\x83R[\xd2\xec\x94\x08\xd3\xe04\x9a\xcd\n\xc1\x1aD\xfeT#U\"\x9cF\x9c~\xde&k\x99\xd5\xeecc\xb4m\xc8\"\x8f\xe2\xba\x8c\xdc\x9e\xc4\x9b\x88\xdc\xd6`\x9e\x1b`RJ\xf3A\x94|$\xe3\xbc\xec\xdcw%\xa46]\x0d5^\xd8I\xdc\xa8fly\xd0\xd4\x8e\xda\xb5\xa5\xad9\xbd \x8d[Z\xfc\x06M\x0e\xeb\xb0U\xbb8S\xbf43\x8d\x92 ,\xf8\x0d\xa1\xaf\x1dX\x07\x02\xeb\xe0|\x1d4\x0d\xbdR\xd7V\xfa'\xff\xa2\xc15\xb9\xb7\xe6O\x16\x95\xc5\x11\x0e\x83v\x95\xcb[\x0f>\xd0 %\x19\x8do\x08St\xeb\x17\x1d)+\x8d\x98\n\xbe\xb5\xf9\x0d\xc7\xee\xc3\x07\xef\x1f\x0f\xde\x8b\x7fll\xfc\x1f\xc8h\x91\x8e\xc9Q\xb8\\F\xc9\xec\xdd\xe9\x9b\x9d*\xc3\xe1\xe0\xaaH&1[\xe7\xc1\"\\\xfe\xff\x00\x00\x00\xff\xffPK\x07\x08-\xe3\xb5\x97=9\x05\x00\xf7\x0c\x1b\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00 \x00swagger-ui-standalone-preset.jsUT\x05\x00\x01\x80Cm8\xec\xbdys\xdc6\x9a0\xfe\xff|\x8aG|w\x152M\xd1\xdd\xad\xc3:,k\x1d\xc7\x9e\xf5\xbb\xf1Q\x963\xf3\x9b\xb7\xa3UQl\xb4\x9a1\x9b\xec\xe1!Y\x13i?\xfb\xaf\xf0\x00 \x01\x10 \xd9\xb2\xb33\xbb5\xacT\xac\x06A\xdcx\xeec\x0b\x16U\x1a\x95q\x96\xba\xa5\x0f\xc4\x83\xdf\xfe\x00\x00\xe0dW\xbf\x92\xa8t\xe0\xf4\x14\xca\xbb5\xc9\x16@\xbe\xac\xb3\xbc,`{\xdb\xf4v\x95\xcd\xab\x84\xc0\x19\xff#\x10\xb5O\x81\xb8\x1e\x1c\x83#\xba\x91?\x9a\x93E\x9c\x12\xda\"\xfb+\x08Ws8\xe3?\xdc\xd9\x05\x0e\xe8\xb8k0g\xe2\xaf\xe0\xfc6\xbc\xbe&\xf9\xcfo\xce\xcb0\x9d\x87I\x96\x92\x0f9)HY\x0f\xa1\xec\xab\xf3\x87\x07\xb7\\\xc6\x85\xdf,\x89X\x8e\x9c\x94U\x9eJK%^\xd0\xe7&\xcc\x81\xc0)\xfc\xf6p\xf2\x87\xbaPT\x85\xd4\xcd\xe5\xca\xf4\x89\x17\xe0\x92Y~\xe1\x89v\xe9\x0f\xb1b'JU\xdavLG7\xcb/h\x17\xcaKl\xeb\x18r\xbfU\x9a\x1c\xc3\xd6\xa4]\xcc\xbb8\x86\xdf\x1e\x94w\x0fj\xa7|T%\x1dU\x14&\x89\x1b\x8b\xc1\xf9\x10\xfb \xfdJ=\xfa3\x81S\xd8\x1aK/\xea\xd6\x9anx\x9bi\xb0\x82S(}H\x83\x88N\x8b\xfe1\x87S\xf5\x10\xfa\xd0Z\xb24\xc8\xf8\xf9\xbc\xbf\x87\xf7x\x1c\x02vL>\xe4\xd9\x9a\xe4\xe5\x1d\xff\xb2\xbdBQ\x96.\xe2\xeb*\x0f\xaf\x12bY\x96\xb4Z\x11\xf1~\xdc~\x7fM\xcac\xc8\xd5\x15\xf3\x9a9\xd29\xa4\xca\x1c\xf4\xd1\x8b\x13R\xd2\xa3^\x06\x97\x97\xa4x+\xeeK\xeb\xac\xc9\x8f\xd8 :\xd7\xb0JJu\x0cp<\xec\xeb\x01{\x9d\x06s\x97\xf8\xe0\x84\x0e]d\x1f\x88:\xbdL\xdf\"\xbd;\xde\x0c\xdf\x99u\x9e\x95\x19\xbd\xa9\xc12,\xde\xdf\xa6b\x8f\xd8i\xc2\xef\xd5\xf6\xd7p\n\xce\x93y\\\x94\x8e\x0f\xa9\x9b\x06\x14pL\xc7\x07\xac\xda\x83;\xd3\xceG*\xf7\xefT\x05\x81\xa2\xcc\xe3\xa8tN\x94[\x99\xc3)\xa4\xee\xfe\xd4S\xf7\x94^\xa8\x99\xf39N\xe7\x8e\x0fNN\x8a,\xb9!\xf4\xcf(K\x8b2\xaf\":\n'N\x8b2L#\xf2~A\x7f\xads2\x8f\xa3\xb0$\xec\x935\x05\x1b)\xd6\xe3[s^\xde%\xf8\xb2\xa0\x7f\xbcH\xe2\xb0 \x85s\xa1\xf6\x9ca\xcfE\x14&a\x8eu\xc9_+\x92F\xf8\xdd*\\\xaf\xe3\xf4\xda\xb9h\xe6PJ`\xb4s\xf9\xe9dS\x1f\xaa\x936\x9c\xa1\xb7\x8c^\x9a\xdf\x1e|\xb1=\x9f\xc9]\xe1\x12/Xd\xf9\xab0Z\xbau\xd3\xadvE+;\x138==\x858\x88\xd39\xf9\xf2~\xe1\x12\xcf\x83r\x99g\xb7\x90\x92[\xc8\xdd\xef~N?\xa7\xd9m\n\xd9\x1a\xa1\x9e\xf3\x1d\x8c\x80\xc0\x08\xbes .`EJ\x88S\x06\xd8c\xac\x90-X\x9d\x92\xd5\xf9\xcb\x8b\xb7?!l\x0f\xbe\xf3\xb4\x8b\xe6\x03\x05\xcaA\x19^3\xc8\x81\xbf\xe8\xe6\xd1\x99\xb1?\xee\xef!\xad\x92\x84\xbf\xe3\x1b\x8a\xaf\xc5\xdf\xf7\xf7\x83\xae\xca\xd6X\xed\x9c\xb7X\x9f\x0bl\xb3\xf9%\xb7\xda\xba\xf4`\xbd\x81\xbc\xd5\xe6\x80a\xb3\xd2Ou>\xf5\xd1\xc3j\xcd/}\xd6\xfcL\xf2y\x8b_j-\xf9\xb0bE\xa5@\xad+\x1fd8\x057\xc5\x0f\x94\xd2\xfa\x83\n\xf1\x9f\x8f\xbf`\xeb\xf4\x14R\n\xea\xe4\xf3\x96\x1a\xce\x9bq\xcd\xd2Yy1\xf0h\xd2\xa7\x9a\x9d\x97y\x9c^\xbb\xc4\xa3\x18\xb2lUzh\x1f\xa8\xca\xf3\x81\x1f\xe9\xac>\xd2\xf5\xb9\xb2\x1dm\xd0F%\x1e:\xba\xc8\x87\x85\x0f\x89\x0fk\x1f\x96\x8c\x06\x81\"x\xdd\xa6r\xe83\xaf+\xfc\xd1\\\xe1\xa6\xaepn\xaepWW\xf8`\xaep]W\xf8\xc1\\\x81\x12\x88\x94\x0b\xc8\xe1\x18n\xe8\xbf3\"N\x17A\x1a\xf8\x81\x12\xf3\xae(\xfe\xed\xc1k\xe8\x0ds\x8b\x97\xbc\xc5\x98\x9eB\xd1Z\\\xb7f\xfe\xe8\nN\xe1\xb2i\x19\xbf\x91\x7f\xe3\xa7'\xadO\xe9\xf5w#Dvx\x98\x10hz\xb8?\x94Lv]\n\xec\xb7\x96\xf4\xdd\x8a\xfe\xef&\x8b\xe70F\x90\xb9\x9aE\x17\x1e\xe5\xa0\xe0\x18Ro\x16]\xf8@\xe9\xa2kZm\x01g\x10\xba R\xc6\xc7p\x87L\x98\xe9\x0e'X\xef5\x7f\x83\xf4\x96\x0f \xfd&\xf1Y\x87\x95\xbb\xf2\xe9\xa1\xa0P\x1e\xb7\xe1g\xcf\x87\xcbYt\x01[\xa7\x90\xe0\xcdu/\xb1\xc6\xda\xf3YOW\xf2[\x17\x7f\x9dB\xa2\x81\xd5f)\xf2 bw9\xf6\xe9I\x83S\x98\xd0?\xfeHI:\xfa\xc79\x9c\xc2\x1e\xfd\xe3\x03\x9c\xc2!\xfd\xe3\x07Z\xe7\x80\xfe\xf5g8\x85]\xac\xf53\x9c\xc2\x01V\xfbH\xdfN\x0f}\xe5\xc6\x17\x9b\xdd\xce]\xe3\xed\xdc\xd3\x8b\xf9\xed\xd4\xef\x1b\xbd\x9dO\x9c'\xd7\xed\xcb\xa9\xf7n`]@b\xe38\xaa\xca\xdc\xd2\xb3\x1c;\xda\xa8\xf3\x8c\x02H\xd2>\\\x1c\xde:N\x83b\xdd\x10F\xa7\xe0\x00\xfd\"\xa5\x18\xe7\x14\x91\x0f\xef(\xf7(%\x90\x84\x11q+\x1f\x9c\xed\xbfVYy\xe2x\x88\x99\xbe\xf3|\x08a\x04\xces\xfamL\xffz\xf6\xc4\xe1d\x9b\xf3\xdc\xb1m\xeffD)\xe7\x8b\xe5\xf2\x94a \xe2\x86\x9e\x0f\xb9\x9b\x07\x1f`\x04y\xf0\x1a\xbe\x87\xd8\xed\xa4\xd2\x04\x1f\xe580+/\\:\x07\xeb\"\x11\\#\x12\x94\xd9O\xd9-\xc9_\x86\x05q\x91{$A\xb1N\xe2\x12\xbf\x0e\x12\x92^\x97Kx\x0e\xbb\xeat=\x1f\x1c\xb6\x86\x94!\xe9C\xdc}\xe8\xc9\xa9R\xc6\xac\xce\xe9\xce\x89\xbbz\x1b\xa7\xf3\xec\x96n\"\xfb+x\x1b\x96Kz\x97\xf1\xdf3\xf1\xfe\xd8\xf2yA\x92\x05\xfd\x98\xfe\xab\x7f\x8a\xef\x8eA\xc0\x01\xd7\x11\x84\xe82.\x1c\xcf\xf5z\xf0\xe05\xc7\x83\xd7\x8f\xc0\x83G\x9d\xa4\xca\xbe\x8e&\xd9\x8d;\xfa\xdfC\xaa\xd8\x89\xb8\x03\x9d\x16\xa0Kb\x90m\xc9\x1b[o0#\xa5\x91d\xe5\x7f\xf27\xed\xe5\xcc\xe9\\b\xfa\xbf\x01\xfb/\xaf^6\xf8p\xbf\xc8\xf3\xf0.\x88\x0b\xfc\xd7\xdcX:\xb8\xb1\xff\xe57E\x9e\xf2\xb0\xb3J9nN\x17\xd0\xbe\x04;\xf2\xe9nM^\xe5y\x96\xbb\xce\xcb0\xfd\xae\x04\x8a\xdd)k\xbd\xcc\xe6\x90\xa5\x00\xec\xac\x9aey\x9bB\xb0\xa6\x15E\xb4e\xb9Vt\xb5\x9a\x1e\x94\xf3\x95\xdfi\x9f\xd0\xf6\xd2\xce\xd3\x89wq\xec\x03\xb9 \x13\xcfuXq\xd3\xfee\xd9\xc7\xbf\xcc\xfb\xf8\x97\x9b>\xfe\xe5\xae\x8f\x7fi\x18\x9c?\xdb\x19\x9c\xe5\xa6\xec\x08\xe5aV}\x8c\xce\x15o\x99\xb2Ns\xc1:\xd9x\xa5.\xdee\xa9\xf1.\x8ckY#3\xa0q-W\xc8\xb5loC\x88\x8c\x05\xbb\xbc\x94\xd5\xa1,\x0b\xf2\n\xc7\x90\"3\xb3b\x8c\xc3Rc^\x9a\xd3\x8f\xb5\xcf\xb0\xb6`rh#Y\xcd\xf7\\\xd7\xdc\xc8\xe9)\xb2:\xdd\x92$\x90H\xc6F\x90d\xa7\xd2\xc5C\xaf'\x05: Dr\xecf\xda?\xa0Oq\x1b#T\n\xf3\xebjE\xd2\xb2\xe0\xb4e\xdfw\xf4\x89\xc2\x82\xc0\xf8\xb8\xb7\x1eH\x02{r\x0be{\x0b\xf5\x07[\x9el\xde\xb2K\x0c\x94\xb5\xfe`\xe3\xd3\xc74\xae\xd0\xd4\xa6\xe7\xa1\xf3m\xab1\xba\xa1\xd6/\xecm\xd5\xea\x95p\xbdN\xee\xb8\xf2\xaf\xde@s\x8b\x0f\xe6u\x11\\\x87\"!\x904!\xb2J\xa5n\xcaE\xce\xfc\xa6\x93\x9b\xcfl\xdc<~\xe6\xba\xab\xe0&\xce\xcb*L\xf0\xe25\xbf\x10\x96x\x9cW\x17\xbc\xfeG\xfa\xcd%\xfd\xdf\x16\xb2\xfc(\x0f`\xdc~\xe2yV\x8e\xfe\x1f\x85\x8b\x9f\xeab3.dk\x953\x1cu\xa8#4\x8a\xa2\x8c\xca\xc3f\xaa$X\xb06\xf7=83W\x96\xd5n\x16\xccE!H\xee\x96\x9e\x8f\xb0'\xa3gtk\x8c\xdc.jL=\x03Y\x04\xcd!\xaa\xeaf\xd5\x0d\x91 \x9f\x87V\x7f\xce5)\x1d\n\xbc\x91\xb8r\n\xf1\xcb@>\xbe\x88\"R\x14Y\xce\x08\x8a\xa2Z\xd3\xfd \xf3-\x0bA\xe1\xdc\x84IEx\xdb\xf4\xd0\x95\x0cY\xa5\x01\xbe\xf0\xfcMI\x0e\xf9\x08l\xa5\xee\xf4\xc8\xb3\xf3\xfd|\x0cO)\x9e0+~\x7f{\xe0\x8a\xcb\xf6\x82\xa2\xe6\xb6S\xa4 w\xd1\xbe\xa0\xea\xfa{A\xd8\xcc\xb3\x9f\xd8o\xe4\x1f\x9a\x1a\xb4\x8f\\\xb4\xebWS\xa3\x06u\xc8\x92K\x82j\xcb%\xda\xdd\xb3\xb0\x85\xa9\xbb7\xf5\x14dk>\xf4\x82\xc5\x0e\x16\xbcF\xecNh5\x99t\xef\xbf:\xb5\xf1\x01;b\x1b\x9f-I\xe67\xb1L\xa8\x9b0\xdf\xa2\x17\xb7}iT\x1a<\x05\xc6k\xd8\xaeL\xdf\xa0\xfb\xf8`uX\xff\x8d\n\x8dne\xba\xb2rCd\x82\x88\x9bc\x1f2\x1f*\x1fB\x1f\n3\xa8\xa4@d\xcbHc!\x03\xd0\xc6\xb9\n\x8fL\xc9T\x88\xe8\x1c\xc9-p\x18\xf76N\x99B\x8e|\x89\x08SJgQT\xe59\x99\x9f\x00\x9dd\xb9$\x90f\xe9\xceJT\x9c\x93\x1b \xe9M\x9cg)\xc5\xffH\x0e\xd3J\x8b*I\x80\xd0VaE\x8a\"\xbc&\x10\xa6s\x08\xe7sTe\x87 ,I\xb2^T \xdc\x86y\x1a\xa7\xd7E\xa0\x9f\n\xfa\x90\xa4 \x1dD*E;3}\xb1.\xcct>}(\x86\x1f\x9bi\x11W]\nR\xcb\x80\x9f\xfck\xf1\xe4\xda`\xdedz\xf8A^\xcc\x92\xd1\xe8\xc2X\xeb\xc1\xf3\xbc \x0dW(\x91}\x93\xde\x84y\x1c\xa6%\xfc)\xce\x92\x10)\x99\xd6WmJ\x8c\xdd\xb2(X\xe4\xe1\x8a\x14\x9f\xb2\x0f\xd9\x9aQ\x1a\xd1\x1f\xcc\x1f\x0e\x82\x01}\x16!OM\x9c\xae\xa4\xac\xeeW\xec\x0b\xb6bvaa\xa3\xd8\xa5\x8eS\xca8\x90`]\x15K7\xed\x10V\xab\xb35_\xacD\x9d\nW\xf2\xca@.\x0b\xe2tI\xf2\x98\x83\xed\xdd}O\xfd\x84\xb1\xe8\x93C\x1d\x03p\x1e}\xf2\xd4\xd8\x16e\xbf*\xe9M=?\xdaK\xec\x86\x0d\x91\xeb\xf9x\x0b\xc7'\x10\xc13\x10\x1c\xd0 D\xa3\x91\xbe\x88\xe2\xc8\x17\xb3H[\xc2\xa4io\xb6`\xcc\xb1Vt\n\xa1R \xa3\xc2f\x94|\xff \xb1\x80\xf9\x16\x8b\x97x\x9e\xccY\xd0\xef\xd4\x91U\x1c\xfb\"\x9b@\x89\xbbP/@\xa9\xec\x16\xb3,(\x83\x9c\x84\xf3\xf0*a@\x98\x1bi\xf0\x92S\xd8\x9a\xb4\xea\xdf\xe6q\xa9\xd6\xafKD}Z\x18&Iv\xfb\xefa\xb2x\xbf&)7\xbdS\x1bRk\xd4\xad\xb5>\xac\x9b\xcc\xd2\x88\xb8\x0eA\x83\xa8u\xf7r\xae[P\xc3\xd0\xf6\xfd=+\xbd\x14\x138/\xc3\x92\x04$\x9d\x13\xb4\xd6\xc9\x83\x94|)?\xc5\xd1gw\xc9\x86\xd0\xdd\xe9\xb2\xbd\x87%m\xcd5\x89\xf2\xccTb\"\xf3b\x8e\x18\xd7\xbf\xc7\xd7\xcb?\x87%\xc9\xdf\x86\xf9\xe7\x16 \xa9\x18\x06j\x86\x83\xfd\xa4\xa5$\xd5\xd4\x17b)w\xab\xde\xfdfB\x9e?h*sR\x94yvG\xe6\xad\xe1\x0f\x1e\xa2$\xcea\xa3\x15\xe7\x14G\xab |\x0c\xf3i\x8e\x98\xfaeP\x8f\x8d\xd60-D]Acu4a\xa12\x113@\xfe\xfd\xa7\xd0X\x9f\xd9&A\xabx\x1d\xdb)m\\p\xc9\xbf\xea\xa3\xfc\xb1C\x86?\xaa$\x11\x17\x16\xcf\xbe/\xdf#\xe2\xcb}\x7f\x13499\xda\xb3\xea\x8a\xec\xbb!\x8e=\xaetN\xd7\xb56\n\xeb\xa3\x8a7\x1c\xdf\xde\xc1\x9e\x01\x8f\xbf\x0d\xcbe\xb0\n\xbfv\xeds7\xde|\x02\xd2\x80\xcc\xe3\xd9\xb73\x88LZ2\x90\xb5\xfb\x87a\x10\xa7\x87\x1b/\xf0\xdf\x85A\x1c64!\xaci+\xc1J8\x93\xee\xa0\xcd\x19\xe3\xdb\x8f\xa8S\xc8\xb5\xb5U\xba\x1d\xf2-\xebg\x9a\x85\xeec\xf7\xdeb\xaeg\x16$\xee\xeb\x06\x96\x8c\x90>:\xf4\\\xa7\xc8#\xdd\xd4\x81\x92\xd3\xb5\xd0\xb6\xcc\x98\x1dI[\xfd\xe5:\x0e\x8c \xf4\xb8=\x8a#j\xca'\x06-\x08\x838-\xd6$*\xcf\xb3*\x8f\xc8\x90C \x08S\xe9f\xf96K \xc1\xa5\x87&\x12=\xb2Y`\xa4\xea\xa9\x8e\x10\x7ffn\xea\x83CYB\x07\xf5@q\xf3\x9b\x1e \x8a\xbc\xe8\xadm\x8c\x97\xa4\xcf\xaa\xe6\x8b\x8a\xd7;\x03\\\xa1\x92i\xb1\x8a\xe0\xd7,N\xdd\xda\xda\xd7\xc3\xf6\x90\xe2\xcd\xe1\xac\x86\x07p\x0c\xa1\xf8\xa9\x94\xc6\xcd\x818\x06wN\x12R\x12|\xefK\xaf\x14K\x8fF\xf2.\xd3[\xf56u0\xd2\xe2.\x1a\xef\x19e;894\xab\x90\xc1\x91\xf8\x08\xb9\xffot\x0d\x7fo\xc0\xb01\xd66_\xbd\x03\x93\xa2\xd9M\xdd\x83\x03\xcf\xc7\xf7\xe3\x86 \xb69\x98\x18\xaf\xe9\xe4@7\xf3\x0b\x8d\xaeT\x9f\xc9\x9d\xd9\xff''\x0b\xf3\x8b\xcb\xcb\x82$\xf6wx]\x8f[ \xcb\xe4%VX\xb7M&[\x83\x9c,\xa4\xcdh7\x13\x0dk\xe63\xb9\xd3\xf6\x14$\x96\xbc\x0d\x1ar!\x962\xc2\x88\xb6\xbc\x92>\xff\xf2/\xec\xf8\x1cC\xd5^\x1c\xfa\xea\x18\xca\xf6\x0b\xdc\x03\x83v\x1b\xb7 m\x97\xaf\xf3l]\x1cChX\xff\xec6%\xf917j\x12\x8f\xd9\xfbI\xb2]\x91\xc4\x1cA\x94\x93\xb0$\xaf\x12\xb2bn\x15}\x94 \x9e\xf1\xda\x17\xa25\xa2\x84\x9e\xc6*I\x0c\xb3\xe0o\xd4\xc1QZ\x83\xdfNY\xdc/\x1e\x14\xc3\xe4\x10\xd3\xc3CP\x03\xef\xae\xb9\xef\xc7\xc2\xf3!\x12\x85 3\x98\x1c\x01\xa1\xfb\xee\xf9 \x8bM\x03v\x84\x05\x1c8\xaeK\xda\xd5\x18\xf2Q+b\x19\x02\xa5\x8c\x810\xe6\xbb\xb7\xbd\x0d[\xa1v5]V\xeeV\xcc\x93\x11\xfd\x1fOZ\xcb\xb7\x84S\xd05\xe8\xb0\x03\xd3\xf6\xca0Y\xc7\xd2\x83*\x88\x96q2\xcfQ\xa4\xa1\xa1%\x94\xb9\xd2\xdaKx\x0e\x13\x13YQ\x0b\xb3\xe6\xc2\xac\xcd]\xd25bb\xac\x1bx\x06\xcb\x13\xb8\x19\x8d<\x98\xcfn.\xe4\xd1\xcdn`\x04S\x83\xfco\xec\xabc\x9a\xab'\xb05\x13\xee\x15\xc8=q\xe8z\xb5\x84\xe4\xc0\x97\x07\x8dO\x94\x9a\x16\xf1#\x9e\x8b;O\xdeD\\xi\x07\xee\xe8\x0et\x0cM\x08\x80\xe9ig\xee\x03c\xfc/\x0eP\x8a\x9e\x96\x14g7\x17\xc7\xaf/\xcc\xeb0*\xb3\xfcn\x90G\xa4v\xc9\x82\xab8\x9d\xbb\xdc\x07\xc9L8\x93@(\xd75/\xc5E\x10%YJ^\xa4\xf3\x8fL\xdc\xfd\x1f\xa4\x97\xb9n\xe6\x18p%\xbd\xcf\xa0,\xfd\x87\xdf\x03\xfa\x07?\xe7e\xc0\xa0\x8a\xcf4\xfb\xebB\x9f?\x1d\xc0f\xf0\xa2\xaa\x0d\x9brTd\x8a\x86\xdb@\x02m\x9b\xe8\x15n\xbfB\xc1\x03\x0e\xbb}j(\x12\xed\x9a\x8b\xb79\xd0\xa9\x14\xa03\x17@\x87\xdd\x9a\xfax\xc80h\xa9\xc3 \xb6\xde\xec\xe0#\x1e\x97\xcft\x0d\xb6\x0c\xef<\x0d\xdaT\x16h\xc3\xca\x15\x15\x11%\xb6T9P\x02g\xb0\xa6\xc5\xa7\x90\xd0\x7f\x8e\xc5/Z\xd7\x00\x9d\xee6\x84Nw\x1e\xac\x87@\xa7\xbb^\xe8t]C'\xbaz+\x06\x9dV\xf0\x0c\xeeN`E\xa1\xd3\xf5l\xa5B\xa7\x95\x05:)\x03\xba\x1et\xff\xf9\xddX\xfa0\x17@\xe0F\x95\x13\xd3\xc3\x1f\x17\x7f\n\x93xn:\xfe\x9bP\xa4\x8a\xbc\x88\x1d\x10AJ00&\xf7\xaa\x10\xc0\x7f\x80~\xe2T\xd2\x0e\x1f\x98Y\xc0\xdd\x83~\xa9@\x87\xb3\x03c%\xcc\xa0+wS\x8f\"P8\xe6\x87\xb0\x99\x8aq\xec\xfa\xc09%\xa6\xab\x8a\x8d\x04ef\x10\xd3\x0b\xc3R\xae!-H\xf9)^\x91\xac*a\x192\xb1\xc5\x15!\xdcK\x97\xcc\x9dn\x91|\xd5\xdfA\x94\x900\xff\x8a.B\xb3\xfc%\xc5s\xd0\x8c\xbe\xd6\xda4Et\xf9\xc6\x06\xc8\xc6\xbf\xcd(\xd3\xb5\x95\"\x880\xb4C\xf7\xb1)\xf6{\xda\xed\x94r\xa4\xec\x0b\xf5\x9a 9\x87\xd1\xa7\xd5\xdc\x1c\xb4l@8\x92l\xb5\x0e\xbd=\xb4\xdb\xe2\n,s[\x16\x10\xf1\xb0eg\x7f\xcdsHm\xb2\x04\xe9 \x9e\xc9?Z\xc4{\xa7\x80(\xad=\x18\xea\xfa\x03\x06\x95\xdb\x06\xa5\x1c\xde3\xf5\xe7\xb1\x04\x85\xa0w`\xb4\x8b\xca\xb6\x8a\xae\xa6\xa2-\x98\nu\xa6i\xfe\xd1\xfeV\xd3@Q\x0c\xb931]\xfe\xb6\x8e\x8e\xf9? J\xe4M\xd5\xeaY:9z\xe0\x83(K\xa3\xb0t#\xb4/\xc4\xb6}\x88D\xa5\xedmX\xba^\x9f\x96\xcet]\xb7\x166j\x96\"\x89\xd0]\x1b\xd4\xe28F\x83uC\x8d\x0f)\x01\x18\xd5\xfaerb;\xe7\xf8\x01\x85\x92\x91X\xd7\x13\x18\x8d\x12x\x86\xdf\xe0\x82\x14\xb3\xe4\"\xc8\xab\xd4\xb5X\xbc\x8a\xa5\x90\xbb\xec\xb9%\xc0%|\xec\x8e\x9a\xf6N\x865\xbc\x92\x0b[Jk\xbd\x1d\xdeP\x85 \x90\xf1d\xc6F\xe9\xa9\x95_\xf8\xc3\xbb\xb1\x830\xf1\xe4n\xd9\x864\xe2\xe9\x87^\xe2\xe9\xef\x08d\xb5\x83\x0c7\xed\xdd\xc3FC\x80V\x07\xc2\x1a\xa0\xbb\x03\xfb\xec\x8do\x1e\xf4\x05{\xe8\xbc\x89s\xbb*qQ\xa5\x92&3\xa44%%x;\x9b\xbbq\x15\x8b\xd3\xb8\xd6:\x0e\xe2\xf1(E\xc0hW\x03\xed<1`\xe9V5J\x1d\xdba\x01\x9d\xcf\xe4\x04Rx\xd6\"\xceO \xa5\xc41\x99\xa5\xb4+\x95@N5\xe28\xe2ZVr+\x96\xcf\xf3a\x82th\x0d\x05\xef\xef\x01\xa3s\x84\xeeR\xa1~\xe7\x92D2\xaf:=\xa6\xc4&p\x9bs)\xde\x06\xee\x85\xd2l\x1c\x94q\x89\xd6\x1f\xceU\x9e\xdd\x16$wh!\xff\xbb\x89\xba\x94\xde\xf0\xf0\x1bq\x10\xe6\xd77\x0c\x7f@\x1cp\xbbAd\xbe\xa4\xdfE]\x1b\xdf\xdd\xe0w\xf3\xf9OqQ\x92\x14\xdb\xbda/Q\xd9\xc0\xfe^,\xc4\x9f9Ye7D\xaf\xccJ_$\x89xQ\x887d\x15\x97\xe2\xefuN\xd6$m\xf5\xc4\x8b\xdf\xa7Q\xab\xddDj\xae\x97\xa1\x98]\xa8\xabw\x15\xa7\xf38\xbd\xeeVR\xe9T\xeb:\xcf\"R\x14\xf5\xc7\xb1f%\xedh[\x14\xdd\xce\x07x\xc89O\x1c\xed\xb3\xe5\x0f\x18\xd9&\\\x88\x91R\xe22y&\xc8\x81\xb3\xe1\xbd\xf9\xd3\xab\xcb7\xef^\xbfy\xf7\xe6\xd3_\xb0\xc6\x04\x9e\xd8V\x9a|)I\xda\x8a\x8bh[\x02\xa6\x9dk\xd3Q6\xf9-.\x0d[:7S-\x9f]\xe2y\x0d\xed\x04\xcf o\xd6\xae\x9c\xc5\x94\xc5\x9e\xa5\x17LD\x1a_|\xfb+$J%9\x9d\xd9]\xa5\x15\xd4\x8fYj\x8c=\xd35\xac:5v\x063n1\x95 N\xa3\xa4\x9a\x93\xa1\xa1\xcb(\xa7_\xf7\xa5\xbc~\xe0\xc6\x0fC[2D@3\x8c_<\x84\x85\xc7C\xe5.\xfdk{[\x84\xc6ce\xf8\xe7\xf66\xe4\xc2\x12\xbd\xd5\n\x1d_\xca\xde\xea\x9c\x06\xbeY\xc4IIr\xb7\xf3-IN(\x11\x17\xa2\x17\n\xfb\x06\xc11z\x0d, \xd4\xe3\xa740d\x0b\x08\xa1\x88\x96d\x15\x06\xf0F\xbcb\xf1\x0d)>\xc8\x16PT\xd1\x12[(Z\xc4a\xe0\x18\x8e\xe3\x12C\x1b\xae\xd6qB\xe6o\x9a\x95\xab8\x0b\xeb\x88\x018>\xcc.\xf4\x0f^}i\x7f \xd6\xd3\xf8\x01E\xcco\xc3u\x17E\nB0\xc4n\x90\xd1\xae\x80>l\xb1\x8e\x8dZv|\xcf\xc3j\xdak\xf0`\x9b\xf6\n\x8b0I\xae\xc2\xe8s+V.}d\x89{\xfdA\x07\xce\x17O:cW\xf1b\x86\xd7\x94\xf9P\x8a\x9e\x9a2C\x0c\xc3vw\x14\x90\x97\x0c\x90\x13\x83Z\xea\x04J\x86\xf9J\x0e\xbd\x1b\xc6W\n\xaf\xa8k\xff@\x12\x0d\xab\xe7\xc55\x9e\x16\xcb\x99\x90/\xb7\xf8+\x0c~|\xf5\xfa\xc5\xcf?}\xaa\xe5b\xa1`\x19:N\x848\x0d\xea07\xf1\xb5\xef\xf2\x80G\x01\xa4\x18\x97\xb6\x8e\xb3\xb1AyF\x9f\xab\x9c\x84\x9f\xdb\xaf\xba\x9c\xe1K\xada\xbd\xab\xc9f]q}\xa8\xa5/\x19\xc8\xfc9\xcf\xd2k`\x9e\x81\x08AD\x97x~\xce\x194\xe1\xbbP\xb3v]F\x01\xcc^\x81\x02vN\x0c\xd6N\xceM \xf3\xe5\x0b\xc8\x0d\xc9\xefz\x80\xa7\xc0\xb3\xb2\x1bN\xa8\x01*\x0dn\x9e\xd7\x916\x05XDn\x88\x83\xc6\x02\xdc,\xa7\x802N\xaf\x13\xc2g\xc8Mq=\xca\xa0\x95a\x9c\n\x98\xab\xbcm\xf9\xec!wA\x1e=\x8dl\xd3i\xd4\x81B\xb59P\xb8i\x9b\x81\xf4\xae5~q\x8f\xc9-\x84\xae\x01o1\xf4id\x89\x05\x1c?\xd6\x1d\xd3\x14\x11\x83\xcc\xa4\xb1M\x1bj\xab\xf8\xdb \xcaP2Ho\x05\xc6\xe4\x81Om\x16\xe9\x83}\xf9j\xcdl\xe9C\xac\x83\xad^},s\xee\x16\x06\xa1\x9b\xb2\xaf\x9a\x0e\xce\x0b\x8a$\x8e\x88{\xe8\xc3\xce\xa4o(\xdd\x0e\xf5{\xbb\xff+\x1d\xea\x87-\xeb?\x80\xd5\xf9\xb7:\xf7\xfb&?U\xe6\xdf\x12\xa7\x8f\xa3\xec\xb3\x9eC:@/+\xb7=\\7+\xf5\xf1\xa3&F\x1d4z\xfaQ\xcf\xd8\x91\x86\xda\xb8a\xfcJj\x19\xc3\xc1\xc8\xb21\xac`\xeaO8\xdc\x0e\xeeR\x81\x9e]G\xe6C\x1e\xaf\xe22\xbe\x19\xbcL*\xa1i\x04\x1d\xf8\xc2p\xbdX\xfc\xc5\xf6\x05a\xe5\xed#\xaeS\xb2FPW-\x16x\xe9\xcb\xfaG]\xed\xc1\xab\xddaR\xf7\xe0\xd0\x0b\xd8{\xb3@es\x0b\x06\x03\xe9\x8e\x1b(9-s=\x80\x08\x06\xf6\x97\x17o\x7fz%\xc2\xae9u\x82\xaa\xb0\xc8d\xdb\xc3U\x98\x7f\xe6\xa6?\xf8\x93\xc7V;mb%\xd1\xfat\xcd\xdc\x8a\xa7`be\x1ef\xb0p\x9bF\xcex\x02\x8c\xba\xa4\xc6b,\xf7\xa4\xe3\xf9\xf5\x90\xd7e\x95\x93\xf32\x8c>\x7f\xcaCth\xb4\xbc\x11\x86\x9cK9\x01X\x86q\x88\xb1\xac\xa05\xd1EYXhy\xbc\x8c\x0eY\xb2\xf6\xaa\xff\xca;,\x9c\xd8 \xe4HZ\xb9\xd5\xf2&W_\x8a\xb9\x0e\xa3U\xea}\x1a\x81s\x0c\x8e\x91f!h%\xd1\xb7 >l1\x07\x9dz\x1f(\x85C\x9a|$\xa6\xed\xd0s\x0b\xca\x94\xd6\xa0\x84\n\xbd\xf6\x026\xf7\x1d\x96\xcdK]\x95Z\x08>K\xdd\xe9x\xeaiV\xf7B\x01\x8a\xef\xf7w'\xe8\x88\xbe\xbf\xdb\xaa\xd7\xc8\xcb\xb1\xde.\xaf\xb7\xc7\xff\xdd\xe7\xff\x1ex\x92\xc5\xcbc\xc5\x9dv/\xc66(S\xcc\xda\xdc lCip,\xd4\xcc\xd6\xdc\xa9\xa5\x9ed\x00\xe7\xeeY\xeap3;Mm\xa0\xdd\x85!ru\xcd\xc4.\x17\x82\xcf\xb8\xa3Q\n#\xc8\xbd\xe6\x00\xef\x1e<>\xae\xce\xe3\x03\xfapV\xea\x11a\x89$%\x8a\x1e\xc4\x84\x87\xf7oE\x1f\xcax\xb9\xce\xb0n\x10=\x99\x05\x8c\xfdg\xf4\xe4\xea\x9bDO6\xdd\x8f\xbfOPa\xd3H\xf0ZF$N,7v\x91dY\xde7:\xcb\xd0\xe2\xe2]\xf8\x0e\x15\xce#\x14#\x8c\xe1\x18\\\xa1\xc1\xc81OZ\xbfD\xc1.\xaa\xe9\x0f\x10\xdcw@\xd5\x10\xb4|\xd4\x9a @X+\x18\xad\xb7\xba\xcc\x13xs\xf5h\xac\xe6_R\xe5\xb2!\x05\xdb\xf27\xfa\x18D\xd7]\xa6\x0b\xad1\xf4\xe4Nh\x0f\xc3\x1a\x9b\xdf6\x92\xdd\xe1#Ah\xb0\xe1`\x14E\xaf\xfc\x0c\x90N\xd6\x9dw0\x0e\"\x9b\x00\xb1\xa6\x12\xd8\x04\x1f\x0e\xbb.qoB\x99\xded2\x8f\x0dTf\x8f\xaefQ\xdaO\xc6\xbd\xb7\xce\x02\x0d\x1e\x15\xd6\xae\x8f^l\x85\xfc\xe2\xf2Z}\xf0\x0c+\xb62\x06VbNm\x19m\xea>\x16\xbe\xdc\xf0\xa8:\xa1k\xa4\xd7\xb0\xed\xca\x87\xc2\xe7\x99\xf0\x0c\x95(\x1e\x8efcC\x00\xe9\x04\xdf\xe8&G\xd9\xb0\xcc{\x1d\x9a/2+.\xba4\x9fZu\x83q\x80\xcf\x8c\x12xv\xbf\x96\xc5(\"\xcf\x98\x07\x00S\x1c\x17|X y\xc0\xe41\xf2\xab\xc2\x87)\x93\xb5\x9eu\xe3BhF\x96\xd4\xf8\x90q\x80\xfa@\xa0/\x16\xa9\xb1\x1d}6}\xc7Xn\x98\x91U\xbf=\x18\x15\xd0\x8f\xbf\x04\xc3.\x9f\xa2\xeb5y\xf01\xedo\x13p\xfd# \xa3\x92\x07L\xff?\x0e\xcf\x84\xec\x9c\xc0M\\\xc4%,\xcbr}\xfc\xe4\xc9\"\x8c\xc8U\x96}\x0e\xae\xe3rY]\x05q\xf6$\xa7\xdf=\x99gQ\xf1\x04?\xde\x99\x93(\x9b\x93>\x81\x9c\x999\xe6\xa3\x91\xc7,\xd5\x9d\xed0\xbf.f\x17X\x8f\xa4\xb4\x89\x9f?\xbey\x99\xad\xd6YJRY\xaf\x96\xc3\x08&\xba\xf2\x8c\xb5\xa1\x06\x7f\x17\xa2\x89,\x1f\x1e9\xbe\x89\x1a_\xf4\x87\x8b?i]\xff\x18\xe4\x10\xee\xba\xaa\x8e\xc1\xf4\xb83\xfa\xba\x0fq;\xacz\xdcs\xea\x06\x9d\x1b\x89\x82\xb2q4\x8f`\xe5\xebb\xf1I\x87\xf7\xcc <\xac^\xb8?\xb4\xff\x12\xeb,\xb7&\xc1\xb78(\x97a\xf9\x11[+\x98\xd8E)z\x1d&\x05Z>\xba\x18H[y\xf7)\xaf\xf8\xab\xb1\xfe\x8a+\x17r\x11\xcfW\xfdn\x19w\x9a\x8f\x88\xb9)\xf9\xf6\xb46^\xf0\x03>\x04\xa5\x9a\xfdO\xe0\x94\x1f\x94\x8d6P\x94v(\xa5\x9e|\xbf\xa5n\xd7\xf7\xf0iI\xe0\x8a 7W\xd9\xbcJ\x08,\xf2l\x05i6'\xc1\xaf\x85__D\xee\xf4\x1ah\xdf\xeb\xcd\xfd[X\x95\xcb,\x07\x80\xd7$\xcf\x8a\x02^\\e\xd5\xe7e8\x8f\x7f%Kx\xb6\xc0\xc2\x7fc\xff\x04Y~\xfd\x1c\x9e \x88\xd4\x94\xb5\x1a\x15\xf6H\x8aA\x12{\xf9\xa4uu\xb9\x1c\xaa\xc5?CC\\\xb4\xb2\xe4A\x93X\x0f\xef\x94\xf2\xb2\xbe\x10\xed\x98+\xd0le\x11|\xfa\xcb\x87W?^\xbe\xf8\xf8\xf1\xc5_.\xcf\x7f\xfe\xf0\xe1\xfd\xc7Op\x06\xd3\xc9\xde\xd3\xbd\xc3\xdd\x83\xbd\xa7p\x0c\x93\xf1\xd3\xdd\xa7{\x93\xc3\xa9\x96\xef\xd6\xd2ah\xc5\x95\x94\xe2\xa4\xc3yF_7\x86\x17\x1f\xc3\xf4Z\xf0\xc9\x14(%\xf1\x1cI\xd190Os\x865:\xcc+l\xb3p\x85\xbd\xd3\xcfqZ\x1e\nCc/\xb8\xbcDl\x7fy\x89!,\x1a\xf9\xea\xb1b*\x82l7o\x00}\x9c\xe8a\xe7\x18\x8c\xe5\xb8\xd3\xa1\x85y=\n\x1b\xc5\x06\xc2\x88\xcb5O\x80\x07\xc4\x97\x95 \x85\x9an\xa0i\xba\xbd6H\xde\x1b\x14\x0d6\x12\x0b\xeb\xb7\x15\x10\xcaN\x89MZ0\x1c\xc9=\x9d\x8b\xda,\xb9\\\x12\xe6\x86\xb2\x88\xf3\xa2\xac\x11?\xac\xaa\x02\xedgB(Z\xd1j\xe5G\x10A\xf6x\x08\x0f\xb63\x105\x01i\x0cr\x1c\xcb\xd6Db\xfd,\x0c\xaae\x0d\x89\xd9l\xe8;!\xb5Q\xe7\xcdm\x87BnR\xdf\x91~\xda\x9c\x89\x16\xcf-W\xe5lo\x03\x91\xcf\x83\xfc\xae\x1dK\xbb\x83\xedFW\xbf\xe0\xea\xae$?\xe1\x89\xf6\xd1\x0co\x0c\x98\xeb\xba)\x86g\x8d4K\xbf\xaa\xdfe\x8bEA\xca\xef\xe8\x11\xc8*4G\xbf\xca\xaat^\xd8vW\xef\x936\x0e#p1\xf7\xf0\xd8\xb3\xf6\xc3\xee\xdc\xf0~0\x00A#cI\xa5\x00n\xa7<\xf0o\x0b(\xd4F.\xd6*x\x81\x8fM\xc5t\x99\xcd#\xe9\x04L\xa4\x0b\x10\xd1\nk\x06H;\xaf\x8a\xc1\xd0O\xd9\xfdc\x93R\xb1\xc5\xd8tx \x1a>\xc7\x05\xad\xf3\xc9\xdf\xdf3\xe7P\xa7*\x17\x87][\xbfU\x04q\xf1\x8a\xc3\x0d7\xb58`\x7f\xe7\x08\xd0\xe2H`\x83!\x056\x94\x1a\xf6\x98n\x12H\xf8t\x0c\xf70g\x1bg\xf6\xd7\x02\x8e\\]\x16T\xa8d\x86\x8e\xb7y\\\x12\xd7\x02U\xd9'u\x96\x02\x97\xf9\x042#\xfc\xb1\x0f\xb1\xf7\xe8\xed\xf2\xfaL\x1f\xc5C\xd7\xb2\xa8\x15\xba\x141uH\xb3j\xd5\x08\xdc\xc3\xd2%\xc2\xe7\xc9\x166c\x08\x906\x9a]Iu\x82\xb8\xf8SLX\xda\xfdv\xb1\xc9\"L\xaa%\x8f\xb4!0\xdb\xa3\xad\xa9\x99-\xd5R\x0e\x11\x1dK\x1caX\xe2\x9b:\xd9f\xd7*pj\xb3\x1eIW(\xc2\x1c\xc3\xfb\x9d\x9cx\xb5\xa2\xcf\x8a Q\xbd\xe5\x84E\x14\xc7\x8eY\xc9\xc5j$a\x19\xa7\x93\xce*Wq\x1a\xe6w\x96* )w\xcd\xe8\x845\x82d^W/U\xb9\xd8\xe9\xac\xc1\x08\xed\xdeQ\xfc\xec\x96\x9eu\xc1\xa1\xe9.*\xa6\xdd\xe3\x89\x8a\x9d\x9e\x1a\xe5br\x90\x90\xbe:;\x1d\x95\xa0\x19\xf7\x14\xbe\xef^\xc1%\xf9\xd2\xdfJ\n\xcf\x9f?\x07\x83?\x114\xdb\x19\x16\xe4`\xaf\xbf\xa9\x1f\xfa\x16\xb2\xd37\x1c\xa0v\x0c\x19\xba1\xc0\x990\x96\xac\x86Ph\xf6SvK\xf2\x97aA0\x03\x19F\xa1k}\xaa\xebR\xcd\xe0\xeb\xa6\x8bc\x11w\xab\x9c\x11\x03\xec\xe7F\x14\x14\xfd\xf9\x02 \xe6\x83:\xbd\x93\x98*\x8b\xfe\xb8\x01\x01eM1\xf2\x05\xdb1l\xa3E\xdc\x92R\xee\x10\x85\x81\xdc?\x0eyNx.K\xe4\xce\xf0\x8d\"\xa2\xa3\xd8}\xa7.9D\x90F+Ie\x1ekp\x94\xfa\xdcB\x82\x852\xc6j1G\xce\xa5\x1ccQ\x88\x04D\xa5\xfa\xe5\x08i\xfd\x94\"\xc0\xb2#\x88\x82\x98e\xdc\xb9\x0e\xc0C\xe0\xc8]\xb7OF\x13\xf6h\\\x99\xc2J\x91\x86}\xda\x99\xc01\\k'\xcarB\x8c\xc2'\xde0\x81m\xa4u|\x8b\x9c\xc1\x86t\x1b\xf1\x85d\x10\xcac\xee\xc0\x19\x1e\x86\xae*\x8d\xe5\x0f\xe7Z\x8d\x95\x93\xb0(\xdfX>\xc0\xb9c\x12%\xfb\xec\x8d\xbc\xcbM\x98\xd4\x84\xbd`WD\xa0\x8a\x9c\x93W\xadP\x14\xe6\x1b\xad\xaf\xbf\x05\x98d,5\x8b%\xbc_(\x1d\\s\x8dB\xa2\x82\xcd[,\xa5\x16`\"\x05\x86\xd1\x18\xffM!\x01'\x04s\x0d\x8c\"=\xc4\x91\x1b\x17Za\x01\xc7ej\xd1\x8eTf\x95\x17\xc4,*\x91\xa0\xd8\xa7L\x18\xd8\xfc\xee\xbdWt\xa5\xa6>\x84\xf0\x04\xff-\xf8\xbf)\xfek\xb8o\xad\"M0k\x1b(\x1f\x06\x0b\x17U\x89\x8c]\xc7<{\xee\xcfo\xd2rr\xf0\xc3+\x97\xc0\xf7r\xb6\x11\xf1\x98\xef\xb9\xd5&H85\xda&\x8d4\x1d\xaaaN \x83g\x10\x9e@6\x1a\x99\x992\xe0\x9d\xe1\xf42\x0f\xc7\x1fQ\xf0\xc1C_-8\x1c\xce`\x07\x16\x9dr\x1d\xd1R\xfd\xa1\x88\xd2\x9dy>\xfb\x1cF|\x81\x8az\xdf\x16tA\xacMr \xbb\xc3\xc2\xd7\xb2\x163\xd89\xe5\xa3\xf1\xf9*X\x80\xb3}mR\x18A\x01\xcf!\xac1I\x08;P\xe08\xf9\xaa=Gf.\xdb\xd9\xe9\x9arM<'<\x88\xed\x9a\xf1\x80kx\x06\xc5 \xac\xbb\x16\x1d\x94\x85\x87\x11\xac=\x16\xa4\x97.\xfe\xbaw\xa5\x81\x9b\xc0\x98\xfc\xbb\xf5\x07\xe3\xeft\xd62\xcbq\x80\x0f1\xa9\xb7+3\xd6\xb3j@vt7k3\xe0[\xf5h\x07\xe8\x061o1J!\xdc\xdf\x9b\xf8\x18\xa1\x04\x97\x90\xb6\x81\xe2\xcd\x05-\xc3\x9b\xa3\x90\xe79\xc4x\x0chqLq\x01\xfea\xee!\xeb\x85\x9d\x19\xfc+L)/7\xb68r\x0bu\xe2\x92|\xe9P=\xe5\xf0\x1c2x\x02\xd3zh\xf8\xabK\xfeP\xb1\xb3W\xb1h\x87\xa3Q\xd5\x05>(\x9aX\x87yA\xde\xa4\xa5K\x82\xa2\xba*\xca\xdc\xa5|B\xe5\xc3\xd4\xf3ar\xd0!7g\xd4\x9a$(\xac\xccu\xcb\x19\xbdi\x98\x8a&\x1c\x00\xf4Dc\x83\x0e\xcde\xcf\xa1\xe1\x8d\xfd\xd5\xfd\x19s\nK\xc7\xc2C\x95\\\xdb\xa0\xd3\xd6\xd3\xd5\xd0\x9e\xec\x06\x03u\x9b\xb2\x11\xd2\xecB 8Q\xb3\xf2L\"\xc6\xb3\xed3\xc1Q\x19D<\xe4\xc4\x8b\xd2M{$\xfam\xc0\xf7\xc0dy\x9bL\xfav\xd8\xa4\x95\xb5\x19\xd4\xf0\x97a\x0d\xff\xd5\xfda\xf3A\x9f\x0fm{\x90VC\x0e\xec\xc0\x83\x93\xf2]\x93\xaeZ}\xb0\xb6\xb7a\xcbu \xc5NS\x0f9\x02~ \x19+!\xed_\xc5\xf9M\xcaO\xc3!\xcb\x84\x93R\xb0\xb1\x7f\xe0C\xc6\xb6=\xf6\xea?m\x9a<+H~\xf8\xda\x03\xff\xaa\x8b\x9fUY\x08\xf4\xe9TXL\xf4\xd5\xa7<\xc8\x0fw%\x91<\xa2[\x85\\E\x85\xfd\x0c\x1b\xd7\x8b\xaeq\xa5RL\xa1\x9af\x1c \xb2\xc5\x10\xf3\x18\x83\x1ab\x14\xddv\x81\xcd\x8c\x85\xf8\xf0E~\x93r\x16\x1bLS\xc5\x83N$\xc6L\x89\xe2A#V\xcaJ\xef\x1e\xc1\x19\xec\xc11\xfb5\xdd\x853\xd8\xe5\xbf&G\x138\x83)\x1c\xdbD/\x08\x91a\x04 \xad\x87[|\x83\xe1Z\x8c\xf8\xc5#\x8f\x8f\x81\x05\xf6kz\xe1kS\xc9p\xf4jY%\xcdh\xb2_\xcfh2\x85{p\xc5\x9c\xe4)Vt\x8a\xd3\xf1\xdeS\xfe\xdd3\xd8\xdf\x9f\x1e\x1dP\x92\x88\x92\xb3\xfbOw\xf7v\xbdo:\xff\xbd\xc7\xcf?\xac\x7f\xedn\xb0\x1ajYhY\xa1Cm\x85\xa4%\xab\xd4%\x0b\xe9\x92\x1d\xec\xef\xef\xee\x03\x06\xf4x\x06\x93\xc9do2\x99J\xcbd\x9c\xa2\x99$\xae\x8d\xb1(_\x84\x9f\xd3\xb6w}\xbc\xc9\x18tl!\xf7\xe7.(>\xa0?\x0f|\x11\xb5x\xc1\xc4\xa8c\xd8\x86\xc9x\xba\x0b\xf7l\x1397\xb3\x7f\xb0;\x1d\xc3={\xb5\xcd\x0c\xc2\xf9w\x1e\x05T\xa3SH\xda\x10\xdf\x06\xa5\xfb)\x12A\x8c\xd8\x15 \x14\xe3\x14\xbc\xbc\xafI>C8,\xee1\xc2\x13\x85\x1b\xf5\x16 \xe9.\x1c\xc7\x0e\x18s\xb32\x10\x04\xf4\x16\x06\xd3\xdcXz\xc0`8\xba\xc9}\xa6\x9a{\xdfCD\xa5\xedEv[\xe8S\xfeE\x82\xda\xb7\xbd\xf0\x81\x04\xe7Iv[\x97t\xef\xc3\xa8l\"\xab`,\xdc.\xbbBT\xdd\xb9#S\xa0\x837\xef\xce?\xbcz\xf9\xe9\xf2\xed\x8b\xff\xef\xf2\x87\xbf|zuN\xcf\xd3\xd8&\x8b;U\x93)\x9b\xcd\x82\xcc\xe5=\xb1\x13\xed\xf9\x8cn\xa4\x88o\x92\xc9\x92\x9e=G<\xb5\x02M\xb6J\xb2\xe3\xb4\xba\x96Y\x00\xd8\x81\xa8\xb3l@8H\xf1\xf0Q\xed\xb5\xe5G\xe21\xc3\x8e\x07\x1f\xf6\xa6\x9cVZd\x99\xebY\xc5\xa1%e\xc8\x98\xa5\xe9\xf6\xb6p\xeb\xad\xcb\xdc\x89\x0f\x13OR*\xb6\x8fjg\x0c4h\xe6\xb0e\x90\x9d\xa8\xe7\xca\xf5\xe8\xc9\xfa\xfc6\xfc\xc2-\xe4P\xc5L\xcf\xd4:\xcb\x92\xf3\xf8o\x14x\x1cN\x8e\xa6\xb4\xe82\xac\xae{M\xb6\xc1\xb6\xb1\x85\xe2\x0c\xa3\x1fo&\xd8\x1e\xe0u$\xb5\x1f5\xe9\x05\x0d\x16\x98\x1dBjW\x1a\x8b2F\xe3\xb9\xa237\xd6\xf1-\xf6\x93<\x9c\xcc\xf66\xff+@{U\xc2\xf3\xb8\xa9e\x17LbF_\x99\xc3\x9c\x16\xbe\xd6\x8a)\xe0)wh7S\xa3\x9d _\x1e\x98\x1a\x01\xc1\xcef\xab\xbf\x81\xed\xa7\xf8\x02Y>D4ca\xd6$\x1bB2\xf3\xbe3\x93\x05`\xde\xd4\x0f\x161\x0b\xea\x86\xc6\x86j\xa1Tb\x00\xf0}\xa7\x05\x17\xe1\xe7\xb4\x08\x17\x83\xe3\xafX2\xb5\xe9\xcdQl\xf1-\x9a\x94\"\xac\x0cjk\xcbmb\xa1\xdd\xdf\xc3V\x19\\\x8a&\x0c\xadG\xd9j\x1d\xe6\xa4\xcf!\x1bd\xf3\xca\xdar\x03\xdb\xd7\xf4QF \xd9\x8b:\xba\xb7P\xac\xb0/\x8c\xb6&\xcc\xf0Eu\\\xee2s\x90\x15{\x8c\x0d'\xf5\xaf\x98\xc5\xa1\xcfdN\x92\x99\xd2\"k\x98Q\x86\xde\xe2t\x8b\xc3\x98\xc5\x17xD\xc9,\xbe\xe8B\"\xa9\xe0\x1cY\xff\xad\x0c$\xf2c\x97\xddZ\x89>\xccw\"\x94zh\x8e\x04g0Q\xe2\xe1Bs^\x84\xf9k\xef\x89\x11l%W\xfe\x94-\xe5\x8fy\xc2}\x06\x06\xdf\xca\x84\xe3\xbf\xc1\x1ee\x80\x8d\xc3?\xa8\x01\x88) )\x0c1\xb3\x18L'\xf8u\xe6\xd5\xc1\xd0!\xb3\xa6\xbc\xfa\xceI\xe2\xa24\x99N\xf2\xe0{\x90-\x04P\xb0YQZ\x0c\x1f\x04\x01m\xa2\xb1\x11>\x98[S\x02$\x18W\x0b!\x0ca\x10\xa4C\xaa\x8b!\x89f\xe9\x85\x95\xdd\x12r)\x05=P\xbch\x86;f>IO\x1d\xa5\x8d\xc2N\x9cW\xdc\x18\xc5\xce\x06\xca \xbc\xfa\x9d\xf6\x8f>\x153\xe6FM8g|E\xf4\xd6\x9e\xb3\x08\xcd\xb9mEg+dg\x8fS\x98\xfb\xa0Pz\x12\xfa\xdc\x1a\xab\xef\x8a\xdbp=9\xe8\xf3\x0c\x17\x0c\x0e\xc6\x8c\xea\xd2\x13\x95F=\x91l\xae\xc9GRP\x12\xbb1\x1d^UI\x19\xaf\x13BWpr\xb0s\x15\x97F\xb4\xa8(\x1a\xc6'h\xbe[\x9e\xb0\xe37\xf5\xe0\x86\xbb&\x11Jm\x8dZ\xd9KA\"\xd1e\x17M\x10\x8b\xa8.\xcb\xee\xf4\x9b.\xcb\xdeW.\xcb\xee\xf4Q\xcb\xb2\xd7Z\x96]\xcfo\x8a\xe82\xb1\x7fLZ\xb8\x0dV\xeb`\xef\x9b\xae\xd6\xe1W\xae\xd6\xc1\xde\xa3V\xeb\xb0\xb5ZO\xcd\xabu\xa0\x15O\xd9?\xfbZ\xf1.\xfbg\xef\xf1kk\x8a\x1f\xd7\xb5\xbah\x9e\xdc\xb5\xc2\x8a\xa6\xa3\x8e\xaa\xc5~\xb6\x02\x08\x9c\xc1\x0b>\x9b1\xa5\xcc\x07\x84\x87\x92\xc7\x93wh\xf2\xe9F+\xf8\x07\x8d`\x98\xcd\x99\xb0\xfa\x1a#\xdb\xf4\\\x9eO\xe3Q\xe2\x0ck\x17\xfd\xa6R\xbd\x91\xda\xd4N*D3<\x8a7\xcda\xb69Y\xc1\x10j\x15\x06Q\xac\xe2\xe1\x9d\xbf\xd8\xa4\xf3.:W<\xbc\xdd_7i\xb7\x93:\x86a\x14\xb2xx\xff\x9f7\xe9\xbf\xd7v\x18\x9a\x86_m\xd2p\x075\x0e\x83(r\x18H\x95\xc3&\x9494\xb3y;l6\xbd\xc4:4v\xd1F\xc6\xfag\x1e\xf9Rx+\x1e\x83\xcd\xbd@~J\xe6\x8e8\x02\xc7\x19j6\x0dF\x9a\xec\x81\x8b\xe4\xd9dmA\xa5T\xa0N\xfeZ\x85Iw`\x170J\x1bzd\x0b\x122\x146\x9a\x9d\x88\x87\xe3\x80\xfb{\x0e,kY\x88\xd9/\\\x9bE\x9c\x16k-xr\x17f\xb2)F\x98\xffRK\xca\xdf9p\x81\x9f\x9es\xb3\xe9\x9a\xae\xa8\xddy\x10Fr\x7f\xc9`\x15\x96\xd1\xd2}\x12\xfc6}xr-2l\x80#\"\xe3\xd6\x8d\xf1\x10\x80,\xc8L\x10\x04\xe0x\x9e\x0f\xce3No\xd4\xe1r\x9e;]\xebb\x91'\xf5\x1a\xb5\x7f\xfb\xad\xd6y<\x05\xb3\xea\x9e\xdb\x0c!\xa2v\x84/\xc8\xb1^/\xaf\xed\xb6\xb4\x17\xcc\xd6,naT\"|\xdd\x11\x03\x8bv\xef\xefQ\x80\x83/b\x1d5\x9b)>\xee\x8f\x9e\xd3\"@\xfbh\xdb|sx\xce\xc7C\xe8_\x9dnBM\xfd^\x17\x02\xad1{-\xa4\x03|H\xeb\xbf\xf2\xfa\xaf\xb8\xfe\xab\xb9|\x83\xc4{\x19\xba\x0e\xec\xd0\xd3\x83!\xcd`\x87\x1e\xa7P\x96\xe8e>T\x1e7\xdf\xc0\x00\xc8B/\x18s\x15\xacb\x99\xc24\xbb\xe3\x13H\x98!\xedh\x94\xd8%\x80\xd1,a\x12\xc0\xc5,\xe9\x94\x00f\x18\xbc,\xe1:sZ\xdb\x0e\x83\x1f!\x01\xcc\xe0\x19\x1a!\xa3\x04\xb0\x82g\x90\xd9%\x802\x94\xc2(\xc2C\"\xbbI}q\xe3\\\\J\x91%\xd7.Ao[\xf7o\xd4\xd9\x9d\x1aR\x03\x03\xaavu\"\x99\xfc\x7fmG\x93\xce\x8e\xd0C\xdf\x0c\xc7l@L\x8b\xb9Y\x93\xb8L|$\xddt\x9f\xf3_\xadVj\x0f\x14\x1d@\x99\x83\xa6\xe4,J\xf9F\xad\x9b\x8f0\xc2\xe0\xb8x\x1d\xa7\x18\x97\xc03\x04d\xe1\xae\x92,r\x81p\x8c\x10\x84\x87\x0f,P\xc7\xcc\xe7\x91t.<\x16\xc9\x11\x92,\xbd\xa6\xfc\xaa\x88Fk\x0f\xa8q\xcf\x00\x85\x18D\xea\xc1\x19\x05\xcc\xac\xd8\x08\x899\x07Ay3\xd9\x9f\x89\xd5\x1db\x94_\xdb\x18K\xa8pGO\xea\n]\xacU,98\xc9\xc1{\x9e\xd7NM\"\xe2 \xe3\xef\xf0\xafA`_r\xeeeg1\xab\xca\"\x9e\xd7A\xa9\xec\xf1I\xf2:\xae\x805^\x86\x02^U'Q\xabJo\x08\xff\xc5/\xdbJ\x0b\x94c\xde\xf2^\xd6k\x18\xdb\xc5\xfb\xbc\xdc\xa0\xcf>\x8e\x8b7y\xb5A\x93_\xab\x8a\x80\xa6\xdb\xdb\x0d\xba\xed\xe5\xb1x\x9b_6h\xf3\x1fN\xd9q>h\xf0\xbd\xdc\x14Z\xf3o\xc4I\xd9,u\x01\x98A\x13s>\xd5\xbd\xa6\x98\xc2\xb1\xdf\xf9T\x97v\xfd\xdf\xf3\xf7\xef\xfa8\n\xbe\"\xe6\x1bJ\xdb9\x06\x11\x0c\xc4\xccr\xcc\xc32<\x06\xdd\x93\x0e\xe9\xa3&oFp\x19\xe6\xb9\x88\x0d\xe6\xf7\xc3R-\xf8*\x05,\xef\xe1\x14\xf6\xc6G\x07\xb6\x90q\xbfv\xe1l!A3I\x92\x1ec\x16\xac\x98\x03\xa3\xce\x97\xd9\x8c\x992@\xa2\xc1)js\xed\x0c\xe40\x87\xde\xcf\xff\xa8S\xfc\x16\x93{3drv\x1bDw\xcb&\xf5t\xb78r\x95\xd8\xa7\xbc\xc1\xb2\xa6+\xa9,\x82\xe3\xb0\xfbG\x98\xab\x1c.F\xe61}\xd3k\xb7\x9ce\x1dS\x8f\x07M\xfdm\xd7\xd4\x15St\x8d\xf1\x90\x877f\xc3\xcbk=^\xc659\xb1m\xd7\xf2Yv\x01#\x98\xee\x1f\xc0\xf7\x90\xcf2S\x90X\xd8t.\x9f\xba\xe6\"4\x12\x13\xd4H\xb0\xd8\x18\xf6H6\x0e#\x01E\x04\xef*NK\xbb}\xc7\x08\xc9 k\xdc\xb7O\xf9]\x9c^c`\x13Lj\x00W\xe4.K\xe7\x82\xf6ak6\xd0\x0b\xf7\xa5*\x82@\xa7\xc8\xc7K!\xbes\xd8\x18\x8ca\x80\xb8\xb0D\xc4\x0f\xb1i\xb2 \xba\xa8\xf1\xe3\x9fY\x03\x03\xe9\x91\xfe\xf4\xd8t\xb6\xe615\x88$t\xb0\xc7\xc1\x9c\x93/ \x8b\x17\x06\xae\xe8\x87\x1ef\x88\xd4>\xfd\x84\xdbS\xef\xe3\x86\x9b\xf5\x92\xca\xed\xd5\xadud\xaf\x17\x1f\xa6\xaa\xe1\x0ewG\x8b/\x00\xf5\x10\xdb\x18\x94\xe7\xd938\x84\xef)\xfd{\x061\x1c\xc3\x04v \xf6<\xb4\xd16\xbc\x184\xe1\x8f\x1bMxoz\xb4wt\xf0tz\xf4\x8df\xbdg\x9f5iOk\x17\xa7\xc5\x16c\xd0\xe4\xde\x0d\xbe\x1f_s\xb0lG\xb5\x03\x9e<\xfa|\xfe\xa4\xcc\xc88\x9dZ\xaer\x7f\xcf\x16`\xec\xb3\xa5\xf6!\xe6<\xae\xdc\xc6t\x97\xbd\xa3+\xb07h\x0c?>z\x0c\x87\x961\xecO\xd9;:\x86Cm\x0c\xf2\xafB\xa7\xeb\x86\xd8\xef\x08\xaf\xb8aJ\xeaS\xf8\xaf\xff*}=\x08&\xe1\xb9O\xfe\xeb\xbf\x88\xcf0\x05\x0bC9\xa2X\xbb\xbe!\xa5\x888RR\xc4^\x17\xe5^\x13\x92\x8c\xe5\xea\x92\xbe!\xe2\x1bR\x7fC\xa4o\xca\xba\x04\x93\x1d\x1b\x03\x985:\xcf\xda\xea\x1a\xd7\xc2\x1a s#\xf9IM\x81\xc1\x8e\x9eeE3\x86\x11\xec\xec\x101\xef\x13<\xda\xe3\x9e\xe9\xd2\x0f\xbe~\xc2\x87C\x00\x02o\x90\xd4s\x9c\xf8\x9a\x82\x83o\xdc\x90\x1e'\x07\xedc5\xa8\xd3\xa9\xa5Sn\xe9\x81\x8b2\xb9@\x9c?l\x1c\xed\xcd\xfe\xbaq \xb5\xa1\x0cf\xc88v\xa7\x8f\\\x8f=}\x1c\xae}A\xe4\xa2)\x16\xb18\x7f\x93\x83\xa7O\x9fN'\x94\x8b\xa8\xdf\xef\x0e\x1c\xf6#\x97\xaf5\xec\xd6\x18.D\xe2Li\x06\x93\x83\xf6\x14\x94Y\xed^t\x8a\xf0\xe9\xb0\xff\xd7A4x~\xca?\x9fL\x0f=.\n\xdf\xe1\xb4\xe3:\xbbu)\x95\x00\xdf\x03\x06\xf3\xec\x05\x07\x7f\x0f\xf0G\x94\x85\x91`[~q\x82\xe4e\x1b\nf\x1a\x14\xcc\xbb\x17)3,Rf]\xa4l\xc0\"}#\x90\x89\xbe\xd7\xf5\x89Gu\xde\xf7\x80\x11!v\xa4{0\x11\xa9\\\x07@\xd7\x0d\x80\xab\x15\x9a\xb5\xd7\xf1F\xf8UX\x81\x8bu\xedw\xa7O\x0f\xe8$S8c\x8c\xd0x\xf2\xf4`\x0c\xf7\x90\xc2q?\x05\xb2\x01\x8c~\xf4t\xd8$\xee\x15\x10\xfe\xfbM\xe7\xdb\x81\xfa\xcd \xbd\n'i\xd9to\xd0p\x87\xad\xfe\xf0\xe1b\xcf\xedA\x0f\x00\xee}\xc3}\x9dd\xa1\x01\xba?n\xb816\xd9(\x1a\xb6\xc6\x82\xeb\x1b4\x8co\xb5j\xadaL\x86\x0e\xe3\xc7\xac\xbaJ\xc8#\x97\xe3\xb0w\x1cc\xc1\x80\x0e\x1b\xc7#\xd7\xa3\x7f\x1c\x93!\xe3@\xe6\xd9\xca\xcdX\x848<\x9d\xa7\x82\xe0\x98\x15\x0b\xaam_\xea\x06\x04:2I=\x96t\xcc\xe6\x88\x12\xdbc\xfce\x1dN\x1fx!H\x13r\xba\x14\x94D\xdaB\x93\xac*#\"N\xa1\x84'\x1039\x90\x15\xbc\xd1\xca\x9dP\xac^I#\x99\xf0w\\\xc9\x14\xabXW\xd3`\xa4$\xad\xa6\x10\x9f\xd5+\xba\xb3\x13c\x808N*\x18\x964\x16K\x9a}\xb3%m\x11\x15\xdd\x16,\x86E\xd5\xd7\x92\x02\x8b\xfd}\x1f\xf5(\xd6|?\xb8;M\x06\\\xb7\xf4\x04\xb4\x96O\x197\xf9\x1f4\x11\x13\x05\xf2\xd5s\x99\xfaLr\xdc5\x9b3\xc3\xf5\xf0\x9b=\x9b\xb0=C\x11)\xa5\xa9>(\x1dl1\x1b\xfb\x91\x166\xd2>\xc9\xc1\x94\xf2\xef8I>\x1b}\x92|\xee\x86IN6\x9a\xa4\x89Z\xf9\xeaI\xee\xf9\x92H|\xd0L\x19\xcd\"f;\xdd\x93\xa6;m\xca'\x07\x96\xbd6\x1cg\xba2\x1f\xcd\xdb\xdfI\x16I+\xf3;l\xff\xe6+cY\x95\x89eU\xa6\xe63\xb3\xdb\xbd2\x93\xc1+\xb3!\x8a\x15\xd2cyY\xb6\xac\x06G\x02\xd4\xb7\xd0\x03\x86\x8e6\xcbN[\xb8%f\xa8d\xc7\xe0\xe6m\xb6\x07C\\lF,=Qz\x1f\x89\xc1+\x19\xdd\x08\x917wJb\x7f\nsL\x86\xdb\xe9\x84.\xf0\xcb\x10C\x14\xf9\x1a\xdew)\x96\xaa\xe0\xf9s\x18S<\x1a~\x13|\xb5!\x05\xf0?e\xa3;\xa8\x88\xaf\xdal\xb1\x17\x12\x81\x915\x04\xc6\xc6;>\xfa\xfb\xec\xf8\xefB\xa0L\xa6O}\xd8\x99L\x0f7\xa7Q\x14\x1d\x12]Z\xe6\x930\xf9\x1a\xfa\xe5w$_v\xa7O\x0f\xe8\\Q\x860\x0c\xb4\xff\x8e4\xcc\xefH\xc2\x04_K{0`\xca\xdd{;\x80\xc4QH\xa2\xaf\"h~Gz\xc6\xbeD\xea\xf5U\x8c$\xc4-\x1e\xb0\x8a\xff@\xc4\x8fE\xfe\xd4\xbd\x8a?i{\xd6\xe7U\xd1\xf4\xb4\xe9~i=M\x06\xf5d\x93\"uw\xf5\xe3c&e\x13\x14m\xd4U\xef\xac\xa2l}\xb7\x19\xdd\xd2\xa4\x9b\x1c\xa3Cd\xed\"\xd8\xd8\xd5\x97\x9a\xa7\x97\x94\xa5\xa41E\x90+\xd0\x0fI\xdd\"Wq\xe45 \x88\xce\x0b\xcc\xfb\xb2/\xbdS\xdc\x8a\x84\xd2\x0cP\x1eVO\x13\xa4\xcb\xf0\xa6\x0c\xf3kR\x9e\x97a^\xf6gC\xad\xcdx\x80\x19kj\xc30\xf7PdU\x1e\x91\x0dz\xc8\xbb\xc6\xcbZ{\x95\xce\xfb\xdb\xcaU\xe7\x8bz\xf5\xd5\x1d\x95\xec\xaf\x08\xc6^\xda\x916Jy92Z\xe5\"A\xcb\xf4[\xb99n=\x12\xc8\x8d\x1b*\x06]\xe6\xcaA\xec\xb1#$M\x0c,]\xc2\xe4\x04b\x9e\xd5`g\x07\xcd\xc2b\x18\x01\x03\x92\x14\xd6\xd1_\xa6\xb8/\xb5\x93\x11eA&d\x17X\x18\xaf\xcd\xb2\xfe\xb105\x9aY\xda\x06\xfd\x1b\xf3\xb9\x14\xa4\xac\xf3\xb8\x94\x8a\xa9N\xca\xcc\x9e2\xcf\x9c\x0bS\xe8\xfd\xba\x00\xc1\"\xc6\xf4\xf6\x1b\x00\x02\x83\xd3\xd5\xc6\x99\xadEz\x02\x0c\xa9\xc1\xd1\xa6vC\x8c\xe9s%\xb8\xd0\xfe\xc4\xe7Y7\xfa2#\x81\xec\xe2$\x07,\xb7Y\x1e\xd1\x87n\xe9t\xff\xa0F\xd4\x96\xf8h\xf6|\xabz\xb2\x19C><\x9b?{\x9d\xf1{h2o\xcb\xb2c\xbfj.\xe0\xdc\xe6Ul\xf3\xfch\xf5\xc7s\x97\x98\xf2\x9d\xf3\xc5b\xa9\x92\xacF\xbf\x1cF\xca\xe0\xe7\x19\xc3\x0dj\x91\xd5*\xfa\xfd`O`\x0c\xe7\xd1\xc4\xcf\xa3\xed\x9b\xa1Tf\x1bl\xe3\xcc\xab%\xba>SF{\xcc\x93\xc8\x8d}h\"{P,gL\x0bo\x87'\x06\x8b}\x04\"L\x93a\x01\"viB\x85\xb6|r\xacB\x96Q\xf8g7\x15)\xeds)\x01\xa6\xd7\x91\xbc\x99\xb2\xdc\"N\x95\xf9\x10\xd6\x13\xe0\xb6z\xe8\xa3\xacLB\xc0\xc5j\x96\xc1\xbfB\xb8\x81\xcd^\xd9\x8a\x91\xa3\x8e\x81N\xf6op\nOf\xff9\xfa\xe5\xc9x\xe7\xe8\xc5\xce\xff\x0bw\xfe\xb6sy\xf1\xe4\xda\xe6z\xf3\xba;\x84+\xa0r\xf6\x0c\x9c1:\xfd\xabiB\x8f\xb5\x02ul\x96\x0e\x7f\xb6*\x00o\xcc\x01\xda\x08\xf0\xa88\x13x\xd2\x9b\xe3\xb2q\x90\x89Ex~S^\x87\xee\x14*1\x0bl\xd3J\xec\xe0\xc1s\x8c\xe6\xbd/P\xf4\xfe\xd3\xdd\xbd\xbd.\x80\x1b\xf3\xfcp\xf6\x1aP_\xd2\xe7\xb0\x7f\xb0;9\xea\xabL\x1f\x96\x88b\x97\x8eggB\x07\xc3\x93ILw\x8f|\x98\x1cM|\x98\x1c\x1eu\x80u\xf1DYZ\xc6ie\xce\xa5$\x1e{\xf6 \xe0c\xaf@\xa4~\xb2J\xf5\xe4\xe7\x1fi\xf4\x98\x10\xaa\xb3Jo/\xdd\xd9\x95\xf0\x98\x1c\xecN\xad)\x04\xc53lU\xfc\xdfy\xc8)\xf7\xd18\x80\x11\xa5\xebvx\n\x82g\xcf`\xc2\x0c]v\xf8l\x8c-\x88\xb4\x89\x9c\xef\x190\x1f;&o\xeeo\xca\x12U\xf4\xdd3\xd6\xe1\x84eg\xe9K\x7f\xc0\x07\x93v\xcf\x83\xef\xdft\xbc7\xb0\xf7\xe9f\xbd\xc3\xf3\xe7\x98\xcb\x00\x03lcB\x83\x94\xfe\x9a\x1e\x0e\x1a\x16\xee\xd3\xb0q\xedn>.L\xba0\x9d\xee\xb1\x10\x1ep\x00\xdbt\x848\xba\x0d\xc6\xda\x03\x1aq\x1e(\x14!\x92\xb4&V\xd2\xdar\xf6\x99p\x86\x19X(i+\x93\xab\xfbu\xd6\x7fy\x8cw\xa6\xe3t'\x13>\xb5\x07\xbfS\xb8&h\xa8\xd4}\xea\x05,\xe8|\xd3q\x19\x90/\xeb,/\x8b:\x85\xf1\xe0\xd6\xf6\x0e5\x8a:f\xc5GZ1\xa5\xd3\x9cY\x86a\xf0y\xd0\xfb\x0b\xc7<\x02\xfb\x89\x15'\xa7\xc0\xefU\xc6\x8c\xae6\xfdb{\x1b\x90\x0d8=\x95\xee\xdd\xc3f\x93\xda\xdd\xf5\\\x16\xb1\xdf\x07'\xcaIX*~m_\xb1\\\xbbOw\x8d\xeb\xb5\xfbt\xcf\xb0`\xb4|_+\xafx\xf9\x81V\x1e\xf2\xf2\xa7\x9e\xc4\x0d\xd4\x07\xbbh/\xe6\x0d\x8f\x0e\xbac\xd0}\xa6\x1c?\x03\x0f\x9f)\xa7sV\xcfk\xad\n\x0d\xa2\x84\x84\xb9\x8b\x87\x9cX\xb3q\xddt\xa7\xd4FQ\x10)\xdd|6\xbe\xf0!\x9fMt\xbb\xff?\xb4\xffRd\xc0t\x0ctWT\x89\xd0\x9c$\x04c\xfc\xc4j\xf95\xa1\x102S\x0b\x97!\xdd\xd7J-,\xb0f\xe8+{_l\xb6\xf7O\xf7,gH\xf9\\_5c\xf8\xfb\x13HwvN\xda\xf0\x17\x05\xa8n9K/p\x01\xa5\xbc\xd1\x1aU\xc9K\xa5,\x9f\xe6+\"\x8ff\xf0\x90\x1b5\x92\x88y\xdad\xc9!\xf4/\xf2\xe8\x8b\xf9\xf4\xe81k\xd8,\xdf\xe5\xe5<,\xc3\xcbK\xe3j\xe4.\xf1\xe0\x0c\xd2\x99E\xbeW\x17\x1f\x83\xb3\x0c\x8b\xa5s\x01\xc7\x90\x06\xabp\xfd\xd8\xf9\xec\x8d-\xe0s\xa2_{\x06\x0e\xf0v\x8b\xa2\x8d`f\xc6D#9\xcb\xe8G!\xe5c\xc7<\xb1\x80\xb0\xc9d\xf7\xb1\x83CP#NH\xec6\xd2N\x8aY\xf3\xaf\x18\xeb\xd3\xb1a\xa8\x9a\xa8a\xd8Hmbbz\xbaY\x0c\x01q\xea\xdbb\x1bT\x12a\x14N\xe3\xb1s\xc6\xd8\"\xaa\x04\xe8\xd8\xe8\xbd\x81\x9d\x98\x1e\xb8\x9d1=l\x1b^\x17\xa7*XB\xf3\xa8\x94:lh\xc6\xd6\xf5\xd8\"\xc1\x0d\xc9\x0b\x8a'j\x0dS]TG\x86sn\xc6\x81\xe3u\xd7\x98\xd0\x1a\xb5]\x8b\xb9\xc6!\xads\xa6,{\x1bO\xa4\xe4K\xf9)\x8e>\xab\xb1\x98;bK\x82\xd8#Q_\x96B\x97\xb6\x08\x0f\x94\x8e\xba\n\xa3\xcf\xc6\x18\x0f\xa2%[\x98\xfb\x9b&\xab$\xb4\xc3J\x9b\xbf\x11\xb1\xb7\xc2.b\x1c\xa3&\x8d{\x02\xd5\xf6$\x80\x14\x16@\x81XI\xb7+X,\xb6\xd8\x93\xdf\xb1\xddb\xbd5}\xe2\x0f\xc0k\x86D+\xe7\xfa\xcd\xac\x83x\x1e\xfa\x86\xda\x93\xdb\xf1\x9b\x0e\xb5\x95{U\x7fzG\xdb\x93\x89\xf1[\x8f\xd6\xb7ir\xc4\xd35\xe0\xde\xd8Z \xcb\xc1\xe9}b\x1ci\x88\x16|\x8a\x1c6\x137\xc1\x83lV\x8dF\x17\xf2-\x99U\x1dq3\xe1[\xac\n\x8bX\xcc\xa5\xc4}\x0bb|\xdd\xc7\xe2? U\xdc\x801 N\xcb,\xda\xee\xde\xa6,\xda\x81\x89*\xc8y\x96B\x13y\x9f\xf5\x91\x8eqJ\x81 \x99q\xae3m\x14\x13\x0f\x86\xe6*\x9by\x86\xe0L\xeb\xf7R3\xe2\xaf\x98e{\xa3\x98\x9c\xa7\x1ek\xfe\xe4 \xb8\xf4\x02L\xa1\xa5\xa2\x84\x1c\x8e\xc1\xcd\xdc\x9cN\xcb\x9734V\x9e\x0f\x99\x1b\xb3H\xb0\xd5\xd0\xccr\x88\x1aL\x8a\xaa!\x01\x88\xd3\x8cc\x04\xde\x80gD\xe3\xa6E\xa1#\x1c\x9a~M\x19b/\xee2\xc5H6\x0fO\x1c\xab\xb8\x85\x01\xf8\xc0%5.1ghKYf\xe8\x98\x9fh\x9e\x13\x1a\x7fJ\x7f\x8f\x15?\xe4f\xee\x03\xb2\xae\xfd^so\xb6\xc6\xb4)\x03\xf3\xb7\xfd\xce\x83\xcb\xa5|\xa3\x1b\x93\xbafZO\xbeH\xa9\xbbwp\xe4\xb9\xce\"\xcb_\x85\x91\x08\xa5\xf5\xa8f%\x1e\xe0H\x17?p\x1e\xe0H\xe7\x0d2\xce\x1b\xe8\x10\x8d\x891\xf6\x9e\x1eJ\x8b\xe2n\xc6\xd0\xf9\x94\xfa\xe2 \xbd\x8d+\xdb\xca\xf4\xf1\x0c\xa6\x94~5\xd8)\x94p\xc6r\x15s\xf3\x8d\xd2g\xc9N\xab$\xa1'\xbcPP\xd7\xf4\xc2W\xa4#\xa8N\x0cy\xe2!\x16g\x15#\xd5\xa6\xa8P\x16v.N\xe4\xf0\x80\x91R\x19\xa1e\xa1Zv\x8b\x01\xd9##]\xcc\x93A\x1a\x12\xa2\xaa\x99 \xd3v\x05\x92V+\xc2_g\xed\xd7\xb7y\\\xb2\x97\xa1\xf2\xee\xc1\x87\x02\x19\xc7\xd8-\xe8\xb0\xe8\xcc\xa2\xe6\x90z\xc1\xf5\x90\xa8\xd3t\xc3\xf8V\xf9\xb00\xb3A\x96]\x89\x1a\xd3\x18\xf3\xe6D\xca\xe6\xecJ\x9bC\xc1\x99\x14\xba\xe8\x182\xce\xe1\xf3\xf7\x14\xae\xa5\xea\xfb\x149\x1c\xb9S\x1e\xc1\x87nh\xd4\x8cAz\xa3\x1d\x06q\x10\x8a\xe6 \x84\x86\x83P\xb4\x0e\x02\x8fa\xde\xde\xf4kR\x1a\xb7\xbc\xa0\xe5\x86\x9dV\x8fB\xd8}\x14Z\x89y\"\xbe\xdb\x11\x1d\x0ff\xc3\xf9\x16 I\x92\xe1\x1c\xdaD\xa9\xc1\x8f\xaf^\xbf\xf8\xf9\xa7O\x9c\xb0\xcc]\x0d\x0e\xb3 \xe7\xc70K\xdd\xfd]O\xcb\xdeO\xbe\xac\x938\x8aK\xfe\xfa)\xdd\x16w\x7f\xf7\x90\xff{\xe4I$\xcf \x18hgP\x05\x8d\x0c\xa9;m p./I\xf16\x9bWZ>\xd6AKG\xdb\x93\x05\\\x8a\xf5C\xea\xd6\x1abwz\xc0AI\xea\xee\x1eq\xaa;u\x0f<\xd7\x11&\x1b\x9f\xc2k\x01Z\x9c\x97\xe7\xe7\x1f\xab\x84\xfc\x14\x17\xa5\xff\xf2\xfc\xfc\xbc\xbcK\xc8\x8f$J\xc2<\xa4#\xa1e\x7f\xa2p\x85UHb\x92\x96\x1fIT\xe2\xcf\x1f\xdf\xbf\x95\xfff\x8d\x8b_\x9f\xb2\xcf$e?\xc22\xfc\x94\x87i\xb1 \xf9\x9b\x92\xac\xb0\xf0u\xcc;\xfd\xf7Oo\x7fz\x91$/\xb3$!8y,\xd1~\xbe\xce\xf2\xd5\xab\x84\xd0[\x8c\xbf\xcf }+J\xde\x92y\x1cbco\xe3\x15\xa1\xe8\x96\xa5\xe9}\x17\xae\xc8\xfc]6'o\xc3\xb5O\xff\xc5:\x1f\xc2\x98\xce\xe1\xaf\x15)\xd8\xd0?$\xd5u\x9c\xf2\x7f\xd8\x97\xe7\x7f\xfa#K&\x87\x15\xce\xff\xf4\xc7w\x88\xa5\xc5\xaf\x0fa\xb9<'\xd7\xf5\xcf,NK\xf1CZ\x85\xf3?\xfd\x91\xcd;\xcb\xd9\xa4\xcf\xd1D\x95\xa1sV@\x97\xfb|I\x08\xfb\xfc\x13eg\xf20\xfa\xfc\x92/x]\xc0~eU\x84#r\x82b\x9d\xc4\xa5\xeb\xf8\x02Z\x8cO0 ~X\xcb\x80\x8b\xd1\xc8\x04g\x11\x1e\xce\x8a\x8b\xf6\xbd\xa7\xe0%\x9fE\x867h0I\xe9\xf2E#\xf4V\xa14\xe6<\xdeJf\xd5\x05\x13\xd2%(\xf9\xa0@\"\x9bE\x94\xab\xc8\x02\\\xd7\x9e\x13\xaf3<\x14\x8e\xfe\xf6P[\x1am*\x96\x13\x02D\x0eH=\x1e\x86\xf5\xd0\x87\x9dI\x1f)e\xbb\xec\xdd\x94`m\"\xd7\x10\x80\x12\xf1\xf72L\xbf+\x81\x0e\x06V\xa4\\fs\xc8R0\xe6\xeaii+7\x1b$\x07-\x83Y\xca\xa9\x0d\xeav\xd2Y\xa8\xc7\xef\x13o\xa6\xbe\x1e\xa1\x87\x19\x16ZR\xa4s\xe3+\xb1\xe3B\xc8\x8b\x80Mlc\xd3\x9f\xa1\xe5\x8eF\x91\xbe\xff\xf4\xde1h\x1aeY\xcc\x83\xfa\xba\xd0^\xb7`\x0d\x1dl\xc9\xa9(w2=\xf4\\'^\xe4\xe1\x8a\xe8\x1d\x89'G\xe8b\x13\xab\"\x92$AA\xc1l0\x8f\x8bu\x12\xdeQ\xac\x97f)q|\x9c\xfb\xa1\x17\x84\xeb5I\xe7/\x97q2g\x99\xca\x83\"\xa7\x80\xd2\xf95\xbc \x8b(\x8f\xd7\xe5\xb1\xe33\xabV\x12DYZ\x92\xb4\xfcs\x9c\xce\xb3\xdb`\x9eEH\\zA\xb6&\xa9\x8bn\x03,j\xa7\xf3\x8c}\xfa\\T ^\x9f2\xc5\xf1\xb3_\x9e\xf0W\x98\x81)\x88\x92\x8cE\x8c/\xf08\xbd>\x81|g\xe7\xc4\x03\xae\x9a\x94t\x8d\xb3l\x96_\xd8\xad\x02\nWS\x89\x9a\xaf5O8\xcf\x94\xd7\x94\xa4\xed\xe7\xa7\x8c\xf0\x89\xabf\x04m\xdb\x0c\x93\xa2\x12\xb7\xf4\xfc:\xdce\xe8\x83\xfa\x9aK$)\xc68e\x0eX\xb4j\xe1\xaaY\x95\x08\xd2\xe0\xc7\x10\xbb\xa9/'\xe8\xed\x07\x87\x02}\xa0\xf7hDb-=~\xae8\x96\xf6\x01?\x9b\xa4\xabx\x17\xbe\xe3\x0e\xce\x1eW\x84\xbb%\xfa\xf5\xb0\x10\xa8\xa9\xb71\xcf.\x11t\xbb\x9e\xeb|&w\x85~\xf2\xd9\xa5U,\xcc7\x1av\x8e\xe1\xa3\xee\xc1\xc5?\x98\xec\xe7\xf1\xa34 #g\xce\xe5e\x94\xe5d\xe7\xd7\xe2\xb2X\x869\x99_^:\xa2O\xf3;\x8a\xe8\x1f;\xa1XL(f\x13\xfa\xed\xa1o:6\xc4\xe9DYZ\x94y\x15\x95Y\xee/\xc3\xe2\xfdm\xfa!\xcf\xd6$/\xef\xfc\xb8\xf8 \xce\xef\xfb\x85\xbf\xe6\xc5o\x8aW5\xbf\xe4\x97\xd9OY\x14&\x84a\x03_\xa0\x05\x9fc\x1e\x99j\xdbl\x95'{^\xb00\xcaTtQKf&\xf6\xfbV\xd6\xcc\x98\xa3\xcau+\xc6#\x9er\xdb\xf9\xb2\xb9\xc6\x18\xd0\x98\x99\xd4\xa0\xb8\xa5\x0d\xcdUfs\xcb\x10PA\xc8,\x94\x17\xbd\xfb\xb7!W9\x9d\x1cy\xee\x96\xec\xeeBq\xcb\xbe\xc7s\xde\xfb\xe0\xb0?\x1c\xbf\xe3\xb0\xa1\xfd\xc9%]\x8a:S>\xf7O\xbaD\x83\xaff\xc8\xbe\x1d\xc5I\xe8\x8d\xb7g\xb6\xaf\xe1\xed\x9a\xa1\xaebHvf\x17\x041@\xda\xee`\x9e\xa5*\xffI\x9f\x07\x06\xbc(\xe0\xc6\xe5m\xe66\x92\x8d\xeb\xad\x9d\x19&\xc2\xfb\x99X\xf7v\xc3[\xb071\xcb\x15[\x9cm\xebF\xd4r\xd7\x02\x89\xb7\xbc[]\xa4K\x08\xd5\xf1\xbb^\xefm2\xed:A\xfd[\xd5%d\xaf\xf3\x11\xff\x9c\xce\xc9\"N\xc9\xdc\xa1H\x84\xc9\x8f\xf8\xabwU\x928Fg1\xa4E;\x119\x0e8\xbf3\x94Jc)g\xc4\xe0\x98\x02QX\xa7\xe6\xd5\xf4\\\xe8\xd1\xca(\n\xbc\x12\xb1\xe7q\xac\x9d\xa1\xb0\x08\xb5\x00\x0e\xab\x80\xc3u+v\xca<\xcfFV\x03KBCP\xe3 m\xdd1T=\x80\xc1D\x02\x8c-\xa8?\x0f\xd3y\xb6r7\xdeM!\x92d\x86\x8a\xaeC \xc2(,]}\x17\xe9xK\x1f\x1c\xef\x92\xd2\x8e\xa3Q*\x92\x04q\xf8\xb1{\xf0x\xb4\xbbk\xbe\n\xfb^M\x8f\xb6/A\xee\xc6\x1c\\\xc7\x9c\xf4\xe3\xf2\x93\xc7\xae\x00\xdd_\xad)fA\xf4\x9bn\x8a7x^\x93\xddn\xaa\xe7\xa8\x9fS\xfd\xef\xa0z\xf6\x9fZ\xf0\xf1\xbe.\xf1\xcb\xcc \xaao\x12\xff\xbb\xf1\xf1\xc1\xc4\xb4\x00\xc1b\xc8>Rn\xc2^ $h\xdb\xe6\x92\x10\xa3\xad\xf3l\x15\x17\x843&\xa5+O\xc4\xea\xc5\xa4y\xb4\"\xd3$\xfdN\x0d\xd2\x9e\x1f\xc29|\xe0}Id\xa5=\xf3!\xea.\xd2\xdalX~\x1e\x04:\xceI\x91%7\x84\x03\xd0\xba\xf0W\x96\x858\xd7\xddZ\x1e\xbe\x82\xff\x98\xec\x99\xa5\x05\x93\xf1#O/\xb3?m\xb2JJk\xc5n\xc6\xffq\xd0L~\x04\x0e\xcc3R\xa4\xdf\x95\x98\xf7g]BN\xae\xc9\x97-\x8b\x8e\x94\x83\xd3\xaf\xba\xd0\xf4\x82b\x8e\xe4\xfe\xabiD\xeep\nO\x82'\x9a|\xc7\x88j\x9d'\xc1\x13\x07f\xe5\x85K\xb4\xbd\x128\xb6\xb5p0\x04o\x93Y~\x81J%\x1f\xb6\xac}@\x0f.7-\xef\xa6z\n\xf3\xe5'A\xa3\xfb@ e\x1b.Tn\xeaN\x0f\x0ft/\xdc\xb8~u\xa8\xbfB\xd2\xceD?\xc4\x01W\xc3 \x85\xd1\xf6\x08\xc8\xeb\xf7g=\xc0DPE\\\xe7\xa8\xed\xd8\xf1\xc0\xaf\xad\x84\x8e2\xd02\x90\xe0\x04\xcb*\xad\xbcFPS\x17I\xe2\x94\xb3f\x8e\xc7\x96\xa1\x9a\x0c\x83*+\x90\xe5\xc3\x91\xb6\x8c!\x9b\xf6\x0ckuWi9I\x0f\xd2\x11\x10\x93\xd9p\xd7N!s\xeb\x1d\xf3:\xb7\xccBPW2A\x9d)@\xb1s\x0f\xff\x1e\xfb\xb7\xc1\xd8\x87\\G\x82h5u\x0f6d\xb6L\x82\x9d\xd4\x9d\x1a\xc9\x9bC\xb3\x01\xc7dl\xf6CAi\xc6c\xc1l\xcc\x1d\x94\x98\xc0G\xfc8Eb\xf4\xb7\x0748j*\xfc\xa6[3:\x97l\xf7\xd0\xbd\x1bC`0\x0f\x84\x98\x87\x9f\x0e)\xf3[v\xb0\xb9U\xb0p\xb5\x08\x06\xbd\xd4Q{;\xb8\x00\xf6\x9a\x94\x92\x84\x89\x0d{C\xbf\x91\xdd\x03}K\x84\xcf\x90\x99\x12\xdd=\xd4\xad\xde\xb9\xcf\xd0\xa1\xceQp\x9f\xa1\xc3\xe9?}\x86\xfeA}\x86(\xaf\x94\xbaO=\x1f\x9c\xb7\xe1\xfa[9\xa1\x1d\xea\xde%\xdc\xebdj\xf6:\xd9\xdb\xd5\x0f ;P\xfa\xf1\x0by\xedG\xfb\x81\x18\xe1o\xc9\x11\x93|\xb628\x06'k\xe4\x0dR\xd5\x8a9\xba\xc4n\x89\xe7\xa1\xa4\xe7\x81\x82\x0c\xc6\xb6\x86\xfd\xc0U_3z\xae\x8f\xc6\xe3\xa7\x93\xa3\xa3\xe9\xfe\xde\xd3\xbd\xf1\xd1\xd1\xa4-nx\xf2\x9f\xee\xd9\xf1\xf8~6\xd99\xba\xf8e\xfe\xbd\xf7/O\xfa\xd6\xc0\xa2\x86\xc1\x10>|:FZxk\xcb%\xd2U\x13\xfa\x13\xc2\xb2\x9f\xc8F\xae13v\xe3hg\xeb\x94\xf9\xee\xe7AI\x8a\x12u\xba\x88\xb1\x84\x0b?\xcb\xffy\xcaC\x97\x96\xf0\xac\xd7\xefd\xc8J\xf5\xad\x82\xed$Xb\xeft\x0c\xf7T\nu:\x08m6\x17\xc2\xec\x84\xd5r\x1e\xa2\xb7\xe1\xc9/\xc1\xfd/3\xf7\xecx\xf6\x9f\xb3_..\xbe\xbfwg\xcew\x17\x9e{v\xec\x9em\xfd2\xf1f\xff\xf9\xcb/\x17\xf7\xbf\xfc\x12x\xdf\x9f\xfd2\xf1~\xb9x\xd2\xbe9O\xfe\xf3\x97\xdb\xef\x1fu@\xb8\x7f_\xa3o\xde\xd2\xc2\xdf\x8bm\xe8>A\x8a9k\xaa\x90bu\xc1U\x96%$L\x9b\x12\xc5Ik\x0bY1z\xbe*q\x9c0\xbaX&\xff\x12_\x10\xb6Cq*d\x88\x1b\xa9\xf9j|\xd4\x96\xe42\xf15\xb9!).\x9d\xf2\x13I\x03!\xe1^\x85_~\x8a\x8b\x92\xa4$o**\x855\xb3/\x8d\xac=\x84|C\xd0\xd5\xd9Xlo\xcc\x04\xda\x9a-8\xedi8\x1bD4k[\x00\xda9L}H\x83Wt-_\xad\xe2\xb2D\xdb{,k\x10\\\xb3\xf2\\\x0d\xa1\xbe\xd5\x16\xbd\xa9\xc3\xa9\xe3\xb7\xea\xfb\x89\xf6}A\xf4\x1av\xa8a3\xd1\x06\x91\xc9\x18\xdd\xc3\x99.\xd7$\x9cH%c\xeduV0K\x8cN\xabm\xf3\xb9\xf2\xd50N\x0f\xea\x8c\xc8*\xee\x8e\xc8 )\x11,\x96\xcd1\x8f&(\x1fsW\xbb\x06\xbf=Pr\x81\xd0\x999M\xd4AwK\xae\x16\xe0k\xee4\xdf*gF.\xedr\xe1\x97i\xa2\xd2x|\x0e\xd9\x14\x97b^\x91!9[\xb0\xb0\x1fb\xf1\x0dY7\xe9\xec\x17\\f\xc7\x1d\xf4~N\xa3\xb0\xba^\x96>Ti\xb1&Q\xbc\x88\xc9\xbc\x9e\x1b\x0e-\x00\xf7;\x9e}\xd7\xf1L\x927\xd6\xdf\x82\xd9t|)\x99 \xefB\xa9\xf6\xd0Z\xe3\xac\xc9\"\xcaW`V^\xd8\xc1.\x83\xcb\xa9\xe75\x0e~\x9a\xed\xb9i\xc9\xba\xfc\xf8\xd2&G\xbfE\x9ah \x7f\xd2\xe5\xca'5\xea\xab\xfb\xb4y\x17\x16\x17r\x82\xde\xb8\xaa}\x92\xb7,\"\xdcD4\xdb\xf6\x91\xed\x84\x92=\xa0J\x813)\xb9\xadG\xbf\xcd2\xe8!\xdct\x1d\xe9\x8d\x83\x0c|\xee\x92@\x0c\x89\x92\xfc\xcd/$\x87}\xfd\xfa2\xae@\xbb\xd2\"\xcaaS\xc4\xc2\x06\x11\x91\x9aOn\xe0\x14fZ\x91\x0f\xe4\xc2X\x91\xf8\xa6\xcet\xb0J\xbb\xbb\x0d\xf3\x94\xcc\x81\xa5\x0b8\xa5\xc8\xbb\x85ZP\xdbjD\x9b\xc7\x06D\x84\xddT\"\xf6\xb0\xde\x1d\xb7)x\x0e\x15vi\x19\x0dsa\x88\xb2\xb4\xc8\x12\xc2\x80\xbf\xeb\xb8i6'\x1e\xd0*\x18>s\x9d\x15E|\x95\x10P\xc8\x84\x15Ye\xf9\x1d$$\xfc\x0csR\x92\xa8$\xf3\x00\xfeu\x0eI=\xeap>\xa7e?\x17\x04\x08\xfbJ\xc7\xf6\xae\x07e\x06q\x1a\xe5\x84\x02\x9b$^\xc5e\xe0\xb4\xb6\xb4\x89\x93j\xa4\xbf\xc4\xf8\xcb<\x8c\x90\x08U\n\\\x91\x0e\xc9v\x932\x14i\x98\xaf\x96^\xb3?\xf9\xf67\xbaY\x82\xc2\xa7(Hy!\xd1\x95&dS25\xd2*\xbb!b\x0et\x98\xb1\xc7\xe3\xbb#\xc2\xa3\x9bNT\xf0#\xa0Y+\x82\x92\xfcKXi57\x10o\x00\xf6\xc9\x96#\xeeYkud}kyS\xfb\x7fQB\xe9w\x81`\xd8\x8c\x0e\xbf\xf4\xcb\xdb\x11w5^\xb0\xfbl$$j\x0c\x901a\x1a\xddQ\xa1s\xcc\xddT\x02k\x94\xea\x97V\xf5\x14\x83\xbdr\xd9T\x0b\x16)\x90T[Q\x15\x98\xaa/\x19<\xd5\xe3-\xab\xb8\xd0p\xa4jlX\x9d@\xb8\xb3C!\x8e!&\x0d\xf0\xc5Hg\xe1E3K\xfa\xab\x99\x17\x9d\xa5R\xc0'\xda\xeeS\xf5\xdf\xc4\xfe\xab\xf6\"I\x86\xf1Vf]{\xebz\xf4\\\x85\xad\x8e97!\xecYf\x1c\xddm\xf3Lg\xf4Q \xa0\xe3\xdc\xed\xed\xce{\xd1\x1e\x92\xb97\xebA'\xe8D\xaf\xccX\xdf\x1en8 \xb6\xb0\xbd\xd0nGLs\xdb'z'\xda\xf9\xc1\xe5\xd0`+\x18y\x9a\xdc\xc2\xd3X0\x83\x1e\xee\xbe Oi\xa1\x8bO\xea\xbbqbotV\xdf\x99\x1dh\xf1\x1d|%\xba\xb6\xd1v\xa8\x93Ag\xd9D\x96\xb6i$\x16'I\xbf\xc6g-\xe2\xcf@\xf9 \x1a\x1f\x8eav\xd17\xd6\x97Y\x95v\x0b\x04tv\xdf\xa6\x1e!\xed\x8dm\x9f\xb3\xc68\x83/\x83!u&z\xee\xd4\x15\x84\x05j?\xbc\xd1\xb8\x11\xfb\x0c;\xc2\x85\xa9_\xf5\x0b 5q.\xcf\xc5!{\xbeO\x0e\x9fz^p^\xe6$\\q\xd7\xdd\xe0# \xe7\xe1\x15Z(\xe0\xef?s\xbfg\xf6\xc1\xe4)\xfa\x86\xfcX\xad\x13\xf2\x85\xa9C1MLP;\xf9\xb1zGS,\xfd\x10\x16\xc5\xa7e\x9eU\xd7K\xa6\xfb\xd8?\x1c\xa4\x83\xed\x0d\xd1d\x0ett#\x92\x99\xb9\x18\x07MyW\x93\x7f\x06\x95?h\xc7\xc4$$\x89\x0b\x8c\xb4\x02\xc2o\x83!\xa1\xb4\xcc\xef\xd4\xa2E\x9c\xc6\xc5\xb2\xcf\xc7\x87>[\x9dK\xa0?\xb5\x96\x8fujG\xed\xa52*{=\x0e\x93r\xa3NQ~\x84\xd6%\x0fD8({\xa3\x80\xfa\xdd5I\xe7qz\x1d]\xed\xecP6\x8f't\x81\x1cW\xd0\xfam\x9b\xf2\x10\x0f \xa2,\xffL\xe6\xdcc\xb5x\x9d\xa3]\xac\xa9XlRIy\\\xd3g\xa7\x86\x00\xa8\xf4y@\xb5\xb7\xc1V\xa8\xe3r\xcb\xb7i\xd5fCB\xee\xe4N\x82\xab<\xbb-\x18\xf12sn\xc6\xc1d\xec\xf8@\xff8\n\x9c\x8b:\xfaW\x13\x0f\x8cA\xc9\xb1\x0f\xfb\x1e\x8f!\xcd\xbci\xb2:\xda\x8f\xda\xdb\xaa\xbe\xa6\xe7e\x88Z\xd9\xeb\xf6pP\xc8\xe2\xee\xeby\x04\xa3 N\x97$\x8f9L\xd8\xd5\xd36\x08\xb1\xa3\xf9\x90\xcc\xc9:'QX\x92c\xbc\xdeO\x0d\x0b\xd8V\x85'\x1c\xfa\xe8z%\xfa\xac\x99\xc6i\xec\xf1\x906\xed\x1aK4\x81h\xf2\xa6(\xde[\x1e\xfcfH\x0c0\xf7\xe1\x86\xf7i\x07\x0cw\xf8\xb1\xe5\xe5\xb5\x114\x03\x97\xaf\x85H\xb23X\xc8N\x1f\xaaW\xda\xf7D\xdcb\"\x0b~\x0dt:\x82\x12\xa6\xe5x\x9b\xcd\xd1\\l\xab\x94\n|\x16V\xd7m\xd7\xd3K(W\xb6\xc5\xfc\xf1\xe8\xf9x_\xbf1PZ\xb5~5X\xc6\xd7\xcb?\x87%\xc9\xdf\x86\xf9\xe7\xf6\x16\xd0'\xc2\x8a\xa2\xdd\x7f\xef\xff`a\x18\xdd\x19L\x0e\xe0\x18&\x07\xbb\x87{\x96UP\x86\x02\\k\xcbh\xd3\x18\xce \x86c\xbe\x16Q\xf3\"\xa2\xe4H\x04\xc7\xb0\xf0\xcd\x8d\xc8\x19\x15[\xef\xbd\x06\x94\x87\xc9\xcb0I\x98\xc0g\xe2\x0b4@\xe6?\xe6a\x9c\xca\x85\x0c\xe2i%\xeaw\x0c3\xa8esR\x94yv\xc7\x0b\xcd;\x92\xe0;\x9e\xe7fN\xa2l\xce\xbd\xablxJ\xa9C?N\xea\xdePB&R\xc1\x00kP-\xbb\xbf\x07\xa7*\x17\x87B\x98$spX@w\\\x9b*\x03\xb3R\x9d\xe2.\x8d\xb8\xb8\x04\x7f_\xe1U\xfe\x90g\x11)\n\xed\xe3,E_\xd1N:O<[\xdd\x94\x92\xfc\xdc41Moe\xd8h>\x9b\xe2\xc9\x99 \xfa.\x8d\xba\xeb1\xf7f\x1cxteG\x87\x94\\\xec\x9f\x95xJ}mE\x07\x0d\x85Q3\x07\xe2\xee\x91\x84\xa4\xbe\xf4\xb7\xe2\x86\xa5?\x0f\x88\x8a\x89g =\xba#G\x8aggGB\xee>\x1a\xe0\xbb\x0dNrc\x1fr\xcf\x97\xb0\x94\xfb\x8as\xe4~k\x1f\x98\xd0\x94 E\x85<\xb5\xe4\\=\xd3_\xd1\xc60f\xbfO\xc5\x1b\xcf\xf3!\x91T\xc5\x83\xf6\xf4R\x05\x8aL\x8en\xdae\"\x1f{\n>\xa4\xbbQ\x89\x9f\x1c\x9e\xa3\xe6@\xc2\x8b\xe8\xbc$V\x8aBN\"0!K*\xc1\xde\xb8\xac\xf7\xe6\x9d\xdc\xcad\xd0l\xae\xa4\xd9\x98&\x91B_\xf4\x03\xf1\x88\xb8\xc6\x1c\x07moc\xf4QA\x0ca\xda\x9b6q\xc4!\xf2\x9c\x969\x06(\xfc\xe0\x96\"\x86\xa5\xc26\xe6n\x03\xbb\x07\xcd\xf3\xd6:vb\xa4?\x0c\xd9\xb4\x04\xcd@t\xd0a\x16\x04\xd5\xdb\x87\xf2y\xa6\x8a\xa0\x98\xcf\xb6~5\xf1o\x84Lv\x82#\x069\x92ln\x89\x02\x02\\\xeao\xe2z\xcd\x98(k$\x05\xe6\nu|\xad\x90\x81\xcd\x82\xad\x1b\xda!\xc7\xa8\xae`&O\x98^\x0e\x95d\x05\x0b\xea\xc6\xa3^\xe0j\xf8\x10\xc2\xe8\xd4$L\xa3\x0f\xc69e\x88\x00\xcd\x7f\xfd\xfa\xf6\xb1\x1bSg4\xf3\xc1q(i\xc1\x10\x80z^F#\xac\xda\x81R\x18IB\xc9\x15\x8bP \xe3c\xcdd)\x8fg\x17\"0<\xc1\xce\xad\x0d\xcf\xb4\xcfz\x17\x05!d\xc4\x9d\xf2\x98\x9a\x8f\x0f\xa2e\x95Z\x18-\xf1\xa0\xb1P \xd29v\xd7M@\xc4\xeb\xe9\x16\xf0\xd0s_\xef\xd0\x04!\x93\xc2\xcd\xc11D\xf5\xa6E>e\xc0\x12\xed8\x98\x17\x8c\xde\xf9\x1a`z\x1b)\xa8\xe8S\xbb\x88\x0b@d?\x0d}2\x1e\x90@\x86\xf2\xado\x81$\xc3\xe0\xf0\x97n\xff(\xc1Abtx%\xab\xb10ld\x85\xfa\xb8\xd0d\xa2\xe1-\xd9O\xbe\x8c\x83\xc6un\x85\x9b%G\xa7\x0d\x0bc\x95Pj\xc0\x1b7A'\xc6SviU\x1aN\"\xda\xeb7\x8e\x05\xf2\xd3\xe7a\x182xe\x9d\x94\x80\xf1_\xbatM\xec\x10\x0d\xe46\xd59\xdd\xdf\x03Q$\x07\x14,Z\x88\x17N\xad T\xd2\x80\x99&{\x18+\\\xd59\xe7\xaa\x90;\x1a\xb8\xa4]\xa8W \xf6\x86\xe6fw\xc8\xd2j\xd3\xa4/\xd9\x94C\xeb\"5\x92EJ\xf2R0p\xad:\x8a\xd4A\xab;e\xe55\x16*\x85\x00I\xbb\x03,\x98\xc8\xec\xe2\x04\xca\x13\x8fN\xa3*\x96,4 \x12\x82t\xd9\xac;\xadyy\xb7\x81d\xaf\x18\xdf\xee\x96J\x1f\xee\xe6\xc4\xfc\xd7\x84\x9b\x93{-{\xac;l:\x8e\xc9\xe5J~0\xcc\xe9\"\xa8%\xae\x9b\x05|\x97U{\xf5\xd2\xbbv\xde\x10\x18\xc7\xe7hL7\x1b+\xc4E#\xf9\xe5\x96JZ\xc5f{)wC\xc2y\xe0\xf8\xe0\xfc\xf8\xea\xc3x<\xde\xb5\xa4F\x83\xf6\x05\xaf\x8b\xed.\xbb\xf8\xda\xb5\xb1\x08\xdc\x13n{\x9b\xff\x15,\xc3\xe2\x0d\xe7\xb7\xc0\xe6\xd3\xf8\x9a\x97IQ\xc7\xda__\xd0\x8bK\xef\xc6\xb0\xda\xbe\xe5,\xac|\xc3\xc8:\xdc\xef\xfa\xe5I\xb5#\xcc\\66-\x1b~\x93\xde\xf6\x15\xf0T\xcd\xdb-\xc9\x8a\xcc\x8f^\xf7a\xcb\x07\x84B\xf3^\xf1]\xedG*5^\xb6\x94\xf2>\xac$\x10\xb1\x8e\xd7\xa4\x0f:0 \x80\x8ah\x9a\x1c\x8a/\xc34\xcdJ\xa0\x0d\xf9\x18\xa7>\xe7\xeaM\x9d\x15\xd1zn\x8b$\xed\x1a:$\xebY\xe4Y\x03cn&\xbb*\xc6\x1e\x19\xdfa\x80\xe4X\xa6\xab\xea\x84\xfb>\xac\x9b\\\xce9nh./\xe8\xd2\x8e\xd2B$\x0d\xd6J*h\x91\xd9|\xf0\x91Zc>\x01\xdd\xfb\x13\x80\xe7\x10\xb4\\A6\x81T\n\x0eM\xa90\xca\x17\xb0\xf0\xd3\x02\x00Rj\x1b\xd1%sr\xd5$\xd3j\xeb[R\xf0}\xd1\xfa\x9d\xe7C\xcc\xe5\xeeg\xc3p\xb7\xa0\x06\xa4#\xc3\xb6>\\\x94$\x07\x92\xcem\xc1*L\xd4\x8d\x84\xa2\xf1\xb0\x98V \xefb\xca\xc3^\xeb\x9c\xb7\x9dK\x07I=c\nZ\"\x9e\xca\xa2H\x00\x89\xb8iH\xe53\xe6\xa9\xa8\x06\xe8\x7f\x1b\xde\xe1Ua\x0b\x81\xb5\x11\xf4\x14PfP\xa0\xb1\x80cM\xd6\xdf\x04\x05a= 9\xa4\xaa\xa3\\C\x9f\"\xd7i\x9a\xa5;\xac\xd9'\x1c\xd3 \x9f\x83\xc1\xbf\xb9A\xae\xb6\xee\x95\xba\xee9+\x89\x05\x1f\x1a[\xf7 f2S\xe6\xe6\xe7\xc6*\x01V\x19\xee~-\x0d\xb2\xed\x0f\xdaq\xf5*\xf1MM\xf7!\xf0R\xd7\xe8\x19\xd5A`\x8e\xdd\xdf\xdc)~}\xb1\xc7\x1e\xe9\xb4\x91<\x92\x9f\x87\xda\x08\xc3\xdeP\x8e\x06_U}A)\x11\x19K\x17\x9e\x99\x05T\x16\x8co\xbd\x03!J9Z|g\xde\x99Y\xaa\x16[\x8d\xac\x86\x91\xb4\xed\x02$ \xd73 \xaaf\xd0\xfc\x1d3\xdd\xd7d_c\xcb\xba\xa0\x05Q-\x18\xc4\xeb\xc1\x04\x0c}\xe7&b#k\xb3\xb5\x1d\xfa\n\x0b\x17\xdc}\xd8\xf0\xc6\x1d\x83A\xf3.?B\xacp\x0cq\x8f\xaa\x8c\"\x1cc\x1c~\xf9\x11\x92\x07c\xee\x05\xf9\xa17\x9d9;\xdb\x8f&\x0b\xd2\x1f Q\x8ey\x19\x8e\x8dL\xbe\xb1\xaeU\xc83:\x85\x89\xf9\xf02I\x8f,) \x1b\xf8\xd1 \x9e\x8b.\x88\x152\xce\x0f/\xb0/\x85\x82\x836 CO\xd5 \xe2I#\xdc\xd9i\x1c\x8d\xba\xda\xae\xd2!\xad+<\x9b\xda\x8bA\xa7!4a\x0c\xc8\xb3\x1f;;\xbe\xa4\x15\xa5\xe4\xab\xa4/\x93\xa4\x1e\xf8\xcb\xa8=k\x0bL\x98\xf6\x8c\x93\xc4\x9dD`A\xca\x1f[\x1a\xf3nZ)\xb6\xa5A\x14\xa4V\x19\x94\xd9O\xd9-\xc9_\x86\x05\xf3\xb0\xd8rg\xce\x92|\xa1\xdc\x11\xd7\xbb\xd3\x7fw\xf0\x8f\xb0\x88\xe2\x98\xfeq\x15\xa7a~\x87\x7f\x85\x059\xd8\xc3ZQ1\xe5\xff\xeeL\xf9g\x93\x83\x84\x88\x16\xc4\xdfyx+\x19\x19\xb9,\xd3\xa2\xa7\x8d\x03\xad\x8cp0\xb59\xe2\x90\xbbm\x8d[\xc1,\xae\x9bt5\x12{@ \xccM\x98 )\x10\xf7\xf6\xb6\x1c\x98\x8e\xb1\xb8\xb5\x8eZ\xc8\xbcr\x19\xde\xe4\x8d \x8bP\x1e3\x10\x8774\x17\xb2Y\xcan)@g\xc8J\x01\"\xe2\xc6>h\\\x0b7\xfdZX]\xb7y&\xd3\xb2)\xd3\x04fiDj\xa1[\x07\xe9F\x1a\x93\xa3\xb1/\x99f\xb5E\xd4 !\x95\xbc\xc5\xa8\x0c\xbc\x82\xb5\xe9\x92\xf1\xdamt\xad\xe4\xdd2\xa8\xb6k\x0bt\x1d\xa0\xf0\x01\xb4\xe7\xd6\xbe\xe6\x852\x1e+\x9fk\xe9\xde\xed\xec\x9f\x9e\xe1~1\x89z\xd3\x1a%\xf7\x8d\xf8[\xbb\xa6U*\xd7\xa9\x7fi\xb5\x9a:\xbd\xfc.\x93\x94\xa4s\xd7\xf3\x81\xb4\"8\xfd\xa1\x19\xa9\x9a\x9b\x11\xb3\xe8\x1f\x8d=\x8a\x0e\xdf\xacVd\x1e\x87%\xd9$\xb5~\x7f\x0e6\xfb\xbe\xf0\x03\xd2\x1b=\xe2\x9b\x0c#u\xf7\x0e\xf7<\xd7\x833\xee\xbf\x8c\xc9\x13\xd1\xb0\xf5p\xff+\xa6z\xd3\x84o>2\x87R\x99\x9a\xd3\xc2\xed\xea\xc1\xc3*\x83k5G\xec\xedPC\xfc\x1275\xb5h\xee\xca\x07\x850\x8a\x0c\xaf\n\xf5M\xf4Uy\x02n\xea\x90\x0d\x0b\x1f4k\xf4\xb8\x95=\xa5\xb2\xf8V\xaa\xdf\xa1B \xc5\x00\xb6\xcc\x1b\xd8k\xfc\\\x17Z\x84\x05\x86#h)\x0bo\xb1\x10Y\n\x16\xf0\xfc\x14\xb3\x14D\xee\x82\xa7\xfc^\xc6\x8d\x93\xd3\x0eDn\xe1.<\xef\x04X\xe4-\x18\x8d\x0c\xea(\xb4\xf3\x91\xa5\xac<\xccP\xc2Q\xe3\x8c\\\xf8\x90\xbb\x89\x94\x02E\xc3\x8f\xbc\xb47\xd3\xfc\xa0\x93\xa6xH\xb4\xb0\x91\x10Tj\x03\x18F\xd4\x9aDo\x96\x14\x8fHa\n\xc2\xc4\xeeA\n\x12]\xa5\xbcx`R\x82\xeeA5\x07\x8b\xd6\xad\xf3\x8b\xb0P\xcc\x9f\xc8\x97\xf2]6'\xaec\xcb\x99\x92ah\x01\xdbx\xb4\xb0\xb8]\x029\x0b\xfb\xcd\x1d\x858\x82g\xcau\x16#\x9bX\xf1w\xb7u\xa1\x90.\xb1!v0\xfdp\xaai\xe5\xc4c\x96\xa8\xa0\xcb\x9aJNY\xe4\xb8i\xe3\xc3\x08u\xfa?V\x1f1x\xe9Zf\x86\x176\x0e\xe6a\x19b\x98\xc2S\x18\x8d2\xf8W\x982s\x07l-(\x96\xf1\xa2t1\x04\x05\x17\xbf\x08\xafkN\xe1\x95\x06m\xd5\x83\x17dW\x05\xc9o\xd0R\xca\xbcx\xd12\xcc\xc3\xa8$\xf9\x8fa\x19\xb6\x82\xfe\xb3V,\x16\xeb\xbd\xf4\x02}X\x9a\x17\x0cai&X\x99\x94{F|(/P\xec\xc0\x15\x94\xa8\xbde\x04\xb0iq\x86\x88\xc5\x1e|3\x1c\xb6^\xe3v\xe4$$p\xec\xaa\xb0&\xc1\xb4\xe4\xf6f\xf6B\xe9\xe8D\xdcO\xdaM\x9d.\xa8C\x8cj\x1c\xca\xdb\xaa\xc4\x84|\xef\xd9\x8e7~\xb1\xb1\xdbze\xbf\x95\xc6\xa6\xffL\xae\xfe#.;:\xb0Th\x1f%\x1bH1\xdf\xa8\xde\xe0\xbb\x80\x8c_\xee\xea\xa2\n\x00\x16\xb8\xd5\xd8lA\xcaO\xf1\x8ad\x15J;\x0c\xdb!U\x182\x80\xa6\xba\xcb\x0e\xfb\xd8<\x98\x96T\xeeA\xba\xb2\x83\xe8\xcaoBeY3h\x9a\xb2f\xaay1\xa7l\\\xfb\xd3}\xfe\xef\xc1\xc6y1;F'\xd2S\x1e\x9a\x92\x8d\xa1\x86\x8f\xa7'P\xc3\x0e\xe7\xdda\x87\xd5X\xe9\x96|WV\xc8 \x84t\xed\x0e\x92,\xc2\xc3~\xdcJaF\x9fe\\\x94Y~g~\x99\xadI\xaa\xb2\x7f\x86J\x98\xf2\xab\xb7\xd6\xeb8\xd1+\xd9\xe6\x0b\xe2\x86K\xf1\x82\x9b3\x7f\x8b\xc9\xcal\x89\xfa\xccV\x1cta\xd8wmxr\xc3\x1dFm\xda\xb8\xb4C\xc5\x9b\xd7\xf1\xde\x0c\x82P\xab=Im\x08\x13\xf3\xb0Ih\x15$\x82B\xbb3\x87\xae\x95\xe3\x83\xf3C\x92]\xd1\x7f_g\xf9\x8a\"=\xe7\xc2;\x01\x16\x16\x13\x13\xf3U\x08\xc0]\xcf\x0b\xe6YJ\x90\xc4E\x8dE\x07\x92\x13z\x97\x98\xe5\x10\xb4\x93\x1f!\xc4)_3\xc693;QV2\x0b/\x86`5,\x91\x0d>\xec\x0b\x93;\x8c\xee\xe0P`\xe0\xd0k\xcb\x0b]=\xc9@\xaf;\xbb$\x1eW\xcf\\\x9f\xb8@h\xd6\xe7>\xdc\xf8p\xe7\xc3\xb5\xde|\x81y\x0f}\x98\x1b\xdc\x92W>\\\xfap\xe5\xc3m/\xbb\x08\x82\x83Z\x83\x08\xb6\xfa\xa2\xc6\x05/\x8c\xf1 \xe8#\xc2\x15v2\x00\x18\xef\x8fe\xec1\x87\xe0k*1C\x8a\x8ej\xd0\xacf/\xfbi\xf8\x86R8i\xad\xdd\xea\xfc\xca\xe2\xfce,\xdddD\xc3Gb\x00vmt\xf9\x05\xbd\xa5G\xe0\xc0\x1bq\xa0\xdb\x95\xce\xe1\xb4^[\n&n\xdaU^Y\xd0\xf1\x0bT\xca5\x82\xedV\x85\xf7p\n/f fNz1s\xfe\xed\xdf\xea\x8b\x85E\xe8\xfc\xf1bvcH\x1a\xfd+\x05\x86L\xdfxc\xe00?S\"\x00\xce\xe0\x1c\xce\xe0\xd6uHZ\xe61)\x10\xa2\xfd\n\xf6\xd4uoX2\xb7<\xbc\xc3\xa9\"\xa2z\x11\xf0\xafio\xef\xdb\x14\xd1\x1bD\xc5W\xf4\x96\xb8o\x18\x19\x8e\"\x0e\xcf\xf3P\xea\xae\x8b\ni\xf5+\xa6>G\xcfj\xf7\xca\x87/>%\x11(\xba\xa5<\x85\x89\xed\xb8\xe2\xabT\xd1\xea\x89\x0fK\xcf\xf3\xe1\x9c\xb6\xf0\x1e\xe1\x8c\xd8 \xec1H\xc3\x15\x93\xad\xbf\xe2x\xfc\xd7\x81P\xe6\xbd\xd5\x9f\xcb\xe3n\xf1[L\xf7\x8bW}\xeb\x15\xdb 1\xb4\x178\xb4_=\x1f\xc2\x19\xa1\x94\xc9\xaf\xf4\xaf/\xf4\xaf\xa5\x0f7f\x11\xdf\xcaj4\xc1\xe6t\x8c\x9bHw\xed\xd6\x15\xd3\xb4\xc8\x14(\x988\x86\xbb\xa6\xba)\xd3\x97x\xf8\xae\x1e\x83A\xb1\xe8\x9bl3A\x90\x89\x97\x14\xc2\xad<\xc0\x7f_\xd0\xa9gt\xea\x97>\xacf\x97\xa6\xf0\xa2,|\x91\x1b\x07\x1f`\x04q\xf0\x1a\xbe\x07wM\xbf{\xe5!\xfc]\x99c\x11\xad\xea\xc2A8\xf7FJH9\xb5\xd0\x0f]\xdfC\x1d\xa7\xa7\xd4\xd2\xe4\xda\x08{\x01\xc1\x8d\xba\xb9\xae\x08\xb3:\xcc\xeb4\xd2\x12}7,\xae\x05\xe4\xb5\x17\xbe+ mk\x0c\x1d\xd6\x81`\x1c\x06\xfd`\xa3\x91X\xe2\xd6\x9aF\xd2\xe30n\x1c\x8c\xd5\x1f\xb9+\xce\xca\x10\xf4S\xf7\xc64\x08DV\x1fX\x9a\x1etb\xe5\x93\xb9\x95\xba\x93}\x16\xa54u\xa7G\x9e]B\xccG\xf3\x14\xb6N-\xcaT\x91\xda{\x1e\xdf8\x9e\x0fN\xf8\xf5j\xd4\xa7m \xa1\xce\xdc\x0b\xc2f\xf2\x1b\x92\xfbS35|\xf4?3\xdd\xa2\xaa\xf6\x9bn\x9a\x19\xa8\x95s\x98\xab\xf1\xcc\xf9A\xa6\x93}\xcf\xdd\xd2)uc&\xf9\xbeu\xb1\xc7\xfa\x0cyB\xc76\")\xda @\x813\x163\x8d\xec\xe5\x9a\xb58\x85\xd0\x83\x94\x1e\xde\x8a\xed_\x88K\xb1\xbd\x0d\x11\x13^\xeb\xc1\x0d\xb8\xf3\"i\xc2\xe7\x16'\x1e\xff\x8e\x12p\xb3b4b\xf1}\xdd\xff\xca\xdc\x08[\xbb\xbfoZ3#\x97h\xb3M\xed\xdd\x9f}s\xaa\xe8\xcel\xfe\x95A\x93\xda\xc5\xf7\x06\xd7\xa4\x94\xb2d\xabV\"\x96c]\x8a\xbd\xe3y+\x91\xc5\x9de\x176\xf9\xae\x9ae\x8b\xf33\x8dW\x85\xf2\xf6L\xfd-\xd1x\xc7\xeag\x9c!?\x83J\x97\xe4n\xb8\xf8\x87\xe6\xc5o%\xe4no\xc5?s\x14\xd7\x03\xee\xcbu\xf8?;G\xb1\xf5\xec\x98\x12/\xfd\xcf\xcd\xa5\xdf\xb9\xcd\xbc\xb7\xf6.+\x16\x8b\xee\x04\xb6\xc1\x04\xd5\xb5<\xb6\xee\xd4RO\xd8,\xd1:{\x96:\xe6\x8c\xb7\x9b\xeda\x9f4m\xb2{\xd0N@\xbf\xfb\xf4\x9f \xe8\xa5\xe7\x7f@\x02\xfa}sR\xc4\x01\x19q-\xe7\xbf\xae`\xb3\x9f\xa4}\xf3@\xe6\xcd\xbe\xc7\x14.\x99y\xe6\x82g\x016\xbf\xa5TOhu\x14\xe1c*DJ\x9c\x82ns\x84 \xd6x6s\x8e\x03\x8e\xc1\xc5\x08\xdb\x98D\xf1e6'/J\xb7\xf0\xe4\xee\x9d\xe7\xc3\xdd\x1f\xa4\xa2e\xe7t\xa5\xdd\x91?r\xf8\x15\xc0!\xa4\xee\xde\xc4s\x13\x0f-i\xbb\x1aK\x1a\xd7\xcb\n\x83\xf4\xfa0\x91\xcc\xae\x1f(eI\xf7\xe1&H\xb3\xdb\xde\xd6\xb0\x96\xb5\xa19\x86\xce\x16\x06\x99\x94\xa2\x9c{\x01\x05zS\x1fb\xfcc\x12d\xe9\x8a]68\xa5\xd4\x07\xc6\xcap\xb3`\x9d\x15%\xbf\x85\x08h&\x18\x81i\x11\x84\xf39&\x1a\x94Se\x197Cj\x00\xc9\xbcE\x10\xafh\x8f\xe7Q\x1e\xaf\xcb\x82\x8e\xac{j\x0by\x0c\xdc\xa1\xdc\x07\xe7{)\xac\x17\x85\x94\xad\x11\xb9\x0e\x9f\x90\x83\xe4\xd4\x16\x1b9\xed\xcb\xc9\xd2\x9c\x84\xf3\xbb\xa2\x0cK\x12-\xc3\xf4\x9a [\x1d\xb9N\x81\xa3r\xbcNK\xf5\"\x08\xd7k\x92\xce_.\xe3d\xeeJ_yA\xbb\xe5\xbe3,\x123\xb1\xc6J\x16MY\xdcS\xab2\xb9\xd3\x94Q\xb2\xa0oN\x84bG\x8f\x99>%\xc4\xd7\xfa\xfe\x18\xd6\x1af\xa0\xb0\xfa\x18\x9a\xecC\x9b\xd1)\xf6\xc1\x9a\x95\x0fVy5},\xce\xf5\xf4\xb996{\xee\xa8\xeb\xd8i\xd7\xda\xdb\xb5\xc5\x04\x9bv\xdd\xd7q\xcf\xeamJ\xe9\xb4\x0c29\xa53\x1ed\xed\xa2O\xbe1u\x89]\xe6YH\x14\xe5\x1e\xea\x9bl\x9e\x857<\xb6U\x16,ZQ\xc4\x05!\x8c9\xc5sRd\xc9\x0d\xf10\x9c-F\xb1[\xc5\x05y\xec\xc2\xb4V\x80-\xcc\x9e\x9d\x04\\\xd1\xad\xef'\x00M\xd4\x9f\xd9\x99\xb2\x0en&9\x963O+N\xdemmQ\x02\xcf\xf9H\xae_}Y#h\x8c\x15\x0f\x9bAS\xb6\xdf\xd6\xda5#u\xa7\x87:A\xd7\xb8v(\xf2\xffA]\xca\x12V\xe3*\xeb\x9dq\x03\x84\xa3\xde\xc5\xb5Q\xd7\x88\xa1\x02\xae\x1b\xc6\xa46\x1eW\x8f\xb12J\x16\xb5\xaeX\x85\x84\x9d\xba5\x15\xcf\xfb\xcb\xb2A\xb9yp\x0e#\xc8\x91Y\xce\xba\xf5\xbc\xf4\x90(\x85\x98\xbf\x9dk*}9|\xd4\xa054\xcb\xae\x89\xecr#\xc2\xb5\xf3}\xec[(\x14\x8e\xba\x8a2\x9d\xd8B\xa9\xf0\x80\x84\x14\x97@\x08Q\x12\x16\x05\x84\x85\xe2%\xfb\xbbLG\x93\xd2\x0bO\xa4\xc9\xbe\xe9\xc4|{W$\xe3Z\xb6\xc8\n\xfe\x02J\xab^\xbc&oS\x96\x1a<\xc5\x18]\\\x9d\x03\xe9h\xd4E\xe8\xe7h\x89\x92Z\x08\xfd\"\xd2\x84\xac\xa0s\x01\x0f\xad\xaeB\xf6\x89\xe4\x95\xbd\x95\x07\x0b\xce\x97\xb1\x80J\xe5\x8c\\l\xb8_\x8f\x03%8WJY\x1d\xea\x1a\xdf\x98\xbf\xda\x1dO\xf5W\x19\x7fE\xe1\x8f\x9c\x86\xb0F|\x86\xdc\xa4\xb5\x89 \x0b\xd4,\x83\xa5\xb2\x1b,iA5\xfe\xd0\xfek#\xf8d\xb9\xea\";\xc1\x163\xc27\x12=\xe7\x14:\x01\xf9\xb2\xceIQ`\xd6\xa4\xaa(\x81\xc4\xe5\x92\xe4p\xc5c\xccf\xb9D\x05\xb1`\xcd\x0e\x8c6\x86J\x1a\xb8\x935s\xccc6\x96\xaa3\x8eJ\xc2\x8d\xed\xe5\x94\xd8-\xd3jC\xa7\xf5\x0d\x0c\x08@\x07\xaa\x91\x96\x85\x95\xd5\xcc\xbd\x0c1,\xd4\xdd\xc6\xfb\xc8\xa8\x11\xb1\xc7g8\xfd\\\xa1CD\xb2\xa1K\\\x83\xcbKJ!}\x93\xfb\xa3\x1aX\xef\x8e\xbfM\xfc\xa4\x03\x93}`\xea\xee\x99\xedz'-\xc5\x12zMS\xe09f\xe1\x07\x0e&\x9eb\x906e\xe5\xbb\xe3\x03\xe3\xf5\x0cMc\x06a\x97\xb6\xce\xb3u\xd1\x845\xa4\x98\xaa\xe4\x01HyIN\x16\x05K\x0d\xc5B\xcc\xad\xe7a\x89\xf9\x0f0Nr&\xad{\xbb\xef\xe2\xef\xd8w\xa4\xba\xdd\x87r\xf4\xa9\xe2# \xa3\xf2e\xb6Zg)\xc1\xbc7\xbf=\xf8J\x95\x82\x94\"EY'\x90\x91\x88\x11%n\xa69\xf4\x90\x04x\xd8\x8f\xdcu\x0e\xf7\xeb\xec\xef|~\x01I\xffZ\x91\x8a\x9c\xf31\xd4V\x15\xbe\x94\x87^\xab\xfb\x92\x87\xa2\x15\x11\x9d|p\xc4\x14T\x01\xa7<\xc9E\x96G\xe4gl\xa8[\xb6f\xe8\xf0u\xf3\xad\x906\x96\x03\x07W\xfa\xe0H]\xab\xe3\x8b\x14\xd8\x17\xcap\xaeP^Qp\x1d)\x85\xaa\x94 \n\x1fb\xb7\x90\x1b\x90Z\xf3\xd4/\xe3\xe2C\x95\x93\xd6\xa9\xe0 D,\x8cB]\xf3\x18B\xf5\xca\xd2\xc6\xa4\xb7\xc5\xb7\x00N\xa9{ ;\xaf\x0b\xf8\xa2\xe1\xbc\xe2mV\xa5%\x99\xf7\xc5\x0d\x14\x14\xb5fc\xa9NC\xdb\xbe6ae\xae/\x1d\x0dm\x18\xe6\xfa\x1f\xc9: #\x16\xa0ph\x1f\xe2n\x18\xea7\x8bm\x86\xec\xf9\xe3\xf7@,\xba\x1c\xac\xfe\x1b7\xfd\xdb\xb7\x1f\xb5\xfd\x04GU\x9e\xe3 \xdd\xdcu\xa2{\x16\xc3\xb2\x9a,\x98#H\xf3\xcburz\x05\x03\xc2\xd4\xf8\x0e\xfa\xdb\x1c\x8c'\xe3\xdd\xdfuQ\x9c\xf3W/?\xbe\xfat\xf9\xe3\xfb\xcbw\xef?]~xq~~\xf9\xe9\xdf\xdf\x9c_\xbe\xffx\xf9\x97\xf7?_\xfe\xf9\xcdO?]\xfe\xf0\xea\xf2\xf5\x9b\x8f\xaf~t\x86\xf4\xa9Q\x12\xd3\x897L*\xd1\x17!\xafu\x97\xcd~z\x14\xfc7T\xb7\xd1I\x8f\xd3\x7f\xba17\xa6\xbb\xba&\x14\n\xae\xb2\xf4\xd5\x97\x92\xa4\x94\xf8-0\xca\xf85)\xb5\x12RD\xe1\x9a\xfcH\xc8\xfa\xa78\xfd\xfc!\xc4\xa4\xcb\x84;\xbb\xb5\x8a\x8be\x98$\xd9\xed\xab\xbfVa\xf2\x1f\xe4\xae\xe0i\x05\xe3d.\x82\xbe\xb0jY^\xb2\xccz$\xb8*3^H\xf28L\xe2\xbf\x91s\x12\xe6\x11ko\x1d\xe6\x85\xfc\xfb\x9a\x94\xe7\xe1j\x9d\x90\xf3hIV\xec;L\xd1\x10\x96\xe4C\x98\x87+\xad\xa4,I\x9e*eo\xe3\xf4'\x91;Z*\x0d\xbf\x18J\xffX\xc5s\xa5\xe0\xc7\xb0$\x9f\xe2\x15Q\n\x99%\x8cR\xf4C\x96%$T;~\x1d'\xeawo\xd2\x92\\#\xad\xd3\x94\xbd\xabVWZ\xd1\xdb8\x8dW\xd5J\x1fn]Fi\xac\x97K\x12}\xe6\xdf\xad\xc8*\x8b\xff\xc6\xba\x8a\x8b7\xabU%\x84~\xa6\xd0>\xe2:_Q\xd6p\xfa\xd4d\xbd\x1e\xd7\xaf\x8fL\xaf3\xfe\xfap\xcf\xf4\xb6\x12\x1f\xef\xee\x9a^\x87\xf5kc\xd7\x05\x7f\xcd9S\xf9\x15\x9d\xdc\xff=\x7f\xff\x8e\xeb\x00\xfa\xec\x19\xec\x9eK\xc2*\x816\xc6\xce\x9b1\xb9-p~\x93\x85\xa4kb\x97\x0d\x11P\x15*+X+\xc6Z\x9d\xf4\xa4\x93\xb2\xa1\xf4:\xedD\xbc\xb8\xeb] \xde\xc8+\x17C\xd6|qy\xe4\x9a2\xfb\xbf\xe7.\xb2]\xaa\xdfj\xdd\xc3\xff\xcf\xde\x9fw\xb7\x8d#\x0f\xa3\xf0\xff\xcf\xa7(\xeb\xc9/C\xb6i\xc5r\x96N\x9c(\x9et\xe2\xa4\xdd\xd9z\xb2\xf42\x8a\xc6\x87\x96 \x8b\x1d\x89TH\xd0\xb62\xf2\xfb\xd9\xdf\x83\x02@\x82$\x00\x82\x8e\xbbg~\xf7^\x9e\xd3\x1d\x8b\x0b\x96B\xa1P{\x85i\x1a\xae;t@E\xb3\xe8\xd8\xaa\xfe\x8d\xbd\xbc\xf70@v4nv4K\x93\xe5O\xef\xdf\xa6S\x92\x125\xef7PO\xab|g\xabr\xe1\x11c*S(VN\xb1\x84,\xe5\x92\xf4\xd9\xbe\xb4}Z\xc0\x8b\x94\x19x\xa3\x8c\xcf\x04oM\x8a\xa6\xde\x93/\x1e\xf1\xfb\xcbp\xe5Q\xccd\x1fe\x14g[\xbe\"\xa6\xf5:\\\x95oB#\xc6 +;D\xf1\xf4C\xe2$\xa2\x80b\x16\xab\x1b\xb8\xa0jV\x0d\x159\xdb\xef\xcf\xa2\x05%J<\xa3\xb1 \x91hA\xefD\xa3\x8d\xf9\xf3\xd9i\x7f\x18N\xe6e\xeb\xc6\x1c\x01\xd2*0J\xc7h\x0dM\xc78{O\xe4^\xd7X#\x9a%\xfe\x18\xc8\xe2$]\xe2 \xc2qn\x08\xef\x03\xa4\x13\xcfcW\xa4m\xc9\xe8\\\xf4\x14e\x05\xdd9\x14}\xe4X\xfd\xf8\x9a{\x91\x13qj\xb6\x8a\x9bu\x97\x10A%^\x87+\x17t2\xa2LJ\xa6\xf9D)\xf2g\xcb\xfdP]W\xe2\xb1\x95\xe5\xa6\x9df&\xd8\xcb\xa0\x12\xd1\x08\xca\x90\xdfa\x97\x7f\xd9\xa8\xcfD=\xabr\xbc\x06\xcb\x9cP\xf7Z\x0f\x84\xa8\xed@\x88D\xa5\xa7\xdd\x00\xf2\xf2n\x1c@\xd4 L\xd9:\xa3d\xf9a\x9e\xc7\x9f_G\xd3\xe9\x82\x9c\x87\xa9]\xe4\x07\x9d\xe5\xce\x04\x13\xd2\x9fJ\xf7I\xc1\x85\xe9K*@\x97Fu/7\xf4H\x86\x0f\x8cyKc\x8fz\xe8\xbfE\x9c$\x8b\xe9\xc3\x1e/_\x8f\xff\xa9\xaf\xe2\xbd\xf1h\x05\x07\xb8v\xb7\xe1\x00\xf6`\x1f!|\x0f\x0e\xe0\x8e\xf8\x9b\xdd\xbf\x0d\xfb\xb0}\xeb_^\xe8\x9dd4\x0d't\xb3\x88\xc2l\x13O7\xd2y{\xc3\xf6\xec&\xf3\x96\x9b\x8c\xa4\xd4?\xd8\xe44\xf17'^\x98\x91\x0d9\x8d\xe2M\x92,<\x12\xc6\xfe\xc1&%\xe1\xe7\xcd\x9a\x12\x7f3\xc1\xc7\xec\xc0\xd9\xcc\xc3t\x83\xf2\xedt\xb3\x08\xb3l\xb3Hb\xb2I\x96\xab\xc5&\x893\xbaIb\x1a\xc59\xf17S\xe2\x9d\xe4\xa7\xa7$\xddL\xa2e\xb8\xd8L\x16aJ63\x8f\xed\xf1\x0dI\xfd\x83M\x14Gt\xb3\xf0\xc8iH\xc9\x86P\xe2\x1f\xf8\x9bi\xb2\x99&\xf9\xc9\x82l\x887\x99'\x9bEv\x10\xcd6\x8b\x8cx\xd1\xcc?`\xf3\x88\xb3<%\x9b8_n\xceHL7\x17\xde\x84\xac\xe8\x86L6+\x0fS4o\x92\x94\xfa\x1bJ\xbcx\x9amPs\xb2Ic\xdf\xf7Y\xd7\x8b\x05\x9d\xa7I~:\xdf\x84\x8b\x8cl\xb0l\xf9b\xcd\x86r\xc1\xa6\x93\x84\xeck\x8f\x84\x939\x9b}D\x18\xd8\x92\xe5&\x8f'\x1e\xdb\xbdl\x80\xa7\x8b\xe4$\\lN\x13\x9alN\xf30\x9dn\"o\xb6Y\xae<\x8e\x03\xd9F\x19D\xecEt3Y\xe4S\xe2\x1d'\xf1\x84\xf8\x07\x9bE\xc4\xa0\x95\xd3\x8d\x14}6\xd4#\xe9,\x9c\x90\x0dI\xe3p\xe1\x1f\xf8\x07\x9b\xcc\xdf,\xbcpy2\x0d7\x84n\x92\xc9\xe7M\x12\x9f\xfa\x9b\xa5\x17M\xd2\x04I\xe0\x06\xf5L\x1b\xaeK\xf07o\xc27\x9b\xd8\x0b\x97$[\xb1\x96B\x1a\x9d\x91\x0d\xb9\xa0\x1br\xbe\x89\x16\x9b\x84n\xf2\xc5\xc2\xdf$\x1e\xb2E\x9b\x15\x8f\xaf\xdc\xa4\x9b\x9cn\xceH\x9aFS\xe2oV^8\xf9\x1c\x9e\x92M\x98\x86\xcbl\x93Fgl]\xd2\x84\x92 %\x0c\x104\x99$\x8bM~\xb2\x88&\xfe&\xf5\xc2\x88a\x8c\x17N\x93x\xb1f\x0b7\xdb\x9cF\x19%\xe9fEB\xba\xf9\x92Gi9\xefl\x92\x93\x0d\xd7\xb3mh\xba\xde0\xaa\xe8\xfb\x9b\xcc;Y\xb3\xc5\x0f\x17d\xba!\x8b\xd9f\x9e\xa4t\x13\x9d\xc6d\xba\x89\xbe\"xB\x1aM6\xa8\xd3\xd9\xa0\xa9a\x93\x9fp\x97\x84M\xbe\"\xe9f\x1dO\xe6i\x12G_\xc9t\x83\xb1\xc4>\x83\xe8r\xb5`\x83\x9f\x93x3\x8f\xb2\xcd\xf7|L\xd1\xce\x06\x87\x11^\xf3z\x8a\xf6\xcc)E\xfb\x14\xab\xfc\xa2AB\xefGR\xbc\xdc\xf4\x86\x99\x06Pw\x06\xae_X\x8b\x8c1\xa6\xd6\xb7N\xf1\xadA\xcb[K\xc6\xd3z\xa7\x01\xc4\"\x83\xc9\x00K\xede\x84za\x00k[\x81\xe2&*H\xa1c\xc9\x84\x8e\\: .1\x19\n\x0fq[\xea\xb9A\x0d\xb1hMU\xdb(\x9a([0\x11\xa7\xc2\x9b\x8d{\x87\x95\x84\xbe$U\xa3\x81\x86\xb8H%\\\xa3\x08J\x80\xf6\xb5l\x12.\x9e\x86\x19\x1b\xd6\x93\xea\x9d\xe7b\x90\xad\xa0\x91\xeaG\x8f\xf6Sn\xe8\xf7n}\xea\x8f\xfe\xd5\xbf5\xfe\xee\xc6-&J4K\x7f\x92~\x16\xc6\x11\x8d\xbe\x92\x8f\xe9\xa2\xb5\x87H\xad_\xabz\xdb0a\xadW\x8b7\xd2\xc9\xd6\x8abp\xa6\xf6\xeck\x8f\xe0SB\x9fL\x18\x97\xcf\xb0%M\x16\x8b(>}G\xb2U\x12g\xed\xd0\xa8\x9dd\xa5\xc2\xbf\x1fe\x8a\xf6_Q\x87\xb0\xa51i\x0c\xaa\xc7\x9e\xfe\xcdR\xbf4\x8b\xe2\xa9\xd7\xaa\xac\x91Wq\xc2e4Li\xf6kD\xe7^o\xafW\xe8#U\x15*\x83\x89\xd7\x9b\xf0\xdd\xc3\xad\xf6\xff\xbe\xf4K,lz\xfe\x01\x98+X\x15\xaa\x1d\xaf'\xba\xe8\x89\xc4\x9b\x1a;\x89\xa1\x8d\x14\x9d\xe64\xe3\xd27\xe2\x17\xca7a\xea*\xb3\xa4\xc5\"O\xa2Y+\xc7\x9aM\x9bx2%d\xb5X\xbf\xa7i\xb4zI\xd65~\xcd\x927\xecZX\xaab\x99[\x94\x81:\xa7L=\xb6ut\xbb\xafZ51\x99N]K\xb7\xd9\xa8\xe4\x8f\xf1q\xb1\xcd\xd4&5\xef5e\xf8\xbf\x19\xb05d\xb1\x86\xa3\x91\xc6\xe4dVh\xe3\x98b\xee\xa1\x17a=D\xd4*\x8a\xc8mv\x87 5<\xa1\x0c\x15o\xe8\xd3V_\x9aU\x90\x91\x86\xec!\x15s\xb1\xa3F\x86\xa2\xdd\xa6\x94\xe2\x80^)\x0c\xb9A-\xeb\xcdp\xddp\xa6\x18\xad\x16\xb4m\xc1)\xb7Z\x94\xd5\x8dMn\xf5P%\xbeU7_n\xdf\xd3T\x94+\x98\x9d6\x83d\x91o\xb1\xd9\x84iM\x18L\xc4g\x1a\xd2\x1f\xa3\x03\xc6\x87\xa4p\xeapX#\xfe\x8da\x8d\x94\xde\x8chR3\xfdU\xdfc\x9bb\"\xfd \xee5\xfc\xfa\xa1\xc8\xbaq\xfbN=<\x05D\xee\x0d\xf4\xb0\xb83\xd0}\xba\x92-\x7f\xbf\xab{\xaa\x0f\x89\xaf\x16_e\x0f\xcf*\x07\x89\n-\xa3\x05\x19\xb3\x16\xf4\xa3\x18\xf5\xe3\x99\x17\x97\x0c\xb8N\xb7\x02\xaa'\x809:\xd7m\xa3\xc1\x01(\"A\x84A\x13\x11\x16Z5\xf2\\.hm\x8d\x95t\xf1<\xc0C\x9c\xe2\xa7Q\x93\x18p\xfe\xad\x9f%K\xd5s\xa2\x8d\xddd\xbd\xac\x95a\x8eb\xc6[\x8db\x8d\xdd\xeb\xb2\xbe%\x9a'\xdf[\x83\xdfc\xeb\xfe\x80\"\x10\xf01\x94\x02T\xef\x97p\x91\x13\x1e\xe8uB`A\xb2\x0c\xe8<\x8cA\xb4\xdck\x8e\xb1\xb9;\xfe0\xf8gv\x18\xd3#\xf3\x98NQ\xe5\x9e\x8aa\xf1\xc6\x9d\x86\xf5Y\xefI\xda~Z\xa0\xa4y\xeb_;\x07\x9f\xa6\xdb\xde\xa7>\xfb\xc7?\x90\xb6\x01EN\xad\x0d4\x04\xc1\xf8\xb8\x0c\xee\xc8\xe0\xfa\xdamt\x0e\x83\x8a!\xe2\x8d;\x0d\xeb\xb5\xceE\xd7mLx*\xd5\xf2+\xd4\xbc\n\xcd\x90\x9bE\x0b\xe24\xc0\x0f\x06\xbfb\xb71\xf6h\x9a\x13N\x1aD\xccR\xb8\xc8\xd4\x1b[\xbb\xca\xdf\x03\xc9\xca\x9bF}\xc2\xbbw\x1a\xf8S\xbd\x8f\xb4\xdb\xb8\xf9`5\n\x1f\xf3\xd8\xc4\xcb.C\xfb\xd9\xe4\xd3\xed68^\xb1\x9f}V\xb8\x0b[VZ6\xef4\xb2w:\xf7s\xb7QIqO\n\x1b}\x9a\xbcJ\xceI\xfa4\xcc\x88\xe7\x07\xb0u\xeb_\xa3\x7f{\xe3\x83\xd1\xee\xce\x83pg6\xfe\xf7\xfd\xcb\x9d\xe2\xef;\x0e\x7f\x0f\xf6.G\xfe\xe5\xd8\x890\xb0\x91;M\xf8\x8d\xd1\x0b\xdf\x9d\x98\x96\xbc\x89\x1b\x9d\xe7]8\x0d\xef\x951t\xa0\xfb\xf0:\x90\xfc\x0e#|f\x08xp\x1e\xdf\x16O\xebpzx\x81\x1e\xc9\xb6\xa5\x9d%\x8bEr\x0e+\xd1I\x0f\xb6u.\xec\xd53\xbc\x19\x9e\xd1:\xb2\xabr\xb67oV~\x9b\xb9Z\x13\xc7\x8b\xac\x1eR\x9e\x93d\xba\x16je\xae`\x8c\xe2\x1ew\x93\xc7_h\xc8:\xbeX.z\xc7\xd0\xf9LyS\xb0\x1e\x867\x17\xe5\x9b<\xc9\x85\xfe\xb5U\xf9\xda,I\x97!5\xbd8\xaf\x8cQ\xec\x00\xc3\xbb\xd3\xca(\xed\xef\x9e\x95\xef\n\xc4\xad\xa7\x1e\x01\x01G\xeet\x950\xa67\xb2f\xe6\\3\x91\xbdT\xcc\x0d\x01\xbf\x8c\xf4\xfd\x83Pe\xf4B\x99\xe0[\xbc_\x15\x9ay\x82\x97H\x16\xd306u\xackJot\x94MN\x92<\xa6&-:\xbbN0\x9c\x8fq$\xcal\xccl\x8d\xb9!\xd4eH&\xa1l\xcb\x8bx\xa6\".\x96X\x06r\xc1\xbe/\xb5i\x95\xcfw[\xbf\xc6\x94\xf1\x92\xf9\xeb\xfe\xf9\xa1\xc1\xc8\x0e\xd2\x00\xd7\xd0B,\xcc\x9e|V\xed\xaa\x9bdvhp\x08\x90\x17O\xef\xad\xd7\x11G6u\xac\xbc\x94\x80\xa7\xc8\x0fD\x7f\xc6/\xda\xed\xcf\xf2\x92\xb4\x88\x1b\xb8{H\xf7 ;\xde\xf88y\\bq\xf6\xe1\xf1\x80c\xe9\xf9\x81\xa1\xfc8h\xf5\xb9 \xb6\xe3\x13F\xd2\xd7\x01\x9c\x16\xb5#0\xb5\xfd\xfb\x00\x0e\xc75\xe1\xd5:\xf6R\xdf\xa4}E\xa7\xe6\x07\xb1\xd4 \xf2\xcfe\xf9 9\xf7w\x82\xd6\xc3,\"\x8b)D\x19\xe6\x0fY\xa5\xc9Y4\xc5\x13@G\xb1e\xa3g\xb6\xc1\xb2\x89\x7f\x85!<\xf3\xa2\x00\xce,N _\xd1\xc4\xc1\xc7\xf3\xd5\xd5\xd9\x00\xc4\x10\xe6\xe5\xd6\x99\xb7\x8d\xe69\x0c\xe1\x0d\x1b\xcd\xdc2\x9a\xe7\xcah\x9ew\x1d\xcd\xb4m\x08\x1fa\x08\xaf\xd8\x10\xea\xa5E\xd4\xeb\xa32\x84\x8f]\x87\x10\x96\x00 \xdbF\xf3\x03\x0c\xe1-\x1bMh\x19\xcd\x0f\xcah~\xe8:\x9aY9\x9aY\xdbh\xbe\xc0\x10\xfe`\xa3\x99YF\xf3E\x19\xcd\x97\xae\xa3\xa9\x1e\x89m\xe3\xf9\xdd\xe2\xb7$/\xe4n\xbc\xdfQC\x1eR\xb2C\x99\x1c\x85\xcd\xaf\xe0\x00~\xf6P\x85\xd6\xcb\x99\xb0Q\xdc}\xc7\xef>\xe5D\xd4\xcc\x17\xc9K\xcc\xf6w\x93\x1bKIf\xab\x07[\xdb\xfc~\x85!|\xf0\"\x0b\xb0qv\xbfv\x18\xe3\xaf\xedc\xac\x1c\x9emC\xfc\x05\x86\xf0\xb9}\x88\xbft\x18\xe2/\xedC\xac\x9e\xd0mc| C8j\x1f\xe3\xcb\x0ec|\xd9>F\x95\xc1j\x1b\xe1\x8b\x96\xa1\x1d#\xf3S\xb0a.\x03}!y\xd6\xa3\xd8\x1b\xf5\"J\x96Y/\x00\xceg\x8f\xfd\x00\xa2\xa6\xa1\xbb\xcd\xd7\x03\x14\xc1\xaam\xdb\xb1\xab\x82I/\xd0I\x82!\x0b\x06\xabV\x97P><\x12\x0fU*\xf0\x02\x190\xf6\xf4)\x13*\x03ap\xe7\xeb`\x1f,\xbb\xa2xJ.\xf6\xa1\xc5g\x90]$M\x93t_\x13/\xa7^\x97\x96x\xb0v\x9cP\x18\xe46\x94\xb8\x01Cx\xdd\x8e\xb47\\pA\x00\xeb\x86+56\xda\xbd5\xfe+\xcdl\nvNI:\x1a}\xbb\xbb\xb1\xc6\xd2 \xc2/\xa8\xab\xd8\xdf0h\xe9\"\xa0\x19\xbco],\x17BwE\x8c\xf2]\xc4\xbd\xae.\x96\x0b\xdc\xb6\xf8\x17\x166\xb2\xad9\xd7\xf3\xb0o\x98\x94/\xbe\xfd\xf7e\xc0\xbe\xbfq#%3\xd5\x1d`\xbdBO\x18\xda\xc7}\xcd\xff\x14%WD\xb9'\xda\x0f\xa7S\xf4M\x0c\x17?\x97O\x0e\xe0o\x8f\x0eX\xe3g$\xcd\xa2$\x1e\xf6\x06\xfd\xdd\x1e\x90x\x92L\xa3\xf8t\xd8\xfb\xf8\xe1\xf9\xce\xfd\xde\xc1\xe3O\xb1pl\x87\xdf^\xbf\x02r\x81K\x0c\x13\x9e\xe2\xf7\x84\xc0)\x89I\x1aR2\x05\x1e\xa4\xf47\xa3\xff\x93\xbc\xa4!LL\xa7\x8f\xa9\xb1\xbd[\x9f\xde\x7f\xf7\xe9\x96\xf7\xe9\xfd\xb6\x7f\xe3\x96\x05\xd9K \xc2\x10\xa2\xd1\xa0\x19\x8c\x08F\xc6B1\x16\x9eJK\xed\xf4)\xea\xcb~{\xfd\xea\x90\xcf\x8d;\x93\xb8\xf8\x80\xb0\x89$\xc2\xc3\xa8l\x8fo\x82\xe7i\xb2\xe4\x1bA\xb4\xd7\x9c\x91T\x8a\x99$\xbb\xa4M\xb2K\xb0\xbcm\xcd\x13&)=a`_\xc9y\x06Pxi\xaaYP\xac\x8e_g\xa2\x0eI=\xa9\x92\xbc\xd8\x12\x94\xe2\xfc\"\x99\x84\xac\xa9~\x86\x8d\x1b\xf4K\xa5\xde\xd2\xb4\xb5z\xa8\xa47\xee\x11y\xf0\x90~\x96\x9fd4\xf5\x06\xbe\xac\x17tS\xa7\x8d\x01\xd5C=\x85(\x86\xd8\x87\xb8^>%\xe5\x8e\x8a\x18g8J\xc7\xb2\xc5!&[\x1bM\xc9$\x99\x92\x8f\xef\x8e\x8a,]^:\xda\x1d\xfbc,\xdd;@u\xa1\xf6\x9d\xc1\x98\xdbU{.\xf8$\xb7us\xcd\x9a\xd9l\xec\xb4\xd5h\x15_\x86+\x07\x7f6\xf19\x12\x83\xea\x8c\x88\x0f\xdb\xd0\x1b\xa2\xb6\xb6\xf9\xb4\x9a\x99T^\x97~\xff\x8f$\x8aqy\x9aS\x13\x19{\xec\x83\x92\xf3\xa9d\xdd\xa0\"n\x17K\xd5yD1W\x04\xd0\xcb\xe9l\xe7~\xcf\xf7\xcb\xbb\xbd\x930#\xf7\xee\xe8\xc6Pf\x10jv\x9d`\xb8Y\x94\xc4\xd9{|\xcb\xe4\xb5\x13.V\xf3\xb0%\x97\xacz\x154\\j\x13\xe7=\x1f\xb7\xd0\x02S\xc1\x85)\xf1\x88\xfa\xccpd\xeb7\xe6\x92\xd0y2\xbd\xf2h\xf8\xe7\xa6\xf1\xc8\xa7\xceLDs\x8c4<\xfd\xb3\xc0Y\x1b\xb2\xf3 5\x98Y\xcb4\xe5\xc6\xce\xe8\x9cT\x94\x8c\xeeQ\x0cF\xbd\x91\xf4\xe6\xa5F\x0f\x11\x85m\xe1\xa5oz\xe5\xdf\xa2\xcc\xd1(\x0e\xd8\x06\x0dt\xfb3\xf5K\x9f\xfa\xff\xd9\xdb\xbdu\x1a@o\xbb\xe7\x8f\xc5\xfe\xd4-\xa9\x91J\x11\xdb\xa6\xd6d\xee\xaa\xac\xa4\xc1\xb1\xa6P\x9a1\xc25- W\xac8\xe5\xb4\xb9\x8ct\xf2\x18\xa9\x8e\xbc\ns\xa9\x143\xa4's\"\xc0:\x8f[d\xcaT:&\xcc\xd9\x98\xd4(\x8d\x96\x9e\xb2H\x9f2\\\xa3c\xb4\xd8\xf4z\xb6\xe1\x1a\x92\xab9\x0d\x93\xc1\xec\xb8\x84\xd9\xd7\xa6{Y\xa0I\xe7\xe6\xd44m\xe6\x9b\xb0\xecd\xf1\xd1\xad\x7f]\xec\x14\xccu\xeb\xb2\x05\xc6\x14t\x7f\xe6\x08\x85\xfdgS\xd8\x976\x85\xf5h#\xecb\x1ba\xf5r\x9f\xca\xff)\x1f\xf0\x94\xdfl\xa7x\xf7\xee\xfb\xfd\x1f\xf2\xd9\x8c\x08\x7fq[\xf5\xa3\xb3\"sSq\xf2\x95x\xa2\xa6\x19\xacX\x8c\xc0%S|o\xc49U\xfe\xe9\x18\x91:nT\x8cr\xca\x06\x89\x94\xae\x1cWjcD\xf59\x0eAaO\xf9T\x94d\xbc\x8bhBL^\x97\xc4\xb8\xbc<\xa4\xaa\x9aL[\xe4K\xe4\x14@-1\xe1c)+S.\xd9zZr\xfdP\xecx\x99\x97\xbe\xaf/\x9b%\xb9\xf4-\xa6\xd6\x16\xc3\xb2\xc5\x17\xae-F\xd6\x16\xb3\xb2\xc5\x1b\xae-&\xed\xb3\xbey\x13\xb6&e\xd3?\xba6\xadI-\xaf4\xbd\xe5mQ.\x87\x8f\x16c\xb7\x06C\xd7\x06\xeb\x898L\x0df\xae\x0d\xce\x1d\x1b\x9c\xb4\xaf\xf8f\x83\xdd:57s\x1d\xdf\xb41>\xf5\x17\xf1R^\x83\x85x\x91\xfc#\xe1\x7f\xc4\x8a3+\xcf\xd5\xcd\xee\xbc$kL\xcf\x17\x8a\x17\xe2)\xb9\xc0\x1b\x19\xbf\xf1$\xcb\x92I\x84\x99!\x00s\xb8\xc4e\x00\x1c`x~\xdc\x97m\xb0\xae\xfbe\x0bl\x00\xfd\xf7\x04k84\xe9\x07\xa6\x19\xf8\xfb\xdf\x8f\x8f\x8f^\xbf\xfe\xf8\xe1\xc9\x0f\xaf\x0e\x8f\x8f>\x1c\xbe\xc3?\x8e\xff\xfew\x8dji\xd5\xfc\xe2\xe5\xe1\xef\x87\xcf\x0c\xaf\xcf5\x1d\xbcyv\xf8\x9b\xf1\x83i\xf3\x83\xb7\xef\x9e\x1d\xbe3~p\x06C\xb8\xdb\xbc\xbd\x86!\x0c\xe0\xd1#]\xb5\xf3S\x18\xc2\x1av@\x93\xaa\x7fi\x90\xf7\x8f\xed5\xae\xf7\xeb\x89$A\xcf\xf9\x9f\\\xa5\x19\x13-?o9\xd8\xb9q\x18\x0b\xbb;\x92\xe4\x0b}\x8bT\x1c\x0dE\x83\xbbn\xdb\xe9=O*\xaf\x7fxh9\x89D\x84\x9bF\xaf^\xa9\x0e%\x0bH{\x98x\\\xa88w\xb0JH*r\x9e\xcb\x94\x05<\xd3\xc6\xeeCLw\x11?\x84h{\xdb\x87t\x14\xf1$\x89\x11\x13\xe8\xcd\xee\xf5\xa9\xd3l\xed\x01\x0d\xaa;:\x06\xa2\n\x98f<\\\x82\xf6\x8f\x8fy\xe9|\xe2\xfd\xc1OW\xf6\xc4\xa9\xe3\xb7\xd6Tb\x85\xf5A)\xe9a\x13\xc1P\xb9\x04\x8f\x1f?6\x995\x84\x92j\x1bb\x11C\xbd\xd9\xc0\x9d\xbd\x07w\x1e\xdc\xfb~\xef\xc1]\x9ca\x19\x99\xf8&|\xa3o\x85MZ\x93\x92\xcf\x04>\"\xcax#\x90\xb7Q\xf1\xe1\x06\x9c?l\xc5\xf2\xeb\xf9\x9c\x0dm|v\x90\xda<\x19jP\x16\x9d\xde\x92Q\x91\x14\x1e\x0da'\xae\x14,\x1cJ\xd0\xd5_&\xf0xXW\xc0\x9a\x06v\xd4\x96\xbd\xf1\x83\x18\xb9\xe3\x86}\xed\xda^\xbd\xaa\x8f\xa1\xbd\x0f\x0e\x80\xab\xc5i\xc4\x986\x97/\xb6\xba\xbf l\x03\x1a\xc5j\xb1\xb4\x8cC\x92\xe5\xe2\x99\xbc`\xac\xde\n\x02\xbf\x9f6\xabT\x83pd\xd6\x9c\x07\xef`\x08{\xcd\xdbo\x9c\xb3\xb6\xf3M\x9d\xa4\xcd6^\xf1\x93N\xbe\xa09\xda\x9e\xc1\x10\xde0\x1cye:\x02\xbe\x1a\x08\xf6<\xca0\xbb\x8833\xfe\\\xae\x94!\x99\xa7\xb4Z\x94\x0b\xc5\xb6\xe0\xa0\xb2l#\xf6\xbd\x85\x8a\xc2\x01\xa4\xc5\x19\x12\x89\xb2\xc0\xd6\xd3\xd0\xe0\x078Mb\xd3\x89\xebH\xab?\xda\xa8\x82uH\x1c\xfd\xac\xe3j\xad\xdcc\x18\xd4\x0fv\xees\xebWW6\xf6\x8b\x9d1\x00S\xd5h\x8a8\xe3\xd4\xc5\xefv5\xe0\xaf\xda\xf4\x1d\x05-\xe7Un\xb5\xc5\x96\xf5\xdd\xfdj\xef\x8e3(o\x90\xd6\x8e\xde`\xedR:ze\xcaM\xa4\x9d\xbb\x92\xb7\xdaiD\xbf8\xc0X\x13\xcc,\xb8\x14\xa7.^Z\xbb(\x92\x01\xa8G\x8e\xdc\x8e \xcf\x95-\x85\xe8>M0]\x83\xb5\x80\xb5\xbc$P\xd1y\xbd\x12\x167\xac\xd5\xe6!\xe7@\xa85\xc3\xfb\x96\xa9^\xd8\xe1\xc5\n3\xd3q\x06\x0d\x92\x14\")\x15 5K2\xe3[.\x0b\xd8\xd3\xcf(\xdd\xf0G\xfb\xe8.o\xeaV\xbb\x8a\xecj\xa6\x083\xc0\xfd\xc5\xb7\xc1\xbdO\x13\x94\xc5$\xc4\xc5\"\x84\xcd\xb5\xa0\x98\x9f\xfd0\xa6\xe9\xbax\x99\xba\x8e\xf2\xc6\xb7\x8dR30\xa2\x0e\x84\x8dSH\x91\xf2V\xe8<\xb6\x1f\xadc\xf3\xbe}pr4h\xe0\"\x14\xef\xd7F\xa6\xfe\xfa\xaa\xa8\xaa\xa8&\x1f\x81e\xb0\xbd\xd1\x918\xa0\xc75\x05t\x00_\xfb/\x0f\x7f\x7f\x0fCx\xca\xfe\xfe\xe5\xc9\xab\x8f\x87\xec\xd7\xcf\xec\xd7\xe1\x9b\x0f\xef\x8e\xf0\xe7\xbb\xa0\xd2\x7f\x14g+\x9e\xed\xbc6\xaa$O\xab\x99\xb9m\xf4\x85\x1d\xf0\xe6\xdc\x0bJ\xcb\xa3g\xe3\x0em\xd6\x1b\"\xdeK\xae\xb7x\xd9Of\x8e\xed\xbc\xf4\n'\x92\xc6\xc0^V\xa7L\xbe8\xb6\xa9\x1b\xdb\xcb\xab/*\x82\xef\xf8\xb84\x8e\xb2\x91\xfc\xbb\x17@\xef\xb2i\xcfQ\xfb\x99\x84\x939yG\xb2\x962\xc7JW[\xbc/\xfc\x10d\xc5\xafB\xd6\xfb\x18\xe3\x83)\x17\x06\x957\x87\xfc\xc5\x12\xeb\xcb\x8a\x0f\xa2\xfc\x99\x14\x1c\xcb\x8f\xc4\xd9\"^\xb0M\xa3\xe8\xdf%\x86HLdB\xcb\x82d\xbc\x02\xa8K\x0f\x89S\x00\xbe\xe8b\xd6\xda\x05\xf1^\x04\xf0\xd2\x0f\xe0Ee\xf1%\xbdu\\\x13=\xa6\xdf\xe0-\xdfp\xc7\xf4\x1b\x16L\xbfQ\x19`II\x1d\x9b\xd6\x0d\xf1\xc65#\xfc\x88!\xfc\xb8\x89\xf07\xae\x19S\xea\xb5\xdd\xf5=|\x13\xa64\xbb \xde\x8f|=\x7ft_\xcf\x1f-\xeb\xf9c\x8dr\xd1o[\xcb\x97\xfd(\xe3-D\x94\xfd\x92\xda[\x86\xdeB]\xcb\xc6\xaf(ro4\xb5\xb7?\x05\xf0\xcf\x00~\x0b\xe0\x1fM\xa5\xe9\xfb\xc3\x7f\xa0\xc2\xd4$9Rj\x11\x1d\x8fCQ+\x83\xd6\x88M\x17\xf6\x95\x18z\x90\xfc\xa50.}&\xebL\xcbC\xf2\x91$\xb26\x88\x1c\xca\xf1gQ\x0b\xab:4\xd2eh\xb1u\xf2Q\xa9\x9f7\xcc\x9f{\x16:+\xe8\xd2\xf6\xee\x84\xe1,\xa8\xdd{*\x0e\x83zm\x1fCG\x91\xa1#y\x16\x95\x06\x8c\x7f8\x1aX\x90\x1b36\xf8\x13k\xcd\xfbI\xe8Z)\xf5F\xe3Ff\x16}\xbby\x0brh\xd2\xe0\x88.\xa8\xdf\xe4\x9a\xbf\x94o\xa4\xfa7~(\xdf\x88\xf5oh\xa5\x9c\x83R\xc8)TOf\xcf\xbe\xabK:\xa3\xcf\x01\x9c\x8dAd\x8a\xed \xf1t\x92Y\xc3\x16\xa0gza\xee\xdb\xa7\xc7\x05\xb9k\x9aEfG\xf2_j\xd8\xa2A\x0f\x0d>\x14\xab\xeb4\x04v\xc29\xa9\xcb\xa8`\xcd\xf4@\x8dL\"xa\xe5H\xd8\x01QZ6\x06\x01\x864\xef>\x84\x1c\x1e\x0d!y\x08\xf9\xf6\xb6\xa9\x11\x10\xe3\x08\xd1S8f\xa2\x15\xec@\xced+\x83\x7f\x15\xc8\xc5\xe6z=\xe2\x85\xa3\xc18@\xc5]8\xda\x1d\xb3/\x03P\x02\xdas\xd8\x86\xa6\x12\x0e\x1a\xe2\x97\xbc\xe4g\x8d\x87\x96\x04s\x0dV\x99g\x83tZ\xa6\xd9\x9f\xbcL\xda\x152B\x96\xaf\x9c\x0d0\x0c\x1b\xbfzV\x96B^\xd2\xf9\xc3}a%\xf0\xb7\xb7\xe11:W\x9b\x1b\x077u\xa7\xbc\x8cjOy]\xc2>\xc7\xcc\xb9P\x1f\xa9i8s\xfbp\xa4E\xbe\xe2w5\x94r}\x8e\xf4z\xa8\xe9\x93j\xbe,\x03\xb8\x05\xbb\x85?\x8b\xf0{\xf1\x03\x89\xce\xf2C\xdb\xc1\xf6\xcfbh\xff\xd4#\xce?\x85\xcd\xa0e\xab\x99\xa0u\xda\x02-\xaa\xaa \xb8\x8a\xc0\xd1WhIm\xceB\xfa\xa5X\xd6\x96BiC\xbf\x1a\xa7\xd4\x13\xaeV\x01\xf4\x9e\xf2(\xde\x8c\x92\x15\x84\xf0.\x8cO \x9c\xaca\x17\x83\x1eAX'w\x83\xea*\xc9\xba#\xb8V~\xa0$\x01\xe0\x9eo\xa2\x1a#.ax\x92\xa1\xeb!\x81G\x82cco\xef\xc4\xd2\x84s\x8c\xc5\"T\xbd\x1f\x89\xa7\x8aj\xf3\x18\x87\x86\x83U\xb1FE\x0f\xfc{B\xa2\x85\xe7\x11\xd8a\x04\xf8\x16\xc4L\xb4\xf2\x99l\xde\x0dw~+`\xf9\x9b\x1ew~\xfb6\xdc9\xd6\xeb\x129\xbe(*\xa5'\xa2\xfaa\xdd2ah\xf6\x84\xda\xdcL\xcf\xadO/\xc4S\xf5\xa1b\xc6\x1a\xfdc,\n\x01\x11\x8f\xd2\x00n\xb0\x95S\xe3\x1eN\x89SIW\xc9\xb5\xb3U`\xe4\x91\xdb\xb4KM\xfb\xe8\xad4g\xf8c]\x05\xf3J\x9f\x9dL2\x15\x7fY\xa5G\xe1![Q-\x95\x1e\xb2CH\xb9\x8b\xac\x11W\x84\x8a\x88z\xf1\x88Q\xae\x14v\xd0\xa3+\x1a\xa3\xf0\xc7:*wf\xc4P\xd1H\xb5\x1bu\x1d\xb4\x93u\xb3\x0e\xe9&\xaa\x9dBc\xf2\xfa\x89\xea56\xdd\xb45\x05\x10\x1e\xa3\xfa\xc3\xc6\x819i\\\xac\xda\x16\xaei\xa1\\\x02/Wf{\x9b\xad\xcd\xf6\xb6C\x14 CuB\x03x\xc1\xe8\xd6\xd5Q\xbd\xee\xe5\xaaC}\xae\x1f\x1eQ-\xcaW\xfa\x9e\x87\xee\xf1lJ\xd3\xf5(wM}\xa2\xeb\xdcX\xbcS\xbe\xb3JSU \xd8ju\xa7%|\xa7%l\xa7E\x0f!1+q\xcfDY\xbc\x14\x173\x82\x1dH`\x1f\x12\x83\x9e\xaf\xb63\xf31V!\xae\xee\xc6D\xab\xb45\n\xa3\xcd\x14\n\xd7\xb5=\x05\xb8\x8c\xfbS\x01\xa1qw\xa6\xad{8\xb9\x8e=\xdcm\x15$\xe4P\xd3\x1a\xfdu{>g{>w\xdb\xe3\xca\"\x8e\xa6\xe5!\x17\x8bC.\xd6\xee\x8b\xc2[\xc5a\xad\x19*\x96\x121\xaeeEhR\x84\x0c\x03\xf7,\xb1\xe5w\xafj\x96\xb5\xd4\xb02\xe8$\xbex\xb1A\x06-vq\xf4\x10\xb6\xbc\x08O\x05\xb5*#(\xb9\xbc\xbdHT]\x84t{[\xec*]\xfdR1\xe5F\x8e -LK}\xf5\xb5\x025I;C\xd5\xa0\xce\xf9\xa2j\x89\xf9v\xf9hh\xd6\xb0\x02\xdd\xb7\x1aQ\xd6\xa1E\xcb\x81\x8b\xc4\x9d\xd1q\x0f\xe0\xd2\x08\x15\x9e\xd3F\xf0R\x81\xf2\xe9\x7f\x01\xcaW\xea\xc8\x17$\xb0\x08!\xe0\xb6\xaa\xa6\x83\x80z\xa0\x14\xc6\xa8\x87\x0e\xcc[4J\xc6\x01#T\x8dC\xc206\xb6KbEK\xc4w\x89\xb1\xf2\xbc\xa4\x9b\xb1M\x9b\x84&\xb6Q2\xe6\xe1\x90\xc5\xd8\xf2\xea\xc0NR\x12~n.\xa8 \xdb\x1a\xc7\x96vy\xffc\xbb\xaf\xb6\xb0F\x82\xa6[l=\x10\xafc\xef\xe1J\xc0\xe3\xf2XmS\x18\xb6oT\x90p\xe3En\x8b\x8dkQ,\xf2\xa0<\xb1\x87\xb5\xafY\xad\xcb\x92\xfdMG\xee\x0c\xefZ\xd0\x805\xbd\xba\x8b]M\xd0\x86\x03\xe8\xbd#+\x12R\x18\x8d{\xb0_\xfe\xe2^\x10\x8aZh\x1bz\xe5=\xfc\x96\xdd\xa1\xd1\x92d\xd0t:^_\x9d)\xd71\xe1|\x08\x1a\x06\xbc\xd2\x8f\xac\xf4\xe3\xca\x85O\xa9\xaa\xf8jFe\xd5\x9a\xc7\x94\x05.\x13\xa9\xec\x1f\x06*#\xca+1{|\xaa\"U\xd2\xba6\xb2\xd7\xa2\xba\xe4\x0e\x0f\xa6\xab3\n\xf5\x91\xa6\xe4\x8c\xa4Y\x177\xed\x16\xb8N\xc9\xc5\xdb\xd9\xd5\xc1\n\x07\xa81\xdc\x19X\xbbY\x84\x19=\xba\x86\xaeJ\x0cm\xed\xf2\xea\xc2\xd4\xeeC\x88\xe1\x91\xb2\xc4\x10;i\"*\xc3\x8d\xeb'ZlUB\xc4Ns\xe9.\xe5tbU\xbb\x11k\xc9f\xc2#\x88%\xc5)Y\xa0X@\xc27\xd6\xd9\x83\xeb\x12?\x1c(l\x05\x9a\xc2H\xe9\x88\x87\xb4\xaaz\x87\x83&f*S=k\xda\xfb\x19}_\n\xfa\xbe\xbcf\xfa\x8e*cI\xde\xf9\x0f\x85\xbas\xed\xee6\xf4\xfa\xfd~y\x97\xc4S\xd8\x06O\x08\x15\xf3B\xcd{\x00=8YW>'+\xcc{\x84I\xe74'\xc1\xf2zO\x029\xdcR\x17 \xdfU\x87\xd28#\x96W:#$\xe7\xe0Q\xd8Q\xfb\xf6\xe1\x96\xd2\x9fq\x7f`\x80\xf4.7\xc8+d\x82\xdf`k\x84:\xf1\xd9\"\xd1\xd8\x1ejCv>wj\x87J\xd1\xa9r\xb8\xa0K\x01\x9e!\xe5\xd3\x80\xdb\n\xf0\x8c)\xef\xfa\xf0hX\xf8\x96.\xa9\xb7\x1b\xc0\xae/\x8e\xa7\xa5@\xeeSB=\xd5* M\x06\xec>\xd1\xdcG\x905\xcf\xae\xe5U\x0e\x9b\xb3\"\xaa\xb2\xb2B\x0d\x85/\x18\x031.\xc3\x1c\xd4r\x07V\x87\x03\xe1Z\x89N\x96\xece\xeeSa\x19((x\xba\x0b\x1b\x93s\x14\x1e\xa1qY\x8d\xd3\x8b\xe1_C5G\xd1w@\xfd\x87\x0c1\x94\x9b\x0f}\xc0\xd7(\xdcR\xdf\xb5\x12\xdcC\xea9\xa5J\x8f\xea%]\x145b\x99\x9a\xffg\xaax\x99\xeb1\x0d\x94UxEG\xd4\x9e(\xb7\xea\xb1\xf2\x96ao\x00o8\xac\xdf\x89\x9c\x19\x14\xd3\xe1\xc0+\x9e\xe8\x1c\x9f3*\x8e\x8d\xb3\x83\xef*Y\x16`\x9fw\xd6 \xc7\xe7a6\x7f\x9aLU\xc8\xc8[:\xe5bT\xaf\nV~\xe8\x08B3\xe3\xf9\x9a\xd6\\M\x11~G\xdccM\xadPji\xa3\xfe5\x1d=\xa5c\xa7/\xb7>\x1b\xc7\x0d\xa6\xc6\xfb\xa2\xea\xc1\xfa(;\x8c\xf3\xa5\x08\xc0Bw8\xdd\x13\xa7\xb1\x98:k\x07\xaf\xfa\xb5p\x98\x8c\x93)\xf9\xb0^\x11@\xd2\x9e\x9dG\xbc\xfeYq\xbf\xad)vM\xc2\x8c\xc0`\xbf\xf5=Ph\x7f?\x8f\xa3/99zf\x9e\xa3\xbc\xb0\xf9\x07\x1d\x9b\x9f&\x13\x0c\x18>\\\x10\xf6\x0f\x9fl\xedf1\x06k\xd3z\xa56\x88-\xa5\xac\x96\xf6=\xfd\xd7l\xb9\xb6\xb7?\xd0@=\xfan\xc2\x07\xbe\xf7?\xe0\xde\xb7\x84\x88\xbc\xa6>\xc3\xfa\x8c\x18=\x1c\xc1\xc1\xd1\xb5\x8aB\x7f\xc8\xfa\xc8C\xfc\x81.\xcfu\x8f\xc1\xde\x9b$\xde!<\x95q\x19H\x98A\x98\x12,\xfa\x86\xd9\xb5\xc9\x14\xc2\x0c>\x93u\xd67\xd5=\x90\xdd\xb3\x0d%\xa2\x8dy9\x89\xd2#$\x80\xa7\xd4\x14W\"/R\xec\x9b}\xd8\xb2\x04x\xb1k\x92\xc4\xb3\xe84w|\xfb<\x8d\xa8\xdb\x9b\x82O\xd7/>\x80\xb9\xa4\x1e\xa8\xe5\x0d+N\xf5\xddH\x86`\x93\x95H\x12\x85\x83\xd7}\xe0\x1b\x1b\xb2\xab\xdb\xd4K\x95\xb5\xdd{\xee\x87\xab\xd5b-\xd8xCD\xbfz]\x06\x162\xc9\xce\xc0\x16\xc8\xb6\x13\xc1\x8aSzI\xf2\x1ax\xff1F\x08\xd1\x042B!\x84\x98\xed\x83\x12rr\x8c\x90\xc4bOXQ\x9f]T\xce\xc1<\xfb\x0e\xf4\xc4z\xeaw:\xed\xa5\xf2\xb5 k\x8caP2\xdah\xf3\x01\xd4\xa0\xc5\xcb)\xb3&y\xfddT\x93\x96\xa5y\x18\xf7@\xa6}G/\xd2\xb7\x06\xde\xbeP\xc7\x10\xce(\xa9\x16\niiG\x03\x05\xbep{\x00\xdf\xf1T\x85\xfd\xc9\x829\xf3Ld\x15\x16\xd6\x97)\xdc\xbdu\x9d\x11\xfcW6_r\x85\xa7\x92\x01\xeau\xb82\xa6<\xfb\xfa\x8d\x96\xc5\xe34IJ\xcd,\xfb\x81\xa2s\x11K\xc3\xf36\xf9:\x93b\xa5\xeb\xacS\xd7\xffP\x93B\xd9\xe7\x94\x11z\x14wh\x1a'\x92\xaf\xa6!%G\xf8\xf22h?c\xcd\xdc\x92}p)Y&g\xed\x92\xb6f\xd6K{\xc3S\xb2 l\x02\xaeM7f\xed:\xe5e\xd7)\xf3N\xea\x0bbO\x1c\xcdE\xc8F\x89\xcb\x03\xe1\n\xe2K\xe3L1\x81\x11\x1d\x8bF\x1d\xc6\xd2D\x0f\xc3h0\xd8\x15\x9d\"E,&Gq\x8b\x8flA\xa2]\x12I\x9c\x898P.\x80-\xcd:\xd1\xbc\xd5\x17\x8f\x91\xbb\\\xf8\xe1\x99\x89\xe2\x99H\x19\x93`\xf0Hk\xc5\xd8\x0c\x86\x10y\xb6\xb2\xdcb\xb92\xbe\\\xc2Y\xb7\x19C\x06F\xa9\xe3\x94z \x03\xb2\xc8\x1b\x9c\x11\x1a@/\x8ay\xb5\xfb\xcfd\xfd3V\x883Cf\x82%\x80-\x1e\xa8\xec\xa5\x99\x98\xf2\x92M\x19\xa9\xd5\x84\xed'\xf3\x07X\xa0\xd4\x9b\x95\x0bhU\x94r\xd6e&f\xcf\x7f-\xd9/\xb1\xdb\xbd \xc3W/)y\x19\xe2\xe3\xd91 `\xa1\xe1\x01\xc4\x9e\x8fc\xd4\xe9\x1a\"\x1eE\xdfi\xd1\x9b\xe0\x9a\xea\x96\xd9\xfa\x0e\x98,Hh-J\xa44\xdet\x8b\xa1\xdc\x1fB\x1c8\xc9yL\xd2\xa3gp BaE\x0c\xe3n\xa0\x9e\x14CQ\xb4S|\x83\xc1\xfb\xc3\xf2\xac\xe0w\xc3\x05\x15\xf5N\xb6\xc4M_pw\xd6\xc9,Iz\xda\xaat\x90\x90\"\x02\xae\xb2ks>\xc0f\x1f\xbfF\xd5\x92c\xb6\xf3\xa4\xe8\x08\xfd\x97\xea|\xd2\xa0\xe9\xc8\xd1\xec\xaeJ\xa0\xec\x86pM\x0fFl\xa9\xd2L\x12 \x84\x03\x07\xad\xaf\xf8\xde \xf0\xf3e8\x90\x7fI\x1d\x0d\x12\xd5}\x88Gj4^\xb3\xa8m\xcb\xf1\x81M>#\x18,\xdbi\x9d#\xd2m\x8dY\x1fN\xeb|%\xd0\x17\xc3J\x88\x87b\x85\xe3\x88\xfe7\xa2\x02\xae\xd6\x81\xfa\xebzQ\"KR\xea\xca\xe7\x1c\x11\xef\x17R\x98\xfd\xdb\xdb\xfda\xdd\x81uT\x1b'\xed\xedWd\xa0\xd6 \x14\xb2\x16[\xa90{\xcdu\x11:\x06@.)\"\x16\xe9\x9f\x87\xd9\x13NO=\x1f\x8f\xa1\xe3c\x12gyJ\xde2z\xedU\x89\xb7d\xa5\xac\x03/zw\xdc\x83\x8d\xf3\xa1zn\xa8\xa3a\xa2\xd8{;\xd8\xc2\xecHjb\xba\xf5\xaf\xf6\xd3\xb22\x05\xc8\xba\xf5 \xce-k\xdb\xdd\x1c\x9c\xa4F\x84\x9c\xc3\x0dw\x99\xa7\x93\x17\xda\xb7:1+\x87{\xe1m\x83r`3\xb3H\x0b\x11\xe1\xc1v\x1e\xc1\x043\x043\xca\xe8l\xee\x01/\xfb\xd4\x02\x01e\xb5[\xf7\x96\x9cI\xc9\xe0\xe8\xb0\x15\x0e\xe0\x9f\xb4dmT\xb6&(\xf3: K\x83\x1c^\xad!%\xf7\x83\xca\xe0\x0c\x04\x83\xa3\x99N\x941\xc9}\x08\xcf5\x9eC\x1fi\x00?\xd0f2\xe0\xd7O~6TO\xfb\xc2\xdeV\x81dR\x0f\xfenN\xfc\x81\xc3oNH$*j\x18\x1f\x8c5>\xac @\x0c\x9d\x9cDt\x89\xe0\x90\x90\x8f\x13\xee\x82\x1c;\xf5\xf9\xcbU\xfa\x9c$yL\xaf\xdc\xe5\xcb\xabt\xf9\x99\xac\x7f\xe4L1i@\xd7\xad\xdb\x17\xd7\xd7\xed\xda\xb9\xd3\x1b\xed\x9d\x1eS^j\xb4\xdc9E\x84M\\\xfa6\x87\x93\xcf\xc8\xbc\x14\x14\xe5'\xea\x89_n\xda\xd0\x1f[S<\xf2\nH\xa6}\xac\x0b\x025!\x0f\xad\xa9,$fGAA}\x10u\xa9FM\xd1\xd4Q\xf8X\xe4\x0c9\x84\x08w\x9bN_a\xc0G\x11%^\xe8\x97\xf8\x82\x06\x10Zy\x15&Qq\x89\xcd\xd3~\xba\xcf\x10Q\xac'e\xfc\xc8\x85\x17\xfa\x01\\x\x0cU\x18\xc4_\xc8\x1c\xae#\xf6\x99k:wB\xec;\xbeVy6\xf74\x9eEF\xf2\x92K\xa0En@\x8e\xac@.v=zm\x95j\x95\x9b7\x01\xb3\xb0V\xd4+<'c\x91\xd8\x97o\x7f7\xce<\xb1\xef\xeeR\x9433\x15\x002\\\x0cu\xf8Ue\x1a\x8e\xb7\x92\x8c\xba\xf2\x9c\xab\x84\xcc\x9ax<\xb9\x8a\xce\xadjx\x9e\x8d2\xf2\x85\x1e>jY9\x13@r\x97e\xe1\xdb\x1c-Cq\x7f\x16\xb1\x93\xc1\x01\xfd\x8a\x8f\xcb\xc4\xb9\xcdA\xfa\xbeb\xedb\x07\xb2\x9af\x17\xe9jy\x8am\x18\xa9\xc0\x94\x87\xca7W7\xb5\xa7\"\x1a\xaa\xf8\xc4\xb6\xe2\x80&pq\x1e\xa5U\xabi\xab\xf7pE\xfe^\x8a\x1a\xa3\x08x\xec\xd2\xf8\xad\xc6e\x02o\xabA0\xa6\xa5\x93\x17\x95n\x19\x86\xf4\xb1\x97\xd5z\xd2\x05A\xc3\xb2\xd2\xf1(\x1a\x17\x0e!\x9a\x81bf\xf2\xca\xd1\xe7\xc5\xa3]G\x89#l9iA\x84\x86x\xf7\xef\xde\x7f\xf0\xe0\xf6\x9d\xbb\x0fx,\xcf\xce\x10\x03ax\x1c\xcc\x9d\xdb\x83{w\xef~\x7f\xef\xae\xef3f\x0f\x1f\xec\xc1M(\xbeQ\xee\xdfa'\xd3\xde\xdd\xbd{w\xee\x0en\xdf\x0d\x80\xc2\xb6h\xea~\x00\x83\xbd\xefy\xf3\xf2\xde\xe0\x9e\xdb42\xe2(\x85\xa4\x02\xc5\x0fm\x15E\xa3\x11\x19\x0b\x01\xa3\xd6\xbb\xfa\xeb\x0b\xba\xba\x08\xde\xec\x0b\x15\xe6p\x18\xb2\xbf\xb9\x15.(\xffD\x9dz\xf1\xd2Q\x1c\xc0\xef-N\x11\xe6\xb9T\x0eCUz\x17\xc7\"g.\xa2\xf2X\x84G\x90\xf3\xd3\xd1HH\xa7\x88\x9e\xd1(\x193\xd4)s-\xb2\x1b\x03\xe7R\xe6\xb5Y\x19\xcd\xf0*\x1fi\x9d!\x16\x1b\xe1;6\xc0\xd3\xb9:\xdd \x9f\xee\x0c\xcfc9\xdd <\x02\x8cm\xda\x9abB\xe0l4\xc1I=\x84\xc9\xf6\xb6\x81![\xc0\x90\x7f\xa7\x17\xc8\x16p\xc0\x9b\x19\x8cq0\x11\xec3\xeeWQN\xea\xbf\xe3|\xb0\x17\xa2g\xd4\x02]\xc9.\xbc\x84IQaIH\xb3\x96\xec8\x18\xc4\x81\x0e~[!\xfb\x7f\xe1\x9a\xf0x\x08\x13]\x98\x8a\x15y\xe4\xc5\xa5Z\xe9\xb1\xf8\xdebp\xaf\xa0\x9b\xe0\xfah\x00\xe8\x88\x1a\xc0\x88u4\xf6+\x1c\x19q\xe1\xc8\xe4%\x9d\x0d\xc8\xc8\x94\x00O^\x11b\xb5 \xff\xb4\"\xa2\xe6\xa8h\xc9\x8d\xd5?@\xcbE\xc9K\"\xbb\x9e6\xb3\xae2\xabQ\x9eMa\x05\":LQ\xf0J9\xd3\xd81\x93\xf7V\x0c\xb7\x90\"em6\xff\x03\xe4\xaf'\xc2\xf6\xbf\x03\x038\x80y\x7f\x95\xf0J\x10\xf3\xd1\x84Q\xa3\xc6\x8d\x11\x1b9\xe3\xc7\xe7\x9c\xc1\xe4\xbf\xfd\x00{\xf6j\xda\xbfyi\n\x97\x02s\x00\xf36\x96\xf42\x80_\xafL\xce\xb4\xd1e\x88]\x86\xcd\x8aB=\x13W<\xafZ?\x9cG~R\x94}\x0c\x9a\x91D\xd2\x10\xae\xe95\x126\xd60\x93snr\xee\xae\x08\xcdF\xe5\xec($\xfc\x11fF\x1e\xf38..#\x11\x1d;Q\x07\xcf\x95\xe9b%3\xb4L\x00\xfd\x84z\xa9 T\x8a\x80H\x04\xcb\x13#\x90\x88E\xaa\xcc$|C\xfd\xf3I\x15\x86\xfa\x97f\x18S\xb95\x04o\x027A\x87\xdaH\xd7\x90PGue\x8e\x96\xa0J:\x1d\x12\xde$\x02_\xdf\xf9J\x8e\x10\x97K\xff\x0e\x1a\xdd\xe1\x00V\xa3\xc5\x18Z\n\xb1sE\xd9\x9c\x9b\xc5\xf8BW\xd7J?;\x1e%>w8(8\x1c0\x94|\xa5\x90\xf7\x99\x95\xbc[\xdc\xbc*\x15\xbf\x04C\xc0\xf63\xaf7\xb3\xf6\x03\xc4\x8c\xdd\x87\x82\xd5\x8f\x1fB\x88i~\x18n\x0ca\xe0C>\n\xc7\x88\x067Q\xb3@F\xc9\xf6\xf6\xd8R\xb3\x0e\x14\xa1t\x94\x8e\xb9\x8a\x8b\xf5\xc8M\"\x98\xe3A\x1f\xcc\xcf\x1e\xaf\x02\x98\x04\x10\x0605@R\x9c\xe7\xec\xffj\xb9z\xb5H\x7f\x93*\x11\xb4x\xb2\x04\xb6\"\x12\x0df\x81c\\\xeaWxS^q\x0eRQp.W\x88?{k\xe03V4\x1fc\x9ck\x0e\xdb\xc6\xd4\xb8\xd0~xs\xa8iA\xd6\xc2!\x15\x1c\xb6\x84\x9a1M \x14\nu\x84\xda\xb6@\xaa\xa8\x84\\!P\xb8\x80.\xa9\x80\x8e\xab\xd6\x10tb\xcf\x86\xf0\x08\"\xdc\xb1>\xbb%h\xbb\x97\xf0-\x1b\xf3\xd7w\x06\xa8\x9d\xe5\xf7\xe8(\x84m\x97rn\x86\xc2\x1f*\xee\x19\x8f\xcc\xe3\x82\x9d(\xac\xa8'5\x93\xe6y\x95\xbb\xe0&\xda\x93\x00\xce\x1b\xe7\xe5/\x7f-;aa$Z\xf8\x08\xce\x10Df\x11)\x81\x03Ht,\x82\xceo\xf2\x97\xffel\x82\x94\xcd\xb4/L\x1cNa\xc6&LF\xa1\x81Lg<\xf8\xc6\x911\xa0\xc4\x9bu=\xa2\x85#\xadC\x0f\x05O\x81\xf6z\xc3\xb1\xd2.\xc3\xed\xec\xac\xe0\x11,\xae,\xb7U\x08\xecn\xa0?\xe0cy\xc0s\xa1y\xc0%\xe5R,c\x14d\"\xce\xfc\x0c\x1e=\xc2#\xbf]L\x9b\xa1\x98\xa6[\xac\xca\x9beT0\x1e\xb3!\xfe\x89\xb4\xd1\x8b`3d\xc2T\xce\xf9 \x06yc[\xad\xf2ZIB\"-k\x01\x92\xbd\x98 \x87\x11\x1a\xcd\x8c\xab\xedm\xfd\x9a\xcf\xbb\x9e\xf2\x8cS\xcc\x88\xc7\x99\x99\x05\x93\x9c\x8cta^\x90K\xe9\x00\xb2\xaaQ\xcbi\x95ZrNj\xc5\x98\xa4:\xd9xyej\xf9\xdf\xacKz\xf9\x9f#\x86\x82\xae\xe9wy\\\xe6Z\x14\x86\xbab\x8e\xa1\x92\xc0\x8f+\x7f\xb8\xbe'&\x8a_\x1d\x0eZH\xe1\x9a1\x14K\xf2\xff }WXr\xee\xb3\x8a\xd5\xf4E\x99\x97P\xc0\x92M\x80\xb1\xee\x13\x93\xf1\xb4\xb3\xa6\xa5]\xcb\xf2\x1f\xd4\xb0\xbc\xd4\x00`\xde\xd8\xe0/\xae\xbc\xc1\xa5\x18\xc3\xa3B\x0b\x9f+\x86 2\xa2\x8e\xdf\x18\x8cu\x0c\xc9\x8b\xeb\xd9\x835U\xaev\x99\x90\xe4!\x06W\x87i\\./\xc3\xea\x19\x05\x12(\xf3\x08\xfd\xc6F\x0ce\xc0\n\xc3H\xd8\x87\x0c-\x01Z4\xaa\xac\x1a\xb68,\xca\x10\x89e\xd3\xe1\xadXv\xde\xa5f\xd7#\xd1)w~c\x91+\xba\xf3\xd2\xb9\xf6\xa5\xfeve\x0d\xac\xa4=n\xd0\x91\x94\xd3\x91\xa8V\xb6\xe8!\xa4\xa2\x84L\xea\x94\"9.\xea\x97\xa0\xe7\xc1X\xadwY\x9f\xdc\xaf\xfaY\xfcrm\x93\xe3L\xa6\xdb\xd4\x0c\xbcN!|\xd5\xe6\xa5\xe7w\x18(\x12(\xb3\xcf$\xfdJ9\x06\x13,@\xa7=}qE0H\x8a\xac\xa0k\x03\xad\x88w\x83\x06\xf0\xd5\x0f\xe0\x86\xdaKL.ZS;\x14P\xa6\x12\xca\xe8_\x19\x94A\x02\xdc\x99\xf2!\xd8\x8b6\x88\xfa\x13\x04\x17\xc9\xac\x0e\xc7\xd4\x98<\x0b\xaa\x8e#\x03)f\x8b\x89Z8\xd6\xa8\xa8\xadZ\n\xe1\xdcg3\xd5AI^\x97en\x9bT\xee\x96\xb6n\xb0\xbe\x99\xa8b!>Q\xf0\xce\xd7v\x1f\x91l\xc4\xc1'\xddS\x0f\xb0\xcc\x1e\xafy\xd6:6\xb5KD\xfbj\x87v\x95FR~f\x19\x83]\xd1\x91\xb4I\x0b\xf8\x92\\\xa6\n\x00\xe4]\xbb\x0cQ\xc3/\x18\xc2O\xd4K\x8c\xf6s\xb0\x8a\x0b\x93$\xa6Q\xdc\xa9\xf8C\xb3\x7f\xe5W\x9f\xfb\xcc\xb6\xecj(\xb7\xa7ic\xb4\xe6J5\xe6I\xad\x11\x90*0\xd9*c\x1e\xea5\xdc\x82;\xcd\x96g\xf2\xd9^\xf3\xd9\xa2\xf8\xce\xe4\xb9\xbf2x\x0c\x9c\x89\xd8\xa1\x0bc~=\x87<\x96\x9a\x88Z\xf6\xe5\x9cxJ\xcaI\x8d\xf0-O\x82\xc8\xa3\x96\x0c\xa3\xb1\xbd\xc6\x03\x1fL*t@\xde3~\\\xa7\xf0\x98g\x8dN\xe1\x11\xac\xe1\x00\xce\x89\xb7\x8b\x0c\xcfY \xe2L\xb1\x10\x04\xf1\xe2>M\xb8\xfc\xedcYZ\xd2\xd9-\x06\xfdD\xdeG_ \xf6\xacI\x03\xd2\xa6\xe9-4\xb5-\xfe&:/\x127O\x8b\xb9\xddaD\xc9\x032%-y@\xd8ArN\x19\x9bL\x1c\xf2\x80(\xc2\x87g\x8e\xb1\xe49\xbc\xc4\x11\xf7\xad9-^E\x19\x85Q/\x80\xde\xb8\x99\xd4\xa2\xd2\x93cR\x8bH\xd6\x8a/\x93\xe2\xfbEVrZ\xcdJn9M\x99\x00[\xb0\x96\xe8+\x83#O\xd2\xe842y\xb6I\x99\x8b\xf5\x14\xf7y\x99P\n7\xe1T\x13\ni\x02P#\xbbF\x05\x06\xdd\xb2k\xb8\xda/\x10d\x84\x83\x8c\xb3U\x95\xaa\xf9&\xbfo\xf4\x0d|\xac:\xb1\x11x\xa4d\x83\xed\xee\xb2\x06x,<\x82]8\x80\xb7\x82\xc7\xc3m\xb6+\"L\xdfJ\xa7\x04\xb4\x00\xf0gD\x1b]\x06`N\xb0Gp=\xe5b\xea\xdf)\xed9\xc74\x8c\x16v\x86J\xba\xf7\x1b_J\xac\x81\x02\x08\xc5\xcf\x18%0 W\xe1$\xa2kn\x10\x1f\xc2{t\xc2\xabG\x0dpy\x10E\xac\x88\xbf\x14\xd5^\xa2\xfd\xe3\x059#\x8b\xf2]\xf3\"n%\x8e\xe1\x06Q\xfa\xd0Z\xee\x00\xf8\xd8\xd6\xba\xd0\x13\x8e\xc6\xec$\xd3w\x13 \xbf\x0b\xae\x8a\xd4\xf7\"\xaa^\x98)y\x0e\xea(F6\x03\x16\x16\xa9\xcf\x19\xdd\xca+`F\xd8\xc2\x0e\xea8}\x1fG\x83o%\x15P5\xa9\xb2v\xc0\xdcJ\x169@9\x84!\x1c\x96\xb9\xb3\xf4\xf3\xdfJ\xf4*\x95\x8a\xe3\xc4\xeeC\xc8\xb8\x8bi\x86~\x92\x02\x16\xd9\xb8\x10\xbf\x8c\x049B7\x91\xb0\x80\x1e\xa3\xf1~\x00a\x9d\x82ip\xf4\xc9\x8c\x92\xc6\xf1\xde\x8a\xa2^\x15G1\xc8\xf8\x1b0UX?Q\xa8oA\xd8\xc8\x8e\xb0\xfaN\x9cp0\xa9\xe2\xa0\xc9\xa2\x848\x98b\xb2L\x86]*\x185(\x88/Ez\xc8\xa0\xf1\xab#r\xca\xcdbE9\xd1d.z\x13\xca\x8a\x08\x95|\x81\xf0k\xcb\x8bi2&\xca\x0f \xaf\"K\xf3x;%\x01,I\xc0\x98\x06[\x1a\xf5\x13\xf3iU\xf2\xea\xf2\x10\xd7BX(\n\x8b\x93]\xbf\x0c\x80J\xbe\xd4\x165\xc3\x0f}3|*\x89D\x04\xe3\xb0\xeb\xd7&\x06\x95\xb8g6\xb70\x00\xa3\x8d\xb5\xa2\xc7 +\xe5\xac\x0c\x9e&\xf2\x92\xc4$\x17\xfeK\x07\x12\xc1\xf8\xf1\xbe/\xa3\xdc\xf1\xa7\x99G\x05\xe1\x97\x92\x8b\xca\x87\xbb\xe8\x19\xbb\x03\xb9\xfd\x93 F\x9a\xee@n\xe0\x1b\xf1\x95\xc7\xb0F\xdca/\xdb\xec\xa1\x02\x08\xad<\xbc\xbc\"t\x9ce\xd3\x9e\x14\xfb\xe1\xd8Rt\x04\x14\xb5\x04V{\xdc\x99\xc0>\xa3\x9a\xf6OD\xcb\xe8\xd9\x15\x8e\xa8>W\nh\xb7\x1d\x80\x0c\xab\xab\xbb\xe5G\xa89nYV\x11 \xea\xbc\x80\x13$/\xd5\x05L\xe0\xf1c\x88\xec\xdf\xcd0\x00f\x9b\x1d\xeb\xf2\x03\xcb2\xcd\x8a\x05\x9d]\xf3\x82\xe2\xb9\xf6\xd0\xe8`\xa1^l\xed\xb5\x19]tW\xa1\x8b2 }\xf5+\x12E\xf6\x98\xa8\xd3\xa6\x90\xaf_\xa1P\x85\xb6\xbel\xb6\xe3\xcb\x8b\x0dcR\xf3%lCpP\x08&G\xf2\x19\xec\xc3\xa4\x0d\xc9A\x8c<\xe7\xae\xe8\x19f\xde\x8f\xf8\xa1\x940\xd4\x88\xd9\xa9\x1d\xf9f\xb7\x04\xb0N\xc9\xb27\x90.6\x1e\xbb%\x948\xd7&\xfb1\x1d\"a#;\xd7\x99E\xa3\x10J59;\x9b\xd98UU9\xfeTT\xe5\x04oH=y\x8c\xbf\xca\xacGa\xa1$\x8f\xf0\x87\"5&\xfc\x86\xd0\x97\xe7\xfcW5\xb9W\xe8\x04\x8a\x0bb\xd3\xa8\x9d\xa2i\xd0C\xc5\"\xb7\xeb3\xf1\xcd\xd1\x14\xfe\xbe e\x13\x88s\xee\x8f/\x92\xf3\xd8c*(w\x9a\x7f$\x89\x9bT\xcc6>@^\x18\xf1R\xf1\xa5\x88l\x1b\x93\xb3\x9c-\x9c\xdb\xa4F\\G\xa1%c\xce\x8c\x9b\xf8&\x1c\x0e|cHXX5I3~B\xc9\xbcQ\x9ed\xc3\xd0\xc6[t\xccXi}\xd8\xa0iE\xb3\xea\xc8\x8b\xe3\x9f\x96n\x99jWA\x05v\x1c\xf2(\xec4xK8(nJ\x13Y\xae\x8e\xb3\x19\x83`\xc2\x9bC3OW\xa8\xd9\xd0\x1f\xa0\x88\xc1\xa3\x8ag*\x15\x1e\xa8k\xe2\xf1\xfc\\\x82-E\xae\x94\x8d\x8a\x89\x97\x8d\x02P\xfa\x91<1\x8f\xa4\xb0\xa0\xd7l\xbf\xaaeU\xcf\x0f\xf2/\x1fq\x81F\xb2\x82\xb0\x0dg&\xa4\xab\xfarJ&R\xf0\xad\xf8\xf5C\xee\xb7\x80\xae8XXuX\xf80\xf0P\xad\x14=\x19\xd8G;C8\xb3\"^[\x99wcE/k\x92\x1e%\xe8EF\x9d\xf1r\xc7\xea\x13\x19\x7f`(o\xac\x98\xf5\xd5t;\x98\x9f\xc1\xcc\xb6\xb7\xb0\xff\x89\x0b\xfb\x8f1\x1e\xb0m*\xce\x10\x1623bc\x8c\xdc\xf4>\x9a\x8dv\xf1\xefm\x0c\x19c-h<\x16\x18>\xe4\xf5\xfd\x95\xb4\x91\xa9\x9c\xe1\x9e\x12s\xc0\x0d\xbf:N\xa5\x1a/Q\x88\x1e\x13\x15\x99f2\xe8t\x1bfl\xd4\x0f}|.\xf6\xd1\x84\x8dkR\xdd\xf1\x070\x92\xc6\xa3\xc9X\xec*&\xd8\xcd`[f\x1f\xc8\xd8\x9fg\xba\x11q\x99\x90=\x9e\x05\xbc\x8c\xfa\x8c\x1d\x00\xfc\xdf\x04\xff\xb5Md\xc1\xa5\xb1\x04#\x08\xf0\xcf\xd0\x7f\x08+\x06\x11\xec9c\xbb\xc9i\n\x95\xa1\xf3\xf1\xea\xf1n\xde\xe6N2\xc5 \x8aG\x18#\xc1\xc9F\xc8%\xee}60\xbc\xad\xa8\xb70\xba\xd1pda\x905\xff\xe6\xe6M\x8c\x03F\xd1l^SA\xb4\xd0\x8a5F\xb0 !\x9f\xf0\xe9-a\x08\xd9CX\xc2c8c\xff0J\xd0&K\x1c\xc3\x10\x16HA\x96z%\x89\xbcXwkAr\x8e\xc7\xbc\xdf\xf2\xb71\x81\x94\x9e\xbf\x93\x1f\xf2\x9e\xcf\x90v\xc1\x10\xe6-\x94 $\x83/A\xe6\xb1E\xc1(\xf6iEq\x92\"\x1b\x13\xfax\xd6=\x1e\xc2\xca\x87\x9c\x81c\x85\x8b\x86\xfff\xdcmaR8(4\x9a\x12z@\xde\x96.|\xb2pGf\xc2q\xc4(\x15\xe2\x87u\xe5\xc4>\x9cX\x85\x19\xb60'\\\xe8~\xfc\x98\x1d\xe8\xb6\x85a\x038A\xea\xba*_\xf7\xe1$%\xe1g\xf3W'BP\xdb\x1e\x82\xc7\xb7\x94\x0f\xdf\xc1 n\x92\x9d\x022b?\x8dN\xf4\xc2\xad~q'\x1c\xab\x1f\x0b5\"o\xa7\x0e\xd2\x8c\xad\xcc\x0e\xcc\xd8\x12M\xf8~x\xc4\xf7C\xe5\x83b93F \xc4\xfb\x92\xba\xec\x08\xaa\xb2\xa3\x8d\xa2\xec\x9c\x924D\xb5Fy\x9cp\xb6\x9bV\xd8\xf9\xb0\xd4\xed\x00\xc6q\x96\xeeU\x13\xd5\xbdj\xea\xea^\xc5\xc8\xc49\xf1r.\xee`\xa4f=\xba\xd1p\x1c\xff\xe1\x96/2U\xf3EV\"\xe8\xcb,k\xa1=\"\x04\x93b[\x99\xe0 Z\x01M\xe9{&\x1c\xc2\x8f\xc5\x9eMp}E\xa5\xbf\xdc\xcbxJI\xbe\xea\xd7\x9dR2\xe5\xf1h\x93\x0e\xe8\x91\xc0c\xe94y\xf3&O\x10Uz%'HR$\xe4\xebYn\x0c+\xf5\xb9-\xc5\x1cw\xab\xdeE\xa5\x9c\xd4Y\x9f\xb1My\xe6\xd4\xfe\x91\xbd}k\xa1\xc7\xa7\x9ce~M\xca\xfa\x8e\xecVg\xbf\x9b\xb3\xff\xf5\xf5\x1d_\xdb\xa1X\x94\xc2\x9c\xd5\x11\xce\xd4\xe0\x07\xd7\x94|U\xd5\xc3\x91bT1+!\xca\x14\xe1(\x02\xe1\x8f}\xb4\xdb\xf7\x8fy\xea \x9e;|\xc1\xed\xcb\x0e\xb9\xc3\x9d\xe6\xf4\xd4\xaaLXre\xc2\x92\x8d\xeb\x03\xf1xu\x9b\x0b\xe25B\xfd\x0c\xad\xffl\x970\x84i'\x90,\xbd1\xf5R.\xf8\xe0(3x\xfdb=6LIA\x0c\n\xff\xac\xe4\xf8\xd9\xd1\x1a\x9aT C\x9e\xb7I\x8f\xb7\\?\xd1\xa6(\xcc\x05y\x1cr\xedi\xf9s\x0f\xbe\x83D:n\xa2\x8d\x88\x1b+\x9b\xc9O\x0d\"\xac\xbcD\xff\xca|\x84\x8a\x05\xa55\xc3>\xf2\xfb4yI\xd6d\xfa\x9e|\xf1\xfc\xee\x94\x99\x8ev\x0d\\\x83\xdf\x9f-\xa2\x95\xc7:x\x1d\xf2|:\nn2\xa2\x9bVp\xb5\x8a\xb9\xaa\x933:\\\xa0\xf1L\x96}c\xd4%\xc2\xc3\x9c+1\x14\xe7\xde\\Q[0\"\x12J\xd1T\xa3\xbcTb\xcd\x8c\xb6\x99\x12\x01rD\xa5\xd0\x1f\x0d\xc6m\x8b\x9dr\xd5\x1e_G1\n\x9ej\xdd8\x08>?\xe1L\x9fK\x12Z\xb6\x90\x8bB)\xa2\x19#\xc90\xf1=\xa9,\xb4\")\x07\xf7\x0d\x17\x94#\xd2s2\x0c\x8c\x1f\x90\x93s\xcc\xbc\xfc\xae\xc5\xeb\x04\xdd\x95\x14\xaf\x93\xe3<#/\xc9:SJYH\x8a\xd7L\xe2k\xea\xf4\x8d\x81\xa6k{\xec\xde\xfc\xab?\xb7\xf9g\x7fn\xf3_[\xe2\xd8\xfeAl)b\x89:\x02R\xed\x9e\xdd`[\xbc\xcd\xabSi\x8e6\xb1?\xc0b\x8e\xb2xIkCgE\x99d\xf1\x91\xac\x7f\x86\xdeg\xb6\xbe\xdd\x07\x0b\xean\x12\xddx\x06F$\xd0U\x14as\x9a\x87Y\xab\x1b*\xa8\x1dE\xf1d\x91OIV\xafj_\xb4(_\xe8\xd6\xec<4\xb78 's\xf2\x8ed\xf9\x02\xf9\xdf8\x00\xc5\xa3\xf0c\x8c\x8f+e\xbbl\x11L\x85ZO\xebL\x01U\n\xd5\xa8g\xe5\xc8\x18\n\xafC\xf4\xb5\xa7fu\x84\xb1\xd8\x95\xe2\x9d\xdau~\\\xdf\xcb\x0e\x82wmR\xbd\xd4n\xca\xaex\xbbf1]\xb2\xf0nN\xac\xf2\x92v\xcd\xd4Z\xbeV^\xc8\xa5\xd0\xd6:\xb6\xf2*\xf7\x19\xba\xb9\x8ev[\xb2!\x01\x86u\xcaw\x95\x0f\x07\xe3@\xf9\xbb\xe1^X\xbf\xecfQ#\x19\x91\x97)\x8b\xb9\x1b>\xb2\x95\xc2\x15\xfe\x99\xc9L\xb0\x0f?\x1b\x11\xa9r\xd3D{\x9f\xb7s\xba\xad\x148\xad\x13\xdd\xb4;i1\xd3\x80\xb4\x1e\xd2\xe9RT\x99\x97%O\xcd\x85~\x0b\x19{(r\xd0G\x18&\x8c\xbe\xf6\xbc\xc4N\xaa\x15\xedp@V\x02\xe44\xbc\xab\x12\xa0\xa8\xc5\xd9\xa6J\x83R\xaf\x9c\x91\xfcXX\x04MD)j\x99\xb2\x9e(9\xcdY\xc5\xe1w\xe6\x14\xce\xdd)\x8d\x14_\x93V*\x83\x8ev\x82\xc0H\xf9\xd5\xfc\xf6\x99\xf0I\x8b8m\xb0\xbb\xa8\xa0o\x82\x95\x06I\xf9\x9dA+\x0c\x14d\xcb\x91\x02\x85\x0c\xdf\xb4\x0b\x00\x06uB\xa3*\xa2a\x8f\x7fl\xf7\\\xb3o\xf0Xe\xb1\xe2\xfan\x8f\xbb0G6.\x8br\xf6\x07-s\xce\x9c\x90<\x05\xbe\xeag\x00*w\xd5a\x9c\xa0\xeeE.%\x9a\xb6\x8c\xae\x8c\x07\x83J\x8dl\xd9\xd2 \x16=\xa1&@\xe4}\xdc\x19\xc0\x8e&\x855\x08\xee\xa1Nc\x8d\\A\x95\xc6V\x1a7\xb4|56\xae\x85;\x8c5\xbc\\\xac\x8f\x0e\xf9\x8f\xf3p-\xc5H.\x03\xd82\xc1N\x1f[d\x9b\x91\xf6\x8c7\xf7\xe0\xb4\xe5\x7fpU\xf9\xb5\x9c\xec\xb8\x19\xa3:\xaa\x19\xf1\xf8\xacH\xd4\xebv\xfcFxL-Y/[[%A\x8c,\xa7o\xf4\xe7\xb2\x03\xc5x\x9a\xbc\x80\xb0\xb5kJ\x0b\xf9\\\x87ia\nl\xde\x94gJ\x9c\x80\xf9\x8c \xf5Uy\xa1\x1d\xe1\x13\x8b[/H\xa9A\xe5\x13\xf0\x832\x91\xe2\xf6v\x00\x91\x87~ \x1c\x02hn6\xe7\xf9dS\xad\xfb\x84\x81\\<;\x1f\xe1\x04\xa6\x1a\x1f\x91X*/\xb6\x03\xad\x03\x9b\xe1\xe8\xfc)q.o\xe5F@\x06eT9\x92\xc4\xfe\x854\x84%.\\ \x08\x9bX6\xda\xb5X\xcd\xe4\x85\xd9,\xb5\x89A\xd5\xab\x8a/34\x15*9\x81\x9ecED\x91[\x1d\x91gfd8\xc1(\xf8\xe8\xf9\x1d7\xdb\xc0\x17W\xe2G\x0d\x11\xa7l\x86\x9d\xdc\x88\x98\x101\x80[\xe8\x83\x83\x81\x88\xe8\x93#\xde\xff,*\x98E\xady\x93\x18\xda\x1c\xf1:ff{\xc2k\xa4\x90\x86\x80\x1cF\xc0 \x81\xcd\x06r\xf6W^\xf4\xc8`\xd2\xa7 W\xa1+\x07\xb1\xe7\x97\x90\xd2\x0fJ8y\xe7\xb0\xa3\xc3\xcc\x0c\x86C\xee\xe9\xe7\xb1\xcd\x96 G\xa4]\xd8\xd7V\x9a8\x13^\x8d\xf6cg\"Y\xcc2\xdc \xc4\xcaZ\xd2\x18\x1a\x96\x06\xc4\x00\xb6\xf0\x94\x8a\xa4Y,,\xd2\xf8x\x93\xfaY\xe1p\x0c\xcb\x0c7\"\xdc\xb4L\nDDQE\xc9\xa4m3:\x89\xe9f4~l~\x00\x93o\xd3SEV\x1e'*\xb2\xea\x95\x8eY\x06B\x87\xd6\x81J8Nu\xfd\x95S\xc3\xa2\x03\x92\xd4\xd7\x12E\x9cqW\x02\xe3\xf3I+1\xbe\x12\xcb&|o7\x1b\xd8\xc2r\x90\xf9\xf66<\x82\xa4\xdcl\x13F\x83\n\xad\x9c8\xc7b,\xf8\x80\xe7X\x84h3\xe1\xe65\x031\n`\xa2\xa3G\x93oT\xd6 \x9b\x1e\xeb\xdfi\x89\xecz:\x896J\xabM\x15\x9fy}\x1c\x96\xf7\x9a\xcfR\xb9V\x0f}\x88ZOK\x06\xaf\xed\xed\x0c\x1e+(\xdfv\x12;E\xbfC[\x04<\xbb.\xedj\x024P\xb5N\xa1\xe0\xaa1 \x96\xd4\xe2Q\x0c\xb0'\x01\xaf\xa3\x13\x88'Oe\x92\\\xf4\xc6P5\x95]\x14\x04U\xac5\x1d\x98\xbf\xbb\x1e\x98v\xb2}M<\xb0\x99\x8c%.{\x84x\x16\x97\xf73\x11da\xa3S\xed\x88n\xe1\xb4'\xad\xa4\x8a\xa7\xe4\xc6\xd3\xb2\xceuO\xfc\x92je\x0d\xb6;\xb3\xb3\xdd~\x00\x9a@\xcbk\xe2\xb9\xbf}Y\x92\xd4e]\xba0\xf7\xdf~\xdet X\xb8\xc9q\x914\x89\xda\xe55MZ(R$\xb3\x0e\x86\x82V\xf8U\xd6\x1f)CT\xa3\x0cQ\xc0\x8f\xb0\xa8\x8d.\xb4\xcb\x0d\x8b\xd2\xeaa\x7f\x99q\xa2\x0b\xac\xe47\xc3\xbfX\x07\x9c\xcb\xcb*x;\x13\xf1L\x16\xf6\x1e\xce\xe7\xd1\x82\x80\xd1)\x0fTu\x00\xda\xae\xd4\x99'\xd8G'\x9a\xe7&$\xfcz-\x86\x8fo\xb6\x04X\xf0\x17\xe9\x94\xa1\xce\x91\x18@1\x1b\xeae-\xb4\xe7LT\x0d1oeve:\xca\x16\xb5(\x10@\xe1\x9e\xb7\xd0\xf3j\x02\x8f\xb0`\xcdM\xc8=\xac\xda\x87e\xf2'\x18\xa8\x0d\xfb2M7R\x84X\x94\x03HPR\xf4\x0bIbk\x17\x8bs\x9a\xf1\xca\xac*g\x0b\xcb\xben\x96P\xfa3L\x19\xa9Y\\\x03\xb1\x8a\xa3\x96B\xe7\xd7F\xa5\x04[\x958))\xa8\x93\xc9\x04\xe4\xb9%R\xcdw2\xcfN\\\xe9\x0d\x88^RA\x01\n\xf7\xeb\xd1`\xcc$T\xd4\x10z\xa1\x8c\xa7@\xecb\xc7h\xeeM\xca#3.\x08G\x1a\xf0\xf3s\xd2N\x16\xd9\x15r\xe7\xdcD\x94F\x9b4\x96\xd7\xda\x82\xf0\x8eJ\x90\xac\xa3g\x97\x19i\xdb(`\xdb\xaa]#C\xdb\x81\xa2\xba\x99\x99~\xb1RT\xee\x91\x89\xd1\xaa:\xf9E\x12\xdc\xd0\x986:2SK\xbe'\xa5v\xa3\xe2 HZ\x8a8 \xb8\x8fR\x1cy\xc4K/\x1e\x00\xffP\xb8\x97\x11\xa3\xfb`\x91e\xdaxD$\xfd,I\xa9\x9b4+>!\x1e\x1d\xdd\x1e\x07\x10\x8fn\x8f\x11\xcb\xe9ho\x0c;\x10\x8f\xf64\x19\x82\xfd\xb2 y-+\x83q\x97\x96;i\x08{\xcd6\xeb\x15\xfal\x0d1\xd0\x8f\x06\xba\x81q\xce\xf5\x85\xa8\xf1\xc1\xdd\xbao\xf0_?z5\x85\xa0 \xa7^Zq\x8a\xfb\xbb(x\xe5b7\xfa6\xed\x82,u\xe0\xdcRG\xe0\xcaK\x02\x99\xad\x0f;\x99\xe0w\x0fC\xd8K\x9fK\x86\xef\x96\x03\xff\xea\xfa6\x07\xf6\xbf\x03g\x88\xab\xd9*\x80\xa1n\x02\x973\xb9\"\xa0\x04\x16\xd8\x00\xc2\x13\x90\xf4\xb3dI\xae\xd2\x01C/K\xf3\xa2\xbe\xd4_\xc8H\xc9\xfc\x989\xe6\xc7\x14\xce\xbe\xa2\x1c\xc5U\xa1\x88\x03\xb4\xcd\xf2\xfa\x05\xe2\x1f[s!p\x13\x0b\xaf\xc9A\xfb\x93$\xceh\x9aOP\xb3\xecF\xdf\x7f28zGE6\x1b\x1e\x81\x84%F\xe8(6j\x0d\x810\x01\xc9\xcd\x818mI\x9c\xcc9\x88\x82\x04Zs\x8aq\x0bv\x14g4\x8c'$\x99)\x15\xcf-N\x11\x089D\x8f\xea\xa7\x95d\x9f\xa9gR=\x17MX9tv\xc5\xa8\x96j\xd7\xb2\xe6e(\xe5g\xb2\xce\x8c~\x89\xf2\xdar\xe3\xca\xd4\x8b\xa6k\x87\xb7\xd8E\xb4\x11\xaeN\x9d\xc8K\xcceJfQL~N\x93\x15I\xe9Zp\xbe\xee\xad\xb0\xeb\x94PE\xb4\xec2\x06y\xa9$\x88\x87Mvj\xe2\xb2\xdd F\xbd\xb2\xcax[\x8fo\xdduJk\x89\x98\x03\xe8=\x0d\xe38\xa1\xacuHb\x08c\x88\x8a\xf4\xbc)\x99$\xe9\xb4\xdf+H&\x8f\xb6\xb3\xb0\x98\xba\xab=s\x9b\xbc\x0c\xd1\x08\xf5\xeb\xb2\x7f\x12\xc5S\xaf\x8c\xbak\xff\xec\x12&!\x9d\xcc\x01\xc1f\x1f\xd0\xa5']\xd3\xe5\x11\x91\x0b\xfd\x04r\xfdq\x88\x81\xbcK\x93\xe5aL\xd35\xd7\x95*\xca\x9fv\\\xe9V(\x81\x0b\x7f\xc3F\x95\x04\x87\xfc\xda\xa4B\x14*\xdd\x1a\xcd\x08%!\x11KT\xfd\xc8\xbc\xacp\x00\x1f\x88p\xe5\xecPmA\x1e-D\xdd\xd9<\xef\x85F\xa2AHF\x99BH\x87\xf0\x9aT\xe1;\x9a\xca\xea\x06\x15\xa8\x17u\x0e4\xfb6\x00\xe2\xbd#\x01\xbc\xf0\x03xw\x05\n\xdc\x14\xfc\x90\x02\xeb0\xa1\xd2|-n\xa0\xb5\\\x1ao\x9b\x17M\xb36\x8c\xfa\x91\xf7\xe4K'\x9a\x81\x8d\xcb/\x9bt\xe1]\x15nN\xa1BgJEf=\xbe\xb1&>Jr\xb8\xa5K6X\x19\xa3L6\x80F\x0d\xe7i\xaa\xcd\x88yJ+\x8798\xfc\xd2o\x04\x89\xd6\x80\xc01\xb7\x15;T\xb2\xa8\x07\x02\xa3\x02\xcf+\x87M\x070\xa4W\x01C\\\x03\xc32\\i\xf0\x15\x04\x18\x1a\x85_\xde}\xdb\x19\x11XB\x94\x9a(Y\x1e\x13\xd5\xc9+\xe6<\x07\xc7e\xea\x11S\xcc\xd2%#P2\xdf\xf2?y7>\xcf\xd2S\xf4`T\x9d\x17\xcdG\x81\xc8\xd7\x1c\xc3>/\x06\xa4\xeb\xcao%\n\xdd\x8e&<\x1eT\xb0\xf8\x16\x08\xca\xe3I\x7f\\\xc4U\xddS\xc3\xa0aD\xdd:\xd8\x8c\x8b\xea\xa8\x90\x97\x96\xa1\xd8\xea}Q\x88 hP\xe1JCT4\xf3U\xc0\x82\xf8\xe8\x17V\x98Wt\xcba[\x8a\xf2$!\xde\x1b\x12\xc0\x0d?\x807\xeaR\xe9\x02\x01\x1d\x89x\x11\x0d\xd8\xa4\xe4o\xbems\xb5R\x1a\xf3\xfah7\x9d3o\x86;\x0cA\xee\xca\x92ig\xea\x86\xf7\xdf\x84\xb0\xd7\x82\xa1\xc4\x15C\x89\xc4P\"14\xe5\xa6\x10\x81\x97N5\xc3\x88\xf7\x8a\x04\xf0\xa3\x1f\xc0\xabo\xe7 ,\xc8\xf7\xeaZ\x90\xef\xcf\xc40\xe2\x8e_\xda\xc9\\\x1b~\xfd\x87\x91\xa8\xc4\x9f\x8e\x88\xf4Lp\xba\xcfT\xe8\x10!\xcc\xb4\xf1\x10\xcdu\x14,D\xbd\x9fg\xff\x95\x88\x84.1\xa6\x87\xec\xfa\x89x\xc6\"z\x8a\x93En}\xab@W,\xd1\x8f\xc2\x00:vr\xb1\xb5\xbc\xb9\xcbo\x1a\xa4Xv5\xf5rZD\xd7\x02\xfb\xbf\x06\xd1\x1d\"C\xdd\xf6\x02\x14\xe1\x95\x15\xb7p\x8b\xf3\xa4\\/\xd2\xe6e\x89\xde\x95\xb6\x11\x02G\x0e]\x18\xa0zI\xde%o}S\x0c\x1e\xf7r\x04\x07<\x91\x0bG\x89\x14Q\xa2\xbc9\xe07\x07\xcd|\xf9\xeaepYt\xa0 \x95s\xb8\x9a\x86\xe0\x9d\xf9\xd1+\xf3\xa3g\xe6G\x98\xa3\xcaK\xe3\x00N(\x13-b\xe5\xcdoT\xb0\x86\xb1\xe0A\xb7\xa1g\xd4\xb0V:\xec||V4\xea\xec\xf3\xb7\xe7qi\xf2\xb1w\xe6\xa8L\xe0i\x9e\xe6Eut\x1b\x9aW7oep#\xaa\x89S\xae\xcc\x85\x89\xaf\x07\xe5\xdfRg\xa1\x89\xd9\xac\xcf\xc4I\xf9[J&Z\x95\x15\xef\xff\xe6Me\x00\x15}\xae~\xb2R\x99\xa0\xda\x06\xcc\xd3\xec\x1f\x93\xe5\x8a\xaeQL.~\x0c!\x8f\x85\xa8\xfd\x1bm\xa6<\xadM\xd5Qc\xdc\\\xb4\xd2J\xcd-\xd4\x7fS\xacZy\xfc9N\xcec\xf8L\xd6\xd0\xfb\x1bl\x03\x85m\xf8[\x0f\x92\x18\xd8/\x89\xc7\x06#y\x05z[%\xf8D1\xfd\xb2\x16\x87\x16)\x1c\xf4\x86\x15cBu\x892\xa9\xd7j\xc1\xadJY\x08e4%\xce\xc1~\xb9\x0e\xcd:\xcc\x955pT\xae\x1b7\x8ey\xa6\xc48\xfb({\x8f\x9a\xf8I\xdcT\x01\xcd\xe2\x00\x16\x0c\xc7z\x7f\xff\xfb\xf1\xf1\xd1\xeb\xd7\x1f?<\xf9\xe1\xd5\xe1\xf1\xfb\xc3\x0f\xc7\xc7\x7f\xff{\xaf\xe9\x08\xb2bog\x0eJ\xa3y;\"\x18\xaa5\x91z\xb5& \x05Y([j\x88\x91\xcd\xe5\x87\xa6\xf4\x8eg\xa0^\xae\xe8\x9a\x87O\x17`tSDL\xdb\xf7bU\xc9\xb5\xb2\x04a\x94\xd9\xeck\xe5\xebb9-\xca\xb3z\x97kJ\\\x93p\x9fY\xe9\xd2\x0c\xf3\x0ex36\xdei\xec\xe9L5\x86v\xd7\xdf\xa0\xd2:\xe7*\xad\xd3\xb8\xd4d\x9d\xff\xbfM\x93uj\x87_\xa1\xee\xd3\x14XT\x7f\xad\xe2\xd1\"\x96\x0et+E\xa9\xb5*\x95Z\xab\xaa\x82I\xfe\xac>\x10\xac\xc1*VuV+\x17\x85\xcf\xca\xa6\xf0Y\xb5)|V\xb1\xdc\x870\x84\xb3X\xdc`[\x11Q2\x00\xe2\xadcF\x9c\xfc\x00\xd6\xd7\xa7\x11Z\xff)\x1a\xa1\xf5uj\x84\x84\xff\xbdM1\xb4\x8eK?}N\xb9O5\x94{\x19\x07p\xcc\xf6\xc9\xda\x81\x16\x9ft%l\xc7\xff!\xc2vn\x85\xe6\x92\x13\xb6%\x1b\xefI\xec=u/\xbby\xf1\x0d\x84\xed3'l\xef\x15\xc2\xc6n\xf5\xf38\x9bG3\xfad\xb1p\x8d\xe6\x7f\xef\xac\xe8~bWt\x1f\xc7\xa5\x83\xed\xb1\xba\xd7\xcecqC\xec\xb5\x13\xdck\x17q\x00\xe7\xd4\x0f\xe0\xe2\xfa\xf6\xda\xc5u\xee\x8a\xf74\x9c|\x86\x11\xdb\x10\xe3\xe6\x86\xb8\xb8\x82+H\xd5\x18?'\xe1\xb4\x89\xcf\xa8\xb7\xa2JRn\xea?\xe4\x89\xd7\xe9\xce\xceC\x1f\xbf\xe7^U\xe6\xbd\x00\x07 \x92\xd0\xe8\xe2\xfe*#_\x11\xf2\xb9\x13\x80\xd8\xa8K\xc3!\xfb\xa5\xc9\xde\xd1\xe8%\xcf\xe6m\xbd(9\xbe\xe5\xfa\xbai\x1d\nM_\xe1L\x82\xbb\x7f\xbb\xd1N\xa00\xc0l\xe0\x01\x02\xb3\xfe\x16\xec\xc0\x80A\xfc1W\x1b\xee\xec\xf8\xf8\x99\x89/\xc0\xcc*E\x1b\xa3\xd8\x90\xfb\x90-X}-\xd8\xa5I\xb4\\\xc5GC0e\xc1i\xe3z(\xf1V\x8d\x8a\xa1\xfcn\xad\xfc\xb9p\xed\xff#\xd6\x8b'\x8d\xc5{\xc2H\x91\x83`\"\xd4\xc9\x98\x1f\xda\xa3\xbe\xcf9\"\xfb\xfa\x959HZ\xa4\x16d\xc0\xf5\xd0m\xd9T\x05o_\x84\x07u\xe0\xd0\x08\xcf\x92gB\x01(\xd1\xc0P\xf5\x18\x8a\xf5o\xa6\xce\x87\x06\x19\xc5;E`\xaci\xfdIm\xfd\xe3\xab\xae\x7f\xd3\xfd\xba\xb1\xfeIke*\x15e\xb3E4!\xde\xc0\xde\xa68\xa6\xba\xb4\xcb\xd0\xd0Q\x1d\xa5\xeb\xca\x05\x83\xeb\xdd\xe9N\xd1Z\xeb\xdd\xa7\x91\xac\xae2\x8b.V\xa6o\x8d\xcf\x16(U\xc3\xa0.x\xc5X\x11;\xd8\x18\x92\xb8\x1c\x99\x8c\xa8|\x16\x8e\x1e\xc5`]\\\xc1b,.\xa2V\xe95h\xb8_{\x95\xa6\xab\x16\xaa\xa2\xa3sZ\x1f}\x99\xa6\xc7\x18\xe3W\x9cLi\xe5d\xc22gQ\x95d\xb1\x83\xe6\xa1\x8fw#\xfb\xe9n_\xc4\xb4\xb6\x88\xd1\x95\xd6\xef\x8fXWa\xba\xb6\x86\xdd\xd4V\x85.\xa9\xa9\xb9R\x10\x14\x0e\xf0L*\xa8\xbd2\x99\x8ea\xc8\xea\xcc\x06\x06=\xd4\xc5\x95\xb5\xa0\"\xee@]\x92\xf2hQ<\xbflH\x11\xf3=\x97\xd6\x10!\xad$\x13Le0H\xac$\x13\xc4o\xd2\x16&\xd0i\xb2n:R\xa7\xd9&z\x1db9S\xed\xd9\x97\xba\x9d\xdc\x8e\x91 \xad^\xff\x92\x9fH\xdb\xe2\x07D\xbf%\xa0\x03\xee\xd9\x8f\xcb`\xb2\xfa\xeag\xc8[je\x1e\xda\xb2\xf3Y3\xf3\xb9D\x05\\\xa0\xd6\x15\x85\x9a!\xbc\xd7H\xef\x87q\x00Otz\xd7\x0fO\x9e\xbe4h^\xdf\xb2\xf7/\x1c\xa4\xfd?\nw\xbd\x96\xfc\xa15\x8f=kF\x99\x92\x19\x8eTN8\xaa;\xeaE%\xfdK\xf9\xaf*upK\x19\xf8\xd9z\xea\x1er=\xc0!\x03\xc8\x1f\xb1\xd7pO14z\xd4..\x16ho4K*\x87\xd3\x08ut\xec\x9f&J\x18!\xa9\xa6\xef\"%o\x1c\xfb\x01\x94.\x93Jh\xc4\xfb\xf5\xf2$Y`\x85\x04\xdb\xf3z[\xb4\x06\x11\xf5\xd7\xdbx\xf4\xa4P/\xbeu\xd1\x06\xbe\xb5i\x03\xdf\xb6i\x03Y\x17\xaam\xed\x8b\x9aE%\x80\xb8\x7fT\x12\xc8\xaf\x01[\xa6X\x97\xfeK\xa4\xc4vH\xf3\xf5\x8cz6V\x04\xc4\x82S\x91\x1b\x97g\xda.\x8f\xf6\xcdFk\xa3\x87\x1acP\xe6{0\x98\xde\xac\xa6m*\xb0GOc\x1a+\x88w\x9b4\x81&G\xf1\x94\\\x90\xe9{\xf2\xc5\x010\n\x89\x7f#\xa2\xce\xddz\xf9\xe9\xbd{\xeb\x08\x1cm*l\x17\xcd\"W\x87pa\x84p\xefn\x1d{!\xa7,\xd2\x94]\xd2I!\x17;\xf6\xde\xa9\xdb\xec:\xbb\xed\xbcI^u\"\xa6\x9d\x9a\xcf\xaa\xb3R >\xce,\xac?/WY\xaa!\xe4\x9c\\ \x052\xae\xee#\xbc\xb86\xd0\xbf\x8a\xb2\x0eK\xbe\"\xd7\xd5/7C\xb8\xf7\xdc\x1b!\xc7r\xb2 \xe3\x9eK\x0f\xa5\xa9\xc3\xb1\xfc\x85Y\xbb\x04\xdb&\xc6\xf2\xba\x9f\xbe\xf2\x12\xc3\xcc\xb91\x8f\x97\xd9e\x94?\xc5\xb0\xc7}\xce\x14\xc2\x01\xe4\x98\x92|\x1fB\xea!\x7f\xd8\x8f2\xc1'J#\xe0\x88\x8e\xb5\x94[\xbd.}wOo\xf5*\x10\xc0\xe2\xf5\xad^\xa6\x8a\x1dP1\x16D\x0d+\x8f\xfd\xabA\xed+\xfb\xb8\xcfD%\x84h\xb4\xebP\xe79)\xed\xad\xb8\x08\xa1\x97\xa0\xc7\xae\x0c\xc4\xcd<\xa5\xd0j\xb3\xde\x96\xbc\xcc\xd9W\xcfD\x95(Q\xfdBW\xd7X^\x92\x92ci\xe9!L\xeaT\x14\xc7\xc4$N\xf9T\xd2S?\x90\xf7f\x8b\x90R\x12{[\xbb\xc2\x12\x83\xdaEM\xd1\x13\xebV\x00\x01\x1c%\xcd\xa8\x13\xba\xc8-\xc4\xfd\xa0\xec\xc0\x87f\x1fJ\x85X\xd86XN\xe4e\x06\xf8%\xaf\x8d\xd6,g\x8b\x0f\xa5\xfaV\xe3\x0e\xed\xc6\x8eH\x8f^\x97\xb4\xc9*\xbbV\xf5 v\x897\x98\xda\x12#k\x0b!4n\x91\x98\xa6Qe\xac.CU\xf4{\xef\xdc\xba9#\xe9\xda\xf1Lq\xe4\x82cK*\xf2\x16.8\x0d\xc0V\xf2\x13\x8a@s\x8e\x03\xbc\xd6\x11~\xa1\x14Z\xe3Z\xa2\xad\x81\x01\xf8uG\x12\xd0\x03\x86\x13]G\xc8\xd4O\xae\x1f\xd4|\x82\x9a\xf0'0\xf5\x19Ok=\xbaT\x8db\xc0d\x9fbNT\xcf`\xde\x00UOz\x80 M\xf4\xe5\xc15\xc3\xe2Z\xa1n\xb0\xa8 KP_q\xeei\x89y\xbb\x89\xaf/S\xa3\x19\x08\xe3@\\6o\xbd\xef\xc2\x92\xc2\xe9!\x1c@\x0f\x19\x1f\xd8\x87^\xd03c2#\xc1=\x8d\x1eU^\xdf\x82\xe96\x1c\x8fE\xa9\xfe\xad\x01\xba\xacn\xa3\xd2\x14\xffE7\xa3-YBJ\x99\x14\xaei\xe1E\x83gN\xaf\xc9Y\x82\xd8\x01N|\xdbg\xb2\xfe\x06\xf2\xf3\xd4iE\x97\x159\xd4\x01\xad\x8a-VM\xd9\xe9\xd4\x19?K;n\xb0\x00\"\xeb\x02\xd7p\xad\xe1\xa0\xf2\x08\xf60?\"\xc3\x14\xd8\xe7\xf9\x90\x1a\xdbAU\x03`\xcdZ\x1b\x01\x84\x03\xf0\"A\xe5\xb09_\xb4K\x8b\xd2\xb7\xbcb`b-\xc8\x9c\xba\x83\xec]t:\xa7\x1d\xe1& \x93\xca\x08\x95\x86(;}\x12\\\x8f0\xbd\xa7F\xbb;\x98\x06\x8d\xbd\xb8\xe3n\x81Tj2\\\xa7\xae\xd0\xb8|E\x0c\xfer\xb5C\x82q#\xddz\xe4yYx\xac\xdc\xbb\x18K\x85\xe9\xb2`\xe8\xbaJ\x9djL\xd4gf\x0c\xc8\x01}?(u\x7f\x03\xad\xf9\xd9\xa9\x97\x93\x9c\xbe\n\xbb\xa8\x07\xf8\xbeF\x0f\x99\xdd\x00v\x06N\xbdD\xd9\xe1rE]l\x0c\xa2\x17\xf5dR\xe4\xf4\xba\xe4\xbe/\x96\xb1\xca\x8c:\xf0\xa2&#\xa4\xd3l&I\x1e\xd7w~\xcb|\x9ex\xb4T%\xf1m/\x04X\xfeq\x07\xbd\n\xf6\xfe\x83+{*\xfaw\xa5R\xa0P\xaa\xaf\xd4\xf3K\x83\x94-\x03\x9eD\x0d\x1d\xf1nc]\xf1{\x917\xc1+\xeb\x94\xf3J\xe2lW\xaa9\x8f\x9d\xa46E\xe6\xd2\xb3\xbb\xf2\xb2\x94R\xc1\xb3@5\xb7\x19*\xe4]\xaa\xe7\xad\xcb\xea\x91/y\xb8\xe8\"l\x9d\xd1\x82l8\xb5/\xb2f:l5\xd5\xe1T\xbf\xb6\x18\xa8\xd5?\xc6ty\x95\xe2L\x94\x96\xf7\xed\x9cb\xb5z\xeb\xcf\xb1_S\xb5Z\xcf$\x0e\xc6A\x0b\x1d3\xc3@\xa2\xa0\x1b\x05\x8e\xaa\x94\xb7\xd5\xfc\xa4P\xb0\x00\x12OG\"\xe5e\x18\x7fgQc\x1ev\x913\x90\x0e\x89\x84\xcbK\x1eC\xb0t\xec\xe5\xa8\x0b\x0d\x97\xfdp\xaf\xd1.=E\xd9\xfb\xfc\xc4\xb1\xc0g!\x03\x0eM>aE\xa5\x14nu\xe6<\xba\xa2\x13r[\xda\xe2<.\x12\xe3t\xc8\xa7\xa5\x9f\xe2\x8a\xf1B]&\xe9\xd9f)`\xa6\xcc\xd2/n\xba\x9fj\x9f\xc9\xfa\xed\xac\xc3\x90\x8aC\x8d1s\x9d y\x0dFB\x1eq\xee~\xc4W\xb42lW?mH\xa9.\xdd.\xba\xab\xd1\x1a%\xbf\xfa\xc8\xcf\xba\xf7\xf7\xf2*\xebb\xe0\xbdq\x8d\xb5\xb9\xac\x9a}/\xc3\x8b\x0e\xbd\xbe$\x9dT\x18\xcb\xf0\xa2\xeb\x99\xfa\xb2\x92\x8f\xc8\xa9\x137\xa3Yc\x06p\x00ob\xee\xc2\xf2\xd5MPZF\xf1\xd5\xa7\xc3\xbb#\xbc;\xd7\xb9\xa5\xa43&jC\x1eA\xdf|\xf69Zu\x80\x9d\xd2\xfe\xeb\x90\xce\xfb\xcb\xf0\xc23T$6tV\x17\xbe]\xa5\x04\xc3\x1ecMzT\xb9\xe3<\x90_\xe7\xd1\xa2\xa3\x99\xa1\x18\xcc\xefW4l|\x8eV\x1fc\x1a-\xbau\xcb\x81.\x87\xdcM\x05\xc5\x13\x82u\xeb\xafi\xe5\xd0d\x06\x03}\x7f4\xfcL:,/\xad\x18 \xae\x80R\xac\xbfkF)\xd6dw\x94b_}\x0bJ]E\x92\xf8\x87\x13w\xab\x940\xfa\x18\xa3\x9a\xb7\x92\xbc\x0d#+[\x18^\xc9NS\xa3vY^L\xa4\x8b\xaa\xb1yJ\x81\x96J\x18\x08vlo\xedL\xd4\xf3o)\xfb_0n\x1a\xc1\x87\xa2J$l\x9b\xa1\xd2L)\xfd\x14\xdf\xde\xbc \xdb\xdb9\n\xa9\xa2AC\xa1ry]\xfa\x01\xe4\xc67.\x03P\xcb \xfd\x17\xadJ\x92vY\x16Z\xf1\xc6b\xdf\xd9\xe5Zv\x85\x16\x8f\x12y\x89q:FY\xaa\x17\xfaN\x85\xc5L\xdb?\x00\xf7\x88G\xf5\xb2F?\xaa\x97!VB\xbd\xa4\xe9&o-N%/\xae\xc3\xaf\x14\xa9\xb2x\xa9\xcaKF4R\x11\xc3\xdb\xfa\x01\xbb2\xe1\xac\xea\xf6\xf6\x04\xdf\x1e\xb4\xb8\xb6\x82n\xafM\x02\xc8P\xe3y\xc0H\xdbp\x08\xef\x84\x98\xf3\x9cad\x86/\xf04\x7f\xa1\xf0\x0c\xf9/X\xdc6\"`\xa5\x00\xda\x87\xdd5\xaf\xec\xe0\xb9*SQ\x1cZ\xdd\x98\n\x19C\xd0\x91/\xed.\x86\xcd\xc3l\xfe4\x99vpt\xa1\xf32\xbb\x00\xd6e\x9a\xab\xd9\x06\xday\x04(\xb6\x17wP\x1e\x0ea\x00\xb7`\xb7\xd8h\x16\xd2%\xcd\xa4\xb3V\x05\x9f\x9b+\x7f*\x8a\xdf\x0e\xf4Uo\x8b\xd7\xf8\xc0\x9c\x16\xbf\xf6\x0d\x1b\xed{\x14\xd2o\xdf\xb9\xbd\xf7`p\xff\xf6\xdd\xdb~P\xdc\x86G\x8f`p\x176@\xe0\xf1\xe3\xc7\xb03\xb8\x1b\xc0\x9d{\x83\xfbw\xee>\xd8\xfd\xbe\xfe\xdem\xe5\xbd\xdb\x01\xdc-\x9fc:w\x8f\xc06\xdc\xbe\x7f\xef\xce\xde\x83\xbd\xc1\x83{\xb0a0\xfd\x17\xdb\xd2\xff\x12\x9f\x0d\xee\x05\xb0\xb7w\xe7\xde\xfd\xbd\xbd\xbbE\xf3\x87\xe2s\xec\xa6x\xf3v\x00\xb7\xf7\xee\xdd\xbbs\xff\xc1\x83\xdd\x07\xbe\xda\x84e\xcby*\x7f\x10c\xad\xcb\x83\x8eP\x83!\xdc\x1e\xc0w\x90\xc26<\x8f\xbd'\x147\xcd\x13\xea\x11\xdfg32w\x0e\x8e\xbbS^\\+~\x85^\xaa\x93r\xe9\xa6\x98\x11v\xd4\xdaA\xb7\xc6\x1d\xdb\xf5\xb5\xe5\xac\xa1 \x88:RX\xb9SW\x06\xb3\xbd\xf8\x9a''Sr\x01\xa8o\xbc\x8eG\x0b\x19\xe0\xfd:\x1e=c\x7f\xbf\x16&\x8b\x8c\xdd\x12\xa1\xa3\xfc\xb6\x08\xac.\xee\xab\x81C0\x84W1>\x89\xe2l\xc5s\xe3\xe3'\xef\x93<\xad\xe6\x95\xd1\x81\xac\xa6D\x12\xee\xad\xd5\xd9a\xeb\x93y\x18\xc5\xbcma\xcb\xe4\xb7\x93\x98\x86\x11F\xa5\xe3\x10\xb8\xee\x12c\xc4S\xdd)9[D\x1dB#\x0b\x01\xe5+1\xae\x84N\xed\xb3:l\xb8\xf7\xbbZ\xff\xcdT15\xcb\x02V\xe1\xae\x93a\xb5\x90&\xa4\x93\xc4( \x1a\x9b\x8bO\x03p\xa3\xaab\x93t\x14\x1a\x97\xe1\xeae\xd5\x07\xd9\x15FW\x00\x02[\xf7:,\xda\xc4\x8c\x06,x4\x82\x05\x08\xd8\xc9Uv\xeb\x87\x18\x93\x9b\xb4f\xeexj\x06\x92<\xd5\xaa}\x19\xda\xf9\xb9\xb5\x9d\x11 \x80\x8e\x9d\x1a{g \x87\xf5\xb3\xb9e\xb3mQ\x97d\\\xd0\x84\xa7aXo\xaegX;\xd7<\xacW\xf6a\xf52\xa4\x81\x15\xe3\x07\x1c\xc0O\xef\xdf\xbe\xe9\xf3G\xd1l\xcd\xd5\xb6\x82Z:\xe6\x16}f%\xc0\x87\xc6L\x9e\x86\xe6\xbe\xb6b\x10\x85G\x05\x07G\xe11\xfe\xbd\x83\xec\x9cS\x07\xcf\x1d:`\xac\xcf6\xec\xdd\xbb{\xe7\xce\xed\xbb\xdf\xdf{\x00\xdb\xe0Q\xc6\x90\xdd\xf3\xf9\x9f\x8f\x1f\xc3^\xf3\xf4\xad.\x94h\xedCT\xaf\xc2h`\x95\xcb\xe5\x95|\xb3\xad\xaeu@J\x1b\xdeV\x82\xa5\x00\xf8\xba\xf2\xd0R&\xa2G\xbe\xaf$-\xc5f\xc5}k\xcb\x97\xac\xf7\xc0\x96GC\x85\xa8\xdel\xe7\x0c\xd2\x80[\xee*1~\xd8\x7f\xeb\xe4\xdd\xed\xa1W\xb0\x9f\x15\x90\x8d\x18ds\xf8\x1f&;\xb0\xad\xc7p \xa9\xb8\x00c\xcc\xef>\x7f\x07\x0e\xe09\x9b{\xce\xd3\x91\xa2\xd5F\xfe\x8cd\xca\xd86\xf0[\xad%\x86T\xe5%\x95p\xde\xc6\x0b\x12\x9e\xb9p^\xd2,7b]\x8c5\x87\xb2oY,\xb6/op\x02 \xf5/\x01\xdc\xe8'3t\xa65~\xc6\xf3\x93(\xde\xf9\xd6s\x96\x14\x1b\xdf+\x88\x81\xb8\xc7\xe8\x80\xc8H\x13\x94\x94\xc8\xcd\xc7\xa9\xab\xcb\xdd\x92z\xbbj\xcaj\x97>\xae\xe0_\xc7\x0e|\xc7\x08\xd5\xebv\xefq<\xf9\xbf^I\xafzC\xfe\xf1,\x0el\xc8\xe6<\x86_#:w9\xa7\xa4\xcc\xa3\xf6b\xc77\xc6\xd3\xc9\x00\x81\xe6\xf8M&\xcb\xca\x9dK\x9fQ\x842=\xec\\\xea\x1b\xd4\x9bE\xdd\x96#t\\o\x0e\xbf3\x8f\x85\x18\xc4kA\x0b\xb3\xb2\x93\x9cv\xd5|:\x9a\xaa\xd3p=\x9b\x0d\x9b/s\xb89@;Q\xf2l\xf3\x12\xda\x15+\x81\xfaX\xb1$\xa8\xb7+&\x85\x17\x81\xaa\xa4\xf5\xf1\xde\x8d\xca\xf2\xf1{?V\x9a\xe6\xf7N\xa8\xe6\xe3s\xaa\xf9\xfa\x82\xd6?oBE\xe6\x97\xdb\x87\xb8 W\x04\xea\xcb\xe6\xfd\xa7\xc9bA\x10\xd2\xfbp\xac)\x90\x81\x01b_5\x0f\xd4\xb4\x92G\x1a\xe7 \x9e\x97o\xa5y\"R\x05^hGI\xf7!\xd3\xe5{\xbb\xbb\xd3O\x9f\xf2\xe9\xfd\xdd\xdd\x1d\xf6\xefl6\xfb\xf4)\xdf\xbd\xcd\x7f\xee\xde\xbe\xc7~\xce\xc8\x1e\xfe\x9c\x91\xbd\x19~3\xc5\x9f{\xbb3\xfet\x97\xf0\x7ffc\xd3\xe0\xcc\x14\xad\x100(\xc9\xa8J\xc7.\xbb\xc1i\xb0\xfb\xa0\xc6\xeb0.\xb2wx\xb1\"\x13J\xa6\x10\x16\xed\xf4\x14c\x8f\xbc\x07\x89\x96\xb0G3\xf0\x94\xf8\x88-\xc5D\xb0\xd9\xc8\xecA\x1cE\xb4\xaf\x11\x1f\xe8\x9e\x864<>\x16\xd9F\x9bX\xa9h\xf1\x84\x14[\x83\x0c\xbb&\x9a\x1aTQP\xb9]\x14\x82M\xaa\xf7yQ\xc4\xbcz\x933\xc4a\xf5f\x86ofUB4\xe9\xb6:\xb7\x1f\xe8\x97\xe7\xce\x83\x96\xe3\x18\xa8\xc8\xcb\xc1Co\x1b\x8e\xeb\xca\xe6\x15\xc6\x0eOT\xe6\x04R\x9c\x80\xf2\xd1V\xc4\xb8\xab\x9b7\xd9\x1f\xb1\x8fJay8\xc6\xec\xaf\x98\x1dA\x95\xfe(\xeb\xf2\xca'\xfe\xed\x07\xb7\xb5\xb3\x1e|_G>\x81\x94\x0f\xeei\x90r\xd0\xc4\xc7\xbd6\xd2!k\xb9pG\xe1\x99\x0e\x15\x17\x98\xb5\xf8&\xe4\xcd\x03\x17\x0b\xb2\xca\xb2\x8c\x8d\xa7s\xc4H\x9dY\x8a\x11\xa8\x15\x03\xe4\x1c\x81\xec-\xd8?sx\x0c+;]F\x9d!\x0f\xd0\xf5\x9b-bAK\xfeX\xa9-6\xc5%n\xb6u\x06C\xd8\x194G\xbd\xe62t\xe3\xfe\xa9\x00C\x08\x07|'\x82\xf4\x8e\xae\xb6\x8dy\x01fx\xfc#\xa9\x0f\x80\xff \xbc\x06\xe8\xf6\xf6\x19<\x82\x956\x11\x00\x1b\xd6\x92\x81ttf\xe0n\x8e\xb1(\xcc\x99\xc6Q\x9c\x01 \xf3\xb1\x89\x13\x18\xc2\x02\x0e \xf3\x8e\x03X\x06p\xc6\x03\x91py\xf7!\xf3\x96\x01\x1c\xe3]\xbe\xfa3\x0d?SK\xe2{b\x92\xae\xd9{'>0\x018\x8aM)\x0b\x10\xa2\x03\xfd\xb3\x93\x94\x84\x9f\x1bO\x9a\xe7\n\xeb\xe8\xd46\n\xb6e;\xd8\x0c\xf0\x93\xc4;\xc5\xd7n\xde\x04oY\xe6\x8c\x9e0\x08Q\xb9-f~\x89K\xa7<\x16\xdf\x18\xdel\xeb\xd1\x06\x050B\x02\xb4\xd0\xb8\x04\xb2\xc8\x08Nb\x89\x0bt\x8c\xfbh\"\x96\xb6\x18\xb8a8\xdf\xba \xda\x13y&N\x10t\xba-~0\xfc_\xff\x9f\xea\x876n\xc8H\xa5\xeas\xa9\xd4_\xdb\x11 /%\x11\xa7\x98&o\xbf\xa0Ml\xdb\xc5\xf0\x08\xd2\x87\xcd\x95C\xd3\xb8GG\xf1\x18\x01\xa7r\x86\xbbZ\xfeOI\xef\xd4\x91\xcc\xdf\x19\xd4y\x83\xe2pkRyQ\x91\xa98^\x9b\xf4\x1e%\x19\xa5\\S\x93\xfc\xa3*\x08\x9f\x1de\x87q\xbe\xe4\x8a\x9f&{\x92\xda\xad\x1db\xe2\x85\xb8VE\x06\xcf\xf7\x85 \xde\xae\xec\x13\xad0\xe6\x9bak.X\xcc\x00z\xec\x0fBz\xfc\xc4\x0d\x9b\xf7\xab\xfd\xe9\x8f\xb4\xcce),\x99\xf2\x15\x06Qch\x10\xeb4\x18h\x9e%m*\x97-\xd2\x8f\x93)aB3\xdek6\x81\xab\x89\xa2w\xb3\x1d\xca\x8d\xd4\xac\x1dZiG\xa3sbk\x9es\xe0\x16\x90A\xc1\xe4\x00\xd2\xfe\x0f\xf9lF\xcaS\xab\xf95\x03\xa3\xc7\x8e\xb7\xb0\x1fe\xb5\xb7Q\x8a\x8d\xccJ\"E\xe2\xa9(\x89\xee\x0f\xfc\xc2X\xdc}\xdf\x1b\x988\xda?''\xabp\xf2\xf9\xe7d\xb1\x9eE\x8b\x05\x0fY\xe9O\xc9*%\x93Z\xedG&O0\x96t\x15\xd29k}4\xc6L\xf1\xf3h1MI,\xbe,~\xb2\xe7e\xb9\xb4)\x99E1\x91\xfb\x0bqr\x91\x84S2\xed\xe9\x14\xab\xa4\xd8a\xfbz\x0e\xa2K\xd1\x19\xda_4\x1e7\x95\xd4\xe6qF\x7f\xc9\x18#\x8716Wk\x08\x83J\x02\x9b\xced\xd4 #\x0c\xea\\t\"\xee\xdf\xd1p\xcb\xb8\xdf\x92~\x94\xb1\xfd4\xe5Q\n\x95\x97\xf8f:\x80\xc8\xcbQ\xe5\xa4\xa7;a\xb7\xb1\xdf\xdd\xbd\xaaZ\x91\xf2\x83\x8d\xd1\x81\xb4]\xb9\xd8\xbe\xb74g\xaa<\xc9\xe5;Z\x87\x17\xa9!\x10\xfa\x05\x91E\x90\x8e\x85;_\xcd\xdf\x84p\x8f\x92H\x16'\xf4\xe2\x9a\xa9\xeb\xf2\xaaX0\xb8_\x97\x818\x16|\x7f\xbf\x15\xc2m\xec\xc4.\xf72\xf0\xb8\x1a\x88\x07\xf1\x17\x9cD\xa1X\xe1\xd2\xe0#H\x1e\xfa<\x85\xe8(\xf2\xc8(\xde\xde\x1e\xfbc\xbdv\x8f\x7f!\x082-h\xebU!\xa0\xd7\xd9\x0d\x1a\xd8.v\xc1^\xfd`\xe3\x8a\x8c;\xdf_\x05^bJii\x18\x8c\xc4{\x07\xc0\x90a\x1f\x12/\xaf\xb8 9M\xae\x97g\x042\x9aF\x13\xaa\xa8\xf6*^X\x0d?\x11\xe9j\x13{\xdf?\xa8\xebF\x94\xe9\x1c7E@&\xbas\x98\xdd\xfb\xbe\xf6\xe5q\xff\x1d \xa7\x8cN\xbe\xa7\xfc@YV_`\x80\xbe\xeb\xf7\x0f\xcfHL\x0f\x97\x11\xa5$mv\x10\xb6\x81Q^%\xd1\x8f2Jb\x92b\xd1M\x8er\x8d\x0ft\x96{\xb1%\xea(\x01\"\xb88\xf6\xee\xef\xfa\x82\x03h\xbe1CA\xfdc\x14\xd3\xfbH\x07\xd9\x9e\xad\x9c\x9f\xcd\x99-85\x1b\xd4\xc0\xb6\xe8G\xf1\x9c\xa4\x11\x15J\xaf\xbb\x1a\xf3\xc0\x8a\xa3\xdd\xdd:\xb1\x06\xa12\xd0 \xd5\xec\xfe\x8am\x9fU\x7fJN\xf2\xd3Er\n\x07\xca\x0f\xaf\x97\xd1\x94\x84\xcb\x9e\x0f\xfbmC\x9f\x06(\xfb\xb3!\xd4w\n\x08\xe1\x88\x81\xb2\x8eK\xe5\xd4\x98X]7\xf9\xb3\x86O\x19\xf7\xd0#i\x9a\xa4=\xc6\xbd.\x92\x8c\xb0?\xa6$\xa3i\xb2f\x7f\xae\xc2\x9c\xdfKI\x96/Iol\x8a\xd6Y\x1a\xd1%\x01\xa1i\x8e\xbd\xbd\x81\xa8a\x81b\xab\xae\xbe\xa0$\x16\x04\xa28\xa3a\x94w\x86\xe5S\xdf\x0f \x13j\x85F\xb6?\x13 OJ\xe5\xb8)\xdaS\xe1!h\x0d\"M\xb0 \xdd\x147i{ym\x8f9q \xa8\xaa\xe2{X\xae\x93^\x89\xc7_\x14xfSJ\x9e\x15\xc5\xdd\xc4\xcb\xacu[*\x15\xce\xc3J\xaa\xc4\xa0N\x04\xdd\xe2\xaa\xd1\xd8\x0f\n\x9d?l\xb3\x86\xab\xd4\x17\xf6\x8b\xaf\x0dJT\xed]RR\xae\xdd\x00\x0e\xb5\x86I\x06\xba\x1c\xeb,zH\xb3\x11\xdf\x9d\xe0\x8aP\xd0\xcf9\xe5Uy&\x85F\xc4KQ\x15\x92\xaa\xdbf\x86\x94\xa6\x19}I\x94\xb8\x83a!\x0c\xd5NK\xcc\x12\\u\xaa\xe8\x1d\xc5g\xe1\"\x9aB\x9c\xc4;\xbc\xd9[\xe2p\x98\xcc\xf3\xf8s\xcf\xb7\xc5\xd3\x18&\"\xb6\xb5\x06n9: \x06\\*A\x02\xee\x15\\L\xc2\xe0\x99\xd7\x86,\x1c\x89\xc4*?\xc6\xc8\x1f\xcf4\xff\xfa\xc7e\xa5\xf9\x9f\xa5j\xf3\xed\xcc#<]\xb1bND\xd8\x10\xa7\xe4#bn\x13\x0c%\xd7\xe3\x06N0e\xa7\xb4z\xe45\xe7\xcb\x16B,\x02\xe7(\xfby\x9c\xcd\xa3\x19\xf5|\x08g\x94\xa4@\xe2)\x10\xc6\xf5\xf7\x10\xd7\xce\x11\xedd:;\x04\x16GU\x97\xb6q\xcb\xc8\x86\x0f\xdf>\xe7M6\x88C^\x1c\x19L\xfa\x8f\x19\xb4 &>\x92\x9b\xf6<\x8d\x84\xae\xbd\x0em!\x85\xcb\xb5:\xa8\x8cw\xc0z{[\xee\x9b\xea3\x9fW\x8fb\xcbP\x1d\x90\x0e\xfb\xea\xaa\x83\xb6\xb5\xda\xa2\x02LH\xb8\xab\xdc\x04n\x92\xa2HV\x8d9,\x99.j\xa4#\x97^\xeeF\xe3\xcf\x15\x1a\xaf\x1b0)\xb8\xa8\x9b7\xe5\x1eVh\xdf\x16\xe1l\xd1\x01\x9b\x02_\xebiHC\xb6\xd4\xa8\xf7b@\xf3v\xf9\x9a:\x12E\x8e\xa4\x05M\x95\xc8\x17\xb36t\x94\xb6\x02\xb8\xff?{\xff\xbe\xdc6\x924\n\xe2\xff\x7fO\x91\xc2o\xc6\x03|\x84h\x92\xba\xd8\xa6M\xeb\x93e\xb9\xc7\xd3\xed\xcbH\xb6\xbb{\xd8\xfa\xa9!\xb2H\xa2\x05\x02l\\(\xab\xc7:\xd1gw\xcf^#\xf6\x01\xf6\x9f=o\xb0O\xb0\xb1\x11\xe7MN\xef\x03\xec+lTV\x15P(T\x01\xa0,\xf7\xec9\xdf\x87\x88nS\xa8B]\xb2\xb2\xb22\xb3\xf2r\xef\x1e\x92F\xc7e\x8bJL\x9a\x16\xfa\xe85\x87\xe7\xd2}C.\xb8\x18\xd4\x9d\x1b\xa9\nU\x17$\x85\x7f\xb8wO\xf7\xba\xe0\xfc\xaaK\xac\x91\x81\xdb\x05\x0c6to\xd7\xf6OO\xf86F\xc3\xe7%\x83\n\xc1\x88\\\x8b\xdf\xe5\n\xe7Y(\xd7\xc9\xffRj\x15u\x1a\x0f3&\x0d vdA@\x11D\xe3\x06.7N\xeb\xb6ix]\x8es\xdf\xc8\xec\x08\xf5P\x19\xd1C\x91\xebN\x1b\xa9\x80.\x02\xd25f\xf1\xa6r\xf3,Hv\\f\xb8\xa9\xc0#\xc8>\xbbl'\x98\x99\xd1qyg\x8eK\x19\xb9\x92SB\xc5\x9fC\x81 \xdfs\x8d'\x0f\x9f\xa3\xd4<\x93 (\x87\xa2z\xc4+]\xf8\xc9[/K\xca.P5]l\xf5\x8b\x94_\n\x86r\xfaT\xd7YBd)\xa9\xd5\x9c\xda\xc91\x95\xcd\xa2\x885\x86z\xb2p\xc3j\x94G_U\xac|\x84\x11<\xdcy\xf8p\xbf\xf7\xd0\xa4/95\xa2n\xae>\x7f2b\xfe\x8dU:N\xf2#\xbb\x87d\xb6B\x9dS\xa6\xf0=(\x1f\x08\xd2\xa9\x9a\x93\xe6\x05\xf1\xa6]z\x08\x88\xb2aQm\x88a%\x80(\x07\x1ac\xa2U\x8dA3!\xcb'\xf6t\x04\x1fQ K\xff\xa5\x9dloSY\xeb\x13\x1d2F\xf7*\xfd5(\xfd\xb5[\xfa\xeba\xf9\xbb}\x17\xd2NG\x9bk\xe0\x86\x9d3\x08U \x0e\xe8!\x92CS\x9e9\xa9h\x0cz\x98\x9f\xb9\xd59}\xac\x87Bn(\xd7H\x8f\xaa\xbd\xf7\xe9\xe9\xa9*+(\xd6/l\x8b\xbe\x16\xef,\xb7XtG\xf7\x0d\x9bI\xce \xb0|\x1f\xef\xfc\xc9\xa5}\xc8#/\x1eV\xdceM\xf3<\xd4\xcf\x93\x0f \xc4$-\xe4.\x18\xc3!\xbf{\xd56\xa0\xcb\x1b\xe3n!%}\x08\xb2\xe0\xaa\x86\x04\x9d\x8e\xf2I\xfe\xa4u`2u\xfc\x93\xb1\xe3\xd2\x05Ln5FY,\xc1z2\x86K\xda\x7f[\xa4\xe0!I\xc10\xea\xf6\xd7\xc2\xb6\x96\xde\xf5\x05\xa1\xab\x86\xf3@\xf5B\xcf\x92\xd94\x17m\xfb\x8a\xce\x9d\xc7Ny0\x0d\xc0\x1a\xa9\x89\xbfL@\xb84\xaer\xae/\xa1\xe0M\xfd\xc9\xa5n\x9c\xad\xfax\xd9\xbc\xc2\x02\xdb\x99\xe6M\xd7\x13\xe2\xbb^1G\xaa\xca\xb4\x1c!Q\xb3\xcd\xd1\xd1\x05u\xc9\xa4\xe5\xdclJ\xaf>\x97\x08 \x8a-l\x8b\x8e\xa7\xb4\xad\x1f\x97\x07\x99\xa7R\xe6\xe3s\x1e+\x02\x8fi\x84\xef\x9a\x0e!\xe5\xe89`]!u\xac0J\xf9\x91\"\xc4\xcf!l\xa5\xec6\xf5i\xa9\x0d\xbb\xa4\xc0\x91\x0f\xa3\x9f\"?\xb4-\xbc\x13\xe9\xf3\x9eyI\xcd\xc1%\x0b\x1a\xdc\x9f\x92\x14>\xb1EQ@\xbc\xd8F\xd9&\xd4X\x94\xd6\xa9Z\x0c\x1a\x8a\x94\xed]\xf5\x00=\x00Lu$\x97H\x91B\\\xb9@[-u\xf2,\xc8\x1c\x06\x9a.\x88\x04\xe5p\x93\xf0\x96\x05\xc5\xa2\xad\xea/\"\xc4\x13Wmt\xd5\x07\xef1qlf\x15\\\n\xdb#\xf0\x8dDI<\x88\xed\x8f\x81\xc5r\xa4\xf4\xa46\xf7\x14\x08uf>\x80\xfa\x81\x82\xb8\x91\x81\xa7\x10\x15p\x8c\x8a\x13\xbf!\xb2\xb2?\x03;c\xd6I\xc5\xe7>\x95\x8e#\x18\xf2\x1f\xe5\x85f\x9b\xc7\xc6\xe9g\xb5\xa6\x96\xe2\xa9\xb4ow:\xb1\xcb\xc1\x81\xab\xbe`Zf\xfefX\xbc!\xdd\xd4\xf3\x03\xae\xe7\xe7\x02\xbc\xa8\xecr\x08A1\xc4\xcc\xa4\x91\x93\x1f\xb3\x85\xa7xn:\x1d}xc0jFA\xb2m\x17\x13\xddFw\xa0\xaam\x0e\x085)q6\x89\xab*p|\xd2\xf5\x82 \x9a\xbc\x0f\x13oF\xdaE\xe1m\xb1+(\xca\xd7\x98\xc5\xc6l\xa7N\xa2\xd55\xaa\xde\x04\xe7c\x97\x83\xe4\x8b\xe0\xbc\x1eSaS\x9c\xf7k\xc2]\xb8M\xc1\x974\xb9\xee\xf0+~\xde\xb9\xc5 K\x19E\xc3ev\xb9{\x13\x9bp\xf4\xb9\x8c\x0c\xbb\xde\xe1\x13\x7f\n=\xd95\x93)\x98\xffd\x910\x17Ql\xc7\x024\xa5\x9dB\x14\xe2\x9d\x02Y\xae\xd2k`J\xe8?i\xe6Bd%9\x13\x02\xe4\xfb\x17\x89\xfd\x7f\xabMrb\x8c\x1dj\xd6\\)=rU\xa1\x98$\xb3\xd2,_V\xf7\\\xce\xcbVD:\x9b\xce\xdej9\xa6\x93v\"I\x8fk\xbfr\xc9\x84\xd9\x93C\xd8\xe9\xe8/\xb20\x1a\xfa8\xe4vq\xc5\xbd\xaaQY\xb6\xadJ\x0f\xf2_\xb2B'f{\xb2^C\xc0\xa5 \x8b\x9d\x9d)\x8c`\xe5\xc5 y\x19\xa2[J_\x17\"e]\xf2;+\xe1\xa0\x9e\x12b\xa43=z\xf2\xf5\xe3\xca\x0d\x9dQ@N\xdd\x98\xffyE\x93-a\xf8\xa8\"\xd3}\xfa$\xd4\x0c\xc5\x8d5\x9f\xf1\x10*\xe2;k\xc7\xcd?qku@G\xec\x92\x18\x86pl\xf3\xcblJ\x10M\xf3\xe4\x04z$TP\x8e\xd4\x9ac`\xfc\xef\xdd\x13\xbd\x98\xdaF>\x99\xa5\x13-\x83\xc6\x88>\x0b\xdb\xa2\xf5\n%\x01\xe6\x15\x11#$\xd2N\"\xd2IS\x95\x97q\xfc\x0b\xdb\xe2u\x02\x92$\x90.\xbc\x10\xaeh\x8d\xa5\x17_Zl\\\xa8\\\x15`\xc3f\x85hw \xd6\x82\xfe\x11\xe1\x95\x19\xde!\xf8l\xe1\x91\xbf\xe3R\xf94\xc2\x01[\x8e+}_R\xa9pMQ\x05\x80:\x8dRI\xe3\xa8*\xd5\x1c\xb9\xc9\xbe\xab\x08\xc2l\x05C\\A\xbe*lic~\xc4\xf7\xe0 \x17\xf0\x86\xfc\x88<0\xe8\xb5\xd0\x0e\xc7\x91u\x7f\xdb\xa8\xec\xd4\xce\"\x07\xa0aFa\xb1\x95$\x85\x07\xc7\x1f1T\xd4\x8d\xe7\xd7(\xa5\xbb\xa8\xb8\x92w\\Q\x10\x9f\xb7\"(R\xc3\x9a\x0bM\x06q\x07\xfc\x04\xc2(\x05\x7f\xb9\n\xc8\x92\x84)\xa9\xd2a\xe5\x06\xc2_\x91\xd67\x10\xb5\x01\xd5\xa2\xb6\x97\x13\xc9\x95\x8f\xae\xc6\x91d8eb\xad&^B\xa07\xd4\x96\x01:\xe0\x0b{\xac\x1af\x0f\x99 }1\xb6\xdfo\xd3\xfe\x98\xfft!\xad\xc9\x13S\xd3\x15\xbfOi\xec\x8b] 5^wI_0\xd3\xb3\x0e\x95n\xe9\xce\xc7%\xc5 \xa0\xa3?N!Z\xa5\xc9\xe8\x8f?Yn\xa9\xb6\x9e\x1f\xa3\x8b\x8c^([\xcc\x90\xb0\xcf\x15r$\x9c\"YJ\xf9\x1dP\x92N\xa3,U\xde\x908\xa6\x92;\x0c\xe1\\\xb9%\x80\xb2\xc3\xb5\xce\x88X<\x0b\xdb\x8a\xc2,\xa4\x03\xb5\xd8m\x92\x08\x88\xca.\xdf\x99\x1e%\xee.\xbc\xe4=\xd6b7\xd8\xa5\x17\x8c\x06,lk\x12\x10/\xccVB\xa7\xb6\x8c\xd6\xdc\xf6\x8d\xc4vn\x1e:\xd7\x96\xce\xfc\xd0O\x16\x96\x0bKm\xf14\xf6\xfc\xd0r!\xd0\x96\x8a\xfdy\xad-\xe5\xb3saB\x89G\xf5\xe3\x90\x92\xeaYM\xd9\xb9\xb6\x8cS\x9b\xb5\xe3\xa2\x85/\xde\x82E\xb2\x96\x10\xaf\xf5\xcf\xafb?-]\xbcn\xa9/\x91\x08\xe6\x9f\x04\xfa\xa8\xf8\xe6\xf5\x9d\x19\xaf\xa2qm\x913d\x86{\xd3\xc68P\x808^2\x18\x91x_\xe4\x11\xc2n\x14N\x88\x00\x0dZ\xbeu\xa3\xb0\x04e=\x9e\x07\x8d\x14\x174v\x15Mrz;\x01B<|\xb3\xbe \x9fs|\x92\xd5\xba\x8e\xa2\xe5\xc5\xf3\xa7\xf8{{\xbb8\xcf\xca\xb58\xfc\x8c+\x8cQ1m\x886~(h\xc1\x7fc\xeb\x84-\x06\xe3b\x17\xe8A\x8cx\xa8\xd1-\xac\xb9+9-3#\xd2\xda\x9c\xab\x171\x89M\xd0\x05\xa1\x12\xe7\xd4*\xcd\xadq(\xfa\xb2\x83\xdd\xees\xa9\\\"\x97\xe8}\xc4\x89\xbb\xf0<.Ux\n}Z\x89\x87_=\xb1\x0b\xfa\xcf\xe3t\xae\x04\x135\xf3\x82\x84\x00v\x0b1IVQ\x98\x10\x17\x84\xady\xa8^\xc0\x96\x96\xb8\xa6\xb4\xd3\xe1\x93C.\xa4\x8b\xedm\xba\x1b\xaf\x1b\x80(H\x15q\\8\xb7\x1b\xa9\x19C8\x86`\xec=;\x17\x14\xc6D\x17L\xb1f\x90s\xe3\xb6j \xcc\xe7Z\nb\xeehYO\x9bx\xdb\x8d\xc7\xc5\xa6\xdd\x9e\xd7u[\x1cva\x97\xfdnw\xf6\x0by\x96\xed\xc4\x9c\xf8k\xbbi{;\x00P T%\x1b\xfb\xaeb\xb2\"\xe1T\x00\xa5\x08P\xae\x96\xb0h\xcd5*\xf4\xee9\x9a\xf0%\x0cy\xf8\x1fcr\x06\x07\x90\xd9\xf2\x0b\xf4n\x92\xfe.[d\x95>\x1d\xc18tK\xaf\xce\xb0\x8a\x08\x1e\xad'x\x12*\x8b\x03\x9b\x1d(e\xfe\x80\xbdS\xb8\x02\x86\xf4\xfc\x9c 1f\xa1 \xb4\xfcn\x0fY\xb1\xe2F.\xe4\xb7y\xb6S\xb9\xd4\xaf\x18\xc1T\x18\xf3Z\x9d\xd5&*\x03\xf3\xda\x17L\xd4P\xbdL\x15\x8f\xc6\xc9\xa5\x90\xc3I\x89\xa3\x17\xd8\xa1\x0d_O?\xea\xd7|T0\x97\xbc\x9c\x07\xccfV\x1cBb\xe4exT\x96\x1d3H\xc5+\xa3t\n\xf6\xb95\xbcX\xc4\x9c]Hy\xc4YnH\xaf\x1f\xf8Vmp\xd2\xb8\x18\x98Y\x83\xedCy\xe6\xfa\xcd\xb2\xe9\xac\xf4\xad\xe4\x8a4\x16\xe7\x1a\"x\x02\xfec\x88:\x1d\x07\xe2qtf\x82A\xad\xc2\xb6b8\x04Z2\xb5\xe61\xdcNlR\x9c\x9f5:8D\x89LZl\xfeY\x97eg\xb03\x17\x9d\x97K\x80\xd8F\xc9\xa7\x8aM\x9c\xf9\x11 \xe4\xbf\xc6\xbd3i\xf7\x9a\x16\xbensF\x95\x1b\xd7:\x899)}Y\xb8Ap\xc3\x0d=\x861\x8a\xce8\x13'gm\xcc\x06h\xb9\xeaA\x10\x18\x8dRY\x84,)lVD\xfb\xf5\xb8\xdcJ\xa8\x07\xbc+*+\x91c\x8d\xcb\x11\xdd\xb9\xba\xf7\xecB\xa4\xa2\xc9\x89\x0d\x0eM\xb1\xa4\xec\x8a%}\xceq\xae<\x94\x04\x85K\xbe\xa6\x9b\x1c\xabu\xeb\xefM\xf3\x93\x0eF\nf\xb8\x8a\xaa\x18m;Z\xc4cL\xdb\x02:?s\x95\xa3\xa68eR\x85\xddo\xc4T\xe0f)eC\x13a|T1?)\xdf@\xbc4GP.\xa2\x9c\xeb\xec\x0c\x15=\x14\xe5n\x9b\x00U\xa8Z\xe9.b\x1c6\xf0\xc92\x1dG\xcd\x16q\xdc\x96\xfb\x08\x0fnd\xde\x0d\x16\x94\xca9R(\xe6\xf8W-\xa6{\x15{\xab\x8dN\xf7\x9a\x1b\x80\xb6g\x7fl8\"\xf2\xe3\xc1\x07?\xe4\xa2\x1d\xd7B4\x89\xbd\x94\x9c,l\x8b\xcefE\xa6\xc0\x85\xfb\xb0\xec/!t\xf1\xf5\x92s\xca,\x1f\xda\xb9A\xf1\xb3[\xbe>0i\xcd\xc0x\x8dI$S\xed*\xf2\xe6\x9a\x04\xce[\xe7\xb00&\x1e\x94!!\x84\xd3\x12(l\xbf4G&\xa7\xfa\x14]\xb6B\xc5o$W*\xa3\xa6^\xb2\xde\xf7\x99Ho\xab\x1f`=a\x95\"\xc4~\x9c\x9f\xef0\xa2+t\xe3\xb9 \xa9\xdb\xb2\x0e\xdaLJ>S\x14\xbb\xc6\xfe\x19\x94\xe3\xd2JR\x01/\xb4EE \xa9\x9b\xdc\xed\x1b\xd1K\xaa\x9bR\xe6\x9f\x87\x81\xadM\xe5\x07\x065\x86\xaf\xbb.\xd7qF\xf3\xfc\x8a\x11\x19$D\x82\xf98:\x93vz\xf7\xc2\x0f\xa7\x9c\xba\xd1\xa2\x1a\x8f\x9cT\xf6\xa6l\x86\x8c\x84B\xe7\xfc\xfe\x908\xc2\xfb;\x16\x14\xa7\x10#\xaa\x13\xd5\xd3\x9e6\xee&\x82\x84\x94|\xbb\x9b\xa3\xd8hL\xaa6rM\xd1Q\xd8\xd2\xc5Qu\x8e\xe5\xd9\xa1\xdf\xc7\xf9,\x8e\x96\xf4T\x86\x11\xbc\xfb\xa7\xa2\xac\x1c1\xdb\xc50\xd8\xed\x02g\x97bpW\xa3M\xb4iB\x1fNc]\x84\xbaz\xa4\x8dI\xeakO\xea\x1a%\xcb\x8dv\xd0\xe5\xcf\xb9\x1bK\x0b\xbb\xa3[_\xf5@\x93\x1bQMd\x01\xfc\xac\xa2\x9c\xd6\xbc.Z3\xee9t\xb2\xce\x98\x9b\xde\x01\xfa\xe0\x14\xc6\x9b\xed\xfbA8\x97\xb8\xd9\x9c\xe7\xf1\x85\xb8 |,\xd0Z\xc7\x00\x91F\xcf&\xe9\xde\xb420\xbb\x16\x02\xe5\x8f\xf9k;\x8f(\xee\xb6Ppo\xf1$\\\x07\x94-\x97'\x18\xb2\xd9\x85\xbaA\xa9/\xcb\xb0\xc2A\xe1\xed+\x9e\xccZu\x96A\xcc*\xfd\x99;d5\xd0\x92[\xc3\xbd\xafg\xef\xe2j\xf4\x85\x8a\x0b\xcd\xb4\xb6\x05%\xaa\xc3\xe7,o_\xfb\xadf\x04\x95ru\n\xe5\nL\x95U\xdf\x86\xb2\xa8\xaaO\x95B~>?\xf6\x9f\xec\xa4\xc8\xb0\x12#H\x84\xec\xd4\x9a\xca\xe1\xf0\x13\x12\xcch\x15\xfc\xf7\xd3'\xb8\xf2\xc3itU\xa5/\xbe>\xb272\x12&_&}\x00\x7f\xc81\xcd\x9f\x16\xaeS\xdds4\xc4~\x816\xc8\x06\xf0\x00\xf2\x9a I\xdf\xf9K\x12eiK)'$W\x10\xd9>;\xc0\x8a\xaf1\x1cB\xc1\xff\xb8\x80\x03\xe0\x85\x15\xb5\x05\xf6\xfb2LI\xbc\xf6\x82[v,>\xd7\xf7,J5]\xcb#C\xfdK\xe9\x83F\xf1\x873\xf9\xa8\x88\xad&\x96\x8fJ\xda\xd2\x98\xcc\x94\xec/\xec\x8d<_\xe5#l\xb7 $\xa55f\x10\x89\xdd\x1c\x0f4s&a\x1c\x05A\x1b\xfd\x90\x0c\x1d;\xa5\xcd\x05\x84\xff\xf9r\x8a\xd2\x87\xfc\xaa\x8a_\xb4\xb7,\xd4\xf4w'\x9d\xa9\xd6p\xb4\xb7s\x84\xf3\xe1$\xf5\xd7\xe8'\xda\xf5\xc4\xcf\xcf\xe9\\\x7f?\xc8/R\xa5\xaa\x1a\x8dV\x91bQm\x15FPl\x99\xe6\\ri\xf7<\n\xc5\xe4\xd9\x9dD\xfe\xb7\xee\xb2G\xe3q\xe5bD\xab}G\xec\xb9\xe5\x92L}\x16\x9b\xa5\x99\x84\x95\xbfP\xb2e\xb2\x01\xa95(\x0e\xe6\xac\x8b\\\x98\xef\xbc\x0d\x87\xa0|\xa3\x1dD\xb5Ni\x18\xe5\xe2\xe2|\xb8M\xde\x9a&\xde\xd9\x14P\xcdGU\xa2\x9f\xc8Q\x88\xea\xd1S\xd8#\xe1\x8d\x82eA\x07R~\xab\x99F\xdfDW,W\x8em\xb4\xfeF\x13\"kA>Zz\xd3\x1eV\x8eq\x90\x1a*l\xd7\xd7\xf0\x92\x89\xef\xd7\xd6\xb8\xf0C/\xbe\xae\xaf\xe2%d\x7f\xb7~$\x93d\xd0Ta\xbb\xa1F:\xeb\xef\x07\xa4\xa9\xcevc\xa5\xd8\xbb2\x94\x83\xe4\x9fm\xc8+\xd9hq\x95\xfbwWwxys\x1b\xf2\xfc\xe8\x18\x19Ee+\x90\x0b\xf7\x07i\xeb\x07.(`3\xff.\xae\xa3\xf8T\x18\x9e5\\\x03\x91\xc7\x8f\x9db`u\xca\x97F\xdc\x85V\xf8+\x9e\x16\x83\x846h\x08\xadP\x11Z\xa2#\xb4EI\xf1H\xd3\xc0\xdaM3 \xbc\xd4\x0f\xfb\x8d\xbd\xd7\xee^\xf1\x88\xbey\x9bM]\xd7nwhEZ\xa0\x05\x8d\x13\x8fP\xe9\x98\x87\xd5\xb8'A8X\xd4\x87\xd8\x12\x0f\xa5\xd96'\xdaez\xcdbQl\xf5\xb4\x9f\xeb4\x84\xba{I\xbc/\x13\xd12\xb6\xca\xc1\xc5\xed\xd213\x1a\xf1X\x85,\xbdQ\xd5'\xc4z\x1f^\x86\xd1U\x08\x82\n\x0c\x81\x0d\xdb\xa8\xc7`\x07l\x99\x12\x15a\x1d\xf2\xb8t:\x8e\xab\x05\xdac#)\xf9(\x92\xc6\xb06)\xe74a\xa0\xd3Dh\x04\xb3\x89k#\xa9\xc0\x0ef~\x10|\xe3\xa1\x96\xce\xbb}/\xb5X-\xcfkV\x9aW\xc0z\xdc\xd9\xa8\xc7Z\x84\x95U\x98\xcc\xfek\x04+\x96f\xdc\x96:^\x98$g\x10\xe3\x0d\xbc$}MP\xce\x16\x81\x11\xe9\xabwQ\x8a\x82\x92\xfc\xeeh\xe11\x8f:\xd9\x1b\xb0\xa4\x0c\xcc\x7f\xe6gUV\x13\xd6\xfa\xc9\x08\xfa\x83\x07\"c\x03<}\n{0\x1a\xc1>\x1c\xc0@\xbc\xd9\xa5o\xfa\xbbp\x00;\xe2\xd5\x0e}\xb5\xd3\x83\x03\xd8\x15\xaf\xf6\xe9\xab\x01\x1c\xc0v\x1f\x86\xb0=\xa8\x1d\x92g8>\x852\xb0\x98\xfev\x19DU!\x7f\x13\x07h\xb4;\x19<\xa4{\xd9\xee?\x1a\xc0=L\x0f\xebH\xb6L\xe5\xa5\xb0\xfe\x9f\xff\xeb\xff4PY\xf40*\xaas{A\xc91\xac_w\xb4\xea\x06\xd27\x0d\xa4_;\x10\xd0\x0df\xa0\x0c\x06\xffV;\x1c\x98:\x1c\xf0\x0e\xdb\x13O\xae\x0f}\xacC2I\x90\x08\xd1\xbd~\xa8`\xfd\x13\xc9\xd7\x0c\xa3y\xa1Wf \xe5qY\xe5}@?t\x94}\x91\xa7l+\xf3[nuS\xb1\xa8`\xb5\x1d\x89\xcb4y?\xe7#\xde\x96\x02\xa0\xd5\xef\xbdD\xab\x01\xa0\xebe\xa7\x85'\x10q0!\xf9\x08\x1dWjt\xf2\xc5\x0cs\xf2n\xb6\"\xa9\x0f\x03\x80\x97\x91\x93\x85\x17\x1fESr\x98\xda\x92\x07\xac\x1aWZ<\xb4\xd1\x98J\xdd{{\x83G\xfb\x80f\xf9OF\xb0\xb7\xbf\xd3\x7fT2\xf8Rp\xa9B\xd0v\x95\x85\xe3)\x9a\xc7\x12D\x06gj\x9d~\xa5N\xff\xcc\x85\xb0pS\xd7\xe6\xd9\xae\xbc\xd1\x9bxh\x89\xa32\x93\xbef&\x83\xe6\x99\xf41\xe5\x85v\xe1\n4C\xa8\xd7\"R]\xaa:\x90\xef\xc3\x0f\xa4\x03\x89]~X\n\xe5@jQ\xdaH\x0d\xf7@fr\\\xc3\xbdtL\x9bS\x82@\xaf\x1a\x0eL\xb7\x12\xa4\x1623\xed\x16\x13\xe3\xafl\xb3\x1d-\x91\xeaq_\x93\x83\xd2ZqV\x83\xbb\x9d\xd9*F\xec\xc06\xde\x94\xa8X\xb1#\xec\xd1B\xb1\x1a\xb5\xf8Qj\xfa\xb3\xf6\x83\xe3\x1a\x86_\xc2\xb4\xb0\x81f\x05w\x87j\xda\xadtP\x8b\x1d\xf9\xa0{.\x02X\xc1\xd4a\x036\xac\xcc\xcc\x8e\xe1|\xa8\x07\xc6\xa2\x86yj\x82\x85\xd4\xb0\xf8E\xca\xd1\xdcX\xc6\xc7\xa8d\x1b\xe4\xa7\xf5\xc2\x7faq\x9b\x9fA\xb9`\xa8\x80\x1f\x97\xcdU\xdd\x9e[\xed\x7f\xbfHB\x87\x9e\x989k&\x98x&\xe7\x18:\x06\xd9\xba\xf12u\xbd\x84\x02>\x1e}\xae\x9a\xdeJ4\xb2\xbd\x8d\x83\xa1\xab\xb7=`bv\xdd\xc0\x90\xb1\x92F\xe6\xb4\x1e\xc3\xe0\xf7\x1f\x03o\x0bC\xef\x8cD\xca\xbc\xf2\xa8v\xf4\xa3\x12\x9d\x97\xb7\x8f\xd9\xb0\x98\xe9 \xcb[\xbeJ\x15E\xb8~\xf5\xeb\xca\xf9\x16V\xa9\x8c\x1c\x9e\x01\xb6\xc1\x0e+\x94[\xbf1\xb4,x\x8f\xf9M\xeb\x86FKL\x1bFR/\xd4S\xcf\xf2v|\xa2!\xa4\xfaq\xd5\xf3Bw*\xa0(+p\xeb\xe1\x14bLy\xd2\x92\x04\xa3\x9cR\xb7\xba\x99)e?/^\x17\x176\x035y\x1f\xcfq\xae\xcf\xcb\xac\xd1\xae#\n#\x04J\xd9T\xca9\x13\xa2j\xda\xf0\x92\xc9}n\x8b\x91\xc6^\x98\xcc\xa2x\xc9\x8c1tn1\x18\x17\xfc\x9d\xa8\xd7\xc2r\nT\xaeY\xe9E/T\x85\xdd\xbcV\xbd\x1fG!\xb5\xe1y3\xb90\x0bi[qY\x1c3\x06\x0e`\xcc\x06\x85\xd0\x857\xb9\x14qj\x96Y\x90\xfa\xab\x80@\xea/Ib\x8cw/\x06\xb2\xc8\xc2\xcb\xdcG%\x1f]\xf1\x86\xa7\xec*L\xadx\x1aWW\x93O[<\xe2\x80apl\xe1}\xe0+\x86;\xb6_ k.\xecc\xe1 \xf8\x9a\xa8\x1bEW\xb6Z\\\xe9\xf1\xa6\xb0\x01\xd58\xdd\xd1\x8e%\xc4\xd1\xd9H\xcak\xae\xaf\xc1\xc1\xc8\x82]\x98\x8a)\xe8kk\x14\xdafZ\xa9|\\\xe8\xad\x97t\x0154\xd5\xa4P\x1e\xb5\x89E\xf2\x89J\x06O\xc5\xbb\x91\\\xc3\x9cgd\x16d\xc9Bj\x80\xfd=\x12%\xc2\xe4\x1e\x0d\xb6W1\xc9\x1d\xf5\xb2&\xbd\xa8\x8e\x9d\x12\xbe\x18e<\xd3\x8fL\x1a\xcd\x81\xfcW)g\x9a\x96\x19\xf3r\xdaZ^\x14\xcaDz\x9c\\\x15\xfb\xa7~\x1e\x9e\x89\xeb+\xdd\xa4hLH\xabLB)\xb1`Z\xc4\xba\xaf\x84 \x10\xe7e\xe5\x9e\xe3\xc8\x0b\x02\xba\x0d\x8bE\x9eF!\x81\xab\x05 \xe1*\xcf\xa8\xb45\x82\x9e\xa5\xe9?U\x89f\x89:n\xd8]\x92\xfaAP\xdajj\x979d4\xbe\x00\x85\xcc\xe6W\xf2\xaa\xb9\xd2;;b\xdcJ\xb4adw\x99@\xab\x93.Q\x90\xdc\xe9\xa9\xdc~\xc5\x97\xac\x18yy0\xa5\xfd\xd6$(T\x00\\|m\x080c\xec\xb6*\xc9\xea\xbb,{\x9a\xd5\x9d\x99(\x9b\xc8\x07\x0c\x85J\xe9\x10J\xf37\xd2m;qa+V\x10I/\x1e\xb5>r\xecXY#<_\xbe\xd0\x89sc\x04\xb1\xeaYP\x7f\xa9R\x0b\xdb\xdc\xe7\x84\xc8\x10\xc5[\x04\x01p\x16B\xb8\xc4\xae`\x0c&\x95\x81\xe9U\xb8,[n\xd4\x15M\x16\xfc/\xe9\x96\xb9-f@\\\xdd\x06=#$Z\xe6i\x90\xf93\x95Q\xac\xb6\xa6l\xb1z{\x0c\x96{=\xe4D\x969\x90\xab\xc4]!.\xb7b\xb5%\x9eZ\x97\x89\x17sH\xcaBQ\x14\x1f{\x93E\xb9\xa2\x94\xe2|\x12\x93\x12.\xb4K\x8b+\xf0*bDSKU\xb9\x0din3\xda\x04@Lgz\xef\xde\x06\x8c\xb6\x9e\x15DK\x97\x10\xbd\xd9\x1c \x18\x04\x10\xd2qxV\xa9|c\xf3\xb4\xb8\x18\xc9X]+\xb7\xa4h\x84\xdb.\x97\x16\x9e\x0e\xfc\xfd3\x9a\x940`\xc7iZ93\xcd\xf5\xf5\xab\x96\xbc\xf6^\xdb\x98X\x16\x95\x18\x84\xa9/\xf0\xe2\xee\xde=\xae\xad\xd8\xc6\xc4\x0c>\x86\xb6\x1e\xe6\x8e\x95x#\xd4\x9c\x1d\xb9\xd5\x1c\xcb\xfe7\xbb\x0f\x06\x8eM\x87\xc4\x91\xd6K\x12\x7f\x1e\xc2\x10\x8bv>\xd7\xa2\xd0\x05\xdf\xc5Tr.x.\xcf\xe6:P\x13\xa4N\x9aH\x0b\xe8\xee+\xe8#\xe7\xcc\x8f\xaf\x95\xaf\xf4\xaeY\x13\x17x\x08@\xad\x07\xd6$\ng\xfe<\xab\xc9$.\x985\xbdl\xd1\xe4\xc1\xb5\xf6\x82\x8c\x0cA1\x02\x96\xd6\x15&^n>V\x9cN\xec\xcec\"]\xe5\xc6\x15\xc9\xba~\xe8\xe6a\x97\x87\\\x8c\x84\xc55\xd4B\xd1\xdd8\xa12\xa5h J\xa6\xb9*k\xc4s\x06\xa60\xa4\x87>B\x86\xb1\x14\xe8\xa7U\xacR,_\xaa\xe0m\x11\xcfn\xfc\xe8\xa1\xe3b:\xd4\xf1\x19\xcbl\xdd@U]\x9d\x02\x9cr>\xde8=\xcb\x99y\xfaG\xb9\n\x92=\x82\xfd<\x86t{\xfb\xb1#|\\-\xcf\x82\x0e\xd8\x9dN\xe8\x14\x1a\xa8\x9d}U\xae\x97\xf4(\xc2i\xc2\xb6f!K\x98\x8bE\xb9\xc4a\xd3\x06 \x0fq\xef\x82\xe5@\x87\xfe\xef\xef\xa2\x8dY(\xbc5\xf1\xec,\xdc\x06\x1e\xc3\xcd\xe32\xcb\xd8z\x8d4\x14\x1f\xe5\x1b\xc3\x9a\x15b\x8f\xc2\xe7\xe0\xa9E\x9c\x8a\xea\xa1\xba7\xe9\x93\xd9\xe8\nU\xde z\xf4\x07\xdd\xed\xf2\xcd\xe7\x12'&r\xe8\xb2\xad\xeb\x91\xbeTM:\xe7\xe7$}s\x15\x8aj\xcfI2\x89\xfdU\x1a)\xf6\xd3\x99\xe9\x83\xd7\xdeR\x0dh\xe2\x99\xea\x9e^//\xa2 iq2i\xd7\x98\x91`~4\xc76Q\xf1\x14\xe5D\xb9\x06\x86\x18\xc8\xec\xc4\x11\xccN!~kC\x0d\xeaW\x1a\x9b\xb6\x99\x87M\xc4\xc2\x14j\x14?\xf2\xd2k\x9b@\xee\xb2\xfa]\x19\x81L\xaa\x0e\x0f0\x82\xdb\x7fY3\x91\xed{r ]/g\xffS\xb9\x95\xcf\xdc\x15}\x1d\xff\x1b\xda\x0fUUs\xa4w\x03\xa3\xdc\xe9mq\x94\x9ek\x9a,xt\xfb\xe4\xc4n<8\xd3B!Fj\x85\x0b$w\xc4\xd8\x10O\xb7\x1a\xe18>C\x07'\xe1H\x91\xa1<\"\xbe\xa8\xacH\xd8\x00g\xb9\x8fv\xfc>\x1f\xfa\xd6\x16W\xf6\xb1\xf0\x03\xe5\x14r\x9f>\x19\xb4d\xc8\xd5\x9b\xf4\x83\x0b\xd24\xdaVX\xa1\xe7\xa3\x88\x0b\xd6\xf99I^E\xd3\x0c\x0dN\xd4\xa5D>G\x16+Yt!/N\xc8\xf7\xde28BnE\x93\x16\x7f]D\x88\x0e\xed\xbdAO\x83q\xc8\xfc\xb0\x80\x0dq\xb7\x18\x04\x1c@\x0cC\xcd\"\x0bSS5\\p\xd1\xa9n`\xb5\xa8\xaa'\x0f|-#\x91\xe3\xaf\x9bx3\xf2M\xe4M+ \xacjID\xce3\xb1\xd0\xc8q|\x88\x03I\xba!\xb9zG\x89@x\x1c\xc7v\xa1IB*\xad\x1c\x97\x1bz\x916\x11\x84\x9d\x87\x06q\x88\x8e\"\xb6\xcbs\xf0\xc3I\x90M\xc9\x10\xc6\xa1=\xe8\xed8g\x12\x12\xfcC\x07\xd3\x1f\x0c\x9c3\x85\xb0-W\x81?\xf1S,\xdf\x1b<\xc0P\x06{\x83\x87\xfc\xdfG\xec\xdf\x9d\xde\x1dM\xe2N7S\x10y\xcc[\x99t\xdf\xbd\xf9\xea\xabo\x8e\xcf\x8f\xde\xbc~\xf1\xf2\xabS|\xf5\xfe\xed\xf3\xc3w\xf2\xab\xda\x9d6\xe8\xed\xfdN;-[M\xbd\xaa\xf6\xd2@\x165\x07\xf3\xf5\x8a\x0c!\xab\x9e\x10+\xef\x9a\x02d\x08v\xcf-\xb6\xa0c\xff\xfdF\xd5\xe2\x02(\x9a?\xd2M\xa3\xf9<\xa87\x0ej\x18\x91&\xabJ>\xa2\xd4\xd4uy12\xfd\xbaYL\xb2K\xce\x19\xe4\xac*\xaf\xa8Y\xff\xfc#63K^\x81\x1cod\xad\x89n\xaeU\xad\n|\x1eA!2\x12\x8dJ\x0ef%l\xec\xef\xa9\x0c\xc8\x97\xc2F^\xa7\x85b'\xa7\xca~\xc8\xe2:\x94\xd1\x8c}U\x1d\x04\xdf\xbca\x83\xae@\xa3i\xd8H\x17\xa1\x18\xac\xa0\xa9\x16\x8b\xde\x19\xba\x9br\x87\x94\x1a\x10\xf9\x1c\x18\xdeQy\xa1\x8f\xb7\">\xdd\xd1\xd6%\xb9N\x90\x91&\xdc\xa3\xc2\xc2\x1d\\\xbc\xc3\xe47C\x16\x14w\x1c\x9e\x9d\x95t.\xa22\xdeZ\x1e\ny\x05%\x0c\x0e\xe9\xd8f]\xa0\x91\x86T\x1d\xc3\xd0\xa7\xb1O\xff\xd2\xe2O\xa3haT}7~\xb9\xd1\x01\xcc \x9a&\x18\xde4\n))\xda2\x1ew\xb7\x1c\x9d:4\xbf\x1cJyK\x96\x87\x98\x90\xfc\xeezE8o\x0c\x1d\xb0\xc4\xed\xaa\x977\xbae\xba\xafn\x18\xec\x86\x9b\xf8\x91~\x0f\xef\xedj\xb7\xf0#\x95\x05\xcbP\x18.\x1a\x0e\xed\xc1\xbecg\x94\xf2\xec;\xb6\xe5\xa7$\xf6\xd2(\xa6\xe8\xd3t\x94\xa7r\xf0\xb2\x1b\xa7F;\xa8\xbb\xba.h&\x8c \xa6#\xa8\xe2EH>\xa6t\x13i\x12\x91\xd3\xdd\x80m\xe3b\xbc\xcc\x87\xbd\x19\xb0%\xf5\x84\n?N\x1a\x1fh\xc1\xba\xdb3\x93\xc0=\xe9\xea\xa3\xc4\x94\xfb$i\xca%\xe8W\x14\x9dEf-\x17\xd7.B}\x04\xe5\xd02N\x81\x98\x06\xae\xf7\x18\x85\xbd\x07;\xbb;\xbc\x7fV\x1f;\xa2\xc8\x82\xce\xdf\xf4-\xf3\xc2L\\\xecd@\xcb2\xd8\xe6\xcdt\xe88\xb7\xf9\xa0\x9e<\x81~\xcf\x81\x0e\xec\xef\xed\xed\xec\xdf\xcd\xa6\xaf\x1c\xa9\xfc\xe0\x18\xf4\x8dg\xea\xc0\xe9\xceI*\x0e\xf9\xe6[Y\xa4\xf3\xeaIjd\xf1H\x03\x8b\x87<\xd1E@L\x0c^l\x13n{\xe4\xdcz'\xf6w\xf4\xd7#\nOV\xa10(\xa4\xb5\x03\xdb+\x92.\xa2z\x034\xc9\x8dl\x0b\xa3\xcd\x0b\x9a:\xf6\xcf0\xc0\xc5\xd8\xfa\x97\x7f\xc9\x87\x83\xaf\xa21\xa5Ng\x9b\xcd\x9b\xae\xf6\x0eJ\xbb\xfd\x1d&\xf5\x0evv\xf9\xbfLM:\xd8ej\xd2\xc1^\xaf\"\x0e\xf7\x1f9B\x14o\xd3Y#C\xad\xc3G\x99E\xf6\xc7\xa1\xddwlK\xdc\xc6\xbf\xf3\xe6\x96s\x06#\xb0~\xc1L\x8d\x1d\xba\xcf\xb7F`\x8d\xd9E\x0b\xfcrf1\x1d\xc1N\xcf\xe1VK\xa5\xe8\xbd\xa2\xa1\xba\xb0\xdd\x1c\xf2y\x9b\x16t\xe89\x80\x01L;`\x9d\x95\x9c\xe3\xb6\xda\xe9\x07d0n\x85\xf6\xee\x80%G\n\xed\xdd\x1d\xc7\x1cx\x8d\x8f\xe4\x01\x9d\xa2^\xd7\x1c\xda\x8f\x1e9\xb65\xf5\xd7Tl\xb0<\xad\x19\xccF\x81\x86\x1fT\n\xd5\x9b\xcc\xaeW\x00\xa0\xd5\xe4%]\xbf\x89\xd0\xd4\xb3\xe6\xe8\xaa\x81'\xb1\xdeV\x813\xe9~\x95\xea\x10\xd3\x95\x9a]\x8e\x13\xc0\x96#\xe6\xb1\xc7\x05I)|\xd1j\xe9\x99\xda(\xca\xd4of\x9b\xb7\xb9\xf5e\x86\xab\x92X\xeb\xc8\x0b\xff\x94\xc2$\n\xd7$N\x81\xa3y\x1a\xc1*\xf6\x97>\x06+\xc4)l*\xd25m\xf7\x81\xe1\xfc\xe9\xef\xe8%\xe8~O\xe5_\xaa\"t\xff\x01\x17\xa1\xfb\xff\xaaE\xe8\x87\x86\x83]}\xcf\x01\xbb\xab\x03,\x05x\xcf\xb1\xad\x97\xc7\xe7oO\xde\xbc{\xa3\x1ez\x9e\xaa\x9e*\x17\xab\xda\xab\n\x15U\xba/F\x8c>?\xf9\xe1>/b9FxXV&\x1e\xa7\xdd\x17\x8f!F\x8b\xb3) HJ\xe4\xac7\xe3h\x1c\x9fir\xa6\n.W\x8d\xed\xaa\xa7\xa3%c\xe5rP\xc7v\xa6b\xbc\xbb\xdc\xca\x1d\xefF<\x05\xdd\xd1\x80\x1b\xd8\x0d\xad\xe7B\xb9\x98{\xe3\x8c3\xb4'\xc6\xec\x93hzVX\xc0\x8c$}\xac\xcf\xb2\x19\xdf\x16\xf1\xf7\x0c\x14\xc5\x80\xf75\x1c\x1b=\x92\xff5(\x8f\xf6\xf4\xa4b_wEG\x99\xc2\xbeco\xb5\xa3\x16\xb78\xd99\x80<.5T\xe9\x00\x82\xa8\xfaz\xc2\xcc7\xab\x10Gsv\xcfaJ\xa2\x8c\x19Z{\x08\x8b{\xf7`\"\xfc\xb44\x1f>\x96\xa3@\xe1j\xe0w\x94,\xe0Z\xb0d!\xff.\xb2'\xd8\xda\xa7OEk\xfa\x05\x9a\xdcv\x81vM<\x12\xb7\xe3\xb3~\xb1\x1c\xba\xe1\x90\x01|\x99\x1c\xe7\xf7\x8ev\xaf\xc0\xe0\x12\xc2\x9a\x18\\\xce\nS.#f\x96\xec)&\x10Km\xcb\xa2\xfb6\xb7\xfa\xbf\xedT*H\xc5pmWg\x9c@ \xb6I\xb5\xdb8\x95\x92^\xe2\xdf\xf4\x94\xff\x15\xe9)\x0d\xe4j\xb0\xa3\xfa\x1dD-8\x18\xc9j7?\xb1j\xcf\xd19I\xdf\x8a\x8aof\xf5A\x92s\x90pZF\xf7\x94\x0b\x11n\xabqt\x06C\x93i\xdf$\n\x934\xce&i\xc4r\xe3\x83\xe4\xb7_.=(\xff-\x1d\xbb\xc3\xf2g\x9c\x08\x1c@\x06\x8aG\xf3\x86\xe0\xef\xdfzK\xcaV\xc7\x9b\xf5\x9e\x1f\x9d\xc2w\x07\xfdH\xf3\x03\xdc\x15\xda\x97\x9e\xe3\xf2\x93h\x8f\x1f\xad(\x0e\x08\xcf\x94\xdd]\xc7\xc5\xfdLe\x03\x177\xed\xa4,\"\x04\xecUI\xb9\xc0\xf2\x82'\xe2~wQq\xcc8:==\xc9XN\xbe\xaa\x19\xc7\xd1\xe9\xe9)eH\x9f\x93I\xe0\xc5\x1e\x9da\xd5E\xe3\xe8\xf4\xf4\x03\x15\xafx\x13ji\xe0\x930=!\x93T_\xfe\xfc\xcd\xab\xdaB6\x17c\xf1\xbb\xe8\x92\x84\xfa\xc1?\xf7R\x8fy\x11\x92\xf8eJ\x96\xfa6^\xf8\x81a\xe4\x7f~\xf7\xea\x9b\xc3 8\x8a\x82\x80L\xf4S\xa7U\x9a\xca_D\xf1\x92k\xbb\xf5\x15N \xfd\xdeX\xe5\x15\x99\xfa\x9e~\x86\xaf\xfc%\xa1b0.n\xf5\xcb\xd7\xde\x92L_GS\xf2\xca[iJ\xa3\xa9a\xd5\xdfz>]\xb1\x9f3\x92\x18\xd6\xe5m\x90\xcd}\xcd|\xd9{\xc3pN?|\xf5\x0d\x1eC\xfa6O?|\xf5:[^\x90\xd8X\xfc\xd6K\x17\xa7\xc4\x80\x0b\xb4<\xf2C\xc3\x80O?|U\x87H\xa7\x1f\xbe\xca\xfdM\x0d5\xa2,\x9e\x10\x16z\xdeP\x83n\x94\xd3\x05!\xa9\x1e\xaa\xef\xc8\xc7\xf4]\xecM.\x8fL[%\xafa(\x8e\xb2I\x0e\xbb\xbc\xe4\x86\xa5\x0b\xf7m\x0cY\xc98\xf05<\x81\xa9\x904a\xdd\xe9\xe8\xf8\xd4k\x17\xe60\x82\xe9x\xad\x18\x9d\xd2g #X\x8c\xe7\x9a\x92sd\xe7u%\x170\x82sJ\xf1\xcfu\xa7\x11\xf0c\x18\xdd\x89\xed\x0bz\xf6~\xfa\x04\x9e}\xe1\xc2\xcc\x85\x95\xe3\xc2\xc58(\xde\x05,\x07s2\x9e\x9f\xb1\xe8\xbaK\x8d/\x03R\xd6kz\xa2\xc7\x0e\\\x8c\xaf\x99\x1a\x99~~\xedB<\xbe>+\xf4\x99\xd0\x96Z7*}\xb4>9\xf4\xbd\xe1~_\xd5\x05e\x82\x954In\xfd\x9d\x07\xfff\xf9\xf4_\x8e\xe5\x93\x99\xd7pl+\x0b\x93I\xb4\xa2\xd2L\xa22o\x1a\xa7m \xdf\x84f\x01\xfcq|\xc6\xae\x00\xfa\x0f\x1c\xdbG\xef\x8f\xbf\x9b\xf5{\x15I~\x1c\x9f\x8d\xd33\xc5\x89^;\x11\x93~\xbf\x16\xf5\xf8\xa2\xea\xc4\x93\xbb5\xc4j\xbfMe\xb7^\xbe\xa1T\xa6;\x11lV\xe9-c\xae\xf6U\xab\xa8\x19\xbe\xae\xdc\xed\x04\x8ckS\xde\xae\xd8[U\xc3\xb0`M\xab\xaf\xa7\x9ct\xa8\xd6\x91k\xf6~W\x1d\xca5\x17,\xd5^\xe7\xfc\xfd\xae\xd3M\x88\xb2e\x97\xbc\xad=\xc7V\xbe:\xe7,\xb1*\xd5^\xf0\xd6T\xf8\\\xf1\xf7*\x01\xfc\x88\x1cf\xae\x8fW\x8eE\x91\x0c{B\x12\xc5\x91\xf0\x18\x8b\xf8\xfd[\xb9\xe8\x10F`\xf1\x8fp\x87\xcf\xecS\xa5\xd77\xf5\xea\xdb\x9f0\x92\xde\x08\xce\xbb\xb3r\x01\xa5\x84[[\xf5\xaa]\xb3\x7f\x9d\xa0\x8e\xc7\xdd\x98$Q\xb0&\xb6\xba\xa6\xf2CX ZY\xe6\x19\xd1\xdd\xcb\xaf\x01\x93\x15\x99 a9\xab\xdd\xc3\xea\x93\xdao\\xc\x96v5\xd9\xfaA\xb2\x0394zl\xf1\xa58!?1\x86\x163_\x8a\xac8\x0b\x12\xdao\x1cY*\xab\x8a\xe55\x1e\xb27*\xf6\xbdl\x9c\xf3\xba\x9aX\x05\xa4s\xc4\xde\xc2\x98\xaf\xe5\xc9\xe4w\xf1,p)\x0e\xdb\xc1)\xa8\x89\xb4J\x7f\xbej\xa2s \xae\xb4\xd2\xee\xb9Q B\xcb\x14\xc7\x01\xf9Y\xe7\xe1\xbc\xcf'\xfa\x1a\xcb\xe6\xa4U\xa0J\x94i\xf7|\xcd\xe4\xc9>.e\xf7\x1c\x00\xe9F\x97\x18\x94e\xe6\xf9\x9ahc\xea\x93\xe0\xc5\x03\xdf\x1b\xcd\xd5'\xbc:E\xb8\xe6\xda3\xac=\x8d\x96\x9e\xdf\x94 \xc4\xb8\x81\xe5\xc7c\xc1.>}b19)\xec0\xdc\xd8[\xc6E\xd1\xbfF\x18\xa4t\x8b)\xf9=d=Fh\xedoc\x0e\xadY\x97\x84)\x89m~\x81\xe0\xd91\x8a\xe6\x94\xc5\x9du\xc9G?\xb5\xb9P\xbf\xd5sX\x1d\x8c\xb4\xb3\xe2\xe6\xff\x070\xb1?\xda\x16\xdfw\xdb\x93\x85\xe7\x870\xb9\x9e\x04\xc4b\xa1\xea\xe9:\xbe\xb4)\x06\x1f\x087\xd0\xd0\x85\xc4\x85 -N\xb0d\x08\x13;6S\x03P\xf7e#Xp\xfc[\x19\x9f\x1f\x9f\xc4\xc4\x94f[<75\xf4\x08\xc2B\x19\x1d=v \xb3\xc3q\xd4\xe9\xe8\"\xc8\x8a\x87n\x12\x1e\xe1&p\xd4p\xad\x9a\xde\xde6\xf6\xb6)\xfe\xea\xb1QF\xac\x1c\xe8\x7ff\xaba \x9c\"\x1c\xa7\xf2\n|\xb9\xd8)\\\x83Rm\xd0I\xa0\x12\xddS\xad\xb7~\xedJ\x9d4\xc2n-\x05S\xab\xc2\x85t\xcf1S\xb4\x8d?X\x184\x84\x01\xe9\x9e_\xd1\x02\xe2t\xcf\xd7,F\x1d\xe9\x9e',{\x04\xe1+l\x13\x86y\xa4{>\xe1\xc6\x94\xf4\xa0xe\x13\xd4]\xd4\x8e\xfcu\xbb\x91\xbb\x86\xc8g X\x9a\xb0{\xae\x0d\x05\x0f\x18\xec5\x9f\x14\xde\x90\xf39\x19\x8e\xdf\xfac\x17\x03M\xb2\x00\xf6bc\x15\x87\x1fL\xd0\x88\xe7\x82\xeefd\x1e\xa6\xe0\xa7 f\xaa\xa9\xa4\xfc \x9c_\xa2%\xd5A[\xe6 $!\xbd\xf9,<\xbf\xd2zGV\xaaM\x87\xba\x84\x82\xf2c\xe0\xca\xc5\xd3\x8ec\x11\xe6\xa1\xf4<~\x8d\x07L\x1f\xcf\xe6\x13\xfe\xfb.\xd9\x80\x93\"\xf3\xed\xadO~g\x88y\xc39\xfa\x87\x0c\xfd\xfb\x14\xbfC\x17\xb6L\xe3m7N>\xbe\xfa\x89\xb4X\xbf\x86\xb5\xbb1\xce\xbf:o\x85\xc9(V\xfc\x12\xf7\xfaq\xed\x86\x9d\xf2\xa8I\xc7.\x88Ma\xb9`\x9d/,\xc7\xc5t\x14\xae\x1c\xd5\xbaU\x14\xa3\xd4F4a\xed\xe6\x98\"\xfeT\x88K-\xd0O\xca\xf1\xb4\xcb_\xe6\x7f\xdd\xb8\xec\x107O\x92\xa9\xf9r\xce\x0e\xff\x92O^\xf6&\x91U\x97\xe5l\xe5\xebJ\xe5\x85\\\x991\x8a\xc5\x80\x9c\xb2-\x8f=\xd8\xddw\xecc\xd9\x86V\x1d\x1f [\xc4\xfc\x16\xa2\xdcO\xb6\x88uu\xac\x0b\x97-\xac\x8f\xa8\x0c5\xd2\x8a\xa9\xec\xca\x19\xf7\x06\x15\xb0\xca\xb5F\xe5\xd4\x83\x94\x92s\xe9\x07\xd9\x18z\x16\xf3?\x87\nL&R\x08_\x0e\xe3<\xf0\xa8\xa7\x96a*\xdfW|\x1e\x98\xb8>\x14\x12Jy\x9d\xcb\xfb\x08\xd1\xa5\xce.\x03\xca\xd6\x89L\x85\x90\x8f\xd3\x88C\x8e\x12.\xcd\xa4\xa0\xc6x\x1a\x8f\xab\xd8%\xb8\xc2\"];?Q\xf0z\xf45\xc6[\xc8\xb3\xf33&\x05KNx\x89\x8c\xcd\xe7]*s\xfe\xd4\xe6\x828\xc5\x93\xed\x18\x97\x13\x7ff\x94\x83\xe6\xc1\xe9Q\x8d-\x1b\x9e8.\x04v\xd0\xfd\n:\x10t\xbf\xc5\xff\xbf\x80\x7f\x86\xadK\x15!\xdf\n\xa6\xe8\xb8\xf41\xb3&\xb5eZ\xc1\xad\xdd\x1f8\xb6\xfcJD\xa3\xcb\x0d\xddY\xc7\xa7\xa5.%z\xa3\xce\x8d\x82\xa7i\x91\x05\x83\xf4\x93\x8e2\x81\xa4z\xea\xb9\xb9\xb4\xef\xb0\xe8\x9bzD\xab\xc0\xa9\x18\xae\x8dl\xd3\xd6\xa5S;j\\\xef\xa6a\xf3Q]\xd9\xf9\xe6\xc8\xd7\xed\x98'\x93i\xc0S\x05\x92\xf6%\xd3\xd4\x0fv\x1fJV\xf0\x95\xbe\x8f\xbb\xcc\xc0\xb9\x8b;\xc8~#\xa3E\xdd\xb4\xbc h\x9a\x92\xcc\xaa\xeaO=F\xb5L\xf6BxsQ\xaf\xbe\xf1y\x15\xb3\xca&j/\xa9\n::\xd6\xdc'\xcaO\xa4\xb7\x9b\x93\x1f\x8a\xe8\x86\x14\n\xf4YSZN\x8f\x91\xf6zV\xb4\xb0\x82\x11D\x9dN3\x07\x98\xd4\xa4p\x10O\xc8(/#\x81tov:n\xa1-\xa3\x18\x81$\xb2\xfd\x08\x01;\xa6\xacE\"\x98\xf4\xb1w\xc6(\xdf\xf6vFKb;l\xe2\n\x8dB3p4\x97\x9a\xd2\xd6\xbb1o\xf9\xa8\x8bG\x97oG\xddu\xdb\x83%\xf6&\x8d{\xf7\xae\x10\xdd\x8c\xc5\xfe\x06X\xbc9nUW\xbd\xd8vP\xa3\xcd\xd3\x88\xb7P\xbf\x02>[\x81\xd8\xf6\xebV@\"A\xf8\xf3V\x97\x83L\xe9\xa5N\x9dgp)\xdd\x1c\xa0\xda^\n \xc84<S l\xc4\xe5\xb6\xa6m\xef\x97m\xe2\x81\x8d\x9fIN\xb38Z\xdaQ\x83\xad\x0c;7\x07F\x90\xe8ma[[\xd6\x17\x01T\xb6\x8a\xb4\xe3\xaa\x86Y\xe8\xcf\xd5\xf7z~A\x02\x9c\x9e\xd8\xa0g\xbf\x06\xa6\x90\x1f\xb9MP\x85:\x9f\x00\xf10\x0f\x80\xb0\xba\x00\xe2\xd1\x9cj.\x0el\x83\xee3]\x1b\xa9\x1d\xd5\xdczk\xe9\xfa\x9d\xa4\xa9\x90\xc8\xa5\x9e\xcbV=\x00\"-u\xe2\xf4\xa6\xa2.\xe4~\x0e\xbb\xfb\xd2\xba\xc5v\xdc}\x0b\x1d\x88\xbb'5wJ3?\xf4\x82\xe0\xba\xad\xba=\xe3\xb7\xc4~\x1e\xc1\x9aJ\xc2\xe2\x0f\x83\xae=4\xddjk\x98\xdd\xca}q(\xab&\x8d\x96\xd7\xfc3\x8fRGT\x84\x95/R\xea\xf8\xab\xca2\xcb\x8f\xce\x9a\x8c\x8al\x94\xad\xf8\xc2\xe3\xe2 u6\x1a\x96\xf9\xae\xf2\x0b\xa2n\xc5\x7fD\x84?\xd8S\xb0\xf1\xb4\x06\x0f\xd3\xb85\x0e\xd2C0\xd5g\xe0\x86<\xd1\x97\xce\x9eV\xdcB\x87]\x82\x86\xed\xfc\xee\x7fX\\\xc68v\x88\x97$\xcd\xd7\xd2m\xe0\x19\xda\x83\xbd\x01\x8f=\xb7\xc3\xff\xdd-\xc7\xaa\xdb{\xc0\xff\xe5\xb1\xea\xf6x\xac\xba\xfd\x1e\xff\x97\x7f\xbf\xcf\xbf\xdf\xe7\xb1\xed\xf6\xf9\xf7\xfb\xfb\xfc_\xde\xce>og\x9f\xb7\xf3\x80\xb7\xf3\xa0\xcf\xff\xe5\xed=\xe0\xed=\xe0\xed=\xe0\xed=\xe0\xed=\xe0\xed=\xe0\xed=x\xa4\x8d\x9d\xc7|j\xdb\xc0\xa2\x11\x8b*\xbeNQ\x1ep\x13\x8f\xe3#\x1e\xae\xb2J\x10\xe5J\xd1\x94\xa0\x17\xb0\x82xH\x06\xd1z`\x8b\xd9\xb5\xf71\x9eJ\x1e\x16#\x8f\x1dR!\x8fr\xa3M\x08\x9a3\xb4\xdc\xe4r|\xe6\xe2\x9c\xf3\xccPy\xa4\x9c\x8c\xf9\xe9\xc6\xf0\x142\xb3v\x80g\xb9\xeb\x14\x99\xa52\x8c\xa2\xe3Sj\xd2\xef\xf7w\xfb\xfd\xbe\xc3r\xf7\x8a;\x91\x13/\x9c\xf3K\x11R\x8e-\xbe\xf6\x02\x7f\n\x93hJ`E'c2\xab\xe4w\xd4\x04\x9e\xb0H\x9dp\x80\xb1~0B,\x8b\xe4\xd9\x01\xdb&\xb0=b\xe5\x0e<}\n\xfd\x1e\xca\x14\x7f\x84~o\xb0\x0b\x1d\x16\xffS\x97|\xcc\xb4'C\x9eSP\xcd\x9c\xbb\xe1\x8ek\xc22CT -\xa52`D\xec]\xb5\xc7\x03\x16;\xa3\x1b{W\\\x10\x8d\num\x1dnP\xcc\xf1\x18\x8e\x84\xf0\x14\xbc\xc7\x0edl]x\x08Z2\xf6:\x9d3\x07\xe3D\xdc\x87\x9eF\x8a\xb0\x8e\xa2,L\x0b\xe7\xac\x90\xcc\xbd\xd4_\x13U|\xe0\xc1\xf8\"x\xaa\x1ar\xf1\xc7\x8e\xe0\xe9\xd3\xa7#\xe8;\xdc\x9b\xb53B\xc3#zb2\x07\xd7\x90\xbdz\xac\xac\xd3\xef\xa7\x84\xdb\x948\x17 \xda\x9a6aQ\xb3n\x1b\x16\xb5\x9a6\xa2\x8eD\x97\xfa\xd0\xad\x00\xe2\x88o\xe7\x84r\x93\x1d\xea\xe6\xe1DM\x99/\xe2[\x10\xd6\x18\x97\xad \xac!\x15\x92(\xec\x84E\x0b%\xac\xf1g\x11\x07\x93dBW\xc5\x0b'\x8b(\xdeH2\xa9\xe5\x06\xf9b`\xd4z+\xf4\x96\xc4\xaaK\xec\xf9\xd9\xc3\xbf\xf0\xe7\x1b\x8d\xbd\xcd\xd0Y\x9b\x16\xfe\xf7\x05G\x1e\xf8\xe1\xe5\xdd\x8f\x9d\xb7\xfa\xc5G\x1f\x05\xd3\xbb\x1f\xfc\xef0\xf0\x99\xff\x91\xdc\xfd\xc8\xd3\xf4\xf7\x18z\x14\xa6\x93(\xf8\x12\xbb\x956MG/\x9a\xff\x82;\x96v\x95\xf8\xbf\x90/7 \xde\xfa\x17\x9c\x83\x9fz\x81?I6\x9aB\x9b\x19\xf8\xbf\x03\x16mLvZ\xc1\x1e\xc9\xfd\"&\xb3/\x0b\xf8d\xe9\x05\xc1F\xa3o3x\xd1\xea\x97\x06=}}\xb9\x19\xe2\xb7\x1a\xbeh\xf6\x8b\x8f?\xbb\xb8\xfb\xc1g\xbf\x07\xd5O\xb2\xd5\x17\x18\xf9\xea\x8eF\x1e\xda\xfb;\x8em-\xbdt\xb2\xb0\\\xe8\xd7\xd7\x96\xc62\xce\xebi\x15\x9dz\x88\x88GH\x02i\xddE\xa2/+\x1aP\xcf\x90\xe7_\x0b\xc7\xc4\x9c\xdaB2\x9b\xf7\xe1@\xd8\xd81\xcf\xa8!\x9a\xb7q}n\xe8\x8c\xc9\x99P\xd8\xc7\x95X\x1f\x10n\x9a\xd5\x9f\x03\x93\xeb\x14-\x17\x06\xb7\x00g\xecV\xdd.\xa0\x15D\xa3&\x88f%\x88\xc62D\xe3\x96\x10\x95\x04\x88\x18C\x95\xf9\x08T\xf6\x86\x832rX\xe8\xa5;\x03hB\xbc\xf8\xdf\xd0\xf3\xce\xa0\xb9\n\xfcT\x8b\x9c\x15\xcbI3\x98\xc4EFh\xf7wUc=\x10z\x8f\xeakv\xb9\x867eU\x8d\x885A\xe3\x14\xcb\xbb\xb8\x98X\x92\x89mYt\x8e\x1a\xa4is\x1d\x02\x92%\x9a\xd0\x01\xe8\x03\x01@\xd9\xd7f$\\\x8bx\x12\x9d\xdc\xceMM\x86\"\x7f\xbb\xe5\xcb\xa9\xd3\x8a\xa8x8:\xfdgkf\xc2\x9f\xb80\xc1p\xd3\x01\x0b\x8b_\xe7u\xbe`\xa1;\xfdy\x18\xc5\xe4\xc8\xc3`}\x96o\xc1\x90\x1ey\xd0\xa1e\xcb,H\xfd\xc0\x0f\xb1hY*\xcaB\x1f\xaf\xda\x0f\xc0\xcaJ\x05I\xeaO.\xaf\xe9\xfbk\xfe\xde<\x84i\xbd\xd3\xfb\xba\xbc\x9a\xb4\xb3\xdd\xc1\xa3\xddG\xfb\x0f\x06\x8f\xf6\xd0\x8e\xff\xe9\xd3\xa7u\x0d`4\xd9b\xbf\xa7\xdd\x04\x83\x9c\xbb\xb0\x80\x0eXs\x93\x85\x00\xaa\xfaX\xf0\xaa\xb8\xdc\x02\xbb\xcb\xbc\xe6\xed\xd0F\xfe`\x1fl\xfd\xf0C\xe2X.,t\xd7\xd0\xf9\x83\x0e\xec\xd7\x0c\x17y\xc0\xce-\xdb\x9e`(1\xd4*C\x07\x92q\xef,\xc7\xf0\xa70E\xad\xe1\x8aG3\xe1*\xa4\xa9+>p\x1c\x17\xb6\xd0h\xbf\xa4\xe0\xc2\xc4\x1f\xbd\xb3\xfc\xe2-v\xebY\x9f\xd2\x83S\x0f0\xd0\x00\x04\xf0\xa4\xaa\xe4\xde\x86\xc1c\x08:\x1dG^\x99B\xa3\x16\xa0\x15\xaf\x8d?FZ\xe5w\xe9\xb9q\xdc\xea\xe098\x9e\x141\x15\xf1\xf2\x9f9\x00\xad\xe8\x07\x0c\x12}\x87g\x89\x90\xc0\xc6b\xc5O\\X\xe5\xad\x8e`\xed8\x8f\x1d\xb8\xee\x06^\x92\xbe\xc4\xb6\xf1>\x83\xf7s\xef\x9e\\\xa4\xc6\xf4\x16\x0f\xdf\x8cSv%S\x84\xf5\xde\x9a\xb1\x06(\xc9\xc4,<\x9f>\x01_1\x96\x93G]>:\xe8bp\xb0\x86\x03X\xf1\xb2\x9e\x0bk\xfc\xa42\x02\xc5,\x99\xb9*X=A\x1a\x85\n\xb3\xe7H\x10\xb3[Q\xb6\xf2\x99\xa9\x92+8\x80\xf1\x19\x0c\x05\x0d\xcau\xb1\xaa\x14\xa8\xd7iK,\x82\x81\xe5\xba\x05Su+>@b\xaa\xc2\x82\xa9\x8a+LU\xa8c\xaa\xe2M\xd9\x80z\xe5|f\x87\xf6\xe0a_U3\xfb\xbchg0P\x8b\"^\xb4\xd7\x7fHIL^&\xc6\x80A\xf1\xf5\\\x1a.f\xda=?'\xc9\xabh\x9a\x05\x18G\x1e\x86\x9a\xa5\x98\x92\x99\x97\x05\xe9P\xbd\x9f\xff\xa7\xea/q\xd2\x8e\xfd.\xff\xca\x85\xa8\xf8i\xa46|L\xd5\xbe'\xd1r\x15\x85\x94\x80\xe8F\x06\x98{B\xf8.}\xe3]GYJ\x17\x8fw\xd8\xb4Y\x8a H\xa8\"_Ny\xb7_S}\x8eW\xe2\x82U@\xbcr\x0b\xc2\x03\xc7\xcb\xe1\xea\x9d*\x9aLl\xca\xf9=\xd4\xa1 \x16\xed\xf5th\xc2\x8a*\xc8\x95\xe5E;j\x91\x97\x17\xed\xabEI^\xf4@>\xda\xf0\xd5\xfe\x9e\x1e\x15'\xbf?*\xcej/\x18\xf3\x91\x91:\xc1\x9f\xd2\xde\x1c\x9b\x1dN\xe8\x88\xe3bA\xa6\x16\xd8\xa4{~\x8e\xce\xe7\xe7\xe7\xc8&\xf4\xdc\x02\x1f\x1d\x9b8\x0e?\xadX\xf5\xfcxTE\x0c\x1d\x98h[\x9e\xd4\x96\x0b)\x1fFTz;\xae\xce\xe5\x92\\\x0f\xc1\x8aI8%\xb1\xe6\xa6\x94\xe3]#3\xb0\x96\xf3c\xac\xe2he\x88?\x03\"UFwN\xd2#\xb1\x85\xcduYd\xf0dE&,!P\x14\xd74\x1c\xb3\xd0\x1fq\xdc\xa2.\xdd\x13\xc4\xb6\x8e\xa20\xf5\xfc\x90T\x1cn\xe4'buO\xa2\xab\xbaZ\x99h1\xa8\xab\xe5\xb1Z\x18\xb57\xb10\x9c\xa9\xb9\xf2\x84U~\x17\xad.\xbc\xb8\xa9\xf2\x8cU~\xe6%\x9c\xde5}\x10\xb0\x0f\xa2\x90r\xeb\x1f\xbc\xc0\x9fzi\x14?\xf3\xa6s\xd2\xf4)&t\xe8\x06\x917\xf5\xc3\xf9i\xea\xa5Y\xa2F\xb2\x97\x9f\x05z/S~\x89\xdd\x9f7\xb0\xf7\x94GZP\x04\xb1\xad%I\x12oN\x90+\xb24J\x01(6A\"P\x9d;T\xf2\xdcQ\xb6o\xf2\x94\xa4\xcf$\xf0\x92\xe4\xb5\xb7$C\xb0\x92+o>'\xf1v\xe6[\xda\xfa7.L\xe0\xc0\xd8\xcf\xc4\xc5$l\x0eO\xc6\xe6\x82\xc5\xe1c!_\xb4b|\xaa\xfe[\xcc\xed\xddv\x9c~8\x8b\x8c#\xbc\x93\x1e\xf8\xc0\xb7'\xf9\xee\xf8=\xba3t\xe2`\xf8\xb7\x99\xe7\x07d\xfa\xaf\x12\x94\x8b\xdd\xd6\xbd\xa5~\x1a\x10c\x0f\xd6\x0b\x04\"\xa4\x11\xd0a\xc1\xe1\xdb\x97\x80l\x88Oi{\xd7r\xcc\x83\xf08rKkq\x84\xae\x95_dE\xcc\xe4\x013A\x9b\x18>\xf1,\xbd\x8f\xdf\xfa\xd3t1\x04\xeb\xe1\xc3\xde\xeacM{\xacz<\xf7\xc3o\xc8,\x1d\x82\xe5ei]\xffE\xfd\x13\x7f\xbeh\xf9AJ>\xa6\x87\x81?\x0f\x87`M\xd0\xdf_\xbfDP9\xdf\xf3\xb7\xff\n\xb01&\xcb(%\x85\xc7n#NZ+\xcb\xe5\xa4v\x8a\x88\xb9\xb5B\xe5_\x92MD,\x8c\x06\xcc\x9cq\xac6\xf7\x11\x89\x1eL\x15\xb2\xa6\nA\xbes\xaa:\x0dE\xea8+\x85H\xba\xb1\x8b&sNIb\xa9\x89(m\x1bl\x8a\x8a\x90;\x15\x8f\xa5\x81\xd3\xd5\xe6Am\xd3\xa2d\xdc\xa7\xcf\xff\xd6\xdf\x91\xad\x96\xa9p\xf2\xc8\xb1\xadrGV\xb3\xf4g\xe6\xd4\xa5J\xbe\x92\x86\x14\xe06\x17o\x83\x87{\x1a\xc1J\x02\x93^\x1ely\x01\x12\xabb\x9f\xa8^\x8c\xb3\xcd0\x8ba\xf5U\xeb\xce\xc2\xabk\x8b\na\x94\\\xb3qWvmy$C\\\x1d\xa7;\xdb\x10b2\x10*\xed3\x89\x8c\x02U\xbd\x8d($\xbaas\x0e\xb6\xca\"=b\x0ey\x0f\xf7\xaa\xfew\xbd}\xa7;\x93\xfd\xe8\xdb\xb4\xd8r\x12\xaa\x01\xeb\xe7Mb\xf0\x88\xbb!>\xe2n\x86|V\x83G\x0ft\x9b\xf4\xf4zy\x11\x05m\x9an\xb2\xf34\xd8\xe1\xaa;\x98\xdby\x1a\xbc\xad\x0d\xce\xd6\x03\xb5q>\xfeG}\xa7\xfb\xf5\xf1\xf7\xe5\xb2 /S>\xe1\xa9\xe5\xd4\x1eXj\xb9G\xeaxXn\xb9=\xf55\xcf-\xa7\xbc\x9d\xe6HR~\xbf\xe6\xefU4\xbd\xe6#T=\xe4\xe6\xfc\xbd:F\x9eV\xae\x82\xed\xec\xb5\x1a\xfe\x92\xa5\x94\x1b\xe83\xcaU\xb0\xed#\x9b\xa8\x1a\xfb\xee\x94\x81E\x95\xd6\x8e\xf9\x08\xd5\xea\x87|U\xd5N\xdf\xb0\xf7j\xf5\x9f\xf0u\xc5\x0d\xf5\x12Fp\xa8\xe6\x90{ #x\xa3\xbe|\x85i\xe1\x94\x97\xefP\x1ed\x18].9\xc2\x92\xbf\x9c\xbey]~\xff\x16FpD\x8f\xf2\xa3n\x82\xaaW\x7fv]\xaeqB\x05G\xdb:_\xf8\xd3) U\x11\xfc5+M\xa3\xb7\xb1\xbf\xf4\x99\xadv\xb9\xc67\xe8\x00\xa6\xcd\xb9_\xae\xf8\x9c\x92{\xdbJp\xf4\xdb1\x99\xfbI\x1a_\xab\xcd\xfd\"\xd7\xaa\xa4\xb9|\xc1J\xa3\xd5\xb6\xa1\xc2{M\x12\xf3r\x8dg\xa6\xf8\x01\xef\xca\xf5~F\x88\xfe\x955V.\xfa\x1eF\xb0\xf53F\x0e\xffY\xca\x08\xa0\xfc\xdd\x9d\xf9\xe1\xf4h\xe1\x07\xd3\xf2\xd7\xdf\x02\x8f\xf18\xa9w\x8d\xe3G\xdf\x03\xd8\x1a\xc1\xa9\xfd\xd2\xfe\xfb\x0d7\x0f\xd33\x91\xed\xe2\xb1@\xd1\xf0K\xd9\xe4\xac^0\xe0\xda\xac\x07\xc6J7N\xd7\xd3\x16V\xd9\xf2\x1bG\xad{\xe3\xc8\xd1\x0f\x0c\x8c\x00H\xa4\xf8\xd2~\xaf\xbf\x9dE\xd7\xd5) HJ\xe0\xfd\x98\x9c\xb9t\x92\xbc=\x1e8,\xc5;\x8a\xf7\xf4\xe7Kl\xa6\x12 \xf9\x06\x86\xf0\xb2\xbcd\x1fj\xb5\x9e \xd9\xd0\xff\xc2|\x0dO\xedw\x05\"\x98\x0d\xd8 K\xa5\x9bV\"|\x96\xbb\xff\x1aF\xf0\x8c\x8e\x98o\x8b\x12\xd6v\xc5\x91]\x02b\x0dBi\x1aI+\x00h\xd5R)\n\xf3\xbb\xba\x19|\xd5\x82\xd5+5<\x12\x8b\xf4\x95\xfd\"_\xc0%\x8b\xf2\x0f#\xb8\xe2\x19\x8d\xe8;Z\xe2\xdb\xbf\xe0\x9d\xdb\x01\xc6c\xc8 \x10f\xe4\xa3\xfd\x9d\xb0\xbc\x93\xe3\x93\xb31a\xb7\xa6\xe2\xf7\x88\xe7\xa8\xc0E\x0bM\x1b\xa1hr\x08\x1f\xed\x1e&\xb6\xd0a6\x0c\x8b\x0e?}b\xd8w\xe2\xc2G\xbb\x8fyv)\x7fR\xf4K\x87\xffm\x0e\x0d\xfa\xed\xcb*_\x0bU`\xfe\xa1\xcd]\xe3R\xeb8\x91;\x93\x87\xcca\xfc\x9a'\x82#th>K}\xc2\xa21\x8a|\xdf\x11<\x05\xff\xb1\x03_\xd9)\x83R<\xf61n\x00\x19\x87\xba\x10\x96b\x05\xeb&\xf0\xe7\xd6\xdb\xe9\x9b\xd2](.|\xcaRY\x19{\xde\xc2\xda\x05\x02!j\xb0\xbc\xa3[>E\xa6\x94\x19\x04\xd8[6#\xd9\x85\x0b'\xff\xf3\x17\xf1[\x94p\xecY\xf8 ]\xbc\xf4\x0c\x0b\xd5k\xd9\xf2\x14\xff\xd2f\x8d\xfc\x19s\xdc\xbd\xd0\xe0\xb5\xa0S\xf9\x90\x08\x1f\xd2\x0b\x16bY\x8f\xa7\xc2n\xe6\xd2\xae\xb1_\x11\x80\n\xab\x8dW\xb6\xca\xa7O\xca\x8e\xe2x[\x8d$sS\x07\x8e\xbf5\xae\xb8\x1a\xee\xe2\x95}\xc1\x9c\xa0c\x1e\xc1 \xe2\x11\x0c\xba\xa5\xdc\x8fl\xf4\x94\xd9b) qe(e;\xc9\x7f%,T#\x0bDa\xc6\x9b\xb8n\xfc\xdfm<~N\xc2\xd8\xf8_a\xe0\xa1\x170\x04>\xa9\x88OJ\x84\xee(&\x95=v\xc4\x9a\xe0f\xcb\xc4\xacB\x8e\xc1\xef\xc5jElJ\xbf\x8cI\xcd>\x8c\xca\xb3*\xea=\xc3\xa5\xf5l\xfb]]\x14,\xc4P\xba\x9ddB_\x0d\x99n1\x96\xb4\x88\x0f\"\xe5(\xaeDN\x17W^+\x9d\xcfX\xaf\xe43\xd6\x93\xbc:\xdd\xca\x14\x89\x94\xd3\x01\xc9\x19\xa9\xac4\xca=\x04\x9b\xf4E)K\xc4\xffOr\xd3\x87\x98\xb4\xe8/.\x15Q`\x04_a\xc4\xa1\xbd]\x07\xff:\xc6\xff\xff\x8d\xbe\xdb\xe7\xaf\xfe\x8c\x15z\x0f\xd9_\xdf\xf1\xf4\x97[\xa1\xfd\xf0!\x02\xd5\xa3\xb3\xb7t\xe2\x82\xe5\xd2\x8f\x91\xbcL\xbb\xf5\x17\xcd|\xbc\x1f\xecEIuE\xc7\x9b\xd9\x19&B\xca0\x11R\xc6T:\xcfTh3\x84\x1dJ\\\x8bl\x17\x90o\xe6\xbfRaa\xe1%/9\xfa\xbb~r\x14\x85\x13/=]\xc5\xc4\x9b\xa2\x90#\xf8/\x17\xcd\xce]n\n\xe623_\x97\x87rt\xd1x\xc8\x95\xe4(W\xac\xcb;o\xee\xca\x99\xfd\xb9\x9d\x91\xe5Z\xf4\x18H\x19\x85\xf8k\xb1E\xd2\xf4\xb1\x03\x0b\xfb\xaf\xe34-'\xbd-HP\x8a\xd9J\x16\xdd$\x8dbB\xa95o\x85\xa4E3!mfm\x93t\x1c*\xedP\x08\x9e\x96`\xc7\xf7w5\xa0Q\x14\xb7d\x15}\xfb9=\xd3:#4^<\x80\xe7tO\x0d\xd9?\xa3j\xea]\x85\xfc^\x92\xeb\x17\xcd]\xa19\xe7\xd7h\xceY\x9b\xd3\xc1\x03\xc6\x01W(\x13\x94\xc3\xed\xf8!<\xd7\xdb\xd3\xd1\x9e\x9e#\x177\x92\xe3\xbb\xd72\xf1YBNI\x9a\x92\xb8AJ\xfb^\x17I\xb2\xd2\x92\xbf\\\x05M\xf6\x05\xdf\x97\xb3\xd7\x01\x94\xf5\xba\xaen\xa1\x0d:O\xa6\x9ao\x91\xca\xaej\xe2F\x99\xf0S\x1b\x93\x96\xfd\xc1>e\x9cN\xedb\xab\xfa\xd5\xafj\x8a}\x92\x0c\xe1\x0f\xe5\ns\x92\xbe\xb9\n\xc5\xf7\xcfI2\x89\xfdUJ\xd1\xe7/u\x15_{K\xda\xd8\xdf\xea\xea\xb0m\x90\x0c\xe1\xbb\x12\x1cQ\xc1R\x06\xa6\xbd\x85\x07l\x8d\x88/\x8e\xc1wjxL!\xa6\x8d\xc3,\x08\xce0\xfe\xcd[[p\x9d\xd6\xdfo\xf8\x9b*\xec\xbd\x8a\x11\x8f\xf2 [\\\x85b:.X\x7f9}\xf3Z\xe3@\xce\xf5EM\xfb\xae\xc4\xfap\x86-=\xe3Y\xe4\x1f\xebb7P\x81\x82sd\xc5a\xef\xebSx\xf3<\xaf\x9c\x1d\xea\x9f\xb9`\x9f\xdb\x95\x94?\x9c\xc1\xffZ6\xe6\x9e\xf3j6i\xc3\x8c\x8b\xbe\xb4\xba!\x16\x1a\x08\xf9\xcc\x8au\xa6\xe3\xd2~\x89c \x03\xc0\x91\x84\x8e\x9dN\xc3\x85\xb7\xdc`\xe9\xa8\xaaz(\xa1\x95\xa4B\x18\xbfFV<\xb4\x07\xfb\x8e\xacZp\xe1u\xa9\x1eK\xc2\xf2f\x86\xd9\xe4\xde\x15\x84\x1b\xff~\xe5\xa5\x0b\x17,\xfa\x0f\xb7S\x81\xc0\xe6J\xc3\x1c\x07\xb6z\xad4\xff\xd2\x0d\xd6\x9ec[K\x92z\xba\xd0\xbb\x1a\xe5m\xa4\xd7\x9a\x8b`\xa4\x8e\xaa\xf3\xf4\xaav\xebI\xa1\xe4\xf3\x93\xe3\x8f) \x13\x9f\xca&\x9f>\xd5\x13D!\xf8\xd4R\xd7 \xa5\x9a\xa8]o\xa5\x9eK\xec\\\xddH\xd6$L\xf9p\xa20\xb1\xa9\xc0\xaf\xec\xc7rW\xf5<\x0e\xe0Q\x9c\xa2\xf7\x91I\xdaC\xb5\x9c\xbe\x90>\xfe\x10\xac7\x16t\xa0\xd3\xf1\xaa\xbc\xa4x\xae\x86j\xb0Z\xf1\xe8\xb4wu\xb0\x0b\x94\x1cR\xd5\x91}}\xfc\xbd68\xf9\xeb\xe3\xe3\xe7C\xd8\xeaWKf^\x92~M\xae[\x9c=\xa0u\xe9\xd0\xa9\xbb\xb85$s$e\x86Fr\x99u\x8a\xde\x14o\xd1\xcd\xc2\x90C\x81e\x01\xc0\xe51J\xe3y\xbd\xa44\xa0\x17\x06{\xac\xbcz\xe1\xb9b\x1d\xd7\xd4\x9d\xa9\\\x93x\xf4\x8b)x\xfcq|\xd6\xad\xe6\xce\xd7\x84p\x9b\x93\xf4[\xe2]n\x02\xf9[\x01dK\x1f\xe3\xa5\xa8M\x8c\x11\xab\xe5\xe73\xc0q\xd5\x06\x1cQ\xf8\"&\xe4\x97\xc6d\x82P4>\xa1\xc7F\xd0\xa5\xc8\x8d\xe6\x146?\xa68\x98\xe8\xef\x19rD\xed\x0c\xab[\xd3\xe4\xca\xbd\x93\x08\x19\xa4'\xc6\xfb\xa6\xe4G\xe6\x89\n\x05]\xac\xcd\xd4\x16\xb2\xc0\xba\xe5\xb5\xc2\x83\xbc\xbaB9\xf7\x90\xb9\xfc2\x94\x02\x84\xf6\x1eug,\xa1J\xef1x\x05\xf30y\xec@\x92g.\xa7\xe7\x867\x9e\xa0\x96\x04\xe5{\xe4*2=O%\x19\x89l\x06\xd0\x87\xfb\x06\x08\xb1\x08\xef~\xc2RY\xc9\x07\x90If\xb5\xb0*\x92\x9c\xd8\xbe}\xa6\xab\xca\xed'_\xe2\xbd\xea \x1a\xb1\x1b:!oV\xcf]+b\\\xbfD\x06\xaf\xfcp\x1a]Q\x88\x16\xbf\ns\x17\x95m\x86\x83\x9aB\x9b\xb5@\x05\x80\xb1\xce+\xa0\x9d\xa8\x8f\x81v\xad1\x1b)|\x8bM\x9e\xe1\x88\xf3Di\x8d\x17 \xe6\xbc7\xb9\x94\xaa!!\xcd\xf9\xe3\xc5\x10\xb9kQ\xa3\xbd\x92\xcdS8\x97\xedn\xf4\x08\xe0\xc0\xdf\x1b-\"\xfa\xbd\x07\x8emy\xc9u8y\xb9\x91\xfd\x86\xf8\x94%GA\x1dL\xab\xef\xda\xd9}<\xba[\xbb\x8f\x9d^\xaf\xc6\x08+\xf9\x0c#\xac\xaa1\x90Y\x12.\xf73\xc4q\xf51\xa7U1\x9fV0\x94\xb6\xb2J\x95}\xbd5D\xd4F\x8c\xa1T\xd6G\x12\xba\x15S\xf9\xe7\xde=4\xa3+\x07v.\x14#\x84eCe\x11\xd9\x12\x92\x82\x97@.Ml\xa9\xe1\x18\xf44\xb0\x02\xa0!h\x17\x05e1+w\xe6\xb0\xc0\x0f\xe1\xef7\xd5\xbb_m\xca\x1b\xf3\xde\xb5\xf9\"R\xd1\xe8\x05o I\x82\xcb\x0d6\xba3\xbbb\x12\x00\xd28XF2\x188\x0e\x1d\xc0\xf8\x8c\xdf\xc5(Yf\x91l\xdf\x86:\x10}f\x8a*W\xc2\xc9\x88\x0c\x0d\xa3V[(\x95Y%\x96\x0f5\x95\x1ceF\x10\xc2\x90\xe5\xc0 \xdb\xf0\x17h]\xb0\xd5wL\xfa\xf6\xc9\x82L.\x87\xd2uB\xabM\xdb\x8aN\xecT\"\xe2}.\x9d\xd8\xfdlKD\xc3!\x14s\x1bUVg\xb3\x81\xdd\x8e\xdc\x08\xc5\x1bZ*\x15\x1d\xb6\xa20M\xf6l\xbb\x06\xdb\xd3==\x97\xb8S\xb1\xf2b2\xfbN_\xb5\xf2bl\xdc\x8e\xfa:\xe1\xd5u\xe9\x89\xe9{\xb5\xf9\x19\x7f\xaf\x0e'\xe0\xcd\xab8\xba\xc2Li%+\xe2r\x85\x85T\xe1\x857I\xa3X\xb1\x85\x9a\xb2\nA\x14\xea\x1bXW\xe3@\\7\xca\xf0mn\xc4\xe7Za\x19\x8d\x87b\x12\x9aD\xfc\xa5\xb7\x1aB\xd4]z+\xbdp?\x8b\xe2co\xb2\xa0u\xf8O}\xbdI\x94\x85):\x1e\xd3\x1f\xfa:i\x84\x04\x90\xd6\xe2?\xf5\xf5\xa20\xb8\x1e\x82&\xe7Y\xb5zn\x9c=\x04\xbf[\xe3\xd3\xf66\x8bI\xa9n\xe9E\xb5~ \x03\x86\xa0\x01\x8e\xbc\xc2C\x98V+\xf8 \xfau\xe5U\xbcn\xf9\x8df\x90q\xb4\xa2\xc7j2\x04\x8d\xf7\x1c\x1b\xd2Q\xe0%\xc9\x10f\xa6r\x8e\x93C\xd0\xac\x13\xab\xf1\xca\xff\xe8\x87C\xd0\xc0\xfe\xf9\x9bWC\xc8\xaa\xef\xd7$N\xfc(\x1c\xc2\xa4Zv~\x9e\xe05\xd6\x10\xd6e\xe4\xd4S\xc8V\xa99\xea\x89\x8e\xacQ3\xf4\x12\x7f~/\x94V\xe9y\xaa\nM\xe2\x02\xb0\x81\xb2\xf5T\x0e\x96\xa5\x13M\xaf\xa2C\xae\xb6~\x1bE\x81\x9a\x8e\x14g\xd1\x9dEY\\W\x8bR\xbd\xfb?\xdc\xef\xdc\x9f\xeb\\{gFA\xc8\xb6,\xe8@\xea\x94\x82\xbd\xff\xe1\xde}K>\x8f\xaa\x0d\x06\xdas\x0d/|i\x1df\x85\x86\x7fN\xa20e\xb9\xb9H\xfe&c7\x88\xb5=\xact\x0b\x05\xd2\xb2\xa4\xd8\x93f\xb3a\x19\xefV\x91\xdb\x99l\xe7c\xc3)\x1b\x88\x9c?]7\x8e\x85\x18\x87\x86\x93\xc4\xe9\xc4$a\xde\x1fb\xc6\x97\xe4\xfamLf\xfeGi\xce\x1c(a\x05(\xf1F@\x996\x03\x85\x0d\xa7\n\x96\x0cK\xf3\xb1U+x50Md\x98j\xa8 ;\xe8(l\x13\x05\xb6\xe5\x05(\xe97\xec \x95\xb1\xd7\x14\xe3b\x84o\xd4M\x17^z\x82\x88\x99\x08d\x17\x8e\x9c\xb05b\n0\xdbW\xa8'm\x87\xbe\x9f\xa0\x9a\x08\x89\xf1a8=a\xf8\xfc5\xb9\xa6\x1dd\xd0\x01{kB\xe7\xcf,yP\xb9C\xff\xc2\xe4\xf2\xf8\xeb\x00,\x0b\x860\xb3\xf1O\x87\x8a2\xf7Qg\x1b\xa2\xe1\x10S\x05M\x9cztYK\xe8\xe2V#g\xacy\xd4\x0c\xd5\x89V\xcc\x90\xdd\x0c\xa1hf\x87b\x08U\x83\x17\xbaV\xe8\x9a\x8b\xa4`j\x13\x8c\x8c\x81\x1d\x96+\xa3\xc6\x7f\xea\x82\xe7\xb8\xb0\xe8\xc6$ ^Bl\xaf~\x0e\xd7&,\xe34\x83\x0eVj@\xfc\n\xa4\x8b\xa3)\x11\x06;u\xf6@\xa5\xad\x81\xee[\xca\xee(\xbd\xacl\x10\xba(\xdetJa\xe0\x87\xf3w\x91\x1d\x88\x89\xdej \xf9F\x96z\x95\xf7\xb2\xf4\xfa\x0e\xc7\xbcp!Q\x04\x8c*\xfb\x96\xb3^u\xa7\x98xP3J\xf1\xa9dM\xa0\xb9x\x10D#(c\x92.\xc9:\xe2\xd1\nS\x17@\x90\xe3\x91z\xdfX\xa6\x0c\xc8O~\x91\x01\xeb\"p S\x01\x9b]q\xb1U\x10\xa6\xda\x0d\xc3|\x19\xa6\xd1\xb7~\xba\xf8Z\xac\xf6\xcb0%q\xe8\x05CX+\xc7,\xe3m\x1b\xf5&B\x87G+\\s\xd7\xc3\xbaA\xe4\xfcp=\xf3/\xf4\xe4M\x00 \x02\x00z\x92Z1\x10/\xf0\xf3\x8b\xf1j\xa1\xbd\xaf\xd31\xdb\xa1M%\xaf\x86y\x0b\xc3\xc1\xae\xd0\xa0Pl\xad (\x07\x12\xac\xaa\xdf\xad\xa2\x95)\xf3\xb5\xc0=\xdc\xbd<\x12|\x15^P\xa7p \xc9\x15~_1B\xaa\xd5\xbfi\x95T\xb2\xc2\x08\x0d\x0f?}\x82\xd8\xb6\x06{h\xcb%\xd16\xdbq5\xf3\xe4w\x1cOx8\x90(\nN\xfd_\x880>V`B\x0f\xb7z\xb3\xa9\x0c\x934\x97^yZAS\xa6o-\xf6\nH\x96\xc6\x86\xebQ\x01\xda\xd2\x98\xb9\xd1kXP/\xb4\xeb\xf8\xf4 2\xfa6\x9f/3:\xce\xff\x1c\xb1\x8cp\xa1\xa0b0\xa2g\xa7\xc6\x02\xb9\xca\xe7P\xce\xa2\xc4\x83\x0fU\x80\xd0\xa7\xc2\xcf\xb7\x84\xc1m\x90\x1cd\xd8m\x82\xe8\xa0Cv\x11\xa8P\x07\x0e\xd0\xe2<\xe8\xf0\xbeb\x92\x05zp\xa6\x8b\x98T\x00\xda\xe6\xc0\x80\xcf\x84V|'\xd0\x8a\x19\xb4tG\x8cx\xda\x03\xac\xe2\xa5\x01z\x98U\xe5\xc0*\xc8\x0c:o\xf8L\xa8\xf9w\x025?\x87\x1a\xe3&\xaa\xb6\x03\xb0)\xe0*\x86O\xd5\x16\x0c\xe7\xdag\xc4\x0fk>\xd7\xfa\x05\x1f\x15?f${\x1f^\xd7\n\xb3\xe5\x05\x89\xe57\x05Ty\x17\xa4\xfb\x87?\xf0\x91\xd1wE\xfe\xf4\x99\xcd8V\xcb\xca\x93\x87y\xd0\x81 \x9dp\x0f\xc5`\xc7\x05\x8d\xc5\n\x9dqM8\xd65\x8a\x9bR\x93CLd\x93\xe8\xa1R\x96\xd0\x89\xc6\x1f\x01d+\x8bkfOq\x0dO\xf2$<\x8f\xe1\xba\xd3q`\n\x9d\x11\xa4\xf6\x8a\x9e\xc9\xe3\xeb3\x17\xd68\x97\x95\x0b\xd7\x0e_\xbd\xea\x0808\xa6\x99C\x98\xb3,\xa5\x06rC\x87?o\"bK\x17\xdd\xc0\xe7\x9c\xbb\xab\xa1\\\xd8\x1c\xbb\xe8\xec\x920\x8d}\x92\xe8\x81!\x9e\x1c(\x17\x0c([\xf6\x12Fp\x8e\xa9\xe9m\xc7\xe9N\xa3\x90<.\x01f\xc9\x0c,%\xd8\\t:f\xe8\x88\x87B\xa9y$\xc6\x01\x98\x01$\x1e:\x89\xabb|\xe6\x91\x88\x07\x0d:lifWhZ\xbbF\x03fN.\xae\xc6\xbd3\x87\"\x9e\x98kO\xcc\xb4\x1e\xac\x06[B\x86+\xb8\x91K[\xac \x01>\x1a\x92\x91\xc9\xcfi\x11+\xba\x0eCb\xdb\xda\xe9[naG\xc2n\xdd\xce\xd8HN\xe1@\xec~\xb8\xf2\xd3\x05\\\x92\xeb\x04\xfenAG\xdcg\xd3\x176qx\x9a[\x17P\xd9d\xddX0\x84S\x17>\xb65?3J\"\xd3R\xc1\x0d\xa5\xb8\x96\xa5\xf2\x1a\xadn\x1b\xeb\x8f@\xad\x8d3\xf7\xe1\xbaw\x8f\xff\xca\x1d\x8b\xabg\xa5\xf5/\xff\x92\x07\n\xd1\x9f\xd3f9)\x97\xf2\x80\xc5\xcdEg\xc3\x18\xcd\x9b\xd3\xb1\xafZ\x80\x1b-\xb2\x89\xc6\xdc\xfa\x0e S\x1e+\xdb\x08me|=\x1a[#k\x08\xd6\xa8g\xc0`k\x88\xc5\x83j\xb8\xa7\x1b\xa3\xc6\xc0\xfa\x03\xc5\xc9\xcaE\xc0\xfd\xf1hxv\x7f\xde$\x9aK\x0d\x91qzV\xed\xb7^\xa6\x0c\xef\x06(=\x9c\xb6 (\xa3\x01-\x1en\x02\x14\x06\x0e\xdb\xea\xb2\xcd\x9c\x8e{\xe8\xe8Ma\xc5\xfe\xee\x9f\xa1\x8dD\x92]0.\xc0\x1e\xd0#Z~\xd1w\x1c \x9a\xf6\xa8\xf7i4p\xee\x1e\xa0\x05\xbe\xea\xf7\xce\xdd\xdc\x80\x0d\x9c\xba\x9bn_\xaf\x07\x18R\x12Y\xb1\xe4\xc7\xa2\x8b\x8b\x98\x95^\\h\x83~z\xd3iL\x92\x84\xd5a\xbf\xb5\xd5b\xc2{\x89\x89\xbe\xa38\xf5'\x01\xe1u\xf0\xb7\xb6Z\xe2Oy%\xfaK[%\x9b\xfa\x11\xabB\x7f\xe9\xaa\\`\xf1\x85\xb6\xc8KX\xfb\xf4\x87\xb6\xc2\xd4g\xe5S__\x1c\xf1b}\xcf\xfe\x9c\x15\xfbsmq\x10M.\x7f\xce\xa2\x94\x8f!\xffS[9\x9a^\xb3j\xd1\xb4\x12P\x05+\xb0\xa5\xd3/\xdcE\x96\xa6Q\xc8*\xe0O]\xa5\x89\x17\xae=\xb6\xb8\xec\xa7\xbe\xd2*\xf5yS\xfc\xb7\xb6\x9a\xcfgE\x7fh+D|i\xe9\x0f}\x85\x80\x97kc\xc6N\xa2`\x1eG\xd9J\xd4\xc1?t\x15\xa7^\xca\x90\x91\xfe0U\x08\xfc$\xcd+\xd1?\xb4\x15\xa7\xac\xcaT[H\xd8p\xa7D;\xdc)I=?Hx\x15\xfc\xad\xad6c\x90\x9d\xce\xb4P\x9d\xfa^\x101\x9cb?\xf5\x95\xd6\xbc\xc6Z[\xcc\xc7\xa9\x1f&\x87\x82v\xfed\x89\x85d\xa9/\xbc S^~A\xb4 \x9a\xf9$\x98\xa2\xe9`l[\xe2\x0f}\xc5\xb9\x8cf\xc5\x9f\x86\xcaYLD\xc5,\xd6\"\xd3,\x8a\xd0+\x93V\xc2\x9f\xfaJ\xf1\x92W\x89\xb5s\\\xf4\xb1x\xd1\xd7\x16\x0eX\xe1@[\xb8\xc3\nw\xb4\x85\xbb\xacpW[\xb8\xc7\n\xf7\xb4\x85\xfb\xacp_[\x88V\x1f\xb4\x98x\xda\xf5\xa0\xef9P\xd8Om\xa5b\x97-\x8c{l\xc1[\xd1\xb7\x90.\x19\xca\xd1\x1f\xba\n\x8c\xc4j \xac?\x8b1\\&-\xc7\x9f\xdaJK\xb6%\xfc\xa5v?\xf8\xe1*c8\x87\xbf\xf4U\x12^A\xbb+//\x18 //\xb4p\xbc$\xd7s\xc2P\x95\xfd\xd4U\n\xbc\x0bN!\xf0\x97\xb6\n\x99\x93\x90\xf5\xc4~j+1h\x05Zp\x05~x\xc9\x8b\xc3K]\x85\xa5\xe7\xb3\x81\xd2\x1f\xfa\n+^\xae]\xe8\xa5\x17_\xf2\xf2X\xdf\x01 3V\x81\x84\x99\xa9\x82\x9frR\"\xfe\xd0W\xe4t[\xe7w\xc8+p\xec\xc5_\xba*\xa1\xc7Ha\xe8iIa\x181\xbfaV\x87\xff\xa1\xab\xc8\x04F\xac\xc6\xc5Z]%\xb6\xbc\xfa\xe3*Z\xa5\xc5F\x12\x7f\x18*\n\xba\x17\x19i^\x94\xa5\x02\xa7\xd9O]%\xd6\x97\xb6\x93\x95\x17{l\x05\xf0\x97\xb6\x8a?I\x05]\xe5\xbf\xb5\xd5D\x15Sq4\xcf9F\xf1\x87\xae\xe2\xcfX\xe3g]Q\xcc&\x12kg\x123(\xc4Z\x08\xc4\xd9\x05\xe3\x99\xe8\x0f]\x056.\xed\x80\x12o\xc9\xfa\xa5?\xb4\x15\n\xd41#NB&\xf9r\xf2\xdf\xfaj\x81\xc0/\xf6S[i\xe9\x05\x0c\xc5X\nN]\x15L\xa3\xc4\xea\xe0Om\xa5\x95\xc7\x07\xb4\xf2\xf4\xa3I\xe3(d$\x95\xfd\xd4W\xba\xe6\x0c<\xfe\xd2V\xc9\x18\xeb\x9ddZ\xe6;\xc9\x96K/\xbe\xe6U\xf0\xb7\xbe\x1a_\x07\xfd~IY\x1c\x95\xd8\xb6R\xe6\xdb\xa2\xa9\x92\xf3\xce\xa9\x89yN\x19\xd9M\xb5$7%\x1f\xd3\\\xa4\x11\x7fh+R\xde\x82\xd5\xa2\xbf\xb4U\x16\xac\\\x9br=\xcd\x8f\xec\xd4tf\xa7>?\x0e\xe9\x0f}\x85T\xc0\x03#L\xeb\xaa0\xaa\x99jIf\x1a{\x93K^\xeeM\xb44\x9e\x11x-u\xcf\x18\x82fZ\xec\\{\xac\xe3\xb5\xa7\xedy\xedO \x13\xa7\xf0\x97\xae\xca\x15\x17r\xae\xf4R\xce\xc4\x8f\x85T\xc9~j+\x05\xfe\xea\xad\xc7\xd7A\xfc\xa1\xab8%3\xc1\xaf\xcf\xb4$\x82\x04\x81\xbf\xe2\x02$\xff\xad\xab\xc6v\x92\x9e5Yzs\xce\xdd,1\x93C\xb5J\xe0\x87\xac\x06\xfda\xaa\xe0\xc5_\xc5\xde\xd4G3f^\xb5x\xa5\xfbh\xe9%\xe2\x1cO\xb4k\xbc\x12\x10Z\x19\xa0\xb3\xf2\xd2\x94\xc4\xa1\xa8C\x7fk\xabE\xc1\xf5\x9c\x13@\xfe\xdbT-\x9f\xa9\xf8CW\x91\xce\xc9\x0bJ\xb3-\xbf\xd2~$\x88kl\"\xadi\xc4\x89L\x1a\xe9\x89\xfd\x9a\xd3\xc3\xb5v\x1d)Q\xc8\xa9\x83\xb6BNtSFuK5\x0c:\"v {\x07:\xa2:\xbbvn3\xdd7\xb9\x07\xfb\xc2\x9e\xecs\xc7\xd1\xdf\xdb\xd8\x01Yx\xe4\xd0\xfe\xe4`\x8cw\xa0\x03\xd6\xd8\x83s\x8f<\xf5\xf6\x97[\x8f\xebcYT\xdckx\xa8\xe7}5V\xb0\xf0\x8b1\xf9\x18\xd7\xda\xa2\x08[\x92\xcfQ\xe9\x03\xb7\x08\xd6\xab\xf5E/3Z\xe3\xc9\x13/\x8c\xc2\xebe\x94%O\x9fj\xb4\xb7\x81Q\xe5\xeb1s\xb9\xb5m\xe1/\xddN\x00\xd4eQ^ym\xe7\xf7\xba\x86zt\xbaX/\x9f\xb7\xa1\"\xbb\xe0\xc5\xaa\xfc\xae\xd7PQ0\xf2\xeb:F\x1e\xf2\xc08X\x91\xdf'\x9b*\xf2 ck\x11\xcf\xd8T\xd1\x0b\xaf\x870\xb5c\xd9\xf6\xef5^`\x9bA\xf9f\xd6\xa4\x82\x17\x8f\xb8\\*\xe2\x99\x14\xe6\xce.DM\xf7\x8b\xca\x15\xccVal\xe0\xc8\xf6\x1d\x0b\xdb\x12n\xdf\xf0\xa3\x05\x1d\x88\xa0\x03\xd6\x8f\x10\xcd\x8a\x94s\xac f\x05\x0b/\x01?\\S\xea\x93{\xcf@\x18\xa5\x98\xc0\x82\x8a\xdd\xfe\x94\x88\xa9vM\xe9C\xc5C\x11\x14\x13I\x8dCC\xb2W\xf1`D\x89\xf2\xa5yV\x1b\xb0B<\xb4\x0b4\xad\xacD\x17\xd0=e\xc8\xbc\xe4\xf3\xa4\xd3\xf71\x16\x99\x02\"\x0c \x8d\xef\x12\xf6.\xc9V\xab\xc0gi>$\xa8\xb9@>\xae\xc8$%S\xf0B\x06\x9d\xaeu\x9b\xebX\xf1\xe4w\xe0<\xd0\xc2\x04\x9e@\x96\x1b\x06L:\x9d\xb6\xa0\x99aj\xc9\x0c\x93\xe2r\xcc\xa2#\x1e\xd3\xb1O\xe8\xaf3\xcb\x05\xaf\x05\xe4\xe8\x02\xcddCJ\xf4T.\x8c.>c\xb2:sx\xf5\xb91\xdc\xe2\xea\xb7\"\x11\x1eb\xf9\xde\xfa\x82;qC$O7@l\xef\xcb#\xb6\xd7\x1a\xb1!\xf1\xc3y@\xe0\x84x\x93\x94s&\x9f\x87\xe5\x9f\xb3\xf0\xa6\xack\x02C\x7fWB\xbce\xd3\xc5/\x99\x19\xb7^c\xe6P\x14zK\x16)K?+\xf5\xf1\x1a\x8d\x9eM\x0f\xc3\xc1\xae\x14\n\x16\xe3\x0d\x97\xde\xe0h\x8a\xad\xdd\x8c}\xe2\x11vp\x95\xc6Z\xb5pc\x1b\xa2W\xab\xcf\x97Gv\xb1\x92\xf4s\xac\x91a\x8d\x7f\x1c\xba\x1b\xb8(\xbc\x92\xbb%\x91\xabu\xb0R\x1fD\x9bk;\x1d\x933Ge0\xe4\x05\x88\x8b\x05\xf0\x0d\xc0\x0e\xab\x94\x05I\xca\xebhJ\x1a9\x8a\xcf\x81\xa1\x89d0\xbe\xf2w%\x18\xff0\xceM\xcc\xb5\x11\xd0\xf2\xa9\xd6L\x93\xdaq`%+\xb3\xad\xd1\x08\x92:T\xbaC\x8e\x8c\xf5\xd98g\x89\xeb\xf2C\xc8\xea\xf7:\xf0 e\xdd\x85\x97H\xd1\x95\xecI+\xd2\x0f\xf5\x0cZ\x17\x19\xb4v\xac\x19|.{\x06\xff\x00\xd2\x15\x85\x1b\x1c\xd1\x1a\xe9@\x8aTW\x11\xd0jL\x0d?o\xeb\x16Q\xd1\xc4\xce`\x810\x1f\x83\x07O \xcd\x19tO\xf6\x866=tR+\xba\xf2\xe9\xd8\x93\x89j\xed\x04@\x12y\xfer\xfa\xe6u\x91?H\x9bYB~6\xdcih\xb2*\x1f~-\xb6Z\x14\xe2\x89\x99o\xcf\xba\xf3\xf2\x16\xe8B)\xda\xef\x8e2R\xe8i\x16\xad\xbb\xb4\xd2\xa4Y\x14\x13\xba\xa0T\x9b\xa9_~\x8c'C\x98\x0f<\xb2\xb7\xfa.\xe4\xab'\xe2\xf4\x96\xd6&\x87U\x17\x8eU\xb1\x14\x8f\x8f\x05\x99\\\xe6`L\\\xb8\xc8R\x88\xc9\x84\xf8k2\x85?&\xe0\xa5\xe0\x87S\xf2\x11\xfe\x98t-\x17\xce1\x99\x0bA\xe7m\x05l\xe6\xd5\xfd]\xb6`\xef1d\xa5\xe5\xc8\x9a\x97\x03\xa4\x1d\x94\x8e\xb3\x86%\x01(\xfb\xd5&\xe5\xd1R\x02\xed\xb4\xa2\x8e\xd0\x9a\xc6\xb6\xd9\x9f\x86\xadxw\xfb-Y\xb4\xb0&\x15\xcfg.\xe9\x7f=\xac\xc6\x8f\xac\xc7\x1f7\xe44Z p9\xb30\x9e\xb4\xc4\xd9Y\x9bf\x817\x1d`\xac\x84;\xe1C\x82\x1c\xd4\xf5\xdb\x01\x1a\xb7D\xbb\x0dswL \xf9\xe8M\xd2\xdf\x11\xeb\x93\xd6X?A\xacO6\xc5\xfa\xc9g`\xfd\xe4\xce\xb1^\xa0p\x86q\xed\x18\xff\xd4\xc4\xb5\xe4;%\xa0;\xa5\x15J\xd3\xda+\xdc)A\xcb\x9d\xb2\xb5\xda\x0cN\x97\x84\xcbdA=9\xfe!|\xe6M\xf3+\x0cZ\xa0\xf0l\x0c\x06,\xc6\x80\x05\xdcs\xe5\x87\x10/\xff\xd0\xd1E\xfb\x95\xec\xf7\x92:\xa5\xef[l\xd35\xf7s[\xd9\x89\x0bAu\xb7\x07\xedv;\x85\xdb4\x07\xdb\xf4\x1f\xb4\x8f+oo$\xafM\xa8\x06B\xd2\xe1\x8f\xd0Z\xe5\x891x\xf2\x02\xf8\xf4 \xfap\x1f\x0b\xf0\x07\x81!f\x00c^2\x84\xfeR\x03@\xe8\xfb^\x18\x02\x13,\xfc\xa4\xbb$I\xe2\xcd\x89\x14\xf8(I\xbd\xc9%\xbaW\xb5j|j\xc8\xff \xcaC\x9b\x11\xa5\xc8\x85\xcc\x85\x04)\xbc\xd6\xe5\x93>6=\x883\xa6\x89D\xa23\xc1\xa4V.\xb0X\xa5\x9e\xc3S.`b&dE\x8f\xbc \xf0\xc3y\x11j\x0dp\xe7xi\x14'0\xf5c2I\x83k\x91\xe4\x85n\x94(\xa6D\xe3\xe2\x1a\xd2\x05\x81\x1fWq\xb4\xda\xa6D'\xf9\x11V\xde\xe4\xd2\x9b\x93.\xbcO\x08\xfc\x987\xd8E\x865\xff\xd3v~\xa4\xfbl\xe2\x05\x01mb\xd9\x85\x13\xe2Ma\x19\xc5\x84r\xae\x8b4]\x0d\xef\xdf\x9f]t\x97\xe4~\x96\x90m\xfcz\xbb\xe8\xc7\xb8I$<\xc48\xd0\xe3\xe8\x0c\x0e\xd0\xd93\xf7W\x15\xef\x18\x91x\xb7 \x85\xacS\"\x9a~\x82\x86\x97\x94\xf1N &?g~\x8cZEY\x9eb|\xb7\x9f&\\\xd4\xf2\x13\xf8\x91vD\xe9(\x0c\xbf\\\x1f\xb9\xbf\xae\xe8\x88Nn\x08\xa9]\xc2\x91&Op\x90\xaf\xe6\xbb\x17~8\xb5\x19\x19\xda\xeak\xc0\x9b\x8b]~r\"F\xaa~\xd7\xabF\x981`\xfc\xba6\xa4\xa3\xe9@v!3a\xbd\xb8k1_\xe1\xf0\xb6\xe7\xb6\xe7p\xe2p\xd0\xee\xa8(\x1d\xa9K\xfay\xdbS\x95\xbeM\x05[\xcf\xd7\xa9\xba(\xaa\x17\x93\x1eb\xd7\xb6\x96\xf2%W>\x8b\x92\x9b{\xef\xe9\xe13\xf1\x12\x92;e\x0fk\xaa\xf0\x9b\xf7\xba*\x85\xbb\xb8\xbe\x16\x14\xd06\xa5 `\x0d S\x84\xe6f\x0c\x9e\xb7\xac\x19\xce.\x99[\xd1\xbas\x8b\xb6I\x97\xacI|m_7x@\x97=\xdeS\xb9\x89\xbaD\x0bk5Bc\xa3\xa8\xb0.9r\x86\xcc\x913\xe4\x8e\x9c\x93\xa6\xdb\x95\x8d\x1c;\xd5\xe7\xa6\xd1\x0f|+n\x953\x82\xce\xc1\x17)O[9\x98\xc7\x8a\x83y\x1b%\xc2c\xd8\xb2}LhPv\xec\xae\xfd\x12\x8a\xbb\x10\x9fyuK\x0b\xd97\x83f\x03gs\xdd\x98Zr\xbd\x18Z\xa8\xad\xb39*\xaf1\xf1\xc5\xb5\x9d\x8d\xfbg\xad&\x02mt;&\x8c\x16\xe1\xa5\x1b\xbf\xaf\xf6\x7f\xd3\x8a\xcc\xcd\xeb\xbd^\xc5=\x8b\xf1|R\xf5\x85p\x00\xdc.\n9?I\xbd~B\xe6\xc7\x1fW\x85k\xba\x05-\xa3\x13\xf1\x9e\xa4\xfc7\x9c\xd3\x14I\xa1\x18\x95\x18[\xff\xf2/R*B\x0b7p\x835\x19\x91\x07\xc8^W\xe1\xc8\"q\xd1\x81\x8b\x11T2W\x1a\x80\xbb4\xc7\x14\x93\x12\xcb\xe1\\rjW\\i1\xb7\xe8*\xe4\xc5\xda\xcc\xb5\xfa\xebJ\\\x82\xfa\xa8O2\x00\x9e{\xa9\x94\xb1g\xea\xa5\xc4\x90\xb4\xa7\xf2%[\xdb\xe2\xdb\x98\xcc\xc9\xc7\x95\xc6\xeb\xd9\x84F\xed\xe0y^\x8f\xac\xfaT\xd1\xe2\xc4n8\xaa\x19\xd2\xd6\x1d\xc3\x8d\xc7\x9e\x98\xbd\x17\"gS{\x86\xd6\x1f\xc5\xac\x0e\xae@]\x05\x0e\xe6\x16#\xaa\x1bP[\x1a\xd3\x14\x89\xae\xfc\x17\xffH\x8a\x88 #v\xc5&g/\x08\x14I\x05F\x94\x95\x0e\xba\xf2\x8b\xc0\x055\xe8\xe7\xad\xccb\xebb\x01\xe5W\xfaw\xd4\xbe\xd5\xdf\xeb\xeewy0\x84[\xb5\xb6.\xc2\xec\xef=tLa\xc5\xfdV\xf6\xcf>\x7fu\xf8\xfa{C\xbc\x87$\xf5R\x7f\xd2\xae\xee\xaa\x08\xb4\xde\xa26\x8f\xf2\xba\xc1\x07\x0b?\x98\x1em\xfa\xd5\x9c\xa4\xcf\x199\xa0;P\xf9\xe6\xfc\xd5\xf1\xc9W\xc7\xcf\xcd\x9f\xbe\x0c\xfd\xd4\xf7\x82\xd3\x14S=l\xf4\xe9\x914\xdcM>\x8dI\x88\xfe\xbd\xe2\x8b7\xaf\x8f\x8e\x8d \xe4[\xe8[?\x08^\xb1p\xaa-@\x92\x7f\xf6\xdc\x9f\xde\xe2+\xda\xd9 \xbb)\xd4\x80\xd4\x84G\x8b(\xa3\xe0\xe0m\xbc_MK\x10m;I\xf5\xbb6\xe3}\xeeOo\xf3\x19v\x17.[\xc3\xe7\xfd\xeb\xd3\xc3\x17\xc7\xe7\xb7\\\x13\xdd\xd7\x1b\x03Y\xd7\xc8\x06S\xcf\xb0\xaa\x94\xcf\xc1z\xf3\xe1\xf8\xe4\xe4\xe5\xf3\xe3\xf3g\x87\xa7\xc7\x1a\xe6\xa7\xda\xce\xc4Htp#\xc6\xfe\x9aLq7\xbd\x88\xa3e\xcd\x8el\xd3\xd7\xcc\xd8\xd7\xd4OV\x81\x87I\xceZ\xb2\xe4\x80\x84W\xfa\x0eT\xbd\xaex\x0c\xd7F\x82\xa6\xb6\xee\x8d\xb2\x9c\x9a\xd8\x9e\xf2\x93\xdf{\x84\xec\x9e;,\x85\x86\x0b;\x1d\x87k\xb4\xc7\xe1\xd9Fw\\\x1aR\xdaz\xdci\xb7\xf25f\x1b\xfc\xfb\x8d\xab+\xd3\x060\x85\x9a\xa1\xddzT\x86\x01}\xc6X*g\xc7\x06\xc3Q\xbe\xc5\x00G\xea\xbb\x11L\xed\xca[ly\xa8\xad\xbd\x11BJ\xa7\xf1\x06\xc3^Il\xaa\x00a\xfenS\xf8\xe5\xccC\xeb\x01l\xb5\xaf\n\xed\xf6\x10\x94\xf7\x91\x1f6\xb7*\x1e\xc1\xe85\x1b\xf5\x8b\x07\xc7\xa3\xda\x02\x86\xadm\x01A\xe8\xbd(\xbb\x88W\x9d\xed\xba\xa5Odo\xf9.\xfc \xadhy6\x9b\xef\xa3\x0c<\xbc\x10I\xc9r\x95\xfa\xe1\x1c\xd2\x88gi\x07\x0fb\x92\x90xM\xa6\x88)t\xa4.\xfc\xf8\xc7\xe4G\x17\xd2\x85\x97\xf2\x03;\xfc\xe1O)\\\x10\x88B\xbc\xa9\xb1\xf8\x8aZpI\xae\xbb\xf0\x9c5\xe5cn:/,,\xa6E\x8b\xf8\x86x\xd3\xc7\xb4\xce\x95\x1f\x04\x90\xa4\xf4\xff\x17\x04\xbc\xc9\x84$,94o\\\xb6\x17\xff\x93>t\xbe\xe9\x11z/\x04\x9a!\xee\xb5\xeeA\xf5\xd7&\xab\x03\x12\xcf=\xa9.4\x1c\xc0d\x1c\x9eqE}\xfbq@!^F\xb6\xee8D\xbd\x87\xe7\x82\xd5z}\xe9RR\xc8^GY,\x19\x0b\xe3\x0dY\xba\xf0B\x88\xc2 \xe9\xc2\xbb\x85\x9fP\xc8\xcf\x02\x7f\x92\xc2\xd2\xbb\xa6k3\xcd\x08m\xc9c\x87Z\xd7ba\x99\xd7\x91?\xb5Q\x8f\x8ct\x0bo\xad\xe3\x86\x80\x93\xf2S\x7f\x01,?\xbc\x13}\x1ch\xf5in\xd6\\\xe3\x86Q\x99Mh\x9a\x97\xa5\xd1\x85\x1fN\xcb&\xf7\x1b\xdcA\xeb\xd3\xfd\x80d$\x98\xa8\x88E(b%cbF\xacs\xcd'\xf7\xeeQd*\xb3p,tm \x8f0?\xc3\xcc\x9b\x10\x13BEk\x12\xc7\xfe\x94\xa3\xd4,\x8e\x96\x1c\xa9\xe8\xd7\x90\xac\xc8\xc4\x9f\xf9\x13\xb40\xef\xc2q\x98d\x0c\xc3RVkI\xd2E4\x85\x10\x93\xd1N#\xbc\x01\xa6-\x06\xde\x8a\x85\xf2\xc4\x91\xf0jhjH\x1c\x97\xdd\\\x94\xb7\x82\x08\xbb\xfb\xe9\x93\x96a\xbc\xcd\xcc\xbe\xc8V!\xedn\xe3\x90q3\xa7\xf00\x11\xa5\xc8`\x1cZ%\x0d\x7f\xaaL7K(\xd9/&\xc8\x160\x8a\x8bAQ2\xceg\x02/\x19\xe9v\xe1\xa7,I\xf9\xb71\x99g\x81\x17\x17\xb6\xf4.=w\x08\xda\x86n\xde\xff\xc6\xbd\xe9 \xea:\xcf\xd7T\xa8\xe1\x8c;\xde\xc7\xfb\xa4\xf3\xf3\x98\x0e\xf60K\xa3g~8}\xeb\xf9\xb1&\x863\xc8\xac\x83G\x8f\x96P\xddf\x19\xcb\x14\xdee\xdc?.)\xff\xedh\xa3\xd0\x8b\x07\xd7Xm\x8c\x19Vxx\x8d\xd5x*\xad\xb9ch8\xf6Z\x98\x8e\xadp\xda\x95\xfe\x9a/\x02\x03{\xc5\x12\x01\xcd\xaa_;0\x1b{gt\xd2\x93\x86\x96jbQ\xcb\x0f\x9d\xd3BG\x00\x9bF\nu\x86\xd3h\xbd\x82\x01\xc4W\xe8\xe6\xd6g\xa4\xa2+(y\xbb\x13\x0c-\xf5\x9b\x16E~\xd6<\xa4w2\xf6Zr\x8f\x80\xfb\x1b\x03\x9b\x9b\x99\x80k\x95\x00\xf2\xd7\xea\x0e|\x1f\xe6V\x04\x94D\xc3*\n\xfc\xc95\xfc1A\x94\xbe$\xf8\xf3jAB\xb6\x03\xe7\x14\xbd\x8b\xadI?Ab|\xcdV\xbff8\x07\x10\x8f=\xc6\x13\xd0\x1f\x14\x19`\xa8\x1b!\x8b*\xcc\xea\xae\xf3\xba\xed\xa0\xcfCT\xf3\xaf'\xcd\xf0d\x11\xadY*\x16\x8f\xf6\xe3\xe6\x1f\xd7~[\xc3+T\x8f\xf8V\x84~a<\xef\xcbbIds\x8b\xb2\x9a\xfc\x01\x9a\xf7\xc4\x05kI\xe29\x11\x89\x97^G\xcf\xb3U@\x0fd\xf25\xb9Nlg\x08G^H\x8f]\xac\x06a\x14n\xb3f\x12$\xe0\xc4\x01\x8d\xc8\xc2r\xa7\x95.\xf5\x90\xe1k\xec\xeb]\xcc-ZXo\xe9U\xc4\xe9w\xc2\x8e{\xca\xe9'\xde\x92P\x14\x1c\xe2\xd1\xdb\xead}LA\xb4\xc2\xa8\xb3\xf4L`Vr\xa2\xea\xc4\xcb\x12nNv\x15\xa9j[\xdb\xa1G\x9c\"L\xdb\x8e\xe088\xdfMw@i\x9c\xf4p\\\xd0\xb7\x97\xe4:\x11,0gL\x0d.\xaa\xc2\x86\xb0\x15ZL\x9bL\x11e\xf6\xd2x\xee\xa1OI\xd7[\xad\x82k\xccE\xe2\xe6\xde \x89\xc1\xd1\x91>(\xd4\x1a\xbe2\xdf\x8f\n\x9b\xb8\xc2\x11%n\xae\\\x18{\x84\xe6\xd3\x1bC\x1ek\xe2G\x83t\xebf\xfbl \xf0\x87>\xd9I\xbb\xfd\xb8\xfel\xc0\x1b\x01n\x04\xea-\x87z\xdd(*\x10f=\xa7\xbb%\x16`WzR[\xd1\xe77\x06\xfd5A#h@X\xb4\x9e\x9f\xfb ~\x84F~\x9a$\xeb\xa0'\xa9U\xa4]6\x0f\xb0\xa4\xaa\xbf\xf5\x18\xf5\x06/\xad\xc6xn\x1c#\x8fY\xce/\x90Z+\xb7p|L\x1f\x1fwI\xf8sF2r\"5\xc51lc\xe95\x9fpK8 c\x9c-\x15`\xb7\x87\xd5\x859\xd90HV\xa2\xf6\x85|\xab.\xf3\xf6p\xae!m\x05d\xeb\xc8%Q\xaeT\xe3\x1a{P(\xd0\xa4*,\x88|p\x94\xf9o\xecY<%/\xc2T\xdb\xaekP\xf5Cg\x04\x83\xa6\xf6A\xd1Y6\x8b\x05\xc0%\"2\x0e\xa1\x03\xfd\x16|*&\x84\x181\xca\xe4\xdf6\x10\xc2\x0d\xa2\xaf\xc8\xb3\xb7\xe2\xda\xedj\x96c\x91\xd07&3\x0cj\xe6\x96\xf6\x850R\x0f\x0b\x93\xf9T\xe4\x172ODh\xef\xf0\x13\x85U\x80\x03\xedk\xdbiT\xe8E\xb6\x865\xf3\xd0\xb0\xaelO\x86\xcc\xf4\x1f5]\x0caI%_\x8e\xfe\xb9\xbf:\xe5]h\xd7\x16=\\\xe4\xeb)*\x050~\x9fR\xc1\xc4\x97.\xee,G\x81\x88\xa7\xdf\xad\x0d\x12o\x8c\xca\xf2\x92\xb5KH\xae\xe0\xc2\x95_\x96\x82\x88`\x8ef\xb9P\x87\xe2<\xd5\xa0'\x12\xdf\xdb+\xd9\x02\x9c8\x8e\x0b+\x9b\xb80\x17?R\xf1c\x89'\xacz-\x82\xbe\x08\xdd\xa9rS\xa2V\xb3\x1d\xd4U\xc8\x83c\x17\xed.XR\nx\xbb\xdb\xedR\x86\xb9\xaa\xdab\xcb\xe3/W\xcc\x1c\x05<\xf8\x915\xf0#\xe7$\x91\x99N\x1cy\xfe\xd3E\xa64'\x13\x8fJ\xb4\xfc\x83A\x14\x92\xffJ\xcb~ \xca\xad\x8d`p5\x80e\xd1\n5\xa9\xd3Y\x80BM\xc1\x0c#\x12j\nD\x04BM\x91p\xd8\xd3\x14\x89(\x83\xba\"\x1eWPS\x84\x91\x04u\xefE\xc8@\x8d\xd62\x8fa\xa6\xf9N\x0er\xa5\xf9\x94\x85\x052N\xcc\xf0\x15\x8f\xc8a*a\xc1\x174\xa5\xdcU\\7\x05\xe6N\xab\x98\xc3jy\xbe\xb0j:\x19\xbb\x10\x96L'C9\x9f\xeag\x10\x0e\xee>\xc9n\x00\x8a[\x13\x17\xac\xf3s\x92\xbc\x8a\xa6Y@,WA?4\xaa\x1f\xca\xd2\xcc\x0d\x1eI\xfc\xf0\xa9\xa3\x1e|\x8aUt\xce\x85\x98dh`\xef\xdeE\xab\x0b/\x1eB$\xfa\xa9\xd42Y\xad\xde(\x84\xd2\xcd\x89\xfc\x8e\x86*\xda\x94\x90\xfa\xa8\xf9\x89\xbb\x05\x14\xe0\x00b\xd0\x8dMX\xd9V\x1c\xb6\xe0\x1f\xbe(\xd5\x03be\x87v\x7f\xf7\xa1\x9a\x03\xd4\x17E{=]^QVT\xc9\x1c\x9a\xe5E\x95l\xa4^^\xb4\xaf\x16%\xdcfU=\xa8&\xcc\x0fWy;\xa3+\x82-\xed\xef1\x9e\x88\xae\xdb\xae\xa3\xb6\x1a\xf0\xf3l\xdf\xd1\xa5*]\x19\xcfg\xd4'\xa6\xe5uN\xeb\xd7\xd9D\xcdoJ\xd0^\xd4r\x07\xd2\xb9a\xba\xff\xb2{.\xf8\x02\xd7\x1d.\xe9\xea\x9c\x7fho\x88\xb8=\x172\xf5\x03\x9br\x9f\xc8v\x9d\x9f#\x13\xd6s!.*\x11\xc7a^E\xb9 \x1d\xea\\B\xc5\xa5|7\n\xdf\xc7\xc1\xd1\xc2\x0b\xe7\xa4\x95+V!\xe6\xa5^<'i\x9dCN\xd4MH\xca\xc4\x00\xb3\x80\x97\xc5\x81JE\xc5\xa3\xf1\x8b\xbeq!\xea\x06\x917=]\x91I\xab\x01GL\x0e\xebR\xa6\xf7\x10\xeb\nA\xeb}\x1c\xa0\x87\xb9\xae\xc64\xba\ni7j\xba\xf3|\x0c\x08\xb7S\xcc\x8e\xd0j\x18z\xb8\xa1\xe7\x9ax\xb3\x88\x89\xc1.\xa6\x98\xb2Mp\xc0\x14\xae\xd87\x99\xd2Y\xe0\xcdrw\x15\x935 \x85t`\x1b\x06.f\xf6>\x0eZ\x0d\\\xea;b\x82W7\x8b\x83\x0d:\xc4\xb1z\xf1\xa4~\xff\x88G\xc0\x89\xa2u\xd0]yqB\xd8\xd7\x8e)\x834\x19[Y\x1cPq\xdb_z1\n\x91\xd6Y\x1ew\xd2\xac\x9c\xa5\\\xd8\x95\x1fN\xa3\xabn\x10\xf1k~\xdcW\x93\x08#\x1f\xdc\xbfoA\xa7Rc\x11%\xa9\xe6\xf5\xcaK\x17\xe6\xeeXmJ\x98\xf8w\x0b?I\xa3\xf8\xba\xfa\x06/v\x98\xcc^-\x93un\\\xac\xb4,\x97\xc5\x1c<\xa0\x83e@KH\xec{\x81\xffK\x0e8]\x86\xde\x9b*\x1am\xb4>b\xd3\xccIz\x14\x853\x7f\x9e\xd8\x0eE\x8c\x84\xa2\xf4\xd8\xa0p\xc1I\x11I\xc7\xc4n\x86r\x899\xef^\xe7\x12Pj\x88v\xc5]\xb2\xf0B\xa7\x0d\xa5\x81<\xb5 \x99\xbe\x0c\xa7\xe4\xe3\xd0\x90\xc2\x1e8\x03$\xe1\xae1\xcb\xb1\x89FE\xe1\x0b?HI\xfc\xc5H+\x03\x7f\xe0]GYZ\xa6k\xacc\x9d\xfd [t\xae<\xd1\x0f\x02\xc9q\x8a\xb4\x90\xa1F\x14'\x14\xd8\xa6\xf8\x92\n@\xab\xfap\xdag\xe9\xa5\xd6\xf9\x88b\xae'\x9dbL;B\xdfF\xa5\xb7\xe3\xea\xa8\xf1\xbe\xcd2\x1a\x98kl\xc29g\xd5\xbc\"L\xd9\xd4\x8cYf\xa0\xb5\xc6\x992\x88T^\x10\xf4\xf3D\x9du\x8b \xd6a\\\xcau\x86f\xa5*\x11Z\xc5\xea\x8e7\x7f\xc4.q\x9a\x08\x02\xde\xa8\xd1\x1d\x1cr\xa2P\xb7\xe9\x0b\x15\xb0\x86\xe0\x9bU\x981k\x7fc\x1a\x03Hg0v1F\xc7`|e\x0bl\x10OkZ\x03z\x9ch(j\xbc\xb7o\x81D\xe2\x06\xec\x8ep\xe86g\x02\xe7\xd7\xa53\x816\x94\xf3\x1c\xe9\xb8\xd0\xf8vK\x10=C>\xe4\xf6@`Z\xce;\x9dy\xc3\x1eb\x80\xd1z\x07\xca\x0f\xbb\xfb.\x11\x13s\xe5\xb8h\x18!n\xae\x89\xf7!\xb6\xf5\xcc\x98pU<\x11\xab\xf8\x8d!i\x9fx\xd0\xc9\x8f\xae\x93\x1f\xce\xb9\x95b\x97\xffIwHVK\x1e\xbc\x9a\x9bqk\xe6\xf9\x01\x99\x1a\xda\xc4\xf3\xde\xebN\xa2\x00\x15\xf3V\x8c\xd9=!S\xdf\xff\xff<\xcf\xab\xb3\xac\x0b\xd0\x11\x80\xe1\xa7y\x9c+\x83\x0f\xa2x\x16\xb5\xf72<`\\=I\x9bb\x17f\xfa\x15TIW\xd3-+}\xa6\xccFh\"\x8eO\x9e\x9aYh\xadE:?\xdd\xfeP\x1f\xdc/5\xb6\x87\xe2\xe1\x1b'\xa50\xad'v.\xe7\xcek\xac\xa4(\x03\xb6j\x98\x03\xcb]\xd94\x054\x07e.S<\x9f\xdd6\xff\xb0\xf6\xb3E\xba\x0c^Dq\xfeQ\xd5uK<7.\x18\x87\x88\xf9\x95\xf2(f\\`\xf4\xf0\n\x86\xa2\xad\xf9;\xd6g\xd3\xdc\xfci1\xbe\xfa\xe9L\xfd\xc4\xbb\x08\xc8t\x08Y}\xc5(d<\xeb\x90\x116I\xd0\xad\xff\x8e\xaf~PO\xb0\xeb\x808uLL63{[\x08b+\xc9\xb0\xcdH\xc2\xd2\xac\xd6\x01RF\x10\xd1\xf4v\x16\x07\xdb\xfcS\xe3\x87)\xaa\x8dY\x9a\xad\x1az\xaa\x01({c\xfeFl\xa5\x02\x94Y\x1c\x98\xab\xb7Z\\\x9e#\xd1pi\xea4\xef7\xffV@\xe4\x19\xbek\xe1\x13\xf8\x93\xcbaem\xf5\x03u\xc1:\xfe\xb8\n\xa2\x984\x05;3\xa2\xc4\xd4_\xb7F\x88\x14\xb5\xd4\xfa\xcd_\xb7\xf17\xe9\xe3*\xf6V+\xf2\x85;a\x13\xd9\xbem_\x91 b\xe6\x8d\xb6\x9c\xd7\x0efA\xfc\xf9\"\x1d\x82\xb5\xd3\xab\xc1\x86+\x7f\x9a.\x9a*%\xf1d\x0831\x90\x1a6#\xa0\xfd\x9d^y\xf39\x89\xe1\xfdK\xc3\xack q\x89\x80'\xac)\xcb\xa9\xfb\x04\x13v\xb7]\x96\xd2^\x11\x8bS\xb7YN\xb3\x8b\xa5\x9f\x0eaaZ\xc1Uw\xe9\xad\xda3\x0b\x92\x04\x9et'A\x14\x8a\x898\xf4\xd3\xfa\xe3\x87q\x06f\x9an\x92\x7f\x1d\x1d\xa5W8\xf73\xc7\x95\x9a\xbe\x91\xa8R\xceCK\xdb_\xbe\xacb\x90Qojd\x18\x94\x02\x80`J~\xccxy\x7f\x15\xce\x1f_x \xd9\xdfu\xfd\x0f\xcf\xde\x9c\\\xf5\xbe\xfej\x1e\x1d\x1e\x1e\x1e\xbe>}\xbf8~??<<|\xb6K\xff&G\x87\xaf\xe8\xbf\xaf\x1e\x04\xfb\x7f\xa5?\xbe\x7f\xf1\xec\xd5\x87\xe3\xf7\xb4\xc2\xfb\xd9\xd5\xad\xfe\xeb\x05\xbf<\xbb\x1f\xf6\x9e\xcd\x16\x1f\x9f\xad~\xba>\xea}\xdc\xbd\x7f\xff\xfe\xfd\xce\xcf\xeb\xdd\xa3\xbf\xac\xfa\xcf{\x8f:\x9dY\xbast\xff\x97\xbd\xfb_\xf7\xf7\xef\xbf\xdfy\xf0\xe8\xfd\xec\xea\xf9l\xef\xe1\xfd\x9f\x1f<\xea\xbc\x8f\x07\xcf\x07'G\x97\x8f\xe8x\xfe\xfc\xdd\xc9\xe9\xbb\xe0\xd5\xe1\xf1\xf1\xe1U\xf8\xe8\xfe\xfd_v\x0e\xe7\xeb\xdd\xfb\xeb\xef_>\xbf\xaf>\xef_\x91\x9f\xfc\xfe\xe5\xe1\xe1\xe1\xf3\x87\xa7\xefO\x9e}\xf8\xf3\xfcY\xf0\xb7W/\x0e\xa3\xbf^=?|w\xf2\xf1\xe2\xbbg\x0ff\x9d\xf5\xdb\xaf\xc3\xe0\xbb\xc3\xbf\x85\xfb\x97\x83\xc9l\xe7\xf0\xd1/\xf7\xdf\xce\xde\x1c=|\xf9\xf2\xfb\xd0\xdf{\xb1\\\x1e>{\xf5\xf0\xc5\xab\xc5\xd5\xbb\xfe\x83\xc9\xa3E\xb8\xf0\xff\xf6M\xff\xe8j}\xfcM?]\xbe}\xde\xfb\xf9\xf4\xeb\x9f\xf7\xe7\xdei\xfa\xed\xfd\xcbW\xdfy\xe1\x87\xe5\xe1\x87\x93\xe7\xef\x83?\xf7\xdf\xac\xb3\xec\xdd\xcb\xd7\xd1\xfe\xe5\xa3\xde\xe9\xc7\xd9\xc3\x9f\x937\xe9\x8b\xfd\xf9\xeel\xd6\x8f\x92\xb7;o\xc2W\x93\x0f\x0f\xa6\xbb\xab_\xa6/\xdf\xa7Y?:\xdc\xfd\xd0{\xfe\xb7\xe8\xeb\xe5\xc7ep\xfc\xfd:}\xfe\xfe\xa7\x9fNw\xd2\xe5\xd7\xcb\x9f\x9fuV\xdf_?\\=\xef\x7fx;{\xf0\xd3\xdb\xe3\xde\xcb\xdd\xde\x9f\xff<\xf1\x9e]\x85\x19\xd9\x9f}\xf5\xcb\xfc\xfat/\xfd\xee\xe5\xfbG\xfbo?<\x88/\x9f\x7f\xfb\xe7\xd7\xdf|\xe8=\xffz\xf7\xc5e\xf4\xf5\xf2\xc5\xea\xf5^\xf4>\\\xfb\x0f\xbf\x8e\xc8\xe1\xe0\xfe_\xbeK\x96\xdf\xfd5\x8b.?\xf6\x12\xff\xa4\xff\xd5\xc3\xf4\x9b\xcb\xd7\xfb\xe4\xd9\xa3\xe4\x9b\xab\xbf\xac\xee__/'\xd7\xde\xdb\xfb\xef\xe2\xb7\x9d\x93\xb7\xcb\x8bW\xaf\xfc\x8f\x93\xbf|\x98\xbf;\xe9{\xef\xff\xf6h'\xfa\xea\xbbd\xfe\xdd_\x0f\xbd\xaf\xf6\x8f\xaf\xe8\xb2\x1c\x9e\xbe\xff\xf0\xe6\xe4\xeb\xbd\xa3\xef_\xbe\x1c}F\xd0\x19\xd2\xbd\xb8N\xc97Lj\xae\xd3.\n\xad\xe2\xc4N5\xf2\x18\xaai\xc6=\x8d\x84\xc34-\xaa\xe9\x1c'\x16;\xf0\xcf`\x87\xd0\x81\xd8\x81\xfb\xb0\x0b\xdb\xd2]\xe9\x8d\x0b\xa4\x9bF\xcf\xaeS\x82\xa6a\xf5\xd7f\xb9\xe9 \xb3\x10\xc4Q2\xcb\x17:*\xe6\xfc:\xee\xf3\\\x14!\xb9\x82\xa8\x92\xe4\xa7\xc6N\x03\xc7I\xa0C+\xb1q*f\xc3x{\xe6BF\xe99%\x06=\x97\x05q\x86\xa7\xd0\xc3\x0b\xe2m\xd8\x85!\xad\x120\xfb\xc5\x00\x9e\xc0\x8c\xfe\xd3\x19\xc1\xae\x83\x90\xf5\xc7iw\xb2\xf0\xe2\xa3hJ\x0eS;p\xce\xe0\xc9\x13\xe8?\x84O\x95\"\xe8@\x9f\x17\x0f\xf4\xc5\x03V\xbc\xaf/\xddq($\xc6I\xa7\x83\xe6\xfa\xf0\xf4)\xf4\xf7\xe1\x1e\x0c\xf6\xf6\xd4\xf7\x0f+\xaf\x07{{pO\x0d-5@)\x9bI\xcf\xe6\xc9\x18\x06K\xe7\xf2\xf4)\xecV;Q\x18\xb3~\xab^\xfa\xbdZ\x90\xed\x9a!\xf6\xf4)\x0cZ\x03\xc0\xd1\xa2\xb4WF\xe0Y\x1c-o\x87\xc2B\x97\xc5\x8d\x12\xe0\x8f\xb0\xc3\xc2=\x8e9>\xf782\xc36\xf8,\xc7\x83G\xff\xe9\x8c\xa0\xbf\xbf\xf3p\xc7\x81\x88\xb1\xe13\x8a\xe0\x99\x8b\xd1n\xb1\x04\x9e\x82\x07\x07\xe0\xc1\xb0x\xa7\xb2\xc0\x0c\xd2>\x1c0@\xa7c\xda\x0d\xdd?\xbc\xd1x\x8c\xc0\x19\x9c\xd1\xcd;&\x0c\xae\xf7`\x7f\x87\xbe\xb0F#\xcbq`\xc8\xb1\xc2\xcf\xd7\xcbf\xed\x0cp\x1d\x1e:\xd016\xdc\xef\x89\x96)b\xe4-\xf3\xae\x06RW\x15\xee=\xbf\x93\xfe)\xf2C\xdb\x92\xec\xb4$E\x91d\xc5\xc9 \xea\xf3\x7f)\x84\xa5\xf8\xab\x92\x9f\xdc{?L\x1f\xb2u<\x90\xff\x18\xb2\x90\x88lQ\xac\xc3gG\xcf\x8f_|\xf5\xe7\x97\x7f\xf9\xfa\x9bW\xaf\xdf\xbc\xfd\xeb\xc9\xe9\xbb\xf7\x1f\xbe\xfd\xee\xfb\xbfy\x17\x93)\x99\xcd\x17\xfeO\x97\xc12\x8cV?\xc7I\x9a\xad\xaf\xfe_\xea\xde\xb4\xc9\x91d9\x0c\xb4\xdd/k\xf6\xfe\xc2~q\xa4\x86\xdd\x99\x83\x04\n@\xdd\xa8F\xd7\xeb\xd7\xd3#55\xd3\xfdl\xaa\x1f\x9fH\x00S\xcaJ\x04\n9\x0dd\x82yTW\xcdT\xafQ\xd2R\xa2H]\xdc\x95(R\x07\x0f\x1d\xe4.IQ\xa4\xb4\x07wy\x99\xed\x9b\xf9#\xfa\x03\xfb\x17\xd6\xc2#\"32#\"\x13\xa8\xaay\xd4\xc2\xac\xbb\x00\xcf\xc88=\xdc=\xdc=\xdc\xafo\xbe\xec\xf5\x07\xbb{\xfb\x07\x87G\xc7\xed\x1d\x8b\xa7\xcbat\xa4\xc8g\xe9\xc1\x13HN\xa0\xdd\xf6\x1cqS+\xc3+b\xc18\x93Q\xd9s\xe8#O\xe7\xec\xe0\x9b\xa9z\x9e\x1d\xa4\xf4\x14\xc35\xc0O\xc0\x1e%c\x0e\xa4\x8b8z\x87\xc4\x13\xa3\xba\x15Q}\x99\xc3W\x178\x1bAO\xd0\x0b\x02\x1e\xac\xb2e\x1a\xac\x97\x98\xf0f\xaf\xaaE\xbb\xca\xef\xe7`\"\x95\xd7s\x9b.\xa6v-;\xfcN\"\xb0x\xad#\xbc\x03=\x0eq\xa3\xe4\xf1\xc8\x87\x8c0\xd3\xfeN\x8b%\xd7\xcc\xc3\xdcD\xf1s\xa4\xe0\xa1\x90\x85+.m\x90\xad@H\xff\xb4G\xb0\xeb \xc2\xd8)] Jr(\xf5\xec\x1f\x1c\xf6\xfb\x07G=\x8a\xd7\xf4 \xba\x8c#\xa6St\xdd\x1f\xf0'\x8c|\xb0\xe7\x03*\x9df\x02\xf3\xed\x88y\x18Q\xfc?\x92p>B\xc8\xa0\n9\x90\x00\x07\xbb\xf0\x08\xa2\xea\xad+>}\x99f+\xe4\xdf\x82\xb1\xd5\xb1d\x0c\xea!\x06\x1d\x0c(jY\xe7\xbaG\xbbZyC\x9eM\xd2\x8d\x897\xab\x0b\xbb\xa7\xa0\x02\x0b\xabM\xe7\xfa\x08>\x84\x80\xca\x02\x942\xa8\x12\x05\xdd\x17v\x9f\xce\xab\xe7\xe8K\xf80\x82\x04\xe7L}F\xd9r\xe7P\x85\xa3\x9f\x10\x9cb\xc3}\x18BO-\xb2\xe6E:\xf4\xb9\xa6\xea\x05K`\x04m\xa8\xe6T@\xc4B^\xbff\x14f\x01\x8f\xf8\x18:s6\x08X\xc0\xd3\xa7#\xe8\xcc\xa9\xe4\xd0\xa6;\x18\xe6t\xdb\x9d`\xf9\xc1\xfe\x01|\x88\xe1\xb2E\x03.\x88\xfa\xe6\xd0\x19\xc1\x91\xa3i\x91\"p\xa4\xb6\x14\x95[\x8a\xf3\x96\xb2\xbc\xa5l\xf3\x96(\x91`7 #\x07\xfb\xda\x87N\xf5\x06\xaa\xe1~3}5\xc2W\x8b\xcc3\x19\x9c\xc2+\xef\x15\x9da\xd8\x81\x1e\x15\xbc\x16\xf9\x9ck\xf44\xc8\xf0>\xf5\xd2Ew\x1d\xbd\xb3\x07\xec\xee[D;Z\xbe\xc8\xaa7\x17KU\xe3\xa8?,U\x15Q$\x94\xf6\x0ce\xe8\xef\xe2 \xad^\x93\xa9\xcdiBq\x9b\"6\x0b\x19\xcf\xd1\x9b\xd6\x1c\xe8\x91w\x9e\xa3\xb7o@o\xf4\xb00\xa07\xc5\xd1\xc1n\xce\xbc\xe5\xd1t\x06{\xb4\xc2\x12\xe8\xf0\xd0\xd1\xe3:\xc5\xe5\x98\x93\xd5H\xdf\x8d\x19/B\xa7\xaf\xa3y~\x85\x12\xd4\x13\xe8\xc1\xed-\xbf#\x8b\x8e\x1b,K\xc4\x13\x14\x8cq\xa7i0\x97\xce0v\xd4\xbbH\xd0-)H^y\xafl\x82>\xf2\xcc\x90\xca\xd0\xe3\x14lJ2\xf2\xc7\xbcJF\xbc\xe7tp\xb8\x0b\xb0\xae\xf92\x8ab\x1b\xbf.\xa3KZz\x87=\xf8\xe4\xd5\xc0q\x81P\\K\xa0\x8cM\x9d\xccq\xe0 \xf4\x91\xf3d\x9d\x0ee\xcb\x1f\x8e\x80\x96\xa7\x07\x82\x11\xee\x94%<\xa5\xfd9\x855\xec@\x02CXW\x10\x89n\x89\xa5CQ,\xa1E\x07\xac\xb6v\x9b\xd6\xb6\xc3j\xcb\xeb\x99\x8b1\xc9\x83(\xb5\x82Om\x82\xb5u\x18\xe6\xca\x8d\x05\xac\xb6\x11,q\xf8\xc8\xbd*E\x96\xe6\xf7F\xd0s\x9c\x13\x08hcG'(\x9f\xb5aQ\x88\xbd\x1e\xa5T\xed\x11\xcc(\xad\xdeAzA\x85\xa7:\x12\x94Qd\x0e\xe0\x96\xbe\xeb\xd3w\x83\x13\xf0\x19\xc5Q\xaa\xcf\x8a\xea\xb3\xbcz_W=\x7f\x15:0\x9b\xc2\xed\x08\xfa\x03\xba\xb1\xae*\x1c\xae\xe1P,+p\xca\xdb6\xf7\xea\x0c\xed\xdd\xc1Q\xe5\xc8[x\x85\x96\x1dk7i\xb2\xb8\x921\xd08\xdb\xc6\xdd\x9f<{\xfd\n\x1d2\xf9W\x9d\x87M\x9e\xe6fXI{S&yMW8\xccwS\xf2\n\xf9\x85\xdd@{[w\xa3\xf1\x9a\xf4\x0e\x92g\xed\xa8\x14\x0d]LPd\x87\xf6\xee\xae\xe2w\x1c\xf0GG{\x8e\xd6\xa57\xfa\xf1\xba\xf4n\xe3\xdd\xde\xa8KU\xd3(H\xf9\x185q\xbbh\xf9\x8a\xe3.\xf3\x11\xa7\xef9\x1b7\x0b\x924^g\xa5\x8eq\xa5j\x94\xcaxM\xd8\xfc\x9c\x12\x03\x161\xc1\xe0\xc3\x11\xdf\xd4(\x8a\x8bP3\xeclT\xf5\x83vN\xa0\x85>\xfaH\xf2\x92Rv\x00f\xee\x0fy\xbc\x0b\x9e\x94\xc0\x85\x16z\xce\n\xa7!\x96\x1f\xc19\xe1\xe34\x18\x85\xde\x83\xef\xb1\x84 u\xda\xf0\x88M\x15\xcb\\n\xa8g\x1e\x84\xderY7\xe4\xfa \xa1\x9f\x16\xfa\x13%]\xbe\xd4\xd2w\x83\xd3\x18l\xd84\x08\xf9L\x9c\xfb2su\xfa\xf1i\xa1\xda[\xf7X\x9ca\xa7:\xe7\xc5\xa9\xf3\xcd\xcd\x9aTN\x9e<\x80\x12\x0bV\xc5\xeeYf1\x8b\xe1\x11\xa4$\xf6.\x96E\xc0\x7f\xe5\xc2V\xd14{\xf2 \xbcb\xb7\x1a\xdb\xfa>\xbc\"\xb4\x8f\xf6\x1d\x17B\xfb\xf8\x00=\xa5\x8b\x0e\xd0\x96\x06\x1bu\xbb\xe07\xfd]\x1d\xc7 \xed\x03\xc7\xb6p\xb6\xd2(\xaez\xea\xb0\xeb\x80\xbb\xa6x\xe1\x94\x89u\x83\xe4\xa5\x98\xebM4\xc89\x85\xd2\x9eUyD\x15\xdc\x8a\xe3\x80\xa5t\xf8\xeew\xf3\xee\xe1\x9d[L\xb7U\x8d\xc9\x12\x97|k7\x9a\xde\x0dWt\xefAWtww_Y\xcb\x81\xd3\xe5w{\xbc$ .\xc3Mj\x92\xd7U\x9a\xca\xd8\x8e\xbbg\xd0\x86\xb8\xfb\xb1\x0b\x16\xabU1\"\xb2V\xd8\xe8\x0e\xa4I\xdb\x08\xa1\x9an\x9a\xeeU\xaf\x94\xf2\xa8\xef\xbd\xaa\x14\xc5p\xeb\xa0:\xbd,F\xfd~5v\xbc\xc7j\x19T\x8b'9J\xf1\xc9\xd3cj\x0b\xbd\x07C{p\xec\xd8F>-\\\xf1\xbe\xd2\xc4e \x068e\x9a,\x91\x88\xceQ\x0d}\xc8t\x9a?K\x8b\xfd<\x80\xce!e\xe9\xc9z\x19\xa4\xb6e9\x1a\xc7-\x1d\xeb!\xe3t\xaap\x9b\xf7\x8e\x0b\x87\xd0\x1aA\xc2\x82\xd5:<\xcf\x91\x9c\x1e\x91=\"\x8e\x93\xab\x89\xe8\x0b\x92%\x86\x1e\xabj\x85\x88R \xe6\x0cm/t\xces\x911We\xd3\xf3o\x9f\xd9F\x82\xee\x9cYC\xa2\xee\xfc\x84\x9e\x8b\xc0\xd7\xe4\x15\xcak^\xbbx&\xf5\xec\xbc\xd2\xb1\xdfnO\x1d\x17\xcf\xa1\xf4\xd0\x14\xdb\x0b\xa7\xebG\xa1\xef\xa5\xf6\xdc^\xa0\x02\x9a\xc2\\<\x89\xce\xf2>\xdc0\x0b\xcc\x15<\x85\x9b\x13\x07\x96\xec\x9e\xd3\xc2\xc5\xb3\xf3l|Cke\xe2\xc2xM't1^\x1b\xf4j\xd2MK\x18B\xb2\xc9\xe6\xd9\x90\xe4<\xe4\x81\x83\xd6w\\Cr(\x0elRO\xb1\xc3\x95\xbd\x19\x88\x8d\x7f\"\xb5\xda\xdf;vl\x8b\xd6n\xb9[\x88\xc65f\xb8\xc0\x8e\xa9`[Fp M7\x19E=\xf5\xda\xf9\xdc\xfe\x89A\xefv\x928\x1f\xda_xW^\xe2\xc7\xc1:\xbd\x9dy\xa9\xe7\xec\x04+u\xd4;\xe3\xcf'\xd7\x83^gr}\xf8b\xbasY-\x12\xb1:\xc7\x9f\x0f\xa7mg\xb8s\xb9RI\xdd\xd8\xeaZ.X;\xb2\xef\xb9\x19K\x12/\x0c\xd2\xe0K\xf2\x83x\xd9t\xf3@\xd8\x92\x98R5\x15\xd7~\xe8Y\xce\xd2y\xb4n\xb4\x12 k\x95\x85\xde>\x1d\xf7\xa6\x0e<\x85\x8e&'\x95\xed9\xdc\xd6\x84\x8a{\xaf\xbb\xa2\xd2\xb3\x1d9\x8e\xb0-1\x0bm\xdcMI\x922\x15\x8e\xe5]DY:\xbcXz\xe1[\x0b\x86\xe0a\xc4<\x19hB\x81M0\xa0\xc0\xe3\xdd=\xbd@\xb4\xbb\xbf\xeblc\x1e\xc6`\xf8\xdd4\xfa$zG\xe2\xe7^Bl\x0c\xd1\xda\xa6C\xa6t \x03\x96W\xe3\x9e\x1a$\xaa`\xbb!\xec\xe9\xc3:\xf4\x0f\xef\x1e\x98\x027Yy4[\xcaUE\xf7\x0e\xaa h\xf8\x04\xefU\xb98\x93\x05\xaad\x8f\x89\x02\x87U\x81\xc2\x03\xae\xfeS%\x81\x98N\xb8\x14\x93e\xc8\x05\xcarIf 8\x85\xa4+\xf2\x87\xe5\x05\xebg\x0d\xb3\x12V\xe6\x0d\x03k\xf2\xa4\x8e\xfal\x80\xaa\xc2<\x92\x93\x1b\x06<\xdfX\x1b,K-\x9a\xc9E}8\x05_\xa4\xfb\xa3\x9b\xa2\xf2\x82\xe0\xc1DS\x19\xaf\xc2\xeaa/\xc3B\x15;\x1aA\xc7\xa3\xdb\xae\xd3\xa3\xbb\xad)~\x80\x89\x9dm.!t\xfa\xdc7\x83\x07\xc1K\xb9\xa2\xb9l\xf2f\n\x90\xd89\x81v;\x84'\x10\x9f8\x10\xf0\x00\x83<\xbcv\xa8\xe6\xc6\x16s\xfa\xa0\x18\xcb9\xa5!~.Z\xed*\xc7\x11\x15\x8f\x83\x1c\xd7TdfX+\xe5\xb2\xdb\x10\x1d\xcd\x87\xac\x88\xdf\xde\xc6\xf0\xa4\xa5\x12 \xae\x86(qW\xf5\xda\x86\x94G$5\xe8m\xc4\xccUB\xd8\x95\xb4$\xef\x95.\x06h\xdbf]\xd4/`\xcc\x9d\x06NE\x07B\x18\xc2\x8c,IJ\x10R\x8ap\xd8\x8c\xa8\x02\xf5\xaa+\x99O\xfa\xb6\x13-D@1\x88\xbb\xe2\xdb\xee^\x95\xe8 \n\xaeO\x92\xb5\xbb\xaf\xcb\x92\x85\x8c\xe0\x8eC\xc8\x0bhu\x83\x04%zSx\x01:\xa5\x01c\xda\x11\xa3H:r+>\xcc]\xe5\x149>\xe5\x88hZF\xb3\xb2\xbe|\xc2\xcb\xc7v\xe8B_:\x9e\xd0w\x93e\xe0\x13\xbb&\x91\xb27N\xa76\xa5\xaaI\x193\xef\xbeR&-H\x93\xa8 0^\xefe!0)\xdfd\xdc\xd7\xe1\x14\x02J\x8dQK\xf9\xe8\x11\x84\xf0\x94\xd9\xf4R<\xd7\x88\xa6\xb6\xd8\x03\xdbv9f\xa4Z\x99_\xf3P\x98YOx\xfbt\x08<\xc5\x1eS\xda\x1e@\x1b\xbd6P\n\x0c\xf9\x03\x1c\xa0\x93\xbf\x84a\xfc\x02\x87\x91\x7f\xfar\xc8_\x0e\xa1\x83\xceXO\xa1\xe7\xb2/#\xad\xd9\xf0\x8aG\xbc`\xac#@\xd6\x11\xc3\x13\x08N\x1c\x88Xh\xb1t\x1c\xd3\x9e\xe8\xfd\x11\xa3;\xe3\xc6~u\xb76\xed\xe2A#.\x19\xe5\xb3\x94m\xb7\x94\x1dp\x1bIO3\n\x18ZJ\x0b\x15\xc4\x16M\x08\xb2`\x8d'\x93lv\xd4\xebu\xe8\xdf\xf9|>\xad\xb8\xa3\xc7\xa2Po\x97\x15\xea\xed\x1e\xcc'\x93lN\x06\xf8sN\x06\xf4\xe7\xa07\xc3\x9f\x83\x9eZ\x05\x9dd\x0b\x9b\xd9\xf5\xc7\xac\x99\x0bSs\xe8\xd85\xfe\xbc\xa1S\xe8\xc3e\x9f\x0e\xe5Jg\xe4\x00\x8b\xcf\xe6\xf3\xa9\xf3\xd5\xe0\xbd\xa52\xf0\xf2`/\xe6\xf3)\x02|sC o(\xcfk~\x9b\xe7Fw,\x16\x89A\x95Y\xb1\x999\xe9\x11\xf6g>=\x15i\xefm\xde\xe9A\xaf7\xe3\xb5\x8e\xb9G\xcd\x94\xd3\xcd[\x0bEL\xc7X\x87\xe5|XU\xff\xce\xa5^\x8e#\xd1\xd5S+\x0f\xed\xe6BX\xad\xbf\xd2\xef%\x8cx\xb6X\x1bGg\x9f\x8e\x8a\x91\xe2\xa0\xe7\xd0\x06\xdf\x05\xeb\xd2\xba\xeb\x9eH\xf9\xa9r\xe9\xb0+\xc2w\xdf\xc6\xd5s\x898\x10V\xa3\x01\x8am\xac;\xb1\xf0\xd1Z\xe3\xc7\xff\xe5\xe7~mj\xddkd\xf5\xccY\xc8JvdS.\x9c\x1f\xf13<\xe2;\x18\xb7\xc72\xdb=\x1a\xf7rC\x02U\x13\x9f\xd31\x8d\xa8F\xde\xd7Pr\x14\xff\xa2\xdc\xdf/\x1d\xb7\xdb\xc1\x14\xe9y\x00O :q\xd81\x87\n\x06\xe98\x98\xa2\xeb\x8dA\x92l:\xcf\xd4`\x83A\xcfU=s\xa3\x96g<\xb9\xf6{\x9d\xc9\xf5\xec`r=;\xeaL\xae\xe7\x07\x93\xeb9~\x99O\xb2^\x9f\x92\x82\xac\xd7?\x9cOw.kpf[zx\x1f\xe4\xb2S\x14\xdfR\xc7a\x96q\x81>\x11]\xdb\n2\xdd}\x12\x0f\x9dJ\x90\x03\xebG?g\x0d\xc1zV!\x14\xd6\x8f\xfe\x96\x1e\xfc\xb7\xf5\xe0\xbf\xa3\x07\xff\x8fz\xf0\xcf\xeb\xc1\xbfI\xc1\x9e\x02\xfe-=\xf8\xdf\xe8\xc1\xffV\x0f\xfewz\xf0\xbf\xd7\x83\xff\x1e\x05?W\xc0\xbfC\xc1\xbe\x02\xfe'\x14\\M\x91j\xfd\xe8\x0f)x\xa6\x80\x7f\x81\x82\xab D\xad\x1f\xfd}=\xf8\x17\xf5\xe0_\xd2\x83\xff\x17\n&\n\xf8\x7f\xd5\x83\x7fW\x0f\xfe==\xf8\x1fP\xf0K\x05\xfc\x0f\xf5\xe0\x7f\xa4\x07\xffc=\xf8\xf7)8P\xc0\xffA\x0f\xfe\x03=\xf8?\xea\xc1\xbfL\xc1\xaf\x14\xf0\x1fQp\xf5\n\xab\xf5\xa3\xff\x89\x82_+\xe0\xffY\x0f\xfe\xa7z\xf0?\xd3\x83\x7fE\x0f\xfeU=\xf8?Qp\xa4\x80\xff\xb3\x1e\xfc\xbf\xe9\xc1\xff\xbb\x1e\xfc\x7f\xe8\xc1\x7f\xac\x07\xff\x1a\x05\xff@\x01\xff\x0b=\xf8_\xea\xc1\xffJ\x0f\xfe\xbf(8S\xc0\xff\xb7\x1e\xfc'z\xf0\x9f\xea\xc1\xff\x9a\x82\xab d\xad\x1f\xfd\x19\x05\xdf(\xe0\xbf\xd0\x83\xff.\x05?S\xb7\xc3oS\xb8\xa7\xc2\x7f\x9d\xc2\xdf,\x14\xf8\x9fSx\xaa\xc2\x7f\x83\xc2\x93jH#\xebk=Y\xfeZO\x7f\xbf\xd6\x13\xda\xaf\x91\x88+\xe4\xed\xeb\xbf\xa3\x07\xff\xbc\x1e\x8c3\xa0\x10\xc3\xaf\x7fA\x0f\xfeE=\xf8\x1f\xe8\xc1Hh\x15\x8a\xfa\xf5\xdf\xd7\x83\x7fI\x0f\xfe\x87z0\x92 \x85,\x7f\xad\xa7\xd6_#eR\xa8\xf5\xd7\xbf\xac\x07#\x99P\xe8\xef\xd7\xffT\x0f\xfe\x15=\xf8W\xf5\xe0\x7f\xa1\x07# R\xf0\xed\xeb\x7f\xa6\x07\xffs=\xf8\xd7\xf4\xe0\x7f\xa9\x07\xe3\x9e\xfd\xab\n\xf8\xd7\xf5\xe0\xdf\xd4\x83\xff\x8d\x1e\x8c\x9b\xf3R\x01\xff\x86\x1e\xfc[z\xf0\xbf\xd5\x83\x91\xd9\xff5\x05\xfc\xdbz0\xca\x00\xca\xc6\xfc\xfaw\xf4`d\xb1\n\x07\xfb\xfaw\xf5\xe0\xdf\xd7\x83\xff@\x0f\xfeC=\x18\xd9\xb7\xc2\xd8\xbe\xfe==X\xcf4\xbf\xd6s\xc7\xaf\xffH\x0fFv\xf2\x93\n\x18\xd9\xc9\x17\n\x18\xd9\xc9_W\xc0\xff'\x05\xbfU\xc0\x7f\xac\x07#'\xf8D\x01\xff\x89\x1e\xfcgz\xf0_h\xc1\xdf\xfc-}i\xe42\xd5\x981\xd6\xd7\x7f\xaa\x07\xff\xb9\x16\xfc\xcd\xcf\xe9\xc1\x7f[\x0fF\xd2\xabH#\xdf\xfc\xbc\x1e\xfc\xf7\xf4\xe0_\xd4\x83\x91 (\"\xcd7\x7fW\x0f\xfe\x05=\xf8\x97\xf4`\xa4\xdf\x8a\x90\xf2\xcd?\xd2\x83\xff\x89\x1e\x8c\x84Z\x91/\xbe\xf9\xc7z\xf0/\xeb\xc1Hc?S\xc0\xbf\xa2\x07\xff\xaa\x1e\x8cT\xb3\x1a\x93\xc1\xfa\xe6\x9f\xeb\xc1\xbf\xa6\x07#\xa1>S\xc0\xffJ\x0f\xfeu=\xf87\xf5`\xa4\xc8\x8aT\xf0\xcd\xbf\xd6\x83\x7fC\x0f\xfe-=\x18)\xf2\x1b\x05\xfc\xef\xf4\xe0\xdf\xd6\x83\x91\xf4VC\xe4X\xdf\xfc{=\xf8w\xf4`$\xa6\x8aP\xf8\xcd\xef\xea\xc1\xbf\xaf\x07\xff\x81\x1e\xfc\x87z\xf0\x7f\xd2\x83\x91\xc6*\"\xe47\xbf\xa7\x07\xff\x07=\xf8?\xea\xc1\x7f\xa4\x07\xffg=\x18I\xef\x0f\x150\x92\xdew\n\x18I\xaf\"\xe3~\x83\xa4W\x11f\xbf\xf9c}i$\xbd?\xa3\x80\xffD\x0f\xfe3=\x18\x89\xe9\x97\n\xf8O\xf5\xe0?\xd7\x82\xbf\xc6\xd5y\xa92\x1e\x9c\xab@\xe1<\xdf\xb0\xe3\x9a\"\xb9|\x83\xc2R\xa4\xc2Q\xb0|\xac\x927\xe4\x1bI\xe1\xcab\xf2\x08a\x8ex\xdb\xab\xe9\xee\xa3Q\x945u\xdc(5\x84tL\xa6\xa5\x17\x9aT\x895J!\x83_\xc8\x81>\x1d\x89\xa2q\xcbx\xf1~\xa3\xeaKo\xde\x12zc\xbcK\x92\xf2\xe4\xdd\xdc\xf2\xc6\x9c\x92\xe4\x81\xa3}\x93\xdb]\xb2\xc2\xee\x82\x1aL\xa6x&\x9b)\x9euv\x12\xf4 \xeb\xf5:\x93\xeb\xc1|r\xbd\xebu&\xd7{\xbd\xc9\xf5\xfeEgr}\xd0\x9b\\\x1f\xd2/\x87\xf3i{\xe7\xae6j\xd1\xc9\xf0>\x9d\xf4:_N\xc7\xcf:?3\xbd\xc5\xff\xbf\x1a\xb8\xef\x11v;\xeeu\x8e\xa7\xf4+{\xc8\xbf \xf4v\xfc9\xfb\xd9\xeb\x1c\xc3t\xe7\x8e\xdd\x0f\x99g\xd8Vv\xae\xdc\x085\x99\\{\xfedr}\xd1\x9fL\xaeg\x87\x93\xc9\xf5\x9c\xfe\x87\nV:\xe1l\xc6q\xca\xd9\x9c\xe3\xa4\xb3Y\x9f\\_0\x85k\x8f+\\\x0f\xe60\x99\xa4\xf4\xf5\x8b\xc9\x84\xbe\xeb\xf5P/;\x9fO&\xe1d\x12c\xa1\xc1\x11\xfbs<\x99d\xfd\x83#Z\xa2\x7f\x84\xd6\x16Z\x11\xfb\xd3g\x7f\x06\xec\xcf.\xfb\xb3\xc7\xfe\xec\xb3?\x07\xec\xcf!\xfb\xc3\xea\xec\x1d\xb3?\x1ek\x81un\x9f\xfe\xd9\xed\xf5\xaaq\xae\x98y\xcd\x826\x0b\xecm0\x9d\xcd\xda\x96\xba\xe1P\x0b=8\xe4\xc3>\xbc\xd0[\xc9\xe8R\xd3I\x9d\xd3\x99\x9a\x1fL\x98\xb6{r\xad\xda\xba<\xad\xe9Mt\x0d-A\x95\x06\x8dU?\xeb\xfc\xcc\x84)\xdaQ\xd3\xceT\xed\x93\xeb\x191\xd9\xd7\xb60\xe4\xf9w2\xe4\xa1\x89l\xbcq\xbf\x96\x92E-\xcb\xed~\x9e\xcer\xb6\x96\x8a\xce\xeb\x8b.x\xd1-\xcd\x07\xb7&\xdb\xa9S\xb5>\xce\x8c\xd6\xc7\x85\xc1\xfa\xa8\xb5\xb5\xe2\x1d\xe8\x8d\x0c\x92\x0b\xbdA\xf2\xaad\x90\xd4\xd7G\x9f\xcd\xca\xaf\xdd\x14&\x96\xf1<\x8fs\x8f\xf3\xdf\xa6\xd3\x86\x96:\xfbt8\xbb].oW\xb71\xb9Mn\xd3\xdb+\xe28\xa7\xdc^9\x8e]\x98\xbb`}`\xa9\xf6NX+\x15}t\xfb\xc9'\xb7\x9f\xde~\xf6\xe2\xf6\xec\xf6\xcd\xedO\xbd\xa8T\x04mX\x9a*+\xfa\xb7\xdc\xa4\x7f\xe2\x8d\xa6\xe6-\x17\xf7\xfb\x87\xf6\xe9\xb0\x7f\xf6\xe6v\xf0\xea\xa3\xdb\xdd\xcf>\xba\xb5O[\xe3\xfe`w\xeaL&\xb37\x7f\xcd\xb1OG\x93\xc9\x05\x92\xf1\xf3\xa9#\xbf\x93\xa4\xb7\x83pv\xbb\x1b\xcfJ\xef\xa4\x8b\xfc\x9dg\x9d\x9fa\xef\x04.\\I\x03\xbb\x97\x8dJ0\xaf\x9b\xcd\x98\x97Y\xe48\xa8\xe6\xf4a\"\xc7a\xd5\x05\x98'@\xeb7:\xd0V;\xcc\x82l\x06_\x12vw\x9b\xe7\xc6\x9cy\xa9w\xae\xcf\x7f\xba\xf0\x92\xc5\x10o\xb6\xc5\xae\xf2p\xe5\xad\xf1\x99\x1d\xd1q\x07\x1a\x0f)\x91f\x0b+(=\xbd\xbb\\\xa6\\\xc6\x11rYU^\xe3\xf6o\xc55\x97\x0bf\x8a\xdb\x8b\xc7\xe1\x03\xed\x9d\xdd\xc4\xec\xc8\xa8\xb3%\x87\xdb\xd9\x92Y\xd6\xcc%\xf1b\x1b-\xc8\x04\x03\xb9\xe8\xa4_1\x13T\xd2U\xfd\xcaD\x18\x7f;f\x1e\xeb\xe3\xfe\xb4\xde\xb4N?\x89\x9c\x0b\x92\xf6\x81e\xed\x92\xc1\xdc\xab\x11\x13x\xca\xf0K\x82\xf2i\x19\xb8\xf0(\x12fe`\x82%\xbd\xf2\x1d\x8f-/u\x1c6\xca\xd2Z\x84\x970\xb5\x9d\xf1d\xfa\xd5\xfb\xdb\xe9\xce%\xd2\xf1\x0f\x1eYR\xb1r3\xb7\xf9}\x07\xa7\xfb\xe1)R\xf4\x89\xed\xdc\xe2\x06\xea\xb69`\xea`M\x1f\xf4\xbb\x1f\x9e2~\xf5\xc1\x9d\xe9z\xcbn\xa1\x0b\x1b%n\xc2\x03\x01o\x1e`\x18\x8d!x\x0e\x13\xfb\xb3\xd2\x8d\x9f\xcdQ'\xcf\xe5\xa6$\xbe\xccs\xb9\xed\x8c?\xefN\xdb\x1f\xect\xc95\xf1m\x8cR\x16\xe0m\xa8\xe2[\xf7\xe5\x8b\xf3\xef\x7f\xf6\xfa\xcdk\xbc\x87j\xe1\xa5\x15\x8b\xdf\xf6Kb\xdf9\xefw\x99\x03W\xd9\x15\x7f\xbb\x99hE\xcc\xd9%\x08\xb7M\xfa)\xed^gl\x9d\x9f\xfbQL:_$\xe7\xc9\xc2\x8b\xc9\xec\xfc\xdct\xa7\xe8\xae*\x05\x8dc\xff\xc6\n\x83\xe6C\xdbf\xb3&\x18\x03\xd2\x96\x85\x87\xac\xe3\xd1\xa3\xdc5\\\xa6I\xe3T\xef\xe6Y\x90\xa5\x0e\x0b\x1e\xc6c\xc6\x90;\xcf\xbe\xce\xfb\xd3:?_F3/Y\x9cSF\x7f\x9e\xc7\x94;?\xd7\x1c\xb9\x14\xbf\xf4\xf2\xf6\xdc\x16\xb5J\x93$\xa6\xa3<\x17\xc1\x1cl\xc5\x83\x0b\xa4\xb33Q\xa6\x0fJ\xde\xca<\xc4P\xbe\xdau\x99\xf4\x85\x7f-\xbf\xba\x82\xd7]N\xd9\x8dU\xe12\xfe\xa0s\xff\xe3\x9f\xce\xfc\xda\xc2i\xf9\n;\x8e0\x90\xc6\xfd\xa0\xe3\xac\xc1\xb1\xa61j\xf6\xb2X\xf9\xe6a\x16;\xa8]\xde\x89L\x18\xeb\xbb\x10\xb2\xdb\xc8\xe8\xc7')\xd7\x08\xf7\xfa&L8\xb8/uh\x12I\xc6\xd3\x07\x12B\xb42\x08\x0b\xd5\"\x89a\xebe\xe0\x93\xa6\x89\xdf\x08\xb9\xf4Bo\xccPH\xbb$-;\x14\xc1\xb6l\xba;\x8b\x04i\x1d\x8c\x1aE\xba\xebh\x8d\xa9\xda\x0bl\xc4k\x15.t:\xf9\x1c\xb9\xd0\xbb\x13\xbb\x15\x93\xf4\x974\xf8\x90\xc7\x13+T\xb6\xe3p:\xee7q\x9f\x87\x1cI\xee\x8b[\x1e\n\xa5t\xa5\x9b\xb1\x0f\xdf\x93Mw\xb2:\xad\x18q\xca\xae\xb9E\xc7\xa7\xd5n\xb7%\x0c\xe1at\xc6\xb4\xe1)^\xb3\x0f\xc7\x01\x9dm\x96\xe0~\x83}m\x1e\xed~\xe3hM\x18\x14\x8bT\xa5\x0e?P\x99n\x96\xdd\x95\xfb7\x12#3r\xb3\x1b\xa1\xa9\xb6;\xf2\xd5Q\x8clb\xb1\xac\xdb\x12\x80e\xcd\x96\x00\x17Q\xb4$^\xc8!\xa7\x94\x0d\xf0T\xae\x16\xb2\x9d\x94\xae \x93\xc8F\xf7\x90)\xb7_\x8c\xd2&\xc0\xb5\xb8$\x1b\xa8\xee\xbf\xdd.0\xd6\xf4-v\xa1f\x03\x16\xdd\xd0\xef\xbe\x101QO\xd3P\xd7\x80\x95\xbbe\x86\x1brv6\xcaoW\xf5\xef\xb7\xedv\x8f\xf6\x1c;\xb4\xf7v\x0f\x9c\xad\x8c\x90\xe63{_\x7f\x1f\xeaPw\x18\x0b\xed\xc3\x83\xc696,s^\x80q\xb3\xcc$\xd0zE\xe0!\xdd]F*\x0c\xb7\x02\xbci\xad\xbe/\xeaH\x04\xb5\xdc\xd5\xd4\x00\xfc\xaed\x84\xe1*\xc3\xda\xbe\xcb\x1f>\x8e\xc4\xf6\xc6\xe9\x14/lx\x86l\x17\nT\x85\xd0^\xfa\x94\xe0\xe4\xd3a\x14\xe0}\xe4Jp\n\xde8AQ\xdc\xa7\x82\xaa\xaf\x91\xc7\x01\xee\xa3Q<2\xdc\xa1P\xe2\xf8p\xbd\xeb\xd1\xde\xd6\xa8 \xc8l`\xa2\xf8\xfd\x928\xf4\xe8\x11\xa6*\x18\x0f\xa6\xec\xd6*\xfd\xde\x9b\xba\x0c\xd8\x9fR~\x96\xb7\xa5\x18\x8e\xa1z\x04J)Af<\xd4Ub<\xdcu\xd6\xfa\x87\xd5\xfbF\xe2:\xa1N\xe5\xd5W\xd5]\x83\xa69\x14wx<\xddd&H\x98\xf8]|e\xf8\x18\xba+`i3b=\xe5\xa3\x0d{\x0e\x96\xbc\xc1(M\x0b\x17f.\xac\xd9\xaep\xe1\xca@1\x91\xee\xca]\xbeAO\x8b\x99\x0b\x0b\x17\"\xb8\xe5w\x0c\xaf\xe8\xa6\xbc\xa9\x1fA\xcd\n\x8a\xb7\xee~\xfak\xbc\xad[]\x91\xeaA\x94Yy\xb6:\x8b\xdeC\xdel>L\x91\x8d\x85dZ\x96\xcb\xfd\x0f\xdea\xb91\xd1\xdf\xcd$\xc6\x07j\xeb\x9e\xa2\xa1>|P\xbf\xaf\xf7b\xea\xf7\xaaV4$\xd5\xbd\xc6 \x1f\x9b\x1e\xf04\xc4\x17D\xf4\xcbh\xae\xde\xd7\x04I8\n\x0d\xb5@.\x1dQF\xe7 &\xfa\x042\x16C\x9aO\xabW:\x13\x96\x11\xbd\xdd\x0e9\x06Q\xa8Z\xbd2\x0e\x10)z<\x13?\x85F1YH\xc9\xf7\x13\x8c\xcd\x8cX/\xc8\xee\x1e\xeb=\xd5\xf6zz\x83\xe8^\xbf\x8a\x12\xc8{\x95@H>\x17\x8e\xaa\x885\xe7\xf0*\".U\xb1\x00\xbdI\x84\xad\xeb\x99\x08\xa2WuOY\x94K\xc5\xdeM\xb5\xc4L.\xc18v\xb5\xc8\xd5\xfd5\xb0B>\xb9q\xe1\xd2\x85\x95\x0e\xfd)\x9a$\xdalT\x17\xf8\x84h\x9e\xbc\x83\x11\x9c\xc3),`\x08\x9e\xf6\xddk\x18\xc1E^BW\xc7\x19e\xf4\xb4\xa2wT\xacY\xc3)\xcc`\x08\xef\x1c\xfak\xa6\x16\x7fA\x8b\xd3Z\xaf\xe5\xe2\xd7\xa6\xe2\xcfD\xc5\xd7\xean~F\xf9\xb9\x8f\xd62u#\xe3&\xf5\xe5`Q\xad\xbe\xba\xd7\xcey\\\xe23\x0c\xd5\\\xb3\xbb\xf2\xf6Zgy\x85+T.\xae\x04;s\\8\xa7\x909S\xfc\x06\x9aU\x1bB\xc4\xa1\xefJ\x0f\xd4\xb1\xb5\xec\x10\x1ea\x90|=\x8dz\x0d#8Cer\x1e\xd9\xc8:?g\x89\x0eg\xe7\xe7\xa6\x0c\xd3_\xc0\x08^H\xaf\x91\xeakzj\x87\xf6\xbe/\xea\x0e\x83o)\x8e\xc3)\xa4,\x984*Vk2H\xbe\x84\x11|\x81Z\xd8\xa28\xd1\xcbD\xc6\xc9\xbe\xb4\xdf\xba\xf0R\xcc\xe3J=&n\"\x03\xb5pQm\xb5\xf6L]\xbe;3F\x95\xd3qc\xec\xb1\xfe\xd4\xb7{\xbc\xaf\xf5\x0b\xc9\xbe}\xbf\x90\xaa\x8c&;\x88`\x01o6\xb3\xd31\x99V'\x83~2\x89\xbey\xb3\x19\x06\xb5* \x94#2\xaf\x8eLq\xe0\x88\xca\xbe\x1a\x99v~\xab\x93\x1b\xde\xcf\xe2\xb3\x91D\xc4\x99i\xe8l\xc48\x7f\x9cbXs[f\xf3t\x8aM\x90\xa6&\x8c\x08m\x8acx\xac\x8fi\xac\xb8\x9ad\x06\xa9\x81\xbbE\x1d\xeb\xa5\x80\xbd^\x95\xdf\xfb*_\xa7\"\xc0@\xe5\xfe9\x8b\xfe\x1e\xd3\x15WytI\x1c\xf8\xc8K\x15G\xd5\x92$\x80a\xd7k%\x81O\xbd\xb5N\x0c\xc8\x9f\xbfB\xa5v\xb5\xc8\x8d\\\x849\xb6T\x8b\\\xcaE\xce\x88\"l\xacJ\xcfQ\x97^-r^*\x82\xca\xf4j\x91\x0bE\xee\xf9^6\x9f\xab\x1d~W\x996\xef\xa7\x02\xf2\xaeZ\xe8z\xe3@\x94g(\x17\x9c\xc25c\x0b\xaf\xe7\x1b\x07\xfe\x13\xb4:v\xe1\xda\x85\x17.<\xab\xa2~\xf2.\xc0\x08|Z\x1d\x96\xef%\x04\xde\x0d\x158p\x06\x98\xcayA[\xa3r\x9e\xd0\xdb[`\xcf_\xcf\xe7 I\x8b\xe7\xecw\xad\x00B?)\x06\x10\xbb\xc0 vy\xf4T\xf6K-\x8f\x1d\xbd\xd0w4\xb7|6\xf5\xb6\xf5\xc2\xa6\xc4=\xc0\xab\x1e\xec\x1bqtY\xbf\xb1\xb5\xa5\xda\x1a\xc2\xd7\x06\xf8Um\xef\"\xbb\x9d\xba\xd0\xd6i\x9d\xf1\xedE\xed\xdbi7\xf4V\x84\xe9/\xf1\x1b\x06jY\x91$\xf1.9\x98\xff0T\x7fc\xe8\xf4\xaa\xbeYfYR\x83\x88\xe6\xef\xcf\xf4\xef\x0bQ\xcd3\xbcvi~\xed\x0b\xe6.P\xcd\x1d&>\xb9Xf\xd3\xfa\x13\x0ch\x8d'\xbd\x96\xd0P\xa0\xb4\xfaE#\xf6 \xe9\xed\x19\xd74\x98\x9b{\x9b\xd7\xf5\x16\xe7\xc3 \xaf\xc1\xed\x08\xe6.<+\x0e\xa2\xe6\x86_b8\xc5\xd7\x88\x88\xaf\xd1T m\xe0Zy\xf0Y\xa1\xb1q\xe1\xa5az\xcf\xcd;\xba\x10\xe3\xcfD\xccJ:\xa83\x11M\xb6\xf4\xa2^v\xbc\xbb\x11\xdb\xe9\x16 3\xf5\x94\xed\xae.i\xdb\xca\x87<\xad\x0e\"\x8cA\xf5\xa5\x89\xb7\xaf v\x85\x15\x8e\xdbm2\x85\x11:\xf5\xa7\x95\xcbq\xce\xb7\xa11\xfbv\x86W;65\xa1@\xd3\xb0\x8cx\xb0\xd7\xd3i\xcc\xfa\xaa\x08\xf5@\xda\x03\x9ewO7\x89\xa8Q\x81G\x10\xa8\xf38gv[\xcd\x89\x123\xef\x19S\xa5.1m\x82M\x1c\xc9\xd2\xd4\xf2\x8d\xf4\xa8Hm\x00#X\x9e\xc0\xba\xc6\xe4\x81\xb9\xb9\xc7k\x83]\xa0e\xfb\xa8\xb1\xc0\xdc(C\xc9\xcbn\xe1lh\xe3\xa0m\xcc\xd03YG\x13i\x1b3\x96[\x88>\x96T\x0c3\x0d]\x14\xe6\x82V%Bg\"+\xea\xd8\x0f\x8dCO>+T4\xf4\xe9il\x0dO`i\x9c\x99K\xb4\xa7\x88\xf91\x98UV\xe8\xce\xb80L_\xe6\xe4\xfa$\x1fox\xae\xf0\xfc\xbb@,J\x11\x7f\x86\x90\xd9\xf4H\x8cP\x86^\x89\xc9\x8c,\x9b3\xce\xe1\x94\xf6p4b\xc7y\x8fW\xc2P\x13\xeb=7\x9b\x9cQE\xa3\xe7 \x171\xf1\xde*OT\x83\xf0\x0d2L\x94\xb2\xfd\xc2\xb7\x1d\xfdF\x16u\x14\x1f\x0dI\x88\xbf7\xa6\x89\xbf@!N\xaaU?\xf5\xefP\xba\x93\x8a\xa9\x03\xba\xa0\xfb\xe6\x1dm\xad\xdc\xc9\x80\xa7lS\xa0\x8c\xd3\xdb\x96\xd8\xf0r\xd8\xf5\x0b\xfa\xecBV{#D[\x16\xdb|'\x97}\xc7\xfc\xd0\xd9\xd4o\xc0\x12\x13\x99)\xe7?(\x82o\x99\x88P\xa6\x91\xfa\xeb\x0e{=}\x0c\xca\xbb\xfbN`\x10\xe1\xc8\x85\xe0\xce\xc7\xe2\xbd\x9e\xfe\xbe\xd0Qc\x97\xd4ZE\xcd\x11\x8b\xefnpHc\xaa\xc6\x08o`G.\x84\x1b\xdc\x0ehf\xb2\x1a\xbd\x816^=)\xc5\xa7\xcf5KR|\xfat\x1c@\x1bX\x8c\xfaqh\xf0>\xbf\xfbl\x9b\xf2\xae\xe8\x8c\x11\n\x0b]s\xe6\xf92y\x11f+\x96\xb0K\xd5R\xf0\xd7.I*\xf1[vfNT\xddEV\xca\x0c\xa4#\x15\xc2J#\xa9\xe5\xc6S\x18V\x0c\xfe.\xc46\xcb\x1b\x94\xd7\xa6\x0dO \xd5XD\xb8'\x1aMh5K\x0c\x0c!\xd0\xe3\xa4\xf7-#M}\x92\x83\x9e\xc8\xe9/c\x91\x9e\xe0f,\x0f\xbf\x86\x89a\x8cN\xf4\xe2D\xea\x15\x8d\x83v\x1b\x13\xc4o@\xc1\x9aB^7N\x84\x81\xb8\xdc\xfd\xa6\xe6\x9eAy\xdc?\xd4_B\xd4'\x0dQme<\x81X\xbf*\x82&\x06\x1b\x9a\xee.\xd7\xf6r\xa8\x8e\xc4\x85\"\xec\x84\xb2\x92\xe8D\x83\xa99\x02\xa3\x00\xca\x9e\xb7\xd0\x19$\xd3\x96ZWJ\xb5\x96(\xbci\xcb.P\x0e\xbe\xbd\x859\xfdoI\xff[\xab\xa5f\x98\xb3\xfc\x94\xb2\x8c\x1c}\x99\xae\x8d\xca0\xba\x9c\xa1r\xce-\xa3\x84\x87~)<\xbe}\xcb\xcf74\xbb\xeb\x8b\xf2\xb3m\xb1*\x90m\xdf\xb0.\"8BUS\x01\xb6\xd6^LB\x0e\xc0\xf7\xd7\xac S,I\x05\x0b\xd5P\x05\xf8Z\xaa\xd2a\xe2\xda\x8d\x0bW\x0e~\x9f1\x03\xf7\x8d\x9e/\xcd\xee\xbb\x8b6&'\"-\xac\xa0\x17\xe9\x89\x03\xb1\xc8\x8a\x12\xea{\x17\xdfy+\xeasS\xec\xe96\xa2\xce\xb6\xdc\xb4?\x0c\xb4#\xe0w\xbab\xae\xa3\xf8\xb6h\xd4\xdd\x15\x1a\xa6\xa4\x1d\xfd\xaa\xec\x16\xe9',\xc3d\x82\xc5\xf4d\xe3|\xfa>^F^\xba;\xe0\xb6w$\xe3\x95\x87\x07{\xfa\x87/\x85\x86E\xf7\xa4\x7f`|dj\xacP\xd9\xe8\x1f=_z\xab5\x99\x99K\x98\xda\xa4\xcfJ\x8db\xa6\xdc\xb1\x0e\x83*o\xea\xeb+\xe9\xeb+\xcfr\xf3G\x05^\xe8\xee\xd5\x07D\x01r\xfbGu58\xae(\x0f\xd0\x18R\x81 \x03H\x05,<(*`a\x0b\xa9\x80\xd1\xfeQ\x85q\x9bG\x05\xfcC\xe2\xbd\xcd\xfb\xd1\xea\xbb\xdbm\xc1\x88o\xc1 '\xf8\xf8\xb3\xd5\xca\xc6tW61\xf7\xc6\x1d\xd9\xec\xcf]#L\xa6fu\xe5F\xfb\xb8F\xf3Ul\xf1\xbeb\xf3\x03\xbe\xcf-6\xc3\xa5d_tr\x18\x1b#\xdd0\x9a\x9177k\x06S\xab\xc0tQx&U\xeba)\xca\xb1\x9e\xb4T\x8f\xc6\xb5\x80\xd2\x10vs\xb8\x98\xe0\x11\xaf\x1a-O>I4~\xba^\x1da\x14\x9f\xfa\xc4\xd3W\xb6+\\Q\x95\xfe\xb1\x98S\\\x8b\xb3\xfbG}'?Zn\xce\x15\xfa\x86\x03Z\x7f\xa3\x03\xdav\xb2eu\xe9P\xf7\x14\xcb \xe3U\x7fx\xa1=\x1eO\x0d\"YHE\xb2\"\x85\xbct\xc8\nq\xff\x97U1-\x9eF\x8e\xb9:\x98\xa4\x8fm\xeeU]\x19\xd2tm;\x19b\xa0<\xe5\xbfQ\xfd$\x99\xbbF\xa0W(\x11>\xc2\xdc\x92{{\xdb\x9cv\xa9\x06E\x8eD\x8e~\x0c0\xe0\xf2\xa1nu\xed\xa6\x99\xba\x9a=!\xf22uW\x1bR\x9b\xca\x92\xf7\xa2\xb1\xd2\x90\x07\x86\x84\xd0\x067\xd9\xbdA\xd5W\x92\xfbP\x0e\xaa'4\xeeC9\xa8\n]\x89^F\xe3N\x94\x8as\x06=t\xf9v\\\x81b0\x0e\xbb\x1axg\x8d\xd0\xa8\x02] 4\xab@g\x08\xad\xe6\xdf\xa3\x07#\x89 \xb2L'\x1a\xb1\x84\xee\xae+4[\xc7\xf8\xbf$\xe4\xd8}\x87\x1dJ\x82\xd2\xbb\xc8\xed\x8b\xd7\x02,\x12\x95\x8a|?\x8eVABD1J\xae\x93hyElV_V*\x8c\xc2FQ_\xc6\xceD\xa5\"\xb9\x90Q\x14\xf3\x9cB\x87\xda\xbcA\xf5\x87\xd2P\xe7c*.;\x96\xb6sM\xc69\xc4>8\x05\x9f\xa2\xba\x9a*\x93\xc7?\x10^\x12Z\xfb\x1e\xdaT\xe7\xb5\x96r\xcd\xca\xa9\xdc\xce\xe4V\xa0\xab\x07\xa7\xd3P\x85\xc6\x03AWE\xbe\xca\x86j\xea]\x0e\xca\xebo\xa8\xc2`\xfe\xafV\x91\xe3\x87\x81\x94\x80\x96MT\x92U_mGovw\x1d;\xb4\x0f\x1d\x17,\xb1&\xa6(5[\xdej\x94j\xe6S\xfc\xf0\x15\x9f\x91\xf4\xe1+\xe5\xcb\xf0@\x15\xf7\x8f\x0c\xa1\xd4\xb6\xb7D\xe4\x82\x87\xb8\xbf\xe7\xf2\xdb)B\xb5\x1e\xd6\x18E#\xaeeW\xb7>p\xa6\x91\x8e#\x9d\xba\x94\xa9Kx~\xb4\xd8\xce\x1cSX[\xd8\\\x8a\xa9\xb9B`\xba\x01\xa9\x0f_\xb57\xd0)\x0b(\xbb\xd4\xc5\xaf\xd2\xad\x86PhV\xcb3\xfewXe\x8bs\xd5\x04\xbf\xdc\xf0\n\xa1A\xc6\xc8\xf8\xe1\xd1c\x99A\x13\xdb\xc7\x95%\xcdW+\x85\x9e;\xd0\x05%\x90Z\x90L\xac\xec\xd4\x90\x07\x17\x89\xd8\x9bh \"\xb8\xc0s\xb8\x85\xe5\x03\xc92\xfd\xa3\x8dn\x83\x1bL[\xb8\xf0\xba@I,\x9d\xa7^|\x96\x86\x1a\xc0)\xa6\xc1mJ|k\xe8\xfe\xce\xf8\xf3\xeex2\x9d\xb6o'c\xfbthwN'\xb3\xb6}:\x9ct'\xb3\xb6s\xea\xdc\xdac\xeb\xf1\xd4\xb1\xe9\xb3\xd3\xd6d\xe0\x8c?\x9fL\xa6\xb7\x93I\xd7\xf9\xf0\xd4\x99\x0c\x9c\xc9\xf4\xd6>\x1d\xe1\x1b\xb7\x93\xf1d\xea\x14_o?p\x9cj^3:\xdc\x9d\xc9\xc4\x9eL\x9c\xd3\xea3\x81\xebGN\x83\x1b\x8a\xe9\xc8\x02\xc5\x0c\xed\x1d\xb0\x9b\xb8\x98N\xf6y4#\x98RV:\x98X\x16r\x14\x11\xfa,.O\x17s\xa2\x8cLGa^GLq\xab\x94C\xff\x83>f\xa2E\xe5y\xaa3A\xc9!%\x18D\x8f:\xd16\x8bH \x8a\xce\x89f\xbf\xf9\x1a\x99I\x06C\xec\xab_\x05\x90,y\"\xf8\x00W5\x84\"\xb4\xa2[\xf1\x14\x026 \n\x8c\x11x\xdf\xf3\x17\xfa\xb8\x07w\xa6\xb4{\xbb\xfa\x83\xc6\xdench\xc3\x1ab\x86\x1b\xb6\xc5\x8f\x92\xe2\x8eK\xdct\x00\xbc\xcf\x11\xad\xd4\")\x9d\xc8\xef:5}\xc35\xfc-mj\x8a\xedL\xd8\xd4\xf4,\xe8\xf0\xae~\x00\xb9X\xe0s\xcb\x07\xe5Q6)\x82\x009\xb9\x15j\xc9\xbcd\xa0\xdd\xf6\xe1 \xcck\xafg'6\x19\xfbS\xa3\xdf\xceR\x90g1\xf7\xd8\xbf5=k\xa1\xbf\x8d\xfa^\xca/s\x97\x1eh\xc5\x074\xac\xd1>\xb6F0\x87SX\xc2\x10Z-{\x0ef\x031g\xa1s\xfc\x9b\xd9k\x17\xe6\xdc\xbekKq\x13\xef\x8d\x87\x06$\xbc\xbb\x97\xc2\xae\xde'doW\xef\xbf\xa2\xca5\xd9\xa6\xc8c\xe8z\xc4\x9cD\x98G\x01\x06\xbcj\xde9w\x9e\xa7\xbc@\x9d\xc2Z,1)\x87\xa8\xaaz\x8c\xdeu\xca7\x91J\xee\xd3\xfd\xb8\x12\xb9\x0e\xee\xd3\xd9\xbd\xdd\xaa2T\xa8\x83\xf4\xa9\xb2\xf7vu\xc4\xe8S/]tW\xdeu\xd3\xb0\xcd\xc2\x98W\xb3\xf5TMA\xcb\xcb\xd5\xaa\x9d\x8aO\xde\x95\x88\x98\xc1+\x13I\xcb#\x93B4\xc9\x13\x9e'\xe8\x0d\xeeA\x1b\x12\x0c\xbc\xe62^\x1c\xd0\xf9\xdeu\\H\xee\x8f\xb6\xc2\x15V\xd1o\xe44V\xf6eb\xde(!\xb4\x01\x05\x9e>\x0c\xa1\xd3wN\xf06K\xd4\xe9\xc0\x10\xda\xed\x88%TW\x90\x85N\x13\xb1\xe9\x91\x0b\xbd\xca$Et\xa4\x9d\x86\xbb\xc7D\xdb\xdbm\xce\xc4_#\xec\x98d\x12\xf8 \xe8\xeb%\x12\xb1w\xe9\xd2\x12\xe8\xa0\x10N`\xd8\x18\xc2\xc1<\x82=\x9d\xa8\xd2\x87\x9d\xaa\"\x0b\xe3\xbbt\x0f\x8f\x0f\x0f\x8ew\xfb\xbb{G\x07\x83\xdd\xfe\xfe!\xd9\xed\x1dm;\x01\xb9\xaa\xfb\x94\xf9^1S\x01\x13\xe3\xa8\x04\x8b_;\x01{\xcc\xc2\xbeu\xe8\xfa\xf7\x1d\xf8\x10\x1d\xeeR\xb1SR:r\xfc7\x92!w\x9d\x0b%^3\xd7&\xe8\xb4\xc3\xaf\xbcW*-\xd8\xf9|\x92\xb4o'I\xfb\x83\xea)\x83Ex\x1ew\xda\xd3\xde\xf5\xb8\xd79\xf6:\xf3i\xfb\x83\x9d@\x15Vv>\xef]\x8c{}\xcdS\x9f=\x8d\xc6\xbd\xce\xa1\xe61\xe5\xe0k/N\xc8\xcb0\xddvI\xe8\x8e\x91\xa3\xbd #`\xbeqR\x95\x10\x05\xb6yc\xa1J\xd3p=\\\xe0\xbf\xd6\xc6\x91\xe6\xd7\xcfN\x8b\xef\xecJ\xb3^\xe8\x89\xd9\xc9\x9e\xdd\x10\xa2\x9b\xa1T\xea\xbd:J\x11\xe4\xae\xa5\x19e\x19\x8f\xda\x95&\xd9e\xb1r2j\x95\x00\x87,\xac6K\x14\xa3\xdd\xc4xN\xf3E\x118\x85\xb9\x9dv\x93e\xe0\x13{\x80j\xa7S\x18\xc0\x10\x8e\xe8\xa8=\xa9X\x84}\xba+r\xf7\x15uK\x03\xb7\xdb\xab\x8a\xd8\x99V \xe7\xa6\x8f\xbdf!\xc9\xcc\x01\x19\xf7a\xb2\x12\xe5W\x86iC)4\xaf\x86\xb2-\x8aGL\x8c\xa1VE\xf1\xfcc\xd3\x172.\xdaf\xf0\x04\"\xe6\xe8\xd4\xc7\xb8q\x81\xed\x8d\xb3)\xbbH\xe6\x9c\x98\xf5\xd1\xa6\xd8\xe7\xdb\xae\x84\x9eN\x18\x82\x0d\xa9\xea\x98L\x08T\x1b\xac\xa7\x86)\xe0\nd\xf2\nT\xef\x1f\x89\x83\x93\xf0\x8d\xd0\xd2\xdeV\xab$\xd5x\x18\x1b\x86\xb1\x8e\x08\xf7e\xae\xe0\x18\x96\xa2\xdfz\xb9\xbe+\xe4\xee\x9f\xe1\x98L\xb7\x8f\x99ne \xc1\xec8~*\x99/\xb9\xd3\x05\x0b\x97!\x9clx<\x18\x92|\x1a\xcd\xb2%\xb1\\\x85\xc1,32,E\x8es\\\xbcs\xbd\x8a\x82/\xc9\xec\xcc[\xad\x97\xe4\xe38Z\x9d\xf9\x0b\xb2\xf2`$=|\x1e\x13/%\x7f\xe3\xd3O^\\c1\x16J\x0d\xbf\xfe\x8d\xd5\xb2\xf2R\x10\xceI,\xfdN\xd4\x9a\xb9\xa1\x1bH\xd7Wk^\x9eh\xf0\xa9\xaf\xa4H \x90\xe7\x87\xf6\xde>=n*H\x85\x8f\x0ev\x9dM\xa3\xb1\xc8|\"\xed\x16\x13\xc9e9\x95\x1a\xcc\xc8\xdc\xcb\x96\xe9\xb0z\xab\xf4;\xea7\x81kj%\"\xf3Q\x8e\x04&\xaa\xcc\xbb'\x90L)\xf3^= \xb2\xa2\xe7d\xe5\x05\xcb-Z\xc8\x12\x12\x7f\x97\xb0\xd5\xe8\xfa\xd1j\xa3\xb6x\xbf\xceg^J:i\xb0\"\xd6\xe6-\xa2\xaf\xc5G^J\x9cn\x1a\xbd<{\xcd\xbc@m\x8d\x1dBs\xda\xc5\xcd\xb9y[\xbd\xcd+=\x9f/#/}\xe0\xaa\x830%\x97\x0f\xdea\x1eD{X#T\x88\x8fX\xe5<\xee\xb6t\x8c\xe9r\x94fQ1\xf8\x0f\xb5\xfd2\xba\xab\x07\xd0\xfaN\\\xe5\xfel#\xb0{.\xc4]\xe6`\x11\xcco\x1c\xadB\x03rC\x8b\x9a\x82H|\x02|>\x8f\xe2\x95g\x88\\EI\x827\xc6\xfc\x91\xe7\x16\xb4!\x98\xa2\x0b\x90\xf6\x12\x92\xc0K\xec]\x90|\x9c\x85\xbecGx\x82\xb2\xd1\x1ek\xfd |\x1bF\xefBxs\xb3&C\xa0\xf5\xa5\xd8\xbb\xba\xa9\xf1M\xc40\xa7J\xa9^u)\x0e\x85\x9e\xf0%\x17\x97\xb2\x9fB\x1f\x8a\x9c\x14\x94\xc9\xe7E\xc6\xfd)\x15\xde\xe4\x9f\x98\xc7\xca8{\xcaR\xe8\xe2\xc5\x81\xf0\xf9\xadY\n\xb4yw9\xfd\xd0\x17\xf1\xb0\x08\xbf\xc4\x17\x10\x8dg/\xf0\xf9\n\xba\xdel\x16\xd0\xc9\xf1\x96\xdfo(?\xc7\xf2AJV\x86\x02h\x14\xe9\x06\xa1\xbf\xccf\xe43\xe2\xcd^\x87\xcb\x1b}\xd1\xb5\\\xf4\x87q\x90\x12ZV/\xe8I\xd3\x9f9e\xdc\x99\x11\xb2^\xdePz\xb6\xfe\xeb\xe4\xc6\xc1#\xff\x07\x1f\xc4dnma\xa5\x94\xe5\x8a\x92ou7\x08g\xe4\xfa\xf5\xdc\xb6\xfe\x8aU\xc9\xcc >\xefM\x16\xa2H\xef\x7f\x1c\xb0\xe0\xb7\x91\xe4\x1a\xae\x176kb\xec\x82hc.f\xc3 \xaf\x8a\xdb6^\x1c{7*\x97\x01\xedy\x01U0\x85\xb7\xf9\xc8l\xed\xbe\xe2\xc1\x06\x14\xcc\xae\xba1\xca\x9fY\xe56\x8b\xfc\xc9E\xf5+*\xd8-\x1cX\x8c\xaf\xa6t%\xe8\xdf\xee\x8c\xacc\xe2{)\x99\xe1\x8d/\xf9Q\xccq\x0d\xd8\x05\xb6\xea\xe3w\x02\xbf\xf0\xf9\x1a\xef\xb9\xcfh\x81\x11\xa46-A\x85B\x83\xd0\x8f\x13\xcd\xb4N\xbe\x03\xb3\xcav\xe9\xd7\x8c\x06W\x90\xbe\xee\xebQ\x01\xaa\x11\x0c\x94y\xf4\x1d\x97\xc5,\xb0o\\\x8c\xb2\xb6\x82\x11\xf4O`\x05O`\xef\x04V\xed\xb6\x03\xb3\xb1U\xee\x12\xa5\x95+:\xb4K}\xb78\xd2\xcfTT6\x91i\x8e?\x0c\x19\xe0\x94\xa7\xb2 \x12v\xbdl\xde\xf5\xc2\x9b\xd7s\xd4\x92\xb1\xaf\xdd\x95\xb7.<5\x9a\xee\xe6\xb2\xf8\xf3:\x9f\x08\x18*ME!\x11M\xe1\xd7\x07lj\x9c\xdas\xfa\x94\xd2q\xd2%a\xb6\xc2\x10\x8c\x82c\xcb\xdf\x87|\xa9B\xca\x0e\x97\xc1\x97\x04\xbb\xe7\xd8\xec5g\xdc\xa3uX\xf3`IX\x8a\x8d\x08\x1d\x9b\xd0\xa5I\x17/_U\x12\xdbU\x19\xbf\x9e\x96\x89\xe1u\x13V\xfe\xd1#\xa6\xb6\x17\x00\xf4h)\xb8\x01{\x8e\x1cF\"C\x8aO\xc6{\xd7x\x04\xd9\x88\xa1\xb2K\xcb\xdf\x1aO\x8d\xb6\xe1\xa9x\xff\xa5\x86\xa7z\xf8|\x13\x86\x19m\xc90\xa3&\x86\x19\xd5\xb3\xf25c\xba\x9b\xf0\xd4\x85\\4\xe7\xa9\xfa\xb23l\x99#\xb4\xbe\xc8\x15\xd26\xfd\xb3\x9b\x9ag\x97(\x86]\xaf\x96\xfa\xc7\x94\x86]b|2\xfd\xf3s|\xbe\x8e\xc9<\xb8\xd6\x97\xb8\xc8kH\xd6\x9eo\xa8\xe6\x1d\x9b\xda0[\xe9\x9f_\xe7\x87d\x03\x03\xcfj\x188\x9a\x07\x1c\x96\xda\xfc\xc7\xc1\xc5\xb3&.\x8e\xd1Y1l\x8c\x15F\xa9wI'\xc7b\xfe\xb1\xf69\x9c\xc29\x15\xcb\x87\x16\xba\xb6;\x94A\xb8p\xc1\xf4\xf37c\xfa\xdc\xba^-\xc3\x043e\x9f\xd3B\xf8\x13o\x03^\x18\x04\x1c\x99)\xa0[\xe5\xdcD|i\xe99\xc5\x07J8\xf0\xef\xed-\\\xd2\xff\xbez\xef2\x08\x0f\\'\xff\xa0e\x18\x96\xc0e\x97\xc7\xe0\xcd\x85\xbf+\xee\x95;u+\x1cbIy\xc3R\x8dZe\xe4\x0c\xf43\x17;\x90\xe5\xa4\xa2\x953?>\xe4\x08U\xfd\xbe\xf8h\xf8\xd3\x8c\xb6>\xdb\xbau\xc1V\xb6n]L\x03/9u\x01%\x9c\xa2\ns\xab\xe7^\x9a\xc6C\xb81T\xee\xc2\x95\x1e\x1b)e?3\xb8XB\xc1\x8a4\xabb\xdfsY\xce6\x9a\x15\x17\xce\x0c\xebb\xdfsa\xb6j\x9f\x97R\nm nk\xd3\x12\x01\x9f\xfa\x17zq\xbbA\x9c~F\xc5ii\xcf\xd0\x9d\xb8\x14\x1b\xf0\x85Y:\xa5}{Q\xb9jh?ct\xa3\xf5b\xfcL\x12\xbcooa-?(Dn*\x8c\x1b\xa6\xab\xd4\x0e}\x8b\x11\x89\xfc\xab\xe8!\xff\xdd\xa58\x1b\\di\xed\xb2\x89\xcf\x15\x8f.YF\x05\xac\x0b\xa54\xda\xd9\xfc\x971\x05K\xf5\xf3\x85\xe8_-\xd3\xae~\xde\x8a\xb78F\x99)\xbd\xf8\xdc\x8c\xf3Q\x0br\xf8l\x9a\xb3,\x14\x9b\xbe\xa0#\xf8\x82>\x91\x80\xcb\xf13<\xf7\xe0\xdf\xf2\xa3\xb7\x14\xfe\x96\x0214f\x82sQ\xbf0\xb5\xa9^\xe4O\xb9\xb3#P;\xef\xca\xce\xe9\xf2\x0cV\x84A1\x00\xbbT\x86\xc1Mv\x19\xe9s\xc5\xe3f\xa6lt\xcd/\x94\xd1\xe3%\xa5\x14|\xa7 \x19\xf5\xa3\xd0\xf7R\n\x1fJt\xf5e\xc3\xb4\xd5\x91Fq\x98\xe4\x0d5\x11\xea\xb2\xb49\x04\xebYx\x93.\x82\xf0\x12|/\x84\x0b\x02\x0b\x12\x13\x83T@;\xedo\xca\x11\xaa\x0d%\xa6s+%r\x0f\xc8g6\xa0\x91|\xe6\xae\xcb\xf8\xbf\xe4\xae\xb1\x12h\xc63&\x94\x17\xf5\x1d]\xd4w\xecT\x96\xb0\x80kl\x85o\xe0\x14\xc6\xfa\xbe\x1b\xfb\xfd\xde\x85kZ\xd1u\xb5\xeb\xef\xb5v\x90\xa5\xd9\x17\x81\xca;\xeci\x19K\xd1\x08Z\xd2s\x05\x82n8vX\xb5:\x01\x1aJ\xfc\xa5\x17{\xb4\xc1!\xb44\xd7\x1b\x83pF\xc2t\x08\xd6$\xad\xdc\xae\xab\x9a\xcb\x00o1\xd4X\xa5h\x7f\xa2\xa2?\xcb&\x13W\xa5<\xc7\xa9\x06\xab\\\x0d\x87\x96<\x05\xf6\xabn1PxK\xec\x0f\x9c\xeeY\x1a\x13O#\xfe\xa3N\x8c~\xb1\xa4\x15\x83\x8a\xf5Jo\xf5\x04\x919\x80\xd24\xcd\xc9\x01=\x05\xd0\xa5\x11\xc7\x1e0\xd1!\xbf\x92k\xb3\xf7\x9c\xee\x17Q\x10\xda\xe8KgYU\xdb\x9a\xf8$\x94\x8c\x19\x84oC4\x08\x1b\xbdD\xd3\xb1\x142\xe0-\xb9I\xec\xd4\x19\xf7\xa6SdyI\xf7\x9c,\xc9\xaa0\xdbr\x80\xa0\xdc\x91\x9bC\x02?\xcaB*\xfd\x84\x12\x0c1\x89\x0d\xab\x0c\xa3-{20%q\x9c\xadS\xcc\x00'\xc0\xfa\x19\xf3\x99\xd3\xbe.4\x14\xf0S2\x957\x95\x87\xf9z\xad\xcd:\xde\xf24l-\x02\"y\xab\xf5m\xa8~r3g\x1b\x1e\x8f\xac\xc7\xd0f\x0epmxl=6\xbe\xf8\x1e\xbd\xa6\xc7dj\x14,7 \x93\xe2z2\xc7\x08%\x94\xad\xf8\xe0\xa5\\\x81B\xfa\xbb\xb9Pv\xc6\x18\xd1\xca\x0c\xf7\x1a\xc4'\xe9\"\xcd\xa48\xb6\xb6\xf9\x0f\x0cty\xee\xcf\xbc\x14\x95RK6\x9d\xb6\xf5\xa45~\xfe\xd1\xb37\xcf\xc6\xf4\xc0)J8\xb9\xe3\xde\xced:\x99>\xdd\xb9t\xc1\x9aN\xa7\xd3\xa7y\xf1\xa7xx\xb5\xa6\xd3\xa7\x16V\xcdW\x13Q\xdf\xe7\xa1k\x96\xd2=\xaed\xc3\xf8\xc5\xf2G\xbb\xb7N\xc1\xc2\x01!T\xd9YpJ1\x90\x0f\x19\x86\xa2\x0b9\x15\x816\xf4\xf1r\x81\xbdd\x89\xb5]T%\xb5zyo\xd1\x13\xd3,T\xbc\xc77no\xa5\xc1\xd5\x8865\x0b%L\xea\xc6w\xf3\xfe$\x9a\xee\x189\xb3~F)E\x19B\xa4\xdf\xd49}\x18\xd2U\xd3\x16\xc9\xc5\xfdd\x08s\x83F.\nS\xe4l\x06e\x13#aC\x08M\x9d@\xca5\x04\xaf\xeey\xd5e\x15\x94\xa9xo\xe0#^\x1d\x1f)\x11\xf2\xc2HL$\x97&\x8a\xcf\xba\x08\xf1\x82 \x12\x89\xcc2\x0f|\x0c\x9fK\xa7$\xbf\x9d`\xa6\x9a\x81\xd14\xce\xd3X*\x95\xd5\xed\x1d\xe1$W\xbc\x94,\x82yZ\x0d\xa8#\x7f*\xc6=\xadKX\xb5|d\x07N\xb3\xc2\x8c~p\xf25gp\xf1\xd1K\xe9z([\n;F\xed\xf5)\xce;\xe3yB\xa1f\xf3\x94\x0b\xa7`=\xd9\xa1T\x8d\xffn\x83\xf5\xd4\x92Kq\x06\xfa\xe8\x11\xb4BZz\x12\xf2\xc7\xe8W\x8c\x17\xc9t\x1b\xcf\xbc\x8aQ\xa3\xd9\xa3\xd5\x92\xf1\x04\x9dr\x8b\xdf]o\xbd&\xe1\x8c\x8a\x0d\xae\x8cO]\x06\x0cJ@\x11\x1d\xccn\xf5\x1c\x17Z\xbdMH\x04]4\x8e\xc9\xf9\xac\x95\xe7K\x9a.i\xa2\x8a\xdd/,\x07\xa7`\x01++=CI\xca\x02\xcb)\xde\x8dq\x85D\xf5|\xfaqo\x08\xd8\x8eiM\xc4\x02\x97\x96\xa5\x15W\xb7\xa4xC.\xa8\"#\xae\x0c\xde\xbd3]\x87\x82\x1a\xa7;-\xcd\xd0\xd0\x0bD\x1a\xf4H6\xa8_9\x0d\x0b\xd5\xb52Q\x16\xf41\xc5\x08\x00\xdd\x04eh8e\x99Px\xaax\xb3\xb5\xc3\xb2\xcc\"\x9c\x89\xcc\x0bW\x00>\xa3\xfc|,A\"\xda\xac\xf894\xb6\xb1\xe0q\xe4\xcd[ef\xe6\xfe\x0b\x863\xe4:}\x13\xf8o\x99\x13J\xba\xe5N\xbc\xaa\x95\x0f+\xc4\x0e\xf5\x1e\xf6\x1c\xda#\x96\x8c\x12\xf2\xd8\xab(\xc9 \xb7\xc79\xe7\xd7V{\xa2\xd0\xb2\x89\x08\xe3\xc1\xd2L\x1agv\xa3g\x94\xf8\xf8]\xb2\nR\xdb\xa2\xd2\x99\xa5\xb5\x9c\x8a\x0f\x15P\xd8\xfaoHT\xeb\xe6\xf1\xa6v\x1e=\xfb\x8a'\xa0[\xbb\x98\"\x91\xb2\xbd\x9e\xa3\x0f\xed\\\xd3\xca\xa5q\xf8\xccf\xdf0\xcb\xe9\xb75\xcb)\x95\xf58\x88\x843\x0b\x7f\xc6\xc4\x9by\x17x\x00\xa7\x04H<\xf7\x97QB\x0c\x91\xee@\x7fl\x00\xc3rT!\xc2M\xa0y\x1c\x0b5=$p\x94\x08\xbb\x92j\x02q\x1b\x8f\xee2\xd4\xc5s\xae\xbe\xe6+\x12'\xa8\xd3\xb0\xfa\xdd\x9ea\xd7\x93\xd0\x8ff\xe8\xe1\x19w\xc5wFr)\xbd\xfa^\x8a\xd9\xd4%K\xb2b*\x85\x02\xf6\"\x87\xd5b\x9f\xd8\x87\xfa\xe1\xa2\xc2a\x08\x99\xcd\xb4\x81E\xecD\xbc\xc8\xc5\x82\x15\xe6\xbe\x06&%\x0c=\x0dm\xe2\xf5 \xc2\x9a\xcb\xf2@\xa2L\xe5@\xba\x88\xa3wH\xc61(\xacm\x85Q\n^\x92\x04\x97!\x99A\x1a\x81\x07,\x14uK'?\x88\xcf\x95\x94\xaa\xbb\xde\xdePdG\x96\x143\xe6\x8a=[\xea-'\xaa\xa1[\xaa\x81\xa9\x80\xdaT\xc0\x10\x94V\x0e\xbc\xdfD\xdb\x08\xaf\xdc\xd6\xc9\x8a\xe2c\xa2R\x86#\x1f\xa5y\x9b.\x89\xc4p\xd9\xee\xa1Ccv<\x91\x01\x9a\xca\xb9\xe2 \xed\xe9\xc6$S\x9dW!$\x96\x91=\xffU\x8a\x1a\xba\xbbg\x88\x18*\x0fG\xb0\xf3\xf2\x00\xadG\xd6\x10\xacG\xdej}R!\x8a\x8f\xad\xc7\xf4\xc9\xcffQZ}d=f/\xad\xa3Dy\xf4\x04\x1f-\xd5w\x9e\xe2\x83\xcb\xf4\xa4\xa0\xa3\xd2\xb0\xb7\xbal\xc5\x89\x17\xa7lH\xbcru\x8f=~d=y\xfax\xea\xec\\\xd6LF\xa5\xc2pL\xaaI\xb4`\xb8m(\x8a\xd2%\xba\x93\xd2\xbc\xf3[\x11\xfd}\xa7\xfb\xe2\x8a\x84\xe9\x8bU\x90\xa6$\xd6)\xf9\xd5\x83t\xccc\xa1.\x02\xe5Z>\xfd\x84\xf6\xee\xbec\x07.&\xd3\x0d\xba\x9f\x15\x14\x93\xb6x\x80\xc0\x1f\xc6A\x9a\x03\xf7\xf6\x8f\x11\xf8Q\xb6^\x92k\x06:\xe8!\xe8M\xec\x85\xc9<\x8aW\x1c\xdaG\xe8\xf7\xbd$y\xb3\x88\xa3\xecr\xc1\xe1\x03\x843\x9d8;\xd8\x05r\xc2\x8f\x00\x9d\xc1j'\xffJ\xca#o\xd2\x9c\x07\xfa\xd3h\x8a\x06a\x1c\x0e\xbb0\xc5X\x0dZ\x89\xe9\x1b\x18\x1bh\xede \x91\xbe*\xc7&}\x93\x91\x96\n\x85\x05\x1f\xc2\x1ac\x92d\xab\xd2\xf7\xdaSY\xd8\x8d\xc2\\$\x0b\xd0\x81\x0e\x01\xb1\x17\x84\x96\x0b\x11B\xce\x83\xe4,\x9d\x05\x11\x957\xe4\x81\x11$*\xb7\xb7`\xb3j\xa8\x18\xe7\x82\x87\x02\x11\xfd\xcd\xc46\x17\x92\xaa\x16\xef\x8a\x874k\xf5M\xf3\xebi\x07\x9bac\x19\xe7\xb8)\xa3c\x9b\xcd^\xb2A\x85\x86{\xe03\x92\xa4qt\xc366\xff\xb1i\xb3\xbe\x9en\xa3\xaf\x90\xed\xb8\xdcN\x1cw\x97A\x92\x92\x90\xc4\xcf)\x1f\xc2\xfd\xe4\x82E(3\xb5\x1c\xc1_\xab\xf4V\xdf\xe2\xdc\x88&\xab\xe8\x8a|\xc2\xdb\xa9\xac\xb9\xf2PZ\x7f\xf5Uy\x9d\xab\xcf\x8a5\xd7\xbe\x89#\xa2\xc2\x92\xaeU\xf9\xa9\xa9\xd5ym\xabsm\xbd\xc5\xd3\x9a\x9d \xc8-\xc3\xe4R?\xab\x10\x19\xdb\xe7\n\xb6\xcf\xf3w\xca\x10v\x94\xa1\x04\xc8b^\xceM4\xdca\x8ec5d]\x7f\xab\xaf\xa0\xeaG=\xa7\xcb\xc2\xe3\x96\x19\x9e0\x1e6\x86\xc8\xa9\xa2R\x8ee\xa9\x16\xcbZ\xcd\\\x0d\x84\x00i\xa7 %\x19#\x8e,E\xbe\xb9Y\x13.I>\xf7B*LR6\x03\x1e\xf8K/I\xc0K\xc0\xcb[\xd2\x1c\x0b\xdf\xf3\x0d\x94\xcb>\x0b\xe2\xcd\x80E\xa3\xe1\x90\xd4\x0b\x96e\x08?\x0e\x8c\xaa^\xcb:$I\xd5\x8c\xe6\xf5r\x9a\x10m\xf5\xf3A\xb7\xa21S~H\xaeS\xa6\x8eR\xc7\xa9\x8af\xf2P\x9eb\xc0\x92|\xb8\xa8\xf5\xc1\xdb\xc0\xc3\xd2\xac\x90\xf2\x94\x10\x17\xdam\xa9\x9a\xf2l\xb8\xa5\xb1g!\xea\xbe\xbf\xfd\xe1\xe7\xfd\xddd\x0ex\xec\x0ci&\xd0\x11\\\x1ec\x051\xb6\x19\xb32b\x13}\xe7\xe2xQk\xddy5\x15'\x1a\xda\xa3.\x9d\x91Z\xbf\xc3\xbe2\xc4\xd3\xd2\x80\xaa8^Y\xf2\xa2%:\xbd.t:RU\xda\x98\x85u3\x82\xb1\x0e\x9bf\xa4\xaew\x0d;\xb0\xdc\xda\x17Q\x106\"\x1c\x9b\xffQu\xfe\xc5E\x0f\x8d\x17s)\xean\xdeY\xe6Zl1m<\xae\nO\xcdM\xe7\xed\xc4\x81\x10\xda#4\x81\x13\xc3\x9a \xaeR;\x7f\xe8{u\xcf1\xc5]o\xb9\x8c|\xbbg\xf0cV0\xa6\xd0\xf57\xa0]13xj\x0eXl\x08\xde\xde\x0f\xc2\xc4\x9b\x13;\x85\xa7O\x9f\xa2v2+O\x9fG\x97\xf3\x04\xb2\x13\x07'.\xc36\xd8\xacF\xfc\xe2\x04^\xde\x8e\xd67,\xb0\x01}\xa5-\n\x96\xa2\x18dl\xd2MS\x1c)S\x9c\x03\xdeSI\x0b\x03s\x06\xdd L\xd6\xc4OK?\xba~\x96\xa4\xd1\x8a\x91\x89\\9\x93/\xd0\xb8ZpZ\x87\xecb7\xe7/i\xd4jlXC0\x92\x1c}\xb8\x1e,.\x05z\xcfMo\xec\xe2h1^\xe3\x89{c\x7f$\x1d\xfb.sw\xbd\xddF+\x90\x88\x0fS\x1cu\x13\x92\xbe\\\xad\xc8,\xf0\xcc\x1e\xae\xdc>\xc3|\x8cx\xcab5&\xb3\xfc\xf1k\xaej\x007\xdb\x98L3\xc0M7iw\x16\xf9\xa8(3\x97[\x97\x12B~_ \xc9k\xcc*\xa7}`\xcc\xa7N\xab\xc2\x8clk:'o\x82\x15\x89\xb2\x14NaM\xc9\xb5[D\x8c\xe7yk\xa6\xccq\xfa\xab\xf7\xdd4bW\xdb\xf9\xe9[$\xb6aQ\x8b\x9a\xe8\x88\xf8Hf\xa0Z\xca-\x7ff\xb6&\xaa\xaf\xf8\x98\xf4[0\x94Q\xa7\xae \xb4\xa1v\xd7Q\x92~\xca\xb3\xf9\xb3\xac?\xc1\x8an\xc93?\x0e\xd6\xa9\xd1\xddG|\x04\x11\xd79\x08V?x\xcc\xefF\xe1\x8a5Woh\xcf\x85\xbf\xbc|\x13\xd3\xab~\x88\xde\x84 \x7f\x18o(f\xc0\xb6,\x17\xac\x0f-~\xa8(\x1a\x0e\xab\xa1\x94K\xb5\xe8W\xc2vP!\xc5\xab~\xbe\xf0\xc2\x90,\xe1\x14l\x1b\xa3\xa7\x90wP~\xe4t\xe9\xbc\xf7\xf5\x03\xaeE\xae\x99\x9d\"\x057\xa9<\xb7\xc0\xd3\x08;1(M\x8a\x01\x0bQ5\x86\xc6E+\nc\xe2\xcdn\x92\xd4K\x89\xbf\xf0\xc2K\x82i\x92\x97\xa3\xddvD\xbe\x8b\xe2\x0e.Z\x06\x0d\x97\xbd@r\xfb\xaa\xdf\x85\x94\x1f_x\xfe[\xe3qV|\xbc\xf82\xd1\xf9\xdb\x89\x8f\xe1\xae=\x14l\xc8\x1f'S\xa6\xdf\x8e\xed\xc4q!i\xb7M\x08\xb7fG4y\xed\x16J\xd9:\x1f\x82\x85y\x89Yzw\xf0\xab\x81\x9b\xa1\xa1\xca\x1a\x1f\x15T\x8e::\"\xa1\x9f\x94\x86\xbb;\x02[h\x17\xeb}\xf4\x1a}\x9e\xe7\xdc\xf5\xa6\xaeL}\x9a@\xf1im\xb8{\xe4O~:\xed\n4k\x16p\xc4'\xc6\xf7(\xd6\xd5\xf7^|\xf2\x14P\x0d\xba\x0b\xdd\x07\xfd\xae{f\xdf[\xdd\x87\xd4\xf9O\xea>\x0d^\xda\xd5\x0f\xf6\xa9\xbfm\x9f\xe2qo\x93\xbbU\xf2\xe7.\xfd\x1a\xdc\xa5_.\xc4\xe3\xfe\x8f\xa3w\xbbw\xef\x1d\xfd\x7f\xf0-\xf7\xb1\xd1\xd5[\xf7A{\xfd\x12U\x0e\x1aw\x0f\xddG/Q\x97J\x98\x84\xa3\xbc\x00\xcc\x83\xd0[.7\xa1\x0f\xccp?\xdf\xe0\xbc`|\xba\xa9\xdfoE\xb7g[Y\xc8\x02\x02\xcedY(!\xcby\x11\xa9?\x0fN\xbc\x08\x12\x0c\x83=\xc4\x02\x92\x0d\xb8\x949\x14y\xb1\xd9\x15`\xf3[Q9\xfb0\x90M3\xf1E\xdd\x03\xe9.#\xdf[\x9e\xa5Q\xec]\x12)\xa2\xa3:)r\xfeTm\x855\xef*\x10aQ.\xb7\xaf\xe5GBa\xc8sn\xa07\x99\x95\xc6\x19a\x87\x7f\x1e\xd2.t\xbai\xf4I\xf4\x8e\xc4\xcf=\x8d\x01Y\xfe\xb5q\xf0R\x10wal+\x8c>\xe2A\x88\xd0\xc0b\x8a\xbd\x0d\x92\xb1\xa9\x1a\x15\x13\x8a\xb14\x9eapm\xb4ai\xe5\x12\xa1m\xa1\x85\xa8\xd2\xb5\xaa\xef\x91\xee\x1e\x81\xf8\xd0*b\xcf'\xa5*\xe0\x14\xfc(L\xa2%\xe9\xe2C\x16\xc0F\x80\xdeyq\x88g%\x1c\xa4\x1aD\x0f\x8c;-W\x170R\x93\xa2I\xaap\xc4j\xda\x87\xc6\xad\xb4\xd1\x1e\xd2+\xe2J\x19\x96\n\xb0\xe4\x06r\xac\xcb\xa3\x14\xda\xfb}\xed\xad\xcfH\xdd\x1e\xdc\xb6G\xe9\x82d\xde\x8b\n\x1c\xa2+\x15\xa9\x01\xc9\x0bG\x12MpS\xac\xb8\x1b\x84\x0b\x12\x07\xd8yt,q%\x98\x1d1'\x93H\xd2\xab\x9f\xa7\x92\xcbH\xddd\x01\xa2\x06\xb7DT\xdb\xde\xc2\xb3\x86.\xcf\xe1F\xcbS~k\xd0\xbf\xc3K\xfd\xfe\x81S8\xc5\xdc\xf1}\xc9}f\x93\x1a\x9a\xec\xcd\xfdc}\x16\xc4\xfe\xb1>\xcf\xcd\xdeAs\xac\xf6\xeaBqK\x04\x0bH-\xc7P\xd2\xeb\xcc\xb3\"zU\x8c\x97R\xd1*g\x13)\x8a5\xe6\xd6\xcb\n\xebWau\xe8z\xc9M\xe8\xf3\xe4\xadYw\x1d\x07\xab \x0d\xae\x08\x9c\xe6.0pZn\x02\x87u\xbc\xef`6\x0c\x1e\x03\xca\xd6\x948pl\x82w\xe5*\xcf\xa4zi\xb1C\x07S\x0e\xc8\xc0\xfd^\x9f\x01\xe9\xd7\x01V\x93w\x15\xfd~\xec\xfd\xde.\x82\xd6,!\xa7\x00\xee!p\x16$\xeb(\x07\xf6\xd1f\xd3]y\xd7\xcf.sX_\xc0\x04\x80\xbd\x19\x939\xba\xa7\x90X\xc0\x0f\xe8\x8e\xa3\x88\x92m\xb9k\x9a\x10i\xef@\x17\xb9\x1du>\xdeE\xa2\xa2\x12>\x99/#9\x97\xf5f\xe8\xc4\xd1$H^y\xafl\x8c\xfb\xcf\xd2x \x96\xa40\x82W\x18\xc3\x153H\x0d\xd8\x9e\x92\x07\xc6\xcb\xc9l\xfd\xe4\xe8\x02\xd9]\xb1 v\x89\x0b~y\x81\x03L\x9dBe\x1f\xbb\xc8?_&\xb9\x8eDv\x04\xb9\xd1\xb8\x83\xbf^\xd3\xc6\x13x\x8c\xa5\x1f\x83\x17\xce\xe01/\xfe\x18|\xe6\xe2sA K\xd0]\xfc\x92\xa4\x0b\x12W\xb5\xe5|\x19\xcbazr\xd1\xc8:?\x17\xd1\x19\xce\xcf-\x16\xaf>\xec\xce\xa3\x18\x9dp \x0cYf)\xcf.B\xe3\x93\xfc[X\x0c#\xe24\x9f]\x0c\xcbh\xd5 s\xd7\n\xa8\x8c\xd1(A\x87c\x82q]R\x1e\xa8\xddW\xee\x13\xb1T\xce\xe7\xe7\xeb8\x9a\x07K\x12\x9f\x9f\x03\x8f\x14^@0$\xa6\xdf\xcd\xd63/%/\xc2+\xbcJ\x9d\x87\x9fx\x90\xbd\xd3\x88\x93\xbb\xba\\\xbcBU+\x89Y\x17A8S\xb1TS\x90.\x95\x8a\xb6r\xe2\xff\xd2\xc3\xa4x(y[\xf1u\x7f\x99\xbc\x08\xb3\x15\x89\xbd\x8b%i\xa2\x07\x9b%j\xd0\xde\x84\xa2\x934g7\xd3\n\xbc\x1f\x18\xe27\xacK\xa5vk\x0ew\xc5n\n\xec\x90\xa58\xf3\xf9q\xdf\xb3)\xae\xa1Ux\xdeM\xa28\xb5\xb5\x04v\x8d\xa9W\x11\xf9\xd7\xb8\xdc\xc3\"\xfbL\x83\xc6}>N\xa7\xc8\xcf\x99\xc4\xed\xd2\x01\xca\x93e<\x88\xf1\xde'\xecE\x96R\xf8T\xd4\xe3\xbb\xb0t!\x1c\xa7S\x17R\x91gD{\xa3\xdctX}\x10\\\xde;\xacRR!\x81\xea\xf3E\x1c\xe9\xd3E\xec\x1d\xf5\x9d\xee\x8a\xa4\x8bh\x96\xe8(\xed\x9e\xf2\x1eg\xd6\xc7\xba\x04\xd3\x9a\xbd\x80g\xc2r\xc9\xf9\xa6\xbbfYl\x0cff,?\x96\x1c\x14J\x89\x1d\x94\xf0\x9d\x0b\x94\x81\xa3J\xcc\x80\x19B\xc9*hL\xdd\xa5?H\xa1o\xb7\x0bW.\xdc\xb8p\xe9\xc2\xca\x85s\x17.\\x\xe7\xc2\xb5\x0bg.\xbcp\xe1\x99\x0b\xaf]\xf8\xc2\x85\xb7.\x86\xb1Z\xe2\xe9KO\xf0\xaf\x98T\xdc\xe2\x020%\xe5\x9cw\xe7\xbai\xc6\xabS\x89\x9eK25\xc5\xfb3\xcct*\x831\xb8\xd3\x08\xce\xba\x97$e\xd1\x87\xcf\xba \xfd\xba\xc2\xaf\xcc\xac\xe1b\x94\xce3f>q\xdcB+\xd3\x8dI\x12-\xafH\xcc\x82\xcc\xbe\xe5\x9c%\x87\xd2=\xfd\x05\x8f\xbc\x144\x04a\xe1\xfc\x97\xfbU\xe5\x04D\xa5\x1e\x94\x1fcp3\xb4\xd6\xbf\xb5#\xa7\xe8\xd2\x88\xf1\xe8\x1b\n\xa4Et\\\xf2%]\xad\xfc\x1c\xfe\x82\x16\xcb\xb8W\xf2%I-\xdc\xb4\x11\xf3\xc5s\\x\xa9\x8dhO\xfb\xc0\xd2\xf2a\x94\xe4\xc2\xfbp\x9e\x93\x13v\x86\x8f\xc6\xbd)\xeaQ\xaap\xd1\xe7\x11\xcb}c\xd6\x08iF&D\x8b\xd8\xb6\x9e\x07\xb1\x9f-\xbd\x18\x82\xf0*\xe2\xaa\x1c\x17\xac\xe7/?{\xfe\x83O\x9e}v\xfe\xf2\xd5O\xbd~\xfe\xec\xcd\xcb\xd7\xafLVwZ\xeb\xa5\xad\x89_\xfe\xbe\x08i]3\x8d\x0f\xd4\x13\xbe\x1a/\x99=2p\xe1\x99\xbc.\x89X\x17n\xc1\xa7bH\x99|\xbap\xe5\xe4y\x07\xe9\xfe\xa8\xd5\xb6\xe1\xe1Y\xbf\xaa\x86\xa1\xb2{\x02\xb5h#\xae\x12\xe4\xa8[\xe0\x90\xc1\xa5\x10\x8dm\xba\xa0\xc9\xa7\n\xbe\x14\n3\x18V\x90\xccqMh\x9ew\xfa\x81\x17\x89\xf9\x03\xa0\xbf\xb0f\x99\xf2\xfb\xe3\xb8VD\xcdu.\xa7\xfa\x7fXR \xdf\xefD\x8e\xc7\xf5\xc4\xb8\x0b\x8d\xd3\x14\xd4.kP\xa6\x06\xba\xcc]\xb8M\xefK\x0dj:\xf7\xc0\xcb7\x0e\xe8\x1e\x0b\xb5\x8b\x17\x88u\xa3\xe2\x97\xe2\xae\x9bi-\xffQ\x1c\\\x06\xa1\xb7\xd4Z\xfb\x85\xb0>\x84/\xd4\x87\\\xd2\x7f\x85\x91\x83\x90\xdb\x8b\x9fj\xd9K\x92nr\x0d\x94\x0f\xf2m.\xe7\xbd\xb5S\x07\xb9\xdc)\xdc\xb0@\x0f\x1c)R\xba\x18*\xd5S[^x\xc9\x16-\x1b\xd6Q\xe3\xda\xa3i\x8a\xf1\xdbMZ3\x900`\xfd\xd5\xf7\x00\xe7\x04\xfd{W\xccM\nF\xf0\x12EU\xee\xbe\xc0~\xbc\x96\xd1\x82=\xb1P\x9a%\xba Q\xea PL\xd8 #\x8fP\xac\xbc\xd4\x0f\x03\xcf\x83\xe7\xf4\xc8'\x89Fn\xde1l\xc5\xdatb\xa3R2\x9f\x9aK9B\x9dC7\x7f\xae\x0ey\x81F\x0f\xccI&\x83\x9f\xe5`>K\x85\x1b\x95\xfdZD\xf1X\x94T\xfa\xfa\xb8\x15j\x7f\xe9\x18\x870S\x1f\xe4g\xe1\x0d&8e\x92-\xdf\x9ej\xb3\xd5\xed}\xa1\x8aj\xe6{,n9\x87\x8e\xba\x86l\x0b\x86\xb8\x05\xc3\xb2\x8cFP\x92 \x99\x8c\x96q)\xb3j7\xde\x92\xa7\xe7\x8an^\x1bg~\xe5*\xa1iki\xc8G\xc1T\x18\x17\xc9[\xa8\xa6=w1\n}P\xefF\x8cH\xdf8w\xbc\x1b\xc5\xd09\xcf\x1d\n~'Mk\xcaW\x8dNhA\xddB\xd6Y\xba\xa3U\xbd\xcb\xf5\xb7\xd6\xcf\xac\xbb\xf0\x121\xf7\xda\xee\x16XP\xd3q\x8e\x18\xb4\xaeT\x93pum\x7f\xa1\x0b\x8c*\xeb\xbe\x86\x10a\xd8*#\x89\x8d\xec\x0b\xcdSN\xbb\";\x13\xa7\x1d\xb5\x15\xe4D\x91\xfdN\xf7\x0cyEd_\xab}\xcer\xc8\x83\x9c\xf0\xfb\xc7\xba\xfc}\xf4\xe4\xaf?\xe1\x0ft'|\xd4Kv}o\x9df19K=\xff\xed\x9b\xd8\xf3%\xb6B\xe48\x1d\x8d\xf6\xa8\x90;#2u\xa7.\xf7\x98\x07\xe5\xfc\x1fj\x89\xa4\xa2c\xd2\x9e\x85#;\xe1\xa1\xb6<\xc6\xd4x4R\x91\xb8\x1f\xed1\x89\xc8\x14\xc9n\xe1F\xa2l\xd8\xf5\xa3\x19\x8a\xddxO\x87\"\x1a-CJ\x02\xcf=\xd6hs\xa3\x02\xe3\xc0\\I\xc1\xe2\x84ln[`\xb1l\x88\xad\x8f\x882\x8f\xa2!X\xb1\xf7\xa5U\xa5Qj\xd9\x0b\x8a\xf1\xd6\xec\x9d\xb7A\xd94\xfe\xf2f\x08\x16\xfdS\x0d-\xecb\x80\x9a\x08s\xb7]x1\xcb\xe1\x16\x7fy\x83\xb4\x81ve\xf6\xce\xc3\xf7\x1eXo\xbbgH\x8d\xaaU\xdc\xa2\x11g\xe5]o\xa0\xd41\x18\x08\x8a[8\x91\xe2o\xeb\xc2\xa0\"w\xa3\xa3n*+:Q\x1a-yhk5\x8df\x17\x9et\x1cS\xf9\x9d\x8cc\x8d\xabi\xa3\xbfN\xc8\x02\x15\xd0}\xdd\xe8{\xc1\x04\xfe\xfe d\xf0\x04\x92\x13h\xb73v\x7f\xad\xd8\xa0\xd9\xd4\xc5\x80\xb7yh\xa2jv\x82J\x1c\xb407\x8bh1\xfd\xdb0\x1c\x1e\xee3\xc3\xa1\xa4ag\xa6\xc3\xc3\x83o\xdbt\xa8_D>V9\xae\xac\x95\xdb\xd4-\x8c\xb4X^\x87\xdaE\xd5;`=\xb0>Y\xe1\x1eA\xd9d\xd1\xb4\x9d\xaa\x1d\x17\xe6f\x8c\x84\x9b\xaf\x0d;\x9em\xebzr\xa7\xbek(&oB\x1fR\x9d]A\x1b*Ks\xc7\x81\xe3\xb0\x1f=\x82`,\xec\x12\x98\xbe\xa1\xf5 f\xd6*\xfe\x1f3\xfc\xe7w\xe5J\x17nS/\x08\xf9n8\xea\xddc7\x88\xd9\x96\xc9\xfc\x96{\xa5\x8e\xd7\xc5E_1\xe7\x88\x08\x17\"\xa06r/\x91\x9d\xbb\xfal\x1eE\xd6\xc3\x18\xda\xc50\x95\xa9\xe4wa\xee\x8a\x0d\x95#b\xc9\xb6\\NDy\xdf\xceW\xee\x92\xba\"\x18\xbb\xc6\x04\xb4\xd4[E\xd7\x1b[r\x16\x9bZrf\xf5\x96\x9c+\x83%\xa7\xd2\xdc\xcd\xa6\x06\x9fK\x9dE\xb5\xac4)\xbf\xb0\xd2\x12\x0c?\n\xe7\xc1e\x86\xb6W=\xd1 \xb9mV\x1f\xf5Z\x04I\xaa#+j\x9akJ\xa2\xe2&a\x05\x84\xc0b<\xb3-\xd1\xa5\xe1RF=\xeb\xfc\x9c\x10t\x1b8\x95b\xcb!\x8c\x1e\xe5(h\xd5\xc5\xbc\xe70\x82\x99P\xc8\\U\xdeva\xe5\xb8RA^,\x1c\xa7S8\xd5\xc5[\xe7O\xe8\x1f\x16\xac\x0d=O\x11:\x821\xb3\xa5\x92i\x01\xe2\x91:\xca3V\x11\xf5B\x9f\x0c\x91\xd0o6K\xae\x1c\x0eL|J\x13\x15\x88\x88|\xcan\x0d7\xb9\x9f\xc8\x8d\xd4\x01{\x03\xaf\x91 \x97\x8df\x8fX\x8c\xadCg\xf7u\xe8\xe7\xf1|\xce\xcf7\x9c\x8a\xf9|\x88\xa2\xef\xa63\xc1i\x84^\xcd\xcd&\xa3\xa5G\x9bR,\x05\xfd\xfb-\xbb\x82X\xce8\x9dn\xf0\x9e\x8a6,\xb6(}[\x9d1\x10\x92w\xc4n\xbe\xd1\xc5\x8b\xc7\xd1\x94\x8a\xb0\x91\x03A\x11\x927\xd0\xcd+{J\xe5\xe4\x81\x88K%4\xfa\x1c\x05\xe3q\xc4]\xe40ie\xdcM\xd6x\xeb1r\xa1\xaf\xbb\xb7\x87\x96\xb4\xb8h6\xaem\x96kc\xc3:\xcf\xf8\xa6eg\n\xc4\xac\xf1~\xe2U\x1e\xd1\xa2v\xdd\x0dt\x82r\xe3\xa0\xbc\xa0\xe6\x15\xd1\xafc}\x1cx\\\xc5Pc#c\xb6!9\xd5\n\xbb\xebH\xd8\x89\x85\xc0\x13\x08\xe9r\x13\x07\xa21\xa1\x0f\xcb\x17\x1dI\xcd%8l4\xc0\xe0\x15\xec2+\xaf\xb7w\x82\x847\xa0/\xb3\xaa\xf9.\x8e\x0bC\x8e\xb6RnJ\x15\xb7\xc9\xaac\xa9\x9b\x80Mnl-\n\xe2\xb2\x08\x92\x86{F\x0d\xf7\x8a6\xb9\x89Un\xaf\"\xaf\xdc\xbf\xf5\x86\x9bVu\xad\xbb%\xdd\xd1\xfd\xfa\xb2\xd1\x8d\xaa\xbf\x14\xfc\xa4\x9fue\x16L\x98\xf7\x1d\xfd\xaf\xf7\xba@\xcch$\xb1\xab:O\xc6K\xe7vP\x85S\xc62\xb7#GGx\xe6\xb6\xec\x0b\xcd\xbc\x08o\xec\xaf\xde3]\x9c,\x1d\xd7_\xa1\x16\xaeb\xccU\x02\xad.3\xdbgq\x88\xf3C#\xadTn\x8c\x08\x9f%:\xa3\xdf\x81\xfb\n\xcc\xdc\xd5\xa9\xea\xd3_\xa3W\xd5\x88\xcd^\x9e\x9b\xb0\x12\x99\xb8h\xaf>p\x80D\xf7+i\xb05\xdeG\xd2\x0b\xe8,d\xa7\xe3\x10-\xcf\xf4o\x19%\x1c\x91\xf4\xce+\x19\xa5\xd5\xeb\xfb\xef\xdd\xedN5\xa8\xf6B}\xd7\x86iy\"~(\xce\x14\xcb\x8aC\xa5\xae\x8b ,\xc5]\xb9\xefQ\x88\xadS\xffX\xa3\x1d(%\x94\xbb\xe3\xa1.`\x9a\x8d\x94\x8a\x07\x0f\xd4\xed\x8d\xce\xd1B\xb3\xcc\x04S6\x92y\x1cUrq\xd5\x9d\xb6Y\xe8v\x14\xddq\x0d\xc7\xa8Gv\x99\x8ax\xea\xb8\xf0\xbd(Z\x12/\xb4Q\x94!E\xb8e,\xc0LA\xe8\x15\xfd\x10c\x96\xf4\xbcG\x07N7HI\xec\xa5\x91>\x90\xe3\xb1\xde}|O\xb9\xcd\xc5\xf6\xe8\xa0\xba\xa3=\xfd\xd6M\xf4\xead_\xbf\xff\xe7\xbc\xcdj\xe5\xcb*^mt\xacV\x0f\xcb\x8b\x878\x8cj\x9e\xcb\x87Q\xf5)\x1e\xe64\xf1\x17\xdf\x1bO\xf2\xe5\xa3\xfa\xb6\x9b\xa8\x10K\x8d\x1e\x94\x8d\xa6\xa4\x17\xb5\xa6$\x0c\xb2T(\xe6\x13\xa6\x98\xf7\xed3\xa4A\x9e}\xc6\x83#\x02\x8f\x16\x8eh\x8e\x0bG!\x11\x0b\xf6\xec\xe4q\xf2\xca\x95\x1bb1\xe0 \xe8\xcc$\xee\xa1S!\xde\xa0\xe1\xbb\x93y{\xda\x97P\xc4\xe9\xa7$\x85a\x11\xbf\xb9\xcdo\xeb\xd1\xf3\xb9}S\x928\xfa\x0e&+\x1bA\x8a\x17\xd1o\x0c\xd2\x10;\xd5\xd1V\x1b\xa4\xf0r\xed\xa5N\x95B\x8c\\R\xb1&t\xe0\x86\xf9\xf2\xa5Z\x07J\xf1\xe1#5$\x0cU\xa0*\xe4\x06\xb3\x05~\xc7\\\x08\xe7|\xa9\x98\x91A\xb5M\xd8\xef\xb0\xbb\xf1\xd48\x178\x0f\xe7\xe8\xe5\xfa\x8e_Ge~4\x94`\x8a\xf9\xa1\x07\xe4\x0b\x18\xc19\x06\x16\xb3\x8b\xc9i]tgQHN\x1c\xb4\xbf\x9f\xc1\xa9\x10\xe2\x983\xf0\x05\xd3\x98p7\xf6\xfc\x17\xe5\xdf\xf6\"\xd7\xa6\\\xbb0\xb3opg,\xf0\xae\x15\x9f\xe6\xebj\xa3\xed\xb6!a\x16]9Mv\xa0\xc2\xdbs^\x83\x0d8\x03\xf2\xda\xebF\x8f\xe3uQoW\xc1\x89k\x8e\x10\xbfz7\xa4\x82]#\x05\xbb*\xc7\x92\x1c\xa9\xb6\xc0\xa2\xd8vx0\xdb:\x9bt\xd5\xd8\x0c| f\x8c\x07\xd8\xb3\xa2\xfbn\x8d\xccW\x89\xb0\x1b3\n8\x1b\xa7,\xcb\x1f\xcb\x9e<=q\xa0\xdd\x8e\xb5\xd4\x0b\x8b\x8e\x80\x17\x9d\x8a\x9c\xab\xf6\x9a\xa9]\xac\xef~\x17\x03\xab\xb9\xe0u/\x13.:\xd5\x1fI\x0bo V\x13\xd3\xb5\x10\x17<&.\xe2\x93~\xf5\xb4Zry\x97\x83\xd8F\xb52/J\xa4J\xc4\x08}y\xfa\xf9\xf9\x8c\xb00\x94A\x14\x9e\x9f\x0f\xc1\xc3\xd0\xa2D\xe7\xccw\x1ez+R\x94\xb9\xb2\xab\x0e\xd0\xef\xcb\xea\x91\xb9\x1dT\x9b\x9cG1}\xbd\x1e\xcb\xf8\xa0\x17\xcc\x0e\x86\x7f\x86\xec\xcf\x08\x02;'\xe8\x8aR\xa4\xf4\xfb-\xb9\xf9x\x93\xc6\x0c\x8e\xe3\xb8\xf9\x08\x04!$(\xd3.\xcc:\xfc\xc5\x98L\x99\xa7s\xce\xc1Hm\xd7\x16^\xf2\x92c\x89\x98\xcb\x98YA\xa4'\xcc\x9f\xcf\x92 J\xaa\xf4 y\x8e\xaa\xaa\xb3\xb5H\xf6R\xa9N-\xc0kU\x1f\xa8\x95s6V\xad\x92\x83EE\xfc\xa7\xf2\xfa\x8a\x92\xc3\xca\xbb\x08\xe3/\xe2w\xe5-\x9e\x13\xa9\xf2\x9e\xc8\x9a\xc4\xde\xe4\xbf\x94w\x13\xe2\xc5J\x93\x0c\xc8\xdfd?\xd4\x17\xd7\xc4\x0fHR}\x93A\xc5\xab\xec\x97\xe6\xdde\x90*o.\x834\x7fo\x19\xa4\xca[\x92\x08PyWz\xc2k\x90 \x9azrAA\xa9'\x7f\x92\xd7\x93C\x94z\xb20\xf1\xa35E\x83\xea,HOx=\x12\xa4\xe4E\x82$F\xa2J\xd5\x9d/\x119\xdaFU{.\xba'\xda\xaf\xb5 \xcb\xba_A\x95*;\xae\xd2\xb1\xc0\xdc1\xb9\xe5MZ\x15\xe4\xdb\xc6\xec\xedL\xef\xd1\xad\x90Qh\x83\xe5(\x0e\xa1\xa5\xdfx\xa4x=\xdf\xb4\xd5\xa4\x92M\x0b\xd4Q.\xcb\xa3\x0cddr\x9b\xa6U\\>\xe1\xed\xe8\xb5\xa3\\\xee\xae\xe4\x86\xc7\xe0\x189\xc6\xd9r\xa7\xf4\xbd\xca\x11\x11{\xe5[\xae\x98S\x8b\xbd\x105\xbf\x10\x94\xe2\xf0\x97\x04f}\x15\xe5\x99\xd0UQH\xe5\xf7\x89\xa5%\xe9g\x8f{[G1b!\xcfP\xdf\xa0\x93\x1cR\x8c\xea\x9f\xcb\x0d\xfac\x90\xd8\x1c\xc52\xdc}4\x9b\xf5:?\n\xb1\xab>Z4\xb9\xbd\xa5\xcf\xe54\x05\xac\xecY^\x16#\x98V\xb3\x18\x9e\xf2\x8b{\xb4\x1d~'\x8ecj\x87\x87\xfe\xb0\xa3b\xd1=\\\xf4\x80\xa2=\xf3\x93\xc5X&\xe3\x1e\xf7q\xc7\x07\xf4E\x17\xbcq\x9f\x03\xbf\xc5\xae\xe7}\xefO\xc7\x11\xe2xvr\xaf~;\xae\xa8\x8c-\xe0\x1d\xf0\x97k8\xb5\x99\x16\xd5\xa1n\x17\x1b\x83\x07\x8f\xa9\xc1\xe4\xac\x1e\x93=\xee^^\x8f\xebyn>c)\x1f\xd9\xc1\x06{\x81\x0b[\x19\xc5.\xf3f\xa0\xaf`\x1a\xc0q\xb2 =\x8d$,\xdd\x9c\x9eJ\xd2\x7f\x86\xe8\xe0\x8d#\x89\x9e\xd6\x93R\x9f!J\xc6\xe24\xb1\xbe\xf6\xa7\xe3\x00\x91.\xba\x03a}\x90\x9e\xe5\x17q\xf3\xce\xd0\xf7\x85\xdf~\xe0\"B\xd3g%\xd0 \xb4\xb0\x18\xb7\x7f?z\x04\xbe n\x0e2\\\xbf\xbb\x8e\xd6\xb6\xe3\xb2E\xe1\xbf\x9c\x0dj\xdeb\xbbH\xd7\x016\xd9'\x9b\x86_\xe1r\x8a,\x97\xa8\xd5\x7fG\xff\xeb\x1eRY\xc5\xf0\x7f\xcco'\xb2\x90\xb4]\x0ci\xc7\x83:\xdf\xe7B\xe2VB\x9c\xdc\xf66G9\xb4w\xa7\xf6W\xef\x91P\xa6\xf6+\xef\x15\xbb\x83\x98\x16I\x1e\xe0\xe1fk\x03\xa9\xbf5z\x18=XYt\xbe\xe3\xb4n)\x1bW\x89\xe4C\x88\xc5\x12\xb9 .:\xc2\x19\xbc\xe0\xca\xc2[PHi\xe18\xd8h\xd7\x95\x85\xac\xa6\xe0\xa1,_6K\xac\xe3B\xc8~\xb5\xdb\xa9\xf3\xed\xf0BIc\x85\xf9\xa3\x90\xf1\xb7p\xa0\xec\x0c_&Va\xe9\xb7\x86*<\x0c\xd1\xd1\xc8+\xdf\x02\xbdy\xc8S\xa0^\xc9\xa0G\xf5\xd0(\x8a\x9a\xe48\xcd|hJF\xf7\n\xc7\x15\xcd\xe09\x82\xb8\x10\xa1\x7f\x01ECM\xd8\xe4\x0dh\xe1F\x18\xce\x8e\xb9L\xcag\x83\xa5d\xc9G5\x00\xe1\xc7\xbb;\xe3<;C\xf9x\x86j\x16M\x136#\x9e\xcb\xf3~\xf3S\x1aC\xfel\x0b\xe4\xe7\xbdi\xd5\xf6\xa6\xe1\xc8@\xe4\xe6=U\x90\xf54\"\xb2W\x16\x91\x93\xb2\x88\x9c\xe4\"\xb2W\xfc\xd2\x88\xc8j\xcd\xc6\x9er\x89\x98\xae\xd4\x86\xd3s\x0f\x96e&\xe4p\xc7\xed\xe5\xcaD\\\xed\xeaw\xf4\xbf\x1e\x86\x07j\xef;\x85v\xff\xb8\n\x8f8\xfcH\x7f\xbfM $..\xcfT\xef\xe0$\xa6\x8bo\xe5b\xdb\x05\x0870mL\x15\xc1\x93\x184\\x\xe7J\xd3\xa5\x0bk\x17\xfd+\xe7\xdcAQ\xa5/u\x0f\xaf\xd0\xba!\xc2\xce\xa9\xcfo\xf0\xb9\x08\xc1X\xc6\xe8\xe2=\xf4\x08\xaf\x97\xe5\x84\xa4QD\x17\xd6\xe2V\x8c\x91\xa1DJ\x07\xbcVj\xd4\xd4\xebC\xad\x80\x88\xd7\x1737\xbb$\x17\x9f{.t\xfa\x945\\\xf1\xcb'\xcb<&\xc2\x9a6\xab\xda\x9c6rX\x8eli\x02\xe1\xaa\xc6o\xf9}e\xfa\xa2P\x04\xe9m\x9e\xbb\xda\xdb\xed\xda\xfb\x93\x90\xbb\xbbI\x11\n\xb4s&;\xee\x8d`\xbc\xc0\x88\x15\xa1p\xe2c\xd4=t\x98\x0d\x0e\xa7V#\xbd\x89O\xcc\x18\x12\xdd\x95KF'\xd6LZ^b\x96|\xe1\x92\xdf\xe0D#>(\x7f\x98\xe9\xa8.R\xec\x8c'4@~=c\xc17\x8a\x80\xc8\xb8\xb7X4\xd8\x88\xf1+\x1e\xcb8\xc6T\nQ\x98\x92\xeb\x14\xf30\xc5\x97\x89\x93\xfbo\xc6,yD\xc00%*P\x88\xae\x89)Et#id\x99\xbe\xf9\xdej\x8a\xc2q\xc5\xeeEr\x9fp\xe3\xa6\x08\xe9\xd0\xd3rV-\x1e\xfeCT\x0f\xa9\x19a\x84\xfc\xccD\x8a\xb4\x1b\xcc\xcc\x9a?\x1e \x13jS\xf9\xd3\x82\x9c\xdd\xd1\xdaXO\x16\xe3\xa4\x08\xda\xcb~\x04\x85MF\xe9>\xbf3\x86X\xa1\xf4\x8a\xffX\xe2\x8f\x9cq\xc5\xdb\xf5e\x81\x0eZZ\x94\xc6\x1b 6-\xc0\x88\x8e\xc3\xa9\x0es*^8\x90u\xe9\xcf\x0dD\xa1\xc4\x9esa\x85\x8b\x14Z \xa5qJ\x12{\xad\xe3\x0fj\xefs\x1a\xc2\xa8\xa2\xe8\xaf\xf9x\xa6\xbd`\x9b\xe1M\xfb\x0d6\xc5g$\x8d\x03rE\n\x8a3\x8b\x08#D\xc1j\xbd$T(\x12h(\x90\xf8\xb1\x96*\x89\x0fk\xda\x9e\xbb\xa0\x1bqe|9\xb5\xff\xafq\x9c\xe5\xcdj\x1aoM\xdf\xf8\xfb\x0f\xd6\xbd\xbc?\xdb\xf5P\xac\x08\xe6n\xe0oh\xd1\xb1\x04)\x04\xaf\xaa\x8a\x81\x85\xca3q\x1a\x93\x8a\x01\xf9`\xbb\xad\x0f\xeaW\xe3\xe7D\x19\xc0R\xfb\x12\x88\x03\xfe\xa64I\x7f\x8e\xc7\xc1\xe8\xe9\x8e\xbeM\xcf\x8e\x1c\x93\x8c\x1f\xe1\\cVF\x9ct\x84x\xb3\x03I\x1elH\xf2\x7f\xd5\xefa\xe9\"\x1asj*\xee\x84y\xccO\xb1\xd5\xe9x\xe2\xe4R:\xac\xb4z\x98\x9fP{]L\xc3\xbf.I\xfa\x19G\xd0\x1f\xd38z\xc5 <\x16LV\xb3\xfd\xef\xa7\xd4\x92\xd2\x0f\xe96X\xe8B%DsXD\xecm\xf1\x88\xbd\x04\x86\"\xa5b#s@\xaf\xb2\xee\xf3\xb33\xba\x1c\xf8\xa5K\x12\xdf[\x17\xfaT\x19\xa8N\x95`,\xcd,H\xc4dP2z\x19\xbc\xd8\xfef\xd1\xec\xdf\x84\x98\xfcl\x16\xc4$\x01\xaf\x08}g\xf4X*\xc5\xbb\x96\x82L\xf1\x10La\x9ea\x81\x12\xcfN\x9f\x1d\x83)ya\xa2t)[\xc2 \xb4\xdb\x01<\x81\xf8\xc4\xc1\x19\xe6\xf9{\xe4B\x01\xde{\x8c\xa0Mg\xff\xe9\x08\xfa(\x05S\x01d\xb7\x8ftgp\x08\"\x03!N@\xc0\n<\x1d\xc1\xdeQ^v\xff\x10\xcb\xd6=\x7f\xf4\x08\xf6\xf6i\x81\x8c\x12\xc6\xc9\x04\x83F\x15\x96\x89\xfe\x01Zr\x80\x12K\x1b\xfb\x1a\xb0*[\xfdJ\xd8\x01\x82uup\xc4\x1f\x88\x0e\x1e\x17_\xf5=D\xe8\xc1~\x0e=\xee\xe5\xd0\xe3\xc3\x1c\xda\x1f\x0c\xf02(\xce\x13\xce\x11\xa5\xe0\xac\xcbe \xce\x9b\xf5\xff\xfe\xc5\x9fY\xb5\xfbPuz\xd78Q\xc8\x18\x8b\x1a\x18\xf6\x0dO\xdan \x91Y\x8a\xcfJt\xe5r\xec\xeeX\xd6\x1b\xbew\xf2\xdb:\xa1\xdd\xef\xdf'\xb0\xa76p=\xad\xd8:?'\xc9\xa7\xd1,[\x12\xabJ\xb5y\x9a 9\x8d\x82\xc3T=\x98K\xaf\xceQ\xc5x}9I\xbd\x94|\x7f\x99]\x06a24l\xdadM|\xd33\xfa\xf1\xb0\xcdd\x08\x99Y\xc8O\xc8\x92\xf8i\x14'C0\x04c\xd2\xbf\xcbR/\x19\xbb\x068\xb6Y\xe6\x13Zs\"\xa6\xc2\xdc\x8f\xbc\xaf\xd1F}\xf5\xf4}U\xf1\xf0;\xfa_\xefU\xf9mn\x87\xf6~\xffX\x89\x90\xcd\xed\x0c:\xbb\x84o\xd3'{J\xa0e\xfeh\x7f\xaf_}\xe4\xe5\x8f\x06J\x90i\xd1\x87\xbd]\xc79\xf9N\xfeL\xe0\x0e\xf8z\xc5O\xca\x98C\x81\x9f\x05s8\xa9\xa0)\xe3\x06_U6\xa7|+G\xa3\x10\x93b\xe6\x05!=\xb65\x1c\xac\x0bC\x1d\xa7eEF$\x93\x19\xbc\xd8(i\xd9\x8fC\x9d\x84\xb9\xd1\xbdB\x99\x07\x1e\xb4X'a\xb1\x1c\x97\xd5 \x93\xdfQ\xbf\xd1q/\x95[B\x97$\xfd$\xf2\xbd\xe5s\xdc\x04\x9b\xc5\xfa\xb3{\x18\x8c\xd8\x8b\x13\xf2\xd3\xde\x8a\xbf\xea\xd8\xb1\x18\xfcv^\x0erC2]|\xdc\xe9t&a\x16/\x87`-\xd2t\x9d\x0cwv\xd6$M\xd2(&\xdd\xe4\x9dwyI\xe2n\x10\xed\\\x0dv\xc4\xaf/\x92(\xb4&\xe1,Z\x9d\x07\xb3!X\x7f\x85?\xe8d\x815 \xd11\xddK\xa3\xf8\x07\xa5:\xa3p\x19\x84\xe5\x1aEAk\x12F^\x96.\x06\x9f\x91Y\x10\x13?-\xde\x1c\xee\xec,\xe9\xbc-\xa2$\x1d\xee\x0ez\xbd\x1dV\xb2\x13\xf3\xa2\xddE\xbaZZ\x93\xf0\xb1v\xd0\x1bQp\xc9\xb5c\xd07hR\xe3\x87\xa9^\x7f\xdc\xdb\xdf\xebi\xb7od\xc4\xdcZ\xf4Q\xbcH\x85\xb5\x120\xfe\xa6\x88\x15=#\xeb\x98\xf8^Jf\xe0\x853\xc9\x91&K\xc8\xac\xdb\xe0C\x03\xf2\xfct\xa9\x98\x87#\xe9\xc9IK\xbbg\xfe\x82\xac\x98uu\xf7\xa8\xf4\xe4\xe3g/?9{\xf6\xf1\x8b\xf3\xb3\xe7\x7f\xed\xc5\xa7\xcf\xb8\xc1vP*\xf3\x93g\xaf_\xc9\xcf\x07\xbd\xdd\xd2\xf3\xe7\xaf?{Q~^~\xff\xa3\x17\x1f?\xfb\xc1'o\xce\xab\xed\xec\xefj\x8b}\xfc\x83O>\x91\x8b\x1d\x95\x8b-#o\x86\xa1\x02\xe8\x97\xea\x83g\xf4P\xc1\x9f=c\x17\xce\xc4\xe3\xc4\x9b\x93O\xc4\xbb\xe2\x87\xae\x80\xa8C\xfa-\x17\x9be\xab5\xc6\x0c\xa4_\xaa\xef\x7f$\x1e\x8a\x1fr\x81\x9f~\xf6\xe9'/\xae}\x82!\xe89\x1e\x96\x86\xf6\xe9\xcbW/?}\xf6I\xddZl8\x87\xe6\xe9K|/D\xd5\x81E\xbfY\xa5gH\xe1\xd8C\xfcZ~\xeaG+\xee{\x12\xd9\x16\xffQ.\xe1\xcdf\xcf\xa5\xf0\xe1X\xb0\x0c\xb3\xee!\xdfI\xfe}\xd5\xab\xfcA>\x9b%0\xbfD\xa5h\xa0\xb3|\xeaJ`/\x9f\xaf\x128iVH\x97_\xf0U\x85\xf2\x1cF0(\x83(\x92\xed\x96A\x14u\xf6\xca\xa0\x85Z\xd7L\xad\xebJ\xad\xeb\x86\xb9\xc2]\xf7z\x9d\xc9u\xefhr\xdd\xfb\xde\xe4\xba\xf7|r\xdd{\xd1\x99\\\xf7?\x9e\\\x1f~\xdc\x99\\\x1f\xedM\xae\x8f\x0e:\x93\xeb\xe3\x8f'\xd9\xc7\x1f\x7f\xfc\x02\xff\xffxz;\x9ed\x1f\x1d\xd1\x97\xb3\x8f\xbe\xf7\xf1\xc7S\xfb\xb4E!\xcf\x19\x84\x96pn\xed\xd3\xe1\xf8\xf3r\xb1\xdb\xcf\x9dJ\xb1\x9dr\xb7.y\xb7\x8e\xf6\xcb\x1ez\xe5R+,\xe5N\xc6\x93\xe9\xe4\xab\xc9\xfb\xea\xe3s\xfa\xf8s\xfbt\xd8\xbam\xb5n[c\xaf\xf3\xe5\xa43m\xb7\x9c\x0fv\x82r\xc9\x8b\xa2\xe4\xf8\xf3\xa2>\xc7>\x1d\xfe\xc4\xb8\xd79\xf6:\xf3\xe9W\x83\xf7\xb7\xec\xfb\x97\x93\xce_9\x99\xecLN\x87\xdf}4\x9a\xb4'\x1f\xb8\xe7\x93n\xeb\x7f\x98|\xf8xbO\x1c\xfa\xf6\xd4\xf9\xf0\x83\x9d@\xc7\"\xde\x19YD\x9f_B\xc33\xe3.\xfb.\x11q\xb5\xaakcU\xc7EM\xbb\x83\x0dj:\xdb\xa6&\xec\xdf\xb6}}alao\xaf\xa8\xea\xb8/}\xdf\x95\x9a\x18\x94~\xeco\xd0\xe03\x83yG+\x9e\xee\x1d\xa1\xb9\x02\xa5K~\xd2>\xc5 9{G0\xa4\xc7\xea'\\\xef\xb0;\x80[`\xc9\x9c\xd91\xbb7@}O\x87\x16j\xd3i\x19B\xa7_\xdb\xb1\xd7\xe6\x998\xca\x15]\xd6\xa4g\xb1\x96s\xc8\x7f\x87\x00\xb9\xc8\x05\x85\xf4\xfb\x07\x12(\xc5BU@?_.\n\n\x19H\xae\xe9\nA\xbd\x81\x04\x9a\xb3R{\x12(f\xa5\xfa\x05\xe8\xbf\xa7\x90]\xe95\xd4}\xec\x16/=\xb6\x1e\xc3\x10\xf6\xa4a\xec`\x0f\xe5\x96&\x14r(u\xe7\xff\xf9y,\xb3/A~\x13\xcb\xc8#E\xaa@\xa1G\xbd\n\xf4\x98)\xabk\x17\xe1\x8b\x9a#\xc6\x93\x11\x1c\xec\xef\xef\xee\xc3)W\\a\x96\xe9\xe7\\\xdfd\xa7\x85\x03j\xf9\x01K\xe9\xd9\xa6\xa7\xb5\x0e\xd6p\x00O\x9fB\x9fJX\xfb\x07\xbb\x83^\xf9\xd1#:\xdf\xbb\x8a\x11\x15\xe4\xd3\xd8[\x90\x13\xd3\x0e\xf6\x0f\x1c\x17^j`\x9f\xb2\x84r\x9f\xc2\x13\x18\xec\x1f\x9c\xc0\xa7\xed\xb6\x03o\xc7\x9f\xd23\xd9k\xfbS\x87\xc7\x19\xe8\xb9\xf0\xb2\x00\xea\x88\xd3\x1b\xad\x1e_hb\xc9;\x08P\x01C\xdeQI\xb7;\x0f\x96$\xf4V\x84\xb2\xf6 \\g)\xde\xdb\x8f\x92 \xc5;\x96i\x97\x9e\x1fd\x18t8\xf0,\xf5\xe2\xb2\x9b\xbc\xda\x97\xe7\xda\xbe0Q\x99\xf7\xb3\xf6\xfd\xef\xeb\xdf\xefF\xe1\x0f\xbd8\x0c\xc2Kv\x96\xcc\x7f\xf2\xeb\xea\xe8y\xca\xeb\xd7-\x0e]\x97\xcf\x94\xd3\"\x15\xd9\x86\x8d\x16\x1a\xf1\xbe1d\x0b?\xa2\x8f \xed^\x918\xa1\xc3x\xf4\x88\xcd\x845\xcb\xd6\xcb\xc0\xf7R~3\xf5'h\x93\xc0\x8eT\x98Q\xca\xe5\x91\x0fC)`\x15{\xb3\\\x12<\x9f\x8a\x96 \x90k\xcfO\xf1b*\xc9U\xba\xb4\x9a\\\xe3n\xc7\x8c+R\xa67m;\x93\xae\xf8\xf6\xc1N\x97\\\x13\xdf\x0e\xc7=\x1e\x03\x8d5\x14,\x97\x9dy\x14\xafdw\xffh\x0e\xe9\x82\x80\xda[*\x8b\xa1\xf4\xf82L\xedx\xdc\x9f\xbal\xafDe\xf8@\xc0\xa5\xb8\x8e\xac\xb5,d#\xc1lhX\xbf\x983\xde\xe6,\xf2\xf3A\x15\x13:\x82\x90E-\xef\xfa\x0b\xe2\xbf\xfd$\x08\xc9\xf7b\xe2\xbd\xa5\xe2[Dw\x90h\n\xef\xdc\x0e\x8a\xaf\xdf\xe7\xad&\xd9\x9a\x8a\xb1d\xd6\xd0hiu+*\xb67\xcf\xfe\xeav\xe8\xa2\xe2\xca\xc0\xb0\xdao\x9e\xfd\xd5\x9a\xc5N\xdfE\x85\xfe\xdf\x12\ny\x16\xd1\x0e\xbf\xd1u8\xef\xa6$I\xed\x18\x03@(K\x9bz\x97\xb0\xf0\xc2\xd9\x92\x80=\x0f\xe2$\xcd+t\xc4$\x94\xfa@[\xc9C*\xa4\xde\xe5\xa7\xde\xda\x85\xb8@\x9b\xc7\xe9\x82\xc4\x84\x1ep=X\xc7\xe4*\x88\xb2dy\x033\xe2/\xbd\x98\xcc \xc9\xe6\xf3\xe0\x1a\xa9\xa2\xf5\x18\xda\x10C\x1b\x1e[R7\x1e;.\\\xb0.\x07\xe6.\xafcB\xab\xb1\x13\xe2G\xe1l\x83>\x8b\xce2\xbf\x87r\xe0\xfc\x92\x96Q\xa5=\xaf\xc4\x92\xe2@U)\xa4\xc8\xdf\xaa\xaa\xe9\x08<\xd1\xa3\x02\xbac\xb0\xd8;\x94\xd8\xf2+\x1e\x888\xb4\x19\xa5<\x08V\x120sz$E\xf5f\xf9\x08\"\xfa\xa7=\x82\xbe\xc3e\x06t\x0e\xf0\xaa\xb6\x15&\xfb=\x19AF\xd7,C\xb9\xa7\xdf\xdf\xeb\xf7\xfb\xc5d\x93\xeb5\xbb\x83\xcf\xa2\x1c\xfc\xe4\xd9\xebW@\xab\xf1\xfc\x94(\xb90A\xdc4\xbca\xab\xe6I4\x84.E\x92\xc6\xc4[\xa1\xc3\x81\x17\x84 \x84Q\xd8Y\xc7A\xc8\xb6z^m\xa2\xab7\xed\xc6$\xc9\x96\x98/\xd53\xad\x99f\xc9>)\x96Lqo\xb9\xe2 \x04\xd0-\xac\xe2,\x833\x1cw\x83\x84\xa7\xdb\x0f%\x0c\xe4\x1a\x9a\x15\x89/ \xac\xbc\xf5:\x08/\x93\x13\xc4\xb6u\x1c]\x053\x8a\xddQ\x16\xfb\x84\xe7o\xa6\x9b@&k\x96\x93\x87\xd8\xa4\x87E[\xf2*xKn\x12;t\x9c|A=x\x02>\xfd\xc3\x164\xc3\x80\x8f\xde\xd4\x95\xe2\x9ce\xd87\x9b\xb0\x90\x94!\xfa\xdb\x04\xecG\xabW\xcfM?\x920Z\xce?\xac\x9b*\xdf\x85\xb9\x8a\xd7Aa\x08\x0cd.\xc3S\xf2\x08#\x91\x95z\x97\xc3\x1bo\xb5\xecF\xf1\xa5;\xe8\xf5\x06C\x9c?\xe6q\xabAsZ7\xbb\xeb\x18$L(2E>\xc0\xa5\xe2\xae0\xf4\xa0\x1d\xe5s\xe7\xc3\x13\x98\xd3?l\xee\x04.Dc\x1fS\x90\x1b\xb07/\xa6\x96\xc1\xe7)\xea]\xe9\x94'y\x8cb\x9e\xde\xa9X\x13\x06\xb0\x99\\\x04t\x8f\xdd\xde\xeaD\xa7\x11x\xecI!`\x95\xe5\x022\x13(\x06o\xc9\x0d&\xe0#\xe3`\xcaB$\xe5\x97~\x83\xe6D>\xea\xe2\x7f\xb9\xd1Y\x8a\x1f2p)\x05\x8d\x92(I\xd1s\x87\xdd\xe8\x12?\xdbmz\xac\xd8\xe5\xc8p\n\xb6\xfc\xc8\xcd\x8f\x9a\xb552Y\xaex\x8d\xca\xe8lz<\xc0\x89\xbd\xa0,\x9en/A\xa8\x18\x85\xc7gmt3\x92$S\x1c\x80\xa8\xacvf>6\xf1\xee\\\x86\x97s\x0e\xd5\x0e\xe1\x84;\x10\x04\xda\xb8\xac\xdc+\xeb\xda\x0e\x1c\x1e}TS[\xbb-\xd7\xa7\xdd)\xb8\xdbv\xd9\xd1\xca\xe0!7\x8bj\x0c~\x9b\xb4\xac}\xf9=\xbc[\x04Td\xe8\xf7\nA\xae\xbf[|\xe7`C\xbf[\xef\x90\x15\xe12\xaa%pv\xbeD\x07\x83\xe6\x89v!\xa6x\xc5\xd6\xfbe8\xa3R*\x9e\x9f\xf8A\x96.\x80\xfc\x90\x16\xdez\xd8\xefu\xbb\x8c\x87\xb0\x0d\x8b\xe1\xc6\x0cq\xa5\x9e\xcd\x0c\x99\x06\x8f{\xc16\x08\xe3\xbe?\xc5\x89\xfb\xd2\x85V\x1f\xbd\xe3\\\xd1\x94@\x0e\xa7\xdc\xbfM\x1aw\x0bf\x8f\xb4 g\xf7|HO\xb9\x83\x10\x9f`\x87\xf3\xb1\x0bo&\x13\x01zj\xf1 !?\x9b\x91\xd0'@\xc24\xbe1\x8a\xd9\xcc\xc7\xacDd\x88\x96\x96\n\x12\xd0\xf28\x8e\xd0\x83\x13Kd$p\x07\xc5\x89\xb4\xfb6\x08g0\x02K\xf4\xc0r\x8b\xcd\x841\xc6\x9a\x04\xca\x9f6\xd3\xa8\\\xc4D\x8c\xd6\xef\x80*\xa6\xd3!\xee\xee\x16\x11\xc2\x1b\x04\x90\xdc\x7fBW\x8f\xb4a\xe8\xf8M\x1a\x18\x8f\x1f+\x99i\x87R\xe5\x03.\x01m\xc2-0\x12m\xc41~\xb3\x17\x86\xb0\xcb\xa4\xa4@D\xb1\xc58\\t\x19Z-k\xf3Z\xd8\x1b\x16\x0b6 \x0b\x94\x91N\xf20\x8a\x03\x9b4\xa7\xbc\x98\x8b\x01\x92\x14p00\xb2~\x89r<\xc9\xb3\xf8\xd1\xd1\xc7\xba\x83pi\x97m\xd2\xbdBL\xcc\xc2\xfc\x04K\xc2\x99\xd0 \xf0\x83\xe8\xbb ]\x04!xpE\xe2\x0b/\x0dVt\xe5\xab\n\x1eS\xa8#.\xb9I\xe3m\x9d1)._M\x96D\xe0T\x9c\x80\xbdK\xa1\xf3\xe0\x07H~\x10\x06r\xed/\xbd\x15C\xc0\x95\x17\xbfM\xac<\x0eqe.X\x16\x85\n\xdd\xcd\x15;\xf2\x195\xf4*:\x9dJ\x9bI\xe6/JGn\xe6\xa5I1\xaf\x8c>\x8c\xb4o6\xef\xeaB7\xaf\xe7*WJ\x15\xba\x02\xe3L\xcd\x97\xd1;J.\xe9v\x8d\xe2R\xff\xcb\xab\xa6#\x7f\xc8\xc8Z\x17\xfa\xf60\x99u\xfd\x1c\x0d\xd1m#F]\xe6)\x08\"\x1a\xc3PU\x83\x85\x8eT\"W8\x85STs\x0d\xe9.\xe5\\\xa2(Ea\xe2\xa9\xee\xb1z~\x16\xe5\x99\xb6-\x0bs\xcd\x9a\xb4\xea\xa8Y\x0bQ\xb3\xf6\x18=\xc1k\x89\xf7\x0f\xcd\xc4[C\x96\x8f\x18Y\x0e\xefA\x96\xcd\x82\x8c\x9e4\x87\xc0K\xc8\xe4\xd9\xd0\x81\x12fV\xb1Zl\xdc\x90o\\v\xd4l\xbd\xb0C\x07\x93\xc76\xd7\xa8\xe5\xb0\xd2\xb6\xc9u \xc5~,\x0f!\x8cf\x04VYR\xe0\x9b\x97\xc2\x92xI\x8a\xaa{I\xcbVb\xd3\xf5\xbb\xa9a\x81\x7fJ\xd2\x86i\xf8\xc2U~I\xf2\xc6\x85K\x17V.\x9c\xbbp\xe1\xc2kf\x8c\xd20\xed7\x06f\xfe}\x033\x97\x16{\x19$) I~Vb\xbfl+Zc\xd4\xd9T\xe8j\xa1\x88\x1e\x9d\xcf\x82\x00pyE\xfc\xcc%\x15\x06@\xb5'\x8c\xd0\x19b]\xc8eLA\x85A\xeb\x1f=R\x04Q\xfbM.\xaf\x96\xc578e\x93\x00\xc3\xca!\x93\x9f:\xd0\\W}\xf8\x84+\xc2>E\x97x\x07\x0d\x1e\xf4\x85O\x0d\xde\x9a'L\x82\xba\xbd\xc5\xcdx\xe2\x94\xbbwZ\xf4\xee\x86\xc9c\xdfJ'a\x88\xd5\xeb\xd6\x8f\x07j\x80\x11\xbc\xa1\x9d\x8cr\x0b\xce\xa7\xf4\xc1\x9ao*z\xea\xbb\x80\x11\xf8\xc5\xa4\xcfs\x92F\xf0<\xd6\xa6\x9c\xecu\x99\xd5\x94\xec\x88\xf9L\xc1)\xbf:\x8eg\xaf\xd789\xdb\xd8X\xdcB\xc9\x9b\x98Og\xc0=w\xcc'4\xe0^;_\xd5\x8475=\xcb\x91T\xfb\xf4\xaa\xf6\xe9M\xed\xd3K\xc3\x06\x04\xeeG\xa3\x0b\"|\x87\xf3\xe3\x92\xab\xac7;?z\xc6$D\x18\x84\xa8\xa9\x1e.\xd6D\xd2\xa1-\xab\xc8\xb4\x07\xecP\x80\x07\x9a\xfd#\xfe\xfd\xf6\x96\xd2\xf2\xb8\xf9\n%\xd2\xc1\xd0\xc5[\xaf\xec\x08h\xd4A\xc9\xefI\x07<\xadL-\x7fX\xaa\xdf\xa6\x91:'pm{t\x9f\x1b\x8a6\xc8W\xf2\x87\xf6p\x9f\xf9[x\x0e\x9c\x99\x1a\xafH\xca\xb9\xc4\xe8Q\x11\xfe\xffc\xee[\xbb\xdb\xb6\x95E\xbf\xf7W\x8cx{\x1c2\x92\x15I~$Qlk\xa5i\xd2z7ur\x9a\xa4\xfbt\xcbj\x16-A6\x1b\x89T\xf9\x88\xed\xbd\xdd\xf3\xed\xfe\xb1\xfb\xcb\xee\xc2\x0c\x00\x82$@\xd2N\xd2\xd6k\xb5\xa1@\x10\xcf\xc1`\xde\x93\xb2d\xe3\xcf\xb5\xdbG\x97\xad\x82\xbf\xe4%\x9c\x82\xfe\xc0\xae\xb7\xd1w\x02\x12\xb6\xf1c\xa4\xc6\x149}\xb6\x8a\xe6\x1f\xa4\xd4\x9a__\xc8l\xb9\xa8kX\xf5\xf2\xa88Z\xc4\x9b\x8f\x02K\x8b\xa2\xb5@r\x02\xb8\x91\xf8\xe4\xff.\xd4\xf9\xc5/$\xc2\xaf_\x97\x86\x9c\xcc\xf2\x0f\x01c\xad\xb9g\xd1\xd5\x93\x14\xee\x9d9\x07\x96\xfa\xee\xf8\x9f\xd2\x13aD\xd8\x98\xf9\x0b~\xf1\x07kN\xcd\x04\xa9\x12\xe8o\xfc ~\x02>\xcc\xa3U\x14\xf2\x95^\x07IR \x9bW\xfe3\xbbKC\x1d\xb3\xa2\xff}\xaey\x9a\xe6X\xdcz\x12_\xf0 \xae\xb3U\x1a\xe0\xd9\xf9\xc0\xaea\xed_\x830q\xd6W\x05\xd5\x1b\xf6\xb9\x19\xdf\x88\x19\xef\x13\xcb\xe5\xf3\x0b\xf2\xd3\x80Mp\xed\xe42yN\xedi08\xc8Y\xcb \x9cG\xeb\x0d\xea_\xd8\x95ec\xf9l\x91\xceS{\xfb\x04\xa2\x18\x96\xd1j\x15]\xb2\x05\x9c]\x83\x8fj\xd0\xd4?\xcbV\xa8\xeca\xebMz\x8d\xca\x0d\"\xfcr\x9c\xa8\xbc\xa6c\xf3\xc6P(\x11\x0dEYeP\xae\xa4\x037DZ\x04T\xca\xa7\xab\x1f+A\x06hB\xb1s\xbc\xd9+k{-b\xd9\x1b\x97\xb7(Hk\xc6\x88\x9e\x81\xa8Qr3\xbfVnV\x80;\x9b\x17c\x93\xe8\xac\xf2Q\x15\xf2\xc4\xd1AH\xb3\x01\xda\xba j\xab\x9c\xae\\\xd4&\xf1d\x81~\xc5\x16\n\xfd\xfe\x81\xc4O\x0f\xce\xbc*\x01d\xa3~\xcaZ]\xccY\xb3\xd4\x93\x88u,\xf9\xc6\x17\xf5\x84\xd2\xc7FB\xe9\xda\xe0\xad\x04\x02H\x859\xa8\xbbi\x86\x05\xd2\x89=\xde\xe9 98IbM\xe9\xc9k0\x1f\xefs8\"\x82ac\xe5EUmN>\x8f\xf6D\x8f\x03\xea\xf1?M\xfeip7\xb2*\xf6(\xc3T\xd3=- \xabM-a\xa5\x8e\x1a\xf3z\xad\x96W\xe8\x0b\xab\xec+i\xd2\x08v\x17\x05\xd8\xfd\xa8\xc1.\xc7\xb7\n~al\x13\x1b\xc7\xf6\xcb\xe4\"\xa7?\x08?\xc2>9\xc5\x9f\x04\xe1\xf9\x8a\xc1\xefY\xc4\xab\x8a\xbdGZ\xa2n\x96\x86\x83t\x1b6\xc3\xdc\xe9\xe78):\x83a95\xbb\x04\x1e-\xc4t\x9f\xff\xd4`\xe2m\xf3\xa9i1\x9eZ\xc9\x88\xf0]\xf5\xd5\xa0\x8d\x18m\xe0\x95\x87d\x03|\x14c\x8dd\x9b-\xce\xa2\xa9\xab\xcbv*\x1aO\x87~\xfb9TrM\x9f\xfcE9\xd0\x7f\x98\xfa3\xafp\xc1\x1c\xa3\xef\x88>\xc9\x16-Rp\xd1\x910\x83\xe3\x1c\x8b\xcf\xcf\xd2\x08]\x89\x1f*Vf\x17\xc6\xf0hO\xfd\xe4l\xc3\xc0\x83#\xfe\xbf\x16\xba\xb2\x80\x14\xda\x11\x19m\x07\xfc\xbb'\x10lo{\xd8\xfb\xd3\xb6k\xc5\x99\x14\x0c\x1b\x87~5\x07\x07\xb0\xebA\x172\xc5R\xa9\x13x\xc1\xae\xfc\x05\x9b\x07k\x7fU\xef\xd2\xa4\xff\xe9K\xf9\x9b\x1b\x95\xe0\xc5N\xb7\xd0ZJ,\xf0!\x8c.C\x10\x11\xd3\x94\xcc\xac\xa6\xeb\xea\xc9\xa8\xc7\xa4~\x8eI\xe9\xe8\xdb0i\xb5\xe1/\x84I\x17Qv\xd6\x06\x93\x96\x06\xd3\x82\x96\xb8\x0dj5\x8f\xc2\x88Z51NGC\xb26\x0c+\x0c\\\xcdXu\x97d\x18\xcd\x8a\xef6X\xd5\xd2H+s'2\x81{#\xac\xdf:\xcf\xdd\x98\xa3\xcd6-V\x07s+\x93\xa7U\xe0'\xb7\xb2x2\x18?\xf6\x8a\xa6N\x9aH\xbd\x14\x8eE7\x84\xbc\x97\x85J\x0c\xb0\x10\xe3(\x19\xc5iw\x92.\xa6\x0fge\xddU\x95\\\xe5`rWS\x14\x94\xba.\xa5\xbc\x95\xdf\x94v\xe1\x9c]\xd1\xcd\xc1\xeb\x8d\xbbl\x06,\xbe\"\xcf\xdd%\xb9}\x12\x92F\xa6w\xe7Q\xfe\xbc;\xd2\xcaw\xf2g)\xe8\xc3\x1f\xfbz\xa5\xc7\xda\xb3Vg\xe7\xa1V_+\x7fL\xa1\x1e\x96\xb5P\x8e7\xce\xbe\xd6\xbd\x10\x9b-IF\xff\xa6\xf9\x18 \xee\xec\xe6\x86\xec\xfb8\x98\xb78X\xcd\xe4J\x80\xbe\xe4ErWX\xad\x8b\x03\xb6\xac\xa5B\x84u\xc6\xb2\x89b\xb8\xe3\x14k\x98g-\x8f\xef\xce^\xdbA\xd4\x0f\x00}eZ\xf4\xd9$\x95h\xbcj\xf29.\x9b\xa5\x8f\xbc\xcdK\xac\xd8l\x05\xe1+1\x8bT\xd3h\xc6gsU@\"\x13\xed\xe6DdP\x14\xdc\x1c\xda\xb3t\xe9\x7f\x99\xc6\xbf\xdfYZ%\xfej\xe3\xb6\xcb?\xbb\xc0\x04\x8af\xf8\xc2\xff\x83\x8c\x078~\xd2wB\xe8\xaf\x0b27Kr\x01\xf9w\x179\x8e\xb9\x14\x15`D\xcb\x10\xfe\xec\x0c%-#\xc6\xbb\x0d\xbeWw8\xbd\x1e\\ \xcc\xe7\x16k\x08C3\xcbv4\xb8<\xd8n\xc4\xf2P;\x1d\x85F\xc8%X\xa0\x99\xa2\xc5\xea\xa6*Q!R\xa4'\xad( \xfd\xbd\x16 \x94\x07\xd0\x96\xde,\xca\xd8\xc0\x998(\x9b\xaa\xa9\xab\x95\x08\xcdnn\x07\x96\xdf\xd5\xc9E\x94\xad\x16h\xabs\xe1\x7fd\xe0\x87\xd7\xd2\xf2\x1a\x95\xb0\xd2\xdf\xbb\xb5\xba[\xe9\x15s\xd1\xd9\x8fjVh\xe4)l\xe1h\xf5\x91\xb9\xda\xd4\xeb\xf1\x84\x06\x13\xef\xfbs\x19;OwM\x93\xfb\xfc\x9e4\xccw\xdc\x82\xcf{~\x05\xb2\xcf=!\xae7\x8c\xbaFh\xbf\xb9\x01g\xe9\xafVg\xfe\xfc\x833\xeb\xc9\xed\x99\x80X\xb7\xda\xeaS\xac=+\xccT\xac\xd1\xd6\x16\xbc\xa7O\xa8\x18\x1f\xcd\xa1d\x10\xa2\xf1=\xdf\xfe\xce\x01\xc6\xe0\xc4\x95\xec\xc2\xbd#H\xfds\xd4< \x98?\x13\xbe\x13\xa2uN+\xf6\xf0 `i\x9a\x97\xdeC\xff\x9b\xca.\x93\xc3{\xd3N\xdeq\xebr#4\xa1'\x13\xdd\xa31\xd9\x82!\xbfS\x9a\xa1s\x94+\xe1\xd0\xcbI\xf7\x91\"~\x94W,\x7fdI(\xd5\xc2\x8a\x7f\xbe\x8a\x12&\xcc\xf8K'\x99_\xe8\x95\x89\xdf\xdc\xc0\xeb\xafr\xf8R\x8f\xcaw\xe1\x87v\x9e\x85\x1a\xfa\xaf\x00\xa9\xc9\xc3P\x90~Z\x18!\xe1KP\x0d#\x94\xf6W\xec\xdc\x9f_\xf7\x94K\x8f\xc8l\xa6m\x18\x99=I\xb1U\x0b\x97E\xdc\xf1\"\x9f\xd1\xfcU\x0f:nIs4\x10tw\x07-z\xcc\xd20\x9ck\x06\xed\x9d\x13m|d\xc1\xdf\xadMC5\xbc\xect\xd63\xfa\xba\x15\xd8=\x19\x0f\x05\x0e\xc8\x8d[\xb8\x07\xa9xH\xc8k\"kiR\x1b\xeb\xe6\xcc!PKNCd\x06\xf8L\xd1\x19\xa0\xa8\xa1\xad\xcd\xb1\xd4\xa8\xa3m3\x04;\xd26\xf8hR\xfc\x05\xfbUPC\xdd[gZ\x1b\xd2\x01\xe4\xb2~1\xc0\xe2\x7f\xb1t\xe7\xae\x81\xa8\x16\x04\x9d6&\xd2;\x8b\xeb\xed'\xe1\xe1\xf7\xd34\x9cI\x19\x1b\xc7\xa7\xaf\x85\xc4\x81\xf0\xa9\x12\x82\xe5`Z\x90<|e\xef\xbc\x88\x0f\x06\x1ak$\xce{\xee\x9e_\x8f(\xdaV\xa4x\x0e\xed+\x8f\xbcbD\x17\x11\xe1A\x1f7_\x90\xccpV\x13\x14\xd0\xad\xfd\xb8\x12\xb7\xe5\xe7\x9c\xa6\x17\xd3D;\x8d\x8df\x9cV\\\x98*\x92\xde\xda\x82sr\xf0,\xee}T\xdc{P\xa18\xc2(\xdc~\xfa\xe6\xd9\xf1\xb1\x16O&\x01?f\x10\x84)\x8b71C\xc7\x87\x04\xd9-\x15tNnmR \x1b\xd0\x82\x9f\x9d\xc0\xee~\xf3\"{\x82\x14hXa\xad\x82\xe6I\xbd\xadc\xc9\xaa<4\x8aQ\x16*\xc03\xf7\xe0(\xecG\xede\xfc\x9dk\x8c\xc2XL\n\xc3d\x86(~G\x0e$\xbd\xa0\xe2\xda\xc9\x901\xa5\x05\xc8\xa7\x80K b\xc9\xd4Wrs\xf3\x82\x1e\xec\xef\x8d\x1e\x8aX\xa9\xfaG\x03Y\x93\x97\x8b<\xfa^\x19\xf7Q\xb2\x04\n\xc5\xd9\xa8YK/\x82\x84\xb6\x100\xfd\x01\xfe\x96\xd131!\x92\xfa!H\x1eQ'\x91\xf1\xd8\x99|\xbc\xb9A\x9e\x9b\xbf\xcc\x03Y\x1eb\xda*\xf9\xab\xd8\x04Q\"XE<\xde\xdc\x90\xd5\x02\x7f\x8b\x01\xaa\xf8;\x19\xa9J\xbdQ\xe4\x1a~)\x7f\x14\xdb.01|j\xf9\x981\nx\xb0b\x8bcQG|\"\xe8wK\xe5\xb7\xf4V\x0d\x1d\xf7.\x07\x06Q\xae\xc9\"\x06j\xb4(\x8e\xd0\x7fJ\x89\x84^\xa6\x1b\x02a\xa1:\x9fH_\x14\x11-m\xa7\x81\x08\x0c\xc5^\"$\x0d\x1c\x158(\xac\x1e\xd3P\xbb\x80<\x08\xf5A\x90\x9bFX8\xb7&\x92\xf3\x89^\xe7 \x0f\xf8\xb8\x0d\xc3'\x1e\xfc\xe0Z<\x8c\xc3|n\xb5\x07\xf4k\x9b8Z\x13E\xc3!\x9d\xe3rW\xc8G\xcb\x96\x1c\xcc-B\xf9\x88\xf3\xfc$\x91aFZH\xac<\x04[\x0c\x07\x10\xf0\x7f(\x04\x1bs\xa3i<\xab\xc7-\xdf\x1b\x0f\x9c<\x99\xdf\x99\xf6/XJ\xaa&T\xc9\xaf\xaa\xe7\x95\xd7\x1a\x8a-\x95\xb5\xe4\xb2N\x07\x06\x9f\x82<\x81C\xe0\xe6\x8aC\xa5\xa1W\x184\x085\xec\xda\x83\xb3,\x85e\x94\xf1[.\x8a\xd9\xad\x128\xe4I\x0c\xbe\xeeU\x93\x1e|\xdf\xb3\xe6+h\xd2B\xb4\xd8S\x04\x99\xb8\xcf\xaeR\x16.\xdc\xea\xf2\xd1\xa1\x1eCV\x9c\x0f\xef\xac\xb4\x1d\x12\xf8\xee\xd8\xd8W\xdaOc\x02\x87Z\xcc,f\xf3\xfd]gS\x8d\x0f\xfc\xe9\xe9\nL\xc1D\x03\xb7\x10z\xb1r\x97r<&.\x12\x89e\xcf\xb2\xe5\x92Pw\x15e\x86E\x94\x19\x8b\x9f\xf3h\x95\xad\xc3B\xa0\xd3\x1c\xee\x02-\xa3\xc19K\xdf\x84\xc1f\xc3\xd2\xa6\x05\xae\x98\xabW\xcfbG\x1b\xae\xa7\x0b\x0dL\xbc7\x88\x00\xf0\xbb\x1a\xc5\xf0pOD\xc0\x91\xf1o\xf4\xd9\n\xeb\x00~\x9do\xd3yvN\x07\xa7\xf1i\xf8\xff\xfe\xaf\x9eU\xc0\xe9\x07\xe1\x82]\xbdZ\xba\xdah\x10\x8b?M\xdd\x80\xf4\x17\x96\x90U\x01lS\xf0\xc0\xc2\"oc\xbf\x0c\x1e\xc0\x88(\x0f3\xb3\x86\xe3\x86~\xbf\x0f8\xf8\xee!\xec\x99\xb9\x946\xeef\xb8Dz\x1e\xbd\xd2Jd\x9c\xec\xd3\xa6\x97\x93Ww^\x9a\xcc\xba,n&\xd0\xf8vieZ\xacJ\xa4\xafJ\xc6\xd7\xf7\x13VE@\x94/\xd7CL\x80\xa8\xba\x80\\\x11sSJ@1\x94\xe0\xbc|4\x00\xefR\xc0\xfcn\xb9\x16t\x0d{\xde\xd5\xee\x8b.8\xbf::\x82\xd2\xcf\x90L\x19\xd86\x1b\xb5\xe3\x18\xef\xf8\xfc\xe8s\x82\x15)\x88{A($\x8f\xea\x1dFK\xbe\x87\xaarN\xb1\xf8)q0\x0e\xc6\xa3W\x98\x00\xf9\xba.\x9f\x9b\xc0\x04\xf9{Q@*\x10\xd2M0\xb9\xa096p\x85\x88\x8az\x19\xd3\xaa1\xde\xad\x11M+L\xf3\x89Hs\xa0])z\xe3\xfc2\x8e]C4\x9c$\x8d+\xd9\xfd>\x04\xe1b\x9c\xabs\x0b\xef\x94\xf7\xd7lu\xdb\xc6\xcd#\xaf\xdb\x17\x91\xe7\xf1Mz\xbdbcp\xd4z9\x7f\xf5q?\x8b\xa2?\xf5\xb8\x1bL\xa7Z\x1f\xf7\xc2\xb1N\xe3\x8c\xe9\xc7\xf8m\xf9\xf7O\xef\x9e\xcbc\xcd\x0b\xf6\xf4\x8f\x97\xfe*)\xd4~Q)x\xfa\xf2\xcd\xf3\xbb\xa2\x85\xbas|\x9b\x81\x7fN\xfc\xe1LE&\x81o\xa2h\xc5\xfcpF}T\xf2\xd2I\nT\xa8\xe1k\xe7^\x8bmL8\xc1\x9a\x82\\\xd2\xad0\x91\x0b4\x06\xb1KmN\xb1 E\xb4\xea\x8b\x16{,\xf7\xbbM_&\x8c\xd1\xae/9\xaf\x17\x96y\xfd\x1d\x10\x88%3\xe2m\xb3\x9aV\xf2\xa6\xed\xe5\xe344\x94\xb5o\xe8\xa1\xd6\x90|*c\xba\xc0\x84\xe9\x820\xfd; :\x12\xd7\xe8\xb2k#\xe0\x04v\x87zS\xc3\xca\"\x17\xee\xe4FU\xe8\x1a_\xe7\xbfD3\xeed\\\xbc\xc7\xf3\x1e\xa8\xf2\xe9i\xdf\x9d\x8c\x83pys\xcc\xff;y\xe1\xddPQ\xe8\x877'\xfe\xc9\xcd\xc9\xd3\x13\xcf\xfbZ7\xb9\xc7\x80\xfc\x98\xadW\xeb\x9c=\xb0K \x8d\xbc\xf3r\x15\xf9_\x84{\xd6\x85\xdb\xa4\x15\xe1\x88\xd6\xedD\x82\x80\xf1t\xda'\x9d\xeaf{\xb3\xcfN\xd2\x18#\xc1\xc8\x11\xc2!H2BX\x1eW\xa8\x91~\x1a\xbd\x8c.\xe5\x89\xe6\xa4\x04L\xf8=>\x06\x11\xfcw:\xeb\x81\xd3\xdd\xceu\xe7\x0c\xe9\x95#q\xc1\xb8d\xf2\xa7h\x91\x1e\xf0\x9a\xcb\x9c\xf4\x10\xa6G0\x11wY\xff\xf5\xab7\xc7o\x8f\x7f~\xfe\xfe\xf8\xe4\xc5\xf1\xc9\xf1\xdb_`,_\x9d<\xff\xeei\xf9\x95\xd3\x0f\xfd0o\xee\xc4?\x811\xb0\"\x85!0\x9b\xcb\xeeFf\x04E2\xe3\x05\x07\x9cZBCX\xe7\xc5Dh\x04\xb7\xe8\x8aIB#\xe6\x9f\xdb \x8d\x10\xees\xb2y\x8c\x0f\xda\xa8\xd8\xdf\x89\xd4p\x89\xd6\xe8\x1c\x92\x1b\x86\x81\xd4hKk\x14\xf0\xa4\x0d\xe2C\xb3l(HN\xfc\x13\xde\x17$\x97A:\xbf\x00\xd7*;\x98\xfb \xd3\xe5\x90cc-\xd0\x16\x07\x81\xcf\xcc\x1dQcJ\x8a\xdb\xa6\xb1\x93\xa7'\xb5\x8d)1m\xab\xc6\xfc\x13\x83<6\xf7x\xb6\x1e7!\xf4\xfb\x12\xab\xc5O\xfeg[\xad\xe3\x93\x17\x9fo\xb5\x8e\xc3e\x9b\xd5\xaab\xa0/\xb7Z\xdb\x9fu\xb9\xb6?\xebzm7.\x98\xe9\xb4\xe7\x9f\x0f\xfa\x03\xc3X\xb4{\xa9H\xf6\xf6 S\xc9\xbc&\x10\xaak\xcaa\x0e\xbfP(\x02fX\x87L\xfe,]C\x99\xfc\n*\xe4\x97\xa2\x8e\xb4\xffy\xdb\xae\xed\xc7\xd7N#A\xd7\xd8\xe2\xa4\xf4\x8b\x93no\xd3\xd9\xcd\x14NO\xd3Y\xd7+\xbc\x1c\xeb\xbd\x17~\x10}H%\xf7=\"\x10\xb1\x85\xfb\xee\xbfn\\N\x8by\xe5n\n\xdf{\x13\xcf\x9b\x14(\xb9V\xea\xdc4X\xb3$\xf5\xd7V+\x96\xcfN\xac\xe5\xe1\xca\x83>\xbbbsA\xb3\xa9\xd2H\x96~\x01r\xcd\x10\x07\xc5\xa23\xd9\x08\xb7L\xf3\xb5\xa7\xf47H\x81\xa9yx\x8a(\xcb'\xa1\xe7'\xf74\xf3\xee\xe7q\x1c\xc5\xae\xf3\xad\x9f2\xe5K\xcbx\x99)(S \xf2\x89v\xd9t8#\xda\xa7\xcb\xa6\xa3\x19y+e\xf4sg\xd6\x83\x0e\x9b\xee\xcer\xf3Wv \xbc\x03\x97\xff\xaf\xff\xee\xed3W,\x83\xc9\xff.\x10\xe1)\xba\xbc \x8aN\xd1e\xd3\xbd\x19\xc5\xa5\xe8\xb2\xe9\xfe\xac\x07l\xfapfC\xc2(p\xc5\x80\xb7\xd3\x873A\x94\x0ez\xb0\xe3=\x81U\xeeK\xb9\xf3\xc4\x83\x15\x1a\xf6\x99\x90\x14\x88\xa8\xd1\xddU\x15\xfd\xd9\xc0\x8bM\x1f\xcfp\xe1\xf9\x9e\xed\xb3]\xb8\x0f\xee\xfe\x00\xee\xe3j\x0df\xd0\x85\xae\xcb\xa6\xc3\xe1\x8c\x83\xd9@\x8a\x00qC\xf4/\xb77\x9e\x88\xcb`]6\x0dzV\x1eFS\xdf\xda\x82e?a\xe9\xdb`\xcd\xdce\xff\\\x93?\n\x0d\xda\xa5\x0b\xce\xd3o\x9e}\xfb\xfc\xc5w\xdf\x1f\xff\xe3\x87\x97?\x9e\xbcz\xfd\xdf?\xbdy\xfb\xee\xe7\x7f\xfe\xcf/\xff\xf2\xcf\xe6\x0b\xb6<\xbf\x08~\xfb\xb0Z\x87\xd1\xe6\xf78I\xb3\x8f\x97W\xd7\xff\x1e\x0cG;\xbb{\xfb\x0f\x1f=\xee>8<\x0dOc\xe7\x96\xec; x\xbe\xc4\x86\xddY\xfbm\xc1\xd3A\xa3b\x9cc\xc7\xc8\xa2\x1e\n)\xf2_H\x1eCa\x9d\x8e\xa8\xe3\"b\xcfr3vi\xbcN1\x00a\x7f\xb7Qk\xc4\xe0\x00\x06\xad4?(\x13\xdf7\xbe\xb6\xe2\xc1\x18\xfe\x0b\x1e\xa1\xf0\xb9\x08\xf6\x9f|q\x06E\xe9\xc5\xf44>\x0d\x0fgB\x86a_\xf4\xa0v[|\x8c\xffc|\x95\xd8\xb7{n\xd1\x07)\xff\xee\xc1\x13\xe0\xab\x9c=\x01\xd6\xedz\xc0\xe0\xbf\xd0\n\x8c\xe4%\xa4\xce\x99\x8b\xfc\x10pt\x04\xc3}\xd8\x82\xd1\xde\x9e\xd7\x03\xbd\xf8Q\xb9t\xb4\xb7\x07[\x90p\xa4\x9f`\x12\x90\x83\x03\xd8\x87\x1b\xf0\x158\x04\x12\x1c\x98\xe9r\x15[4\x00\x19\x087\xc3\x81\xdd\x87}T\xd1|\xd2\x90`\x0c\xc3GJ\xd0Slk`lk$J\xf1S\xe1q\xc8\x97F\xaf\xb3\xab\xbe\x8c1\xe9\xc62\x8e\xd6\xea\xc1\x9d#O\x80\xe8\x1e\x1f\xe7u w[\xa9\x08\x06\xf6\xe0,\x0e!\xd0\xf6Z\x93\xb6\x00\x1d\x93s\x8b\x15\xa1X\x80/k\xc45~\x0d\xae\xb1@\xe7N :\xf1\xe4\xfb\xd3\x00\xb7\x8fo\xfa\xfe\x0eR|Z\xe9\xc8T\xba_*\xdc\xdf\x81-@s\x1c>#7\xe0\x10\xfb\xc8\x83.\xa4SfW\xa8\x16\x01t\x87\xf4\x87\x9fyD0\x86Q\x0e\xae\x85v\x06\xa6vv+\x85\x07\x07P\xeeq\x7f\x17\x1b\x1e\xe6\xc0\\h\xb9:\xc0\x83\x83J\xc3\xfb\xbb\xc5\xf6z\x10\x17\x01O\xfd\xfad\x02\xc2\xca\xceVd\x7f\xc58\x93U\x02\xc1*,\xbc%\x89\x16\xd5x2X\x9c9>\xf1\xca\xb7\x19\xf2\x97\x985\x12\x83[o\x03C\x80\xca\xfc\xb8\x91>z\xae\\\x83\xf9\xe1\x0b\x9f\x90 \xd8\xea6\x16\x88|\xa1\xf3)\x9b\xe5I\xc0\x94\xa8\x96\x16|\xe6\x08f\x15E\xb2q\xb3=\x87\x08\x84\x13\x84\x10\xd7\x1b\xf0\x04\xa2Id\xd3j\x08\nY\xdfo\xecZ\xfe\xdd\xc9P\x07i\x9f\xe6>x5a\x81\x90\xa8;1k^\x16\x11\xce\xa2U\xd2\x0e\x058\xc5SyG\xfa\xa6*\x9c\xf8\x93<\x8cZ\x1c\xfa;\x9e\xe1\x8d\x1f\xc4\xc9\xdf\xeb\x10\x0b\x7f\xdd\x9a\x83\x9a\x89\x19=\x8dc\xff\xda\xf5\xa5\xdb\xa3R\xf4\xf0\x13\xec\xdf\xed\x04\xfbx\x82\xcd'7h}r\x03\xf4\xe1G\x93!\x0d\xe1~`\xd7 \xff\xba\xec\xd6ok%\x9b\xb2\x19Ge\xd1t\xc0o\x19\xfcw6\xfb\xd3\xa1\xde\xb2\x8f&\x9a\xfac9\xd4\x99\xf0\x06\xb6\xeccT\xd8\xc7\xcc\xb8\x8f\x99m\x1f\xf9ne\xb8[Ae\x89{\x10\x89\xb5\x0b\xc4\xda\x05\xb8vV\"&\xfa\xeb\x0fp\xf1\xd6\xbe\xe51N\x98Uun\xf6)\xfcrg\xb8\xf6\x82\x0dB\xb0\xc4\xfe\xd2\xee\xb1\xb0'L\x10\x15\xa2\x0d\xa7lV{\\>/\xc4\xdb\xf0\xfc\xdf\xcd\x8f\xf2\xb7\xe4A\x16.\xd82\x08\xd9\xe2\x13%/5\xcbp\xfbE\xf5*\x19\xe6o\xcb\xcf}\x8c\x82\x85\x8c(V\xd7\xbb\x89\x93\xab\x13\xfa\xfd\xcd\xbc\xa1\x7fK\x1e\xc4\xec\x9c]}\x11U\xca-\xe4f\x01F\xa6\xc1zm.'\xe5Mg\xa6\xb19\nxp\xfa\xc0\x9d\x9e\x07\xeb\xd9}\xef\xeb\x07R\xb3a\xae\x1e\x1bb\x0c\x80\x18\x94\xf3@\x8a\xdd\x07V%\x02i:\xa4\x05o8\x1d\"\x1b&\xd5\x07G\x9c%mq]\xf3\x9e\xd0\x9aw\xcar\x03\xa0\xb8`\x0b\x947Si\xe5K\xdf\xc1\x7f\xce\x8a\xcbS\xa2-:\xa9\xdf\xca\xab[0\"\xea\x81e\xc5P\x93\x95kFY\xaf\xcc\xc7|\"\x92PT\x1au\xd0\xd6\x14\xe6\xb6\xf8\xa4vC\xf8Zu!\xed'Q\x16\xcf\x19ty\x81ua\xd3\xfe\xf9*:\xf3WB\xe7\xd7=\x04\xe7\x9cB\xf5\xe5\xa9\xe7\xf3Wkz\x15\x9c\x87Q\xcc\x9e\xf9\x89\xfe.\xe0\xef\xd8\x97BfO\xb4J\xea~\xd1\xa21]\x06\xe1\"\xbaT@A?\xfb,\xd9\xc4\xc1\xda/\x19\x06\x06\x8d\x98\xd1\xa8N\xf8-y \x07\xff\x17\xe3\xc6\xaa\xbaF\xfe)\x18p\x11\x06\xf8\xe6{\x16\x11!\xc8\xf48}4\x0e\xe3g\xa1\x9eM\x8f\xfd\xf0\x9c\x8dkyo[TQq8^\xc7\xd1y\xec\xaf\xe9P\x84\x18\xfb\x8e\xef\x98\x0c-v\x16-\xae\xb58<\xce\xf3+\x0e\xf9I\x10\x85oR?ek\x16\xa6\x8eVu:\x98\xa9&\\\xe7i\x1cG\x97/\xc4\n\xe7_\x96?`\xea\x0d}\x8bN\xcf\xb7\xfd\xca\xc0\xe6\xebZ\xb1\xba5hD\xd4\x9f\x84\x8eEt\x9c\xe6\xcd\x0f\xb4\x8d\x0f\xeb6\xbe~\xd3\xff\xb0`s\x9b\xc3\x0b\xdej\n\n\x88\x81\x95\xdb0\x14\xbfu(\xe0\xbbc\x84\x82\xbc\xaa\x82\x02^\xd7\n\x04\xc5\xfae \xe0\xc0v\xeb\xaf\x0cf\x10/\xfc`\xc5\x16\x90F\xca\x16B!\x0c\xbb6\xc5\xd8\xc1\xc6\x8f\xfdur\x0b\xab\xd0H\x06T\x0d\xfd\xb5 >\xc5\x0di\xec\x0cW\x1c7\xba\x07\xce7\xabh\xfe\xa1t\xde\xec_\xe1\xf2Mp\x0d\xe4\x02\xbaQ\x0fB\x199x\x8a\x96\x0b\xfc>\x9e\x0egt\x01\x0b\x95\x8b^\xdd\x91\x08\x02#F\xe5\x9f\xd2g\xf5&4w\xbe\xa1\xe5\x00\xfe\xd4;Z\xdd\xba\xcat\xed\xcb\xda8X<\x00\xf6F&\x8b1\xf7\xd1N\xa98\xa3\xda\xe5b\xbfN\xdaW\xac\x9a4\xcb\x15J\x08\x0f\x0e\xe1q\xb1h \x870,i\xb3Vp\x08;\xa3\x12(\xf0\xb2\x9db\xd9\x05/\xdb-\x96-x\xd9^\xb1\xec#/{X,\xbb\xe6e\x8f\x8ae\xe7\xbc\xac4\xbe5\x1c\xc2ni,\xefyY\xa9\xdf3^V\xea\xf7\x12\x0ea\xaf\xd4\xc7\x15\x1c\xc2~\xa9\xbd7\xbc\xac4\xb7\xe7\xbc\xac\xd4\xc7S\xbe|%7\xc4W\xbc\xac\xf4\xedo\xbcl\xbfX\xf6\x01\x93\x15\x96*\x1eca\xa9\x97\x1f\xb1\xb04\x95\xb7ph\x80\xf8\xc1\x18\x9c\xd3\xd3\x81\xe1\x1ez\x88o|\xc3\x9bG\xf8\xe6\xcc\xf0\xe61\xbeI\x0do\x86\xd4Qhz5\xc4W\x1fM\xafF\xf8jiz\xb5\x83\xaf\xca\xd4\x1c\xff\x1b\xd1\xd0\xcbBh\xfe\xb7\xb3;\x86{\xa7\xa7\xce=\xc3\xd8\xa9\xaf\xd3Scg\xd4\xdb\x89\xe9\xdd>M\xed\xbdi\xa5F;\xd4\xeaK\xf3Kj\xf5uI\xc6P\xac\xfa\x8c_\xd6\xce\xb5\xd3\x03\xe7\x17\xfe\xbfk\x96\xe0\xb3\xf8\xe7\xf9\x1b\xfe\x0f\xd2\xbc\xce+\xfa\xff \xff?>\xd2S\x84\x8f\xf4\xffWX{\xb9\xc4\x8a\xe2\x9f\x17/\x9c\x99)\x90\xc6\xeb*\x92\xcc\xc5\xb5%\x0d4Y\x9e\x1c\xd6z\x93\xf5(X\xc6ho\xcf#B\xe8\xca\xa1h\xbd\xa3b[\xca\x02\x19\xab\xef\xef\xed\xed\xc8\x0f2\xf1\xc1\xae\xe1\x033\xc9\xde\xa1FvG\x8fw\x1f\xef?\x1c=\xde\xf3\xbcb\xf8\xdby\xb4`\xb0\x89\x82Bz\\\x8av\xb8\xf6\xafe\xda\x85\xf3\x98\xf9)\x8b)\xf3\xc2\xe0\xea\x85\xf83\xd1\x0d8\xd0wb\xa0\x8f\x8a;[\xf8%o\xbc\xd3SG\xc4p\xcc\x836\x0e\xf0\xfbm\xc5'{\xd0\xd5\x987S\xb0\x92\x9f\xaa\x9b\xa5\x85\xac\xc6\x9d\xc9crG2\"\xb6\x0c0\xfd\xa3\x9f^\xf4\xd7\xfe\x95\x8b\xf9\xc1E\xf1\xcd\x0d\x8c<\x19\xda\xfbC\xb09\x0e?\xfa\xab`Ami\xbf\xf58\xdc\xcbUt\xf9\x92}d+\xa4`\x83\xe4$\xe2kz\xee\xa6\xf9\x1bO\xfa\x1fie\xb2\x97\xf4z%\xe2m\x17\xaeU\x1bE]\xcd\xffkH\xdfU\xe0\xdcrw\xfe\xff\xfca\x919\x87\"\xfb \x19iP\xc6\xd5\xb8\xa40`J'C\xce\xff\xd1\x13\x8a\x88:\xa4\x8c\xe4\xf14\x10Z]q\x16\xd84C\x0f\xeeN\x87\xc8\x99,7]\x1d\x91A/\xff\xcc\xc0\xd5r\xd0\xc8\x94\xff\xb6\xd7\x03\x97\x12\xb8\x95B\x90\xf7eV!\xde\x0foOdt\x98\xf7u7\xcb\x1e\xf8\xd4\x99\x8f\nk\xfd\xd5\xd4\xe7\xe3\x0b\xa7\xd9\x0c\x0e\xcb\x91oA\x13p\x17\xe1\xd9\xd5@\x8c\x03\x0e\xb6\x98H\xf3H\x05;Q\x9c\xfe\xc0\xae)\xd5\x8c\xfaQ\x8c\xde\x1e\xb2\x7f\x06\x0b\x19=]\xfd\xba\xb9\x81G2\xf6y\x18\xfd\xc4\x96\xd4\x86x\xd4[\x08\xa3g\xd1z\xe3\xa7?\xf2\xe3Lu\xb4\x02\xbd\xe6<\xe2\xd0\x8d\xeeV\x97b)\xb5\x02\xbd\xe6\x1d\xe2\xc5\xcb\\Du\x9f<\xbf*\x86\x98\xc7\x9cWa\x1e\xa6\xbe\x98I\x9a\x97,2\xfe\x85\x9f2a\xa7@\xa5Y\xc2\x16\xdf\xeao\n\xc1\xfdL8\xe2\xc4x\x98\x10\xe8\xc5i\n\xe0\xb0\x14:\x96y\"w1)\xe6\xb6\x87\x04\xd7|l\x89f\xaa\xf4\x04\"8\x80\xe4\x89\x879\x1a\xd0j]\xa6\xe6\x17n|\x98\xf8?\xf2\xd0\xda\x87\xfcCD\n\x0b\xd1A\x82\xa9\xdd\nox\x97\x14\xc65Bc!z\x0eu!\xc4\xa9\xe0\x03C\x01\xd7\xddC\x08<>\xc4\xeea\xd9\x9dL\x80\xb0_\xbbD/\xebbo\x9bc\xebJty\x1f4\xce\xce\xd4\xf6\xb7U\x14-\x19\x0e\\\xb1\x15\x87>z\x9c\xd76\xf4okC;\xa3b`\xaa\xe1h\x1f\x99\xf7\xfda9\xf2\xd5\xe8\xf1\x1e\xff\xc5)\x94\xdcm\x82\x93$\xe2\xd7\xcd\x0d\xec=\xdc\xd9\xdd-~\xc7/\xe3\x1d\xfe\x8b\x92Q\xa8\xaa\xbc|\xbf\xd4\xf5p\xb8;\x1c\x0ek'\xf2\xc2:\x11\x9cb\xa9\x1fl\x99?\xbe\xcf\x1f\x9f\xe6\x8f\xaf\xf2\xc7\x0f\xf9\xe3\x8f\xf9\xe3e\xfe\xb8\xa8\x1d\xd6;\xeb\xb0\x1e\xfcz\x1a\xde\x07\x19\xc8D\xdfn\xf9\xc4\x0f\xd27\xd5X#\xbfs2\xa7X\xf4\x0b\xe7U\x8aE\xff\xe4\xb4M\xb1\xe8g\xc0\x88\xd2\xd5A\xfeP\x1fg\x9d\x8f#\xd2\xed\x9b:\x86\xe8'sK\xf9\nO:\x85\xfa\xa8\xbe}Kx\xa0R\xce)\xd5\x7f\x8b\xec\xa3\x85\x04%\xa5\x9d\xc4x<\x9do]\xba\x8c|,;\xcb\x1f\xdf\xe4\x8f\x97\xf9\xe3\xfb\xfc\xf1i\xfe\xf8*\x7f\xfc\x90?\xfe\x98?.\xf2\xc7\xeb\xfcq\x9d?n\xf2\xc7\xe3\xfc\xf1*\x7f<\xcf\x1f/\xf2\xc7\x8f\xf9\xe3\xf3\xfc\xf1713{V\x17C\x82\x07\x839\x8a\x97\xbf\xed\x10\x0bb\xf2\x06\x0e[\xff\x13a\x05c\xdd\xef\xd7\x9a\xcdS\xff\xe3m'@\x91\xdd\x9a'\x02\xe2\xe6\x8a\xa7\xa3\x861\x83\xca\xffB\xb3\x9c\xa3\xfa'\xe2'=\x81.\xe7\xf50\x9b=_\x07Q\x01&\xfcqL\xc9\xeb\xa0\x0b\xffp\xe7\xc4L\xa2\xd2\xa2\xb63{\x98K\xc8A1\xb2V\xfa\x83\x83g\xe65A\xfb\xcf\x8d\xd0~\x0f3\x934+\xf7\xe4\x9fb\xa4s\xaa\\p\xcaV\x1aI\xc8LK\x84\xd0\x111h\xfb\x80\x0e;\x9c]\xdb\xdf\x19\"\x11P\x8dO\x1a!WL\xdf\xec\xef\x8c\x06\x90\x07+\xdd\xd9\xdd\xe1\xcc6\n\xa6^\xbb\xc3\xc1\x08\xbd\x96\x19lS\xeb\x949f[|\xd6%\x1e\x8e/\x1b\xa7\xdd\xc6$\xf3z+\xcce\xbb\x87\xd0AJ\xe6\xdf\xfc\xe2\x99@:\x8df0\xa6[\xee\xb5\xd9\x1bM\xff\x93\xba\xd4\xba=\xf3(}\xa8\xb9!\x11\xfc\xc1\xbee\x05\x99n\xb0\xdeDI\x12\x9c\xad\x84\xb7\xfb\x18\x02!\xaa$\x0b\x10\x8a=\xe64\x11v\x7f\xb8\xf5\xfc\xfc\xd7\xf64Rp(\xe95)\x00\xc4\x90k\x06-@\\D&\x85XRF\xf9E\xc8\xcf\x1b%\xd46\x7f7\"|\xa4\xde\xf1Q8]\x07\xb7K\x1e\xcam\xbalNC\xa7v\x86\xdf[\x19a\xdb\x909l\xe4(u{\x88\xb9/\xa9\xf4\x85a,\x8a\xf8\x99\xb2\xf1/E6\xfe{G\x98\xa2_\xd0\xfe1\xf8\xf39\xdb\xa4 \xaa\xde\xf0\x06^QN0\\\x81{M7MqZ\xd3\xd5\x8cff\xbfy\xecW\x8ad\x87cc\x95\xda\x90\xd3\x06\x83,#\x9b\xdf\xa9\x97\x8f\xfeOA\xc6G\x87\xbe\xcc\xb3\x17\xf4\x07r\xc8a\x8f\x8er\xd8\x83\xce\x10C\xdf\xa8\x9f\x03Cj\xe0\x04\x14\x94P\x13\xe5$\xad\n\xf9\xe9,\xed\x01E\x85+r\xb9\xe5\x14\xa6\xbc\xf9y\x0fV=\xb4\xff\xa8\xbaIq\x00Ea\x87z\x85\xbe=\xf2MU\\\x86\x02;W\x93P\n\x8dX\xae$Q\xbbM\"@-al~\x13\x18\xda\xd1\x8a\x1aZ\xd4?.\xa0:\xa5\xee\\g Z\x12\xf8pF\xa9n([y\x9d\x05\"\x14D\xacDB,\n\xfa\xb6\xec \xf1`C\x0fE\xf6\x9c\xd5\x10\x1b\xceW&\xe2@\xedb\x1c$\xa1\xd6\x12\x91%\xc2)'p\x16\xd3h6\xeb \x1cCf\x80>\xe5`\xa7\xff\x08\xee\xf1t\xb58A\x02\xf8\xf1l\xf0\xa7\xdc\x9b\x823\x1e2\xeb\xbb\xac\xb3\x14[\x875\x8b\xc9\xcc'\"r\xd3\x84\x13\xaa\xe2\x11\x1c\xe5\xf1MS-\x1d{?\xf1\x97\xec\xdb\x92\xb5B\x8d\xe5\x1eM1\xee\xb3\xab\x94\x85\x0b\xb7z\x8e\xc8Fs\x0cYq\xb7\xf0\xc6/\x8d\xeeN>?\x02\x90\xc85V\xba\xd6\xf0\x83\xed\xbc\x7f\xcf\x92\x1f\xa3E\xb6\xaa\xc6.\xfd\xe8\xaf\xb2\xa2w\x1f:\x8a\xf5\xcfY\xfa,\n\x97\xc1\xf97\xd7\xefb\x0c\x86\xdb_D\x97\xe1*\xf2\x17T\x0e\x87\"\x1eB>\x80\xdc\xe9h4\x18j;h\xf8\xd4\xae\xf1*\xdb\x16\x18\x15\xbd\xa2\x92;\xe0C]\x86\xfd%K\xe7\x17^\xc5E+\x9f\x93qJmvU\xd51\x92-\xca\x97\xb8\x9fl\xd8\xfc)\xd6L\xccH2\xf7\xe7\x0dJ\xcb\xe1\xa6^?\xbd`\xe8\x07\x17\xe9\xe9F\xe5\x9f:E\x91y\x14\x80\x9aSM\xbe\x8c\xce\x88\xa8.\xed'\xa9\x9ff \x1c\x1d\xc2\xee\x00\xd3[\x04\xfdl\xb3\xf0S\xf62\xf2\x17Ax\xfe\x06\xdf\xbb\xce\x12\x1d\x17i@\x9c\xb3\xb8e\xb5w\xf1\xcaux\xc1<\n\x93h\xc5\xfa\xa8\x14se\xffo\xd9U\xaa\x91'Y\xbc\xe2@\x86\x17\x07R\x89\xcc\xe5[)\xdcQ\x7f\xf1\xd7+\xea\xc1s\xc3~\xca\xae\xca!\xb4\xa1\xaaF\xfb[\x9d\x1f\x1d\xf2\xcfY\xda\x12\xd2R^\xf78t\xcbw\x15L\x80\xc1\x18\xa6l\xf6\xf7\xc2\x12\xa5s\xaf\x08w~\xfa\xf7\x0c^\x84H\x91\xcb\x1b<\xef\x0b&\x10\x83)9\x93\xd4\xc7\x96\x83\x17\x16[F5\x9a;\xdc\x7fT\xea1\x11#\xd9-\xe2!j\x93\x02I\x92\x0b\x06\x07\xbcL\xbe\xf0\xdc\xa0\x07I\xff\xdd\xebo\x9f\xbe}\xfe\xfe\xd9\xab\x93\x17\xc7\xdf\xbd\xe9\xb5\xdc>\x0c\x0e\x8d\x80\xeccp\xd1\x7f\xbc\xf1\\\xd6\xdf\xf8\xd7\xfc\xa8\xeb(\xde3\xf7\xfa\xf6\xd5w\xdf\xbdl\xdb\xab\xbc9U\x07f\xb5/\x02UEt\xa2\x86\x9c\xf0\x97=\xe8\xc4\xc5\xd1\x05\xc2\xf3t\xe6}\xc5\xf7\xf9\xc1\x83\xff\x03\x14J\xe2G\n\xdb\xf4\xee\xa7\x97\x87\xc9\xa5\x7f~\xce\xe2\xed,\xd8\xe6xg\xe1\xaf\xa2\x90m\xa3N$\xed\xff\x96\xf4\xd7\xfe\xe6\xff\x07\x00\x00\xff\xffPK\x07\x08v\xf2\x8aA\x86\xba\x01\x00\xc5\x87\x08\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00 \x00swagger-ui.cssUT\x05\x00\x01\x80Cm8\xec\xfd{s\xdb8\xb27\x8e\xff\xff\xbc\n=\xbb\x95\x9a\x99\x1dS!EQ\x17\xabf\xeb\xc8\xb1\x93q6r\xc6\xcem\x92\xad\xad)\x8a\x84$\xda\xe0\xe5\x90\xd4\xcdz\xf6\xbd\xff\x8aw\\\x1a $;s\xf6\xf7\xad\xb3\xd9dl\xe2\xd3\x8dFw\x03h4\x00\xb2\x9bl\xed\xe5\x12\xc5\xda\xda;\xfc\x9fN\xe7\xe5\xdf\xfeo'\x08c\xdf\xc6\xde#\xea:I\xd2\xd9\x0c\xbbzW\xef\xfc\xbf\xce\xec\xfac\xe7\x9d\xe7\xa0 A\x9d\xff\xd7Yz\xe9j=\xef:\xa1\xff2@N\x88\xed\xe4%M\xf7\xb7\x97\x8b0H\xb5\x85\xed{x\x7f\x9e\xd8A\xa2%(\xf6\x16\x13'\xc4a|\xfeWs\xde7,\xe3\xdfD\xfd\x9dU\xea\xe3\x03\xf6\x02\xa4\xad\x90\xb7\\\xa5\xe7F\xd7\xb0&\x9a\x9fh)\xda\xa5Z\xe2=\"\xcdv\xef\xd7Izn\xe8\xfa\x8b\x89\xb6E\xf3\x07/\x85K)\xce\xf3\xd0\xdd\x1f|;^z\xc1\xb9N\x95\xd8q\xea9\x18\x9dQ\xcf\x12\xcf\xa5\x9f,\xc20E1\xf5h\x85l\x97y\x14\xd8\x1b\xea\xf7\x049\xa9\x17\x06\x07\xd7K\"l\xef\xcf\xe78t\x1e\xe8\x16\x1b\x87\\K\x99\xf0\xe7=\xe4OJ\x19\xbb\x83!\xf2;\xb4\xa4\x0bo\xe9\xd8Q\xc6\xf0\x8cy\xbc\x8eii}\xdb\x93UZPT\xea0\x90\xdf\xe9\xeb\xd1\x8e\x96+>T\xca\x9d\x87\xbbL\xe4\xdd2\x1f:\x16a\xec\xf3\xca\xfbg\xba\x8f\xd0/1JP\xfa\xaf3\xbe Y\xcf}\x8f)\x01*\xcbf\xb5\x92\xa2(\xfdW=\xb6\xdaQ\x84\xec\xd8\x0e\x1ct^\x14\x01\xd5\x974\xe7\xe7\x9a\x1f>j\x8b\xd0Y'\x9a\x17\x04\xcc\xd4C\x8a\xaa\x04-\x85o\xc1\x16\x95\xf3 \xde\xeb&\x91\xed\xba\xd9l\xa0K\xda\xd0\xb0\x89\xbd`)n@+\xae\x92^\x02,E\xa7\x11\x87p\x9df\xbevnD\xbbr\xec\xed\\\xe4\xc0\x8fh\x972\xb3$\xc2n\x82\xd2C\xd5\xb0\xaei!\xbf\xd3\x1d\xe6\xff\x0e\xb8a\x01\xa3%\n\\h\xda\xac\xe7\x14j\xd6$\x9e\x16\x83a5\xacW\xdd>\xb5\xe7\x18M|{\xa7m=7]\x15\x1d\xa5\xd6\xf2d\xbb\xf2R\xa4\xe5\x83\xf4y\x11y1Sl\xb8\x8cQ\x92\x80\x83\x8f\xd2(Xw\xe1\xbaw\xd9\xeb4\x04\xac\xeb\xac\x90\xf30\x0fwP\x1f\x89m\xd7\x0b\xffu\x92Vd\x0e\x15\xac\xfd9\x8a3\xef-\x19\xe7^\xa9%\x91\x17h@\x17\x14\x10\x85\xeb\x94&:\x94C\x90\xa0\xa1 \xb2cg\x05v\xdfLY\xb9\xc7LJ\x0f\xd3\xc2\xc5\"A\xe9\xb9\xd6cB+\x8aU#K\xf1@s2nX\xdc\x06\x11]\x13\\@\xd2q#[C\xbf\xf00\xd2\xd6\x11\x0em\xb7R\x82pt\xcaG\xed\xcaO\xe9X\x00\xa5\xb6\x87\x13:\nE\xc1Z\x12\x85&k\xdf\xb7\xe3}\x8d\xc0^\x92j^\xca\xf4*\xc7\x0e66\xec\xc4\xb4V\x8b \xed_\xcc$\xe4G\xd8N\x115\x93Rd]\x17\xcd\xd7\xcb\xce\xdf\xa8q! \xb1\xe7v\x96!v\x01\xac\x96\xf7;\x90\xe2\xaf\x8b\xc5\x02\xa2\x98c\xdby\x80)\xd8\xf8\xa7\xa4X\xc6\x9eK\x04Ndx\xdbY\xc7\xf8G\xd7N\xeds\xcf\xb7\x97\xe8e\x14,'Y\xf7\x1d\xf4\xcf\xbc\xcf\x17\xef\xef\xb6\xfa?\xde,\xc3\xe9t:\xbd\xf9\xf0iu\xf5i\x99\xfd\x98\xffs\xfdj\xfau:\x9d^^]\x0e\x07\xef\xb2\x07o~\xbf{\xfd\xe5\xd7\xbb\x8f\xf3\xde7\xdd\xed\xbd\xde\x7f\xbb\xbd\xb8\xf8\xf6f\xec}\xfbp\xf1v\xfe\xe5u\xf0\xed\xf3[\xfc\xf5\xcb\x9d\xe58\x18\xff\x96\x11\xecW\xd1\xe7\xd7+\xfd\xcb\x951{\xef\xdfl\xe6\x1f\xacU\x81\xb7\xfa\xf3\xdf\xa7\xc5\xff.\xb7/\xd1\xaf\x17\xab\xaf\xbd\x14\xbb\xaf.\xbco_\xdch~\xaf{\xc3\xe1\xfa\xe5\xb5w\x11}\xbb\xd4\xbd\xcf\x8f\x9fofW\xc6\xf6\xb6\xf79\xb4?\xad\x06\x8e\xff\xf9#z\xb0>}5\xa3\xf8\xeb#~\xb8\xbe\x1f\xfd|}\xb9\xeb\xbf\x0fV\xa9\xf3\xc6\xc0\xee\x9b\xab%zc$\xf3`6@\x97\xba\xf7\xf5\xcb\xdd\xe6\xab\xffi\x90\xfd>\xff\xf2Y\xff\xfaa\xe4]\xff\xba\x1c\xa07\xc6\xd6}\x93\x8c\xaf\x1f^?\xcc{o\xf1\xf5\xeb\xd5\xcd\xa7W\x17\x97s\xf3-\xbe\xbe\xfc\xb4\xbe\xf1\x8c\xfb\xd9\xc7\xab\xdd\xf5\xa5c\xbd\xbb\xbf2\xde_\xce\xf67\x1f\xb6\xcb\xd9\xfdtw\xf3a\xb4}\xffa\xb4\x9b\xbd\xd2\xb7\xb3\x8f\xe1nv\x19\xeeg\xaf\xa6\xcb\xeb\xea\xef}\x7f\xf9\xdb\xafo\x1f\xbe\xddG\x1f\xee\xae\xbe\xd6\xf28\xfe\x9d\xff\xdb\x87\xb7\xa1\xfb\xeb\xdd\xf6\xbd7\xda\xb8\xa6k\xbe\x0b\x9c\xc7w\xfex\xffm?\xda\xbd\xff\xf8`\xbd{\x9c\xee\xdf=^\xef\xdf\xfd\xfe\xf6\xe1\x9bg<\xa2/\x96\xfe\xf5\xf7e:\x0ff\xf7\x04\xdf\xabo\xbf\xdf\xdc;>\xde\xbao\xf0f\xee]\xec\xbf\xbd\xf9:\xf8\xfa\xe5\xed\xc6\xfd\xfdv|\xed]7:xcl?~\xd2\xc7\xd7\xfeJw\x7f\x9d\x0e\xde\xed\xc7kg_\xdb\xe2~\xde\xd37\xe8\xcd\xeb\xed\xbb\xc7\xab\xf5\xec\xd58\x9d\xe7\xfaY\xa5\xf37\xd6\xe3\xfb\xe0F\xff\xe4\x7f\xa6d\x9e\x07\xb3u\xa9\xd3\xf5\xd7\xde8}g\xaeV\xce\xab\xd1\xee\xdd\xfdt\xe3\x18w\x96\xf3\xe6\xd3\xe6\x93\xff\xf9qn~\xde\x7f\xed}\xfe\xf0\xed\xcb\xd7\xfbk\xef\xa2?\xff\xb2[;\x8fQf{EY\n9\x9c+\xe3\xe6\xfd\xc3\xdd\xe6\xab\xf99\xfd\xf6\xc5\xd2?|\xba\x1d_g\xb6~e=\xd8_n\x07\xb3\x8fw\x97\xef?~\xed\xdf\xe8\x9fz7\xfa\xe7\xd7\xb3\x8f\xaf_\xdf\xdc/{\xb3\xc7o\x97\xb7\xf7\x0f\xdb\x9b\x87\xdb\xfe\xec~\xb9\x9d]]\x13\xfc\xf0\xda1\xefVs\xff\x06\x13\xfc\"\x9a\xdf\xad\x1a\xbf\xcb\xe8\xd2\xf1?\xaf\xdc7\xe3\xfd\xe77\xe3\xcd\xfcR\xf7n\x0b\xfd,?\xbdYm\xdc7\xe3G\xfb\xcdx{}usy}y\xbd\x9d}\xfc\xb4\xfc\xc7\x95\xb1\xfa\xda\xc3\xeb\xbc\xec\xd5\x83\xf7\x9b7\x1d\x95v\x1a\xdc\xbd\xf9\xbc\xb7\x7f\xff\x86\xbf]}\xdb\xcf{\xfa\xd21\xef2\x1d\x0e\xec/\xd6\xa3\xfb\xe6\xf5\xfak\xef\xf3\xdb\xbbK\xdd\xcb\xf0\xef|\x1c}\xbb\x0c\xcd\x9b{g\x7f\xfbpk\xde\xdc\x7f5o\x1f?\xedf\x9f>\xf5n\xef\xdf\xbe\xba\xd5?\xedo.\xa7\xfd\xd9\xc7\xe9vv\x7fe\xce>\\\xd7\xfc\xbe\xbd\x19\xdf\xbb_\x0c<\x0f\xee\x08~w4\xbf\xc7V~\x9bL\xf6w&\xe0\x93\x99\xaf\xbe\x1a\xe7~\xf9\xe9\xe1\xeeM\x81+\xfa]\xde\x0f?\xf6\x97\xbf]\x8e\xfb\xce\x9b\xd7\xf7v\xef\xb3~\xfd\xe6\xf3:\xeb\xef\x8ew\xfd\xf2\xb7\xe4\xe2\xc3\xcfof\xd9\x08q\xff\xe1\xd3\xdd\xc5\xe7_\xef\xed\xaf\x9b\xc7\x97/\x1fG\x97\xef\x92\xcb\xfe\xd2y\xf3\xbb\xf7\xf5j\xfa\xe6\xe2\xfa\x1fo.\x02\xf4\xf2\xe5\xe2u\xb4\x9d.\xb7\xd3\x8b\xf1hj\xbf\xeeE\xf7\xf8\xd3mF~\xf1\xf6\xee\x93u\x15?\xbc].\x97\xbf\xfc\xf2S'F\x11\xb2\xd3\x8e\xde\x11\x8e\xa4\x9a1x\xc6\xc1\xf4\"\x1f\xe6n\x8b\xc1t\xba\x18\xbd\x1c\xaf\xfew0\xfd\xdf\xc1\xf4?u0}\x7f\xf9u\x7fw\xbf\xba\xba\xbb\xcc\x06\xd3\xaf\xfb\xd6\xc1\xafe0m\xf8\xdd\xaa\xf1\xfb\x0f\x1aLo?\xb6\x0e~G\x0d\xa6\xb7\xed\x83\xf3\xf7\x19L7\xaf>\xe8\xc6u6\x18\xcd\xea\xc1\xd4\xbf\xeb\xbf\xb4~\xbex\xfd\xdb\xc5b:{\xed\xbf\x9c],w\xa3\xbb\xe9\x9b/\xaf\x02c:\xf5?,\xcd\xfe\xed\xe0\xe1\xe2\xf2\x1f\xb37\xb3\xcbW\xdb\xebWhv\x8d\xfc\xd7/\xad[{{\xe5E\xd3/\xdbO\xab\xed\xd5\xfd\xecr3\x9f~\xc1_\x1e6\x9f/\xb6\xeb\xd1\xe6\xf6zz1\xbd\xda^\xbc\x8aV\xa3O\x03G\xcf\xc7\xa5+\xfc\xfa\xe3\xc3\x87\xf5\xad\xff\xea\x95\xd2\x00<\xd2\xf2x\x97\x1c\x85\xb3`\x99\x1d~\xef#T\x8f\xbf/\xc7\xf7/\xfb\xb7\xd3\xafw\xbf\xaf\xa2o\xcb\xe9\xf4\xc3\xa7\x87\xff.\x03\xd9\xe6\x7f\xbf\xbdL\xa6\x17\xaf\xaf\xdc/71\xba\xcdF\xe6\xdbj\xe0|\xd9\xbf\x9d\xed\xec_\xeft\xe72\xdc\xbc\xebY\x8f\xef\xfcb\x1c{\x97\x8f\xb5\xe3\xfe\xd7\xdf\xa7\x9b\xd9\x87\xfe\xf6\xddv:\xfa\xcd\\m\xbf~\xb9\x89\xbf\xfd~\xbb\xfc\xea\x7f\x0e\xec/\xfd\xf1\xf5\xfa\xe7\xe1f\x7f\xbd\xb4\xbf\xdc\x8e\xaf\xb1c|\xfcxq\xe3\\\xdd`\xfb\x0d\xbeF\xc1[\xfc\xc9\x8c\xde\x7f~s3\xb0{3\xeb\xdb\xab\xeb\x97\xb9\x8f^f\xfd\xf7\"\xfd\xf6\xfb\xdd\xaa\x19#\x96\xe3\xeb\xb2\xee\xf7\xbe\xf5\xf8\xde\xcf\xc7\xe0M\xd6\xe7\xf31\xf9\xd7\xbb\xf8\xb7\x0fo\xab\xb9\xe2\xeb\xc7\xcf\xd3\xe5mo\xbc\xff\xf6aj\xbc\xbb\xff\x9a~}\xbc\xda\xcd>L\xcd\xf7\x1f\xfa\xbb\x9b\x8f\xcb\xc7\xd9\xfd\xa7\xa4\xec'\x9b\xd9\xe5\xc3f\xf6q\x9a\xce.\xaf\x06\xb3\x8f\xd3\xc1\xec\x9e\x18c_]g\xe3~\xed_\x8d<\x99/\xea^\xad\x1b\xd35\xdd\xbde\xce\xf6\xd6\xc6\xf1\x9d\xcd\xec\xe3\x83\xf5\xfe\xc3h;\xf3F\xfb\x99gd\xf4\xa9cf}\xf1u\xff\xdd\x17\xeb\xf1z\xdf\xf0\xbd{\xf3\xf9\xf1\xab\xf96r~\xbd\x8b\xe6\xbd\xfe2\x1b\xbf\xdf\xfb\xaf\xbd\xb9\xf9Y\xff\xed\xc351Nf\xe3\x00Q\xa7\xcc\x1e\xfb\xff\xc0\xb1\xf9\xf7\xe9\xe0\xd6|\x8b\xbf\xfe~\xb7q\xf0\xddf\xde\xdb\x12\xf3\xe2E87\xef6No\xb5q^]\\\xde\xee\xa7\xfb\xd9\xe5\x95q\xfdju\xf3\xf5\xcbM4\x0f\xb2\xb2eT\xf0\xb9\xb8\xf9\xf81z;\x0fn\xf4\xaf_\xac\xfbo\x9f\xf0\xd5o\x1f\xdef\xfc\xd7\xf6\x17\xfc\xf0\xfe\xe1z7\xbb\xbf\xd6\xdf\x7ft\x1eo\xee\xddW\xb3\xc7\xab\xdd\xdd\xc7o\xaff\x0fo/\xef>^\xeb\xb3\xcb\xe5nv9\xdd\xcf>:;\x82\xdf\xd5\xbcwc\xcc\xbf|^\xbbW\x0d\xbfoo(~z+\xbf|\xee\xac\xe7\x13\xec\xf8\xb8\xf7\xed\xcb\xdd\x1b\xc7\x1f\xa7\xd7\xbf\x16\xba|\xef\x8b\xe7\x85\xdb\xfb\xab\xfd\xec\xfe\xd6\xbay\xbc\xea\xdd\xe8\xd7\x8f\xf9\xbc\xf0p\xbd\xbf}\xb8y=\xbb\xbf\xdd\xbe\xbf\xbc\xda\xce.\xafw7\x8fW^\xc3O\xde\xfa7\x97\xa3\xf0\x1f\x97\xe3_\x7f{\xfc\xf4\xb2\x8d\xa6\xfd\xef\xe2\xe5v:\xbd{5\x9d^O\xa7\xcb\xcb\xe9\x87\xeb\xe9tuu1\xdd]]\xbc\x1c\xddN\xbfd\xe3\xe6\xed\x14\xf8\xdf\xd7\x8b\xe9\xed\x15\xf0\xfc\xfa\xeajzu1\x9d\xce.\x98\x82\x8b\xe9\xe5\xd5\xab\xa9~u7\x9d^]^\xf0<\xef\xae?\xbe\xbe\xf8\xf4\xe5\xea\xc3\xf5\xe6\xa5=\x9dn/\xa7\xb7\xd3WW\xb7\xb3\xbb\xe9\xe5h\x1a\xbe\x0f>~6n?^\x0e\xdf\xbeMV\xbf\x99\x9b\x0f3\xf3\xb7\x97/\xbf)\xcd/\xc6@m\x829*\xbe\xcf\xe6\xd7W\xb7\x0f_\x96\xbd\xe9\xff\xc6\xf7\xff\x7f\x1d\xdf\xab\xce\x01t\x1c\x9e\x8d\xad\x8asV\xcfH\xc9y\xab\x8c!U\xe7\xad\xc7\xcf\xbf\xe2\xed\xb7\x0f\xe3\x0f\xdf~\xbf\xd9\xb8\xbf\xbf\xbd\xcf|\xe9\x9b7{\xb6\xf8Y%\xae\xbfy\xfcj\xce\x1e\xde^\x15I\x97\x99!\x1f\xbf\xdb\xd7\x1d\x0d\xbf\xaf\xad\xfc\x9e-\xbeoOn\x1c\x15\xdf\xdf]\xb6\xf2\xfbN\xf1=\x1a\xbc5\x1f\xb2\x11\xe2\x91M\x96\xe8\x9f.\x93\xd9vv\xff\xe1.\xfc\xfa\x9b\xf5\xe6\xbf\xfb\x1f~\xbb\x99\xdf\xdd\x7f\x9e]\xdd\x1a\x8bWw\x97\xcb\x9f\xbd\xe0\xe5\xe0\xe7\xb7\xc6\xf4\xed\xa7]\xb2\x9c^\xbd\x99NM\xe3b\xfav\xf6A\x7f\xf3\xb5\x18\xcf?|\xfa\xfc\xfe\xee\x1f\xd6\xab\xaf\xd7\xd7\x92\x04J\xb3\x15C\x1f\x8e\xa1\x7f\x03\x8e\xcf\xccCwO=\xe0N\"\xb8\xf4A\x04\xd7\xa3\xcf\xcd\xb8\x98\xfe\x95\xdeZ\xae6\xe6\xe8\x87\xfc\x01\x9dE\x18\xfb\xf4F\xacA\xff\xda\xa3\x7f5\xe9_\xfb\xf4\xaf\x16\xfd\xeb\x80\xfe\x95?\x0b\xb4J}\xba\x15\xf9Nu\xb1\x89\x83|\xdb\xc3\xff\x12\x95\x96\xdbT\xa2\xe2\xc8N\x92m\x18\xbbB@\x8a\xc4\xbcS\xb4K\x85\x85\xeb\x98!,\xb64\xe9G\x1e\xbd\xc7c{\xf4.UH7\x9a>'\x101\xe7\x94\xca\xf3Q\xd4\xb3|\xd7\x93~BKPmK\xd2\x0fW\xf4\xaf\xb4-\xd6\xf8\x94\x0dH\xba7\xd8I\x84\x9cT\xcb\xf7\xd8\x0e\xe2\xf3%b\"M3\x06\xbbq\xb5\x9b\\\x9d0\xb2\x06\xdd\x9e\xf5BF5\xde\x19\x03\x96\xca\x18\x0e\xbb\xc3\xa1\x94\xac\xbf3Y\xaa\xa1\xbc\"s\xd7\xe7\xea1\xcd\xaeiJ\xa9\x06<\xd5`\xd0\x1d\xb4\xc8\xc6\xb7\xc8\xd2\xa5$\xa3\x9d\xc5U\xd3\xeb\xca\x1bd\xedF\\5\x03y5C\xbe\x9a\xa1\xd1\xed\xf7Z\xea\x19r\xf5\xf4\xe5\xf5\x18;\x83#a\xcf,2$\xc5\xc9\xb5C\xedq\xf6< \xf1:E\x934\x8c\xce\xf5I\\zd\xc9M\x9f`\xb4\xc8~'\xce\x0eT\xe7k\xb2\x9f\x1f5/p\xd1.\xfb\xe5\xdf\xff\xe5#\xd7\xb3;\x89\x13#\x14t\xec\xc0\xed\xfc\xe8{Ay\xea\xc0\xd4\x91\xff\xd3A,W\x90<\xa17d\xd4'u\x08\x80P\xadO\x00\x84\xed\xdd\x02\xaaM\xa9g\x00\x84*\x9d\x03\xaa\xaf\xbd\x7f@\x95)t\x11\xa8\xb2\xf6^\x02\xe9Q\xa5\xa3@\xb5\xb5\xf7\x15\x88J\xa9\xbb\xe4\x84\xcf\xdfc\x14\xbaL\xf9\xb0>\xbd3h\xe9G\xfeS\xba\x91\x7fb/\xe2\xe8\x14;\x11G\xa7\xd0\x87\xf8\xba\xd4\xba\x10G\xa7\xd4\x83\xf8\xda\x14:\x10_\x95J\xff\xe1\xabR\xe8>\xbc\x06\x95z\x0f_\x97B\xe7\xe1\x89\xd4\xfa\x8e\xff\xe7w\x9d\xb6^\x82\x9f\xd2K\xf0\x89\xbd\x84\xa3S\xec%\x1c\x9dB/\xe1\xebR\xeb%\x1c\x9dR/\xe1kS\xe8%|U*\xbd\x84\xafJ\xa1\x97\xf0\x1aT\xea%|]\n\xbd\x84'R\xeb%\xf8\xbb\xf4\x12\xb2^\xcf_\x1e\xe8c\xa0\xb4XN\xb8A1y\xce>?W\x9d?\xfd\xbf\x9e\x1f\x85qj\x07)K\x12\xa4\xb6\x17\x00D\xf9s\x82\xac}\xa6;\xf0\xc2d\xd3\xee)\xf2\xc0t\xacH\n2)\xcc\xbe\x85\xa0\xfeirBd\xc7\x89)\x94\x08\x9f&\x11D\xc6IDQ\xce\x97\x9a\x83\x82\x94v\x9d\"\x19t\x1e\x84\xe5O\x13\xa2\xac\xf6sn\x90\x98/\xb54\x8c\x8e\xe6\x93\x86\x11\xc7'\xef4Gs\xe2;\xc5\xbc\xea\xc7G\xf3*\xc88nY\xe7=\x9a\xd7\xf1\x8b\xab\xda*L_P\xaaN`\x98SX ms\n3\x89yNa'\xb1\xd0)\xec\xda\x82\x12\xd5\x11\xa51\xdd\xf1N'\xb2\xdc\xf1\x9c\xc4\x86;\x9e\x97\xccn\xc7s\x93\x99\xedxnmV\x93\x1a\x08\x1f]\x9d\xc8@\xc7s\x12\x1b\xe8x^2\x03\x1d\xcfMf\xa0\xe3\xb91QL\xb7<\xfe\xce\x1f\x83\x07a\x1aqL\x1389O\x94\xc2\xe4zMt\xfc\x18\\\xf1\x08\x92\x13\x84\x05\xa9\x14\xe4%\xe9\xda|[uD\xaa\x98\xfb\xa7\xb4\x03 Ri\x86\xaf\xdc\n\x89\xc0\xf8\x14\x81\x01\"\x15\x811)0\xed\xfb6}\xcf-g9)\x1f\x95\xd18s\xbb\xa7;O+\x9alt\x00\xe8\xb2\xc7\"\xda\xfa^]1\x1e\x00\xd4E\x81\x88~N\xdf_\x86\x18\x94%\"\x0e\xb8\xe2\x90wz\x80>\x7f.\xa2\x0e\x80{\x81\x94\xba\x8e\xef\x8bs;\x9f\xd2\x8f7\x03Av\x8a%\x08\xf2S\x8dA\xb08\xdd\x1e\x04\x93\xd3L\xc2\xa9\x0f\xb2\x8a\x82Y\x14\x86\x9b\xb9\x9d\xcd\xe3'\x98\xca\x7f\x92\xa5\xfc'\x1b\xca\x7f\x06;\xf9O4\x93\xffT+\xc1\x06\xc1'\x19\x04?\xc9 \xf8\xc9\x06\xc1\xcf`\x90'\x0ee\xac\xe6@\x83\xd04Zq\xd5\xaf\xa2\x13\xbc\xe3 \xc3\x05\xc8\x8eA\xb0a\x18\x1c\xd8\xb5\xe3\x07m\x19\xdb{\x06k\x9a&\x87\xf5=\x17\x82Z\x96\xc5A\x01\xd8p8\xe4`\x89\x877\xcd\x85\xef\x128\x1e\x8f9 .\x8c\x0d\xc1m\xdb\xe6%\x0d\xc3\x00\x92\xc1q\x1c\x01k\x00\x8c\x10\x82u\x9b\xdf\xd2d\xc0\x8b~\xf6\x87\xc3\x83P\xf6&g\x85\xd3\xc6:\x0d]%\xd8\xfeQ?\xd3_\x9ce\xb1\xf8Yw\xfc\x93\x80p\xd4B8\x12\x11\x0e[\x08\x87\"\xc2A\x0b\xe1@Dh\xb5\x10Z\"\xc2~\x0ba_Dh\xb6\x10\x9a\"\xc2^\x0baODh\xb4\x10\x1a\"B\xdd\x92\x13\xeaB\xed\xe8\xbd6\xd2\x9e\x98\xd6h%6 \xea|\x8c\xe1\x9c6^\xces\xda3\x1dt\xd8\x82\x88uX\x92\x08p\xd6\x82\x88uV\x92\x08p\xd4\x82\x88uT\x92\x08p\xd2\x82\x88uR\x92H\xa8\x08\xd6AI\"\xc09\x0b\"\xd69I\"\xc01\x0b\"\xd61I\"\xc0)\x0b\"\xd6)I\"\xc0!\x0b\"\xd6!I\"\xc8\x19K*\xd6\x9f(2\xb1+\xf1\x8eH\x11\x82N\x98O`1r\xd9\xc1{\xa8\xf7u~\x9c\xe5\x81\x8bE\xdf0\x07\x82Y\x01\x82\x0f{\x16?\x89\x84\xb1\x1d,\xf9\x81~`\x02\xf3\xf32\xc4<\xd7\xf9\x10@\xee\x11\xc6\xe1\x96\xc6\xf2\xaf\x0e\xa8\xa5\x85\xe0\x7f]\xcc\x17\x86\xcdO\xa8\xd1:\x8e0+\xb0\x85z\x8e\xcdO\xe6\x05w\x90\xc2\xee\x0f\xccE\x0f6J\xe4\x05l\x04\xe2Z\xba>\xe2\xad\xb2\nS\x08\x9d\x99f\xce\xcf\xa9 r\xa4\x0b\xa7v\x10o\x9b.\x1f\x8e\x94\xc1\x10B\x01\x837\xcc\xe1\xd0\xe2\x9b B\xc7\xf6x\xc8\x0b]E\x19<\xc1\x18\xa1\xb9\xc3\xeb$\xb07l@\xa2\xeb\xc6\xbc\xcf\xb3\xce\xa5\x9e\xe35k\x1b]\xef\xf7\xc7|\x08\x03 Mk\x88\\\x91W\x01\xf8\xf1\xc0q\x80 &\xc7\xa3\x04$q\\\x04\x91l\xedd\x85\\\x88`1X,\x16\xbc\xf4%\x01\xa4H4Z\xb8\x0b\xde{K\n\xb8s,\x16\x0e\x9a\x8bH\xa0\xde\xef.\\\xbe\x15d:\x91\"\x10f\x88\xe6\x9aV\xbe\xea\x84&\x80\xde\x7f\xd2\x9d\xc7\xf5\xd0\x1d\xdb\xae\xb7N\xce\xd9\xa1\"6\x18@\xd7\xe8Y1b\xd3\xadq\x8f\x85\x81(\x93EA\xa0>\x032\x00\x8cf\xe8\xac\xe4@R9\xd6\"\x0fc\x067\x1e\x8f\xc7\xc0\xea\xaf\xdew+\xc0y\x92<[iUz!\xd7\x90\xc5:P\xa41\xad\xd8U,\xe0UV\x1bbU\x96\xb5q+\xf7\x16[\xe4\x82*\xe2y\x15\xdb\x81\xa2\x96\xc8\x05kO\xb6\x1cX\xe7\"\xd3Q\"\xff\xe21\"\x17\x03\x90\xb0\x97\x01@\xd0\xd1x\x9c\xc8\xd7\x00\xa4\xc8\xddx\xa8\xdc\xe3\x98\x8c\xdfS\x9c\x8eO\xdd=\xd9\xefT\xa4Sw=\x86\xdb1\xde\xa7\xe0~*\xb9\xbeX'\x12oB\x97d!B\x8f\xe4\x80\x02\x87\xe4p\xb0?\xb20\xa1;r@\xa17\xb2\xc8\x16g|\xb6\x01\x90\xcbN>\xdd\x15\xdbe;\xc2\x13\xfd\xef\xe3\x88\x02\x9fc'!\xc0\xe7X\x88\xd0\xe78\xa0\xc0\xe78\x1c\xecs,L\xe8s\x1cP\xe8s\xc7M\xb9,\xbc6oc \xa2\xa0<\x9e\x06\xfb\x1c\x9b\x80}\xba\xcf\xe1\xe7\xf49|\xb2\xcf\xd1\xfc4\xadx d\xc5\xaeH\xf5\x02/\xe5-\x82\xf8,\xe4d\xa0\xf93\x0eZ\xdeF&\x91\xc0&f\xb6\x84\x08\x03D\xe3\xf2w\xd4\xb5\x0f\xd1\x07\xb8!\xdcn\x8f\xb4-\xd8\x92a\xb5\xc8(\x1cDd\x17\x1e\x08\x9b\x86\xc7\x81\xd6\xe1`\xa0\x818\x14l#&\xee\x15\x9a\x89\xdb\xbe\x17Z\x8a\x0f\xf5\x85\xc6b\xf7\xe2\xebm\xc0v\x83\xa9\x0cl[\"\x1a\x15\x1a\xd1W\xb4!\x8b\x13\x98\x90\x85\xc1\x16\xf4U\x0c\xe8+\xd9\xcfW3\x9f\xafj=68\x16\x1b\xcf?\xc1v\x023\xe1V3aE3\xb18\x81\x99X\x18l&\xacb&\xacd&\xacf&\xacj&6\x9e\x14\x9b \xc3f\xa2\x80\xc9\xcav\xc3\xadf\xd0\xd7\xba\xf3\x87\xe7zG\xef\xf4\xa3]\xa7\x17\xed:\xf4\xa6\xcbD \x05\xd6\xd4\x13\xd54R\xaa F\x815\x99PM\xbd\x92\xbe\xbd]r$Xc_Vc&\xb9\xaeP\x1f\x84\x03k\xb3\xa0\xda\xfa\xa5\xc4m\xb5\xc9p\n\x83\xf0\x01t\xa2lT\xff\xd3\xfcHR\xd9\xf3\xbb\x92\xa0\xb2\xef\xebM-\x95\xb6\x99\xf8x\x87\x12T\xf8,>\xa5\xe0T\n3{\xedi\xfe\x9f\xe8h\xc2\xba\xbe\x83\x9f\x81u}g7\x93\xd6\xd9f\xf4\x13\xbc\x0c\xac\xefOp2\x99?\xe1?\xd1\x9f\x84u}\x07\x7f\x02\xeb\xfa\xce\xfe$\xad\xb3\xcd\xbe'\xf8\x13X\xdf\xf3\xf8\x13Ua\x14\xa3\xfa\x0b\x1e\xda.\xff\xb4E\xfdq.m_~\x08\xa8\xf9\\W\xe2\xc4!\xa6?%\xd2\xcdb@=\xff\xe6\x11\x13\xb0\x15Q\x9f~\x80S\x89E\xa4\xa7W\x9fRb\x8a\xf3\xf0N?\x14\xe9I\xbe>#\xaf\x8f\x0fa\x8b*\x8d\xb2J \xc4-j5\xaaZyD^\xb1QT\xcc\x97fu\xf7\xf2\xba\xf9\xc8\xb8\xa8\xbbW\xd6\x0dD\xceE\xdd\xbd\xaan\x1e\x91\xd7\xdd+\xea\xe6K\xb3\xba\xcb\x86k\xa2\x96\xd7M\x07\x10e\xfdM\xe3\x01L.A\xd5|\xa0<\x97\xa1P\x80&\xd2@\xad\x02\x00Q\xc9P+\x01\xc0\x142\x94j\x00\xca\xab{\xd4\x9a\xb6\xf00>HoS+\xcc\xd0\x07\xde\x99\xb3\x98\x01\xf0\xe7\xc2'\xb3B\xc8-Ko\xcf\x8a\xa5\x0e_\xa4 \x9f\xcf\x1d\xbb\xaa[\xe4\x99u\xf5B\xe7o$\x10\xfb?!\x84\xc0\xc9+9D^Z\xcb!\xec\x08\x8d\x1c\xe2\xbe@\xc8!r\xf8J\x10\x89\xcf75\xc9\xdc\x9e\xa8K\xec\xf9u\xb3\x84\xce_\xcb#\xf6\x7fB\x1eI\x17 \xe5\x11\xf6\x82F\x9e\xb6\x8eP;\xad\xb0/(t\x06\x85p\xb5\xe8!\xbe\xa4\x83\xf8\xd2\xfe\xe1\xb7t\x0f_\xda;|y\xe7\xf0\xdb\xfa\x86\xdf\xde5\xfc\xb6\x9e\xe1\xcb;\x86\xdf\xd6/\xfc\xf6n\xe1\xb7\xf6\n\xbf\xb5S\xf8*}\xc2W\xe8\x12~[\x8f\xf0[;\x84\xaf\xd2\x1f|\x85\xee\xe0\xab\xf6\x06\xffI\x9dA\xe8\xf7X\xe2\xf7X\xea\xf7\xb8\xc5\xef\xb1\xd4\xef\xb1\xdc\xefq\x9b\xdf\xe3v\xbf\xc7m~\x8f\xe5~\x8f\xdb\xfc\x1e\xb7\xfb=n\xf5{\xdc\xea\xf7X\xc5\xef\xb1\x82\xdf\xe36\xbf\xc7\xad~\x8fU\xfc\x1e+\xf8=V\xf5\xfb\xb6\x80\x88&v\x16\xe7\xf6\x82}5j\xf6t\x8e\x16a\x8c\x0e\xe5\xc7{\xcf\xff\xd2\xf9\x0b\xfd\xe5A\x98\xcd\xc1\xc1\xc8\x8e\xcf\xe7a\xbab\x01\x87\xbf=\x86\x99o1\xcfqI\x92I\xc7\x14U\xdc\xf2\x960esqMAYt\xd2N\xb9\x93O\xa3b\x91\x9aRP\xaa\xa6\x18\x12\xac)U\xd8 V\x9d\x8e\x9dl\xa8\x93\x08\xecK\xe5\xf5e\xe2\xfa\xea\xd2\xc2\x82\xc9\x8c[\x17\xc2\x82a\x99`\x98\x12\x8c*u\x03\xd9\xe7\xfc<\xe6S\x81L\xf1\\\xf2A\xc2\xae\xeb\xcd\xdb?4\xd8u\xbd\x94E\x01\xfd\xc5m@`\xa9C\x17k\x0eb\x17\xddn\xaa\xc5\xe1\x96\x81\xc5\xe1\x16Bi\xcb8\\G<\xb6x\xceQ8!^\xfb\x01+A\xfeP\x80\x05+ \x8b8:m\xe1\xed\x90{(\x90\xd8\xde\x87\xeb\xf4<\x7fD\xbc\xfeJ\xa1\x7f\x1c\x18\xdbg=Lf~\xb2\x1c\xf6\x00\x12\x01;\x01\xcfC\xe0\x07\x00\x1046\x89\x83\xbd\x81C\x08\x1d\x82GJ}\x02\x84K\xdd\x02\x10\xa5\xdd3DDR\xe7\xc8\xd73R\xffPp\x10\x85\x01\xd4\xcd\x06:\xa9\xd3\xf8m>\xe3\xb7\xb9\x0c\xcbA\xe41\x1c\x0ev\x18\xbf\xcd_|Uwa\x81ro\x01\xd0rg\xe1\xe4P\xf0\x15\x98F\xee*\xfe\x93<\x05v\n,w\n\xdc\xe6\x14\xb8\xcd)X\x0e\"\xa7\xe0p\xb0S\xe06\xa7\xc0\xaaN\xc1\x02\xe5N\x01\xa0\xe5N\xc1\xc9\xa1\xe0\x140\x8d\xdc)p\x9bSPt\x0b\x8cvu%D\xee\xbd\x0e{5?\xd12\x10\xf9,\xfb\x9dfS\x9a\x08\xe4V\x99\x99aJ\x90\x90E\xc4c^R\xcd^\xa7!\xb5E\x90==7&\x95\x94\xe7F\xc7\xe8\xe4\xd9|\xfa\xb7\xc6\xeb\xf5\xfc\xe7\xea\x85\xa9@\x15\xf9\xe1S\xae\n\xbd\xa9\"\x7f\xe7A\xfd\x13\xc0\xa1\x8c$H\x1ea\xece\xeb\x89\xea\x0b\xe3\x13\xb2\xcc\xf5\xe2\xe2\x95\xff\xe5\x17\xcb\xeb\x9a\x88\x92\x82\xe5\x04|\nH\x90\xc5H@\xf5\xab0\xf6\x1e\xc3 =A\x808\xdc\xb2\xb5s\xfd#/\xdf\xc6vt\xa8\x19d\xbf\x9dg\xffL\xe8_A\xbd\x03\xa4\xc5\xc3 \xfb@P\xaf\x16\xa3\x0d\x8a\x13\x04\xd4_\x15M\xe0\xc7B+6,\x8f\xb6fU\xa3\xd0\x9c\xb4L\xa2R\xd8\xbc2\xb9Z\xcd,\x91\x8c`\x0d\xd8\x1b\x96\xc9K\x91\x9fhIj\xc7)%N\xf1\x19\xfd\xfcyS\x15\xf90\xff9\xff\xbcy\x92\x8f)\x05\x0f\x889\n\\\x805\n\\\x96q\xf6\x88c\x8b\x02\x17bZ\xbe\xe8\x93\xe7[\x14\xb0\xac\xcb\xa7$\xf7\xe2\x11\xc4{n'(\x1b\xc8\x00\xeeU\x11\xcb\xbf~N\xd6P=\x845\x1e\xa3\xd4Y\x81:\xcfKx\xad\x17\x8f\xc9\n\xcag4\xff\x04\xe1Ee\xd0\x8aE\x06\x07\xac\x97A\x85\xc6\xcb\xf9\xe4\xb6\x03\xb84\xa6jxp\x96\xca9T\x86\x02\x98PF\xc9\xf9@6\xc9\xb94&\x01\xf80\xca\xcf9\xc1\xba/uS\xaa\x1e\xd4\x0e\xa9\xe5\x9c\x13\xa8\xe4\xfbu\x92z\x8b=\xd0q\"\xdby`\xfb\x0d\xf1\xac\"\xac\xb2T\"\xedW8\xb6\xf3\xe4\xac\xa8\xbeS?\x01YsF\xa9Q|\x07\xca9\xb1\xfd\x87|\xc8\xd6\x00\x99\xab\xc2\xccQ\xbaE(\xe0+(\x01L\x0d\xd5S\xb6\x8a$\xb2\x1dT1\x83k\xb2\xf3\xd74\x1eh~\xae\x97\xa4\xb17_\xa7H\xc0\xb2\xa0\xa29\x96\x08\xb6\xf7\xe4A\x0da\xc3\xc29\xda,X1\xa3\xbaP\xc3\xaa\xe9Ar{Ul\xd8~\xd4p\xa2\xba\x91\xcc4\x15\xab\xda4<\xaf\xca\x0c43\x89\x11*\x9e\xac\x11\x1a\x96\x84% \xaer;0=\x95\xb4\x04\xd9Qk\x96P_-\x0e\xdf\xea\xccl\xebz\x81\x8d\x8bh\x9c\x88A\xb5\x1c|\xaeO\xca\xffB\x9c\x0c \xa7\x1e\xcb\xc9(9\x19\x10\xa7\x9e\x84\x93\xc9r\xea\x95\x9cz\x10'S\xc2\xa9\xcfr2KN&\xc4\xa9/\xe1d\xb1\x9c\xfa%\xa7>\xc4\xc9\x92p\x1a\xb0\x9c\xac\x92\x93\x05q\x1aH8\x0dYN\x83\x92\xd3\x00\xe24\x94p\x1a\xb1\x9c\x86%\xa7!\xc4i$\xe14f9\x8dJN#\x88\x13\xb6\x93T\xe6\x9cz\xf6?\x96\xe38\xfb\xdf\x84\xf8\x19\x085\x97Y\xd4\xa7\xcb\xd6C\xe5\xbbm7\xe8\\\x9f\xd4$\xe0\xca*\xe7e\xc8\x96o\x0d/\x83\xe0e\x00\xbc\x92U\xec\x05\x0f\x99d\x15i\x80\x966)F\x81\x00\x05)\x89\x0d\x80\xd8\xa0\x88\x0d\x85\\\xdb\x81\xe7O\xe4\xfd\x88\xc6\x9e\xbe\xa4\x86\x18>\xf7\xaaZc\x0e\x0c/\xbe\xcb\xc2\x1a\xac\xe5\xf8\xb55\xcbFmA\xf6\x9c\xcbk\x81\x04\xadK\xafgZa\xe7\xd5W<\x8e^d\xf3\xd4\xa7\xad\xb3a)\x9e\xba\xd4>\xcd\xb8\x7f\xcaj\xfbT\xab\x7f\xbf\x057+\xd1\xf3\xae\xb9a\xee\xcf\xb2\xec\x86Y?\xe3\xca\x1b\xae\xe0\xb9\x17\xdf\"\xfd?\xd7\xfa\x9b\xeabOY\x82\x8b\x18\x1d\xbb\n\x17\xf19a!.bu\xdaZ\\\xac\xa9\x13\x96\xe3\xacY\x9f\x7fE\x0e\xd6\xf0|\x8br\x90\xfd3\xaf\xcb\xc1:\xbe\xd3\xd2\x9c\xb2\xee3\xad\xce)\x9eO^\xa0\x0b\xb8\x9d\xb6F\x170;u\x99.`\xf7\xc4\x95\xba\x80\xeb\xd3\x17\xebB\xc3\x1c\xbb^\xe7\xe7\xeb',\xd9\xe5\xcc\x8e\\\xb5\xcb\x99\x1d\xb9p\x973;r\xed.gv\xe4\xf2]\xce\xec\xc8\x15\xbc\x9c\xd9\x91\x8bx9\xb3#\xd7\xf1rf\xc7/\xe5[\xfc\xf6\x89\xaby\x96\xfb\xe2i\x0bz\x90\xddS\xd6\xf4T\xf7?aY\x0f\xd3\xb3+{\x85\xa5\xbd\xc21\x9a\x9c\xa7\xff\xcc\xcb}\x9e\xdf\xb3\xaf\xf6\xfd?c\xb1\x0fTr\xc2Z\xdf?a5\xf8\xacK}P\x80\xd65\xdfs\xad\xf4\xfd\xa7,\xf4Y\xe2\x13\xd7\xf9\x90\x0cO^\xe6\x9fb\xd7?g\x95\x7f\x9a\xc1\xbf\xe3\"\xdf\xff\x9ek|\x88\xf9\xf3,\xf1!\xce\xcf\xb9\xc2\x87\xf8?\xfb\x02\x1f\xd6\xfd\xb3\xad\xef\xfdgZ\xde\xc3|\x8e^\xdd\xc3lNY\xdc\xc3\x9cN\\\xdb\x8b\xb4t\xca\xd2\xde\xff\xde+{\xa0\x82g\\\xd8\x03\xdc\x9f{]\x0fT\xf1\xbd\x96\xf5\xfe\xf3\xaf\xea\xfd\xe7\\\xd4\x83\xccN\\\xd3\x83\xbcN^\xd2\x83\xdc\x9e\xba\xa2\x07\x99>\xc3\x82^`\x93\xa3\xd7\xf3\xec\xcc\xfc\x94\xe5\xbc\x8c\xd7\xb1\xaby\x19\xafc\x17\xf32^\xc7\xae\xe5e\xbc\x8e]\xca\xcbx\x1d\xbb\x92\x97\xf1:v!/\xe3u\xec:^\xc6\xeb\x84e\xbc\xd4]\x9f\xba\x8a\x97\xae\xae\x8e^\xc4K\x17\x84'\xac\xe1\xfd\xa7-\xe1!\xf2\xe3V\xf0\xa2\xc5:~\xe6\xc5:\xcf\xef\xd9\x17\xeb\xf8\xcfX\xac\x03\x95\x9c\xb0X\xc7',\xea\x9eu\xb1\x0e\n\xd0\xbav{\xae\xc5:~\xcab\x9d%>q\xb1\x0e\xc9\xf0\xe4\xc5\xfa)v\xfds\x16\xeb\xa7\x19\xfc;.\xd6\xf1\xf7\\\xacC\xcc\x9fg\xb1\x0eq~\xce\xc5:\xc4\xff\xd9\x17\xeb\xb0\xee\x9fm\xb1\x8e\x9fi\xb1\x0e\xf39z\xb1\x0e\xb39e\xb1\x0es:q\xb1.\xd2\xd2)\x8bu\xfc\xbd\x17\xeb@\x05\xcf\xb8X\x07\xb8?\xf7b\x1d\xa8\xe2{-\xd6\xf1\xf3/\xd6\xf1s.\xd6Af'.\xd6A^'/\xd6AnO]\xac\x83L\x9fa\xb1.\xb0\xc9\xd1\x8buvf~\xcab]\xc6\xeb\xd8\xc5\xba\x8c\xd7\xb1\x8bu\x19\xafc\x17\xeb2^\xc7.\xd6e\xbc\x8e]\xac\xcbx\x1d\xbbX\x97\xf1:v\xb1.\xe3u\xc2b]\xea\xaeO]\xacKWWG/\xd6\xa5\x0b\xc2\x13\x16\xeb\xf8i\x8bu\x88\x9c[\xac3\xf4\x87\x05\x0e\xed4\x7fG\xce\xe4\x0fz-\xcc@\xe3\x12\x9a\xbf1\xa7\x05\x1b\x94\xd8\x93\xde\x82\xb4\xc8\xdf\x82\xa4.W\x83V\x12\xad\x81+\xbcYH\xfd\xfc\x81\xe6\x1f#\xb2\x7f\x94\xc4\xbe\xba\xc0\xb0l\xc7\x98\xb9\x06\xab\xc9\x86)\xd9\xa8\xd2\xc4\x0e\x12-A\xb1\xb78,\xc2 \xd5\x16\xb6\xef\xe1\xfd\xb9fG\x11FZ\xb2OR\xe4\x9f]`/x\x98\xd9\xce\x87\xfc\xd7\xd7a\x90\x9e\xd9\x1b\x14xq'@\xbb\xea\xe7\xb3\x15\xc2\x1b\x94-r\x9b\x9f:\x01Z\xa3\xb3\xf5|\x1d\xa4\xeb\xb38\x9c\x87ix\x16d\xff$h\x19\xa2\xce\xda;\xb3c\xcf\xc6g\x8d\x14\x8ct\x9c`K\x14\xc6K\xcf>\x83\xc0\xb9t\x9a\xa0E\xc2*J*\x9e\x80\xc7:\xa1\x8b\xa8\xf7\xa0e\x0f(\xa2Wa\x90\x84\xd8N\xce\xfc0\xb0\x9d0\xfbO\x98G\x13,\xa3u\xec\xa1\x98!\xcd\x9fun2\x95\x96\x00\x11}\xad`\x8a\x03\xa3\xf6\xc6\x1e\xa2\xb6\x17\x86\xa3x\x00v\x15R\xa7+\x84\xed\x84&/\x9e\x9dI\xccT\x16\xa9Z5\xf5|D\xd7\x91?\x81\xa0\xf3\xd0\x0d\x03\x8f\xc2^\xe4\x8f:\xb3\x8f\x10\xde\xb1\xb1\x97\xa4!m\x85\xe2\x99\x80bi\xc7\xb6\x1f\x06.-|\xf9\x10\x14\xc9N\x1eP\xbc\xf10\xa6\xfd\x84x\x0e\x91\x95\x8d(>\xa1\xe5\xa56\xf6\x98\x0f_/\x12\xad\xc8\xc3\x91\xc0\xe2\x89\xc2`I\x8f=\xf9;\xafT\xebc\xb0e\x95\nu*\x0c\xd0^6\x88\xaa\xca\xe1\x1f-\x06X#V\xaf\x11\xd25\x8d%M\xb2-r\xc8}\xee\x93\xefT1\xf7E\xf8\xc5\xd6\xa0\x00\x06\x0f\xe8Q\x80\x1e\x0f0)\x00\xf7y\xfa\xc5\xb6/\x17q\xb1\xb5(\x80\xc5\x03\x06\x14`\xc0\x03\x86m\xcd\x1cQ\x80\x11\x0f\x18S\x80\xb1~\xfc\x9b\xba\x19\x8f\x15Z\x84E@Fa1\x90]X\x0cd\x1a\x16\x03Y\xa7U\xe2E\xf1\xb9\xb36\x1b\xb1\x18\xc8L\nm\x1f\xb1\x18\xc8X,&\xb3\x97\x82\xc1\x14F\x05\xba\xbf\x8b\x8d\xe8\xb7\xb5\xc3` \xa0 \xfdv\x0b\xfa\xed\x06l\x11v\x91\x7f\xed\xac\xd5|~\xbb\xf5Z\x1b=b \xa0\xed\xfc#M'\xb6R\xdb\xe0\xc7\x00@+\xe1v+\xe1v+\xe1v+\xb5\x08\xbb\xc8?v\xd6j%\xdcn\xa5\xd6F\x8f\x18\x08h%\xcc[\x89\xc2xA\xb4N\xb5\x18%\xa8\xb9\xdfnG\x11\xb2c;p\x8a/qN4?|d\x1f2&Z\xa7i\x18\x14l\xce\xcfs\xfc\"t\xd6\x89\xe6\x05\x01\xfb\x16`\xa2F\x1eZ~\x86\xed\\\x9fD\xb6\xebz\xc1\x92]\x18\xaf\x8cC\xb9\xd1\xca\xbf>y\xd5\xab\xca\xf8\xd7\x19\xaf\xcc\xaa\xac\xcf\x97\xf5\xab\xb2\x11_f\xd5\xf5\x0d\xf8B\xadW\x17\xf7\xac\x17l\xa1\xa5W\x85\x16\xfb\xa9\xe5\x956\xac)\x87<\xa5\xa1\xd7\xa4\xfcg\x9a\xf3\xcd\xe6\x1cBl;\xf3\xb0\x0d-\xddf\xc5\x15\x93\xf2\x01\xc5\xa4\x84@1-#\x0b\xc8D\xdb@R\xb2\xc0U\xf1\xce\xb9\x12\x90\xfd\xcc\x96{\xc1\n\xc5^ZA\xca_\x15\xe6\x89\x03\xe39\xd9t#q\x1e\xa2\x18\xf2\x1f\xa2\x18r!\xa2\x18\xf2\"\xb2n\xd8\x91\xc8\xea!_\"\xcaAw\"\xcaa\x8f\"E\x10;U\x86j\xf7+JX\xd0\xb5(qA\xef\xa2\x04\x86\x1d\x8c\x16Y\xecc\xbc\xd0\xb0\x9b\x11\xfc$\x9eF\xa0*gS\xf06\x85\xa8d\x95E\x132\x0f\xf4\xa5\x0e\xe8K\xfd\xcf\x97\xba\x9f\xdf\xe6}\xbe\xdc\xf9|\xb9\xef\xf9-\xae\xe7\xabx\x9e\xaf\xe2x~\x9b\xdf\xf9mn\xe7\xb7z\x9d\xaf\xe6t\xac\xbc\x02\x9f\xf3U\\\xce?\xce\xe3`\xe7\xc2R\xe7\xc2R\xe7\xc2R\xe7\xc2R\xe7\xc2m\xce\x85\xe5\xce\x85\xe5\xce\x85[\x9c\x0b\xab8\x17Vq.\xdc\xe6\\\xb8\xcd\xb9p\xabsa5\xe7b\xe5\x158\x17Vq.\xcc9\x17\x05Lc\xdby@\xee\x01\xa34E\xb1\x96D\xb6\x93E^]\x83\xfb>E\x01\xd4\xd2\x8c\x19\x0b\xd7\xba\xba%\"\xf0\xd1\xd2\xe6\xd8\xf72x\xfb\xb8z\x009\xe6\xdf/:F\\\x80\xa2Mb\xa8\x92\\h\x05\xa9\x15f\x83\xba\xaac[\xc2\x11\xb46\x84\xafB\xa1\x1d\x12\x91\xf1\xb1\"s\x04\xad\"\xf3U\x14\"S\x14x\xa5%!\xf6\xdcC\xbe\x8f^u\x16\x0e\x93z)F4\xa6\xdb\xb38\x98\x13F{\x06e)\x98\xfa\x00\x8a\x94;O\xbbT\x1cL$\x18\x0f\xb4\x9e\xc9\x0fk\x89}%\x81}EyY\\\x9b\xb82\xc9\xb0\x92dXQ2\x16g\xb1^\xe5\x05\x0f\x87\x14\xedR\xcdEN\x18\xdb\xe5 Vv\xd1\x9b\xc1\xce\xb8'\xe7\xb6\x93z\x1b\x04\x14\xe4\xcb\\\xe0\xf9*\xdc\xb0k\xe4\xfc\xb9\x80\xff\xc6K\xbc\x145o\x1cMc;H\xbc\xea\\g\x18w\xba\x86\x95t\x90\x9d \xcd\x0b&\xd2R\xbe=\x85\x90\x87p\x9df*:7\xa2]\xc7\x0d\xd3\x14\xb9\x1dg\x1d\xc7(H_eLX\xba$=d\xff\x14Yn-\xddGP\x8e\xc0\xdf\x16\xab\xc1\xda\x15\x81\xd9zk\x90\xe5\\,\xe1o{D9\x1f\xc6\xf8[\x93(\xe7\x03\x19\x7f\xdb'\xca\xf9P\xc6\xdfZd\xfd|0\xe3o\x07\x04\xc0\x84$\x18\x92\x12@U\x8c\x08\xc0\x00\x92qL\x00\xc6\x90\x0c\xc5+\xd4\x1b\xd0I\x9b\xf1\x859\xf2\x85\x93\xdc\"\x0c\x042\n\x0d\x01\xedBC@\xd3\xd0\x10\xd0:\x8c,\xa0\x81h\x0cl#F\x1a\xd0L4\x06\xb6\x14\x8d\x11\x1b\x8b\xc6)\xec\xf6\xab\x8e\xdd\xa5\x15\xfdV#\xfa\xad6\xf4[M\xe8\xb7Z\xd0o5\xa0\xdfn?\xbf\xdd|~\xbb\xf5\xfcv\xe3\xf9j\xb6\xf3\x8f3\x9d\xd8J\xb8\xd5J\xb8\xd5J\xb8\xd5J\xb8\xd5J\xb8\xd5J\xb8\xddJ\xb8\xddJ\xb8\xddJ\xb8\xddJX\xcdJ\x98\xb3\x12\x05\xdb\x1a\x07\x91Z\xb7\xbd\x83H\x9f[\xf3 R\xe4\xb6\x7f\x10ipk\x1d\x84\xaa\xcb<\xa1*e=`\xab\xf5\xaa\xb2\x1ePVq\xe5\xd6\xd0[\xcd\xac\xe8L\x9e\xce\xac\xda`\x9a|Y\xd5\x08\xb3\xcf\x95\xf5+\x9e}\x9e\xa7U\x95q\x0b\xf6\xad6\xa8\xca\x06|\xd9\xb0*\x1b\x02eU\xfb\xb8U\xfeV\x1bUt#\x9en\\\x95\x8d\xf9\xb2,\xe0\x10\xf5\xb7\xad\x96\xae\xbc\xd8\xad\x95\xd35\xb3\xff\xf1\xa0mX\x00\x93\xaaY\x83\xee`0\x18\x0c9d\x9e\xc7.0\xf9b\xbc}\x80?0.\x9aM\x13b/mJ!GmJ!_mJ!w%\xea\x85=\x96\x00@NKH\x06\xf9-Q\x0c\xb9nS\x0cz/Q\x0c90Q\x0c\xf90\xa1\x16\xc8\x8d\x9bb\xd0\x93\x9bb\xd0\x99\x9bb\xd0\x9f\x89b\xc8\xa5 \x9b@^\xdd\x14\xc3\x8eM\xdaD\xe0\xdb\xa4\xeaZ\xdd\x9bh\xab\xcc\xc3\x1bX\xee\xe4\n^\xae\x10\xc6\xe4\x01\x8a\xc4\xf3}\x99\xe3\xfb2\xbf\xf7en\xef\xb7x\xbd/uz_\xea\xf3\xbe\xd4\xe5}\xa9\xc7\xfbR\x87\xf7\xa5\xfe\xeeK\xdd\xdd\x97z\xbb/uv_\xea\xeb\xbe\xd4\xd5}\xa9\xa7\xfbrG\xf7[\xfd\xdc?\xc2\xcd}%/\xf7\xd5\x9d\x1c\xf6g,\xf3g,\xf3g,\xf3g,\xf3g\xdc\xe2\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xee\xcf\xb8\xd5\x9f\xf1\x11\xfe\x8c\x95\xfc\x19S\xfeL!\xc2\x0d\x8a\x178\xdcj\x1b/\xf1\xe6\x18\x1d\xaa\x07\xe7\xe5\x03\x01|\xe5\xb9.\n\x1at\xf1\xbb\x00\x9c8q\x88q\x03.~\x17\x80\xf3H\xaa\x86\xf2;\x1b5p\xc7\xc9\xac\xedZ\xa4\xde\xb1rk;\xb9\xe4;Vvm'\x97~G\xcb\xaf\xedd-\xd8\xf3-\xd8\xb7\xb4`\xcf\xb5`/o\xc1\x9ek\xc1^\xde\x82=\xd3\x82\xfdi\x01-\xebXY\xe8p\x94oQ\x04\n\xeeE\xe1[=\x8cB\xab8\x19I\xa0\xecg\x0c\x91\x92\xab14\n\xde\xc6P\xa88\x1cE\xa2\xeas\x0c\x91\x92\xdb14\n\x9e\xc7P(\xcc\xc1\xaa\x81&\xe7\x92\xfe\x91\x1e\xe9\x1f\xe7\x90\xfe1\xfe\xe8\x1f\xe9\x8e\xfe \xde\xe8\x1f\xef\x8c\xfe\xb1\xbe\xe8\x1f\xed\x8a\xfe \x9e\xe8\x1f\xef\x88\xfe\xb1~\xe8\x1f\xe9\x86*\x1e\x87\x8f\xf48|\x9c\xc7\x1d3\xc7\x92`%\x8f\xc3'x\x1c>\xde\xe3\x8e\x9dki\x02%\x8f\xc3'x\x1c>\xde\xe3\x8e\x9dsi\x02 XKR;\xf5\x9cCq\x055\xcc\xdf\x8d\x91\xb2\xb7Ob\x84\xf3;\xa2\x0d\xaazB\xe3\xecy\x12\xe2uJ\xe0\xaa'4\xae\xf8\xa8~\x0d\xca\x7fU\x18\x8e\x0f\x80\xe0\xd9\xc8\xae$;\x05\x94\x8bOA%-\xa0pE#\x14Z\xa10\xa9\x94M\xf3\x15[\xe6+7\xccWk\x97\x7f\\\xb3\xc4-\xc0\x8a-\xc0\xca-\xc0j-\xc0\\\x0b\xe8N\x92'r\xc3\xc8v\xbct\xcf\xbdu@\x1b7e\xdd1[8\"\n\xd9\xbb\xe9\xda\x90(d/\xc1k\x03\xa2\x90\xbdm\xafYD!{\xad_\xeb\x13\x85\xec\xfb\x034\x93(d_T\xa0\xf5\x88B\xf6\x8d\x08\x9aA\x14rJ\xd0\xad\xa6P\xe7$\xd2{d1{0\"\xd4\x1a\xce\xccy\xfb8L\xed\x14i}\x8b>o\xb0\x08c\xff\xbc(\xfb\xb1o\xb9h\xf9\xd3D\xf0\x1cd7\xd6\xc5\xec\xc6:\xcc\xaex\x0e\xb23L\x89x\x86)\x90\xaf,\x809\x8e$\x12\x1a#\x81\x88e\x01\xc8\xb1\xd7\x93\xc8\xd8\xeb d,\x0b`\x8eC\x89\x8c\xbd\xa1@\xc6\xb2\x00\xe4h\x1a\x12\x19MC cY\xa00\x96\x1e`\xd7\xd2\x88\x0f\x1c<\x8fwI9\x9e\xe6`R\x96\xa7\xfa\x98\x9c\xe9\x89n&ez\xaa\xa7\xc9\x99\x9e\xe8lR\xa6\xad\xfe\xa6\xe0p\n\x93w\xe3\x85\xfes;\xa1\x84\xe1\x89>(\xe1x\xb2\x0b\xcax\x9e\xea\x81\x12\x9e';\xa0\x8c\xe7\xa9\xfe'\xe1\xf9D\xf7\x93z\x1a~nO\x930<\xd1\xd3$\x1cO\xf64\x19\xcfS=M\xc2\xf3dO\x93\xf1<\xd5\xd3$<\xdb=\x8db:\xc7\xb6\xf3\x90EP\xf9y\xce\xf3x9\xb7\x7f\xd4\xcf\xb2?\xdd\xf1O\x10t\x04AG t\x08A\x87 t\x00A\x07 \xd4\x82\xa0\x16\x08\xedC\xd0>\x085!\xa8 B{\x10\xb4\x07B\x0d\x08j\x80P\xdd\x02\xa0:\xdb\xae\xed\xca+\x02\xde\x02\xbbJp\x8e}qf\xe8\xfa\x0b\xded\x05|$\x82\xb3f+\xe0C\x11\x9c5]\x01\x1f\x88\xe0\xac\xf9\n\xb8%\x82\xc3M\xed\x8b\xe0\xac\x19\x0b\xb8)\x82\xb3\xa6,\xe0=\x11\x9c5g\x017Dp\xd0\xa4%\xf6\xaf:{\x93:@v\xacQ\x10\xc3`V`\xae\x1d?h\xcb\xd8\xdeW\x08\xd3dVw\xbe\xe7R\x00\xcbb\x96ad\xe1p\xc8\xacG\x13\x0foP\\\x15s\xefB\xc3\xf95\x0b\x1ad\xdb6#A\x18\x06\x94\x08\x8e\xe3@lH\x08B\x08\xd0E\xae\xdd\n\xb2\xe8g\x7f\x00\xf5\xd7\x80\xc5\x02PV\x8c\xdc\xba\x92\xa1\xde\xd7\x19\x0cQ\xbcX\xf4\x0ds\x00IJ\x81\x86=\x8biN\x18\xdb\xc1\x92\x10c\xc0]\xe9_\x86\x98\xe00\xe7\xae\xd9\xef\x11\xc6\xe1\xb6Dd`H\n\n\xf4\xd7\xc5|a\xd8\x8cy\xa2u\x1c\xe1Z\x10\x0b\xf5\x1c\x9b\xbd\x9c\x90s\xa2qv\x7f`.z\x80\xea\"/\xa8=\xd1\xb5t}\xc4\xe8n\x15\xa6\x14&S\xe0\x9c\xb1\x10]>\xd2aW\xa0Q\xb6\xe9\x0eA\xb7G(\xa8{\x869\x1cZ=\xd6\xb3I\xc0\xd8\x1e\x0f\xfb\xb0\xdf\x11\xb01Bs\x87iW`o\xf6M'5\xe6\xfd> \xcd\x1c\xafQ\x03\xea\xf7\xc7\xec\xcb\n\x88r\xd3\x1a\"\x17\xb4)\x89\x1a\x0f\x1c\x87u\xe1\x1c\x85\x12\x1a\xe8\xb8\x88\x03n\xedd\x85\\\n\xb6\x18,\x16\x0b\x04\xc2(\x15\xa0\xd1\xc2]X \x8eq\xb9\xc5\xc2As\x10H\xf5\x10w\xe1ro'\xc3a\\_\xb1/\x80\xd5-AZkK\xad\x8e<\xe6\xb6\xf3\xb0,\xde\x91ZPH\x83\x90\x8ap\xd4B\xc8\x85$\x15\xe1\xb0\x85\x90\x0bP*\xc2A\x0b!\x17\xaeT\x84V\x0b!\x17\xbcT\x84\xfd\x16B.\x94\xa9\x08\xcd\x16B.\xb0\xa9\x08{-\x84\\\x98S\x11\x1a-\x84\xdc\x0cY\x11\xea\x96\x9c\x90\x0b\x81\xe6K\xad\x8e\x828\xca\xb6\x80\xa8&\x86\xdc\xa7-<\xaa\x89!\x17j\x0b\x96jb\xc8\x8d\xdaB\xa7\x9a\x18r\xa5\xb6@\xaa&\x86\xdc\xa9-\xac\xaa\x89!\x97j\x0b\xb2jb\xc8\xad\xdaB\xae\x9a\x18r\xad\xd6\x00\xact/\x9e\x92\x0f\xc7\xe6K\x8d\x88\xc8x\x02.8\x9b/\xb5&>\xe3\xf1\\\xa86_ju\xb4\xc6\xc3\xb9\xc0m\xbe\x14A\xb90n\xbe\xac\x824\x1e\xcc\x05u\xf3\xa5F\xc5u< \x17\xe2e\x92\xd7Q\x1e\x8f\xe7\x02\xbe\xba\n\x01\x01\x17\xfeU\xba/\x02<\x9e\x00\n\x06+\xc7\x80\xe0\xect9_\x16+\xe4\xc8\x8eQ\x90\xf2\x14D!l\xe3l\xc2\x03\xda\x01D\x98\xf3\xa5\x00\x0c\xc5\x9b\xb5\xa2D$|\xf49_je\x00\n\xe1\xf9X4s\xa3,\x1c\x85\xd0|d:_VA\x00\x87\xe7\xe3\xd4Zz\x11 \x18\xb5\xce\x97U@\nt\x02 \x86\xadk\x11RA\x11me\xb8<\xd4\xe4I\xa0\xf8v\xbe\xd4\xea\x10\x176\x1f\x1b\xedfM\x11\xa1\xf9\xd8\xb7i\x88\x88\x86\x8f\x84\x9b1&\x8b\xe0\x80A \x88\x8b\xf3\x81C\x00\x07\xa2d\xa2\xb3\xc2DP\xcc\x9cu\xd8,l\x86\xc6U>\x82\xaeZ\x91\x87\xab\x10 \x10O/Eh(\xba\xae\xdb \xa0\x81b\xed\x8a\xa6\x0e\xb7\x81\x81\x0d\x88\xbc\xb3a\x87\x08\xbe\x013\x02qxC$R2\x14\x957T\xe2\x0e\x06\xc4\xe8\x0d\x99hT\xe1#\xf6\xf9\xb2\x0e\xd79\x020r\xcf\xef\x97\x17s%t\x07\x9d,\xce\x7fn\xd6N\xec\xbb\xd7rd3\xf3\x8a\xb9\x11\x18\x8a%71\x17\xf0zn\x16sl \x14Cn\xe6.\xd0\xd5\xe4-\xe6W#(v\xdc\xcc^\x80\xe5\xacx6\xdc\xac_\x00\x8bY\\\xcc\xa8,\xa7Xq1A\x01%\xc3\x021C\nE\xb1\xe5\xe2\x86R+U\xe8 Q\\\x0d\xa1\x18r\x81\x05)\x81\x9c#\x81\xa1Xr\xa1\x07\xe1[y8\xd1\xe2\x7f\x05\x86b \x05'\x05E\x0bC\x88\x17;\xdc\x10\x1dI\x1b\xeb-]-C\x90\xecd+h\x92l\xd4\xcax$f\xcc.\x8fH\xb2a+\xe3\xa1\x981\xbbt\"\xc9\x06\xad\x8c\x07b\xc6\xec\xb2\x8a$\xb3Z\x19[b\xc6\xec\x92\x8b$\xeb\xb72\xee\x8b\x19\xb3\xcb1\x92\xcclel\x8a\x19\xb3K5\x92\xac\xd7\xca\xb8'f\xcc.\xe3H2\xa3\x95\xb1!f\xcc.\xf1\x88\xae$\xed 5\x82d\xdc\x96' Ie\x9d\xa4F\xc8\x98\xc3\x1d\xa5J%\xb41\x1f\xca\x99\xc3\x9d\xa5J5\xb41\x1f\xc8\x99\xc3\x1d\xa6JE\xb41\xb7\xe4\xcc\xe1NS\xa5*\xda\x98\xf7\xe5\xcc\xe1\x8eS\xa52\xda\x98\x9br\xe6p\xe7\xa9R\x1dm\xcc{r\xe6p\x07\xaaR!m\xcc\x0d9s\xb8\x13\x95\x81\x9e\x98w\x05 Y\xcb\xa2\xc3e[HW#\n\x8e\xd0\xd2\x00\x0c\x17\xa9\\\x8d\x94=\x174\x02\x8b\"8~$\xd3;\xd2*\xd8(\x12X\xb2\xc0\x01%\x91\x10\x92V\xc0\x84\x95\xc0\xb2\x19\x8e0\xcb\x0c\x92\x94\xb7\x94\xaf \xe4\xac\xd3MR\xceT\x84\x08,\xc9\xe0\x18\x94\xc9NIk\x00\"Q 9\x00\x07\xa5dJK\xae|&4\x05V\x89p\x94J%\xc1\x14\xda!\xadC\x10\xb6Ry\xb3\xf6~@\x06\x9c\xc0\xbaP\x18\xc7V\xa96i\x0d-\xcc\x05\x81-\x95\x98\x93\xf2'q\x82Z\x84i\xbc\x9a\x89B \xbddci\xae\x1a\x85\xb0z\xa9\x12Y/\xd9\xe0ZZ\x93 \xce^\xaa\x84\xdaK6\xda\x96\xd6$\x08\xbc\x97*\xb1\xf7\x92\x0d\xbf\xa55 \"\xf1\xa5J0\xbed\xe3qiM\x82\xd0|\xa9\x12\x9d/\xd9\x00]Z\x93 V_\xaa\x84\xebK6b\x97\xd6$\x08\xde\x97*\xf1\xfb\x92\x0d\xe1\xa55 \xa2\xf9\xa5J@\xbfdcziMpdBl\xf6\xb5\x8fA\x92\x9e\xab\x16\xef\x13\xbb\x83\n\xb5\x89{\xaf\xda\x02\x80\xd8NT\xa8M\xdc\x83\xd5V\x04\xc4\xfe\xa3Bm\xe2^\xac\xb6D 6,\x15j\x13\xf7d\xb55\x03\xb1\xc3\xa9P\x9b\xb87\xab-\"\x88-Q\x85\xda\xc4=ZmUA\xec\xa1*\xd4&\xee\xd5j\xcb\x0cb\xd3U\xa16q\xcfV[wT;l\xe2\xaajDQO\x15\x14\x01\xdbo\x05^\xca\x8c\xe3\x03\xed\xcc\x15\xd0zsN\xcc\xad\x810<\xf9\xad\xbb\x82\xa0\xd8\xbd\x133,\xcb\x19n\xfc\xc6^\x81^\x86X\"\\^\xcap\xe27\xfd\nl\xb1\xc7 \xe6U\x96\x93\xdc\xf8-AR'm\x0c)\x14-$\xb0mX\xd0\x14{\x80b\x9ee9\xc5\x0d\xdaT$%h\xe3I\xa1(\xce\xd0\xc6#\xe1\xb0\x91\xe0\x05\xbd,\x84\xe2 \x9f\xbc\xcb\x08\xaa\xcdI1\xcb\x1a\xc1\xb97\xbbsYjK\xca\x0d\xe2\xc4\xefjR:\x92\xf2#0\x0cW~\xdf\x93PQ\xbec\xd6\xa2\xc6\x02Cq\x85vF\xcbN!g\x08\xf1\x02\xb6M\xc96\xb5p$A\x14_hg\xb5 \xec\x8dd\xcd\x98\x97R\x9c\xa0]WB?s\xbc\x968x\x03ax\xf2\xdb\xb2\x05\x81\x9c\x1d\xcf \xda\xb2%U#\xe7G`h\xed\x01\x9b\xba\x04E\xb5\xaf\xdb\xc2\xb8\x86Q\xbc\xa1\x9d\xdf\x82\x88\xd8\xfc\x15s&A\xb4\xaf\x03\x9b\xc3\x14I\x8b+Q(\x8a3\xb4\x81L\xd1\xb4\x0d\xc74\x8c\x96\x1a\xd8e\xa6\x88\xa43$\x81a\xb8\xf2\xfb\xd0\xa5\x07-\x15b\x02\x12T\xf0\x05\xd2&\xc2\x08\xa18\xa6#\xe5.c,\x0e\x19\xc8#=R\xf6l\xe0\x00U\"\x8a!\xeaC@\xd2\x1a\xa8H\x02b/\n*\xca3CR\xe6Dh\x01\xb1\x16E\x19\xf5\x01#)s\xca 9\xf6\xa2\xb0\x839\x8f\xa4\xa0}y=\x928\xa4>\xc4$\xad\x84\x8a\x19x\xf6\xe2\xc0\x849\xf3\xa4\xd0\x92\x96\xaa\xc4\x91\nyP\xaa\xbd\xb3\x11\xb37_\x898t!\x8eVI\xeb`\x02\x18\xb8\xdf\xc1\xb1Ly\x16Kn\x0f9kQpC\x1d\xdcR\xb1\x85\xbc\x1aQ\xb4C\x9d\xf5j7\x059\x07\xf0\xd5\x88\xc3\x9f\xeax\x98\xbcw\xcb\x99\x0b\xe3!\xfa0\x99\x82\xae\xe4\x15\x89\x03\xa4\xf2\x00\x9a\xb4\x06\"L\xe2Y\x8b#&\xf2\xb4Z\xbb\x19\x889\x1e\xaaD\x18B-\xdb\xf9KY\x8bc*\xea0\x9c\x82 \xa4\xd5\x88\x83,\xf6\xfc\\{ML\xa8\xc5W&\x8e\xba\xe8Sw\xd2\xaa\xf8\xd8\x0b\xe8\x84\xc20\x8c9\xa9\xa7R\x93\xdc\x85\xc5q\x19{\xbcO\xa5\xae\xb6 K\x18\xa8Q\x87\x02Uj\x92\x07&\x92\xc8\xadu\x17\x99\xc0\x08*\x00\xf7\x94#[?\x08\xbe\xdf\x1a\xd9F]\xd4\xedY\xdc{j#\xbb\xd7\x94C\xc5f]\xcc\xbfY7\xb2\xfbu)\xffj\xdd\xc8\xb6\xeaR\xfe\xdd\xba\x91=\xa8K\xf9\x97\xebF\xf6\xb0\xa9\x97\x7f\xbbn\x84\xeb\x06k\x18-R\xae\xd5\xd8\xa0\xcb\xc1\xa6\xe3\x1e\x03\x820&\x8d\x01\x94\x80\xfb4\x04\xd0\x04\xb6h\x08\xa0\x0e<\xa0!\x80N\xf0\x90\x91\x05PL\xdc(&\xce\x06\x16N3\xb1\xc1\x00@\xd5\xc4=\x16\x05\x81L\x06\x04('\xee3\x18@;\xb1\xc5`\x00\xf5\xc4\x03\x06\x03\xe8'\x1e\xb2\xf2\x00\n\x9a7\n\x9a\x87i\x1a\xfa\x9c\x86\xe6\x06\x8b\x00U4\xefq0\x08e\xb2(@I\xf3>\x0b\x02\xb44\xb7X\x10\xa0\xa6\xf9\x80\x05\x01z\x9a\x0f9\x99\x00E\xa5\x8d\xa2\xd20\xe2\xb4\x94\x1aT1\xa8\xa2\xb4Gc \x88IA\x00\xe5\xa4}\n\x01h&\xb5(\x04\xa0\x96t@!\x00\x9d\xa4CZ\x0e@!\x1bF!\x93\x16?\xda@\x1ab\x89@\xbdm\x00\xbdq\x84\x10\x1d\xafL\x96\x0cP\xf0\x86W0K\x05(}\xc3+\x9d\xa5\x02\x0c\xb1\xe1\x0d\xc1R\x01\xc6\xd9\x00\xc6\xe1\x1a\x06Xl\xc5\xce\x125\x11<6\xae\xc0Y\x83!\x02-\xb6\x82\xa6\x12\x96\x10\xa2\x03\xa6\x17\x86\x0c\xb0\xd8\n\x98q\x18*\xc0b+`\x12b\xa8\x00\x8b\xad\x80y\x89\xa1\x02,\xb6\x82\xa6*\xb6a\xc0\xc7\x85l\xfd\xe0\xdb\xf1\xd2\x0bX\xdb\xf8\xb6Q\x95@\x06\xf0\xed^]\x0c\x95\x9aU)\xf0\x95'\xbb_\x15\x02\x9fU\xb2\xad\xaa\x10\xf8Z\x92=\xa8\n\x81\xaf-\xd9\xc3\xbaN\xa0\xa1\xb8j(\x18\xbf\xf8\xd8\xa0\x8a\xc1&\xe3\x1e\x8d\x81 &\x05\x01\x1a\x8f\xfb\x14\x02\xd0\x00\xb6(\x04\xa0\x06<\xa0\x10\x80.\xf0\x90\x96\x03PH\\+\x04\xec\x9b~l\xd0\xe5\xa0J\xe2\x1e\x03\x820&\x8d\x01\x94\x12\xf7i\x08\xa0\x95\xd8\xa2!\x80Z\xe2\x01\x0d\x01\xf4\x12\x0f\x19Y\x00\xc5\xcck\xc5\xc0\xf3\x8c?7\x18\x00\xa8\x9ay\x8fEA \x93\x01\x01\xca\x99\xf7\x19\x0c\xa0\x9d\xb9\xc5`\x00\xf5\xcc\x07\x0c\x06\xd0\xcf|\xc8\xca\x03((\xad\x15\x04\xc4)~j\x90\xa5\xa0j\xd2\x1e\x05\x81\x10&\x89\x00\x94\x92\xf6I\x00\xa0\x91\xd4\"\x01\x80:\xd2\x01 \x00t\x91\x0e)\x19\x00ElhEL\xe4n\xb3\x01\x143Qp\xa4\x0d\xaf-\x96\x0c\xa2\xe248i\xf5\xb4\x0d\xa7\xd4I\xab\xe7m8=OZ=q\xc3\xa9~\xd2\xea\x99\x1b\xde\x1al\x83\x00\x0b\xad\x98Q\xbf\"\x81\x87\xbc\x154 \xd0$\xa0\x85V\xc0\xc4\xc0\x90AT\xfc\\A\x13\x01\x16Z\xf1\xb3\x07M\x03Xh\xc5\xcf'4\x0d`\xa1\x15?\xc3\xd04\x80\x85V\xc0\x9c\xc34(\xb7P\xfb[-\xe9\xd7\nFv\xfer\xce2\x96\x01\xf2-d\xa9 \xe5BA \x84I\"\xc0\xc4\x0b \x00s/$\x00L\xbf\x90\x000\x03C\xc9\x00&a\x08\x84(\x0f\xc3A\x04\xa9\x18\x1e\x07\xc1L\x0e\x06&d8\x14\x98\x93\xe1P`Z\x86C\x81\x99\x19^.09C\xc2D\xf9\x19\x1e#H\xd1\x00@\x08g\xf280Q\xc3\xc3\xc0\\\x0d\x0f\x03\xd35<\x0c\xcc\xd8\x00\xb2\x81I\x1b\x12'\xcc\xdb\x00 A\xea\x06BB@\x13\x00\x82 \x1c\x00\x07\xe6p\x00\x1c\x98\xc6\x01p`&\x07\x92\x0fL\xe6\x90@8\x9f\xc3\"\x04)\x1d\x0e\x06\xa1L\x16\x05&vX\x10\x98\xdbaA`z\x87\x05\x81\x19\x1eN&0\xc9\xc3)\xaa=\xcf\x03kN1\xd5\x03\xeaS-\xdb\x03)Y)\xe1\x03)^)\xe7\x03\x19C)\xed\x03\x19H)\xf3\x03\x1aM-\xf9C\x92*\xe6\x7f8\x92cR@<1D\x0b\x91\xc2\xd3\x9aJ\"\x88#T\xcd\x05q\x84\xaa\xe9 \x8eP5#\xc4\xb7Q9)\xa4\xe5\xdfs\x8f\xe1\xbc\x10Q(H\x0d\x91\x08\x08`\x12\x000AD\x94\x839\"\xa2\x1cL\x13\x11\xe5`\xa6\x88\xac\x1fL\x165\x00Q\xbe\x88E\x08RF\x1c\x0cB\x99,\nL\x1c\xb1 0w\xc4\x82\xc0\xf4\x11\x0b\x023H\x9cL`\x12\x89@\x89\xf2H\x1cD\x90J\xe2q\x10\xcc\xe4``B\x89C\x819%\x0e\x05\xa6\x958\x14\x98Y\xe2\xe5\x02\x93K\x04L\x98_\xe21\x82\x14\x13\x00\x84p&\x8f\x03\x13M<\x0c\xcc5\xf100\xdd\xc4\xc3\xc0\x8c\x13 \x1b\x98t\"pp\xde\x89\x01\x08RO,\n\x02\x99\x0c\x08L@1\x180\x07\xc5`\xc04\x14\x83\x013Q\xac<`2\x8aUPk>\nT\x98ZJ\n\xd2\xa2RV\n\xd0\xacJb\nP\xb6Jn\n\xd0\xbfJz\n0\x89J\x86\n\xb2\x92R\x92\x8a T\xcbS\xb1\x04G\xa4\xaa8R\x80\x12\"\x04\xe7(\x85\x84\x15K\xa6\x98\xb3b\xc9\x14\xd3V,\x99b\xe6\x8ak\x9b(y\xa5\x90\xbdR\xf8&Kd\xeb\x9a_\xc5fPF\xab)\x14%\xb4\x08\x04\x040 \x00\x9c\xcej\xca\xe1lVS\x0e'\xb3\x9ar8\x97E\xd4\x0f\xa7\xb2|f\xad\xc0\"\x0c\x16!Jd\xb10\x08e\xb2(8\x8d\xe5\xf3\xb1=\x0b\xb2X\x10\x9c\xc4\xf2\xf9\x98\x9d\x05\x0d9\x99\xe0\x14V\x83\x12f\xb0X\x88(\x81\xc5\xe1 \x98\xc9\xc1\xe0\xf4\x15\x8b\x82\xb3W,\nN^\xb1(8w\xc5\xc9\x05\xa7\xae\x1a\x988s\xc5aD\x89+\x1e\x08\xe1L\x1e\x07\xa7\xad8\x18\x9c\xb5\xe2`p\xd2\x8a\x83\xc19+^68e\xd5\xe0\x04\x19+\x1a JX1(\x08d2 8]Ec\xe0l\x15\x8d\x81\x93U4\x06\xceU1\xf2\xc0\xa9*FA\n\x99*Hc\xaa\x89*@\x8f\x8ay*^\xb9ji*^\xe1jY*\xde\x08jI*\xde0j9*\xc0X\x8a)\xaa\x86R5C\xc5P\x1c\x95\xa0bi!R\x88\x12\x9c\xae\x94\xd2S\x0c\x9drv\x8a\xa1SNN1t\xca\xb9)\xb6}\xea\xa9)\xbf\x8c\xd4\xa0\xccT]&JL5\x00\xa8\xdcl\xca\xe1\xb4T]\x0cg\xa5\xeab8)U\x17\xc39\xa9\xa6n8%\xe5\xd3k\x04\x16`0\x00QB\xca\xe7\xc3\x7f\x16d2 8\x1d\xe5sq=\x8b\xb1\x18\x0c\x9c\x8c\xf2\xb9\x88\x9d\xc5\x0cYy\xe0TT\x0d\x12f\xa2\x18\x84(\x11\xc5\xc2 \x94\xc9\xa2\xe04\x14\x03\x82\xb3P\x0c\x08NB1 8\x07\xc5\xca\x04\xa7\xa0j\x948\x03\xc5BD (\x0e\x07\xc1L\x0e\x06\xa7\x9fX\x14\x9c}bQp\xf2\x89E\xc1\xb9'N.8\xf5T\xc3\x04\x99'\xaa\\\x94x\xa2A\x10\xc6\xa41p\xda\x89\x82\xc0Y'\n\x02'\x9d(\x08\x9cs\xa2e\x81SN\xb4b\xda3N\x80\xa2\x14\x13N\xbc\xf6\xd4\xf2M\x9cF\x95\xd2M\x9c\x92\x95\xb2M\x9c\xde\x95\x92M\x9c)\x94rM\xbcu\xd4RM5\x9db\xa6\x89\xc6\x1f\x93hb(\x01B\x88\x0e\x9a{T\xd2L4\x95j\x96\x89\xa6RM2\xd1T\xaa9&\xa6]\xa7\xa5\x98\x04\xd9$\\\x85SP6\xa9)\x14e\x93\x08\x04\x040 \x00\x9cMj\xca\xe1lRS\x0eg\x93\x9ar8\x9bD\xd4\x0fg\x930\x13\xd7\xb3\x08\x83E\x88\xb2I,\x0cB\x99,\n\xce&a>\x16gA\x16\x0b\x82\xb3I\x98\x8f\xb2Y\xd0\x90\x93 \xce&5(a6\x89\x85\x88\xb2I\x1c\x0e\x82\x99\x1c\x0c\xce&\xb1(8\x9b\xc4\xa2\xe0l\x12\x8b\x82\xb3I\x9c\\p6\xa9\x81\x89\xb3I\x1cF\x94M\xe2\x81\x10\xce\xe4qp6\x89\x83\xc1\xd9$\x0e\x06g\x938\x18\x9cM\xe2e\x83\xb3I\x0dN\x90M\xa2\x01\xa2l\x12\x83\x82@&\x03\x82\xb3I4\x06\xce&\xd1\x188\x9bDc\xe0l\x12#\x0f\x9cMb\x14\xa4\x90M\x824\xa6\x9aM\x02\xf4\xa8\x98M\xe2\x95\xab\x96M\xe2\x15\xae\x96M\xe2\x8d\xa0\x96M\xe2\x0d\xa3\x96M\x02\x8c\xa5\x98Mj(U\xb3I\x0c\xc5Q\xd9$\x96\x16\"\x85(\xc1\xe9J)\x9b\xc4\xd0)g\x93\x18:\xe5l\x12C\xa7\x9cMb\xdb\xa7\x9eM\xc2eP\x06e\x93\xea2Q6\xa9\x01@\xe5fS\x0eg\x93\xeab8\x9bT\x17\xc3\xd9\xa4\xba\x18\xce&5u\xc3\xd9$L\xaf\x03X\x80\xc1\x00D\xd9$\xcc\x07\xf9,\xc8d@p6 s\xf1;\x8b\xb1\x18\x0c\x9cM\xc2\\l\xceb\x86\xac{U\x1fl?w\x15\x1fV\x00w\x17\x1f\xd4\x00w\x19\x1fR\x01w\x1b\x1f\xd2\x01w\x1d\x1fR\x02w\x1f\x1f\xd2\x02w!\x1fT\x03}\xe7\x1e\xd6\x01}\xe9\x1eT\x00}\xeb\x1ej=}\xed\x1ej:}\xef\x1ej7}\xf1\x1ej4}\xf3\xbelq\xfb\xc1\xcb\x033f\x90\x17UD\xa3\x1d\x05\x01\x07<\x12\x01\x8ey$\x00\x1c\xf6H\x008\xf2\x91\x00p\xf0\xa3d\x00\xc7?\xf6\x00\xabh\x08\xe4q\xe0(\xc8\xc1\xc0\x81\x90C\x81c!\x87\x02\x87C\x0e\x05\x8e\x88\xbc\\\xe0\xa0H\xc0\xe4\xe3\"\x00\x04\x87F\x1e\x07\x8e\x8e<\x0c\x1c y\x188F\xf20p\x98\x04d\x03GJ\x02\xd72XBHp\xbc\x04\x80\xe0\x90 \xe0\xc0Q\x13\xc0\x81\x03'\x80\x03\xc7NH>p\xf8$\x80\xb2\x11\x94\x83\x81\x83(\x8b\x02\xc7Q\x16\x04\x0e\xa5,\x08\x1cMY\x108\xa0r2)l5\xaa\x9ef\x0f\xc8\x83W\xc2\x81\x96@\xc0\xe3l\x03\x80\x87\xd9\xa6\x1c\x1ee\x9brx\x90m\xca\xe11\x96\xa8\x1f\x1eb\xe9\xfd[\xe1\x08\xcb\xc2\xe0\x01\x96A\xc1\xe3+\x03\x82\x87W\x06\x04\x8f\xae\x0c\x08\x1e\\Y\x99\xe0\xb1\xd5gF\x1b\xd1\xd0\xca\xe1\xe0\x91\x95\x85\xc1\x03+\x8b\x82\xc7U\x16\x05\x0f\xab,\n\x1eU9\xb9\xe0A\xd5g\x07\x18\xd1\x98\xca\x03\xe1!\x95\xc3\xc1#*\x07\x83\x07T\x0e\x06\x8f\xa7\x1c\x0c\x1eNy\xd9\xe0\xd1\xd4\xa7\xc6\x1a\xd1`\xca\xa0\xe0\xb1\x94\x06\xc1C)\x8d\x81GR\x1a\x03\x0f\xa44\x06\x1eG\x19y\x14\x86Q\xc1\x88\x89\xeb\xe1F4b\x12\x08x\xc4l\x00\xf0\x88\xd9\x94\xc3#fS\x0e\x8f\x98M9\x96\xdc\xca\x05\xfajr\xc1\xa8\x10\xa6\x95C\xdb7\x12Kf\xae\x1d?\xb4\xf2\x92}I5\xe3\xf3\x80\x0e)\xda\xa5\x9a\x8b\x9c0\xb6S/\x0c\xce\xb1\x17 -]\xc5\xe1z\xb9\xa2 \xd6\x81\x8b\xe2\xac\x98\xa3\xa9K\x18\xc7\x0b51M\x10\x06Ha\xe9s\x00d\xce\xd6Q'\x88\x0d\x91)H\x0e\x91\xe5\xc2+H\xaf\xb0p+\x9b\xe4\x9f\xd4\"\x9eJ\xa5A<\x95B{\xc4\xa2\xe3\x93D\xe7\xa9TD\xe7\xa9\n\xd1)\x8a\xb4D\xd9\xd8[\x06\xe7YT\xc0\x94\xc7dy>Q2\x00\x87\x048(HQ\xac`\xed\x03#E\xed9bA\x18\x08(\x0b\x83)\xc5Q\x90G\xc1\xfbR\\y\x83DF\xbf]D\xffh aaZ-G#`a0$\x0c\x0d\xaa,\x9c\x7f!~\x11\xc6\xfe\xb9cG^jc\xef\x11\xb1P\xccBq\xb8E\xb1c'\x1cr\xcd\"\xd7Q\x04#\x03\x16y\xd2p\x98\x12\xce\xa1\xd4\x12\x00-n\x0c\x00\x16\xb7\x07\x00+\x0c*\xcan\xda\xb8\x98Z;9\xb0\xa4\x99\x1cV\xd2J\x0e\xab\xd0HA{8\xb7\x92\xb5\xe7\x08\x1f\xe4\xb1\x92\xf6pX`8]h\x833\xe6\xc1\n\xd9n>\xab/\xc2 \x8b\xf5\x1e\xd19\x1fR/4\x8b\xa5K\xd6s\x80\x94\x0f\xa1\x17\x06Ql\xf2\xc5=\xa2\xb8\x07\x05\xea\x0b\x93@\x18@\x90\xbe\xe8S\x00\x88\x85E\"\xf8\xe2\x01Q\xdc\x1d\x0d\x01\x06C\x12Q\x00\xda{\xc3\x81\xd5\xbd\x16$\"\xf5g\x9d\xae\xc5\x02\x005a\x04\x9a\x01d\x07\x1a\x01\x99\x82F\x08\xacA\x83`\x83\xb0\x18\xd0&\x0c\x080\x0b\x8d\x10X\x86\x01\x15\x18\x05\xeb(\x8cU\x99\xc9|\xa1\xc5\xfcV\x83q\xb4\xa4\xbd\xfc6s\xf9m\xd6\xf2\x15\x8c\xe5\xb7\xdb\xcaW0\x95\xdff)_\xc1P\xfe\xb1v\x12\x98\x04\x0bM\x82[M\xc2\xd1\x92&\xc1m&\xc1m&\xc1\n&\xc1\xed&\xc1\n&\xc1m&\xc1\n&\xc1\x80I(\x8c\x8f\xecd\x1d\xa3C\xd3O\xb2\xce\x03b\xb2r\n\xd8\x17\x01\x03;\x8e\xc3-\x01\xedq<\xbd\xc0EAZLi\xc5\xcf\xe7Fs\"+m?\xcf\x98\xf86\xc6\x9acG\xe5\xe8\xb0\xb1c\xcf\x0e\xd2\xf3\xe69\x8dO\xe3u\xe0\xd8):\xe4\xc9\x81<5\x82\xce\x83p\x1b\xdb\xd1$\xdc\xa0x\x91\x7f\x9c\xcfs]\x14Lr\xa9\xea\x87\x08c/J\xbcDa\xcc9\xc0\xeaH\x94\xd5\xcb`[4L\xa3EJ\xae\xe3\xbd'\xea\xb9\x1e\x88UU\x9d\x11\x9c\xaem\x05u+\x0c\xf1\x95\xc2|u\x13\xf8\xc7X\xc0W1\x80\xff<\xfa\xf7\x8fT\xbf\xff\xdd\xb4/Q4VW4>F\xd1XE\xd1\xf8y\x14\x8d\x8fT4~\x8a\xa2)\x96U\xb9\xe6\x84Aj{\x01\x8a\x0f\xf5\xa3\xfdy\xe2\xc4!\xc64E\xb1h\xa6\xb7\x12\xecu\x1aN\xc8\x9d\x96\xec\x01\xa3\xddX\xcb\x1e\xf2t\x0c\x0cS\xb0\x86Y{\xe7<\x00bj\xec\xd9\x1buIARPX\x8d9\xf4\x94\x03\x15\x04V\x18M\xcaV\xf8'7\x02\xa0\x84\xdb\xe0\x1f\xdb\x04\xb1\xb4\xf8di\x01JXZ\x0cHK\x8b\x82\xbd\xe8\x10\x85\x89\x97'\x02\x17\xde\x0e\xb9\xff\xd7\xf3\xa30N\xed \x9d\xfcQ\x97\xd8\xf3$\xc4\xeb\x14\x11\x85\x19\xe9y\x8c\x9c\xf4G#\xdau\x88\xbf?\xd1Eg\xc4\xdf\x9f\x14\xcc}\xe0\x04\xcc\x1c\xe7\xcf\x94QAH\x15\x9f\xcc$\xf7\xff\x83\x04\x17\xc9\x88\xff\\\x19)\x01\xb6\x89\x16\x84\xb1o\xb3#u\xf6\x88F\x16\xa370\xa0\xd3\xb0(\xa6#\xc9(>>'X\x0b\xc5\x07J\"\xb9\xe0\x90\x8a\x13\x8d\x85e\xd2)\x88\xa7\xe0m\x8d\xcclt!\x14\x19\nCx\x89\xfd#\x05\x96\xca\xa6jfp\xe6\xe6e\xc3\xbcl\x14f\xa3\xcd\xed\x04\x1d6(N=\xc7\xc6e:;{\xc6\xef\x91l4\xdfsY\xa8\xef\xb9.\xe6\x80i\x18\xb1\xc04\x8c\xb8\xaaS\x9f\xab9\x0fp\x14\x0c~\x00\x9a\x91\xf9\x8ezK\x00\xb4\xb01\x00\x16n\x0f$B\xd1$\x856)8q\xd9P^o\x92vr`q39\xa8\xa0\x95\"\xbb\x1d\xed\xf8e{\xf01\xed\xe1\xc0\xe2\xf6pPA{\xf8\xfa\xcb\xf6PX\xd7\xf3\x0fad;^\xba?7\xb8\xa23\xf6\x01\xf41\xfa\xecq\xf1\xfdym\x8b\xe6\x0f^\x99\x15/f\x90\x92w\xa7kXI\x07ez\xf1\x82IK9'\x86\xbc\xd6J\xfc\xae\xc5\x13\xdaN\xeamP\x03\x19M\x94d\x0c\xd7\xa9\\\xc8p\xcd\xec\x9e-q\xb8=\xe3\x9e@\x82\xe7\xcf\xbf\xa3\xbe\x14\xea\x15\x18|\x95-\x03\xf3S\x11\x9dn\xfe\x9f\x1a\xa8\xab\xa9\xedXQ\x9b\nKC\x95\xf5\x9e\x89Py\xb3\xda@y\x1b\xd9\x16\x18\xdf\xa7\x05\xcd\x06{^+\xa4w\x16R\x98 _\x7f\xb6\xef\xe1/\xe3p{\xd0\xfc\xf0Q\x0b\x93\x9dVd\x0f\xfd0LW^\xb0<_\xc6\xf6>ql\x8c\xea\xb6\xcdm\xe7aa;H\xdbx\x897\xf7p\xd6\xf2r\xc1+)\xa24\x93of\xe5?a;E\xdf~\xd4\x7f\x9a\x88\x9e\x03\x1a\xe5Xu\xba=A\xa7:\x02z:\xe4\xac\xa5\x16^\xdb`\xd7\x89\xe1.\x9b\xeb$\xb7\xc0\x8fFW\xb7HM\x11O\x81:\xcaaI\xc4\xac;\xe6Yu\xc7\x00#\x0d\xdb\xf1\x12\xfd\x7f\xc5A\xbc\xe0\x18\x1f\xe1\xd1OEI\x9d\xa5\x80\x88L \xf2\x9a\xb2\xb4\xcdwz\x90\xeb\xf4\x84\x06o\xf7\x1f\xc0\x17\xb3\x87L0\x1dzAZ\x8fH\xce:N\xc2\xf8\xbc|H#\x93\x95\xed\x86[\x0d\x02N\xea\xc5b\x8c\xb0\x9d\x89\x05\x99\xdd\xc6\xb8\xd3\xb5\x92\x8e\xb3\x9e{\x8e6G\x8f\x1e\x8a\x7f\xec\x1a\x03\xeb\xac;\xea\x9fu\xfb\xfd3\xe3\xa7\xc9\x91x\xb1\x88\xe7\xf6\"\xcd\x04\x0d\x83\x14\x05\xe9\xf9_\xfe\xd2\xf8\x7f\xb8\xd3\n\xe4\xb9\xde\xd1;\xc6 \xdauz\xd1\xaeC\x9e\xf7\xeb\xfd4Q\x86\xe5\x07;c\xdb\xf5\xd6\xc9\xb9\x17\xacP\xec\xa5\x93f\xd2\xe4\xd6\xd1\x93\"\xf3\x99\xe7e\xf4I\x11A\x1a\xba\xfeb\xb2ByN'\xff\xf91\xcf\x98\xee\xce5\xf9\x9cu\x846Ui$\x1a\xcd\xfd\xbb\xd0\xeb\x99\x18Ej_\x10d\xcc\x97\x9a\x1dx\xbe\x9d\xa23\xc1s\xa8/\x11\xa5\xc2\xd0\x89=\xc4IM\xdb\xec(\xd0\n\xa6\xa5~\xd4\xf4Ce\x17\x9d-2\xea\"\x83-\xea\xd5E=\xb6\xc8\xac\x8bL\xb6\xa8_\x17\xf5\xd9\"\xab.\xb2\xd8\xa2\xf1x\\\x17\x8e\xc7c\xa0\x98*\xe7\x00\xbe\xbdk\xa45\xfa\xc3\xfe\xc8\x1c\xf4\x87,\xaa\xf4\xf2\x1aY\xfe\xce\xc3\xbc\xd4\xb3q\x0d\xe3\xb3\x95\x8f\xda:HP\xc3(\xff\x8d\x86\x04(IQf\xa0h\xaf\x15\x11T\xdeM:!\xb3\xaf,\xc2Ej\xb05>\x10\xbf\x9e\x1b\xecB\xa2\xa4k6\xae \xda\x95\x01\xd6\x01c{G`\xcd#\xb0\xfd#\xb0\xd6\x11\xd8\x01\xa3\x17\xe8`\x7fA\x8f\xbd$\xd5b\x94 \xa1q\x08\xc4\x9a{\xf1\x1c\x99\xaf\xd6'94I\xf7\x18i\xe9>B\xc5\xd1*\xa1%\x8b\xed\xa5N\xf4sDm7u\x8f\xdbo\"9&(B\xb1\x9d\x86q\xce\x94\xe0at-A\xfb=\x7f\xd9\xf1\xfc\xe5\x81\x18\xd2\x9b\x9cG\xfe\xab\xeb%\x11\xb6\xf7\xe7s\x1c:\x0f\x02\x1d\x06\x0fI\xc7>\x94\xe7\xe1Mk\x88\\\x17\x9a\x02\xf8\x01k\"-\x95\xd5\x06\x0d\xb6\x0c\xa2\x9c\xf5\x0b\xa9\xc6\x03\xc7Y,\x9e_\xaamlG\x11\x8a\x05\n\xec\x0f\xf4hW\x1a\xf0\\\xef\xe4\x9b&\xa5\x0b\x9d\xeb\x9d^VH\xcd\xf0\xdecVRN\xcf\xf3p7\x01\x9f\xd2\x12\x84Qn\x1a-\xb5\x97Z\x82\x9cL\xeaCe4\x82ymH\xcdO\xb4\x05F;\xf2Y\xf6;%I\x18{\x993V\x99\x18\xaa\xcc\xf5\xe2\xa2\x9a2%:\xa98\x12%N\x88\xd7~0\x01\x9f\n\xc5\x7f\xba\xd8\xe4 \xe0F,\xeai\xfe\x8b\xe6\xa5\xc8O\xaaG\x95E\x0c=\x0b\x97\xb2\x7f\x8c\xea\x9f \x134\x8aB\xc4^\xc2E\x81\xbddR\x9b,\xef\xb9F\xb4\xeb$!\xf6\xdc\"\x1c\xb3\xc6g\x03\xebld\x9cu\xcd\x9f\x84*)\x9d\xb8\x99\xf5\xa9\x1b\x1e:\x1bj\x93\xca$\x8e\x18\xf5I'\xd4;V\xb4\x9b\xe4\xa5\x0b\xdb\xf7\xf0\xfe<\xb1\x83DKP\xec-&U\x1f\x9e\xf7\x0d\xcb\x10\xf2\xee\x06\xa1\xe6\xa2\xc4\xe9$\x91\x1d\x1cH\x03d\xfa>7j\xd5\x9f\x1b\x93\xe2?BV\x9dd\xb3\x84\x82\xa2\\\x85}^\xab\xfdD\xc2\xca\xb71u\xde\xa9_5t[\xcc\x04}]\x9f\xa8HK\xf4\xd1\xdc \x8eWVd\xc7\xb6\x8fR\x14\xff\xf1G6\x15\x90B\xf5\xa2]\xcd\xdf\x8av\x1d\x9db\xef\x87A\x98o\x10P\x82\x0ft]V\xdb\xc6C[\xad\x9a\x06\x1f\x0e\xfc\xca&\x9b\x04\xcch7\xa9\x0e>\x90\xfe`\xa9{\xb9\xc5\xdb\xc3\x82\xedq \xdc\xcd\xc8j(\xba\x02\xd1\x07\xfe\xaa\xeb:\xb3\x10\xe9\xb3\xc3a\xb3\x921\x99E\x8c1\xe6\x16;\x00\x04\x14\xad\xd3M\xedy\x1e8\xa0\xf8\xe9#\xceQ\x0eOV]\xfc\x9c\x8dC\x87\xc6\xdb\xfa\xfc\x90s\x04\xa3\xf3\x85\x17'\xa9\x16.\xf2\xf0\x83a\xdb\xd1;\xfa\x11\xbc\xbaebs\xd5/:9\xe7S\xa7\xf3*\xd7Y\xfc\"\xb3\xbe\xad\x999L\x1eSY\xfa\x8bj\xb5\xd9kV\x9b\x99\x9f\x00kd \x9b\xf3\xfb\x8f\x9a\xa5\xbf\x00\x13=U\x111\xb4.c{\x0f6\xab\xeb%Z\x18\xa1\xa0\x19n\x92\xb5\xef\xdb\xf1\xfe \x1a\xe13\xef\x16h\xa8fQL\x8a\x95'V\xd6\x1a\x95s\xd0\xc4\xf7\x82*\x82\xb5\xb2\xdf A\xd9\x1b\x83\xa3\x9f\xe0~c\x00\xcb\x7f\x83\xe980\xe6(\xd9\xcf\x8e\x01w\xb0=G\xf8\xe9\x1d\xef\xa4\xa9\xfe\xa8f\x95\x922C79,\x0fu\xbd\x1eG\xb9\xc30'\xcc\x1aJ\x02\x95\xfd\x91\x9a\xa1$\x9d[\xc0j\xd5g'J\x95Q\xadi\xeds4\xae\xe8C\x9a\x8f\xd2U\xe8\xca\xe6\xed\\\xcf\xf5\xd6\xe5H'f\xd0A\x16\xa8e\xe3\x05w\x03\x8c\x99\\L\xba\x0b\xe5\xd3ONC\xf5\x04\x9d\xed+\xf2v.\x16\x0b\xc5F\x86\xf9\xd2,3\x80\xe7\xb6\xf5\x97\x92$\xb2\xd3\xd5\x11\xd0?\xfepQ\x14#\xc7N\x11\xa5\xccAD\xf4\xacS{[n~\xbdq\x08\xbdc\x16\xab\x19\xfa\xb7'w\xd0\xc96\x8c]m\x1e#\xfb\xe1<\xffW\xb31\x96\x85c\xaa\xf1R\xb9\x19N\xec\xe8\x0f\x07\xa3h\xc7l\x81\xff\x07\x9a\xaf\x17\xed\xd8\xd3\x9d\xcal\xd8\xcd:,\xbc\xa6\xab\xd4p\xa6\x8b*r\xc8\x16\n\xb1\x17\xe5\xebR\x82\x81\xa9:\xe4<\xdfH\xf3?4\xe9\x90\xd1\xbeZp\xc7\xc8\xad\x18\xe0\xf7\xea\x00\x9f\x98\x95\x9e=\xb2\xe7\xa4\xab\xf6\xad\x19\x19\xcb\xb0m\xc4,5\xe0\xf8\xaab\x19\x85IJ\xbc\x8f\"3p\x7f\xec8c}\xc2\xae\x80\x87\xe6YO\xef\x9f\x19\xfd\xbe0\\\xa1\xb8\n\xa7\x1drN(\xea:\x81\x19(\xb3\n\x1f\xf5p\xf9h9\xd7\xac&\x17\x8em\x98\xbc&{V\xef\xcc\x18\x18g\xfd\x91\x82&\xd7j\x8a,\xaa:\x9e\x17(\xb1\x02\x9b\xd3\xd4\xa8\xc2\xdeE\x18\xa5\x88\x95kl\"\x13\xf1\x9a\xec\x8f\xcf\x06\xbd\xec\xff\xad\x8a,\xd8\xaa\xe92\xaf\xec$v\xa0\xd8j\x9cN\xd4\xa8B\x0dK\xc4:\xe6\xc0\xb0\x17\x0b^\x9d\xe3\xe1\x991\xb4\xcez\x96B\x17_\"5\xc7,\xaa:\x9e\x17(\xb1\x02\x9b\xd3\xd4\xa8\xc2>\xb2Sg\xc5\x88e\xe9\xc8tz\x9c\"G\xfaY\xaf7<3\xc6\n\x8a\xcc\xd9*\xa9\xb2\xa8\xec\x14n\xa0\xd4J\x8cNS\xa7J\x05\x19WF\xae\xb1n\xf4\x00\xb7\xcc\xa6\x1cc\xa4\xe6\x96\x19W%e\x16u\x9d\xc0\x0c\x94Y\x85\xcfi\xaaT\xe1\x1f\xe6\xb1^\xc2H\xa6\xbb\x96m\x0fym\x9agc\xfd\xcc\x18\x0c\xdb\x95Y\xf2U\xd2gQ\xdbi\xfc@\xc1\x15Y\x9d\xa6U\x95*\x88\xb0\xbe>\x15:\x98\xd0\xa2\xa2y\xf6\x07\xce\x14\x8d{\xc0\xab\xa5\xc4\x95(i\xb9\xa8\xefd\x96\x07Hzun\xa7\xe9ZR\x0b!\xa0\xb3B>J\xb8\xa4\x9c\x1aY\xa7[\xfe\xa0\xa5^\x8aQk\xaef\xe1\xe14kD\xb3\xd6*\x9eh^\x90Eq\xd4\xd6b\x1eI\xe7{T:\xb5oU%\xd8{M\n\xd2\x1d\xb9.b\xbc*\xb5\xe7\xa7\xad\x82\xa8\x9a\x8bex\xdd,b\xe3\x1b\xd8\xf3N\xedy\x07{l\x1a\x8d<\x89N\xf1b\x16,\xc7\xaf\xfe\x8a\xfa\xd8\\8\xb7bbv\xf2\x99\xcf\x96\xf5X[C\\\x85\x89\xecb\xdf\xbe`5\xa8WeF\xb4\xa3\xceK\x11)l\xc1\xfe\x1e\xbb\xbdW\x08Q\xfa\xf8\x81\xc9\x90\x81\xbeI\xae\xbe\xb5r\xaf\x1aLJhh\x97\xa28\xb0\xb1\xe6\x86N\"\x87\xe6^\xfdGy\x13\x8a\xb5+\xbd\xcdX\xbb\xa8U\xa5\xb5\x8f7\xa8\xa4)\xdc\x11\x12ik\x84h\xb2ALf\x14h\xd3\xf3\xb6 :\xa6\x01\x020%\x7f\xc4fR\x9f\x9e\xb3\x15\xaa\x939\x0fC\x13\xa3\x1dr\xd6)\xaa\xe0\xf50\x98\xbb\x81\xfc\x9d^\x0ci\xa7;O\x03r\x1c$\xc7\xe5>7.\xcfCw\xaf\xe5;\xb0u,r\xd2\x98\xf7?s \x82\x97\x9ez\x86\\/=P'\x16\xf4V\xfab#\x83T\x9a\"M'A\x189i\xb5\x9bkB\xb3W\x8c\x92(\x0c\x12\x94h^\x100f\x96\"\xb9\xee\xc8\x95[\x82\x9eXN\xa3\xa7u\xc6\xaa\x96,\xec\xf8#I\xedt\x9d\x80{\x0fOeJ<\\\x07n\xe8\xac}\x140\xb9]\xe3\xd8d\xf6X\xcf\xfeH\xaa\xce\xcf>1\x9f\x0f\xcd\xcf\x93UY\xef\xbe\x8e\xfc\xc9\xf36\xb78o\xf5?\xd1Zb<\xfd\xe3\x8f\xc2g\\o\xd3\xf5\xed\xf8\xc1\x0d\xb7\x01\xec]2\xca\x18\x05.\x8a\x91;+9\x80\x9b\x7fE\xa0\x93\xbf\xb9\xcd\xa1\x8f\xc75C-\x10\x9a\x91\xa7\x1c\xa8d\x9e\xd1\xef\xf7\xd1q\x9a\xe1\xf6\x9dT\x1aW\xa9\x85\x9dEThY\xc5t\xa2\x038\xad|g\xc9\xedg\x90\xdc>\x1c%\xf0h<_\xe8\xfd\x89\xe2\xbd'\x15\x89\x9a\xd6\x14\xa9\xf3\xe7h\x13}\xd8qd\xcc\x0d\xddy\x82d\xec\xce\x95\n1'T\xba:N\xd3\x8b\xc5BxbN\xb8\xd3\xaaeSW\xf3\x1b\x0e\xed|\xe4+\x0e\xdd\x93G!\xa9\x0ej6gl\x9b\xfd\xfa\x96\xb7TP\x15F1w\xa6\x0b\xee\xfb\xcc\x95\xef<\xa2)69\xb3\x9f\xca=\xce\xecwx\xe7\x93{\x98C\xab\xe0c\xb5\x8fV(H\n\xf1\xb3\xa0\x83z@\xfd\xa24\x06\xd5/\x89ae;\xd6\x8er\xcd\x15'\x18\x1at\xf3\x96\x86\x16\xban\xb1\xdc\xcf\xba\xddAr.y\xe5-W\xc5{\xc0\x9d\xd0\x05\xd6~2\xf4\xdf\xbb\xbe\xe7\xc4a\xfe\x80|iN\xe9!\xbb\xeaHN_g\xce\xe8\x0c\xd8\x13\xd6Y\x1f\xc8\xdcQ+\xd7y\x89\xf8\xc4S\xee)\xe5\xca\x138tJZj\xe8\x8ezc\x138\xed@n2\xf2\xc6&\x0d\xf8\xd1K=\x8c\xbd\xb5\xdf\xf9\x82\xe6g\xc4\x84/\xe9\x97L\xc4P\xb6\xd9\xd4\xeb\xc5\xed\x90\xdb\xdb+r \xc4+\x88\x88eT\x8f\\\xf3\x9bE6\x83\xdaG \x8ej\x83\xa7\x95\x98s\x1a\x96\xe0P\x13\x07\x93\x8bX'n\x9e\xbe^8i\xa7XQ\xba\xbf+\x1dLzr\x13\xbe\xe7\x92\xa7\x1a-\xb5\xe2\xb8\xb5U,,N\x88D[\x94T/`\xeat\x93a\xd6\xcb\xcf\xe6T\xa0\xe0\x85\xb9\xd5l\xd2\xf8p\xe5\xb3\xe5\x89J\xe2x\x7fq\xd1\"\x9bW\x9a1\xc1x\x8e\xa37\x91\xed\xbc_'\xa9\xb7\xd8W\xe3L\x8d}\xaa7\xfei\xce\xd0\xa2\xf4\xfaQ\xdbH.\xa6,3uD\x8f\xd1\x81\x1e\x03'\xf2,\xfdEs\x18\xb5\xce\xd9\x95\x8c\xa5\xa7O\xf3\x13\xa6g\xc2\x13\xa8T\xb1\xc0\x1fO\xe8\x11\x12-\xcc\xd1\"\x8c\x91 aI\xb5\x93\x8e\x9a\x88Dm5\xdb\x11G\xc8\xb5\xbcG\x01\x07r\xeb \xec<\x0e\xd3\xfc\x87\x8e\x91t\xbc`\xe1\x05^\x8a:\xd94n\xc7g\xc4%\xcf\xc9\xf1\x14\xcd{\x12\xb8\x04x\xb1\xf7i\x9d\x15\xff/\x0e\xbe\xe6\xf3b\x1aF\xe5\x9e\x039;\x0c\xd8{\xb1y\xa6\xa9\xf6\xf3S.\xa0\xff\xfb\xbf*\xf2\x07\xb4_\xc4\xb6\x8f\x92N\xd5\xb0C\x1a\x02\xf7\xa0\xf3R\xf4\xa3\x91\xae\xe3\x80t\x1a\xea\xf9\xbf\xff\xfd_\xcf\xccO\x14\xec\xe7&\xa5N\x93W\xc3\x9c\x02I7\xfb%\x0eq\xa2\xd9\x8e\x83\xa2\xb4\xda\xac)\x87dj\xf3g\x19#\x14<\x85g~\xf5\x83\xe0ED,\xdd!\xf2!K\xcc\xb1\x17<\xa0\xf8`\xe9/\x9a\x17\x86P\xba\x15 H1\xcbc\xb5\x9d\x95y8\xba\xab\xda\xdd \xcc\x93 u\xb8\xe1\x05\xdc\x92\xb2\x06\x9d\x81O\xcf3\xa7\x83\xce\xfaU\xb7\xba\x8b\xea\xeb\xdf$\xc7\xcf6(N\xbc0\xd0\xa2\xd8^\xfa\xf6\x81\xdc\xaa\xa8\x83K\xe4\xb3\xe9?\x9a\xea\x8f?|\x94$\xf6\x12==\x82:u\xde#\xe5&\x06\xfcn\x0f\xf9@\xd8\xcc\\\xa0E>q\xd8\xb4\xcb\xc5\xf4\x82\xc6\xfe\xdd\xf56\xc4\x8bE-\xcbY)\x9dmTb\xde\xc9\x171Mt\\m\x97\xba(\xfbS\x8b\xdb\x8fv\x9d~\x11\xf6\xb2\x8bN\xba\x9ay\x1a\xb4\x9d\xb5&\xaf'\xf5\xc8\x83\x9a\xec\x19A\x93?6h&\xfcH\xbc\x8c\xed\xbd|\x05\x9as\x89\xec\x18\x05\xe9s_e8a\n\x9d\xa7A\xf6WK|\xd1\xc5\xad~\xa9\x19\x8e\xee\x9f\xae\x97\xd8s\x8c\xdc\x7fU\xef\x9b\x08\xc2\xcc\xe5p\xb8En=[uM\x8e\x90y?\x00s\xb9\xc9b\x9aer\xd7\x9fx\x04\xdf&\xc7\x0e\x1c\x84\xd9Sa\x8b\x81> \x97_e\x01i\x12\xb9\n\x0b\x0e|u\xf6:]\x85\xb1\xf7\x88\xe8\xeb\xd8\x13z\xb4\xab\xb8T\x07=\xe5\xa7?y\xe1$\xf5\x16\x89\x86\x05\x0e\xed4\xff\xb6\x0cm>p/\x9e\xa1\xdf,\x0f\x0b\x0fc\xf8\xc8e\x86-w\xaa\x80\xfe\xd9\x1f\x8fu\xd4\x03\x92[T9\xc7Q\xcb\xb8D\xa7\x0d\x9f\xe4\x8aZ\xc0\xb8\xe8\xff\xc7\x0fN4\x83r\x1f\xbcxU\x15\xd7\xb13\xadv\xb8\x03\xe2\x0c\x07l\x0b\x18\xe4\xa4\xf9_F\xdd\x95Y\xec\"\xf3\x98\xb5\x83\xb9\x18P\x0e\x0e\xca\xa2\xd3\\3\x0f\x95s\xce}\x98\xb8\xf7Y\xf6B~w\x8ef\xcc\xa8V\x06-\x0f\x80\x13}E\xcf\xfe\xb4\x89-\xbc\xf5\x0bO*\x05\xeb\xa1\x9e\xfd\xa1X\xcf\xd7i\x1a\x06\xec\xdb}\xc2u\x9a\x0d.\xbc\x02\x0bx\xd7\x0b66\xf6\xdc\x03\xbfVIV\xf6\x03\xeat\xfbI\xc7\x98\xc0O\xdb\x0e\x03\xffu\x81\xb83Fe\xd0{\xc4\xc4\x9b\xa7\x18\xac\xea\x1e:\x7f\xbc\xa7\xcc\xd9\xca\x13\xbb\x8ba\xf6\xa7\xb3\x8e\xf1\x8f\xae\x9d\xda\xe7\x9eo/\xd1\xcbd\xb3\xfcy\xe7\xe3\xc9\xdcN\xd0\xa0\x7f\xf6\xdb\xaf7\xbdo\xfb\x8b\xfe\xfc\xcbn\xed<\xea\x9e\xfd\xeb\x9d\xee\\\x86\x9bw\xa6k\xba{\xcb\x9c\xed\xad\x8d\xe3;\x9b\xd9\xfdt;{5~t}\xc7\xbb\xfe\xf5[\xf4\xedw\xf7\xd5\xdc\\\x8e\xaf\xef\xa7\xcb\xd9\xab\xe9\xbe\xf8{\xfd\xf3\xf5\xab\xe9\xf2\xfar\xb7\xfd\xfa\xfb]x\xfd\xe6v|\xfd\xa0\xeff\xfb\xbe>\xfb\xb8\\\xde\xec\xfb\xfd\x9b\x8f\xf8\xfe\xdd\xfd\xb59\xfb\xa0\xafg\xf7_\xfb\xef\xee\x9d\xed\xfb\xfa\xe7\x07\xf3\xfd\xab\xe9\xf6\xfaU\x7f\x7f\xb3\xef\xefo\xee\x97\xeb\xd9\xbd\xb3\xcf0\xb3\x0f\xf9s\xeb\xe6\x1e'\xef>\xce\xd6\xef?N\xfb\xd7\x97\xb3\xf5\xfb\xcb\x9b\xfbw\x1fj|\x9aa\x9b\x9f\x1f\xcc\xf7\x1f\xa6\xdb\xf9+\xfd\xf1\xdd\xfd\xc3\xf6}\xfe\xdf\xe5\xe3\xd7}V\x9f\x93\xbe\xbb\xbf\xee\xdd\xd4?\x17u\xbc\xfb\x90\xd5\xf1\x90=\xdb\xe5|\xef\x97\xeb\x9b\xc7\xa9U\xfd\xfc\xfe\xa3\xd3\xbf\xbe\xbc\x98\xcd>N\x97\xb3\x8f\xaf\x93\xb2m\xe9l\xdf\xdf\xdd\\\xbe\x1e\\{\xa3\x9f\x7f+\xf4\xf4\xf3O\x9d<\xaf[\x9c\xfc*b\xceN\x10j1\x8a\x90\x9d\x92\xf3ZqS\x9f{#\x84<\xa3\xd9SK|f0\x95(\xa8Y\xb9G\x11\xb2\xe3,Z(F\xa4\xfcEm\xecC\xe6w\xc0\xdd\xff\xe9\xafq\xeaE\x18\xfd\xabJ\xfeZ\xd4\xc15\x0b\xf4V\x80\xd1\x9f\xde]\xe9\xbd\x07.\x89\xd8\xcbg\xd8\xa3\xee\x94 8\x19#\x9d\xbd\xe0\xa5\x94\xdd}\xea\x99\xa4\xfch\xe1?\xb3%\xf5/\xc8\xb7=\xfc\xaf3A\xe9\xc2\xc3HX\x18\xd9I\xb2\x0dcW\x08H\x90\x1d;+aq\xb6\x1e\xa3\x0b\xb3'v\x8clRE:\x91l\xa2\x1dh\xc4\x0c\x8f\xc4\x86\xa1;\xce\xfe\xb4\x0d\x8f\x8b\x85\x9a\x15\xff\xf3\xd5\xd5\xbct&\xdf\x8a\x91\x1b\xbb\xeaO\xd2V\xb4\x81\xea\xd6\xb4\x01\xcbV\xb5\xc1\xf2\xd6\x81\xa0\xaa\x95\x7f\xca0\x00d\x8ar6\x07C\x7fq6\xd6_\x00Y\xb6:\xa5k\xba?jF\xb4\xcbF]0\xe5K\x96\xff\xbb\xa7\xbf8\x1b\xb5\xf2\xeb\xc9\xd9U\xc5\xff6\xf5\x17g\x96\xfe\xe2l\xd8\xcaQ\xeb\xb7HX\x95\xff\xbb\xaf\xbf8\x1b\xb4\xf2kaWs#3k\xff\xab\xd1g\xd1(8\x1403\x07y|\xbc\xd9\x9a\xeaQ\xb7\xe8\xf9\xd5\x137l\x92\x01u\xcb\xbb(\x8e:-\x00\xccMUK\x8aw|\x1d\xf8\xd0\x17\xb8\x1fU\x0f\x11\xce:\xe6\x0f%\x13[r\xe4d\xc2\x9c\xd5\x88QN\"P\xc0\xb3\x9f\xd9rV\xc8y\x98\x87\xbb\x03\x19\xf5\x97+Y`mD\xeez\x08\x1eW*\xd5\xb3?peOx\xfd\x86\x80aD\x1dD\xef\xeb:\xf1\xd1\x8d\xc2\x0e\xe4y\xb9J\xf3,HU\x8bP\xba\xae\x16\x85\x98L\xaag\xff\xaa\x9b\xca/\xa5\xa5t?\xe7\x8a\xfa{\xb7xC\x8f\xf0\x8dJt.K#\xf7\xcb\xf27/Tn7 \xcf\x91\x8f\xca\xedn2\x0ef\xcf|\xd0[Q\x8c\xff\xa1Q\xf6G\xf4\xb2$=_\x02T i!\x97\x08\"\xde\xf1\x90\xf7\x83\xfa\xa7\x13U\xd7\xfe\xca_\x85WFKk;\xcf\x7fB.e0^Y\xf9\x1a\xf8/\xc0\"\xd8Y\xd9q\x82\xd2_\xd6\xe9B\x1b\x9d\xbd0_%\x9be'\xb7\xe0/?\x18\xfa\x0f\x9d\xc2\x82\xbf\xfc0\xfa\xa1\xb3\xf1\xd0\xf6\"\xdc\xfd\xf2\x83\xd9\x19v\x0c\xbd3\xfa\xa1\xb3\xf3q\x90\xfc\xf2\xc3*M\xa3\xf3\x97/\xb7\xdbmwkv\xc3x\xf9\xb2\xa7\xebzV\xc7\x0f/\xcc\xab\x17\xe6\xab\xc8NW\x9d\x85\x87\xf1/?\xbc\xe8\x99}\xa3?\xec_\xfd\x90?\xd0\xe25F\xbf\xfc\x806(\x08]\xf7\x87\x8e\xfb\xcb\x0f\xb3A\xd74\xcd\x8ea\xbd3;\x86\xd1\x1d\x0c\x86\xd8\xc8\x9eh\xd9\xbf\xfdN\xaf\xd3{W<\xce\xc40;\xa3\xac\xec\xf1\x87\x97EMY\xa5/\xcc\xab\xbf\xfc\xd4\xb1\xf4\x17\xcdZ\x93\xd6\xa8\xeb\xd98\\j\xeb\x1d\xf35\x9d \xf9\xa2U\xea\x1e\x8b^\x1dV\xaa^\x03,`\xd8\xe9f\xbaw\xe30\x02\xb8K\x19\x8an\xc1\x8c~\x12V\xe5\x87\xae\x8d\xa9z\xea-m\xae!\xd4\xfe63)\x16\xbf\x9a\xe5\xdcP\x7f\xf3\xc3\xe2\x86\xe2\x937\xf8\xf9\x05JuY\xafm\x81\"\xc8\x07\xe8\xd1\xaeS\x9c\x9c\x92\xbe\x04Z\x8ckUj\xb5\xb1&;\x06g\xf5\xc90\x82O*J\xd8\xd2\x17U\x80{6U\x9e\x9c\x9fk\x95V\xb8\xd2\xba\xe9K>#f\x81=h\x16\xd8O8\x9a\x04\xd5\xff\x94\xd7\xce\xd5\xb1J\xaf8/':*[:\x16\xe96'\x9d\xffQmM\xa7\xeb\xe00AZ\xfe\xf8\x88\x94\xfc\xf3e\x9bd\xc2\xad\xc8\x0f\x83\xf7\xd8c?\x03\xf2\x0d^\x8d\xe8\\\x1eN\xb4Ir\x82[\xf8\xa1+O\xef\x98\xfa\x91g\xea\x85\xb5t\xba\xc4}\xd9$\xb2\x99\x1b\x11<&u\xabc\xb9\xb6\x9e\xfd\x11\x9d\xcc\xe5(\xff\x9e\xba\xcc\x8dK\xf5w\x0f\xe5\xcc\xb44\\.1b\x8fh\xc1\x81\xd7@\x14x\x95\xa6\xccF\xa9N\xd7D\xbe\xc2\xebo\xb8\xe1]\xf8*`u\xe4\xa9\x08\xe8C\x0e$\x03~**\xcf\xf1\x8cu\x17-\x81\xf3=\xe5s\x8eN\x0bc/\xcf\xa6\xe9/\xb2(a\"*\x10\x1b\xaa\xeb\x84\x18\xdbQ\x82\\\xf1\xa9#\x81P\xf9c1\xe7\xf2\xac\x1et\x02\x8d\xdd\xc0\x12\\\xa1=*\xd2k\x0f\xe0\xaa`\xb0\xd7o\x82\xc1\xec\xe7:\x1a\xcc\x83\xea~\xa7\xd7'c\xbd,\x8c3\xf4\xce\xe0\xdd\xa8k\x8d;\xc3n\xdf\xe8\x18f\xd7\x18v\x8c\x1e\xd6\xfa]k\xd4\xe9w\xad\xf1;C\xef\x18#<\xd0\x06m\xf1\x1b\xb7W\x90\x05/\x90\x16\xef\xd7~\xa4\xa5a\xfe60`\xe1\";\x01\xc43\x10\xbfz\x8a:;\xa8u\xfb\\g\x03-\\\xdc\x87\x97\x1f\xe3$\xa0\xd5\xbb\xa5\x8aG+/H\x0f\xc4!\xbb\xfcG\xf6cc\x04T \xab\xd1\x1d!\x7f\xc2\x9f\xe3\xab\x86\xff\xae\x81\xfcN~\x14\x08\xf8\x1eo9<\xaa\x04od\xb85\x84\x1c\x9e\xb8D\x95\xad\xfb\x99\xc3F\xe5\xc9\xb2\x02\x9a\xd4W0ub\xf2\x97\xbdR\x9a\x97M\xc2\xbdz\xc1)1{\xeb\xfc\x0b\x0f`\x9a,\x96b\"7Qh\"\x7f\xef5\xcd\x9e \xd1\x9e\xe5-\x86'\x85Ap\xb2\xe8Y\xdf\x13.\x0f\"\x06:w\xbc\x86S\xd5\x13_\xa3\x0d\xf0;\xe9\xcd\xde\x1c\x9f\xe3\xde_\xce\x92[\xac\x07\x90\xddEo\xdd\xf6\x02\x0e\x0b05\xa8\x0d\x99\xf9\xeaQ\xda\x17*F\xc0e\x97\xfa\x82\xc3Q\x1f\x1c\x02\xde\xc6\xa7>\xd8\xb0\xdf\xeej\x91\xb5\xc5F\xc3\xe3\x98\xd1Q \xf1\xda\x90\xa3\xb8\xe4\xa7\x83\x18&\xad#\x12\xc7\xa6|\x90\x08\x0cLM\x0b\xa3\xfa\nVf\xab\xe6\x15;\x96B\x85\xf3pw\x90\x1e\xdai`T\xc2\x19\x8ca\x95\xcd\xcc\xbe\xcc\xa7\xae\xe4\x08\xb7\xe6Ni\xd5L\xba\xd0\x0b\x87,\xf1\xa4\xce\xf4Ty\xcf\xb4\xf4\xec\x0f\xc4\xac\xa9U\xdb\xdaq\xe0\x05K\x903\xb7|\xab^\xdcR\xddn\x17\x1fV\xe4_Q\x97\x8du\x7f\xcf\xfe)\xa7\xe5\xee<\xb6\x1d\xa4\xe5\xabZjF\x84\xceBEq\x18i\x81\xed\xb3\x87\xb8\xa9\x15I#\x1d@\x9c\xfbx\xa5\x18\xcb\x06\x10(X\xfb\xb2\x0b\x8f9(\x0b\xb1\xed\xf4 \x9e4\xba \x8a7(\x16\\\x1f{\xb6\x0bYd%\xa2\xebW\xf47f@\x06\x9dU\xbf[\x9d%\xaf\xee\x1e\x94\x01E\x8fUcE\x92\xdas\x8c:i\xf55\x16So\x01\xba\"\x9b\xd5\xd2eQ \xf8\x85\xdb u\x1f\x82H\x82i\xc4\x9dNy\xe5\xf0\xeb\xfaKWik\xa3\xdb\xe1^\x0eE\x1c|\x87I\xbbN\xe8G\xeb\xack\xadc\\\x0f\xcd\xfc\x91~\x10_\x1cC\x07\xf5E\x9c\xaa\x9d\x88&l\xce\xf5\x978\x9c\xdbX+\xea\xfa\x8f\xbe%*\x90\xb4\xd6S9\x00\x92g\x9c{\xd50$~=S\xf5\xaa/\xc0\xdd\xcb1C\xe0\xed\xb9\x03@/\xc3\xa12nZ\xb5>?\xaf~\xe0\x99\x94\xc3]\x9a\x9fLJ\xe3\xac?\xd4\xbcX\xafg?\xd6,`\xc0\xf8tu\"\xa5O\xbe\xe2\xab\xd8\x84\x82ZU\xde\xefN2IZ\x12dp\xa7|j\xda\xac\xec\\\x80B\xaa7\xb7)\xe9E\xa2\x91fl\xe9Q{\x0f\x03\xe2\xe6 \xf0V\x9f\x92m\xfe\xea\xc6\x9c\xed\x99\xact\xd5vz\x8cI%\x13\xd7b\xf2c\xf2\x8a\xeb\xb7\x9e\xda\xa9Bf\xae\xaa\xbe\x8c\x93\xb0/\x93\xe0\xce\x02\xc1\x1f\xd52\xf9\x17>Ix\xd2\x97\xcdJ\x86B\xfa?\xfe\xc8grI\xc4\xd1\xd7O\x99\x14\x99\n\xba1\xfa\xef\xb5\x17W\xaf\xc7\x11\x0d\x12\"*\xf86+\x1c\xe0i\x03\xfasCM\xca\xac\xe2\xf6\x97R\xf0\xf2e\xd0V1\n\x0e\xd8o\xae6\xb2\xa0]\x8a\x82\xc4\x0b\x99l2\x81\xf0\x14^\x9csLW\xe5?\xccBT&|m\xfe\x13+\x8d\x91+V\x81\x1f\xa5\xfb?66^\xa3?\xf8\xc4\xb5ID\x03\xe5\xda\x91\x8b\x0e\xb8\x17\x0cJ\xb9\x97\x93=\x15L\x0e\x8f\xe2\xd0\xad\xee%5\xc1<\xffjH\x8c\x80\xab\xee\xfc\xa6^\x1aFs\x9b\xfeb\x0dpE\xa7|s\x0eDZ\xfd\x17~\xcd`\x89\xb1O\xdb%{r\xbe\x07\x14\x98:U\x95\xe7\x06\xd9!U%WB\x8eb\xf9^3\xbbIR\x1c\xb9\x90\xaf_\xd8cD\x95\x84E\xca\x06\xd8\xcc\xe2#\xd1\xca\n\xf5+J\xd61\xae_\xd3\xf7d\xad\xe7m5\x9b\xd6\x9b\x93\xea \x01\xca/r\xa2\xc0e\xaevfO\xd8{\x9dy)\n\\\xf56\xb4\xcc$\xa5\x86\xf8seV\x7f\xb8\x80\xbeJV]h\x12\xdf*\x91\x8b\xd3-f!\xed\xf4\xb3WOw\xeb 8\x99\x0e\xa8\xe3p\xa76\xa9\xbcgG\xcf\x9aJ\x1d\x82\xf6\xd2<\xc0\x92\xbf\x19\xf2\x18\xa1\x8a\xa9\x9f\x93\xa3\xd7\xc8\xd1\x9b\x94\xff!\x94#t\x0b\xea\x04$\xb0\xee(\xcf\x0dR\xbf\x1f#<\xf5\xb4\xbc\xd5$\x89D\xc88\xae_\x1e\xf2\x90\x9c\xe1$\xae\xd5Q\x8b\xa8\xb2qG\x0e:^\xb0\x08\xeb;\x1d\xc0K(\xb3\xf2\xce*\xbf\xee\xd7\xf5m/`\x97urt\x87=\xc4\n\xc0\xb1w\xc6?\x8c\x80g\xc5z\x89\xe0w\xda+\x0f\x0b\x19\x0d\xa0\x02\xf6\xf3\xc8\xc5C\x13z\xd8\x87\x1eZ\xc7\xbf9\xa0\xa0,\xdenU\xad\x8f\x8b\xdbb\xea\xe9C\xdd:\xf2\xa4.\xf4\xee\xf7\\\x0e\x9b\xd5\xeeQ\x1b\x11-\xb6\x80\xae\xc9\x16\xb5\xd2\xef\xbc3\x16\x83\xb1\x03xay7\x9f\xdc\x9f\x02\x98u\xe7v\x824\xe0\xe80\xa9\x0b\x93:\xdbZ\xcf#G)Qh\xcc.\x9bF5\x07O{w/\xc1\x95\xff2\xaad\xc1`\xb5\x1c\xae(\xd6\xef\xe4\xcb\x9d{\xc5\xc0\xc2.\x8d\x93u\xc4\x1dd\xb5\x86\xcc\x01\xb7\xa1;\xea\x8f!\xf3\x92\x92\xe7\xaf\xdbST\x057T\xd9\xebt\xa5\xcd\xd3\xe0i\x01\x0e\xbd6\x7f\x8e\x17U\xc8\xa5,\xeeK\xbba\x80\x0e\xf2\x14rN\xf8\xa4\xa6)M\xd4\xcf\x1a\xbb\x912w\x88\xd7\x040)\xd0&4\xd1\x9a\x97\xe3\x01\x9c\xc0\xe4\xa1\xc1\xdeo(\xd2\x89-\xa7\xe6d\xdc\xe1M)a\x1dl8E3#v\xcd\xcbc\xffV\xb4\x13\x1d\xb7bH\xeb\x8f\x8e\xf3\xc1\xbe\x94\xae\xf5&\x9a\x84\xa0\x08\xa3\xd9\x1b\x90R)Q\x1c\x87q\xc2\x0e\xa8\xd4\x06\x18?Y=y0M\x9c0BIg\xd5{\xfa\x94\x9f\xb3\xd2\\\xb4\x90\x1f\x8b(\x1b\xaa1V\xe9\xc1\x0eXu$\xe2\x92\x9acc\xf4)b^\x80E>\xe5C\xd2\xea\xfaZ\xebd/\xf9&\x15-v\xf9;\xdb\nx\xd3\x0b$e\x8fl\x08\xdf=\x7f\x92]\x05U&\xc4\x8b\x9f\xc0M/\x86\xae\x882\x9f>P\x9e\xb4\x06S\x90\x8c\xd6a\x8f\xba\xac\xa44P+\xb99t\xc7\xb1\xf0\xb7\x03x9\xad\xbc\x971\x02\xeej\x8c~\x9a4\xaf\xc6\x02\xdfAV\x00\x0d\x9e\xd6hH\x0d\xfav\xe0\xff\xb4,\x94\x9d\xee\xf2kaq\xb7\no\x9aTZ\xe5\x1d\xf9J\xef\xff\xbc\xfc\xdb_;I\xb8\x8e\x1d4\xb3\xa3\xc8\x0b\x96\x9f\xee\xde\xfd\xd20\xea:I\xd2\xf5\xed\xe8o/\xff\x7f\x01\x00\x00\xff\xffPK\x07\x08_;\x94/\xe8Y\x00\x00\xa8X\x02\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00 \x00swagger.yamlUT\x05\x00\x01\x80Cm8\xec\xbd]w\xdc6\x927~\xefOQ\xab\x8b\x91<\xa3\xb4bgf\xf6<\xda\xf5\x9eu\xec8\xd1\xacck\xfd\xb2\xfb\x99\xe3\x1a=9CYF\x9a\xb2\xe6\x0f\x00\xacp-\xfe\x01@\x9b\xcd\x06U\xbbsx.\x9f\x80\n\xd7MUR@E\x01\xf5\x1a\x03\xfe\x9c\xd3:/W\xa0x\xc87{b\x1f\xbf\xcf\xcb\x0c\x9f\x83h\xfb+\xba\xb8\x81\xafg\x7f\xfc\xe6X>K\xb6\xb8\xe2\x82_,\xf6-\xc9\xdf*L\xb7\xa4\xa4\x98*\x91\x00\x8e\x9f~\xfd\xf5\xf1\xfe\x7f\x07m=\x07\xdad\x19\xa6t\xd9\x14\xed\xdb\xb3\xce\xd34[\xe3\x0d\xea\xbe\x0fP\xef\xb6\xf8\x1c\xc8\xfc\x7fpV\xf7~\xd8VL\xb8:\xef\xb6/\xa8\xaf\xb3. f\xa8\xaa\xd0\xee\xe0\xb7\xbc\xc6\x1b\xcd+\x16 \x04\x99\xe5\xd8\xbf~\xd5T\x85\xfeW\xc5\x9e\xd6U^\xae\x0c\x8f\xf4\xb4\xf8o_\x19\x9e\x02x\x0e\x1f\xdf\xbd>\xab0%M\x95a(\xd1\x06C\xbdF54e\xfes\x83\x8b\x1d\xe4\x0b\\\xd6\xf92\xc7\x94\x0f\x11\xd66\x90\xa5\x91!{\x86\xe2*GE\xfe7\xbcxd|n[\x91\x9ad\xa4\x80y\xb3\\\xe2\n6\x98R\xb4\xc23\xf8\xb0\xce\xa9\xec\x1bl\x1aZCF\xca\x9aM\x0c\xa4S\xa5\xa0\x02#Z\x9b\xdb\"%\x86\xa3\xb3#\xc8\xd6\xa8BY\x8d+\xd6\n\x86\x02\xd1\x1a(^mpY\x03Yr\xd1?\xbe{}L\x81M0#7.T\x85\xb7\x15\xa6\xb8\xb4\xb4\xca\xd8-\x9b\xa2\xd8\xc1\xcf\x0d*\x98\x06\x17B\xbf\xb2)\xae\xc9\x13D!/\xcdL\xae\x99(g+BV\x05\x9eq\x9d\xcd\x9b\xe5\xece#\xa6\xd8\xf5c\xd1\x13\xce\x96\xaeIS,`\x8e\x81\x9b\x1a=!\xc8PI\xca\x15\xff\xa5\xd7\xa7@*(\x89\xfc\xf5\x94\x8f\xc6\x0c\x95@\xf8\xecd\x1a13\xc454[@\xbc\xef\x96vqu\x8b+\xa1\x9a\x0d\xdaR1\xb4\xb8\xe45Q3\x0b\x16x\x99\x979k\x93\x02\xa2FfKR\x14\xe4\x13=\xb7|\xdb\xdf\xc3\xc5r\xdf#6,\xb6\x15\xb9\xcd\x17x\xd1v\x9a\xfd\x11Q\xdal\xf0bfc\xf4\xbc\x84\x1f>|\xb8\x84\xef\xbf\xfb\x00\xa4TSP\xcc\xb1]\x8e\x8b\x05 \xe3\xdb\x7f\x1dN\x8b\x0f\xbb-\xfe\xe9\xaf?\x19_\x00\xb8EE\xc3\xc7\x83\x18o|\n\xa0\x9a\x7f\xa1mE\x16M\x86\x01\x95\x80\xab\x8aT3\x9b\xd4\xfb\xe5\x99\x02\xaa0\x1b\x9f\xe4\x13^0ug(c\xb6\x85\x90\x9bf\xcb\x96\xac\xa6\xa8)\xcc\x11\xc5\x0b\x8b}\xe2\xe3\xca\xf43\x1f\x84\\\xc65\xba\xe5Cp\xd3\x99C\x0b1\x89\x90\xea\x12\xfb\xf7-\xc9\x17\x80J\xf3\xc0\x02) 7\x1f\x15^\x92\n\x9f*\x06\x8c/\xaa\xf3y^\xe4\xf5\x0eJ\x8c\x17|\x18\xcd1p\x93W\xddZz\xc2\xfb\x92\xadQ\xb9\xc2\xfc%>ggp\xf2\x91b\xe5\xc50-\xb1\xe1\xc9l\x96\x18\x9f\xa8D+[\xef\xe7\x15F7\xcc\x06I\xc6\xb3\xc7\xe6\x11\xf5\x86\xd4\xf8\x1cj\xb6\x86,\x9b2\x133\x8c\xf5C\xda\xae\xac\xa9*\\\xd6\xc5\x0e\xd0-\xca\x0b4/\xac\xe6\x92\x8dG\xb2\\\xe6Y\x8e\n\xc7Z6o\x96Pa\xb6\x12\xe1S@\xe5\x82\xd9\x1f\xd9hC\xf1\x82\x0d\xb5\xfd\xbc4\xb2\x9a\xe3U^\x96\xac\xb3\x9f\xf2zmY\\v[<\x13\xe3\x1fms:\xcb\xc8\xc6f\x8d\xdf\xf3\x99J\x81\xd4ka(\xca\xa1\x95\x82\x13&\x1f\xf3\x017\xdbz'\xa7\xf6c\xf3\"\x98\xaf\xd65\xcc-F\x89w\x9au\x02\xf2\xcd\xb6\xc0l\x91\xe5\x13\x06\xe8\x16g\xf92\xcf\x80\xe2\x0d*\xeb<\xa3\xfa\xa9\xc6\xe7\xea\x08\x17H\xcc\xeds\x98\xefj\xd3\xe8\xf2\xf5\x92~d\xe6h\x8e\x011\xa1\xf2E\xc7\xc19\xf0c\xe4\xe2\x8e\xe6\xe4\xd6<\xa6\xa5\n\xe4T\xd0u\xdfG\xb2\xeb\xe7\xe5\xeeZ\xb9G\x94\x19.T\xcd\xf3\xbab\x93\xd8,\xa1\x96\x95Z#PA\xe4\xd0\x03\xa4\xff\xb4\xcc:\xf3\x85FH8\xef\xbb\x85\x03\xf7\xaf\xf5\xea\x0cC\xf3RM\x9c\"\x9fs\xb1\xe5:B\x816\xdb-\xa9\xf8\n\xbeE\xd9\xcdYS\xb2\xff\xb0u[\x8c\x0b\xfd\x0c\x92\x0b\xbd\xd9\xb1!Khja\xd8\x94y\xa0\xcc\xb0\xa2\xc5\"\x17\xb6\x02V\xb8d\x1b\x18.|\xbd&\x0b*\xbb\xa5\xe5\xc7\xe4\x11\x9fP\xdf\xdew\x9f\x11\x1b\xfc\xf0\xe4\x1c.\x99\xfc\xcc.\xc8\xae\xa0V\xe9y /\xfe\xf0\x07\xcb2\xf9\x8a\x10X\x12\x02\xcf`6\x9b\xfd\x8b\xf11&\x0c*w\xe6\x07P\xb9\x9b11^Uds\xb2$\xe4\xb1\xf9\xd1\xd9\xcc\xbc\xfe\xe5K8a\xac>\xf2\x8e| '\xbfc\xbc\x1e\xc3/\x16\x1bn\xe3\xf7\xab]wO\x1d\xba\xfb\x0b\xbaE\x93)\x0f\x9eq\xdf\x90\xb52\x81\x86rz\xf2\x8a\x90YV J\x1d\n\x12\"\xb2\x97D\x1f;/\x9ae0h\xaeU\xdd7\x0e\xd5]\xee\xea5)-\xca\x13R\xbd\"\xe4d6\x9b\x99W\x83Vq'\xd6g\xf8\xe0\xe3j\x8d\xd5*cr!\x94\xfa\xf2\xbb\xf7/\xde]\\~x\xfb\xee\xb1i\x91\x00\xd9\xac\x18\xa8\xf6\x86E\xd3vu\xfe\xd1\xa1\xce\xef\x89Y\x93\\\x95\xe7\xcf\xe0w\xdb\xf9\xec\x15!\xbf\xccf\xb3_\xcd\x0f\xa3rw\xca\xdcP\xf6\xc6V8Q?\xa2\x8a\xaeQ\xc1\x94l\xef\x88M\x85C),\"\xe4\xcb\x81\x00\x1f\xcb\xcd^\x04. \x9f \xfc\xa9\x7fz\x06e^X\x07\xb8].\xc3Hf\x9b[\xaege\x8b\xd5F\x03\xe6\xbb\xbd\xdb\xa5V\x8fOyQ\xc0\\\xef\xf5.\xf0\x125\x05\xf7\xc5\xf4M\x1dk\\\xaa3\xb6\x7f\x9f\xf1\x1f\x98\xbbz\x0c\xa8\xb3\xda\xb1\x95\x90\x8d\x04\xd3\xda F\x88\xbe\xb1vi)\x8b\x9d\xdaW\x1e\x04\x0bZ7\x19\xd0\xb2\xe6n\x9b\xbe!\x1e\xc78>;\xd67%\xd7D%2\xdf\xed\x02\x96#\xfahI\xc8l\x8e*\xde\xd9\xcfg\xbb\xd9\xdf\x8e\x84\x16\xf9\xdeK\xcb\xcf\xbc\x15\xe5\xa2\x1e1\x1el9\xd4>\xf2\x97\xf7o\xdf\xe8\x7fy\xf6\xec\xd93\xf3\x18`\xef\xedc.\xc2\x8f$\xcc\x1cH'H\xec\xeb\x1a*\xbd\x91\n\xaf\x9a\x02Uz~\x87l\xd8+\x0b\xbcw[N\x01o\xe6x\xb1\xd8;0\xa7\xc2\x1d\xd7\xb1C\x86\xe8M\xc7\xa5X\xf2\x8d\xec\xf5\xbf3\xd5]\xcb`B\xeb\xb6u?\x8e~\x82H\xf3sn\xd9\x80\xa0\xec\x86\xd9\xa0\xfd\x86x\x99\x17\xd8\xbcn(\x9bu\x89+JJ\xeb\xb4\x95\x91\xb8e^\xd1\xfa\x8a\x7f\xe1g\xf0\xc4\xcc\xb9}\x81\x0dJ\xf5\xfc\xd3\xf0\x15\x0c\xc0*\xd5\x11\xd7\xe5\xd19\x1c\xe9fm_\x0d3\xd1\xcb\xa3S\x1b?\xde\xbf7h\xc3x\xfe\xab\xe8\xc2\xbfY_`\xfd\x1b<\x1f\xda\xc9\x8b\xa5\xdcp\xf5\xc7\x9a\x18\x0d9\x85O\xb8(\xbe\xba)\xc9\xa7\x92\xdb\x995\xa2\x80 khM6\x81\x93\xab?\xe4O\x85\x03?\x98\x07\xc2xv\xc4a\x03\xd8\xb0\xb9BbH\xeb\x1b\xbb\xe6\x93Q\x8d\xf35)\x16b\x90\x0b\xc9\xc5T\xce\xcbv~\x80\x88\x00\xeaY\x89)\xa3o\x87\x8b0k\x17\xe7\x13f\xd7\x94\n\x0fBC*b\xfa\xd3_\x7fzl\x99HS\x8c\xb9~\x83\xf6a\xc7U\xc5X>\x99=}\xf2\x94\x1eY\x86\xd0\xf0/\x12iR\xc8\x05\x0fC\xd90\x1cE[\xb4\xcaK.\xda\xa1\xdb\xd4\xdbu\xee\x1f\x14\xa1Ci[;\x7f\x96K\x8b\x0e\x97\x11d\xc5A\xec(H\x89?\xd7W7xgFH\xac\xdb\x7f\xe7\xe6\xbf\xd7\xd5\xff5m\xfd\x95\x14l:\xb2\xae\xb2\x7f\xcaX\x18\xa2T\x04\xfc.\xd1\n\xbf\xc3?7\x98\xd63\xf1\xbb\x81\xd9\xcf\x0d\xaev\x9c\x0dc\xcb\x14\x89aCh\x0d\x98G\x98xXj\x06\x17ug*n\xeb\x1d\xe4&\xb0\xa5^\xe3\n\xf3\xef^\x12\xd8\x90\n\xabp\xa3n\x19\xa9I\x8d\x0c\x80\x92\xb72\x9b\xbc\xac\xff\xfcG=\x0f1\x1a\x8d1\x14\xde<\xd7\"\xffG\xd9l\xe6\"X\xa2\"\xa4\x9dp\x9c\xa9\xbf]E\xf3\xa1}\xc5\x99\x99f\xf3'D\x81\xe2\xfa\x14\xf2\x9a\xaa\xc0/\x85\xa6\x14\x83y!ba\x9fr\xda\x1f\x1f\xf6\xc0\xcb\x7f\xb2O\xa8@\xcdwr\xdc\xab\xc1\xa1\xe6\x810\xe0K\x19O\xe3\xaf\x9c=\xd7O\xc6w\x97/\xa4/x\x18A\xd0\xc3\xac\xedC\xd2\xa15C\xa7%4\xa5\x80B\xf0B\x84\xb4\xef\x12A\xe5\x0d\x98\xe0S\xed\xd8\xca\xc8B\x13\xde\x13/\xe4e\x8dW\x9aX\x95\x1a\x89yY\x7f\xf3t\xf0\xab\xb4\xfdA2,p\x8d\xf2\"\xc1\xbe \xf6M\xb0\xaf\xa0\x04\xfbrJ\xb0\xef!%\xd87\xc1\xbe&J\xb0o\x82}9%\xd87\xc1\xbe \xf6M\xb0\xaf\xa0\x04\xfb&\xd87\xc1\xbe \xf65Q\x82}\x13\xec\x9b`\xdf\x04\xfbvh\n\x08.\xc1\xbe\x9c\x12\xec\xfb[\x81}\xb7\xa8B\x1b\\\xe3\xaa\x03\x1d|\xc5-o\x17\xa7\x9d\xdd\xe0\xee*h\x838%\xa6\x89\xa4 \x15\x07\x01yPI\xc2l\x12(j\x01P\x1e\nZ\x0db&\x1c\xd1d\xd3\xde\x05j\xbee\x0b\x1e)\xf9^\x91,\x97\x14\xd7l\xfb\xd5\x17\x17:\xa1l\x8a\xeb\xaeQ\xcc\xcbs\xd1V\xe7o\x15\xfe\xb9\xc9+\xbc8\x87%*z \x9e!H\xa0\x0d\x0ch\x94(\xe43\xe9q\xb0)\x97\x9d\xe1\xaa,\x9b\x0d\xae\xf2L\xfd\x8d\xcf\xb6\x0c\x95\xac?\"*\xb2\xc6\xa5R|S\xb6\x81\xa8\x81\xfby\xc1\xb9\x15\x98\xd2\xbd\nE\xe8\xa6\xa1L\xd578P\x9f}\xf6w\xac\xdc\x01D\xacQo\x91or_\xed\xf2g\x15\xbcjB\x8eE\x90\xb2;\x82%\x18\xdb\x14\x03\xf0R\x84$\xba\x7f\xbaXB\x81\x97\xb5B\xda%\xf4\xae\x9cF\x1e_\x15\x13D4\xc2\xf4<\xdf\x01F\xd9\x1a\xd0v{\x8fZ\xec\xe2\xdf\xfb\xf7m\xba\xec\xbc\xc14\xcaG(\x81\xbaj0\xb0\x7f\xe4\xe5\"\xcfP\x8d[\xa4Ej\x90?(\x07R\x97]^fE\xb3\x18\xb8\x84H\xb4\xd2B]\x83/\xc6\x81\xd3N\x04\x96\x99\xee^:I\x8f\xd9\xc7\x0b:\xf8Z\x83.p/\xba\xc2TB\xdc|z\xed\xe7#\x9br39\x9b\xf2UI\xaaA\xfcZ\xcd\xc6~\x13B3c?\xec\x9c\x90\x02\xa3\xd2\xf6\x01+|\x8b\xab\xde\xab\xb6\x8f'\x9f\x1e~\xb8\xbc\x93?Qa\xfdL\xe8\xf1am\xe0\x92#{\xa4Z\xe0j\x18\xc0r\xe4\x1cL\xa1\x8d\x1a\xadz\x8b\xd8\x7fJn\xc7\xd6\xc3\xecg\xbf\xa0\xc5\xa2\xc2\x94\xfez\xec<\xd7\xbe?\xd6.\xff_\xa2\xfc\x02\x8e\x01\xc2\x9d\x10\xc6K}f\xdd\x91\xf5G\xaao\x0f\xfb\xc4\xba)s!*\xe7\xaa\xb6& 8c\xef>\xc1\xeb\xc9S\x0f\xfc\x12\x0fb\xd2\x0e\xec\xe9\x05Q\xc9\x05\xbc \x03Cgj\xc1\x04\x89\x05\x91i\x05F0\xd6/\xa9`TJATB\x01\xa0\xa20i\xd1/\x9d &\x99\xc0\x06\xf1y\xa5\x12L\x9cH\xe0\x95F0a\x12\x813\x85`\xa2\x04\x821\xe9\x03\xc1\xc9\x03\x13\xa4\x0eL\x9c8\xe0H\x1b\x98 \xe0\x1f\xa2,_\xb0\xdf\xad\x13o\xa0?\x02\xe6\xd7\xa3\x07\x13A\xfc^\x00\xbf\x1b\xde\xf7\x01\xf7\xadZ\x0c\x05\xf6}a}\x13\xa8?\x01\xa4\x1f\x00\xe8\xc7\xc3\xf9\x16\xd0\xdc\x17\xca\x9f\x18\xc8\xb7H\xa4\x1d\xa9Q\x10\xbe\x8a\xbcj\xf8\x19\x00\xfc\x89\xe1{3x\x1f\x0b\xdd\xf3\x88\x80Np=p?-lo\xda\xf89!{\x13\xa6h\x82\xeb\xa7\x05\xeb\xe3\xa1z\x03,\x1f\x05\xca;\x01\xf80\xf8\xdd\x1b|\x0f\x84\xdeC\x80w#\xecn\x96\xc6\x17\xfe\xf4\x83\xdc\x03\x01\xf7\x00\xb8]\xdb\xb5i\xa1v\xd3\xa4\x18\x01\xb3k\xe3\x14F\x90=\x0eb\xb7\xc1\xe9\xd3\x83\xe9\xe3G\x927\x90\xee\x0b\xa3\xf7\x97H\xff\x03\x9e\x11\xe7;\x07\xdc\xba\xc7;\xf7\xed\xa7s\x9b=J\xe76]\x83rO\x13\x83'\xbe\xf0I\x1c\x80bd\x96\xcem\xa6s\x9b{\x8a\x81Z\x8c\xcc\xd2\xb9\xcdC\x9a\x08v\x19\x07\xbcD@/\x93\x80/\x93\xc3/N\x00\xe6\x0e \x98\xbb\x02a\xee\x00\x86 \x01bb\xa1\x18\xab\x0dw\x811\x13\xc21\xbe\x80L $39(\xe3\x86eF\x033\xe9\xdc\xa6S\xb28\xa0F\xcb*\x9d\xdb\x8c\x81l\\\xa0\xcd4\xb0\x8d'\x16\xe1\x84n\x02\xc0\x1b\xe7\xf9\xb9@\x00'\x9d\xdbL\xe76}\xa0\x1d\xa7VC\xe1\x1d\x7f\x80'\x9d\xdb\x1c\xd0\xc4pO:\xb7\xd9\xa5X\xf0G\xcb,\x9d\xdb\x0c\x80\x82\xc6\x80AZv\xe9\xdc\xa6\xf6\x05/\xf8(\x9d\xdb\x9c\x0eLJ\xe76GCM\xd3\x8c9o\xb8\xc9\x1fp\xf2;\xb7)\x0f\xa1tx\xf4v\x91\xf2\xe7^\xed]\xf5\xb7\x9a\xc8b\xb2\xcb~\xe0+\xe7e{{\x96o\x7f8\xa7\xae\x1a\xc7\x11\xb4\xc0\xa39B\x98\xab\xf9\xee*_\x9c\xfd\x92/<\x8e\xe6<\x17\xef|\xbb\xbbxypJGun\x7fJG\xfe \x0e\x90\xa9~z\xdcE\xf9\xe7\xd9S\xdbm\x94\x1d!\x1e)%=\xecS>WR7\x01\x18Y\x98\x9a$3C\x11\xdf.(\xdaQ\x9e\x13\x1f\x1d\xb0\xd1\x8d\x81m&\x9d\xab\xf6\xd9\x84\x8f\x0e~M\xf8\xa8_|\x0d\x12>\x9a\xf0Q\xe3\x93 \x1f\xe5\x94\xf0\xd1CJ\xf8h\xc2GM\x94\xf0\xd1\x84\x8frJ\xf8h\xc2G\x13>\x9a\xf0QA \x1fM\xf8h\xc2G\x13>j\xa2\x84\x8f&|4\xe1\xa3 \x1f\xed\xd0\x14XU\xc2G9%|\xf4\x1f\x01\x1f\xcd\xbb\x06\xdbV\xcb6_(\x04\xaa\x8f\x18\xb6\x9b\xd7=j:\xc7\x1c8\xcd\xf1BF\xe6\x97\x07\x0b\x83\x8c;\xf3\xd8\xd4\x1cc6Xe=N8)\xf2\x1b\x1e\x1c\x1b4D\x1f\x8b\x10\x89\x1cs=v\xcdv\xc1w_5Q\\\xd8`\x81eS7U\x1bDj%m\xea\xb5@v'Du\xa1\x8f/\xb5uEMh\xaf\x16\xec\x9d\xe3l\xfd\xcdS3\xbe\xfb-\xff\xfd\xb2\xc2\xcb\xfc\xb3T1\x85y\xe7\x8f\xf2\x05\x1fXR\x8b\xdd~{\xc8\xeb\xc1\xa2\xb6\xa2\xdfW[.l\x00ng\x8f\x92t5\xe0\x84^\xfb\x1f\xa4\xdaf\x03fA\x97\x8e\xfe\xb9}(\x81\xb3\x07zL\xe0\xacOp\x0f\x128\x9b\xc0Y\xe3\x93 \x9c\xe5\x94\xc0\xd9CJ\xe0l\x02gM\x94\xc0\xd9\x04\xcerJ\xe0l\x02g\x138\x9b\xc0YA \x9cM\xe0l\x02g\x138k\xa2\x04\xce&p6\x81\xb3 \x9c\xed\xd0\x14@Y\x02g9%p\xf6\xb7\x02\xce\x86\x1d\x07\x15\xa0W{O\xdb\x15\xdb\n\xdbokS\xe7\x00kL?\x90\xf7b*g\xa4\xbc\xc5UM\xdb\xbb\xdc\x9e\xabS\xa1\xec1\xf6\xfdz\xfb\xf0h8Q\xd7\xb8|\xee\xc1\xc2\x8aJ\xb3B\x03\x01\x90\x93}\x83\xafS\x85\x13_\xd4\xf3\x90\x1fq\x7f\xaa3\x01\x8c\xdd\x17\x12\xc0h\xf8=\x01\x8c\x1dJ\x00c\x02\x18\xf7\x94\x00\xc6:\x01\x8czJ\x00\xa3\xa2\x040&\x801\x01\x8c\x9e^R\x02\x18[J\x00c\x97\x12\xc0\x98\x00F\x0d%\x80Q\xfbL\x02\x18\x13\xc0h\xa0\x040&\x801\x01\x8c `\xec\xd0\x14`O\x02\x189%\x80\xf1\xb7\x020zT\xc7\x150b\x87\xd3\x84\x87!;{\xe9qP\xa7h\xc1\x03\xeb\x14\xf8\xd8\x07\xc2\xb1\xb6\x0e\xd6)1Nimj\x02\xdd^\x8f\xc58{\x8d\xca\xe7\x1e<\xc6\xc9\x15\x10\x04zYb$^\xf8gOMN\xfc\xb3\x87\x9a\x0e\x18&\xf8S\xffB\x82?\x0d\xbf'\xf8\xb3C \xfeL\xf0\xe7\x9e\x12\xfcY'\xf8SO \xfeT\x94\xe0\xcf\x04\x7f&\xf8\xd3\xd3KJ\xf0gK \xfe\xecR\x82?\x13\xfc\xa9\xa1\x04\x7fj\x9fI\xf0g\x82?\x0d\x94\xe0\xcf\x04\x7f&\xf83\xc1\x9f\x1d\x9a\x02\x8aJ\xf0'\xa7\x04\x7f\xfe#\xc1\x9f\x07\xbb\xe2\x91\xf8gP\xd1\xd7\x0dY4\x05\xbe\x92\xc5n\xa9\x19\xde\xfc\x91?(\x8fl\xd2\xfd\xcd\x9eE!f\xc5\xe7\x9c\xd6\x1c\x03\xe0\xcf\xa9\xea\xb9m\xac \x1a\xe1\xec\xb7\xfbH\xa9\xe3\xa1b\x9b==v\xa9N\xc0T\x02\xa6z\x94\x80\xa9\x04L\xe9)\x01S \x98J\xc0T\x02\xa6\x120\x95\x80\xa9\x04L%`*\x01S-%`*\x01S \x98J\xc0\x94\x89\x120\x95\x80\xa9\x04L%`\xaaCS\x80\x04 \x98\xe2\x94\x80\xa9\xdf\n0%\xc8\xbe;\xe4\xc0Q\x1f\x7fq\x1f\x9a:\xfc\x04\x9c\xcd\xd9\x00?zw\xf9\"\x1d\xa0\xd2\xbc\x90\x0eP\x19~O8U\x87\x12N\x95p\xaa=%\x9c\xaaN8\x95\x9e\x12N\xa5(\xe1T \xa7J8\x95\xa7\x97\x94p\xaa\x96\x12N\xd5\xa5\x84S%\x9cJC \xa7\xd2>\x93p\xaa\x84S\x19(\xe1T \xa7J8U\xc2\xa9:4\x05f\x90p*N \xa7\xfa\xad\xe0TaU\x1b\x07\xa7\x99\xce~a\x13\xd2V\xb4\xb1\x87J}\xbbc\x03\xbc=\xda\xc4\xbea\xff4\x13\xe4\xe5\x92\xb0\x95X\xfe\x99q\x97\x1c\xcdg\x96\x04W\xf9\xd8C?\xb8d\xc2\x83\x0c\xd0\x8e\x1d\xd8\xa9\xad\xb0\x8e3\xa2\xe1\x13\x12\x98\x1c\xd0\xf1\x83sb\xc0\x1c;h\x13\x05\xd9\xf0&\x0c\x0c\x9d\x80\xcd\x04pM$Xc\x0cq\xfbA5\xa3\x80\x9a(\x98\x06PQ\x98\xb4\xe8\x07\xd2\xc4@4\xb6\xc0\xa9\x17@31<\xe3\x05\xceL\x08\xcd8\x81\x99\x89`\x991\xa0L0$3\x01 31\x1c\xe3\x00c&\x87b\xee\x06\x88\x99\x1c\x86\xf1\x07a\xe2 \x18\x8b\xd2]\x00\xccd\xf0\x8b\x1f\xf8\xa2\xf1\xfe\xcc\xf6ub\xe0\xc5\x05\xbb\x8c\x04],\x90\x8b\xd3=q\xc2-~\xfe\xcb\xb4P\x8b\x0bhq\xcb\x14\x07\xb2(\xcb\xaea\xe8\x82X&\x04XF\xc0+zP\xd4\x06\xaeL\x0b\xad\xd8\x81\x95)`\x15/\\\xc0\x01\xa9x\x03*\xe6\xd8g8\x98b\xe6\xa5\x8d3L\x02\xa3\x84(\xcb\x17Bq\xeb\xc4\x1b>\x89\x00O\xf41\x99\x89\x80\x13/\xd8\xc4\x0d\x9a\xf8@&V-\x86\xc2%\xbe`\x89 *\x99\x00( \x80I\xe2A\x12\x0b\x14\xe1\x0b\x90L\x0c\x8fX$\xd2\x8e\xd4(`D\x81 \x1a~\x06XdbP\xc4\x0c\x89\xc4\x02\"<\"\xa0\x13\\\x0f\x87L\x0b\x86\x986~N \xc4\x14\xa95\x81 \xd3B \xf1\x00\x88\x01\xec\x88\x82:\x9c\xb0F\x18\xa8\xe1\x0di\x04\x02\x1a!p\x86\x11\xcc0K\xe3\x1bT\xf6\x032\x02a\x8c\x00\x10C\xdb\xb5i\x01\x0c\xd3\xa4\x18\x01^h\xe3\x14F\xe8\"\x0e\xb8\xb0\x81\x14\xd3C\x14\xe3G\x927<\xe1\x0bN\x8c;B#\xe0\x80\x89\xce\xd1H\xc4\xa2{\x98f/W:%\xd3\xa3tJ\xc65X\xf741\xa8\xe2\x0b\xab\xc4\x01+Ff\xe9\x94L:%\xb3\xa7\x18\x08\xc6\xc8,\x9d\x929\xa4\x89\xe0\x98q\x80L\x04$3 (39,\xe3\x04f\xee\x00\x9a\xb9+p\xe6\x0e\xe0\x99\x10\x80&\x16\xa2\xb1\xdap\x17H3!L\xe3\x0b\xd4\x04B5\x93\x835n\xb8f4`\x93N\xc98%\x8b\x03p\xb4\xac\xd2)\x99\x18(\xc7\x05\xe6L\x03\xe7xb\x14NH'\x00\xd4q\x9eV\x08\x04v\xd2)\x99tJ\xc6\x07\xf2qj5\x14\xf6\xf1\x07~\xd2)\x99\x01M\x0c\x03\xa5S2]\x8a\x05\x85\xb4\xcc\xd2)\x99\x00\x88h\x0cH\xa4e\x97N\xc9h_\xf0\x82\x95\xd2)\x99\xe9@\xa6tJf4\x045\xcd\x98\xf3\x86\xa1\xfc\x81(\xbfk\x86:\x87T\xe0\x0b_.\xc4\xc5\xb2\xdc)t\xc9\x7f\x87\x9f\x1b\\\xe5X\xdc%\xb4\xef\x89\xb2z\xbd\x935\xcf\x9bz-\xdez\xa4d\x7f\xa0\x07j\xba}\xefRO\x18\xf1\x90\x88\xc1\xcaEj\xaf\x00\xb5\x06\x89\xc3F\x87\x8b\x80Q(p\x82I\x1b\xf4\xf9j\x837\xe4\xaa\xc5>,\x98\x93W\x16l\x93\x97\xf5\x9f\xff\xa8y\xa4\xfe|E\xf3\xd5U\x91or\xcd\xe9\"\x98\xae\x8d\xbf\xe1\xab\x8c\xd0\xfaj\x8b\xab\xab\xf9\xae\x1e\x9b\xd6kl\x8c\xf5\xe6\x16W\xf9r'\xda\xc3\x8b\xa7\x7f\xfa\xd3\x93\xff\xf3\xa5\x9a\xa38\xdb>\xfd\xd3\x9fo\x9eL\xdf\xa0\x07\x94-\xe6\x9e\x17z-\xf1j9\xc7\xdf]\xbe\x18\xf0K\x80u\x02\xac\x9d\xd1Z\x9f\x80'$\xc0:\x01\xd6\xc6'\x13`\xcd)\x01\xd6\x87\x94\x00\xeb\x04X\x9b(\x01\xd6 \xb0\xe6\x94\x00\xeb\x04X'\xc0:\x01\xd6\x82\x12`\x9d\x00\xeb\x04X'\xc0\xdaD \xb0N\x80u\x02\xac\x13`\xdd\xa1)\xc0\xc3\x04XsJ\x80\xf5o\x05\xb0v\x96u\x9c\xa3\xf2\xa6\xc5\x91\xe7\xa8@e\x86\xe9\xd9/h\xb1\xa80\xa5\xb6\x92\x8e\xcf\x8b\xe2[\xf9|\x8b,\xf3\xd0\xac\xf8#\xb7\xb7E\x01\x19aV\x8b)\x1b\x01\xcd\xcb\xd5\xbe\xca\xa3\x1ex\xdes\x95??X\xe4Yi\xeba\x80@\x0b\\\x92Mt\xf8\x03m\xf4U)\x059^\xf7 >\xbc y\xd9\xe2\xef\x08jr\x83K\x198\x10\xa2\xe7\xa5\xb0+\xcct\xa1R\nd\xda'\xbfy\xfb\xe1\xbbs\xbe\xd2\x8b\xe7\xe4\x92\x99\xf3\xa8\xc6EYKc\xd2F\x92\xba\x16E\xcbP8X\xfa\xc6h\xbe*Q\xddT\x98\xb6\x19\x1c\xcc\x9d\\\x91\x15\xe1\xd3\xf7p\x15\xee)D\x0d\x13\x05\xe2\xb6\xff/'\x88\x80\x1a\xf2\xf2 \xac\xb5E+\xa9\x14gv\x83zp\x90\xe1\xd0\xfeY:u\xba\xc9 \xc8:\xf8\xecC\xaf\xc4\x9f\xeb\xab\x1b\xbc\x1b\x89\x93\xfb\x95\xf7\xfa_S\xd0MI\xa1\xb4\xcc\xfe)\xa3\xd0\x88R\x11j\xbfD+\xfc\x0e\xff\xdc`Z\xcf\xc4\xef\x06f\xcc\x9c\xed8\x1b\xc6\x96)\x12\xc3\x86\xd0\x1a0\x8f\xed\xf2\x80\xf0\x0c.\xea\xce\"\xb8\xadw\x90[\x8a\x9dV\x98\x07\xfeK\x02\x1bRa\x15\xe8\xd79p5\xa9Ql)Wg\x96\x03@\x9d\xd7\x05\xb6D/y\xf3\\\x8b\xfc\x1fe\xb3\x99\x8b0\xa5\xc2&:\x81pS\x7f\xbb\x8a\xe6\xa6\xfe\x8a33\xad\xa3\x9f\x10\x05\x8a\xebS\xc8k\xaa \x17\nM)\x06\xf3BD\xa1?\xe5\xb4?><\xb2(:\x8b\x89W*\x85\x8e\xc7Yw\x9d{w\xf9b\xd8\x89\x94^\x91\xd2+\x1c\xb3\xf2\x0e\x90 [\"\xa2\xf4\xddL\x03P\xfe\xac&\x82\xfa\xdf\x9aH\xb3\xd7\xaeO\xcb>\xa892\xa1q/\xdf~]bV\xd8$\xe6\xc0\xd2K\xd3\x8e\xa4}\x10\xb5\xc39\x98)\xad\x8d\x9cA\xed:\xc0\x8d\xffj\x80\xd5\xf1\x1e\xb2\xed\xa6\xcb\xb6\xbf-\x8b\x1dG\xde\xc9\x12\xc8rIq\x0d\xa4\x82\xbe\xb8\xd0I\xa1\xa0\xb8\x1e\xea\xeag\xe9j+\xda+k\x89\n\xea\xd4\x96ae\xd4(Q\xc8g\xd2\xe3\xc02\xca\xcepU\x96\xcd\x06Wy\xa6\xfe\xc6wy\x19*Y\x7f\x04\x1a\xb7\xc6\xa5R|S\xb6v\x7f\x10\xf6\xbc\xe0\xdc\n6\x86Z\x15\n\xc8\xb0a\xfe>c\x10\xa6\xcf>\xfb;V\xee`\xa5\xd4\xa8\x97gTzj\x97?\xabf\x96i\x01\x15nIw\x04\xcb\x05iXAm+\xa0\xb0\xee\x9f.\x96P\xe0e\xad\x1c\x0e\xe9\x81\xa8`%\xc7\xf5\xc5\x04\x11\x8d0=\xcfw\x80Q\xb6\x06\xb4\xdd\xde\xa3\x16\xbbn\xc0\xfe}\x9b.;o0\x8d\xf2\x11J\xb8\xa1aN\n\xe4\xe5\"\xcfP\x8d\xdb\x0c\x1f\xa9A\xfe\xa0\x1cH]vy\x99\x15\xcdb\x10\x8aD\xa2\x956\xc5j\xf0\xc5\xf8\x8a\xd2qx\x98\x9b\xd0\xf3\xaa{\xcc>^\xd0\xc1\xd7\x1at\x81Go\xd9Z/\xd6~>\xbd\xf6\xf3\x91M\xb9\x99\x9cM\xf9\xaa$\xd5 oB\xcd\xc6~\x13B3c?\xec\x9c\x90\x02\xa3\xd2\xf6\x01+|\x8b\xab\xde\xab\xb6\x8f'\x9f\x1e~\xb8\xbc\xe3FVX?\x13z|X\x1b\xb8\xe4\x19e\xa4Z\xe0j\xb8!\xd4^a\xfe\xcd\xa4\xda\x18\x1bB9\x9b\xef\xae\xf8\x0e\xd7\x12K\x91\x0e\xa61\x8e\xa2\"'l\x9f\xe8\x1fK\x91\\\x1f\xa9^?\xec8\x8a\xc9\xd9\x8b\xda\x94Z\xa2!\x0e\x87\xcb\x16 \xb1\xbe\xea\x8e\x82L\x19\x03\x998\x02b\x8e\x7f\x84E?<6erTzm\xc8\xe4\x16\xac?\x90\x15\xa5jl\x92\xd2\xee+\xed\xbe\x9c\xce\x9a\x92\x8f\x1b\x1a\x93t\xfcG%[&\xcc\x15\xfb\x8b\x97x\xa1\xcblO\xc0\xb0U\x96KuE>\x95\xb8\xa2g\xbf\xf0\xff\xb3A\x15\x1dC\xf4\x92=\xfb\x96\xbf\xd8\xae\xb6|A-\x8a\xf6\xf6)\xf9QT\xf68\x07\xd4\xd87\xaf\xf3\x8c\xc3\xc4m7\x98\x01\xdf\xdb\xcc\xae W\xba\xe9i\xf7X\xeb\xad\xfc\xf9X>\xdb[\xbb;r\xca\x9f\x1f\xec\xfa\xdd\xfd\x18\x0f\xc3f\xc8\x0f8b\xd2\xbb\x17sAj\xfev\xe3\xee\xed\x9c\x96\x83\x87\xeaG\xcf\x90t\xc3\xa7OFGI\x90Ci\xe0\xa18p\x01I\xe0\xa7>\x17\xa0\x04~l|\xbf\xc2\x94\x8e\x95\xa0(\xf7\xca\xc8MB\xd96\x90 \x82]-A>*\xda\xdb\x92VI\xb4\xae\x9a\x8c5\xb5\x87\xf5yY\xe3\xb25\x82l\xf4j\xb9\xf1\x11M*\x89\xd4\x1b\xd2~\xf7\xe3}\xff\x018\x08s\x83K\xbe\xc9\xed\xe5\xb3\xc86msY|:\xf9\x9cmv\xec\x13t\x86\xcd\x1a>\xb7\xde(\x0f\x1eMX\x1c\xa7\x84\xc5\xed\x7f\xf2Uf\xc2\xe2\xb8/\xd7qh\xda\xad_w\x86\xb0\xdd\\\xbb\x07\xe41\x8fC\x17H\x11{\x96\x8f\x8b\xc39\xed\x98\xcdi\x7f8\xf85\xed\x0f\xbf\xf0\xfe\xd0\xba\xff\x1aL\x1d\xb1\xf9\xea\xce\x92\xfd\x9eL-'\xed\xd6\xac\xbb\x83ak\xf3p\xda\xdc\xd5\x862\xc1y\xee\xdd-$8O\xd2\x1d+\xd7\x0dD%8o\n-&8/\xc1y\xbf-8\xcf\x1cg\xa4W\x1b\\\xa3\x05\xaa\x919\xc2\xf8\xbf\x83\x08#\xfdQ\xbe\xd2\x83\xf4\xb2\x82[S\xc5\xae\x0d[\xe0[V\xdd\x85^_\xa7\xab\xdf\x98|\xe2\xc1F\nU\xc7\x1f\x88\xeb\xd8\xd5\x81\xf6\x01\x0f\xffO\x04?\x9b2\xaf\x1d\xe1F}\xd7\x04\x19;(\xc8#\xb0\xe7\xee\xac gp\xcf\xa3\xcb\x8a|BO\xdd\xa7\x99/\xdbF\x9b\xc4\x84\x10\xad\xf4\xca\xdc\xac\xf2[l.\xb0\xd0\xe5\xc5\x94\xce\xcb\xce@\x83j\xb2y\xac\x0f\x9a\n\xc2\x9f\xb7\xa4\xc4\xf6\x80\xa4k\x1b\xd5\xa5\xce\x96\xca\xb0\xb7W\x14\xa6%%gWQ[\xf2I\xac_O\xbe\xde\xff.\x82\xcb\xa59\xe6(h\xd3\xd8\n\x1d \xaaP.O\x14\xce\x11\xc5W-\xe2\x93\x97\xc2H\xb3\x7f\xe3\x9f\x1bTXc\x9c\x82\xf8\xd7\x13V\xe9c\x99\xd7\xc7T\xeew\x1c\xaf=\x91\x9f\xf4\x19<\xf9\xfa\xffk\xfb\xb8\x17\xc7\xf5>/>\xa4\"\xbe\x9d^\x90\xa5\x18\x1b\xfb*:Y\x85\x99\x97a\xae\x12#\xa8\xed\x01cq\xccX\x1c;*l\x08je\x7f\x06\x7f>\x85z\xdd\xd0sx\x02\xec}\xd1\xb7?{\x8cUT\xe4\x88\xda\xa7\xb1\x8fQ\x11\xe40-\x82\xbc\xa7\xbc+`\xa5HvA\xecK\x8a\x9cr5\xca\xc9\xae~S\x10\xbf\xf7\x847>\xe3\x17\x95\x84\xdeg\xed\xcc/$\xa3\xe1\xc3:\x15\xc8)Zo#\xde\xc8\xd1\"'R\x9e\xa9\xb0\xb3\xfe]\xb7*;K\xcb\xd0n*\x95v'\x1aw&\x8c\xcc\xc4\xc4\xecy\x17]bs&z\x0d\xf47r\xac\x99a_\xf8\xdf\xc4t=\xd9\xef\xc0\xd9/m\xef\x8c\xfc\xf8\x8c\xef\xcc\xb9\xaf\x0d\x13k\x91\xd3m\x81\x0ca\xf1\xf0\x1eZ\xc6\x98l\xa9\xdd\xcf\xc8u\xaeY\xad0e\xbb\x04i]\xd9Hk;\xebb&|i\xe1<\x1a\x8a\xdfp\xff~\xa2\xde\xe9a\xeao\x8e\xed\x03\xf9\x98/\xe2\xddXU\xafx\x1d\x87\xe4N\xf0\xea\x1c^p\xb6\xf0\x9c\x99A=O\xba\xdb\xccI|@\xcf\x7f8\x8a\x86\xf6\x1b|&\xa3\xfc\x9b*>G\xd7\xe4S \xa4\x04\xfcY\x96\x932r\xe3\xbd{\xfe\xe1\xed\x8f\x8feU\xc2\xcct\xd6\x1bD \x84\x8f\x0c~^_\x08 ?\xb7\xb50\x93k\x83\xb4\xa7\xa6\xca\xbf\x80\n?\xbe\xbb\x10q\x8a\x05\xc9\x1a^\x19\xf1\x840\xbf\x81\xed\x84\xbf\xca\xd6(/\x1f\xcbH\x94\x04\xfc\x8c\x9c:\x07\xe0\xf3R\xf8Wl\xa3\no\xb7\xe2\xaf\xc1j\x19bx\x82\x9a*\xbfZ#\xba\xfe2\xba\xf9\x01\xd1\xb5X\x05\xe9\x1a=\xfd\xd3\x9f\x815-\xe0\x8dVa[\xc2\xac\xfd\xfd!\xe1\xc3 N\x0d\xc3\xaa\x12\x9cz\x87\xcau\x03\x81 N\x9dB\x8b NMp\xeao\x0bN\xb5\x9c\xdb\xd8\xe3\xa9\x1eG7\x02\xd0T\xbe\xab\xde\xc79\xb5 \xf5\x87\xe0\xe9\xdf\x1bvj\xf2\xaa\xa2\xf6\x80N\x04\xd4\xe1\xdf8\xd1O\x17La\x85'\x1c\x9e\x1f8\xbb'\xc8\x89yz8q\xe0\xdc\xb8\x0c\x9f\x0d@;\x1d\xf0\x06\xf0\xe2\xa0\xbeX\xa7\x0f\xd2i\xdf\x0e\xec\xc9\x17\xe5\x0c\xd1\xcc\xb4\x08\xa7\x1b\xdf\x0cB7\x9d\x88\x0f\x84b\x9b\xe3\x90\xcd\x89q\xcd8Ts<\xa6\xe9\x81h\xba\x0c\x85 \x0f4\xd3s2\xbb\xc2)\x82\xa6\xc51m\xd3\xdc/Z61\x82\x19\x8f_\xba\xd4\x17\x83]Z\x855 \x97f\xdc\xd29\x0e\xfc\x8c\xd6\xb4\x88\xa5\x1f^iE+\xc3\xfae\x1cI\x13\xe2\x94\xbe(\xa5\x19\xa3\x0c\xebS\x10>95:i\xc3&\xc3\xbaa\x1crQ\xa8$\x97\xde\xc0\xd0\x13\x93\x8cA$\x9d;\x0cEF4r\"\xa5M\x85C\xc6\xa3\x90\xde`\x99\x1d\x81\x9cN\x1f\xd3a\x8f\xa1\xc8\xe38\xdc\xd1S\x91n;7\x0eq\xb4\xe0\x8d\xbe\x10\xc6D\x08F\x020\x12\x80\xf1\xf7\x03`\x0c\xbd\xdc\xde\xf0\x13n\x85\xa5\xc0\x08\xfb{7?~\x18\xbb\x1bqd-(\xf3?\xf0J\xed\xfa\xe0F\xe9\xcf\x9c\xdf\xe0R\xe9Ay\xae\xf2\xe6\xef\xfb\x8e\xed\x11\x91'\x8a\xcb\xc5\x15.\xd1\xbc\xc0\x0b\xdbB\xf8[\x08\x1dY\xbb)H\x17Z\x1d\x92\xdf\xc2\x0f\xf0\x1e\x97\x8b\xefD\x93\xe2z\xcf\xfe\xad1\xbf\x04\x10\xb5s\xd7\x1c-`\x8c\x19WS\x16\xa6W\x07^\xb2\xc5\x9amI\x16\xe7\xf0Q\x9c\x05\xef\xf6(/\xd5m\xee9\x85E\xfb\xac\xf1R\xaaW\xa4\xe2w\xb4\xd1\x9c\x9e\xb6W\xe7\x94\xf8S\xb1c\xbe\x1eS@W\x1b\xb2\xcc\x87\xed\x82M\xc9L\x0e.c\xbb\xefkR\xf1\x9b`\xc4\xcd\x99\xe2\x8e\xc7\x0d*\xf3mS\x88\x0d7\xdf|\xe4\xb4\x97z\x94\x9b\xbeDI>),\xec\x06\xe3\xeda\xac\x7fO\xcf\xb9\xfd\xe9\xbbO\xff|*\xda\xe2 \x0b\xfe\x9c\xd3Zl\xba\xe7(\xbb\xf9\x84\xaa\x85\xa9\xd9\xfe\xe5\x9ed\xd9\xf6~\x99\x17X\xbb\xcb\x93\x0e\xc8\x95\xef\x047\x8d\xf9\xde`\xb9\xb4\xde\xdf\xaf\"0\x1a\x83\xab\xe1\xa5\xf7\xd7\x06\x17\xaew[:\xf4\xd6Z\xa0\\\x18y\x9d\xb5\xe4\x92%\xb7,\xb9e_\xd2-\x0b\xf2p\x0e'\xa8\xce\xcf\xe9\x9a\xden\xbd\xb4\xee\xdfqY\xb3\xbf\xabqj\x9am<\xfe \xbc<\x89\xf5\xd6MU\xd2\xfe)G\xb1/\xe3\x17\x17\xb7\x17\xbfv\xdaj\x99Q\\\xd7y\xb9\xeab\xca\xcf\xcb\xdd\xa04\x02\xdf\xe2\x11,n\xd5\xe5\xee\xced\xbe\x9a\xb7\x9ff\xf4\xd1R\xda2'wX R\xda\xb2\xa4\x94\xb6\xac\x88\xaf\xb4\x9d\xc9\xecY\xe2j\xff\xc6\x80_Hu\xab\x7fn\x1fJ^\xe6\xe0\xd7\xe4e\xde\x81\x97\xe9\x0c\xfe\x19\x8b\x1f\x0f\xa6\x8dxX\x99\xd0\xd61\x93\x7f\xde\x91\x06>\xa1\xb2\xe6\x1bhh\xb63x\x8d\x99\x0b'\x8c_M\x98\xbb\xdac\x87\x8ab\xe8\x8aBd\x8e\xdb\xf0#k>\xafAw\x19)\n\xcc/\x8a\x7f%5\xb7i\x8a:?PTJ\xf3\x0e\xcb\xa1Mi\xdew\xa8\\w\x82rJ\xf3\x9eB\x8b)\xcd;\xa5y\xff#\xa4y\xd3\xad\xdcd^\x05\xdd(\xdcY\xed\xde+\x0e\xda+\x86\x19\x7f>\x16\x9dw\x0d\xb7\x0c\x07\xf7\xe4x$\x14\xe9\xeb\xf2\x1f\x08&\x1fz\xb0A\x99tK\xb1$\x9fp\xcc\xb4\x85\xe4\xa3\x8a\xc8?\x90[\x8a\xdb)\x9c\xee+\xeeS\n \x0d\x1fN\x01\xa4\xc9\x02H\xc3\xc5E\x1bFZ\xf5\xe2H\xedM\x0e\x03\x86]\x9cr\xd8\x91\xfd5\x0f\xc7T3\xd3=\xe3N\xa9\xaaz\x8a;=\x94\xb8\xd3\xc8[\xb74\xab\xdd\xb4\xb9g)\xf0\x13\xbb\xabN\x81\x9f;T\xae;d\x91\x02?Sh1\x05~R\xe0\xe7\xb7\x15\xf8\xd1\xc7}\x9a\xed\xb6\xd8\x99\x03<\x1f\xd8Xx\xcf\x1f\xea\xc5s\xc4\x18\x11o\xf7b9\xda\xbc\xe9\x0e\x97G\xaaK\x0f4\xf8\xd2UH\x97\xee\xc3\xa5K\xa1\x97\x87\x13z\x91\x9bZ9\xe2U\xc8\xa5\x1d\xffm\x88e\xf0\xa2w\x84E\x1bv\x88\x08\xbbh\xb88m\x8e\xa0\x14\xa0\xe9R\n\xd0\xfc\x1d\x04hL\x1d\xe1K^g\xd1\x89?\xcd\xd7]\xff\x8cg\xf9\xda?\xa6\xa8\xca\x81~RT\xe5\x8bFUR\xac\"l#\x98b\x15w\xa8\\\xf7.;\xc5*\xa6\xd0b\x8aU\xa4X\xc5?L\xac\xe2l\xbe\xbb\xeal\x8c\xb5'\xa0\xf8\x83o\x97\xfd\x0c\x94}\xacBf\x9a\xf0\x0d\x9b6^\xa1\x18\xf2\x8e| '\xbfc\xbc\x1e\xc3/\x16\x1bn\xe3\xf7\xab]wO\x1d\xba\xfb\x0b\xbaE\x93)\x0f\x9eq\xdf\x90\xb52\x81\x86rz\xf2\x8a\x90YV J\x1d\n\x12\"\xb2\x97D\x1f;/\x9ae0h\xaeU\xdd7\x0e\xd5]\xee\xea5)-\xca\x13R\xbd\"\xe4d6\x9b\x99W\x83Vq'\xd6g\xf8\xe0\xe3j\x8d\xd5*cr!\x94\xfa\xf2\xbb\xf7/\xde]\\~x\xfb\xee\xb1-\x94\xbb\x1f\xa8\xf6\x86E\xd3vu\xfe\xd1\xa1\xce\xef\x89Y\x93\\\x95\xe7\xcf\xe0w\xdb\xf9\xec\x15!\xbf\xccf\xb3_\xcd\x0f\xa3rw\xca\xdcP\xf6\xc6V8Q?\xa2\x8a\xaeQ\xc1\x94l\xef\x88M\x85C),\"\xe4\xcb\x81\x00\x1f\xcb\xcd^\x04. \x9f \xfc\xa9\x7fz\x06e^X\x07\xb8].\xc3H\xfe\xc0\x8f'd7\xad-V\x1b\x0d\x98\xef\xf6n\x97Z=D\xee\xbc\xde\xebUyw\x0d5\xf8,\xc7\x1a\x97\xea\x8c\xed\xdfg\xfc\x07\xe6\xae\x1e\xab[\x91\x94\x1f\xc7G\x82im\x10#D\xdfX\xbb\xb4\x94\xc5\xae\xadI?\x0c\x16\xb4n2\xa0e\x8duI\xe5\x82x\x1c\xe3\xf8\xecX\xdf\x94\\\x13\x95\xc8|\xb7\xdbF\xe9\x8f\x96\x84\xcc\xe6\xa8\xe2\x9d\xfd|\xb6\x9b\xfd\xedHh\x91\xef\xbd\xb4\xfc\xcc[Q.\xea\x11\xe3\xc1\x96C\xed#\x7fy\xff\xf6\x8d\xfe\x97g\xcf\x9e=3\x8f\x01\xf6\xde>\xe6\"\xfcH\xc2\xcc\x81t\x82\xc4\xbe\xae\xa1m\xacw\xd5\x14\xa8\xd2\xf3;d\xc3^Y\xe0\xbd\xdbr\nx3\xc7\xfcn\x009\xbbO\x85;\xaecg\xbaN\xab\xe3R\x88\x94\xa0\xeb\x7fg\xaa\xbb\x96\xc1\x84\xd6m\xeb~\x1c\xfd\x04\x91\xe6\xe7\xdc\xb2\x01A\xd9\x0d\xb3A\xfb\x0d\xf12/\xb0y\xddP6\xeb\x12W\x94\x94\xd6i+#q\xcb\xbc\xa2\xf5\x15\xff\xc2\xcf\xe0\x89\x99s\xfb\x02\x1b\x94\xea\xf9\xa7\xe1+\x18\x80U\xaa#\xae\xcb\xa3s8\xd2\xcd\xda\xbe\x1af\xa2\x97G\xa76~\xbc\x7fo\xd0\x86\xf1\xfcW\xd1\x85\x7f\xb3\xbe\xc0\xfa7x>\xb4\x93\x17K\xb9\xe1\xea\x8f\xb5\x16@\xf8\x84\x8b\xe2\xab\x9b\x92|\x12\x89hkD\x01\xd9N\xa8\x99'W\x7f\xc8\x9f\n\x07~0\x0f\xf6\x07\x8f\xa48l\x00\x9b\xce\x02\x8a!\xado\xec\x9aOF5\xce\xd7\xa4Xts\xde\xc4T\xce\xcbv~\x80\x88\x00\xeaY\x89)\xa3o\x87\x8b0k\x17\xe7\x13f\xd7\x94\n\x0fBC*b\xfa\xd3_\x7fzl\x99HS\x8c\xb9~\x83\xf6a\xc7U\xc5X>\x99=}\xf2\x94\x1eY\x86\x90\xf8\xaf5\xdfi\x7f\x895L\x91\xa8\x04\xee\x83\x0d=\x9b8A\x83\x8a\xb3@\x1b\xa7\xe5m\x82#\xdb\xdeT\xfd-{L\x93\xf6t\xe4\xf7\xb8\xba\xcd3\xec\x91\xeb5/HvC\xcf\nTc*\x91=]\xba\xd7\xf7\xb8~\xcd\x1f\xf9\x96=\xdf\xde\xa7\xc0\x91e\xfew\xe0\x8c\xb4I\xc9\xfdw\x1f\xa9\xfe=\xd0\x8c'\xde\x8f\xab\xdcx\xab@TN\xcd\x88;\x18\x1d\xc1\xa5-\xaa\xea+\x8a\xeb\xab5F\x0b\xac\x81C\xc1%98\xa5gd9\xd9 N\x0cU\x91\x0d\xa5WdV\x15\xf8\xa8\x0b\xdc*k\x8f_^\xa2\xaa\xa6\xb8\xfe\x81k\xee\xf0s\x8b\x87\xf8\xa0\xbdx\xa9\x1b%\x9a!\"\xaf\x80\xed^,\xb5\xe5\x91Z\xee{_\xd3\xc5\xcd\x15\x7f\xf5\x1a\xf2\x92\xd6\x18-\x0e/~\x1d3\xcc\xeev\x0c\xc8\xb8\xba\xe5\xeb\x88\xee\x8b\xcb,y?y\x02\x9f\xe3kZ\x93\xc2|\xe4\x02\xf3\x07\xe9\x92\xd7\xd8\x01\xbf\x93\xc8\x82\xd0v\xfbe\x9b\xf4 \xd6\nz\xc1\xcccI\x1b\n\x19\xda\x8ad~\x89L\xca?WM!\xef\xd7\xd9V\x84\xd9U\xbb\x88\xa8\xfd\x9e\x02Ee\xff\xc3\xef\xba=\xb5D\"\xbaY\xb5\xcc\xe7k_\x12\xd9]m~\x0cU\xdb[!\x95\x85\xa1\nz\xefa\xb1c\xcb%q\xc0\xaf1\xc1PW\xa8\xa4\xc2\x07\xdc\xa0l\x9d\x97\xdaH8#.\x9d\xd6\xfa+\xf2\xf8\xa4\xa6\x0c&E\x1e,\xfcle\x9d\x9b\xae\xb9\x86\xd0f\x16\xa8\xc6_1~\x86'\xf9.\xcb\xbc6*\x9apF\xdb\x17\x02\xf0\xed \xf8,\x08\x82\xbc\xd6RE^=\x05\xef\xde\x82{\x8dm\x1f\xf3Zk\x15\xf9\x8c#En\x95C\x88\xda\xc1_\xf5\x9ek\xf2\xe0a\xfd\xda\xac\x88\x0f\xd9\x8cl6ym\xb9\x04\x1b|{\xe4\xd5\x13)\x18kN\x94\xb1\x15Vs\xb0a\xea\x12\xfb\xe9\x0b\x89\xc7!ET\x93\x8a~\xa1\x06\x87\xfaXV\xfc\xa2\x7fn\xbf\x814\xf5\xb6\xa9\xf7\x7f\xdbV\xf8V\xa8\xcb\xc0\x8d\x9f\xe2\xff\xe2}hW\xcc/\xd4\x1e\xdan\xbfPK|~\xc8\xbc\x91/\xd4$\xbe\xcd\x17\xb8\xcc\xf0\x17j\xae\x1d\x7f{\xb7\xc7\xe2\x8f2CM(\xae\xaed\xcd\xce\xbb\x96\xaf\xe7\xcc \x83\xd7\xcb#\xde\xa7\x0f\xf3\xb3\xbf\xc2\x92\x88\xa5I\x0b\xe4\x1b\x8f\x1a8\x97*\xf7\x12U\x7fv\xaa\xc3|0\x04\\\x87C\xc0W\xa7\x1eZ\x85 7\xf9\xc3g\x99\xe8\xa7\x82\x92\xdc\xb3\x14\xa8\x8f\xf0\x1d\xff]F6~\xe0N\xdd\x1f\x9eX0LuF\x97\xa7.\x16\x05S\x1a\xb4\xd5\x93\xb8\xe1\x9a\x01\xfc7>\xae0\xfcOCk@\xab\nc{we\xa6\x1a?\xe6.B\xe3\xd6\xf6yJ\xeb\x06#u\xb9\xa5\x10\xfd\xf9v\xfb\x03\xa2\xeb\xfd\xfd\x94\xb2\xec\x01cMm\xabr\xfd\xd9\x901\xe3*\xc4\xf4R\x9c\x04\xe9\x00\x0f\xbc\xb8\xc7Rx\xe2H\xa6CH1\x1c\x97 \x9b\x96\x04eK\xeel\xcc\xdb\x1b\x80i\x07\xbe\xd3\x91t\xcb+\xa8=\npuKj|\xe5\xee\x84 O) @\x12F\\\x06\xeb (E\x01\x02@\xa0\x10\xa0\xd8{=\xe9m\x8a\xba\x84\xcb\xc6zy{\x9f\xbe\x82\xf7\x17\xdf\xbf\xf9\xee\xe5\xd5\x8f\xef\xbf\xbf\xfa\xf0\xff.\xbf\xbb\xfa\xf8\xe6?\xde\xbc\xfd\xef7#8\\\xbe\xfb\xee\xbf\xde~\xf8n\x1c\x87\x17o\x7f\xfc\xf1\xe2\xc3(\x1eo/\xdf\xbe\x7f\xfe\xda\x93\x85:Q2R\x1f\xfe\xf6\xbeO\xef\xf3U\x89\x17?\xd2\xd5\x87\xf6$M-\xd3\xcb(\xff\xc9\x9bS'7\xa3\x17f\xb1Z\xeb\x01\x19\xbf\xe99\xfc\x17\xa9\xada\x91\x01\x99\xbf\xcb9\\r/\x07\x15~\xec\\\xa1\x8c>EL\x9c\x90\xed\xa9\xa0\x8a4\xa5%\x02\xd1\xa7\xb0\xbd\xb2 \xdb\x89\x1f=\xb9\xe3\"}\n\xb4u\x10a\xef\xc0{;\xbf\xa7\x88\xcf\x07\xbe.\xd9\x90\x82b-}\x8a\xd0\x1eDj\x90\x91gT\xa6O1\xe3NQ\xf8\x94P\x14\xfa\xc1!\xfe\xa3C\xec\x87\x0f\x8c\xf4\xf4\xc9+\xee\xd3\xa7:\xdf`Z\xa3\x8d#R\xbf\xa7\x08\x85\xf8\x06Q\xfb\xd4\x862\xdc\x1b\xce>\x8d\x90\xd0\xfbS\xed\x853TT\xd0S\xcc\xb8\x0f\xb7\xb7m\x85\xa20\xb1\xeeRc1.\x08[\xd5\xf7\xc91\xcc\xfb\xd8V\x98y\xcc\xa7\xf2\xd4\xcd&\xe7'\x86\xbc\x98\x89\xc7\xb9\xc3-bj\xfbH\x19\xeb\x8b\x9f\x1f\xd2\xf1[\\\x8fr\xcf~\xee\xf3\x01\x02\xedu\xa8\x9dN\x9e\xfd\x80\x92g\x9f<{7%\xcf\xde\xf54$\xcf>l\x0d\x14\x94<{#\x85O E\xa1\x1f\x1c\xe2?:\xc4~\xf8\xe4\xd9+J\x9e\xbd\xa0p{\x9b<\xfbC\xfa\xd2\x9e=7\x8bW\xb7\xa4\xce\xcb\xd5\xd5\x96|\xf2\xb3\xe1\x81\x1f\"\xcc\x14\xee\xc7\xec\xc3\x90'\xc8\xf4DJ\xe2krB\xc7\xe7K\x05\x10\xb1A\xfa\x9d\x84\x87\xf6`\x9d\x02\x8c8\xe8\xecd\xd6~\x18\xe9VC\xfd\x890^\xcb\"\xcfx\xf186j\x1dc\xae`\x8e\xe8UV\xe4\xb8\xac\xafP]\xa3\xec\xe6\xbea\xabN\x0f\xae<2K\x05\x05\xc8\x02\x81\xf2\x804\x8dx\x11\xe8U\x05\xca\x04\x11r\x81#\xf1XO\x11\x82A\xa4p\xe0\x93\xba\xac\xa7\xd0\x84f=E\xf6\x15F\xf4\x17\xfcR\xa2\xf5\x14h\xb0\x86\xa4\x0c\x983}ZO\xce\xa4j=\xdd\xa7\xd0\xa1&xH~\xc9\xda\xc1l\xfb\xc9\xdd\x83\x14\xee`n\x9e)\xdfz\xf2M\x04\x0ff\xdcK\x1c\x0fN\x0f\xd7Sh\xd2\xb8\x9e\xdc\xa9\xe4z\x8a\x1e\xc8a\x11\x1dE\xd1\xcd\x85\xfaL]\xb2'\xae\xebi\x02A}]\xaa>y\xa6\xbe\xeb\xe9\x9el\x7fL\xbc\x00\xc6)\x19\xc2\xb7V}\x1a\x118R4B\xdb0R\xe3\x10\x1bPR\x14\xb3\xc1\x1eR\xfc\x8cT\x14;p`\xfc\xe0\x81\xb1\x03hT\x00JQD J\x91\xff\x81\x03=\x8d\xd0\xdf\x08\xbd\x85\x1fY\xd0\x93\xc7A\x06=\xddG\xb7\xbd\x8f\x11\xe8\xe9>Dvg\xdd\x9a\xc9\xf7\xfcE0c\xdby\x0d=\x85\x9d\xe2\xd0\xd3}\xa8\xdf\xf7\x1c\x88\x9e\xeeCb\xf7I\x12=\xdd\x87\xac\x01gQ\xf4t\x1fB{\x9ef\xd1\xd3}\x08\x1cv\x1eFO\xfe\xa7d\xf4\xf4\xe5\xfb=fw\xeey,'\x88\xa7\xf9\x08\x8f\x9e\x84G\x11\xa2\xe7H78\xd6\xfd\xfd;\xdax\x06\xa5\x18(\x1a\xe3\x99\x87C`\x8a\xd2\x8e\xd3\x87\"m\x82\xa0\xb4\xe3\x8c\x1e\xd7\x8a\xe2\xa7\xa2\xa2\xd8\x81\x03\xe3\x07\x0f\x8c\x1d@\xf7\xbd\xe3\xdc_\xd5\x19\xaaA\xa19\xfb\xd15=9\x0f\xb4\xe9i\xd4D\x197M\x94%\xbdZ\x16Hs\xfd\x9c\x0f\x8d\x1ehai\xa4}\xfa\n\xbe}\xfd\xf6\xc5\x7f\\]\xbc\xbcz\xf5\xfa\xf9\xf7\x81)\x94C\x1ar{\xfe\xed\xfb\xef\xde\xf8g\x86\xf6i\xc8,0\xcd\xb4OCfo.|\xb3M\xfb\xd4\xe6\x9eN\xa7\xb6\xf8\xad\xb7 1\xb9\x17\xaf\n\xb4j/)W\xb7}|[d\xe4\xe6\xe2e\x14(#\xa85\x03\x90\x8b\x94\x10\xd7\x0b\x87\x14\x9d\xb2\xd4\xa7\xd1\xf3d\xb49\x0eH\xcc8\xa4\xc9\xc4\x8fC\x1a\x04\x05\xe7@\xf5i\xb2>D}\x821\x9b-A/\xf8v\xe7}\xbe\x12i\xdb\xccGS\xb8\x1eO\xabR\xa7\xc6#X\xf3;`\x05\x7f\xff\xed\xd7\xb8>\x89\xd6\xfag\xe1\xdb\x14\x1b~F?,\xca*\x82\xb3\x9f\x10\x95\xdb\xc2Z\xd4*@\xf2\x80}\x10\xaf}\x14\xceO\x1b{\x1bA\xb1\xf7v/b\xdd\x8fY\xed\xf7}\xf1\x7f'\xd6\x0b\x8a\xf0\x80\"\xb4 (F\x17\x82\xa2\xed\xf8(\x032\xc2xl\x9b\xf9\x95\xe3jV=E+\x17F)\x98\x11^<\xfd\xd3\x9f\x9e\xfc\x9f\x98WG*\x1a\xc6)\x1b\xf8m^\xd9\xf6\xe9\x9f\xfe|\xf3\xe4\xefQ\xfc1\x1e\xd9e3/\xf2\xec?\xf0\xae\x17\xd8\xbb\xc1;\xda\xb9\xac)\xce\x8fj(\x16\xb7\xda\xfcWk\x90\x02\xb9\x84&\xde\xf6i\xd4G\x19\xb3\x99o\xe3\xc2\xdb*'U^G\xcf\xe3/*\xbb\x92:D\xd8Hs\x13kh\"\xed\xf8\x08eF\xcf\xcbH\x0b\x1e\xa9P\x18\xa1T\x18g\xbbG(\x17\xc6(\x18\xc6Z\xed\xfb\x13<\xde^\xdf\x99\xb5\x1ek\xab\xc7X\xea\x11\x1f\"\xce\xd2\xc1\x146\xfa\x8bK\x1dw\x16EQ\xa4\xb8a\xa2\xb2}\x18)\xaf\xfcA\xb0@\xa9\xc2\xa4\x99\xef\xfe\x86\xca:/\xf1U\xd8\x8e(l'\x14\xb0\x03\n6\xee\xe1&=x\x85\x0c\xfc\x02\x82\"l_\xf0z\x18\xac,\x88R\x18\xc4\xae~Q\x8a\x838\xe5A\xfcZ\xf7e\xc5\x8cY\xd9\xee`M\x8b_\xcd\xe2\xcck\x94\x92\xc3\x8c\x99\xa0\x11\xab\xd6\x17\x901n\x85\n\x14,P\xa4\x90 x\xa4$\xbe\x01\xee\xd0\xd8\xe9k\xb6\x8a\xbe\xe0g\x01\x9f\xf3\xa3\x80cO(\xca\xc2\xa3\x9d3\xb3\xa8\xae\xf1f\xcbO'\xd6\x0469-0Z\x00\x12\xe7\x10\x9d\xfc\xc49E]\xc0\xb4\x93\xfe\xabW\xbc\xd3\xba\xbb-\xb9\xcb\xcb\xf0\xfa\x9a>\xa3\xc9\x91(#\xdaq\xa5\x0e\xf8$\xbf\xb8\xd3\\\x9cj\x03/\xd5\x81W\xae\x81\x97\x02\xc1\x7f\xa9\x08J1\xf1\xea)x\xf7\x16\xc0;A\xc4\xef{*\xf2\x19@\x8a\xdc*\x87\x10\xb5\x83\xbf\xea\x03\x933\xbc\xd20|\x12.|\\i\xa7\x03\xed9\x14|\x07BP\nD\xc0\xc7\xf0Kk\x88M`\x88LU\x88LJ\x08O?\x18\x95h\xe0\xef6N\x95<\x10\x94&\x10\x9c\x10\x100j\x02&\xb0\xa7'\x13\xd1\xb8\x9f\x07\xe3\x0d\xc6GH\xe0\xe8~\x88\xeb\xe4\x0b\x9as\x08\xdc\x83\x93\x1e\x10\xf6\x93\xc8\x03\xf26\xc0\xd8\x06\x86\xe8\xd0\x89\x1b\xca\xd7^\xacv\xf8\x99\xd4\x9dl\xef\xf32\xe3\xa9\xeftC\xe8Wtq\x03_\xcf\xfe\xf8\xcf\xe9\xfe5\xd7\"\xe3\x96\x0b\xfc\x8a\x0dx\xcf\x105?\x9cg\xf1\x9d\xa5\x02\xa6n\xd2o\x02\x80\xf7\x91~H\xf7\xaf\xf5\xc9}h\xde\xe3\x93~\xb1MR\xba\x7f\xcd\xddA\xf0_\xf0\xd2^\xc9\xa9r\x08Q;\xf8\xab\xfe.\xf6J\xfe\xc7\xa1\xbdz\xe4\xd5\x93\xf0\xc3\xcc\x1e\xc7\x96\xa7\x12\xcf\xfb\xd4\xebT\x0d\x0e\xf5\xe1<\xff\x9b\xee_\xf385;UK\x01g^\xa7j\xd2\xf3\xc4\xeaT\xcd\x85\x9d7\xf5?Y\xea%\x9f\xbf\xaf6l\x98\xed\xa1\xc4\xad[\xf9*/\x91t\x9c\xda\xc7,\x9c$\x83S\xa9\x1c\xb6\xabAl?\xf6-\xce\xd6\xdf<\x95\x12[+G_\x94\xf0\x01\xab[\xdaO\xa1^3Yd\x0d\xebk\xa6jz}\n\xf3\xa6V\x8e\xe0\xfb\x97\xffa\xbb\xf7\xff\x13w9oqUC^CM\x86\xb2\xd8Da\xbe\xe9\x1c\xd75\xae\xe0\xe3\xff\xf5\xd8\x10\xba\xcf\xca\xee{\x96n\xb3\xe3\xe45\x87\xfc\x07r\xba\xcd\xce\xe6\xe3\xa4\xdb\xec\xd2mv\x87\xe4+ \xa4\xdb\xec\x0c\x94\xee\xbc\x90\x94\xee\xbc\xe8P\xba\xf3\xc2\xeb\x9d\xa0\x82\x14a\x91\x07A>\x18|\x9f\xdcQ\xa6>\x05\xda:\x88\xb0w\xe0\x1d\x1c\xd9S\xc4\xe7\x03_\x97lHA\x91\xab>Eh\x0f\"5\xc8\xc83\xc6\xd5\xa7\x98q\xa7(|J(\n\xfd\xe0\x10\xff\xd1!\xf6\xc3\x07\xc6\xcd\xfa\xe4\x15E\xeb\x937 \xaa(B!\xbe!\xe9>\x05\xe3\xc5\x8aFH\xe8\xfd\xa9\xd2\x9d\x17a\x1a\x8bqA\xfe\xae\xef\xbcH\xb7\xd9\xe9(y\xf6\x92\x92g\xdf\xa1\xe4\xd9{\xbd\x93<{\xd7\xb3\x8aB\x1d\xbd\x88\xcf\x07\xe1k\xa0\xa0\xe4\xd9\x1b)|J(\n\xfd\xe0\x10\xff\xd1!\xf6\xc3'\xcf^Q\xf2\xec\x05\x85\xdb\xdb\xe4\xd9\x1f\xd2\x97\xf6\xec\x1f\xe0\xf9\xact\x9b\x9d\x99B\xc7g\xba\xcd\xce\xc54\xddf\x17\"\x17\xa4\xdb\xec\x1c\x14\xd9W\x18\xd1_\xf0K0\xd7S\xa0\xc1\x1a\x922`\xcedt=9S\xd4\xf5t\x9fB\x87\x9a\xe0!\xf9\xa5\xbe\x07\xb3M\xb7\xd9\xb9\x92\xed\xf5\x14\x9a\x82\xaf'wb\xbe\x9e\xa2\x07rXDGQts\xa1>S\x97\xd2mv\x1e4\xc6\xf6\xc7\xc4\x0b`\x9c\x92!|k\xd5\xa7\x11\x81#E#\xb4\x0d#5\x0e\xb1\x01%E1\x1b\xec!\xc5\xcfHE\xb1\x03\x07\xc6\x0f\x1e\x18;\x80F\x05\xa0\x14E\x04\xa2\x14\xf9\x1f\xdf\xd0\xd3\x08\xfd\x8d\xd0[\xf8\x01\x10=y\x1c\x0b\xd1\xd3}t\xdb\xfbP\x86\x9e\xeeCdw\xd6\xad\x99|O\xb3\x043\x0e\xbf\xcb)\xecL\x8c\x9e\xeeC\xfd\xbe\xa7j\xf4t\x1f\x12\xbb\xcf\xe5\xe8\xe9>d\x0d8\xd9\xa3\xa7\xfb\x10\xda\xf3l\x90\x9e\xeeC\xe0\xb0\xd3Ez\xf2?s\xa4\xa7/\xdf\xef1\xbbs\xf7 \x9dp\x0b\x98n\xb3\xe34\xc1@\x08ws\x83R\x0c\x14\x8d\xf1\xcc\xc3!0Ei\xc7\xe9C\x916AP\xdaqF\x8fkE\xf1SQQ\xec\xc0\x81\xf1\x83\x07\xc6\x0e\xa0\xfb\xdeq\xfa\x14W\xd3\x93\xd0\x9co\xf5\xe2.9\x0f\xb4\xe9i\xd4D\x197M\x82J\xb9\xe9i\xf4@\x0bK#\xedSl18=E\x96\x88\xd3Sd\xe18=\x85\x97\x93\xd3\xd3\xa8\"sz\x8a\xdfz\x0b\x9a\xaa \x9d\x9e\x82\xca\xd4\xe9):e\xa9O\xa3\xe7\xc9hs\x1c\x90\x98qH\x93\x89\x1f\x874\x08\n\xce\x81\xea\xd3d}\x88\xfa\x04c6[\x82|\x0b\xf3E\xb0N\xb7\xd9\xf5(\xddf\x17\xe7\x05Ex@\x11Z\x10\x14\xa3\x0bA\xd1v|\x94\x01\x19a<\x82\xef~P\x14\xad\\\x18\xa5`\x88\xbd\x13B\xd1(E\xc38eC\xfc]\x11\x8a\xeeW\xfc1\x1e\xd9\x1d\xdc%\xa1(\xfeN E\xa1\x89\xb7}\x1a\xf5Q\xc6l\xe6\xb7\xf1wN(\xba\x07\xd9\xb7\xe96\xbbC\x8a\x9e\x97\x91\x16D\x9c\xa5\x83)l\xf4\x17\x97:\xee,\x8a\xa2Hq\xc3DM\xb7\xd9Y)\xd8\xb8\x87\x9b\xf4\xe0\x152\xf0\x0b\x08\x8a\xb0}\xc1\xeba\xb0\xb2 Ja\x10\xbb\xfaE)\x0e\xe2\x94\x07\xf1k\xdd\x97\x153fe\xbb\x835-~5\x8b3\xafQJ\x0e3f\x82F\xacZ_@\xc6\xb8\x15*P\xb0@\x91B\x82\xe0\x91\x92\xf8\x06\xb8Cc\xa7\xe96;\xab%wy\x19^_\xd3g4\xa5\xdb\xec\x1c\xd3\xc1s\xa9\x08J1\xf1\xea)x\xf7\x16\xc0;A\xc4\xef{*\xf2\x19@\x8a\xdc*\x87\x10\xb5\x83\xbf\xea\x03\x933\xbc\xd20|\x12.|\\i\xa7\x03\xed9\x14|\x07BP\nD\xc0\xc7\xf0Kk\x88M`\x88LU\x88LJ\x08O?\x18\x95h\xe0\xef6N\x95<\x10\x94&\x10\x9c\x10\x100j\x02&\xb0\xa7'\x13\xd1\xb8\x9f\x07\xe3\x0d\xc6GH\xe0\xe8~\x88\xeb\xe4\x0b\x9a\xffVo\xb3\xf3\x91\xed[q\xfa\x99B\x8d\xdbK\x15\xd8W\x13?\x9c\x8a\xcd\x13\x13V&}o\xcd\xb7W\xc8)\xa1;Z\xbc\xccq\xb1PWH\xe0\x05s1\xe7\xfd\xbb,\xbaO\xdb\xa5\xfe\x1e\xd7\xaf\xd9\\\xaf\xb9\x84\xef0\xdd\x92\x92bu\xd9F\xa5\xfe\x9fwbI*\x8d\x11\xf8\xcf\x06W\xbb\xb3>\x1fxw\xf9b(\xf9\x06\xd7k\xb2\xd8\xcb\xa6,[\xe7\xb1\x9e\xa8\xcfKhJ\xfcy\x8b3\xd6G\\U\xa4j\xe5\xe9\xf6\x90fk\xbc\x19\xd4\x7f7.m\xe6\xc5\x8c7p8\x03-s.#\x0b\xcd\x94\xb5\xbb96wU\x96\x01\x0c\x92a\x81k\x94\x17\x1a\xc3i\xf3\x10\x8c\x9e\x81\xc3#py\x02\xec\xf5\xab\xa62\xfa\x82\x1e\xf6\xcb=\xc5\x04=\x87\x8f\xef^\x9fU\x98\x92\xa6\xca0\x94h#\xe7}S\xe6?7\xb8\xd8\x013\x06u\xbe\xcce\xdc\xa3\x16\xa5\x19\x8d\x0c\xc5\x05\x12U\x8e\x8a\xfcoxa>\xd0\xbf\xadHM2R\xc0\xbcY.q\xa5>\xdaL\xdc\x8b!\xfa\x06\x9b\x86\xb6\xc6 \x90\xd9\xb9*0\xa2\xb5\xb9-Rb8:;\x82l\x8d*\x94\xd5\xb8b\xad`\xbe \x04\x8aW\x1b\\\xb6\x16\xf8\xe3\xbb\xd7\xc7\x14\xb6\xa8^\x1b\xb9q\xa1\xda\x82Q\xe6V\x19\xbbeS\x14;\xf8\xb9A\x05\xd3\xe0B\xe8W6\xc55y\x82(\xe4\xa5\x99\xc95\x13\xe5lE\xc8\xaa\xc03\xae\xb3y\xb3\x9c\xbdl*^1\xe1\xfa\xb1\xe8 gK\xd7\xa4)\x160g+\x88\x91\x1f\x82\x0c\x95\xa4\xcc3T\xf09dn\xf9\x04\xcfV\xb3S\xa6Z^\x0b\xe2hv\xc4\x8c\x19\xbf/%\xcb\xf0\xb6\xc6\x8b\xc7\xb3G\xe6\xd7/J\xd82e\xe7\x19>\x85\x1a\xa3\x0d\x85\x866\x88\xa9C\x94\xd5\xda\xe6\x05\x93\xb4&\\\x19\xf3\xbcD\x95\xd9\x07\xe7W\xb4\xec\xb6X\xde\x95R\xaf\xf1\xce\xdc\xb4\xb0u\xf2J\xa0\x86v\xeb\x81\xd6\xf83\xff\xd4\xcf\xcb\xdd\x0c~ \x9f\xf0-\xaeN\xad\xfe\xd5\xc7w\xaf\x95\xff\xc6X1\xb3m|\x96[P\x0c\xd7\xeb\xba\xde^\x9f\x8a\xff\xd2\xebS \x15\x94D\xfez\xcaGc\x86J |v2\x8d\x98\x19\xe2\x1a\x9a\xad,\x88ji\x17W\xb7\xb8\x12\xaa\xd9\xa0\xad\xbcS\x89K^\x93\xb6**\x8f^\xe6\xe2:\x17d\x8e7.IQ\x90O\xf4\xdc\xf2m\x7f\x0f\x17\xcb}\x8f\xd8\xb0\xd8V\x84y\x0d\x8b\xb6\xd3\xdc\xb7\xa1\xb4\xd9\xe0\x85\xa5\xfa\xea\xef\xd9\xe2\xf4\xc3\x87\x0f\x97\xf0\xfdw\x1f\xd4\x05:\x1f\xdf\xbd\x16sl\xc7\x97g\xb3\x0b\xf4\xd7\xe1\xb4\xf8\xb0\xdb\xe2\x9f\xfe\xfa\x93\xf1\x05\xee)7|<\x88\xf1&\x97\x11\xfe\x85\xb6\x15Y4\x19\x06T\x8a%\xcc\x9cg\xf7{x\xbe/ZB\xf9\x8dA\x88\xe9Lx\x10\x19\xca\x98m!\xe4\xa6\xd9\x82<& sD-Y\x90\xc4U\xe5\xe5\xe3\xbb\xd7\\\xc65\xba\xe5Cp\xd3\x99C\x0b1\x89\x90\xea\x12\xfb\xf7-\xc9\x17\x80J\x1b<$\x04\xe4\xe6\xa3\xc2KR\xe1S\xc5\x80\xf1Eu>\xcf\x8b\xbc\xdeA\x89\xf1\x82\n\xcf\x08\xb8\xc9\xabn\xad\xf9\x9c\xa4df\xb6\\a\xfe\x12\x9f\xb338\xf9H\xb1\xaa\xeb\xc4\xb4\xc4\x86'\xb3Yb|\xa2\x12\xadl\xbd\x9fW\x18\xdd0\x1b$\x19\xcf\x1e\x9bG\xd4\x1bR\xe3sq\xb1\xd8\xb2)31\xc3X?\xa4\xed\xca\x9a\xaa\xc2e]\xec:\xb1{\x8b\xb9\xe4\x17:-\x97y\x96\xa3\xc2\xb1\x96\xcd\x9b%T\x98\xadD\xf8\x94\x97\xba\xc9k\xd5hC\xf1B8}j^\x1aY\xcd\xf1*/K\xd6Y\xe6\xe2Z\x16\x97\xdd\x16\xcf\xc4\xf8G\xdb\x9c\xce2\xb2\xb1Y\xe3\xf7|\xa6R \xf5Z\x18\x8arh\xa5\xe0D8\xa5\x807\xdbz'\xa7\xf6c\xf3\"\x98\xaf\xd65\xcc-F\x89w\x9a\xfb\xe9\xf9f[`\xb6\xc8\xf2 \x03t\x8b\xb3|\x99g@\xf1\x06\x95u\x9e\x19RZ\xf9\\\x1d\xe1\x02yl\xdd|\xbd\xa4\x1f\x999\x9ac@b[\xd3qp\x0e\xfc\x18U\xd6hNn\xcdcZ\xaa@N\x05\xed\x0dq\x1e\x92]?/w\xd7\xfb\xbd\x1b*\x01U\xf3\xbc\xae\xd8$6K\xa8e\xa5\xd6\x08T\x109\xf4\x00\xe9?-\xb3\xce|\xa1\x11\x12\xce\xfbn\xe1\xc0\xfdk\xbd:\xc3\xd0\xbcT\x13\xa7\xc8\xe7\\l\xb9\x8eP\xa0\xcdvK*\xbe\x82oQvs\xd6\x94\xec?l\xdd\x16\xe3B?\x83\xe4Bovl\xc8\x12\x9aZ\x186e\x1e(3\xach\xb1\xc8\x85\xad\x80\x15.q\x85j.<\xdbh\xa9JUZ~L\x1e\xf1 \xf5\xed}\xf7\x19\xb1\xc1\x0fO\xce\xe1\x92\xc9\xcf\xec\x82\xec\n\xea\x96+\x7f\xf1\x87?X\x96\xc9W\x84\xc0\x92\x10x\x06\xb3\xd9\xec_\x8c\x8f1aP\xb93?\x80\xca\xdd\x8c\x89\xf1\xaa\"\x9b\x93%!\x8f\xcd\x8f\xcef\xe6\xf5/_\xc2 c\xf5\x91w\xe4\x039\xf9\x1d\xe3\xf5\x18~\xb1\xd8p\x1b\xbf_\xed\xba{\xea\xd0\xdd_\xd0-\x9aLy\xf0\x8c\xfb\x86\xac\x95 4\x94\xd3\x93W\x84\xcc\xb2\x02Q\xeaP\x90\x10\x91\xbd$\xfa\xd8y\xd1,\x83As\xad\xea\xbeq\xa8\xeerW\xafIiQ\x9e\x90\xea\x15!'\xb3\xd9\xcc\xbc\x1a\xb4\x8a;\xb1>\xc3\x07\x1fWk\xacV\x19\x93\x0b\xa1\xd4\x97\xdf\xbd\x7f\xf1\xee\xe2\xf2\xc3\xdbw\x8fm\xf1\xbe\xfd@\xb57,\x9a\xb6\xab\xf3\x8f\x0eu~O,\x05\xec\x98*\xcf\x9f\xc1\xef\xb6\xf3\xd9+B~\x99\xcdf\xbf\x9a\x1fF\xe5\xee\x94\xb9\xa1\xec\x8d\xadp\xa2~D\x15]\xa3\x82)\xd9\xde\x11\x9b\n\x87RXD\xc8\x97\x03\x01>\x96\x9b\xbd\x08\\@>A\xf8S\xff\xf4\x0c\xca\xbc\xb0\x0ep\xbb\\\x86\x91\xcc6\xb7\\\xcf\xca\x16\xab\x8d\x06\xccw{\xb7K\xad\x1e\xe2bP\xbd\xd7+\xa3d\xcc-\xd17u\xacq\xa9\xce\xd8\xfe}\xc6\x7f`\xee\xea1\xa0\xcej\xc7VBY\xd2P\xcbP\x8c\x10}c\xed\xd2R\x16;\xb5\xaf<\x08\x16\xb4n2\xa0e\x8du!CA<\x8eq|v\xacoJ\xae\x89Jd\xbe\xdb\x05,G\xf4\xd1\x92\x90\xd9\x1cU\xbc\xb3\x9f\xcfv\xb3\xbf\x1d -\xf2\xbd\x97\x96\x9fy+\xcaE=b<\xd8r\xa8}\xe4/\xef\xdf\xbe\xd1\xff\xf2\xec\xd9\xb3g\xe61\xc0\xde\xdb\xc7\\\x84\x1fI\x989\x90N\x90\xd8\xd75\x14\xabp\xeb\xaa)\x90\xa1\xdc\xf6!\x1b\xf6\xca\x02\xef\xdd\x96S\xc0\x9b9^,\xf6\x0e\x8c\x888k\xd9!C\xf4\xa6\xe3R\x888\xf3\xf5\xbf3\xd5]\xcb`B/\xe4\xae>\x8e~\x82H\xf3sn\xd9\x80\xa0\xec\x86\xd9\xa0\xfd\x86x\x99\x17\xd8\xbcn(\x9bu\x89+JJ\xeb\xb4\x95\x918~\x9f\xed\x15\xff\xc2\xcf\xe0\x89\x99s\xfb\x02\xcf\xb0\x90\xcf?\x0d_\xc1\x00\xacR\x1dq]\x1e\x9d\xc3\x91n\xd6\xf6\xd50\x13\xbd<\xb2\xdd$}\xc4\xfb\xf7\x06m\x18\xcf\x7f\x15]\xf87\xeb\x0b\xac\x7f\x83\xe7C;y\xb1\x94\x1b\xae\xfeX\x13\xa3!\xa7\xf0 \x17\xc5W7%\xf9Tr;\xb3\xe6\x97mg\x0d\xad\xc9&pr\xf5\x87\xfc\xa9p\xe0\x07\xf3@\xdd\xaa\xdc\x8a\xc3\x06\xb0as\x85\xc4\x90\xd67v\xcd'\xa3\x1a\xe7kR,d\x89\\.\xb9\x98\xcay\xd9\xce\x0f\x10\x11@=+1e\xf4\xedp\x11f\xed\xe2|\xc2\xec\x9aR\xe1AhHEL\x7f\xfa\xebO\x8f-\x13i\x8a1\xd7o\xd0>\xec\xb8\xaa\x18\xcb'\xb3\xa7O\x9e\xd2#\xcb\x10\x12\xff\xad\xd1\xaa\x03\x1a|\x05\xefqu\x9bgL{\xc7g\x19\xa1\x1bB\xcf\xe6\x88\xe2\xb3=nvv\xfbd\x8ek\xf4\xe4\x8c\xe3x\xf4\xec\x17\x91\x97\xf4\xeb\xb1`\xb3\xda\x1f\x0f\xa5\xcdf\x83\xaa\xdd9|\x8f\x05\xfa\xf4\xedN\xdc\xa4\x0d?7\xb8\xca1\x95P S\xf4*\xbf\xc5\xa5LqRV\x8bl\xb1\xe8\xf3\xc5\xe2\x90\x87|F\x01O\x9dN\x1c?\xfd\xfa\xebc3z\x05\xb4\xc92L\xe9\xb2)\xee\x12\xb62\xe76\x19\x99\x81\x13\xca1'\xd48\xe3\x17\x8e\xe8\x85W\xae\x92UrpJ\x0f\xee\x9c\xa4\xda+\x17\xc9'\x07\xc9\x9e{\xe4T\x17\xb8U\xe6\x99cd\xcd-2\xd4i\x97\xef\x1c\xbfd&5C5^\x9c\xc3\x96\x87\x02\xb9swM\x177\xa2*\xf15\xe4%\xad1Z\x1cO:\xcc\xeev\x0c8\x0b\xf2\x87\x96\xdew\xca\x04^r\x81_\xe1|\xaf\xb1\x03\x9d\xf1\xe3\xac+\xef,{?u\x93>\xd1@A~\xe5\xe9\xa1_n\xde\xd6\xd5^!z\xdf\xc2\xf2\xbe%\xe4\xfb%\xe1-\x0cC\x8b\xc5\x87\x96\x85w\x17\x80\xf7\xf8\xa4_,\xe1\xd7^\x92=\xa8\x19Wf\x95gA\xf5 g\xb4; \xd5\xab\x83\xe0\xb3 \x08\xf2ZK\x15y\xf5\x14\xbc{\x0b\xee5\xb6}\xcck\xadU\xe43\x8e\x14\xb9U\x0e!j\x07\x7f\xd5{\xae\xc9\x83\x87\xedy\xbf\xfe\xa5\xbd\xbdz\xe4\xd5\x93\xf0\xc2\xdc\x1e%\xb8\xa7\x12\xcf\xbb\x82\xf3T\x0d\x0e\xf5\xe1\xacem\xad\xcc\x1aV\x85z\xaa>\xf8\xd6\x90\x9e\xaa=w\x05\xe8\xa9Z\n\xa8\xdfEL\x9c\x90\xed\xa9\xa0\xa0\xeb\x00\xc2\xf6\xca\x82lGJ\xf4\xe4\x8e\x8b\xf4)\xd0\xd6A\x84\xbd\x03\xef\xed\xfc\x9e\">\x1f\xf8\xbadC\n\x8a\xb5\xf4)B{\x10\xa9AF\x9eQ\x99>\xc5\x8c;E\xe1SBQ\xe8\x07\x87\xf8\x8f\x0e\xb1\x1f>0\xd2\xd3'\xaf\xb8O\x9f\xbc\x8f\xa3*\x8aP\x88o\x10\xb5O\xc1\xa7u\x15\x8d\x90\xd0\xfbS\xed\x85\xcb\xcb\x05\xfe\x1c&Z\xd8\xb8\x0f\xb7\xb7\xde\x07|\x15\xdd\xbd\xc6b\\\x10~\xca\xb7\xcd\xbe\xe0g\x80+\xcc<\xe6Sy\xacc\x93\xf3#)^\xcc\xc4\xe3\xdc\xe1\x161\xb5N\xf5\x94%1\xa4@\x0d\xa9\xe3\xb7\xb8\x1e\xe5\x9e\xfd\xdc\xe7\x03\x04\xda\xebP;\x9d<\xfb\x01%\xcf>y\xf6nJ\x9e\xbd\xebiH\x9e}\xd8\x1a((y\xf6F\n\x9f\x12\x8aB?8\xc4\x7ft\x88\xfd\xf0\xc9\xb3W\x94<{A\xe1\xf66y\xf6\x87\xf4\xa5=\xfb\x07X\x1ds?f\x1f\x86_*\x80\x88\x0d\xd2\xb1\x95:\xdb\x0f#\xddj\xa8?\x11\xc6kY\xe4\x19\xaf\xd6\xc9F\xadc\xcc\xf1\x1a\x9eW\xa2F\xe7\x15\xe2\x05D\xef\x1b\xb6\xea\xf4\xe0\xca#\xb3TP\x80,\x10(\x0fH\xd3\x88\x17\x81^U\xa0L\x10!\x178\x12\x8f\xf5\x14!\x18D\n\x07>\xa9\xcbz\nMh\xd6Sd_aD\x7f\xc1/%ZO\x81\x06kH\xca\x809\xd3\xa7\xf5\xe4L\xaa\xd6\xd3}\n\x1dj\x82\x87\xe4\x97\xac\x1d\xcc\xb6\x9f\xdc=H\xe1\x0e\xe6\xe6\x99\xf2\xad'\xdfD\xf0`\xc6\xbd\xc4\xf1\xe0\xf4p=\x85&\x8d\xeb\xc9\x9dJ\xae\xa7\xe8\x81\x1c\x16\xd1Q\x14\xdd\\\xa8\xcf\xd4%{\xe2\xba\x9e&\x10\xd4\xd7\xa5\xea\x93g\xea\xbb\x9e\xee\xc9\xf6\xc7\xc4\x0b`\x9c\x92!|k\xd5\xa7\x11\x81#E#\xb4\x0d#5\x0e\xb1\x01%E1\x1b\xec!\xc5\xcfHE\xb1\x03\x07\xc6\x0f\x1e\x18;\x80F\x05\xa0\x14E\x04\xa2\x14\xf9\x1f8\xd0\xd3\x08\xfd\x8d\xd0[\xf8\x91\x05=y\x1cd\xd0\xd3}t\xdb\xfb\x18\x81\x9e\xeeCdw\xd6\xad\x99|\xcf_\x043\xb6\x9d\xd7\xd0S\xd8)\x0e=\xdd\x87\xfa}\xcf\x81\xe8\xe9>$v\x9f$\xd1\xd3}\xc8\x1ap\x16EO\xf7!\xb4\xe7i\x16=\xdd\x87\xc0a\xe7a\xf4\xe4\x7fJFO_\xbe\xdfcv\xe7\x9e\xc7r\x82x\x9a\x8f\xf0\xe8\xc9v=\x91\x9e\"\xdd\xe0X\xf7\xf7\xefh\xe3\x19\x94b\xa0h\x8cg\x1e\x0e\x81)J;N\x1f\x8a\xb4 \x82\xd2\x8e3z\\+\x8a\x9f\x8a\x8ab\x07\x0e\x8c\x1f<0v\x00\xdd\xf7\x8e\xd3\xe7j+= \xcd\xf9\xde\x1d\xdb%\xe7\x816=\x8d\x9a(\xe3\xa6I\xd0EZz\x1a=\xd0\xc2\xd2H\xfb\x14{\x15\x97\x9e\"/\xe8\xd2S\xe4\xb5]z\n\xbf\xccKO\xa3\xae\xf8\xd2S\xfc\xd6[\xd0T\xd7\x81\xe9)\xe8\x920=E\xa7,\xf5i\xf4<\x19m\x8e\x03\x123\x0ei2\xf1\xe3\x90\x06A\xc19P}\x9a\xac\x0fQ\x9f`\xccfK\x90\xef\xb5h\x11\xac\xf3\x12\x90\xf5\xb24=\x8d\xe9\x93\xc7\xc5jA\xfc\xf4\x97\xb0\xb5\xd7\xad\x05\xf1\xb2]\xcd\xa6\xa3\xbd\x8d\xa0\xd8{\xbb\x17\xb1\xee\xc7\xac\xf6a7\xe7+\x8a\xf3\x82\"<\xa0\x08-\x08\x8a\xd1\x85\xa0h;>\xca\x80\x8c0\x1e\xc17\xef+\x8aV.\x8cR0\xc4\xde\xc8\xafh\x94\xa2a\x9c\xb2!\xfe\xa6~E\xf7+\xfe\x18\x8f\xec\x0en\xf2W\x14\x7f\xa3\xbf\xa2\xd0\xc4\xdb>\x8d\xfa(c6\xf3\xdb\xf8\x1b\xff\x15\xdd\x83\xecJ\xea\x10a#\xcdM\xac\xa1\x89\xb4\xe3#\x94\x19=/#-x\xa4Ba\x84Ra\x9c\xed\x1e\xa1\\\x18\xa3`\x18k\xb5\xefO\xf0x{}g\xd6z\xac\xad\x1ec\xa9G|\x888K\x07S\xd8\xe8/.u\xdcY\x14E\x91\xe2\x86\x89\xca\xf6a\xa4\xbc\xf2\x07\xc1\x02\xa5\n\x93f\xbe\xfb\x1b*\xeb\xbc\xc4Wa;\xa2\xb0\x9dP\xc0\x0e(\xd8\xb8\x87\x9b\xf4\xe0\x152\xf0\x0b\x08\x8a\xb0}\xc1\xeba\xb0\xb2 Ja\x10\xbb\xfaE)\x0e\xe2\x94\x07\xf1k\xdd\x97\x153fe\xbb\x835-~5\x8b3\xafQJ\x0e3f\x82F\xacZ_@\xc6\xb8\x15*P\xb0@\x91B\x82\xe0\x91\x92\xf8\x06\xb8Cc\xa7\xaf\xd9*\xfa\x82\x9f\x05|\xce\x8f\x02\x8e=\xa1(\x0b\x8fv\xce\xcc\xa2\xba\xc6\x9b-?\x9dX\x13\xd8\xe4\xb4\xc0h\x01H\x9cCt\xf2\x13\xe7\x14u\x01\xd3N\xfa\xaf^\xf1N\xeb\xee\xb6\xe4./\xc3\xebk\xfa\x8c&G\xa2\x8ch\xc7\x95:\xe0\x93\xfc\xe2Nsq\xaa\x0d\xbcT\x07^\xb9\x06^\n\x04\xff\xa5\"(\xc5\xc4\xab\xa7\xe0\xdd[\x00\xef\x04\x11\xbf\xef\xa9\xc8g\x00)r\xab\x1cB\xd4\x0e\xfe\xaa\x0fL\xce\xf0J\xc3\xf0I\xb8\xf0q\xa5\x9d\x0e\xb4\xe7P\xf0\x1d\x08A)\x10\x01\x1f\xc3/\xad!6\x81!2U!2)!<\xfd`T\xa2\x81\xbf\xdb8U\xf2@P\x9a@pB@\xc0\xa8 \x98\xc0\x9e\x9eLD\xe3~\x1e\x8c7\x18\x1f!\x81\xa3\xfb!\xae\x93/h\xce!p\x0fNz@\xd8O\"\x0f\xc8\xdb\x00c\x1b\x18\xa2C'n(_{\xb1\xda\xe1gRw\xb2\xbd\xcf\xcb\x8c\xa7\xbe\xd3\x0d\xa1_\xd1\xc5\x0d|=\xfb\xe3?\xa7\xfb\xd7\\\x8b\x8c[.\xf0+6\xe0=C\xd4\xfcp\x9e\xc5w\x96\n\x98\xbaI\xbf \x00\xdeG\xfa!\xdd\xbf\xd6'\xf7\xa1y\x8fO\xfa\xc56I\xe9\xfe5w\x07\xc1\x7f\xc1K{%\xa7\xca!D\xed\xe0\xaf\xfa\xbb\xd8+\xf9\x1f\x87\xf6\xea\x91WO\xc2\x0f3{\x1c[\x9eJ<\xefS\xafS58\xd4\x87\xf3\xfco\xba\x7f\xcd\xe3\xd4\xecT-\x05\x9cy\x9d\xaaI\xcf\x13\xabS5\x17v\xde\xd4\xffd\xa9\x97|\xfe\xbe\xda\xb0a\xb6\x87\x12\xb7n\xe5\xab\xbcD\xd2qj\x1f\xb3p\x92\x0cN\xa5r\xd8\xae\x86_\xd6\xff-\xce\xd6\xdf<\x95\x12[+G_\x94\xf0\x01\xab+\xdbO\xa1^3Yd\x0d\xebk\xa6jz}\n\xf3\xa6V\x8e\xe0\xfb\x97\xffa\xbbX\xfe\x13w9oqUC^CM\x86\xb2\xd8Da\xbe\xe9\x1c\xd75\xae\xe0\xe3\xff\xf5\xd8\x10\xba\xcf\xca\xee{\x96n\xb3\xe3\xe45\x87\xfc\x07r\xba\xcd\xce\xe6\xe3\xa4\xdb\xec\xd2mv\x87\xe4+ \xa4\xdb\xec\x0c\x94\xee\xbc\x90\x94\xee\xbc\xe8P\xba\xf3\xc2\xeb\x9d\xa0\x82\x14a\x91\x07A>\x18|\x9f\xdcQ\xa6>\x05\xda:\x88\xb0w\xe0\x1d\x1c\xd9S\xc4\xe7\x03_\x97lHA\x91\xab>Eh\x0f\"5\xc8\xc83\xc6\xd5\xa7\x98q\xa7(|J(\n\xfd\xe0\x10\xff\xd1!\xf6\xc3\x07\xc6\xcd\xfa\xe4\x15E\xeb\x937 \xaa(B!\xbe!\xe9>\x05\xe3\xc5\x8aFH\xe8\xfd\xa9\xd2\x9d\x17a\x1a\x8bqA\xfe\xae\xef\xbcH\xb7\xd9\xe9(y\xf6\x92\x92g\xdf\xa1\xe4\xd9{\xbd\x93<{\xd7\xb3\x8aB\x1d\xbd\x88\xcf\x07\xe1k\xa0\xa0\xe4\xd9\x1b)|J(\n\xfd\xe0\x10\xff\xd1!\xf6\xc3'\xcf^Q\xf2\xec\x05\x85\xdb\xdb\xe4\xd9\x1f\xd2\x97\xf6\xec\x1f\xe0\xf9\xact\x9b\x9d\x99B\xc7g\xba\xcd\xce\xc54\xddf\x17\"\x17\xa4\xdb\xec\x1c\x14\xd9W\x18\xd1_\xf0K0\xd7S\xa0\xc1\x1a\x922`\xcedt=9S\xd4\xf5t\x9fB\x87\x9a\xe0!\xf9\xa5\xbe\x07\xb3M\xb7\xd9\xb9\x92\xed\xf5\x14\x9a\x82\xaf'wb\xbe\x9e\xa2\x07rXDGQts\xa1>S\x97\xd2mv\x1e4\xc6\xf6\xc7\xc4\x0b`\x9c\x92!|k\xd5\xa7\x11\x81#E#\xb4\x0d#5\x0e\xb1\x01%E1\x1b\xec!\xc5\xcfHE\xb1\x03\x07\xc6\x0f\x1e\x18;\x80F\x05\xa0\x14E\x04\xa2\x14\xf9\x1f\xdf\xd0\xd3\x08\xfd\x8d\xd0[\xf8\x01\x10=y\x1c\x0b\xd1\xd3}t\xdb\xfbP\x86\x9e\xeeCdw\xd6\xad\x99|O\xb3\x043\x0e\xbf\xcb)\xecL\x8c\x9e\xeeC\xfd\xbe\xa7j\xf4t\x1f\x12\xbb\xcf\xe5\xe8\xe9>d\x0d8\xd9\xa3\xa7\xfb\x10\xda\xf3l\x90\x9e\xeeC\xe0\xb0\xd3Ez\xf2?s\xa4\xa7/\xdf\xef1\xbbs\xf7 \x9dp\x0b\x98n\xb3\xe34\xc1@\x08ws\x83R\x0c\x14\x8d\xf1\xcc\xc3!0Ei\xc7\xe9C\x916AP\xdaqF\x8fkE\xf1SQQ\xec\xc0\x81\xf1\x83\x07\xc6\x0e\xa0\xfb\xdeq\xfa\x14W\xd3\x93\xd0\x9co\xf5\xe2.9\x0f\xb4\xe9i\xd4D\x197M\x82J\xb9\xe9i\xf4@\x0bK#\xedSl18=E\x96\x88\xd3Sd\xe18=\x85\x97\x93\xd3\xd3\xa8\"sz\x8a\xdfz\x0b\x9a\xaa \x9d\x9e\x82\xca\xd4\xe9):e\xa9O\xa3\xe7\xc9hs\x1c\x90\x98qH\x93\x89\x1f\x874\x08\n\xce\x81\xea\xd3d}\x88\xfa\x04c6[\x82|\x0b\xf3E\xb0N\xb7\xd9\xf5(\xddf\x17\xe7\x05Ex@\x11Z\x10\x14\xa3\x0bA\xd1v|\x94\x01\x19a<\x82\xef~P\x14\xad\\\x18\xa5`\x88\xbd\x13B\xd1(E\xc38eC\xfc]\x11\x8a\xeeW\xfc1\x1e\xd9\x1d\xdc%\xa1(\xfeN E\xa1\x89\xb7}\x1a\xf5Q\xc6l\xe6\xb7\xf1wN(\xba\x07\xd9\xb7\xe96\xbbC\x8a\x9e\x97\x91\x16D\x9c\xa5\x83)l\xf4\x17\x97:\xee,\x8a\xa2Hq\xc3DM\xb7\xd9Y)\xd8\xb8\x87\x9b\xf4\xe0\x152\xf0\x0b\x08\x8a\xb0}\xc1\xeba\xb0\xb2 Ja\x10\xbb\xfaE)\x0e\xe2\x94\x07\xf1k\xdd\x97\x153fe\xbb\x835-~5\x8b3\xafQJ\x0e3f\x82F\xacZ_@\xc6\xb8\x15*P\xb0@\x91B\x82\xe0\x91\x92\xf8\x06\xb8Cc\xa7\xe96;\xab%wy\x19^_\xd3g4\xa5\xdb\xec\x1c\xd3\xc1s\xa9\x08J1\xf1\xea)x\xf7\x16\xc0;A\xc4\xef{*\xf2\x19@\x8a\xdc*\x87\x10\xb5\x83\xbf\xea\x03\x933\xbc\xd20|\x12.|\\i\xa7\x03\xed9\x14|\x07BP\nD\xc0\xc7\xf0Kk\x88M`\x88LU\x88LJ\x08O?\x18\x95h\xe0\xef6N\x95<\x10\x94&\x10\x9c\x10\x100j\x02&\xb0\xa7'\x13\xd1\xb8\x9f\x07\xe3\x0d\xc6GH\xe0\xe8~\x88\xeb\xe4\x0b\x9a\xffVo\xb3\xf3\x91\xed[q\xfa\x99B\x8d\xdbK\x15\xd8W\x13?\x9c\x8a\xcd\x13\x13V&}o\xcd\xb7W\xc8)\xa1;Z\xbc\xccq\xb1PWH\xe0\x05s1\xe7\xfd\xbb,\xbaO\xdb\xa5\xfe\x1e\xd7\\\xb6ow\xe2\xf6\x81w\x98nII\xb1\xban\xa3R\xff\xcf\xbb\xb1$\x95\xc6\x0c\xfcg\x83\xab\xdd\xd9\x90\xd3P\xf2w\x97/`\x83\xeb5Y\xec\xe5S\xd6\xad\xf3hO\xdc\xe7%4%\xfe\xbc\xc5\x19\xeb'\xae*R\xb5\x12u{I\xb35\xde\x0cj\xc0\x1b\x977\xf3\x82\xc6\x1b8\x9c\x85\x96y\x97\x91\x85f\xda\xda]\x1d\x9b\xcb*K\x01\x06\xc9\xb0\xc05\xca\x0b\x8d\xf1\xb4y F\xef\xc0\xe1\x15\xb8\xbc\x01\xf6\xfaUS\x19\xfdA\x0f\x1b\xe6\x9ef\x82\x9e\xc3\xc7w\xaf\xcf*LISe\x18J\xb4\x91s\xbf)\xf3\x9f\x1b\\\xec\x80\x19\x84:_\xe62\xf6Q\x8b\xf2\x8cF\x86\xe2\x12\x89*GE\xfe7\xbc0\x1f\xea\xdfV\xa4&\x19)`\xde,\x97\xb8R\x1fm&\xee\xc6\x10}\x83MC[\x03\x05\xc8\xec`\x15\x18\xd1\x83\xb9\xb2'Rb8:;\x82l\x8d*\x94\xd5\xb8b\xad`\xbe\x11\x04\x8aW\x1b\\\xb6V\xf8\xe3\xbb\xd7\xc7\x14\xb6\xa8^\x1b\xb9q\xa1\xda\xa2Q\xe6V\x19\xbbeS\x14;\xf8\xb9A\x05\xd3\xe0B\xe8W6\xc55y\x82(\xe4\xa5\x99\xc95\x13\xe5lE\xc8\xaa\xc03\xae\xb3y\xb3\x9c\xbdl*^5\xe1\xfa\xb1\xe8 gK\xd7\xa4)\x160g\xab\x88\x91\x1f\x82\x0c\x95\xa4\xcc3T\xf09dn\xf9\x04\xcfV\xb3S\xa6Z^\x0f\xe2hv\xc4\xcc\x19\xbf3%\xcb\xf0\xb6\xc6\x8b\xc7\xb3G\xe6\xd7/J\xd82e\xe7\x19>\x85\x1a\xa3\x0d\x85\x866\x88\xa9C\x94\xd6\xda\xe6\x05\x93\xb4&\\\x19\xf3\xbcD\x95\xd9\x0f\xe7\xd7\xb4\xec\xb6X\xde\x97R\xaf\xf1\xce\xdc\xb4\xb0u\xf2Z\xa0\x86vk\x82\xd6\xf83\xff\xd4\xcf\xcb\xdd\x0c~ \x9f\xf0-\xaeN\xad>\xd6\xc7w\xaf\x95\x0f\xc7X1\xc3m|\x96[P\x0c\xd7\xeb\xba\xde^\x9f\x8a\xff\xd2\xebS \x15\x94D\xfez\xcaGc\x86J |v2\x8d\x98\x19\xe2\x1a\x9a\xad,\x8aji\x17W\xb7\xb8\x12\xaa\xd9\xa0\xad\xbcW\x89K^\x93\xb62*\x8f`\xe6\xe2J\x17d\x8e9.IQ\x90O\xf4\xdc\xf2m\x7f\x0f\x17\xcb}\x8f\xd8\xb0\xd8V\x84y\x0e\x8b\xb6\xd3\xdc\xbf\xa1\xb4\xd9\xe0\x85\xa5\x02\xeb\xef\xd9\xe2\xf4\xc3\x87\x0f\x97\xf0\xfdw\x1f\xd4%:\x1f\xdf\xbd\x16sl\xc7\x97h\xb3\x1b\xf4\xd7\xe1\xb4\xf8\xb0\xdb\xe2\x9f\xfe\xfa\x93\xf1\x05\xee-7|<\x88\xf1&\x97\x11\xfe\x85\xb6\x15Y4\x19\x06T\x8a%\xcc\x9ck\xf7{x\xbe/\\B\xf9\xadA\x88\xe9Lx\x11\x19\xca\x98m!\xe4\xa6\xd9\x82<* sD-\x99\x90\xc4U\xe9\xe5\xe3\xbb\xd7\\\xc65\xba\xe5Cp\xd3\x99C\x0b1\x89\x90\xea\x12\xfb\xf7-\xc9\x17\x80J\x1bD$\x04\xe4\xe6\xa3\xc2KR\xe1S\xc5\x80\xf1Eu>\xcf\x8b\xbc\xdeA\x89\xf1\x82\n\xef\x08\xb8\xc9\xabn\xad9\x9d\xa4df\xb6\\a\xfe\x12\x9f\xb338\xf9H\xb1\xaa\xed\xc4\xb4\xc4\x86'\xb3Yb|\xa2\x12\xadl\xbd\x9fW\x18\xdd0\x1b$\x19\xcf\x1e\x9bG\xd4\x1bR\xe3sq\xb9\xd8\xb2)31\xc3X?\xa4\xed\xca\x9a\xaa\xc2e]\xec:\xf1{\x8b\xb9\xe4\x97:-\x97y\x96\xa3\xc2\xb1\x96\xcd\x9b%T\x98\xadD\xf8\x94\x97\xbb\xc9k\xd5hC\xf1B\xb8}j^\x1aY\xcd\xf1*/K\xd6Y\xe6\xe6Z\x16\x97\xdd\x16\xcf\xc4\xf8G\xdb\x9c\xce2\xb2\xb1Y\xe3\xf7|\xa6R \xf5Z\x18\x8arh\xa5\xe0D\xb8\xa5\x807\xdbz'\xa7\xf6c\xf3\"\xc8\xbcS\x98[\x8c\x12\xef4\xf7\xd5\xf3\xcd\xb6\xc0l\x91\xe5\x13\x06\xe8\x16g\xf92\xcf\x80\xe2\x0d*\xeb<3\xa4\xb5\xf2\xb9:\xc2\x05\xf2\xd8\xbe\xf9zI?2s4\xc7\x80\xc4\xd6\xa6\xe3\xe0\x1c\xf81\xaa\xb4\xd1\x9c\xdc\x9a\xc7\xb4T\x81\x9c\n\xda[\xe2<$\xbb~^\xee\xae\xf7\xfb7T\x02\xaa\xe6y]\xb1Il\x96P\xcbJ\xad\x11\xa8 r\xe8\x01\xd2\x7fZf\x9d\xf9B#$\x9c\xf7\xdd\xc2\x81\xfb\xd7zu\x86\xa1y\xa9&N\x91\xcf\xb9\xd8r\x1d\xa1@\x9b\xed\x96T|\x05\xdf\xa2\xec\xe6\xac)\xd9\x7f\xd8\xba-\xc6\x85~\x06\xc9\x85\xde\xec\xd8\x90%4\xb50l\xca\xae\xd6X\xad2&\x17B\xa9/\xbf{\xff\xe2\xdd\xc5\xe5\x87\xb7\xef\x1e\xdbb~\xfb\x81joX4mW\xe7\x1f\x1d\xea\xfc\x9eX\x8a\xd81U\x9e?\x83\xdfm\xe7\xb3W\x84\xfc2\x9b\xcd~5?\x8c\xca\xdd)sC\xd9\x1b[\xe1D\xfd\x88*\xbaF\x05S\xb2\xbd#6\x15\x0e\xa5\xb0\x88\x90/\x07\x02|,7{\x11\xb8\x80|\x82\xf0\xa7\xfe\xe9\x19\x94ya\x1d\xe0v\xb9\x0c#\x99mn\xb9\x9e\x95-V\x1b\x0d\x98\xef\xf6n\x97Z=\xc4\xe5\xa0z\xafWF\xc9\x98[\xa2o\xeaX\xe3R\x9d\xb1\xfd\xfb\x8c\xff\xc0\xdc\xd5c@\x9d\xd5\x8e\xad\x84\xb2\xac\xa1\x96\xa1\x18!\xfa\xc6\xda\xa5\xa5,vj_y\x10,h\xddd@\xcb\x1a\xeb\x82\x86\x82x\x1c\xe3\xf8\xecX\xdf\x94\\\x13\x95\xc8|\xb7\x0bX\x8e\xe8\xa3%!\xb39\xaaxg?\x9f\xedf\x7f;\x12Z\xe4{/-?\xf3V\x94\x8bz\xc4x\xb0\xe5P\xfb\xc8_\xde\xbf}\xa3\xff\xe5\xd9\xb3g\xcf\xccc\x80\xbd\xb7\x8f\xb9\x08?\x920s \x9d \xb1\xafk(V\x01\xd7US C\xc9\xedC6\xec\x95\x05\xde\xbb-\xa7\x807s\xbcX\xec\x1d\x18\x11u\xd6\xb2C\x86\xe8M\xc7\xa5\x10\xb1\xe6\xeb\x7fg\xaa\xbb\x96\xc1\x84^\xd8]}\x1c\xfd\x04\x91\xe6\xe7\xdc\xb2\x01A\xd9\x0d\xb3A\xfb\x0d\xf12/\xb0y\xddP6\xeb\x12W\x94\x94\xd6i+#q\xfcN\xdb+\xfe\x85\x9f\xc1\x133\xe7\xf6\x05\x9ee!\x9f\x7f\x1a\xbe\x82\x01X\xa5:\xe2\xba<:\x87#\xdd\xac\xed\xaba&zyd\xbbM\xfa\x88\xf7\xef\x0d\xda0\x9e\xff*\xba\xf0o\xd6\x17X\xff\x06\xcf\x87v\xf2b)7\\\xfd\xb1&FCN\xe1\x13.\x8a\xafnJ\xf2\xa9\xe4vf\xcd/\xdc\xce\x1aZ\x93M\xe0\xe4\xea\x0f\xf9S\xe1\xc0\x0f\xe6\x81\xbaY\xb9\x15\x87\x0d`\xc3\xe6\n\x89!\xado\xec\x9aOF5\xce\xd7\xa4X\xc82\xb9\\r1\x95\xf3\xb2\x9d\x1f \"\x80zVb\xca\xe8\xdb\xe1\"\xcc\xda\xc5\xf9\x84\xd95\xa5\xc2\x83\xd0\x90\x8a\x98\xfe\xf4\xd7\x9f\x1e[&\xd2\x14c\xae\xdf\xa0}\xd8qU1\x96OfO\x9f<\xa5G\x96!$\xfe\xbbE\x15\xda\xe0\x1aw\xb3\x85\xbf\xe2\x96\xf7\\&\x1duX\xe4\xe5\xf90\x94]\xe1\x9f\x9b\xbc\xc2\x8bs\xa8\xab\xa6\xabt\xc3\x86Z\x97BR\xa3U\xaf\xf5\xf7\xb8\xba\xcd3\xc6\xec,#tC\xe8\xd9\x1cQ|\xb6\x87\xef\xcen\x9f\xccq\x8d\x9e\x9c\x95d\x81\xaf\xf2rI\xc4\xeb\xab\xfd\xf1T\xdal6\xa8\xda\x9d\xc3\xf7\xb8~C\x16\xf8\xa2\\\x12\xf8\xb9\xc1\x95\xc2\x1dd\xe0\x06\x18\x0b^\x0dK\xd9J\xb2\xc5B\xd3\x17\x8b\xde\xdb\x8fT\x87\x05\xd2\xd5\x91\xf8\xf8\xe9\xd7_\x1f\x9b\xe12\xa0M\x96aJ\x97Mq\x978\x99tH\xae\x06:\xe9\x92\x91+8A$\xb5\xe9\xbf\xb2VT\xb76\x00\xceF\xf8\x13O-\xa0\xbfG\x94f?\xc4\xac%\xbd\x1dE\xd2\xa7k\xc8Z\xd6|\x9af\xfa_\xde\x90Q\xe7h\xaa\xc8i\x8dK\x9e\xf3\x11\xf5~\x89\xebO\xa42h\xd4\xf1\xae\xc7\x902\xbe\x9b\xadQYb\x1d\xd0\xea\xf1\xb23\xa6\xb7!e~c\xca\xdes0\xe7\xa1\xd1;\x9b&\xf5g\xd75>\xce\xbe\x03T\xdb\xcc\x9d\xe5c\xe1\xd3\xa9\xb7n\xb6\x0b#\x8c\x0e_\x86bt\x8f\xb6\xdb\xab\xe8\x97\xc7\x0c\xc7U\xee\x93\nl|}\xde\xe4\xc5\xe2\xaa\xbf\x1e\x06\xbc\xbe\">\xe6\xd9\xd1\xfa\x02o\xad\xad\x9b\x13\x1c\xad\xc9\x8d\xce1\xef\x1a\x0c\xf2\x19T[\xd3J=F=\x7fL$\xe1m\xc8\xa2)\xb0\x1d\x9e\xf7\xb8B$\xaaQ\xc9\xd7\xf8<\xb5gX\x86\xb5\x99\xadqvC\x1b\xbd\x9f\xdf>\xf5\xa3\x90,\xefl\"\x99\x13\xfc_B\xd2\x0b}\xc9P\xe1\xa2]\xd1\xc5\xcd\xa8\xc1\xd7\nq\xfc>/3^\xab\x94\xf1\xfd\x8a.n\xe0\xeb\xd9\x1f\xbf9>x\xa7\xe7eud<\x90\x9f\xfdO\xc7\x91k\xb3\xa9Z0\xc1\xc8U\x97\x99u\xc0\xc6\x96\x94\xb5O\xc3\x1a\xb8\x91\x8a\xde]\xbe\x18\xee\x1eR>V\xca\xc7r\xcem\x1f<\x0fR>V\xca\xc72>\x99\xf2\xb18\xa5|\xacCJ\xf9X)\x1f\xcbD)\x1f+\xe5cqJ\xf9X)\x1f+\xe5c\xa5|,A)\x1f+\xe5c\xa5|\xac\x94\x8fe\xa2\x94\x8f\x95\xf2\xb1R>V\xca\xc7\xea\xd0\x14\xb91)\x1f\x8bS\xca\xc7\xfa\xad\xe4c\xc5\xe7B\xd1]\x99\xe5\xa5,\xf2b\xc8\x84z/\x9ei\x13\xa1x\xf2\x93|\xd1\x94\xff$\xdf\x91\xbf>\xd8\xf4\xa7^\xf7\xbb$x\xcd )0\xea\x07\x82\x9cP\x9f\xecz \xd2\xd7W\x98\xa2TiARB\xf6\x12\xb2\xb7\xa7\x84\xec%doO \xd9\xab\x13\xb2\xa7\xa7\x84\xec)J\xc8^B\xf6\x12\xb2\xe7\xe9%%d\xaf\xa5\x84\xecu)!{ \xd9\xd3PB\xf6\xb4\xcf$d/!{\x06J\xc8^B\xf6\x12\xb2\x97\x90\xbd\x0eM\x81\xb2$d\x8fSB\xf6\x12\xb2\xb7/\x7f\x8ekzV\xa0\x1a\xd3\xda\n\xf3\xbd\xe6\x8f\xb4\xf7A\xbd\xc7u\x8b\xf8\x89\xb7\xf7\x15\xd5\xbf\xa2\xb86!\x7f\x87l\xe4\x83\x0f\x16\x04\x14\xd7]\x98.\xcb\xb1F\x1elw\x9b\xd8\xae\xd4\xab\xef\x01er\x9c\xd9vFX\x1c\xf7\xd39\xa4\x03\x0f \xc1\x03\x0b\x03\x1fQ\x05\xf9\xc4T\x14\x85\xe2bVf\xfa\xe0\x88e\xaf05>\x06n\x8c\x0c\"p2{\x07P\xbd\xf6\xc6\xca`*\xbc\x0c\"13+C\xa6\\o\xdc\x0c\xc6cg\x10\x8c\x9fYY\xc9\xb8~\x10\x86\x06S\xe3h\x10\x88\xa5A(\x9ef\x1f\xd9-\xd6\xe6\x8b\xa9\xc1\xd4\xb8\x1a\xf8ak0%\xbe\x06\xa316\x88\xc3\xd9`*\xac\x0d\xa2\xf06\xfbt@\x14/\xdc\x98\x1b\xdc\x0d\xee\x06w\x88\xbd\xc1\xdd\xe0o\x10\x88\xc1A\x1c\x0e\xe72\xc1~X\x1cL\x8b\xc7A\x00&\x07\xe1\xb8\x1cD`s\x1e&\xf3\xb1\x07>\x07S`t\xe0\xc2\xe9\xc0\xdf=\xf3\xc0\xeb \xd0\x8b\x0b\xc6\xed\xac\xdc8\xa6\xe7\x81\xddA\x80\x94\x13bx\x10\x84\xe3\xc1\xd4X\x1eD\xe2y\xf6qE\xdd\x98\x1e\xc4\xe3zF~\xacE\x17\xb6\x07\x93\xe1{\xe0\x0fS\x81\x0f\xce\x07aX\x1f\xb8\x82\xf3\x91\x98\x1fx\xf0\xb5\xc4\xff&\xc2\xff J\xb9\xfe8 x\xf42\x02\x0f\x84XL\x10\xecZ\x9d\x0e\x1b\x04\x7f|\x10<1B\xf0\xc6 \xc1O\xeb\xe1x!\x04a\x86`\xc5\x0da*\xec\x10B\xf1C\x18\x89!\x82\x87z\x03\xb0D\xb8\x0b<\x11|d\xb4\xcc\x84\xe9\xb0E\xf0\xc1\x17a\x04\xc6hd\xc8\x1e\xb4\xe1\x8c05\xd6\x08N\xbc\x11b1G#7\xb1G\xb5o\xd7=\xb0G\xb0B$`\xc5 !\n\x874\xb2\xb2\xe2\x93\x10\x8bQ\x1a\xb9 ?\xd0\x125\x9b\x0e\xab\x04/\xbc\x12\"0K\x08\xc3-!\x06\xbb\x84`\xfc\x12\x1c\xab\xad\x03S\x82\x00\\\xc9\x17\xcb\x84\x18<\x13B1M\xb0w<\x06\xdb42\xeb \x87\xbeS\xc6\x0f\xe3\xb4N\x88re\xc79aZ\xac\x13\\x'\xd81O\xe3;\xb1X(L8v\x030Q\x08\xc2E\xa1\x83\x8d\xf6\xe9\x96\xd4y\xb9\xba\xda\x92O\xa6\x02\xca^\x91 \x1b\xa2'H\xdd\xf4|\xb5\xadrR\xe5\xb5\x03\x11\x1b\xd5Z\xbfZ\xa9\x02\x14\xb5\xb5J\xb5\xe0\xac\xa2-Z\xe5%\xff\x16\x87\xc2\xf6\xda\xd8?(\xe2\xdc\x98\xc7(:\x7fU\xcd\xe9\xf0XA\xb5\x0d\x08t\x94n\xc6\x9fk3\xc6\xe8\xd4\xa73\xa2\xd4\xeb\xea\xff\x9a\xa24J\n\xa5f\xf6O\x19\x94E\x94\x8a(\xf4%Z\xe1w\xf8\xe7\x06\xd3z&~70\xfb\xb9\xc1\xd5\x8e\xb3al\x99&1l\x08\xad\x01\xf3\xd0&\x8f\x89\xce\xe0\xa2\xee\xd8\xaam\xbd\x83\xdc\x14\xab\xa8\xd7\xb8\xc2<&^\x12\xd8\x90\n\xab\x18\xb8n}\xaeI\x8d\x0cH\xaa\xb72-u\xeae\xed]c\xb8\x8b7\xcf\xb5\xc8\xffQ6\x9b\xb9\x88\xbc\xa9c2\x9d3\x19\xa6\xfev\x15\x9d\x91\xa6\xac\xaf83\x93\x19\xfb\x84(P\\\x9fB^S\x85HPhJ1\x98\x17\"\xe8\xfa)\xa7\xfd\xf1a\x1f\x17\xda\xa4\x06\xaf\xe3\xbd\x03F\xeda\xdf.#u\xc9z:\xeb\xab(\x9d\xf5Mg}\xf7\x94\xce\xfa\xa6\xb3\xbe{\x9a4G!$?!(7!\x9d\xf5\x1d\x9b\x87\x10\x91\x830I\xfeAx\xeeA:\xeb;&\xd7 $\xcf \"\xc7 \x9d\xf5Mg}\xd3Y_\xdf\x1c\x81I\xf3\x03br\x03\xd2Y_\xd3c\xce\x1c\x80\x00\xfc\xdf\xe7$k\x08\xee\x9f\xce\xfa\xa6\xb3\xbe>\x18~:\xeb\xcbi\x0cN\x9f\xce\xfa\xea89\xb1\xf8X\x1c\xde\xb86\xa4\xb3\xbe\x87\x94\xce\xfaF\xe0\xe7n\xec<\x147\x0f\xc0\xcc\x83\xf1\xf20\xac<\x9d\xf5\x0d\xc3\xc3\xd3Y\xdf\x96\xfe!\xcf\xfa\xdanU\xdf#\xb0\xb3\x1b\xdc]\x05m\xd0\x95\xc40\x914\xa1\x15\xae\x9b\xaa\xe4A% \xabI\xa0\xa8\x05\xb2)7\x93\xb3)_\x95\xa4\x1a\xc4\xaf\xd5l\xec7!43\xf6\xc3\x0e\x8b\x83k>`\x85oq\xd5{\xd5\xf6\xf1\xe4\xd3\xc3\x0f\x97w\xf2%*\xac\x9f =>\xac\x0d\\rd\x8fT\x0b\\\x0d\x03X\xfa\xab\x92'\xd5\x86\xb9l\xc5qX\xdd\x8a_DY\x86_e\x95\x08C\xe9\nm>\x85*^\xd1K\x8c\x02T\x03\x82U~\x8bK\x10\xacMu,t<\x1f)]\xa4J\x16\x8a\xea{\xc8\xa1H\x95,,\x14\x9a\xf5ae\xa6\x0f\xfd[\"aSg\x7f\x80;\x03\x04\"\xb2@\xec\x1dH\x95,b3C 8;\xc4\xca*U\xb2H\x95,b3H .\x8b\x04\xa6\xca$\x81\xa8l\x12\xfbtH\x95,\xc2\xb2K 0\xc3\x04\xe2\xb2L\\&\xd8/\xd3\x04\xa6\xcd6\x81\x80\x8c\x13\x08\xcf:\x81\x88\xcc\x13\x0f\x93\x99*Y\x08\n\xceJ\xb1rK\x95,R%\x8b\x01M\x93\xbd\x02\xfeI\x18\xe0\x93\xc5\x02a\x99,\xe0\x82\x9e#3Z\xc0\x83o\xaada\xa1\xa8\x8c\x17H\x95,$Ee\xc3@PF\x0c\xa4J\x16>\x992p\x17\xd92\xe0#c\xaad1m&\x0d8\xb3i 6\xa3\xc6\xc8-U\xb2\xf0\xcb\xc01rK\x95,<3s 8;\x07R%\x0b-\xc5d\xee\x18\x99\xa5J\x16\x8aR%\x0b\x0d\xa5J\x16\xa9\x92\x85\xf6\x01gD)U\xb2\xd8\xff\xe4\xab\xccT\xc9\xc2\x90\xd6\x90jY\xec\xc92\x9aR-\x0b\xcd\xeb\xa9\x96E@6C\xaae\x91jY\xeci\xd2,\x85\x90\x0c\x85\xa0\xec\x84T\xcbbl&BD\x16\xc2$\x19\x08\xe1\xd9\x07\xa9\x96\xc5\x98l\x83\x90L\x83\x88,\x83T\xcb\"\xd5\xb2H\xb5,|\xb3\x04&\xcd\x10\x88\xc9\x0eH\xb5,L\x8f9\xb3\x00\x022\x00|*5\x84 \xff\xa9\x96E\xaae\xe1\x83\xe2\xa7Z\x16\x9c\xc6 \xf5\xa9\x96\x85\x8e\x93\x13\x8d\x8fE\xe2\x8dkC\xaaeqH\xa9\x96E\x04\x82\xeeF\xcfC\x91\xf3\x00\xd4<\x181\x0fC\xcbS-\x8b0D<\xd5\xb2h)\xd5\xb2\x90\xa4\x0eH\xaf\xbbgiA\x9e7\x1e\x84\xb2\xf7\xc7\x8d\xeb\xaa 8T\xef\xfbS\x96o\x90>\x01^\x88k\x8a8\xbfy\xfb\xe1\xbbs\xbeg\x16\xcf\xc9\xcdg\xce\xf1\x81\x978\x93ny\x8b\xc9t}s-C\x11\xaa\xd07F\xf3U\x89\xea\xa6\xc2\xb4\x9d\x89l\x91Z\x91\x15\xe1\x8e\xf0\xe1~\xb6\x9fJ\xc8&\x83RJ\x7f\x8e\x1c\xd3\xfe,\xd1\xbc}\xa0l\x9e:\xd5\x9bA\xf1 X\xfd\x898x$eaIJYX6\xbbr\x07\x00\xe6p\x05\xfbO\xb9\x1e\x1e[\x17\xb0\x05.\xf0\x8a\xd7\x19:\xfb\xa5\xfd\xf7\x95\xac\xf6\xf3\xebY\x85?\xa1jA-\xa5\xa0:;\xb8\x97\xe2\xfd\x9c\x94\x1f\x983\xf4N\xbc\xdb[\xed\x84\x97$\xb9\x02\xca\xb2\xaa\x11v\x01q\xf7\xb5e\xd5\xe6#k\x97B}C\xf2\xc9\x07\xbb&\xca^?\x8c\x11\xdc*\xf8jle'\xd1-\xfb\xeb\xfa\xbe 2\xf6P\x90\xa3\x9f\x82\\\xbd\x15du\x05\x049{+\xc8\xee\x16\x08\xf2b\xe5\xe3\"\x08\nv\x14\xac\x8a\xe0.\x84\xc3]\x10\x14\xe54X\xf8\xc9P\x9f\xcdu\x10\x14\xee@\xb8R\xb1\x05\xed\x8d\xc7Ke\xef\x84\x01\xd9G=\x85\xad\xda\x0f*-\x1f\xb2\xe4j\x97,\x8e\xa9\xfa7\xfb\x00bN8\\\x1ce\x05\xdb/Z\x14\xd2\x13\xd1X\xc7\xb6\xa1!SC\x96\xfe}\xd8\x94\xe4j\xb7\x145k\xee\xc9\xd5\x16k\xb2\xd2\n\xcf\xd2i6|x\xf7\x07\xa4\xc5\xdf>\x98j\xdc\xf9\xd0/\xd3\xf1\x8e\xb7\xc1\xbfH.\xb7\xa4\xe4r\x7fa\x97\xdb\x86\xb8\x1c8\xd3\xa6\xa1x\xf0`o*\xb6\xbf\xaa\n\x9cP\x13y,l\xd9_\x0cF\":w\xb9}8\xfb\xe5\xc0\xdd\xb4U\x97\xdd\xcf\xf3\xc0-\xc4~\x05vl\x1b\xd2\x8e\xe1\xf0g\xd7\x04L\xab{K\x7fO\xab\xfb\xd0\xcbty\x98\x9d\xc9\xa3\xe1\xe6Z\xe8'\\\xe3\xd3\xf2>\xa0\xb4\xbc\xa7\xe5=zy\xdfK~\xb0\x14\x9b$?x\xb0'y\xfb\xeb]K~'\x8e\xc9\xbe\xbc\xba\xdb\x0f!U{\xde\xba\xef\x89\xec\x99\x0c\x82\x016\xef\xa3\xcb\xed\x91\xd2\xc6\x03\xf5?\xee\xa8\x08\xbda\x1e\xbaW\xf9\x8e\xc2\xb5C\x91v?\x02[{\xd4\x9a\xa6\x9d\xf5\xcba0\xc5\x7f\xa5\xeb~\xc4\xd1k]o|\xa5\xd5NRZ\xed\xd2j\xf7\xb0\xd6\x0c\xb6\x93XT\xe8\x93\xfa\x8b\xcf\xca\xf1\xdf\xf2\x9d\xe7\xb2\xbfj\xf9P\xbcZE\x84, \x03\xa6\x8f\x94~\x1e\xe8*2\xd4[\xd0\xb4\x1d\xc8<\xe4\x159\xbc\xc2l\xfd@\xdf\x13\x18\xfc\xe1\xb0HV_R\xb2\xfa\xc9\xeaOn\xf5\xadF\x9f+\x80\x9aM\xf9%\xff\xbd5\xdc\xe2\xf1\xf6dP\x87#l\xc8\xa2)\xb0\xdetw\x9e\x13\x0c\x1f\xa9\xee=P\xa3\xddUK\x97\xfaIZB\x19\xdd\x8f\xbd\x1fOJG}\xb5\xec\xc9:\x99\xecSi\x9fuY\xa3\xcf\xe6\xb9f\x99\x0dsD\xf1U[\x8b\xcf\x96\xc2\xe0bD\xca\x86N\xc2\xa9\xb7\xb4]\xe1\x12\xcd\x0bl\xe54L\xe7\x05\xe7\x1e\x8aO\x081\xfe\xbc\x161\xb9l\xc9)\xf0\xee\xf2\xc5\x80_Z\xb2\xd2\x92\xf5\xe5\x97\xac8\xe7~\x1f$\xd0\xa1Q<\x91;\xa74'\xa5\xc5\xado\xf7\xe9/\xda\xa7\xdb\x85\x01eY\xb3i\n^\x1db\xcf\x8c\xcf$\xe4Hg\xd3\xb0\x95\x8f=\xd8\xe5a\xdfC\xc7\x12\xd1QEw\x99\x10\x7f\xceeY\xa7},\xb1\xc2\x19\xceo\xb1&uf\xe4ra\x12\x16\x1c\x13\x0b\\\x19j\x8e \x06N\xe9\x049s\xd3<\xe6\x8a\x0be\x03?6\xee8\x9c\xa0`\xc4\xcd\xd2y\xdf|\xb4(\xe4\xcd\xc8\xcd3\x17-\x0c\x81\x93uM\xf5{J\xcdT\xf7Z\x89u\xbc\xcet\xe6h\xbf\x93l_I\xab\xf2\xe0\xd7\xb4*\xdf\xc1\xaal\xdbH\xfec@N\x0e\x0f\x8345\xad\x11?\xe5v\xe5N\xabo\xe7\xf6\xdb\xfdk\xc3l\x18\x05\xe3\xf3\xd8\xe1\x81r\xec\xae\xc6!\xdbGJO\x0f\xd4\xe5p$\xc3Dy\x06F\x9e\x90\xdc\x82!%\xb7`@an\x01x\xa9\xd06\xed;\xe9\xe9\x1d[\xa2ar\xd2\x94_\xa9hB\xf9X\x0dr]G\x06\xbb\x13\xc8\xb9\x1fP\xd2\xfc\x16\x8b\xd3\xbc(\xbb9\x15\x15L)P\xae>\xa0\xa8\xcc\xb5\xf7Jfk\x9c\xdd\x84\x9f\xca\xb3t\xd9\xe9\x1a\x0d\xf8i*\xf0\x0e<%\x8dZS\xec]Rr\x99\x92\xcb\xf4\xb0\\&Z \xba\xc6^n\xd2{\xf1h\xeb\x1b\xf1W\x01\xdf\ns\xb9\xf4\x8f\xc1HF\x8f\x94\n\x1e\xa87$U\xf30&\xd7\xfe\xd3mq\x95\x13\xc7a@\xafib\xb98d\xc9k\xc7\x9b\x829\xceV\xdc\x8b0\x0cF\xc3wl\x14u\x97\xdf\xee\x92\xd9\x19h\x86\x1a\x91\xf2~\x8e\x9cr\xff#\xcfx=\x97z-+\xc2\xd0\x9aT\xfc\x96\x18\xc3\xdb\xfc\xce\x83\x9c\xf2b\xdb\xaa\x8ax\x91\xf1@#\xa0-\xfb2U\xce\xff\xbdQ5Nh\xcdKbk\xb9qWL\xeb\n\x08g`\x9f\x04\xac*\xdb\xa3\n\xb7\xd0D)\xab\x0d\"\xd9\xeb\xb5\xa1V<\xc9x-m\xd7\x0189\x86\xfbG\x8f\xe4\xdf\xfc\xc2\x82cn&\x12hU\xa7\xccK\xcf\xc5\x984\x00\x99n&J7\x13\x0d\xe8\x01\xdcL\xd4\xf7\xbe\xe5\xba\xe7t\xb9\xdd\xd1H\xb5\x14'\xc7ZRr\xac\x93c\x1d\xedX\xef%\xa75\xaa\xf8-\x85\x07\xc5\x1cm.\xcd\xe0\xad^'\xd453\xedC\xb2N\xe4\xbe/\xc3\xc9.\x97\xe7a\xffBko\x1d|PG\x899Q\x1c\xec\x0e:.\xab\x8e=\xd0n\xa7\x1a\x9aajL54\xefP\xb9\xee1\x9ajhN\xa1\xc5TC3\xd5\xd0\xfcm\xd4\xd0\x1c\xe6\xdf\xe2\xdb|\x81\xcb\x0c\xb7\xe1?\xf5\x07s|\xefyQ|'\x1f\xdagZ\x15\x05\xa87\xb5\xb1\xbc\xceK\x8fT?\x1eh\x18\xaf\xaf\x81.\xfd]\xfa\xf2~\xd15H\x97\x8b\xa6\xcbE\x8dO\xa6\xcbE9\xa5\xcbE\x0f)].\x9a.\x175Q\xba\\4].\xca)].j\x1e\xd3\xe9rQA\xe9r\xd1t\xb9h\xba\\\x94S\xba\\\x94S\xba\\\x94S\xba\\TP\xba\\4].\x9a.\x17M\x97\x8b\x0e\xc9\xf7\xa2\xc7t\xb9(\xa7t\xb9\xe8o\xe5r\xd1=\xf5v\x88\n\xbf\x90\xd0T\x1f\x9c9\xd8_\xa7tMN)]s\xff\x93\xaf2\xff\xe1\xd25\xf5\x87\xa5:\xd0f|\xa6f\x17T}w\xf9b\xd8\x89\x94\xb3\x99r6\x9dAN\x9f8!$\x9c7\xe1\xbc\xc6'\x13\xce\xcb)\xe1\xbc\x87\x94p\xde\x84\xf3\x9a(\xe1\xbc \xe7\xe5\x94p\xde\x84\xf3&\x9c7\xe1\xbc\x82\x12\xce\x9bp\xde\x84\xf3&\x9c\xd7D \xe7M8o\xc2y\x13\xce\xdb\xa1)0\xb7\x84\xf3rJ8\xefo\x05\xe7\xb5\x95NH\xc7\xd1\xc3\xce\xfa\xa6\xe3\xe8w\xa8\\\xf7A\xeat\x1c}\n-\xa6\xe3\xe8\xe98\xfao\xeb8\xfa\xb1\xf3<\xfa\xd9/\xea_WkD\xd7\xb6\xbb\xea\x0fN\xa7\xb7\x19Q\x1c\x92\x01R\xee\xff\xc2xi\x0f\xac\xffVN\xabGe[\xd5\xd6d\x04g\x1c\xde'\x90=y\x1a\x82_\x12BL\n\x82=\xd5 *\xd1\x807a`\xe8L3\x98 \xc9 2\xc5\xc0\x08\xcc\xfa%\x18\x8cJ/\x88J.\x00T\x14&-\xfa\xa5\x16\xc4$\x16\xd8\xe0>\xaf\xb4\x82\x89\x93\n\xbcR\n&L(p\xa6\x13L\x94L0&\x95 8\x91`\x824\x82\x89\x93\x08\x1c)\x04\x93'\x10\xdcM\xfa\xc0\xe4\xc9\x03\xfe\xa9\x03q\x89\x03\x16\xa5\xbb\xd2\x06&K\x1a\xf0K\x19\xd0\xc4,\xcc\xf6u\xe2t\x01W\xb2\xc0\xc8T\x01K\xa2\x80\xd3=q& \xf8\xf9/\xd3&\x08\xb8\xd2\x03\xdc2\xc5\xa5\x06(\xcb\xaea\xe8J\x0c\x980-`DR\x80>\x95\xc7\x96\x120mB\x80=\x1d`\x8ad\x00/4\xdb\x91\x08\xe0\x9d\x06`F\xec\xc2S\x00\xcc\xbc\xb4\xd1\xf1I\xc0\xff\x10e\xf9\x02\xffn\x9dx\x83\xfe\x11\x90\xbf\x1eI\x98\x08\xee\xf7\x02\xfb\xddP\xbf\x0f\xd0o\xd5b(\xc8\xef\x0b\xf1\x9b\x00\xfe \xe0\xfd\x00p?\x1e\xda\xb7\x00\xe8\xbe\xb0\xfe\xc4\xa0\xbeE\"\xedH\x8d\x82\xf3U\x14V\xc3\xcf\x00\xe6O\x0c\xe5\x9b\x81\xfcX\x18\x9fG\x04t\x82\xebA\xfci!|\xd3\xc6\xcf \xdf\x9b\xf0E\x13t?-p\x1f\x0f\xdb\x1b \xfa(\x80\xde \xc6\x87A\xf1\xde@| \x0c\x1f\x02\xc2\x1b!x\xb34\xbeP\xa8\x1f\xfc\x1e\x08\xbe\x07@\xef\xda\xaeM\x0b\xbb\x9b&\xc5\x08\xc8]\x1b\xa70\x02\xeeqp\xbb\x0dZ\x9f\x1eX\x1f?\x92\xbcAu_H\xbd\xbfDz\x1c\xed\x0c:\xd7)Or\x0e\x80\x03E\xe9\xe6\x0dI\xe9\x14g:\xc5\xb9\xa7t\x8a3\x9d\xe2\xdcS\x0c\xd8bd\x96Nq\x1e\xd2D\xc0\xcb8\xe8%\x02|\x99\x04~\x99\x1c\x80qB0w\x00\xc2\xdc\x15\x0cs\x07@L\x08\x14\x13\x0b\xc6Xm\xb8\x0b\x8e\x99\x10\x90\xf1\x85d\x02A\x99\xc9a\x19703\x1a\x9aI\xa78\x9d\x92\xc5A5ZV\xe9\x14g\x0ch\xe3\x82m\xa6\x01n<\xd1\x08'x\x13\x00\xdf8O\xd3\x05B8\xe9\x14g:\xc5\xe9\x03\xee8\xb5\x1a\n\xf0\xf8C<\xe9\x14\xe7\x80&\x06|\xd2)\xce.\xc5\xc2?Zf\xe9\x14g\x00\x184\x06\x0e\xd2\xb2K\xa78\xb5/x\x01H\xe9\x14\xe7tpR:\xc59\x1al\x9af\xccy\x03N\xfe\x90\x93\xdf)\xce\xde\xd1\x96\x0e'm]_\xfeP\xaf\x06/\xff\x83\xb4\x86\x95\xa8\x88\x8a\x17\x07\xf71\x02Lp\xf1\xb5vs\xee<\xd5\xb3\"\xb7\xed\x81\x1e\xae\x07z\xf6\x8b\xf8\xef\x15k\xc5v\x98\xe7\x92?\xd6\xbbhr\xafI\xd5\xe9\x15\xb9\x85\x0dY4\x85\xfe\xf2\xc9\xef\xc9\xad`\xf3Hu\xf9\x81\x1e\xe6\xb9%\xfcvl\xa1\x9aC\xdf\xb8'S\xef\xd9AI\xe6V?\x15.\xf8\x86\xb8&\xf2\xf9\xc3u\xca(&8\xf1.%\x02\xaer\xb2\x88L\xc6\xed\xf5\xe95.Wu;\x94\x05{\x10\xec\x87\x82/\xf0\x96\xd0\xbc\xf6\xd3U\xffa\x0fe\xc9\x17&\xd5\xd6&/\xaf$_\x9b\xae\xf4\x80%\xd8@Kp &\xc8\x05^\x02W[I6\xb6]\x99\xf3\x832B\x1b\xd2\x94\x86^\n\xf2`\xe3\x13Hc\xf4\x82\xe4\xfb\x92\xe4\x08jr\x83K\x19\x08\x13\xddQGL\xd9R\x8cJ)\x9c-\xe6\xfa\xe6\xed\x87\xef\xce\xb9\xf7*\x9e\x95n`\xce#u\x17e-\x17\xc86:J\xad \x80\\=\xc5\xc6\xc1\xdc(\xcdW%\xaa\x9b\n\xd3\xd6$\xb3\xad\xd2\x8a\xac\x08_\x9a\xf4\x1efOI?\xe6e\xbei6j\xf4\xf2\xfd\x02\xe2\x9f\x9dPT\xb0q\x8dK\xb6\x1d\xb1N,F\x1b\xf4\xf9\xaa\x9d3\x93\xcdns&=\xfa\xcc\xe5\x16Mq\xb1\x9f3\x951\x0f\x84M\xcc\xfd\x84\x04\xf6\x19\xdb\x1e\x99v\xa5\x17e^\xe7\xa8\x90qh\x18f\x19\xb4\xb4!e\xbd>\x88a\xd7\xa8(v~v\xa5\xfb\xa8\x87U\xe1\x8fOjS~nH\xd5\x18\xe6\xab\xf3\xe38\xc3\xec\x9e_O\x8e\xba-\xae2\xe6\x93\xaeD8\x99\x1f\xd4\xa65\xba\xc1\x1c\x89j\x17!\x91/c\xc2\x06\xe5\xb9s\x8eZ\x99>[FJ\x9a/0\x9b <\xa8\xaf\x1b\x06\xf5\xba\xc2\x94\x8d\x9f\x07\xa2\x1b6b+\xb5k\xfd\x7f\x98rMPq\x08\xbe3?\xb7\x88\x1a0\x15\x80\x972\x0e\"G\xf5\xd7\xb3?\xe9\x9e\xbc\xc55\xb9z`\xbd\x17{|\xb2\x84\xff\xc2r\x0c\xf09\xfd\x81\x0f\x11\xf1\xbf\xdcY\xb3\xc0\xf9]%\x99G\x06\xeb<^\xcc\x86\xaazr\xf6M_U\x1e\xa9_\xc2a\x0cI\xfc\x92\x9e\xea\xbb\xcb\x17\x03~)\xed+\xa5}M\xe6\xed\xa4\xb4\xaf\x94\xf6\xa5\xa7\x94\xf6\xc5)\xa5}\x1dRJ\xfbJi_&Ji_)\xed\x8bSJ\xfbJi_)\xed+\xa5} Ji_)\xed+\xa5}\xa5\xb4/\x13\xa5\xb4\xaf\x94\xf6\x95\xd2\xbeR\xdaW\x87\xa6H\xc1Ii_\x9cR\xda\xd7?B\xdaW'\x05\xaa\xc3\xc7\xb6\x93\xec\xbc\xd1\xe2\xcd\xe2\xb3u\x10\xe7\x9a\xc8\xbb\xc5\x97\xa4:UE\xe8E\xbd\xf8\x1e\xb3#\x91\x07pt\xdaW\xef\x11\x87\xa9\xd9\x0fl\xa3u$\x91\xf7\xa3 \xd3\xc8L\xf9b\xdat1 \xfc\xc9\x17\xb4)b\xea\x91~\x96X\xfb\xd7\xb6\xea\xf3*\xbf\xc5%\xd0\x1a\xd5\x0d\xd5&\x8a\xb5\x9c\x1e\xa9N=\xd0D\xb1\x81V\xbaT\xdf\x03\xfe\xa5\xc4\xb9\xca\x0d\x88sP\xfc\xc7r\xaf\x85`\x94\x07\x82aT\x98\x14J\xf5\x16\x15\x0c\xb1\xc2\xd40\xab\xb7\xb4\x80\x08\xbb\xb2|\xe1V@\x85\\aB\xd8\x15\xd2B\xaf0%\xfc\n\xc9!X\x88\xbcm#a1H\x08\x8da\xc3\xb10%$\x0b\xa9aY\x087|Jx\xd6[\x98\x15\xfc\xc4>2\xb80m\xf0\x81\xa8\x96\xe1P-\xe4\x0d\xd7B,d\x0b\xe1\xb0\xad\xf7\x9c\xa9\xe1\\\xc8\xd8w\x13\xc2\xba\x90\x14\xda\x85\x1d)yc*\xb4\xb8G\x08\x8cU\xbel^\x90\x91\xd2\x8b\x8f\x1f.>\\\xbe\xfdavy\xf5\xf6\xea\xd3\xe5\xec\xd3\xfb\xcb\x8b\xb3\xd3\xf3\xef\xcf\xcf\xde%\x9c\xf5\xee\xec\xe2\xc3\xe5\xf9\xd5\xec\xe2\xec\xe3\xf9\x87\x94\x13\x7f\xfapu\xfe\xfe/\xe9\xe7]\xbc\xbd\xbcL\xaa\xe1\xc7\xb3\x7f;;\xbdJ:\xe5\xfb\xb7\xe7?xO0)\x97\x13\x1c\x88]U1\xb1\xe3K\xd9\x07\xe4\x9d\x94\xdf\xfe\xea\xe1\xd4kA\xf27& e\xff\xd0\xd3\xa7\xb7\xfb\xbb`\xb0+\x04\x9b9\xc8S73\xc8.\xbdv\x18\x1cG]x\xd8\x9bv\xaf=\xfc\xddRj\x18]\x14\x16[\xf9\x16T\xf5\x92\xd0\x81\xdfE^\xe9\x02m\x91\xbe\xbb[\xcb\xc1\xcf\xb8J*|\"g\x1d\xd5s\xb2[9\xf5\xf7@\xadD\x7f\xb2r\xa4\xc5k\xe86\x10\x17\xda\x14m\xcb\xd2\xaaf\x9e\xc8\xdd\xca\x99_\xf2U\xef\x9a\xb1\n\x1a\xf6w\x99\x00\x9dTK5\x08\xec\xd6Q\xfd=_\x0do\x8ar\xe5\xab\xdaMY\x15\xab\x99\x92\x86P\xb1!\xdf\x88\x8e\x1dZvK4\xf9\xe8\xf2\x17\xa5+a\xa4\x13\"\x8b\xca\xdd\xe0\x02?\xdf\xb2\xc0\xf4\\\x12Dj>\xd2\xb9\xe5\xae,`\xf9\xf1\xe2\xf4H\x07Y\x8c*JU\x07\x9e\xd4z\xb3U\x02\x18\xdb\x8a\x97\xabp\x00\xd6\\\xe9e;\xd4(\x91s5V-\xfc\xbd\x81\xe7!?^>\xb2\xf6\xe5\xdeq\x85\xe2\xba\x15\xdf.{\x97\xf3\xb2\xaa\xf7\xafLU\xcf\xc4\xb7\xd8\xec\x8e\xf1z\xaf\xc2\xda\xed\xf5\xba\xe43^\xaes$y-\n\xce^\x8b\xb2\x9c\xc7\x19\xf1\x19V-\x9e\xe6\x82R\xae$,\xcf\x14F\xab\x94\x05%\x9aP\xbd\x14\xd7O\x11BM(\xe7\x00J\xac Y\x14v8K\x96l\x8aV>\x1c\xee\xcc+\xe7\x84\x16t\x9a*\xe9\xa4U\xd5Z^4O\xf4\xb4\xe9+>\xd1\xc3\x86\xe9&f2=\x98\xac\xce\xeb\x86\xe9{\xb7\x16_\xd3\x8d~m/\xeb;\xd6T\xc5\xee\x9e[\xca|jQ\x9bb\xa9\xbb\xd9n\x83\x07u\xec\x0f\x1ci\xd4s\n\xf1<\x81v&\xe1$\x12N\"\xe1$,\xad\x9c\x95T\x9eB)\x93p\x92\xef\xb0(\x8d\x9c@\"cd\x81R\x08d\x12N\"\xe1$\x0cML\xc2I\xd2\xf6!\x86I8\xc9UR\x94\n\x9eJ\x04{\xdf\x0d$\x9c\xb4k$\x9c4\x81\xe4\x8dS\xbc\xa9\x04o\x02\xbd\x9bL\xee\xa6Q\xbb$\x9c\x94F\xe6\x92pRg$\x9c\xa4\xad\x13N2z3\x8a\xde\xb2\xca\nE3Gg\x0d\x82\xb9=\x06&C\xbb&*5\x9e'=)v\xf9\xe5PK'\xba\xf8\x85\x90Jd]\x0e\x89N:q\xc9\xa7G$CX\xe4\xd3\xa2\x90\xbb\xf8cY\xbdQa}\xebo\xbd$\xd8M\xb1j#\x9a`\xe0\xa4\xdf\xf1\xc4{\xeca\x89\x1c\xec'\xdbQ4;\x92`GP\xebI\xa4\xba\x19\x0f\xefj>X\xee\x1a\x8c\x82\xf2\xc7\xc1\xf0\xa3\xfeR,\x16\x0dk[\x13\x9e\xb7\x07\xbd\xbe\xa4\x0c\xf7\xd5TR\x0f4\xb5\xb7\xa2\xdd\x01\xa3\xb1R\xed\xfc\xa6\xab\xcbZ\xb8i\xea\xf5\x93\xd4\xb8'|\x8e?\xb3G_\xb5Go\x19\x0d\xc9\x14z\xa6\xde0\xbem*\x19\xbb\xd0\xdc\x86\xe6\x11:\xa2FF\x1c\x96\xa3\xa5\xf9\x0e\xbf\xe5\x11J\xe6\x83\xf8\xaeR\xe2~P\xdf\xdc\xb4\x8cC\xdd\xc0\xb0\xba`EL[\xc63{\xcb\xb3\xfe\xecp\xa2\xaa\x9f\xcf\x8f\xa3\xb5_\xdd\x18\xe9J\x99KQ\xce\xcd\xdf\xe4\x98\xa4u\x0d\xd5\xe2\xfb-\xab\x8c\xe3\xb7U\x17\xef\x18\xbd\xbd\xcfei+\xd1\xe7;\x17\xaa\x08\xc1\xb6\x15\xae\xfe\xcc\x12\xfd9,\xfe\xc0\xce\x1d1G\x0e\xf7\xae\xcau\x89\xf5\xae<\xd6\xe0:>\x14I\xc5\xc2\xec\x1e\xac>9\xc5\xaf\x83\xd26j\xe5\xdb\xfe\xd3\xf9\x0d\xac\xd8\x0d7\xe8\x96f\xb9\xcc\xc4G\x86\xf1\xd4\x03\xa2.\"\xfc|\xfd\x08\xac\x98\xdfB\xb1\xd9|A/\xda@U\x7f~\xc8\x97\xd6\x19\xc2\xa3\xb2\x87\xd6R\x06\x13\xc4\x7f\x94\xd5\xa2\x9c\x17\x9cu\x01}C\xea\x8b\x03uG\xb2\x8b+\xab\xf9j\xbb\x18\xad<\x14\xea*\x1dQ1\xbac\x92\xcf\xb1\x02}r_A\x9bO\x1c\x14\xf6\xe9|<\xb1\x1d5A.\xd64\xac\xd5$\x95|\xbc\xfa\xe7Qm\xcd~\nk\xff5A8\x10\xa5\x1b8R\n\x1c\x95\xf7\xf1\xe2\x94t\x02\x81t\x02\xb3\x8d\x87\x93XC\xd2 \xc4\x10\x86Y\xf8\xc2)t!\xe9\x04fd\nS\x88\xc2$\x9e\x90t\x02\xf7\xa5\x08'0\x84Y\x08\xc2t~\x90t\x02\xf7\xe1\x06S\xa8\xc1\xcc\xcc \x8e\x18\xcc\xc8\x0bbiAG \x80t\x02\x87\x86\xe0\x03\xb1\xb3\xa4d6\x90t\x02QD\xe0\x14\x1e\x90t\x02}\x87E\x19\xc0\x04\x02\x10\xa3\x82\x97B\xff\x91N \xe9\x04b\x18?\xd2 \x94\xb6\x0f\xd5G:\x81\xae\x92\xa2\x1c\xdfT\x8a\xcf\xfbn \x9d\xc0]#\x9d\xc0 \xb4^\x9c\xd5K%\xf5\x128\xbddJ/\x8d\xd1#\x9d\xc04*\x8ft\x02;;\x04\x89\x97\xa3\xcf%Pxx\x06/I'\xb0\xb4G\xee\xe1\xe6n\xfd!\x83\x80\x98Z\xed\x87r\x01#M@{D*\xe5\xeep\x83a\xb0\x97\xec\xe0\xcdv\xaa0M\x0e%\x8f\x13\x1d,o\x03\x92\x1e\xef\xf4!\x9d\xa4G!5z\xf4\x1fe\x00\xb0-\xab\xe5j\xb7\xf5\x03=\x0fS\xcc\x0b\xe3\x82g\xaa\xe7aZ\xf6h4 1F|\x906\xe2\x83\x88\x0f\xea\x8d\xf8 \xe2\x83z#>\x88\x13\x1f\xe46\xe2\x83\x8c\x11\x1fD|\x10\xf1A\xc8Y\x12\xf1A\x9d\x11\x1fd\x1b\xf1A\xc4\x079\x8c\xf8 \xe71\xc4\x07\x11\x1f\xe41\xe2\x83\x88\x0f\">\x88\xf8 \xcbr\xb0\x1a\xc4\x07I#>\x88\xf8\xa0\xe7\xcb\x07\xd1\x16uS\xf7\xff\xa2-\xea\x0e\xe8\xdcx\x1f\xa5-\xearx\x91\xb6\xa8\xa3-\xea\xfe\xb8[\xd4u`\xeb\xc9\xaf\x1d\xd9\x18\xda\xb7\xce\xf2\xa6!\xc4\x0c\xee\xaa\xe1V\xb3\xc5lY\xa9gP\xf4G\xb5\x8b\x8d\xb9\xee\xf9\xbb~R\xa2\x8f~\xbbX4!\x1aV\xff\xf4\xdcaX\x1f\xbf1 )\x8b\"\xad\xd1 D\x14\x84\x8a\xc0\xac\x91\x0b\x84\xa0N\x1e\xc1XI?\xcci\x89\xd0*\x02M\xcd\x0b\xa6\"\xb1\xd4t(5\xee\xa0\xcc@j\x10G\x9d\xac\xc5\xa5+9\x01\xb5\x1b\x95F\xa4\x9d6\"\xed\xf2\x8c,D\xda\x11i\xe76\"\xed\xa4\x11i\xb7kD\xda\x11i\xe73\"\xed\x88\xb4\x93F\xa4\x1d\x91vD\xda\x11i\xa7\x8cH;\"\xed\x88\xb4#\xd2\xcegD\xda\x11iG\xa4\x1d\x91v\x96\xe5\xa0\x9e\x88\xb4\x93F\xa4\x1d\x91v\xbf\x05\xd2\xae\x0b<\xfb\xea\xdf\x1d0\xda}Q\xc5\x00ud\x8f\xb5p\xd3\xd4\xebA;\xda\x8c\x0d\xc9\x02X\xc8]\xe8\x02D\xc5\x95\xf8\xfd\xa3\"\x84\x0cJ!_\x15r }\xb8\xf1\xde]\xcd\x99\x13\x93\xb0\nya\xda\xf9LQ \xd9\xae\xdd\x89\xfc\xa0.\xaa\xed\xf6\x9do\x94\x90\x8a\x98,\x89\xdfv\xdf\x9f\xde\x9a@4\x0e\x17\xd8\xeb.\xb2\xfa\x14\xdc\xe3.r\xae\x7fo\xbb\xc8\x89\xf1=\xed\xbc\x05 \"\xd8VOJ\x89b_9\xc2\x1b\x14\xc3\xd6F1l\x8aa\xf7F1l\x8aa\xf7F1lN1l\xb7Q\x0c\xdb\x18\xc5\xb0)\x86M1l\xe4,\x89b\xd8\x9dQ\x0c\xdb6\x8aaS\x0c\xdba\x14\xc3v\x1eC1l\x8aa{\x8cb\xd8\x14\xc3\xa6\x186\xc5\xb0-\xcb\x11O\xa4\x18\xb64\x8aaS\x0c\xfb\xf9\xc6\xb0\xb3\xc4\x84\xefj\xceB[I\xfd$~\xef\xa2\xc1\xf2h\x15 ^\x96w\xac\xdai\xef \x14,\xcf}aZ\xfcL\x83\xc0\xb2I\xcf#~\x15M\xb3OZ\xbf \xec9\"\xda<}\xd7(\xb5\x00\xef;\x1d\xb3\x02\x03*\x89\xbaa\xf3\x82\x8b\x87\xe0\xa2a7b\xa2\xa6\xc2\x0e\xbf\xa8\x0b\xb4\xbf@Y\xb5\x9c\x15\x0b\x1d\xdc\xba\xf1\xce\xad\xa0\x13\xf9\x10#\xa4\xee\xac\xfe\xd1J~\x10-\xd4$\xb2\xbc\x81_V\xacz\xa5\xaf\xf95|\xfb-|\xf3\x8b\x9e\xa0\x16\\7V\xbcD\xbc\xc5\xdd3\xb9\xa8\xf7\xcd1\x9cWP\xac\x02\x0b\x9ej\x19q^\xb4\xac=\xd2\x8b\xadr\x8a3\x12\xa8\xf1\x9e\xff\xd3\x87\xab\xb3\xd9\x87\x8b\xab\xf3\x0f\xefg\x9f\xde_^\x9c\x9d\x9e\x7f\x7f~\xf6\xce\xf7}\x10\xbd\x93\x00\xac\xda\x06t ^\xfb\xae\x88<\xe3og\x97\xc8#\xdf~wy\xf5\xf6\xfc=\xf2\xe8\xf7\x1f\xd0\x07\xce~>\xbf\xfa\xeb\xec\xa7\xb3+\xdf)\x06\x03Hj\xaa\xee-\xe1G\xe8y\xec\x9c\x16~Z\x95!:\x8a\xb2pwQ\x96\xdei\\\xe7\x85\xba\x8e\xeb\xf8X\x07r\x9d\x13\xe8F\xee\xc3\xa3\x9dI\xd9\xa4.\xd5\x9f\x8c\x1b?\x95\x89W\xec\x075D1)UVp=5Q+\xeeb\xa47\xbdU~\xc1\xfa\xc3\x82\xca\xd4k}Y\xdf\xb1\xa6*\xaa\xb9=\xa3\x89\x9c\xe9\xbf\xf1^GX\xfa\"U\xfd\xba\xde\xd8\xd5\x0d,z\xb8.\xf7\xb7\xb3\xcb7\xe3?X\xc5?\xea\xc9\xcb\xb4\xc2u\xe7z\xe3\xfa\xe3@\x05DAX{\\\xe9\xfd\x877\xa3\x7f\x0f|\xb4W\xc9}\xef\x1d_\xa3\xffex5\x19\xd0\xb8c\x1c}]\xf5*\xcc0\xd8\xe0\x9f\x82\x9f\xe5%\xd9\xc2z\x12\xfa6l\xabRr\x0b\xb2\xfa\xa2\xfb\x8b\xff\x08\x14\xd6nVed{\xc5\xa8\x9a\x96mzs\xb7\x97\xee\x93^:\xce\xc24\\4\xd5j\xa4\xba7\x95\x98\x93\xa3\x1f\xdb\xb7\xaa\x90y]\xb5ek\xf6\x84\xed\xe0\xce\xf3wGj\x0c\x11\xf3\xc4#\xb3Z\xe7w\x9e\xafc\x0c\x1a\xa3\xbe\x1d\xcc\x1er\xa2<5QS\x83\xd4N0\x93v[\x94\xb6\x08\xe8}\xf6F\xbb-\x02\xee\xcb\xe7\x8f\xb4\xdb\xa2\xfc\xfcN!g\xd5\xb7\xfe\xc7\x8b\xd3QiD\xce\x129\x1b}gc^\\@\xe4,\x91\xb3\xde#\x89\x9c\x95F\xe4\xec\xae\x119K\xe4\xac\xcf\x88\x9c%rV\x1a\x91\xb3D\xce\x129K\xe4\xac2\"g\x89\x9c%r\x96\xc8Y\x9f\x119K\xe4,\x91\xb3D\xceZ\x96\x83b$rV\x1a\x91\xb3D\xce>_r\xd6\xb9y\x17\xed\xb3\x08q7\xd2>\x8b\x07tn\xbc\x8f\xd2>\x8b9\xbcH\xfb,\xd2>\x8b\x7f\xe0}\x16%nu\xf2\xab\x84\xbbB\x1b,\xbe\x94\x88\x98\x9d\xfa\xb1pl\xa5X\xf7I \xe7\xef\x8e\x142&\xf7P4t\xdbNJ\xc8\x0b\xd3\xf2g\x9c\x11\xe2\xc32&1b\xd1\xbc\x8ehl!J6\x052:\"\x85\x87\xf8pLL`r&\x07\x94\xbe\xa0\x14.\x8f#k\x16\x076\x87c\xbf\x0c\x8e\xa4\xfc\x8dh\xa7\x08\xc1\xf8\xa9\x10>\x16\xbeO\x81\xee\x91\xb0}\"d?\x01\xae\x0ffk\xf0H\xae\xc6Sl\x0f\x1a\xcf\xd1\x88v\x06e\xf1\xfc\x8c\xd4\x8e\xe1:+\x96\x9b\x91\xd2I\\gD\xf22\x12;\x8c\xb2 \xdd\xa6?\x153\n*\xcb\x9c\x8f19\x1b\xe3Is1\x0e\x98\x89\xf1Ty\x18\x87\xca\xc2x\xf2\x1c\x8cx\x06\x06b(\xc1\xf6\xf8\x8c\xb9\x17\xd1\xcc\x8b\xe8\xec\xba\xb7\xd4\xac\x8bxs\xf7\xce\xb8\xc8\x99o\xe1\xea\x02H\x1c<\x95\x06'\x18\x9c`p\xe7\xef\x99\x06\x10\x82\xc1 \x06w\x1b\xc1\xe0\xd2\x08\x06\xdf5\x82\xc1 \x06\xf7\x19\xc1\xe0\x04\x83K#\x18\x9c`p\x82\xc1 \x06WF08\xc1\xe0\x04\x83\x13\x0c\xee3\x82\xc1 \x06'\x18\x9c`p\xcbr\x80\xb9\x04\x83K#\x18\x9c`\xf0\xdf\x02\x0c.\xc3o\xbe\xba\xcb\x1f\x07\xb5V\x7f\xd1\x1b\x00w\xc1\xb3\xcd\x97\xdb\xfd\xf7D\xde\x9e\xf6\xe4W\xf5\xff3QR\x08\xf0\xbb\x90\x87u\x84_\xb1ZY7\xd8\xdc\x8ae}\x07\xebz\xb1]\xb9\xb7\xfb\xfdK}\xf7\xd37\xaa\xa0\x17\xa6a\xcf\x97\xeb+\xab\xe5L9gw\xd2>\xbe\xdb\xfd\xb1#]\xb9\xceC\x0d[\xc9/u\x15\x9b/\xab\xe5\xee\x0b\xd4[M\x88\x06\xe2L\x15XS\xd6S\xb9\xc1A\x9b~`\xd5\x92\xdf\x9a\xfb\xaa\x8a\x07U\xfc\xb8\xe2zwk\x9c\xaf\x86\x07#\x9c\xa5O\xc8\xea\xaduY\xcdt\xb9\xcf\x97\xf3Z\xb0\xaa\x0e\xf2Y\xd1\x1b*\xacX\xd7\xdb\xea\xa9\x10\x8f\xd3\xba\xb4\xa1\x0e^\x7ff\x95^\xa1S\xcd1\x00\xba\x98#\x14\x95\xae\\h1\xf8\xfd\x87\xab\xb37rZ\xad\x8e\xedhPq\xfay\xc5\xf5\x9b\xbb[\xb6m\x83\xd1 \xfdZW_4\xfe\x8b\xb6\xe5\xb2*\xf8\xb6am7\xf0\x8ao\xb8e\xbd\xac\xe5;\xd3=\xf5\x1d8\xe9\xc7\xb2*\xd7\xdbu\xb7\xf3\xbb\xa4\xb7z\x86\x83\xd7\xc0*\xf1B\x08>X\xc2\xd6\xc5\xc3\xac{f\xb2=\xdd\xde{\xf8c\xf1 \xeb\xad.%\xab\xfdV\xb8LL\x8d\xc4\x83\xd9?\x90\niq\xbd{m;\xafJ^\x16+\xbd@\x0ec\xfc\xa1\xb3u]\xf1\xdb\x9d\xc5u\xb9g:n\\\xb1\x0fE\x8c*\xf9wc\xff\xc7\xb6n|\xf9O\xc7\xff\xe2:\xf2\x8e\xf1z\xf6\xb4\x8dS+\x03\xf5\x0d\xfcd\x08>\xf9\xc0]\xc9\x1b\xac\xfe)\xe7R\x01\x08\xc0\xf6\x81\xff\xbe\x8a\xb6\xb1\xc5\xf1\xd8\x13\xdf\x9c\xfc9\x19\x14S\xb3\xb9\x14TLO$ \x16#X\xcc\xf5{\xa6\xa9\x08\xc1b\x04\x8b\xb9\x8d`1i\x04\x8b\xed\x1a\xc1b\x04\x8b\xf9\x8c`1\x82\xc5\xa4\x11,F\xb0\x18\xc1b\x04\x8b)#X\x8c`1\x82\xc5\x08\x16\xf3\x19\xc1b\x04\x8b\x11,F\xb0\x98e9\xc0\x1d\x82\xc5\xa4\x11,\xf6\x87\x80\xc5zB\xc9*'\xf4%i\x9d\xd1\x05\x83\xd5m\xb3\xc2\xc1\xbc\xd6\xbb\x17\xde\xd4\xcd\x91\xd1\x8fTR\x8f\x83\xc2\xbeRA\xfa\xaf\x8e\x86\xee\xfdJ\xc6\x90\xc5\x0f\xe2C\xeb+\x1d\x16\xff\xea H\xae1\xc8e 2u\xac\x13\xde2\x87\x0c\xf9\xad\xee\xaf\x9d:\x9b\xd2\x90iy\xc1\xb7m\x00\xe1\xd2'\xbe0mz\xa6\x10\xd7\xc83\xb6\xf1/\x10\xfe:\xf4N\xfdz\x90\x88\x80I\xcfc\xa7q\x1e \x05\xf6GE\x11'@.+\xf5\x96\x1a\x1c\x8c\x14\xe7^#\n|2IK\x0e\x14F\xca+x,\\\xa8,5h\x18kF\xc1o\xd1\xa1CeY\x02\x88\xca\x92\xc3\x88\x91\xf2d\x901!\x98\xa8l\xcf\x90\xa2\xb2\xb4\xc0b\xac\x1d*\xe4\x91\x14^T\x96\x1ad\x8c\x14'&/)\xa1FeI\x01\xc7X\x7f\xef\xc2\x91\xd8\xb0\xa3\xb2\xe4\xe0c\xec\xf9l1!He\xd9\x02\x91\xa6\xb8}\xc2\x91\xca&\x04%\x95e M*K\x0fP\xc6\x1e\x133\xef\x89?Q\x07 V*;T\xc8R\xd9\x01\x02\x97\xcaR\xc2\x97\xca\xd0A\xcc\xd8\xa3d\x858\x91\xa1Le\x19\x03\x9a\xca\xb0aMe\x8e\xef\xab\xf8{!5\xc4\x19\x1b\xd6T\x00\x14\x11\xe8T\xb6w\xb8SY0\xe8\xa9\x0c=\xd9C\x04@\x95\xa5\xcd\n\x93\x83\xa1\xb1^z]\xdf1DHT\x19\xbe\xae\x19\xc3\xa3\xca\xf0AReYC\xa5\xca\xa6\x04Lc\xbd\xad\x8d\x87M\x95M \x9e\x86\x8b\x13W\x8d\x85P\x95\xe5 \xa4*CF\x04\x95E\x83\xaa\xca\x12B\xab\xca\x82\xd1\x10iS\xc2\xac\xca\xe2e\x07\x96]\xb3\x05^\x95Mq6>\x08\xab,\xde\xde \x01Ye\x93\xc2\xb2\xca\x82>\xce\x17\xa2U\x86\x0c\xd4*\xc3\x84k\xbb#\x11A[e\xa8\xbb\x90\x1e\xc0U\x86\x0f\xe3*\xf3\x07s\x95e \xe9*K\n\xec*\xdb'\xbc\xab,\xee\xec\x84P\xaf\xb2\xec\x01_e\x88\x9a\x06\x9f\x94|!`e\xd1@\xb0\xb2)\xe1\xe0@qF~8\x14\x14V6%4\x1c(\x8e\x9b5+o\x80X\xd9\xa40q\xa0<\xf5\xb5\x1c[@@\x84\x8c\x95\xf9c[\xca\xfc\xe1ce\xe9A\xe4@a\xc1\xf0\xb29dB\x909P\x9e\x9am\x06\xd7\xf9\xf2\x05\x9c\x95\xc5\xc3\xce\xcaR\x83\xcf\xca\x12B\xd0\xca\x92\x03\xd1\x83\xd3\x90\xe1he\xe17w$L\xa8\x0c\x1b,\xc4\x06\xa8u\xa9\xa9aj}ZJ\xb0ZY\xd0\x05S\x02\xd7\x81\xe2\xac\xc00\xfe\x91\xc2\x05\xb1#\x8fK\xb5\x0c\x87\xb2\x95e\x0ch\xeb\x02Came\x81\xe0v\xe0\xac\xa9aoe\xf9zuB\x08\\\x17\x8c\x0e\x84+sOfTf\xdd\x9e\xf4\xe3-\xef\xbb\xed\xd9{6\xc8\xd7^\xdb\xd1\xf6\xf6\xfe\x9e\xc3\xef\x0b\xb9\x17\xfc\x11\x94\xbc54[\x0b\xdbJu\xe6\x85\x02s\xee\xcbv\xd8?0\xdaY\x06OG\xc9g\xb9J8\xe9\xc1\xfe\x8f\x17\xa7\xe3\x06\x90\xa0\x16 je{\xbb\xa52\xf3\xaa\xef\x92\xa0\x16\x82\x8aw\x05B\x93y\xf8d\x12\x9e\x04\xb5\x92Yw\x12\xd4\xb2\x0dA\xb3g\xe3\xd8\xf7#\xd8'\xb0\xebY\xa8\xf5t^\x9d\x04\xb5\xf6\xe1\xd2S\x88t4\x8bN\x82Z$\xa8\x85\x9e%%3\xe4$\xa8\x85\xa2\xc4\xa7\xf0\xe1$\xa8\xe5;,\xca~'P\xdf\x18\xb9\xa8\x14\xd2\x9b\x04\xb5HP\x0b\xc3f\x93\xa0\x96\xb4}\x88k\x12\xd4r\x95\x14\xe5\xa8\xa7\x10\xd4$\xa8e\x1b\x82\x8e&A\xad |s\x9clNe\x9a\x13h\xe6d\x8e9\x8d`&A\xad4.\x99\x04\xb5:#A-m;\xbb/*J\xcd*+\x14\xcd\x1c\x9d5\x08\xe6\xf6\xb8\x1b\x1fnk8\xac\xe7\x93\xe2\xa5_\x0e)u\"\x9a_\x08\x1dE\xd6\xe5\x90\x88\xa8\x13\x0b}z\x144\x84\x7f>-\xf2\xb9\x8by\x96\xd5\x1b\x15\xd6\xb7\xfe\xd6K\xc5\xdd\x14\xab6\xa2\x15\x07N\xca\x1fO\xf6\xc7\x1e\x96\xc8\xc1~\x82\x1fE\xed#I}\x04\x9d\x9fD\xe4?\xcd^\xae{\xdeWSI=\xd0\xd4\xde\x8av\x07\x8c\xc6J\xb5]\x9f\xae.k\xe1\xa6\xa9\xd7OR\xe3\x9e\xf09\xfe\xcc\x1e}\xd5\x1e\xbde4$S\xe8\x99z\xc3\xf8\xb6\xa9d\xecBs\x1b\x9aG\xe8\x88\x1a\x19qX\x8e\x96\xe6;\xcc\x98G(\x99\x0f\xe2\xbbJ\x89>B}s\xd32\x0eu\x03\xc3\xea\x82\x151m\x19\xcf\xec-\xcf\xfa\xb3\xc3\x89\xaa~>?\x8e\xd6~uc\xa4+e\xceH97\x7f\x93c\x92\xd6\xbbT\x8b\xef\xb7\xac2\x8e\xdfV]\xbcc\xf4\xf6>\x97\xa5\xadD\x9f\xef\\\xa8\"\x04\xdbV\xb8\xfa3K\xf4\xe7\xb0\xf8\x03;\xd7\xb3\x8d\xb3\xe5\xdeU\xb9.\xb1\xde\x95\xc7\x1a\\\xc7\x87\"\xa9X\x98\xdd\x83\xd5'\xa7\xf8uP\xdaF\xad|\xdb\x7f:\xbf\x81\x15\xbb\xe1\x06\xdd\xd2,\x97\x99\xf8\xc80\x9ez@\xd4E\x84\x9f\xaf\x1f\x81\x15\xf3[(6\x9b/\xe8E\x1b\xa8\xea\xcf\x0f\xf9\xd2:CxT\xf6\xd0Z\xca\xa3\x82\xf8\x8f\xb2Z\x94\xf3\x82\xb3.\xa0o2\x12\xc4\x81\x0eQ\xbf\xb2\x9a\xaf\xb6\x8b\xd1\xcaC\xa1\xae\xd2\x11\x15\xa3;&\xf9\x1c+\xd0'\xb7\x93\xb4\xf9\xc4Aa\x9f\xce\xc7\x13\xdbQ\x13\xe4bM\xc3ZMR\xc9\xc7\xab\x7f\x1e\xc5#w\xac\x9f\xa6rY\xd5\xcd(Lj\x9e\xc6\xe1%\x94g\xf6\xbd\xb1\xd7u\xbdb\x16\xe5\xec\xb8\x81\x0d\xbbc\xcd\xe0\xd4\xd0\xcd\xd3G\x8fo\\i\x01y\x0ds? \x83r\xc45X%\x01\x92\xbaY\xb0f\x1c'\xb9,\xab9{\x03J\xf9\xf6u\xbb\xf8\x0c\xfft\xfc\xcf\x7f\xce\xea\x0d\xf4\xc6\xe9\xe6\xb5y\xf2k\xf7IT.\x82{\xa7\x9b\xd9\xa0Q\xdf\xdd\xf4|\xae\xa4\xc9z!:s\xe8\xf9;s\xab\x7f\xfb\xf2\xbb>Vn\x12\xcf\xeb\x13\xd1\x8d\xc6y\xa3\xf0iX>\x97G\x122\x9ebKp\x8e\x10\xcd\x8d\xfaA\x19&ll,\x15\xfd\x0b\x16\xc6'\x88\xe5fE\x00!\x8e\x01\xc2\x04\x140\xdc\x80T\x91\\\xd7*\x7f2\x12\x08\x13\xb1\xc0`\x81\xc9\xf2\xb8{\xe2\x81\x90\x8c\x08\x06\x8b\x9a*\x8c\x9b\x15\x15\x84D\\\x10R\x91\xc1p\xcf\x9e$\x88\x9b\x15\x1d\x04\x1c>\x089\x11B\xd8\x1b#\x84i(!\xe4\xc2 a\x12R\x18~\x1c\xf0\x02\xb8\x07@\x0b\xe1\x80x!\x1c\x061\x84D\xcc\x10\xa6\xa1\x86\xb1!\x98\xa3pC\xc8\x8b\x1cB\x02v\x08\xe9\xe8!L\xc0\x0f\x11C&V\xeavo\x0c\x11b(\"\xe0\xa7g\x08$\x11\x12gq\xc9hb\xb0\xb4\x14q[l-3b\x8a\x90\x84*Bn\\\x11&\"\x8b\xe1~\x85\x14\xb4\x9d\x88.z\xcb\xe3H1\xdb<\x08#\xe0I<\xc0\xa0\x8c\x90\x863B\x8c?\x9a\x885\x02\xa2\xdc\x00\xe2\x90 q\x84I\xce\xc5\xa3\x8e\x80h\xe5\x04\xe4\x11\xa6b\x8f\x10Q|\xcb\x86?\x02\x1e\x81\x04$\x06 h\x14\x12p^OG\"! \x8b\x84\xa8=\x0b\xdc\x1bN\x14\xf6i\xf9\xe0\xde\xfc\x82\xb0X9X\xdc\xe0\x91O\n\xb6\x1b>\x82B\xb0\x892\xb0\xbd\xdc\xab\xa7\xbc\xa8\x08\xac\xb9J\xaa\x04,\x8f\x91\x1bqn\x03!\xfe\x1a}\x0b\xa3\x85_\x11%\xc5E_q\x85\xa0\x05_#\xc5E\xc5^\xa3\xd5\xc1HA\xe2d^\xb3\\\n!\xf0\xca\x9f\x01M\x14\x95u\x8d:\x03P\x92\xae\x88bp\x83T\xb2\x98+B\xb25\xaf`+R\xaeu\x8aX+R\xaa5\xealL\x07F\x89\xb4f\xb9RX\x9e5z \\\xbf\xc9&\xcc\xdaO\x8d\xc7\xbf\xc4+\x92O\x92\xd5]\x8b\x04\xbdI\x94\xdc\xe4H`rT\xde\xc7\x8bS\x92\x97\x04\x92\x97\xcc6\xb2\xa72\xa6&0\xef-\x10\xcb\x97feKI^\x92\xe4%{\xcb\xca\x8c\xa6\xf0\xa2I\xac(\xc9K\xee\xcb\x85N`B\xb3\xf0\xa0\xe9,(\xc9K\xee\xc3~\xa6p\x9f\x13\x98O\x92\x97$yI\x92\x97\xc42\x9bYy\xcd)\xac&\xc9K\xfa\x0e\x8b2\x99 <&F<1\x85\xc3$yI\x92\x97\xc40\x95$/)m\x1fn\x92\xe4%]%E\xd9\xc8\xa9\\\xa4\xf7\xdd@\xf2\x92\xbbF\xf2\x92\x13x\xc68\xcb\x98\xca1&0\x8c\xc9\xfcb\x1a\xbbH\xf2\x92i|\"\xc9KvF\xf2\x92\xdav\xe4%K{\xe4\x1e|IZ\x87\x0c\x02bj\xb5\x1f\xca\x05\x8c\xa4$\xed\x11\xa9\x94\x9b\n\x0e\x86\xc1^\xe9\x857\xdb]\xa1\x17\x9c\x9e\xd1\x9e\x020':\xe2\xdf\x06\x94`\xde\xe9C:%\x98B\xaa:\xe9?\xca\xd8_[V\xcb\xd5n\xc3we`\xde\x0dH\xb5g\xab\x02cZ\xf7#\xd2\x8eH;iD\xda\x11iG\xa4\x1d\x91v\xca\x88\xb4#\xd2\x8eH;\"\xed|F\xa4\x1d\x91vD\xda\x11igY\x0e\xea\x89H;iD\xda\x11i\xf7[ \xed\xba\xc0\xb3\xaf\xfe\xdd\x01\xa3\xad%U\x0cPG\xf6X\x0b7M\xbd\x1e\xb4\xa3\xcd\xd8\x90}\x01\x0b\xb9\xbb^\x80\xa8\xb8\x12\xbf\x7fTp\x90A)\xe4[B\xae\x9e\x0f7\x14\xbc\xab9\xf3c\x12VI/L;\x9f)*!\x1b\xb7;\x91\x1f\xd4E9\xc0\xbe\xf3\x8d\x12R\x11\x93%\xf1\xdb\xee\xfb\xd3[\x13\x88\xc6\xe1\"\x1b\xf9EV\xa0\x10\x1b\xf8EJ\x08o\xdc\x17?\x19\xb5a\x9f\xb7\x18DT\xdb\xea])\x91\xed+G\xc8\x83\xe2\xda\xda(\xaeMq\xed\xde(\xaeMq\xed\xde(\xae\xcd)\xae\xed6\x8ak\x1b\xa3\xb86\xc5\xb5)\xae\x8d\x9c%Q\\\xbb3\x8ak\xdbFqm\x8ak;\x8c\xe2\xda\xcec(\xaeMqm\x8fQ\\\x9b\xe2\xda\x14\xd7\xa6\xb8\xb6e9b\x8c\x14\xd7\x96Fqm\x8ak?\xdf\xb8\xf6\xbeq\xe2\xbb\x9a\xb3\xd0\xeeR?\x89\xdf\xbb\x08\xb1\xf9\xfa\xe0\xa01j\xa2f\xf6\xef\x12\xe5\xa9)\x9c\x1a\x0fw\x82F\xb4\xd3\x9d\xb4E@k\xb17\xda\xe9\x0ep3\xcc?\xd2Nw\xf23'\x85PT\x1fV\x1f/NG\xa5\x11\xa1H\x84b\xb6\xf7(\x11\x8aD(\xba\x8d\x08EiD(\xee\x1a\x11\x8aD(\xfa\x8c\x08E\"\x14\xa5\x11\xa1H\x84\"\x11\x8aD(*#B\x91\x08E\"\x14\x89P\xf4\x19\x11\x8aD(\x12\xa1H\x84\xa2e9h1\"\x14\xa5\x11\xa1H\x84\xe2\xf3%\x14i\x8f\xbb\xa9\x1b\x88\xd1\x1ew\x07tn\xbc\x8f\xd2\x1ew9\xbcH{\xdc\xd1\x1ew\x7f\xcc=\xee$iu\xf2\xab\xe4\xbaB\x9b\xdb\xbd\x94t\x98\x8d\xd8/\x1c\xdb\xd8\xd5=l\x7f\xfe\xeeH\xd1br\xff\xba\x97\xba<7z\xff\xc2\xb4\xfc\x19\x93\xf7>,c\x12#\x16\xe5\xe7\xa3\xb1\x85(\xd9\x14 \xe7#\x85\x07\xa9y\x1ea\xe6\x9fb\xdf\xba8+\x1fu\x9f\xb28'?\x8d\x92Oc\xe4\xd3 \xf9$>~\x12\x1d\xbf\x07\x1b\x8f \x1b\x19\xcb\xcc\xc5O\xa6\xe2\x9f\x94\x89? \x11\xffT<\xfc\xa1h\xf8'g\xe1\xe3$#\x1c\x97p\\i\x84\xe3\x12\x8eK8.\xe1\xb8\xca\x08\xc7%\x1c\x97p\\\xc2q}F8.\xe1\xb8\x84\xe3\x12\x8ekY\x0e4\x92p\\i\x84\xe3\x12\x8e\xfb[\xc0qe\xf8\xcdWw\xf9\xe3\xa0\xd6\xea/z\xfb\xcb.x\xb6y\xc2\xbd/\x0dw\xb5.+~r\xf7\xcd5\xe3\xc57'EUm\x8b\xd5LNU\xda\x1e\xaeq\x81Vo\xe5\xa1\x17\xdd\x91f}\nD\x81b\x18UeA_\x96z\xd5\x99\xa6\x0d\x08\xabqa/L;\x9f)h\xe5\xf1\x93m\xc1e\x9e\xe0\x02O|\x01e\xe7\xf2&&\x1b\xbd\x07\x8e\xc2\x06wEYX\xbaK\xf6\xa0\xf1\x1dCE\x87]\x05\x9d\xect$\xdaZR\x1bE\x8aC\xcb\xa4\x07XdM\x1a+\xcb\xeafe\xa9\xfc\xb9\xc6\xc8ss\x88\xa6s\xdd\x0fiWP`\x80\xecJ\xd2?>\xdb\x91q\xe4\x15\xdb2\x8d\x88\xbd\xbb<\x83\x9e\xc7\x9f\x8e\xa2\xdc\xb0K\xe7\xeb\xe9cZ\x7f\xe3?^\x9c\x8e\xe7l4\xb6\xd1\xd8\xf6\xcc\xc769W\x0fL\xfe.\xe4\xef\x83QM\xe5g\xc8\x94\xa4\x9b\xeeQ\xec\xe7\xfc\xceQ\xed\xc7\xb2\xe2\xaa(\xfd\xeb\xb3\x1d\xd6l\x87\xd86\xfcDQ^\x19j\xb9\x1a\x07\x98\xcf\x93u\xbd\xd8\xae\xf2*\xb9\no\xcf\x16\xac\xaa=\x90x\xb4\xc3hmQ\xae#a\xf3\xba\x94\xdf\xb6\xa2\\\xc7\xf1\xdd\xf8:k\n\xcef*\xb4\xbd\xdf\x95\xd7\xc5C\xb9\xde\xae\xcd|U\x15)>\xb5\xfb\xb1\\\\+X\x99u\xf1\x90\xa7\x12)\xd7,=x?\xfa\x9ae\x85\xbb\xe6\xb2.V\xb3\xeb\xbaZ\xb0\xa9\x99\x18\xfa\x8a\xa2 q\x937\xac\x99\x8b\xb7\xa6*\x13\n^\xaf]_\x07\xd7\xabz\xfe\xb9\x9dmX3{d\xc5\xb4D\x0dD\x16HW\xbd\xee=\xa7.,\xaa \xe2\xc2\x83s\x10/q5\xac\xa0\xde\xe0\xfa\x9d\xad\xc74\xa2V\xe9}\xed\xfa\xfd\xb9\xbd\xaf\xd5\xcb\x06\xfd\xc6\xb6\xbe\xe2uG79rE\x8f\xb5to+\xc5\x98\xabW\xd5\x91NW)y\x0b\xed\xf6\xba\xdd\x14\x125\xeb\x17\xea>\xb3G\xe7\xdb\xfd7\xf4f\xc7\xbc\xd8\x07\xefu#\x00\xdfy,\xeb\x0b\xdd\xb8y\xd2h;U\xd2=\xd0\x87\xbdgN\x1a\x88i\x10\x06\x1a\x84\xdd\xa7?\xb7A8\x14\xb30\x0f\xa9\xaf\x07vc\xa5=n\xa8!UL\xae\xfb\xbd\n\xfaQ\xf7f\xc8\xeeN\xc9\xba\x1e\xb5\xd0T6 \x11\xf2\x99=\x0e\xaa(\xfemB)]\xcdt(\xde\xb4)k-\x13\xdft\xa6\x0e\x81\x97\x9d5\x12]\x9a\xa3\xbb\xf7\x9dL\xcf\\\x89w\xca\xb2l9k\xd8\xa2kV+\xa3\x95\xe2\xc7\xcf\xecq\x9c\xc89n\xfb\xc0\x89/\x9d)\xf4\xff\xeaL'\xef\xaa\xa4\x7f|\xb6\xaf\xc6\x91\xa7m\xfb\x12\x03D\xf8\xad\x88x\xc4\xc5M\x0d\x9f\xbc\xf7\x1ek\xde\xab\xc7\x83:}_\xb5\xb2\x05\xfb'\xb0\x1bN\x14V\xa3\xfb\xa9~`\xddL\xa9\x8c\x95\xb3\x87\xb2\xe52O\xc4s\x8c\xfdT\xbb\x8fq\xf7\xed\xc1\xa1\x88\x89@\xd7\xed\xbb\xb9\x80=\xea\x0c&\x05\xea\xd1\xebd\x86v\x93\\\x8a\xd5\x0e\xd3\x8e|\x9c\x03\x8d\x8d4\x93f\x1c\xa3_i\xc6q\x80\x19GT'\xe5\x1f\xdb\xa2)*^V\xac\x8f\xda\xcfyy\xc7N~\xe5\xf5L\x13\x05!\x85\x94\xf3\xf6\x7fuE,`~\xcb\xe6\x9f[\x05\xa8B1WB>\xb7E\x0b\xf5\x86\x9b\x04\x9d\xfe\x92\xee\xd8\x94]\xa0>\xe0\xd9\xbe\xd3\xcav\xd67\xc7\xb1\x94\xa6\x8a\x1c+\xf9\x18\x8b\x8f\xe2\xc3\xf2\xe5\xba\x93\x96\xecQ\x8b\xe4\xe6\x0e\x8d|\xec(i\xd7\xeb\xca\x10C\xed\xe0\x8e8\x87\xdb\x8f\x17\xa7\xfd\x90+q\xc4Q9\xc3^\"\x87b\xfa\xf8\xa2\xa1\xf0)\x87\xc2\xd0\xc7W\xff \xf9\xfa\xa0\xf5\xa8\xe9\xc5_\xf3O^\xabQ\xef \xab\xe0\xa8\xbd\xe55v\xcc~\xbb\xe5\xb5y\x8e[q\x84n\xd1\x96\xd7\xaf\xbb\xc7\xb8e\x9c\x97\xd5\xd2Lt\xecaH\x0f\xec\xceO\x17k\x08\xb9\x1a\x0eQr\xadF9\xe1\x18\xceo\xa0\x80\x9b\xa6^\xdb?wL\xbb\xa4\xc1M\x95^\xf4.\xd4US\x8b<\x05\x1f\x16\xa0\xb9\xcd\x17\xd6\xf1J\xc3\xecXg\xe9\xfa.w$'u\xfe\xd6w\x05\x8a\x1a\xa9\xd5K\xabY\x06\x17\xed\xae\xa6\x0f\x1f\x12i\xb6\xc7_\x0c\x9b\xf3\xec^j\xc2\x133G\xed\x8c}\x91\x11\xa5s\xf8\x1e\xa3B\xe4\x89n\xd8\x9c\x95w\x12tS?\xec\xae\xc2\n\xb3\xbbQ\xae\xca\x8c\xbb\xa6\xfc\x8e\xd2\x1a{\xc1\xca\x98\xdb\xe4\xabH|\x8a\xa1\xac\x19\x85\xb6\x9cO\x83Y]\x15\xdfU\xf7\xbe]3\xc1Txw+Sc\x08\xef\x845\xca^\xc3\xdbOW\x1ff\x1f\xcf./>\xbc\xbf\xfc\xdd\xd9\xe9\x0f\xe7\xef\xcf\xbcn\xd6\xc2a)\xf5\xc2\xdc\x1b{\xe08\xabx3\\\\\x13w\xc9\xfa\xd6\xad\xa5\xa4\xc2\xee\x9b\xac7\xd1\xcf\xa0\xa8d\xf6\xf3\xee\xdd\x89Wh84(\xbd\xc8\x9d\xde\xc2*\xae\x96\xc6\xc4\xc54\x88,\x07ZG\x81\xa3\xa9\xa0\xb2\x8c[\xde\xee\x12\x13\xae\x81SYp\x88\n\x0fP\xb4\xfb-\xed~;\xb2\xe7\xb2\xfb\xed`\xea\x81\xfbz\x1b\x153\x9c.\xd2\xc7\x1b}\xbc\xfd\xe6?\xde\x1c_3bp\\2.\xe5}a(j\xb7\xe7\xe7\\_U{\x9a\xe7\xab\xecx*h $\xc9)\xa1\x95\xe8\xc3k-n\xee\x18I\xf7\x8e\xa39\x05\xa7io\x00\xf0\xf7a\xda\x1b\x00ho\x00\xda\x1b\x80\xf6\x06PF{\x03<\xd3\xbd\x010\xab\xa7'\xbf\xda\xefaZL}\xb6\x8b\xa9f\xfaM\xab\xa9\x96E\xa7\xf8\xb4\x9a\xea,\x87VS\xe3\xab\x96\xeesh5u\xe7\x88x\x85h5\x95VSi5\x95VSi5\x95VSi5\xf5\xb9\xaf\xa6f\xa93\xad\xa5\xa6-T\xd1Z\xea\x01\x9d\x1b_\x05\xa4\xb5\xd4\x1c^\xa4\xb5TZK\xfd}\xad\xa5\x06\x96Ro\xb6\xd5\xa2\xf5/\x97Z\xbe\xb2\x90\xf0\xef\xc5Ij\xf1\xd4\xde^\xb5\xb8\xae\xb7\x1cd\x89\xaa\xab\xcb}?\xae\x19\xeb\xef\x965\xf7\x88-\xa1\x9e\xdf\xc0u\xcdo\xa1\xb0\xa70E\xb5\x18N\x12\xc4\xbd\xb1\x163\xabGh\xb7\xf3[\xfb:]\x81\xaab\xe35Kh\xd8\xb2h\x16\xf2\xa5P\xdf\xf4\xb7\xec\xfe\x96\xe9M'\xd8\xe3K\xdd\x0cX\xb0\xf9\xaa4\xcb\xaa\xf2\x19)F\x13\xac\xbe.v\xff\xd8Vf\xf3!-\xc2m\n2\x95*J\xb9\xc6$\x9ch/\xe2\x9aE_\xb9\x98\x10Xw\x95\xab\xbc\xac\x94\x15\x1e\xd4\xa8\xb2\x96p\xc3k\xc0U]\xbd6\xb5z\xe1\xba_\xba\xaarI\xbcz\x04\x7f]\xae\xe4\x00'?\xfe\xa0\xec\xa7ze\xa56\xa1(;\xcf\xf9*\xe4\\F\x1ew\xbf\x17C\x1f=\xbb\x85\xe4\x7f\x8c\xea\xfbL\xbeo\xb2/%\x07\xd6Uw?=\xc47\xa5T\x9ci\xd8\xbc\xdc\x94\xd6\x06f\xf3Z\x89J\x07vK\xebF\x13\xd7(2\xb4\xfey\x9b\xd9\x9d\xcc\xe7\x95\xf0\xbdP\xb6_\x06($\xb8\xcd[\xf9n\xc9Q}\n\xd9\x83lU\xfb\xfd&]\xb6)\x1a\xae\xa5\xc7U\xe9\xf0\xc8\xb8\xb5\xa6-\xfd\xefv\xa6\xfc\xe9\xc0\x8e\xf3vhe\xb1n\xad,\xa0\x82e\x0cq\x9b\x84\x15k1\x1d\xcaP\x14\xf6\x9e\x03\x9c\xd6\xa5\xbd\xd3*\xaf?\xb3J\xefh\xa3\x1af\xa6r2\xad\xd5\xbf\xf5\x93\xa9|h[%\x80\xf7\x1f\xae\xce\xde\xc8\x91Z\x1d\xad\xb5\xde\xd5'\xf7y\xc5\xb5\nv\xb7\x05R\xbb3\xdd\x19\x9a\x16\xc9VB\x1d\xa1\x0b\xb7\xe5\xb2*\xf8\xb6a}DTL\xfb\x97\xf5\xb2\x96\x1a\xd4\xbe\xb0 \xd6\x91j\x1c1\x81\x1c\xd5\xb6~\xf3\xac\xb2\x1a\xbe\xd1\xb4\xfc\xc8\x1e\x01\x1d\xf3\xca\x0c?\x1f\xbe\x94Be\xd8\xb6u\x93\x86ab\xa1\x98 \xa8Y\x04\x93\x93!vW\xd6\xdb6\xb0=_7\x8bq\x1c\x81\xa9\xcb\xceL\xd0t\xdb\x967\xdb\xb9\xb8\xb9#\xa5\xf6V\xdd\x16ga\xc3\x89bxh\x1fTn\xfc\x82\x95\x1f#\xd5M\xad\xa7\xa1\xaa#\xb4%\xd7\xb2\xa0\xde\x94J\n\xb9\xec\x1a\x85\\\xfa\x9f\xb0\xce\xa4\x90\x8b\xfc\xdc\x1c\x0f\x0e\x93\xa2.;#\x0c\x05^(\xf0\xf2\x1b\x0b\xbc\x8c\x1e\x8fi\x1fC\x8e%\x14p/\xa3\xc0\xc4\x15\xa4=\xa25\xa3\x16\xfa8\x9e~wI\xf5\x91\x01\xe77\xf6\xda\x83\xdb\x95z\xbf\xeab\xd5\xea-\\\x87K\x03\x90\xb7\xb5\x14\xe7\xc1x\x8b\xe2<\xca\x0e\xec\xdcx\x84\x82\xe2<9\xbcHq\x1e\x8a\xf3\xfc\xbe\xe2@q\x1fm\x14\xf71Fq\x9f\xa1Q\xdc\xa7\xb7\xd8\x0c\x8e\xe2>\x99\xe2>Y\xf2](\xea#\x8d\xa2>\x14\xf5\xa1\xa8Oo\x14\xf5\xa1\xa8Oo\x14\xf5\xf9-G}\xf0RI\x14\x04\xa2 \xd0\xf3\x0c\x02\x99\xe7\x8a\x82@\x03\x8b~kQ\x10\xc8i\x99b\x19Y\xdcFA\xa0\x1d\x8buke\x14\x04\x92FA \n\x02Q\x10\x88\x82@\xf1\xb8\x05\x05\x81\xcc\xc1\x14\x04\xa2 \x90\xb6 s}\n\x02E\xe7q\xb1\x19\x1c\x05\x81(\x08d\x17\x87\n\x02ei+\x85\x80\xd2\xd6\xd7)\x04t@\xe7\xc6\x83\x17\x14\x02\xca\xe1E\n\x01Q\x08\xe8\xf7\x15\x022\x11\xa0vU\xb4\xb7e\xb5\xec\xe2?r\x16\xa3Oq\xc5x.\xe4\xef\xb2Ve\xf7Ak\x7f\xc5\x9a\x12a]/\xb6+S\xbf\xc1\xea\xfc\xa5>D\x95\xf5\xc2\xb4\xeb\x99.\xcd\xdb\x1e\xb1m\x8f\x0f\xf2\xb6\\Vl1\xbb^\xd5\xf3\xcf\xed\xec\xbe\xac\x16\xf5\xfd\x9e\xdf\x93\xbe\xcf\xc9uY\xcd\xf4\xe56\xac\xc9s-\xcfB\xc0\xa2\xbe\xafx\xb9f\xb3\xbf\x17\xe5j\xb6\xd86\x9eE\x12\x88_Kv\xa2\xd9MS\xccE\x11\xb3E\xbd\xbd^1\xd9\x8eI\xc5E\xab\xbes=\xd5\x92C\\,\xbed\xa6\x1f1k=l\xf4\x94\xc9\xf9\x8d\x18\xce\xaf\xd5\x12\x88y\xe4\\\xb7_>\x84\xc3E\x0c\xdf\xfa\x82\x1c\x1b\xd4\xc5\xbb\xcf\xf0n\xb3\x0f\xfdo\xd1\xf6n!T\x9ep\xa2\xab\xfb\xf1\xe2tT\x9e^\xde5\xff\xa4\xaf\xf0\x1d\xff\xd0Wx\xf6\xaf\xf0\xe4\x17\x9e\x18T\xcaj9+\xab\x9b:\xf0\xde\xbbT\x87\x9d\x8b\xa3\xba\xb7\x9f>W\xab\xa0\xdf\xc8\xd0\xb8\x8c[\x17\xbcn\xcc\x8bm\xf8\xea\xb3\x8a\xd1\xbf?\xdb\x17\x9fh\xd5\xf3\xe8h\xfb\x06\xa1[^4|v\xcb\xca\xe5\xad7L\x17-$\xfe\xae\x85\xc0\xe0\xda\xdb_e5\xa00Q\xb3\xae\xc3\xc8U\xd6\x9b\xb2i\xb9\x98\xda\x17\xd5B\xfc\x99\xc1\x87\x8f\xe2\x07oq\xdbJ\xbcn\x9d\xdb\xc5\x88[\xb8`\x0f35M?p\xc3\xe3\xef5e\xe7\xa2J&`\xd8\x8a/\x9bF\xc6\x0c\xc5\x98/\xbe\xf4\xc4{W\xbe]\x86~\x19\xaf\xf9\xf6v]W\x0b\x16\x88\"\x96\x15\x14 \xa7Z2\x18\xba.\x1e\xd52\x99\x9a\x15A\x01\x9b\x86\xcd\xeb\xb5\xf8\xf6\xad\x1b\xa8j~\x0cW\xb7\xa5\xdf\xe1e\x05\xf3\xba\xfa\xfb\xb6\x92s\x05\x15t\x153\x7f\xef \xbf\\\xca+}'g{?\xcb \xd8/\xeau.\x86s\xd6\xac\xbb\x85ny\xbb\\_\x12\x83\xe2~,\xdb\xd6\x14\xf7]\xc9\xdf\x8aG\xf1\x17w\xbcQu\x8d\xd9\xb6\xe2\xe5\xf4Q\xba\xbf\xff\xa2?\xbe\x167h\xcf>pU\xaeY\xcb\x8b\xf5\x06d\xcdto\x18\xde\xf4\xb2\xd5\xb5\x87\x85\\\x11\xf0\x16\xb6*\xefX\xc5\xda\xb6\x9b\x7f\xba]\xc1\xeb\xf5u\xcb\xeb'\n\xbe\xfe\xac\x810\xd5\xa3\xd4\xc2\x86n\xd8m\xd1\xaa\xe0e_#x\xf5\xb9\\\xf9\x9eaa\xf5V~\xe6\xf7\x85\xb4\x8c\x7fm\xbe\xb7[\xc6\xfd\x9d\xaf\xae\xe6\xe3\xc7Iuv\x19\xbb\x17\xdf\x92w\xf5\\\xad\x08\xd4\x8d\xe1\xb5\xfc\xa5\xc9F\xcd\xeb\xea\xa6\\n\x1b\xb6\x80u\xd9^\xb3\xdb\xb2\xb8\xab\x1d;d\x81\xfc\xfe\x10\x9d\xd5|\xea\xc85\x05\xe6\x98v)K\xe8\x8a9\x86\xa2\xb7\xa0\xeb\x03\x9f\xd9F-@\xdd\xd5\xe5\x02\xb6U\xc5\xc4\xfb\xb5h\x1e\xd5\x8b\x0e\x1aV,\xc6\xeb#\xb6\xbd\xaf\xcdZ\xce/\x97\xdb\xf5+\xd7\x13\xfa\xf5/P\xac\xee\x8b\xc7V8\xbdX\xf9\xc7\x97\xc1\xf3}\xaa*\xe8|\xbc1\xcd\xfc\xc9\xdcuk\xe2aq#]\xa7x9\x9a\xcc\xd8;9\xda\xb6\xae\xab\x92\xd7\x8d^\x85.\x1b\xb7K\xbaGR|O\xdd\x95|g\xcf\xb2\xee=)/f\xd6\xff1\xb3)c\xa1(\xfc\x1e_\xe6\x14*\xa7P\xf9\xc8\x9e$T\x0e\xa8\xe7\xd9\x0e\x16\xa9V\xa9\xfbu\xcd\x16\x0b\xb5\x04\xb9\x1c\xc4\xca\xf5\x17f\x0b\xf7\xe2\x9e\xb9n\x8f{\xde2\xaf\x1bU\x86\\\xbb4x\xae.M\xbe\xc0\xe4\x12\x84\xed\x19\xa7;\xcc\x19\x97\xf5\xba\xaf\xf7\xaf\xde\x91O~\x0cmX!\xe6\x82\xdf\x15Mw\x93\xbe\x85o\xfe{\xe8\xa4\x81[d\xff\xfe\x16\xfe\xe4<\xe3?\x07\x7f\x0c.\x84\xd8_j\xa8\xe5\x10W!'\x83\xcf\xc6\x8f\x17\xa7c7\xd1\x1a \xad\x91<\xf9\x1aI\x88T\xa0\xf8vZ\xf0\x90\xe2\xdb\x07tn<2K\xf1\xed\x1c^\xa4\xf86\xc5\xb7\x7f_\xf1\xed\x97\xb8\xf5\xfe\x93_\xe7u\xd5br\x1b\xed\xefg;\xf8=\xfeh]\x8aO_\x10\xa5\x8e\xb6\x16\xf7\x85\x01^\x98f?\xd3(\xc0]\xb1\x9a\xd9>\xcb\xfa\xb9\x1d\\\xd8\x8fLn\xe2\x8b\xfa\xd1\xd9Q|1)\xf6\x81\x99u1?\xb0\x94\x1f_\xc8\xcf\xd0\xd8\xf8\x07(\xe4_\xc0\x0f/\xdfO\\\xbc\x1f\x8f\xfe\xbd\xa5,\xddg]\xb8\xc7/\xdb\xc7\x17\xed\xd1\xf7:\xbc`\x8f\xbb\xdf\x19\x17\xebQK\xf5\xb1\x85\xfa\xf82=\xae]\xd3\x96\xe8\xa1\xde\xfa\x12\x0f\xa7,\xd0\xe7\\\x9eO]\x9cOX\x9aGw\xb8}\x07\x97\\K\xf2\xf9\x16\xe4\x91\xcb\xf1\xf1\xe6\xe5]\x8a\xc7,\xc4\xe3\x97\xe1\x9d\x15\x1e\xbf\xf9}\xeb\xf4z}\x90-\xc4\x19r\xea\xe3(\xccE\xedc\xd7\xdf\xb2,\xbf\xd1\xea\x1b\xad\xbe9\x7f\x7fN\xabo\xf6\xd7\x88\xaf\x17\xda\xc7\x98'\xc2\xfc\x93\xd7:\xdc3zF\xad\xb2\xf6\xcc\x84\x89\x7fp\xf1\xe2\xb3\xfd\xbd\xb5`+\xb6\x94o\xb0\xf6\xe4W\xfd\x8f\xba\x91\x0d@j\xc9\xbc3'\xbd\xeb\x8b\xea\xbe\xc1\n\xb9\xc2\xd2\xff\xbd\xbe\x81B\x7f\x84u\x17\xebJ\xd2n2\xcf\xea\xe0\x93\xccu\x15}\xdc\xb3\xfd6\xeb\x1b>s\xd4\xd1\xd8\x97x\xf0\xfa\x9a\x85\x1f\x9e\xa0\x92C\xec\"\xd0_H\xf7\xa8\x00$\x06\xb8\x07\x16P\xefr\xfb\xd8\xd1\xf5\xcd#y\xcd\xe6\xb7\x7f\xfe\xd3kV\x89Ay\xd1=\xa1\xb5/\xcc\xa8L\x9c\xd9\x15\xe9&*@\x0dKj\xb2\xf0\x85Z\xbds\xfd\xfd[\xdd\x15\xe9ou{[4aM\x8f)MU\xa5\xea\x99\x98}\x03\xc4\xa4[\xff\xd8\xb09+\xef|\xfa1x\xdf\xf5c\xcb\x18\xef\x16\x9f\xc1\xfa\x83\xb4\xfe\xcc\xaa\x16n\xd9Jj]\x04\xc4;\x8a\xb9\x9c,\xeb\xcf\x8c\x00\x03t_)\xdd\x8c\xba\xb2z\xd7\x91\xfc\xa2.[(\xda\xb6\x9e\x972\xea\xda}\x11\xfb\x8a\xba\xab\xa5@\xc2\xa6\xbeW\x8b\xb0u\x15\x00\xdf\"\xb7\xf4\xbaX\x15\xd5<\xf2v\xcd0@Dd^P}&.\xf1\x82(\x06\xdbK\x12\xc5]\x10\x12.y\x05\\\x90\xf2-S\xc4[0.\xea\x1f\"\xfb\xc3@~+\x17+\x19\xdf\xaa\xed\x07\x8d=H\x01#\xafXT\xc9\xc5\x94\x8a\x17e\xd5\xdazj\xb6\xe9\x9e*W\xa3\x16\x8bR\x16\xcbk38tKa\x12\x86i\xb7%7\xf1\x08ga\xf3\x95\x0c\xc2u/\xec)\xdf\x92\xae\x17\x7f\xdfcV\xab\xd10\xd6\xbe\xec\xa1*\x7fi\xbb\xcfiF\xa9\x93>\x90F\xfa&\x04m\xfd>\xa0\xadp\xbf\x90_F\xae\xaf\n{\xd4\xc2/e8\xbf\x82>^\x9c\xeaa\xb8\xbf\x8f\xb4\x8a1\xfa\x95V10\xaf\x14eo\xe1\xd3\xc7\x1fN\x1a\xd6\xd6\xdbf\xce\xe4\xaa\x84ZF\xddV\xe5?\xb6l\xf5\x08\xe5\x82U\xbc\xbc1\xf1O\xd9o\x03\xd3{\xb9`\xc8\x9a\xb2X\x95\xff'\x94\xa5!g\x04\xf3z\x05\xd7\xdb\x9b\x1b\xd6\x98\x9b\xa6\xc39\xaamJ\xe6C\xbf\xac!\xa0\xfd\xb8bE\x1bd\xe2\x19|u\xf2\x15\xcco\x8b\xa6\x98s\xd6(\x19\xd2U\xd1rh\xd9rmi\xab|\xfa\xf8\xc3\xcbv\xbc^24Y\xa9n6\xef\xbf\xaa(\xeef\xbbZ=\xc2?\xb6\xc5Jxp\xa1\xfck\xf8\x06\xe1\xc9W\x85\x8c]y\x0b\xf9ET\xe5dY\xd7\xcb\x15;\x96>\xbb\xde\xde\x1c\xbf\xd3\xb9\xb6\xbf|\xadZ\"\x8b\xed\xf9'o0\x0cTl\xb2\xae\xcay\xb1\x92\xcf\x90\xff\xca\xaf\xd8\xf1\xf2\xf8H\xb8V\x06\xfc\xbf:\xfeJ\x8c_2r\xa2\xf5'\xbf\x0e\xcd@\xcf+\xd8\xc8\xf4\xda9;\x02\xce\x8au\x0b\xdbv[\x08w\xa8\x10\xde\xa6\\I\x91\x9bZ}\x0e\x95U\xd1\xf8\xf3\x10\xe4\x14\xe7q\xc3\xda\x0e0y\xf4_Z\x8dub\x9e\xc7k\xd8\xb6\xcc\xccEDG\x12/\xd7\xfa\x06\xdeV\x8f\xc7\xf0\xd7\xfa\x9e\xdd\xb1\xe6\xc8;s\x13\xf6\xe9\xe3\x0ff\xc6'\x8a\n&\xfe\xc8\x11\x94\xc1/\xb7\x9co~9R\xff\xdf\xfer\xa4\"N\xfa\xd7#\xd9\x1b\xe7E\x05\xb5|:\x85G\xfc\x052\x0e\xdb\x8d\xf8$x\xdc\x84\xae\xcb\x9a;)F\\pX\x17\x9bVu-Ys^wP\xae|\xb7\x95\xea=\x12\xc8/\xbb\xa9W\xab\xfa\xbe}\x13\xb8\xb7\xffU*\n\x9b\x16\xc1@-X7Z\x7fgn\xd7\xe2\x1b:P\xd0\xdb\n\xfezuu\x01\x7f9\xbb\x82\xba2\x8f\xa0z\xc6\x1e\xe57\x8b?\x11\xec\x7f\x8f\x1f\x8b\xab\xc7\x0d\xfb\xf7\xff\xfd\xef\xde\x13\xc0\xbc\xea+\xdd\xdf\xf4kD\xde\xa1MS/\xb6s&\xc3p\xe2\x15\xe6_\x96\xf8\xaf\xf0v\xb3Y\x95s\xfdN\x16\x93\xacB\xf8LM\xf9\xe6\xc5\\\x8c-u\xfdy\xbb\xe9f0\xd7E\x1b\xca\xf6 '\x80\xc9N(\xeb(C\xe1\xfc\x96\xad\xadgh\xa1\x1e\xa2\xc24\xa9\x0b\xa1\x85\xf2z@WP\x0e\x1f\x0d\xbb\xa9\x1bvd\n\x10\xe5\x16\xbc\xbc.W%\x7f\x84\x8a\xb1\x85\xa1\xe4\xe4\x90\xd7\xdc\x05Z\"\xdb2\xbf-\xaa%\x93'\xc9g\xf6\x18^}j\x19\xdc\xb1\xa6-ehUvO1f\xa9\xfeYT\xc52\xd4\xfa\xeb\x86\xc9%fS\xf0\xf1\xd7\x81\x0f\xdf\x9a\xb37\xc0\xc5;\xe4F\x87\xfd\x0b\xd9\x0e=v\xf5b\x9f\xd6\xa4\xd2?\\\x8a\xfeX\xcb\x19\xb8\x7f.\xa9\xdfe\xd7[1a\x15o\"\xa6WZ\xb8\xb9h\x97\xbf\xdf=\x97\xde\xa2$%+W\xf2\xc5\xf7\x7f\xe0\xe5\xf2\xb8a\xc7\xaa\xff\x17\x9b\xb2=\x9e\xd7\xeb\xd0h|)\x9f\xd4V\x87\x8c%\x0f:\x1a\xa5\xe0\x95N\xf2W\x1f\x16\xea\xd1\xfe\xda\xff\x12\x94\x18\xccu`PR\xd0j\xc9\xadU\x06\xbd\xc4\xb6a\xf3\xf2\xa6\x9cC\xcb\xd6E\xc5\xcb\xb9G;\xf5\x00\xa1\x98\xb1agI?\x8a\xe1\xe8\x9a\x99\xa8\xad5\xc1\xd9\x99\xc7\xe8\x97{q]\xdf\xf9\xfb\xb4v\x81~\x14\xa6\xae\x8b\xfc\xf2\xb6z\xfc\xc5Z\xcb\xa8\xa0h\xaeK\xde\x88\x87\xd8_CgQ\xe6\x1dQ\xacj\xdd\xf5|K#bt\x96/\x1aU\xc3\xeb\xe1\xb4p4\xfd\xebfu\x9e\xaeya\x1e\x9cUy-\xab\xad\xdf#-\xb4\xdb\xcd\xa6n\xe4\x1b|S\xcc?\x9fl+\xf1\x7f\xe2\xbd\xad\xfa\x85\xfb \xd2/z\xff\xc4\xa6\xbe\x81-W\x03\x9b\x19\x1eZ1\xb0\x9aU\x9eb\x05KV\xb1F.\x8e\xaa\x0f-#\xd5\xea,O\xd4G\xddB\xf7\xf5\xce\x1e\n\xd1\xf9\xe1\x9b7pQh\xa6I7\xa5\xe8\x9c^Vp\xfa\xdf\xfe[\xe05\xf9}]\xc3M]\xc3\xb7p||\xec\xcf\x8c\x11\x95)\xaaG\xff\x01E\xf5x,\xaa\xf1}S\xaf_\xdd\xd4\xf5\xd7\xfeC\x8f\x8f\xfd\xef\xbf\xf2\x06^\x89\xa2>\xc9\x86\\\xd5\xaf\xfe\x8b(\xeb\xeb`\xa2O\xa8\xbc\xff\x0c\xfb\xeeO\x11\xdf\xfd[qWds\x1e|+\xe7\x86\xe2*\x19\xe9\xd6\xa9^\x15\x85\x9c+\xa7\xbe;\xbb<\xfdx~q\xf5\xe1\xe3\xd7\xa1e\xfa\xbe\xa3\x86/\xac.\x1dv\xe7?G\xdc\xf9\x97\xda\xefI\xe9\xca7\xdf\xc2\x7f\xd9\\\x1f\x7f_\xd7\xbf\x1e\x1f\x1f\xff\xa7\xff\xe0\xa2z<\x12\xd3Pq\xc6FM\xa2~,\x9a\xf6\xb6X '\x87\x1b\x12r\xe1\xb8\x16\x81*\x947\xa3\n|\xaa\xd6}\x15d\x05\xe5\x03\"\x8f\xfa\xbf\xbe\x85\xaa\\\x85S\xfd\x82\xf5\xf2\xf4\xe4+\xb9\xa8<\xff\xdc\x8d\xc5\xe6C\x03\xae\x1f\xfbi\x97y{\xa8\xb5Q\xf7\xac\xd7del[\xcf\x9c\xe5\xa5cJu\"\xbe\xdf\x8f\xe5\x0fb\xba\xfa\x12\n\xebm'\xde\x84\xa2'\xf8\xde\x0d\xaa\x87\xb8/\xd6\xbdZ\xaa\xd5\xa3\xf9\xae\xdcY,\xe8\xa6\xc9P\xdcp\xe6Z)T&\xd71^\x9e\xbct_J\xbf\x13M\x95\xe5\xd7.0\xdd\xa3\xbf\xba\xa9\xeb\xe3\xeb\xa2\x91\x8d}8y<\xfe?_)/\xcao/gy\xfeOQY\xd5\xafD\x19\xe2u\xe8<\xe4\xdf.?\xbcw\xff\xf2\xed\xb7\xdf~\xeb\xef\x03\xe2\xbc~\xcdE\xf3\x91r\xbb\n5 R\xdfu\xdb\x96\x19`l\xb9]\x15\x9ed\xf5\xddbT\xf0\xa4\x9f\xb6\x1c\xf5)\xb6\xfa\xe9>R\xd3qWq\x85g\xf5\xc6\x9aR\xa8\xe0\xdb/\xff\x9fp\xdd/z1\xa1\x9b\xb6\xd97\xc7\xfd\x80\xe8\xe1\xe7M\xe0\x03\xa4\x98\x7f\x16cP\xffA|S\xae\x98\xff\xbda\xc6\xac\x0b\xd6\xb4u\x15|l\xf5J\x9c$\xdbg\xf2\x0eG\xb2s\xf5 \xa2S\x9a\xe3\xdd\xb9\xb9\xd2|o0\x80`\xad\xbe\x92\xbe\xfc\xea\x0d|\xe5zj\x87n8V\xad\xfc\xea(T\x9el\xdf\xfbb-\xca\xfc\x7fU\x13\xfeG\xf0\x04\xd1\xbe\xd1\xf1\xa9\x8d<\xbf\xd1\x1f\\\xc3\xbe\xa6zC\xd9\xc2=[\xad^\x7f\xae\xea{\x15\xf8\xbd-Z(t,6\xf1\xe1\x1av\xf9\xa3\xd1\xfe\x0b\xea9\xe8\x03K\xba:\xa2\x03{>\xae\n\xd5\xa5\xdd\x17\xfbE>\x8c\xa6\x9f\xdf\xd6\xab\x85\xea\xe4:\x8a,\x1f\xe5Q`\xd57\xb2\xe9G\xc6}\x1dY\x85\xe3\xee\xe5\xfcJ\x8ck\xc6\x85;KCf\xc5\xf4\xdf\xff\xf7\xbf\x7f\x1dx\x90r\xf4\xb9\xe1\x05\xc3\xddN\xbaJ\x14\xf9\xcd\xf1\x9f\xbe\xf9S\xfbU\xa0\x0b\xa9\xff\x0f\xc1\x84C4\xc9*j\xf019\xf3eE\xfcR\xfd\xb6MrbL\xf1\xb6\xb7 \x8e\x1c\xb6`\xe0\xc7mU>(%\x85\x10\x8c\xd4\xdb\xc0\x89}\xc11'J\x00\xa8X\xcd\"\xa9\x18\xbd%91\xdd%\xa3\xfa\x0c\\\xa2\x7f30>\xa249+\x1exFj\x86\xf8\xd3x\x8c)\xb0_\x0cq_\xdc#}U`\xb8Mm\xe4\xc5\xaa\xcc\x1a^_[\xda\x04*oA>\xc82t\x82(\xc9vc\xd8}im\xb4\xe7\xa0g\x15o\x1e\xad\xc4\x97\xc1\xadC\x8c\xc0\xa0H\x85\x86\xad\xd8]QqX3^,\n^\x84\xea;\xa8\xad~\xaft;g\x0f*\xa0\x7f\xf4\x15\x86m\xf6\xc7\xe1\x93j\xad\xfa\xaf\xcaVm\xba-\xb7\xdf.\xe72d\xe1+\xa6\x9b4\xbd\xb4\xa7\xfe\xd5R&\x92\x05r\xc1n\x9az=\xb8\x82\x99\x87\xf4\xddC\xae\x14\xa0\xaa\xd0\xbf\xbb#\xa9^\x91\x17v\xfce\xfdt\xdb\x82\x0f\xbe3D\xbd=i%\xc6P\x97\x06\xf4\xe5\x81\xe6\x07#\xa3\xf9\x01\xcd\x0f\x8c\xd1\xfc`l4?8\xec\xfc\x00\xd5\xf3Q\xf7\x18\xdf\xee\x9d6\xfb\xf3\\\xfd4\xbc\xcbuV*,\x94\x81T\x15\xb0(\xd2\x94\xe4\xd7@\x81vZ\xacN\x81\x0d]?\x90\x1d\x8b\xf3\xa4\xdd\xf8\x80\xfb\x86\xd31\xe5\x1fgy\xdag\xad\x99\xca\xb8+\xdf\xe5&\xed\xed5\xb4\xbf\xbc\x9e\xa2\xe4]i\xe1$Mc\x94\xbc\x0b\x7f\xe4\xe4\xdd\x9d\xe6\xc8@\xce >2-kw\x18\xab\xf1J\x90\xf57\x92\xb2ww\x1cD\xd9\xbb\x98\x17\x1eP\xf6.e\xefz\x8f\xa4\xec]i\x94\xbd\xbbk\x94\xbdK\xd9\xbb>\xa3\xec]\xca\xde\x95F\xd9\xbb\x94\xbdK\xd9\xbb\x94\xbd\xab\x8c\xb2w){\x97\xb2w){\xd7g\x94\xbdK\xd9\xbb\x94\xbd\xeb~@({w\xc7\xb0\x99\x94\x94\xbd+\x8d\xb2w){\xf7yf\xef\xb6\xcd|6\xdc\x1a\xc0W\xef\xdd#\x07u\xefY\x14\xab\xee\x1d\xee\xc1$\xc58nAj&\x97\xa7\x0d\x8b\x96#\xdb\xb0{dR\x1b\x86\x02\xe8\x19[@9\xd4\x18oQ\x0e\xb5\xb2\x03;7\x9e\xfdK9\xd49\xbcH9\xd4\x94CM9\xd4'\xdb\xea\xba\x96\xcd\x99Y\xdcM\xe26c\x9fL\x19\xbe\xfd\xc6\xba\x8b\xec\xec<\xd6\x15&\x93\xaf{W\xee\xcc\xc5L\x87roA\xe6\xaa\x81>\xe1\xd9\xe6g\xf7\x9e\x7ff\xc9\xd9\xc3.\x12H\x99ED\xa10\x81\x1cp]3\x9a%\x1d\x8c\x19w\xc5yck\xb8\xdd\xc02\xb60}\x07\xb0`\x0b\x7f?\xf9CI\xc9;\x88\x1b\xa2\x0c\x9f\xb4\x83\xbd\x81\xca\xc6\xc9:\xa5;M\xa7\x7f\xba#\xe5\xe1\xf2s\x92rs\x92}\x84\xcb\xc9I\xf4\xd3(\x17\xa7t\xa4\xe1\xa0\x9d\x84\xcb\xbfI\xca\xbdA;)\xad\xd9\xa1|\x1b\xbdO\x9d>$\xc0=)\x13\xaf\x99\xc5v\xa5\xb0\x1a\xbd\x9f\x1e\x88\x0f\"\x943\xbe\xa4\x13\x02\x8d\x1f4%\xc3]\xc7W\xcc1E\x18\xa5\xc7T\xd6<%\x9a\x1c\x93\x96\x183\xa8\xe68i\xd65;\n%\xcfb\xda\xech-\xb4\xbcn\xf4\x84L&\xcd\x8a\x8f\xe1\x15\xb3\x13c\x9dE\xf5\xd5\x0bd\xca\xca\x8d\xd6M\x89\xd66\xf7\x95p\xabx\xde_\xcb\xd92[\xc8\xa4]\xca\xbcp\x1e@\x99\x17\xfdOXgR\xe6\x85\xfc\xfc\x1b?\xef\xf8\xfd\xd3F\xa59\x08_\x95\x98\x11\xbc\x02\xed\xabf\x8c23\xf2|.Qf\x06ef\xb8\x8d23\xa4Qf\xc6\xaeQf\x06ef\xf8\x8c23(3C\x1aefPf\x06efPf\x862\xca\xcc\xa0\xcc\x0c\xca\xcc\xa0\xcc\x0c\x9fQf\x06efPf\x06efX\x96\x83\x92\xa7\xcc\x0ci\x94\x99A\x99\x19\xcf33\x83r\x02\xd2\x80k\xca 8\xa0s\xe34;\xe5\x04\xe4\xf0\"\xe5\x04PN\x00\xe5\x04\x9ct\xfcV(\x11\xe0?\x1c\x89\x00?u'\x0e\xf0\xff\xbe<(\xab\x9bZ\xf6P\xb5\xd9Zw\xe5\xae\xac\x10\xee\x7f\xa9\xda\xe0\xb8\x9c>\xfa\xd9\xb2\xfe\xbd\x07\x9e\x07\xc8av\x8ezB\xfe}|\xc9\xc1\x04o\x08\xbd\xf7\xee\xf2 \x89vy\xff]\x02\xf4`\xf0\xf9\xb2\x92\x13y\xf7\x07\xeb\\\xdc\xde\xaa\xdd\xb6\xb3\xcd\xf6\xda\x0b\xf2E\xbd\x0b\x08\x0f\x03\x02\x97\x01\x9c\x87!\xc1\xcb0\x01\x9d \x16\xe6\x8e\x9f\x04\x96\x13s#4\x10\xc7h`\x02J\x13n@\xc1o\xd18\x0d\xe4Bj`\"V\x13,Pj\xabc\xd1\x1a\xd8\x1f\xaf\x81d\xc4&X\x94\x0e\xfd'a6\x90\x1b\xb5\x81D\xdc\x06R\x91\x9bp\xcf\xeep\x1c,v\x03\xb9\xd1\x1b\xc0\xe17\x90\x13\xc1\x81\xbd1\x1c\x98\x86\xe2@.\x1c\x07&!9\xe1\xc7\xa1\x10\x9f\xbcQ,\x07\x0e\x83\xe6\xc0\x01\xf1\x1c8\x0c\xa2\x03\x89\x98\x0eLCubC0\x0e\xd7\x81\xbc\xc8\x0e$`;\x90\x8e\xee\xc0\x04|\x071d~\x8d@x \x07\xc6\x031\x94\x07\xf0\xd33\x04\xd2\x03\x89\xb3\xb8d\xb4'X\x9a\xc4~\x10x\x0f$\xd42#\xe6\x03I\xa8\x0f\xe4\xc6}`\"\xf2\x13\xeeWm\x1c\xfb\x81\xe9\xe8\x8f\xb7\x9c PH\x13L\xc0\x9a \x0dm\x82)x\x13$#N\x10y\xdbF\xb0\x13H@O\xb0\xb8\x13LA\x9e \x15{\x82p\xc3\xa7\xe0O\xde\xc2,\xb8\x08\xfb\xc8\xe00\xa8\xe0\x03Q-\xc3(\x14\xe4\xc5\xa1 \x86DA\x18\x8b\xf2\x9e3\x15\x97\x82\x8c}7\x01\x9b\x82$t\n,|jh\x7f/\xca\x15[\x84\xa3S\xe3h\xf8\xd0\xb0\xdf\xf0\xeaJ`\xf4\x07\xeeo\x99^\xc9\xb1U\x05\xb1\x16\xc4\xaa\xed\xda?C\x7f\x0d\xdf}x\xffnvy\xf5\xf6\xea\xd3\xe5\xec\xd3\xfb\xcb\x8b\xb3\xd3\xf3\xef\xcf\xcf\xde\xa1\xcf\x10\xffJ<\xfc\xfc\xfd_\x90\xc7\x07\x0b7b\x0eIMP\xa2?O\x10\\\xd6\xeaB\xaa\x1b\x83\x85\x0c\xcauv\xf9\xdb\xab\xb2\x9a\xaf\xfc/\xfe\x96\xadn^\xf7\xfa;\x9eN\xd0\x93\x12j_\xe2'h\xda\xf8\x92}\xdc\\\x12;\xfa\x8fe\xdbnUL\xc1\xff*\xb2\xa2\xea}\xa9\x9e\xc5Q\xbbv{7\xa0;n\x04u\xf6\x7f\xe7\xacY\xb7\xe0\xde\x17\xb6\xb7\x88\xe6]\xb6\xb8\xfd\xba\xae\xca\xcf\xcc\xa1\x15\xd2\x1b\xe2\x06C\x82\x8f\xa0\xbf\xaa\xb5]\xfb\xedv]T\xaf\x1bV,$\xd8%gt1\x1f\x01\xc6O\xa0 \x00\xee% \xe00m4\x97\xb5E\xb7L|\xb3\xff\xb1-\x97U\xc1\xb7\x0d\x83W\xec!\xfc\x91\xfc\xe9\xa2n$\xa5\xf9?\xd9\xe3u\xd12\xef\xe0\x0dp\xcf\xae\xdb\x92\xe7X\xed\x1f\xb4X\x17\xebl\x91\xf9mUV\x9fCC\xcf|\xdb\x94\xfcq&\xbfU\xe6A\x05\xc4)5\x8c\xdc\x93\xf1\xe5\x9d-a\xeb\xa2\\E\x83\xe4\xa6(\xd0E\xf9\xdb\xec\x95\xb5\xe9mJSu\xb1\xe6E\xa0\xc2R]#\xf4\xaf\xeeZ\xf5r\xaca\x1dJD\xb50\xda\x93\xd8;4\xae\x96\xb9;GP\xde\xf4?\x1e\xc9\x11U\x1f\x11\x80i\x8c>e\xd9\xeeL\xef\x96[K\xff.\xe6\xa2\x90\x08e\x82\x83b\xc2\x93\xe9N\x92\xd2\x92\x01\x17\xad\xcb\xaa\x93\x9f\xf4\x167\x9cH\xf2\xda(\x11\xb2\x98\x83\xe6\xf5z]\xb6-\xf6\xa5\xd9\x1f>x1Z\x7f\xee\xb3*\x0e\xfc\xca\xeb\xaf9k\n\x1e\x16l\xc5\xde\x15p\x94;h\xa8V\xc2\xb4\x0e\n\x96\xa5\nPHA\x17\x06Wz\xac\xf2{\x17\xf1\xdaC\xf9\x0b\x90>\x03]\xa7\xd8\x92<\xe2\x810\x96\xe2Z\xd0\x977\x9f>V\xb7\x91\x7f\x9e\xdf\x16\xcdR\xce \xa3\xc5\xf4\xb3\xc1#\x90\xc9\\7\x12\xdf\n+\x8c\x02\xac\x8b\x87\xd9\x17v\x80\xa9\xc2\xa0_\xad\x8b\x87r\xbd]\x8f=\x12-L\x8d\x8e\xfd\x93?/*`w\xac\xd1\xaeL\xf6\x8d\xe2M\x9e\x83\x8b\xac\x9a8=\xb5(\xca\xa8\x1e\xad\xccNhX\xd1\xb2\x1dL\xd7\xf2\xb4rR\xb4\xa8\xb8\x13\xb7\x1b\xf1v\x88\xca\x1d#\x1d\x87}\xe3@\xa2\x83\xadZ\x9a\xe7P\x86\x18\xe4\x1f\\O\xe5}\x04\xb3Swj\xe1\xf6\xcb\xba\xacf\xe2C\xd5\xd2\xea\xdf\xe3-\x8cm\xa7\xe3\xaa;\xcb-/[\xf9\x05\x0d\x0b6_\x15\xe3\xa4\x90Qa\xb2\xc7\xe9\xa3M\x81A\x8d W\xfa\xc4\xbf:\x0e\xc74\xa8\xcb\x19\xb0\xbe\xb3\xbaV\x1c\x01\xaf\x97j\x05L\xc6\x1d\xb8I\xb7q\x16U\xac\xad\xb4\x1cw\xf5\x7f\xb2\xfc#f\x0e\xe6s]\xa7\xe7\x97\x0d\xb0\x07u\xc7U\xef\x90s\x8d\xb2\xf2\xbc\xed/WE{+\xdeu&=\xc5G\x13\x17\xe2>\xa8GU\x87\xb3\x06\x979R\x0c\xa2D\xe2\xea\xa6\xf1\xbd \xe7\xc5j\xbe]uQ\x9c\x9b\xad\xf8$s_p[\xd9\x1b;\x08\xdf\xd5[\x0e%\x97\xc9\x1b\xd5\x12\xea;\xf9a\xdb\xad9\xc0\xcf\xb7\xacRMu7\xa0\x19\xae\xe0\xb8\xaf:\x9c\xc7\x1e\x8d\xc6\xa4\xb2\x15\x93\x83E\xc9\x0d'VX\xfd\xcdY\xde\xfdm\xdd\xb2>\xb3\xca}Q\xfb6\x96\xed\x80\xc8\xb4z\x84lZ\xdf\x04gI\x8b\xb2\x0b\xd9\xaauw\x89\x15\xba\xaf:\xb8\x7f\xc7\xf0S-\xdd\xba\xa9\xefYc\xb2!\xcd\xedb\x0b\x19\x86\xf5v[\xb3@+\x9b\xe0\xbe\xdaz\xbb\xe2\xe5fU\xaa\xca\x0d\xaf\xbds\xc2\xe0\xa9\xb3R}\x9c[{\xb5/U\n\x90\x14\xa0\xf6\xee\xd2@z\xd0\xd2\x06M%=h\x943\xffpz\xd0;\xfdB\xa6\xfe9r\xe4\x82*\xd0\x8e5E%\xfb\xec\xca\xed#\xb1gm$\xf6\x9cg\x86\x99\x9a\xb1\xa6\xba-\x89=#\xb2\xd3x\x8e\xcc\xb4)Yi$\xf6\x9c1\x03-%\xfb,)\xf3\x8c\xc4\x9e\xf7\xcd2\x9b\x90a\x96%\xbb,=\xb3\x8c\xc4\x9e\xf7\xc9$K\xc9\"\x9b\x90AFb\xcf$\xf6\x1c\x99%%g\x84\x91\xd83*\xfbkJ\xe6\x17\x89=\xfb\x0e\x8bfx%dwa\xa4\x8cS\xb2\xbaH\xec\x99\xc4\x9e1\x19Z$\xf6,m\x9f,,\x12{v\x95\x14\xcd\xb4\x9a\x9ae\xe5}7\x90\xd8\xf3\xae\x91\xd8\xf3\x84\xec\xa8xfTjVTBFTr6TZ&\x14\x89=\xa7e;\x91\xd8sg$\xf6\xac\x8d\xc4\x9eI\xec\x99\xc4\x9e\xfb\xbfMvn\\\xa6\x98\xc4\x9esx\x91\xc4\x9eI\xec\x99\xc4\x9e-\xb1\xe7\x93_\xbb\xffV\xbfMU\x7f\xee\xc4\x9f-\x18\xcf\xa3\xfb\xdc\x1f\xd2\x95\xb7)\xca\xee-?\x90\x7f\xde\xbd\x90>\xea\xf9\xcb>\xfb\x88\x8eI\xd8\x19N\xbc9\x1a\x9b\xc0,\xee\xe7\x96mN\x17m\xc6I6\x07\xbd Q\x8f\x02\x02wAx\x14\xd0^\x85\xa9\xd0K\xa0\xbc$\x91\xe6\xac\xe0K\x14}\xc9\x0d\xbf\xe0\xf1\x97L\x00\xcc4\x04&P\\\xa2,\xf3\x9e\x18Ln\x10&\x11\x85\xc9\x0c\xc3\xa4\xe10\x89@L\xa8\x0fw\xa8\x0c\x16\x89\xc9\x0c\xc5\xa0\xb0\x98\x8c`\xcc\xbeh\xcc$8&\x13\x1e3\x05\x90 \x14\x86\x16^>\x00$s8L\xe6 \xa0L\x1a*\x93\x1d\x96\xc1\xe22Y\x81\x19<2\x93\x0c\xcd\xa4c3\xd1\xa1\x10'\xb1\xbc7:\x13\x95WFM\xa8\x10\x00M\xca\xac+\x19\xa2 \xbd\x04\xd1\xa2\xca\xb8\xfaeDiR`\x9a\xcc8\xcd4\xa0&\xd4\x83PB\xca\x13\xa1\x1aOi\x1c%\xa2\x9c\x07\xacA\xd3!\x08\xb8& \xaf\x89i\x90NAlbezCm\x99@\x9btg\xe2a\x9bX\xdb&\x007\x13\x91\x9bP\xc82\x1bv\x83\x06op\xe8\x0d\x16\xbeAx9\x1d\xc0IAp\xc2\xb2\xc8Y0\x9cD\x10g?\x14'\xe6\xd0\x04\x1c\xe7\x00@N\xb4v\xde\x9e\x9e\x0f\xcbA\x809\xd3\xd1\x1cOq<*\x7f\x9c\x15\xcf\x89\x01:\x13\x11\x1dOYq\xd9c\x04\xa6\x13\x96<\x0e \x1e\xe7\x86u\xb2\xe3:~`''\xb2\x83\x81v\xd2\xb1\x9d$pg\x02\xba\x93\n\xefDD\x8c\xc3\xb5\xc3\xe2\x14X\x84g\x02\xc4\x93\x88\xf1\x04\x9a;\x05\xe5\xf1\x14\x85\x90-\x9e\x82\xf3\x04\xba|\\\xb28#\xd2\x13\x95+>\x04\xd6\x93\xab/&\xa0=)p\x8f[\x8c8$E\xcc\x9d\x91Z\xdbp\xdf\xbdSD\x88\xa5\xd8\xb0\xa7<\xac\x04qH\x80\x18W\xf3I\xe2\xc3\xc9\xd2\xc3\xd1u\x92\x90\xecp\xaa\xe8p\x92\xe4p\x9a\xe00Znx\x82\xd8pHj8\xea?\xdc\xcd\xdeWd\x18#1\x8c\x13\x18\xce\xd4\xa0\\\xd2\xc2xaa\xbb^{U\xbc;j/I\xe1\x88b \xdf?\xaa\x1c\x95\x12\x8e\xdeJ@{\x052\x8a\x08G\xb5\x14\xe3\x02\xc2\x99[\x96Y:\x18-\x1c\x1c\x95\x0dNo\xe7\xbe\x92\xc1x\xc1\xe0\xf4\xba\x05\xefA6\xa9`\xacPpT&8\xbd\x81\x93%\x82q\x02\xc1\xd1\n\xc5\xc5\x81q\xf7#\xa70\xf0>\xb2\xc0\x18Q`\xb4S\xc2\xf2\x8c\xa9\x8eI\x11\x03\xc6\xbc%\x00-\x05\x1c\x13\x02\x1e\xb4d\x7f\x19\xe0\x0c/*\xbc\x000\xee.@\xba\xf8\xaf\x12\xf8\x0d\x94\xb7\x97\xf4o\xd4G\x80\xf2\x13\xa0D\x7f\xa3\x1d\xde\x18\xde\x99\x80\x96\xfb\xb5\xe5|#\x05\xa2\x05mqR\xbf\x07jv\x8a\xc8\xaf\x1a\xcb\"\x05\xe6\x90\xf8M\x12\xf8=\xa0cP\xd2\xbe\x9dto\xa4\xc0\xa8\xb0o\xd45(\xd9^\x94;p\xef\x04HrZf\xb9\xde\x80X/Z\xaa7\xea\x0b\\\xeb2\x8a\xf4&K\xf4\"\x05z\xe3\x0d\x99&\xce\xabeW\x1d\xe5\xf9\xa5ys\n\xf3\"ey\x93Eym\x01^w\xe3\xfc\x92\xbcy\x05y1r\xbcy\xc5x\x11R\xbc\x93\x84x\x8d\xe8\xae\xab\xbc\xa8\x0c\xef4\x11^\xbd\x1c\xe9(\xcf/\xc1\x8b\x16\xe0\x1d\xb4\x02\xe7\xef\x94N\xb3\xaf\xf0\xbc2\x8c\xfc\xbc2\x9c\x08\xbd\xb2\x0347\x97,\xbd2\xbc8\xbd2\xbb\xa6\xd9\x1a\xd4\x1d\xbb\x97\\\xbd2\x84\xd6mv~$*c\xaf\x0c\xd9\x19 \xd1\x7f\x90Q\xd8^\x19\xca\x87\x18\x91{e\x07lw\xa2\xec}\xb4\xbcW\xec\xe1\x18/~\xaf,*\x81\xafl\xaa\x17\xf6\x95\xc3W\x86\x17\xc5W6\xb5\xb6\x88{\x96M&\x7fX\\L,_YT2_\xd9\xd4\xe6O\x96\xcfW\x86\x13\xd1W\x86\xac\"\x96aO\xb9\x8b\xe9\xe2\xfa\xc1\xe2\n\xbe\x97\xc4\xbe2\x8c\xd0\xbe\xb2D\xc7\xc5\x92A`\xb2\xf3\xb0\x02\xfc\xc1\xe2\xf4\x98>E\x86_YL\x8c_\xd9\xa0\x85\xfbK\xf2+\xcb\xfc\x1a\xc6\x8b\xf4+K\xb9k\x90.\xd8\x1f-O\x15\xb2\x97l\xbf2\xa4\x1f!\xc1\x97\x80\x12\xf2W\x86|\xa0\x8c\xa5\xba\x1d\xd0\xd2\xfe\xa8\xa2,\xf9\x7f\x9cf{o8\x99\x7feO\xe0\x94\x14\xe1\x7fT\x81j\x14\x8e\xc8\xff\xa3JJ\xf3)z\xa3\x00eO\xe4Z\xd4\xd6\x01\xa8\xd2\xcc\xf6\x02\xf1\x0d\x04P\xc5\xe1\x9c\x8b\xdaj@Y\x82CS\xde\x8a0\xc1\xf9\xa9[\x10D\x0b\xbc/\xda\xd0F\x04\xca\xd0\xdb\x11(C\xfa+\xa5\xed\x197(\xe8\nL\xdb\xa6@\x19r\xb3\x02e\xd8\x06N\xdb\xb8\xc0[\\/_\x1f\xa4\xb3\xd371\xf0\x16\xa567\xc0ne\xa0,yC\x03\xff\xd5\xfb\x8d\x0e\xc2\xdb\x1a(\x9b\xb4\xb9A\xb8\xe9\xa8-\x0e\x94\xa5nt\xe0-\xc8z\x10\x10\xdb\x1d(\x9b\xb4\xe9\x81\xbf\x06e\x87\x0f\xf0\xf0\xd6\x07\xca\xa6m\x80\x10l\x0c[\x04\xb6AP6m3\x84\x9d\xa7U\x8a\xa4\x0cUH\xba\x8d\x10\xca6a/\x84\x912\xca\xc7\x8b\xd3q\xddi[\x84\xe1w9m\x8b\xe00\xec\xbb%5qNu`\xda\x16\x01\x91\x10\xe7\xe2\x88\x92\x13\xe1\xf0 p\xb4-\x82\xb1\xd4\xc4\xb6\xe0:iJB[R\"\x1bm\x8b\xb0o\x82\xda\x84\xc4\xb4, i\xf8D4\xda\x16!G\xa2YJ\x82\x19:\xb1\x8c\xb6E\xa0m\x11\xd0\xb3\xa4\xa4\xc4/\xf5\xb9D\xdb\"\xd0\xb6\x08\xb9\x92\xb1\x90y@\xd1\xe4\xab\x84\xa4+\x8c\xe8\x7fJ\x92\x15m\x8b@\xdb\"`\x92\x9fh[\x04i\xfb$1\xd1\xb6\x08\xae\x92\xa2\xc9HS\x92\x90h[\x04\xdb\x10IE\xb4-\x02m\x8b00\xda\x16!-Q\x87\xb6E\xe8\x8c\xb6E\xd0f\xb6\x18\xd8a\x05\x07\x1f\x91C\xd6\xb0\xa7\x06\xf5\xdf\xbb\xcd\x04\xf8\xae\xd8\xbc=,\xed\xb9\xb9\x00x\x98J\xdf\xa6\x03\xbe=\x07\xa4\x1f\xf4\x19\xae\x0d\x06.:?\x0d6\x17\xd0\xc58\x00\xbb\xc1v\x02\x97\xea0Y\x88\xf9\xd2|\xb6\xbb \xd8\xae\xb0mP\x19u\x90~\x1a\xe5j\xba\x9cYuN\x92o\xc1\xb2\x85u\xbd\xd8\xae\x1c+\x12\xdezA4\x9e\x85AK\xa3\xcb9\x83\xc6\x8c\x18P\x0dz(\x1eT?v\xf2K?Do\xae\x8b\x07K\xf87T\xab\x90\xdcu\x1c\x0f\x1eT|xQSqCDu\x11zKU\xd9WuV\xf1&\x18?\xccZo\xef\xd2\x98U\x15\xab5`\xfe$\xa7\x9a\xa5\\s\xecn\x86\xa7$\x8bW\x90\xf1i\xeb\xdf\xaf6\xac\x81MQ6'\xbc)kg:\x81\xb5\x05\xc93\xf1\xccn\x8d\x8c\x83\xfa\xdbl\x0d\xb3\xe6\x18/E\xb3aM[\xb6N<_8v\xb6`U\xed\xc9\xb1K{\xb8\xfa\xd2\x06\xd8\x9e\xf8\xb3\\-\x9f\xd7e\x05\xf2\xf7\xb2\xd2L\xd4n\x89\xeb\xb2\x9a\x8dx\xdf\x89u\xd3j\xcb\xfe\x1e\xb8{\xa5\x8et\xbd-\xca\xea\xf5}\xb9`\x1d\xce\x15\xc5\x8b\x15\xca$\xa6G\xc5\x88\xe4T\x10'(\xf4\xa9\xe7\x7f\x06\xe5 \x18\x11\xf5N\x89\xb2!\x9a\x06QGK\ndX\x16A \x04\x81D# \xb8\x91\x8a \x10\x82@|G\x12\x04\"\x8d \x90]#\x08\x84 \x10\x9f\x11\x04B\x10\x884\x82@\x08\x02!\x08\x84 \x10e\x04\x81\x10\x04B\x10\x08A >#\x08\x84 \x10\x82@\x08\x02\xb1,G@\x9e \x10i\x04\x81\xfc^ \x90dX\xa2\xaeW\x01T\xa2\xaeW\x03HB\x1c>\xe0>\x06l\x848\\\xff\xfd\xf9\"\x11]\x83m\x1b\x02\x11\xa2\x91v\x80s\xa7\xd5\xbdyk\x00\xd18NU\xf3\x99\xcaD\x9f\x85\xd4=#+\x19\xfb\x940h\xb5\nA\xd6\xf5\n\x1d\x80\x14^\xf9xqJ\x01G\xa0\x80ct\xb5\x0d\xb3`\x05\x14p\xa4\x80\xa3\xf7H\n8J\xa3\x80\xe3\xaeQ\xc0\x91\x02\x8e>\xa3\x80#\x05\x1c\xa5Q\xc0\x91\x02\x8e\x14p\xa4\x80\xa32\n8R\xc0\x91\x02\x8e\x14p\xf4\x19\x05\x1c)\xe0H\x01G\n8Z\x96#\xf8C\x01Gi\x14p\xfc\xa3\x06\x1c\xc7\x89\xb5\xae\xb0\xe3O}\x1e\xac >\x16\xab\x95\x95\xfajV\x16\xf9\\\xa9\x9a/\xcb;V\xe9\xbd\x0c\x9d\x91\xc9\xbeD\xfd\xeb\xb3\x8dO\x86\x12\x8f\xf9\x17\x08\xfd(7\xd6\xcd\xacX,\x1a\xd6z\x8eB\xad\x7f`\x96\x10\xc0q\xc9A,\xd6\xfcm\xbc\xe1\xc3K\xff\x12\x96)\xef\xbf\xc35\x9b\xdf\x02\xab\xe6\xf5B\xaeR\xcaG\xdf\xfd\x8a\x9b\x8b\xdb[\xb5\xdbv\xb6\xd9^\x7ff\xde\x9d\xd8\"\xde\x05\x84\x87\x01\x11`\x03\x9c\x87!\xc1\xcb0!\xd8\x16,\xcc\xbd\xe2\x12\xf8\x00\xc9\x1dt\x83x\xe0\x0d&\x04\xdf\xc2\x0d(\xf8-:\x00\x07\xb9\x82p01\x10\x17,P8\x17\x1d\x8c\x83\xfd\x03r\x90\x1c\x94\x0b\x16\xa5\x83\x05I\x819\xc8\x1d\x9c\x83\xc4\x00\x1d\xa4\x06\xe9\xc2=\xbb\x0b\xe0a\x03u\x90;X\x07\xb8\x80\x1d\xe4\x0c\xda\xc1\xde\x81;\x98\x16\xbc\x83\\\x01<\x98\x14\xc4\x0b?\x0e\xd6\x0e\x1d\x91&\x1c \x98\x07\x07\x0c\xe8\xc1a\x82z\x90\x18\xd8\x83i\xc1\xbd\xd8\x10\x8c\x0b\xf0A\xde \x1f$\x04\xfa =\xd8\x07\x13\x02~\x88!\xf3kD\xd0\x0fr\x04\xfe \x16\xfc\x03\xfc\xf4\x0c\x11\x04\x84\xc4Y\\r00X\x9a\x0c\x14\"\x02\x82\x90P\xcb\x8c\x81AH\n\x0eB\xee\x00!L\x0c\x12\x86\xfbU\x1b\x0f\x14\xc2\xf4`\xa1\xb7z[\x17\x0f\x81\x9d>z;\xa0\x03L\x15\x06\xfd\xca\xec\xaf3\xf2H\xb405:\x0ew\x01aw\xac\xd1\xaeL\xf6\x8d\xe2M\x9e\x83\x8b\xac\x9a8=\xb5(\xca\x80>\x84\xb1\xb2\x12O[\xcbv0]\xcb\xd3\xcaI\xd1\xa2\xe2N\xdcn\xc4\xdb!\xf8\xa6\x01\xbc\xe3\xb0o\x1cHt\xb0UK\xf3\x1c\xca\x10\x83\xfc\x83\xeb\xa9\xbc\x8f`v\xeaN-\xdc~Y\x97\xd5L|\xa8\xce\xfa\x0f\xd5=\xde\xc2\xd8v:\xae\xba\xb3\xdc\xf2\xb2\x95_\xd0\xb0`\xf3U\xd1\x04\xd6\x88\xcc\xc6<\xfahS`PU\xa0\xac\xe6L\xbc-\xdbu\xdd\xben\x17\x9f\xe1\x9f\x8e\xff\xf9_\x1d\x87c\x1a\xd4\xe5\x0cX\xdfY]+\x8e\x80\xd7K\xb5\x02&\xe3\x0e2f >\xb3\x9dE\x15\xebz\xdbQ\xc5\xee\xea\xffd\xf9G\xcc\x1c\xcc\xe7\xbaN\xe8+\x1b`\x0f\xea\x8e\xeb\x8d\x88j\xb9\xdd\x92\xe7m\x7f\xb9*\xda[\xf1\xae3z\x1f>\x9a\xb8\x10\xf7A=\xaa:\x9c5\xb8\xcc\x91b\x10%\x12W7\x8d\xefM8/V\xf3\xed\xaa\x8b\xe2\xdcl\xc5'\x99\xfb\x82\xdb\xaa\xbf\x95\xad\xf4]\xbd\xe5Pr\x99\xacQ-\xa1\xbec\xf6\x1eJ\xc7\xf0\xf3-\xabTS\xdd\x0dh\x86+8\xee\xab\x0e\xe7\xb1G\xa31\xa9l\xc5\xe4`Qr\xc3\x89\x15V\x7fs\x96w\x7f[\xb7\xd6v]\xee\x8b\xda\xb7\xb1l\x07D\xa6\xd5#d\xd3\xfa&8KZ\x94]\xc8V\xad\xbbK\xac\xd0}\xd5\xc1\xfd;\x86\x9fj\xe9\xd6M}\xcf\xd4k\xea\x9au\xb7\x8b-d\x18\xd6\xdbm\xcd\x02\xadl\x82\xfbj\xeb\xed\x8a\x97\x9bU\xa9*7\xbc\xf6\xce \x83\xa7\xce\xca\xe6\xe9\xc10\xbd\xfb\xa0\xca\xfaY\x80\x7f\xb3\xbbM\xb1\xd4{\x8c\xed\x0ek\x83\xcb\xf4\x07\x0e%\xfd\xfa?\xeb~\xef\xca\xe9Q\x16\x9c\x08F4\xfe\xd8\x03\x9fy\xd3G\xa2#o\x14\x16\x1c4\xf5?|\x03\xb3\xa9\x85\x19\x8b\xc5\x7fj\xde\xb6h[\xb5\x18xQ,\xd9G\xf6\x8f-k\xf9\xb1\xfa\xddSX\xbf\x15\xa8(V8\x92\xc1\xban90I\xadJ\xdc\xf5\x18\xce\xb9\x15\x86\xda\xf0G(}\x18\x9a\x18C\x99|\x8c\xab\x1a\xd6u\xc3\xcc\x98\xe5\x1a\xd9d_\xdd\xd3\x99[\xff\xc7vlG9\xb5\x90Z\x9a\x15\xd5~\xbb>3\xccZ\x1a>\xbe\xf6\xda\x8e\x9e\x8bA`\xa6\x1e@\xcf\xe1\xf7\x85x_\xf2#(yk`\xf3V\x8e\xa2*\x0e#\x17.\xee\xcbv\xd8?|\x0d\x91\xd9y}2\x1cV\xeaq'}\xceX/\xff\xd8\xfd@\xea\x8f\xa3_I\xfd\x117\xdf\x82 i\xaa\xb3\x92\xfa#\"\xf9\x8c\xe7H<\x9b\x92tF\xea\x8f\x19\x13\xccR\x92\xcb\x92\x12\xcbH\xfdq\xdf$\xb2 dY\x92\xc7\xd2\x13\xc7H\xfdq\x9fD\xb1\x94$\xb1 b\xa4\xfeH\xea\x8f\xa4\xfe\x88M\xf0\xca\x9a\xdc5%\xb1\x8b\xd4\x1f}\x87E\x13\xb8\x12\x92\xb70\xda\x86)I[\xa4\xfeH\xea\x8f\x98\x04,R\x7f\x94\xb6O\x92\x15\xa9?\xbaJ\x8a&RMM\xa2\xf2\xbe\x1bH\xfdq\xd7H\xfdqB\xf2S<\xf1)5\xe9)!\xe1)9\xd9)-\xd1\x89\xd4\x1f\xd3\x92\x99H\xfd\xb1\xb3?\xa4\xfacO\x1d\xf6\x93\x9e\xd7r\xe4}\xb3\x9bV3\xf8\x88\xd4\xc98\xac*\xaeWj\xe1E\x05\x16\x85\xdb\xac\xd0\xb0\xd4xT\xd0\x9eK\xe4\x11\xe4\x1b\xe5\x8d:\xd7\xfa[\xc3\xfe\xb1-\x1b\xb6x\x037\xc5j\x10\x1bs~\xa9\x9b*\xf7Q\xe1\xe3\xcf\xec\xd1W\xf5Q\x9cU\x07V\x0b=\xea7\x8co\x9bJ\xe9\x0b\xaaX\x9f\x8emuQX\xb9z\xb5\x1c-\xf3\xc8\x16\x88\x86\xc6\"\xab\x1f\xc4;\xba\xae\xe4\xe7m}s\xd32 \xb5\x0f\xab\x0b\xd6\xea{\xcbxfoy\xd62\x1cNT\xf5\xf3\xf9q\xb4\x8e\xa0\x1b#]Ym\xd7\xac)\xe7\xe6or\x80\xd0\xf0\x82Z\xc8\xb9e\x95q\xfc\xb6\xea\xd6\xceF3\xe6sY\xda\x8a\xb5m\xefB\xb5\xda\xb4m\x85\xab?\xb3D\x7f\x0e\x8b?\xb0sGqj\x87{W\xe5\xba\xc4zW\x1ek\x00\x00_\xf8Z\xad\xab\xda=X\x93\x11\xdb\xd5(\xde\xaaVQ\xec?\x9d\xdf\xc0\x8a\xddp\x13\xee\xd7\xf1\x7f3\xcf\x95K\xc2\xea\x01Q\x17\x11~\xbe~\x04V\xcco\xa1\xd8l\xbe\xa0\x17\xed |\x7f~\xc8\x97\xd6\x19\xc2\xa3\xb2\x87\xd6\xc0\x9b-\x03\xf1\x1fe\xb5(\xe7\x92\xd1\xd2\xc1!\xedAy\xa0\xeeHvqe5_m\x17\xa3Yl\xa1\xae\xd2E\xe7FwL\xc6z\xadEc1l\x0e\x98\x96Aa\x9f\xce\xdb\xd1\xdd\x1a5AN\xfc\x1b\xd6\xea\xa8\xbc|\xbc\xfa\xe7Q#^\x8fx=\xe2\xf5\x88\xd7\xb3,\x07;E\xbc\x9e4\xe2\xf5\x88\xd7{N\xbc\x1e\xed\xab=u\xd3b\xdaW\xfb\x80\xce\x8d\xef\x08M\xfbj\xe7\xf0\"\xed\xabM\xfbj\xd3\xbe\xda6h~\xf2\xeb\x90\xe2\x0dm\xbam\x81eh\xde\xbcg.aS\x94n\xfc\xfc\xddX\xb5\xeb\xb7\x04\x9d\xfb\xc0\x8dI`Y\x0c\x1d\x0f\x16\x0d\xd1\xe2!\x0d\x1aG\x84:p\xf1\x02sd*.\x1e NFa\xf1\x04T\xf4[\xea\xe3\x1c\xd3\x17\x15X\x13Q\x9b\xc5\xcf\"R\x9c\x80wLJcGW\x1f\xbc\x985\xec\xa6\x0f \x049A\x0f\xf1\x8b\xedJE\xd04\x8e\x07\x05G\xb9\xe0@M\x0f\xb4)\xb5\x8eX\x97:^\xaag\x15o\x1e{\x96\xac\xb2^\xe3\x91\x1d\xfb\xe5\"~\xc3V\xec\xae\xa88\xac\x19/\x16\x05/\x10\x88\xa3\x1e$\x15Un?\xc569\xa9\x0f\x9a\x02\x82\xb9&)-\xaf\xe5&\xd3\xab\x95\xfc\xac\x87\xb6\xac\x96+kr\xf7\xd2\xb5\xde\xdfWL\xfc\xbf\x93\x93\x14\x0f[WZ?\xd1\x11\x9f\x1f\x95|\x1c_K\x14\x9e-`U\xb6\xfc\x90d\x97\xeb\xf4\x13\xd7,jt \x11_\xda\x88\xf8\"\xe2\xab7\"\xbe\x88\xf8\xea\x8d\x88/N\xc4\x97\xdb\x88\xf82F\xc4\x17\x11_D|!gID|uF\xc4\x97mD|\x11\xf1\xe50\"\xbe\x9c\xc7\x10\xf1E\xc4\x97\xc7\x88\xf8\"\xe2\x8b\x88/\"\xbe,\xcbA\xdf\x10\xf1%\x8d\x88/\"\xbe\x88\xf8\xda\xaf\xceY\x89/\x17\xd4\x15\xda \xd9Z\x01\xe8v2s\x84\x0b[\x07\xdfe]@\xbe2\xbb\x82v\xd8\x18\xf7\xfe\xc9\xae\xcb\xe8\x13\x9e9\xdc%\xdc\xfb<\xf7Q\x8e\xa1T\xa8\xf5-\xcc\x12\x11\xe4\x06\xc5\"\xa8\x18\x1a\x16\xcb\xda\xc2\xac\xc8XT\x97*\x88\x8d\xc5\xc1\xb1\x08:\x16\xedb\xcab\x1dMY\x02@\x86\xba!\xca\xb0\x10\x19\xfe\x06*\xcb\x0b\x92\xe1P\xb2$\x98l\x82\x8f0@Y\xb2\x9f\xf2Ae8\xac, ,KpRZ\xb3\xb3\xe1e\xfb\x01f(\xc4\xec`N\xc0rh\x19\xee:\xbebYI\xb54V-+\xad\x86ks6b\x0d\xc5\xac\xedG\xad\xd1\xf6\xd3\xda\x06M\xa5\xed\xa7Q\xce\xfc#l?\x8d\xc0:\x83_BA\xd2sT\x9a\x83\x1d\x1amN\xed\xfc\xa4#\xe6S\x1b1\x9fy>\x97\x88\xf9$\xe6\xd3m\xc4|J#\xe6s\xd7\x88\xf9$\xe6\xd3g\xc4|\x12\xf3)\x8d\x98Ob>\x89\xf9$\xe6S\x191\x9f\xc4|\x12\xf3I\xcc\xa7\xcf\x88\xf9$\xe6\x93\x98Ob>-\xcb\xc1\xdf\x11\xf3)\x8d\x98Ob>\x9f'\xf3I\xdbT\xa7\xed\x01L\xdbT\x1f\xd0\xb9\xf1\x0d\x96i\x9b\xea\x1c^\xa4m\xaai\x9b\xea\xdf\xd76\xd5\x86\xfb\xe7\x0f\x1d\xf2\xdf\x96\xeb\xed\xaa\xe0z\x05{S\xb7\xbb$\xff\xa5>\x04\xcc\xb1-\xb0\x076\xdfr\xd1\xc4\x02xSTm!\x17)\xd5G[\xcb\xcbu!\x7f\\\x16\xa2\x8f\xc8!A\x959\xe0\xf5M\xb9/L\x13\x9f)\x9a\xbf,\xdaYY\xdd\xd4\x11\x1a\xcd\x1cf\xc6R\xf1\xdfb\xa8\x91\xfb\xa4^\xd7[\xae\xdd\xd1\x8f\x9f\xda\x9fN\xa4\xd1[O\x88\x92\x19\xa2\"\xf7E\xc5\x99C+\x160A \x04M\x85Y\xf8\x07\xf8K\xd1\xfe,+b|\xb2.\x1e\xca\xf5v\x0d\xdb\xaa\xe4r\xc5\xfa\xben>\xc3\xbd\x8eL\xaa\x80\x18\x7f\xf0\x03j\x1b\xd6\x88\xca\xb9>@E\xab\x85s\x9f\xa8\xcd\x7f)\xdaOm\xdf0\xbd\xafm}#or1\xe7\x8a!\x98\xd7\x95\x0e.\x0f\x8bR#J\xa4C\xe9\x97C\xd9\xda\xaf\n\x13\xbc8L\xd7Y\x14\xbc\xd8\xd3\x818\x9e\xd1\xdbe\xde\x15\xbc\x90S\xbd\xeaQ\xd6\xa6\x1fjo\x1a\xb9\xed\xaf\xfar\x92Q\xe6j\xb1\xf2\x04\x8a\xc0\x8cPu%\xdfO?~\xba\xbc\n\x84\x01W\xacZ\xf2[\xd84\xec\xa6|P\xcf\xa7\x1c\xba\xc5h\xdf2\xf11\xc3\x99\xaa\x8d\xaa\xc4v\xc5\xcb\xcd\xca\x1783u\xec\xaa0~\xc7\xf6\xf6N|\xdc\x8a)\xc1B\x13E\xdd\xc6\xc8-\x17\xb3\xa4M\xbd\x91C\xe4\xe2\x08\xae\xb7\\V\xd0\xdb\xe2u\xdb\xe7\x0eAY\xb5\x9c\x15^\xb0\xe9\x9a\xcd\x0b\x89\x9cp(Vm=\\\xe6\xf9\xb1]\x0e\xc1@\xcf\x92\xcf\xaa^N\xec+\xb8\xae\xf0C\xbd\x1cVlU/\x07\xa3\xea\xd4\x1e\xe18\x80\xdd\xb1\x8a?c]eY\x8c\xf7W\x84\xcb\x85\x15\x9c7\xe5\xf5\x96\x873pb\xcdU\x16\xc9\x08\x02\\\xd3\x95a\x1c\xa0\xcc\x8b[\xdb\x86\xf2\x85\xb1\xe8\xb0\xd5[0\xd2\xdf\xdb\xa1._V\x0b\xf6\x80\xbd\xfcxv\xec2\xdcSh\xecL\xf7\x90\xd33\xe4'^\xca,.\x19\xf4\x0e\x96&!p\x04\xec\x0d \xb5\xcc\x08}C\x12\xf8\x0d\xb9\xe1o\x98\x08\x80\x87\xfbU\x1b\x87\xc0a:\x08\xee-O\\1\x06\x83C6 \x1c\xf0\\3`\xc0pH\x83\xc3!FsN\x84\xc4\x01Qn\x00\x18\xcb\x04\x8c\xc3$\xe7\xe2\xc1q@\xb4r\x02@\x0eS!r\x08{5\x1fL\x0ex\xa0\x1c\x90P9\xa0\xc1r\xc0y=\x1d0\x87$\xc8\x1c\x82\xa09\xe4\x82\xcd!\x158\x87=\xa1s@\xb87\x01>\x87C\x00\xe8\x80\xa9c\xe0I\xc8\x07\xa3\x03\x06H\x87=\xa0to\x81\\\n\xe1\xf8\xc1t\xc8\x0d\xa7C\x14P\x87\xa9\x90\xba\xb74\xf5\x8d\x1a\xfe\\G\xc0\xea\x10dj!\x08\xad\xc3$p\xdd[T\x10h\x87\xa9P\xbb\xb74\x1d>\xf0_.\x1f\xdc\x0e(\xc0\x1d&@\xee\x90\x06\xba\xc3\x14\xd8\x1d\x92\x81w\x88\xbcm#\x102$\x80\xc8X\xf8\x1d\xa6\x00\xf0\x90\n\xc1C\xb8\xe1S`xoa\x16j\x8e}dpP|\xf0\x81\x90\xd0V\x00\x8c\x87\xbcp<\xc4\x00y\x08C\xf2\xdes\xa6\xc2\xf3\x90\xb1\xef&@\xf4\x90\x04\xd2\x83\x05\xd3\x0f\x0d\xf7\x05>\x88\x82\xed\xe2\x1d&\x92\xd6\x1f\"_C\xee\xb2\xc4\x88\xa6\xa0\x98\xb7\xd5c\xeb\xfflt\x82\x8d\xff:88\xacif\x80@[\xa1\x8a[\xf2m\xea)s\xefGz\xc9\x9a\xbbr\xce\x8e\xbb2H\x82J\x19IP\x91\x04Uo$AE\x12T\xbde\x8d\x84\xa6DA\x93\"\xa0$A\xb5o\xb4sB\xa43K\x943=\xc2I\x12T\xfbD4S\xa2\x99\x13\"\x99$AE\x12T$A\x85\x8dDf\x8dBN\x89@\x92\x04\x95\xef\xb0h\xa41!\xca\x88\x11XJ\x89.\x92\x04\x15IPa\"\x85$A%m\x9fh IP\xb9J\x8aF\xfc\xa6F\xfb\xbc\xef\x06\x92\xa0\xda5\x92\xa0\x9a\x10\xa5\x8bG\xe8R\xa3s \x91\xb9\xe4\xa8\\ZD\x8e$\xa8\xd2\xa2n$A\xd5\x19IPi3\x1a!\xd7\xf5\xc2~\xf7\x95\xd5\xce\x9f\xbc2Q\xae\xd8\xcf\xff\xd3\xb0\x9b7\xf0\xf2\xff>\xb1\x16\x0e\xb5\xd4\xc61\x7f8\xd6R\x1b}hJ\xed\x10\xf3R\x971\x16\xeb\xd0\x91,\xb7\\\x07\x7f\xd0\xc7\xba\xb6\xdc\xfc\x0b\xe3W\x0f\xadJ\xc2\xbba|~+\x06\xf9\x87V\xca\xe9\xd8\x19\x96\x03\x15\x0e\xeb$\xfd\xf3\xd3\x08q \x9dfU\xcf\x84\x05_\xbe\xe8kA\x01\xbd\x81Q@\x0f\xb7 \x04\x14\xd0\xa3\x80\x9e\xf7H\n\xe8I\xa3\x80\xde\xaeQ@\x8f\x02z>\xa3\x80\x1e\x05\xf4\xa4Q@\x8f\x02z\x14\xd0\xa3\x80\x9e2\n\xe8Q@\x8f\x02z\x14\xd0\xf3\x19\x05\xf4(\xa0G\x01=\n\xe8Y\x96#\xb8B\x01=i\x14\xd0\xfb#\x04\xf4\x94\xaa\xa5U\xc4\xe0#R\xfdj\xb2\xc9Ve\xab\xd6\xbc-\xddzy\xc4\xce\x17\xe9\x14!\xfeq\xb8\xc4\x11(\xf1|\xc0\xcf\xeb\xd5\x8a\xc9\xea|\xaf?\xdf\xa5\xce\xf4N[i/\x9a\xb4\x8d>h/\x9a\x03:7\xbe\x8b\n\xedE\x93\xc3\x8b\xb4\x17\x0d\xedE\xf3\xdb\xdc\x8b\xa6\xf7\x86\xac\xc1\xec\xda\xff\xca\xfa\xd3h\xb1\xf75|\xf8\xf8\xee\xec\xe3\xec\xbb\xbf\xcd>\xbd\xbf\xbc8;=\xff\xfe\xfc\xec\xdd\x1b\xe7_\xbb\x85e\xb9\x1a\xbc\xad\xd4\xe4\xb1\xad\x1b\xde7\x1e>\x88\xff\xfb\xee\xd1<\xf0r,y{y\xaa\xe3\xdd)\xd6\xedr\x16\xddo&\xde1\x95\xc5\xbb'\x84v\x93R\x16\xed\x04\x10\xd9\xc5IY\xcc\xb9\xca\xbe\xcc\xe6F\xb2\xc8\xc81HG(\xc3\xed\xf7\xa4\x0c\xe7\x18e\x08\xf7(C;I\x19\xdeU\xcaP\xbbA)K\xf0\x9a\xb2\xa8\xfa\xb7m\x89\xa5\xe3\xc6\xab\xa1\xf5;/u\x1f\x1dU\x7f\x83\xe1\xbe)6\x1b\xd6\x88\xaf\xdc&\x04\x94\xf5&\x06\xc0\xcf\xecQ\xae\xaf\xab)]\xd1Dd\xd5\x8d\xa9\x86\xb6f\x9399\xf7,\xee\xd50\x1f\x08t&\xb7\xfcR^G\xe53\x98V\xb3j\xb0\xcbPR\xc3%J+\xc6\xfd\xee\xb9\x88\xb5\xd7\x00\xd8\x9f\xd9\xe3I\xbf\xc3\x95Fq\xc5'\xf8\xc8\x15\x91\xe2\xb0\x8eJqS\xfavL\xc1\xe2\xf4VMfS\xa6\xb6^G:Ep3&Hh\xcc\xdb\xefN\xcf\x7fTA\x8c\x1f\xeae\xdf\xcd\x85\x8f\xb7s\xbem\x98i\xa4\x940\xac\xd4\x86h\x01\"\x93?\xc82\xbb\xc8\xc8\xaa^\xba\xeb\x88\xab!v\xfe \x06\x83\x85\x9a:\xf87\xe0A\xcd\x11\xdc\x9b\xce\x02f\xc0\x19~\x86\xf4\xf1XkO3SC\\]\xb2m*\x8b\x9c\xa4\xbe\x1dl\xa8\xda\xa8\x94)\xc3\x92\xee~\xb9\xd9\x96i'\xd8I\x155\x1b\xbe\x8a\xaf\xe2H5\xb9gr\x15}i\xc6_\x93<\xba\xb9X\xd4\x0b\x80~.`j.M\xa0\xbc\xa4M\xc5\xb2\xe6\xd3D3jr\xe7\xd4\xe0\xb3j2\xe5\xd5L\xcb\xac \x14\x97\xb8\x8d\xd8\x9e\xd95\xb9\xf3k\x123l2\xe7\xd8\xa4e\xd9$\xe6\xd9\x84\xfap\x97\x81\x83\xcd\xb4\xc9\x9ck\x83\xca\xb6\xc9\x98o\xb3o\xc6\xcd\xa4\x9c\x9bLY7S\xf2n\x02\x85\xa17\n;@\xee\xcd\xe1\xb2o\x0e\x92\x7f\x93\x96\x81\x93=\x07\x07\x9b\x85\x935\x0f\x07\x9f\x89\xa3\xa6\x11 \xb98\xe9\xd98\xd1\xa1\x10\xb7%\xd8\xde\x199\xd1\x05\x01\xd4\x84\n\x91\x97\x932\xebJ\xce\xcd \xbd\x04\xd1\x9b\x80\xe1\xea\x971C'%G's\x96\xce\xb4<\x9dP\x0fBm\xfc51W\xc7S\x1aGm\xfa\x95'_\x07\x9dt\x82\xc8\xd9I\xca\xda\x89\xed\x993%s'V\xa6\x97\xe0\xcd\x94\xbf\x93\xeeL|\x0eO\xacm\x13\xf2x&f\xf2\x84H\xe8l\xd9<\xe8|\x1e\\F\x0f6\xa7\x07\xe1\xe5\xf4\xbc\x9e\x94\xcc\x9e\xf06^Y\xb2{\x12\xf3{\xf6\xcb\xf0\x8994!\xcb\xe7\x00y>\xd1\xday{z\xbel\x1fD\xbe\xcf\xf4\x8c\x1fOq<\xba]W\xd6\xac\x9fX\xde\xcf\xc4\xcc\x1fOY\xf1m\xba\x10\xd9?\xe1-\xbaB\x1bt\xe5\xce\x01\xca\x9e\x05\xe4\xcf\x03\xca\x99 \x84\xc9\x05J\xcf\x06J\xca\x07\x9a\x90\x11\x94\x9a\x13\x14\xd9t+\\;l\x96\x0663hBnPbvP\xa0\xb9S2\x84\x89\xed1X\xb4m=/\xe5\xca\x90\xde\x1fu(\xfe96l5\x15L\"\xa3\x0e\xadM>\xc0\x82\xdd\xb1\x95\xe8arI\xbd\xe0\\\xc2\xe0\xdd\xc4\xcf[\xa0\x85\"\x00\x0f|`\x1a\\\xf5;\xb6,\xab\xef\xc4\xe8u\xd4\xfd\xed\xacZ\x8c\xferz\xcb\xe6\x9f;b{\xd7\xc4d\xc1\x1c\xfb\x8e\xad\xca;\xd6\\=\x04\x16\x0e~(8k\x8e\xecX~\x0bk\xc5M\xfcc\xcb\x1a\xf1\xd9\xa2\xf2\xf4\xf8-k\x99\x1e\xe8\xf6Y8\xd5sKUOF\xa5\xc4u\xa4\x1e\x8b\x1a\xd7VR\xd2\xf8\xbe(y\xeb %\xc8w\xfc\x83^E\x9dK,\x8d+\xe5\xc2B\xf3j\xa8\xea\x8a;\xbeS[\xf1\xc7\xac\x95-@3&\xbd\xf4I\x1f\xab\x16\x13V\\e\xdf:k\xfbvRu\x95\xd6\xa3k\xcd\xa7\\Kr\x90\xb3q\xb5\xc2\x9dz\x10\x96\x97\xd2\x1b}T^\xfd\x93'\x05\xe5u!\xe3>lwo\xf5\x17\xbf\xb6\xe3K\xf7n\xc8'\xb2\x7f\x9c\xfc\xaaP\xc5\xff\xd4Z\x8c\x9e\xdd\x91%+\xf4s\xc9o\xaf\x1e\xdan\x87d\xdd\xc5\xd4\xaa\x9eF\x18\x80?t\xab\xaeCI;'\xd8\xf1/\xc7\x7f2\xaav\xe3\xbd\x95\xedK\xeaC\x9eFF\x12\xbf\xbf\xb2]E\xdacY\x1a\xe1\x13\x84O\xf4F\xf8\x04\xe1\x13\xbd\x11>\xc1 \x9fp\x1b\xe1\x13\xc6\x08\x9f |\x82\xf0 \xe4,\x89\xf0\x89\xce\x08\x9f\xb0\x8d\xf0 \xc2'\x1cF\xf8\x84\xf3\x18\xc2'\x08\x9f\xf0\x18\xe1\x13\x84O\x10>A\xf8\x84e9B\xd9\x84OH#|\xe2\x8f\x80O\xecl\xce5\xf8\x88\xd4R!:J\xa8\xff\xa5\x07?\x15_3\xbb\xf4\xd9\xa3PY\xbd\x19\xaf\x86{\x19\x0c\xcf7\xb9[\xae\x9f6K\x9e\xb6\xd1\x1em\x96|@\xe7\xc6\xb7\xf9\xa5\xbd sx\x916K\xa6\xcd\x92\x7f\x9b\x9b%\xa7\xe3/\xbf\xde\x16\xedm\x04{\xb9z\xb0`\x17\xfe \x9eR{\xdb\xcb1\xb2\xf2\xc4\xfb\x9d\xe2A\x95^#\x84\xf0\x14\xc2S\x08O\xd1Fx\n\xe1)\xbd\x11\x9e\xc2 Oq\x1b\xe1)\xc6\x08O!<\x85\xf0\x14\xe4,\x89\xf0\x94\xce\x08O\xb1\x8d\xf0\x14\xc2S\x1cFx\x8a\xf3\x18\xc2S\x08O\xf1\x18\xe1)\x84\xa7\x10\x9eBx\x8ae9P\x01\xc2S\xa4\x11\x9e\xf2\x87\xc0S\x8a\xd6\x1e\xa2\x86Y\xdb\xe2\xc7.T\xfe \x8f\xedx\x94#`\x95J\xfa\x96]\xec\x96=\xe8\xa7\xf8\xf8\xa5U\xde\x9e\xa4\n\"\xb0\xb7\xdd,\x9bb\xc1\xba\xe8\x9e\xdcc\x83-f\x9bUQ\x9d\xfc*\x1a\x19\n\xf3\xbdUG_\xac\x8aJoL!\x1ac\xf6\x1fZ=\x82.\x0e\xf4e@\x14+\xe6\x11%o\xa5\x07\x9d\xd1@\xab\xd4\x17\xa6\xd1O\x11\x13\xe4\xe9\x917\x85\x1c%\x85\xbd|\xf0\x90\xb1\x00\xe0\xa4\x90&\xfd\xb7\x82\xeb\xc7]\xfc\"\xfdz_\xb4\xc6\xe1\xc3\xb7Zxe\xe3\x7f\x89\xfeh\xf9|\xca\x96\x08\xb2\x8c\x13\xbb;|\xbc8\x1d?\xf3\xb4G\x02EQ\xa3K\x88\x98U8\xa0(*EQ\xbdGR\x14U\x1aEQw\x8d\xa2\xa8\x14E\xf5\x19EQ)\x8a*\x8d\xa2\xa8\x14E\xa5(*EQ\x95Q\x14\x95\xa2\xa8\x14E\xa5(\xaa\xcf(\x8aJQT\x8a\xa2R\x14\xd5\xb2\x1c\x11-\x8a\xa2J\xa3(\xea\x1f!\x8a*\xfe\xd7*`\xf0 )\x9fD\x1d\x89\xb2\x17\xa1MdQF\xbeLTU\xbc\"\xc6\x99\xa2\x19\xe3\xa7\xffK\xa7Yz\x83\xa7[~[7%\x7f\xf4GL?*\xd1t\xd5\x84\xb9\xca\xcdU_\xad\xe6\\\xb9\x14WW\x8b\xed\x9c\x9b\xb0\xa9\xf9fD\x88\x82\xff\xabS\x12\xfc\xad)\xfc\x85q\xc23\x8d\xa7\x16\x8bE\xc3\xda\x94\xad*R|\xa2\x8b*\xf9\x8a\xbd\xd1aO\xe3\x98x\xd0S\x878G\x9e\xa4(&E1\x1d\x86Y\x05\x03\x8abR\x14\xd3{$E1\xa5Q\x14s\xd7(\x8aIQL\x9fQ\x14\x93\xa2\x98\xd2(\x8aIQL\x8abR\x14S\x19E1)\x8aIQL\x8ab\xfa\x8c\xa2\x98\x14\xc5\xa4(&E1-\xcb\x11Q\xa2(\xa64\x8ab\xfe^\xa2\x98\xa9\x11A\xbd\xde!\xd3)\xfdA\xc1Su\xd4 \x8dR\xdd2\xf9\xf7A\xf6\xa43c\xd2*@\xff\xfcl#|\xbd'l\x1bTE\x86t\xcb\xb8\x0bz\xf3\xd6\x03\xa2\xe1\x1e\x19\x81v\xde\xf7\xe8\x1a\x07f\x91\x00\xe0\x92q+z\xad\xd3'M{t\xbcE\xfedF\x00\xb9r\xe3\x99F\x81}\xb27\xbc\xa3\x97\xfa\xba\x05\x81\xfa\x86\xdfk5\xe2b\xb3Y\xa9/-\xb9\x08R\xac\xe0\xab\xbaz\xad\x0b\xf4\xf5\xffy\xbd^\x17\xd5\xa2\x85\xc5V:#P59\x90\xc3wlYVr\x87g=\xd5\xea\xa7P\xdd\x9d,=\xc9\xaa\xbd)\xa5\xe8b\xd5\xca0\x82\xb7\xb5\xbc\xfe\xff\xd9{\xf7\xee6\x8edO\xf0\x7f}\x8aX\xef9#\xe9\x0eE\xdan\xb7o_\xedz\xcf\xd2\x12\xed\xe6\\Y\xd2\x92\x94\xbd\xbds\xeeRE A\xd4\xa8P\x05W\x15(\xb15\xf7\xbb\xcf\xc9W\xbd\x90\x8f\xc8DB\x82\xad\x88?\xba-\xa2**3\xf2\x1d\xbf_D\xc2\x9c\xb5l\xd6\xc2\xfb%\x13\x0e\xb6\xac\xaf\xb26\xc5,+a\x99\x95\xf3\x82A\x06\xb7\xf9\x1d\xb39\x1d\xbb\x86\x11>v\xdb7u%\xa4\xcaZ\xbal\xda\xae)\xf9Zq\xc3X)\x10\x04C\xd2\xe7^tA\x8f\xbaT\xe7v\xa7\x9er\xe8\xf7\xf9\xc0\xbbZ\xe6\x0dT\x9b\xf6I\xb5x2\xcfZ&\x13i\x0flm\xd1w\x95\xaf\x18T5\xfc]\x07\x19\xdb>\\\xb3l\xb6\xe4K\x92\xdcn\xf7\xdf\x15\x05f\x1f\xf2\xd6\xd4\x84m\x1e=\xb2\xb4\xef\x90\xd7\xe6 \xd7\xb3\xc3\xf8{\xce\xd7\xd9Y\xd6\xb2\xf9SYc\x01\x06t\xd4\n\xe9\xd4\x17\x8d5\xef\x9e<\x16OZ\x14\x8e\xde\x87\xa2\xba\xcdg6\xc3u\xfd\xa0f\xab\xea\x8e\xcdaQW+a\xc2\xcb\xe7\xffn=\xf7\x8b\x8dI\xde\xa8}\xabrb\x0b\xbf\xf0Q\x07\x8ft\xb3E\xbb\xac\xab\xf7\x86)\xd1\x1e\x9b\x0e!-`\x8eQ\x87\xa9\xf5\xff\xa7\xcd\xfaW\xfd\xbd\x1c\xa3\x80um\xbe\x95r\xed\xaeY\xcd?i\x9f\x0c^\xa9\xa3\xda\x9c\x9f\x8cE;*\xc3L\x92\xc3k\xc9\xcbE\x15YwE\x10\xb1\xf6(\xe1\xab\xea!\xa8\xde\xa3\xdeMm\xe5\xa2R\x98\x8dJ\xcao\x83l\xaa\xf2\xc9l\x99\xd9\x11\xe8f3[\xca\x0c\x11\xb7y+\xe6\xe1\\]c!<\xe1Y[\xd5\x0d\xcc\x04\xf0\x9cm\xdaj\x95\xb5\xf9\xcc\x01i\xea\x02\xb6\x95\xe1\x01\xbd\xaa\\\xcf\x8a\x9c\xefY\x9a6k\x9d\x03\xd8J\xb0\xf0\xad\xb9\x80 Y \xda \xd0s\x00\xc4R-\x1c\xfa\xb6\xbc\xed\x0e\xe7SZ\xba\x85\x97p\x91\x9ar\x81']$\xa2]\xc4\x11/\x1c\xea\xb8A\xd1\xd4\x8b\x9d\xc9\x17\xa9\xe9\x17\x81\x04\x8c\xc4\x14\x8c0\x12F \x0d\xc3\xd5\x87;\x82\x06\x96\x88\x91\x98\x8a\x81\"c$\xa4c\xecJ\xc8\x88\xa2d$\"e\xc4\xd02\x1c\xca\xe4\x1e\xcbK\xcc\xd8\x0b5c\x7f\xe4\x8c\xbd\xd03\xc2\x08\x1a\xc9)\x1aX\x92FR\x9a\x06\x9e\xa8\x11L\xd5\x08'kx\xa7\xc2\xc7\x08\xbaF\x02\xc2\x86\x87\xb2\x81\xdcP!h\x1b!\xbb\xae`\xea\x86k\x11\xbc\xa9\xee\x18\x82\xbc\x81-_B\x02G\x08\x85#1\x89#\x8e\xc6\xe1\xeaA\x8d\x9f\xc8\x11M\xe5\xb0h\xe3_\xf3\x919R\xd19\xd0\x9c\x04\x04\xa5#\x88\xd4\xe1\xc1`\xa3\x88\x1d>\x9dV\x80'\x11\xbd#\xdc\x98x\x8a\x87\xafn\x114\x8fH\xa2\x87\x0b(KF\xf6@\xd3=p\x84\x0f,\xe5\x03a\xe5p\xdaG\x08\xf1\xc3E\xfdHD\xfe\x08\xa4\x7f\xecF\x00\xf1\x194\x80\x04\xb2\x07\x1a\x88\xb7t\xd6\x9e\x9e\x8e\x0c\x82\xa0\x83\xc4\x13B,\xea\x84\xd3\xd1A IL\n\xf1\xd1B\"\x89!\x16]\xf2d\xe8:\x1c#\xc8!.\x04\xdbE\x10IO\x11IN\x12\xb1\xd3DR\x12E0T\x91p\xb2H\x10]$\x820\x12J\x19q\x92F\xdc\x10>\x1e\xc4\xc7\x12G\"\xa8#\x81\xe4\x11Guc\x08$\x16U\x03r\x06nH\xe0H$\x8e._\xde\xbai$I\x89$\x1e*\xc9~\xc8$\xa9\xfab\x00\xa1$\x84R\xd2\x93J\xa4 \x924\x0fh\x1e\xfexe[\x92\xe6!\xd9\x84\x924Sx\xb3\xf1w\no\x1e\x08\x857Sxs/I\xb1\xb5\x10d-\x08W\xa3\xf0\xe6]\xd1\xb4\x08,- \x92\x16\x8e\xa3Qx\xf3.\xf8Y\x08z\x96\x18;\xc3!g q3,jf\xd8\x10Sx\xf3X\x108\x19v\x97\x14\x8c\x91Qx3\n\x19\x8b\xc1\xc5(\xbc\xd9\xf6\x98\x17\x0b\x0b@\xc20\xc1\xbb!(\x18\x857Sx3\x06\xeb\xa2\xf0f!\xbb\xa0[\x14\xdel\xd2\xe4\xc5\xb3b\xd1,\xeb\xda@\xe1\xcd\xdbB\xe1\xcd\x11\xa8\x95\x1f\xb3\nE\xac\x02\xf0\xaa`\xb4*\x0c\xab\xa2\xf0\xe60t\x8a\xc2\x9b;\xd9\x07\"\x95\xa2\xcf\x05\xa0Qx,*6\xbcyU\xcd7\x05\xbbV\xbe\x97\xc6\x1e\xe1\xfc\x8bx\xf0W\xf5\xdc(\xc8\xb9\xc8\x1b\xe14\x95\xba\xb4\x1f\xa7\x91\xa1r\"\x06I\xcf[\x88T\xbf\x7f1\xa6?\x1e\x7f]=q\xb0\x11\xd2F\xa3\x0e\xa5\xfd\x0c\xa0\x91=L\x1a\xe5-Q!u\xe58\x81\xb7\xaa\xaa\xf1\x1dU\xfd\x1d\xbe\xa9=4\x1b{\x0ccW\xb0\x19o\xcb\xb2\xd94\xd3\xd8ig)1!\x90\xa3\xbe\xd7\xb9f\xf8\\\xab:\xbct\xe65\xdbE\xb0\x9c\x17\xcc\xfd~\xebQ\xbf\x8bg\xd2\xcf\x84O}:\x1a\xa5\x1bUE7\xb3\xbc\xeeKiP\xa85\x8d\xf7\x19\x08\\{<>\xe3\xa1\xed\xc98\x9f\x9d\xfa\xde{\x84\xbd\xad7\x1e\x80\x1d\xc0|W\xab/\x92?\xdb\xb4\xcb\x7fv\xa8\xfbm\x9d\x95\xad#|\xff\xe1\x05k7u\xd9t\xf4\x81\xb7\xa7\x9bvY\xd5\xf9?\xa5O\xfe\x08\x84\x06\xe9\x0d\xe4\x16\x91\xffd|\xcb\xd2\xff\xb3>6F\xe7\xff,>\xfe@\xd7\xfd@\xf1\xda\xa1\x89\x86\xd2~\x06\xb0,\x1b\x1a\xdf\xed.r\xdc\xd1\xea\xff\x0e \x809\xc0y\xa6\x00\xe9\xe0\xd1\x12\n\xd29\x95\x99=5\x8e\x83Kj\xb0\x0e\xfc\x80\x1dD\x80v\xee\nd\xed\x12\x0d\xdcA*\xf0\x0e\"\x01<\xa7\xc2\xc0\xdb[w\x06\xf2 \x18\xccs\xaaR C\x10\xa0\x07\xa9A=\x08\x04\xf6 \x14\xdcs\xf7\xec\x88\xbb\\\x13\x83|\x80\x03\xfa %\xd8\x07;\x03~\x10\x07\xfaA*\xe0\x0f\xa2\xc0?\xf7p\xc0\xde\xed\xba\x17\x10\x10\xf6\x08\x04\xc2~\xc0@\x08\x04\x04!\x0e\x14\xf4M\xc18`\x10\xd2\x82\x83\x10\x00\x10B8H\x08\x11@!b\xca\xc4\xdd\xf8\x9a\x000\x04\x1fh\x08\xf8\xed\x19\x02<\x84\xc0]\\0\x88\xe8\xd4\x86\xbf\xff\x15_\xca\x84\x80\"\x04\x81\x8a\x90\x1aX\x84Hp\xd1\xdd\xafPw\xc1F\x83\x8cV}-\xea>\xd8T`#\xe013\xc0\x80\x8e\x10\x06<\x82\x0f)\x88\x04 \x01\xa1\xd7\xe1\x8cL\x04FB\x94q\xf1\xa0$ j\x19\x01NB,@ \x9e\x1b\xf2\x92\x01\x95\x80\x07+\x01 X\x02\x1a\xb4\x04\x9c\xd5\xc3\xc1K\x08\x020\xc1s{l\" \x13B\xc1L\xd8\x11\xd0\x04\x84y\x03\x80M\xd8\x07\xb8 \x982:FB:\xa0\x130`'\xec\x00xZ\x15\xb6\xde[e\x13\x03\x9f\xe0\x05?!\x16\x00\xb5j\xf3\xdf.\x8b\x02B\xc1s\xc3\xac\xfb\x8e\xd9\x18P\xd4\xaa\xcas\xfbl$`j\xd5&\xf7\x81\x0e\xafY:\xe0\x14P\xe0)D\x00\xa8\x10\x06\xa2B\x0c\x90\n\xc1`*\xf8\xee\xa3\xf5\xdd\x02\x8a\x07\xb9\xb0\xc0*\xc4\x80\xab\x10\n\xb0\x82\xbb\xe21@\xabU\x19\xe2v\xda8\xc0\xd59 \xfc7\xd4&\x05^\xc1\x07\xbe\x82\x1b\x80\xb5\xbe\x13\x0b\xccB\xc2\xbe\x1b\x00\xd0B\x10H\x0b[\xf7\xd8ja\x1f\xd6y\x8d@\xa8P\x84\xe6y\xd6\xb2'm\xbe\xb2YYE::\x0e\xfa\xfcex\xbfde\x0fH\xca\xee(\xca)\xd30\xeb\xee\xb9\xae7%\x9b\x1f\xc3\xb9\xfd\x80Zn\x8a\x82\xf7\xf1\xa1>{S\xce+\xd6\x94\x0f[\xe9\xc7\xccdYz\xfb\xc0#\xe9z\x9aU\xe5\\\xf9\xb1]\x04\xfd\xb7#\xf8\xcf\xd1YW\xd9=d\xebu!|\xa3y)\\02\xc8Y\x15\xd8\xb4\x01\xc5\xe4\xb4\x16\x88-\xdc\xe6w\xac\x815\xabWy#\x83\xc8\xdb\n\xd8\x076\xdbX\xfcG\xfc\xbbj\x8f\xa76Ere\x1cX\x82[f{a\xf3;sF&\x99\xa4\xb2\x96\xc8m\x87P\xf3!\xdb\xc3\xd3\x06]\x1a\xac\x9e\xfc\xb4\xcen\xf3\xd2\xd2\x9fG\x05\xec\x1f\x94\xd0\x04\x13n\xa5\xc1_u\xdc\xb8 \xad\x96\xd2\xba\xb0[7j[\xb2\x0f\xed\xf5;vo\x0f\xb3t\x0e9\xaf\x13\x10\xd3?\xfaRh\x86\x04\xffO\xe5G\xcf\x9aF\x02\x07\xaf\xb3[v!\xc9\x15\xc7\xf2w\x8b2\x99(\xa1\xd5\x91\xf6k\xbe\x8e\xad\xaa\xa6\x05&\xbc\xd1\xc2\x8d\xddG\xda\xdf\xe8x\x8e\xdc6z\xf9`c\x02\xc6(+XU5\xd3\xb0\x85iK\xd5Vmf\x01\xbf\xd1\xc6td\xa0\xf7M\\\xe2\xf3\xc2\x8a\xe2?\xca\xcd\xeaF:Ku\x98\xd5 \xa6\xc7V\xdf\xa1\xa1g\xd5\xa6l\xaf\x852\xdb\xc4\xf1>k\xa0a\xed\x91H\x01\xa0@\xa4F\x90[xg\x9eK?\xf9\xfb\xbc\x19\xf7\x0fD\xfc\xb8$z\xc4\xc7\x8b\x9f\x8e\x079\x05\x86+\xa1\xc0p\n\x0c\xef\x85\x02\xc3)0\xbc\x97\xa4\x1c\x92\x10\xfeH\x10w\x84\x02\xc3w\xe5\x89DpD\x92\xf0C\xc2\xb9!\x14\x18\xbe\x0b\x17$\x84\x07\x12\xc1\x01\xa1\xc0p\n\x0c\xa7\xc0p,\x87#)\x7f#\x86\xbbA\x81\xe1\xb6\xc7\xbc\x1c\x8d\x00~\x06&\xec9\x84\x97A\x81\xe1\x14\x18\x8e\xe1XP`\xb8\x90]x\x14\x14\x18n\xd2\xe4\xe5J\xc4\xf2$\xack\x03\x05\x86o\x0b\x05\x86G\xf0\x1b\xfc\xdc\x86P^C\x00\xa7!\x98\xcf\x10\xc6e\xa0\xc0\xf00\xbe\x02\x05\x86wB\x81\xe1Jt`\xb8\x02q\x07:v\xcee>U\xce\xf6\xa3|\xd5\xdc^\xb7\n\xf4\x18\xbc\xe3:\x04\xbfR\xde\xd3\xa3\xd1\xcbG\x92\xee \xa0<\xd1\xa1%\x8e*\x96Y\x85\x89\xaf\xb2v\xb6\x9cv\xec\xdb\xfc\x8e\x95\\\xd3\xd6\x81 \xaf\xd4\xcc<\x12Fz\xf87X)\x90\xd7\xaa\x9e\xb3z\xea`\xf4\\\xde\x9f\xc2\x1a\xde{LL\xf9TN\xd4>\xe1\xe4\xa3\xfa\x0f\xd7\xbd%?\xcbG$GF\x99c\xc8b{+~\x19\xa7]\xe1\xc3Z\xa9\xd6\x8d?j\x0f\xf3\xc5\x0f\xdf\xdb\xb3\xb0\xe8\xef?\xd06\xa2d,^\x82\x8a\xdaiF\x833\xaa\x01\xa3\xdf\xa7d0\x94\x0c\x86\x92\xc18dG\xf2\x0e\x04\x13x\x9c\xaa(\x19\x0c%\x83\x89%\xf9@\x1c\xd1\x07R\x91} \x8a\xf0\xe3\x1e\x0e\x94\x0c&\x8c\x00\x04\x81$ \x88#\x02\xf9\xa6`\x1c\x19\x08\xd2\x12\x82 \x80\x14\x04\xe1\xc4 \x88 \x07!\xa6LJ\x06#%\x988\xe4\xd4F\xc9`(\x19\xccD\xd2\x10\x8c\x00\xcf\x93\x01\x0c\xd1\x08\xc2\xc8F\xe0c\x07D\x92\x8e\x00\xa1\x97\x92\xc18$\x8a\x94\x04\x94\x0cFI\x14a \x82HK@\xc9`0d&\xd8\x07\xa1 0e\xa4d0i\xc9N\xe0%\x8e\x81\xe2\xdb\x19\x832\xb5\x98\x8e~ADKt\xe7\xde\xf8H\x89\xfe(NQ\x12J(J\x82\xa2$z\xa1( \x8a\x92\xe8%\xe9i(\xe4,\x14t\x12\xa2(\x89]\xcf?\x11\xa7\x9f$g\x9f\xf0\x93\x0fEI\xecr\xe2 9\xef$>\xed\xe0\xce: O:\xd8sN\xe0)'\xf4\x8cCQ\x12# >\xd5P\x94\x04EI\x00EI\xf4BQ\x12\x14%AQ\x12\x14%a\x13\x8a\x92\xa0( \x8a\x92\xa0(\x89\x81\xa4`\xacS\x94\x84\x10\x8a\x92\xf82\xa3$\\\xa7\xc8XZ_G\xe0\x1bi\xdbb\xba%\x0b\xd0PL\xbd\xa0Z\x05\xf1\xfaFU\x1b)\xf3\xb1\xf9v\xac\xe3\x0e\\\xde&\x8c\xc2\xdb_\xaf'\x1c\xe9\x9a\xa9\xdb\x88\xbd\x80\xb2\x91\x9b\xb4{\xf0\x17\xe3\xf5\xa69\x0c\x0c\xcf\xc9\xccE\xf9\xaf0. \xd8a [\x15\xfa\x19\xba\xbb\xdf\xdd\x17Z\xbd\xa0\x11m\xd5\x15\xc5\xd5\xf5\xb2u\xf1uI\xc8\xd8\xf5rv\xbd=\x18\x10\xbd\x18\x10h4\xe0\xda\x1b\x02\xec\x04\x11\xc8\xb4S\x99\xd9=\xe98\xad\xa7F\xa8\xc1\x8fRC\x04R\xed\xae\x00]}\x18\x8b^C0\x82\xedTEW\x1fF\xb1{\x13#\xdb\x80C\xb7!%\xc2\x0d;\xa3\xdc\x10\x87tC*\xb4\x1b\xa2\x10o\xf7p\xc0\xb2}\xf7\x82|\xc3\x1e\xd1o\xd8\x0f\x02\x0e\x81(8\xc4!\xe1\xbe)\x18\x87\x86CZD\x1c\x02Pq\x08G\xc6!\x02\x1dGL\x998\x0ep\x02\x94\x1c|H9\xe0\xb7g\x08\xc4\x1c\x02wq\xc1\xc8\xb9S\x1b\x8e\x11\xec\xcb\xc6\x9e\x94\x15l\xe6\x05\x835Xt0{\xf2\x7f>l\x02\xc3E\xe9\xfa\x02!\xae\xec\x1c\xbd\xd0\xf5\x05@\xd7\x17\x0cdL\xc8\xdf\xe1\xee\x82\x81g\x8d(\xf9J\x88\x92\x9f\xc6\xe1\x15\xea\xf8\xd0\xce\x0d\xabB\xac\xd3#\xa9\xc3\x83(\xf9D\xc9\xef%\xa9##\xc4\x89\x11\xe4\xc0 J\xfe\xae\xce\x8a\x08GE\x12'E\xb8\x83\x82(\xf9\xbb8$B\x9c\x11\x11\x8e\x08\xa2\xe4\x13%\x9f(\xf9D\xc9o\x89\x92\xdf\x0b\x86pN\x94|\xdboD\xc97>C\x94|\xa2\xe4[\x84(\xf9D\xc9'J>Q\xf2\x07\x92\x82\x1eM\x94|!D\xc9\xffr(\xf9CK\xee\xc8\x19\xa7\x8b\x0bb\xb3\xc2\xd3\xc5\x05{4\xae?\xe5>]\\\x90\xc2\x8atq\x01]\\\xf0\xa5\\\\\x907\xcd\x86\xcd\xfb[\x0bP\x81N?\xde\xff\xac\xc2a,\x11O\xb7\xf9\x1d+\xf9h\xceJ\x1dK\xa2\xb4\x8d\x1a\xe2\xa1\xd1$\xdf?T\xcfZ\x82\xa4\xba\xaf?\xd0&\xa2h)!>z\x05EK%\xad\x1eEK\xd9\x95Q\xb4\x94\x07\xd9\xa6h)?\x91\x08R\x91\x89 \x92P\xe4TH\xd1R\xbb\x93\x8c \x90h\x04\xa1d#w\xcf\xa6h\xa98\x02\x12\xc4\x91\x90 \x15\x11 \xa2\xc8H\xee\xe1@\xd1Ra\xe4$\x08$(A\x1cI\xc97\x05\xe3\x88J\x90\x96\xac\x04\x01\x84%\x08'-A\x04q 1eR\xb4\x94\x94`R\x93S\xdb\x9f\"ZJ,fb>\xbba\xac\x04\xe9\x14\xb0\xdc7\xa8\x85b\xa4\x84P\x8cT\xff\x13\xd6\x98\x14#5\x89\x91\xea\\Z)\x82\xa5z\xef\xdc0jjZ;\xb3\xeb\xad{\x88\x82\xab&\xbfRp\x95\xafO\xf7\x12\xea'\xd1\xbe\x10\xabB\xac\x8f$\xa9\x7f\x84\x82\xab(\xb8\xaa\x97\xa4~\x8f\x10\x9fG\x90\xbf\x83\x82\xabv\xf5mD\xf85\x92\xf84\xc2\xfd\x19\x14\\\xb5\x8b\xff\"\xc4w\x11\xe1\xb7\xa0\xe0*\n\xae\xa2\xe0*\n\xaej)\xb8\xaa\x17L\xe8\x10\x05W\xd9~\xa3\xe0*\xe33\x14\\E\xc1U\x16\xa1\xe0*\n\xae\xa2\xe0*\n\xae\x1aH\x8a@\x17\n\xae\x12B\xc1U_Np\xd5\xf0\x80G\xc1U\x14\\E\xc1U\x14\\\x15gE\n\xae\xa2\xe0\xaa?ip\xd5m]m\xd6'w\xdf\xc8\xff\xb8\xce\xcbEu\xf2Q\xfd\xf7\xdc\x15Y\xf53\x7f\xe6\xbc\\T\xa2\x849k@\xbc\x05\\C\xcfLU\x7f\xeb2\xe0\x8e\xc2\xa3:\x15\x0ft\xd5\x0e4(\x8a\xd7\xc9C\x18\x13\xd5Vsgo\x1a\xcd\x12\x13fHJ\x11\xcb\xe7\xfb\xe33\x8d+6\xd7\xd5\x92D\x088\x7f\xaeg\x1cK\xad\xf8\x89o\x95\x1b\x18v\x80)\xdf\x98\xee\xc7\x15u!K39\xdfMB\x97D)\x1e6\xf2YSiV\xac\xcd\xe6Y\x9b\xa5(\x90\x15\x1a\xd1\x1f\x11\x9b\x93\xf2~\x00@t\xbf\xf0\x05\xaem\xb3\xd9R\xc2\xa3v\xa0\xd3jW\x05\x1cFV\x04\xd1\xf2>&\x9b*\x80\x9e\xf1\xe5\xd4\x98\xcd\xde\x0d\x91\xce\xack\x91\x15?A\xd5\xcd2_[\xd45m\xbd\x99\xb5\x9bZ\xae\x83\xb6\xc3\xc8{\xb9O\xadY\xf6\x0e\xd8\x87\xbc\x11\x11x|tTMV4\xc7\xf0\xdb\x92\x95|\xc6\x16vW\xdf\xb4\xa9b\x02\xad\xcb\x1bU\xe0\xf9\x91\xed\xa3\xd5P\x9dhTq\x14\x14\x8c\xb1Uu\xc7\xeb\xbe\xcc\x9b\x81A,z\xf2rV\x0b\xc8OD\xd4\xcd\xc5\x96\xc7\xf6\xcdY\xb6iX_\xb3~\x16\xad\x8a9\xab\xf5\xb7T\xc7\xcf\xf5\x84k\xa5\\.\xb2\xdc\xc4\xb4\x15+\xfa\xb5\xb4DdO\x1a\x0d\x89\xa1>=T\x9b\xcdj4\x0f\x9b)\xcd\xc6\xbeWwQ\x08\xa4\xc4\x14\xcb\x93\x08m\xcc3\xe8\n}W V\x88\xfaU,k\xb8\x8c\xa8\xb7\x02\x9bW\xae\xd0\xaf\xed\x95u\x93\x88\xa4\xec\xa1\xba\x08Z\x11\x8eS\xa4\xd5q{\xd9k)H.V\x02\x86\x14d-qd\x0c)!\x16\xd1E\xf4\xd32\xd4\x08\xb5\xf32\x06\nc]\xaf\x83\x85\xa2\xdfG\xe9\xb3G!\x81\xa1e\xbe\x86\x1b\xd6\xbeg\x03\xb2HV\x9a\x0fU\x99\xb5\x89\x0cS\x91\xe2\xa1v\xe3`L\x89\x93\xde[\x99\xd5^\xcf\x90S\xb5\xbb\xe4\xc3k\xc5\x91|\xc0'V\xb5\xa6|x\x94\x0f\xef\xcb\xc9\x877\xdc):x[\x93'&\x9a\x88\xd0\xd5\x0b\x11\xba\x88\xd0\xd5\x0b\x11\xba\x88\xd0\xd5\x0b\x11\xbaZ\"t\x99\x85\x08]Z\x88\xd0E\x84.\"t!wID\xe8\xea\x84\x08]C!B\x17\x11\xba\x0cB\x84.\xe33D\xe8\"B\x97E\x88\xd0E\x84.\"t\x11\xa1k )\xc85D\xe8\x12B\x84.\"t\xc5\x10`\xf6E\xe8\xa2Dy\xb1Y\xc8(Q\xde\x1e\x8d\xeb\xef\xa3\x94(/\x85\x15)Q\x1e%\xca\xfb2\x12\xe5\xad\xab\x82O\x82\xcd\xf5\xcd\xfd\xb5\xc8)\xd8\x1d+\xa0*\x9f\xcc\x96\x99%R4/\xa5\xb1x7\xe7\xdd2\xf3@\xb4\xfe\xa2\x8dY\x10]\x02\x9f D\xdc&(V8\xbd\x7f3\xa82\"\xe0\x94\xd1G\x08e\xf4\xe9\x7f\xc2\x1a\x932\xfa\xf4\x19}&\xdc\xaaIf\x1f\xd3k'\xa6\xf7(\xb3\xcf@(\xb3O\x1a\xceQ(\x8dC\xfb\xbe\xad\n\xb1\x14\x8e\xa4\xf4\x0d\xca\xecC\x99}zIJ\xcb\x08\xa1d\x04\xd11(\xb3\xcf\xae\xd4\x8b\x08\xdaE\x12\xcaE8\xdd\x822\xfb\xecB\xaf\x08\xa1VD\xd0*(\xb3\x0fe\xf6\xa1\xcc>XZDRJD\x0c\x1d\x822\xfb\xd8\x1e\xf3\xd2\x1e\x02(\x0f\x98\xbc5!T\x07\xca\xecC\x99}0\xb4\x05\xca\xec#d\x17j\x02e\xf61i\xf2\xd2\x0fb\xa9\x07\xd6\xb5\x812\xfbl\x0be\xf6\x89\xa0\x0c\xf8\xe9\x02\xa1T\x81\x00\x9a@0E \x8c\x1e@\x99}\xc2(\x00\x94\xd9\xa7\x13\xca\xec\xa3D\xa7^\xc8\x06\x91\xfb\xe0\x0e\x86\x1e&\x05\x18\x87Bo\xa3\xd1;\xa7\xf7\xa1,>q)R(\x8b\xcf\x1e\x8d\xeb\xcf?CY|RX\x91\xb2\xf8P\x16\x9f//\x8b\x8f\xfc \x7f\xb3hO\xf5\x10\xfft\xe4\xf3\x91\x7f\xe9o_s\xa5\xf2\x11\xff|\xa0\xebN\xa9|:\xf1\x110(\x95OK\xa9|L\x82\x81e\x80R\xf9P*\x9f-\xf1M9\x80\xe0}\x01\xae)!\xa0\xa5 \x82\x03\xe6T\xd6R*\x1fJ\xe5\x13\xc0\x13\x83`\xae\x98S\x15\xa5\xf2\xa1T>\xb1|2\x88\xe3\x94A*^\x19Dq\xcb\xdc\xc3\x81R\xf9\x84q\xcd \x90o\x06q\x9c3\xdf\x14\x8c\xe3\x9dAZ\xee\x19\x04\xf0\xcf \x9c\x83\x06\x11<4\xc4\x94I\xa9|\xa4\x04s\xd4\x9c\xda(\x95\x0f\xa5\xf2\x99H\x1a.\x1b\xe0)Y\x80\xe1\xb4A\x18\xaf\x0d|D\x94H~\x1b \xf4R*\x1f\x87D\xf1\xdf\x80R\xf9(\x89\xe2\xc6A\x10?\x0e(\x95\x0f\x867\x07\xfb\xe0\xce\x01\xa6\x8c\x94\xca'-\xaf\x0e\xbc\xdc:\x88\xe5\xd7Y\xb5Q*\x1f\x1c\x1f\xcf\xaa\x8dR\xf9 yz\x10\xcc\xd5\x03J\xe5c\x94\x18\x1e\x9fU\x19\xa5\xf2\xd1B\xa9|\x0cB\xa9|\xbe\xf8T>Y\xd3T\xb3\\\x9c\xee\xc5bg\x1ed\xdd\x06\xcf\x08\xd1Sf\x1f!\x94\xd9\xa7\xff kL\xca\xecc\xca\xec#\xfe\x19\x91\xd9G\xd2\xbd(\xb3O/\x94\xd9'\x0d\x05)\x94\xd5\xa1]\xe1V\x85XFGR6\x07e\xf6\xa1\xcc>\xbd$ei\x8404\x82\xd8\x19\x94\xd9gW&F\x04\x0b# \x03#\x9c}A\x99}va[\x840-\"X\x16\x94\xd9\x872\xfbPf\x1f,K\")C\"\x86\x1dA\x99}l\x8fyY\x10\x01\x0c\x08L\xde\x9a\x10\xe6\x03e\xf6\xa1\xcc>\x18\x16\x03e\xf6\x11\xb2\x0bS\x812\xfb\x984y\xd9\x08\xb1L\x04\xeb\xda@\x99}\xb6\x852\xfbD0\x08\xfc\xec\x81P\xe6@\x00k \x981\x10\xc6\x16\xa0\xcc>a\x8c\x00\xca\xec\xd3 e\xf6Q\xa2\xd31\xe88\xec\x81\x92\x888m\x05O?l\xb61\xe1\x9ds\xfc\x84\xa4\x04\xa1\xe4?\xe07#%\xff\xd9\xa3q\xfd}\x94\x92\xff\xa4\xb0\"%\xff\xa1\xe4?_P\xf2\x9f\xfb\xeb\xbc\\T'\x1fUb\x12W\xd6\x9f\x81\xe5\xa6\x9c4C\xee\x9f{\xc9\xfc\xea\xa2y\xb73\xa0t\xdaLD4K\x8e \xf19\xf5\xc8\xc1\xa6\x07\xe2\x15\xb7q7\xa28e\xce\xdc>^\xf0!q^\x1fwV\x1foi\x10\x9c\xac\x88}\xa2\xa9\xa0\x8el>\xdeR\xa6\xce\xe4\xe3\xce\xe3\x13V\x1c+4\x942\x83\x8f\xaf\x178\xb3\xf7x\xeb\x13\xda \xacUN\x91\xb5'.gO\xb2\x8c=\xa8|=\xce\xc9\x03\xbc\x13\x08 x\\\x88f\x03t\xbb@,\x9b\xcb\xa1/(CORF\x97\x97\xd3\x95\x9a\xd5\x85\xe7u%bv\xc5q\xbb\x1c\xea\x02s\xf2\xec\xc8\xefJ\xcd\xf0\n\xe4x%fy\x85\xf1\xbc\x02\x99^\xae>\x1c\x91\x85')\xdb\x0b\xc5\xf7J\xc8\xf8\xda\x95\xf3\x15\xc5\xfaJ\xc4\xfb\x8aa~9\x94\xa1\xb3\xee\xec\x81\xfd\xb5?\xfe\xd7^\x18`a\x1c\xb0\xe4,0,\x0f,)\x13\x0c\xcf\x05\x0bf\x83\x85\xf3\xc1\xbcS!.\xbf\xce\xce\x9c0on\x1d\xd4\x86\n\xc1\x0c\x0b\xd9u\x05\xb3\xc3\\\x8b :\xa3\x0e\xae| 9b!,\xb1\xc4<\xb18\xa6\x98\xab\x07\xa1\xb2\xe8D\xb2\xc5,\xdaZT\x06\x9d4\x8c14\xed \xc1\x1a\x0b\xe2\x8d\xf9\x12P\xc4p\xc7|:\xad\x18r\"\x06Y\xb81\xf1,2_\xdd\"\x98d\x91\\2\x17\x16\x9f\x8cO\x86f\x94\xe18eXV\x19\xc2\xca\xe1\xcc\xb2\x10n\x99;'N\x12~Y \xc3l7\x8e\x99\xcf\xa0\x01<\xb3=0\xcd\xbc\xa5\xb3\xf6\xf4t|3\x04\xe3,\x9esfQ\xd7zs\xdf$\xe5\x9d\xf9\x98g\x91\xdc3\x8b.\x7f\xce\x1b\x04\xff\xcc\x9d\xef\xc6\x95\xed&5\x0b-9\x0f\xcd\xceDK\xc9E\xc3\xb0\xd1\xc2\xf9hA\x8c\xb4\x08NZ(+\xcd\x93\xc1\xc6]:,O\x08\xcbM\x8b`\xa7\x05\xf2\xd3\x1c\xd5\x8d\xe1\xa8YT!r\xd6\xc4\xf0\xd4\x1c]\xde\x9f\xaf&!W\xcd\x9b\xabf\x1f|\xb5T}1\x80\xb3\x16\xc2Z3g\xa2\xf1\xe5\xa1\xf1\x9e\xe1q9hp\x07\xe4\xc4\xf9g\x9c\xd9g\xfc%\n\xcf<3\xcc2cP\xe8\xcb;\x13\x94\xc3B\x94i\x92\xbeb+a\x85.\xb8|j\xa2\xb0\xa5\xcc\x15\x94\xb9\x822W\xf4B\x99+(sE/I1\xcd\x10D3\x08\xcf\xa4\xcc\x15\xbb\xa2\x98\x11\x18f\x12\x043\x1c\xbf\xa4\xcc\x15\xbb\xe0\x96!\xa8eb\xcc\x12\x87X&\xc4+\xb1h\xa5\xe1 B\x99+\xc6\x82\xc0'\xb1\xbb\xa4`l\x922W\xa0\x10\xc9\x18<\x922W\xd8\x1e\xf3b\x90\x01\x08$&/C\x08\xfaH\x99+(s\x05\x06c\xa4\xcc\x15BvA\x15)s\x85I\x93\x17G\x8cE\x11\xadk\x03e\xae\xd8\x16\xca\\\x11\x81\x16\xfa\xb1\xc2P\xa40\x00'\x0cF \xc30B\xca\\\x11\x86\nR\xe6\x8aN\xf6\x81\x04\xa6\xe8s\x01( \x1e\x03\xc4d\xaeP\x91}\x03\x1d\x81\x11\x94\xed${\xc54\xe8y\x87\xac\x15\xb6\x18_s\x88\xafz\xd4\x14\xcc+ \xb2\xa6\x0b\xdc\xcd\x8aB\x16YPQ\x9b6k\xbb\xe3\xf4\xa8\xf2\x0f\x8dq\xcc\xffz\xfc\xcdC\xf5\xf4v\xdc\xae6\xe4\xc1\x86\xeb\x0eM5\x94\xf63@V\xb6\xb0\xda O\x8d#\xaarb\xe2\x98\xb0Zg`-\xaa\x9cq\xc1\xb5\x0f\x1b{x\xad/\xc06\xbcX\x0e/V\xca0[p\xd9\xd9\x19c\x8b\xaa\x12\xaaG\xf8\xae\xc0\x08\x8c\xb2\x85\x15_\xfa\xeaf\x99\xaf\xad\nq!\xb6:\xc8V8\xae\x81}\xc8\x9bv\x18L\xdb\x1c\xc3oKV\xb2;V\x8bvP\xdf\xb5+c\xc2\xd9\x9a7\xaa\xd8\xf3#\xfb\x87\xab\xa1J\xd1\xd0b-\x17\x90\xff\xaa\xba\xe36X\xe6\xcd\xc00VMy9\xab\x85\xd7\x96o\x05\xca\xb9$\x15Z\x9f\x9ee\xfc\x14\xd3\xd5\xb0\x0f\xcb\xaa\x8a9\xab\xf5\xf7\xd4\xc0\xc8u\xc2\x05\xeb\xcd-\x00\x8b,7s\x18E\xb2\x8fki\x93\x1dz\xd8h\xd0\x0cu\xea!\xddlV\xe35Q5\xd3C\xd5\x1e\x16\xaf\xb5\x8f\x96\x83*\x1c\x8e\x9a\x83\x1f\xf9\xe1\xf4\x9c\xf7\x0e\x840\xc5\xbdP\x11\xbc\x1c+L\x9b\xd9\xe6\xa2\xf1\x0e\xe0\xad\\/%4Y\x14}\xcb6\xa0\x8a\xd1o!\x1eNt\xd1\x1dNBFU\xa5;\x9cP\xc6\xfc\xe2\xeep\xda\xea\x17=\xff\xadq\xd0\xde\x9a\xe9\xcdL\x13-\xb6\x0d\xfc\x83\xbe\x10\xc4\x83\x1b \xf1\xe0p\x0b\x12\x10\x0f\x8exp\xd6'\x89\x07'\x84xp\xdbB<8\xe2\xc1\xd9\x84xp\xc4\x83\x13B<8\xe2\xc1\x11\x0f\x8expR\x88\x07G<8\xe2\xc1\x11\x0f\xce&\xc4\x83#\x1e\x1c\xf1\xe0\x88\x077\x90\x14\x9c$\xe2\xc1 !\x1e\xdc\x97\xc0\x83\xa3\xeb\x8f\xc2\xee\x96\xa1\xeb\x8f\xf6h\\\xff\xc5=t\xfdQ\n+\xd2\xf5Gt\xfd\xd1\x17q\xfdQs}s\x7f-\x08\xa5'\x1f\xc5\xff\xb9\xae>\x92\xcc\x8a\x1f\xefO\x05Yut\xdbQ\xc3\x07\xae$\xb1*\xd6\xaand\x03\x15Zix\xa0\xebG\x8ch/y\x81\x18\xd1\xc4\x88\xee\x84\x18\xd1\xc4\x88&F\xf4\x0e\x85#FtzF\xb4\xda\x06\x88\x8dR\xcf\x85\x16\xb7\x1f\n\xe4\x93\xff\xb1\xf33\x1b'l\xa2E\x0b!Zt\xff\x13\xd6\x98_\x1c-\xda\x91\x16To\xaf]\xec\xe8\xc9#\x13]\x13\xeat_\x00\xa2D\x8f\x84(\xd1\xbe^\xd9\x0bQ\xa2\x89\x12m\x16\xa2D\x0b!J\xf4\xb6\x10%\x9a(\xd16!J4Q\xa2\x85\x10%\x9a(\xd1D\x89&J\xb4\x14\xa2D\x13%\x9a(\xd1D\x89\xb6 Q\xa2\x89\x12M\x94h\xa2D\x0f$\x05=\x95(\xd1B\x88\x12\xfd%P\xa2\xb3\x01{\x0bBy;\x99\x9d\xb5\xb3cVP\xe2l\xc7\x12b\x89\xb3\xbdG\xe3\xfa\xd9\xc6\xc4\xd9NaE\xe2l\x13g\xfb\xcb\xe1lK\xb6\xdc\xc9G\xb5\xb2bh\xdb\xbfH\xd2\xe26o[\xb1\x191\xc4m\xa9\xe3\x81\xae%1\xb7\xbd\x1c\x0bbn\x13s\xbb\x13bn\x13s\x9b\x98\xdb;\x14\x8e\x98\xdb\x9f\x99\xb9=lq\"p\x1b\x1f \x02w\xff\x13\xd6\x98D\xe0\x1e\x11\xb8\xe56\xdb\xcd\xe0V\xdb\xf9\xda\xcc\xe1n\x89\xbaM\xd4\xed\x80\x85\x9e\xa8\xdbD\xddVB\xd4m\xa2n\x13u\x9b\xa8\xdbD\xdd&\xea6Q\xb7\xb1\xbb$\xa2nwB\xd4\xed\xa1\x10u\x9b\xa8\xdb\x06!\xea\xb6\xf1\x19\xa2n\x13u\xdb\"D\xdd&\xea6Q\xb7\x89\xba=\x90\x144Z\xa2n\x0b!\xea\xf6\x97A\xdd\x164\x9c\x81\x8e uG\xb2t\x14\xe04\x04{\xa7\xf40 \xc26\x11\xb6\x89\xb0-\x84\x08\xdb\xfd\x9f\xf1V$\xc26\x11\xb6\xff\xec\x84m\xcd\xfb;\xf9\xa8\xff\xeb:\x9f\xbb\xc8\xda\xaf\xd5c\x1dM;\xeb\xb8\x83=u\xb0\xfbK>\xb7\xb3\xb5\xb5\xa6\x07\xba\x86\x07J\xd6\xd6\x95\xf1q\xc6\xba:+\xb2X\xf7\xefrQ%\xe5\x87\xd9\x18\xdb^<\x01Afrs\xb5\xf3\xb9\x9evt\xedL\xa7=\xd1\xb9\xae\xd7U\x91\xcf\xee\xaf\xd5\xae,\xb2\xc4\x18\x08\xc1\xfcA\x07\xbf[\xbb\x9f\x15lk9\x86\xf32o\xf3\xac\xe8Y\xbf\x97\xae\xa9\x03\xd1\x99Y\xb9Y\xd9\xa6\xfa'\xf0\xfa\xe2\xd5\xebW\x97\xa7/\xae/\xafN\xaf\xde\\^\xbfyy\xf9\xfa\xec\xd9\xf9O\xe7g\xcf\xd1\xef\\\xbe\xf9\xf1\x97\xf3\xab\xab\x807N\x9f=;{\x1d\xf2\xc2\xc5\xd9\x7f;{\x16\xf2\x82\xeaY\xe8\xe7\x7f;\xbf\xfa\xfb\xf3\x8b\xd3\xdf^Zz\x87\xa47\x07\x9ak\x91\x97Yq\xddfEq\x7f-\xcf \xbbt\xbemmc\x07~\xb3Y\xc9\x04\x08E\xa1\x02e\xd8\x1c\xee\xaa\xd6\xc2S\x00\xc9\x06Z\xe6\x8dm\xd4u\x13\x8d@k\xf8y\x9ekS\x9cA}l\x94^\x81\xac\x1d\xccF6ocV\xce\xc5\xd8\xb4~\xafZo\n\xc1zPX\x13\xafj^\xde\x1eq\xf5w\x95\x9c\xa4X\x9dWs`\xa5\x88\xac\xb2\xd2q\xbb\xa2\xb3\x0fl\xb6iE\xa1,\x8f\n\x17\xb1\x88\x08[f\xeb5+\x1b\x893\xb8F\x9b\x95T\xed\xdb\xf9s\xb9g\xcd\xb5\xd8 \xd9\x11c\xef\x98\x86i\xa7\xe9\x94\xeams\xd7\x01T0\xd5=kd_\xb0\xe1\x9f\xd9M\xc3\xbbR\xfa\xa2\x8d\x14\xdb\x8a\xa7\x1e\xb2\xec\x0d\xb9\x94U\xfa\xa2i\x9d\xb6R\x95\x95\xdbfeu\xcd\xd7\xb2\xeb;\xd6\xee\xa7t\x13\xed\xb6r\xf2'Le\x94c\xe6Z\x8e\x99kV\xeez\xeeL\xb1\xb3\xde*S\xe7\xba\xecv\xd87\x82\x0b\xaa\xb0\x1b\xf9\xbcE\xd9Jq\xe2\xe6U9\xf5Z\xf6\xf2\xa6\x14\x8e\xe2l\xe0n(\xe0\x97\xe6\xf6\xec\x03\x9b\x89\x9dPV\x14l\xae\xbf\xfa\xa8\xad\xd4\xa4a[\xb8\x87\x1b\xfeee\x83\xf2AN_\xc2\x1b\xd5\xbb9\xd4G\xc4\xdeo:\xa15\x8fm\x13\xa7\x9e\x08;|\x8c\xd7\xd7\xf6Y\xe1\x99\xcc\xf9\x06-/\xdb#\xcd\x0e\x80\xb7\xdb\xcb\xc7[\xfe\xdb[\xb9\xa7yk]tX1\xef\xb6q\xd6o\xcefU=\xcf\xcb\xdb\xe2\x1e6\xeb\xb9-FQ\xda\xb5\xaa\x13,\x86\x13U\xba\x13\x89Jj\xaf\xecdo\xe5\xd4$W\xb4\xe9\xde\xebe\xd5^l,\xe1\xe3\xdeQ\x83\xdcx\x9d\xfd\xbfg\xcf\xde\\\xbd\xba\xb8\xbe8\xbb|\xf3\xe2*l\x076}\xf9\xe5\xab\xab\xeb\x8b7\xe6M\x8c\xf3\xc5\xcb7\xcf\x9e\x9d]^\x86\xbf\xf8\xd3\xe9\xf9\x8b7\x17g\x96\xa6\x9cn\x9b\xc2*\xab\x90\xd4\xfd99\x1c\x0b9f)\xf7GI\x01\xa6\xa3H\xc1\xf5{)\xa1\x11SNe\xad\x916k\xc7\xa7!u\xe4\x14\xf8\xa3\xa7 \"\x82\xca]\x81\xac]\xa2\xa3\xa8\xb8\xb4)\"\xa9 2\x9a\xca\xa9\x90\x1b\x17\x1dQ\x05\xbbGUApd\x95S\x95\x8a\xf8\x08\x8a\xae\x82\xd4\x11V\x10\x18e\x05\xa1\x91V\xee\x9e\xddEaa\xa3\xad u\xc4\x15\xe0\xa2\xae e\xe4\x15\xec\x1c}\x05q\x11X\x90*\n\x0b\xa2\"\xb1\xdc\xc3Acp\xbeq\xb3\x97\x88,\xd8cT\x16\xec'2\x0b\x02\xa3\xb3 .B\xcb7\x05\xb7\xa8(-H\x1b\xa9\x05\x01\xd1Z\x00\xc1\x11[\x10\x11\xb5\x85\x982\x1f#\"\xb7 E\xf4\x16\xf8\"\xb8\x00\xbf=CDrA\xe0..8\xa2\xcb\xa9MD{!\xa2\xba \xa0\x94 \xa3\xbb (\xc2\x0bRGyAd\xa4\x97\xbb_5\xfeh/\x88\x8f\xf8\xb2\xea\xe3_\xf4E}A\xb2\xc8/\xc0\x070\x01&\x02\x0c\xc2\xa2\xc0\xc0\x17\xb6\x11\x19\x0d\x06\x08\xbd\x0efx\xa2\xc80\x882.>B\x0c\x10\xb5\x8c\x88\x14\x83\xd8h1p[5]\xd4\x18\xe0#\xc7\x00\x19=\x06\xe8\x082\xc0Y=<\x92\x0c\x82\xa2\xc9\xc0\x19Q\x06\xa9\xa2\xca 4\xb2\x0cv\x8c.\x03\x84y\x03\xa2\xcc`\x1f\x91f\x80)\xa3c$\xa4\x8b:\x03L\xe4\x19\xec\x10}fU\xd8\n\xe6\x99=\x02\x0dRG\xa1\x817\x12\x0db\xa3\xd1\xac\xda\xe4\x19\xd5}\\GD\xa5\x813x\x06\x9c\xd1i\x10\x15\xa1fU\xe5\x8c\\\x83\xd8\xe85\xab6\xb9\x0ftx\xcd\xd2E\xb1\x01*\x92\x0d\"\xa2\xd9 ,\xa2\x0db\xa2\xda 8\xb2\x0d<\xab\xad'\xda\x08\x02\"\x8e\xb0Qn\x10\x13\xe9\x06\xa1\xd1n\xe0\xaexL\xd4\x9bU\xd9 \xa6\x0c;dp\xd1o\xce\x01Q\xde\xba#\xe0 m\x14\x1c\xf8\"\xe1\xc0\x1d\x0dg}'6J\x0e\x12\xf6\xdd\x80h9\x08\x8a\x98\x83A\xd4\xdcXp'p\x0d\x80I\x0ej\x917\xc2\xad\xfd\xb6\x99\xbf;\xfe\xa5\xb9}\xab|\xe5]\xbf\x91\xb0\xf5\xdc\x99v\xb3\x07\xafE\x1a\xd0\x89\xe7dT,\x11u\xa0Y\xfe\xe6\\\x91]4\x01]\xe9\xdf\x0b\xe5\x85\xa4\xbc\x90\xbdP^H\xca\x0b\xd9KR\xd42\x04\xb1\x0cB+)/\xe4\xae\xc8d\x04*\x99\x04\x91\x0cG#)/\xe4.\xe8c\x08\xf2\x18\x81:R^H\xca\x0bIy!\xb1\xa8aR\xc40\x06-\xa4\xbc\x90\xb6\xc7\xbc\xa8`\x00\"\x88\xc9z\x18\x82\x04R^H\xca\x0b\x89A\xf5(/\xa4\x90]\x90;\xca\x0bi\xd2\xe4E\xe7b\x919\xeb\xda@y!\xb7\x85\xf2BF j~4-\x14I\x0b@\xd1\x82\x11\xb40\xf4\x8c\xf2B\x86!d\x94\x17\xb2\x13\xca\x0b\xa9\xa4\xcb\xea\xd5g\xa7\x1a\xe81&`\xba\xb6\\1\xdb\xc7]\x0e\xa7\xa2\x9dSDZ\xd2}\x84\xa7\xe0j\xc69\xb8ND\xa8\xa5#\x13\xd7\xe0\xe4|\xc5\x1f\xbd\x90Q\x8b25\x99\x9a{\x85O\xba\x0fg\xcc\x86\x89\"\xc6\xd1\x8d0\x8c\xe3oZ>`\xf2r\x1cd\xca\xc7\x16+\xa5\x03J^\xe87\xabV\xebM\xcb\xf4\x18\x10\xee'\xf9\xd1\x81\xaa\xace\x83\xe0y9r\xa4\x8b\xa5\xac\x84\xe7A\x04]\x1e\xc3+E\x82\x17n\x9ceV\xce\x8f\xf8>mRF\xf9\xf0@\x9f(R\xdeB\x93\xaf\xd6\xa2\xae}\xedM!\xab\xb2<\xd0\xb4U\xddgo\xeb\x94\xf5\x1fj\x1bV,t?\xd9\xce]60\xb8z\xe6`\xd3\x97\x89\xeao\x9f\"Fe\x91\xfddx\xd5e-/\x1b\xe4;5\xfe\xdb\xf6\xe2m- xA@O\x06\x03\xaf\xfb+a\xe6\x02D\xd6\x82\xb0\xe2$\xc8V\xe0\xceT\x10V\x9c]2\x14`\xb3\x13\x04\x97(:+\xc16\xbb`0\x0e\xcd\x04\x83\xf1\xccH\x1c\x03-\xc41 \x8eA/\xc41 \x8eA/\xc41h\x89c`\x16\xe2\x18h!\x8e\x01q\x0c\x88c\x80\xdc%\x11\xc7\xa0\x13\xe2\x18\x0c\x858\x06\xc410\x08q\x0c\x8c\xcf\x10\xc7\x808\x06\x16!\x8e\x01q\x0c\x88c@\x1c\x83\x81\xa4\xc0{\x89c \x848\x06\xc41\xf0p\x0c\xe4\xd5H\x07\xcf1\xb8\xbe\xb9\xbf\x1e\xde*r\xf2Q]\xb3\xe3\xba\xf3kp~\xd6\x01\xbb\xcd\x8f\xf7\xf2\x12/y!\x88\xbe\x0c\xac\xbf\x03\xa4\xcbC\xb8}\x01S\xa7L\xde\x013\xberi\x84\xb5\x9b\xbf\xa6\x9e\x11\x08\x10@\xf8\x97\xb2#\xed_J\x18\xf9\xdfW\x8f\xc8\x0b\xd2\xc2\x03\x01<\xea\xf8 +$\x1c@JPP\x80\xaf\xbfw!\x03\xd8\xd0\x00)\xc1\x01\x02\xbe\xf1\xd9`\xc2\x04\xa4$\x0b\x16\xd0\xeav \x19\x90\x12\x118 %I\xf8\x80\x94\xf0 \x02\xdf0\xc1_\x9c\xb6\x97\x80\x02)\xfb\n+\x90\xb2\x87\xe0\x02)!!\x06R\xd0\x81\x06\xbe\xa14\x08C\x10}\xd9\x1fn %a\xd0\x81\x14l\xe8\x81\x946,\x00AJh\x18\x82oZ\x0b\xbaN-AH\x82\x14\xef\x95j\x01\x9b=D\x90\x82\x94\xb0]ap\xc0\x82\xaf\x97\x06\\\xae\x16R\xd6\x84!\x0cR\xf0\x81\x0cR\x92\x863H\x89 j\xf0\xf56\xe4Ekq\x01\x0enu-\xf2\xb2\xb5T\xc1\x0eR\x90\xac})\xde\xc0\x07)\x01\xe1\x0fR\xbc\xf7\x17E\x85BH\xf1\xebv^\\\x92(8BJ\x8c\xb1\xf1\x81\x12R\xfc\xf5\x8d\x08\x9a\x90\x12\x15:!\xc5}9L\xb20\n)\xc8`\n)\x98\x90\x8a\xeeID`\x85\x14T+\x84\x07YH\xc1\x87ZHq_\xcd\x96(\xecBJP\xf0\x85\x94]B0\xa4\xf8\x8d\x1d\x10\x8e!%yP\x86\x14DI\x9d#%]\x98\x86\x14o\xb0\x86\x94\x98\x90\x0d\x87:\xedK\xf7]\xd9\x16\x17\xbe\xe1P\xd7j\x9f\x95\xf3\xda\xb6\xc8P\x0e\x87>\xcc\xd5m\xa8\xb0\x0e)\xee\x1b\xa0|\x17\xb8\xc5\x04z8\x94y/q\x8b\x0c\x04q\xe8\xf3^\xe4\x962(D\x8a?4DJh\x80\x88\x94\x800\x11)\xc1\xc1\"\xa3\xd7\x90!#R<\xd7\xba\xf9/\xc7\xc2\x13\xfa\xb1A$Jkh(\x89z-$\xa0D\x8a\xd3\x041\xc1%\x0eu\xa8K\xde\xe2\x02M<\xc3\x05s\xd1[\xd2\xa0\x13\xa5\xd0w\xd9[\xdcuo\xbb]\xf8\x96\xb2W\x07\x84\xa9(\xc5\xe8`\x15)\xb6\xcd\x0c\xd6;\xa0!@\x08\xba\xfc\xcd\xaan\x9a\x8b\xd1t\xfd\x9b\x14L\x01\xbb\x8b\xdft\xd2\xc1LG\x83t\x04B~PZ\xf1N^\xcb\xc8\x1a\xf9\xbb\xcd\xc9/)\xccC\xc2\xb3\xf11\xde\xc7\xb2Q\xe0 \xef\xcas6\xcb\xe7\x0c6k\xeb)\xe4\xb4\xaf\xf8\xac*\x9b\xbci%\x15M\xe0\x08v\xb3\x1au\x0d\xee\xd9\x1b\xe3\xee\xc6\xa7\xa5\x9d\xf96\x8cO9\xfc\xff\x9bj\xc5:(\xa3g\xf4gMS\xcdr\xe1\x98\xd0\x14U\xb3B+\x9d\xdf\x18\xea\xd4\xb3\xe8\xfb\xbf\x08\xfd\xb7\xf9\x1d+\x8d1<\xdd\x97\xb2\xdb\xbc\x14\xc3c\xfb\xf03\xfeV\xf7\xe0( \xe5\xe0\xcfj\xebf\x8a\xda\x91\xe2\xc4\xa3\xdd8t\xc9>\xb4\xd7\xef\x98!I&`\\\x91^\x17\xe4\xa8\xaa\xff\xd36`u)4C\x83\xff\xa7\xf2\xef\xf3\x1e `\x8d\xd7\xd9-\xbb\x90\x999\x8f\xe5\xef\x16e2=+W\xc3\xd5rC2XUM\x0bLx\xc7\x85c]\x90\x1b\xfb\xc5d\xdd\xde;/~\xac\x99\xe8\x08e\x05\xab\xaaf\x1aT1\x8d\xff\xb6j3\x0b\x98\x8f6\xa6\x83\xc7\xde\xe6m\xc1\x1c3\x9f\xf8\xbc\xb0\xa2\xf8\x8fr\xa3\xa7\x10\x9dLj\x00\x19\xd8\xea;4\xb4`\x9c]\x0be\xb6\xd5E\xc4P\xb0\xf6\x08\xf2\xb6\xe9)1\x9bRv\xe6\xb9\xf4\xd7\xbf\xcf\x9bq\xffp\xcf\x93\xa3\xdb3\xc7\x01p\x93T\x97\xa6\x17\xbb\x9b5\xc7qz\x94\x02\xb3\x17J\x81\x19\x10SE)0\xc3\x982\x94\x02sw.L\x18\x0b\x86R`\x9a\x15\xe2x.\xc1\x0c\x17J\x81\xb9\x0b\x87%\x9c\xbdB)0wa\xa9\x84\xf0S\xd0\xcc\x14J\x81I)0\xd1\xbb\xa4`F \xa5\xc0DqFb\xd8\"\x94\x02\xd3\xf6\x98\x97 \x12\xc0\x01\xc1$x\x0c\xe1}P\nLJ\x81\x89ajP\nL!\xbb\xf0/(\x05\xa6I\x93\x97U\x11\xc3\xa7\xa0\x14\x98CAp%(\x05f\x04\xdb\xc1\xcfs\x08e8\x04p\x1b\x82Y\x0da|\x06J\x81\x19\xc6R\xa0\x14\x98\x9dP\nL%:\x05\xa6J\x906\xd0\xe1:E\xfa\xb3\xe5\xb5]\x02\xae\xb5\x06\xa0d\xe2\x93 r\xd9A\xcb\xc3\xe9k\xe7\xb4\x99]b\xcf\x0eG>~\xc7\xeem\x95\x9b \xb3\n\x8a\xcd\xd4\xc2 /\x96\x94wFJtP\xc1_\x1dn+\x1c\\\xb7\x13O\x90\x00b\xf9d\xe6\xc3b_\xf1e\xbc*\xc5 \xb8Z,\x04\xad\xa0\x86qqa\xe0\xa0oX;\xb5\xd5\xef*\xfd\xa7\x96\xdeX\x8b\xach\xbc\xd6\xb2\xb8;\x0cF\x94\xe5Cv\x12U\x19a\xcar\xb3bu>\xd3\x7f\x13s\xc8,+y}\xa4\xafg\xc9Jm\xf8M\xd9\xb9\xd7&\x9bj\x99\x93Gd\xaf\xe8L(\x1dR\x9b\x86\x9b\xfa\x1d\x0b\xb4\xe7X\xfd\x9e\x8d;A\xb6\x0d\xe6-\xf2U\x8e\xb5\xaex\xb6\xcb\x17b\x01\xbc\xa5\xebu\xd8\x83\x15\x97\xa2\xbf\xbeT\xcaZ:Z\x86\x7f:_@\xc1\x16\xad&\x08(\xc6\x80\xde\n\x0b\xaf\xb1\x1c \xf2#\xdc\xce7\xf72\x91R\xb6^\x7fF+\x0ea\xfb\xfe}\x97-\x07o\x88\xdc$L\xd4\x8fO4\xc0\xff#/\xe7\xf9,kY\x87\x1f\xe9\xf4\x19\xfcACDy^\xce\x8a\xcd|\xb2\xd1\xcd\xe4W:\x00o\xd2b\x02\x0e\x1e\xf8\x95\xf9\x824b\xc1\x8c\x94\xbd9\x9f\xe6\x9c\x9bTA\x9c\x0dj\xd6(\xe0^\x0c\xaf~<\xf2!\xa73\\\xe5\xb7eUO\xbc\xf2z4\x8e?!-\xb3k\xc3\xdeTU\xc1\x06T.C\x03\xd6\xec\x8e\xd5\xa3W]\x8d\xa7\x9e\x9e6\\>\xa0}\xd4\xcc<\x12Fz\xf87X)\xf0\xca\xaa\x9e\xb3z\xea\x96\xbb\xcc\xcb\x19{\n2\xc1\xf2\x93f\xfe\x0e\xbe>\xfe\xee/I\xad\x81O\xeb|W\xb5\xec\xfa\xe6\xfeZ/\x9f\xd7\xfc\x0f\xf5\xe4\x1e\xe9\x8f\xe2\x8f\xae\xfc\xce\xbfV-\xfb\xb1\xe3\x9a\xf0\x7f\xd5]F\xe7L\xe6C\x93geI\x80\x13\xc0\xd0\\\xfc\xbd\x83\xb8Fi\x9b\x0d\xfa\x1ehC\x1ch\xcef^\x19\x0fIM\xd8A\xcd\xb4\xe2\xbf\x07,\xb8\x81\xc1\xb7m\xd3\x8b\xb5`\xe0\xa5t\x0c\xbe\xb0?j\x95\x91\x00\x08\xa6\xcb\xd4[O:ga\x80\xc8\x82nY\xbd\xf6\xec0-\xd6\xd6\xf9\x1e\xcc\xc5\x18}E>8l\xdd\xfaa\x03\xb3e\x95\xcf\x98\x06\x95]\xd5E\xd4\xca\x95\xdf\xe8 \xfc\xfa\xea\xea\xec\xfa\xd5\xeb\xab\xf3W/\xbd\xb9}\xa6\xcf\xff\xe3\xcc\x96\x82h\xfc\xdc\xe9\x8f\x97W\xa7\xe7\xb6LG\xe3g_\xbeB>&\x12L^\xffzve~\xa1\xcbd\x84\xaf\xa0;O\xb6\xd7\xce\x18\xd8+m\x86l\xde]L\x9d\xc2\x9b=\xda[\x17\\\xa6\xb9Q\x8d'\xc9\xa1\xdbQ\x1a\xb9\xf7\xf2\xfe~=\x7f\xd93?#\xa8\x89\x86I\xde|\x05\xb7}5\xd0B\xac\xc4^\x88\x95H\xac\xc4^\x88\x95H\xac\xc4^\x88\x95\xd8\x12+\xd1,\xc4J\xd4B\xacDb%\x12+\x11\xb9K\"Vb'\xc4J\x1c\n\xb1\x12\x89\x95h\x10b%\x1a\x9f!V\"\xb1\x12-B\xacDb%\x12+\x91X\x89\x03I\xc1\x10#V\xa2\x10b%~ \xac\xc4\x01\xf6=\xd0c\x04\xab\xb7/\xe6\x96x\xf5\xa7\xbd\x98\xbb/\xfa\xdd\x04\xee2c\xdb\x83\x9b\x93\xe4\xdf&@w\xc22\x87\x91K\x9a!\xbbd\xcc+\xf1\xf0I\x9a\x1e\xf2spI\xac\xf4\x91\xc1\xeb\x0ft%\x0f\x98:r \xf8\x9a\x97$\x12\xe4_\xc2\xdf%\xbb\x13U\xc4I\x16A\x158\x1da\xc4M\x19IN\x1aA\xd5\xcew1\x16\x9eYaz\xc3N\x1e \xa3\x8f\xa0 $\xc1\x14\x92(\x12\x89\x8fF\x82\xb2<\xc6W \x89\xc9$v: \x82P\x82\xaa\x15\x8eT\x82\xaf}\x18\xe3\xc4\xa3\xc6r-\xde\xa8,|}\x18^^\xac\x96\x15\x99\xda\x0f\x99\xddN^\x0f\xab3\xdb\xe9\xe4\x88w\xdd\xa5\xb1[\xb4\xbe\xa9>\xcam'\xc4\x15A\xd1\x0b\xe5\xb6\x03\xdc\xba\xfa%\xe5\xb6\x9bl\xf3\xec\xe4\xb1\xe1VR\x0f\x92\x89\xc2\x96\x88cD\x1cK\xb3\x98\x13q\x8c\x88cf!\xe2\x98\x10\"\x8em\x0b\x11\xc7\x888f\x13\"\x8e\x11qL\x08\x11\xc7\x888F\xc41\"\x8eI!\xe2\x18\x11\xc7\x888F\xc41\x9b\x10q\x8c\x88cD\x1c#\xe2\xd8@R\x90x\x888&\x84\x88cD\x1c;h\xe2\x18\xe5\xac\x0bK\x08F9\xeb\xf6h\\\x7f\x1f\xa5\x9cu)\xacH9\xeb(g\xdd\x97\x90\xb3N\xd0\x8aU\xae:Tv\xba\xe6\xc7{[Z:w\x1a:\xfd\xe2\x03])\"\x11\xfb\xb8\x16D\"&\x121\x91\x88\xa5\x10\x89\xd83\xbc\x89Dl!\x11\xdf\xdc+\x0e\xb1q\x9a \xf6\xb0\x10b\x0f\xf7?a\x8d\xf9\xc5\xb1\x87\x87daO\x9a\xc9n\x9bH\x19%{!bp\x9a\x05\x9a\x88\xc1D\x0c6\x0b\x11\x83\x85\x101x[\x88\x18L\xc4`\x9b\x101\x98\x88\xc1B\x88\x18L\xc4`\"\x06\x131X\n\x11\x83\x89\x18L\xc4`\"\x06\xdb\x84\x88\xc1D\x0c&b0\x11\x83\x07\x92\x82\xa4I\xc4`!D\x0c\xfe\x12\x88\xc1\x07\x97\x96\x91\x88\xbf\xb1\xacJ\"\xfe\xee\xd1\xb8~\xca*\x11\x7fSX\x91\x88\xbfD\xfc\xfd\xb3\x11\x7f\xf9\xf9\x93\x95Y9c'Y\xdb\xd6\xf9\xcd\xa6e'w\xdf\x9c\xa8Et\x9e\xb5\xd9\xc9G\xf5\x0f\x17\xff\xf7T>\xf2\xcf\x88\xbc\xa3\x0ba\xd2q2\xec\x1a\xd2O\x90\x86\xeb\x93\xbe%\x89\xea\xf3\xf9\xa9>{@\xc9\\g\x0c52l\xfdO\x9f%T\xa7\xbfa\xb3\xe5_\xbe\x9dr\xcc\xf5Cm\xc5\xa7F\xe9}Q\xf3\xdd@\xef\x8eg\x8f\xe8)\xbc9\xf9\xd8\xfdY80\x9c\xd3\xb8~R\x0d\xda\xa6\x8f\xe5\xd0\x7f\xa8J\xc8\x14k\xb6\x9f\x8a\xb8^\xa5k<\x8dO\xf5=\xd05?\xd0\xc9\\W3\xe9h\xb2tX\xc5\x04\xd5\x94\xe4\xc9\xb9Us\x83\x04G\xa2\xb3t#w\xe1b\xaa\x1fZ]\xcb.\x9c\xe5\x01\x87g\xf8\xf3d\x81!\xee2q\x97\xff\\\xdce\xcb\xdeg:u\xed\xb0\x03\xda\x9aUi\x1fD\xfb\xa0\xc3\xda\x07\x8d\xb6\x08\xb6n(\xf0\x0f}&\x19-\xfe|\x8a\x943_\xd2]\x0fy\\c\xddY\xe4q\xdd\xa3q\xfd\xbeB\xf2\xb8\xa6\xb0\"y\\\xffH\x1e\xd7\xc1\xe4C\xbeX!av\x8a8\xe2w\xff\xe8|\xb4'\xcd,+O>6\x9b\xc5\"\xff\xe0:\xea\x0f,|\xc9\xe7\xef\xee\xa0?8j\x0e\x8e\xfa\xeap\xfaH\x9dN\x1fKony\xaf\xa34\xdaY\xbf\xc6\xb72\x1c^\x12\x9bdI\xd4o#\xe7\x00\xff\xac\xfa\xfb\xa1\xfb\x03\x826\xa2\xeesR\xa6\x19%\x8a1\xa37\x166\xa7\xd2p\xabeb \x8bp\x8d\xa6\xc9oE$B\xf5\xc3\xb4\x02\xdd\xbb\x87\xb11\x16\x13\x81\xf1\x17\xd4\xb6v\xd4 \xae\xb6\xb6\xa1\x87\xc1\xbc\x1f\x97K|\xdd\\\xb0~\xd7-J\x11\xa9[T\xc1\xc6EDT\xcf\x97v\xe2\xf4\xea\xea\xe2\xfc\xc77Wg\xd7W\xffx}\xe6M\xc7`~\xe9\xcdy\xc0\xd3\x82\xf9\x83~\xfa\xf2\xea\xe2\xfc\xe5\xcf\xf8\xe7\xdf\\\x9c\xe3\x1f>\x7fy\x85\x7f\xf8\xa7\x17\xafN\x03\x1e\x7f}\xf1\xca\x9a \xc3\xf0\xf8\x8f\xff\xb8\xb2\xa6\xf0\xe8\x12g\x047\x96\xcf\xa7\x03\xbd\x13\xf7\xea~\xcdF\x19\x0e\xdaA\x94\x84py7-\xdf\x81\x98V\xde\xa1L\xfa\xafyh\xc8\xe9p\x87Q\xab*v5\x98[\xc7\xe7\xd6\xbc\x81\x9bjS\xcem\xbe<\xf6a\x9d\xcb\xd5\xeaz\x9e\xb5)&\x90\xa0D\x1cW\xb9\x8e]\xce\x86\xaev\xb1w\x17E3\x8eywsvM\xa9xl2\x12s\xcew\x88'r.i\xdaz3k7\xb5\xdc\xb7\xce{\xfcu,Y\xd3T\xb3\\\x84\x95\xc8\xe8\x9a\xd2\x00\xa5HQ%\xcazG\xf7\xb8\xf9\xa7\x8b\x1a\xb9\xb0GB.\xec\xfe'\xac1\xbf8\x17\xb68*\xf0\xfd4\xcaI\xad\xdc\xd2b\xdbO\x9eh\xf2D\x1f\x96'\x1a\x87\xc8\x0f\xb7!\xdd\xf2>\xf0A\xef\x85\xf6;:\xcb\x82\xc7g!\xdc\xe2\xe3|Pu\x9bg\x85\xc1s\xde\xb0\xac\x9e-\xc5\xf0\xbc\xc9\x1a\x91RN\x84\x93\x8f\xf4\xdd0~T\xccK\xb8x\xfe\xf2RYu/\xb5$W{\x98\x1f\x93\\\xed{4\xae\xdfIL\xae\xf6\x14V$W;\xb9\xda\x85\x90\xab\x1d\xedj\xff\xe8\xa3\xd3\x0dL\xdb\x9f\xbcc\x1c\xed|f~4\x00\xd6\xf9!\xea\xb1\xb78\xb9\xdc\xc9\xe5N.w%\xe4rG\x8dtr\xb9o\xe9\"\x97\xbb\xa5\xe7\x90\xcb\x1d\xbed\x97\xbb\x875\x8er\xc4\x9b4\xf4lq\xf2\xcd\x93o\x9e|\xf3\x83\x1f\x9c^k\xe2\xad\x933\x9d\x9c\xe9\xbb\xba\x81\xc9\x99\x9e\xc2\x8a\xe4L'g\xba\x10r\xa6\xbb\x9d\xe9\x0d*\xb9\x88\xc9\x8d\xdeD\xf9\xd1\x879\xe4\xf5^\x7f\xcb\xd5kv\x9e\xffQB\xd7\x83v\xc3DU'\xbf9\xf9\xcd\xc9o\xee}\x9c\xfc\xe6\xe47\x1f \xf9\xcd\xc9o>\x15\xf2\x9b\xf7?a\x8dI~\xf3\xb1\xdf\x98\xd2\xe9\xe6s\x17$\xd8\x9d\x88@\xbe\x065\x9bU\xf5\\B\x80S\x07\xdf\xf4f\x01\xd6\x1c\xfct\xbb\x97<\xd4\xd1\xbdkW\x0f\xf7\x9a\xd5\xab\xbci\xf2\xaa\xf4\xe80WK\x8a\xb5rR\xbc\x85\x00/~$\x80\x8cg\xcf\xce./\x11`\xc4\xe8\xf1_\\\xd8\xcb\xe0\xb9\x1f\xdf\\\xd8\xd1\xa2\xc1s\xcf\xcf^\xbf\xba\x8aJL-\xfaZ1\x87\x12c?2Y\xa0G\x03\xa0\xac\x9eTkq\x85\x99\xc2\x05l0\xe9\xf8\x03\xbc\xe7=\x1d\xfe\xa3\xe39\xaa\x0b\xef\xc5\xd1lV\xb3L\xdd\xca\xc9\xeb\xc5\xcf\xfe\x8b\xae\xaa\xa8\xef\xf0\x9e\xfbt\xf8\x0f\xc3w\xe6\xcc\xf8\x1d\xb1\xe9\x93F\x95\x1e\x88Y\x95\x97\xb0d\x85\xb8\xf5\xb4\xff\x15Wa54\x9eN\xfem(\x0e?\xcet\xcdY3\xd1)f\xe2(#\xee\xd3W?\xa8\xe3\xfe\x8a\xb5\x99\xb8\xed\xa6\x99U\xbc\xd7\xc8]*\xaaDz\x04>\x9d\xfe\xc1P\xa6\x9a\xad\xaa;\xb6U\xa8\xc6P\xaaE]\xad\xb6\x8aU\xb9Z\xab\xad\xb3\xb2Y\xb0Z\x1aX\xbc?\xd49\xb8\xa7\"+\x85\xa7\xde5\x0e\xe4\xc3\xd86\xe1\xf3\xca\xd3\xf1?\x0d\xb5\x17u\xd7\x17\xf6\xb1\xf9\x11,\xf2R\xdd\xac.V\xf26\xefm\x93\x97\xf2\"E\x98\xf1}BQ\xb094m\xd6\xb2c\xb8Z\xe6\xe6\xeb\xd2\xa5\xa8\x0dAV4j7\xde\x0cTH\xe5\xda\xe7#\xfe5\x9fP\x7f\xa72g\x05\xe3\xe3\x11e 1o>\x1d\xfd\xcb`\x87l>\xd7\xc5\x14\xb3K\xa3w2b\xcd\x07Uq\x0dB*\x83\x0c&1\\\xab\xe8\xa9\xf9\xe9\xf4\x0f\xc6)\xe2\xaez\xc7\xdb\xa6a\xe5\xbc\xdf0\xa9\x01;\x18\xc1r\\\xf0W\x16\xd9\x8c\xbf\x9e\xb5\x0c\xd8\x87\xd92+o\xad\xcc\x0e\x10\xd7\xde\xe6M\xb7Y\xcbo\x97m\xe7\x81Q3-\xef\x04%\\\x9c]^]\x9c?\xbbr\xae\xbb\xaa\x15M\x9f\xc3,\x1dr\xd1\xf8\x99[\xbeG\x87\xc5e\xa4UQ0q\x8b\xfet\xd5P\xc8\xb1Q\x9dv\xd4\x0b\x14\x9a\x15\xecV\xde\xb6?\x1etj_;.2\xe2\x10(\xcb\x8a:\x04\x9a^?\xf9E\x14C-\x93t\x0c\xa4c\xa0o\x93\x8c\x19@\\N\xe1\xcd\xc5\x8b\x93\x9a5\xd5\xa6\x9e\xe9`\x8ae\xd6\xc2\xa6\xcc\x7f\xdf\xb0\xe2\x1e\xf29+\xdb|\x91\x8f\x198V\x85b\xd3\xd0\xddYm\xdf^\x89+igU\x017\x1b\xbe\xd1\xd3\x8d&\xd7\x05\xcdX\\m\x9aV\xd3\x16\xc1\xb1\xe1+X\xd6\xb4\xf6oU%\x83\xafN\xbe\x82\xd92\xab\xb3Y\xcb\xeacA\xd2\x117\x947\xecv\xc5z_\xf5\x9b\x8b\x17\x0f\x9b) 7\x16Q\xa8\xee\x9ab\xfbW[\xc3\xe5\xe9\xc2\xbe\xda-\xce-\xf9(k \xb7\xdc\xd5\xcd\xe5-/\x8a\xf5\x1a\xdf\xb7\x8feM\x84\xda\x1e6\xcb\xed\xab\\\xc6W\xd1\xaa\xccgY!\xc6\x90\xfd\xcb\x8f\xd8\xf1\xed\xf1\x117\xad\xf0\x06\x7fu\xfc\x15\x9f\xb6\xca\xaa\x15\xd3\xff\xbae\xf3\xc7S\xe7\xf0P\xceKXsc\xe73v\x04-\xcbV\x0dl\x9aM\xc6\xcd\xb1\xe6\xa7\xfc\xd5:/X\xbf=\xb8\xc9\xcb\xac\xb6\x9d\x19\x81\xef\x00\xd49@\xe3\x12\xf7\xf6O\xcb\xa9\x0er\xb1G\xda\xf0\xe9Vn\x0eyGb\x1fDS\x9f\x96\xf7\xc7\xf0\xf7\xea=\xbbc\xf5\x91s\xe7\xf0\xe6\xe2E\xa3.\x8dV7\xe2\xdb?,fP\x06o\x97m\xbb~{$\xff\xbfy{\xc4wEe\xa5~=\x12\xbdq6\xe0\x0d\x15\xf6j\xf3\xbd\xeff\xadHS\x8e\xef\xb2\xfaN\xdc\xc4/rx\xaf\x1b\xd9\xb5D\xc9\xf9vM\xdd>-\x0e)\xe2r\xeb\x062\xfb\xd6kQ\x89\xdd\xd6SG\xdb\xfe\x0b\x9c/\xfa\x1a\xf1n\xa1\xf3\x84w\x95\x16Ph\xd3lVl\xee8c\xfd\x0b_\x9b\xfe~u\xf5\x1a~>\xbb\xe2\xfb\x065\x04\xe5\x18\x13\xb7\xfd\x83\x99\x0e\xc6e\xeb:\xed\xab\xfb5\xfb\x8f\xff\xfe\x1f\xd6\x17\xba\xfb\x18K\xd5\xdf\xd42\"Zh]W\xf3\xcd\x8c\xf1\xc3\xa1X\xc2\xec\xbb\xa0\x7f\x81\xd3\xf5\xba\xc8g\x99\xb2e\xcd\xe4\x0eU\x92\x9ef\xd9\x8c\xcf-U\xf5n\xb3\xee\xc0\x97\x9b\xac1^\xe9(\xa5r\xb3\x17E'\x14e\x14\xb7\x88\xb5K\xb6\x1a\x8c\xa1\xb9\x1cD\x99\xae\x12\xff\xef\xbb*\x9f\x8fX\xf2\xdb\"\x0b(\xa6\x8f\x9a-\xaa\x9a\x1di\x05\\o\xd6\xe6jWY26\xd7\x1bm1\xe5\xd5w\x8e\x9a\x88\xba\xc8]\xa4\xdc\x00\xf31{\x0c\x8f\xde4\x0c\xeeX\xcdwa\xdcJ\xbc{\x8a\xd4x\xa2\x7ffev\xeb\xaa\xfdM\xcd\xb2w\xe2\xc4)\x15\x1f?\xb6\xf7\xa8\x97U\xcb\x9e\xca\xf3\xd2bS\xce\xe4\x08\xe3\xf5Ps\xd7lS\xd7\x82\x8c0\xa4U\xd9\xa7K\xde\x1f+\x01\xbf\xdb\xd9Tj-\xbb\xd9,\xa0f|%bG\xe2f| Y\xf3\x8f\n\xd8_l\xef\xf4\xb8\xb4\xaa\x12\xe4\nA\xd1\xe7{U\xc7\xe2\xb2}\xd9\xbbk6\xbe\x14#\xb5\x91\xcc.I#\x98\xccR\xf0Ha\x10\x12\x03\x97C\xfb\xb1}\x11\x14{\xff\x1b\xc7\xa4$\xb9\x0e|\xc3\x9d\xaf\xd6\x05[uW\xfa\xab\xabcg\xd0\xb0UV\xb6\xf9\xccr\x10\xda\x83/{*\xd8]\xd2/|:\xbaa\x92\x06\x90\xcf\x07\x1b\x9c\xad}\x8c\x06\x0fo\xaa;{\x9f\xeeo\xcfm-LzL\xc9\xde\x9e\x96\xf7o\xf5\xf6HPQ\xb3\xfa&ok>\x88\xed%4\xaa\xd2kDVT\xaa\xebAfnZ>;\x8b\x85F\x96\xf0\xc6@\xcc\x1e|[\xef\xea,]\xf3\xb5\x1e8E~#\x8a\xad\xd6\x91F\x9f&\xf9\xfc\xb0\xcef\xefN6%\xff?\xben\x9b9\xbcR\xd4Bo\xdf\xd8T\x0b\xd8\xb4rb\xd3\xd3C#\x9c\x16\xf3y.\xe7\n\xb8e%?6\x8b\xc2\xf3s\x96F<\x8d\xfaxyd\x13\x9a\xbfw\xf6!\xe3\x9d\x1f\xbey\n\xafy\xf9\xf9\xbc\xa0\xaa\x92uF\xcfKx\xf6_\xff\xabc\x99\xfc\xa9\xaa`QU\xf0\x03\x1c\x1f\x1f\xff\x1f\xd6\xc7xa\xb2\xf2\xde\xfe@V\xde\x1f\xf3b\xfcTW\xabG\x8b\xaazl\x7f\xf4\xf8\xd8\xbe\xfe\xe5\x0bx\xc4U\xbd\x11\x15\xb9\xaa\x1e\xfd\x17\xae\xeb1|t\xcc\xe1.}\xff\xe9\xb6\xdd\xb7\x1e\xdb\xfd\xb7\xec.Kf<\xf8A\xec\x0d\xf9W\x12X(o\x1e\xfdTU\xc7\xb3\"k\x1a\x8f\x81d\x11\xf9K\xb2\x8e\x83\x17\xede\xb0X\xae3\xdd_<\xa6{}\xdf.\xab\xd2a\xbb8\x7f}\xf5\xea\xe2\xb1\x0b\x0b\xea;\xaa\xfb\xc3\xf2\xd3ns~\xe71\xe7\xcf\x95\x03N\xe0\xa6|\xfa\x03\xfc\x97\xf5\xcd\xf1OU\xf5\xf1\xf8\xf8\xf8?\xed\x0fg\xe5\xfd\x11\xdf\x86\xf27\xd6r\x13\xf5KV7\xcb\xac\xe0FvW\xc4e\xc2i)\x1cE\xc8\x17\x93\x02\xbc)W}\x11D\x01\xc5\x00\x11O\xfdo?@\x99\x17\xce\x0e\xee.\x97\xa5'_ \xee\xc9\xec]7\x17w\x17\x12\xdd\xdc\xf7\xdb.\xbdz\xc8\xe8\x00\xf3\xaeW\x93\xf96\x8de\xcf\xf2\xd0\xb0\xa5:\xe1\xe7\xf7c\xf1\x03\xdf\xae>\x84l\xb0\xda\xf1\x95\x90\xf7\x04\xdb\xda {\x88\xf9c\xdd\xd2R\x16\xf7\xfa\\\xb9\xe5,\xe8\xb6\xc9\x90-Zfr\x0eJ\x11~\x8c\x87'\x0f\xcd\x9fRk\xa2.\xb28\xed\x02S=\xfa\xabEU\x1d\xdfd\xb5\xa8\xec\x87\x93\xfb\xe3\x7f~%\xad(\xce^F}\xf6\xa3\xa8(\xeaW\\\x07_\x0e\x8d\x8f\x888;\xe3/?\xfc\xf0\xc3\x0f\xf6>\xc0\xdf\xeb}.\x99\xf6\xed\xf2m\xac\xd8\x04\xc9s\xdd\xa6a\xda\xb1z\xbb)\xb2\xda\xaco[\x0d\x7fe\xce\xfam\xcb\x11\xb0\xd5\x0d\x9b\xcf\xfb\x0d\xcc\x91\xdc\x8e\x9b\xd4e\x16\xef\xcd`K\xb1\x10\x07\xd9\xb7\xff77\xdd[\xe5L\xe8\xb6m\xc3\xc61\x0f\x105\xfd\x07\xf5\x07\xe2E^0\xfb\xba\xa1\xe7\xac\xd7\xacn\xaa\xd29l\x95'n\x91\xd7M+n\x15\x85\x1f\xe0\x1b\xbb\xe6\xee\x05\xde)\xf5\xf3\xdf\x86\xaf`\x00\xceR}%l\xf9\xd5S\xf8\xca4j\xc7f8\x96\xb5\xfc\xea\xc8\xa5O\xd4\xefe\xb6\xe2:\xffOY\x85\xff\xcb\xf9\x02\xaf\xdf\xe4\xf9\xd0J\x9e/\xd4\x81k\xdc\xd7do\xc8\x1bx\xcf\x8a\xe2\xc9\xbb\xb2z_\x8ayf\x99 pc\xd3\xb4\xd5*pp\x8d\xbb\xfc\x91\xdc\xc0O\xc6A\x1fZ\xa5\x8a\xc3;\xb0\xe5p\x95\xc9.m\xfe\xd8[1\x18u?\x97a\x7f\xc2\xfd&J.\x87r^v\xe3C\xe1dfUr\xc8\x98\xbf#\x8ap\xdc-\xce\x8f\xf8\xbc\xa6M\xb8\xe5\x1a\xd2\x1e\xd3\xff\xf8\xef\xff\xf1\xd81\x90R\xf4\xb9\xf1\x07\xdd\xddN\x98\x8a\xab\xfc\xe6\xf8\xdbo\xbem\xbert!\xf9\xff\xae\xe8\x94|8a\x8f\x03St\xa8\x7f\x0dsVV\xab\x8ep\xb8E\x1d\xd81\x1c%\x9c,Vm\xcaV@\xe3\x1fE\xc9\xd0l1\x19h\x93\xb5\xd9vd\xa8\xac\xa3z{\xc4\x1a\xeb`4\xfe\xf6\xf3>\xd4\xf4` d\x16\x1f\x88\xd3\xfb\xb1\x1d\xbd\xdf[y\x1a\x98\xca\xe6\xd2X\xe3\x85\xc7\x16\xb2\xa8\xa1Lm\xbdx \xd8l\xa5\xe1\xda\x03\x8ciA1\x8b\xc2\x0eZ^\x88dK\xcd0\x1bmQt\x01\xac\xcaWxST\xb3w\xb3e\xd6\xb9p\xc6\xb9.\x8aB\x02\x97\x07\x9f\xefBU\x8b\xe0'\x82\x9f\x08~\x12B\xf0\x93\x10\x82\x9f\xb6\x85\xe0'\x82\x9flB\xf0\x13\xc1OB\x08~\"\xf8\x89\xe0'\x82\x9f\xa4\x10\xfcD\xf0\x13\xc1O\x04?\xd9\x84\xe0'\x82\x9f\x08~\"\xf8i )\xa0\x00\x82\x9f\x84\x10\xfc\xf4g\x81\x9f\xb4\xd0\xcd\x7f#\x19U\x99n\xfeC\x19\x93n\xfe\x93Q\x81\x1dF\xb9CP`\xa7\x83R\x9cRl\xa0\xd73\x89q\xee\x01\x81\xb3\x04\xceZ\x9f$pV\x08\x81\xb3\xdbB\xe0,\x81\xb36!p\x96\xc0Y!\x04\xce\x128K\xe0,\x81\xb3R\x08\x9c%p\x96\xc0Y\x02gmB\xe0,\x81\xb3\x04\xce\x128;\x90\x14@\x19\x81\xb3B\x08\x9c\xfd\xb3\x80\xb3\xae\xd8\xc0\xa6\xcd\xda\xcd\xf0`\xe4\x82,_ixV\xbe\xc5[a\x91\x17\xad\xb8\xb1S\x81\xb3\xe3\xc2<\x81_N/\xfe\xfd\xec\xe2\xfa\xf2\xea\xf4\xea\xcd\xe4\xdaW\xebO\xf0\x04\xde\x94\xa2\x0f\x9f\x9c\x97\xf2\xf0,a,\xb8\x9c\x96\xd6\xf0\x8d\xd7\x17\xaf^\xbf\xba\xdc\xfe\x80\xfe;<\x81\xf32o\xf3\xac\xe0\xf3\xe9\"\xbfUF\x875\xab\xf3j~\x04\x9b\xf5\\\xde{(\xfdsG\xd0V\xefX\xa9/n\x15\x0e\xa1\x9a\xf1\xc3\xde\xb1\xbb ?\x9d\xbf<}q\xfe\xffm\x97\xa4\xfb\x01\x9e\xc0\xb3Q\x11\xba{?\x8f\xa0f\xd9\\\xe6fU\x1f\x16\x1f\xdd\xbaos\xfa\xd1\xd3gW\xe7\xbf\x9eM\xbf(\xff\nO\xe0R\xea\xca\x1b]\x87#\xa87\x05\x93\x0e\xc9\\\x80\xed\xb3\xa9oa\xfa\x89g\xa7/\x9f\x9d\xbdx\xb1]\xaf\xee\x07\xfe\x8al0>\xff\xdc0V\xf6W\x8d\x1e\xc1\x9a\x95\x02\x1d\x98\xb3\xa6\xad\xab{\xf7\xc7\x9e\x9f]^]\xbc\xfa\xc7\xf6\xc7\xba\x1f\xfa\x8f)C\x899O\xccK\xac\x84\x9a\xcd2\xf9Y}\x7fj\xc3\x9b\xbd\xc9\xe7\xac\x16\xb9ZE\x19\xf8\xccU\xce\xa1\xac`\xb1\xa9\xb7n\x80\xcd\xd4\x99_u\x88iX\xec\xef*\x96UK\x1f\x17\xbb\xc8\x8a\xc6\x13\x18\x0b\xc6\xdb\xb7\x1d\xe3\xc6\xf9\x9c\xee\xe3\xce\x87\xba\xee\xe7|Jv\x19\xe7#]{;\x9f\xea\x1aj4\xc9\xa8\xcb\xaf\xfd\xd5\xd4\x13U\xcf\x089~\xc7\xee\x91\x13\x96\"Udj\xafW\x8b\xd0a\xe1\xfdV8\xbf\n\xdc\xed\x18\x18\xc2g};q\xee\x8a\x06\xd6\xf7\xbd\xbaX\x15\xaf\xf8\xce\xbc*\x85S\xabZ,\x1a\xd6BU\xc3\xb8\xb80\xc0\xdc\x1a\xd6&\xefLF\x0f\xa6\xc1\x88\xb2|6;N\xbc\x87\xaa2\xc2\x94\xe5f\xc5\xea|\xa6\xff&\xb6\x05\xb3\xac\xe4\xf5\x91\xee\xdb%+\xb5\xe17e\xe71\x9f,\x0d\xe7B[\xc1\x9a\xa67\xa1\xf41\xcb\xabu\xdf\xb1@{\x8e\xd5\xef\xd9\xb8\x13\x8e\x8a\xc1\xbcE\xbe\xca\xb1\xd6\x15\xcfjv\x87\x8d\xba\"\xd1\x94a\x0fV\\\x90M1aYH\xdf\xe9\xf0O\xe7\x0b(\xd8\xa2\xd5T\x1f\xc5\xfd\xd1\xa7[\x01\x04\xc9\x01\"?\xc2\xed|s\x0f,\x9b-![o\xa5\x01\xf8\x84V\x1c\x12p\xfa\xf7]\xb6\x1c\xbc\xc1-*zh%\x12\x14\x00\x88\xfb\x9c\xe7\xf9,kY\x07 +\x0b\x8a\x07UG\x1a\xaa\xcb\xcbY\xb1\x99O\xce\xae\x99\xfcJ\x87\xc9OZL0<\x06P\x11_\xc3\x07|\xb6\xc9\xe4\xf2\xe6|zg\xfe\xa4\n\xe2\xb8_\xb3FQq\xc4\xf0\xea\xc7#\x1fr\xc7j4\xe5\xb7eUO\x806=\x1a\xc7\x9f\x90\x96\xd9\xb5ao\xaa\xaa`\xa3\xcb\x9f\xbb\xc9g\xf2\x8b\xa1ikv\xc7\xea\x91RW\xb3\xaa\xa7\xa7M\x9a\x0f\xa8]53\x8f\x91\x91\x1e\xfe\x0d\xb5\xfd\xa8\xea9\xab\xa7>\xf8\xcb\xbc\x9c\xb1\xa70\xab\x9aU\xd5\xe6sd\x8e\x7f\xe03k\xd1]Y~s\xaf\xee.\x90XF\xcd\x1a\xbd\xb36\xa4\xf8\x7f\xa0\xabz\xd0\x892lT\xac(rh\xebdTy\xc1D\x0c\x1a\x97\x9cK\x85cR\xc5\xf0\xa8\xdc|\xa9(\xb6\x94\xf8\x84E\xa1\x97+\x95\x80)\x15\xc9\x93\xb2\xb2Kp,\xa9\x9d8RQ\x0c)~p\xb1Y\x11\xc7\x8f\x8aaG\xb98\x0b(nTbf\x14\x8a\x17\x95\x90\x15\xe5\xe5D%bD\xed\xc2\x87\nfC%\xe0B%fByxP\xc9YP\xfb\xe1@%g@\xe1\xf9Oq\xec'\x87\xd1}\xdc\xa7d\xcc'\x1c\xef\xc9\xe0x\xb5\xcf\xaf\x899O>\xc6\xd3\x8e|'\x07\xdb\xc9\xbb=\xf12\x9dp\xfb\x97\xb4,'\x1f\xc7\xc9_\xa68~\x93\x9e\xd9\x0d\n}\xec\xa6\x84\xdc\xa6\x1d\x98Mf>\xa2\x8b\xd7\x94\x96\xd5\xe4\xe64\xa5`4\xa1(9\x1e6\x13\x9a\xcbd\xa7\x1d\x84\xf3\x98\xec\xba\x8c\x10_\x12\x06S\x88\xb1\xb0\xec%\xbfM\xd0\xcc\xa5\x08\xde\x92\x19\x0eM\xc4YB1\x96\xfc|%\x0c[\xc9i\xc5P\xa6\x12\x96\xa7dc)%\xe0(\x050\x94\xe2\xf9I\x0e\x16\x10\x96\x9b\x94\x98\x99\xe4(\x91\xb1\xa7Fq\x92\xb4\x87\xd6\xa0\xcf\xc2HJ\xccG\xb2\xb3\x91b\xb9H\xc2#`*\xb8\x99\x89\x94\x96\x87d;\xf8y9H6\x92\x84\x8d\x7f\x94\x96}\x14\xcf=\xb2\xf0\x8c\xa2XF^FQ\x18\x9f\x08\xcd&\n\xe4\x12\x850\x89\xac<\"{i\xb0|\x0e\x1c\x87(\x90A\x14\xc0\x1f2V--w\xc86(v\xe0\x0d\x19\xfd\x14V\xd6P\x1cg\xc8\xc5\x0fJ\xcf\x0e\xda\xbd'\xa1\x99AX^\xd0x\x89DD\xa2K\x00\x00\x15\x85\xae\xe2\xce\x7f\x99^\x82\x0c\xdd\x92A\x11\xe8\x14\x81N\x11\xe8R(\x02\x9d\"\xd0{\x89\xc1X\xac\xca(\x02}[\x12\xe1-\xbb!.\x11\x98K\x12\xd4%9\xee\xe2E^\xf6\x80\xbd\xec\x0b}\xd9\x03\xfe\x12\x82\xc0\xc4b0\xce9\xdc\x87\xc2$\xc4a\xb0HL \x16\x93\x1c\x8d\xf1\xe31;#2\x14\x81\xee-Y\x1cBcTE\x11\xe81X\x8d\x0f\xadI\x83\xd7 A\x08/f\x13\x80\xdax#\x81\x03\x91\x1b\x8a@\xa7\x08t\x0c\xa6\xe3\xb5j(\xae\x83Gv(\x02}\"\x89q\x1e\x8a@\x1fJ,\xeacTF\x11\xe8\x01\x18\xd0.(\x90Q\x1dE\xa0\x1b_@\xe1F\x14\x81\x9e\x0eE\xa2\x08\xf4\x9d1\xa64}\x0e\x8d3\xe1\x91&\\\x04z>\x9c\xb0G\x07Hq\"\x95\x81)\xfc\xb4\xa3\x82Ud\xf7[M!\xa6=]Pk\x89\xbd\xe1\xc5\xac\xde\xe3cof\x15\x9f4\xab\x92\xef\xbcd\xf4M6\x13q^\xea\x95Q\xc4\xcd\x99P\xfe@W\xe2@#n\xa4 \x0e\x03z\x12]#\xda\xe9\x92\xadxKD\xbe\x8eqy<\xab\xf2A\x9a{\x95O@\xba+\xc6w/\xf3 3+U\x81l\xa7\xf3\x97\xaf\xae\xce\xe4\xbd\xcd\xf29\xb5P\xe7\xc2\x97r^\xb6j\n\xeb\xfcW\xc3y\xcc\xa8Pn\xeb\xcc\x1fk\xf2\xdb2k75k\xba\xe1\xc47\xb1\xb7\xd5m%&\x8d\xf1\xda\x8f@\x93e\xe7F\xa1\xc9\xa6\xd7\x15\xb6,\xb5\x10\xa6L\x98\xb2\xd7\xa1\x8a\x19\xa0@\x982a\xca\xd6' S\x16B\x98\xf2\xb6\x10\xa6L\x98\xb2M\x08S&LY\x08a\xca\x84)\x13\xa6L\x98\xb2\x14\xc2\x94 S&L\x990e\x9b\x10\xa6L\x982a\xca\x84)\x0f$\x05\xbeG\x98\xb2\x10\xc2\x94\xbflLy\x0bO\xd6\x91\x8b\x9f\x19P\xbee\xad(\xcf\x8a\xb5\xd9\xb4\xbaiU\xcezj9\x1c\x02?\xb7\xf9\x1d+\xe5\xd3N}\xdc\xc4\x02\x86\x81M\xd6V\xab\xc7\xf6\xed3\xfb\xb0\xaeJfG\xf3\xc1\x8b\xa7\xf62@V-\x17\x92K \xb1\x8c.\xdf\xd08\xeb\xea\xbdt\x97}\xf3u\xff\xbbX\x98\xaa\xd2\xe5\x0d\x96\x00\x9c\xe3\xa0\xc5\x07t\x96\xab=\xf5M\xd6\xb0k\xd90\"\x11\xb2L\x80\xcb\xff\x9b\xf1\x8dv\xdf\x1eN}b\xd2xS\xe6\xed\xc3F\xb5\x9b\xf3\xf1oTW\xf8\x01\xbe\xf9\xfa\xff\xef\xea\xd6\x17\xc5\xfd\xb6\x00\xde4QbP\xfej!\xfbA\x8f \xc9\xdb\x0c\x1c\x08 \x0c\xcb\xce\x15<\xe4\n\x1ez|\xcb0l\xb1\x1f\xe0{\xbe_\xd84O\xe1\x1b\xe0o\xcbZ}\xef\xed\x93Y\x91g\x8dk\x88\xfa'\n)\xce\xe9B\nr0\xfbn\xd2\x97\xa2\n.\xf3\xbe\x17y#\x0c\xa7\x86\xb1\xfeM\xaf\x9f\xfe\xae\xe3\x1a\xe6\xae$\xfeC\xe9\x9bp0~2^\xa6\xcd\xac\x9dzc3O\xa1F\xd4\x9b\x8d\xea\x17j\xa8\xe43I\xd31\xb7\xaa\xcf|\x83\x85a:\x0bj3\x0e\x07\x92\xcc\xce\xec*\xec\xac2B3|LD\xad\\\xd8I\x8b\x7f`Z\x03\xf179\x10\x1f\xf5H;\xff\xa5\xab\x93E\x9b\x18\xc9\x83\xf1\xf4\xb5q\xd0\xcc\xf3f]d\xf7)\xeae\xedI\xea\x1b]>x\xb5BmnoY\xd3\x8a{A\xc4<\xc9\xfbSWE\xb7*\x99o|V\xe4\xdcN\xa6j\x89\x9di\x82:=4\xe7(\x7f\xe8\xea\xa8\x0f\xc5\xa2\xab\x19h\xad\xe6It \xec\xdf\xb1\x12\x1e\xb1\xdb\xa7\xf0L(\x85S>\xa1\x9946\xf7\xab\x9bj\xbf\x19\xb1\xe5'\xfa\xeb\x10\xc4\xdd;\xf2o\x9a6\xd1,\xf9I\xb0*\x81}\xd0\x08+/\xbdE\xe1\xe9\xd5\xab_\x1e+6\xcd\xcc\xe6\xa3\x00\xdd\x87\x1b\xe1e\x92\x1fWM\xebH\x17\xeaK\x17\xafeS\xe7{5\xda\x9b\x8bs\x01w\xc3\xbc\x9am\x04\x8b\xe7Q\xc5Wx\xa8\x16\x8b'\xb3e\x96\x97\x8f\xd5M\x1d\xca\xd9b\xd13p\xd5\xe4\xa5\xdc\xff\xe4Uy\xdc\xdd\xfc\x14h\x8a\xef\xcd\xa6\xb8^f\xcdr\xdf\xf6\xf8{\xd6,\xe5\xca\xd5,\xb3o\xff\xfa=\xf0\x8f\nWYo\xa4u\xc5w\x81\xc2\x85\xfa\xe6\xe2\xdc\xb6\x82\x9f\xf39Z\x80\xb8m\x05w\xac\xce\x17\xf7\xc2\x986S\x88\xae\xa3?1\xcf\xe7\xe5\xc3V\x01\xf6 \x0d\xe9\x9f\xe7\xf4\xd1\x0e\xb1T\x1a\xde\xce\xec\xab\xa0m\xf5\x13G\xdc\xd1\xa92\x9e\xe6i:\x9c\x12\xbf\x93\xf8\x9d\xdb\x82\x9b\x10\x88\xdfI\xfcN\xdb\x93\xc4\xef\x14B\xfc\xcem!~'\xf1;mB\xfcN\xe2w\n!~'\xf1;\x89\xdfI\xfcN)\xc4\xef$~'\xf1;\x89\xdfi\x13\xe2w\x12\xbf\x93\xf8\x9d\xc4\xef\x1cH\n\xae\x1d\xf1;\x85\x10\xbf\xf3K\xe0wN\xa9$\x9f\x96\xab\xc9\x1b:/o\xf1\xd9\x7f\xb2\xa2\xd0t\xcc\x06\xd4\xdb\xa2\xa3H\xfe\x9b\xca\x08$r\x04)%#\xba\xe6\xdf\xe5\x1b\x0ft\xc5\x0e\x94\xa2y\x93\x15\xdcP\x07\x82,)\xa2\xef\x0e^\x15#oX\x01\x99\x1d\x8dX\x13\x86D\xd5E\xdb2\x8b?U4\xaf\xbb8.\xfe\x97\x87\xfb\xe5\xb1\x96\x14\x9f\xcd\xa4x \xa3(\xeb\x817\xb1\x92\x14\x94*\x8c\x8fHJ`\xaa%o\xe1]\xae\xc6\xc8TL\x0e}j\xe1p%d\x92\x12\x96\x96\xa9\x97\x91!eR\xb2!+h\x9e/\x16\xacfe\xab~\x13\xde\xe5a\xe76:0\xddl\xb8\x1f\xd5\xeb]\xa3t\xd4\xf4n\x14\xf1\xb6\xd0_Ygy-=\xaa\x16g5\x9fVsE<\xdc\x9aT\xb3m\x1e?\x88\xb5\xe4V\xb5\xfbvg\x1c\x99\xa4\x7fpX^\x8d_\x0d\x7f\xd6\xbcGn~\xd6\xb4\xdbvq\x8eH\xf7H,\xd9\x87\xf6\xfa\x1d\x8b\xa5\xc0y\xdd\xc1~\xee\x08\x0cJ\xa1\xe7<\xfe\x9f\n\x1d\xc9\x1a\xc5\x86y\x9d\xdd\xb2\x0be\x01\xf9\xbbE\x99\\\x0c\xb9\x1a\xae\x96\x1b\x92\xc1\xaajZ`\x02s\x10@\xc51\x9c\xb7\x83\xcd\xd9\xba\xbd\x87\xdc\x06\xbf\xb7KV3\x01H\x95\x15\xac\xaa\x9ai\x00\xca\xd8A\xab6\x8b%\xadicn\xec,p\x1f\x1bT|^XQ\xfcG\xb9Y\xddH\xf7\xb9\xc6\xcc\x06\x00\x8d\xad\xbeCC\x8b>\x7f-\x94\xd9\xe6\x88\xf7Y\x03\x0dk\x8f o\x1b\x0d\x056\xb0)e\xa7\x9eKt\xe4}\xde\x8c\xfb\x07\"}\x9b\xda\x8b\xc4\x13{d\xec\xc9\xdf\xc5B\xd9P\x027\"\xf8x\x97\x7f\xec\xe2O\x04\x1f\"\xf8\x98\x85\x08>B\x88\xe0\xb3-D\xf0!\x82\x8fM\x88\xe0C\x04\x1f!D\xf0!\x82\x0f\x11|\x88\xe0#\x85\x08>D\xf0!\x82\x0f\x11|lB\x04\x1f\"\xf8\x10\xc1\x87\x08>\x03IA\xb6 \x82\x8f\x10\"\xf8| \x04\x9f\xc3\xb8\x14\xac/O\x0f\xfc\x1e\xbfc\xf7\xb6\xb2M\xa0T\x85\x9dfjJ\xafY\xbb\xa9K\x99'D\xc2y\x8a\x8a\xd3\x01\xad\xc25u;\xf1\xe1\x08\xe4T\x13\x86\\\xe0\xe9+\xbe\x00W\xa58\xbbV\x8bE\xc3Zn\xa0qqa\xe0Zo\xd8\x08\xb5\xe6\xb6\xfa]\x11\x9f\xb4\xf4\xc6ZdE\xe3\xb5\x96\xc5Qa0\xa2,\x9f\xcd\x8e\x13'\x81\xaa\x8c0e\xb9Y\xb1:\x9f\xe9\xbf\xc9\x9c\x16Y\xc9\xeb#\xbd4KVj\xc3o\xca\xce16\xd9\x0e\x9f\x0bm\x05\xefC\x9d \xa5+i\xd3pS\xbfc\x81\xf6\x1c\xab\xdf\xb3q'P\xb4\xc1\xbcE\xbe\xca\xb1\xd6\x15\xcf\xf6\xe9V\xcc\x08\xb5t\x9a\x0e{\xb0B}7\xc5\x04L\x95.\x92\xe1\x9f\xce\x17P\xb0E\xab\x11}\x05\xf1\xebM\xac\xf0\xf7\xca\x01\"?\xc2\xed|s\x0f,\x9b-![\xaf?\xa3\x15\x878{\xff\xbe\xcb\x96\x837\xb8EE\x0f\xad\xc4D\x032\xe9\x9a\xcc6\xd4!?\xca\x82\xe2A\xd5\x91\x86\xea\xf2rVl\xe6\x93-j&\xbf\xd2'\xef\x19\xb7\x98\x00r\x07\x1ea\xbe\x94\x0ch+\x93\xc9\xe5\xcdy3i\xadI\x15\xc4\xae\xbef\x8dB\xdc\xc5\xf0\xea\xc7#\x1fr\xc7j4\xe5\xb7eUO\xfc\xe9z4\x8e?!-\xb3k\xc3\xdeTU\xc1F\x0c\xaen\xf2\x99\xfcbh\xda\x9a\xdd\xb1z\xa4\xd4\xd5\xac\xea\xe9i\x93\xe6\x03\x06G\xcd\xcccd\xa4\x87\x7f\x83\x95\x02\x83\x14\xc9\xf7\xa6\xae6o:\xa1}\xda)\x90\x04[\xb26k\x1a\xd6J\x17\xa6\x8f\x0b\xfb\x92\xb5\xa7\xfc\xe9_\xc5\xd3\xcaH\x0d\x94\xac\x05\xa1EyBEo\x1d\xad\xe2#\"\xecX\xcb\x03]\xff\x03\xe5\xc3\x96\xac\xbd\x16\x95\xbb\x96\x95;\x0cB\xc6\xba\xceg\x1e(\xe2@R\x8e\xfa\xd9\xa3\x085\x18\xf0\x01\xc2y\xa3\x08vhZn(\x92\x19\x1a\xcb\x0bU\x146\xd19\xf4^`V\xf1\xa2\xb5L\xad\xce\x1a\x0d\xe2=\xfaa\x03wU\xb1Y\x99\xcb+\x7fJ\x00w98w~\xd6\x9d.\x87\xaeN\xbfD\x8a\xc6m\xc6\x87\x06\xb9$\xbfg\xb5\xbd \xd6\x9bz\xb6\xcc:\xfcs\xc9\xa4\xb9\x8c\xcfo\xd6\xf3\xace\xf3\xeb\x9b\xa2\x9a\xbd\xbb^\xb2\xfcv\x19{\xc1,\x04\x1a\xc4\xf4im\x03\xf17P\x7f\xab\x16\xd2\xdd&_0\xcd4R\xe1h\xd6\x1d\x8c\x11i\xb8\x87[\xb3\xf8\xf64'\xf58&{CnP\x04\x17q\xbc\x1c(vd8#q\xb26\x11%\x91(\x89\xbe\xf1\x88]U\x88\x92H\x94D\xb3\x10%Q\x08Q\x12\xb7\x85(\x89DI\xb4 Q\x12\x89\x92(\x84(\x89DI$J\"Q\x12\xa5\x10%\x91(\x89DI$J\xa2M\x88\x92H\x94D\xa2$\x12%q )\xe8aDI\x14B\x94\xc4/\x9b\x92\xf8\xd9\xef\x94534De\x1a;'\xe3\xb5\xf8\xbd\xcb\xa7#\x00\xcd\xae\xfe|\xe2\xfepr\x93\x95\xef`U\xcd7Ew\x8c6\\$+\x15=\xd0\xf59P:\xc6\xd0\x1cC\x99\xe4\xff\x116\x19\xe6D\x1a\xdbD4\xeb\xc8 \xbdX\x0b\x05^Tj\x95}\x90\x9c\xabk~4/v\xcd\xfc\xb3C\xb2\x9aU\xf6!_mV\x9a\xb4P-@\x96Hx>\x8b\xa2z\xdf!\xbf\xcam)\xaf\xde\xb49--\x0b;+\xb3\x9b\x82]\xdfVw\xac\x16\xfd\xd6U\xe1m\x06S/6\xce\xd7P0\xde\x1e\x18\\\x05\x98/\xa0/\x98t.\x8b\x0dE]\x15\xa2\x0f\xc8\xea7\x90[oQ\x93\xfej\xd3~cS\xd6\x8c7\xe0\xacesy\x9b\xe9u\xcdn\xd9\x87\xc8\x06\xf7\xb5f\xa67k\xc0>\xf0\xf5\xa8\x11W]\xea\xab\xcc\xb2\"\x9fg-\x1b\x81\xf0.\x07\x10\xc0\xa2\xaeVPr\x9b\x17\xfa\xceU\x95\x00\xab\x81G\xbd\xd1lK@\xf7lV3\xb9;o6b\xb8\x08OT\x91\xb5\xbc9E\xee1]\xb8\xbc\xb2\xb9\x8fei\xfbj\x8d\xcfl\x08\xfa\x80\x9c\xb6P\x99\x8c\x14S@\xcd\x98\x17\xaf\x9fM\xf4\x11g\x808\x03>\x879n\x16\"\xce\x00q\x06lO\x12g@\x08q\x06\xb6\x858\x03\xc4\x19\xb0 q\x06\x883 \x848\x03\xc4\x19 \xce\x00q\x06\xa4\x10g\x808\x03\xc4\x19 \xce\x80M\x883@\x9c\x01\xe2\x0c\x10g` )\xf0[\xe2\x0c\x08!\xce\xc0\x9f\x853\x10\x987A\xa2\xa9\xbe| \xfd\xdda\n}\xad\x16\x12\x16\xab\xca\x1e\x7fU\xb7\xdf\xa8wG\xc0\xfc\xa5xK\xfdr\xb0\x90\xbc-\xc2\xdf\xaa\n\xbcP\x8d#\xf5\x80\xc7A\xe1J7\xe0|\xd5\xef\x18H\x99\\ *\xad\x80}\x8e\xb2'\x14\x08K%\x80\xc0[e\x9fD\xe1\xad\xa6\xd7\xd5\xcd1R\x0b!\xae\x84\xb8z\xdd\x8d\xfe\x81)\x85\x10WB\\\xcdB\x88\xab\x10B\\\xb7\x85\x10WB\\mB\x88+!\xaeB\x08q%\xc4\x95\x10WB\\\xa5\x10\xe2J\x88+!\xae\x84\xb8\xda\x84\x10WB\\ q%\xc4u )\xd0/B\\\x85\x10\xe2\xfagA\\\xff\xa0Q\xdac@\x98\xb5\xd9\x16\xe7\xd2\xcb\xa7\x0e\xa5}t\xeb8 \\i\x1d\x94H=v\xb0 \xb4\xc5\xc5\xe2t\xae\x8c\nz\xd5\x1b[\x18T\xf7+\x15\xec*\x0f\xd1\x16[o)\xdbrs\x0c,\x89\x82N\x15X\xba\xdd\x00Z.^?#\xf4\x94\xd0S\x8f\xebp\x0f\x8eG\xd7\xb4=\x9a\xf5l\xbdpr\xbd\x16\x1fw\xd3\x91\xc5\x17\xd2\xa2\xaa\xde\xc1f=\x1ed\x81\x93\x1a\xec\xbe\x0c\x80\xd9\n\xe1\xab\xc3|^\x9f|\xe4\xff\xdb\xb8\x96\x83\x9fY\xfb\xe3\xfd\xe9|\xce\x07f[\xe7\xecN\xa6\xb2\x97\xd6\xb9\xcd\xefX)<\x14\xcaN\x8fX\xf3\xd88\x81wj\x1e\xe8\x1a\x1f\xe8\xb4-\x1a\xf0@F\x9b(\xcbu>O;^\xa6\xe2\xcb\xf5\x00\xf0F\xf0\x15\xe0\xfc\xb9Z\x0c\xf2F\x16\xed\x18\xe0\xbc\xe7\x035\xf3w\xc7\xa7\xb2\x1bX5\xf1I\xb3^d3\xb9\xac\xf0\xc3\xf9\xfb%\xab\xbb\x9b\xf7\x98\x1c6\x1d/(/\xe1\x99\xb8\xb4\xc8l\x1e\x05v\x88Nv\x00f\x12\xfey1\xfeG%S\x97\xc9\x0d\xcf\xa4\xa3\xdf]pq-\xb7YL\xdbXc\xa1|\xb3%\x9c\x0f\xba)\x8c:\xaa\xf7\xe5hV\x1c\x8b\xabGK\xb1\xf6k)\x9e\xde-\xc5\xd7\xc7\xa5\xa8\xf6w=\x82jG\xf5\xa0l\xaang\xbe\x18m\\\x1fI\x185/]\x0e\xcb\xba*\xac\xcb\x94\x14\x7f\x7f\x90\x92 ]\xfd\xc8\xd1\xc5\xe8\x9apD\x9epc\xd5 \x0d:\x93Ce\xe3\xc2\xbd!\xc4`\xac\xdc8\xefU\x02\xb1\xa4\xbc>\xbd\xb8\xfa\xc7\xf5\xd5?^\x9f]\xbfyy\xf9\xfa\xec\xd9\xf9O\xe7g\xcfC^{uq\xfe\xf3\xf9\xcb\xd3\xabW\x17!o]\x9e]\xfcz\xfe\xec,\xe8\x9d\xf3\x97\xbf\x9e]\x06~\xe7\xd9\x9b\xcb\xabW\xcf\xcfO_\x86\xbc\xf4\xea\xb7\x97a%;\xfd\xe9\xa7\xf3\x17\xe7\xa7Wg!/\xbd\xfa\xe5\xe5\xf9\x8fo.C^y}\xf1\xea\xd7\xb3\x97\xa7/\x9f\x05}\xe8\xd9\xab\x97W\x17\xaf^\xbc\x08\xab\xd3\xaf\xa7/\xce\x9f{\x9bU\x9f\x06b:\x12\x06\xb3\xed\xc5\xd6W\xa3>\x0d\x8a\x04+\x8f'\xb3\xaa\x94\xbe\x15\xcf;\xb6\x8e\xff\xd4\xfcg\xf5\x0dy\x8fQU\xe7\xe2FA\x07u\xca\xf4\x11=N\x9e\x9a\xfe\xd8\xc3\xbfsv\xd3\nZS>\x13\xdeG\x8d\xd5\x06}K\x8f\xaf\xa7\xa6?\x8a\xcaH\xb07\x9fA^\xde\xb1&\xb4.\xddX|j\xfc\xabn\x92\xb2\xcd\xdb{\xb9\xc0v\xf5\x13\xbe\xb0y\x9e\x95\xaa\x92\xeaF(a\xda\xb0J\x8a\xb1\xfdt\xeb/\x83\x94^bF_g\xb5$\xc1d\xa5\\r\xf5\x8a\xc3\xd7\xcf\xa0\x0fvs\xc3S\xe3_\xa5]\xe5\xe7\xa4\xbb\xa5\x84l\xb1\xc8\x8b\xf3\x8a=]\xef\xfdt\xe8S~Q\xdd\xf1,Ob\xd7\xb7\xab%kD\xeeH\xbe\xa3\xee\xbb<\xb4\xd5\x1a\nv\xc7\n\xb5\xe9\xd7SP\xcdfU=\xb7O{r\x17z\xf2\xf1\xcaJn\xec\xa6]V\xb5\xa0\x83\xb5\x15ol\x96;\xa8q\xd5b\xf1DN9F\xe7\xfd\xe0`oT!|w\xd7\xa2\x7f]{\x0el)k\xdf\x0f\xc2\xf1\xa2\xc3\xdbYb\xd3\xa6\x8aX\xf5\xe9.w\xd9f\xe5<\xab\xe7\xc3\x85B\xad[\x8d\xc0\x04\xc7\xd1p\x8d\xbdc\x8f<}\xfdAO\x94M\xf4m>\x95\xb4m\x9d\xdflDR\xcc{\xe1*\xb4\xaa\xbba\x8a\x8c+\x88\x18\x02\xc5\x94s\xb8^\xf4x/\x9f1\xc5\x81\x9d\xdcx\xdd\x8br\xa2\\\x8b\x99\xf2\xba\xae\x8ab\xb3v7\x97{.\xc6\xcd\xd8\xd8V\xfdMM\xe6YQtCx\xe4\xd0P4\xdf\xa6\x1b\xcabNr\x19MO\x85#5\x0f\x1b=!JH\xd3\xde\x8a\xb2\x99\x8a\xa6RId\xc5f\x83O\xa3zU\xf9A\\\xce-\x0b\xa7tf\xa5}\xe6W\xe5\xd6\xb5\x8b%\xa2^\x8a\xef\xf5A~uU\xb5P\xb3\x05\xabY\xa9\xdch|q)\n6\xd3\x94\x0b\xd7\x9c\xcf\x0b.\xfaUU2\xbe/ZU5\xb3\x17qT@\xe9\x19\x1drb\xef\xf5\xdf\xbaKn\x07\xe0\x14o\xbfE\xb5)\xb7\xd2\xd5\xea\x06=\x10\x1f\xab,\xcd\x9e\xdd\x87\x9f\xd6U\x89\xe9W XT\xc2\xaf\x9b\xcf\xf5n\xa1\xdb\x1d\x1b\x1c\x98\xef\xb3n\xfbmU\xa8R\xf7\xca\xe1'\x0dk\x9e\x9cT\x8f\xdb\xf3\xeaK\xaeI\xd7S\xe4\x9a4\xbbbB\xde\"\xd7$\xb9&\xb1\xb3\xad\x14rMn\xfd\x91\\\x93\xe4\x9a4}\x92\\\x93\xe8\x12\x92k\x92\\\x93\xfbrM\xfaK\xd6\x1d\xe0\x15\xbb\x81\x89\xfd\xa2Ln\xd1\xe6\xfa|\xc8\xed\xc1\x87\x88\xcb9\xe3,\x8a\xa0t\xd9\n\xe9\xdfO\xfa\xeb!3tT\xbd\xf1\x86~1u\x9cg\x1f\xd8l#NEjkl\xe7 \xb7\xf7\xeb|&\xb22\x88X3\xae\xdd\xf8\xacR\xb4C\xd5\x12\x9e \xf5\x86_L\xb02\xb8B\xa4\x8bj+\x11z0+rV\xf6\xe7C\xe1H\xb4\xea\xdaf\x07;\x0f\x85\xd9f\x9e[\xad\x80-\xff3ym\x10\xdc\xdc\x1f\xc1f=\xef\xfe\xbb\xcdW\xaci\xb3\xd5\xba9\xd2\xd1\xf2PnV7\xac>r\xbaqjV\x08\x1dy\xb9\xa8l1V\x88\xd3&\xe6\xac\xa9n<\xba\xe6\x85vMf\x88\x0e\x01\x83N\xc1\xd5=\xe1\xd5w\xa9\x94cC\xb9\xe8\xd9 \x7f\x9c\xcfj\xca\x95\xc6\xca\xb6\xbe\x17\x1e\x00\xf7\xadL}\x1dn,WMIA\xd6`P*\xcb\x81Y\xae\xf5\xaa\xc9EQ\xa5\xf3\xcb\xaaTu\x89\x83\xb6\xb1\x889S\x05\xf5V\xe4S\x1aZ <9\xd2\xd2j\x90\xf9Kg#R\xf72\xa0T[n\x1f\x93\xe2\x9f\xe1A\x04\x95u\xab\xf1d&\x90\xb5\xcc\x1b\xc8\xcbY-\xf6\xabj\xe6r\xeac\xd9l\xa9\x9a\xc3\xfa\x9c\x95\xf8\xddKXS\x05\xd4\xb1\xcb?\xb0\x85\x92\xb8\xfd9bX\xe5Uy\"\xeb\x06\xec\xce\xbe\x81\xf7\x97\xea\x94O\xee?\xf1\xf5\xa4\x81Y\xb6\x96\xfb\xa2R\xb6\xacHQwSmD\xf2!\xd9\xfd}\xdbt\x91\xdb\xe4\x1dS]\xb2K\x17S\xce\xf5\x88b\xf7\xd2!\xbd\xca\xe6\xa6jb\x96\x93K\xb5\xe4w\xcew\xc3\xea\x0f\xd9m\x96\x97\xbc\xc0\xdd\x9ah\xd45v\xa8\xf27\xb2r\xc6,p\xc4\xd5\xc0\xf1&B\xc7Dn\x9al\xa2\x84WVm\xb5\xd4\x06\xcc\xa8,/\xef\xaa\xe2N\xa4\n2\xfe.\x13\xff\xfd\xa6\xacV3\x01g\x95\x95D\x06.$\x94\xa0=\x82\x99\x82#\xac8p\x0fLd\xda|G\xa2Q\xb5-\xc5\xbc\xb1\xaa\xeeL\xb7\xd8\x8d\xd1\x06\x0d\xfb\x8c\xf1\x06\xfd\xd7 \xc4A\x01\"\x87\x018|\x92]\xecI\x97Y\xae\xee\xdd\xbb\xd2\x0c\xc7\x00\xbf\n\xd4t\xa5\x12\xafH\x14\xc0\xaa\xce\x80\xd6\x9f\x16M\x05*x\x92o\x13\x7f\xe2\xa7V\xeb\xf6\xf6\xd3 ,\x18\xcb\xf4@\x87\xde\xcd\xebQ6E8\x86H\x86U\x9d\xb4'<\x124v>\xa3.\xf2\xa2e5\x9b\xc3\xbb\xbbu\xcd\x16\xf9\x07\xde\x89\xea\xac\xadj\xf3\xf1Y\xf9\xcb\xadf\xf1VH)\xe8\xd2\xf8\x0d'T]\x93i\xb2\xc1{p\x9ci\xfb\xf9\xad\x12\xbe\x89j\xb1P\x9e\x00a\xa1>\x7f\x8b\x7f\x07\x90hS\x8c\x80_\x10\xdd\x07P\xe6\x14O\x8d\xf7@[\xce\x99>\xdf\x94\xabk@\xdf8\xd6g\x96Y\xb3LW-^n\xaeQ\x85\xee\xf7T\x0f\xddG\x9c\x90\x8ckR\x82\xe0\xb2\xf86'*\xf1\xc2\xf6~\xa4+\xec#\xfeA\x11\xe2\xee\n\xf1\x85\xfe`\xcd;\xeb\xa4\xa5f\xd5j\xc5\xb7v\x993\xd7\x96\x88X\xfcdU\x97\x9f\x93\x87k]\xec~\xcd\x14\xa9\xe6\x9c\xdb\x08-]l\x15<\x92*\x1f\xf7K\xb4\xb0\xc9Ig\x06=\xb59\xf5\xf1M\xc2;K\xff\xc8\xcb\xf5\xa6\xfdC@\xb5\xbe^\x8cnL\xdc\xfe\xb0\x97\x97\xbc)u\xfe\xc3Y\xb1\x99\xcb\xa9\xb7\xc8\xcbwp\x93\xcd\xde\xa98\xfcA\x9aH\x8fB\xde \x1c\x19]\xd4\xcc\xebXR\xa5\xa0\xab\x8bX^\xa5\xe0\xba\xb9a\x1aU\x8be\xefY\xfe\xa9\xaa\xe1\xaci\xb3\x9b\"o\x96\x1e\xb4\x18\xf4F\xd4\xe5\x86\xf6\xcd\xa7\x01\xe6\x08\xa9\xa5y\xd2\xcd\xd9L@\x12\xc3\x15\x99W\xd9\xa3\xf0u]\xad+\xbe\xc2 \xea\xdbM\x91i+-\xaeb^\xebr,\x04\x1b\xa4\xad73qb\x13\xfb \x95\xaa\xc8\xa1\xaci\xb3v\xe3\xa3L m|>\xbc?[l;\xc4\x84$vk\xba[\xe9\x82z\xc7U\x07\xba\xfc\x8f\x8d8\xb3I\x8f9oB\xe1\xe2[o\xdc\xc0\x10\xda\x968\xd6\xc2\xc5\xd9\xb3W\x17\xcf\xaf\xcf_\xbe~su}yuz\xf5\xe62\x00\xfa5\xbf\xff\xfa\xe2\xd5\xebW\x97\x91/\xcb\xbfy&F\x85\x94\xefR\xf8\xb0\xd9\xd5k(\x9f\xa5=\xaf\x0f@S\x0f\xdc#\x16\xca\xac\xc8\xe7'\x9bR\x9e\x7fd_\xe4\xfd\xc2\xf3\xa2\xa7\xb9\xcc\xf6\xd4\xbfNa\xdd\xc1\x08\x18&\xa6\xecf\x1dA\xef\xed\xce4\xb2{\xc7\x95O\xfe\xcd\\:\xf97s\xd9r98G;\x9bu\xcd\xee\xf2j\xd3\x14\xf7[\xc3v\x00\x89Z\xcb\xa9&\x8c\xab:\x9b\xbd\x93@\x93\xdc\x99t'\x1d\xa6W\x13\xcc\xe9\xc4;\xfb\x0c\x95o\xedOy}f\xcb\x9c\xc9\x94\xc1PmZ^i\xc7\xf4\xe3+\x91\xd4\xf0\x87\xd8e}\x86u\xf6\xefj\x8d\xedB\x08\x94\xc1\xbb..\xff}\xd2\x9dO=\xfa&.\x91O\xb4\x92]\n]\xba\x1e\xba+\xf5G\xee\xc9\x81\xc8\xa3M\x94_\xd9A\x0d\xc1\xbc\xbc\xd5Y\x12\x8e\x16Y^lj&\xd3o\xb3\xd2\x9an\xaaS\x87m3\xec\xd2v\xf9\xe6E\xf0\xba\xb0\xfd\xe6\xeb\xd3K?\xb5l\xfc\xca\xe5\xbf\x9f\xbf\x0e|\xe5\xa7\xd3\xf3\x17\xd8U/\xa6^\xe1\xeb\x9d\xe5+>\xc3[_\x1c\xadq\xb0)\x1b\xe6\xa3\xc0\x04\xb0\xc9\xb6\x9blj(\xfe\xb7\xd1B\xc1\x06\xfd\x9e\x0f\xe0>\xb9G\xe8\xe7xsO?\xc7\xff6\xf8\\W\x15~*n\xf2\xb9>;\x8b\x0f\xbf\xcb\xd7k6\x87\xf9F\xacR\xab\xbci\xf80R\xb3\xbfH\xeaUd\xf7l\xde\x977\xb4\x80\xbcsM\x0b\xc8\xfff\xb5GO\xc66\x96\x9a\x8fl\x93\xfb\\\x0bf:\x92\xc7\x8bWr\xf6`\xe5,[7\x9b\xa2+\x8a^\xce\x16b\xd1v{\xb1\xa0_\xcb=\\\x1d\x7f\xb9\xe4w\x1f5\x8f\x07\x99\xa8D\x8a\xffj\xa1\xcdS\xde\x8e&OOv\x7f\xb9\xbd\xd7\x0f\xf7\xf13:D\xc41\xfd\x1f&c\x7fZ*m\xa8\xde\xb3\xad\xce\xd9\xa3\x07\xed]\xc5\xe1\xf6\xd6p\x81\xe1ewK\x9ev\x8e\xf1j!\xf6w\xa29\xb3\xb6\xcdfK\xf9\x95\x8e\x07\xc2\xc7\x17\xcbf\xe6\xa5n\xdc\xebU\x9f\x14\xa7S\x07eidH\x85\xfb\x8c!$\xfd\xc7 \x04If\xe2\xe1F=\x10\x14\xe9\xd3vO\xd5\xe0]\xbc\xc86\xb64\x86&\x9d3\xc1\xb0\x890\xc3\xe1g\xb1\xa9+\x8chq\x17-6\xfa\xfe\x9ey:>\x9f\x0b\xc2\xe6\xb0\x9d\xdeJ\xf8\x10;{\xb6\xcb\xbc\xbc\xb5\xcf\xf1\x08\x1bBtI\x06\xff\x94\xb3\x8a\xbf<\xef\xd9M\x93\xb7\xce\xd4p\x10Y\x1eq)B\x05\x8b\xbc\x9c\x0b\n\x82\x04\xa6]t,\x80|V\x95\xfb*\x8bt\xf5\xf1/\x98\xbf?\x8a\x8f\xdd{\x88\x13>\xc4\xb6\x8f\"V+\xc50P\xd8}\x9a\xbd\xdf\xd9>)I\xdd\x0e\x85\x011>{\x8f\xef\xf9T\xb1=\xfb\x8c\xeb\xf9\xec1=\x9f4\x9e\xe7\x13\xc7\xf2\x1cD\x1c\xcfa\xc7\xf0\x1cP\xfc\xce\xe7\x8a\xdd\xf1{\x04@F\xbc\\\xf1\x8e\x91\xd5\xd2\xd9>\xcf\x17\x02VhE\xa4K\xd3E\x9f\xf0c\x81CQ\xd74\xabL\\\x0bb\xdb\xb5x\xcbt*\xd3\xaf\xf4\xb4KE\x8eVt:\x9d\x86B\xdc\xa8-7U\x8f\\\x01\x06\xed\x92\xe5\xf5\xd0\xf7+jen/]\x07q\x12\xbd\xceM\x94F)\x9ff\xef\x8589b\x17\xcc\xde\xa8\x96\x1c\x04\"\xa5\xb2\xca~*\x93^\xd8\xf3\x9er\x11\xb7BJ\xff\xd0\xf4x\x1a\xee\xd2\x10\xac\xd7\xcbQy4)\xb8]\x0e\xf2\xd5\xaa\x0eq\x04\xfa\xd2\xdf\xc6L_\xeaV\xf1F^ghru4]n\x0f\x8c\xab\xc3X\xf0\x81\xd7\x02\xb6\xd3vL\xb2\xd0\xf6\x0e\x11\x83&\x84\x8bd\xd47\xc9K\xf2 \xbc$\xea{\xcf\x07\xc7\xf5\xa1\x8f\xa4\xfb\xaayD9MA>\x92\xa9\x90\x8f\xc4\xfeX\xb0\x8f\x043yI\x19vny\xdd\xcd\xad\xc1\x1d\xa8\xe3v\x96Y9\xbfw%\xa8\x99Dvf=\xb5\xe9\xe0\x9d9j\xb4\x9f:\xe2\xb3\xaa\xf7e3\x99h\xf2\xd62\xd2\xc9\x8b\xe3~\x83\xbc8\xae\xc7\xc9\x8bc\xfe;yq|\xdf!/\x0eyq\xc8\x8bC^\x9c\xcf\xed\xc5\xc9F^\x9c{U\x8a\x91'g\xb1)\x8aE^\x14L\x06\x15[U5\xf9m\xc9'\xc3\x0c\xda:+\x1b\xd9i\xa2\x8e_\xdam\xf0\xa9\x8e\xaa\x0e\xfbx\x038\xc6\xf7\\6\xeex8g^\x17\x17\xa1\x14Q]C\x00\\g\xf2\x9b\xbc\xcc\xea{x\xd4Gf\xe8\xd0k\x8b\x83\xad\xc8\x9a\xc6\x19T\x81/\x90\x8c\xefR\\.\x11\x93\xd5v\x17\xf9\x0f\xbb\x85d6e7\x85\xa9\xb1\xdc\x0d\xf5L\xe9\x08pM9\xfb]s\x14\xe2\x9a2[0\xde]5\xf6 \x8d=V\xe6A\xb4\xb3\xd3J\xc5Q\x91\xcb*\x85\xcb*\xcd\xb0q\xccI/\xd5\x88\x92\xecE\xd5\xf6\xeaj]\x9d\x04\xa6K\xa8b\xd52\xf1+;\"c\xbf\xc8\xc8\xc7\xe1\xec\xd5\xf5\x02_hT\xbb\x9fX\xb4\xacW,B\x82U:-\xc1Jm\xab.\x82\xb6\x9f\xb5\x9dys)frK>C,\xc7g\x8c\x99\xc4\x14\xf1\x9c\xf7t\xf3z*C\xb6\x8f\xfa>\xe9\xce\x1c\x06 \x17\xd5A\x18\xd4#\x96\x8b<\x81\x93\xe4\n\x8f\xf9iE~\xc5\xed=\x90\xc3\xd0\xf1@w\x17j\xf4\x1c{\xda\xa5\xef\x93\x0c\xf8\xd1\x8e\xb8\xc9\xda\xbcQ)\xa0*\xc8\xd6\xeb\xe2\xde=\xd7\xca&\xb9\xdc\xca[\xa3x\xc6\x99z\xc0\xf8\xbewNAtML}\xc5\xae\xac\x9b\xee\xec7cxb\x85F\xf3\x93\x98\x91D\xe8M7Q\x99\xbb\xa5d\xb8_\x8b\xaaD\xd7\xe1J\xed+\xa5\xb2it\xd3d\xc1{\xa4\x1a\xd3\xdef2\xac\xaf\xaau\xd0\xdf\x8b\xf3\xcb+\xdb\xa0B\xb4\x81\xdb\xb7\xfb\x04\x9e\x9f\xfdt\xfe\xf2\xfc\xea\xfc\xd5K\xbc\x07o\xfb-o\xdc\xeb\xf6+\x9ehW\xdb\x0b\xc2\x1e\x96\xb7:\x8fdx\xa5\xfc\xfbc)N\x83\xd9\xed\xecxi\x1a\x1b$\"[OT\xa4\xabU\xa1Hw`\x9f\x8a\xec-\xb4m\x1cC\x98k\xd6\x87\xa0\xab\xbc\n\xca\xaf#RT=\xea\xd6F\x8d\xfc\x94U\x0bU\xf9\xc4\x97<\xdf\xd6\xa6\xdbe2\x04\xb7\xea\x14\x0fz6\xdc\np\x9d\xac\xd7\xe1\xc5\x10]\xcbV\x16\xf1\xa3\xb9@\xd9\xbd\xa98\xe3[A\xc4\xa3\xbd;\x07\x96\xd9\x9dkI\xe7\xda\x1b>)Z\xf3:\xa9K\x98\xf3\x9bB^2\xb3\xff\xfb\x1c\x08D\"\x10\x89@$\x02\x91\x08D\"\x10\x89@$\x02\x91>\x0d\x88t5\xea\x8a\x83]\x0f\"\x8d\x84\xbbZ\xa6\xd3\xf1\xd0y?>\xb8I:\xa8\xe3\xc0,S\x8f $J\x12eO:\xef\xbf<\xcc\x9f(\xf7\xfd\xd6\xdb\xfe\xb5l\xe8(\x07C\x96\xd4\xdb\x8e2\xccY\x9b\xe5\xc5\x81 B\xc2\x0f\xe6 jz\xcfF\xe2\xe4\x17\xfd\xb6\xd1\xd1\xdd\xf9\x19\x07e~\"Q\x089Tl\xbdO\xd0\xf8\xd5\xec\xbdbm&\xddm\xd3\xd0T\xc1\xa4\x15Y\x96\x8a\xaaz\x07\x9b\xf5p\x84\xe5\xe5SXg\xa3\xfc8z\x9e{\nm=rJ\x98\xdb\xcb\xd0R\x163\xf4G\xe8\x9f\x94\x11f\xcd\xdd\xe0\xf7U^\x9e\x0be\xf0\x8d\xfak\x9b\xdd\x8el\"\x06\xf0\x03\x80\x87'|\x9b\xc8\xca\xac\x9c\xb1\x13]\xf3\x93\xbbo\xbaD\x9d|\x02=\xf98\x85\xff\xfe\xf3\xa1Tv\xcb\xba\x11\xddlV\xab\xac\xbe\x1f\xcd-f \xb8f\xed\xa6\xe6\x13\x98-\xa8B\xcf2b\xad\xee\x94M\x03/\xb4\xf1m\x13\xdb\xd5t\xa9\xba\xce\xe70\xe3;\x03\xe9\xed\x16\xbe\x89\xcd&\x9f\x1f\x01;\xbe\x1d\xcdT\xdf\xdf\xcc\xbe\xce\x9e\xcc\xfem>\x7f\xf2\xdd\xdf\xfe\xf5\xbb'\xff\xf6\xdd\xdf\x16O\xfe\xfa\xed\xd7\xdf\xb3\xef\xbf\xfe\xfe\xeb\xeco\xdf\x1dA\x067l\xb6\xfc\xcb\xb7=\xb2l)\xa8\xeaE\x93\x8f\x0c\xcd\xfb\xcd\xef_\x7f\xfd\xf5\xfc\xeb\xdf\xbfe\x7f{\xff\xd7&\xbb\xff\xeb_\xb2\xc5\xefs\xd6|X\x7f\xfb\xcf\xdf\xff\xf9\xae\xfenQ\x1e\xc9-\x9c\xfa\xe4d]\x1f\x138\xba?\x9b?\\\xb3\x99\xfc\xe6\xd2\xfa\xcd\xf7\xdf};\xff\xfd\xdb\xff1\xbf[\xcd\xb3\x7fn\xde\xffs\x96\xcd\xe7\xcb\xe5\xdfnW\x9b\xdf\x97\xec\x9f\xdf}w\x0c\xe7\x8bN_\xde%U3$\xf8\xd8.K\xab\x12\xba\xdb\x9a\xbe\xdbR\x8b%N\x919\xec\xb5\xe5\xdf\xe6c\x91\xcd\xc5h\xec?\xf8\xe3\xbd^q\x8e\xac\xf9G\x1a4\x19F\xe6\x9eo\xbb,\xaa\x83\x04\xf9\x97\xac\xfb\xf3\xf5h7\xd2Vb\xd4\xcb\xcd\xba\xf8\x9d\x17d\x05*i\xb9t\x8a\xebv\xe9\x12\xe7\x9e\xcf\x9f\x9aG\x8dzP/\x89\x83\xc1\xfc\xf0\xdb\xaf\xbf~h_W\x079\x8c\xba\xb7\x87\xf3V\xa2\x15u\xc4\x92\xe8J\xbd=\xbd\x8f\ng~Io=\xde\xd7\x99\xc8\x81dn\x9cmf\xbcsAs/g\x9eB\x83O;x\xbf\x00\x01$\n\xd4\xf2\x87Dz\x93R)\x90\xd1+\x98S\x13:<\xc8\xa1\xc3\xf1\x02(H\xc3'\x0e2\xc2\x87\x19}\xcaJ\xfa\x91\xa2(\xac(\x06-\n\xc7\x8b\xc2\x11\xa3\x08\xcc(\x105\x8a\xc0\x8d\x82\x91\xa3\x18\xec(\x06=\n\xc6\x8f\xe2\x11\xa4\x90\xe9*\nEr\xea\x0b\xbd6z\xefH\xd2\xa7\xc3\x92\xf6\x8b&\x1d\x00\x9e\xf4\x89\x11\xa5O\x8e)\x1d\x08\xaat\xe8\xb8\xd2A!K\x9f\x0f[\xc2\x9d\x93\x12\xe2Kh\x84 W\xb2\x84\xa1J)\x83\x95P\xe1J\xc8-Y\xc8 \xdbi+/\x87:(l\xc9\x13\xb8\xe4\xe3O\xa3\xaa\x9e2| \x13\xc0\x14V\xac$AL\xfe\xa6K\x15\xc8\x944\x94\xc9\xd3\xfa\xb8\xcd\x9b\xd1\xe1\xa6\x99\x8aa\xc3M!Y\xa6\xe3\xe4\xc8\xbfw\x9d\xcf\xaf\xf9!\xd6\xdc\x03p\xc56*\xec\xfd\xc5\xc6\x9b[-\xaa\xf2\xf9\xc9\xe4Ra\xac\x9f\x110N%\xbfCiZ\x97\x1dG\x06j\xae\xc2Y\x19\x0c\xa5\xeb\xf0\xe0\xec\xbd\xf8\xc4\x96\xd5\\\x93\xbb \x10\xb1\xb9\x1f\xb6\x9aX\xdeYy\xd8\xd6\xb9\xd6\x17kJ#\xa9\x7f\xad\xab\xbaw\xa0\xb9\x82q\xa6\xea\xd0\xb6\x19\xffa\xb39\xf4^d(\xb0\xb2\x19Z\x89xgl\xd9H\xfb\xf1\x8e\xb8\xa3\xb9b-\xc1?\xad;\x8b\x02\xf5\xe4\x97\xba\xab\xbf\xf7\xd4g\x12t\x91\xd8:\x0f\x1a[\xc0\xae\xb0d\x1ft\xad\x83kk\xe8H\x8e \xda\x1e\xf2\xdb\xae\xfb\xe7\xe0N4~\xb8\xc9\xfb\x0d@|\x07\x82@'d\xfb#'\x89\xe4\xd0\x93\x1f~A\x16\xdf\xb7\xeb\x93\x92&\xaa\x17\x82\"{\xc1\x1b\xdd\x0bH\x974\xc2)\x8d\xeaaR0\xfdL\x8a\xaf\x89\xa4 \x1bJ=\x1c\x13\xf9\x0b\xea+\x88\xe8_\x88,\x907\xa4\x14\xc4!\xb9E\x84\n#\x14\xe1\x82\x89\x01\x1fP\x0c\xa1\xd5F\x8ez)!6\xf2\x1e\x8e\x11\xd1\xb6\\\x06!\xc8\xfe\xa0[)\xfe@c\x085Sh\xcd\xd1A\xc7\x08}\"\x12\x1b\x1fx\x0c\x01\xc5M\x19\x80\x0c\x81A\xc8\x1eU]\x88\xb2?\x10\x19\x10\xc1\xc8\x80\xb6JhP\xb2\xbb!\xf8\xb4\xb6K`2`\xa7\xfe\xe3\x80w\x17\x8ft\x04c\xed\x0d[e\xb4r\x0d\x10.\xa4&\x84k\xb0\xd5\xd8>\xae\xc1\xe1\xd8hG\xc6\xc1T\x1d\xdaB!p\xf2\x01Y+\x86w\x80a\x1eDZq\x995K6O\xe5\xa9\xdd\xbf\xf9\x06\xe5\xd5=N\xfe\xc9\xed\x8e\xe0\x92\xc0`n\xa2\x06\xdaL\xb1\xf5O@\xd6\xc0\x0f\xb7\x00\xca\x9c\x94\x90Z\x99\xe9sy\xe8\xb26T%&\\\xc8\xe7\xbd\xc3k\xde\x878\xe3\xaa\x8cZ\xe9\x00\xb9\xdaA\x10\xb3N\n\xb2\xff\x00~\xa8A`\xc3@\x00\xe3\x0e\xad(t-\x84\x18\xf6\x9d\x94\xc3\xb4\xa0o\x8d\xf4*\x0c\xe1\x96IA\xf0\x94\xfe\xa0\xb6\x8c[A\xb7U\x19V\xd1@\x0dam\xe0[< \xcc\xe4\xbb\xd9\xd1\xbb\x94x\xf5\x052\x1e\xa5\xec\xb5\x13\xeef\x91\x9dX\x81&\x85(n \xcbm8\x81\xff&\xb2R\xd4\xfd\x910\x83&/o\x0b\xe7\xe9\xa4\xd9B\xf4\x9bj\xc5\x80}h\xebl\xc0\x8c\x93\xfe\xc3\xbc\xdd.\xae\xbf\xb0F\xba\xa3t\xcd\xdcC\xb9Y\xdd\xb0\x1a\xaa\xbaK\xaaa-\xecD\x81\x11\xca\x1f\x19\xb8?\xc1\x1b\xb4=\xca\x17}z\xb2\xc7\xdb\x14M\xf1\xcbvg\x1cUW=%\x9d\x84\xb3j}\xdf\xef%\xe5\x0f\xc2E(\xe3\xe4y9\xdb%kt\x82\x15\xc3r\xe7\xdcl\x04\xa4 \xb1n,\xbcc\xc7\xdf\x98`\xf8\x18&w\xd1X0\x99\x8cF\x89\x85t\xbbbJd\xa3\xd9\x98\xd3\x0e\x8dE\x7f\xa7\xc1e?:\xb6}\xeb\xbc\x15F\xc9\x8a\xa6\xd2@\xb0\x81\xa11.\x91EU\x8a\xbcH\x06\xd5\xa6\xd4@\xae^sSU\x05\xb30\x01\xba\xd5\xde\xf1\x0c\xaek\x19\x13\x16\x89\xe1\xb5(\xb2[1 \xbd_2\xd1\xd3\xc6\xe9\x8b,\xfal\x9e\xadf;\"\x0b\xd5\x87\xfa\x1cI[\xe9\x8a\xb4\xb0\x0f\xb2\n\xce\x13\xd2\xa7\xb4\xe9\xa4@vs\xaa\x07E\x0d\xad\x89\xec\x85\x8aE]\xad\xbc\x86\xe8\xdb\xd22\x97\xc2'6\xc4\xa4@\x98~e%Pw:J\xb8\xaf6\xf5$k\x96\x14w\xa9\x8c!|\xa8$\x97\x13E2\xe5\xa59\"\xf0\xe2\xf53JxI /=\xcb\xfe'Nx9\xdd?\xd8:\xe2d\xc8\xc4n;\xd2l6\x1c\xcb\xc3h\x19\xdfq\x0b\xb1\xf3\xc6!\xf1v!\xaf\x05&\xcb8\xd2\x02Q\x8b\xff`\x99\x1f)\xb3,\xf9\xfb\xac\x7f\xea<\xb9'r\\\xc8_\xb9a]\x99sMx\x7f\x9f,\xd78E\x8dR\xe5J\x08_\x9bj\x94S\xd4\xa0Z/\xd0\x07\x9bR\xd4\xe4\xd1\xb0m\x05v?\xce\xbb6\xce;\xc5\xf9\xe3\x9c\x06\x80\xdb\x03 }\xbe\x89C/\xd3%\xeep\xfa\x07S\x85\\6S\xf2\x89#\xe0\xd2\x17n\xe9\xdaij\xf1\x12\xa4\xbd\xbdH\x8a\xbf/I\xf1\xc1\xc6\x80m\x14\xf5h\\\x80e\x8b\x895\x82\xa8\xa2 \xbc\xc8\x89\x02+\xb1a\x95j.J\xea8G\x8dd)x\xbb\xb4I\x82)\x83C)1\x81\x94\x01\xa6 \xabo\xba\x10\xca\xe0\x00J\\A\xd3\x06O\x86\x84N\xaa\xef8\xf5\xe1\x02'\xfda\x93\x18[\x84\x86L\xfa\xa6\xfa\xdd\x02&\x11S\x18\xaa\xd3\xe2j\xde\xa6\n\x94\x8c\x0d\x93D\x05Ib\xea\x12\x1e \xe9\xba\xd95\xf0nWd\x9b\xf8\"\x8db\x03#\xa3\xc2\"#\x82\"\xe3B\"w\n\x88\xc4\xf9w\xc1g<\x97\xd5\x13\x84B\x06\x07B\x1ej\x18\xe4\xc1\x04A\xfe!B [D\x00dP\xf8c\xc2\x9d\xbdwO\xe3\x9b\x8c\xec\x81d\xf8\x97\x90!\x8f\xe1\x01\x8f\xe1\xe1\x8e\x11\xc1\x8e\x81\xa1\x8e\x11\x81\x8e\xc1a\x8e1A\x8e1!\x8e\xc1\x01\x8e\xf1\xe1\x8d\xf8\x19\xde\xde#)\xb3wDP\xe3~C\x1a\x0f \xa0\xf1\x13\x873~\xf2`\xc6\x03 e<\xf4@\xc6\x83\nc\xfc|A\x8c\x98CT\xd2\x00\xc6\xae\x91|\xe1\x8bI\x82\x17}\xd5\x8b\x08\\t\x1f\xd4w\x0b[\xc4\xadz[^\xe362d\xd1\x11\xb08 \xab\xd95\x1d\xb2A]\xcfK\x0d\x89\xe9\xd8%P\xd1\xeb\xdc\xf6\xbb\xb5\xc7\xf5\xf0m\xd9S\x00$8\xfbB\xc2\xd0D#\xb5\xc0F\x90\x9c4\xac/\x08\xe3\xf3\xdb\xc5\x17j\xe1!r\xe3B\x85&\x9f\x0ca\xb5\x1f\x80\x85b\x02(\xbc\xa1\x13q\x96C\x85\x1f\x1e\x80\xc9\xdca\x87\xda\x14\x0e}\xd1F\xfa\xa4\xb9\xa1\xa7\x1fvF\x88\xa4\x1aL[Q\x13\xee@C|m\x8c\x8a!\"\xc8pD\x14\xda-\xc4\xd0\xbbF\x01j\x9d\x82\xe0\xe0BTO\x01\xec@\x82\xa0\x86\x80\x80\xa0B\x1f\xf5_K\xd0*\x06\xb1\x01\x85\x87f5\xdf\xea\xe6Q\xd7\x06\x06l!\x02\x9a\xfe`\xf6\x8bY\xfb\xb6\x15\x85\x87\x0e\xc6[\xdd\x1f6\x886\xf2.\xb6\xc3,\x07{\xed} ;\xdb.v\xd89L\x10\xd1%c\xa2\xee\xd2\x04\x086[\xf8uPx \x85\xd1a\x8f\xf5\x01|\xf6^\xd20\xdb{i\xec\xbc\xe7^(\x8cn(\xf6\x93J\xa2^2C\xc4\xabu\xbe:\x98\x98W\xeb\xdc\xf8y\xa2^m\x93\xee\xa7\x88{\xc5\x1e\xd2\x92\xc6\xbe\x06D\xbf&\x8a\x7f\xc5T3\"\x06\xd6\xa3k\xc7(X\xfc\xea\xb8\xe5\xf3n##a\xe9\xf2\xce]\x01\xa0q}\xfc\xc7\x01\xef.\x1e\xe9\x08\xc6\xda\x1b\x12F\xc8\x06PO\xa5L\x1a\xdb\x1fYt(6\xf2\xc5\x139\xfb\xe5\xb6:\xb4\x85\xc2B;\x0e\xc6Zq\xd1C\xde\xd8\xa1X+\xa2\xa2g\x0f\xc8|ty\xe7n\xf1\xb4\xf8\xe1\xb6\x15dD\x97wv\x82Y\xed 8\xbe\x16\xdd\x7f\x00?\xd4 \xb0a \xce\x16\xad(t-\x84\xd8X\xdbC\xb5\xa0o\x8d\xf4*l\x03\xe3\x1eQ1\x82\x7fP[\xc6\xad\xa0\xdb\xaa\xc2#p\xc3bp\xc3\xa3p\x83L\xbe\x9b\x1d\xbdK\x89W\x1f]\xde\xe9Q\x88\x8c\xcb\xc5\x95;Ml\xee\xce\xd1\xb9\x98\xc2\x1a\xe9\x8e\xd25\xd3_\xde\xb9\xd8\xe7\xe5\x9d\xd3R\x9b\x9f\xb2t?g\x87\xf3\xd7\xde\xf1\xadQ\xef\x9a\xde \xe43\x00\xb2N\xe6If?u\x1a\xce!\xe6\xa5\xde\xa0K\xbb!\xb2\xa6\xe7D\xb3rV\xcd\xd9\\\x15pZS\x8a\x08w\xccf\xd3\x8faH\xf2c\xc1P\xe6\xfd\xc1V\xbd\x8c;\x80\xe5!\x8a\x08\x1f\n\x05\x1fwOR\xf0qX\xf0\xb15(b\x12\x95\xbc5\xa7\x9ab\x94'\x0fY\x83\x94\xedAA\x14\xbd,\x85\xa2\x97)zy\xb7\x05\xd5\xb1\x8cR\xf4\xb2\x14cO\xa1(Z\x8a\xa2\x1dN\x01:\x8a\x16\x13D\xdb\x9cd\x85\x9a\xd5L\x91\xb2\xc6%\xaf9-\n\xa8Y[\xe7\xec\x8e5\x90\x15\x85\xe5,\xd4\x18\xe3\x1c\xad:\x1fh\xb3\x1chP\xa3\xf9Lx +\xd4\xa8L\xee\x85fgT\x1bw\xdc\x93\x82X\xdb\x00\xefMM\x1e\xd68\xecH\xfe\xefz\xbf\x82o\xc9l\xd9\xf1%q&q\xd5>\xf8d\x92]\xd7\xce\xc6>\xb63S\xfb\xe4bKl\xb7b\xb5\xd4\x96(\xdb\xed\xb3\xfb\xdf\xbf\xe2U\x94\xc4\x0b\xa4V\xdb\xedo\x9a\x0f\xe7d\xe4&\x08\x80$\x00\x82\x00H\x0b\x9c\x95bQ\xad\xe5\x08Z\x10QH\xe2%<\x00\x01\x9e\xd2PY\x13Y\xaaKm\xcf\x000\x08\xdfB\xe5L\x80l\x90\xf4Q\xa3:\x89^q\x93$\xc3\xc5\x12\xbd\xadk\x95$YIq\x16y\xb6i]\xbbf<\xe4\xccz\x97\x1c\xfe\x9e\x92\xc4\x0dV!\xf2H\xa2\x8a\xe2I\xea\x9a\xd0\xf0d\xda\xef\xdc\xccD\x1c\x9dC#\xf7\xf0Np\xbd\x96;\xb5\xc9T\xee\x88\x1a\x1fyV&\xb1tR\"\x99\x86\x13\xc8\xea \xac\x08\xa8y\xd8tX\xb5\xb3u\xc0A&\xa2y\xf3u:\xb1Zcd\xecX\x81\xd6\x01Z\xfd\x82\x9b\xbb9;v\xfa]Gx\x80K\x0c\xe2\x0ek\xd34\xc2\xce\x01J7(\xd7Q\x8f\x98\xe5\xd0B\x15\xcdz\xfb\xd5#T>\x1c\xab\xbc9\x9c\n\xc5&\x07#\x1f\xdb\x00{\xf0\xa9\xf9!\x14\x0e\xbaA<\xeb\"\x0e\x89A\xb6\xf4j%\xa5\x0c\xe5e(\xb6\x18\xc8\xba\xe1< \xc6\x13\x87\xa2\xdcW\xa1~\xa4\x853\x9c\xfa\x15c\x87\x01\xcbkh\xdc\xb0\xd5dpG\x0e{\xa5cK*\xae#v\xb8Ax}\xab\xa8\x8f**h\xd8\x83gi\xbdtU\xed\xb5\x05\x93nC\x03\xf5/\xff\xe6\xa1\x81\xa2-0\xf7<:/\x02a\xb8\xd7P\xf4\xe9!\xe7}pj\xfe\xad\x0e\xaaB\xf6gLT\x93\xf8\xdb\xe5u\xd0\x0e\x0d[\xa1\xb7d\xe9\x96\xef \xe9\x0e2\n`\xccC\x02!1\xe1\xa2\xd6\x96\xa8\x85.\x12\x0e\xcf\xf1\x0dQ\xc1\x97\xef2\xf2H\xaf\xd9\x8f\xbd\xb5#'\xe4&q\x86J\xb3\xc6\x83e\x982a\xbb\x84\xc1d\xb3D\xd03\xde\xa3\x89\x82\xd6\\\x83?\xff\xeb\x9aqm\x9dI\xf5#\x0c56\xed\x0b\\\xca\x9bLcU\xbc\xf3\x1ar\xdcF\x0b\x1ah\xa7\xf5\xdb\x0dR\xe9\xb8\xf4?\xe5\xc5m\xd9\x0e\xcar\x11\xf0\xe6<_\"\xe4Q)`fzTzh\xebh\xc9\xe9R\xe0\xb5\xd8v\xd2k2\xda\x90\xc7\xae\xbd\xf3\x80\xb9\xd4\xd9A -\x8d\xaa\xc0\x998\xcf\xc4(g\xfc{HZW\x92\xfe}\xe3\x0c\xc6\x1e\xfe\xc4\xa3;f|\x9b*%\xda6Uj\x83R\xa5\xb6Y3\x7f\xcf\xac\x99\x9a~\xc3&i\x9e\x88}*u\x80;D:=L(C\xbc\x1c-_\x86 \xce\xe9\xbc\x18\xc2\xc8\xce&\xb3\x9a \x16&\n\xfc\x80\xcbh\x88\xd3\xc2\xef\x9a\xe8\xeb\x8c\x08\xf2\xb3 ~\xcd\xccm\x99$\x16\xf6r\xf7\x01\x90\xbb\xc3\\\x0d\x86C\xa1\x01\xcd\xe2\\\x18\xe4N\xb0:\x0d\x9e\x99\x8b\xa6\xbdU\xf7\xf7\xf1\xb2\xaf\xb3\xa0\xeb\x12h\x80\xb3\xba\x07\xfa;\x04\x9a\xa7\x9b\x06\xb0\xae\x0b`\xd0\xa1\xdf8\xda7\x80\xd9\x8f\xf9\xdd\x83\xfd\x90\x89]I\x88\xcbC:pZ\x87\x1c\xe9\xe5\x1ei\xc0 \x1d\xe2\x83\xc7\xf6u\xf2\xa9o\x8ah\x9aG\x98\xe6\xc5\x1e7\x9cd\xcf\xc0K\xaag\x97\x7f\x88N\xe7\xbc\x8f~,\x15\xa7\xa9\xe5VE\xda\x97\xa8\xa4\xec\x98'\x87Ce5\xd1\xf0\xe6y\\\xa5\xda\x1ajd\x90\xb6\x86\x92?\xd9\xd8\xbcQ\x93\x89fk #~\xd4\x88l3\xb8&\xe5A\x93'u[\xc1-1\xc7\x8f\xd7U\x91\\\xa7$\xbb\xa1\x8e\x00F\xffi\x065O4\x9d\xf3\xf4k\xbb\x7f\xfe\xdb_\xbb\xfa\xb1jm\xbe\xe1~\x82\xb6\xc0\xd8z\x07D\xdbz\x076\xc8;\xf0\xf7<\x1b\xf7~\x97]\xd9\x0be\x94/\xc8\xde\xbf\xf8\xff]'\xf1\xff\xc1\x1ea\xd7\x92\xa0\xfc\xc7\xf2\x92um\x18\x0fg|\xad]2CA\xfe\x0c\x91\x8c\x16\x89J\xa2\x12\xff\x9b\xd6f|\x99\xdcd\xa4xS\"\x19\xdd\xafN42L\x8a\xc4\x88\xe3\xe77-\x14*\xf2G\x1bk\\H\xceo\xc8\xf6\xe6\xb9f+\xecN\xe9uWy\xcd*\x18\x9c\xcd\x1e\xc9\xe2E\x9ed|3\xb1ab\xd7\xbd\xb4\xe4\x08\xb3jV\xc7$\xd5+N\x8e^\x15\x89\xb5\x07\xc9\xa2b\xc9\xe7\xde}\xc9\xd3g`\xce\xc9\xd2\x00\xcb\x1d\x1f\x92!\x96\xae\x10S\xe2w\xf5hl\x86\xd4N2\xcd\xf0:=fG&g\x05\x12d\xb0\xc0\xf2\xad\x99\xd8&1\xfc\xb5S\xbc\x14k\x1e\xda\xef7\xaa\"A\"\x8a\xf2M\xa3\xf2\xa9\x89\xe1k\x0fmTr\xd1gS:\x17\xc7\xd6.\x85\xd9\xa5Jr\x8f`\x99j}\xb4\xb5ME\xdb\xda\xa6\x1bd\x9b*ib@\x1b\xad\x96\xdc\xd6\xee\xede\xf7VE\xb2\xf7\xaf\xaaH\x06\x18\xbc?.N\xfb\x99\xbbrL\x0d\xac*\x92\xa0)\xfb\xe3\xe2T\xfedk\xc8\xf2\x16\x92\x05[Cvk\xc8n\x0dY\xe7\x06q.\xc9\xad\x0d\xbbMiq\xc9\x8cmJK\xff`\x0f\x0f\xb8mJ\xcb6\xa5\xa5\xdb^f\x9e\x86\xc5\x99x\x00nSZ\x1a\x0d>\x13}\xa3T<\xa0\xb6)-\xdb\x94\x96mJ\xcb6\xa5e\x9b\xd2\xb2MiyE)--\xbf\xd78\xd7\x00?.N\xb7\x97\x00\xb2m/\x016\xe8\x12\xa0\xe9\xc6\xdb\xfa\xffW\xf2\xffosB\x86\x06\xdcosB\xd6\xc8\xdcp6\xc36'd\x0c.nsB\xa0\xa7f\xb4\xcd \xf9\xff6'$x\xd9\xfd/~Y\xe6\xbb\xea\xd6\x96s}\xb3\x9d\xd9.\xb6'Kn\xf8sxo\xcav\xd5Q\xfbm\xf6/\x8a\x0b\x9b}\x8f\xed\xb2f\x07\x1d\xb4=\xb7\xd1A\x8br\xf5\x9b\xe8\xe0=4\x14\x07\xe0\x1d4\xe4\x06\x1a:d\xbf\xdb\xe7\xb0Cf\x85\x9bgk)\xe9\xd1\xee\x9d\xff\x0e\xb7\xce\xc3\xb7\xc1\xdf\xfe\xde\x19\xe82\x019K\xda\xee\x11tq\xfe\xa5\x05p\xeb&\xd9\xbaI6\xcaM\xc2E\x87\x01j\xeb(Y\x8b\xed\x180\x1d\x03\xaf\xcd\x9e]\x9e\xa4\xa9\xf2\xb9\x02\x83\"\x1d\xc6\xa2\x01\xe8\x17E\xfaf\x1b\x8c\x1b\xb2\xb1\xb7\x81\x8f\xdb\xc0\xc7A\x06\xe8\xdf\xc3\x04\xfd\xdb\x1b\x92\xa2m\x03\x18;\x0d \x18\x00W\xde\x08\xcc<4\xec\xc2\xc1\x03m\x1b\xc0\x08\xb9EG\xbdfh\xc8U\x86\x07\xdc6\x80Q\xb4\xd1\xe7i\xd8\xa5\x88\x07\xe06\x80\xb1\xd1\xe03\xd1\xf7J\xc5\x03j\x1b\xc0\xb8\x0d`\xdc\x060n\x03\x18\xb7\x01\x8c\xdb\x00\xc6W\x15\xc0hx\xaf@\x1e\xf9\x16\x00\xe5\x9f7\xbdi\xdb\xe0E\xd1\xb6^\xf9\x0d\xf2\xca\xff=\x9d\xe7\xdb(\xc3\xa1!\\\xdb(\xc3527\x1c\x1f\xb7\x8d2\x1c\x83\x8b\xdb(C\xe8\xd1\x16m\xa3\x0c\xff~Q\x86\xe2\xdal\x96,\xf6\xfe%o\xa2\xa0EuTG}s\xcc\x162/\x92\xd4x\xaa\x97\xef\x924)\xc5V\xb9I\xee\x89\xbe\xf3B\xb8>z\x92\x84\x9b\x11\x181\xc4\x98\x88\x16\xd2\x84\xa3g\xbftV\xe3\xcb?n\xec\x85\xb3\xa8\x1bUUI<\xae\xd9\xea\xb0\x0c[4r\xce\xe7S=1%z\xcbP\xe9^7vf\xe7\xb5\xdf\x1dJ2\x06\x9d\x8f\xb7\xf7\x8e\xbcm\xef\x1d;\x0dp*\x03x\xaa\x10\x98yh\xd8\x11\xc4\x03m{\xef\x08q~\xa1^34\xe4p\xe3\x01\xb7\xbdw\x14m\xf4y\x1avL\xf2\x00\xdc\xde;6\x1a|&\xfa\x1e\xb2<\xa0\xb6\xf7\x8e\xdb{\xc7\xed\xbd\xe3\xf6\xdeq{\xef\xb8\xbdw|M\xf7\x8e\xca\x81\x01\xbasT\xb7\x8c\xda\xeb\xb2\xcd\x02\xda\xde7Z\xfe\xbeI\xf7\x8d\xdd\xe0\xfbm\x1e\xd0J\xde\xdd\xedU\xe6\xd0{\xa2\xedU\xe6\x1a\x99\x1b\xbe\x84\xdb^e\x8e\xc1\xc5\xedU&\xf4\xb4\xbc\xbd\xca\xfc\xff\xf7*\xd3u\x93\x19zjp\xbe\xd7p\xff[\xfef\xfb\x18\xaeh\x1e\x9b\xf6\xb5\xdd\x18\xfe\xedo\xfd\xfcX\xf1\x0d\xd8\xe3\x19Zyd\xad\xdf\x9bm\xc1\xdb\x9eW\xb7\xe7\xd5\x8d:\xaf\xfe=\x0f\x95\xbdC\x86\n\x12\xe5E\xbc\xf7/\xf1\xff\xd7\xec\x94\xef\x8b\x19\xba\xe0?c\x96\n.\xa2\x99|WK\xf4\xd5\xe2\xdb\xc5\xdf+.[\xf40;\xcc\xc4\x91\x0e\xe6x\x07\xcd\xab\x922\x13\x07\xa3 \x89f\xef\x0f\xe5O\x95\xdfa\x07\x91w7\x865)\xfezpwxC\xf6\x9f\xf0\x13\xad>\x1e\xd1\xc7\xa3\xc7\xa34\xbd?z\x8c>?\xd1\xf2\xe1\xc3a|w\xf83\xbe\x9f\xc7\xf8\xa9zx\x8ap\x1c\xcff\x9fn\xe6\xd5\xfby\xf4D\xde\xbfc\x08ix<\xa4f7\x89\xf9yO\xc6/MT\x08TU%q\x1b\x81\xcf\x07\x9f\x7f\xfb4\xc1\x87\xbbG\xd3\xf7G\xbb\x1f\x8e>\xe3\xddO\x1f\xf1o\xbbS\x12\xe1\x83\xc9\xfe\xd1\xc1!\xd9G\xfc\xd11\x01\xa3IGc\xd8\x83\xbb''\x15w\x8f\xe9\xed\x03I\xdf\xa1\xcbd\x9e\xa4\xb8H\x97;5\x0e\xdd\xd0OE9\xbb\xbf[N\x8b\x9fQ\xd1\xe4%\xdb\x80J\x99\xf0\x7fk\x1b\x80\xcf\xe82\xaf\xde\x14\xc6\xb9\x83\x89\xd3\x82\x94\x94\x9b\xb5\xa6\xd5\xfao\xect\xc7\xcf\x08\xd8\\8\x0ct\xbdn\xb8A\xc1\xce\xd5\xa9\x1e@\xba\xc9\x8d\xb5\"\xac\xe6wv\xd0\xea-\xc0&\\\x9c\xa6ja\x0bk\x9b\x0d\xc3g\xb2/\xf8z\x82\xde\xb2\xc9\x90\xa3\xed\xd5\xdf\x7f\xb5\x8e\xd6\x9e\x1f\xed\xfdw\x8d\x875\xe3\x15\x15\xaa\xf6\x84\xa6\x10g\xf1^\xbd \xae\x93&\xffZ\x9b\xcb6\xa0\xfe\xc9 \x9a\xe0\xd84\xf7\xea\xa3\xc9\xf4\xd8D\xec\xaa\xb9F\xb9@\xe5\x0b\x13g1\xcar+\xf7\xdf\xb5\x00\xc8\xdb\x0c\xfa\x90\xb3\xe5\xcco4\xb8\xfb\xc0\x90%&I\x0c\xb2\x02\xcb\xff\xcb\xdc0K\x14\xe7\xd9\x1b*9>\x15\xd2\x9co$\xc6<\xf9\xba\xb51\xfcI\x87\xab;hRQ\x94\xe5\xd4\xc2Ys\xbam`\x18j\xe6Rfd\xe9\xd9b\x7f\xd4{&\xceI\xc9\xf0\x9cc\x1a\xcd\x1a\x87=\xa3\xbf9#\xffX*\xa3k\x07\xd5\xd1\x9b\x9c\x17\x02\xa5R^ Q\xa5\xbcL\x0c/\x89\xfe|\xad{2\x8a\xf4G\x05D\x1dP\x1bJP\x0e\xa7\xc15\x17\x9a\xbd\xd2\x90\xd4%\xbf(\xaa6\xf4\xd4\xc5 \x0b\x1c{\xacF\xbf\x8c\xd2\x14r\xf0\xa1\xc0\x8b\x85z>]l\xbaY\x9e\xc6\xa5>\x01\x89\xbd\xff\xd6z\xc9%\xb7\x19\x89\x7f\x1d\xf5\x88\xe4\xa0\x0c\x85\xa0\xa2 d\x84B\x0f6#\x98\xd1\x07\xb8\xb0e-t\xcb\xc8\xda\x8f,\xb9\xab\x08:\xfd]\x1ei\x92R\xeev\x84N\xe7\x8b\x94\xccy\xa1\xc92\xbe}w\xe2\xac3\xa4\x1aWYS\x1c\x89\xe3QU\x12f\x1a\x16Z\xc9\x13\xb1\xd5\x94Q\xe7\x8f:\xfa\xc2\x1d2.&\x8aw\xf7#\xbei6\x88\x99\xb5\x80i`(]\xe6yFq\"\x82\xc8=0\x1a=\x85%\xc9\xfd5D\xcd\x05N\xd3\xfcAj1\xae\x14\x9d\xe7a\xa4\xd6\x9b\xe3\xef\"0>\xc4<\xfb\x81J5\xe7\xc1J\xb5\xe0\x9e\x11-\xbcsD\xf3\x86<\xd7\x0d4\xef\xf2\xa7\xb2\xbe\x98\x0c\xd8\x97V\x99*7\xf6V\xd5\xbf\xfa\xd5\x0b\xa6\xc8SO@\x91h\x90\x15$\x1a\xe6\xf0\xea\x1d\xa9\x90\xd1SN\xf8j\"\x8f\xd4\x1f\xee(\x1awv\x15y$\xb6`Uz\x83\x86P?\xe6\x91\xac\x9a\x87\xe8f'\xae\xf3\x93\x8b\xab\xff\xb9\xbe\xfa\x9f\xf3\xaf\xd7?\xbe_\x9e\x7f\xfdr\xfa\xed\xf4\xeb\xef\xfd:\x9e]\x9c\xfe\xc7\xe9\xf7\x93\xab\xb3\x8b~\xfd.\xbf^\xfcy\xfa\xe5k\xcf^\xa7\xdf\xff\xfcz\xd9{\xac/?.\xaf\xce~?=\xf9\xde\xaf\xdb\xd9_\xdf\xfb\xe2w\xf2\xed\xdb\xe9\x1f\xa7'W_\xfbu;\xfb\xef\xef\xa7\xff\xf8q\xd9\xaf\xd3\xf9\xc5\xd9\x9f_\xbf\x9f|\xff\xd2s\xb0/g\xdf\xaf.\xce\xfe\xf8\xa3/m\x7f\x9e\xfcq\xfa;`\xa2\x95;m\xd8\xf2\n\xdb*\xcd\xe6Z\xc5\x03\x87\xe7\x972\xecl\xca\xbd|Q\x9e\xc5 C%\xd8\xcb\xb5)\x8e\xed\x9f\xe5(\xb8\x14\x97\xa9 \xbf\x8f\xf1\x86z\xda\x86Q{\xe8\xd8\xf6\xb1\x0e\x88\x8b\xc9\x84\xa2\x92\x14\xf7I\x94d7hZe\x11Wc=GS{\xef\xd8\xf6Q\x1c\x96\xb8\xb3<\x89P\x92\xdd\x93\xb2?=z\x9f\x1e[\xbf\xaa\xa9\xc9hB\x97B}k\x1a\xa3\xaa\xa4y\x9c\xe0L\x12\xaa\x9eQg\x0c\xeeK(\xdf\xf7\xc7\x9d/\xfa\xd6\xb2\x14\xf2\x7f\x81\x0b\xba\x948q\xa5\xad\xb4\x14\xd3\xbe=\x87\xd4r\xe3\xd8\xfaUpW\x0c(\xce\xc9\x19\xc2\xd3i\x92&\x98\x12\x84o\n\xc2\xcd\x90\x9e\x83J\xa9sl\xf9&\x06\xe4V\x0fN\x85s=\x9fJ\x04\xb4\xddT\xe4\xa9 ~\x9e%\x93\xaaD\x13\x9c\xdd*\xad\xd8\x13\x95Z\x96\x1d\xdb?\xabK\xd6\xd6\xed\xb1\x9e\x06\xa3\xfe:\x9b\x02\xa3\xce\xa58\xe9\xceq!\x91\xc6\x11E8\x1a\xb0\xa9k\xb9yl\xff\xdc\\\x9f\xa20g\xcd'mC\xaa]/M\x18\xf46!\x88d9%~c\xa6\x8b\x90\x16\xc8\xc7\xd6\xaf6t\xeeq\x9a\xc4| \xcb\x94;\xbe?4.^\x04T\xeeT\xc8\xb2\x08_T\xd5\x0dreU7i\xabi\xd7\xb6\x9e\xfe7%*\x93\x9b\x0c\xd3\xaa\xe0\xa7X\x85\xaa\x07\x1e\xcc\xee;A\xe7\xc6.W\xd6(\xdb\x82{I\xc6\x04\x1e\xe7\"\xb3\x0b\xbd`\xba%V\xd5Rtv\x83\xeb\xc1+~*g|HHYo\x04D\xf3\x05J\xc9=IU\x89Z!\x9e<\x90\xd4\xc9^X\xb4\xef\xda\xa0\xb9\xdb\x9b\xf1\x19\xe1l\xa9\x0e\xfa>\xe1\xca%\xc5<\x8f\x93\xa971\x80\xc7\xc3\xb3\xd3\x94aI\xab\xe3\xae\xc0\xa0yV\xf5\xae\xd3*\x13\xfeG:\xd3x\xa7 w\xd8\xe6\x99\xe9\x84\xb6\x06\x18\xb3\xc60\xb9\xc6\xdc[\xf3,\x87\xb0\x80E\x0f_\x07'\x9aI\xf2\x00*rp\x19\xc7pEgy\x91< \xf9Y\x90\x88$\xf7\xbe\x85\x90O\xa7\xbbB8\xf1y\xe9\xa6\xecj\x87\x84\x03\x08\xbf@\xbb\xe6\xeb\xee:x8\x1c\x9b\x0f\x99Q\x8e\xdaTUl\xf6E\x80U\x8b \xef\xda4|/\x97\x14g1.\xe2F\x19e\xa1\xefJ\xee\xbd\x9c\xe3\xe2\xd6r\xd9Z7\xf5ko\x86PAPY-\x16y\xc1\xf0\xd3\xc7M\x8e9\xdf\x11L\x18QZ$\x93\x8a\x124\xc7K\xee\xbd\xf7\x00\x9c\x10&\xdd\xb3\x1b^\x89\x9bsA\xea\x04\xa5H\xd9\xde\x88\x98\xddb\x89H2\x9bt\x12]s\x99{]\xe4iZ-B\xd3\x1a\x92\xecP\x0d\x00\x9f\xff\xbf\xa4z\xc0i\xaaE\x80\xda\x11\xb5\xa39\xa1\xa5\x16\x05\x1e`\xea\xaaO \xd5\x06\xa07\xa5\x12\xad\xd3\x84\xa4\xb17\xf9JL[Z\xe6\x88dx\x92\x8a3?\x8f\xa8\x94\xba\xea\xdf\xb9\xa7Z (\xa0\xfa\xd6H\xed*W4\xda'\x0d\xc6\xb6K>\xaa\x8a\xc3\xc1\xa8\xc8s*n\x1cH&\x1d\x87Le\xa5)\xe1\x86S}\xa3\xe1\xc2Q\xd5}\x17a\xa3\xf2\x1e\xc4\x83\xa8r\xc0^'\xd94\xb7/)\x18%\x0d@\xb5w\xcfL\x02\xc2\x93\xbc\x12A\x87I\xbcW;\x98\x1c\x00ked\xfdA\xd0\x91\x16v\xa1=\xa7\xf3\xb9\xc1D\xf3:\x8b\xdfy\xe2\x07\xde[\xbb\xdb\x1a\xb7\xc1.\xa1\xa09\xbe(\xc84\xe9\xe4w\xd5m\xcdT\xc8\xf1\x151\xf2\xbf\x98\x14\xd5\x99_D\xff8H\x8c\xf8GU\xbd\xc0\xb4x\x85\x9b\x05AE\xb1\xf1\xa5I5\x00\x9c\x9f\x1fl\x05\xac\xc8\x86\xbe\x14\xaa\xcbs~\x98\x13A\x16b\x04tO\x8arD\xdaF\x98\xe2\xbe\xb4\x99\x93\xc6\xff=#\x8f\x8a:0U\xc6\x02p\x0bTv\xe4\x1cO\xaa\x9a\xd0\x02\xa2\xd5\x01\xa7!p]\x173k\x16\xb4\x92\x8a\x15\xe7|-\xdbZ\xa2\xe6\x97\xc7>(\x8d\xfb-\x90\xdcVs\xfaB\xc2\x1b\xce\x14\x88x\x87\xc2\x82\xb1\xc4\xf8\xaf\x11\x84\xc4Zy\xd3B\xb5\xa9\x11\xf4g\x0f8\xab\x9a\x84s\xeb\x05\x14\x84\x1ew\x14-\x01'u\x84\xa50\x88\xd4\x11\x94\x86c\xb5\xb4\x89V\xa7\xa2.\x95>\xaf\xc7\xbaby%6.\x96\x07\xd5Bx\x04T\x8f\xe2\xd5\x0b\xc0\xd9\x05nux\xb4\xc4\xb8\xe3\xf6Y~\xdc\x9bvW\xb1\x83\x92\x92\x0c\xda\x85n\x89\xa5x\xc0\xfe\x9b\x16\xe5\xbf\x8f\n\xa2\xbd\xf7\x92\xf1\xae\x8d\x87\xd4\xa11\xcc\x1e\xbf3\x0e\xe0\x8e\x03-&\xd1 KJ\xb4\xa0\xf3K5\xe0\x1c\xcb\x1f\x8f\x11\x1d\x01\x8b\x8f\x80z\xcaE\x1b7Fb@\x94DOF\xc2\"%V\x88\x95\x18\x1e-14^bh\xc4\xc4\xe0\x98\x89AQ\x13\x83\xe3&\x06FN\x0c\x8f\x9d\x18\x1e=10~b\xd5\x08\x8a~\x12^4g\x14\x05\xa0\xaf\xbd\xe7\xd08\x8ag\x8b\xa4x\xeeX\x8a\xe7\x88\xa6\xd8\x98x\x8a\x17\x89\xa8x\xa1\x98\x8a\x8d\x8a\xaax\x1dq\x15\x1b\x18Y\xf1\xb2\xb1\x15\xf0\xe8\n\xe8\xbd\x9aj\xd0\xfb5\xd5\xc6\x8c\xb1\x80\xdb\x8e#\xc5Y\x0c\x8a\xb4\x80b\xa9\xef\x12\xa5s\x85\xf0\xd2\x13\xa2\xe48\x15\x99\xfdX\xc4G\xf0dqo\x008\n\xa3\xc5SC}H\xc3\xec\\\x18m<\x8d\x87\xe65\xfb\xcc\x8b~y\xcfH\x1eIT\x05\xf7\xb74\xefw\x18~I\x84\xd3t\x89\xa2\x14\x97%\x1b\xc1\xd9Qv\x1a\x81\xdc5\x9c\x81\xd5\x81E\xe4\xd7&$\x15\x97\xf2L#3\x13 Jy\xd1\x1c-\x83bL\xb1\x17^w\x89\x06\x0f\xc1\xb8\x8a}5m\xfb\xd1\xf3\x85\x1f\xbfc4Y\xee\xa0j\x11\xeb\x7f\xd3dNJ\x8a\xe7\x8brG\xbb\xcfD9\x95\x1d?=O\xef\x9bY\xf6\xed\xf6\xfd\x8c\x9d\xbc\xff\x92\x9cn\x15\xf9\xbdhD\x1a#,#\xd0\x1a\xb5\x04\xda\xad\x8eG\xc3\x8a\xa5\"{\\\xf1\x97g\xf6\xcfs\x8e\x97\x0d@}\xc9\xe3 \x88\x80Ok\x0b\xdc\xb0\x88\x08kL\x84\xdf\x1c\x04\xd8P\x10\xfb\xe9\xf9\xaf\xbc\xda\x81[\x8dJ\x0b\xf6\x80\x87f5\x10\xb7ei\xccE(\x92a\xad4\x05\x84\\\x07\xcd\xa1\xe1\x0c\xa8Q\xbb!\xfc\x1bP<\xdb\x86p\xa6\x1b$e|\xa99\xe4\x05G\x1b\x95<`\x1c\x92\xff|\x1d<2\x90\xad\x1d\x12\xc67\xc9'/\xbc\xe6&\x03\xf1\xc9\x1f\xe8\x01f\xce\x10\xaaG\x88\xf5\xe8\xb7kFZ\x08Ch])\xd8\xc3\xb9P<$\xab\xa0B\x9f^D}\x891\x81\n\xab\xdaf\xce\x89\x08@ot\x92\xa1\xfbu\xc9]\xc8L\x82t%\x02\xeaK\x84 \xa1\xda\xa2\x01\x97\x06\x82\xcb \xb4\x9e\xe0m\xd1\xf4L\x85u'z\x1e\xea\x00\xc7\x99\x16\xd2\xc35\xa9 \x0c\xc8'\x986E\x9b\xc7\xab1t+BFV\x04\x90g!\xbd\x81\xfa\xb1j\x18\xfd\xa3h\x10\xd4s\xb5\x8c\xbcH\x86Q\xbe\xa2>A\xf6%d'_]l\x00\x82\xcf\xe1\xe4X\x81\x8ew\xe2\xb2G\xb5\xad\xf9\x00\xd6\xa6i\x04[\x03(B\xa0\\G\x16,\x9d\xfa&x\xa1\x85:\xae\x8b\xa0r\xeaL{XAm\x0e\xa7VVLm\x80=\xf8\xd4\xfc\x10\x92A\x1b\xc4\xb3.\xe2\x92\x7f=\xc0XR\xa0\x06\xf32\xa4\xb8\x80\xac\x1b\xce\x13\x88\xd2\xf2\x82[\x85\xfa\x91\x16\xcep\xeaWT\\\x80\xe5ec\x05\x04_\xe9t\xfc\x8b\xd7J,jU\x84U\x81W\xed\xbb\xcabT\xe6s\x82\xc8#-\xec7\xc3\xe6\xb3\xe5\xd3\xbc@\x89%\x03;\x8c\x93\x1c\xb0\x14A\x14K\xe3)\x0c]\xd0Q\xfdB\xd7tD\xbc\xa4\xa3\x05\x98Q\xe4\xd1S\xd2Q\xfe\xaa\xbbD\xc4\xc2x\xdeL\x01\x81\x8ck\xb9\x8e\xa4\xb4\x9f=*c\xaf^\x1cuX\xb5 \xf5\x1dB\x7f\xf2r\x062I\xdd\x0bKF\xf2[Jl\x9c\xa4e\x8en\xb3\xfc!C\x98\xad\xdfo\xcc\x10\xf2\x86k\xbc\x84\xeb\x18\xc6\xb1:QAm?u\xef\xa12\x14ZY\x08\x01\xd1\xa9y\x8d\xde\xf2\xca\x99 \x9d\xa1i\x92RR\x90\x18\xdd\xde+\x0dOI\x81i^\xb8\xc3\xccd\x0c\xbb\x97] \x02% %o\x1a\x96\xaf\xa2LL\xb5\xb7$\x84\x969\xcb\xe6mT\xce\xe3\xfa\xf2\xe9TF\xd05\x9f\xc0\x802+\xb8(Fs\xc8\x00\xd3+\x80K\x11\x81\xa7\x01\xa1\xce\x0d{'\x18\x92O\x87\xda\xb2Ahrb\xbd\xbf\x9b\xe1r6>\xa9\x8c\x0e\x06\x99\x13\x91\x19\xb5_\xd4Z\x0b\xa6]\x84\x84\"\x1a\x84\x17`\nd\x19q\xcb-\xb6F\xfe-\x1b\xf8\x9a\xfd\xcc\x1f\xc5\xc4\x9a\x0eT\xe3\x95\xe8\x9b\xb3\x19\xe5\xf3y\x9e\xf1\xf1\xfc\xd1\xa4\xe2u\x91\x17a\x87\x18Z\x04\xaa)2\xea[S\xfe\xc0X\xf0rY5]\xfe\x1a\xbd\x15`\x7f\xad/k9\x9f\xf64k\xbc\xb7\xb6\xaa1\xd1\x9bd\xf7\xf9\xadg-%\xd9\xa2\xa2\xaf6\xd5\x0b\xb2\x0bzM<\xc4\xe8k\xb7\xefl\xda\xe5C\xb1\xb2r:\x9b\xf84\xc9n\xd1\x04G\xb7\xb2\x82<\x00\x12\x8fV\xe0I\x1b|\xd1\xf8}]\xba\xd2{\xd8\xd9\xd5\x93\x05@\xf3@4\xf8V\xb1\x88o\xa9\xe8\xeb\xe8\xf1o\xa0\x94\x8b\xaf%\xc5\x934)g$Va\x0d\xa1ps\x88\x1c\xef\xc9\xa6\xbe\x94\xdb\x05~B\"\x9e\x92`X\x15\x00x\x8cQ\xe8\xbc\xc8\x17y \xe7\x81\x16\xcb\xeba\xc4\xb4\xc8\xe7|\xefr\x9c\xa6\xdc\xb7G\x8b*\xe2qE\xdcF\x9a\xe3\xa2\x9c\x05\xc2\xd7\x11*)\xa6Up\xeb\xf7\xe3\xff\xa9\xcezI\xa6\xc2l\xe2\x82\x8f\x8bH\xb5\x0c\x15\xd2\xa0 \xd0 \x19?+\x1eA$\xa2\xe5\xd9\x14\xf3\xfb\xb2E\x15\x16\xcf\xbdx\x0c\xcf\x9a\xbc\xf8\xfa\xe5\xec\xe2\xf7\xeb\xd3\xef\xe7?\xae\xae/\xafN\xae~\\\xf6\xcads\xc18\xbf8;?\xbb\\\x01\x80\xf8\x16\xec\xae3\xf1V%\xa4\xbf$\x0f2\x0f2\x03\x01\x10F\x02\x16 5\x84\xc7}\xe14\x89\xf7\xaaL\x9c\x17\xc5\xbaek\x07\xd090\x95v\x1e\xab\xbf\xb6S\xc5\x8c\x1d\x93!\\L\x12Z\xe0bYK0^\xb7P\x9f\xf9\xc4V\x18\x8e\xa3\xf8f\xc7P|\xb3\xe3\x97\x88\x0d\xdd\xb0\xc0\x16\x05\xb9O\xf2\xaa\xe4\xcf\x896\xb7\xba\x91^\xe5\xc5U\n\x9b\xab\x02G\xb7\xc27&,'}\x02$J'AOe \xe9e\x0e\xd2\xb1\xb3\x19m\xd1,!\xf7\xe2\x89\x95\xbc\xa2!\xb1\x93g \xec\x04\xa4Wk\x15\xbe\xb0\xae\xffO\xa9\xe7\xa9\xaa\xb1*\xd8 +\xd8 \x9a\xe8\xb1W?\x84\xd9rC\xbd\x80\x06\xbd\xe40\x15]j \xd6n\x8c\xfa@\x08\x00Vg:I\xde\xc8\xad\x9cd7\xea\xd9\x9e\x9d)N\xd2\xaa\x00\x1c!\x11S\xc3\x0b\xf1\xb6m\xf0\xc7\xbdf\xbd\x8f\xd2\xbd\xfc\xf1\xc7 -\xd5\xed}~r K\xaeov\xbb\xfc\xaf\xd3\xf3\x01\xdd\xbe\x9d\x9c\xfe\x11\xecf\xe8\xe4\xa1t\x0e\xd3\xc6\x8e\xd1 \x93\xe2\xec\xdc\xd0\xc0\xa8\xca\x9a\x8f\xc1\xbbZ\xcf|\xfa\xee\x94\xb6\x99\xc7\xbe5T\x181v\x13\xd3\xa0\xf5\xfbUC\x86d\xcb\xa1=$\xfbf\x0c\xa9I\xda\x8b\xf2\xacLb\xe5}\xe0\x83\xdf&\xfc\xfe\"\x16Oy\xcd\x93\x92?N/\xf5Q^\xa0\x98\xa4xIb`\x8a\xa2\x03I\xb6\xf8\xdaH\xb2oN\xbe\xd4W\x8bV\xcc\x99\xccp\x85\xa2\xab\x06\x15z\xe2Pu&\xe4\x13\xc9\"\xbc(\xabT\xa3$\xe5\x16?\xc5B\xfc\x88\xa8\xb6<\x00\xd9\xc90\x1c\x05\x0eo\xcb_Q\xfd4/\x7f\x11<\x9f*\x96e7\x0dq\x1d\x98%\xf6Sq\x90Q\x1d\xear\xc5 \xcctx\x1d5\x96\xdaX*\x06\xd6w\x19\x00=[Z\xcb1YK.\xc9\xcb#\x07\xb8\xf0l\x9f\xe8+\x91|\xca-W>\xe5\x98R\x1c\xcd\xc4h:\xa3\x95\xedM\x82#w\x81\xed\xe6\x8e\x91\xeb\x98\x9f\xdd\x83\x89\xdb\x0d\x16K\x8c$\xe7\xf2L:4\xe4g\x99\xedd'Y;\xaeF TjB\x0bD(\x19\x91HN\x80\x8d%\xb0\xe6\x88$\x90\x0fo\x8d\x1bE\x8f\xef\x0c2\x92\x13\x1a\x0c\x1c\xaa\xa7a\x93#\x86\xdaX\x0e\x0f\x15\xd2\x90 ,\x81\x05&n\x02[\xc6\x08W\xad\x97\x0d\x90=\xc2Y\x10\xf4Mn\x02\x7f\x0cT\x15\x83\xa4\xd7\x8f\x7fZ\x0b\x87^ \"\xca\x189\x14\n5\xc6Ny\x85\xd9\x10\x00\xaa@Z\x02\x015\x05\xda&C\xc8\xf6\x0c\xd4\x05O\x07\xdbd\x88A\xbc\x1aC\xbb \xb4M\x86\x80S>\xf2\"\x19F\xf9\x8a1\xa5\xa8O2\x84\x94\xcbc\xa6BX@\x8e\x97\x08!\xed\xeb\xe7L\x83h\xd23\x82e\x01\x14\x1dP~\xa3\x0e\x8e\x81\xd3\x89\x7f\x1f\xf4K\x80hMvX%m\n\x8fVVDMp`\x0e\xad%~}\xfd\xdcZW\xda\xc3@.\xbe\x96#\x90\x05_\xcb9\xc8\x0bi\x04\x86\x85\xf49\x90MC\xe9\x1fE\x93C\xe9\xeedT\x8cyF\xb2\x02w\x9e\x95@\xa0\xb8\xc0EI\xec>3\x05I\x06i:\x04\xd4v\xa8g\xe2\x1f\x82\xaf\x1f\x04\xdfj\xa8\xe7\xc4\xa0\x1e\x89\x80`@}u!\x1a\x96\x10\x886\x96\x83!\x1d\x19\x04H{\xa5v!XB\xd4+\xe5\xe50\x0d\xda\x05e\xd1\xa2=!\xf4\x9b\x83\x90\xf2@\xfdX\xbe\x1a\x1f\x83\xaa$\x08\xafw\xba%\xea\x9dt\x88\x9e\x93##\x1c\x16\x01Kuh\x12\xa2\xb8\x16v\xe7 \xca\xe3\xdc:R\x10\x05hO\xb2\xa1\x1c[^\x03w3\x07yNaw\x9e[\x83\xf0_\x898\xba(_,k3M\xfc\xa1\x99\x9f\x84x6\xa3k\xc8\x80\x1e\xf7k\xef\xa0\xf3;\xb8\"!\xf3\xd9quc\xb5\x0d\xd5L\n%\xb9\x83\xc8\xbb\x1b\xd7\x9e\x12?=\xb8;\xbc!\xfbO\xf8\x89V\x1f\x8f\xe8\xe3\xd1\xe3Q\x9a\xde\x1f=F\x9f\x9fh\xf9\xf0\xe10\xbe;\xfc\x19\xdf\xcfc\xfcT=\xe3\xddO\x1f\xf1o\xbbS\x12\xe1\x83\xc9\xfe\xd1\xc1!\xd9\x17%\xeb\x95\\\x8b\xdco\xe95\xb9\xec\xc3\xf5\xe0\xee\xc9\xc9\xe5\xbb\xc7\xf4\xf6\x81\xa4Vf\x06\x12(\xc7bg]a\xaf7C\x8f>\xed\xbf\x9f~\x9aD\xbb\x1f\xf7?\xfe\xb6\xfb\x81L\x8ev?\x1f\x1dLw\x0f\x0f\x0e\x0f>\xfev\x10\x1d\x92\xa8\xc5P1\xd8J,\x15 \x0e\xee\x1e\x9dL\xfd\\\xde\xa5\xd1\xec}\xf9\xf8\x90}\xf8\xf0\xf3h\xff\xe7\xd3\x0d\xfdT\x94\xb3\xfb\xbb\xe5\xb4\xf8\x19\x15.r\xf8\xf3\xcb\x8c y\x96.k\x16\xa0\x84'\xee\x19\xd7\x078-s\x17~\xf2u\x0c\xab\xb8v\x9f\x95\x83\x93)C.\xcc\xd3\xab\xc8\xdej\xf8\xe4h\x8e\xd2<\xbfe\xd2\xd9\x02E&\xfb\x08\x87\xa4\x0f\x0f\xdf;\x00\xda^\xf3\xfc\x06\xb6\xf2\x1a\xe8\x08A6M\xf1\x0dW-\xfai\x81\\\xfd\x8c\x93\xe9V\xa5\x02\x88\xd4n2\x8a\xa8$\xb5\x06R\xc7\xb9r\x91g\xa55\xcaC\xa3#\x13\xe07\x88Af\xd2~\x88G\xfemS\xae\xc4\"\xf2(\x10\xf2\x1e\xe9\x9f\x93C-\x84\xdc\x0c\x92?\xe4\x14:/N9\x08\x1eX\x14b\x84\x9a\x19\xa7\x85\x82\x9e\x99\x11-\x84 +\xc5\x99o\xacadh\x99W\x854\x95\x9al\xf0c%\xd3\xbb.$\x07Q\x1d\xed'\xfe\x9bqFF\x88\x13\xf4\xcf\x8a\x14\xcb=U\xec\xf7\xe2\xfcK\x0b\x98\xc8,\xad\x87W\xa1\xbd\xc6\xcf\x1a\xd8\x9cd\xa8\xca\xc8\xe3\x82D\xcc\xda\x13oK\xa9\x91\x8d>e4#s\xdc\x9c9\xa7\xed\xe7\xb6\xfb8\xfc\xee\xfc{\xe4y\x94\xc7\x16\xe1+:\xb8J\x9e\xab\x85\x92d\xf4\xfda\x87?\x8er\xe1\x1e\x1cbBq\x92nHM\x0e\xd6\xfd\xba*\x9c\xaf\xda\x04u#Oe\x1d\xdc\xdb\xea\x82X\xe0\x02\xcf %\x85\x81\xf3\xae\xd0\xdf\xa6\xf9\xedZ\x83\xad\x1d1\xd8`\x1f\xd7LO\xb2c\xb4\xc0\xd4\x8c\xb4d{=)H|\x8chQ\x99\xdb\xc3\xca6\xc5\x01e\x0b\x01\xc9\xefk\xb1\x0f\xb3\xd3}\xe6\xe3\x10;\x9cq\xeb\x8e\xc9&+\xbb\xa68-\xe1\xfc\xd266\x94c\xbd\x8d\xf2\xa1\xa6\xb8\xe6Z\x03\x9a\x85\x83c\x9b\xdd+\x18\xdb6\x13{\xc4\xd9j]\x075\xe6 n\x7f\xaf \xb9\x86\xd1\x0c\\M\xc3\x0cm\xcb\xe9w\x88\xed8\x84\xf6\xae=\xe4\xb2\x94:l\x91fn_\xce\xf4\xb1\xb0\x91e\x90M\xe6M\xcbH\x06\xb2f\xa0i]\x1b\xd1\x0dh/\xcb\x81\x96u\x0c\xe4\xc0 \x9b\xda\xb0\x9e\x1b\xc0\x1c\x96\xf4:\xe9\xa7\xf8\xa6a\xbd\xfcS\x8e\xf3f\x8f P\x92\xe1,\"{sBq\x8c)\xde\xbb?\xd8\x13+v\xef_\x86\xb9\xf2\x7f{\\\n\xbc\x11pn\x886\xbb\xcbj>\xc7\xc5\xf2X>\xcaQ\x12\\D3\xf5\x00\xa8\x10\x1d\x8aN\x17\x8f\xaf\x8cP\xd6\x1d&\xf7\x95\\\xdf\x11\xc5\xcaj\xad'~e\xd1|pK\xa1i\"\xd4\xfa\x84\x81\x83\x9b\x07\x02gCs2\xbc5\xac\x16\xfe\\\xa7\xb9\xb4m\x03\x01\xfd\x1fchY\x86\xa3\x06hL\xa5\x8d\xc5\x0c\xc5\x1a\x15\x88U:\x86EZ\x8f\xf8o\xe8t*\x0c\x80\xa6\xce\xaf\xd1\x14\xef%*\xc5U\x10Z\x15\x19\x7fS\xc6\n\xa2\xf5\x0c\x80 \x86\xd8\\D\x0cv\xcb%\x19\x1e\xa5e\xcdC\x06\x91,5\x8e\xdc\xae1\xf8\xcb7t\xc6\xed\"n](\xb6\xec4\xa8\xdb\x91/\xea\xd5\x98`\xe3\x8d\xa2\x1a%\xf6+\xfe\x86Q\x9cgo\xa8\x08p\x90\xe5}P\xc9\xec\x17\x0e\xdeX\x8e\x18Mpl\xde\xa9\x18\x88\xea\x1f\x9ds\xf8\x8c:\xdcX\xd9\xa2\x96\x94^=(\xceI\x89\xb2\x9c\xa24\x99'\xb4\xa1;\x19f\xadEU\x8f\xd5\xa9(i\xc8\x8c\xbc$\xc6\xe9\x8c?\x18\xc4\xe7Ee\xb1M\x13A\xb2\x92Au\xdf\x7f,\x95\xef`\xa7\x81\x86\x1e^<>DuQ$c\xdcKB\xbb\xc6\x02\xce\xe2\xbd\xbc0\xf4\x84\x00\xc3\xd8[\x88,T\xa5\x19:\x16\x83\xec*\xbb\xa8\xed\xa5\xebi\x9d\xc6R\xb0\xbe\xffEqH\xe8NC\xa0\xbf9\xdc\xdf\x7f\xe3\xf6\x82\x18\xd9\xb8V\xcd;\x92\xff\xc3\xe1Kn\xe0\xa2\xb7/\x9b\x16]g\x94\x7f\xb4\xf9\x96P\xc8\xa5\xe0w(\x04\xbd\xdb\x9e\x18\x9d\x90\xab\"t\xc5\x85`\x0e\x07`\xd4C8\xcd\x12\xa1\x1f\xa2d\xe7\xe9\xefHW\\\x10\x0b\x1f\xa1\xd3\xf9\"\xe5\x0f\xd4\x95\xa8\x8co\xdf\x9d\x04\x92\x08\x11\xf7>\x15S\x1c \xdf\x1c\xafe)\x1e\xe5\x12=\x89\xd8!\xcap\x9dCX\xdd\xf8\xf3\xe7\x9e82\x9f3O\xb5`\xc1\x93\xe0\x9e\x11-\xbcsD\xc3\xeb*\xc6\x89\xed\xefX\xbeU\xc9\xeb\xfeZcE\x9e\x06\x8b\x8cAV\x90h\x98\xc3\xabw\xa4B\xc6(\xac\x1b\xc9\x92\xb3\xc0\x08.\x99\xdeN\x00\x856{1\x0fV0\xa4\xf1\xeay\x9f\x1a\x1a\xcdG\xef/N\xff\xe3\xf4\xfb\xc9\xd5\xd9E\xbf~\x97_/\xfe<\xfd\xf2\xb5g\xaf\xd3\xef\x7f~\xbd\xec=\xd6\x97\x1f\x97Wg\xbf\x9f\x9e|\xef\xd7\xed\xec\xaf\xef}\xf1;\xf9\xf6\xed\xf4\x8f\xd3\x93\xab\xaf\xfd\xba\x9d\xfd\xf7\xf7\xd3\x7f\xfc\x08\x97[q\xbc\xf0\xdf\xaf_\xfd\xb8~\xbf~\xfa \xfc@7]\xa5e\xd0\xf2\x82\xdd\x8c\xd5\xcd\xb5\x8a\x07\x0e\x8f\x90|\xdf\x9f\xdf0\xf5\xa8\xb6\xe2\xd8\x14\xc7\xf6\xcfr\x14\\\xf2g7\x8b\xe4&\xc90\x85\x14\x9d\xb4\xee\xa1c\xdbGu^(QL&\xecDT\xdc'\x11\xb3\xee\xa7U\x16\xd1\x8e\xa7+<\x9a\xda{\xc7\xb6\x8f\xc2\x8f\xc2\xe3\xb1\x92\x08%\xd9=)\xfb\xd3\xa3\xf7\xe9\xb1\xf5\xab\x9a\x9a\x8c&t)\xd4\xb7\xa61\xaaJ\x9a\xc7 \xce$\xa1\xcai\xc1\x18\xdc\x97P\xbe\xef\x8f;_\xda\x85\xde\x16\xb8\xa0K\x89\x13W\xdaJK1\xed\xdbsH-7\x8e\xad_\x05w\xc5\x80\xe2\xe5\xf8\x0c\xe1\xe94I\xf9\x83\xfd\xf8\xa6 \xdc\x0c\xe99\xa8\x94:\xc7\x96ob@n\xf5\xe0T\xdc\xec\xca\xc7a\x97\xb5\xddT\xe4\xa9 ~\x9e%\x93\xaaD\x13\x9c\xdd*\xad\xd8\x13\x95Z\x96\x1d\xdb?3\x84\xd4aP\xcd\x839\x0d\x05Y\x14\xa4\xe4\xa6\x18\x9b\x82\xba\x9c\x9et\x145K:\xe3h\xc0\xa6\xae\xe5\xe6\xb1\xfdss}>\xcc\x92hf\xf0I\xdb\x90j\xd7\xebj\xb3 A$\xcbi\xa0\xbat\x17!-\x90\x8f\xad_m\xe8\xf0\"\x8e| \x8b\x02;b\x7f@j\x01!\xfd\x84t\xc8\xb2\x08GH\xd4\x0d\x12+Q7i\xabi'\xad\x9e\xfe7%*\x93\x9b\x0c\x8b\xb7\xa3K\x8d\xaa\x07\x1e\xcc\xee;A\xe7\xc6.W\xd6(\xdb\x82{\xbc\x1e\xb6\xe0\"\xb3\x0b\xbd`\xdae\xca\x83%n\xfa\xe8\xc1+~1!\x1fn\xae7\x02\xa2\xf9\x02\xa5\xe4\x9e\xa4\xf2H\x11\x8eZV\xbe\x0ea\xd1\xbek\x83\xe6\xceE\xc6g\x1e\xf7+=9>\xe1Z?7\xbft\xbf\x16-La~\x9a\xb2\xd4\x1b\xb5\xa9\xaa\xd8\xec\x8b\x02\xe9-\x82\xbck\xd3\xf0\xbd\\R\x9c\xc5\xb8\x88M\xf5\"\xf5\x9d\xf09\xceqqk \xf4\xa9\x9b\xfa\xb5o;\xb0 +\xab\xc5\"/\x1a%79\xe6\xef\xe4\x15;\xa6\xb4H&\x15%h\x8e\x97\xdcU\xea\x018!L\xbag7$F\x93%\xe7\x82\xd4 uY\xb5<\x8b\x98\xdd\xc2\xe4\\I,\x9e;\xd1\xa4\x93\xe8\x9a\xcb\xdc\xeb\"O\xd3j\x11\x9a\xd6\x90d\x87j\x00\xf8\xfc\xff%\xd5\x03NS-\x02\xd4\x8e\x90\x17FY\x8c\x12Z\xda\xaf|\x9bM]\xa8(\xa1\xda\x00\xf4\xa6T\xa2u\x9a\x904\xf6\xbc\xc3\xaf\xa6--sD2\x02\xea)\x17m\xdc\x18\x89\x01Q\x12=\x19 \x8b\x94X!Vbx\xb4\xc4\xd0x\x89\xa1\x11\x13\x83c&\x06EM\x0c\x8e\x9b\x18\x1891\xf3\xa2_\xde3\xc2\x9eX\x93\xe6\xfd\x0e\xc3/\x89p\x9a.\xeb\xc7\xf5\x9d\x1de\xa7\x11\xc8]\xc3\x19X\x1dXD\xa6hBRq)\xcf423\x01\xa24!Y}\x1e\xe6\x11\x13^x\xdd%\x1a<\x04\xe3*N\xbc\xdc\xe9C\xcf\x17~\xfc\x8e\xd1d\xb9\x83\xaaE\xac\xffM\x939))\x9e/\xca\x1d\xed>\x13\xc5\xfd\xfc\x8f_\x8ad\xabT>\xcf6\xcd\xddd\x80O\xd8\xd0\xf3\xb5\xf0$\xc4\xd7\x8c\x88\x90\xe0\x04. d,\"\x06v\x97\xb1%\x04Z\xec1\x19\xc7D\xf6X\x17&Ae\xac\x00\xc9h\xb1\xe4\x8f\xb2I|A4M\x96#R\x04\x93\x02\x08u\x1e\xc77\xfd\n\xc2D\x91\xab'(\xe0\x90\x0e(\xf3\xfeL\xae\xc0W3\x85).\xa9B\x1aD\xd8\x86\xce#\x0f\xcbKF\x9cH)2`\xc4\xbaJ\"5\x9bQ \xe9\xe3\x87\x10\\0g\x98m-m\x97\x96\x9c\x13\x9c\xe1U\xc7\xa3\x82[\xff\x00\x7f\x13\x97\xe1\x04G39\xe3\xde\x0e\xce\xb2N\xcd\xb6\x96\x15a\xd2-\xf1\xb0D\xf6\x856\x8a\x14PI\x9e\xed z\x11\xb9\xf7\x1f\x93`\x18\x9e0\x15\xf7\x8di\xd7\x12Ex!\xacLk\xd4\x0e\xdb\x81^Pz\x9d\xe7h\x8eo\x89\\\xea*w\x8f\xa9+\xb9\xb1\xc9\x12=\x90\"0c8v\xfd\x00\xaat/\xa5\x01\xa5\xe3\xab\x0c[J[\x17\xf8\x06'YI\x8d\xd3\x8c\x13^\xd3\xf5\xcez\xe1,\"\x9e\xe8\xb3+\xc3\xed\xfa\x90\xa4)\x9a\xe1{b\x8c$9\xccc\xca\xa8:\x92&\xc4-\x16\x92\xec>O\xef\x9b\xe9\xe3\xed\xf6\xfd\x8c\x9d\xbc\xff\x92\x9c.\x88L\xc4\x16\xa1`\x17\x8dHc\x84e\x04\x1a\xdb\x7fN\x80u<\x1aV,\x15\xd9\xf9\x8a\xbf<\xaf}\x9e\xdf\xbb\x9e\xde\xad/yFy\x8e\xa7\x05nXD\x845&\xc2o\x0e\x02l(\x88\xfd\xf4\xfcW^\xed\xc0-\x8d\x80;\xe0\xa1Y`\xc3mY\x1as\x11\x8adX+M\x01!\xd7Ash8\x83 \n\xc4\x15H\xb0\xd3\x86p\xa6\x1b$e|\x01\xbe\x91`,\x1e0\x87\xe4?_\x07\x8f\x0cdk\x87\x84\xf1M\xf2\xc9\x0b\xaf\xb9\xc9@|\xf2\x07z\x80\x993\x84\xea\x11b=\xfa\xed\x9a\x91\x16\xc2\x10ZW\n\xf6p.\x14\x0f\xc9*\xa8p\xcc7}\x1a@\xd1\xd8\xef\x9eBf\x12\xa4+\x11P_\"\x04 \xd5\x16\x0d\xb84\x10\\N\xa0\xf5\x04o\x8b\xa6g*\xac;\xd1\xf3P\x078\xce\xb4\x90\x1e\xaeIM`@>\xc1\xb4)\xda<^\x8d\xa1[\x11\xda>|\n\xa7|\xe4E2\x8c\xf2\x15\xf5 \xb2/!;\xf9\x9dGo\xc68kY\x81\x8ew\xe2\xb2G\xb5\xad\xf9\x00\xd6\xa6i\x04[\x03(B\xa0\\G=\x1e\x7f\x0b_h\xa1\x8e\xeb\"\xa8\x9c:\xd3\x1eVP\x9b\xc3\xa9\x95\x15S\x1b`\x0f>E\xbd\xde\xd5\xda \x9eu\x11G\x80\xc7\xdc,\xbdZ\x11\xedCy\x19R\\@\xd6\x0d\xe7 Diy\xc1\xadB\xfdH\x0bg8\xf5+*.\xc0\xf2\xb2\xb1\x02\x82\xaft:\xba_`\xd3\xbe\xab5<\xc1f\xc5I\x0e\xe8y\x98\xad\xfeE#\x1d\xfamb[Af\xa5\xce\xf6_\xb8\xf3\xb6\xbb0\xc4rx\xde\xfc\x00\x81\x8ck\x91\x8e\xa4\xaa\x9f=\x16c\xaf^\x12u0\xb5 \xf5\x1dB\x7f\xf2\"\x0625\xdd\x0bK\xc6\xef[\nk\x9c\xa4e\x8en\xb3\xfc!C\x98\xad\xdao\xcc\xfc\xf1\x06i\xbc\x84\xc3\x18\xc6\xb1:=Am:u\xdb\xa1\xf2\x12Z\xb9\x07\x01\x81\xa9y\x8d\xde\xf2z\x99 \x9d\xa1i\x92RR\x90\x18\xdd\xde+\xbdNI\x81i^\xb8\x83\xcbd\xe4\xba\x97] \x02% %e\x1a\xf6\xae\xa2LL\xb5\xb7\x10\x84\x964\xcb\xe6\x1dT\xce\xa3\xf9\xf2\xe9T\xc6\xcd5\xdfV\x842+\xb8(Fs\xc3\x00\x93*\x80K\x11\x81\xa7\x01\xa1\xce\xbdz'\x04\x92O\x87\xda\xb2Ahrb\xbd\xbf\x9b\xe1r6>\xa9T\xbe\x94\xce\x89\xc8\x8c\x8a/j\xad\x05\x93-BB\x11\x0d\xc2\x0b0\x05X\x84\xabY\xee\xae5\xf2o\xd9\xc0\xfcUx\x7f\xec\x12k:<\x8dm\x82\xd6lF\xf9|\x9eg|<\x7f\x0c\xa9x\xcf\xeaE\xd8!\x86\x16\xe1i\x8a\x8c\xfa\xae\x94\xe6\x90+e\xd5t\xadj\xf4V\x80\xfd\xb5\xbe\xa2\xe5|\xda\xd3\xac\xf1\xde\xd5\xaa\xc6Do\x92\xdd\xe7\xb7\x9e\xb5\x94d\x8b\x8a\xbe\xda\x04/\xc8.\xe85\xf1\x10S\xaf\xdd\xbe\xb3i\x17\xa5\x8dT\x99s\xfejM\x92\xdd\xa2 \x8eneqx\x00$\x1e\xa3\xc0S5\xf8\xa2\xf1{\xb8\xf4K\x04a\x17WO\x16\x00\xcd\x03\xd1\xe0[\xc5\"\xbe\xa5\xa2\xafc\xc6\xbf\x81\x12-\xbe\x96\x14O\xd2\xa4\x9c\x91X\x053\x84\x82\xcc!r\xbc'\x9b\xfaRn\x17\xf8 \x89x\"\x82aU\x00\xe01F\xa1\xf3\"_\xe4%\x9c\x07Z,\xaf\x87\x11\xfc\x99\xc7\x85\xc2i\xca=z\xb4\xa8\"\x1eM\xc4m\xa49.\xcaY h\x1d\xa1\x92bZ\x05\xb7~?\xfe\x9f\xea\\\x97d*\xcc&.\xf8\xb8\x88T\xcbP!\x0d\x9a\x00\x9d\x86\xf1\xb3\xe2qC\"F\x9eM1\xbf%[Ta\xf1\xdc\x8b\xc7\xf0\\\xc9\x8b\xaf_\xce.~\xbf>\xfd~\xfe\xe3\xea\xfa\xf2\xea\xe4\xea\xc7e\xaf\xfc5\x17\x8c\xf3\x8b\xb3\xf3\xb3\xcb\x15\x00\x88o\xc1\xee:\xffnUB\xfaK\xf2 \xf3 3\x10\x00a\xa4]\x01\x12Bx\xb4\x17N\x93x\xaf\xca\xc4yQ\xac[\xb6v\x00\x9d\x03Si\xe7\xb1\xfak;A\xcc\xd81\x19\xc2\xc5$\xa1\x05.\x96\xb5\x04\xe3\xd5\n\xf5\x99Ol\x85\xe18\x8aov\x0c\xc57;~\x89\xd8\xd0\x0d\x0blQ\x90\xfb$\xaf\xcat\xd9\xd9\xeaFR\x95\x17W)l\xae\n\x1c\xdd\n\x8f\x98\xb0\x9c\xf4 \x90(\x9d\x04=\x95\x81\xa4\x979H\xc7\xcef\xb4E\xb3\x84\xdc\x8b\x97\xc2\xf2\x8a\x86\xc4N\x9e\x81\xb0\x13\x90^\xadU\xf8\xc2\xba\xfe?\xa5\x9e\xa7\xaa\xb2\xaa`'\xacL\x83h\xa2\xc7\x9e\xf6\x02\xb4\xddP/\xa0A/9LE\x97Z\x82\xb5\x1b\xa3>\x10\x02\x80\xd5\xf9M\x927r+'\xd9\x8dzdgg\x8a\x93\xb4*\x00GH\xc4\xd4\xf0\x82d1h\"\xfb\xccz\x1f\xa5{\xf9\xe3\x8fAZ\xaa\xdb\xfb\xfc\xe4\x12\x96R\xdf\xecv\xf9_\xa7\xe7\x03\xba};9\xfd#\xd8\xcd\xd0\xc9C\xe9\x1c\xa6\x8d\x1d\xa3A&\xc5\xd9\xb9\xa1\x81Q\x95\x95$l\xab\xf1\xdb\x8c>Y\xf4\xdd)m3\x8f}k\xa80b\xec&\xa6A\xeb\xd7\xa6\x86\x0c\xc9\x96C{H\xf6\xcd\x18R\x93\xb4\x17\xe5Y\x99\xc4\xca\xfb\xc0\x07\xbfM\xf8\xadE,^\xdd\x9a'e\xc96\xa7\xd4Gy\x81b\x92\xe2%\x89\x81\x89\x89\x0e$\xd9\xe2k#\xc9\xbe9\xf9R_(Z1g2\xc3\x15\x80\xae\x1aT\xe8\x89C\xd5\x99\x90O$\x8b\xf0\xa2\xacR\x8d\x92\x94[\xfc\x14\x0b\xf1#\xa2\xda\xf2\x00\xe4$\xc3p\x148\xbc-\x7fE\xf5k\xf0UJ\xb9\x90\x96,\x13O\xa7jq\x1d\x98%\xf6Sq\x90Q\x1d\xea\"\xc5 \xcctx\x1d\x95\x95\xdaX*\x06\xd6w\x19\x00=[Z\x8b0Y\x0b-\xc9\xcb#\x07\xb8\xf0l\x9f\xe8+\x91|\xca-W>\xe5\x98R\x1c\xcd\xc4h\xf5\xab\x81y\xc1\x13\xa2\x9c\xb0\x9a;F\xaec~v\x0f\xa6k7X\xac\x1f`\x14\xfb!\x93\x0e\x0d\xf9Y\xe68\xd9I\xd6\x8e\xabQ\xc2\x93\x9a\xd0\x02qIF\xfc\x91\x13`c \xac9\x0e \xe4\xc3[\xe3F\xd1\xe3;C\x8b\xe4\x84\x06\xc3\x85\xeai\xd8\xe48\xa16\x96\xc3\x03\x844$\x08K`\xe1\x88\x9b\xc0\x961\x82T\xebe\x03d\x8fp\x16\x04}\x93\x9b\xc0\x1f\x03U\xc5 \xe9\xf5\xe3\x9f\xd6\xc2\xa1\x17\x88\x832F\x0e\x05@\x8d\xb1S^a\x0e\x04\x80*\x90\x96@@M\x81\xb6)\x10\xb2=\x03u\xc1\xd3\xc16\x05b\x10\xaf\xc6\xd0.\x08mS \xe0\x94\x8f\xbcH\x86Q\xbeb$)\xea\x93\x02!\xe5\xf2\x98 \x10\x16\x90\xe3\xa5?H\xfb\xfa9\x93\x1f\x9a\xf4\x8c`Y\x00E\x07\x94\xdf\xa8\x83c\xe0t\xe2\xdf\x07\xfd\xd2\x1eZ\x93\x1dVI\x9b\xc2\xa3\x95\x15Q\x13\x1c\x98Ck\x89Z_?\xb7\xd6\x95\xec0\x90\x8b\xaf\xe5\x08d\xc1\xd7r\x0e\xf2B\x1a\x81a!}\x0ed\xd3P\xfaG\xd1\xe4P\xba;y\x14c\x9e\x91\xac\xc0\x9dg%\x10(.pQ\x12\xbb\xcfLA\x92A\x9a\x0e\x01\xb5\x1d\xea\x99\xee\x87\xe0\xeb\x07\xc1\xb7\x1a\xea91\xa8G\xfa\x1f\x18P_]\x88\x86\xa5\x01\xa2\x8d\xe5`HG\x06\x01\xd2^ ]\x08\x96\x06\xf5Jy9L\x83vAY\xb4hO\x08\xfd\xe6 \xa4\x90\xd4\x86q\xb8\x00\xd5X\x0cm\x95\x9b\xaa\xc9o\xd6\xa2\x0b\x90*~|p\xf7\xe8$\xf6sy\x97F\xb3\xf7\xe5\xe3C\xf6\xe1\xc3\xcf\xa3\xfd\x9fO7\xf4SQ\xce\xee\xef\x96\xd3\xe2gT\xd8\xb8\x10\xbc\x8c\x19\x89 \xad\xab\x17\xcd\x03%Y\x1a,\xf0\x818\xb8;t2\xe0\xe1\xc3a|w\xf83\xbe\x9f\xc7\xf8\xa9zx\x8ap\x1c\xcff\x9fn\xe6\xd5\xfby\xf4D\xde\xdb \xcb\xec\x16U\xd6\xcaar\x0b.\xf8*\xdek\x1b\xc5\xf3\x1b\x18\xab\xda\x18 ~MS|\xc3%\xaa\xae\xa3\x9f\xab_z\xb4\x87\x86\xd1\xb8\xbf\xd0\x07\x97r\x91g\xa55\x9eA!!\x05\xed\xe6p\xc5T\x0d\x03\x99\xa2@\xf4\xe5 y\x14(xO\xab\xcf\xc9\x93\x16Bn\x9e\xc8\x1fr\n\x9dw\x82\x1c\x04\x8f\x99 1\xa2\x9e\x0b\x87\xfeD\xcf\xcc\x88\x16B\x90\xc5\xe1L\xa5\xd502\xb4\xcc\xabB*\xf2&\x1b\x1aX\xf1B\xb3\x17\x92]\xa8\x8eZ\x13\xff\xcd\xd8 #\x9d \xfagE\x8a\xe5\x9e\xa8L{q\xfeE&Z\xd6\xb0UH\xea/\x8e\xa1N2Te\xe4qA\"fh\x88\x97\x90\xd4HF\x9f2\x9a\x919nN\x8b\xd3\xecp\x9b\x1c\x1c~wr=\x1a!\xcac\x8b\xa3Otp\x15\xe8V\xab \xc9\xe8\xfb\xc3\xd6_\x9d\xc5\xad=8\xc4\x84\xe2$\xdd\x90Z\x12\xac\xfbuU8\xdf` jW\x9e\x829\xb8\xb7\xf5\xe8\xbc\xc0\x05\x9e\x13J\n\x03\xe7]\x91uj\xaai\xd7\x1alm\xc2\xc1\x8a}\\u\x9ed\xc7h\x81\xa9\x19!\xc86rR\x90\xf8\x18\xd1\xa22\xb7\x87\x95m\x8a\x03\xca\xfc\x05\x92\xdf\xd7Z\x1ef#\xfbL\xc4!60\xe3\xd6\x1d\x93EVvMqZ\xc2\xf9eX\xb7.\x9e\xfdo\x8bg\xfd\x0cbK\xd7\xb1\xac\xe0\x11\xf9\xd0\xb6\xda\x80\xebg\x90\xb1g\x9au\x0dh sf\x08\xd1]\x15\xedR\xdem~Hc\xab';\xfaXy-{\xae\xbb9^\x9e\x19-C\x0d\xc8\x8c\x81\xe6]m\xc8u\xd6\xc6\xcbq\xa0e\xa1\x0190\xc8\xae3,\xb8\x060\x875\xb7N\xfa)\xbei(\xd9\x7f\xcaq\xde\xec-\x8a\xfc\x9ed8\x8b\xc8\xde\x9cP\x1cc\x8a\xf7\xee\x0f\xf6\xc4R\xde\xfb\x97\xa1U\xffoO\xee\xf67\x02\xd2\x0d\xd1\xf6aY\xcd\xe7\xb8X\x1e\xab\xd2a%* .\xa2\x99|ZQI E\xad\x8b\xd3WF\xdcQK\x8f\x89\xfdc\xd1ep=\xe6U``\xe5\x85.\x93y\x92\xe2\"]\xee\x98\x12Pb\xac\xe1\xb550\xaa\xd9\xe0Da\x04\x95\xc2X\xa8\x01\x1as\xb7\x83\x92\xa9z\x013\xdeQ\xf5\xb5\x10\xaeG\x87XKcXJ\xf5\x88\xff\x86N\xa7(\xcf\xd2\xa5\x9a\x1by\x07W\xa3\x89\xd3\xb4U\xd8\x0dS5\x8d\x85I'\xad\x8a\x8c?\xd9a\x85]\xcfP\x03zBk>\x18\x93\x82\xb3\xb8Y\x00\xa6U\xf4\x8e\xefk\xfb\x80\xad\xf5\xc0\x85\x84X\xb2tF\xf4j6\xa8\xe5\xa7\xa6\xae\xa1\xcb\xd1\xc2i\x993\xdc\x0cvH\xccZ\x18\xd5HxP\x93\xbch\x19\xc95l\xda\xaa0\x86\x93L\xa47a\xda\x8e\x02\xe2\xcf\xacL\x88s(=\x8a\x8a^P\xa38\xb8\xb0c\xf2\x0c7\x12\xf7\xf5\xa44\x19\xd5`P=z\x9bS!\x8al\x94h`'h\x82c\xd3\xe5\xae~\x84\x92\xe9\xb1I\xf1\x95c\xd29\x12&\xf5\x0f \x9d\xe5\x15m\xce\xbfm\xb36\x18z\xe2\xe4\xa6\x86\x97-\xd1\x03^r\xed\xa3j\xa0\xd5\xf6\xd7\xdb\xc6\x0e6\xb9\x8e;|7\xf9\xfd\x8b\xd1\xc7\xc0\xed\xd7\x06rW\x0f9\x9b\x1a\xfe\xeeM>\xf5\xcchs{\x99SindM\x1a\xfbQ\x9cgo(\x17\x01<\xd2A\xd6\xf9A%\x9e\x93\x96\xd8\xb6\xf3\x8bC\x7f\xcb\xc4\xae\xe2\xb614\xafz\xd5\xe1&\xc2ma\xac\xba\x89m'WS\x9c\x93\x12e9U\xcb\n\x95U4\xeb\xf2\xcb\x83V\x0b\x13\xfe~\xd0$\xa73c O*Z\x87xh@q2\xe5\x9f\xd4\x12.\xcd%\xfb\x8f\xa5\xf2\xdb\xec\x18\x0b\xa0\x1e\xab\x94\xef\x14Q]I\xc9@\xf1\x92\xd0\xfa\x0c\xa0:\xee\xe5E\xc7\x12f\xf3P\x88\xbc\xd5\xc6I\xa0q\x85\"\xfbj$\xc5W]\x82\xeb4\xaeM\x05\xf5\xfa\x992 \x0d;\xe5\xcd\xe1\xfe\xfe\x1b\xb7\x0f\xca\xc8\xe1\xb5\x1a\x94#y\x9f8e\x81\xab9\xabCP\xda\xfc\xc2\x0f\xa7\x0b\x95F\xea5(4\xcb\xd3\xb8\x94\xb7tZ\xcd\xf5\xbd:\\\xfd\x1e\xcf\xe75\xf5\x84\x08\x85*\x0b\xbdD1\x99\xb0#Aq\x9fD\xec\\9\xad\xb2\x88\xb6\xfc\xed\x90\xd1\xd4\xde;\xb6}\x14\xa7I\x1e\xd1\x95D(\xc9\xeeI\xd9\x9f\x1e\xbdO\x8f\xad_\xd5\xd4d4\xa1K\xa1\xbe5\x8dQU\xd2i\x1bR\xedz]\xec6!\x88d9\x0d\x14\xb7\xee\"\xa4\x05\xf2\xb1\xf5\xab\x0d\x1d^C\x92/aQ\xdfG\xec\x0fH)\"\xa4\xdf\xad\x0eY\x16\xe1(\x96\xbaA\xe2Y\xea&m5}\x89\xa1\xa7\xffM\x89\xca\xe4&\xc3\xe2\xc1\xeaR\xa3\xea\x81\x07\xb3\xfbN\xd0\xb9\xb1\xcb\x955\xca\xb6\xe0\x1e/\xc7-\xb8\xc8\xecB/\x98v\x95\xf4`\x85\x9d>z\xf0\x8a\x1f\xcb\xe5k\xd1\xf5F@4_\xa0\x94\xdc\x93T\x1e)\xc2A\xd3\xcaq\",\xdawm\xd0\xdc\x97\xc9\xf8\xcc\x1dy\xf2\xa0\xef\x13\xae\xf5\x1b\xf7K\xf7\x13\xd5\xc2\x14\xe6\xa7)\xcb\x0b\x19\x02\x83\xe6Y\xd5\xbbN\xab\x8c\x9d\xed8\x97\x15\xdeiRRQo\xcb\xbc\x08v\xc5L2L\xaeq\xe4\x7f3b\xc4CX\xc0\xa2\x87\xaf\x83\x13\xcd$\x15\xe2\xcd\x08\xe7\x1c\xc3\x15\x9d\xe5E\xf2$\xe4gA\"\x92\xdc\xfb\x16B]\x97\x9a\xcfK\xb7\xc8\xbfvH8\x80\xf0\x18\x9ck\xbe\xee\xae\x83\x87\xc3\xb1\xf9Po\xd4\xa6\xaab\xb3/\xea\xb3\xb7\x08\xf2\xaeM\xc3\xf7rIq\x16\xe3\"6\xd5\x8b\xd4w\xe2I\xfb9.n-\xf1ZuS\xbf\xf6m\x076ae\xb5X\xe4E\xa3\xe2'\xc7\x9c\xef\x08Q\xf2\xabH&\x15%h\x8e\x97\xfc\xca\xc4\x03pB\x98t\xcfnH\x8c&\xc2\xe1.uB]\xd5-\xcf\"f\xb709W\x12K\xfe\x81h\xd2It\xcde\xeeu\x91\xa7i\xb5\x08MkH\xb2C5\x00|\xfe\xff\x92\xea\x01\xa7\xa9\x16\x01\xcd\xa4\x07~\xdfAKd \xf4|\xe1\xc7\xef\x18M\x96;\xa8Z\xc4\xfa\xdf4\x99\x93\x92\xe2\xf9\xa2\xdc\xd1\xee3QF\xd0\xff\xf6\xa6H\xdbJ\xe5\xebp\xd3\xdcM\x06\xf8\x84\x0d=_\x0bOB|\xcd\x88\x08 N\xe0\x02B\xc6\"b`w\x19[B\xa0\xc5\x1e\x93qLd\x8fua\x12T\xc6\n\x90\x8c\x16K\xfe&\x9c\xc4\x17D\xd3d9\"E0)\x80P\xe7m~\xd3\xaf L\x14\xb9z\x82\x02\x0e\xe9\x802\xef\xcf\xe4\n|5S\x98\xe2\x92*\xa4A\x84m\xe8<\xf2\xb0\xbcd\xc4\x89\x94\"\x03F\xac\xab\xb2U\xb3\x19u\xae>~\x08\xc1\x05s\x86\xd9\xd6\xd2vi\xc99\xc1\x19^\xf4<*\xb8\xf5\x0f\xf07q\x19Np4\x933\xee\xed\xe0\xac\xce\xd5lkY\x11&\xdd\x12\x0fKd_h\xa3H\x01\x95\xe4\xd9\x9e\xa0\x17\x91{\xff1 \x86\xe1 Sq\xdf\x98v-Q\x84\x17\xc2\xca\xb4F\xed\xb0\x1d\xe8\x05\xa5\xd7y\x8e\xe6\xf8\x96\xc8\xa5\xaer\xf7\x98\xba\x92\x1b\x9b,\xd1\x03)\x023\x86c\xd7\x0f\xa0J\xf7Rec\xab\xf8*\xc3\x96\xd2\xd6\x05\xbe\xc1IVR\xe34\xe3\x84\xd7t\xbd\xb3^8\x8b\x88'\xfa\xec\xcap\xbb\xf2J\x023|O\x8c\x91$\x87yL\x19UG\xd2\x84\xb8\xc5B\x92\xdd\xe7\xe9}\xb3\x0cA\xbb}?c'\xef\xbf$\xa7\x0b\"S\xbaE(\xd8E#\xd2\x18a\x19\x81\xd6\xa8f\xd1nu<\x1aV,\x15\xd9\xe3\x97f\xbd\x8by~\xefz\xf9\xb7\xbe\xe4\x19\xe55\xa0\x16\xb8a\x11\x11\xd6\x98\x08\xbf9\x08\xb0\xa1 \xf6\xd3\xf3_y\xb5\x03\xb7\x1a\xa5(\xec\x01\x0frf\x03\xa1\x0c\x8d\xb9\x08E2\xac\x95\xa6\x80\x90\xeb\xa094\x9c\xc1\x04\x05\xe2\n$\xd8iC8\xd3\x0d\x922\xbe\x00\x9fh\xa0\x8dR'0\x0e\xc9\x7f\xbe\x0e\x1e\x19\xc8\xd6\x0e \xe3\x9b\xe4\x93\x17^\xde\xae\x03\x14\xe6\x93?\xd0\x03\xcc\x9c!T\x8f\x10\xeb\xd1o\xd7\x8c\xb4\x10\x86\xd0\xbaR\xb0\x87s\xa1xHVA\x85c>)\xd4\x00\x8a\xc6~v\x152\x93 ]\x89\x80\xfa\x12!H\xa8\xb6h\xc0\xa5\x81\xe0r\x02\xad'x[4=Sa\xdd\x89\x9e\x87:\xc0q\xa6\x85\xf4pMj\x02\x03\xf2 \xa6M\xd1\xe6\xf1j\x0c\xdd\x8a\xd0\xf6\xddU8\xe5#/\x92a\x94\xaf\xa8O\x90} \xd9\xc9\xef\xbc\xb93\xc6Y\xcb\nt\xbc\x13\x97=\xaam\xcd\x07\xb06M#\xd8\x1a@\x11\x02\xe5:\xea\xf1\xf6\\\xf8B\x0bu\\\x17A\xe5\xd4\x99\xf6\xb0\x82\xda\x1cN\xad\xac\x98\xda\x00{\xf0)\xea\xf5\xac\xd7\x06\xf1\xac\x8b8\x02\xbc%g\xe9\xd5\x8ah\x1f\xca\xcb\x90\xe2\x02\xb2n8O J\xcb\x0bn\x15\xeaGZ8\xc3\xa9_Qq\x01\x96\x97\x8d\x15\x10|\xa5\xd3\xd1\xfd\x00\x9c\xf6]\xad\xe1\x058 \xdb\xf3\xda\x9b\x1a\xdd\xf1\xa6\x9a\xcc\xde\xed\xce\xad\x98\xd1\xe7\x0d\xf1\x17\xc8\xb8\xd6\xd9H\xda\xf6\xd9\xc3)\xf6\xeaY\xad\xe3\xa1e\xbdU\x84\xfe\xe4u\x08dv\xb9\x17\x96\x0c\xc1\xb7\xd4\xc68I\xcb\x1c\xddf\xf9C\xc6K\xd7\xa2o\xcc\x82\xf1\xc6Y\xbc\x84\xcf\x17\xc6\xb1:\xc3@\xad\\ua\xa1R\x0bZ\xe9\x03\x01\x99\xa7y\x8d\xde\xf2\x92\x97 \x9d\xa1i\x92RR\x90\x18\xdd\xde+\xd5LI\x81i^\xb8\xe3\xc3d\xf0\xb9\x97] \x02% %(\x1a&\xab\xa2LL\xb5\xb7\x96\x03\xaa+'7\xae\x91r\x1e\x90\x97O\xa72\xf4\xad\xf9\xc0\"\x94Y\xc1E1\x9a'\x05\x98\x17\x01\\\x8a\x08<\x0d\x08u\xae\xc6;Q\x8c|:\xd4\x96\x0dB\x93\x13\xeb\xfd\xdd\x0c\x97\xb3\xf1I\xa5\xf2\xaduNDf\x14mQk-\x98/\x11\x12\x8ah\x10^\x80)\xc0\"\xe2\xccr\xfd\xac\x91\x7f\xcb\x06\xe6E\xa7\xfd\xe1G\xac\xe9\x083\xfexAs6\xa3|>\xcf3>\x9e?\x0cT\xbc,\xf6\"\xec\x10C\x8b\x083EF}\xddIs\xc8\xad\xb0j\xbat5z+\xc0\xfeZ\xdf\xb2r>\xedi\xd6x\xaf[Uc\xa27\xc9\xee\xf3[\xcfZJ\xb2EE_m\x8e\x16d\x17\xf4\x9ax\x98\x05\xd9l\xdf\xd9\xb4\x8b\xeaD\xaa\xe69\x9b\xf84\xc9n\xd1\x04G\xb7\xb2\xaa<\x00\x12\x0f3\xe0\xd9\x16|\xd1\xf8\x9dT\xba\x9c\x7f\xd8K\xd5\x93\x05@\xf3@4\xf8V\xb1\x88o\xa9\xe8\xeb\xb0\xefo\xa0\\\x89\xaf%\xc5\x934)g$V\xf1\x08\xa18q\x88\x1c\xef\xc9\xa6\xbe\x94\xdb\x05~B\"\x9eK`X\x15\x00x\x8cQ\xe8\xbc\xc8\x17y \xe7\x81\x16\xcb\xeba\x04\x7fMs\xa1p\x9ar\xa7\x1c-\xaa\x88\x07\x04q\x1bi\x8e\x8br\x16\x88;G\xa8\xa4\x98V\xc1\xad\xdf\x8f\xff\xa7:]%\x99\n\xb3\x89\x0b>.\"\xd52TH\x83&@gR\xfc\xe4\xcf\x84\xc80w6\xc5\xfc\xa2kQ\x85\xc5s/\x1e\xc3\xd3\x1d/\xbe~9\xbb\xf8\xfd\xfa\xf4\xfb\xf9\x8f\xab\xeb\xcb\xab\x93\xab\x1f\x97\xbdR\xd0\\0\xce/\xce\xce\xcf.W\x00 \xbe\x05\xbb\xeb\x14\xbaU \xe9/\xc9\x83\xcc\x83\xcc@\x00\x84\x919\x05\xc8\xe9\xe0\x01[8M\xe2\xbd*\x13\xe7E\xb1n\xd9\xda\x01t\x0eL\xa5\x9d\xc7\xea\xaf\xed\x1c/c\xc7d\x08\x17\x93\x84\x16\xb8X\xd6\x12\x8c\x17\x1c\xd4g>\xb1\x15\x86\xe3(\xbe\xd91\x14\xdf\xec\xf8%bC7,\xb0EA\xee\x93\xbc*\xd3eg\xab\x1byQ^\\\xa5\xb0\xb9*pt+\x9cZ\xc2r\xd2'@\xa2t\x12\xf4T\x06\x92^\xe6 \x1d;\x9b\xd1\x16\xcd\x12r/^G\xc9+\x1a\x12;y\x06\xc2N@z\xb5V\xe1\x0b\xeb\xfa\xff\x94z\x9e\xaa\xe2\xa8\x82\x9d\xb0J\x0b\xa2\x89\x1e{\xda\x0b\xd0vC\xbd\x80\x06\xbd\xe40\x15]j \xd6n\x8c\xfa@\x08\x00V\xa7(I\xde\xc8\xad\x9cd7\xea\xc1\x9d\x9d)N\xd2\xaa\x00\x1c!\x11S\xc3\x0b\x92\xc5\xa0\x89\xec3\xeb}\x94\xee\xe5\x8f?\x06i\xa9n\xef\xf3\x93KXV|\xb3\xdb\xe5\x7f\x9d\x9e\x0f\xe8\xf6\xed\xe4\xf4\x8f`7C'\x0f\xa5s\x986v\x8c\x06\x99\x14g\xe7\x86\x06FUV\x92\xb0\xad\xc6/$\xfa$\xc2w\xa7\xb4\xcd<\xf6\xad\xa1\xc2\x88\xb1\x9b\x98\x06\xad_\x9e\x1a2$[\x0e\xed!\xd97cHM\xd2^\x94ge\x12+\xef\x03\x1f\xfc6\xe1\xb7\x11\xb1x\x85k\x9e\x94%\xdb\x9cR\x1f\xe5\x05\x8aI\x8a\x97$\x06\xe6\x16:\x90d\x8b\xaf\x8d$\xfb\xe6\xe4K}'h\xc5\x9c\xc9\x0cW\x0c\xb9jP\xa1'\x0eUgB>\x91,\xc2\x8b\xb2J5JRn\xf1S,\xc4\x8f\x88j\xcb\x03\x90V\x0c\xc3Q\xe0\xf0\xb6\xfc\x15\xd5\xef\xf0W)\xe5BZ\xb2\x8c?FX\x8b\xeb\xc0,\xb1\x9f\x8a\x83\x8c\xeaP\xd7\x19N`\xa6\xc3\xeb(\x8e\xd4\xc6R1\xb0\xbe\xcb\x00\xe8\xd9\xd2ZG\xc9Z+I=\xd6g\x07\x14\x9e\xed\x13}%\x92O\xb9\xe5\xca\xa7\x1cS\x8a\xa3\x99\x18M\xa7\xa2\xb2\xbdIp\xe4\xae\x8c\xdd\xdc1r\x1d\xf3\xb3{0\xe3\xba\xc1b\x89\x91\xe4\\\x9eI\x87\x86\xfc,\xd3\x94\xec$k\xc7\xd5(\x11FMh\x81\xd0\"#\x84\xc8 \xb0\xb1\x04\xd6\x1cJ\x04\xf2\xe1\xadq\xa3\xe8\xf1\x9d\xd1ArB\x83\x11?\xf54lr\xa8O\x1b\xcb\xe11>\x1a\x12\x84%\xb0\x88\xc2M`\xcb\x18q\xa6\xf5\xb2\x01\xb2G8\x0b\x82\xbe\xc9M\xe0\x8f\x81\xaab\x90\xf4\xfa\xf1Ok\xe1\xd0\x0b\x842\x19#\x87b\x98\xc6\xd8)\xaf0\x8d\x01@\x15HK \xa0\xa6@\xdb,\x06\xd9\x9e\x81\xba\xe0\xe9`\x9b\xc50\x88Wch\x17\x84\xb6Y\x0cp\xcaG^$\xc3(_1\x18\x14\xf5\xc9b\x90ry\xcc\x1c\x06\x0b\xc8\xf12\x18\xa4}\xfd\x9c\xf9\x0bMzF\xb0,\x80\xa2\x03\xcao\xd4\xc11p:\xf1\xef\x83~\x99\x0b\xad\xc9\x0e\xab\xa4M\xe1\xd1\xca\x8a\xa8 \x0e\xcc\xa1\xb5\x04\x9e\xaf\x9f[\xeb\xcaW\x18\xc8\xc5\xd7r\x04\xb2\xe0k9\x07y!\x8d\xc0\xb0\x90>\x07\xb2i(\xfd\xa3hr(\xdd\x9dT\x881\xcfHV\xe0\xce\xb3\x12\x08\x14\x17\xb8(\x89\xddg\xa6 \xc9 M\x87\x80\xda\x0e\xf5\xcc\xd8C\xf0\xf5\x83\xe0[\x0d\xf5\x9c\x18\xd4#\x83\x0f\x0c\xa8\xaf.D\xc32\xf9\xd0\xc6r0\xa4#\x83\x00i\xaf\x9c,\x04\xcbdz\xa5\xbc\x1c\xa6A\xbb\xa0,Z\xb4'\x84~s\x10R\x1e\xa8\x1f\xcbW\xe3cP\x95\x04\xe1\xf5\xce\x93D\xbd\xb3\x05\xd1srd\x84\xc3\"`\xa9\x0e\xcd\x1e\x14\xd7\xc2\xee\xe4Ay\x9c[G\xee\xa0\x15#1\x9e'\x9fP\xfd@\xa2\xaa\x95qic\xa3NO|\x9bL\xd5;\xd1$\xfe\xb5\x9b\x84\xc8\xff\xd2]2\xad\xbb%\xfe+\x11\x92\x17\xe5\x8bem\xf1\x89?4S\x9d\x04R\xae\xbc\xc7\x80I\xe07\x04\xfc~\xd3\xe0\xca\x0e\xcf\x022\x06A\x11\xce\x10Ix\x11\xee A\x98/\xe2\x1dD\xde\xdd\xb8\xb6\xe4\xe7\x83\xcf\xbf}\x9a\xe0\xc3\xdd\xa3\xe9\xfb\xa3\xdd\x0fG\x9f\xf1\xee\xa7\x8f\xf8\xb7\xdd)\x89\xf0\xc1d\xff\xe8\xe0\x90\xec\x8b\xaa\xedJBD\xee\xe7\xe4\xa4R\x97\x03\xfap=\xb8{\xba!\xfbO\xf8\x89V\x1f\x8f\xe8\xe3\xd1\xe3Q\x9a\xde\x1f=F\x9f\x9fhy\xf7\x98\xde>\x90\xd4\x86q(\x15q,v\xd6E\xe6z3\xf4\xe8\xd3\xfe\xfb\xe9\xa7I\xb4\xfbq\xff\xe3o\xbb\x1f\xc8\xe4h\xf7\xf3\xd1\xc1t\xf7\xf0\xe0\xf0\xe0\xe3o\x07\xd1!\x89Z\x0c\x15\x83\xad\xc4R\x01\xe2\xe0\xee\xd1\xc9\xd4\xcf\xe5]\x1a\xcd\xde\x97\x8f\x0f\xd9\x87\x0f?\x8f\xf6\x7f>\xdd\xd0OE9\xbb\xbf[N\x8b\x9fQ\xe1\"\x87\xbf@\xcc\x98\x90g\xe9\xb2f\x01Jx\n\x9c\xe1\x88\xc7i\x99\xbb\xf0\x93\x0fDX\x05_\xf0\x9ei\xa49m\xdd*i\xf6+\xa1\xd9\xe0\xb3\x0f\xc4\xc1\xdd\xa1\x93\xcb\x0f\x1f\x0e\xe3\xbb\xc3\x9f\xf1\xfd<\xc6O\xd5\xc3S\x84\xe3x6\xfbt3\xaf\xde\xcf\xa3'\xf2\xde\xc3\x00\xf7\xe9{\\\x06\x98\xa7f\x915\xd6\xf0\x05\xd2\x1cM\x93\x8c\x0b\xc4\xc0\xca\xe4z#\x93\xb6\xac\x98_\x99dl\xe9 \xb3\x93\x84\x07\xd5G\xa6\xef\xc5\x01m`z~\x03\xe3E\x03\x1d\xb1\x1c\xa6)\xbe\xe14\xe9G\x0cr\xf53D\x03\xcfhJ\xfd&C\x9eJb\xa4\xda\xab\xc3g\xb9\xc8\xb3\xd2\xcb\x1a\xa9,7\x879\xa6z\x0f\xb1\xc7\xbb\xe6\xd4Y\xb1'c\xc8\xa3\xc0\xc3\xebvxN\xc6\xb4\x10r3F\xfe\x90S\xe8\xbc\xdc\xe5 x\xf0S\x88\x11\xf5\x848L\x1f\xf4\xcc\x8ch!\x04Y!\xce\x9ch\x0d#C\xcb\xbc*\xa4\x0d\xd6d\x83\x1f+YT\xa3\xbc\x90,DuH\xa2\xf8o\xc6\x1a\x19\xc6N\xd0?+R,\xf7T\x1ftq\xfe\xa5\x05N\xe4\xbf\xd6\x08\xa8\x00d\xe3g\x0d|N2Te\xe4qA\"fH\x8a\xa7\xab\xd4\xd0F\x9f2\x9a\x919n\xce\x9d\xd3\xact\x9b\x94\x1c~w\x05xtD\x94\xc7\x16\x89+:\xb8*\xaa\xab\xa5\x92d\xf4\xfda\x87?\x8ej\xe4\x1e\x1cbBq\x92nH\xe5\x10\xd6\xfd\xba*\x9c\x8f\xe6\x04\xf5-O\xb8\x1d\xdc\xdb\xea(Y\xe0\x02\xcf %\x85\x81\xf3\xae\xc816-\x17\xd7\x1al\xed\x89\xc1\xb6\xce\xb8\x16N\x92\x1d\xa3\x05\xa6f<(\xdb\xedIA\xe2cD\x8b\xca\xdc\x1eV\xb6)\x0e(;\x13H~\xdf\xd3\xd0\xb03\x90\xcf4\x1fr\xc6a\xdc\xbac\xc2\xc9\xca\xae)NK8\xbf\xf4\xf9\x05\xca\xb1\xde\x07\x9e\xa1\xc7\x1c\xcd\xb5\x064\x0b\x07\xc7>\xd2\xacp\x90\xb1\x1d_F\x9c-\xc30\x07N\xd7`S\xde0\xda\xad\x14v\x0d\xf8\x11\xe9l\x18\xdd@J\x87\x1a\xea\x86I\xde\x80\x07\xb7B\x87\x10\xde\xb5\xac\\6W\x9b'\xd2P\xee\xc9\x95>\x16\xbaa\x8bo4GZF6\x90#\x03M\xf3\xda\x08o\xf3\xe4\x059\xd0\xb2\xae\x81\x1c\x18d\x93\x1b\xd6w\x03\x98\xc3\x12_'\xfd\x14\xdf4l\x9f\x7f\xcaq\xf6\x98l\"\x19\xce\"\xb27'\x14\xc7\x98\xe2\xbd\xfb\x83=\xb9\x9c\xf7p*\x0d\xb9\x1b\xa2m\xf4\xb2\x9a\xcfq\xb1\x0fLR\xa2\xe4&\xcb\x0bS\xb6\xf8\x7f\xee\xd9\xb6\x05a\x87\x1e\x8f\x03\xf4\xa5\x16\x8cD\xac\xbdX\x92z\xdf\xe2\x82\xb4\xf6\xae\x8f\x0b\xc2\x1d\xc6\xc6\x17q\xeb(/bR\xbc\xfb\xc5\xc7\xba\xcb$\x8b\xc81\x8a\xf2r\x9e\x97\xbbe|\x8b\xf6\xdf}xo\xed\x10\x8a(\x14\x8a[\x1bs\x02o2\x9f\x908\x16\x9a\xfd\xe6\xe2\xfc\x8b6\xd5d,_\xe9Y\x87Z\xfa;\xcb\x01\xebE\xfa\x0e}\xed8\x8f|\xa6^x\x92L3N\x14\xb5\x92\xde\x1dsc\xd4\xde\xcc|\xea\xb2O\xddF\xf9\n\xc7\x1ae\x17\xf9\xccx\x8f&\nZs\x0d\xfe\xfc\xafk\xc6\xb5u&\xd5\x8f0\xd4\xd8\xb4/p)#V\x8dU\xf1\xcek\xc8q\x1b-h\xa0\x9dJ\x1d\xc3\x97\x16W:.\xfd\xcf\x8e\x0f\x84\xef\xa0,G\xf3\xbc\xf0\x9c/\x11\xf2\xa8\x1403=*=\xb4u\xb4\xe4t)\xf0Zl;\xe95\x19m\xc8c\xd7\xde\xe1\xf1\xf8\x84\xee\xa0\x84\x96*\xd1\x9c\xd9h\xe2<\x13\xa3\x9c\xf1\xef!iU\xd8\xf7\xef\x9b\xba\xe0Q\x9f\x12\xaa\x9d2I\x1a\xdc\xf9\x97m\x11\xd5m\x11\xd5\xc0\xf6{\xe6\"\xaa\xdb\xb2r\x7f\xcf\xb2r5\xfd\x86\xd5\xd1<\xf3\xfa\x94\xe6\x00\x87\x87tk\x98P\x86\xf81Z\xde\n\x13\x9c\xd3=1\x84\x91\x9dMf52,L\x14\xf8\x01\x97\xd1\x10\xb7\x84\xdf\xf9\xd0\xd7\xdd\x10\xe4g\x13\xfc\x9a\x99\xdb2:,\xec\xe5\x0e\x02 w\x879\x13\x0c\x97A\x03\x9a\xc5}0\xc8a`u\x0b<3\x17M\x8b\xaa\xee\xef\xe3e_w@\xf7\xd0\xdf\x00gu\x00\xf4?\xf27\xcf/\x0d`\xddC\xfe\xa0c\xbdqxo\x00\xb3\x1f\xe4\xbbG\xf7!\x13\xbb\x92\x10\x97\xc7p\xe0\xb4\x0e9\xb4\xdb*%\x87\x8e\xe9\xc1\x83\xf9:\xf9\xe4\xaa\xa1\xfa&PDuA\xa2\xbd\x7f\x95\xad<\xc8\xff\x93\xf5M\xdduU/\x1b\x01$\x82ku \x9dV|\x89:<\x88\x9cR\x9e;\xac\x96\x8f\xa5\xfaj\x03\xf4/\x8aK\x1b]\x86\xf5\xbaA\xaf\xcbJ\x1f\xe46\x08@F!\xe8(8\x02\xea\x95\xae\x0b0\xcf\x01\xce\nT\x9f\xb0euT[\xad\xd5\xe6*\n\xe4K\xfb#&Ah\x87\xce\xfcHUy\xcb\xa7r\xad\xcat_\xe9\xde\x10\xa9\xbeR\xcc\x06JO6iK\xbc\xa5\x1eC\xc5\xf9|\x87@\xd5\x82%X\x82\xabH\xb4\xf0Z\x12-\x14\xc0\x8a\xa0\x93\"\x7f*\xa6\x86\x87\x99\xea\xf5\x11\xae\xafE\xa1\xa5\xce\xfa\xa3\x02\x88g\xc1\xf5\xf8\xbc\x18f\x12\xe14]\x8aJ\x014Wu\"\x83`x\xcd\xc7\xebp\xf9K\x9d\x896\"\xb1\xa0\x9d,\x1a\x9c/t\x94\xfa\x7f\xbd\xab\xffA\xea\x01\xf5`M?z\xc7\xab\xfa\xd7\xbb\xe6\x1f\x0c\xd1S\xb6\x99\x9aj]\x05J\x88\x92\xa6;\xf5j\xdeA8\x0b$[\xe4U\x11\x11\xa3\x00\xd7[\xf9\xb0H\xab\xcc\xf1\xaf\xcc\xee\x12\xe3x\xe1ai7\xf8.F\x0c/\xd0J\"\xfe\x84\x9b\x89|Vx\x95\x13.\xe3e\xa1oTb\x9a\x94LO\x89r\x0b\x8bE\xba\x0c\x89z\x9b\xc5\x84\xb3XU\x86\xc0\xf2\x07\x0e\x08\x00\x11\x06Z\xb40\xca\x19\xa8Z\xca\xf2\xb2U\xdd\x12\xbf\xe1\xf2\x1f\xa8)\xef\xb8\x04\xe3\xe5\x99\xb4\xe0s-Xa\x92_s\x92V\xa2\xe5\x8aQ\xa2\xcf\xc0\xa8U9\xab\xa5\x7f\xdf\x86\xeb\xb8\xcbbsy\xa1J\xd1\xfdqzy\xe5\xdev\xa09 \x15\x96\xdaE\xbf\x7f\xfdv\xfa\xfd\xf4\xea\xf4\xec\xfb\xf5\xd5\xff\x9c\x7f\x05\x96Z\xea\xf6\x03Tm\xecv\nVjtu\xe1\xbcq\xf6\xd3U\xa4\x86\x10\x17\xbe\x06T\xcd\xcb<\x1f\xd7=\xdd\xa0U\x1be\x95\xc6=Y\xb5QxH|\xa2\xcb=c]6Y\n3\xe2\xba\xe0j}1\xc4\xfe\x8b\xdf\xa8\xbd\xd5\x1a\x96\x8b\xb2\xa4DYNu\xa9\x16\xaf\xd2t\xcdp\x17+K1FU\x0eY\xc9\xceNA\xc6\x96\xde\x1f\x82\x08_j.l\xf8\x1f\xed(\xe1\xa5\x0d\xa1(OS\x12Qy=\xcc\x7fZ\xd6f\xc9\x0c\xdf\xfb\xcd\x02\x06\xbfd\xc2\xd3\x93\xfb$\x0f\xa3\xc9$%\xd7\x0b\x1c\xb0\xa4G\xb4\xec\x836MH\x18\xb198?\xb9\xb8\xfa\x1f\xf8f\xedt:\xbb8\xfd\x8f\xd3\xef'Wg\x17\xf0>\x97_/\xfe<\xfd\xf2\xb5G\x8f\xd3\xef\x7f~\xbd\xec5\xc6\x97\x1f\x97Wg\xbf\x9f\x9e|\x87w9\xfb\xeb{\x1f\x9cN\xbe};\xfd\xe3\xf4\xe4\xea+\xbc\xcb\xd9\x7f\x7f?\xfd\xc7\x0f\x7f\x0d\xc0F\x87\xf3\x8b\xb3?\xbf~?\xf9\xfe\xa5\xc7 _\xce\xbe_]\x9c\xfd\xf1G\x1fZ\xfe<\xf9\xe3\xf4\xf7\xc0$j!\xdf{\xc9\xc0%\xbc{E\xba\x86E\x81\xe7 p&/\xa0\x81e\xfe\x1c\x8b\xfb\xd8\xfe\x99\xdf\x81\xf0\x12\x99\xfc2\"\xe1\xfe\xcc\xd0I\xc7\xba\x17\x8em\x1f\xeb\xa8\x97\x98L(*Iq\x9fDIv\x83\xa6U\xc6eZ\xa04\x9du\x0f\x1d\xdb>\x8a\xcb\x1c\x9e\x0c\x90D(\xc9\xeeI\xd9\x8f\x0e\xbd\xdf\x8e\xad_%\xa3HF\x13\xba\x14JK\xd3\x16U%\xcd\xe3\x04g\x92@\x11\x8f$\x98\xda\x87@\xbe\x7f\x8f;_\xda\xd5\x84\x99\x88^J|\xf2\x87Lx\xe9\xf9U/%\xf3\x1e\xc3\xe9\xfd\x7fl\xfd*8*\x06\xe3V6['\xd3i\x92&\xfc\x88pS\x102'Y\xa0@\xa5Mz\x1c[\xbe\x89\xc1\xb8\x05\x8cSa\xf2\xe7S9\xb8\xb8\x92\xcb3Z\xe4\xa9 z\x9e%\x93\xaaD\x13\x9c\xdd\"\x1c\xf1\x0b\x86\x1eh\xd42\xe9\xd8\xfeY\x9b*\xcd\x1b\x16\xcd\xfa\x82,\nR\x92\x8c\xca\xdc(]\xa7Y<1\xd9z+\x04G=7m-\xff\x8e\xed\x9f\x9bk\xf1a\x96D3\x83?\xfa\x1c\xa1v\xb5va$\x04\x91,\xa7\x01\x9f\x8dC\xb0\x1e[\xbf\xdaP\xe1\xf6%_\xae\xc2\xbb.\xf6\x01\xc4\xa4\x82\x1c\xa2\x10:g\x93\xc0\xcfR*\xdd(N\xa6\xdcf\xa2\xa8\xc8S\"\xa6)\x11f\x92=\xc1I5=Is\xbcdS\xee\xb6zd\x91\xed\xc6\xc24\xec\xa6\xf6a\xce\n(D\x9e\xedT\xae\x1c\x1e\xdc\x8e\xeb\\a\x04\x0e\xea\xe2\xba\x8f\xc9[\x9c\xa6\xf9\x03\x89\xf7\xd4\xdd\x8et$\xec\xc9z\xda\xd6\xfe0\xad\xd7\xf1\x1aSK\xcd\xc5\xd2r\x81\xd2n\x9e\xba\x8c\x96*'v\xb3\x10\x86\xb2\x05\xdc\xb0\xa2)\xab\x94L :\xb7\xc3n\xed&\x1d!\x93}\x8c\x0b\x12\x18\x7fQ\x0774\xb8HJ\xd9'-\xdcZud\x93\xf9\x12J\xfa\xf6\xac\xbd.0 W\xfa\xe4\xd7n\x00\x87\x86\xa4r\x07\x93\xb8\x87q\x0eT\x08e\x03X\x06+\x04\xe9\x817\x98I\xfe\xfcu\x10k\x86Q\x1d\xccU\x1fk3\xf5,y\x02\xa7f\xb4r'#\x16; \xea(\x04\xd2S\xa8w\x99\x13\xd0JA\xd0\x8d\x84zM\x04\xeaQ\xdeD\xff0\x00\xaf\x97\x16CCK\x9bl\x1a\xd7B\xda-\x00\x8e\xf6,\x1d\x01(\xad\xf0\xca\xf87D\xf7u\x01\xf5/b2\x9c\xeb\xe1\x02&`&\xaf\xc2;\x88:X\xeb\xea\x1bq\xb1\xad\xc2\x87\x95\x0b\x96\x00\x96d\x97)a\x8c-'\xddP\xdd\x12\x0b\x94\xb2s\x7f]\xd75\x01\xd40ymUHJPD\\p]\x85g\x07\xd9\xde\xa1\x88p\x86d\xc0\x04\xbf\xa6bS\xbf\x83\xc8\xbb\x1b\xd7\xa6\x88\xc9\xf4\xe3$\xda\xc7\xbb\xd1\xe78\xde\xfd\xf0\xe9\xb7\x0f\xbb\x9f?|\x9a\xee\x1e\x1d\xee\x7f$\x1f\xf7?\xee\xe3O\x1f\x84\xc3L\xee\xd0\x80.m`\xe4\n\xba\x90\xaa\xd5\x8b\x98\x1a\x87\x01<\xb8\xdb\xdf\xdf\x8f\xf7\xef\x0e\xc9\xa7\x87\xa3\x12/\x8f\xde\xe3\xe9]L\xca\xc7\xc5\xe1\xd3\xdd\xd3m\xf1a\x9a9s\x96OE\xa2\x00N\xcb\\]%Z\xee\xf1\x9b\x189@\x15$\x12\xd8\xcc\x9c\xd8<|8\x8c\xef\x0e\x7f\xc6\xf7\xf3\x18?U\x0fO\x11\x8e\xe3\xd9\xec\xd3\xcd\xbc\xba\x9b\x91\xa7\x0f\x1fl\xd4\xbaO*#\xad\x12\xf3\x88\x91\xc90D\xc3\x0dBs\x94\xe6\xf9-\xaa\x16>&\xca\x0c\x1d\xe1\x96\x9av=]\xd2\xfe\x12K\xce\x1d\x9d\xd6\xb0{-\xc6\x96\x17\x01\x95(\xee\x1d\xdf\xeb\xac\x00\x9bx\xdbbB\xfa\x97\x7f\xf3bB~\xac,:\x12\x94\x10\xdb\x02c\xa6\xc76]\xcb\xdb\xccX\xd1\xb6\x99\xb1\x1b\x94\x19\xdb\x96\xbc\xae\x85\xd8\xda.C\xcd\x95q\x8c\x14\x8fi\xd2P\xff+\x9a\x1e+\x1b\x1c#\x9b\x19Iv\x8c\x16\x98\x9a\x0fz\xd5\xb9C\xb4\xa8\x82\xb9sj\xd2[\xb1I\xbe\x89\x1ejq\x0c\xb03\x00s\xed\xb0.z\xdb\x14\x9e\x0b2\x8b%1$i\xcb\xc1\xf9m\"\xfa\xdf3\x11\xdd\x95\x9b\x17N\xcd+\xf7p*\xb5 0\x17\xaf\xa0\xb4BW\xa2S\x9fN\xe1\xd4BWrQ\x9fN\x90\xf4Bg2O\x9f^\xc0\x14CW.\x8c\xb7\xd3\xf04\xc3~:\xc1\xbdV\x07\x0d\x8dT*O\x9fdC\xe7\xc2\x1f3\xdd\xd0\xb1O\xd6\x92p\xe8\xd8_\xa3\xa5\x1c\xba\xf6\xe2\xb3&\x1d\xda\xf6\xf6Z\xd3\x0e]\xb2a\x8d\x89\x87v\xc9\xf2\x02\xa9\x87Ny\xb51\xc9\x87N\xd9\xf82\xe9\x87.\xa1\xfb\x1c \x88\xd0C\xda\xa8I\x88=\xd2\x10GJD\x84\x909 \x191\x00k\xc5tD\xb8v\xec\xf8\xbc\xe9\xc0\x94DoRb\x8f\xb4D8\xea\x16\x90uDl\xbfL\x90U\x92\x13A\xeey\x88c\xbeIO\xf88\x10\xb4\xe2\x81\x8e`(\xbf\xd1\x88\xa9\x8a=b\x00EkMv8\xc5cSx\x14J\xec\x08\x06\x96C\x13\x90Z\xc3\xf6\x8b\xb1\xdf\x18n\x0dK\xe3\x08&q\x0c\xe5\"(\x8dq\x83\xd8\xb7}\xcf}\xb5\xc4F\xf8v\xebd{l\xdfs\xd7\x0d\xa2\xedP\xefDG\xf0\xfaA\xf0\xad\x86zN\x0c\xea\x91\xf0\x08\x06\xd4W\x17\xa2\xa1I\x8f\x9b\xca\xc1\x90\x8e\x0c\x02\xa4=\x13\xd0@\xc9Z\xaf\x94\x97\xc34h\x17T\xffT\xc8~\xc9\x90\xfd\xd3!{\xb1|5>\x06UI\x10\xde\xf6=\xf7\x00@`\x82$\x0c\xefq\x92$WN\x93D\xed|\xc8\xa8\x1b\xd9\xe8x\xf8\xdd\x11\xe0\xa9\xdakK\xc0\xdcfn\xe9_\xfe\xcd3\xb7D\xdb>\x03\xdfi \xd1\x0d\xb2\x1c`\xccC\xc3^E\xf3@\xdb>\x03\xaf\xe7g\xb4\xe7\xc5\x87\xbc\xb7\xe6\x01\xb7}\x06^\xb4\xd1\xe7i\xd8\xcbm\x1e\x80t\xfb\x0c\xbc\xd9\xe03\xd1\xf7\xdd7\x0f\xa8\xed3\xf0\xdbg\xe0\xb7\xcf\xc0o\x9f\x81\xdf>\x03\xbf}\x06\xfe\xd5=\x03\xdfI\xfc\x1c\xb5\x04\x06\xcfL\xdd\x96\xc1\x10m[\x06\xc3\xb71\x9f\xb9\x0c\xc66/\xff\xef\x99\x97\x8f\xb6\x0f\xc4\xf7\xaac\xe10?\xb6\x0f\xc4\x8f\xcb\xdc\xf0\xd3\xe6\xdb\x07\xe2\xc7\xe0\xe2\xf6\x81x\xe8\x81\x1fm\x1f\x88\xff\xfb=\x10_F\xf9\x82\xec\xfd\x8b\xff_\xe0M\xf8K\xf6\x1bT\x12\\D3\x95\x1a\x81xG\xb5\n\\\xec\xbf\x9a\x11\xf1C\x94\xc4;\x8c\xe3\xf2\xc8\x1e\xef\x88\xa4\xbf\xba\xaa\x98\xf8\x95\xa5\xb2\xd8\xe7\x83\xcf\xbf}\x9a\xe0\xc3\xdd\xa3\xe9\xfb\xa3\xdd\x0fG\x9f\xf1\xee\xa7\x8f\xf8\xb7\xdd)\x89\xf0\xc1d\xff\xe8\xe0\x90\xec\xa3\x1a#]\xb2\xab\x9e\x1b^-\x8c\xff\xf1\xe0\xee\xe9\x86\xec?\xe1'Z}<\xa2\x8fG\x8fGiz\x7f\xf4\x18}~\xa2\xe5\xddcz\xfb@\xd2w\x02gR\x96*\xd4f\xc7<\xda\xb5\xf0\xe7\x19\x82\xea\xfe\xdf\xe8\xd3E@\xff\x87\xfc\xd9\xc1\xdd\xa3\x13\x95\xcf\xe5]\x1a\xcd\xde\x97\x8f\x0f\xd9\x87\x0f?\x8f\xf6\x7f>\xdd\xd0OE9\xbb\xbf[N\x8b\x9fQ\xc1q\xd4\x00\xe5%\xb2B\xd5\x86b\x8d\x8a\xc4U^)\xdb+\xbc\x8a?\x1e\xdc\x1d:\x11t\x176{?\x8f\x9e\xc8{sg\xfc\x1b\xd3 \\\x0e\xc9\x19\x92\x11Q5\x9a\\\x9c\xcaUR\xea\x1d\xf8\xce\x01B\xb0O\x07`\x98`\xd4*\x927\xfa\xc2\xeab\xb0E\x1f\x0d/<\x8a\xc1S\xe8 \x92\xa5\x86\xab\xc95\x06\xf7ypsE\x9a'\x8a-;\x0d\xeax\xf1\x85\x06&\xb8 \x96\x85\xc8~Egd\x89\xe2<{CE\xf8\x1e\x17r*\xd1\x94\x837\x96#F\x13\x1c\x9b\xa1\x00\x06\xa2\xfaG\xe7\x1c>\xcf/h\xacl\xc4O\xe7z\xf5\xa08'\"aX\x18\"\xb4\xde\xcf9^6\x00\xf5%\xcf(o\xcd\xb5\xc0\x0d\x8b\x88\xb0\xc6D\xf8\xcdA\x80\x0d\x05\xb1\x9f\x9e\xff\xca\xab\x1d\xb8\xa5\x11p\x07<4\xebk\xb8-Kc.B\x91\x0ck\xa5) \xe4:h\x0e\x0dg0A\x81\xb8\x02 v\xda\x10\xcet\x83\xa4\x8c/\xc0\x07\x80\x8c\xc5\x03\xe6\x90\xfc\xe7\xeb\xe0\x91\x81l\xed\x900\xbeI>y\xe157\x19\x88O\xfe@\x0f0s\x86P=B\xacG\xbf]3\xd2B\x18B\xebJ\xc1\x1e\xce\x85\xe2!Y\x05\x15\x8e\xf9`]\x03(r=T'#\x00\xbd\xd1I\xc8\xf2@\x1dd&A\xba\x12\x01\xf5%B\x90Pm\xd1\x80K\x03\xc1\xe5\x04ZO\xf0\xb6hz\xa6\xc2\xba\x13=\x0fu\x80\xe3L\x0b\xe9\xe1\x9a\xd4\x04\x06\xe4\x13L\x9b\xa2\xcd\xe3\xd5\x18\xba\x15!#+\x02\xc8\xb3\x90\xde@\xfdX5\x8c\xfeQ4\x08\xea\xb9ZF^$\xc3(_Q\x9f \xfb\x12\xb2\x93\xdfy\xd1m\x8c\xb3\x96\x15\xe8x'.{T\xdb\x9a\x0f`m\x9aF\xb05\x80\"\x04\xcau\xd4\xe3e\xd3\xf0\x85\x16\xea\xb8.\x82\xca\xa93\xeda\x05\xb59\x9cZY1\xb5\x01\xf6\xe0S\xd4\xeb\xd1\xc8\x0d\xe2Y\x17q\xc9\xbf\x1e`,)P\x83y\x19R\\@\xd6\x0d\xe7 Diy\xc1\xadB\xfdH\x0bg8\xf5+*.\xc0\xf2\xb2\xb1\x02\x82\xaft:\xba_\x14\xd5\xbe\xab\xc6\x83\xa1VX\xbd\x1f\x11\xb5\xe2$\x07,E\x10\xc5\xd2\xa80\xad\xab3\xea_4\xd2\xa1\xdfZ\xdf11\x0bu\xb6\xff\xc2\x9d\xb7\xdd\x85!\x96\xc3\xf3\xe6\x07\x08d\\\x8bt$U\xfd\xec\xb1\x18{\xf5\x92\xa8\x83\xa9\x05\xa9\xef\x10\xfa\x93\x171\x90\xa9\xe9^X2~\xdfRX\xe3$-st\x9b\xe5\x0f\x19\xc2l\xd5~c\xe6\x8f7H\xe3%\x1c\xc60\x8e\xd5\xe9 j\xd3\xa9\xdb\x0e\x95\x97\xd0\xca=\x08\x08L\xcdk\xf4\x96\xd7\xcbL\xe8\x0cM\x93\x94\x92\x82\xc4\xe8\xf6^\xe9uJ\nL\xf3\xc2\x1d\\&#\xd7\xbd\xec\x02\x11(\x01))\xd3\xb0w\x15eb\xaa\xbd\x85 \xb4\xa4Y6\xef\xa0r\x1e\xcd\x97O\xa72n\xae\xf9\"0\x94Y\xc1E1\x9a\x1b\x06\x98T\x01\\\x8a\x08<\x0d\x08u\xee\xd5;!\x90|:\xd4\x96\x0dB\x93\x13\xeb\xfd\xdd\x0c\x97\xb3\xf1Iet0\xc8\x9c\x88\xcc\xa8\xf8\xa2\xd6Z0\xd9\"$\x14\xd1 \xbc\x00S\x80E\xb8\x9a\xe5\xeeZ#\xff\x96\x0d|\xcd~\xe6\x8f]bM\x87\xa7\xb1M\xd0\x9a\xcd(\x9f\xcf\xf3\x8c\x8f\xe7\x8f!\x15/`\xbd\x08;\xc4\xd0\"\xb1\x15\x86\xe3(\xbe\xd91\x14\xdf\xec\xf8%bC7,\xb0EA\xee\x93\xbc*\xf9\xdb\\\xcd\xadn$Uyq\x95\xc2\xe6\xaa\xc0\xd1\xad\xf0\x88 \xcbI\x9f\x00\x89\xd2I\xd0S\x19Hz\x99\x83t\xeclF[4K\xc8\xbdx\x0b3\xafhH\xec\xe4\x19\x08;\x01\xe9\xd5Z\x85/\xac\xeb\xffS\xeay\xaa*\xab\nv\xc2\xca4\x88&z\xeci/@\xdb\x0d\xf5\x02\x1a\xf4\x92\xc3Tt\xa9%X\xbb1\xea\x03!\x00X\x9d\xdf$y#\xb7r\x92\xdd\xa8Gvv\xa68I\xab\x02p\x84DL\x0d/\xc4Cq\xc1\x1f\xf7\x9a\xf5>J\xf7\xf2\xc7\x1f\x83\xb4T\xb7\xf7\xf9\xc9%,\xa5\xbe\xd9\xed\xf2\xbfN\xcf\x07t\xfbvr\xfaG\xb0\x9b\xa1\x93\x87\xd29L\x1b;F\x83L\x8a\xb3sC\x03\xa3*k\xbe\xac\xeaj=\xb3\xe8\xbbS\xdaf\x1e\xfb\xd6Pa\xc4\xd8M\xfcQr\xfd\xda\xd4\x90!\xd9rh\x0f\xc9\xbe\x19Cj\x92\xf6\xa2<+\x93Xy\x1f\xf8\xe0\xb7 \xbf\xb5\x88\xc5\xa3[\xf3\xa4\xe4/\xbdJ}\x94\x17(&)^\x92\x18\x98\x98\xe8@\x92-\xbe6\x92\xec\x9b\x93/\xf5\x85\xa2\x15s&3\\\x01\xe8\xaaA\x85\x9e8T\x9d \xf9D\xb2\x08/\xca*\xd5(I\xb9\xc5O\xb1\x10?\"\xaa-\x0f@N2\x0cG\x81\xc3\xdb\xf2WT\xbf'\xcf\x9f\xd7\xcc\xa7\x8ae\xf2\x1dd%\xae\x03\xb3\xc4~*\x0e2\xaaC]\xa48\x81\x99\x0e\xaf\xa3\xb2R\x1bK\xc5\xc0\xfa.\x03\xa0gKk\x11&k\xa1%yy\xe4\x00\x17\x9e\xed\x13}%\x92O\xb9\xe5\xca\xa7\x1cS\x8a\xa3\x99\x18\xad~40/xB\x94\x13Vs\xc7\xc8u\xcc\xcf\xee\xc1t\xed\x06\x8b\xf5\xfb\x8bb?d\xd2\xa1!?\xcb\x1c';\xc9\xdaq5JxR\x13Z .\xc9\x88?r\x02l,\x815\xc7!\x81|xk\xdc(z|gh\x91\x9c\xd0`\xb8P=\x0d\x9b\x1c'\xd4\xc6rx\x80\x90\x86\x04a ,\x1cq\x13\xd82F\x90j\xbdl\x80\xec\x11\xce\x82\xa0or\x13\xf8c\xa0\xaa\x18$\xbd~\xfc\xd3Z8\xf4\x02qP\xc6\xc8\xa1\x00\xa81v\xca+\xcc\x81\x00P\x05\xd2\x12\x08\xa8)\xd06\x05B\xb6g\xa0.x:\xd8\xa6@\x0c\xe2\xd5\x18\xda\x05\xa1m\n\x04\x9c\xf2\x91\x17\xc90\xcaW\x8c$E}R \xa4\\\x1e3\x01\xc2\x02r\xbc\xf4\x07i_?g\xf2C\x93\x9e\x11,\x0b\xa0\xe8\x80\xf2\x1bup\x0c\x9cN\xfc\xfb\xa0_\xdaCk\xb2\xc3*iSx\xb4\xb2\"j\x82\x03sh-Q\xeb\xeb\xe7\xd6\xba\x92\x1d\x06r\xf1\xb5\x1c\x81,\xf8Z\xceA^H#0,\xa4\xcf\x81l\x1aJ\xff(\x9a\x1cJw'\x8fb\xcc3\x92\x15\xb8\xf3\xac\x04\x02\xc5\x05.Jb\xf7\x99)H2H\xd3!\xa0\xb6C=\xd3\xfd\x10|\xfd \xf8VC='\x06\xf5H\xff\x03\x03\xea\xab\x0b\xd1\xb04@\xb4\xb1\x1c\x0c\xe9\xc8 @\xda+\xa1\x0b\xc1\xd2\xa0^)/\x87i\xd0.(\x8b\x16\xed \xa1\xdf\x1c\x84\x94\x07\xea\xc7\xf2\xd5\xf8\x18T%Ax\xbd\x93,Q\xefTC\xf4\x9c\x1c\x19\xe1\xb0\x08X\xaaCS\x0f\xc5\xb5\xb0;\xf3P\x1e\xe7\x9e-\xf1P\x8c\xe7\xc9;\xd4?X1\xed\x90\xff\xa5\xbbHZ\xb7I\xfcW\"\x08/\xca\x17\xcb\xda\xc6\x13\x7fh&7\xb1?\x95\xfa\xda\xbaK\xbd\xd7\x08\xf0\xab~\xbf\xa74\xb8\x96\xc3|G\xc6 (\xc2\x19\" \xaf\xd9=!\x08\xf3e\xbb\x83\xc8\xbb\x1b\xd7&\xfc|\xf0\xf9\xb7O\x13|\xb8{4}\x7f\xb4\xfb\xe1\xe83\xde\xfd\xf4\x11\xff\xb6;%\x11>\x98\xec\x1f\x1d\x1c\x92}Q\xe4]\xc9\x84\xc8\xfd\xfa\x9cT\xe3r@\x1f\xae\x07wO7d\xff ?\xd1\xea\xe3\x11}|\xf8y\xb4\xff\xf3\xe9\x86~*\xca\xd9\xfd\xddrZ\xfc\x8c\n\x1b\x17\x82\x971#1\xa1u\xf5\xa2y\xa0$K\x83\x05>\x10\x07w\x87N\x06<|8\x8c\xef\x0e\x7f\xc6\xf7\xf3\x18?U\x0fO\x11\x8e\xe3\xd9\xec\xd3\xcd\xbcz?\x8f\x9e\xc8{\x1bd\x99\xdd\xa2\xcaZ9Ln\xc1\x05_\xc5{m\xa3x~\x03cU\x1b#\xc1\xafi\x8ao\xb8D\xd5u\xf4s\xf5K\x8f\xf6\xd00\x1a\xf7\x17\xfa\xe0R.\xf2\xac\xb4\xc63($\xa4\xa0\xdd\x1c\xae\x98\xaaa S\x14\x88\xbeY\xba\xac\xed\xa6\"O\x05\xf1\xf3,\x99T%\x9a\xe0\xecVi\xc5\x9e\xa8\xd4\xb2\xec\xd8\xfe\x99!\xa4Jn\xa8y0\xa7\xa1 \x8b\x82\x94\xdc\x14\xe3!\x96\xba\xdc\x9btl5K\x0e\xe3h\xc0\xa6\xae\xe5\xe6\xb1\xfdss}>\xcc\x92hf\xf0\xa9~\xbd^\xeez]\x0d5!\x88d9\x0dT?\xee\"\xa4\x05\xf2\xb1\xf5\xab\x0d\x1d^d\x90/aQ\x00F\xec\x0fH\xad\x1a\xa4\x1f6\x0eY\x16\xe10\x87\xbaA\x02\x1e\xea&m5\xed\xe5\xd6\xd3\xff\xa6Der\x93a\xf1\xa2q\xa9Q\xf5\xc0\x83\xd9}'\xe8\xdc\xd8\xe5\xca\x1ae[p\x8f\xd7k\x16\\dv\xa1\x17L\xbb\x8cv\xb0\x04K\x1f=x\xc5O\xe5\xf29\xe1z# \x9a/PJ\xeeI*\x8f\x14\xe1\xa8Zu\xb2\x17\x16\xed\xbb6h\xee\xf7f|\xe6!\xa8\xf2\xa0\xef\x13\xae\xf5#\xe8K\xf7\x1b\xc6\xc2\x14\xe6\xa7)\xcb\x13\n\x02\x83\xe6Y\xd5\xbbN\xabL8 \xe9L\xe3\x9d&\xdcc\x9b7\x1e5t\xa6C0L\xaeq\xe4\x7fT`\xc4CX\xc0\xa2\x87\xaf\x83\x13\xcd$\x15\x03\xcc\x08\xe7\x1c\xc3\x15\x9d\xe5E\xf2$\xe4gA\"\x92\xdc\xfb\x16B]\xb8\x98\xcfK\xb7\n\xbcvH8\x80\xf0 \x8dk\xbe\xee\xae\x83\x87\xc3\xb1\xf9Po\xd4\xa6\xaab\xb3/\nx\xb7\x08\xf2\xaeM\xc3\xf7rIq\x16\xe3\"6\xd5\x8b\xd4w\xe2\xcd\xf39.n\xbd/\xee\xab_\xfb\xb6\x03\x9b\xb0\xb2Z,\xf2\xa2Q\x12\x92c\xcew\x84\xa8 U$\x93\x8a\x124\xc7K\xee\xbe\xf7\x00\x9c\x10&\xdd\xb3\x1b\x12\xa3\xc9\x92sA\xea\x84\xba\xecW\x9eE\xccnar\xae$\x96\x00u\xd1\xa4\x93\xe8\x9a\xcb\xdc\xeb\"O\xd3j\x11\x9a\xd6\x90d\x87j\x00\xf8\xfc\xff%\xd5\x03NS-\x02\x9aQ\xf1l\xaa\x12Z\"KdB\xbb\xa9\xbb>%T\x1b\x80\xde\x94J\xb4N\x13\x92\xc6\x9e\xd7\xe1\xd5\xb4\xa5e\x8eH\x86'\xa98\xf33\x91\xact\xd5\xbfsW\xb5@P@\xf5\xad\x91\xdaW\xaeh\xb4O\x1a\x8cm\"\xfeN\xbf\x9d\x8f\x8a<\xa7Fu]nI\xa3(OS\xc2\x0d\xa7\xfaJ\xc3\x85##\x80\xaf\xb6<#\xfa\"\xc4\x83\xa8r\xc0z\xc2Ha\x944\x00\xc1\x8b\x84\xb9=6\xb52\xb2\xfe \xe8H\x0b\xbb\xd0\x9e\xd3\xf9\xdc`\xa2y\x9fE\x07\xd7v\xd1\x1c\x0f%\x0c\xae\x99\x8aP\xce_M\x91'kJ\x03\x13\xff\xf0'L\xad\x89 \xafp\xb3 \x88|%VB~m\x10?\xdc\xb9\x10\x08\xca\x86\xbe\x14\xe2P\xbe\xdcX\xb4\x8d0\xc5}i[)\xf3\xcd\xb2\x00\xdc\x025\x9cJ\x0e\xc3\xbd\x0bmXa\x94nY\x14\xcb\xc5\xcc\x9a\x05m05\x1c4\xe7k\xd9\xd6\x81\xfco\xef\xb5\x11j\xdfR\xc1\xe464\xdb\xfb\xc5\x99\x02\x11\xefPX0\x96\x18\xff5\x82\x90X+oZ\xa865\x82\xfe\xec\x01gU\x93pn\xbd\x80\x82\x80eU\x8f\xbf0FX\n\x83H\x1dAi8VK\x9bhw\xde\x9f\xcf\xeb\xb1\xae|\x11\x89\x8d\x8b\xe5A\xb5\x10\x1e\x01\xa1\x97xw\x13\x1e-1\xee\xb8}\x96\x9f~\xdb\xb4.\x98\xed(1\x02z\xf4\xc2V2[2\xde\xb5\xf1\x90:4\x86\xd9\xb3\x99\x8f\x94\x04\x9d_\xaa\x01\xe7X\xfex\x8c\xe8\x08X|\x04\xd4S.\xda\xb81\x12\x03\xa2$z2\x12\x16)\xb1B\xac\xc4\xf0h\x89\xa1\xf1\x12C#&\x06\xc7L\x0c\x8a\x9a\x18\x1c710rbx\xec\xc4\xf0\xe8\x89\x81\xf1\x13\xabFP\xf4\x93\xf0\xa29\xa3(\x00}\xed=\x87\xc6Q<[$\xc5s\xc7R\xae\xf8\xcbS\xfb\xe7\xf9\xbd\xebi\xd8\xfa\x92g\x94\xe7bZ\xe0\x86EDXc\"\xfc\xe6 \xc0\x86\x82\xd8O\xcf\x7f\xe5\xd5\x0e\xdcj\x94Z\xb0\x07<4\xcb\x81\xb8-Kc.B\x91\x0ck\xa5) \xe4:h\x0e\x0dg@\x8d\xe2\x0d\xe1\xdf\x80\xe2\xd96\x843\xdd )\xe3\x0b\xb0\x86?m\x94\xf2\x80qH\xfe\xf3u\xf0\xc8@\xb6vH\x18\xdf$\x9f\xbc\xf0\x9a\x9b\x0c\xc4'\x7f\xa0\x07\x989C\xa8\x1e!\xd6\xa3\xdf\xae\x19i!\x0c\xa1u\xa5`\x0f\xe7B\xf1\x90\xac\x82\n\xc7|s\xa6\x01\x14\x8d\xfd.'d&A\xba\x12\x01\xf5%B\x90Pm\xd1\x80K\x03\xc1\xe5\x04\xfa\x7f\xec}[S\xdc\xba\xb2\xff\xfb\xffS\xe8-Y\xa7 \x13H\xc8\x85\xaa\xf30;+9\x9b\xda9\xc0\x02\x92U\xe7\x89\xd2\xd8\x1a\xc6\xc1c\x0f\xb6\x0c\x0c\xbb\xf6w\xff\x97\xae\x96m]\xda\xc6\x03CU\xfc\x94\x98q\xab\xfb'\xa9\xbb%u\xb76\x13\xbc-\x1e\xddSa\xdb\x89\x9eF:\xc0r\xa6\xc5\xf4pKj\x12\x03\xe2\x04\xb3\xa6h\xfb\xb0\x1a\xc3\xb6\"\xf4\xfbbN\xb8\xe4#\x0f\x92a\x92?\xd2\x9e \xfb\x10\xb2\x8b\xdf\xb9\x94e\x8c\xb5\x96\x95\xe8x+.{T\xdb\x86\x17`m\x99F\xf05\x80*\x04\x8a:\xeaq9Y\xf8@\x0bu\xb6.\x82\xc6\xa9\xd3\xeda\x03\xb5=H=\xda0\xb5 \xf6\xc0)\xeau\xef\xd3\x16a\xd6e\x1c\x01.\x1b\xb3|\xd5\x8ah\x1f\x8ae\xc8p\x01\xa1\x1b\x8e \xc4hy\xc9=F\xfa\x91\x06\xcep\xe9\x1fi\xb8\x00\xc3\xcb\x06\x05\x84_\xb9\xe9\xe8\xbe!L\xef]=\xd5\x15a\xb2A\xcf\x1da\xfa\x17\xba\xa6#\xe2%\x1d-\xc4\x8c\"\x8f\xde\xeb\xc2\"\xfb]8b`\xd2\x12\x17\xe5\"\x10\xbe\x8ePI1\xad\x82S\xbf\x1f\xfeG:\xeb%\x99\x0b\xb7\x89+>\xae\"\xd50TL\x83:@'d\xfc\xaax\x04\x91\x88\x96g]\xcc\xcf\xcbVUX=\xf7\xc2\x18\x9e5y\xf6\xf5\xcb\xc9\xd9\x9f\x97G\xc7\xa7?..\xcf/\xa6\x17?\xce{e\xb2\xb9h\x9c\x9e\x9d\x9c\x9e\x9c?\x82\x80x\x17\xfc\\g\xe2=V\x90\xfe\x9a<\x08\x1e\xa4\x07\x02$\x8c\x04,@j\x08\x8f\xfb\xc2i\x12O\xaaL\xac\x17\xc5\xb8ec\x07\xf0q\xa0+\xed\x18\xab\xbf\xb6S\xc5\x8c\x19\x93!\\\xcc\x12Z\xe0b]k0^\xb7P\xaf\xf9\xc4T\x18\xce\xa3xg\xe7P\xbc\xb3\xf3\x97\x88 \xdd\xf0\xc0V\x05\xb9M\xf2\xaaL\xd7\x9d\xa9n\xa4Wyy\x95\xca\xe6\xa2\xc0\xd1\xb5\xd8\x1b\x13\x9e\x93^\x01\x12e\x93\xa0\xab2\x90\xf62\x1b\xe9\xf8\xd9L\xb6h\x91\x90[q\xc7J^\xd1\x90\xda\xc93\x10w\x82\xd2\x8b\xf5\n\x9f\xd9\xd6\xffS\xday\xaaj\xac\n8a\x05\x1b\xc4#\xbe\x98\xd4\xd7\xf9\xb7\xb6\xa1\x9e\xc1\x82\x9es\x9aJ.5\x04\xebm\x8czA\x08 Vg:Il\xe4TN\xb2+um\xcf\xce\x1c'iU\x00\x96\x90\x88\x99\xe1\x15\xc9bPG\xf6\xe9\xf5>F\xf7\xfc\xc7\xf7AV\xaa\xfb\xf5\xe9\xf4\x1c\x96\\\xdf\xfc\xec\xfc_G\xa7\x03>\xfb6=\xfa\x1e\xfc\xcc\xb0\xc9C\xe5\x1cf\x8d\x1d\xadA:\xc5\xf9q\xc3\x02\xa3*+I\xd8W\xe3\xe7\x1a}\xf2\xe9\xbb]\xda\x06\x8f\xbdk\x980b\xcc&fA\xeb\xfb\xab\x864\xc9\x86C\xbbI\xf6\xcehR\x8b4\x89\xf2\xacLb\xb5\xfb\xc0\x1b\xbfN\xf8\xf9E,\xee\xf2Z&e\xc9&\xa7\xb4Gy\x81b\x92\xe25\x89\x81)\x8a\x0e&\xd9\xe0k3\xc9\xde9q\xa9\x8f\x16\xad\x9c3\x9d\xe1\nEW\x0fT\xe9\x89E\xd5\x89\xd0O$\x8b\xf0\xaa\xacR\xcd\x92\xd4[|\x15\x0b\xd9GD\xb5\xe7\x01\xc8N\x86\xf1(xx]\xfe\x81\xea\xfb\xde\xab\x94r%-!\xcb\xae\x1a\xea:\xd0K\xec\xa7b!\xa3>\xa8\xcb\x15'0\xd7\xe1e\xd4Xjs\xa9\x00\xac\xcf2\x00v\xb6\xb4\x96c\xb2\x96\\\x92\x87G\x0er\xe1\xde\x9e\xea#\x91|\xce=W\xde\xe5\x98R\x1c-Dk:\xa3\x95\xcdM\x82#w\x81\xed\xe6\x8c\x91\xe3\x98\xaf\xdd\x83\x89\xdb\x0d\x88%G\x12\xb9<\x93\x1b\x1a\xf2\xb5\xccv\xb2\x8b\xac7\xaeF TjR\x0bD(\x19\x91HN\x82\x8d!\xb0\xe1\x88$\xd0\x1e\xde\x06'\x8an\xdf\x19d$;4\x188Tw\xc36G\x0c\xb5\xb9\x1c\x1e*\xa4)A \x81\x05&n\x03,c\x84\xab\xd6\xc3\x06\x08\x8f\xd8,\x08\xeeMn\x03>\x06\xab\n \xb9\xeb\xc7_m\x04\xa1g\x88\x882Z\x0e\x85B\x8d1S^`6\x04@*\x90\x95@@K\x81~'C\xc8\xe7 \xa4\x0b\xae\x0e~'C\x0c\xc2j\x0c\xeb\x82\xd0\xefd\x08\xb8\xe4#\x0f\x92a\x92?2\xa6\x14\xf5I\x86\x90zy\xccT\x08\x0b\xc9\xf1\x12!\xa4\x7f\xfd\x94i\x10MyF\xf0,\x80\xaa\x03\x8a7\xea\xf0\x18X\x9d\xf8\xe7A\xbf\x04\x88Vg\x87M\xd2\xb6`\xf4hC\xd4$\x07Fh#\xf1\xeb\x9bGkSi\x0f\x03Q|)K \x0b\xbf\x96u\x90\x97\xd2\x08\x80\x85\xec9\x10\xa6\xa1\xf2\x8fb\xc9\xa1rw2*\xc6\\#Y\x89;\xd7J R\\\xe1\xa2$v\xaf\x99\x82\"\x83,\x1d\x02Z;\xd43\xf1\x0f\xc1\xc7\x0f\x82O5\xd4\xb3cP\x8fD@0\xa1\xbe\xb6\x10\x0dK\x08D[\x8b`\xc8F\x06 \xd2^\xa9]\x08\x96\x10\xf5B\xb1\x1cfA\xbb\xa4,V\xb4'\x85~}\x102\x1e\xa8\x1f\xe4\x8f\xc31hJ\x82\xf4z\xa7[\xa2\xdeI\x87\xe8)\x11\x19a\xb1\x08\x18\xaaC\x93\x10\xc5\xb1\xb0;\x07Q.\xe76\x91\x82(H{\x92\x0de\xdb\xf2\x18\xb8\x9b9\xc8s\n\xbb\xfd\xdcj\x84\xffJ\xc4\xd1E\xf9j]\xbbi\xe2\x0f\xcd\xfc$\xc4\xb3\x19]M\x06\xec\xb8\xdfz\x077\xbf\x83#\x12\xd2\x9f\x9d\xadn\xac\xa6\xa1\xeaIa$w\x10ys\xe5\x9aS\xe2\xa7{7\xfbW\xe4\xed\x03~\xa0\xd5\x87\x03z\x7fp\x7f\x90\xa6\xb7\x07\xf7\xd1\xe7\x07Z\xde\xbd\xdf\x8fo\xf6\x7f\xc5\xb7\xcb\x18?Tw\x0f\x11\x8e\xe3\xc5\xe2\xd3\xd5\xb2z\xb7\x8c\x1e\xc8;\x1be\xffn\xefH\xd2\xeb}\xdc\x08g\x88$\xbc\x02\xf9\x8c \xcc\xa7\x9eW\xe8\xcf{\x9f?~\x9a\xe1\xfd\xdd\x83\xf9\xbb\x83\xdd\xf7\x07\x9f\xf1\xee\xa7\x0f\xf8\xe3\xee\x9cDxo\xf6\xf6`o\x9f\xbc\x15%\xeb\x95^\x8b\xdcw\xe95Q\xf6\xf1\xbaw\xf3\xe0D\xf9\xe6>\xbd\xbe#\xa9\x15\xcc@\x02\xe5Xp\xd6\x15\xf6z\x03z\xf0\xe9\xed\xbb\xf9\xa7Y\xb4\xfb\xe1\xed\x87\x8f\xbb\xef\xc9\xec`\xf7\xf3\xc1\xde|w\x7fo\x7f\xef\xc3\xc7\xbdh\x9fD-@Ec\x8f\x82T\x90\xd8\xbb\xb9w\x82\xfa\xb9\xbcI\xa3\xc5\xbb\xf2\xfe.{\xff\xfe\xd7\xc1\xdb_\x0fW\xf4SQ.no\xd6\xf3\xe2WT\xb8\xc4\xe1\xd7/3\x10\xf2,]\xd7\x10\xa0\x84'\xee\x19\xc7\x078-s\x17\x7f\xf2v\x0c\xab\xbav\xaf\x95\x83\x9d)C.\xcc\xd5\xab\xc8\xdej\xec\xc9\xd1\x1c\xa5y~\xcd\xb4\xb3\x85\x8aL\xf6\x11\x1b\x92>>|\xf7\x00h\x7f\xcd\xf3\x1b\xd8\xc8k\xb0#\x14\xd9<\xc5W\xdc\xb4\xe8\xab\x05r\xf53.\xa6\xdb\x94\n\"\xd2\xba\xc9(\xa2\x92\xd4\x16H-\xe7\xcaU\x9e\x95\xd6(\x0f\xcd\x8eL\x80\xdf\"\x80\xcc\xa4\xfd\x10F\xfeiS>\n\"r/\x18\xf2.\xe9\x9f\x12\xa1\x16Cn\x80\xe4\x0f\xb9\x84\xce\x83SN\x82\x07\x16\x85\x80P=\xe3\xf4P\xd0\x13\x03\xd1b\x082R\x9c\xf9\xc6\x9aF\x86\xd6yUHW\xa9 \x83\x9f+\x99\xdeu&\x11Du\xb4\x9f\xf8?CFF\x88\x13\xf4WE\x8a\xf5D\x15\xfb=;\xfd\xd2\"&2K\xeb\xe6Uh\xaf\xf1\xb3\x067\xd3\x0cU\x19\xb9_\x91\x88y{\xe2n)\xd5\xb2\xf1M\x19-\xc8\x127{\xce\xe9\xfb\xb9\xfd>N\xbf\xdb\xff\x1e}\x1e\xe5\xb1E\xf9\x8a\x0f\\%\xcf\xd5@I2\xfan\xbf\x83\x8f\xa3\\\xb8\x87\x87\x98P\x9c\xa4[R\x93\x83}~Y\x15\xce[m\x82\xb6\x91\xa7\xb2\x0e\xfe\xda\xba\x05\xb1\xc2\x05^\x12J\n\x83\xe7]a\xbf\xb5'\xe0\x1a\x80\xad\xe9\xd0\xd7_\x1d\xe6\xa5\xfa\x9c\xa7!^h\x92\x1d\xa2\x15\xa6fT&\xd3\x0bIA\xe2CD\x8b\xca\x9cJV\x88\x15Z\xad\xcd\xf5\x06R\x83\xbc\x99\xd183\x96Q\xc0\xae\x1c\xbc\xf0\x1aw\xb9\xc5\x10\xb8aZ\xd3\n\xc1\x1c\xa7%\x18\x83\xda\xfb\x07B\xd0\x7f\xb90t\x91\xa0\xe1lP\xb3\x8c\xee\xb1\x17\x04\x8fX\x06\xd8\x9c\xff\x11{\xab\xe11\x03;l\x98\x97mY\xfa\x0eq\x1c\x87\xc8\xdeu\x86\\nR\x07\x16\xe9\xe3\xf6E\xa6\x8f{\x8d,\x8dl36-\x0f\x19\x08\xcd@\xbf\xba\xf6\xa0\x1b\xd4\x9e\x17\x81\x96k\x0cD`\x90Cm\xb8\xce\x0db\x0e7z\x93\xf2S|\xd5p]\xfe\x92\xed\xbc\x9a0\x1dE2\x9cEd\xb2$\x14\xc7\x98\xe2\xc9\xed\xde\x84O\xf8\xc9\xbf\x95~\xfb\xcfD\x0c\xe1\xc9\xbf\xa5\xd1c`\xfeg\"G\xff+A\xfa\x8ah7\xbc\xac\x96K\\\xac\x0fU\xcd\xbb\x12\x95\x04\x17\xd1B\xde \xaaf\x8d\x12\xdf\x05\xfd\x85\x11&\xd720B\x81Y\x8c\x0c\xdca\xf2zJ`/ \x9d'\xcb$\xc5E\xba\xde15\x82\xe4X\xd3k\x9bFT\xc3\xe0da\x04S\xc6 \xd4\x04\x0d\x87e\x87\xd9/e\x9fvT98\x84\xeb\xd6!\xce\xcc\x18\x8eL\xdd\xe2\x7f\xa1\xa3\xb9\xb0\xb2M\xc3Z\xb3\x89\xd3\xd4\xa8Y(\x93\x96d7\x16\xa6\x9c\xb4*2~\xd7\x8c\x95v\xddC\x0d\xea \xadq0:\x05gq\xb3^Q\xabZ#\x9f\xe8\xf6\x06[\xe3\x81k\x0d1d\xe9\x82\xe8\xd1lH\xcb\x17\xa7]?\x94\xb3\xc5\xfc\x0b\xc6\x9b\x01\x87\xe4\xac\xc5Q\xcd\x84\x875\x89E\xcb\x87\xadi\xd3VA\xc7\x81IxkV/\xba\x18>\xba\xc2n\xa4\xae1\xe3\xe5uK\xb9d\xd0f\xeeub\x8b\xeaq\x17\xd8}\xd4\x99\xb5C4\x14\xa2\x8a\x82\x94Q\xf0@\x18\xc1\xb6\xe7\x801B\xe1\xa4d\x84~\x88\x02\xb7G\x7f\"]\x9fD\xe8\x0f\x84\x8e\x96\xab\x94_\xe7X\xa22\xbe~3\x0d\xa4\xdc\"\xbeW[\xccq$v\xb2y\xe5Wq\x85\x9d\xf8\x92\x88\xb9\xa6\xfc\xf9\xf6\x92\xa0\xf9|\xc9\xcbe\xeej\xac\x04g\xa8?-\x98\xb5\x82)\xbb\x89\xe5:\xce\xc4\x1f\xfd\xd2\xf8R,\x15x\x05\x1a\xa2\xfa\x02\xa7i~'\x95\xb3\xcc\xe8\xf7\x91\xeb\xecZ\xd4O~\x975\xb6s\xdb\x8fo\xeb[=\xc1\xf2@\xc19#\x9e\xf0\xcc\x11\x0f\xdeT\xe9Zl\xbf\xf5\xf5\xb5*\xf5\xe0\xaf\xccW\xe4i\xb0$\x1fd\x04\x89\x07sz\xf5\x8cT\xcc\x18e\xa8#Y\xa0\x19\x18\xef(\x8bA\x10@Y\xda^\xe0\xc1\xca\xeb\xec\xa2\xd3\xe9\xd9\xc5\xff]^\xfc\xdf\xe9\xd7^\x15g\x1a\x1f\x9e\x9c\x1d\xfd\xcf\xd1\xf1\xf4\xe2\xe4\xac\xdfw\xe7_\xcf~\x1e}\xf9\xda\xf3\xab\xa3\xe3\x9f_\xcf{\xb7\xf5\xe5\xc7\xf9\xc5\xc9\x9fG\xd3\xe3~\x9f\x9d\xfc}\xdc\x97\xbf\xe9\xb7oG\xdf\x8f\xa6\x17_\xfb}v\xf2\xbf\xc7G\xff\xf8\x11.N\xd4\xf8\xe8\xf4\xec\xe4\xe7\xd7\xe3\xe9\xf1\x97\x9e\x8d}99\xbe8;\xf9\xfe\xbd\xafl?\xa7\xdf\x8f\xfe\x04t\xb4\xaei4hx\x85\x9d\x95\xe6\xe3\x1a\xc5\x03\x9bG\"\x08P\x9e\xc7\xf6\xa8M\xe4\x98\x14\x87\xf6\xd7\xb2\x15\\\xf2Kj\x8b\xe4*\xc90\x85\x94h\xb5\xce\xa1C\xdbK\xe5\xa1\x97(&3\xb6$(n\x93\x88\xad+\xe7U\x16\xd1\xce\xd6p\xb855\xf7\x0em/\xc5j\x92G/&\x11J\xb2[R\xf6\x97G\xcf\xd3C\xeb[\xd55\x19M\xe8Z\x98o-cT\x954\x8f\x13\x9cIA\xe5~\x1e\x07\xb8\xaf\xa0|\xde\x1fv\xde\xb4\xcb\"\xaepA\xd7\x92'n\xb4\x95\x95b\xd6\xb7g\x93Zo\x1cZ\xdf\ntE\x83b\xf9\x97!<\x9f'i\x82)A\xf8\xaa \xdc\x0d\xe9\xd9\xa8\xd4:\x87\x96w\xa2A\xee\xf5\xe0T\xc4A\xc8\xab\x94\xd7\xb5\xdfT\xe4\xa9\x10~\x99%\xb3\xaaD3\x9c]+\xab\xd8\x93\x95Z\x97\x1d\xda_3\x86T\x01 \xd5\x0ff7\x14dU\x90\x92\xbbb\xac\x0b\xea\xe2\x93rG\xb5Y\x00\x1dG\x03&u\xad7\x0f\xed\xaf\x9b\xe3\xf3n\x91D\x0b\x03'\xedC\xaaY\xafk3'\x04\x91,\xa7\x81Z\xec]\x86\xb4B>\xb4\xbe\xb5\xb1\xc3K\x9e\xf2!,\xcaQ\x89\xf9\x01\xa9\x9c\x85\xf4\x85\xeb!\xcf\"\x1cOT?\x90\xc8\xa2\xfa\x91\xbe\x9a>\xd5\xd0\xdd\xff\xaaDer\x95aq\xd3z\xa9Y\xf5\xd0\x83\xf9}Stj\xccr\xe5\x8d\xb2)8\xe1\xd5\xe3\x05\x8a\xcc/\xf4\x92i\x17\xf5\x0f\x16\x84\xeac\x07/\xf8\xb2\\^s^O\x04D\xf3\x15J\xc9-I\xe5\x92\"\x1c\xe3\xaf6N\x84G\xfb\xa6M\x9a\xefe2\x9c\xf9F\x9e\\\xe8\xfb\x94+\xd7\x14\xfc\xc6\xfa\xb5\xfbnu\xe1\n\xf3\xd5\x94\xe5B\x17\xc1As\xad\xea\x1d\xa7U\xc6+\xed1\x94\x15\xdfiRRQ\x1e\xce<\x18uE\xd72N.q\xe4\xbf\xe2d\xc4EX\xc0\xa3\x87\x8f\x83\xa9\x06I\x95\x94c\x82s\xc4pE\x17y\x91<\x08\xfdY\x90\x88$\xb7\xbe\x81P\x97Q\xe7\xfd\xd2\xbd\x93BoH8\x88\xf0P\xa7K>\xee.\x83\x8b\xc3\xb1q\xa8'j\xd3T\xb1\xde\x17\xd7 \xb4\x04\xf2\x8eMc\xef\xe5\x9c\xe2,\xc6El\x9a\x17i\xefJ\xbe{\xb9\xc4\xc5\xb5%,\xae~\xd4\xaf}\xd3\x81uXY\xadVy\xd1(P\xcb9\x7f#\xc3>0\xa5E2\xab(AK\xbc\xe6G&\x1e\x823\xc2\xb4{vEb4\x13\x1b\xee\xd2&\xd4E\x08\xf3,b~\x0b\xd3s%\xb1\xa4\xcb\x88Gn\x12]r\x9d{Y\xe4iZ\xadB\xdd\x1a\xd2\xecP\x0b\x00\xef\xff\xbf\xa5y\xc0i\xaaU\x80\x9a\x11\xf5FsBK{\x8cD\xf3Q\xe7nJ\xa96\x08\xbd*\x95j\x9d'$\x8d\x9d\xb9\x00HE\xeb\xf0\x83 \x92\xe1Y*\xd6\xfcL%+[\xf5\xdf|\xafZ0(\xa8\xfa\xc6H\xa6o@S2\xda;\x0d\x06\xdb9o\x95\xdf\x92Ax\x99\xef<7\xf6\xf4\x85'\x8d\xa2*\xd8\xc39P<\"\xab\xa0\xc21o\xc0j\x10Ec\xdf\x12\x0c\xe9I\x90\xadD@{\x89\x10$T[<\xc0\xa1\x81\xe0z\x02m&x[<\xba\xa7\xc2\xb6\x13=\x8dt\x80\xe5L\x8b\xe9\xe1\x96\xd4$\x06\xc4 fM\xd1\xf6a5\x86mE\xe8\xf75\xc1p\xc9G\x1e$\xc3$\x7f\xa4=A\xf6!d\x17\xbfsE\xd4\x18k-+\xd1\xf1V\\\xf6\xa8\xb6\x0d/\xc0\xda2\x8d\xe0k\x00U\x08\x14u\xd4\xe3\xaa\xc4\xf0\x81\x16\xeal]\x04\x8dS\xa7\xdb\xc3\x06j{\x90z\xb4aj\x13\xec\x81S\xd4\xeb\x16\xba-\xc2\xac\xcb8\x02\\}h\xf9\xaa\x15\xd1>\x14\xcb\x90\xe1\x02B7\x1c\x13\x88\xd1\xf2\x92{\x8c\xf4#\x0d\x9c\xe1\xd2?\xd2p\x01\x86\x97\x0d\n\x08\xbfr\xd3\xd1}_\xa1\xde\xbb\xda\xc0\x85\x85\x92\xb6\xe7\xc6B\xd5\xba\xf3\xcaB\xbe\xff\xda\xed[\xd1\xa3O\x1b\xe2/\x98q\x8d\xb3\x91\xac\xed\x93\x87SL\xea^\xad\xe3\xa1e\xbdU\x84~\xf2:\x042\xbb\xdcKK\x86\xe0[jcL\xd32G\xd7Y~\x97\xf1\xd2\xb5\xe8\x1b\xf3`\xbcq\x16\xcf\xb1\xe7\x0bC\xac\xce0P#W\x1dX\xa8\xd4\x82V\xfa@@\xe7i\xac\xd1k^\xf22\xa1\x0b4ORJ\n\x12\xa3\xeb[e\x9a))0\xcd\x0bw|\x98\x0c>\xf7\xc2\x05\x12P\x12R\x8a\xa2\xe1\xb2*\xc9DW{k9\xa0\xbarr\xe3\x18)\xe7\x01y\xf9|.C\xdf\x9a\x97\x89B\xc1\n\x0e\x8a\xd1vR\x80y\x11\xc0\xa1\x88\xc0\xdd\x80P\xe7h\xbc\x13\xc5\xc8\xbbCM\xd9 5\xd9\xb1\xde\xdf-p\xb9\x18_T&\x07\xa3\xcc\x85\xc8\x8c\xa2-j\xac\x05\xf3%BJ\x11\x0d\xe2\x0b\xd0\x05XD\x9cY\x8e\x9f5\xf3\xafY\xc3\xbc\xe8\xb4?\xfc\x88=:\xc2\x8c_^\xd0\xec\xcd(_.\xf3\x8c\xb7\xe7\x0f\x03\x15\x17\xb8=\x0b\x1c\xa2i\x11a\xa6\xc4\xa8\x8f;i\x0e9\x15V\x8f.]\x8d^\x0b\xb2\x7f\xd4\xa7\xac\x1c\xa7\x89\x86\xc6{\xdc\xaa\x1e\xa6z\x93\xec6\xbf\xf6\x8c\xa5$[U\xf4\xc5\xe6hAfA\xaf\x8e\x87y\x90\xcd\xe7\x98u\xbb\xa8N\xa4j\x9e\xf3\x8b\xc5\x92\xec\x1a\xcdpt-\xab\xca\x03(\xf10\x03\x9em\xc1\x07\x8d\x7f\x93J\x97\xf3\x0f\xefR\xf5\x84\x00\xe8\x1e\x88\x07>U,\xea[\x1a\xfa:\xec\xfb\x1b(W\xe2kI\xf1,M\xca\x05\x89U1\x15\x86\xf3(\xde\xd99\x14\xef\xec\xfc%bB7<\xb0UAn\x93\xbc*\xd3ug\xaa\x1byQ^^\xa5\xb2\xb9(pt-6\xb5\x84\xe7\xa4W\x80D\xd9$\xe8\xaa\x0c\xa4\xbd\xccF:~6\x93-Z$\xe4V\xdc\x8e\x92W4\xa4v\xf2\x0c\xc4\x9d\xa0\xf4b\xbd\xc2g\xb6\xf5\xff\x94v\x9e\xaa\xe2\xa8\x02NX\xa5\x05\xf1\x88/&z\x17\xa0\xbd\x0d\xf5\x0c\x16\xf4\x9c\xd3Tr\xa9!Xoc\xd4\x0bB\x00\xb1:EIb#\xa7r\x92]\xa9\x0bwv\xe68I\xab\x02\xb0\x84D\xcc\x0c\xafH\x16\x83:\xb2O\xaf\xf71\xba\xe7?\xbe\x0f\xb2R\xdd\xafO\xa7\xe7\xb0\xac\xf8\xe6g\xe7\xff::\x1d\xf0\xd9\xb7\xe9\xd1\xf7\xe0g\x86M\x1e*\xe70k\xech\x0d\xd2)\xce\x8f\x1b\x16\x18UYI\xc2\xbe\x1a?\x90\xe8\x93\x08\xdf\xed\xd26x\xec]\xc3\x84\x11c61\x0bZ\xdf<5\xa4I6\x1c\xdaM\xb2wF\x93Z\xa4I\x94ge\x12\xab\xdd\x07\xde\xf8u\xc2O#bq\x0b\xd72)K69\xa5=\xca\x0b\x14\x93\x14\xafI\x0c\xcc-t0\xc9\x06_\x9bI\xf6\xce\x89K}&h\xe5\x9c\xe9\x0cW\x0c\xb9z\xa0JO,\xaaN\x84~\"Y\x84We\x95j\x96\xa4\xde\xe2\xabX\xc8>\"\xaa=\x0f@Z1\x8cG\xc1\xc3\xeb\xf2\x0f}\xb2/\x8e\x8c\x18W\x122q]\xb0V\xd7\x81^b?\x15\x0b\x19\xf5A]g8\x81\xb9\x0e/\xa38R\x9bK\x05`}\x96\x01\xb0\xb3\xa5\xb5\x8e\x92\xb5V\x92\xba\xac\xcfN(\xdc\xdbS}$\x92\xcf\xb9\xe7\xca\xbb\x1cS\x8a\xa3\x85hM\xa7\xa2\xb2\xb9Ip\xe4\xae\x8c\xdd\x9c1r\x1c\xf3\xb5{0\xe3\xba\x01\xb1\xe4H\"\x97grCC\xbe\x96iJv\x91\xf5\xc6\xd5(\x11FMj\x81\xd0\"#\x84\xc8I\xb01\x046\x1cJ\x04\xda\xc3\xdb\xe0D\xd1\xed;\xa3\x83d\x87\x06#~\xean\xd8\xe6P\x9f6\x97\xc3c|4%\x08$\xb0\x88\xc2m\x80e\x8c8\xd3z\xd8\x00\xe1\x11\x9b\x05\xc1\xbd\xc9m\xc0\xc7`U\x01$w\xfd\xf8\xab\x8d \xf4\x0c\xa1LF\xcb\xa1\x18\xa61f\xca\x0bLc\x00H\x05\xb2\x12\x08h)\xd0\xef,\x06\xf9<\x81t\xc1\xd5\xc1\xef,\x86AX\x8da]\x10\xfa\x9d\xc5\x00\x97|\xe4A2L\xf2G\x06\x83\xa2>Y\x0cR/\x8f\x99\xc3`!9^\x06\x83\xf4\xaf\x9f2\x7f\xa1)\xcf\x08\x9e\x05Pu@\xf1F\x1d\x1e\x03\xab\x13\xff<\xe8\x97\xb9\xd0\xea\xec\xb0I\xda\x16\x8c\x1em\x88\x9a\xe4\xc0\x08m$\xf0|\xf3hm*_a \x8a/e d\xe1\xd7\xb2\x0e\xf2R\x1a\x01\xb0\x90=\x07\xc24T\xfeQ,9T\xeeN*\xc4\x98k$+q\xe7Z D\x8a+\\\x94\xc4\xee5SPd\x90\xa5C@k\x87zf\xec!\xf8\xf8A\xf0\xa9\x86zv\x0c\xea\x91\xc1\x07&\xd4\xd7\x16\xa2a\x99|hk\x11\x0c\xd9\xc8 A\xda+'\x0b\xc12\x99^(\x96\xc3,h\x97\x94\xc5\x8a\xf6\xa4\xd0\xaf\x0fB\xc6\x03\xf5\x83\xfcq8\x06MI\x90^\xef\xe0\x8f\xbbs\x12\xe1\xbd\xd9\xdb\x83\xbd}\xf2VTmW\x1a\"r_''\x8d\xbal\xd0\xc7\xeb\xde\xcd\xc3\x15y\xfb\x80\x1fh\xf5\xe1\x80\xde\x1f\xdc\x1f\xa4\xe9\xed\xc1}\xf4\xf9\x81\x967\xf7\xe9\xf5\x1dIm\x1c\x87R\x11\xc7\x82\xb3.2\xd7\x1b\xd0\x83Oo\xdf\xcd?\xcd\xa2\xdd\x0fo?|\xdc}Of\x07\xbb\x9f\x0f\xf6\xe6\xbb\xfb{\xfb{\x1f>\xeeE\xfb$j\x01*\x1a{\x14\xa4\x82\xc4\xde\xcd\xbd\x13\xd4\xcf\xe5M\x1a-\xde\x95\xf7w\xd9\xfb\xf7\xbf\x0e\xde\xfez\xb8\xa2\x9f\x8arq{\xb3\x9e\x17\xbf\xa2\xc2%\x0e\xbf\x81\x98\x81\x90g\xe9\xba\x86\x00%<\x05\xce\xd8\x88\xc7i\x99\xbb\xf8\x93\x17DX\x15_\xf0\x9ci\xa4>m\x9d*i\xf8\x95\xd2l\xe0\xec#\xb1w\xb3\xefD\xf9\xee\xfd~|\xb3\xff+\xbe]\xc6\xf8\xa1\xba{\x88p\x1c/\x16\x9f\xae\x96\xd5\xbbe\xf4@\xdey\x00p\xaf\xbe\xc7\x05\xc0\\5\x8b\xac\xb1\xc6^ \xcd\xd1<\xc9\xb8B\x0c\x8cLn72\xe9\xcb\x8a\xfe\x95I\xc6\x96\x0fdv\x92\xd8A\xf5\x89\xe9\xbbq@;\x98\x9e\xdf\xc0\xb0h\xb0#\x86\xc3<\xc5W\\&}\x89A\xae~\x86h\xe0\x1aMi\xdfd\xc8SI\x8cT{\xb5\xf8,WyVz\xa1\x91\xc6r{\xc01\xcd{\x08\x1e\xef\x98Sk\xc5\x9e\xc0\x90{\xc1\x87w\xdb\xe1)\x81i1\xe4\x06F\xfe\x90K\xe8<\xdc\xe5$x\xf0S\x08\x88\xbaC\x1c\xae\x0fzb Z\x0cAF\x883'Z\xd3\xc8\xd0:\xaf\n\xe9\x835a\xf0s%\x8bj\x94g\x12BT\x87$\x8a\xff3hd\x18;A\x7fU\xa4XO\xd47\xe8\xec\xf4K\x8b\x9c\xc8\x7f\xad\x19P\x01\xc8\xc6\xcf\x1a\xfcL3Te\xe4~E\"\xe6H\x8a\xab\xabT\xd3\xc67e\xb4 K\xdc\xec;\xa7[\xe9v)9\xfd\xee\x08\xf0\xd8\x88(\x8f-\x1aW|\xe0\xaa\xa8\xae\x86J\x92\xd1w\xfb\x1d|\x1c\xd5\xc8=<\xc4\x84\xe2$\xdd\x92\xca!\xec\xf3\xcb\xaap^\x9a\x13\xb4\xb7<\xe1v\xf0\xd7\xd6\x8d\x92\x15.\xf0\x92PR\x18<\xef\x8a\x1cc\xede\xb9\x06`kB\xf4]\x0b\x0c[\x01\xf8\x1c\xd3!\x1e~\x92\x1d\xa2\x15\xa6f\xec(\xd3\x0cIA\xe2CD\x8b\xca\x9cJV\x88\x15Z\x86\x9b\x03\x04l\xb0cd\xb8@\xed\xd9\xebp\x87F\x93\xb2^\xa1\x00\x85\xec\xbf\xa4\x19\xba\x90\xd1#\xa3A\xcd2J\xc6^\xb4\xe8\x10\xc1\xbb~\x95\xcb\xe3jc\"\xdd\xe4\x9e\xa8\xf4\xf1\xcf\x0dO|\xab\x11i\xb9\xd8@D\x06:\xe6\xb5\x0b\xde\xc6\xe4\x19\x11h\xf9\xd6@\x04\x06y\xe4\x86\xef\xdd \xe6\xf0\xc37)?\xc5W\x0d\xcf\xe7/\xd9\xce\xab S\xcd$\xc3YD&KBq\x8c)\x9e\xdc\xeeM\xf8<\x9f\xfc[\xa9\xf5\xffL\xe4\x00\x7f%\xa8\\\x11\xed\xb0\x97\xd5r\x89\x8b\xf5\xa1\xbe>\xa4$\xb8\x88\x16\xf2nR\xf9\x99\x92\xd3\x85\xf1E}\x82\xcct\xef\x0e3*\xcah\xec\xa8\x8al\x015\xad\x89\x8d\xa0\xa3\x19C\x9a\x1e\x07a\xb7c\xd9\x85*4\xac\xbb\xfe\x00\xee\xf2\xd9|\xbdF\xb3\x107\x0f\x9d'\xcb$\xc5E\xba\xde\xa9y\xa0\x8dr\xecV\xa7\x84\xd7ej9\x15-I\xc6p\"\x1aX\x06\xbc\xc0u^\xbd*H\xdd>[@\x15\xfc\xa0\x02%\x99y\xb1\xcd\x7f\xa1\xa3\xb9pDp\xdb\x98\xd7\xe3F\xde\xfdh\x1e\xde\xf0\x9bv\x8c\xe2~\x05\xa1U\x91\xf1Ksl\xa4M\xb7\xa6\xa6\x8b\xd3\xb4>\x9c\x91\xf9a\xa2'\xfb\x92\xaf;\xe85\xeb\x0c\xd9\xda\xa4~\xff\x87\xb5\xb5v\xff\xa8v\x9d\xeda\x0d\xbcv\x9e\xd55\x87JB\x9c\xc5\x93z@\\&M\xfcZ\x93\xcb\xd6\xa0\xfe\xc9\x14\xcdpl\x1e\x11\xa9\x1f\xa1d~h2v\xd1\x1c\xa3\\\xa9\xf2\x81\x89\xb3\x18e\xb9\x15\xfd7-\x02\xf2\x0e#z\x97\xb3\xe1\xcc\xef1\xca\xe7M]b\x8a\xc4\x0f\xef$Y\xfe?s\xc2\xacQ\x9cg\xaf\xa8D|.4:\x9fH\x0c<\xb9\xda0\x9a\x9fvP\xddA\xb3\x8a\xa2,\xa7\x16dq\xe3z\x84.\x19qec=\x94\x99X\xba\xb7\xb0\\\x1a\xf1_\xc69)\x19\x9fKL\xa3\xe6\xf5`\xc6\xf7f\x8f\xfcc\xad\xb6Yv\x0c/N\xdc-%]\x13q\x11\x14\xd5u\xae\x0c\x0e\xcf m\xb9\x8aR\"\xfdR\x11ap\x15\"\xad\xd8\xbd\xb7\xdb\x1ch\xda4\xe8\x12iG\xb1\xb6%joD\xb9\x0b\x86\x0d{\xb5\xff\xf6\xed+\xf7\xa6\x91\x91bmu6F\xda.r\xec\xb7\x87\xb7\xf9\xb4\xcb\xcd\xf0\xd1\xf5a#u\xdf\x16Z\xe4i\\\"ul*&\xff\xeb\xc4\x16\xc4\xe2>\xca}\xfc\xb9\xaao+\xd4\x13\xc0\x15\xda!\n\x9d\xda\"\xd8>\x0f0$&\x9c\x83\x8b\xd0\x0fQ\xcf\xf5\xe8O\xa4\xcbq\x88\xe9\x8e\xd0\xd1r\x95\xf2k\x07KT\xc6\xd7o\xa6\x81\x0cSi\xb3\xe68\x12{\xa2\xbc\xd0\xa9PS\xe2K\"\xe6\x9a\xf2\xec\xda\xcea\xf3\xf9\x92\x97\xcb\xdc\xd5X N\xc8~Z0k\x0dSv\xf3\xa8uX\x85?\xd8\xa3\xf1\xa5p%y\xc1\x15\xa2\xfa\x02\xa7i~'\xcd\x98L`\xf7\x91\x8b\xdc\xa7K\xfcR{O\x90\xa1o\x0fU=\xc1j8\xc19#\x9e\xf0\xcc\x11\x0f\xdeT\xa5Vl\xbf\x9d\xf4\xb5\xaal\xe0/DW\xe4i\xb0\x02\x1dd\x04\x89\x07sz\xf5\x8cT\xcc\x18U\x97#Y\x8f\x18\x18\xde'k\x1f\x10@\x15\xd6^\xe0\xc1\xaa\xc94\xee\xb2\xefS`\xa5\xf1\xe1\xc9\xd9\xd1\xff\x1c\x1dO/N\xce\xfa}w\xfe\xf5\xec\xe7\xd1\x97\xaf=\xbf::\xfe\xf9\xf5\xbcw[_~\x9c_\x9c\xfcy4=\xee\xf7\xd9\xc9\xdf\xc7}\xf9\x9b~\xfbv\xf4\xfdhz\xf1\xb5\xdfg'\xff{|\xf4\x8f\x1f\xe1Z<\x8d\x8fN\xcfN~~=\x9e\x1e\x7f\xe9\xd9\xd8\x97\x93\xe3\x8b\xb3\x93\xef\xdf\xfb\xca\xf6s\xfa\xfd\xe8O@G\xeb\x12>\x83\x86W\xd8Wi>\xaeQ<\xb0y$\xc2\xdb\xe4\xc1^\x8fR<\x8eIqh\x7f-[\xc1%\xbfL\xb5H\xae\x92\x0cSHER\xeb\x1c:\xb4\xbdT\xcez\x89b2\xa3\xa8$\xc5m\x12%\xd9\x15\x9aWY\xc4\xcdX\xcf\xd6\xd4\xdc;\xb4\xbd\x14\xab%\x1ea\x97D(\xc9nI\xd9_\x1e=O\x0f\xadoU\xd7d4\xa1ka\xbe\xb5\x8cQU\xd2X\xff\xa8\x8f\x1d\xbc\xe0\xabry\x1dw=\x11\x10\xcdW(%\xb7$\x95K\x8apH\xbbZ\xd9\x0b\x8f\xf6M\x9b4\xdf\xf7f8\xf3\xf8o\xb9\xd0\xf7)W\xae)\xf8\xcd\xeak\xf7\x1d\xe0\xc2\x15\xe6\xab)\xcb\xfd%\x82\x83\xe6Z\xd5;N\xabLl@\xd2\x85\xe6;M\xf8\x8em\xde\xb8\x14\xd4\x99\x8b\xc48\xb9\xc4\x91\xffF\x8f\x11\x17a\x01\x8f\x1e>\x0e\xa6\x1a$UA\x8d \xce\x11\xc3\x15]\xe4E\xf2 \xf4gA\"\x92\xdc\xfa\x06B]5\x9c\xf7K\xf7\n\x06\xbd!\xe1 \xc2cf.\xf9\xb8\xbb\x0c.\x0e\xc7\xc6\xa1\x9e\xa8MS\xc5z_T\xcfo \xe4\x1d\x9b\xc6\xde\xcb9\xc5Y\x8c\x8b\xd84/\xd2\xde\x95|\xfbr\x89\x8bkK|U\xfd\xa8_\xfb\xa6\x03\xeb\xb0\xb2Z\xad\xf2\xa2Q\x8f\x95s\xfeF\xc6=`J\x8bdVQ\x82\x96x\xcd\xb7\xef=\x04g\x84i\xf7\xec\x8a\xc4h\xb6\xe6(H\x9bP\xd7\xdc\xcb\xb3\x88\xf9-L\xcf\x95\xc4\x92\x1d\"\x1e\xb9It\xc9u\xeee\x91\xa7i\xb5\nukH\xb3C-\x00\xbc\xff\xff\x96\xe6\x01\xa7\xa9V\x01jF\xd4;\xcd -\xb5*\xf0\x10Sg}J\xa96\x08\xbd*\x95j\x9d'$\x8d\x9d\x01\xfbH\x85\xab\xe0\xb4\xcc\x11\xc9\xf0,\x15k~\xa6\x92\x95\xad\xfao\xbeU-\x18\x14T}c\xa4\xde+W2\xda;\x0d\x06\xdb9o\x95_\nAxU\xeb<\xa7Fik\xeeI\xa3(OS\xc2\x1d\xa7\xfaH\xc3\xc5#\x13\x80\x8f\xb6<#\xfa \xc4\xc3\xa8\xda\x80\xf5\x84\xfe\xc2$i\x10\xaaw\xf7B\x15\xfa\xdc;6\xb51\xb2\xfe \xb8\x91\x16\xdeB{\xca\xcd\xe7\x06\x88\xe6y\x16\x1d\\XI#\x1e\xca\xd6\xdd\xb0\x14\xa1\x84\xdbZ\"O\xca\xa2&&\xfe\xe1\xcfV\xdc\x90@^\xe5fa\x10\xf9\xea\x1b\x85\xf6\xb5Ax\xf8\xd3XA0\xf4\x950\x98\xac:\x96l#tq_\xd9\x1e\x95vj\x19\x00n\x85\x1a\xae\xe3\x00\xe3\xbdK-\xa0Z\x1dt\xba5\x89,\x073\x1bV\xb4\xc1\xba\x0c\xa0>\xdf\xc8\xb4\x0e\x14_\xf0\x1e\x1b\xa1\xf6)\x15LoCK-<;(\x10\xf5\x0e\xa5\x05\x83\xc4\xf8\xdf\x08Jb\xa3\xd8\xb4XmZ\x04\xfd\xdaC\xcej&\xe1h=\x83\x81\x80\x954\x18\x7f`\x8c0\x14\x06\x89:\x82\xd1p\x8c\x96\xb6\xd0jU\xd4\x95\xd2\xb7\xeb\xb1\xa9\xf4\x1d\xc9\x8d\x0b\xf2\xa0Y\x08\xb7\x80P8\xd3\\<\xa0\xde\x05Nux\xb4\xc4\xb8\xed\xf6\x19~\xfab\xe1\xbaZ\xbd\xa3\xbe\x0f\xe8\xc6\x19[\xbdz \xbck\xe2!\xb5h\x0c\xc3\xb3\x9d7\x04\x057\xbf\xd4\x03\xecc\xf9\xe31\xa2#`\xf1\x11\xd0\x9dr\xf1\x8c\x1b#1 J\xa2'\x90\xb0H\x89G\xc4J\x0c\x8f\x96\x18\x1a/14bbp\xcc\xc4\xa0\xa8\x89\xc1q\x13\x03#'\x86\xc7N\x0c\x8f\x9e\x18\x18?\xf1\xd8\x08\x8a~\x1a^<\xce(\n\xc0\xb7\xf6/\x87\xc6Q3L6\x9e\xc7C\xf3\x1a>\xf3\xa0_\x9e3\xc2\xee\xdf\x93\xee\xfd\x0e\xe3/\x89p\x9a\xaeQ\x94\xe2\xb2\xf4\xd6j\x96\x1f\x8d \xee\x06\xd6\xc0j\xc1\"\x92l\x13\x92\x8aCyf\x91\x99\x0b\x10\xa5 \xc9\xea\xf50\x8f\x98\xf0\xd2\xeb\x0e\xd1\xe0\"\x18Wq\xe2E\xa7\x8f<_\xf8\xf2;F\xb3\xf5\x0e\xaaV\xb1\xfe7M\x96\xa4\xa4x\xb9*w\xf4\xf6\x99(\xf2\xe8\xbf\x19U$\x8a\xa5\xf2\xee\xbey\xee\x16\x03\xbc\xc2\x86\xae\xaf\xc5NB|\xc9\x84\x08)N\xe0\x00B\xc6 bdw\x19,!\xd2b\x8e\xc98&2a\x9f0\x0d*c\x05HF\x8b5\xbf\xb1O\xf2\x0b\x92i\xb6\x1eQ\"\x98\x16@r\xc5\xed\xd8W\x10.\x8a\x1c=A\x05\x87t@\x99\xf7gr\x04\xbe\x98.LqI\x15\xd3 \xc1\xb6\xb4\x1fyX^2bGJ\x95\x01\x13\xd6U\x89\xaa\xf9\x18u\xa9>\xbc\x0f\xd1\x05#\xc3|k\xe9\xbb\xb4\xf4\x9c@\x86\x97\xa4\x8f\n\xee\xfd\x03\xf6\x9b\xb8\x0e'8Z\xc8\x1e\xf7~\xe0\xac\xa6\xd5|62\"L\xb9%\x1f\x96\xc8\xbe\xd0D\x91\n*\xc9\xb3\x89\x90\x17\x91[\xff2 \xc6\xe1\x94\x99\xb8o\xcc\xba\x96(\xc2+\xe1eZ\xa3v\xd8\x0c\xf4\x92\xd2\xe3\xf2 \x19&\xf9#\xed \xb2\x0f!\xbb\xf8\x9d\x1b\x91\xc6XkY\x89\x8e\xb7\xe2\xb2G\xb5mx\x01\xd6\x96i\x04_\x03\xa8B\xa0\xa8\xa3\x1e7\x03\x86\x0f\xb4Pg\xeb\"h\x9c:\xdd\x1e6P\xdb\x83\xd4\xa3\x0dS\x9b`\x0f\x9c\xa2^\x97\xaem\x11f]\xc6\x11\xe0\xa6?\xcbW\xad\x88\xf6\xa1X\x86\x0c\x17\x10\xba\xe1\x98@\x8c\x96\x97\xdcc\xa4\x1fi\xe0\x0c\x97\xfe\x91\x86\x0b0\xbclP@\xf8\x95\x9b\x8e\xee\xeb\xf9\xf4\xde\xd5S\xdd\xcf'\x1b\xf4\\\xd0\xa7\x7f\xa1k:\"\xd7\xf5|F\x91G\xef\xed|\x91\xfd\x12#10\x9e6S@0\xe3\x1a\xae#\x19\xed'\x8f\xca\x98\xd4\x83\xa3\x0e\xab\x16\xa2\xbeA\xe8'/g \x93\xd4\xbd\xb4d$\xbf\xa5\xc4\xc64-st\x9d\xe5w\x19\xc2l\xfc~c\x8e\x907\\\xe39\xb6\x8ea\x88\xd5\x89\nj\xfa\xa9s\x0f\x95\xa1\xd0\xcaB\x08\xa8N\x8d5z\xcd+g&t\x81\xe6IJIAbt}\xab,<%\x05\xa6y\xe1\x0e3\x931\xec^\xb8@\x02JBJ\xdf4<_%\x99\xe8joI\x08\xads\xd6\xcd\xd3\xa8\x9c\xc7\xf5\xe5\xf3\xb9\x8c\xa0k\xde\x9b \x05+8(F\xdb\x90\x01\xa6W\x00\x87\"\x02w\x03B\x9d\x13\xf6N0$\xef\x0e5e\x83\xd4d\xc7z\x7f\xb7\xc0\xe5b|Q\xa9\xbcP\x9f\x0b\x91\x19\xb5_\xd4X\x0b\xa6]\x84\x94\"\x1a\xc4\x17\xa0\x0bd\x1dq\xcb)\xb6f\xfe5k\x98\xdf\xf6\xe3\x8fbb\x8f\x0eT\xe3\xa5\xe8\x9b\xbd\x19\xe5\xcbe\x9e\xf1\xf6\xfc\xd1\xa4\xe2B\xb1g\x81C4-\x02\xd5\x94\x18\xf5\xa9)\xcd!\x87\xcb\xea\xd1\xf5\xaf\xd1kA\xf6\x8f\xfa\xb0\x96\xe34\xd1\xd0xOm\xd5\xc3To\x92\xdd\xe6\xd7\x9e\xb1\x94d\xab\x8a\xbe\xd8T/\xc8,\xe8\xd5\xf1\x10\xa7\xaf\xfd\x1c\xb3n\x17E\x8eT\xe9t\xd6\xf1i\x92]\xa3\x19\x8e\xaee y\x00%\x1e\xad\xc0\x936\xf8\xa0\xf1\xefu\xe9R\xef\xe1\xcd\xae\x9e\x10\x00\xdd\x03\xf1\xc0\xa7\x8aE}KC_G\x8f\x7f\x03\xa5\\|-)\x9e\xa5I\xb9 \xb1\nk\x08\x85\x9bC\xf4xO\x98\xfaJnW\xf8 \x89xJ\x82\xe1U\x00\xe81\xa0\xd0i\x91\xaf\xf2\x12\x8e\x81V\xcb\x9b\x01\x82\xdf\xb4\xb9R<\xcd\xf9\xde\x1e-\xaa\x88\xc7\x15q\x1fi\x89\x8br\x11\x08_G\xa8\xa4\x98V\xc1\xa9\xdf\x0f\xff#\x9d\xf5\x92\xcc\x85\xdb\xc4\x15\x1fW\x91j\x18*\xa6A\x1d\xa0\x132~U<\x82HD\xcb\xb3.\xe6\xe7e\xab*\xac\x9e{a\x0c\xcf\x9a<\xfb\xfa\xe5\xe4\xec\xcf\xcb\xa3\xe3\xd3\x1f\x17\x97\xe7\x17\xd3\x8b\x1f\xe7\xbd2\xd9\\4N\xcfNNO\xce\x1fA@\xbc\x0b~\xae3\xf1\x1e+H\x7fM\x1e\x04\x0f\xd2\x03\x01\x12F\x02\x16 5\x84\xc7}\xe14\x89'U&\xd6\x8bb\xdc\xb2\xb1\x03\xf88\xd0\x95v\x8c\xd5_\xdb\xa9b\xc6\x8c\xc9\x10.f -p\xb1\xae5\x18\xaf[\xa8\xd7|b*\x0c\xe7Q\xbc\xb3s(\xde\xd9\xf9K\xc4\x84nx`\xab\x82\xdc&yU\xa6\xeb\xceT7\xd2\xab\xbc\xbcJesQ\xe0\xe8Z\xec\x8d \xcfI\xaf\x00\x89\xb2I\xd0U\x19H{\x99\x8dt\xfcl&[\xb4H\xc8\xad\xb8c%\xafhH\xed\xe4\x19\x88;A\xe9\xc5z\x85\xcfl\xeb\xff)\xed\x12\xc9\xe7\xdcs\xe5]\x8e)\xc5\xd1B\xb4\xa63Z\xd9\xdc$8r\x17\xd8n\xce\x189\x8e\xf9\xda=\x98\xb8\xdd\x80Xr$\x91\xcb3\xb9\xa1!_\xcbl'\xbb\xc8z\xe3j\x94@\xa5&\xb5@\x84\x92\x11\x89\xe4$\xd8\x18\x02\x1b\x8eH\x02\xed\xe1mp\xa2\xe8\xf6\x9dAF\xb2C\x83\x81Cu7ls\xc4P\x9b\xcb\xe1\xa1B\x9a\x12\x04\x12X`\xe26\xc02F\xb8j=l\x80\xf0\x88\xcd\x82\xe0\xde\xe46\xe0c\xb0\xaa\x00\x92\xbb~\xfc\xd5F\x10z\x86\x88(\xa3\xe5P(\xd4\x183\xe5\x05fC\x00\xa4\x02Y \x04\xb4\x14\xe8w2\x84|\x9e@\xba\xe0\xea\xe0w2\xc4 \xac\xc6\xb0.\x08\xfdN\x86\x80K>\xf2 \x19&\xf9#cJQ\x9fd\x08\xa9\x97\xc7L\x85\xb0\x90\x1c/\x11B\xfa\xd7O\x99\x06\xd1\x94g\x04\xcf\x02\xa8:\xa0x\xa3\x0e\x8f\x81\xd5\x89\x7f\x1e\xf4K\x80huv\xd8$m\x0bF\x8f6DMr`\x846\x12\xbf\xbey\xb46\x95\xf60\x10\xc5\x97\xb2\x04\xb2\xf0kY\x07y)\x8d\x00X\xc8\x9e\x03a\x1a*\xff(\x96\x1c*w'\xa3b\xcc5\x92\x95\xb8s\xad\x04\"\xc5\x15.Jb\xf7\x9a)(2\xc8\xd2!\xa0\xb5C=\x13\xff\x10|\xfc \xf8TC=;\x06\xf5H\x04\x04\x13\xeak\x0b\xd1\xb0\x84@\xb4\xb5\x08\x86ld\x90 \xed\x95\xda\x85` Q/\x14\xcba\x16\xb4K\xcabE{R\xe8\xd7\x07!\xe3\x81\xfaA\xfe8\x1c\x83\xa6$H\xafw\xba%\xea\x9dt\x88\x9e\x12\x91\x11\x16\x8b\x80\xa1:4 Q\x1c\x0b\xbbs\x10\xe5rn\x13)\x88\x82\xb4'\xd9P\xb6-\x8f\x81\xbb\x99\x83<\xa7\xb0\xdb\xcf\xadF\xf8\xafD\x1c]\x94\xaf\xd6\xb5\x9b&\xfe\xd0\xccOB<\x9b\xd1\xd5d\xc0\x8e\xfb\xadwp\xf3;8\"!\xfd\xd9\xd9\xea\xc6j\x1a\xaa\x9e\x14Fr\x07\x917W\xae9%~\xbaw\xb3\x7fE\xde>\xe0\x07Z}8\xa0\xf7\x07\xf7\x07iz{p\x1f}~\xa0\xe5\xdd\xfb\xfd\xf8f\xffW|\xbb\x8c\xf1Cu\xf7\x10\xe18^,>]-\xabw\xcb\xe8\x81\xbc\xb3Q\xf6\xef\xf6\x8e$\xbd\xde\xc7\x8dp\x86H\xc2+\x90\xcf\x08\xc2|\xeay\x85\xfe\xbc\xf7\xf9\xe3\xa7\x19\xde\xdf=\x98\xbf;\xd8}\x7f\xf0\x19\xef~\xfa\x80?\xee\xceI\x84\xf7fo\x0f\xf6\xf6\xc9[Q\xb2^\xe9\xb5\xc8}\x97^\x13e\x1f\xaf{7\x0fN\x94o\xee\xd3\xeb;\x92Z\xc1\x0c$P\x8e\x05g]a\xaf7\xa0\x07\x9f\xde\xbe\x9b\x7f\x9aE\xbb\x1f\xde~\xf8\xb8\xfb\x9e\xcc\x0ev?\x1f\xec\xcdw\xf7\xf7\xf6\xf7>|\xdc\x8b\xf6I\xd4\x02T4\xf6(H\x05\x89\xbd\x9b{'\xa8\x9f\xcb\x9b4Z\xbc+\xef\xef\xb2\xf7\xef\x7f\x1d\xbc\xfd\xf5pE?\x15\xe5\xe2\xf6f=/~E\x85K\x1c~\xfd2\x03!\xcf\xd2u\x0d\x01Jx\xe2\x9eq|\x80\xd32w\xf1'o\xc7\xb0\xaak\xf7Z9\xd8\x992\xe4\xc2\\\xbd\x8a\xec\xad\xc6\x9e\x1c\xcdQ\x9a\xe7\xd7L;[\xa8\xc8d\x1f\xb1!\xe9\xe3\xc3w\x0f\x80\xf6\xd7<\xbf\x81\x8d\xbc\x06;B\x91\xcdS|\xc5M\x8b\xbeZ W?\xe3b\xbaM\xa9 \"\xad\x9b\x8c\"*Im\x81\xd4r\xae\\\xe5Yi\x8d\xf2\xd0\xec\xc8\x04\xf8-\x02\xc8L\xda\x0fa\xe4\x9f6\xe5\xa3 \"\xf7\x82!\xef\x92\xfe)\x11j1\xe4\x06H\xfe\x90K\xe8<8\xe5$x`Q\x08\x08\xd53N\x0f\x05=1\x10-\x86 #\xc5\x99o\xacidh\x9dW\x85t\x95\x9a0\xf8\xb9\x92\xe9]g\x12ATG\xfb\x89\xff3dd\x848A\x7fU\xa4XOT\xb1\xdf\xb3\xd3/-b\"\xb3\xb4n^\x85\xf6\x1a?kp3\xcdP\x95\x91\xfb\x15\x89\x98\xb7'\xee\x96R-\x1b\xdf\x94\xd1\x82,q\xb3\xe7\x9c\xbe\x9f\xdb\xef\xe3\xf4\xbb\xfd\xef\xd1\xe7Q\x1e[\x94\xaf\xf8\xc0U\xf2\\\x0d\x94$\xa3\xef\xf6;\xf88\xca\x85{x\x88 \xc5I\xba%59\xd8\xe7\x97U\xe1\xbc\xd5&h\x1by*\xeb\xe0\xaf\xad[\x10+\\\xe0%\xa1\xa40x\xde\x15\xf6[{\x02\xae\x01\xd8\x9a\x0e}\xfd\xd5a^\xaa\xcfy\x1a\xe2\x85&\xd9!ZajFe2\xbd\x90\x14$>D\xb4\xa8\xcc\xa9d\x85X\xa1e,V\x80\x80\x0d^\xde\x8c\xbb\xa8a\x08\xdc0\xddd\x85`\x8e\xd3\x12\x8cA\xedc\x03!\xe8\xef\x94\x0fu\xc55\x9c\x0dj\x9614\xb6\xdb\xfd\x08g\xdb\xe6b\x8f\xd8[\xad\xe3\xa0F?\xc1\xfd\xef\x0d1\xd7p\x9a\x81\xa3i\x98\xa3mY\xfd\x0e\xf1\x1d\x87\xc8\xde\xf5\x87\\\x9eR\x07\x16\xe9\xe6\xf6E\xa6\x8f\x87\x8d,\x8dl36-'\x19\x08\xcd@\xd7\xbav\xa2\x1b\xd4\x9e\x17\x81\x96w\x0cD`\x90Omx\xcf\x0db\x0eOz\x93\xf2S|\xd5\xf0^\xfe\x92\xed\xbc\x9a0\x05J2\x9cEd\xb2$\x14\xc7\x98\xe2\xc9\xed\xde\x84O\xf8\xc9\xbf\x95\xf2\xfd\xcfD\x0e\xf4\xc9\xbfk{\xf4\x9fW\x82\xe4\x15\xd1\x1exY-\x97\xb8X\x1f\xaarw%* .\xa2\x85\xbc\x0eT\xcd\x16%\xb6\x0b\xf2\x0b#B\xaee\xf5\x84\xe2\xb2X>\xb8\xaf\xe4u\x92\xc0\x0e\x12:O\x96I\x8a\x8bt\xbdcj\x02\xc9\xb1\xa6\xd7\xb6\xd7\xa8\x86\xc1\xc9\xc2\x08\xf6\x95A\xa8 \x1a^\xd4\x0e3\xaa\xcah\xee\xa8Jp\x08\xd7\xadC<\xac1\xbc\xab\xba\xc5\xffBGsa\xfa\x9b\xd6\xbef\x13\xa7\xa9Q\xaeP\xe6+\xc9n,L9iUd\xfc\x9a\x19+\xed\xba\x87\x1a\xd4\x13Z\xe3`t\n\xce\xe2f\xa9\xa2V\xa1F>\xc1\xed\x0d\xb6\xc6\x03\xd7\x16b\xc8\xd2\x05\xd1\xa3\xd9\x90\x96\xafK\xbb\xce1g\x8b9=\x8c7\x03\x0e\xc9Y\x8b\xa3\x9a \x0fk\x12\x8b\x96c]\xd3\xa6\xadZx\xda\x8ea*?\xd2\xf4\xf8\xd5@3\xe2lJ\xb7\xa2<%\xd5\x8a\x03\x85\x1d\x133\xdc(1\xa1;\xa5 T\x03\xa0\xba\xf56R!\x89l\x92hbS4\xc3\xb1y\xb2\xa4~\x84\x92\xf9\xa1)\xf1\x85\xa3\xd39\x13\xa6\xf4w ]\xe4\x15m\xf6\xbfm\xb26\x00\x9d:\xd1\xd4\xf4\xb25\xba\xc3kn\x86T\xb5>\xed\xbc\xa1\xd7\x8d\x19l\xa2\x8e;\xb8\x9bx\xff?\xe3\x1b\x83\xb7?\x1a\xcc]\xdc\xe5\xack\xf8]M\xf9\xdc\xd3\xa3\xcd\xe9ev\xa59\x91\xb5h\xecGq\x9e\xbd\xa2\\\x05\xf0\x98\x1cY\x91\n\x95\xcc5o\xaam;^\x9c\xfak\xa6v\x15\xdaF\xd3\xbc>[\x07M\x84\xdb\xcaX}&\xa6\x9d\x1cMqNJ\x94\xe5T\x0d+TV\xd1\xa2\x8b\x97\x87\xad\x16'\xfc\xce\xabYN\x17\xc6\x10\x9eU\xb4\x0eF\xd2\x84\xe2d\xce_\xa9!\\\x9aC\xf6\x1fk\xb53\xb6c\x0c\x80\xba\xadR\xde\xadEu\xcd/\x83\xc5sB[\xab\x04\x9c\xc5\x93\xbc0| A\x83\xf5C!2\xac\x1b\x1eqc\xa9 \xbf\xd5L\x8a\xb7\xbaX\xdcQ\\\xbb\n\xef\xe4\x1f\x95oh8,\xaf\xf6\xdf\xbe}\xe5\xde\xe53\xb2\xcd\xad\x9e\xe5H\xfb{\x8e\xb3\x92\xf0\xae\xac^l1|tq\xddH\xdd`\xc6+\xeb\x96r\xa9\xa0\xcd\xdc\xeb\xc4\x16\xd0\xe3\xae\xad\xfb\xa8\xe3j\x87h(D\x15\x05)\xa3\xe0Y0\x82\xed\xcc\x01\xc3\x83\xc2\xf9\xc8\x08\xfd\x10\xb5m\x8f\xfeD\xba4\x89\xd0\x1f\x08\x1d-W)\xbf\xc9\xb1De|\xfdf\x1a\xc8\xb6E|\x9b\xb6\x98\xe3Hlb\xf3\xa2\xaf\xe2\xf6:\xf1%\x11sM\xf9\xf1\xed\xa5@\xf3\xf9\x92\x97\xcb\xdc\xd5X NN\x7fZ0k\x05Svs\xcau\x88\x89?\xf0\xa5\xf1\xa5X*\xf0\xe23D\xf5\x05N\xd3\xfcN*g\x99\xcc\xef#\xd7\xd9\xad\xa8\x9f\xfc.k\xec\xe4\xb6\x1f\xdf\xae\xb7z\x82\x95\x81\x82sF<\xe1\x99#\x1e\xbc\xa9\xaa\xb5\xd8~\xe1\xebkU\xe5\xc1_\x94\xaf\xc8\xd3`5>\xc8\x08\x12\x0f\xe6\xf4\xea\x19\xa9\x981*PG\xb2630\xd4Q\xd6\x81 \x80\x8a\xb4\xbd\xc0\x83U\xd6\xd9E\xa7\xd3\xb3\x8b\xff\xbb\xbc\xf8\xbf\xd3\xaf\xbd\x8a\xcd4><9;\xfa\x9f\xa3\xe3\xe9\xc5\xc9Y\xbf\xef\xce\xbf\x9e\xfd<\xfa\xf2\xb5\xe7WG\xc7?\xbf\x9e\xf7n\xeb\xcb\x8f\xf3\x8b\x93?\x8f\xa6\xc7\xfd>;\xf9\xfb\xb8/\x7f\xd3o\xdf\x8e\xbe\x1fM/\xbe\xf6\xfb\xec\xe4\x7f\x8f\x8f\xfe\xf1#\\\x97\xa8\xf1\xd1\xe9\xd9\xc9\xcf\xaf\xc7\xd3\xe3/=\x1b\xfbrr|qv\xf2\xfd{_\xd9~N\xbf\x1f\xfd \xe8h]\xceh\xd0\xf0\n;+\xcd\xc75\x8a\x076\x8fD\xfc\x9f<\x8a\xedQ\x96\xc81)\x0e\xed\xafe+\xb8\xe4\xf7\xd3\x16\xc9U\x92a\n\xa9\xcej\x9dC\x87\xb6\x97\xcaC/QLflIP\xdc&\x11[W\xce\xab,\xa2\x9d-\xe1pkj\xee\x1d\xda^\x8a\xd5$\x0f\\L\"\x94d\xb7\xa4\xec/\x8f\x9e\xa7\x87\xd6\xb7\xaak2\x9a\xd0\xb50\xdfZ\xc6\xa8*i\x1e'8\x93\x82\xca\xfd<\x0ep_A\xf9\xbc?\xec\xbciWD\\\xe1\x82\xae%O\xdch++\xc5\xaco\xcf&\xb5\xde8\xb4\xbe\x15\xe8\x8a\x06\xc5\xf2/Cx>O\xd2\x04S\x82\xf0UA\xb8\x1b\xd2\xb3Q\xa9u\x0e-\xefD\x83\xdc\xeb\xc1\xa9\x08\x81\x90\xb7(\xafk\xbf\xa9\xc8S!\xfc2KfU\x89f8\xbbVV\xb1'+\xb5.;\xb4\xbff\x0c\xa9\xda?\xaa\x1f\xccn(\xc8\xaa %w\xc5X\x17\xd4u'\xe5\x8ej\xb3\xf69\x8e\x06L\xeaZo\x1e\xda_7\xc7\xe7\xdd\"\x89\x16\x06N\xda\x87T\xb3^\x97eN\x08\"YN\x03e\xd8\xbb\x0ci\x85|h}kc\x87W;\xe5CXT\xa2\x12\xf3\x03R4\x0b\xe9\xbb\xd6C\x9eE8\x94\xa8~ AE\xf5#}5}\x9a\xa1\xbb\xffU\x89\xca\xe4*\xc3\xe2\x92\xf5R\xb3\xea\xa1\x07\xf3\xfb\xa6\xe8\xd4\x98\xe5\xca\x1beSp\xc2\x0b\xc7\x0b\x14\x99_\xe8%\xd3\xae\xe7\x1f\xac\x05\xd5\xc7\x0e^\xf0e\xb9\xbc\xe1\xbc\x9e\x08\x88\xe6+\x94\x92[\x92\xca%E8\xbc_m\x9c\x08\x8f\xf6M\x9b4\xdf\xcbd8\xf3\x8d<\xb9\xd0\xf7)W\xae)\xf8e\xf5k\xf7\xb5\xea\xc2\x15\xe6\xab)\xcb].\x82\x83\xe6Z\xd5;N\xab\x8c\x17\xd9c(+\xbe\xd3\xa4\xa4\xa22\x9cy \xea\n\xace\x9c\\\xe2\xc8\x7f\xbb\xc9\x88\x8b\xb0\x80G\x0f\x1f\x07S\x0d\x92\xaa&\xc7\x04\xe7\x88\xe1\x8a.\xf2\"y\x10\xfa\xb3 \x11In}\x03\xa1\xae\xa0\xce\xfb\xa5{\x1d\x85\xde\x90p\x10\xe1QN\x97|\xdc]\x06\x17\x87c\xe3PO\xd4\xa6\xa9b\xbd/n\x12h \xe4\x1d\x9b\xc6\xde\xcb9\xc5Y\x8c\x8b\xd84/\xd2\xde\x95|\xf7r\x89\x8bkKD\\\xfd\xa8_\xfb\xa6\x03\xeb\xb0\xb2Z\xad\xf2\xa2Q\x9b\x96s\xfeF\xc6\xa2`J\x8bdVQ\x82\x96x\xcd\x8fL<\x04g\x84i\xf7\xec\x8a\xc4h&6\xdc\xa5M\xa8\xeb\x0f\xe6Y\xc4\xfc\x16\xa6\xe7Jb\xc9\x94\x11\x8f\xdc$\xba\xe4:\xf7\xb2\xc8\xd3\xb4Z\x85\xba5\xa4\xd9\xa1\x16\x00\xde\xff\x7fK\xf3\x80\xd3T\xab\x005#\xea\x8d\xe6\x84\x96\xf6\xd8\x88\xe6\xa3\xce\xdd\x94Rm\x10zU*\xd5:OH\x1a;\xd3\x00\x90\n!\xe2\x07A$\xc3\xb3T\xac\xf9\x99JV\xb6\xea\xbf\xf9^\xb5`PP\xf5\x8d\x91L_~\xa6d\xb4w\x1a\x0c\xb6s\xde*\xbf \x83\xf0\n\xdfyn\xec\xe9\x0bO\x1aEy\x9a\x12\xee81\xb6\xa5\xadp\x10d\x02\xf0\xd1\x96gD\x9f\xbbx\x18U\x1b\xb0\x9ePm\x98$\x0dB\xf5\xee^\xa8Z\xa1{\xc7\xa66F\xd6\x1f\x047\xd2\xc2[hO\xb9\xf9\xdc\x00\xd1<\xca\xa6\x83\x8bLi\xc4C\x99\xcb\x1b\x96\"\x94|\\K\xe4I\xdf\xd4\xc4\xc4?\xfc\x99\x9b\x1b\x12\xc8\xab\xdc,\x0c\"_\xad\xa7\xd0\xbe6\x08\x0f\xecM\xe9\x05\xc1\xd0WB\x1cJ\xdc\x1dK\xb6\x11\xba\xb8\xafl\x8fJ\xc1\xb5\x0c\x00\xb7B\x0d\xd7\xb4\x80\xf1\xde\xa5\x16P\xad\x0e:\xdd\xfaL\x96\x83\x99\x0d+\xda`\x8d\nP\x9fodZ\x07\nQx\x8f\x8dP\xfb\x94\n\xa6\xb7\xa1e'\x9e\x1d\x14\x88z\x87\xd2\x82Ab\xfco\x04%\xb1QlZ\xac6-\x82~\xed!g5\x93p\xb4\x9e\xc1@\xc0\xca;\x8c?0F\x18\n\x83D\x1d\xc1h8FK[h\xb5*\xeaJ\xe9\xdb\xf5\xd8T\xc2\x95\xe4\xc6\x05y\xd0,\x84[@(\x9c\xbf.\x1eP\xef\x02\xa7:\x02\xbaS.\x9eqc$\x06DI\xf4\x04\x12\x16)\xf1\x88X\x89\xe1\xd1\x12C\xe3%\x86FL\x0c\x8e\x99\x18\x14518nb`\xe4\xc4\xf0\xd8\x89\xe1\xd1\x13\x03\xe3'\x1e\x1bA\xd1O\xc3\x8b\xc7\x19E\x01\xf8\xd6\xfe\xe5\xd08\x8a'\x8b\xa4x\xeaX\x8a\xa7\x88\xa6\xd8\x9ax\x8ag\x89\xa8x\xa6\x98\x8a\xad\x8a\xaax\x19q\x15[\x18Y\xf1\xbc\xb1\x15\xf0\xe8\n\xe8\xb9\x9az\xa0\xe7k\xea\x193\xc6\x02\xee;\x8e\x14g1(\xd2\x02\xca\xa5>K\x94\x9b+\x84_P'\x12\x99\xf8\x1fd\xc6\xdbU\xc6\x13\xc8\xbc\x01\xe0(\xcc\xd6H\xb5\xaaa\xb2\xf1\xdc\x1e\x9a\xd7\xf0\x99\x07\xfd\xf2\x9c\x11v\x17\xa1t\xefw\x18\x7fI\x84\xd3t\x8d\xa2\x14\x97\xa5\xb7n\xb5\xfch\x04q7\xb0\x06V\x0b\x16\x91R\x9d\x90T\x1c\xca3\x8b\xcc\\\x80(MHV\xaf\x87y\xc4\x84\x97^w\x88\x06\x17\xc1\xb8\x8a\x13/:}\xe4\xf9\xc2\x97\xdf1\x9a\xadwP\xb5\x8a\xf5\xbfi\xb2$%\xc5\xcbU\xb9\xa3\xb7\xcfD\x15L\xff-\xb1\"m+\x95\xf7\x18\xces\xb7\x18\xe0\x156t}-v\x12\xe2K&DHq\x02\x07\x102\x06\x11#\xbb\xcb` \x91\x16sL\xc61\x91 \xfb\x84iP\x19+@2Z\xac\xf9\xed\x85\x92_\x90L\xb3\xf5\x88\x12\xc1\xb4\x00\x92+n\xc7\xbe\x82pQ\xe4\xe8 *8\xa4\x03\xca\xbc?\x93#\xf0\xc5ta\x8aK\xaa\x98\x06 \xb6\xa5\xfd\xc8\xc3\xf2\x92\x11;R\xaa\x0c\x98\xb0\xae\xdaa\xcd\xc7\xa8$\xf6\xe1}\x88.\x18\x19\xe6[K\xdf\xa5\xa5\xe7\x042\xbc<\x7fTp\xef\x1f\xb0\xdf\xc4u8\xc1\xd1B\xf6\xb8\xf7\x03g\xfd\xb3\xe6\xb3\x91\x11a\xca-\xf9\xb0D\xf6\x85&\x8aTPI\x9eM\x84\xbc\x88\xdc\xfa\x97I0\x0e\xa7\xcc\xc4}c\xd6\xb5D\x11^ /\xd3\x1a\xb5\xc3f\xa0\x97\x94\x1e\xe79Z\xe2k\"\x87\xba\xca\xddc\xe6JNl\xb2Fw\xa4\x08\xf4\x18\x8e]?\x80\x1a\xdds\x95\x8d\xad\xe2\xab\x0c_J{\x17\xf8\n'YI\x8d\xd5\x8c\x93^s\xeb\x9d}\x85\xb3\x88x\xa2\xcf.\x8cmW^I`\x81o\x89\xd1\x92D\x98\xc7\x94Q\xb5$M\x88[-$\xd9m\x9e\xde6\xcb\x10\xb4\x9f\xe3\x13\xb6\xf2\xfe[\"]\x10\x99\xd2-B\xc1\xce\x1a\x91\xc6\x08\xcb\x08\xb4F5\x8b\xf6S\xc7\xa3a\x05\xa9\xc8\x1e?7\xeb],\xf3[\xd7\x1d\xd5\xf5!\xcf(\xf7V\xb5\xc8\x0d\x8b\x88\xb0\xc6D\xf8\xddA\x80\x0f\x05\xf1\x9f\x9e\xfe\xc8\xab\x1d\xb8\xd5(Ea\x0fx\x90=\x1b\x08eh\xf4E(\x92a\xa32\x05\x94\\\x87\xcd\xa1\xe1\x0c&)\x10*\x90`\xa7-A\xa6\x1b$e\xbc\x01^&B\x1b\xa5N`\x08\xc9\x7f\xbe\x0c\x8c\x0cf\xeb\x0d \xe3\x9d\xc4\xc9K/o\xd7\x01\n\xe3\xe4\x0f\xf4\x00\x833D\xea\x11b=\xfa\xcd\x9a\x91\x06\xc2\x10Y\x1f\x15\xec\xe1\x1c(\x1e\x91UP\xe1\x98\x97_5\x88\xa2\xb1/\x08\x86\xf4$\xc8V\"\xa0\xbdD\x08\x12\xaa-\x1e\xe0\xd0@p=\x816\x13\xbc-\x1e\xddSa\xdb\x89\x9eF:\xc0r\xa6\xc5\xf4pKj\x12\x03\xe2\x04\xb3\xa6h\xfb\xb0\x1a\xc3\xb6\"\xf4\xfb\x86`\xb8\xe4#\x0f\x92a\x92?\xd2\x9e \xfb\x10\xb2\x8b\xdf\xb9\x1dj\x8c\xb5\x96\x95\xe8x+.{T\xdb\x86\x17`m\x99F\xf05\x80*\x04\x8a:\xeaqKb\xf8@\x0bu\xb6.\x82\xc6\xa9\xd3\xeda\x03\xb5=H=\xda0\xb5 \xf6\xc0)\xeau\x01\xdd\x16a\xd6e\x1c\x01n=\xb4|\xd5\x8ah\x1f\x8ae\xc8p\x01\xa1\x1b\x8e \xc4hy\xc9=F\xfa\x91\x06\xcep\xe9\x1fi\xb8\x00\xc3\xcb\x06\x05\x84_\xb9\xe9\xe8\xbe\xaaP\xef]m\xe0\xaeBI\xdbsY\xa1j\xddy[!\xdf\x7f\xed\xf6\xad\xe8\xd1\xa7\x0d\xf1\x17\xcc\xb8\xc6\xd9H\xd6\xf6\xc9\xc3)&u\xaf\xd6\xf1\xd0\xb2\xde*B?y\x1d\x02\x99]\xee\xa5%C\xf0-\xb51\xa6i\x99\xa3\xeb,\xbf\xcbx\xe9Z\xf4\x8dy0\xde8\x8b\xe7\xd8\xf3\x85!Vg\x18\xa8\x91\xab\x0e,TjA+} \xa0\xf34\xd6\xe85/y\x99\xd0\x05\x9a')%\x05\x89\xd1\xf5\xad2\xcd\x94\x14\x98\xe6\x85;>L\x06\x9f{\xe1\x02 ( )E\xd1pY\x95d\xa2\xab\xbd\xb5\x1cP]9\xb9q\x8c\x94\xf3\x80\xbc|>\x97\xa1o\xcd{D\xa1`\x05\x07\xc5h;)\xc0\xbc\x08\xe0PD\xe0n@\xa8s4\xde\x89b\xe4\xdd\xa1\xa6l\x90\x9a\xecX\xef\xef\x16\xb8\\\x8c/*\x93\x83Q\xe6BdF\xd1\x165\xd6\x82\xf9\x12!\xa5\x88\x06\xf1\x05\xe8\x02,\"\xce,\xc7\xcf\x9a\xf9\xd7\xaca^t\xda\x1f~\xc4\x1e\x1da\xc6//h\xf6f\x94/\x97y\xc6\xdb\xf3\x87\x81\x8a\xbb\xdb\x9e\x05\x0e\xd1\xb4\x880Sb\xd4\xc7\x9d4\x87\x9c\n\xabG\x97\xaeF\xaf\x05\xd9?\xeaSV\x8e\xd3DC\xe3=nU\x0fS\xbdIv\x9b_{\xc6R\x92\xad*\xfabs\xb4 \xb3\xa0W\xc7\xc3<\xc8\xe6s\xcc\xba]T'R5\xcf\xf9\x0dMIv\x8df8\xba\x96U\xe5\x01\x94x\x98\x01\xcf\xb6\xe0\x83\xc6\xbfI\xa5\xcb\xf9\x87w\xa9zB\x00t\x0f\xc4\x03\x9f*\x16\xf5-\x0d}\x1d\xf6\xfd\x0d\x94+\xf1\xb5\xa4x\x96&\xe5\x82\xc4*\x1e!\x14'\x0e\xd1\xe3=a\xea+\xb9]\xe1'$\xe2\xb9\x04\x86W\x01\xa0\xc7\x80B\xa7E\xbe\xcaK8\x06Z-o\x06\x08~\xa5\xe9J\xf14\xe7\x9br\xb4\xa8\"\x1e\x10\xc4}\xa4%.\xcaE \xee\x1c\xa1\x92bZ\x05\xa7~?\xfc\x8ft\xbaJ2\x17n\x13W|\\E\xaaa\xa8\x98\x06u\x80\xce\xa4\xf8\xc5\xaf \x91a\xee\xac\x8b\xf9A\xd7\xaa\n\xab\xe7^\x18\xc3\xd3\x1d\xcf\xbe~99\xfb\xf3\xf2\xe8\xf8\xf4\xc7\xc5\xe5\xf9\xc5\xf4\xe2\xc7y\xaf\x144\x17\x8d\xd3\xb3\x93\xd3\x93\xf3G\x10\x10\xef\x82\x9f\xeb\x14\xba\xc7\n\xd2_\x93\x07\xc1\x83\xf4@\x80\x84\x919\x05\xc8\xe9\xe0\x01[8M\xe2I\x95\x89\xf5\xa2\x18\xb7l\xec\x00>\x0et\xa5\x1dc\xf5\xd7v\x8e\x971c2\x84\x8bYB\x0b\\\xack\x0d\xc6\x0b\x0e\xea5\x9f\x98\n\xc3y\x14\xef\xec\x1c\x8awv\xfe\x121\xa1\x1b\x1e\xd8\xaa \xb7I^\x95\xe9\xba3\xd5\x8d\xbc(/\xafR\xd9\\\x148\xba\x16\x9bZ\xc2s\xd2+@\xa2l\x12tU\x06\xd2^f#\x1d?\x9b\xc9\x16-\x12r+nG\xc9+\x1aR;y\x06\xe2NPz\xb1^\xe13\xdb\xfa\x7fJ;OUqT\x01'\xac\xd2\x82x\xc4\x17\x13\xbd\x0b\xd0\xde\x86z\x06\x0bz\xcei*\xb9\xd4\x10\xac\xb71\xea\x05!\x80X\x9d\xa2$\xb1\x91S9\xc9\xae\xd4\x85;;s\x9c\xa4U\x01XB\"f\x86W$\x8bA\x1d\xd9\xa7\xd7\xfb\x18\xdd\xf3\x1f\xdf\x07Y\xa9\xee\xd7\xa7\xd3sXV|\xf3\xb3\xf3\x7f\x1d\x9d\x0e\xf8\xec\xdb\xf4\xe8{\xf03\xc3&\x0f\x95s\x985v\xb4\x06\xe9\x14\xe7\xc7\x0d\x0b\x8c\xaa\xac$a_\x8d\x1fH\xf4I\x84\xefvi\x1b<\xf6\xaea\xc2\x881\x9b\x98\x05\xado\x9e\x1a\xd2$\x1b\x0e\xed&\xd9;\xa3I-\xd2$\xca\xb32\x89\xd5\xee\x03o\xfc:\xe1\xa7\x11\xb1\xb8\x85k\x99\x94%\x9b\x9c\xd2\x1e\xe5\x05\x8aI\x8a\xd7$\x06\xe6\x16:\x98d\x83\xaf\xcd${\xe7\xc4\xa5>\x13\xb4r\xcet\x86+\x86\\=P\xa5'\x16U'B?\x91,\xc2\xab\xb2J5KRo\xf1U,d\x1f\x11\xd5\x9e\x07 \xad\x18\xc6\xa3\xe0\xe1u\xf9\x87>\xd9\x17GF\x8c+ \x99\xb8&X\xab\xeb@/\xb1\x9f\x8a\x85\x8c\xfa\xa0\xae3\x9c\xc0\\\x87\x97Q\x1c\xa9\xcd\xa5\x02\xb0>\xcb\x00\xd8\xd9\xd2ZG\xc9Z+I]\xd6g'\x14\xee\xed\xa9>\x12\xc9\xe7\xdcs\xe5]\x8e)\xc5\xd1B\xb4\xa6SQ\xd9\xdc$8rW\xc6n\xce\x189\x8e\xf9\xda=\x98q\xdd\x80Xr$\x91\xcb3\xb9\xa1!_\xcb4%\xbb\xc8z\xe3j\x94\x08\xa3&\xb5@h\x91\x11B\xe4$\xd8\x18\x02\x1b\x0e%\x02\xed\xe1mp\xa2\xe8\xf6\x9d\xd1A\xb2C\x83\x11?u7ls\xa8O\x9b\xcb\xe11>\x9a\x12\x04\x12XD\xe16\xc02F\x9ci=l\x80\xf0\x88\xcd\x82\xe0\xde\xe46\xe0c\xb0\xaa\x00\x92\xbb~\xfc\xd5F\x10z\x86P&\xa3\xe5P\x0c\xd3\x183\xe5\x05\xa61\x00\xa4\x02Y \x04\xb4\x14\xe8w\x16\x83|\x9e@\xba\xe0\xea\xe0w\x16\xc3 \xac\xc6\xb0.\x08\xfd\xceb\x80K>\xf2 \x19&\xf9#\x83AQ\x9f,\x06\xa9\x97\xc7\xcca\xb0\x90\x1c/\x83A\xfa\xd7O\x99\xbf\xd0\x94g\x04\xcf\x02\xa8:\xa0x\xa3\x0e\x8f\x81\xd5\x89\x7f\x1e\xf4\xcb\\huv\xd8$m\x0bF\x8f6DMr`\x846\x12x\xbey\xb46\x95\xaf0\x10\xc5\x97\xb2\x04\xb2\xf0kY\x07y)\x8d\x00X\xc8\x9e\x03a\x1a*\xff(\x96\x1c*w'\x15b\xcc5\x92\x95\xb8s\xad\x04\"\xc5\x15.Jb\xf7\x9a)(2\xc8\xd2!\xa0\xb5C=3\xf6\x10|\xfc \xf8TC=;\x06\xf5\xc8\xe0\x03\x13\xeak\x0b\xd1\xb0L>\xb4\xb5\x08\x86ld\x90 \xed\x95\x93\x85`\x99L/\x14\xcba\x16\xb4K\xcabE{R\xe8\xd7\x07!\xe3\x81\xfaA\xfe8\x1c\x83\xa6$H\xafw\x9e$\xea\x9d-\x88\x9e\x12\x91\x11\x16\x8b\x80\xa1:4{P\x1c\x0b\xbb\x93\x07\xe5rn\x13\xb9\x83V\x8eD{\x9e|B\xf5\x03\xc9\xaa6\xc6\xa5\x0dF\x9d\x9e\xf8:\x99\xab{\xa2I\xfcG7 \x91\xff\xa5;dZgK\xfcW\"$/\xcaW\xeb\xda\xe3\x13\x7fh\xa6: \xa6\\y\x8f\x01\x97\xc0\xef\x08\xf8\xf7M\x83#;\xdc\x0b\xc8h\x04E8C$\xe1E\xb8g\x04a>\x88w\x10ys\xe5\x9a\x92\x9f\xf7>\x7f\xfc4\xc3\xfb\xbb\x07\xf3w\x07\xbb\xef\x0f>\xe3\xddO\x1f\xf0\xc7\xdd9\x89\xf0\xde\xec\xed\xc1\xde>y+\xaa\xb6+\x0d\x11\xb9\xaf\x93\x93F]6\xe8\xe3u\xef\xe6\xe1\x8a\xbc}\xc0\x0f\xb4\xfap@\xef\x0f\xee\x0f\xd2\xf4\xf6\xe0>\xfa\xfc@\xcb\x9b\xfb\xf4\xfa\x8e\xa46\x8eC\xa9\x88c\xc1Y\x17\x99\xeb\x0d\xe8\xc1\xa7\xb7\xef\xe6\x9ff\xd1\xee\x87\xb7\x1f>\xee\xbe'\xb3\x83\xdd\xcf\x07{\xf3\xdd\xfd\xbd\xfd\xbd\x0f\x1f\xf7\xa2}\x12\xb5\x00\x15\x8d=\nRAb\xef\xe6\xde \xea\xe7\xf2&\x8d\x16\xef\xca\xfb\xbb\xec\xfd\xfb_\x07o\x7f=\\\xd1OE\xb9\xb8\xbdY\xcf\x8b_Q\xe1\x12\x87\xdf@\xcc@\xc8\xb3t]C\x80\x12\x9e\x02gl\xc4\xe3\xb4\xcc]\xfc\xc9\x0b\"\xac\x8a/x\xce4R\x9f\xb6N\x954\xfcJi6p\xf6\x91\xd8\xbb\xd9w\xa2|\xf7~?\xbe\xd9\xff\x15\xdf.c\xfcP\xdd=D8\x8e\x17\x8bOW\xcb\xea\xdd2z \xef<\x00\xb8W\xdf\xe3\x02`\xae\x9aE\xd6Xc/\x90\xe6h\x9ed\\!\x06F&\xb7\x1b\x99\xf4eE\xff\xca$c\xcb\x072;I\xec\xa0\xfa\xc4\xf4\xdd8\xa0\x1dL\xcfo`X4\xd8\x11\xc3a\x9e\xe2+.\x93\xbe\xc4 W?C4p\x8d\xa6\xb4o2\xe4\xa9$F\xaa\xbdZ|\x96\xab<+\xbd\xd0Hc\xb9=\xe0\x98\xe6=\x04\x8fw\xcc\xa9\xb5bO`\xc8\xbd\xe0\xc3\xbb\xed\xf0\x94\xc0\xb4\x18r\x03#\x7f\xc8%t\x1e\xeer\x12<\xf8)\x04D\xdd!\x0e\xd7\x07=1\x10-\x86 #\xc4\x99\x13\xadidh\x9dW\x85\xf4\xc1\x9a0\xf8\xb9\x92E5\xca3 !\xaaC\x12\xc5\xff\x1942\x8c\x9d\xa0\xbf*R\xac'\xea\x1btv\xfa\xa5EN\xe4\xbf\xd6\x0c\xa8\x00d\xe3g\x0d~\xa6\x19\xaa2r\xbf\"\x11s$\xc5\xd5U\xaai\xe3\x9b2Z\x90%n\xf6\x9d\xd3\xadt\xbb\x94\x9c~w\x04xlD\x94\xc7\x16\x8d+>pUTWC%\xc9\xe8\xbb\xfd\x0e>\x8ej\xe4\x1e\x1ebBq\x92nI\xe5\x10\xf6\xf9eU8/\xcd \xda[\x9ep;\xf8k\xebF\xc9\n\x17xI()\x0c\x9ewE\x8e\xb1\xf6\xb2\\\x03\xb05!\xfa\xae\x05\x86\xad\x00|\x8e\xe9\x10\x0f?\xc9\x0e\xd1\nS3v\x94i\x86\xa4 \xf1!\xa2EeN%+\xc4\x1a-\xed\xbbC\xf1\xea\xed\xec\x0fu\xf15f\x0dj\x16\xfc\xc6v\xe7\x1f\xe1\xc4\xdb\\\xf7\xd1\xfa\xca\xf0\xc9\x81\x9d5\xd8\x8b\x1f\xd7wg\x08\xdc0Cb\x85`\x8e\xd3\xb2/\x06\xad\x93+\x00\x06\xfd\x1dy\xc3e\xb7\xf6q\xd7}\x1fQ\xce\x86\xcb\x0d\x94t\xa8\x9bn8\xe4\x0dzp\x1ft\x88\xe0]\xbf\xca\xe5q\xb51\x91nrOT\xfa\xf8\xe7\x86'\xbe\xd5\x88\xb4\\l \"\x03\x1d\xf3\xda\x05oc\xf2\x8c\x08\xb4|k \x02\x83\xd47\x8b\x94\x04\x17\xd1B^[*\xa7\x8c\x82\xc0\x05\xffE}\xb8\xcc\xcc\xcf\x0e\xb3\xaaJ\xa3\xee\xa8bm\x01K\xa5\x89\x8d`\xa6\x18C\x9a\x1e\xc7g\xb7\xe3\xda\x08-i\xb87\xfa\x03\xb87hs\x03\x1b\xcdB<@t\x9e,\x93\x14\x17\xe9z\xa7\xe6\x816*\xb5[\xbd2^\xb2\xa9\xe5U\xb5$\x19\xc3\x8bj`\x190\xb8\xeb\xbczU\x90\xba}\xb6\xb6*\xf8\x19\x06J2\xf3\xce\x9b\xffBGs\xe1\x89\xe1\xb6?S\x8f\x1by-\xa4y\xae\xc3/\xe11\xea\xfe\x15\x84VE\xc6\xef\xd3\xb1\x916\xfd\xba\x9a.N\xd3\xfa\xdcF\xa6\x8e\x89\x9e\xecK\xbe\xee\xa0\xd7\xac3dk\x93\xfa\xfd\x1f\xd6\xd6\xda\xfd\xa3\xdau\xb6\x875\xf0\xdaOQ7 * q\x16O\xea\x01q\x994\xf1kM.[\x83\xfa'S4\xc3\xb1yz\xa4~\x84\x92\xf9\xa1\xc9\xd8Es\x8cr}\xcb\x07&\xceb\x94\xe5V\xf4\xdf\xb4\x08\xc8\xeb\x8d\xe8]\xce\x863\xbf\xe2(\x9f7u\x89)\x12?\xd7\x93d\xf9\xff\xcc \xb3Fq\x9e\xbd\xa2\x12\xf1\xb9P\xf6|\"1\xf0\xa4cg4?\xed\xa0\xba\x83f\x15EYN-\xc8\xe2\xc6\xcd ]2\xe26\xc7z(3\xb1toa\xe9\x85\xf2_\xc69)\x19\x9fKL\xa3\xe6\xcda\xc6\xf7f\x8f\xfcc\xadv`v\x0c\x07O\\;%\xbd\x16qG\x14\xd5%\xb0\x0c\x0e\xcf my\x91R\"\xfdR\x11ap\x15\"\xe3\xd8\xbd\xed\xdb\x1ch\xda4\xe8\xeaiG\xb1\xb6%\x07\xffO\x89%< \xc3\xbc\xbd\xda\x7f\xfb\xf6\x95{?\xc9\xc8\xbe\xb6\xfa!#\xed$9\xb6\xe2\xc3;\x80\xda\x1bg\xf8\xe8\xd2\xb1\x91\xba\x8a\x0b-\xf24.\x91:Q\x15\x93\xffub\x8boq\x9f\xf2>\xfe\xc8\xd5\xb7K\xea\x89\xed\nm\x1e\x85\x0et\x11l\x0b\x08\x18-\x13N\xcfE\xe8\x87(\xf5z\xf4'\xd2\x95:\xc4tG\xe8h\xb9J\xf9\x8d\x84%*\xe3\xeb7\xd3@\xf2\xa9\xb4Ys\x1c\x89\xedR^\x03U\xa8)\xf1%\x11sM9}m\xbf\xb1\xf9|\xc9\xcbe\xeej\xac\x04\xe7j?-\x98\xb5\x86)\xbb)\xd6:\xe2\xc2\x1f\x07\xd2\xf8R\xb8\x92\xbc\x16\x0bQ}\x81\xd34\xbf\x93fL\xe6\xb6\xfb\xc8E\xee\x83'~\xdf\xbd'\xfe\xd0\xb7\xbd\xaa\x9e`\xa1\x9c\xe0\x9c\x11Ox\xe6\x88\x07o\xaa\x88+\xb6_\\\xfaZ\x15=\xf0\xd7\xa8+\xf24X\x9c\x0e2\x82\xc4\x839\xbdzF*f\x8c\x82\xcc\x91,U\x0c\x8c\xfc\x93e\x11\x08\xa0@k/\xf0`\x85f\x1a\xd7\xdc\xf7\xa9\xbd\xd2\xf8\xf0\xe4\xec\xe8\x7f\x8e\x8e\xa7\x17'g\xfd\xbe;\xffz\xf6\xf3\xe8\xcb\xd7\x9e_\x1d\x1d\xff\xfcz\xde\xbb\xad/?\xce/N\xfe<\x9a\x1e\xf7\xfb\xec\xe4\xef\xe3\xbe\xfcM\xbf};\xfa~4\xbd\xf8\xda\xef\xb3\x93\xff=>\xfa\xc7\x8fp\x99\x9e\xc6G\xa7g'?\xbf\x1eO\x8f\xbf\xf4l\xec\xcb\xc9\xf1\xc5\xd9\xc9\xf7\xef}e\xfb9\xfd~\xf4'\xa0\xa3uu\x9fA\xc3+\xec\xab4\x1f\xd7(\x1e\xd8<\x12\x91o\xf2\xcc\xafG\x95\x1e\xc7\xa48\xb4\xbf\x96\xad\xe0\x92\xdf\xb3Z$WI\x86)\xa4X\xa9u\x0e\x1d\xda^*g\xbdD1\x99QT\x92\xe26\x89\x92\xec\n\xcd\xab,\xe2f\xacgkj\xee\x1d\xda^\x8a\xd5\x12\x0f\xbeK\"\x94d\xb7\xa4\xec/\x8f\x9e\xa7\x87\xd6\xb7\xaak2\x9a\xd0\xb50\xdfZ\xc6\xa8*i\x1e'8\x93\x82\xca\xfd\x1e\x0ep_A\xf9\xbc?\xec\xbci\x17\x08\\\xe1\x82\xae%O\xdch++\xc5\xaco\xcf&\xb5\xde8\xb4\xbe\x15\xe8\x8a\x06\xc5B9Cx>O\xd2\x04S\x82\xf0UA\xb8\x1b\xd2\xb3Q\xa9u\x0e-\xefD\x83\xdc\xeb\xc1\xa98l\x97\xb7\x01\xafk\xbf\xa9\xc8S!\xfc2KfU\x89f8\xbbVV\xb1'+\xb5.;\xb4\xbff\x0c\xa9R8\xaa\x1f\xccn(\xc8\xaa %w\xc5X\x17\xd4e\x18\xe5\xc6V\xb3\x148\x8e\x06L\xeaZo\x1e\xda_7\xc7\xe7\xdd\"\x89\x16\x06N\xda\x87T\xb3^W)N\x08\"YN\x03U\xc9\xbb\x0ci\x85|h}kc\x87\x17\xff\xe4CX\x14f\x12\xf3\x03RC\n\xe9;\xc3C\x9eE8j\xa5~ \xf1+\xf5#}5\xbd\xf5\xad\xbb\xffU\x89\xca\xe4*\xc3\xe2\xb2\xf0R\xb3\xea\xa1\x07\xf3\xfb\xa6\xe8\xd4\x98\xe5\xca\x1beSp\xc2\xeb\xa8\x0b\x14\x99_\xe8%\xd3.o\x1f,\x8d\xd4\xc7\x0e^\xf0U\xb9\xbc\xa9\xbb\x9e\x08\x88\xe6+\x94\x92[\x92\xca%E8\xda]\xad\xec\x85G\xfb\xa6M\x9a\xef{3\x9cyh\xb8\\\xe8\xfb\x94+\xd7\x14\xfc\xd2\xf5\xb5\xfbzp\xe1\n\xf3\xd5\x94\xe5j\x13\xc1As\xad\xea\x1d\xa7U&6 \xe9B\xf3\x9d&|\xc76o\xdc\x17\xeaLSb\x9c\\\xe2\xc8\x7f\xd9\xc7\x88\x8b\xb0\x80G\x0f\x1f\x07S\x0d\x92*\xae\xc6\x04\xe7\x88\xe1\x8a.\xf2\"y\x10\xfa\xb3 \x11In}\x03\xa1.(\xce\xfb\xa5{;\x83\xde\x90p\x10\xe1\xe14\x97|\xdc]\x06\x17\x87c\xe3PO\xd4\xa6\xa9b\xbd/\n\xeb\xb7\x04\xf2\x8eMc\xef\xe5\x9c\xe2,\xc6El\x9a\x17i\xefJ\xbe}\xb9\xc4\xc5\xb5%\xf4\xaa~\xd4\xaf}\xd3\x81uXY\xadVy\xd1(\xd5\xca9\x7f#\x03?0\xa5E2\xab(AK\xbc\xe6\xdb\xf7\x1e\x823\xc2\xb4{vEb4[s\x14\xa4M\xa8\xcb\xf1\xe5Y\xc4\xfc\x16\xa6\xe7JbI\x1c\x11\x8f\xdc$\xba\xe4:\xf7\xb2\xc8\xd3\xb4Z\x85\xba5\xa4\xd9\xa1\x16\x00\xde\xff\x7fK\xf3\x80\xd3T\xab\x005#\xea\x9d\xe6\x84\x96Z\x15x\x88\xa9\xb3>\xa5T\x1b\x84^\x95J\xb5\xce\x13\x92\xc6\xceX~\xa4\xe2upZ\xe6\x88dx\x96\x8a5?S\xc9\xcaV\xfd7\xdf\xaa\x16\x0c\n\xaa\xbe1R\xef\x95+\x19\xed\x9d\x06\x83\xed\x9c\xb7\xca\xef\x8b \xbc\xe0u\x9eS\xa3\xea5\xf7\xa4Q\x94\xa7)\xe1\x8eS}\xa4\xe1\xe2\x91 \xc0G[\x9e\x11}\x10\xe2aTm\xc0z\xa2\x82a\x924\x08\xd5\xbb{\xa1\xe2}\xee\x1d\x9b\xda\x18Y\x7f\x10\xdcH\x0bo\xa1=\xe5\xe6s\x03D\xf3<\x8b\x0e\xae\xb9\xa4\x11\x0f%\xf2nX\x8aP.n-\x91'\x9bQ\x13\x13\xff\xf0'2nH \xafr\xb30\x88|\xa5\x8fB\xfb\xda <\xfc\x19\xae \x18\xfaJ\x18\xccc\x1dK\xb6\x11\xba\xb8\xafl\x8f\xcaH\xb5\x0c\x00\xb7B\x0d\x97x\x80\xf1\xde\xa5\x16P\xad\x0e:\xddrE\x96\x83\x99\x0d+\xda`\xc9\x06P\x9fodZ\x07\xea2x\x8f\x8dP\xfb\x94\n\xa6\xb7\xa1U\x18\x9e\x1d\x14\x88z\x87\xd2\x82Ab\xfco\x04%\xb1QlZ\xac6-\x82~\xed!g5\x93p\xb4\x9e\xc1@\xc0\xaa\x1d\x8c?0F\x18\n\x83D\x1d\xc1h8FK[h\xb5*\xeaJ\xe9\xdb\xf5\xd8Tf\x8f\xe4\xc6\x05y\xd0,\x84[@(\x9c\x84.\x1eP\xef\x02\xa7:\x1b\x19\x11\xa6\xdc\x92\x0fKd_h\xa2H\x05\x95\xe4\xd9D\xc8\x8b\xc8\xad\x7f\x99\x04\xe3p\xcaL\xdc7f]K\x14\xe1\x95\xf02\xadQ;l\x06zI\xe9q\x9e\xa3%\xbe&r\xa8\xab\xdc=f\xae\xe4\xc4&ktG\x8a@\x8f\xe1\xd8\xf5\x03\xa8\xd1\x95\x85\xe9\xea\xf8*\xc3\x97\xd2\xde\x05\xbe\xc2IVRc5\xe3\xa4\xd7\xdczg_\xe1,\"\x9e\xe8\xb3\x0bc\xdb\x95g\xca/\xf0-1Z\x92\x08\xf3\x982\xaa\x96\xa4 q\xab\x85$\xbb\xcd\xd3\xdbf\x9a}\xfb9>a+\xef\xbf%\xd22/>\xcbE(\xd8Y#\xd2\x18a\x19\x81\xd6(&\xd0~\xeax4\xac \x15\xe9\xe3\n_\x9e\xda\xbf\xcco]W6\xd7\x87<\xa3\\\xe3\xd4\"7,\"\xc2\x1a\x13\xe1w\x07\x01>\x14\xc4\x7fz\xfa#\xafv\xe0V\xa3\xd4\x82=\xe0\xa1Y\x0e\xc4\xedY\x1a}\x11\x8ad\xd8\xa8L\x01%\xd7ash8\x03j\x14o\x08\xff\x06\x14\xcf\xb6%\xc8t\x83\xa4\x8c7\xc0\xbb5h\xa3\x94\x07\x0c!\xf9\xcf\x97\x81\x91\xc1l\xbd!a\xbc\x938y\xe95'\x19\x08'\x7f\xa0\x07\x18\x9c!R\x8f\x10\xeb\xd1o\xd6\x8c4\x10\x86\xc8\xfa\xa8`\x0f\xe7@\xf1\x88\xac\x82\n\xc7\xbc\x0b\xaaA\x14\x8d}_.\xa4'A\xb6\x12\x01\xed%B\x90Pm\xf1\x00\x87\x06\x82\xeb \xb4\x99\xe0m\xf1\xe8\x9e\n\xdbN\xf44\xd2\x01\x963-\xa6\x87[R\x93\x18\x10'\x985E\xdb\x87\xd5\x18\xb6\x15\xa1\xdf\x17\xe6\xc2%\x1fy\x90\x0c\x93\xfc\x91\xf6\x04\xd9\x87\x90]\xfc\xceeIc\xac\xb5\xacD\xc7[q\xd9\xa3\xda6\xbc\x00k\xcb4\x82\xaf\x01T!P\xd4Q\x8fK\x03\xc3\x07Z\xa8\xb3u\x114N\x9dn\x0f\x1b\xa8\xedA\xea\xd1\x86\xa9M\xb0\x07NQ\xaf\xfb\xd8\xb6\x08\xb3.\xe3\x08p \xa0\xe5\xabVD\xfbP,C\x86\x0b\x08\xddpL F\xcbK\xee1\xd2\x8f4p\x86K\xffH\xc3\x05\x18^6( \xfc\xcaMG\xf7\xcd}z\xef\xea\xa9\xae\xee\x93\x0dz\xee\xee\xd3\xbf\xd05\x1d\x91\xeb\xe6>\xa3\xc8\xa3\xf7\xe2\xbe\xc8~\xbf\x91\x18\x18O\x9b) \x98q\x0d\xd7\x91\x8c\xf6\x93GeL\xea\xc1Q\x87U\x0bQ\xdf \xf4\x93\x973\x90I\xea^Z2\x92\xdfRbc\x9a\x969\xba\xce\xf2\xbb\x0ca6~\xbf1G\xc8\x1b\xae\xf1\x1c[\xc70\xc4\xeaD\x055\xfd\xd4\xb9\x87\xcaPhe!\x04T\xa7\xc6\x1a\xbd\xe6\x953\x13\xba@\xf3$\xa5\xa4 1\xba\xbeU\x16\x9e\x92\x02\xd3\xbcp\x87\x99\xc9\x18v/\\ \x01%!\xa5o\x1a\x9e\xaf\x92Lt\xb5\xb7$\x84\xd69\xeb\xe6iT\xce\xe3\xfa\xf2\xf9\\F\xd05\xaf\xd4\x84\x82\x15\x1c\x14\xa3m\xc8\x00\xd3+\x80C\x11\x81\xbb\x01\xa1\xce {'\x18\x92w\x87\x9a\xb2Aj\xb2c\xbd\xbf[\xe0r1\xbe\xa8T\xde\xb5\xcf\x85\xc8\x8c\xda/j\xac\x05\xd3.BJ\x11\x0d\xe2\x0b\xd0\x05\xb2\x8e\xb8\xe5\x14[3\xff\x9a5\xcc/V\xf1G1\xb1G\x07\xaa\xf1R\xf4\xcd\xde\x8c\xf2\xe52\xcfx{\xfehRq\xd7\xd8\xb3\xc0!\x9a\x16\x81jJ\x8c\xfa\xd4\x94\xe6\x90\xc3e\xf5\xe8\xfa\xd7\xe8\xb5 \xfbG}X\xcbq\x9ahh\xbc\xa7\xb6\xeaa\xaa7\xc9n\xf3k\xcfXJ\xb2UE_l\xaa\x17d\x16\xf4\xeax\x88\xd3\xd7~\x8eY\xb7\x8b\"G\xaat:\xeb\xf84\xc9\xae\xd1\x0cG\xd7\xb2\x84<\x80\x12\x8fV\xe0I\x1b|\xd0\xf8\xf7\xbat\xa9\xf7\xf0fWO\x08\x80\xee\x81x\xe0S\xc5\xa2\xbe\xa5\xa1\xaf\xa3\xc7\xbf\x81R.\xbe\x96\x14\xcf\xd2\xa4\\\x90X\x855\x84\xc2\xcd!z\xbc'L}%\xb7+\xfc\x84D<%\xc1\xf0*\x00\xf4\x18P\xe8\xb4\xc8Wy \xc7@\xab\xe5\xcd\x00\xc1/\xe1\\)\x9e\xe6|o\x8f\x16U\xc4\xe3\x8a\xb8\x8f\xb4\xc4E\xb9\x08\x84\xaf#TRL\xab\xe0\xd4\xef\x87\xff\x91\xcezI\xe6\xc2m\xe2\x8a\x8f\xabH5\x0c\x15\xd3\xa0\x0e\xd0 \x19\xbf*\x1eA$\xa2\xe5Y\x17\xf3\xf3\xb2U\x15V\xcf\xbd0\x86gM\x9e}\xfdrr\xf6\xe7\xe5\xd1\xf1\xe9\x8f\x8b\xcb\xf3\x8b\xe9\xc5\x8f\xf3^\x99l.\x1a\xa7g'\xa7'\xe7\x8f \xde\x05?\xd7\x99x\x8f\x15\xa4\xbf&\x0f\x82\x07\xe9\x81\x00 #\x01\x0b\x90\x1a\xc2\xe3\xbep\x9a\xc4\x93*\x13\xebE1n\xd9\xd8\x01|\x1c\xe8J;\xc6\xea\xaf\xedT1c\xc6d\x08\x17\xb3\x84\x16\xb8X\xd7\x1a\x8c\xd7-\xd4k>1\x15\x86\xf3(\xde\xd99\x14\xef\xec\xfc%bB7<\xb0UAn\x93\xbc*\xd3ug\xaa\x1b\xe9U^^\xa5\xb2\xb9(pt-\xf6\xc6\x84\xe7\xa4W\x80D\xd9$\xe8\xaa\x0c\xa4\xbd\xccF:~6\x93-Z$\xe4V\xdc\xb1\x92W4\xa4v\xf2\x0c\xc4\x9d\xa0\xf4b\xbd\xc2g\xb6\xf5\xff\x94v\x9e\xaa\x1a\xab\x02NX\xc1\x06\xf1\x88/&z\x17\xa0\xbd\x0d\xf5\x0c\x16\xf4\x9c\xd3Tr\xa9!Xoc\xd4\x0bB\x00\xb1:\xd3Ib#\xa7r\x92]\xa9k{v\xe68I\xab\x02\xb0\x84D\xcc\x0c\xafH\x16\x83:\xb2O\xaf\xf71\xba\xe7?\xbe\x0f\xb2R\xdd\xafO\xa7\xe7\xb0\xe4\xfa\xe6g\xe7\xff::\x1d\xf0\xd9\xb7\xe9\xd1\xf7\xe0g\x86M\x1e*\xe70k\xech\x0d\xd2)\xce\x8f\x1b\x16\x18UYI\xc2\xbe\x1a?\xd7\xe8\x93O\xdf\xed\xd26x\xec]\xc3\x84\x11c61\x0bZ\xdf_5\xa4I6\x1c\xdaM\xb2wF\x93Z\xa4I\x94ge\x12\xab\xdd\x07\xde\xf8u\xc2\xcf/bq\x97\xd72)K69\xa5=\xca\x0b\x14\x93\x14\xafI\x0cLQt0\xc9\x06_\x9bI\xf6\xce\x89K}\xb4h\xe5\x9c\xe9\x0cW(\xbaz\xa0JO,\xaaN\x84~\"Y\x84We\x95j\x96\xa4\xde\xe2\xabX\xc8>\"\xaa=\x0f@v2\x8cG\xc1\xc3\xeb\xf2\x0fT_\xd5_\xa5\x94+i Yv\xd5P\xd7\x81^b?\x15\x0b\x19\xf5A]\xae8\x81\xb9\x0e/\xa3\xc6R\x9bK\x05`}\x96\x01\xb0\xb3\xa5\xb5\x1c\x93\xb5\xe4\x92<O ]pu\xf0;\x19b\x10VcX\x17\x84~'C\xc0%\x1fy\x90\x0c\x93\xfc\x911\xa5\xa8O2\x84\xd4\xcbc\xa6BXH\x8e\x97\x08!\xfd\xeb\xa7L\x83h\xca3\x82g\x01T\x1dP\xbcQ\x87\xc7\xc0\xea\xc4?\x0f\xfa%@\xb4:;l\x92\xb6\x05\xa3G\x1b\xa2&90B\x1b\x89_\xdf~\x10|\xaa\xa1\x9e\x1d\x83z$\x02\x82 \xf5\xb5\x85hXB \xdaZ\x04C62H\x90\xf6J\xedB\xb0\x84\xa8\x17\x8a\xe50\x0b\xda%e\xb1\xa2=)\xf4\xeb\x83\x90\xf1@\xfd \x7f\x1c\x8eAS\x12\xa4\xd7;\xdd\x12\xf5N:DO\x89\xc8\x08\x8bE\xc0P\x1d\x9a\x84(\x8e\x85\xdd9\x88r9\xb7\x89\x14DA\xda\x93l(\xdb\x96\xc7\xc0\xdd\xccA\x9eS\xd8\xed\xe7V#\xfcW\"\x8e.\xcaW\xeb\xdaM\x13\x7fh\xe6'!\x9e\xcd\xe8j2`\xc7\xfd\xd6;\xb8\xf9\x1d\x1c\x91\x90\xfe\xecluc5\x0dUO\n#\xb9\x83\xc8\x9b+\xd7\x9c\x12?\xdd\xbb\xd9\xbf\"o\x1f\xf0\x03\xad>\x1c\xd0\xfb\x83\xfb\x834\xbd=\xb8\x8f>?\xd0\xf2\xee\xfd~|\xb3\xff+\xbe]\xc6\xf8\xa1\xba{\x88p\x1c/\x16\x9f\xae\x96\xd5\xbbe\xf4@\xde\xd9(\xfbw{G\x92^\xef\xe3F8C$\xe1\x15\xc8g\x04a>\xf5\xbcB\x7f\xde\xfb\xfc\xf1\xd3\x0c\xef\xef\x1e\xcc\xdf\x1d\xec\xbe?\xf8\x8cw?}\xc0\x1fw\xe7$\xc2{\xb3\xb7\x07{\xfb\xe4\xad(Y\xaf\xf4Z\xe4\xbeK\xaf\x89\xb2\x8f\xd7\xbd\x9b\x07'\xca7\xf7\xe9\xf5\x1dI\xad`\x06\x12(\xc7\x82\xb3\xae\xb0\xd7\x1b\xd0\x83Oo\xdf\xcd?\xcd\xa2\xdd\x0fo?|\xdc}Of\x07\xbb\x9f\x0f\xf6\xe6\xbb\xfb{\xfb{\x1f>\xeeE\xfb$j\x01*\x1a{\x14\xa4\x82\xc4\xde\xcd\xbd\x13\xd4\xcf\xe5M\x1a-\xde\x95\xf7w\xd9\xfb\xf7\xbf\x0e\xde\xfez\xb8\xa2\x9f\x8arq{\xb3\x9e\x17\xbf\xa2\xc2%\x0e\xbf~\x99\x81\x90g\xe9\xba\x86\x00%\xc0i\x99\xbb\xf8\x93\xb7cX\xd5\xb5{\xad\x1c\xecL\x19ra\xae^E\xf6VcO\x8e\xe6(\xcd\xf3k\xa6\x9d-Td\xb2\x8f\xd8\x90\xf4\xf1\xe1\xbb\x07@\xfbk\x9e\xdf\xc0F^\x83\x1d\xa1\xc8\xe6)\xbe\xe2\xa6E_-\x90\xab\x9fq1\xdd\xa6T\x10\x91\xd6MF\x11\x95\xa4\xb6@j9W\xae\xf2\xac\xb4Fyhvd\x02\xfc\x16\x01d&\xed\x870\xf2O\x9b\xf2Q\x10\x91{\xc1\x90wI\xff\x94\x08\xb5\x18r\x03$\x7f\xc8%t\x1e\x9cr\x12<\xb0(\x04\x84\xea\x19\xa7\x87\x82\x9e\x18\x88\x16C\x90\x91\xe2\xcc7\xd642\xb4\xce\xabB\xbaJM\x18\xfc\\\xc9\xf4\xae3\x89 \xaa\xa3\xfd\xc4\xff\x1922B\x9c\xa0\xbf*R\xac'\xaa\xd8\xef\xd9\xe9\x97\x161\x91YZ7\xafB{\x8d\x9f5\xb8\x99f\xa8\xca\xc8\xfd\x8aD\xcc\xdb\x13wK\xa9\x96\x8do\xcahA\x96\xb8\xd9sN\xdf\xcf\xed\xf7q\xfa\xdd\xfe\xf7\xe8\xf3(\x8f-\xcaW|\xe0*y\xae\x06J\x92\xd1w\xfb\x1d|\x1c\xe5\xc2=<\xc4\x84\xe2$\xdd\x92\x9a\x1c\xec\xf3\xcb\xaap\xdej\x13\xb4\x8d<\x95u\xf0\xd7\xd6-\x88\x15.\xf0\x92PR\x18<\xef\n\xfb\xad=\x01\xd7\x00lM\x87\xbe\xfe\xea0/\xd5\xe7<\x0d\xf1B\x93\xec\x10\xad05\xa32\x99^H\n\x12\x1f\"ZT\xe6T\xb2B\xac\xd1\xd2\xfe%\x14\xaf\xde\x0e\xe9P7Tc\xd6\xa0f\xc1ol\x97\xf3\x11\x8e\xa6\xcd\xbd\x1c\xad\xafZ\x07!\x8d^\x1a\xe4y\x8e\xc6\x99\xb1\xe4\x05\x0e\xa3\xc1\x8b\xe4q\x97\xc6\x0c\x81\x1bf\xe1\xac\x10\xccqZ\x821h\xf8\xcb@\x14\x86\xf9\xd8\x96\x85\xef\x10\xb7q\x88\xec]W\xc8\xe5$u`\x91\x1en_d\xfa8\xd7\xc8\xd2\xc86c\xd3\xf2\x8f\x81\xd0\x0c\xf4\xaak\xff\xb9A\xedy\x11h9\xc6@\x04\x06\xb9\xd3\x86\xe3\xdc \xe6p\xa27)?\xc5W\x0d\xc7\xe5/\xd9\xce\xab \xb3\x1f$\xc3YD&KBq\x8c)\x9e\xdc\xeeM\xf8\x84\x9f\xfc[\xd9\x9e\xffL\xe4@\x9f\xfc\xbb6\xc7\xff\x99\xc8q\xfdJ\x90\xbe\"\xda /\xab\xe5\x12\x17\xebC}gGIp\x11-\xe4\x85\xa0\xf23%\xbc\x0b\xf8\x8b\xfa\xbc\x95i\xe7\x1df\x0e\x95\xb9\xdbQ\xf5\xcb\x02\x8a\\\x13\x1bA\x8b3\x864=\x8e\xccn\xc7'\x11z\xd5\xf0K\xf4\x07p7\xce\xe6\xbf5\x9a\x85\xb8n\xe8\xa1\xd3B\x04\xdb\xbb\x01\x06\x90\x843V\x11\xfa!\xaa\x9f\x1e\xfd\x89t\xf1\n1\xdd\x11:Z\xaeR~\xd7_\x89\xca\xf8\xfa\xcd4\x90\x8f)m\xd6\x1cGb\x9b\x93\x97\x05\x15jJ|I\xc4\\S\xee^\xdbcl>_\xf2r\x99\xbb\x1a+\xc1\xe9\xcbO\x0bf\xada\xcan\xd6\xb1\x0eB\xf0\x87F4\xbe\x14\xae$/OBT_\xe04\xcd\xef\xa4\x19\x93\xe9\xde>r\x9dEm\xfd\xf0\x9b\xe4=!y\xbe}Q\xf5\x04k\xc7\x04\xe7\x8cx\xc23G:=;\xf9\xf9\xf5xz\xfc\xa5gc_N\x8e/\xceN\xbe\x7f\xef+\xdb\xcf\xe9\xf7\xa3?\x01\x1d\xad\x0b\xde\x0c\x1a^a_\xa5\xf9\xb8F\xf1\xc0\xe6\x91\x88\x10\x93\x87u=\n\xd78&\xc5\xa1\xfd\xb5l\x05\x97\xfc\x06\xd3\"\xb9J2L!\xf5;\xads\xe8\xd0\xf6R9\xeb%\x8a\xc9\x8c\xa2\x92\x14\xb7I\x94dWh^e\x11\xed\xec\x1c\x86[Ss\xef\xd0\xf6R\xac\x96xh[\x12\xa1$\xbb%e\x7fy\xf4<=\xb4\xbeU]\x93\xd1\x84\xae\x85\xf9\xd62FUI\xf38\xc1\x99\x14T\xee\xf7p\x80\xfb\n\xca\xe7\xfda\xe7M\xbbf\xde\n\x17t-y\xe2F[Y)f}{6\xa9\xf5\xc6\xa1\xf5\xad@W4(\x16\xca\x19\xc2\xf3y\x92&\x98\x12\x84\xaf\n\xc2\xdd\x90\x9e\x8dJ\xadshy'\x1a\xe4^\x0fN\xc5!\xb9\xbcgw]\xfbME\x9e\n\xe1\x97Y2\xabJ4\xc3\xd9\xb5\xb2\x8a=Y\xa9u\xd9\xa1\xfd5cHU\x87Q\xfd`vCAV\x05)\xb9+\xc6\xba\xa0\xaeL(7\xb6\x9a\xd5\xb1q4`R\xd7z\xf3\xd0\xfe\xba9>\xef\x16I\xb40p\xd2>\xa4\x9a\xf5\xbapoB\x10\xc9r\x1a(\xd4\xddeH+\xe4C\xeb[\x1b;\xbc\x1e&\x1f\xc2\xa2V\x91\x98\x1f\x90\xb2JH\xdf\xc6\x1d\xf2,\xc2\xc1&\xf5\x03 ;\xa9\x1f\xe9\xab\xe9Mo\xdd\xfd\xafJT&W\x19\x16\xd7p\x97\x9aU\x0f=\x98\xdf7E\xa7\xc6,W\xde(\x9b\x82\x13^Z\\\xa0\xc8\xfcB/\x99v\xc5\xf7`\xb5\xa0>v\xf0\x82\xaf\xca\xe5\x1d\xd8\xf5D@4_\xa1\x94\xdc\x92T.)\xc2\x01\xe0je/<\xda7m\xd2|\xdf\x9b\xe1\xccC\xa8\xe5B\xdf\xa7\\\xeb\x9b\xfb\xd7\xee\x8b\xb7\x85+\xccWS\x96\xdb>\x04\x07\xcd\xb5\xaaw\x9cV\x99\xd8\x80\xa4\x0b\xcdw\x9a\xf0\x1d\xdb\xbcq\x13\xa73s\x87qr\x89#\xff\xfd\x17#.\xc2\x02\x1e=|\x1cL5H\xaa\xde\x18\x13\x9c#\x86+\xba\xc8\x8b\xe4A\xe8\xcf\x82D$\xb9\xf5\x0d\x84\xba\xc66\xef\x97\xee\x85\x05zC\xc2A\x84\xc7\xc1\\\xf2qw\x19\\\x1c\x8e\x8dC=Q\x9b\xa6\x8a\xf5\xbe\xa85\xdf\x12\xc8;6\x8d\xbd\x97s\x8a\xb3\x18\x17\xb1i^\xa4\xbd\x13\x17\xf5/qqm\x89\x99\xaa\x1f\xf5k\xdft`\x1dVV\xabU^4\xaa\x97r\xce\xdf\xc8\x88\x0dLi\x91\xcc*J\xd0\x12\xaf\xf9\xf6\xbd\x87\xe0\x8c0\xed\x9e]\x91\x18\xcd\xd6\x1c\x05i\x13\xea\nuy\x161\xbf\x85\xe9\xb9\x92Xr)\xc4#7\x89.\xb9\xce\xbd,\xf24\xadV\xa1n\x0div\xa8\x05\x80\xf7\xff\xdf\xd2<\xe04\xd5*@\xcd\x88z\xa79\xa1\xa5\xfd\x08\xbd\xf9\xa8\xb3>\xa5T\x1b\x84^\x95J\xb5\xce\x13\x92\xc6\xce@q\xa4\x02mpZ\xe6\x88dx\x96\x8a5?S\xc9\xcaV\xfd7\xdf\xaa\x16\x0c\n\xaa\xbe1R\xef\x95+\x19\xed\x9d\x06\x83\xed\x9c\xb7\xca\xafP \xbc\x06t\x9eS\xa3\x104\xf7\xa4Q\x94\xa7)\xe1\x8eS}\xa4\xe1\xe2\x91 \xc0G[\x9e\x11}\x10\xe2aTm\xc0z\x82ya\x924\x08\xd5\xbb{\xa1zv\xee\x1d\x9b\xda\x18Y\x7f\x10\xdcH\x0bo\xa1=\xe5\xe6s\x03D\xf3<\x8b\x0e.C\xa4\x11\x0f\xe5\xb6nX\x8aPzj-\x91'\xc1O\x13\x13\xff\xf0\xe7\xf6mH \xafr\xb30\x88|\xd5\x80B\xfb\xda <\xfcI\x9f \x18\xfaJ\x18L\xed\x1cK\xb6\x11\xba\xb8\xafl\x8fJ\xd2\xb4\x0c\x00\xb7B\x0dW=\x80\xf1\xde\xa5\x16P\xad\x0e:\xdd\n>\x96\x83\x99\x0d+\xda`\x15\x03P\x9fodZ\x07J\x15x\x8f\x8dP\xfb\x94\n\xa6\xb7\xa1\x85 \x9e\x1d\x14\x88z\x87\xd2\x82Ab\xfco\x04%\xb1QlZ\xac6-\x82~\xed!g5\x93p\xb4\x9e\xc1@\xc0\n\x00\x8c?0F\x18\n\x83D\x1d\xc1h8FK[hw\xd6\xa1o\xd7cS)9\x92\x1b\x17\xe4A\xb3\x10n\x01\xa1p\x86\xb3x@\xbd\x0b\x9c\xea\xf0h\x89q\xdb\xed3\xfc\xf45\xbcumwG5\x1c\xd0\xfd,\xb6\xea\xee\x12x\xd7\xc4Cj\xd1\x18\x86g;\xef\xd3 n~\xa9\x07\xd8\xc7\xf2\xc7cDG\xc0\xe2#\xa0;\xe5\xe2\x197Fb@\x94DO a\x91\x12\x8f\x88\x95\x18\x1e-14^bh\xc4\xc4\xe0\x98\x89AQ\x13\x83\xe3&\x06FN\x0c\x8f\x9d\x18\x1e=10~\xe2\xb1\x11\x14\xfd4\xbcx\x9cQ\x14\x80o\xed_\x0e\x8d\xa3x\xb2H\x8a\xa7\x8e\xa5x\x8ah\x8a\xad\x89\xa7x\x96\x88\x8ag\x8a\xa9\xd8\xaa\xa8\x8a\x97\x11W\xb1\x85\x91\x15\xcf\x1b[\x01\x8f\xae\x80\x9e\xab\xa9\x07z\xbe\xa6\x9e1c,\xe0\xbe\xe3Hq\x16\x83\"-\xa0\\\xea\xb3D\xb9\xb9B\xf8\x15f\xa2\x12\x1d\xff\x83\xccO\xba\xcaxi7o\x008\n\xb35R5c\x98l<\x8f\x87\xe65|\xe6A\xbfF\xad\xa9\x0f\xefCt\xc1\xc80\xdfZ\xfa.-='\x90\xe1\x05\xdc\xa3\x82{\xff\x80\xfd&\xae\xc3 \x8e\x16\xb2\xc7\xbd\x1f8+d5\x9f\x8d\x8c\x08Sn\xc9\x87%\xb2/4Q\xa4\x82J\xf2l\"\xe4E\xe4\xd6\xbfL\x82q8e&\xee\x1b\xb3\xae%\x8a\xf0Jx\x99\xd6\xa8\x1d6\x03\xbd\xa4\xf48\xcf\xd1\x12_\x139\xd4U\xee\x1e3Wrb\x935\xba#E\xa0\xc7p\xec\xfa\x01\xd4\xe8\x9eK\x07J\xc7W\x19\xbe\x94\xf6.\xf0\x15N\xb2\x92\x1a\xab\x19'\xbd\xe6\xd6;\xfb\ng\x11\xf1D\x9f]\x18\xdb\xae\xe4\x19\xe5f\xa3\x16\xb9a\x11\x11\xd6\x98\x08\xbf;\x08\xf0\xa1 \xfe\xd3\xd3\x1fy\xb5\x03\xb7\x1a\xa5\x16\xec\x01\x0f\xcdr n\xcf\xd2\xe8\x8bP$\xc3Fe\n(\xb9\x0e\x9bC\xc3\x19P\xa3xC\xf87\xa0x\xb6-A\xa6\x1b$e\xbc\x01^7A\x1b\xa5<`\x08\xc9\x7f\xbe\x0c\x8c\x0cf\xeb\x0d \xe3\x9d\xc4\xc9K\xaf9\xc9@8\xf9\x03=\xc0\xe0\x0c\x91z\x84X\x8f~\xb3f\xa4\x810D\xd6G\x05{8\x07\x8aGd\x15T8\xe6\xf5H\x0d\xa2h\xec+d!= \xb2\x95\x08h/\x11\x82\x84j\x8b\x0784\x10\\O\xa0\xcd\x04o\x8bG\xf7T\xd8v\xa2\xa7\x91\x0e\xb0\x9ci1=\xdc\x92\x9a\xc4\x808\xc1\xac)\xda>\xac\xc6\xb0\xad\x08\xfd\xbeC\x16.\xf9\xc8\x83d\x98\xe4\x8f\xb4'\xc8>\x84\xec\xe2w\xee\x0f\x1ac\xade%:\xde\x8a\xcb\x1e\xd5\xb6\xe1\x05X[\xa6\x11|\x0d\xa0\n\x81\xa2\x8ez\xdc\xa3\x17>\xd0B\x9d\xad\x8b\xa0q\xeat{\xd8@m\x0fR\x8f6Lm\x82=p\x8az]Q\xb6E\x98u\x19G\x80{\xf1,_\xb5\"\xda\x87b\x192\\@\xe8\x86c\x021Z^r\x8f\x91~\xa4\x813\\\xfaG\x1a.\xc0\xf0\xb2A\x01\xe1Wn:\xba/\xb3\xd3{W\x1b\xb8\xcd\xce\xca\x93l\xd0s\xc7\x9d\xfe\x85\xae\xe9\x88xIG\x0b1\xa3\xc8\xa3\xa7\xa4\xa3\xfcUw\x88\x88\x81\xf1\xb4\x99\x02\x82\x19\xd7p\x1d\xc9h?yT\xc6\xa4\x1e\x1cuX\xb5\x10\xf5\x0dB?y9\x03\x99\xa4\xee\xa5%#\xf9-%6\xa6i\x99\xa3\xeb,\xbf\xcb\x10f\xe3\xf7\x1bs\x84\xbc\xe1\x1a\xcf\xb1u\x0cC\xacNTP\xd3O\x9d{\xa8\x0c\x85V\x16B@uj\xac\xd1k^93\xa1\x0b4ORJ\n\x12\xa3\xeb[e\xe1))0\xcd\x0bw\x98\x99\x8ca\xf7\xc2\x05\x12P\x12R\xfa\xa6\xe1\xf9*\xc9DW{KBh\x9d\xb3n\x9eF\xe5<\xae/\x9f\xcfe\x04]\xf3\xc2J(X\xc1A1\xda\x86\x0c0\xbd\x028\x14\x11\xb8\x1b\x10\xea\x9c\xb0w\x82!yw\xa8)\x1b\xa4&;\xd6\xfb\xbb\x05.\x17\xe3\x8bJ\xe5\xf5\xf3\\\x88\xcc\xa8\xfd\xa2\xc6Z0\xed\"\xa4\x14\xd1 \xbe\x00] \xeb\x88[N\xb15\xf3\xafY\xc3\xfc\xaa}\x7f\x14\x13{t\xa0\x1a/E\xdf\xec\xcd(_.\xf3\x8c\xb7\xe7\x8f&\x15\x97\x84=\x0b\x1c\xa2i\x11\xa8\xa6\xc4\xa8OMi\x0e9\\V\x8f\xae\x7f\x8d^\x0b\xb2\x7f\xd4\x87\xb5\x1c\xa7\x89\x86\xc6{j\xab\x1e\xa6z\x93\xec6\xbf\xf6\x8c\xa5$[U\xf4\xc5\xa6zAfA\xaf\x8e\x878}\xed\xe7\x98u\xbb(r\xa4J\xa7\xf3\x0b\x91\x92\xec\x1a\xcdpt-K\xc8\x03(\xf1h\x05\x9e\xb4\xc1\x07\x8d\x7f\xafK\x97z\x0fov\xf5\x84\x00\xe8\x1e\x88\x07>U,\xea[\x1a\xfa:z\xfc\x1b(\xe5\xe2kI\xf1,M\xca\x05\x89UXC(\xdc\x1c\xa2\xc7{\xc2\xd4Wr\xbb\xc2OH\xc4S\x12\x0c\xaf\x02@\x8f\x01\x85N\x8b|\x95\x97p\x0c\xb4Z\xde\x0c\x10\xfc\xee\xcc\x95\xe2i\xce\xf7\xf6hQE<\xae\x88\xfbHK\\\x94\x8b@\xf8:B%\xc5\xb4\nN\xfd~\xf8\x1f\xe9\xac\x97d.\xdc&\xae\xf8\xb8\x8aT\xc3P1\x0d\xea\x00\x9d\x90\xf1\xab\xe2\x11D\"Z\x9eu1?/[Ua\xf5\xdc\x0bcx\xd6\xe4\xd9\xd7/'g\x7f^\x1e\x1d\x9f\xfe\xb8\xb8<\xbf\x98^\xfc8\xef\x95\xc9\xe6\xa2qzvrzr\xfe\x08\x02\xe2]\xf0s\x9d\x89\xf7XA\xfak\xf2 x\x90\x1e\x08\x900\x12\xb0\x00\xa9!<\xee\x0b\xa7I<\xa92\xb1^\x14\xe3\x96\x8d\x1d\xc0\xc7\x81\xae\xb4c\xac\xfe\xdaN\x153fL\x86p1Kh\x81\x8bu\xad\xc1x\xddB\xbd\xe6\x13Sa8\x8f\xe2\x9d\x9dC\xf1\xce\xce_\"&t\xc3\x03[\x15\xe46\xc9\xab2]w\xa6\xba\x91^\xe5\xe5U*\x9b\x8b\x02G\xd7boLxNz\x05H\x94M\x82\xae\xca@\xda\xcbl\xa4\xe3g3\xd9\xa2EBn\xc5\x1d+yECj'\xcf@\xdc J/\xd6+|f[\xffOi\xe7\xa9\xaa\xb1*\xe0\x84\x15l\x10\x8f\xf8b\xa2w\x01\xda\xdbP\xcf`A\xcf9M%\x97\x1a\x82\xf56F\xbd \x04\x10\xab3\x9d$6r*'\xd9\x95\xba\xb6gg\x8e\x93\xb4*\x00KH\xc4\xcc\xf0\x8ad1\xa8#\xfb\xf4z\x1f\xa3{\xfe\xe3\xfb +\xd5\xfd\xfatz\x0eK\xaeo~v\xfe\xaf\xa3\xd3\x01\x9f}\x9b\x1e}\x0f~f\xd8\xe4\xa1r\x0e\xb3\xc6\x8e\xd6 \x9d\xe2\xfc\xb8a\x81Q\x95\x95$\xec\xab\xf1s\x8d>\xf9\xf4\xdd.m\x83\xc7\xde5L\x181f\x13\xb3\xa0\xf5\xfdUC\x9ad\xc3\xa1\xdd${g4\xa9E\x9aDyV&\xb1\xda}\xe0\x8d_'\xfc\xfc\"\x16wy-\x93\xb2d\x93S\xda\xa3\xbc@1I\xf1\x9a\xc4\xc0\x14E\x07\x93l\xf0\xb5\x99d\xef\x9c\xb8\xd4G\x8bV\xce\x99\xcep\x85\xa2\xab\x07\xaa\xf4\xc4\xa2\xeaD\xe8'\x92ExUV\xa9fI\xea-\xbe\x8a\x85\xec#\xa2\xda\xf3\x00d'\xc3x\x14<\xbc.\xff@\xf5\x15\xfbUJ\xb9\x92\x96\x90\x89Ki\xb5\xba\x0e\xf4\x12\xfb\xa9X\xc8\xa8\x0f\xear\xc5 \xccux\x195\x96\xda\\*\x00\xeb\xb3\x0c\x80\x9d-\xad\xe5\x98\xac%\x97\xe4\xe1\x91\x83\\\xb8\xb7\xa7\xfaH$\x9fs\xcf\x95w9\xa6\x14G\x0b\xd1\x9a\xcehes\x93\xe0\xc8]`\xbb9c\xe48\xe6k\xf7`\xe2v\x03b\xc9\x91D.\xcf\xe4\x86\x86|-\xb3\x9d\xec\"\xeb\x8d\xabQ\x02\x95\x9a\xd4\x02\x11JF$\x92\x93`c\x08l8\" \xb4\x87\xb7\xc1\x89\xa2\xdbw\x06\x19\xc9\x0e\x0d\x06\x0e\xd5\xdd\xb0\xcd\x11Cm.\x87\x87\niJ\x10H`\x81\x89\xdb\x00\xcb\x18\xe1\xaa\xf5\xb0\x01\xc2#6\x0b\x82{\x93\xdb\x80\x8f\xc1\xaa\x02H\xee\xfa\xf1W\x1bA\xe8\x19\"\xa2\x8c\x96C\xa1Pc\xcc\x94\x17\x98\x0d\x01\x90\nd%\x10\xd0R\xa0\xdf\xc9\x10\xf2y\x02\xe9\x82\xab\x83\xdf\xc9\x10\x83\xb0\x1a\xc3\xba \xf4;\x19\x02.\xf9\xc8\x83d\x98\xe4\x8f\x8c)E}\x92!\xa4^\x1e3\x15\xc2Br\xbcD\x08\xe9_?e\x1aDS\x9e\x11<\x0b\xa0\xea\x80\xe2\x8d:<\x06V'\xfey\xd0/\x01\xa2\xd5\xd9a\x93\xb4-\x18=\xda\x105\xc9\x81\x11\xdaH\xfc\xfa\xe6\xd1\xdaT\xda\xc3@\x14_\xca\x12\xc8\xc2\xafe\x1d\xe4\xa54\x02`!{\x0e\x84i\xa8\xfc\xa3Xr\xa8\xdc\x9d\x8c\x8a1\xd7HV\xe2\xce\xb5\x12\x88\x14W\xb8(\x89\xddk\xa6\xa0\xc8 K\x87\x80\xd6\x0e\xf5L\xfcC\xf0\xf1\x83\xe0S\x0d\xf5\xec\x18\xd4#\x11\x10L\xa8\xaf-D\xc3\x12\x02\xd1\xd6\"\x18\xb2\x91A\x82\xb4Wj\x17\x82%D\xbdP,\x87Y\xd0.)\x8b\x15\xedI\xa1_\x1f\x84\x8c\x07\xea\x07\xf9\xe3p\x0c\x9a\x92 \xbd\xde\xe9\x96\xa8w\xd2!zJDFX,\x02\x86\xea\xd0$Dq,\xec\xceA\x94\xcb\xb9M\xa4 \n\xd2\x9edC\xd9\xb6<\x06\xeef\x0e\xf2\x9c\xc2n?\xb7\x1a\xe1\xbf\x12qtQ\xbeZ\xd7n\x9a\xf8C3? \xf1lFW\x93\x01;\xee\xb7\xde\xc1\xcd\xef\xe0\x88\x84\xf4gg\xab\x1b\xabi\xa8zR\x18\xc9\x1dD\xde\\\xb9\xe6\x94\xf8\xe9\xde\xcd\xfe\x15y\xfb\x80\x1fh\xf5\xe1\x80\xde\x1f\xdc\x1f\xa4\xe9\xed\xc1}\xf4\xf9\x81\x96w\xef\xf7\xe3\x9b\xfd_\xf1\xed2\xc6\x0f\xd5\xddC\x84\xe3x\xb1\xf8t\xb5\xac\xde-\xa3\x07\xf2\xceF\xd9\xbf\xdb;\x92\xf4z\x1f7\xc2\x19\" \xaf@>#\x08\xf3\xa9\xe7\x15\xfa\xf3\xde\xe7\x8f\x9ffx\x7f\xf7`\xfe\xee`\xf7\xfd\xc1g\xbc\xfb\xe9\x03\xfe\xb8;'\x11\xde\x9b\xbd=\xd8\xdb'oE\xc9z\xa5\xd7\"\xf7]zM\x94}\xbc\xee\xdd<8Q\xbe\xb9O\xaf\xefHj\x053\x90@9\x16\x9cu\x85\xbd\xde\x80\x1e|z\xfbn\xfei\x16\xed~x\xfb\xe1\xe3\xee{2;\xd8\xfd|\xb07\xdf\xdd\xdf\xdb\xdf\xfb\xf0q/\xda'Q\x0bP\xd1\xd8\xa3 \x15$\xf6n\xee\x9d\xa0~.o\xd2h\xf1\xae\xbc\xbf\xcb\xde\xbf\xffu\xf0\xf6\xd7\xc3\x15\xfdT\x94\x8b\xdb\x9b\xf5\xbc\xf8\x15\x15.q\xf8\xf5\xcb\x0c\x84\xe7i\x88\x17\x9ad\x87h\x85\xa9\x19\x95\xc9\xf4BR\x90\xf8\x10\xd1\xa22\xa7\x92\x15b\x8d\x96\xf6/\xa1x\xf5vH\x87\xba\xa1\x1a\xb3\x065\x0b~c\xbb\x9c\x8fp4m\xee\xe5h}e,,\x81\x9d5x):\xee\x02\x94!p\xc3\xec\x88\x15\x829NK0\x06\xad\xc3\xa0\x86\xf0p\xef{C\xcc5\\f`\x17\x0ds\xb3-k\xdf!\x9e\xe3\x10\xd9\xbb\xde\x90\xcbO\xea\xc0\"\x9d\xdc\xbe\xc8\xf4\xf1\xaf\x91\xa5\x91m\xc6\xa6\xe5\"\x03\xa1\x19\xe8X\xd7.t\x83\xda\xf3\"\xd0\xf2\x8d\x81\x08\x0c\xf2\xa8\x0d\xdf\xb9A\xcc\xe1GoR~\x8a\xaf\x1a\xbe\xcb_\xb2\x9dW\x13fBH\x86\xb3\x88L\x96\x84\xe2\x18S<\xb9\xdd\x9b\xf0 ?\xf9\xb72?\xff\x99\xa8\x81\xfeJ\x90\xb9\"\xda\xe7.\xab\xe5\x12\x17\xebCU\xe0\xaeD%\xc1E\xb4\x90\x17\x80\xaa\x0f\x95\xa8.\x98/\x8c\x98\xb8\x96\xad\x17\xca\xcab\xef\xe1\xde\x91\xd7-\x02\xbbD\xe8\xda\x1e\x0f\x0c\xb5\xf1Sb\xc6\xad\xee\x96\xd4\xddj\xf5\x87\xfcH\xc3\xe3\xcd\x80&\xc4;\x94\x1eEYGj\x14\x0f\x17vM\x9ea\xab\xa8\x84\x9e\x14\x9bQ\x16\x83\x9a\xd1\xdb\x9c\x8aQ\xe4\xa2D\x03;F\x13\x9c\x9awI\xeaG(\x9b\x1e\x99\x14_y&\x9d#aR\x7f\x9f\xd1Y\xb9\xa4\xf6\xfc\xbb6\xab\xc5\xd0c/75\xbcb\x85\xee\xf1\x8a\xab\x1eU\x9fO\x1blh\xc7\xda\xc1&\xd7q\x87\xef&\xbf\xfff|c\xe0\xf6\x9b\x85\xdc\xd5}\xc9\xa6\x86wg*\xa7\x81\x19\xb5\xb7\x979\x95\xe6F\xd6\xa4\xb1\x1f\xa5e\xf1\x8ar\x11\xc0\xa3pd\x0d*T3s\xdc\x16\xdbn~q\xe8;L\xec*n\x1bC\xf3\x8al\x1dn\"\xdc\x16\xc6\xea3\xb1\xed\xe4jJKR\xa3\xa2\xa4jY\xa1z\x99\xcc\xba\xfc\n\xa0\xd5\xc2\x84w\xb9\x9a\x94tf,\xe1\xc9\x926\xe1G\x1aP\x9aM\xf9+\xb5\x84ks\xc9\xfec\xa5|a\xbb\xc6\x02h\xc6\xaae7-\xaa\xab|\x19(^\x12\xda:\x19\xe0\"\xdd/+\xc3\xee\x110\xd8\xed\xf7\xd9\xd9_\xa7}\xf1;\xfe\xfa\xf5\xe4\xdb\xc9\xf1\xd5\x97~\x9f\x9d\xfd\xdf\xe9\xc9?\xbe\xc7+\x11Y\x1f\x9d_\x9c\xfd\xf9\xe5\xf4\xf8\xf4s\xcf\xc1>\x9f\x9d^]\x9c}\xfb\xd6\x97\xb6?\x8f\xbf\x9d\xfc\x01\x98h]\xc0h\xd0\xf2\x8a\x1b+\xf6\xe3[\xc5\x03\x87G\"\xe2O^\xbe\xf6(D\xe4\xd9\x14G\xee\xd7r\x14\\\xf3\x8e\xb4Uv\x93\x15\x98B\xea\xb1:\xf7\xd0\x91\xeb\xa5\xb2\xd0k\x94\x92 ;\x12TwY\xc2\xce\x95\xd3e\x91\xd0\x8e\x1b8>\x9a\xda{G\xae\x97\xe24\xc9C\x15\xb3\x04e\xc5\x1d\xa9\xfb\xd3\xa3\xf7\xe9\x91\xf3\xad\x9a\x9a\x82ft%\xd4\xb7\xa61Y\xd6\xb4L3\\HB\xa5?\x8f3\xb8/\xa1|\xdf\x1fu\xde\xb4k .pEW\x12'\xae\xb4\x95\x96b\xda\xb7\xe7\x90Zn\x1c9\xdf\n\xee\x8a\x01\xc5\xf1\xaf@x:\xcd\xf2\x0cS\x82\xf0ME\xb8\x19\xd2sP)u\x8e\x1c\xef\xc4\x80\xdc\xea\xc1\xb9\x08z\x90}\x93W\x8d\xddT\x95\xb9 ~^d\x93e\x8d&\xb8\xb8UZ\xb1'*\x8d,;r\xbff\x08\xa9j?j\x1e\xcci\xa8\xc8\xa2\"57\xc5\xd8\x144\x95&\xa5G\xd5\xaev\x8e\x93\x01\x9b\xba\x91\x9bG\xee\xd7\xf6\xfa\xbc\x9fe\xc9\xcc\xe0\x93\xb6!\xd5\xae\xd7\x85\x983\x82HQ\xd2H\xe1\xf5.BZ \x1f9\xdf\xba\xd0\xe1\xf5M\xf9\x12\x16\xb5\xa7\xc4\xfe\x80\x94\xc9B\xba\xbbz\xcc\xb2\x88\x07\x0f5\x0f$\x8c\xa8y\xa4\xad\xa6o0\xf4\xf4\xbf\xaaQ\x9d\xdd\x14X\xb4U\xaf5\xaa\x01x0\xbb\xef\x18\x9d\x1b\xbb\\Y\xa3l\x0b\xee\xf3R\xf1\x82\x8b\xcc.\x0c\x82iW\xf0\x8fV\x7f\xea\xa3\x07\xaf\xf8\xb1\\\xf64o6\x02\xa2\xe5\x02\xe5\xe4\x8e\xe4\xf2H\x11\x0f\xe8W\x8e\x13a\xd1\xben\x83\xe6\xbeL\xc6g\xee\xc8\x93\x07\xfd\x90p\xe5\x92\x82\xb7\xa7_\xf9\x1b\xa9\x0bS\x98\x9f\xa6\x1c\xdd[\x04\x06\xf6Y5\xb8N\x97\x05/\xab\xc7\xb8\xac\xf0\xce\xb3\x9a\x8aZp\xe6%\xa8/\x94\x96ar\x8d\x93p?\x93\x11\x0fa\x11\x8b\x1e\xbe\x0e\x8e5\x93T\xfd8F8\xe7\x18^\xd2YYe\x8fB~V$!\xd9]h!45\xd3\xf9\xbct\x1bPh\x87\x84\x07\x08\x8fk\xba\xe6\xeb\xee:z8\x1c\x9b\x0f\xcdF\xb5U\x15\x9b}\xd1;\xa0EPpm\x1a\xbe\x97K\x8a\x8b\x14W\xa9\xa9^\xa4\xbe\xab\xb9\xf7r\x8e\xab[G\x0c\\\xf3\xa8_\x87\xb6\x03\x9b\xb0z\xb9X\x94\x95U\x8d\x96c\xfeZF\xe0`J\xabl\xb2\xa4\x04\xcd\xf1\x8a_\x99\x04\x00N\x08\x93\xee\xc5\x0dI\xd1D8\xdc\xa5Nh*\x0e\x96E\xc2\xec\x16&\xe7j\xe2\xc8\x8d\x11\x8ft\x12]s\x99{]\x95y\xbe\\\xc4\xa65&\xd9\xa1\x1a\x00>\xff\x7fI\xf5\x80\xf3\\\x8b\x00\xb5#\x1aGsFkw<\x84\xfd\xa8{7%T-@\xafj%Z\xa7\x19\xc9So\xe0?R\x81S\xfc\"\x88\x14x\x92\x8b3?\x13\xc9JW\xfd\x9d\xfb\xaa\x05\x82\x02jh\x8d\x14\xba\xdd\x99\xa2\xd1=i0\xb6]\xf2QyK\x0c\xc2kz\x97\xa5\xe1\xd3\x17\x964J\xca<'\xdcpbhK]\xe1\x01\xc8\x08\xe0\xab\xad,\x88\xbew \xaa\x1c\xb0\x81\xe0l\x18%\x16\xa0\xc6\xbb\x17\xabO\xe8\xf7\xd84\xca\xc8\xf9\x83\xa8#-\xeeB{J\xe7\xb3\xc5D\xf3*\x9b\x0e.+\xa59\x1e\xcbU\xde0\x15\xb1t\xe3\x86\xa2@\xc2\xa6\x06&\xfe\x11\xce\xd5\xdc\x10AA\xe1\xe6@\x10\x85\xaa;\xc5\xfc\xda ~\xe0`\x12/\x88\x0d})\xc4\xb1T\xdd\xb1h\x1ba\x8a\xfb\xd2\xb6V\xd2\xadc\x01\xf8\x05j\xbc\x8a\x05\x0c\xf7.\xb4\x88h\xf5\xc0\xe9Vdr\\\xcclX\xd0F\xabR\x80\xe6|#\xdb:Rz\"xm\x84\xda\xb7T0\xb9\x0d-4\xf1\xecL\x81\x88w(,\x18K\x8c\xff\x8d $6\xca\x9b\x16\xaa\xb6F\xd0\xaf\x03\xe0\x9cj\x12\xce\xadgP\x10\xb0\x82\x0e\xe3/\x8c\x11\x96\xc2 RGP\x1a\x9e\xd5\xd2&\xda\x9fE\x1a\xf2zl*\xc5Jb\xe3cyT-\xc4G@(\x9e\xb1.\x1e\xd0\xec\x02\xb7:\x05\xe1\x95\xed:@q>\x85\x03=\xc0\xcc\x19B\xf5\x08\xb1\x1e\xfdv\xcdH\x0ba\x08\xadk\x05{x\x17J\x80d\x15T8f\xbb+\x0b(\x1a\xbb%0d&A\xba\x12\x01\xf5%B\x90Pm\xf1\x00\x97\x06\x82\xcb \xb4\x99\xe0m\xf1\xe8\x99\x8a\xebN\xf44\xd4\x01\x8e3-\xa4\x87kR\x13\x18\x90O0m\x8a\xb6\x8fWc\xe8V\x84~\xf5\x04\x86S>\xf2\"\x19F\xf9\x9a\xfa\x04\xb9\x97\x90\x9b\xfcN?\xa81\xceZN\xa0\xe3\x9d\xb8\xdcQm\x1b>\x80\xb5i\x1a\xc1\xd6\x00\x8a\x10(\xd7Q\x8f\xbe\x88\xf1\x0b-\xd4q]D\x95Sg\xda\xe3\nj{8\xb5\xb6bj\x03\xec\xc1\xa7\xa4W\xcb\xb9-\xe2Y\x17q\x04\xe8s\xe8\xf8\xaa\x15\xd1>\x94\x971\xc5\x05d\xddp\x9e@\x94V\x10\xdc:\xd4\x8f\xb4p\x86S\xbf\xa6\xe2\x02,/\x17+ \xf8J\xa7\xa3\xbf9\xa1\xf6]m\xa0;\xa1\x84\x1dhO\xa8F\xf7\xf6'\xe4\xfe\xd7\xee\xdc\x8a\x19}\xda\x10\x7f\x81\x8co\x9d\x8d\xa4m\x9f<\x9cb\xbf\x99\xd5&\x1eZ\xd6[E\xe8O^\x87@f\x97\x07a\xc9\x10|Gm\x8c\xe3\xbc.\xd1mQ\xde\x17\xbct-\xfa\xca,\x98`\x9c\xc5s\xf8|a\x1ck2\x0c\xd4\xcaU\x17\x16*\xb5\xa0\x95>\x10\x91y\x9a\xd7h\x87\x97\xbc\xcc\xe8\x0cM\xb3\x9c\x92\x8a\xa4\xe8\xf6N\xa9fJ*L\xcb\xca\x1f\x1f&\x83\xcf\x83\xec\x02\x11(\x01)Aa\x99\xac\x8a21\xd5\xc1Z\x0e\xa8\xa9\x9cl]#\x95< \xaf\x9cNe\xe8\x9b\xdd9\x14\xca\xac\xe8\xa2\x18\xcd\x93\x02\xcc\x8b\x00.E\x04\x9e\x06\x84:W\xe3\x9d(F>\x1dj\xcbF\xa1\xc9\x89\x0d\xfen\x86\xeb\xd9\xf8\xa42:\x18dNDa\x14mQk-\x9a/\x11\x13\x8ah\x10^\x80)\xc0\"\xe2\xccq\xfd\xac\x91\xdfa\x03\xf3\xa2\xd3\xe1\xf0#\xf6\xe8\x083\xde\xbc\xc0\x9e\xcd\xa4\x9c\xcf\xcb\x82\x8f\x17\x0e\x03\x15\xdd\xda\x9e\x85\x1dbh\x11a\xa6\xc8h\xae;i \xb9\x15V\x8f.]\x8dv\x04\xd8\xdf\x9a[V\xce\xa7}\xcd\x9a\xe0u\xabz\x98\xe8\xcd\x8a\xbb\xf26\xb0\x96\xb2b\xb1\xa4/6G\x0b\xb2\x0bzM<\xcc\x82\xb4\x9fS6\xed\xa2:\x91\xaay\xce\xbb2e\xc5-\x9a\xe0\xe4VV\x95\x07@\xe2a\x06<\xdb\x82/\x9a\xb0\x93J\x97\xf3\x8f{\xa9z\xb2\x00h\x1e\x88\x07\xbeU\x1c\xe2[*\xfa&\xec\xfb+(W\xe2KM\xf1$\xcf\xea\x19IU\x9f]\xfcq}rz\xfe\xfd\xea\xfa\xf2\xea\xf8\xea\xfbe\xaf\x144\x1f\x8c\xf3\x8b\xb3\xf3\xb3\xcb5\x00\x88w\xd1\xcfu\n\xdd\xba\x84\xf4\x97\xe4Q\xe6Af \x02\xc2\xc8\x9c\x02\xe4t\xf0\x80-\x9cg\xe9\xfe\xb2\x10\xe7E\xb1n\xd9\xda\x01|\x1c\x99J7\x8f\xd5_\xdb9^\xc6\x8e)\x10\xae&\x19\xadp\xb5j$\x18/8\xa8\xcf|b+\x0c\xc7Q\xbcsc(\xde\xb9\xf1\xcb\xc4\x86\xb6,\xb0EE\xee\xb2rY\xe7\xab\xceV7\xf2\xa2\x82\xb8JasU\xe1\xe4V8\xb5\x84\xe5\xa4O\x80D\xe9$\xe8\xa9\x0c$\xbd\xccA:v6\xa3-\x99e\xe4NtG)\x974&v\xca\x02\x84\x9d\x80\xf4b\xad\xc2g\xd6\xf5\xff#\xf5Y\x0cR.\x8f\x99\xc3\xe0\x009^\x06\x83\xb4\xaf\x9f2\x7f\xc1\xa6g\x04\xcb\x02(:\xa0\xfcF\x1d\x1c#\xa7\x93\xf0>\xe8\x97\xb9\xd0\x9a\xec\xb8J\xda\x16\x1e\xad\xad\x88lp`\x0em$\xf0|\xf3\xdc\xdaT\xbe\xc2@.\xbe\x94#\x90\x03_\xc79(\x08i\x04\x86\xc5\xf49\x90MC\xe9\x1fE\x93C\xe9\xee\xa4B\x8cyFr\x02\xf7\x9e\x95@\xa0\xb8\xc0EY\xea?3EI\x06i:\x04\xd4v\xa8g\xc6\x1e\x82\xaf\x1f\x04\xdfj\xa8\xe7\xc4\xa0\x1e\x19|`@}u!\x1a\x96\xc9\x87\xb6\x96\x831\x1d\x19\x05H{\xe5d!X&\xd3\x0b\xe5\xe50\x0d\xda\x05\xe5\xd0\xa2=!\xf4\x9b\x83\x98\xf2@\xfdX\xbe\x1e\x1f\xa3\xaa$\n\xafw\x9e$\xea\x9d-\x88\x9e\x92##\x1c\x16\x01Kuh\xf6\xa0\xb8\x16\xf6'\x0f\xca\xe3\xdc&r\x07\x9d\x18\x89\xf1\x02\xf9\x84\xea\x07\x12U\xad\x8ck\x17\x1buz\xe2N6U}\xa2I\xfa[7 \x91\xff\xa5\xbbdZwK\xfcW\"$/)\x17\xab\xc6\xe2\x13\x7f\xb0S\x9d\x04R\xbe\xbc\xc7\x88I\x106\x04\xc2~\xd3\xe8\xca\x8e\xcf\x022\x06A .\x10\xc9x\x11\xee A\x98/\xe2]D^\xdf\xf8\xb6\xe4\xa7\x83O\xbf\x7f\x9c\xe0\xb7{\x87\xd3w\x87{\xef\x0f?\xe1\xbd\x8f\x1f\xf0\xef{S\x92\xe0\x83\xc9\x9b\xc3\x83\xb7\xe4\x8d\xa8\xda\xae$D\xe2o''\x95\xba\x1c0\x84\xeb\xc1\xcf\xc7\x1b\xf2\xe6\x11?\xd2\xe5\x87C\xfap\xf8p\x98\xe7w\x87\x0f\xc9\xa7GZ\xff|\xc8o\xefI\xee\xc28\x96\x8a8\x16;\x9b\"s\xbd\x19z\xf8\xf1\xcd\xbb\xe9\xc7I\xb2\xf7\xe1\xcd\x87\xdf\xf7\xde\x93\xc9\xe1\xde\xa7\xc3\x83\xe9\xde\xdb\x83\xb7\x07\x1f~?H\xde\x92\xa4\xc5P1\xd8Z,\x15 \x0e~>x\x99\xfa\xa9\xfe\x99'\xb3w\xf5\xc3}\xf1\xfe\xfd\x8f\xc37?\x1eo\xe8\xc7\xaa\x9e\xdd\xfd\\M\xab\x1fI\xe5#\x87w fL(\x8b|\xd5\xb0\x00e<\x05\xcep\xc4\xe3\xbc.}\xf8\xc9\x06\x11N\xc1\x17\xbdg\x1aiN[\xb7J\x9a\xfdJhZ|\x0e\x818\xf8\xf9\xd6\xcb\xe5\xfb\xf7o\xd3\x9fo\x7f\xa4w\xf3\x14?.\xef\x1f\x13\x9c\xa6\xb3\xd9\xc7\x9b\xf9\xf2\xdd\xf0UTWK%+\xe8\xbb\xb7\x1d\xfex\xaa\x91\x07pH \xc5Y\xbe%\x95C\xd8\xe7\xd7\xcb\xca\xdb4'\xaaoy\xc2\xed\xe0\xaf\x9d\x8e\x92\x05\xae\xf0\x9cPR\x198\xef\x89\x1ccme\xf9\x16`kC\xf4=\x0b\x0c;\x01\x84\x0c\xd3!\x16~V\x1c\xa1\x05\xa6f\xec(\x93\x0cYE\xd2#D\xab\xa5\xb9\x95\x9c,\xd6\xdc\xd2\xb6;\x94_\xbd\x8d\xfd\xa1&\xbe\xe6\x99\x05\xcd\xc1\xbf\xb1\xcd\xf95\x8cx\x97\xe9\xce\xe6\xea'\x13\xa3\xce\xc9\x9a\xe2\xbc\x06\xcf\x96a\x95\x03\xa7k\xb0\x1d?\xae\xf5>>\x0fZwW\x00\x1e\xf47\xe5\x0d\xa3\xdd9\xcb]\x03~D:-\xa3\x1bH\xe9PC\xdd0\xc9-xp+t\x08\xe1]\xcb\xcags\xb5y\"\x0d\xe5\x9e\\\xe9c\xa1\x1b\xb6\xf8Vs\xa4ed\x0392\xd04o\x8c\xf06O\x9e\x91\x03-\xeb\x1a\xc8\x81A6\xb9a}[\xc0<\x96\xf8&\xe9\xa7\xf8\xc6\xb2}\xfe)\xc7\xd9g\xb2\x89\x14\xb8H\xc8\xfe\x9cP\x9cb\x8a\xf7\xef\x0e\xf6\xf96\xaf\xf7q.\xed\xb8\x1b\xa2M\xf4z9\x9f\xe3ju$\xfa\x83\xd4\xc7y\x8e*B\xab\x8c\xdc\x11\xa6\xe2r!\"\xb4\x9bW\xd7\xbe9I\x8dO\xfe\xa6\xc8\x13\xcb\xc0\xc0\xed\xd5\xdb7o^\xf9\x8f\x03F\xf2\x9cs\x11\x8dt\x10\x104l\x87E\x1d\xf0\xeaDa#\x00|\x14\xf5\xa3\x8b\x07`{#\xf8Ee<7\x8a=\xdfE\xa5\xbd\x93?\x90N\x94\x16J\x14\xa1\x93\xf9\"\xe7\x0d\xa1jT\xa7\xb7\xaf\x8f#\xb9?\x88\xef0J\xaa)N\xc4y\x95\x17\xa1\x13mp\xc4\xb7l\xfdVD\xef\xb9\xf6\xb6m?\x9f\xcbz^\xfa\x07\xac\x9f-a\x0e\xc6\xdaF\x9b\xd7\xdd|7}\xfd\x15\xbb\x94\xb3\xbe\x15\xd7]<5\x9e\xa8\xb9\xc1y^\xde\xcb\"\x072\xd50\x0c0\xe0 D\xa2\x05\xf1\x8b\xad_\x00\xac\xb0\x07^\x0f\xf2\xc7b\xba\xb1\xbb\xb3\xdc\x8e\xcaJ\x8d\x15\x11\xaa\xca\x1cR?\x08\xb4\xb6\xc4\x839\xccf\xe7*\x94\x8c\xba\x99\x89\xac( \x08\xd0@\xca\x19\x9e\x88\xad\x1a\xad\xa4\x87\xfa2\x12^\x15``\x13\xf8v[j\xdd1\xbd\xef\x97\xaa z\xdf\xefT3\xf3\xbe\xdf\xe9\xfe\xe4}?\xe4=\xc5\xfb~\xa4\xfb\x81\xf7\xfdP\xf6\xf4\xee\xfbY\xd3z\xbb\xef\x97M;\xec\xbe_\xea\xb6\xd5\xd1\x0fu\x89\x86\x81\x8b\x0e\xe6t6\x1f\xdf\xfa\x86\xec\x0d\xf7\x97H\xf6\xe6\xe6\xee\xdb\x9e\x05\x17\x9c\x1b\xe6\xc8\xfdZ\x8e#{\x8eW\xd9MV\xe0!]\xfa\xd5\xfe:r\xbdD\xba#\x7fJ&T6\xe3\xcf\x8a\x1b4]\x16\xbc\xd9z\xac`@w<\xb5/\x8f\\/\xc5a\x83GSd \xca\x8a;R\x0f\xa1I\xef\xe1#\xe7[d\xb5O\xe7F\x80\xa63Y\xd6\xb4L3\\Hb\x89P\xf4\xa2\x9bzoD\xb8L8\xea\xbciW}Z\x18\xdd\xc7\xb9\xdaW\x9a\x8d\xe9\xee\xde\x83j\x99r\xe4|+x,\x86\x14\x1d\xa0\x0b\x84\xa7\xd3,\xe7\x8d\xb7\xf1ME\xb89\xd3{X)\x91\x8e\x1c\xef\xc4\x90\xdc~\xc2\xb9\xb8E\x91m\x1eW\x8d\x0d&\xba\xe9\x17\xa8\x9c\x17\xd9dY\xa3 .n\x95\x1e\xed\x8dL#\xe7\x8e\xdc\xaf\x19J\xaa\xca\x81\x9a\x0ds2*\xb2\xa8H\xcd\xcd:\x1e\xd5\xa6+l \x7fl\xab\xca+N\x06m\xf2F\xa6\x1e\xb9_#G\xa3\xff\x86WM\xd7o)\x05t \xca\x8c R\x944Rr\xd6\x85\x92\x16\xd6G\xce\xb7.\x84xm7\xbe\x98E\xdd\x0d\xb1W %B\x10?4S\xde\x146.q\xe3\x17\x93\xe6\x03\xb9\xa44\x1fi\xedi\xff\x86^\n\xafj\xdeU\x1f\x8b\x9e\xb0\xb5F8\x08\x11j;\x1e\xa3sc\xef+\xbb\x96m\xcb}^2Wp\x94\xd9\x96\x11@\xdd\xb6\xeeI\xb8\x0e\x06\xea\xa9/\xaf\xb8{O\xb6fm\xb6\x07\xa2\xe5\x02\xe5\xe4\x8e\xe4\xf2\xc0\x02 oT\x1eDa\x1d\xbfn\x03\xe7u\xe9\x19\xcf\x11.\xc2\xd5\xc8\xa5\xd7\xa96\x9aJ\xaf\xfc=a\x914\xac\xf9\xa9\xcdQ\xd1^`a\x9f\x90#\xebwY\xf0bC\x8c\xe3\n\xfb<\xab\xa9\xa8\x90c\xf8\xcb\x03\xf1\xe9\x0c\x9bk\x9cD\xeb\xbc\x8f{\xd8\x8b\x9e\x13\xfa\xac\x8dc\xcd2Ua\x871\x81\xf3\x0f/\xe9\xac\xac\xb2G!i+\x92\x90\xec.\xbc8\x9a\xba\xb2|\x9e\xbaE\xba\xb5c\xc4\x0b\x86\xdf\xab^\xf3\xf5x\x0d8\x88n\x82#\xcdf\xb6\x15\x1c[\x13\xa2\xcer\x8b\xb0\xc8\xaa5\xbcA\x97\x14\x17)\xaeRS)I=)\xfaK\xcfqu\x1b\xe9n\xae~\x1f\xde,l\x02\xeb\xe5bQVV\xfd>\x8e\xffky\x1b\x88)\xad\xb2\xc9\x92\x124\x0f\xaeL\xa4\xef\x0c\x93\x19.nH\x8a&+\xce\x0d\xa9K\x9a*Me\x910\xcb\x07\xc5\xbc'\xc4\x11k\xac\x1e\xe9\xd8\xba\xe6\x12\xfc\xba*\xf3|\xb9\x88/\x81\xb8\xae\x80k\x95>\xab\xe5/\xa9tp\x9ek1\xa2v\x92\xbc$/R\x94\xd1Z\x8b\x93 8\xd9\xd7\x03)\x11m\x81zU+A=\xcdH\x9e\x06\xfav#u\xe1\x8b\xf3\xbaD\xa4\xc0\x93\\\xf8\"\x98\x88WZ\xf0\xef\xb4Z*\xe7\x9a\x80\x1b^QE\xd3\x1cFR\xea\x9bD(\x03E\xefn\xdd\xdd\x1cUeI\x8d\xe2\xa9\xdczGI\x99\xe7\x84\x1bi!\xff\x8b\xbe\xd9\xba/\xc4\xfa,\x0b^\x0c\x9f7\x0d\x0f\xa2\xab\x9c\xca\xa3\xa4j[\xc0\x1a\xff\xe4\xba\xb5\xa0\x82B\x13\xe0\x02\x848\xff\x9e\xde\xb9n1\xd5\x0cA\xa0k\x14\xf4\xd03\x10\xcf\x13\xdb 5\x11\xa9\xd1B\x12\x0d\xce\x99V\x80\x00\xfc\x10\xff\x88\xa5\xdal\x01O\x1aDQ\xa8H\xc7h\xbc\x89\xe5c\x01Y\xd2\x9f\xd6h\xee\xd5\x98T\x8e4\xf1\xfd\xa9\\3\x9f\xca\xb1,Br\x1c\x92\xa4\x0c\xa5\xa1\x0b1\"\xd1\xbd\x90\xbae7\x1cwZO\"\xdf\x01 \xc8\xc0\x95\xb01\x11\x10\xc96\x8e\xdc\xbd\xa1\xf6u\x1fTe\xc0\xf3\x8b\xb7\x84A#)\x8fh\xbe\xa6=\xa8\xf1\xbf\x91D\xca\xc6\xf9\xd4B\xd9\xd6*\xfau\x10`\xabfD\x7f\xce=\x9b\x92\x81e\xf9nf\xb9\x8c\xb4@\x06\x12=\x8a\xe2\xf1\xac!\x17\xf9\x16\x9a\xf1.\x9f\x89y8t\xc3\xec\x1a\xc8\xc2\xb3\xc5(\xd2\xfd:\x133\x8eI=/-C\xf6W\x9e\x8f\xfe\xe5\x7fx\x9e\x8fx\x16\x98_\x8a2\xd4\xd6\xc0\xbd\x81\xa2\x9d\x0c\xca\xf9a\xfe\xadI\xbc\x90\x8e2\x9f`\x90\xf8\xbbe_\xd4L\x8b\x1bi\xb7d\xe5\x17\x97 a \xd2\xa50\xe6!\x81\x90\x98p\xe1\xf7\xac\x08]V2\x85\xff\x1c\xdf\x10\x95m\xf5\xba \x0f\xf4\x9a\xfd\x98\x96\x01h\x13r\x93\x15!\xa7\x15\x8f+UE\xe3\x19L6K\x04\xcd\xcb\x9a\"2\x9dfIF\n\x9a\xaf^\xa3\xb3\"\x97\x1e\x9ePDN9\x9d\x8a\x1bvFGh\xdczV.\xf3\x14MH\xc83)\xa0\x8d4?\xcb\xac\xa0\x1f\xde\x8f0C\x92F>I\xc5r\xce/\xdf\xe5;\xe1\xbd\xc6\x05\xa3Kt\xf0\x99\x91\x82\xb3\xc2\x0f.\xab\xd1\xb2\xc0w8\xcb\xf1$'A\x0f\xe3 \x1f5\xe7mX\xd4\xdc\xb01\x0b\xb4\xe4\xbd\x1an\xc9\xb0\x89\x92\xb3\x11\x1a:\xc4\xcb\xbe\xce\x80\xee\x91\xdf\x02\xe7<\xfe\xf7?\xf0\xdb\xa7\x17\x0bX\xf7\x88?\xe8Po\x1c\xdd-`\xeec|\xf7\xe0>db\xd7\x12\xe2\xf2\x10\x0e\x9c\xd6!GvWM\x94\xd8!=z,\xdf$\x9f|\xd5\x12^\x85\xcb%,H\xb2\xff\xafv\xda\xf7\xbfe1\x03W\x05\x05\x83\xb7\xfc\x10piEW\x08\xf6\xd5\xaa\x88P+\xf6B\x9d!x2\x84\x06c\xff\xa6\xb9\xf9\xf4M\xe9\xd5\x8ct{\xa6\x02\n5\xa5\xc9\xc7w\x04\xff\xfef\x8f\xe0$\xdd{\xfffJ\xf6>\xe1t\xbaw\x90L?\x1c\xbc\xff8\x99~\xc4o\x1d\x85m=x\xba\xcb\ni\xa6\x1e\xfc,\xeeo>~(jL\x1f\x0e\x17\xf9\xe1\x87\xf9\xf2\xfe\xcd\xdd\xa7\x15\xcd\x1f\xdf\xfd\\\xbe\xfb\xf1\xf0an.\x9f\x7f\xac\x94D\xdd\xe5\x1cj\x12\x92\nU4\xb9\x9d\xb6.\x0e\xc9TI\x193\x00\xf8\x92\xe8\xd7v\xb1i\x1eV\xbe\xcf+\xfe\x98\xf5b\xe4\xdf\xd4\xc60N\xfb\xca\xb23\xf1\xd9\xe7\xe73\x07J\x8a\x0d\xdd\xf2\x19\xd6\"\x91\xbf\xda\xee:\x1a\xd7\x16i\x11_\x94\xe3\x0b\xa5\x91\xad\x8b\xf9X\x84\xd7\x1a\xbe\xa5\x08\xba(\x06\x1dEG@\xedQ\x82\x81c\x80S\x1c\xc0\xa3\x85\x1a7\xccRT\xd5h\xea\x94\xa3\xa6\xba\x86\xc5\xf8H\xba\x9a9q\xde1\xad\xd9\xfdo\x1e\xf1\x90;\x83\xfct\\\x7fdf\xd5\x13\x9d\x03\x04\x9a\x07\xf6\xc4\x9a\xf3\x00\xe7\x00u\xb7\xd9)\x9e\x1b\x15\x10\xe8,+\xbc\x05\x90\xdb_o\x04\x1f\xe3\xbf\xcae\x1b\xc7\xea\x9eL\xea\x8c\x06\xfd\x14\xe2\x19\x86\xd5\xf7\x8bo\xba\x9c\x1a\xb9#\x85pV\xb2%\x12B*K\xcab\x93\x18\x95S\x84\x0b>\x8a\xf7\xde\xda\xcc\xa5\n-\xb0\x90cI=\xd1$5\x10%\xf6l\xeb\x0c4i \x9b\xc9\x88\xfdv\x9b\xcc\xf3\xb8\xce\x8a\xbb2\xbf#Qa\xf5\x14\xb4B\nv\x0c*\xd51\xa4HG\xff\xf2\x1c\xfd\x0bs\x0c(\xc9\xd1\xb3\x18\xc7\x802\x1c\xbd\x0bp\x0c)\xbd1\xa4\xe8F\xefr\x1b\xc3\x0bm\xc4/\xd3\x9a\xc7[\\\xc3\xf3>\x96\xf1\xd7\xb7\xac\xc6\x13\x14\xd4x\xbaR\x1a\x9b-\xa2\xb1\x05\xe53\x9e\xb8p\xc6\x93\x97\xcc\xd8\x92b\x19\xdb^&c\xab\nd<_i\x8c\xd8-\xb2xx\xf1\x88+\xb6DTDw\x9aMy\xe6+\xe5E#j\x9dN\xcc\x0e\x1fAPz\x92\xe6x\xc5\xa6\xdco\xf5\x000;\x16\x05\x00\xe4\xba\xcd\x88*\xd0\xd0JLfHI\xd3l\x07\x17\xa1x\x17:#Ye\xa6\xccs\xea|\xb3\xd7n\x9d\xb5\x15\xf6*\xf0\xdc\nW\xb0\x0d\x93\x13wk\xc4,mj\x12r)\x1c\x00&fa\x82kU\xba\xa2}@\xf6\xc4+G\x16\x83\xc3\xd7\xa7\x82\xa8U\x8a\x00\xaf?)\x97\xc9\xae\x07LE\xearY%\xa4\xdemt\x7f\xbd\xcb\xbd\\IY\xd4Y*=G\xa8\\\xd2\xc5\x92\xd6Ar\x83\x85Ql\x07\x8d\xcb5S\x16\xb2<\x85\xcbU)/\xe9]\xcc\xb2SA\xfc \n\xb0\x05\xd0\x856,\xadp\x8d\xa4\xc2\xa8S\"\xee\x90\xb0\xa8\x88m\xd21\xbcB0\xe6\xa2\xd1\xd2\x08\x9d\xce_\xdf\xc9\xd3\x9e\xd3X\n\xe1\xb33e\xcd\xd4AP&X0\x7fj\x9by\xe3I!k\xbd\x0e\x80\x1b\x92,h\xfc(\x9c*\x08\xe2\xcf \xd2GH\x12\xecK\xea\x08Ka\x10\xa9k\xa5\x06\x06WK\x9bh\xfb2\xa6Kk\xc8\x88\xd9T4W\x1d\xbf9\x88\x8e\x81\x00\xe3\xa0^\xf7\x07\xc0\xf9\x06n\xfe\xd1o\x11\xc0^\xef\x98]%\x9e?\x0c\x0f\xb7\xa9\xf45r\x80\x1ap-\xeeF9\x1b\xad\x12\x0d\x99P\x04\xba\x88\x00O&Z\xfb2\x02<1h-\xac\x06]I\xf4\xb8\x94\x18\x8e\xdb\xb0\x8b \xe8\xd5\xc4zx\xc5\xaf':\x9f\xf5\xd88\xb32Ok\x99\x9fl\xdf\xd6\xf13kV\xa3\x19.\xd2pi3Z6gSU\x7f\xb1\xa6\xd52\xa1\xcb*P\xa7\x0e|\xab\x02;\xa7\x02N\xaa\xe0\x89\x90\x02\xe8\xd8]i\x9ds\xa6\xbc/\xea\x96\x0c\xcch@\x82\xc0/V\x9e\x9e\\\xc8\x05\xcb\xc0+\x96a\x97,C\xaeY\x86\\\xb4\x0c\xbaj\xe9}\xd92\xe8\xbae\xc0\x85\xcb\xb0+\x97a\x97.\x03\xae]\xd6\xb9x\xe9'\xe0\xfa_\xbeD\xc0\x0d\xaaj\xfe\x04\x170Oy\x05\xb3\xe9K\x98\xad\xb8\x86y\xf2\x8b\x98g\xb8\x8a\xd9\x9a\xcb\x98\xed\xbf\x8e\xd9\xb2\x0b\x99\xe7\xbc\x92\x81\x9e\xcaF\xbd\x96\xe9q1\x03\xc5\x0f[\x973+\x89\x91uA3]\xe6\xf94\xcbs\x19\xfb\x1d\x04Wg7\xbca9F\xb4\xc2E-\x16\xd5F\x8e\xa0\xea\x16\xe09<\x00\x11\x9er\xe3\xb8\xb1\x95\xb1\x8at\xd5\x0b\x9c\xb3\xb7\xd9\x9e\x11`\x10\xbe\xcdp=\x1b\x81\x0d\x92>F\x00\x83h]'M\xb2\x02W+\xb4\xd3\x94\xe4\xce\x8a\x9a\xe2\" l\xd3$\xc7u}\x1d;\xdc\xf7C\xce\xecI\xca\xe1\xef+Il\xb1\n\x91\x07\x92,)\x9ex\xab\xe5\xc7'\xf3\xb3\x84\xb5\xfe\x9d\xd5\xc8\xb7V\x91{+\xb8yX\x07/\xb5\xdcW\x89^`\x81\xfb\xae\xee5\xec(\xf54\x9d@\x87\xdd}\xb9n\xbf\xdc\xf4\xfb\x8e\xf0\x00\x97\x18\xc4\x1d\xd6\xa6i\x84\x9d\x03\x94nP\xae#\x07\x96\xdeK\xb1\xa8\xe8B}o\xc6\x1c\xd3\x1e\xbb\x1e\xdb&N\xadyS\xd6\x05\xd8\x83O\xf6\x8b\x91\x8a)>\x05\xcf\xba\x88K\xfe\xf5\x00\xe3\xa8\xf6<\x98\x97\xcfP|\xb3\x8b@\xecj-\x08n\x1d\xeaGZ8\xc3\xa9_\xeb\xb6\x0d\xb4\xbc\\\xac\x80\xe0\xeb4\x19\xfc5;\x83\xd2\xb1%\x15\x8b\x14\xd5\xe5\x9c \xf2@+l\\? S!s\x94A\x8a#l\x11.Z~\x17+#\x95T\xa5\x1e\x05\xf0\xb4\xb0l\xb7\xaa\xb0I\xd8\xe1\xe9\x92<\xb5\x9f\xa4\xbf\xb5\xf15\xf3\xc8~]k\x8e\xb0\xbf\x80\x82y\xf4k\xcdq\xed\xfd\x88`8\x95\xc7\x81\x0bq\xc6\xe2G+U((\xa9\x08\xd6)\xcb\x81*\xa2\xe2\xe9\xd8\xc2\xc1N\xabY\xc1\xac\xf48\x99\xa3][D\xefZa\xebL<\xb1)\x12\x0fp\xa2\xe4\x8f\x8d\xe3\x99^9\x9cK\x91O\xd9(\xd13b\xf3\xd3\xbe\x08E\x1d4\x88\xbbA4\x16h\x87\xae\x16Y\x82\xf3|\x850c)-\xe5\x993\xea\xb1\xb5N\xbc1\xe7\x95\x14w\xe1\x9d.\x9e^d\x03w\xbdx\xfa\xf0(\xea\xd6\xd8\xf9\nb\xd1\x97\x9a\x9d\xca\xb3zFR\xb9k\xbd\xb1\xbb\xea\x89\xb97\xc4\xd3\x8bM})W\xfe\x10\\\x18M\xc9\x16\x19I\xb8\xf41N\x9a\x00x\x8cQ\xe8\xbc*\x17e\x0d\xe5\x01\x14\xdd\x13\xb6\xe1\xdc\xfe\n\xcc\x97\xf1n\xb3\xd2\xb9\xf7!\x02O8-\x8c\x16J;2C\xbe\xe4n\xe4r:\x95]\xb6\xc3\x85z\x91\xb8\xee\xca\n\xc2o\xef\xb9T\x88\xfc\\WcZ[E\x1c\xf3\x92\x0d|\x96\xb8g\xc5\xf2n\xd6\x98f5\xd3w\xdc\x8f\x8e\x17\x8bvY\xdc\x16\xe5}\xb1\x9f\x15\xfcrI\xd45\n\x8b:\xff\x0cv\x19\xa6\xfeb\"\xc4\xed>\xaeJ\x9ar\x8e\xec\x7f\xbc\x0e\xe6\x8e\xd6\xd3*\x82\xa9(\xa9v\xf1FS\x9e\xdc\xf3\xdd\xc5K\xee\x1c\xf3&X\xf5\xb6T\x92\x16\x1bZ\x84\x97|j\xd9\x0f\xc3P\xe1K\xcf\x87\x0f\xff\xa3\x1b)\xbcr\xa1d\xb5\x06\x14?m\xae\x03\xd1\x0c\xdf\xc5L\x0b\x1e\x9e\xcd\x84,\x93\x83!1\xb8(\x8b:\x9b\xe4\xa2+e\xc4b\x1f\xf9$\x11\x15W\x10\x81\xf5+\x00\xca\xf3\xd1\xaf\x00\xa8_\x01P\xce\xe7W\x00\xd4\xaf\x00\xa8_\x01PpD~\x05@E}H\xbf\x02\xa0BV\x93\xc0\xef\xcaZ\xa6\x86\xe5\xd5>0z@\xc5\xc9ty\x03\xcc \x11\xfb \x1a\x8e\xeb\x90nlQ3\x8fGR\x89\x9c\xed}\x1di\"\x9c\x18\xfb2L\xc4\x03\x08\xaa\x1d;>o+\xfe\xc3u\x90\xf6\x82\nF\x7f\x18w;c\xc5~8@\x8e\x17\xf9\xe1\xa2|\xc3q\x1f6=\xf1\xe3@\xd4\x8a\x07:\x82\xa1\xfcF\x1d\x1c\xbd1\x1f\x00\x17R\xdd/\xe2\xa35\xd9\xdb\x1c\xef\xe1Dux\xb4\x87\x0d\x0e\xcc\xa1\x8d\\\xd8o\x9e[\x9b\x8a\xf3\x18\xc8\xc5\x19\xaeg$\x1d\xcbS\xbby\xf6\x19\xf8\xaa\x15'^\x85\xdd\x11\xa8\x9bb>\x84a\xcf\x10\x14\xd3\x1e>\x16\x12\x13]JP\xba;!$!\x95\x86zR\xe5\x04\x8e\xb2\xbej\xcd\x04\xc5\x05.\xca\xd2\xc6\xe1e\x94p\x87\x91\x0c\xd2t\x08\xa8\xedP\xcfHG\x04_?\x08\xbe\xd5P\xcf\x89A=\"\x1f\xc1\x80\xfa\xeaB4,\x02\x12m-\x07c:2\n\xb0_,\x1b\x82E\x80\xbdP^\x0e\xd3\xa0]P\x0e-\xda\x13B\xbf9\x88)\x0f\xd4\x8f\xe5\xeb\xf11\xaaJ\xa2\xf0z\xc7\x97\xa2\xdeQ\x96\xe8)9\xb2f\xc4e\x17\xe0\xa8Q\x97\x8e\x13\xb8?\xe62p:\xa97\x1fqiU\xcb\xf7\xc6[:k\xe3\xbb\xdb\xb7\x0e\x8e\xb6|Y\xfd\xdckP|dt?\xc4'\x089\x06\x83\xb4\x86\xb0\x9f\x81\x8d\"\xdc\xc0,t|q3\xeef\x12\xf6\xc3\x07\xa9\xa1\xad%\xba\xdf\xbb\x1bB\x84\xa6\xe2i[\xca'\xddp\xe6P\xb31\x0f4\xeaM\xfd \xd5\xc5C\xfeN\xe4\x14\xdc\xa2? D@\xa3gbjGbE\xfa\xb7\xf9\xd8\xea\x96iA\x86rh^p~\x86\xb6\xfa\xee=?/\x076\x02\xf4\xce\x8cl\x0f\xd8cey\xa4=zbF\x0c\xea\x07\xe8]\x01\xaaK\xa0\xaf3`\x0c\xabnERP\x03\xd3\x16\x14\xa3\x9d\xa9} pq\xfe\xf9W'\xd3_\x9dL#&\xc9\x13w2m\xdb6\xbe\x85\xd8\xda,CM\xa2a\x86P\xc8\xe4\xb1\x0c\x9d\xc1&MV\x1c\xa1\x05\xa63\xe3U\xd3F\x8dVV\xcc\x9es\x12\xda}Qm\xdb\x03\xc8\xd5\x81\x86\x8be\xa2\xf8\xa7\xa9\x97\xb9\xe2S%\x9bl8\xd7\xe5\xa2il\xf4\xe4ao;\xc5u\x12\xec\xc3\xbf\xaeu\xf2|\xfc\xfb\xd5\x9f\xf8?\xb3?\xb1\xafec\xb4cc\xbd\x8fs\xa9\xb3\\-\x1a\xbb\xd6\x0c\xef\xd4^\x11Ze\xe4\x8e\xf0Z\xf2\xaem\x01m\xe0\xc7\xa0\xfdM\xb1\xe2\xe54\xf1\xdb\x12\x9b\xc3\xc2)l:\xac\x1d\x97\x00s\xbc\x88\x07`\xad \xb8?|\xf4\xc4Ts\x15\x05\xc6\xb5\x16\xdb\x98\x9d\xfb@s\x82\x80\xf3\x82\x80i\x9c\xc09A\xdd]\xf6\xabln?\xdc^z\xd9\xdc-\xae@k\xaf\x81\xf1\xfa\xfb\xfd*D\xdb\x8e;\xef\xf3\xd5\xaf<\x8c_y\x180\xb7\x9fz~\xe5at^\xfe\xca\xc3\xf8\x95\x87\xe1\x1a\xf2W\x1e\x06\x18\xc3_y\x18/ \x0f#\x82_\xef.\x81Ah;\xb8\xe0\xc1\x08=\xfa\x04v\x03T\xb6\xca\x02\x06\x9f\x9a\xfb(\xe4Q\xbb\x06\x0e\xee\x1b\x08Y\"#\xf5\x0e|\x99uX]W\">H\xc14\x1c\xbb\xd1\xd4\x18Y8]\x88M\xc4U\xbfh\xe55\x9a\x0f\x82\xfc*\x10\x9f\x8aEM|\xf7G7\xed\x06\xb6\xac\x85\xa2/\xf6\x18 !\xed\x15\x17\x0d:\xb6\xe79\x1em\xbc%\x0c\x8a\x85\x16\x07Wd\x07\x1a\x94=\xe0~t\xdb\xc3\xa7\x16\xca\xa8w\x8b\xc2aM\n{\xb4)\x04\xf3j \x13\xa21\xbf\x9bY.#-\x90\x81D\xaf\x19\xd6\x1b\\C.\xf2!hvU\xbd?\x96\xd7/\xe7Z\xf2m\xcdP^\xd7U\x906\xa3U\xd0n\xe8:L=/-\xec\xf6W$\x9d\xfe\xe5\x7fx$\x9dx\x16\x98;\xc0\xbc7<0\xdc\x1b(\xda\x8a/\xf9787\xff\xd6\x841y\"\xef\xd4#\xf1w\x0b\xdc\xa8m\x18\xb7\x0co\xc9\xca/\xa3A\x12\x1a\xa4\xc0a\xccC\x02!1\xe1\xa2\x9aUE\xe8\xb2*DJ\xdf9\xbe!*\x8a\xf1uA\x1e\xe85\xfbq\xb0:\xe3\x84\xdc\xf8\x83\x97\xd9\xc3\xa3\x17\x98N`\xbb\x84\xc1d\xb3D\xd0\xbc\xac)\"\xd3i\x96d\xa4\xa0\xf9\xea5:+\xf2\x15*\x0b\x12\xce\xda*\xa7S\xe1Met\x84\xc6\xadg\xe52Oy\x99F\xe2\x99]\x05m\xa4\xf9Yf\x05\xfd\xf0~\x84\x19\x924\xf2I*\x96s\xeep\x95\xef\x84\x8b\x0e\x17\x8c.\xee7\xe3\xf5\x89\x19+\xfc\xe0\xb2\x1a-\x0b|\x87\xb3\x1cOr\xf2:\xc4\xb4\x13>j\xce\x0eRzn\xd8\x98\x05Z2\xad\xc9\x06\x1a4Qr6BC\x87'*\xcf\xe6\xd9\xd6\xcd\x13GJ\x99@\xb4\xa487\xd2q\xa4FeRtb\xed\xb2\x00@\xa9H\x96\xb9\xd8$\xe1\xa9\x9a\xa2\x9cL)\"\xf3\x05]\xa1LV\xae\x96\xd71\xa2\"\x9d\xd8\xe0\x02\x016\x17\x93\xd0*!8\x99!\xbcX\xf8f\x80;\x7f\xaf9\x95\xb1y\x08)(\x04Td\xa8\xd7L\x18\xc8\xb1\xf9\xe0;\xa5\xe4\xa1\x98\xa8\xe5Y\xc6\xb1\xbcg\xc9\x7f\x06B\x8a\x10\x95\xf1\x11\xf8\x08\x0b\x0c\x94\xa9\xd5^\x0b\xdc\x95\x87\xf4\x16d\x0c\x08\x003\xb4XV\xa0\xef'up\x1d\xb4H/\xd9\xe6\xacx\x98\x96._\xde\xc8\x13&2^\xa3\x93\x90\x13?\xabQvS\x94\x95)[\xc2?\x0fl\xdb\x8a\xb0\x03P \xce\xe4\xb9\x16\x8cD\xac\xbdX\xb2f\xdfr\x0b\xdd\xda\xbb!.\x88tx6>)x\xed\x9d\xb2JI\xf5\xfao!\xd6]fEB\x8ePR\xd6\xf3\xb2\xde\xab\xd3[\xf4\xe6\xf5\xfbw\xce\x0fbNN\xa1\xb8\xb51'\xf0&\xf3 IS\xa1\xd9o.\xce?kSM\xfa\xf9\xea\xc0:\xd4\xd2\xdfG@\xb3H_\xa3/\x1dwN\xc8\xd4\x8bO\x92i\xc6\xa9\x0b@\xee\x8217F\xe3\x1f\x94\xb1\x1b\x0e@~\xa3|\x8dc\x8d\xb2\x8bBf|@\x13E\xad9\x8b?\xff\xcf7\xe3\xda:\x93\xeaG\x18j\xfc\xba\x03\xd7\xf2B\xcdX\x15\xaf\x83\x86\x1c\xb7\xd1\xa2\x06\xdaI\xd3\x1dA*\x1d\x9f\xfe\xa7\xbc|,\xdbAE)\"\x99\xbc\xe7K\x84\x02*\x05\xcc\xcc\x80J\x8fm\x1d-9}\n\xbc\x11\xdb^zMF\x1b\xf2\xd8\xb7w\xee1\x97:\xbb(\xa3\xb5Qw\xb7\x10\xe7\x99\x14\x95\x8c\x7f\xf7Y\xebf,\xbco\xdca\xb2c\xa6$\xf1 \xde_iI\xe2\xf9\x95\x96\xb4EiI\xbf\x12\x18\xfe3\x13\x18\x1a\xfa\x0dk\xc4>\x0b\x87\x94\xe9\x00G\x88tw\x98P\x86\xf87Z^\x0c\x13\x9c\xd7m1\x84\x91\x9dM\xe64>\x1cL\x14\xf8\x01\x97\xd1\x10wE\xd8)\xd1\xd7\x0d\x11\xe5\xa7\x0d~\xc3\xccm\x19#\x0e\xf6r\xc7\x01\x90\xbb\xc3\x9c\x0c\x86+\xc1\x82\xe6p+\x0cr$8\xdd\x05O\xccE\xd3\xd2j\xbe\x0f\xf1\xb2\xaf\x9b\xa0\xeb\x0c\xb0\xc09\x1d\x03\xfd]\x01\xf6\xb9\xc6\x02\xd6=\xfc\x0f:\xee\x1b\x87z\x0b\x98\xfb\x80\xdf=\xd2\x0f\x99\xd8\xb5\x84\xb8<\x9e\x03\xa7u\xc8a^\xe5`\x9apb\xc7\xf7\xe8\x81}\x93|\xf2e\xeb\xbd\xf2\xa6\xeb\x91\xba\xce\xcab\xff_\xf2\x1f\xfc\x96\xfa\xdf\"\x8bO&\xc8y\xb3\xf7PMp\x95\xccT\x94\xad\xb8\x9eT+\xc27\x15W:\xee&Kw\x19\xf7\xe5\xe1=\xdd\x15\xa1xM\xc6\xb7\xf8\x95#\xeb\xfb\xd3\xc1\xa7\xdf?N\xf0\xdb\xbd\xc3\xe9\xbb\xc3\xbd\xf7\x87\x9f\xf0\xde\xc7\x0f\xf8\xf7\xbd)I\xf0\xc1\xe4\xcd\xe1\xc1[\xf2\x065\x18\xe9|\xeef\x9e\x9a\xc4\xee\x83\x9f\x8f7\xe4\xcd#~\xa4\xcb\x0f\x87\xf4\xe1\xf0\xe10\xcf\xef\x0e\x1f\x92O\x8f\xb4\xfe\xf9\x90\xdf\xde\x93\xfc\xb5\xc0Y\xf0\x87\x03\xdb5\x0fy-\xfcy\xaa\xba\xba\xde7\xbe\xe9\"\xa0\xff#\x7fv\xf0\xf3\xc1\x8b\xca\xa7\xfag\x9e\xcc\xde\xd5\x0f\xf7\xc5\xfb\xf7?\x0e\xdf\xfcx\xbc\xa1\x1f\xabzv\xf7s5\xad~$\x15\xc7Q\x03\x94\x89\xca\nU\x17\x8a\x0d*\x12WY<\xc5]\xe4G\xfc\xf1\xe0\xe7[/\x82\xf7\xef\xdf\xa6?\xdf\xfeH\xef\xe6)~\\\xde?&8Mg\xb3\x8f7\xf3\xe5\xbby\xf2H\xde\x99\xbb\xe4\xbf\x986\xe12I\xce\x90\x0c4j\xd0\xe4\xa2U\xae\x92Z\xef\xc6\xd7\x1e\x10\xc6\xcam\x83Q\xabH^\xec\x0b\x0b\x8c\xc1\x16\xdfhx\xf1Q\x0c\x9eB\x07\x91,5\x9cN\xbe1\xb8\xf7\x83\x9b.\xd2TQl\xd9\xb5\xa8\x13Q\x84&&\xb8\"\x8e\x85(cSW(-\x8bWTT\xe4\xe4\x02O\xf5\x8e\xe1\xe0\x8d\xe5\x88\xd1\x04\xa7fP\x80\x81\xa8\xfe\xd19\x87\xcfK\x86[+\x1b\xf1\x93\xba^=(-\x89\xe8\x01$\x8c\x12\xdal\x9e\xda\xea\x07'\xbei\xc6\xea\xd4\x133dFY\x13\xf3\xb2\x96Ij>/**\x9c\xa7\xc4\xe9\x990\xb1\xfe\xc7JY)\xbb\x16\x1azx\xe1\x80\xa2JO\x9bssI\xf4\xebk\xf3\xdb\xfd\xb2j\x15\x05\xa8\xb5>1\x0eB\xea\x93\x86\xcd\xe2S\xf9\x89\xda^\xddDf\xe5+\xd8\xee\xc4\xe5\x88\xd3Vo\xdfn\x04\x8b\xef\xf6\x7f\x0d\x7f\xab\x07#\x14\x83\x8a\xa2\x90\x91\x0eC\n\xc5Q\x01\xfc\x15\x00\xaf.\x02\xb8\"\xd9\xf3]d,\x9f\xfc\x81\x9aLe\xbe\xf0\x11:\x99/r\x9e\x0bR\xa3:\xbd}-S\x19\x03\xb0\xb2\x82\x92j\x8a\x13ae.k\xc2\x0c\xbeJkM\"vH\xd5t\x07\x08\xc0\xfa\xcc\x8d\x1e\x1f\x13\xc1y\xdeO\xcbL\xea\xa9\xbf\xa1\x13]xDX\xf8>\xb0\x15\xb8\xa5\x1b \x125\x17*\xc6\xfd>\xa3\xb3,\xda+\xd8\x1fu&\x93h\x03!\xc6!_\xa0z\xa2Y\x03\xd1=#\x9e\xf8\xce\x11\x8f\\K\xe1\x1f\x01\xe7]\xfeTLl+\xa0[f\x1f\xa1\x1d\x95\xd7\x12\xce\xab\xa9\xca<\x9e\xdd\x0eXA\xe2\xc1\x1c^\xb3#\x152z\xca\x85\x85@\x1e(\xb4\xe4\xf0\xa2*\x13\xb1\x05\x83\x9d\xa2\xe5\x07p\xe6A\x92~\x07\xa7\xfd\x0eM\xfc\x1d\x96\xfa;,\xf9w`\xfa\xef\x80\x04\xe0\x81)\xc0\x83\x92\x80\x87\xa6\x01\x0fM\x04\x1e\x94\n\x8c\xd6J\x06\x8e]pu\x9f\x91\x13\x82\x07\xa6\x04?QR\xf0\xd3\xa6\x05o>1xKR\x83\x9f!9\xf8Y\xd2\x83\xb7(A\xf8%\xa4\x08o]\x92\xf0\xf3\xa6 \xab\x00\xeb\x98e\x01\x8b\xe6\x12\x0f4\xa6K<\xd2V\xd3w\x92z\xfa_\xd5\xa8\xcen\nL\x97\x15?\x92*T\x03\xf0`v\xdf\xb1HzV\xe2ZZ\xa3l\x0b\xeeg\x05\x13x\x9c\x8b\xcc.\x0c\x82i\xf7I\xc7\xb1\xb4\xcf>z\xf0\x8a\xe7n\xa8\xa4g\xbd\x11\x10-\x17('w$\xb7\xca\xdb\x84\xa3\x1e\x85\xafCX\xb4\xaf\xdb\xa0\xb9s\x91\xf1\x99\xd7\x9c\x97\x9e\x9c\x90p\x15 \xd8e\x9aM\x83\xd1\x83w=\xf8\xd1\"\x00\xb1\xd4~\xcd\xf1XN\xff\x86\xa9\x80d\xea\xeb\x1fG\x89\x11\xff\x08\xe7Vo\x88\xa0\xa0ps \x88\xacT\xfb~m\xb7`\xfc\x08\xe7\xd5\x83\xd8\xd0\x97\xc2\x11\x92\xe9a\xb4\x8d0\xc5}i[+g\xde\xb1\x00\xfc\x025^\xa8\x05\x86{\x17\xda\xb0\"-k\x94h\x19M\xd0F\x8b\xb3\x80\xe6|#\xdbz\xdd\xa2,\xbdJ\xb2\xd8s\xfaL\xc2\x1b\xce\x14\x88x\x87\xc2\x82\xb1\xc4\xf8\xdf\x08Bb\xa3\xbci\xa1\x8az\x17_\x19Rz\xc5\xf8\xd13(\x88\xb1J\xae\xf4%u\x84\xa50\x88\xd4\x11\x94\x06\xb0\xcc\x8a:\x15u\xa9\x0cy=6\x95\x86\"\xb1\xf1\xb1<\xaa\x16\xe2#\xa0f\x94',\xda\x05\x8f\x96\x18w\xdc>\xcb\x0f5\xa5\xf3\x95d\xf0\x94\xf9\xe3\x8e\x96{\x1c\xbeiQ\xfe\xfb\xa4\"\xda{/\x19\xef\xdbx\xba\xbcv\x9c=\xa3UR\x8cFE\xc0\x96\x94x\xa2\xce/\xf5\x00\xe7X\xfex\x8c\xe8\x08X|\x04\xd4S.\x9eqc$\x06DI\xf4d$,Rb\x8dX\x89\xe1\xd1\x12C\xe3%\x86FL\x0c\x8e\x99\x18\x14518nb`\xe4\xc4\xf0\xd8\x89\xe1\xd1\x13\x03\xe3'\xd6\x8d\xa0\xe8'\xe1\xc5\xe3\x8d\xa2\x00|\xeb\xferh\x1c\xc5\x93ER\x80\xb5i\x1a\xc1\xd6\x00\x8a\x10(\xd7\x91\x03K\xaf\xbe\x89^h\xa1\x8e\xeb\"\xaa\x9c:\xd3\x1eWP\xdb\xc3\xa9\xb5\x15S\x1b`\x0f>\xd9/b2h\x8bx\xd6E\\\xf2\xaf\x07\x18G\n\xd4`^\xc6\x14\x17\x90u\xc3y\x02QZAp\xebP?\xd2\xc2\x19N\xfd\x9a\x8a\x0b\xb0\xbc\\\xac\x80\xe0+\x9d\x8e\x81\xce\xa2\xcaweu\x0eu\xc2\xea\xddM\xd4\x89\x93\x1c\xb0\x16A\x14+\xa3\xdc\xb4\xae\xce\xa8\x7fa\xa5C\xef8[\x9a\x98\x95:\xdb\x7f\xe1\xce\xdb\xee\xc2\x10\xcb\xe1i\xf3\x03\x042\xbeE:\x92\xaa~\xf2X\x8c\xfdfI4\xc1\xd4\x82\xd4\xd7\x08\xfd\xc9\x8b\x18\xc8\xd4\xf4 ,\x19\xbf\xef(\xacq\x9c\xd7%\xba-\xca\xfb\x02a\xb6j\xbf2\xf3'\x18\xa4\xf1\x1c\x0ec\x18\xc7\x9a\xf4\x04\xb5\xe9\xd4m\x87\xcaKh\xe5\x1eD\x04\xa6\xe65\xda\xe1\xf523:C\xd3,\xa7\xa4\")\xba\xbdSz\x9d\x92\n\xd3\xb2\xf2\x07\x97\xc9\xc8\xf5 \xbb@\x04J@J\xcaX\xf6\xae\xa2LLu\xb0\x10\x84\x964+\xfb\x0e\xaa\xe4\xd1|\xe5t*\xe3\xe6\xec\xe6\xc0PfE\x17\xc5hn\x18`R\x05p)\"\xf04 \xd4\xb9W\xef\x84@\xf2\xe9P[6\nMNl\xf0w3\\\xcf\xc6'\x95\xd1\xc1 s\"\n\xa3\xe2\x8bZk\xd1d\x8b\x98PD\x83\xf0\x02L\x01\x16\xe1j\x8e\xbbk\x8d\xfc\x0e\x1b\xf8\x9a\xfd,\x1c\xbb\xc4\x1e\x1d\x9e\xc66Ak6\x93r>/\x0b>^8\x86T\xb4\xc3z\x16v\x88\xa1Ex\x9a\"\xa3\xb9+\xe5\xad;\xa2W\xca\xea\xd1\xb5\xaa\xd1\x8e\x00\xfb[sE\xcb\xf9\xb4\xafY\x13\xbc\xabU\x0f\x13\xbdYqW\xde\x06\xd6RV,\x96\xf4\xc5&xAvA\xaf\x89\x87\x98z\xed\xe7\x94M\xbb\xec!'\xcb\x9c\xb3\x89\xcf\xb3\xe2\x16Mpr+\x8b\xc3\x03 \xf1\x18\x05\x9e\xaa\xc1\x17M\xd8\xc3\xa5;\x11\xc4]\\=Y\x004\x0f\xc4\x03\xdf*\x0e\xf1-\x15}\x133\xfe\x15\x94h\xf1\xa5\xa6x\x92g\xf5\x8c\xa4*\x98!\x16d\x0e\x91\xe3=\xd9\xd4\x97r\xb7\xc0\xcfH\xc2\x13\x11\x0c\xab\x02\x00\x8f1\n\x9dW\xe5\xa2\xac\xe1<\xd0by3\x8c\xe0\x8d\xf8\x17\n\xa7)\xf7\xe8\xd1j\x99\xf0h\"n#\xcdqU\xcf\"A\xeb\x08\xd5\x14\xd3et\xeb\xf7\xe3\xff\x89\xceu\xc9\xa6\xc2l\xe2\x82\x8f\x8bH\xb5\x0c\x15\xd2\xa0 \xd0i\x18?\x96?\xbe\x84\xa5\xd4\xdb\x9f]\xfe\xef\xc9\xf9\x80\xcf\xbe\x1e\x9f|\x8b~f\xe8\xe4\xa1t\x0e\xd3\xc6\x9e\xd1 \x93\xe2\xfd\xd8\xd2\xc0hY\xd8mV}O\xcf,\xfa\xee\x94\xb6\x99\xc7\xdeY*\x8c\x18\xbb\x89\xf7'\xd7\xdd\xa6\x86\x0c\xc9\x96C{H\xf6\xce\x18R\x93\xb4\x9f\x94E\x9d\xa5\xca\xfb\xc0\x07\xbf\xcd\xf8\xadE*\xban\xcd\xb3\x9a\xb7}\x95\xfa\xa8\xacPJr\xbc\")01\xd1\x83$[|m$\xd9;/_\x9a\x0bE'\xe6Lf\xf8\x02\xd0\xd5\x03\x15z\xe2Pu&\xe4\x13)\x12\xbc\xa8\x97\xb9FI\xca-~\x8a\x85\xf8\x11Qcy\x00r\x92a8\n\x1cv\xea\xdfP\xd3[\x9e\xf7\xda,\xa7\x8ae\xb2)\xb2\x12\xd7\x91Yb?\x15\x07\x19\xf5AS\xa48\x83\x99\x0e/\xa3\xb2R\x1bK\xc5\xc0\xe6.\x03\xa0gkg\x11&g\xa1%yy\xe4\x01\x17\x9f\xedc}%RN\xb9\xe5\xca\xa7\x1cS\x8a\x93\x99\x18\xad\xe9\x1aXV\xde\x907\x8f\xf8\x91.?\x1c\xd2\x87\xc3\x87\xc3<\xbf;|H>=\xd2\xfa\xe7C~{Or\x17\xc6\xf1\x02Tc1\xb4Un\xaa!\xdf\xaeE\x17!U\xfc\xf8\xe0\xe7\x83\x97\xd8O\xf5\xcf<\x99\xbd\xab\x1f\xee\x8b\xf7\xef\x7f\x1c\xbe\xf9\xf1xC?V\xf5\xec\xee\xe7jZ\xfdH*\x17\x17\xa2\x971#1\xa1u\xf5\xa2y\xa0$\x8b\xc5\x82\x10\x88\x83\x9fo\xbd\x0c\xb8\x7f\xff6\xfd\xf9\xf6Gz7O\xf1\xe3\xf2\xfe1\xc1i:\x9b}\xbc\x99/\xdf\xcd\x93G\xf2\xce\x05Yf\xb7\xa8\xb2V\x1e\x93[p!T\xf1^\xdb(\x81\xdf\xc0X\xd5\xc6H\xf0k\x9a\xe3\x1b.Qu\x1d\xfdR\xfd2\xa0=4\x0c\xeb\xfeB\x1f\\\xeaEY\xd4\xcex\x06\x85\x84\x14\xb4\xdb\xc3\x15S5\x0cd\x8a\x02\xd1\x97'\xe4A\xa0\x10<\xad>%OZ\x08\xf9y\"\x7f\xc8)\xf4\xde r\x10\xf1\x81\xaf@\xb7Z\x05YA\xdf\xbdm\xfd\xd5[\xdc:\x80CJ(\xce\xf2-\xa9%\xc1>\xbf^V\xde\x1e,Q\xed\xcaS0\x07\x7f\xed<:/p\x85\xe7\x84\x92\xca\xc0yOd\x9dZ\xb6\x8ao\x11\xfe?{\x17\xf64o\x1c\x9f\x8ee\xd3d\xc5\x11Z`jF \xb2\xcd\x9cU$=B\xb4Z\x9a[\xc4\xc9:\xcd\x05i\x02\xfb8\xd0\x92C}-\xe6avr\xc8L\x1cb\x073n\xfdd\xf2\xc8\xc9\xae)\xcek0\xbf\x0c\xe3\x0e\xc8\xb2\xc1\xe6\xe0\xb8F\xe0\x88\xe5\xdd\xe6\x874\xb6z\xb2\xa3\x8f\x95\xd7\xb2\xe7\xba\x1b\xe3\xf9\x99\xd12\xd4\x80\xcc\x18h\xde5\x86\\gm<\x1f\x07Z\x16\x1a\x90\x03\x83\xec:\xc3\x82\xb3\x80y\xac\xb9M\xd2O\xf1\x8d\xa5d\xff)\xc7y\xb5\xbf\xa8\xca;R\xe0\"!\xfbsBq\x8a)\xde\xbf;\xd8\x97\x1b{\xff_Mi\xa4\x7f\xbf\x12\x10n\x88\xb6\x0b\xeb\xe5|\x8e\xab\xd5\x91*\x19V\xa3\x9a\xe0*\x99\xc9\x96\x8aJ:(*}\x1c\xbe2\xe2\x8dZ\xbaK\xec\x1b\x87\xfe\x82\xeb\xae\xa0\xd2\x02+,t\x99\xcd\xb3\x1cW\xf9j\xd7\x94|\x12c\x0d\xaf\xaduQ\xc3\x06/\n#\x18\x1f\x8c\x85\x1a\xa0\xa1\xd3vQ6U\x9d/\xd3]UW\x0b\xe1ft\x88\xbe\x1bC\xd75#\xfe\x17:\x99\xa2\xb2\xc8Wjn\xe4\xdd[\x83&\xce\xf3VA7L\xd54V&\x9dtY\x15\xbcU\x87\x13v3C\x16\xf4\x8c6|0&\x05\x17\xa9]\xf8\xa5U\xec\x8e\xefg\xf7\x80\xad\xf5\xc0\x85\x83X\xb2tF\xf4j6\xa8\xe5\xa7\xa5\xae\xa9\xc2\xd1\xc2y]2\xdc\x0cvH\xccZ\x185H\x04P\x93\xbch\x999\x0dl\xda\xaa,\x86\xb3B\xa45a\xda\x8e\xfe\xe1\xedU&\xc4;\x94\x1eEE-\xa8Q<\\\xd85y\x86\xad\x84}=)6\xa3,\x065\xa3\xb79\x15\xa3\xc8E\x89\x06v\x8c&85]\xed\xeaG(\x9b\x1e\x99\x14_y&\x9d#aR\x7f\x9f\xd1Y\xb9\xa4\xf6\xfc\xbb6\xab\xc5\xd0c/75\xbcb\x85\xee\xf1\x8ak\x1dU\xfb\xac\xb1\xbbv\xac\x1dlr\x1dw\xf8n\xf2\xfbo\xc67\x06n\xbfY\xc8]\xdd\x97ljx\xbf\x9br\x1a\x98Q{{\x99SindM\x1a\xfbQZ\x16\xaf(\x17\x01<\xc2A\xd6\xf7A5\x9e\x93\x96\xd8v\xf3\x8bC\xdfabWq\xdb\x18\x9aW\xbb\xeap\x13\xe1\xb60V\x9f\x89m'WSZ\x92\x1a\x15%U\xcb\n\xd5\xcbd\xd6\xe5W\x00\xad\x16&\xbco\xd0\xa4\xa43c O\x96\xb4 \xed\xd0\x80\xd2l\xca_\xa9%\\\x9bK\xf6\x1f+\xe5\xaf\xd95\x16@3V-\xfb\x13Q]A\xc9@\xf1\x92\xd0\xc6\xf6W\x1f\xee\x97U\xc7\x02f\xf3P\x89|U\xeb\x04`]\x9d\xc8o5\x92\xe2\xad.\xbdu\x926\xa6\x82\xfc\x9b\xb2\x04\x0d\xf3\xe4\xd5\xdb7o^\xf9]OF\xea\xae\xd3\x8e\x1c\xc9\xe9\xc4 \x8b\xdc\xc89\xfd\x80\xd2\xd4\x17\xee7]\x9f4QM\xa0\xd0\xac\xcc\xd3Z^\xcei-\xd7\xf7\xc6p\xfd\xeb\xbb\x90\xb34\x10\x19\x14s4\xc5.\x07\x11\xcc]\x04\x8c\xb5\x88'w\"\xf4]\x14\n=\xf9\x03\xe9:\x0fB| t2_\xe4\xbc\x19^\x8d\xea\xf4\xf6\xf5q$u\x11q\xdfa5\xc5\x89\xf0\xa4\xf2\n\x9a\xa2\x01\x98\xf8\x92\x88\xad\xa6\xac\xf6\xb6\xe1o?\x9f\xcbz^\xfa\x06\xab\xc1\x99\xbeO\xcb\xccF\xbe\xd4\xdd\x04]}_\x1f\x8e\"\xb0\xbe\x14'\x05^\xc9\x83\xa8\xb9\xc0y^\xdeK\xd9,/\xd6C\xe0\x12\xff\xe5-o\xb5\x1e\x88^\x0b\xb9b\xd5\x13-\xb3\x12\xdd3\xe2\x89\xef\x1c\xf1\xe0M\x95\x00\xc5\xee\x9e\x99;*e>\\\xe1\xac*\xf3hi3\xc8\n\x12\x0f\xe6\xf0\x9a\x1d\xa9\x901\xca\xf9&\xb2\xd0-0nL&\xd5\x13@y\xcf^\xcc\x83\x95)\xb1:\xac\xf7\xa9\xdca7\xd8\xbf8\xf9\xef\x93\xd3\xe3\xab\xb3\x8b~\xdf]~\xb9\xf8\xf3\xe4\xf3\x97\x9e_\x9d\x9c\xfe\xf9\xe5\xb2\xf7X\x9f\xbf_^\x9d\xfdqr|\xda\xef\xb3\xb3\xbfN\xfb\xe2w\xfc\xf5\xeb\xc9\xb7\x93\xe3\xab/\xfd>;\xfb\xbf\xd3\x93\x7f|\x8f\x17y\xb1>:\xbf8\xfb\xf3\xcb\xe9\xf1\xe9\xe7\x9e\x835\x8d\xfc\xfb}\xa7\xdb\xedG>\xd3\xb5a\x06-\xaf\xb8\xb1b?\xbeUi\x1bR\xedz]\xe36#\x88\x14%\x8d\xd4\xb4\xee\"\xa4\x05\xf2\x91\xf3\xad\x0b\x1d^:\x92/aQ\xd6G\xec\x0fH\x05\"\xa4\xdbU\xc7,\x8bx\xf0J\xf3@\xc2X\x9aG\xdaj\xfa\xeeBO\xff\xab\x1a\xd5\xd9M\x81E\x9f\xeaZ\xa3\x1a\x80\x07\xb3\xfb\x8e\xd1\xb9\xb1\xcb\x955\xca\xb6\xe0>\xaf\xc2-\xb8\xc8\xec\xc2 \x98vq\xf4ha\x9d>z\xf0\x8a\x1f\xcbe\x93\xe8f# Z.PN\xeeH.\x8f\x14\xf1Xi\xe57\x11\x16\xed\xeb6h\xee\xcad|\xe6~9\xe6|G\x88J_U6YR\x82\xe6x\xc5oL\x02\x00'\x84I\xf7\xe2\x86\xa4h\"\xfc\xedR'4\xc5\xdc\xca\"av\x0b\x93s5q\xa4\x1d\x88G:\x89\xae\xb9\xcc\xbd\xae\xca<_.b\xd3\x1a\x93\xecP\x0d\x00\x9f\xff\xbf\xa4z\xc0y\xaeE\x80\x9d\xeb\xc0\xaf;h\x8d\x1c\xf1&\xedG]\xbb)\xa1j\x01zU+\xd1:\xcdH\x9e\x06z\xfe\xabi\xcb\xeb\x12\x91\x02Orq\xe6g\"Y\xe9\xaa\xbfsW\xb5@P@\x0d\xad\x91B\xf7\x8fR4\xba'\x0d\xc66\x11U\xc9\xbb\x0d\x10^.\xb9,\x0d\x97\xbe\xb0\xa4QR\xe69\xe1\x86\x13C\xbb\x1b\x9bb>\x8c\x00\xbe\xda\xca\x82\xe8k\x97\x00\xa2\xca\x01\x1b\x08\x0e\x86Qb\x01j\xbc{\xb1\xd2o~\x8fM\xa3\x8c\x9c?\x88:\xd2\xe2.\xb4\xa7t>[L4o\xb2\xe9\xe0\x8a=\x9a\xe3\xb14\xd0\x0dS\x11\xcb\xe4l(\n\xe4\xc2i`\xe2\x1f\xe14\xb8\x0d\x11\x14\x14n\x0e\x04Q\xa8pN\xcc\xaf\x0d\xe2\x07\x0e\xe6G\x82\xd8\xd0\x97B\x1c\xcb\x82\x1c\x8b\xb6\x11\xa6\xb8/mk\xe53:\x16\x80_\xa0\xc6\x0b\x04\xc0p\xefB\x8b\x88V\x0f\x9cn\xb1\x1b\xc7\xc5\xcc\x86\x05m4\xe1\x1f4\xe7\x1b\xd9\xd6\x91\xac\xfe\xe0\xb5\x11j\xdfR\xc1\xe464\x87\xff\xd9\x99\x02\x11\xefPX0\x96\x18\xff\x1bAHl\x947-Tm\x8d\xa0_\x07\xc09\xd5$\x9c[\xcf\xa0 `\xb9\xf2\xe3/\x8c\x11\x96\xc2 RGP\x1a\x9e\xd5\xd2&Z\x9d\x8a\xbaT\x86\xbc\x1e\x9b\xca\x02\x92\xd8\xf8X\x1eU\x0b\xf1\x11\x10z\x8en\xaa\xf0h\x89q\xc7\xed\xb3\xfct\xc7\xda\xa6\x0c\xba\xa7p\x0c\xa8\x95\x89\xab\x10\xbad\xbco\xe3!uh\x8c\xb3g;[\xcfD\x9d_\xea\x01\xce\xb1\xfc\xf1\x18\xd1\x11\xb0\xf8\x08\xa8\xa7\\<\xe3\xc6H\x0c\x88\x92\xe8\xc9HX\xa4\xc4\x1a\xb1\x12\xc3\xa3%\x86\xc6K\x0c\x8d\x98\x18\x1c31(jbp\xdc\xc4\xc0\xc8\x89\xe1\xb1\x13\xc3\xa3'\x06\xc6O\xac\x1bA\xd1O\xc2\x8b\xc7\x1bE\x01\xf8\xd6\xfd\xe5\xd08\x8a'\x8b\xa4x\xeaX\x8a\xa7\x88\xa6\xd8\x9ax\x8ag\x89\xa8x\xa6\x98\x8a\xad\x8a\xaax\x19q\x15[\x18Y\xf1\xbc\xb1\x15\xf0\xe8\n\xe8\xbd\x9az\xa0\xf7k\xea\x193\xc6\x02n;\x8e\x14g1(\xd2\x02\x8a\xa5\xbeK\x94\xce\x15\xc2\xbb}\x89<&\xfe\x07\x99\xf0vS\xf0\xfc\xb1`\x008\x8a\xa35R\xe1_\x18m<\xb5\x87\x96\x0d\xfb\xcc\x8b~y\xcf\x08k\xec&\xcd\xfb]\x86_\x96\xe0<_5-\xfd\xbd\x1f\xca\x8fF w\x03g`u`\x11 \xd4\x19\xc9\xc5\xa5<\xd3\xc8\xcc\x04H\xf2\x8c\x14\xcdy\x98GL\x04\xe1u\x97h\xf4\x10\x8c\x97i\x16\xe4N\x1fz>\xf3\xe3w\x8a&\xab]\xb4\\\xa4\xfa\xdf4\x9b\x93\x9a\xe2\xf9\xa2\xde\xd5\xee3Q=0\xdcrSdm\xe5\xb2)\xdc\xb4\xf4\x93\x01>aC\xcf\xd7\xc2\x93\x90^3\"b\x82\x13\xb8\x80\x90\xb1\x88\x18\xd8=\xc6\x96\x18h\xb1\xc7d\x1c\x13\xd9g\x9f0 *c\x05HA\xab\x15o\x05'\xf1\x05\xd14Y\x8dH\x11L\n \xd4i\xc9o\xfa\x15\x84\x89\"WOT\xc0!\x1dP\x16\xfc\x99\\\x81/f\ns\\S\x854\x88\xb0-\x9dG\x1e\x96\x97\x8d8\x91Rd\xc0\x88\xf5\x15\xb4\xb2\x1f\xa3\xbc\xd5\x87\xf71\xb8`\xce0\xdbZ\xda.-9'8\xc3k\x9d'\x15\xb7\xfe\x01\xfe&.\xc3 Nfr\xc6\x83\x1fx\x8br\xd9\xcfFV\x84I\xb7\xc4\xc3\x11\xd9\x17\xdb(R@ee\xb1/\xe8E\xe4.|L\x82ax\xccT\xdcW\xa6]k\x94\xe0\x85\xb02\x9dQ;l\x07\x06A\xe9u^\xa29\xbe%r\xa9\xab\xdc=\xa6\xae\xe4\xc6&+tO\xaa\xc8\x8c\xe1\xd4\xf7\x03\xa8\xd2\xbdT\xc9\xd8*\xbe\xca\xb0\xa5\xb4u\x81opV\xd4\xd48\xcdx\xe1\xd9\xaew\xf6\x15.\x12\x12\x88>\xbb2\xdc\xae\xbc\x90\xc0\x0c\xdf\x11c$\xc9a\x1eSF\xd5\x914#~\xb1\x90\x15we~gW!h?\xa7g\xec\xe4\xfd\x97\xe4tEdF\xb7\x08\x05\xbb\xb0\"\x8d\x11\x96\x11hV1\x8b\xf6\xd3\xc4\xa3a\xc5R\x91<~i\x96\xbb\x98\x97w\xbe\x86\xbf\xcd%\xcf(M\x80Z\xe0\x86ED8c\"\xc2\xe6 \xc0\x86\x82\xd8OO\x7f\xe5\xd5\x0e\xdc\xb2*Q\xb8\x03\x1e\xe4\xccFB\x19\xac\xb9\x88E2l\x94\xa6\x88\x90\xeb\xa094\x9c\xc1\x04\x05\xe2\n$\xd8iK8\xd3\x0d\x922\xde\x00;3P\xab\xd2 \x8cC\xf2\x9f/\x83G\x06\xb2\x8dC\xc2x'\xf9\x14\x84W\xb6\xcb\x00\xc5\xf9\x14\x0e\xf4\x003g\x08\xd5#\xc4z\xf4\xdb5#-\x84!\xb4\xae\x15\xec\xe1](\x01\x92UP\xe1\x98\x9d\x84,\xa0h\xecn\xab\x90\x99\x04\xe9J\x04\xd4\x97\x08AB\xb5\xc5\x03\\\x1a\x08.'\xd0f\x82\xb7\xc5\xa3g*\xae;\xd1\xd3P\x078\xce\xb4\x90\x1e\xaeIM`@>\xc1\xb4)\xda>^\x8d\xa1[\x11\xfa\xd5n\x15N\xf9\xc8\x8bd\x18\xe5k\xea\x13\xe4^Bn\xf2;\xadv\xc68k9\x81\x8ew\xe2rG\xb5m\xf8\x00\xd6\xa6i\x04[\x03(B\xa0\\G=Z\xce\xc5/\xb4P\xc7u\x11UN\x9di\x8f+\xa8\xed\xe1\xd4\xda\x8a\xa9\x0d\xb0\x07\x9f\x92^\xdd\xbc\xb6\x88g]\xc4\x11\xa0\x85\x9c\xe3\xabVD\xfbP^\xc6\x14\x17\x90u\xc3y\x02QZAp\xebP?\xd2\xc2\x19N\xfd\x9a\x8a\x0b\xb0\xbc\\\xac\x80\xe0+\x9d\x8e\xfe\xbeo\xdaw\xb5\x81\xc6o\x12v\xa0\xc9\x9b\x1a\xdd\xd3JMf\xefv\xe7V\xcc\xe8\xd3\x86\xf8\x0bd|\xebl$m\xfb\xe4\xe1\x14\xfb\xcd\xac6\xf1\xd0\xb2\xdc*B\x7f\xf2:\x042\xbb<\x08K\x86\xe0;jc\x1c\xe7u\x89n\x8b\xf2\xbe\xe0\x95k\xd1Wf\xc1\x04\xe3,\x9e\xc3\xe7\x0b\xe3X\x93a\xa0V\xae\xba\xb0P\xa9\x05\xad\xf4\x81\x88\xcc\xd3\xbcF;\xbc\xe4eFgh\x9a\xe5\x94T$E\xb7wJ5SRaZV\xfe\xf80\x19|\x1ed\x17\x88@ H \n\xcbdU\x94\x89\xa9\x0e\xd6r@M\xe1d\xeb\x1a\xa9\xe4\x01y\xe5t*C\xdf\xec\xbe\x8aPfE\x17\xc5h\x9e\x14`^\x04p)\"\xf04 \xd4\xb9\x1a\xefD1\xf2\xe9P[6\nMNl\xf0w3\\\xcf\xc6'\x95\xca\x16\xeb\x9c\x88\xc2(\xda\xa2\xd6Z4_\"&\x14\xd1 \xbc\x00S\x80E\xc4\x99\xe3\xfaY#\xbf\xc3\x06\xe65\xa7\xc3\xe1G\xec\xd1\x11f\xbcw\x81=\x9bI9\x9f\x97\x05\x1f/\x1c\x06*\x1a\x8a=\x0b;\xc4\xd0\"\xc2L\x91\xd1\\w\xd2\x12r+\xac\x1e]\xb9\x1a\xed\x08\xb0\xbf5\xb7\xac\x9cO\xfb\x9a5\xc1\xebV\xf50\xd1\x9b\x15w\xe5m`-e\xc5bI_l\x8e\x16d\x17\xf4\x9ax\x98\x05i?\xa7l\xdaEu\"U\xf2\x9cM|\x9e\x15\xb7h\x82\x93[YT\x1e\x00\x89\x87\x19\xf0l\x0b\xbeh\xc2N*]\xcd?\xee\xa5\xea\xc9\x02\xa0y \x1e\xf8Vq\x88o\xa9\xe8\x9b\xb0\xef\xaf\xa0\\\x89/5\xc5\x93<\xabg$U\xf1\x08\xb18q\x88\x1c\xef\xc9\xa6\xbe\x94\xbb\x05~F\x12\x9eK`X\x15\x00x\x8cQ\xe8\xbc*\x17e\x0d\xe7\x81\x16\xcb\x9ba\x04o\xa2\xb9P8M\xb9S\x8eV\xcb\x84\x07\x04q\x1bi\x8e\xabz\x16\x89;G\xa8\xa6\x98.\xa3[\xbf\x1f\xffOt\xbaJ6\x15f\x13\x17|\\D\xaae\xa8\x90\x06M\x80\xce\xa4\xf8\xc1\xbb\x84\xc80w6\xc5\xfc\xa2k\xb1\x8c\x8b\xe7^<\x86\xa7;^|\xf9|v\xf1\xc7\xf5\xc9\xe9\xf9\xf7\xab\xeb\xcb\xab\xe3\xab\xef\x97\xbdR\xd0|0\xce/\xce\xce\xcf.\xd7\x00 \xdeE?\xd7)t\xeb\x12\xd2_\x92G\x99\x07\x99\x81\x08\x08#s\n\x90\xd3\xc1\x03\xb6p\x9e\xa5\xfb\xcbB\x9c\x17\xc5\xbaek\x07\xf0qd*\xdd\xf3\x89\xad0\x1cG\xf1\xce\x8d\xa1x\xe7\xc6/\x13\x1b\xda\xb2\xc0\x16\x15\xb9\xcb\xcae\x9d\xaf:[\xdd\xc8\x8b\n\xe2*\x85\xcdU\x85\x93[\xe1\xd4\x12\x96\x93>\x01\x12\xa5\x93\xa0\xa72\x90\xf42\x07\xe9\xd8\xd9\x8c\xb6d\x96\x91;\xd1\x1c\xa5\\\xd2\x98\xd8)\x0b\x10v\x02\xd2\x8b\xb5\n\x9fY\xd7\xff\x8f\xd4\xf3T\x15G\x15\xec\x84UZ\x10\x8f\xf8b_{\x01\xdan\xa8g\xd0\xa0\x97\x1c\xa6\xa2K-\xc1\xc6\x8d\xd1\x1c\x08\x01\xc0\x9a\x14%\xc9\x1b\xb9\x95\xb3\xe2F5\xdc\xd9\x9d\xe2,_V\x80#$bjxA\x8a\x144\x91}f\xbd\x8f\xd2\xbd\xfc\xfem\x90\x96\xea~}~| \xcb\x8a\xb7?\xbb\xfc\xdf\x93\xf3\x01\x9f}=>\xf9\x16\xfd\xcc\xd0\xc9C\xe9\x1c\xa6\x8d=\xa3A&\xc5\xfb\xb1\xa5\x81\xd1\xb2\xa8I\xdcV\xe3\x17\x12}\x12\xe1\xbbS\xdaf\x1e{g\xa90b\xec&\xa6A\x9b\xceSC\x86d\xcb\xa1=${g\x0c\xa9I\xdaO\xca\xa2\xceR\xe5}\xe0\x83\xdff\xfc6\"\x15M\xb8\xe6Y]\xb3\xcd)\xf5QY\xa1\x94\xe4xER`n\xa1\x07I\xb6\xf8\xdaH\xb2w^\xbe4w\x82N\xcc\x99\xcc\xf0\xc5\x90\xab\x07*\xf4\xc4\xa1\xeaL\xc8'R$xQ/s\x8d\x92\x94[\xfc\x14\x0b\xf1#\xa2\xc6\xf2\x00\xa4\x15\xc3p\x148\xec\xd4\xbf\xa1\xa6\xfd\xfe2\xa7\\HK\x96\xf1^\x84\x8d\xb8\x8e\xcc\x12\xfb\xa98\xc8\xa8\x0f\x9a:\xc3\x19\xcctx\x19\xc5\x91\xdaX*\x066w\x19\x00=[;\xeb(9k%\xa9^}n@\xf1\xd9>\xd6W\"\xe5\x94[\xae|\xca1\xa58\x99\x89\xd1t**\xdb\x9b\x04'\xfe\xca\xd8\xf6\x8e\x91\xeb\x98\x9f\xdd\xa3\x19\xd7\x16\x8b%F\x92se!\x1d\x1a\xf2\xb5LSr\x93\xac\x1dW\xa3D\x18\xd9\xd0\"\xa1EF\x08\x91\x17\xa0\xb5\x046\x1cJ\x04\xf2\xe1mp\xa3\xe8\xf1\xbd\xd1ArB\xa3\x11?\xcd4ls\xa8O\x1b\xcb\xe11>\x1a\x12\x84%\xb0\x88\xc2m`\xcb\x18q\xa6\xcd\xb2\x01\xb2G8\x0b\xa2\xbe\xc9m\xe0\x8f\x81\xaab\x90\xf4\xfa\xf1W\x1b\xe1\xd03\x842\x19#\xc7b\x98\xc6\xd8)/0\x8d\x01@\x15HK \xa0\xa6@\xbf\xb2\x18\xe4\xf3\x04\xd4EO\x07\xbf\xb2\x18\x06\xf1j\x0c\xed\x82\xd0\xaf,\x068\xe5#/\x92a\x94\xaf\x19\x0c\x8a\xfad1H\xb9\x0b\xc8\x18\x04%\xb8@$\xe3E\xb8'\x04a\xbe\x88w\x11y}\xe3\xdb\x92\x9f\x0e>\xfd\xfeq\x82\xdf\xee\x1dN\xdf\x1d\xee\xbd?\xfc\x84\xf7>~\xc0\xbf\xefMI\x82\x0f&o\x0e\x0f\xde\x927\xa2j\xbb\x92\x10\x89\xbf\x9d\x9cT\xear\xc0\x10\xae\x07?\x1fo\xc8\x9bG\xfcH\x97\x1f\x0e\xe9\xc3\xe1\xc3a\x9e\xdf\x1d>$\x9f\x1ei\xfd\xf3!\xbf\xbd'\xb9\x0b\xe3X*\xe2X\xecl\x8a\xcc\xf5f\xe8\xe1\xc77\xef\xa6\x1f'\xc9\xde\x877\x1f~\xdf{O&\x87{\x9f\x0e\x0f\xa6{o\x0f\xde\x1e|\xf8\xfd yK\x92\x16C\xc5`k\xb1T\x808\xf8\xf9\xe0e\xea\xa7\xfag\x9e\xcc\xde\xd5\x0f\xf7\xc5\xfb\xf7?\x0e\xdf\xfcx\xbc\xa1\x1f\xabzv\xf7s5\xad~$\x95\x8f\x1c\xde\x81\x981\xa1,\xf2U\xc3\x02\x94\xf1\x148\xc3\x11\x8f\xf3\xba\xf4\xe1'\x1bD8\x05_\xf4\x9ei\xa49m\xdd*i\xf6+\xa1i\xf19\x04\xe2\xe0\xe7[/\x97\xef\xdf\xbfM\x7f\xbe\xfd\x91\xde\xcdS\xfc\xb8\xbc\x7fLp\x9a\xcef\x1fo\xe6\xcbw\xf3\xe4\x91\xbc\x0b0\xc0\x7f\xfa\x1e\x97\x01\xe6\xa9Yd\x8dY\xbe@Z\xa2iVp\x81\x18Y\x99\\o\x14\xd2\x96\x15\xf3+\x93\x8c\x1d\x1f\xc8\xec$\xe1A\x0d\x91\x19\xea8\xa0\x0d\xcc\xc0o`\xbc\xb0\xd0\x11\xcba\x9a\xe3\x1bN\x93nbP\xaa\x9f!\x1ai\xa3)\xf5\x9b\x0cy\xaa\x89\x91j\xaf\x0e\x9f\xf5\xa2,\xea k\xa4\xb2\xdc\x1e\xe6\x98\xea=\xc6\x9e\xe0\x9aSg\xc5\x9e\x8c!\x0f\x02\x8f\xa0\xdb\xe1)\x19\xd3B\xc8\xcf\x18\xf9CN\xa1\xf7r\x97\x83\xe0\xc1O1F4\x13\xe21}\xd0\x133\xa2\x85\x10d\x85xs\xa25\x8c\x02\xad\xcae%m0\x9b\x0da\xacdQ\x8d\xfaB\xb2\x105!\x89\xe2\xff\x8c52\x8c\x9d\xa0\x7f.I\xb5\xdaW\xdf\xa0\x8b\xf3\xcf-p\"\xff\xb5A@\x05 \x1b?\xb3\xf09.\xd0\xb2 \x0f\x0b\x920CR\xb4\xaeRC\x1b\xdf\xd4\xc9\x8c\xcc\xb1=w^\xb3\xd2oRr\xf8\xdd\x15\x10\xd0\x11I\x99:$\xae\xf8\xc0WQ]-\x95\xac\xa0\xef\xdev\xf8\xe3\xa9F\x1e\xc0!%\x14g\xf9\x96T\x0ea\x9f_/+o\xd3\x9c\xa8\xbe\xe5 \xb7\x83\xbfv:J\x16\xb8\xc2sBIe\xe0\xbc'r\x8c\x0dk\xd4\xb7\x04[[\xa2\xbf\xf9:\xd4h\xd5f\x93\x05\xadk\xaa\x8en\xa0\xaea\x96\xba\x8c\xd1\xac8B\x0bL\xcd\xc8U&\x97\xb2\x8a\xa4G\x88VKs#;'X\xcf\x95\x1c\x1a:S=\xcfm\xc3Nk\xa1C\xc4\x90\xd3\x18\xe3\xd6O&F\x9d\xec\x9a\xe2\xbc\x06\xf3\xcb\xb0\xca\x81,\x1bl\xc7\x8fk\xbd\x8f\xcf\x83\xd6\xdd\x15\x80\x07\xfdMy\xc3hw\xee\x89\xae\x01?\"\x9d\x96\xd1\x0d\xa4t\xa8\xa1n\x98\xe4\x16<\xb8\x15:\x84\xf0\xaee\xe5\xb3\xb9\xda<\x91\x86rO\xae\xf4\xb1\xd0\x0d[|\xab9\xd22\xb2\x81\x1c\x19h\x9a7Fx\x9b'\xcf\xc8\x81\x96u\x0d\xe4\xc0 \x9b\xdc\xb0\xbe-`\x1eK|\x93\xf4S|c\xd9>\xff\x94\xe3\xbc\xdag\xc2\x89\x14\xb8H\xc8\xfe\x9cP\x9cb\x8a\xf7\xef\x0e\xf6\xe5\x92\xdd\xffWc\xee\xfc{_,\xf2\xfd\x7f1N\xfe\xfb\x95\x80wC\xb4\xf1^/\xe7s\\\xad\x8et+\x91\x9a\xe0*\x99\xc9>\xa5r\x87(\x8a}\xdc\xbejn\x93\x99\xb6\xd9eF\x87\x12\xa0\xbb\xaa:[D1i`#h%\x86\x90\x86\xc7\xa5\xdf^\xc7\xa2\x10B\xd1\xb0*\xf4\x07p\x93\xc2eKX\xc3B\xcc\x08t\x99\xcd\xb3\x1cW\xf9j\xb7\xc1\x81Z\xa5\xd9\x9d\xc6\x10\xaf\xd1\xd42:[\x94\x8cadZ\xbc\x8c\xe8\xd7U\xb9|U\x91f|v\x98\xaa\xf8\xa5\x05\xca\n\xb3\xc9\xcd\x7f\xa1\x93\xa90Tq\xdb|i\xd6\x8d\xec\x03i^\xe4\xf0\xae;F\xa1\xbf\x8a\xd0eU\xf0\x06:.\xd0\xa6\xd9\xdb\xc0\xc5y\xde\\\xd4\xc8\\11\x93}\xc17\x13\xb4\xc3&C\x8e\xb6\xdf\xbc\xff\xcd9Z{~\xd4\xb8\xde\xf1\xb0f\xbc6KT\xcbCE!.\xd2\xfdfA\\g6\xffZ\x9b\xcb5\xa0\xfe\xc91\x9a\xe0\xd4\xbc.R?B\xd9\xf4\xc8D\xec\xca^\xa3\\\xbc\xf2\x85\x89\x8b\x14\x15\xa5\x93\xfb\xaf[\x00d?#z_\xb2\xe5\xcc{\x1a\x95S[\x96\x98$\xf1\x8b< \x96\xff\xcf\xdc0+\x94\x96\xc5+*9>\x15\xb2\x9do$\xc6\x97\xf5\xbc\xf4\x0dV\x83\x93\xb3\x9f\x96\x99\x8d\x84\xa9\xbb9\xd5:\xc4\"\x1c\xf8a})LI^|\x85\xa8\xb9\xc0y^\xdeK5&\x93\xd9C\xe0\x027M\xbc\xc1} \xe00\xe4OUO\xb42Nt\xcf\x88'\xbes\xc4\x837U\xb5\x15\xbb;\x95\xee\xa8*\x07\xe1\xa2tU\x99G\xab\xd1AV\x90x0\x87\xd7\xecH\x85\x8cQ\x819\x91\xb5\x89\x81\xa1~\xb2\x0e\x02\x01Td\xed\xc5\xfd\xdcs\xb0\xcfg\xa7W\x17g\xdf\xbe\xf5\xa5\xed\xcf\xe3o'\x7f\x00&Z\x97\xf3\x19\xb4\xbc\xe2\xb6\x8a\xfd\xf8V\xf1\xc0\xe1\x91\x08u\x93\x97|=\xca\xf2x6\xc5\x91\xfb\xb5\x1c\x05\xd7\xbc\xb1j\x95\xddd\x05\xa6\x90\xea\xa4\xce=t\xe4z\xa9\x8c\xf5\x1a\xa5dBQM\xaa\xbb,\xc9\x8a\x1b4]\x16 Wc=GS{\xef\xc8\xf5R\x9c\x96x\xb4]\x96\xa0\xac\xb8#u\x7fz\xf4>=r\xbeUSS\xd0\x8c\xae\x84\xfa\xd64&\xcb\x9a\x96i\x86\x0bI\xa8\xf4\xf7p\x06\xf7%\x94\xef\xfb\xa3\xce\x9bvE\xc0\x05\xae\xe8J\xe2\xc4\x95\xb6\xd2RL\xfb\xf6\x1cR\xcb\x8d#\xe7[\xc1]1\xa08(\x17\x08O\xa7Y\x9eaJ\x10\xbe\xa9\x087Cz\x0e*\xa5\xce\x91\xe3\x9d\x18\x90[=8\x17\xb7\xeb\xb2\xfd\xef\xaa\xb1\x9b\xaa2\x17\xc4\xcf\x8bl\xb2\xac\xd1\x04\x17\xb7J+\xf6D\xa5\x91eG\xee\xd7\x0c!U\xfbF\xcd\x839\x0d\x15YT\xa4\xe6\xa6\x18\x9b\x82\xa6\xee\xa2tl\xd9\xb5\xbfq2`S7r\xf3\xc8\xfd\xda^\x9f\xf7\xb3,\x99\x19|\xd26\xa4\xda\xf5\xba,qF\x10)J\x1a)C\xdeEH\x0b\xe4#\xe7[\x17:\xbc\xda'_\xc2\xa2\x12\x93\xd8\x1f\x90\xa2QH7 \x8fY\x16\xf10\x95\xe6\x81\x04\xac4\x8f\xb4\xd5\xb4\xa7[O\xff\xab\x1a\xd5\xd9M\x81Ew\xf0Z\xa3\x1a\x80\x07\xb3\xfb\x8e\xd1\xb9\xb1\xcb\x955\xca\xb6\xe0>/\x9c.\xb8\xc8\xec\xc2 \x98v=\xfbh-\xa4>z\xf0\x8a\x9f\xcaek\xeef# Z.PN\xeeH.\x8f\x14\xf1\xf0vu\xb2\x17\x16\xed\xeb6h\xee\xf7f|\xe6\xb1\xe0\xf2\xa0\x1f\x12\xae\\R\xf0.\xeb+\x7f?pa\n\xf3\xd3\x94\xa3\x97\x89\xc0\xc0>\xab\x06\xd7\xe9\xb2\x10\x0eH:\xd3x\xe7\x19\xf7\xd8\x96V\x83Po^\x12\xc3\xe4\x1a'\xe1\xee\x1e#\x1e\xc2\"\x16=|\x1d\x1ck&\xa9jj\x8cp\xce1\xbc\xa4\xb3\xb2\xca\x1e\x85\xfc\xacHB\xb2\xbb\xd0Bh*\x88\xf3y\xe9\xb6c\xd0\x0e \x0f\x10\x1e?s\xcd\xd7\xddu\xf4p86\x1f\x9a\x8dj\xab*6\xfb\xa2\x92~\x8b\xa0\xe0\xda4|/\x97\x14\x17)\xaeRS\xbdH}Ws\xf7\xe5\x1cW\xb7\x8eX\xab\xe6Q\xbf\x0em\x076a\xf5r\xb1(+\xab6+\xc7\xfc\xb5\x8c\x8b\xc1\x94V\xd9dI \x9a\xe3\x15w\xdf\x07\x00N\x08\x93\xee\xc5\x0dI\xd1d\xc5\xb9 uBS\x7f\xaf,\x12f\xb709W\x13G\xa6\x88x\xa4\x93\xe8\x9a\xcb\xdc\xeb\xaa\xcc\xf3\xe5\"6\xad1\xc9\x0e\xd5\x00\xf0\xf9\xffK\xaa\x07\x9c\xe7Z\x04\xa8\x1d\xd1x\x9a3ZkQ\x10\x00\xa6\xee\xfa\x94P\xb5\x00\xbd\xaa\x95h\x9df$O\xbd\xc1\xfbH\x853\xe1\xbc.\x11)\xf0$\x17g~&\x92\x95\xae\xfa;wU\x0b\x04\x05\xd4\xd0\x1ai|\xe5\x8aF\xf7\xa4\xc1\xd8v\xc9G\xe5\x0d\"\x08\xafp]\x96\xd4(s\xcd-i\x94\x94yN\xb8\xe1\xd4\\i\xf8pd\x04\xf0\xd5V\x16D_\x84\x04\x10U\x0e\xd8@\x180\x8c\x12\x0bP\xe3\xdd\x8bU\xeb\xf3{l\x1ae\xe4\xfcA\xd4\x91\x16w\xa1=\xa5\xf3\xd9b\xa2y\x9fE\x07\x17Y\xd2\x1c\x8fe\xeen\x98\x8aX\xf2mCQ }Q\x03\x13\xff\x08g.n\x88\xa0\xa0ps \x88B\xb5\x8eb~m\x10?\xc2)\xad 6\xf4\xa50\x9a\xb8:\x16m#Lq_\xda\xd6JAu,\x00\xbf@\x8d\xd7t\x80\xe1\xde\x85\x16\x11\xad\x1e8\xdd\xfaD\x8e\x8b\x99\x0d\x0b\xdah\x8d\x06\xd0\x9cod[G\n1\x04\xaf\x8dP\xfb\x96\n&\xb7\xa1e\x17\x9e\x9d)\x10\xf1\x0e\x85\x05c\x89\xf1\xbf\x11\x84\xc4Fy\xd3B\xd5\xd6\x08\xfau\x00\x9cSM\xc2\xb9\xf5\x0c\n\x02V\xde`\xfc\x851\xc2R\x18D\xea\x08J\xc3\xb3Z\xdaD\xabSQ\x97\xca\x90\xd7cS\xa9<\x12\x1b\x1f\xcb\xa3j!>\x02B\xf1\xacs\xf1\x80f\x17\xb8\xd5\xe1\xd1\x12\xe3\x8e\xdbg\xf9\xe9&\xc3M\xe5zO\xad\x1fP\xf7\x19W\xedz\xc9x\xdf\xc6C\xea\xd0\x18g\xcfvv\x0b\x8a:\xbf\xd4\x03\x9cc\xf9\xe31\xa2#`\xf1\x11PO\xb9x\xc6\x8d\x91\x18\x10%\xd1\x93\x91\xb0H\x895b%\x86GK\x0c\x8d\x97\x18\x1a118fbP\xd4\xc4\xe0\xb8\x89\x81\x91\x13\xc3c'\x86GO\x0c\x8c\x9fX7\x82\xa2\x9f\x84\x17\x8f7\x8a\x02\xf0\xad\xfb\xcb\xa1q\x14O\x16I\xf1\xd4\xb1\x14O\x11M\xb15\xf1\x14\xcf\x12Q\xf1L1\x15[\x15U\xf12\xe2*\xb60\xb2\xe2yc+\xe0\xd1\x15\xd0{5\xf5@\xef\xd7\xd43f\x8c\x05\xdcv\x1c)\xcebP\xa4\x05\x14K}\x97(\x9d+\x847h\x13u\xf6\xf8\x1fd~\xd2M\xc1\xab\xcd\x05\x03\xc0Q\x1c\xad\x91j5\xc3h\xe3y<\xb4l\xd8g^\xf4\xcb{FX/>i\xde\xef2\xfc\xb2\x04\xe7\xf9\n%9\xae\xeb`\xddf\xf9\xd1\x08\xe4n\xe0\x0c\xac\x0e,\"\xdd6#\xb9\xb8\x94g\x1a\x99\x99\x00I\x9e\x91\xa29\x0f\xf3\x88\x89 \xbc\xee\x12\x8d\x1e\x82\xf12\xcd\x82\xdc\xe9C\xcfg~\xfcN\xd1d\xb5\x8b\x96\x8bT\xff\x9bfsRS<_\xd4\xbb\xda}&\n>\x86\xbb\xa4\x8aD\xb1\\\xf6\xf1\x9b\x96~2\xc0'l\xe8\xf9Zx\x12\xd2kFDLp\x02\x17\x102\x16\x11\x03\xbb\xc7\xd8\x12\x03-\xf6\x98\x8cc\"\xfb\xec\x13&Ae\xac\x00)h\xb5\xe2\xdd\xfb$\xbe \x9a&\xab\x11)\x82I\x01$O\xdc\x1e\xbf\x820Q\xe4\xea\x89\n8\xa4\x03\xca\x82?\x93+\xf0\xc5La\x8ek\xaa\x90\x06\x11\xb6\xa5\xf3\xc8\xc3\xf2\xb2\x11'R\x8a\x0c\x18\xb1\xbe\xaaT\xf6c\xd4\xa8\xfa\xf0>\x06\x17\xcc\x19f[K\xdb\xa5%\xe7\x04gxy\xfa\xa4\xe2\xd6?\xc0\xdf\xc4e8\xc1\xc9L\xcex\xf0\x03oe-\xfb\xd9\xc8\x8a0\xe9\x96x8\"\xfbb\x1bE\n\xa8\xac,\xf6\x05\xbd\x88\xdc\x85\x8fI0\x0c\x8f\x99\x8a\xfb\xca\xb4k\x8d\x12\xbc\x10V\xa63j\x87\xed\xc0 (\xbd\xceK4\xc7\xb7D.u\x95\xbb\xc7\xd4\x95\xdc\xd8d\x85\xeeI\x15\x991\x9c\xfa~\x00U\xba\xb2\x12]\x13_e\xd8R\xda\xba\xc078+jj\x9cf\xbc\xf0l\xd7;\xfb\n\x17 D\x9f]\x19nW\x9e)?\xc3w\xc4\x18Ir\x98\xc7\x94Qu$\xcd\x88_,d\xc5]\x99\xdf\xd9i\xf6\xed\xe7\xf4\x8c\x9d\xbc\xff\x92\x9c\x96y\xf1E)B\xc1.\xacHc\x84e\x04\x9aUL\xa0\xfd4\xf1hX\xb1T\xa4\x8f+\xfe\xf2\xd4\xfeyy\xe7\xeb\xd1\xdc\\\xf2\x8c\xd2\xb7\xa9\x05nXD\x843&\"l\x0e\x02l(\x88\xfd\xf4\xf4W^\xed\xc0-\xab\xd4\x82;\xe0\xc1.\x07\xe2\xb7,\x8d\xb9\x88E2l\x94\xa6\x88\x90\xeb\xa094\x9c\x01Y\xc5\x1b\xe2\xbf\x01\xc5\xb3m g\xbaAR\xc6\x1b`3\x0dj\x95\xf2\x80qH\xfe\xf3e\xf0\xc8@\xb6qH\x18\xef$\x9f\x82\xf0\xecM\x06\xe2S8\xd0\x03\xcc\x9c!T\x8f\x10\xeb\xd1o\xd7\x8c\xb4\x10\x86\xd0\xbaV\xb0\x87w\xa1\x04HVA\x85c6\x7f\xb2\x80\xa2\xb1\x1b\xe4Bf\x12\xa4+\x11P_\"\x04 \xd5\x16\x0fpi \xb8\x9c@\x9b \xde\x16\x8f\x9e\xa9\xb8\xeeDOC\x1d\xe08\xd3Bz\xb8&5\x81\x01\xf9\x04\xd3\xa6h\xfbx5\x86nE\xe8W\x87\\8\xe5#/\x92a\x94\xaf\xa9O\x90{ \xb9\xc9\xeftG\x1a\xe3\xac\xe5\x04:\xde\x89\xcb\x1d\xd5\xb6\xe1\x03X\x9b\xa6\x11l\x0d\xa0\x08\x81r\x1d\xf5\xe8\x12\x18\xbf\xd0B\x1d\xd7ET9u\xa6=\xae\xa0\xb6\x87Sk+\xa66\xc0\x1e|Jz5`\xdb\"\x9eu\x11G\x80\xae\x7f\x8e\xafZ\x11\xedCy\x19S\\@\xd6\x0d\xe7 Di\x05\xc1\xadC\xfdH\x0bg8\xf5k*.\xc0\xf2r\xb1\x02\x82\xaft:\xfa[\xf5i\xdf\xd5S\xf5\xea\x93\x03\x06\x9a\xf5\xe9_\xe8\x9a\x8e\xc8\xd7\xaa\xcf(\xf2\x18\xec\xd4\x97\xb8\x1b\x1a\x89\x85\xf1\xb4\x99\x02\x02\x19\xdfr\x1dIi?yT\xc6~\xb38\x9a\xb0jA\xeak\x84\xfe\xe4\xe5\x0cd\x92z\x10\x96\x8c\xe4w\x94\xd88\xce\xeb\x12\xdd\x16\xe5}\x810[\xbf_\x99!\x14\x0c\xd7x\x0e\xd71\x8ccM\xa2\x82\xda~\xea\xdeCe(\xb4\xb2\x10\"\xa2S\xf3\x1a\xed\xf0\xca\x99\x19\x9d\xa1i\x96SR\x91\x14\xdd\xde)\x0dOI\x85iY\xf9\xc3\xccd\x0c{\x90] \x02% %o,\xcbWQ&\xa6:X\x12B\xcb\x9c\x95}\x1bU\xf2\xb8\xber:\x95\x11tv\x0fM(\xb3\xa2\x8bb4\x87\x0c0\xbd\x02\xb8\x14\x11x\x1a\x10\xea\xdc\xb0w\x82!\xf9t\xa8-\x1b\x85&'6\xf8\xbb\x19\xaeg\xe3\x93Jes}NDa\xd4~Qk-\x9av\x11\x13\x8ah\x10^\x80)\x90u\xc4\x1d\xb7\xd8\x1a\xf9\x1d60\xef\xa3\x12\x8ebb\x8f\x0eT\xe3\xa5\xe8\xed\xd9L\xca\xf9\xbc,\xf8x\xe1hR\xd1\\\xecY\xd8!\x86\x16\x81j\x8a\x8c\xe6\xd6\x94\x96\x90\xcbe\xf5\xe8\xfa\xd7hG\x80\xfd\xad\xb9\xac\xe5|\xda\xd7\xac \xde\xda\xaa\x87\x89\xde\xac\xb8+o\x03k)+\x16K\xfabS\xbd \xbb\xa0\xd7\xc4C\x8c\xbe\xf6s\xca\xa6]\x149R\xa5\xd3\xd9\xc4\xe7Yq\x8b&8\xb9\x95%\xe4\x01\x90x\xb4\x02O\xda\xe0\x8b&\xec\xeb\xd2\xa5\xde\xe3\xce\xae\x9e,\x00\x9a\x07\xe2\x81o\x15\x87\xf8\x96\x8a\xbe\x89\x1e\xff\nJ\xb9\xf8RS<\xc9\xb3zFR\x15\xd6\x10\x0b7\x87\xc8\xf1\x9el\xeaK\xb9[\xe0g$\xe1) \x86U\x01\x80\xc7\x18\x85\xce\xabrQ\xd6p\x1eh\xb1\xbc\x19F\xf0\xae\x9b\x0b\x85\xd3\x94\xfb\xf6h\xb5Lx\\\x11\xb7\x91\xe6\xb8\xaag\x91\xf0u\x84j\x8a\xe92\xba\xf5\xfb\xf1\xffDg\xbddSa6q\xc1\xc7E\xa4Z\x86\ni\xd0\x04\xe8\x84\x8c\x1fK\x1eA$\xa2\xe5\xd9\x14\xf3\xfb\xb2\xc52.\x9e{\xf1\x18\x9e5y\xf1\xe5\xf3\xd9\xc5\x1f\xd7'\xa7\xe7\xdf\xaf\xae/\xaf\x8e\xaf\xbe_\xf6\xcad\xf3\xc18\xbf8;?\xbb\\\x03\x80x\x17\xfd\\g\xe2\xadKH\x7fI\x1ee\x1ed\x06\" \x8c\x04,@j\x08\x8f\xfb\xc2y\x96\xee/\x0bq^\x14\xeb\x96\xad\x1d\xc0\xc7\x91\xa9t\xf3X\xfd\xb5\x9d*f\xec\x98\x02\xe1j\x92\xd1\nW\xabF\x82\xf1\xba\x85\xfa\xcc'\xb6\xc2p\x1c\xc5;7\x86\xe2\x9d\x1b\xbfLlh\xcb\x02[T\xe4.+\x97u\xbe\xealu#\xbd*\x88\xab\x146W\x15Nn\x85oLXN\xfa\x04H\x94N\x82\x9e\xca@\xd2\xcb\x1c\xa4cg3\xda\x92YF\xeeD\x8f\x95rIcb\xa7,@\xd8 H/\xd6*|f]\xff?R\xcfSUcU\xb0\x13V\xb0A<\xe2\x8b}\xed\x05h\xbb\xa1\x9eA\x83^r\x98\x8a.\xb5\x04\x1b7Fs \x04\x00k2\x9d$o\xe4V\xce\x8a\x1b\xd5\xb6gw\x8a\xb3|Y\x01\x8e\x90\x88\xa9\xe1\x05)R\xd0D\xf6\x99\xf5>J\xf7\xf2\xfb\xb7AZ\xaa\xfb\xf5\xf9\xf1%,\xb9\xde\xfe\xec\xf2\x7fO\xce\x07|\xf6\xf5\xf8\xe4[\xf43C'\x0f\xa5s\x986\xf6\x8c\x06\x99\x14\xef\xc7\x96\x06F\xcb\xa2&q[\x8d\xdfk\xf4\xc9\xa7\xefNi\x9by\xec\x9d\xa5\xc2\x88\xb1\x9b\x98\x06m\xfaW\x0d\x19\x92-\x87\xf6\x90\xec\x9d1\xa4&i?)\x8b:K\x95\xf7\x81\x0f~\x9b\xf1\xfb\x8bT\xf4\xf2\x9agu\xcd6\xa7\xd4Ge\x85R\x92\xe3\x15I\x81)\x8a\x1e$\xd9\xe2k#\xc9\xdey\xf9\xd2\\-:1g2\xc3\x17\x8a\xae\x1e\xa8\xd0\x13\x87\xaa3!\x9fH\x91\xe0E\xbd\xcc5JRn\xf1S,\xc4\x8f\x88\x1a\xcb\x03\x90\x9d\x0c\xc3Q\xe0\xb0S\xff\x86\x9a\xde\xfc\xcb\x9cr!-YV\xdcX\xe2:2K\xec\xa7\xe2 \xa3>h\xca\x15g0\xd3\xe1e\xd4Xjc\xa9\x18\xd8\xdce\x00\xf4l\xed,\xc7\xe4,\xb9$/\x8f<\xe0\xe2\xb3}\xac\xafD\xca)\xb7\\\xf9\x94cJq2\x13\xa3\xe9\x8cV\xb67 N\xfc\x05\xb6\xed\x1d#\xd71?\xbbG\x13\xb7-\x16K\x8c$\xe7\xcaB:4\xe4k\x99\xed\xe4&Y;\xaeF T\xb2\xa1E\"\x94\x8cH$/@k l8\" \xe4\xc3\xdb\xe0F\xd1\xe3{\x83\x8c\xe4\x84F\x03\x87\x9ai\xd8\xe6\x88\xa16\x96\xc3C\x854$\x08K`\x81\x89\xdb\xc0\x961\xc2U\x9be\x03d\x8fp\x16D}\x93\xdb\xc0\x1f\x03U\xc5 \xe9\xf5\xe3\xaf6\xc2\xa1g\x88\x882F\x8e\x85B\x8d\xb1S^`6\x04\x80*\x90\x96@@M\x81~%C\xc8\xe7 \xa8\x8b\x9e\x0e~%C\x0c\xe2\xd5\x18\xda\x05\xa1_\xc9\x10p\xcaG^$\xc3(_3\xa6\x14\xf5I\x86\x90ry\xccT\x08\x07\xc8\xf1\x12!\xa4}\xfd\x94i\x106=#X\x16@\xd1\x01\xe57\xea\xe0\x189\x9d\x84\xf7A\xbf\x04\x88\xd6d\xc7U\xd2\xb6\xf0hmEd\x83\x03sh#\xf1\xeb\x9b\xe7\xd6\xa6\xd2\x1e\x06r\xf1\xa5\x1c\x81\x1c\xf8:\xceAAH#0,\xa6\xcf\x81l\x1aJ\xff(\x9a\x1cJw'\xa3b\xcc3\x92\x13\xb8\xf7\xac\x04\x02\xc5\x05.\xcaR\xff\x99)J2H\xd3!\xa0\xb6C=\x13\xff\x10|\xfd \xf8VC='\x06\xf5H\x04\x04\x03\xea\xab\x0b\xd1\xb0\x84@\xb4\xb5\x1c\x8c\xe9\xc8(@\xda+\xb5\x0b\xc1\x12\xa2^(/\x87i\xd0.(\x87\x16\xed \xa1\xdf\x1c\xc4\x94\x07\xea\xc7\xf2\xf5\xf8\x18U%Qx\xbd\xd3-Q\xef\xa4C\xf4\x94\x1c\x19\xe1\xb0\x08X\xaaC\x93\x10\xc5\xb5\xb0?\x07Q\x1e\xe76\x91\x82(@\x07\x92\x0d\xe5\xd8\xf2\x1a\xb8\x9b9\xc8s\n\xbb\xf3\xdc\x1a\x84\xffJ\xc4\xd1%\xe5b\xd5\x98i\xe2\x0fv~\x12\xe2\xd9\x8c\xbe!#z<\xac\xbd\xa3\xce\xef\xe8\x8a\x84\xccg\xc7\xd5\x8d\xd56T3)\x94\xe4.\"\xafo|{J\xfc\xf4\xe0\xe7\xdb\x1b\xf2\xe6\x11?\xd2\xe5\x87C\xfap\xf8p\x98\xe7w\x87\x0f\xc9\xa7GZ\xdf\xbf\x7f\x9b\xfe|\xfb#\xbd\x9b\xa7\xf8qy\xff\x98\xe04\x9d\xcd>\xde\xcc\x97\xef\xe6\xc9#y\xe7\x82\x1c\xf6\xf6\x8eD\xbd\xf6\xe3&\xb8@$\xe3\x15\xc8'\x04a\xbe\xf5\x82D\x7f:\xf8\xf4\xfb\xc7 ~\xbbw8}w\xb8\xf7\xfe\xf0\x13\xde\xfb\xf8\x01\xff\xbe7% >\x98\xbc9rx\xfbe\xc6\x84\xb2\xc8W\x0d\x0bP\xc6\x13\xf7\x8c\xeb\x03\x9c\xd7\xa5\x0f?\xd9\x1d\xc3)\xae\xfdg\xe5\xe8d\xca\x90\x0b\xf3\xf4*\xb2\xb7,\x9f\x1c-Q^\x96\xb7L:;\xa0\xc8d\x1f\xe1\x90\x0c\xe1\x11\xea\x03\xa0\xed\xb5\xc0o`+\xcfBG\x08\xb2i\x8eo\xb8j\xd1\xad\x05J\xf53N\xa6_\x95\n R\xbb\xc9(\xa2\x9a4\x1aH\x1d\xe7\xeaEY\xd4\xce(\x0f\x8d\x8eL\x80\xdf\"\x06\x99I\xfb1\x1e\x85\xb7M\xbd\x16\x8b\xc8\x83@(x\xa4\x7fJ\x0e\xb5\x10\xf23H\xfe\x90S\xe8\xbd8\xe5 x`Q\x8c\x11jf\xbc\x16\nzbF\xb4\x10\x82\xac\x14o\xbe\xb1\x86Q\xa0U\xb9\xac\xa4\xa9d\xb3!\x8c\x95L\xef\xba\x90\x1cDM\xb4\x9f\xf8?\xe3\x8c\x8c\x10'\xe8\x9fKR\xad\xf6U\xb1\xdf\x8b\xf3\xcf-`\"\xb3\xb4\x19^\x85\xf6\x1a?\xb3\xb09.\xd0\xb2 \x0f\x0b\x920kO\xf4\x96R#\x1b\xdf\xd4\xc9\x8c\xcc\xb1=s^\xdb\xcfo\xf7q\xf8\xdd\xf9\x0f\xc8\xf3\xa4L\x1d\xc2W|\xe0+y\xae\x16JV\xd0wo;\xfc\xf1\x94\x0b\x0f\xe0\x90\x12\x8a\xb3|Kjr\xb0\xcf\xaf\x97\x95\xb7\xabMT7\xf2T\xd6\xc1_;]\x10\x0b\\\xe19\xa1\xa42p\xde\x13\xfa\xdb\xb0\x98|K\xb0\xb5!\xfa\x9bXC\x0d+mBY\xd0\xba\xe6\xd4\xe8F\xd4\x1a\xa6\x93\xcb`\xca\x8a#\xb4\xc0\xd4\x8c eR)\xabHz\x84h\xb547\xb2s\x82\xd5\\\xb5\\\xfb\xd6,\x0d\xb2\xa5F\xc3\xcc8\xc4\x01\x97\xd1\xe0c\xdf\xb8\x87=\xc6\x81\x9fLf;Y0\xc5y\x0d\xe6\x81Z\x18@\x06\xf4=\xf9\x0d;\xef\x85\x8e!C\xces#\xf2\xcb\xb2\x98\x81L\x1bfe;\x8e\xbeC\x0c\xc7!\xb4w\x8d!\x9f\x99\xd4a\x8b\xb4q\xfbr\xa6\x8fy\x8d\x1c\x83l3oZ\x162\x905\x03\xed\xea\xc6\x82\xb6\xa0=/\x07Z\xa61\x90\x03\x83\x0cj\xc3t\xb6\x80y\xcc\xe8M\xd2O\xf1\x8de\xba\xfcS\x8e\xf3j\x9f\xe9[R\xe0\"!\xfbsBq\x8a)\xde\xbf;\xd8\x97\xebz\xff_\x8d\xb5\xf2\xef}\xb9\x8c_ H7D[\xdd\xf5r>\xc7\xd5\xeaH7\xe9\xa8 \xae\x92\x99\xec\x00*?S\xb4\xfa\xf8|\xd5\\\xb02\x99\xbb\xcb\xac\x05e\x0d\xec\xaa\x82e\x11=\xa7\x81\x8d\xa0\xe4\x18B\x1a\x1e\x17\x80{\x1de#\xc4\xa8\xa1p\xf4\x07pm\xe3R3\xd6\xb0\x10\x0d\x83.\xb3y\x96\xe3*_\xed68P\xab\xe8\xb9SO\xf2\xb2E-k\xb1E\xc9\x18\xd6\xa1\xc5\xcb\x88\xa5\xb5*\x97\xaf*\xd2\x8c\xcfNA\x15/&\x88\xb2\xc2l\x1f\xf3_\xe8d*,L\xdc\xb6\x86\x9au#;,\x9aw\x1b\xbc\x9f\x8dQ\xfb\xae\"tY\x15\xbc5\x8d\x0b\xb4i\xaf6pq\x9e\xdbb\x9d\x0d\xc3g\xb2/\xf8f\x82v\xd8d\xc8\xd1\xf6\x9b\xf7\xbf9Gk\xcf\x8f\x1a\xd7;\x1e\xd6\x8cWT\xe8f\x82\x8aB\\\xa4\xfb\xcd\x82\xb8\xcel\xfe\xb56\x97k@\xfd\x93c4\xc1\xa9y\x19\xa3~\x84\xb2\xe9\x91\x89\xd8\x95\xbdF\xb9`\xe5\x0b\x13\x17)*J'\xf7_\xb7\x00\xc8NA\xf4\xbed\xcb\x99w\x0b*\xa7\xb6,1I\xe2w[\x12,\xff\x9f\xb9aV(-\x8bWTr|*\xa4:\xdfH\x8cy\xfc3k\xf8\xe3\x0eWw\xd1dIQQR\x07g\xb1\xd5\x84\xa0\x0bF4Fl\x962#K\xcf\x16\xfb\xa3\xde3iIj\x86\xe7\x1c\xd3\xc4n\xc2e|o\xce\xc8?V\xcaW\xb2\xdb\x98t\xb2\x83\x93\xb4xD\xbb%\xaa\xcb@\x19\x18^\x12\xda\xb2\x1d%E\x1d\xb3\x89\xb1\xab\x12Y\xb7\x96\xa1dY\x90\xf6B\xd3\xaaAW\x10;I\xb5.\xf9\xf07E\x960\x19\x0c=\xf6\xea\xed\x9b7\xaf\xfc\x9e\x1f#\x03\xd9ip\x8c\xe4\xf3\xf1\xf8\xcf\xe3\x9e:m\x833\xfe\xe8\x82\xab\x89\xeaj\xc5\xab\xad\xd6-\x0br's\xc5x\xf8\xcb\xad\xaeu\x83\x19\xbd\x19\x08\xc47\xc5\xdc<\xb1\xebA\x04s\xd6\x00#F\xe2)\xaa\x08}\x17\xe5NO\xfe@\xbaZ\x85\xd8\xee\x08\x9d\xcc\x179o\xeeW\xa3:\xbd}}\x1cI\xc0\x94:k\x8a\x13\xe1\xd7\xe4u@\x85\x98\x12_\x12\xb1\xd7\x94u\xd76\x10\xed\xe7sY\xcfK\xdf`58_\xf9i\x99\xd9H\x98\xba\x9bf\xac\xa3\x0e\xc2\xb1\x10\xd6\x97\xc2\x94\xe4\xf5H\x88\x9a\x0b\x9c\xe7\xe5\xbdTc2\xbf;\x04\xaes\x86m\x1e\xde:>\x10\x83\x17r\x84\xaa'Z,&\xbag\xc4\x13\xdf9\xe2\xc1\x9b*d\x8a\xdd=@wT\xe2\x7f\xb8N[U\xe6\xd1\x02m\x90\x15$\x1e\xcc\xe15;R!c\x14%Nd\xb9^`\xf4\x9b,\x0d@\x00EJ{1\x0fVl\xc5\xea\x18\xdf\xa7\xfe\x88\xf5\xe1\xd9\xc5\xc9\x7f\x9f\x9c\x1e_\x9d]\xf4\xfb\xee\xf2\xcb\xc5\x9f'\x9f\xbf\xf4\xfc\xea\xe4\xf4\xcf/\x97\xbd\xc7\xfa\xfc\xfd\xf2\xea\xec\x8f\x93\xe3\xd3~\x9f\x9d\xfdu\xda\x17\xbf\xe3\xaf_O\xbe\x9d\x1c_}\xe9\xf7\xd9\xd9\xff\x9d\x9e\xfc\xe3{\xbcT\x8d\xf5\xd1\xf9\xc5\xd9\x9f_N\x8fO?\xf7\x1c\xec\xf3\xd9\xe9\xd5\xc5\xd9\xb7o}i\xfb\xf3\xf8\xdb\xc9\x1f\x80\x89\xd6\x15n\x06-\xaf\xb8\xadb?\xbeU\x8b\x0e\xae;\xa49\x1eKf\xdd0\x15\xb1|\xd4\x86\xa2@F\x9f\x06&\xfe\x11N\xe6\xdb\x10AA\xe1\xe6@\x10\x85\xca\xff\xc4\xfc\xda ~\x84\xb3\xfb\x84IP\x19+@\nZ\xadxC;\x89/\x88\xa6\xc9jD\x8a`R\x00\xc9\x13\xb7\xc7\xaf L\x14\xb9z\xa2\x02\x0e\xe9\x80\xb2\xe0\xcf\xe4\n|1S\x98\xe3\x9a*\xa4A\x84m\xe9<\xf2\xb0\xbcl\xc4\x89\x94\"\x03F\xac\xaf\x9c\x94\xfd\x18\xc5\xa5>\xbc\x8f\xc1\x05s\x86\xd9\xd6\xd2vi\xc99\xc1\x19^\xb1=\xa9\xb8\xf5\x0f\xf07q\x19Np2\x933\x1e\xfc\xc0[\x12\xcb~6\xb2\"L\xba%\x1e\x8e\xc8\xbe\xd8F\x91\x02*+\x8b}A/\"w\xe1c\x12\x0c\xc3c\xa6\xe2\xbe2\xedZ\xa3\x04/\x84\x95\xe9\x8c\xdaa;0\x08J\xaf\xf3\x12\xcd\xf1-\x91K]\xe5\xee1u%76Y\xa1{REf\x0c\xa7\xbe\x1f@\x95\xee\xa54\xa0t|\x95aKi\xeb\x02\xdf\xe0\xac\xa8\xa9q\x9a\xf1\xc2\xb3]\xef\xec+\\$$\x10}ve\xb8]y\xa6\xfc\x0c\xdf\x11c$\xc9a\x1eSF\xd5\x914#~\xb1\x90\x15we~g\xa7\xd9\xb7\x9f\xd33v\xf2\xfeKrZ\xe6\xc5\x17\xa5\x08\x05\xbb\xb0\"\x8d\x11\x96\x11hV1\x81\xf6\xd3\xc4\xa3a\xc5R\x91>\xae\xf8\xcbS\xfb\xe7\xe5\x9d\xafmqs\xc93J+\xa3\x16\xb8a\x11\x11\xce\x98\x88\xb09\x08\xb0\xa1 \xf6\xd3\xd3_y\xb5\x03\xb7\xacR\x0b\xee\x80\x07\xbb\x1c\x88\xdf\xb24\xe6\"\x16\xc9\xb0Q\x9a\"B\xae\x83\xe6\xd0p\x06d\x15o\x88\xff\x06\x14\xcf\xb6%\x9c\xe9\x06I\x19o\x80\xfd%\xa8U\xca\x03\xc6!\xf9\xcf\x97\xc1#\x03\xd9\xc6!a\xbc\x93|\n\xc2\xb37\x19\x88O\xe1@\x0f0s\x86P=B\xacG\xbf]3\xd2B\x18B\xebZ\xc1\x1e\xde\x85\x12 Y\x05\x15\x8e\xd9\x0f\xc9\x02\x8a\xc6\xee\x19\x0b\x99I\x90\xaeD@}\x89\x10$T[<\xc0\xa5\x81\xe0r\x02m&x[A\xee%\xe4&\xbf\xd30h\x8c\xb3\x96\x13\xe8x'.wT\xdb\x86\x0f`m\x9aF\xb05\x80\"\x04\xcau\xd4\xa3q^\xfcB\x0bu\\\x17Q\xe5\xd4\x99\xf6\xb8\x82\xda\x1eN\xad\xad\x98\xda\x00{\xf0)\xe9\xd5\x93l\x8bx\xd6E\x1c\x01\x1a\xe19\xbejE\xb4\x0f\xe5eLq\x01Y7\x9c'\x10\xa5\x15\x04\xb7\x0e\xf5#-\x9c\xe1\xd4\xaf\xa9\xb8\x00\xcb\xcb\xc5\n\x08\xbe\xd2\xe9\xe8\xef^\xa7}W\x1bh_\xe7\xc4I\x0e\x18hj\xa7\x7f\xa1k:\"^\xd2\xd1\x01\xcc(\xf2\x18(\xe9(\x7f\xd5]\"ba\x1dj\xcbF\xa1\xc9\x89\x0d\xfen\x86\xeb\xd9\xf8\xa4R\xd9o\x9e\x13Q\x18\xb5_\xd4Z\x8b\xa6]\xc4\x84\"\x1a\x84\x17`\nd\x1dq\xc7-\xb6F~\x87\x0d\xcc{\xeb\x87\xa3\x98\xd8\xa3\x03\xd5x)z{6\x93r>/\x0b>^8\x9aTt\x05{\x16v\x88\xa1E\xa0\x9a\"\xa3\xb95\xa5%\xe4rY=\xba\xfe5\xda\x11`\x7fk.k9\x9f\xf65k\x82\xb7\xb6\xeaa\xa27+\xee\xca\xdb\xc0Z\xca\x8a\xc5\x92\xbe\xd8T/\xc8.\xe85\xf1\x10\xa3\xaf\xfd\xfc\x7f\xf6\xfe\xb5;n\xdcH\x18\xc7\xdf\xe7S\xd4\xcf/\xd6\xf6Fn\x8f=\x97$~\x1e?g\xe5\xcb\xcch\xe3\x8bb\xc9\xd9\xdd\x93\x93\x7f\x9b\xdd\x8dn1b\x93=$[\xb2&\x9b\xef\xfe?\xb8\x91\x04\x89K\x01dk\xe4\x19\xd4\x9b\x19\xab\x89B\xa1\x00T\x15\nU\x85wt\xday\x91#Y:\x9d\xbd\x17\x95\xe6\x97\xb0H\x96\x97\xa2\x84<\x02\x13\x8bV`I\x1bl\xd1\xd8}]M\xa9w\xb7\xb3\xcb\x93\x05H\xf3\x80\x03~\xabh\xc4\xb7P\xf4m\xf4\xf8\xf7\xa8\x94\x8b\xd7U\x9d,\xb2\xb4\xba +\x19\xd6\xe0\n7\xc7\xc8qO6\xf9\x8e\\/\xf0S\xb2d) \x1d\xab\x02\x81\x8f2\nN\xcbbWTx\x1e4b\xf90\x8c`\x8fe\xee$Mk\xe6\xdb\xab\xcb\xfd\x92\xc5\x151\x1bi\x9b\x94\xd5\x85#|\x1d\xa0\xaa\x93z\xef\xdc\xfa~\xfc?i\xb2^\xd257\x9b\x98\xe0c\"R.CI4j\x02\x9a\x84\x8c\x7f\xecY\x04\x11\x8f\x96\xa7S\xcc\xee\xcbv{\xb7x\xf6\xe21>k\xf2\xc3\xeb\x97\xef?\xbc\x9a\x9f\xbc;\xfdx>?;?>\xffx\xe6\x95\xc9f\xc2q\xfa\xe1\xfd\xe9\xfb\xb3\x11\x08\xf8\xdf\x9c\xcd\x9bL\xbc\xb1\x03\xf1\x97\xe4N\xe6af\xc0\x81\xa2\x93\x80\x85H\x0daq_I\x96\xae\x1e\xefs~^\xe4\xeb\x96\xae\x1dDc\xc7T\xeay,\x7f\xed\xa7\x8auvL\x0eI\xb9H\xeb2)oZ \xc6\xea\x166g>\xbe\x15\xc2i\xe4\x7f\xd3S\xc8\xff\xa6\xa7/\xe5\x1bZ\xb1\xc0v%\xb9J\x8b}\x95\xdd\x0c\xb6z'\xbd\xcaJ\xab\x106\xe7e\xb2\xbc\xe4\xbe1n95'@\"u\x12\xf6T\x86\x92^\xddN\x06v6\x1d\xdb\xf2\"%W\xfc\x8d\x95b_\xbb\xc4N\x91\xa3\xa8\xe3\x98\xbeX\xab\xf0\x17\xd6\xf5?\n=_\xcb\x1a\xab\x9c\x9d\xb8\x82\x0d\x1cx\x8b\xc7\x8d\x17\xa0\xef\x86\xfa\x054\xe8\x19\xc3)\xc7%\x97`\xeb\xc6h\x0f\x84\x08dm\xa6\x93\xe0\x8d\xd8\xcai\xbe\x91\xcf\xf6\x1c\xad\x934\xdb\x97\x88#$P5\xbc#\xf9\n5\x91>\xb3\xee\xa3t\xcf>\xbe \xd2R\xc3\xd6\xa7\xc7g\xb8\xe4z\xb5\xd9\xd9\x9fON\x03\x9a}\x7f|\xf2\xc6\xd9\xac\xa3\x93C\xc7\x19\xa6\x8d\x0d\xbda&\xc5\xd8X\xd1\xc0\xb0\xcf+\xe2\xb6\xd5\xd8\xbd\x86O>\xfdpJ\xfb\xcc\xa3\x7fST\x18\xe9\xec&\xaaA\xdb\xf7\xabB\xba\xa4\xcb\xa1\xdf%\xfd[\xa7\xcbfH\x8f\x97E^\xa5+\xe9}`\x9d_\xa6\xec\xfeb\xc5\xdf\xf2\xda\xa6UE7\xa7\xd0GE +\x92%7d\x85LQ4\x10I\x17_\x9fH\xfa7#_\xda\xabE-\xe5Tf\x98B\xd1%`\x85\x1e?T\xbd\xe7\xf2\x89\xe4\xcbdW\xed\xb3\x86$!\xb7\xd8)\x16\xe3G\x84\xd6\xf2@d'\xe3h\xe44<\xa8\x1eB\xfb\xa6\xfe>\xab\x99\x90\x16,\xe3o\xd06\xe2\xda1K\xf4S~\x90\x91\x0d\xdar\xc5)\xcet\xf82j,\xf5\xa9\x94\x0cl\xef2\x10z\xb6\xd2\x96c\xd2\x96\\\x12\x97G\x06t\xee\xd9>n\xaeD\x8a5\xb3\\\xd9\x94'u\x9d,/xoMF+\xdd\x9b$Y\x9a\x0bl\xab;F\xaccvvw&n+,\x16\x14 \xce\x15\xb9ph\x88?\x8bl'\xfd\x90\x1b\xc7\xd5$\x81J*6G\x84R'\x12\xc9\x88PY\x02\x07\x8eHB\xf9\xf0\x0e\xb8Q\x9a\xfe\x8dAFbB\x9d\x81C\xed4\xdc\xe5\x88\xa1>\x95\xe1\xa1B\x0d&\x0cKp\x81\x89w\x81-S\x84\xab\xb6\xcb\x06\xc9\x1e\xee,p\xfa&\xef\x02\x7f:\xa4J\x06 \xaf\x1f\xfb\xd3A8\xf4\x0bDDuzv\x85BM\xb1S\xbe\xc0l\x08\xc4\xa8PZ\x02\x90\x9a\x02b2\x84\x80[\x18\x9d\xf3t\x10\x93!\x82x5\x85v\x01\x88\xc9\x10\xf8\x91O\xbcH\xc2F>2\xa6\x14|\x92!\x84\\\x9e2\x15B\x83r\xbaD\x08a_\xdff\x1a\x84:\x9e ,\x0b\xa4\xe8\xc0\xf2\x1b\x064:N'\xf6}\xe0\x97\x00\xd1\x9bl\xb7J\xba+<\x1a\xad\x88Tth\x0e\x1d$~\xfd\xf0\xdc:T\xdaC \x17\xbf\x94#\x90\x86^\xcd9\xc8\x8ai\x02\x86\xb9\xf49\x92M\xa1\xe3\x9fD\x93c\xc7=\xc8\xa8\x98\xf2\x8c\xa4En<+\xa1P1\x81\x0b\xe9\xca|fr\x0e\x19\xa5\xe9\x00\xa9\xed\xc03\xf1\x0f\xf0\xeb\x07\xf0[\x0d<'\x06<\x12\x01\xd1\x88|u!\x84%\x04\xc2\x9d\xe5\xa0KG:\x11\xd6^\xa9]\x80K\x88\xfaBy\x19\xa6A\x87\xa84Z\xd4\x13\x83\xdf\x1c\xb8\x94\x07\xf8\xb1|\x1c\x1f\x9d\xaa\xc4\x89\xcf;\xdd\x12\xbc\x93\x0e\xe1692\xc1a\x11\xb1TC\x93\x10\xf9\xb5\xb09\x07Q\x1c\xe7\x0e\x91\x82\xc8Q[\x92\x0dE\xdf\xe2\x1ax\x989\xc8r\n\x87\xf3\xdc\xeb\x84}\xc5\xe3\xe8\x96\xc5\xee\xa65\xd3\xf8\x0fj~\x12\xb0lFS\x97\x0e=n\xd7\xdeN\xe7\xb7sEb\xe6s\xe0\xeaN\xe46\x943\xc9\x95\xe4\x11\x90\xd9\xc6\xb4\xa7\xf8\xa7O~z\xba!_\xfd\x9c\xfc\\\xef\xbf\xfb\xb6\xfe\xfc\xed\xe7o\xb3\xec\xea\xdb\xcf\xcb?\xfd\\W\xd7\xdf<]\xfd\xf4\xf4\x1f\xab\xab\xed*\xf9y\x7f\xfd\xf32Y\xad..\xfe\xb8\xd9\xee\xbf\xde.\x7f&_\xeb0\xdb\xbd\xbd\x13\x8d\xbe\xf1\xe3.\x93\x1cH\xca*\x90/\x08$l\xebY\x07\xfd\xa7'\x7f\xfa\xc3\x1f\x17\xc9\xd3G\xdf\xae\xbf\xfe\xf6\xd17\xdf\xfe)y\xf4\xc7\xef\x92?\xfa\xd3\xb7O\xd6\x8f\x9e>y\xfa\xe4\xbb?%\xcb\x1eCyg\xa3X\xcaQ<\xf9\xe9\xb3\x91\xa9\x7f\xaa~\xca\x96\x17_W\x9f\xaf\xf3o\xbe\xf9\xc7\xb7_\xfd\xe3\xe7M\xfd\xc7\xb2\xba\xb8\xfa\xe9f]\xfecY\x9a\x86\xc3\x9e_\xa6L(\xf2\xec\xa6e\x01\xa4,q\xafs}\x90dUa\xa2O\xbc\x8e\xa1\x15\xd7\xe6\xb3\xb2s2E\xc8E\xf7\xf4\xca\xb3\xb7\x14\x9f\\]@V\x14\x97T:k\xb0\x88d\x1f\xee\x90\xb4\xd1a{\x07\xa0\xb1\xd7,\xdf\xe0V\x9eB\x0e\x17d\xeb,\xd90\xd5\xd2<-P\xc8\xcf\xd80\xcd\xaa\x94#\x11\xdaMD\x11U\xa4\xd5@\xf28W\xed\x8a\xbc\xd2Fy4\xe4\x88\x04\xf8;\xc4\xa0n\xd2\xbe\x8bG\xf6mS\x8db\x11\xf9\xcc \xb2\x1e\xe9o\x93C=\x82\xcc\x0c\x12\x1f\xb2\x11\x1a/N\x19\n\x16X\xe4b\x84\x9c\x19\xa3\x85\x02\xb7\xcc\x88\x1eA\x98\x95b\xcc7np\xe4pS\xecKa*\xa9l\xb0S%\xd2\xbb>\x08\x0eB\x1b\xed\xc7\xffM9#\"\xc4 \xfceO\xca\x9b\xc7\xb2\xd8\xef\x87\xd3\x97=d<\xb3\xb4\xed^\x86\xf6v>S\xa89\xcea\x9f\x93\xcf;\xb2\xa4\xd6\x1e\x7f[J\xf6\xdciS-/\xc86Qg\xceh\xfb\x99\xed>\x86\x7f8\xff\x16y\xbe,V\x1a\xe1\xcb\x1b\x98J\x9e\xcb\x85\x92\xe6\xf5\xd7O\x07\xfc1\x94\x0b\xb7\xd0\xb0\"u\x92fw\xa4&\x07m>\xdf\x97\xc6Wm\x9c\xba\x91\xa5\xb2\x06\xb7\xd6\xba vI\x99lIM\xca\x0e\xcd\x8f\xb8\xfe\xeeXL\xa6%\xd8\xdb\x10\xfe&V\xa8a\xd5\x98P\n\xb6\xa195\xb9\x115\xc2t\xd2\x19Li\xfe\x0cvI\xdd\x8d \xa5R)-\xc9\xea\x19\xd4\xe5\xbe\xbb\x91\xb5\x13,\xe7\xaasTBNV\xf0\xe1j\xda#\x15\xe5\xc0OT2jY\xb0N\xb2\n\xcd\x03\xc9~$\x03|\xcfWa\xa7*\x9b\xb1\x1frj\x9a\x90_\xbd\xeb \x85Wx\xfb\xfb@\xc4)F3rF\xc3\x0cm\xcd\xe97\xc4v\x0c\x19\xfb\xd0\x1e2YJ\x03\xb6\x083\xd7\x973>\x166h:\xb9\xcb\xbc\xe9\x19\xc9H\xd6\x04\x9a\xd6\xad\x11\xad`\xfbe9\xd0\xb3\x8e\x91\x1c\x08\xb2\xa9;\xd6\xb3\x82\xcc`I\x1fr\xfcu\xb2Q\xac\x97\xbf\x88~\x1eS\x8dK\xf2$_\x92\xc7[R'\xab\xa4N\x1e_=y,\x97\xf5\xe3$\x13\xb6\xd8\x864fv\xb5\xdfn\x93\xf2\xe6\x99\xaciW\x1dg\x19\x94\xa4.SrE\xa8Z\xcf\x9am!\xc7\xd7T\x879Y)\xcd~'\x87\xc9\x97C\x87\xc6\xfbO\xbf\xfa\xea\xbe\xd9\xb0\xef$\x98i\x17\xd3D&\xbd\xd9\x01\xf0KX\xc7\x82\x1a\xbby;:\xc0\xca\xe5l\xe4\x80\xb0\xa5\x01{\xa5W}\x11yW\xf1m{-`\x96\x14\x07d\xf15\xf4\x1c\x8b\x8f\xe3\xdb\xf6\xe6&>\x8c\xc4'\x8c\x07>3\xde\x7f\xf88\xbem\xafm\x14\xdf\xb6\xefB|\xdb^\xb3a\xe2\xdb\xf6\xde}\xc4\xb7\xed\xe3\xdb\xf6\xf1m\xfb\xf8\xb6\xbd\xeb\x8e\xad\x0b\xf1m{\x13\xc4\xb7\xed1\x85c\x96\xf1m\xfb\xf8\xb6\xbd\x11\x90'l\xec\xf9:\xbem\x8f\x1c\x11N\n\x00\xb8\xdfD\x8fo\xdb\xc7\xb7\xed\xb5\x10\xdf\xb6\x8fo\xdbk\x01Ga|\xdb>\xbem\x1f\xdf\xb6\xef\xde\xaf\x9b\xec(\x84\x0d\x85\xb1\x9fn\xff\xca\xab\xc7\xdaNX\x98\xb1$\x8d\x1a\xd6e\xb6,;s\xe1\xcev<\xe0\x98\x1cBn@&\x04\xe7\xfe\xb7\xa8P\\\xc1\xd5\x16\xb9\x13\x9c\x99\xa2\xe4L\x8d|\xfd\xba\xdb\xad\xf8\xdf/\x83G\x1db[\x87D\xe7o\xf1m{+2\xccX'Z\x08!c\x1d\x99QiX(\x96!/\xbf\xb8jn\x98\x99D\xe9J@\xeaK\x00W\x82_\x0b\xc8\xa5\x01x9\x01\x03\xed\xb9\x8c\xe5\xdcb97_^M\xa1[\x01b97\xfc\xc8'^$a#\x1f\xa9O@\xbf\x84\xf4\xc3\x1f\xa4\xf2Oq\xd6\xd2\"\x9d\xee\xc4\xa5\x8fj;\xf0\x01\xac?\xa6 l\x0d\xa4\x08\xc1r\x1d\xe5\x05\xc9\x19+\xcc\xe8\xd2\n\xf6yr\x95\xa4Y\xb2\xc8lW\x8c\x00'\xac\xd7\x8c=2(\xe7\x86\xf6\x99\xc3\x9e\xbdDvI\xc2&J\xcc\x86\xadk\xfbDe\xe9\xd6\x16<\xf5\xcb\xcc\x13#JZ6uQ'Y\xa7<\x9c|\x16\xac.\xe8\xd0:\xbb\xcc\x82\xb0n\x9e\x13c\x9b\xc4>Uk\xc8\xc8\xba\x06\xb2\xdd\xd57\x90\x8aKa\x91u\xc0\x1f\xa7\xe2\x1b\x9c\x13@\xe7ba[%,\xfa!\xd9\xedL3\xc0\xae\xe3\xe7l\x94\xaeyp\x85\xa9b\x14\x19x\xcdD\x878`\xd1\x8e\x8c\x03u\xb9'\xd0\x0b\x8aN\\%u\x05\xff)\n!B\x84\x12\xb4MF\xc2)\x90\xa6V\x7f-\xb0\xb45h\xb6 \xe8K[I\xe8h\xb14\x87\x8f'\x95u\x1d\xf4\x86\xcejWPc\x84\x97\x8ea\xe2\xa1\x95'Td\xcc\xe0\xc4v\xbeO+H7yQve\x8b\xfds\xcb\xb6- =\xe0XBe~\xa9\x05#\x08\xeb/\x96\xb4\xdd\xb7IIz{\xd7\xc6\x05~\x9fA\xfb\xe7O\xa2BQ\xaeHi\x0b\xad\x008K\xf3%y\x06\xcb\xa2\xda\x16\xd5\xa3ju _\xcd\xbe\xf9Z\xdb\xc0\x15~\xc3\x15wc\xccq\xba\xc9vAV+\xae\xd97\x1fN_6\xa6\x9a\x08\x1c\xaa,\xeb\xb0\x91\xfe\xc6\x12q\xcd\"\x9d\xc1\xeb\x813\xc7f\xea\xb9'\xa9k\xc6\xc9\\\x16\xe6}\xe9n\x8c\xd6Y\xc86\x9d\xd6>5\x1b\xe5#\x8e5\xd2.\xb2\x99\xf1\x16M\xe4\xb4\xe6\x14\xfe\xfc\xafi\xc6\x1b\xebL\xa8\x1fn\xa8\xd1i\xdf%\x95\xc8\x05\xe9\xac\x8a\x99\xd5\x90c6\x9a\xd3@;\x11:\x86--\xa6tL\xfa\x7f\x18+d<_\x02XT\n\x9a\x99\x16\x95\xee\xda:\x8d\xe44)\xf0Vl\x1b\xc7\xdbetG\x1e\x9b\xf6\x0e{\xea\x95\xd4G\x90\xd6\x95P\xd2\xccF\xe3\xe7\x99\x15\x14\x94\x7f\xd7i\xa5\xae\x0f\xfb\xbe\xe9\xd4&\xf0)Y6,i \x81\n\x8cX\xb6,\x96-\xb3o\xc0[.[\x16\xcb\xc0\xfc6\xcb\xc0\xb4\xe3\xef\xd8\x1d\xea\xa9\xd7\xa66\x03\\\x1e\xc2\xb1\xd1\xc5\x12\xe2\xc9\xe8\xf9+\xba\xe8\x8c\x0e\x8a\x10F\x0e6\x99\xd6\xcc\xd00\x91\xd3\x87\\F!\x8e \xbb\xfb\xc1\xd7\xe1\xe0\xe4\xa7\x8a\xfe\xc0\xcc\xed\x99\x1d\x1a\xf62\x17\x01\x92\xbba\xee\x84\x8e\xd3@\xc1\xa6q \x04\xb9\x0c\xb4\x8e\x81[\xe6b\xd7\xa6j\xdb\xdbx\xe9\xeb\x10\x18\x1e\xfb\x15tZ\x17\x80\xff\xa1_=\xc1(\xc8\x86\xc7\xfc\xa0\x83}\xe7\xf8\xae \xd3\x1f\xe5\x87\x87\xf7\x90\x89\x1d%\xc4\xc5A\x1c9\xad!\xc7v\xb1G\x14<\xae\x83\xba\xf3h~H>\x99j\x9e\xdd7\x15=c\x9b\x96\xd5Z\xa8.\xd2\xdd\xe3\x7f\x8a\x90\x8c\x7f\x892d\xba\nh\x1d\xbe\xfe\x95\xb6~/[\x0b\xb6u\xe2\xca\x94\x9bM\xb6_\xb2\xb4\xe2\x9bF\xe4\xc8\xf7^vOxc.J\x18Yr\x85)\xd5\xd4\xd4~\xc5\x17w\xb7\xa0Z\x1315\xad\xe9n\xb0\x8e{cd\x1c/\xd6\xcd\x84T\xf0\x80\x92\xf2P\x97\xfb\xa5\xce\xca\x97~\x89l\xad\xbd\xe58`\xc4{W\x06\xf1\xdeu\x00\x88\x93)\xc2S\x07h\xe6A\xd8!\xc4\x82-\xde\xbbb\x9c\x7f\xe05C!\xc7\x1b\x0b\xbax\xef\xcaa\xf2y\n;(Y\x10\xc6{W\x05\xf03\xe1{\xcc\xb2\xa0\x8a\xf7\xae\xf1\xde5\xde\xbb\xc6{\xd7x\xef\x1a\xef]\xbf\xa0{W\xd5\x8b\x81\xbaz\xeda\xe0\x17\xb1=/L\xbc}\xe5\x10o_\xef\xd0\xedk\xdf\xd17\xe1\x1bG\xf1Z3^k\xfa\xdc\x19\xc5k\xcd\x032\xd7}!\x17\xaf5\xa7\xe0b\xbc\xd6\xc4\x9e\x9b\xe3\xb5\xe6o\xe4Z\x932\xf2\xf1\xd5\x93\xc7YQ\\\xeeqW\x99\x1f8\xf3\xde\xb0\x16\x8c\xfaT\x16\xba\xcf263\x15,\x8a}\xbe\xea\x94\xe7\xd3]\\*\x97\x93\n\xd2\xdfIF\xdc\xd1\xbbI}\x95\xe9\x11\x16\xad\xc1h\x94/\xd6\xe4\x1c+\x95\x06x\xf6J@\xbb(4\x17N\xdd\xea\x97\x9d\x9feH\xab\xf1J)z\x1d\xba\x10\xbd\x0e_\xbc\xd7\x81\xc9NEF\x85{\x1e>\x90\xaa\xc8\xaeHt7Dw\xc3]w7(\xebO\x96\xff\xa9\x0bX\xa7\xf9\x8a?\x7f \x1f,U=\xec\x93\xb9)\xe21\xdd\xef\x0c\x14\x8f\xe9\x07d\xae\xfb\x80\x19\x8f\xe9Sp1\x1e\xd3\xe31\x9d\xc1o\xfa\x98\xae;\xa53\xed]\x99\x8f\xe6\xa7\xec\xf7\xe6L\xce?\x97+\x96\xe9\xebm\xb1\xdagD\x1b\x1f\xfc.\xd9\x12\x8e\xe0wr\x84w\xf4\xfc\xddeC\x17z\xc7Y6xy\x94\xe5\xf7\xeaM\xac\xa4\xe0\x89\xca\x8e\x16F\x1c`\xb7\xc9\xe7yE6[\x92\xd7\xf3\x8c\xe4\x9b\xfa\xc2v\xfa\xb2\xbd\xe9\xd11\x8e\xed\xa7\xafm\xf29\xdd\xee\xb7\xc0\xbb\x93n\x02\x10T0Q\x9fe\xc5\xb5\x8e\xd84\xbfub\xd3<\x94\xd8\xe4\xf3\x9c~;\xcf\xc8\x15\xd1\x19\xf90-\xa5\xc6C\xad\xe4w+\xfd\xbbC\xa8\x9a1\xcc\x00^\x7fN\xb6;\xf3\x83\xc1\x9f\xd6E1[$\xe5l\x91\xfc\xfc \xae\xa5\x89\xa8\x0b\x8ea\x18\xe7\xfb\xbc$T\xb5R\xad\xc0\x98ae\xc3\xd8(k\xc1\x88\x15\xdd3[\xb6\x8d\xd25tI\xe0\x03\xbf$7\\\x143\x1a\xe9\x11\xbb\x84\xbcP7\x0e\xe2|\xcd\x85\x0f\xea`-\x8e\xd2B\xde}8}\xd9\xc3\x17\xcf\xd6\xf1l}\xebgk/\x87{\xc9\xbd@\x8f\xffI\xff`w\xb7swQ\xd7\xd1^w\xde\xa9\x1a\xbe\x8c\xc7\xbd\xc1y\xfbv`\xcf\xd3\xce\xf0\x89\x9f\xee\xac\x8e7\xe6\xa1X\xe7Fz\xcc\xc5\xef\xb2\xc2\xa4<\xc67.\x0ci\x0e\x89Y\xa8\x86\xce\xdbV\xc6\x99H0 N\x97`U\xd8\xf9_\"f\x82\xbf\xdc\xab\xbc\x15*\x0dL\xa6NV+\xa8\xf6\x8bGL\xe2\xab\xe6\nB\xb0\x8a)\xf7\x91\xac\xea*\x91\x10\xc5j\x14\xab\xb7.Vm.\xcb\\} UY|\xb9x\x9eU\xecqE\x00L\xea\xae\xc4\x9c\xe0\xb6\xd5fMHE%\x7f\x92efi\xcf\xda\xb2\x8b\xd4\xb7\xd5\xa6\x12\x8f$\xb3\x97\xc7hs]\xfa\xe1V{\x98cx\x8e\xb3\xecm\xb5\xf9\x9e\x90;\x7f\xa2\xdbV\x9b9\x1d\xe0\xdd\xd8\x05\x94\x9a\xd1;!Y\xf1\xd7\xfb\x93\x8c\x8e\xcc\x88\xc6\xf9\xce\x9f\x8a\x879]w\xc9\x0d\xa49$\xf9\x0d\xbc,\xd2\xfc\x01,\x92J<\x1b\x9c\xc0+\x92\x17\xe6w\xdf\x93|\x05\xc7\xdbb\x9f\xd7G\xe2\xbf\xd2\x8d\xfb3)\x0b\xd3\x93\xdf\x0e~\x02\x82\xa7\xc0\x96V^l\xcd?#\x98J!ad\x8fDc\xd7\x9a-P\xfe\xb6\xd7\xe1P\x17\x97$\x97v\x16\x1b\x8et\xf0Q\xc6&\xb9 \xce\xfd\xc4\xdf9\x15F\x9c\xff\xfcud\xfe\x88\xf7I^\x8b=\x9f\xd2c\xe3V\xbe!o\xc4\xb6\xdcWu\xb1\x15\x8a\xd9\xdcicXt\xcc\x8a\xc5\x0dl\x8aM\xb1+\x8b\xda\xf0\xfcpI\x96\xe9.%ff;\x19\xdd`\x98\xd3%Z\xcdwE\x9a\xd7\xc6\x15bW\xbc\x1c\\\xa7w\xfb\x86\xe2\xe2P\x1a@\xcb\xa2d\xb7\x0c\xd7<\xb9\xba\xae\xd8\xfb\xd4\xf4\xfc\xca\xfd\xec\xed\xc3\xfdz\xce\xa6\xec\x15\xff*\xadj\xe6PZ\x17\xfb\x92\xbd\x02Y\xe9?\x7f2\xe3>\xa7j\xc3-\xae}\x99\x1dA:#3x\xcc]\x8f\xb3E\x92_\xce\xae\x9e,H\x9d<\x99\xbd\xad6g$7L\xea\xd3Y\xe3Hi\x05\x04\xd3\x11\x0f\xc4~.\xd6L@\xb0E\xfaP\x8f\xe4\xebY\x1b\xde\xd1\xcc\x14\x1b !T\xa60Nh[~\xd2O\xec'}?\xdf\xcc\xb8\xcfV\xf6\x90V\xb0\"\xcb,)y&\xfb\x0d\xbf\x99\xa0\xbf\x13\xa1\xa7\x19R-.\xde\x91\xf4\xdfQB\x1f|\xf5\xe8\xc9WG_}\xf5U_z\xc5\xc8\x17\x05b\xe4K\xfb\x13\x96\x99\xbf\x85\xc8\x171\x90\xe64H\xd7xs\xf9M-\xd1m\xb5\xb9\xcf_\x9d5\x1b\xa1\x0d\xc6x$T\x7f\x8bG\xc2[>\x12\xc6\x90\x11\xbf\xfb\xf8\x182r@\xe6\xba\x83\x1db\xc8\xc8\x14\\\x8c!#1d\x84A\x0c\x19\x19:\x1c=\xa3Fz\x91\x12tU~\x96\xe8DS\xc5\xd5\xc8\xcf\xd41r\x84\xc3\x88\x03\xe0:+\x8ar\xbeI\xaa\xf9\xaeL\x97\x06\x93\xc9u\xecX\x16yU'y\xcdul]\xc02\xc9\x96\xfb\x8c\x8a2f\xbc\xb3}\xbeI*\xfe\xaf\xea\")I\xc5\x9d\x04\x06\x84\xcc\xc4\xdfV\x1b\xda@O\x91\xcb9\xe8\xb20\x9d\x8eA\x84\x91\xe8r\n:Q\xe0\x1c\x82S\xbb\x03\xa7t\x06\xa2\\\x81!\x8e\xc0\xfc\"\xa9.\xe6;R\xce\xf7\xd5j\xbeM\x0f\x7f\xa8\x16\x9a\x92\xf6\x0b;R\xc2\xbeZ\xc16\xcd\x98,Z\x16\xf9\x15]M\xf9\x86\xfd\xb9.\xf8w\x1at\xfcK\xf6\xe0\xf9\x9a\x90\xb9e\x919I\x17t\xb5\x18\x99\xe7\x89a\x94\xf6\x18\xff\x07%)\xad$\x91l\x07*\x08\x11\xb7\xb51\x0c&\x1e\xce%\xb8D\xe7]<\x9cc\xac\x93\xfa35L\x1a\xd54\xdfV\x9b9s8\xb7we\xbb\xa2\xb2\x96\xd1})\x1b\x9f\x7f\xfe\x9e\xa9\xb2t\xcb\xfeY\x01\xf9L\x96{& \x12\xa8\xcb$\xaf\x92e\xe3\xbc%U\x9dn\x13\xf6\xe3&i=\xcc{\xba\x0c\xb8\xd0V\xbd\xe9\xda\xcb\xd5^\xd7\xe2\x93;k\xf4\xa87\x88wd\xe5ZU\xbes\xe1\xd9\xd5\xbd\xa39F\xd5O\xab\xe8\x83\xd4\xbcP\xe8Z\x846%\xef\xaf\xe2m6eo\xf14\x8f\xe7\x8a\x81\x14kX\x16i.\xdd\x0e,\x0b\\\xb4\xa0F\xa3\x06a\xe7\x18!\x81\xa9\xfb\xb86!\xae\xcd\xe1\xdat3\xa4]=\xcd\xe2\xe4\xf6c\xbbD\xd9\x8f9!+\xb2\x02s\x95\xe4\x8e\xae\xa8\xe0\x818\xf2T\xf0{y\\\xd2\xde\xa2\xe6EM\x0d\xd6\x0b\"\xbfb\xe6\x9fT\x10\x9d*\xe1\xect\xc7>b\xa7;\x0d*~\xa4\xe4g\xcd>#\x84\xde\"+z>\xf4\xb2W\x9cv\xb7\xd8\xfdJ\x0f\xd2\xealYH\xff\xa8r\xb0\xcb/\x05\xa9}\xcaz\xca\xd3\xc3\xce\x8d\x06n4pu\xbf\xdf\xb6\x81k\xbb}Z\x14\xab.s\xd2|\xf0'cd\xe1D\x8b\xae\xfe<\xa7\xb4\x86\n m\xf8\x80\xb2\x87d\x07\xcdED\xc7\xc4\xae\x8b\xc6\x0e\xefK0\xb15\x99\x99o:\x8b[)t\xc7/\x0c\xbb\x90\x9eoF\x18\xa9\x95\x83\xba\x90d\xd2\x17\xa6\xc1\x97\xae\xa9xg\x0d5w!\xcc\xed\xd0\x1f\xe5&\xa9\xe6\xc9\xea\x1f\xfb\xaa\xdej\xe3\xa6\xf8\x08\xf9\x1d\x80q\x0e\xd6Y\x91\x0c7\x8b\xcdJS\xbbm\xa4w\xfb\x97u\xb2\xac\x8bRXi\xdb}V\xa7\xbb,\xd5\x96\xfa\x95Ue(\x02\xa9\x13ZW\xf9\x82\x87s\xd4\x9f\xe5<\x8f\x14\xfem)\xdf\xeec\x17\xfe\xa2\x1fs\xe8,\xc9uR\xae\x98K|\x99%\xe9v\xbe#eZ\xac\xe6\xfc\xef\xf3UJ\x17\xdeb\xcf\xf4?\xc7\xe5x\xc9\xe5%\xc5r\xca\x90|`8^uQ4/\xbb$\xcds\"\xac[\xe0\xdd\x02\xef\xb6A\xa6t\x0f\xdb\xa4^^\xc8\xabdNmU'\xf5\xdep \xb5\x12\"Z\xdc\xd9\xf3)r2\xba\xf0K\xa8\"\x85\xccT\x93\x16\xc2\xc1C\xa7X\x0b\xf4+Sq.W\x81\\<\xe9\xca\x14\xb2\xc9\xd8\xb7+\x8bM\x99lo\x9bLK \xed9\x7f\xe2\xb1y\xd6\x86\x92 \x82Ly\xc9\xca\x92^\xd8\x0f\x0b\x92\x15\xf9\xc6\xf4\x14\x0b\xb5\xf9\xf5\xc3\xe7g\x01\xb1\xd1\xe7\xbb\xa2\xc8\xe6\xeb\xa2\x9cw\xa7\xce\xc4\x10\x9f\x81T\xfb-\x8b\xaf\xcc2n\xf9\x97IN\xedI\xd1\xaf\x10]\xf4\x1c@\xfb5b\xe2\xf4\xe8\x07\xe2\\\xad\x80X\xb1\xe0:\xcb\x02n!\xb8\xce\xb4\x80A\xd3\x9d\x14\xd4\x1cPF\xaf\xd3\x9c\x1e\xe4\xb2\xacX&f\x0e\xff\xc68\xc9\x179\xbf\xba\x9b`{O\xb5\xbb\xfb\xe1\x0drW\x88;\xc6 6\x85\"\x81I\xbe\xd2\xe5\xe6q\xe0\xc3\xb7?\xa3\xe0\xca\xd3\xe3\x80\xe5\xc01\xaf\x83\\\x92]I*\x923\x0fs\xban\xd5\xb7\x14\xdc\x0d\x1bz\xea\xbf\x0f\x17I\x05l\x88:V`\x88\xb2\xda\x04G\x9c\x06j\x1d\xefVlg\x89(\x17\x92\xafL\xcf\xdd\x90+j\x88\xd97\x9cB\xd8\x1ba\xf5P1i\xa5F\xecM\x19\x82\xc0\x98\xd4\xc7}\xd0`\xee\xa1i\xc3\xc1*9b4w\x8c\xe6\xbe\xb3\xd1\xdc\xfa\xabe\xfb9\xa1q\xc5u\x9f\xa4\x14G\x17\xfdn$+\xdc\xd1#:\xe6\xa2c\xae\x0f\xb7\xec\x98\x8ba\xe1~1\xb71,\xfc\x80\xccu\x074\xc7\xb0\xf0)\xb8\x18\xc3\xc2cX8\x83\xdftX\xf8\xfdP'\xf8\xe3\x7f\x8a\xbf\xa6\xab\x7f)_W\x8f\xff\xd9\xf3\x86\"\xdf>\xb7[\x8b/nN^u\xbc\xe6Xo\xb9\xea,?y\x15\xe0\"\xa7=\x8bV_\xac\x9b\xdcd\x17\x06\x1d_Q\xcen\xa7\x95\x868x\x85\xb8\xb9\x91N\xee\x83\x90\xe7\xed\xc36\xf8\xaa\x83=\xd5\x03\x8a\xbc\x9c\xd1V\xff\x9a\xd3Q\xea:\x148\x9d\xa4\xce)q;H\x1d(\xdcn\xe6C8\x99\x7f\x0d\x9cs\xbb\x95\x9d4\xb8]\xca\x03\xe6{y\x8d-s\x80\xf5\x0d\xbb=\xc3\x18\xbf0\xc6\x01;\xb5O\xd8\xe2\x11v\x933\xb17\xd8\xe5\x0b\x1e\xe1\x85\xa2\xaaX\xe7\x89\xeaa\x12\xb1\x02.\x0fT\xd3*:\xa0\xd4\xdf\xa2\x03\xea\x96\x1dP\x8d)mZ\x81\xbd\xcdrn41\x88\xce2V-b\xdd\xfd\xb9j\x87\x8c\xacy\x07\xeesx\xcf\x84\xf4\x18\xb62<\xfc\xa0\xaf\x13u\xc0\xcb\x920a\xd6\xbbM:\xd4\xc8}\x8f_bA\xb0aU\xa8\x97\xd7:\xac\xd2\n\xcecQ\xddp\x18x$\xd8\xc5\xfb\x12\xab\xa3;$z \xbe \x90,\xb9g\"\xc9W\x9a\x08\xa4\xf6\xa8k E\xb2\x11&\xbe\xbf\xb3'\xac\xb0\x9a\xb3\x03\x93\xa6\xad2\xdb\xf8\x88\x18\xf7%w\xcd\x07\x03\xb1$\xc4\x87,\xe2k\xda\x87\xe6\x82\xa5-\xf2\xc8\xe5%8\xf1qE\xc8\x83\x17\x97\x10\xf6s\x97z\xf2\xe2\xbb\xcfl\x92\xbb\xcc*\x0e\xdc\x86\x95\xc7\x06\x86R\x04\x97\xd2C\x19\xc9\xd2M\xba\xc8T\x01V\xf5\xfdx]\x10#\xfb\xad\xc4\xb3pE\xc1e\nj*\xd8\xa9\x97}.\x97\x04Ca\xe7\x97u\x1c$\xdf[X\xf1\x08^\xbe9>y;?;?>\xffx6\xff\xf8\xee\xec\xf4\xf5\xcb\x93\xefO^\xbf\xc27a\xff<~\xf1\xe65\xb6IX\x03\xcc\xab\xde\n\xbe_L\xd4\xfd\xef\xd0U!\xccP\xd3Qo\xbb\xafjV\xbe\xbfwGo1)\x9fu\x04\x0d\xb3\x99\x1c\x0d\x1b;\x916d\xa3`\xb2p0\x12\x8b\x85\xa9\xb6l\xfe\xcf\xdd\x98S\xdbk\xda\xcf*\xd3\xdb\x9djC\xf2y\x97\xaa\xa1\x07!\x17\xe5\x83\x85:4\xeb\x91\xa6<\xd2|G\x99\xec\x083\xddi\x9a\xe3\xcd\xf1\x18\xef\xe6\xb7Db\xbc\x1b\xc4x\xb7\x18\xef\x16\xe3\xdd8\xc4x7\x05\xcf\x17\x19\xeff\xbfo\x11^\x13\xd1\x12u\xcdr*\x9a\x98/V$R\xf5\xd6D\x1d8\x1d\x9c\xe5\xcaDv\"\xbe\xb8\xb3\x97$Z6v\xe1\x978\x06\xdd\xb2\xbb\xf08\x97\xa7M\xba\x87\xf6y\xfa\xd3\x9ed7\x90\xaeH^\xa7\xeb\x1b\xcd-\x85\xe1*\x82\xf9>\xc2)w\xf9N\x80=\xeaLi\xbc \xd9N\xa5\xef\x83\xce\xb5\xd0\x82X\x8e\xb3\x07o\x93\xcf\xe7\xb4\x9b7\xec\xbd\xde\xe7O\xbe\x19\xbc\xb3\xc2\xa1\xcb\xa0C\x8e\xe8\xec\xa2(k\xb9YE\xaf\x0b\xb9\xe3<\x06\xf5\xaa\xa5W\x0e\xed+\xcd#2\x1c\x9a{f2_\x97\xc5vn\xbc)\xe4\x80\x18\xa8\xb2\x9e\x94#/\x95^\xe9\x8aT\xb0\xde\xe7\xab\xf6\xbd\xcf\xee%\x13\xec\x8a\"C\xdcny\xe5Z\x0f\x8af\xed\xb9\xe0\xe7\xcf\x89\x8a\x1bbEV\xfdV.\xa8J\xb2\xe5\x8f\x88\xf28\xcdE\x92Q\xdd\x82b\xac#g\xba\xc1,&\xbb\xb5\x82\xea\xa2\xb3\xe8 Y\xd7\xa4\x04\xf3\x8d\xa1z\xb5\xc8\xde\x0b\xe4n\xe6\xdf\xca\x14 \xcf\xc3\xdc\x8e\xcagj\xfa\xdb!\xc921I\xf2\x82wq\xc3^\xfbJ\x97\xe9.\xc9ks\xb1\ny\x11\xbcK\xaaZ\x9d\xa9\xdf\xca\xecl\x93\xcfR$-n\\\xc2S\x99\xa4\xb7\xe2\xa9u)\xf7H\xa9\xde\xd3\xd1?\x08|\xbf\x19f\xf2w\xdf\xe6e\x91e\xc5\x15)}\xd6\xfc[\xf9f\\\xafz'[\xa2mDE\x01\x12\xf9o\x85\xa9\xdc\xfd+\x82\xd5*\xb2,\xf2\xd5\x18\xed\xeeoR\xbek\xce\xc3\xa2wn\x0c\xf4\xb2`\xb2\xa4\xd2_\xd25A#\xf3\xaaN\xcaz^\xa7\xdb1\x86\xa5\xa4\x7f\x95\xd4\xe4\x11\xc5\x85\x19\x82M\xa0\xa6[\"\x07\xa4\x98\x10\xd2\xbb\xc7\xa8fqg\xd5u\xca\xee\xb0Lw\xa1\x00g\xe7\xc7\x1f\xce_\xbf\xe2\xdeq=3\xe4\x15V\x13\xb0D\xf2\xd5\x97\xc3\x93\xb4j\x06@\xf7\"\xc9WG\xf6\xe7\x18)\xac\x92:\xe1\xee\x8b\xb4\x967\x98\xfb\x9d}\xadH\xae\xcc\xb7\xc9\xe7\xdb\xe6\x8cy\xf8o?\x9e\x9d\xd3A\xebiWv\xea\x1d\x9c\xd6\x8f\x15s\xa5\xd4\xa4\xcc\xd9\x8b\xbc\xca\x13\x1ct\x81\xd7e\xb2\xbc\xe4\x17[\xfb\xb2$\xb9\xb0 \x8c\x08\xf9P\xef\xb3\xe0\x7fv\xceI\xb7\x86U\x9f,\xeb}\x92}\x01k\x9e\xf4\x85\x00\xdb\xf3|\xb5\x7f\x7f\xf2\xee\xe4\xecG\xfb\xf6\xa6p\x92\xa7u\xca8\\\x91\x1a\x92\x8a{k\xddk\xe6\x97\x12\xeb\xaa\x89\xce\xe2\x15\xa5\xbe\xe5\xcf\x9f\xees]\xf4\x0d\x07\xb1R\x10\xe9a\x07\x1a\x87eN_vW\xb1TT\xc2\xd7\xab\x1e\x17\xe9\xde\xa8\xe0\xc9#&\xcb\x8c\xf8\xd2|E>\xa7\xf9F\xcf fJJ\xcb\xe7\x97\x9bW\x0b?\xb6\xc2d\xcd\x0dS\x9f\xf4\x03X\x97\xc6\xe2S\xe0\xb0\xc3\x0c\x01\xc2\x1c\x14z\xe5$\xf1(<\xed\xec\x84\xb3\xcf\x15=yv~|\xfe\x1a\x11<\xd8~{\xfa\xfa\xdd\xab\x93w?8\xbf\x13\xa6\x80\xf3;)T\x9c\x1f\"\xa3\"\xb1#\x12\xee\xac3\xc6u%\x10R\x1b\xcb\xac\xc5\xc2\xee\xc6\x99\xb3x\xce\xef1\xee\xcaB\xff\xa1L\x96Dnx\xee\xa1\xe8\xabr\xc9wy\xe5b{\xca<\x1fX\xc0\xfb\xbcN\xb3\x01N\x92\xb3w\xb8l\xa8\xe4,\x1a?\xb0\xe8\x96\x9f\xf6I\x96\xaeo\xd2|3\x17\xb5\xe0\xed\xfc\xd6;\xba9\x18\xdd\xdd\x1c\x10\x87(\xdc1\x8a\xceYF6\x16a\xc0\x01\xd5!\xa0;\x85\xcei\xd4\xc1\xab\x16\x10\xab\xb4\x05\xd4zmA{\xd6\xed\xac\xab\xf6\x82D\xf2\xcb\xe85\x90 \xa4\xf9]\x1c\xdf@\xcf\x04\x8dO\xcc\x9fh@\xa5\x8c\xfb8\xcd\x01+'Z\xe8\xb9\x1f\x9aK\xe5}E\xca6V\n\x81H\xd8C\xd9M3\xd0\xe6\xd1\xfe\xab$KWIm\xb2\xa4\xba\x80\xde\x0e\xe0\xb5%\x00\xe3\xd8h\xc1k\xc1`\x9c\x1d-x\xa0\x96\xeb\xfcv\xd6\x81X\xbaq\x1dt\xc0c\xb2\xe0\x80\xeb\xa0#\xcf\xaf\xc8\xbc\xaa\x93KB-\xec%\xa1\xca\xd8\xa1_\xc0\xaf\xaf\xa0\xa5#DHK\x92\x12R\xb5 Yqm\xb5\x0dZhV\xc8\xfd\nv\xc55)\xa1L\xf2K\xe3\xc9\xa3\x85\xaeN\xb8\x93<\x12\xdb\xeb\x97\xe3\x91\x1f\xcd\xc7L\xb7\xbe\x12;\xb8\xad\x97 \x1fV\x14\x7f\x17oL$\xb9\xf9\xec\xc8!\xad;\xcf\x0b\xf1\xf4\xbb4K\xeb\x1bX\x96iM\xca4\xb1\x11\xcf\x9e\xb3X\x13M\\\xba\xf2\x15Vd\xe0\xc5\xc5\x17jII~\xb9-\x8d/\xd3\x92\xc2\x8f\xef\x97\xb0\xa4\xda\xceZeJ\x94\xf0s\x042v_\x9c,\xcb\xa2\xaa\xd8Ee\xb3\xe9\xab#P\x1e{Zk\xd2+\x86\xd0\xbe\x05\xb3 <:\xcf|\x0f\xdd\x02zG\x81\xd7\xae\x82/O \xfb\xad\x06.<\xcf\xc5\xf4\xf4\x85\xa7\x9c\xb6[\x12\x9eWE<\x82b\x05'\xe5\x95[\xa8|\x99B\x137\xb6_\xb5\xc0\xa4,@\xa0\x8a\xc2R\x0b\x1e\xa8\x1b\xe6\xcf\x9bw\xad\x1c\xc6\x1b\xf8u\xe0\xbdO\xdcA\x8c-\x9c\x16UJO\x11\xed\xa3\\%7\xd6\xd3\n\x92\x1d{\xa4K\xc4\xde!\x90\x89\xa2i\xc9uR\xae\xda\xc7\xba\xe8J\x94\xeb\xec\xfa\x82\x98/\x1bZX\x169\x7f\xc4\xb52\xfbP[HJ\x02[R?\xa0K?\xe7G\x0b\x1e\x86\xdf\xa0q\xf9~[\x90w\xa2\x8ch\x99\x8b\xc4\xe7\x97\xfd\xed\xe1\x0c\x8eE\xf6\x85\xe1\x9d\n\x15~&e!\xd3\xa2\xd9\xb6e.\xf8dKP\x03\xab\xa0\xc8\xedT\xfbI\"\xae\xac\xffZ\x0cO9W\x05\x7f\xb1\xbaQ\xd3T\x11;\xb0\x05\xaai\x85\xe4\xbf4\xeenN\x9b<$\x8a4\xa2m\x92\xdf42\xeafg\x16\xea\n\xd2c\xf1\x88*?ur)[\xb0@\xfc\xce\xa5\x87\x0e\x93\xd89\x8a\xc3\x7f\xf0\x99\xd2\x95Z\xcf@\xde\x11\xc8\x07E\x86\xe1\xf8\xf3\xba\x13\x86/!\x968PA\x19s,q\x80b\xe6o\xa1\xc4\x81\xb5\xac\xa3\x9a\xc6\xd2\x943X\x16y\x9d\xa4\xeak\"\xfa\x94\x17 \x96\x1c\x9aX\xbf@\xfd-\xd6/\xb8\xe5\xfa\x05\xad\x0e1-\xc1\x9e\xb8<\x86u\x9a\xd5t;\xf3k \xa6D5\x99c\xc3\x02\x01\x7f\xf9\xf8\xfa\xc3\xff\xcc\xcf\xff\xe7\xf4u\xbf<@\xb5#\xcbt\xcd,C\x95\x90~\xbb\xe37o\x9e\xb1\x03J?M\x8d\xdd\x7f\x0f\xaa\x03tZ\x8a\x00\x8cg\xb0\x13Qg\xbd\x98\x15\x86\xe0\xb9\xad\xeb\x97\xe7'\x7f}\xfd\x0c\xb8\x8f\\\xdb\xdc\xd2\xfa\xfd\xc7\xf3\xb3\xf3\xe3\x1e \xec9{3:\xdbpd\x1c\xc23X\xa7yZ]4U\x9e\x8d\x14\x85$.\x0e\x16\x9a\xae\xfe\x80~Z\xcd\x1f\x1d\xbfyc\xfeQ\x17'\xa3\x99\x06\xf3\xef\x1dF\x9b?\xd2D\xcf4\x910\x8e\xe1\xc4r\x04~+&\x96#\x80X\x8e \x96#\x88\xe5\x088\xc4r\x04\n\x9e\xd5\x97X\x8e\xc0Q\xffY\xdad\x8f\xffi\x7fEG92\xf5^\xca\xe9\xd92\x98\xf7p\x06\xe8~'\xb9\xf1E\xd4\x1d0\x9d^\x82\xdc)\xb7\xf6\x80\xcc4u\x03,U\x03\x9c\x14\xbb\\\x11\x81\xf5\x02<\xab\x05t\x99r\x98q\x84W \x08\xac\x11\xe0U!\xc096#\xdf+\x1f\xffV\x192Y\x1e>>\x0b\x1f\x91\xab\x8df\x81\xbfX\x1e\x91}\x8f\xcf\xbd\x9f\x9c~\xe3\xfcM\x9bso\xcb\xb8\xf7\xcd\xb7\xbf=\x1eL\x98go\xb3\x8d,9\xf6\n\x9d\xa1\x19\xf6N\x86\xd9\xb2\xeb\xf1\xb9\xf5\xb8\xccz\\^=2\xab\x1e\x93S\xef\x99Q?>\x9f\x1e\x99M\xef\x9c\x95\xc9\x96\xf1\xa4Y\xf4\xfe9\xf4\xae\\y\xa3\xf4\xc7\xe6\xc9sN\x9a\xb3\xe4\xad9\xf2\xcec \xe6`\x02\xa8\xecxDW\x80\xec\x0e\xfc\x93\x12\x9c\xeb\xad\x05\xc4\xcakA{V\x1c\x931\xee\x99\x90p;\xe3\x9a \x13>8\x19\x01\xb7\xcb[\xc0\xe5\xc0\xb7\x99\xcdN\x84\xe1\x99\xcf\xc8%\x0f\x1e\xcb\x1e\xdc\x8e\x80\x16<\x16\x87\xdb9\xd0\x02\x1amp\xce\xbb\xf7\x9c\xa3\xf2\xdd\xe3\x9c\xf7\x017\x1b\xe0\x83vd~;\xba\x9f\x80E2Uf{`\xce\xf6\xc8\xac\xf6Crf\xaa|\xf6 \xce\xf8P;m&\xfb\x88TLL\x16;R\x14`\xc5\xc0\x17g\xf1 3\xbb\xbf8\x8b\x07;\xae\xdb\xb6x\xdc\xa9\x97\xb8\xb2/\xc8Lu\xb1\xcb\x9c\xe8\xbcR/\x91{\x06<\xf6\x0d|9\xea\xd3g\xe6\xa7\xcdK\x1f!\n]9\xe9\xc8)\xc5N\xe7\x17'\x06\x11\xb9\xda_\x9c\x08\xc4\x8c\xe9W*\xfe\xba\xd9\xbeL\xb49\x11F\xd1\x87B\x1b\x92g\x8eF\xee\xb9\x13\\\xe1o-L\x98_>Qv\xb9On\xf9t\x99\xe5\xd3\xe6\x95\x8f\xc8*w\xe6\x94\xfb\xc8\x97)\xf3\xc9\x83\xd4\xabB\xec4\xb9\xe4\n\xca\xc0Lr\xff<\xf2\xf3\xc1\xe5\x81\x10u\xbc\xeb\xeb\xc4\xf2F>>-\xf6\xc5\xcd\xc9+}fl\xc9sw\x07wt\xfa!\xc4\xe5\x86f8\x99\xfd'Z\xf1\xcf\xa1\xdc-\x05\xb6-\xa9/\x8aU\x9b-\xa3\xcd\x93\xf9\xce\xce\x0e\x8e\xf4\xbc`$\x05\xb3\x83.\xaf\xca\xc2\x0d\xd0-E+\x8b\xb4t9Y\xc4\xbe\x9e\x8aC/\xc8\xf2\xe2\xeb\xa7\xa7%Y\xa7\x9f\xbd9\xb3`\x8d\xe7;\xd6\x1a\xbbN\x9aDY]\xd7\xe6\xc1w\xbf\xa6\x83o\x87\x1d0\xea\xd3\xa4}\xff\x131\xcem\xf2y\xbe%\xdbb\xbe\xbcH\xcad\xa9\x8a\x15\xeb:Pw\xf5\xe7y\x95n\xe6,\xe92\xb8\xfd\xcfd\xbe,\xaaz\xbe#%[\x91!\x88(\x15W\xa4L\xd77\x1c\x17Y=\xfd\xf6\xdb'\x7f\x9a\x02UE\x96\xbb\xa7\xdf~w\xf9\xc4\x1f\x99\xb2J\xf8\x0c5\x95j\xe8\x9ahezs`\xa4\x93\n\xdbb\xb5\xcf\xd8=\xbfn\xaa\x99\xe8<\xe6'\xd5f\x03\xb5\x96\x0cz\x0d\x88\xc3\xee0:\xdc\xbe\xda\xef\xeb\xd7\xe5\xec\xe9}\xdeq\xcf\xfcrPk\xdc\x1e\xa2\xf9\xb0eg\xab \x18\x14\xca\x95>7z\xb6\x84\xde\x82\xd0\xdb\x0dF}o?[\x1c\xc3\xc7\x0fo\x1e\x97\xa4*\xf6\xe5\x920M\xcf\xad\xdf~\xa6Z*V\x13\xe3\x1c\xd7\xf4=T\x15=4d\xe9\xcfd\xd5?\x11\xed\xca\xa2.\x96E\x06\x8b\xfdzMJi\xba\xcd\xe0\xfc\"\xad\x04\xcd\xdc\x1b\"\xeccHj\xc8HR\xd5}L\xf4Xq\xef\xf1=h\x04\xca\x8c\x19\x1fYR\xd5P\x91\xcd\x96\xb4I\xbe\x1f?\xbc\xb9_1+CW\xe2\xaf9:\xf5{\xa0M\xd7\xfb,\xbb\x11\xd1.\xd4\xfag\\\x91\xb9\xc3t\xfc\x0f\x92\n\xd2\xbc\xdf\xf4\x13\xed\xec\xf1\xa6(6\x19\x99\xb11/\xf6\xeb\xd9\xab=\x8f=\xfa\xf4\x90\xd3\xca\x90\xb5\x15\x05\xe8`{x\x96I^\xe4\xe92\xc9\xd8n\xef\xf7\xf2\x80\xcc6\xb3#\xca\x1e\x16\x88zov\x8f\xae\xee\xbc\xa8\xe9\xa2\"\xbb\x9a\xac\x1e\xf6sd\x01Nr\xd8Q\x86\xa5Kr\x045\xa1\"b_\xedY\x10\xe7\xae$\xcbb\xbbK3J\x8bH\xecZ\xa4yR\xde0\x7f\x13\x1do\xff\x10)/\xb3o\xfa\xdd\xf0\xe3\x07\xa4,\xdfw_\x11\x99\xf4N\xa7\x95|fSs\x9c\xdf\xcc\xe0\xc7\xe2\x9a\\\x91\x92{\xb0>~xS\xc1\xf5E\xba\xbc\xe8a\xa3\x08\xe82\xeb\xaf3z\x82!\xf0\xe9\xa2\xaew\x9f\x8e\xf8\x7f\xabOG\xc0\xfc\x05\xe2\xd7#\xb6R\x96\x9dB]\"\\u\xbf\x1b\xb0\x9b\x8ep\xd0\x07)\xaf\xa4\xd7d\x9b\xec*>\xed\x8c\xd2\xba\x90\xeb\x17:\x86%=\xe3\xaf\x8b,+\xae\xabg\x03\xee\xff;\x9c\xac[\xda\x80\x07\xa2^\xa5+\xb2j\xc8g\xfe\x89\xaa\xdao\xc9\xaa_\x16\x06\xfe\x9d\x9e\xed~e;`\x06\x0f>V\x04\xaeHY\xa5E.\xca\xf0\xb0\xbd\xccWD\x92'\x9b\xe1\xf8\x16%I.\xe9.\x15\xe8f\x0f\xfbs\xfb\xae\xa8\xc93\x1e\x9c\xbc\xde\xe7K\xbeV)\xa5bO\xb7\xaf\x1dt\n\x82\xe9\x98Y\xb0\x9a\x1c\xc3\xea_r9@I\xa8D%G\xc21$;`\x15@\x98]\xd0\xacpVO\x85e&^\xa7\xf5\xc5@@\xde\xec\xc8\x8c\xaf\xb5d\x97V\xb3e\xb1\x1d\xca\x9b3\xb6\xd2+^S\x8cW \xe9\xedWx l\x11^\xe2\x82o\x8d\x87\xb0M7\x175,\x06\x1b\x92\x17*I\xeb\x0bH\xb7\xbb\x8cPA\xcf\xfd\xea\xa20\xd2\x12*\xb2M\xf2:]*.&\xcd\x11\xd9\xa8(5\xc7a\x0ev\x0d\xfa\x96n\xc2\x05\x91\xde\xc5\x8e\x1a\x1c\xe8=\xa1B\x92EqE\xa0\xa9\xe8\xa4\xe3\xef\xef\xdc}\x7f:\xceo>\xb5\x0e\xa5$\x87\xa4\\\xa4uI\x17\xbd\x85\x06)\xbb\x92\xacP\xc6\xcfx\x9bt\xd9N%\x0c\x13\x80\x9c\x86\xc5\xd0\x00\xe8\xf6#u\xba\xb2\x14N\xe5\xe2\xcb\xd2\x05#\xacIe\xae\xf6\xbb]Q2=\xb1K\x96\x97\x8f\xf79\xfd\x0f\xd5\x0e|\xce\xaa\xe1*\xef+\xc3b\x0d\xfb\x9aok\xb9u**L\x92\x15w\x13'\x19lHNJ\x163\xc5M\xb9J\x12N\xfb\xe1\x8c\xeeb|\xfd9\xa1\x8b\x0b\x9eP;zy\xc9v\x8a ,i\x18\x97\xe6\xf0\xf2\xf7\xbf\x1f\x08\xe9\xef\x8b\x02\xd6E\x01\xcfa6\x9b\xfd\x9f\xde\x8f\xb4\xbb$\xbf\xe9\xff9\xc9of\xb4\xa3\xef\xcbb\xfb`]\x14\x0f\xfb\x1f\xccf} \x9c\xae\xe1\x01m\xf6\x91\x91u^<\xf87\xda\xee!\xfcs {\x86m\xff\xa5\x1b\xebS\xc7X\xff3\xb9J\x82\x06\x0b\xcf\x99\xae\xa7\x18=\xc7\x96V\x0f\xbe/\x8a\xd92K\xaaJ;4\xde5\xfd\x94S\xdc\xf9\xbc\xdf\x8b2\xe6f\xd0_;\x06}zS_\x14\xf9`\xd8\xbc\xdf\xef\x8b\xe2\xc1l6\xebW\x04h\x86\xfc@\xf3\x0b\x9bf\xc6\x06\x0c\x17h\x83\x13\xce\x84W\xaf\xcf^~89=\x7f\xff\xe1\xe1\xd0\xd3\xd7.\x04\x1dj\x8e\\7\xfco\x1c\xc3\xff\xa1\x18\xdci\xd0\xa1?{\x0e\xff\xb6[\xcc\xbe/\x8a\x7f\xcef\xb3\x7f\xf5?I\xf2\x9b#j6\xd0\xefv\\i\xbeM\xca\xea\"\xc9(St\x04\x0e\x07\xdf\xefg\xd0I\xba\xeeu\xf11\xdf\xb6\x9d0\x12\xd8bc_\xfd\x7f\xcf!O3\xcd\x02\xd2\xf5\xac\xac\x94svn^^6rC\x1al,\x1b\xbc/\xd5\xf8\xad\xd0MS\x9fi_)\xfa\xeb\xbeFe>\xa6g\x8c\x19\xfb\x81\x1a\x11\xf7\xa9\xfd\xd8HW*yY\x8a\xda\x05\x11\xf3\xd3E\xd7\x88\xb2<\xbb\x916\xf2\xe0\xc8\xd2\x98'\"\xbf\xa0\x96\xa7\xa4\xfb\x8f\xefw\x91\xa5\xb9\xd2-\xb7\xc8\x89X'\xf7\xd6E1[$%#\xf8\xf3\xe3\x9b\xd9\xcf\xf7\xf8X\xb9\xcd\xd97\x9cYw\xf7\xe8WT\xacv~\xf8\xcf\xb3\xf7\xef\xba\xff~\xfe\xfc\xf9\xf3>\xb7\xe97\xed\xa9\x8c\xeb\xf6\x82n\x05\xa1\xe8\xb8\xd5\xba\xaf\x88<\xc9o\xf6YRv\xb1\x0c\x1b\xd7,\x00\xafURG@\xb6\x0b\xb2Z\xb5\xea\xeaH\xe8=\xe5,\xd7Q k6\xd0O\xffA\x87\xfa\x89\x1fR\xd4;\x1c\xc9\xb8\x99\xdc\\\xcf\x06\x06X\xb2\xbc\xa4\xfb\xaa5\xcf\xd7iF\xfarJ\xee\xbeSRVE\xaeY\xb2\xe2\x94\xbcN\xcb\xaa\x9e3N?\x87'},\xcdgt\xaa\xe5WO\xed2\x11@\xd3\xdb=6\xe2{\xcf\xe0\x9en\xed\xaaC\x99q\x9a\xef\x1d\x0d\xb10j\xdf%[\x8a\xe9\xffr\xd2\xfe\x9f\xe63Jm\xef+\x1b\xc9'ka8\xaas\xc9\xe7\"\xad\xe0\x9ad\xd9\xa3\xcb\xbc\xb8\xe6\x97\xa0\x17I\x05 ,\xf7U]l\x07KQ]4G\xdc\xe0\xe9\xad\xa4\xb6\xfc\xb0\xe8\x90.\x90|\x03 _\x1e]t\x9f\xd82\x95+\xe5\xa2\xc8\xc4\xa5e\xa7wv\xe2\x17+L\x16R\x11\x0b\xac\x8b\x89\xa1nV\x15\xb0\xebp9\xd0\xc1QO\xfa\x18\xfe\xfe\xb7\xbf?\x1c,\xc0\xf0\xd9U\x91\xeb&\x98\x0d\x97\"z2{\xfa\xe4iuo0m`\xb4au\xfe3\xb3\x1b\x9b\xfe\x95\xb5x,\x9a\xc0\x87\xd3\x97\x02\x93\xf4h\xbb\xfdt\x01\x97\x18\xa2a\xdfS\xa7^\x15\x0e\xae\x08\x07\x989\x98\xae\x04k\xc3U\xa0\xf1\xac\xe2:\x94L\xea\xd8\xb3\xb9\xf6\xa6t\xeeM\xe8\xde\xb38\xf8F\xb9\xf8\xa6s\xf2\xb9\xdc|\x81\x8e\xbe\xa9]}\x16g\xdf\xd4\xee>\xa3\xc3o\xb4\xcbo\x80/\xd1:\xfd\xa6v\xfb\x8dv\xfcM\xee\xfa\x1b\xe5\xfc\x9b\xde\xfd7\xa1\x03pj\x17\xe0\x84N@\x8c\x1bpBG\xa0\xd9\x158\xce\x198@\xa6s\x0e\"\xdd\x83c\x1d\x84\x03tC\x87a\xb0\xcb\xd0\x10WcQ\xc5F\xc7\xa1[K\x07:\x0f\x87\x82K)\x0f\xdf\x1d\x8e\x8d\x82\x89]\x88:'\xe2$n\xc4\x89\x1d\x89CW\xe2hg\xa2\x82\xab\x1e8\x16\xc7\xb9\x16\x1d\xfe6\xa3{\x11\xe1`\xd4zB<\x9c\x8c\xfa\xf6\xff\xd2\x8f=\xc8\xd5\x88\x1d\xbc\xcb\xddh\x1f\xa9\xd3\xe5\xe8\xe5t\x1c\x1e\xb1G:\x1e\x1d\xaeG\x9b\xf3\xd1\xee~4r\x05\xeb\x82t;!\x87n\xc8Q\x8eH\x94+2\xc4\x19\xa9g\x85\xd3!9\x99K\xd2\xd0\x7fo%M\xea\x98\x9c\xdc59\xb1srZ\xf7\xa4\xc5A9tQ\x0e\x9d\x94S\xb9)'tTN\xed\xaa\xc4:+\x11\xeeJ\xb4\xc3\x12\xe7\xb2\xd48-u\x8e-\xbck\xcb\xee\xb8D\xbb.Q\xce\xcb\x01\xf1S:0'waN\xe9\xc4\x9c\xd2\x8d9n\xbe\x9d\xaeL\xb73S\xba3\xa1\x8d?\x94\xdeA\xfe\x96\x01\x9d\xd2\xcfi%\x92p\xf8/\xa2\x85\xee\xb5A\xc52\xd7\xbc2X3Q\xdfy\x85\x02j\xed\xe3\x82Z_\xa3\xde\xd3\xa8\x7fD\xd0x\xb4\xc1ED\x0c\x1e\x0b\x9c\xe4\x91\xc0\xa9\x1e\x07D=\n\xa8y\x0c\xd0\xc9\x14M\xd2\x9e))h\xccc\x7f\xc8G\xfe\xfc\x1e\xf7s{\xe4\x11a\xf5\x03\x97|\xa5\xf1\xc9K:\x8d\xcft\x18]\xf6oYx\xb2@\xfd\xe2\x86\x8a\xd8P\xef}\x0c\xb3\xf5\xf4\xc4O\xe6\x87\x8fa\xb6#|\xef1\xcc\xd6\xcf\xdf>\xd2\xdb>\xb1\xaf}\x84\xa7}j?\xfbd^\xf6i}\xec\x93y\xd8\xdd\xfe\xf5\xc9\xbc\xeb1\xcc6\x86\xd9\x8e\xf1\x91\xc70\xdbq\xbepL\xe4i\x0c\xb3\xed@\x0c\xb3\x85\x18f\x1b\xc3l\xfd}\xd9\x93z\xb2\xa7\xf4c\xc70\xdb\x18f\xabb\xc1\xf9\xaa\x11\x9e\xea\x18f\x1b\xee\x9f\x0e\x9f]\xa7o\xda\xe5\x99v\x86\xd9Z|iV\xff^\x17\xc7c\x0d\x12\xf8p\xfa\x12\x13~\xab4\x8dA\xb8!n\xbf\x18\x84\xdb\x81\xa9\\\x80.'`\xa0\x1bpjG`\x0c\xc2\x0dw\n\x8ev\x0bN\xee\x18\x1c\xe5\x1a\x9c\xde98\xa1{pj\x07\xe1\x84.B\x8c\x93pB7a\x0c\xc2\x15\x10\xe8P\x8cA\xb8\xa1\x0e\xc6\x18\x84\xcb\xa1\x8eA\xb8\xda\xb1\x079\"\xb1\x83w9#c\x10n\x0c\xc2\x8dA\xb81\x087\x06\xe1\xc6 \\\xa7[\x13\xed\xd8D\xb96c\x10\xee\x14N\xceq\xf3\xedtt\xba]\x9d\xbe\xceN\\\x1cc\xb7\xb9\xea\xe7\xac\x14\x17\xa7d\x87W\xd1\\\x86\x95\xd7e\xf5\xf6|\xee:\x05w\x07\xc3\xdeYk\xbd\n\x89\xd6Vy\xe50\xe8T\xdf1\xb8\xca\xf7\x02&\xc6c\x10#\xab/\xe5;\x02\x97\xb9\xaco\x18Rg\x89\xdfi\xd0j\xca\xfd\xfa\"V\x96\x82f\x8da\x02wE\xb5`\xad\x17\x7f\x91Td\xc6+\xb7\xb7\xe5\x9f\x9bXd\xf4\nV\xa2\xcd\xb5\xe3\xd3\x9e\xdc\xcd\x11\xe6\"\xb0<\x11Z\xbe$\xf5\xbe\xcc\x99\x9fJ\xc4J\x8b\xf8\xf8&\n\x9dy\x996i\xf7\x95\x126,\xf6\x8c\xbf#\xbe\xfc=\xb5\xa0\xc4[\x16\xfc\xedhz\x1e\xbd$\xdd\xb3f\xeba\xaeH-7Z\xff\xa1i\xeb\xc8{\xcb\xc5C\xa8\"Okj\x98\xfe4\x85B\x1a\x9d\xee4E\xaa\x933\xcdI+\x8fp\xd2\xb8?\xe5\x0d:lJ\x931\x89\xa9\xc57\"\x7f\xa9\xb7\x14;\xd6\x8fi-\x8aO\x9a\xc5x\xcd\x98\xd7\x9e\x03\x96E\xc9?b\xc7\xb3\xde\xdae'A\xa6a\xbb\xa3j6\xb8\xba)EO\x83]Y\x92\x1da^\xec\x17I\xd9\xb0\xcc\xbc/\x05\x1a\xb62\xfa[\xb2\x7f\xbc:\xceo\xd0\x1b\xa2\x1e\x84\x94nD\xdd$37\xcc%\xc9Y\xd0\xaa\"\xfa%B\xb9\\\x18\xc7\xf8\xdf\xd4U\xd1*\xa3>N\xc9VW\x0e\xd6pc|\xccS|\xdeJo\x85\xb9\x97\x0ck\xd0r\x90\x8fW\xa8\x94\xaeSw\x93^\x91\\|\xbd\xcf\xd3\x9a\xb9Wa\x9f\xd4\xc5\xf6\xa1\xdc\x1a\xe4\xf3\xae\xc8\xc9\xb0p\xbd\xe5.\x01\x95=!\xf1v\xc9\xdc\x15\xd7\xfcP\xff\xe4\xab\xf6w>\xe19\xe1.\xed\x0e\x862I\x85-\xb5H*2\xe7\xc3` <\xd4\x9d\xfe?\xa1\x06Vg\xac\x0d\xf3\xefW|\xdc]\x84O\x04+\x9e\xc3\x93\xaf\xfe\x7fM\xff-\xf2\xee\xb7\xcc\x11-w^\xa7\x7fzf\xa6\xfck=\xaf\xcb\x92$5\x81\xa4\xed\x9b~t\x9f~t\xbf\x83\xb0\xef9j\xfa\x7f\x0e\xdfQ\xfd\xb5\xaf\x9e\xc1\x13\xa0\xad8}\xdf\xf5\xe6)\xc9\xd2\xa4\"aw2\xbd\xb5$_\x14\xe1\x18y\x8aO\x96V\x8cn\xb1\x8a\xe4o2\x87\xab\xb3\x92\\\x1b\x9eq\xa03\xe9\x89\xd8\xf4}\x87H\xc2\x91v\xf7\x80\x14n{\xc1D1\xf7\xe9R\xeeG\xfd\x8e{K\xead\x95\xd4\x89\xc7\x86k\xa9\xb7m;F\xd4\x9cR\x13\xc6w\xf4]\x98F\xc7\x8c\xba\x08\x1b% \x06\xd8\xd4\x85\xc8a(68\xe8\x85\x07\x07\xbd\x08\xe1\xe0\x1a\xd1xq\xc2a\xa4P\xd1sz\xd8\x8d\x8f\xa8\xe10R\xe0\x0c\xf0q\x01dpX\xfb\n\x1f\x0e\x03\x11\xc4A\xb7!8\x0c\xb6E\xb7\x81vaO \x9a8\x98M\xacQbJ\"\xc7 +eL\x1da\xd2\xdf\x99r\x84]\xedE\x87'\xfa\x86e\xd1\xa4\x94\xd2\x95\xe1c*([\x896\xee\xf7\xcd\xfe\xc6\x97\xda\x83\xf6\xf2\x96\xfe\xd2\xb2\x89\xadJ\xb9f:\xf8\x9e\xc3W\xcd\x12Y\xa5\xd5.K\\\xf9N\xe6i\x11\xed\x9btC!\xb3\xf6\x9b\x0d\xa9\xa8E&v*\x9d\x9c\x86\xccas\x9e{\xb3\xccR:>I\x1a\x95x>t\xdd\xd7\xa7\xcd\xdd\xef\xcf\xe8}&J\xbbG\x13\xe5F\x9d\xd9\xee\x0f\xc8\xe6\x19\xbcd\x88\xe0\x98n)\x89\xa5\xba\xd9.\x8a\xf0l\x18\xde\xbc\xcdN\xa5}\x89\xbf\xc9\x9b\xef\xea\x82\x1e\xa6\x8b\x1c\xc8gy\xa9\xc7\xa89>\x7f\xff\xf6!\x0fU\xe8 \\\xaan\x16\xb1\x06*\xe6\xb0\xe2\x9d\x08\x16{d\x18\xee\xcb4x\x80\x1f?\x9c\xf04\xd8U\xb1\xdc\xb3 \x88\x07\x05\x95\xd1P\xac\xd7\x8f\x96\x17I\x9a?\x14\x89\xc8\xcdUN\xe3\x17\xea\xa0Is\xaeuX\xa6\xdf{qI\x8e\x18\xc2w\xed\x10\xe6\x17Iu1f\x1c?&\xd5\x05\x17e\xd5E\xf2\xf4\xdb\xef\x80\"d\x1e\xb4vp\xbb\x82\xeaLv\xb6\xfa\xf8\xe1\x84\x9ef\xee\x8b\xd4/%\x7f\x8f'\xf6\xf3\x94\x98\xce\x9f\xd9\xf4HT\xabt\x95\xdf\xaf\xc5\xbdk\xc0\xa0\xf5{T\x1aY\x08\x99)Z$\x18\xdb\xed\xb4Su\x02a\xb9U$_\xcdI\x9e,2\xb2\xbaK&\xd9\x80\xa4\xee\xf7\xfd\x9c`\xbb\xb5sF\xf2\xd5k\x8e\x8e\xc7c,\xb9O@\x18*\x89\xc2\x03\xa8\xea\xa4\xdeW\xf0\xe0\xfa\x82\xb0[\xe5d8\x08H+\xd5\x02\xa0\x08h\xeb\x8ez7\x13\xf4\x8aN7\x15\xc8\xabg\xf0\xb1b\xd2\xadKa\x9a\xcb\xda )=q\xc9o{q\xe7%\xbbH\xac\xd2\xea\xa8\xf1\x8f\xe7\xe4:\xbb\xa1{\x96\x0e\xa2;\"\xe1\xae\xe0Np\xd1\xac\x83\x8cO\xa0\x82\xff\xac.J\xe6.\xe6\xe1\x10\xfc\x82\x7f\x9b\xe4\xe9n\x9fu\xdc\xcdi\xd5\x95\x06\xfc\xfa\xffzxSzI\xc8\xae\x9fG}\xcc\xee=\xd5\xcd\xf2\x87#\x8e\x93y\xfd\xd9c\x8c\\i/\x92\xe5\xe5uR\xae\xaa^\x0c\x86b\xa3\x89q\xc1:\xcdH\xa3\xa7\xc4\x8d\xc5\xdc\xb6\xc8\x8d\xa9\xda\xa7\xd6\n-\xd2X\xa2{\xafS\xa7E\xb7#\xf9\xd3|Y\xf6\x82{G\xfc\x0b\xc9\x08\xb7\xca\x9d:2\xe9\\s\xc6\xcfm\xb6\xe3\x04\x0e\xba\xa9]tX'\xdd`hr\xa2\xa4\x15\xd1\xfc\x9b\xaa\xa6\x8c\x9f\x80\xa8\xf0i\x16h|Z\xd4'\xd7\x1e&\xca\xb7\x07@\xe4\xdc\xb3\x8f\xe2\xd3\xa2\xcd\xd3\xa2C\xf9\x05\x88\"E\x9df\x02\xdb\x87\xd3\x97\x92HM\xd9\xa2\xbe\xd8\x14\x8dCEf_b\xa2v\x83F\x10\x1af=\xdeOhVJo\xca0\xabD4\xd1\xf8\x89\xdc\xb9\x80s\xe9\x82\xcb\xad\x0bZ\xd7.\x8c\x1a\xdf(7o\x0f]]\x98\\\xbd\x00!\xee^p3\xc4,\x0f\xfc]\xbf\xa0s\xffZ\xcdp\xadyl5\xc6\xbb\xad\x1f+\xcd=\x0fmm\x98\x92\xbf\xaf\x8bk\x9a\x825\xee\xdb\xe6\xd3\xfa\xbb\x06\xc1s],\xee \x81\x89\x02\xe98\x0c\x8e\xab]Zn\xc9\xe2\xd7{\xf4\x1c\xcd\\\xa6s\xf0\xf1v\x80i\xcc)w\x80L\x9cz\x07\x7f\xc7\x1d~9\xd8\x86>:PO\xc1&\x83\xf6\x94?\x8e\x8e\xddS7\x83\x88\xe3\xd3\xadJ[8\x9f\xf2\xbdU\x1eFWgtu~\x19\xaeN\x8d\xfaRV\xa4R\x87\x94Y#\x9d\x16\x1d\x1f\x16/z\x8b~w\xc0\xa0G\xab\xd1.\xae\x03\xebQ\xa33\xccj\xfa\x19\x1cb\xb6\xd3\xba5\xf8I\xab$\xedj\xd2\xa2(\x9dg\x7f\xb7\xe6\x93_M\xe8 s\xb8\xc8\xecN2\xb7\x9b\xcc\xed(\xc3\x8ezJg\xd9t\xee2\xb4\xc3,\xd4e\x16\xe24sQ\x8ar\x9b\x8dq\x9cY]g.\xe7\x99\xd5}\x86\xd8D\x93\xba\xd0\\J\x19\xa6r\xa3\x858\xd2\x0e\xe0J\xd39\xd3F\x19\xeb\x13\xba\xd4,N5\x83[\x0dO\xb9fb'p\xae\xb9\xddk:\x07\x1b\x9ej\x94\x93m:7\x9b\xde\xd1\x86'W\xb3<\xc2\xddm\x03T\x9d\x88I\x9d\xc3\xcd\xcf\xe5\x86p\xbai\xdcn\xa3X1\xa1\xf3\x0d\xed~s\xfa\x9b\xc0\xe8\x82\x1b;\xd6)\x1dq.W\\\xa83\x0e\xc1\x1e\x9b\x14 s\xc9i\x9dr\x83\xae\x14\xa6J\xab\xbc->\xc2\xfc\x14L\xdc(\xe1tL\xf2\x8b\xa0\xa1\x92l\xd2\xaa&\xa5Z\xd7\x98v\x19C\x89\xe2\xf9\xfaK<_\x0f\x8e\xb5\x9e\xae\xe9*\xd07=\xd1[~\x1e;B\x1f|\n\x16\xdb\xfa\x97\x8f\xfe0D_\xb7\xed\x86\x11\xd8\x80P%SFbCs\xba\xeeGc\x83>\"\x1b\x9c\x04N\x10\x99\x0dSGg\x83>B\x1b\xc6Gi\xf7\xd7\x17\x13\xa3\xc3Hm\x08\x8d\xd6\xee\xe1\xe8\xc7n\xeb#\xb6\x81M\x92-j\x1b\x8ckP\x99\xdc\xa0\xe8m\xab\xd8\xea\xbd\xd0\xd8\xc5<\x94Y\xcd#\x85\x9f\x1f\xd3>\xbaR\x84Q`\x95Q\x9dE\xe7-\xa8b>\x87\x97\x14\xd1K\x90\x81\xf4\x88\xc6U4\xae\xee\xaaq\xf5\xbf\x8a\x94\xd2\xc8\x0e\xc7\xe5E\xdb\xa2\xb9\xb1\xe0\xf8\xb4\xe7\xa9?X\x05\xd7Nl\x9c\x98\xea\xa2\xdbZ\xc1\x97\xe1]$c\xee\xc1\x0f\x98\xeaR\xc9\xa9\x8fI/\nDa\xda\xa7\xe1N\x0bS\xd5\xe43\xca3e]\xaa\x0fS\xb6q\x1e\xeb&\x8cCZ\x83\x92\xd06\xe8\xe3~\xa5\xd99c\xee\x8c\xcf\xf6\xbb]v\xf3~\xed-y\xfbR\xcec'i\xc4\xa9a\xc5\xc4\x84\x19\xdd*\xebM\x19&cF\xb6\xf1K\x999\xa7\xbb\x857\xf5^\x1f\x15k\x16\xf5\xf2\xdd\xd3\xcbB\xd2\xf2 j\xf41\xff\x97\xb80b\xfaW4p\xaa_\x85;\x01\xba\xb8\xd3\xdaq1\xe3!c\xa2\xb6\x8e\xda\x1a4\x84\x9a\xe4\x1aF\x88v\x9a l\x03_\xb2A\x9avNYh\x01\xda\x13n\x9aY\xf2\xadP\xd0,\xdf\x91\x1e\x91\xd6\x0b\xc2\xd1)\xee\x8ff\xf8\x15i\x86O\x05\xe6\x84\xe3\xd6\x9b\x1e\x9dO\xf4\xa3\x1e)\xb6\xa7\x13\xd8\x18Q\xddecM\xf2\x15)\xb7i^7\x1c=~\xf1\xf2\x84-Jo\xbd\xec\xf9lU\xb3\xef\xb3bc\x9d\x944_\x17\x8e\x0fV\xe4\xb3\xf5\x0b}\xc7Ao\xf8\x07>1\xbb+\x8bb=/va77J;0\xd8<0\xf1\x85\x0dk6\xf8\xabE\xaaC\x9f\xa7\xc86\xd6'G\x00\xd4\xe4\xe8\x16\xc2q\xban\x87N\xe9d\xbd\xdf\xb5[:\x07\xca&\x11d%\x1f\x95Y&\xd9\x92\xddo\xe4\x1bxK\xca\xcb\x8c@Y\x14\xf50\xb0\x8d\xeelvA\xb8,\xf6\xfd\xe2\xd4\xc0CL\xda\x87J\xe4\x0bF\xfcR\x9eEg\x92\xaa\"K\xfa\x1bC\xd2)\x19?\xc0\x94\x93ts\xb1(\xf6%kW\xac4\xcf\xc8\\$\xd5\x856\xab\x81=\xd8\xc3\xe2`jQ\xb7;\x81\xd5\x9e\xbf\x8f\xd4\xc4\xfbH\xce0i\xc2?\x14\xdaq\x80P\xf7\xda\xd8y+o\x9a\xdf\xccv\x90\xe8\x8cyv\x04\x83\xd96\x92]R\xd1Vw\xa2\xd5\xe4\xf7\xea\x11\xcbg\\\x95f`\xea\x05\xd5p\x04\x17\x94\xe76\x9d\x01\x06\xf1C\xe5e\xb5K\x86\xb5!\x9c\xfaf \xa3\xf5w@\x8a\x03\x80\xfd\xd44d\xae\x02\x81M\xf5\xb5\xe2\xf8%;\xe6\xc8lLS\x19\xe6P>/\xb2by\x89V8\x17$Y\x91\xc1S\x8a(\x81*\xde\xbc\xea Un\xd1\xf10\x9a\x05%\x85\xe9\x1e\xf5\x1b\x93,5K\xd2E;\xa8.X\xc5\x97\xc5@\x06Hv\xbb\xe9\xd0\xd9e\xe1K:\xc9y\xb5\xaf`\x99\xec\xb8a\xc1Oq\xf2\xcf\xe5>\x13!\xb1\xbb\xb2X\x92\xaa\xe2e\xf8%\xf7z\xe8h[\xf6\x13\x8bL;\xea\x0b\x884_f{^\xc8\x9f\x9eG\x9a\x0f\xb9\xe0k\x96s\xd5<\xd9\xc1{\xd7\xbe\xf2\x98\xb4O\xbb\xdd\x1f\\\xcbS\xc3\x93@]&y\xc5k\xfeo\x93\xe5E\x9a+/M\xb1\x9e\xe7\xa9\xf6\xc2w\xc0\xe6\xbe\x10\xb0|j\x0en\xafSt\xd6\xb4D\xb1Jj\xf2\x88\xb6\xeb\xfc\xca^\x9e`\xcc3P\xef\xb5zu1|\xc8\xd5\xa6\xd1\xbc\xbb\xa4\xac\xe7\x15\xa9\xe7\xfd\xbd+\xc1b\xab\xb8r(4\x07\xd0\xe6'\xad1\xda\x05W\xc2\x81\x9e\x0d\xe0b\x05\xb8\x8c\x1b!sN\x93\xb2\xaeH\xfd#\xe3\xca\xef4\x1f0\xc9x\xf2\xaa?\xcd\xcbb\xbbMk\x9f\\g#5\xa2#\x8a\x8a\xef)\xbe\x87\xe9\xd6\xeb|G\xff9Aw\xecy\xb8\xa4.\xcaj\x02d}\xda\xd7%\x0b\xb1&T\x06@\xb1\xafw\xfb\xba\xfd\xdb\xae$W|h\x1d\x0c\xcc\xdb2)M\x8d\x84\x9c\x00W\xb2\xdbM\x80\x85\xad\x17\xe1,\x99\x00\x1d\xb9JW$_\x92 P5\xf3\xd7\xaa\x95\x9e\xde\xa5\x1b\xbf\xa8H9\xd7\xa6J\x1b\xfb\xb3\xeb\xb6>R\xe9\xa8)\xcat\x93\xe6\x89P@\xcdg2c\xf5H\x0c\xa3\x1e\xd8\xbc\xec]\x9b\x17dy\xf1\xf5SA\xcd R\xea$\xef\x18D\"nI\x9aY\x9f([\xaaOG\xb0\xd8\xd7\xd2\x97y\xf6\xea\xcfGp\xcd\x14\xee\x15)k\x18\xe4c1\xa7\x8a\xd2g\xbfK\x16\nE\xea\x9a\x94\xf0\xf1\xbf\x0df7\x97;\x8a\x15\xd9\x1a\x8f\xecn\xbe\xa5Z\xb0\x85\x0bp\x89/\xb8\x80U\xfdy\xfc\xc9V+{q~\xd7\xc1\xa28\xff\\\xf1 k\xe9\x12ev\x04?kp\xab\xe1?8\x03f?2\x95\xff\xfb'\x839\xe6\xee\x1c\xf6\xb0q\x96\xd1\x01B\xe3>e\"f\x06\xf0_\xe4~I\xe0\x1f\xfb\xaa\x86dS\x12BM\x1e\xed\xa3\xb2<\xad\x8c=\xde4\xe8\x87\xd9\xe8[\x92\xe4\x82bN\xd6\xf1n\xc7\x82\xe4W\x05\xe1\x8f\xa0r\xa3\x8a\xc5\xfeU\x84\x923\xf0\xd7\xbf\xe2g\xd3N\x92vEx\x1e\x115\x90\x12\xf1z\xa4\xc0\xd3D\x08ve\xa8\x14\x06AK`\xd8\x18\xc2\xd6\x81\xb7\x87\xa39\xd6\xcc\xaf\x8a\x9a\xcc\xf5\x84p\xb0\xda%.\xcb\x04\x80\xe1\xd7z1\xc0\x8d\x1c\x10\x1d\x80Dc\xfc\x15a\xa9p \xf9\xde\x90\x8a*\xe1\x11\x9c\x9d\xfc\xf0\xee\xf5\xab\xf9\xdb\xb3\x1f\xe6\xe7\xffs\xfaz\xfe\xf1\xdd\x9f\xdf\xbd\xff\xafw\x9e\xadN?\xbc\xfe\xeb\xfb\xf3\xd7\xfe\xad^\xbe\x7f\xfb\xf6\xe4\xdc\xbb\xdd\xfb\xd3\xf7g\xc7o,\xcdD\xc0\xe7\xb3\x80\xf1\xb9|J]8K79Y\xbd\xad6\xe7\xcd \xbb\x16\xef\xc7V\xec\xa7\xee\xa3\x91\xfa\x97\xa6[h\x94\xa6\xe6\x99M\x05\x8c\xfc\x7f\x06\x7f-j\xcdS\xe9\xce\xd6\x9c\x9f\xcf\xe0\x94\xa9\xc8$3\xa3\xd0\x9d\x8fT@.N\x97\x9d\xce\xa1,\xf6\xb96f\\\x82\xfb@\xc0\xa1\xd3\xdd\xd7O\x8d\xdf\xe9\x0f[* \xf68 \xf79X\xcf#- Y\n\xae3J\x17\x9c\x078\x15\x90\xa3\x06\x8f\x91\x03X\x8fz\xbd\x0f\x91\xf3,\x01\xb7\xbc$`&\x01\xfc&\x02|&\x03q|\xd4~>\xa4\x87\xe6\xa2&G\xf4\x7f\xb8\xcb\xe1\x08\x8a\x12\xf8\xff2\xe3\xc6\x88\x8c\x1d\xbc\xdb\xa35\xa5\xd3\xac\xa7:\xbaL\xf73\xb3\xa2\x16\xd1\x8a\x8aV\x14D+J\x0b\xc8\xc5\x89Ss\xd1\x8a\x02B\x07\x85N\x1b\xc5\xe3\x8a\xdb\xd1\x83O4\x0f\na?\xe2\xc7\x1e\xe3\xa3\x07L\xe4\x8f\x1e\xf4\xf1@z\xf0Z,\xeeS\x9d\x04/\xb4\x18}\xd9\x85a\xf4\x91\xe1\xbb0\"\xdc\x96\xab\x04K\xfc\x92\x1e\x0e,\xb7\xb0\xe7\x13\xf0g\x0e\xe0LN\x15<\x0f\x8f\x12<\xb9\x04\x01\x9c\x02\x9fC\xa5\x04\xeca\xa0\x0f~\xab[\x82\xcfdB\xd8\x84B\xc8\xa4z\x1fB{\xcd\\\x87Q \xf6\xa81=x\xf2\xc0s\xec\xb8\xb83=\x18\xa2\xd1\xf4p\xc8aXc\xc7\xf4pHr\xccitz\xc0\x06\xce\xa1\x90\xf5\x83\xeb\xf4\xe0\x0e\xb9\xd3\xc3!\xd9f\x0b\xda\xd3\xc3!\xa9\xd1\x87\xfd\xe9\xe1\x90t8\x02\x07\xf5pH\x82,\xa1\x87z8$1\xee\xe0E=\xd8C\x1a\xf5p\xb8qxF\x03\xeaB\x00\xf5\xc0U\x8dkp\x1e\xb6\x89\x8fMrG,k\xe7=\x8a\x04_S\x08\xe7\x1f\x95\x10Mj#D\x93\x9a\x81\xdf\xb2\x96\xe03\x99\x106\xa1\x102\xa9\xb7eR\xb7I\xd7\x18.\xf0\xd1\x9b\x9f1P\xc1\xfa\xa8\x81\n\xde\x8b\xd1\x7f)J 2_g\x89\xa6\x96\xba \x82&\xdc\x1d?\xa1\xc2#x\xf1\xe6\xfd\xcb?\xcfO^\xcd\xbf\x7fs\xfc\x03\"\xd6\xa0\x0f}\x0c\xc7/\xce^\xbf\xb3\x87G\xa8\xd0G\x80\x88\xafP\xa1\x8f\xe0\xdd\x89-\xccB\x85&\xe8b\x1c\x1b\xfc\xce\x0d\x1c\xf8FY}\x9f%\x9b\xce\xbb\x07\xbch\xc1\x8blY\\\x9e\xbcr\xc6_\xa8\xd0l)H\xf1\xaeK\xcf\xbbO\x15\x82\xd6h\x90HB\\\n\xab0\x8a4\xbc\xdb\x8d\x03\xea2U\x85Q\xf4\xa1Y\x17\xe2\x1b\x07x\xc9\xcc\xcf\xb3t\xc3\xe3\x83\xa8\x9e\x97\xcecv?+\x93\x1a\x90\xe8\xd2\x1c\x12\x81\xd3\xe50\xf6\xa5\x97cU\xd30\x9a;B\x96\xdf!\xcd\xee\xeb\xc4\x11d\xd4\x98\xdd\xe2\x15\x84D\xe6s\xb4\xc7m3\xf5\xed\x1e\xaa\x08\"\xd4\xc8\xa9o\xb0Z\xa6\xa5\xcd\xb5\xf2\xf0\x1a\x14\xa9=\x91#\xe1\x80\x1d\x0f\x07/9\xe4\xbd\x91<7\xd1n\xbf\x18\x16\xae2\x81\x17S\xc0\x9b1\x14\xc8\xea\xe9\xb7\xdf>\xf9\x13\xf6\xf3\x00\x06\x81?\x93\x80\x95\x1dZ\xee\x9e~\xfb\xdd\xe5\x93\xbbF\x9a\xafv>\xdd/\xb2t\xf9gr\xa3\x1c\xe6/\xc9M\xb7\\\x16^\xbf\xee+\xc2\xcb\x1a\xfd\xb5\xd9\xac\x88\x96\x98H\x13\x15\xbc\x99\xe9{pi\x9c/\xbb2-\xca\xb4\xf6\xda\x13\x07\xa1KR\xe4\"\xc4c[\xfalH\x0f9\xe5\xc9\x04\xaf5\xee!\xa1<\x18\x01\x9e\xcc\x00\x7f\xd9\xe4\xc9\x14\xf0e\x0c\x84H\xa5\xc3\x13\xe5'\x8f\xb0\xd2\x88\xca\x19\x14B_Y\xe4+\x89<\x19\x88\xdf\xed\x10*\x83\x0eF\x11>(\xb0i\x81'\xc5M\x06\xb5W\x8b|nw\xd8\"zt\xf7\xb4\xb8\xf99\xc9\xeb4's\xb7\xc5\xe9\xb64\x1d\x16&JH\xe1D\x13JB#8\xc4\x01\xb9\xcfQ\xf2\x185H@\x0f\x14|\xa4/z\xc0\x80\x1f4\xf8\xc9\xda\xc3\x90\x80\x95\xac>2U\xff<\xa6\n(i\x8a\x17\x13h\xe6\xb87.\x07O\xa99a\xffx \x89\xe8\x14\xd1\x9d\xcbQ\xe4\xd1\xcb\xd8P\xe67T*\xbfd\x01\xc5\xc7,\x9e\xd8\x15\xce\xcc\xfd\x0dZ\\\x9d \xf6\xa4\xae\xc9v\xc7B\x99\xeb\x02\xb6i\x95\x91d\xc5\xde\xdd\xdd\\\xd4\xe2!B\xe9\xa8\xe8\xc4\xad\xb4\x0c\xd1J\x1e\xbd\x94\x99\xa4@\x97\xe6\"\x8f\xe3\xd0]\xb5\x98.\xe7be.\xe5'#\xff$\xb8\xb6\xca\xdd\xab\xcce\xba\x102\x99\x13S\x964q^\xd18\x98b\xbev\xf1\xb9`\xf1\xb8J\xf1\xb84\xc1]\x8fx_\x84(\xf3\xe8\xbc\xc5p\xdfM\xa0n!\x1c\xd3\xe0X\x99\x16\xd5\x80DlV \xd6\x1b\x00$v\x0d\xd9\x8a\x9e\xc1\xba\xe5\xf5\x0ew\xb3\xca\xf2r\xa2\xeb\x9d\xe4\x1dl}w\xb9\xbe\x18\xeb\x0b\x1e$_A[\xcf\x94\xb1\x89\xffp\xc4\x0f\xa9\x94\x10\x11V\xb3\xeb\xd5\x13\x13hx\xcdoQ\xe7\x8b\xd7\xf7_\xa8\xa5\xc4\x9c\xd5S\x7f 5\xeb\xf3\xc5\x0d\xafM\xe5]\xc1{\xa8\x9a|4\xec\xe8:pV\x15\x14\xa0 \x0dj\xc7\xaerl\xeafJ\x8dkU/Z\xd5\xd2\xcb\x9f\x11\xdf\xdc\xef\xbe\x15\xba\xcbHR\x11f\xeb\x7f\xaaV\x97<\x8a\xfe\x13\xa4yU\x93du?hZ\xa7\x99\x0bc\x02\x136]i\x84\xf9aI\xc8\xf4\x1c\xf0Q\xa1\x93\x16\xdb\xa3\xe0Up\x0f\xdcE\xf7\xc0Y\xbe\x18\xf0r\x0d+\xcf\xa2\x85\x18-D3D\x0b\x11\xaf\x86\xa3\x85\xe8\x02\x0f\xb6\x03^\x07p\x88\x16\"\x02\xb0\x13\x05\xfe\x93\x05\xbe\x13\x16-\xc4\x01\x04P\xe2du\xb4\x10\xf1*\xf4\x8b\xb0\x10\xf11\xc6\x80g(N\x94 \xcb4\xc3\xd4\xfd\xa2\xb6\xb2g\x8f\xae-\x8c]7\x13\x96p\x06\xef2\xce\x10P\xca\x19p\x9a\x0f\xa3\xed\xd0e\x9d\x01\xd7' \xfb\x05\x9f\x12\xcf\x80\xef\x1b<\xfa\x07t\xb9g\xf0#\x00<\x89\x00\xbf\xd2\xcf\x00\xf8x*=x\x8e\x05\x02\xc6\x03\xbe\xa5\xa0\x01/\x00\xfa \x05\x02\xba\x023\x07\x8f\xb2\xd0p\xcb\xc4aEW\x1fp\xb1bht\xc6\x07\xdfQ'O \x8eH3=`\xe3\xcf\xd0\x08uqj\xee\xa84=`c\xd5\xf4\xe0Sb\x1aB\x16\x1e\xee\x04-\xc1\x1b=V\xe7w\x01[v\x1a\xc6\x11\xe42 T\xf0.A\x0d\xb7';}\xcey\x10\xc64\xc0\x9b\xe0*\x04\x1c\xd4%\x04p\x0f\x029\x08\xbe\x07x >\x07\xa8>\xf8\xef\x0c \xbe\x13\x0e\xe1\x93\x0e\xa1\x13\x1ft\xe0\x97\xe0q\xf0\x97\xe0\x8es\xd4C\x00_\x02\xf8\x81\x8f\x94\xd4\x83WYk\xb8\xa5a9\xa3\x1b\xf5p\x1b\xa4\xe1\xd3\xa5[\xc0\x86{\xa2\x11\x9a\xa2\xea\x87\x80\x0b\x16\xd5\xc3m\xb0\xd3\x15n\xaa\x87\xdb\xa0\xcc\xa7$6\xdc\x12M\x88\x90W=\xdc\x06q\xde\xa5\xb2\xe1\x96\x08\xc3\x85\xdd\xea\xc1\x1d\x8c\xab\x87\xc3\x8f+\xe4t\x86\x8c\xfaE\xe1\xc2T\xe4\x06tUn\xf07\xcb|\xcd\xb1;x A]\xf5I\x08\xb1\x08\xf1\xaeu \xf1$\xe2\xb1\x079\xc4\x93\x88\x11\xfc\xb7\x84\x04\xdf \x87\xf0I\x87\xd0\x89\xbf\xed\x93\x88_\xf5oh8\x82\xa9_*\x01Y\xc7TB\xd0\x02\x0e[\xbe\xcer\x13f\x08^\x18\xb80$\x15|\x8aW\x98\xc1\xa3\xac\x85\x19<\n^\x98\x01W\n\xc3\x0c\xdeE2\xcc\xe0\x7f\xf4\xe2\x80\xac\xb7\xe1\x89\xd5Y\x9d\xc3\x0c\xdeW\xfa*\x04\xaf\xe7`1\x87\xb88\x1d\xc2h2\xfd<\xa9\x1c\xd01\x02*\x8c\xa6\xd5\x8b\xa5!\xc6;\x07l9\x13\x0f\x94\x1e\x95\xc6!\x90vt\xb1\x14\x146cA\x15T\xd5q\xf0\xa9<\x0e~:\xceG\xb3\xb9kBv\xc1O\x93{hq\x8f\xd1q\xf0\x19#\x07o\x19\x17\xb4\x19\x036\"\xaa\xe6d\x17\xbc\x99\x05A\x0c\x03\x9f\xba\x94]\x08b\x1c\x841\x0f\xfcjWv\xe1v\xc9\x0c\xb1\x1a\xb0\xb5.=P\x86U4\x07\xaf\x1a\x98]\x08br\xc8\x81m\xe7W'\xb3\x0b\xb7@\xe3\x0eY\xed\x1c\xfc\xb7\xb7\xef\xc6\xf6\x94\x83\x01\xcc\xf1\xde\x1f\x9e\x12\xd0\x93A\x10\xc0$\x08\x93}\x01\xcc\x82\x10\x86A\xa8\xd4\xbb=\x02\xfd\xe5\xdd\xe4\xd2.T\xd6\x85H\xba\x00\xc6\xfaI\x10\x18#\xe3\x0eN\x9d_\x1co\xd3\xca\x8f,\x1cI\xa8\xca\xea\x80\xef\x1d\xd7\xabO\x95u@[\xd2\x08\x0b\x1a-\x0c\xf1\"\x10\xad!\x90\x1c\xe4\xe0!C\xd0\xfa\x00=x\xf0b\x00\xf8J\x7f/F\x80\x1f3\xc0_\xd6\x1f\x96\x1c\x1f\xc9\x8e\x95\xe9\xc8\x170\xfc\xa4\xb9\x9f8\xf2b\x1aN(p\x08\x90\xda\x07\xa0\xc5OB# @v\x8dq\xdcy\xf6\xe8r\xcaa\xfdCSVz\x87\xf0j\xef`\xaa\xf8\x0e6)g\x96h\x93Wy4\\\x12s|\x88\xd2\xb2\x83\x8b\xdfX\xdeQ\x0b\xb1\xbc\xa3\x02\xb6\xcbF\x9b u\xa8J[\xa8\xeb?\x04\xf3\xecWz\xbe\x97w\x9e\xd7t\x9e\x17r\xf8\xab\xb7\xa0K\xb6\xa9\xab\xd1\x83\xcf\xcd\x16b\xaa\x10\xab\xfcv\x1e.A\xdc*y\xf4d\x18\xce\x84U\xeb\x07\xe8\x06j\x18}!c\xb8l\xe9\xa1\xd3^\xbd4U\xc1[\x9e\xc9B\xe2gi\xbed\x01y\xd5\xb6\xa8\x1eU\xabK\xf8j\xf6\xcd\x1fb\xd1\xf0\x018\x97\x95\\T\xb1hx\xfb\x15.i+\x16\x0d\xef\xfe\x15\x83.\x16\x0d\xef\x83\xcbh\x89V\xa5n\xa9\xc4\xa2\xe1\xfd\x1f=\xc6\xe5\xcc\"\x89E\xc3-\x18\x11\x19\x14\xa1\xa8\xbf\xe4\xa2\xe1n]\xde\xef\x80\xbdBtA\xa0(\xd3M\x9a'B\xd16\x9fIO\xf6\x91\x06\x15\x1f,\xb5&\x13j\xdb\xbeP\x9f\x1e\xd248\xc9\xe1\x9c\xc8'\x88\x8e\xa0\xbe\xa0}\x8b\xcad\x9f(\xcb\xaaOG\xb0\xd8\xd7\xd2@8{\xf5\xe7#\xb8\xd6\x890\xf1\xe6\x11\xa45\xd4E\xbfo]\xd7\xd46Y\x90\xba&%|\xfco\x8b\x81\xed\xce\xachG\x10K\xabC,\xad\x1eK\xab\xbbl\xa7XZ]\xfd\xc6e\xbdq\xc0E\xac\xc7\xc2\x99\xb8\xf1\xba\xc5\x98\n\xb1p\xa6\xfb\xf2\x1f\xfc\x164\xe6\xec\xc3\x01\x95M\x87;pq\xb0]\xa2\xa8\x80\xcd\x9aC\xca\x0e\xf0\x90\x1f\xe0<\xfb\xb5\xe0\xc1vpi\xf8>\xa0\x0e\xd4*xp\x03<9B\xc1+\xdb\xcdg]H\xc0/M \xd8\x89\x02\xff\xc9\x02\xdf C\x1e\xe7\xb5M0YkN\xe7|\xf3!~\xa0.O\x94\n\xe8\xbb\x08 \x01\x948Y\x1d\x0bg\xe2U\xe8\x17Q83\x96V\xf7m\x19-\xc4\x16\xa2\x85\x18-D\x1b e\x07x\xc8\x0f\xf00<<\xd8\x0ex\x1d\xc0!Z\x88\x08\xc0N\x14\xf8O\x16\xf8NX\xb4\x10\x07\x10@\x89\x93\xd5\xd1B\xc4\xab\xd0/\xc2B\xfc\x05\x03~ci\xf5!\xc4\xd2\xea\xb1\xb4\xba\xeb;/\x02\xc0\x93\x08\x88\xa5\xd59 \x05@\x1f\xa4@\xf0\xaa^\x1eK\xab\xbb!\x96V\xd7\x036\xa2O\x0f\xb1\xb4\xba\x1dF\x10\xe42 T\x88\xa5\xd5\xb1&\xb8\n\x01\x07u \x01\xdc\x83@\x0e\x82\xef\x01^\x82\xcf\x01\xaa\x0f\xfe;C\x82\xef\x84C\xf8\xa4C\xe8\xc4\x07\x1d\xf8%x\x1c\xfc%\xb8\xa3A\xf5\x10\xc0\x97\x00~\xe0\xe3I\xf5\x10K\xab\xfb\x91\xe6\x97\xdf\xcc\x01\x1b\x14\x8bF\x88\xafB\x86\x0b\xa9\xd5\xc3m\xb0\xd3\x15\x94\xab\x87\xdb\xa0,\x96V\xf7$.\x96V\xef\xc0\xe1\xc7\x15r:s\x07\x00\xe3%K,\xad\xde\x07\x7f\xb3\x0bu\xd5'!\xc4\"\xc4\xbb\xd6%\xc4\x93\x88\xc7\x1e\xe4\x10O\"F\xf0\xdf\x12\x12|'\x1c\xc2'\x1dB'\xfe\xb6O\"\xb1\xb4\xba\xb3\xb6\x82\x1e\x82\x17\x06.\x0cI\x05\xdf\xea\x0cz\xf0\xac\xd9\xa0\x07\xcfJ\x0ez\xc0\xd7w\xd0CP\xd5\x07=\xf8\x1f\xbd8 \x8bGxbE\x95\x9a\xd0\x83\xf7\x95\xbe\n\xc1\xeb9X\xcc!.N\x870\x9aL?O*\x07t\x8c\x80\n\xa3i\xf5bi\x88\xf1\xce\x01[s\xc3\x03e,\xadn\xfb\xd4K\xc7\xf9h6\\!H ~\x9a\xdcC\x8b{\x8c\x8e\x83\xcf\x189x\xcb\xb8\xa0\xcd\x18\xb0\x11\xd1\x85$%x3\x0b\x82\x18\x06\xbe\x05&%\x041\x0e\xc2\x98\x07\xfe\x85'%\xdc.\x99!V\x03\xb60\xa5\x07\xca\xd0r\xc3\xbe\x85*%\x0419\xe4\xc0\xb6\xf3/`)\xe1\x16h\xdc\xc5\xd2\xea\x16\xf0\x94\x80\x9e\x0c\x82\x00&A\x98\xec\x0b`\x16\x840\x0cB\xa5\xde\xed\x11\xe8/\xef&\x97v\xa1\xb2.D\xd2\x050\xd6O\x82\xc0\x18\x19wp\xea\xfc\xe2x\x9bV~d\xe1H\x8a\xa5\xd5\xf5\x80\x17\x81h\x0d\x81\xe4 \x07\x0f\x19\x82\xd6\x07\xe8\xc1\x83\x17\x03\xc0W\xfa{1\x02\xfc\x98\x01\xfe\xb2\xfe\xb0\xe4\xf8Hv\xacL\x8f\xa5\xd5\x158\x00-~\x12\x1aI\x00\xb2k\x8c\xe3\xce\xb3G\x97S\x0e\xeb\x1f\x8a\xa5\xd5[\xb0\xcdf,\xad\x8e\xb8^u\xaa#\xb7\n\x8aE0\x15\x88\xa5\xd51\xb7T\x9e\xd7t\x9e\x17r\xf8\xab\xb7\xa0K\xb6XZ\x1d\xad\xd6\x9c\xb7J\x1e=\x19\x86\xf3\xab+\xad\xde\xa3\xe1\x7f\xbb4\xbc\xe09<\x15\xd4\xa4\xa98IY\xc8\x7f8\xe2\xe6&%J\x84\xac\xedz%;;\xa8\xd6)\xc9V\xb2h&YQ\x8d\xbfP\xabu\x0eHi\xd8\xf1\x03\xa9Y\x87/nx\xfd\xc6\x0f\xa4\xda\x15yEd\xe1\xd0R\xfe\x9b\xd1F\xad\xe5\xf6\x9a\xf8/{R\xde<\xeec\x90\xf9;\x1fN_\xc2\x96\xd4\x17\xc5\x8a\xf6\xcfk\xc8\xcf\x16IEf\xed\x88gWO\x16\xa4N\x9e\xcc~ \xf5\x1b\xba\xe98*I\x04_j\x03\xb19\x14\x94C\x85\xae\x95\xb5z ;\xd4F\xc6\xa5lX\xc0V\x05\x1d`A\x19\x141\xda\xe0\x19(\xde)k\xddZ\x15\xabV\xa1\xf6\x12\x10\xe5\xeb\x02\xaf\xc8\xae$T\xd4\xae\x9e\xc1.#IE\x98\xd7\xecS\xf3 \xc1'H\xf3\xaa&\xc9*\xbe80\x00\xa7\xb4\x95\xf3\x17_\x1ch\xbf\xc2\xe52\xc6\x17\x07\xba\x7f\xc5\xa0\x8b/\x0e\xf4\xc1e\xcb\xc7\xc3\x96n\xa9\xc4\x17\x07\xfa?z\x8c\xcb\x99\\\x15_\x1c\xb0`D$\x16\x85\xa2\xfe\x92_\x1c0\xf6\xefYV\x9f\xef\xb8XK?\xd6\xd2\x8f\xb5\xf4\xcdVA\xac\xa5\xaf~\xe3\xb2K8\xe0R\x14b\xa5T\xdcx\xddbL\x85X)\xd5\x1d\xed\x01~\x0b\x1ac\xd5s@\xa5O\xe2\x8e\x12\x1cl\xb7f*`\xd3$\x91\xb2\x03<\xe4\x078O5-x\xb0\x1d\\\x1a\xbe\x0f\xa8\xa3\xa2\n\x1e\xdc\x00O\x8eP\xf0Jo\xf4Y\x17\x12\xf0KS\x02v\xa2\xc0\x7f\xb2\xc0w\xc2\x90\x07Um\x13L\x9a\xa2\xf36\xa6\xf9\x10?P\x97\x8fE\x05\xf4\xe5\x93\x84\x00J\x9c\xac\x8e\x95R\xf1*\xf4\x8b\xa8\x94\x1ak\xe9\xfb\xb6\x8c\x16b\x0b\xd1B\x8c\x16\xa2\x0d\x90\xb2\x03<\xe4\x07x\x18\x1e\x1el\x07\xbc\x0e\xe0\x10-D\x04`'\n\xfc'\x0b|',Z\x88\x03\x08\xa0\xc4\xc9\xeah!\xe2U\xe8\x17a!\xfe\x82\x11\xde\xb1\x96\xfe\x10b-\xfdXK\xdf\xf5\x9d\x17\x01\xe0I\x04\xc4Z\xfa\x1c\x90\x02\xa0\x0fR x\x95\xab\x8f\xb5\xf4\xdd\x10k\xe9\xeb\x01\x1b\xab\xa6\x87XK\xdf\x0e#\x08r\x99\x04*\xc4Z\xfaX\x13\\\x85\x80\x83\xba\x84\x00\xeeA \x07\xc1\xf7\x00/\xc1\xe7\x00\xd5\x07\xff\x9d!\xc1w\xc2!|\xd2!t\xe2\x83\x0e\xfc\x12<\x0e\xfe\x12\xdcq\x8ez\x08\xe0K\x00?\xf0\x91\x92z\x88\xb5\xf4\xfdH\xf3Kh\xe7\x80\x0d\xf7D#\xc4\x97\x9d\xc3\x05\x8b\xea\xe16\xd8\xe9\n7\xd5\xc3mP\x16k\xe9{\x12\x17k\xe9w\xe0\xf0\xe3\n9\x9d!\xa3~Q\xb8b-\xfd>\xf8\x9b]\xa8\xab> !\x16!\xde\xb5.!\x9eD<\xf6 \x87x\x121\x82\xff\x96\x90\xe0;\xe1\x10>\xe9\x10:\xf1\xb7}\x12\x89\xb5\xf4\x9d\xc54\xf4\x10\xbc0paH*\xf8\x96\xe3\xd0\x83g\x91\x0e=x\x96\xee\xd0\x03\xbe\xa0\x87\x1e\x82\xca|\xe8\xc1\xff\xe8\xc5\x01Y-\xc4\x13+\xaa\xb6\x88\x1e\xbc\xaf\xf4U\x08^\xcf\xc1b\x0eqq:\x84\xd1d\xfayR9\xa0c\x04T\x18M\xab\x17KC\x8cw\x0e\xd8\"+\x1e(c-}\xdb\xa7^:\xceG\xb3\xe1*\x7fJ\xf0\xd3\xe4\x1eZ\xdcct\x1c|\xc6\xc8\xc1[\xc6\x05m\xc6\x80\x8d\x88\xae\x1c*\xc1\x9bY\x10\xc40\xf0\xad(*!\x88q\x10\xc6<\xf0\xaf4*\xe1v\xc9\x0c\xb1\x1a\xb0\x95H=P\x86\xd6\x97\xf6\xadL*!\x88\xc9!\x07\xb6\x9d\x7f\xc5R \xb7@\xe3.\xd6\xd2\xb7\x80\xa7\x04\xf4d\x10\x040 \xc2d_\x00\xb3 \x84a\x10*\xf5n\x8f@\x7fy7\xb9\xb4\x0b\x95u!\x92.\x80\xb1~\x12\x04\xc6\xc8\xb8\x83S\xe7\x17\xc7\xdb\xb4\xf2#\x0bGR\xac\xa5\xaf\x07\xbc\x08Dk\x08$\x079x\xc8\x10\xb4>@\x0f\x1e\xbc\x18\x00\xbe\xd2\xdf\x8b\x11\xe0\xc7\x0c\xf0\x97\xf5\x87%\xc7G\xb2cez\xac\xa5\xaf\xc0\x01h\xf1\x93\xd0H\x02\x90]c\x1cw\x9e=\xba\x9crX\xffP\xac\xa5\xdf\x82m6c-}\xc4\xf5\xaaS\x1d\xb9UP,\xef\xa8@\xac\xa5\x8f\xb9\xa5\xf2\xbc\xa6\xf3\xbc\x90\xc3_\xbd\x05]\xb2\xc5Z\xfah\xb5\xe6\xbcU\xf2\xe8\xc90\x9c_]-\xfd\xa6*x\xcb3YH\xfc,\xcd\x97, \xaf\xda\x16\xd5\xa3ju _\xcd\xbe\xf9C,\x1a>\x00\xe7\xb2\x92\x8b*\x16\x0do\xbf\xc2%m\xc5\xa2\xe1\xdd\xbfb\xd0\xc5\xa2\xe1}p\x19-\xd1\xaa\xd4-\x95X4\xbc\xff\xa3\xc7\xb8\x9cY$\xb1h\xb8\x05#\"\x83\"\x14\xf5\x97\\4\xdc\xad\xcb\xfb\x1d\xc8\xd7\x86\x8a2\xdd\xa4y\"\x14m\xf3\x99\xf4d\x1fiP\xf1\xc1Rk2\xa1\xb6\xed\x0b\xf5\xe1#M\x83\x93\x1c\xce\x89|\x80\xe8\x08\xea\x0b\xda\xb7\xa8L\xf6\x89\xb2\xac\xfat\x04\x8b}-\x0d\x84\xb3W\x7f>\x82k\x9d\x08\x13/.AZC]\xf4\xfb\xd6uMm\x93\x05\xa9kR\xc2\xc7\xff\xb6\x18\xd8\xee\xcc\x8av\x04\xb1\xb4:\xc4\xd2\xea\xb1\xb4\xba\xcbv\x8a\xa5\xd5\xd5o\\\xd6\x1b\x07\\\xc4z,\x9c\x89\x1b\xaf[\x8c\xa9\x10\x0bg\xba/\xff\xc1oAc\xce>\x1cP\xd9t\xb8\x03\x17\x07\xdb%\x8a\n\xd8\xac9\xa4\xec\x00\x0f\xf9\x01\xce\xb3_\x0b\x1el\x07\x97\x86\xef\x03\xea@\xad\x82\x077\xc0\x93#\x14\xbc\xb2\xdd|\xd6\x85\x04\xfc\xd2\x94\x80\x9d(\xf0\x9f,\xf0\x9d0\xe4q^\xdb\x04\x93\xb5\xe6t\xce7\x1f\xe2\x07\xea\xf2D\xa9\x80\xbe\x8b\x90\x10@\x89\x93\xd5\xb1p&^\x85~\x11\x853ciu\xdf\x96\xd1Bl!Z\x88\xd1B\xb4\x01Rv\x80\x87\xfc\x00\x0f\xc3\xc3\x83\xed\x80\xd7\x01\x1c\xa2\x85\x88\x00\xecD\x81\xffd\x81\xef\x84E\x0bq\x00\x01\x948Y\x1d-D\xbc\n\xfd\",\xc4_0\xe07\x96V\x1fB,\xad\x1eK\xab\xbb\xbe\xf3\"\x00<\x89\x80XZ\x9d\x03R\x00\xf4A\n\x04\xaf\xea\xe5\xb1\xb4\xba\x1bbiu=`#\xfa\xf4\x10K\xab\xdba\x04A.\x93@\x85XZ\x1dk\x82\xab\x10pP\x97\x10\xc0=\x08\xe4 \xf8\x1e\xe0%\xf8\x1c\xa0\xfa\xe0\xbf3$\xf8N8\x84O:\x84N|\xd0\x81_\x82\xc7\xc1_\x82;\x1aT\x0f\x01| \xe0\x07>\x9eT\x0f\xb1\xb4\xba\x1fi~\xf9\xcd\x1c\xb0A\xb1h\x84\xf8*d\xb8\x90Z=\xdc\x06;]A\xb9z\xb8\x0d\xcabiuO\xe2bi\xf5\x0e\x1c~\\!\xa73w\x000^\xb2\xc4\xd2\xea}\xf07\xbbPW}\x12B,B\xbck]B<\x89x\xecA\x0e\xf1$b\x04\xff-!\xc1w\xc2!|\xd2!t\xe2o\xfb$\x12K\xab;k+\xe8!xa\xe0\xc2\x90T\xf0\xad\xce\xa0\x07\xcf\x9a\x0dz\xf0\xac\xe4\xa0\x07|}\x07=\x04U}\xd0\x83\xff\xd1\x8b\x03\xb2x\x84'VT\xa9 =x_\xe9\xab\x10\xbc\x9e\x83\xc5\x1c\xe2\xe2t\x08\xa3\xc9\xf4\xf3\xa4r@\xc7\x08\xa80\x9aV/\x96\x86\x18\xef\x1c\xb057\x16\xd8\xee\xabf\x9fCRCF\x92\xaa\xd6\xe3+r\x02\xf7\x1e\xdf\x83\xe5ER&\xcb\x9a\x94\x14\x13a\xd61Td\xb3%y#\x84>~xs\xbf\x82]R_\xb0\x0e\xb4\xe8\x9a\x0cx}o\x14\xcdz\x9fe7\xf0\xd3>\xc9(WV\x9cg\xa2\x0b\xc6\x9d\x07I\x05i\xaeG\xf0\x89v\xffxS\x14\x9b\x8c\xcc\x18/\x16\xfb\xf5\xec\xd5\xbed\xa9t\x9f\x1er\xea\x19\xca\xea\xa2\xd8g+X\xb0\x825\xfa\xc4\x88e\x92\x17y\xbaL2\xb6A\xf4=> \xb3\xcd\xec\x88\xb2\x90%\x05\xde\x9b\xdd\xa3\xa2\x82\xd5u].\xc9\xae&\xab\x87\xb3\xdf\xe9\x9b\x9e\xe4\xb0\xa3LM\x97\xe4\x08j\x92l+\xd8W\xfb\x84\x0e\x9f\xd7\x04\xd8\xa5\x19\xa5\xae.x\xf9\xd44O\xca\x1bH\xb2L\xcf\xbb\x9b\x1d\x11\xb5]\xeb\x0br\xa3\xef\x92|\xde\x91\xa5,#\xbc\xafd\xb1\x1e\xb6\x18\xc8g6\x95\xc7\xf9\xcd\x0c~,\xae\xc9\x15)\x8f\x98\x84\xfb\xf8\xe1\x8d\xfep\xcf\xed\x01\x8a\x86.W\xfdz]^\x90-\x81O\x17u\xbd\xfbt\xc4\xff[}b\x95\x0e\xf2B\xfcz\xc4V\xd92\xc9\xa1`\xbb\x89q`\xa8P8\xecw\xa2\xf2\x90\xa1?R^\x91\x92\xb3a\x9b\xecD\xbde:\x02v\xec\x13%\x8a\x98\xfb#\xe5Uj\x13\xfd\xd8\xd6E\x96\x15\xd7\xd53\xc3\xdc\xfd;\x9c\xac\xdb\x11\xd0)\xdf\x95\x05U\x96\xabf\x90LMW\xd5~KV\x86\x12G\xff\x0e\xc79\xfcx~~\n?\xbc>\x17\xa5})\xad|\x83\xde0%\xa6_\x99\x7f\xeb/\xf1\xf3\x9b\x1d\xf9\xfb\xdf\xfe\xae\xfd\x98\xc9\xf2=\x9bk\xb1\x86\xb8\xbcg\xb3\xb0+\x8b\xd5~I \xc9\x81\x94ea\x88\xde\xfew8n3R+V\xa58\xa1\xfc\xe1\xbau\x99,\xa9L(\x8a\xcb\xfd\x0eD.\x02P\xe5\xb6\x82\"7mt\x03\xa9\x1f?\xbcat]$WlYm;{a\xc57C\"\x87A\xff\xff\xaaHW\x90\xe4&\xbf-'\x8am\xfb\x92\xac\x8b\x92\x1c\xc9\xc6\x14gR\xa7\x8b4K\xeb\x1b\xc8 YU\xdcN\x00&\xa2\xca+c\xc0L\x91Sq\x98o\x08k\xc0\xf6\xdd\x0c\x1e|\xac\x88L\x96\xa7\\\xa1\xcb\x8e\xca\x19\xbe\xee\x92<\xd9\x98F\xbc(IrIe\x87@:{\xa8_-\xef\x8a\x9a<\xe3E\xc4\xd7\xfb|\xc9w\n\xa5]\xc8\x9b\xe5\xbe,I^g7\x1dg\x9e%#\xbaX\xaf\xd3e\x9ad\x16=\xb2\xd8\xaf\xa1$T;\x90#\x96\xa7\x9c\xd6\xb2\xb3=\x9d\\f\xfe4\xfbkA6i\x9e\x9bl]j\xbd\x19\x84\xfe\xcd\x8e\xcc\xf8zNvi5[\x16[\x93\xc4\xd4\"\xdb2\xcf\xcf\xc2 H\xd8\x00\x99\xc9\x99nw\x19\xa1\x8a\x8e-~\xa8vd\x99\xae\xd3%Td\x9b\xe4u\xba\xd4\xc4\x15\xb1\xfd\x16`R\xf8\x1c\x07\x0c\x16\xc7[*:\x16DV\x04\xe9\x18\x0c\x03\xdb@\xe6\x9a/\x8a+\x83\xb1\xc1\x87*\x96s\x7f\x98.j>\x1d\xe77\x9f\xda\xe3D\x92CR.\xd2\xba\xa4\x9b\xcfB\x95\x90\xd1\x03tIV\xe4\x1b>#\xc9p\xca\xa8\xd4dB\x9fS\xb5\x18\x9aS\xdd>\xa5U\xa4Yf\xa7r\xe1g\xe9\x82\x91*\xe4z\x05\xd5~\xb7+J\xa69w\xc9\xf2\xf2\xf1>\xa7\xff\xa1\xfa\x92\xcf7\xb3J\xfa\xe8\x98E\xa35\x1e\x8a5\xeck.|\xe4v\xae\xa8\xe0KV\xab\x94\xefm\xd8\x90\x9c\x94I\xcd\x08\xa6G\x87\xa6<\xc0\xb1F\xde\xf1)\x1a\xf6\xf3\xfasB\x170\x9bv\xa1\x99;\xe3\xdd\x99\xd9\xf5\x8d\x83]?\x14\x86\x8a\x1e\x94U\xcf\x9e\xc3\xbf\xed\x16\xb3\xef\x8b\xe2\x9f\xb3\xd9\xec_\xfa\x0f\x93\xfc\xe6\x88\x9ak\xf4\xeb\x1d7@\xde&eu\x91d\x94\x89f\xc2Ml\xea\xf7l\xe86]\xf7:\xfd\x98o\xdbn\x19Qla\xb3\xaf\xfe\xbf\xe7\x90\xa7\x99q\x81\x9ai\xd1\xacDzhc|\x94rP\x1a\xdb\xb0\xb8iM\x15)\xb1\xf9\xc3\x1c7\xd2\x01:\xc0\xb6\xaf4:\xff\xbe\xc6\x0cyL\xcf\xa23\xf6\x035\xe5\xeeC\xd2\xd1*T\xe3\x88\xda-\xc3\x1e\xd8\xac\x0f;i\xc4x\x9e\xdd\xc8s\xd3\xe0\xc0\xdb\x98\x8e\x90\xack\xc2\xad\x19z\xde\x1e\x92\xfc\xf8\xfe\xb0\x0bq\xa0\x93$\xf2\x13\x1c\x11+\xf3\xde\xba(f\x8b\xa4d\x83\xfb\xfc\xf8f\xf6\xf3=\xce-~\xd6\xd0\x1f\xab\x18)\xf7\xe8\xb7T\xbd\x0c~\xfe\xcf\xb3\xf7\xef\x86\x7f}\xfe\xfc\xf9s\xfd<\xd2\xef[?\x00\xb7\xa9\n\xbaM\x85\xc1\xc0\xcf*\xfb\x8aH\x87\xdbf\x9f%\x9a\xdavC\x14\xf4\xf3\x15i\xd5\xfc\x11\x90\xed\x82\xacV\xad\xc2\x17N\xc4D\xe3=\xe8\xa8]\xee=\xfc\xf4\x1f\x94\x1d\x9f\xc4!W\xf1\x8aJ\xe6\xce\xe4\x96\x7ff0\xa2\x93\xe5%\xdd\xf3\xedam\x9dfD/\x7f\xa5|8%eU\xe4\xc6m#<8\xec\x9d\x979\x9b\x99\xe7\xf0D\x8f\xb1\xf9\x98]e\x8ao\x9f\xe2\xa5?\x80\x91\x8a{\x8c7\xf7\x9e\xc1=\xdd\xaeQ\x87;\xe3#\xba\xa7{Q\x89\xe1bcy\x97l)\xbe\xff\xcbI\xfe\x7f\xc6\x8f\xe9Xz\xdfb\x07t\xb2\x16\x07\x03uM\xf0\xd9L+\xb8&Y\xf6\xe82/\xaes\xb6\xaf/\xd8#O\xcb}U\x17[\xc3\"W\x97\xe0\x117@{\xebR\xbe\x12\xd4tK\x17Z\xbe\xd1\x9c\xeb\xd9\xb2\x1bv\xf2\x89m\x08\xb9\x0e/\x8al%jg\xb5\x941\x0f\x96X\xbf \xbcEb\xf9\x0e\xf1\xb1n\x9a\x95\x0b\x0f\xa8|\x90\xac\x18\xb8\x15\xa4\xe7\xec\xef\x7f\xfb\xfbC\xc3\"\x1f\xbbF\xd4\x8e\xcc\xcb\x84\xb1\x81\xa2{2{\xfa\xe4iu\xcf0\xed\xdd\x7f\xd9\xe2+,'1\xf3\x85\xad3x$\x00\xabr\x8cj\x1c\xfe\xf2\xbe\xa1\xee^34.\xf9G\x15i\xaevv\xc9&\xcd\x19\xefZb\x14\x9c\xed\x07M\xd8O\x92w\xff*\xd1\xcb\xbb\x8dV/\xd7:\xa7\xba\xde\x9d\xce\xaa\x8a\x0c\xfc\xf2F~\x18O\xba\xe6\xfb!\xe8\xf4\"\xd9C\xffW8m\x92\xaa\xe2\xde\xa8\xd3dC>\x90\x9f\xf6\xa4\xaag\xfc\xf7\x1e\x92\x9f\xf6\xa4\xbca\xcd):\xca \x02\xdb\xa2\xaa\x810\x97\x08\xf3\xa1\xcc\xe0\xa4\xee\xec\xdd]}\x03i?\xce\xa4n^\xec\xca\x0b\xd8\x16%\x91\xbe\xaf\xaea\xa3 !p2ES\x92\xcf\x14\xe6\xc5\xd03n\xb0\xff\xc9\xf7\xdb\x05?\xd9K7\\\xc7\x17\xd4\xa7\xbf\xcb\xa8e\xb1\xcf\xeb9C\xd2\xdf\xe6\xd7I\x05\x15\xa9\x8f \xad+\xe9I\xac`\x9f\xf3\xc5\xb4\xe2\xce\x98\xebT\x84\x8c\xe9\xe7\xcfz\xa3e\xbd[\x13\x08\x9a\x1b\xb6.\x82\x177\xfc\x895\xf8p\xfa\xd2\xebv\xed]\xb1\"'\xf9\xba\xf0\xbeS\x13&\xe5@}\x9f\x93\xfa\xba(/Q\xdfZ\xa6p\xf0\xed\xf2\"\xc9s\x92U\xa8\x8f\x8d2r[\xe4\xe9\xa5\xfe\xe5\xf0\x01\x12\xb67G/\xaf\xfa\xb3\xa9\xc2\xb7e\x0e\xcb\xdd\xd2\xebQ\xcfN\xc9\xcb\xe1\xbe\xf0\xd8\\\xd4\xd0E\xf1&\xd9\xed\xe6\xe8\x8f}\xa6y\x93\xda\"\x07\x07\x9f/\xf6i\xb6\x9a\xd7\xc9\x06\xb7.6\x85Ml\x18\xb0\xaf\xc8N\x8b}\x18\xaf\xa5\x8d\xd52\xae\x19\xfb\xe5\xfa.\xa9\xb5Qm\xd6\x9d\xdf\xe8\xbam\xb1\xdag\x84!\x19|d\xa9J\xec\x85\\\xe0\x19|W\xe9\x03\xc2p\xb8\x97\x17dyY\xed\xb7}&\xf2_\xdf\xf2\x9e\xfb\xf6\xdd_9%'j\x15*\xae\xca\xe6\xd5\xea\xd2k\xd2\x9b\xce\xee\x9f\xa5\xf9\x92\x95\xb9\xa2x\x1eU\xabK\xf8j\xf6\xcd\xd7\xf7\x9boU\xeb\xb3\xa5Ak\x7fj4g\xebJ\x1f`\xeb*\xffA3\\8\x8dl\xc6\xa2h82\x1f5\x7fv\x93/\xd3|\xe3\xad\xe5+\xde\xae/~\x16E\x91\x91$\x1f\x8e\xb4\x1b;\xd4\xeb\x139P\xd1*p\x9c:\x9b\xc8{\xd01\\H\xb6\x88\xe1B1\\(\x86\x0bA\x0c\x17\x8a\xe1B1\\(\x86\x0bq\x88\xe1B1\\\x88q*\x86\x0bu!\x86\x0b\x0d\x00\x1f\x11\x13\xc3\x85t\x9f\xc4p\xa1\x18.\x14\xc3\x85\xfa\x10\xc3\x85b\xb8P\x0c\x17j \x86\x0b\xc5p\xa1\x18.\x14\xc3\x85b\xb8P\x0c\x17\xfaU\x86\x0b\xd9\xee\xb4n9`\xe8\xc7N\x91\x99\xc1\x12\x1f.\xefa\xf0\x82\xe3M\\\x8fm\xa3\x89\xcc\xf1_\x1e\x83h\x1c_\x14fW\x0e\xee\x9dW\xc0\xbe\xdfjx\xa1\x15\xfb\x06\xab\xf6mU\x95\x11\xfaWV]\xef\xa8\x0e_J\xd5\xb00\xf8*U}\x93\xd4\xda\xac_\xa1\xc4\xf0r\xa8\xc7\n\x1bV\"r\xae\x8e\x9eP\xb6\x16g\xd2R\x02\xf6\xe8#}\x11&\x8e Q\xecj\x10\x8d\xa6/\xb6d\xd1\xc5\x16\xfdc-\xaa\xa4-\xa6d~;\xd2:\xd3J\xcf\xee\x17\x1e5o8b\xd1\x1b\x9f\x06\xc4\"\xe8\xd3\xe7|\xe4P\xa9\xf3o\x7f\x9e\x10K\x83\xe9\x11Al\xfb\xe1S\x7f\xd8\x96\x96\x07\xf9\xb0(\x0c\xcf\xe6a\x9b\xdb\x1f\xb7\xdb\x19\x9f\xaf\xd3\xe27\xcb\xfa>\"\xa9\x95\x8b2\xa5\xb6\xa2\x10\xcc\xd0/\xd3s$H\xae\xe9\xe1\xa6{\x99\x9a\xc0\x0b\xb5FO\xe7\xb7\x93\x1c\xce\x1b\xc5|\xc4o\xb5\xe4A\xec\x13\x1dz\xf5\xe9\x08\x16\xfbZ\xfa3\xce^\xfd\xf9\x08\xae\x89,\x01\xc4o\xb0\xcd}u\xbb\xa2\xbaiA\xea\x9a\x94\xf0\xf1\xbf5aM\xee\x07\xf3ZJ\x05\x0b\xdaw\xf1\x1c\x96\x06\x8f\x0dC[\x1aj|\x9dv\xfa\x8c\xb1t\x86\x10Kks5ZN\x89\x91\xb3\xb5V\xe2\xe1|\xa2\xe0\x1c\xdc:-\x8bb\xfd~\x87f\x17\xfb\xc4F\xb1r\x1e\xc1\xee6*i}[\xe97\x95\x18O\xf7\xd8E\x87\xc0O]\xcd\x0d\xea2\xc9\x96\xfb,a\x95,\xdf\x92\xf22#P\x16E\xcdCU:\x1a`Y\xec[\xff\xc0\x82tn\xd3\xe4u>\xf7\x8aR<9\xa9*\xb2\xa4\xbf1\x0b\xaa\xeb\x03\xcc\xa9\x15\xb3(\xf6\x9d\x01\xe5\xc5\xaa\xf1\xfbQ \xd5^#\xf1;\xe7\xf3\xee\xeeL`\xb5\xe7VVs\xb3&\xc7\xc9N\xf3\xfcCy8h\x03u\xdaM\x84\xd87\x02c\x85^\n\xc5\xee\xc0aj\xeaZ\xeb\xa2\xd0X\x18\xc6\xf84\xabg`x\x16VVb(\x1e\xdb=\xed\xf8\x15: X]\xa7\x1c\x82W\xab\x82\xa5\xbbr\x955\xcbA]\xb9\x1c&\\\xbf\x1c\xd4Ul:\xee\xca\xe5K\xfb\x13\x0c\xdb\xd1?5h\x17\xdc\xff\x90\xa5\x15\x8b\xb9\x92\xdf\xcf\x82\xf6]e \\G\xb1c\xdf5gk\xf4\xc6\xc3\x18\x1d\x83\x90M\xed\xde\xd3\xef\xbcZ\x1b\xa0i\xdc\x05\xf6\xa0\x84 \xc31\xcd\x81\x98\xd3\x85`N\x16|i\x0c\xbb\xd4\xdd?!\x03.\xa7\n\xb5\xb4\x07Y\x06\x85W\x06\x07V\xb2\xf1\xf6\xe3\x01\x8d!\x95\xc1\xc1\x94\xdc\x97\xdf\xc3f\x08\xa3\x1c\x13@\xc9\x82%\xfb\xa3\xd1\xdc\xf1\x85\x04M\x9a\x03$G\x86F\xa2\x82\"\xf1\x01\x90#B\x1fG\x04=j\x04\xc6\x84\xa1\x8d\xd3\x065N\x16\xce\xe8\x0ed\x9c,\x84\xd1\x14\xbc8&lQ\x1b\xa2Xc\x82\x13C\xc3\x12\x8d!\x88\x81\xc1\x87\x9a\xb0C\xa3\xa2\xc4]\xc0\x0c4h`xa\x1bJ\xa8\xe3\xef\xef\xdc}\x8f\x0b&\xe4\xc1\x83\x1dt\xc30\xc2 \x02\x08\xc7\x85\x0e\xf6Vy_\x19\x8e\x0c\x17\x14\x8c\xeeb\x1c\x13\x18h\x8dz3\x04\x03:\xc3\x00\x87\x11A\xf8\xd0\xbfa\xdb\x7f\xe9\xc6\x1a\x14\xe8\x87\x19\xac+\xb8\xcf<6g@\x9fG(\x9f\x1a\xf502|\xcf\x1a\xb8g\x0e\xd9\xb3\x05\xebi\xb9\x80\x0d\xd0s\x85\xe6\xf5\x83\xf2F\x84\xe3!\x02\xf1\xfcC\xf04\x01o\xae\xb0\xbb\x89\x02\xee4=++eTx]?\x9cnL \x9d&pnT\xc8\\?Dn\xca\xe08cX\\?V\xa8\x1f\n7M\x10\xdcd\xe1o\xd3\x06\xbe\xe1B\xde\x9c\xc1n\xe2\x94\xec\ns\x13\x9fY\x03\xdc\x06\x91`\xc3\xde\xb0\xa1J\xf6p6d \x1b\"\x84M!y\xca\xb0\xb5Q\x01k\xc3\x00\xb5\xe9B\xd3\xa6\x0bJ\x0b\x9f]g \x9a+\x04M\x8ao}\xd8\x99\xd6\x12\xd7\xddj[\x82\xcc\x908\xc6\x84\x94\xb9\x1c\x84\xed}\x0e\xdaE\xa8\xd6\xe4\xd0\x8caX\xb8C\xf3\x11\xe6~KW\xa8C\xf3\x99\xae@\x87\x0e\x9b\xa60\x87\x11\x9bZ\x90\xa3>\xc0\x0d\x84\xae\xf8\x86\xf1x\xe7,\xbaa(\xb8\x81F\xa8+\xb4\xa1)\xb2\xe1\xc6\xa7)\xae\xe1s\xa5\x08\x8e\xa2\x1aZ\x020\xc54\xa6,\xa4\xd1\xd9H\xcb\xf2fW\x17\xb3\xe6\xe5F\xf4.\x1a\xbc\x98i\x15\x06\x9dc\xbd\xe6uK\\S\x19\n\xe3\xf1\xc8\xa4\xe6 \xc9\xce\xd8wOw\xb3W\xdcj\x94\xdcB\x0f\xdf\\bK\xbb\x8d\x0cu\xba\xfae\xb5\x8c\xcb\xd3\x18kw\x17\x02\xf6\x8c%\xb34h\xb4\xa5\xb24\xdf\x0dJd\x05\x8a\xe0aI,\xdcb\xd3\x94\xc1\xd24\xec\x95\xbf\xf2\x98z}\xc9+\x03\xdf\x8d\xa5\xaez\xdf\xdb\xd7\xf6\xfb\x96X\xc4\x02\x1f\x12\xa8!NK\x98\x9d\xa8S\xb1q\xfe\xda\x9d<\x04=\xca^\xb1\xce\xa1\xb26{\xfb\x03\xdfP\xd9\x13\xb8f\x9d\x91\xd6\xecx\xfc\xa2\xed\x1c1\xc2~8\xa3\xc7j\xd2\xd7nr\x84\x05\x1b\xfb\x00k\xb8\xe4].\xde\xe7r\x17\x8f\x0d\x1f\xee\xa1c\xb7u\xfa\x10b\x980\x8c\x18l\xa1\xc4\x80\x08'\x06mH1\x98\xd9\xac{P\xd6\xa9\x13\xfa\x93\xa1\x86\x19\xa3P\xe8\x1eD4\x84\x1cC\xd8\xea\x9d26\x17\xf1N\xac\x91B\xb0R\xc9\x9a\x9a\xdf\x86\xe5Xm\xef\xc2\x9a\xe6D\xc2\xa1\x9e\xceu\xbe\x01k|\xff\xd5\x1c\xb6\x0c\x98\x95c\n\x9c\xb6\x840\x83>\x8c\x19\x02\xba3\x86\x13\x87 \x1b\x17\xde\x0c\xce\x10g\x08\xa0\xc9\x14\xee\x1c\x82k\x18\xfa\x1c\x82\xc5\x12\x06\x1d\x82\xce\x10\x12\x1d\x82\xca\x1e\x1e\x0d\xd6\x10i\xf0\xee\xcf3v\xb8\x1f0\x0c\x86\x08S\x94\xd9Q\x7f\xd6\x92\xeeU\xf4S+i\x02o\x83\xcf?\x8b\x8ac\xd2i\xc8\xb4&\xbf7\xe0:\xf2?8\x03fW\xed\xa8.\x9c\xa5\x9b\x9c\xac\xdeV\x9b\xf3&\x12\xb6\x16Q\x19\x15\xfb\xa9{\x99k.,\xc7\xa1Q\x11\x86j5\x0d\x18\xf9\xff\x8c=\xce\xad/\x1ahm\xcd\xf9\xf9\x0cN\x992J23\n\xddi@\x05\xe4\xe2tY\xa5\x1c\xcab\x9f\xaf\xdc\xbd\xd9\xcc_\x0e\x9d\xee\xbe~j\xfcN\x7f\xb4P\x01\xb1\xc7\x01\xb9\xcf\xc1j}\xb7\x80d)\xb8,\xf2.8\x8f+* G\x0d\x1e#\xa7`9\xd8\xf4>D\xce\xb3\x04\xdc\xf2\x92\x80\x99\x04\xf0\x9b\x08\xf0\x99\x0c\xc4aI\xfb\xf9\xf0\xe8\xd4\xfflK\xaa:\xd9j\x9c*\x9d\x8fp\x83\xb2\x9d\xcbUhN\x1b\xe6\x9a\xfc-x\xf6nee\xdb\xb1\xe1 \x81\x16\xb0\xeb '7\xa8\xb0O\xa8a\xed\xeer\x8a\x91bU\x14\xd5\x06\xed\x1d:\xd5N\xf4\x88X\xd4\xe4H\x84\xf7nS\x1en\xcc\xff\x97\x197Fd\xec\x98\xd9\x1e$)\x9df=\xd5\xd1e\xba\x9f\x99\x15\xb5\x88VT\xb4\xa2 ZQZ@.N\x9c\x9a\x8bV\x14\xe0Y\n8\x19\xcc!ZQ\x1d\xc0L\x02\xf8M\x04\xf8LF\xb4\xa2\xb0\xbdG+\xcaCE\xddY+\x8am\xfb\xb9\xad\x92\\\xf3\xa5\x9bi\xee\xad\xde\xae\x8b\xc3\xf7\xe5\xdcr\x1e\xbd\xd8\xb6\x1af\x0d\xbc\x92\xceE\xba\x10^\x0b\xd7b\xebf\x95\xceF\xeeSo\x98\xa4E%\xcc\x99\xfa\xba\xa0\xed\xd7Y\xbad\x89\xebt\xc5h\xe68\xa3F\xc2|\x99\xa5$\xaf\xe7I]'\xcb\xcbC\xba6;\x14\xcd\x0d\x17\xfb\x1c\x10Z\xcc\xd5\x174\xcc@hND\x7f\x80\xec\x134\xb1\x15z@v\n\x1e\x1d\x83\xfd1\xb1>`b6\x0c-\xf1\xb4\x83'\xfd`\x8e\xfa\xd0\x03b\xa3\xf6An\\m\x84\x88\x1e\xb4q#z\xb8\x0d\x820b\xa5\x0f\xf8x\x14\x14:c\xc9;[\x94\x8a\x1e|bWP\x08\xfb\xf1-\xf6\x88\x16=`\xe2\\\xf4\xa0\x8f~\xd1\x83\xd7bq\x9f\xea$x\xa1\xc5\xe8\xcb.\x0ccm\x0c\xdf\x85\x11\xe1\xb6\\%X\xa2u\xf4p`\xb9\x85=\x9f\x80?s\x00gr\xaa\xe0yx\x94\xe0\xc9%\x08\xe0\x14\xf8\x1c*%`\x0f\x03}\xf0[\xdd\x12|&\x13\xc2&\x14B&\xd5\xfb\x10\xdak\xe6:\x8cJ\xb0\xc7H\xe9\xc1\x93\x07\x9ec\xc7EY\xe9\xc1\x10{\xa5\x87C\x0e\xc3\x1a)\xa5\x87C\x92\x03\xc6\xea\xcaz\xc0\x86\x89\xa1\x90\xf5C\xc9\xf4\xe0\x0e0\xd3\xc3!\xd9f\x0bQ\xd3\xc3!\xa9\xd1\x07\xb9\xe9\xe1\x90t8\xc2\xe4\xf4pH\x82,\x81vz8$1\xeeP==\xd8\x03\xf8\xf4p\xb8qL\x10\x02\xa8\x07\xdd#\xe3C\xf0\xb0M|l\x92;bY;\xefQ$\xf8\x9aB8\xff\xa8\x84hR\x1b!\x9a\xd4\x0c\xfc\x96\xb5\x04\x9f\xc9\x84\xb0 \x85\x90I\xbd-\x93\xba\xb9\x7f@M)\x1f\xfd0\xaeU\x0f\xdahW=x/F\xff\xa5(%\xc8|\x9d%\x1bl\xa3\xc0 w\xc7O\xa8\xf0\x08^\xbcy\xff\xf2\xcf\xf3\x93W\xf3\xef\xdf\x1c\xff\x80\x885\xe8C\x1f\xc3\xf1\x8b\xb3\xd7\xef\xec\xe1\x11*\xf4\x11 \xe2+T\xe8#xwb\x0b\xb3P\xa1 \xba\x18\xc7\x06\xbfs\x03\x07\xbeQV\xdfg\xc9\x06\xd2|\xc5.=DEHx\x91-\x8b\xcb\x93W\xce\xf8\x0b\x15\x9a-\x05)\xdeu\xe9y\xf7\xa9B\xd0\x1a\x0d\x12I\x88Ka\x15F\x91\x86w\xbbq@]\xa6\xaa0\x8a>4\xebB|\xe3\x00/\x99\xf9y\x96nx|\x10\xd5\xf3\xd2y\xcc\xeegeR\x03\x12]\x9aC\"p\xba\x1c\xc6\xbe\xf4r\xacj\x1aFsG\xc8\xf2;\xa4\xd9}mx\xdf\xbd\x0b\xdc\xec\xaey\xdeJ\"\xf39\xda\xe3\xb6\x99\xfav\x0fU\x04\x11j\xe4\xd47X-\xd3\xd2\xe6Zyx\x0d\x8a\xd4\x9e\xc8\x91p\xc0\x8e\x87\x83\x97\x1c\xf2\xdeH\x9e\x9bhP\xb4\xda\x06^L\x01o\xc6\x80\xae\xfe\x87\x0b\xbc\x19\x04\xfeL\x02}}\x11\x17\xdc\x0ei\xbe\xda\x19[\xe8\x04\x89N_\x0e\xc5\x05\x98H\x13\x15\xbc\x99\xe9{p\xd9\xb9^=\xd4\xc3\x01\xe9\x92\x14\xb9\x08\xf1\xd8\x96>\x1b\xd2CNy2\xc1k\x8d{H(\x0fF\x80'3\xc0_6y2\x05|\x19\x03!R\xe9\xf0D\xf9\xc9#\xac4b\xf57]\xc8@\xd6<\xf6\x90E\xbe\x92\xc8\x93\x81\xf8\xdd\x0e\xa12\xe8`\x14\xe1\x83\x02\x9b\x16xR\xdcdP{\xb5\xc8\xe7v\x87-\xa2GwO\x8b\x9b\x9f\x93\xbcNs2w[\x9cnK\xd3aa\xa2\x84\x14N4\xa1$4\x82C\x1c\x90\xfb\x1c%\x8fQ\x83\x04\xf4@\xc1G\xfa\xa2\x07\x0c\xf8A\x83\x9f\xac=\x0c X\xc9\xea#S\xf9s\x00v|(i\x8a\x17\x13h\xe6\xb87.\x07O\xa99a\xffx \x89\xe8\x14\xd1\x9d\xcbQ\xe4\xd1\xcb\xd8P\xe67T*\xbfd\x01\xc5\xc7,\x9e\xd8\x15\xce\xcc\xfd\x0dZ\\\x9d \xf6\xa4\xae\xc9v\xc7B\x99\xeb\x02\xb6i\x95\x91d\x05 \x0f`\x06\x1e\xc0,\x1d\x15\x9d\xb8\x95\x96!Z\xc9\xa3\x972\x93\x94\xa3\xd2\\\xe4q\x1c\xba\xab\x16\xd3\xe5\\\xacC\xa5\xfcd\xe4\x9f\x04\xd7V\xb9{u\xa8L\x17B&sb\xca\x92&\xce+\x1a\x07S\xcc\xd7.>\x17,\x1eW)\x1e\x97&\xb8\xeb\x11\xef\x8b\x10e\x1e\x9d\xb7\x18\xee\xbb \xd4-\x84c\x1a\x1c+\xd3\xa2\x1a\x90\x88\xcd*\xc1z\x03\x80\xc4\xae![\xd13X\xb7\xbc\xde\xe1n{\xdb\xdd\xc3\x89\xaew\x92w\xb0\xa9\xeer}i\xce\x93W\x9cM\x83\xed:\xdc\xa4\x17A/%\x1b\xe5\xb2\x87\xf2\xd3\xc8_\x94\xdeR\xe4\xed\xd8\xaafFy:\x90\xa3&N\x7f\xdf\x88\xb4A\xcf]\xb1e\x17TV\xd1d\x15F&\xf1\x83\x108\xe3E\xcc\x80'/;\xb6\x10f\xf1\x85\xbe\xf4\xdf\xb3y\xf4\xebFg\xeb\x0c\xed\x1c\x8f\x15;v\xb1\xc5'\xfe\x1dO\xfc\xebl\x14\x9d}2\xb6\xc4\xbf\xd5\x1e\xb1\x0cVo\x87`m\x10\xa4\xfd\x81\xb4=\xdcv\x07B\x04\xb40^\x18\xb4\xe0\xb45,,\xb6\xac&\x83}\x81@\xa6\xb7+\x8c6\x05\x02c\x8f\xbc\x89\xec\x08\xbd\x0d1\x9d\xfd\xe0\xb0\x1d\x1a\xc2\xd1\x02\xdc\xb0\x91\xb4\x0c\xeco\x1e\xf7\xb6qn\x18\xe7V\xb1m\x12\xe4\xf6\x98fcX\xb6\x04\xd6\xe6\xd2l\x00k\xd3\xfe\xa2\xd7,w\\\xd7\x93,\xee\xc1b{\xd5\x14gE\xac3\xa5 \xab\x87F\x18\xeca\xed\xfe5\x9b\xee#\x0b\xaf\x8e*\xba\xca\xcb\xacvi\x1f\x14\\\x1dWlu\x92B\xab\xc3y\xd5U\x18@Ot\xbf$\xa9\x87u\xc6>\xed\xfc\xdb\"\xc7uz\x1c[\xf8\n[\xea\n_\xdc\nY\xce\xca\xab\x80\x95\xfe\xbd| \xbeE\xaal\x85\xa8|KO\xf9\x16\x9b\x8a>R\x88>\xd2>\x1c\xc8Gj\xb01\x9d\xcbMg_:\x8c`'\xce\xde\xd0\xac\x85\x8c\xfc\xd7\xb2\xc1\xf6\xf5\xa3\xca\xac7C\xca\x0c\xf5\xcb \xfdN\xe9\xbd+w\x06\xa5\x84\xfa%\x18\xa3\xd6h j\x8d\x06\x9c\x8b;j\x8d\x0eD\xad\x11\xb5\xc6\xaf[k\xd8b'\xb4C\xd2-Mc!94\x86q\xe7z=7\xc3\xca\xbcuJ\xba <\xda\xc2n\x83#\x9f\xf7)\xcf\xf9\xc4\x85\x87\x02\xd7=b\x11 ]\x87v\x00\xb8D\x8a\xe9V\x1ak\x13\xe8\xbf5\x17N\xc6\xdb\x06\xa6\xaf\x0dE\x92\xbdl\x04p\xda 0\xb1\xad\x00\x01\xf6\x82\xa9\x8d\xab\xac\xb1)\xd6\x13\xa5fu*\xcb\x90fo\xd7\x83&\xf9\x0b\xd6\x84\xf9\x11\x1a\xfbPJ\x15ag\x80\x8brpR\x0fv\x9b\x03\x9c\xfc\x96\xe0\xb2=\xc0\x99X\xeed\x17\xb8Y\x86\xb0E\xc0f\x8f\x80\xd9&\x01\x17\x89\xf6\x1b\x13\xa7}\x02X\xfc\x9a\xa1;K\xf5\x86\xef\x19K\xf4H\x18\xb5\xf6\xf8\xc4`;\xc6\xc4\x11mi]mA]\xddc\x04Q\x1bFm\x18\xb5\xa1\x02Q\x1bFm(~\xc2,\xe5\xa8\x0d\xe1\x8b\xd4\x86\xae\xec\x08\xe3pMK\xdfZ6\xde\x1b\xdbt\x8e#\xf3\x0cL\xe5 \x00\x937\x00\xd0\xc5\xdd\xb5rM/\xcb\x1c\xe5\xdb\x03,\x1aG\x81\xf61\xda\xe2\xb0\xa2\xdcYd\xdd\xb7\xb4\xba\x93&@\xd1\x05\xb8\xe2\xe9(\x15\x00\x9d\xc5\xed\xacK\xee,\x8f>u\x97\x98d$\x0e\xf8\xb2\xe7\xd0\x964\xb7\x0d5\xa8\xd8\xb9O\x89\xf3\xb6|\xb9\x05\xa1oas\xdfr\xe6\xee\"\xe6\x88)u\x95UD\xa0\xc0\x99<\xf6\x92\xe4^\xdd\xb8\xea !\x8b\x8eO\xb8\xa3\xdde\xf3P\x03\x04\x8c]\xc7\x01e\x12K@\x8d\x14\xd0\xa3\x05\xb7\xa9\xdc|\x862\x99%`\xd6\x91\x047\xcb\xc1\x87\xed\x80g=\xd2\xb4\xee}l\xaf@\x88/\xe5\x8d\x1a\x11j$\xfe\xc5\xba\x11%\xba\xa7\"\x0f]Mz\xaa\x0e\xfb\xfcp\xd6\xcf\xb6\xd6\xc6\xf6\xab\x88=\xd5\x18\xb05\xaf\xa7\xea\xcf]\xd5z\xaa\x9e<\xeaVO\xd5%\xb22\xf5T\xdd\x81W\xed\xe9\x1d\xba\xe2\xf44\xf4M\\S\xdaVI\xda\xa9\xac\xdcJ\xea\xd6\x8c\x1aG5h\x9c\xf6\xb39\x1d$Dk\xa6\x05\xd4H\x01=Z\x88\xd6\xcc\xd4\xd6\x0c\xa6\x8a2\x1f\x8b\xbd\xf2\xa3\xb3\xe6#r)`\x17\x823\xe1\xbe\x0b\x1e\x93\x81\xab\x7f\xec\xce\xec\xc1\xb5C\xd6:vf\x01\xe1\x9a\xb9\xeb\x1a#3\x86\xf4\x80\xad\x9d3]\xe5bgM\x80. \\\xd9*x\xac\x1a\x8f\x0d\x8c\xacC\x1c\xd0\xb9\xeb\xac\xcd\x01]i8\x80\x02\xc7\xf0\xf1n&|\x05a\x96\x85\x85\xc0\xa4\xf7\xcc\xe0(\nOO4 DT\x06v\xd4\x03\xb6JS\xbb\x0cu\xd5_sI{\xab\xa4GHy\x8c\x84G\xecP\xe4\xf2D-MD\xbd5\xc4\xc0\x0098\xc0WZC\x0e\x12\xb0\x03\x05\x9f\x1ak\xd3w\x8e\xd3\x10\xd3\xd6VCTV\xc3\x14\x17\x03]\xb7\xf7\xcdw\xae\xceL\x1dY\xea\xbb\x1aq\x9ap\xb9+\xb8\x9a,\x87)\x0b\xad\x1d\xa8\xb6\x97E\x9e8$\x89K\x868\xa4\x87sj\x11[\xd7)+\xc6w\"\x1d\x0f\xbe\x9b\xdf\xb6\xc5]\xeb\x1e9\x9f\xfa\xcd\x85\xda\xca\x81=\xb8\xb6\xad\xf7\xd6\xba\x8d0\x99\xc0b\xa2\xfa\xc4#l Qc\"\xcd\x9b\xb4\xc2\x17\xd12\x05\xda\xa0\nd\x0c6\xaei\xc3:Sv\xacXm\x98\xc1\x90\xc4\xc3a\xa4|a\xcd\xc3\xf7\xbd\xdd\xdf\xe5\x13\xdalja\x0ep\xb6\xb4px\xb8<\x82\x9d9x\x87<\xcbf\x18\xff\x80\x7f\xf8\xb3\x11\x91#,\x9aCHp\xb4\xb9\xa5+D\x9a\xc3-\x94\x88\xb7\xde\xd0\xf0\x1e\xec\xfe|\xf7\xed\x8c\xebn\x06qjp\xedG@\\\x11 \xd8\x05n\x9d\xcc\xc1\xe3>\x061:@\x8e\x10\x00y\x13\x83\x997 \xee%\"\xc1\xc5`\xc03\x19\xb0\x8c\xf6\xba}A\xdc\xbd8]\xc1\x88\x01\xe0\\\xbfh\xcf\xb7G\x8fFV9\xc3\xba9`\xd6\x85{?;\x9d\xd9\xe3G\x84Q\x01S\x05\x80s\xb0\x87\x81s\xd0\x06\x83\x8b\xe6\x9a\x04)\x0e\xd1\xd2\x88\x96F\x03\xd1\xd28\xa8d\x8a\x96F\xb444\x80bt\xb44\x00\xc3\xaahi\xfc\xc2\x96\x86\xcb\x0f&\xbe\xb23\xc6\xbe\x15\xad\xe9h\x1cF\xe1?\xe4\x9b\x0e\xae\xf9\x1c\x95\xb86\x1c\xc9uaN_\xe3\x80Kb\x13\xe8\xc2\xdc\\\x8e\xb46\x0e#\xedPG\xa2\x1b\x07\x84\x06r\xf5\x03\x8e\xd47\x0e\x88\x8e\x00\xd9\x19`\x92\xe18\xf8\xa6\xc4\x89V8Z\xc1\x83^\xc0%\xc9q@\xc8\xc8.\xc8\x0d\xe6L\x98\xe3\xe0L\x9b\xe3pH\"\\[\xbe\x0f\xf8t:'\xaa6\xdd.0\xa9\x8e\x83oj\x9d\x13!\x1f\x81o\x82\x1d\x07\xdf4;\x0e\xeed;\x0e\xe8\x85\xe0\x8aQ\xe7\x80F\xe7\xd2I]\xb0\xa7\xe2\x89o\xfc;\xc6\x85\n\"\x93\xf38\x1cH\xb6`\xeco\xf0c\x02\xb8M1\x15<\x0e>\x12<\xb8\x01\x9e\x1c\x01\xec\x81H\x02\xc6\x00\xee\x03~\x85J\xc0N\x14\xf8O\x16\xf8N\x98\xd7\x01\xaa\xd7\xc4\x1e.\xcf\x01\x9f\x02\xc8\xc1c\xbc\x1e\xe3\xf4O\n\xe4\x80H\x0d\xe4p\x08\xb2\xd1\x89v\x1c\x0eA\xc2\xb4\xe9\x83\x1c\xfc\x92\x089\x1cbl\xd8\xb4B\x0e\x87\xa0\xc0\x9dh\xc8\xe1\x10}{\xa4\x1er8\x04\x11\xc8dD\x0e\x87 \xc0/=\x91\x03>I\x91\xc3\xf4tO\x9c\xb6\xc8\xc1\x96\xbc\xc8\x01\xa9\xa8\xb1\n\xfa\x174\x15\x1d \x8e\x1c|l\x01\xb7SLB\xb4\x11\xa3\x8d\xe8\xfa\x1a<&\n\xfc'\x0b|'\xec\xd06\"&\xb1\x92\x03\x1f\xa9=\xbd\x92\x833\xc9\x92\x83\xd7\xc2\xf2[V^i\x97\x1c\xbc'\x12\x97\x82\xc9!4\x11\x93C`:&\x87\xc0\xa4L\x0e\xfe\xa9\x99\x1cF%hr\xc0\x04\xf9wa\xaadM\x0e^)\x9b\x1c\xd0\x97J*x\xaf;o\xf1\x81L\xe5\xe4\x10L\x0e\xceW\xc3\xc1yK\xa5B0M(\x16\xf9:C\xa7L\xfa\xec\xe2\xb3y\x0b}h\xf4J\x03\xb5b\xd2\xbe`iI\x06\xe5\xe0H \xe5\x80\x90\xfd\x18\x89\xefJ\x12\xe5\x80\xd3\\\x08\xad\x85\xa0\x9a\x03\x86v\x0ehY\xe1\xb5 <6\x803EM\x02z\xf0\xe0\xc5\x00p'\x9f\xa8\xe0\xc5\x08\xf0c\x06`\xd2TT8,9>Z\xd0'\xeb\x05\x81\xce\x95\x17\xa3\x82\xebz]\x05/\xa6\xf9\x18\xf0\xa8|\x1a\x15\x0e@\x8b=\x81\x95\x03r;a7\x12R\x8ex\x0c\x16\xbdN\x91\x12\x049`\xf0\x184\xf8\xc9\x0e\x8f\xc1\x83\x0f\x03\xc0Wj\x1c\x8e\x10\xbc\xbc\x98TZ\xf8\xc8\n\x1fI\xe1\xc1(\xdc\xce\x84\x10\x1919\x15\xb8\x88\xa4\xe6k\\\xf7\xf6\xae-)\xb6\x1c\x1c\xbd\xd8\xb1\xbb\x93n9\xd8-1\x8b\x05\xe6\x14\x1en\x91\xe1\x94\x90(>#\xf6\xa2S\x1e:\x07\x03\xa8\x01\x01V\xfa\xa1\x06\x06\xb8\xc1\x01^\xd6M\xdb-F\xb2\xf9\xc8\xb4\x98\xf4o\xeb\x13'\xa1F\xc9\x8c_2\xa610\xcbx\x80\xa7\x13\x99\x1a\x9ck\xfcc\xc7\x13?\x90\x0cC)0\x88\xbcs\xc5\xd8i\xa5\x8d^\xba\x18+\xfck&BN\xc1 \xe0l\x10\xdf\xe6\x8b\xc2\x8a\xcc\x15/6\x8c\x0c\xd3\xb0\xb0\xaf\xc9\xb5\\\xd6\xed@5V\xcb\xda\xac\xbf\xbd\x0c\x91V\x1e+lx\xb5\xe2\\\x1d=\xad`\xbd\xc12jV\xb365\xdc?qL\x88W]\x06\x92M\x7f{d\x15hF\x05h\xbd\xfb\xd1\xde\xf2\x98c~\xac3\xad\xf4\xec\x8e\xd8\xd1\xc4\xe6`\xd1\x1b\xa3O\xb0\x08\xc6\xc5\xc5\xd8#`\xfe\xff\xec\xfdks\x1cG\x92&\n\x7f\xef_\xe1\xcb\xf7\xb5!9\x03\x15W\x9a\x99cv\xb8\xab\xb6\x85HJB\x8fDbIP\xbdcmm@\xa2*\x00\xe40+\xb3\x94\x99\x05\x12\xb3\xd3\xff\xfdX\xdc2\xe3\xe2q\xcb\x88B\xabg*\xbfHDez\xdc<<<<\xfcy\"\xb6\x0e\xae\xfc\x95\xd8\xef\xed\xec\x93\xd8/=\xb9#\xb1\"\x1c\x99\x1f\xb1\x9f\xfb\xf36\xdc\x19\x1aq\xf23\xf3+\xac\xd5\x95\xad\xf4\xdf\xcd\xebZ\xc4\n\xebH\xdcO\xb1reL\x933\xc9>\xb4\xe4O\xef\xf96\x1a\xfe\x0d\x86'Y>\xe8^:]\x03\xf98S\xe0sE\x87\xfc\xbeL\x17\x02\x91\x17\x97\xbc^\xca\xa5\x90O0A=\xe4b\xc8\xc7\x9d\x84\xee\x19\x8a\xe2\xb7\x11\xe2\xe9\xe3Q\xe2\\;\x80@Rx\xc6\xccpg\x87Dk\xafc\xa3\x1b\x95\x9c\xe3\xad9\x04k\x0fn\x97g\xfa\xd9\xeb\xfa\xc8'f\xcf\xeaO\xa4 v\x17\x84\xbb,2=\xc6\x9b\x10\x13N\x93\x8e\xd2D\xb4\x86aWJ>\x9et\xe7\xa5\xc5\x07\x13}\x97\n\xces\xc1\xe4\x13\x97\x8c\xbc\xb4\x8e\xa1\x14\xe3\xa5r\xdd\x89\xc3K%F\xa4\x03/\x15\x1dH\xf2]*6.u7\x9c\xac\xbb\xac\xfcL\x17Q>X\xd2\xed\x02'\xad\xf8bx\xbc\x9a\xf7\xb8\n\x9a\xcf#\xac\x82\xbeDP^G\xfc\xd8\xc6yh\x13\x18\xa2\xd0\x00E\xa5pFt^\x88\xcf'-C11\x1531\xf92>\xddrQ\x82\xa56\xfe\xc1L\xc9\xb8\x1c\xc8\xe8\xac\xc7\x88\xa1\x8a\xd0\xf2\xc7b\x08 f(&\x94\xe4h\x8e\xb6\x8c%\xa5\x14\xa2 \x83\xfe\xadoRR \x92\xf8g\x88C\xd3\x00\x1d\x89\x7f\xa8\x1d\xc0g\xff\x91\xb1Y\xfd2` \x8f\x8c\xcd\xff\xe9\x18\x9b\xf1\xbc\xb1\x05Nia\x8f\xdb\xa3\xc6\x19.\xa4W\x81\x83\x9a\x15\xd0\xab\x80\xea\xe6\x89/\xaf\xb4~\x95\x8d\x1a5L]#\x94u\x91\xecPz\x80S\xa8)\x10\x8f\xd7c'\xf3\xbc\x00K\xddl\x15\xf3p\xfb$,E\x1e\xf6\x9e\x05\xf3\xf1\x00\xb7\xdd{\x19xb\x0f\x08 T\x07\x08\xd6\x03\xc2\xcc:\xc1\xc9\x06\x8afx l\xbc\xdc9\xa5\x8a \x1f\"\xc0\xf2\x83\x04_\x0fF\x1e&@\xce\x81\x82C^4\xebM\xec\xc1\x02\x04\x19n\x02\xc3\xe5\x83(\x07G:\xb4-w\xf3\xd5D\x8b\xf6\xed\x1f\"\x18i\n\xcc\xba\xc7\x88+D\x86c \xa6E\x10\xd5*\x80`h\x06\xa6\xd2B\xe1\x19\x88\xd0\x05\xf9\x84Q\xbfQ]\nq\xdd\x1a\x19\xb2Q^t\xa3y\xc3\x07\x18\x10S\xfb`\xad\xe3\x0f3 \xcc\xdf\x92[\x9d\xe0\xc1\x01\x14(\xa4\xccA\x07D\x1fv@\x81:\x87\x0e>\xa0@\x19~\xf6\x94\\\xe9\x11\x07\"P\xa0\x98\x08\x06\x94\xdc\"\xe2\x0eJ \xea\xb0\x04\xb2\xebS\xe8\xe0\x04<\x8c%\x19\x0e\xedA\x17|\x0f\xebHx%Q\x84\xa3\x0c#\xc7\x95\x1ey\xc2\xad\x82\xe3J\x8f\xbf\xe8^\xe9Cl\x1d\xbc\xde\x7f\xddKq\xa3\x0en \xbe\x93\xc3<\x1b\xa9\x878\xd87\x11\x9c\x1a\x89\x879\xd8'~\xfe\x8cE\x87:\x00q\xd8\x98h\x86\x0c\xed\xdc'\xa4\xfc\xce\xdf\xa3\xcf\x85 ^\x13\"'[\x04\xdfEb\x81an\x8b\xe0y\x11\xa4\x97\xeaif\xe1\xb3#K\xa4#ur\xf9\x19\x12f\xcb|t\x12\x1e\x12 \xa7\x99\xf2$\xd2z@\x89>\x93y\xa8\xb3\xedG8*\x0d\x80\x10\x0b\xd9\xfa\x08\xf8aDc \xa6A\x10\x0e\xea\xf3\xa7\\\x81a\xb3\x9a|\x02\xe0\x94\x14\x80\x1b\x86\x8e\xb4 \xae\xe1a\x0f)\xe2\xc4\x80?\x99\xa5\xe1G]\x10RM\xbfR\x1ep\xef\x14\x98Q\xc1\xf9\x14\x9eM\xc1\xb9\x14l\x00\x84\x1b\x01q\xb3\xa8LQ\xa1\xf9Sj\xf6x\xe7Nx\xe6\x04\x1b\x1b\x9a5\x91s&\xab\x9c\xd0\x89\x1b\x84\n\xc0\x84;H\x01P9\xd8\xf7~\xd8?\xb6\xb2\xe6\xde~\xeb\x98\xe1\x9e\x96;\xb5\xd41\x9f=3\xd97\x87=\xb370\xee\xdei\xe4\x9d\xab\xcb\x05\x97>\xc0\xf6\xe9e\xc4\xd8\xd8\n\x1f\x9cR\x89R}\xd3'Z\xdd\x91\xad\x85\xf7[}\xdb\x80;\xd8\x0b\x91\xe8\xf6}\xd7\x8b\xf1\xe7\xe7U?~\x90a \xde2k\x0e\xd8\x9ao\x04n\xf8\x17f\x90\x06\xeb\xc4%\xa0CG\x00\xc5j\n\xbfu0\xb1%\x198?\xf4\x0c>\xe6\xec\xddid\xdc&\xc6q\xc6\x1ee\xf3\xd1\xc3n\xf4,}\xa9\xb8P\xf6\xe1\xa2\xb3\xf2\xa9\xf7\x0cq\x813\xf2\xc5g\xe3\x08\xd8\xce{&\x1es\x16\x8e\x9f\x81;\xba\x19\x0b\x81;G\xc4e<\xed\xb3\xed\xa0\x08,\xbe\xe19\xc3^\xa0\xbde\xf1'\xc1H\xb5\xd7\x1d\xf7\xbb\xe2\x9e\x884n\xe4\xd4'\xe4)\x1e\xea\xa4)\x18avF\x96\xfdg\xc7A\xcd\xb1j\x13wF\xec8\x1bN-\xce{\x9e\x9a*,\xff\xac7|\xc6\x9bZ'\xdfYn\xaa,\xfc\xcc6UJ\xe0l6U\x9c\xe7\x0c6UT\xf8\xac\xd5\x7f\xc6\x9aV^\x81\xb3T\xf3\x0c5\xc1\xf1(\xb2P g\xa2n\x0b\xa7\x08\xd1\xce@\x8f+\x84\xf6\xd3\x7f\xc2\x15\xc2u\xe6\xc8\xebsXPF\xf0,1\xd0)\xee\xb3\xc3\x943\xc3\x84\xb3\xc2\x843\xc2\xb8\xb3\xc1\xe43\xc1\xd2 \xaf\xa8\x83\xbc\xc00\x044\xf3p\xbf\xbb\x1b\x18\xc6\xea\x13\xc7\xf2\x7f\"\xad\x1ea\xa1\xba6\x07U$\xa8\xa3\xea\xc9\xe4*\xb6P\xdd\x8c\x84N\x16\xde\x8c\xbbj\x80n\xbd\xde\xf7=qE_\x84\x82\xe8;r\xf17\xbf3\xb5\xabn\x85\x81sz\xf1\xf2\x05\xc3\x93\x9f\xfe,:ON\xa9En\x1a\x03_:\xb29\x11\x95sf\x10\xf9\xf4g.EZ\x01\xfa\xbfc\x07\xd7\xb49\xc3\xc0\x87\xf6\xbc\xba%\xef\xc9\xaf{2\x8c+\xfe\xbb!\x84o\xf3\xe8\xe7T\x1c\xed\x08\xba\x8b\x19F 77\xf5\xba&\xed\xd8<\xac\xe0\x8c*V\xd3P\xd9d\xbb\x1b\x1f\xa06\xc9\xdb\xc7;\xd2\x136\xf0m\x07[\xaax\x02\x98\xa9*\x1f\x92\xae\x15\xec\x14d\x16\xba\xb8|x\x1c\x87\xf6\x06\xfb\x9fv\xbf\xbd&=U`Q\x15%\xb1\xd4\xaa\xbf\xdaQk\xaa\xf8\x97L\x88\xb96}\xae\x06\x18\xc8x\x02\xf58P=\xdc3\x1b\xbco\xb92m\xa0\xa3\xfd\xf0\xb9\x1e| M^+\xba\xc0\xa4\x0b v|\xfa\xf4t\xbd\xdeo\xd9\xd4\xdf\xbc2\xb6\x16\x11\x86\x1c\xdb\x8d\x1c\xc2\x96\x1f\x83\x9d\xcb\x83\x9dx\x03\xfd\n\xa0\xadC\xf3\x0b\xcax\x0b1\xa6\x13\xf4\x89\xecF\xa8hO\xf5\xfb\xb6\xa5K\x06\x9bJ\xa4?\x81u\xd5R\xeb\xa1\xac\n#T\xed\x03Ky\x8cVW\xdby\x89\xd6\xd5G9\x19;*\xea\xc1\x14u\xa9\x17?\xf9\xee\\^\x11\xb7=JWg\xb7.ZG\xdd>+\xaa\x14\xe8\ni{\xa8\xd6\xb7\x81\x9e^\xe4\x8f&z\xa2\x85|\xd0|\xefS\x0c\xa5D\xb2\xea;\x9d\xd3\xa6\x91\x0cX\xc9\x9b\x1c\xa2]\x15\x05\x0728\xf4\xf5\xcb}o\xa5\xc0g\x04\x11N\xe1\xe3\xfb\x9f^\xf4d\xe8\xf6\xfd\x9a@[m\x05\xbew\xdf\xd6\xbf\xeeI\xf3\x00\xb4ac}S\x0b\xdfy\x14\x88\n\x9b\xb0\x08` }]5\xf5\xbf\x13d\x93\xcf\xe6\xfe\xbak\xe0z\x7fsCz \xc5Xq\xe5\xe0u\x87\xed~\x98@\xc7t\xc9hH5\x8c\xb6\xac\xae%\xf0\xe4\xc5\x13X\xdfUT\xffI\xbfb\xf6\xab\xa9\x86\x11\x06rK\xad\x94\x0c\xd1\x7f|\xff\xd3\xd3\x01v\xd5x\xc7\x84[\xa2&\x9d\xb7K\xa1\x9f\xdf\xec\x9b\xe6\x01~\xddW\x0d\xed\x81\x0d\xef\x1f!\x9a\xf5\xc4\xb3j\x80\xba\xb5?\xbe\xa2E\xbe\xb8\xed\xba\xdb\x86\xacX\xdb\xaf\xf77\xab\xd7\xfb\x9e\xe9\xee\xd5s^c&n\xb8\xeb\xf6\xcd\x86.\x97\xb4\xd1\x96\xa4u\xd5vm\xbd\xae\x1af\x01\xec\x92\x9e\x91\xd5\xed\xea\x84v\x15\xb3\x87OVO\xd8T\xebF\xba\x98\x93\xddH6\xcf\xb1\xc8\xceY\x0b;f<\xd6\xe4\x04FRm\x07\xd8\x0f\xfb\x8a6\x97\xe7\xd9\xefj\xea8\xb7\xd4L\xde\x11\xb8\xae\xdb\xaa\x7f\xe0G\xa4\x0f;b\xd3]2\xa5\x19\xef\xc8\x83]\x14\xb5\xb9\xeb\x11\xea\x91\xce\xfc\xfd\xa0\xc2oF\xba\x0b\xe9n\xe0\xb4}X\xc1\x8f\xddgrO\xbd\x07:\xd1?\xbe\xffI\xccpK\x1e\x15A\xd5\xcf\xd6\xbf\xf5\x1d\xd9\x12\xb8\xba\x1b\xc7\xdd\xd5 \xff\xefp\xc5\xd0\x02m'~=a\xdaC\xfd\x93\x8e\xcd\n\xd6\xe2\x81\x8c\xb0\xdfY\xf28~\x08)\x87\xf4\xf7\xa4\xe7M\xdeV\xbb\x81\xab\x02\xab\xf1\xd8M #\xb6>\xd7\xdcTU\x03\xdctl\x99y\x89\x8c\xc5\xdf\xc3\xd9\xcd\\C:|\xbb\xbe\xa3\x16e35\x82\xad\xc7\xc3\xb0\xdfRC\x86\x088m\xe1\xc7\x8b\x8bs\xf8\xe1\xcd\x05\x88C\x88\x8f\xef\x7f\xe2\x13\xea\x81-\xe8\x15\xfc\xc9T\xc7\x8b\x87\x1d\xf9\xf3\x9f\xfel\x89\x03\xb9\xd7i\xe5\xb8\xf3\xc5\x87\xf5\xe4\xae\xef6\xfb5\xa1\xde\x01\xe9\xfb\xce\xba\x9f\x83\xd5f\xce\xfe\x1f\x98\x81fK\xac4\xfdk:W\xbb\xee\xd3~7m\xd5\xae+\xba\x83\xedZ\xd4\xac\x00m\n+\xfb\xae\xbagC\xbfUtt\xc3\x95\xb4\x92U\xa5\xff\x7f\xdf\xd5\x1b\xeao\"\xa2x\xc1l\xfa\xf5\xe4\xa6\xeb\xc9\x89\xfc\x90\xca\xab\xc6\xfa\xban\xe8\xfaO\xd7\xaaAn\xb0\xa9\x89\xe8\xef\xc9\x06\x91\xd7\xb5\xd4\x0c\xb5\xb7\x84\xbd\xcc\xe6\xc6\n\x9e}\x1c\x88\xa4\xe8\xa3\xad\xa6\xeaA\xe7:\xd7\x8f\xaa\xadn\xb1V^\xf7\x84\xafzB\xe0\xea9\x12\x90\xedF\xf2\x12Fj3o\xf6\xed\x9ak0\xad\xaf\x98\xf3l\xad\xa3\xbbxu\xf7\x8bwk\xc7\xb6\xfc\xf6\xa6W\xd8\xea\xeb=\xddIS\x0bLN\x98\x1fY\x8f\xb2\x90=\x1d,\xb6I\x9d\xf4\xfe\x9a\xdc\xd6l\x0f` c\xec\xb9\xb6\xb9x\xd8\x91\x15\xd7\xc7jW\x0f\xabu\xb7\xc5\xac\xd4\x076#\x06\xbe\xd1\xa6\x13\xae5g7<\x13'\x87\xachq\xdf\xf7\xdd\xf6\xd9M\xd7=\xb7_Y\xadl;_\xdf\xc03\xfa\xe9GV\xc1\x8b\xee\xd9\xdf\xd1o\x9f\xc3\xffEl\x1b\xf6\xfd_\xf0\xb6\x7f\x13h\xfb\x1f\xaa\xfbjq\xe3\xe1[\xe6kP\xa9\x0bZZ\x0f\xcf\xbe\xef\xba\xd5\xba\xa9\x86\xc1\xd1P^\x05\xfa2\xaf\xbb\xf2\x81]\x96\xd1\x03S\x17\xfcc\xa0\x0b\xce\x1f\xc6\xbb\xaeE:\x81\x97\xfe}\xd7=[\xadV\xcf\xb1\x81\xe6\x1d\xf0\x0c\xfd\x8d)\x01\xeb\x96\xd8^\xa1\x1f\x9d\xf1Ny\xfd\xe6\xc3\xab\xf7g\xe7\x17\xef\xde?7\x8d\"\x08\xf1\\Q\xf0\x02x\x11xw\xfcS\xa0;~\xe8\xec\x9e`]\xf1\xf2[\xf8\xbb\xdd\xf5\xea\xfb\xae\xfb\xbf\xab\xd5\xea/\xf6KU\xfbpB\xdd\x18\xfa\xe6\x8e/\xde?W\xfdpW5\xb4\x93\xf0\x8ab]a\x96\x86\x14U\xdf\x18\x05}l\xb7sQ\xac\"L!\xd9[\xff\xed[h\xeb\x06U0\xbc|C\x93.\xd8)\xc7\xfa\xd3d\x83\xa4C \xd7\x0f\xf3\xf2.\xad$\x0f\xf6?H\x0c>]\x12uqO\x91\xe5\xfa\x05\xdd\x1b1\xb6\x82\x15um\x9eR\x1fw\xb2\xd8\xd4\x9aKt(\x1f1]\xe0d\x1a\xdb\xe6A\xfa\xf3\xd6fkr\x9b\xc4\xae~\x94{\xbc\xa7/\x9e\xea\xe2\xc4\x86B\x16\xcdw\x10Dh\xcf\x93\x9b\xae[]W=\xab\xf4\x97\x17\x0f\xab\x7f\x7f\xc2[\xcc\xfdb\xdb\xc5gE>\xa1\xefQ\xf3\xac\xfd\xf4\x87\x0f\xef\xde\xea\x7f\xf9\xf6\xdbo\xbf\xb5\xfb\x9e\xbe7\xef-\xb9?\xd1\xd1\xe9\"\x16S\xee_\xef\x87 \xd3p\xbbo\xaa^\x97c\x7f>\xb2\xec\xbdy\x19<\x01\xb2\xbd&\x9b\xcd\xbc \x9e\x88\xb5\xd5\xd8\x91*\xcb\x13\x8f\xee]\xfd/\xda\xec+\x11B\xd1h\xbed'\xae\xe4\xf4{\x898\x88\xd5\xfa\x13\x9d{\xf3\x86\xe2\xa6n\x88m\xdf\xe4\x1c='\xfd\xd0\xb5\xa8:\x8b\x9d\xffM\xdd\x0f\xe3%\xeb\xf9o\xe1k[\xd2\xf4\"c\x01\x15\xef}\x13\xb6\xa8\x00h\xa9OX\xfb\x9f\xbc\x84'\x98f\xeb\xcdZ\xf1\xda?9\xc1\xe4\xb0z\xbf\xad\xb6T\xd6\xff\xe4U\xfc=\xfa\"\xad\xb7\xf1^\xa8\xf2g7\xc2\xb1\xd5\xc7\x98\x8fP=\xc0g\xd24_}j\xbb\xcf<\xce{\xc7B\xf1\"0k+\xaa\xaeN'\xdc\xd92tl>\xf5\x13ER\xc5a\xc8q\xa66\xba\xc0+\xa6\xc4R\x87\xee\xbaf\xa3\x85\x86\xd9\x14\xa8\xdbI\xf7@D\x12\x84\xea\xe9\xb2\x98\xf8I\xe3\xe0\x19\x9d\xbf\xb2\xb9\xd6\xb6UFQ\xfe\xfc\xa7??G\x943g\xbc\xf5\x02\xf0!g\xcd\xa6\xa2\xbe^}\xf3\xf57\xc3\x13d\x18\xe5\xffi^\xf5\xc4\x9f\xd7\x93q\xdf\xb7\x1cf \xff8\x1c\xcf\xb4\x8fg\xda\xbf\xf53m\x1d\x13\x8a\xc4\xcbcr{\x95\xcf\x84\xb4\xf7\xe7\xafd%\xad\x03m&\x1c@(\xd0\xdc&Yg9\xd6\x82\x93\xa9\x85j\xcd\"\xde*\xa3 \xfb\xebXOYPR\xbb\xfc\xca\x9aH:\xba\xad\xdb\xcb\x8d\xaa\xe6pT\xa9\xdf\x88JYM\xfb\xb9n\xeb\xed~+uG\x80C\xa4ZP\x95!-\xf5\xd8\xf85\x0e\xc0A\x19R\xd6\xb6\xfa\"\x07:\x0e\xae\xe1\x8e\x1f\xfc\\}a\xf5\xe0bX5NiK\xe9\xa2Ez\xa6\xbb\xb2\x8a\xb4cg\xc5\x85\xb3\xb6\x1ek\x0d\xbf\xcf\xc3,\xa0^\x8b\x08\xdb\xae\x1d\xef0\xc0\xb9\xa6\xe26\xb5\xcd \x00\x16\xec%\xea\xe5\xc2mwO\xfa\xb6\xa2&_VbpL\x1fy\xd7A\xf4\xcc\xc94\xf6,\xb0m\xdb\xf1\xe39\xce\xf1\x1c\xe7x\x8e#\x9f\xe39\x8e\xfc\xfcx\x8es<\xc79\x9e\xe3\x1c\xcfq\x8e\xe78\xca\xbf\x8f\xe78\xc7s\x9c\xe39\xce\xf1\x1c\xe7x\x8es<\xc7\x81\xe39\x8e\xf6\xda\xf1\x1c\xe7x\x8e#\x9f\xe5\xa3[\xe8\x1c\x87\xdf\x9c\xb7\xb7\xe8\x8c\x0c\x1f\xdc\xbe\xe3U\xdeEz\xf9\xe1\xe2\xf4\xe2\xe3\x87\xcb\x8fo?\x9c\xbfyu\xf6\xfd\xd9\x9b\xd7\xde\xf7^\xbf9\x7f\xf7\xe1\xec\xe2\xf2\xfc\xcd\xfb\xb3w\xfeW\x7fywq\xf6\xf6\x87\x987\xcfO?|\x08\x94\xfb\xfe\xcd\x1f\xde\xbc\xba\x08\xbc\xf4\xfd\xe9\xd9O\xca+\xd3\xc5\xb11\x8du\xc7\xbae\x00\xf3\x03\xebi\xd6\x97\xcc\xf3U8\x08\xc5(\xf0\xab\x0e\x95\xb8\xac\xa6/\xdeN\xf7V\xd2 i\xe7+\xe1\x14\x9e\xe6e\xaf\xfcE\xe9\xe3f\x97\xa6\xff\xae\x04\xf6\x8db`\xb3gV\x90\xd7\x84\x05\x84\x95\x82\xf5\xc8\xb8\xa3*\x9a^\xd85\xd1~\x8e\xab\x08\x8f\xcb\xa7\xd6\x83k\x9d]\x01\xfewO\xc9\xda\x18s\xb3vW\xa9\x81<\x0eQ \x14/5\xda\xae\x80\xfc%\xa7\n\xd7\x84\xb4\xd0\x93\x7f#\xeb1X\x13>m\xecz\xf0\xbf\xe7\xd4\xe2\xa6\xaa\x9b\xb9\xf8\x9b\xba\xad\x9a\xcb\xb1j\x9a\x87K\x1e\xc3z\x191\xfd\xec\xaf\xe4\x894\xfb\x05\xd8/\"&&=\x93\xf9`\xe4\x8fw\xa4U\x841D\x10_`\xa6z\xdf\xd7\x15\xdc\xbe?\x7fu\"bE\xf2\x08\xaa\xedF\xd8u;\xc1y\xb7o\xc7\xba1\xb6\xcdR\xc4\xd3A?\x1ab\xeb\"i7\x0b\xc9\xdd\x9f>\x90\xe1iTd\xa5\xba\x1e\xa8\xbf\x14\xf5\xee\xd3\xb6\x8b\x13\xdav\x97\xd4s\xbb\xbc'c\x17\xfc`\xd8_o\xeb\xf1r\xac\xb7\xb1\xb7\xfc\x9aw\x15\xcb\xb33\xd2nr\xc4\xf0\x8b\xd6\x8f'\xae\xbf\xd1\x13Wq\x03\xfe0V}\x96\xb6\x089Y\xca\xa2u\xb2\\\xdd\x8d;\x1bz\"zfK]\xd3^\xd8:\xe4\xd83p\xea\xf9Aq\xcf\xac*\xaanY\x8cK\x16Z\xd6\x9d\xafa\xaeX\xc0\x0d\x0b\xba`^\xf7+\xda\xf5\xc2\x15\xbe\x84\xcb\xf5H\xeeVhL\x0e\xe1j\x19\xee\xcd\xa3\xbbX\xc1\xf2\x0f\xe3Z\x19n\xd5c\xbaT\xb8;\xf5X\xae\xd4\xecF!\x96F\\\xbd\xc4\xf4#\x19\xf5\xeaX/\xa3\xdc\x144O\x03b\x8e\x81,\xd83\x92\xa0\xe7\x11\x84\xad\xa8\xd8:\x0f\xffyo\xf8X\xec\x0bX\x92r\x1c\x02KX\xe1{<4\x07\xa8h\xca\x1f\x18i\x7f\xde\x8cXcr\xc5$\xc4\xca\xcaF\xe6\xc3\xaa\xc5\xa4\xdf\xa3!\x13\xb3\x0e\xeb\xf5:\xa7\xbbW\x91=L\x07\x8eI\xbf\xd0\x95\xfe/|ml9cP\xd8\x1c\x1c\xe4j\xd8\xdf\x9b\x9c\xe7YfA\xd7/n\"P\xb5\x97\xf59\xb2\xb0\x1cYX\xfe6XX\xcc\xf5$a\xdd\x1a\x92\x16\xae\x85\xb7l\x8a\xad\xbc\xf7\xb2M\xed\x1d;1\x99\xdf\xb9\xd9\x13\x1e\xa2\x1b;\xf1\xfe\xa2\x19%\x8bBn\x89rj\x90V\xd7\x9fH{;\xde\xc9\x10$\x9a+>\xe5\x89\xfb\xda\xac\xbf\x14\xd1h\xf1\xc1\xa2V\xa30\x058:\xd4\xcb\xd7\xd0\xa2+h\xbeC\x0df\xe3\xb3\xc0\x0e\x10\x00<@\xf4l\xb1\x93\xfd\n\x82\x1f\xc0\x01\x80\x00\x03\x04\x01\xfc\xf4\xc0?\x1f\xd5W\"f#{}\xd1\\\xfcu\xdf\xf5\xfbmdg\xc6\xad\xe8v/\x8b\xd1\xdf\x91~M\xda\x91\xae\xc5\xd4`\xb1\xd5l\x18\xabOD\xb9\x8a\xe3\xbe\x1b\x89P\x0fq\xbcb\xad\xed\xd7Vb\xee\xbak\x87zC\xa8B\xb28\x9d\xb68\xdf\xf5d\xa0\xe3\xf9Hm\xa4\x1a\xd2\x8f\"c\xe5_ ;\xa5!\x1c\xcc\xa2\xea;\xf5eV\xf0\x9a\x07\xfap-\xfa\xef\xab\x7fV\x1brO\xc6\xee\xf2\x91[\xc3]\x81\xee\x06~!bl\xd8\x9c`\x17\xc3\x8b\x7f\xb2d\x02\xb3y\xc1\x11\xa3\x8d!\x9b\xa9\x03d\x93\xbf~\xf1\x8f\xc8\xce\xf8\x00\xd76\xdb\xfe\x84\xa8|\xb2G![\xfdr\xc9\xec+\x18\xce\xb2 H\xe0[#\xdd+\xe4\xe8\xb8\xd5%{\xc7X\x10\x9a\x04\x81\xdb]JB\x94\xa0$L \xfcw\xbcd\xc1\x95\xa0 d \x82\xb0%X\n]\x82\x1c\xf8\x12\xd6c\x0f;\xa6/\xce\xfb^2`L\x88,\xeeH9\xef|\xc9\x833!\xe2\xf6;\xe7\xbd/\xa5aM\x90\x0fm\x82\xf2\xf0&\xc8\x838A\x1e\xcc \x9f\xa2h%\x8b\x81\x9f\xa08\x00\nJ\x82\xa0 \n\x08\x05%\xc1P\xe0\xbd\x1f&\x0f\x14\x85\xcdq\xf4\x8e\x18nj\x82P)\xc8\x86K!\x02\xb1\x9bb\x16\x83\xa8\xc0u[L`\x89\xf7\xdc\x18\x13\xb3\xfe/\x04Vaf\xcfyoL\xa8\x1ey +C\x18\x83\\\xa1\xb7\xc7\x14\x01[Ai\xc0\x15 \xa0+\xc8\x07^\x19\xd2F\xe4\x1e\x99<(\x16\x84\x10J\xe0\xbbM&\x02\x96\x05\xaek,\x12\xe0Yn\x19Vz~\x16T\x0b\x12:#\x04\xd9\x82`\xbb\x83\xd0-H\x83o\x01z'B&\x8c\x0bBP.\x08\xdc5\x13\xbam\xc6\xd3K\xb1\xd0.\x88\x80w\x01z\xebL\x16\xcc\x0b\xe2\xa0^\xb0\x08\xee\x05\xce\x8e \xc2\xbe\xa0\x1c\xf4\x0b\xdc\xb5\xb04\xad(\x0c\x0c2\xa1`\x86(\xec^\x9a\xc2\xe00(\x0c\x10\x03\xff\xed4\xd8\xfd4\xd8\x0d5\xa5\x00cP\x124\x06\xc5\x81c\x00\xb1\xe01\x88\x01\x90A<\x88\x0c\"\x81d\x80\xdfX\x83\xdfa\x12\x0f;\n\xddZ\x13\x0d,\x838p\x19`\xcd( 2\x83\\\xa0\x99!\x0b\xb9\xcd\xa6$\xf4\x0c\x8a\xc2\xcf [\x1f\x8204\x88\x80\xa2\x81v\xb3\x8d\x0dI\x03\xdfn\xc6\x84\xa6A(u7\xf8\xae\x13\xa2\x86\xbf\xee\x82\xa9\xe1o[P5\xfc5\x04\xae\x86\xbfh@\xd6 %wz\xfe\xc0\x97\xc3Q\"\x97Z>\x8f\x94S\xed.\xee\xf0\xb9\xd5\xf2A dx\x95\x0e\x9ck\x9d\\\x9f\xc3\xe4^O\xd5\xb0\xa1mx5\x0e\x97\x8b-\x1f7\xc4\x0d\xaf\xd1ar\xb3\xe5\xa3C\xdd \x00w\x83`x\xa4\x00\xec\xcd\x90\x98\x89}3\xa4!!w)5\x06\x0e\x07\xcbN\xc4\x10h\x1c\xf8ce(D.\xf0\x8d\x0d\x95\x0b|\xe0\x86\xccy>D\xa1s\x9e\xf7\xdd\xa0(PR\x8bld\xd4b\x91\x0eH\xdd,\xef\x98\x1d\xf4\xb7\x9f\x1d\xe4\x01\xe6\xc1R\xc5q\x82\xf4\x16J,\x0e\xd8\xf3\xe6M\x9a\xe9\x07Q\xf9\x0d\xb2RIy\x93\xe2\xa3\xf4\xd4I\xd9\x96\xbf\xb5\x94\x7f41b\xb1\x81\x18\x9d\xe9\x11\x01\x1b\x11\x9e\xf0P>I\"\x94&Q:Q\xa2p\xaaD Y\";]\xa2l\xc2DL\xcaDF\xd2D\xd9\xb4\x89\xa8\xc4\x89\xb2\xa9\x13\x11\xc9\x13\xc5\xd3'\x02 \x14\xcbR(PA\xde\xb4\x8a\"\x89\x15\x91\xa9\x15\xe8\x97I\xe9\x16\xd9 \x17\xa5S.\xdcI\x17\x85\xd3.\x0e\x91xQ8\xf5\"6\xf9\xa2p\xfa\x85?\x01\xa3x\n\x86; #!\x0dcy\"\x06*\xcc\xc5e\xcb\x9f\x8cd\x0cg:F\xd0\xa5\xf0\xa6d\xc4y\x1c\xe5\xd22\xfc\x89\x19\xe1\xda\x14M\xce\xf0\xa7g\x14K\xd0\xc8M\xd1\xb0\xc41\x8f\x06u\x1e\xca\xa6i\xb8\x125\xf2S5\"\xf2\x13\xbc\xe9\x1a\x91 \x1b\xceS\xdf\xc4\xa4\x0d\xb7\x1c\xe4(,;u#\xa5sb\xd27\xc2\xbd\x10\x95\xc2\x91\x9c\xc4\x81\x1f\x14\x16H\xe4\x88H\xe5\x08%s\x84\xd39\xbc\xbd\x96\x92\xd2\x11\x97\xd4\x81\xa7ud'vD\xa7v,M\xeepwST\x82G\xd1\x14\x0fO]\x10M\xccJ\xf4\xb0\xa4!\x89\x1fES?\\\xc9\x1f\x99\xe9\x1fv\x95\xedt\x90\xf2 !\x81\x94\x10<)\x04O\x0b)\x99\x18R85\xe4\x10\xc9!)\xe9!\x91 \"I)\"\xf1I\"\x8e4\x11Wb@|j@8U$)Y$:]\x04mP\xe9\x94\x91\xb2I#\x8e\xb4\x91\xd2\x89#\xa5SG\xf2u$*}$.\x81DO!\xc1\x93H\xbc{0,\x91$-\x95$\x9cp\x10\xf1\x81/\x9d$:\xa1$!\xa5$2\xa9dAZI(\xb1\xa4lj\xc9\xa3'\x97\x84G\xfb\x90\xe9%\xce\x84\x8e\xa0N\x1d.\xc5$\xa9N\x87M3q&\x9a\xfcuRMB\xc9&\x8f\x9fn\x82%\x9c\x84SN\xc2\x81\x9f\xac\xb4\x13K\x1a\x96\x86R:\x11\x05MEIOFY|\n\xe9HI \xa5)\xb8\xd2R\x02\xdf\xe1\xa9)\x81\x8f\xfc\xe9)\x9e\x8f\x9d)*Q\x07\xc0Xj@(Q%C\xb0']\xc5\x9d\xb0r$\x89KNa)\x9c\xc4R\x8a$.\x98\xca\x92\xa1Z\xde\x84\x96\xc5r\x0f\x90\xd6r\xe4\xa7;\xf2\xd3\xfdv\xf9\xe9\xfe\x03\xcd\xb3J#\x92\x91\x1f%eZ]P\xaf\xe5=\xeb\x82\xe4\\+\xe6\xf18\xe6\x12\xf7\x86\xd4i\xd4\xf3\x0e\xa4\xaa\xb7\x98\x07\xeao\xe7\xfe\x07O\x16\x1d\xd2\xe51\xe3\xcb>K\x1a\xdb_\xba\x91$\x0f\xea}7Zw\x07D\x8d\x8d3%.}\x16\xd3:\xc4\x11^\xf3\xd4\x1a\xfdU\xff\x8a\xff\x9a\xecz\xb2\xa6>3]Y\xc8\x0d\xe9e\xd2\xd0\x15\x176\\A\xdd\x0e#\xa96\"\xbdlZ\xc2\x072\xda\x87\xa1\xd4F\xd6d0m\x01;\xa2\xd9\xf0\x90x}\x03W\x0di\x9f \xf9\xcf\xe1\xdbo\xe1\xeb+\x11f\xafF\xd1\x08\xe6~\x7f&\xec\xf8\xfek\xd3\x03?kY\x86\x94\xf1W\x9e0\xb0\xae\x062h\xbb\x03f\x8e\xe5n\x7f\xec\xe0\x97w\x17o.\xdf\x9d_\x9c\xbd{\xab\x06\x07\xb0\x9csd\x98p\x1c\x92C\xa6\xf7\xbd\x7f}\xf3\xc1\xfb\xfb\xe9w\x1f.N\xcf\xdez\xdfy\xfb.\xf0\xf3\xe5\x1f\xcf.~\xbc\xfc\xe5\xcd\xc5;C+D\x90'\\q1N\x98\x02\x1e6\xaf\x1b\xd3f\xfe\x04\xbcc<\xc2\x07 \x03\x85\xbdm\x0f\x17\xf6\x16>h\xd8\x9b\xd6\xd0\xe1/9\x06\x90? \xc38\x7f\x12\xda\x01\x00PS\xf9\x8eOC4V\xc7\x08\xff\x84^\x00'\xfe\xbb\xad\xef \x9e\x06\x86\xfa\xa0\xe8\x9b\xee\xe1q6P\xd9\xa9\xb4\xddW\xddN\xad\x9a\xe3\xe8\xd1\x1a\xd5\x97\xe6\x1f\x14\xa1\x0f\x82\x0c0E\xa4P\x81\x97\xd8\x1fA\xa5\x9f\xe6\x8br\xb2\xfc\xb7\xef^\x1a\xff\xd6za\x81\xbcY\xc7L\xc9\xf3/z\x19lCx/ \x06\x9d\xa5q\x03\x9e8\x85C\xd1\xe4?2\xa1d\xa3h\xe9\\\xb7}[\xb3x\xd3\xc4I\xc9\xfeg\xd85\xb5y\xc3\x11}>\xd4\xed\x9a\xbc\x14.\xc3W\xc3\xe6\x13\xfc\xf7\xd5?\xfd\xa3n\xe9\xb8\x07\xfd\x14\x7f\xf5\xe9\xf4\xae\xbb\xd6\xb4\x9eJ\x0dy\x87\xb5\xfe\xdd\x19\xb0\xbcr\xf6%#\xca\x1cF3\xdew\xf6\xfaDFhI\x7f2\xddUk\x0d\x87\xcd\xca\xa8:B1\xae\x16\xabE\x1c##}5\x1d\xa5\xc0H)M/\xeb7\x8eP@\x9c2\x8f(|)\x0b\x99\xe2<\xe7\xcc\x12W\xb7.\xf7\xac\xb8\x83\xe6r\xd1\n;i\xde\xc1s\x9d\xf3\xc5.\x95\xe1\xd5?n\xe5\x0f\xae\xfa\xd1+~\xd2j\x8f\xbam\x8f\x1b\xdft;pA\x17\xce\xe7\xc4\xa5\x0c\"\xf6\xbe\xcb\x91\x8b\x1dP\xec]\x873\x970\xb8\xfcI\x1a\xe2\xf9\xa3\xb0KW\xd8\xa9Kr\xeb\x1e\xc9\xb1;\x88kwx\xe7\xae\xbc{\xf7\x88\x0e\x9e\xcf\xc5K9\x8a@\xdc\xbc\xa2\x8e^\x94\xab\x17\xef\xec\xf9k\xbf\xd4\xe1+\xe5\xf2Y\xf5\xe3\xac\xdf2\xf0J\xbf\xe1\x8e\x00\x9f\xf4\xc3\xf1H\xe0x$\xf0[?\x12\xb0\xb7\x17\xb1[\x97\x086y\x16L>W\xeev\x88\xd8\xb4\x98\x970\xa0C\x81\xea\xa6[/s/]0H\xfb]W, \x17\x12,\xac\xfd\xef\xd1\xda\xf7\x0b\xaeS\xb0\xe1T\xcaU\n\xaek\x14\x16\xd6\x1a\xed\xf3\xa5\xd7&\x18}\x9etI\x82\xa2w\x86Ae\x7fb\xbaL_\xa9\xdb[Q\x85\xae\xc5\x16\x91\xc1\xa7\xd7\xef\x95\xfc\x9e\x08\xbd6\x0e\x95\x90.\xb6\x0e\x93\x90w\xf4C$\xe4\x05\xfc\xf0\xc8z\xd1\xee.\xde\x1cey\x1d\xc6\xaa\xddT\xbd8H\x93\x9ek\xf4\x05\xcc\xbfL'<\x11\x9d\x83\x86\x11\xbcZ\xa8\x19`#h\x80|hn\x99\xdc\xb3-/8\xa0\x9f\xdc a\x81\xdc\x80\x00\x9c\xa9\x05\x18\xa1\x80bA\x00\xb4\xeb\xcd\x9dc\xccn\xd1\xb7C\x0c\xed\n=;\xc1\x88\xdd_\xe4\x8e\xcf\xda\xce\x1f\"J\x86o\xd8\x8f1\x96\xc8\xccgd\xeb]l\xd3\x1d\xb9\xdd>\xf8F\xbb\xf0\x16\xfb\x90\x9b\xeb\x92\xdb\xeaG\xd9P\xe3[i\xc7\xf4\xf3\xedn\nm\x9c\x03[\xe6\x98\xcd2^\xcb%\x1b\xe4\x12[c\xc7\xf2\xffN1{Vo\xab&.d\xd8\\\xe6\xccg\xc4\x1c\xa6+`\xb0\xa2\xcc\x14n\x9c\x96\x9a$ep\x84$\xdb\x10\x1d\xc8\xfc\x141:\xe5MM\xbe\x819\xa0Y\xc1u\xbdno\x13\xf7\xb9\x8e\xebNQ\xab\xa4)\\\xf0\x9aS\xedm\xb5n\xae\xbd\x90\x90\x91\xb6\x07\xb2mat\xd3Mw\xe8\xbf\x8e\xb7\xe9sl\xca\xd9\x0f@m\x88\xdd\x92bv\xc4\x16\xbd\xd0\x96\xd8\x82J\xd8\x13[\xea\x12\x9b\x82I)hW\x00qT\xfc\xdb\xf6i\xd9\xcfvK\x9c\x0e\x896\xefW\xafUhG\xc4<\xcf\xdc\xd5\x0b$I\xe7\xdf\xd9\x9b\x90\x8dC\xec\xdfP@\x89s\xfb\x86\x83H\x16\xb8\x9b\x8b!#\xaa\x90\x1c\xac\x88D\x86\xa8\xf2\xe2 !x\xb3\x84\x02i\xf3\x96WJ\x8c5\x97E\xff\xbcf\xe1n\xa86\x9b\x9e\x0c,b\xc8\xfe:\xd6\xf7\xb6\x97\xe4\xd0\xd3\xc4\x05\x19\xbd\x89\xfb\xa8M\xbf\x05m\xb2\x9a\x96u\x87\xb6\xef\xfe\xec\xb0\xfb\xa5\x87\xe6\x0b\xde\x99\x8d\xdd\x97\xad\xde\x95\xad\xd5BSq\x97_'\x8a\x8e\x8fn\xaf$\xba#z\xd2,\xb3\xec\x822\xe1\xc0Q\xb7\xd1AO\xeb\x89\xbb\x85\xc2NE)i}t\xb4%\xa9h\x0b\xd2\xd0z(h\xc7\x1c\xfa\xd9r\xd4\xb3!\xda\xd9\x85\x94\xb3\x8b\xe9fY\xbbm\xf0\xb8\x87jv1\xcd,\xb7\xbd\x96<'\xc5l\x0e\xbd,\xecw\x96<\x17\xb5\xec\x12ZY\x1f\x85l6}l\x14ul\nMl\x16El\x06=,jV\x8a\xd2\xc0\x96\xa6\x80-H\xff\x1aC\xfdZ\x90\xf6\xd5M\xf9Z\x94\xee\x15\xa7z\x1d\xe3h^\x97R\xbc:\xee\xda\xc5\xe8]\x17S\xbb\xa2\xb4\xae\x9e\xa5\xd8C\xe7\x1aZ\xa5K\xd1\xb8\xba)\\}5\xc8\xa3n\xe5T\xad\x9a@\x8c\xb6\xb5\x08ek\x1e]\xab5K\xec\x05\xb7$M\xeb\x88P\xb4\xe6\xd1\xb3\x06\xd8G\x9d\xb4\xac\x11\x94\xac\x18?c\n\x15+\xf6\xfd_\xf0\xb6/\xa4_\x8dk|\x98v\xd5\xd7\xd2\x08\xba\xd5$\xaaU\x93\x97.\x9bb5@\xaf\xea\xa3V\xf5\xd3\xaa:z%\x9eN5L\xa5j\xd3\xa8fQ\xa8F\xd1\xa7.\xa1NE\xa9J\xc3\x94\xa9\xc5\xe8R\xd1\xf2\x0dM\xca\xa2H\xb5)Qs\xe8PQ\xfa\xd3,\xeaS\x9b\xea\xb4,\xcd\xa9\x87\xe2\xd4f~\xb4\xa9MK\xd1\x9a\x16\xa44-Mg\x1aKe\x1aAc*v\xfea\nS\xf1b\x80\xbe\x14a\xfa\xc4J\x8d\xa5\xa3\x0cQ\x96F\xd3\x95FQ\x95\x1a\x95/KQ\x9aEO\x8a\xd1\x91\x96\xa4\"-IC\x9a3\xde\x11\xf4\xa3a\xeaQi\xfcM\xc2Qto`\x9f\n\xc7\xb1i\x86\xa8&\xbd\xaf\xba(E#\xc8D\xa3hD\x83\x04\xa2I\xd4\xa1\xee3\x80Rt\xa1\x8fH\x14\x1a\x1a\xb7CQ\x84\"D\x9c\x7f\x15Z\xd0\xa8z\x1c\x8e\n\x14!\x01}l\xfaO7\xf1\xe7cR~\xead\x9f>\x9aO\xf7\xf4\xcb\xa2\xf6\xe4T\x9e\xb3\xb0L\x0eOc\xf3.E\xc4\x90u\x8e\xd8)\x0c~\x06\xf3@\x86\xcb\xb5}|\xe9\x88\xfa\x88\x14\x8e\x84/\xda.\xed\xe5 \x19\x11\xf9\x19J\xbf\x89\x8aw\xd1\x0c\xba\xc96\x93\xc48\xa85\xc7\x03\x9c\x9a\x1dO\xa7\xe7'\xf6t\xdaC|\x994\xccN\x9a\xcb$)[2V\x9bj\xacRN\xb5\xe57\xbc\xaf\x1e\x94\x90\xe5\xf4K5\x8e\xd5\xfa\x8e\x9fJ\x8c\x16\xeb\xb0&\xad\x04\x97&~0\xfdAq\x12\xadV\xe9)\xc5a_)\xe4\\8_\xc3\x1c\xc2\x803\x18t\x04\xbdN`\xb4\x03\x88O\xb1\x12\x8e\xdf#9}\xa119\x84\xc3g8Y\x8f\xee\xe8\x05\xcb?\x8c\x83g8w\x8f\xe9\xd8\xe1N\xddc9t\xb33\xa7\x1b\x19\x86m\x16\x995\xc9dK\x8e\xc59\xcaO\x92\x15-@j\x89$Mz\x04a\xcb7\xe6T@a\n\xc2\xe3\xd5\xf2!V\xee\xfc\xab\xe5\xdd\xdeV\xd14L0R1]=\x8eM\xae\x18\xaa\x01Y\xd9 %\xad6}\xd3\xc9\xd2d\xb2\xdca\xbdk\xe7L\x8f:\x0bGX'\x1c\xf3}\xa1\xcb\xfe8\xfcUG~\xfe\x14KP\x8a\x9f\xdf\xd7\x05\x99\x16A\xd7/n\x1dP\xb5\x97\xf592\xe2\x1c\x19q~\xab\x8c8\xe8\x8a\x95\xc4\x8e#\xbf\x89]\xb3x\x16w\xf2\x8a%\x11\x84\n\xcc\xc1j\x8c\xf6\x8e\x9d'NF\xba\x0d\xef \x8f\x0c\x8e\x9dx\x7f\xd1dr\x00\x1a\xc1\xa7\x8b.\x9e\xf9n4\x98\x8d\xcf\xc2\x9e@\x00\x7f\x02\xd1\xb3\xc5\x1a\x83\x92X\x14p\xe0Q\xc0\xc0\xa4\x88\xdb9\xfc\xf3Q}%b6.\xbf\xb7\xc3\xa4\x0e\x83\x8c\xce\xccd\x0c3\xc4][\xc9\xa5.\x0e1\x00\x94G\x0c\n4\xa5_@\x1f\x86\xeb\x84B!\x06\x1e\x1a1\xc8\xaf\xf4R\xf6\xb0`\xff'\xf1\x89!\xabt\x8c\x1f \xd0Y\x1e\x9a\xbc\x95v\x11O\xb2\x07 \x1b\xfcr\xc9l\xc9\x8f5\xd90.x\xa4\x85ot@\xbb \xb4\xf6\x85\xf4\x8e?Ea^\x10\x80z\x01\x94\x85{AY\xc8\x17\xf8a_\x00\x99\xd0/(\n\xff\x82\x08\x08\x18,\x87\x81A\x0e\x14\x0c\xef\xbb\x87\x1d\xd3!'\x1c\x0cr a\xa84\xee?9aa\x90 \x0dC\x05\xeewNx\x18,\x84\x88\xa1\x82<\xb01(\x01\x1d\x83X\xf8\x18\xfae\x02\xa4\x0crae\x90\x07-sMtGU\x0bB\xce\xe0\x00\xb03(\x0b=\x83H\xf8\x19\x94\x85\xa0\x81\x17\x86\x06P\x18\x8a\x06N8\x1a\x08\xc3\x15\x01I\x83\x0cX\x1a*\x8cA\xd5Ph\x1a\xe4\xc0\xd3\xc0\x05Q\x83\xb0K\xe1\x81\xaaA\xa4\xc7Q\n\xb2\x06^\xd8\x1aD\xd4&\x0f\xbef\x89cp6\x14\xc2\x06\xa5`l\x90\x0de\xb3\xc41\x8f\x06u\x1eJB\xda@N#\xa4Ay\xd06\x08#\xbc\xc0\x07q\x838\x98\x1b\xb8\xf05\x89p7\xf0\xc8A \n\x99\xd07H\xea\x9c0\x04\x0e\"z!\x02\n\x07\xa9p8\xc0{'\x1f\x16\x07ah\x1c\x04\xe0q\x10\x84\xc8\x81\xbf\xd7\xe2\xa1r\x10\x05\x97\x03\x142\x07\xb9\xb09\x88\x85\xce\xc1B\xf8\x1cx\xba)\x02F\x07%\xa1t\xe0\xab\x0b\xa2\x89Y\xb0:K\x9a\x05\xb3\x83L\xa8\x9d]\x02\x02\xbd\x83\\\xf8\x9d]e\x13\x8e\x07\xc5!y\xe0\x87\xe5\x01\n\xcd\x03\x14\x9e\x07\x05!zP\x16\xa6\x07\x07\x80\xea\x01\xc4\xc3\xf5 \x0e\xb2\x07)\xb0=\x88\x86\xee\x81\xc3\xfa; ]\x90\x00\xeb\n\xc1\xf8 \x05\xca\x07\xb1p>\xc0\x1bT\x16\xd6\x07\xb9\xd0>K\x1a\x02\xf5\x83\xc2p?(\x0c\xf9\x83\x02:\x12\x01\xfd\x83(\xf8\x1f(\x10@@`\x80\xe0\xdb\x7f\xe1\x97F\x87\x92\xa4}\xef:a\x81\xf8\xeb.h \xfe\xb6\x05\x0f\xc4_C \x82\xf8\x8b\x06L\x10R2\xc5\xe7\x0f|\xb9+%2\xc7\xe5\xf3H\x19\xe4\xee\xe2\x0e\x9fI.\x1f\x04\xb6\x87W\xe9\xc0\x99\xe5\xc9\xf59L\xa6\xf9T\x0d\x1bN\x88W\xe3p\x99\xe7\xf2q\xc3\n\xf1\x1a\x1d&\x13]>:\xbc\x10\x02\x10C\x08\x86q\n@\x0d\x0d\x89\x99xCC\x1ar\xf0 \xa5\xc6@\x10\xc1wr\xe8>7t\xc0\x11\xc1\x1f\xe1\xf3\xc0\x12\x03_\xe2\xf0\xc4\xf0G^\x98\xa2\xe7s\x14\xae\xe8y\xdf\x0d!\x03%\xd7\xcaF\xa3-\x16\xe9\x801\xc2#\x9d\x1a\x1f\xd3\xa5\x1e#]\xca\x03\x86\x84\xa5\x8a\xe3\x04F.\x96h\x83$\xbd\xa2\xca\x82%-\x89%\x00\x93\xde4V3\xc5$*}EV*6\x8dU\xbc\x9f\x9e\xc9*\x9bq`\xf0E)\xcc\x05\x9e\xf7\xe2\xb6a\x87\xc0P\x8c\x9e\x0c\x98\xe0\x81U\xd8>\xf1gi\x1e\x8cC\x9c~\x9c\xe3\x88\xa6\x16\xcf\x85)\x9e\x0d\x13\xcc\x87)\x90\x11S:'&.+&+/\xa6tfLdnL\xe9\xec\x98\xa8\xfc\x98\xe5\x192.q\x8cR\xd9\x9b#S0K&\x98'S(S&'W&9[\xa6@\xbe\xcc\xf2\x8c\x19\xa7\xe5A\x0f\x1a\xe4S8k\xe60y3\xc53g\xe2sg\x8ag\xcf\x84\xf2g\x96d\xd08\x04My5\x9e\x1c\x9a\xa4,\x9a\xc2y4\xa1L\x9a\xcc\\\x1aO6M\x84{\x12\xc8\xa8\x89\xf5_Jf\xd5\x84\xf2jb\xeaT8\xb7&\x94]S0\xbf\xa6x\x86\x8d/\xc7&+\xcb\x06\x91Fk2:\xf2lJd\xdaD\xa5\x93\x04\xb2m\xa2\xf3mY\xe8\xd9b\x81\xcc\x9b\xb4\xce\x8a\xcb\xbe\x89\xe9\x93\xc8\x0c\x9c\x0598\xaes\xd8\"y8Q\x998\xe1\\\x9c\x98l\x9c@/\xa6e\xe4\xc4\xe6\xe4\xb8\xb2r\n\xe4\xe5$d\xe6,\xcf\xcd\xf1uZd~N\xe1\x0c\x1do\x8dPM-\x9b\xa7\xe3\xc8\xd4)\x9c\xab\xe3\xce\xd6)\x9d\xaf\xe3\xc8\xd8\xc9\xca\xd9A\xa4\xb96~\x81<\x1eW&\x8f+\x97\xa7l6O\xf1|\x9e\xc3d\xf4\xa4\xe5\xf4Dg\xf5$\xe6\xf5\xa4d\xf68s{\xdc\x99\x1b\xf1\xb9\x1b1\xf9=\x89\x19> 9>\x8e\xa6e\xe4\xf9$L\x8a\xb2\xb9?\xce\xec\x9f\xf2\xf9?\xe53\x80JhRd\x16Pl\x1e\x90\x9e \x84\xe7\x02yw\x8fX>PZFP8o$\xe2\x03_VPt^PBfPdn\xd0\x82\xec\xa0P~P\xd9\x0c\xa1G\xcf\x11\n\x8f\xf6!\xb3\x84\x9cy9A\x9d:\\\xa6PR\x9d\x0e\x9b-\xe4\xcc\x17\xfa\xebd\x0c\x85r\x86\x1e?k\x08\xcb\x1b\ng\x0e\x85\x03UY\xd9C\x964,\x9b\xa8t>\x11\x9aQ\x94\x9eS\xb48\xb3\xc4\x93Y\x14\xca.\xf1g\x17\x05\xbevg\x18\x85?\x0cf\x19yD83\x8d\xa2\x8e\xd4\xb1|\x8cP\xbeQ\x86`O\xd6\xd1\xe3\x9e\xd9\x1fy\x0fSr\x91J\xf1\x1e\x063\x922T\xcb\x9b\x97\x94!\x17\xcfN\x8a\xcf8+\x92\xa1t\x90\x1c\xa5#\xf7\xe3\x91\xfb\xf1\xb7\xcb\xfd\xf8\x1fh\xd2\\\x1a\xe9\x93\xfc(6m\xee\x82zS\xefY\xeb\x93\x13\xe7\x98'\xe6\x98F\xdcKSgP\xcf\xfb\x8ej\xddb\x8e\xb5\xbf\xed+]<\x19\x92\xc80\xc4\x0c7\xfb,v\xa8\x7f\xe9F\x92<\xc6\xf7\xddh\xdd\x04\x125T\xd2\xf0\x16`\x95\xa7u\x88c\x94\xe7\x99N\x7f\x05J0^p\x92\x17\xcb\x1f|&=c_\xbf\xaf\xbb\xfd`,d.\x857\xad\xc74T\xc3\xd8\xef\xd7\xb4\x8b\x0cJ\xbb\xc17\x91\x8dI\xec3Z\xa4\x7fP\xcdc\xfa\xae\x92\x9a\xcfKi>\x0f:\xb2GG\x96\xff\xfdo\xc8\x91u\xf9B~\xffk\x92`\xcc\x19\xd3\x03\x1b\x9aj\xb8\xab\xdb\xdb\xa5\x99\xb6\xd4\xb6\x92\xcd\xa5\xc8\xe1\xf9\\\xb7\x9b\xee\xb3\xe9l\x19c&\xc7K\x1d\xaem\xdd^\nQ;\xd2\xa7\xc9Q&\xc3\xa6\xfb\xdc\x8e\xf5\x96\\\xfe[U7\x97\x1bq\x97\x80W\x0e\xeb\x80\xcb\x9b\x9e\x1f\xa4]n\xba\xfduCX]\xd2\x8b\xb7d\xf1\xda\xa4\n\xd2\x94A\xe4j(\xa6t\xd4\x93\xec\xa6[\x04\xaf\xf9\xec\x94#jg\xb0Zc\x9d\x9dpc\xf9\xd5QF\xcf\xaf3\x103\xd7\xcd\xa9\x1e\xd0\x9f(\x99\x86Q\x0d\xe9\x92Gf\x8cNy>wV)\xa8_\xe9B\x8b\xeb\xdadtK%\xd5\x85\xf4\xf7C}\xdb\xd6\xed\xedY{\x83\xa6*\xf81NU\xc3\x86\xa5no/\xa9+\xb1H\x9d\xd1\xad\x9fK3\x18o\xf4\x1d\x82\x00\x0d\x0e\x9bsu\xfb\x91I\x83J:\xcc\x0c\x92S\x8d]\xcf\xd6 v\xf3\x0eT\xb0\xae\xda\x0d\xfd3\x81w\xef\xd9\x0f\xfb\xf6\xdf\x18\xe3\xbf\"\xb2n7\xe4\xcbews3\x90\xfc\xda\xf9\xbd\xe83Z\x94\xf4\xf0\xa9\x1f\xb7\xee\x99\x93O6@\xaa\xf5\x1dP\xbdfZ\xa1\xb7\xa6\x12I\xa4\xe6j[\xb7\xf4'jP\xd8\xcec[=\xf0\x1d!\xb7 \x0c&J\xd6\xddv[\x8f\xfcV\xe5Q\\\x01n\xdd&\xb9\xee\xda\x7f\x13\xb7D\xf2}\x0dr\x19\xc0\xd5\x07&\xf5;f\xbf\xfe\xc8L\xcd\xd5\x94d8\x92~;-\xcb\xacC\xf1Kd\xaf~\xae\xa9\xeb\xc5\x85|W\x8f\xa7tSy\xa5\xfa@|x.\xd9\x1d\x05\x89\xa3\xe1b\xc5\xf7\x8d\xc8E\xbd%\xc3Xmw\xfcV\x0416\xfa\x10\xd4\x83\xa8\x15l\xf6,B\xdd\xd4\xf7\xa4%\x83y\x9b\x84\xb4L\xbaK\xb7\xbd\x1e\xc6\xce\xda\xd7\xb8w5\xfe\xfa\xfe\xf1\x8e\xb0\xbbk\xf9x\xca{YY5\xef\xaa\x81o*\xe62\xe1\xd9\xa7\xba\xa1\x15\xa7[\x05\xebV\xf9\xf9\xd3\x81\x8c\xcf\x99\x17[3\xf7\xcd\x1c\xfa\xae]\x9bj\xc9\xd5\x8amZ\xe9\xe6\xf2\xbe\xe3\x17>\xd3z\xb1l\xb6\xf6\x81\xfbz\xb6\xa2\xdd\xd4\xb7{\xba\x17\xdd\xd6\xc35\xb9\xab\xab{\xfd.\xe9-S\x10\xb9L2W3\x928w\xf9\xb4<\x05Q\x0e|\"\xbbq\xbe(z\xdf\xb6dM\x86\x81]\xb2N5\x15zRm\x06\x8b\x9b\xe0m7\x8a\xbd\xdf\xd5\x87\xfd\xf6\x19\xa6\xe3\xcf\xaf\xa0j>W\x0f\x03\xed\xae\xaa1UG\x9b\x17\xafxe\xae\x1c;B\x1d\xa3(GDY\x10\x94P\xc54`O\x07\x10&\x9f\xef\x1e\xe9 m\xbb\xb6\x1e;\xa3\x17\xc7;Rk\xa9\x95R\xd9Y\xd2\xd5}=\xaa\xac\xd7\xdc\x14\x9b\xeb\xc9t\xf2\xa6\x16\xc8\xb6\x19\x92=\xfb\xbej\x18\xa0]\xae\"\xbf\x03d\xdb\xe2Z\xe7b\xd6S\xe53!\xed\xfd\xf9+\xd9\xaa\xe4\x156\xddO\xc4\x96\xd5\xb2\xd1\xd7\xd4\xd0\xab{\x0d\xf6\x86\xc7\xdcS\xea\x00+\xb1o-^\\\xcbP\\\xab\xf0\x9a\\pU\x8e[\x97\x0b\xad\xcc1k\xb3ou\x8e\x1a\x1f|\x85\x0e\x8fQ\xc9U\x1a_\xa7\xdd+uB\x04\x12\xa9y\xc9\xf5:a\xc5.\xb9f\xc7\xac\xdaQ\xebv\x94\x8a,\x99\xc2\xb9\xebw\x89\x15<\xb8\x86\xfb\x9b\x91\xb1\x8e\xeb\xc30\xad\xe9\xf6J\x1e\xb3\x96\xfb\xd6\xef\xaai\xe6\x8a\xc8\xf6c\xd1\xe5\x84m\xe41\x14|\x0c\x05\xcb/\xdcc\xc8\x8bT\xdc\xbfN\xbb^\xb6n\xd9\xadw\xb3G(nu\x1d\xe03\xebk}\xb5[w=\x7f\x91\xa5\x85 \xa7t\xba\x08\x96Ze\x16\xf0Q\x9b\xa9\xb5M\xbe\xf9\xa1\xdb\xce\x95Bo\x82\xed\xc9\x8e\xb0\x1b\xf7\xbe\xab\xfa\xa9g]\xf7\x1bkmd\xcae\xdem\xcc\xafv\x0d\xb9\xc9Iq'\xf5;!/\xc5Q\xc6\xacV\xb4\x8f\x1c\x93\x8b\x8d{\xad\xe8L\xc0\x17\x90\xa2>*\xee\x9d&\xd4\xc6\xbd\x02\x14\xf4B\x97\xfb\x9f\xaa\xbf\xa9\x08\xc4<\xcfl\x9f\xd3\xefm\xe2~\xa6\xb7\xa7m\xdf\xd2\xdd\xdb\xa5\xfcI\xd3\x93\xc4|H\xdc{t\xd7\xad\x94\xc7\x18\xe5+fx\x89\x8aW\xa8\x08D\xfc\xc3\x80gXd\xfa\xe4x\x80y\xbe\x9f\xc7\xeb\xc3\xab\x9b\xe1\xe9\xd9\x11\x1b\xcd\xc7C\xbd;i\xba\xc7\xea\x93j\xb9\xbf\xeb\xda\xcd\x07\xe5rpk\x14t\x84\xe3w\xef\xde\xbev\xdftm\xfeN\xff\xe5\xfd\xf1\xec\xed\x0f\xe8\xaf\xca\x87S\xf6\xb1\xafh\xdcc\x98\xdb6y\xb2\xca\x9d\xc0S\x07\xab\x18GG!/\x01%\xdej\xa1n\x05\x05\xd8<\xc9\xf4\xfb\xb1\xf1.\xa1\xf2\xf8\xffa#\xce\x95P\\\xd7\xcb\xed\xbaW\xde\xd9\xdb\x1f\xa4\xc0\xb3\xb7?x%\xee\xdbk\xee\xef8\x04\xca\xeaETn\xae\x98C\xb5^Q\x032\x0cu\x02\xff\xdcz\xfa\x84Q\x93(>\x81{\xca\x9b\xdfh\x99\x01u[\x8f5\x0b2\xca\x97@\x80_\x99\xe7(O\xf4Tq=\xa98G\x90\xaa!\xf2\xd7\x84\x1d\x0d-'\xd2q\xd7\x1a\xf7\x94~(\x15\xd6\xa87\xac\xef\xaa\xfe\x96\xefS6\xa4!\xb7l\x07v\x02\xcc\x03\x90'\xa3\xab\xa7\x8a\xf0m\xf5\xe5riM\xac\xdd\x82\x14\xa6u\xb1dg1\xabj:Xk+fB\xee9\x81F\x7fK\xcc&\x18-\xe0,\x1be\x1b\xa2\xc8D\xdb\xb3\xa9j\x96\xcfF\x15\x82\xa7\x95\xd8\xc12c\xb5d\xadw4e\xbf\xa3\x8e\xc9eBFD\xc0\x91Q\x04Jmi\xaaa\x9c=Dk@\xaaA\xd0\x95`\xc0\xa8y\xb6N\xbd\xa1\x080x\xc2$\xc1\xa16C\x82f\xe0\xfd<\xa5#l\x81>\xd8hO\x1dh\xde\xd8s&\\\xba\xa6b\xb9\xf3\x84\xcd\x0c\xd5&\xf9\xe6\x88s~,\xa8\xf4\xa291\xd7]\xad\xb2\x7f>\xe0\xd50T%\xc3\x92K+>a\xfc\xa3T\xf55W\x8d\x94\x15k\xd2\xa6%\x90b\xebc\xa9\xbe\xd7d}\xf7\x8f\xdf|E\xdau\xc78\xa3\xc4\xaf\xa2\xbf\xa7\xcf\xa4\nLm[R \xeb\xe3\xb8JX\x8b\xe3pW\xf56\x1c\xd1W2\xffB\x8c\xb2\xda2\xce\xe9P\xf1\x94\xe55\xa9\xefQ\x8b5\xf9y\xf3\xb0\x99\xc9;\xd4G\x11\xdb\xd6\xee\x13i\x07\xb8#\x0d\xcb~\xe6\xa9\xf0,;\x9bo\x88\x84\xa8\xees\xcb\xd3\xa3\xbbV\xe9g\xce\xa7]\x0fP\x0dC\xb7\xaeY(Gn\x86'\x8a\xdf\xee3\x8f\x9cu-1\x87%J\xe5\x92\xcf\x167\x86\xb6\xa2\x9f\xe1\x9f\x82_s!ze-\xa5\xc1\xe0\xd7\xe2\xf8\n\x95\xd2f@4:\xbe\x16\xe9\x9am\x89\xd0\xe2\x9e\x055\x1cJk9\x98\x9aN\x9f\xeb\xaa\xa9Z\x9bQ(R1c \x0f1\xa8G\xd8\xech=\xbb\x18\xd21\x8b\xc8Ar\xd8\x18\xfd8\xb8F\xc8\x10\xaa\xb1_\x16\xbd\xa9\x1a\xd2\xb2x\x88\xa2J\xe4\x0b\x03\x03\xf1\x0d\xdd\x08\xeb\xae\x1d\xab\xba\x1d\xa6\xf4i1\x88,\x90\xb8\xd9\xd4\xec\x9b\xb1\x93\xfa;\xc5(\xd9\xd1\xc5\xb0\xafGvL@\xdd\xc3u\xc3\xe0Y\x13\x00\xd7k\xff\xe6\x86\xc4\x1a\xbem\xd7\xd6\x9f\x82\xa1#\xad\x8b\xc4'\xcaH\xdf\xed\xb7U\xfbUO\xaa\x0d\xab6\xa3\x97\x921qK\x9d\xeb\x0di\xc7z\xf4\x93\x0d\xfa\x9c+)@\x8d\x1dp\x8a\xa5\xaa\x99\x7f\x9c\xc6\x1e\x9e\x91/+\xf8x\xde\xf5#hn\xd5\xbf\x90\x87\xebj \xcfe\xc5>\x93\xeb\xa1Ns\xfa\xc4'hM\xe4oM\xdd~\x9a\xd6u\xb2\xde\xf7\xf5\xf8p\xc9\xf4c\x1d\nx\xebv\xd0\xf8\x16-\x94l\xab\xbaa}/_\x07\xf1\xba\xac\xc2\x86\x8cU\xdd$\xf9\x16\xe2\x13i\x82y\xa4r*R\xfc\x8a\xcc%E\x1f\xd1\xe8\x87\xf2\xb2G\xab\x7f\xac\x87\xb1\xeb\xebu\xd5$\x9d\x84\xdc\x91jc\xebu\x94\xd1\xbc'\xfdP[ \xda\xfc\xd0\xe3\xba\x1a\xea\xb58 \xa8\xe7L(O\x19\xe0\xc5\xe71I\xe6\x1f=K#\xf8\xcf&\x01\xaa\xdd\xae\x9c8\xff\xce\xff\x155H\xed\xb0\x1f`]\xed\xb8\x99\xe5\xfbE\xf9\xe7~\xdf\xccW\xb4\xad \xa7\xe1\xaa\xa6\xde3\xc4\xb1%\x98\xfe\xb4\xbe\xab\xea\xf6\xc4N\xcc]7{\xce:\xd14\xca\x8b\xc0.\x8e\x99\xf0b\xc3t_\x0b/\x1d\x8d5T\xbb]S\xf3\x18\xfc\xd3\xc1,h\x18\xe9\x96m\xec\xabv\xe0vz[\xad\xefT\xd60\xbe\x8b\xac[\xeb\xc6eG7\x17I\xd2^\x90\x9e\x8fe\x065\xd50\xf2c\x03\xbb\xf6\\\xc3w=\xb9/\xa8\xe0w\xd5p\xb7P!\xad\x84\x04\x0e\xbf\xbd\x1c\xc8xiNo\xf98k\x08A\x98,\x92\x1e0\xfd\xc4\xa4\xd6\xedHn\x91T\x1e\x08\xa4\xda\x80\xb3\x1b \xd4\x15\xe0\xef\x8ei\xd0\xce\xab~\x1c\xc8\xf8#\xeb\x15s\xb8\xf9\xe9\xd3\xa5]\x85\xa0\x06YE\x8a\xe2\xa8(>\xb7\xb8\xa2\xd0)\xa8\xbcG\xffY\xa0\xb89/\xa6\x800\xb3\xee\x13SI\xb5\xdbA\xb7\x1fw\xfbQe/\x91s@\x91\xc0\x12^\x8a\xd6i\xb2\x94\x05dU\xbb]\x01)L_DVE\x01q\xe4\x9e\xfackR@\xd44~\xf3\xf2b\x98\xa7\x1d\xa3\x85&\xa9[\xde0\x98\x89\xcf*-\x826\xc3\x93\xd9\xf9\x17\x9f\x05\xdc&)\x81$$\xa5\xa1l\xc22\xfd\xab{\x97\xef\xb5-\xfe\xa5\xdd\x16\xad5\xdf\xb5\xe3\x7fjg\xf0I9\xff\x83E\x0d@\xc6\x0c\xea\x16\xfe\xf0\xe1\xdd\xdb\x95\xf6\xfe<\x1fv\xfbk+\x81\xcdk\xdc}\xa6\x9d~v\xb9\xefQ\xdb\x1e\xb0\xbe\xa1^\x02vN\xfe\xf1\xfdO/z2t\xfb^\x92\xeb\xb2}\xe0\xbe\xad\x7f\xdd\x93\xe6AlJnj\xd1{\xa3\xb8R\x00K\x18\x06\xb6K\xe8\xeb\xaa\xa9\xff\x1dK\x84\x06\xde\xd6\xb1[w\x0d\\\xefonH/\x93\xa7D\xca o\x0bl\xf7\xc3\xb4\x0d\x85j\x84\x86T\x03\x92\xd2\n,a\x81\xc0\x93\x17OXh\xbcZ\x8f\xa4_\xb1\x1d8;\x01\x19\xc8\xedV\xa1\x07\xf9\xf8\xfe\xa7\xa7\x03\xec\xaa\xf1\x8e\x15\x80\x8a\x9b\xa2*xiT\xcc\xcd\xbei\x1e\xe0\xd7}\xd50~Z\xdeg\xa2\x08\xd6;\xcf*\x96\xbb\x8d\n\xb8\xa2\xc5\xbf\xb8\xed\xba\xdb\x86\xacX_\\\xefoV\xaf\x05\xac\xf2\xea9\xaf=\x139\xb3\xb3\xd2N@\xa5\xad\xab\xb6k\xe9\xee\x82\x19\x02\xbc\xc4gdu\xbb:\xa1]\xc8\xfc\xcf'\xab'\xf2DY2\x8b<\xc7\x89)\x00\xceZ\xd8\xb1h\xfd\x9a\x9c\xc0H\xaa\xed\x00\xfba\xcf(by\xca\xd0\xaen\x08\xa7\xd1a\xfeo\xdd\xb2\xb4\x8a\x89\xe8Z\x7fF\xc6\x8b\xcd\x94k\xbc#\x0fx\x91\xfc\xb6\x02\xa8Y\x88\x82\xf3\xe8K\xbf|$_\xd8P\x9e\xb6\x0f+\xf8\xb1\xfbL\xeeI\xcf\xf9\xf6?\xbe\xff\xc9\x9e\xbb\xf4\xe1\xa1 *\x06\xcd\x93\xa7\xcf\xb0\xbe#[\x02Ww\xe3\xb8\xbb:\xe1\xff\x1d\xaeNx\xb6\x8d\xf8\xf5\x84i\xd9Z\xd9\xab6\x0f,q\x06\x13\xb8\xdfA\xc5\xda\xea(\x8f\xf4\xf7D\x1c\x9eo\xab\xdd\xc0U\x86\xb6\x80]\xd0!R \x99\xbdb\x0e\xfc\x00\x15\xde\xb6\x9b\xaei\xba\xcf\xc3K\xc7\xd8\xfd=\x9c\xdd\xcc-\xa0C.Y\xcd\xa6F\x8a\x10\xdf~K6\xd8\xed\xaaL\xc8i\x0b?^\\\x9c\xc3\x0fo.\xa0k\xe54\xe2\x13\xf4\x81\x85\xb7p\xcd\xfc\x93\xa9\xe2\x17\x0f;\xf2\xe7?\xfd\x19}Y\xd0\xfd\xd3\xb1\x16:\xc4\xd756\n\xbb\xbe\xdb\xec\xd7\x84\xa5\x1f\xf5\xbd\x99e.\x9f\xbf\x87\xd3y[\xc4\xc9q*\xda?\xfc\xa8o]\xad\xa9M\xe8\xbaO\xfb\xdd\x94wy]\x0dd\x03\x9d\x0d\xb2\x00>\xd1\x1dU\xfd\xf8\xfe'V/\x96B7\xde\x91\xad2\x17\x04\xa7T%\x9b1\xa5\x1dU\xad\xc9\x94#\x1f^)6\xed{r\xd3\xf5\xe4D~LeVc}]7\xf5\xf8\xc0\xae4\x97g\\\xccD\xf5\xf7\x06Tf~\xbaV\x9c\xef\xb2\x0f\xd8\xbc[\xc1\xb3\x8f\x03\x91\xd1\x01\xda+T\xed\xa8\x9d\xe1zW\xb5\xd5\xad\xab\xc5\xd7=aQ\x0d)t\xf5\x1c\xd7\x96\xb7\xddH^2\xb6m\xb8\x11I\x83\x15\xab\xbb\xb0739\x8c\x92L\x8cCO\xe8\xd3\xb1,j;\x8f\x98?R\xb1\xa0'tu \"h=\xa5\xcbL\x18\xedy~]\x93\xdb\xbam];\x95\xcf\xf5x\xe70\xfa\x0f;\xb2\xe2\xfa\\\xed\xeaa\xb5\xee\xb6.\x8b\xf9\x81\xcd\xb6A\x84\x99\xc6\xbb\xaa5-\x0b<\x13\xb1E\x9e\x0c\xce\xa7\xe7sT\xd8\x96\xa5\xa8^;\x0c k \x0bPO\x01eq\xac\xc0\x99\xd3\xd70\x90m\xd5\x8e\xf5\xda\xa2\x9cD\xaf\xd7\xe0O\xc0\xa5\xf0n\xe7\xe2<\x8e\x9f\xa9\xe9\xb8&2\x98\xa68\x0c\x96o \x16\xd5\xea\xba\xbbw8\x1b\x92$\x9e\x8f\x91\xf1N\xa86W\xa7\xed\xc3\x95\x12\xedn\x95K}=\xb5\x126\xda\x12W5]{+\x8e\x0c\xec!\xa3V\x93\x19}^\xabk\xdb\x9dR\xcb\x94^\x11\xa2f\xe7R\xf1\x9b\xfa\x9aUU\xd8\xf5\x01\x86\xfdn\xd7\xf5l\xe5\xdcU\xebO/\xf6-\xfd\x0f]/\xf9x\xa3\x882\xe6\xd1\xa0\xceCw\x03\xfb\x91\x1b\x1f9\x9d\x07j\xf8\xe4\x11@\xd5\xc0-i\xd9\x1d\x02\x1bq|19\xd5\xa7\x88\xbd\xe3Cd\x97\xf3\xe6KE\x15\x18\xbe~ \xe7\x95\xc8Q\x16U\xaf\xa6\x05\xb1n\xe1\xd5?\xfc\x83c\x99\xfa\xbe\xeb\xe0\xa6\xeb\xe0[X\xadVh.=\xeb\x84\xaa}\xc0\x7f\xac\xda\x87\x15-\xfa\xfb\xbe\xdb>\xbb\xe9\xba\xe7\xf8k\xab\x15\xbe\xf6\xd47\xf0\x8c\x8a\xf8\xc8*}\xd1=\xfb;*\xe39\x0e\x01\xf0\xc8\xf9\x8b\xbbo\xbe \xf4\xcd\x1f\xaa\xfb*\xbbs\xe0[\xe6[Q\xe9\x19\xbdP\x0f\xcf\xbe\xef\xba\xd5\xba\xa9\x86\xc1\xd3 \xbcJ\xf4\x03\xde\x1e\xe5#\xbc\\\xa4w\xa6\xee\xf9\xc7@\xf7\x9c?\x8cw]\xeb\xe8 ^\x93\xef\xbb\xee\xd9j\xb5\xc2-\xf1\xd49\xcf\x9c\xbf3\x05b\xdd\x96\xdak\xf4\xe33\xdei\xaf\xdf|x\xf5\xfe\xec\xfc\xe2\xdd\xfb\xe7x\xbc\x8d\x17\xc5\x15\xcd]\x18/\xce\xdd]\xff\x14\xe8\xae\x1f:\xbc\xa7XW\xbd\xfc\x16\xfenw\xbd\xfa\xbe\xeb\xfe\xefj\xb5\xfa\x0b\xfeb\xd5>\x9cPw\x8d\xbe\xbd\xe3\x0e\xc8\xcfU?\xdcU\x0d\xedDw\xc5]\xddd\x96\xec(\xb6\xbe1\n\xfd\xd8n\xe7bY\xa5\x98b\xb3\xb7\xfe\xdb\xb7\xd0\xd6\x8dSA\xdduA4\xf1\x82\x11\xc4\xac?Mvp\xa2\x10\xbe~\x98]\x15i\xb19\x1e\xecA\xa64[\xd2\xf6\x03\xb2\xe6?E\xdc\x90\x17t/\xbab?PW\xee)T\xca\xaaBW\x1cq\x9c`\x97\xc0F\xdd.d2\xe3m\xf3 \xf7M\xd6\x86wr\x1d\xa1\xba\x19 \xf7f\xe8~\xdb\xae\xf2\x8b\xa7v\x11bC'\xab\xc8wpDh\xe6\x93\x9b\xae[]W=k\xdc\x97\x17\x0f\xab\x7f\x7f\xc2{\x8b\xef5\xf0m\x15\xab\xca\x13\xfa.]^\xac\x9f\xff\xf0\xe1\xdd[\xfb\xaf\xdf~\xfb\xed\xb7\xf88\xd2\xf7\xe78\x80\xc0:0\xd2L\xee0\xf0\xbd\xca~\x98\xee}\xba\xdd7Uo\xcb\xb2E\xf0\xfc\x8fy\x99?\x99\x11kb\xf6\x9d\x08\xff\x01\x89\x1e(\xcb.\xcf+\xb8\xfa_\xb4;\xae\xc4&wrc\xd4\xce]\xc9)\xff\xd2\xe1DW\xebOt\xce\xcf\x9b\xb5\x9b\xba!\xb8\xfd\x95\xf6\xe1\x9c\xf4C\xd7:\xa7\x8d\x88\xe00\x04\xd5%\x1b\x19\x17\xbem~\x99\x05n\xc5\xbb\xdf\xc4[\x7f\x00g-\x9e\xb0\xbey\xf2\x12\x9e`\xb3Fo\xee\x8a\xb7\xe8\xc9\x89K\x16k\xcb\xdbjK\xe5\xfdO^\xe5\xdf;_\xa6m1\xde\x8dm\xd0\xd9\x8d\xd8\x18\xe8:\xc1G\xb3\x1e\xe03i\x9a\xaf>\xb5\xddg\x9e\x84r\xc7\xd2)E\xfa\x08\xae\xe4\xba\n\x9e\x18t\x94\\/gp\xaa(\x96*Z{\x8b\xec\xeb\x99\xda\xd9\x85\\\xb1 !\xf5\x90_'\xac$\xb6\xb0\xe9dd\x8e\xd0\x9f\x85\xfa\xda\xf2X1\x93\xe6\xc23\x86s\x11]a\x85\x15d\xe4\xec\xcf\x7f\xfa\xf3s\x87\x92\xe7\xea\x88^\x90[MX7Pq_\xaf\xbe\xf9\xfa\x9b\xe1\x89c\xd8\xd5\x7fq<\x19\x1e)^F/ \x11j\x02S\xfbY`\xc7\xb4@\xf7\x0c\x1b\x13o\xb3\xa3#\x81\x1b4\x05J\xa4\x0c\x07\x06\xfe\xce\xfe\xc9\xac\xbd\x9e\xf7\xa1\xa1nLp\x0c<\xe3\x85\xbe\x98\xd0(\xe2\xffXLR\x17\xeb\xd9\x98\xba\xaeI\xf0c\x94\xdcojh%\xffk3n\xc9\xf5\x1e*,\n\xcb$\x1f\x9e\xcf\xb7\xf4tDd\x03\xda\x19\x88,2\xc6~{V\xb7\xebf\x05\x03in\xbe\x9a\xd3\x13\x8d\x11\x98\x13<\xb1l\xc8\xf8\xfa :k\x8a\x9eOk\x18\x16]\xfc\xb1\x1e\x86=\x8f\xe6U\xde3\x9b9\xad\xdfl\x80\x91`\x86\xff\xa4\xfeC;6R\xff>\x92~;\xb8\xb3\xc4\xe43.;\xf2\xb1\xd2\xda\xe6'\x10\x9e)\x99\xea6?v\xd2\xdb\xfc\xa4T\xc8\x11\x10Z\x9e\x12\x87\x8a\xebz+9n~\xac4\xb9\xf9IiIj\xea\xdc\xfc\xb8\x93\xe8\xe6'\xa5&\x8e>]\x98o\x87\xca2r\xf0\xe6\xc7\xca\xc6\x9b\x9f\x94&$d\xe8\xc9gZ/\xca\xf2W\x85z\xd6,V\xf6\xea \xddxN?\x9e\xb0\xf9tg\x10\x0c(\x97P\xce\x8f\xb9$\xdf\xee[\x04\x99i\x94m'SE5x)\xd5\x92^\xb0\xaf\xc9[\xba\xb5\xabg\x9b\xe2i\xee\xd8\xb1\xe3\x8d\x86\x8c\xc4\xd5\xe0\xb5\x81\x1a\x9d\x1f\xad\xc6k\x1b\xae6\xea\xb8\xaf\x19\xb2V\xc8>\xbb\xc1\xa9\xf3\x13\xeaULN\x02\xcc \x957\x1dy\xf8\xa1\xab\xf3\xe3i?\x043\xce0`\xeb\xfc\x04\x0c\x00Dv\x11\x88b\x16\xc0\xf9\x9c\xf20l\xe9\xfc\xe08\xd9\xf9)\xd8\xb0tt\xa0S\x94\x8d\x1a\x0c\xe3i\xe7\xc7\x8b\xac\x9d\x9f\xc2MO\xc6\x18:\xa5\x8d6_\x85\x0f\x81;?(\x16W\x91\x1bw\x18\xe6\xb2\xac\x10\xd9!K\x00\xbc\xb8AQA\xbd\xf2a\x9c\xe0\xa4\xb9\xb9\xc4`b\xfc\xc9\xf0\xdd\x11\xe9\xd6n\xef\xe9\xc0\xf6\x16\xec~\x8d\x8as\xc6\xb5t\x84-a\xe2-)\x08;,\xf2\xdc\xdc\xce\x1f_\x85\x7fQ\xb2\xf7\xad\x8c\xfe\x13\x18\xbb[\xbe[\x9e\xc0N|\x03\"\xd0<\"\xe9 \x97\xf8\x94\xf3 \xc8\xcd\x8a\x88\xbb\xd6=\x90/|T\xf8\xc8\xb1u\xafn\x87\x15|\x10\xd4I\x9a8\x99\x9a`f\x0fU\xb4\xef\xf84\x10\xe1SM\xec \xcfu`G\xf4]\xdf\x93\xf5\x08\xeb\xaaY\xef\x1b\x19y\xd4\x84\xdd\xec\xa9\x1b\xad\x17\xb0o\xe7n\x1fX\xfb\xbb\xfd\x08\xf5Hz\xbe\x90t\xf7l#1\xed\xac\xe0\x8fw\xa4\x15w\x8dT\xbd\xae\xf8\xea\xfeR/\x85\xa5\x06\xa8\x1dn\xf2\xeb\xac{\xb2\xa9'\xb8Y\xa5\x82\xe5>\xdfu\x83^\xce\xc4Q\xa6\x17\xa2\x0eC=gy\xb0\xc2\xe6\x91\xe4U\x9f\xab\xba\xa9\xa7p\xbd\xe9\xb9\x88\xb4\x05\xbd\x14\xad\xffW\xf0\x8b\x8a\x8a\xa3&\xf8\x9aLC@6,,\xcfT\x89\x077D\xf5t\x89\xdb}3\xd6\xbb\xa6\xe6\x95\xd0\xe5\xb3\x17q\xcd\xd6\x11&s\x18\x98\xe7s2ET\xba\xb8\xe56\x8bv\xa8\x02\xdf\x17\xa2X\"\xe8tn$\xe8\x81\xc6\xae\xe7\x0d\x90\x97G \x88\x8b\xe0\xba\xa7s{d*\xc8\xdd\xdc\x1d\xe9\x87z\x10\xd0\xb6\xab\xf6J\x08\xdbvs\xfe^O\xd6\xa4\x1d\x8d\x8a\xcb\x1f\x9f]\xb5W\x82\x96hb\xd77\x0b\xbc\xba\x9b>\xbd\x14w7^\xcd^\xdes\x0f\x1c'\xf1V\x0f\x97\xcf\x8d\xdaKmx\x0c\xa7YXD\xee@\x8b@\";_7]_\xba.\xceY\xdaf\x89f\x0e\x7f\x04)\x92.PVD.\xb13\xcd\xdf\xfc\x8eZ\x15\xd1\xbb\x05\xeaa1\x0c\xc8K7\xe7\x1a\xcd\xf7pv=\x90\x9aY\xe2\xa9\x83TS\xa0\x01\xe1z\xa2\xfc\xf2lGz\xd8Uu\xffb\xec\xebn\xda\xff\xdb\xfaR\xa0E\xb6P\xd9\x96\xb9W\xe7w\xa6\xc6\x8d\x9d\x9c#\xb2v\xb4\x85\x97\x06\xd65\xaca\xf3W\x9a\xf3D\xff\xcc\x82;k\x8eg\x9d\x81\xab\xd3\xb8\xd6\xed\xa5\xb1\xf3\x08\x94\x8bqF\"R&\x1f\xfd\xae\xaa\xdb\xaf>\xd7\x1b\"\x17z\xcb\x81a\xe7\x10\xaa\x87\xae\xfb\xaf\xdcu\x15\x0b\xe8\xbc\xea\xfc\xce\xea\x04qK\x86\xda\x01\x06A\x89m@|\xe6\xa1\xeb\x9ah\xe3\xd0v\xe3%7\xe7\x97f\x84\x16\xe9\xc5\xd87\xb5\xe6Mx^Z1\xda\xbdsR[_\xadY\x9b\xc4\x82B\xcd|\xdb\x8d_\x89\x7fr\xf4\xf2\xb0\xdf\xed\x9a\x07y\xf0H\x7f\x12\xe2L\xb5pt\x07c\x84,\xc2Kpi_\xab\x8c\xee=\xbd\x04\x05\x96\xe3\xea\xdc\xbe\xba7\xae\x01\xae\x03\x089\xfd!w\x18)!\x8aw\xc0'\xc8\xdc\xb6\x04\xe8\x11 \xbf\x0d\xcb\xb8\x13|\x82\xcc6\xe0g\x08)\x15_\xc6\xac`\x89\xb1\xda\x9f\xcd\xb0`\xc8\xab-\x88f>\xe9\x82)\xb0\xb5\x92\"\xd0\x8e\xb7\xb8\x18`\xe9$B\xaf\xe4\xf4\x8c\x9d\xeb*N\xe7'\xfe1Z\xcc\xd5`\xc8\xc9!l0D\xe1\xd7n\xc6q8\x80\xb7\xc1e\xb8\x1c\x14\x81Zrj&\xb5\x83\"I#yp5\xc8\xb1\xa4\xd0\"|\xac\xc4J\xfb\xde\x9f\xbf\x12\xb2x\x97G\xae_]?\xcbH\xbf\xbd\x03Y\xc9\xacE\xbc,8.\x14\xacI\x0eZG\xac|\xc1\x80Wx\xe5(\xb8\xfeyV\xc0\xa85\xb0Hk\x8a\xad\x84\x9e\xb5\xd0\xbd\x1a\xa65a\xf9\x8a\x18\xee\x8b\xbcU\xd1\x12\xc7y\x88\xac?g\xae\x8c\xb6\xbc\x1blmt\x0e\x05\xba>fL8\xc7\xc5\xd5\xde1u_Z\x9d\x11\xa4-\xb5^\x16]1\xddWU\xc7\xaf\x9a\xfe\xa6g\xad\x9c\x9a$\x85\x11 \xd3\x98\x02\xab\xa7\xb5~\x82\xd98l\x0d\x9a\x07\xb5i\x8c\x19?<\x9d\xaf\xe9\xb0-i\x81+\xa0E \xfax\xef\xf3\xf1\xb2\x8f\xec\xcb>\xf0\xf1\x0b\xfaoN\xb7Q\x15\xf0\x02\x93\xa0\\\xac\x1a\xed@~\x94\xb1\xc8\x1cOr\x0e\x0c?\xae#y\x10\x9a\x85B\xbe\x9e\xc3\xcf\x0b\xfax\x195/\xe4\xd79\x1c +\xc8\xcc\x1flx\xf9c\x0d\xb2\xfa\xc1\"> \x9e\x02\xd2\xb5\x8e\x04)\xfe\x04\xbc\xcb01P\xb8\xa3\xf9c\xd4Fv\xb6\xf8\xd7|\xd9\xc6\x1c\xee\x1f\xbb\xee\x93C\xd8\xae\xa9\xd6\x16\xd6\x13x>\xcd\xae!\xac\x1c\xd7\x89~B\x9b}\xa7\xfaF\xbb\x8d\x82e\xf3\xf6m\xfdeN\x88\x9a\xdb6\xbf\x8e\xb5B$\xfe\\:\xbcQ\xfe\x04[\x117.FY\xdaR/\xdcy\xf1J\xc3\xa1\xca\x9b}\xc3\xcfuqyb{\x01\xd5\x18hd\xc1\xc6y\xea>v\xb15\nw\x17b\xfd\xdf\xb4c\xafe\xad\xceC\xcc\xe7,\xc7\x94\xdb\xb2z\xd2\x90\xfb\xaa\x1d\xe9\nTm\xaa\xb1\xf2n\xc7\xe4\xc9Q\xc5/\xae\xc2\xcf\xc4\xc4K\xb1\xae1\xd2\x1a~\xd4\xcb]I\xe64\x0eu{\xdb(\xfb\xb0\xa7\xca\xad\x11\x9a0\xfa7c7\xc7\x0f\x97\x85\x04\xf5\xfc\x99v\x13\x9d\x12_u\xfd\x86P\xc7\xbeQN\xc2\x8e^\xe9\xd1+\xfd\xdb\xf0J\xcd\xf9\x93\xe1\x9ezE-\xf1S\xa7\x9c\xa4d\xe7t\x9a\xa8\xa6K\x1a5m\xfc|^NM\xf1\x9b\xde\x85L^3s\x97!/\xc4\xe3\xe5g\xf1r:dnglt\xf2wy\x97\xb9\xf0\x82T\x98\xb9\xcb\xcf\xdbU\x96\xb5\xab(g\x97\x97\xb1k\xcc\xe3\xeb*\xc9\xd6\x15\xe6\xeaZ\xcc\xd4U\x92\xa7k\x0c\xb3t\x95\xe4\xe8\n2t\x15\xe6\xe7\xf2\xb2s-\xe1\xe6\xf2\xf3p\x15`\xe1\x8a\xe2\xe0J\xe3\xdb\xcad\xdb*\xcb\xb5\xe5:/)\xca\xb3U\x9ee\xab(\xc7V\x1c\xc3VQ~-\x1f\xbbVan-\x17\xb3\xd6\x18\xcb\xab\xb5\x94U\x8b3h!\x02qN\xad\x0cF-\x07\x9f\x96w\x89\xf7ri\x85\xd7\xffr\x16-\x7f=\x8a2h\xf9\xf8\xb3\n\xb1g\xe5qg!3 [\xca\xcb\xf2f\x8d(kV.gV\x90\x14\xca\xc3\x97\x15\xc5\x96\x85\x93\xe7\xa41e\xe12,\xe2\x8cl\x8e\xac\xd8\xce\x88\xe1\xc7\xf2\xb7;\x8a\x1b+\x91\x19\xcb&\x12)\xc0\x8a\x15\xe4\xc4\xf23b\x85\xf8\xb0\x9c\xbd\x94\xc2\x85\x15\xc3\x84\x85\xf1`e\xb2`Er`-c\xc0rpN\xc5\xb0_\x15\xe4\xber\xd4\xc2\xd2\xb4,\xd6+\x8c\xe5\xaa \xc7\x15\xcep\x95\xc5o\x85\xf1Y\x95f\xb3\xf2rYa$?\x18\x8fU9\x16\xab\xa2\x1cV\xe5\x19\xac\xe2\xf9\xab\xa2\xd8\xabD\x1c#\x86\xbbJ\xbc\x1ad\xaeBy\xab\xf0\xd2c\xf9\x88\xc2\x9cU \x8cU\x91|UV3JsU\x95d\xaaBy\xaa\xca\xb2T\x95\xe5\xa8\xca\xd3\x87(~\xaa\x18v*uY\xc1\x98\xa9\xf8n\x06\xe3\xa5\xf2\xef\x12\nsR9\x19\xa90>*=C\xb2\x10\x1b\x95s[\x871Q\xc5\xf2PE\xb1P\xc5qP\x05\x19\xa8\x12\xf8\xa70\xf6)g\xfb\xb5\xde.\xc3<5\xe7\x98,\xbf\x85\xd3\xd2HSh\x0e\xe3\xd4\x8cOs\x1d\xbe\xba+\xa6\xfc#\x8fkjL?\x98p\xb2Ly\x83\x16Z\xf5\x0b1L\xb9\xf9\xa5\xe2\xab\x82\x86H\x963K\xe9\x18S\xf9\xb8x\xa5\x9c\xacR\xf1\xf5_\xca(\x15\xe6\x93\x8a\xaf\x03\xda\x87\xe5\x98\xa4\x1c\xce\xa8`#\xf1\xfc\xa7\xd8\x86frE\xcdM\x0c2E\xb9x\xa2\xcc\x0c\xad\x85,Q\x0b,k\x98\x1f*<\xed\xcarC%2C9\xdb\x0c\xc1\xbcC7\x7f\x90w:CT\xa7@q6(\x1f/\x92\x9f \xaaPs\x8aq@\xe9\x08\xfa4\x06\xa8(\xfe\xa7\x82\x0dNf~r\x9ca\x81\xde\xeeX\xde'/\xeb\x93\xb7\x99\xe1\xdcP\xad\x13\x96\x10;a$NA\n\xa7\x85np\x0ey\x13\xfb\xab!o\x16c\x9fX\x04\x88\x9b\xdc\x15-M\xdaT\x94\xb2 'l*H\xd7d\x935\x95\xa3jR\xb7cj %i\x9aP\x92\xa6\xeb\xb2\x14M\x08ASiz\xa64r&\x07\x82\xc5J\xcc\x8b\xc8\x0e\xb4?\xceJ L\x07\xac\xcc\xc4@\xb3\xc9\xe1\x9f\x95\xc5\xa9\xf8\xb3\x07\xbdv9\xb4\xf0,\xcc!t\xca9\xde\x06\x1a\x99S\x18\xca*,\x9dWX8\xb3\xf0x\x1b\xa8\xf6\x94\xcc2\x8c\xca3,\x9bi\x18\x91kX<\xdb\xf0x\x1b(\x7f\x92\xb2\x13\xb3\xf3\x13Kg(\x1eo\x03U\x9f\xb8\\\xc5\xc2\xd9\x8a\xc7\xdb@\x8f\xb7\x81\x1eo\x03=\xde\x06\x1a\xcel\x8cH\xe7;\xde\x06\x1a\xd391\xd9\x8e\xe1^\x88\xcaxL\xcey<\xde\x06*\x9e\x98\x1c\xc8\xe3m\xa0\xf9\x19\x91\xc7\xdb@\x93\xb3%\xed*\x1fo\x03-\x95Iy\x88\\\xca\x94l\xca\xc8|\xca\xa4\x8c\xca\xf8\x9c\xca\xe3m\xa0K2,\xcb\xe6X\x1eo\x03\xf5e[\xc6\xe5[\x1eo\x03]\x98\x7f\xe9\xdd\x98\x1eo\x035\xa5\x17\xcf\xc9\xb4\x13(\x97\xd6\xc7K*\x96\x9f\x9b \x8e\xecLp\xe7gZ\xa9e\x8524\x17\x1f\xf9\x1co\x03\x8d\xcc\xd9D\xc5\x1do\x03-\x9a\xc3y\xbc\x0dt~\ngu&\xe4u\x1eo\x03U\x9eBy\x9e\x8b\xeds8\xdb3\xdc\xab\x98\x9c\xbc\x8c\xcf\xe4\x9c\xcf@\xd6g(\xef\xf3x\x1bhn\xc3\x8ae\x82\xe6\xe5\x82Ff\x83\x16ozrN\xa8S\xdax\xbc\x0d\x94=\xc7\xdb@\xfd\x15.\x9dXZ8\xb5\xf4x\x1bhB\x9a\xe9\x7f\xbd\xdb@\xc1\xd4\xee9QS\xb3\x9f\xf3\x9f\x8f\x17\x02\xc8\xe7H\xbdj\xd6\xe1o\x88z\xd5\x93\xd4\x1cG\xb8\x8a\x08H\xc8\xa9\xd6\xaf\xadMN\xa7\xbe\xab\x87\xd11\xc3\xe8O\xda\xdcRn\x0fe3\x97\xa7J\xf2+{\xc5\x8er\xd1$\xe3W\x03c\xda\x94\x04s\x13\xd9c\xb6\xab$\xb4\xec\xba\x1a\xea5\xbfT\x98\xd5\xdf~\xcf\xb7\xe1\xf0o7\x98T\xdc3\x0d\xfa\xa5\x9e\xf9\xc1\x9fj\xb7;\x8c\xe8\x90\xfb\x04\xf0J\xa6\x8d\xc3\xba\xda\xf1+p\xb8\xcb'\xff\xdc\xef\x1bqC\xee\xae\xef\xd6d\x18\xf8\xc6\x8e\xf5\x07\"O\xd8d\xf63\xbb\x97\xf5\x04;\x15\xa9\xdbu\xb3\xe7\xc7<\xd4TM/S_\xb6\xa2\xed\xdd\xafy]\x84##j\xe1\xcc\xfb\xae\xe6\xe4\xcb\xa7\xc85O\xfc\xac\x80\xc0\xd8W\xed\xc0O\x81\xb6\xd5\xfa\xaen\xad\x9c-V\x8b\xcb\xda:\x13\xf1\x0e\x85\xeb\xc6\x03\xef\xe8\xf9\x02A\x07\x00\x8a\xb13N\xd6\xd1x\xeb\xf8\x0c\xda\xf5\xe4\xfe@\x13\xe8\xae\x1a\xee2\x95\xdc\x91\xf5\xb7\xab\xfa\xf1r \xe3%ff\xe4\x13\x885\x84j\x0f\xf8\x92\xa8\xfd\xccJ0\xefu6\x9f\xf0\x05\x17\xbe\xae\x82\x98\xee\x82p\x97M\x03~^\xf5\xe3@\xc6\x1fY\xcfa*\xc3\xf6~\xe3%^\xa5(\x8dD\xab \x8a\xa7b\xf9\xbc\xe6JG\xa7\xbf\xf1.\xfdS\xe1\xe2g\x0f\xb5\xb0`\xb3]\xec`\x939\xfe\xbb\x1dt\xfbq\xb7\x1f\xe7\xbf\xcds\xcd\x90\xc2\x9c\xc5\x83\xd5q\xc6 \x95\x95[\xedv\x85%2\xfd\x13\xfe`a\xd1\x84\xee\xb6\xda5),v\x1a\xffy\xf9D\xcc(\xb54\xdd@\x96\xde\xa6\x1c\xb7\x05\xe13Z\xf3\xee\xa6e\x95\xef\xc8\xf8\x8c\xe36S]\x06\xef\xabf (A\x85}\x91\x10z\x8d\x90\xc7\xd4\xfa\x8cl\x08\x8f\x07\xa1\x8e\x89qw\xcaa\xf3 \x11\x9f\x07\x11\x18=\x08/T\xa1ej\xf4\xe0\xf5 \xdc\x83\x10\xd9\x8b\x90\x81\xdds\n\x1c\xb5$tG\x0e\xe8B\x0c\x9fS\x98\x07\xdb\x07K\xf1}Ni\xacR\x01\x8c\x1f@\x01\x9c\x1f,\xc7\xfa9\xe5UQx?\xc8\xc3\xfc\xc1R\xdc\x9f\xbb\xdaM\xc3\xfa+\x88\xfd\x83\x85\xf8?\xa70\x86\xfa\x89\xc0\x00B\x06\x0e\xd0-\x90\x8c!, \x94\xc4\x03B\x18\x13\x08\xa5p\x81\x90\x85\x0d\x84t| \x94\xc0\x08B\x06N\xd0c\x9f\x1c[b\xf9\x14\xc6\x0b\xc2\x810\x83P\x1e7\x08 \xd8AX\x8c\x1f\xf4\xdap?\x86\x10\x16\xe1\x08\x9d\xa2&|\xa1\x07K\x08\xc2\x05\x88\xc4\x13BiL!\x04q\x85\x90\x8b-\x04\x1f\xbe\x10\xe2\\\xa0\xe0\xf69\xdeK*\x897\x84 \xe6\x10\"k\xb6\x0c{\x88\x8a\x92k\x84\x1f\x7f\x08%1\x88\x90\x87CD\xe5\x89\x85\xde\xed\xd8d\xe0\x11Qy\xb4>\xa3\x03\x93\x08Ep\x89\x10\x07\xbf\x83\x10>\x11\xe21\x8a\xe0\x03.-\xc0*B@\x9e\x03\x97Q\x00\xb7\x08\xc9\x9d\x17\x87_\x84\xc8\x1e\x8a\xc41\xc2\x12,#\xb8{\xae\x0c\xa6\x11\xe2p\x8d\x10\x81m\x84(|#\x84{5\x0d\xe7\x08\xd1XGp\xe2\x1d\xa1\x04\xe6\x11Rp\x8f\x90\x81}\x84@\x17Fb \xa14\x0e\x12B\xf5rh\xf2\"L$*\xc9s\x7f\x04\x7f\x8ab#\xc1\x83\x8f\x84\x0c\x8c$*\xccu\xcb\x04\x7f2\xb0\x92\xa8<\xf7V4\x80\xa1\x04'\x8e\x12\x9cXJ(\x8c\xa7\x84,L%*\x0e\xc3Y\xc2R\xac%*\xc9\x8f\xbf\x84D\x0c&\xc4\xe30!\x15\x8b IxL\xf0\xac`\x1e\xcc\x1d$\xe0\xeeb\xb0\x99\x90\x8a\xcf\x84\x14\x8c&\xb8\x1b\x99\x81\xd5L\x9c\\\x19\x18N\\\xb7]8NX\x8c\xe5\xc4E\xb9\xf0\x9dp\x00\x8c'\x14\xd2\xb9H\xac'D\xe3=\xc1\xc2|\x82\x13\xf7 \xd3\xde\x18\xc7~B\xe4\xder1\x06\x14\x95&S\x16\xdd8PpbA\xc1\xacq9<(\x84\x03 8.\x14\x92\xb0\xa1\xd8\xdb\x0e|\xa8\xebU\x0c#j\xbf\xeb\x14\x9a\x84\x15\x05p\xe1E!\xdc_\xdaH\x95\xc2\x8d\xc2\x9c\x11\xec\xc4\x8eBb\xdd\x1cz\x9f\x8c#E\xa5\xa8i\xeb.,)\x185\nUX\xf9G.\xa6\x14\xf2\x8f(=\xf8R\x08\x8f\x04\x98\x8d+\x863\x85\x00\xd6\x14\x16T\xce\x13\x18\\\x82;u\n\x93w\x88x\xb0\xa7\xe0\xc7\x9f\xc2\x82\xd6-\xc7\xa1B$\x16\x15\x16\xd4\xca\xd3\xe7%q\xa9\xe0\xc3\xa6\x02\xab\x94\x13\x9f\n\x0b\x9a\xb5\x00\xa7\n\x11XU\x08W%\x94\xb3\x16\xd3\xfb\x19\xd8UT\x9e\x8e\xe5\x08\xe3W!\x88a\x85\xf8\x8e\xc8F\\\x95\xc3\xb4B*\xae\x15\xbc\xd8V0[\x90\x83o\x85\xfc\xb5\"\x06\xeb\n\x91\xbd\x0e\x19\x98W\xa7\xc0k\x92\x8e{\x85p\xbf@D\xdf@\x00\x03\x0ba\x85\x96Ol\xf7\xc1\"<\xacW\\\x04>\x14\"p\xb1p\x98\xc6\x16\xc4\xc8B6N\x16\xe2\xb1\xb2p\xb8\xeeH\xc3\xcdz\xc5\x99iv)\xd8Y\x08\xe3g!\xae\x13b\xac:$tT1<-\xb80\xb5\x10\x87\xab\x85p\xfbc\xdaT\x14c\x0bq8[\x88\xc1\xdaBD\x03r1\xb7\x96@4\x8bk1\x0e\xd7\x92$q\xb9N,.\x94\xc6\xe3\x82\x03\x93\x0b\x85q\xb90o\x91\x11l.X>]\x12>\xd7\x92\xc5\xf0\xba.\x8c.\x14\xc0\xe9b\x8a\x81cu!\x17\xafkI\xc3\xf0\xbb\x10\x83\xe1\xc5g\x8b\x07f\xe8\x048\nh\xa3\xfe\x95\x90\xf7\xfe\xfc\x95\xac[\x1c\xc4\xf1\x9c\xba\x90\xe97\xc50\xcfSq\x8e\xb4\xd6\xf1\x1fE\x0c\x99\xe5\x80\xb2\xf3@\xe9\xab\xf2\xb5\xa7\x1e`\xdbm\xf6\xcd2\xec\xf0\xa2\xdb\x0e\xb5J\x1a\x1b\x01aU\xf9\xa6@\x04\x81Y\xde \xe6\xca\xd3\xb5xF\x89`\xa5cX$\xf7^N\x0f\xaah\xc2e\xc5\xe4\x12?\xc3u\xe7w\xcc\xaa\x91v\xec-\xd75\xbb^\x96\x85W\x8aRj \xf2O\xec\xc0\xb2f\x16~\xeaD\x15\xd6oe\xef\xf6D\xf9\xf5\xd9\x8e\xf4\xb0\xab\xea\xfe\xc5\xd8\xd7\x9d\x16S\x99\xa1\xb2\x07j\xa9]\x80l\xdf\xdc\xfb\n^W\xbe3v\xb0#\xfdP\x0fZP\x82\xb6\xfcrC\xdan\xbbDK\xe7\xaf5\x87\x8f\xfe\x99\x05\xd8\xa8\x91\x04\xf6\xbb@\xe6k\xbaP\xb7\x97\xc6\xae+\xb2\x0e.\xec8\"q\xda\x8f\xdcUu\xfb\xd5\xe7zC\xa6[\xe3Lgk\xbc\xb3\xc0\x00\x95\xe1\x97s\x97\\,\xdc\xf3\xea\xc6\xbe\xd2:\x061^!\x83\xc9\xdfN\x80\x7f\x9fw]\x93n\x19\xbb\xaeq\xd9\xc5\xaektB\x05\xfa\x87\xba\xbd\xe9\x16\xd9\xc0\xb6\x1b/\xf9*u\x99pqw\xca\x17H\x8f+]\x12\xeco\xda\xba\xf8\xde~\xafL\xff\xf4\x05I5\x1e\x97\xb2VJ\xfb\xb8\x88\xb2\xd7\x98\xa9e\x9a\x1b\x00O\x90\xc1\x17\\\x98\x0f2\x0e\x0b\xc5\xb2\xca\x91\xd3\xf8\x9a\xac\xef\xfe\xf1\x9b\xaf$\x9eJGe\xf9E\xd9\x1e\xf54\xb7/\x87~}\xe0\x16\xa1e\xd9\xc7\x8d\xda\"\xc3\xe1S\xa88\x89/\x93=\xe0k\xdcf\x18\x1f\xadqJY\xfe\xc69\xc6j\x18%\x85K\xb8\x85\xe8\xea\xca\x1fl:\xcd\x0f\x8aMT?\xcc\x02\x83\xf3X_\xd7z\xa2\xea\xfc\x89\x08@\x84#\xec\xfc\x89\x19$\xfe\x18\xb5\x03\x9d\xe5\x83\xffM\xf2B\xfaB4\x9a\xa2\x8e]\xf7 vM\xb5F\xb3\xce\x81GWw\x0da\x05\xfbb2\x89\x9d\x12\x8a\xcb$u\x8c^C\xad_\xf6m\xfde\x0e\xb6\x07\xd5\xd8\x14\xe8\xea\x14\x11U\xbe\xbc\xae\x9a\xaa]\xe7vJ|S\x8dr\xd1H\xb7\xfc\xed\xf3\x1d\xf15R\xb7Wc\xd5\x8fXD\x8a?|oJM\xc4\xa3\xb5t.RZ\xa3y\x03\xaf\x18\x9b\xaf&\x1b\x15\x94\xc5'\x10\xdfF\xab\x8dw\x1d\xf4\xc5\xd4Tu-\xde\xb4c\xaf\x1c\xfc\xea\x1d\xcc\xed\x92;k\x10\x18V\xe8\xbejG\xea\xd4T\x9bj\xac\xb0zi\xb5\x92\xdb\x83\xaa\xe7!H\xad@\xf1\xa3)$\xd4,\xb5AzNbS\x0f#\x07\x99\xef\xaa~\xac\xd7\xfb\xa6R\x1ci\x04L=W\xa7\xbde\xce!\x12\xdc`\xc4 \x9aD\x01<\xd6\x8e\xbf\x8c\"\xa7\xc1\xb7\xc49Nh\x1c\xcb\x8d{\xa9q,3\xde%&p\xa3\xbd\xeaE\xd2\xfa8\xb2\x11\x8e\xab\xd8q\x15;\xaeb\xc7U\xcc)\xeb?\xc3*\xe6\xd57\xef\x08\x84\xdbc\xb5E\x0d*\x90_\xf7\xf5}\xd5\x90v\xe4\xcb\x8a\xf52\"\x90|Y\x93\xdd\xc8S\xa4k\x94\xdeaF\\NZk\xe43\x8b\xa1\xe3v\x8d\xc5\xa7\x91s\x14\x80a_\x8f,\xf8\xc6\x12\x0f\x9a\xdaA'1\x05!\xd4\xbe\xf5\xf5\x8c\xda\xcc\xd8\xee0\xda\xad/\xefbA\xd5+7\xf1r\xa4\xf4\x82\xd6f]\x1c\xd2~\xab\xe5G\xc2\xd1#\xe1\xe8o\x95pT?\x8aC\x83\x90\xa1@\xa7\xf6\x91\x90\x96|\x10\xf7Q\x9e\x91\xbc\xb6\x8c@t\x10\x94\x9f\xb3\x98A\xcf\xa8\xb9\x11\x08<:\xf5A\xeb\xcbeQE<\x828\x87\xbc2*d \x89\xab\x10\xbaA\xf1\x9c5\x1d\x96\x9d+b\xa7\x10\xf0\x87B\xbb\x83\xf0zm\xef\x08j|/\xa0\x9c\xf5Qw\x1f\x15\xe5\xd8\x02D8\xff\x91\xed\x8cN\x125\xbd\xf9\x1as\xe4\xe7\x16\xf9|\xf5\x08/\xbd@ \xd8\xe7\x93\x0bl\x86x\xa5\xe1l \x9b}\xc3S>0i=Y\x93\xfa\x9e@5z\x9bV\xa8I\x9e:\x8f]\\]B]\x84\x18Q\xc3SV\xb2\x94\xbd~r\xc0C\xf6Fx\xd0\xe3n+\xd2\xe3\xf6\"\x90V\xc00v,\xab\xa9ixtg\xa8\xdb\xdb\x86\xa8\x91\x1d\xa5X\x1e\xc6\x99\x05R-\x9e>Q\xd2zZ\xda\x1fT\xcf\xbf\xea\xfa\x0d\xe9\xc9\x86\x85\x8fx\x15\xf1\xea\xa9\xac\xdc\x86\x9b\xea[!\x91\x16 \x81\xf1\xe7\x83S\xa2\xd7k{\x85\x8e^!\x95\xf0\xce#\x1d\x12\xce%\x16Z\x0e\x02+5\x7fR\xe6\xe5o\xe4\x88\xf0\xb0\xadY\xe6 \xf8E\xd9\xad)\x03r\xd30l\xaa\x87\xc4\x0c\x01\xffQ\x98K+\x8a\x12\xea\x0b\xc5\xa4LH\xe29\xa7D\xe4br\xa3|G\x1a\x16\xbf\xa8Z\xa8\xd6\xcc\xf9\xb6\x1b|F\x1d\x01{\xf7\xdb}ny\xec\xa3k\x15\x1b%\x08\xb6\x18\xc5\\\xb7\xae\xab)\x9d\x90\xf9\\J^\x9e-\xef\x86J\xb2\xcbq\x0c\x85c\xc1Z<\xe1\xac\xe4\x1d\xfex\xc7\x94G\x90\x12?\x0b\x8d\xde+\x9e\xee#\xa3>l\xa0\xe6\x8c\xcc9\x0b\x88u4\x1d\xb7-\x1f6\xbb\xe7\xde\xbe\xbbx\xf3\x92\x914\x88P\x17g;\xa8\xd9\x12y\xd6J\x8fn\"\x01\x13I>8\x8c\x9d\x1bp\xbb\x90 AG\xf5\xf5\xd7}\xdds\xa5\xb8\xedn;\x86\xb8\x8e\x8d\x8c\xe0\x0b\x8e\x1e\x17y\xed\x8a\x89h\x92\xe6 \xd0\xef0\x8dI\x8e\x85\xc8\xf8\x87*\xeb\x18\xfe8\x86?\xd4\xbf\xff\xd6\xc2\x1f\xa2\xa2\xbaO\xe7\xf3\xae\x9c\xde\x9d*\xe0\x05&Aq\xefb\xbd\xbbd\x97nZ\x03\x16\xc5=\xfc\xd4\xd7qQ\x06k\xc8\x17\x12]\xcf\xa4\xd6\x86\xbc\x10\xc5\xb5\x9f\xdc\xda\xb9\xec\xb9\x17\xbd\xd1Ie\xed]\xf3B\xcb\xd7r\xe2j\x873\xe6\xa7\xac^BV\xed&\xa5^DG\xcd\xc4#\xc2\xbcD\xd4c\x1e\x05\xf5B\xf2i\x94\x14!L;\xbd\x98pz\x11\xd54\xdd\x00c=\x16&\x99^B/\xed\"}\x0d\x12K/\xa5\x94\xa6V\x1c\x11\xe7%\x93^B#\xed\xa7\x8b.@\x14\x1dE\x11\x9dF\x05\x9dI\x02\xbd\x94\xfe\x19\xd0\xd3{\xd7\xde\xb0(\xe5sy\xb2\xe7\xa24\xcfq\x04\xcf\xcb\xa8\x9d\x1d\x1d\xec#uN\xa7s\x9ei\x9b\xb19\xee r\x1ec)\x9c\x97\x927s\x92fD N\xdb\x9cA\xd8\xec\xa0j\xf6.\xf1^z\xe6\xf0\xfa_\x8e\x92\xd9G\xc6\xec\xaf\xc72\x02fiI\x0da>\xea\xe5B\xa4\xcb\x19t\xcb\xf8L\xc2\x96\xf2\x0c\x8aeZ\x9e!mD\xc9\x95si\x95\x83\x9c\xc0\x1e*\xe5(\x12e\x9c\xeb4\x8d8\x19\x97aq f\xd3$\xc7vF\x0c5\xb2\xbf\xddQt\xc8\x89D\xc86\xb7b\x01\xf2\xe3 \xed\xb1\x9f\xf08Du\xec\xec\xa5\x14z\xe3\x18bc\x8c\xd28\x93\xcc8\x92\xc6x\x19\x81\xb1\x83\"8\x86\xb4\xb8 ]\xb1\xa3\x16\x96\xa6-\"'\x06\x0f\x11qA\nb\x9c|x)\xed\xb0\x93b8\x83\\\x18\xdd\x86x)\x841~S\x8c6\xb8\x1ca\xf0r\xaa`\x84\x16x\x11!\xb0\x97\xfc7\x9e\xf67\x8a\xf0W\xc41b\xa8~\xc5\xabA\x92_\x94\xf9\x16/=\x96^5L\xe9\x9b@\xe6\x1bI\xe3k5#\x83\xba\x17U\xe3\x0c\x82^+\xda\x82R\xf3.#\xe5u\x11\xf0\x96\xa5\xde\xcd\xd3\x87(\xba\xdd\x18\xa2]uY\xc1\xc8u\xf9n\x06\xa3\xd5\xf5\xef\x12\x16S\xe9\xe2\xcc!N\x12]\x8c>W?\x0d.D\x9c\xeb\xdc\xd6ad\xb9\xb14\xb9Q\x04\xb9q\xd4\xb8AR\xdc\x04:\xdc(t\xfb,W\xe9\xed2\xe4\xb7s\xee\x04\x96\x11\x10W\x13K#M\xa1\xf3\xa9\x82\x83\xe4V=[p\xc9rf\xa7\xbb+\xa6\xfcC;\xd8P\xff\x1eEi;\xa6\x1fL8 l\xbdA\x0b\xad\xfa\x85Hk\xddt\xb5\xf1UAC$K\xc8i'\x12ZD\x9e\x8b\x96\xd6IH\x1b_\xff\xa5$\xb4a\xfa\xd9\xf8:\xa0}X\x8el\xd6A3\xeb$\x98\x8d\xafx2\xa9\xac\x9fN\xd6Yp$\xe9\x8d\xd5\x8f\x19\xb4\xb1,\xdck\x883\x17M'a\xec\"j'\x7f\xe6olC3)a54\xac\x9f\x0c\xd6E\x03\xab\xd5t9\x01\xec\x02\xcb\x1a\xa6{\x0dO\xbb\xa5\x14\xafp\x8dEx\x13\xc9]\x9dm\x86 \x1e\xd7M\xe7\xe9\x9d\xce\x10\xd5)\xb0\x88\xb8\x95qp:\xa4\xf9\x989\xfdd\xad\x85\x9aS\x8c\x9aU'\x7fJ#e\x8d\xa2c-\xd8\xe04\xf2U_\x02\xa9\xd6\xeeX\xdaU/\xe1\xaa\xb7\x99aT\x84\xd6 K8T1\xae\xd4 K\xeaB78\x87\x13\x95\xfd\xd5\x90\xa7\x82s\xcd}n\x80\x07\xd5]\xd1\\\xeeS0\x12\x0b\x16\xb3\x9cN\xac\xa6\x8a0\x9c\xdf\xb4 \xb3\xa9\xcdiZ\x8e\xcdT\xdd\x8e\xa9%d0\x98r\xc6RE\x16\xca]z\x9d\xc9Zj\x98\x03\x84\xaf4\x8b\xa9\x14a&\x0dr\x92\x8aL@<\x01/\x84\xe9\x98U<=\xc9\x0f\xc1\x83\xa4c9f\xbf\xedq\xa1\x1cn\xac\x82\xc7\x0c\x87\xd6\x99Bh\x0b\x07\xd2\"\x88\xb2\xc8\xa8y!d\xc5o\x97\x14&\x02\x7f\x19\xe1h\x840\x98\xe1\x8e\xe6OA\x1c\xa6\x13\x89\x19\x85\xc5Lh\xb3\x9f\x80\xc5\xdc\xf3\x94BdFa2#Z\x117.e\x91\x99\xd1\xd8\xcc\x1c\x9a\x0e0\x1bW\x00\xa1\x19\xd3]\xe5P\x9aA\x9c\xa6Q\x9deHM\x7f\x9br\xd1\x9a\x9a\xb0k\x9b\x80k x\xf3\x8899bN~S\x98\x13|\xfc\xe2\x9d\xc2D\x08\n&j \xd48\xdd)\x9d&\xe8\x81}Q?n%\xcb\xa1[\x88^q\xca\xf9\x1fA\xfcJ\x08\xc1\xe2u\xdf|\xce\xdb\xe8\xc4\xb1\x04\x97\xc6P/Ay4K\x08\xcfR\x1a\xd1R\x18\xd3\x12@\xb5d\xe3Z\xca\"[b\xb0-\x19\xe8\x96\xb2\xf8\x16\xae\xad\x01\x84KY\x8cK\x04\xca\xa58\xce%\x80tY\x86uA\x05y\xf1/E\x100\x91\x18\x18\xf4\xcb$\\L62\xa646\xc6\x8d\x8e)\x8c\x8f9\x04B\xa60F&\x16%S\x18'\xe3G\xca\x14\xc7\xca\xb8\xd12\xdcpE\xe1e\x96#fPa\x0cE\xe3\xc0\xccd\xa1f\x9c\xb8\x99\xa0K\xe1\xc5\xce\xc4y\x1c\xe5\xf03~\x04M\xb86EQ4~\x1cM1$M.\x96\xc6\x12\xc7<\x1a\xd4y(\x8b\xa7\x11\xd3\x08iP.\xa6&\x02H\xe2\xc5\xd5D\"k\x9c\xe9\xf9\x89\xe8\x1a\xb7\x1c$g9\x1bc\x93\xd2918\x9bp/Dam\x92\xd16xFw\x01\xc4M\x04\xe6&\x84\xba \xe3n\xbc\xbd\x96\x82\xbd\x89C\xdf\xe0\xf8\x9bl\x04N4\x06g)\n\xc7\xddMQH\x9c\xa2X\x1cO]\x10M\xccB\xe4X\xd2\x10\x84NQ\x8c\x8e\x0b\xa5\x93\x89\xd3\xb1\xabl\xe3v\xca#w\x02\xd8\x1d\x1c\xbd\x83\xe3wJ\"x\ncx\x0e\x81\xe2I\xc1\xf1D\"y\x92\xb0<\xf1h\x1e\x07\x9e\xc7\x85\xe0\x88\xc7p\x841=I\xa8\x9eh\\\x0f\xda\xa0\xd2\xd8\x9e\xb2\xe8\x1e\x07\xbe\xa74\xc2\xa74\xc6'_G\xa2p>qH\x1f\x1d\xeb\x83\xa3}\xe4\x1e\x0c\xc3\xfb\x84\xf74\x851?\x1e\xd4\x0f\x8e\xfb1jX\n\xf9\xe3\xdd\x98b\xe8\x9fx\xfcO$\x02(\x16\x03\x14\x81\x02\x82\x14\x1c\x10\x8e\x04\x8a?\x1d)\x83\x06\x82\x00\x1e(\xbe>\xde\xc4\xa1|T\x108pAz-|\x15T\xfe\x91\x87\x0dZ|\xe4\xe3D\x08\x05\xc33ZC\n\xa1\x84|8\xa1\xb4\n9\x02B\xcb\xd1B\xa8\xb8\xaew\xe2\x85<\x88\xa1\xb4\x96,E\x0d\xc5\xe0\x86\xd2j\xe2\xe8\xd3r\xe8!'~\x08\xdc\x08\xa2\xb4&$\xa3\x88B8\"o\xf1\xbe$\xb6P\xcf\x16\xc6\x13% \x8a\xfc\x98\xa2\xa8\x06\xbb2\xd8\xe2\x1b\x9d\x89-JB\x17\xb9\xf1Ev\xb6\xddB\x84\xd1b\xfb\x1c\xc6\x19\x85{\x15\x93\x93\x875JF\x1b\x052JC9\xa5>\xccQ\xd0\x00@d\x17Aq\xe4\x91\x1f{\x14B\x1f\x15mX1\x0cR\x1e\n)\x12\x87T\xbc\xe9\xc9h$\xa74}o\x11\x8dG\n \x92\x82\x0d\x0eYV\x88\xec\x90%p%\xdc\xa0\xd8\x10\xa6\x08\x10S\x96\xef\x9e\x03e\xb2\x84\x89\xb7\xdc`\xa6 \x9c\xc9_\xe1\xd2\x90\xa6\xc2\xa0&\x17\xac\xa9(\xb0 \x836\x95\x047\x81\x13\xdeT\x16\xe0\xe4\x808\x15\x079\xa10\xa7\xf2@\xa7\x08\xa8\x13\x98\xda='j*\x87\xd8M\xc3Z\xfa\xeb\x9e\xf4T\xd4\xfc\x8e\x14rL\xab>\xa6U\xff\x86\xd2\xaa\xcd\x8a:\xb2\x97\xa3\xa1|,IZ\xc8\n!\xfa\xd4[\x0fy\x97[\n\xec\xbc\x7f \xcb[F\x07\xca\xd8\xeb.\xc1\xc8\xd9\x98\xb8\x19\xb16\xf4\xeb%\x15\xd1\x14\x03\x95f\x07m\xf5+\x90y\xc2\xb0\xcc\x90V\xa4\x89\xef\xed\xaan\x86\xb1`U\x15i\x81\xaa\xe2\x17\xb9\xcb\x9a\x9b\xf5\xb5\xe0z\xe3\x01r\xdf\x03\x80\xbc\x88\xed\xfc\x92\xf8E\xd2\x95\xe8\xf6\xc5\xe7\x96<\x04{\x17@\xddE4li\x9c\xa2\xd0\xb5\xe6.\\X\x00\x88\x97\xe1B\xa7^R\xee\xaf?z5\xb9\xfbB\xf2\x8cz'_9n\\,n \xf4\\4\xee\xab\xcb\x92K\xc5'\xd0\x9d&\xc9\x06\xe0i\xe5\x9a\xd0;M\xb8\x06\xb9\xc3\xab\xab\xddz\xad\x9d\xd77\xf50r\x98\xdd\xae\xea\xc7\x9a\xba\x8d\xbd\x06\xb5\x9b\x8b\x9a\x86I\xc3\xd9\xb1\x13<\xedka\x9f\xb5\x90\x9b!~\x1a\"=J\x14\xb1X\xb2n\x8e^1\x9d\xc6\x0eU>\xdc\xc0\xe91\xbf<;\xa6\xdb-\xa7\xc5\xf2\xd6\xce\xb6RfTr\x811B\x8c\x8f\xd3\xec\x84\xd7\xcd,\xbbb\xda\x11\xcc\x82\x84\xab\xa0\xcd\xd4\\{\x01\xd7\xea\xf9\x84m)\xb4\x92\xb3\x8c\x82f\x08b\xa7C2\xceN\xad\xc6%\x99g\x14\xfa9.\x02B\xaeDp\xeb\x90J\xa9\x969\xf5\x0ci\x96\x03\xe1u\x1f\x82\x8d\x89\xa0N+61!\xe4\x138k\xeb\xef\xe0\xacYk\xc8B|\x01\x97'\xb0\xb0\xb6\xb9s\xda\x10\xe7\xf4\x00\xdc\xb5\xc8\x9a\xe8\x8a\x1cs\xed\x0f\x1b\xdc\xf0\"\xaf\xd9\x05\xb0\xee{\xac\x90\xca+\x17?\n\x81\xf5\x14y\x9a\x83<\xd9\x97=N&H\xb9\xe71\xc2\xd0e\xd9\xb8E\xd6mr\x82rn\xb7\xb3\x84,\xdcuCx\xe7\x1d_)k.-\xdf\x85\x1b\x82\\;[\x08\xef\xc6\xa1D\xf5sw\xe6\x10h\x03J\xa8\x83\xed\xd2\xc1E\xa6\xe3\xd8\xadC\xe8\xe07L\xa3\xe3\xd9OA\x04\x85Nh\xcf\x05YK0*\xce\xf6\x88\x95\xa2\xc2\x1c:\x91\x0d\xce=#[\xb6n\x07D\xd9\x0d\x8e\xa0\xdb 48\xa61\xa9k<*\xc4\xe7\xad\xcf\x8f{\xe7\x0fe\xda\xb2\xd4\x03p\xab6?\x00qE\x02 \xa2^\xa5|\x02\xfe\xe0\xb4<\x0b#\x03\xd6\xa7Ng&-B\xa0\x08Qc\x056\x1bO^\xa4@\x11de\x96\x9fr\xacrYC\xc5\xde\x84\x0bK\xa7\x08\x8d7\xd9\xbf\xb1\xd3\xca%f\xe3Q\x0e)\x93\xcdC\xc6\xe1\xa46\x03\x16\x9b\x01e\xda)\xe2\xec\x8a.\xa8\xe0\xe2\xe9\x8e7\xb3\xd8\x14\x8f8\x9a\x9cr\xe1\xa2u\xdeM\xc5\x19\xee(m$\x17\x12o\x1aD\x9b\x8a@\x07\xe5\xa6\x9bl\x13u\x1bp\xa7aD\xa95\x9d\xeb\x81\x7f1+H\xa4\xe9\xa6\xd0,G\x9eY\x8c6\xd3I\x989.\xa7\xca,E\x92\xe9\xa7\xc7\\D\x8c\xb9\x98\x12\x93\xb5\xd7\xdc\x19;\xc90\x17\xd3`\xa28!\x07\x01f\x0e\xf5%\xa3\xb94[\x83\xb0\xb3,\xa1\xbbtS[f\x92ZF\xd1Y\xc6SWf\x90Vf\xd0U\"\x06\xa3 )eY:\xcabD\x94a\n\xcab\xe4\x93.\xda\xc9\x1c\xc2I\x94\\r\x8c\xa1\x95\\J(\xe9$\x8f\\H\x1b\x89\x10F:\x17\xca8\xac\x86\xb5\x82.$\x86\x9cI \xb1\xfe\xfd]\xb8\xec<\x1aHN\xfb\xa8\x88\xb3 \x0bP?\xe6\x91>\x1aZn.\x86\x99D\x8f\xa2\xa3U\x899\x94\x8e^\xbeB\x07\x8dc\x90\xc0\xd1\xe6r\x8b'm\xb4\xbf\xfd\x0b\xd6\xd6E\x14\x8d1\x8d\x0d\xd12\xba\xdb\x16\xa4bL a\xd4\xf9\xaa2\x89\x17\xbd\x94\x8bn\xb2E\x1f\xcd\"\xda\x0b\xb1\xd4\x8a!RE\x93N1\x83H1\x82B1\x9d<\x11\xa1*\x0c\x11&\x16\xa2JDJ\xd64%\x8b\x18\xd1$B\xcc\xa1@D(\x0f\xb3\xc8\x0eMr\xc3\x92\xb4\x86NBC\x93\xe5\xcd$1,C_X\x8c\xb8\xb0,ea\x1cYa\x90\xa6P\xec\x92C\x04\x85\xe25/5\xa1\xc5\xe1g\x97\x16K2\xe7'\"\x8c\xa4 \x8c \x1f\xd4\xaa\\\x92p0\x8bj\xd0\xa6\x16,G*X\x8eNp\xf9\xe8\x06)\x04C\xe4\x81\xd2|\x9b\x84\x81\xdc\x077\xa9\x02\xdd\xbenAz@\x94\x18\xd0\xa4\x04\xd4jR\x82\x0c\x10\xddt\x98\x04\x801\xd4\x7fA\xd2\xbf0\xdd\x9f\x97\xe8/\x92\xe2\xcf$\xf7C\xdb\xa7\xf5b>\xa1\xdf|\xb8lR\xf9\x85K\xd7\xb4\xc9\x144G{\xd3\x88\xfb&9\xc3\\\xc5\xb9L\xbc\"\xca?\xb4 \xb3\xfa\xf7 M\xdf\x18\x1f,FI\xf9\x9c[`\xad\xaa\x05\x88\xf8p\n\xbe\xb8\xe2m\x88\xc9b\xc2=0\xf2D\x00\xa5\xdaCI\xf6\xe2\xea\xba\x84X\xcfO\xa9\x17W\xeeB\xbe<\x8c\x1b\x0fe\xc5\x8b\xabE\x12\x13\x9e\x9b\x03\x0f-,\xe2\xa0RS\x94\x82\\w\xe6\xca\x82\xb2\xdc\xb9\xf8\xed\xbc\x8d \x1cp:\x1a4&\xf2\xd8\xcd\x0dP\xc4y\x19\xec0\xee:\xf3\xe8u\x01k]\x82\xb9\xf2s\xd4\xf9\xedC\x06/\x1d\x0b/\x1a\xe2\xe2\x18\xe9\xd0\xb6\x817i\x17\xe7*sN5\x086\x1c\x8a\xb2\xcd\xb9h\xc7\xdc\x0cs\x99UO\xe7\x93C5[>iLrA\x0e\xb9\x02\x8dKf\x8cC\xc2\xfb\xa0\xb3\xe5\xc5p\xc59Y\xe2\x9cMJ\x00\xa4.!\x7f3\x89\xde\xbc\x14o\x89>]\x0e\xa1\x1b\xfb\xeb\xef\xd4\x86\xceY\xbe\xeaf\xcaC\xe2\x86W\xac$q[>e\x9b<\x92\x13\x02\xe7\xf3\xeb\\\x9a6\x83\x9a-\x93\x94m\xde\x17\x08y3\x1d[6\x11\x9b \x8e\x12\xe2f\n\xb6\x82\xe4k\x06\xedZI\xc2\xb5\x00\xd5\x9aH\xab\xa1\x95_U\xd7\xebzJ\xac9\xfd\xee\xd5\xd9\xcf0\x19o\xa8jM\xb5%-\xf0?\xc8\xe44\xad\xc2\x92wp\x1e}M\xa0LG\xfaD\x1e^\xf0\x00\xf4\xae\xaa\xfb\x81G \xa9\xad\x8a\xab\xb5\xbb\xce\xacj*\x0f\"\x0cM\xbdf>\x80ZkQ\xe2gZe\xb2\xadGf}\xf6,\xd2;t\xda2M\xbe\x90\xf5~\x0cd\xf5\xe9\x06AY\x9c\x86\xb1\xdf\xaf\xd9\xe6Y\xd4\x88\xf9\xbe-03@=\xc8/\xec[\xe3.\xc1\xa6\xbb\xf5\xdb\x1f\xd9\xb9\xd1\xa6\x07I\x8d\xd3\x14\xd8Pw\xeb\x0d\xbc\xc3\xf3TY!\x0c\x0d\x8d\xba\xb3'~\xa8\x86\xb3\xf6\xa6\x8b\xee\x87\xdbj\xb8\xfc\\\xb5\xa3\x1d;u\xec,\xf7\x9e}\xf2\x0f\xd5\xf0G&K:F\xd2\xff\xdc\xb7\xf5\xc8N\xc6?w\xfd'\xf8,rz\xf8j;~aG\xf4\xa4\xa7%\xac\x94j\xd1\x1dS\x99J}\x1c\xe6*\xcd\x8b\xed-\xf5k\xd7#\xcfQ[w\xadH\x8d\xc2\x04\xd0.\x9d\x1d\xeb/\xf3\x1c`BD\xee\x99w\\\xde3\xcf(zX6\xd5XE\xb6]K&q[\xdb\xd7\xd5X\xb1\x14\xb0\xf6\x81I\x87\x9e\x8c\xfb\xbe\x95Ql\x19\xf8g\xb1\x88v\xd3\x90^\x99\xe8p6\xc2\xcf\x1f?\\(\xe2\xf4\xfc\x99\x86\xb4\xb7\xe3\x1d\xeczrS\x7f\xe1I\xa9,+\x9d\xc1\x8f\x08\xdd\xb1\xd3)AK\xe5\x85q7\x83 7\x86N\x85\x0e\xda\xc1\xf2k\xb2\xeb\xc9\x9az0\"\xa5\x93\x1f\x9b\xb1\xdc\xce\xbai`\xd7\xed\xb8\x83s\x02\xd7\xfb\x91U\x84\xf4\xcc\xbf\x90h&E\x98\x98T\xbf\xd3\x9a\xb3\xaeX6\xe1\x08U3t\xfa \xdc\xcf\xc3\xadN\xb1\xf9\xf1\xfdORQC^\x881$\xd4\x18\xea\xf0\xe5\xee\x16\xea\x96\x8f%\xd5\xa7\xf0`\x08\xc1G\xf7\xa6\xa8{\xe3I\xce\xe2\xcfR\xff\x07\xc2\xa2\x0d\x17X}\xb8h\xfc\xc6\xab\x18\xdf\x8a-\xf1\xf3\xaaT\x0f3\x96\xe4\x13y\xf8j\xf68\xe8.\x7f\xe8\xd6u5\xefm0\x04.\xd3:\xa7\xb3dT\x81\xfb\x17\xcc\xd4\x0fP\xcdi\x9d\xb0!\xf7\xa4\xa1\xa3\xc8\x82c\xd58V\xeb;\xf5L\\\x99\x10\xba>\x1aY#\x12H\xf9\x1d\xb9\xad\xdb\xef\x9an\xfd\xe9d\xfa\xdb\x9bvc\xfc\xe5\xd5\x1dY\x7f\xba\xf8B\xd7[T\xcak\xd2\xd4\xf7\xa4\xbf\xf8bd\xe5\xfcT\x8d\x84n\x05\xfb\xaa\x1d*\x91\x04\xb6\xad\x1e\xe8\xc6J\x92I\xef\x07\x86N\xb9#\x03\x11\x13\xd3\xe1\x9a\xfd\xbe\xa8k\xa6\xc8\x92\x96T\xf9\x93\xcfz\xa8\x96\xf1\x11\x8c\x88\x0d&\xf0\xce\x9a\x90^\x17\x84\x14\xf8@\x05%a\x05\x05\x81\x05\x1ehA\x16\xb8\xa0\x1c\xbc \x040X\x081(\x0d2\xf0\xc0\x0cJ\x03\x0d\x9cP\x83l\xb0\x81%\xafbmE\xca)\n8\xc8\x86\x1c\x14\x07\x1dd\xc1\x0e\xca\x03\x0f\nB\x0fJ\x83\x0f\n\xc2\x0fb\x00\x08\x05!\x08n\x10B\x1e\x0c\xc1\x12\x86\xc1\x12\xf8\x92\x15\x04&\xe4B\x13,q6Ta1X\xc1\xe1\xc6z\x96b\x8f\xeb\x1aZ\xa5\x17\xc2\x16l\xc3%a\x0c\xbc\xff\x95\xdf}5(\x0c^\xc0\xe0\x0bE\x00\x0c\x85!\x0c6\x88!\x1b\xc6\xa0\xc9\x1a-HC\x1e\xa8!\x90\xe9\xef\x046D@\x1b\xf0\x8b\xea\xe3\xe1\x0d\xf8\xf7\x7f\xc1\xdb\xbe\x08\xe4\x10\xdb\xf8\x10\xd0\xc1\xdf\xd2 \xd8! \xee`'\xf7fB\x1e\x02\xa0\x07\x1f\xec\xc1\x0f|p\xf6J,\xf8!\x0c\x7f\xb0\x01\x10Y\x10\x88(\x10\xc4\x12\x18\x04\xde\x15A(D10\x84\xa3|C\x93\x8aB\"\x8a\x83\"\n\xc3\"\xca\x02#<\xd0\x08\x1b\x1ca\xc3#J\x01$\nB$J\x83$ba\x12\x11@\x89h\xa8D\x1cX\x02\x81K\xe0w\xf3\xc7&\xd5\xfb!\x13\xd1\xa0\x89(\xd8\x84U\xf9\x92\xd0\x89\xe2\xe0\x89\x92\xf0\x89\x92\x00\x8a\xbc\xf1\x0e\x82(\xc20\x8a\x19H\xe1;K\xd6\xa2\x86\xf6\x19\x89\x8c6\xce\xaf\xb0A\xa7\xf3\x8e\x9f \x9d\xb6\x0f\xfa\xb1Nl\xbe\x13?=Sxx\xb8i\x91\x91\xdb\xef\xd9&\x85\xe9\x93\x11\xec\xf5\x9e\xc9)\x87\xf9\xd1\x07s\xa3v$\x82l\x9c\xb0c\x90\xf1\x00aU\xe4\xa0\xc3\xb9\x8dK\xd9\xf4\xf9\xb2\x1f\x8a\x1do\xf3't\xc8\xed\xaaL\xc1$\x8c\xec\xf4\x0b\xa7r]|I\xbe\x13 #]]\\\x16HW\xf1\xeb\xa6[\x7f\x12\xb2\xa4\xd2~\xb9\xab\x86\xbb\x94sJ*H9\xf0\xa0f\xfan5\xf5\xd9\x86\x0c\xbb*\xc8\x1a%\xeaD\xd7\x0f\xf6\xfa\x945\xfe\xaa\xdb\x10E\x96)&&\xf9\xcb\xb6\x0f\xfct\x96\xca\x93\xf5\x8c8C\xd7\x84<\x15V\x86\x8d\xed\x89\xdc(<\x15/\xf7\xd5\xe7\xcb\xc4\xc3^m\xf6\xd0\x1e\xed\xf6\xe3n?E\xf9\x95\x13\xb1\xa7\x034\xdd\xed-\xe9\xe1\x19\xd5..\xf4\xf9\n~f\xc7L\x8a\x94\xb6k\xbf\xda\x90\x91\xf4\xdb\xba\xad\x87\xb1^+\xe7\xd0\x0768H\xb2\x9e*\xc7\x1c5\xfe\xb8)$\xb5\xbeT\x05!\xf6\xcb<\xebV_\x7f\x9cc\xe5\x119 W~q\x1f\xfe\xbaO\xc5\xd5\xcf\xb1F\xf0\xc7\xd1\x14\xfex\x1b\xc4\x1f\x7f\xb3\xf8\xe3<3\xe7O\xa0\x85\xfc\xf1\x1c\x8f\xf3'BJ(\xa0(\x9f\xa5\xcb\x90\xbf\xf2\xae\xb4A\xfe\x84\xd6\x01\xfd \xb7d\xf1\x1a\xe6\xd51\xac\xfe\xd9K\x9c\xfa\x84\x1a\xb6\xfcH\xdb\xeeC%\xfb\xd0\xfa\xd1:\xd0\x86@\xe5r\x13\x125a\xd2\xc9\x17i\x89\xa1\xd2cM?\x9d\"\x9b$\xab_O\xa9~\x10\xb5\"\x9d\xa29\x16\xb2Dw9\xc9Y\x82\x9e\xb5\xfaTK\xc0\xeb\xf9\xb5\xc9\xf2hfv9\x16f\x02F\x17,\x13\xfeXn\xbd]\xech\xe5\xa4G\xa1\xcb\xc6#sb~\x82\xc3\x9191#\xa9\xe1\xc8\x9c\x98\x96\xc8\x90\x99\xc6P8\x89!#\x85\xa1t\x02C\xb1\xf4\x85\xb2\xc9\x0b\xc5R\x17\xc2\x89\x0b\xc5\xd2\x16\x8e\xcc\x89G\xe6\xc4\x9c\xe4\x83#sb^\x92A\x0c\x99\xe0\x919Qy\x8e\xcc\x89pdN\xb4\xc2\x1a\xa1t\x81B\xc9\x02G\xe6D\xf1\x94L\x1082'\x1e\x99\x13u)qI\x00\x11)\x00G\xe6\xc4\xe5\x07\xff\xcbG7x\xe8\x1f:\xf2\x97\xe6{\xac\xb7d\x18\xab\xed.%\x90\xa8\xc77\xeb9\x04\xb3\xeb\xc9}\xdd\xed\x07~\x0c\xb9\x82\xef\xe9\x0e\x92\x9dE\x0e\xf0{\xf8\xfa\x04\xea\xf1)\xef\xf6\xcf\xec\xafLE6\xb5\x066\x9bY3@\x04}\xa6:\xeaw\xa4\xc0}7N\xde\xa8(\xf1\xa7j\x18_u\xdbm=\xaa\x85k\x06\x0e\xbe>\xd1\xcc,\xad\x11\xf5F\x87z`%\xc9\xe5\xe7\x08\xb1U\x0b\x90R\x18*\x9c\x85'\xda\x910:\xa3\x9eT\x03\xfc\xd4\xdd\x1aiX\xbc.'VpH\x19yyO\xd6\xc9$\x9d9\xabzE\xd5JFPT}\xb3\xfa\xfa\xeb\x13\xfa?\xff\xb4\xfag\xf6\xdf\x7ff_\xe0\xe32g\xc6\x84\x8e\xe1\xe4\x0d_0~\xe1\xec\x01\xd4e0/\xf1\xbb\x98\xa3\xd0cu;(G\x9a\xdc\xdb\x96\xce:C\xda\x0f|\x99\xdf\x10vy\x96\x92\xc0\xb3\xee\x1fvc\xb7b\xcc\x04C=_\x19\xf6\xaa\xdb\xee\xaa\xf5\xf8]=\x9eR\xe3\xcaM\x88e<\x05\xc5h\x07g\x820\xe3u\xfcfB\x96\xabCX\xc8\x92\x1a\x97\xebz\xe4\x0b\xcb\xdc\xe15\xe3\xb0ba\xbf\xb1\x03\xd2\x0e\xfb\x9eL\x07\x0f\xd3\xcddl\xc4\xc6\xea\x13\x19Xd\x9fs\xa1\xa9\xe4cB\x1e/\x90o\x85\x98c\xc3%(\xac\x91\xb2\xc4\xb6\xa3%\xf4\xa4\xda\xc0P\xdd\xc8p)\xff;\xedO\xc6h\xc5X\x15\xbbV\x04ga\xcf#_\xd3@\x8f_VC}\xdb\xaa\xd7\xc2}\xa8o\xdb\x9f\xa7D$\xab\x97U\x12\xe5\xaf\xe0\xc3\xd9\x0fo/\x7f~\xf7\xfa\x0d\xc2Y\xac\xfe\xfa\xfa\xec\xfd\x9bW\x17\xc8\x0f\x17o\xfe\xcf\xc5\xc7\xd3\x9f\x9c\x9f\\\x9e~\xfc?\xc8\x8f?\xbd\xf9\xe1\xf4\xd5\xbf^\x9e\xfe|\xf6\xf6\xdd%\xf3\x94\xedw\xde\x9c\x9d_~\xfd\xff~-\xd4@\xd0,\xbb+\xec\xc8\xae\x13\xbd1{\xda|Ec}\x06[\xfa\x0b\x8f\xf5\x8e\x03P7~\xa2|\xbd\xddW=\xb5Od\xce\xf1d\x03G\xfbO9T[w\xedP\xf3\x1b++\xbaU\xac\x87\xb1\x7f`\xaa\xd74\xc0w\x06\xb40V\xd2\xcc$\xc74\xeb\x15\x1bB \xebnx\x18F\xb2]\xc1\xe9n\xc7g\\\xc9\xee\xa4\xa5\xf1\x94\x12Z)\xba\x95\xd9\x90u\xbda\xcb\x00\x0f\x07\x8b\x89*\x04\x89O\xcc!\xf6\xe9\xe8?\xaf\xbe\xd1\xbc\x9a\x89\x0bo?\xde%Q\xc0)\x93)5\xd8\xf1\xff\xef\xa9B<\xfd\xff\xbdP\xb2-^\xd8U\xfa\xc0J\xa0\x95z:}\xec\xde\xb3\xa9\xf5\xd1\xd8\x8cU\x834\xdf^ \x0c\xc7F\x9a\x07\xd6\xed\x8a8A\xc8\xaa\xfc\x85\xce-NO\xd6\xdd0\x1f\x9b\xf98,ac[\x8d\xe2\xd2iS\xac\\@\xbe\xeb6\x0fO\x07=\xe6/\xf6Il\xbcYXUJ\x95\x88\x95]_o\xd9\xd9/\x935mI\xba\x96X\xc95\xbb\xeaa0\xc3_7d\x9a\xee7D\x89\x90\xb8\xfb\xf0{2\x91&\xdf\x10\xbe*\xdfV\x034\xf5\xb6\x1e\xa7\x9eS\xb7\xb9J\xddE%5\xa6z#\xd5'\xd4\x1e\xd6\x8a\xa9\xea\\6!\x82\x8eV\x913\x13\xd3\xaa\xd25r\xdcu7\xb0\xdc$r_5{N\xef\xcb\x96\xcbn\xc3\xa1%\x9b\x8ee\x89\xca;\x19\x141\xdc\x13\xe0\xf1\x10U\xbc<\x1f\x9f\xd5\x85\xae\xa2\x9c)\x97\x0cc\xbde\x01\x9a\xfb\x9a\xae\x80[\xc1A\xbc2\xe6ET2 \xdf\xe8`\x19\x0ev\xa0\x0e\x0d\xd3y\x82t\xbe\x10\xdd\x86\xb4\xdd\x16\x0bvy\xa3hXm\x83\x9f\x85\xc2c\xaf\xba\xbaUv\xf9\xec\x1a\x96\x99:\xb9\xed\xb6u\xcbM*\x1d\xca\xaa\x15\x95\xc0H5\xde\xbe\xbbx\xc3q)b\xff81\x00V-\x9c\xb5\xf2\x86\x85\xc9R\xab\xb1}K\x18?\xa6\xb3\x0b\x99\xd4h\x98\xe7\xff\xf5\x03\xdcv\xb7\x1d\xdb\x99\xea\x07\x93\x02\xe0\"\xeac\xf1Kr2g\x91\xcfT\xd5b}\xbb!\xaa\x8e\xdeV\xc3%\x9b\x94\x89y0{\x1bP!jc\x0d\xc2T\x04\x18\xa4\x9c\xb7\x95\xd8\xfd\x08\xddg\xfby\xddM\xb2\x95N\x04\x88\xcc\xae\xbbf\x89]\xecR\x90\xfd\x94b\xcb\xd2\xd4\xa0[\xaf\xf7\xbdjKv\xd5\xc3\xb2\xebb\xac\xa6\xb1\x1b*\x062r\x9en\xc3zI\x18bM\x1d\x00j\xf2v\xd5\x83\xb4\x1e7t\x93\ng7\x86\xbcI\xd4L9R\xad\xd7l<\xd9\xea\xb0\xab\x1e\xe6\xcf\xcd.`v\x9e6\x8d\xbf\xcb\xb2\x8e\xc6/\xb2>\xcf\xb8\x8d\xdc\x8b-\x1a\xfb\xebFn\x0f\x99\"\x9b\x9d-\xd7\xf1\xe7VI\x03\x19\x85\x1d\x9c\xbef\xce\xf4\xdf\xb7\xdd\xf8\xf7\"\x9d\x8d\xdbc\xba\xc81\x87\xeef\xd2gS\x96\\\xdd\x905A\xd5S\xb6\xb3\x8f\x1d5\x97*\xd67s\x17\xd3\xf5\x80w\xd73ROwmic8_2\xb2w\xa45s\x01\xac\x0b\x9e\xcbTr\x89l`%\xb0zO\xd7o\x98#\xd6\xb1\x11\xa5\xa3\xa9\x82\x1f\xb4\xda=\xe5A\x8e\xeb\xaa\xa9\xda5\xa1:cG\xed\xab\x1d\xb5\xc6}]\x8dD)T\xeeo\x80|\xa9\xe9\x12\xc6\x1b\xb3\xbe\xab\xa8E\xecl\xac\x86\xb2{\x9b\xa5\x0cb\xdb\xc4\xb6\x027U\xdd\x88\x8f\xc6z\x17\xe3\x06\\\xd4;9\xeb\xa7\xdbr\xc6z\xa7$\x16\xaa\x11w\xd6\x11\xccR\xd5\xd4 \xb3M\x97\"\x8c\xd9k\xdd4\xebT\xac\xf5m\xdbQ\x8bY\xdf\xa8M\xad7\xed\xd3Q:\xe2c\xbd\x1bN\xa0^\x91\x95\xfc\xa1\xda\xe8\x11p\xcd\xcf\xb8\xba\xa8w\xaf\xc9\xba\xeb\xab\xb1\xeb\xafh\xb5\xeaq\x80]7\x8c\x02k\x1c\x11\x9b\x9e\xf6r\xc7\xa5[{\x8eK\xb7{\xe9f\x96\xb0V\xb9\xf5\xc6z\xb7K\xb5\x7f\xfc\x9bI\xfaf\xd3\x93a:\xc0\x96\x0b\x8bX\x95&\xfb+J\xc5c\xb3rM0\xd2>\xa5\x8b/\xec&\xdf\x1fM\xf8.\x19\xa7g{~\x99`o\xd8y{\xcf\xf6]\xdfU\x9bu5\x8c\xd1\x91\xf1\xef\xde\xbf;}\xfd\xea\xf4\xc3\x85;\x9dHlY\xda\xcaHx\xf1\x9e\xf9s\xf1\xe5=wL\xa2\x032\xe3\x97K\x16\xb3N=\xf9\x03S\xcf\xa5 i\x82\xfa\xea3\xe6\xe0m\x11\x10\xbeQ\x16r\x91hh\xb6\xa3\xaf\xa93\x1e}A\x99\xf5\xe8\xef\xa7\xda\x0b\xb1\xb3\x1f\xbc\xeb]\x19+\xa0\x08|\x7f\xfe\xca\xb2\x04hk\x92\xad\x01*\xe5\x90\x16\x01\x92\xac\x02Z\xbd\xc3Y\x06H\xb2\x0eh\xe5\x0eg!\xc0\xb6\x12\xaee\xdd\xb6\x15\xd3\x8c\x15\xff\xa4\x93rR>K\xf5\xa6\xcf\x84P\x98\xcc\x07\xfc\xf7\xfd`7\xfe`w\xfe\x94\"\xf7\x81\x92\x04?P\x9c\xe4\x07\xa2\x89~ \x86\xec\x07\xe2 \x7f \x92\xf4\x07\xf0;\x80\xf0[a\xe2)bB\xf7\x00E\x93\x00A\x1c\x11\x10`\xcd(I\x08\x04\xb9\xa4@\x86,\xe4~\xa0\x924AP\x94*\x08\xb2\xf5!H\x19\x04\x11\xb4A\xa0\xdd\x15\x84\xd2\x07At\x10\xda\x8e\x97gQ Y6|SW\xadA'\x04\x90K)d\x08\xe3U\xb2i\x85\xc0G-\x04\x8e\x03\xd7\xf1\x11\xce\x9dG\xc7\x91\xaaw\x0f\xea?FuU\x9b?\x87=!\xf6\x1e\x94z\xdb$\x9f\x00w\x10\x84OPK\x15\xe3\xa1)\x82\xa9\x18\x17U\x11\x04g\x97|\x96S\x169\x04V\xadM[\x04\x11\xd5YJ_d R\x8e\xb0,\n#(Fc\x04\x11TF\x90Og\x04\xc1\x9e+Jk\x04\x18\xb5\x11@\x01z#C\x1e\xf7\xbf;\xdb\x9b/Fs\x04E\xa9\x8e \x91\xee\x08\xfcX\x9c,\xda#E\xd0D\x80\xa4R\x1f\xd1g \xfdQTJ\xa2\xa8\xf6\x94\x93(\xfe=*I\x89\xe23$5\xd1\x9b\x89\xf8=\x89\xcf<4Q.\xd8\xa2\x93K\xc7\x87\xe2o\x9c\xd6\x1d\xc7\xdd8^\xf7e\xba-\xc6\xda\xa8Br`6\x12V\xa3\xca\xfb\xff\xd8{\xb7\xee\xb8q,M\xf4=\x7f\x05\xc6\x0fm\xbb[\x0e\xa7\xb3.\xb3Z3\xee\xd5\xb2,g\xaa\xcb\x17\x1d]2\xbbN\xad:!\x88\x81\x08\xb1\x1dAF\x91\xa0,U\x9f\xfc\xef\xb3\x80\x0d\x90 .\x04HB\xce\xcc\x1a\xe0\xc5V\x90\xdc\xb8\x03\x1b\xfb\xfb\xf6F\xb8G\xcdLGX\x8b\x13\xac\xb5\x0d\x1d\xce\xaf6o\xc3HN\xafv\x87\xd7PgW\xcd\xd1\xd5Z'\xf7z1\xdb\xb9\x95\xfbZ\xaaSa\x82c+\x8d\xe8\xd4jwh\x9d\xe5\xcc*\xddW\x15y\x03\x8e\xac\x86\x13\xab\xb5Gl\xc3)\xaa\xe3\xeat\xa7\xd5\xc9\x0e\xab\xaa\x8b\xaa\xdaV\x93\x9cUG8\xaa\xdaG7\x0fN\x01\xea\x84}\xa9\x90>\xa8\xbc\xa2r\x03\x133\xf8\x9bn\xd1\x10K\n\x9f\xc27\x0fF\xaf\xf3\xa5\x90\x9fu\x117\xa3\xf1u\x90\xac\xd7$\xa3y\x0bo=\xd9\xe0z_\xe5\x19y\xd2\xaa?\xb0L\xb6\xe3\x1d.\x84(w\x04\xed\xf2]^4;\x91\xad\xa4Ztt\x89\x1d\xd9\xed\xcbrk\xdf\xe9\xbe'\x94k\x99?\xe5\xf4\xf6\xf2\xbe\x9e\xc0\xbb\x7f\xa4\xe0,\x97\xf7\x8e\xa0,\xf4\x1e\xb4 \xad]\xb5\xc3\xb1\xf8\x96\xff\xb1\xcc\x8d0\x87a>\x01\xe1D}\xc7!j\x8f+\xba\xac ]\xde\x12\xbc\xb2\xfbi\x8e#c\x95\x14;\x99X.V\xf6\x10'\xdb\xac\"\xf2\x9d\x1b\x9d\xe7E\xb1B\x9d\xe1\x8a\xd6\x84\xfe\xc0k\xfc\x8d\xf6\x90\x8f\xb5\xd3\xb7j\xefL\xeb\x9a8\xed)\xa8\x0e\x96\x16\x80\xe2\xde\xe0:\xcf\x84'\x07;\xcb9ZjR\xbcb\xad\xf2j\xf2\x9e\xdb\x07\xe2m@\xc2\xfb\xfd\xe3\x88\xf6\x1d\x9d\x99\xd2X\xd4\xa4\xa8\x9b\x1aex\x0fz\x1a\x05r\x95\xf8\xb9j\xb6\"NR\xef\x18\xc8\xdb\xc3\"O\x9d\xd6|\xe57\x0e\x84\xf0\x16[\xb8\xe5i\xaf{\x19N*\xedy\xa6[\xb7\xa1\x14N:a\x8f\x0fk\xcb\xb0\xa6\x98\x8a\x05\x08\x0c\xae;\x9c\xdd\xe6\x05\xd1\x8f\xb0\xbc\x14\xbd\x15H\xa6\x81\xae\xb0y#y>\x19\x9e\xe74\xdfM\xa5w\xac0%/\xd8\xf7\xda\x1b\x1c:0\xd7W\x99f\xcc\x0c\xfb\xa2\x84|\x05F~c\xd6\xe0z,S\x04\xe3\x9fc\x9dn\x1f\x0f\xae\xd72\x0d\xfb\xd2@r7\x15\ni.\xe4o2\xcf\xba\xae\xbd\xd4_\xdfe\xe2C\x05<4\x971w\x1c\xd4e\xcc\xc4\xc2|\x86\xd5\x9aM{\xed]\xf6S\xe4\xec\xb9m\x1e\xd3\xb2\xaa#\x0b\xd6\xeb\xd5\x99\xa0\xf6{\xc9\xdco\x7f\xdbW\xe4\xce\xba|\x16\xe4\x9e.\x1f\xad\x8c\xed\x8a\x1eY.\xde\xef#K\xe4\xe3OP9#\x8b&w\xf9\x8a\x14\x19\x89,\xb6\xed\xffn\xdb\xb4\xe8\x1fl!*kR-E\x10\x8fX\xf9\xf76y\x98\xf0\xfd\x88\x87\xady\x90\x87\xe6\x96\x9e\xae\xec\xbd\x10g\xceQ*Z\xef\x80\xd1\xfeh9h\xc8\xe4\x84u<\xab\xe1L:\xe3\xe5}\xad\x04\xdf\xbd\x11\x1e5p\x04\x04]\xe1\xdf\xc5\xd1\xe4\x07\xbe\xb9\xff\xcb++q \xace\x9c3\xbf\xdd\xf2\xb3\x0e8kU\x02\x08\\ \xf4\x13yZ\x11\xf4_\xec \x887\x15!]hT\x8b<\x08(\xc9\xcd\x00\xd6\xfc\xb8\xff\xc3\x8e\xe0B\x94\x1e\x8ax\xb4\xdf\xff\x80\xeb\xdb\xee\xfc\xdd\x99\xd9IMX\xb1\xfaj\x8e\x18\xaco\x99\xae\xd5\xb3\x88\xd7\x04\xad`\xd5\x80FI\x96w%%Kw\xe1 y\xb5\x10\x7f\x8e,\xf1\xbc\xb0\xfbyPF(03$\xc5\x0d\xbe\xe1\x9d\x89j\xd2#|\xd8\x13\x04\x1b>y\xbb\xfcp\xf1\xfd\xf2\xf2\xcfg'\xcb\xab\x8f\x7f\xfa\xf8\xe9\xa7\x8f\x13\xbe<;?\xf9\xf1\xd3\xe5\xc9\xb4/\x8f?}\xf8pz9\xe9\xdbOg\x9f.\xda\xf8\xfb\xae\xd4\x0b\x9f?\xbe\xbe\xfee\xac\x9fx\x84\xdc\xd5\x87zs)h1\xe0{\xc2\xa6\xb4\xb0\xaa*|C\xb73i\x97\xda\xed\xcc\xba\x18i\xc9\xd97\x87\xe8\xc7\x92\x1a8^\xa0\x04h\xe7Ct\xc67O\xbc\x1d\x16\xe3:\x89\xf5\xd3\x88\x01\x1d\xa2\xd5C\xaa\xca\xa6\xb0\x1c\xac\xfa)\xec(\x01I\xc9\xfaw\xdf\x0d\xbe\xeb>\xd6\xf5S\xe0\xda\x81F\xac\x1f\xc8{\xaa\xe9\xd2\x88fG\xbe\x1d^OAG\xc5~\x1a\xd1\x1ahd\x8b\xb0\xe49T\xf6\xd3\x98q!S\xf8\xd0\x94)\xb4\xa3\xd0\xf8\xceBc;,\xf0\xa0j\xfd\xc4~l\xd5_\xb52\xca,/\x86W\xd4gc\xe9\xa7\xf6$\xe7\xd6\xf7\xfbiBI\xbcM\xdd\x15\xc2\xc3\n\x824f\x1c\x86\xafO-\x06\x1c\x96}\xcc\x16\x18\xb3\x85\xb2]\xaa\x7f_\x0d;\xaa\x97\x94\x1c\x08_\xc0]\x0e^\x8b\xf0_\xae\xac\x0d\n\xe4G\xfe\xee@\xcf\xca<\xbc\x8f*\xfb\xad\xeb\x15\xae!\xde$\x0d1\xfc\xcb\xa4!v)i\x88IC\x1cJ\x81k\x07\x1a\xb1~\xa0\x11\x8a\xc7\x88fG\xe1{\x00\xa4\xa4!\x06\xa4\xd0\x8eB\xe3;\x0b\x8d\xed\xb0\xa4!\x1aiBI\xbcM\x9d4\xc4\xf0-\xf47\xa1!\xf2eeyW\xd2\xbc\xd8,\xf7\xe5\x97\xe1\xb5.\xb0A\xc3\x96\x92n,}\xdd|\x83\xa6\xf2\xc8\x1c}S8t\xdc\xbc\x95\x86d6xN\x84\x19\xb93\xbbK\xc32`3m\x03:\xc5Iz\xe4\x97\x92\xc9Xo\xf3\x8c\x13\x1f\xb9\xc3\x91}Ll\x99\xc2\xb3\x84\xe8\xb2K\xee\x04\xf1\xf9k\x99\xb3\x95\x12.\x07\x084\x90\x02\xf2D\x81\xf9\xa2\xb6\xa1\x02w\xfb\xc0\xbc\xd1\x88\xfc\x91\x83\xf7dO#\n\x80F\x16\x02\x0d1\xa7\xec)\x94OeO#\xeb\x82&\xd4\x07\x0d3\xb2\xec)p\x01\xd0\x93\\\x10\x9c\xec-{rr\xba\xec\xe9k\x16.t\xe9\xd2S\x18W,X\x9c\x9dS\x16z\xf2\x94\xc9\xc34\xb3\xa7P\xfeY\xb0@\x1bO\xcd\xcfJ\xb3\xa7P\xae\x9a=\xb9\x19l\xf64z\xe0\x85\x9d\xa0e\x1a->t\xcfW\x93\x9d/gO3\n\xe4S \xfa\xc9\xc3\xb8\xb3\xa7\xaf\xb4v\x8e9\xe7\xa1i\x8d\x86\xc2U\xf0~\x9apP\x97iB\xeb\xa1\x89-\x88\xc6\x1e\xe0e\x1as\x80\xd2\xd3\xf8\x99!\xd3\xd8\x0eG\xd3;\x1dM\xed\xf8I\x07~\x99F\x1c\xfce\xf2\xf3\x1c\xediB\xbbLh\x8fp\xa6\xa4=\x0d\xf0'\xed\xe9kT\xcb\xcbn\xb4\xa7\xafQ4\xbb\xcf\xd6p\n\xa5{\x06\x0bt\xb1\xea\xcd\x14F\x16\xb5\xa7\xaf\xd1\x9c>\xba\xa9=}\x8d\x92\xb9 \xab\xf6\xf45\xca\x14@y\xb5\xa7\xafQ8\x0fi\xd6\x9e\xbeF\xc1\xc2h\xb7\xf6\xe4'\xe3\xda\xd3\xe3\xd7k\xca\xe9,\x90\xf5\x1b$\xcbd\x06\xdb\x13\xec\x9c!\xed6R-\x1b\xab\x8e\xfd\n\x0f$AP\x9fLS4\xc2p\xd3\xbaL\xe9$2b\x0eBJ'\x11g\x1a?%d\x1a\xdb\xe1hz\xa7\xa3\xa9\x1d\xff\xb5O\"]\x90\x8c\xd0\x96\x81\x16\x19\xbel\xa8\x9f\xbcW\x0f\xf5\xd3\xa4\x01\x90\xa2\xa3']\xca\xd1\x9b\x8b\x93\x8f~\x86Q?\xe9B\x02iJ\xfd\xa4\x0b\xf9x\xeac+\xf5Swo\xe6\xecf\x19\x7f\xf4\x82\x04\x93l\xf5n\x8b7(/V\x1cg\xabE\xec\x857\xdb\xac\xfc|\xfav\x941\x19R;\x1dQ\xae_\x14\xe9K\xa3!\xfd~\x9a<\x9e'/s\x01\xc0\xa9\x99f\x17s\x9c%\x15R0G\xa0\x9ff\x97uT\x93NQ\xde!Al\xc8\x8b|\x03\xb4<\xa6{H\x1c\x81\xd3\x0e\xa4\x1f\xd6\x08\x91\xfcnU\x11s2\xe0\xb3)e\x07\xe9z\\5\x01i\x8bh4\xe1G\x8f/\xfc\x9e\xa4]\x17\x16\x0eK\x97\xb4\xce\xd21\\\x93n\xfe\xd5\xc4{$\x18\xb1\xc7\x8d\xd9\xd9\xba\xb2\xfa\xdf\x1d\xbb\x93\x8f\xd8\xc5G\xd4\x0e\xd2\x98:B\x1a\xbd\xc6M\x9a\x8c\x13&\xe2\xbe\xb9Y\x06\xdc}\xd8\xa5\xd1\x8d\x85&5\x18Kd\xf5\xdd\x1f\xfe\xf0\xea_\xc7|2\xb1\xe1\xd0\xb4\xc6C<\xa0V\xb6\xff\xee\x0f\x7f\xfc\xfc\xea\xd7\\\xcc)Z\xc3Ys\xb3\xcd\xb3?\x91\x87\x9e\xb1\xe43y\xa8\x95kl\xc6\xed\xf9MM \xcc\xde\x8f\xed\xc4\x0f\xfc:\x94\xa4\xd5O\x93\x1ay\xca\x81\xad\xb5\x91\xed\xab\xbc\xacr:z>=j\x19e\xe9B\n5rz\x8f\x9d\xd8#\xd7\xc1 \x8d3z~\x8c\\\x01G6\x10\x9a\xd0Hh\xda\xda7\xa1\xb1\xd0\x94\x06CSW\xbd\xafW\xc0\xf1\xeb]\xf4\xd5n\xeaZ7e\xa5\x9b\xd0\xb0\xe3V\x104g\x8d{\xf4\xd2\x8d\xe3\xf1\xb6_\x8d+VX\x91\x98N^\x16K\xbf\xa1=0\xf7\xb0\\o\x1e\xfe\x8e\x0b\x9a\x17d\x19\xa6Q\x87i\xd2\x01\x1at\xf0b\x18\xbe\x04\x06\xef\x10\x81-\x08i\xc4\x1a\x12\xbc\x1f\x04W\x1e\x8dj\x004v\xf5\x1f\xd5\x10h\\c\xa0\xf1k\xfd\xe3\x16g\xcc\xca\x1e\xba\xa6\xf3\x1b\x8f|\xc2\xd0\xc8\xd5|\xdcr4\xaa\xd1\xc2\x16\x05H\x13V\xedG(\xcb\xb8\x15:\xb0\x00\x81Y\x87\x18\xeeF\xe6\xe83\xca\x85\xda\x87\xde\xb3]\xe2\x98\xfb\x1b\x1cqw\x03\x9f\xd7\x03\xd8x\x9c\xf2\x14\xff\x18L)\xd9\xed!\xd4s\x89vy\xbd%x\x850\xf88 \xf0qP\x0dD\n\xc5\xab\xdfP\xceU\xce\xbd\xa2E\x8f\xf2\xe8\x00\x89A^@xX\x03\xf8M\xe1\x1d\xad)\x85w\xec\xa5!\xb0qH\x85z\xacH[A\xf0_@\xe3\x0dCzc\xc1\xbb\x910\xddH@.\x1cz\x9b\x04\xb2\xf5\xfa\xdf\x8b\x8e\x85\xe1]\xc1\xc8V@W\x05\x8cr\xcf\xf66\"\x93\xe1m\xcd\x8b*\x8d\xc8\xc9Q\x9d\xde\xbe\x19\n\xf3\xb8\xc1\x9b\xe1m8\x18\x90q\x80-\x9a8+\xf4\xb2\xc7\x1bq\x95H\xd7h\xbdRu/(\xf7\x91(?\xca\xeb\x15\xe4%0\xca% \xb6\xb5\xc4\xbe\x82p\x06\xacq\xa4qv\x97\xb3\x93\x86\xaeT\xe9r\x91W\x80\xb0\xff\xcaKH\xea\x1a\xee\xa0?\xc3\x1br\x0eW0,\xe0\xb9&\xe4o\x0d\xa9\xe0j\x01&\x8e\xb5\x04A\xbb\xb2\xa6\x88\xf0k\xd1\xf9-\xea\x0btJ\x95;\x07\xf7\xf4\x01\xe5\xbaND\xdb\xe8\x92E\x89veE\xe4\x8d\xf7\xea\x08\xb1lw\xdeF\xb1\xb8\x8f\xb9\x8e$\\\x93l[y\xb9F\x0d\x85\xe5DN\xd3\x9a-d\xb8S\xcd7\xa4 \x15\xa6\xbc\xa8\xf4\xb6\\\xb5\x91\xc3\x8e\x8c\xf5\x0b\xbaD\xcf\xe3\xe4\x1e\xb3!\x8a^\x1d\xa23VN6?E\x91\xb1\x1a\xca\xfb\xf8_\xfe\xc5\xba\xa9\xbc+K\xb4.K\xf4\x1a-\x16\x8b\xffey\x81U\x1c\x17\x0f\xb6G\xb8xX\xb0L\xdfU\xe5\xee\xd9\xba,\x9f\xdb^Z,l;G\xbeF\xcf\xd8\xe7W\xbc\xa8\x97\xe5\xb3\x7fb\xdf?G\xffm]\x15\xed2~v\xb5\xc5w\x9e\xb6\xf8\x0f|\x87g5\x06z\xcd\xf5\x1a&yb\xbd\xf3\xfa\xd9\xbb\xb2\\d[\\\xd7\xcejCQ\xd8\xebP\x0b\xe5\x13[\x8eF{\xb4\x0d\xf2;O\x83\x9c=\xd0[v\xde\xb1\x08\x852\xbc+\xcbg\x8b\xc5\xe2\xb9}\x10@s?=\xbb\xfct\xfe\xdcn\xe3\xea\x06\x92+\x1b\xc8\xc8\xd5<\xbf\xf74\xcf\xf7\xa5\xd5z\xc1\x9a\xe6\xf05\xfa\xa7\xfd\xcd\xe2]Y\xfe\xf7b\xb1\xf8\xd9\xf6\x1a.\x1e\x0e\x98\n\xc5\xde\xdd\x83z\xf0\x01W\xf5-\xde\xb2Fs\x15\xd8\xde0z\x9e\xd6\x0c\xf3\xb5\x96\xddU\xb1\xeb2\xe4\xc5\xe1\x83\x96\xbf\xf5?^\xa3\"\xdf:\x06\xa0\xab\x14\xc6H\xbb\xe4\xf7\xfbg\x9f\xdbUL*\xb7\xecH\xbd\xd7\xd7Z\xc08\x1f$\xb3\x81\xd3\x145\x81O-*\xc1Kv~[\xf0\x07L\x95z\xcat\xeev\xedg\xfb\x82\x08\xc1\xa8\x89\x82\x1e\xd53h\x97\xdbb\xfb O\x1d\xc6\x01\xb1U\xdb\x10^SqC??\x9b>}\xf9T\x17(\x8e>\xb20p\xd6!b\x84=Y\x97\xe5\xe2\x06W\xbc\x1a\xf7/\x1f\x16\x7f\x7f\x02\xad\x00Z\xbb\xed\x18\xc2\xb3}\xc2\xded\x8b\xbe\xf6\xf0?.>}\xd4\x7f{\xfd\xfa\xf5k[\xbf\xb0w\xbb\xb31\xe81%\xbf\xcd\x1f6m8\x0345\x91\xb8\xe8\xa6\xd9b#\x08\xb5)\x80\xbd\xbc\"\xddf{\x80\xc8\xee\x86\xacV\xdd\xb6{ \xf6p\xe3D\xadl\x80k\xde\x00\xd7\xff\xce\x9a\xe0Z\x1c\x02{\xd4\x05\xd9\xa0\x0b9]\x0f\xad\x8a*\xce>\xb3\xd9\xda\x1d~\xd6\xf9\x96\xd8VI9\xaf\xcfHU\x97\x85c\xe0\x0b;\x06\xbf\x86q\xc9{\xe25ze\x93\xd6\xbe\xcay\x86\xe2\xcd\xef\xc2Vg\x84\x1c\xb9?\xe1\xad\xf1\xe4\x10=\xb1\xcd\x82~\x15\x17P\x8f'\x07vI\xbc\x06\x1f\xf1\x8eI\xfb\xdfP\xd4\x7fs\xbc\xcaj\xa0\xbd\x19R\x8d\xd3\xb5P\xb7\xfb}\x0f\xfd\x96\xd7\xe8\x0b\xd9n_|.\xca/\x05\x9f\x9f\xb7\xb8F\x18eMM\xcb\x9du\x18\xf7\x07\xda\x01({\xda\xe8\xebH\x12\"S6\xa0\x8a\x8dam\xe1\x83K\xcf\xe0\x9a\x0fx9\xd6n\xcb\xedJ\x04\xb1\xed\xca\xc4-7b\x8c\"a+\x11CT\x97\xc6\xb3h\xc7&z\xc6f\xbdl\x02\xe3(.\xedE\x7f\xfd\xcb_\x9f[\x87\xf1\xbc\xf1\xd0\xcf\xc45$x\xf5\x99\xb0W\x8b\xef^}W?\xb1vr\xf7\x7f\x07\x11\xcby\xa0\xf1\xd8\xcb\xf3\xce\xe4\xb6\xaf\xc8]^6\xb5\xb8\xc3\x14\xbdcg|\x8e\xf2\xd7\xe8\xdf\xd0\xab\x03\x94\xd3\xa7\xd05_\xf8\xaf\xc6\xf9{GV9fk\x91\xde\x94|\xdd\x90\xe5nUj8\x1d\xf18\xe9\xbd\xabL\x17\xefqM\x05\xcf\x8a\x15B\x13\x06EB\xaf_\xa3WF|a^B\xa6\xc3\xd7y\xcdsT\xb7K\x1b\xe0J\xbf\x02\xeeL\x1d\x90\xea\xe0\x19t\x18Fu\x15\x1b\xd2\xe3\"\xc4\x83@\xe9`\x9dd\xf2\x92\x0d\xbd\x08j\xacl<\x17l@67e\xb9%\xd8f!\xf3\xcd.\x998V\xd7\x81\xb3\x9cgX\xe7\xc5f\xcb\x11\xd8\x17\x1d\xc8x\x80p]\x97Y\xceO\xa0\xdc:c\x17\x88\x0b\x18\xcdca:\x00\x0d\xb9M\xb0V\x911\xb4\"wd\xcbz\x9e\x1b\xc4\xf8\x9d\x04\xb7\x8aZ`\x08R ,D-\xea\xb8\xe4\xc1\xbc!\x9b\xbc\xe0\x9c\xad\x83\xf6\xb7\x93b\xa5\xfdr|K\xb2\xcf\x97\xf7lCrJzK\xb6\xf9\x1d\xa9.\xef-\xc7\xa2\xf7\x98\x92\xea\xa0\x7fK\xf2\x0ep5\xc9\xacjxDs\xb8~\x19\x16\x821\xe6\x14\x81\xc3\xb6\xb0& \xccB\x90\x84\\\x85n\xddEOw`WHX\x0c\x8d\xd5K-\x9drctY\x13W\x16\xa2\x1cb\x83\xd3\xcd\xf8\xa0\x7f\x97\xa66/\xa5uaR\x0bJ\x16\xe8\xa7[R\x11\\\xa3\xf7\xe5\xa6\xd6\xf9\xaa\xacL\x07V\x83\xa1\xa2<\xee\x08\xc5+L\xf1A\x9b\x03W\xe7\xfb\x85V\x0bl\xdaO\xac\x14\xbe\xef\x16\xaf^\x1d\xb0\xff\xfc~\xf1\x07\xfe\xef\x1f\xda\xaf\xdc\xfd\xd6\x91\xfb|ptE\xb6\xe4\x0e\x17\x14\xd1{\x087\xdf\x1f\x87\xb2V\xdcH\xad\x96\x97\xe2M\xad\x10\x02\xe0\x8c\"\xe3\xceoKN(\xe6\xea\xcb\x8ad\xe5\x8a\xac`\x10\xf4J\xdc#!\x0eR!U\x12\xe4\xe5}K}t2\x1bk>f'\xf0\x1b\x95\x85\xdf\xb6\xcd\x18\x9b\xcb|\x16d-+\xbe\xcdk\x0eRY\xe8\x90\xb5\x85\x0f9\xb6\xa4\x8e\xcd\xcf\xb5\xe5\xd9\xdd\x8b\x06\xb6\x9e!\x8e\x98\x9f?igP\x0ef8\x8aE\xe9\xe4Q\x0ef\x11\xca\xa5\xb4\xb1)\xa5`;\xabn\xa8\xb5\xc28\x956Vex{\xf9\x99\x95\x0enex\x16\x06\xbf2\x9c\xa0\xe2gXzx*6\x96\xe5\x90\xe2\xe8P\x1a\x07\x15\xc6aeq\x90o9<6 \xf9<\xbc\x1c\xacK\xafz\xe8f^\xfa4k\x0f-\xd1\xab]\xfb\x9a\x0c\x92\xeb\xc0\xd0{\xee\xd3~Cx\x98\xfe\xeaB\xf2r1\x83\xaa\x0e)\xa4\x01 \x0502\x03\xdb\x02R\x10+s\x84D\x9f\xb2\xddOc\xb9\x99\xfe\xc6\x19\xc3\xce\x9c\xc2\xcf\x0c\xaf\xe1$\x8e\xa6{\xf8\xb3E\xd1\xcb\xd2\x8c\xcd\xd3\x1c`j\x866Dd\xb6f\x00_s\x80\xb1\x19R\xe8\x19\xacM\x8747o\xd3_\x9e\xd0\xad\x91\x0eq7\xbd\xbb\xa2\xc9\xdf\x0c\xdf\xcf\xe7p8\xdd,\xce\xc9:\xe5\\.\xa7\x8b\xcd\xf9\x18\x05\n\xe0t\x9a\xac\xce\xc1m\xc5gr\xb3s;\xbd\xeb\xbbo\x94\xa2\xf8\x0cO\x1f\xc736\xcb32\xcf\xd3\xc3\xf4\x9c\xcd\xf5\x8c\xcb\xf6\x0c\xe1{\xce`|\xc6\xe5|\xc2h\xf5\xb0>\xe3\xf2>\x03\x98\x9f\xd1\xb9\x9f\x1e\xf6\xe74\xfe\xa7U\xd0 '4\n+4\x90\x17j\xfdr\x14Wt6[46_\xd4\xcd\x18\x8d\xcc\x19}\x0c\xd6hd\xdeh(s42wt\x98=\x1a\x9d?\xeaf\x90\xc2\xc2\x15\xc4!\x9d\xce\"\xb5\n\xe3\xccR\x07\x8ft\x16\x93t\xe0\x00\xe9Q)\xc6D\xa3ph\x1c\xf18\xa5\xc3\xacR\x7fi\xa22K\x87\xb9\xa5\xd1\xd8\xa5s\xf9\xa5\x868\xae\xd1X\x95\x87\xb8\x1cS1\x8d,\x15\x9a\xcb3\x0d W\x0erM\x03\xd9\xa6N\xca\xdaH\xc6\xa9[\x8e\x85\xc73\x9bw:\xa6qB\xb8\xa7\xfeV\x08\xe2\x9f\x8ef\xa0\xdaYN\x11X\xa8\x01\x9b\xd5\xc3g\xb53Z\xed\x9c\xd6\x98\xac\xd6\xc8\xbc\xd6\xc7`\xb6\x8e\xe1\xb6\x06\xb2[G\xf1[\xc3\x19\xae\x0e\x8e\xab\x8b\xd5\x18\xcek\xf4\xf3\\G1]\x83\xb9\xae\xd6\n\xc5\xe6\xbb\xc6e\xbc:8\xaf\xb1Y\xaf\xb1y\xaf\xf3\xc7H\x10\xf75\x8c\xfd\xda\xe7\xbf\x0e\x84\"\x1c8\x86\xf9N5Qy\xb0\x03L\xd8\xc8\\\xd8a6\xac\x87\x0f\xeb\x02\xc2\xe9W\xe4\x08P'\xd4\xed9S\xfb\xe0mw% }\x0d\x1c\xdf\x03`{j(S\x00\x8d5\x00\xdb\x8e\x99\x99\x875+3\x1b\xe2\xcd\xfag\xa4L\x93\xb8\xb3NiBQ\xb0\xb3gC\n\x15\x8dA\xeb\xe7\xd0\xc6d\xd1\x86\xf1h\xa30i\xfd\xad8\x89Mk\xae:\n\x8ah\xe7\xd3Ng\xd4\x1a\x82$a\xd5\xcd\xa9\x8d\xcc\xaa\x8d\xcd\xab\x1d\xcf\xac\x1d\xee\xc7h\xec\xda\x01~\xedT\x86\xadQv\x953\xea\xe2\x9av\x15\n\x8f\xaa\xdb\xe3\xf7M\x0f\xb1\x8b\xd0[\xa6]f\xdc&\xb8/k\x8a\xee\xbe]\xfc\xfe\x8f\x8b\xfbC8MB|W \xb1t_Y7\xa9\x14\x98W\xa4\x14\x98\xd7\x12\x98\xd7\xa8\xb1\xb5\xb6\x8e\x9a\x8aZ\x86W\x8a\x7fi\x1f2\x16\x86z\x18\xf9\xfd\xf2\xbe~\xf3\x00\xfb\x87\x10u~v\x01<\x91\xd6\xa8\xfcR\xa0\x9adM\x95S\xfd\xd4\xb1ip\xc5\xf4\x01\xdb\xde\xccI<\xac{\x14\xf6JV\x16u\xbe\"\x15\xdb\xefPE6yM\xab\x07nQ\xdbn\x11\x983X\x19x\x01\x0c\x8e\xa48A\x1e\xf3\xc9\x80HV\xd6\x0f5%\xbb\x05:\xda\xefk\xb1(R\xc1\x1a\x81uY\xa2`&\x0d\x85\xe7\xa5g\xd0e-d\x02\xa3\x91\x17~\x9b\x7f&\xaaHa\xd1P\xbe\xb1\xd1UI\x91\x95M\x857P\x9crO\n\xd6\xca;V\xdb\xb3s\x847Li\xa2\x02&\xcf\xb7<\x03\xbcbmS\x90/(\xc3\xa6>\xc6\x0d&y\xdd\xf5\x1fo`\x81#\n})\xaf\xbaR\xa1\xba\x14Pc\xbe^\x93\xcaBf\xc2\xac\xa2\x9c\xc0\x80\xa1{j\xca\xce\x02\x82\x1d\x003St\xa4\xa9[;f\x9ac|\xb6\x18-\x07X\x9b\xa2\xeb\xf0v\xd01-H\xec\x81Z^\x15\xf9/\xde\xaf\xc6\xf1\xc6\x9c\x84\x87\xc6/j\xce\xda(\x97\xb4 h\xd3\xb7e\x06\xe4\x01\x9dhsG*\xd0\xd3\xf8\xb4h9\xc6\xa0\x16\xb3c\xc7@\xa9\xc4\xe2qh\xfe\x04\x07\xbfu\xc3\xb5\xcb^\xb9`\xe8\xb1\x96\xe09?\x00]\xb6\x9f\xc7m\xb3\xc3\xc5\x8b\x8a\xe0\x15\xdfm)\xb9\xa7\x0d\xde\x1a\xc6fv\x0e\xdb\xcb%\x16\x08%F\xdb\xaa_\xe8\xbd\xcc\xab\xa8\xb7(\xd7V\xf2Z!r\xb0\xc9'fGP/\xb1\xa5\xd2\xec)\xf6kHoY\xd6\xb3\xb7e\xf66\xafHF\x8f\x9a\xfb\x05:\xaaQ\xb9\xdf\x97BC\xd3\xb39\x80Q\xdd\xce\x13M\xdc\xaa$\x9c:\xa27EE\xfe\xd6\xe4\x95\xdc\xa1\xea\xb6p\xe5\x1d\xd3\x168\xd1C\x7f\xc1\xd5\xaa\xd6\xf8H\xc3]s\xb4\xcb\x8b\x12\xce3\xca\xecF\x15\xd9\x95wp\xff\x08\x00Kl\x0e\x0c\x0e\x1b\xb1!\x1e\x9a?)\x03F\xaa\x16P(\xd6\x90'\xa7g\x88\xbf#\xfbJ\xddF\xf4v|\xfb\xa7\x05:'\xebC\xc4\x19:\x87/_\x92|_/\x08\xd7\xbb\x9b\xdd\xa2\xac6/ON\xcf.\xd8\xcf/\xd8\xe6\xacw\xc4\xb1\x9c\x0e\x07\x96R\xe6\xb5\xd8\xf3`\x03dM\xda[\xc7\xf5%\x07W9.\xa8a\x00\xb8iZfSK\xf7\xe1\x87g\xd0)\xdf\xfeI\xc1\x13\x17\xe8\xb2D\xa4\xe0\xeb\xc3\xc9\xe9\x19+\xb3n\xb4~(\x1b\xce(\xb3l5\xec\x84\xd3\xe1\n\xd7\x97\xf7\xc7e\xb1\xce7\xd7\xb0:q\xd0\xa1\xd0)G\xa6u\xf8ZV\xf2\x07\\\xac\xb6\xa4\xba\x96\xdd\xc2J\x03$WV\xe8\x1df\xa5\xce\xf2\x15\xdf\x06\x01h\x14\xeb\x89.Q|\xab\x8f\x9d\xa0Y\x01\x17g\xb0\xb4k\xb64\xeft\xc7@\x8fO\xa9y/>\xb0\xcf;\xc7:\xa9\xe2\xb2_\xfbZU\x01\x94u\xfe\xa4\xce7\xaaz\xdbS\xd2\xa4d\x8d\x16\xa4+\xca\xad\x82^V\x0e\xd1\xc3G\x06(x\xf0\xc1\xe1&\xa7\xdc\xdel\x1c\x1d\xe4\x03e\xf2\xc1\x1a\xc0\xaf\x88d\xdb\xa4\xe8\x9e\xb6t\xb8\xdb\xe4:a\xe1\xc7\nrO+\xbc\xbc\xc9i\xbd\xaciY\xd9\xa35\x8f\xb9\xe5\x82l\x0dk\xb9\xf7\xa0\xdc\xb3\x1e\xb8u\xecc\xb6@f\xf4MN\x8fx\x1b\xe5\xf6\xa9\xc2:\x93\xfb\x9f\xb6v\x00\xd6\xac`\xe0\xefi[\\\x99fg`\xb1\x9f\x91\xa2n*\xd22\xa8\xb9\xa2\xb9\"+0EQ\xfc\x99\xd4@>\xde\xe5E\xbe\xc3[\x84\xb9\xdb\x82\"\xb0?M\xa1\x10\x00\xe1s\x98\x0b\x04\xe6\xc5\xc6Z\n\xb6\xf4\xd0[\xa6x\xa0\x1a\xaf%\xdfR,I\x05%\xc5J\x10.\xb3\xb2\x10\n\x02j\x80\xc4\x06\x82\xd8X\xe6\x1b\xe3Xg\xe7\x91\x93\xd4\x98\x9e=\xf3NW\ny(\xcd\xca\nN\xea+9\xe3\xea\xeel\n[\xbe\xf8S\x0ejE\x9c\x04\xdf\xd9qAZe\xf5 \xba\xe7\x97\xaa\xf2)\xf2\x8dR,>%e!\xe0\x94\xcc\x16\x02\xae\x07\xd8>\x1e\x9e\xe0\x17\xcaY?`\x86\xf7\x8f\xeb\xb3\x8e\xea\xd6\xe9\xa3\x1f\xd1\xfd\xc7\xf3\xc1\xa3\xf9\xf0\xb1\xdc{$\x0f=\x8e\x0f\x1d\xc5\x03\x8f\xe1\xee\xe5a\xea\xf1\xdby\xe0\x8ez\xd8\x9eu\xd06\x8f\xd6q\x8f\xd5Q\x8f\xd4q\x8f\xd3\x93\x8f\xd2_\xe9\x18m=B?\xfe\xf1y\xcc\xd1\xf9\xb1\x8f\xcdQ\x8e\xcc\xee\xe3\xf2\xec\xa3\xf2#\x1e\x93\xe7\x1e\x91\xf9\xa1X\x91\xa7\x1d\x8f\xa3\x1e\x8d\xcdc\xf1\xa8#\xb1\xff8\xfc\xf5\x8f\xc2\x93\x8f\xc1_\xf1\x08<\xeb\xf8;\xeb\xe8k=\xec\xce=\xe8\xb2\xa3\xad:`\xb5Cn\x94\x03n\xdc\xc3m\xc0\xc1v\xf0P\xabk\xb9\xa0 \xba\x14\xcb\x9e\x0e'W\xa9\x16\xed\x16\xdd\xd4wqa\xbb)\x9b\x9b\xed|\xa4\n\x0d\xb5Fu\x93\xdd\xb2\xcf\xb6e\x86\xb70\xf6,\x8b\xb7\xea\xe7\x05U\xb4\xea\xb3\x9f\xaa\x15\xa9\xde<\xa8z\xac\xa2Y\xaaZ\xe5\x0b\xf4\xe9\xfc\xed\xc9\xf9\xf2\xcd\x9f-z\x98\xf2\xf0\xe8\xe2\xd8\xfc\xf1\xed\x89\xf8\xb5\xd5\xea\x9c\xc2\xec\n\x9d=w\xbb\x18\xc7\xde]V\xb4\xd3\x98KV\xef\x05\x12\xd5\x97\xc5\xe2\x04\x9c\xa3\x8bch>\xb6\x9d`\x05m\xefW\xf2\xb0\xf7W\x07\xdc\xd7\x19\x81\x93\x0d\xcf\xc2\xf2-k\x8b\xc3\xfe\x9f\xed\xd7\xac\xf2\xc6\xe7b\xc0)e\xe5\xef\xf2%\x08*\xd5\xbelv\xf0\x05\x1fx\xa3`L8\x02\xf5Q\xff\x11&\x04j\xf5\xa0w\x1e\xfa\x871\xb4\x88\xfe\xf2nO\xf9x>\xf2\xd1\xbc\xe3\x9d~\xf1t\xbaG|,_\xf8a/\xf8I\xfe\xef\x93=\xdfy}u\x0b\xb3\xd3\xe7}\xb2\xb7;\xa8\x1b\x9a4\x87\x9f\xfb\x1c\x0fwnP\xd2kcq\xc2\x98\xe2\xd5\xee\xf6`\x9f\xe9\xbb\x1e\xe4\xb5\x1e\xee\xa1>\xc37}\x86W\xbae\xc1\x88\xe8{\x1e\xd7\xeb<\x9a\xbf\xb9\xdf\xd3<\x9a\x8f\xb9\xcb\xbb|\x8e_\xb9\x95\xfeHC\xbc\xc7\xa7\xfa\x8d;}\xc4'z\x87[\xc8\xd7\xe3\xac\xe3\xc8\xbb\x83N\xf4\xff\xee|\xbdm\xed\xfb\x8d?\xefy\xde\xde\xe0\xdd\xad\x883\xfd\xbc#xx\xcf\xf3\xed\xd6F\xb9\xbe\x19\xce\xf4\xe7\x16\x0d\xdd;\xb9\xcc\xf0\xdc\x1etKvxk{\xfd\xb4M\x97\xcdp\xdfl\xf3\xdb\x9fmu\x9d\xe4\x89\x1dRY\x9f\xf7\xb5\xbbn^\x8f\xeb\x11\xbe\xd6}\xb7\xb4\x99\xfe\xd5\x83\x9e\xd5n\x9f\xea!ojk+\x84zP\xfb|\xa7u\xaf\xe9\x19\xfe\xd2\x01\x9e\xd2\xe3}\xa4-\x1e\xc9>\xbf\xe8H\x1e\xd1\x96\x9c{#e\x96\xff\xb3\xee\xef<\xc7\xd3\xd9\xe2\xd9<\xcb\xa7Y\xf7a\x8e\xe9\xbd\xec\xf4[\xd6\x9d9u_\xe58^\xca\xd1\xfc\x93\xe3z&\x87\xf9${\xbd\x91\x03\xfd\x90C<\x90\x0dW]3\xb7P_\xd2a\x7f\xe3@O\xe3\x00\x1f\xe3^\x91c\xfa\x15\xcf\xf2(6=\x88\xe3\xf9\x0e\xc7\xf3\x1a\x9e\xde\xbb^Oa\x9f\x8f\xb0\\\xbe[\x92\xc2d\xa6R \xc2C;\xa2-\x07B\xb2=$\xb3I\x11\xd6\xb9J)\x98\xae\x85\x03\xf1\x99<<\x95\x96\x91\x9a\xfc\xad!\x85\x1a\xbe\xddz\xccp\xf8o\xb8\x95|)\xb7e1\xc8\xbf\xe5Q\"\xe3\x1e(\x07b\xc0\xf5j\xdf[\x9a[_\x90\x8c{,S-\x84?\xb4\n\xdf\xdd0\xda\xe4w\x84\x8f\xd1\x8a\xd4\xb54\xa9\xb3\x03\x98\"\xb0\xef\x15\xb9\xaf\xb8\xc3\x1c\x9b8[\xfc\x00.\x96\x9f\xc5A\xcc\xeef\xd2\x99M\xb5.\xeb\xf8!\xbc\x8b\x9c\xc41Z\xee_l\xc9\x1d\x91\xa4\xf8!\xd6\xd8E\xbek\xb6\x98J\xaf\x9d`C\xad\x1a\xf44p\xbc:\xefR\xe8\x8dWz/;T\xbd\x16\x80\x0d7QR\xbb\x07\xda\x02]\x90b\xc5\xd1gz/\x00h\xcd\xf9\x8c\xde/\xf9\xef\x81\x03\xd1\xcd\x04\xd3\xca\xbb\x14\xb9\x89\xdd\x97\x95@\x0d\x15\xab\xbck\xc5v~78\x14z\xbd\xd3y\x1b\xc1\x9f\xd4\xe6l$\xbf\x11\"|\x9eF]\x16#\xef\xdc\xd8\xe0Z[\xb7zu\x90\x8fe\x99U\xbfa|S6\x94\x07\xd8\xe5\xb6\x0b\xa1h\x89\x0e\xee\xc5\xd75\xcaa/\x0b\x1a\x08T\xec5jX|\xe3\x86\xcd\x1a\xdf\xe3\xfa'\x9eQ\x0b\xbc\xe1\xfb|\xd7\xecPSpB\xcf\x1a})\xab\xcf\xe8\x8b\xb0\x00\x82 \x8b\xde\x9bn\x83{R\xb1B,\xb4Z\x98\xb1\x8dg\xd7\xe1{\\_\xd5]\x81q/\xca1\xce(\xd8\xbbe\xb8cY 0T::\x18\x1evC\x92\xff\xd5n$\xb3\xba\xd2\xbc \xc3\xdb\x00#-So1\xc5@\xda|\x00NeEhS\x15\xd2\x0fZj\x13\xdc\xfe\xcaq_%d9:\xd5\x11\x93\x0fW\x17\x97\x16\xc3\xdc\x96\x14\x1bz\xcbv\x80u~\x0f\xe3\x9c\x83e|5#{\\aJ w\xc8\x94m\xa0L\xa1\xb1G\x1dl\x0bP\x1b\x87ou\x19\xbc\x04f\x14\xd3\xcd9\x80\xc4\x14\xb4}\xb9\xe7s|u\xc01wV R\xf1\x9b.\xa4\xdf\xa2&P\xac\x9cz>7$\xc3\x1c\xba\x00BG_\xdd\xffPo\xfan\x90L\xf5W\x04\x18\xf7^8\xfbt\xb8\xeb\xde\x97\x9b~\xc6\xdc\xa3ZY]\x1c=\xe8jN\xb5\x8c\xe9\xb2\xbd\xc1*@\xf2\x07\x11I\x97\xed)ix4\xcb4)`H\xbal\xaf'\xe9\xd7p\xd9\xde\x84k4\xc4\x85\x19\x9a<\xb9 h?[w$\xe5\x1duQ\xff\x85\x16\xb2t\x85A\xba\xc2\xc0*-]a\x80\xd2\x15\x06\xf6|f\xd2@\xa4\x90\x002\x88\xf5\xcb`\x82\x08\xa4\x194\x11H3\xc8\"\xae\x89\xee(j4\x1a \xa4\xb8d\x12H\xd1(%\x90\xfc\xc4\x12H\xd1\xe8%\x90\xd2\x15\x06\xe9\n\x03\xb0\xad\xa4+\x0c\x944\x8f\xe6b\x88\xa3\xe9\n\x03?5\x06\x92/x\xbf\x9f&\x03)]a0\x8eV\x03)]a\xc0\x93\x8f\x86\x03)]a@g\x10v \xa5+\x0cF\xd2}\xcc\"\xa7+\x0cbP\x84 \xc5%\nA\n\xa3\x0bA\xf2\x92\x86 \x05R\x87z/\xa7+\x0cx\x8aI5\x824\x8bpdHKW\x18\xc4\xbe\xc2`\xf8\x84\xd2\xb3<\x9b\xd0\xa0\xb4Zw\xaf\xf0\x81\xc2\xe647Dh\xd2\x8e\x8a\x87qq\xd0|\x04\x8a\x80x\xad\xf2\x1b\x8dH\xe1cP\\\xe6\xfb`\xd2\x04\x00\xef:\x03\xc5\x13\x14\xc7\x90\x0b\xc9e\x87_\x91\xa24b\xa6:O\xcaz\x89\x06_w\xd3`\x10:.s5\x864-?\x93B\xec\x0fP$\x19R\x9a\xad\x06\xec\xf4\xca3\xd6\x14\xbf\x8f\x9f.O\x0e\xf9\xbe&(\n-\x92\x8d\x0btZP1m[\x1bBo\xeeB'\xf5\xe4\xd5\xf9\xa6\xc0\xb4\xa9H-\x03\x18p\x1dgSnJ>W\x14z\x02p\xe7D\xb6\x06O\x82\xefS\xf9\xfe\x1b\xf9\xf2~O*\xbd\x1b\xb5\xe6\x92\xa1\x87\xf9\xbb\xadD\xa0\x8ei$5\xb4\xc7\x0f|\x1f\x12\xd6\x16\x99\x93\xbd\xbd/\xf3}\xcb\x91\xca\xf7\x9d\xedhG(~\xa1EM\x80/\x9c\xf3\xc6\xc6\xd2\n\x1e\xcc7\xe5\xca\x126\xac\\\xb5\xb1\xb4E\x9cwn:\xe3&q\xa5-\xbbbj\xad\x18\xc4\x10\x91!\xe3\x13\xc2E\x13\xc2\x95\x10\xae\x84p%\x84+!\\ \xe1\xea\xa7\x84p%\x84\xcb\xafq$\x84+!\\\xbd\x94\x10.\x91\x12\xc2\x95\x10\xae\x84p\x05\xe5\x9c\x10\xae\x84p\xb5)!\\ \xe1\xd2R(z\x91\x10\xae\x84p\xf9\xc6\xc8/\x87p \xa33x\xe7\xc8\xab8\xdb_\xc1\x16\x00^\x19\xe0\x89GZ\xd0A\x93\xd4]6\xa07\x11\\\xf3\xda\xca\x04<\x85\xf7\x8f\xf0Sg#\x0d\xbc\x08\xcb5\xbf\xe1B\xdc\x12\xab\xc99j\xe8-\x0f\x0e`\\>\xa4\xc4f\x86\x8ba/\xef\x9f\xd6\nP\xb2@'8\xbb\xed\xe0\x12\x19\x14@\x00\x17\xc6\xcdY\x98\x0fR\xd3\xc3\x87\xb6\xd7\x95\xb2\xbd\x8b\xef[l\xde\xf2\x0b\xbdQNQ\x99eMe\xfa2\xbe\xe1n\xa8w\xa4\x903D~\xa7\x17\xe8\x994}\xb67\xd7\xf2\xf7\xec}fXHx\xc0\xe25\xa9*0Ha X\xe4;~\xda\xec\x02!\xec\xf1\x03<[\x13\x1d(\x94\xe9\xcbm\xb95orS}\xc0\xbb\x9fwdW\xda@\x8b\xd1n\x90L\x90\xf4`\xed\x8e\xc9EI\xc9\xcb\xac\xdcq3;\x0cH\xd9?H\x83]\x8c\x96\xff\xe9\xe8\xfc\xe3\xe9\xc7\xef\x0f\xd9\xe2\x90ms\xb8\xe5\x97\x89\x87\xb8\x03\xdb\x07D\xeeE4qrO\xa59\xbc(\xa9y\x99[\x86\xb7[\xbe`\xeeJ\xeb\xf5F\xca%\x06\xf0\xe65+\xf8\xb5\xf4;E\xcfj\xa2\x8b\x94\x11\xab79\xbdmn\xf8\x92\x00\xe0\xd5\xcb\x0e\xd5z\x99\xd7uC\xea\x97\xff\xfa\xea\xbb\xef\x9e\xab\xad\xce\xc6\\\xd9\xd0%\xdc;\x1f\xd8\xfe\xfe\xbbS\x8d\xcbdE>\x12\xff\xe2\x97\xe1\xcb\xcb\xeeA7\x83E\x19<\xc2\x95\xf8\x06|\xd1/J}\xf4\xde\xb4\x18\x1ah\xae|\x19\xbf\xc5\xbd\xd9N\xee))\xea\xbc,\x96`\x0cO\x98X\xc2\xc4\x12&\x960\xb1\x84\x89%L,abzJ\x98X\xc2\xc4\xfc\x1aG\xc2\xc4\x12&\xd6K \x13\x13)ab \x13K\x98XP\xce \x13K\x98X\x9b\x12&\x9601-\x85\xe2\x1d \x13K\x98\x98o\x8c<\"&&l\xdd\xc6\xd9\xc4\xb0;\x83A\xa3=\x9d\xc8_y\x7ff\xb8\xe8\xb0\x88\x1b]\xef\xe6&m\x03\xa8\xfarK\n\xb1\x1c\xc1\xdd\x01j>p\xc5\xaa\xbcU}\xc1\xc6%S\x80`\x01\xab \xbfU\xb8/\xcea3e\xa34\xc3\xc5S~\xde\x03'\xb5\x15\xc0N\x86}\x9e_\xe7 W\xec*B\x8a\xb2XfUN\xf3\x0co\x97\xc9\x18\x9f\x8c\xf1\xbd\x94\x8c\xf1\xc9\x18\x9f\x8c\xf1\xc9\x18\x9f\x8c\xf1\x96\x94\x8c\xf1\xc9\x18\xef\xd78\x921>\x19\xe3{)\x19\xe3EJ\xc6\xf8d\x8cO\xc6\xf8\xa0\x9c\x931>\x19\xe3\xdb\x94\x8c\xf1\xc9\x18\xaf\xa5PCk2\xc6'c\xbco\x8c$c|tc\xfcC;\xea\xf2MQ\xaa\x0e5\xbd3\xdb\xe5\xfd\x1b%\xfc\x14\x0fE\xc5/F\xec]%\xc8\x9a\x00o\xb7\xad\x13\x0e\xfb\x17\x95w\xa45\x14\xe1\x86\xdeN\xbb\x92\xb3\xf5\xbai?\xb4y\n\xb4\xf2\xdb\xa0`\x0d\xbd-\xab\xfc\xef0\xbf*\xc2\xef\xea\x1a\x88\x98\xa5\x8e(yx\x07\x13#T\xe9@:\xb0\xecJv\xccc\xad\xban\x1d*:O\x9fIa\xe8\x8c\x93\xbe\xff\xbe\xc4\xfe\xb5\x9d]D\xb6\x9e\x1b\x95\xf2\xbb0u\xd2\xecV\xec\x98\xe2\"\xb5\xd6\xf5I\x11\xd7\xf7\xa0\xea\xbc\x9e\xfanN\xb4\x14W\xf1eeQ\x90\x8c\xb2\xe5\xaa\xcd\x90\xc7MS\xaf+R\x04n\xf3\xcf=\xfda\xe8\nN\xa6\xb8\x955\x1f\x1b0\x92\xb4\xa1){\xbb\xa6\xb8X\xe1J\xa8E\xad\xd9\xe9\xa6*\xf1*\xc35/\\?\x8a\x9b=N\xdb\x9b6\xfc\x1a\xf5\xc7j\x93\x1ee\x93\xfa<8\xf4 u`O\x03F\"\x9f\xcd%*\xde4\x845\xc5\xc4\x99\"bL\x03\xf8\xd2,l)\x1e\xae\xe4\xc3\x94&\xe2I\x93\xb1$\xb0_[Z\xcb\x89#M\xc6\x90@\x851\xe49\xf1\xa39\xd8\x11j\xf6\x86<\x17n4\x053\x1a\xc2\x87fcCA\xb8\xd0\x18\x0ch\x16\xfe3\x03\xfb\xb1.+Q1\x9e\xd8\xf8NDl'\x04\xd7\x89\x88\xe9\xb8\xf1\x9c\xa8X\x8e\x1d\xc7\xb1\xa8\xf8\xb6Uj*~\x03X\x8d!\xce\x86\xddL\xc6m\xac\x98\xcd\xc0V<\x80\xd5\xf8v\xe9X\x18\x8d\x1b\x9f\x19*\xc1<\\\x06p\x98\x9e@\x1b&\x13\x05\x8f\x99\x87\xc5\x18\xb3\xc4\xdcpcb0\xd4\x82\xbf\xcc\xc3^<\xd0\x82\x13s \xc0[l\xc6\xd718\x8b\xed\xfb\x9f\xedu\x9f\x88\xad\x84U\xde\x8f\xa9\x0c\xd54\x00K\x19\x85\xa3\xe8F\xa7\xd9\xf8\x89\x07;\x19\xc2M\x861\x13G\xab\x84c%~\x9c\xc4\xc4Hf\xe1#A\xd8\xc8\x14\\\xc4\x8aC\xf8\xf1\x90hX\x885\x7fm$\xcd\xc2?L\xbcc\x0e\xd6a\xc56f\xe1\x1a&\x8e\x11\x17\xc3\x18\xc0/L\xb3\xae\x89[\xc4\xc2,\"\xe2\x15\xb1\xb1\x8aP\x9c\"\x00\xa3\x08\xc6'\xc2\xb0 \x8b\x19\xdf\x96k\xa8\xad\xd9\x87G\x04c\x11A8\x84V\xf8\xb8\xf8\xc3,\xec\xc1\x865\xc4\xc4\x19bb\x0cs\xfa;\x00[\xf0\xe3\n\xdd\xe2\xef\xd6\xaa\xa5\xc1pB\xb0+Gx\xab\xc9\xa1\xad\x1c\xc1\xacb\x06\xb2\xb2\x05\xb1\xa2\xe3\x03XE\x0c^%\xda\xa9w\xc2\x9e\x15\xb4\xca\x0cSe Q\xd5\x0fOe=+\x0e\x0d\x99\x98!\xa9\xe6\x84\xa3\x12a\xa5\xb4\xb2\xf5BQM\x08C59\x04\x95+\xfc\x94\xb5}\x1da\xa7l@R\xccpS\xbePS\x03\x9e-4! \xbd\x94\x90\x84\x84$$$!! IHHBB\x12\xb4G\xbe]:! 4! IHHBB\x12\x12\x92\x90\x90\x84\x84$$$!! I\xf8\xd5# 6\xcf\x849^ \x16?\x84\x88>\x08\x16K\xd8\xac@@\xa1A\x80h2\x95\xf6R2\x95&Si2\x95&Si2\x95&Si2\x95j\x8f|\xbbt2\x95&Si2\x95&Si2\x95&Si2\x95&Si2\x95&Si2\x95&S\xe9/g*\xb5\x87i\x89\x1c\xa2\x85\x92bE\xaa]^\xd0\x05\xbe\xc9\xf2\xc5\xc9\x1d)hp \x0c\xfeJ\xd7\x15\xe6\xc1\x0cSZ\xe57\x0d}\xecX\x19\x9f\xc9C\x8ccb\xb4\xf3f^\xac\xc8\xbd]\xd0MYn V\xe3\xa2\xf4\xba\xf4)\xef\x81#\xd9n\xe01P\xe7\xc5fKX-_\xc0\xb6\xb6\xc7yu\x80p]\x97Y\xce\x0fBbOB\x84}\xbdxj\x0e\x95v\x82p\xf9`-\xaa\x11\xee\xacHhE\xee\xc8\x965/\x84x\xa1\x14g\xb7\xea\xb6\xa6\x04uQ\xc8\xfd\xe7\xa4\xde\x97EM\xde\x90M^\xbc\xd9\x96\xd9\xe7\x83\xf6\xb7\x93b\xa5\xfdr|K\xb2\xcf\x97\xf7l\xd0k\xdf\xbf%\xdb\xfc\x8eT\x97\xf7\xad\x86\xfa\x1eSR\x1d\xf4\xa2\xb7\xa0\x1d~`\x13\xe2o\x0d\xa9\x98\xfa\xd2\xd4<\xbe\x0b\x9fh\xbc\xe6\xb5sL\xb7-\x1a<\xb8{c\xca:\x08,\xdd\xaf\x8d\xa0\xd0\xcf\xb4\xf1b\x8e\x94\xc7\x19#\"\x12N\xb3\xdfTxE\xdap8\x1f\xcaU\xb3%?\x82q-\xb8\xbd\x98\xc6\xe0\xa9\xb9X\xacU\x13?\xde\xef\xd1\x8e\xe7'[P\xcd\xd6)i\x98\xd8\x9f\xb1\x11U\xd4M-\xa59r\xeb\xb5j\xeb\x0d\xd0\xab~k\xd0am\x0c\x9f\nS]mf\xd3\x8e\xdd\x8b\xbc\xc8x9\xa4+\x03\xfav\xf1\xfb\xdf\xb9[\xfcl\x8b\xa36\xb4\xdb\xd6tA(\xec\x16\xbc\x13\xa4;\x8b,\x0f@E\xfc\x91\xdc|\x1a\xc5\x87A\xbc\xd6\xd3R\xb4\x16\xae\xcb5\xfd\xc26F\xb6\x86\xec\xf7[0 \xf0F\xc4[\xf4\xa4,^\x08!OPV\xeev\xb8X\xa9@\xc7\xaa\xe1\xd5P~\xa1\xad\xdbO\xb7\xc2\x88\xb3\x8drf\x112\xf9L`K\x1aY-\xd0)7\xa6\xe2m]*\xe2Xmz\xe2K\xb4\"\x94d\x94\xed\xf2\xdc\x02\x8a\xbb*\xc8\xaa1\x15\x01vf\x84\xd1&\xbf#E\xd7`\x1cVP%\xca\xa2\xc0\x07\x15\xcc:\xda6+S!o\x08)8 \"\x0eU2\xc3\x03\x94S\xde\xee\x8a\xb8\xbe=U\xe0\x13-\x14\xd4\x955\xafQ\xd9\xd0\x17\xe5\xfa\xc5\nS\xd2\xe9,\xb28\x979\x9bsj\xb4\xaf\x1f\xc0\xb7%\xef\xe9:\x15\xc1\xd9-\xd3J\xc5\xc9\xb2\x95\xcf\x87\x03\xb9\xcf\xa9\xea\x87\x138IY\x89^\xb0\xf7\x03\xc6\xe7[\xa6.gl\xbd:\x842\x03n!\xaaQ\x03>\xc1\x1bp\xd5\xbe\xb9P\xde\xb4\xf4\xc4\xb6\xdc\xe4\x99Z\xc9\xb6\x0f*\xb2+\xef\xc8\xaas\x1c\xbbx\xfb\xa7\x9e\x85\x84\x9f\x0f\xf2Z\x1c\xe9\x84m\x9e\x9b\xc1\x0fZt\xa6\x9d)\xf4\xb6*\xbf\xb4\xde_\xa3\xfc\x93\xfa\xab\x98}YB\xe2\xe0+}\x92h\xeb\x90\xd4\xf5\xf2NX\xb1\xf7\xa4b\xa2\xc9J\xb5c|\x12\x96\x80\x15\xca\xd7\xd0b\xa2B5i{\xb5\x1f\xa9oh\x11\xef\x95\x8b\x9b\xfe\x14]\xa25\xec\xb7\x13\xb3X\x97\x02\xea\xc9\x8bl\xdb\xac8\x16\xf5B\xbf\x7f\xbfn\x98\xceQ\xf3iF\xf9\x02\x91S\x18\xec\xdc*\x8fiY\xb1e\xb7\xd9\xae\x10nh\xc9\xb4\x11\x08\xd6'\xf3\xa1r\xb2\xcb\xa5j \xeen\xcb\x9abj\x8cWM\xbf\xb4k\x97\xd4J pj\x85\xc3@CD2\x80\x9b\n\x10\x8f\x08\x10\x8d\x06\xe0$\x01\xd8\xacR\x81\x14\x80X\x04\x80a\xf8\x7f\x12\xf8\x1f\x17\xfaw\x02\xffqa\x7f\x07\xe8?\x13\xf27\x9a\x9bZ\x00\xff\xb8p\xffL\xb0?2\xd4?\x03\xe8\x8f\x0d\xf3G\x03\xf9\xe3B\xfc\xd1\x00~?\xbc\x1f\x0d\xdcwA\xfbs\x80}+\x90o\xb1\xa6\x99\xeb\xcd<\x10\xdf\x02\xdaO\x84\xec-\x06\x14\xe7F\xe94\x9e\x0c\xef\xa0\x13\x81\xfa\x0e\x98\xb7\xb5\xef7\xfe\xbc#C\xf4&@\x1f\x01\x9e\x8f\n\xce\xeb\x9b\xe1L`^4\xb4*q\x0e\x14?\x88E;`x/\x08o\xe2~\xe1\x00\xbc\xf9\xed\xcf\xb6\xbaN\x82\xdeC*\xeb\x83\xdd\xddu\xf3B\xee#\x00\xf7>\xbe2\x13l\x1f\x84\xda\xdd@\xfb\x10\xccnm\x85P\x88\xdd\x07\xb0\xeb\xf0\xfa\x0cp=\x00Z\x1f\x0f\xac[`m\x1f\xa8\x1e R\xb7\xe4\xdc\x1b)Q\xc1\xf4\xc8PzT =&\x8c\xee\x04\xd1udR\x07\xd0\xe3\xc0\xe7\xd1\xc0\xf3\xb8\xd0y\x18p\xee\x85\xcd\x03A\xf3\x10\xc8\xdc\x00\xcc\xcd\xdcB\xc1\xd3a\xb0<\x10*\x0f\x00\xca{E\x8e \x92G\x86\xc8\xe3\x01\xe4\xf1\xe0\xf1\xe9\xbd\xeb\x85\xc6}\xc08,\xdfv\x1d\xf6l\x8bU0CE\xf3\xf0M\xd9P\x84\xd1~\x8b\x8b\xa23\xb6\xf2\xde\xe4\x86\xe4\\\x86w\x12\xb2x\xb4\xae\x85\x1b\xce\xf8\x7f\x1aR=\x1c\x81!\x9e\xe5+A\xbe`\x88#\x96\xf1\xf4V\xda\xb8\xc1\xcc\xd2\x8b\xe9\xa4\xdaOY\xcd\xd1\x17\xdc\xa1\x07\x03\x0d\xe9\xaa\x9c\xcc\xa5\x92\x7f\xf3\xf9!\x11\x16\xfe\xd5K\xe53!\xed\xfc\xecX\x0e \xd8h\xbc\xcd\n\xf7\x9b\xd0\x87\xd1\x8d*\x82\xb5\x0d\xb4j\x1fe\xb4\x82X\x7f\x04lY\x18\x81\xed%r\xb7\x84h\x05\xf9\x81\xa7\xaa\xc7p\x18\x9f4\x82\xf6-\xa2fT\x8cwv.\x17\n\x9eE;\xe4\xd9\xb3\x85\xd6@AV\xe2>(\x87\xa6[\x88\xe7\x00t\x9a(\x1b\\\x87bCv\xc8\n\xdb\xa1\xf9\xd0\x9d&\x0do\xeb\xd2\x80\xefP\x04\x08O\x13g\x00zh>\xa8\xa7I\x13=\xa7g\x12\x13\xdcC.\x80\x0f\x8d\x02\xf9\x90\x01\xf4\xa1\x10\x93\x8e \xf8!\xef\xb8\x8f\x08\xfc\xa1!\xf0\x0f\x8d\x02\x00\xd1<\x10\x10Y\xf62\x14\xd2\x82\xfd=\x0d\x0d\x82\x82h.0\x88\x02\xc1Ad\x00\x84h\xa8.\xae\xfb\xd5\xe6\x81\x85\x9a\xb0\xb9\x88\xa1\xb1\x98(?x0D\xe4\xda!\xd0\x00S\xcd\x85'\xa2\xa1\x96D\xde\xd9\x83\xe2b\x8b\xc8\xe3j\x1c\x13cD1qF4\xecp<\x0boD\x111G\xe4\xc5\x1d\xd1T\xec\x11\xcd\xc1\x1fm-\xf6\xb0\x97\xf7\xc8\xd9\x9d\x8fg\xe0\x90\x16Y\xb0v9\x1d\x90\xe7\xe1\x91\x16q\xcd\xde\xe9\x84\x1c\x1b\x97D\xf3\xb1I\x14\x1f\x9fD\xf30J4\x0f\xa7\xb4OQk!\xa3\xa1\x97(:\x82\x89b\xa2\x98(\x08\xc9D1\xd1L4\xe8\xac<\x0f\xd5\xb4\xcdq\xab\xc3r \xd6\x89f\xe3\x9d\x16\x816\xb7\xe5\xc9((rQ\xc9=[\xfc\x80\xfbr\xc8\xfe?\x11\x19\xb5-{N'f_9\xe6\xa1\xa4\x9a0\x8e\x99Z]\x99\xa3\xa0\xa5(6b\x8a\xac\x0e\xcd\xb3\x91SM\x1a\xb585\xcf\xc3R\x91\x0fbDC\xae\xcd\x01\xb8*r^\xe8\x1f\x8e\xaf\xbae\x18\xf6\xf5YX+\x1a\xd1\x18>\xcc\x15y\xeb\xed\xc5^\xd18\xfc\x15Y\x1d\xf4f\xe2\xb0\xc8\x87\xc5\"\x8f\xe3\xb3\xcf\xf5y\xa0\x95B\xb1Y\x14\x80\xcf\"\xab\x0b\xf4,\x9c\x16\x85a\xb5h\x12^\x8b\x9c\x0d\xe3\xc5mQ<\xec\x16\xb9Ka\x8c\xb4\xa88.\x9a\x89\xe5j\xa2lN\xd2\x91\xd1]\x14\x19\xe1E\xc3\xae\xd26gi\x9b\xbbt,\xc4\x17\xc5D}Qt\xe4\x17\x05\xa3\xbf(\x04\x01F\xe1(0\nD\x82\x91\xdd}\xda\xeeP\x1b\x8e\x1b\xfa\\\xa8\x83\x91a\x14\x86\x0e#[5b\xa2\xc4h.R\xac\xc9\xb2\xb8V\xc7\xc4\x8eQT\xfc\x18\xcd\x1e\x0f^\x1c\x19\x05`\xc9\xc8\x83'\xbb\x10\xba\x10\x18T\xf9LH\x1b\x0d\x83\xf6\x9c\xf4\xea\xd1\xf0 x\xf2-\xc5\xc9\xfc\x91\xbd\x93Mlp\xf0 \x18\xe2% \xc9\xf0\x95\xf4\xcav\xf8M\x8a\xef\xc6yOB\x1a\xc2%\"xRBr\xfaSZ\xca\xd0\xbfF\xab\xdf\xcf\xa8\x7f\x01\x1b\x14\x02\xec1\x02\xd2#y\xd5\x95F\x11$%x\x99\x01\xf6\x8198+\xd4\xcf_\xf6\xbf\xff\xa6\x9b\x1f\xed\xc4\x90\xbf\x8du2\xe5\xf2\xaf\x04\xa6q,\xebxA1%\xa3\xe7O\x07\x8dH9v\x0f+\xbf\xebs;\xec\xec|\x83\xdf\x0d\xf9\xb2\x07\xd4(\xbc\xe1\xedrl\x1d\xd0\xb60n\xe8\xed\xdf\xdb\xf6\xfd\xbe\xc2#b'` A\xfc\x9d\xaf\xd1z\xb3\x05Q\x0ehrL\x9b\x0f\x18%\xc7\xb40`\x08\x0c\xdaF\x1b%\xc7\xb41\x00\xd0L\xf0'2\xf03\x03\xf4\x99\x01\xf8X\x16\x8c\x88\xd0N\\X'\x1a\xa4\xe3\x87s\xa2A9\xc91-9\xa6\x8d\x80\\\x92c\x9ahhU\xe2\x1c0%\xc4W+9\xa6))9\xa6\xa1\xe4\x98\x96\x1c\xd3\x92cZ,\x98\"\x1aD\x11\x17\x9e\x08\x83&\xbc\xb0D $\x11\x02G$\xc7\xb4N\xd6,\xb8!9\xa6\x059\xa6q\xbb@^Y-^\x0eC\xa1\xe9I`c\x95\xb3\x17:\xaf\x88M\x85\x0b*}\x19\xf6y%\x1c\xd9D\xe7\xed\xab\xa6\xe0>&kT4\xdb-\x0f\xb5\xaak\x98\\\x82\xda|\xab\x92\xd4\xc5S\n\xe7S\x0c\xf9uuA\xcf\xe0x\x93\x95\xc5JX\x01X\xe7^\xf7L|\xbdN\xdd\xe1\x07\xe1jCK\x94\x17\x82\xaaN\xba\xe2\xc3\x16i\xb7\xads;#wb\xa9\xd1\x9eT\xbb\xbc\x06\xdb6-\x11\xb9'Y\xd3\x9eZ\x984\xb1_I\xbf\x1b\xbe\xca)%g5\x196h\x1e\x99vJ\xea\xb7n\xf2J\x90j\xb0\x97\xe1\x1dO\x00\xd9d%MV\xd2d%MV\xd2d%MV\xd2d%MV\xd2d%MV\xd2d%E\xc9J*R\xb2\x92&+i\xb2\x92&+\xa9\x96\x92\x95\xb4M\xc9J\xfa\x7f\x99\x95T\xb7\x90\x9af<\xb8!k\xc5\xa3f\x08c)\xbdE7%0`e\xa0(\xd2\xea\x7f\xc2R'\xe4\xb1Q\"\xec{\xed\x86 \x97m@\xfc\x90\x82+\x92u^C\xa3\xf0\xf7\xff\xd6\x90\xea\x01\xfevY\x1b9\x0b\xf3{\xc8\x89\xff3\x9e\xd5\xcdK\xf5\xc8dn\xc3\xb2\xa9J\xb1\x9c\x93\x0c+\xa7\xe7}\x87\xc5S\xfd\xca\x12nd(\xe0\x88\xcb\x06\xda=sz$\xfbNs\x90\"ZE!\x0d\x87\x1e\x89i!\x85\x14\xcdN\ni0\x00\xc9,\x9b)\xa4X\x96SH\xfe0$\x13\xad\xa8\x90&\xdbR\xedm\xe7\x0fF2\xc3\xaej\x95\xe6\x0dH2\xcf\xc6j\x158\x18\x94d\x9a\xbd\xd5*h0T\xc9lK\xac\x14\x12`\x8f\xb5~\x19l\xa3\x854\xc3R\x0bi\x86\xbd\xd65\xd1\x1dE\x8df\xc9\x85\x14\xd7\x9e\x0b)\x9aU\x17\x92\xdf\xb6\x0b)\x9a\x85\x17\xd2PP\x93y\xd6^\xfbZ\xe1\x08l\x12h\x07\x864\xd5\x1al\x15\xe6\xb2\x10C\x9ah'\x86\xe4\x08q\xe2U)\x06\xc3\x9c\x84i\x1c\x13\xad\xc8\xf6\xc5t \xd8\x89\xbf4\xf3\xec\xca\x86\xb8\xa1\x90'Ql\xcc\x90\xe6Y\x9a\x0dq\\\xa3\xb1*\x0f3\xad\xcefN\xd6\xe0'\xf3l\xd1\x90\xbc1?\x06C\xa0\x04X\xa7!9\")\x8c\xb0TCr\xc9\xb1\xb8\x97\xcf\xb2]C\no\x1c\x9f\x1d\x1b\x92\xaf\x15\xbc6mH#,\xdb\x90l\xce\xf73\xad\xdc\x90\xbc\xe1Q|\x01R\xfc!R\x06[-\xd4\x06\x0e\xc9g \x87d\x0b\x952\xcb*\x0e)\xc06\x0ei\xbc\x85\x1c\x92\xab\x99\xbc\xd6rH\x91l\xe6\x90\x9ce\xb1\x8c\xc4YVtC\x9a%\x98\xca<\xdb\xba\x99\x835\xa0\xcaL\x8b\xbbYd3\xc4J\\;<\xa4\xc10+\xf6@+\xf6P+\xb1\xec\xf3\x90\xa2Y\xe9!\xc5\xb5\xd5C\n\xb3\xd8C\xf2\xda\xed!\x05Z\xef{/{\x03\xaf8B\xaf\xb8\x82m\x84\xdb}\xfd\xe1W\x82m\xfb\xe2e\xbf\x85\x1f\x92\xa5B1\xad\xfd\x90f\xd9\xfc\x0di\xd6P,1\x91\x00%\x9b(x\x00\xa4\xb9c\xc4\x8b\x0d\x08q\x01AY:\x9c\x00\x92\x0d-\x804p\x0e\x1b\x8a\xd4n\x8f\xdb=\x1bE\xe8I\x13Vq+\x96\x00i\x1c\xa2\x00\xa9wX\x030\xa0\x1f\xf1C\xfc\x069\xae\xe4(\x12\x85\x91;\xf5\x1eo\xf2Bk\xd0\xfe\x05\x12\xed\x0b`c#\xfc\xdc\xa7\xfc*\xa3\xdd\xc8\xa0\x13\x9d\x0e@\xc3\x89\xcb\x05\xb9\xa7\xcb\xcf\xe4!*\x1f\xcb\x08\x1c/s\x91q2\xd8\x7f\x85\x81\x08\xd75\xb4\xd1\x19\xde\x90s\xf2\xb7\x86\xd4t\x01\xcf5!\xbc/\xf8\xe7L\x1ck \x82veM\x11\xe1\xe6\x17n\xaf\xe1\xb7:t\xeb\xc4\x9e>\xa0|\xad\x0f\xd8[R\x11nw+J\xb4++\"\xedl\xaa\x12EK\x8aC\xd9\xdc\x03\xe1n\\\xd1\xe9\xb9x\xde\x1a\xfc?E\xb3\xbb\x01+\x824\xf9)v'\xbd\xfcjCeeS\xd0%\x17\xa2/)_p\x8djB\x0fx\xe8\x1ba\xb5\xacQS\xc0`Z\x81\xe1\xe7K^C?\x0e\x84>\xb1\xa2e\xe1\x11Oz\x9f\x87D8Q2\xad\x12D\x97 \xba\x04\xd1%\x88\xceH4At \xa2\xb3\xbe\x9c \xba\x04\xd1\x99)At \xa2C \xa2\xd3\x05%\x88\xceH\xe1(T\x82\xe8l\xaf$\x88.At \xa2\xd3S\x82\xe8\x12D\x97 \xba6%\x88.At \xa2K\x10\xdd\xaf\x04\xa2\x83\x8b\xcd\xbb\x02\x80\x94\x04\xd1%\x88\xee\xb7\x04\xd1U\xf3 \xbaj\nD\xf7\xeb\xc4\xe6\x12v\x96\xb0\xb3\x84\x9d%\xec,ag ;K\xd8Y\xc2\xce\x12vfI ;K\xd8Y/%\xecL\xa4\x84\x9d%\xec,agA9'\xec,agmJ\xd8Y\xc2\xce\xb4\x14\x8a\x8b$\xec,ag\xbe1\xf2[\xc2\xce\xd0\x00\xb20\xef*\x11SXO\x8e\xd9\xf4S\xae\x151\xc7\x96\xfb\x9a\x11H\x81\x97\x8d@\x1a\x82\xa2\x82/\x1e\x81D\xc3\xaf\x1f\xb1\xe4\xdd\xab\xd50F\xc8F\xbfD+o\x1e\x12V\x98\xb0B\xe5\xf7\xdf\x1aV\x18\x06\x12\nx\xf0\xa8?I\xce\xcf\x8e\x85<\x03%\\\x13\xc2\xe7\xc5\xc4\xdb\xca\x0d\x97:k\x1f\xb9\xadI\xe2{Y'A<\x90\n_S\x131\x83\xf9\xaeZ\x80]\x1a\x17\x99\xb4\xfb\xe4\x95\"k\xdd\x14\xabv\x08\x19\xbe{\x93\nF\x86\nvCX\xa9\xe4b\xa3\x95N\x11\x85\x0bX\xaf\xd9GO\xeb~1\xdbO\x1ck\xd0\xd3Nf\x86\x0bn\xfb+\x1eX)np\x9dg\x07l\xb9\xcd\xcb\x15\xfb\x9f4\xd9\xaf \xe9\xa4.\x9ej\xf5\x0fZ\x92h\xba\x1ai>&\x9a\xaeF\n\xc3;\x01c1\xda(]\x8ddb\x99\xe9j\xa4t5\x92\x96\xfc\xf8b4d1]\x8d\xf4\x0fw5\x92P\x8e\xe1\xfc\xc67\xca\xb2\x02\xd2&\x93\xf2\xa7\x1f/\xd8\xdfl\xf4\xb19S\xad\xfalQ\xb6\x87\xc9uw@\x9f\xe4\n\xe9\x91THFs\xcf|\nR\xa7\x1f\xf1\x93`\xf7g\xd8!p\x94^d\x8d\x1f1Q-\x8a\xa9\xf8\"]\xf9E\xae\xe0\x15\xb3\n\x1bE\x19F\x1e\x85\x18\xd9\xfb\x1c=\x9eb\x8c\\\x83\x00\x0d\xd2\x0b\xedJ2\x1a\xb6?\xf9\x1a\x1a\xc5U\x98\x91\x97T\x18OqF1\x95g\xe4\xa3\x13\xceP\xa2QDE\x1ay\x95i4U\xa1Fs\x94j[\x8b\xf9)\x84\x93\x95k\x8b,/}p\x8e\x92m\x117H\x1d\x8c\xabl\xa3\xf9\n7\x8a\xaft\xa3y\x8a7\x9a\xa7|\xdb\xa7\xa8\xb5\x90\xd1Tr\x14]-G1Us\x14\xa4\x9e\xa3\x98*:\xf2P\xff\xe6\xa8\xea\xb69\xee\xa0\xfd\x05)\xf0h\xb6\x12o\x11h'\xfcMT\xed\x91\x9b\xec7\xb8\xc5\x0f\x12\xfd\xfc\xfb\xffDu\xdf\xb6\xec9(~1\xd5\xfeA\xc3\xb5\xa1\xf6\x07\xd9\xae[\xc5n\x8c\xd9\xba\x9f_\xfdF:\xd8L?p<\xb2\xc3\x8b\xf5018\xb2|Cg\xc6\x91\xc2\x1c\xe0\xec\x88aj\xe6\xe3c\xe2\x85\x16;\xda\xe1\xc2\x7f\xbcp\x1e0\x1e\xf5\x881p\xc8H^L\xd3\x8f\x1c\x91\x0f\x1d\xc9\x8b\xa9\x97b\x1e@\x82\x8e q\x0f!\x01\xc7\x90\xe8\x07\x91\xe4\xc5\x04i\xd4\xc1e\xf6\xd1%\xf6\xe1%y1\xa9)\xec\x18\x13\xf9 \x93\xbc\x98\x92\x17\xd3\x0c/\xa6\xb8\x87\x1c\xe4\xc4\x1d\xc4\x86\xc6\x17\x83\x1bB\n\x94\xd7u\x93\x02T\xa0D:\xfbm\x93\xce\x06\xce\xd2\x83\xa7xU\xc6K\x8b\x90^\xb0\nY\xfa\x8b\x9c\x1d\xc6\xc4\xe1\xfeE\xbd\xfa\x8c\xbe]\xfc\xfe\x8f\xe1\xc7\xfdt\xcaO\xa7\xfct\xcaO\xa7\xfct\xcaO\xa7\xfct\xcaO\xa7\xfct\xcaO\xa7\xfct\xcaO\xa7\xfc\x88\xa7\xfcv\xa9a\x7f>MNf\xe9\xbc\xaf|\xf3\x0fu\xde\x0f:\xe6\x1b\x07\xfc!\xb4\xbeXw'\xf7\xe3-\xae\xeb\xe0sz\xbe\xd2\xcf\xe7Z\xcf\xd8\x9a>_\xb5s\x89\x1f.\xf9\x11\xa7;\xe0\xb4\x0b\xce\xc7w\x97\x88\x87Ma\xcb\xa8p\xf3\xae\xf3]\xbe\xc5U\x7f\\J\xcd\x94)\xc9\xea\xd1\xf5\xe4\xfc\xf8\x7f~\xf7J\xbc\xc86\xd9 \x85\xe5g\x00\xb5\xb8\xb7\xcd\x0e\x17/*\x82W|`\xa8\xc7\x0e\xb3\xc0\xea`\xfe$\x94V\xf1S\xfd\xb0\xbb)\xb7a%\x82w\xb9\xa2X |sS\x91\xbb\x9c\xc7&\xe2\xb9\xb3./\xd6T\xcfZ\xcfP\x1d\\A\xb9*\x1f\xf0\xac\xd1M\x95\x93u\xef\xe7r\x1d\x92sS\xe5a9\xf6Z\xbe\xa9\xf2v4\xf3\x0c\xd8\xc0\xc5+L\xb1\xdcF\xca\xf5\x9a)Cy\xc1\x17\x1cv0\x80\x8e\x82\xed\x1c\xb3\xaf\x15y|ds\xf5\x84\xf5\xd3\xf5[L\xf15\xc2\x94V\xf9MC\x99jf\x96zy\x8b\xeb\xdb\xb0\xa2\xcb\xb7\xa1\xa5\xf8\xffdX\x8c2k\xf8\xf9w_\xe626rS\xe5f\xff`\x8a\xf5\xbc\x82v\x0f\xbbm\xc2\xb9P&\x7f\xc0\xe4\x0f8\xdd\xa6\x00\xe7\x18\xa3\x8d\x92?\xe0\x18\x8a\xf2L\xab@\x90= \xfc\xec?\xe3\xd4?\xe3\xbco=pF;\xd5\xc7=\xcfG;\xc9\xfb\xcf\xf0\xd1N\xef\xc9\x1f\xf0\x1f\xce\x1fp8\xefy1?!\xc6\xa7\"\xce\x8c\xf6\x19!\xce\xe7\xbc\x08\x9f\xda(\xd77\xc3\x99Q=EC\xab\x12\xe7\xc4\xef\x1c\x0cN\xe9\x88\xd9\xe9\x8d\xd6i\x06\xee\x0b\x8f\xd0i~\xfb\xb3\xad\xae\x93\xe2q\x86T\xd6\x17\x83\xd3]7o\xdc\xcd\x11\x117\xfb\xc1\xc9fF\xd9\x1c\x8c\xaf\xe9\x8e\xac9\x14S\xd3\xda\n\xa1q4}\x114\xf5\xd8\x993\xa2f\x06\xc4\xcb\x1c\x1f)\xd3\x12\x97\xd2\x17\x1d3R\\LK\xce\xbd\x912+\n&\xd2\xa2^\xce\x89wi\x89o9+\xb2\xa5\x1e\xc92f\x0cKg\xf4J=\xa4\x9f\x1e\xb12N\xac\xcahQ*\xe3\xc6\xa7\x0c\x8bL\xe9\x8dI)N\xc9\xbeh\x94\xe2\xb5\xc18\x94F\xc0F3\xb7\xd0\x88\x82\xc3Q'\x03\xe3M\x06D\x9a\xec\x159ft\xc9Yq%\xcd8\x92\xf1\"H\xc6\x8b\x1d9\xbdw\xbd\xf1\"}\x91\"\xbb\xe5[\xda\x181\xc5-\xe9h\xbf\xef\xb4\xf7\xd6\xd6\xa7\xdbV5\xbbYO\x1f\x06\x13\x9fj\xad\x05\xc3\xa1\x90Q\xaci\xabM[L\xdf\x1f\xdf]\x06\x1b\xbe\xb9\xdc\xe5$\xf3\xb7\xfc\x94\x1d\xef\xcb\x0c\xec\xb8|\xd1\x11\xd5T\x8d\xdc\xad\xb1D3m+\xe2zF\xee\xd0\x02\xe5+\xb0S\x0e\xd9\xde\xc57\xc1\xc6[\xd5^\xcbz\xcbi\xad\xed\x04?\xa6}U\x08HV\xd5dUMV\xd5dUMV\xd5dUMV\xd5dU\x95\x89&\xabj\xb2\xaa\x8a\x94\xac\xaa\xc9\xaa\x9a\xac\xaajJV\xd5dUMV\xd5dUMV\xd5\xdf\xb4U\x95i\xab\xaaQU3\xa8\x0e\x99R?\xbe\xbb\xec\x19R\xd9\xebv\xb3)g\x1a\xbf\xc1\xdbi\xf1\x83we#\xef\xb3@\xaec\x83\x85\xda-*j\xcb;\x84\x17->Q\x9c\x9d\x07\xea\xc6\xad\xca\xa3k\x96uDj\xeb\x17\xf6\xaf\x90f\xc4ECG)\x17\xaf}\x06\xc1Z\x93\xe4\xb7D\xf7\xed\xcf\xc8 Z\xa3)\x15\x88G\xbaF&\xf1\x1aY\xc8\xd7(\xa0\x94QH\xd8\xc8E\xc4\x0e)A4B6\xd2\xed\xfa!\xb9\x1b\xbd4\x9b\xa0\xad\xc9c\xb2\xc6\x92\xb4\x91\x15H@\x01\xd5\x19\x0d(\xd8\xfb\xb2\x07/ \xd7DG)V\xf5\x08\xf8\x01\xc5\x84 \x90\xcf\x9d\xdc\xa6\xd7\x07B\x11(\"\x1c\x81\xbc\x90\x04\x9a\nK\xa09\xd0\x84\xad\xc5\xfc.\xe4\x93!\n\x8b,\xaf\xfb\xf8\x1c\xa8\xc2\".\xc5\xaa\x9e\x07_\xa0y\x10\x86}\x8aZ\x0b\x19\x0d\xd8@\xd1\xc1\x0d\x14\x13\xe0@A \x07\x8a t\xa0\x14\xab\xda\x96\xcdD\x80\x04\xfd\xa3\xc6\xaaF\xder\xcc\x03P4a\x1cN\xb1\x80((\x0e\x90\x82b\x83)\xc8\x02\xa8\xa0\xf9\xa0\x8a&\x8d\x1a\x10\x0b\x9a \xb3 \x1f\xfa\x80\xdcp\x0b\n\x81\\\x90\xf3\x16\xfap\xe8\xc5-\xc30\xbd\xcd\x82a\xd0\x88\xc6\xf0\xc11\xc8[o/,\x83\xc6A3\xc8z#\xf7L\x88\x06\xf9`\x1a4\x08\xd5 \x0f\\\x83\x86Z)\x14\xb6A\x01\xd0\x0d\xb2\xc07h\x1e\x84\x83\xc2`\x1c4 \xcaA\xce\x86\xf1B:(\x1e\xac\x83\xdc\xa50FZT\x88\x07\xcd\x84y4Q&\xe8\x83b\x03?(2\xf8\x83\x86\x00 d\x01\x81\x90\x05\x08B\xd1\xc0 \x14\x13\x10B\xd1A!\x14\x0c\x0c\xa1\x10p\x08\x85\x03D(\x10$B\xd6\xd5\xd9y\xb5\x7f(\xa40\x0c\x18\xa1p\xd0\x08\x85\x01G\xc8V\x8d\x98\x00\x12\x9a\x0b\"i\xb2\x0cH E\x85\x95PTh \xcd\x1e\x0f^\x88 \x05\xc0L\xa8\x075!\x13n\xa2SI\xfch\x06\x91\xbf\x8f\x05\xf5\xb0\x9a\x10$\x08r\n\xc7\x81&\x04\xb3\xcd\xe0;\x1d\x0b\x8a\x1b\xc9V\x07\x8d\x06\x0f\x96.\x8b~L\xe8h\x1axd\x83\x8f&V%.\x88d\x85\x91\xec@RHy\xa3\x81I\x03pRH9,\xed6\x19e\nk1\x03w\x9aX\xcc\xe9\xe8\x939P\xdbpA\x93\xf0'\x17\x02\x15R\xb18(\x94\x0d\x87\x1a@\xa2R\xa8\xe3\xe9\xb8Tdd*\x85:\xee\xa5\x98(U\x10N\x15\x17\xa9\n\xc0\xaa\xa2\xa3U)\xd41\xa4Q\xe8\xd6l|+6\xc2\x95B\x1d\xab)\x0c\xeb\x8a\x8cv\xa5P\xc7)\xd4\xb1\x07\x05\x1b\xc6\xc1\xfc\xa5\x89\x8a\x85\x0d\xa3a\xd1\xf0\xb0\xb9\x88\x98!\x8ek4V\xe5!.*\xe6\xc2\xc5\xe6#c\x01p\xd0 :\x16\x88\x8f9\x8d\xec#12\xb7\x1c\x8b\xe5q6R6\xa6qB\xd02\x7f+\x04!f\xa313\xbb]6\x02n\x16\x80\x9c\xf9\xb03?z6\xd8jc\x10\xb40\x0c\xcd\x8e\xa2\xcd\xc6\xd1\x82\x91\xb4\xa9X\x9a\xbb\x99\x82\xf0\xb4\xa8\x88\xda@Y,#q\x16\xaefH\xb3\xe0lQ\x916\x17\xd66\x13m3\x8bl\xa2o\xf1\xf17\x0f\x02g\xc7\xe0\xec(\\L\x1c.2\x12\xf7\x18X\xdc\x184.\x10\x8f\x1b\x85\xc8\x85cr\x0eT\xce\x85\xc3\x84#1~dn\x146\x17\x8c\xceY+\x14\x1b\xa1\x8b\x8b\xd19P\xba\xd88]l\xa4n\xfe\x18 B\xeb\xc2\xf0:\x1d\xb1\x8b\x8b\xd9\xcdA\xed\xec\x97\xaf\xd0p7\xa9tiJ\xba4E~\xe1\xeeC\xc8R\xc1\x83\xcb\xde\xd2\x93\x17hs~v\xdcA\xc4b\xde\xd7\xe8\x0bo\xeb\xbe*\x93\x95\x15\xbc\xc8\xd7\xab\nj\xd2.\x15ly\xe46\x12\xb5\x9a\xbd\xba\xc97/\xca]W(\xebjQ\x91=\xe1\xc7\xdc7\xb8j[\xd6\xb5\xaf\xf5\xea\xc8\x07\x97\xbe\xab\xc1\"`\x00\xe5\xe3.\x93\x11\x9f\x84\x81\xe5\x1f\xdf]\x8e\x06\xca\x8b\xb5\xe1 \x1a\xb4\x12\x98\xc1\xf8\xd0\xd0\xa0w\x8d\xec\xd9\x81\xf94y6|{l!G\x04\xeaCS\x9c\xfa\xc6\x06\xedC_\xcf\xdfN\x11\x92\xbc\xec\xda\x14\x17\xcd\x8c\x8ae&/\xbb\xa8\xf8%\xf5\xa3\x971\xb1K/r\x19\x19\xb7L^v\xf3Q\xc8\xb8\x18d\xf2\xb2\x0bE\x1e\xa3\xe2\x8e\xc9\xcb\xceH3\x90\xc6\xe4e7\x1fYL^v\xc9\xcb.@F\xf2\xb2\xeb\xa7\xe4e\x97\xbc\xecP\\L0y\xd9%/;HQ\xb1\xbd\xf8\xc8^8\xae\x17\x84\xea\x8d\xc0\xf4B\x11\xbd\xe4e\x17\x86\xe1\xc5D\xf0\x92\x97\x9dEX\x00j7\xe8e7.\xa8#\n\x0c\xec\xd8G\x07\x14\xeb}\x082\xc0d\x86\xa2\x02\xe3\xfd\xe7\x8a5}d\xe79;\x80\x10\xe2\xbfc9\x10N\x86\x11\x0cI\xc3\x8erS\x8a;\x12N\x98\xe8\xad5\x05T\xf8\xaaNT=1\xc9u*\xb9N\xd9\x05\xc4\x04\x1dB`\x87\x19\xc0C\\\xe8!\x08|\x88\x0b?\x04\x00\x10\xd1!\x88\xe4:\x05i\x14d1\x1b\xb4\x88\x0d[$\xd7)5\x85\x01\x18\x91!\x8c\xe4:\x95\\\xa7\x92\xebTr\x9d\xf2\xc3\x1d\x016\xfe\xe4:\x15\xd28!\x10\x88\xbf\x15\x82`\x90\xd1@Hr\x9d\x12)\x04\x18I\xaeS\xf3a\x92\xe4:5\x1aB1\x8b\x9c\\\xa7b\xc1+\x8f\x01\xb0\x8c\x81X\x02A\x96Q0K8\xd0\x92\\\xa7\xa6\xc0.q\x81\x97\xe4:\xf5\x15\\\xa7\xc6\x031\xa1PL\xf2\x8dJ\xbeQ\xc97\xea\x17\xf3\x8dRQ\xca@\xf8S\xf5\x8a\xaa\x07\x00\xd0O_\nR\x8dF@K\xf6\x95\xbe\x10(CV-|/\x87\x90\xd2\xf3\x0f\xc2\xe0\xdb\x8bf\xbf\xdf>\x8c.~\x8cK\xfe\xfaY\x87\xd4\x0b\xbe\xb0VlS\x95\xcd~q\xf7j\xf1=\xfb\xcfi\xb1.\x83\xeb\xa2\xa2\xae\xa1\xf5@\xfa,\x04\x18\x96v\x01VO\xdf\xca\x8d\x03\x8a&\x1bn\xb5\xcb\x8d\x0d@\xcb\xaf'\x98\x7f e\xe3\x8c\xaf#*\x8c\xdc\xe6\xf0\xb4\x86weN\x12\xa2\x1d\x93Y\x0b\xeb\xf2\xbd\xf0A1\xc8\xb5Oh\x890\xa58\xbb\x85\xcd\xc0\xa8\xa00qOkR\xdb*,\x04\xf25Q\xec@\xb4bg=\xc5\xc6\x8e\xdb&\xd8\xb1\xb5\xae\xaao\xf3=\xcb\xb2\xc9hS\xa9K\x1a\xd3\xc0\xd45\xea\x0b`|\x15\xc1\x9f\x11\xb9\xcfk\xcaV:6D\xca\x1ao\xeb\x05\xfa\xe9\x96\x14\xe4\x8eT\xbc5\x84l\xf4\x85p\xcbp\xae\x02CP\x98\xd5\x81*\xbcT?\xe3M\xca\x17\xe1\x92-q\xbb\xf2\x8e\xd5\xe56\xaf\xd5\n\xe6EVqS2\xd3\x03\x8b\x15\xdf U\x89\x19f\x87\xc2\xb6|-\x86\x82\xca\xed\x8aTR\x92\x18\x17y\x0d\xad\xc2\x1ah\x8ds\xa9\xb3\xf0=h u\x1836\xd4\xef\xe4x\xac\x9b]o\x0c\xca&z*\xda\xa8\xdd\xa9\xb3\x8a\xaf\xefK\xec\xcbR\x0e\x8c\x15\xa6\xe4\x05\xcdw\x8eM\xaf\x93\x07\x0c\x01\xf6fM\xf1N\xean\x0f\xac\x1f\xbf\xdc\x92B\x0e\x0d\xbe\xcb\x8a\xaf\xa0P=y\xed\xaa\xd1\xa9\xe7P\xc3\xdb|s\xfbbK\xee\xc8\x16\x95\xc5\x0bNq@y\x01\xa5d\x9d\xc6\x16(\xdc\xcd\x00\xebj\xf4\x817J\xf0z\xc4?^FY\x95\xa4\xa8\xb0\xb5i\xa7\x94\xd3\x10\xd5\x8db\xf6\x9d\xf8\x8b\xad\x08\x0b\xad\x98A\xda\xacX\xc0\x02\xf56mA\x84\xb5\xafW\x12\xb6\xf8\xf5\xd7FUG\xd4\x07{xf\x86>\xd8\x1f\xffm\xe6w%_9\xc4S~\xce\xeb\xf8\x03\x1b>\xec\x00\x8f\xd2\xc4}\xab\x16\xd3\\\xb1QpA\x03Vn}\xd9\x86\xc2/\xfa\xbd\xa2\xcd\xd1\xc1\x02\xb8\xe7*\xb2tZ\xe0l\x15#\x8bMW\xfe\x91k\xb2\xc2\xa4\xd2\xa7kE\xb6\x00A\xb3\x0d\xe0\x86\xd0/DY\x02\xd8\x8a\x8a\x95j['\xebY\xb9\xcd\xb3\x87Q\n\x841\x98\xfd\x8b\xa96\x88-\xfb:\x14y\xcfK\xb3\xf8\xa5W\x86\xc7\xd3Z~Q\x9dEk\xdeY\xaa\x8b{\xd9\x18\xa7\xc0h\x83P\x91\xd3\xea3\x0e-\x06v7\x84\xd1*_\xafIE\n*\x8eRLA\xc0\xa8j8\xf8\xdej\x0f\xb2\xd6+\x92\xe5\xac|Kh\x0c\xbd\xf6A\xcb9\xb5\xb2\xe4\xc2V.c\x89\x8d\xc8\x89s\xb3\xe1\xe2\xf1\xe0\xa21\xe0\x9c\xdc7:\x9d\xf5\x16\x8b\xef6\xcct\x9b\xc4q\x9b\xccn\xe3\xf5\xd5IYN^\xdbdF\x1b\x18T5i\x0e.\xdb\x1c\x16\x1bg\xac\xe9\xb5\xb1\x00-S\x98kn\x96\xdaL~Z\x103-\x9c\x856\x83\x7f6\x83yfY0\"\xf2\xcb\xe22\xcb\xa2q\xca\xfcl\xb2h<2\x17\x83l\x0ew\xcc\xca\x13\xa3!\x0c\xb1\xa9\xdc0'\x0fl\"\x03\xcc\xc2\xfd\xf2j\xd8\xc3V|c\x07\x9d\xc8\xf1\xea\xf8\\\xb6\xf6\xfd\xc6\x9f\xf7R\x1c&R4\x0eR\\\xf6Q\x18\xef\xc8\xcb8\n\xe4\x1a\x85\xb0\x8c\x0c:\x8e\x99[(_d\x98S\x14\xc8&\n\xe0\x11\xf5\x8a\x1c\x93;4\x8b5d\xb2\x84\xe2\xf1\x83\xe21\x83\xa6\xf7\xae\x97\x0d\xe4\xe3\x01\xc9\xe5\xfb\x97G\xa4\xc0\xde\xe6\x01\xa6Z\xfdZ\xb3\x0b\xce\xc0\xa9\x84\xc0\xce\xf6\xa9\xdb\xc1G\xe2U\xf3\xcd\xdf>\x0cgz\xf3\xe9\xfc\xf2\xe4\xed5\xaa)\xa6M\xbd\xf8&JO\x81\xb0\xc3\x80\xf6\x867mj \x02\xb5d_\xd6\xa0`\x8a\xf3\xdd6_\x13\x94=d[\x0bv\xd7\xae$\xe8\xb4\xc8i\x8e\xb7\x1d/\xf6B\x9f3\x8e\x01C\x8af\xa7.*/\xd0\xd9\xf9\xa7\xb3O\x17G\xef\x97\x17\x97G\x97W\x17\xcb\xab\x8f\x17g'\xc7\xa7\xefNO\xde\x0e\xbewq\xf5\xe6\xc3\xe9\xe5\xa5\xe7\xad\xa3\xe3\xe3\x933\xdfK\xe7'\xffqr\xec{I\xf4\xe4\xe0;?\x9d^\xfe\xf0\xf6\xfc\xe8\xa7\x8fJ\xcf\xf0\x83\xfea@5\xd7y\x81\xb7K\x8a\xb7\xdb\x87%`\"!\x1dl~\xd5?v\xd6\xcd\x8e/\xbax\xbb\x15[:Y1\x85\x8b\xd4\x82\xc5\x99\xd7\xdf\xd8\xa6\x15?\xdd\xe3\xec\x96\xbf+\xb01\xce\x0e\xcfka\x88gZZ;\xf7\x00,`c\xb4'\xad\xdc7[nU\x15\xb6\x05V\xcc\xbc\xd8\x1c\xb0\x8f\x85\xd6\xb7'U^\xae\x10)8\x05OY\xdf\x149\xe4\x9ed\x0d?v\xa9\xd2\xf9\xd1\x89\xd3\x00o\xf1~O\x8a\x1a\xce\xbc\xfa\x08\x0c\x82\xca\x1fH\xbd\xcc\xfa\xe4Y\x14\x8c\x95\xb7\x1fK\x1d\xa0mh\xc1\xc4{ 5\xb4\xb9\xbaP\xe3\x9b\x9au\xd3\xf4l{\x02\\Y\x8b\x97\x94\x9d\x16q-oz\xb6\xf2[W\x8eEi\xd6\xb5(\x97lM\\\xde\x11:/gM\x8a\xab\x0c\xec\x8d\x96?\xc2\xc7\xd9\x12\xc6\xd9\x92\x14\xa1\xba\xa6G\xbf\xe8\x93I\xf4\xa0f\xbf\xc2\xcaN\x035/\xab\x11\x8b\xa6\xf6\x89\xec,^\xbc\x96\x9d\xd3?:tkP\xbb\xfb\xa9\xfd/\xf7\xc1\x8f%=o\x8a\xe9\x9b\xe0\xc9\x7f\x9e\x1c_]~:_\x9e\x9f\\\\\xbd\xbf\xf4\xef\x86\xfa\x07\x1f?].\xcf\xaf>\x86\xbd|qu||rq\x11\xf6\xf2\xbb\xa3\xd3\xf7W\xe7'J\xf3\xea[\x9b\xbf\xf0\xd2\xb1e\xd2\xd1#8\xca\x1cuD\x07s\xae2>\xe46*\xfbi8\x1aX<\x06TD\x0e\xd4`\x040:\x9d\x07\x15\x8f \xe5\xe3BMdC\xc5\xe6C\x0d0\xa2bs\xa2\x9c\xac\xa8\xd9\xbc(C\x9e+\xb2W\\n\xd4lvTt~\xd4,\x86T|\x8eTD\x96Tl\x9eTD\xa6T\x08W*\"[j(\xe2\xd6\x1c\xc6\x94!\xcc\x1ei\x8b\x86p\xa8\xe6\xb2\xa8\x0cq\xb6\xe8Z\x13yU\x8e\xa8Z\x03[\xf1@4-\xdf.=\x91ae.\\\xce\x08ZC%\x88\xcc\xb3\xb21\xad\xa2p\xad\"\xb3\xadL\xbe\xd5l\xc6UO\x16\xb5D\xc8\x9a\xc3\xbf\xf2\x06\x7frF\xc5\xf2\xb2\xb0\xec\xe1q\xc2\x99X\xf6\xef\x7f\xb6\xd7}\x12\x1f+\xb4\xf2>N\xd6pM\xbd\xbc\xacQ\xcc,\x93\x870\x93\x9d\xe5\x8dn5\x14\xd9j8\xaa\x95\xa3UByZ~\xa6\x96-\x8a\xd5\x0c\xb6V\x10_k\nc\xcb\x11)\xca\xc7\xda\x8a\xc6\xdbr\xe4\xaf\x8d\xa4\xa8\xec\xad\xe8\xfc\xad\xc8\x0c\xae\xb8\x1c\xae\xc1\x08Sf\xe0\x1d3\xb2T\x1c.WD6Wl>W(\xa3+\x80\xd3\x15\xcc\xea\n\xe3uY\x03-\xd9r\x0d\xe5\xff\xf8\"F\x05\xf2\xbb\x82\x18^F\xe1c\xb2\xbc\xa2\xf3\xbcb2\xbdbr\xbd\xe6\xf5\xb7\x97\xef\xe5g|\xa9Q\x9f\xdcZu\x1b \x87C\xf8\xdb\xbc\xe6f\x90\xebz\xf5y\xf1\xa1\xde\\\xd7\xd0=m\x7f\x80\x11|\xc56\x11\xd5x\xab\x08\xe4\x01\x93\xea!J\x97d\xaf\xb4\x11\xa3ZfX\x0b\x7f\x1eu\xf1#\xd8j%\x9eg\xb8\x10\xdc\x81\xd6<\xd5\x16@\xfc\xad\xf0\xbd$\"MK\x0eR\xaf\x08j\xf6\x8a\xaer\xd4Y\x9e\xb3\xb2\xa8\xf3\x9a\x02\x8c\xc7m0\xe1M\xe0(\x07\xb4\x02\xdb\x8a\xd8\xe4`\xff\xd6\xe5\x8e\xb4\xa6\x1e\x85\xf4c\xb9ZC%\x94\xb8\xa8?'\xc2\xc2~\xae\xd8\xe4\x8d3\x9fj\x07\x1fe\x03\x0f\xb6\x7f\x07\xdb\xbe\x03\xed\xde\xe3m\xdev\xe2\x87\xbd\x99\xba\x18e\xfcR\x80r\xad\xa3\x0fe\xd5\xc5\xda\x92c:\xac\xe1\x0e\xd1Q!\x8e\xfe-V\xc1m\x9f`pZ\x04H\x13-{\x88~\"``b\xdf?\x10\x8a\xaa\x06\xd4\nY\xc6\x10a\xa2\xf5\x0f\xb9\x16\xd0V\x0e\xd87-\"\xc5VkA\x14Z!\xccO\x91\x12\xfc\xe1\xc4\x06\x12\x92\x93\xe8>-\xa7\x8a\xd0\xa6*x<\x170\xcfY3[\xe5\xab\xe2)\x15yvY\xba\x06\xfd\x85B\xcb\x08\x1d\xecN:\x80\x9f\xf1\xe0e;x\x99\x0e>\x96\x83\x8f\xe1\x10\xccn\x18\x9e\x05\xd0n\xed\xe8\xef\xa8=\xfcwb\x1f\xecfN\xb61.\x199\xacw=\xe3]o\xe5\xc3\x96\xe5\"H4|\xf9\xed\x98b\xb7\xa40\xa9b\x03\x9dr\x88\xdeq|\xd1-\xad\xc3 [|vU\x16\xa4=!\x94\x0d\xcd\xca\x16\x8f\x16k8\xd5hROk\x9d\xf44P69\x1eb\x97-\xafQE\xfe\x8bd\xe2&\xa0Ye\x14C2\xac\x88\xbd\xad5\xaf\xd1\xae\\\xc1!\xa9C\xb7ENJe\x062o\xc7\xfbaoG\xc6\x05\xdbk\xd9\x8e\xb8\xaa\xf0\x97\xc2\x82\x9d\xd7\x14W@\x93\x95\x0d\xc0c\xcb\xc9\xac~\x82\x02\xe7u\xcb]\xe9\x1aX\xd40\xaf\xd1O2\x03\xdb\xaa\xc3\x83\xaf\xb5\x11\x92F\xc7\x8a\xcb\xdbX*\xc8`\xd2\x16\xebRb\xe2]\x08&\x19\xf4\xad\x17\x82d\x14\xc9F\xbfH\xcbi\x04\x1e\x88\x089L\xf9u\x04JAf\xb0\x14\x14\xcc: \x0e\x9a\xa2\x87zC\x8f\x1f<\xc8\x17\xf6\x0d\xd9\xe2\xa7\x0c\x96`B0\xceq\x91T<\xa1\xe0\x102\xc3\xc1\xa1\x98!\xe1\x90#,\x1c\x8a\x11\x1aN/\xb4\x16(\x0eE\n\x16\xc7\x92=`\x1c\n\x1e]\xb3\x02\xc7!\x87\xab\xce`\xf6#\x02S\x8dw\xdb\x19\xf6\xd7\xb1\xaf\x95\xb2\xda\x10\xc6R\x0d6\xa7\x04\xbb\x1c^z\xc1\xddb\xfcU\x8b\xa2a\x1f\x97\x07\x03d\xebQ\xd7\x17\x0eL\x7f\xab\x0f\xc4\x98\x15\xd8\x0ceg\x11\x1b\x10\xd0\x0e\x92\xa3-\xd0@{ W\x88;H\x03\xcd\x82\xccMa\x94\xabT\x97l\x13\x16\xd2\x98\xec\x1d\xb7\x08\xcd\xf0\xaf\xb2\xca\xeb\xfb\\u\xc9\xbe\xab\x89oFT#`\x87\xf3\x87\xc7\x83d\x0f\x92\x07\xc9S\xa4\xe1u \x92\xde\xf9\x81k\x93\xdds\xcb\"R\xeb\xd0Y\xb1\xf4,S\xae\xcb\xd72\xd9:w\x1d\xf9wo\x0f\xe0v\x9fM~G\x8av\xceKq\xb6\xe0\xe9\xbd\x0c\xba\x17z\xf1\xd7\x95\x9f\x05$!\x97\xdcIze\n\xc2\x9e\x82\xb0#\xf7\x96\xaf\xed\xd1\x96]_\x7fc\x84\x02\xc0}\x7fsR\xbfy8b\xba\xffhE@q\x88\xca\x1f\x9b\x17\xeb\xd8\xfb\x06\x96\xc7\xa1=/\xd0\xd1\x11\xd2\xafN\x15\xb1\x1c\x07\xc7\xb4D\xe0\x91\xd0<\x10\xba7\xcf\xd0\xcc-{\x7f\xc0>\xea:)\x1a\xb2l\xbdg=BF\xea\xbc\x1c\x15\\\x0e\x83\x96\xa3\x02\xcbC\xb0rdP\xd9\x05)[\xec\x0d\xf6\xf5m*\x9c\xec\xb8\xce\xcc~\xa1\xd9,(\xd9\x01$\x0fn\xf1\x83 \xb2\x7f\xff\x8f\x07 \x0f\xc1\xc7\xc3\xe5\x88\n\x1d\x0f\x01\xc7\x91`\xe3y\xa0\xb1e&\xd9\xb6\xf2\xb8\x801\xb5\xc2\xc5s\xc1b/\x1a:\x00\x14\x07\xc1\xc4v\xd4h\x1cDl\x97aX\x8cg\x83\xc3\xa1\x8d\x11\x02\x0c\x0f\xd7;\x08\x14\x1e \x9b\x16\xf4\x08p\xb0\x17\x0c\x1e\x86\x82}@\xb0\xb3\x95\xc6\x80\xc0!\x10\xb0\x0d\x00\x9e \xff\x06\x82\xbf\xd3\xa0_\x07\xd8\x1a\x02\xfbF\x04}\x1d\xa50F\xda,\xb8\xd7\x06\xefF\x04w\xed\xd0\xee,`\xd7\x06\xe4\xc6\x86q\x07A\\\x1b\xbae\x03p\xe3\xc1\xb7Q\xc1\xdb\xf8\xd0m8p\x1b\x04\xdb\x8e\x00mC![+`k\xcf=\x14\x88\xf3\x83\xb5#\xa0\xda@\xa0\xd6\xa8Fl\x906&Dk\x05h\xe3\xc2\xb3q\xc1\xd9y\xe3!\x08\x98\x0d\x81e\xd5m\xe51\xa3\xf9\x1b\xc7\x88\x88P\xac\x01\xc4\xbas\x8e\x0c\xc2\x9a@H/\xeb!\x08\xc8\x054=\x8c\xbd~`^\xd8\xe1G\xf6\xb8|$\x87\xc71X\xd0\xd7ru\xb4]\xfd2\xdf\xd9q\x06\x10\xd4/Hd\x17F\x17\x0e<\x16\xf7\xe9\xae\x821\x04\x85\xf9-F\xba\x0e\xc6}!\xcc\xcc+a\x0cilk\xb4]\n\x13\xefZ\x98\xa1\x8ba\xc2G\xde\xec\xcba\xbe\x06\xc7g\xfc.b\\\x12c\x91jc\xee\xc4\xd8.\x90>1M\x98[\xe3\xe5\xa8\x91)(\xdc\xbb\x99\xa2S$\xeaM/\xfd\xe6\xa87\x01a)\x8cW\xe4\xc0\x14\xc2\x82\xd4\"\xb8\xd2!\xe9EI/JzQ\xd2\x8bP\xd2\x8b\xfe/\xd0\x8b\xd4\xe6O\xeaQR\x8f~\xed\xea\xd1\x80\xc2bW\x8b\xda\x1b\xe1\xc2\x0dDI\x03J\x1aP\xd2\x80\x92\x06\x84\x92\x06\xf4\xdb\xd4\x80\x9e^\xc3\x8a\x0c\xbc\xd1\xedV\xd5\x83D\xb6LC\xa9)\xa6d\xf1T\xc8HZO\xd2z~\xadZ\xcf\xffo1\n\x0d\xa9=\xba\xba#\xbe\xbe\xc8\x8b\x8c\x1c\n\xd5\xe7E\xbd\xfa\x8c\xbe]\xfc\xfe\x7f.^\xb9\xd4\xa13\xb1\x80\x8eV\x88\xe4\xca\xeb\x9aI\xe2\xb1,z\xf7w\xb1.'\xcd\x9a\xe8>T\x86^\x94\xaf\xe4zm\xf3\x88\xe1\xed&\xdca\x963|\xc9lr\x06T&X\x88M\x97\xa59\xbeU\xd3\xf4$[\xa3\xc0o\xbd+\xdb\x91C;F6\x0d\x19\x05\x97\xb9\xcd\xa9=\xf5j\x8dE\xea~\xef\x91\xaa\xb7\xb8\xd4\xcd\xcd.\xa7K\xb6\xe7\x056\xd9\x1c(_\xc9\xcd\xbf\xd7\xb6s\xc3\xc4\xf1A\x8e\xb6\xfd\xc2\x08\xfaJ>k\xbd\xcc@9\x85\xb1*\x7f\xea\xbb\x00\xd2\xb66\xb6\x9a\xd4\xec\x0b\x83\x8a\xc8\x9d\xa7\x80\n\x95\xd7\x88\xef\x14L\x19P\x94\x03\xbcE\xfb\xa6b\x9d\n\x9c5\xe7\xbc\xfc\xaa\x8d\xd2\xcf\xd3\xdb6\x82\xac\x81m\xf1^y\xf7\x0f4\xd1O0R\xa4W\x9e\x94\x95\xd7\xad&n\xd1\xea\xd1\xba*wFf\xe4./\x9bZH\xd0\xf3i\xd5f\xb1\x9fg%\x1b\xc0\x05P\xeb[\xd3\xd2\xf5\xd1\x9bO\xe7\x97'o\xaf\xb9~\xd5\xd4\x91{\x14\x84\x1e\x8e\xe8\x0f\xf8\xc2\xa6q\"\xd08\xf7e\x9d\xab\xfa\xdb6_\x13\x94=d[\x87+`\xbb\xd6\xa1\xd3\"\xa79\xdev\xbb\xf9\x85mJ\x0e\x0c2R4;}\xc9{\x81\xce\xce?\x9d}\xba8z\xbf\xbc\xb8<\xba\xbc\xbaX^}\xbc8;9>}wz\xf2\xd6\xfb\xee\xc5\xd5\x9b\x0f\xa7\x97\x97\x01o\x1e\x1d\x1f\x9f\x9c\x85\xbcx~\xf2\x1f'\xc7!/\x8a\x9e\xf7\xbe\xf7\xd3\xe9\xe5\x0fo\xcf\x8f~\xfa\xa8\xf5\"\xe7\xbc\x1e\x06V\x7f\x9d\x17x\xbb\xa4x\xbb}X\x82\x926fP\x98_\xf7\xb9\x95u\xb3\xe3[\x06;=\xc0\x89\x8c\xac\xd0]II\xcd\x06\xab&\x8c\x9d!\xf5Q\xdeN\\Nv\xc5\xd9-\xffZ8\xdfq\xad8\xaf\x85B\x8c\xa92\xbb\xc1\x8f\x88\x8dzCb\xb9o\xb6\xdc!BPnY\xe1\xf3bs\xc0\x04\xdc\x950\xbdI\x95\x97+D\n~\xa8v/\xb8\xe4\x9ed\x0d+\x89qH\xe7\xdc?~\xd8\xbf\xc5\xfb=)j\xa0z\xda\xc6\xf3(\x1f\xdf\x07R/\xf9\x96\xac?\x18<\xc2j\xdd\xd8\n\x91:Q\xdb5\xe2<\xfd@j\xe8\xa5\x85&\x08\xdf\xd4\xacs\xe7\x17\xa1'\xc8U\x0c\xf1\x92\xa6e\xb0T\x94\xf3\x8b e\xb8r/J{\x1b\x14\xe5\x92\xad\xd2\xcb;B\xe3\x94B\x93\xe6*\x0f{C-\x0b\x8c\xd5%\x8c\xd5%)\xc6\xea\xeeSt.#OY\xd8N\xf7\xba\xe1N\x8f\x82\xfc*\xe6\xd3\x0e\x1c\xc84a\xab\xb2 \xc6\xb6vUl\x99V\x8eQ\xddd\x19\xa9\xebu\xb3\xdd\xa2\x0f\xf5\xe6\xe4\x9ed|/\xc6\xdb-Y\xc9\\\x9e\xd1RLBbps;\x95\xef\xb64\xf9\xff|\xda3\x81]>R(\xd7.\xf4\x85\xa0~~\xd0.\x15`.\xeb\xcb\xbb!\xbc>\x06\x11\x9d\x82il_\xe6\x05=\x90\x0e\x04\xe8\xda\\8\xaf\xd9\xb3k\xd8e\xafac\xd75U\xa10\x18ydYY\xad\xf2b\xb3}@\xcd~\xa5[\x92\xa0}\xcaj\xc2\xf2\xae}*;\x9b\x17\xbe\x0ds\xd0?\xceuk\xa2>o\x8c]\xfecI\xcf\x9b\"\xce\x16\x7f\xf2\x9f'\xc7W\x97\x9f\xce\x97\xe7'\x17W\xef/\xc3\xf6z\xfd\xa3\x8f\x9f.\x97\xe7W\x1f\xc3?\xb8\xb8:>>\xb9\xb8\x08\xff\xe0\xdd\xd1\xe9\xfb\xab\xf3\x13\xad\x0b\xf4\x0d;\xac2\x82\x04>\xffX\x98nH\x88\x1ar\"r\xd0\x89tCB/\xc5\x0c@\x01\xa3\xb5N7$L\x08Fa\x15\x94nHPS\xdc\xe0\x15\xe9\x86\x045\x85\x85\xb1\x88\x1c\xc8\"\xdd\x90\x90nH\xf0\x04\xb8H7$\x8c\x0fva\x88\xa3\xe9\x86\x84\xb0\xd0\x17\xfe\xbb\x01\xc2\xc2_\xa4\x1b\x12x\x1a\x19\x0e#\xdd\x90 RHx\x8ctC\xc2\xfc`\x19\xe9\x86\x84\xd1\x814\xcc\"\xa7\x1b\x12b\x05\xd9x\x8c0\x1bc\x02m\x04\x86\xda\x18\x15l#<\xdcF\xba!\x01M\x08\xbe\x117\xfcF\xba!a(\x10GX(\x0e\xfd\x86\x84\xe1\x13\x8a4:\x03\xe3f\x9b\xd7\xdc\xfcu]\xaf>/>\xd4\x9bkaKk\xfb\x0f\xa0\x99\x15\xdb\x04\xdd\x04\x00 K\x8a\x13\xa1\xe9\xa6\xa0\xd3\xe7\xfaL=\xf94\xd05A\xbe.\xc3\xaaC\xf0\x8b\xc9\xcc\xbc\x7f\x10o\x85aV^\x08/o\xb0d\x8f\xc1\xcd\xfb\xa5<\x19\xec\x0d\xe4\xe0\xe8\xb9\xe1\x18\x07 \x13^\xfa\xb9\\\xbd\x01\xb6^\xd0(\x0bb\xc3[\x0c\x1c\xb1X{\x0e\xde\xde s/\xa8b\xfe\xe9c\xa9U,\x06\xdf \x87o.\xe7+\x84\xc7\xf7\xd8M\x14\x89\xcf\x17\xc6\xe8\x8b\xc8\xe9\xf3\xb2\xfa\xa2\xf1\xfa\xe6\xf7\xb2\x8d\xdb\xe7\xef\x9fy\xfc>s\x1dS\xd7\xca`\x86\xdf\xe0\xf0\xb3Q\x00\xc6\xf1\xfc\xc60\xfdFp\xfdF\xb0\xfd\xc2\xf9~\xe1\x8c\xbf\xd1\x9c??\xeb\xcf?\\\xa22\xff\xec\xdc\xbf\x99\xec?C\x9a\x8d\x0d\x18\x97\x0f8\xc4\x08\x0c\xe4\x04N&e\x0c0\x03\xbd\x10J$v\xa0\x97\x1f8\xae 39\x82C,\xc1q\x05\x99\xca\x14\x0c\xe3\n\x8e.\xcbD\xbe\xa0\x971\x18\xb4\xf7O\xd5\xfb\xa22\x07\x1d\xdc\xc1\x98\xecA\x1f\x7fp\x1e\x83\xd0\\\x1b\xc4\x89\xd9\xc6!\x8c\xcc\"t\xf2\x08\xbdLB\x0f\x97\xd0?\x08\xa2\xf2 \x03\x19\x85\xf3T\x8a0*\x9e\xf73;\xb3p\x02\xb7p4\xbbp:\xbf\xd0\xc50\x9c|\xa8\xb5ni\xc3\x9b\xda0\xd7\xd0\xbbr\xfaG$\xa4\xa9\x8cC\x878\xda\x03\xce\x1d\xb8Ut\xd6at\xde\xa1\x97y\x18\x81{\x18\x9b}\x18\xc6?\x9c\xc5@\x8c\xcdA\x0cd!\xc6\xe6!\x061\x11\xa7s\x11]\xe2\x08\xf5\xb1\x11#\xf2\x11\xbd\x8c\xc4H\x9c\xc49\xac\xc4\xd1\xbc\xc4\x08\xcc\xc4\xe9\xdcD\xe7\xca\x83\xdc\xec\xc4\xe8\xfc\xc4\xc7a(F\xe7(\x86\xb3\x14\xa3\xf3\x14}L\xc5)\\E\x87\xa0\x96\xc18\xc0V\x1c\xc5W\x8c\xccX\xf4q\x16g\xb2\x16\x07x\x8b\x01\xea\x89\x87\xbb\x18\xaa\xbf\xc4\xe4/\xfa\x18\x8c!e\x8a\xccb\xf4\xf1\x18#2\x19\xa3s\x19\x87\xd8\x8c\xb3\xf8\x8c\x16i\xac$\xd4\xc1h\x8c\xc1i\x0c\"\xeeyx\x8d\xc1\xcc\xc6\x01\xb2\xd3hv\xe3\x90,+\x8b#\x02\xc7q\\c\x85\xf1\x1cC\xda$\x90\xeb8\x81\xed\xe8b\xbcDa<\x06q\x1e\xfd\xac\xc7\x10\xde\xa3\xa7\x15\xc7q\x1fC\xd9\x8f.\xfec\x04\x06\xe4\x08\x0e\xe4t\x16\xe4P\xa3\x052!#s!\x07Kd\x1d\xa9q\x19\x91\x0eNddV\xa4\x9b\x17\x19\x9b\x19\xe9\xe0F\xcebGZ\xa4\xb9\x0e~\x1e\xc6\xa4\x8b3\xe9bM\xc6\xe5MFgN>\x0ewr\x1c{2\x98?9\x92A9\x86C\xe9dQ\xba9r\xe1,\xb9\x10&\xe5H.\xe5\x086\xa5\xa3j3\x18\x95#&E\\\x96\xa5\x93g\x19\x9fi\x19\x9fk\x19c$\x05\xf2-C\x19\x97:\xe7\xd2\x7f\xa6\x8a\xc9\xbbt0/!\x0d\x15\xa4\xe5Z\xca\xc0\x9d\xed\x1di-\xd1\xe3\xa8\x8bK\xcb\xd6Y\xf1<\xc3\x85\xa0k\xf5\xe4u\x04\xaf~\xcf\xf10\xa5}\x06\x10-9\x89gEP\xb37t\xc8\xa3\xaeBYY\xd4yM\x81z\xc0\xad\x8da\xcd\xd4\x93\xa7\xe2O\xfd\x9c\xa0\xbd\xd8\xe6\xcd\xa6)\xfb\xb7.w\xa45{v\xccA\\\xd7e\x96\xf3c\x9b\xa4\xfe\xf4\x05\x194B\x0b\xbb\x0fo;v_\xf7\x0b\x97\xb7\xc9\xefHa%G\xa6\x00\xac)\x00\xeb\xaf5\x00k\xffV\x9ea.\xb6\x9d\xe6\xdd{U\x08\x0bc~_\xe2\xed\x96 o\xb6t4\xdd\x9b#\xec\x8e\xe9\x044\x00u&U\xd0\x86l\xf4\xb1g\x93\xa6\x8f\x83\xcd\xe3\x1c*\x11\x18<\x03\xdc\x9d\xb0lg\xf0u\xecL\x9d\xb0l\xa7\xb0s|\xbc\x9c\xe0\x9cGsqL\xff\x06\xcb\xc0\xec\x8f}\xe5\x85\xc0\xb1\xfecI\xc9\x9bv~\xb1\xbf\xc6\xdfG\xc5\xda\xcc1\xe49 O\x14\x91\xff_\xd9\x92\xe4>\xb5\xe4\x08\x0eP\xfe\xaaIS@\x91\x14\xd89\xa1\xb1F[m\xa1\xef\x05\xd1\xdd\xd9`#\xf9\xf3\x8a\x04\x16\xc4h\xad\n\xb9=\x1cd+\xf6\xb0:\xd0(\x0e\xddR\xe1\x05\xb5\x17\xaa\xa75\xcan\xcb<#\x12\x7f\xb4\xfb*8Km\x0f\xb1\xf5\xe3\xa7\xcb\x93\xe5\xa7\xb3\xcb\xd3O\x1f\x07\xa3j\xa9\xef\xfd\xf9\xc4\x8c\x89\xa5>?zsqytj\x06\xdaR\xdf\xf9\xf8\xc9\xf3\x98\xb3a\x97?\x9e\\~\xd2ZI\xd0]\xfc\x05\xff\xfa\xf1\x9dY?\xa9\xbd\xf1\xa8\x11\x935W\x0b\xda#\xdbq\x0f\x8bn\x02\xb3=\xbdO\xc9\x1e\xd8\xb8\x07\x96\x97\xfe\xcaeyq\xd4\x95z\xec\x8b\xfa\xcd\xf4\x00\xea|\xdd?\xd4\x16\x9f\xb8.Z\xceU*\x88M\xe9\xf7\xa4\x98\xb4VYW\xab\xc1\x02\xcd_\xb1\xeckV\xb4Uk\x12\x8d\xcf\xbf\x04\xd8\xde4W\xaf\xb0\xf5\xcb\xbb\x82\x05\xafa\xa3V\xb1_\xca\x13N_\xcb\xbe\x8aG\xd9\xbc5\xcd\"\x90-1\xaa\xd3\x0b\x86\x8f\xc1\x04\xe19\xa5\x83;\x83<\xa1K\xa3L\xeb\xe4`jD\xe9\x8c\x9e\xce\xe8\xbf\xf63\xfa\xd0\xc6k\xee\xee\xcaK#\xce&\xf5\x9b\x87\xe9\x87\x92\xb4\xa1\xb7)t\x81O\x1b\xba\xe7\x8d\xb4\xa1\xa7\x0d\xdd\xb3\xa1\xdf<\x88\xfd\xbc7Q\xd2N\x9ev\xf2\xdf\xc2N\xee9\xa4\xcb7\xfc{\xb8b\x8c\x0c\xde\xb5-\xc6lk/\xcc4b;\x0c\xd8\xfe\xac&\x1a\xaeM\xa3\xb5?\xab\xb1\xc6\xea!CuPn\xf3\x0c\xd4}\xd3s/2\x80\xf8\xd0\xf4\xed6\x1d\xb5-\xc3\xe8\xc7\xd6\xba\x1c0~\xac*\x99\xb5\xf6\x8e9\xea\x00\xf2\x0d\x1e\xd7\xc0\x19\x1a4\x8e\x01\xe3\x19,A\x03\xc5\xae\xdat5\xe6u\xe3L{1\xd1\xb8'\x85\xb2\x8c\xd5\x92\x97\xd2\xb3\xeatT\x14g{8\xcb\xd5\xf1h\n\xd4\x14\x9d\xdb\x85\x92\xa9\xe0U)\x17\xceT\x846Ua:x\x19\xcd}\xa8\xff\xa0\xd0v\xe4\xde\xa9\xac\xd0\xee\xbe9\xb4\xfd\xa8\x96]l\x8d\x01\x12?~:\xd4\xfeV\n%\xf6>\x9f\x84\xae\xf3uY\xdd\x93\xbeT\x0eO\xb2\x1dN\x97\xbf\xaf\xca;R\xe0\"#\x0bLi\x95\xdf4\x94\xb0yp$\xff\x08\xde\xa2\n\xec]\x07\xfa\x1b+\xdb\x19d.\xfc\xebv\xb7\xe9\xbb\x13\x0d.)=\xfd}@>\x97\xd9\xaaI\xf2\xe7%\x97\x1d\xfc}\xab\x12\x0e\x14\xcc\xdc`\x8e./\xcfO\xdf\\]\x9e,/\xff|v\xe2\xdcc\xf4\xd7\xaeN\x07\x9fs\xda\xe3\xc0\xf3\x8b\xcb\xf3\xd3\x8f\xdf\x0f\xe6p~:\xf4\xf8\xf4\xe3\xe5\xd0\xe3w\xef?\x1d\x0d\xbepv\xfe\xe9\xf2\xd3\xd0\x0bo\xfe|\xa9l\xa5\xed\n\x16\xd0\\\xb6\xc3H;`/\x1f\xf6\xa4w\xd0\xa4\x8a\x0b\x14\xdf\xd7jZV\xdc\xe9\x12\xb4\x13\xf9\xa1\"\x8c\xf7\xb6\x1c,z\xb0?k\xb7\x8b\"\xf1A#\xd4\x9c\x9ep\xb6\xa1\xdd\x94M\xb1\xea\x8e\x83\xe4~\x9f\x03\x9br\xc96\xc6\xc0\x01\xef\xd9C/s\xe9\xb8\xcfV\xa56w~\x92\xe4\x19\x8a\xf1\xab7a\xdb|\x82\xc0\n\xee\xc9+v\xc0} c\xbf\xbb\xfb\x9fm\x02V\x8a\x1f\x96\xce_B\xe3\x0bY_.\xdb\x19\xe8\xd9j\xbd\xe3bh\n\x0dM\x9f\xe1\xa930m\x06\xa6\xcc\xe0t\x19\x9c*\x03\xd3$t\x8a\xf4\x06D\xdb\xc3C-8$R\xd9\xe6\x84\xa4\xa6\xe0\xe4\xec\x972\xb2\x1b\xeb89\x1a\xad\x9d`\x8a\xbf:\xedm\xfd\xc6:\xcb\x06p\xe7<)\x06\x85xhqf@?\xfe\x1e5M\xber\x96\x82u\xb7Q\n\xce\xcc\x1eQ\n\xb6\xd7\xc8\xa2\x88\x9f\xd9\x7f\xff\xab.\x0b>\x1f\x9c\xb9\xc3\x802\xf2\x87\x9f\xc7\x94\x80{B\xe6Y\xaf=\xdc-\x7f~j6\xfc\xf9\xe9\x98\xfc\x94-\xc26\xd8\x0d\xf1\xa7\x1f/G\x88/P^P\xb2!\x15z\x96\xe1\x9a\"\x1e$\x83\xfe\xf1\xf7\xcf\x9dy\xf2Yd\xe4\xca\x7f\x1d\x91/Zo\xcb.\x9e\xa2}6\x1a\x99\xf0_\xc7d\xa2{\xf9*1\x0b\x1e(\xa9\x9d\xd9\xf3\xf9nd\xcf\x7f\x1d\x91=S\xa7a\x05\xe7\xd8\x10\x9b%]\xb6b\xf1\x9f\xbfe\xb6#\xd0\xb5\xcc\x9f\xe1\nK,*@\x87\xdc\xe1\xfb%\x17\xb9\xdc\x92bCo\xf5=Q\x8c\x97\xf6W\xb9)\xf6m\x1d\xa2z;|\x9f\xef\x9a\x1d\x02Q\xac:\xed\xa9v\xbb-\xbfp\x9f\x0c\xa3\x1d\xcd\x05\x14\xaa\xd0k\x1eA\xaa\xdf\xc3\x93\xb5p\xc4\xef$\xed\xcaU\xb3%C\xea5\xb7;\x1e\xc1.\xf9\x16S<\x1e\x06\x0c\xd0\x91\xfb\x16\x19i8U,2\xb0\x89k\x03IV\xa7\xe3\xf0\x8a\xb7-\xc7\xec>)\xcaR\x1f\x99a\xcf\xa8\xdaf\x01\xd6W\xe539\x0c\xb8W\x9f\xbf\xfd\xe4/BB=\xba\x15E\xcd\xa6\xe1\xa9v\x0dPB&f,b>Cy\xfc\x8f\xb6*5X\xcayC\xf3#\x90\x906\x05SQb\xcf\xa8\x8f\xb5\xeeL\xd8J\xc2V\x944\x03[\xd1\xe6\xbek.\x0e\xae\x00\xaa\x84\x97\x86\x88\xd1\xeb\xc0\xd4\xf9?f\x1d\xed\xf5\x12\xb6\xa8\x83T9\x02\xcaP#b-\xe8\xed\x12\x1ceUe\xd5u\xbe\xe1\xf1uJ\xc3N1m\x81\n&|\xf4M7\xaa\x08?\xd0\xee6\xe3@\xb2\xc6\x86\x19\x10>\x10\x0f\xc6g\x9e\xe9g\xec2\xf2\x84\xc9\xd2L=\x90\x06\x8a\xed\xa2kxO\xadC/\xf7M@\x8e\xb74C\x90\xe3-\xc3\x1c\xe4x\xafo\x14r\xbc\xd47\x0d9^\xd2\x0dD\x8e\xd7t3\x91\xe3\xb5\xbe\xb1\x08R\xe8y\xb8K\xae\x95v\xba\xf9\xc8\x10d\xd3'\xdbg\xa3\xef\x8d\x98`V\x82\xe44.y\xb3\x1c\xc5] 57\x89\\\xad\xcd\x1f\xc3\xf4\xd4o\xe6B1@\xf5r\xee\xfc\x86\xb5n\x92\xc7\xb1\xa4x%\xc5\xeb7\xaax\x05)\\\xba\xaa%$\x8dU\xb4&\x9f\xb4<\x9a\x96\xadC\x1eI\xc7z-\x0b\x96t,5\xf9\xf4\xa2\xa4c\xb9\xdfJ:\x16J:VP\x96I\xc7J:V\xd2\xb1~{:V\x98UKW\xb2\xe4\xc0\x0f\xd4\xb2\x00p\x18\xada\xed\x15\xa8\xc5\xa8\xcf\xde\x041\xf8O\x84\x92\xaaU\xa1:\xd8\x02\xd2\x88I\xe4\x06n:A:x\x83\x9c\x00\x0eB\xb1A\x1cK\xc3\x86t\xa1\xc0~\xce\xcf\x8eC;\xef\"\xc3\xc5\xe8\xaeK\xcaqR\x8e\x93r\x9c\x94\xe3\xa4\x1c\xa3\xa4\x1c'\xe58)\xc7\xbfn\xe5\xd8PtBT)\xf6~\x80\x12\xf5\xa1\xde\x1c\xadV\xa1\x88n\xafX\x8eO{k\xe9\x87z\xf3R}\xc9\xf4\xf6\x1b(\xd8[\xb2%\x94L-\x9b\xe3k\xa3x\xda{\x13J\xf8\x96_\xe7\x9b\xd1I%m\xc7\xa2_\x9c\xa3\xe8\xc6\xfbB\xe0\x98\x8a\\\x10\x1a\xce\x8e\xd2[\xda\xfe\xb1Q\xda\xfek\xa3\x8aw\xc5o\x06l+x\xd2\xeeu#K\xea\x95c\x14Z86M.\xea\xbc\x02:\x8b\xa5\xbd7X\xc2\x1d\xae>\x93\x8a\x93\xc0\xf9\xad\x91jQ\x94\xe5Sc\x7f\xf3\xab\x00\xed\xacox\xf4\xa1\xc7\xbe\x86\xdf\xde\\\x9d\x7f\xd4\x7f{{r\xf6\xe9\xe2\xd4xU^,l\xbe\xfe\xfe\xe4\xf2D\xff\xf5\xe8\xed\x87SC\xf2\xe5\xf9\xd1\xc7\x8bw'\xe7\xa2%\xa5nh/\xb9}\xd2A\x8b\xf4\x9aw\x95\xaf\xd7\xa4\"\xec\xf4\xc6/I+\xd7hO*q\xcd\xaf`\x95a\x04\x8d*/h\x91\xda\xc4\xa6\xc2\x05\xbf\x83\x93\x9d\x97\x0b\xa9\xc1\xa9\x0e[f\xe9l%F\xaa3\xd1\x8br\xef\xf0RRz\xe2P\xfd\xa3\xa5\x1d\x8a;\xa9h\x89\xf2\"\xab\x08\x16\x01\xfbY\xa9\xb7\x0f\xc0\xe4\x86\x8a\x182YO\x1e\xaa\x7fXd\xae\x88U&7m@\xf345\x9c\x95\xf3\x02\xdd\x92-\xbf\xf4\xa0{jVD\x0c\x95C\xedoK\xd65Q:\xa1\"\xbc\xc32\x02^\x93y-\x1f\x08\xfa\xact\xb0|Yg%\xebQ\xb0\xbb\x18\xb9\xcb\x11y\xa8\xff`\xc9\xbf\"\xbb\xf2\x8e\x18\x05\xa8-%XW\xe5\xce(B{\x1f5\xadpQ\xafI\x05\x8d\xc4\xdfU\xbfo\x0d l<\xc1\xe5T=v\xa89s\x0e\xfb\x7fZ\xca\xceK.\xbd2\xc9\xea\x00.C\xe5\xc4\xe9\x92\x89\xa7yW\xb3\xf6F\xc1\x8c-%\xfc&\xdb\x9ab*\xae\x8c\x14E\xc00\x89\xf0\xb6\x16V\xa2Zy\x1d\x04\xc9\x1b\xd1\xf8_p\xcb\xd7\x8am^yY\x18\xf5\xe0s\xfd\xb0\xf7\x97\xa5\x16x\xb5\x92\x19\xf3Y'|%%\x95M\x14[j\xff\xa2:\xcaD6\xdbO.'\x87\xfa\x0f\xd6\xe9tW~&\x9cu^\xac\x10\xd3\xe4\xe1\x0c\x00\x03^\x99\x010\xd6\xd8'k\x9c\xb1\xcf1%\x88\xdc\xc3\xedl\xb2\x0c\xfc\xfaMQ\x99\x8a_\x1d\x96\x8b+6\xc4\xfa\xc2\xba\xa6@\xe7'\xecP\x7f|y\xf2V\xb6\xaag\xa5\xff\x9e5L\xb8U,\xc0\xf1Ji\xc0I\x16*C]7\xcd(\x03{\x8f\xf6\xc2\x07\xdd,a\xdb\x87\xb4'\xfd\xddH{\xa8\xedI\xc6\xa7\xca\xce\xa4=S\xf7'\xedQo\x97\x82\xe4\xdb\xab\xe4[.\x1a\xe4\xec}\xab'K\xeea\xde\xdd\x0bR\xd4=L\x13\x19q'\xd3$\x7f\xcd\xfdL\xcb\xfa\x17\xd9\xd5\xb42\xfc\"{\x1b\xa4\xf9;\x9c\xd1\x9e\xbf\xd0>\x07)\xcen\xa7\xd5\xe9+\xefyZ\xee\xbf\xdc\xce\x07i\xd6\xfe7\xac\xda\xf3-\xb03\x1e\xf2\x9bm\xca\xed\x96\xf0\x8b\x0f\xf55R8\xd5\xb6\x96\xd7u;\x94Y\xf7m\xe0J\xc4\xfe\xb0\xcd\xca\x82V\xe5\xd6\xb9\x11\xbf\xc1[\xf6K\xd4MX\xab\xb1,\xad\xec\xab>\x14u\x03\x05\xe0\xa6\xd6n\x8dbsq\xda\x0e\x1e\x8c1\xadHQ\x1a\x08\x89\xd3h\x87w\xa5\x11\x12\xdf\xf9\xfa\x90u\xf3\x98\xad2\xdd\xd6C\xcb\xcf\xa4\x10\x1d\x0bE\x92\xe6Z\\\xacx_\xef`\xad\xe9 \xf9\xf8\x89-0\xdc\x08\xcf\x1f\x8b\xfb\xb2x\xe8\x16tZP\x11#\xa3\xbd\xbc\x15Z^\\@\x04F\xb7\xfe\x16\x9bo\nL\x9b\x8a\xd4\xdc:\x9cWp\x01\xdc\xa6\xdc\x94\xdcWqa\xefY\xdeK\x8eM\x1e\x9e\xf1\x95T\xedb1%tC\xa7\x18\x86=\x87F-J\x10k\x0f)i\x8f\xf3\n.\xa9\xcd\x0bn\xe8\xcd\x89:\x1b\xda\x15\x87\xe5\xc8\xefo\xea6_\xeb,\xf8\xc0\xffwA1m\xc2\xcc\x0f\x1f\x8e\xce\xfftr\xbe\xbc\xb8<\xba\xbc\xb2[!\xfao\x9c\x9d\x7f:\xfbt\xe1|\xfc\xee\xf4\xe3\xd1\xfb\xd3\xff\xd7\xf9\xfc\xe8\xf8\xf2\xf4\xc7\x13\xc7\xc3\xe3\xa3\x8f\xc7'\xef\xdf;?~\xcb\x96\xa5O\x7fn\xed\x0cB\xb7\x1b\xae\x82}\x08\xab\xed\xd4\xeb\xf7;\\\xe5eS\xc3\xa6TwJB\xbb\x0c\xe1\x02.\xa9Wm\x0d\xce\x12\x0c\x14\x0e\xbd@W\xc2\xdd\xfcT\xb8\x9bC\xa1\x10\x94\xca%]v\x80.Z\xfe\x8e^\xa0\xd3\"\xa79\\\x98\xb4\xce7\xe2b+\xb6\xfe\xe6\xe5\xea\x005\xdc\xa2U\xcb;\xc1\x0f\xc4\xcc\x15j\x18\xbf\x90\xba\"X\x0d\xc3\xe8\xecd\xbd\x0c\xed\x03\xf4\x02\x1d\xf72o5\x82\x03T\x11\xbc\xe2\xf7\x9a\xcb,yv\xbd\xf3\xa9m\xcc\xe8y\xc1\xaf\xe8\x05\xba\x00)y-\xcb}\x80\xaaf+b=\xe6\x1c-\xca\x88\xb3.\xed\x98\xd3\xe5\xb7\x0f\xd8'\xd01\xb7\xb8F7\x84\x14\x9d\x1ar\x80\xf6\xa4\xe0\x13sEjZ\x95\x0f\xael\xda\xa1\xabg\xd3>\xe8\xb2\x11\xcd\xc2o\x87\xe3\x80\x0e)PE2\x0c\x19J=\xaa\x86\xfb\xb0V\x84-q\"w\x02\xf7r\x14%Z7\x15\xd3\xf0\xdae\x84\xc2U\xf0\xd0\xe5\xce=\xf4#\xa1GuM\xe8\x8f\x1d\xb3 `'\xddWyfx\xec\x06\x81p\x96\x9d\xcb\xb1\x11\xd9v-\xff\x86\xdd\xdb\xb1\"\xecWqw\xab\xf0\xbdJl2\xbc\xa1\xa5\xf6\x91\x95,\x97\x96T!\xf90\xac\xfb\x9e\xd6\xe8\xae\xdc6-\xbe\x0c\x7fx4\x1d\x07\x98g\x03\xf2@\x9e,H\x87\xe1\xf1&\xad\xb5\x93\x1c\xdc\xcaF*\x82\xf6M\x95\xddbq\xf7\xbc\"\x8e\xf2 wy&\x8b\x0b\x0b\xd4jy\xb3-\xb3\xcf\xcb[\x1e\x84pV\xe1m\x02e\xe9\xf9oH\xfcV\xae\xf9\xbd\x96\xe2\x83o\x14\x11\xbd\x99\xa1\x8c\"\xa8\xe4\xd3\x1a\x15\x84B\xe3\xdb\"\x07t3lB\xd8\x00\x0er.aQ\x98\xd5\x0c\x92q&\xc6n\xb9\x96+M\xcb9kw;8U\x89\xf5T\xa5\x01\x90\x02\xdfl\xc9rS\xde\x91\xaa\xe8\xf4\xed\xaeD7e\xb9%\xb80\x8a\xa4\xff\xde\x9b\xa7y\xb1\xca3\xbe)\xe5k\xd4\xc9F7|\xb4\x08\xcd\xbf\xee\x8eZpUb\xb7\x98!\x1e\xbb\xa5b\x8b`\x9e\xb1\x9e\xe6\xf3yY\x91\x0d\xb9\xf74\x99\x9d{&.\x8bE\xe4~\xcfT68v\x01\\\xcf\xb7jv\xc2\x12M\xc5\xb3\x12T\x078t\x17\xac\xca[E\x1e\xb4\xa3\xa4+\xd4\xe8YWEuui\x9f\xb3\x8dK\x1c\xca\xf8\xe0`\xd9\xee\xb7\x98\xb2\xb6\x84\xc3\xbd(\x04p%\x8a\xde\xed\xa1]\x91!\xdaH\xaf\xa1\xc3C>\x08u\xc7\x1a\xf0\xa1\x1b\xce2:\x02\xa9\xc7\xd3:g\x85(\x08>\x1c\x8d%\x06Y\xed\xae\xea7\xfd\xe2A2\n\xa9~`\xa5\x02\xb9hm\x01VY\xed5\xc36\xab=7,\xb4\xdas\x9b\x9dV{\xc5j\xad5\xc4\x186[\xed\x0d\xd3r\xab\xbd`\xb1\xdfB\n\xb3\xe2\xcaw\xdd\xb6\\HS-\xba\x16Q\xbbNa\x1ba\xd7\x85\xf4\x08\xd6]Mpt\x1b\xaf&\xff\xeb[z\xb5\x02\xfc\x82\xf6^\xad$\xbf\xa0\xd5\x17R,\xdb\xafV\xaf_\xd8\x02\x0c)\xa6\x1dX\xab\xdf/b\x0d\xd6\xca\xf0K\xdb\x84!\xcd\xb4\x0cC\xf2\x03is\xac\xc4=q\xb2\x8b}\xb6bW\xa1,\x9a\x0b\n \xfb\xc1\x11\x1d\xbe\x13\x92\xac\xd4?SE\n\xa4|E\x08\x88uy\xdb\x8f\x83e\xc6\xbd\xe2:c\xcf~\x19/\xcc\xd5@#l\xb7\xd0|\xe3uE1\xd6\x1eWUd\xaf/\x9bj\x1b\xac+\xfa\xb5\x8d#tu\xfe\xfeeE\xea\xb2\xa92\xf0\xe1\x00m\x02\x02\xd3o\x1fP\xbe\"\x05\xcd\xd7\xb9I\x897\x84uq\xf7L\x85\x82\x1b\x0c\xb2r\x8bn\x1a\xa6\xca\xc8;\xe5aM\x95\xae4\xbb\xa6n#\xe9!L\xd1\x96\xe0\x9a\x9a\xb2\xca\x82\xa0'/\x9f\xa0\xec\x16W8\xa3\xa4Z\xf0!\xc5O\xc65\xd9\xec\x08\x1c\x1eYq\xaf\xce\xdf?\xad\xd1\x1e\xd3[.\xdc\x10\xd5F\xb44sa\x9f\xaf\x9b\xed\xf6\x01\xfd\xad\xc1[\x08\x8e\xcd\xdbG\x88\xe6-\xf1\x8cGN4?\xbefY:\xef\xf0\xbf~\x0e%\xe6\xe2\xea\xdb\xb2aj\x05\xb7\x05\x9aJ\\\x86\x8b\xb2\xc83\xbc\xe5'U3\xa7gd\xb1Y\x1c\xb0\xa6\xe2f\xbe'\x8b'lJ\x14%\xe5\x8b\xe5\x9e\x92\xd5\xf3\xc57\xe6g\xa7\x05\xda\xb3\xc6\xcb3r\x80(aG\xab\xa6n\xf8u\xd2\xfb\x8ad\xe5n\x9foI\xb7\x15\xde\xe4\x05\xae\x1e\xb8\xb9\x8fk\xa0\x96\xd6\xc2\xdc\xc9\xe9\xc1\xcc\x8a\xdc\xef\xd9\x191\xe7\xbb{S\x13\xa9\xc6\xb0\x8e&\xf7\xbc\xab\x8e\x8a\x87\x05\xfa\xa1\xfcB\xeeHu\xc0g\xef\xd5\xf9\xfb\x1a\xacU\x86\xbc\x06\xb453\xa3:\xbb%;\x82\xaeo)\xdd_\x1f\xc0\xbf\xf5\xf5\x01\xdb\xe1\x8bR<=\xe0\xa3'S\x88\xfbl\x07!\x145{C\x1ex#X\xf2!\xd5\x9d4\x1c\xed\xf0\xbe\x86\xa1\xc0K\xccT\x0d\x18\xd9\xa0\x16\xe7\xc2\xae\xc9vd\xae\x19\x1cZ\xfa\xe2\x9f\xd1\xe9\xba+!\xeb>\xb6F\xe5+\xa6\xa7\x88Jp{B]7;\xb2\xb2h\xeb\xff\x8c\x8e\n\xf4\xc3\xe5\xe5\x19\xfa\xfe\xe4R^\xcbpu\xfe\x1e&\xd4\x03\xb7\x01b\xf4\x17}8^>\xec\xc9_\xff\xf2WC\x1c\x92\xac\xf4B\xf6;\x18IxK\xee\xabr\xd5d\x04\x191\xe0\xd5\xd2\xec\xf7\xdb<\xc3\xa2\xee\x15\x91\xa6\x10\xd6<\x19\xce\xd8\\-\xcb\xcf\xcd\xbe%\xd5\x83\x1d\xa5\xb4\xbb\xd5 V\x15\x9e7\x0f\xe1Go\xc9N\x19\xa3+\x18\xa4X\x16\x95\xfd\xff\xae\xe4\x17@\xeb\xa7`\x96 c>\xfd*\xb2.+r ?d\xf20\xcd\x85.S\x10\xb2\x92J\x1b_\"\xaa;\xb2\xb2\xc8+\x0b\x04:\x0b\xa8Yln,\xd0\xb3\xab\x9a\xa0;R1-\x81@\x94^>\xd7a|\xe0\x02ol\xb5\xbc\xa9\x08\xfe\xcc\xcf\x1a p\xf1\xdc\xec\xe9\x8f%%\x87\xa09\xaf\x9b\"\x83\x11\xcc\xca+\xe6|\xd6T\x15\xf7\xb7P\xfd\x14\xec\xcdZr\xe7\x0c\xd3=A\xac\xd57\xcd\x1aU\x84\xad\xc0\xe4\x80[\x9cs*3i\x84\x89T\x19\xf77d\x93\x17\x85\xcd\x8c\xc0ms\xe6r\xf1\xb0'\x0b\x18\x8fx\x9f\xd7\x8b\x8cm\xf7\xe6k\x17|F\xd4\xe0\x12\xc1&\\\xa1\xcfn\xf4Ll\xf5\xe0S\x02S\xe89\xda1\x0d\xd1\x10wc\x99\xcc\xbc2\\\x91k\xad\xe2\xa0\xb2\x8a+\x112T\x93\x1d;5g\x9a\x9a\xfcX\xde\x8d\x96]\xfa\x03\x9b\xc67L\xad\x06\x1cN\x0fp\xab\xec\xab\xd2\xcc~S\xdeY6\xe8\xf6\x96\x07\xaa\xb98\x0e\x95\xe0\xfa\xa8x\xb8\xee\x05\xb8\xedn,\x19(\x89\\\x07\xf1\xb6\xd4\xda\x02\xd4\xe6~W\xb0\xd5\x8a/\xa8P\x92\x1b\x8b\x07\x9e\x92\x97\xd4\x1e\xb4!s&\x07\xee6\xbf\xe1\xc5\x13\xebh-\xcf\x08\xdcP\x89\xb3\xcf/\x9b\x82\xfd\xc3\xf6\x1di\x19\xb5\xcc\x12s\xc3-\xd7\xa8\xa1\xb0@\xc8\xe9W\xf3\x03\xe4j\x95\xc3\\\x84@\xd1\xa0\xf3s\xbd\xbb\x85\x1b\x8e\xb4\xf5\x08\xba\xa0/\xff\xe4\x1e\xb3A\x88^\x1d\xa23V>6\xefDQq\xdb\xa0y\x81\x8e\xff\xe5_,\xdb\xc0\xbb\xb2D\xeb\xb2D\xaf\xd1b\xb1\xf8_\xc6cVY\\<\x98\x0fp\xf1\xb0`\xd9\xbd\xab\xca\xdd\xb3uY>7_Y,\xccu>_\xa3g\xec\xd3+^\xc0\xcb\xf2\xd9?\xb1o\x9f\xa3\xff\xb6\xacm\xb6\xef\x7f\xb6\xd7\xfd;O\xdd\xff\x03\xdf\xe1\xc9\x95G\xaf\xb9\xae\xc1\xa4N\xa8i^?{W\x96\x8bl\x8b\xeb\xdaQQ(\x02{\x19\xca\xae|`\xe6\xa5\xb5@\xdb\x04\xbf\xf34\xc1\xd9\x03\xbd-\x0bK#@\xee\xef\xca\xf2\xd9b\xb1xn\xebhh\x80g\xd6g|\x10\xf0f m\x15\xf6\xd1)4\xca\xdb\x93\x8b\xe3\xf3\xd3\xb3\xcbO\xe7\xcfmV\xe3n\xa0\xd83\x80,\xec\xcd\xf1{Os|_Z\x0c\x98\xac)\x0e_\xa3\x7f\xda\xdf,\xde\x95\xe5\x7f/\x16\x8b\x9f\xcd\x97p\xf1p\xc0\xd4\x18\xf6\xe6\x1e6\xef\x0f\xb8\xaao\xf1\x965\x92\xbd\xa0\xb6\xa6\xd0s\xb3d\x95\xaf\xb5\x8c\xae\x8a]\x97\x15/\x08\x1f\x90\xfc\xad\xff\xf1\x1a\x15\xf9\xd6:\xc0\xec\xf9k#\xe9\x92G\x99\xc8>\xb7k\x90T(\xd1\xcdC\xb7\xbd\xcbU\x12\xdc2\x1f\xa4\x19\x9bm\x89}qO-\xdb\xf5Kv6Z\xf0\x07L\xb5y\xcat\xdcv\xc5f\xab9\xeb-\xca\xaf8c%\xe9\x0bl\x97\xc6b\xfb \xf5y\xe3\xb0\xd5\xaaM\x08\xaf)\x81]\x9e\x9f\xf1\x9e\xbe|\xda\x17'\x0e\x142k8A\x101z\x9e\xac\xcbrq\x83+^\xe8\xfb\x97\x0f\x8b\xbf?\x81\x1a\x83^l\xaa\xf8<\xcb'\xec=\xb6<\xf7\x1eq\x87\xfb\xde/\xaf_\xbf~m\xb6=\xbf\xdf\xc0r[B!6S\xd0\xaf\x9b\x9aH\xe3\x05G\xf3\xfar\xcc\xcf\xd9\xab+\xd2m\x83\x07\x88\xecn\xc8j\xd5m\x88\x07\xedm =Q\xca\xf6\x04\x84\x80\xeb\x7fg\xd5\xbe\x16t\x80vkW\x1bq!\xa7\xdf\xa1EA\xc4\xd9g6\xf7\xba\x03\xc5:\xdf\x12s}\x93s\xf4\x8cTuYX\x87\xb38\xf9\xaf\xf3\xaa\xa6K\xde\xf2\xaf\xd1+SR\xfb\"\x1b\x00\xf2\xbd\xef\xfc+*B\xd6\\\x9f\xf0\xfa?9DOl#\xbb_\xad\x05\x94\xfe\xc9\x81M\x0e/\xf7G\xbcc\xb2\xfe7\x14\xf1\xdf\xac/\xb2rk\xef\xf9\n\x7f\xba\x16\x8am\xbf\x8f\xa1\x87\xf2\x1a}!\xdb\xed\x0bN\xd0\xe2s\x8d\xd3q$\x97\xc3\x1c\xa8\xfd\xe1t\x00\xca\x966\xc6:\xffl\x91%\x1b8\x9c\xd4\xc7\x87M_\xe05\x1f\xc4r\x0c\x81\xff>\xed\xd8$|\n\xe4E;\xf6\xa4\x15Z\x0c\xbd\xbe,.\xbe\x1dq\xe8\x19\x9b\xbf\xb2\xba\xc6\xb1UZQ\xfe\xfa\x97\xbf>\xb7\x0c\xce9\xfd\xdd\xcf\xc0\xde\xe5\xbc\xdaL\xd4\xab\xc5w\xaf\xbe\xab\x9fX\xba\x11\xfeM\x01\x05R@\x81_{@\x01\x0dn0\x0c\xe0A\xe6\xf5\xf6+!+\x04pxK\x8ar\xf7A \x87\xe3\xcd\xed\x8ek7\x83&\x82\xda\x00!\xdd\x0e\\\x9d\xa6\xc8\xa9\xc5\x1eo\xe3[X\xd9\x16\xd6\x02Br\xd9\xfb\x91\x8b;\x8f|\xdc\x0d\xbfI\x01I\xd9\xc6\xd5\xba\xb0\xcf\xaa\x96n\xb8t\x12\xdef\xcd`\x15\xf6\x8c,6\xa8\xc1\xb4\xdc=7\xf5Sr\xbf/\x0bb\xb2\xfaQ[\x11[\x882H\xee@e\x90Bj*\xf3W+\xbb/\xbf\xc0D|\xf5m\xf7\x9c\xef\x8aeA\x00-\xb0\xca\xaap.\x14\xd6\x1b\\\x13`r\x01\xf4\x89\xcaj\x05\xdc4\xc2\xb4\xd8\xae\xed\xacr\xf8\x0c\xb8*r\xfa\xb4\x16\x1c)\xebk\xafD\xc3\xbfF\xaf\xbe\xfd\xff\xdarvY\xdb\xbf\xe2\xa8\x80$\x8f*\xe5,\xd7\xd0G\x9d\x19\\p\xbfpW\x1e\xab\xc0r\x8d\x9e\xb2\x0f\x9f:\x0czHm\xe5\xd7\xe8\x8fL\xc1h\xeaC\xf4\n\xb1\xaf\xa0\xf4\x7ft\x8e\x0f\xbc\xcdqm\x1b\xfeC\xa4&\xe4&6!\xff\x04\x91a\x81 gn_o\x11r1\x07\xe43\xb9\xdc)\xf3@\x138\xbc'*}\xdd\xbf\x19\x17\x82\x1b\xe9v/\xec\x184=\x0e0\x9b\x87\x9dcO\x9dg@n\xed7\xad\xa8\xa3\xb2\x80\xe9\xb3]\xd6X\x1d\x8ck\xe5\xa6\xd9\xac\xcc\xd5\x82\xb0\x91\x14\xb4f\xfa\xa6%\x13\xa4\x97\x85\xff\x06\xc3\xf4Y\x07\x8a\xb1']\xf3\x19\x01\x9fz\xa3\xee\xdb\xde\xd0Z\xe5\xf5~\x8bC\xf5\x9d\xe1.\x14\xb2\x14\"(_+\x9b\xcdF\x01\xac\xa1#\xdb\xa2\xdbE\x80'M\xb6eZLO\x0f1#\xf1\x85\x95\xf5\xe9E^d\xe4P\xdc\xa7\xfc\xa2^}F\xdf.~\xff\xbb\xa7\xb6\x91\xf0\x94/\xeb*\xb3\xac\x87hr\xc6\xf93\xb29D\xc7\\\x18:b\xd3U\x95T?\xecn\xcaPmix\x08\x80(\xa9`\x08\x1f\x0f\xf8M\"\x91\xf5-;\xdc\x94EK\x0c\xa9\xa1tG\x97\x9f>\x18\xab\x08\x07\x923\xfd\xe8\x8b\xe4\x18\xaa\xb9!A\xd2\xe9xW\x98\x88\x83\xbd)\x95\x97\x9a*\x8fR\xf9\xab\xf3S\x8eT\xa1U\x995\x1c\xb0~\xc6\xce\xf8l?Z\xbf\xc8nq^<\xd7\xef\x95k\xcf\xf0\x9a\xa8\xbc\x80\x1d2/\x8b\x05\xfa$\x0e\x0f\x81U\xfbc\xbfj\xcb[\\[\x03\x88N\xa9\xdf\x0f\xb8\xbe\x85e\xb5\xbe\xc5\xdf\xfd\xe1\x8f\xec\x80z\x0b\xdc\xbd\xb6\xd2\xfb\x92\xed\xfb\xdc*uu~\xcaT\xfa\xa75Gd4q\xb4Dw\xa4\xca\xd7\x0f\xbcQ\xf4\xaa\xf1.\x95\"W\xf9\xaaxJ\x05\x866\xa3A\xdc\xeb\x81\xd4Z\x03\xd6r\xe5+l.\xd3\xfa\x91\xc1\xad\x18\x0f\xaa\xe1\xea\xd7/{\x9f\x0fj\xe0'uV\x95_F\xab\xde\x84\x7f\xf6\xb8D\x97\xe40\xda\xa50'\x9c\x81C]\xbf\x9fC\x0etp\x9a\x83\xef\x84\xa4\x90#\xdd\x0f\xe0\x02:zD W\xd3G&O\x8d\xe5\xd9km:\xc5\xb5\x1a\x92\xe6`\xadf;\x9a\xaao=>\x0e\x1f \x07\x8e\x90^\x1d\xd9>\xb3\x02>\xf5\x9f\xc9&\xcf5\x8b\xac9S\xce\"NLB\xcb\x93\xb0\xb9\xd8\xa5^3Lw\xe1\x86d70Ep\xe7\xee\xc9\xeb\x11\x87-N\xdd(\x196\x93a\x13~\xff\x0d\x196\xb5\xad)|\x13\xfc\x81/\xe8c\x0c\x9b\xf0\xdd\xe8M\x10\xa4\xe8[`\xd0\xf8\xa7VZ\xf0D\xc5=\"!\xd8M\x07\x8eG\x06\x8eF\x05v\x12\x81\xe9t\x1ap,\x12\xf00\x05x\x12\x018.\xfd\xd7I\xfe\x8dK\xfdu\x10\x7fg\xd2~\x8d\xe6\xa6\x16\xd2o\\\xca\xefL\xc2od\xba\xef\x0c\xb2ol\xaao4\xa2o\\\x9ao4\x92\xaf\x9f\xe2\x1b\x8d\xe0\xeb\xa2\xf7\xce!\xf7Zm\xff4\x84\xca;\x8f\xc8k!\xeeN\xa4\xedZH\xbb^\x9dhXQ4v\xd0\x89d\xdd\x8e\x9ckk\xdfo\xfcyG\xa6\xe9\x9a$\xdd\x08\x14\xdd\xa8\x04]}3\x9cI\xce\x15\x0d\xadJ\x9cC\xc7\x1d\xe4\xa3:\xa8\xb8^\"\xae\xc9\xfd\x0b'\xe1\x9a\xdf\xfel\xab\xeb$\xfamHe}\xd4[w\xdd\xbc\xb4\xdb\x11\xa4\xdb>\xc7j&\xe1v\x90n\xeb&\xdb\x0eQm\xad\xad\x10J\xb3\xf5\x91lu\x8a\xed\x0c\x82m\x00\xbdv<\xb9\xd6Bm\xf5\x11k#\xd1j-9\xf7FJTBmd:mT2mL*\xad\x93H\xab\xb3\x13u\x12m\x1c\nm4\x02m\\\xfal\x18y\xd6K\x9d\x0d$\xce\x86\xd0f\x0d\xd2\xac\x99[(\x81r\x980\x1bH\x97\x0d \xcb\xf6\x8a\x1c\x93(\x1b\x99&\x1b\x8f$\x1b\x8f\";\xbdw\xbd\xf4X\x1f9\x16\x96\xef\x9e\x0ek1\x9a\x85\x9b\xe7\x82,r\xbd\xd0h\xe3]\xfb\x0bB\x97\xf3\x01>\xf1\xe0\x9e\xa8`\x8f\x0b\xea\x19\x0b\xf4\xcc\x8c\x7f\x08I\x8f\x82(D\xbb\x1b}\xc0h?t\xdf\xe1\xdc\xe8\x88\x86@93\xd5\x18\x89\x90\x86#%B\x9aY\xc19\xb1\x13{\x82&DP\xec}\xaf=\x06\x1e\x9d\x1a\x83\x8f\x7f0\x00R\xe8\xeb\x14\x87G\xc2\x99\x1f\xfd\xcf\x83V\xc6\xdf\xea}\xc7\xf6\x88\x93(\xc4\x98\xe3\x06\xb8bD\x9fD\xc3\x11(Q[B=\xda$\x1a\x88D\x89\xf4&\x8e\x11\x8d\x12\x05E\xa4DCM\xeaZa\xe6E\xa7\xd4\x84A\xac\xca\xd0\x08\x95(Z\x94J\xe4\x8bT\xf9H\xf7Zk3\x14bG\x8f\x9e\xa1\xfa\x1e>b~\xa5\x90\xc7\xfe\x8d\x7f`\x11\xefwX\xc8\x88\x10\x91\xde\xf9wBR72\xf2\x9bl\x81\x15\x88e!\xe3\xe8\x89;\x06/\xc5\x9f\xc1#\x03p\xdd\xe5\xbe\xac|!\x8b\xc5\xf4\xe6\x0bxYQ\xc4\x0e>\xbc)\xa9\xb0\x85\x90\x8e\xa6\xc0\x81\xd3^\x06\xd9-.\n\xb2\x0d\xcfC|\xc0Z;$\x1b>T\x0em\x82\x84\x1a\x03k\xb4l\xaeJQ[\xd2d\x88:\x19\x10\x8f\xf5gr\x17\xdc]\x0d\xefkQ\xf3*\x92\x91\xfcn\x8c\x98\x8ad\xf9>'\n\xd3I@\xa1+RS\xd9V\x9cK-\xc7L\xbe#eC\x0d=\xd4\xdd%\x97\xf0\x85T!+\xb2\xc5.\xc8\xa5\xf2\x9e\xbd\xd7\xa3\x1b={\x97\x7f\xe6N\xe4wmW\xd2U\x1e;\xfa\xe4\x85\xa88y\x82\x04\x04\xe0\x8a\xed\x92D\x0b\x17{\xf4\xc5P\xdd;\xf8\x116\x00\xf6\xc7S\xf6G0\xa3\xf20 \xda\x0c\xdd\x8e\x87\xc2\x8a\xde\xac\xbe\xafcE75\xfaV\xda\x02Uu\x8b\x1bP\x04\x9c\x81w\xda\xd1\xb2\xeddO\x12bs\x98Qj \x1d\x9b\x81\xa5\x86z\xdf\xae\x04*\xda\x19\xab\x08\xe8\xb2\xfb\xb1\xd3\xd3\xe1I\xe9D\xf7z\xac\xbb\xa0'aV\xbc{\xcb\xe4\xc6M\x0b\xdc\xeah@\x93\x04\xb83m\xc4z\x97*\xa2G\x16\xb1\x12]\xb0i\x90\xa5\xacO\xa1+n\xb0\xc3\x05\xa5\xf69\x90\xb2\xd8\x0c[^\xacZ\xa5\x1a\"\xc55\xf7!H\x18\xb9V\xc0\xed\xbe\xb8Sc\xe0\x13\xad\xc2\x03\x9b\x95\\q\xba\x03P\xd9}\xb1\n\xc8\xae7m3tE9\\lhy\xbe:o\xd6m\xfa\x97)^\xbd\xea7\xb4\xbc\xaaV\x89\xfb\xd6\x88\xf5\xf2\x83\x1am\xeaJ,\x15_9 \xb5\xed\xe4\x83*,\x8dO\x85F\xc9\x10\x83.\xe1\xabMG\xd7U\xacFl\x9c{\x0f]\xc9\xb7\xf8\xd7\xa6\xed\x06\x0d*o\xbf\xe4e\xd2\xfca\xbb\xdd\xf7t#\x1d\x8e\xa8K\xe7/bT\x1a\xbd\xac\xf1\xb1\x15\xca9\x03\x03\x9c3R\n\x9bH\xcb\xdb\x1f\x7f\x90\xf8|)&\x9c@\xfa4\x1e\x13\xa6<\x95G}2\xf9\xff\xdf\xd2o\x92\xcb(w\xc8\xc2\x84\x0ex\xf7\xfb\x1e\x83)\x1e\x8a\xaej\xb7=\x979\xa4\xa37E'0\xf6\xb2\x0bA\xc3\xf8\xb0H\x15:\xe1\xf5\xee\xd4+\xc9\x12\xc5\xe8h\xa2D\x11B\x16r)\x8c\x99\x14:\xa1\xc1T\xbd5\x1c\x0b\xc0\xa0\xcf\xa2M\xf3\xb7\xf1o\x86\x9cV\x94#\xa2\xc8\x99\x01\xe2O\xb821S\xe1\x19y_\xdc\xd3\x91\xa3\xe1\xb6jnt\x9f2:\xbe\x1c\xfa\xda?a\xb3b\xbd|\xa5\xd7}5LN\x99\xc1\xc3`[\xb2\xae\x9a\x15\xf8\xbbx\xfe\x156\xe5z'U\xd96sz\x80P'F\xe5$\xe1\x13\xd6\x17\x1f\x82L \xd2\xb56\xb6\x82T>n\x8bf\x05\xb0V\xa9!\xd8\x18V\x99\xcen\xdb)\x17N\xfb\xb5\xa1\xdd\x958\xae&FA\xe0\xba\xcbY@Sk\xbf\xca\xbcHj\xd7V\x83\xda\xb5\x9b\x82\xef\xcd\xab\xaayh\xeb\x07\xea|\x92S\x98\xc2\n_\xbf \x1f\xcf>]\xfe\xb7\xcfa\x87<\xf4\xe1\xd3\xf9\xbf\x9f\xbf?\xbb\xfc\xf0\xc9\xff\xcc\xc5\xdbO\x7f\x9c\xbfy\x1bx\xe2\xfc\xfd\x1fo/\x824\xde|\xbe\xb8\xfc\xf0\xdb\xf9\xd9\xfb\x00+\xff|\x1f\xea\xe3\xec\xdd\xbb\xf3\xdf\xcf\xcf\xac\xd2\xd8&\x85\xffz\x7f\xfeo\x9f/\xfc\x0f|\xfc\xf4\xe1\x8f\xb7\xef\xcf\xde\xbf \x10y\xf3\xe1\xfd\xe5\xa7\x0f\xbf\xff\x1e\xe2\xe5\x8f\xb3\xdf\xcf\x7f\xb3&M\xf95\xa3K\x10\x8aA\xf1\xad\xa0\x8f\xacpp\xf2\xa4\x17\x06!\xe5\x927~\xf5-\xfek\xfcgA\x1d\xa2\x05\xda\xae\xe2\xe9\xb0\xec\xb2\xc9\xe8^y\x8d\xfdHT\x14\xfd\x8a^\x0fI\xa1\x01\xf0\xfd\xfd\xda\xf9\xc5H\xd5[\xf5\\V\xecD\xff\\\x96Iy\xc3\xc4B\x80\xbc\xfa>^\xa3\xbf\xc2\x0c\x01qU\x0ex\xbd\xaej.V\x8b\x9b\x8e\xf2k^\x88\x7f\xf8\xba^#\xbf\x01q.\xf9\n\xc8\xbc\xc3\x98\x86\xceTvX\x8e\xf5g\x83\xbao\xaa\xebmO\xae\x8b\xe6N\n\xd1@\xb7\xe37\xfb\x1a\xff\x99u.Q\xfbr.\xf5\xa9\xb4\xb37\xd7my\xc7\x95\x15\x9es\x81\xf4\xf7E7\x8cJGQF>\x9aQ>\xbc\xc6\x7f6\xf7\xce\x18z\x0b\xe3\x1f\xf3~\x88\xafJhN\xe4YE m\xda\x81~\x17\xe8\\ \x9e\xd7\xe8\xafX\xd72\x90\xa1\x178$\xd8\xa7\xb6\xc6F\xc6c\xee#\x9b5\x0e\x0d(:\x88\xd1\x1e\xf3\xffumM{y\x9c1\x1a\xa3\x9a\xcbAC\xdb\xde\xd1!\x0d\x017\xa6.\x17k\xc3\xe9\x81\xadC\xa4#Yo\xebz]\xd5#D\xaf\xba\xe1)]\xf4\xecG\x1c\x8f\x06+\x95\xa87\xcaDa\xf3\xf4a\xcb^S\x08\x93\xcc\xb8\x886*\xc3`LP3s\x17\xc7\xccC2\x19\xb1\x1a\x9dH\x0f\xf4L%`&U\xd3\x0f\xec\x0e!7\x0eO(qe*\xbf\xa1n\xf4,a\xfc\xdd\xd3A\xa5J\xd1\xe7\x95~\xa3\xe5v(\xaeE \x7f{}\xd1[\x8ba\x16V\xe0l\xb1\x81\x9e\xabe\xe9\x9fk\x182\x19\xae[4\xd1\xee\x9f\x80 \x14\xe5\xeb\xd0\xa6P\x94\x89\xbd\x9aDe\xc3l\x99c\x9ba1E\xe9\xe9VT\xd9B\x1c,dUU]e[WeC\xad\xac\x8a\xec\xdc\xab\xd2\"\xd6W\xd9\xdc\nr$\xc6\xe3<\xab\xacl\x98uV\xf5\x90\xd0\xfdDk\xadE\xc5\xd9G\xf3\xac\xb7\xf6\x18G[n\x8e\x15wl\x86\xbe\x81\xda\x0e\xdaF\xcc3\xbe1\xc7\xb4\xde\x06W\xa31\xc2\xb5\x8c9\xfd:s\x84\x12 \n \x84\xa3\x0e\xab\xd5\xe9\xb8i-\x82~\xe4\x9dy\xa5\x99`\xba\xb0y\xcd\xdcl\xc1\xef1\xae\x0d\xda\xbd\x93\x18\xee\x10\xa1\x81Z\x8e\xed\xab\x9e\xb3$6\x0eQ\xb6C\x8e6\x05\xb4\x98@&:V\xf3\x07\x13['\xdbA\xc7\xed2D\xbcpG\x84^\"\xac\x13\xef\xde\x84?\xca\x16\x1c~\xee\x08'\xc0\"qB\xb1\xb1LX\xca\xdc\xb1d\xc1'q\x12\x01\x18%!\x9a_d\xf4k\xd8V\xfc$\xdc\x93#\xf6|B\xaf\xf7\xdb~\x83\xb6\xcb\x90\xe5\xb2\x8fZ\x80\xa3\xb6\x95\xe0G\xb6\x98\x1d\xd8ob\x8d\xb0\x17Vm\xdf\x0bm\xe3\x13\xa8Z\x90eUd9Sy%UpDH7\x84V\xa1es \xa9\x1a\xa6\x12\xf8\xd9\x9f\\W\xddc\xad&Q\x8b5 Z\xadI|b\xc5C\x9a\xd6\xa6V\x94\x8f\xd6\xf3\n\xa3\xeaQ\x0f\xf5GR;.F\x82\xe4\xd9\xb0\xdb\x884%\x05\xe1\x99\xd1\x846\xd9iZ\xa9\xe7\xce%?h\xfc\x13\x80\x96\xc4W\xe4s\x80\x16\xde\x94\xe2\x99\xd8u\xe0\xd9\xbb\xb6#o{\xa6\x03W\xfd-\xb2\xe9\xc6\x06\xdb\xbb\xf7\x8d\x1d\xbf,\xc8\x964\xec\xd4\x11\xa9\xe2\xd3\xcdXb\x9bl*Z\xf2\xcfPW1\xd9\xf0\x02\xc4 f\x95\xae\xc2c\x8b\xb1u\xcev*~'(\xf8\xe6y>\xee/\xd0\xf5\xe1J\xe0\xddD\xfcZ\\R\xf2L\x00\x1fT5q\"\xab\x89\xb7\xa2\x07H\xdc\xc2>\x14\x0f1\x95\xae7[\xaa\x9d\xf1\xc4_|>\xf9M\xc4\xb8*\xf7\xc5P\xf5\"\xec\xaa%\x05\xa4\xd6\xbd\xadp\xbb\x17L\xae9A\xfc\xc6\x03y`\x0b\xf1\x80\xf3n\xf0+\x9f%\xb1\xcf\xc0\x08\xa5\x04\x0e\x041:!V*\x9eL\x93\n(\xbd\x02d\x83\xa8\xb2&D\x86\xbb\x99\xc0\xfd}\xc5Y\xcf\xe6\xf9R\xdc`\x81\xc8('Q\x0c\xc93\xb9P\x9f\xde\xbe\xf9\xf0 wD\xb4\x9d\xf8\xeb\xd5\xef\xe7\x17\x97\xd8\xd6\x9f\xe1\x10\xf9\xed\xed\xbb\xf3\xf7\xe7\x97\xe7\x1f\xde\xa7\x98\xce\xb17>~\xfa\xf0\xf1\xc3E\xf2\xe3\x81\x81\xfa\x1e\xe6\xe3F\xdeP\xa6\xff\xbcA\xc4\xb5\xcb\xc8\xc4\xe4vH4[q\xd1\x10\x91\xb8\xe9T$r\xb2\x12\x9b\x1b\xcd?\xdb.\x0f\xf2/zg\\-\xe0\x82s,\xe4\xca\xfe\xc5+\xd9>S\xa7\x8ctX6\xed\xa0l\x11^s%\xbeF.?\xf0\xbba&\x97i\xf9\xefU\xb5\xb6Q\x86\xf2*\x83\xd6\xa9\x97\xc7\x02\xdf&>>\xf8\x1fqf\x8a\x1d\xc6J\xd9\xd65-\xe5\x15N\xe4\xcaV\xc7\xf1m\xf1\xe0; \x07\x91\x9b\x8e\xcb\x16L\xb4l\xda\xa6\xaf\xaekz%lO{P\x14\x03\xe7\xb7_\x18d\xfaG\xf3<\xa49>\xd2\x1c/i\x96\x9f4\xd9S\x9a\xe5+\xcd\xf0\x96\xe6\xf9K\xf3<\xa6\x19>\xd3)^\xd34\xb99\xd5s\xea!\xc6\xfd\xa9$\xe2;\xdd\xab\xf7\xf4\x10\xfe\xd3}yP\x1f\xd5\x87z0/\xea\x01\xfd\xa8\x8f\xeeI}\xba\xbe\xd4'\xe2M}\x0c\x7fj\xecB0\xd9\xa7\xea\xa1f\xc7\xa6\x8cM\xf0qil7M\xe3\xb0/#\x7fC_G\x86\x81\xdd\x0cuw\x9ay\xa9\x01_\x98\xe7\xb2\x08\x08d\xee\xae\x85\xe2(\xa7\xca\x17\x07\x17\xd8S\xe1R\xb3\xde\xcc\xf0\xa2\xa17-\xc4\x87f\x18vq\x1fZ\xfc\xd4CH\xcc\xf1\xa2\x11\xe1\x9etG`[\xd9'\x1a\x81M~'\xdc\xd8#6\xaf\xd8|\x11\x87\x07\xaf?\x0d\x9b\x05\x94\x9e\xc7\x9f\xe6,\x8e\xcf\x9fv\xf81O\xf4\xaa\xd9d\xa2#Nq\xc5<\xc2\xe8g\xfa\xd6\x1c\xefZ\xe6\xac\xdc\x16\xfd-]M5Y-?\x1d\x1a?r\x1e\xe0'\xb0|-\xbc-p\xe7bt\xd8\xb9\xe3\x9a\xe1`\x8c\x8f'\x01\xfe\x00-\x85k\x1c\nQ\xf9\xc4\xb7\x01r@ V\xab\xd1\x9a\xb0\x1a\xa3\x9a\xc2C\x8a:yb.\x9e8J\x02Z\xd4D\x10\xdd\xe2$qb\xc9B\xe8 h}\n\x86\x02Z2\x92\x02\xda\xe3\xce\xc8\xe4\xb3\x00#\x968\x1fi\xe7\x02y\xfc\xb9\xf1\x9e\x14\x89$\xf4dT\xa9X\x0c\x8c\x8eOh\x92\xb4)\x9a6\xfeY\"\xd4%\x976\xd2\x99\x9ba\xdaH'!80BA\x1c\x07\x89\xf0\x87\\s\xfe\xc9\xc3\xfa\xbaQ\xa5/H_57u\x82v\n\x0e\xc4{J\xe8\xb7\xa1+44\x04\x18O*\xadj\x95\x9f)\x14z\x02\xf7\xd6\x9d*#\xdb\xa9\xe8C\x8c)\xfd&\x85\xfa\xce\x8c \x1b\xc7\xa2\xbd\xf6\xacZ\x8f\xe9D\xbf\x1ba1V8\xab1\x0c#\x16\xb5l7\xbbQY\x13a\xa1\xb7\xc5 \xe2\xa7 \x05&\x85$\xdc\xdbz\xe8\xe7\xc7K:\x07\xa0w\xef\x86w\xacM4-\xd0\x1d\xe8\xce\x0fvw8\xb0\xfd\xc7\xcb\x05\xbd\x93\xf9\x81\xefd\xd9\xe0w\x12\x0d\x80'j]g\xd5vu\x16}V@\xbc;!\xee'\xe9B\x9c\x83\xab\xec \x8c'\xa1\xe0x\xb2\xa7\xb9\x99\x1d,o\xd1\xf3\x0d\xcc\x1b4O\xf64\xb0\xb9A\xf4\x16\xb9]\xbb\xed\xa6\x07\xd2'd^\x16\x04\xf6\x13J/\xce=\x98uG\x06G\x12\x8b\x8ck\xb5\x7f\xe9\xedU>\x17\xc0\"\xea+\x86\xd1^\"\x16\xdd{\xe9\xf3_\xf7&\xc4[\x18{/)\xee\xdc;v\x92\xd9\x97\xf6O\x12\x8c5\x0f\xc6\x99\xa7\xf7\x98\x1e_\xee\x8f-\xcf\xeb\xcd\x17S\x1e\x167\xd3b\xc9G\x0d\xce\x9e\xbd@\x1cy0\x86\xdc\x075@A\x06\xde\x89Y$f<\x1c/\xbe\x0c\xa3>\x04D2\xf6!\x15\xf5\x90\x86wHC:$b\x1c\x12\xd0\x0d\x89\xb8\x86$DC*\x96!\x15\xc5\x90\x84_\xc8C.\xc4n\xa3\xd3\xd0\n\x9e8\xef\x00NaO\x08\x85\xfdb\x13\x96G%<\x12\x1e\xe1\x00H\x84\x83`\x10\x1e\x11}\xf0\x14q\x07\x8f\x8e88,\xd6`\xe1\x94\x96\x1aIG0.\x18\x84=-\x00\xdb\x1b|=\xf9\x1e0\xc4\",R\x03\xae\xdd\xf8\x89\x98R4L\x0f\xb2\xf6\x05X\xc7\xba\x9c\x11X\x8d\xed\x89\xc5\x02\xaag\x05S\xaf\xd2! \xf8\x06\xb3A \x8e\xe9\xd8\xb4\xa0\xf8\x95\x07\xf4\xc5\xd1F\x9c\x00\xfb xM\x07\x1f\xe4#\xe3\x1em\xf3\x96\xb8q\xbc\xdfOX\x85\xb2{\x8b\xba\xf7\x82p\x0e\xc4\xa9\xe7L5\xe6\xca\xdb\xf7\xa8&\xb8\xe8B\xae\x97\x04\x97\xc5\x81\xc7\xe7\xf7\xc6\xb8\x7f\xb1he8\xd7\xcc\xbf\xb9.\xb5\x89&z\x97\xec\x04\xa7Y:\xdf\x19\xcb\x93\xc3w\xb6\x0b,a\x11\xb3\x0d\x8f~w\x97G\xa2\x16\x8d\xdc\x0fQWW\x8e\xf9\xb1?\xab\xeb\xdc\xdc\xc6\xc74\xb1\xb1\x01\xcc\xb5p\x87\xd2\xc4\x12\xb2)\xf8=\xb5\xf2&P\x1d\x1fP\xaa\x83\xaa\x9f\xa6\xfdM\x85B\xf6\xd2\xd2-Y\x98t\x1e\xde\xd1]\xe2\xd7\x9a&L\xff\xc7\xfe\x8c\xef(\xdc\x13E2\xf6\x9b\xf0\x15mk\x07\x11\xcc\xb5^{\xfa\xd6\xa4\xa6\xeb\x81\xd0\xfb\xcd\xb0#\x95H\x86 \x0cl\x10\x05\x07[\x1a:b3w\xbd\x83\xa2\x89\xc5fc\x1e\x90\xdbf\xb8\xe2|b3\xb3\xac\x9fN\xeb\x8c\xcd\x10\xdfO-\x19\xba-%\x96\xbd\xa1\x18\xb4\x19\xe1\x0f\x8aE6 \ni\xe7\xb8\xd6\xa1'\xa9\xd8\xd8\xab\xc0\xcd\xceDmN.\xab4)\xe6|\xde\x9f\xcf{g\x05\xac\xa1\xb4l\xab\xb2S\x81\x96*\x17\xc5\xf8\x15\xb1\x0f\xe5D\xec\xf9\xea\xa6i;'\x0b\x80\xfc\x8a\x1c\xf7qoo\xe2\x8e2e\x08\xbdT.\xbbX\xa2#{\xa1\xaaq\x17s+\x06\xb6\x93-J\xac\x1f\xda\xf0\xc8\x81\xb6[\xd1\xee\xe4o\xf60/\xaa\xa6\xa4\xafI\xd9\xf6\xf7m\xff\xa2_\xdd\x91\x97'\xaf~T\x0f\x89\x8b\xad!\xbfA@\x8fY\xcb9\x1f\xf4\xfe\x9a\xaeV\xc0\xc7\xcd\xa7\x8fo\xd4 (\xae\x8fp.)\x89\xa3\x91\x1b\xd7\xff\x84\xbc\x15w\x94\x0c\xedK\xd3z\xc8\xac\x1c\xea\x8c\xd0\x0c\xe7/\xf0\xb1H.\xf5c\x9a\x1c\xec\xed\xf9\xaeihA'\xed\xa2njA\xd0?\xa3$\x01\xa1\x1c\xca\x04\x13\x99Q2\xc9\x89\xed\xbe\xb7X\xef\xc9nmh\xd1$\xea\xf9<\xa4;\xba\xa1\x85S\xa9O\xeb?\x94N=v4@\x9b\xe6\x06GI\xa5'UOH\xab\xee\xf77C\x9b\x1e\x83\xbf\x88\xab\x1cZ<\xc1\xfa\xfe\x86qL%`\xbb\x10S\x1e<\xa6\x12 \x89\x82a\xaas\xdeC\xec\x98J\xc0\xfe1\xd5i\x8f\xd0=\x9c\xeb\x1e\xe9|?\x0e|\xa4\xa3}\xb9\xf1\x91\xae\x0e\xeb\xccG\x18x\".}\x84\xb3\xc7p\xec#l\xec\xdd\xbd\x0f\xed\x89\xa5\x12\xf0\xf0\xb1 >\x00\xda4\x94\x00\xb4`\xa2\xf6\xa8\x1a\x93vU\xf3\xcc\xc3$P\x01J\xc9\xbbP\xbet\x8diJ\xe6\x0c\xe8\x01\xb4P\x86\xf7T&f\x80\x11\x0c:\xc8\",\x06L\x806\x0b\x9e\x00mq\x90\x82\xe2\xcct\xf6M\xc9U\x81\x12\x19}}1\xd8\x82C/\xe8\xda\xb7\xef\\\x13\xad16\xcf\x13\xb6\xe1\x02\x01\xfb6\x17Q\x98\x03J\xc5r\x9d\xfa\"\x98\x9dez\xcc\xbc\x15\x1efr\xa0\x108\xa1\x84q\xc7\xfc\xee\xd0\x1ea\x0e\xbc\x9e\xfb\x84\xd7\xa7E$\x9bO\xec1\x85\x83\xdb\xd1\x040\x05F*>\xaa\x89\x0b\x9c?\xaal\xa8\x05FdV\xa4\xf1D\xf0\x85A\xa3\xb7\x81\x18q\x08\x06\xc2\x981\x86\xd1l\xaf\xd4\xcbp]S%\xbd\x9eZ,\xf01,r\xa9\x81\xcd\x05\x8dX\xe4\x9c\xb0Hh\x18x\x84\xec\x11@BB\x1a\x89_\x1fq\xc0$$&\x9f\x82\xc7O\\tM\x07\x97 \xc4\xe6!Lp\xee\xb0~\xbc\xc0\x13\xe2\x01\x9f\x90\xd4YD\x80\x14$i\x1e\x97\x01\xa3\x90\x08 \x85,\x0dJ!^`\nA\xc1)\x04\x07\xa8\x90\xbd\xcf\xeft\xc0\nBL\x006P\xd0\n\x99\x05\\A\x88YP\x16\x12\x82\xb3\x90\xa0\x8c%\x119K\x92fr\x06\xbc\xc5\xff)z .d.\xcc\x05\xa1W5(\xd4\x85\xcc\x80\xbb`\xbd\x00\x00\xc6\x0by!(\xec\x85\xf8\xa0/\xe4\x00\x0b\xbb\x1c\x14\x86$\xc1aH\x1c\x12C\x02\xc6\x95\xe9\xd0\x18\x8b\xd0\x08\x94\xb1\xfe\x80\xa1e\xf2`\xa4\xca\xab\xc0\xaf\xa2:\xf8j\xb4dH3\x93\xabLe\xa8\x93\xf2x\xc5\xd4\xad\xe5\x90\xa4\xea\x10\x17rT\x9d\xe7dS\xf4\xc2\xf6\xae-\xcb \xfc\xdd\"\xc2\x0f\xf9\xe8 \x7f>\x96>\x12R\x14\xa9\xe7\xd7\x81\x95\xb9i\x01Z\xe0\xe8\xe3\x84x!\x7fy`H\xdf\x1eT\xa2\xc2w\xa2\x8cr\xc9\xe1_\x9f(M\xf0\xd8\x9b\xf0k\xc1?\xc7\xe7\xa4\x1az\xadJB\x03\n\xe6\x8a\xb4l\x1e\xbeV\xc2H\x8e\x7f\xe5Q\x10\x15\x99\x97\xc7#\x1d\xce\xf5\x1b\xe3\x9a[0/w\x1b\x03\xbb\xa5-\x88\xee\xb7O\xa9\x00\x12\xad\xf9\x11\xa9\xf2\x11\xaf\xeb\x91\\\xc9\x03\x9f\xff\xe9\xd5:\x8c\xca\x1c\x82Z\xb0>\xc7S\xa8\xc8\xf1(58\x9e\\\xd5\x8d\xc1\xa9\xb3\xa1\xe0n\xfa7`\xf5\xae\xdc\x01+\xf5\x94\xfe1V\xbdZn\xef\x17f\x81\xc9\x9cS\xc4=AL\xf7 * W\xc9\xd8\xb6\x15\x86fK\xa1\xb9\x8a!\xd6P\x8cZ\x9cr\x1c\x87\xe6\"\xcf\xd2\xa8\xda\xe82\xfc\xeb\x9f\x86 s\xd0b\x82\x9a\x81\x19\xf3\xec\x81\x7f\xa7\xc3\xbf\xed\xceV\xab.\x1b$\xdb\x97\xed\x86\xee\x1b\x13\xcb\xfa@<%\x81\x0b`\xc0H\xe1\xf7\xbd}\x06\xf8\xea\xf9o\x1al\x95u}B\xc8\xf9\xfd\xa6\xe6\xe0\x87\x9e\xf4\xab\xbb\x13 u\xab\x9a\x81v\xeb\xa2\xa4\x88\x96\xc8+\x853u\xbeS.U\n\x1a\xb1\x96\x1f\x9e\xbc\xe1\x8a\xac9\xdc\x08\\w\xe9as\xb9\xc3\xc6i\x19`\x15\x16\x83\x9bn\x07;+>\xa6\x19\x13(hF\xe5\\\x89\x8c\xf8|CV\x8d6\xa5\xc6{\x1c\xcc\xe2Ix\x83\xc1\xfc<\x10\xbf\x80?.\x86\xe4\x15\xeb\x83\xfd)j\xadWS[\xe0\xf8\xc7gR\xdac\xb0\x8c\xae\xad=\x00\xe2\x18n\xa2\xe0\xef\x8e;Uv\xa7\xa6\x1a\x8c\xecLc\x0e\xe6\x1b\xddtm [\x13-\xfdIR&\xe0\x88\x9b4\x91[)\x0f\x1eq\x93$\xc9\xc8@\x8e\xb8\xc9#n\x12\xef\xfc\x88\x9b<\xe2&\xff\xa2\xb8I\xe9\x05\x0ci$>\xc3-I0\xee\x92Q\xc3P\x0eO\xb5\x84\x7f\xef9\xb8\xb1`\x97\x06n\xd6\x16\xcc8TB:\xca\x19 ;\xa5`\x14\xba\x11\xfb\x13\\\x87\xb1sxc\x88J\x0f\x0c)v~\\r\xbf\xbbD\x90\xaa\xadI\x86vCj\xfa@k\xa1|\x8e\xee\xfb\xb2\xedV\xbd\xd0\xa2NH\x84 \xb79\xb0\xb9\xe2\xd9\xcb\x85\xe9X\xe2F\xdbU\xb5F\xcc\xf8\xac\x1b\xae%k\x9a\x9a\xbcf\x00u\xf3\xb6P5d\xdb0}\x9c=\xefP\x93\x9c\xd4U\xcf\xe6\xaf\x15\xf4h\xdf\xeb`\x06h\xac\xd7\xab\xa2,\x11Mw\xa2\xb2\x8d\xea\x84\xb159S\xc3\x93W\x02\x8e\xbdec-\xb6\xc3m\xdbU\x7f\x82t\xe9hI\xab\x07\xaa\x95\xdaf#p\xe8\xa1)\xe0aF\x8dg\xb9\x1d\xe4\xca\x88\x93\xca\xb8ZEG5nrS\x18\x8f&\xa2\x00\xa3\xeeF\xbb\x18\x8afUt+]\x80\n9\xdesl\xcc}\xd1\xdd\xd1N\xfd\xe6n46\xa1\xfdv\xb3i;\xd6\xa3i\x0e\xe2{\x8d}\x9a\xc3\xd0U\xd7\xdb\x81\xdb\xaf\xc0\x7fu\xed\xde\x14\xca\xdb\xa2\xb9\xa1+r\x0dfx!\xdb\xa4\xf0g\xbb\xaed\xa7)\xea\x9f\x12\x97]^\x1bvw\xd5\xb5u\xbd\xdd\xe0\xd3\x8e\xcb\xaa\xb0$\x8b\xad\xca?\x85p+\xeaZ}*\xc6E\x94Oe\xc5\xae\xf5\xe2\x93\x91vD\xf7K\x13\xa2\xc3x\xfd\xef\xbd\x14 \xeb\x8a\xd6+\xc4I\x08\xd3\\\xf7-\xa1Mq]\xc3=\x8b{\xc9\x85T\xfd\x7f\xdci\x06\xcc\x08Z\x1c\xfa\x04\xfc\xf8>\xf8T\\\xd6\x05\xa7;V\x85\xef\xdav\xd0l\x94\x12\xf9\xab\x1b)\xa5\x0cd\xcc\xb057\xe8q\x17\x7f\x07\x161\x87\x15\x83\x11\xb0\x0ci\xd8\xaff'\x7f\xe3\x1f\xc8W\xda\xd1\xb1\x06\x03\x1f\xf3\xba\xdd6\nq&\x17d\xcf\xb6%\xe8e!3\xcb~L7\xb1M\xae\xc2\xae\xc3\xf8e1\xed\xc5\xa8\xbeA\x1d|\x87\x1elo\x98\x18\xf3c\xde\xe0\x05\x9e'\x9e\x1eGS\xcd\xd1T\x13\x7f\xf6h\xaa\xb1\xef9)O\x1fM5GS\xcd\xd1T\xe3\xfcz4\xd5\x1cM5GS\x8d\xd5\x0ec\xaa\x11\x1c\xa8[\x98p)R\xae\xf4@t\x07\xff\x03\xff>\xd8\xa0x\xd8\x04\x9e\xb2\x1a\xcf\xbc\x13P~\xfc\xc3\xe7\xb1\x9c\xae\xf7\\\xd7\x81E\x1c'\xfb?\xa1\xa7=g}UeQ\xd7.\xbe\x99\x07\x84*\x08\x87\xfa\x15^\xcc`y\xc6\x85@j\x93\x10\xd9\xc0.\xa6\\^\xb3\xa3\x82\x9dGe\xcd!\xdc\xea#\xe3\xd6\xa8qe\x1dr2\xf9\x0cv#(\xb6+\x17\x9b\x1d\xe3\xef\x0d\xbfw\xb0\xbb\xe5s\xb2\xdd\xac\xd4\xff\x0f\xd5=\xed\x87\xe2~\xd3?W\xe1X\x00\x91\x83\xe8\xd5\x8e\xd6(\x83X\x8a\xa0\xc0\x15#\x18\x9d \xac]1\xa6\xb0/:\xa2`\xcbEc\xaf\xbf`\xc3\xc1H\x8c\x91\xcc\xec\xb1S\xf6\x18\xc0~\xf9\xa6\xa3\xcd\xd0\xed\xf85M\xf0\xe2\xe5\xf1\x1a \xdd\x88r\x88g\x8f\x1f\xec\x84=\xa2\x0f\x894-\xb1\xca\xf7b\xe9\x9e\xc4\\\xd5E?H\x86\xbc\x8c\xees\xc2\xb8\xb9\xb7\x8a\xcc\x98\xd8\xd4~.\xaaf\xa07\x14S\xe8\xe4l\xe1\xf1\x0c\xb1K(S3\xc4\xd1`}Y\nGT5e\xc7\x95\x1f)\xdb=\xe1\x05r:\x9d? \xc4\xf4\xf4)N\xe0]\xf4\x81\x98R)l\xd9\xaamN=\xfc\xb1F\x1f\\\xed\xce\xdf\xfb\x19\x13n\xef\x98\xfc\xecIYl\xe0\x90Ec\xca\xf9\xf6S\xfb\xa1%\xf7\xc5\x1d2A|\x8b\xa8x\xc4f%w2\xdd\x815\xec\xbeX\xe9o\x05\xedz\xe2hR\x96=\xe4\x94\"\xc5MQ5\x8c1%\xeb\x0d\x1a\xa6uH\xa6L\xb0l\x98\x97\x9au\x82\x03\xaco\x8b\x07\xaaQ\xd4\x825\xa9J\xa0\x01fVH\xe1e\xe3\xf8\xdf\x7f`\xca\xf9?\xc5\xc8-D\xf6'\xc3\xffB\na\xbf\x14Y'\xa4\xd5\xb2\xc0\xa6\xe29_\x089/\xfc\x1b\xbcoy\xff\xf2A\xd3<)\xed\xbd\xa6\x81R\xfe\x9ad\xa2\x14\x96\xd2\xfdZ(\x17\xd5tN\xb1\xdc\x8c0\x8c\x13B\xfe0\x80\xb4\xc2\xac\x08K\xe1P\xd3\xddVgu\xdf\x12\x8e1&\x05S9\xde\xb1\xeb\x82\xa3\x02-kj\x0d\x8dt\xb4\x84J\x0dN\xee`\x8f 4(\xb4\x9fq\x1c\x1f\x932\xeb\xaa\x1ehGW\xe4\xeeA&\x0d\x18hW\x0cmg\xdeG\x84\xa1\xce\x19\xa6\x97a\xf1\x82\xdc\x8b\x86\x90\x91\x9c\xc2r\xd4;\x19 \xbd\xc3\xec&\xa3\x0ch\xf9%\xae]\xaf\xf5\xc4-z@\xb2o\xc0\x13\x15\xa7\x80]6M\xfa\x0fv\xea\x19\xeb\xfa\xc9\xe7A\xedY1c\x0e\xb9\xe5\xb2\xcb\x14\x8d\xe6x\x94\x0b\x84\x1a^\xe7%\x93\xf1\x9dzp3\xc1\x0e:\xc9\xcc3\xd6\x01Oe\xf3|\xbcu\xe0q\xad\xc4\x9e\xcd\xb2\xbd\xbfg:@q\x8f\xdc\xa5!*c\xf1!\x01Y\xb8\x95\x08Vm\xb8\xbc\xba\x93\xb0\x8d\xe6\x0b\xc8#\xe4\x19\x90\xfan<' \x0d\x8f\x1a\x9e\xfa\xce\xd9)tg\xadY\xd5l\xb6\xc3\xa3:1\xfcyc\xa3\x06|\xe3\x10\xe3\xa8z\x11w\x01\x01\x91\\T\xd4UsG\xae\x8b\xf2\x8e\xc7\xe4\xddR=6\x80\xcd\xb0\x9b\xb7B\xd5e\xf7\x95\xca\x8f\xf2\x15\xc9J\xe2\xff\xcc\xed\x0cS\xcf\xde\xb5\x1dy\xdb\x0f\xc5u]\xf5\xb7t%u\x02\xcc\xe4\xe3\xfb\xda\x13\xf8\x8d9m\xfc\xa2\xa0\xa2%7\xe7\xe9B\x9a1\xed!\xf4Q\xc6\xc7\x04\xc6\xa1>\xe4y\x83\xe1%9T<\xce\x9a{\x00%\xfa\x1f\x8e\x90\xfb\xa2\xebo\x11\xf3\x10!\xfdP\x0c[\x9f[-2W\xe7\xca\x8a[\x890\x0c\xfe\x89\xf1\x0fP.\xb0dH\xad\xb3w\xc2\xfe\xb5\xe5*+X\x9d\xd8\x12p\xa4\xfcf\x8b\x1bE\xa3s\x13\xf6x\x89\xb0\x9e\xf3\xf7\x1f?_^]\\\x9e]~\xbe\x88zB|\xefYAdi/\x19\x81efS^\x9a)L\xc6l0\xd0\xa2\x13\xe0\x9b\xb9\xc8kX\x18\x9a\xddD8\xda\xa9\x08O\x13{\x86\xad\xa7\xe7\x85\xc8\xb4\xe3\xf3\x84\xc4\xac\xd9;\xb4!Ew]\x0d]\xd1\xed\xc6\xaf\x9b\x1b\xc5\xd41\x02\xdb0\x8f/\x19\xc3\xe6\xff\x1b\xceS\xd5\xbbad\x9b\x8e>T\xed\xb6\xe7\xf1\xe5\xd6\xe74\x9a\xed\x1d\xfe\xc4\x87{\xd9\x15\xe5\x1d\xd8[\xe1\xecSJ%\x95r6\xa4\x10\xfa\xf5l\x8d\x98\xa3\x9d0\xbe\xcb\xdb\x8a>@f\x18\xc85\x07\x005$\x00\x1e\xe9Yd\xa7{\xccsz\x8f\xe7\xcb\x7f\x88\xb3E\xc1\x01\xc5\x04\xa9-\x07\xff>\x1dUv\xa9\xfbz\x08zn/\xf3$\xfb\x05\x7fW\xf2)\x97v\xbce`\x906\x0f)1>\xb1\xe5\xab\xe6\x86\xf4[\x8eE|\xbe.\xaaz\xdb\xd1\xe7L\x1f\xdc@\n\x80is\x1e\x13\xf5\x17\x9f\x7fO\x96\x9f\xee\x1b\x1f\xcf.\xfcn}\xf3\xd1\x8b\xff<\xff\x98\xf8\xe8\xbb\xb3\xf3\xdfc\xd2?\x87\xeft\xb9\xef\xa1\x9a\xdb!1d=\xd96=\x1dR|\xf6\xee\xe4\xda=\xb3\xdf\xach\xd7q\xe7\xf10w\xd8@\xeb-\xa6\xd0\xa0\xdd\xb0\x85\xb1\xbba\xbfi\xdd(\xd6O\xcdd\x99\xbc\xc3\xbb\x8a\xe7\x0e[A*\x96\xfb\xaa\xe7\x19p\x84\x1cl;\xb2\xa2u\xb1\xa3\xab\x91\xcfT\xc6\xd86\xb0\x19c\xbfy\xc7?B\xbdPn\xd97E\x91\x04u\xa1\x0f\x1e\x14\xd4\x0f\xf0\x9d\xd2\xa6,6\xfd\xb6V]K\x01\xbe\x86\x98o.\x08\xd4Y\x84\x0e4P\xf3\x17\xe9\x1f\xe8?\xeb\xbf#cn\x01\x9e\x12\xa1]\xcba\x8bDO\xb2w\x01v\xe6\x8a\"&\xd8K\x08\xd1\x94(U \xdcD\x04\xe5\xe3\xe0\xf6\xec^\xe5\xc0G\xf3\x95\xb8\x1b\xe10>\x87\x9e\xdf\xa6ed\xb8@W\xe0L\xf6\xf5\xac]s\xcd\x82/C1\x0cEy\x0bT\x95\x03\x90\xeds^3\xcb\xd8q\x065\xb1W\xf8}$V\xf7V\xc2M\x0d;\xac\xfc1\xc9\x0c\x0b\xf1\xc1l\x92\xf6l\x8a\xdd\xcf6\x11\x0b2\xafx\x0e\x1aD\xef\xfe\x89\xfc;\x12I.\xdd\x18\n\x13\xdd\xef!\xed\xedD#Yz\x1a\x01\xfc\x9d\xd9=F\x93\x0c\x8c-R\x12'\xa7\xdfx\n\x82\xb1\x85\xca\xe0\xe4\xf6\xe9+\x7f\x13\xa9.3Q'\x8f\x07Z\x8c\xb1\"B\"\xea\xe1:)\xfb5VPfI\xce\xfd\nh\x06\xb26\x1dU\x9b\x8a\xa8ME\xd3&#i\x93P\xb4\xc9\x08\xdaD\xf4l:r6\x1d5\x9b\x88\x98\xcdE\xcb\xc6\xce}\xff~\x08\xa3d\x01\x0f\x8b\x90\x0bj\xdb{C\xc7\xee\x1b\x19\xbb\x0fT\xec\xa3!b\x0f\x82\x86=\x10\x12\xf6QQ\xb0O\x13\x01\xfb\x04\xd0\xaf\x87F\xbe\x86n\x92\x13\x8b\xba\x90{\xb4\xa4\x9b[\xd0%\x00\xd91\n\xb9Tv\x11\x17\x19\xc4\xc7\xbb\x05\xe5\xe1\x19\xbbO\x0c\xb7\xb4r\x05\x86f\xdd\xe2\\\x9b\xf3^Z\xd9\xd3\xf7\xaa\x15\xcd\xbah\x8e\x93\xe2\x89\x0c\xabVc\xae\x1e\x08 \x84\xb9\xb9.0\x7fw\xdb \xd7\x92\xf8\x15\x93Cy\x16\xa8k2\xab\xa6\x89\x7f\xaa\xb4[$q\x83\x16\xed\xdcG\x9e\x8b\xa9F\xcf\xb8\xa2\x1a{\xe5xKEnf\x82\xfe2\xa5]\x8fw\xd4\xff\x83w\xd4\xb8\x1c\x9c\x9bX\xcf\x9d\xcd`Y\xd6\x03^\x9a\xc5\xd73\xb7\x04\xeb\xf1\xb6|\xbc-\x1fo\xcb\xc8\x86:\xde\x96\x8f\xb7\xe5\xe3m\xf9x[\xfe\x8b\xdd\x96\x17-{:\xb5\xe4i\xa0\xdc\xe9\xac\xabJ\x14i*\xa2J\x8cZ\xa6\xee\x8d\x91xp@qml\x98S\xab\xd4_\xa74\xde\xf1\xac\xfa\xa4\xf8nY\xb0.\xe9\x9e\xee\xef\xe6\x15\xdb\xbc\xc2\xe3\xbbP\xbb\xc5kt\xa2\xf1>\xff\xc7\xef\xf0\xd3\xb6$\"|\xde\x8b]\xfaI\xab\x10 Kx\xc8\xf0\xd31\xd4\xd32X\xe1\xe8\x07\xe3\xc7\xff\xd50\x7f\xfdKV\xab\xe3CA\x0f\xcb\xc0\xc7\x8b\x91\x10\x8f-\x81\x00w\x80\xfb\x0c\xad\n\xd9\x18%\x13\x9a&\xe9\x18G`\xfe\xfdpq\x04!V\xce\xd9\xd6\xc1\x857\x04\xe3<\x1f\x17\xff\xb9\xa8\xbd\xc9d7\xba\xc2\x12\x9a\xfc\x8cV<\x1f\x85\x15k\xf6\x1d\xd3\x1f\x81:\xb7\xd4\xf0]\x8b\x10Re\x0d\x93E\xc6\x99\xca\x17\x01\x88;CS\xe9\x8b\xa1\xeaE\x0cvK\x8a\xcd\xa6\xde\xe1xT\x98\xc0\x0b'\x80U\xe0\xa6\n\xf1\x80\xf1\x9e\xf7\x13\x9b$\xfa\xce@\xbfW_\xb7\x95\x81AKA!QY\xda\xe7\xe8P\xe3\x1f$\x07\xcf\xaa\xef\xd4\xdc\x1c\x80\xa4\xbb\xe2\xac&\xf3x)T\x06Q\x8d\xcd\x8a\x16\xb5\xe4\xf13\xb9\x08\x02\xd8\x8e\xecb\xad\x80\x8a\xbdu\x03s\x88\xdb\x88R\x8a\xfc\x84\x9e\xf6FjD\n\xff\xc4\x1f\x1cK\x00\x8d-\xb9\x18\x90\xfeJ\xd8(2\xbd@\x90C\xcaB\xef\x06J\x05\x85\xba?|\xd1\xa0\x107\x07(\x1f\x14\xef\xfe\xd1\n \x8dmpJ\n\xc9&\nwU\xd75\xa4f].\xab\xe3\xd1\xd8k\x98\xa9b\x0f\x1d\x8d\xbdf;\x1a{\x8f\xc6\xde\xa3\xb1\xf7h\xec=\x1a{g\x19{/\x8d-\xa5\x9d\xf6\xb6\"\xff7\xe7U\x8bm\xec\xb6\xa4\xdb\xfd\xcc\x8b\x00\x98\xed\x84I\x07r\x12\xd8\xda\x80\x00\x1a\x9dje\xd0\xd8e\xeeTX\xfe\xd4\xb3\xfe\xb3B\xb7\xc3\x11$n$\x01\xac\xa3Qs\xcc|M;\\\xf1\x1f&\xd9\xf8,\xfd\xc7\x18\x85\xa2lr]\xacV]\xaf\x19\x1e\xaf\xb7\x03\xd7y5\xae\xac \x0e\xab\\\x1f \x95c\x15\x85X\xd5Ki\x85W]\x83\x01\x0c\xd51\x87M\xaa\x0e\x195o!\xf7\xed\x10\xa1\xc9\xa6+\xc4\\\x85\xf6\x83\x9a\xa5\x962E\x99\xe6\xa7\xd08\xf3\xadI\x1e\xbb\x91\xfd\xadO\xb5\x0f\xb9\xd6 A0\xc3&\xa4\xdb\x81<\xbb\xf1\xc3\xc5Y]\xff\xde\x96L\xe1\xeaE\x11\xe4\xe4\xfd(R\xa3\\u\xfa{\xe3\xbbv\xe2U_BV\xbf<\xb2:\x80\xc3}]\x177|o\xab\xac\xad\xad|P\xca_\xf1tCv\xedV\xd7\xc9\xc0\xe4r\xa2\xa6'\xbd~\xb8\\3\x950O\xaf-.\xa7\xb9W\x92A\xb0pb\xcdHR\x05\xf1\xbd\x17\x0f\x17u\xc3\x0bq/\xd6+\xc8C)lQ\xfe\\\x15\x19\xe7\x95\xc5o\x9c\xf3\x98\xd7\x0f\x97\xb1\xa3\xa1\x12\xe2\x1f\x9az\x07\x05.\xd6\xb2l\x7f\xdb\xf1Z\xfc&\xc1\xfe\xb6\xdd\xd6+nr4\xab\x9d\xc0K\x99\xb3\x82T\x0f\x0f\xdf\x92\x04k\x90Fi{\xcf\x15{\xf1\x1b\xa8\x8eE\xc3\xd3\xb7\xf5\xd2\x7f\x02\x13iQ\xd96\xaa\xd4\xb8S\xb5\xe4\x9cS\xafy\xa8\xbf\x9c F\xbb![\x1e\xef|G\xb3g\xcb\xee\xc2\x9d\xbc\xba\xba\xb73\xc9.?w\xbc\x13yZ\xfaj\xb0C\x85z}\xbf\x0d*\x1c\xd9\xa2\xc7v\x92;}kR\xd3\xf5 \xeb\xd0\x0b\xaf\x96\xb8*\x835\x07\xb64t\xc4f\xeez\x07!\xb5\xc5f\xa3\xcf\x89V\xe6\x1d\x9b\x19,et(\x9dtxn\xb4\xce\x08T\xf2\xe1\xe9\xa3\xba-%\xd6\x1d\xa1\x18\xb4\x19\xe1\x0f\x8aE6 \nigOO\x01=\xc9\x1b\x9b\xbd\n\\\xad\xd2\xea\xe03Y\xa5I1\xe7\xf3\xfe|\xde;+`\x0d\x85\xd75b\xba\x11-\x95Sq\xfc\x8a\xd8\x87r\"\xf6|u\xd3\xb4\x9d\x03y\x97_\x91\xdd\x8d[\xef\xa8\xa3\x0f\xb4\xeb\xe9\xfe\x17Ktd/T5\xee\xe2\xa2\xa3\xf8N\xb6(\xb1~ \xe9\x06i\xbb\x15\xed\xec|\x9d\x84\\TMI_\x93\x92\x97*~\xd1\xaf\xee\xc8\xcb\x93W?\xaa\x87\x84*a\xc8o\x10\xd0\xea4\x04>\xe8\xfd5]\xad\x80\x8f\x1b\xa6\x84\xca\x13Pdu\x85sII\x1c\x8d\xdc\xb8\xfe'\xe4\xed7\x98]|~0Ea\xd4\x8e\xe1\x9f\x88rl\xbc\xc6\x14dA.\xaa&[\xfdeV\xcf\xae\xc5\x8b\x93.\x1a\xc9`\x02n \xc9\xf7[I\xf4\xf0X\xd6\x8c\xdd\xb1W\x9b\xb6j\xf8\x94\xa2\xf5\xa1\xc4\x88\xae\xb6]\x95\xdf\xa3xy\xece\xdbU\xc6\x93\xb4)\xbb\x1d_\xf2+G\xfdH\xe9@D\x0f\x8fd\xf8\xd9(\x06\xa8\xbd\x12\xfa\xf6~\x1b3\xee\x8au\x17+\xc1\xf3\xab\xd3Q\xed\x7f.\xac\x07\x9a!\xa5\x00\x0e\x9e\x19\x04\xf5\x02o\xc0\xc9wn\x9e}5'\xe6\xc7\xb9\xed*\xbe\x93\xab\xe1\xef\xbd\xfe\x92\xce\xd1\x88^\xb1\xd4_c\x94\x86\xeeZ\xb6\x9b\x1dQ9(\xc4Wc'\x10\xa5\xbd\xca\xd11I\x81\xf4\xaa\xe5dO2s\xae\x9an\x91cJ\xbb\xa5\xaa\x13\x8f\xbaN\xf6\xa8\xb2\x13\xbf@\x08\xe1S\x90\xef'\xe2\xc1 \"3\xe2\x86\xfd\xe9\xeaz\xc2\x9f\x8f\xa8a!E\xd7\x16\x1d\xb7\xcc\x84\xa3\xb6\x12\xe25\xb2\xe4\x99\x9f|{P\x89\n\xdf\x892\xca%\x87\x7f}\xa24\xc1coB\x9e\xa0\x90\x0e\xcfy9\xe3\x11W\xd5\x80\x82\xb9\"-\x9b\x87\xaf\x95\xf0\x18&]\x9f\xd3\xbdK3.\xd0\xe2\xad\x8fL\xd7\xed\x93\xaf\xce\xf7\xc57v\xc9\xbc\xaais38\xde\x13\xbb\x8e\x8e\\\xb1q\xc1\x8c\xf1C\xdf\x86[\x13Q\xbd%\xdb/\xe4=\xf5\xbe]mk*\xc6\xd8\xa7\x0f\xf2\xff\x94\x03\xc3\xb7\xd1\xb0\x19I\xb2\xd4\x18/\nrI.M\xa7\xcfLk\xcdF\xdb\xa1\xce\xc86\xc1-$T\x12\xd80\x93\xe4\xb6o\xbf\x13\xef\x9e'\x9e\xaaQG;\xc0\xcc\xddn\x91s\xec\x00\x89;>G\xb8\xce\xdf\xf3\xb92\xc72\x1a\"g\xe1_G*\xd9\x93\x94#\x86\xb2\x97!S\xe8\x88\xb3\xc6\x9e\xdd\xa4/\x0b1\xfczu\x9a\xe9F_\xaf\xc97\xd6W\xc4\xdc\x1b2\xf6\xc6H\xa7\x19z\xfd[oa#\xeft\x13\xef\xfff\x03o\xfa\xf6\xfbk\x1f\x01\xce\xc7\x9f%\xf33\x05L\xffo;\x9eh+W\xdeC\xfa\xab\x08\x0e\xea\xaf#\xf2\xddc\xd9\x9a\xb8\xd0\x19 ^\xb5\x16J\x12\x98\xbc`\xd3N\x86\xa3\xf30\xe949:\x0f\xffR\xceCW\\\x91\xff\xab\xc7KH\x94%\x9c7\xe2\xe5\xc5\x84\xd9\xe7O\xe7\xb9g\x8f\xf1\xbd\xff\xd5\x8e\x9d<;\xf0\\op\xc6'\x94\xaeTO4\x02O\xf7\xf8\x9at\xe69{\x8f\x00\xce\x99\x9e\xdb#\x803\xdd+{\x04p\xc6\xfc\xacG\x00'\xb4\xe5|\xa8q\xff\xe9\xff.\x00'\xa6J\xe4X\xe4\xe0EAn\x9a\xfar\xbc\x89\x1dob\xc7\x9bX\x9a\x1a\x99j\xf6\xfd\xcb]\xc2\xa0\x1d\xe1\x9eG\xb8g\xc2,N\x86#.sY G\xb8\xe7\x11\xee\xe9\x95\xb1d\x11T\xe0\x8c\xeb\x87\xffS<\xc2=U;\xc2=\x8fpOh\x19\xda\xd9\x11\xeey\x84{\x92\xf4\xcbv\x82\xbf`\x81\xeb6\xdf\xb8\x17\xecR\xf3\xbb\x8enI\xb8i\xc7QIS\xef\xbd\xe8\x8d7D?p\xcb\xf5\xddoC\xe4\xe2wZ| \x17\xbc\xc7N\xbb\xc1\xa6\xde]}\x9b\x81\x8f\xfb\xb6\xda\xe4:\x8d\x04\x8b\xc1\xd9=:\x8e\x92\xef\x95\x19g\xca\xde\x8f\x93\xe9wG\x93\xce\xbck\xe3\xd1q4\xf3\x0ext\x1c\xa5\xdf\xef\x8e\x8e\xa3\xd8\x8d\xed\xe88\x82\xb6\xdcm,~\x13\xfb_\xe08\xb2\xf5\x87\x14O\x91|%Q]\x1d{\xc8\xf4\x0b\x01Hi\xbb5j\xc3\x0e\xe9\xae!K\xfe\x19\xe3\x1ek\xbaB}\xd2j\xd5\x93g\xac+Wgc#\x17\x89+Agz\xaa\xae\x04G\xa5#\xfe\xa3\xe0\xe8NXD\xed#\xbe\x15\"Gw\xc2\xd1\x9dpt'\x98->\xbf\xd3\xd5L\x84\xd8\xd1\x9d0M)\xf5\x7f\x8aGw\x82jGw\xc2\xd1\x9d\x00-CC;\xba\x13\x8e\xee\x04\x12\xb8\x82e\xf8\x0f\xf2.a\xc1\xec\x10\x067H.\x87\x9e\xca\x1a\xa7\xec/k+\x9d\x83\x16\x95\xef\xef|\xd8%\xdf\xfaR\x0c\xd3\xa3\x97\x02\xaf\xd8\xfeLZ\xedU\x9e\xf2\xb6\xd6S\xa1\xcb\xac\xe7\xecg1 \xa6\xd2\x89\xb7\xd9\xadO\xd8\xae\xca\xb6\x19\xd8\xf6\x17\xf47][\xf2b\xf4f\xb9D\x94C\xbbbOB\xa5\x9ex\x85\x9eXe\x9eXE\x9eh%\x9e`\x05\x9eh\xe5\x9dH\xc5\x9dx\xa5\x9dx\x85\x9dHe\x9d\xd4\x8a:~ :\xa7\x82\x0e^/g\xf1:9\xfb\xaa\x8f\xb3d]\x9c\x83\xd7\xc3\xd9k\x1d\x9c=\xd7\xbfy\x94\xba7O\xab\xde\xcd#\xd6\xb99L}\x1bib\xb2\x0f\xb6T/\xaa8\xb4\x94\xa5LM\xfd\xdf{^U\xbc\x18\xb6\x1dW\x1ddG\x7f\xd3\xde:\x83\xc29R\xb6\x88S\x93\xed\xce\xd3\xaaa_2g\x9c\x1f\x87\xae\xcbZ\xab\"\x1d:\xe1/w\x1b\xc3\xb6\xab\x9d\x87\xfaY\x189\x07\xc3g`\xe8\xfc\x0b\x9d}\xc1s\xcf{\xe6\x05\xcf\xbb\xc0Y\x17>\xe7\xc2g\\\xe0|K9\xdb\xf0sm\xd93m\xd1\xf3l\x1fg\xd9R\xe7\xd8A\xcf\xb0\xbd\x9d_{<\xbb\x0e~n=\x9d3\xeb\x91\xce\xabC\x9cU\xe2\xcc\x98QjM\x14V\xf3\x9d\x16p\x8b\xda\xcf\x8dp\xb0\xabIY\xab\xc8\xf7\x83\xba\xf2\x89\x0b\x9d \xb2D!) \xa8\xdf\x0f\xd3\xcbp\x19\x86\x0b\xa8\x1b\x85z#e'\xcf\x86\xb1\xa6\x14\xaf\xcd%\x0bu\xf11k\xb4\xd4\xe8\xcb\xf6\xfe\xbem\x8c\x1a\xd7`;\x98\xc0 \xbc\x08\x9f\xb6\xec\xd8\xaa\xba\xab\xf65[R\xb0\xa3=\x83\xd7\xbe\x13\xb7lc\xc0|\x0c\xa7\x8aY\xa8;W\xf4L.\xb7wb>mN\xc4f\x1a+\xc2\xe9F;\xf9\xf1o\x9b\xea\xcb\x96\xd6;R\xad\xd8\xd7\xb0f_\x83\xa0-\x1e\x11\xd4\xa4;\xd6*\xf1\xe7\xd9\xc8\xdc\x14\xf3\x7f='\xa5; $\x8e!\x10V\xa6$\xdb\x95\xd1A&\x84\xe0 9'\x9f\x1a\x0e\xe0/\xe7\xdb\xf7\xef\xb7t\x83i\xce\x8e\x83R\x7f\xc9\x9bl\xa2\x80g\xaf\x9d\n\x81T\xd1n<\x94@\xec\x9c\x10\xf2\x87QZ\x1d\x84\x98f\x9d\xd4hq\x10\xcb !gu\xdf\x12^\x83\x9e\x14l\x11\xde19\xaaU/\xefi\xdfWm\x13KT\xe6q=`\xa3\xa8Vr?\x0b\xda\xcaf:X\"\x96\x94\x1d\xb5D+yfV`\xe5'\xdc\xba\xaa\x07\xda\xd1\x15\xb9{\xd8tt]}c\xdal\xc7\xee\x0c\xf2\xc8\xda\xe8\n\x84\x8f\xb1\x8dy0$\x9e\x0bL_\xfaF\xcb-\xf8|\xf4\x9d\xa9\x97ct\xbfY\xb7 l\xc6\x07\x9b\x03\xdcYD\xd1!\x8e\xb2\x93\xdac\xaa\xd2C\x9c\xef\"\xa5\x07G\xb2,\xa9\x04\x91\x88\"D\x10e\x88Laz\x9ebd\x11\x13\x9f{\x9arDD1`\xfb\xe3N\x82\xcb%gRp\xd750I\x96\xf4~\xcf\xe6C\xf8\xc7\xe00\xe0\x1fb]5w\xe4\xba(\xef\xb8?\x9b]5\xd8\xa1\xcc\xad\x0e|\xaeL\xef;Ru6\xcaE\x00@\xe5\xfb\xa0r\xeb\xd0Bs\xbf\xab _\xbeMD\x02\x9f[\xb4X\xadC\x0b\xe5tp\x8b\x04\x8f\x7f\x08\xb3\xbb\xee\xda{\xbe?xi\xdcu\xc1#U\xbam\xc9m\x90\\\xb0\xde\x17]\x7f+\x8c\x90\xb2\xf5C1l\x1d0\x9c\x7f\x06\xce\x95\xb5\xa3Zk5\x8e\xf9\xb6\x97\xcb#\xbbW\xab\xd4v\xe4_\xec\xc4,\x1crp\xe3eS*\xabf\xa7\x8f\xdb\xf6\xe6A{A>\xbd}\xf3\xe1\xd3oW\xe7\xef?~\xbe\xbc\xba\xb8<\xbb\xfc|\xe1q\xee\x85\xde\xf8\xf8\xe9\xc3\xc7\x0f\x17\xc9\x8f\xc3o\xceg&\xac\x83y,\xc5\x00'\xd1!\xba\xb3\x12yA3b\xf1R\xc6\xdc$q\xbam\xb8\xda\xe2\x10\x83\x15g+\xe3\xfc)2\x99\xf8L\xc8\xbf\xda\xa64mg5\xa4\xe8\xae\xab\xa1+\xba\xdd\xf8\xadqW\xb7\x12\xba\xb0\x89R9\x82\xdfp~\xe07\x9c\x9b\n\xb6\xb9qrl:\xfaP\xb5\xdb\xbe\xde\xb9\x1f\xc0h\xc0\xd28\x13\x9f\xd7eW\x94w\xa0&\xc3\x19\xa1\x94 *%\x1a\xae\xc6\xc0\xeb\xfa;\xce\x11\xcc\x18+o+\xfa\x00f\"\xa8D\xcf\x18r \x8a*\xf5\xfb=\xa0\x16\x13\xc1\xff!\xc4/?\x91\xd8\x06\x10CS\xfb\x00\xfe}:j\x82R\xe1rHY\xd3J&\x88\xc3\x0b\xfe\xbc\x85E\xd0\xd4UWK\xaa$\x87\xc8g\xc5\xf7[\xd5\xdc\x90~[2B\xcf\xd7EUo;\xfa\x9cI\xd0\x0d`\xc2\xd2\xe7\xd0/\x1f/>\xff\x9e \x86\xdcg?\x9e]\\D\x1f\xba\xf8\xcf\xf3\x8f\xd1\x87\xde\x9d\x9d\xff\xee\x17\x96i\xfc\xa5\x88I\x0f\xa5@'\xe3g\xef\x10,\x1a\xb2mz:\xe0\x90\x02\xbcS6evo\xec7C\xbaPm\xc7p\xd4\x12,\xffz[\xc7;`\xd3mw\xc0~\xd3:P\xec2U\xb5\xafVR\xbd\xe5]\xddU\x9b\x0d]\x91\x15`j\xef\xab\x9eC\x99\x85li;\xb2\xa2u\xb1\xa3\xab\x91\xc38Klqm\x96\xd8o\xde1\x8b\xcf\xc7\xc7'\xfb\n\xe8\xea\xc4\x15\xa0\xa0B}\x00\x01@\x9b\xb2\xd8\xf4\xdbZ\xd1\x972o\xcdE4|\x99J>[\x9e`\xfc\x1b\x07\n\xcf\xfa\xef4k\x06\x07\xa7\xb5k\xc9\xbd\x80\xdcK\xfa-`\x94@\xadq/xr\x02\x04\xfa\xd3\x96\xc4\xf2\n\xc2\x87=\xd1 \xe0\xff*l\xearP\xa3\xb9@h\xd7\xc6\x83\xa6\xcd@_\x03\xccz\x00\x8bd\xcf\xe5\x99\xb2-\xb4k~2\xf2 -\x86\xa1(o\xe1\xc0+\x94\xa9\xa2\xed z\xd0\xdc\x02\x86\xc8\xe4\x9an\x8a\x0f\x1c\xf6\xc7\xf9\xea\xbcY\xb7\xc9\xa6#\xe4&3q\xf6\x15%\xb5}\x8a\xaf\xfc\xf1\xde\x9ap'\x88I\xbcw\x05\xc6\x96\xe5\x18\xb9\x92\xd6\x1baZ\x85\x7fm\xdaN\xff\x0c\xd5\xd3.?c\xf8\xd7\x82<\x8dD%_\xda/\xe9\xbc\x81\xfew\x95`\xf9\xcbaN\xa3*\xb9\x13\xb7\x15\xfeS\"{l\x81#La\xfd\xb3\xd7d\xaf\xd7\xb4\xbc\xfd\xf1\x07\xf1\x16y\xa0]\x1f\xeaW&\x13\xbf\xaa\xd4\xd6w:1\x85\x83\xfe\x02\xa9L\x9fMq\xddn\x87qe\x98\xc4P*0\x02dw\xb8\xc9\xb2\xbce\xe5AO\x05dK\xa2\xde/\x11\x06f}\x88D\x9f\x18\xfb[$\xcbq\x95\xf4Y\xca\x87Q\xe6\xb0\x0f\x93,\xc8`\xce7\xeag\xd4\xfc\x0cH\x88?\x84\x8d\xe4\xaf\xc1\xcf@\xc6\x04!\x0c\xe8\xc3\xe7\xff\x7fK\xbfI\x16\xac\xae\xb5 C\xfc%\xfa\xb94zI\x1f\x8a\x8e\xdd\"\xf9\x97G:zSt<\x06D\x19\xbd\xaa\xb8\x7f\xe4\x9c\xa9m\xc9']\x82\xa8\\\xd2P8\xeb`]\xca \xb8\x04\x9e j\xeb\xb3;E\xacz\xa1\x9es-y\xf6\xa5\x15Se\xe7\xd9\xed@\xa1\xd5\xc8\xa1\x16;tL.\xe6>\xdd\x18\x96l\x99K\xb2\xc9e[\xe3\xfc\xa7et\x10\xe9\x9d\x90\xa0\xed\xcdck{JV\xb6'e_\x9blY\x8b\x8b\xd5\x0b\xed+sv\xba\x89\xa6M[\xfb\xa4\xdd\x1d\xdd\xd9Y\xbb\x1a\xdf\xd1Q\x86\xd3\x88g\xef\xe4\xa7\xb2\x8b\x9f\xe2\x0e>\x93!^l\xc2\x00\x9b*m2\xcdx\xec\xa9\xfbtl\x07\x83\x8d$Y3H?%\xa7\x9bc-\xd3G\xc2\x196\xc3\xd8\xaa\x19W5\x82)f\xd6\xe4C-n\xb4\x8c\x98S#\x86\xd4\xa0 5\xc3x\x1a>\xd5\xe6\x18L\x89\x81P\n\x98J\xf7h$}R\xe6\xd1C\x1aF\x175\x89\x06e\xc9\xc5\x86\x96\xd3lj\xec60Q\xff\xc7\xcc#\x82\\\xcc\xbaf\x9a3q[\x9b \xb5\xb0\xc1\xcd\xa4\x9adu\x13\xafx\xd8\x93&O\xf8a\x11;\x9c!\x01\xa2\xdd\xc91 \x7f\x19\xc7\xa3\x11L\x1e\xd9\x92\xd6\xbb\xd0\x90\xb2\xecy^2\xd8 &\xda\xf8\xd4\xbbI\xa6\x0d\x9c\x0ds52\xac~\xe8\x8b~\xeb\x9fB:\xb1\xc7I\xb52\xe6\xc7\xb2\x07zX\xcd2\x05\xda\xec%\x9an\xd2l[\x8e\xc7\xd0\xee\xcd+W\x8ciP\x92\xc5\xa2\x86\x98\x12\x9d\xd9\xde\x83I1:\xaa\x14a\x14!\x11\x1cSHD\x91C\x8c/GhY\xb4,\x91\x9c>\xee\xa9\x86\xcd\xc8hR\xc5\xc2\xe4\xf5\xcaX\x9e\x1c\xbe\xe3\x86\xd2\xfcED\xcc\xa8\x8a\x0b[/I\xb7\xa9b_p\x82\x9d\xf5B\x7f-Y\x0b\xea\x17p\xef\n5O\x00\xcf1\xa8\xba\xd1\x8b\xed\xe7N8_E\x0f\xdc\xd8\xdb\xae\xc5\xdc\x8a\xfb\x9c\xc8+\x03~_\x91\xa2 \xe9\xb42\x9c\xc5O\x10\xff*\xc6\xc8\x8f|5q.\xfc\x10\xb1\xdb\xa6\x92.\xc6\x979\x1c\xba*\x8b\xba\xde\x81\xda=\xb4\n\x83\xcc\x11\xc4W6\xe4\xf9\x08\xa7\x8d\x9a\xd8\x1dZ\x08\xa7x\xc7`\xcc3\xf6\xab\x0c7\x02l\xf8\xf3q\xe9\x9e\x93\xa2Y\x91\xbe\xddv%\x1dU\x1c\x83\xdc3Z\xf1P\x1a\x80.\xa8p\x84\xef\xd8u\x11\xe8\x821\x8c\xed-\xe3E\x15\xd7d\x7fy\x06\xbf\xca\x0e$\xae\x9c\xfc;\x94\xe1'}1T=\xfb\xfc\x01C\xb1\xd9\xd4;\x1b\xe9\x86\xc8+>&\x01\xdd(\xc4\x03\xe2\x0dd\xc3\x87d\x84\xc5){t\xfc\xa6\xb85\x085\xc9H\xe3\x95\xfeY\xf0\x0f\xc1LE9~)*e\x10\x87\xdf\\q\x96\x82\xbc\\\x8apf\x91@\xcf29Y\xd2\xea\x99\x9cNa\xd53\x98\x10\xd6\xbf\xdf\xcf/.\xbf\x0b\xcf\x89k\x13\xfa\xed\xed\xbb\xf3\xf7\xe7\x97\xe7\x1f\xde\xe3\x89\x01|\xcf\xa1\x0e\x0e\xfb!\xcb\xb9\xe1\x7f\x84\xf3\xae\x9eS\x86\xa2\x14\xe6B\xa6\xa2\xc0\xfbA\xe2(\xbe\x8e#\xeb\xb8\x99\xf8T\x98\x8d\xc1\xcf\xa8\x7f0\xfeyr\xfbC\xac\xc4\xc5\xe8Q\x1bs\x81\xb1\x7f\xf1dh\xcf\x94,\xe4\x1fX\xd5\x93\xa6\x1dH\xdb\xbc0RVy\xf8\x90\x96b\xfcw\xcb\xb8\xf3`\x04\x909\x96bK\"\xa7t\xcc\x17\xd8\xd7;\xff#\xceB\xb1\xc3\x18(\xdb\xba\xa6\xa5T\x83\xf9\xa3c,:\xb9-\x1eL\x01\xcf\xe8\xf5\xec\x93\xd7\"\xdaD\xcc_u]\xd3+\x11\x8f>\xe9\xf4wN\x13\x17\xd1\x1a\xc9\\\x82<\x84\xe6\xf0\"\xc1,&\xc8\x13H\x1e/\x12\xceh\x82\x0b.\xd3hvw:\x92y\xc5\x83\xfd\x04\xcc\x82\xc45\xea\xd3o\x90d\xc3\xf1\xb8\xc0\xfc\xcd\xcf\x18cu@\xbcY<\xc4\x83pl\xae\xc0\xdc\xc9a\x84\xb6\x17\xcb\xcc\x94\xfd\xd7Iy\x13\x92\x9f 9p\xc4\xcb\x90\x97\x04\x13\xc1\x19IJ\xac\xbe3\xd3\xe3hn3\xcb\xc2\x8c\x12\xc0\x89\x10\xfb\xbb\xc0\x8c\xfcY\xe5Q\xfc\xf2\\\xb6\x80\x050R+e\xa6M[6\xccP\x1ba\xcbo\x9a$3\x0d\xe0\x08\xc1\xadk\xb6\xb4M\xe3\xb2a\x97d\xf5\x8e}Y6_BV\x15\x9a\x7fm\xa1\xe1\xd3\x07-8\x89\xe2\x914\x93\xbaz\xdcgZ\xd7\x1fH\xebr\xa6\xa9]6\xaf\xc9]\xb6\x04\x8e\x82\xdb\x1cZh\xcb\x89'r\xad\xf4^J\xa8\xf5^6\xcc\x8a/[\xc2P\xd3\xc6\x91g\xdd\xf7\x92\xf2\x01\xeb\xf5\x16fh\xaa\xd5\xdf\xb3U\xa4\xc9l\xb2\xf5_6\xd7\x0b [h\xf8\x9f\xf7&@I\xf2F\xc4\x1e5\xbd\x12\xb2ey'\xc6\x97B\xc8\x04\x12\x9b\x8c\xbc\xce,\x90{\xd4k\xe1g\xe1q\xbc\x17~~\x0e\xe4\xc5\x881\xf0\xa8\xde\x0c\xd90\xaf\x86lA\xef\x86\"0]\x81\xf3\x9e\xba\xbeO=\xd1\x0b\x82>\xec\xf1\x86\xa0\xcf\xa2^\x11\xf4I\xd4;\x82>\x89{I\xd0G]o \xfa\x18\xee5A\x1f\xc5\xbc'\xe8\x83\x1e/\n\xfa\xac\xc7\x9b\x82>\x8byUdK\xf7\xae\x8co\xc4\xe4\xe0\x82\xde\x16\xd9\x02\xa9P\xc8~\xbc/(\xe9E\xbd0h\x0f\xb3\xbd1(\xd5\xc3xe\xd0\xae\x97\xf7\xce\xa0\xdd\xec\xc3K\x83vt8o\x0d\xda\xfd\x13\xf0\xda\xa0|\x1d\xda{\x832\xb1W/\x8el\xe1\x0b\xe0\x0c'\x0fJ\xcf\xcc\x92\x0bm\x92\x03\xc8z\xd9a\x7f\xbaC\xc8\"\xa4\xdcC \x8e\xa1\xb1\x99y\x10l\x98'c@\xea\x99\xf8\xfdG\x94\x8f\xd4\x9d3VH\x86i:w\xfat&\x04y}\x04\xfc\xe2\x81\x0b\xd5\xeat\xb4\xa8X\xe4\x06O\xa4\xd0\x89\xb9:\xf9&S\x93\xcf\xcc\xbbr\xd0\x92\x14?\xe9\xcd\xbeIN\x84\x14B\x0d\x89ep;A\xa3\x19\xc8A\xc79!\xb6\xc1%\x12\x19e\x0c@O\x0e;\xe2\x19\xd1\x0e\xc4\x8dx\xc8\x9a 4dK\xb6\x03N\xc1\xd4\x90.\x9c\\h\xd0n\x98\x07\x89\x0d5o4\x13\x03>l2\xee(\x9c\x00\x0bW\xe8\x92$nQB$-b\x0c!\x87e\x92\x8a\x0c%\xe2\xe8\x08\xbb9l\xf6\xf1\xfbt\xd4\xfc\x1d\xb5\xf3\xc7'\x92L\x898\xf3\xd0\xe9q\x17\xb8\xfb\xa0\xb3z>\x99M\x1eq\x16&Jq\x8cT\xd2\x1c\xa4Ht\xf2\x98\xf3\xe1\x95\xf1I\x04\xb0\x14]\xf9\xf3\x84\x8b>\x922-SF=C\x10\xba\xc4R\xc67c\xd9\xa7\x8c/;B\x0e'\x13\x88\x94#A\xce\x90K\xc5?\xbbb\xb3\xa1\xdd\xa8J\x17\xa4\xaf\x9a\x9b\xda\x17G\xdf\xe8\xfaD\xdf\xdeSB\xbf\x0d]\xa1\xf9\xefE\xda\x92a\x8c\x96~ZEy\xfa \xa6\xc1\xbb\xee\xe1\xf5\xb6\x89\xa6\xe1\xd5\xc8B\x985\x87\x03\xdb\xeb\xb8\x1cv\x8d\xcc\xc7\xaf\x91e1l$\xaf\xc0Ix\x1d\xa7a\xda\xc8r\xb86\x92\x84m#\x8b\xe2\xdb\x08~\xd9\xf3\xe2\xdc\x88\x9a\xdfe\xabW\xcd\xc6\xbdY\xf4\x06\xad\x12\x95>\xb0\xbfvY\xae0&.\xa1N\x97x}yT\x9c8k`\xbe\x1d\x89\x1dA\x13\x8f\xab\xb4\x7fY\xefU\xe9\x16\xc0\xace\x88*\x9fmpQ\x8c\x9a\x85O\xc3\xb0i>\xb7&\xea\xd2\x0c\\\xd3B\x974\x1f(,\xa2\x87e\xe0\xcf\x86\x10\xf6,\xad\x9b\x050gA\xbcY\x84\x8b\xc8Mc\xd9po\x12\x00\x8b\xa5\xcd\x96G)\x1e\xb2\x01b\xfe\xf0o\xe2\x87\xbb\xf9\x99X\x0c\x14\xb6\x10 \x0c\x07\x83\xf9\xf8_\x02\x04\x96\x0f\x00\xf3|<\xd9\xa2kI\xc0W\x04\xec\xe5\x05z\xf9x\x9b\x03\xf0\"\x8e\xdf\xd7\x0b\xee\xf2\xce\x19\x86\xf4\xc8\x01t%\x83\xb9\x12\x81\\\xe9 \xael\x00WX]Z\x0c\xb8\x95\x0f\xdazJ\x80\xadG\x05k=Y\xa0\xd6\xe0\x01iE\x01Z\x13\xb4\x18\xf4\x84\xf3\x15XJD\xec\xa4\x02\xb1\xd2@Xi\x00\xacD\xf0U\x02\xf0*\x11t\x95\x04\xb8J\x05[\xa5\x02\xad\x92@Vy\x00\xab\x98\x91oQ`U\xb0\xbe\x14\xbai\xe6\x02\xaa\xf6\x0b\xa6Z\x1eH\xf5H \xaa\x03\x00\xa8\x0e\x02\x9ezD\xe0\xd4S\x04M=:`\xea\xb0`\xa9\x85\xc3\xde5\x92\xf9\xc8'L\x05\x9f\x8exrB\xe0IR\x18<\xb4\x85\x91NA\x94\x93\xff8\x99\x8bn\"X\xdeZ\x1f\xb2)\xc3\x80\x16B3\xe5\x9b\xcfr\x10^^\xaf\xf84cv\x02ji\xe6x\xf2AI\x01\xc8C\x14\xef\xb3(\xeb1\xb0\x9d\xdf\x8b\xe9\xfe\xc5\xa24u\xc4^\x98\xd1\x9e\x87:\x07V\x946\xb8\xc5\xb2\xc6.\x00!\xf2s\xecx\x9cs\xf1\x9a(\x01\x92\x06\x19\"\xce6\xca\x83\x0bym\xd0~\x0b\xb4\xcd\xae{\xc3\x0b\x1a=gb\xdb\xec\xde\xbd\xc2\xcf\xe31$\xa9p gU\x1e\x03\xbe\xe9a\"(+\x13\xc8D\xc7\x1a\x93\xa2\xe4\xd0\xe3\x9e!WI^\xe2j\xb7\xfb\xbd\xa0\x1a\xdd.&\x08%\x97Pl,\x13\x962w,\x93\xa0; \x0b\x9e\xe9\xc0\xcd\x87\xebp\x90\x8e \x18\x82\xea\xa4\xbbq\xfb\xb3\xba\xceM\x0cu\xcc\x99\x13\x1b\xc0\\\x8c@(g\x0e!\x9b\x82\x1b\x81\x0c?\xba\xc1\xcb\xf8\x80\xbat\xb5\xfcOE\xad\xffM\xf9\xaaz\x89\x16\x90,L\xba^\xdc\xd1]\xa2*\x94\xa6\xdd\xfd\x8f\xfd!\xdfQ0\xc2\x083tG\x87m\xd7\x80\x12\xf1\xb1\xb8\xa1\x12\x0fq\xd2\xd0o\xc3\x15{xh\xc95\xbdq.\xcf_\xb6\xb4\xdb\xc9\xb2\xdf\xeca6)\x94\xdc\xb7\xfd@\xe8z]\x95\x15m\x86zwB>4\xf5\x8e\xb4\x0d\xbf\x0f\xb7\xeb5X\xe0\x18\x1b\x16\xc1\xfe\xb6\xdd\xd6+\xee\xb3\xa3\x83.\xdd\xe0\xa5\xccY\xd9V\xcd\xf0\xf3+\xff\xbc8\x02N\xb0\xc6\xa7\xa6\xd9\xdes\xbb\x9b\xf8\x0d,\xf6*p\x9f\x0eQ\x9b\x93\xcb*M\x8a9\x9f\xf7\xe7s\x17_g\x0d\xa5mj\xb0m\xd1R\xe1k\xc6\xaf\x88}('&$\xcf\"'\xbf\"\xbb\x1b\x98%\xf3\xb2\xc8\xd4#\xf4\xda\xbb\xecb\x89\x8e\xec\x85\xaa\xc6]\\t\x14\xdf\xc9\x16%\xd6\x0fT\xd0#m\xb7\xa2\xdd\xc9\xdf\xeca^TMI_\x93\xb2\xed\xef\xdb\xfeE\xbf\xba#/O^\xfd\xa8\x1e\x12\x06AC~\x83\x80\x1e\x93\xb8q>\xe8\xfd5]\xad\x80\x8f\x9bO\x1f\xdf\xa8\x13P\xd8\xe1\xe0\\R\x12G#7\xae\xff y+n<\xc9z\x97\xa6\xf3\x90\x19 \xe5\x18\x99\xc9\xf09\xe0a\x81\xc4r\xd3\xf2r'W\xe5\xe8q\xd8^\x90V\x88\x1e\x89\x02\xf9\xa0\x05\xf5\xfdT\xf4\xd4\x1ch\x1f\xb4y\x103\x0fhj\x06\xe4\x0f\xa5g\xd6j\xd1~\xf7\xa4\xa6\xf3\xbb\xcf\xc5{\xbe\xec&\xc1\x15\x87\x16KQ\x17NR\x17\x9dX\xf1P:PP\xbc\x10KU\x97\xd7\xf1\x02\xd0AhA\x00!\xb4$\xbe\xa2aK$\xba)\xc53\xb9x\xc3\x00\xad`\xea\xbap\xf2\xba\xc4a\xa7\x8e(\x0f\xa1\x18 \x96\x92\xc4.\xce\xd6b\x98Eh\x0b!\x17\xa1\xf9\x93\xd9\xc5\xc6\xb5\x04\x96\x11Z>\xa2\x11Z\xf0+\x9f%\xb1g \x1dQzE\x0c\xef\x08-\x98\xde.\xc6\xf3\x1c\x04$J0\x96\xe4.:\xc7\xa1\xecW\xe98@\xdf\x1b\x81dw\xc9(\xc9\xf0\xc3x\xc2\xbb \x88I\xf9Z\xccj\xb9 z\x12Z>\x86\x12\xdaSBR\xfa8: \x9e2\xcc\xc2\xa3\xa3*\xa1\x0d\x81\x04x \x08KAd\x9e\xa2\x188\xbf\xfd\xc2 \x03\x7f\x89<\x1eI\x87\x97\x8a\xc5D\x9e\x0d\xa6\xc4K\xc6e\"\x0f\xfb\xd3\xe2%c4\x91\x87C\xa9\xf1\xd2\xf1\x9a\xc8\xd3\x91\xf4x\x89\xd8Mhy\x08N\xf9N\\nNEsz\x88q\x8cg\x08\xd3 mO\xc8N\x84\xf8\xe2\xf8N\xa4\x8fEP\x9e\x08\xdd\xc3a=\x91\xce\xf7\x83\xf8D:\xda\x17\xee\x13\xe9\xea\xb0\xe8O\x84\x81'\x82\x01E8{\x0c$(\xc2\xc6\xde\xf1\xa0\xd0b\x17\xcf\x89pQ\x0f5\x0cD\nm2\x94\xd4x\x1d\x19\xc6tX\xa9CjZ*\xbd\xc5!\xa6\xd0\x82@S\xa4_dj\xe6\x82N\x1d\x82CRR\xbd\xc9F\xe0\x10\x18\x15\xda<\x13pl\xbe\xc8\xa2 Uh\xde\xb4M \x80Uh\x87\x1e\xf3D\xb4V\x08l\x88v\x93\x02\xf1y\x84\xd1\xcf\xc4le b\xd1\xee\x83\xe9\xf7\x1ea:\xe6\xe0e1\x82\xe1 \xf0\xe5\xa3\x8a\x0c;w\\3\x80k\xf1\xf1$ k\xa1\xa5p=\x07e\x8b\x12\x9c\x94\x9a/\xea\xe4\x89\xb9x\xe2\xe8[hQ\x13At\x8b\x93\xc4\x89%\x0b\xa1r\xa1\xf5)\xd8\\h\xc9\x08]h\x8f;#\x93\xcf\x02\x8cX\xe2|\xa4\x9d\x0b\xe4\xf1\xe7\xc6{R$\x92\x98\x9a\xc2/\x0d\xe9\x0b-a\x8a\xa6\x8d\x7f\x96\x08u\xc9\xa5\x8dt\xe6f\x986\xd2I\xc8`\x8cP$\xad_\x98\xbf\xd9Xa\x83\x9a\xc4\x0dG\x93\xfbYL\xa1h\x13us\xfc\xca\x19Z\xa1\xdd+i\xf8\xd4\xd2\x05\x1e\xb3\xa0-5\xb0\xb9\x08g\x8b\x9c\x93\x05\x0d\x1a\x86t&{D;\x93\x90\xf6\xe3\xd7{\x1c\xe43\x89\xc9\xa8\x99\xc1'\xd3\x91\xd0\x08\xb1yph\x9c;\xac\x1f/J\x9ax\x90\xd2$u\x16\x11\xd4/I\x9a\xc7e\x90\xd3$\x82\x9e&K#\xa8\x89\x17EMP$5\xc1\xd1\xd4d\xef\xf3;\x1d]\x8d\x10\x13@\x06\x14aMf\xa1\xac\x11b\x16\xee\x9a\x84\xb0\xd7$(cID\xce\x92\xa4\x99\x9c\x81\xc5\xf6\x7f\x8a\x1e<6\x99\x8b\xc9F\xe8U\x0d\x8a\xcb&3\xb0\xd9X/\"\x81\xaa\x0f\x9fMP\x8c6\xf1\xe1\xb4\xc9\x01\x16v9\xdc6I\xc2n\x938~\x9b\x04l\xf0\xd3q\xdc\x16\xa1\x11\xd5m\xfd\x01\x83v\xe7\xc5<)w!\xbf\xc6\xea\x91\x02\xa3\x19\x85\xefk\xae\x91\xd8\xcaT\x86:)\x8fWL\xddZ.\xecI\x1d\xe2B\x8e\xaa\xf3\x9cl\x8a^8\xd5\xb4e9\x81\xbf[D\xf8!\x1f=\xe1\xcfG \xb2\x90\xa2H\xa9\x9d\x0e\xdcGMK\xee\xdb\x0e\xd1\xc7 \xf1\xc6\xa7\xe4E\xee\xf8\xf6\xa0\x12\x15\xbe\x13e\x94K\x0e\xff\xfaDi\x82\xc7\xde\x84_\x0b\xfe9>'\xd5\xd0k\x98\xa5\x06\x14\xcc\x15i\xd9<|\xad\x84\xf7\x0b\xff\xca#\xa8\x7f2'm\xef\xbc\xd8\x83wm\xf7F\\N\xb1*\xfb\xb0r\xcew\x10\xc9\xe9kX\xfa\xd0\xe5\xf6\x0bC\x9bPZ\xc6\xf5%\xb2\xad\x9b\xd7f\xed\x0f\xcbeY\x9f\x99a}\xc1\xec\xea\xc7\xc0\xdd\xd8\x00\xe6^kC\x81\xbb\xe9R\"\xf2}\x12$h\xe9oj\xc4|\n\xa3\xf2\xc3\xd7\xc5>\x84\xca1\xca\xe9\x18\xe5t\x8cr\xd2[\x82\xfd\xfa\x18\xe5\xe4\xb4\x04\xffNlS\x8ag\x8eQN\xc7(\xa7c\x94\x93\xdd\x8eQN\xc7(\xa7c\x94\xd31\xca)\x89\x85c\x94S\\\x14\x1c\xa3\x9c\xec8\x8b\x94\x07\x8fQN$Qn\x1e\xa3\x9c\x8eQNh\xe7\xc7(\xa7c\x94\xd31\xca\xe9\x18\xe5t\x8cr:F9\x91c\x94\xd3dd{,V\"\x1aN\xf4$F\xef\x878\xbb\x7fA\xe9\x1d\xa3\x9cB\x04\x8fQN\xaa\xa5p\x8d\x12#\xc7(\xa7\xbd\xc4\xadx\xa5}D\xeb9F9%\x11K\x9c\x8f\xb4s\x81<\xfe\xdcxO\x8aD\x12\xc7(\xa7\xdc\x91\xce\xdc\x0c\xd3Fz\x8cr\xf20\x85\x87<\xf1{\xebN\xc3\x19\x06c\x9f4r\xa8\xef\xcc\x980\xe7\xbe\x81\xff\xd5\xda&\xe8\xc6\xf0\x8f*@\xd3\xd8\x05\x12\x13\x8b\x9c\x0d9\xbc\x9a\x1f\xef2\xbc\xea\xdf\xa6\xa1\x118G\x14)z\xa3\xb8\xb4\xf8\x92iS\xb6+\xba\x12\x8c\xc8\x11<\xb5\xf8\xb4>\x88\x19\xf2\xca\x83\xb0\x14\xb0\x89\xa6\x81+\xc9B\x00K\x87\x03'\xfb\xfbb@K2\x1flI\x96\x05\\\x92c\xcc\xe1\x82\x03\x9b\x0b\xce\xb4\xc891\x87K\x004\x03\xd8nA\xef\xa0\x10Mq\xa0\xc2\xda8\x82\xc8\x87\xbd\xb4\xa5w\x92\xecr\xef\xf7^y\xe53}2\x12\xa7\x18\x8e\x11\xd8:!\xe4\x0f\xc3\xa3-\x90\x8f\xeclE#b\xfa\xb2\xdd\xd0\x13B\xce\x984\xe00\x00~:\x90w\xec\xf4\xb0\x1c\xc6=\xed\xfb,\xa1\xeb\xd5\xc3}\xa3\x93',\x95}qaG\xbf\x89s\xe4k1z\x0f\x04\x06\xc8\xc0\xda\xb8Fh\xaeS\xac\xabz\xa0\x1d]\x91\xbb\x07y\xb3\x19\xd8y\xd4v\xba\xd1~\xd3\xb5%\xed\xad\xab\xac\x8fQ\xf1\xb0\xd4\xc2\x8c\x8b\xb9\xe4\x10\xa6\xbe\xdeIEk\xc7M\xfb\xdfh\xb9u43b#\xb5\xdcs\x13\xb7\x91{o\xe8\xfe\xbb\xb9\x10\xec\xaeF\x1d\xd4\xa5\xc5D\x0c6,\xd0\xf2\xc8\xf0\xb1\xab=)f\xc9\"\x86\xe3\xfdR{\xc7!|b9\x10\xf4+nSK\xe9\x0d\xbd+\x00.\x0fQ[\xa9b\xe2\xd90b\xf68\xbaK\xc0@\x11j\xd6\xec\x95\xed\xfd}\xdb\xa0\x180\x10f\x0b\x0e\x04\x08\xc2\x01!q\xaa\x16\xe2D*\x04\x84m% \x0f\xe3\xafa\xce.!b\x04\xa2\xedT\x0dJ}\xb9U\xf3\xd0\xde\x19\xeb\x83\xe1\x92}@\x13\x14b2\xd1\xe50\xd1\xcej\x1c~\x1c\xbb-@Lp\x96\xf2\x8f\xbe\xae\x9a;r]\x94wZ\x0b\xd5\xe4`y\xfd\xa8V\x1fV7\xc2g\xd8\x1f\xe9\xff\xac}\xc8\\\x85\xbfE\xe9y\xb9\x1f\xe6\xe3By\x04\x8e\x02\xb1\xad\xf9\x1dk\xe8\xb6\xe5\xb0\x85\x10\xd4ms_t\xfdmQ;$\xfa\xa1\x18\xb68\xb6*8;\xe7\n\x90P\xad5\x80;\xff\xa4\xe4RJV\xd4\x8a\xb6\x1d\xf9\xd7\xb6\xc7M\x97\x85p$\x0b\x8b\xbe\x0f.\x1f\x99\x8f\x10\x86S \xdc\xce\xdf\x7f\xfc|yuqyv\xf9\xf9\"\n\xe6\xc1\xdf\x8a`9\xb1W\xbcxN\x051\xcag/\xc5\x86\x14\x1d6>[\x91\x97L\xa4\xa6@h\x9e\n\xc4&J\x10v\x87\xa6M[-<\xd1\xf8\xec PN{'6\xa4\xe8\xae\xab\xa1+\xba\xdd\xf8\xddrx\xb4:\x00`\xc3\xe5p%\xa1\x9c\xfe\xbf\xe1\x1cU\xbd\x8b\x9f\xdct\xf4\xa1j\xb7=\x0f\x9d\xb7>\x9a\x11gbq'\x01\x0c]Q\xde\xc1}E\x80\xda\xa5\x92G\xa5\xd4\xf4\xabh@B\x7f\xcfQ\x19\x18\x83\xe5mE\x1f\xe0\xa6 \x80\x03\xc6\x18NT\x00\x12\x0e\x7fp\xeeE\xfc\xff\x87\x10\xfd\xfc\xb4d\x1bF\x0c_\xed\x1b\xf8\xf7\xe9\xa8\x0dK\x05\x13%\x87,\x01\x99!~/\xf8{\x92?\xb9`J\x8d\xc7\x0d\x98\xc0\xb1\xe7\xf3\xe4\xfb\xb5jnH\xbf-\x19\xb1\xe7\xeb\xa2\xaa\xb7\x1d}\xce\xa3\x00 5A\xfe\x1c\x87\xe5\xf1\xc5\xe7\xdf\x13E\x9d\xfb\xfc\xc7\xb3\x0b\x1c%\xde\x0d\x9a\xb3\xa2\x07W\x8b3fd\xf2Y\x80\x11K\x9c\x8f\xb4s\x81<\xfe\xdcxO\x8aD\x12X\x86\x83)s\xb6\xa4F\x9e<\xfeY\"\xd4%\x976\xd2\x99\x9ba\xdaHg(\xf9 [F\x1fv\x88\xbf\xd9\x81x\x06\xb5`\x01\x15 \xd5\xabb\x9bfq\x14%\xd6\x9eZ\xa9\x8dcY\x86\xa5\x0667j\xd0\"\xe7\x94e\x80\x86E\x0f\x12\x9b\xb7\x05#\x08IH\x8d\xf1+0N4!\x89 \x9b\xe0\x99\x13\x97A\xd3\xa3\x0b\x11b\xf3B\x0cq\xee\xb0~\xbc\x91\x87\xc4\x13}HRg\x11\x89\xa4#I\xf3\xb8L4\"\x89D$\x92\xa5\xa3\x12\x8972\x91\xa0\xd1\x89\x04\x8fP${\x9f\xdf\xe9\x11\x8b\x081\x11\xb1\x87F-\x92Y\x91\x8b\x081+\x96\x91\x84\xe2\x19IP\xc6\x92\x88\x9c%I39#\xbe\xd1\xff)zb\x1c\xc9\xdc8G\x84^\xd5\xa0\xb1\x8edF\xbc#\xd6\x0bD@zc\x1e \x1a\xf7H\xf8\xe6Bb\x1f\xc9\x01\x16v\xb9XH\xd9_8\x1e\x92\xc4c\"Y\xf3\x81\x18\xa6\xc7FZ\x84\xc6HI\xeb\x0fX\xb8d^\x1e\x81M\xd7>T+\x81\xc01\xa2oG{H\xbb\x96\x1a\x89\xadLe\xa8\x93\xf2x\xc5\xd4-D\xa4z\x0f}c N*\x01u\x88\x0b9\xaa\xces\xb2)z\x81J\xd1\x96\xe5\x04\xfen\x11\xe1\x87|\xf4\x84?\x17B\x93\xaf)\x97\xa2\xb6\x81\x9f\xa9uPc\xbfi\xc9}\xdb!\xfa8!\xde\x98\xef\xc0\xa4 g\x8co\x0f*Q\xe1;QF\xb9\xe4\xf0\xafO\x94&x\xecM\xc81ltxN\xaa\xa1\x97\x017=\xd96\xa0`\xaeH\xcb\xe6\xe1k%\xaa3\xe1_\xb9\x1brKF`\xd6X!\x8c\x84\x02{\x05\xa9\xd4\xf0\xde\xdc\xec(\xa8\xef\x1e]*\xbf \xb3\x9c\xf6\xaa,\xa0\xbcm\x1aU\xf7\xec\xd7\xbe\xff\xf2\xc3\x0d}\xf9g\xf1\xe7\xb0\xfd\xf9\xa7\xe1\xdbO\xdf~\xaa\xeb\x87\x9f\xbe\x95\xbf\xfe9\xf4\xfez{?\xde\x97\x7f\xd2\x1f%5\xd7\xe7\x9d9\x02\xe5\xc1N\xaa\x91\xf8\xeb\xf7\xbf\xfe\xe3\x97\xeb\xe2\x87\x17?\xad\x7f\xfc\xe9\xc5\xab\x9f~-^\xfc\xf2s\xf1\x8f\x17kZ\x16\xdf_\xbf\xfc\xe9\xfb\x1f\xe8K\xa3>\xa2\xe1\xffv\xab\x0f\xf2?\x7f\xff\xe5O\xef<|\xf9V\xdf}\xa5\xb5\x1a.\x12\xff\x9b;`E\"m\xc8?\xfd\xf2\xf2\xc7\xf5/\xd7\xe5\x8b\x9f_\xfe\xfc\x8f\x17\xaf\xe8\xf5O/~\xfd\xe9\xfb\xf5\x8b\x1f\xbe\xff\xe1\xfb\x9f\xff\xf1}\xf9\x03-\xad!\x0b\x10\x9b\x7f\xd0\xf0\xc0\xf7_\xbey\x87\xfdk\xff\xa5.o\x7f\xec\xbf}m^\xbd\xfa\xd7O/\xff\xf5\xe7\xcd\xf0K\xd7\xdf>|\xd9\xad\xbb\x7f\x95\x9d\xce\xe0%\xaf`Z4\xa0<\xa8\x81\xb0S\xb40\x00\nE\xdd\xb7:\x1f\xe2\xa8P&\x16\xd3\xa7\x82N\xab\x90J\xba\xb7\x03\xa2#\x0d\xaf\xdf\xd0\x92\xbam\xef\xb4\x83O\xde\x989;v\x1f\xcb\xa5\xf1\x11`\x94\xf8u\x1c\xfe\xe3\x8fkd\x84\xd5H\\\x07{\xaa,=c\"\x114=\x91XP'K\xc0\x82\x03\x13=\xa4\x8dM>=ch\xc7\xd4Q\xb1\x01\xcc5\x02\x85RG\x19\xbd\x9a\xc7\x1bIN\x8f\x91\x95\x1b#;1\x86\xf5%\x1b\x1c\xab\xcfP\xb7\x8e\xc2\x8f\xfc\x8av\xdb\xd6\xab\xde\xda\x87\xcf\xb8\xd2\xcf\xc7DW\xdfM\xd2H-\x96\xbco\xfb)\x90 \x8a,\xc9\\\x80\x1a\xb5B\xd8\xf5\xcfP\xd2\xf3\xfc7\xa2\x12\x0cp\x16N\x089\xbf\xdf\xd4\xf4\x9e6CO\xfa\xd5\xdd\xc9\x99\x00\x9eT\xcd@\xbbuQbw F\x83\xe7\x95\x00U\x15\xde\xa0p\x8fb\xd3[upSy\xc3\xaf?\xf6\xd0\xa3\x00\xf6\xfdL\x81\x92\xc5X) e\xca\xe7O\x19\xf5b\x91\x9b\x15k<\xd8\x8a\xca\x99+\xea\xba\xfd*\xa2\x95$\x80\xdf\x16\xfc\xac\xb5_\x1b\xda!\xeeY\x182\x1e\xa9\xe6\x8dS\xf3\xee;h1w\xf0b\x89-L89)J0l<\x93q\x16x\xa8t\xd7\xd6\xfe$\x12\xd1\x18\xb2\x82\xbf?\xeef\xd9\xa9\x96\xf8\xa5\x14\xa9S\"\xee!\x11\x81A\x03\xf9.\x92&#\x1c\x19\xf6\x82|<\xfbt\xf9\xdfW\x97\xff\xfd\xf1mRL\x93\xf1\xc2\x87O\xe7\xff~\xfe\xfe\xec\xf2\xc3\xa7\xb4\xe7/\xde~\xfa\xe3\xfc\xcd\xdb\xc4\xa7\xcf\xdf\xff\xf1\xf6\"\x99\xf6\x9b\xcf\x17\x97\x1f~;?{\x9f\xf6\xf8\x87\x7f\xbeO\xe5\xe3\xec\xdd\xbb\xf3\xdf\xcf\xcf.\xdf\xa6=\xfe\xe1\xbf\xde\x9f\xff\xdbg\x7f\x9c\x9c\xf1\xf0\xc7O\x1f\xfex\xfb\xfe\xec\xfd\x9bD\xe2o>\xbc\xbf\xfc\xf4\xe1\xf7\xdfSy\xff\xe3\xec\xf7\xf3\xdf\x02\x0b\xa4\xc2\xea\xb2\xb6A\xdc\xb8\x05\xcd\xb7\xbb|\xddq=\xc2\x1f\x10F\xbb\xae\xedR\x02\xe8<\x9b\xf45\xfe3\xf4J\x8a\x1e\xcc\xee\x157\x1b\x85\xa2Q\xd1=\xfd\x1a\xfbq\xb4H\xad\xe8\xf5@z\xda=T%\xd3C\xd7\xdb\xa6\x1c,55\xd4\x8b\xfc\x16^c?\x82\xfa\xc5\xdd\xa5UI\xaa\xe6\x81\xf6\xe9\xfc\xab\xef\xe65\xfa\xab\x98\x1c\xda\x0c\xd5\xb0\x83CI\x8d\xa9\xdc\xf6C\xbb\xaa\x8aF\x0cL\xa4B\xe3\x13\x99:0\xfe\x1d\xbev~\xb1\xe3\xcd7E7\xec\x04/\xfc\xb0\x92R\x9d\x9dB\x89]\xa9\xef\xf85\xfa+\xcc\"t\xc4\xa3{\xd9\x9eX\xaf\xab\xba*\x06J\x8a\x9b\x8e\xf2C5\xb13!\x05^#\xbfAG\xfc4/j\xd0a\xdb\xb5\xe8X\x9d\xfa][\xc3`\xef\x9b\xeaz\xdb\x93\xeb\xa2\xb9\x93\xa7I\"\x0b\xa3ly\x8d\xff,\xcd\xea\x96\x1fAMwG7\x1d\xed\xb9\"\xc1\x91\x1c*z\x1f\xae\xfcV\xe6\xa2\xa2\xcc\xf8(G9\xf6\x1a\xff\xd9\xdcw_o+(m%\xe6e\xcc?$\xbeZ\x95\x0c\xa6\xa2\x846\xed\xe0I\x9e\xe42\xa2\x04\xe4k\xf4W\x8c\x0d\x9e\x13\x82oM\x88\x95\x84\xfd\x1eN1\"\xfd\xd2a\xad\xc6\xe7L\x80\x16s9@\x13:\x8a\xba\x83\xa9\xe5\xfc{O\xfa\xea\xa6)x\x8a\x96jt\x95#t\xc2z\xce\x19\xf9\xa8}\x8dR\xcbb\x9f\xcc)O\xef\x04\xb3\xc2\xf5 ;`\xbe@ z\x11t\xf1s\xe6\x92\xdf\xa3\xd8\xf8*\xda\x8f\x1b\x96\x0c\xed\x86\xd4\xf4\x81\xd6B\xb55\x8d4\x98p\x02\x0d\xed\xc4&\xc9\x93\xfa\xb1y#E\xb3\x93\x97\xb4\x1e\xbe\xd3\xfbvU\xadQW\xd3 \xd3\x1e \xff\x80\xbey;\xa9\x1a\xb2mx\x08u5`NI\xc9M]\xb1\x1b\xa2\xac\xa8'M\x1d\xb6K\x8b\xf5|U\x94x\xaa\xbc\x19*\xbdG\xcf\x8c\xaf\xd2\x99\x1a\xac\xbc\x84\xb0\x81\xf0\x91\x17\xdb\xe1\xb6\xed\xaa?A\nu\xb4\xa4\xd5\x03\x1ds\"!\xc4\xf8\xbc\xa2\xa5Ja\x86\xadW\xb8\x0d\xff\x8a\xef\x82+\xef\xd5b\xee\xf8\xc6\xcf\xc0\x14\xe0l\x95\xc0\x89\x90\x98:Bn\x92\x8b\xa1hVE\xb7\xd2\x85\xae\x90\xfe=\xc7q\xdd\x17\xdd\x1d\xed\xd4o\xa8'\xb7\xa3\xa4\xdfn6m\xa7\x17\x9a\x05~N\x84\xe9\xb4\x18\x86\xae\xba\xde\x0e\x94\xdc\x17;iDEh\x95\xb7EsCW\xe4\x1a\\IB\x1e\x8e\xd1\xe1mS\xb2\xb3\xd8\xebc\xe5\x97\xef+.\x87\xae\xba\xb6\xae\xb7\x1b\xdf\"\xec\xcf\xe1\xfaO!\x12\x8b\xbaV\x1f\x94q)\xe6\x13[\x0d\xbd\xfa\xb0dJO\xec\x8b\x14\x82\xc6 \xf0\xf7^\x8a\x9buE\xeb\x15\xea\xf4\x86i\xaf\xfb\x96\xd0\xa6\xb8\xae\xe1~\xc7q\x1fB\x1e\xff?\xee\x06\x06\x86\x04\xb5\xa2\xc1.\x80\xd2\xd6.\xc6bNzx:.8u \x9ab\x97\xd6v\xd0\x12\xf9p-\x8e\x94m]\xd3R:M\xa5\xad\x8a\xb1dss-\xc0+\x1d8\x06\x11\x86TDSnu\x16\xe3\xc5\xac\x00%\xe4v\x8d\x08\x88'b$sM\x88\x93\x03+\xd5\x8c\xed\xb9b\x8a\xd5\x0f\x99\x14@\xa9\x88\xc0\xffL\xa8\x8f1\x81\xe1\xb1/\x12\x8b\x86\x8c0\x8e\xa3\xab\xd3O\x93\x91\nI\x80KG\x98\x990y\x083qD36\x91\xee\xb7\xee\x0fpI\xf9\xe0\x8d\x88\x96\x9c\xaf\xde\xa2\xe6\xb1\xae.$\x03\xbc\x01+\x0b\xedX\xf4\x183\xba\x8e\x88\x88>\x12\x82\x18\x14!\xb1\x18\x94\x83\x0d2E\xc2\xc4h\x84\x87\xa8\xfdk\xc2W\xb4\xe8X-VL \xe5\x84\x86 \xe4\x8c\x8f4>\xfa\xb9\",6\xa4T\xe1\x16\x9b\x98\xc0\x10&,Y\xd6\x10\xe2\"1\xc6\xbc\xbd\xaa\x16\xa4\xc3q\x9e/\x9bb_\xf4bOQ\xc0O\x13\xf2\xd1\xec/\x0d}\xdc\x057\x8f~\xca\xb2\x13\x91\x07^K\x19e\xc4M\xba\xb5\xde\xa5 \x0d%\xa6'\x8d\x12\x13godu\xbb\xf7\x0f\xf7qREF\x1cq\xd1\xb5\x10\x0f\xcdq\xc6\x85\xddq1C\x15\xb4\xe5\\r\x89N\xb9\xc4\x89\x89\xa7l\xcc\xf2\xc98\xaf$8\xe7r\xdds\xb9\x0e\xbal\x17]\x96\x93.\xdbM\x97\xe9\xa8\xcbw\xd5\xe5;\xeb2\xdduS\x1dvi\x92\x0f\xda\xc2N\xbb\x0c\xb7\xdd\xde\x1dw\x87r\xdd\xed\xd3y\xf7\xe8\xee\xbb\x83:\xf0\x0e\xec\xc2{\x12N\xbc\xa7\xed\xc6{B\x8e\xbc\xc7q\xe5\xc5\x9dy1S\xb6l1\x93\xb6lK\xb8\xf4\xe2\xba\xd2t\xb7\x9e\x87\xa07-)\x19\xb9Qvxq\xcb\xa5\\ \x83\xc8t\xfe\x07\xfe-\xb1A\xcazJ^\x9a\x13\xb3\xe2\xc4k\x7f\xf1\x08B9b\xdd\xdd$\xec\xefc\xb6a\xa1G>\xc7\xe7\x7f\xb7\xa9\xca\xa2\xaew\x81\xba@\x82\xc0\x84a,p\xf1\x91Z0\x80y+Z\x83\xd3\x88\x1dG\xec\xdc+k\x1e\xba\xaa>\xd8U1\xe0\xee\\\xc7\xe9\xeb\xbf\xf9\x14\xdb\x15\x16\x9b\x9a\xc6\xef\x1b~\xb7Z\x91\xeb\xdds\xb2\xdd\xac\xd4\xff\x0f\xd5=\xed\x87\xe2~\xd3?W6\x06\x08\x15z\xee\xf1\xa4\x10\xd2\xd1Zd9^\xb7.\x9b\xd1\xebT\xec2\x05\xd7\xc0\xd5\x15c\xd2'5\x12.\x0dr\x91\x19\x99\x17l\x98>Rc%-\xf6\xe8){\x14B#\xf9\xc6\xa5\xcd\xd0\xed\xf8\xd5U\xf0\x15\xe4\xf9\x1a q\x87\x96\xc01^cL\xbf\x01\xc2a)\x962\x96\xe6_,\xf3\x93\x9b\xc7\xba\xe8\x07\xc9\\\x90\xf1CL&\x87%T \xb3)>\x8e0GU3\xd0\x1b\xeaSH\xe5L\xe2q\xe2\xd0b\x07\x0f\xe1*\x928\xba\xac/\x16F\xc4SZ\x95\x1dW\xde\xa4T\xf1\x84oC\x83\xe9F\xff,\"S\xe7/C\xe2xD\x7fh\xd18\xbe\xed\xab\xb69\x0d\xf0\xcb\x1a}\xc0\xb5\xd6\x08N\x87 \xd7wL\x8e\xf7\xa4,6\xa0$\xa0\x1e\x14\xbe}\xd5\x1e\xc2-Y\xf7\xc5\x1d\x15[K\"\xd0\x8bf%\xbf\x06\xba#_iG\xc9}\xb1\xb2\x87\x11\x13\xe5\x17\xe2\x18U\xdeh\xe4D%\xc5MQ5\xbc8\x93<\x7f\x1c:\xa6u\x8e=\xcd\xe3=\\\x1f\xfc\xa5f\xf1\xe1A\xaf\xb7\xc5\x03\xd5(\x8b\xd9iVR\x1d\x11J\x8aC\xa8j\x1e\xda\xfa\x81\xae\xb0X\xeb\xf7\x1f\xd8%\xe5\x9fbv\xac\xc8Y\x19\xa4\xa2*\xe2\x81?\x1eMC?z\xe5\x0b9U\xcf\xf9\xa2\xc9y\xe3\xdf\xf8}\xfb`\xd71\x18\xed\xb4\x93\xf2\x83Z\xafg9\xe1P+\x9aG xrVh\xdb+:\x86Lz}mf\xd0\xa3\xab?hsy\x90T\x9eN\x7f\x11\xe7\xbcz<\xc8z\xc8\xcb\xbc(\xfb\x1ey\x86\xb2\x12\xf5\xe0GH\x85G,\xfe\xf7i\x8cYcf\xbc/i\xbf-4\xee%\xd2l\xea\xb4R\x9c\x7fI\x8cM\\\x85\x9ci\x9e\xe4\xe9\xf3\xae\x122\x14\x89\x9c\x98\x93\xf6\xd3 B\x96JN\x1fZ\x81\xd9\xf7\x1f\xc9\xf2\x0c\xad+\xf21\x91eAT\xd0\xd4L\x1f<\x0d\xa7\xd5sD~\x8b\x87#\x83\x08\xcbo\xb2\x9f\x81\x04\xb4d\x84\xb1i\xd2\\'\x16\x9a\x83%sa\x8e\x14\xd3\xe4[\x02{3W&o\xda'J:\x82\xaf\x9b9,'\xb3\xe5\x14\xfd\x13%\x92\xae\x85:\xf4\x82\xce\xfc\x85\x94R\x9b\xe7 \xa7\xd5\x02:\x83\xcd\x85W\x02\x06\xcc\xb3\xc9Y\x8b\x9de:\x88\x82\x9b8\xf2\x14\xe9\x99D(a\xdceR2\xdaG\x98\x03\x9711\x1f \xafc\x88\xd8\xdc\xb9YB\x9fL\x1ak\xaa0N \x15\x1f\xd5\xc4\x05\xce\x1f\xd5DA\x9d\xb0\x0d\xf4!\x86\xf8\x12\x86\x06\x7fzau\xff] \xbf\xb0\xd1\xb7 \x0c\x99_\x9a\x9d\x96\xf0K%\xd6\x90O\xa8\xdc\x1a\xc4I\xad\xa1\x11t\x93l\x88\xe7\xc6\xa5\x84\x05\\\x16\x80\x07\x9d\xd8\xdbe\xe2!\xb37?\xd7\xe9\xb8`#Z\nX?!\xe4\x0f\x1e\xc6$\x02b\x04H\x0e%\x85\x84\xbc\x9d\xd5}K\xa0\x846\xaf\xcd\xfe\x8e\x1d\xc0\xa8\x03l\x9ff\x9dHU\xe8\x95m\xce\x916B\x1b\xe8\xa7\x83\xfaPR\"\x13\xd43\x9e\x8b\xa4\x1an\xc9\xba\xaa\x07\xda\xd1\x15\xb9{\x90'\xd2@\xbbbh;\xd7\xbb- f\xe8\xf0\x83\x03\x90%;\xc5\xf7ihH\x92sX\xb6z'\xbf\xcd\x9d\x0f\xb9\xa3UP\xe50\x81v\xbd\x16\x8ey'5w\xb0\x10\xf7\xbck\xe3b\xa9@\x06\xd3e\xe2\x80\x1e\xf8\xfc\xa8=/f\x12%\x19\xaa\x9f\x9d\xc7\xcf\xad(\x91]4c\xb8\xa3ZD/\xbc\xd0\xf7\xf5\x93\xac\xfeC\xbe\x0b\xf0yc\xee\n\xc9\xdc3\xd6\x11\xaf\xfa\xf2<\xe0\xcb\x96\xad\xed\xec\xd9.\xdb\xfb\xfb\xb6\xe1\xfd\xe0\xe8\x0eH\xdc\xb4\xd7aB\x17\xe0\xeb\x86,n\x9d]\xc9^y\xba\xd9&\xf5\x17qb\xed\x19\x90\xfbn\xb4\xe8\xf3y9UCV2\xa4j\x1e\xda;dm\xa1@\xb2_\xe8=\x0e\x048\xb4\xdb\x92\x16\"M\xe3\x81\xf6\x9e-\x83H\xb9 i\xc3 \x9b^\xd5\xdc\x91\xeb\xa2\xbc\xe3)roE\xd8b\x0cJ\xc8\x16\x0f\xbfl\xab\xc4\x95\xfe\xdbv\xe2\xd0\x12l!)[\x91\xb8\xe2I\x9c #\x1a\xea]\xdb\x91\xb7\xfdP\\\xd7U\x7f\x1b@\x1e\xab\xe4\xa3>\xd8\xd4\x81\xea\xff\xfb\x05\\EK\x0e\x95\xd3\x8f'6\xbc\x00\xb1\x8f]\xbbi\x99\x84\x8e\x8cM\x89\xa5e\x06\xc8\x13\xedmd\xdfkn4\x18\xbam\xc9\x1d\xa9\xfc0\xbd/\xba\xfe\xd6\x03\xb7\"\xa4\x1f\x8aa\x1b\x82\xcf'\xcc\xe3\xb9BQVk8\x9c\xb8\xa0\xe0\xa2Dn\x13\xc9\\\x04\xb5F@\x16\xffk\xcb\xdd\xa8\x80\xe6\x12\xd5\xba8\xcdys\x16G\xb3\x7fz\xfb\xe6\xc3\xa7\xdf\xae\xce\xdf\x7f\xfc\x9cYE\x1f\x7f\xf7\xe3\xa7\x0f\x1f?\\Lx\x11~\x0b\x08,\x81\xb0\x9e\xcap\xba\xc4\x8bNJhF#\xafj\x00\xdc :\xbbj8\x08\xf2t\xdb\x80\x82\x0e\xfb\x8b\xady\xe0\xa5\xc8\x92\xe0s'\xffjC\x83\xb5\x1d\xdd\x90\xa2\xbb\xae\x86\xae\xe8v\xa3\xa4\xe0 \x1f\xd4\x01\n[6\x9f7\xf8\x0d\xe7\x0c~\xc3\xf9\xaa\xe0C34\x83MG\x1f\xaav\xdb\xf3|\xf5\xd6'8\xc2kQ\x1e\xc5G\x7f\xd9\x15\xe5\x1d\\\xea\xe1\xe4W*:\x95R>\xa6Z\x87o3\x1aQG\x7fcc(o+\xfa\x00\x89EE)\xf9\xb6\x89\xddh\x9c?\xc1\x9bONk9\xd0\x19\xf7\x1f\xe2|\x1bd\xb2\x171\x91j\xab\xc2\xbfO\xc7\xcb\x12\x9e\xb2Q\xb6\x18\\k\x99\x13\xe5\x82\xd3\x90|\xcb-1\xde\xf7\x12\x13\x95\xc8&\xc6,>\x9d\xaa\xb9!\xfd\x96g\x9ey\xbe.\xaaz\xdb\xd1\xe7\xec\xd0\xd9@i\x82yk\x92r\xc4\\|\xfe=KV\xbbo}<\xbb\x08\x87\x04\x99\x8f_\xfc\xe7\xf9\xc7\x8c\xc7\xdf\x9d\x9d\xff\x9er\xf2\xe4\x8e#\xef\xcc\xf1P\x9f\xd211\xce\x19\xb2mz\xcaoz)\xb1>\xee\xc4\xdb\x1c\xb0\xdf\x0c\xb1L\xb5\x9d\xcaS\xf5\xc3f[o}J\x18\xda\x15[4\xbb+\xf6\x9b\xd6\x95\x1a\x02\xbb\xc3\xf5\xd5J\xdc\x00\xa1\xd3\xbb\x8a\x1b\x1fWPR\xe6\xbe\xeay%\x1f!s\xdb\x8e\xach]\xec\xe8j\xe45\x879\xb6El\xe6\xd8o\xdey\x18\xed\xf2(\xc7\xec;\xb4\xb1a\xb2\xc5\x04\x06(\xdc\x1f\xe0\x1b\xa7MYl\xfam\xadX\x90\x07\xc7\x9a\x1f\x89 H\xe4y\xe8!\x18\xd0Q\xc3\xbc@_\xcf\xfa\xef\xc8XJ\x81\x97|h\xd7r*D!+\xc9I\xeb+\x95B\x84\xf2+\x1f\x1c\xb3\x18\xc9d>\x1e!\xfc4\xe2\x93m.\xe4\x84\x8c\x06Lq{\xec\x9dpe\x94\x9e\xc7\xb2)-\xbf\xd6;\xfeU:Sv\xcfv\xcd\xb5!\xbeT\xc50\x14\xe5-PW\x81\x00\xec\x1bA\xf1\xbb\xe6\xce\x15\xfb\x8b\xdf\xc3\xbc\xc1$\xc6\x94 \x0e\xc4\x8c\xb4\x8d\xb8t\x8a\x9f\x05\x14\xd7\x1c\x922\x06L\xf2R\x9bo\xa7\xbb\xa7}\xae\x1b\xcf\xa4O\xf3\x14\x04\xed\x1c\x0bnT\xd5\x8f\xd7\xd3,\x16\xc0\xebE\x1e\xa7\xf1 \xeec\xbb\xbb0\xbcF=\x1d\xe2\xfb\xb1\xc1\x91\x18'\xd3\xd04I\xc3}*\x95\xc8\x97,B\x1e\x18\xf1\x12>l\x8dT\x8a\x97:\xc4\xd5\x13\x86\x11\x06\xb8\x8e\xde\xedb7\xbb#\x8ap\"\xf7Ib.\x05\xa2\x16\x15td?\x03\x89\xc2\xd9\x16\x90{\xe4\x88\"\xcc\x9e\xf6\x89\xe0\x14\x92\x82\"\x14\x92d\x0e\x86\x10!\x91\xa5\xa29\x04}\xda\xf5\xb2\n\xdb\xa3\xa2\x07M\x1eb\x1a]\x1fI0\xe7M1\xe7,\xceA\x14\xbf\xa41\xa7H\xcb\x042\xd1\x11/\x02([~\xf4~\xf8\x99\xfb\x17\x94\x1e\xae>\xa7\xce\xcaS\xd1,\x11~\xe6\xa9\x97I\x13\xb0\x84\xa2\x990\xae\xd4\x13'J(\x0e\x94\x9c\xa9\xa5\xa2\xc4\xfc\xdaj\xa9\xc3\xbbQ\x82\x86&ki\xad\xde!\xcdV]\xeda\xcc8\xa1\x17\xd4\x9dl\xae\xa6!\xc5I:Z\x9c\xe4!\xc6\xc9\xa3\xcf\xc8\xe4\xb3\x00#\x968\x1fi\xe7\x02y\xfc\xb9\xf1\x9e\x14\x89$\xa6\xa1\xcb]:Kj\xe4\xc9\xe3\x9f%B]ri#\x9d\xb9\x19\xa6\x8dt\x86\x92\x9f\xb0eRQ\xe8\xe0\x8a\xf0\x83\xd0\x85B\xba\x04\x06\x1dH\x05P\xe7\xa2/\xab\x00\xb6Sm\xd3\"\xaa\x95\xca,\xdb\xcdn\xd4\x8fD1K\x1bX\x0b0v\xab\xc66z\x0e\xe1\xa7\x8f\xd7v\xe5\xdd)\xe1\xfda\x19\xb0R\xeb=\x8f\xaf.Q\xf3\x99x\xed@\x13G\xa5,\x14]\xd5n{\x8e\\\xa4:X\x07:\x92\x10v\xfff\xbf\x18\x0f7G\xf5\xf5\x14f7T \xf4\xe4E/\xc5\x18\xb2eF}r\"\xaa\x91k\xd4\xb2\xea\x92\xf7^\xb0\xd1\xbc!\x0dxq\x98\xa4z\xe3v\xce\xcf\xe4:\xe3v}q\x18\xc2\xb2A\xb6\x9e`\xc1\xc0\xb5XLO\x91U\x9a\x00+H \xe5V\x1a \xc1z\x02\x01\xceq(lr\xc5\x80\xd4:\x01i\xd5\x01\xd2j\x02$V\x02H\xc8\xff\x9f\x98\xf5?)\xd7\x7fj\x86\xff\xd4\xbc\xfeI\xd9\xfc\xf3r\xf8\xc7\x8c6\xd3\xf2\xf5CV~\x87X\x00\xb9\xbb\xa7\xdc\xfc\xfb\xcd\xc8\xbf|\x1e\xfeG\xca\xbe\x7f\x80\x9c\xfb\x07\xc9\xb4\xff\x88\xf9\xf5\x9fbV\xfdG\xcf\xa5\x7f\xc8\x0c\xfa\xbe\xbc\xf9\xfe\x1b_\xf8\xce\xa7\x0e\xdeI\x99\xf1q$\xf3\xf4,\xf8Vqk\xc7e\xe6\x97\xe3\x13\x8bX[%\xab5\x82\xd3\x8aW\x0fs\x8aV#Ir\xc3\xc5\xaa\xd1\"\xd5\x19z\xa1\xa5\x17\xf9gwz\xf1i[\xdd\x8d\x17\x9d\x0e\x16\x9b\xce\xe5{ZQi\xb9d\x1a\xa59\xc5\xa4'\x17\x91\xd6JF[{,\xb1xt\xb8h4.3|\xd2\xc2?\xcb\xd3\x8bBk%\xa0\x8d\x01\xa6\x14\x83\x9eX\x04Z\xf2\xe0~\xea\x88\x19J\x0dsn\xb1\xe7\xb1\xbc\xb3 h\x17y\x0e]\xdd\xcfW\xe7\xcat\xb9\xe7\x0b\xfc\xcaEV\xe6\x02w\x15(\xd1\xc6$\xcc\xe4\"\x05F\xe0\xa0;\x11\x84\xe4r\x0c\xb9\xf8M\xed\x97$\xc6L\xbfb\\\xb8\x8do\xa5\xb8\xcd=\x9d&L\x02\xd2i\xdc\x83\xed\x87\xa3\x1a\xe4\xb4-=\xdaJ\xa4\x8d\x8b[\xc1;zSt+FX\xf8\x8eH\x15\xb2\xe5r\x82\x9ft3\xf8r\x1f\x89_\xe8\xa9\x8f#\xc9\xb57\xd3\xdf\xe9\xb8\xf1\xf8\x9f3\xbc\x9bxb\xea\xf8\x90\xff\xc7\x18\xb2\x95\x91\xda\xeb\x83\xb4G\xbf\xa0\xc3\x11u\xc8\xc7\xc7a,\xddD'\xfcr\x0ex\xbf\x1fn\xa9#y\x9e\xdf\xcd\x85\xa4\x88\xcf\x10\xf7\xb3\x8d\x9e\xa1\x12\xcb\x03\xb8\xdchtPIt0\xea\xfc\xcd\x1a\x8b\xd7_\xb8\xd4Xf\xfb\x07\xad\xd1\xf8\x17\xc3\xf2\x0b.5\x80\xb9~@\xee\xf93\x07\x80\xfb\x00u\xb9>:\x00\x05\xd0\xc7\xf5\xff\x81\x8a\x96\xe4\xfd\x13\x94a\xe2\xf2\x8e\x8c\xd7\xf8\x0c\x89=\x05L\xaa\xc4\x9a\xfcG}|hOxoH\x8f\xde\xb7\xfd\x14\x88\x17\xeaC\xc2\xd6\xf1\x08\xea1\x14h>\xc3\xe1\x83P\x13.\xa0<\xc7\x8fl~\x07\x90l\xfb\x99\x829\x0e\"\x84\x1c\xb7($;\x8ad\xb3\x1dF\xb2a\x06\x02\xd9\xbcIe\xbc\xfb\x0eZ\x0c\xbb\xedq,\xc9\x16\\\x05\xf1\xc8\x14G\x93l\xa1\n\xd8\xb1\xfc\x0dKV\xbfN\xaa}\x9d0\x19\xe14.\xc9>,\xf4\x85\x84\x9a\xd7i>-\xf4\xe9h\xbd\xebD\x1f\x17\xfax\xb8\xd6u\xa2\xcf\x0b}\x9fP\xe3:\xc9'&[\x9eol|+\xec#\x93m\x9a\xaf\xccK.\xb9\xb2\xf5\x9e|g(\xf9\xc5}hh/\x8b\xf8\xd2P\xca\x87\xf3\xa9\xa1\xdd\xef\xc7\xb7\x86v\xb5/\x1f\x1b\xda\xd9a}m(\x0bO\xc4\xe7\x86\xf2\xf6\x18\xbe7\x94\x91=\xfb\xe0d\x8b\xd5\xb0\x0e\xf9\xe4d\x0b\xfb\xe6d\x9b\xe3\xa3\xb3hx$\xfbb>;\xd9\x1c\xdf\x9dl\xf1sf\xa2/\x0f\xa1dz\xf7&\xfa\xf4d\x9b\xe5\xdbC\xe8In\xc2>>\xd9P_\x9fl3Tz\x8f\x9e\x19_\xa5\xe9>A\x84\x18\x9f\xd7\xb8oP\xb6\xa0\x8fP\xb6\xb9\xe3\x9b\xe6;D(\xc9M2\xc7\x87(\xdbd_\"Bk\xf4.\x92\x0c\x9f\xa2la\xdf\xa2la\x19\x18\x93~\xf1U\x9a\xee{\xc4\xbeH\xe5\x8dL\xf4A\xca6\xd1\x17\x89\xed\x15aJ7|\x92\xb2\x85\xa7c\xae\x8f\xd2\"\xa7<\x96\x8e\xafR6\xe5\xefq#+\xc2\x9c\x1a/\x8e\xf6\x90\x84l\"\xc8\xed\x1a\x11\x10O\xc4H\xb6\x803U65c\xbe@\xef\x85\xb9\x9c\xe4l\x95\x0d\xf1\x03\xee\x9f\xe1\xb1\xaf|g\xacl\xf0\xbb\x1b\xecIb\xfc.\xe9\xa4\x95m\xf2\xe4-\xec\xbc\x85\x06\xbf\xf6\xdel\x14)\x1f\xbc\xfev\xd6WoQ\xf3XW\x17\x92\x01\xde\xec\x12\x0b\xedX\xf4\x183\xba\x8e\x88\x88>\x92/((Bb #\x0e6\xc8\x14 \x13\xa3\x11\x1e\xa2\xf6\xaf _\xd1\xa2c\xb5X1%\x94\x93\xc7\x01!g|\xa4\xf1\xd1\xcf\x15a\xb1!\xa5\n\xb7\xd8\xc4\x04\x860a\xc9\xb2\x86\x10\x17\x891\xe6\xedU\xb5\xb0\x16\xd3 \x99\xc9\xa1:\xa2\x17{\x8a\x02~\x9a\x90\x8ff\x7f5\xe3\xe2.\xb8y\xf4S\x96\x9d\x88\xa2mZ~\xe7ROrd9\xe8\xb4l\xce(1=\xc3\xb3\x988{#\xab\xdb\xbd\x7f\xb8\x8fS\xd7!\xe2\x88\x8b\xae\x85xh\x8e3.\xec\x8e\x8b\x19\xaa\xa0-\xe7\x92Kt\xca%NL\xbc\xbeB\x96O\xc6y%\xc19\x97\xeb\x9e\xcbu\xd0e\xbb\xe8\xb2\x9ct\xd9n\xbaLG]\xbe\xab.\xdfY\x97\xe9\xae\x9b\xea\xb0K\x93|\xd0\x16v\xdae\xb8\xed\xf6\xee\xb8;\x94\xebn\x9f\xce\xbbGw\xdf\x1d\xd4\x81w`\x17\xde\x93p\xe2=m7\xde\x13r\xe4=\x8e+/\xee\xcc\x8b\x99\xb2e\x8b\x99\xb4e[\xc2\xa5\x17\xd7\x95\xa6\xbb\xf5<\x04\xbd5D\xc8\xc8\x8d\xb2\xc3\x8b[.\xe5J\x18\xa4\x91\xe3\x7f\xe0\xdf\x12\x1b\xa4,~\xec\xa591\x85mxZxv\xa8\xa1\x1dG\xac\xbb\x9b\x84\xfd}L\xb1!\xf4H;\xb1\x16\xb4a\xb7\xa9\xca\xa2\xaew\x81\"\xbe\x82\xc0\x84a,p\xf1\x91Z0\xa0t+Z\x83\xd3\x88\x1dG\xec\xdc+\xeb\x8a6\xe3%\xc8\x8e\xd6\x1b\x9b\xe3\xf4\xf5\xdf|\x8a\xed\xaaBG\x9b\xc2\xef\x1b~\xb7Z\x91\xeb\xdds\xb2\xdd\xac\xd4\xff\x0f\xd5=\xed\x87\xe2~\xd3?W6\x06H*\xf8\xdc\xe3I!\xa4\xa3\xb5(I\xb4n]6\xa3\xd7\xa9\xd8e\n\xae\x81\xab+\xc6\xa4Oj$\\\x1a\xe4\"32/\xd80}\xa4`O\x0b/4=e\x8f2\xf1!|U\xb4\x19\xba\x1d\xbf\xba\n\xbe\x82<_\xeffp\xacq\xe2\xb9\x01\xc2a)\x96R\xe0\xbfK_M>\xb1\xccOn\x1e\xeb\xa2\x1f$sA\xc6\x0f1\x99\x1c\x96P%\xcc\xa6\xf88\xc2\x1cU\xcd@o\xa8O!\x953Y5\xc3\xcf\xaf\xc2l\x87P\xb3\x8d:\xba\xac/\x16F\xc4\xf3O\x97\x1dW\xde\xa4TA\x0bZ\xc9\x06\xd3\x8d\xfeYT\xa5\x9a\xbf\x0c\x89\xe3\x11\xfd\xa1\x15\xde\xf9\xb6\xaf\xda\xe64\xc0/k\xf4\x01\xd7Z#8\x1d&\\\xdf19\xde\x93\xb2\xd8\x80\x92\x80zP\xf8\xf6U{\x08\xb7d\xdd\x17wTl-\x89@/\x9a\x95\xfc\x1a\xe8\x8e|\xa5\x1d%\xf7\xc5\xca\x1eFL\x94_\x88cTy\xa3\x91\x13\x95\x147E\xd5\xf0J\xca\xf2\xfcq\xe8\x98\xd69\xf64\x0f\xe6p}\xf0\x97\x9a\xc5\xe7kU\xd7\xe4\xb6x\xa0\x1ae1;<\xfew\x90Z\xbd\x19k\x0f\xadj\x1e\xda\xfa\x81\xaeN\xfe\xe6v\xf2\xfe\x03\xbb\xa4\xfcS\xccNG9\x1a\xa3i\xc1A\xfe\xc9\xc8(@\n\xe1\x8fGk\xc6\x8d^\xf9BN\xd5s\xbehr\xde\xf87~\xdf>\xd8E\x07G;\xed\xa4b\x1e\xd6\xebYN8\xd4\x8a\xe6Q\x02\x9e\x9c\x15\xda\xf6\x8a\x8e\x99Y\xbd\xbe63\xa6\xd1\xd5\x1f\xb4\xb9\xe5\x87\xe9}\xd1\xf5\xb7\x1e\x90\x15\xe1\xa5w\xb6!\xd0|\xc2<\x9e+\xecd\xb5\x86\xc3\x89\x0b\n.J\xe46\x91\xccE\xb0j\x04d\xf1\xbf\xb6\xdcy\n\x18.QP\x9b\xd3\x9c7gq\x0c\xfb\xa7\xb7o>|\xfa\xed\xea\xfc\xfd\xc7\xcf\x81\x92X\xe9\xef~\xfc\xf4\xe1\xe3\x87\x8b /\xc2o\x01\x81\xa5\xaahMc8]\xe2E'%4\xa3\x91W\x8d\xca\\\x012U\xc3\xa1\x8f\xa7\xdb\x06\x14t\xd8_l\xcd\x03/E\x96\x04\x9f;\xf9W\x1b\x10\xac\xed\xe8\x86\x14\xddu5tE\xb7\x1b%\x05O\xf3\xa0\x0eP\xd8\xb2\xf9\xbc\xc1o8g\xf0\x1b\xceW\x05\x1f\x9a\xa1\x19l:\xfaP\xb5\xdb\xbe\xde\xb9\x9f\xe0\x08\xaaEy\x14\x1f\xfdeW\x94wp\x95\x17\xa5\xc5\xa4\x8aN\xa5\x94\x8f\xa9\xd6\xe1\xdb\x8cF\xd4\xd1\xdf\xd8\x18\xca\xdb\x8a>@\x9e\xd0v;\xb0\x81\xb6M\xecF\xe3\xfc \xde|rZ\xcb\x81\xce\xb8\xff\x10\xe7\xdb S\xbc\x88\x89T[\x15\xfe}:^\x96\xf0D\x8d\xb2\xc5@Z\xcb\x9c(\xa2Z\x9c\xe0[n\x89\xf1\xbe\x97\x98\x9eD61f\xf1\xe9T\xcd\x8d,\xd1\xf7|]T\xf5\xb6\xa3\xcf\xd9\xa1\xb3\xa1\xcd*8\xdd)k\x92r\xc4\x84\xeb-\xa6\xbc\xa5\xd5`Ly\\\xab\xcb\x98\xf2\xb8\xaa\xd5\x88\xb5\xc4\xfa\x8d\xf8\xab9gN~\x9d\xc7\xe0\x01bW\x80$\x89\x11>{\xa9\x07\x99\xd2\xd5\xa3\xd5\x88Lan\x9fu#\xed\x16\x13\x18\xa0p\x7f\x80o\x9c6e\xb1\xe9\xb7\xb5bA\x1e\x1ck~$\x82 \x91\xe7\xa1\x87`@G\x0d\xf3\x02}=\xeb\xbfS>,(\xd0N\xc6\xca\x96P\xe1Wq\x02\xa9\xb1Pb\xa0\xfc\xca\x07\xc7\xdcE\xd2t\xec\x11\xc2O#*\xd9\xe6BN\xc8h\xc0\x14\xb7\xc7\xde RF\xe9y,\x9b\xd2\xf2k\xbd\xe3_\xa53e\xf7l\xd7\\\x1b\xe2KU\x0cCQ\xde\x02u\x05\xffg\xdf\x08\x8a\xda5w\xae\xd8_\xfc\x1e\xe6\x0d!1\xa6Lp f\xa4m\xc4\xa5S\xfc,\x00\xb8\xe6\x90\x941`\x92o\xda|;\xdd)\xeds\xd8x&}\x9a\xa7 h\xe7Xp\xa3\xaa~\xbc\xfee\xb1\x00^\xdf\xf18\x8d\x07q\x1a\xdb\xdd\x85A5\xea\xe9\x10\xdf\x8f\x0d\x89\xc48\x99\x86\xa1I\x1a.\xdc\xc2\xbc\xc6\x96C\x8eWcE\x0eX\x985\xf8O\x8b\x8cx \xcf\xb5F*\xc57\x1d\xe2\xea \x83\x07\x03\\G\xefv\xb1\x9b\xdd\x11;8\x91\xfb$1\x97\x02L\x8b\n:\xb2\x9f\x81DAl\x0b\xc8=r\xc4\x0efO\xfbDH\nI\xc1\x0e\nI2\x079\x88\x90\xc8R\xd1\x1c\x82>\xedzY\x85\xedQ1\x83&\x0f1\x8d\xae\x8f\xa4\x95\xf3&\x96s\x16\xe7 \x8a_\xd2\x98S\xa4e\x02\x99\xe8\x88\x17\x81\x91-?z?\xe8\xcc\xfd\x0bJ\x0fW\x9fSg\xe5\xa9h\x96\x08?\xf3\xd4\xcb\xa4 XB\xd1L\x18W\xea\x89\x13%\x14\x87G\xce\xd4RQb~m\xb5\xd4A\xdd(AC\x93\xb5\xb4V\xef\x90f\xab\xae\xf60f\x9c\xd0\x0b\xeaN6W\xd3\xf0\xe1$\x1d#N\xf2p\xe2\xe4\xd1gd\xf2Y\x80\x11K\x9c\x8f\xb4s\x81<\xfe\xdcxO\x8aD\x12\xd30\xe5.\x9d%5\xf2\xe4\xf1\xcf\x12\xa1.\xb9\xb4\x91\xce\xdc\x0c\xd3F:C\xc9O\xd82\xa9\xd8spE\xf8\xa1\xe7B!]\x02y\x0e\xa4\x02Xs\xf5\x80\x0d5G\x01\xe6VIM\xab+\xad\x1ef\xd9nv\xa3\xd6$jV\xdap[\xda+\xdf\xc7\xc8>z:\x05\xcaC:'\x90w\xf3\x84\xb7\x8c2\x8f$\xd53&\xf3k\x1a[\xd4\xec\n\xc7D\xf2\x94Q\xe5\x98\x90P\xc4\xf3\xd4\x89\xb1\xc2\x9e#U\x8f\xf1\xb7\x97(}LBF\xcc\x89\x83\xb3,\x99\xa9\xa5\x90\xc7W\x97(\x87L\x82%\x91\x89\x1a\x1d\x96RM\x1d\x8a\xc8\xdf\xc2C\x9f]\"\xd9\xa2\xa7YX\xf1\xd2\xc2$T*\x99\xecy\x94\xba \x8c\x0eR>\xe4\xf1c\x1b\x15\xec\x87\xa2\xab\xda-\xd8[HGo\x8an\xc5:,\xd0-_%}\xcf\xea\xf9\xf4O\xda\x8b\xbcI\xdf\x92\x02\xb9\xa2\x92\xfa\xbb\xb1\x8e\xe6X,\xa8\x92>[\xf8\xba\xfd;\xd7\xd2k\xd42\xa5. =\xee6\xc8P\xe0]\xcbh\x9a\x1aw\x06\x016j\xb4\xc3m\xd5\xdc\xf8n_\x93\xe8k\xff\x84\x0d\x89\xf5\xf2\x95^\xf7\xd5@\xaf\xb6]=\xa5\x97\xcf\x9f~gg\xd7\xbajV<\x19\x1e\xa4Q\xb3\x93uVe\xdb\xcc\xe9\x01\xa2V\x18\x15I\xd5(\x1bH\xa7\xc5\x0b\x87>\xfa\xb10\xa2\x90*z\xb5\xca\xf0\xee\x11I\xe9\xaed\x12\xba%x\xc3\xd0\xb6Ie\x08R\n\x0f\xc4K\x0d\xc4\x8b\x0b$\x94\x13\x88\x14\x10H(\x19\x10-\x12\x90R\x16 \xa5\x10@4\xf5\x7fz\xb2\xff\x90r8-\xa1?\xa4\xed7\x08y\x00\xbe{H\xda\xbf\xbf4\xfd\xcb&\xe6\x7f\x84T\xfc{N\xbe\xbf\xf7t\xfb\x8f\x94`\xff\xa9\xa5\xd4\x7f\xd4$\xfa\x87K\x9b/\xd4/\x9eY\xfe\x92\xada\xd1\xc1\x9dzU\xad\xb9;n\xe0\x19\xe5{\x95\x08\xbem4\xd0//J\xbb\xed\x1dm\xce\x10pgPLx\xcc\xd3*\xb2 \x8b\x9c\x15\xb2P+',L\xb6E\xc3m\xaa\x95\xfe5k\x110\x9c#9e\xb61{\x11% \xfb\xfe4\x0e\xd2\xf0u\xea:xO\x8a\xban\xbf\x8a\x94\x06r\xac\xd7E/\x0bF\x1b\x11=\x88NaO\xae\xab\xb9\xab\x1c\xbd\xd2V]ut%\xa7\xfd9\xe9h\xdfn\xbb\x92\xfd\xaf:*\x94\xf1\x96\xcd9\x06\xf4\x16\x92Nw6&\xdf$>\xe9\xd6\x95\xe5/\x14\x81\xfb\x99\x8d\xc6O\xb2\x84\xaf\xca_~\xa4\xc5?^\xbe\xa0E\xb9z\xf1\xea\xe5\x9a\xbe\xf8\xb5X\xad_|_\xae\x7f\xfe\xfe\xd5/\xd7\xeb_\x8a\x1f\x10+\xb8\xd1\x99n46L\xae\xf00{\xf6\xfb/\xcd\xd7\x9b_~n\xfab\xf8\xf6\xd3\xa6\xfe\xe9\xe7\xfb\xed\xd7\x97\x0f\xbf\xee\x86\xfa\xcf\x1f\xbfl\x7f\xfc\xd7\xb7\x9f\xef%W\xd2jdlrg\x87\xdb\xd6(\x9f\x95\xca?_x?)F*\xd4\x0bm\xccH\xd0\x1aS\x19\x13\x86Y\xebL\xab\xe7\xbe'@\xef%m\xf8\xf0\xc6\xc4\xc1s\xf3\x80\xf6\xae=x\xaf\xfdu\xa9q\xcf\xb6\xb7Z\x06\x0e\xff\xeaYv\xd6\xa5\x060\xd7\xae\xca-\xa9\xe6\x00p\x9bj@\xec~\x1a\xfb6\x9c\x84\x9a\x9dU\xbc\xabY[M\xc1\x9dgz\xd5z\x86\xe9N\x97\xb0\xca\xe4d\x99{\x9c\xf1\"O\xca\x01\xaa\xe4^\x0b\xd9N<\xecx\xa9\xf8)9\xd4\x16\xae\xfe;\xd3Z%\x9b\xd7\xa6c\xffq\x96\xf5J6\xef\x1c\x92(D\xca\x9f\xb6#8\x87$\xdb\xca\x85\xbd\xb5H\xbfI\xd6/\xd9\xbcV0\xd9\xf2zO\xb3\x8a\xc9\x86[\xc7d\xcb\xef\xd9\xb5\x96\xc9\xe6\xb5\x9a\xc9\x86)\xce\xb2y\x93\x04\x049\\\xca\xaa&\x9b\xdf\xba&\xdb\xf2c\x08\xc5\xba'Y\xe1\xd0\x87#e@\xe3V9\xf4\xc9`\xe9\xcf\x04+\x1d\xfa\xa8\xbf\xdcg\x82\xd5\x0e}4T\xe23\xc5\x8a\x87>\x1b)\xeb\x19\xb5\xea\xc9\x96n\xdd\x1b\xdf\xf0[\xf9d[\xcc\xda'[$\xac\x7f\x0f\xd6?\x94\xf4\xa2V@\xb4\x87\xd9\xd6@\x94\xeaa\xac\x82h\xd7\xcb[\x07\xd1n\xf6a%D;:\x9c\xb5\x10\xed\xfe X\x0dQ\xbe\x0em=D\x99\xd8\xab\x15Q\xb6p\xb2\x88\x19FF\x94\x9enx\x94-\xc4\xc1B\x86H\xd9|\x06I\xd9\x02\x86I\xd9\xf6\xa1\xa3\x04\xef/\xf1\x03j\x9e!\x13!\xd86\xc8\xc5\xc8T\xe7|\x8b6\xc3\xc0\xe9\xae\x854w\xe6\x18:\xc7f^\x8d\xb1K\xb1Jk\x81Yz\x90\x1c\x17&\x14\xc4E\xd7\x85\x97\xca}{\xc4\x86\xe4\x87Pz\x0cT\xd6*M\xb8\x8a\x97(\xe6K\xb6\xe9\xf7\xf0\xf8>6\xba\xce\x01\x88a_:\x1e8c\xae\x81/^\xe6`\x83L\xc1\x99\xc5h\x84\x87\xe8EO\xc9v\xb8\xb1z\x00X\xf6\xcfb\xf4\x98X\n\xa0\xc4\x90.\xf1`\x96\xf4+olH\xa9\xb0\xb6\xd8\xc4\x04\x860a\xc9\xb2\x86\x90\x05\x84C\xde\xf7\x01\xe2\xac\x93t\x9a\x7f/\xb9(@\xef\xb7E\x06-i\xc1Z\x88Q\x8b\xe4\xbcS}1\xbbd\xd4\xfe&\xfa\xf9M3\xaa\xe9\xa7\x8d\xea\x0d\xd7\x1d\xbc\xc3\x9e\x1c\xc093\xef\xfa4\x1bet\x96\xc8\xa4\xde\xb3,\x95 \xb6\xca|\x1e\xf2\xec\x951\x8b\xe5\xb4\xfe\xfdVK\xe7q\xcf5C\xdf\x9c\xb7m\xbd\xeaE\xa0\x96iD\x97u\x8bo\x8bf\xb5\xf3\xd5\xd4\xb5\xaa\xca\x17De\x9av\x99\x8b\x1aU\xc3\xea~B\xf6U\xefD\x8a\xaf\xf2,Ps\xba\xfd\xda\xf4\x96 \xa8\x06\xe4\x8b\x8c\xdbU\xf77\x8cp.\xd1,\x0bk\x9e\x8d5\xc7\xca\x9acg\xcd\xb2\xb4&\xdbZ\xb3\xac\xad\x19\xf6\xd6<\x8bk\x9e\xcd5\xc3\xea:\xc5\xee\x9a&\x18\xa6\xda^=\xc4\xb8E6f}\xdd\xab\xfd\xf5\x10\x16\xd8}\xd9`\x1f\xd5\n{0;\xec\x01-\xb1\x8fn\x8b}\xba\xd6\xd8'b\x8f}\x0c\x8bl\xcc&;\xd9*\xeb\xa1f\x03B\xc7\x16\xe6\xa30l\xb3;\xd1\xb3a\x9f]o\xebz]\xd55]\x91\xaf\xb7\xb4!}u\xd3\xf0\x98'|\xd8]\xd1\xf4\xb0)\xb2\xaf%\xd2\x8c\xb8\xcf\xab\x9ag\x1e\x86X\xcd\x19>%\xc6\xa7\xe1I\x04\xef](_\x1e\xfe4%s\xd0J\xc8\xa8\xe9\xbc\xae\x9a\xa2\xdb\x91gcA\x99\xaa\xe9\x87\xa2)\x91O\x81\x97`\x9a\x9aHJ0\x01\xa5\xa1Dj\x12^\xd2IJ1s\x99!\x05uq]\xdb\x0b\xe1_\x847\xe2\xddE\xcc\xce\x0b\x18\x9e\xb3L\xcf\x1e\x93=\x9aa\xd9vOLJ\xe5\x87\x12I\xb7D;\xf4\xact3a\x0c\xc8Dk\x8c\xcd\xf3\x84m\xb8@>3\x9b\x0b\xaf\x89:(p\xcdu\xf6&xr\x96\xe91\xd3\xfay\x98\xc9\xb5[\xa7\xa4\xeaq\xba2\x7fx\xbc\xe4~ \x8c\x91@\xd2&\xe4\xe9\xec\x84M\xe6\x13{\xccp\xe7v4\xd1\xd0\x9d\xbf\xe2\x13\x178\x7fT\x93l\xdfI\xdb@\x1fb\x88/\xf4\xdc\xf2\xe7eB\xa5\x8a%M\xbc9\x9bHz\x85`c@\x81\xdcM\xb8\xc4\xef\xcd\xc4\x17>p\xa07\xc7\x93?b\xe0\xe84X\xd0i0O\x99\xf3|`\xef\x85\x8e\x07\x19\xc6D\xf9\xa6\xaa\xae\xd9U\x00\nb\x88{\x80\xcb)J\xaf\xf2\x94o\xfd\xcb\x96\xf8\xd4ue\xb5\xa2\xa1\x9a\x82\xc3\xb2\x85\x1a\x8b\x91 \xafM[\x95E]\xef\xa0(\xcd\xd0\xaa\xf2\xae\xe3}\xc0wm\x16\xdf\xf1\xb10\xa8\xd3\x92\x86\x9d:\xa2\x03\x17\x06\x8d\xb1u\xcev*~\x0b\x83\xda\xc0\xcf\xc7\xfd\xf5\\\x1cV\xec\x12\xe6\xddD\xb2V\xe03\x11\x07\xd8\x9a%\xb4\xbf#C+z\xe0\x1e(\xfe\xa1x\x88m\x8a\xae\xb8\xa7\x03\xc5f!<\xae3\xc2MFkY\x96\xca\xb0p\xf4\xc5P\xf5L\x04C]\x9e\xcd\xa6\xde\xf9\xef\xf70\xb9\x17\xce\x91-\x8a\x05\x15\xe2\x01\xe7\xdd\xe0W>Kb\x9f\x81-Q \x1c^}\x0f+\\'\xbf\x97Q*\xa0\xf4D\xb9f^\xa9N\x89\x0cw3A\xc4\xd6\x15g=\x9b\xe7Ka7\x00\"\xa3\x9c\xc4\x12\xa9\x93gr\xa1\x02\xb5J\xdbN\xd6\xb2\xfc\xfd\xfc\xe2\x12\xdb\xfa\x919\x0e\xc5\x18\xfc\xf6\xf6\xdd\xf9\xfb\xf3\xcb\xf3\x0f\xef\xd3\xbc\"\xee\x1b\xc1\xf2\xac\xee\xe3\x81\x81\xfa\x1e\xe6\xe3F\xdeP^\x9d\xbcA\xa4\xe8\xe2\xc1\x89\xc9\xed\xd0)\x9d\xc7\x0b\xb0\x9e\x8a\x82\xacP \x1b\x17\x0b\xfe\xd9vy@\xaa\xae\xf2\x83\x11\x04\xa7(\xb7-\xec\xe3\xb7\xb4\xa3\xe4\x99:e\xa4\xe7\xbai\x07e\xf9\xf1\"\x80\xf15r\xf9Aj\xad\xca\xaa\xdfr\xd3;\xf5V\xadS/\x8f\x05\xbeM||\xf0?\xe2\xcc\x14;\x8c\x95\xb2\xadkZ\xca\x0b0\x7ft4\x8b\x93\xdb\xe2\xc1w@r\x0c\x18\x13Xh\xb1|\x11\xcdZ]\xd7\xf4JX\xfb\xf6\xa0(\x1e\x1d\xe2)\x0f\x1f\x1d\xe2G\x87\xf8\xd1!~t\x88\x1f\x1d\xe2G\x87\xf8_\xc9!~il7M\xe3\xb0/#\x7fC_G\x86\x81\xdd\x0cu\x07\xa6y\xa9\x01\xdf\xa3\xe7\xb2\x08I3\xb8\x97\x1dB~N\x95\xf7\x13.\xb0\xa7\xc2\x85i\xbd\x99\xe1\xb3DoZ\xa1\x9a\xb0\xfd\x0c\x7f%B\"\xdd[y,=v,=\x96\xe7\xa1\x8c\x150\xb2\xbaY\xc4y\xb5\xfc\xe8\xfd\xae.\xf7/(\xbdc\xe9\xb1\x10\xc1\xf0\x04\xec\xd11kw3\xd9-\x1b\x1b\x8f\xe3\xe6<\x96\x1eKB\xa1\x90\xf8:\x93\xf8\x16'\x89\x13K\x16C\xa5\x90td\n\xc9C\xa7\x90G\x9f\x91\xc9g\x01F,q>\xd2\xce\x05\xf2\xf8s\xe3=)\x12ILC\xb2\xb8t\x8e\xa5\xc7\xc8>G:\x11\xed\xe2\x12\x9a\x85xA\xae9\xd1:d\xbdW;]\x04\xed\xe2d\x8f\x0c\xd4)s\x91.\xb6\xaf/\x07\xe9\xf2\xc4\xaa\x99\x05\xb1+\xde}\x19\xde\x8d6\xd1\xb4\x9c\xae\x9c\xee\xa4\xbc\xae\xa1\xeem\xc7\xf0\xfcL\xaf$!\xdb+Q\x93\xb7\x9f\xdaD\xa5\x0b\xd6\x8a\xa5?\xb5(\x95\x1e\x18\x97?\xdd\x82\x95\x01\x96\x90\x94\x9aM%\x82\xeb\"{\x9e\x1c\xe7\xdb\x8eM\x0dn\xb6\xc9\x99\x18be\x87%H\x82Ur\xac\xd2\xb5\xe0\xc0\xe6f\x93\xb5\xc8\x85\xabt\xa9\xde\xfdy]\x95\xfe\x8d\xd5\xee\x12o/\x9dSV\x1c\xa20\xd9\x8e\xd0\x8f$\xed\x1e\x97h\xff\xc7\x85W\x93]\x00\xd8\xa8/\x94\x7f\xff\xcc\xcd\xce\xea\xbdC\xfao\x8f\xb8\x19$\xa8\xea\x19,'e9\xf0\x8e\x9ed\xf6\xa5\xfd\x93\x04s\x1a\x04\xb3\x19\xa4\xf7\x98\x9e\xc1\xc0\x9f\xbb \xaf7<_A0\x19\x80\x0f.\x80\x02\x05\xd2\x94\xa5\x99\x19U\xc3Q\xff\xcb\xf0\xeb\x033$\xc3\x18R\x01\x0ci\xd0\x854\xd0B\"\\!\x01\xa8\x90\x08QH\x02'\xa4\xc2\x12R\x01 IP\x84<\x10B\xecb9\x0dx\xe0\xc9\x82\x1a\x80\x1c\xec l\xb0_\x98\xc1\xf2\x00\x83G\x82\x16\x1c\x00Tp\x108\xc1#\x02 \x9e\"\x84\xe0\xd1\xc1\x03\x87\x85\x0d,\\#I#\xe9\x08\xc6\x05S\x94\x86\xd2\x93\xdaV\xb8\xbd((\x01\x05=|:\xccKAJ\x9c\x1d\x1bJ?\x8a-\xc42iGgE~\x1b\x13\xd4\x07\xf1\x13\x98\xe9\xc0\x82O\xb8\xc9B=\x96Bc\x1a\xdc\xb7F\xbbj\x02T\x82 \x95o\xbd\xab\x90qW,\xbd)E\xf3/\x8a\xe64\x97\x133\x86b\xde-,3\xe7\xfe\xb8MqJ\xf9\xf3D\x06s0.\xca\xb4k\xd3\x0duM\xc2I=\x03il#\xa3t\xddCi\xb7,\x8bD\x8a\xb3'\x89\x9f\x8cyN\x9e\xcel\x1fMp)\xf2\xacW~7\x0c\xfa\xf5\x08\xcf\x8b \x17\xf2\xbf$\xdb\xb0\xfa\xb3\xba\xce->w\xac\xb5\x15\x1b\xc0\\\xebh\xa8\xd6\x16!\x9b\x82_\xb7*oE\xaa\xf1\x01u\x1e\xb7\xfcOE\xad\xffM\x05\xe7\xf5\xd2P*Y\x98t\xe0\xdc\xd1]\xe2\xb7\x99&\x03\xff\xc7\xfeh\xef(\\wD\x84KG\x87m\xd7\x00\x12\xe5cqC\xa5)\xf8\xa4\xa1\xdf\x86+\xf6\xf0\xd0\x92kz\xe3\xa8\xa9_\xb6\xb4\xdb\xb1\xcf\x9b\x8d\x98=\xcc&\x85\x92\xfb\xb6\x1f\x08]\xaf\xab\xb2\xa2\xcdP\xefN\xc8\x87\xa6\xde\x91\xb6\xe1w\x93v\xbd\x86\xbb.c\xc3\x96\x08\xb7\xed\xb6^\xf1\xc0D:\x18\xd6-\xfeR\xe6\xacl\xabf\xf8\xf9\x95\x7f^\x1ca&X\xe3S\xd3l\xef\xf9\x0dW\xfc\x06w\xa9\xa2a\xbc\xf1\xeb\x0e\x0f\x04\x87\x89\xb4\xa8l\x9b\xe2\xa1\xa8\xea\xe2\xba\xa6'\xb6/\xe5\x9cS\xaf\x99\xbe\xa2&\x88\xd1n\xc8\x96\xc9+F0w\xb6\xec.\xdc\xc9\xab\xab\xfbj\xefs\xc7;\x91\xe2\x7fh\x87\xa2\xd6\xfc\xd0\xc2\xb3\x0b\xfb\xc8\xd8o\xc2\xd5\xb0\xad\x1d\x8c*\xd7&\xed\xe9[\x93\x9a\xae\x07B\xef7\xec\xc6.\xc2\xf3\x85\x9d\x08\xe2\xb2`KCGl\xe6\xaew\x84\x16\xe5-)6\x1b}N\xf8\xdd\xf7\x8a\xf3\x89\xcd\xcc\xb2>\x1e\xad36C|?\xb5d\xe8\xb6\x94X\xd7\xe6b\xd0f\x84?(\x16\xd9$(\xa4\x9d\xe3\x13\x86\x9e\xa4\"`\xaf\x02\xbfL\x11\xb59\xb9\xac\xd2\xa4\x98\xf3y\x7f>\xef\x9d\x15\xb0\x86\xd2\xb2\xad\xdaq\xcf\xb3\xca\x8e0~E\xecC9\x11{\xbe\xbai\xda\xce\x89K\x97_\x91\xdd\x0d\xcc\x92\xbe`\x1dej\x0e\xdd\xffb\x89\x8e\xec\x85\xaa\xc6]\xcc/\xe3\xd8N\xb6(\xb1~h\xc3\xb1\xecm\xb7\xa2\xdd\xc9\xdf\xeca^TMI_\x93\xb2\xed\xef\xdb\xfeE\xbf\xba#/O^\xfd\xa8\x1e\x12WEC~\x83\x80\x1e\xab?r>\xe8\xfd5]\xad\x80\x8f\x9bO\x1f\xdf\xa8\x13P\\\xcb\xe0\\R\x12G#7\xae\xff y+\xee\x0b\xa9J\x97\xa6\xf2\x8c^\xc3\xecB\x94\x8c\xcaT\xc7!p0\xbf \xe51Y\x0b\xf6\xf6|\x9f&\xb4\xa0ooa\xff&\xb4\xc0\x9c\x92\x04\xa4\xec1\xc7\xfb\xd29\xd6\xd3<\xa4\xd0\x0e\x9d\xe3\xfd\x11\xd3\xa8/\xe9Q\x85v\xcc\xa6~\x0c\x1e\xb7]4)O\x1f\x83\xc7=O\xed\xcb\x9f\x8b\x10_\xdc\xab\x8b\xf4\xb1\x88o\x17\xa1{8\x0f/\xd2\xf9~\xfc\xbcHG\xfb\xf2\xf6\"]\x1d\xd6\xe7\x8b0\xf0D<\xbf\x08g\x8f\xe1\xffE\xd8\xd8\xbb\x17\x18\xda\x13\x0b\x1e\xf7\xf0\xb1\xa0\x1b\x19Z\xac\xd6e\xd4\xa5\x0cm\x7f\xba\xce\x02a\x9eS\xdd\xce(1\xe9\x8a\x0e:\x9f\xa1\xf9\x97r\x19G4\xb4Y\xeehh\xcb:\xa5\xa1\x85\\\xd3H\xaf\xc8$\xcdtS;\xf4\x10\x059\xc0\xc2.\x07\xaa I\xc0\n\x12\x07W\x90\x80!d:\xc8\xc2\"4B.\xac?`\xb8\x8b<@\xa2\xb2\x9f\xf3K\xa5\x0e\xe3\x19-\x10\xd2u\xe8*S\x19\xea\xa4<^1uk9L\xa2:\xc4\x85\x1cU\xe79\xd9\x14\xbd\xb02k\xcbr\x02\x7f\xb7\x88\xf0C>z\xc2\x9f\x8fe]\x84\x14E`\xdd\x1d\xd8S\x9b\x16\xdc\xd5\x8e>N\x88\x17<\x96\x07\xab\xf3\xedA%*|'\xca(\x97\x1c\xfe\xf5\x89\xd2\x04\x8f\xbd \xbf\x16\xfcs|N\xaa\xa1\xd72\xc07\xa0`\xaeH\xcb\xe6\xe1k%\xcc\xc1\xf8W\x1e\x06\xe5\x90\x19\xd9\x042\x81A\xd9I\x04\xd8K\xaf\xa7|\x10paL\x06\xd0\xc7\x92\x068+\xff\x19\x107\xe7\xbfiH\x1b\xd6\xe5 !\xe7\xf7\x9b\x9a\xfb\x90z\xd2\xaf\xeeNd\xfd\xf5\xaa\x19h\xb7.J\x8a\x88\xa0mO\xd9I\xd1\xa9\x022\x14\x84\xad\x96V\x95\xbc\xe12R\x1fb\x10]\xb4\xdcP=v\xbf\xd1\x8d\xc5M\x00\x83\x9dB\x16.7l\xf9-zT\xce\x8e\xb4\x9a\x7f\xad\x86\xdb\xaa\xd1&Q{\x01\xf0\x15\xd8\xd0\xb2\x02\xd1\xb2-\xb2b\x1d&Xvd\x9d#\xbc\xec\xfe3\xe9GB*\x96\xb4\xf5\x84R%\x05\x7fo\xdc\x85\xb2#5\xa9\x90+\x8d\x89Z\xc1\xca\xa6kK\xbe\xc5Pzh5\xac\xe8\xa0C\xe5I2\x10\x069\x80\x92t8I:\x98$\x03J\x92\x08$\xc9\x80\x91$\x83Hr $9\x00\x92d\xf8H>x$\xae}N\x05\x8ex\x82\xffI8\x01\x00\xd9/hd\xff\x90\x91\xfd\x00F\x1e\x11.r \xb0\xc8\xc1\xa0\"\x8f\x0c\x14y\xaa0\x91'\x01\x1294DD\x1a\x81\xfd\xda\x84\xef\xd6\x1e\xbf\xd7+\xfd@Y\xba\xd5\xb2\xfd\xbd\xe7\xd5\xf0\x8ba\xdbq%_\xb2\xf17\xf4}D*\x9f\x01pE\x8a>\xa1\xd1\xb0O\xe1\xb4j\x98P\xe1\xa3\xe7\xfa\x87]\xd0\xcd-\xbf\x8f\xc2\x07\xc2\xe7\xc2%w\xb3H\xb0\x8a\xda\x84dh7\xa4\xa6\x0f\xb46\xe0\xb72\xb5^/t\x9f\x13\x12$\xc7a/l~x>Na'\x90\x90\x98vU\xad\x1d\x9b\x0d\xeb\x82W\xaf\xd3t+\xa9\xf4\x03mS{\xaf\x1a\xb2m\x98\xa6\xcc\x9e\xb7hI.\xea\xaa\x1f$$\x84\x92\x9e\xf6\xbd\xee\xb5b\x8d\xf5xU\x94\xa5\xa3\x8dNP\x83\xb3C\x87G\xbc\xb3T\xd09\x1a\x86\x8d\xae\xd8\x0e\xb7mW\xfd R\xa3\xa3%\xad\x1e\xa8V#\x12\xd1\xfc\xd1\xaa\x7f0\x83\xda\x93\xfc\x92{e \xbe\x13\xaf6\x91\xb1\x8c[\xd8\x14\xac\x83\xaa#\x16`\xd0\xdeL\x17C\xd1\xac\x8an\xa5\x0bC!\x91{\x0e\xa5\xb9/\xba;\xda\xa9\xdf\x9c8\xb0\x8e\x92~\xbb\xd9\xb4\xdd \x80C\xbc3\xce\x07\xdfO\xec\xa3\x1b\x86\xae\xba\xde\x0e\xbc\xc0\x19\x98#\xaf)\x931\xcd\x8d\xa3\xa4_\x83ME\xc8))\xc4\xd9\xce*\xd9y\x88\x18\x1b\xc5\xe5\x92\x970\xdb]um]o7\xfbw\xd7\xfdS\x88\xa9\xa2\xae\xd5G`\\\xfe\xf8\xe4U\xec\xf2,>\x06\x1b\xa0f\x114^\xfe{/E\xc2\xba\xa2\xff?{o\xd7\x1c\xb7\x8d\xec\x8d\xdf\x9fO\xd1\xb57q\xeao+v\x12\xef&\xae\xda\x0b\xad\xed\x9c\xa3\xfa\xe7\xc5\xc7\x92w\x9fs\xa5\xa5f0\x12#\x0e9!9\xb2\xe5z>\xfcS\xc4\x0b\x89\x97\xc6+1\xd28\x87\xb8\xb25D\xa3\xd1\x00\x1a\x8d\xee\x1f\x1a\xd5\xda\xf0\xf22\xb1V]\x03\xa4.\xae*B\xb7=\x1a\xe4\xe0\xba\xf1\xef\xd4\xe7\xc9\x18\xe1\x94\xe8Mt\xc6\x0b\xbe\x88-1k\xa5\xeb\xd4\xfd!=U\xda6M/=W'\x10V\xf2{uB\x9b\x0dl\xac\xe1J^\xe442\xd32\xaf\x97\xc6\x84p|\xb8\x12z(\xdfD\xe1\xa2$\x9d7\x92~\x04\xaf\x0c\xda\x1bp\x83\x96\xac\xe98\x0e\x99\x89#\x0cY4~\x8c2\xc7\xfe\x11\x91\x0d\"\x92\xc1\x89>(\xe8\x1f,\xed\xba\x9d\xd1y\xd93B\xc1:\x0e\x06\"\x04\x840\xe0\x07\xd8`\x02S\x17\x1d\x8eW\xb4\xeb\x04\xb3V\xf4Z\xd4\xa9-\xa9t\xa6\x92\x93\xdb\x88U\x8c\x80\xc6TR\xd2\xff\"\xe6l\x18\xd3\xc6v\xebl\x1a\x96T:\x916\xa4E\x80A\x90<\xe7P0\xc6\x95\xc6\xcf\x99\xcd\xe1\x03\xdb sIPq\xc5_\x96\x048K\x02\x9c%\x01N\x0c\xa8lI\x803\x13!\xb6$\xc0 G\x7f- p|x\xae%\x01\x0e+\xf9\xb0Zk/N\xeb\x0bH\x80\xa3\x1b7\xe0Hu#cY\"\xe1+\xe9\xa9l\x0e\x9d\xbcf\xa5\xe0d\x9c4\\t\xc0\xea\xa3a\xc5\xaa\x91Y\xf1\xdcNr\x03\x19f jPz\x03\x95$\\\x0d+nt\x0d+\x87\x14\x87\xed(\x1c\x82\xbaA \xd2\xc8E\x14\xf6\x86\x15\x0c\x81\xc3\x8a-\x00\xc1\x8a\xf7\xde\xae5\xf5\x8f?\xf9\x8f\x13\x9f\xc3\x8agl\xf8G\xa9X\x1dVl\x88\x1dV|\x97\xc2!\x05\xbd\xe3\xa05\xe2zl\x18\x1eV\x82\x04\xe3J\x12\x03\xb1\xa8\x1e\xa4\x8a7Y\x8cQ\xc3\x930\xc6\xf8\xde\x934\xc6\xf8\xde\x978\xc6\xa8\xe0J\x1ec|\xecK cTp'\x911>\xf7&\x921jx\x93\xc9\x185\xdc e \x17$\xea\xf9\xd1A\xac\xa4a\x84\x1c\x04\x03\x13\xcc\xc0a\xf1BH\x03\x07A\x0d!\xedd\xc3\x0e!\xb4\x1f\x16A\x840p8\x1c\x11\xd2\xd8!\xd1DHs\x0f\x8f)B\x988\"d\x11\xc2\xddc\xe1\x8b\x10V\x1e\x00e\xc4\x8a\x0bk\xc4\x8a\xfd<*\x17?\xee\x88\x95\xb9\xe8#\x85\x8aU\xf7\xa7#\x91,\x04\x9dIvB\xf6\xa3Dl\x12JK\xe0\x95f#\x94X\xe9\x13qJV\xde\"\xd0J\xacX1K\xac\xcc<8Xm\xd7\x90qKG4\xa1\xe4\xa8\x9c\xc3pM\xacx\xd1M\xac\xe4\xe8i\x1a\xde \xa5%\xa6\xcf\\\xd4\x13+\xc9\xd8'\x94\x1a\xc7C% \xa0X\xf1\xe3\xa0X\xf1\xe9M\xbf\xc6\x0c\x19\xb5t|\x14JN\xa8\xa5(\x94\x14+\x89X)|\xfe0^M\xc4\x14+>\xc1\xccEO\x19\x04\xaf\xee]\x18*V\x84#lF\xfe\xa9\x0c\xb0\x0e\x89\x9c.\xb5\xa3s\xf0e\x82e\xb12J\xf0A\x92Di\xad\x85\x00=\xec\x89\x81\x1c\xa0-V22nQ#\x08#\xe0Cw9 \xd9\xfa\x9a#\x99\xd3D)\x02\xf1ag)Q\xea\xe1b\x8d\x86~\xc8\x95Q\x00\x99\xfc\x01\x0e#c\xc5\xc7\xa3I!J\xf7\x18\xf4,^\xe8\xac\x9a\x08\x85\x9a\xb1\xf2P\xeb$\x01\x93\x86R[\xd2\xdea\x8dH\xffK\\\x9a\xd9\xfb\xad\xb1\x04K\xda;\x9d\xce\xff\xbe\xb4w\x89x;\x84\x12\xfd\xd0\x92\x9fn\xc9H'\xcal\xdc\x9fF\xaf_2\xd2\x81\xce[Fl \xb8\xf6\xf9%#\xdd\x92\x91n\xc9H'\x15\xbf|\xd3\xb1\x88\x08\xb1%#]\x1ar\xd1\xbe\x14\x97\x8ctcY2\xd2-\x19\xe9X\x890'\x97\x8ctKF:0\x8eX+\x0dE\x0b\xae\xdcs\x89H]v\x8cc\"6&,\x82\xcfe\xdf+\x1e(tD\x90)j\x07\x89\x86Rp\xa9\xc1\xf1UG\xb1\xd3X^ \xa1\xfb\xda0*\x02h\xb1j\x89\x80YH\xe4xGOF\xad\xa1\xadY,*;\x17\x98l\x89p:,\xab$H&\x06\xc4\x14\x84b\x11\x96n\x1c\xa5\x83s\x1c3\x19\x8c\x94\x0c\xc5G\x86\xa1\"\xc3\xb0\x90\x81\x08\xc8\x00\xdcc \xda1\x08\xe3\x18\x8al\x0c\xc53\x06\xa1\x18\xe3\xb0\x8b>\xdb%\x0d\xa7h\xc9e\xe6@'\x1e\x08\x93xX$b~\xfc\xe1#\xa1\x0e\x1f\x00k\xf8 \x08\xc3G\xc4\x15\x1e#\x9a\xf0\xd11\x84\x0f\x89\x1c\xb4\xe1\x05]g;\xf7\xc9n\x0e\"\x107k\xd3\xd1\x7fZ\x1e2\x03\xf3\xc7\xdb\x1b\x816<\x0cD\xa81\xc0^\xa7\xe9\xc7\x87\xf2\x06\xe6\xa9\xab\xbd\xectJ\xeak\xd4\xa8\x91\x80um\xa8F\xbd/\x82k\x19?\xc6!3\xe4\x13Y\xed\xa9\x9d\xc7\xed\x94\xa7\x03\xfdrUT\x95\xec\xfbZUE\xd7\x0d\xf4\xf8\xdf\xf8\xc7y-Ra)1\x8fvI*\x86\xde\x1a\x94\xf2\xa0\xf3W\x15u\xdd\x8dS\x1e\x05\xc7\xc9#\xa2\x99\xa4\xc5~]\xda\x02\"_\xbd\xa6F\xed\x1a\xae\xee\x9f\xc2~\xb7\x1e\xff\xdd\x97[\xd2\xf5\xc5v\xd7=\x1dC\\\xec\xd8\xc3\x9e\xb4kIE\xbf\xa5\x8fY\x7f\xa5\x89#\xe8(\xcb\xcc\xe9\xf5\xe5\xd0f\xe4\xc9m\xa8\xf2l\xe0P\xad\xc6\xa6B\xcfp\x9a\xe4\x9b\xe1\x03\xe6\x97\xa1#O\xea\xbe\xbd\xa7\x06=o\x19\xe1\xe5*\xf4`-\xb5e\xb1\xa9\x99\xe6\xe6\xd2\xe5\x07\xffU\xd3\xca\xadry?\x82\x04\xaa\xa2\xebE\xf3\x08C\xb9\xc5@a\xb5\xa5U\x0e|~am\x96uO\xaeI\x8b\xca\xc0z|\x1f\xb6I\x11\x8eQ\xe7.c\xa7\xec\xa0\xacW-\xdd\xb6\xc5\xfa\xa1\x17\xc5Y\xff%\x92\xdc\x9b\x14'\x0d\xb9yN\x00\x81\x84\x1269\xca\xa6\xfe\x865K_\x887t\xa8\xa2(N\x87\x95\xfc\xd3\xa0 :X\x15;\xa6\xf1Q\xfc \x1d\xe0q\x0c\x1a\xd8\x16\xb7J\xbf\xe8\x80\x8c/~\xd5k1K\xc8=|$-\x81m\xb1F\x1c\x0d#3\xdc\x1b0a\x07\x11\x85\n\xc5uQ\xd6\x03\x1b\x93\xea\xc2\xb0\x1e\xc37\xd4\xdd <\"\x17\xd2\xe1\x91\xfa\x86n\x8a;\"Q\xe1}\xa5\xf9\xe3\x94wW\xc5\xcb\xef\x93\x0b\xf2\xd7\xdf\x06\xab\xee_\xbco\x9a\x1b\xe9\xbd\x92\xd7\x12\n\x8e\x87\xa4SdBA\x16j\x97\x9fR\xf1\x8a\xfe\xd3\xd9\xbcmh\x9b>\xd7\xc9\xd9\xfal\x0c\x00\x1f\xd8\x81\xa2\x07\xceGR`\x85\xe2\xf0\x0eiP\x9b\xa9\xa6\x81\xae\x99\xcdK\x08JF\xfa\x1ca \xc3\xa5\xcdg\xcb\x04\xa3I\x7f\x89b\x8f\xff3;\x83\x12\xdd\xc9\xb6\x92\xfe\x16\xcc\xa4\x8a\x9dA9C\x99\x08E\xc38\x9a\x0e\x10\n\xda\xb4\x1f\xbbb\x13\xd5\xc8\xc1\xaa\xf1\xa6rT\xb4\xaeR\x81m\x1e\x98\xcae8\x96r=\xe9\x0e$jb\n$\xcaf\x12\xac\x04nHa\xee\x7fA\xd4\xae\x1cVK\xa6GZp\x06\xa3\x14\x86\x95\xd1y\xc9\xdd\xc2W\xa4\x95\x81\x08\x01!\x0c\x84\xacKS`\x8c\x0b\x0c/\xc6v\xcb .vW\xb4e\xb3gk\x0fZr]\xb44\xe4X\x8c;\x97;M\x1b\xfb(\xfa\xa1\x1cVM\xd7SaK\x15\xd9\xbf!a\xd6\xd9\x83!)\xd4|>\xd6\x99\xc1\x11\x8d\x9a|\xe2VAVHP\x03\x0b\x94\xc0\xf2\xfeLt|\x05\xa5\xb7\xbc?cx\xac\xfd\x9f.\xef\xcf,\xef\xcfXIg\x8f\xdf\x18-d\x89\xe1\x18T\x1f.\x8ec4}\x98X\x8e\xd1\xcc\xa1\xe29FC\x0f\x1b\xd31\x9a?\x92\xb8\x8e\xc1\xd7c\xc4v\x0c&\x0e\x1c\xdf\x81\xe5\xfd\x19V\xd0|\x0f\xb9\xe2?`\xc4\x80 \xc0\xffjt9c<\x08\x90\x98\x10`q!8\x80q>/N\xa4\x11c\xe3\x89Y\xe7Z\xbc\x08\xbc|\xcd\x8c!i\xd4hD \x91c\xd4\xfd\x08{p |f\xaf;\xc4\x02\x10\x11f1\x03M\xe0\x086\x81\x8f\xb3\xc0h\x8b;\xe8\x04\xce\xc0\x13\xf8x\xc8(\x1dK\x10\n\x1c\x81(\xf0\xb1\x17(\"O@\nlA)\x00W`\n\x9c\xc1)\xf0\x1c\xfaR\xa3V\x08)#\x8e\x05\xb6X\x16\x04\n4OL\x0b\x1c\"\xc8\x16\xdb\x82\xa8\xf8\x168\xb5[\xce8\x17`\xb1.\xc8\x1b\xef\x82\xbc1/\x08\x8b{\x81\x1a\xf7\x08\xf6\xab\xabU\xa2\xae\xd4\xe3n\xf54\x9fz&G]\xb6\xa0\x1b\xb8\x03o\x90\x93\xb70\xef:\x12\xb4\xf0\x06\xe2 +\x9bQ>v/\xbbh\x0c\n\xe63l(\xb5\xf4\xa8\x9d\x95\x10\xd6\xa7\xe4\xd8\x81T?,z\xe0d#B\x9eab\xf3\x07\x15\xc2\xe4\x8d\x86\x85\xcc\xeb\xd8\x1e\xa62\x86\x01\xc1:\xa4I\xd6.\x1e\x16\x04\xdf\xden\x9d\xd4\x90/D\x08\xbe0!\xe4\xe72L\xa9!\x810\x08 \x1b\xc2\x01\x18\x8eRoN\xc6\xf1t\x18N~s\x87\x13\xc1\x11R\x84\x04f\xfcZ\xc0\x1eZ\x9c\x1c\x14\x96\x04@\xf65\x8fV\x9cc\xa4\xe0\xa1\xb4$\x9bE\xe7-P\xeb&\xeebzkV-\x80\xf7P\xa3\x86( C\xd4\x07\xb0y\xbc\xbd\n\xd1\x1a\x1e\x12\xce>\xa9\x7f\x88\xd8+s\xf5\xcfd@\xf4\xd5\xfcE\xa3\xa5\xa9\xa0\xf0~\xa7Z&\x9e\xde\x84\xea\xa7\xe4\xf1\x8a\x18\x9e\x18\xbe\xfd\xaa,~\x10\x11\x0c\x85\x8e\xd3t$\xdf\x11G\x90\xe1t\xd9l \x90O}[p\x1f\xed\xa6\xe4\xa9K8\xb5\xb2\xf7C,\x96\xb7\xf0\x96\xb7\xf0\x96\xb7\xf0b\xd2\xd9,o\xe1\xcd\xccM\xb3\xbc\x85\x17\x9ewfy\x0b\xcf\x97Ify\x0b\x8f\x95|Yb\xd6\xde\x0c1_\xc0[x\xbaI\x15\xf9,\xdeT \xde\xbf{\xcdI\x85&\xddH{ \x8f\xd7\xd5\x8d\x91\xbc\x99(x+\xfa!\xdf\xea\xc3\xf2d\xae\xb5\xb8\xe2Yq\xfa\x0df\xc5\xc4 \xf3\x96\xdc\xc6\x98[,i\xa8d\\\xfe\x02\xa9\x8cb\x93YYa\x08\xe5\xb1\xfa\x81\x0f>\xabx\xe42J\xc7\xc0\xa3\xdbO>\x08\x8a\x99\x95\x10~\x13\x11\xcd(-%S\x8eU\xfa\xd6\xe3\x94\xef0\xe5F:\xb3\x12ph\xf0\xe3zY F\xf7\xe2\xd8gV\\\x08hV\x028\x0e\x84\xfa\xfa\xd1\xd0\xac\xb81\xd1\xac\x04p\x95]\x8e\x0e\x944+.\xac4+\x01l\x07\n3\x007\xcd\x8a\x15=\xcd\n\xe3\xc8\x86\xa1f\xc5\x8d\xa4f\xc5\xb7\xf1dEU\xb3\x82b\xabY\xb1\"\xacY \x1f\x86\xc0\xfeD\x00\xb1\xad\xf4L\x806+nN\x92\xc1\xda\xb8\xdc\x8a[\x12 \xd9f\xc5\xa7\xca\x13\xe1\xdb\x06\x1d\xd5;\x87\x82\xb8YI\x85r\x1b\x84ph7+\xa9\x00o\x83\x90\x04\xf8\x0e\x85y\xb3\xa2!\xb7\xf5 \xef\x1b\x16\xad\xfa\x1cL\x95BP\xdf]\x8f\xce\x0bm\x81\xdf\xa6A\xc4Y1\xa0\xdc\x0f\xc6s\x08^\xca\nr\x850\x009+\x19\xd9\xb7\xe83\x94\x15\xf0A1=\xa4\xdc=\x96\xe0\xc9\x8f\xdfg\x89\x19\x88\xc6\xa6\xeb\xe4\xec\xfd\xc6\xc1\xa9\xde\xce\xa2\x130\x14\x02\x16\xc4X\xe2(\xc4\x889\x1a\xfa\xa5Vw \xda\xf9\x87v\\;+A\xcc&c\xdcQre\xed\x1d\x81\xd9\xe7\x1f;\xf6\x9d\x95\x00\xab\xcb\xb3\x98\x00\x87%\xa7\xa2\xe1Y\xf1b\xe2Y9 \xf7a\xfa\xdb\x82\xeff%\x08+\xcf\xca\x01:\xe2\xb0\x92S\x80\xf5^b.\x19\xd84\x1b\x84u\x1d\x19\xa0p\xfd\x16\xc0\xde\xcc\x91\x89\x13{\xa2\xa6\x03|\xdc\xd4n\x19x\xd8\x14\xfb\x13%\x12n\x85\x1a\xf4\x9c\xc1\xfcLF\xa9\xces\xc2n\x95\xc1f\xd0\xb9\xb0j@\x87{V?NY\xd5\xa41L\x0fb\xe0\x06\xf6f\xa3\xd7\x11s%Z;\xb4$\xe4\xf0u`\xae#\xc9\x95\x90CiU\xdb !\xfc\xaeq\xd4\xbb\xfe]\xfc\xfdbu\xfd\xae\x8d\x08\xa6`u\xf4\xb2\xae\xc6L\xa37M\xb5\xee\xf4\xc5\xf5\x84\x1e\x1eh\xb7\xc8\xfa\xeb$\xcbV\xe3\xc9Z\xdbN\x01\x9c\x88\x82 \xb7\x03\xea\x1cs\xc1)?\xb0[\xc1go`\xbcH\xca4&\xc0\xd9vWQhj\x07\xdd\xfa\xf6\xe4\x94\xc3\xe0\xca\xba'\xed\xa6Xag\xa9\x81\xc6\xbe\xa3X\xe2v\xb4@\x08;\x8f\x0d\xe2-[v\xe2yM\x8fQz\xd7\xbd\xd7\xa4\x0f#\x82I\x0d#\x17\xa1\xc7\xa8\x00\xfdJ\xfe\x1d;\xa1\x0d\x85f>'BrEU5\x1f96\xb6\xac%\x01k5\xe9\xdd1$\xf4\xca\xba\x8c_\x94\xb6^\x93\xb6\xce;V|\xa8\x16\xcf\x05iO(\x08`\xe6\xe5h\xd7\xd5\xe8\x00\x8cr\xb6k\xd1A\x97\xa2\x03\x84\xe1\xbe\x10\x1d}\x1d:\xf62t\xdcU\xe8\xb8\x8b\xd0\x91\xd7\xa0#.AG^\x81\x8e\xba\x00\x1d{\xfd9\xf6\xf2s\xd4\xd5\xe7\xb4\x8b\xcf~'\x19+\x99/=\x07_y>\xf0\x85\xe7\x87\xb9\xee|\xb8\xcb\xce\x8f|\xd5\xf9\x01/:?\xe85\xe7#\xb8\xe4|\xccW\x9c\x8f\xe6\x82\xf3c\\o\xf6]nv\x07-X\xf1\x85.X\xc9q\xad\xd9s\x03*\xf9J3\xf2\x88\x12H\xa8\x1a\xe3G\xff>sA\x0fR\xe2\xbe\xf38a\xa1ovP\x91;Rq\xd3Vu\xe4`\xca\x89Yh':\xc9\xed\xbec7\xa5\xa1\xa8\xef\xc5!\xad\x93\xae\x04\xa2!\xab\x9e\xddm,d\xbbO\x1cl\x18}\xf5tR\xd6\xb0\xaf\x07\xab\xdfr\xf1[pS\x95\xc3 \x11\x1a\x05\\n\xe0\x07\x87\x96/\x8b\xd5\n\xb5\xa2g\x98\xf4\x16;\xd3?J\xa7cg\xc5!d\xe8\x08\xedy\xb1\xefo\x9a\xb6\xfc\xcc\xb4PKV\xa4\xbc\x1b\xf4\xe1\xe6\x99m1io;\xc9W\xc7eg\x9b(4\x16pIg\xc1\xa5\xf5h1\xb7\x7f\xd32P\x15\xf80J,\x18\x811\x8cP\x12\x93\xe4\xbc/\xeau\xd1\xaee\xa5\xcb\xb5?\xbb!\xb8-\xda[\xd2\x8e\x7fC#\xc2-\x81n\xbf\xdb5\xed\xd0\xeax$\xa1\xfc\x9cp'r\xd1\xf7my\xb5\xef l\x8b{\xe1NFh\xadn\x8a\xfa\x9a\xde\x04\xa7}\xe2\xfaPl\x1a\xc3\x8c\\\x0d{\xb15VK\x0f\xdf\x97T\x0f]\xb6MU\xedw\xb6A8\\\xe0\xf6_\\%\x16U%\xbd##M\x1d*\xd8\xb2\xef&\xc7\x0c]\xfd\xa8@\x84\xa2Q\x08|\xd5 uC/\xfa\xa3\xc1s&\xf6\xaak\x80\xd4\xc5U\xc5\xcew\x14?\xc2\xf5\xf1\xdfi8\x991\xc4\xa9\xe1\xf7\xeb\x85\x07\x9a\xf7E\x15\xba[\x1c\xecf\xe4x7\x14\xda\xa6\x91\x1f\xc1\xa1V\x1c\xac\x9a\xaa\"+\x11|\x1d}\xaa\x1fk\xe38z\xc5A0-\x0b0\"\x0c\x8d\xe8\xf6yOO\x04\x03\xd5\xf1\xd35\xa2 \x8e\xc4If\xfa\x10\x93/\xd9\x8c\x12[\x9e\x9c\xb00<\xb5\x05\xbe\x9b1\x1e\xc6q\x14v\xf8n2Q\x81\x00\x88\xb5\x87\x99\x04\xe1!\xcc\xf8\x91\xd1\x98 \xcd\xb5n\xbf\xa5\x12\xb2\xe0\xd3\xae\xa7@\x83\xa0\x1f0\xefj&\x1d`\xbd\x90\x92i\xc6\xa2\xdb\x98\xd2\xb4GEt\x8e\xc7(\x00\x7f\x90\xc2h\xe2\xd0z\xc4\xdf\xc9\x10\x0d\xe3\xa3\xe1\xee\xa2\xf4\xbf\x84U\x94\xb5\xaf\x1a+\xaa\x862\xae\x8f \xe4\x94E\xea\xef\xfd\\\x15\xe6\xebR\xa8r\xf3 \xc6\xd1\x85\x84!\x8b\xea\x82_%\xfa\x98\xd7GU\x03\xb7,\x99\x8ai\xf1\x87\xe0\xe6\xd1\x0f\x19\xf6%S\xf1X<\x818\xefX\xf0\x8f\xe6\x04\xe3\x96L\xc5\xc11\x19\xa3J@p.6<\x17\x1b\xa0\x8b\x0e\xd1E\x05\xe9\xa2\xc3t\x91\x81\xba\xf8P]|\xb0.2\\\x97\x1a\xb0\x0b\xd3|\xacd\x0e\xdaE\x84\xed\x0e\x1e\xb8{\xa8\xd0\xdd!\x83w\x8f\x1e\xbe{\xd0\x00\xde\x03\x87\xf0\x8e\"\x88w\xdca\xbc#\n\xe4=N(\xcf\x1f\xcc\xf3\xb9\xb2E\xf1\xb9\xb4E\xc9\x11\xd2\xf3\xdbJ\xe9a=\x0b\xc1%S\xb1R\x96L\xc5\xe6\xc9g\xc9T<\x95\x80C\x83\x18\xe4l\x19v\x97L\xc5y\xe4\xb8d*^2\x15Ke\xc9T,\x15\x9f*_2\x15/\x99\x8a\x8f\xc9\x0b\xadGE\xa7\xcb\xa3\xd6X\x9bz\xfd\xd3\xb4\x1f$Y>H\"7\xa3=Op~\xfc\xdc\xc9\xba+\xca\x9c\x95}\x8b>CY\xf1F\xf0=\xa4\xdc=\xe6\xff<\x8e>K\xccL\xe7%\xe9o\x99\xfa\x9d#\xb3\x9cL+$\xf8\x17\xc4X\xe2(\xc4\x889)\xd2\xb7d*\xf6\x9e\x7f\x04\xcb3\xac.\xcfb\x82\xbc *VFI/\x99\x8aE\x89\xed\x88\xc3JF\x18K\xd3\xe621\x97\x0cl\x9a\x0d\xc2\xba~\x08\xe4\x16+YF&N\xec\x89\x9a\x0e\xf0qS\xbbed\xc9L\xb1?Q\"\xe1V\xa8A\xcf\x19\xcc\xcfd\x94\xea<'\xecV\x19l\x06\x9d\x0b\xab\x06t\xb8g\x97L\xc5Qyk\x97L\xc5v\x1a9\xec\xc9\xa0\xbe\x86*\xe3\x00R\xfe^%\x0ep|\xaf\x12\x15u\xc04\x90\xbb\xe8\xe2\xebq2\x15\xd30\xd4\xbd\x94+LKY\xac\xa71\xb3d\xa0\xc9\x8b\xa4c\x8d\xe8\xe3\x9e\xb8[\x1c,`\xf5\xcd$\xf9 \xf6\xc4X?\x01\xf8'\xbd\x8f\xc4o\xb6p\xb4\x1bJ\n\xb9\xbbvZu\x0d\xdc\xd6\xcd\xc7\x1a\x8aa\x02\xfc4\xec\xa4h$\xeb\x90\xfe\x19\xb7\x04&\xe0\x9e\x98)\xc2\xd9\xa7#\xf6dt\x1eJ\x8a\xa7}zB\x93\x8a\x94\xfd\x0dl\xca\xaa'-Y\xc3\xed\x9d\xd8Zz\xd2\x16}\xd3\x9aaj\x8e\x15C\xbb\xef\xec\x00\xaf(\x16\x9ab\xea\x08\xce\xd9\xb0U\xf7b\x91\xdd\xdb 8\x93+\xb5\xa1\xf1\xfef\xb3\xe1\x11v#W\xb7+X0\xf3\xfc\x97-\xa7G\xaf\xc6>\x0c\xf4\x02\x95\xcf8\xe7\xb9$Q\x927Ew\x93\x87\x9f\x81\x12e\xa6\x9e\xee-\x8e\x83h\xc5 \xdaV?D\xb5\xef\nB\xb0\xe05\x16w\x10\xcc=\x19\x1a\xa2I\xcf\x9e:\x82\xd2\xa24\xad.\xedU\xb3\xdd65m\x07\x87i\xb0\x1cL\x07\xed&k\x82\x05\xadY\xca\xb6V\xf2\x99\xd3$\xc2c\xc8z\x98\xa4\xb6\xab\x10\xac\xacnJr\xc7\xb2\x846\xfb~\xe8hS\xfbN4\xc6O\xac\xe6\xd1Y-\x0f\xb4\xc7\xfd\x17\xdf\xdfz\x91\xb5\x85\x0br\x9c\xaa\xec\xff\xdfL\x87%<\xf7\xa2(>\xdcU\x9e\x1d\xe5\x9c\xd2\x10|\x8b)1\x9d\xf7\x023\x8e\x88\xc2\xfb\xcc\x97NY_C\xb7\xa7)d\x9en\x8a\xb2\xda\xb7\xe4\xe9\xb0\xe9\xec\xd8[\x05\xf3\xc6$d\x8b9\xff\xf0s\x94\xae6k\xbd;=w\xdf\xedQ??\xff\xff\xcf\xdeE|\xfe\xd3\xe9\xd9\xcf!;Ol?\xe2\xf6\x1c\x0b\xf5\x94\x86A\xd9g`_w\x84\x9e\xf4B.\xed\x98\x82\xd79\x18\xfe\xa6\xa8e\"\xcdT\x9a\xbb\x9fM\xb6\xcd\xdef\x84\xa1M\x0d\x83\xa675\xfcMjj\xec\xc2p\x86\xeb\xca5?\x01\xb2FoK\xeaR\\\xb37f\xb6eG\x9f\xf6\xe1:\xb7iaM\xaa\xe2\x9e\xac'^c\x98\x1b\xa6\x88\xce\xdc\xf07\xab\x1c&\x07;\xca\xf1\xb0\x0eu\x90\x97(>\x85\xc1\x0c\xee\xdf\xd8\x1a'\xf5\xaa\xd8u\xfbjdAl\x1c\x1b\xba%2E\"\xf6C\x0bA\x87\x8d\xea\xe6\x85\xb5\xf5\xa4\xfb\x1a\xa6\xa7\x15\xe8\x1b\x10\xcdF\x88\x82\xbfl%8ilo\xa7\x007~\xc5\x87S:\"\x91\x95\xc7\xa2\x84\x8f\xe3\xa2\xb1\xce\x85\x10\xc8\xe4\xc0\xe4\xa7\xc7\xce\xb8w\x8c\xd2\xb3x6\x85\xe7W\xabc\x1f\xa5\xd3\xd1\xef\xd9l\xa85D\x87\xaa\xe8\xfbbu\xc3\xa8\x8f\x88\xfea\x8d\xa0@\\u\xe6\xf2\xf9E\xcfa\xd6[!\x8a\xc88\x07\\\"M\xcd\x0f\x9d\xfc\xcf\x1cS\xabvit\x06$\x85\x9b\xd5\xda\xe1qf[\x0c\xc6\"\xf4\xb4H\x81\xd3\xcf\x91q\xa2\x8e\xedXC\xc6|\x00\xac\xe1\xe0I\x8c\x0f\x12\x07\xd6\x9bs\xe3d\xc6\xaf]|?6\xca\x11\xe3$\x0d\x16\x13\xd4]v\n\xb3:[\x1e\xb2\xbf\x12+\xa2\xc3\xdc\xadA\xff\x94\xa5\xc79\x82\xd1\x12\xa9\x90p\xb3\x8b\xab#\xc6\x03:\xb8\xf6\x9e\xed|'\xbb\x05\x0e\x98\xc8}\x90\x9a\x0b\xc1\x9ay\x15\x1d\x1c\xa6#^\\Z\x06\xbd\x07\x0b\x1c0Z\xec\x89(\x13\x08\x81\x03rM2\x07\x0c\x88\x90\x882\xd1\x0c\x826\xeb:\xaf\xc1\xf6\xa80@\x95\x07\x9fE\xd7y2\xc5Ys\xc5\x19\x83\xf3 \x86_P\x9fC\xb4e\x00\x19o\x8f\xb3 \xc3\xf2\xf7\xde\x8e#3\x7fA\xe9\xe1\xe6s\xa8T\x8e\xc5\xb2D\xf8\x99g^\x06 \x87\xa1\x19\xd0\xaf\xd0\x1d\xc7K\xc8\x8fx\x9ci\xa5\xa2\xc4\xec\xd6\xeaJ\xc6i\xa3\x04\x15KV\xb3Z\xad]\x9am\xba\xea\xdd\x98\xb1Cg\xb4\x9dt\xae\xd2 \xdf\x10\x0e\xfb\x868\xe87<\xbaD\x92\xf7\x02\x8cX\xa0<\xc2\xf6\x05x|\xd9Xw\x8a@\x12i0q\x93NN\x8b<\xb8\xff\xb3T\xa8I.\xac\xa73'CZOg\x18\xf9\x01S&\x14N\xceB\x11v497Hs\x80\xc9\x95\x96\x19]\x07\xb0\\|\xc0Y\x1a7\x15\xe9\xa5F\x89\x1c\xfef\xa3\xf1\xea\xa6\xe69\x92\x9e\xcc\\5\xbb\xfb\xc9\xb4\xe2oZ\xea\x98\xdc\xa1q\xe3\xbdnt\x0bs<\x0bilS\xd6\x19\xe6\x9eW\xa3\x0f%\xe8\x15e\x98\xffx\xb4FM\x7fU\x19\x04O\x11\xcfI\x83\x03\x9b\x9e*\x96)C@\xb0`f?1\x1d\"\x1aV%\xd7\xa3\xd30\xe7\xe1i\x00\xf3\xf1ip\xb9e\x13\xc7B\xf3\xcd\x86>F=U\xcd\xf1 \xb5\xc4\x88y\xfa\x99\xd7\xb1\xe4\x07\xaa5r\xcc\x06\xc2\x1e\xa9\x06\xfbC\xd50\xb2\x8fe\xac\x1b\x0d\x14\xe47w\xdfR\x1f\xae\xd6\xc8\xac\xc6g\xac!\xec\xf1jp=`\x0d\x07\xee\xac\xbc\x1dy\xbb+>\xc6\xde7\x08\xec\xa8\xf5ak8PGg?t\x8dLY\xf7\x08j[/\x1c\xa8cs\x1f\xc0\xd6\xc8\xdd7\xfb6\xe4\x11l\xd6s\x98P\x13\xec\xff\xfd\xdcg\xb0\xe9}\xb0\xdf\xe8CC7\xe5\xee\xbd,E\xc3\xd00\x8d\x0c\xe3f\x0f\xa2\xdf\xbe\xe8'\xc9\x01v\x05M,\\\xca\x99\xd2\x14^\xa6\x0f\xc6$Vc\xfa/\xe9\xb7]\xd1\x16[\xd2\xd3\xe7\xa1\xf8xq\x16\x92L\xbb[r\x1f\xb8\xb3X\x8f\x8eJ/\xfe\xaf>\xcfo K\x06\xca\xafr\xb4\xa4\xdf\xb7\xdc*~W\\\x131#Oj\xf2\xa9\xbf\x1c>\xee\x1b\xb8\"\xd7\xc6\x04\xffc\x98\x94\x02\xd43|<\x08\x85\xc0\xb6\xe9z \x9bM\xb9*I\xddW\xf7'\xf0\xdb`T\xd0\x87\xa46\xd0l6,\xab\xf3\xc0\x86F\xb0\xbbi\xf6\xd5z0?\xb4\xb7\xc6X\xa5H\xa9\xec\xcd\xdcu\xee\xf5\xcfYc7\x8e\xf6[\x9a\xb7\x99\xff\x8d%3,jz\xb3\x92BvoH\xcd\x05\xa9Q\xd9\xd7\xc5]QV\xc5Ue\xe6(;\xa3\xd4+\x8a\xe7\x14\x02\x1ah\xd7\xb0\xa7\x00\xb5[\x12--\xbd SxU\xb9\xd5sq\xe6\x97\x1dmD\xe8\xb0\xbe\xe9\x8bJ:\x88 $\x18\x9dG\xca|\xebG\x9c\x98FoGa@\xba\xf86P\x91M\x0fd\xbb\xeb\xef\xa1\xe4Y\xde8(\x93\xe1\x97\xd8\x94f\x0d\x0d\x92\xbb\xba\xa78&(v;Y&4#\xde%\xe5\xf3\xf0\xfb\x8a\xd4\x18\xb0w\xf4\xe8E\xa9vO@K\xed\\\xf4\x92D\xe8\x87|\x90U\x82\\\xdb\xe9\xe2)XK\xc2t\xd4G\x81B\xbaa\x9c\x9cTWIZ\xccX\xde\x1f\xce:c\x04\xb4\xae\xd0\xd3\xc2\xb0c\x91U/\x16\xc5\xb4\x8a\x86\x85r\xc2\xe7|y]7\xad\x01L\x10\xabHo\xc6|m\xb0%w\xa4\xed\x1e\xc0f\xe5\x0d\xe9\x03UN\xb3\xb8h >\x935JC;\x0cU\x0dM\xbb&\xad\x99M\xf0\xbc\xacW\xe4\x15\xac\x9an\xdbt\xcf\xba\xf5-\xae\x07\xdb@\xec\x80\x1cO\xc7\xf6\xa5Q\xe3H\xe4\xa6\xf1?\x81\xb7\xdc\xc3\x8b\xcb\x07\xb5&&\xa3\x85\xbb9L\x9bE\xad7X.\x9c`\xb4\xfd\xc2v\xa5`\x03f\x8aZ\xa7\xe5\x0e\xd0\xb4\xa3\"\x95S\xf6\xcei\xb3\x19\xc17\x1d<\x19\x9a\xfa\x1a\xbd\x96\xcbs\x90\xab>\xf7c\xf3(\xa1W\xb9-\x1b\xc5\x9f\xce4g\x053\x08\xe1\x80F!\xd8F\x08\x9c\x01+\xc3@\x04\x9fs\xd9\x19c\xf0\xfb\x9c\xd3\x0dF\x84\xd8<\xab\x11\xe7\x0ek\xc7jL\x82\xc5\xa0\x84P)\"\xc6\x11\x04\xc91\x8f\x81 \x1e#\x13r\x1b\x9a`56\x0158\x017:\xe1\xe0\xf2M7B\x11b\xdc\x08C\x0dQ\x98e\x8c\"\xc44\xf3\x14\\&*8u,x\xf4,\x04Ir\x86\xc9j_\x8a\x16\xb3\x15\xe6\x9a\xae\x08\xbd\xb2F\xcdW\x98a\xc2b\xad0\xa3\xd6j\xc6\x02j\xca\x82\xcd\x9c\x85\x07\x18\xd8|\xe6-\x04\x99\xb8\xe07s\x01\xec\x97V\xd2\xcd]\x8d\xd0d\xfcj?`\x16p\x9ckh|w\x89\xfa\xde\xe5\x03\xd5\x84\x7fi6\xc2\"\xd1\xfd\x9c\x11\x16\x9a\xd8^\x83L\xb4d\xef\xd0\xb8\x89s=:\xee\xe7\xb0+:~\x0bI\x1a\x96\x13\xf6\xbbF\x84n\xf2\xde\x1d\xfe\x8c+M:\xa6T\x8b\"np-\xc5\xbaa\xe2\x02X\x8f\xf1q\x0e\x0e\xdb\x1c\x1cU\x85mG\x99\xf4\x92\xc1\xbf,(I\xf1\xe8\x93\x90\xdeY$\xfdS\xfaH=O\xb0\xd2\xc1\xbef\x06\xe6\x1a\x9aA\x0e\x1f\xcb\x8e\x8dc\xd8\x01-\xdc\xad\xac\xd6\xe4\xe4\x82\\\xcc\xbft\xd7\xa7\xeb\xf5k\x0e\x198\xdf\x91\xd5ECS\xde\x0f\xff\xf4\x9c\xd8\xf0^\x04Qtvj\"\xf4\x8d\x9dRL\xf7h\xa57E_\x9c\xd2\x9b\xac\x9en\xe9\xb3\xc8I\xc3\xe8\x08}\xa7mM\x15*\xbd\xb0^\xac\xa4\xf7\xcd\x18)\xf3\xf3N\x876\x9dm\xa5\xba\x8bc\xf6\xfc\xdb\xc4\xe5\xa0\x12\x85b\xf0s\xfb\x8f\xb2^\xffv\xfes\xb3*\xfa\xc6\xc7\xad\xa9\x1f+VO?\xe9\x07\xe9U\xcaw\xa0\xfe\xe0r\x12oj\x08$\xfa \x12R\xafwMY\xd3\x8dj \xb9\x86+\xd9\x92\xe6\x1c^\xee\xdb2\xae-^q\xa2\xbfoK\xe9;R\xaf\xda{\xba\x8c\"\xf6\x06N\x9a\xf6\xbc\x93HPm\xafg\xda\xb5[\x15o\xa6\xa7=\xf8\xc0q\x89\xd3\xc7\xa5\xc8\x94\x1f\xe9)\x7f\x97Mz\xb2\xae`\xad?\x91\xc8I/\xd8q\x1eL\x7fJ1\xcaAV\xa2\xfb\xb6\x04\x86\x18\xfa\xaa\x93\xab\xc8\xdc A8y\xba\xa3\xf3\xcf\xad>\x07\x1d\xa3T\xe3\xd4B\x95\xcb\x1bR\x91\x9e\xc8*\xea\xa7\xb6\xd9\xceV\xa0\x81dC\xb5\xa8\x87\x1cu\xe9\xf1\xcf\xd3:=\xc2R\xf3u\xd8 \x99\xde\xd9 4\x1b7\xac\x8b.C\xdaZt\xd9\x83\xe82\xcb\x0c\x8c[\x04c\xf5\xc8\x89\xcfp\xa0\xf3\xd6\xb2J\xc3\xaf\x85\xe5Z11\x06\xad\xc1\x8c\xda\xc8A0n\x18\x10B\x91\x032\xdb\x80u\x921-\xc2\xb6\xd9\xb2\x97\xab\xf2\x98\xb1R\xeb\x89\x96\xac\x8d\x82\x87u\xdd\x9e\xe5\x04c\x99\x9e7\x8f\x14\x12\xa1\x0b\x81=;\x96\xb2\x0e\xc6m=\xd32\xb0\xd3\x8b[\x05&\x9d\xe0E\xf0Ky\xdd\x16=\x99N\xb9\xa9=\xb2\x12\xc2\xe7\xd1\x96~>L$\xe1 \xa6\x13\x8a\xd3\x93\xa2\x81\x0e\xd6\x9bu\xb9\xb9_, \xa4\xad\xc5\x92x\x10K\xc22\x03C\x17\xafV=x\xcd\x9e\x93\xfe\x94M\xa6a\xc3I]\xb08\x15|\xb5v\xa4\x1f\xd6*{\xb1s\xf8\xc7z\xd0;t\xf5r\x82T\xe9\x0f\x82\xe4\xb3\x9c\xb2\xeb\xec\xc5\x07\xfa\xfa\xa7\xe4^K\xed\x88\x95\x10\xde\x17\xd1\x07\xbb\xda!\x14RLc\x7f-\xf3\xab\xd2\xdey\xd4\xd1\xbf\xda2\xed\xe4fj\x18\xcf\x15T\xfb\xe2\xc2\xaf\x9b\xc6\xe4\x0c\x005\x96\xa1\\HU\xb3q\xb1\x94\xa74\xd4\xd0\xb4\xca\xc2b\xaf\xfe&y\xf1\xdd\x17M\xad\xba)\xcc\x9bo8\xaf\xa3/\x90:\x13\x06 \xd7F\x83.\x8b\x1e\xbaW \x97@]\x97\xfb\x02.\xc5=p\xff\xec\xf7\xfd\xcc_4Z\x11\xd77}\x976\xad\xdd\x8c\xe9M\xe2\xb5\xccp\xbe#\x86'\x86\xef\xe8K\x96\x01\x83\xe8\xd9|\xfd\xfa6t\x1f\xb6S\n\xde\x92)\x89\xa0\xb3\xbd\xa9\xf7\xf8%\xa7\x085\xaf\xd6\x88\xd6\xef\xbc\xbeD\xd0\xd0\xe7\xb3\x94\xf8\xc8^\xe0D\x0b\xd3\x03#U\xab\x96V\xaf\xbb\x9d\xa0\x02;\x80\x02\xd6\x89;\x15\xed\xf45\xce\x9f-\x0bR6\x1e#\xf2L\xf9x\xb5\xe6:\xc9\xc6lT\xf6\x12'\xbb\xa9\xfaZ\xbbZ\xe9\xd3\xcb8\x0fc\xb2(\xf3\x0e\x9a[\xc9*\x15!&\xc9\x1d\x0eKA\xb9\xb3\xacmp\xc2,\xf1+\xd6\xe0\x92+Da-\xe5{\xb5\xe8jW.M\x9f\xd8\x04g\xcb\xce\x91\x99\xcb\xa0\xa5/>\xb62k[\xfcp\x00\x86c\xf4\x80\x9bq<\x7f\x85\x93_\x84\xa5\xe0\x15\xe6f&Ax\x083~s\x06\x13d\x88\xc1\x12\xeb\xbb\x97*E\xba,\xa5\x9a\xf3N\xa3\\i\xc4\x9eE\x91j\xd1\x96\x8aA\xee\xa1\xce\xa1*\xf3\x81\x1bG\xd8\xeef(y\xb5-\x9fu\x13y\x02\xd5F\xe1p\xe6O\xc4aS\xab\xe2`7\xf9\xec\x12\xc6\xbag(2\x1e,\x83{|(\x9b\xca\xd7\xd5(K\xcbI\xca\xd6\xb9T\x0b\xcc\xc9y\xe8\xae\x11\xcc\xb1\xc7\xf5\x06^\xeep\x17\\X\xc670\xa6Q\\\xae\xb7\x04;\xce\xedp\x03\xdf\xde\x19nx\x18\x82\x82\x14\x07\x1cB\xa3\x0b\xc9\xdd\x16\xe4\x88\x83\x07\xeem\x88\xae\x0c \xe3\xed\xabO\x8b\xc2C\xf7{\x86^\x858\xa7\x9d\xd9\xfc\\k5\xa0\x87\x89J\xc9$\xe4\xebK\xc2P\xc6\xf6\xc5o {I$;\xf72\xa0Dlt\xe2\x1c{!H\x05\xdb\x1d\xdb\x08\x8bY=\xed\xc7\xda\xcaFv\xa0\xbcN=\xfc\xc0?\xd3*I?\xe4;\x0f\xf8\x99\xb8\n\xd1\xcf\xe8q\xd4{\xa0\xcf\xc5\xe0\xdcC\xbc\xed\x00\x1ff\x99e8\xb8G\x0b\xe8A\x0f\xeb\x91\xf0\xa2\xa9N\xcaQ=\x1a\\d\xd38\xb8\xe5\xeaS;\x8a\xb9\x1a\xab{tj\xbecz\x06M\x84\xda\xabY\xd6\x95\xcf\x16\xc5zh\xd5P.{3'\xb7\x11\xba\n1-TR\xd2\xff\"Vf\x18\xd3\x16\x17\xb7\xa5iU\xabM\x7f\x9e\xfa\xa5Q\x0b\xed\xe5<\x95\x17e\xe5\x05\xf1\x13!\xe7`q\xfa\xf5b\xccP\x04\xeb\xcb\xf9\xf6\xda\x0c8#\xab\xcf2\xc1\xc5\xab\xcf1\xa5i\xb8\xeeT\xab\xccR\x9c\xbfK}F\xd7\xc5\x96\x0c\xb3\xf9]\xd1\x16\xdb\xf0 \xbc->]v\xe4zK\xea\xfe\xb2\"\xf5u\x7f\xa3\x0b\xbb\xac{rM\xa6\x9c;B\x83\xaaIV\xf8\x18l\x8bO\xe5v\xbf\x05Fj\xd0\x004\xfe\xca[\xa0\x99\xb3\xaa\xaa\xf9(\x1a/\xeb\xac\x8d\x97uL\xe3\xc5'\x1a/\xbe\xac\xc8\x1d\xa9\x8c<\x92Q-+SO\xc8`J&#\xb3\xd1\x8d|\x9c\x00\xbc\xfdTlw\x03\x81\x7fo\x9a\xe6\xe4\xaahO\xae\x8a\xcf\xff\x96h}\x14y\xeeDJ%Z\xf3r_\xb7d\x98\xca\xab\x9e\x07\xbd\x0d\xf6C\xf3s\xf3\x0e\xacIO\xda-\xbd\xd9Tn@&\xcf\x98\xbf%\xf7,{\x14m\x9f\x9d\xeb\xea\x06\xb9\xd9\xc2\xa6\xdf\x98=\x91\xd9\xa54\xf7\xd7\x8e\xfd\"&:%\xbbm\xd6\xfb\x8aXf2M`\xc3\xe8E\x9f\x85w\xd2*0x\xdc\x99\xb5b\xbd\x86n\x7f\xf5\x8cN\x0d\xc4\xf0\xc5D\x1724\xbcJ\xd0\xb8\xd0\xbc\x80?7\xcd\xed>>\xd7\xb2\x8a\xe5b\x15R\x92,\x8b\xf1\xabYm\xb1&;\xb8j\xf6\xf5\x1a\x8a\xeb\xc1\x1c\xea\xa1P3*\xf3\xea19\xfb\xd6\xd3\x15],y\xaf5co\x84\x86_\xb2\xf5-\xd9\xfa\xc0j\xef\xdb\x17]\xc4\xba\xe6\xb4\x9c\xab\x9bg\xb6\xfa\xb5\xd8\xfa\x94\xae\xc2(RM1\x86D\xea\xab_\xb9\x1a\x9dX\xb5\xf3\xf1\xba%EO\xde7M\x9f\xc0\x0d^y\xe4i\xe0G\xfd$\x94+\x96_\"\x81#\xb3\xa2!\xa1\xe9\x93Pn\xd8\x85\xf9\x04n\xcc\x8a\x067\xd3'A\xdc0Z\xab\xa6]\xcf\xda\x07p\x15O}\x04T\xa9\x0fu\xf8\xafQ\x9b\xbckC_\xe7\xdc\xd0y{\x1f\x13\xb6\xf2\xff\x00DSMreN\x87\xaeo\xf7+Jj\xcf\x15\xf6UY\xafy\x16\x8a\x9br7(\xaf\x82u\xef\xa6$m\xd1\xaen\xa8\x8e\x17\xc7\xfdUSUd%<\x8c\x85\xb8\xe0\xaf9\xb3\xba\xeb\x0d!\xdd0\xae\xaf\x8bj\xb5\xaf\x8a\x9e\\|\xfa\x89\x90.\xf6a\xa8\xfe\xd3%\xf5\x1c{F \xdd\xcf\x94I+\x08 \x85\xd7\xb7E\xdd\x15\xac#}\x03]\xb9\xa5\\\x8a\xbd\x83g\xd6\xbe\xbc*:r\xb9&u\xb3\xf5p`\xdf8MR\xc0s=\xd3\x86 \x0b\x04\xb0\x1f\xe8_\x07\xed{]t0\x88P\xa2Sn\x06C\x9aV@\xf2\x7f\xd77Ew#\xb8\xbf.\xba\xcbb\xfd\xfb\xbe\xeb\x87\xc3\x85\xce9\xdb\xa5\x0c\xd9m\xaa\xa6\xe8\xf5y\xa8\xecx*Y!I\xe9/\x9bb\xd57-7 \xb6\xfb\xaa/wUI&SJ\x0dk\x90\xae/\xb7EO\xa6\xd4\xcfW\xcc.\xe8?\x89\xf1\x10W2\x14\xe9\xe2Sj\xda\xc9l\xaf\x838\x82\xde\x8e\xf9\x1ai\x9c\x16\xebu\xc9L\xbc\xcb\x81d\x92\x9djDsl\xb1\x1cmb\xca4\x10\xfb\xa6\xd8\x0eVF\xe0\xe7.C\xf0uSJF-\xf4\xcd-\xa9Ef\x18\xca\x92\xb0l\x8b\xc1\x8c\xaey\xc3Z~\xf4_\x7f\xbbx\xfb\n.\x86 D\x7ff~G\xaa\xa4j8\xab{\x9e\xad\xa6\x1c\x8e\xc2\xfc\x88|C`\xb5\xef\xfaf\xcbGQ\xa17\xeaHIC^\xdd\xc3us\xdd\xec\xda\xa6oN\xf4\xa9\xad\x8d\x14KE>\xb1\xd3l`\xd5\x94\xb5H\x03.2\xcf\xd2~m\xbbkyuR\xbbm\x19\xee#\x1dn{\x9cv\x1a\xb7q\xf0\x99\x1d?M\x01\xfacM\xc8\x9a\xebem\xebP\x9er\x16\xb3\x02\xfe?\xa1\xbe\xbf\x96eP7=7$\xf8\xaf@_\xc1\xe5\xeaFz\xc0gS5|\x07\xd8\xb5\xe5J8\xfe\x98GPtK\xe8\xce\xf5\xe5u\x11\xba9\xeeQO\xb1Bi\xd4\xe9\xa3\x00\x86?\xda\xfbO\x89\xb9\x95s\xf09#H=\xff\xd2]\xffD\xc2\xb5\xf1\xb6\xbb\xbe\x1c>\xbb\xdc\xb7\x95SJ\xaa2x\xa5\x0bI\x996\xea\xb7\xf4\x1d\x95]q\x0feM_\x04\x1fV\xcb\x13\xb8*\xbarUT\xd5=\x14\xf0\x86n\xed\xc3\xe28\xa5b\x95_]f\x7f\x11o\xb1|&m\xf3\xb5\xc6f\xd0\xf1\x1f\xd1\x0c\x96\x85\x8ei\x85H\x83&\x83>\xc8\xab\x0dBuAKV\xe5\xaeD,\"\xa5\xeb\xe3W\x83\xc5Vv\x974\xd1YB4\x06I\x0d\xf9\x13\x19\x17\xc2\xaai\xe9S8\x1f\xd9\xcbZ}'\xa5k\xa3\xc7\x951\x0f\x9b\x90[\xd9\xc3\xaa\xa9\xbb\xb2\xebiH`\xd3\xec\xdbA'\xf4\x9d\xf8\xe0\xc5 \xad9\xa8!\xba\xba\xf6m\xf5\x14\xca\x13r\x02\xdf\xb0\xd70N\xae\x8a\xfa\xf6\xe4\xee\xc5\x15\xe9\x0b\x9e\xf8\xac\x1e_\xd9\xf9\xf6dt\x9fO\x13\x9c\xea\xb3'|v\x0e\xf6~}\xcf\x86x\xd4l\xdf\x9dL>\xadQr\\s\x0e\xab\x80\xf5\xe8\xdf\xb8P\xff-\xa8|\x7f\xc2\x1e\x05\x11\xf5\xcb\x0e\xd6dU\x15-{l\xec\x9e\x99\xbb\xc3\xef\xe4\x8e)jJ\x06\x18\x19\x11!\xd9\x10a^>y\xfe\xec\xc5\xf3\xa7\xcf\x9f?\xff\xda\xaaI\"\xc3\x92T)\x0f*\xf2\x92*e\xb7\x86\x18\xc6\xa9/\xea~\xb4\xf2G5\xcf6\x08\xfa\x84\x8b0\xf1\xa1\xbb)\x86\xa9\xcbN\x00t\x1dm;y\xc9N\xddZtBv\x9d@\xcfM\x97;\xd2^\xee\xbb\xf5\xe5\xb6to\x13\xbe\xcd\x94\xbb\x01\x07\x9a\xb0#-\xec\xbb5l\xcb\x8ant\xab\xa6\xbe\x1bF\xa9\xbe\xa6\x7f\x16g6N\x82\xfdJ\x11\x02\x1b\x12v\xe4\xe4mN5\xe9\x9a\x1bO\x98\xf2\xa9\x92\x8a\x9130\xf9+\xd2\xe2\xa4|\x0d\xe1\xa1Ri\x81\xd1M\xfd\xb4\xaa\x98\xde\x8b?I\x0d{\xf7\xe1mj\xdcB\x90Ia\xa6\xb5\xc5Z\xe0\xd5,\xce\xe2\x19\x96\x83A\x89[\x12N\xfb\x81\x15\x8b<\xc0 \x11\xb4\x9c5\xc0%\x14\xb0\x9e9<\xd5\xdc@\xca\x19\xba\xc5\xa04G\xc5\x18\xc4\xb8\xca1\xfe\x1e\xaayXAl\x12V\xac\x02\xf3\xd9'ru,vm\x8f^\xe3\x13w\xae\xdd\xc2\x8a\xd7zae\x9e\x0d\xc3J\xa2%\xc3J\x1e{\x86\x95\xd9V\x8dB\x8d[8\xaam\x03K(r E\xb2\xbf\x1f[(\x923\xaa\xbc\xdd4>\xc7ZT\xd5\xb0\xc8\xbf\xea\x98\"g>\x18\xf31\xe3\xad\xdb\xba\xf8\x12\xb1X\xd6\x83\x048\x06w\xe6\x81B\xa3\xc6\x1dU\xc8T\x8c\xbaG`1\x11\x1c;\xbd\xcd<\xb0Vq\x9b\x06\xb9\x0c\x83\x8cf\x01n\x14\xc4\x98\x04\x8e\x03 \xccS\x19\xe9\x87\x13\xf0\x1eP\xc0\xc5Z\xde\x83\xca\x81\x80c-\xf9X\xb4\xebA\xb5\x9cR\xd7\xe6\x1bR\x91\xeb\xa2\x0f\xd7*\xdc\xe6\xb8\xe4\x9e\xe1\xb4s\xa4\x1ah\xe7V\xcc\xa4\xcb\xbb=}\xdbd\xb3\xaf`\xcd\xf9\x1b\xf7\x18\x8e0\xcc\xd9\xbe\x81Yt\xb6\xcf\xfb\xcf\x7f\xa07\xc5\xb4\xf5n_\xce\xa2\xab|\x01\x8e/\xfd\xee;\xd2\xc2v\xdf\xf5pS\xdc\x0dk\xaem\xe9\xae<6/\x0cO\x89\xd6]Q\x95\xeb\xa2o\xda$\xfd\x1c>\xb1\x03\xdd&b\\\xd2\xe4\xc2\x87\xe0\xcf(\x17i\xbd\xdc\x91\xcb\xae/n\xc9\xa0\xf7V\xa4\xee\xcb\xca\x87\xec@\x17\xcaT[y\xe2\xfc\x8aT\xcdG*\xb6Q\x04_\x0dV\xf6G\xd2B[\xd4\xb7e}\x8d-\xa2\xb9L\xf1\xa1\x9b\xcb\x14>9T%%?\xea\xc1\xd4)\xff;G8\x0c\x1b\xe0`\xb4\x8d\xbb\x0f\xa9\xca\xeb\xf2\xaa\xac\xca^\xb8RVm\xd9\x93\xb6\xd4\x1f\x99\xd0\xd5\xe2E[\xd4\xddF\xbc\xd5r|j\xb1\xe7\xfc=\x96Z4\xdb\xcf\xa1\x16\xa7\xca\x93& \xe3\xdb \x93\x1e(Vm\xd3u\xd4\xba\x1egU'\xc7\x9b\x94\x18\xde\x86\xb40A`\xae\x08{\xad<\xf1b|f\xfd\xe0\x9a\xf5b\x0e\xea\xb3^\xefT\xbeY\xff\xcf\xe6x\x0d\x81\xbb\xe6\xf1\x8c\x00\xb5\xed\xa3\x9c\xe9\x03\x8bG;\xcb\xa5M\xf9r\xc4K\xf9\xeeD\xbaC\x10\x8aT\xdf5]9ld\x13\x18\xabe\x9b\xd0p\xb6\xd91pV\xdf0g?;4\x16\xc3\xc4\x9f\xa0X\x83\xf4\xe4N19\xd2\xe3\xe6\xaa\xa9\x99sMq\xe1\x15-\xbd\x00\xf0dC\xef}\xb0\x0d\xae\xa9\xab\xfb\xe9\xf3\xd1\x93\xc8l\x166@\x14\x9b8\xcaB\xa27\xfc\xfa\xf5 \x9c\x8a\xd7\x856\xd4\xc9-\x1c2t*P\xee\x8b-Q\xd8\xe8\xa0\xa9]\x90\xe8i]\xebZ\xe4\xaea\x0f1\xe5\xd3!\xaf\xab\xa2\xdc\x92\xf5{\xfa\x87w\xa4-\x9b\xf5\x1b\xd2\x17\xe2h\x17\xa0RV\x03\x85\xc1\x0e)\x1b\xf5\x1d\x88\x84 Bi\x01\xa35%HeX\x186 f\x91g\xa7L1\x9bV\xab\xfd\x96\xe3[\xe81Sj\x1b\xeb\x1a\x93\xd9+\x9c&\xfb\xb1\xb3\x11z\xdcu\xfc'\x0c\x8e\xe2\x0b\xc7:\x99AKr\xd1\x92\xeb\xa2\xa5O\xb5\x17\xf5\x04\x14\xfe\x8aO\x0dNm\xe81\x1bW\x86\xb0S\x06\xd6\xba\xa0~\xe9\xae)\x1b\xa7U\xc5\xf8\x88\xf7\x01\xb2\xf9\xceH^\xd2F\xf5Y\x9f7\xce\xb8`\xf7\xa6\x126\xfd\x8c\xae\xa9Z`\xc5\xe6!\x9b6U\xc5\xf5rE\x94\x19D\x1f\x9e\x1b~\xdd\xb5\xcdu[lG[\x85i\x9d5\x9d\xb8\x07\x8e0\xf39\xc69@\xf2\xaeXg\x81\xd3\xa9\xa7 \x87/\"\xde\n\x18ib\x1c\xd3\x9d\x7f\x81\xf7\n\x968q\xdc\x847\x88\xe5\x89\x13\xf3\xe9.\x86\x90\xef\x97\xc6\x04f\x05\x9b\xc6\xac\x18\x93Y\xae\x80\x0e\xbe{\xf8\x1d\xa6\x89\\\x9c\x93\x01|\x13\x9d\x15\xa7\xf9\xa2|\x88\x9a2\xca\x17\xf9\xd8\x895w\xe4\xe24}\xacmy\xcd \xa5\xa6k`\xc1;\xb8\xe0Z\xe1\xacxe\xe9Z\xed\xacxI\xf8V>+\xf9\xd6?+IZ\xc0BK\xdd\x0c-\x1f\xc5i\x04\x08\x12L\x92\xb9\x86\xd01\x0c8\xbf\xe9&\x17\x85Qe s\x15&Wp\xf5\x8aw\x84ms\xb4sSw\x94\x10\x10\x9b\xf3\xf6n\xf1\x0e}\xd5Y\xfb\xa3n\xa9\x16\xa3\x84\xb3\xcfS\xba\x15+>l\xe3\"m\x9b\xad\x85\x14\x82\x0f\xa6\xfd\xd1,Z{l\x8b\x8aq\xe8\x99\xdaXUq\x82j\xa3\x1d\xcf@\xe54\xa8S\xadi\xa79\x15t \xf3XIV\x15\xe1\xd0\xd4\xe1\xf6\x91\xcf:\xb2j\xd1%8\x1ec\x0b\xcd\x0f\x8eGXA6\x1b\x08\xb5\x80\x12\x8d\xdf\x00\xdb\xc7\xb3\xb7y\x0d\x8d@\xab\xc7g\xf3db#\xdd\xda \xb4uR-\x1d\x8f\x9d\xe3\xb3r\x9c6\x8e\xd7 \x14\xe0\n\x03\xccl6\xcd\xf5\x1f\xe0\xf6Oq\xf9[\xb7\xb7\x05\xac\x14\xb5\xbc\x0cb9\xc0J\xae\xaegu\xdf\xc7\xb9\xee\x03\xdd\xf68\xf7Y\xdd\xf5\xe1\xaez;\x9c\x9c\xc9\x8e\xb1\xf5\xa6\x1c\xa6\xe0\xd5\xbe\x8fq\xa8Z\x95T\xf2\xbe7LAM9 \xe9\x1fb\x9b\xbd\xa0\xef\x7f\x8a\x1b\xf9\xda\xa6\xcb\xbdg4]2\xfd\xe1\x8aTM}\xdd\xc14U\xe5\xdd\xb8\xbb\xdc5Mu\xb9i\xdaKY.\xaf\xec-w\xfb-M#RU\xb4\xf9\xeb\xb6\x18\x8cpE\x7f\xd1\xe6\xf1\xb9\xf8\x98\xbb\xbd\xdccG\x077e]T\xf4\x15\x8fU\xf1\x85\xf4,\xe1\x0e\x83g~\xe9\xb94\xc40\xf3\x15\xec\x94\x85\xb2\xc0H\xbdN\xcf \xae\xb0u\n\x9b\xaa\xb8\x9e,\xf4A\xc7\x94l\x0d(\x8bo\xe4\x8d/\x80\x9b\xa2\x03\xca\x85K\xcd9U\xcbSF\xaf\xec\xc4\x03\xba\xc0=\xd4\xa4\xa6\xcb\x90\xdc\x91\xf6\x9e1\xc1\xc9M\x02\xb1\x04\x83H{\xefl\xb2\xfb\xc7\xfd\xd9\x9b\xe8\x88 bt\\\xae\x0d\x1d\x89\x12\xc3 \x1aD\xb3\xc3\x93\x1c\xba\x13\x1e\x00\x1d5W\x97B\xac>E9\x98\xa7S\xc1e\x17>\xa2\xdb\x03\xd7\xb7\x90Q\xe7\xc2\x91\xf6\xdc~\x1a\xf1N\xd8\x80\xf9:G7\x83G?\x83UG\x83CO\xc3\x81t\xb5AW1\xaa\xe7\xebl](\x8a\xb0t\xffZ\xa4\xd2\xe6\xc9\xfd\x85\x95LsL\x8d\xfem'\x99\x19\xbbF\xf6\x1d\xe3\xcbt\x18\xc4\xec0\xde=&#\x1bsw\x9a\x84\xbd\xe6 \xbb\xcd\xf1\xfb!\xec{O\xde\xdd\xe7\xf8%1\xd33\x164\xaf\xe7\xeeH\xfe=\xc9\xb5+\xb9\xf7%\xbfGj\xc6fe\xd0\xd26\xaf\x00\x9fP\xae\x0d\x0c\xdb\xc2\x0c\x06~.\xbb^\xac}g\xeb|Bw4qa\xc9\x9c\x9d\x82\xe6A\xd2n\xb2}+\xe98\xbd\xe4\xdd\\\xf2nB\x9a\xc1\x86\x1ak\x15_$|\x9e\x86\x19m\x9d\xd3jCm\xc5S\xe6\x1d\x8d\xb6\xd9\xc2\xdeo3\x94\xf4\xf4\x8a\x1bW$\\\x8d\x89\xb8\xbcirp\xc3\x88\x7fp\xd9\xf5\x08\x0c5\xaf=xT\xa6\x18\xd3\xa7!\x96\x18~\xb1\\i3\xf5\x0e\xbfq \x8f\xb7~\xe4V\x07\xdb\xcf\x87\x19\xb3G\x8e\xdf\xbaAJ?\x13\xe3A\xab\xe2\xfdE\xf9#\xf5\x1e\xe9\xd23x\xfd\xf3\xe9\xd9/\x97\xe7\x17\xa7\x17\x1f\xce/?\xfcz\xfe\xee\xed\xeb\xb3\x9f\xce\xde\xbe\xf1\x7fJ\xff{\xfa\x8f\x9f\xdf\xfa>\x8d\xfb\xd0\xdf\xf4\xdb\xff\xf3\xee\xec\xbd\xf1\x19\x7f\xec\xeeUh\x8f\xb8\x1e\xa4\xea\xea\x9c\x89\x96\xefYt\x05\x8fq\"\x1axBF\xed\x80\xabN\xb1\xa7FE\xe4Zh.\xfb\x89i\xd5S\xa6\x9ed\xec\xbe\x14z\x12\xcb\x9b\xb5!\x87\x9b\x14R|\x99}e\xb3QQ+Ji\x7f1\x95\xc6\xb2\x98J:\x0f\xc7f*\xe1\xab*\xd8T\xd1|[\xd3\x02cg\xbd\xf1\x04\xa6\x98\x1c\xdb\xa2_\xddL\xeb\x9do\x0e\xee\x18\x89\x12}N\n\x89\xa8\x16\x85n\xbd\x04-\x9a\xecA\x87\xd3Z\xbcO1\xac\x84}]\xfe\xb1'\xd5=\x94\xeb\xe1\xf0\xb9\xb9G\xac\x12e\xae\xd2I\x16\xc6\x11\x9f\x90\xf41\xe2\xbe\x81\x1bR\xed\xd4f\x98\x80\x81K\xf8\xe4\xc9/\xc5\xa7\x8b\xa1\xce\xcf\xa4\xbe\xeeo\xfe\xfe\xe2\xfb\xe7\xf2\xb3*r/\xa2\x180V\xc4\xf9M\xd3\xf6\xd0\xed\xb7\xdb\xa2\xbd\xe7t\xaf\x86\x83\xf7\xc4\x94\xbe\x0e$\x16\xdfL|\x08F\x9fK\xefB\x0cetb\x92\xcbM\xdbl/\x0d\xa3\x19\\l+\xc3\xa5\xcc\xe2a\xae\x96k\xd2\xc1f_\xaf\xa7\xb7\x81d\xf3\x0evMS\xd9\x02D!\xd1\x10F\x8c\x83Z\x9a\x0dmj\x90\x0d{\x8e\x9fc7\x95\xa5q\xfc!\x91\x96l\x8b\xb2.\xebk\xe6\xb4\xbc*\xaaa\xbd;\x04aL\x99\x0b\xba*8\x15.\xfeIo\xf6\x8d4\xe4Plz\xd2B\xad\x9a\xd3\x1a=\x8a\x1de\x1b\xd2\xf1\x8b\x8f\x1f\x18\x12@\xd9\xd8\x84\x1a\x8e\x1cL\x80\xe2 ruO\x1f\xa5)W\xe5\xae\x18l\x19q0\xd9\x15\x9d~\xbePdz\xfc\x92\xdb\x16\x9f\xc4\xd2\xbb\xba\xc7\xd5\x80\"@\x81\x9b\x17k\x99\xa8x<\xfa\x07N\xe5\x0b\xe8<\x87\xf5\xb7MU5w\xa4\xf5\xcf\x1f\xed2\xe3\xf8\x180\xb3\x9c\xc7Cr\x03\x82\xe4\xf1\x0bA\xf1-wd\xd5\xd4\xeb\xd0} tG\xffu\xbad\xc1\xe8\xb3\xcdBE\xe4AUt\xaa\xd9)\xfc\x1c]_\xb4\xfde_nC\xf7u\xc1\xd7\xba\xe8\xc9\xb3\xa1^\x8cB(\xb7D\xb0\xa7l\"\xd0\xdd4\xfbj\x0d\x94\x1b\n\xd2\xeb>\x96\xd4fk\xe0\xfc\xe2\xf4\xfd\x85q\xd2\xa4\xe7I\xb9C\xe4\xd3\x8e\xacz2ypH\xbd~\xfc~\x95\xdd\xc8\xd8\xd0\x17R\xaf\x9fN/Z\xad\x8b\xbe\xa0\x17\x1b4re/l\xea\xfd\x0e\x1b3\xd1\xb3\xcbm\xf1)\x7f\xef\xec\x9d\xf9\xe5\xc3\xf9\xc5\xd0\x85\x13\xdb\x0c\x7f@\x91\x7f\x18d8\x98\xb2mM\x1f\xeeS\xde\xe7\x19&P\xdf\x16\xab[f\xf3\xf3\x1b\x1f\xf2\x82\xf8J\xdf\x92\xf9\xed\xfa\x81\x0b\xb9{\xc5\xaa\xdf\x17\xd5#\xce)\xa2/\x14\xba.\xd8l\xfa\xe9\xec\xd7\xb3\xf3\xffz\xfb\x86/\x068\xab\xcb\xbe\xa4\xd2\xe8\x88\xbeu\x16\x1d;\xa5\xda\xc6\xeepjI\xf5+R\xf7\xa6\xd0\xe5\xec-\xb6}-\xfb-\x80fn\xa3#\xe6\x88-\xcf\xe6\xef52)\x84\x1bP\xb5o\x87\xa9\xd6\xc1\x8bgl\xd9\x96\xf5\x9a|2\xaf\"O\xdb\xdc!\x85\xba5.\xd6\xa9\xc2\xd5\xe1\xcc\xf4\x8e\x0f\xb6[j\xfet\xa3!!\x1d\xe6\xb5C\xc5\x12\xd63\xccC\xfalP\xe9\x17o\x1dnD\xf1\xc5\xbb\xb7\xbf\xbe9\xfb\xf5?-\xbf\xe2\x1b\x83\xf8U,\x0e\xcb\xcf\x98\xbfs\xf4v\xba\xf9\xe3\x07\xcbs*\x1b\xc5\xbd\x89\xba\xf5\xa5\xba\xe4\xd3\xael\xd9\x15\xc7f\xb3\xe9\x88\x1f^\xcbJ\xe8\xfc0\x94\xc8\x7f\xb6\xc5\x8a\x88\xd9\xcdN&\xbaR\x17r\xa2\x0e-\xde\x9bq~i\xe4\x84\x85\xb1\xaf\xfb\xb22(\x91\x9a>\xd76\x10\x10\xd2\xd5\xeb\xeb\x9b\xf6\x1f\xe3\xa54\xd7U\xd4\xc3&\x143\xb3\x9cL\xc5A\x12\x16\x08\xb2\x8fSwc\x94\x86\xa9\xd5\x17\xa5~\x05\xd0\xe0)\x10\xfe\xd4 \x14\x18\xfd\xe9bX\xc7\x8c\x17}E\xcb\xee\xdeg\xf0\xdf\x1f\xde\xbe\xff\x9f\xcb\x8b\xffy\x87\xf9R\x95\x9fO\x7f\xfe\x19\xfb\xb3\xea\xffUk\xbc\xbe8\xfb\xe7[\xec\x97\xdf>\\\x9c_\x9cZ+*~\xe1\xd1\xe3\xeb`U\x19\xb1\x11ph\xeb\xdd+\xd8\xd7\xdd\x8e\xac\xca\x0dU2\xf7\xbbq\xf5\xe8\x1d~Ew\x15u\xf8;\xe6\"\xed\xd0:\\\x1a\xaf`\xc7\x83U\xda\xd4\xa1U\xff\x8e7G\xa5\xf5\n\xd8\xf1\x16\xad\x88\xd6\x93d95K\xb3\xe3\xd8 \xe1\xcc\x0b\xb1\xbf\x82MY\x97\xdd\xcdx\xd7\x0d\xe1B\x9e\xe2\xc3d3\x9c\xec:n\xbea\x90\xcd\xa8 =\x0b\xcc'm\xcd\x98\x8bz\xee]\x84\x03\xc1\xa0\xe7\x81\xffP\xf8\x9f\x93\xb3\xcc\x10@\xb57\xd1l \xbbT<\x140\x1a\x0c\x18\x08\x07tv@\x19\xc4\xd9\x90@/(\x10\xd9\xa4\xe6\xc2\x02\x9d\x86\xefQ\xdc\x1a \x01 \xfa\xcd\x9ed\xa0\xa0AI\x84t\xadP\xc1\xe3\x17\xa9\x0b8\x18&\xcad\xf0\xa0)\xce\xa2\xeb]\xf0\xc1\xe3\x97f\x00\x98\xd0r(\x8f\x86\x13~\x01\xc2\x08\x01\x17Z\xfc*\x89\xf0\xc2\xe3\x17\x8a\x1fl\x98\xc9\x8eH\x84\x1c\xfaA\x87A\xfc\xe1`\xaa\x00\x85\x92\x0f|\x88 \x19\x82\x01\x88\x8f\xd4\xc7$ \xa2\x1d\x8a\x18\x02F\xcc\xd7\xd38Hb\x10(1\x1fs\xc80\xa4\x82\x13\x0dB\x02\xach\x83'\x06\x02\x14\x0f\xda\xd9\xcc@E\x1c\xaa\xe8\x04+\x06u/F\xad%@\x16C@\x8b\x99\xf8D\xc6 \x11\xcdh.w\x04\xdd\x18\x8eo\xcc\xd4\xbfl(G\x14\xe7\xa85\x16\x8btt\xf6\xd1v\x1f\xdc\x87w\xf4!\x1e}\x98G/\xea\xd1\x8d{\x84P\xe4\xe3\x1c\xec\xa3\x17\xfd\x98i\xf6 \xab#3\nr>\x0e\x12\xb5\x1f|XH\xdc\xd5\xc4J\xfe'\xe6]\xa8H\x0fa\xf0\x12\x87\xf0\x10\xab\xc7L\x16%(pd9\x10\xa4 \x0d\x83\xc3\xad\x87\xe4\x7f\x06R2!\xf4\xea_gS\xc9\x86\x97\x84\x044^\xc0\xf4\x84\xa0)\n\xae\xd3\xdbT\x82\x068$\xda\x1aD*\x1a?\x195n\xb90\x94\xcb\xb8\xe9%\x11S\x19D;j\x88s\"+\x03\x90\x86\x90\x8e\xae\xcc\xdf\xf7\x9c\x18\xcb\xa0\xbe\x87\xf1\x96\x0fi\x19\x0d\xa0r\xa3-\x03\x96\xa4\x7f9\x1e\xf9N\xefA0\x1e\xfdN\xef\xe7\xff!v\xfaL@\xab\xccH\xcc\x80\xf9\x0bAs\x18\x8eoK \x1b\xa3|\xc8\xcc\x04\xd5bCg\x06\x0c\x8b\x7fH\x8e\\\xad8P\x8fG\xafR\xdc\xbc\xff)\xd4I4r3`\xceB\xd0\xbc\x85\xe3S%q8\xce \x82\xc1\xb3\xd2\x8f\xe6\xcc\x89\xe7\xcc\x83\xe8\xcc\x8e\xe9\xcc\x8d\xea\xf4\xe1:\x1d\xc8\xce\xd0\xd5\x9a\x15\xdd\x19\x86\xef\xd4X\x9b\x87\xf0\xd4\x88EbL@\xac\xaa\x88g\xe0\xc9U\xfcW\x00r|\x0e\xa3\xaa\xe6\x98\x89\x10\x87RV\x1d\x066\xdc\x8d\n\x9f\xd3\x8bp\x0c\xb82\xf5\x0e(\xcaT\x947\x96\x82\x16\xc1w{\x91\xdd\xd9:2\x17\xc1\x0d\xcak\x84\nv\xdb\x82\x16\xce\xb0\xfe\xd5\xdd\xdd\x8b\xd1v\xa3\xb3\x93\xf9I\x04^\x1b@\xeb\x10\x88u2\x93Y\x00\xd5\x1a\x94\x1a\x95B\x08\x88:\xd0k\xe3\x02\"\xdb\xc1\xd2v\x98\xb4\x03 m\x83F\x07\x80\xa2\xd3\xe0\xd0\x0e t\xf2\x18+\x0b:#\xe0y\x0e\xd4Y\xd9\xe4\\\xf0f\xd6\xeb\xbcw\xe8m\x00f\x0b\x19\x17)\x08C\x1bX\x0c&Q\xbc\xf1P\xd4\xec\x8b\x05\xf7\x06 \x0b\xf2\xf2\x99\x08B\x8eB\x11\xd8g\xfaTb!\xc7\x13@\x15%\x17\x02ZuL%\x08\x80\x028A\x00\x9eA\xf2\x05\xfe\x9d\xd5\xa3 \xc4A\xb2\x8f\x84\x0d\xffo\x96}<\x0c\xd8\xc3N\xd0\x00%\x80~\x19\xbc\x15%g\x85\xbc&\x00}s\xf4.\x01\xd6\x1b\xdf;\x1f'I ^;,\xc2\x0f\x88\xb0\x01w\xff\x0c;\x9c\x03\xd4zT;\x9c\x8b\xcfC\xedpq\xd884\x1d\x06\xc8\x90\xaf9 \xdb/S\x0d\xfb\xa4\x9d\x04\x9a\x9d\xb3\x941\xa0\xec\x9fa\x19[\x80\xa4G\xb5\x84m<~\x81\xcb7\n\xd4\xfae.\xddp\xa8\xea\xdc\x19\xe4\x06\xa6FCR%\xe0)JP\x1f\xbf 0j\x1a\x0c\x95\xf2\xe8\x9eT\x81\x00T\x07\xcc\x14\x05\x98\xfa\xd6I6P\xa9O\xe5*\x8c\xa4\x03I\x152\x11\x10R\x0bx\xd4\x8f)89\x9f\xdcp\xc6\x04Wa*6\xaf\x15\xee=\xc3=g\x16\xaf\x19\xe61\xf3x\xcb\x14II\xb8\x13\xe3k\x19pbIP\xa9\xf0?\xe5\xa5\xb4\xa4\x87\xd4\xeb\xf1\x1e\xbeb\xf1\x19\x01k\xf1\xd7C2X\x06\xd6\x1c\xb1'\x1co\xe2\xac\x17\xebR\xfc\x7f\x01\x00\x00\xff\xffPK\x07\x08I\xd4\xc29\xe2\xb5\x04\x00\xaf\xd78\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xd4`4t\xc7\x01\x00\x00\xbd\x01\x00\x00\x11\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00favicon-16x16.pngUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(6B\xc8\xd7\x7f\x04\x00\x00u\x04\x00\x00\x11\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x0f\x02\x00\x00favicon-32x32.pngUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xb9\xb1\xf1mT\x02\x00\x008\x05\x00\x00\n\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd6\x06\x00\x00index.htmlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(]\x12r 9\x03\x00\x00T \x00\x00\x14\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81k \x00\x00oauth2-redirect.htmlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(-\xe3\xb5\x97=9\x05\x00\xf7\x0c\x1b\x00\x14\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xef\x0c\x00\x00swagger-ui-bundle.jsUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(v\xf2\x8aA\x86\xba\x01\x00\xc5\x87\x08\x00\x1f\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81wF\x05\x00swagger-ui-standalone-preset.jsUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(_;\x94/\xe8Y\x00\x00\xa8X\x02\x00\x0e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81S\x01\x07\x00swagger-ui.cssUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(I\xd4\xc29\xe2\xb5\x04\x00\xaf\xd78\x00\x0c\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x80[\x07\x00swagger.yamlUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x08\x00\x08\x00E\x02\x00\x00\xa5\x11\x0c\x00\x00\x00" + data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00 \x00favicon-16x16.pngUT\x05\x00\x01\x80Cm8\x00\xbd\x01B\xfe\x89PNG\x0d\n\x1a\n\x00\x00\x00\x0dIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\x00\x00\x01\x84IDATx\x01\x95S\x03Luq\x1c\xfd\x8c\xf1\xc3\xec0\xa7)\xcda\xb6k6\xb2\x9b\xf9\xb2k\xc85/\xdb\x8dqx\xc6\x94m\xcc{\xef\x7fO\xff\xf3l\xdc\xed\xf2\xe0\xfe\xf8\xc9\xffP\x14\x11/\x14[\xa3P\xc4\xa1\xbc?\xf1t>7\x12s\x13\x03\x85\xca7IR a\xb5j\x8f\xa71\xbe]\x88\xf6\xb9L\xf0\x1c\x93\xcf\xda\xe3)\x10\x93f\x8d\xe4\x06\x13\xcf\xde<\x9b\xd14\x95\x8a\x92\x81OA\xcfF\x89\xdd<\x9b M\xe6}L\xe4\x07\x15\xc5\xf5\xe3\xffI\x0c{\xd6\x8d\xffs\x994\xbasfh\xae?\xafk\x1aprw\x10 <\xb9\xdb\xc7\x86\xa6\xd1\x19I\n\xa8\xb1\xd7\x84y3g\x171T$\xb5c\x7fq\xfbbq\xbfk\x8e'\x1dQ\xb0\xc2,\x92\x0bx|;F\xe5\xf0\xef\x00\x83\xf2\xa1\x1fx|?q\xbd\xcb\xc2\x16\x80ZF\xf0\xc4J\xf3\xe3\xe4n1\xcc\x17k`:}\xcby\xe8\x98\xcbB\xc7|6z\x97r\xd14\x9d\x06\xd3\xf9\x8a\xe4\x94\x90\x8b\xb6\xd9\x0cP\xebc@\xd0|\xbe*\xc94\xc8\xa7\x98'\xcdh\x00\xe3\xd92\xa6vK}\x0cB\xa4\xf0+D\n\xc7\x81)\xb0\x10\x9a\xe3\xa9\xd8\x8bx\xe4(\xa2\xbb\x8dl\x0d\x01\xb6\x8a-\xf378\xbe\xdd\xc7\xa6\xb6\xc9\xd9\xc6d\xd8\\m\xf4\x0c\x92 uQ\x0e\xd2\xf5\xb3\xd1\xf1w\xdfQ\x16\xb34a$\xa1\xc4\xc4(V\xbcF\xd9\xdf\xa4\x91\xe9\xb0&,\x12+\xcd\x93\xcf\x1c\x1cb\xdc\xca\x00qt\xeb\xcc-\x14\x89\xfe\xfc\x0fm2j\x88\xec\xccs\x18\x00\x00\x00\x00IEND\xaeB`\x82\x01\x00\x00\xff\xffPK\x07\x08\xd4`4t\xc7\x01\x00\x00\xbd\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00 \x00favicon-32x32.pngUT\x05\x00\x01\x80Cm8\x00u\x04\x8a\xfb\x89PNG\x0d\n\x1a\n\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x04|ID\xc4\xcf\xd0@\x04&%\xad\x1e\x16\x0f\xf7\x8d\x97AR\xfa\xca\xe7l\x87\x05\xf8\xd2\xfb\x0c\x84\x1d\x0dLVY\xdc/ju\x13\x1a\x88\xd2\xa0\xaaa\x82|nzp_\xf4\x03\xc8 \xd4;^\x8a9}\xeeu\x9a\x91 `\x04\x14s\xec\xe1\x0c\xc6]\xa3\x05``\xd1w\x12*~ \x00\xf3\xae\xd3\xa0\x9cb\x82\xa2bx(\xb3n\x1fqx\xd2\xf2\xda4\x1d\x8a}\x1ck\xd4>\x9cI+\xeb\xb3\xf4k\xc8u`L\x93\xf3]4\xb5\xd0\xc3\xe33\xd9\xee\xd7\xf2\xd9\x19\xea\x18\xc9\xc1Y:\x18\xfb(-\xadN\x82\x06e\xd5\x1f0\xa2\x1dV\xf8\xbe0\xc1\x985\x01\xf8\xd2~\\\xa6\xa5\xb5)&\xf6\x98V\x80l\xe4\x03\xf8\x03\x04\x00s\x9a^\xec\x85\x00\xf4+\x0b\x00\xe1:G\xf2p\x96\x0e\xc4,\xe46\x1e5\xbbP\xdd\x15J\x80}\xce\xa4\xe2\xc8{m\xa4\xe2\xc3\xc2\x01\x07\xc0\xdb\xa4\x18-\xa1\x931\xba\x10S\xfa%\xb6P`\x10\x19v\x99#|Gg\x9b \x10W\xf6\x8dI1\xba\x92\xd66\x17E\x12\xfa\xd9\xa8\xf3UTe\n\x1b\x95\x9d\x81f\xe5\x18\xa5umc\x81\x86\xa6\xeb\xec \x804\xcbg\x17\xa19\xfa\xc6\xf7<\xa3\xbd\xf2\x0e\x7f\x02\x80\x97Y\xc7\xac\x184$h\xa3v\xba! \xcc{\xcd\xb4!\xb1\xd8\x92%h\xe3\x93\xdc\xd3_\xda1\xe6\xaei\xcf\x83\xa6p\xbc$\xf0\xb2\xda\x94\xa2q\x14B@\x13\xdb\xff\xf3\xd7\x0d\xfaA\xb9\xc5n{\x8e\xd6Y\x08\x01u\xc1'~\x16\x8e\xe9\x04\xa2\xfbA+\xc74\x0c\x98\xab\xd7:\xfc0\xd1v\xaf$\xa2#\xb7\xf1\x08\xfdm!OXh8\x10j|g\xd1\xe0a\xb2\x99\x04\x9a[y\x9a\xbdk\xf24C$\xa0\x9e#\x9f\xa3\xa8\x001\xc6\x1a\"\xc0\xe4i\xa6\xcc0\xf3\xf7\xb7\xf5XE\xb8\xe0\xa1\xc9\xc2\x0c\x90\x83\x80$\x838\xdf\xd6\xe3\xd4\x82FNG\x0f\x876\x8a\xbf1\xa8d(\xa7@\x8cQX\x90\xdb\x19\x9f\xc5YG\xe9\x9e\x00\xa5y3]\x9aJ\xe1\"\x00\x00\x00\x00IEND\xaeB`\x82\x01\x00\x00\xff\xffPK\x07\x086B\xc8\xd7\x7f\x04\x00\x00u\x04\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00 \x00index.htmlUT\x05\x00\x01\x80Cm8\x9cT]k\xdc:\x10}\xdf_1Q\x1e\x92\\\"\xfb&\x81p\xf1\xb5\xfd\x90\xa6\xa5\x81\x94\x06\x92}(\xa5\x14\xd9\x1a{\xa7\x91\xa5E\x92\xf7#!\xff\xbdX\xf6\xae\xb7\xdd\x90BYX\x8f\xe7\x9c9\x1a\x1d\x8d\x9c\x1ep\x0e\x1f\x1f>\xddBe,8/<\x95 \xc9yKE\xeb\xc9h(Z-\x15B\xd1\x92\x92\xc0y>I\x0f\xae?\xbf{\xf8r\xf7\x1ef\xbeQ\xf9$\xed\x1e\xa0\x84\xae3\x86\x9a\xe5\x13\x80t\x86Bv\x01@\xda\xa0\x17P\xce\x84u\xe836}\xf8\xc0\xffc\x03\xe4\xc9+\xcc\xef\x97\xa2\xae\xd1\xc2\xf4&\x8d\xfbL\x8f*\xd2\x8f`Qe\xcc\xf9\xb5B7C\xf4\x0c\xfcz\x8e\x19\xf3\xb8\xf2q\xe9\x1c\x83\x99\xc5*c\xae\xd7\xe0-E!\xbb'A\xa5\xd1\x9bbjD\x8d\xf1\\\xd7\x9b\xeaJ,:\x9c_\x9c\xaf.\xce\xa3\x008zB\x97\xb1\x90a\x10\xff\x9d\xde\xd9\xe5\xea\xec\xf2\x17\xbd\x90\x19\xf5\xc2\xc6\xfa\x18\x82\x9bC\xf8<<\x01\n\xb3\xe2\x8e\x9eH\xd7 \x14\xc6J\xb4\xbc0\xab\xff\xb7\xb8Y\xa0\xad\x94Y&\xc0\x1b\xf3\xc4]i\x8dR\x85\xb0\x8e/\xd0z*\x85\xda\xe7\xf2u\x02=q\x83\xbdL\x86\xe0\x9f\xd3M\x90\x14X\x19\x8b\xe3\xbb\xa8<\xda7\xfb#=CK~O\xb40r\xbdW\xd8\x08[\x93N\xfe\x1d\xdb+D\xf9X[\xd3j\x99\xc0a%\xba\xdf(\xd5\xfd\xa7\xf1\xd6\xaf4\xee'\xac\x0b;\xf9\xc1OI\x0b \xb9;\x0e,OcI\x8b|2\x18^Z\x9a{p\xb6\xdc%\xf1~\xc6\xa3\x1f\x8e\xe5\xdd*\x81\x94\xbfY\xe1\xbc\xd0R(\xa3\x91\xcf-:\xf4o\x14\xf7/K\xd2\xd2,#\xa3\x95\x11\x122\xa8Z]v\x17\xec\xf8\x04\x9e7N\xc51\\\x85{&\xc0\xad\x9d\xc7f\xc8\x97F;\x0f-A\x06\xc3m\x99\xde\\\x85\x9e\x8fGG[\xab\x12`Q\xeb\x8c\xd8v\xfb_}K7\xd3F\xfe]\xb1\xa1\x82h%q{\x8b\x9b6\x88/\xc4i }\xc07u~}\xe5\xad\xfd\xc9\x98\xe7q\xd8_}o\xf1\x92%\x9dx\x15\x9f\xd3yO\xbdX]\x1aA\xc9>t\xd6o\x93\xd3\x92\xf2\x04l\xc5\x8d\x92jz\xc1jN\xd6\xf2\xa9\x87\xfa\xb5]\x05\xcc\xf9\x1acB\xa9,\x9f\xd0\x08\x05\xb7\x962\xec\xdb\xb6\xe2\x16b\xc6\xd5\x942H\x05KfI\x06\x7f\x9c\x98\xa8\xc0\xd5\x9c\xa2\x0c\x13\xa3\xe7U\x8e\xb55;'Nk\xe6\xd0\x9d;\xd4%^\x14\xbd\xd5\xf7\x92QN\x8e.\x1c`\x079m\xe3\x9e\x8a\xfe\xed\xa2\xad\xe0y>\xe6\xe23\xdc\xf8u\xa7=\xa3\xf6\xa1\x98\xb4\x17g\xa9\xf4\x1dA\xa8Z\xe4\xf6\x88_\xfc)\xf8\xd5N\xcf,\xea\xb4\xabS\xf2\xd2\xe0v\x10\x90\x82\xbd\xb3\xe1\xc1g\xc8>\x120\x0c{\x1d\xbd\x1c\xd1\x7fd\xb4\xbf\x82|\xf7\x9f\xd0\xa7\x1e\x82\xc5`H\xc0\x94F3p0$H.\x0f]v3\xaa\x9b\x1c\x83EW}\xba4\x12O`_\xb5!H5\xd1 \x9a\x0c\xaa\xcd\x04\x8cE\xe7M:\xe1\x08\xfe\xefQ\xab\x02\xfe\xb7A\xeb\xb6k\xbb\x05{\xef\x8e\xde\x84\xcb\x9c\xb2\x8f\x04\xd7U\xf9\x9aQ:\xbe\xf51\xf1\x1a\xaaW\x97uR\xdd\xe7\xf59\x974\xb7\xfc5s\xd0\xc4P\xdf\xdd\"\xd7\x96\xc2\xdab7x\xb8;\xfc\x01\xfa'\x00\x00\xff\xffPK\x07\x08]\x12r 9\x03\x00\x00T \x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00 \x00swagger-ui-bundle.jsUT\x05\x00\x01\x80Cm8\xec\xfdyw\xdb6\xf68\x8c\xff\xffy\x15\xd7\xfa\xf6\x9b!kZ\xb1\x9d\xa5\xad\x13\xc5\x93\xc5m\xb3g\xe2\xa4\xcb\xa8\x1a\x1fZ\x82,6\x14\xa8\x90\x90m\xb5\xf2\xef\xb5\xff\x0e.\x00\x12$\x01\x10r\xdc\x99\xf9<\xcf\xc3s\xdaX\\\xb0\\\\\\\xdc\xfdn\xc1tI\xc7,\xc9h@\"`!\xfc\xf9?\x00\x00\xbd\xec\xf4w2f=\x18\x0c\x80\xad\x16$\x9b\x02\xb9\\d9+\xe0\xd6-\xd3\xd3y6Y\xa6\x04\x0e\xe5\x1f}\xf5\xf6\x00X\x10\xc2\x01\xf4T7\xfaG\x132M(\xe1-\x8a\xbf\xfa\xf1|\x02\x87\xf2G0\x1c\xe1\x80\x0e\\\x839T\x7f\xf5\x8f/\xe2\xb33\x92\x7f|\xfedI'));&\xe6'\xffs\x15\xb0YRD\xd5\xf4\xd5\xd4s\xc2\x969\xd5\xc0\xa2\x1e\xf0\xeb<\xce\x81\xc1\x00\xfe\xbcz\xf0?\xe5M\xf5*\xd0 \xd7_\xe6W2\x85\x80\x0d\xf3Q\xa8\xda\xe5?\x14t\x1e\xd4^\xe5mg|t\xc3|\xc4\xbb\xa8=\xc4\xb6\x0e \x8fZw\xd3\x03\xd8\xdak\xdf\x96]\x1c\xc0\x9fW\xb5gW\xf5N\xe5\xa8\x08\x1f\xd58N\xd3 S\x83\x8b \x8b@\xfbEC\xfe3\x85\x01l\xedj\x0f\xca\xd6\xaand\x9b\xb4?\x87\x01\x90\x08h\x7f\xcc\xa7\xc5\xff\x98\xc0\xa0\x8ep\x11\xb4@F\xfb\x99\xc4\xc5\xf5\x1a\xde\xe2\xd2\xf7\x05J\xbc\xcb\xb3\x05\xc9\xd9J~\xd9\x86\xd08\xa3\xd3\xe4l\x99\xc7\xa7)\xb1\x80\x85.\xe7D=\xdfm??#\xec\x00\xf2:\xc4\xc2j\x8e|\x0e\xb46\x87\xe6\xe8\x15\x86 Z\x93\xfe\xc9 )^\xab\xbd\xd1\xc25\xfdR+\xc1\xe7\x1a/SV\x1f\x03\x1c\xf8}\xed\xb1\xd6\xb4? X\x04\xbd\xb8\xc7\x81\x1c\x01\xabO/k.Q\xb3;\xd9\x8c\\\x99E\x9e\xb1\x8c\xef\xca\xfe,.\xde^P\xb5F\x02\x9b\xf0\xfbz\xfb\x0b\x18@\xef\xf6$)X/\x02\x1a\xd0>'\x12w\xef\xde\x13\xaf]\x05\xc3\x06~P\xbd\xff\xde\xb2 P\xb0<\x19\xb3^59\x9d\xdc\xd0\xe0\x1b\xd5T\xd4D\xb5ZS\xf5\x8f\xbe\xbdw'\x0c\xbc\xbe3\x0f\x81\xe9+-\xb6\x08S+\xd9\x05PN#\xb6\x02\x02 -XL\xc7\x9c\xbe\xb10\x046\xcb\xb3\x0b\xa0\xe4\x02>\xac\x16\xe4(\xcf\xb3<\xe8=\x8d)\xcd\x18p\xe0B\x0c\xe34.\n\x88\x0b\x88\xcb\x1ezacG\xde\xcct\xaaG\x1c\xc1\xf3\x08)\x15\x0d\xf6\xef\xef\x87\xf5M\x94\xc0\x00\x82\x1c\x06\x90\x85|\x07\xe4\xf5\x1d\x90\xc3\x81\x01y%\x9cZ\x1bO\x1f\x8f\x01\x96M8\x96t\x98\x18\xc1\x8c\xafd9\x04|\x06|\x13\xef>\x00\n\x0f\x81\xf5SB\xcf\xd8\xec\x01\xd0\xedm\xd3G\xa0f\x8d\xc4\x99\x8e\x1e\x18\xdf\xc8\xfb\x15m\x81A\xfd\xe7z\xcd\x89\x11\xe4}\x9d@I4\xe9\x9d\xc7\xe9\x92\xf4 \xa1\x90s\x88\x05y\xff\"OX\xf9F\x18A\xb0\x1bA\xa2 \x10\xf2\xc9\xe5\xfdOd\xc5igk(\x0djo\xda\xb9%\x009.\x18\x08\xb0\xf6*E*\x16h\xdb\\\x1c\x04\xb9\xbc\xcf\xbf\xd6)H\xbd\xcf+\xbf\x1d\xa5\xef\xc4\xfaHJ\xc4\xa0\xc17\xf7\xef70\xadB,N\xca\xff\x9dX\x7f\xf7\xde\x7f\x0e\xe9\xad\x04\x84\xe8\x14\xe3=\x99\x92\x9c\xd0\xb1\"\x1b\x9c\xd7\x81Y\\\xd0\xbf18%\x84BB\x13\x96\xc4iR\x90 \xec@\xb1\\\x90<\x08kop\x12C&\xbd\xd0x\x86l1\x8e\xd3%c\xb65\x18@p\x9e%\x13\xd8\x85\x01\xe7\xd2\xe0\x10zK*N\xedI\x0f\x0e\x9a(\xcc\xe9\x1bg$+\xaep\xab\xe4\xed\xf8\xc7\x04\x0e\xf4s\xe9\xaf[R\x18@\x1cp\xec\xfa6l\xaci&\x1f\xdd\xb9\xfb]\xf3Q\"\x1f\xdd\xbd\x17\x86&>0n\xb3\x05\xea|6p\x05\xc4\x8d\x1e\xc4\xb6\xb9\xae\x87'\x16\x90\xdf\xba\x05t\x99\xa6\xb8\x92\xccr\xf6\x1cs,\xe1\x8ceN\x8a\x82\xcfs\xbe,\x18\x90\x84\xcdH\x0e\xa7D4\x90\xe5\xdaa\x14\x01?\xacz\xb0\xbd1v4\xd0\x8eT\x04\x88o5d@\xab\xd7\xf9\xe8k$\xca\xc8\x19\x16,_\x8eY\x96\x9b\xa0\x0d\x88\x0f\xe9\x92\x1c\x00i3\x85\xd0d\x1c\x0d\x8c%\xbf\x14\xdd6\xb3\x96\xd0fPw[/5\xc87'\xae\xf2PPk|\x88\xd3\xcfk\xc7\x01\x13\x92\xce\xc9 \xc2\xe0\xe4\x84\x1fT\x1b\xf2\x01\xb8\x1b*\xa0\xe7\xae\x83\xd6\xbc\xd5T+|\x85\x1e\xe7y\xbc\xd2x\xc3\"M\xc6D\xdb*\xa0o\x17f=\xae\xc5\xdc\xeb\x8b/\xf9\xceqNbV;\x99\xc20\xd2\xf1\xa4\xaf-9\xe7\xc7\x1b\xdb\xc8<\x14\x03C\x0f\xd5\xee\xc5}-6\xec\x8b\x80\x84^-\xe6\xce\x16\x97U\x8b\xbf\xfa\xb6\x989[,\xaa\x16_\xfa\xb6\x98t\xcf\xfa\xd6-\xd8J\xab\xa6\x7f\xf0m\xda@\n\xb5\xa6\xb7\x82-\xc1\x1c\x91\xe1t\xe4\xd7\xe0\xd2\xb7\xc1\x85g\x83\x85o\x83\x13\xcf\x06\xd3\xee\x15_\xaf\xb1[\xaf\xe6\xc6\xbe\xe3\x9b\xb5\xc6\xa7\xffbA.X7\x16d\xea\x8fD\xfcA\xfbI\xf1\x9c\x95\x9ck,\xee\xbc$+\xc2\xc5\xf5\xa5|\x81N\xc8%\xde(\xc4\x8d\xc7E\x91\x8d\x93\x98%\xe7\xfc\xa3T\xdc|\x9bOH\x8eo\x8d\xf9\x0d\xd5\x06\xef\xba_\xb5\xc0\x07\xd0?&\xfc\xbcJ\xda\xf4c\xca\x05\xc4\xbf\xff\xfd\xe4\xe4\xf9\xeb\xd7\x1f?<~\xf2\xea\xe8\xe4\xf9\x87\xa3\xf7\xf8\xc7\xc9\xdf\xff\xdekS\xd6E\xfb\x8b\x97G\xbf\x1e=\xb3\xbc>1t\xf0\xe6\xd9\xd1/\xd6\x0ff\xed\x0f\xde\xbe\x7fv\xf4\xde\xfa\xc19\x0c\xe0^\xfb\xf6\x1c\x06\xb0\x07\x0f\x1f\xc2\xb9A\xf1\x00\x03\x98\xc3\x0e\x18\x8e\x96\x15*\x9c\xda\xf7O\x8dZ\"\xa8\x8e\xb2\xad\xbd\xd6SC3'\xd7i\xc6F\xcb/\x9c\xd8J\xfa\xd8$g\xc4\xf6\"O\x92|dn\x91\xc8\xa3\xa1lp\xd7o;]\xf2\xd3\xcc\xf6\xf0\xd8q\x12q\xbee\xbd\x86\xdd\xb6\xf4W\x13*_\xc7l\xd6\x9f\xc7\x97\xfc\x90&R\xb2\x84\x1dT\xb4\xf0c\x88\xb3Tx8\x06\xa8O\x13Rh\x06\x0f\x81>\x80\x8c\x8b\x9f\xf90\x1b\xf1\xe3j\x98\xc160\x83\xac)A\x99{\xcd\xf6\xa9s94\x9e\x8c\xf4\x8b\xe4\x0f\x05S\xfcs\x80\x0cE\xc2\xe9\x02#\xc1cq\xba\xf2'^\x1d\x7f\xb2B\x12\x99P\xba\x9c\x9f\x92\xbc\xc6\x82\xba$o\x8a\xd0\x7f\xf4\xe8\x91 \xfc\xa0\x1a\xe5|&\x15\x1c,_\xa9\xbb\xfb\xdf\xdd\xfd\xee\xfe7\xfb\xdf\xdd\xc3\x19\xd2R\x05\xfb&~cn\x85/2m\xe3\xba\x0d|\x0c\x1e\xc2.\x1c\n o\x03\xab\xc9,\xe0\x00\xcec\x97\n\xaf\xc1\x14\xda\xdaxkb\xe2\x1aM\x05rm94\xe4Zs\xe8\x08\xa1\x1e\x1e\x0e`\x87\xe2\xc9^g\xce\x0d/3x\xc4\x01\xe85\xb0w\xd6\x95\x97\xa3z-G\xee\xb9a?\xf8\xb6\xc7\xfc\xda{\xed\x018}c\xc0!P\xce]\xcb\xc5\xd6\xf77\x83m \x9c\xf5n\x087\x9cC\x12\xef%\xa8di\x9d\xf4\xfa/\x8e\xdf\xcf9\x1dhS\xe6\xdf\xf9y\xd1\xbe\xfd\x06\x06\xb0\xdf\xbe\xfd\x9e\x9fR\x95tW\x19K\x8eW\xf3\xd3,\xe5\xeb(\xfe\xea\x8bM\x9d\x19\x8c \xcf\xc4I\xa7^0\x1cm\xaf`\x00\xef9\x8e<\xb3\x1d\x01\x1f\xcd4\x87\xcd\x92\xa2O\xc9%\xf3f\xc6?\xab\x95\xb2\xe8\xa8\x94\xc1\xa4Z(\xbe\x05\xf7j\xcb6\xe4\xdf;\xa8(\x1cB^\x9e!\x19\x1c \x91v\x9e\x86\x99Y\xb2\x9bd\xd4v\xe2z\xd2\xea\xef]T\xc19$\x81~\xcequJ\x9a\x96A\xfd\xe1\xe6>\xb7~\xf4ec\x9f\xb8\x19\x83\x866H\xb3\xf4!\xcexu\xf1\x93\xb9\x0be\x91\xe1C\xb5\"\x82\xd4!\x08\xa3\x85\xdf\x8c~tw'\x0e\xd3\xf7Hk\x87\xefG|\xcb\x90\xe1\xb3\x91a\x08\x0d\xb5\xcc@?\x13\xd5\xf0\xbcF\xf4\xb3\x07\x8c\xd5\xc9\xabCXp)^]\xbcpv\x81\x1a\xa0\xe6\x91\xa3\xb6cB\xd0 \xab\x84\xe8>\xcb\x8e\xc9g\xbc\xa5Z7\xb7\x0d\x1aP\x0b\"\xc5'\x93M\x18\x95X\xe4\x02\x181\xae4(M\xa9M\xbfut\xb9 cF&\x82A\x83,\x87DIE\xa27\xc8\xa6b\xcb\x15\x11\x7f\xfa \xa5\x1b\xf1\xe8\x00\xb5\\\xb6n\x8d\xab\xc8\xaf+_d\xfb\xf5\xcb\xe0\xdeg\x19\xcab\n\xe2r\x11\x96\xed\xb5 \xfdi\x9e\xcd\x8f(\xcbW\xe5\xcb\xc4w\x94/\xbfl\x94\x86\x81\x11} |\x9cR\x8aT\xb7\x96\xdec\xfb\xc19\xb6\xe0\xcb\x07\xa7F\x13\"4\x19\xdeo\x8cL\xff\xf5QSU\xb1\xec\x98\xe5 =s)\xdd\xb4\xc1\xf6\x86\xcf\xe5\x01=\xea\xd5{\x88\xe0c\xff\xe5\xd1\xaf\xc70\x80\xe7\xfc\xef\x9f\x1e\xbf\xfax\xc4\x7f\xfd\xce\x7f\x1d\xbd\xf9\xf0\xfe9\xfe|\x13\xd5\xfaOh\xc1Q\x1f\x06\xcdQe\xcb|Le\xf2\xd9\xb3M\xd3\xd8^\\\x7fQ\x11|''%\x00{|$\x7f\xf6\"\xe8]\xf5\x9cc\x1e\xc7\xe3\x19yO\x8a\x0e\xeb\xa8\xd6\xd5\x96\xe8\x0b?\xc4sOt-e\xbd\x8f\x14\x1fL\xf0\xfc\xd2\xdf\x1c\x88\x17+\xac\xef\xb3L\xc8\xb2a$\x1eI\xc1Q\xfbH\x9e-\xf2\x05\xd74\xca\xfe\xbb\xac\x18\xdaDR\"\xbdx\x04\xa3\xd8\xd2\x01\x98{\xc8\xf2\x0d\xba\x18wv\xc1\x82_#x\x11F\xf0km\xf1\x15\xbd\xf5\\\x133\xa6\xbf\x14-\xbf\xf4\xc7\xf4\x97\x0eL\x7fY\x1b`EI=\x9b6\x0d\xf1\xe5\x0d#\xfc\x90#\xfc\xa8\x8d\xf0/o\x18S\xf6\xbcz\xf8\"Liw\xc1\x82\x1f\xc4z\xfe\xe0\xbf\x9e?8\xd6\xf3\x87\x06\xe5b_\xb6\x96/\xfaI!Z\xc8\x08\xff\xa5\xb4\xb7\x1c\xbd\xa5\xba\x96\x8f_S\xe4\xbelko\xbf\x8a\xe0\x9f\x11\xfc\x12\xc1?\xdaJ\xd3\xe3\xa3\x7f\xa0\xc2\xd4&9\x12\xe2\x10\x1dOb\xe4\xca\xd0\xa3L'6\x1b\xb1\xaf\xcc\xd2\x83\xe2/\xa5q\xe9\x13Y\x15F\x1eR\x8cDr\x83\xd5PN\xf8\x07\xc2\xc7\xadF\x077\x19\x1auN>\xa9\xf4\xf3\x96\xf9\xa3\x80\xe1\xaf\xa0\xcb\xbb\xbb\x93\x86\xb3\xa8q\xef\xa9<\x0c\x86#\xaf\x8e2KG\xea,\xaa\x0c\x18\xff\xf04\xb0 7fm\xf0+\xdeZ\xf0\x95\xd4\xb5\x12\x12\x0cG\xa1_\xbbq\x07r\x08\xa3fR\x883\x0fy@\xd9\x05 \xdb\\\xf3\x93\xea\x8d\xdc\xfc\xc6\x1f\xd5\x1b\xd4\xfc\x86Q\xca9\xac\x84\x9cR\xf5d\x16*\xbfL\xd2\x19~\x8a\xe0|\x04\xfc\xb8O6\x92x6\x92Y\x97\x1d@/\xcc\xc2\xdc\x97OO\x08r74\x8b\xc2\x8d\xe4?7\xb0\xc5\x80\x1e\x06|(W\xd7k\x08)\xf1T\x97\x11\xc9\x9a\x99\x81\x9a\xd9D\xf0\xd2\xca\x91\xf0\x03\xa2\xb2l\xecE\x10\x0b3F\x0c\x0f\x07\x90<\x80\xd8\xeeF\x07r\x1cK\xde\xc6\x90r\xd1\nv \xe6\xb2\x95\xc5\xad\x0e\xd4b\x0b\xbd\x1e\x0b\x96\xc3\xbdQ\x84\x8a\xbb\xe5pw\xc4\xbf\x8c\x80\x84\xa5\xa6$\x86mh+\xe1\xa0%~\xa9K}\xd6zhU\xfb\x936\xab\x8c\x9et~Df\xfc\x17/\x93q\x85\xac\x90\x15+\xe7\x02\x0c\xc7\xc6\x8f\x81\x93\xa5P\x97r\xfe\xf0_X\x05\xfc\xedmx\x04 \x1c:\x1a\x07?u\xa7\xba\xacjOu]\xc1\x01|F\x07F.\xcaKL\x12\xe8L\x86{\x8d\x93\xa8\xfc\xa8}\xdb\x03M\xb2\xfc\x1ax2\xb5;\xb1*\xca\xa4y\x94\x0b_L\x8eR\x11XQ\x83\xe3M\xfd\x0c\xa3\xd5\xbe\x91\xba\xcf\x0c\x9bx\x19\xd0\xb0?\x8f\x17\xd5\xba\xbb\xda\x05m\xd2\x08Q\x0c\x1d\xa06\x10:Ts\x13b\x1d\xd2\xaf\xff\x81!\xa9-\xd0^t\xb4\xeaD\xd0\xeb\x99|\xcd\xf8\xd5\xeb5=\xf7\xf0;N\xd3\x17\xde*\xab\x85\xfbT1\xf0#/9\x1b\xc1\xa1\xb4 \\:\x7f\x95\x14\"\nfB\xc4\xf3_\xeb\xcf_\xc7\x0b\xa1\xbb\xf2\x1a\xce\xc4=\x1ce=\xae\xf9]\x0d\x14O\xdd\xd4\xaa\xe9\xaf\xf9Acf\xdf\x11\x1cwHe\xbe$\xb0%\xf5\xef\x0c-\xcc%Fm\xd9\x18%\xc1\x82j/\xeem\xa0\xa6\x97N\x08o\xa7V#\x06So\xb8\xb6f \xb8y\xf9f\x10\x868\xa1\x00=\x0f\xf4\xbb\x9bN\x10\xec\x93\xf4\xa7f[f\xc7Q\xd2'\x9f\x97qZ\xa0J\xde\xf4\x02\xd3^\xd8Ro\x07\xcc\x93#?\xf7Z\xf2\xee\xe5\x8d\x03\x11M\xa4\xd9\xb5+\x87\x07\xed&+o\xca\xc7\xda\xcd\xe6\xe7''\xb3\xb8\x98\xb5\x1a\xa8n\x97\xaf\xd4\x1e\xac\xd7B\x7f\xcco.\xe5\xb0\nu\xa3\x907\xc6\xea\xc6\x18=\xa5;\x90\xb2\xe9\xc1!\x0d\xd1\xf8\xdb \x1b\xe5Z\x81\x9e}\xe6\xb6\xf9H\\\xac\x06J\x88})#\x04\x1d\xe6\x8f>9'\xf9*\xe8T\xa8\xa8K\xb1B9\xda\x00\x83P\xec\x82Nv\"\xe3@\x98\x91 CNQ8/\x06\x94\xc3\x15o\xeeb\\\xa1\xed(\x00\xf4\xdf\x97\xfdq.\xc2c\x8f\xa8q\xda\x16\xa8\xe5gc\xee\xbc\xf1\xaaZ@\x0b\xcd\xd1\xd5\xbe\x88m\xda\x0d\xdbB\x90\xb4 \x0exg\x0d\x0f\xf9\xe6\xa5xK\xc7\x12\x10\xa9\x05\x81\x01$f\x08\x1b\xa17\x15\xc10\xc6/\x16 \xb6\x8frE*\xd1\xc7\x14<\xa8_\x1c\x9e\x9c\x13\xdd\xc2\xd8\xb4\x00\x9d\xa43\xfe{\x86<\x01\xe9\x9f\x11\xf4\x8a\\\x85\xfc \xbf\xab\xddB\x1cQ\x185\x95\x1ek\x06\x8a \x885V\xf1q\xaa\x11\x13\xbe\xa8\x0b/\xba7w\xd3\xbd-T4\xea\xf1bsM\x02\xe2\x1c\xbbj\xc0\x8c\x8fB\x9f\xa3\xbc\x1e\x1a\xfa\xa4\x86/\xcb\x1e\xdc\x86\xdd\xd2\x9fE\xfa\xbd\x84\x91zC}\xe8:\xd8\xfeY\x0e\xed\x9ff\xc4\xf9\xa7\xb4\x19tl5\x1b\xb4\xce:\xa0U\x8b\x8c\x11*\x02O_\xa1\x15q9\x0b\x99\x97b\xd5X\n\xad\x0d\xf3j\x9c\x91@\xbaZE\xa0\xe2\xfb\nF\x16\x10\xc3\xfb\x98\x9e\x118]\xc1n/\x8cpo\xe19\xb4\x1b\xd5W \x0d5\xe8[z\x1bv\xc3\x08i\xba\xf6\x02\xc5e\x94K\x18\x9f\x16\xe8z\xc8\xe0\xa1\xe4\xd8\xf8\xdb;T\x99pN\n\x16\xe75\xdd&\xa1\x13M\xb5y\x82C\xc3\xc1\xeaX\xa3\xa3\x07\xfe=&I\x1a\x04\x0cv8\x01\xbe\x0d\x94\x8bV!\x97\xcd7\xc3\x9d_JX\xfeb\xc6\x9d_\xbe\x0cwN\xcd\xbaD\x81/\x9aJ\xe9\xf1i\xc1\xf2x\xcc\x9a\x96 K\xb3'\xc4\xe5fz\xe1|z$\x9f\xea\x0f53\xd6\xf0\x1f#\x15`\x1a\x10\x12\xc1K\x8e\x19z\xdc\xc3\x19\xe9\x0c\x04\x82\x86\x15\x86\x93G\x94\x0f4M\xfb\xf0\x932g\x84\xa3\xb6gc\xa3\xcf\x8dL25\x7fY\xadG\xe9![S-U\x1e\xb2\x03\xc8\x85\x8b\xac\x15W\xa4\x8a\x88\x04t\xc80\xecn\x07=\xba\xb2\x11\n\x7f\xbc\xa3jgf\x1c\x15\xadT;\xf3\x9a\xac\x9fu\xc84Q\xe3\x14Z\x937\xbe\x95\x9956\x9bikJ \xaa7\xbd\\M\xa8/\xf4\xc3CbD\xf9Z\xdf\xb3\xb8p&\x02\x80\xa6\xa5S4\xdd\x08\x93o\xa9\x02\x1a\xbd|\xe9\xc6\x12\x9d\x8a\x9dU\x99\xaa\"\xc9V\xeb;-\x11;-\xe1;-{\x00\x89;\x16:\xe6\xdf\xe3bf\xb0\x03 \x1c@b\xd1\xf35vf<\x8a n\xee\xc6\xc4\xa8\xb4\xb5\n\xa3\x89\x17\xc8\xae\xb3=%\xb8\xac\xfbS\x03\xa1uw\xe6\x9d{8\xb9\x89=\xbc\xd9*(\xc8\xa1\xa65\xfb\xf7\xed\xf9\x98\xef\xf9\xd8o\x8fk\x8b8\x9cU\x87\x1c\x95\x87\x1c5\xee\x8b\xd2[\xc5c\xad\x91\xf7\x0dk\xbb\xb2&4iB\x86\x85{V\xd8\xf2SP7\xcb\x86v\x94\xb1\xe8$\x9e\x04\xd4\"\x83\x96\xbb8{\x00[\x01F\x9cKyT\x08\xa4\x18\x8b\xb7'\xb4\x10A&d\xe2\x08\xf2\xedm\xb9\xab\x1e\xd8\xa5\x91\xbc s#L+}\xf5\x8d\x025\xcb7\x86\xaaE\x9d\xf3D\xd7\x12\x8b\xed\xf2\xbd\xa5Y\xcb\nl\xbe\xd5\x98\xb6\x0e\x1dZ\x0e\\$\xe1\x8c\x8e{@,\x8dX(\xaf\x8d\x10\xe4\x12\xe5\xf3\xff\x02\x94\xaf\x0e\x15\xfd\x14)C\x08D\xca\xa2\xb6\x83\x80~\xa0\x94\xc6\xa8\x07\x1e\xcc[6LF\x11'T\xadC\xc226\xbeK\xa8\xa6%\x12\xbb\xe4A\x17\xdd\xa4.m\x12\x9a\xd8\x86\xc9H\x84C\x96c\x8b\xeb\x03;\xcdI\xfc\xa9\xbd\xa06lk\x1d[\xc6\xe5\xfd\x8f\xed\xbe\xc6\xc2Z \x9ai\xb1\x8d/\xdf\x08\xab\x8a+\x01\x8f\xaac\xb5Ka\xd8\xbdQA\xc1\x0d\x11\xa5\x02\x9eC\xb1(\x82\xf2\xe4\x1e6\xbe\xe6\xb4.+\xf67\x1f\xfa3\xbcsI\x03\xe6\xe4\xfa.v\x0dA\x1b\x0e\xa1\xf7\x9e,H\xcc`8\xea\xc1A\xf5\x0b\xbd \x98\xa6\x16\xda\x86^u\x0f\xbf\xe5wX2'\x05\xb4\x9d\x8e\xe7\xd7g\xcaML\xb8\x18\x82\x81\x01\xaf\xf5\x93\xd0q\xba\x9c\x10o.|Ft\xc5W;*\xab\xd1<\xa6,\xf0\x99Hm\xffpPYQ^\x8b\xd9\x13S\x85\x03\xa5\xad\xab\x8d\xec\x83\xb0\x13\xc3\x8e\x08\xa6k2\n\xcd\x91\xe6\xe4\x9c\xe4\xc5&n\xda\x1dp\x9d\x90\xcb\xb7\xd3\xeb\x83\x15\x0eQc\xb8\xb3\xe7\xec&\x8d\x0b\xf6\xfc\x06\xba\xaa0\xb4\xb3\xcb\xeb\x0bS*UT\xb9\xc4\x98+\xcaJ\xb0\xca\x03\xa36\\\xda<\xd1\xa8S A\xbd\xe6\xb2\xb9\x94\xb3\x11\xab\xba\x19\xb1Vl&<\x04\xaa(N\xc5\x02Q \x89\xd0\x98\xf0F]7\"~xP\xd8\x1a4\xa5\x91\xd2\x13\x0fI]\xf5\x0e\x87m\xcc\xd4\xa6z\xde\xb6\xf7s\xfa\xbe\x92\xf4}u\xc3\xf4\x1dU\xc6\x8a\xbc\x8b\x1f\x1au\x17\xda\xddm\xe8\xf5\xfb\xfd\xea.\xa1\x13\xd8\x86@\x08\x15\xeaE\xb2\xe0\xed\xc1\xe9\xaa\xf69Y\xf0\x86{!\x9e\x07\xed\x93`u\xb3'\x81\x1an\xa5\x8b\x84\xaf\xebCi\x9d\x11\xabk\x9d\x11\x8as\x08\x08\xec\xe8}\x87p[\xeb\xcf\xba?0@zW\x18\xe452!n\xf05B\x9d\xf84\xcd\x0c\xb6\x87\xc6\x90\xbd\xcf\x9d\xc6\xa1Rv\xaa\x1d.\xe8R \x02\xb2\xcb\xa7\x91\xb0\x15\xe0\x19S\xdd\x0d\xe1\xe1\xa0\xf4-]\x91`7\x82\xddP\x1eO+\x89\xdcg\x84\x05\xbaU@\x99\x0c\xf8}f\xb8\x8f k\x9f]\xab\xeb\x1c6\xe7eTemy,\xf6-\xf8\xbf:\x92\x0c\x06|.vi@d\x17p\xaf3\x94\xf6D\xb5\xd0\xb5\xf3 4\x13mp\x89\x03\xed\xc3j\xf5\x85\xe7#\x0eGB\xd4@sV7s\x16V\xd8\x8dz\xc3J$\xe0\x90\x93\xf2`k\x03S\xf8\x1a\xf3\xe0iw\xeb*G\xeaT9\xd6%\xc4\x08\x12\xa3\x06\xd1\xbcl\x19l\x8b\x11\xed\xf0\x01\xe4\xfe\x0b\xd4\x92\xd7\x8c\x00\xdc\xfc\x00\xae\x80g\x1co\x03\xa0\x969\xf9\x02\xd9\x0c\xce\x9b8\xec\x95 \x9d9\xd5!\x0d\xe8\xf3E\x7f\x84\x16\xc9\xbf\x98\x03P\xca\x17\x94\xd7c\x1f\x91kuC\x0c\xc1\x8a4\x16F\xf8}\xc8\x1fe\xb8\x1d\x9aU\xc5\x13\xfegy_\x92,\xf9 \x9eq\xe7ed\x91\x81\x8f8%*\x9d\xd3 \x89\xe0\x94\xe0\x9f\x17\xd5\x9fG\xea\xcfSRF\xf4\x887\xb5@\x1e\xf1\xbe\x0c\xf29jH0|\xa1/\x89-\xbb\x04\x9el\xc9|\x89 &v\xf6\xab\xd3\x8e\xdf\x0b\xaa$,\x11\xec\x87*\x7f\x06\xbe~\xe0\xbfk\xee\xdf\xbbw\xe7\x1e\xdc\xe2\xe7\xd9\x9a\x13s\xfb\xc6)\xdfd\xe2M;\x92\xe3^\xd9F\xb7\xbbG\x8f\x1e\xc1\xde\xfdP\xde\xe1O\x02V\xde|\xf8\x10\xf6\xee\x8b\xdc3!\xac\x9b\xce\xf8\xb6P\xa6\xe3._Il\x1en\xc1\xde\xee7w\xbe\xb9\xbb\xf7\xed\xfe]X\xc3\x9d\xfd\xfd\xbd\xfd\xfd{w\xbf\xe1O\xfc\x9c2\x9fZ:\xd2)&\xac\xd7\x8e\xe0\xeb\x92\x86Z4\xd5\xdd>\x8f\xaa\xa3\xb6\x07\xa3\xbb\xe3\xae\x9e\xb7\x9a#4Px\xc5\x18\xa8qY\xe6P\xa5=\x18\xd8}\xce\x12\xf4)\xdc\x92C\x15\x0e;\xc2\xa7\xc21P\xd0\xf0t\x17\xd66\xe7(q\xec\x8d\xe0\xbd\x80\xf5\x1b\x993\x83`:\x1cxF0\xf1\x19>\xe7T\x1c\x1b\xe7K}\x9d,\x0bp :\xdb\x08\xc7gq1{\x9aM\x88\x06\x19u\xcb\xa4\\\xc4\x96\xaa\x90-\x1d\xa4\x9e \xb43\x9e\x1f\x9a\xbe\xaa\x08\xbfw\xc2c\x8d\x84a\x97\x1a3\xa9\x9c\x0b\xcb\xaf\xc9\xf09\x19y}\xb9\xf5\xd6:n\xb05\xceOS\xb4q?/\x8e\xaaT\xd8\xe8\x0egz\xe25\x16[g\xdd\xe0\xd5\xbf\x96\xa3\xa0\xd9\x84|X-\xf8\x96\xdb\x0d\xa1\xb8H\xd8x\x06Au\xbf\xab)~\x8d\xe3\x82\xc0\xdeA\xe7{\xa0\xd1\xfe\xfe\x92&\x9f\x97\xe4\xf93\xfb\x1c\xd5\x85\xcd\x7f\xb7a\xf3\x93l\x8c\x01\xc3G)\xe1\xff\x88\xc96n\x96cp6mVj\x83\xdcR\xdaj\x19\xdf3\x7f\xcd\x97k{\xfb5\x89\xf4\xa3\xef\x16\xbc\x16{\xff5\xee}G\x88\xc8\x07\x12r\xac/\xa4,z=G\xd7\x06\n=V6\xd5\x01\xfe@\x97\xe7\xa6\xc7`\xefMFw\xc8%#\xb4H\xaa@\xc2\x02\xe2\x9c`\x92\xe38M\xb3\x0b2\x81\xb8\x80OdU\xf4\x9b\x89\xb3\x9b\xdd\xf3\x0de-n\xf1\xdc\x98\xc3X\xbf|\xd2\x11\xab\xab\xbb*\x86~iI\x8c;\xde\x94|\xbay\xf1\x01\xcc~\xb1\xea\xc2\x15j\xac\xc3\xa6$C\xb2\xc9Z$\x89\xc6\xc1\x9b>\x08\xad\x0d\xb9\xd5m\xfa\xa5\xcb\xda\xfe=\xf7\xe3\xc5\"]I6\xde\x12\xd1\xaf_W\x91\x83L\xf23\xb0\x03\xb2\xddD\xb0\xe6\x94^\x91\xbc\x16\xde\x7f\xa4\x08!\x96AA\x18\xc4@\xf9>\xa8 \xa7\xc6\x08\x19\x95{\xc2\x89\xfa\xfc*\xe7`\x9f\xfd\x06\xf4\xc4y\xeaot\xda+\xe5kI\xd68\xc3\xa0e\xb41\xe6\x03h@\xeb'4]\xf1&\x85\xd6\x14\xd5\xa4c\xe1\xd4{J\x80s\x0fd\xd2\xf7\xf4\"\xfdd\xe1\xedKu\x0c\x13\x8c\x92f\xa1 \xf5b\x16\xfc\x85;{\xf0\xb5HU\xd8\x1f\xcf\xe2\x9c3/\x8fY@Q\x98\xb1\x8aG\xc7\xa4\xed#\xad\xff\xe2\xbd?&U\xc6\x84\xa48*ic\x9bj\xbc\xf5\xdaa,_9\xf0V\xa9;\x8d4\xf3\xcf\xab\x08z\x7f\xefE\x82]\xb4\xea\x04\xc6\xb18\xe2]{\\\xf6cs\xf57\xa0Y\xd8\x16\x97\xdf\x91\x08>XE\xe6\x9fI\xfc\xe9u\xdc\xd02\n\x06/xGd\xe6\x02\xf9\x92\xa1qqF\xb6\xa1\xfc\x1c;<9I\xe6\xf3%\x92p\x8em''\x8d\x14\xed\x1d)\"\x03lE\xfc\x0e\x9e\x93&\xd2\xf3\xfe\x7f\xe7o\xec\xdd7$\xa6\xe4\x0f\xf6\xef\x192\x1f\xbf\xb7\x0cY\xb2\xf86)\xfa\x95e\x03\x9c\x91@\xc4f\xa1tV\xb9\xcd/H>\xcd\xf2\xb9P\x7f\xc7\xa2\x8d\x8b\x84\xcd \xa6\x90\xd0iB\x13F\xa0H\xfe \xbe;\xf0\xa3[\x8cw&\x0d\xfbE$\x0d\xfb\x8cMp\xfeb\x1c\x94\xf9\xd3\xf9\xb3>\x1f\xd9\xeb%\x8byO\x85\x16\xd6\xd2\xa5\xab\xce\xad\xe9\xed^\x91\x80*-?\xedO\xb3\xfc(\x1e\xcfj\xf1V\xc6@\x06u)R\x8a\xdc\x15m\xa9\x9b\xd4e\x8a\x82\xf6\x03\xe7g\xef\\ \x7f\x90\x8el\xe6\x1fI\x04'|\x9e\x1f\x89G2\x9d\xd2| B\x8a\xcb\x038r\xa9\x88\\\x8bd%!\x1d\x15\x86`{\x00\xfb]\xa2\x14\xda\x85\xe1Q\x95@\xc6p,\xbfN\x8a\"\xa1g\x82 \xc3^?\x91\x95\xc8f\xc1\x86\xd4\x94fR]\x82y\xe6/E\xfcU\xde\x97-\xdc\xbds\x9d\x11\xfc\xd76_\n\x85\xa7\x96\x01\xeau\xbc\xb0\xa6<\xfb\xf8\x85\x96\xc5\x93<\xcb*\x959\xff\x81\xa2s\x19K#\xf26\x85&\x93b\xad\xebb\xa3\xae\xff\xa1'\x85r\xcf\xa9 \xec9\xdd\xa0i\x9c\xc8r1\x89\x19y\x8e/\xaf\x0c\xd5\x0cm\xdfn\xba\xb29\x99g\xe7\xa4S\xd26\xccz\xe5nxBR\xc2'\xe0\xdbtk\xd6\xbeS^m:e\xd1IsA\xdc\x89\xa3\x85\x08Y\x92\x17\xa5G;\x94\xae \xa12\xce\x94\x13\x18\x92\x91l\xd4c,m\xf4\xb0\x8c\x06\x83]\xd1)R\xc6b\n\x14w\xf8\xc8\x96$\xda'\x91\xc4\xb9\x8c\x03\x15\xa6\x8d\x95]'\x1aw\xfa\xe2qr\x17K?<;Q<\x97)c\x12YM\xcbb\xd6RW\x01\x03\xc8\x82\xa5\x83\x06\xca\xe5*p\x02K\xe9\xac\xdb\x8e!\x03\xab\xd4qF\x82\x04cH\xd0p\xc3\xf7n\x04\xbd\x84\x9e\xc7i2\xe1\x94\xf8]\xccf69\x88\xcf&\x85\x01\xc4.\x0fT\xfe\xd2XNy\xc5\xa7\x8c\xd4*\xe5\xfb\xc9\xfe\x01?\x07I0\xae\x16\xd0\xa9(\x9d\xe2\xec\xc7r\xf6\xe2\xd7\x8a\xff\x92\xbb=H9\xbe\x06I\xc5\xcb\xb0\x10\xcf\x8e4\x82\xa9\x81\x07\x90{\x9eR\xd4\xe9Z\"\x1ee\xdfy\xd9\x9b\xe4\x9aZu\xd0\x1a;`\x9c\x92\xd8Y\x94Hk\xbc\xed\x16\xc3\x84?\x84Ym\xc0:\xea\x8d\xb3\xee\xf6k2P\xe7\x04J\x8b,_\xa9\xb8x-t\x11&\x06@\x8e\x86 b\xb1\xfeE\\<\x16\xf44@\x1f\xb6\xfe\xc9 \xa1\xc52'o9\xbd\x0e\xea\xc4[\xb1R\xce\x81\x97\xbd{\xee\xc1\xd6\xf9P?7\xf4\xd1pQ\xec\xd2\x0d\xb6\xb8x\xae41\x9b\xf5\xaf\xf7\xd3\xb12%\xc86\xebA\x9e[\xce\xb67spR\x1a\x11r\x01/\xfde\x9e\x8d\xbc\xd0\xbe\xd4\x89Y;\xdcKo\x1b\x94\x03\xdb\x99E:\x88\x08\xba3\x93\x80a\x82\x19\x86\x19eL6\xf7H\x94}\xea\x80\x80\xb6\xda\x9d{K\xed\x98\x8a\xc11`+?\xd2\xfeI*\xd6Fgk\xa2*\xaf\x03\xb24\xc8\xe15\x1a\xd2r?\xe8\x0c\xce\x9edp\x0c\xd3I\n.\xb9\x0f\xe0\xb3\xc1s\xe8{\x12\x01\xb2W\x8dd\xc0\xaf\x1f\xbf\xb3TO{\xc2\xdf\xd6\x81dS\x0f\xfedO\xfc\x81\xc3oOH&*j\x19\x1f\xac5>\x9c @,\x9d\x9c&l\x8e\xe0PN\xb14\x13.\xc8\xd4\xab\xcf\x9f\xaf\xd3\xe78[Rv\xed._\\\xa7\xcbOd\xf5\xa3`\x8aY\x0b\xba~\xdd\xfezs\xdd\xae\xbc;}\xd9\xdd\xe9 \x13\xa5FK\xa7\xe6*\xc2\x86V\xbe\xcd\xf1\xf8\x93H\xd3\xa9(\xcaW$\x90\xbf\xfc\xb4\xa1?t\xa6x\x14\x15\x90D\xc6\xaaVRJ[\xb3_u6k\xa6m\x1ce\xac\xe5o\xd1\xab\xf8\xc0\xe6\x8eyr\xb2\xc8\xc9\xb9\xc9\x14\xec\x97\x85\xe5\x9f\xbeIQ\xeb\xc5_\x9f8\xf2\xf6fJ\xaa#\x11d\xa5H\xc7\xf0\x87F\xe9\xa8\xb8!\xa5\xbb\\\xfc\xaa\x13\xbd\xcck\n\xbf8\x93R\x7f\x8fz\xed\xe0{>\xa0\x7f\x92`\xd73\xff\xdd?\x9c\xb8z.k\x92\x9b\x8d\x9c\n\x15-\xab\xadt8\x17\xc1\xa9\xc5\x9d\x12d~\xd8\x8b\xe0\xc4\xa1\xbc\xc1\x04pL\xf5\x86\x91/\n\xbc\x11h\xcaU\xb1\xb8I\x04q\x18\xc1\x96T}T~U\xe6\x0eD\x1e\\\x19~\x18$\xb2P\xd7!\xe7\x02\xa4\xf6`g\x0fK~\x1d4\xab\xc9\xf1\xeb\xcae\n\x17zvl\xc6g\x14{U\xf9\xc6\x9fp\x9bW\x93\x1cZ\xa1'\x8a\x8f\x19\x1f\x9b\x82@m\xc8C\xea*\x8b\xb2>c\x16\x95\xd4\x07Q\x97\xb4\xd5\x14\xa4\xa5\xa3@O\xb8\\p\x08\x19\xee6\x93\xbe\xc2\x82\x8f\xd2\xe9\xa6\xd4/\x89\x05\x8d`\xe9\xe4U\xb8D%$\xb6\xc0\xf8\xe9\x01GD\xb9\x9e\x84\xf3#G\xc12\x8c\xe0(\x881\xeb\xc3\x05?'D\x0e\xd7!\xff\xcc7\x9d;cn\x1e\xaa\x95\xa8\xf4W\xe1\xf6\xd9\xba\xff\xc2\xcf\x13\x976\x80c\xea[l\xcc\xf2\x08\x1b\x0c\xf8\x02h\xac\xf3\x8br\xa6\xb2\xbaP\x04\x99\xc9\x96\x83\xbbW$\xde\x0e\xaa$_U\xcb\x07\xda\xdf\x8f\x1e=\xe2\xf4\xe3\x16\x9c\x99\xf7\xf9\xb2\xde\x08\xba\xe9k\x1fY),\x1f\xef\x8f8^\xaci\x1b\xc3Z\xfc\xb1\xc4qI\xbd\xea\xb0\x82\nl\xc3\xb9\x84\xccH\xe8\x15\x07\xf5\xd5\xcdB\xfe\xe5C\xf1\x1d\xe1+\x0d\x070L\" \xbeK\x9e3\x17\xbd\xac\x12k`\xf5\x82Z\x86\x02Z\x9a\xe8:\x12\xdfph\xd1a2\xb2\xd3\xcc\x02M\xb46\xeds\x1c,\xd1-:\xe0\xaf\x15\xf5\x8c\xc6>~ \xd3V4\xa1\xba\xae\xc2\x90\x1f_\x8be1\x0b\x0c\x9eEV\xf2\x12+\xa0e~@\xce\x9c@.w=zmUj\x95[\xb7\x00\xb3\xb0\xd6\xd4+\"'c\x99\xd8Wl\x7f?\xce\x12\xc1S\x82\xc9h\x87\xbc\xa3QX\xe3\xc8\x98\x0fG\xa6.\xe5l\xc0\x86\xb6\x04x\xea\xca\x10\xab%\xf9'5\x115FEKl\xad\xfe\x01F.J]\n\xd9\xcd\xb4\x99wU8\x8d\xf2|\n\x0b\x90\xd1a\x9a\x82W\xc9\x99\xd6\x8e\xb9d\xb7\xe0\xb8\x85\x14\xa9\xe8\xb2\xf9\x1f\"\x7f\x9dJ\xdb\xff\x0e\xec\xc1!L\xfa\x8bLT\x82\x98\x0cSN\x8dZ7\x86|\xe4\x9c\x1f\x9f\x08\x06S\xfc\x0e#\xec9hh\xff&\x95)\\ \xcc\x11L\xbaX\xd2\xab\x08~\xbc693F\x97!vY6+\n\xf5\\\\ \x82z\xfdp\x11\xf9IP\xf6\xb1hF\x12EC\x84\xa6\xd7J\xd8x\xc3\\\xce\xb9%\xb8\xbb24\x1b\x95\xb3\xc3%\x13\x8f03\xf2H\xc4q \x19\x89\x99\xd8\x89&x\xaeM\x17k\x99\xa1U\x02\xe8\xa7$\xc8m\xa0\xd2\x04D&Y\x1e\x8a@b\x0e\xa9\xb2P\xf0]\x9a\x9f\xa7u\x18\x9a_\x1acL\xe5\xd6\x00\x82\x14n\x81 \xb5\x91\xae!\xa1\xce\x1a\xca\x1c3AUtz\xc9D\x93\x08|s\xe7\x0b5B\\.\xf3;|\xef\x8d\xe1\x10\x16\xc3\xe9\x08\xdc!\xeb3\xa1(\x9b\x08\x0b\x8cX\xe8\xfaZ\x99g'\xd4\x04\x13\x8f\x83B\xc0\x01E\x97\x85F\xde\xc7N\xf2\xeep\xf3\xaaU\xfc\x92\x0c\x01\xdf\xcf\xa2\xde\xcc<\x8c\x103v\x1fHV\x9f>\x80%\xa6\xf9\xe1\xb81\x80\xbd\x10\xe2\xe1r\x84hp\x0b5\x0bl\x98lo\x8f\x1c5\xeb@\x13J\x87\xf9H\xa8\xb8\x84/|\x80 \x05\xb7\xb1\xda\x98\x81\x90\xf0\xc7\x8b\x08\xd2\x08\x96\x11\xcc,\x90\x94\xe79\xff\xbf\x08S/\xa1\xc4\xe5?\x16,\x86{\xf0/\x98j\x9c\x8b\xba\xe3h\x0f?\xde357\xab\xda\x99\x99\x11\xf1tSr\x7f\"\xd1m\x86\x14\xfc\x00R\xf8\x17\x92\xfd\x14\xd6`\xc1\xd0\x0b\xed\x93\x82\x05\x8b\x08\xa6\x11\xcc\"8\x0d\x9b\x01\xf8\x1d\xe2\xc7yY\xed\xa3\xf2\x80\xb0\x1f\xb5B\xbdZ\xa6\xbf\xc9\xb5\x08Z!\xc5P\x80O\xb9\xa7\x1eb\x99=Q\xf3\xacslz\x97\x88\xf6\xf5\x0e\xdd*\x8d\xa4\xfa\xcc1\x06\xb7\xa2#\xe9\x92\x16\xf0%\xb5L5\x00\xa8\xbbn\x19\xa2\x81_0\x80\xafH\x90X\xed\xe7\xe0\x14\x17\xc6\x19e \xdd\xa8\xf8C\xbb\x7f\xedW_\xf8\xccv\xecj\xa8\xb6\xa7mct\xe6J\xb5\xe6Im\x10\x90:0\xf9*\xa7|\x06s\xb8\x0dw\xdb-\x8f\xd5\xb3\xfd\xf6\xb3i\xf9\x9d\xcds\x7fa\xf1\x188\x97\xb1CG\xc6\x80a\xe4\x9b\xbb\xf3XZ\xe4\xea \xe6\xc9+\xa9\x9d\x99/\xa4\x18:\xec\xaa\xe7D\xdd5\x1e\xc4`r\xa9\x03\n^\x89\xe3:\x87G\"kt\x0e\x0fa\x0e\x87p\x81\x99\x07\xf2\x08U\x0c\x18g\x8a\x85 X@\xfb,\x13\xf2w\x88ei\xd9\xc6n1\xe8'r\x9c\xfc!z6\xa4\x01\xe9\xd2\xf4\x96\x9a\xda\x0e\x7f\x13\x93\x17\x89\x9f\xa7\xc5\xc4\xed0\xa2\xe5\x01\x99\xb1\x8e< \x0b\x16\xc1\x05\xe1l2\xf3\xc8\x03\xa2 \x1f\x81=\xc6r\xc1\xb4#\xeeKsZ\xbcJ\n\x06\xc3^\x04\xbdQ;\xa9E\xad'\xcf\xa4\x16\x89\xaa\x15_%\xc5\x0f\xcb\xac\xe4\xa4\x9e\x95\xdcq\x9ar\x01\xb6d-1I3\x8e<\xcb\x93\xb3\xc4\xe6\xd9\xa6d.\xde\x13\xed\x8b2\xa1\x04n\xc1\x99!\x14\xd2\n '\x0c6\xcb\xae\xe1k\xbf@\x901\x04\x99d\xabjU\xf3\x1dE\xa00\xb1\x7f\xe5\xc4\xc6\xe0\xa1\x96\x0dvs\x975\xc0c\xe1!\xec\xc2!|\x92\x19\x0cq\x9b\xed\xca\x08SqsW\xa8\x1f\xf7\xc43f\x8c.\x03\xb0'\xd8c\xe8\xfb\xa4\x16\xd3\xfcNe\xcf9aq\x92\xba\x19*\xe5\xdeo})q\x06\n \x14\xdfb\x94\xc08^\xc4\xe3\x84\xad\x84A|\x00\x97Xo\xbb\x195 \xe4A\x14\xb12\xf1R\xd6x\x89\xf4ORrN\xd2\xea]\xfb\"n%~\xe1\x06\x89\x08\x9b\xa8BL\xcbuV^\xf6b\x14\x1c^\x9b\xb8\xdc;7\xd3\x05\x82E\xac\x14~\xad \xa4\xcf13z\x17^\xb9\xe2,k\xdbj\xb3\xf4-H \xcaJ\x1c\x9aU\x03 \xcb,\x992T\\h2\xaf\xcah\xaf^R\xba\x0d\xf1p\x91&c\xe4\xdb\xf6lQ\xbb\xb5\xc1&\xb4 \xf9&d\xa0\xd1\xcbn'8\xfe\x0d\xc9$tjZ\xfeTK\xab'\x9b\xc0\x15\xe6\xf8\xd3\xc8>!%%\x81j\xd7NE\xc1\x19)'(\x16\xcbb\xd6\x05 %\xbcU\x11\xfa\x96]\xae\xc1\xc9\xca \xe1\x1b\x16\xbai%\xe0\x9f\x90\x11\x91dQ\xd9R-;\xbe\xe6\x16\xbc\x8b2\xbb\x96\x16\x11%w*\xe8*l\xe3\x1e\x1e\xe6^%\xd9\xea`\xcb|\xf3:|R\x87\xecn\x04;{\xeeV\x97\x14wWW\xcb\xad\xf5\xb8\x16\xb0\xad\xa1a\x9f\xf0\xc8\xd9\xf1\x05\xb3#\xfbd\x99HnH7\x07\xb1\x17(\x9a@\xee\x00\xf0&\x89W\x1e\xfb'^i\xf7\xe1\x95\x90\xa3\xd9\x91o\xe2\x95vw\x1b\xe4\x19y\xec\x97g\xc4\xdc\x87\xd7\xb4\xce\xaf\x93\xd7\xe3qg\x9e\x91&\x9fx,\x08\xad\xd7\x89\xa6o\xc2v\x11\x8dz\xcb\xbe\xf5\x97\xce\xbf\xa8\xee_9\"Y\xe2\xaf\xac\xfa\xe7\x1e\xddfI\x19\xca\xedi\x17gOJ\xe4\xb3\xaf\xcd\x06\x05a0\x14\xb1\xabB.\x9e\xa8\xa7\xec\xdfW\x04\x86b\xd1\xd6\x8d)\xd0F\xd9)\x9aur\xa5\xfe\xd8 _\xbc\x02\xa1s@\xa1\x04\xc1\xa2\xd7w\xa6\xd7\xad\xec\xdc\x98\xc8_\x92d\xe2\x82\x05:\x9b\x135\xb8\x9c\x1a\x87\xa3s7\x91\xc6\xdcl\x94\x90\xc2\xb4\\I\x81\x12\xf6\x00&\xac\xad\xc1\x9a\xb1v\xe2\x89W\xcf\x8f?X2O\x9c\xa3\x05]\x83\x9cM\x7f5gV<\xc0\xb1\xa3h\xac%-\xa8f\xd2\x8cn\xd3\x7f\x9d\xb3\xe1\x8c\xa9`\x90sV\x05\x83\x9c\xb32\x18\xe4\x9c\x95\x89\"\x9f\xc8\x9c\x91\xda\xbbx\xbf|[\xbd\xa5~\xe1\x8b\xa5\xfd\xed\x89\xb2\xc5i\xb7\xd5\x17\xea\x17>\xaaR{=)\xf3|U\x0f\xcadOOj\xd9\x9f\xf0\x85f\xe2\xa0'\x0d\x89\x19_\xd2\x93\xf4<\xd1r\xf6\xc8\x87z\x0e\x9d'\xb5\xa4:\xa2\x0b=\x03\xce\x13=#N\x04\xf3\xb6\x08\xf4\x84L\xb3\xdcd}\xb4iZh\xe9\xd0\x84\xde\xcc\x0c#\xdb\xca\x8d\x81\xeb\\\x86^hL\x97Y\xbb\x88\xfaC\xe1\x13e\x0e\xad\x15\x0e\x80\x8f\\\xadK=\xe1p\xc4O2s7\x99\xf4\xbb\x10\xaaHs/LT\xbd\xb0S\xf2\x18\xf4Q\x0c]\x06,,R\x1fs\xba\x15\xd7\xc0\x8c\xb0\x85\x1d\xd4q\x86!\x8e\x06\xdfJj\xa0jSe\xe3\x80\x85\x95,\xf3\x80\xf2\x12\x06p\\\xe5\xce2\xcf\x7f+1\xabTj\x8e\x13\xbb\x0f\xa0\x10.\xa6\x05\xfaIJX\x14\xa3R\xfc\xb2\x12\xe4\x0c\xddD\x96%\xf48\x8d\x0f#X6)\x98\x01G\x1fO\x19i\x1d\xef\x9d(\x1a\xd4q\x14\x83\x8c\xbf\x00S\xa5\xf5\x13\x85\xfa\x0e\x84\xcd\xdc\x08k\xee\xc4\x0b\x07\x93:\x0e\xda,J\x88\x839&\xcb\xe4\xd8\xa5\x83\xd1\x80\x82\xf8Rf\x86\x0c\x1a\xbf6DN\xb5Y\x9c('\x9b\x8ceoRY\x91\xa1\x92/\x92~mq9M\xceD\x85\x11\xc4udi\x1fog,\x82\x15\x8b8\xd3\xe0J\xa3~b?\xad*^]\x1d\xe2F\x08KEay\xb2\x1b_\xc2\x04-,\xc8\x1dQ3Ryf\x87O-\x91\x88d\x1cv\xc3\xc6\xc4\xa0\x16\xf7\xcc\xe7\xb6\x8c\xc0jc\xad\xe9q\x96\xb5rV\x16O\x13u)b\x12K\xff\xa5C\x85`\xe2x?PQ\xee\xf8\xd3\xce\xa3\x82\xf4K\x89e\xe5\xc3]\xf4\x8c\xdd\x81\xd8\xfd \xaa\x18\xf9k\x16\xbe\x11_y\x04s\xc4\x1d\xfe\xf2\xdca\x0f\x95@\xe8\xe4\xe1\xd5\x95\xa0\xe3,\x9fvZ\xee\x87SG\xd1\x11\xd0\xd4\x12X\xedq'\x85\x03N5\xdd\x9f\xc8\x96\xd1\xb3k9$\xe6\\)`\xdcvx\x97/a\xd1t\xcb\xcfPs\xdc\xb1\xac\xc2\xa9\xd5\x7f\x01S$/\xf5\x05L\xe0\xd1#\xc8\xdc\xdf\x8d1\x00f\x9b\x1f\xeb\xea\x03\xc72\x8d\xcb\x05\x1d\xdf\xf0\x82\xe2\xb9\xf6\xc0\xea`\xa1_|\xed\x8d\x19]L\x97Z\xf4\xa5M\xe8k^\x89,\xb2\xc7E\x9d.\x85|\xf3ZJUh\xe7\xcbv;\xbe\xba\xf80\xd2\x86/a\x17\x82\x83.\xf5#\x92\x8f\xe1\x00\xd2.$\x079\xf2X\xb8\xa2\x17\x98y?\x13\x87R\xc2Q\x83\xf2S;\x0b\xedn \xe0\x9c\x92co ]l=\xf6K(qaL\xf6c;D\x96\xad\xec\\\xe7\x0e\x8d\xc2\xb2T\x93\xc3\x0e\x17\x92\x96\x9a\xaa\\\xfc\xd4T\xe5\x0co(=9\xc5_U\xd6\xa3e\xa9$\xcf\xf0\x87&5&\xe2\x86\xd4\x97\xc7\xe2W=\xb9\xd7\xd2\x0b\x14G\xcc\xa5Q;c\x18\x06}\xc6\x07$\xec\xfa\\|\xf34\x85_\xb6\xa1l\x03q,\xfc\xf1er\x1ewL\x05\x11N\xf3\x0f\x15qS\x8a\xd9\xd6\x07\xc8\x0b#^j\xbe\x14\x99kc\n\x96\xb3\x83sK\x1b\xc4u\xb8td\xcc\x19\x0b\x13\x9f\xb4\xe5\x89\x8d\xa1`\xe1\xd4$\x8d\xc5 \xa5\xf2F\x05\x92\x0d\x136\xde\xb2c\x18\xc0\xd8\x1c6h[\xd1\xa2>\xf2\xf2\xf8'\x95[\xa6\xdeUT\x83\x9d\x80<\n;-\xde\x12\x0e\xcb\x9b\xcaD\x16\xeb\xe3l\xc7 \xd8\xf0\xe6\xd8\xce\xd3\x95j6\xf4\x07(c\xf0\x88\xe6\x99J\xa4\x07\xea\x9c\x05\"?\x97dK\x91+\xe5\xa3\xe2\xe2\xa5g\x1a\xc3\xa7\xf6\x91\x94\x16\xf4\x86\xedW\xb7\xac\x9a\xf9A\xf1\xe5C!\xd0(V\x10\xb6\xe1\xdc\x86t5sD\xc9DJ\xbe\x15\xbf~ \xfc\x16\xd0\x15\x07\x0b\xab\x0eJ\x1f\x06\x11\xaa\x95\xa3'\x03\xffhg\x00\xe7N\xc4\xeb*\xf3n\xad\xe8\xe5L\xd2\xa3\x05\xbd\xa8\xa83Q\xeeX\x7f\xa2\xe2\x0f,\xe5\x8d5\xb3\xbe\x9en\x07\xf33\xd8\xd9\xf6\x0e\xf6?\xf1a\xff1\xc6\x03\xb6m\xc5\x19\x96\xa5\xcc\x8c\xd8H\x91\x9b>@\xb3\xd1.\xfe\xbd\x8d!c\xbc\x05\x83\xc7\x02\xc7\x87\xb8\xb9\xbf\x92.2\x15s\xdc[j\xd8\x86\x86_\x13\xa7R\x13\xfb+\xd1#\xd5\x91i\xac\x82N\xb7a\xccG\xfd \xc4\xe7r\x1fa\xf5\xac\xb4\xbe\xe3\x0fa\xa8\x8cG\xe9H\xee*.\xd8\x8da[e\x1f(\xf8\x9f\xe7\x86\x11\x8d\x85L\xc8\x1f\x8f#QF}\xcc\x0f\x00\xf1o\x82\xff\xba&2\x15\xd2X\x82\x11\x04\xf8\xe72|\x00\x0b\x0e\x11\xec\xb9\xe0\xbb\xc9k\n\xb5\xa1\x8b\xf1\x9a\xf1n\xd2\xe5N2\xc3 \x8a\x87\x18#!\xc8\xc6RH\xdc\x07|`x[Soat\xe3\xc4\xbc\xb2X0]|s\xeb\x16\xc6\x01\xa3h6i\xa8 :h\xc5\x1c#X\x90\x90\xa7bz\x9c\xdf(\x1e\xc0\n\x1e\xc19\xff\x87S\x82.Y\xe2\x14\x060E\n\xb22+I\xd4\xc5\xbb\x9bK\x92s:\x12\xfdV\xbf\xad \xa4\xcc\xfc\x9d\xfaP\xf4|\x8e\xb4\x0b\x060\xe9\xa0L\xa0\x18|\x05\xb2\x80/\n\xc6\xac\xcfj\x8a\x93\x1c\xd9\x98e\x88g\xdd\xa3\x01,B\x8898\x16\xb8h\xf8o!\xdc\x16*\x07\x85VSR\x0f(\xda2\x85O\x96\xee\xc8\\8\xce8\xa5B\xfcp\xae\x9c\xdc\x87\xa9S\x98\xe1\x0bs\"\x84\xeeG\x8f\xf8\x81\xeeZ\x18>\x80\x13\xa4\xae\x8b\xea\xf5\x10Ns\x12\x7f\xb2\x7fu\"\x05\xb5\xed\x01\x04bK\x85\xf05\x9c\xe0&\xd9)!#\xf7\xd3\xf0\xc4,\xdc\x9a\x177\x15X\xfdH\xaa\x11E;M\x90\x16|ev`\xcc\x97(\x15\xfb\xe1\xa1\xd8\x0f\xb5\x0f\xca\xe5,8%\x90\xef+\xea\xb2#\xa9\xca\x8e1\x8ar\xe3\x94\xa4KTkT\xc7\x89`\xbbI\x8d\x9d_V\xba\x1d\xc08\xce\xca\xbd*\xd5\xdd\xabf\xbe\xeeU\x9cL\\\xb0 \x16\xe2\x0eFj6\xa3\x1b-\xc7\xf1c\xbf|\x91\xb9\x9e/\xb2\x16A_eY[\xba#B0)\xb6\x93 F \xc6\x9a\xbe'\x15\x10~$\xf7l\x82\xeb++\xfd\xc5A!RJ\x8aU\xbf\xe9\x94\x92\xb9\x88GK7@\x8f\x04\x1e)\xa7\xc9[\xb7D\x82\xa8\xca+9A\x92\xa2 \xdf\xccrcY\xa9\xb7])\xe6\x84[\xf5.*\xe5\x94\xce\xfa\x9co\xcas\xaf\xf6\xdf\xb9\xdbw\x16z|.\xdc\xe1>\xb0\xaa\xbe#\xbf\xb5\xb1\xdf\xcd\xf9\xff\xfa\xfa\x8e\x1f\xdcP,Ka\x8e\x9b\x08gk\xf0\xb5oJ\xbe\xba\xea\xe1\x9dfT\xb1+!\xaa\x14\xe1(\x02\xe1\x8f\x03\xb4\xdb\xf7OD\xea \x91;<\x15\xf6e\x8f\xdc\xe1^sz\xeeT&\xac\x842a\xc5{|\xcd\x02Q\xdd\xe6\x88\x05\xadP?K\xeb\xbf\xbb%\x0ci\xda\x89\x14KoM\xbd\x14K>8)\x1c\xfc\xbcHI\xc1,\n\xff\xa2\xe2\xf8\xf9\xd1\xba\xb4\xa9\x12\x06\"o\x93\x19o\x85~\xa2KQ\x18K\xf28\x10\xda\xd3\xea\xe7>|\x0d\x89r\xdcD\x1b\x910V\xb6\x93\x9fZDXu\xc9\xfe\xb5\xf9H\x15\x0bJk\x96}\x14\xf6Y\xf6\x92\xac\xc8\xe4\x98|\x0e\xc2\xcd)3\x19\xeeZ\xb8\x86\xb0?M\x93E\xc0;x\x1d\x8b|:\x1anr\xa2\x9b\xd7p\xb5\x8e\xb9\xba\x933:\\\xa0\xf1L\x95}c\xa10\xfe)%\x86\xe6\xdc\x1bkj\x0bND\x96J45(/\xb5X3\xabm\xa6B\x80\x18Qi\x19\x0e\xf7F]\x8b\x9d\x0b\xd5\x9eXG9\n\x91j\xdd:\x081?\xe9L\x1f+\x12Z\xb5\x10\xcbB)\xb2\x19+\xc9\xb0\xf1=\xb9\xfc\x9e(\xca!|\xc3%\xe5\xc8\xcc\x9c\x0c\x07\xe3kt\x7f\xf7\xcc\xbc\xfc\xa6\xc3\xeb\x04\xdd\x954\xaf\x93\x93eA^\x92U\x01U)\x0bE\xf1\xdaI|m\x9d\xbe\xb7\xd0tc\x8f\x9b7\xff\xec\xafm\xfe\xd5_\xdb\xfc\xc7\x8e8\xb6\x7f0W\x8aXV\x1bA\xbd{~\x83o\xf1.\xafN\xad9CR\xe6\x08\x8b9\xaa\xe2%\x9d\x0d\x9d\x97e\x92\xe5G\xb2\xfe\x19\xfa^9\x15b\xfe\x83\x05}7\xc9n\x02\x0b#\x12\x99*\x8a\xf09\xcd\xe2\xa2\xd3\x0d\x15\xf4\x8e\x12:N\x97\x13R4\xab\xda\x97-\xaa\x176kv\x16\xdb[\x1c\xc7\xe3\x19yO\x8a%\x86Q\x12\x1aaE3\xe9Q\xf8\x91\xe2\xe3Z\xd9.W\x04\x93\x12C\xcc\xce\x14P\xa7P\xadzV\x9e\x8c\xa1\xf4:\x14\xbc\xa1]\x1da-v\xa5y\xa7n:?\xa1\xef\xe5\x07\xc1\x9b.\xa9^i7UW\xa2]\xbb\x98\xaeXx?'Vu)\xbbf\xee,_\xab.\xe4RHg\x1d[uU\xfb\x0c\xdd\\\x87\xbb\x1d\xd9\x90\x00\xc3:\xd5\xbb\xda\x87{\xa3H\xfb\xbb\xe5^\xd8\xbc\xdcfQ+\x19Q\x97-\x8b\xb9\x1f>\xf2\x95\xc2\x15\xfe\x9d\xcbLp\x00\xbf[\x11\xa9v\xd3F{?ws\xba\x9d\x148o\x12\xdd|s\xd2b\xa7\x01y3\xa4\xd3\xa7\xa82\xc6\x81bbz7\xc5\xadj\xa6d\x18&\x8c\xbe\xf6\xa2\xc4Nn\x14\xedp@N\x02\xe43\xbck\x13\xa0\xac\xc3\xd9\xa6N\x83\xf2\xa0\x9a\x91\xfaXZ\x04mD)\xeb\x98\xb2\x99(\xf9\xcc\xb9\x86\xc3o:\xeb*o@i\x94\xf8\x9atR\x19t\xb4\x93\x04F\xc9\xaf\xf6\xb7\xcf\xa5OZ&h\x83\xdbE\x05}\x13\x9c4H\xc9\xef\x1cZ\xcbHC\xb6\x18)\xd0\x92\xe3\x9bq\x01\xc0\xa2NhUE\xb4\xec\xf1\xef\xbb=\xd7\xdc\x1b\x9c\xea,\x16m\xeev\xba s\xe4\xe2\xb2\x88`\x7f\xd02\xe7\xcd \xa9S\xe0\xa3y\x06\xa0sW\x1b\x8c\x13\xf4\xbd(\xa4D\xdb\x961pW\xa8Yj\x90-W:\xc1\xb2'\xd4\x04\xc8\xbc\x8f;{\xb0cHa\x0d\x92{h\xd2X+WP\xa7\xb1\xb5\xc6--_\x8f\x8d\xeb\xe0\x0e\xa9\x81\x97\xa3\xe6\xe8\x90\xff8\x0f\xd7Q\x8c\xe4*\x82-\x1b\xec\xcc\xb1E\xae\x19\x19\xcfx{\x0f^[\xfe\x0f_\x95_\xc7\xc9\x8e\x9b1k\xa2\x9a\x15\x8f\xcf\xcbD\xbd~\xc7o\x86\xc7\xd4\x8a\xf7\xb2\xb5U\x11\xc4\xccq\xfaf\x7f-;P\x8e\xa7\xcd\x0bH[\xbb\xa1\xb4P(t\x98\x0e\xa6\xc0\xe5My\xae\xc5 \xd8\xcf\x98\xa5\xb9*/t#|\xe2p\xeb\x05%5\xe8|\x02~P%R\xdc\xde\x8e \xe3\x0d\xe5\x12\x02hn\xb6\xe7\xf9\xe4Sm\xfa\x84\x81Z<7\x1f\xe1\x03\xa6&\x1f\x918*/v\x03m\x036\xc3\xd3\xf9S\xe1\\\xdc\xc9\x8d\x80\n\xca\xa8s$\x89\xfb\x0be\x08K|\xb8\x12\x906\xb1b\xb8\xeb\xb0\x9a\xa9\x0b\xb3Y\x1a\x13\x83\xeaW\x1d_\xc6h*\xd4r\x02}\xc6\x8a\x882\xb7:\"\xcf\xd8\xcap\x82U\xf01\xf3;~\xb6\x81'\xbe\xc4\x8fX\"N\xf9\x0c7r#\xe2B\xc4\x1e\xdcF\x1f\x1c\x0cDD\x9f\x1c\xf9\xfe[Y\xc1,\xeb\xcc\x9b\xc4\xd1\xe6\x9d\xa8cf\xb7'|@\ni \xc8\xe1\x04\x0c\x12X\xaf!\xe6\x7f\xc5e\x8f\x1c&}\x96 \x15\xbav\x10\x07a\x05)\xf3\xa0\xa4\x93w\x0c;&\xcc,`0\x10\x9e~\x01\xdfl\x85tD\xda\x85\x03c\xa5\x89s\xe9\xd5\xe8>vR\xc5bV\xe1\x06K\xac\xac\xa5\x8c\xa1\xcb\xca\x80\x18\xc1\x16\x9eR\x992\x8b-\xcb4>A\xda<+<\x8ea\x99\xe1\x86\xc9p\xd3*)\x10\x93E\x15\x15\x93\xb6\xcd\xe9$\xa6\x9b1\xf8\xb1\x85\x11\xa4_\xa6\xa7\xca\x9c\xe09\x96!\xda\xa4\xc2\xbcf!F\x11\xb4\xdd\xe5\xaf\xf45\xbe\x9e\xb2N\xda\xf4x\xff^K\xe4\xd6\xd3)\xb4\xd1Zm\xab\xf8\xec\xeb\xe3\xb1\xbc7|\x96\xaa\xb5z\x10B\xd6yZrxmo\x17\xf0HC\xf9\xae\x93\xd8+\xfa\x1d\xba\"\xe0\xf9u\xe5V\x13\x10T\x13tM\xa1\xe4\xaa1 \x96\xd2\xe2\x11\x0c\xb0g\x91\xa8\xa3\x13\xc9'\xcfU\x92\\\xf4\xc6\xd05\x95\x9b(\x08\xeaXk;0\x7f\xf2=0\xddd\xfb\x86x`;\x19K|\xf6\x08 \x1c.\xef\xe72\xc8\xc2E\xa7\xba\x11\xdd\xc1i\xa7\x9d\xa4J\xa4\xe4\xc6\xd3\xb2\xc9u\xa7aE\xb5\x8a\x16\xdb]\xb8\xd9\xee0\x02C\xa0\xe5\xcd\xf0\xdc7\xb0,Y\xee\xb3.\x9b0\xf7_~\xdel@\xb0p\x93\xe3\"\x19\x12\xb5\xabk\x92uP\xa4De\x1d\\JZ\x11\xd6Y\x7f\xa4\x0cY\x832d\x918\xc2\xb2.\xba\xd0-7L+\xabG\x07\x8f\xcf1\x04+\xf9\x8d\xf1/\xde\x81\xe0\xf2\x8a\x1a\xde\x8ee<\x93\x83\xbd\x87\x8bY\x92\x12\xb0:\xe5\x81\xae\x0e@\xdb\x95>\xf3\x04\xfb\xd8\x88\xe6\xf9 ?\xde\x88\xe1\xe3\x8b-\x01\x0e\xfcE:e\xa9s$\x07P\xce\x86\x04E\x07\xed9WUC\xac[\x99_\x85\x89\xb2e\x1d\n\x04\xd0\xb8\xe7-\xf4\xbcJ\xe1!\x16\xac\xb9\x05q\x80U\xfb\x90(\xa7\x18\xa8\x0d\x07*M7R\x04*\xcb\x01$()\x86\xa5$\xb1\xb5\x8b\xc59\xedxeW\x95\xf3\x85\xe5_\xb7K(\xfd\x15\xa6\x8c\xdc.\xae\x81\\\xc5aG\xa1\xf3\x1b\xa3R\x92\xadJ\xbc\x94\x14\xc4\xcbd\x02\xea\xdc\x92\xa9\xe672\xcf\xa6\xbe\xf4\x06d/\xb9\xa4\x00\xa5\xfb\xf5po\xc4%T\xd4\x10\x06K\x15O\x81\xd8\xc5\x8f\xd18H\xab#\x93\x96\x84#\x8f\xc4\xf9\x99v\x93E~-\x85sn\"K\xa3\xa5\xad\xe5u\xb6\xa0\\\xb4\x90\xac\xa3g\x97\x1di\xbb(`\xd7\xaa\xdd C\xbb\x01E\xf533\xfd\xec\xa4\xa8\xc2#\x13]@M\xf2\x8b\"\xb8Kk\xda\xe8\xccN-\xc5\x9eT\xda\x8d\x9a\x83 \xeb(\xe2$\xe1>\xccq\xe4\x99(\xbdx\x08\xe2C\xe9^\xc6\xac\xee\x83e\x96i\xeb\x11\x91\xf4\x8b,g~\xd2\xacb\xa2\x022\xbc3\x8a\x80\x0e\xef\x8c\x10\xcb\xc9p\x7f\x04;@\x87\xfb\x86\x0c\xc1aU\x90\xbc\x91\x95\xc1j\xb1I\x86l\xa4v\xd2\x00\xf6\xdbm6+\xf4\xb9\x1a\xe2\xa0\x1f\xee\x99\x06&8\xd7_e\x8d\x0f\xe1\xd6\xfdR\xfc\xfa!h(\x04m8\xf5\xc2\x89S\xc2\xdfE\xc3+\x0f\xbb\xd1\x17\xe2 \x1fJ\x89\x1bV\xbc\xc8\xc9d9\xde@\x87![\xff\x15=+\x05;G\xd1\x87S(*,\xf9\xf2\xdd\xb6\x0c\xd4\x8a\xe5&\xdfWG@\xca&\x03\xaf\x0f:\x12\x89\xf9\xcc\xc3\xf5\xf4|\xff\xd5\x8b'\x13\xf5s\xec[N%\x8f\xbfu\x0b\xa8\xa6\xbf\xad\x85M\xae\xd7U4\x82\xf8\x05[\x03\xde\xedz-b[\xbd\xc6\xfb\xb2\x8a\xbf\xf8\x02\xa1Y\xea:\xf91OH\x90\xfbz8\x97k\xd6\xf2\xb3\x04\x81\x84\xf3\x84\x06u\xcb\x14\x0c\xfc\xf6u3\x0b\x9f\xf0\xf3\xac\xce\xc4\xdfE\xbcv&Bx\xb6T\xfd\x0bM\xa2\x81Z\xfa=i\xa9\x10\xe4\x95\xd9\x92\xf0\x81\x06\x94\xf6|\xba\x05Y\xe2\xc1\xb9\xe5\x9e\xc0U\x97\x022_\x1f~2\xc1O\x01\x86\xb0W>\x97\x1c\xdf\x1d\x07\xfe\xf5\xf5m\x1e\xec\xff\x06\x9c!\xaef\xa7\x00\x86\xba \\\xce\xe4\x9a\x80\x92X\xe0\x02\x88H@\xd2/\xb29\xb9N\x07\x1c\xbd\x1c\xcd\xcb\xfaR\xffFFJ\xe5\xc7\x8c\x11\xbb\xa5\xb3\xaf,Gq](\xe2\x00]\xb3\xbcy\x81\xf8\x87\xce\\\x08\xc2\xc4\"jr\x90\xfe8\xa3\x05\xcb\x97c\xd4,\xfb\xd1\xf7\xaf,\x8e\xdeI\x99\xcdFD a\x89\x116\xcb\xb3\x0bD\xf1\x0f\xab\x059\xca\xf3,\x0fzG\x97\x0b2fd\x02\xc3\x97\x11\xfc4\x02\xb6\\\xa4\xe4\x00z\xb0\xdd\xcaHk\x19\xc3?\xdd\xd1U\xaf\x88\x8cG\x08#x\xea\x1b`\xf5\x8b\xbb\xcd\xa5\x00[^\xb1A\x19\x17x\xbd\x9a\xfe\x87\xbb\xe9z\xc4V {\xfaUc\xb88\xb7\x15j\x81\\^\xbd\x12\x8f\xea\x1c\x9c\x14\xd7\\zT\xee\xf6\xd6\x13\xb41\xce\x9aY\xdd\xf1-\xe9\xa4/\xf3\xac\xbf\xd0\xb3\xcbW\xdf\x0bm\x13k\xa7.\xb5\x8c\x9eu\xe6\xba'\xf0Hf\xa3<\x10\xc5>\xe0\x10v\xf8\x0f\xbfs\x9fZ\xb6\xf2\xb9\xf4E\xfb\xc9x\xe0\xa3\x14m\xe7\xa5\xf9\xd3\x9f=0\x1f\x8f\xc0\xd3\x94@\x96\x03\x06E\xef\xa4\xc9\xa7r\x0f\x98I\xbc\x18\x14\x1f\xb5\x81@X\x97\xd9\x0b\x16yG\xe2d\xc1A\x94$\xd0\x99SLX\xb0\x13Z\xb0\x98\x8eI6\xd5*\x9e;\x9c\"\x10r\x88\x1e\xf5Ok\xc9>\xf3\xc0\xa6z.\x9bpr\xe8\xfc\xa2\xa8\x96\xea\xd6\xb2\xc6U(\xe5'\xb2*\xac~\x89\xea\xda\xf2\xe3\xca\xf4\x8b\xe5+\x8f\xb7\xf8\xc5\x8c\x11\xae^\x9d\xa8K\xceeB\xa6 %\xef\xf2lAr\xb6\x92\x9c\xaf\x7f+\xfc:#L\x13-7\x19\x83\xbat\x12$\xc2&7j\xe2\xaa\xdb F\xbf\x8a\xdax;\x8fo\xd3uF\x1a\x89\x98#\xe8=\x8d)\xcd\x18o\x1d2\n1\x85\xa4L\xcf\x9b\x93q\x96O\xfa\xbd\x92d\x8ah;\x07\x8bi\xba\xba3\xb7\xa9\xcb\x12\x8d\xd0\xbc\xae\xfa\xa7 \x9d\x04U\xd4]\xf7gW0\x8e\xd9x\x06\x086\xf7\x80\xae\x02\xe5\x9a\xae\x8e\x88X\xea'\x90\xeb\xa7\xf1\x9c\x94\xa1\xc3\x9fD(^\x8c?&d\x1a/S\xf6\x13\xe7\x960\xe7\x8c\xb5\x1b\xfb\x00\xc4\xea\x88\x80\xc3\x8f\xa4\xa9\x98P\x97\x05q2\x94)\xcaS\xab\x15C\x9d\x99t]\xa5\xe4\xa7\xb1P\"\xda\xb1\xa9h\xd3\x7f\xb1\xe0\x1d\x8b\xe0#gL\xde\xdd\\\x95\xaew7Y\xa5\xebm>!9\x99\xbc\x8e\x17\xf0g/\x82\xdeU\xbbV\xd7\xbbk\xd4\xea:\xd7k\x04\xf0\x95\x125\xfc\xed\x90\xadyh\xc9b:\x18F\x8a\x1f\xd2PT\xa6m\xd5\xd0z\xf7o\xaenS\x96\x9d\xe1S\x92I\x95\"}\xb4\xb5{\xa1\xcc\x88\xe0\x1c\xf5f\x95\xbf~g\xae\xdaG\xef\xae_\xfbHo\xb8]\x06\xb5\xd6p-\xf5\xb8\x0f\xb0+\x90U\x9f\x06\xa8\xb8\xd1 \xa7?rv\xbf\x91nDGD+\xf2i\xa30\xd8\xd2\xba\xdc\xe8E\xbe\xb9\x80\xa1\x0e\x90\xa1\x05\xd6\x12\xde\xe57/\xbf\x12\x17\xed\xa1O\xf3l~DY\xbe\x12\xbaRM\xf9\xd3\x8d+\x9b\x15J\x10\xc2\xdf\xa0U%\xc1#\xbf6\xab\x11\x85Z\xb7V3BEH\xe4\x12\xd5?\xb2.+\xdf\xd5\xaf\x99t\xe5$\xfe\xd5\x16\xd4\xd1\xc2\xf4\x9d-\xf2^\x18$\x1a\x84dRh\x84t\x00\x1fX\x1d\xbe\xc3\x99\xaanP\x83zY\xe7\xc0\xb0o#`\xc1\x1b\x16\xc1\xafa\x04o\xaeA\x81\xdb\x82\x1fR`\x13&\xd4\x9ao\xc4\x0dt\x96K\x13m\x8b\xa2i\xce\x86Q?rL>oD3\xb0q\xf5e\x9b.\xbc\xa9\xc3\xcd+T\xe8\\\xab\xc8l\xc67\x0e\xdf\xef\x159\xdc2%\x1b\xac\x8dQ%\x1b@\xa3\x86\xf74A\xd7\x1d\x89y*+\x87=8\xfc*l\x05\x896\x80 0\xb7\x13;t\xb2h\x06\x02\xa7\x02\x9fk\x87\xcd\x06`\xc8\xaf\x03\x06\xda\x00\xc3<^\x18\xf0\x15$\x18Z\x85_\xde|\xd9\x19\x119B\x94\xda(\xa99\xe0\xd6&\xaf\x99\xf3<\x1c\x97I\xc0l1KW\x9c@\xa9|\xcb\xff\x14\xeb\x10\x8a,=e\x0fV\xd5y\xd9|\x16\xc9|\xcd\x14\x0eD1 SWa'Q\xd8\xechB\x1b\x9f\x0e\x96\xd0\x01Au<\x99\x8f\x0bZ\xd7=\xb5\x0c\x1aV\xd4m\x82\xcd\xba\xa8\x9e\nye\x19\xa2N\xef\x8bRL@\x83\x8aP\x1a\xa2\xa2Y\xac\x02\x16\xc4G\xbf\xb0\xd2\xbcbZ\x0e\xd7RT' \x0b\xde\xb3\x08^\x86\x11\xbc\xd7\x97\xca\x14\x08\xe8I\xc4\xcbh\xc06%\x7f\xffe\x9b\xab\x93\xd2\xd8\xd7\xc7\xb8\xe9\xbcy3\xdca\x08r_\x96\xcc8S?\xbc\xff\"\x84\xbd\x11\x0ce\xbe\x18\xca\x14\x862\x85\xa1\xa2\xda\x96\xc2K\xaf\x9aa,x\xc6\"\xf8!\x8c\xe0\xd9\x97s\x10\x0e\xe4{v#\xc8\xf7Wb\x18\xf3\xc7/\xe3dn\x0c\xbf\xfe\xc3HT\xe1\xcf\x86\x88\xf4Jr\xba\xaft\xe8\x10)\xcct\xf1\x10\xedu\x94,D\xb3\x9fW\xff\x95\x88\x84\xc7\xa5\xed!\xbf\xbeb\x81\xb5\x88\x9e\xe6d\x11;\xdf*\xd1\x15K\xf4\xa30 \xaa\x12\xa3\xd8Z\xdd\xdc\x157-R,\xbf\xdaz9#\xa2\x1b\x81\xfd_\x83\xe8\x1e\x91\xa1~{\x01\xca\xf0\xca\x9a[\xb8\xa3\xa2\x86Z/\xd6\xe5e\x89\xde\x95\xae\x11\x82@\x0eS\x18\xa0~)\xde%\xee|S\x0e\x1e\xf7r\x06\x87\"\x91\x8b@\x89\x1cQ\xa2\xba\xb9'n\xee\xb5\xf3\xe5\xeb\x97\xc5e\xd1\x83&\xd4\xce\xe1z\x1a\x827\xf6G\xcf\xec\x8f^\xd9\x1fa\x8e\xaa \xa7\x11\x9c\x10.ZP\xed\xcd/T\xb0.\xa9\xe4A\xb7\xa1g\xd5\xb0\xd6:\xdc\xf8\xf8\xaci\xd4\xf9\xe7o/he\xf2qw\xe6\xa9L\x10v\xd0YY\x1d\xdd\x85\xe6\xf5\xcd[\x1b\xdc\x90\x18\xe2\x94ks\xe1\xe2\xeba\xf5\xb7\xd2Y\x18b6\x9b3\xf1R\xfeV\x92\x89Qe%\xfa\xbfuK\x1b@M\x9fk\x9eli\x1f\xd7l\x03v\x9dT\xff\x84\xcc\x17l\x85br\xf9c\x001\x95\xa2\xf6/\xa4\x9d\xf2\xb41UO\x8dq{\xd1*+\xb5\xb0P\xffM\xb3j-\xe9'\x9a]P\xf8DV\xd0\xfb\x1bl\x03\x81m\xf8[\x0f2\n\xfc\x97\xc2c\x8b\x91\xbc\x06\xbd\xad\n|\xb2\x98~Y\x8b\xc3\x8c\x14\x1ez\xc3\x9a1\xa1\xbeD\x85\xd2ku\xe0V\xad,\x846\x9a\n\xe7\xe0\xa0Z\x87v\x1d\xe6\xda\x1ax*\xd7\xed\x1b\xc7OCZ\x9f\xa9\xccS\xea\xca\xac\xd8\x9a)\xeb\x9ci\xfb\xe8\xae\xcd\xf4\x86\xb4\xfd\xce>\xae\xcf\x1eX!\x91\x07\x06\\k:jZ:\x00])e1Y_uk\xd8\x8dS\xbc9v\xf3\xdf8C\xe25\xc1\xff\x84 \xa1\xbeA62\x0dT\x1b@\x06\x0d\xf8\x1a\x04\x1ap\xa8w\x82\xcc\x16z\xd7j\xc0\xb1\x15\xa8\x8c\xc5\nuxO\xd7\xed\xd3\xf2\xd7\x19a\xefT\xf3o\xa7\x9c\xb4\xd8\x11E\x1b\x7f\xde\xcc\xe4\xed\x17(\xb2\xec(\x99--\xfe\xebu\xdd\xcb\xb0\xaf\xee\xf6\xde\xa3\x93D\xcf\xab\xb3\xc2\xdd\x993'\xfd9E\xff\xde\x94\xcacgk\x1c\x94\xc9\xe9\xf9\xb3k'\xa7O\xae\x9d\x9c\xde\xc5\xc1\x97\x92t<\x99\xd8\x8b\x11\x18\xb6\xa6\x17 S7 \xb7\x82-\x04\xe1\x16\x19N\x9b9\xa4\xeb,zF+[UFK\x0bUy\x1b\xeb`\x97\x0f\xda\xe5\xb73*Jdk\xd5\xb2\xab\x9b?'\x18\xd4\xa2\x1e\xf0\x9f\xd5\xc3V\xf9m\xf5\xe0\x19!\x8bF\xf1\xed\xfa\xc3F\xb3\xeaV\xfd%c\x01\xef\x8c\x1aJ\x8dg\xd4XA\xbc\xbc\xdd\xae \x9eQ\x8f:\xe0\x19\xed\xdb\xeb\x80\xe3CW\x1dp\x16\x144\x82#\x8ey\x05\xbd1\x07\x93\x82\xa2-Yf\xd0\xf6\x96D\x02Nq\xfb\x9f\x88\xb0?\x9bZ\xbd1\xa9\xaawL\x98U\x9a*\xbeH\x9a\xaa\xb8Vg\xbb\xf1d\xe2\xdb\xee\xa4\xc0\x9aq\xac\xac\xbcC\xb7\xb7CH\x026\xa4\xa3\xb0}\xec85\x8a\xe5\xb1\xcd\x8f\x1d\x8b\xfa\xc6x\xec(\x07\xa9Z$\xc1p\xb7yx4\x96>\xa1\x8c\xe4\x05\x19\xb3\x9b]\xfe*\xa3\x12\xf3\xab\xbd.0\xc4/\xbeC6\x94\x98NeS\x18\x9f\x17\xcb~-,0\xf0\x14N\xbfg\xd6'\xe7$_y\xb4\xac\xae\x12\x1dJ#\x8cE\xf5\x0b\x02 \x90\xcd\x93\xa4\xc5\xa6$\xeefZ\x1aHR,OY\x1e\xff\x7f8\xf2o\xc2\x91\xeb\xc6ry\xa2\x08&\xb2\xbai\x14Q<\xa4\xcf1\x85`\xc43G\xab\xe5\x10\x81\x93\xebi\xf4$9H7I=/K\xaf6\xd1q\xafCM\xd3\x1e\\[\xe7T\xdf!Y\xce|y\x819\x0d~.\xbdw:Nf\xde\xee\x93\x95\x8f^\xc2\xd08\xebn\xff/\xd2 \x15\x7f\xadz\x85iZ\x85\xb61\xcf#3t\x90c\xcc\xb9\xafa\xd88\x1d?\x85Xk\xc4\x9b\xea\x80L\xf9\xb0;\xd5[\xc5\x7f^\xfb\xb3\x99\xc2G\xf65\x8f?\x91\xe0\x0bu>8\xfb\xa48FM|J\xdb*\xa01\x8d`\xcaq\xac\xf7\xf7\xbf\x9f\x9c<\x7f\xfd\xfa\xe3\x87\xc7O^\x1d\x9d\x1c\x1f}89\xf9\xfb\xdf{mG\x90\x05\x7f\xbb\xf0P\x1aM:\x11\x81X\xaa5\xb1f\xb5&\x05\x05U([j\x88\xb1\x1c\x9c<4\xa5w<\xae\xf0|\xc1V\"|\xba\x04\xa3\x9f\"b\xd6\xbd\x17\xebJ\xae\x85#\x08\xa3\xcaf\xdf(_G\xd5\xb4\x88\xc8\xea]\xad)\xf3M\xc2}\xee\xa4Kc\xcc;\x10\x8c\xf9xg40\x99j,\xed\xce\xbf@\xa5u!TZg\xb4\xd2d]\xfc\xbfM\x93u\xe6\x86_\xa9\xee3\x14X\xd4\x7f-\xe8pJ\x95\x03\xddBSj-*\xa5\xd6\xa2\xae`R?\xeb\x0f$k\xb0\xa0\xba\xcej\xe1\xa3\xf0Y\xb8\x14>\x8b.\x85\xcf\x82\xaa}\x08\x038\xa7\xf2\x06\xdf\x8a\x88\x92\x11\xb0`N9q\n#\x98\xdf\x9cFh\xfe\x97h\x84\xe67\xa9\x11\x92\xfe\xf7.\xc5\xd0\x9cV~\xfa\x82r\x9f\x19(\xf7\x8aFp\xca\xf7\xc9\xdc\x83\x16\x9flJ\xd8N\xffC\x84\xed\xc2 \xcd\x95 l+>\xde\x13\x1a<\xf7/\xbby\xf4\x05\x84\xed\xad l\x97\x1aa\xe3\xb7\xfaKZ\xcc\x92){\x9c\xa6\xbe\xd1\xfc\x97\xde\x8a\xee\xa7nE\xf7)\xad\x1clO\xf5\xbdvA\xe5\x0d\xb9\xd7Np\xaf\x1d\xd1\x08.8\xb5<\xba\xb9\xbdvt\x93\xbb\xe2\x98\xc5\xe3O0\xe4\x1bb\xd4\xde\x10G\xd7p\x05\xa9\x1b\xe3g$6\x14\xaaG\xbd\x15\xd1\x92r\x93\xf0\x81H\xbcNvv\x1e\x84\xf8\xbd\xf0\xaa\xb2\xef\x058\x04\x99\x84\xc6\x14\xf7W\x1b\xf9\x82\x90O\x1b\x01\x88\x8f\xba2\x1c\xf2_\x86\xec\x1d\xad^\x96\xc5\xac\xab\x97J\xdbP\xae\xaf\x9f\xd6\xa1\xd4\xf4\x95\xce$\xb8\xfb\xb7[\xedD\x1a\x03\xcc\x07\x1e!0\x9bo\xc1\x0e\xecq\x88?\x12j\xc3\x9d\x9d\x10?\xb3\xf1\x05\x98Y\xa5lcH-\xb9\x0f\xf9\x825\xd7\x82_\x86D\xcbu|\xb4\x04S\x96\x9c6\xae\x87\x16o\xd5\xac\x18*\xef\xd6\xcb\x9f3\xe9\xda\xff\x98\x9a\xc5\x93\xd6\xe2=\xe6\xa4\xc8C0\x91\xead\xb4u\x05$\x0c\x05G\xe4^\xbf*\x07I\x87\xd4\x82\x0c\xb8\x19\xba\x1d\x9b\xaa\xe4\xed\xcb\xf0\xa0\x0d84&\xb2\xe4\xd9P\x00*4pT\xa7\x10\xeb\xdfN\x9d\x0f-2\x8aw\xca\xc0X\xdb\xfa\xb3\xc6\xfa\xd3\xeb\xae\x7f\xdb\xfd\xba\xb5\xfeYge*\x1de\x8b4\x19\x93`\xcf\xdd\xa6<\xa66i\x97\xa3\xa1\xa7:\xca\xd4\x95\x0f\x067\xbb3\x9d\xa2\x8d\xd67\x9fF\xb6\xb8\xce,6\xb12}i|\xb6D\xa9\x06\x06m\x82W\x9c\x15q\x83\x8d#\x89\xcf\x91\xc9\x89\xca[\xe9\xe8Q\x0e\xd6\xc7\x15\x8cbq\x11\xa2\x7fe\xd6p\x7f\x08jM\xd7-TeG\x17\xa49\xfa*M\x8f5\xc6\xaf<\x99\xf2\xda\xc9\x84e\xce\xb2:\xc9\xe2\x07\xcd\x83\x10\xeff\xee\xd3\xdd\xbd\x88yc\x11\xb3k\xad\xdfcj\xaa0\xddX\xc3\xcd\xd4V\xa5.\xa9\xad\xb9\xaa\x10\x94\xe3\xeacZMH\x9f\xcc\x86a\xc8\xfa\xcc\xf6,z\xa8\xa3kkAe\xdc\x81\xbe$\xd5\xd1\xa2y~\xb9\x90\x82\x8a=\x977\x10!\xaf%\x13\xccU0\x08\xd5\x92 \xe27y\x07\x13\xe85Y?\x1d\xa9\xd7l3\xb3\x0e\xb1\x9a\xa9\xf1\xec\xcb\xfdNn\xcf\xc8\x84N\xaf\x7f\xc5O\xe4]\xf1\x03\xb2\xdf\n\xd0\x91\xf0\xec\x17\xcb`Q\xd1\x98g(Z\xead\x1e\xba\xb2\xf393\xf3\xf9D\x05\x1c\xa1\xd6\x15\x85\x9a\x01\\\x1a\xa4\xf7c\x1a\xc1S\x93\xde\xf5\xc3\xe3\xa7/-\x9a\xd7O\xfc\xfd#\x0fi\xffq\xe9\xae\xd7\x91?\xb4.\xf3\x7frf\x94\xa9\x98\xe1L\xe7\x84\xb3\xa6\xa3^V\xd1\xbf\\\xfc\xaaS\x07\xbf\x94\x81o\x9d\xa7\xee\xb1\xd0\x03\x1cs\x80<\xa6A\xcb=\xc5\xd2\xe8\xbbnq\xb1D{\xabYR;\x9c\x86\xa8\xa3cCjH\x84k\x85\xa4\x9e\xbe\x8bU\xbc1\x0d#\xa8\\&\xb5\xd0\x88\xe3\xd5\xfc4K\xb1B\x82\xeby\xb3\xadf}|\xfd\xd7':|Z\xaa\x17?\xf9h\x03?\xb9\xb4\x81\x9f\xba\xb4\x81\xbc\x0b\xdd\xb6\xf6D\xb7\xb5E@\xfb\xcf+\x02\xf91\xe2\xcbDM\xe9\xbfdJl\x8f4_\xafH\xe0bE@.8\x91\xb9qE\xa6\xed\xeah_\xaf\x8d6zh0\x06U\xbe\x07\x8b\xe9\xcdi\xdaV\xd8c\xa61\xad\x15\xc4\xbbm\x9a\xc0\xb2\xe7tB.\xc9\xe4\x98|\xf6\x00\x8cF\xe2\xdf\xcb\xa8s\xbf^^\x1c\xfb\xb7\x8e\xc01\xa6\xc2\xf6\xd1\xccc\x82\xdf\x9e\xfa\xa4\x07\x9c\x85Y-H6\xc5\xfc\xda/\x8eQ\xe7\xc8\xff\x10\x16\x1e\x0b\xf8P\xbb\xc4\xdf\xf1\x9d\xde\xdb7\xff-\x13|\xfb\xa6\x9c\xe2\xdb779\xc9\x97du\x0dAC\xf8\x13\xd8\xfa\xa4\x93F\x8f\x1eU\xa3\x10\x98\xfcS\xcc\x89\x1aX\xcc\x1b\xa0\xebI\x0f1\xa1\x89\xb9<\xb8aXB+\xb4\x19,j\xc8\x125W\x9c\xa1\x84\x8ay\xbbYh.Sc\x18\x08\xe7@|6o\xa3oRZR\x04=\x84C\xe8aE\x028\x80^\xd4\xb3c2\x83\x01\xf4\x0czTu} \xa6\xbbp\x9c\xcaR\xfd[{\xe8\xb2\xba-,%\xfc_t3\xdaR%\xa4\xb4I\xe1\x9a\x96^4x\xe6\xf4\xda\x9c%\xc8\x1d\xe0\xc5\xb7}\"\xab/ ?\xcf\xbdVt^\x93C=\xd0\xaa\xdcb\xf5\x94\x9d^\x9d\x89\xb3t\xc3\x0d\x16A\xe6\\\xe0\x06\xae\xb5\x1cT\x1e\xc2>\xe6G\xe4\x98\x02\x07b\xc3\xb6\xb6\x83\xae\x06\xc0\x9a\xb5\x0e\xe4\xc8\xe0\x10\x82LR9l.\x94\xed\x92\xb2\xf4\xad\xa8\x18\x988\x0b2\xe7\xfe {\x9f\x9c\xcd\xd8\x86pS\x84Ig\x84*C\x94\x9b>I\xaeG\x9a\xdes\xab\xdd\x1dl\x83\xc6^\xfcq\xb7D*=\x19\xaeWWh\\\xbe&\x06?\xb9\xde!\xc1\xb9\x91\xcdz\x14yYD\xac\xdc\x1b\x8a\xa5\xc2LY0L]\xe5^5&\x9a3\xb3\x06\xe4\x80\xb9\x1f\x94\xba\xbf\x80\xd6\xfc\xee\xd5\xcb\xe9\x92\xbd\x8a7Q\x0f\x88}\x8d\x1e2\xbb\x11\xec\xecy\xf5\x92\x14G\xf3\x05\xf3\xb11\xc8^4\"\xae\xcb\xe9M\xc9\xfd@.c\x9d\x19\xf5\xe0EmFH\xaf\xd9\x8c\xb3%m\xee\xfc\x8e\xf9<\x0dH\xa5J\x12\xdb^\n\xb0\xe2\xe3\x0d\xf4*\xd8\xfb\x13_\xf6T\xf6\xefK\xa5@\xa3T\x1fI\x10V\x06)W\x06<%\xe5\x98\x88w\x17\xeb\x8a\xdf\xcb\xbc AU\xa7\\T\x12\xe7\xbbR\xcfy\xec%\xb5i2\x97\x99\xddU\x97\xa3\x94\n\x9e\x05\xba\xb9\xcdR!\xefJ?o}V\x8f|^\xc6\xe9&\xc2\xd69)\xc9\x86W\xfb2k\xa6\xc7V\xd3\x1dN\xcdk\x8b\x81Z\xfd\x13L\x97W+\xceDHu\xdf\xcd)\xd6\xab\xb7\xfeN\xc3\x86\xaa\xd5\xcd'\xd6\xaa\x1at\xf9\x8e5>&\xc6<\xa0\xea\xba\xf2\xe4\xf7\xc4.}\x93m\xb8\xdf\xa5\xf8\x81;|\xa3\xd3\xa5\x14Y6\xe7,,\xd5\";xn\xea']V\xc2%m\n\x97\xbc\xefa\x16\x01\x1d9\x05L/\xd6\x8aO\xff%\xf1%n5o\xf4M\x84=T\x8dQc\xa9]\xf3\x98\x1agd\xc7\x8a\xe8 7\xb3z8\xda\xb2\x99MF\xb1!rx\x0e\xa5\x02\xdc\xa6\xe3\xf1_-\xcf\xa1\xbc$r\x05\xfdF\x91o\xcc\xbc \xe8\x1f\xfb5\x9f\xc6\xec\xf5\xb5\xa51\xdf5\x02m\x13\xffb\xae\x93\xa4\xae&m\xabk\xea\xbb6\xb2\xd6Bn8k]\xc7\xa1\xae\x895o\xf1\x8d%O\xd9\xe2\x06ga \xd9\x1f5)\xc1WD\xd0\x8f\x12\x7f\x8c\xe1\xa7\xdd\xab\x0d\xcc\x90\xf5\x82y\x1e\xd8R\xa1\xa4.\xef\xfa\x14\x1f\x9fa]m\x9b>5\xaa\xfcd}\x07\xfe\x9cz\x0e\xddTnZ\xf8\x03c\xa1MUa:\xabU\x98\xee\xcc\xb6\x9c`\\\x90GV\xe4\x00}\x1a\xb1Z:\xc6-\xa9\xa4\xc4I\x04+\xceJ\xafB\x14\x13V\x95\xbf\xa7\x19D\xaee\xf1:\xad\xce\xf2l\xb9\xf8w\xb0\xe2~6\xbc@f\xbb{\xc7P\xd5\xc5\xf9wO\x06\xde\xc8\xb9w\xe9\\\xf8\x95\xb59w\xfe\x99\xe0\xdc\xbb\xf7\xb5~I\xf0\x04\"\x04r\xbd\x86\xe1(\xc4\x18\x06\xccY>\x8c#HFp\x00\x89\x87q\xd0A\xc7\xec0P(\xe8G\x81\xb3:\xe5\xed4?U\x14\x8cD\x90\x04&\x12\xa9.\xcb\xf87\x165f\xf1&r\x06\xd2!\x99py%b\x08V\x9e\xbd<\xdf\x84\x86\xab~\x9e\xd3M{J\x8a\xe3\xe5\xa9g\x81\xcfR\x06\x1c\xd8|\xc2\xcaJ)\xc2\xea,y\xf4J'\xe4\xb7\xb4\xe5y\\&\xc6\xd9 \x9f\x96y\x8a\x0b\xce\x0bm2\xc9\xc05K 3m\x96ay\xd3\xffT\xfbDVo\xa7\x1b\x0c\xa9<\xd483\xb7\x11$o\xc0H(\"\xce\xfd\x8f\xf8\x9aV\x86\xef\xea\xe7-)\xd5\xa7\xdbts5Z\xab\xe4W\x1f\xf9Y\xff\xfe^^g],\xbc7\xae\xb11\x97U\xbb\xefy|\xb9A\xaf/\xd8F*\x8cy|\xb9\xe9\x99\xfa\xa2\x96\x8f\xc8\xab\x13?\xa3Yk\x06p\x08\xef\xa9pa\xf9\xe8'(\xcd\x13z\xfd\xe9\x88\xee\x98\xe8\xcewn9\xd9\x18\x13\x8d!\x8f`n\xbe\xf8\x94,6\x80\x9d\xd6\xfe\xeb\x98\xcd\xfa\xf3\xf82\xb0T$\xb6t\xd6\x14\xbe}\xa5\x04\xcb\x1e\xe3M\x06D\xbb\xe3=\x90\x9fgI\xba\xa1\x99\xa1\x1c\xccO\xd74l|J\x16\x1f)K\xd2\xcd\xba\x15@WC\xdeL\x05%\x12\x82m\xd6_\xdb\xcaa\xc8\x0c\x06\xe6\xfeX\xfc\x89l\xb0\xbc\xacf\x80\xb8\x06J\xf1\xfen\x18\xa5x\x93\x9b\xa3\x14\xff\xeaKP\xea:\x92\xc4?\xbc\xb8[\xad\x84\xd1G\x8aj\xdeZ\xf26\x8c\xac\xec`x\x15;\xcd\xac\xdaeuq\x91.\xab\xc7\xe6i\x05Zja \xd8\xb1\xbb\xb5sY\xcf\xbf\xa3\xec\x7f\xc9\xb8\x19\x04\x1f\x82*\x91e\xd7\x0c\xb5f*\xe9\xa7\xfc\xf6\xd6-\xd8\xde\x8eQH\x95\x0dZ\n\x95\xab\xeb*\x8c \xb6\xbeq\x15\x81^\x06\xe9\xbfhU\xb2|\x93e!5o,\xfe\x9d[\xae\xe5\xd7\xd2\xe1Q\xa2.9N\xcf(K\xfdB\xdf\xa9e9\xd3\xee\x0f\xc0?\xe2Q\xbf\x9c\xd1\x8f\xfae\x89\x95\xd0/e\xba\x89;\x8bS\xa9K\xe8\xf0kE\xaa<\x1c\x1aUD\xa3\xac\xdf\xeb7\xd1B:\xab\xfa\xbd\x9d\xe2\xdb{\x1d\xae\xad`\xdaki\x04\x05j<\x0f9i\x1b\x0c\xe0\x8d\x14s>s\x8c,\xf0\x05\x91\xe6o)=C\xfe\x0b\x16\xb7\x8b\x088)\x80\xf1\xe1\xe6\x9aW~\xf0\\\x97\xa9(\x0f\xad\xcd\x98\n\x15C\xb0!_\xba\xb9\x186\x8b\x8b\xd9\xd3l\xb2\x81\xa3\x0b\x9bU\xd9\x05\xb0\x8a\xf3L\xcf6\xd0\xcd#@\xb9\xbd\x84\x83\xf2`\x00{p\x1bv\xcb\x8d\xe6 ]\xcaL:\xeeT\xf0\xf9\xb9\xf2\xa36\x16\x0ea\xcf\\\xf5\xb6|M\x0c\xcck\xf1\x1b\xdf\xf0\xd1^\xa2\x90~\xe7\xee\x9d\xfd\xef\xf6\xbe\xbds\xefN\x18\x95\xb7\xe1\xe1C\xd8\xbb\x07k`\xf0\xe8\xd1#\xd8\xd9\xbb\x17\xc1\xdd\xfb{\xdf\xde\xbd\xf7\xdd\xee7\xcd\xf7\xeeh\xef\xdd\x89\xe0^\xf5\x1c\xd3\xb9\x07\x0c\xb6\xe1\xce\xb7\xf7\xef\xee\x7f\xb7\xbf\xf7\xdd}Xs\x98\xfe\x8bo\xe9\x7f\xc9\xcf\xf6\xeeG\xb0\xbf\x7f\xf7\xfe\xb7\xfb\xfb\xf7\xca\xe6\x8f\xe5\xe7\xd8M\xf9\xe6\x9d\x08\xee\xec\xdf\xbf\x7f\xf7\xdb\xef\xbe\xdb\xfd.\xd4\x9bpl\xb9@\xe7\x0f(\xd6\xba<\xdc\x10j0\x80;{\xf05\xe4\xb0\x0d\x9fi\xf0\x94\xe0\xa6yJ\x02\x16\x86|F\xf6\xce\xc1sw\xaaKh\xc5\xaf\xd1K}R>\xdd\x943\xc2\x8e:;\xd8\xacq\xcfvCc9k( \xa2\x89\x14\xd6\xee4\x95\xc1|/~\x10\xc9\xc9\xb4\\\x00\xfa\x1b\x1f\xe8p\xaa\x02\xbc?\xd0\xe1+\xfe\xf7\x07i\xb2(\xf8-\x19:*n\xcb\xc0\xea\xf2\xbe\x1e8\x04\x03xF\xf1IB\x8b\x85\xc8\x8d\x8f\x9f\x1cg\xcb\xbc\x9eW\xc6\x04\xb2\x86\x12I\xba\xb7\xd6g\x87\xad\x8fgqBE\xdb\xd2\x96)ng\x94\xc5 F\xa5\xe3\x10\x84\xee\x12c\xc4s\xd3)9M\x93\x0dB#K\x01\xe5#\xb3\xae\x84I\xed\xb38j\xb9\xf7\xfbZ\xff\xedT1\xb7\xcb\x02N\xe1n#\xc3j)M('\x89a\x12A6\xb2\x17\x9f\x06\x10FU\xcd&\xe9)4\xce\xe3\xc5\xcb\xba\x0f\xb2/\x8c\xae\x01\x04\xbe\xeeMXt\x89\x19-X\x88h\x04\x07\x10\xb0\x93\xeb\xec\xd6\xd7\x14\x93\x9btf\xeexn\x07\x92\xdaI\xf5\xbe,\xed\xfc\xde\xd9\xce\x90E@F^\x8d\xbd\xb1\x90\xc3\xe6\xd9\xdc\xb1\xd9\xb6\x88O2.h\xc3\xd32\xac\xf773\xac\x9d\x1b\x1e\xd63\xf7\xb0z\x05\xd2\xc0\x9a\xf1\x03\x0e\xe1\xc5\xf1\xdb7}\xf1(\x99\xae\x84\xdaVRK\xcf\xdc\xa2\xaf\x9c\x04\xf8\xd8\x9a\xc9\xd3\xd2\xdc\xc7N\x0c\"\xf0\xb0\xe4\xe0\x08<\xc2\xbfw\x90\x9d\xf3\xea\xe0\xb3G\x07\x9c\xf5\xd9\x86\xfd\xfb\xf7\xee\xde\xbds\xef\x9b\xfb\xdf\xc16\x04\x843d\xf7C\xf1\xe7\xa3G\xb0\xdf>}\xeb\x0b%[{M\x87\x0bu$\xbe\xae\x8eD\x19\xa8\xc5\xef5\xceD\x91^\xa0|\xd08\x14;\x89\x9a\xec\xb6\xb1\xb0\x0c\xa3o\x0f0\xfc\x161\xa5>p<\xd82s\xf2\x93/M\xdf\xe0\xa73\xbf\xd1\xc0\xa9=\xbf\x93b\x9a\xd0 JO\x9e\xdd~\x817\xdd!:\xd3\xc1\x01\xec\xb4\xfd\xffLfN>*?\xc3\xd5\xb9\x9e>S\x99\xa8\x9c\xa3\xd1\xd2\x0c\x97{\xc7\xcb\xd53\x8d\x0b\xf6\xfc\x9a#+\x8dq\x7f\xd9\xe8n\"~\xc3\x13qn2~\xc3\xb7\xcb\xc5\x06}*Dm\x86\x15\xd9\x9d\x98\xf9:U\x96\x02.u\x8a\xa0Z\xb1\x10\x98\xf6j_\xfe\x89\x15\x8c;\xb23\xf2\x8b\xa8\xec\x8c\x9c`\xef*\xe7~t\xce\xafRDt\x04\x85VI\x15\x959\xa3\x03{J0\xef\xc9\xd1\x1eB\x0e\x07\x90\xab\xd0\xfdc=\x02x_94\x88\xd61\xc7\x81gP\xb0r\xee\xfc\"\xf2Qz\xab\xfe\x15$\xe4:\x8e\x9f\xa2\x9a\xbdW\xeb7\xe4\x9a\xe8\x89\xfd\x1b;\x0d6\xd2k\x87\x88\x82\xaa\x14]]\x0b\xa5e^\xafG\xd3\xdc\xba%\xf8\x8b\x99\x96dU\xe1\xed\xb5\xfc\x11EUmKV\xa5M\xdd\x117s^j\xc1\xe3\xd1\x00v1\x07\x85%\x90\xc8\x02(d\xbefUt\xd1\xce^\xf5\xa5<\xb4Z\xd5\x14\xc1v\xc61\x92/\xb2b\x13\xd3\xe6\xf5\x93|\xf8\x99\xf5\xaa\x12\x03%\n\xec\xc3\xd7\xea\xd7\x0e\xec\x89\x02\x03\x0e\xcb\x9f-\xf5\xa1~)\xa3\x01s\xca\xe5\xeaJ\xbe\xd8V\xd79 \xad\x8d`+\xc1R\x00b]Eh)\x17\xd1\xb30\xd4\x92\x96b\xb3\xf2\xbe\xb3\xe5+\xde{\xe4\xca\xa3\xa1C\xd4l\xb6\xf3\x06i\x84\xb0\xaa\x19\xd0~\xc7\xfe;'\xefo\x0f\xbd\x86\xfd\xac\x84l\xc6!\x1b\xc3\xff\xe5\xb2\x03\xdfz\x1c\x07\x92\x9a\x0b0\xc6\xfc\x1e\x88w\xe0\x10>\xf3\xb9\xc7\"\x1d)Zm\xd4\xcfL\xa5\x8c\xed\x02\xbf\xd3ZbIU^Q \xefm\x9c\x92\xf8\xdc\x87\xf3Rf\xb9!\xefbd8\x94C\xc7bq\x1e\xe5\xa5 \x00J\xff\x12\xc1\xcb~6EgZ\xebg\"?\x89\xe6\x9d\xef}\\\xc3\xbf\x8e\x1f\xf8\x9e\x11\xaa7\xed\xde\xe3y\xf2\xffq-\xbd\xeaK\xf5\xc7+\x1a\xb9\x90\xcd{\x0c?'l\xe6sN)\x99G\xef\xc5\x8do\x9c\xa7S\x01\x02\xed\xf1\xdbL\x96\xb5;W!\xa7\x08Uz\xd8\x89\xd27\xe87\xcb\xba-\xef\xd0q\xbd=\xfc\x8dy,\xc4 Q\x0bZ\x9a\x95\xbd\xe4\xb4\xeb\xe6\xd31T\x9d\x86\x9b\xd9l\xd8|\x95\xc3\xcd\x03\xda\x89\x96g[\x94\xd0\xaeY \xf4\xc7\x9a%A\xbf]3)\xfc\x1a\xe9J\xda\x10\xef\xbd\xac-\x9f\xb8\xf7C\xadiq\xef\x84\x18>\xbe \x86\xaf\x8fH\xf3\xf36TT~\xb9\x03\xa0m\xb8\"P_\xb4\xef?\xcd\xd2\x94 \xa4\x0f\xe0\xd4\xe0\x03\x81\x01b\x1f\x0d\x0f\xf4\xb4\x92\xefX\xfb\xb9\xc8\xcb\xb70<\x91\xa9\x02\x8f\x8c\xa3d\x07P\x18\x1e\xe8Y%\xe7\x86\xe7\xef\xc98\xcb'\x07\x90\x9b\x9e\xc5\xf4\x8c\x1c\xc0\xca0\x89\xf7dAb\xde\xa4\xe1YR\x1c\xc0\xccp\x7f\x9agsLmkK\x97|\x15\x01\xe9\x93\xcbE\x96\xb3\x02\x93\xc4 \xac\xbcr\xfb\xb4\xf5\x96\x05\x81\x82\xe5\xc9\x98i\xf9i\x94 ]\xdbn\x9a\x0f\x8d\xdeQ\xb3u\x15\xfb\x16G\xb0\x8c\xa0hn$L\xc6\x1e\xb00\x82-\xe3\x1e\xe6]\xa7m\xfa\xa7\xa5\x01C=OX&L;\xca\xf3,\x0fz\xaf\x13\x9aL\x132\x01r9&\x0b> \xc8\xc6\xe3e\x9e\x93\xc9\x03\xe0\x93d3\x024\xa3;s\xf5\xe2\x84\x9c\x03\xa1\xe7I\x9eQNu1\x02\x8b\xbf4]\xa6)\x10\xde*\xccIQ\xc4g\x04b:\x81x2Ix\xb3q\n3\x92.\xa6\xcb\x14.\xe2\x9c&\xf4\xac\xe8\xf7\x0c\x14\x9b\xa4\x05q\x90\xfc1\xe7i\x9a\xc0r\xf8\xf7L\xed\xfcfP\x07\x05\xeb\xe7d\x91\xc6c\x12\xdc\xfe\xbf\xc5\xed\xb3\xa8\x9b\xa8AE\xd8\xc6\xc3\xe9\xf6v;\x84\x17\x90\x8a\x85a\x9f\xc6s\x0c\x8dxN\xcf\xe3<\x89)\x83\x9f\x92,\xc5\xe4\xdb\x86\xfc\x92\xad;l\x96g\x17\x90\xf6\xa7y<'\xc5\x87\xec\x1dV\x91\xd9k\xa6b\xd3\xb0\xfa\xcb\x91\x98\x06w\xee\x86f\xdc\xcd\xaf\xdf\xba#K\xa2L~>!\xd3\x84\x12\x95\xfc\x9c\x8bE\xbd\x93\x13R\xbc\xce&\xcb\x94\xf4L\xa4T:I5\\\x9e0\x8f\x12\xe7\xbb\x9ef\xf3yF\x8f.\x19\xa1\x85\xcc\x7f\x8e\xf7\x1bwH1\x8e\x17XS\xf1UB?\xbd\x8b\xb1\xae\xa2J\x9d\xdf\xba]\xcc\xe24\xcd.\x8e>/\xe3TV#d\xfd\xd3e\x92N\xbe\xcf\xf2\xf9\xb3\x98\xc5\xe2\xb5,g$\x97OY&o\x92<\x89\xd3\xe4\x0frL\xe2|,\xda[\xc4y\xa1\xff>#\xec8\x9e/Rr<\x9e\x91\xb9\xf8\xee\xaf\x17\xc7o\xdf\x88\x9d\xd1\xe9\x01\xc6\xf2U\x07\xb3\x8c\xb6*D5\xab\x8eF\xe8\xa8o\xdd\x82^\x86\xbd\xf6D\x11\xb2\x86\xb1\xa0\xb7\xa4b\x9fNzp\x00\\\x82*\xf8\xc6\x8d\x97)\x0b\x03\x16\x86\x8ex\xd7+\x18\xc7l<\x03q8\xb6\x1e\xcb\xef\x1a\xd9\x1b\xae\xf8^\x16\x03J\xa6\xabNH\xc8F\x8e\x05\xc3|$\xf9f-\xa9<\x1c4\xfb\xc6\x1e\xe2<\x8fW\x1bt@d\xb3\xe8]\xa3\xff-\xeaI\n+\xefp\xd4\xeeH\xb0%\x92O\xd2z\x03b\x0eM\xe3\xabr\x84\x1eT\n\xae\xe6\xb3\x9eAB\x0b\x16\xd31\xc9\xa6\xb0RK\xd2\xe7[\xd2\xf5i /\xc6\x01U\xcf\x86\x8b\xb7\xd2\xb2)\xce\xb8\xcb\xb4\xbc$\xec\x8b\x8c\xce8\xdb\xea\x95\x8a\xd9\xac\xde4\xd5Nd\x98`\xf0Cv\xcc<\x0b\x05)\x15\xa3)\x87\xbb\xd2\xfd\xecF\xb0\xacP\x91\xb4\xb3\xf3v [\xe6\xf0\xc5!3$\xe80\x14\xbe\xeb*\xc6N\x879\x17\x0f\xc90\x1f\x89\xf4\x8at\x99\xa6fMt+\x13&\x82\x8cf\xf9\x1c\x0f\x0f\x81s\x03\xb8\x8c\x90N|O}\x91\xd6<\xc1vOIQ\xd2\x9dc\xd9\xc7\x92\x8eo\xbe\x175\x11\xaff\x9b\x99\x9a\x8dT\xe2u\xbc\xf0A'+\xca4\x93\xfa\xba\xf4\xa2\xf5ue\x01_Y\xa1\x8a5\xe5\xee\x84?\xdb\xa5\x84p\xc8\xef\xb1\xcb\x7f\xdb\xa8K\xc5x9^\xa7\xee$s\x1e\x08Y\xd7\x81 U\xda\xfcn\\\xdd\xa5\x18r\xb1\x01\x98\x8aU\xc1\xc8\xfc\xc3lI?\xbdN&\x93\x94\\\xc49\xf1E\x9c\xee\xfd\xcf\xfa\x93\xa4X\xf0\xb3I2\x8eH\x97\x9cp\xe9n\xd4\xf4\xb2\xd3\x82\x05\x1d[\x08\xcd\x93\x01 0\x959\x0b,\xbel`\x14#\xccw\x0d\xe7\xa0\\#\x0e\x80e\xf14\x9btC\xf9\xbcL\xb2\xa5\xaal[I4+55\xc1\x05?[.\xf8D\xfc\x93\xa8+\xe0\xec\xf7Ty\xd4m\xe8\xf5Bc\x06\xa5\x10\x19pK0\xf3\x95\\f~\x82\xf9l<\x8c\xce\xa9N9\xa5\xc0\xe1\xbc\xa7\xfc3\xd0\x8a)V/\x8a\x13\xb2\x0d\x0eu\x9a\x11\x99\x83\xc0p\xec2\xce>\xb0\x91\x1d\x96\xf5^\xfaI\x81\x9dQ\x91\xf8\xfe\xa05\x88\xf6\xfcg\xc9\xd9,M\xcef\xdd\xdc\xa5Z\xe1I6Fu\xab\x99\x01\xd9\xaa\xf8\x8c\x9e!s\xaf\x08N`\xe4\x92=\xcd(#\x94\xa94\xac\x8f\xe0\x1e\xb9S\xc5\x03\xe9\xafX'\xdf\x8d+\xb5\xec0\xba\xd2@\xa4\x83\xab\xfa\x88\x90\x0b\xdf\x8dP=\xb2\x1c\xee\x8e\"\xd44\xecE\xa8@ \xfd\x84R\x92\xff\xf8\xe1\xf5+\x91q\x18\x16\xa8V\x10r\xb2\xa8g\xbb\x80\x87\xf0\x0d\x92\xc9\xdf~\xc3\xfdJ\xa5\xe7\xdc\xd8\x99m\x86\x03\x84\xf7\x94\xaa\xae\xb7\xb7\x8b\x910\xafM+\xd8\xecE\xb05\x86\xf5\x1a\x16\xf0\x08\xbe\x15\xbd\x08\xaa\x80w\x87\xb7\x7f;\xbe\xddg\xa4`\xc18\x8c\xf8\xdb\xfc\x83\xdb\xc3\xaf~\xbb\x18i\xf7\x83\xdem9\xb2\xf5\xbal\x80\"iN\"\xf8[\xefo\xa0\xdcN\x92\x08z\x7f\xeb\xe9?\x97\xc3\x02v\xe0\xee\x08\xb6\xd1)\x9e\xf2g\xbd\x9d\x9d\xdf.\xefp\x99\xbc\xba\xf5\xf5\xed\xdeh\xb8\x18\xb9\x8de\xb8,SQ\x98\xa1\x1f/\x16\x84N\x9e\xce\x92t\x12\xc4\x9a\xc8}\x94\x12\x8efA\xafX\xc4\xb4\x17\x86\xfd\x82\xb0\xc7\x8c\xe5\xc9\xe9\x92\x91\xa0W\xb0\x15\xaa\x03\x86\xbdq\x96f\xf9\x01\xfc\x9f{\xf7\xee=\x80iF\xd9\xce\x05\x11 qO\xb3t\xf2\xa0\x17\xe1\x8a\xe1\x7f\xfa\xabxo4\\\xc0!\xae\xdd\x1d8\x84}8@\x08\xdf\x87C\xb8+\xff\xe6\xf7\xef\xc0\x01l\xdf\xfeW\x10\x07\xa7\x05\xcb\xe31[\xa7I\\\xac\xe9d\xadL\x0fk\xbeg\xd7E0_\x17$g\xe1\xe1z\xc9\xb2p}\x1a\xc4\x05Y\x93\xb3\x84\xae\xb3,\x0dHL\xc3\xc3uN\xe2O\xeb\x15#\xe1z\x8c\x8f\xf9\x81\xb3\x9e\xc5\xf9\x1aE\xdb\xc9:\x8d\x8bb\x9df\x94\xac\xb3\xf9\"]g\xb4`\xeb\x8c\xb2\x84.I\xb8\x9e\x90\xe0tyvF\xf2\xf58\x99\xc7\xe9z\x9c\xc69YO\x03\xbe\xc7\xd7$\x0f\x0f\xd7 M\xd8:\x0d\xc8Y\xcc\xc8\x9a0\x12\x1e\x86\xebI\xb6\x9ed\xcb\xd3\x94\xacI0\x9ee\xeb\xb48L\xa6\xeb\xb4 A2\x0d\x0f\xf9<\xb0\xf6\xe8\x9a.\xe7\xebsB\xd9\xfa2\x18\x93\x05[\x93\xf1z\x11\xa4\xc98a\xeb,g\xe1\x9a\x91\x80N\x8a5*M\xd69\x0d\xc3\x90w\x9d\xa6l\x96g\xcb\xb3\xd9:N\x0b\xb2Nh\x9c\x06\xe9\x8a\x0f\xe5\x92O'\x8b\xf9\xd7\x01\x89\xc73>\xfb\x84p\xb0e\xf3\xf5\x92\x8e\x03\xbe{\xf9\x00\xcf\xd2\xec4N\xd7g\x19\xcb\xd6g\xcb8\x9f\xac\x93`\xba\x9e/\x02\x81\x03\xc5Z\x1b\x04\x0d\x12\xb6F\x95~p\x92\xd11 \x0f\xd7i\xc2\xa1\xb5dk%\xfa\xacY@\xf2i<&k\x92\xd38\x0d\x0f\xc3\xc3u\x11\xae\xd3 \x9e\x9fN\xe25a\xebl\xfci\x9d\xd1\xb3p=\x0f\x92q\x9e! \\\xa3\x8ai-\xd4\x08\xe1\xfaM\xfcfM\x83xN\x8a\x05o)f\xc99Y\x93K\xb6&\x17\xeb$]gl\xbdL\xd3p\x9d\x05\xc8\x16\xad\x17\xc2\x10\xbe\xce\xd7K\xb6>'y\x9eLH\xb8^\x04\xf1\xf8S|F\xd6q\x1e\xcf\x8bu\x9e\x9c\xf3u\xc93F\xc6\x8cp@\xb0l\x9c\xa5\xeb\xe5i\x9a\x8c\xc3u\x1e\xc4 \xc7\x98 \x9ed4]\xf1\x85\x9b\xae\xcf\x92\x82\x91|\xbd 1[\x7f^&y5\xefb\xbc$k\xa1b[\xb3|\xb5\xe6T1\x0c\xd7Ep\xba\xe2\x8b\x1f\xa7d\xb2&\xe9t=\xcbr\xb6N\xce(\x99\xac\x93?\x10<1K\xc6kT\xe7\xacY\xbe\x1c\xb3\xf5\xf2\xb4\x18\xe7\xc9\x82\xad\x97\x0b\x92\xafWt<\xcb3\x9a\xfcA&\xeb\x8b\x84\x8dg!\x87\xe8|\x91\xf2\xc1\xcf\x08]\xcf\x92b=\xcb\xb3\x8b\xe2p\x9d\xc7\xb4H8\xd2\xe4K\xb2\xceW\xeb\xd5\x82\x041\xee\x8f \x99\xae\x93\xc9\x9a\xc6s\xb2\xce\xa6a\xb8^\x064\x18K4\x9f\x90i\xc0\xd9E\x8e'\x19]\xa7\xa4(\xd6\x85\x18#K\xd2p]\x90u\x91\xf0\x05:\x0f\xe2|\x9d\xe4l\x19\xa7\xeb,\x99\xacQm\xca\xd7\xe7\"\x18\xcf\xe2\xfc\x84\x89\x01\x91\x9c\xacgIJ\xd6 \x9b\x85\xeb\xcb,_\xaf\x12\x92N\xc2\xaf$\x01\x9cr~iw\x14r\x16T'9\x8a\xdc| \x97\xecM6!\xc14\x0cC\x91Al\xc1)\x94\xa0\xeb\x9cF\x1c\xf0\xf3c\xaa\x1d\x00{{\x0f`k\xb8\x17\xc1\xed\xe1o\xb7\xff\xbc\x1a\x06\xbf\xedl\x7f=x\xf8\xe8\xe0\xc1\xfa\xb7\xdf\xfa\xd1\xe1\xd6\xad\xbf\xff\xfft\xfa{{\xf8\xdb(\xac\xdfhPhI\xa0\xc7\xbc\xe3\x0cS\x93sR\xff\xb0\x07[x\xceH\x12=.\xa9\xf3\x98\x1fS\xdb\x90\xc26\x12\xe8m\xd8\x1b\x95\x7f\xee\x8f\x90 \xffvyg\xbc\xb5\xb3\xd3So\xf2{\xb7\xbf\xae\xff\xbc\xcdi\xe1\xff\x11-\x8e\x86;;\x8b\xd1\x03\x87\x07\xcf\x14\xb6\x070\xf6e.\x8d2\xda<^|\xc8\x1a|\x97M\xf5as\xb1\xe4\xc7b#\xc9~\xf9\xcapo\x04\x87\xf5\x9f\x07\xd0\xfbDV\x06\x96D)\x06\x0d\xed\xef[\xdb\xdf\xaf\xb7\xbf?\xaa1[\xaf\xe3\x85\x89\xe1k0\x90\xaf\xe3E?)\x84\x96\x04=\x81\x84\xf7\xc3\x06\x1cd\x9dc\xa4\xa2\x82\x0dE\x0b\x89\x89g\xe4\xfd\xd3*\xef\xfd^\xa5\x11\xea\xcfI~F\x02\x93\x14x.\xa3\xe5\xbbG\xc3\xdf\xe4\x8c\x155V\x07\xe2O\x0bK\xf4\xbc2\xecl\xed\x99\x9fM-:]p*=K\xe6o\x11\xc1\x04\x06(~&\x9a\x96RE\x06\x04!\xa6 \xe4\x83\x0b\xf8\xb6\x9e\xd4\x1c\x85\xc2\x07r\xd8..\x8e\xf72\xe3\x14\xc3'8\xfd\\\x8e%\xab\xc62C\x17Y\xe7Ws\x0e\x83\xceP\xf63|k\xaf\xe3\xad\x15\xe7i\x83\xb3\x08h\x99m'\x82\x9c3X\xc12\x82yS\x0d\xad_mTPB\xc7\x8a\x0b\x1d\xb1r\xfe\xc0\xec\x87\xb1H\x9a\xb72s\x83\x06b\xa1\xab\x86\x8d\xdf\x8c\xa5k\x05r\xe5\x86\xef\xa7\x9c\xfbHm\x18a\xc7\x15~ma \xdeI_n\n\xedo[\xe2\xe6\x8e\xee@\xf1\xf7\xa14\xe0M}\xe1\xd0\xba#\xc7\x14\xb7I)\xb9D\x8e\xf4\xfb$%o\xe29\xf9>\xcf\xe6R\xa6y\x96\x14\x8b\xac@\xe3\xeb\x8f$\x9ex\x94\x95W\"\xde\xedi\x92\x12~l\x0fz\xc1\xf0_\x0fF_\x87\x0f\x0e{\xb7\x93>\xb9$c\xa3\xe1\x00\xcb\x9e\x08\xdb\x00g\xea\xebm\x94MT-\xd8\x88\x93\xaa\x9e\x82\xcdh\xb2\xa1F\xaa\x8c\xf9\x19\x94\x12n\x99\xa6m\x08-\xe2b\x1c\xa7O\xe3\x82\xc0\x00\x9e\xd6\xef|/\x07\xd9 \x1a\xd9\xc3\xd3\x80Tf\xe2\xdf\xfa\xc3\x7f\xf5o\x8f\xbe\xfe\xea6\x17%B\x93\xc6*\xa6 K\xfe \x1f\xf3\xb4\xb3\x07\x0e\x802vlK\x8b\x1d\xe3\xc2\x9a\xd0u\xb8ekM18\xd6{\x0e\x8dG\xf0\x19a\x8f\xc7\x9c\xcb\xe7\xd8\x92gi\x9a\xd0\xb3\xf7\xa4Xd\xb4\xe8\x86F\xe3$\xab\x14\xfe\xfd\xa4\xd0\xb4\xff\x9a:\x84/\x8dMcP?\xf6\xccoV\xfa\xa5\xbaCx\x97Wry\xc2\x15,\xceY\xf1s\xc2fAo\xbfW\xea#u\x15*:\xe9\xf5\xc6b\xf7\xf4\xf04\xfd\xf3*\xac\xb0\xd0V\xa8\xc1LlK\xd5N\xd0\x93]\x88&\x8dv\x12K\x1b|\xcb\x06\xd40.s#a\xa9|\x93\xa6.5v\xa1\x0d2CVA\x887\x9b\xb7\xf1dB\xc8\"]\x1d\xb3\x8e\xbaLmJ\xf3\xdeP\x86\xffye\x0eLi\xe0hf09\xd9\x15\xdaU\x1cQ\x1edC6\xc2\xbdr\x08\x13\x92\x12F\x80\xdf\xe1B\x0d\xff\x87\xf3\x03\xe2\x0dj\xcce`\xcaV\xabl\x03\x06\xb2\xa7\xa2!\xbd\x08\x89)`\xd6\x95\x19HV We=\x95Y\xd7r\xa6X\xad\x16\xa4k\xc1\x89\xb0Z\x94\x87\x12 \x1d\x0c\x84F|s\xad\x89\x08\x84}o\xdf\x00R\xc5\xect\x19$\xcdQ\xc2\xe0\xe2\x13\x88#\x15\x03\xebS\xf4\xbd\xf8\x90\x95\xfe\x1c\x1ek$\xbe\xb1\xac\x91\xd6\x9b\x15M\x1a\xa6\xbf\xfa{\xe7\xb2\x92\xe7I@\x83oL>\x12ctH\xba\xf7\xcd\x9e\xe1\xd9T~x\xef\x1b\xa3{\xc5B\xb9f|\xbbkz<)\x1f\xdf5=\x9e\x95\x8f\x8d\xe3:\x97\x8f\xef\xdf36>W.%\xbb\xf7L\x8f\xcfpV{\xdf\x99x\xff\x95\xfc\xf4\x8eqR\xa7\nX\xfbw8\xe2\xd7\x9e\x97\x04\xfa\xa4\xc3w\xe1\xd6-\x0c\xe1P\xbeU\xd2\xb5\xd8\x8c\x8b\x12\xa5M\xa5\xea\x9bQ\xf3\xfa/\xbe\xb0\x170\x80\xf2\x08lO\xe5\xc8\xe0\xc0\xd3\xad\xd9o\xc9\xc8fsL{\xb06`]ndv\xae\n\x047&on\xfc\xd8\xd9\xf8\xd6\x16q\xdaW}(\x95c\x0dtO\xa9\x89\xfa\xc8\x06\x86\xa7\xce\x91\xf2~\x17U\xbf\xfc\xe7\xd4\x7f\x18u\x07\xaeN\x16\xce\xa1\xf8\xd9\x8c\x8b\x18Z\xc4a\x0b\x8br\xc7\xda\xf8\x9dz\xe3wD\xe3NN\xbcn\xa2\x97} \xefQ\x7f\xc8\xca\x87\xeb5 `\xcfk\xc7\x88\x0e-\xab\xfd\x18\x9d\x84\xab\xfc\xdf\xb4b\xbfM\x9a\x15\xd0\xfd\x00\x86\xd4\x92\xf6\xces\xa3\xc1!h\x02AR\x04\x182\xc5Q\xd5\xcaq\xf9\xa05\n?\xb6\x06|\xfc\x0e\xf0\x08'\xf8i\xd6&\x06\x82{k\xd4l\xeb*`\xb3\xc5{\x99k\xc3\x1cR\xceY\x0d\xa9\xc1\xeau\xd5\xdc\x12\xeds\xef\x93\xc5\xe1\xb1s\x7f\x80\xb2\xa7\xc2#\xa8\xc2\xc4{?\xc5\xe9\x92\xc0|Y08%\x90\x92\xa2\x006\x8b)\xc8\x96\xbd\xca\xd9?\xb68fn0\xa6\x87\xf61\x9d\xa1\xc2=\x97\xc3\x12\x8d{\x0d\xeb\xad\xd9\x85\xb4\xfb\xb4@9\xf3\xf6\xbfv\x0e\x7f\x9bl\x07\xbf\xf5\xf9?\xe1\xa1\xb2\x0chRjc\xa01H\xb6\xc7gp\xef,>\xaf\x9b\x8d\xcecP\x14#\x01\xcf<\x87\xf5\xc1\xe4\x9b\xeb7&<\x95\xb6\x02\xe2\xf0)\xb4Cn\x9a\xa4\xc4k\x80\xaf-\x0e\xc5~c\xec\xb1|Iz\xb2n0?D\xa7qZ\xe87\xb6v\xb5\xbf\xf7\x14#o\x1b\xf5\xa9\xe8\xdek\xe0\xcf\xcd\xce\xd1~\xe3\x16\x835\xa8{\xecc\x93/\xfb\x0c\xedw\x9b3\xb7\xdf\xe0\x92\xe2M\xfc&\xe0\x9f\x95\xce\xc2\x8e\x95V\xcd{\x8d\xec\x8d\xc9\xef\xdcoTJ\xd8S\xa2F\x9fe\xaf\xb2\x0b\x92?\x8d\x0b\x12\x84\x11l\xdd\xfe\xd7\xf0\xcf`t8\xdc\xdd\xf9.\xde\x99\x8e\xfe\xfc\xf6j\xa7\xfc\xfb\xae\xc7\xdf{\xfbW\xc3\xf0j\xe4E\x18\xf8\xc8\xbd&\xfc\xde\xea~\xefOL+\xde\xc4\x8f\xce\x8b.\xbc\x86\xf7\xcc\x1a3\xb0\xf9\xf06 \xf9\x1b\x8c\xf0\x95%\xd2\xc1{|[\x94\\\xc0{rvt\x89\xfe\xc8\xae\xa5\x9dfi\x9a]\xc0Bv\xd2\x83m\x93\x03{\xfd\x0co\xc7et\x8e\xec\xba\x9c\xed\xad[\xb5\xdfv\xae\xd6\xc6\xf1\"\xab\x87\x94\xe74\x9b\xac\xa4RY\xa8\x17\x13\xda\x13N\xf2\xf8\x0b\xcdX'\x97\xf3\xb4\x87\xee\xf2\xda\xcd\x9eEU\x99T\xea\xce\x9c\xa0\x9b\xc2\xc4\xf6j\x0c\xc2;J\xbe^`\x84\x8b\xe8\xc8\xa2\"\x8e\xcb\xd5\xca\xedv\xc7X47\x97|\x8e\xa5\xf3\xb1\xf6\xa6d=,oN\xab79q\xb6\xbd\xb6\xa8^\x9bf\xf9\x8f\xe0,\x82\xd3\x08N\"\xb8\x88\xe0(\x82\xcb\x08\x8eG\x0d\xe1\xd59\xf6J\xdfd|\xc5V\x92\x0eYB\xe4\x9f\x9f\x86\xcd\xb9\xbf\x97\xb4\x1e\xa6 I'\x90\x14@3\x06\x8b<;O&x\x02\x98(\xb6j\xf4\xdc5X>\xf1\x8f0\x80WA\x16\xc1\xb9\xc3%\xe1#\x1a8\xc4x>\xfa\xba\x1a\x80\x1c\xc2\xa4\xda:\x93\xae\xd1|\x86\x01\xbc\xe7\xa3\x998F\xf3Y\x1b\xcd\xe7MG3\xeb\x1a\xc2\xf70\x80g|\x083\xc7\x10\xbe\xd7\x86\xf0\xfd\xa6CXV\x00q\x96\x1d\xe1\xa3\xf9\x03S]a\x91\x11\xfbh\xfe\xd0F\xf3\xc7\xa6\xa3\x19W\xa3\x19w\x8d\xe6 \x0c\xe01\x1f\xcd\xd81\x9a'\xdah\x9el:\x9a\xfa\x91\xd85\x9e\x9f\x1c^K\xeaB\xee&\xf8 5\xe41#;\x8c\xcbQ\xd8\xfc\x02\x0e\xe1\xf7\x00Uh\xbd%\x176\xca\xbbo\xc4\xdd\xe7\x82\x88\xda\xf9\"u\xc9\xd9\xfedsb\xa9\xc8l\xfd`\xeb\x9a\xdf\x8f0\x80\xd7\x81\xab\xda\n\xce\xee\xc7\x0d\xc6\xf8c\xf7\x18k\x87g\xd7\x10\x7f\x86\x01\xbc\xed\x1e\xe2\xcf\x1b\x0c\xf1\xe7\xee!\xd6O\xe8\xae1\xbe\xc0\xec\x8d\x9dc|\xb1\xc1\x18_t\x8fQg\xb0\xbaF\xf8k\xc7\xd0N\x91\xf9)\xd90\x9f\x81\xfe\xaax\xd6\xe74\x18\xf6\x12F\xe6E/\x02\xc1g\x8f0\xc9N\xcb\xcc\xdd\xe5\xe9\x01\x9a`\xd5\xb5\xed\xf8U\xc3\xa4_\xd1E\x82#\x0b\x86\xaa\xd6\x97P=|'\x1f\xeaT\xe0Wd\xc0\xf8\xd3\xe7\\\xa8\x8c\xa4\xb9]\xac\x83{\xb0\xfcJDVKC\xde\x95\xe6\x85\x995\x0e,\x99\xc4\xd4\xe5\xac7\xdb\x89\x13\x1a\x83\xdc\x85\x12/a\x00\x1f\xba\x91\xf6\xa5\x0f.H`\xbd\xf4\xa5\xc6V\xab\xb7\xc1{\xa5\x9dF\xc1\xcd))7\xa3/w66X:Az\x05m*\xf6\xb7\x0cZ\xa6\xf8g\x0e\xef\xdb\x97\xf3T\xea\xae\x98U\xbeK\x84\xcf\xd5\xe5<\xc5m\x8b\x7fa~\x12\xd7\x9a\x0b=\x0f\xff\x86K\xf9\xf2\xdb?\xaf\"\xfe\xfdW_\xe5d\xaa;\x03\xac\x16\xe8\xb4F\xfa\xb8\xaf\xc5\x9f\x0b\x91\xcf#!\xf2w\x95\x16\xe6]\xf5\xe4\x10\xfe\xf6\xf0\x907~N\xf2\"\xc9\xe8\xa0\xb7\xd7\xdf\xed\x01\xa1\xe3l\x92\xd0\xb3A\xef\xe3\x87\xefw\xbe\xed\x1d>\xfa\x8dJ\xb7v\xf8\xe5\xf5+ \x97\xb8\xc40\x8e)g>O \x9c\x11\x8a\xc9\x19' B\x94\xfef\xf5~R\xd7yY^\n\xa7\xd3\x9fsQ \xb8\xfd\xdb\xf1\xd7\xbf\xdd\x0e~;\xde\x0e\xbf\xba\xed@\xf6\n\x88\xb2\x84\x94'*C\xddXx\xa6,\xb5\x93\xa7\xa8/\xfb\xe5\xf5\xab#17\xe1J\xe2\xe3\x01r.\xcb\xaa\xd5\xdb\x13\x9b\xe0\xfb<\x9b\x8b\x8d \xdbk\xcfH)\xc5l\x92]\xd2%\xd9%a\x08\x87M?\x98\xa4\xf2\x83\x81\x83F\x8eJ\xe9\xa3\xa9\xa7?q\xba}\x9d\xcb\xcc\x86\x7f\x1at\x85 \x93\x17V\xe2|\x9a\x8d1\xcbN\xbf\xc0\xc6-\xfa\xa5Joi\xdbZ=\xa1\xa4w)MD\x16\x94byZ\xb0<\xd8\x0b\xfb\xc5\"MX\xd0\xbbe\xd2\xc6\x80\xee\x9f\x9eCB\x81\x86@\xfb\xb3\xb8x{A\xcb\xdc7\xb9pS\xc4(\xc3a>R-\x0e\xb8XE\x86\x132\xce&\xe4\xe3\xfb\xe7O\xb3\xf9\"\xa3\x84\xb2 \x1f\xee\x8e\xc2\x11\x0c \xe7T\xe8\xd6-0\xbe\xb37\x12v\xd5\x9e\x0f>\xa9m\xdd^\xb3v\x1a\x1b7m\xb5Z\xc5\xfd\xca\x97\xab\x81\xd0\xd6\x8cD\xca\xfdA\x0f\xb6MO\xc9\x90\x19\x0d\xb3\xfd\xdf\xb3\x84\xe2\xf2\xb4\xa7&S\xf5\xb8\x07\xa5\xe6S\xcb\xb9\xa1r\x17Sr\x01$`\x9a\xb9\"\x82\xde\x92Mw\xbe\xed\x85au\xb7w\x1a\x17\xe4\xfe]\xd3\x18\xaa\xd4A\xed\xae3\x0c6K2Z\x1c\xe3[6\xaf\x9d8]\xccb\xcf\\\x83\xa0\xbb\x8f)m\xe2\xac\x17\xe2\x16J \x07h\x9c\xf3)i\xcf,G\xb6yc\xce \x9be\x93k\x8fF|n\x1b\x8fz\xea\xcdD\xb4\xc7\xc8\xe2\xb3\xbf\n\x9c\x8d!{\x0f\xd2\x80\x99\x8d\x14S~\xec\x8c\xc9I\xa5\x8a\x8d\xe6\xe4\xc7z\xfa+_^b\xf5\x10\xd1\xd8\x96\x1c5\x88\xbd\xeao&x\xbb!\x8d\xf8\x06\x8dL\xfb3\x0f\xb5\xc4k\xfb\xbb\xb7\xcf\"\xe8m\xf7\xc2\x91\xdc\x9f\xa6%\xb5R)\xe6\xda\xd4\x86\x94]\xb5\x95\xb48\xd6\x94J3N\xb8f\x15\xe1\xa2\x9aSN\x97\xcb\xc8F\x1e#\xf5\x91\xd7a\xae\x94b\x96\xbcd^\x04\xd8X\xa0\x063\x8ektL\x9a\xb31\xa5Q\x9e\xcc\x03m\x91~\xc3\xecx\xbd\x13\xb4\xd8\xf4z\xae\xe1Z\xb2\xaay\x0d\x93\xc3\xec\xb4\x82\xd9\xc7\xb6{Yd\xc8\xe3\xe6\xd54ig\x9b\xe8N\xc2z\xfb_\x97;%s\xdd\xb9l\x915\xf7\xdc_9Bi\xffY\x97\xf6\xa5ui=ZK\xbb\xd8ZZ\xbd\xfc\xa7\xf2?\xd5\x83\xb2\x90\x16\x0d\xee\xdd\x0d\xfbO\x96\xd3)\x91\xde\xe2\xd7\xca\x06hN\x88\xd9\x9cfI\xa9\x8c\x92\x99\xc8\x15\x0f\xff\x7f\xf2\xde\xbc\xbbm\x1cK\x14\xff\xbf?\xc55\xa7_\x8a,\xd3\xb4$\xaf\x91\xedx\xb28\xdd\x99\xc9\xf6b\xa7\xea\xd7\xa3\xf2xh\n\x92\xd8\xa1H\x15\x17;\xae\xb2\xe7\xb3\xff\x0e.\x00\x12\x04\x01\x92rR\xd3\xfd\xde\xe3\xc9\x89E\x12\xc4r\x01\\\xdc\xfd\x9e@\x15\xcb\xf2\x13\xf1\x83\x9c\xc7\xa2\xfc\x17$\x0b(\x81p\x047a\x16\xe6\xb0\xc8\xf3\xd5x{{\xe6\x07\xe4:I\xbex\xf30_\x14\xd7^\x98l\xa7\xf4\xbb\xedi\x12d\xdb\xf8\xf1\x16#\x9fRo\x91/\xa3\xd3P\xc4nd\x94\x86\xcb\xf3\xb9A\n\xc7\x90\x1fA\xba\xb9\xe9@\x0c\x9b'`=\xf1\xd3y6\xb94Q$\x157\x97\xa2\xcb\xaeB\x1f\xb2:\xeaq5ED\xcd$\xed\x1f\x94\xb3\n\xc8\x99uG\xe2l\xa2\x99\xa4\x16\x1dS\xe5\x15\x98C[\xd2\x1a\xd8\x12\xc58j\xc4\xca\xca\n\xef\xbb\xc4\xa8'\x14\xd8\xe7\xa4\x1f\xac\x932\x1a\xf1#\x9a\xacB\x19\xcbcf\x1d\xa8nz\xf5#\xcb\xfd\xe0\xcb#\xba\x80\x11\x98\xd9\xb8\xe9/:r\xfa\xb7W\x9b!\xb7\xd0}D\xb3\xc2\xb8\x17[\xd6\x18\xfd\xf6j?\xc5H\xcfk\xb5^\xd4\xb3\xbd\x88\xa8=\xad\xca\xa8\xf2\x84\xc84'\x04\x8b\xac\xc3\x8c\x102x\x06{p\n\x19l\xc1\x1e\x8c1\xf3R\x00'\xb0w\x04\x01\x1cCv\x04\x01E\xe3\xd1$\xa0\x05.\xe5\xda&AKb\xf0\x1b\xee\xa5n\xb6\xa3\x86R\xdb3\x93\xe9\xac\xd4c\xc1\xb0\x8d\xe2:q\xd1\x16\xd0\xd4\xc4\x9eux\x8a\x03\xb75 \xdb\xe5\xdf\x1c\xdcR,h\x8a\xc3\xa3p\x8afOSzb\xc2\x7f\xd1\x9f\x05\xfd\xf9_\x90\xcc\x90Zd\xcfV\xecYV\xacV\x11=\x7f\xf2\x84=O\xf0\xb9\x0b\xe4\xeb\n\x03\x9c\x80\x1fC\xe9\xd8\xe1\xfd=\xe3\xa1\xbf=\x8d\xe8A\\z)\x19\xc8\xb3\xbch\xe5X\xc4EK\xde \xe7\xb2\xe8H\xe9\xde\xa9\x8b\x16\x97\xb0\x8d\x99\x95\xd9\x03\xdb\xacN\xe4\x0b\x1d\xf3y\x1eJ\x91~h\xb2taQ\xaeo\n9\x8f\xc2pQfP\x88\xda<\xf1\xc5E;?/\xe5W\xf3\xd6\xf2f\xd8\x1a\x82\xc5\xf5\xda\xe4\xd9\xc2_\x911\xac\x9aoD\xa07\xed\xcb\xa5\xbfzY\xbe\xef\x8d\x1ef\x88\x9c\x1ew\x06F\x18\xe5>\xb3\xf5\xe7\xb6\xb6\x87X\xbc\xd9Z\xdb\xf9\x8a\x9f\xf4<+\xb5'#V\xd0<\xeb\xdaN6\xb9\xcd\xae\xb3\xcap2\xb1V\x0dg\x8d\xae\x9f\xbf\xf2~\xfe\xca\xfb\xf9+\xf6\xf3WM\xd9\x94\xc7\xfb\xcfl\x8b\xed\x7f\xcb\xed?\xe1D\x87.\x9b\xb3\xadi6,S,d\xf6\x9a\xc7\x99\xec&&z\n~\xb3\xaf\x82+\x11|t}\xbb\xf2\x11h\x9c\xc7\x84\xfeu\\\x1f\x1e\xb3R\xa5\xef\x85\xfc}\xac\x8e_\xf4\x97\x16\xaa0+r\x1ae\xcen\xbb\x14>\x03\x06F\xac\x05\xdf}\xd0\x8c\xac\xd00]\xe2]\xce\x8f\xe1\xb4\x0c\x9e\xa7\x9b\xb0\xb5N\xe0}~\x02\xefK'\xf0\xbe\xee\x04\xde\xef>\x81\x05\xd5\x00'\x80\xa6+)\x0b\x9e\xc7\x8c\x1c]\xe1\xbd\xcb\xe2\xb3\x9e\x02QQpm`2\xe2\xe5\xc9\xe8\xa5\xe3\xb14u\xa2\xc0\xf6\x1b\xe7\xe3\xad\xcfl\x9f\xb2\x15 \x18S\x16\xc6\xac@\x88\x05<\x94\x97\xb0\x86\xebk\xad\xb1\xa2\x98&A\n\x0f\xbc1t\xb4++\xf6\xc2\xac\xec\x96\xfa\xcd\xa0\x16\\U7\xed\x99\x96\xfco\xd2ar\xf4D\xed\xec\x8b\x89\xa7P6\xa9X\xec\xac\xd5\xe44B\xda\xa6#\x87\x8f\x81X \xdb\x89\x95\xa8/\xb1\xf2_\xa5\xac\xe0\xbft\x14\x8aQ\xec\xd8\x8c;\xe2\xb4\xc2=2\xc9\x1b\x9b\xa0\xaf\xe0\xaeI\n\x02\xf2\xc6\x8b\xb4\x1b/(7^\xc4I\xdfH\"}g\x8c\xf4\x9d\xc11DG0\xa3\x1b/\x98\xcc\x9a\xa4\xef\xcc\x10\xd0i\x85\xaa\xa6\xc44\xe7\xb1\xbdj\x9ds\xbaf\x0b3\xfd\x84F\xd0\xf6\xeaQKB\xa2_3\xcd\x92X\x18\x96D\xd8E\xbf\xa2K\x00#\xd5\xfa,\x10fW\xc1'S\xef\xe7\xa3\x19\x00-#\x1ce\x0d]\xc4y_\xa5\xc9\xea\xa2\x1cS\xd6\xe8{\xb9\xe2\xb4\x99V\xca\x95s\x83\x91\xab\xca\xc8\xf5.\x92\xb8\x03\x97\xd3\xac<\xa1-,\xe1\x18\xe6G\xb0\xa4\x8b\xc4<\xa5\x18ZJE\xb27.,\xcbEL{9\xa1\xfd]\xd2_\x97V\x89t\x03\x13\xb5K\x81x'\x9f\x82\x08\xae\x12\x80w\x1d\xf3\xd0\xb1\x19\x85xC\x17.\xbb\xb9\x1f[\xb7`\xa2\xdd\x82a\xb9\x05\x13\xc7\xe5 \x10\xc1\x87cH\x8e\xc0\xa7\xd0\x0c'~}\xbb\xf9\xe6s\x0eQ\x07vU\x01r\x88:]\x16\x7f \xf3\x8d\xb8r\xb7\xab!\xa2[\xae~\xfe\xcaq\x84\xdaq\xf8\xe58B\x8eJB \x95\x14\x0c\x95\x14p\x0c\xe1\x11\x14t\\\xfe\xa4h\xa2\x92\xc2\xa4E\xe2(\x8cLrC \xe3^\xca\xda\xf6\xd2\x17r\x97]H\xfb\xc9NV\\\x08\x9a\x91 \x89\xa7e\xd7\x9c\xe6V\x8bM[\xad\xc9\xe6\xb6o5\x90\xa1\x8b\xe1~\xe5H=\xe5\xbe\x9b\xb1}G\xb1jP\xee;\x8a\x9cW\x1c9\x9b9T\x81N3u\xef\x05.\xcc\xca\x99G\xa4\xb8\xf5\x8c\x02\xc5\xa6\xe3\x08&\xb3K\xfa\xcc\xa9v\xa1\xdf\xc6s2\x8bi\xe3Nl\x92\xe5\xa0\xc5\x8a\x0fNs\xf5\xea\x0f\x98l\x9d\x9d<3\xd3\xe7\x92\x05\x8bb\xb7U1\x060\xae\xbdk\x9eK\xb1\xa9\"\xb4\xd1\xd2r\x15\xb5:G\x97Z\"\xee\xff\xa5\xd3\xfe\xb1\xc7y\xd1~\x9cO\xff\x87\x8e\xf3\x9b2\xcec%\xffi=X\xbb4\xebK\xc4x7-\x18o\xd9\xb5\xeb\xe9)\xbdTw\xfd\xc2\x85\x9b\xda\x89\x8b\x1c\xe2M\xf7Y\x0b=%J\x9d\xc6\n\xed[u\xd5\xdc\xaa\x95|G\xfeT\xfc\x925\x85\xcc~\xecQ\x8a\xa3\xed\x1f\xcb\x9f\x8c\xc3\xde\xf2\xb3,\x9cWl\x92\x1d8p\x1e\xc6\xd3\x94\xc0y\x92.\x8a\n\x01\xfdk\x14\x06$\xce\x08\xbc{sQ>\xfcq\xbb\xfc)tR<\x8d\xd9\x9c\xe4\x92)\xd7\xf9\xdd\xf2:\x89\xb2\xa6\xae\x8a\x97\xae%\xb9\x94\xbek\xea\xae\x1a\x1fp\xcb\xca\xbb7\xd9Y\\,\x19\xda9\xd2\xc2\xcdH\xc4\xe8=\xa9pS\xf3\xe6\x18\x94Z\xc3\x89\xdcp\xbb<\xba\x83\x85u\x93\x7f\x1d\x98|\x11\xc9\x04\xb1\x8e5%\x96\x0b\xd6\x1e\xb34\xd4\xc2\xee\xbd\xbf$\x99M\x9c\xc9\xe0\xb2\xb5\x0355\xf1\xef\x0fL)<8\x82\x18\x8eaH\xffR\x84\x97O\xac+\xba\x15X\x0f1\x0f\xd3\xcb\x85\x9f\xbeL\xa6\xc4\x8e\xd1t.\xd6\xf7\xd7\x1a\x0cG;\xbb{\xfb\x07\x87O\x99}KK_s\xc5\xa6\xadK\xc4\x95\xabq\x84\x00$\x0b5\xab=\x8c\x8bXw-I\x91\xe8\xc9p3\xb4\xb6\xb2\xd2\xb6\xc2\x94\xd7\xc4\xbb\x9aE\xfe<\x83'PPZ\xe5\xa5\x1f,\x08K\xa5@[\xd1\xcbxo\xcaLG\x154\xe8\x17)\xd1$\x80\x06\x11\xa7\x82%m\xc2\x82M\x9c@\xc6\xb2\xb8\x02\xed\xe7\xb55!zV\xed\xea\xc3Vm\xfb\x0d\x8fx\x1fO\xc2\x8e8\xea\x19\x02\xddw\xbc\xabi\xb2|\xf3\xaa\x9d\xa2f\x16\xb2Z\xaeN\xbepTGU\xd4\xd1\xe4\x08\xa1\x91`P\xfa\xf3\xf0:\n\xe3\xb9Yy..\xda`d'\x94\x8b\xecjP\\3\xdbw\xa1\xcd\xa3K\xbe\x02\x9e\x91FC\x08\xa8\x97Y\xe7L\xaf\xd4\xb6vF\x16\xed\xa7\xb1\x98A5\xdd\\\x12bi\xde\x9f\xe8\xd7\xe6\x9f\xf4\xdf\xeb\xb6\xc0\xb4\xb9\xb5\x19\xd1\x9aU4(\xbd92\xec~&qa\x96\xd7\xb0\x81%M\xc4\x03w\x7f#\x98\xda\xdb[\xf9)\x89q\xc3:\xb2vA\xb3\x01p?U\xc5\x0d\x83\x83jI\x91\xd2U\x11\x87q\x84U\xa4\xde*Y\xd9\x8e\x83\xd8\x8a\xf6Y\x98U>y\x02+z\x96\xaa(E\x90\xac\x7fj\xb6%\xb8\xe3\xfa8\xe7$\x7f\x19%\x19\xc9rq\xc6\xbcN\x93%\xed\xf2\x18\xa6\xaeZ\xb4Y\xa6\x9d\xfc\x12\xf4\xfeT\x1b\x97^\x82 \xca\x0b\x99I\xba\x84\x13y\x18\xc2\x9c\xfb\x87\xd5\x81\xd8\xe8\x1c\xfd\x86vLt\xb2\xabsa=\xfb:\x91Z\xc6\x98\xcc\xd6\xce\x0e\xba\xf2T\xcf%7\xba\xf2Y\x07\xa7\xc3V\x98T\xdc\x11V\xf7\xa4\xaa\xfb#\xae\x13\xd4\x8f\xda\xd6\xce.\xb6\n'\xf5\xb7\x86v\x8e\xca@\xfcl\xc5\xe4b\xc5\xe01!\xf7\xdd\x08\x7f\xa9P\x1b\x84W) \xe8\x96\xadvl\xc3nD\x14\xe1KC!ub\xf9]\xafe\xd3\nf&L\xe7\xd1\xb2\xe9\xc9Y\x1b.\xdd/E\x14\x19\x8d\xa5\xf5<\xf8\x02\x9f\xaa\x04\xa4\xdc\xc5\xea\xb0\xac\xbeR\xce{\xe6\x1d9\x06k\xe4\xedy{\x96\xaeMM\xc0\xe6\xab+\x86\x01\xe8\xdf\x13q^~+);\xd0\x19\xe0N\xac/a<\xa5|}J\xb2$\xba!,\xf7Z\x9ca\xae)z#D\xc8\x1ff\xf4n\x95\x92i\x18\xf89a\x9f\xacR\x92\x91\x18\xcbq\xf3\xffs\x9e\xec\x8de}{\x1e\x85~F2\xeb\xb2I.O\xac,\xf0#?\xc5\xb2\xe4\xd7\x82\xc4\x01~\xb7\xf4W\xab0\x9e[\x97\x1d\x92\x11#y\xe5\x82__ \xe1\x8c\xe5\xb9\xc8\x85'\xac\xcc\xe1\xe6}\xc3\xb4\xd3Z\xb6x\xd8 \x0f\x9d\xc1?\xcc\xd0w\xb7b\x1bS\xfb\x87\xcf\xf1\x978\xb9\x8d\x81\xa9.\xc0\xfa\x81\x13\xa8?X\x10f\xb0$9%\x80\x90KD\x03oHf\xac\x0cae\xfe\xf6\xfc\xdd[\\\x04\xde\x0f\xcaju\\\xc8\x17a\xe6\xe5\xfe\x9c\xae8~G'\x0f7:\xfe\xe0\xf1\xed\xf9;>\xa1\xf8Z\xfc\xbe\xbf7\x8b\x96@b\xd3\x15\xb3\x07^c\xb9.\x98[Ky'\xd7\xda\xea*\xa1\xad\xb5Z`,\xbctu[\x1fO\xb9\xf4\x18f+\xef\xd4Q\xf35\xc9\xc7-\xee\xea\xa5\xe4\xc5\x8a\x05k\x0f\xeae\xe5\x85\x8c\xec\x1cs\x1e\x95\x9f\x96\x1f\xf8B\x9e%hB\x8c1 \xaf\xb7\xb8\xaf\x08'\x9e\x90\xcb\x9eK\x93^\xfe\xa4d\xc6LR\x9f\xc6\x82\xf2\x1d\x17\xf8\x92\x0e\xab%-\xd6\x95ii\xe3Rc\x0b\xbb\\\x82b\x81W\x165\xf4@\xea\\\xd9\xbdx\xf4\n\x85\x8dvG\x8em\xdd~\xc9\xd4\xf8j\x8c+\x1f\xee\x1b\xd8\xf2\x1d\xc7cR\xdd&s\xaeM\xdc+\x99\xe3\xda\xfd\xfc^\xf8\x02G\x91\xdb\xfd=\xd8\\\xf6\xe6\xd3\xd9\x0f\xc5C\x1f\xf5\xb0cH\x1c\xdbb\xfda\xc6`\x92\xb3\xd4\x83\xe3ey\x82\xa9\x92\xd3>\xb0\xd1#\xfd\\\x0e\x15_\x0f\xdc%\x80\x19\xda\xb1\xbd\xb7\x7f\xa8\x06\xacO\xf8\xab\xa7CG+7\x08\x8dC\xef\x1f\xa3\xde\x10\x9f\xfe\xe1O\xcd_\xe5\xbel\x13\x89\x0bmD\xdb\xc1\x00\x1c\x81\xab\xf6}\x15\x11\xa7\x17\x81)\xce\xf1\xa5\xf0\xae\xfa\xb0\xb3Y\x90\x08\x05S\xb0Gz\xa5,_\x96\xf1}\x88!\xe1\xcc\xef\xfd\x8e`*\xed1\xd8J:\xb5`bH%\xeb\x19\xc1\xbck\x98\xe3\xa6@\xd5u-\xef\x1a\xe3V\x18%[\xb0\xbcj\x94EbHW\x8e\xa4\x9e;G|\x9c\x06\xe6\xb5_`\xb7\x90\xa7\x16\xf3\xb5\x88\x0e\xa0_\xbe\xaf\xee\xa0t\x1b\xe8\x18\x9bIi\xc6\xb2\xf64c\xd0\xb3i\xe0\xcb+\x14(\xd67W\xa7\x1f\x9f\xf6\xa9\xe0\xa1\x1a/\x1f\xd8\xea\xd4\xd0\xcd:\x91\xb7\xd0\xe6\xfayN\x96\xab\x1c\xf2\x04\xa6\x84\x1d\xf5E\xca\xbc\xd9\x84\xbdni`\xa0*\x03\xaa\xcdl\xf7\xa2^%:u\xbf\x1d\xc9\x0f\xf7\xb5H~4\xfc\xbf\x16\xc9K\x07\xa0^\x1c=\xdc\xd3\x82d\xf7\xa9F\x1a\x1d\xdb\x0d!u\xc1\x1e\xab\xa9M\xfaz]\xa3\xf2\xc1\x05f\xbd\xb2\x02\x0c\xe0\x0d\x99\xf7Z\x8f\xaa\xa6e\x81\xbf\xe8\x0b,\xca\x02\xe7\xfa\x027e\x81\x8f\xfa\x02\xcb\xb2\xc0\x0b}\x81yY\xe0g}\x81;8\x81)\x9cB\"\x92.\xd1\x99\xe5\xd9\x97~7e\x11\xbb\xc6h&\xa5\xb6W_\xe8\x8a\xd7\x9c\xc2\x18\x16\xf4/\xcb\xecd\xa7\xbc\x95\xdf\x1f\x9c\xaa\n\x03\x9b\x8f\x9a\x9ei)\"\xca\x1d:1\x98\x9a|\x03\xf3\xe0^)\x11\x8a\xae&\x11\xd3\xb1\x14\xf6\x1d\xaa\x7f\xe8h(\xb1\x1d\xc0)\xbe\x841\xaa\x81\\\xb8c:!\xac[k\xbf\x85\xa5O\xb14\x8caI\xcb\xd1JB{\x86&yc\x98c\x07\xb0\x9a\x13\x98\xc1i\x07c\x00\x12\x83_\xd1\xb8z\x0b?\xf9B\x96n\x11f\xb5x\x1e]\xe2\xd3\x0c\xf3#\x83\xad\xea\xd6\xba\xbe\xa3W\xe0g\x04\x06\xe3\xcerP\xb7\x8f\xd1L\xa1za\xcd\xc3\xf5k\xb6u\xf8\\\xbd\xb0\xf2\xd1c*\xd7\xc60\x92\xaf\x0ea\xb1Z\x996W\x99\xb8\xccu\x95b)f5C\xe7\xdc\xad\x94\xa3\xfa\x1a5\xdau\x90\xc4\xa1\xd5\xfebr\xd9r\xc3\xea\x02\x88\xb3d\xd47\xca\x86\xa8N\x91\x19\xae\xfe\xd7\xfc\x0d\xaa5]\xc0of.\xfb\xcc\xb6\xef\xbc\x1b\x96\x14\x1b7^u\x87\xb8\xc4a[n\xe6r\x8c\xf4\x89~sM\xff\xdb\xb8\xa6\xaf\x9e<\x01\xdf\xbev\x01\xab5\xa7(\xc9\xbc\xd7\xcci;\xf3\xfe\x02'0\xa2?\xce\xe1\x04v\xe9\x8f\x8fp\x02\x87\xf4\xc7\x0bZf\x9f\xfe\xfa\x19N`\x07K}\x86\x13\xd8\xc7b\x9f\xe8\xdb\xd1\xa1[\x93\xb70Q\xfc\xbaR09\xeeT\x85=n\xc3x\x9a\xdc\xd2!\xb1_\xde;\x0c2q\x82ZL8\x15\xef\xc7\x86\xcf3\x12a\x10e\xfaW\xfd\x14\xdf\x8dAL\x84m\x89\xd9^\x84\x99\xe5\xc8\xa6_Zq\xdb\x9c\x8b\xdb\xe6\xdf(n\xeb\xe2\xbc\\~b\x8f\xf6\xd5\xd3\x16\x03\x81\xd1S\x9eE\xcaN\xeb\x9cT\xda\xceI\xa5\xa6e\xa1e\xa0\xda=\x1aPBEx`\xb0\xb0\x96\xd9(w\xb5\xc7\x7fT\x901h\xd4\x83\xa44r\x1ak9\x9b \x89g\xe1\xbch)q\x9b\x86\xb9x[\x1f\"\x86\xa0g\x07r\xec\xd6T\xb1\xd0=wfym \xd1\xd8\xde\xdb\xd9Q\xa6\xa8\x9a\x91Z\x7f\xf4M\xeavH\x8d\xfb\xd4\x8b7\xe3>\xfd\xff\xc6\xb5\xa7\x8e\xeb\x8f_z\xe52j\x17\x15\xd6\x94%\xc3#\xc8\xb5\x860\xb9\xde\x10\xe6F\xcd\xd4\xa0\xb5NoDr\xeb\xb0\xea+\x0dUx\x8072I/\xb9\xf7\x94\x89\xe3\x01\xbd\x89\x00=\xa8\xde\xef\xef\x0d\x06\x07\xec\xfd\xfe\xde\xde\xce\x1e]I\xfc\xd7\x13`\xf2&z\xb7\xaby.*\x1c\x94\x95\x1d\xb2\xe7\xc3a\x95]J\x14\x1a\xee\x96\xa5v\x86\xb5\xcf\x87\xa3\x83\xf2\xd5p\xef\xa9\x03<\xbf\xd63\x18\x0e\x87\xbb\xc3\xe1\xd0a\x97\x04\xd3&T4\xbe\xba!\xcf\x02\x87\x9d6\xa11\x8a\xfe\x18\xc06\xc1\xb6 l\x9d`\xf9}\x07\x9e=\x83\xa1\xca\xbe\x8b\x8b\"\xbf\xbd\xfd\x9d\xd1\x80~5\x1c\x8cv\x10&FM\xaf\xce\xac\xb6I\xf5k\xd1\x9a\xeeS\xad)\xf8\x0dw6\xdd~bO\xfc\xad\xdf\xfe\xe5\x92\xfe?\xd8zz\xf9\xfb\xd0\xdd\x19>8G\xdbs\xc5\xe0\x8dR\xc5\xdb\xff\xf9/\xb6}:\xfe:\xf1\xb7f\xbc\xf0\xe1\xc3\xfd\xa4\xfc\xe98\xdb\xcaW,\xe7\xec\xeep_+\xb4n7\xc5R\xc4\xa5|\x88\x89\x1d\xf0\x14\xcc\x01\xe3\xd0w\xf6PO\x92{\x01\x1f\xf1\xf3\xdc\x1e\xe0\xb2\x88Dx.F\xabc|\xab\xaf\xcc\x946\x9f\x0c/\xeb\xb9\xaf\xe0\x140\x80\xea\x9b8\xb7\xf3\xd2D\xcf\x85\xe1>\xa5h\x1a\xaf\x86\xf4\xd5\x00\xe3\xb4\x16v\x8cD\x8f\x01\xcc+\n\xb8\xc9\x93\xe3g\xd6\xe5v\x1d8S\xe9\xcd\xbc\xfe\xaai\x02B/\xeb\x895\x06\xeb\x89\xbf\\\x1diB#[\xc7\xf86\xca\xb5/\x9f\xe1\xcb\xb9\xf6\xe5\x0f\xd6\x0f\xf4\xe5\xafE\x92\x1f5b\xd15\xa7\xed\xc6\x88S\x16\xb2\x11\xb6\xac-\xe0V\xba=\x84x\x93K\x06a\x86\x1eK\x9a\xc1\x85\xe1:\xfa\xe0\xd6dVR2Lq\x0c\xe6z#c\xb4`\x149H\xf8W\x06\xe6\xbeKum\x0coH/2\x89/y\xe4\x1bm\x19]\x0c\x91\xfa<95Z\xdb\xc5l\xc0=\xd2\xe9q\xa0[\x1368\x8e@.y\x04\xf3V \x11\xff\xb4q<\nSW~\xbe5\xcd\xa9\xeb\xdd\\\xf8xN\xd3\x9fE\xcc\"\x1d\xbek\xcfgWJ\x1e\x84b\xd4\xfa\xe5\x17\xcb\x81c\x18p\xcd\x16)\xe3,\x86.X\x7f\x1eZ\x8e\n\x99\x9f\xfc(\x9c\x9e\xc5y\x98\xdf\xbddf(>}\x81x3\x99\x92\x8fI\x88j\xea\xc2e\x9ajZ\x17\x96\x0eI/A\xb4\xd4\xb5'\x86\x9ee\xae\x9c\x18\x08\xbb\xc5\x06\xff\xd7\x1c\x03\x84w\xb6\xb1\x12I\xd80\"\x83\xa8v\xea\xc2\x8d\x0e\x19\xb51Ak\xc9\xd8\xa5\xa0\xd6U\xe0\xcbS)\xc1;\x8c\xf5\xf2\x98\xae\x1e\x19E\xeb\x0dn\x8f1K\xfb\xeai\xcbD\xeb{\x87Z\xd1\xfa\x81Z \x13\xad\x0fGj-\x8f\x93\xad\xbb\x92\xf4\xdc ^_t\x89\xd7o\xba\xc4\xeb\xcb.\xf1\xfa\xbcK\xbc~\x07'L\xb6\x8d\x923.\xe3f\n\x13!A7\x8a\xbc\xcd\xa2\xf5\xc5\xba\xf2\xf8+8\x81kI\xd8G\xbf\xb9\xae \xff~\xd7\xa5Q\xaaD\xechY)\x89\xd8\xd1+\xd3f\x82v\x14\x91\xdfA]\xd0~\x87\x82\xf6S\xb8\x831\xc4\x0eJ\xd4\xe9\xb1\x8c\xc2\xa5\x00\x8fp!&G\xc9\xb9Q\xa0X\x98\x04\x8aw\x8c\xc4\xb8c\xe2@!2\xfc\xec\xb8\x80\xb2\xc2\x0d\x9ee,\xe4\x02\xc3\x15\x06\x08\x10\x02y\xf1\xd6\xbe\xe2\"G\xa301\xf5\x02\xa6\x9eJ\xdc\xffi\xc1\xa2Y\xf5\xa5*\xb3\xb8\xeak\xa0\xaa\xc4\xf8\x06Uw\"\xdd\xa0\xdb\x96J\x00\x15\x9a}hP=\xdc\xf0\xa8\x01\xdc\xcc&\xc4\x1c\"\xda\x85W``KtM0R\xdf<\xf22*\x95\xed\x82\x85\x11\x15~\xec?\x9c\xa0\xe1\x0coH\n\xba\xec\xbb%\xf9\xe4\xa0U\xcd\x0f\x0e\x8fF\xf6\xactu?\xde.}\"\x9e\x19\x03\xfe\xaegP\xa7\xf1X\x8b\x99\xea3\xb7\x0b\xc7\x85\xd4N\xbd\x8f\xb0 \xa9\xf7\x1a~\x84\xa4=\x02\x83\xe0o,\x0b&\xe4\xd2\xa6c0\x02)gF\x03\n\x05}\x7f\x0f9w\x88\xa3_K\xd9\xe0\xeb\xc3u0 #\xc6O\xae\xb15\xddG\x15\x8e\xba\xeaU\xdc\xc3\xfa$_\x84\x95\xd1\xfa\x83,on\x9a\x19\xd0\xfab:\x0c\xa3\xb4\x1aq\xd5\xc0\x05r\xe3G\x8em\xb1\xc7U\xf5F# \xcd\xb1Y\xc9\xdc\x11\x93\xb1[\x1d\xaf\xf6\x9d\xa4\x905Q\xe3S\xdd\xe6\xfc\xfe\xa2\xc6^\x9e\xb37\"\x19E\xa3\x01\x91xb\xacMT\xb1\x08\xb3SV\x160\xf1\xf0j\xb9\xd0\x84\xe7C\x91\xd89\xf6\xb2\x15 \xceIDh/2\xcd#\xbc\xfb\xb7,i\x15\xf7\x89\xa3\xcc\xf4\xad. \x8e\xb8x\xa7}\xbb\xa0\x0cmi \\\xd7\x1e\xd25\xa8XH\xff\xfe\x80\xb1lb\x9d\xa5\x80|}H\xc3\xb1\xc6\xdeF\\\x0f\x18\xd5\xd3\xd4l\xeeB\xd8\xf7x\x85j0\xe2\xd4\xb8\xf5\xd3\xd8\xb6p\x95\xde\xa6\xfejE\xd21\x04I\x11M\xe3\x1fr\x98\x13\x16\x17\xd4r\xdc\xa6\x9fa\xb3 \xad\x17\x99@dt{\x0c\xfe\xa1\x86\xf4\xcd\x86[\"\xe3\xf2\xcdGiZ\x7f\x15\xaa\x9bO0\xae\xcd\x944\xcc\xf9\xae\xbe\xc9v\xbc\x81g!\x8d\x9fW\x0c\xdan\x17\x13f\xe6\xfe\x0f\x9d.\xeeU\x1d\x15:\xc1\xa7h\xe3\xcf\x08\x91J\xde\x8eqCE\x02l?\xe6\"\xf7\x0d\xc3\x88\x1f-R\x1c\x1d\xa8RBLy\xd1\xe4\xd1d*\xa0\xa4\x06\x18\xda\x96\"\xb2\x887M\x8e*\xa5\xfcb\xd2\xcaQ\xea\xa1\xa7\x0f\xcf$\x8f\xa6\x1f\xaco\xfa\xc4V\x16\xae\xbdL\x03[\x03\x03\xed\xba\"\x0d[s\xa9tx?\xd6\xfc\xb2\xdb\xcc\x7f\xae\x8b\xf9E\x92D2\xb3\xd9\xab}I\x90\xac\xda\xa7\x0b\xab\x1bu1\x84\xdcv[uZ\xf2+k\x80\xfa\x99-\x9f\xb23\xa6\xf1\xdc\x95\xa2\xe6\xd4\x0b\xab\xd1s4\x87\x13\xba\xb4\xa3\xeb1\xda\xe8P\xb4\x8a\xe4Qj\xc7\x8ekN\xdb_\x1e\x0d\xa2\xdaZ\x89\x1a\xe1\xfe\xd0h\xcf\x9a\x93\xdcb\x91j\xe8\x9cg\xe2\xae\xb9I\xad\xe7A@\xb2\x8c\x9e\x7f\x18\xab\xb9X\xd19#S\xd36\xb5\x90d\xe1u3\x86\x8c\x99\x87\x95\x0e)kn\xe4~Vb\x0dw\x84\xb5\xac\xc4\x1e\xd7\xa4\xbab\xbe\xa5\xc9N\xb7a\x83\xcb\x81\xce\x88,\xb6w\xf6v\xb5\x8a\x91}Uz[\xf0\xe2\xaa\xe7\x02J\x9f\xecCu\xafD\xac\xd1]u\xe4L\xf1\xaf\x96\x9ei\\\xadV\x18\xb0\xb3\x0eS\xb4L\x9b\x93\xfcc\x92Dd\xaa\xe6\x87Xh\xe4\x1a7%2)\x1f\x97'\xeb\xb2\xc1\x1d\x9cy\x98\xde\xea\x13 \x928\x08#r\x91\xfaq\xe6\xb3\xd2O\x9e\xc0\x0d0'\xff\xe1h\xc72YOP\xeem\xa2l\xdb8\xccY6\xcfq;\xe3\xc5<]\xc34\xbf+i\xdb\x8ce\x18\xc3\xbc\x18\xecX\xae}\xa5\x88\xa54\x82\xabu\x1a\xd98\xa9\x9a\x81S\xb0g(\xb5\x0d\x08%\x19\xcd\x9f9.\xdc\xdaH\xfe\x95\xdf\x9e\x18\xc3\xb0?\xa8t\xe6z\xc0 \xfc(\xba\xf6\x83/\xff\xbb \x05\xf1R\x92\x91\\\x11{<\x16\"\xf5\x9a\xe3$\x0fgw\xcf\xa3H\xad\xbd\x1a\xc8\xa5nI\xdd5\xe3\xff1\x1f\xe7j\x98\xd2\x9a\xb2\x9d6\xb8\xf2\x95\xebj\xfa\xd7\xd8\x07\xa2\x19\xcd\xba=i[\xd5R%\x1b\x83v\xdb\xa8\xeb6\xe35\xe2]-\x93\"\xce1\x15\x06lA.\xdf\xb7V{\xd5F\xdej\xe1\xa2\x88G\xeb\xab\x96\xc5\xfe\x18\x8ev-\xc4\x9c\xe2\xb9C\x7ffI\x9a\xdb\xd7\x8e\x0b\xab\xcd\xcdz%Ud\xba*\xaca\xce\xa3\x1a6\xd7\x0b\x17tR\x04:\x9b\xc4\x06\x0fQ\x1f\xe7\xe8jE\xe2i\x18\xcf_\xf2\xd9\xcb\x9a\x0c\x1c\xba\x156\x0b\x96\xb3_xQ2\xbfHVo\xc9\x0d\x89>a\x88'c\xa0\xa3\x1b\x1e\xbd\xd6\x90\x9e(\xf4\xae\x82\"MI\x9cs\xc6\x0c\xf3\x89c\x9e\x03?\xc8E\x1b?3\x16\x0b\x8f\xe4\x88\x8d\xa2\x11g\xcba\n\x03\x8be\x03,VS?',\xb8WD\x97\xd4{\x7fI\xe8\xaa\x14\x0c\\\x1e.\x89\x9dt\x19\xab\x00\x87F\xe6\xadH:K\xd2\xe5g\xac\xf7\xcd\xec=\xa1\x84\x85\x9f\xde\xd9\xa1\x8bF\x0d\xcd\x85\xcct\xa7 *n\xa5F\xcf\xe2)\x8b\x0c\xae\xe7>{D\xbe#\nf \xf1\xaf\xf4\xaf\xedO\x82K\x97\xef\xc2\xe2:\n\x03\x11\xb8\xc6V}>\xfe\xd4\xfc\x95\xd8\xb2\xdf\x19D*R\x9c\x93\\\x1a\x1b\x9f\x90\xac\x03\x8d\xf1\xad8oC\x87\xc2-4I\xfb\xe0\xc4v\xb4\x14z)\x89\x88\x9f\x11\xbb\x89\xa0\x1c\x03\xd6b_\xb6!\xa4Z\x9d\xba\x99\xee@v]\xa1\x86\xf8\xd2\xea&\xb6\xa1\x02i$\x16$\xcf\xd1\x89>M\xc6N\x88\xc2-E\\\xd0\x93\xe2\xd5R\xa1k\xd6\xf3\xa7S\x8a\x9c\xc3x~\x91\xd8w\x8a8\xef\xb6M\xcc\xc9\xa3\x0b\x95h\xf1\xfe\x1e\x16\xc6(Y\xb3\x0e\xb7:\xa1\x88\xbb\x93\x8f\x1c=\x86!b\xf0\xf6\x95HKO\xd7\xc2]9\xad\xba\xd4v\xdaN\x19{\xc3\xa8<}\xf3\xe2\xe4\xd0\x04\xb5\x03-\xfd\x08\xb9|\xd4\xd7\xd6tWG\x8d\x82\xa4\xb3\x06/`\\\xed,2V}\x81^Sn\x8cL\x19\xee\xcb\x9a\xeb\xb4\xcc\x17\xd3\xb2`\x97t,7^\xbd\xaaf\x05m\xfb\x84\xe3\xb9\xcf\x1c\xb5\x97\xe75\xd1\xdbP\xf2\x16\xc3\xec\x05m3\x8c\xe7\xbcQFFb\xa0\x81\x9c\x0b\xe8PZ\xe0]\xb1C\x03\x8b\xbfGm\x08\x17Ji^\x9c`N\xbc!\xd2\x98\xdaQ\xb5\x8ed\x16\x15\xd9\xe2\x85\x02\xd5[\x85\x19\x8a)G\xceT\xca\xcd\xe5\x88/\xf5\xf3g\x16\xb1\x88\x8b\x94L\xc3\xbe\xe5\xb4\xe2>\xbd\xb6\xb0I^\xb0\xfe\x08@\x9f\xe7\xa9\x9f\x93\xf9\xddz}9\xa0}\xd1gOQ\x00\\\x92T\x87\xf8\xc95\xdd:\xbe\xf2Es\xda\xc5GO\xe9G7\xfa\x91\xb5M\x9a\x9f\xf9\xab\x1e\xa9T\x03[\xb3\xe6\\N\x97\xf0[\x8f\xd5\xf5\xd2\x8f\x7f\xc8\xc5\xb2\x06?\xc6&@\x1cP\x10\xc6\xe0c\xe8E\xf25\x87\xdb\x05II\xc1\x87\xe2c\x08\x85\x1c\xaeI\x18\xcf\xc5\xf6\xf4\xe8\xb8\xa6%5\x80\xfds\x19n2\xb2>z\x81\xd6\x19>]C\xce\xb0\x11\xdb{C\xc7l\xb4\xc3q\xc0\x01\x9d!\xbd*\xe9\xf7\x07\x17,\xbf\xa1B\x02FytP\x06r\x13]s\xeaxU\x9c\x8c\x87G\xa84\xc5\xd3.O9\xcc~@\xc1\xf2T\x17\x1f\x07_\x8d\x86\xea\xab\xd0\x14h\xa2\xd4b\xa0\xcd_\x861!\xe4\xf7\xa5\xf6\xa4\xd3[^\xc8tUSWz=@\xd7\x8e\x95\xf5\x0b\xdd\x1d%U|\xaf$\xe5Q\xcf\xe4\xd7,\xe2i\xa9\xa0\xa9\xcc*O\xab1\x8e\x0d]]\xcf\x83\xe8\xbb*D\xc4/\xd9;\xb1\x1b\x18\xd2\xac\x9d@hW\xfa\xae\xd6)\xe3\xfd\x97\xc3JR\xe8H\x86\x00c\xd4\x03U\xddk\x9d\xc3\x7f\xc4\xfc\xad\xd1\xf7\xc7oG\xb3\xd4\x93\xb3\x97J\xc4O}S&\xfc\xd6 \xd0\x9a^Bgx\xfe=\xc6( T\x0d\x86\xe6\xaa\x84\x94\x0bTu\xf2T;\xb6\x9f:.L\xaci\x98\xad\xe8\x01\xf2\x12=\xa9-\x17\xac\xab\xdcOylVz\x1b\xfbyx\xc3\xfc+1\x96c\xf6\x8a\xcd\xf7\xc7\x94\xd0gd\xca\x9eRT\xee\xcf\xd1\x08\xee\xa5\xa94B\x1f\xca\xdd%j\xd8p\xdf\x18K\xdb\x10\x1d\xad4\xfb\xd3ft\x03\\\xd4\xa7\xd8i\x96\x01\x8e{\xe3Y\x0c\x00\xec`\xf0y \x8f=D\xc5\xecX\xfa&\x9e\xf8\x9a\xdc!\x0d\xe8\x08Y\x1d\xe6B\xf5\xd4Y\x87S\xdd\xc31l\xb08\x8e1\xb7\xde\xfb\xa9i\xbc(i\x84\xbd&\"\x80\x13\xa0\xdcU\xd8\xb0\x9aR\xf6\x1bZY\x89\xc8\x9d\x1a\xc4\x81<\xb1\xbe\xfc\x9f\x9acN\xedL\x96\\\xd5\xa7l\xc5\xfa\xf6J\x9c\xea=$L\xcdAmh&\\H \xd4\xd5\xda,\xc9t\xd5\xc4\xabw\x05}\xa1\xea\x8fl\x87\xd9\xf8a\x88\xcc:7#M\x08\xafM~r\x02h\xadf\x9e\x95\xc6\x8c\xb4r\xa7Y\x9e\xac\xa4I\xe9\x00\xda\xfa\x80P\xeaGH(\xcfZ@\xc1\xb0\xea\x0bD\xbd\xbc\xc2\xda\xa3\x13\xa6\x80\xee\xbd\xb8:\xc1\xb1\"i\x86\x99\xc4\xbb\xd7N\x98}d\x85\x19\xdaj\xb4\xd3\xd6\x8c\xfc\xadv\xbf\xd4J\xf7\x96\x9a\xd6\xa6\xa7\x07\xae\x84z\x0c\x0d\x96\xd1\x0c\xf1\x0f\xd3\x84k\xa3\xd3\xeb\x94\x15\x95\xd0\x9aebB\x146\x89//\xb5\x12\xd1j_;.dU\xe7\x98kc\xe6\xf9\xc5|I\xe2\xfce\xe4g\xbd\x1dNd\xb8\xa8\xbe'5\x1f.\x84\x8d!b\xda\x0d\x8fn\x10\x93[\xf5\x18J\x99\xec\xbf\xfc\xd0\xa9\xdda\"\x16\xf9A\x9d\x98\x06\x8c\xa6.\x8f3E&\x18\xfbR>f<\x9e\x8b\x98\xa4\x19\x908H\xa6a<\xafgD\xc8\x17$\xc6\x8d\x87\xc9\xd2\xca\xc3\x0fD\xe0\x17\x1fx\x03\x06e\xb88c\xb9\xc1@/\xd57\xffF\x18\x19\x18\xcc\x04\xf4S\x13\xb5\x88\x85\xc0\x0cCC\x8c\x9b\x1f\x84}n}\xdc<\x9b\xa6\x0f\xac\xa2\x16gp\xbd\x03\x1d\xae\xdb\x17\x0c\xdb=y\x82LO\xb9\x1e\xe4w\xcdC\xbe\x85P\xc3\xd0>\xde\xf5]N\xde\xf2l\xdd1FWA\xcf\xf3\xea1\x1cWv\xcb\xeaV\xfd!\x99\xcd2\x92\xff@\x97@R\xe4\x90\xcc\xe0:)\xe2if\x9a]\xb5MZ9l\x82\x8d\xb6\xfd\x03\xc7\xd8\x0e\xdbs\xfd\xdb\xc9\xeb\x99\xd1\x99!juO!\xd5@\nuE\x80\xae\x08n\xe0\xb1\xee1\x05\xb3\xbe'\xad\x88)oCD\xb4\x00\xcf|\xd8\xbaU4J\xe2\xda\xec\x8f\xf5\xde,\xdd\x04\xa1\xb84\x9f#@\xcb\xe8\x0e\xf7\xf7\xcc\xed\xde*\xf2\xd9a\xdb\xd4od^\x98\x9dq\xbca\xc7\x8ei\x13 \xd4bIh\x83\x1d\n\xac+%\xee\xd1\xed$\x90\xce\xd3\x01\xdc\xc3\x82M\x9c\xde\xe2\x10\xf8\xe1\x8a\xd3\x81\xc7V\xea8\xdem\x1a\xe63/HX\xa7\xdcL\x8d\xe1\x98\x11\x91\x84rZ$\xb9)\x1bUJi\x08\xfag\xf3\x04\x86t`\x18\xbax\xb4\xb7\x07O \x9f\xa4\x1a=\xd7Z#\xd4$^\x85r\xdd<;\xa1\xbc\x95\x89jy^e\x96\xf1#\x0c\xbfB\xf8\xce\x82\xc8O\xe7\x842\xa8~\x0cK\xffk\xb8,\x96\x90\xa1;\xc7\xe0+\xe5\xb3}9\xcd\xf5p\xdfAWNJ6i)\x9e\x12a\xdf\xf7\x1c\xd4\xa2u%J'\x8b\x9c;JH\xcb\xf5\xdb\xb4\x0f\x92\xd6\xdasHe\xbc0\xfb)$,\xd0H\xf31\x9d\x88\xfb{ \x06\x14/\xf7\xb4\"0\x9b\xbd\xd5\xb8\xd6W\x8c\x9e\xa5\x13r\x80\xb4\x9c\xdb\xa1\xc0\xa9\xcd\xb2'\x9a\xedU[\xbe\x1b\xc3\xa3#\xa7\x14\x0d\x1bOB\x14\x88Z~\x16\x84\xa1\xa5\x17\x8b\xb2\x12\x91\x9f\x87\xf1\xb0\xb5\xc8u\x18\xfb\xe9\x9d\xa1\x08H\x12(\xfdq\xc2*A2\xaf\xad\x95\"\x9fm\xb5\x96`\x84vg/^\xdb\xc41\x02\x1c\xaa\xe6\x82l\xd4\xde\x9f \xdb\xea(\x91\xcf\x86\xfb\x11\xe9*\xb3\xd5R\x08\xaa~\x8f\xe0\xc7v\x08.\xc8\xd7\xeeZbx\xf6\xec\x19\x18\xac\xb6\xf9t\xfa\x19\xd9\xdf\xed\xae\xea\xb7.@\n\xa32cE\xa8\xedpzO\x0cp&\xcc\xc6\x1d\x95;\xf5\xe8f.\xcf\x8f\xd6\xf8T\x95\xbe\xeb\xd1\xd7M\x1b\xc7\"\xf6\x16\xd1F\xc6\xe7riz\xfc\xb9\xe2\x10L{5\xba\x94\x98*\x83\xc6\xa1B\x01\xa4\xa4\x189\xc0\xb64\xd3h\x10\xb7\xc4\x94;L\x99\xf0\x1cOn\xe49\xe1\x99,\x91;\xc575\x11\x1d=\xdd\xb7\xca'\x87 b\xa1I\xcf\x1cV\xe1f\xecB\x98\xbd\xf7\xdf\xdb\xb1S\x16K\xf8\xe1\\\xca\xb7\xb6`\xe8\x08\x91\x80(T\xbe\xdcDZ?\xa6\x07 \xe9p\x84@\xcb\x95V8\x00\x8f\xfe$7\xdd\\\x19@\xa2\x8c`m1\xa3\xd7\xcc\xcdm\xf4k\xafk\xf9A\x8bH\x8c\xd9\xdd#\xcf>K\x93%\xe5\x15S\x07\x15\xc35\xae\xac\xc6J\xe5\x15\xfb\xb45\x841\xcc\x95\x15eX!Z\xe1\x13\xaf8\x87'H\xeb\xb8\x069\x83\xe9\xd0\xad\xc4\x17\x92\xf6\x97\xc7\xd9\xc5\x08\xa4\xa7\xadE*\xf5\x04\xe7Z\xb5\x85#?\xcb\xdf\x18>\xc0\xb1O\xf2\xcb\xb6\xd1ky\x97\x1b?* {\xc1\xae0\x08Q\xce\x843Z\xfd\xe8q\x15\xfe\x06d\x12\xb2\xf0l\x86\xd8o\x85\xb4p\xf5%2\x89\n\xd6O\xb1\x14\\\x95\x89\x14\xd8\x89\xc6\xf8\xef\xb4\x8a\xc6\x99*h\x14\xe9!~\xb8q\xa1\x15>\xe0gY\xfd\xd1\x96\xf4\xcc(/@\xb2\xb6\xa2\xd8GL\x18X\xddw\xee+\x9fEO-`\x9bEQ\xe5\x7fc\xfc\xab\xd9o\x8dG\x8a`\xd6\xd4Q\xde\x8dai\x92FX\x00{\xe2\xa5\xc4\x9f~~\x13\xe7\xc3\xfd\x17gv\x0e?\xea\xdc\x18\xf5\xfb\xdc\xa8E\x16\xce\x8e\xa6A#M\x87j\x98#\x08\xe1\x18\x8a#\x0877\xf5L\x19\xf0\xc6px\xa1\x83\xfdG\xad4OQ\x1cp<\x1c\xc2\x16\x04\xadr\x1dQS\xf9!]9\xb4\x9b\xa1\xe3\xb2\xcfa\x93\x03(+\xe7-\xa0\x001V\xc9\x91\xec\x16K\"\xc1j\x0ca\xeb\x84\xf7\xc6\xe5P0 g3lb\xd8\x84\x0c\x9eAQ\x9e$\x05lA\xe60\x7f`\x84\xda3d\xe6\xc2\xad\xad\xb6!\x97\xc4\xf3\x8c\x07\x0b\\1\x1ep\x05\xc7\x90\x1d\xc1\xaa\x0d\xe8P\x03[{>\x1cCz\x04\x9b\x9b~\x1b\xfa\xa0\xc7\x84\x9c\xf7\xa2\xb8\xce\xf2\xd4\xa6|\x82\xef\x02O\x8d\xa1_X8H\xa4\xd6\x8a\x8a\xa0\xf0\xf5e\xc9\x84\xee4f\xba\xdb\x03\xe9\x89\xcaz-\x9a\xeb\x8eE\xc3+{a\xbf\xa6\x1bJ^\x16\x0e\xaa\xe4\x9a&@\xa6\x96\xae\xfa\xb6d6\x18(\xeb\x94smM.]Y\x14V\xb2\xf2L\"\x963\x87K&8\"r\x02\x94\xb8C\xa2\xafK\xa8\x98\xaf;\xe8\xdb~\x83\xae\xc1\xa6W\xc5g\xfd*~a\xff\xb6~\xa7\xbf\xf6\xad\xbb\x97V\xa3\x92W\x96\xde\xb6|\xd6\xa4\xadF\xa4\xa0\x15\x1b\xb6\x9d\xd3\xd3i\x84i!\x1c\xbe \x19+!\xcd\x9f\xcf\xf9M\xcaO\xc3!\x8f\xdaL\xd1\xc6\xde\xbe\x0b!\x9b\xf6\xc4)\x7f\x9a4yF\x94\xfc\xf0\xad\x0b\xfe\xbc\x8d\x9f\xad\xb3\x10t\xd8q\x8d\xc5\x84SH\x91\x07yq\x97\x13\x91\xf1\x9dbU\xf5!WQ\xe5u\x9b\xae\xb6~\xbdl\xeb\x17\x05\xf3;?_x\xcb0.i\xc6\x1e\"[:\x9f\xe8\x1aq\x04 \x8an\xdb\xd0&\xa5\xbd]\xb4\xafu1F\x07\x99$-\xc9\xe5\x03\x11,\xc1X\x82\x9e\xe0\x11e\xa5w\x9e\xc2)\xec\xc2\x98\xdd\x8dv\xe0\x14v\xf8\xdd\xf0\xe9\x10Na\x04c\x93\xe8\x05iE\xd8\x84\x19\x1c\xa3\xb0O\xc8\xeffm4D\x9f\x04\xb8\x11\x1c\xc3ptX\x12rQ\x8b^ \x04\x9da.\xd2'-.m\x8er\x19\xc3\xa7#x\xc2\x88X2\xa1\x83\x1b^:L8@\xd9\x17{g\x08O r\xe0\xf8\x18\xf6\xe1\x1e\xf6w\xe0 %^\x9f\x89\x0cb\xd8\xdd\xec;t\xd7`\xf6).\xb9\x7f<3>\xde\x8d.]e(!\xf6\xbe\xfe\xcc\x97F4\xdc+G4\x1c\xc1=\xd8bL\xf2\x10}:\xc4\xd1`\xf7\x80\x7fw\xcc\x13\x96\xdd\xdf#9+%x\xfb^\xe3\xdf}\xfc\xf8\x8b\xf2ng\x0dh\xd4\x9f\x15\x06\x08\x1d*\x10\x92@\xe6\xd7AV8\"\xef\x1b\xad\x89\x82\x8c\xa5\x92\x1bI`\xd2\x0eQO\x12\x97\xc6X\x94/\xc2\xcfi\xdd;.\xee\xe4!\xc5s\x81\xdc\x9e\x1d\x94i\xe4\\H\x19>\x0f\x98\x18u\x00O\x00\xf3\xc5\xdd\xb3I\xe4\xdc\x0c\xcb%w\x0f<\x95\x1cer\xc4w\x18\x1bg\xf3\x04fM\x8co\xc2\xd2\xdd\x14\xc9M\x19\xa7\xa9M|\x8a\x8aq\x8a^\xbe\x94$\x9f&\x1d\x1d\xb71>\xe7b\x10\x9d\xde\x02$\xdd\x85\xa5\xc9V&\xaeT\xaf\x0c\x04(\xc3\xa2\xa4\xa8=\xa4\xc7\xeb\xe6I\x9f\xce\xf0\xe3&u\x99j\xeeK\x07\x11\x157\x81l7\x8eO\xf9.\xf7\xb8b\xe9\x84\x1e\x0e\xb9w\x1e%\xb7\xe5\x93\xf6y\xd8$U\x84N\x82\x12V\x0dC\xc0\xba\x95y\xa8\xba\xb37\x1b\x1e8\x90{o\xde\x9f\x7f<{yq\xf5\xee\xf9\xffw\xf5\xe2o\x17g\xe7t=\x0dL\xb2\xb8\x139\x89\x0e1\x98\x05\xe9\x9fwy\xf6\x18\x83\xdf\x0b\xdf\x1a\xc5di\xd8a\xa2R\xb3J2\x9fie)\xbd\x00\xb0\xe5\x18N\x92\x1e\x01\x13\xc4\xc5{\xb5\xdb\x94\x1f\x89K\x8f;\x1e\\\xd8\x1dqZi\x96$\xb6c\x14\x87\x12\xca\x901K\xd3'O\x84'x\xf9\xcc\x1eb\xc2\xbcJ\xa9\xd8\\\xaa\x9d\xd9\x0d\xf8\x1864\xb2\x93\xfa\xbab\xf1u\xbe\xbc\xf3\xbf\x96\x91\xa3|\x1b\x05\xcb\xab$\x89\xce\xc3\xdf\xe8t\x1e\x0e\x9fb\xf2\xa1+\xeea\xd3\xb9\xe2\xb5\x13[sJT=\xbf\xb8`\xbb\x87\x1f\x8cT\x7fd\xf3\xf0EZ\x0b\xcc\x16!\xb5\xec Y\xeb\xa3v]\xd1\x91k\xcb\xb8\x06\xfb\xc9st\xf5\xa7\x0d\xb1_\x18\x1cJ+!\x13\xdetY\xa9Xa_hmM\x98\xe1K\xdd\xd5\xad\xcd\xccAV\xec16\x08\x02ZGc\xdf\xd43\xd0\xc9\xb5\xd5\\j\xb5\xd0B\x0c\x933\x0c\xd2\"\xd5\xa5\xbc\x07\x99\xc4\x97FvK\xc8\xa5j\xc7\x83\xad\xcb\xb3\x0f\xdcV\xdc\x84\xee\xcc\xbd0\x13\xe7>7F1\xb3\x812\n\xf7\xff\xa0\xf9\xa3\x97\xcf\x8c\xb9Q\x13\xce\x19_\xe1 \xdf\xb1\x16\xa1Z\xb7is\x91J\xce\x1e'\xb0p\xa1F\xe9I\xc7\xe7\xc6\xa0\xfe.\xbb\xf5W\xc3\xfd\xb6x\x9d\xa0\x06\x0fh\xd3\x13\x11\xad\x9eH6\xd7\xe4=\xc9(\x89]\x99\x0e/\x8b(\x0fW\x11\xa1\x10\x1c\xeeo]\x87\xb9\xf6X\xac)\x1a\x06Gh\xbeK\x8e\xd8\xf2\x1b9p#\xe2\x9f\xba\x98\xb4R\xc7\x7f e\x82\x1cB\x04\x04\x10\xeb`\xd9\x19}W\xb0\xec~#XvF\x8f\x02\xcbn\x03,;\x8e[=\xa2`b\x7ftZ\xb85\xa0\xb5\xbf\xfb]\xa1u\xf8\x8d\xd0\xda\xdf}\x14\xb4\x0e\x1b\xd0:\xd0Ck_y\x9d\xe8\xda\xf9\x83F0\xcc\xe6LX}a\xfc\x16x&\x8f\xa7\xf2(\xb1\xfa\xd5\x8b~S\xb1Z\x890\x90\x90\x1f\xa2\x19\x1e.\xba>M\xa0\xd9(\x96>>\xa1\xbd\xe5w\x9d\x1f\xe3\xeac \xa4\x89\xe4\xcc%\x19(\x1b\xa5\x1b\xd0\x83\xee\x14\x17\xef\xc5\xc7j1\x9b\x9c\xac\xa0\x0f\xb5\n\xbd(Vq\xf1\xc6_\xae\xd3x\x1b\x9d+.^\xef\xf3u\xeam\xa5\x8e\xa1\x1f\x85,.\xde\xfe\x87u\xda\xef\xb4\x1d\x86\xaa\xe2\xf3u*n\xa1\xc6\xa1\x17E\x0e=\xa9rX\x872\x87j4\x17\xfdF\xd3I\xac\x03\x94v\xd1Z\xc6\xfa3\x8b\x0eUz+\x8e\xb51\x14\xd4\x8b0w\xc4M\xb0\xac\xbef\xd3\xa0\xa5\xc9\x1eD\x0c\x12\x1c\xac)\x0cI\x1d\xa9\x93_\x0b?j\x8f\x1f\x01ZiC\x87lA:\x0c\x85\x8df\xeb\xc1\xc3\xcf\x80\xfb{\x8e,KY\x88\xde/\\\x19E\x18g+L+\xd6\xefd2)F\x98\xffRC\xca\xdf\xdaqq>=\xe3f\xd3%]Q\xba\xf3 \x8e\xe4\xfe\x92\xde\xd2\xcf\x83\x85\xbd\xed\xfd>z\xd8\x9e;\xde\xdf\x930\xb6-\xb0Dx\xb0\xb22\x9e\xec\x89\xa5P\xf7<\x0f,\xc7q\xc1:\xe6\xf4\x06\xae+]6\xf4:\\\x0c\xf2\xa4N\xa3\xf6\xef?\xd5*\x8fW;YU\xcfmf{\x8e\xda\x11\x0e\x90\xb1Z.-\xed\xb6\x94\x17\xcc\xd6,i\x9c\xa8\xb9\xf0u\xa7'pY\xef\xfd=\np\x06,\xd5\x9cr4\xeb)>\xee\x8f\x9e\xd2G\x80\xf6\xd1\xa6\xf1\xa6\xf0\x8c\xf7'\xa7\xbfZ\xdd\x84\xaa\xf2\x9d.\x04Je\xe6RH\x07\xb8\x10\x97\xbf\xd2\xf2WR\xfe\xaa6_/\xf1^\x88\xae\x03[t\xf5`\x0e,\xd8\xa2\xcb\xa9\x90%z\xa1\x0b\xbe\xc3\xcc7\x10\x9c\xa5^0\xe1*\xd8\x9ae\n\xd3\xec\x0e\x8e`\xc6\x0ci77gf `4\x991 `0\x99\xb5J\x00i7ia\xd6KZ\xda\x8c\x83\x1f!\x01\x0c\xe1\x18\x8d\x90Q\x02\xe8\xc31\x84f \xa0\x8c\xa5\x82\xa8\x98\x92>\xb1\xc6\xa4\xb6\xb8q.\x82\x92\x9b\xe3\xdbf z\xd3\xba\x7f\xad\xc6\x96\xf5\x90\x1a\x98:\xaf\xad\x11\xc9\xe4\xff[\x1b\x1a\xb66\x84\x1e\xfaz\x0cf=\xbdp\xdf\xd4E\x10\x86\x1cm}\xa5\x10?X\xac\x0f\xda0@\\X\"\xe2\x87\x984\xd99\xba\xa8\xf1\xe5\x1f\x1a\x03\x03\xa9\x91\xfe\xd4\xd8t\xa6\xeacz&IB\x07s\x1c\xcc)\xf9\n\xb2x\xa1'D\xff\xde\xc1\x0c\xe5\xa5O\x7f\xce\xed\xa9\xf7p\xc2\xf5z\xc9\xda\xeeU\xadud\xaf\x17\x17Fu\xc3\x1d\xee\x8e\x96\\\x02\xea!\x9e`P\x9e\xe3c8\x84\x1f)\xfd{\n \x8ca\x08[\x908\x0e\xdahk^\xf4\x1a\xf0\xfb\xb5\x06\xbc;z\xba\xfbt\xff`\xf4\xf4;\x8dz\xd7<\xea\xbc9\xac\x1d\x1c\x16\x03F\xaf\xc1}\xea\xbd?\xbeea\x99\x96j\x0b>y\xf4\xfa|U\x1bQ[J\xc6\x90\xeeB\x04\xc0\xc0e\xa0v!\xe1<\xae\\\xc7h\x87\xbd\xa3\x10\xd8\xed\xd5\x87\xb7\x8f\xee\xc3\xa1\xa1\x0f{#\xf6\x8e\xf6\xe1P\xe9\x83|\x97\xa9t]\x1f\xfb\x1d\xe1\x15\xd7OI}\x02\xff\xfd\xdf\xc4U\x83`\xe6p\x8a\xa9Z\xfe\xfb\xbfs\x97\x9d\x14,\x0c\xe5&=\xb5\xcb\x1dBD\xc4\x11B\x0f\xf6\xf2Q\xeaT!\xc9\xec\\\xf9&\x17\xdf\xe4\xe57\xb9\xf4\x0d)\x9f\x10\xc7`\x03\xecT:\xcf\xd2\xea\x1aaa\x0c\x90\xb9\x96\xfc\xa4\xa4\xc0`K\x8d\xcb/\xae\xb8\x0c\xf3\x9b\x08q\x86\x81\xbb\xa81\xe7\x9cNH8\x19\x13S\"\x80\x0d\x04)\x00\xd2\x95\n\x07\xaa\x85V\xf7\x80P\xd8\x0f\x11\xd5\xe0\xedYO\xb9\x1a\xe1\x92\x19!\xb8A\xaaM\x90\x13\xb2|\xa3\x05\xf7\x89\xe56!\xdcgoX\x12G\x9b\x9bt\xd89\x17\xae\xffxB\xe9\x1e\xe7\x88\x13\xb5\xec\x1b\xd8\x84\xf0\x12~\xd4\xb9v\xebIY\xfd\x88_\xfccF\x0c\x9b\xb0\xb5\x95\x8bq\x1f\xe1\xd2\x1et\x0c\x97~\xf0\xed\x03>\xec\x83\x10\x84\xc6\xa9\x1c\xe3\xd0U\x15\x1cl\xe2\xfa\xb48\xdco.\xab^\x8d\x8e\x0c\x8drK\x0f\x04\xca\xf0\x12\xcf\xfc~\xfdhN\xf6\xb7\xf5\x03\xa9\x8dZg\xfa\xf4cg\xf4Hx\xec\xaa\xfd\xb0\xcd\x00\x91\x1f\x8d\xf0\x11\x8b\xf37\xdc?88\x18\x0d)\x17Q\xbe\xdf\xe9\xd9\xedG\x82\xaf\xd1\xedF\x1f(gc+#\x18\xee7\x87P\x1b\xd5\xcee\xab\x08\x9fv\xfb\xff:\x8c\x06\xcfN\xf8\xe7\xc3\xd1\xa1\xc3E\xe1[\x9cv\\%\xb76\xa5\x12(X\x1d\xc7\xedF\x07\xff\x10\xf4W\x03\x8c\x84\xdb\xd2\xcb#$/\x9bX0T\xb0`\xda\x0e\xa4P\x03\xa4\xd0\x08\xa4\xb0\x07\x90\xbe\x13\xcaD\xdf\xebr\xc5\xa3:\xefG\xc0\x88\x10[\xd2>@\xaf\xd3\x9e\xd8u\x0d\xe4j\xc4fM8\xde\x88\xd8\xaaF\xe4b\x84\xfd\xce\xe8`\x9f\x0e2\x86S\xc6\x08\x0d\x86\x07\xfb\x03\xb8\x87\x18\xc6\xdd\x14\xc8\x1a8\xfa\xd1\xc3a\x83\xb8\xaf\xa1\xf0?n8\xdf\x0f\xd5\xaf\x87\xe9\xebx\x92>\x1b\xed\xf6\xean?\xe8\xf7\xef.\xb6\xdc\xect\x0f\xe4\xde\xd5\xdd\xd7Q\xe2k\xb0\xfb\xe3\xba\x9b`\x95\x95\xa2ac \xb8\xbe^\xdd\xf8^Pktc\xd8\xb7\x1b\xaf\x92\xe2:\"\x8f\x04\xc7ag?\x06\x82\x01\xed\xd7\x8fG\xc2\xa3\xbb\x1f\xc3>\xfd@\xe6\xd9\xc8\xcd\x18\x848\xc8\x86n\x92\xda\x01\xc7\xacXPm\xfbF5 P\x0f\x93\xd8\x81-\x8a\xf2M\x8e(\x899\xc6_\xd8\xe2\xf4\x81\x1b\"\xafBN\x13AI\xc4\x8dc\x92\x15eD\xc4 \x10\xd8\x86\x84\xc9\x81\x8c\xe8\x8d\x16n\xc5b%$\xb5d\xc2?\x10\x921\x161BSc\xa4$AS\x88\xcfJ\x88nm%\x18 \x8e\x93\n\x1a\x90&\x02\xa4\xe1w\x03i\x83\xa8h\xb7`\xd1\x00U\x85%E\x16{{.\xeaQ\x8c\xf9~pv\x10\xe4\xb3(IP\xd2\xcd\xb1\xb5\xbc\xca\xb8\xc9\x7f\xaf\x81\xe8(\x90o\x1e\xcb\xc8e\x92\xe3\xb6\xd1\x9cj\xb6\x87[\xcd\xd9\x90\xcd\x19\x8aH)M\xf5\xf7Z\x03,G*=|z\x0e\xb27\xa5\xfc\x07\x0e\x92\x8fF\x1d$\x1f\xbbf\x90\xc3\xb5\x06\xa9\xa3V\xbey\x90\xbb\xae$\x12\xef5RF\xb3\x88\xd1\x8ev\xa5\xe1\x8e\xaa\xe7\xc3}\xc3\\k\x963\x85\xcc{\xfd\xf4\xb7\x92E\x12d\xfe\x80\xe9_\x1f2\x06\xa8\x0c\x0dP\x19\xe9\xd7\xccN;d\x86\xbd!\xb3\xe6\x11+\xa4\xc72X6\x8c\x06G\x02\xd57\x8e\x07\x0c\x1d\xad\x97\x9d6\xce\x96\x84\x1d%[\x1a7o\xbd=\x18\x9e\xc5\xfa\x83\xa5#J\xef#Op_:n\x88\x10y3\x89z\xc1~\nsLv\xb6\xd3\x01]\xe2\x97\x05\x86(r\x95s\xdf\xa6\xa7\x94\x0f\xcf\x9e\xc1\x80\x9e\xa3\xc5w9\xaf\xd6\xa4\x00\xfeO\x99\xe8\x16*\xe2\x9b&[\xcc\x85D`\x84\x15\x81\xb1\xf6\x8co\xfecf\xfc\x0f!P\x86\xa3\x03\x17\xb6\x86\xa3\xc3\xb5i\x14R\xd3!Q\xd02\x9f\x84\xe1\xb7\xd0/\x7f \xf9\xb23:\xd8\xa7cE\x19B?\xd4\xfe\x07\xd20\x7f \xf3\x88\x81\xfe\x81t\xcc\x1fH\xc6T\xf9\x10\\%\xedA\x8f!\xb7\xcfm\x0f\x12\xa7F\x12}\x13A\xf3\x07\xd23f\x10\xd5\xb7o\xcdHB\xec\xe2\x1eP\xfc'\"~\x0c\xf2\xa7v(\xbeR\xe6\xac\xcb\xab\xa2ji\xdd\xf9RZ\x1a\xf6j\xc9$Ejo\xea\xedc\x06e\x12\x14\xad\xd5T\xe7\xa8\x82du\xb7\x1e\xddR\xa5\x9b\x1c\xa0Cd\xe9\"X\xd9\xd5\xe7\x8a\xa7\x97\x94\xa5\xa42E\x90\x0b\xd0\x0f\xf3\xb2F\xae\xe2HK\x12\x10\x9d\x17\x98\xf7eWz\xa7\xb0\x11 \xa5\xea\xa0\xdc\xad\x8e*\xf26\xc3\x9b\xdcO\xe7$?\xcf\xfd4\xef\xce\x86Z\x9a\xf1\x003\xd6T\xba\xa1o!K\x8a4 k\xb4\x90\xb6\xf5\x97\xd5v\x16O\xbb\xebJ\xeb\xce\x17%\xf4\xeb3*\xd9_\xe5\x18{iK\x9a\xa8\xda\xcbM\xadU.\x12\xb4L\xbf\x95\xea\xe3\xd6\xe3\x1cTn\xa8\x18t\x99+\x07\xb1\xc5\x96\x904 \xb0t \xc3#HxV\x83\xad-4\x0bK`\x13\x10I\"\xae\xa3w\xba\xb8/\xa5\x93\x11eA\x86d\x07X\x18\xaf\xf5\xb2\xfe\xb105\x8aY\xda\x1a\xedk\xf3\xb9d$\xaf\xf2\xb8\xd4Lubf\xf6\x14:\xfa\\\x98B\xef\xd7\x86\x08fa\x14\xad\x87\x084NWkg\xb6\x16\xe9 0\xa4\x06?6\x95\x1d\xa2M\x9f+\xe1\x85\xe6'.\xcf\xba\xd1\x95\x19 $\xde\xaa\x16\xb0\xdcdy\x04\x18\x80\xe8\x18m\x8c\xc5Am\x88\x8ff\xce\xb7\xaa&\x9b\xd1\xe4\xc33\xf9\xb3\x97\x19\xbf\xfb&\xf36\x80\x1d\xdb\xad\xe7\x02NM^\xc5&\xcf\x8fF{\x95\x12`:-\xc9\x9b)\xcb-\xe2T\xe9\x17a9\x00n\xab\x87>\xca\xb5A\x08\xbc\xe8OB\xf8_P\xaca\xb3\x977b\xe4\xd4\xfb@\x07\xfb\x19N`{\xf2\x9f\x9b\xbfl\x0f\xb6\x9e>\xdf\xfa\x0f\x7f\xeb\xb7\xad\xab\xcb\xed\xb9\xc9\xf5\xe6\xd7\xf6\x10\xae\x80\xca\xd9S\xb0\x06\xe8\xf4_O\x13:V\x1e\xd4\xfbfh\xf0\xb5Q\x01x\xa3\x0f\xd0\x96\x03\x8f\x8a3\x84\xed\xce\x1c\x97\x95\x83L\"\xc2\xf3\xeb\xf2:\xb4\xa7P Y`\x9bFb\x07\x07\x9ea4\xef=qD\xef\x1d\xec\xec\xee\xb6!\xdc\x90\xe7\x873\x97\x80r\x93>\x83\xbd\xfd\x9d\xe1\xd3\xae\xc2\xf4b\x89(vh\x7f\xb6\x86\xb43<\x99\xc4h\xe7\xa9\x0b\xc3\xa7C\x17\x86\x87O[\xd0\xba\xb8\x82$\xce\xc3\xb8\xd0\xe7R\x12\x979{\x10\xf0\xbe\xfb R?\x19\xa5z\xf2\xf5O\xd4{\\$\xed-u\xb6\xd2\x9e] \x97\xc9\xfe\xce\xc8\x98BP\\\xfd\xa0\xe2\xfe\xc1]\x8e\xb9\x8f\xc6>lR\xban\x8b\xa7 8>\x86!3t\xd9\xe2\xa3\xd1\xd6\xc0O\xc5\x84\xf3==\xc6c>\xc9\xab\xfd\x1b\xb3D\x15]\xfb\x8c58d\xd9Y\xba\xd2\x1f\xf0\xce\xc4\xad\xe3\x10\xf37\x1a\xec\xf6l}\xb4^\xeb\xf0\xec\x19\xe62\xc0\x00\xdb\x98\xd0 \xa6w\xa3\xc3^\xdd\xc2y\xea\xd7\xaf\x9d\xf5\xfb\x85I\x17F\xa3]\x16\xc2\x03\xf6\xe1 \xed!\xf6n\x8d\xbev\xa0F\x1c\x07O\xd9\xa0\x8b3 \xd2i\x05\xc9\x94\xc0*1x\x91\xc9U\xb2\xf1\xee>b\xbc\x87t\xbc\xbb\xe4\xeb*I\xf3\x0cN\xe0\xf7\x07\x89v,\xc1\x106<\xd2\x1b\x9b7#\xf9E\xb8$I\x91\xc3\xc2g~\xa0\xd7\x84\xc4 B\xe6W\xf0~\xd04\xe0w7\x10D\xc4O\xbf\xa1\x89\xa2\xb9\xe0\x19n\xc5\x18`e\xef\xab\xe8\xc2\xe5#\n>\x95o\x16T\xe3\xc9 \xf3\xe2\xda`\xf9\x8e5\xf5\xd0C\xb6z\xecv\xd4\xab\xcf\xb7!\xaab_\xd4\x97\x81\xc8\x0f\xa17\x955\xa6\xef\x10U\xb2\xa5SF\xcb\xd79\xfc\xb7\xb6\xd0\xac\xab\x94\xd2v\x07\x0f\xa8&l\xa3Z\xac\x8d\x95\xa0\x1d\x03f\x9d\x11\xdf\xc8\xbc\xa6\xb4\x10O\xe5\x9b\xb1\x8av[\x13k\xd0\xeaU4-\xdf\x19\xe6\xc9\xd4\xa9\xda\xe2=\xad\xdf\x8e\xd5,\x89\xad\x1d\xa3M\xa8Y\x15\xcb_\xb6\xb4\x9a\xe8\x1e\xe7\xa9\xcd&Jb\xb3\x00C\xbf\xd4\x9f\xcdx\x12\xda\xe6\xc6Y5f\x04\xb3\xb7b\x1a\x0b\x9bW\x05\xa5X\xe0\x14[\x14\x01\xc4\xed\x08\xc3\xa7b\xdd.D\x92\xecuj;\xed\xfbu\xdah\x16\x89\x88\xc0\xc4L\xd2\xb3\xad\xb0W\x1a\x8a\x01\xfb\xd8\xc6KR\xa6S\xf4\xed\x083\x11\xe9\xd79~@\xb1d$\xe0\x8aA\xc4x\xf6\"\x9e\xf2cv\xe9\xa5El\x9b<\xfc8(\xe4&;v \xf0D\xcfl\x8f\xea\xe6N\\\xfd\x8ev&T\xa7\x98K^\x86U\x1a_\xe9\xa1\xdd\x16P\x12Q \xab\xc8G\x14\xc8b5h+\xa5\xabV~\xe1\xf6o\xc6\x8c\xc2\xc4\x95\xda\x06\xf9\x12\xf4\xc2^\xe2\xean\x08d\xf2K\xc6\x9b\xe6\xe6a\xad.@\xa3\x01\x8eL;\x1a0\x8f^\xfb\xe6A\x05\xd8C\xebN\\h\x858(\x0b\x9c\x15(9\xe1B{\x96\xe6\xe8D\xcaZ\xaa\xab\xee\x86n\xec\xaa\xc5\xc4\x8b\xc9\xd7\xfc\"\x0c\xbe\xb4\x12\xa7b\x9fR\x8a\x80\xd1\xbc\x8d\xb8\xcdM\x93!\x94W\xa8\xc5\x9e\xc1\xb0 \xce\x12\x17\xc4\xcc'\x93\xb2*\xea\x97G\x10onRr-f\x86XR\xe8\xe8F\x98\xfd\x883\x1b\xe4V\x80\x0fe\xf7\x98\x15Z\xa2\x07\x03\xfa_aO%T\xe8\xc2B\xb6\xabG\x00\x9b\xcfF> <\x1c+[\x8e\xd5\\\xd4\xaaM\xbc<\xcc#\x0cJz\x9d&\xb7\x19I-\xfa\x90\xff\xe6a\xf2\x13\x8f\xc47H\x07\xd2\xdf~:\xbf\x11y5\xbd\x1b\x92ft\xfeX$\x93\xf2>+K\xe3\xbb\x1b\xfcn:}\x1bf9\x89\xb1\xde\x1b\xf6\x12\xdd\xd1\xd9\xef\xd9L\xfcL\xc92\xb9!ja\xf6\xf4y\x14\x89\x17\x99xC\x96a.~\xafR\xb2\"q\xa3%\xfe\xf8C\x1c4\xea\x8d\xa4\xea\xccK\x8d\xef\xc0\xc9e\x1dz\xd7a\xdc\x99\\\xa5A\xb5\xae\xd2$ YV~\xccC\xa4HA\xf1\xea\x8d\x04\xb7\xd3\xb6\xf9\x16\xac\xd2\xb6\xa5|\xb6\x98\x86\xe9\xe3z\xc6>\xed\xeaW\xb1\xf4\xb3/=z6\x90\xb6>h\xb8\x10E\xc5o\x15\x19AEO\x90KL\x9c\xcc\x90\x98G\x84\x1a\xa0\x8a\xd8\xda\x90Uu:}\x0f\x06\xb1\x15\x03\xf5\xcb\x8aU\x19C\x83k|\xc4@\x9aH/\xd5\xe2\xd0\xca\xbe\xe6\xa4\x0bk&f\x94\xd8\xc0p\xc7'0\xa4\x88E\xd2\xdeT\x98jx\xc9\x835\xc8\x8f\x9a\xf4DlLx+duZ\xb0\x19\xd7\x07\xa8\xc2{\xb5\xd7Lt\xcfP{\xea\xa8\x02|\x9fb\xdep\xe2\xd7\xb1\xaeof\x961\x17\xd6\x86\x88\xa2\x19\x0b\xd0 \xc3&\x91\xa1\xa1GnHzW\xcb\"\xdd\x95\xda\x0c\x19\xb7x\x92^j\xf8\x1bts\xb1\x19W\xcdp2\x9b\x04\x17B\xc7a:\xb5\xd05s\xf2Z\xde\xbb1\xf15\xc2\xb5 \xc7\xb8\x84cN\x0f;8\xc5\xe0\x14C\x1e\xd98e\x07\x1c\xcb\xb9 )\x85k3\xa9\x9d\xe4-\xa0\x16\x97\x00]\xfb\xa6\xef\x03}6\xc4Y\x9a,[Yv;4\xcc\xc3\x83\xf1\xb8\x8f\xbc\x94dE\x94\xbf.\xe2\x80\xae%\x17\x9f\x04\xc9rU\xe4~\xce\xd9\x94\xce\xcd&6Z\xe3\xe5\x03\xab/#\xf9\xa7GWJgH[q\xed\xa1L\x0c\x88_\xb9wuE\xb2w\xc9\xb4@\xf6\x8d\xf2i\x98:\xd6/\xa2\xfc\x1dY&,soB\x9f\"\xda$\x02\x8b\xbedH\x94\x11\x1d\xe5\xcb<-\x82\xbcH\xc9\xb4D\xb6}\x18\xefGP\x99\xbeBe6\x99s+\xc1<\xb8F\xea]\xc8\xfeM\x1dg\x87C\x06\xb30\xcd\xf2*^\";\x18\xfc\x18X\xf5p\xbb )\x01\xe2\x07\x0bX\xf1\\\xbb\x94\x11\xf0A\x9c%\x9a\xa3\xc3Gk\xb0\xb2SG\x0d\xa0\xd0\xbd\xc6\xd3\xf8~!wYC\x88UR\x8bq\x1dU\xb5\xf9\xc3\xd3\x0dY_\x0e\x8e\xdb\x93\xe4\"Z\x84\x9cW\x08\x81\xd3~\x03F\xfb\x11N\xfb\xe5\x93\xb4\x9d\xee\x03i(^J\xa6E@l\x85\x13\xea\"\x98\xc9\x84R\xcb\x97\xcc\x18R\xa3\x8es\xe1\xf7\x07E %\xb1\x9fu\x91\xb6\x8f\x04L}\x99\xd3\xf5m'z\xb5\x97\xc2\xa7 \xee#\xb6\x87\xc3\x03\xe5@D\xc6\xc6\x1e\xed\xee8zV4\xb6\x87\x83\x01\xa5\xfc\xda\x1a\x00Y\x84'\xd2'$6Z\xabK\x83\xea\x91TLZ\x12\xcc\x18tM\x96\xb4\x1a\xea\xc1\xaeaD\xed\xcc\xf5\x86\x1c\x0b\xd5\xc4G\x8b=\xb6\xf1H>Z\xedq\xac*$\xeb\xfb\x8e\xc9\x9c\xc6`\x8d\xbc=o\xcf\xd2\xad\x12\x8d\xfd\xe1\xd5\x153\xd4\xa4\x7fO\x84\xdb@o\xf0\x8d\x0e\x0e\xd6\x86\x9f\xcc\x85\xca)\xe7j\xb2\xeau\xa7Q\xbf`\xf7\x0ev\x95\xe7!\x7f\xbe\xa7<\xa7{\xc7\x9ap\x9c\xf8\xbe\x88\xa2K%Tx!\x17\xf8,\xd2\x9d\xab\xa524n?E\x13\x04f\x0fx\xe1\xcf\xcb\xcc\xde\xdf\x01R\xd2\x89Bo\x0b\xcc|2\xe6\n\x16\x08c\x8ev\x99q'\nF\xc6\xc8&?\x16\xb0{OGz\xc8>\xdd\xeb\x9cx\x0d\xbd,\x96q\xc2\xdej\xb7E\xca\xb2\\\xc4%\xd8\x1e\xdb\xf7\xd1Su\x96Y\xdf\xf7w\xd41\xb1Uqp\xd89$\xc3\x0c\x85\x0c\xde)\x83w\xb26\xbc\xf5\xb2> !\xef\x0e4#\x91NXJl\xb4\x93\xd4\x82V\x99h\xce0\x89s c\xa42\x84U\x98\xf9\xbc\xab\xbdx0\xc0\xad>\x96\x90\x1f\x14\xfbR\xb5\xa1\x17\xc6\x0b\x92\x86\xfc\x149\x1c:\xcd3-\xb6w\x06\xeaL\x16\xac\xae\xda*\xac\xea\xb2g.\xf8\xd2\x9br\x80\x19\xae\xbd\xa2\xd2\"\xf0\x14I\x83#\x88\xe0\x18*uFD \x80\xe6\xda\xa5\x04t6\x89\x14\x18\xce\xaa\xfa&\xc1%\x8a\xb9\x94G\x94)\x93\x1f\xb4\xebwg\x86C\x879\xc7\x88@\xda\xc9\x0cfU~IJ\x12\xce\x1a\x84\x96_W\x95\xb9P\xa8\x0f\x10\xfbo\x08\xd7\x89\x94\xf8S\xff:\xe2\xb1c\x17aV=9a^\x80\xf5\xf2\xb7i\x98\xd7\xcb\x97Oxy\xa6q\x89\xa2\xe4\xf6\xaf~4\xfb\xb0\"1'\xd3\xeb\x15\xd5K\x94\xb55>,\xabL\xe2\x80\xd8\x16\x89\xa7\x96\x0b\xabvp6\xb5\xf4\x9a\xba\x85\xc3\xc1\x95\x18\xc0y\xee\xe7\xc4#\xf1\x94L\xe9\xcb\xb4\xd4\xc5\xd9S\xd6\x85.\x1d}c\x0e\xb16[E\x0d\xf4\xe2;\x99\x1d*\x1f9\x19.\xaf!\x17,\xd1\xaf\xbf\x86\xf3\xc5\xcf~N\xd2w~\xfa\xc5r\xd56\xe2bIRZn\xdc\xd0\x85\xcfI>n\xa7\x98\xc5\xe6\xd6\x00b!7[\xdf\xfc\xd5\x80\x1c\xb7\xd7P\xa6$\xcb\xd3\xe4\x8eL\x1b\xdd\xef\xddE\xc9\x9f\x86\xf5V\xacS\xec-]@\x8d\x12\xb5\xf1TK\xac\xfe\xa5W\xf6\x0d\xbd\xce4\x80(\x0b(d\xb9B\x08\xd4\x06\xa2\xc7\xc8\x7f\xfc\x10*\xfd\xb3i\x10\xb4\x88Q\xe1M\x19,I\xe1z\xc5\xbf\xea:\xe4\xb1Av\x80\x14Q$6,\xae}W\xdeGyM{\xff]\x0e\xca\x9d\xe1\xc8\xb1\x1f{\x8a\x93\xca=\xabT\x91t\xd1\xe8k\xf6o\xff@w\x90\xb3\x10\xf7\xfe\xd7G\xf6;\xb1\x07.\xd2\x1e\xdf\x00\xccu\xcbk\xa9\x94\xa1flvl\x1f:]\xf2\xbe\x90;~z\xe2l\xfb\x98$\xc2\x16\xc0\xc4@\x0b\x82\xa6\xf9\x1d*8\xf4\xb2;\x19\xc1 \xc3Pz\n6\x05\xd6F\x0bez\xd0\xd2\xef\x1b\x86\"\x1a\x9a\xb2}\xd4D>\xca\xf1h\xa7\xe7\x8cm\x8d\xf6,t\xb7\xc5\xedVP.\xde\x16\x9bH\x03\x1f8\xe6\x1b.I\xa2\xf3\xf07R\xe2\xad:L\xe8vl\xa4o\xad\xdd\xfa((\xab=*\x1a\\&\x16\x9cNi\x9d\x94\xb9I\xc6\xed\xa8@\\%\xfb\xda:-q\xad\xcf\xdc\xba\"\xf6\xe6$\xa7\xf7\x88\xac\xd0\x01\xca\xa7O\xcb\xf1\xa2czu{\x02\xc3\x81C\x0b\xa4$\"~F\x98\x84\xaf)\xa1}\xd0\xa8oc\"\xd2\xa9b\x83\xe9X\x05\x08\xbd\xf2\xdbD-\xd5\x0b\x06\x8fY\xe4 \xeb\xa6\xd6Y\xe8\xa0[\xec1\x8b\x10\xe0\xe8\xc0\x01\xda5\x0f\xbauO\xab\xe8\x03\xce|\x91\x92\x06@\xbbD;\xe2\xfa\x16h\xa5\xdf\x05Zi\x19G\xa9\x114Z\\\xfd\x01\xd6\x88\xc8\x00z\x98\xcd\x92\"\xed\x02Y\x8bT\xf1[\xa0\x96|\x17\xa8%R\xf4\xa9\xd4Q\xf5\xf9\xe2Z\x0bp\xae\xd6\xf1\xb8\x8e\xca\xf4Gg\x81O\xdb\xe4ju\x03\x7fmq\xb3\x98tO\x95.%\xfcy\xb7l\xc4p\x94\xa7v\xb2\xfe9.\xf7\xe8\xd1-s\xb9\xd1#\xc8\x08\x89\xfa\xda\xd1\xcb\x8a\x0e\xb5\xe2\x96\xe1P}\xce\x98\xfd\xe1\xfe\x81c[Y\x1aX\x1a\x9e\xff5\xefH)_k\xca\xdfX\xfe\xc1\xc2\xf1\xb2U\x14\xe6\xb6%J\xcaR\xd8\xd8\xde\x1f8\"a\xf99F\xca\xe8\x03$\xce=\x93\x9a\x05\x98m\x94~\xe1\xda-tr\x84\xc8d\x0d\xafx4FH\xe4\x87\x14s[\xb1\xbf$\x16\x1a\xd1$\xd5=7\x9fDIxi\xd2cK\x9f\xf9\xd5\x17>/\x87\xf2\xd6M\xf6{\x0c\x19\xb3H\xe0\xde\xcb\xb9\xe3\xb0\xa8b,\xb6\xcbi)c\x871\x14\xe2\xb6\xf64\xa9\xd6\xc4\x18\xec)\x89HN\xf0\xbd+\xbd\x92\xd7\x94c\x97\x93(3\x85\xe54\xb5hu\xf84h!\x87\x04\x14\xa7}&>Ja$a\x87\xdc\xfeZH\xa1sM\x94z:9\xf4\xc1\xa9\xc4A\xc0\xb8\xcb^\xa5\xd76\xeb\xa4\xbe\xf5\x9bo\xb4o\x10\x81\xef\xeckw\xdf\xde\xaeJ\xc53Q\xdb\x81Z<\xe3\xc5UYj\xc4\x9f\xab\x12\xbb\x80?W\xeb\x99\xf1\xe7*2X\xa1\xd0\x8ci\xb3\xce\"B\x0f\xc4z\x81\xa9T\xe0\xb5O\xc9\xe4\xbbz\x81\x05+\x10%\xb1\xbe\x82\x1b8\x81\xb4\xfeh\xd9I\xb47t7\xd0<\xc8\xe7Z\xb2\xf9\xe5\"\x8c\xa6)\x89\xc7\x86sx\xe9\xaf\xc6\x10zK\x7f\xd5$\x0b\x80 1\xcf\xfc`A\xcb\xf0\x9f\xfarAR\xc49-\x85?\xf4e\xf2\x045\x9f\xb4\x14\xff\xa9/\x97\xc4\xd1\xdd\x18f\x8dw\x1a\xca\xe5e\xb2\\%1\xa1M'^y\xd3,\xf7\xb1HI\xadl\xedA\xb3|m\x05\x8cA\x03\x1cy\x86\xc7\xa0\x81J\x98\xfd\xe4G\xe1\xb4,Rx\xf5'\x9aN\xa6\xc9\xea\x82\x99De\xa6.\xbd\x8c\xfc,\x1bC`z\xcf\xd7\xe4\x18\xa6\xa6\x12\xef\xc2\xafa<\x86e\xf3\xfd\xab\x0f\xef\xc6\xe07\x9f\x97J>\x8d\xf1\xe9\xd5U\xb6J\x89?\x1d\xc3M}q\xea)\x829>\xfdc\x90Nc\x93\x87L\x12\xf0\x94\xb2\x1e\xf6h\x7f\xbf\x12\x14V\xe2\xa5\x85\x9f}\xb8\x8d\x85\xc8P\x8b\x9cF\xfb\xaa\x9eO\xcf\xa1~!wc\xd8\xd0XA\xa6d\xa6\x7fqu\x95\x91\xc8\xfc\x0e)\x84\xb1\x9a\xbeX\xeb\x10\x9a\x19O\nI\x9cG\xbc\x94T\xbbJ'?\x8e\xfaU\xf3\x85\xdcI\xd5\x88_BU\xa1\xe1\x1cX2C\x03Y\xd2\xd4*\xd3\xeb\xcf\x7ff'\x96vE\xe6\x98^\x994_\xe0\x1ch\xb6\x16NA\xdc|\xbeJ\x93U6\x86B\x03\xff\xe46\xa6|PhZ\xd6P\x01\xa7\x8a\x0b#\xbd\x0f\xea\xc7\x88\x060:`\xa4\xcc\xd0\xfaw\x1d\x97\x06&\x0b\xf0\x15\xe8,\xc0\xd1\x9b\x96\x11\x04:\xde\x19\xd5S)\x84t\xf1\xe4,3\xcf\nm9R2s\\\x88\xc4\xc3\x19:\x98\xc0&\xa0\xd2\xcfqky\x06=\xb6\x84\x05\xe91.\x9f4\x8b1z\xb7^\x10\x9f!\x1d\x14\x96\x921\xe6\xb5\xb6Q([\xd3\xe6\x99\x87}f\x1f\x93OR5\xe3.\x05\xdfTg\x18\xb5\x05\xa3&d\x98\x0eh\xea\x80\xef\x05\xfc\x8c\x84Fl\x8f2\xe2\xc3\x14\xbd\x944\xcb\xb4T\xf2-J\xc3\x9e)\x85\x11S\xef\xdd\xc01L\x8f\xe0fs\xd3\x81\xc5\xe4\xa6n\xd8s\x83\x811\x9b\\\xee\xc0\xad\xf7\xa9\xee\x8f\xf8\xd0\x18 \n\xdf\x88\xb0?\xa3\xf0\xcat=\xa5\x9d\\\xa21\x87\\\xb2\xd9|\xb5.\x96N\xcd\x96\x8c\x02^\x9a\x81e\xc3\xe0\xfeA\xb77\x02\xba\xdag.\xac0\xa9&z4\x05E\x9a\xd2\x03\x10\xfc\x1aK\x13\xd4\xc9\xaa^Fp\xca&C\xb7\x9e\xd2 P\xbbWs\x8f\"\x0f\xae\xa4P\x9a\xa7G\xfa\xf3x\xfa\x89\xc5F\xf8w\xd2\xa9t\xa8\xc6\xe81\x86\"w\x19\x96\xa5\x7f\xf8>\xa0?\xf8:'\x1e\xc3*\xf4\x17b\x1eu\xfc\x12M\xd1\x13_\xf8\x0c\xb8\x94\xa8\xb4\x7f\x7f\xa8*n\" \xd4\xba\xd0-\xdc|\xb5\x00~8h\xce~\x0cj\xdd2\x16\x8d\x87_\x17\xd2\xf1kHg!\x90\x0e\xdb5\xe5\xf2\x90q\xd0T\xc5A\x0c\xdel\xe1\xe39.\xaf\xe9\x12mi\xde9\n\xb6\xf1\x0d\xd8\x86=\xb7e$F\xf9\xbb\xba~\x8c\xe2\xbd\x15\xf3\x81\x99\xd1?cqG\xcbj\xb0\xd3rM\xec\xb4t`\xd5\x07;-;\xb1\xd3\xbc\xc4NK\xc7\x85;\x86\x9d\xee\xe0\x18\x96GpG\xb1\xd3|rW\xc7Nw\x06\xecT\xeb\xd0\xbc\xd7\xfe\xe7{c\xea\xc2B \x81\x9b\xba\xfe\x9c.\xfe:u\xfch&\xb8\xa6Gc\x0bD\x90\x12\x0c\x8d\xc9\xad\xca\xa4i\xf0'\xe8&M%\xb1\xd3\x81\xe3\x9d\xdf-\xaf\x93HO\xe9\xa6\xebU7:\xd4\x9b\x0d\x0d\x0f\xbf\xcd\xd6m\x83C!\xa9\x0c\xd0q\xc1\x7f\x8b\xdd\xdb\xc8 \x81|\xaa\xaa\x19\x19\xd3\xbf\xdf\xb0#bt\xf5\xfe\xb0sdf\x94+E\x12\xe4f]p\n\x13r\x89\x96g\xfe\xb7\xc8\x131\x1e~cxJ\xf8\xbb~\x13\x11\x1aB\x972\x95\x1b\xa9\xechH\x13W`\xe0b\xd8lD\xe1\x11k\x7f\xc0j\xa4\x93I\xfbF\xe8\xddV\x02\xa7`m\x0d,J_u\x8c\xbf\xc6p\xe9$E\x9cUb\xe7+F\x1c\xea9C\xc4\xcb\x8a\x15I\xaf\xb8yq\xc5lU\xd6c\xacR;\x97eqM\xec\x15$\xb1\xd0E\x9a\xc4\x17\x98\x98_\xcb @\x87]\x8a\xb8\x84\x89\x82\x9e\x0b\x03\xd6\x8dY8/D=\x1a\x9f\x81\xda\x93\x87\xbaU\xf1\xa3\xc0\xd6\\\x0e\xaa\xd7\xb9\xc2\x88\xc45(\xd7\xe0Z\x9f\x80\x98\xdc\xa2\xe9r-.w f\xf8\xfe\xb6\x07\xfb\x9d\x9b\\\xb7kj\xa6\xceJ\x98\xd8\x97~\x1c'9\xd0\x86\x11\xc5%)\x14q\x19sH\xbb[\xbe\xcb\xa0\x1a^\x1f\xcaxyt@\xfb\xa0\x81@P\x10\x91b\x04_\xba_S\xb9\"\xe6\xfb\xdb\\\xdd\x9ch\x19\xab\x99c\xe5\xfe\xf02\x9d\xd0\xec\xe3\xc9\xf4\x87x.\x89\x93\xa8>\x04\xdd\x0c\xd9\x03\x17B1 g\xed\xc3\xa9\xe7\x8c\xb9\x06\xa0\xb5\x18\x0d\xab;M\xf2\x99\x16f\xab\x18\xff\xf7\xc3\x8cr\xa8\x98X\xe6\xfe\xbeK\xceT\xc6\xd6\xe6Lm\xccX*\xd2dj\x1b\x10|\x048\xca\xc7\xa5\x9c'\xed\x92\xf30S\xef\xfb{a\x06\xde\xc4\x0b \xefg/\xcc\xde'\xf9\x82EcH\xdd\xda\x0b\x06\x8a>\x04K7=W\xf5An\x83\x0b\x93\xfb4\xa1\xee\x04NBpjbB\xc9\x079\xd5o\xad\x99\x94\xac\x88\xdfo\xdd0\xcf\x1e\xf5\xe8\xc6\xa5\x133\xda;f^\xd61lb\xd4L\xccP\x85\xc5\\\xefL\xcf\xc1\xe6F\xf4[e\x81\x1a\xcby1\x18/\x8c\x83\xa8\x98\x12\xa1\x95\xe9p\x1fG\xef\xe0\xb2\xad\xda\xeb\x07\xae\xc9\xed[S\xb3\\\x9bEM\xee\xe5\xfe\x9c\x9b[\xd3_O\x9eP\x1e>\xa4\x8b\x88\x89\x92\xe9O<\x13M!a\x1f\xd0\xaeJkJ\x86ofa\x94\x93\xd4n]\x91PAn\x8b\xc7J.\xb1v\xaeV*\xad\x93\xe6\x84i\xa2\x16r\xf3\x15\x9c\x0e\x14:\x88\xdf\xf7\xf7hK\xc6\xde/WQ\x18\x84,\x1dIy#\x97 _\xa5\x12\xe5\x8d\xae\x8e\x9e3\x85\xb2A/J\xfc\xe9\xbfs [Y\xe0G~jq1\xbex%\xd3Y\x89m]\xa0s&\xbac\xc6I\xbc\xc5\xbeA\x84LO\xbc|A\xa0\xec\x7f\x14f\x18\x07\xdf\x87,X\x90\xa5\xef\xc1\x1b\xf1*%Y\x12\xdd\xd0\x13!\x99AV\x04\x0b\xe6\xed\xdf\x08l\xe3Y\xcdIe\x86=\xc9r\x15Fd\xfa\xa6\x82\x9c\xcf]\x08,\xd1\x01\xcb\x85\xc9\xa5\xfa\xc1\xd9\xd7\xe6\x07\x02\x9e\xda\x0f(m\xf9\xce_)\x14v\x03\x9etK\xf2\x1d\xa4\xd5X\xd0\x8b\x01k\xac\x95\xdf\xe3{\xf2kA\xe2\x80\x98K,\xfd\xd5\ns\x1f\x98\n\xcc\xfc(\xba\xf6\x83/c9h\x97\xb8\x1e\x94H\xf3\xd0q\xea\x8b+\x9e\xb0\xadx9\xc1m\x8af\x16\x9eh\xa9z\xa6\xf1\x15m6GQ9a\xa8\\\xe7\xa7|\x84q\xed\xf3#\x16,v\xe8H2'R!!U\xae\x08Fj\xd2\xd6\xae\x16\xc3\x9aP\xc9Jz\x15\xde\xab\xb3\xd7\xcf?\xbf\xbd\x10\xfa\x95R\xc1\xdf\xb6\"\xc4j\xa8w3\xbb\x0d1\xb2\x9c:h\x1d\xdc\x03?#0\x1ck\xe7\x03\x83'\x8a~)p\x9c\x0c\x0c1\x02\x0c\xf1\x96\xb1\x9d\x91\xb9\x1d\xb9b\xb5)\xd5G\\\\\x86\xa6\x04\xd3\xa2\xfd\xa6\x86d~N\x93x\x0e\xcc3\x141\x88h\x12\xd7\xcf9\xc3&|\x16J\xe9D\x9b\xba!\xe4y.SA\x0e\xa2\x83u^{\x92;.l\x90^\xf1_\xc49+[K\x17\n\xa2R\xf0\xe6\xf9\x8a\x04\xe1,$\xd3\x12-\"C\xcfQc\x06v\x92RD\x19\xc6\xf3\x88\xf0\x11r_]\x07\x83\xc6\xfba,pn\xed\xad\xa72\xb5k\x84\xb1\xd1\x0d#\\w\x18\x7f{\xfe\xee-\xc7\xde\xb51P\xbci\x1a\x81\xf4\xae\xd1\x7f\xb1\x8f\xc9-\x14\xb6\xe6\xdcb\xc7\xa7V\xaa#\xf0\xf8X\xf5\x05\xac \x93\xbb\xad1\xd7$\xf6\x86\xc3\x9a\x19\xdf\xa1\x96\x96K\xda\xe4\x956\x81'\xf4\xa5\x1aXLn+\xd4\x1e+\xef>\x9f_\\}>?\xbb\xfa\xf8\xe9\xc3\xc7\xb3O\x17\x7f\x1b\xeb\x92\xa1\xfe\xf5\xf9\xf9\xd5\x8b\x0f\x1f\xde\x9e=\x7f\x7f\xf5\xd3\xf3\xb7\x9f\xcf\xc6\xb0\xab/\xf5\xfe\xf3\xbb\xb3Oo^\x8aR\x87\xfaR\x1f?\x9c\xbfA\xd6@)>2\xd4\xfa\xe1\xa7\xb3Oo?<\x7fu\xf6J\xed\xc6\xce\xa8\xf9E\x18\xd3\x85\xf1\xea\xc3;\xc1\x10\xbfD\x19[\x97\xf3\x12H\xb2\xd1P\x7f:\x02'v\x89\xc7\xab\x0e z8\x98NS\xe0\xe2h\xe2\xbd\xfa\xf0\xeey\x9e\xa7\xe1u\x91\x93\xf7\xfe\x92d+?\xe8\xfe6\xd3\x7f\xdb\xf5Y$>\x13\x00\xe8\xf5U \xbez\xc7\xe3\x9d\xbc#\xf9\"\x99\xf2\xef\xf4\x98\xba\x94W\xccP^\xe1\x85\xd9\xcb\"\xcb\x93e\xd9_J\x18\x16\xdeU\xe3\xb9\xb0\x97\xe4^U\x9a/\x9d\x16\xba\x1f\xf0`]\x95s\xa0\xea\xd7fL\x12f[\xbb\x87\x96\x0b\xb3\x16co\xdaw\xa4\xcd\xbc&Y\x98\x877\xc4X\xa7\x1e\xcb\xf5\xab\xfc\xc3\x0dI)\x07E\xa6\xc6\xe1\x9b\x90b\x93\xc9\x95/\xc3F\x06~\xf2/<\x05\xe2\xb0 \xf8L\x1e\xa5x\xa6\xefd\x19*(\xb5\xad\xbd\x01\xee?\x174[\xb4ms\x03\xdf\x9a7\xe8\x9c>\xeb\x08[\xb5\xf0j{\x02N\x14sA\xf9\xd2\xbbi\x00:\x96k\xb1\x88\xad\xd4\x8e;\x0es|\xcd(\xaf\x17\x19\xbf\x92w\x1b\x9c@\xc4\xca\x07\xc6\xf2\xf5\xcd\x06'\x10\xb0/dD7\x99]6lv\xc4\xa5\xe1\xd7jO4\xbeq\xd6\xf8\xf9\xd6\x7f\\\xf9[\xbf\xfd\xf2K1\x18\xbc\x1cl\xe1\xdfW\xfb\xec\xcf!\xbb}\xcdn_\xb3\xdb\xd1\xeb\xd7\xf4\xcf\xce\x01+\xbcs\xf0\x8a\xfdyMo\x87\xaf\xf1\xedh0x\xb9\xc5\xfe\xbe\xc2?\xac\xf0hx\x88o_\x0e\xd8\xed\xeb3z\xbb3\x18\x0c\xe9\xed\xab\x03\xfc\xf6\xf5S\xf6\xf6\xf5\xab\x97x\xfb\xea5\xbb}\xfd\xfa\x95&|Is\x05\xbdyu\xf5\xfc\xe2\xe2\xd3\x9b\x17\x9f/\xce\xae\xde?\x7fw6\x06k\xea\xe7\xfeVJ\xfc \x0f\xa7Vs\xfb}\xfa\xf0\xe1\xa2\xed\xa34Ir\xcdg\xf5/\xae\xce/\x9e\x7f\xba\xb8z\xf9\xd7\xe7\x9f\xb4F\x85Ji^\x0e6\xc1\xfa\xe5\x97-o\xb0\xf5\x14\x81\xfc\xe2\x00\xa19\xe0\xc0\xddg\xd0\xdcy\xcd\xa0\xb9;\xd0t\xa3Z\x1cz\xae\x1e]\x0d\xb3,d\x8e\xd2\xf1\xd4O\xa7\x0c\xff\xeb\x91y\xcbQ=n\xa4\x16\x00\xb4DV\xca\xf7\xa1\xb3\xea\xfa \xa6\xfai'\x13jj!3\xe2\xc00\xf5\x03\xb7\xbd\xb2I~\xe9\xc8\nr\x8d\xd6\x15\x8c\xa8B|3ln7\x13)\x8a\xe6\xcdFS\xcf\xef\xceO\x1c\x1c\xee\xd4\x18\x8a\x1df\xa3\xfc\xd4\xc0W4x\n\x8a\xef\xfc`\xf1\x89\xcc2.\xe1Bi\xc7\x157\x9d\xe264:a\x87\x9e\xcfX&E\x9cK\xf6\xf1\xea\xd8P\x98\x1f\xa2\xb5\x94^.V eZ\xaf\xc6\xae\x7fi\x94\xe7\x10\xb5\xdf\x92\xce\xa7\xf9\xd2K\xc9\x8cI\x91\xe7$\xffD7\xff;\xda\xea'\xe2O\xefl\xc7#\xf1\xaf\x05)\x08z\x04R\xcc\xdc\x86_\xe7$\xffk\x92\xe5\xef\x93i\xe7\x8e(\xbb*}c\xb7:6\x17q+P\xb5\x8dxSRN+3\xb1S&\x94>S+n\x08\xb0\xeb\xfd\xe0\xf1\xf3Z'74M+\xe3\x8c\x94^4'\x12\x95:(T\xc6\xc4\x13!\x97/_\x05I\x9c\x93\xafF\xdfdM\n\x10\x90\xd6S\xeae\x8b\xa4\x88\xa6\x9fWS?'\x08\x14_\x9ft\x18\xf0\xacA-B\x1d\x82\xbe\xc3\xec1\xeb \xb0\xc5\xa8]\xf6\xd5\xe3\x16`\xdcc\x016\x11P\xdbT\xadH:K\xd2%\x1b\xef\x9b\xd9{\x12\x90,\xf3\xd3\xbb~\xfe\xcb\xc4\xbb*\xf0\xcb\x17~\x1e,\x98\x86\x8f'\x8a\xc51\x9ajo\xac\x9f\nk\xe81`\xf8=0\xe0\xc8\x10\xedo\xb8\xfbT\xab?\x1b\x19\xfc6w\xf6\xd4\xf2\x183\xad2\x08\x91\"YN\x93\xa0\x10\xd3\xab J'^{\xe2\xc7\xbb\x84)q\xf4\xb5\xc5\xfeM8\xc7h\x9erf\xe5\x93\xe6{\xaf\xc8H\xfa|\xce\x1b\xde\xfe\xe5\xfal:'\xbfl\xff2\xdd\xf6r\x92\xe5\xb6\xa6\xa0\xf6\x1c\xd0\xf8x\xd0\x8d\xd7\xf0\xa9\x00\xd9\x82\xcc\x8b\x93\xa9\xc1:*\xe69V\x995\xa7~W\x8b8\xedz\x8e\xa5\x16?\x9e\xc7\xb1\x8cK:\x00\xc3Y\xb2,h\x93\xf4\xd2\xc5\x1d\xa5\xd9\xbch\xc5Z\xed\xb6E\xbe\x8c0\x8a\x1c\xda\x8e\xd1;\x07\xc6\xd2{\x8aP(\x1c}V\x00\xf1\x8bi\xfd\xd6\xd6]\x84Q)\xbbv\xd2p\xc8=\x16(\xdc\xf0?\x94db\x02\\\xdd\x0b:\xf7\x95\xd9B\xed=\xa5\xe1\xea2\x0bf\xeb\xc1\x03\xeb\x89\x92\x82a\xf9\xfc\xe9\x0d\xc6\x83\xd2C\xe1\x1c+\x10\x85\x84\xd2\x94A\x8e\xb7\xaf>\xbc\x93\x7f\xb3\xca\xc5\xddE\xf2\x85\xc4\xec\xc6\xcf\xfd\x8b\xd4\x8f\xb3\x19I\xdf\xe4d\x89\x0f_\x87\xbcQ\xba\x9d\x9fG\xd1\xcb$\x8a\x18\xc7\x8bO\x94\xdb\xd7I\xba\x14\x0e\xca\xf4\x9e\x85t\x16O\xde\x91i\xe8ce\xef\xc2%\x1e\x80\xcc\x8d\x9b\x9e\x03S\x8a\xce\xde\xf9+\x97\xfe\xc52\x1f\xfd\x90\x8e\xe1\xd7\x82d\xac\xeb\x1f\xa3b\x1e\xc6\xfc\x0f\xfb\xf2\xfc\xa7\xbf\xbc\xc5\xb5\x8e\x05\xce\x7f\xfa\x0b#\\\xc5\xddG?_\x9c\x93yy\x9b\x84q.n$(\x9c\xff\xf4\x176\xee$e\x83f\xd15^\x14\xb3\x99\xa8\x8b\x82\xfb|A\x08\xfb\x9c\xa2\xa1\x8b\xd4\x0f\xbe\xbc\xe4\x00/\x1f\xb0\xbb\xa4\x08\xb0G\x96\x88\xe7\xe1\xd2y\xcc\x18\x99\x93\xa1(Dl\xd1L\x1f\xb4\x93\xee\xccb\x92iv&\xddK)\xdd\x89\x8d73\xe0\xfb-\xa8,G\x15t\x81\xce\x1b3\xee\x8a\x94`\xc8Q\x17\"\xba\x10'\xd1%\xdd\xee\x1e\xc2\xb5c\xcd\xab8\x91\xa1\xa62\xbcI\x17\x024\x1c\xe9\xb1\x08T\xe2eQ\x18\x10\xfb\xd0\x85\xada\x97!\xafi\xbb\x9b[\xeb\xce3\xd5\x99c\xea{\x04\xc7\xeem\xd8o$xj\xee \xf6\x10\x9e\xd0s\xbf\xb9\\\xea\xee\x07\xf6\xc8PNrd\xb0w\x0de\xb8\xbb\x84\xa2;_\x0fAJ\xb8pG\xe5\xbd8\x0f\xb7o\x8a\xd8\xde;xp\xe5\xe5\xe3B\xd2\xb5\x84\x8c\x1d\xdc\x1d8\xdeL\xd7\xc3=},\xe6&\xee\xee\xda z&\x82E\x99M\xd0\x1e%\xe6&\xc6D\xf6\xc9\x08\xb9\xf6\x93\xa0l\xac\xb92T\x97\x93\xbe3\xb9&\xa4\xba\x98\xf4\xdd\xbd=\xc7\xde\x18\xd4D\x95\xa3\x9d\x03\x87\xc7\xedq\xc1jF\xcf\xd1\x9bG^QR\x8eG\xfb!\xc2\xfe\xee\xaa\x9e\x82\xe3\xa1%\x06\x8f\xb0\xb6\x12\xd1\xc2\xae4>\xfee\xb8\xba\xabPooRK\xfe}\xaa\xa5\xa8\x10\xa8<]L\xe3\xf54\x895\xe1\x18\x90\xdbB\xff\xdb\x9c\xf1Wbl\x9b'\xa5\xaf\x84n\x8e\xcd\xaeK\xbc\x9d\xa1qn\x1d\xed\xe4\xfe\x13!\xf5\x162n#\xb6\x87\x83\xa1c\x1b\xa7\x9a\xb7{@\x11\xbb>\xae\xef\xef\x0f.X~#\x8c/\xf4\n\xe5+7\xd1x\xa9\x88\xe7\x1c\xcf_\x07\xe8\xfd\xe0\xda\x9aQ|c\xa3!Vn\xcf>\xadU\x8ftat#\x89\xddk6e\xb3(\xdd\x01\xc0\x02\xcb\x86\xf1#\x17\x1c\x81g0@\x1e#ET\xf1t08\x18>}:\xda\xdb=\xd8\x1d<}:\xa4,\xc7\x9a4\xfd\xb7d\xb5lM\xa1\x07[0d\xe6\xc0\xd6\xbb0fVs(\x12\x06B\xc9\x0f\xf8\x17\x0cyFi\x90#\xb8 \xb30\x87E\x9e\xaf\xc6\xdb\xdb3? \xd7I\xf2\xc5\x9b\x87\xf9\xa2\xb8\xf6\xc2d\x1b\x15\x99\xdb\xd3$\xc8\xb6\xf1\xe3\xad) \x92)ar\x9f\xd30\xbe\xf1\xd3\xd0\x8f\xf3\x13\xac\xb2\x96:\xa6L\x1bHQ\x8e\xf5\xc4O\xe7\xd9\xe4\x92\x95\x8bi\x15\x9f?\xbd\xa9d\xdfRb\x19\xd8\x84\xa1\xeao\xc4\xea\xc0Qc\xae\xb6\"\x8a`I\xb2\xcc\x9f\x13t\xb4\xcb\x08>\x8f\x93xk)F<%7@\xe2\x9b0Mb\x14\xaf\xd2\x8f\xf1C\x1cG\x06~<\x05\x7f:\x0d)\x80\xfd\x08\x16$Z\xcd\x8a\x08n\xfd4\x0e\xe3y\xe6)n27<,d\x95oHM \xc0\xa8\xbc\x04\x85d\x14\xf6o\x04p\xe0\xa70\x89\x90\x9d\xc2\x8c\xb8\xb3\xd4_\x92\xec\"\xf9\x98\xac\xe0\x84\xceT\xf2\xc8\x8d\xd1\x87\xbe\xe3IC)]CJ\xb7\xeb\x1c\xc9\xd3\xf5Vk\x8bI\xa7x\x03\xedj\xaa\x86\xf7\x998\x03\x1a\x91\x04\xa1\x81\xf4r\xe1\x1d\xd5\xba+\xa4\xc6j.Up\xdat\xb1\x1aW)L\xf0\xd9%\x93\x94\xc6\xcd\xc8\xc0\xd887T\xe9\xdb\xbcu\xcd\xca\x9b\x932\xf2z\xdf\xa3\xdc\xb5_\xa5\x1a\xaf7\xa5\xa6\x0fi\x99\x8ee\xcdJMu2}M\xbf\xaa4\xda\x0bm\xadl\xd6{\xd7\xaaqU\xd7\xd6\x8aa\x0f\xfa\xd7\x8a\xc5;k]\x1b\x9e\xb2\xab\xa2\xae\xc2Od~\xf6u\xd5\xb7\xb6r\x8d\xb2\xcf:\x16i\x0f\xa7F\xb9\xee\xfe\x8e\x8dR\x1b\xaf\x14\x0f\x84^\xbd\xa7\x1fu\xf4\x1dq\xea\xda\x15\xe3WR\xcd\x0c\xcfIf\xe5X@\xd7\x9e0\xea\xe8\xdd\xa4(\xd5\xb9d>\xa6\xe1\x12\x0d\xfc\xfaV]\xedk\xd4\xeb\xe9P\x07\xbe\xd0l/|n\x88\xe5\xa0[\xe2P\xcf\xc4\xa7\xed?\x93O1\x970~S\x16{p\xca\x185\xb1\xbd\xb7\xebx\xec\xbd\x9e\n]\xdf\xfdWs\x8e\xe1\x04J\xc1K9'#\x0e\xd9\xbf=\x7f\xf7\xf6\xeck@V\xfcx\xc5\x97)\xf13\x9cY\xc2\x1f,\xfd\xf4\x0b\x0b\xfc\xc0n9\xe9pR%v\xa1\xe5)\xcc\xec\"\xfe\x12'\xb71\xb0g\x8e\xe5\xc0&/\x85\x95\x9c\x82\xc52\xfe\x89'\xe5)f\xe3\x99b9n\xd9\xe5U^\xa4\xe4<\xf7\x83/\x17\xa9\x8fQ\xc6\x0codk\x19)\xee\x01\xad\x10\x9fe\xb4$\x86\x0d\x14\xc4\x87\xc3\x9f\xd1.K\xe9\xcd\xca_iK|\x0b\xd6 9\xedOj\x8c\xbb\x90\xd6_\x8a\xb1\xb6\xae\xec\x1b9\x1b\x01\xce\xd3&Xc\xd0G\x0c\xc9)e\xd79 .lT\xc1\xfcq\x1e0\xe1\x07\xa3\nM\xd3\xe1(\xa1\xb4\xd6\x8e\x83\xd3%\x8884E\x91\xa0\xd3\x94*>$\xa5\xff\xc8$\xb6wv\x07\x8e\"h\x15\xbe\x83\xf8\xfe`o\x88\x96W\x07{#\xb5\\\xe5j\x82\xe5vx\xb9]\xfew\x8f\xff\xddw$w\xf1G\xecN\xf1T\xe6\xaat\xe9:b{\xd4Hu\x11r\x13\x08\xf5\xb90\x8dP\xa5\\E\x15\x103\xf5\xe6L\x14NX\x0c\xaf&\x92\xc8L\xd2-\xd1\xd3\xb61\xaaeso\x1af+\xca\xc82O\x0fo\xb5\xf032\xfdD\xe6a\x963\x05\x08Z\xeeNbs\x14\x89\xc2&\x8d\xa0\xec\x0f\xf4Y\xdc\xb4\nJ\x99\xaa\xdd\xbb\x12\xcd\x8a\xa1\xa2\x01\x8b\xf6\x05\x8b\x1c/\xbdy\xc3\xcf\xb6\xc6'\xe5\x0b\x17\xeaq\x86\x9a@\xd4\x04\xd4\x14\xe1\xfaz\xc1\x03\xa5\xfc^\x9e\xfa7$\xcd\xc8\xc5m\xf2\x91\x96\xb3\x89w\x95\xfb\xe9\x9c\xe4\xb4+.dJN\x9bf?\x02\xbd\x18}\xad\xbe\x98\xe6\x97\xd9\x99\xc8\x1dj\x14\x03!\x9e\xa3|=\xa6\xd6@\x05\xb8\x00$\xd3M7#X\xd2K3\xfaX\x1d1@]\xe6\xd1\x1c\xff\xcc\xb4H\xd1\xc8\x85\x99s)PH\x95\xf1\xb7-\xef\xce\x8f\xf5 \xa1\xfb\x9a\xafj\xcd\xc0\x1f\xb3\x84\x93o[\xc2\xd0 \xc8U\xdf\x05\xadB\x80\x16\x9a\xa9\x0bw\xa0I\xc6\x04\x1c\xae\xd3\x86\xce\xd7\x0f\x82bYD~^.\x85W\xbcM\x92u\x19pb\xf0\x83\xa8\xd5R\xb2\xad\xfa\xf3/\xe1\xea\x02;\xde\xab!U\x15nj\xe8U\x98\x92 _s\x14\xab\x9e\x95\x9f\xc59I\xdf\x12\xff\xc6\x00\xa6\xd2\xb4W\xd7R\xb5\xed\xaajlf\xcd;\xe3 ]L\xabF\x7fRO\xf1\xe97\x1f\x8d\x86\x93Q\x1fy\xaeyb\xf2\x88\xceC\xdd\xc9\xa8;I3\xc3I\x1aUI\xa6~Ws0a\xcc\xf9\x86\xc9\xd1\xacK\x8c\x04b+\xd9\xa1G\xbe\x92\xa0\xc8\xa5y{\x13\x7fH\xa7\x84\xd3\xedh\xfb\x95}$i\x86\x1b?\xb7\x193&\x13\x94\"\x0f\x91\xdd\xd8\xdd\xf5^\xf5f\x8f\x11\x81n\x0cZ+\xeb\xcd\xb9\xb3\xca\x86\xad\x95-\xfaVfy(\xe9\xf4\xae\xd2$A\x93\xaa7\xaf\xea\xf5\xd6\x17\xd2M\x03\xadH\x1e\x00\xcdF\xd8\xcb\xb3\x1b\x12\xe7\xccl\x01\xe7a\x0c\x89\xa7\x7f\xd3D\xf4\x8dr\xd9\x0b\xee\xde\xa7\xa9\x83\xbfk\x9d\xb2\xa2\xa4\xdb\xfa\x19\x06ku\xe51S@ZOw-\xfcR<\xd6\x1cD7\xdce`\xd1H\xf4I/;\x9a\xe4,\xfbh\xc4\"\x81\xfd\xfe\xe08\x93\x10#H\xe8\xeb\xc2\x94_\x8d\xf3\x81\xd9\xebd\xda0b>\x1a|z\xd3p\xfa\xb1\x1a\xbc\xeeY \x866\x00J\x84o\x0f\xa3|\xa1I\x8b\xb4=\xa3\xe4C\x9f9\x00)6\x84v1\x8b\x0b\x835XI\xfc2\n\x83/\x96>\x90B\xa3\xdcK\xc6\xe6\xf6(\xfe*)\xae#\xd2\xb7r\xa9t\xff&\xde%EF^%\xb7\xf1:e\xd7\xac\xfe]r\xb3V\xd95\xab\xff\xbc\xea_\xb2\xbbj\x90\xf4t\xf6\x06\x92\x8a\xfeu\xc4\x12\xbcbT\xc0\xdc\x05\xeb\xba\xc8s\xb6Cy2H+\x8cWE.?\xc8\xd0\x14K~\x92\x93\xaf\xb9\x9f\x12\x9f?sZ\xbc\xa8[#s\x88K\xf4\xb2\xe98\x05\xa0\xea \xc4\x85\x87s\xe3\xcd\x03\xb3\xceV]'DDJ\xf59\x8bY\xed\xc8b:=\xeeH\x8dx\xa8T\xf2SZ~\x92^\xb6a\x00\x96/\xe8\x11H`=\xb4\xc5\xf9\x8a\xdb0\x8a^\xd5Z4=g\xed\x9bG\xae\xc7AX\x1dO\x81\x94N(tz\x0c\xfey\x14\x95lC\x17\xd5)\x98<=\xe0\xeby\xbc\x15\x12[\\\x14O6\xfcpc\xb4\x82\x89&\xf1\xe5$\xbflC\x8ab\xfcf\xf0\xeb\xc4\x06\xe2B\xf8\xa4\x86i\xd0=\xb7\xb9\xa1<\x87)\xef`\x8f=\xf1\xa0J\x90\xf2\xd4\xe7\xc7{\x7f\xca\xbb\x84g\xe8\xf2\xa3r\xc5H\x83\x9a\xfd\xa1\xdff\x7f(.a\x87\xe8O2\x03|p^\xba@O \xda\xc8\xab\x8dF\x1e\x83\x19\xf2\xccv8D.7\xa4\\\x91~q4\x11K\xf3 \xdf\xdea+\xbc\x99\xebU\x13\xdefR;\xc0\xbe\x05\x1a.X!\xba\xd2$ Y\x86U\xffo\xdaHW\xf5b\xcf\x04M\xe8\x94\xfc\x01d\x88%\xe1\x14V0\x86\xa9\xe32\x80Q\xaa\x0c\x93\xb1\xfa^JP\xd5\xfd\xd2/\xe6\x8b\x9c\xe9\xc2[\xbbyu\xb5*\xd29\xe90\x81\x89*S\x0fc=\x12\x91\xf4\xc2\x8f\xbf\xf4\xcb\x8f\x1d\xd5\xeb,\xef\x0c,!\x0b\x01\xf0\x8d,a#\x85\x97` \xd5$A\xfa\xe8:7!\xb9\xed\x9aK(\x83\xe9\xd1\xd2U\xd0n\xbc\xd5\xaf~1\xfd\x89\x16e\x82\xf0\x99\xf4n\xc3x\x9a\xdc2\xcb\x81\xb2b\x8d\x87%H\x87P\xeea\xe2\x85W\xdcKM_\xb8<\x0eO!\x16!o\x7f\n\xc9-\xc6t\xe5\xfe'?\xb3\xc6\xc7\xc0z\xd1\xdc\x85MffJr?\x8c\xfa\x00\xac\x04\x12\xfb\x84\xb6\xdb\x199\xbb5B\xa6\x0b\x89\xda\x16oCRZIy@\x1bf\xa3\xf8\x85\xe7\x17s\n5\xcc\xa3e\xfb\xcc\x0bT^\x94\xfe\xb7/J\xb5\x93\xcb\xe4\xa6\x13_\x10\xcc\xa7\x1e\xe4o\xe2\x9c\xa4\xb1\x1f \x01\x1d\xdd&\xa8El\xdb\xae=\xc4R\xe5t\xe8\x9bi\xab}\xe1w\"\xd3\xbaF\x9e{\xff\xae\xdd\x90\x92\xbe\xde$#1C\xcah\xd7\xac\xc7?\xbdTS8\xa9\xd5\xf7\xdb?nH\x8d\xbcLVwi8_\xe4`\x07\x0e\x8c\x06\xc3}\xf872\x85\x9f\xfd\xdcT\xec\xefdz\xcb\xea\xabl\xc5\x02\xbaz\xd1E\xb0,\xff\xe3\xf6\xffQ}\xdc0\x1f(\xfa\xcd\x05u\xab\xd6:)\xa9D\xbd,\x91G3t\x02\xc8\x14\x16\xe1\xd9\xbe\xa5\x10\x17\xcdh\x95-\xe1,\xc4\x86\xafl\xeat\xf49plo\xcc\x9f\x0c\x92\x90\x85\xcbaR3Q\xa5$\x958\x81P1Y8\x81\xd0\x01\xc2\x9c\xfe\xda\xa8\xb32}L\xddb+u\xca\xaf\x13\xcf_\xad\xa2;\x9eP\xa9\x95\xbf,+\xaby\xc3\x86z\x82O\\\xe5D`F\xa0\xd4\x11\xc6\xc6\xa9\xc8\xcb\x93rG\x17\xde\x1f\xff\x9b\xe9G\xc2\xf2\xceZ\xd0\x1aKR\xc6c\xacy\x814\xeai0\x92\xd2\x85\x0eGk\xd7\xb4\xa2-x\xb2\x9e\x9e\xfa\x81C9\xc7\xd8\xb4(\xcb\xade\xf7\x95T\x9e\x0f\xf6zV\xc8\xdc.\xb8\x0f\x8a\xe3\x9e\x1b:\xd5\xf3?\x81A\xaf\xda]\x16*\xbc\xde\x9a\xe8i\xea\xc7\xd3diw\xfan\x18\xbak1\xf36\xdb\xf2\x82$\x0e\xfc\xdc\xae\x85\xc4\xc74\xc6cJeX\xce\x95\xe5\x82\xbd\xb9\x19\xc3&\xa4Ne\x0e\xb1\xb3\xff\xf8\xe43\x8dh\x06<\xb5e\xe39Sp\xec6\xe6\xcb\x07\x83\xd5|\x05\x8d\xdcc\xd9o\x87\x83\x81\x03\xa7\xfa\xd2\xd0-ZF\x94V\x06Y\x0d\xe9\xf2\xdd\x188.\xa46\xe5\x9d\x13\xa7\xdd\xd0\xdd\x14\x8c\\\xb6v\x7fh\xb4g\xcdInQ\\\xc1\xacW2q\xd7t\xfc\xb2\x9e\x07\x94aKR%\xdc\xb4\xc9\xf3\xcbBw\x0c^7\xe5\x0cE\xb2i\x0f_P\"\xf1\x11KTsP\x89\"\xeb\x9a\x17\xc7e\xce\x88F\\\x9f>=\xc1\x9d\x11\x9002l\x9aY\x94$iW\xef\x0c]\x0b\xb3\xf7\xfe{\xf4\x81\xd9\xc44\n\x03\xe6\x12\xc3v}\nc\x88\xd7O\xe8!\xe1\xa4Q\xaf\x87J\xe3>\xc3\x99\xa6\x91\x1b\xb4\xc4qn\xf4\xc1 \\R\xcaK\xddh\x98\xd6\x88\xcb\xd4\x93\x9d\xfe=\xd1\xb0n\x9aO\xea\x9d\xa91p\xf2\xa5\xf0\x8c\xba\x05\xd9\xe7\x0c&\xd5\xa9[\x92ofC\x08X\xe3\xd05\xef\x97\x7f\xa0\xe7\xaa\xd9Gr_\x9f\xc8b\xcf\xe4\xc3\xd9\x89\x0eR;Y?\xffZ\x97\x98gO/\xe69\xd0Iy\x98\x87Y\xf3\\\xc4A\xd5\x1f3\xbd\xff\xb0;\xc7\x9e\xd9\x14.cF<\x1ao[\x96\x94\xdeGk%\xcb\x82 \xb9\xd4\xb9\xf7\xa2\\\x7f`\xf0\x06\x8f\x1a\x11\xd8C\xb3\xe7\x1cH\x82']8`!^\x9ad\x97]\x84\xaaT\\\xe3%\xe72\xef<6\xa6f\x02\x0ds\xc21X\x1f,\xd8\x84\xcdMM\xf2oq\xddj\x93l@\xe3\xdc\xc1'\xad\x92\xf9\x99H\xeb\xa2\x8dfB\xaf\x7f?\xfb\xdb\x184\xf6#\xef\xcf\xce^\xe9\xd3\x17\xce\xfc,\xffw\xa2\x86\x873mg\xcc\x1a\x90\xc8A5\xb5n\x0b\xcc[]\x9f\xb6\xf2\x14\xacs\xca\xfdX\x1f\xd1X\x9f\x98e\x1d\x1b!NOk\x04a,\x97\xd5:\xf4\xdaj\x97{lT\xd4\x9bu\xd6R6P]_\xc4\xa5\x9fLq\x86N\xd2K/lNl\x13\xf2s\x92\xffL\xfc/\xeb@\xfeQ\x00\xd90\x84H\x84&<6\x86\x7f\x088zi\x05\x92\xf8uJ\xc8o\x9dBn\xa8*\x8f\xd0\x1e\xd4\xa3\x8b\x9b\xfe\xc2\xd8vO\x9e\x80\x00\x13\xfd\x1d\xd8u\xb6K\\:\x02\xb0\x8d6c\xfc\xee\xef\x0fe\xb8\xe77\xd9Y\x19yC\xfb\xf5Z\xb4\xc9\xef\xdf\"]\xd6W\xadw{\xcf]\xb0\xaa\xc8F\x0d\xf7w\x8e\xf2\xe4xG\x947\xf7^\xbe={\xfe\xe9\xea\xc5\xdfPs\x847\xf8\xeb\xfd\xd9\xcfW\xcf?_\xfc\xf5\xea\xecS\xf5\xe0\xfc\xe3\xd9K\xfa\xe0\xea\xc5\xf3\x8b\x97\x7fm<.\x1f\\\xfc\xf5\xd3\x87\x9f\xdfkJV/J\xc5\x05\xedCLn/(}\x1b\x9f\xa5\xed\x9eg|u4\x97\x0e\xc5A\xda\xa8\xcd+\xff.J\xfc\xe9\xb8%\x83$\xd4\x89y\xb5C\x18/\xf3[z\xa59@\xca^\x91\x8e^\x9c\xafH\xf0\x8d@\xc9\xbe\xbd\xf9o\x06\x81&\xbe^\xef>\xbf\xba\xa6;\xd7j2\x01\x0d\xc4]~\x9c\xadH\xa0i92\x1f\x02\x8dO\xb5\xad\x06\xbac\xa5\xfc\xd4/\xf2\x85\xa6\xd5Y\xedT\xc2\xd2\xb8\x80\x95b\xab\xaa\x18;\xc9\xaa\x92W\xd7w\xcc-\xb37_\xb6\xaf2X\\\xc6\xaeK\xdcY\xba?3\xa5\xc0\xe5\xda\xe1C\xdaH\xed\xfb{\xb4\x0fa6?\xc4\xa1\xef*\xeasMfs\x7f\xc7\xe1\xec\x96\x0b\x16s?5E\xaf\xeaE\x98H5\x0f\xf4\xee\x88\xfb\x0d\x19\x0bO\xf7?\xd03\xb0\xfb\x03\xbd\xf0e\x7f\xb0\xdb7\xdc\xb1\x10nli\x98\xa1\x98[U\x01W\xd3\x0c0\xe6\x16W\xe2\xd6\xd7\\\x92r?c\\@\xb6s\x04\x9b\x9b9\x1cCl\x0c\xb3\x99\x1a3\\3\xafa\x92\xdb)f\xcfK'\xc3\xcbv)\"\xbd2\xd9\x0b\x98\x9f@\xa9[{\xccm\x0fO \xa9?\x9f\x13\x96\xfc\xaa\xf6p\xe1\xa3\xe5J\xfda\x86%\x8b\xbauK\xb6\xde\xdc\x0f\x07{}$c*\xd8$\x93\xd0\x13)_x\xbc\xb5u\xd4\xe4C\xb8\x94~\x12_\xb2\xfc\x83\x92\x19\xb0\xf6\xac\xd8\x1a>z\x8f\x0c\xba\x93\xd1kFS\x0d\xe4\xeaj\xea\xe7\xfe\xd5\x95\xb6_\xa9\x9d;p\n\xf1D\xc3:\xe7\x94u\x16\x8f\xc7`-\xfcla\xd1\x134\xf6\x96\xfe\xea\xd1\xe31\xb8C\xed7\xe2\xf2\x89\xf0v\x06w\xa8]\xfd\xc6\xec\x11\n\xd7\x84\xeeD \x9dlA\xde\xa5!\x85\x86.:\xc6)\xf86*\x93\x12\x9b\xe0\xba tg\x89T\xddc\x94\xb8v\xc0M\xee\xdbZ\xbd'\xde-\xb9^\xf9\xc1\x97\x8fIt7\x0b\xa3\x88\xab\xe4\xa7d\x95\x92\xa0\x99\x17\x14=\xdeW~\xbe\xc8\xb8=I\x15z\x99\x7fY\xde\x9e\xb0\xf4\xb3z\x06\x8f\xb8`\xb1dM\xda\xd8f\xb5p\x91\x9a\xf0tk\xc5>#^\xd4x\xad0\xd6\xad\xfd\x0c\xffG\xfa\xa8\x11\xc64\xfa\xd8\x9c\xad\x13\x18>R_\xab\x9a&\xd4\x07@w\xdd\xf6\x7f\xda\xa7\xe3\xc1\xfdd\xb8\xf5\xf4\xf2\x97\xe9\x8f\xce\x9f\xb7\xbb\xb6\x88\x01\xa3$\x95\xb1\x8f>\xef\xfb\xc6\x86\xfd\xff\xb3\xf7\xef}q\xe3\xc8\xe20\xfe\xff\xbe\x8a\xc2\xe7\x9c\xac=\x18\x03I&\x97\xce\xb0,\x03\x9d\x1d\xce\x06\xc8\x0f\xc8\xcc\xce\xaf\xc3\x971\xb6\xba\xdb\x1b\xb7\xddk\xab\x9b\xb0\x9b<\xaf\xfd\xf9\xa8$\xd9\xb2,\xd9\x86\xb0{.\xcf\xd7\x7f@[\xd6]\xa5RU\xa9.T9\xd3\x18\n\xc9`\xc4*{\xf2\x04\\\xd5EI\xde\xf0A\xb2\xb1\xc7M\x87\x0b\x1e]\x80xX\x80\xc0\x1f`k\x97\xff\xfa\x0f\xf4e\xcfi}\x8c\xc5\xfb\x80\x99\xd2]L\xf5\xcd\x82\xed(\x17\xfa5\x8a\xe9\xa2\xf9z\x8b+\xd8\x18\xf1\n\x86\x03P\xba\x82*\xae}\xc8\xa1\x83\x90\xd2\xb1\xa1`\x1f^Y\xc8\x9dg\xfa\xfd\x99 w\x9e\xe9\x0e\xc6\x05V}\xa6\xd3\x99\xa5\x99*M\xc5%\x81^\x0d^\x18\xb9\x85\xd7&\xa4S7\xf7\xdats\xea&Zj\x8c\xa9\xa1\x96:\xc7\xd4\x95\x96\x8a\xe1\xdd\xea%q\xb9\xe1\x91\xe2m(\xfc9!\xb7W\x08vk\x97\xbb\xe3`\x7fQ\x97\x8c\xbb\xacqw=\xae\xd5\x947\xca\x9e\x84K\xb5X\xee\xf1\xd01j\x96\xf7E\xbeHJ\"\xb3%\x01\x0f*N\\^_\xd8\xc8|A\xa8Z_\x88YV\x8d,\xbf\x90\xf0\x93\xd6\xec\x8ao\x0fw=\x08ZK\xe3=_\xa62\n|c\\9r\xcf6\xfd\xbc\xd8\x9d\x8b\"\xf4\xc1>\xa4n\xc6\xdd\xdbh\xd7~\\\x81P*)\x18/\xf7\xf1Z>\xea\xbc\x967\xac\\\x9b\xa6\xc5z\xa6\xc3\xea\xc1\xe9\xb4T\xb1\x1cVE\xb5\xca\x96j\xe2a\xd5\xe0\xfa[\xaa\x98\x0f\xab\xa2\x82\x8fFn\xa3\x8a\x81\x8235\x05\xf2AV\x0d\n\x89\xfd\xecu/\x95e\xbf|\xce5\xaeG\x88nF`\xb4%\x13}W\xb4arq\xaa\xf49F\xb4v\xbf%T\xe1\xd8\xf2\xd5\xce\x90Au\xf2\x0d;\xdc\xb9>\x1e\x82\xe8[\x97x^\xcdJ\xc8x0l\xf3f\xf0\x03$o<\x94i\x91I\xee\xd2I\xb6\xb9y\xe5]\x19\x07\xcf\x8d\xf2\x90\xd7\x16\xf4\xa8\xa6_?h\x02\xccr\xfb\xfaZ\xb45\xb4\x0d\x1a\xacIQ&\xdc\xef\x92PE\x92IA\x92\xc5\xe4\xf3\xd9\xd4u\xd6;\x81\xe3u\xe7\xd8e9\x9e<\x11\x02:s\x8eW,\xcf~\xcf\x85cF>\xd3\xcb$\xd2n\xb1z\xf4u\xfaUX\x18V\xad\xd5X~\xefDa\x9a\xde\x84\xd1'\xa7\x92\x1eb\xf8Y\xb8!\x8aZ\xcb\xef-\xaa\xc5ka\x07\xc7c(\xb4\x94\xb3\x8de$\x8e4\x06F\x92\x0f\xa2\x85\x9d\x1e+_\x8b\xc2\x97|$*\x08\xe4LZ\x8d}\xa0G}K>\xed\x1a{ie\xf5\x11\x1aT\\]\xdb\xa2X&\x1f=\x10\x89\xfat\xe9w\xc9\xe7Q\xbbjU>\x93Ooo\x9f\xffk{k\xd5N\x93OW\x87\x07\xd9b#.D\x12SRS\xee\n\xb6\x90\xb3 \xb9\xb9B\xc8\xd0\x9e\xdc \x1e$\x93ps\xf3\xaaa\x8d\x10\xf6D\xe5\xfd\xe6YQ\xcd\x03zt\xfd\xbf\x0e\xbd\x81\xd68<\x14\xe3\xd5hL=wU\x07\x89\xdf{f\xcdx\xbb\xa6\xb5\x89\xcc/\x84\x97E\x93<2\xe9;\xb2\x92\x0c\x91\xe0$\xbb\xc2s(S\xfc\xc2u\xd9\xb5Y\x84\x10y\xf5]\xa9F\xfe\xca\x83i\x91/\x00\x9d\x83\x85i\x9aG\xca\xcf\x0fY\x19NI+\xe1\"\xcdo\xb5#\x81\x91\xa3n\xe2\x16\xdc\xa7\x0c\x0d*w\x94\xa1\xe7C\xe2\xe6<~b\xc8\xdb\xea\xa7G\xf0h0x\xce4\x1f\x0c\xceA\xe34\xc8rq\"\x88\n\xcc\x94\x8biRX\x0f\xf9\x1c\xdc\xb3\x8b\xbdg\x97\xd6\xc5\x8e\xeeI\xb0j\x9b{6I\xae\x0d\xc1\x14\x98\xc2\x05\xc2>\x14\xc14\x91Z\xc1\x8c\x86\x13\xaf\xcaoT\xb07\x8c],z\xaf\xf2\xe9?a\xec\xf5\xd2\x98\x16E\x01\xbe\xff\xc2\xce\x15\x01\xeb\x81`G{\x05\x87\x83h=u#e\xee\x8b\x97\xdf{\xae3\xcd\x8bq\x18\xcd\x9dA\xa8\xa8O\xe3\xf5\xd9\xaeY\x10\xf1\xcc\xe2\x06r\xf7\xb5.)\x10\x82\x88W\xaa\x18\xd7\x1dL\x8c#R\xc3\xf8$+T\xcfL\x8d3\xdb\xbaC\xfe\x01\x9e6\\\xe5n4\x84\xban)\x9c\xc3r\x97\xb1D\xb0/\x0c\xc2\xcb\xc6\xd1\xf5T\x04\x8c\x94\x8c\x0dFO[\xa1I\x13\xe7\x0b6\xd0n\x08\x93\xc3J\x7f\xd3\x89\x1c\x11\x93KI#2\x04\x97\x92v\xebx\x9e\xcf\x0d\xe1\x1b\xa3\x82Z\x91\xc6\xe0\xc6\xb0\x19\x96%kgP\xc5\x9fI\xfbs\x1d\xa2G\x8fK\x0c%\xdb\xfen\xee\x96\xac[ld\xb5x\xf6\xab\x17\xcc\x86\xf2\x83b\xa9|\xdd\xef@u\x0di^\x15\x945\xf1@\x06\xe6\xc5I\x1b\x8b\xf3LY\x1c\x86\xceh\xa5\xec\x03#H\xc4=\x88\xf8\x8e\x16\xe8\xcd\xef\x19\xb7qS\x1a\xe5\x1fqA\xd3\xba\x0f\xca\x17\x0d\x18$ \x945 \xac\x0c\x80P\xb6\x00\x01},\x98\x16\x1d\x05\xd3\x86%G\x9bd\xc3J7A\xc1\xa0\x01\xa4\x82B\xa9\xafv*V;\xf5D\x0c\xbd\xe8~(\xa9\xc6\x12\xadp\xb9\x02I<5_\x01={f2\x18\xcb\\\x8b\xb0rwW\x17nrt\xb7\xfbB\xc7M\xdc\xa7D[R\xa9\xaa\xbd\xb8TS\x82\xd5\x87\x88\xbe\x05\x97&\xb8\x8e}\x98\xfb\xb0\xf6a\xe1\xc3\x0c\xf6`\xa9\xaa\x89\xdbhU);n}dD\xa5Y\x94w\x87\xc2\x06\xde\x11\x06\xd9Oa\x04:\xbae\xcf\x0d\x92\xe0\xcd \xb6q\xc6\xb3\x1e\xe3\x8e\x84r8i\x99v\xb0\x1a\x13wf\xd4\x19E\xba3\xe6\xa6\x072F\xef\x1b\x88\xe1\x0fp\xf3\x06n67\xcd\xd46\xab\xd1]\x08G\xacwn\xe8\xce\x91T\xbd\xb9\xf2\xf0\x8em.\xee\xd8\xee\\L\xf3P\x06\x81\xb7_\x0b\x1e\x0b\xb2\xba\x9a]4!\x1a\xcd\x7f\xcd}\\\xc3\x1eTq'\xde\xc0\x066\xb9F\x8e\xc3\xf5\xbc \xce3b\xb8\x14\x06\xb5\xb3\xb9\xbb\xf6\xe1\xce\x879\xb7\xc5\xe3w\xc4\x03\xba\xf6\xd5\x0b~<\x1f\x1f\xfc\x99\xc7j\xa5\xc1\xf9\xf8\xf2\xc3\xf9)\xec\x89\xdd\xf6\x8d\xe7\xb3\xd5'u\x11\x1c\x8d\xdf\x1e|xw \xfd\xfe\xa9ww^\xf5\xf8\x9d~)\xfcL\xbf\x12\xff_\xdf\xdb\xdf\xb4BR<\xb7\xdcm\xec\xe8\xdb<1\\\xf1\xdc\xdf\x94\xd1rH\x85Fm\x8aD1pD\xee\xc5\x0d\xb1\x18\xddd\x83\x00\xad6a&\x1f\xec\x96\xd6+W\xa8\x869O_\xeaGCU\xcchc]}\xb5-\xdc\x0e\xa7}\xd9\x7f\xdep\x05\xa7\x07\x82\xc9\x8cxp\xf8\xda \xb39FQ\xde\xe2(\x10\xa6I\x16\xa6ig\xd7:;\x0eP\xb9&\xeb\xcf\x08r\xa4Q\x9a\x97b\x00\x9d\x05\x9aF\xe6\xdcu\xc5\xe0\n\x86\x0c\x0e\xba\xe6\xde\x93\xa8\x15{\x1a@\xba\xd2\xb0\xd9)\x81d-\xb0\x11s\x03a\xdbu\x8b|V\xed\xab\x05\x90\xd8\x81\xfb\x83GM?\xae\xff\x93U\xbcNh\xe7u*\xcffA$\xa0\xf8\x80\xbaa\xa7+\n\xae\x01\xd6\xa3T\xc5\x88,\xe7\xc9\xdfV9}\xd3\xe1\x8b\x83=7\x05 ?\xd9\xb3\xf0\xd6^\x0di-\\,\x1f\xa5\xb1\xd7C\x1a\xfb\xb7\xcfO_>Fk/:\x14\x0d\xa1j-}\x94i|\xd1\xa3b\xc8\xdb\x9a}k[\x83t\xd8\xa2<\xa3I\xb6j\xdf\x0c\x81\x95\xc5\xe3|0j\xf6\xbb l2\xfcX\xaen\xf8\xb5\xb5\xbb\xf2!\xf4\xe4e>\xe3@\x19+\xbc\xa9#:s\xe5b\xaf\xca\xfa\xf7Y\xc9v\xe50\xd2C\x0c<\x92\xbaH\x83\xea2\xfa\xa67\x851\x0b\x852\xb5\xd9@\xaf\xcd\\\x96\"\xbf\xce@ [\x92\x96FId\xb8\xb5\x9d\xa2p\xa1\x99\xb6l\xa3\xabvx>\xf6\xd0|yp\x93\x17t\x04N\xc8\xfe\x1b\xd0\x1f\xcb\x92%\x0b\x0c\xe11\xce\xe2\x11\x94\xae\x13\xca\x04\x92\xc5\\\xff\xb9\x99\xd4]\xcb1%<\"H\xb3\xaeD&\xeb5\xd6\x1f\xba\xeb\xbd\xa0!\x1b\x89Zg\xc9\x92\xf4\xfax\xa2\xb1\xae\x1f\xd3U1\x02\xe7&]\xe9&\xed\"\xc3a\x98\xbdO\xc3\xbb\x118Q\x98-\xd3\xf0\xae3\xdb\xe5\xbc\xc8W\xb3y\x9d\x9b\xf2\x04K\xa1y\x98\xcd\x08\xcb\x8c?,\x99RT\x01w\"\x8c e\xce\x92/\x96y\x99T\x0b\xe6Du\x82uu\x94Bb\x1e\xd5b\x1dS\xa6\x14\xfc\xb0\x8cQ&\xa0\x96\\a\x9a\xadhF\xc9gzB\xb2\x15\x16\xc2\xb7\x05\xc9V\xb6\xecK\x9c\xf8|i\x9b\xf5\x15v{e\xe9\xa9\x12\x1ek\x04N|\x93v\xcc\xe1Q\x11\xceX\xa6\"\x9c\xd93\xf0\xd9ey\xac\xd3\xca\xb3QRT\x19)\xb1\x80\x16f\xfd\x9cP\x99\xf3sb\x1bG\x11\xce0\xc0\xa3\xc8\x99\xb2\xdf\xf6\xacg\xeb\xaa\xf5|\xdd\xd5\xb8\\w\x96\xb3c\xc1\x8f\x8a|\x89\xb9\xf2\xa5%\xc3\x8ao\xd7\n\x9ec\x91\xd0\x05\xd7\xe3\xc5\x92&\x84\xcd'\xe1\xbf,\xd9\xb2\xa8\xb8[R\x9eQ\xfe\xb6e\x8dE\xb6\xd8\x9a\xa5(r67\x84\xfd7gy\x9bG\xabr\x04\xce\x94\xfd7g9\xce\x96\x08x<\x02\x981\xcb\x9f\xc9\xddQ~\x9b\x8d\xc0\xf9D\xee\xe2\xfc\xd6\x82\xca\xfeL\xee\xde\x17\xa4,y\xbe%\xfbi\xcd\xf8a\xc9s\xad,\xab\xf0\x0e-\x93\x19\x0f2\x92f\xca\x8cs\xe9\xca|Bh\x18\xab\x05\x16\"\xc1^H\xc2\x0c\xcb\xdf\x013U\xe0\xb8\x118\x0b\xf6\xdb>\x07U\x108\x99\x95qW\x1dY\xcfp\xee1gn\x9b~\x9e\x91\xef\x03\x9e\xd3\xba\x11D\x988\x99\xd16\xbb\xef\xc3\x121\xdd\x92\xfd\xb7eY\x95<\xcb\xaa\xb4e\xe1G\x89\xfd\x1ca\x19\x92l&\xf2$\x99\x05\x19\xbd/\xf2\x99\x80\x9b\xa5\xf8i\xcex\x1eRRm\xcb\"\xa4\xa4kKr \xdb\x08\x9c\x12\x7fX2\x11\xf2 \xb7Y\x89?\xec\x99\xf80J\xfe\xcb\x96-\xe5\x91=\xab.\x962\xa5\xb3\x9f4LS\xde\x07\xfe\xcb\x92mU. b\xec\x92\xff2g\xbb$\x9f\xa9\xdc\xd1T\xfe\xb6dM\x16\xa4:\xf3h\xb2 ]\x87\xdde\xbe\x8a\xe6\x87a\x16\x116\xa5\x94\xbdE\xf8\xd6\x91\x9d\x1f0\x98\xd7\xde_\xf6U\xec\x17\xcci\xdf/\x98U\xeeX\xcc\xdb\xb1e\xf1\xda/Q\xa9>Z\xa5\xd4d_3\xcdX\xd1\xcfy\xbaZ\xd4P\xb7\xc6\xd7\xae\xf5\xfc%L(\x87\x96[\xfe\xcb\x92mNp*o\xd9\x7f\xcd\x04\xb4Y`\xcex(\x1e\x85\xa6\n\xa2w|\xe4\xc0\xa6\x90\x18\xb9\x8d8\x04^P\xa6ID\xdc\xa7^\x93\x1dX\xa3j\xdb?\xbe\xa2VE\x93\x94>'2\xd2Z\x1d\xa4\xb0}\x990 p\xad\xa9\xa2~\xf99:\x8f\xf9)\xcc\xe2\x94\\\xe6\xcbwdMRw\x1d\xcc\x1b \x9e\x0f\xeb\xa0]=\xec\xf5{ll\x8e\xa2$t\x9ca@\xcc\xbe\xae\x19\xdb{\xf2\xc4\x98\x1e\xd4\xd5\xb6\\\x01j\xb3X\xb6\x9b7\xb5.5\x88\xdc\x0dc?\xbe|\x01\xe3\x87\xa0\xaa\xdf\xed\x0e1\x97b\x81\xcb|\x80S\xd1\x86\xa4\x98\xfa\xd0\xed;O>b\x00=j}\x95\x16\xde\\D\"\x99\xcc\xaf`\x0f\x96\x9b\x9b>D\x13\xf6&\x82\xfcV\xaf\xed\xe5\xe6\x11 `\x0f\x92V\xc0\xc6#\xc20%\xc9\xa2\x84\x94\x13r\xd50f\xcb\x87\x08\xb3P\xcb\x9d\xed\x1c\xabu[\xa1\xc7\x99\\\x89X2+\x1e\xa7\xd8\x91{\x9d\xcb\x86Wht/v\xbd\x07\xfbfp\xa2E\xb8\xfcqu\xc3\xd6\x11?(\xb5\xf8\x12e\x08\xb3\x9d\xd4\xe5G\xfd7\xd5\xa8\xd4 \xaa}@%Gg'H~\\\x88\xf3\x96W\xe4TGqc\x02\xe4\xa1\x0c\x1b;\x9d}\x16\x01o\x95\xf6\xaa\xea\xeb:\xee\xd9cC\x0d\xc6\xc2\xbf\x1c\x9f\x1e\x9d\xfdr\xfd\xd3\xc1\xe9\xd1\xbb\xb1\x1c\x0bR\xd4r(x\x86p\xbe\xbb\x1e\x9d\x9b\xba\x92\xde\x16\xa3s\xef1\xbc\xb7\xa2dUEf\xc1}\x96\xf2\xd8\x17_\n\x01 \xf3\x04\x90`uI\xe6\x08\x15\xd7\xc1\x93\xd5\xecO\x92\xf5\xf5\xa8U\x81\xec\x10\x96G\x1a\x97u\xca\x87\"\x10\x1f\x85N\n\xbeck\x98\xc0\xba\x1d\x9b\xf7\xd6\xb0\xb6W>\xc4\x93\xd5\x15\xef.n\xc7\xbdVHy\xe8;.\xf4Z\xfb\x03\xd5\x80b\x867\xa8\x9f-\x85bK7\x1aK\xfd8\xfdhB\xcf\x90\x8e\x88\xc86<4\xe9\xfbpF\xfe\xf2k\xcfA\x86\xb7\x17\xfa\xad\x1e+\xdd\xe9Kz-\x9c\x86\x9a\n\xba\x0e\xa2\x19\xfcm\xd2\xe3\x92\xf7$\xaa\xd3\x06UQ\xa0k|$+W\x85\xc0`?\x87\xe9\x8a\x9c\xe4YB\xf3\x02 \xba\xdeq*\xae.\x90T\xc0K\xdcu`\x984\x97\xed\x80\x0d\xcc\xb41\xed:|\xd8$\xac\x82\x82L\x0bR\xce\x95~\x95\x96\xfb@\xd3R/\xf8\x18\x94\xd2\xe8\xebzZ\x87\xecR\x1fm?To_-\x06\x08\x83<\x904\xc5\xd4Ur\xa5\xd1P\xb4\xe6\x94k\xb4^\x17\xab\x94\x94\xd7\xd7\x0d\xdd\xf0\xeb(\x8c\xe6\x04\x13-\xd7\x8b\x85Bp\\_O\x93,\xc6\xdcv\xaa\xa5\xad\xf7W5-\xc8\x04~\x8d\xb7\xb5\xfb\x06\xa8\xd5\xb1`\xb3\xe0ds3\xbbB\x85\x01\xae*s\x0fO\x83\xbe6\x82(_,\x93\x944\x07a\xbaB'\xa2\xfb\x06\x96\x83M\xa1\xe3hT\x0cQ\xc6)\xecI\xddn\xda\x8e\x04\x84\x13\x98\xfc~\xe3\xf5\x18\x07\xa8\x95\xa2\xae\xfe?\xd0\x07q\xaby[ OY\x92\xc7\xda\xe2\xae\xf3:\x86oD\xa9\xec\xc9\xd4)p\xd1!X\x86\x13!\x07G\xf9\xe0\xbe|\xd1Z\xe5#\xcd\x82if\x88M\xdd\x1a\xad\x0d\x1cB:\xd0\xf2\xa5\xa8a\x99o\x01\xa3\x11\x1a^\x12\xb1\xbe\xea>\xa3\x19Doq\xb5\x81B\xb5\x8c\x16V\xd1\xef\xc3\xa2$\x05\xb0\xe9C\xc3\xb2i\xbeB~\x1f6A7K\xd7\xf6Eq\x15L\xa5\xf1g\xebK\x98b$c\xfc\xff\xe5\xcb\x90]\xdf\x9c\x9d\x1b2\xcd\x0bb4\xf7k\xb9\xb1ZK\xcfx\xbd\x93\x94Hm\x9c\x8eI\xca\x1fs\x92\x82r\x89l|\xee\xc3\x8e\xc9\xf5!C+F\x13R\"\xd9K\x93C\xc4if4/\x0dS:\x82\xa4\x9e\xf2\xd6\xb6\xbb\xd7\n\x84SJ\x8a\xff=\x0b\xc0o~\xff\xa7-\x02\xc34\xf7@\x13F\x04\xa0M\x08\"/\xdb$\x18T[z'\xc10q8 \xc5cM\x02\xefA\x9f\xf2\x17\xcb\xd0\x0cJ\x8b\xae` \x8c\x00e\x06\xdc\xe3cs.\x86\x1dy\xf5Y\xd9\xd2\xa0\xe7\x87\xd9\xb0j4\xba\xa4\xda%fU!\xca\xce\x1e\xc3N8g]\x87E\x98\x853R\x8c \xc9\xd6a\x9a\xc4bg0\"\xc5\xb4'\xa0\x8d\xbd\xe9\x95:*=\x84\x13\xe6\xbe\xef:\xc5I\xd9Z(}\"\xdc\xeee\xf2\xfe\x17\xcc\xe5\xeec\xcc\xe5\x8cP\xde\xbb\x01jo\xc2\xcb\xc1\x9e\xdeB\x0d\xef\x15\xe1\xe9\xb6\xfa1!W\xda\x1e\xfd\xea\xdf\xdf\xf3{\xbf\xbb\x93\xce\xbd\xbb\xe6nC\nn1hq\xd6\x8e\x16\xc0\xc12/O\xc2\xcf\xed\xaf+\xf9\xb5\xfd\xa9\xc4OIy\x9c\xbd\x0boH\xda>x\x94\x8f^M\xc7\x9b\xf2\xa5,\xcf\x87l\x11\xd2hN\xe2\x8b(_\x92\xb2\x8e\x0dj\xfc\xbc\xb5\xe5\xb7*C>\x05{\x8bf\xf5x4)\x9d\x10\xa2\x14F\\\xed\xbe\xe1\xa3\x82\x1f 4z\x9ag\xfdz\xcd\x0fN7\x07\xa1\xca\xaf\xea\xecaq\xcf\xf3 \xdb\xdclCr\x15\x82\xfb\xf53\xe1\xdb\x11\xbd\x04\xb2\x9f[[V\xd2\x99\x0b{\xcc\xbc+\xea\x80\xb5\xbe\xb4u\xabP)\xb7$EP~J\x96\x97\xf9'\x92\xd9\xc3\xef\x80\xa2\x11\x0f\xfb\xdc\xc5\x19_l\xcb\xa4\xc3\x1e\xf7\x0cb\xfd\x9a\xc1\x16\x9ft\xbe\x06+}\xfeK\xff\xe1a\x15^\xdb\xa2`r)\xba\xeb\xfc\xdd\xf1\x8cq\xa5\\%\xb6r\xa7V\xaa\xd4w\xbd\xa8=B\x15\x02\x8f\"\xc1C]\xc7a\xc3\x17\x0d\xf6j\xa3\xa9\xf5\x0f\xd3\xb8m\xc8IL\xa1H\x9d\xc30\xfb=\x85(LSX\x10:\xcfc\xc830b\xd4\x96\xcb\x8d{\xcew+&\xa20S\xd8\xf5\x02)x\xd2no\xd0a\x87\x08\xe0\xe2\xe6M%\xf5^\x1f\xa4\x96\xc5H`\x1f\xb4\xaa\\\xf4:\xaf\xd8\xb1\xdd\x7f`}\x9d1 S\x14\xd5\x15jD8\xcdW\xb8\xc0\xb6y\x1b\xc1!\x8dd\xf2\x97\xedr\xedt\x19\xae\x9c\x87]+\x10\xe1\xc8\x18\xd3^\xdd\x9e\xa1\xe6\x8eJ\xd1?\xc7\xd9\xf4\xfeun\xfcs\xbak\x83\xe4<[\x93\x82\x82p\xfbKsX\x16\xc9\"\xa1\xc9\x9ap\xefON\xdf.\xd3\xd6\xb9\xe9\x0c\xec\xfb\x9d\xfb\xfa\xe5\xd0\xadpd\xd4w\xdd'\xb8\xf0\xf4\xf5B\xd7\x1f\x0dE\xfa\xae\xe7:\xc7\xe3\xeb\xf7\xe7g\x97gz\xd0\xd1U+jA\xe3s\xd9%\xc8\x02)\xcc\x12\x8e\x99\xdc\xdd\xef_x\xae\x93L\x8bpA\xf4\x86\xe4S\xe0\x05\xa0\xcdS+\x8f\xc2\x12\xa0I\x10#7\x97ix\x07{\xe0dyF\x1c\x1f\xa3R\xecx\x0d;\x17\xee\xa4\xb0,\"\x96\xed\xaf\xe1:\xe4VE#\xc7\xe7\xa4(\x0dP\xe3/\xa3\xbf$Y\x9c\xdfV\x08\xc3\x0b\xf2%\xc9\\\x1e*\xa0H(q\x9d\x1fx\xd1?T\xc2\xec\xb7{\x1c\xbf\xfe\xf0q[|r0?\x1a\xbc\xba\xc2\x95\x14 \xde\xbe\x81bk\xeb\x8d\x07\"<\x8b\x12oe\x92L\x8a+\xc3\x8d\xa4\x00\xcc\xd2\xd5\x0e\xc4\xaecE\xa0\x1eP\xa3\xb6Zi-#\x02\x16\xa2v\xe9.Kq\x8e\xcf\x8f\x17N\x91\xa0\x03t\x1f\x9a\x9f\x85\x93\xd3I\x88n,\xd1\xfe\x04=\x9fka\xd4\xa5\xe3h7\xfb\xff^D\xfa\x17O=\xd7\xf9D\xeeJs`\xdf\xdd\xdd\xfe83\x96\x8e\x17\x82\x86w\xf1\x07w(\xf9\xe0~>5\xd9$\x17\x13\x871\x11\x05\xd9\xfaky]\xce\xc3\x82\xc4\xd7\xd7\x8el\xd4\xfc\x0d\xef\xfb\x1f8\xa2\\\x8e(\xe7#\xfa\xc7\xd7\xbe\xf1\xd8\x10\xab\xa38\xd2\xf7\x9b\xd7\x90~R\xbe\x97 |6\xf5M\x04\x99O\xf3wy\x14\xa6\x84\x9f#\xbe\xe4\x9e'\xb0u\x82~\x07\xd1\xa1\xacsVG]B\xbb\xb2\x02\xcd\"-T\x18;\\\xc34%8be\xe9F\xc2\x12\x19\x1e\x008\xde5#8773\xd8\x84\xc2\xab\x18\x13F\xc4\xf7\x9dl\xd6\xbd\xf0\xd2\xe2\xea\xf7\xd9\xffx\xb6\xf7y\x0f\xa9\xf4\xe2\xe5C{\xfb\xa8\xa4\xd2\xee\xeeK/\x98\x9a\x899\x93\x07\x17\x13\x9e\xea\x1b\x87\xf9\xbe\x07\x95a6r$V3!='5A\xeeC\"\x03\x84\xa2\x03\xb6\xf6foz\xa25\xdd\xecH\x87\xc6\xcd\x8d~\xcf\xb9\xea\xf5\x80\xf3t\xd74\x03\x18{\xbdw-\x19#b\xcf\x04\n\xcem3X(\x03_\xf2\x18B\x82\xa7!\x0d\xdf\x11\xc6XI\xa0\x13L\x8c\xa5\xf9\xf2Eu\xd4\x9e\x19$a?\x86\xb1\x8cW\x04\n9ju\xcf\xc7=)g\x95\xec]}\xaa\xcb3\x11\xd5J\xa0\xd1*\x11e\x13\xe8\x8eVc\x1d\xbf\x81uy\xfa\xbdY\xd4\xf0\xbdM\xce\xd9\x07\xbe F\xefd\xc8\xbf5W|k\xfc\x9b\x03\x9b\x90\xa1\xbf\xdb8'e\xf6{\na\x14\x91%\x85\x82\xcc\xc8\xe7\x96\xd3[\x01\x11\x02\xa9~\xdb\xa6f[\x14\xa5\xc5\xfd\x9b\xd3x\xc6\xc3\x1el\x07\xdb\x9aH\xc9x\xe2:\xdb\xc1\xb6\x03\x13r\xe5jnu\xaa\xa3\xd6(\x80\xef=\xbe\xe9\xa4\xb8\xe2\xf6\xb8\xb0am\x03z\x8et\xd3\xfcn\xdc3\xe0\x11\xc5\x8d\x8c\xb4\xfd\x90\xec=L(\xb27F\xac\xda2Q\x16\xa2\xad\xd6 \xc9M\xa0\x9f\xefx\xc1\xf4\xa1k\x9b\x07\xfc\xcc\xe7\xec\xa9|\xe1\x81\xa1\xfe\xf1\x15\x83.\xd4\x19\xfe\xa1Gtq\xae\x91\xc4!xAs@\xdd\x1d\xd4\x97'\x90d\x1c\x93\xac0f\x95 c\x0b|\x1c\x06\xd3\xd65I\x1f\xac\xb7\x97DH\x8cf\x84*\xfc0\xef\xb6\xd9\x8d\x07\x0fXz\x7fT\xdf\xa1\xcd\xb5\xfd\xddFs\x90\xdf\xc1\x1fc\xc2\x05iI\x9e\xc19\x89VE\x99\xac\x89\x94\xb8\x92\xcf\x94dq\x92\xcdZ\xc5\xc2\x15\x9d\xe7\x05\xfc\x9c\x84\xd1\x9c\x94i\xb8\x86w9-\x17a\x96\xaf\xe1\x87T\xfe|\xf5\xfa\x8f\xb3E\x98\xa4A\x94/\xfe\xd0\xaa#M\"\x92\x95\x04N\x8e/\xb5oz\xd6\xcb9\xe6\x82w\xa2\x84{r|\xe9\xf5\x949\xcc\x97wE2\x9bSp#\x0f\x9e\xee\xec>\xdbz\xba\xb3\xfb\xca\xd8\xe5\x9e\xaa\xde\x93b\x91\x94\x18\x14,)aN\nrs\x07\xb3\"\xcc(\x89}\x98\x16\x84@>\x05\x06_3\xb6L9\x84\xd9\x1d,IQ\xe6\x19\xe474L\xb2$\x9bA\x08Q\xbe\xbc\x83|\xaaW\xcf\xce\x11(\xf3)\xbd\x0d\x0b\x02a\x16CX\x96y\x94\x84\x94\xc4\x95\x1e/Zf\xc04II .\x9d\x13p.D \xc7\xc36c\x12\xa6\x90d\xed\xca \xc8\x9cp\x9b\xd0y\xbeb(\x9d\x83M\x92g\xbe\xf0s\xcdz(?\xa7\xc9\"\x11\x0d\xb2\xe28\x8b%\xd0\\\xaf{U\x12\x1f\x07\xe5\xc3\"\x8f\x93)\xfbOp\x0e\x96\xab\x9b4)\xe7>\xc4 k\xe9fE\x89\x0f%K\xc4\x05\xf4\xd9(\xb7\xf3\x02J\x92\xa6\xac\x86\x84\x94\xc6\x89\xa9\xfb\x8eE\xf0\n\x80-\x06\x15\xd3\xcbz\x05\xb7\xf3|\xd1\x1cgR\xc2tUdI9'X&\xce\xa1\xcc}\xbd\xfarU\xdd+\xb0\xd2\xd3>\x1a\x1f\x81sp\x01\xc7\x17\x8e\x0f\xbf\x1c_\xfet\xf6\xe1\x12~98??8\xbd\xfc\x15\xce\xde\xc2\xc1\xe9\xaf\xf0\xe7\xe3\xd3#\x1f\xc6\x7fy\x7f>\xbe\xb8\x80\xb3s\xbd\xe6\xe3\x93\xf7\xef\x8e\xc7G>\x1c\x9f\x1e\xbe\xfbpt|\xfa'\xf8\xf1\xc3%\x9c\x9e]\xc2\xbb\xe3\x93\xe3\xcb\xf1\x11\\\x9ea\xfb\xa2\xe6\xe3\xf1\x05\xab\xfbd|~\xf8\xd3\xc1\xe9\xe5\xc1\x8f\xc7\xef\x8e/\x7f\xf5\xe1\xed\xf1\xe5\xe9\xf8\xe2B\xaf\xff\xed\xd99\x1c\xc0\xfb\x83\xf3\xcb\xe3\xc3\x0f\xef\x0e\xce\xe1\xfd\x87\xf3\xf7g\x17c88=\x82\xd3\xb3\xd3\xe3\xd3\xb7\xe7\xc7\xa7\x7f\x1a\x9f\x8cO/\x038>\x85\xd33\x18\xff<>\xbd\x84\x8b\x9f\x0e\xde\xbd\xc3\x96\x0f>\\\xfetvn\xea\xfd\xe1\xd9\xfb_\xcf\x8f\xff\xf4\xd3%\xfct\xf6\xeeh|~\x01?\x8e\xe1\xdd\xf1\xc1\x8f\xef\xc6\xbc\xe5\xd3_\xe1\xf0\xdd\xc1\xf1\x89\x0fG\x07'\x07\x7fb}?\x87\xb3\xcb\x9f\xc6\xe7\x98M\xf4\xfd\x97\x9f\xc6,\xa957\xa7pp\n\x07\x87\x97\xc7g\xa7l\xcc\x87g\xa7\x97\xe7\x07\x87\x97>\\\x9e\x9d_V5\xfdr|1\xf6\xe1\xe0\xfc\xf8\x82\xcd\xde\xdb\xf3\xb3\x13\x1f\xd8R\x9c\xbdeY\x8eO\xdb\x9d>=\x1d\xf3J\xd9\xaa5\x17\xf7\xec\x1c\xdf?\\\x8c\xeb\x9e\x1e\x8d\x0f\xde\x1d\x9f\xfe\xe9\x82uH\xcd\xacC\xcdv\xe3]\x9e%`!\xf7\xa5\xf4\x02\x92\x8c\xc1g\xc4\xe3\xfc\x8a\xf3\xb5J9\x12\x97$\x8d\xc4s2\x1b\x7fn:\xf1S\xe2oAS\xc7\xdd\xd88\xea\x874Z\xb6q\x10R&AE\x04\xaa}\xf9\xab\x0e\xca\x00#dI\xa8\x12\xa6\xc1XU\xa5x\xc26<\x1a\xd0\x19\xbc\x92\xf7w\x95M\x89\xa7\xb2U,\xc1E%\xa4\xcbdA\x1a\xd2.k%|\n\x1b\xd5\xf0$\xa3ZVK\x17\xebCF>/I\xc4N\x992\xa1+\xe1\x83e\xd0\x8a\xe4VI\x97\x14\xd3\\_#o|}\xedT\xf7PUh\x99\x96\xb0\xab9ak\xe1\x94\xcbH%\xda\x00\xc1\x10\xe0h\x17\xad\xccd\xd4\xfa:\xd0G\x1d g\xe7\xaa\xd3\x96\xc6R\xefS\xaf%\xab\x9c\xec\x18\xae\x14\xe5M,7\x9e\xec\xce+*\xe4jz\xb5N\x1aZ$\xf3\xeb\xf3\xaa\xbc\x0f\xbb\x06\x9d=k\x14M\xc3\x04\xa0\xf9]%\xe0\xc4\xb7\xa6~\xe0\nidA\xb2~\"w\xa5\xbb24iu\xa1\x0f\nc\x84\x12\x9f\x90\xfb\xa2G\xe1I\xee\xa2gz\x1e\x19$T\xc1\xc2\xd0S\xd2\xe8\xa9\x8c\x9c\xeb\x86\x93\xb2\xba\xf54h6\xaay*\x90%f\xeb\x06\xf5Y\x0b\xa5\xea\xc9\xd0x\x8cm\x03\ntN\xd5\xdd\n\xa8\x8b\xa2\x85G\xaf\xee\x83\xd9~i\x8e\x0c\xa35\xe5\xe2\xba\x97\x8bw\xb3F\xa2\x90\xf9\x8a\xb7\x04-\xd6\xd5\x94\xb6\xf7-\xf5\xf9\xea\xf9\x90[s|E\xdd\x96\x11?\x06\x9a\x13\\\x88O\x86\xd5\xa3\x8d\xd5\xa3m8\xa3ze\xbc\xd7\xbc\xc2f:\x0f,l\xec\xa0!d%\x1bMhA1\xcd\x80\x94\xcf=\x11Oq\x10\xbf|\x1f\xa5K\x9b\x00\xbb\xbd\xf4D\x89\x92\xc4\xd6\xd6b\x94\x88\xcc\xba\x01u\xb4\xd4{qZ'W(\x11n\xe7\xcf\xb8>\xba\x1et\x9a=\xea\x8e\xa7\x86\x1do\x0d7,Q6\x9d\xe4\x96\xbdc\x0c\xb9\x94\x08\xffqO\x9e\x98\xa6\x85\xf1\xf7[\xbb\\\xc6W[\x08M\xf2+6\xbcb\x92_a<\xf7\xc3\xa4\x88ViX\\90\x92\xa9\x04\xb3\xf9\x90 \x97\x0e;\x08P\xe2\xa3!\x00\xaa)\n\xac!\xf6#\xe56ih\x9f(\xcc\xd3D\xda\xd0\xf2\x0bR\x96\xe1LV!\xdf\xf6\xea/C+*i\x18}\x12\xd5\xf0\xdf{2\xd5P\x85\x14\xc57w\x04\x03\xf0 \x06\x922\xde\x06\xe1m\xca\xe4\xad\xf8\xc2-?\x84\x1f_\xe0~\xd5\xf2\xecn\x91\xafJ\xc7\x83Mpp\xfe\x1f\xacP\xf8\xfd+\xf35\xe3\x0bc\xc8#\x96n\xf2|\xcc\xd2\xf5k\x80\x95H\x7f\xed\x99\xcc'K\xbb\xd8\xc9\xa4\x10\x8d\xda8J\x84\xbb\x1d\xae\xf0j\xd0\x9d\xe2zS\xdc\x19? \x0b\xd7{\x03\x9b\x9b\x14~\x80\xcc\xa8S,g\xa2\x1do \xa4\xec\xbc$\xd4-0\xfeW1\xd9\xbd\xb2\xe9\xed\xd6\xbf\x14\xa5'\xde\x07\x86\xac\xfdF\xb2P\x8f\xc2`\x1ceS\x15\x9em\x94f\xe2{\xe9\xf9\xe0\x9c\x84K\x9b\x10x\x90V\xbc\"Un\x85\xd0\x13\x10e\xf1\xea\xf8\xc2\"\xd2|\xd1\x12\x81\n\x88\xda\xd5E\xf4\xa5H\x7fi\x84\xb4\xd4\x0ei\xc2< \x0ei\xc8\xad\x140\x1a\x99\xd1\xca\xaaL\xfe\xce\xf1\x05\xfbaX\xf4\xd4\xb0\xe8\xb9\xdfH\xae\x16=i\xa6\xf3E\x0f\x9b\x89|\xd1W\xcdD\xbe\xe8es\xd1S\xe3\xf2\xa8C\x1e\xacN\xdb\xf0\x9b\xb2\xb5\xcb\x1d\xa7\xd0\xca\x9c\x98\xeb\xdcK\x1f$\x9b\x9b\x19\xfc\x00\xc5\x1b\x0f\xc8$\x87M\xc0\xf81\xed\xb05\x92o\xd3\xe6l08\xbdx\xaa#\x1c\xa1\xf2\xfcZ\x07\x1bcL6\xa3\xaaS\x0b\xda\xba\x84\xc4m\x18\x0c\xd5\xe0\x8a]\xec\xb9\x8a\xb1\x90,@B\\Q\x1e(\xdc\x90\x1b\xb6[E\xc7Z\x8dj\x10\xb8V\xbe\xaf\xba\x03\x1dF\x83\x9a\xf7\xf4\xea\xbe\x8b`>%\x9e\xebkcZ\x83\xf6t'\x9a\x97\x8c\xf6\x14'\x03\x16\x0eq\xd37\xaa\xb6\x08u\xc7A\xab\x99\xb3\xaf<\xe8L\x15E\x15\xd56\xb8\x87\x92\x8dU;\xbd\xd9\x9ey)\x06!\xed\x0e\x1b\xb1z\x95\x9e\xe9\xab\x015\xf2m!e\x90\xbaB\x16\x8e\x08\xffl\xd0 \xcbcry\xb7D\xd2\xc9d\xfe\x88\xf7Af:\x92;\xa4\xc7zH\xa3\x1e\x83\xe9%\xdfW8\xbb\xd5\xd4\xec\xf1\xab&\x19t^\xb0&&\xbf\xe0l\x1e\xdd\x15\xec\xc3*HJ-7\xb2\xd4\x9a\xde{{\xfeAgPv\x9f=\xf7\xaa\xcb\xd5!z7\xafwv^\xee\xbe~\xfd\xf4\xfb\xe7/\x9f\xef\xbc~\xbd\xfbP6\xc5\xe4\xbf\x1d\xe7\xf1\x0f\x8c(\xc7_\xff\x81\xbe\xf1\xb93\x02\x02?\xec)\xa2\xb0\xfek\xb1{\xf5\xa6\x1b1I\xdc\xde\xba\xd4\xed\xe9\xceC\x80\xfb\xe9K\x9d\xc0\x04\x01\xdd\xdf\x08\xc1l\x13\xe4\x8f\x00\xc1\xd5NH\x1a\x10\x8cU\xa3\xb9cDJ\x83\xc5\x9env\xd0\xca\x00\x9d\xf7\xe0 \xe5]u\xeb\x05\xf9\xdb*)H\xe3\xc5uV4I\x1d/`\x03\xb3xb\x01U\xae\xfc\xe5\x8b\xdc\x8e7 \xdeD6^du\xc6zz\x02[}u=\xfbf\\=`3v(W\x99\xaf\xd6[FT\x0c\x04\xb6?\x06_>N\xdc\xfd\xd1\xe4\xffL>^]}\xf7\xc5\x9d8\xbf\xbf\xf2\xdc\xfd\x91\xbb\xbf\xf1q\xd7\x9b\xfc\x9f\x8f\x1f\xaf\xbe|\xfc\x18x\xdf\xed\x7f\xdc\xf5>\xea\x81Yx\x00\x98\x8f\xb7\xdf\xfd{oH\x07\x8b!S\xc3\x8eI\x17\x8bV\x92t\x01\x98F\"k\xc3\xad\xb0\xc7\xc6\x1ed\x08\xd4%R1JB\x158B\xa64\xdc\x0em\xa0F .?\x8f\x05\xc2\xa3\xc8n$\xea\x9b,A\xf9\xf6H\xa4\xd3<\xf7^\x86\x0e\xf7BD\xf7\xa4\x1f\xcd\xf2\"A\x99pm\xd3\xcaE\x17\xf5\xc1\xb9\xbe&\xe5I\x1e\xafR\xe2\xe8\x1a B\x1bAU\x08AC\x9b\x05Y\xe4\xc9\xdfI|\x11.\x96)y[\xe4\x8b\x8bhN\x16\xa1\x90*\xf0\x8f\x87\xa8,\xf8\x97\x93w\xe3\xcf\x98\x8d\xb3\x10\xf8\xf3/\x8bT+\x94dSR(\xefe\xbbfq\x00\x824\x81i\xd4\xac(z(\xec\x98\x89\x1b\x0b\xdd\xcc}\xf1\xfd\x0b\xcf\xb0\x0f\xf0\xd3\x8b\xd7\x9e\x91\x97\n\xed\xeb\x83\xa0\x10\xd4\xf3(T\xf5\xdaXKFF\xd0\xddZ\xfd\xae\xfdk-|\x19\xb6+\xe1\xa2\x99\xe1qm\xa5,\xa7\x95\xc7\x10F\x8bg\xbd&\x8b0I\xef\xd1\xc2\xaa$\xc5\x1f _\x8c \xca\x17\x83\xda\x12\xfdb,(\xd9\xa2\xc9\x828\xc3[t\xe5\xf5\x95\x17\xd0\xfc\xf8\xe2L\xa8\x84\x19\xf8\x02\x83<\x05\xd1\xc4\xf0\xb6\x06\xc5u\xe3\x95^O\xd3<\xa4\x8f\\u\x92Q2{\xf4\x0e\x0bT\xd8G\xff\x83\xb2\xca*\xf6\x94\xb88\x10 \x8dW\xad\xf2\xa5\xdd~\x13\xdc\xdb\xbcLw'\xa4\xcc\x82mt\x17\x9d\x0frr%\x99\xdeyF\xff3 \xc4f4h3a\xf2AO6\xc14/\x16\xa1\x812\x02\x81\x12V\x13\xd4O\xbcv`\x13\xb8\xa9\xcc\xca\x18\xd5S\xc2%\xf6.)\xdf\xae\xb2\xc8s\x13\xc6c%\\O\xda\xf9\x90}\xca\xf2\xdb\x0c\xb5 \x85K\x1b\xec]\xd7\xd4\xa46\\Xa%\xcb\x0d\x93<2[7\x89\x7f\x00\xa4\xa3\x15U\xd6\xfa\x8ep\xf7\n\xf6\x9b\xaf\xa3\x96)\xa8|r\xd3RP\xcbR \x99\xd9\xb1\x14\xca\x97\"P\xe1\x8035V\xb3Vg\xaa9\xef\x1c[\x16\x00m\xce\xb26\x844\x93\xcf\xa2\xe3\xdb\x0c\xc9\xb0\xcf\x0bC\xc0f\xf60\x1c6\xc3;j\xf3\xf7\x1b\xfc\xbe,\xc841x\xb4b\xcfuU\x03F\xab5g\xba\xe5S\x9b\xad\x16\xe6\xef\xe3\x8aG\xb6\x1c\xe0a\xc7\x01\xceN\x90\xd4C\xa8\xfa\x97\x9c\xe2a\xdf)\xee\xb2Y\xbd\xc3K\xff,\xa7\xe1\x8cM\x8e\xc3\xcd\xa5\xdc\x1b\xd8\x87\x1bF\x96\x8f\xd0>\x16u\x01\xee|\xb8\xe6\xde\xd2\x17\x13\xf6\xdd\xf9\xbcH\xb3r\xc4\xce\x8e\x1b\x96 _\xd1_\xc1\xb5\x85\xc0Q\x0f\x05\xc48\x91\x0d\xf9\xb2\xdc\x11\x83\x07\xd8\x03\xfe\xff\xcb\x17\x98qK\x10\x9f\xa7HU\x0d\xe5\x85\xe5\xe1P\x023\x11\xa9>\xae\x88\xbf\xf5$\x93nn\x9b'\x04\x9e\x0d\xd3\x81ns\xe5\x13\xc9\x1d\xc8\xfd\xb6\xb2\xca\x85\xdf^v\"\xe4V\x9d\xa6\xd6\xf94g\xad\xcf\xef\xdd\xba|\xb6\xac\x8b\xfb\x8d\x0bs\xaf\xf6E\xaeV\xa6\x01\xe4\xb6U;\x91M\xfd\x85\x99\xdc\xee!\xa7\x0f\x199\xad\xec\x19\xb4$\x95\x1b\xf0\xc2N\x9d\xb2\xbe]\xe8q\n\x0e9\xde\xd8\xb8\x98\x1c*\x84\xf7\x97/\xb0T?\xd4$7#\xc6-\xd3\xd5h\x87\x95\xe2H\xa2\xfa){(\xde\x03\x06\xb3h\xa9\xd2\xb5l\xf2a\x03\xff\xd4R\xbc\xc3\xba\x90Jc\x9d\xad\xde&;Wv\x96E}\x0ed\xff:\x0fm\xfd9\x93\xa5\x04D\xd91\xbd|\x16\x93j\xd4\x12\x1d\x1e^UG\x16\x92M\x07l\x04\x07\xd04\xb5\x9dN\x0e\x91\xef\xc1\xff\xcdOg,\xfd\x8c%~b\x7fJ\x9c\x8b\xee\x85\xf9\xdaw\x80\xc9\xa7\xd9\xd9=hw\xbe\xe1\xf3H\x9dA\x8d\x18\x94\x03p\x1byx\xba\x05\xce\xd5\x87\xad\xfa{d\x99.\x86\x15h\x82\xc7{Tw\xe5;\x05\xd1\xa8pa\xf0^\xa2[\x8e\x04\xde\xf7L[\x17j\x94\xcc\xa4h\xa8\x0fQ7\xa9\xcd\x118\x07\xd9\x1d\x9d\xa3\x0dT\x98\xc1\x0dAc7\x0bU\x80\xe1Q\x86\x9e\x08zC\xa5\x8doeH\xee\x11\xcf\x99\x018R\xcc\xdc\xb8 \xffSv\xd4W,\x15&\xcd\xd9\xf9\xdbB\xff\xb7lQo9WV\xa2]\xb8Xa\xc6\xe1M\xcc}\xb7\xf6\xfb\xab\x0fcV\xd1X\xef\xfaW\xe3=\xc8\xd4x\x89'\x05\x8e\x11\xff\xda\x84R\x86\x0d\xb3\x86\x9c+\x97x\xc3s3\x93\x19lL\xa24\x94\x81{M~\x0b\x92,\xc6\xc0*\xceG\xaa\x85c\xd3\xaf\xe1\x00\xcda;.\xa5X\x7f\x92\xba?\xd3\xbe\x1b.-\x7f\xda\xaf&Q\xcd][t\xcf\xd5\xf0\xc8\x9aq\x87\x95V\x9ex\x15\x87\x05O[\x84\x9f\xabxrU\xc6Fb\x85\x1b\x95 hw\xc1`\xd7$\x85\"2OCl\xd8YY~?\x8ds\xd5\xd8\xa0\xbb\xe2\xc4Z\xb1\xeaz\xc5\xb0\xd2\x0dGY>d\x01\x06W\x19/\x12\xca\xdd\xdcc\x9a\x12\xac\xa3\x9ayy\xbb\xd8\xf8\xaaMz\x9dG\xac\xfeI\xf3\xfb\xaeV\xbe$z\x0e\xbb\xd4\x03\xa9&\xe5\x06\x9b*\xc6(D\x06\xa8\x10\xbe\xebL\x1e\x152X\xacJ\xca\xd0g\x08<\x1e\xf2\x9a\x88[)\x8b\x1b\x05#\\\x11\x0eo\xf5\xcc6GD\x16 \xed\xb7\x9f\xe7\xfe\x8f|X\xf9P\xfa`\xf0\xc4\xac\x83\xb9\xabm\x03\x0c!'\"\xe5\n+\x1c$\xc4\xd4l\x01~F\x05'\xb7\x9d\xce\xd5\xd2\xda\xe9\xd2\xd0\xceDo\xb1\x9e\xa1\x8b#U^\xe3\xa9\xc6oc^5\x9f|\x03\xcd\xc3F\x1f eZ\xbe.\xbf\xff\x90E\xe1j6\xa7>\xac\xb2rI\xa2d\x9a\x90\xb8\x1a\x1bv-\x00\xf7\xf7\xb0\x89\x0e\xa2\x1d\xcf\xe4.\x84\xb7\x17\x05\"j5\xa7\xde\xa3&\xdak\xcdq\x82^\xa2\xd4\x19\x98\x90+\xbb\x92\x05\xd7\xc2\xc8<\x0f\xca\xdb\x04UXt9\x97i\xca\xa2\xb0$\xb0k\x8e\xf4/\\\xb0\xa2[t3\xd5\x82>\xa4\xdb\x9f\xb0\xd2\xa7\xbd\x95\xfa\xcdu\xba\x7f\x13\xcf\xee\xd9\x84\xfa\xf6\xf4\x9e\x0d\xca\x9b\x7fc\x99UE\xd4\xf7[\xe1\xb1\xfd\x18.\x97\xe9\x9d\xe8\xe0J\xd7{\xad\x84\xf4\xb9k\n\\\x83,\xd4\xfd\x1a\xc4C/\xc5\xeb-n\xda\xe2y\x95^t\xc9C4r\xc7\xe5Pnnz\x90N\xca+\xad\x8bF\xfc\xa3j\x954\xb1L\x18\xc7J\xcc\xd0N\xe5!\xb6\xe3\xc26$oX\xfc\xce\xa4\xb2\xda\x1aYV\xa7^\x17\x96\xecAU\x0d<\x93\x91[5\x02)~cx\xd3u\x94/\x0e\xfa\xff(\\\x1a\xc8.y(\x90\xaf:8\x02\xaaU\x94\x04\x08/\xa5\x9f\xf6\xae\x074\x87$\x8b\n\xc2\x90\x0d\xfa\xb7\x08\x9c\xd6\x92J\xe4\xea\x9b\xe9/\xd9\x7fZ\x84\x11\x1e\x82\x8d\x04\x0cL\xd7u^\xe7h\xe6\x00\x1b`\x15\xb9&<\xfa\x8du5\xd9\xc3\x03\x88d\x12\x83\xee\x83[\xfd\xdec\x8c\x8dyU\xd0\x08[F\xd8J8M\xf0\xad\xeb\xd4\xbf\x13\xfb\xb7\xdaA\x9a\x0e\xe3\xad\xd6F\x07\x81\xad\xed\xd1\xb3\x156:\xc6\\\x15\xe5\x9ci\xeb\x8ax_g\xf4\xd1\x87\x98~\xe6>y\xd2\xb9/\xda]2\xb7f\x05t\x8a\x0e\xc8\x1a#\xd6\x97G8\x02\x90K\xd8\x9eh\xa3\x0d\xb7J+\x19\x8a\xe8\x8dh\xf0#cC\xaa\x0b\x0eF\x9e\xa6\xb0\xf04\x96\x93!\xb3\xa1\x03\x83\xc6\x04N\xd0\x9bjo\xbc\xb1W:\xa9\xf6\xcc\x16\xb4\xf8\x0e1\x13]\xcbh\x03\xeat\x10,\x9b\xc8\xd26\x8d\xc4\xdd\xf1\xea\xdbx\xbfE\xfc\x19(?I\xe3\xc3H\x8b\x16e\xea\xeba\xbe\xca\xba\x05\x02:\xbboS\xae\xa0\xed\x85m\xc3YRy\x94\x14\xd3`q\xa0R\x87+\x96\x16\x9c\xfd\xf8F\xe3F\xec#4\x1c\xe6\x95\xbaJ\xa3T\xbfI\x80n\x0cD5\x0f4\x99\xfbl\xe7{\xcf\x0b.hA\xc2\x85\xa0H\x82s\x12\xc6\"\x02\x1b\xbe\xffR$T\xbcg\xee\xee\xeb\xefQ\x80y\xb4Z\xa6\xe437\x80\xe3)\x97E\x98\x95\xd3\xbcX\xf0\x8aww0\xf5}X\x96\x97\xf3\"_\xcd\xe6<\xf3\x8b\xe7\x83LMz\x1d\x01\xf28_&T,\xdc9>\xdf\xf1l\xf4\x9fA\xd7\x1e481II\x12\xc6|\xa1|\x84\x07\xaa\xe0\xa7PF\x8b\xbbf\xd24\xc9\x92f\xc0E\xdb9\xbd\xd19\x07\xfa#-\x0f\x08o\xd4~\xb6\x93F\xaf\xec\xf9\x04R*\x8c\xe6\xfb\xea\xb3\x16^d\nd\xe0o\xc2\xc8 \x82P\x1f\x1a,\xb9\x93\xc5\xe8fk\x8b\xf1y\x18v\x1d+`3h-k\xbe\x07\x02\xac1\xca\x8bO$>'\x7f[\x91\x92\x96o\x0b\xf4\xe9mJ\x96\x8bDP/\xcdPlO\xd3\xdb\x92\xcfW\xee\x91\xa5\xf5\xedk\xc7\xeeV\xb7\xd3]\x9b\x0fYq\x11\xc6\x06\x0dn\x8a\xfc\xb6\xe4\xd4\xcb\xc4Y\xef\x04\xbb;\x8e\x0f\xec\xc7\xeb\xc0\xb9\xaa]\x81\x04kR\x94I^y\xf9\xf0\xe1{\x8fk\xd2\n{\xda\x04\x87w\x99\xe8KpW\xed\xd3\x0b\x1a\xa2-\xfc\xac\xdd\x9dT\xd8\xad\xbc\xd0\x8e\x954H\xb29)\x12\x81\x15^\xed\x1aX\xaa\xc8h-\x02(|\x12z\xa6#\xdc\xe0\xcf\x06\x99IL\x05\xfe\xd1=\x0e\x80\xd4uvw\x9f\xefJG6\xed,\\u\xebC\x92\xd1W(i\x025`\x8d\xd7R1e\x03\x98\xfb\xa8\xa1\xc5\x1a}iE\x0d\x0b,l\xf983bg\x10\"6\xee\x82\x8a\xa3C\x0420\x84Q\x05e\x1fSU\xf6k \xd5\x11\x99\xf0\x8b\x8e\x93\xd9\x15\xfc\xeaz\x7f\xea/\x10\x19z\xb7\x0f\xbb/`\x04\xbb/\x9e\xbdzn\x99\x85FW\xd0\xaa\xf4\xcb\x17A\x0c\xe7\xb0\x0f9\x8c\xc4\\\xa4\xf5\x87\x94Q$)\x8c \xf2\xcd\x95\xd4\xb1~\xdc\xf6w\xafF\xe6az\x18\xa62,\xa7/\x0f\x02\x12\x1f\x15a\x92\xa9\x89\x1c\xe7i)\xcdr\xfclh\xa6\xc5\xa4\xa4E~'\x12\xcd+\x82\xf1\xf99\x7fE\x82\x98Dy,\xa2\xc9\xd8N\xaaF\x1eVxZ\xb5\x86B\xb2q\x16\xe5\xa2\xb7\xa4\x95\xf6\xe5\x0b8+:}%\xe5I*\x13\x87 l\xc5\xb5\xa1rD\xab\xe4)\xef\xb2HJL\xd8\xfb\x0dn\xe5\xf7\xdcZW+\x9cg\xa8\xff\xd2\xab\xb8\x0b\xedC\xb3\xef\xc4\xe4A\xdc\xaeoU\xec\xd8\xad\x84RpY\xf4]\x16u\xe7\xe3\x81\xe0\xb0\xe3\xd1\x8d\xfd@d\x14c\xff\xa8\xe4C\xb4\xb9%\xb2\x81\x8a\xc6 \x15\x7f \xf7\x1eII\xe6+\xbf\xd9\"X\x1b\xf9\x8a\x871\xf5\x0c\xc4\x87\x99\xa6\xd2\x9f\xad-\xe5x\xf71r\x80[\x9fJn\xeeC\xe1\xf9\xca9\xe5^\x08\xa6\xdco\xad\x03\x97\x9br\xb9\xa8\x14\xa9\x12\xc1\xd8\xf3+,V\x19\xe3\x15\xdc\xdc-\x1e\\\x81\x0f\x17\x1cT\xecZ(\xe89\x8aO\x00es\xd0A\\\xf5+\xf8\xe0\xad\x01\xec\xc1\xd8\xd5YD\xfd \xf1\xcc\x90{\x07\x7f\xb7\xb6 C\xde2\xb9\xa2dX\xea-gB}\x8cfZ\xba\xd78\xcd\xfcj4gsv\xed*\xef\xf6\x91\x1b\xbfXi!\x05\x01\xa8@Y'\n\xf8kl\xfa\xba\xdb\x8d\xfciX\xd2\x1f\xbb2T`\xa6\xd4\x88\x8a\xcem$\xaa\x03\xc2\xae\xb9\x03\x92\xdf\xdai`-\x8d<\xcc\xc8-\x84\xfcf\xb11\x016\xba\xe0\xce\xbc\xad\xb9\xe6s\x930\xd8p\xe7\xfc\x12\xec\x8ew\x00\x8d\xbe\xd9\x8f\x06-\xe05\x1c\xa0\xdeY|\x9f2n\xf6V#\xfaX~N\xa6(\xe1\xa2ok\x0e\x0e7\x08\x9e\x94f}\x0c\xbe\x86\xca\xc5\x87\xc4\xcb\xe2\x8b\xed\"A|^\xeb%\xd7u\xd1\xb5\xbd\xac8\x01\x95\xc22e\xaf\xfej/\x8eg\xb4R\x98\xbf\xef\xc9/\x9e\xe7\xc3T\xb9-\x1e\xb4\xa67M\xa4\xc8E\xe9\xc6k\x03\x15\xec\x19\xfaP\xf6F(_\x05>\xc7\xcb\x03\xe5\\\xc4\xa8+r\xa6\x18\xe6\xa4\xf2$\xe4a\x87\xf9\x17\x97\xb7^\x7fSk\xd9\x1d4\x9ake4\xa6Ad\xd0\x17\xf0Q>\"\x06\xa3<\x83\x9e<\x01\xaa\x10C\xb8\x06-\xe2Hb\xe4\x98\xa59\x06,\xfc\xd5\x15\x07\x84\xc68\x16n\x8d\xbb\x07\x8d\xf3\xd6\xdawj\xa4?\x0c\xb6\x0c\xeb\xca\xb1\xb2\x86:\xcc\xb2\xa0j\xf9PD\xcfo#\xd8\xc9g\x9b\xbf\x8a\xf87b&;\xc1\x91\x8b\xcd\xcd5\xf4\x8a\x0e\x83AtZi@l\xe6\x93(\xa9e\x05\xe6\x0c\x95R\xf4\x8a\xa3\xcd\x92\xcf\x1b:\xfd\xcb\xf1\xc6\x82k=\xa1w \xbc'\xc3\x1c\xbb2\xd0'\xce\x86\x0f+\xd8\xdc3\xc9\xd3\xd8\x93\x07a\x9a\xf2\x83\xa0\xe4^\xd8\xe4\xee\xe3;\xa6\xf2\x92\xe6\x83\xe30\xd2\x82\x1f\x00Mx\xd9\xdc\xc4\xac\x1dG\n'I\x18\xb9b\x11\x0b$\xa2\xaf\x89*\xe7\xf1\xecb\x04qN`?l\xe7L\x1b\xd6\xbb(\x08)&\xee\x94\xc8T\x9c|\x10\xcdW\x99\x85\xd1\x92\x0f\xea\x0b\x05DP\xf6\xddy\xb99r\xbf\x88\x87\xc1}\xb5B\xbb\x88\x99\x1a\xdc\x1c\x8c \xad\x16-\xf5\x19\x036\xd5\xc0\xc1\x0b\xae\n\xb9\xa3\x81S\xdau\xf4\xca\x83\xbd\xa6\xb9\xf9\x1e\xb2\xd4ZW\xa9\x87\x0bhn\xa4Z\xb4\xc8H^\x86\x06fM\x07\x9d\xc2\xa7\\\x8f\xb4\xbc:\x85*\xf1\x96\xb6\x07xx\xf0\xc9\xd5\x1b o<6\x0c\xb4=\x92\xa28\x9c6\xebJk\xe1\xe9\x0c\xc2\xca>A~\xb7\x171\xb3s$e\x1e|p\xf8pZ.\x92\xf4gF\xe8\x08\x0d\xad\x84\xc8\xb5\xdbI\xa3\xfe\xa8\xb7{\xd5\xd4\x1b\xdc\xda\xa8\xcfW\x1f\x1c\x8d\xe9\xe6}\x85\xa4\xacE\xbfBYI\xcbX//\xe3nH\x18\x07\x8e\x0f\xce\xd1\xf8\xfd\xce\xce\xce3\x8b\x8f3ho\xf0*\xb9\xd7\xfd\x99\x85E\x10\xb1\xb4\x9e<\x11\xbf\x82yX\x1e\x0b~\x0bl\xa1C\xa5\x9b\xe8z\x99&\xed\xd2Wh(\x07{\x03s\xfb\x16X\xb8\xf3\x0d=\xeb\x08\xe0\xd5/O\x92Z\x90\x1bsU\xdf\x94\xd4\xfc&\xdb\xed\x9c\xe3\x92\x0e\xa6\x9a\xbc\xa4\xc2\x8f\xce\xfaN\xcb\xaf\x88\x85\xe6\xbd\xe2;y\xce5\"\x9c\xb4\xee\xe5}P\x15G\x97\xc9\x92\xf4a\x07.\x01h\x1e4uP\x90\xc30\xcbr\n\xac\"\x1f\xd8\xafB\xdcp\xea\xac\x88\xd6r[$i\xbf\xa3C\xb2\x9e\x1b\xf0\x1b\x18s\xbb\x8d\xfd\x86\xc1#7\x88\x0b\x85\x8d\\\xa5\xab\xd01:W\xa1_V\xae8\xdd\x02\x17\xb4P'4\xb6\x1fi+$\x0d\x94\xe2\xdc\xed\xaa;L\xf0**Y\x06\xd3\"_\xe8\xf1\xe3\x00DH\x05\xcb\x16D\"\x85\xebWpT\x8dT\x18\xe3\x0b\xf6\xf1U\"@FmsEX\xbc\xe1\xd1$\xd3\xcd\xdak;\x86\xac\xaa}\xe1\xf9\x90\x0b\xb9\xfb\xfe\xb0\xb3[R\x03\n\xc8\xf0\xa5\x0f\xa7\x94\x14@\xb2\xd8\x16d\xd3D\xdd(G\xb4\xc5y\x86\xd8\x8b\x19\x9e\xdc\xab\x16\xe7m\xe7\xd2A\xb9\x9e1Y-\xc9'\xb4\\$\x80B\xdc\xd4\xa4\xf2>\xf7\nN\x1az\x80'\xe1\x1dn\x15>\x11\x98\x1bQ\x0fF'+Q_\xc0\xf1\x8c\xd1\xa3\xb9,A\xb1\xa3\xc989\xd4\xbc\x8er\x0dm\x1eg\xeb0Mb\xc8\xf2l\x8bW\xbb-N\x1a\xe4s\x1c\x0f\x95\xc5\xb9/\x8e\xe6\xbc\x87\xcdy/xJ.\xf9\xd0v\x10\x10\xb9\x069\x97\x99\xf2\x00\xd2n\xde$\xc0B\xc3\xde\xaf\xa4A\xb6\xf5AU\xae\xdek|S\xd5}\x078\xd1o\xf4\x8c\xd7Axw#\x17E\x8b[\x82{Jl_\xda\xe1\xc2G>F\xf2H}\xbeVz\x18\xf6\x8a\n\xee\xb2\xa4\xda\xa0\x8c\x88\xcc\x95\x0d\xcf\x15\x03,\xce#\xcc|\x9e\x94F\x18\xf8\xce\xc2\x18\xb9@>\x95\xd8j\xd3\xaa\x1b\xc9\xeaF\x0b\xb8:8\x12m\xde\x0c\x9a\xcb \xed\xfd\xa6\xeck\xa7\xc3GR-\x18\xc4\xed\xc1\x05\x0c}p\xc3=\xb6\x19\xd8Z\xfb\xfc\xdb\xb8\xe0n`\xc3\x1d7\x02\xc3\xcd\xbb\xfaH\xb1\xc2\x08\xf4P\x84\xda\x83\x07\xce\x08\xb2\x1eY\x85\x90<\x8c \xe9\xce\xc8v:\x8fgo\x07M\x1f-\x86S)\xca1O\xc3\xc8\xc8\xe4\x1b\xf3Z\x85<\x9b{\xd0vs\x06\xb5\xa4G\x95\x94\xacj\xfc\xd1\x89\x9e\xcb.\x8c\xb5\xf2A\xa2\x8cvL\xa0& \xc3\xa0j\x10\xf1\xa4\x11\xee\x1c\x1a77\xbb\xea^eCjo\xf0l\xcdV\xda3 \x1b\x16H\x9e\xbflm\xf9\xca\xad(:\x82\xac\xef\xcb\x14\xa9\x07\xbe\x19o\xcf\xda\x02\x13\xbc=\x93$q'\x11X\x12z\xd4\xba1\xef\xa6\x95\xd0\xd6\xd2\xe2\"O\xb8\x99\xa2\xf9\xbb\xfc\x96\x14\x87a\xc9\x8d,6\xdc\x893'\x9f\x19w$\xee\xdd\xd9\xff-\xfc\x11\x96Q\x92\xb0\x1f7I\x16\x16w\xf8+,\xc9\x8b\xe7\x98+*\x9f\x8a\xff[OE\xb1\xdd\x17\xe8k\x17k\x90\xbf\x8b\xf0VQ3r l\x82\xe3xZ?P\xcf\xa8\xb2\n\xd0Ng\xe9`\xb2\xde\xf3\xe8d\xb2G]W\x83+\x83\xf2\x81I3\xd7\xca&5X\xe6[\x93\xda\x89\x91\x83&U\x9c\x83\x91\x91\xe2F\xae\xba\x97\x93\xee\x18W\xe3\x80h\xef\xdd\xe6\xe8\xbc&\x84]\xdf\x87\xcf\xc8\\\x85J\x15\xd7C\x1e\xe3\xc4\x19\xb1\x96,\x96)Y\x90\x8c\x92\xb8\x87\xb5\xa9/\xe7\xb8h\\\xfdF\xb2x`g\xaa\xbb\x8c!{\xdb\x1a\x90 \xa9\x02\xc2\x055\xe2\xeeW\x11\xbd\xdf\x8b\x99\xa8\xcd\xbf\xa1\xe9$\x83{\xa8\xaf\xee\xa8\xa5\xcc\xabP\xf1MQ\xab\xb0\xc8\xcbc\x8e\xe2p\x87\x16R6\xcb\xd8\xad\x06\xd2\x192S\x80\x07q\xad\x1f\xb4S 7\xfdJX]\xd5\xb9\xaf\xd2\xb2\x19\xbf \xcc\xb3\x88TB\xb7\x0e\xd2\x8d\xd6*G;\xbe\xa2\x9a\xd5\x16Q\x83r\xa8\x14-Fe\xe0\x16\xacT\x97\x8c\xdb\xee^\xdbJY-\xd3\xd5v\xa5\x84\xae#\x14\xd1\x81\xf6\xd8\xda\xdb\xbcl\xf4\xc7\xca\xe7Z\x9aw;\xdb\xc7\xd8\x8d\xf7\xdc\xf9\xf5%\xf7Z\xfe\xd6\xb6\xe9*S\xf3ToZ\xae:O/\xbf\xcb%%Y\xecz>\xd0V\x0c\xf8\xdf\xd5=U\x03\n~\xcf\xa0\xd4}\xb6\xf3\xcac\xc7\xe1\xf1bA\xe2$\xa4\x04\x13w\x87\x85\x0ex\x8c(\x83F\x04\xf2\xbbf\xe7\xbf\xb9\x1b\x99\xfb\xe2\xf5\x8e\xe7z\x95\xdbN\xc6-a\x98\xc8\x17\xafw\xbfa\xa8\xeb\xcam\xfc\xcb\x1ds\xf0\x84\x17\xa6\x88?\x99\xfb\xea\xa9!\x86\x97n]-\x0e\xf6f\xc6\x95)jSWx\xa0R*E\x867\x9a\xff\xc5\xb4\xa1.y\xdf\x05\\W^\x1b\"_u\xa5\x0f\xb51\xa2\x12\x9f!\xb4\x98W6\xcb\xe1\x85@\x86\xc1W\xb9A\xb0W\x9b\xbaF\x9a\x93\x05~F\xa0sI\xf4p\x11y\"\xce]\x04\x7f\xd8\x83\x1d\xc6&\xb0\xb4\x914H\x96vN[\x90\xba\xa5\x1by\xde\x1b\xe0a\xee`s\xd3p\x1d\x85z>\xaa\x94\x95rq\xc2T\x1c\x8d\x13z\xe5C\xe1N\xbdz\x8c\x1a\xbf&R\x15w\xc9\xdf\x00\xcd\x0d#\x89\xd6i$\x05\x95Z\x07\x86\x11\xb5&\xd1\x1b1\xd3\x8bHaJ\xc2\xc4nD\n\x8aT\xb8\xf1\xe1+\x97\x12tw\xaa\x06,\x967\xce#\\r\x11\xc0\xe1\x92|\xa6\xa7yL\\\xc7\xe9p\x1cn\xd0\x00QT\xaf\x06\xdc\xaf \x83\xd3\xc1\xe6{\xf2\x80\xe7\x97\xeb\xdc=\x16\xb5\x9d\xdfC\xfc_f\xfd\xfe/\xb11\xe3W\xb3D\x05\xad\xd6\x9a\xe4\x94E\x8e[;Z\"B\xf3\xa3\xca\x8f'8\xd1c\xd0\xc8\x077l\x1e\xc4!\xe5\xe1|\xf6`s3\x81\xff\x80\xa7\\\xdd\x01k\x0b\xcay2\xa5.z\xa1\x10\xe2\x17ix-(\\6\x82 \xad\x96qH\xc9\xbb\xf0\x8e\xcd\xf3\x00*\xd7@\xb2cD\x0f\x83\x80u\x19\xde\xa5y\x18w\x84\xfb\xa9;\xf06I)\xe9>\xe5{:`\x10\xc9\x0e\xeb@9\xcfo\xfb\xc9C\xc6\xa0\xb6|B\xf5\xf8>\xe7\xc1\xb4\x94\x04#UE*\x17\xb0\xba\xfby\x06\xc5\xb6\xe1\xae:\x86ke\x1b\xb3\xd9\xc8\x14\xbf\x8e=l\x16\xb2\x91\xe1.\xc5f]\x88s\x17\xcd\xc3lF\x84UW\xff\x0c\xdes\xfe\xda\xbe\xe3\x1d\xe7\x11\xa70|\xe4)\\\xe41\xb9\xd7\x0c\x9a\xb8/c\xd0\xae\xf6\x06vR\xdc\xb1\xd7|\xf7\\\xf37\xa7\xcd\x9f\xb5\x91\x81Vr\x8a\x1b\xcfi\xb3p:Z\xd1\xca\xb1\xc1:m~\xae\xc2J2;\x83+\xee\xa2\xf2\xbf\x1ea\xe2\xf5mH\xc9\x8fd\x9a\x17d\xfc\x99D+\x14l\xd2 \n3\xf1\x8a~.y\"k\x0cOR%m\x1e\x96?\xe5\xe2\x12\xa6\xfa\xfeKB\xe7'\x84\xf2Y[\x86E\xb8 \x94\x14\xe6\xd4\xe3,JW%\xab\x94P\x9ad\xb3\xb7ya.\xf6\xe3\xddqL2\x9a\xd0;\xfc\x1e\xa6i~{Y\xdc\x1d\xd3\xb3\x15\x95\x85\x16\xec\xa8\xafn\x0ddj\xa1\xbf\x96\xcb<+\x89\xb9P\xa9\x16)\x1b\x05\xf8\x1b\x0dg3\x12\x9f\xc9\xb1\x96\xcd\xa1\x97\xac\xbb\x97\xe1\xac\xca{Dh\x98\xa4\xd5\xab)\xfby\x9e\xd3c\xaet\x87r)\xca\xa3Z\x88\xf6\xe6rzo\xc2\x92\xbc\x0f\xd1\xacO\x00@Rw`\x9ad\xf1Q\x95\xc6+!\xd1\xaaH\xe8\xdd\x91\x96U\xa6\xf3i.\xf2x\x15\x89\xa6\xa2<+W\xb2\xdd\xbc9\xc2eH\xe7\xb2\xfcb\xcd\xfd!I\xe3g\xfcM>SRdaz\x94G<_\x92M\xf9^M\xca\xb3\x83\x8bg\xbc\xec\x92D\xd5\x8f\xff,9\xa8\x9c\x932O\xd7$\xbeX\xdd\xd0\x82\x88\xe6Y\x06\xedC+\xbdQS\xf5r\x91\xaf\x8a\xa8\xce|Ay_WE}\x19\x8b,\xaf!>\x82\xa2\x15\x94\xb9\xafLA\xdaQ\xa5'GyA\xd1\x0c\xf1Wt\x87\xf8+\x9aH\xafn\x13cm\xbf\x97\xd0nVa\xb0\x1c\xfd\x08\x17\xecL\x9d\\1\x96bF\xe8q\xe6N\x9c\x05\xa1\xa1\xe3\x83\x83K\xe6T.\x9e5G\xb5\xd4\xf3a\xe2T\xdb\xact\xae<\x1f\x0f\x8d\x12Eh\xffy\xe1\xb9\x93+\xcfC\xc8\xea\xb1\x87\x94\x97\xa0\xc1I\xb8\x0c\x92\xf2$\\\nE%\xec\x93\xeb`\xb0\x06\xaf\xd6\xf4\x16\xc9I&\x12\xb5\xb9A2\x81\xf7\xe4$\\z*9\xea\xab\x98\xe1g\xae\xe0\xd2\x7f\xf7a\x9a\xae\xf7Bj%)\xbf \xb1O\x94\xe7\xf1\x0e+\x93%\xa7\xea]RR\xcf\xf5\xbc\xa0 l\x1f\xb9\x8d\xaet\xdd\xc1\xc8\x08\xa4\xb1\x081A\x959\xd9\x97o\x88\xb8\xaf?/R\x87[5\xd4\x89]r\x19F\x9c\xbbj}\x9b\xe0\x04\x0el\xca\n\xf8r0\xb0j\xce\xbb\xbe\xfc\xffP\xa3\xa87\xa7\xbe<\xe6AX\x8e\xb3\xff\x1a:\x87\xf1\x84|\xf2\x83\xa4d\xffT\x81$ \xca|A\xbe\x11f+\xe0\xd4\x94\x8d\xfbf\xe4\x92\x07\x1d\xba\xf49>\xa5$\xa3,\xc9\x0c\xabz\xc7\x14\x08}\xd3\x9aH6\xd5\xb1K\xbcj\x9f\xf7\xed\xef\xd6~f\x0b\xda&\xd5\xb8\x8b\x92\xfb\"\x8f\x81\x953Tz\"n\xceZ\x1fQ\xa7\xac\xb5\xb5x\\]r+vW\xbb\xd8\n\x1d\x93`1yb]\x8bM\x811\xd2\xcd_Fp\x89\xd1\xf30j\x15\xcb\xe8,V)M\x96aA\xb7\xa7y\xb1\xd8\x8aC\x1a:u\xb6\xbcX\x1c\xb1\x14\xcc\xcapE\x12\xe1q\xb8\xfdy\xeb\xf6\xf6v\x0b\x8b\xac\x8a\x14\xaf\xd7I\xecT~\xda\x8d\x04\xb96U\x06h\x14\n*\x15\xc0\x189\x1aI\x894\xf2\xe5\x9d\x00Z\x1d\xe3\x87\xf5\xe1\xde \x83&dy/\xb0c\xc7\x8a\x9c}\xc3\xa1\xd2\xc6*\xd1\xaa(HF\xdf\x0bR\x84\xd3e'\xcdS\x19A\xc5\xfd^\xbfrY\x99y\x04~1\xf4\xd2k\xd6\xc1\xce\xff\x893#\x14\xe1{\xc5\xff\xe5%\xfe\xe7\x1e\xba\xd8\xaf|\x89D\x0f\xfb9'a,\xf6B4g?\xd0\xcb\xa6\xa3E\xd2\x88z\xc5\xde\x15Wf;\xd7\x00Z\xf7\x9fS\x1e%M\xa5VX\xd1P\x08\xcb/HJ\"\x9a\x17\x9e\x1b\xf5\x05\x82\xac\xb0\"\xee\x8b\xaaBM\x9d\x9fs\x04\x9cHz\x94\x86V\x85\x1e\x15\x9d7Q\xd3d\x8f\xd2\x0c\xab\x8e\xa3\x0cG\xf7\xfc\xef\xeb\x04\xe1\xa35\xc8k\x14\xcdf9\xdd\"qB\xf3\xc2\xd6\x01A\x9e>J\xf3\x7f-\xf3\xac\xa2>8\x18\xe9\xb3\xacm\x86%\x87$\x8dp~\x94\xce\x14\xa2\xbe\x9e\x0e\xf9Vz\xbe\x97\\R\xdbC\xecSh\xccB\xf7\x11\xc5Qr\x8b\xce\x91\xcd\xca\x80\x89\xc3\xe8\x03~M\xa8\xa6d\xdc\x8f1\xce\x05\x8f\xca\x8a \"~b\x19\x9c\x151)H\xccg%X\x90bF\x18\xc3S\xd3\xa9#\xdd\x16K[\xbbx\x08\xb3\xf4mK\xd9\xdd\xd3\xa5\xdf\x00<\xcf\xd7\x97\xbeZ\x87\xf6\xaa7\xde\xe7*\xff7\xa8c\xd3\x96\xbaC\xb3\xc6\xb5\x88#)\xb9K\xf34\xcc\xfd\xee\x0b\x16\xd1\x98n\x0f\x8a0+8\xd8\xfe\x8a\xbb\x86\xf1Wi\xaf#\xc8\xcai\xde\x9e*m\xae\x16|d\x1aG\xfd\x98\xddP\xab6\xac\\\x83\xb57\xb7\xbb\x1e\xd8\xae\xda\xaa\xa8\xb3u,h\xc3\x9f \x84%\xe5\x0c\xe6\x0e,\x06v`{\xbd\xefNv\xb6^_}\xe7}\x0c\xda\xbf\xb6\x93\x80|&\x11#p\xb8\x0b\xb7]\xd3lH\xe9\x87\xb9+\xf1\xc0\xae\x10I\xeb2\x02\xaag\x12\xee\xdaB\x18s\xe3\xb3\xbe\xc6\xf1\x0e\x9a\x07\x0e \xca\xe4\xef\x04~\x80]\xaf\xb9\xfb\x05\x17\xdbf)%\x03\xd7\x93\xad\xb9\xd6\"\n\x1d\xec\x83K\xda!\xe9H\x87\xca]\xdd\xd5\x8d\xaad\xd5Uk\x18bc\x1bV\x83\x1c\x10F\xae\\\xb3\xb6\xf0d0\x15\x97K\xd9\xf0\x9a\xb7\x8f\\W\x1f\xb6\x9a\xbd\x9a\xf2\x0bB\xe7y\xdc\xab\x9f_-\xb7U\xa6.\x9f\x84U\xc6\x18\xfb-\xc6\xd8\x9bU\x07\x80\xc3\x95\xe5J\xdat/\x8f\x87\xf0\xa8\xb9\xda\xfanh\xbc\xdf\xe8r\xc3oCR\xbc\xe1\x0bB=\x974\xd9\xb8\xbe\xe3\xe5Z\x97f>vGd\xd5}\x1d\xb9\x95\xc8\xab\x12\xb2~[O$\xd5)\xeak \x9e\x0c\xc8\xca,\xf8}\xd4n(U\x1b\x89\xfc\x968\xba\x97\xd0\xab]\xbfY)=d\xd3\xeav}\xa0W\xbe\xd031\x82xS\xb0!\x08g[\x15v\xb5\"\xd4 F\x99D\xeb\xa6\xdcoI\xe2\x1fe\x96\xd5.\xda\x85\xa1P\xcd\xb6r3\xf0(\xed\xcb\xfa\x8cK+\xee#\x1e\xa5!V\x97\x99I\xac.@\x1e\xa5\x1dQ\xdd\x006\xa5\xfbf\xc6\xdc\x99;\x1fn|\xb8\xee\xbe\xceku\xac\x11\xd8\xdd\xaa\xc5Qe\xe7\xd7\x8c\xaeSu\xd0\xe9\x9b\x02\xf9\xa0\xd7\xa3\xae\x0c2\xd3FS\x18\xda\xaf\xb5\x06j\x07o\x13:\x97\xaa6\xe5\x80\x91\x19+\xd1p>'Z\xe4\xd0\xab\xf4\xa1#W\x1f\x03b\x17|\x8ekP\x11\xd5\x9f\xaf5\xe3S\x1f\x04\xcd\xdeU\xe9\x8f\xdc;\x83E\xb2\xfe|m\x85\xb6o\xe7\xb0~\xb6\xfbpnt\xca\x80|\xe4c$%\xb4\xbd\xa5\xa1h\xae\x97#\xeeC\x1fe\x8b\xb3\xbaz\x0f\xc7\xc6\xfbg\xd9\x87\xfa\x8a\xb6\xf7\x94\x92S\x82~\x81*\xc4\\]\x02q\xe5\x01W\xd9G\x83\xee\xcf\xa05\x1a\xe5\xc6\xcc\xa0?\xd1\x89\xc6\x9a\x83\xbc\xd0\xd8\x08\xe5z\xda<\xed\xb7>\x8c\xfd\xc1\x13A\x06\xdf{\x81r\xc6+`N\xab\xf3YEl|5\xaflJ\xb7\xf2d\x0e\"\xf4\xab\xcfH\xf8]\xf4\xcc'\xf7\xa2\x10\x02\xe9\xf0\xd0\x07QZ\xfdD\x06\xce\xb2@=\xc6A1\x8c\xbf\xd32\\G\xe8\xd9\x03\xfb\x08C\xfb \xf6\xed\xff\xd5\xea2\xf4^\xcbZuC\xb9w\x94w\x8c\x1d\xfb\x11TPn\xc8\x9fz6\xee!'\xb1\x0d\x8a\x18\x83\x10F\x95i\x10\x9c\xe2x\x0e\xf3l\x9a\xccJ\xb6<\xf6\x85\xc5\xcb,\x06\xb8\x17yAM>\xd0\xe5\xc3\xfd\x10\xd7{\x92\xe7\xef\x04\xf5\x0b\x94O\xe4\x05\xfd\xf1n\xd8\x9a(e\xcd\xee\x00\xba\x02\xd4\xea\x8f\x9c\x0f\xa3\xdej!t\x1fV\xd8?R\x94\xca\x1cL\nK\x14}P\xe9\xeb}\x90]\xe8\xb0\x11\xff\xea5)\xa6>\x0f\x0c\xf2\x9e\xdd\xd8g\xe9\x83\xbc\xee\xb3\xbe\x1a\x93\xbc'^z\x02{8t\x8aU\xb8\x05^\xd0\xf7\x0eV\xc1\xdb\xdd[\xbb>\x96F\xdc\xd9[\xd6\x01z\xa0\x8a\x0e\xca\x11$\xf7F\x04\x86\x9d\xd9\xdc\x82\xbe\xa6\x07e><\x86\xca\x9ck\x192\xaf\xf0~\x17\x1a\x9f\xf0LST\xb4\x1e\xa93\xbc\xbe>&\xa1\xf1~\x80]ik\x90=J\x8f\xb4j\xef\xd5\xb13\x8e#\x9b\xban\xf7\xe0O\x0e\x95\x1b_\x96U\xb2\xc9&\xa8P\xb4\xeb\xee\xd1\xc2\xa7\xc1-\x98\xb4\xfa\xee\xd1\xd0\xc1\xe0\x86\x0c:\x85U;\x1d\x0dh\xc6)M\xbd\x10\xa3\xfa\xe2\x90\xdeK\x04v\xef\xbbw\xa3JW\xf3|5\xa3\x92\xfcA\x8a \x03\x9b\xb4\xcaW\x8a\x81\x9c\xb0\x14E\xe7\xb89\xb2\x06\x9d,\x15\x9c2y\xc9\xe2\xd8\xc6\x08\xe2\xa4\x1eX\x0b\xa6\xcd\xc3r\xce\xc5\xac\xf8\xf30\x8f\x89q@\xa0\xe3y\xc3\xa5\x9aXq\x93\x11\xca\x03Y\x85JQI\xed\xb6Y\xf7NMi\xb7o^\xb7N,\xf3\x9ec\x99\x1ee^\x1d\xda-\xc2y\xe9)+\xab\x16\xc2@\x13\xa9c\x7f8\x98^'\xb2\xa3\x0c\xab\xe6\x0cf7\xf4{\x1f\xe3.\xbe\xffh\xfe\x19\xdb\xf7\x1b\x01\xa5\xb0\x80\xc7P\x90\xb0\xae\xca\x99\x98\x93\xdc0\x95&\xe5\xf0oD\x83\xbc\xd0\xd5c\xa1\xb8\x07T\x97\xd4\x9ah]\xba\xa1\x0d\x04\xd7y1\xa5N\xa4<\xac\x0c\xb8\x02p/Z\xd7\xc1\x8e}\xd0\xf7\x17\xf2i\xcd\x0e'\xfa>W\xf5\x93k\x1d\xff\x07Hj$\xdanH|\x8d:r\x06\x17<\xdc\xcc\xb1V\x1a\xc5\xf8\xcf\xce\xb6\x08K9\xd9Q\x02\x12\xaa\x11\xa2do\xe0\xd2\xde\x9f\xff\x81*\xa9lRz\x95R\x0d\xb3p\xf2\xaf\xd155\\\xa3\xa0\x99\xb2\xf4\xf1\xd2\xb9\xbd\x1f\x88\xd0\x85\xccU(y^y\x9d\xf7A\xb9T7\xe5#\xaa\xe5\xb5;\xbd\x97@x\xff\x83A\xac\x1a\xaa\xa0x\xa7\xd4\\\x8a\xdf\xb5\x7f\xb11\x1e7\xe5p\x95\x05M\x1f\nl\xcc\x8fP\xaa\x0b\x16!\x8d\xe6\xee\xf6\xffq'\xe1\xd6\xdf\xaf\xd8\x9f\x9d\xad\xd7\x9b\x1f\xb7\x82\xab\xef\xbc\xd1\xb6E\x0b\x97\xbb\xa0HJ\x19\x90\x80\xb1\xed\x1c\x92\xb3V\xd0\xc1\xd6)\xcb/P$\x8a\x14\x92\xef\xd6G\xe7Z\xac\x0f\x1f\x9e\xc33\xe6\x9ar^\xc3\xf6\xc1`h\xd47%\xa2s\x13gN\xe9\x12\xd54)]\x96\x8a\xb7\xac\xe3\xaa$\xf7\x90U\xb7\xdce\xf4\xd4)\x0d\xe9\xdd,zd\x8a\xc7\xa1S\xecF\x19-\x8d\x07\xdb\xe6Rp/z\xdf,M\x96\x03\x02\xcfJqj\xe5\xfa\xd1\xa0\x0b\x93\xa9\xeb\xd8\xc65\x7fm\xf7\xc4\x8c\xd6\xf61\xde#W\xf3> \x97\xda\xb6\xf9\xaf\xb7\x8d#\x8a5\x9c\xf8\xddp8\x98\xcf\xd4\xd7\x92p3\xf3\xa6W\xc2\x92\xd0\xd6+\xe7\xc7\xb9E\x12J\x80\xc7\x8b%\xbdC\xfb\x9f\x8az\xc6\xaf\x12N\xf1\x93\xb4\xa8\x92\x89\x9a\x16\xe0a\x18\xcd\xd5:M\x86S\x82O7\x7f\xc2\xb4\x0bi\x9c\xb5\x0c\x8b\x92\\\xe6\x95U\xd5\xc5\xf8\xf2\xfa\xe2\xf0\xa7\xf1I\xc3\x9c\xfa||q\xf6\xee\xe7\xf1\xd1\xf5\xc5\x87\x1f/\xcf\xc7\xc6oj\xda\xd9\xfb\xf1\xf9\xc1\xe5\xf1\xd9\xe9\xf5\xc9\xf8\xf2\xe0\xfa\xe7\x83w\x1fx\x99\xc3w\xe3\x83s\xf6~\x8c\xf9\xde\x1f\x9c\x1f\x9c\\(_\xce\xc7\xff\xbf\x0f\xe3\x8b\xcbF\xca\xc5\xfb\xb3\xd3\x0b^\xfc\xdd\xd9\x9f\x1aYXoO>\\\x1e\\\x8e\x8fZ\xe9\xedw\xa5\"S\x0fD\xdf\xc7'\xef/\x7f\xe5\xe9\xd7\xc7\xa7\x87\xef>\\\x1c\x9f\x9d\xaa\x19\xf0\x93\x9a\xf0\x9f\x17\xcd\x0c\x1f\xce\xdf\xa9\xaf\x17\xef\xc7\x876\x034\xd8\x83\x1b7s\x9f~\xaf\x93\x9d\xb9\xf8\xf2\xea\xb9\xfe%\x91e\x9e\xe9_B\xf1\xe5\xf9S\xfd\xcbJ\x96\xd9i\x15*\xc5\xa7g\xcf^\xe9\x9f\xd2\xea\xd3k\xfdS$\x9b\xfa\xdek\xd0\x8f\x1c&/\xfaT?%\xb6z\xc7\xe8\x8e\x82,\xd30\"\xee\xf6G\xba=\xf3\xc1\x01\xd0\xf1\x96\xcdkc\xad/\xd6Fsh/q\xdd>\x1f+3g\x8d\xaej\x9e\x1c\xcd\xbd\xf5-\xb6\xf9\xa7\x1d]\x18\xe0\x1c\xe0\x03j\xe9?\xb8\xf5\xdbok\x9d\xa1\x85\xde\xc5\xec\xe9\xc2\xf8\xa1]\xe0\x06\xf6\x88\x13\xcd\xbc\xb8! bO_>w\xf4\xc5\xcc\xa9q\x95?\x8b\x86\x9e8P,\xf7?x\xb4\x9f\x86\x0b2\x02K\xf0\xa8%?\n\xac*\x85I\xf9\x97E\xaa[\xfd\x00\x0crL\x80\xf3\xd6)\x89\xb4\x1b\x9b\xfe\x8b\xa6\x0f\x87o\x9d\x1c1\xb9\xddSS\xdcsjR\x12\x16?\xeb\xa7\xed\x83A\xfb\xf8A\xf3q\"\x14D\xdbj\x1c\x03\x96U\x9av\xa1\x91a\x1f)\xdb\xd3\xfd\xbf>\xa8\xfb}\xbb\xc1\xb2\x9c\x9f\xc8\xdd\x08tS\xbd\x87\xcc\x80\xb4\x1d\xfb\x1f:\x03\x1a\x1f{\xcf\x19`\xf0\xab\x10\x96\xdf2\xf6\xcb\xc7\x1d\xbbT{\xbe\x87\x0f\x10eD\x92r\xfe\x96\x01\x9d\xfc\xb7\x18PI\xe8}\xd9[\xdb\x80\x8e\xee= \xce\x9ew \\6^\x0bx\xca\xf1\x1ad\xc3\xb6\xf16\x89\xd9iEd\xbe4\xd9\xa5e\xaen\xd1\x19W\x05Z\xf4\xe5\\|\xda}\xd9\xfa\xb4\x96Ti\x9b\xcc]\x88O/_\xb4\xc8\xdcY\xf5\xa9Ej\xdfI\xc3R\x13\x93{c=\x14dh\x1e\xd51\x04\xe9v\x0ca%w\x1a\xf3xm`\x1e\xd0\x14Q\xfa\x9fA;\xc8\xe6\x18n\xdb\xfcG\xa3\xc8\xaaH\xb5\x12c\x03\x07\xd3(\xc2\x95\xa8\x1be>\x9b\xd8\xa0F!<\xd2\xb5R\x83\xb8\xabF-\x84\xf1\xc9\xbc\xae\xfa\xfaF\xab\xf5\xd0\xc2\xc7\xf1\x8a$\xf3l\xec\xd0'\x13O\xc8\xcb\x95\x84^\xcb\x8bt\xad\xd4\x81\x81\xb3T\x0b!\n\xd3\xca\x9cup\xa9uYq\xe9m\xa9\xe3\xbd\x81\xf3\xe5e\xd3|f)ca\xa0y1D\xb9\xb6Q\x9e\x18\x99\xf1fAS\x8b\xc7\x9d\xec\xbdZ\xbesi\xfe:@\x8a\xd0\x00\x95J\xccz\xbd 4\x14\x87j\xb3\xceS\x8b\xb4\xa2QOm\xde\xda({\xde#\x051\xd6q]r\x81\x8bV\xd7Q\x05\x0c\x95\x80\xc5a\xcb/e\xaa\x8d\xcc\xef\x86\xaa\xb8\xb9;>\xba\xa8\x16R\xc5J\xdc\xa6\x9bH\xab\\zS\xe8\xd3K\xfeV\x19:\xad9\xb8\xc5\xe7\x01\xe6,\xcdGLQe\x937J\x96\x8c\xdc\x99\x10)\x8a\xce\xea\xf8\x95\x9c027g \x85{R\x83\x1c\xd4\x1a\x16\x10\xc3@\xc0\x97/\x90\xb8\x18\xb0\n\xc1\xb6C\x87\xabD\x0bqF\xda\xb1i-\xda$\x1d{\xbez\"h\x91\\\xaa\xa0\x0c\xa7\xe4]\x1e\xc6\xc6h]j4=\xf3T\xf2\xa5a\xf4t\x9e\x8aX\xfb\xe8\xf1-\x0f2r\xcbx\xf6qq\x9fN\x9b\xa7\x8f=)Y\x93t\x042\xa0\x935\xdf\x82\x94e8c\xc4GP\x90\xb0\xcc;\xcc\xe4\xd2$\xc3|\x8b\xb0\xf8\xc4OQ\xf6+`\xc9\xa8\xdb[\xbfmb\xe4 .:\xb3\xcck{\xf2l[\x05\x03\x1d)\xde6\xf7\xc0Uba\x85\xb0\x0f\xce*\xe3\"et\xf2\xc1\xb6VTo\xad\xd0\xe3&\xe0M\xd1\x88\x1bz\xec\xd0\x1fH#}0\xc4\x95\xfb[\xa5\xbf\xa5Hf; a0\xecM\xab\x86d\xe5\x85\xa8\x7f\x7fBus6`\x8f\x82t\x83\xde\xbbO\xa1\xf2\xff2\xed\x00\x8a\x15\xecA\x18L \x8d\xe6\xf6L%f\x12S\xd5\x01`\x98\xed\xe0\xc2\xc0\xe3\xc8'\xaaD\xb2\xb8\xfa)\xec\xc3?\xbe\xc2\x08R{\x91\xa9\xbcT\x14:\xc2f\xb5\xa0\x0fh, 7\xe6mXd\xdc\x91\x84\x98\xa2\xc6:7\xc2tB\x99d\x11\x81\xf5\xb3`w'\xd8\x810\x8b\xe16IS\xb8!P\x90E\xbe&1$\x19\xac\x9f\x07;\xc1\xce\x1bX\x95\x04,r~\x11\xd0s\xc3\xf1|\x0ep\xb6XW\x0c4\x18i>\xedRv\x8e10\xd9\"\x8fI*/ZN\xc2\xa8\xe8\x88*5\xc7\x12\xd5\xcdVO\xee5\xe6\x16C9\xce()\"\xb2\xa4y\x87R\xf5B\x94\xe0\x04\x8cR\xc42\xcaz\x95\xeb8?y\xe5i\xc1\xad\x9dG\xf0\xfb\xf6\xca%x\x1e\xac\x8a\xd4\xaa\xfe\xc5&\x8fq\x15\x11\x83\x88wIFNW\x8b\x1bR\xbc\xcd\x0b\xb4\xcf\xdb\xb7}h\x86\xdd0\x84\xc2\x90\xcf]\xd5\xcd\x0bZ\xd8\\w\xcb\x1b\xb7\x0eT\x8f[\xca\xe8cH>\xac\x8dN3\xe4\x9b\xb0$Gyd\xe5\x1dA\xb8\x00mB\xc8\x08b{\xf6&x\x8c\xa0c\xd3\xb7ac\x04\xeb\xae\xec-\xc0\x18\xc1\xc2\x98\xfd\xab\x17\xd09\xc9\x06\xe8WA\xe3\x8e\x95M\x98\xbd\x03\xec\xe1\xf6\xad\xfc\x1a\xd6\xae*\x9eL\xc1Mz \x0c\xa8$\x02\x0e\xba\xf3\xcf\xcc$\x06\x082\xa3y\xfb\x9f\xe1\x1do\xa6(\xd6t\x0d\x11T\xe5\xbc\x81\xda\x9a\xeac%K\x08?\xcf\xd9\xa4LWi*\xb6\xc8\xcc\xbd\xf3\x95\x14i\x15\xc0\xd2\x96\xdc\xc8\xb5\x91\xbd~ \xfe\x9a'\x99\xeb\x04\x8eZ\x04)\x15FU\xcb\xd8\x93$\xa0\xdcE\x9b\x9c7\x1f\xb5s\x84\x8b iu\xccr\x9a\xef\x93\x89\x0f\x8e kz\xa3?\xcb\xa7\x11\xcf\xaa#\x10\xa8\xfa\x08\xb9! Dc\xbd\x85\x86X\x01\xda\xa1\x8e= #\x13/qV\xc6E\xf1#j\x99\xe4\xdf`9XhWfvS\xaaVr\xcb\xfc`r\xa5\x1dGo\x85>\xda\xa2&\xc6\xd8kZ\xbf\x96\x15Y\xcdh\xc7\nh\x81X\x03\xdfQ5b\xa8\x0f!\x0f\x80\xe2C\xec\xc3\xdc\x87\xb5\x0f\x0b\x1f*k\xdf[\x1f\xc6V\x85\xa1\xba\xed\xdbb\xd0\x86\xc1p\x0bo\xdexP\xde&\x9c\xca\x0f\x96\x05F\xfc\xe2\xc1\xd0\xbb6Z\x14\x96\x04vF\xddk;\xe5\xe7\xd7\xdf\x82\xf2\xae\xa4d1d\xe3\x12\x19\x8c\xf1y7\xdc\xb0\xe7\xa6 a;\x92\x9a\xfa\xd8\xc1\x05lH\xc2\x89\xc9\x8d\x00\x1e\xe9\x05`\x04q\x9e\xfd\x9e\xc2<\\\x13\x08\x81\x0f\x06h.\x0c`\x08\xe4\x99\x0f\xe1M^\xd0$\x9b\x05\xdcaQxS\xac\x96h\xe2\xc1\xda\xb0\x05\x07\x069\x93\xcf\xfbg2\xd3yQ\xc1\xc6\x92\xa2\xa8)d\xc1\xb1N3\x1fi\xe2\xbc\xa2\xf2\xf8P8\xef\x97#E\xaaS\x9e\xa1\xa4\xfc\xade\xee9\x04\x94\xd6\"R\xe8`\xacK\x0dw\xf3\xb6\x87U\x1eb\xe8\xd4\x14\x91\xf0\x12\x91\xf0\xa2\x1fh\xe1\x1bp\xb0\xe9\xf9\x16\xbclz\x86\xe0j\xd3S)\x14\x8au{\xeaw\x99\x1b\x9a\x1el\xf9\xe9\x83[\x0e9\x91K2\xea\x0b\xb6\xbc \xe5*\xa5'\xe1\xd2\x17\xbc5\x83\xf2_\x12:?\xe4\x0e=%\xcaV\xa0\xed\xa5\x0f\x89\x9b\xe2\xf9z\xbfi\x93O\xc5tL9\x1f6\x8c\x96\xd2\x1f\x13[r\xf7\xb0\xaat\x96\xe5\xe6a\xd5\x98\xd8\x19\x83\xa2\xd2\x90\xc7\xc8\xea\xdc\xde\xbb\xaa>bQ\x7f\x10\xbc^>\x18\xbc\"\x05\xbc\x96\x88x9\x9f\xc4\x8f\xba\x88sWP\x04a\x9a\xe2 R\xba\x1e\xf7f\x86\x8c\xcc\x10n\xc9\xf6\x0c\xe4\xa2lO\x9b\xbbZ\"w\xb5\xd4\xcc\x16\\.\xa1\xb8?\xfbdz*l`b\xa0\xe6\xee\xfa\x7f\x1b\x03ez\x1e\xc2T\x99\x9e{3Z\xa6\xa7\x9f\xf92=\xa8Pm`\xba\x16\xd2\xbd\xf6\xac>WW\x885\xe3\xf6\x87\xb4\xfa\xd0\xa2\x83\x1e:\xbd\x15f\xef\x94\x10u=\x96\xa3`\x04\xf6\x08\xf0\xb6\xe7A\x88h\xf7\xfb\xfba\",\xe4\x90,v\xeeW\x0e\xd4\xcdX\xd2|i\xf1\x91cz\xba\xa9g\xf9|\xc5\xe8\xf1&G\xb6\xc6\xdc6\xc9\xa4\xfa\xb4\xae\xf0z|)\xa8O5Xs\xd0\xcf\xde:\xba\x07\xfd\x95Q\xc3\xab\x8an\x13\xb8d\x00bW \xd6\x9d\x9a\x9c\x0d\xbb\x93\xab\xcac\xcfR\x9a\xd0\x074\xff\xcf\x8b!D\x84\x15\x9c\xa7\x8a\xc8X\xd4\xd6=\xc0\xae\xf5\xe1\x90\xdb\xc3~\x8e\x95\x83\x92{-\xafxz\x1f\xaf\x8dx0\x10I&>\xed\x06\x07\xe4\xf1\xfaz\xf4\xba\xbbG5c\xf1\x1aO\x87\x1d\xec!^V\xba\xbb\xbb\x9e\xafK\xfe\x02j\xbb{\x80\x8aL\xed\xa1Sc\xb3\xa1\x83\xcb\xc6>\xae \xd3\xdef\x9e\xd9\x9b\x19\x8a\x11\x86\xec\xfe6\xd0\xab\xbb\xda\x87\x89\xb1\xd4\x841j\xbb\xaf\xafZ\x1f\xaf\xda\x0e2\xe0\xd9\xf7\x0d\x9d{\xab\xb5\xc77^\xec\xffM\xc6\xc1\xf4+\xa8\x03\x0cC\xfaV\xf7LX\xbd}m\xdb\x02\xdc\xd3\x11x\x8fJ\xdcy{\xff~\x8b\x8e\x9fT\xd8l\xaf\x99m\x80\xfe\x10\xdb\x1c+o\xfdO\x1a\xdd\xc4\xe2\xc0F\x0cO\xc5\x83\xf7\x1bi\xcb0\xe9[\xd6\xee\xf0A\xa3\xab\xb4\xa5\xcdC\xe4.\xc1\xef\xbd\x84]\xf6X\xdf\xae'\x7f\xf1\xcf\x18\xe9#\x98\x13\xf0\xb058\xea\x9f\x85\xe9\xc2\xf0iS\xb7v\xd3\xbc\xed\xc1j\xae\x03&\xa5_=\xd7\xfc\xb9`'\xb6\xc9\xcd\x81e\xc9>uAK\xc3\xb8\xef\xbf\xe7h\xffv\xaf\xd1\x1e\xf4\x8c\xb6e\xe0\xf8\xbfa\xd0g]\x83n\x18y\xf6\x1e\x9c\x1d\xe34\x8c\x857\xff\xbe\xab\xf9\x96\xd9io\x17\x86*\xe5\xd9Tn\x8aa*{\xf9P\x95\xbd\x95&\xeb6\xe7\x12\xf1\x06\xc3\xf2YOu)\x12\x96\x0c<\x18\xca3\xe7\xe1r$qW`\xcc1\xc5\x1c\x95\x8e\xa8\x05m\xc2\x1e\xacl\x9c\xc1\xfd\xb4S\xac\x9a)\xe6\xec3\xbc0\xe0\xacD\x9b|M\xa6\xe0\xce\xe0\xc9\x13\x98)\xa1\xc7\xf4w)y\xd2\x93\x85{\xd2~\xf1\x93\xa4iY\x0d\x1bBK\x86{\xc7\xaa\xcf\x89\xf6\x1e3\x98\xa5w\xc6\x0b\xcf;\x1d\x07\xb9\x93\xd4\x87\xe8\x8am\x84\x8c\xad6\xd2X^\x17\x9bJ\xd4)\xd9k\xbe~\xf9b\x8d\x1f\x00\xca\xd6P\xcbLx\xc3\x1d\x1e\x0c\xdd\x0dt\x0e\x8e\xa1\xfcv\x84\x8b\xa52\xf9;w\xda\xe1\x9a\xea\x82=p\x0c\xbe\x97\xc0\xcc#\xa0H\x07\x83\xc8}\xa6\x1f\xaa\xc8Lq-\xfa\x91\xcaH\x01\xcd/\xd0\x12\x96\xb1\xcf\x02<*\x00?\x8eQ\xc8\xa7\xbe\xefi\xdfG\xbcP\xca\xfeD\xa2\xf3\xcd\xfcY\x90/\x8fcw\xc6\xefc<\xd4)\xe5d\x96k]\x136\xa97\xb0\x07)l\x823r`\x13\"\xf3\\2v\xb6\xe0\xb1>\xca\xa0D\x1c@\xe2\x0bLro\x90ko%w\xe8_]\x8bjX\xbe\x9f\xc3\" oR\xd2\xa5\n\x05\x18,\x9d\xe5\x1eU=\xe9\x96\x08\xb0\xa5,\x97aDFpc\xcd\xf8\xb5_\xbap\xfb\x08=\xedo\xbf{\xce\xabv+\xf7>\x15t]{\x12\x91\xec\xc35\x8c\xe0\xd6G5^=R\x1d\x0e\xa2\x9d\xec\"\xa0\xf0\"\xad\xa8u\xa2L+\x9d\x17B\x87!\xdfm\x7f\xe7\xd8\x17y\xac\xb6\xfac\x1es\x9c\xc4\x8b\x9bK\xb1\xc1\xdd\x05I\xf9\x9f\x17g\xa7\\0\xed\xb9cT\x8cW\xab\x81=`\x19\xb86\xbc;\xf6F0f\xfba\x8csi\xc8<\x16\x93\x0c\xa3\xf6\xa7\xf6\x86n\xa5\xb0\xa1|\x163\xaf\xb8\x01\xf9\x07z\xe6m\x8f\xe33\xee\xc4\x9bU\x92J2\xcc\xfd\xec\xf9P(\xc4\xa8\xab\x1c\x90\xf5A\x08\x9f\x0d\xb5\x11\xc3\x11\xa6R\x19\xbd\xfeq\xd7\x0d!\xe0\x84\xea*:\xea\x93\x9bG\x99u\xab0\x16m\xc2\xd32\xc0\xbc\xe1\x9bD>_U\xf8k\x0e\xd3p\x97\xcc\xc6u\x01{p\x14R\x12d\xf9mG\xa8\x9bLRg.\xd1\xd5\x05\xad\xd3F\x83x\xc5Qj\xa3\x0d\xd8\x82\x8bj\x0dyO-c4\xa8O}\xf5\x84\xa0\xad\xbfyuJ{\x1a\xea8c\xb9\xf6F\xd7}\x0b)\n.^\x98\xab~m\xccg\x9ei@\x8d$\x0b\xafI\xdan{\xf4aK\xf5\x04\x83\xa3\xaf\x1d\xab\xa3\xaf\x9d\xa6\xa3\xaf\x9d+T\xe37P\xef\x15%\xda\xfe\x96uR\xa0\x89\xd8\x07\xb9b\x9e\xc3}\xfeP\x0c1\xc9\xcb9Wf\x1fi\xdd\xa4\x9bT\xd2$\xc14\xebR\x9a\x0f+}\xd5\x01\xf4;\xe9\xe7\x07\xca\xea\xf6\xdf\x16\xa5\xce\xed>\x0c\xb9\xfa\x80\xe6\x1d\x8b_K\xd8\xa9\xfc\xb0\x1d_W8x\xednl\x8a\xf7\xc9\xed\x03\xcb\xce\x08D\xa6\xa3\xca\x9c\x9d\xd1J\xdb\x9f\x17\xe9v\x12P\x86\xac\xa6\x96N\xccq\x00\x15\x81\xd8\xe8\xbe\x0f\xb1\xfd\xec\x16\x80\xb0\xd2\xb8C\xd4},\x9a\xb85\xb1md\xa1\xfcm\xd1\xbf\xe7\x8a\xdf\x96\xa5\x96\xd8\xa2\xdfb\xd8V^\x92\xc4V\xednS,\xdc\xa9\xa5\xab\xc2\xb4\xd9b\x9fa\x0c\x97\xbb4\xa0\x1c+\xce\xc1_=\xce\xa8H@>/\xf3\x02\xfd>7\xe7\xbb\xb2\xf1\xcd\xdc\x97\xcf\x9ej\x90P\xdb\x087\xbdO\x19\x9b\xb4\xb57@,\x89\x91]\\n\x00\x12f\x11\xbaUD\nKA\x80\xe8\x11\xb4\x80$\x03\xe2\x01\xde\xea\x03\x9b,T\xb4p\xd1\x1f\xeb\x08\x92,\xca\x8b\x82D\x14\x92l\x9ds\x07x\x1b\x16W\x8e\xe4~3hv\xe7U\xd9(\xb9\xaf\x9f+\xcdT\xc3\x0f\xa6CD\"\x19\xb9\x1d\x805Y\x8f\xda{\x8d\xd15\xc1\xb2\xc8\x17 \x8a4YUdX\x9096\xe9\xca\xfcRm\xbe\xb3\xf6,;?\x861\xbc\x17mEyV\xd2b\xc50\xb3M\x97\x11O \x1f\x0f\x1b\x83\xbc\xd6\xf3y\xe7\xc5\x05*\xcb\x84\xbe\xe5D\"\xa3~1M\x0b.\xf3U\xb5;\x1c\xb4t\xf5\"}\xbfcZ\xa4\x01bB\xd4\xb0\xe3GW\x921\xd8D~\x9aLrv\x16\xe3\xbf=\xa0\xec\xdf\x08\nVG\xee\xe3\xeb\xbf\x04\xf2^>\xdf\xb5\x8c\xaax\x8c\xea_\xbd\xb0\xd4\xce@M\xd7g\"\x9f\x97i\x12%t\x04\x13\xd6\xb1\xe7\x8c\xe0u_>\xff^\xfc\x7f\xe1\xa9\xdeP\x1f\xde\xbb\x0eJR\x99\x97\x17\xbb\x167\x93\xec\x9b\x8e\xea@\xd0=\x9a\xc7\xca`s\xeb\xea\xbb\x91\xb7\xef~\xdc\xfe\xb8\xed\xed\xbb\x93\x8f\x17\x1fK\x0c\xc9\xd9.\x1eb\xf1\xc9\xc1\xd6\xff\x1f+\xe0\xffw\xb6^on\x05W\xdf\x8dX\x05\xdb\xedB\x8c|\xb1\\\xad:\xff\x86\x9e#\xc3r\xae\x87\xf3\xae\xb3\xec\xb3,\x7f[\x91\xe2\xce\x9eg[\xfatDG\xca\xd6l\x7fd\xd9\xc2\x15\x92x\xbb\xb6\\\xa7\xe1)\xeb\x13\x8fH.\xaf\x86w;\nl\x8f\xdc\x8f\xf1\xa6\xf7\xef\xdb\x18\xc8\xbch\x14\xebo\x04{\xac5\xd4*c\xa8\xa6}\xce\xc9\x87M\xe7\x08v\xcd-\xe3D\x8e`\xb7\xf5Q\xf5# \xaa\x9b\x8d\xd4\x8e\xaf3\xaepo\xb3\x94C\x015\xfa\x83s+\xc3m\x1a\xa4\xe2\xd4\xe2\xc2@\x8bp\xd5\xb9I\xf3\x9b\x91#d\x9e\xcb\"\xa7y\x94\xa7\x1e\x87{v\x96\xb8\xab\x8c\x94Q\xb8\x94\xbc\x13\x9bF\xcf7WH\xd2\x92\xe8\x8e\xea\xf6t\xf7\xd8\xf2A<\x981\x1cX\xb7E\xb0b\x1fJO\xeaz\x14\x93\xcc \x91\xac\x1bR-\x99\xad\xda\xd6uS\x84\xa1\xdb$\x03\x94\x90\xba\xacr6_\x93LG\xaf\xf2Ql\x14\x8a\xa0L\xc3rNP\xfc\xec\xd6o\x8c\xb0\xa5\x9cQ\x9f\x17dj\x8a\xfa\xd3J\x91\xbc\xe9\xef\x9a\xd9\xccp\x11u{;\xad\x02\xfaZ\x89g\xf3\xa4\xc8\xb5\x1e\x01\xe5\x0e\x9f\xd9\xbf\x80\xe6\xef\xf2[R\x1c\x86%A)\x8fc\xb1v\x17\xa3\x1f\xc1\xc6\x06\x9d<\xb5\xec\xbe\x82\x94\x94U\xff\xac\xbd\xd1\xf4+V\xf3\xd0\xa7\xb6C\x14*J\x8f\x1d\xf1*\xb17\xad\xbdPW0E\xcd\x82\x176\x83\xdc\xec\xa9\x94\x1a\xf7sn\xc1\xb0\x12\xc1\x91-\xdc\xcc\x02j\x97\xdd\xe6\x1c3\x96c\x9eX\xb8\x8a;\xd8\x83\x9dv\x7f\x10L+\x88f\x84\xd3\x02\xad\xf5\xe5f\xaaR\xb8=\x8e\x8f\xcb\xcf\x1d@s\"B \xfe\xb3Q\xf50\xabJ\xe4\\\xcc\xe7\xf1\x82)RH\xec\x9c\xdap\xd9q\x13\xb9\x84{.\xf6\xbc\n\x0f\xe0\x85H(A\xdd\x87Y\x03\xea\xe5\xef/_ \xe1\x1eu\x95\x8cU\x15\xc8\xf8\xc9\x17DL\xea\x9b\xe3\xf8\\l\xc1h7\xea7ku\xd7\x93\xa7l\x83N\xb6\xdd\xe0;o\xbbq\xf4xo\xe0\x0e~\x80\xb5\x10s\xbc\x81\xbb\xcdM\x0f\x91\xb5\xcbx\xd8\xf5\xe4\xee\xca\x9b\xec\\\xf9\xdc\x12{\xb2{\xe5C\xc9f\xa5\x84}\x98M\xe6\xb8\xef\x19|\xb7]j\xb2\x1c\xff\x8f\x1b\xa3,@\xfaX.=~\xc9\xe1dh\xfe\xa2f_\xb2>\xee\x83++\x15\xa0\xb3#tT\x95\xa4\x1861\xb7\x87A\x87\xb5\xfczf,\xcfs\xc6(\xfc\x15\xbb\x9c\xf7C\x14\x8eq\\z1\xdek\xcf\xf3\xe5@\xf1\x9f\\\xa5\xe5\xe4\xd9\x15\xae\x96Hd+\xb0\x9c<\xbfR\xebe\xff\x9a\xa8\xc0\xb0}8`\xcd\x02<\xe9\x90\x14\x12\xbf=\x84+\x15 @\xf1c?\xab\x8e\x91 \x9a\x87\xc5\x01uw\xc4\xdc\xea\xdfy\xef8GQ\x9f=\xa2\xd5*\xd3\x00?\x11\xa0\x92\xdd\x18\xe9\x0c9\x14g\xdb\xf1\x82r\x99&\xd4\xe5?\xe5\x0cn\xedz\xd2a5Q2x\xbep\"\xc1A\x8e\x1b\xbce\x93\x02\xb6\x18\xfd\xc1\xb7\xd2.7s\xdby\x03\xc5\xd6\xd6\x1b\x0f#{\xe0M\xd9\xa4\xb8B\xcf\x19\xac\xba\x08#\x13\xec\"~\x0d\x9a\x19\xdcf\x0e\x1fB\x06\xd6#\xee\xb7\xc3\xdd\xa9\x03Z\xb8 \xf7j\xe0C\xab\xc4\xd6V\xb7\x94\x19\xd7&\x0bVY9O\xa6\xd4u\x1c\xcf\xc7~\xb2\x89\xceq\xa9\x82\xea\xed\xcb\x17\xc8\xb8\x0e\x1cf\xcb\x84\xce\xfc\xb6)\xa2\x8a\xb2*\xbe\xbabl\xde\xd8\xb7\xbc\xa0*f\xe0\xfa\xa93\x19a\x97\xff\xe0\x85yf~{\xc8\xdeV%)\xc4b\xb36\xca\xf26/b\xfc\xcc\xbe2B\x13\xa7d\x89\xdf\xd9\xab\\\xb5Q\xab\xfcr\xb2S\x81}\xa3.\x86#\x04\x02d_\xf2\"\x99%\x19oP\xc1\x86\xa2\xbb\x88l\x93\x94\x8c*\x98\x95y\xf6\xd5\x97Mp\xb6\xb7\x1d\xd8\x94\xc5F\xe00|\x8dM3b\x01\xab\xaf/3\xb53Q}\x9b\xf2J\x85)B\x1b\xc4KBG\xbd\xac\xa7|\xf0\xe0\x13'\x94\x19R*\xeb\xaf\xae\x0bh\xae2\xca9\x86n\xa5\xd1\xdeX\x17\xd2\xdd\x84\x8b\xd4\xaa<\xa8x\xa0\x85d\x82\x17\xc9=\xe6_C4{9\xd7\xd0c\xee*Zc0K}H\x14p\xdd\x17~1\x12 \xb2I\x05\xb2\xd5\x95/\x0f(o\xc8Q\x8d\xc3\xe92\xd7\x84\xa1#\xa98\x9a\xa1\xa3I\xf8\x96\xe2\x13\xbd\xb9'\xba\xcbS\xd9$\xcb\x1e?\xc64#O7\xb4c\xdb\xa3\x8f\xf1\xe6\xbfos\x1a\x9a\xb2Yv\x85\xffxe\x0b'\x12!\xd0`\x99/\xdd\xaa\xc3bSS\x81\x96F\x8e\xa7\xcc\xbf\xfc\xa8\x14\x7f\x9c\xc9\x97 \xd17F\x95\x08\xa2\xcd\xf3\x94\xf5\xa9\xa6\xa56z\xa2N\x0f\xeb\x95\xa4\x8d\xfa\x94\xbcQ\x0c\xd0o\xf4=\xc8\xd6\x13\x0dW\xd9\xc4V\xad\x0b'3\xfbx\xe0\x8f\xc0\xf97\xcb\xb5\xb6\xfaHhP(\x82\x0da\x16\x1e\xb2M\x05&\xe5V\xf5\xf9*X\xc2\xc7@\x15R\x8c=\x08~\x8d\x99\xccF\x1f\x15\x05Rr\x02\xa1\x84\x1f`U\x91\xaf%;\xe7\xed\xf3\xcd\xca10ZM\xca\x0e\x0d\x9dT\xd2q\xc9$\x9d\xec^\xb1\x1e\x8a_\x1a5w\x8fnK\xa2\xa1>\x11\x93\xc6\x89\x98\x18O\xc4D=\x11\x13\xc3\x89\x98\xe8'b\"O\xc4\xa4\xa1\xde\xd3\x0e\xeei\xba\x9f\x14\x05F=\xb2o@\xd7vMNI\xf1\xa5\x8f\x04\x89\xf0\x8c\x84\xf5%\xd3\xbb\x0e\xcd\x1b\xca\xe5\xd1v>\x0f@\xc6\xc9\x95\xe3\xb7\xd0e\xd8%1s\x85\xdc\x04\x85<\x1c\xb7\x18\xa9\x88B\x07\x81\xb8;\xfa\xc4\xe3\xb4n\"\x1d)\xd0\xcb>\x9f\xf2\x91\x1d\xf9U\x97\xfc\x15\x9d\xc4 \xcc\xcd=%\x8d\x11\x7f\x15\xb9T}\xe7\xc7H\xfd\x05I\x7f\x96\xfeGG\xfe\xcc\xf8J\xf3\\\x92\x10\xcf\x87\x8d4X\xa6\xabY\x92\x95\x93\xec\xaa\x0biR\xb9\x86\xe35\xc9h)\xeby)\xeaQ\xab\xe9>5\xe4)G\x03\xb2\x167\xab\x1d\x1e\xad\x14D\x9fd\x10z\xb0r\xc3Iy\x85\xeb\\z\xb2\x17\xaf\x1c\x94;\x19<_\x82\x11\x17\xab\xd7\xb4\xed\x95\\\xd9h\xfe\x94w\xf94\\\x90\xa3\xa4\\\x864\x9a\x0b\xedd\xb6\x19\xcen\xb3\xcaP\x99{\xc9b]{\xed\xa0*BGY!8m\xceA\xad\x8f\xb1\x9c\x87%\x89\xcf\xc9,))\xd7q`uhS\xc6A\xcd\xb0|\xd5\xfc%l\xfe\xacR]\xaeS\xab\x0d\"\xf1<(\xdd|\x92\\\x89\xe9\xe8\xd9\xe9P\xa3?=\xae\xed\xefLy6HPh\xc3B\xfcR\xba\xed\x0f\xa2\x07>c\xd3;\x17\xaf\xb4/\x9e^'\xbfB/\x19\xf5\xc1\x17kwg\xa7\x02\xe7\x8e\xccH\x06\xb7s\x1c\x91%\xc9b\x92EI\x95M\x01\xf1Iv\x15\xc4J\x0ee\x10\xf2\x97\xa4K\x9a\xfd\x16\xfb\xaam\x95e\x83\xa7\xb6\xda\x91e,\xfd\x19\xd5!\xb5s/\xf3\xb2LnR\xd2\x82M\xe1\x01\xa0 \xa1\x19;\x9e\x10y\xbc\xc7\x11a\x8c\xc9>\"#\xafVf\x97\x9d\x81u0\xba\x8a\x83\xe7\x92&~0\xb0\x95\x0bu\xd6\xbf\xa7\x1b\xe5\x8fw\\)e\xc0M?\n\xa5,\xb2f.\x0e\xc3k\x11\xeb\x0e#m4\xd1G\xa7\xe6\xe2N\xc5\x8e!\x133\xeeI\x10\xadH\xb9\x93\x8b\xafr.\x9f\n\x9c\xc4\xf3\xe0\xad8\x17\x80\x0dD\x9fH\xa1\xf6L\xf4\x8c\x88 \xe6\xc0\xf66/p\xd2\x87\xce3 \xe2\x06T\xb7\xc7\x8flUk\x13V\x17\x16\xf6\x1d\xdc.\x84\xb2*\xb3[g]\x1b\xc3\x86\x8e\xbbNqn83\x08\x8f\xcb\xa7\x02)\xd4\xac1`^\xf9\xe0\xc9\xaeC@\xd1 V\xa0\x80\x96}\x96\xb2Iq\xd5\x01uP\x1f:b\xc2\xdbQ\x85\xe4\xd3u\xfe\xcaG\x92\xcd\xab4\xed\x82\xaa\xeb\x82\x94\xa4\xb1}Gv5Nh\x11[\xb9\xb8\xe4A\x8fg\xad\x8d\xc3\xe5\xe1\xe2\xb2\x94\x91]\xed\xe1Wd\x8e\xe4'\x8c\x97O\x12\x88\xedg~\x1f\x12\xa1\x1e\x0f\x9e\xdb\xde\xd7\xa2{\xd4\x88\x13$Yk]\xd6\x8evC\xbc>\xf6\xa0\xd0\xdb\x0d\xd5v\x8bI\xd8\xbc\x804j\xd9\xaa\xf4;_\xcf\x87S\xe9\xdc\xa3\xa2\x99VG/\xd0\xee\xd3\xdd\xa7\n\xdd+Hw\xf7\xb51\xfe\xc6\xaaC\xdd\xad\xa6\xb9P4\xfc\xe5\x0b8\xab\xecS\x96\xdff[\xb8\x8e\x9a\xf0\x85\x04\x11w\xe9p\x19\x163B\xf1biF\xe8i\x1e\x93\xb7E\xbe8\x16\xf7\xa8n\x81\x97\x84\xfb\x10\x06I\xb6\xce?\x91?\xad\xc2\"&\xf1a\x98\xa67a\xf4 }Cp\x7f\x99\xd8-\x82W\x14\xe6\xbcU\x16\xdf\xd0zc\xef4\xa9\x8a\xb6\xdeER\x8e\xb38)\xe7}\xf8X\xecK\x87\xe6\xcb\x93|U\x92\x0fK)\x94b\xd3C\xf3\xe5e\xbe\x8a\xe6\xe3,6%\x1f\xb2\xf1\xa7\xe2K\xd7\xb6N\xca\x93|M\x1e\xd0\x1dV\xcc\xd4\xb2\x92\xde\xdd\xee\x05\x0d\x0b\xfa\x80\x86\x8f\xf2\xdb\xcc\xd40\xd67\xa0e\xa1\x82{\x94\x14$\xa2\x129\xf4u\xa2>\x1c\xaf\xe5\xe9\xf8.))\xc9\x88M\x0b;k\xe6\x960i\xc0\x03M?T\x94\xd3\x10\x8cXx\xe6\x18\xa1\x8dA\xb4\x19\xde3\xcf\x18\x18\x18\x14\xfc\xc4\nS\x97\xd83J\x95<#\x90\xfb\xc6 0}\xac\xc6[},\x06-\n/M\xca\xe36\x95j\xb9\x16]WV\x80C\x97\xa6\x18\xbc4\xec\x9c\xd5\x9d0w\xe8\x01I4\xb6\xf3\x06r\xf8\xa1v\xd5\xfc\xe4 l\x90 )\x19b\x0fg\\[\x9e\xe6\xcb%\x89]\xef\x0d\xe4\x9b\x9b^\x8d\x1d'\xf9\x95\x0fE[U\x12\xa4\xc2\x10^X7\x90\xa9!\xe3\x03W\xe9!K\xc4Fr@/\x8b\xd5`J\xbe_\xbay\xff\xed\x06\xf7\xdar`\\[\xdaI\xbc)\x84!\xbf\x19\x87\x1f\x1a7\x7f\x1d+\\lnv;\x18B\x8azR\\\xb1Ue\xe4\x9f\xa2\xfd3)\xdajG\xa0\xdc\x15\xa0\x87\xe0'O\xd8\xa6\xe6\xc1\xb3e\xc1n!\xa9\xbe\xd8Xe\x97\xfaU\xe7\xde\xee\x847\xda\x05U\xf3\xb0\xac!\xaa\x0f\x80\x14\xf1E\xbb\xbd\xaeV0\x9e7\xef4C\x98\x0cq\x0el\xab\x08\x0ce\xf5@/\xed\xd6t\xd4|\x9f\xd6Zh\xbd\xbb\xb5\xa4<`k\x81\x0e#{\x91\xa5\xe4\x18\x82\xba\x14\xcf\xdb3\x9ew\xf9-Zw,\x16y\xf6\x90\xe6,U\x0cj\xfb}\xc8\xce\xa1{\xce$6\xd9,\xd93\x8f\xb4\x08\xd7\xa4(\xc9\xe5m\xfe\x9e1\x8c\xc3\x14\x11\xaa\xe6\xf4\xe2U\xa1!m\x8e3J\x8aw$\\\x1bZE\xd7\xe6FYu\xab\xed\xba\x1a\xadp'\xfc\xa0\\&\xc93\x93g\x0f\xfe\xf10_,\xf3\x8c\x11\x03\x05\xe9]\x00\x90'l\x1b\xbf\xb4Q7\xaf\x9fU{\xc9\xc7\x10\xa6C\xea\xcf\xcd\xf5\xff\xce\xfcfa\x8f8\xc6x8{\x042 U\x95\\\xf1:\xb9\x0dd\xcc\xb1\xaah\xcb\xa4\xa33j\x14kUQ\xa1\xc2\xc9\xee6\x86\x02\xe5^M\xe3FL\xccN\xcb\xca\xac\x9b}je/\x08\x1a\xca\x1c\x86\xab\xd9\x9c\n\xd7\xe1\x9d\xb2\x02v\x8aY\xcdr\xd6\xc2&\xd4\x12\x14\x86\xdb\xe4\x14\xf5Y\xf4\xadp\x91<\x1c.\xcc\x164&n\x97S7\x94\x13\xd7_\xbe\x00 \xca\"\x1a\xa7dA2|\xbfM\xb28\xbf}\xa3O+\xdb\xef4@\x9b\xaer\x99gq\x92\xcd>\x94D\x96\x93\xfaG\xd6\x1c\x9e\x0f\xcfxh\x9c \xcbc\x82F\xfd\xfb<\x8c\x1c\xc9\xf0\xe0i\xe8(|\xab5\x8e\xd0-t\x9f\xaa\x163y\x10\x85\xd9\x87\x92\x1c\x9d\x9dT\xe0\x1b\xe7\x11\x1a\xef\x06\xc9b\xc9{\xca/'\x9f<\xb1}\n\xe6a\xf9\x96\x84tUH\x7f'\x1b{\xd6z\x94\xcc\xae\xe3\xf8\xa8\x1d\xdc\x98\xd9\xed\xef\xbekB\xcdwp8'\xd1\xa7\x92Af\x98q\x81?$%\x94\xab%[_\x1e\xc0\x89\xce \x08.IP\xc7\xe82=['E\x9ea7\xb4J\xf56N\xcf.\xc7#\xb8\x9c'%\x8f\x0f\x95\xe5\x14n\xf3\xe2\x13\x08\xa3\xbd\xf4\x0e\xa9\xce,\xcf\xb6f\x8c\xc6I\"\xde\x13\xd6\x8fh\x0ea \xbf\xf1H\xca\xbf\xf9z\xd5\xbf\xa1\xb8\xee7\x1f~K\xf30f\xff\xd1\x08\xfc7\x1f\xa3Q\xfd\xc6\x1ds\xfc\xd6\xd7\xc1\x1f\xf3\xa2\xc8oK\x98\x16\xf9\x02N\xf2\x98\x14Y\xf2\xf7\xa2\xaf\xd4\x1f\xd1^\x14\xfe\xc1\xb5\x0f\xbe\xd6\xd7%\x17\xab\xe94\xf9\x0c(D\x84L\x98\xaf\xcf\x02p\xa24\x89>9z\xbdUE\xfb7y\x9e\x920chq\x89K\x8e\xab\xc3\x16\x07\xd7@$\xa2\x9c\xb7\xb1J\xed\x1a\xa51AU#c\\dE\xedenW\x90\xb036\x0b\xd3\xd6\x874\x89HV\x92z\x9a\xe0Y\xb0\x13\xec,\x0b\x02\xee\xe1\xaa\xa4\xf9\x02~\\%i\xec\xc1\x1789\xbe\xd4\xcao7\xde}\xbb-\x9e\x8eL\xd0~@\xddS_\xbe\xf0[\x82\x0d\xd7 \xe3\x18\xe7Z\xd2\xc8\x0e\x83Z\xb9GjVA\xbfY\x91\x1c\xb5\x93g\x0el\x9a\xfc`\xa1PP\xad\xecM\xbbOF\x92e-\xae\xa0\xab\x8d\x1a\x15$\xa4\x12=\xb9N\x9c\xacM\xea\x1daP\x12z@i\x91\xdc\xac(q3\x1f\x84\xb3\xe47\x8e\xd0\xfe7\xaa\xc2\x84\x93\xcc&2\x05\x85\x9d@Mb\xae\xbdr;'\x95\xd8\x0c\xa4~\xf2\x10\xac\xc2\xef\xe6\x03^\xde\x07\xe7Y\xb0\x83\xaa\xd6\xc9\xa3!\xd3\xd6\xd1}\x90\xd2\x118aJ\xffL\xee\xf4\x90\xbayF\x8b<\x1d\x81\x13\xd1\"m\x7f?!4\x1c\xa1\xdb\x82\xb0\xfd\xf1b\x9eLY\xcd\xa8W\xcd>\xd7C\xb0\xd0:\xb6\x03\x0e\x0dW\xb3\x90&k\x82\xf3\xd3\x86\x12\xf43v\x92\xc7\xc94!\xc5\x05\x0di}\x8d\xd4\xfe\xd4bO%\xa0\x16\xad\x1b\x83\x8aS\xc43dc\x83\xaa\x90PC\xc1\xb0\xf3\xbau\xcd\xf2\x08K\x99\xb9\xaf^\x1b\xd4_2\xf7e+=\xe1j1\xbb\xdcv\xf4\xd9k\xfc\xf7t\xf7\x95\x1e\xfd\x9a\x8b\xe4w\x9f\xeb\xe5W\x98\xfe\xec{\xb3X\xbe4b\x151d\x93h\x92S\x18\x93\xdd+!\\\xa7\xe8\xb5\xf8\"\xb9I\x93l\x86\x1eu\xa6IQ\xd2\xc3y\x92\xc6\x86)_\x8b\xab\xf6\xc4\xedc\xafH\x90d%)\xe8\x8fd\x9a\x17\xc2\xb1D]\xa1q0\x91\xad\xaeB\xd4\xc58\x0dQ_\x8b?3\xe94XM\xb7Z3\xb3ob\xdcl(07+\xeaTaK\xec\x840\x8fI\xa4\xcc\xb8]\xb8\x95\xba\xdc\xee\xba\xe0\xd7\xf7\xdc\x82\xbdCk4\xafh_\xf5\xd1\x88g\x1c\x1cZ$Q\xb4\xdaA\x91s:l2\x97\xd6\x03l\x88\x1c\xae\xba\xcf\x9d\xec\x1a\xee\xdfb\xac\x1b?\xef\\\xf1;v\x12\xf0`\x9b\x08\x89-\x0eK\x0355+\xed\x1eFl\x83\x89\x8e\xe5\xab\xc4\xef\xddK\x87|P\xcfR5\xfbZ\x0cc\xfc\xe6\x0861\xa3\x15\x8b|U\xa6w\xe7d\x99\x86\x11a$?\xe3\xe3N\xc2\xe2\xd3j\xd9DS\xeb\xb6k\x8c\x9e\xf2-\xef \x05\xcfuD\xd2d\x91P\x12_\x92\xcf\x03\x0d<\xe4\x84\x11\x8571K~\xf9\xbda\xe7\xb4\xe6\"\x1c\xe8>\x17\x9e\xa7n\xe1\xeb\x14\x08\xeb\x19\x8a\xf6\x18\xe4\xe4x=\x02\xfb\xe0\xae\xf0\xde\xcf\xf3!v\xf9u(E\xd5||\xeb\x95]-\x8b<\"e\xf9\x01=\x14\x97\x03\xc4e\x0d\xeb\xae\x9d7\x90)\"\xe67\x90\xd9u\xab+\xf0\xb2\xea\xabHS\x98\x02oXm\xf5@\xa5]\x7f|z1>\xbf\xbc>98\xff\xf3\x87\xf7=j\xf6\x88u\x0b\xe9\xd8\xc7\xe7GJ\x11\x84SJ\n6\xa7}\xd1\x0d\x06\xd9\x05\x9c\x9c\xfd<\xbe\x1e\xff\xe5\xf8\xe2\xf2\xf8\xf4O=\x1d\x9a\xf2\x0eL\x85\xb8\xf6\x9f\xd4\xa3\x8b\xf1\xc0\xf9 \x1b\xf3\xf3\x18M_\x8e\xffry}xvz9>\xbd\xeci|\xf5\xe8\x8d\x9f\x8fq-N\xcf\x8e\xc6=m/\x9b\xeb0T\xc9\xe9\x9e\xf2\x9a5\xa6>\x88\x1a\xb3{\x01\x9a\xd3\x05#\x9f\xe7\x94.G\xdb\xdb\xb7\xb7\xb7\xc1\xed\xb3 /f\xdb\xbb\xaf_\xbf\xde\xfe\xcc>kd\xf3\"\xa4s{\x99W\xdb'!\x9d\xe3\x9f\x93wZ\xc9r=3\x16{\xba\xb3\xb3\xb3]\xaeg\n\x01\xfe8C\xed%u\xd5\xe8\xe9\xb5\x0d\xf6\xc9\xc5\xc1r\xc9\x10(\xfe@S\xde\x0f\x19\x0f~\x1f\x85\xe9[y>*\x94P%\x826\xaa\xbfvV\xd3\x1f\xd6N^L\xa9\xad\xb4aI\x17\xac\x8e\x1e\xdb\xdb\x8cQ\x8d=s_\xed\xbc4\xd0\xf1\x99\xfb\xf4\xc5+\xcf\xcd\xdc\x97\xdf{AR\xfe\x1c\xa6I\\\xc9\xe6\x1a\xb9CE\x19\xdee4\x7f{\x12nV\x94\xe6\x99\xd9\xaf_4'\xd1\xa7\x9b\xfc\xb3\xf9k\xb2\xc0\xf8\xfe\xa6O\xf3$\x8e\x89\xa5\xd2\"\x8c\x93\xdc\xf2\x89\xa0\xed\xa6\xe9S\xb9\xbaY$t\xd4\xd2L\xb6i \xe9\xeb\x8d\xe2\xee\x0dv\xc8\xe3\xa0H\xfc.\xc9>10\xac?`x\x04\x99\\\xb8\xce\xab\x97N\xaf\xae\xb2\xde\xcc\n\x95X]\xadR\xa9\x9f\xc8\x93\xf2\xec\x10\xe5mR\xc7\xfc\xd5\xab\x9ev\x0c\xdePZ\xed\x88Q\xf5\xb4\xf4\xba\xd1\x92\xfc\xc5\xc002\x9a\xd2\x8a\x88\x11Ch-P\x18f2\xa1\xa8\x93\x19N\xb8.\xd6\x15\x17N\xcb\xee\xf0\xb7\x82\x84\xf1Y\x96\xde\xf1\xb78)\xc3\x9b\x94\xc4\x8c\xbcb\xfd\x1f\xa1\xcb\n\xe1 \xeb\xd7|%\xc3\x83\xc6\x10\xc2o\xd8\xad\xdfX\xd2\x12h\x0e!\xa3y\x160MH\x1a\xc3mB\xe7\xf9\x8aB\x98\xc1o\xb2\xc1\xdf`\x1efqJ\x8a@\x91\x93\x16$\x8bI\x01!\xb0\x8el\xe5\xac'XC\x00\xc7\\\x90\xc7\xeb+\xe7\xf9*\x8d\xe1\x86\xc0bEY\x171\xd4\xfeo\xc22\x0e\xbd\xf7\xfd\x16\xc0\x19\x9d\x93\xe26)\x19\x99@(\x90\x84\xbd\xab\x1d\xc8\x0b\xf8M\x8e\xf8\xb7\xc0d2n\xd9~$~\xf8\xfc?\xe2\x94\x8b\xbe\xfc\xb7\x98\xf4C\xd1\x97\x7f\xd2\xb4\xcb\xd2#H\x026\xf3\xbf\xeb\xc8?\xb5\xda\x13-\xdb\x9b\x16u\xc8m|\n\xbf\xcb\x99\x11\x94q\xdb\xfc\xbf\xd3J\xb0\xe5\x08\xe95\x9b31\xa9\xdc\xff\"\xe4S\xf8\x8d[~m\x82\xf3[\xd0\x0ckh\x94]::m\x00\xa2Oq\x0b) \x18\xbc/\xf2%\x1aE\x0c\x83\xcc\xa62td\x03^6\xbe\xc8\xa4\n-%\x16\xd1\xa4\xb8b\xc74\xe7\x9a\x1c\x06\x88\x8e/\xee\xeb\xf2\x0e\xcb\xa9D\xf5\x89\x83\xe0\xcd%\xdb\x89\x0c\xfb\xc7\xba5\xedV\xdb\x99T\x99\xafP\xd5\xdeN\xde.u!\x81|zI\xd4&d\xcd\x08\xfdY\xc7\xbe\xa6.V\x9a5\xf5\xf1\xb5\x8f68(\xbc\xa8\x12\xff_\xf6\xfew\xbdm\x1cY\x18\xc4\xbf\xf7U\x94\xf9;\xa7\x0f9\xa6\x15\xc9v\x9cD\x89\xe3\xe3v\xdc\xd3\x997\x89sbg\xfa\x9d\x9f\xc6G\x0f-A\x16'\x12\xa9CRv<\x93\x9c\xeb\xd8o{\x0d{\x01\xfb\xec%\xed^\xc2>(\x00$\x08\x14H\xcaq\xf7\xf4\xec;\xfc\x90X\x04\x88?\x85B\xa1\xaaP\x7f\xc4_\"X\xf5\x8d\x15\xc4\xdf\xee\xfb\xc4\xa6=\x8d\xbd\xeb\xa7\xea\x11\xaa\x8d\x84\xd9a\xf5Z\x1f\x81|\xdd4\x06i)vVn\xc6V\xc1\xb7+$T\x94Ql\xd7/\xe4\xfd\xa9\x1c^m|M\xb3q\xb4\"\xab\xc8vJ\xf2{\xa4\xfd\x10\xce.*\xf8\x1aFI\x10?\x1c;\xd5!\xb1\x08\xe8\xfd\x12|\xa7\xe4\x18\xb7\xcc2\xfb\xe2\x1f*\xf5\x8c\xa9\xc4\xb1]\x88\xa0\xd2f\xa0\xda)cI\xa9\xd5\xa0k7Z\x95T\x15N\xab\xcb\xd26|UO\xe5\x98\xb4/b*\x90\xb3@\x92L\x96\xc8h\x18\xc4\\@\x06\x8f#\x8a\xc4M\xb6\xc1\xc1\xaa\xa7\x95<\xd0X\xf0\x0dv\x06\n\x0bd\xae\xd6\xca%\xabN\x83\xdd\xa6)\x0e\xb9\x8f\x95\x8a2q\x9f\x8e\xcc\x87\x16\x0du\x00\x8f\xb0\x0e\xfeQ\xf0}\x82\xdc*\xda\x1f\xa2\xa0Xa>9\xe5FB\x80N-\xa2\xa4\xba\x9a\xec\xdbwFZl\xb1\x9a\xcf{i\x16#\xec\xc2\xedZE\xadV\xd1z\xff)\xa1\xfb\x89\xdd!%\xb2q\xdc\xa8cjW\x84\x87\x90\xb4\x10\x15\xe1\x04\xc4\x0fg\xcf\x9aK\x08*\x00#\xcd\x8a\xf89\x06Q\xb2\x071\x03\x7f+\xab\xdc\xb3G\x91H\x99\xb9\x95\xfal\xc4\x7f\xa1\xaa\x1e\xffp\xdf\xf8\x96\xd06\xd6\xef^\xc8\xd9y\xc1\x15\x9c\xeb\x0b\xb75\x10\x7f\x132\xa6^\xb7\xd0\xea\x12\x17\x8b\x18\x81'\xab\xaca\x85\xbd\x94\xbd\xceU\xd0I\xd7=\xb7B\x1e\x12b\xf5\x10\x91\x88wUl5\xfe\xe6\xa8^%\xb6\xaa\xc40\x84Z\xfcG\xbc\x8dV\xe9\x9a\xd1T\x07\xff\xc4\x97\x9f\xd8\x9d|\xf7\x89\xdd=\xc4Z\xd17\xcb\"Tf\x1bAV\xac/M\xaa\xbdCo\x08\xdea\xdf\x11y\xd1\x1bb\xf1\xae\x9d\xba\x9bH\xf8\xa3\x80\xfd/\x9c9\xf6=4J\x08\x14u\xf7\x1f\x8d\x0e\x87\x97\x8f\xae\xc3\x0e\xe7\x87\xbaZ\x1e1\"\x96c\xa3._\xc5\x0f\xfdV\xa0\xf4q\xda.\xa0\x1c\xee\xf2\xe2\xe1&@\x11\xe0\xf0U\x8466\xea\xa3\xb7)\x87\x95\xf8\x8dQ1Y/__ D\xf4w\x05\x83S\xbd\x18\x04\x81\x06M\xff\xb0\xff\xe5p7xx\x80V\xf8J\xd3\x8a\x07 \xce\xec\xe2\x8a\xf6\x0fP\x916\x18\xec\x9a\xd7\xe6\xf2z]\xde\xab\xef\xef\x05\x9d=\xda\"BN\xec\xb1\xe4\xbf\xd6l\xcd\x04\xdfP\x8f\xccm\xb7@h\xbbJ\xdb I\x94\x1a\xcf?\xfd\x14+\xe8C\x0csQ\xa9\xb8\xe4\x82\x8ah/z*B!\x11\x014\xb3\x8e@\x92\x04fF\x8a\x8e\xf2\xf7\x0b\xd8\xed\xe3\x95\xdb6x\xe0\xf3&\x86\xc0q5\x93a\xaeB\xf0\x02^\x16x\xa0g\xffs\x87\x16p\x9d\x1fh\xeb\xed\x1a^\xa2\x0e}\xad\x03\xbd\x01\xdb\xed?\xce\xdf\xa6\xeb\xa4h\x97\xa0\xd4R\xd1\xfd\x83n\x86RH3\x94\xdeXH\xfclZ\xdaT\xd77\x89!I d\xaa\xecr\xbb\x08\xed\x8b2\xd9k\xe9\xbc\x88U\xed\xe1\xa9mc\xaf-\x94\x9cEu\x84\xd2\xeeb\xbd\xf1\x8a\xa1\x95\xa9\xea,\x87#\xea\xad\x08\xbf\x88\"\x13\xf5\xcd!\x8c\x8a\xcb\x10\"\xebB\xbb\x11 \xaf\xa51^\x07\x11\x93\x91\x03%\xdej\x03\xa5\xbe)\x07\xda\xecM \x07\xfac\x9aM$-\xe8\x8aM\xf4bH\xe3\xder@Z\xc3(\x98\xf0\x11\x15fJ\x0crH\xf2\xe6\x1e-\xaa\xba!T3\x9aH#\xf4rd\xd8\xf0\x7f\xf0\x9e\x14\xac\xaa2\xbdo9l=\xc1\x82\xa6\xd4\x97\xbf|\x02\x99\x85\xf5_\xd5\x90\x17\x84\x9b\xa2a\xd2\x80\x86\xc9e \xf0\xb0\x0b0\xcfYA\x01\xd2\x05\xc5\xc4 E1[?\xa1\xc0\xf8\xe5\x0b\xd0\x05\x870\xba\x0c\x02\x85\xb0|\xd4\xa6{\"=jy\xe3\xe4\xd8=\x0e,\xa86\x8327\xc7h,\xac7\x96\xc9\x0e\xf9\xf9\xdb\xbe1\xcc\xe5\xec\x0093\xd6\x99.\xf7I]\xc0\xee\xae\x87#\xe7\x07\xea\x86l\xc77x\xc9'\xfe`/\xa0\xb8\x90\xbd}\x9a\x0b\xe1<\x86\xee\xaf\xa9\x8f#\xbd\xff8\xba\xdd\xed\xdeT\xc1\xdeP\x928I\xa7\x8c\x16j&\xf3(\xe3\xa5h/\xccP\x1b\xc0yI_(\xbaU)^M\x0d\x84?ARZ\x06\x0e\xf6\xf8\xde\x92\xc8P\xc0\xcbC\xd8\xdbE\xd5\xc1^\xa9[(`\x08\x1bJ\x9a\x15h\xad<\x15\xd2\xc5`\xf7)y\xdd\xbao\xde\xc2b\x98\xc7\x91`\xa1${si\xb0\xe3k8\x04u\x0d]\xe9V\xeaurB\xfbR\xaf\x81q\x0e\xcb \x80\xf5\xb2 \x86,\xa8+k\xec\xdb\x89\x85\x90\xeae\xde\xc3M\x97[\x18a\xf3\xf7\x18\xaa\x8b\x05|\xdfD\x8dJ\x0fdf,\xf2\x84\xe24\xa15\xe9\xd3\x0c\xe7\xa4\xd4Ex\xb5\x8c8\xa8$\xd2yO\x1a\xf7\xaam~X\x0f\xfe\x9e\xe8w\x01\xc2\x8eK\xf4\x94\x04\xbc\xea\xec\xbe\x08\xb5\xfb\xecI a\x8c>\x83j5\xcff!4\x82\xbe\x93\xbc\xa2\xf7\xe3\xcaJ\xd3\xb2eA&1\xd2a\xe7\xb3\xde\xd5]\xc1\xde\x08u\x12\xcd\xf8b6\x9a\"\xe8\xe5\xac\xf0\xc5\x0f\x0cb\xdd\xe6\xdec\x8e^\x05\x87\xc4\xf5\x9b\xc7yo*\xe6\xa5R \x0e!\xe2EJmm\x16\xba\xc1\xa0\x00\xaam\xfc\x01n\xf2G\xfa\xc6\xff\xef\xbe\xd8\xf8\xfa\xbeG\x94\xc4\xa8\x0b\xc5\xfc\x03\x9b\xac\xb3<\xc6$\x86\xebP\xf8r\xf1\xf7mWB\xb8w\x8d\x8dk\xedX\xc5\x95H\xaabs\xab\x9e\xa7|(\x84s\xb8f\x1c%\xe84z\xda\xce\xd2u\x82~\xbcY\x9a\x16\x8e\x9c\x98\xe6~\xc6I\xce\xa3\xfc\xa3BhmB\xc0\xec`\xf3q\x15\xc4\xb0\x99{\x16&B$fuq\x8e\x01\xcb{ \x94\xfe&u\xec\xc5c\x90\xfc\x1a\x14\xf4}\xe4\xc0\x02\x02\xd9\xd4\xf3\x95\xcc\\V^\x94\xb9\xc6\xa7\xae\xdbb\xdf\xb4u\xd5\x9f\x08\x15\xaar\xd4\xeeyjg|\xd4qV\xe9(\xb9l\x99\x18\xb9\xdb\xaa\xe4w_\xeb\xb2~3\xef^\xa2E\xa1\x19(;\"yH\xc3\x12\x91\x92\xbdL\xf9\xa9l\x9cD\x96,\xe1K\x89\xb9 \x12\xf9\x13\x0fl.\x89\xc8\xdfe.fyh\xf0wE\xc6\x98\xe5\xd8EN\x14\xcd\xb5Y]B\xf0q\xdbh{\xa3\xe8!w)l\xb1:\xc6\xd0\xa8d \xcb7Q\x08\xef\x83\xc7\xa6\xbeD\x08\xefOLY_\xba8\x0e\x1e\x93.\x8e\xcf\x06OZ%\xac\x86k\x04\xce\x06Q\x97\xc0\xbc\x81]G\x19\x17\xf2\xf7\x1ce\\\xc8\xdfw\x94q\xf1\xfe\xc0Q\xb6\x82Cx\x0c\xea:\x9cH\xa2<\x05y\xfd\xbd&iV9\xd9\"\xe4\xb4w\xde\xc8D\xdf\x84\xb0\x0c1\xd1\x1bnKL\xea\x96\xfa\xd7A\x08W\x98kv\x8d\xd9\xe4\xf6\x82\x10\xc6\xfcL\xf1\xef*6\xfbV\x90\x99S\xf4\x05?\x82)\xefo\xccE\xa4\\\xfd\xeaW\x06R\xcfa\x0c/\xe1\xf69\xdc\xba\xb6*\xdf\xa6\xfe\nc_p\xa2,\xa3\xe4/\xe1\x10\xae\xfc\x1b8\x84\xbb\xd1\xede\x08\xb7!\xf0\xc1\x99Z>\xb3\xa1$\x80\xd3\xd1-\xe7\xf5\x974\x11\xe1OI\xc5\x96A\xb7TA\xa0\x18\x9a\xbdf\xbf\x17\xd0\xcfjw\xff\xa0\x9a{\xdc\xb9\xb9\x9b\x0e\xad\x1dtn\xed\xb6Ck\xbb\xed\xad\x9d\ny\xe5\xc6\xbd$\xda\x891i\xe4\x7f\x14\n\xc3\x11\x17K\x86\x80\xd9\xf5&p\x04\x13\x18\xc2i\xad\xba\xe9\xeax/\xcd\xa9\x14\xdb\xc4a^j$\x8a\x10\xbc*\xd3\xb7g\xfa^H\xd3z\x9d\x0d\xe3T\x13Sv\xa5Y\xfcW\x95\xde\x1d\xcf\xdf\xf2\xe5\xf1\x04\xed\xca\xa4-\xda\x0fQ\x1eO\x8e\xd7\xc5\x9c%E\\\xa6bpV\xff1\xcd\x96\xef\xa3,Z\xe6F\xad\xd5jA~\xfe\xbeJ V\xf4V\x19;V\x05\xaf\x97\"!1\x16\x9c\x9c\xbd\xfb\xf1\xf5\xef?~8\x1d\x1f\x7f\xbc\xf8 _\xfd\xf1\xf8\xcd\xebW\xc7\x17\xa7\xf8\x83\xbf=\xfb\xf0\xfa\xff\x7f:>\xe3\x7f\xee\xe2\xcb\xf7\xb2\xbaU\xf0\xe6\xec\xf7g\x1f/\xea\x1f\xe2\xaf\xf3\x9f\xce~\xc6O\xc6\xef\xcf\xde\x7f|\x0f\x87\x8a(|W\x81T\x86\xcf\xf5\x13\x7f\xff\xb1yE\x9f\xca\x92\xdd=\xea\xf2\x1e\xbf\x19\x04\xb5C*\x9f\xa7\xb7\xaf\xf8\xa2\xc6\x1c4\x9d|\x9e\xecm_`\xea\xf9 A\xa1\xa3\xbbE\x1aM\x87\xcdbG\xb9\x16\xdf\xd2;A\xfe\xbb\xf5\xbeH\xaf\xd3u'V\xdf\xd5\xf5\xea\xbe]\x97\x13?\xe3\x7f\xed~\xcb\x18\xa6\xf7\x1d\xc3\x04\xa3=\xaf\x05\xe2\x7f\xcb\x08\xe6\xf7\x19A\x1d\xb1#\x85\xbe\xfdg&\xfe\xaee\xd1\x9ee\x96\x92\x0bV\xa7OZ\x9e\x10nEJn\x13&\x1e\x15\xf5\x92\x8a\x1c{zJ\xacv\xcf\xa26\x89\x89c'{|\xab\x8dW\xe9j\xbd\xf2\xec+\x8c:%\xf0J\xcc0\xaa\xae\xea\xf4\xc3\x13\xc8kT\x9ab\xcaK\x17\xf9\xf1V\x19\x1b\x97\xed\x8fSD=/\xa4\x89\x98gU4\xa0?\x17}i\xc4\xd0S\x17\x97\xd8\xa6E8\xbd\x12\xe1p\x10^\x8d\x1a9\xe8o+NV\x9c\x1c\xc5\x95\x94\xcay\xdcp\xc7X\xb3!\xe2m\xd1cY\xd6XKx\xd2\xf3\xc6\xe8\xf2H\xc4,K?\xb1\x84\xae ,\xa8\xa5[#]e!\xf2RM\xe6l\x19\xd15&\"\xc2E\xb4t\xf8\xfb\x8b\x9b\xb1kV\xf8\xdel\x91\xdeR\xe1\x82d\xc4\xf4uO\xe2x/\xbf\x8d\xae\xafY\xf6\xf1\xf5\x076\xc5\xb8\xcf\x822\x85\xe0E\xe51+t\x063\xcep\x88\x1c;\xbd\x84\xdd\xf2e;\xcd\xcc\xa4\xfe\xea\xe1\x8d\xbc\x9e\x92G\x04\x7f\xf2t\x9dM\xd8P\xe5\x90\xa7\xe1\xc1n\xd8b\x08\xdem\x94%qr\xed\xa8%%\xc1!x\n\x8f\xc4\x91\xbf\x8c\xee\xe0\x8a\xc1\x1a\xddgCXEy\xce\xa6\x90\xa3y\xc5m\x94\x83\x88\x0e\x86J\x8e\x9ce7,\x83\xf7F\x95\xe4\xdf\n\x89ml*\xc2|a\x1eRQ\x9b\xb0C\x0cB\x88z\x18J\x0c\xed+~M\x10a\xafm\x00\xf2\xfb!\xc4j\xdd\x03?\xa2<\x821\x13\x97qH5\x0c\xdf\no\xa8\x1e\xdc C\x88\x88.\\$U\xa7\n\x14\xaf\xf6\xeb\x92\x04\xd6\xb8\x11c\x11X\xc3\xb9\x11\x059(\x13\xab\x91u\xd62\x84\x87\x98\xa0\x9b$Tu.\xac\x8bt\xf5L\x84zu\x11\xb3\xa4x\xedhk\xa6\xd59g\x93\x8c92\x9b\xaf\x9c&\xba\xfc\xb9\xce\xa2\xa4\x18\x8b\xf3\xdfS\x03s`\x1e\x7f\xf2I\xca\xabrp\xa6+\x96K\xfbF |\x16\x01\xac+A\xf5\xa0\xc7\x9e\xa3l.}\x15\xcd\xf7JKy\xc5\xa5 A\xc0\x16p\x04\xf3^\x9dL\x1c\x82\x87\xf2\x06\x9a_\xf2\x1d\x92\xf7\xae\x8a4\n\xfc\xa8\xcc\xf8\xba\xc6\xbbM^\x96V\xbbgEy\x9d\xf3G-:\x89\xfc\xae\x8f\x14 \x87\xb0&\xe9\x8a\xcc\xc1[\xce\xc2\x9f\xa0\x06`*\x97s\x1cs\x08M\x82\x10f\xf5\xf79\xae3\xdf<\xe8\xba\xd5y\xf2\x93r\xf2\xb3\x00\xd3\xec\x99\xf2\x9b\x83&\\\xa5\xd3\xbb\xa1ji\x1d/\xa6\\8{\x15\x15Q\xe0\xaf\x1c\x8a\xcdu\xb6\x18\x8a\xe0\xce\xbe\x87T\xe3c\xb60Y\x0e\xf5\x08\xb8\xc6\x0eD`\xd1\x94e9\xc9\x96\xf2\x07AH\xb2\xcdPR3\xe2N\xdcI\xafB\xb7\xb0\xf9[\"U\xa9\xac\xc1w\xdf\xb7\x10\xb3f\xe2\xb2\xeeH\\l\x93b\xfd\xa9a\xe7\xb0\xcb\xce\xdc\x84\x8a\xd0\xc1\x00\xd4S#lr\xfbL26eI\x11G\x8b\xbc\x9d\xc4\xa5m\xb4\xcdI\xa3\x1eb{M\xee\xb3e6\xd9{r\x83\xb4\xec=\"r~\xc7\x0d\xe4\xd6\xe9\xb4\xdb\x00\xb98\xf3D\xba:\n\xc6\xf6c\xb6hV\n;m\x8f\xb3\xb2\x8fV!\xa1h\xe5\x1b\x8a\x96\xadVt\xd8j\xc57o\xb5\x1a\xbaG\xfa\xbe\x1bO8\xc7\xefF\xf7 f\x08(z\x13g\xd81\xac\xa5\x0e\xa6!8`\xa1\xd5\x12\xc7\xd4\x10\xd6\xee\x9aj\x11\xc7\xeb,\x1e\x12V\x04\xd0\xb8\xc3\xb2\x07\xd8af\xd2U\xf5\xb4\xef\xb0t\x93\x1df'\x9c\xbe\xd7\x0e\xa2\x95\xa8\xff\xdcJ\xb5\xe7a\xb6\xd2o\xe6\xd4\xfa\xbbm\xe3\xbf\xff\xe6\xbc\xff\xf1\xb7\xd9\xe6\xfc\xa5\x8e\xbf\xeaZ\xe4\xc1x\xc7\x99C\x13%\x90\xfe\x9a\x152\xeb\x1f]+\xef\xc6\x7f.:i\xcf\x84\x824\x8d\xf2\xbds\x0c\xae\x9e\xbaR\x15 \xbdh\xbeb\x93\x96\x8a\xabrx-\x15\xa7Ho8\xe68\x96\x0e\xcbQ6\xa0+\xdc\x94W2(}\xcd\xe1\x08\xfe\xf6\x15\x9cR\xc6\x12\xdb\x93\x08AW\xb9\xae\xb7\xb8T-.\xe9\xeaw-\xec\xf9\x95\xd05dD\xa4 \xfe\x8c[4\x97\xb7p\x08\xfeJ\xc3\x07\x1f\xad\xe2\xff\xf65\xe8E\xd3)\xde\x11E\x8b\xff\xe0\xf0\x11\xd6\xfa\x82-\xa3\xdb:%\xae\xaf\xf4\xb2Y/\xce\xcf\x8e\xcf\xf7\xfc\x80\xcb\xb0\xfd\x10\xa2J\xa0\xbe\na\xd2\x13\xb1\xf7\xd9\xf4\x1cul\xbe\xc8\xac\x0cC\xa2\xee\x8c\xcfXV\x08\xeb^\xe2\xbaU\xd1-\x1c\xd5\"\xf6\x89\xa6\xb2\xaa\xa9\xdb@\\\xa6\x9f\xca\xb4\xf4\x87`\x08\xfa\x7f\xfb\x1a\x82,\x0c\xe1\x96\xb2\xe3\xe3[\xee3\x1c\xc2i\xe9\xd1\xe0;\x88\xc89\xd1\xbc\x93\xa8\xf2\xf3|\x85a\xcc+\xd9\xf2\xd1_\xf24 \xa1`\x9f\x8bG\xabE\x14'!\xfc\xee\xd1\xef\x1a\xa8\xbcw\"\x82[\xee\\\xdc\xad\x98g4\xf6y\xe7\xf6\xf6vg\x96f\xcb\x9du\xb6` ?\n\xa6\xb6b\x13\x04\xb5\xba\xa6\\\xb3z3VL\xe6\x8eY }\xfd\xec\xd8'\x18\xd6i\x08\xde*\xcd\xcd\xdb\x0c\xf5\x94d\xf5\x9c.\x97\x12\xfd\x8dc_\xe0i\xe18\xf9e\x9c\x1bt\xf3\xe2`N\xb3!\xac\xfd\xa0g\xbfw}\x9f\xaf\xd2$gD\x03V\x81\xd5\xc0\xd7\xa0\xc7\xf92\xbf\x99[\x02\x8d+\xd3,KYo\xcaO<\xf7\x92#\xf5\x97.\x91B\x1b\xfd\xe5\x0bx\xaes\x0d\xd4\x15\x88\xfc\x02;9\xd5>\xa3\xed X/\xfd\x84\x0e\xcc_\xbe@\x06G\xb0hWw\x83\xa6\xf2v\xd0Z\xe8\xa8\xd2\x86\x8e\xeaqhP\x7f\x13\x16\x85\xa0T\xe0yG\x158\x94\x8c\xc1\xd8=\x00\xa9\n\xb7\xf9zP\xdd\xfd\x03\x00\x8f\xf5\xf2\"*\xd6\xf9\x05\xfb\xec\x9a\x08\x85\xe6\x98\xaai\x03<\xaf\xacQY\xa0l\xfch\x04D\xcb\xc5r\xb7\x89\x9b]\xf5K\xec\x90\x06\xae\xf9\xa6\x0c\x00P\xfb\xc4m\xf2C\xe7\xa6\xd2\x1f%\xdbh!M*\x17\xad#}\x03\x8bL\xa4\xcd\xe6E\x99\xdc\xb9\xc2sp\xfb\x10\xbc\x10\x98H\x16%\xc2\x04\xe0\x0ft\xee\xc5\xbf\xc6S\x96O\xb2x\x85b\x9e\xfe\x91\xf6\xbe\xf6\xa9\xfeA\x93m\x92\x96k\xcb\xf6\x0e\x02\xa0|\x86\x00\xfd\xec\x7f\xf3\x18\xbd\x01\x1a\xd7^\xfd\xf6l\xab\x10\xad\xfe\x14-\x17\x82\x81s\x99\x10\x95\x19\xa7\xc8\xe8\xbb\x98k*\x15!U\xeb&\x12Y\xb3\x89\x84\x91\xbb\xb6v\xb7o\x0d\xac\xd1\xd8\x94\xdedR\xea\x89\xab\x0bk\x0c\x87\x1cM-g\xea\xc6\xc4p\xb2\x19\x91\x0fT\x13X8\xa2^\xcc\xb3\xf46\xe1\xa8\xaa\xd3\x9f 4q\xfe\xb7\xb7\xf4\x8b4\x9a2a\xc8vq\xf6\xfb\xdf\xbf9\x1d\x0b\xeb\x8bs|\xf5\xf1\xfd\xab\xe3\x0b\xfdU3^\x98\x16\xc5\xbf\x14Z\xacUh\x86Flh\xb1=\"\xb4\x11\xa5\xed\x91q\xd2s\x0e\x9e\xd9 *PrH\x16\xe9\xf5\xf5\xe2\x9b\xcc\xd1\x08\xe5\xe5}\xac\xa1\x88e\x93\x064\xf9X@\x8ep\xc9&\x96\xbf\xfcH\xcc\xcc\xd3W\xa0D\x9br\xb2m\xba\x86\x1a\xfd\xbf\x07\xf6\x97\xafK;\xadL}D\x07AG\x03\xfd<\xc3\x8bmi\xae\xcf\x92\x9b\x9aA\x7f!\xcd\x17\x95\xc9?\x92\x1b\xe4e\x95}?\xe7\xbcr\xcd\xe0\x7f\x95\xe6\xc20[\xfdz\x1bq\xc1M\xf5%\xed\xb7e1\x9e\x9e\xd6Z\x90j\xe3\xf1U:\xbd\x1b#\xf6y\xb6,e5&\xb3T\x8d/\xfe\xf4\x9enN2Vx\xbfk4\x18\xd5\x1b<\x7f\x7f\xf6\xee\xfc\xb4\xa9E\xb1\xd3\x9b\x9a\\\xd7\xe1\xc5\xc14\xfe\xe3\xf1\x87\xd7\xc7?\xbc9%\xe6,\xa06\xbe\x91\x08/\xa7\x8d-\xde\xeb\xd8\xbf\xd1\x02\x95R1\xc2\x12\x7f\xb7O\xba\xc2\x0e\x1e\x9b\xf1\xad\x84/\xecc\xb3\xbap\x85}b\xbe\x16\xee$\xfb\x8f\xcd\xf0\xa8\x0b\xe19kjK&b,\xfbf\xf5\x99\x18\xcc\xb3\xc0\xf7\xe2\x82e\x11Fv\xaaWYq\xfe\xdf\x1f]b,\x14\x8c\x9c\x91p\x8e\x1a\xe2\x04\xe4K\xdf\xf4ui\x94\xd2@Sl\xcc\xe3\xbc\xbe-*\xc8:\xdd}Q\xfa\x9a\x87\xca\xd3\xd5l>\xf7\x13\xacdFQ\xe2+u\x17\xc2U\x08c\xe1\xea\xda\xae\xe0\xc50\x10\x98 \x0b\xf3R\x9c\x94\x9e\x8e'V~Z\xf5tr;\x15148\xe4\x1a\xf2\xad\x89J\x88\x9fM\xd5\x80\x96{\x1b\xebk\xdf$\xec\x16\x12\xe9\xa7\xee\xc8\xe7\xa6\x9eMT\xa9\x9b\x8c\xa8\xfbH\xec\xbe\x08\xf3\x13\xf4P\xc4\x10\xb5\xaf\x15B\xdb\x95>K\x07 \x0e[8<\xa4n\xe3\xce\x85\xd8k\xbd?\x11\xdc\x02\x1d#\x8e?\x9f\xe0\x10NF3\xcc\xfas2\xf2\xfe\xfd\xdf\xcb\x8d\x85\xafn8>\x9d\x8cn.\xed/\x8f\xe1\x10>\xa1\xc3\xb4\x7fC\xdc|\x9d\xc1!\xdc\xc0\x11|\x86#\xb8\xf5=\x96\x14Y\xccr/\x80!\x1c\x97~\xd9\xf6g\xe8\xd4\x85\xb1&\x84~\x1f\xfb\xef\xc9\xafyoF\x82@\x8e\xf5\xefQ\x1f?\x86C\x98\xf8\xefeT6v\x0b,\x08\x02\x8c\xe5i\x86\xbc\xe2\xd5\xc7\x98\xb3\x13?\\\xf8\xe3\x10N\xe55\xb7\xb8\x93S\xa8\xa0\xdf1\x8c%\x94\"^}\x16\xc24\x08B\xf8\xcc[\xc0\xbc_\xe5\x02\xf1\x1e?\x89X \xbc\xf5s\x19i\xf4\xb8#\x95\xf9T\x05c0\xb4i8\xba\xef\xbf\x87\xadk\x0c>\x8f[}\xeb\\,\x90\x1a\xda \x0e\xed8\x08a=*\xb8\xa8z\xcc\xff:\xe5\x7fMC |\xa49\xfc\xee\x9c\xf6ObNC\\D\xbej\xb7\xbe\x9a\xa6\xe3\xaeS\xc4Y^V\xd5\x91n8*\xcbU\x1d\xc2\x19\xb1U\xe0\x9a\xdeV(\xd8_I\x1f}\xfc\xff\x84O=\xe6S\xbf\n\xe1ntuI\\\xa8\xa2\x03x\xea\xa7\xbd\xf7\xb0\x0di\xefG\xf8\x1d\x08o\xff\xf3\x00\xe9\xef\x1d\x1d\x80e\xc3(\xf7\xfa)\xb0\x95\xf8\xfb\xfb\xa8\xd5\xddJ\xfc\xc7\x83\xc0\x9dQP\xf6\xf5\x04\xb6\x0e\x1d\x829?\x80\x0f\x02\x99\x9f>\x04/\xb2ds\x10\xc9w\x86\xedDL\xf5f\x83\xdc\xc0\xb6^\xe5\\!\xefg:\x07\xdaxLG\xc9|B\xe5\x85\xe1l\xc1^\xe0[9cd\xb0\x8d\x83A\xe0{\xafO\xc7\xef?\x9c]\x9cy\xf7\x0e\xb0\x11\"g\x92\x92\x894\x84\xc2\xd2z\xbdp\xc5M\xc3P\x82\xeb\x00\x12\x0ci\x89z{\x7f\x8d\xb0\xc0\xa8\x902\xc4/\xf1\xe1\xf32 \x0e\xbc\x84\xfcy \xbf\xe3G\xc0(\xdf\xde\xbe\x14f2\xff\x1d\xfb\x0bl\xed\xcb\x97\xaa5\x1a=\xcd\xa8\xe2\x9d\x17hw\x10\xf4T\nb\x1a\xa4\x99\xb8\x8fP\x95d\xd0\xdd\xcdzq\xa1\x01u\x0bb/\xb5\x8d\x0e&\x1d\xa7GN\x06\xd3\xac\x07\x8btj\xe4$\x8a\x08\xcdy\x8ca\xe8F\xf1%\x0c\xe9\x13\xc1\x0en\xaf\x07 \xad\x97\x1e\x19\x91\xef\xab\xc3hX\xffL\x86\x88:\x82\x08\x86T\xe4\xf8\xce\xd0\xdf\xdb#\xa0\x9f\x8d\xbc\xf1x\x92fl\xe7/\xf98\x9fG\x19\x9b\x8e\xc7\xe2\xa8\xf7]e\x87\xf0\xb7\xaf\xad\x1b\xcf\x01\xd2t$r8\xfa\xa9\xd0\x9c\xfe\xedk\xd02\x1f\x17=\xbd\x9fF\x91%\xeb%\xcb\xb8\xf04\x84-\x7f\x00\xdf\x03E\x01\x94\xf7\xb4\xaa\xb7\xeb\xa8w\x9b\xc5\x85\xaa\xb3\xef\xa8\xa3\x14#\xb5\x82o\xba\xd8\xa9Z.\xb7\xef\xfe\xe3\xc0\xdf\xd2\xb5\xd4\xfc\xddA\xe0\xcbh\xbf\xe0\x89?\xbc\xa6$\x1a\xa8g\x1e\x17p\x08\xd2\xa2\xaeT\xca\x8f\xe3\xfa\xcdG\xe8>U\xf8\x98\x98L}/\xda\xb3!Rj\xe0\xc71I\xc5\x12xyXQ\xc6#b\x15%L]<\xe34M\x98\x9d\xe0\x15\x86\x18\xcc\x0d2\x91\x7f\xa0\x9a\xdb\xf6a\x19V\x8f:Feg\x04\xaf,\xfb\x19\xd4\xfb\xd1\x10z\xc3cr0\xa0\x03R=\xde\xbb\xefv++4\x05\xd3\x8fC\x88\xc4y(\x17>\xf5\x0bS&V\x0f\x1e\x05~\xe2(\x15A\xa6]\xd1\xd2\xe4\x98rx\x01}\xe1\xd7\xfeR\xb8V28\x02\xcf+\x85\x00\xbeP1\xb6\xa4\x05/\xcc\x83\x00^\xc0\xe3\xc7\xbb\xcf\x0e\x90\xbd\x83\x97\xf0\xf8`o\xf0L4\xb4\x0d\x03\xe9\xa8\xc9iKd}\xcc+\x88\x06\x0e\xf6v\xb1\xf3\x887\xf0do\x7fO\xf6/\xeacG0\xc44H\xe2m\xbe\x88'\xcc\xcfC\xec\x04s\xd5D\xb0#\x9b\xd9\xe6\xe3\xdc\x91\x83z\xf1\x02\x06\xfd\x00\xb6\xe1\xe0\xf1\xe3\xbd\x83_v\xb7\x9b\xfa\x11\xa9\xab1\xb1G\x86-3\xe9\xbeT\xd5\x98\x1a\x9c\xb5\x0c\xf1a\x9e\xc6RWs@\xebj\x06\x96ng\"\xeb\x9b\x83\x94\xca\x9a'\xffT\xd6\x10\xcf?\x955\xfa\xf3Oe\x0d>\xffT\xd6\xfcSY\xf3Oe\xcd/\xa6\xacqjj\x06duw\x18\xd1\x03\xc7\xdd\xc9\xe3\xbe\x83o\xd3\xc2\xb3w\x12DQ\xfcL\xdb$\xa5\x0d\xf9\xca\xb7Q1\xef-\xa3\xcf6\xcf J\xe2\xa4\xc3 \xe9\x18\xb0d\xb4\x19\xf2\\}8\xe2b4l\x83\n\xc2\x19\xfb\xcc\x88\xc9\x0f\x1b\xac\x8f\x9e\xc8#4\xb2\x96\xc4\xb9\x9e1c%_\xbf\xceOK\xb9/,\xd27\xe9$Z0)\x1b\x95)Qpo\x9c\xcd\xbc^\xbeZ\xc4\x85\xef\x85\xde\x86\xec\xfb\xde\xde\xaf\xa2Dq\x04\xad\xdd\xa5\x95i\xc8o\xe5+6A\xfa}\x8f\x15\x95\xea\xb2H.hk\xca\x14\xcd\x13,\xc2CH\xfd\x16Q\x923?\nF\xf1e \x13\xef\xa4z\x92\xf3\xeeh-b\x17\x87J)h\xddR\n^v\xff\x89 \xab\\nL\x07/{`\xf2\xc4\x13Zs\xc2Y\xd9\x89\xca\xcdl\xb3\xb0\x93^\xce\x8a\xd7\xcb%\x9b\xc6Q\xc1l~u\xd2\x9b,X\x949j\xcc\xb1\xc6[a4\x7f2\x8f\x92\x84\x19~\x867X\xe3U\x9c\xaf\xa2bb\x98},m\xe5\xe55\x11\xca\xe7\xae\xed@CA\x1e\x0ea\x9b\x9fe6I\xe6'\xcf\xb5\x99:\x85\xce\x90\x01\x9a\xe1\xc5\xb5\x93\x9b\x95A\xd2x\x85\x10\n\x9f\xf0 \xa8\xbd1\xa6s\xd5\xcad\xdf\xc9\\ \xc2Q\xa5\xdeV5\"<\x96\xa7(D\xae\x1a\x9b\xac\xa5\xfd\x18]\n\xad\xed\xe09D\xd95n\xed\xbcR\xec&\xcf\x03\x95C\xa3,\x1d%\xdb\xdb\xe6I'\xf7\xcf\xf5h{{y\xd9\xb6\xd0\x02(\x7f\xe5\x0c&_\x87\x9b^\x92\xde\xb6\xb6\x86\xb5\x9c\x0d\xcd\xe1H(\x13|$\x93\xec\x16\xe6A\x8f\xd3\xbd\xdd\x10R\xfcc\xd0K\x93*\xb4\xf9\x95\x08T\x1f\xf9qo\x95\xe6\x85\xdc\x85Hk\x06\x18\xcfi\xd2\x8b\xa6\xd3\xd3\x1b\x96\x14o\xe2\xbc` C\x9aN.\x86\xd6\x00r{\x93^\xbc\xe4=\x9e\xa3\x17P\xceG\xd6<\xb5\x89>\x06<@=/\x04\xefw\xf54\x07\xf6\x88|ON\xc8C\xaejK\x8c\x1c]\xa5\xd2$c\xd1\xf4\x0e\x03\xee\x89p|(]/|O\xf8&a\xaa\x15\xf7\x88\xf2^\xb4Z\xb1d\x8a\xf9\xe8}\xed\xab\xa0g\xb7\xdc\x86\xc3y/c\xcb\xf4\x86\x89\xc6\x90g\x0e\xcb}\xea\xf4\x1c\x80\xa6\xcc\x959+.\xe2%K\xd7\x85\x86\x11\x9c\xe9\xa8\xbe\x0f\xeaF\xb3\xd6\xf7V\xa4Y\xa4\xd5C\x98VM\xe0_]\xb9\x15\xf7`\x1b\x9doh:\x8a\xeaF\x9a\x1f\xbf\x19\x02k'\x9b]\x1cv\xdc]\x13\"\x1f\xc8\xae\xdb:n\x81\xde\xa6\xec\xce\x13:D\xff\xe0I{V3G\x9e\x8f\x0cie\xea\x17vj8\x91\x90\xa8-\xb5q\xdc\x9b\xb9\xb2\xfe\xfa\xfd\x10\x92^\xc6\xf2tq\xc3\x02\x8cl\x8f\xa9\xfc\x96\xb1\x96\xdfjC\xc0X\x10\x10\x80yF+\x01\x91\x0dDg\x86v&\x90\xe2\x00\xe9|\xf3\x98\xc7\x8f\xcb\xc9Z\xdaT\x91wF\xb2x[[\x9c\xc9\xf3>\xb0\xeb\xd3\xcf+\xa4\x8di-%\xe6\x86s\xb6\xf8<\x95\xb0\x81\x9c\xf3\xe3{\xe1\x82ZN?\xed\xc9\xab7\x11\x9aA^\\\x89w\x9cK\xb10>\"\xc2\"F\xd2A\xc0O\xf0\x161\xeb\x9d\xa3C(\x17ac\xb7\x05\x00\x88l\x9e\xb6\nA&\x8c\xf1B\x88\xee\x0d\xc4g\xae\xdb\x84Zf\x97Nr\xa9\xa6\xeb\xc9\xea\xc9\xc57\x1a\xd1\xee\x9eC\xa69\xd8Cyc\x12\x15\xbe'\xf8)O0\x1dB\xc2\xab\x875\x9e\xd5\xeez5\xbe\xf4]\xb4d\xbf\x8e\x9c\xbdk\"\xa2\xdc\x934~Z\xe6\x0fR\x9aylj\xce\x854c\xdd\x9eKaf\xcf\x14Z\x16.@\xbc\x92\x0e\xc8\xba\xe4&\xe0&lS\x8e`\x01- peF$\xcc\x98'\xae\xf9\"\xbf\x90\xda\xb7\xd2\xccL|`\x1eH_\xad\xaedN\xa5\x92\xf4\xa6\xfeV\xd6\x9bii\xfdB`\xa3\xe2\xb2m\xc5\xcc\xe5Jp\xa7\x96\xb1C\x1el;\xa8D\xae\xf8\xc9\xa5\xe0\x8a-~\xa6\x13R\xb9Y\x94\xd2\xdd3\xf1\x1f\xef\x99\x18Ty\xeb\xd4\xfdr\xbat\xd9v\xed\xf4\xec\x80\xde\xa4O\xcc\xf7\xb1c3\x08\xf4\xb6\xac=\xe4\xbd\x93\x95tGS\x94Ey\x1e_;\xd4Q[\xb8\xb5[L\xaa\x944KE\xb4-\x1c\xef9\x92\x9c\xdf-\xaf\xd2\x05\x15[\x06\xb9\xe9\xe8j2e\xb3\xeby\xfc\x97O\x8be\x92\xae\xfe+\xcb\x0b\x8f<)e:\xd1'!dJ\xbf\xe4\x05\xbdY\x9a\x9dF\xad\xd1\x1a\nq\x86\x18\x0e\xadA(,\xc4r\xe1l\x1b\xf0\x0e\xca\xf3I\xdc\x95\x89\xa2\"\x08d\x98L\x0f\x93\xeeVn\x16_\xeb\xcc~\x9b\xd7\\\x84{\x9e\xc3\xdc\x94rC\xa49\x83PFK\x9f\x85\xa8!\x89{\xb3\xe7\x90\xc3KX<\xb7\xf9\xd2\xb2\xe5\x95\x90=\xd7\x9ap\xbc\xe0\xc2q(\x14!\\\xfe\xf3\xa7\xe510\xf1\xa7B\x98\xf1\xa7A\x88\x8a\x90y9\x86\xa5H\xc2u\x03/a\xf9<\x00I&\xa6!\xead\xe6\xa3eiQ\x95\x8cV\xa8S\x1f\xad\x1c2\xb8\x96a\x0d\x86\xdd\xb2J\xb5\xed\x9eA\x9f\xe6\xd7\x06\xa6nI\xec\x9e\xdd\x03j\xf7\xf8\xbc\xe0\x80s\x8f\xfe`\xf7 \xa8\xd9{<\xc5\xd7\x8f\xf7\x1e\x93)\x1a\xd6\xd4\x98\xa1t\xd7\xcc\xd2U\xae\xb9\xfdV)\xd4\x95_o\xc6f\xb9\xcc\xe2\xc7\x7f\n\xafh\x9c\x19\xea\xef5Jc\xf7\x9d\xff\x1d\xfb^\xd4\xdd\xa8\xd7\x9aof\x9c\x7f`\xd1\xa4\xd0\xf3\x10\xf2\xed\xa2W\xc9e>\xfd6\x9e\xb1\x8c\x85e\xe4\x82wg\x89\xc7\xbc\xbe[\x87e\xca\xf8\xa7\x8f\xbd\xa0>\xbf\x9e\x91\xd3\xbf\xbc\xaf\x0ceD\x05\xa2\xae\xcab\xafR\xb7\x85\xe0\xa9)\xd4u\x06\xfa$gi6a\x1f\xed\x00\x01\xe4j\x19\x1d\xfeX}\xab\x04x\xd6qp,\x04O\xeb\xba>\xbeE-\xab\xf1Z\xcfj\x9c\xd7\xf3#\xb3[X\xd4^\x1a)\x97s.\xd3\xe5z\x03ZkA\xfd\xcb8\x7f\xbf\xce\x98\x85\x15[\xfd&\x95AY\xd3r\xe5\xe2\x8di\xa5\xb9\x86\xa8p_\x82\x92\xf8\xcf\x02\x9b\xbc\x18\x0bc\xf5l\xfe\x90\xae\xafa\x861\x0c\xba\xfe\x07\x91\xcb\x13q\xb5k\x1fjk\x10\xf5+X;nb\xee\xbf\x04\n\xe8z\xc2\xb0\x07n\x9aT'\n^\x84\xef.\xf1\x17\xdf\xb8\xf5_\xbe\x97q\xdc\xed1q\xaf\xe4\xa1\xc9\xf0A\x7f\xd0\xdf\xfb\xc5F\x9a\xf8\x8f\xf7\xefm\x9d\x86\xe2\xd6\xd6`C\xd6\x98\x1eP\xed\x82\xf0\xfc\xf4\xe4\xc3\xe9\xc5\xf8\xd5\xd9\xf8\xdd\xd9\xc5\xf8\xfd\xf1\xf9\xf9\xf8\xe2\xa7\xd7\xe7\xe3\xb3\x0f\xe3?\x9d}\x1c\xff\xfc\xfa\xcd\x9b\xf1\x0f\xa7\xe3\x1f_\x7f8}\xf5\x0d\xees\x0f\xe65O\xc1u\xd7\x12\x0f\xa51\xe0\x01\xed\x92\xf7\xd82\xd0\x92v^\x074\xc3\xbd\xfb\xe4q\xdd^\xf4\xc9\xbe\xfe\xbb\x87)\x13=\x91k\xfe\xbcH3\xe65\x98}\xaa\x05\xed]i\xb3\n\xabV\xd2\xe5U\x9c\xb0\x0fl\xba\x9e\xa0\xd7gkKi\xcd\xdb\xa0j\xe9*N\xa6\"\x8c\xd0 \x1fY\xda\xa9\xb1\xd8\xd1X\xb4Z-\xee\xde\xc6\xd3\xe9\x82\xddF\x9d&\x189Z\x9ap2\x9fwia\xbd\xb1\x1b\x85\xe3 Ps\xe8\xd0g\\\x1bs\xd1\xd3o\xcb\x80\xc9|\xb0V\xf46\x8e\x8aFJO\x92.a\xf4\xb3\xda\xad/\xe7\xb1\x11\xf9\xc4\xb5\x98(38m-\x15\xf1\x16\xff\x88:\x9f0\xa5/\xc5BED*\xe5\xd3\xcf+\x8c\xf9\x00\xc5\x9c\x01K\xe6Q2a\x19\x14)\\1\x88\xca\xe9\xf6\xa8\xe8\x8ajq}\x16\x08C\xd9Z\x0d[+A\x8e\xa9h\x1bS&\xb0\xbf}H72\x99/\xa1g\xc6{j\xfb\xf5\x84pM\xe1\xef\xf1\x9e\xda~\xbd\x92\xa7W\xad\xa0D\x88)\xa9\x8e\x9c\xe1\xda\x8a\x1c(\xe2\xfa[X\xc6\x06&\xb0\xe8F\xe7MVS\x8bNM\xdc\xd0L\x8csAX\xd3\x82,\xd4\xe5]\xebj\x80v}M\xa5O\x95s\x98\xfaA\x08\xb32\x9a\x8dU\x0d\xb4\xa94\xda(\x8a\xd4\xdb\x0d\x15@\xea,\xb6\x06!\xef\xd5\x1e\x91\xfe(\xd9}&\xb23\x9f\xd9W\x14\xe63C\xfd\xc4\x84\xf9I\x08\x03\xda\x8a\x0b\xac]A\xbfu\xad\xe4\xd2\xbd\x92[Y/B;\x02k\xe9d\xf08X\xae\xf3\x82/\x19\xc6\xe2\x05!x\xe5=\xf8\x983\x98\xac\xf3\"]\xc2\xb2\xa4\xe8\xa8e\x88\xf2\xbbd\x02\x91\xf8\x9c\\^#-:\xeb\xa1l`\x0d\xe1\xdf\xca!Dw\x98\xb2}\x1e\xdd0\x88\x12(\x83\x1d\x83\x87jiPvG=\xf8\x89W\xb9K\xd7\xb0\x8c\xf3|\xc5\x16\x0b6\x85\x08PD\x89\x92\xe2\xe8\xdf\x1c\xa3Y\x11\x00P\xa7g\xd9\xfdT\x1a\x804\xce\xcd\x1dFs%E\x1bNSr\x7fA\x9a\xc2~\x85Y\x9cD\x8bEc\x1b\x03\xfb3\x9b|\xe8\xf6\x12\x9c\\\xcd\xc4\xd9 \x93\xa6k\x89\xe1\xb7\xb7]\xc8\x7f#3\xb6\x17\xa3\xc4aD\x92\xb6^\x80\x82\xa6\x92\xfb\xce]m\xe9\x0c\xc8\x15\xf7^\xbf{}Q\xff\x94V\"\xadI\xc3L\xb5hd\xec\xf1|}\x95O\xb2\xf8\x8a\x91\x11\x96\xafKq\x87\n\xf5\"\xe4'\x89$m\x92\x1f\xdc\x9bp\xf2\x93,a\x9f\x8b\x0f]O3\xf5H\x1d\x0f\x05Y\xf58!\xac\x1e*Th})BX\x8f\xd2^\xd4j?sS\xf9)\x11I\xacu+Fz\xb8\xdaJ\xb5C\x1a\x14\xb4 5\x91\x0e\xeb\x8b\xbb\x15\xa3\xe0\x9d^\xc9t\x89\x12\xd8\x8a\xec!\xac\x9d=\x96\xe4\xb6\xddJ\x9f\x95\xf6\xd4\xe2/\x7fn\x9e\xeb\xfaC\x93~@)\xa2\xe1pQ\xa2Ma9\xc3\xeaO\xa3\x0d\x82z\xd6\x89\x06\x7f;l\x90z\xba\x9cQ\xf8&\xe8\x843P\x0d\xcf\xf2&\x01\x81|\xcc\xc2\xc6\xf2\x05\x11)\x87\x0b]\xb4K\xecc\xeb\x0e0&Q\x91\xef\x94!x\xff\xfe\xef\x9c\xb9\xfc\xfc\x88\xff\xac\x07\x93\xff\x06\x89Z\x17\xf1\x1d~i\xd6\x9d\x8d\x14E\x1f\x9bWB\\\x1a(o\xc7\x84\xd8|I\x84\xc2Qfk.\x9f\x87\x9cp\xfa\xad\xd7\x10\x1eh\xa5Mo\xad\x8c\x1f;\xb9a\xb3X\xaf!\x92\xb9\xe2\xb5\x81\xe8\xa6v\xc1\x1c5\xea4\x90{\x89\x91{\x01\xcc\xd7\x8a\x7fm\xa1hS*\xdal^\xbc\xc0\x1b\x93\xc8b\xcbxs\xa8$\xe6\x1cIQ5\xd1\xb7\x9bH\x90\x1d\x17\x8e\x07a\xcd:\xda\xb3mY\xc8\xa3\xca-\xd7%\xba+2\xbe\x91\xf0I\x02^uV\xa1\xf7\x83 \xda\xe3~\xd0\x8bzB\xa3e\x82~cm\xd5\xa6\xf5\x9dkm.u\xc9\xcc0\xf2.\xacP\x97\xc7x_\xa6q9exIq\x19\xa8Y\x83^\xda\x8b/xQ\xc5\x18\x95\x08\xd0|\xda\xd0\xac\x8d\xdd\xf8\x80n\xbc\x18\xf5/I\x04)zBz\xf5k\xb0l\x18AWB\xca\xfc\xa2\x87j\x18\xc9\x80\x87\x15T\x88\x13\xc88\xec\x1fDq\xf8`J\xbc\x10\n\x15\x00\xb9\x8b\xf2S\\\x10\xd5(\xb7&}\xc0\x11xq\x12\x17q\xb4\x107P\n,*\xabr\x91\x82\xae\x9b\x83!\xa6\x1c\xbf\x89\xd3u.\xd3)gl\xc2\xe2\x1b6\x85\xab;]\xffP\x8b\xec\xaakM\xcb\xd1w\x81e\xb5g\x9f8\x9cQ-\xdb{y\xb1i\x1e\x19\xca\x84\x9frG\x1d\xc0#\xd3\x98]\xb8Q\x1cA=b\x02\xe5\x90\x86r\x0d\x1cA^\x1e\x07e\xc5j\xf5)}5GJ\x8a\xba\x13y\x06\n\x97Q \xaf\x1f\xfb5\xcb\x95\x82KXh\xc3kW\x8d\xf4\xaa\x0bL\xee!\xe8y\xc0\x17\xd6\xa3i~A4\xa6\x08z_\x18\x9fp\x1c\xe3@,\xf8\xaf\x9d5\xc7\xaa\x9d>G\x96d\xb3\xadS\xed{\xa7\xbd\x9c\x96\x0f\xa8\x84\x0e\x9e>\xe2\x08\x92\xb6t\x87\xa5G\x1f\xbe\xae\x0f^_\x0cm\x80Ay\xb6%\xfe\x9e2\xf0\xde\xdc\xfc\xb6\xcd\xbcag l\xbf\xe5\xa9\x8b\xb6\xf4}\x18j\xb1\x01\xd2\x92\xb0g\xc1s\xd8\xde\xe64={\x1e@*\xe8y\xe1\xb3Qr\x89\xcaT\x87\x1dh\xba\x19\xd4\xb5\x83\xf1\xc9A\xe0{E\xfaq\xb5b\xd9I\x943\x97\x15'}Hv\x02\x0eqA\xaf\x06\xb0C\xd8\x1c\x8bh\x97\x94\xaf\x7f\x81>_\"%\xc6!\xec\x14\xf0\x12R \xcb\x14\xb6\xd1h\x0b]\x81\x12Y\x90r|\x0c\xca\x8f\x12\xd8>\x844\x10\xe0\xe6\x1f'\xf2\xe3\x04v\xf8\xef\x97/1v7\xff\xe3\xd0\xcczU.h\\.U\x8aK\x95\xc1\x0bH\x9f\x07\x10\x8f2\xb4\xa5\x19e|$\xf4a\x17\xb7\xac\x92\xb9D|.\xc2\xc2\xd5\xf7F\x7f\xfe\xf3z\xb7\xdf\x9f\xfe\xf9\xcf\xeb\xe9\xd3~\x7f\x87\xff?\x9b\xcd\xfe\xfc\xe7u\x7fO\xfc\xec\xef\x1d\xf0\x9f3\xb6\x8b?glw\x86\xdfL\xf1\xe7n\x7f&J\xfbL\xfc7\xbb\xdc\xdc`W\xce#\xe9\x15,/\xdaM\xcf\xbabG\x08\x19\x85 \xa9\x03A\xe2\x86\xbdD\xac\x1a\xdee\xc6\x12\x03\xf8\nmo\xa7\x97\xb8v)\xbc\x80\xf8y h\x9e\xcfw\xd7(\xbdD\x0f0\xc76\xdb\x90\xb8U\xdbl\xf0\x9420\xae\x84\xf1J\xcdA\xc6\xd7\x8fI\"\xe3\xd6\xb3\xa0\xe1\x9a4\x04)\x9c\xf6\"\x05\xad\"H\x89[\x83\xa4M\x84US-\x99,ZQ-v\xde\x11(\xdeLXldhx5\xea\x13\xa6\xcf\xa0\xd6[\x04*\xb7\xc5{<\x0f\xb9\xec\xe5\xa7\xd5A\x17c\x1eHs\" \xc7)r`\xd7\x07`\xd7,q]e\x00\x88{9o\x14/\xb4\xbe|A'\xc1\xdaG_i\x94)\xbfO\xd8\xad\x1f\xf7N\xf0\x17\x97\xe38\x0bo\xe0\x13\x7fT\x15\xcc\x8e\xa0\xef\x9ax3\x94\xb3ng\x05\xfbd\x19\xf5\xc6\xba\x04}\x9c\xdf%\x13%,\x9b\x82tM\xd6vUZ\xeb\x95~\xcf\x12\x116\xc0U;\xd7k\xbf\xcf\xd2\xcfw\x97\x8e\xab\xf7\x16\xf9\x18\xad\xff\xdb\xc4\xe1\xcc\xe5F\x81\\\x0c:\x95\xe2_\xeb\xf2\xaf\xb8\xfc\xab\xcd\xc8\x86\xa2\xdd\xb6\xd6\xa1\xc52\xb8y\x92\xa5i\x17\xb5\x01\xdd\xeax\x0d\x11m\xff'\xfe\xb4d\x86jmY\xf8\x8fm\xd2\xecWj\x11\xf4\xd4\x10\x1b\xa2\xfa\xa0\x1f\xf8\x89\x7f\xb0\xff$\xd8\x88{ih\xd0\xdc%b\xf3\xec?i92\xcbKo\x19\xfa\xc8q\x80\nv\x15\xad\x0c\x95.\x06\x8a\x92h\xab\xa2-\xe53\xb4\x95\xfa\x89\xf0kV\xf4\x1c#\x02&h\xae\xaa\xf7\xc7x\x97m\xa7r\xc3\xacim\xdc\xee3\xda0\xe4\xc0\xca2\x14\xa1\xb1n\xed\x15\xa7\x07\xbbm\xd8\xae\xd8\x80<\x84E\x08\x13\x8a\x19@g\x02\xf8\x9e\x0c \xaf1\x8cv\xa9\xc8\xa8Dq\x07x\x1f\xc6\x019E \xfb3@\x1f\xdd\x97\xb0j&%\xc2\x8f\x9a\x9f0\x94nm\xce[\x11\xc5\x9a\xe85\xc7%\xb6\xdb\xbaq\xf08Kq\x87f\xbd\xbf\x96`\xe0\x12\x17?\xb63B\xf4\x04\xc5\xf9\xa0\xbb\xb8\xa0N\"!k!dE\xce\xfb\xdc\xc0\x0bX=w\x1d\xe5\x98\xa7\x96\x8c\xef\x02\xd2)\xba\x18\xdd\x10we\x1c\x00y\x80M\x8c\xf9\ns)\xd9\xbf\n\xe1\x0eC\x1d\x15\x88\xa1\x13\xcc\xca\xe8\x8b8F7\"\x9d\x13\x7fK\xb7\xa6\x99r\x8c]*\x1f^o\x1c`\xea\x9a8Y;\x92\x0c.\x0d\xcb:\xfd\xb9\xcaX\xf4\xc9*\xb1I!:\xa77\x8db\x0b\xa5\xf1V]V\xed\x93\xd8\xbf\xc6j\x9cA\xbd\x13\x9a\x1a\xbe\xfb\x17\xd2\xcdTl\x8bIP\xe1\xd2\xb50\x06p&\xbdl\xea\xb1 \n\xe0\x84\x04\x90\xd0\xf8*\xe2\xa7\xc4\x18+\x86/\xd0\x15\xee\xa3\x85\\\xdar\xe0\x8e\xe1|\xeb\x82\x90\x87\xc8\xa4'<\xcaQCZ\xfe(\xeaN\xe9\xf8\xd7\xbd\x84\x95o\x92\xf35\xc9\x9e\xc4\xac\x9a\x98\xefT\xcc\x97\x84\xa9e>N2\xbf\xf7$\xe8}\x8c\x93\xe2)\x8a\xb1\x0fr^\xee>\xa3B\x80r\xb1\x87\xbe\xc79\xd8\xbf\xaf\xe8)\xe2\xa5~\x93/\xddSz\xac\xbb\xedcr\xeb2b\xa1\xa5q(g\xf8l\x8e0\xf4_\xe6\xc7!$\x1dp\xa4D8x\xfc8\xf03\xc7\xd6M7\xebc\xd0\xa7\xa3RqN\xcd\xbf\n!'&v\x0d\x870\xf2X\x96\xa5\x99\x17\x827Y\x08\x7f5o\xca\xf2\"K\xef0\xb0N\xb4\x16\xef2\x96\xaf\x97\xcc\xbbt\xb9\x08\xdd9\x11&\x06y\x1b\xc3a\x88\xde\xe0ROf\xce\x154\x1aU\xe8F\x86\xb1]\x0f\xbd\xc9\xc5\xed\xd3\xdbt\xca\x9b\xdc\xdab\xda\x0b\x19Z\xd9\xb7\xeb\x99o\xbe|\xc1O3\xb9\x7f\xce\xca\x12\xc7\x1d\xa40r\x98\xc7\xd7\xf3\x9f\xa3\x82eo\xa3\xec\x93\xbd& id\xd5\xeeO\xed\x1f\xac\x89\xd1\x1d\xc1\xe0\x00\x8608\xd8{\xba\xef\x80Bm(\xfc,\xe0S\x12'\xa42\xa5\x10\xb0\x88\xaa\x82(\x90\xd9c\xd6!\xdd\x08\xc6\xfb\x9d-\xd24\xf3\xedr\x15\x96@\x08\x8a \\\xeeo\xca\x84\xed\x18\xe4R\xcb\xd8\x1e\x8b<\xe9\x9c\x8f\xd5_\x9d\xa4k\xf4\xa5W\xf5f\x8b\xf4V\xa4\x1a\xd7j\xb2D\xa4\xc8/\xf3\xb5\xb3d*\xe8W\xed-\x87\xb2\xf8\xb6|\x85.>\xc2\x9d\x05\x7f'\x8cM\x15\x91\xac5(Z\xa3\x8a\xd4\xda\x89 \x8aF\xfbbC\x9cO\xe6l\xba^\xd4G#\xf7\x8f\xf9\x12-\xe9N\x93I*\x87\xca\xacw\\\xae^\x17\xb3\xa7*\xe3|t\x1b\xc5\xc5\xab,\x8a\x13\x0dNr\xaeo\xd3\x8c\xd5\xdb\x9f\xa4S\x96\x99\xe0+{\x13oY\xf5\x8a\xa3\xc4\x1c/\xb2\xe6\x92\x82<\x0bzBE\xf1J\xb4\x15\xd8M\xb3[\x98\xfbU#\x81\xdd\x8fVX\xc3W\x97\xe7\xd7\x95\xdb\xf3\xcb\xa4\x1c[\x88\x8b:e\xb8\xaa8\x08>\xb4+\xd2\x95\x0dG8\xce\x8c\x03\x92\xd7\x17DK\x04\xa9\xa8\xad\xb8\n\xf1 \x14\"4\x03\xcc\xebV4\x06\xdb/w|\x10\xba\xd8f\x89\x1b\xda\x87\xea\xcdaU\x1a`\x14\nW\xdcx\x07 \xc7\xd5m\\\x16B\xeab\xe9%\x17\xc1\x0c\x88\xd8`\xabL\xcd\xe1\x08\xfc\xc8\xd8c\x9d\xf8\x04\xd4\x8d\x8b=\xac\xd6\xc9\xee\xa7\xaa(\xf1\xcc\xd5\x1ah\x9c{Y\x99\xb7\xde\xe4b\"\x94\x01\x8a*!\xd4%\xddRy\xd3\xc2*\xb1\xd06o\xb8N}aX\xb1\x91d'\xf6\xed\n\xa0\xb9xI\xb9\xfa!\x9c\x93\x97\xf7\x1ct\x11\x86.\xf2\x91f#\xbew\x82+B\x81\x9es&\xa2\xe4,zq.\xd8'?\x13\xce\x07\xfa\xb6A\xcd%e\xbb\nztn\xa5*1NKa\xa8W\xf7Mz\x9d\xdcD\x8bx\nI\x9a\xec\x88f\x1f\xc9\xc3a2_'\x9f<39\x9dz\xf0\xb8wLDnk\x02n\x11F\xb0\n!F\xe1\x93\x13p\xbf\xe4bb\xcc\xc7c\x0cY\x1a\x9c\x96\xf1\x97\xfb\x1c\xa3]\xf37?&\x93\xc5qi\x16\xb3\x0bi6\xc7\x1c6\xcdv\xde\xc6\xdc\x16\xbdY\x96.i\xdc\xc0 f\xfc\x94\xd6\x8f<{\xbe\x9aC\x9e\xe0({\xeb$\x9f\xc7\xb3\xc2\x0f \x9a\x15,\x03\x96L\x81\xdd`\xf0\x8f\x00s80\xb48\x10!\xfa\x10X\x02U\xbb\xb4\x8d[F5|z\xf6\xa3h\xd2\"\x0eQyd`nK\x0em\x8c\x0bXn\xda\xdb,\x96\x97{&\xb4\xa5\x8e\xaeJ\xf5\xa5\x8fw\xc0{\xfbT\xed\x9bz\x99\x0ci\x8c\xe9\x9ej\x03\xa2\xb0\xcfT,\xb6\xad\xd5\x16\x93`\xe2$\x84\xd5\xb9 \xdc$r\xc0/L\xe6\xb0b\xba\x98\x93\x8e|\xf5\xcd\xf8\xe3\x0e\x1a\x7f\xab\xd1xj\xc0E\xc9E}\xff=\xd4\xddEp)\n\xc1\x16\x1d\xf1)\x88\xb5\x9eFE\xc4\x97\x1ac s\xa0\xf9}\xb1\xa6\x1d\x89\xa2@\xd2\x92\xa6*\xe4Kx\x1b\x14\xa5\xad\x01\xee\xfb\xef\x914\x06\xa1XT3\x10d\xed\x17\xed\x94q\xa5\x87q\xf2J\xc6\xeb\xdb\x93\x9f\xea\nc\x82\x7fP\x01\xad\xea\xaf+\xce\xcf^bB\n\xae\x8d\xc7\x89\x80\x8e\xee\xfd\xc6\xfe\xf9 \xdf\xee,\x13\x82\x06\xbf^\xc5\x88,\xd5\xdf\xf5\n\xe3u\xa2\xd7)\x7f\x19\xb5\xaa:\xad\x87\x99\x90\x06\x10;\xd6\x8b\x05G\x10+\xccw\xbdq^\xb7K\xc37\"EE\x06\xe4\xf29\xc9AVG\xf4\x04\xcfoC{Th1\xdb|\xa4kxld&7/r\x15eu\x86\x9b\xa1;\xa1 \xfb\xc2\xba\x07U\xac\x9e\xf4\n\xc3\xa0\xa9\xe3*\x1c\x1a\x126;\xfcH\x1d&r\xcf\xb5\x9e\xe4\x97/_\xc2\xa0\xf6k\xb7\xf6k\xbf\xf6\xebi\xfd\xbb\x83\x10\xd8\xf6v`:]\x83\xe0\xb6\x03T>\xbd\xa8q\x17\x0c\xe7\xab\xa0\xa9\xcf\xbc\xb04\x06\xfd\x10\xfa\x1dc\xdb\x9c\xd3PPW*\xed\xc2\x97\xdd;\x97\xf3-e\x05\xc7\xfa\xa9\xef\xf1\xd7\xea\x9d\x17V\x8b\x1eP\xdfH\x9d\x88\xe2\x04\xd2*\xf5\xc6 \xba\xa3\x0d\xe1\xa4f\xe6\x02\x0d\xf3<\xa1\xe7)\x87\x04j\x92\x9e\xc8\xb0\x80\x0c\x87\xfe\xee\xc2N\xea@\xf7\xf3\xc9}\x82\xd4\xf4!\xc8\x82\x9b\x1a\x92~\xa8O\xf2X\x10\xd6\x8e\x13\xbb\xca!\x864\"\x01\x0bXV\x9c\x16\x17\x10\xce\x9c\xab\\\xeaK8x\x8bx\xf2\x89\x1ag\xa7>\xde\xb7\xaf\xb0\xc2v\xa1y\xa3zB|w(\xe6,eZ\x85\x90\xa8\xd9\x96\xe8\x18\x82\xb9d\xdarn6\xa5\x8bo%\x02\x88bS\xdf\xe3\xe3\xa9m\xeb\xe7\xf5AJ\x0b\x01\xa5|\xf2\x83\xe7\x86\xc0\xe3\x1a\xe1\xdb\xb6C\xc88z\x8eDWH\x1d-F\xa9{\xaf\xe3\x98\xdeu\x13I\xfaB\xfbU\xb9\xb0\x08\x07\x16\x0c7D\xe2\x15_$\x91\x93\xa4\x16^\x8a\xb8g\x92%;\xa6\xf4\xa0\xff\xd2\x15:\x99\xd8\x93\xcd\x1a\x02)Mx\xe2\xecd\x9a\x91$\x9f\xef\xc0\xb4\x95\x02\x0d\x01 \xa5\x0dM 1\x8a\x00\x8d\x9er\xfd\xa4r\x832\n(\xa9\x9b\xd0\xfeZ\x9al\x0d\xc3\x0f-\x99\xee\xcb\x17\xa5f\xa8n\xac\xe5\x8c\x87`\x89\xef\xa2\x9d\xb0\xfc$l\xd4\x01\xbd\x16\x97\xc40\x84s\x95q\x81\x13D\xd7<%\x81>T*\xa8@k-p0\xfe\xdf\x7f\xafzq\xb5\x8d|\xb2\x0c\xd0Q\x03\x8d\x13}\xa6\xbe\xc7\xebUJ\x82\x10C|\x18Q\xae\x04\xe4\xaa\x93\xc6\x96\x97q\xfcS\xe5\xf6\x00\x0b\x96\xe7P\xcc\xa3\x04ny\x8de\x94}\xf2\xc4\xb8P\xb9\xaa\xc0\x86\xcd*\xd1\xeeH\xad\x05\xff\x91\xe2\x95\x19\xde!\xa4b\xe1\x91\xbf\x93R\xf94\xc5\x01{A\xa8}_S\xa9HM\x91\x05@J\xa3T\xd38\x9aJ\xb5@or\x10\x1a\x82\xb0X\xc1\x04WP\xae\x8aX\xdaL\x1e\xf1}8*\x05\xbc\xa1<\"\x8f\x1cz-\xfe\x7f?\xd0u\x7f;\xa8\xec$gQ\x02\xd01\xa3\xa4\xdaJ\x9a\xc2C\xe2\x8f\x1a*\xea\xc6\xcbk\x94\xda]\x14?\xb0\xea\xa7\x9b\xa1 \x1ew\"(Z\xc3\xc4\x85\xa6\x80x\x00q\x8e\x81s\xe3\xe5JdH`6\x1d6n b\xcc2\xd2\xca\x8c\x96\x82\xd6\xf7B\xb8#\x8b\xa7Y\x14'^\x083\xb2T\xed\xcf%Y*g\x17\xc2\"\x109S\x8d\x8f\x13N\xaa'\x0deWd\x99\xa467AX\xc6\xbd\xde\x8au-!^\xeb\x8fo\xb3\xb8\xa8]\xbcn\x99/\x91\x08\x96\x9f\xcc\xa88\xb9_\x1b\xd6w\xe2\xbc\x8a\xc6\xb5E\xceP\x18\xeeM;\xc5\xb2\x8e\xeb\x06#\x1a\xef\x8b\x04\xf2\x8c\xab\x8cQ9^\\X\x17\"\xea!|\xeb\xc9X\xc6\x02\xc6\xd5.\xa0A\xac\xb20Pes 24\x00\xd4\xb2!8O\x05\xc4$1\xc1P\xb6\x14*j\xc5Jk\x1c\x8e\xbeBt\x91\xd1@k\xe4\x12\x1d&%qW\xa1\x0ej\x15^\xc2\x80W\xda\x11\xcd\xbe\xf3+\xfa/x\xcc\xad\x95b\xa2f\xd1\"g\x80\xddB\xc6\xf2U\x9a\xe4,\x04ek\x9e\x98\x17\xb0\xb5%n(\xdd\xde\x96\x93\xeb\x8bl\xca\xbc\xbdMw\xe3\xb2\x05\x88\x8aT\x15A\x08W~+5\x13\x08'\x10L\xbc\x17\xe7\x82\xc1\x98\x10\x11!\x9a\x06y\xed\xdcV-\x84\xf9\x8a\xa4 \xee\x8e\xee\x9ai\x93l\xbb\xf5\xb8\xd8\xb4\xdb\xab\xa6n\xab\xc3.\xe9\x89\xbf\xbb\x9d\xfdJ\x9e\x15;\xb1$\xfed7]o\x07\x00\xac`n\xba\xb1\xef*c+\x96L\x15P*/=\xb3D\xe4\x98iP\xa1\xf7\xc6h\xc2\x97\x0b\xe4\x91?F\xc5%\x1cA\xe4\xeb/\x02\xb4\xe3\xab~\xd7-\xb2j\x9f\x1e\xc2( k\xaf.\xb1\x8a\xf0\\J\x1c\x04OCeu`\x8b\x03\xa5\xce\x1f\x88w\x06W \x90^\x9e3|3\xc7%\xa1\x95w{\xc8\x8aU7r\x89\xbc\xcd\xf3\x03\xebR\xdf2\x82\xb1\x18\xf3&\x9d\xd5F*\x03\xf7\xdaWL\xd4\x90Jz\xc1\x1f\xc2\xc9%\xd6b9\xeb\x1c\xbdR\x11\xce\xe3\x9c\xfeh\xe0\xfe\x88U\xcc\xa5,\x87#lIXq(\x89Q\x96\xe1Qi8f\xd8^\x19\xfa)8\x90\xd6\xf0j\x11KvA\x18\x13%R\x92%p\x18\x9d\xfd\x9c\xfcB\xe9\xf0#\x0f\x0b'\xa8S\xa8\xcf\x9c\xde,\x9b\xce\x8an\xa5\x163\xb4\xff\x1cb\x0c\x15\n\xf1\xf6v\x00\xd9(\xbet\xc1\xa0Qak\x19\x0e\x01I\xa6nd\x9c\xc3w~Q\x9d\x9f\x0d:8D\x89H[l\xf9\x99\xca\xd9\x13\x850\x08\x0c@\xec\xa0\xe4cc\x93d~\x14\x08\xe5_\xa3\xfe\xa5\xb6{]\x0b\xdf\xb49S\xeb\xc6\xb5Ib\xcek_Vn\x10\xd2p\x83\xc60A\xd1\x05g\x12\x94\x82\x98\xdb\x00\xadT=(\x02C\xf0l*FRe\xb3\xa2\xdao\xc1\xe5.B=\xe0]Q]\x89\x9c\x11.G|\xe7R\xef\xc5\x85\x88\xa5\xc9\xc9\x1c\x0eM\x99\xa6\xec\xca4}\xcey\xa9<\xd4\x04\x853\xb9\xa6\x9b\x1c\xabM\xeb\x1fM\xcb\x93\x0e\x0e\x0d\xcc\x08\x0dU1\xdav\xb4\x98\x19\xde\xc8@\xfb\x9d\x00]\x9e\xb9\xc6QS\x9d2\xcc`\xf7[1\x15\xa4YJ\xdd\xd0D\x19\x1fY\xe6'\xf5\x1b\x88\xf7\xa4\x01\x12\xe0\xd9*\xd1<\x08(;CC\x0f\xc5\xb9\xdb6@U\xaaV\xbe\x8b\x04\x87\x0dr\xb2B\xc7\xd1\xb0E\x82\xb0\xe3>\xc2\x83\x1b\x99w\x87\x05e\xfd\x1c\xd1\x14s\xf2\xab\x0e\xd3\xbd\xcd\xa2\xd5F\xa7\xbb\xfb8\xef|\xf6g\x8e#\xa2<\x1eR\x8c\xc7\x83\x0c\xa5\x10\xa7[\xc5^NN\xa6\xbe\xc7g\xb3bS\x90\xc2}R\xf7\x97P\xba\xf8f\xc9\x99 \xcb\x87nnP\xf2\xec\xd6\xaf\x0f\\Z3p^c\x16\x9a\xa9\xb6\x8d\xbc\xa5&A\xf2\xd6%,HW4\xfe\xe8\x90P\xc2i\x0d\x14~Z\x9b\xa3\x90SS\x8e.[\x89\xe17R*\x95QS\xafY\xef\xa7B\xa4\xf7\xcd\x0f\xb0\x9e\xb2JQb?\xce/\x0d\x04\xd1U\xba\xf1R\x90\xa4\xb6l\x806\x93\xba\xcf\xd4<\xceG\xe9%\xd4c7kR\x81,\xf4UE\x0d\xa9\xdb\x1c\xee[\xd1K\xab\xcb8\xf3/B%3=\x85F\xc7\xf5\xfe\xca\xe1\xdc\x80\xfa\x1agt]^1\"\x83\x84Hp=\x8a/\xb5\x9d\xde\xbb\x8a\x93\xa9\xa4n\xbc\xa8\xc1#\xa7\xd0\xbd)\xdb!\xa3\xa1\xd0X\xde\x1f\x16\x81\xf2\xfe\xce\x14\xe7Z\x89\x11\xf6Di\xda\xd3\xc5\xddD\x91\x90\x9ao7\xe9z\xc2\x92\xf5\x92e\xbc.\x97\x13lj\xb3\x91k\nEak\x17G\xf6\x1c\xeb\xb3C\xbf\x8f\xf1,K\x97\xfcT\x86Cx\xfb]UV\xcf\xac\x10b\n\x1eG\x82\x05C0\xae\xe5j\xb0\xe3Mti\xa2-\x1b\x90\x88\x99Q\x16\x94\n\x83\x94<\xaa\x1b\xb4,_\xc9Q\xd7?\x97~,\x1d\x0c\x8f\xee}\xd7\x03m~D\xee\xd0\x02\xe23K;M\xbc\xaeZsn:\xf4\xb2\x8e\x84\x9f\xde\x11:\xe1\x94\xd6\x9b\x1b\xf4\x83p\xae\xb1\xb3%\xd3\x93*yA9Y\x08s\x9d{\xba6i\x17\xa7\xd6\xc0\xfcF\x08\xd4?\x96\xaf\xfd\xf2\x04 ;h\xb8\xb7\xe4=\xce\x11\xe7\xcb\xf5 &bv 5(\xf3e\x1dV8(\xbc~E\xd0\x92\xfa,\x87\x9cU\xfbYzd\xb5\x10\x93{\xc3}@\xf3w\x99\x1d~\xc1\xf2\xa1\x996\xb6`\x84u\xf8\x96\xe5\x1d\x90\xdf\x12#\xb0\xca\xcd)\xd4+\x08]Vs\x1b\xc6\xa2\x9aNU\x06\xf9\xe9\x9ca\x87\x0c\xc8\x96\x95\xa1g\xaa\xfbvDd\xafL>\xabG\xcf\xca\xd9B\x04\xb5\xe4\xff\x7f\xf9\x02\xb7q2Mom\xfa\x92\xd2\xe1\xef\x91\x93p93\xd1Y.\xa0\xc4\xb4xZ\xf9N\xf5\xc6h\x89\xfd#\xd2K\x07x\xf0\xcb^\xce\x8a\x8bx\xc9\xd2u\xd1Q\xccI\xd8-\xc4~*N\xb0\xeak\x8c\x87P1@!\xe0\x00d\xa1\xa5\xb7\xc0~_'\x05\xcbn\xa2\xc5=;V\x9f\xd3=\xabR\xa2k}d\xa8\x80\xa9}\xd0*\xffH.\x1f5\xb1\xbe\xd5|\\S\x97fl\x86\xb6\x91\xba\xec=3\xe6k|\x84\xed\xb6\x81\xa4\xb6\xc6\x02\"YX\xe2\x011g\x96d\xe9b\xd1EA\xa4C\xc7g\xbc\xb9\x05\x93?_OQ\xfc\xd0_\xd9\xf8\xc5{['D\x7f\x0f\xd2\x99i\x0e\xc7{\x1b#\x9c\x8f'E|#\xb4\xaf\x91\xfa\xf3[:\xa7/\x08\xe5M\xaaV\xd5\xaeW\xc0\xcbC\x99S\xc9l\x15\x0e\xa1\xda2~+/\xcaz\xe34Q\x93\x17\x97\x12\xe5o\xea\xb6\x87p\xb9\n1\xa4\xd5n\xa0\xf6\xdcr\xc9\xa6\xb1\x08\xce\xd2N\xc2\xea_Ta+*Rh\xd5\xe08X\xb2.za\xb9\xf36\x1c\x82\xf1\x0d9\x08\xbbNm\x18\xf5\xe2\xea|\xe8\x94\xe0lc\xe6\xd9\x11S-Eeb\x9c\xebq\x88\x9a\xf1SY$\xe1\x9d\x82\xe7\xc16\x17\x82q\xbeE\xfa&\xbd\x15 \xc9|\xa7\xfd7\x1a\x11ys\xf6\xd9\xa3\x8d{D9FBj\xa9\xb0\xd3\\#\xca'q\xdcX\xe3*N\xa2\xec\xae\xb9J\x94\xb3\x83\xfd\xe6\x91L\xf2\xdd\xb6\n;-5\x8a\xd9\xe0`\xc1\xda\xea\xec\xb4V\xca\xa2[G9h\x1e\xda\xfd{\xda\\\x95\x1e\xde\xf6\x16\xaf\xefnG6,\x8a\x931\x08\x95B.\xdc \xac\xab'\xb8\"\x81\xed\x0c\xbc\xba\x90\x92S\x11x\xd6r\x11T<\x7f\x1e\x94\x03s\xb6\x0c]p\x17:\xe1\xafz:\x0c\x12\xba\xa0!tBE\xe8\x88\x8e\xd0\x15%\xd5\xa3M\x03k\xb7\xcdd\x11\x15q2h\xed\xbdq\xf7\xaaG\xf5-\xdbl\xeb\xbaq\xbbC'\xd2\x02\x1dh\x9cz\x94\xba\xae\xc1\xe8\xa9mO\x82r\xb1h\x0e\xb2\xa5\x1eN\xb3}I\xb4\xeb\xf4ZD\xa3\xd0R\xd8\xea\x0f\xa5#\xa4n&\x1d\xd1{\xc5\xe5b\xed\x989<\x94\xd1\nE\x120\xdb+\xc4\xfb\x98|J\xd2\xdb\x04\x14\x15\x18\x82\x18\xb6[{\x88V{uJT\x05v(#\xd3Q,W\x07\xb4\xc7F\n\xf6\x99C)/\xdb\xe4\xac\xd3B\x80\x8e\x88\xd1\x08n#\xd7VR\x81\x1d\xcc\xe2\xc5\xe2M\x84z\xba\xf5\xfd{i\xc4j}^\x93\xda\xbcf\xa2\xc7\xbd\x8dzlDX]\x89),\xc0\x0ea\x15\"\xe7\xe4k\x1d\x9b\x92B\xed\x17\xd6[Dy\xf1\x8e\xa1\xa0\xadB#\xf2W\x17i\x81\x92\x92\xfe\xeed\x1e \x9f:\xdd\x1f\xb0\xa6\x0d,\xff,\xcf\xaa\xc8&\xf3\xa5\xa9\xc5\x8bC\x18\xec>QIb\xe0\xe5Kx\x0c\x87\x87p #B\xe3\x9b}\xfef\xb0\x0fG\xb0\xa7^\xed\xf1W{}8\x82}\xf5\xea\x80\xbf\xda\x85#\xd8\x19\xc0\x10vv\x1b\x87\xb4v\x1c\x9fJ\x1bXM\x7f\xa7\x0e\"[\xca\xdf\xc4\x05\x1a-Ov\x9f\xf2\xbd\xec\x0f\x9e\xed\xc2\xf7\x98\x14<\xd0\xac\x99\xeaK\xe1\xfd\xdf\xff\xd7\xff\xe9\xa0\xb2\xe8cTU\x97\x16\x83\x9ak\xd8\xa0\xe9h\xa5\x062p\x0dd\xd08\x10\xa0\x06\xb3k\x0c\x06\x7f\x9b\x1d\xee\xba:\xdc\x95\x1dv&\x9e\x85T\x88>\xa7\x90L\x93$\x12t\xb0\x1f\x1aX\xffB\xf36\xc3x^\xe8\x97YCy\\V}\x1f\xf0\x0f\x03c_\x94\x89\x0d\xeb\xfcVho*\x11\x17\xac\xa9\xa32\xc2\x99\xbe\x9f\xcb\x11\xefh!\xd0\x9a\xf7^N\xaa\x00\xf8z\x95\xd9T8\x8a\x07\xf0\xaf\xb0\xcb7P\xbfI)_\xa5n\xf4K\xf2\xee\xb6#i\x0e\x04\x80\xd7\x91\x93y\x94\x9d\xa4Sv\\\xf8\x9a\x0f\xac\x199Z=\x18b\x9f\x8b\xdd\x8f\x1f\xef>;\x004\xcc\x7fq\x08\x8f\x0f\xf6\x06\xcfj&_\x06.Y\x04m\xdfX\xb8Q_\xa4-\xd6 \xb2{i\xd6\x19Xu\x06\x97!$\x95\xa3\xfa\xce\xe0\xfeF\x1e\x14\xde\x9a3\x19\x103\xd9m\x9f \x1f\xa5c\xe1*4C\xa87\"\xd2\xc2M1\xeb7\xe2G\xda\x81$n?\xa8\x9c\xec\xf5\x8d\xd4r\x11\xe4&\xc7\x0d\xdc\xcb\xb6ksj\x10\xe8\xdb\x01\xc1\xc8\x95h\x84\xcc\x84\xdcbj\xfc\xd66\xdb#\x89T_z\x9b\x1c\xd5\xd6J\xb2\x1a\xd2\xf1\xcc71b\x0fv !\xb0bOY\xa4%j5\x1a\xf1\xa3\xd6\xf47\xed\x87 t\x0c\xbf\x86iI\x0b\xcd\x9a=\x1c\xaa\x91[\xe9\xa8\x11;\xcaA\xf7C\x04\xb0\x81\xa9\xc3\x16lX\xb9\x99\x1d\xc7\xf9\xd0\x0c\x8ci\x03\xf3\xd4\x06\x0b\xada\xf5WQ\x8f\xe7\x06\x87\x10\xd75\xd3\x8a\x91t\x0b\xff\x95\xcdmy\x06\x95\x82\xa1\x01~\\\xb6\xd0t|\xee\xb4\xff\xe3*\xef%\xfab\x96\xac\x99b\xe2\x85\x9c\xe3\xe8\x18t\x03%\xd5Mhs\xbb\xf5\xbd/\xec\x14\xd1\xe5\x9bD\xa3\x04c\x92V\x00\xd71\x89\xf3\xfc\x9c\x10$\x81\xe2/\xeao\xf0:I[\x91:\xd4\xa5\x88\xd0xK\xf5\xc0\xf8\x8f\x1cV\x1d\x9d\xebc\x92RL\xe3]\xc2\x8d\x99\x17\xbd\x81\x01\xae\xec\x93+\x8aAs\x0e\x19\xbc\xe0M(\xd2hW\xba\x91\xd9\x03\"\xbf\x18e\x97\x0e\xfe#E\x0d}\xd9L\x8a\x8e\xbcB_\xaf\xa1@\x8aG_\x08)\xdd\xc8\xce\x0e\x0e\x86\xaf\xde\xce\xae\x10\xb3\x9b\x06\x86\x8c\x956\xb2\xa0\xf3\x18v\x7f\xfd1\xc8\xb60\xf8\xce\xa1\xca\xd2Y\x1f\xd5\x1e=*\xd5y}\xfb\xb8M\x8bQOhly\x9b*\x96\x01\xfb\x8d\xaf\xad\xf3-\xb1\xa9\x8c\x1e\xa0\x01v\xc0O,\xcaMn\x0c\x9a\x05\xef\x0b\xcfijh\xf5|a\xf5\x0d\xa3\xa9\x17\x9a\xa9g};\xbe \x08\xa9C4h\xe4\x85\x1eT@\xa9C\xeb\xde\xc3\xd1\xc4\x98\xfa\xa45 \xc68\xa5\xeeu5\xa3\x9b\x1ei9Nn\xb4\\Pt\xa63LcS\x164\xa9\xd7\x11\x87\x11\x04\xb5\x84*\xf5\xb4 \xb1\x9d\x01\xabfu_Zc\x14Y\x94\xe4\xb34[\ns\x0c\xca3\x06C\x83_\xa8z\x1dl\xa7\xc0d\x9b\x8d^h\xa9*\xe9\x95\xb5\x9a]9*\xb1\x0d\x0f\x9c\xc9\x95[J\xdb\xca\xea\xf2\x983v\x80\xe068\x84\xae\xa2\xc9'\x15\xaaf\xb9^\x14\xf1j\xc1\xa0\x88\x97,w\x86\xbcW\x03\x99\xaf\x93O\xa5\x9bJ9\xba\xea\x8d\xcc\xfaW\x94W\x852ut\x88Y\xf8\xdc\x93M\xbb\xda\xc5\xf3'5Lw\xfc\xd4\x8al\xaeLd\xe1\x05\xa4D\xe0\x8d\xaa+\xdf,\xb6z\xfcZ\x99\x81Ri\x04\x19\x9bj\x88C\x99I\xeakN\xd7\x90`\x14\xf1.\\\xc5\x1c\xf4\x8d5*u3\xafT?/h\xfb%\xc2\x13\x83\xaa\xa6E\xf3h\xcc-RNT3y\xaa\xde\x1d\xea5\xdc\xa9Ff\x8bu>\xd7\x1a\x10\xbf\x0fU\x89\xb2\xbaG\x9b\xedU\xc6J_\xbd\xa8M1J\xf1S\xca\x1d\xa3\x8eg\xe4\xc8\xf4\xd1\x1c\xe9\xbfj\x99\xd3Hnl]\x12\xd7\xfa\xa2p.r-\xc9U\xb5\x7f\x9a\xe7\xb1v\xb1}\xb5\xab\x14\xc2\x88\xd4\xe6\x12j\x99GY\x15\xee\xde\x8a\x14\xa0\x0eL\xeb\xa2\xe3$Z,\xf86\xac\x16y\x9a&\x0cn\xe7,\x81\xdb2\xa9\xd2\xd6!\xf4\xcd\\\x86B\x8bi\x10\xcd\x1au\xdc\xb0\xbb\xbc\x88\x17\x8b\xdaV3\xbb,!C\xb8\x03TB[j\xa5V\x0b\xb5w~,\xd8\x95x\xc3\xe0\xee:\x816']\xa3 \xa5\xdfS\xbd}\xcb\x9d\xac\x1ay}0\xb5\xfd\xd6&)X\x00\xae\xbev\xc4\x98qvk\x8b\xb2t\x97ug\xb3\xa63\x13\x85\x13\xfd\x80\xe1P\xa9\x1dB\xac|\xa3]\xb7\x17!le\x06\"\xd1\xf2Q\xe7#\xc7\xcf\x8c5\xc2\xf3\xe5\x17:q\xbe:Al:\x174\xdf\xaa4\xc2\xb6t;)t\x88\xe25\x82\x02\xb8\x88\"\\cW0\x0c\x93\xc9\xc0\xf4-.\xcb\xd7\x1b\x0dU\x93\x15\x03\\\xf4\xea\xdc\x960!\xb6\xb7A\xdf \x89\x8e\xa9\x1at\xfe\xccd\x14\xed\xd6\x8c-\xd6l\x90Q\xf8\xc2fZ\x10Y\xe1Cn\x12w\x83\xb8\xdc\x8b\xd7\xd6\x98j3\xeb$G_\xcc#\xa9KEiv\x1aM\xe6\xf5\x8aq\x95\xdf~\x92\xb1\x1a.tK\xdf\xab\xf0*\x16D\x93\xa4\xaa\xd2\x8a\xb4\xb4\x1am\x03 \xe7\x069\x8eug\xb4iV\x10M]\x12\x99`\xbe\xc08\x80\xc0F\xc9\xa5U\xf9\xab/\xf3f\xa3\\`\xaeUX\xd34\xc2}\x97\x8b\x84g\x00\x7f\xfb\x86&5\x0c\xd0Sen\x92\xb7\x16\x89\x1d\xb9jq\xfe.z\xe7c\xfa_\xd4b\x14B\x7f\x817w\xdf\x7f/\xd5\x15;\x98\x9b!\xc5\xe8\xd6\xc32\xfc\n^ \xb5\xa7O\xef4\xc7\xba\x0b\xce\xc1\x93\xa7\x81\xcf\x87$\x916\xca\xf3\xf8:\x81!\x16=\xfbV\x9b\xc2\x10\xd2\x10\xb3\xc9\x85\xb0\x0eA\xf5h\xec\xadNv\xbd\xd6\x85\x05\x7f\xb4\xb8 Evg|E{g-B\x90Q\x00I'\xacI\x9a\xcc\xe2\xeb\xb5r\xc3\xea\xd3\xcc\x7f\xe4t\xd2js\xe2\xc2,\xd8C0\xcc\x80\xb5u\x85IT\xda\x8fU\xa7\x93\xb8\xf4Xhw\xb9\x99%Y7\x0f\xdd=\xec\xfa\x90\xab\x91\x88\xd0\x86$\x14\xc3\x8d\x13\xd4\xa35\x0cJ\xa6\xa5.\x0b\x1d!ez\x0d?\x13\xf9\xc1\x05K\x81\x9eZ\xd5*e\xfa\xad\n^\x17\xc9\xd4\xd2\x83\x83 \xc4\x8c\xa8\xa3\xcb\x10\xe2v\xaa\x1aR\x1ap\xce\xf9\xacG\xec\xb2d\xe6\xf9\x8fz\x15${\x05\xf6\xf3\x1c\xd8\xce\xce\xf3@\xb9\xb9z\x91\x07\xdb\xe0oo'A\xa5\x82\xda;0\xe5zM\x8f\xa2\xdc&|o\x96\x88\x9c\xb9XTJ\x1c>o\xb0\x90Q\xeeC\xf0\x02\xd8\xe6\xff\xfcM\xb51K\xa4\xc3\xa68;+\xc7\x81\xe7\xf0\xf5y\x9de\xec\xbcF\x04\xc5G\xf9\xc6\xb1f\xaeD\xf2 \x9eZE`\xa9\x1e\xec\xbd\xc9\x9f\xc8OB3\x01\x95\x03\xfd\x81\xba^\xfe\xfa\xad\xc4I\x88\x1cT&u\x1a\xe9\xeb\x00\xaa\xaa]\xb3\xe2\xec6Q\xd5^\xb1|\x92\xc5\xab\"5\x0c\xa8#\xd7\x07\xef\xa2\xa5\x19\xd3d\xed\xaa{~\xb7\xbcJ\x17y\x87\x93\x89\\cA\x82\xe5\xd1\x9c\xf9\x85\x89\xa7('\xea50\xca@\xe4\xe7\x81bv*\xf1\x9b\xce-G\xae4\x7fpOg\xa1H\xba\x9eQ>\xb6\xfa\xd2\x93M\xa0\xa1\x86\xfd]\x1d\x81\\\xaa\x0e\xcc\xe7\xbe\xfe\x07\x9b\x89n\xe0SJ\xe8\xb4\x9c\xfd]\xbd\x95o\xdc\x15\x8f)\xfe7\xf1\x07\xfb\xe6n\x89iO0\xce\x9e\xde\x17I\xf9\xc1Fd\xc2\xe3\xfb\xa7\xa4v\xa3\xddK\x12\x0c\x19\x92+\\!\xbd#\xc1\x87\xac\xa9\xe5HF\xd9%\xfa8)_\x8a\x08\x05\x12\xf5\x85\xb5$I\x0b\xa0\xf5>\xba1\xfcr\xe8[[R\xdb'B\x10\xd4\xd3\xc8}\xf9\xe2P\xe0![\xefR\x10\xceY\xdbh;\xa1\x05\xcdH\x15!x\xe31\xcb\xdf\xa6\xd35\x9a\x9c\x98K\x89\x8c\x8e.W\x06\"\xde<\xda}v\x81\x88\xbdX9\x17\xae\xdf/\xd6\xd7q\x92\x0f\x1d{\x8be\x99\xab\x08\xb0\xed\xe9z\xc2\xb2|\x08~\x9f\x0b\xbar\xe9\xcd\xe2E\xc1\xb2\xee\xc4\x80\xf5>\xb1\xbbs\xf6_~\xd0c7,\xd3\xc8\xb4\x13\xb4`u_\xb4d\x0bD\xa9mT4d6Q\xb2?z\xb8f\"\x16aw\xb2\xefDg\xd6[\xb2\xec\x9a\xf9N \x19\xc5T\";\xdc\x06X0\xfe\xe1O\x0f\x8d\x08\x9a\x1e\xa3\xf2 N~\x0dtH\xe8pZ\xbf\x06\x805)\xb2.\xc2\xc5B\xe5\xb6k^\x97\x89\xcb\x0f\xf3m%\x94\x0f:\x0b\xe5j2\xa6\\./e\xec\xc9\x95\xaa\x03\xc3{\xfa;\xfb/>\x83\x85uG\xc5\x19\x9b!\x18WS\x0bv\xc3\x16\xc32`|\xadl\xc9\xf2<\xba\xe6Go\xe9\xe6\x8d\xb5\x8c\x1e\xff\xbe\xa2\xb7K\xaf\xd5\xa4\xe1\xb4`\xfb\x97\xfc|\xc5&C(z\x9c\xc98W\xda$\xfc\xf5\x87\x04\xd6\x91\xb28f\xf35\xe8\xc0\xb1\xaaok\xa2\x80\xd8\xa1\xf8b\x15 \xbe\xc4l\xba\xc2G\x87\xf6\xf0\xc9\xae\xa9\xd4\x7fH\xed!Er\x08\xf7\xf8\xff\x15\xf4\x80 \x87\x8e7\xd3\x11\xd2\xe4]q\x8f\xc6\xff\xdc\xab\xfe\xdc\x0f\x02a:\xf3\xf7'_\xb4!\xa3\xeb\xc0\xe8\x80\xc67e\xb41\xc4ZI\xc7\xbd\xa0\x17'S\xf6\xf9l\xe6{\xd2\xe21\x9dA\x84g\xbd\x9f\x07\xa6\x11)\x947\xd1/a\xc7\xe9\xf6\x7fS:q\x1b] \x07ft \xa3:S\x96\xb6\x98\x05\xa1\xf0\xbd\x90\xea\x1e\xf4i\xe7z\xfb\xa1\xab\xc3>\x92\xd8\xed\x0ebB\xadqq3\xe1\x9b\x88\xd0\x90\xd7\xcdh\"\x91i\xdc*'4\xb1\xab\xe5\xef\x970\xc0\x83}\x1b\xbc4\xc3\x18)\x05\x0c!\x1b%\xb0\x0d\x83K\xa3\xea\xae\xac\x8a\xc0\x0b\xc1\xd3kj%X\x80\xbf\x9c\x03\xfc\x1a\x82\x97\xcf\xd3\xf5b\nW\x0c\"\x97Z\xc3O6\xc9$\xe0&~\xbf\xe9\xfdD\x9c\xbdEO\x1c\xfc$\xa1\xd1nu\x1dD}\xb0\xf7TCZ\x071\x0f\x91_\xfcMC\xe6\x1b(\x8dkw\xfa\x14\xf9\x11&@\x9e\xf2s\xeay\"e\xeaj\x11M\x98\x9f\xb0[\xf8\xc0\xaeO?\xaf\xfc$\x04\xef\x9aW\xf7\xbc\x80\xd2\x1b({\xa2\xdf:\x1e.\xa2\xbc@ss\x11Yr\xb1\xc0\x1fy\x19\x16\xd6@+R\xb4\x10\x98\xf6\xd8|\x1d[M\n\xa5\x8b0{U\x0cl\xd0q\xf5\xea\x80l\xd3\xb1\x94k\xae\x8b}JXU\x9a\x16cm\xaa\xa9\xd6\xc1B\x8f:n\x1aB\xd9=oG\xe3\xc8\xbf\xc5$\xe9A\x97\x9d\x90F\x1cs\xb0a\xdb\xe5\x92}\x11\xdd\xa5\xeb\xa2\xdb={)\x88\xfc\x03\xdc\xafS8\xfeP\x1c2}\xbf\xbe\xdb\xef\xbb\xef\xd7\x9fv\x16\xe5\xffW\xe0\xab\xff\xbe\xdb\xca\xc6\x99P\xaahvM\xa3\xa8HaM\xfc\xd0X\xb3& \xb4\xb0\xab\xe6\x98\xa4\xd3\xb8\n\x96hm\xaen\xe7\xa3J/\x90\x86\x90\xf7>\xbe\x7fu|q:~s\xfc\xa7\xb3\x8f\x17-\x8a\x82\xfaQ+\x88\x00\x9e\xa0R\xb9\xa7S\xc2\xc6\xde~|\xfd\xe6\xe2\xb4M\x91\\\xefM\x08\xde\x9b\xf5v\xfe\xd3\xd9\xcf-\x9dX\n\xca^>Oo\x13\x9b\x0e\xa9\xa3b]j\xed\xabO\x8ay\x9c\\\xbb\x1c\xe0\x94\x16\x1f\xdb\x95\x87T\xd5\xc8\xdf\xf8\xd8;\x1ev\x1c\x0e\x19\xe1\xd8\xd8\n\x07 \xf5\xb7g\xafN7\x06\x07\xce\x8d\x06GUi\x99N\x99c\xfa\x18\xea\xdc\x1fy\xbcJ\xee]\xaa\xfb\xab\x84\x0f5\x13\xb1C\xd0\xc6\xd9\xabO#\xfd\xad\x1c\xa5|\xd9\xce\xd7\xcbe\x94\xdd\xe1\x94o\xe7\x91\xc8\x0f\xc4\x7f\xc4\xf99_U\x11\x86}\x9de,)~D<\xd5\xdf\xb8\x98-u\xec<\xdd\xfbUO\x1d\x82\x95\x13de`Z\x97\xe5\x92\xda\xe8T\xa5\x9aS\x07\xf6\xe8Z#\x13\xda\xf2\x86\x04\xb4\xba\xb6&\xc9\x80S\xdd\xb50\xd6\xa5 {\xb4\xd6\x8brw'i\xb6\x8c\x16\xf1_\x19\xba{\x05\xd2\xfe\x1d\xfb\xd6wp\xae\xef\xe0\x00\xcb\xeb\xaf\xf9w 9\xcc\x1a\x0eu\xda\x8d\xa5\xdd\xab.\xa0\xd7SX\xe9\xa6\xb1pT\xff\xe9\x8e\x9e\xd3>kj\xef\x1a\xea\xe5\"0\xa6jo\x1bA\x94\xbaK\x06\xb6\xfc\xdb\x81\x1d\xdfBf\xc3c\xd3\xb8Hk\x18\xd2\x89\x94T\xf2\xcf\xdeAG\xd7/N\xa5\x8c\xa1\xd0jt9\xc0\x14\xf3\xe6d~\x12\x8c\xfa\x97!$\xa3\xc1%zc\xfa&EoTm\xab\xbb!\xd6\x13\xcd\xda\xc2\xa90\x14\xd7\x90#\x16\xfec\xd2\xc8Y\xa4\x0e\xac\xf7\xf8]\xfd\xaf\xce\xb0zb\xd2\x0c\xa9\x96x\x16\xf8^\\\xb0,\xc2\xa5\xb0\xc9\x9b\xe1K\xd9\x06o\xc7\x8a\x9b\xa1\xf4\xfd\xac\x87\x0dk\xc9\xc71{\xdaa\x8d\x9f\xddp\x8a\x8dsI\x8d\xb0\"\xf6\xfa\xab\xe5\x1a=\xb9\x1ce\x97f\xfe\xbdX.b\x93\xa4\x06\xaa\x1f#*Q(\xa1\xc8)NM^\xa5\x1a\x108\xb1[oA\x83 \xedx\xd3\xd9r_\xc4AB?\xe6*\x84\x93\x19oE\x913\xf3=\xbdi4\xc0\xd1R!?\xccb\x02\xa6X\x86Y\x97\xda\xa0\nMr\xb0z\xa6i\xc2\x86b\xdc\x9d\x83^\x878\xb0\x0d\xba\x8f\xa86\x98\x1f;\x08\x03\xeb\xe0\x1e\xd5\x05\xcb\x7f\x05\xfe\xe9\x97VE\xe4xk\xea^\xbe\xdb,Z\x1d+\xfdBC\xee\xe8\x7fH\x85\xc5\xde\xaf\xcb:.Paa\x99\x94\xaf\xcb\xa2\x81Y\x94\xcb\xa2\xbd\xfd\x03Z\x97AD_\xfd\xa7.\xe3\x97\xde\x97$:\xadHw\x81X\x95\xec\x99%\x91,yj\x954i),!c!\x9b\xd9\xb3\xba\x9eH\xb5\xc6\xc0x?\x93\xefwI\x84j\x08S\xfaK\xd8\xb9\xd4\xf4,\x99\xa6g\xd1\xac\x0f\xb3\x10fJ\x06?\x7f\x7fz\xd2M\xefQ\xe6G\xd0\xa2\")\x81\x1b\xa3\xe9\xa2Z\x04-Ru\xa5\x08\xe8\xa3V\n\x01\xc7`>~x\xd3m,\xb2\xb3u\xb6\xd0\xfb\"\xc4\xf6\x86\xce\xfep~\xf6n\xa3\xde\xfe\x92\xa7\xa6\xb4u\x96MY\xc6\xa6\x9a\xee%\xe8\xdc\xff\x87\xd3\xf3\xb37\x7f<}\xb5\xc1\x18P\xf8\xc9X\x9e.n\xd8\xd4\xbb|\xf8\xb1\x8c\xcf?\xfep\xf1\xe1tc\xad\x0c\xad\x8fI\x84\x13\xbd]\x98J\x13\xdab\xde\xa2\xa4Qs=__\x15\x193e>]\xad\x14\x04\x0ehd\xdd\xa1\xf0\xfe\xf8\xc3\xf1\xdb\x87\x9a:\x9f\x9d{\xe6Y\xb4|\x17- \xd0\xc4U\x85\xd7\x84\xd6o]\x15\xdb\x85y\x13\xcc1\x9cg/\xce\xff\xe7\x92\x88 7!tB\xea\xbd\xf0T\xe6\xe7\xcf\xfc$\x9d\"\xd1\xda\x8a\x05g\x0dG\xb0\x16\xaa\x88$Z2\xa17\xeby\xb0\xad\xde\xc6\x89|\xc7?\xde\x11\x05\xaa\x1d\x1f\xf3\xf7\x97_\xc4\xf61\xca\xe9\xea\x02\x8e\xc0\xc3\x19\x8d?/\x17\x1e\x0c\xe5/Z\x7f\xa0i\xf7\x18\xe6\xf3F\xeb$7\xd6dA\x08#\x0f\xa1\xc9\n\x86Wv\x93\x10f\x97A\x08yg\xac9}\xfb\xfe\xe2O\x02w\xc6\xaf\xdf\x9d\xbc\xf9x\xfe\xba\x95\xb0l\x84EoY1O\x89\x1a\x0f\x83Kq2Y\xac\xa7\xect\xb9*\xee\xfe\xc8Ak\xf3-\xc2\x1cx+.y\x1ee\xc2v\x1be\x89\xef\xfd\x1ce \x06\x1el\x02\x08L\xd0\xe4\"I\x0b\xb8f \x17^\x19D\x80c\xfb\x1f\xec\xae\x87\x16d6\n\xe4\x18\x1d\xd7\x81#\x0f\xb3\xe8c\x04@\xce\xd9g/\x84\x9c\xaf\xfd\xba}\xed\xffx\xfc\xe6uE3\xce\x7f\xbd\xe5\x8e\xf3\xb3\xe3\xf3=z\xad5\x05YGH\x04\x84\xfa\x9f0\"\xe7\xb4\xe3\xd1\xe7\xe5\xe2Q\xdc+X^\xf8\xb1\xd8\xde\x1c\x0d\xd6K\x96\x8f\xc5\x96\xa4\xbe\xe4{x\xd2\xe3\x9ca\xc4\xa1\xf3s\x8c\xf3\x8bd\xcc\x10ArB\x18\xb1\x86!6\xdfcl4]c\xb7_R\xd3\xefx\xfb1S\xd6\x8f\x1a\xed\x10m\x95\x8e\x15\x94\x01\x95K\xecV\x18\"\x8e\xb0\x9bh\x11\xf3\xc9\xbd\xe7\xad\xa3\x91\xfb\"\x84\xb4\x835\x18\x87FAR\xe4\xa2\xa2\xc8!(\x0b\x85Ks\xfe\xa4\xd1\x93\x1d\x15\xa5}\x7f\x08\x93\xfco\xdc%\xdavx(\x1cH\xdaq`t\xd9\x15\x07\xbaX\x03\x81\xc5F\xd6\xacCj\xdd\x12\xb0\xdf\x18\xf0\xe7\xa7\x17\x9c\x9b{\x7f\xf6\xee\xfc\xc1\xb8\xb8\xcc\x8c\x07\x035\x1e\xce.\xc3k\x9d\xde\xd2A\xc8\xd6\x0ef\xc3_\xa3\x13\x1d\xc2\x07\x8e\xc0\xd0\xea\xdb\xa0\x15\xd6\xd2dP,\x8e\xfcC\xd1V/!\xcf\xc6\xd2\x90_T\x92? \x9e\xaa\x88\x8au\xce\x19\x16U\xb5zS_\x9bP\x96g,_\xa5I\x8eY\x02\xb2\xa07g\xd1\x94\xa19\xd2\xba\xfc\xfb\xcb\x17K?\xc0\x17c\x824\\\xe3}\xb1\x1d\x8e*i\x08\x91\x8b\xdd_;(\xe4B\xc1\xae\xf7\xc3\"\xbd\x12\xda\x97iTDzPm\xbb\x8e?A\x8a\xed\x1aD\x08^\xc1>\x17\x9cr\x88\xd6\xf8\x112\xe9\x88\x95\xff\xf1\xf1\xf4\xbc\xedJ\x7f\x03\xa4\xfc\xaf\xcd\x902\xd6\x90\xb2U\xec\xf8\xaf5\xcb\x0b9\xe9\xd8\x05\xf9.\xa2\x05\x9f\xf9\xdb\x8f\x17\xc7\x17\xa7\xaf\xfe\x91 \xb0\\\x17Q\xc1\xa6\x1f\x1e\x0e\x10\x929<{\x7f\xfa\xe1\xf8\xe2\xf5\xd9\xbb\xf1\xdb\xd3\x8bc~B||0:\xd5$r9\xa4\"\x01\x92O\xec\x8e\x96\xa6F\xad,\x85\x83[\xeaz\x1eYN\xa0\xe5J(V\x0e\xb5\x0e\xae\xcf\xf3 \x080{dY\xbd\xd2\x0el\xfcI\xab\x90\x8d\x9f\x1eUX\xe2\xaa\xb7\xe0\x87ll\x9f\xaci\xd0M\x1b$\x98\x87\x87>\xc5\x9a\xb0\xa3qOL\xd9\x82I&C'\x87Y\x08\xe9e;\xde\xab\xc9<\xe8\xd6\x7f\x98\xb9\x94{\xbb\xe3T8-;?\xf9\xe9\xf4\xed\x83\xadI>\x993\xeat\xfe&*\x96\xf2s,\xd6\x11\xd5\x13\xfdTT,\x13\xca\x87/_\xb0\x9e\xbc\xb6\x1dR\x1fxc \x83s\xf1\xe6\xb2\x9e\x97$(\x7fv\xbe\xbf\xdd\xa3c\x99=\xdb'4\xdd\xf2\xb67_\xb1I\xccr\xaf\x8b\x1d\x00\xb9\x16!\xb2d\x99\xcf\xd0_?/\xb2\xf5\xa4H3\x12zZ*\xa8HK\x0f\x7fx\x08~\x82mD\x01\xdf\xdb\x98\xdbh\x08\xa9n+\xd0\xe9*\xe1\xa6\x16\x87\x15\xe7\xb8\xff\x8cV\xd8\xef\x99 \x91\x86\x85\xfb\x94\xce>\xf1\x07V\x948\xa9\xb1\xa7\x14\xf6\x93\xde*K',78\xdbU\xc9\xfd\x94\x89\xf6k\xe5S,\xafg\xc0\xaf\xd7\x98c\x8d\xb7\x82\x9f<\x99GI\xc2\x0c\x85\xdb\x0d\xd6x\x15\xe7\xab\xa80\xc35/1\x1di\xed\xd55\x11\x80\xee\xae\xed*\xf7F\xa67\xd8\xb6\xc3_\x83\xd4\xea\\\x1bWJ>s\xe6\xbeW\x97Z\xd7V(R\xf5\x08\xba\x82\x15B(|B\x92\xa9\xbd1\xa6s\xd5h\\\xc1\x1fu\xe1%x\xcez[\xd5\x88V|\xe7O1\xc6\xc1\xaa\xb1\xc9*G\xba\x8c\xd6\xcaQ{\xf0\x9c2lJ\xaa\xe8\xaa\x95\x11S\xb2\xbd\xed\xb8g\xbb\x1emo/[o\xda\xd7\x8e$\x1a\xf2\x06\xe8\xc7j\xe0\xa1\x15\xae:\x84\xcc_\x06!,\xbf\xd3^5\xc7\x86\xd7VG\xff\xc8\x93[\x00\x87\x90\xf8\xcf\xf6\x02\x7f\x16\xe0\xb5l#\xec\xd0\x94\xe1\"\x9e|\xf2#\xff\x0e\xe3\x94\x0ct\xfe\x0f\x86p\x83\xc6`\xbd$\xbdmm\x0dk9\x1b\xc2\xd0\xc2\xb12\x19N\xd8-\xcc\x83\x1e'{\xbb\xfct\xe2\x7f\x0czi\"\x8578\x84\xab\x10\xbb\x8b\xfc\xb8\xb7J\xf3B\xeeB$5\x03d>&\xbdh:=\xbdaI\xf1&\xce\x0b\x96\xb0\x0c\\\x01\x0b\xb5\x06P\xdb=\xe9\xc5K\xde\xe39\x86S\xcdU\xd0c\xf7\xd4&\xfa\x18|tt\xe3\x07\xca\xef\xea\xa6\x87\xf6\x88t\xa7\xa1\xab\x10\xb6\xc4\xc8y_^\x9ad,\x9a\xde\xa1\x1d\xc2d\x1e%\xd7\xcc\x838\x81\x85\xef\x89 \xaf\x1e_>\xf7\x88\xf2^\xb4Z\xb1dz2\x8f\x17S_\xfb*\xe8\xd9-\xb7\xe1p\xde\xcb\xd82\xbda\xa21\x91 \xa7\xdc\xa7\x06\xce\xd6\x16\xb5a|\xac\xb8\x88\x97,]\x17\x1aF\x84\xd0\xaf\x1f\xb8\xfa\xd1g}?\x84\x95q\x06pZ=\x84i\xd5\x04\xfe\xf5\xedq2\x1bM\xebh:\xea\x08\xc2\xcd\x9f\x9b!\xb0v\xb2\xd9\x18\xc9\xb5\xb5kBQ\x02\xb2\xeb\xb6\x8e[\xa0\xb7)\xb3\xb3\xfb\x94dvv\xfb\x8f\xef\xc3\xe2`\xb2\x10\xa4\x95\xa9_\x88|\x1b:\x9b#\xed\xedJK\x08[\xf1\x82\x91\xa2{3;\xa5\x98\xf8\x82\xf3\xc2\xa8\x05\xe3b\x92\xb4\xa4\xe5\xec\xc32\xce7\x8cs[\x8fu\xffd\xef[\x02\xda\x17\xba\xe5\xc0!l\xb9\xcc\xb9w\xfb\xbf\xa4Q\x8e>\x1eY\xa7\x8b\xa5d+\xf3\"\x9c%\x1d\xa1\xc5]\xa8\x8f\x89\xe1\xd40j\x8aw2\x9a\x13\xd8\xe3\x81\xccOC\x88\\\xb5\xa112\x85zn\xa4\xb3}1J/\xfd\x88\xd0\x10\x98\x8f\xd0\x0e\xa2\x8a\xc2Y\xb7=\x8a\xb3ztF\x9e\x0c$\xa3\x1e\xdb\xe0K=x\xeb\xb7\xeeM\xd3\xa4\xda7%`\xd5N\xf0\xf3\x00c\xfav\xd0\x80\xab'\xf3=\xce\x15\xcb\xc8\x1b\x89\x88\xd7 \xd2'\\\xb6exq\x918\xc2^\nM\xc0\xb7R_\x84\xc9\x8e\xe5\xff\x98\x0d\x87\x8b\xdb\x9b\xa1Q5\xe9\xc1>}\xca>1\xe5j\xa9R\xd83St\xca\xfc\x15\xe6\xa1,\xc4\xf0\xa7\xfd.g2\xba\x1f\xe4\xd4\xc9\xbc\x15\xa1d\xa9TP\xf5\x8dX\nb\\\x84\xdf\x19\x84(\xb2\xa3\xa7|\x8aQ\xe2\x82@Jb\xa1\x90\xdaa\x07\x06!J\xe9\xecy\x99o\x12\xc5\xbe\xed\xed\x05\xbc\x80\xc9s\xd7\x81\xc2%\xa4\xb5_\x8c\x16\x97\x0e\x82\xcc\x05w\xc2y\x81O\x01{\x995I\xc7\\\xa6_\x8d\xa6\x0e\xe9XO\xaf\xcd\xbb\xe1\xc2C\xee\xdf\x840\x0da\xc5\x99{QA\x98r\xceQ\x80\xb9\xe1\x9c\xfc\x0d\x0c!\xe6c\xc6@\x17\xfc\xcd\xe8\x92\x9f\xceT\xf8!\xebM\xe6\xaf\xb0\x83y \x00\xc6\x87\xf7\x9d\xfb\x13\xb5>\xf7E\xc2\xbd\xfdN\xbc\x1bq\x14{\xe31\x9a\xb9\x8e\xc7b\xaf\xe0\x9e\xe0\x8c\x88\xfc\xc0\x86z{V\x9cZ\x12\x19\xa2\\Z\xa1\x12V1Zb\x1a\xc3\xbf\x01\x95\xd7\xa3\x82\x0b\xf7\x1b\x9a\xb5k\xf4\xc9\xe4\xc5\xd261\xab9\x10\x16C\x95\x9c0\xc4\x0d\xc1\xab\x9b\xe2\xb6\xc5\x8f\xc10\x94\\&E\xb3\x07B\x06p\x9b\xf7\x7f\xf5\x1d\x8b\x9dv\x81\xc7/lN\x1cBQ7\xa1\xc8Q\x17\xcd>\xb3\xc9\xba`\xf2N\x0b_ \xfb\x81?\xe4ir\xbeb\x13\xed\x95\xfc\xe9\nJ\x11\xfb\x89\xbfO\x862\xe7%\x83=\x87\xa3<\x91\xecX\xad\xc5/c\x0b\\\x9bL\xa3\x0cU\xa9\xec\xf3\x15\x9bH\x07\x05R\x1aj\xc4VfX\xf6TL{(L\xd1rv\x91rx\xcbz\x89^\xc55\xa1\x90Z\xa9_c655\xa1\xa9\x1b\x0c+\xc71\x14 #\xcc\xe5\x04\x11\xbc\x80\xe29D\xdb\xdb\x01\xc4\xa3\xe8\xb2\x96&$\"\x0e\x08\x13d1\x82*N\x14\x06\x7f\xa8_\xcf\x9dD\x939\xa3\\\x8c\x94\xd4\x11\x8f\xfa\x0e\x07\xa5\xdc\x0eP\xbf\x0e\xab;\xce\x80\xb2K\xe0\x8f_\x8f\xb9I\xe5\xacq\xf2\xe9F\x7f9\x1a{\x05\xbd\x7f\xc9\xd8\x8c\xa3<\xdeb\xf3\xedh\xcc\xd2W\xa3\n\x81]n\xc2\x80\x87\xd4F\x7fh\\!\xcd\xb8\x94\x0c\xda[\xa4\xd7\xb2k\xe1\xb6\xea\x9b\x1a\xdc\xfah-J\xb5\xc1h\xcb\xb0\x8c\xf7\x1f/\xc3`\xc7\xd2\xae\xd0\x8aRcP\x95\xbf?]\xef\xa2c\xb8\xd1c\xbd\x9d\xa4\xcbU\x9a`VJ\x0b\x04e\x94\xb6\xf3\"\xcd\x1c\xd6\x01Z\xa0b\xbb\x02\xde\xaa\xd5z\xb1\xeb\x08\xab\xa6\x8c%S\x96\xd9\xa5\xb9\x0c\x1c\xfe\x89\xbd\x8dV+6=I\x93\"\x8a\x13\xaa\xea\xa2\xdc\xbeK\xb6L\xe3\xbf\xb2\xc0\x8fDvr\x91>:F\x1e\xdcJ\xa2\xe5T\x0bfiZ\xbcN\xf8\xda8\x9d\xd9\xf4\x99\x0d\x810\x1c\xe7\x0f1\xf8\xa19\xd0\xdc\x1e\xe8\x02\xc7J7)\xa05\x84\xb5\xfdYd\xdd\x88\x80\xc5\xcb\xba=\xd5Z/\x9a6r\xf6\x02\x0d\xd9(\xc2\xd9\xe2\xf4\x05\xbf\xa8\xe3\x17Tk\xeft\xfe\x02d\xe58\xf3\xfe\x94bf\xd0=\xea7\xb2\xf1uTD\xfa'p\x04\xff$0\xb0\x81y\xbb\xe6\xcc\xdbcj\xbe\xd7$[\x17\xcb\x12\xda\xe5\x0cK\xac\xd6\xd6\xaa5\xca\x01\x11?1\x0b\x16\xb2\xc0\xead\"\x0b\xac>f\xb2\xe0\xc0,X\xe1\xd2\x99\x97\xe4S\xac\xbe2\xde\xcee#O\x9eXC\xbd\x11\xe2\xffc\xf3\xfa|)?y\xfa\xf8\x19\xcd\xe6^\xff\xbal._W+\x1d\xb4C\xe5k\x13\x81\x06\xa3l \x8eR\xa7\"Y=\x9a&\xb9\xad*\xd4\xaf\x18\xf2\x8aM\x12\x1a\xefL\xda\xe1L\xcc\x02?\xeb\x952\xb3\x8a\xe8\xbf\xae\x19\x9594\xe7n\x0d)\x90:\x04\xfd\xd1F:\xab\x19\x06%r\x98\x8b\xda\xdbQ\xfb\xdc?\xb1\xbb!xb\x1f{\xf4A\xa0?\x9224r\xec\xd4#\x07>-\xf5\xd7\"\xee\xc7\xa9Hl\xcf\xe9\x91a\xbf\xf67\xf4u\x0fdn\xf3U\x96\xaer\xf9\xf7$M\n\xf6\xb9h\x81#\xb4\xc2\xf2\xebe\x10\x12\xe1\xd8\xcbb\x7f\xd5+\x89\x9dK9\x8d\x98KC-\x95\x9c\xc2\x0d\x1fp\xc2&\x85\x16\xdb\xa4-\x80\xeb\x8dL\x8eo\x9a_\x7fE31\xe6S\xd1'\xd5\xa3PD?\xbe\x96\xd1\ns\xd0_\xa4\xfc\x04@\xdb\xe7v\xa9\xc1h\xb0}\x9d\xf1\xde\x9a\xba\xc7\xd4\x1f\xf7\x9a|\x0d\xfc\xa4\x8c\xf1D\x146d\xf6Ij7\xee\x0d\xd4d#J\xb2\x01\x15\xf9\xadP\x107t\x1f\x96rl@5\xeeC1Z\xa8\xc5M\xef}\x96\xde\xc4\x9c\x97\xef\xd0\x18 j\xa6Y+j\x82\xe0\xb16\xa3Qn\xf2t_:\xdf@\x97Zh\xd2W\xb1\x81`h$\x0ci\xb4\xf4j\x8c(]r\xc6)\xe7\x8c\x1b=\xa7by\xd9JS&\xd2\xba'\x1670\xc9(\xbd\x0c!\xc3\x7f\x19\x99\x88\xa6i6c\xbc\xacp\xb0\x9f\xc44\x85\xcdc\x830\xde,\xb1C\x9d0\xb8x\x1c\xf58(\x82\x9b|\xeb\xa4\xff>\x14C\xa4\xac\xc5\xda8\xb6\xf6\x93\xe2\x8a\x03'\x12Z~\x8c\xb2G\xa3^\x13=\xb5\xa9J\xb1)U\x11\x14e\xa2\x90\xfb\xe7x\xb1\xf8\xc0&,\xbeA\xa1%o 2&\x81id%\xf9\xa3M\xb8\xda\xbd\x9b\xd2\xd4\xafM\xa4\xa7#y\xdc\x944\xaa\xcb\x06\x0e\xd8e\x1d7\x14 \x8a\xa4\xd3\x96\xa6\xee\x8b8A\x18\xb9n\xdc\xf4\xa7@a#\x0e\xc1\xcb\xd2\xb4p\xdd\\\xa8\xa7\x9d\xa5\xdb\xd8\xec\xc1A\xfa\x1a\xc8\xde\xd7P\x97B\xc9\xedn\xc5c\x03\x8db\xa9\xaaY\x08\xde\xf1j\xe55\xcc}\xde\xabl/x\x7f\xbek\xe6q\x88\xb7\xa2\x81\xc5\xcc\xb4\x1aUTJ\xb3$Z\x12z\x8e\x16\x90{\xd3\xf8\xc6\x92\xe5\xd5\x93\x17w\x0b\xd6\x14\x14i\x15M\xa7\xe8B\xee\x0d\xd8\xb2\x01k'\xe9\"\xcd\x86\xe0\xfd\xff\xa2(r\xe4\xbd\xb3W0\x04\xef\xff\xf9\xdf\xff\xb7\xff\x03<\xf7\xf9\xea\xc5\x9e\x00\\\x08\xdeI\xe9\xa8.\xd7\x96/\x0c\xe6\xbf>\x84\x02\x8e\xc0\xe38\x0f%\xb5\xf0`\xc8\x17\xd1\x0b!g\x0c\x8a9+\xbd\xe3=+\xe4w}b\xb7\xad\xca(\xb5&\xdd\x18f\xb9B[>\xab\xd8o!oW\xdcx\x9c\x7f`\xd1\xa4h\x17.\x9a\x0dI\xf5\xa7\xf3\xd1\xa5\x9e\xf2\x08k\xa7:\xd0\xc2\xdf&N\xfe6i<\xad\x92{\xf0\xb7\xd0*\xd5\xd1'RB\x9eHI+\x9f\x0b\xdd\x89\xb9z6%\xea\xea\xa9\xae\x02:\x9cI\xea\xe9 \xe1&n\x1a\xdcI\xc2\xc5\x1bwz\xda\xd2\xbd\xa8Dl\x01\xa3\x06\x0d\xa8Y\xb5\xed\xde\x1dZM\xfdJ\x06\x95\x91\xb7\x83Yy;\x88\x96\xa9\xe2v0\x85\x17\xc0\x9eC\xba\xbd\x1d \xd7Y\xbb\x1dt1\xb0\xa0\xdf.\xe9h\x9b9 \xd7\xc9TP\xb6XOG\xc5\x87\xea\"\x92\xe36\x89G:d;VL=\xc27\xbb\xc0c\xc6\x8d\x1f\x8e\x99Q\xd4\xddPgW0\xb4\x94\xc6\xf6\x19\x9d\x86\x10\x9b@\x8ag\xe0\x97\xc6[U\xe2\xbf4\x90A+\x13v\x0b\x17w+v*\x12x\xbdcl\n\x11\x88\x0fB(R\x981\x0e\xfd\xa8:#z\xf0s\x94\xc3u|\xc3\x12\x880\xd5\x8d\xaf\x99\x04\xa5\xfcPY'BM>\xe5\xe7\x89q\xe1\x9aZA08\xd6 \xa3-3*\x84\\U\xce\x8b\xc5\xbc]\xe4(\xb0\x1b\xfe\xf3N\xb1\x9f>\xfa\x14\xe0\xcf[?\xc2\x1f\xb7\x82[\xf3\x99\x1f\xf4\x16\xe9\xb5\x0c\xeeR\x9d\x86\xb38\x99j\xc7\x1e\xe70$\xb3Q\x0e\xa0\xd3%\xa1\xdb|_Nx\x08\x89\xff\xe4\x89i\xc8W\xe9\x8c\xeb\x97\x03]\xba\xa4\xaf'\xdc\x03\x99G9^\xb3\x0bG\x89w\xe9\x94\xe5C\x18\xddX\x12\xc2:\x04\xe1V\xa4\x90\xd5w\x10T4\xdb\x16\xb1\x93\x1c'\x838\x94\xd7x\n$x\np\xc4Jz\xf2,\x80\xa1\x8a_\x87\xb1\x89\x9d:\xee\x05\xca\x11\x92\xfd\xec)\xa4\xc6hl[\xfd\xc6\x03\xd0\x81\x8e\x8dwR4,\x0b\xa1U\xd1\x1b4\xb8@\xd26[g\xd0\x84\x1b\xec7\xf1\\\xf5Q\xcbKC\x93\xceO\xd1b\x8cz[\xc4K\xa2\xc4SE;\x8bt\x12-<\xbb\x06[F\xf1\xc2~\xbdL\x93bn\xbfN\xd6\xcb+F\x8ck\x15\xe5\xf9m\x9aM\xed\x92\x8c\xef\x07\xfbu\xce\xa2lBtP0b0\x9c\xef'\xde\x923^gD\x03\xb7\x8c}\xaak`\xdb\x94tN.W\\N*v\xb6\xfe\xab\xce\xb5\x92\xac\xae\xce\xe5\x16p\x04[[\xd9Hp\xce\x98b\x8e\xcf4\xcaX$+T\xe3}p\xfc\x12\xa9\x03\xcf'\\\x8c|\xc3f\xc5\xd0\x0c\xe1U\xabq\x91\xae\xac\n\x19\x9be,\x9f\x8b\n\xb8m\xf3\xb6}\x98\xf5\xac~Q:\xf8\x1c\x9aE\x17)\xfaK\xf7\xeejm\xb4\xee\xc3\xec\xdb\xe1\xe4R\x83\xfa\x83\xc7\xa6u\xbatM\xb7B\xc1E]\xd4W\x9c\x82\xb7\x86\xd6f\xbdY\x9c\xe5\x05\xaa\xf4\xddZ\x1b\x94\x9f\x12\x112\x06\xd3ic}\xferO\x8aS\x1cC/\xeeV\xd5\x89s\x93\xc6S_\xbc\xc7\xa5\x83\xc3v\x0f\x15@`k\xeaX\x8bU\xd2V\xc5T\xfbvW\xf9r\xae\xba\x15\x82{\"a]918\xe2\xc4]\x04\xd3AMy}j\x15\xde\x04F0\xa6o\xa0\xdc\xdd(\x07}\x1f\xcbz\xb3t\xb2\xce\xcds\x86v^~\xf0\xdd\x1f%\xf1\x12c\xdb\xbf.d\x90\xfb\x93t\x9d\x104\xf6*\xcd\xa6,{\xbd\x8c\xae\xd9\xd9\xba@\x06\xbf\xa1\xca\xf9\"\x9e\x10$Y\xab\xf1s<\xa5\x8e\x95\xab\xf4\xf3\x8f\x0b\xf6\xd9Y\xf0\xfb,]\xaf\xc8\xd2\xb3l\x1a'\xd1\xc2Qa\x92.\xd6K\xd7\xdcDan\x17\xcc\xc8\xa1\xcc\xc48n\xe9\x92\xf7i\x1e\x17\xf1\x0d1{^z>\xcf\xe2\xe4\x13]\xf6\x8e]G\xee/1\\\xb1]t\x9d\xc5\xd3\x0f\xd4Xd\xc1iB\x1c\xc5\xb2\xec|\x15%\xee\xc2\"\xca\x08X\xf1\xd2\x13\x84WS\x99\xb3WQ\xec\xeeX\x96\xd3}\xcf\xd2\xa4\xf8\x99\xc5\xd7s\xa2l\x11'\xecd\x11-\x89\xb5\xe7E?9>KW\xd1$.\xee\x88\x02\x1a\xdci\xb6\x9aG\x14\xaa\x14\xd1\xd5y\xfcWb\xedn\xe3izK|\xf0\xd7\xd7\xc9\x94\xc2\xae\xbf\xa6\xe9\x92\x98z\xbcX\x9c\xb9\xc6:[\xa4\xe9\xd4Y\xca\xb9\xd9\x86\xc2,\xfd\xc4^E\xf9<\xca\xb2\xa8\xb1B:\x9b\x91\xdb^\xd4x\x1b\x17,[\xc4\xcb\xd8Y\xa3e\x0c%A(\xcb\xbe\xda\x17p#\xefgv\xf5).\xbc\x10\xbce\xce\xff}\x9b\xfe\x95\xffw\xe6i\x9a\x1e\xa9\x89\xf9\xc4\xeer?\xeb\xe2\xee\x9d\xdauh\xa7\xe3Q\xeba\x0e\x9a:\x11\x13WL\xe6Qv\\\xf8\xfd\xa0W\xa4\x1f\xb90+5\x99\xbc,__ \xc3\x0b\x7f@\xd9\xa4\xa3!\xe8%gf\xf4\xd0\x97X\xa6\xa98\x8d{\xca\xd8\xa2\xf1q\xfe1\x89\x8b\x05\xcb\xf3w\x92i7\xdcs\xf3y\x9a\x15\xf3(\x99*\xad\xd5\xe9\xe7U\x94\xe4\"'\xa3=\xc5\xabh\xf2\xe9:K\xd7|\x8f\xd3\x00\xa8j\x1c\x17E4\x99/\x19Ev\xed\xda'\xb4\xaccW\xc4#\xa4KEA\x8d\xd3\xe4\x7fnR\xf9O]*\x7f`+\x16\x15C*\x8d)\xa1:\xb1;i\x87\xdd\xfd\xc7\xdeiD\x92\xc29F\x81\xa5\x8eC\xba^\xe9\\\x98\xc76W*W\xb6\xfb\xd0~H\x8b\x82\x93\xc2\xa6\x01\x8a:\x9d\x86)\xaav\x1a\xac\xa8z\x8f!\x0b\xf1\xa9i\xc0\xbcF\xa7\xe1\xf2\x8a\x9d\x06\xcb+\xdec\xa8\x1f\xc4y\xd84V\xac\xd2i\xb0X\xb3\xd3h\xb1\xe6=\x86\x8bbg\xd3`/\xd2U\xa7\xa1^\xa4\xabN\x03\xbdHW\x1b\x0d\x93\xf3&\xae\x11\xf2\xb2\x96Ny\x95?FY\x1c5\x11\xca&\xfeG\xafC3\"\xeaib\x87\xd4\xc3[\xf91Z\xc6\x8b\xbb\xae\xf3O\xd7\x05o\xd8\x05\x02Y\xdc\xb2D\xb2V\x0b\xacd\xad\x86\xe5\xf9\x8e\xfe\xe5P\x15\xc4\xf8\xf6\x9b\x84\xaa\xc4\x7fj\x06\xe3K\x85a\xd0`\x1f\xe3\x02\xee\x89\xf0\x80O\xfb\x96\x83\xbc4 \xc2rv\x0b\x1f\xd8\xf5\xe9\xe7\x95\xef\xfd\xe7\xc8\x83m\xc8z\xc7\x17\x17\x1f^\xff\xf0\xf1\xe2t\xfc\xee\xf8\xed\xe9\xf8\xfc\xe2\xf8\xc3\xc5\xf8\xe4\xa7\xe3\x0f\xb0\x0d\xde%]\xa9,\xfe\xdd\xbfXi\xcd\"\"\x1e\xfbZ\x06\x80(_\x96w\xa5\xb9\xf3\xaetkkmG`\xc7\x00\x81\x11\xf1\x9e\xcb\xfd2\xfb\x1a\x1a\xb4\xf9\xeb\x11\xbb\xc4\xb0\xaf\xa8\xdd\x85!\xf8\x91\xf6\xa6\x16H\x9bNs\xdc\xc5\x9e\x10\xf3\x84\xcc\xa3\xfc\x874]\xb0(\x11:\x80\xef\xbf\x87\xad\xaa\xe8\xddz\xc9\xb2xR\x16\xc5\xf9\xbb\xe8\x1dg\xfeT\x05%\xce\x99\x15\x0bx\x01\x83\xb2\xd6\xd9\x0d\xcb\x16i4eS\xab\xaf\x01\xa9\xc0\x03\x89<\x13[\x1f\x87V\xcbo\xa3\xec\xd3z\xf5c\x9a\xbd~\xd5\xaaJ\x13\xd3\xcez\xaf_\x8d\xeb\x88\xc0q\xe0\x90cHj\x85\xb4\xae#@\xce\x8a\xe3\xa2\xc8\xe2\xabu\xc1\xac>\x1d\x8c.f\x9b(\xbf\xf2\x89\xee\x89\xe0\xefM3\xfd\x90\xa6m\xd7\x95\xe5T?\x9c\x9d]\xd8\x93\xfd\xb7C\xcf\xfb\xb7\x0d\xe6i\xf4HB\xd7\x9a&\xd1uXK\xdcK\xf4k\xccT\xed\x8c\x0ePV\xea?\xbc\xfc\xe6\x1f\xc5,'\xf6\xd7Q\xad\xc2\x08U\xc8\xb4Q\x15j ]\x82\x0bF\x8b\x14.\x1f\xa5~\xd0\xf3huc\xe9\x07\xd6\x8b\x14tl\xb3\x0e\xf5\x94\xf6\xff\xe6n\xfc\xf2E\xbcl\xd8@\xfdRE\x1e\xab5\x86!\xfe\xad\x90\xbb\x93\xbe\xb2\xc4\x9d8?Y\xe7E\xba\xac\x16\x15\x01X\x91\x0d\xbc\xc1\x1a\xa2\xf8V\xf5 \x01\xba\xc1*\x1b\xbdtXl9\xc4\\RL\x15{\xa7\xc00#\xc6`<\xaf\x05\xd1\x11\x80ndk\x880\x92\xb6\xe0[a\xe1[\xd1\x8co\xa4\x1f!h8\x94\xf60cW\x9c&T\xbeD\xf5\xf0\xa6\xe2@hw]\x06~l\x913GgP\"x\x8a\xee\xbd\xba\x02\\\x98}\x89\xabb\x13pb\xb9\xe8\xeeT\x9b|\x02y\xf11/\xed>\xd0$Q\x81\xe8\x8eo\x8cK:@\xabzZ\x06\x0e\x9a\xbdQZ\xdfq4\x93\xa4?k\xfb\xa3|\x15M\x1c{\xb5\xfa\xea\xc8\xa0~\xef\xce\xfd\xb5\xc8\xa2\x877\xbc\xe8.O\xed\xe8\xb4\xd3\x8eN\xac\xf6}l:P\xa9\x8c\x8c\xf7\xd8\xa5s\xc4\x8e+|\x9b0\x08Hc\xd0}\x82\x14\x14\x06^Lz\xdaV\xd2(\x86\xdcA\x1d\xf7\xa0\x8b\x0886a.\xf3\x00\xf8\x8a& P\x89\x84\x15\xfaXmH\x15%\xa4\x1a\xc7V\xc7\xf4Mh\x145\x8c\xee==\xf0\xc9\xb71%r\x9e|\xa5\x85\x7fgJ\x94\x06\x9c\xad\nU\xf0\xe3\x06r\x84\x1d\xdb\x04\xc2\xbd\xd9\xab\xa3U' \xee\xddj\x1f\xabG\xc0F1\xb2\xd3\x03\x0c\xfb\x8b\x7f{\x0e\x9fc1J{a\x8d\x93\x9d8d\xc5\x97\xf4>\x12\x17\xe2m\xc8R\xfer\xc8f\"9\xe77\xcaf\x03*lq\xe2\xef\x0e\x1c\x11\xc6\xcdp\xeb2\xcf\x97\xd9\xca\xba\x92\xdc\xb6\x06\xa4\x91lnq\xb1x\xd7\x8bV\xccY\x9a\xa25\xcd\xebW\x95\x0dv\xcd\xdci\xc5\x92i\x9c\\\x7fD\xa3\"\n]\xda\xbe\xc1\xe5\xb7\xb1\xc6\xf0.\x10w\xed\xf2\xcaU\x06C \xf1\x04\xc3\x9aW\xf6B\x94\xfdL\xc5\xb1|\xff=(\x03>\x89\x98>\xeb-\xd7\x8b\"^-\xa8\xb4P\x15\x1e8\xc5=\x82X\xde\x94\xd9\xd8\"\xcc\x81B\x1b(\xf5\xd2UaGEu\xde\xba\xa3\xbbA&\xc4d\xdd\xe5 \xa9\xbb\x1cd#AhG\xe9\xe5\xff\xcb\xde\xbbv\xc7\x8d\x1b\x0d\xc2\xdf\xf3+J\xcc\xacCF4\xad\x8b\xc7c\xb7G\xd1\xeb\xb1\xe5\x8d\xb3\xe3\xcbZ\x9e\xe4\xeci+Z\xaa\x1b\xdd\xcd\x11\x9bdH\xb6de\xac\xe7\xb7\xbf\x07\x85\x0bA\x12 \xc0\xb6<\x93d\x1f|\xb0\xd5$\x88K\xa1P\xa8*\xd4\xe5\xac\x93\xc0\xa4\xd5\x92\xd2B\xdcn\xc1L\x89X\xd0\xcd\x0e\xb1\x8b\xa7\xf9\x197\xa4\xd2\x93\x02\xacPaLU2\xc7[\xf1\x0d\x9e\"\xed\xe7Gj\x82xQ:\x1a\x13\x137\"A\xc3\xa6\xde\x02O{r\xda\x01R\x907\xb3@&\xa0l\xdb!t\x87\xba\xa3#\xac\xb1\xe2k\xe2\xc7\xd3\xbd\xee\x17F\xcc\x12\x7f\xe9\x05\xef%\xa9\xff\x9cW5\x06Mq8\x9f\x84<\xc1b\x19\x99\xecA\xf3\x8c\xd9\x01Nz\xd6\x8c\xe2\x8d~\xb3q_xv\xb8\xf4\x97k\xf0\xc8]\xe7\x9b\xac\xfe\x1b\xeb\xcba\"\xe2\xa0U\xf6\xb6\x8e\xdd\xed\x8c\xbf\x07>QZ$\xc8\x9c1*\xc9\x92:\x89Sn\xb9*\x08\x07et2\x984!?\xf1\xbdI\x8f\xc9\x12\x8eU\xecs\x83\xaeP\xc2\x7fX\xcc\x17EXw\x8d%\x8e\xa20@\xf2\x10\xceoy\xe7\xec\"\xcf|~\xeb\x0e\x04\xdf\x85\xba\x9b\xd8\x0eP\xcd\xb9\xe3*.|\x1ec\xcb\x18\xd5\xe0\x96\x85\xaa5\xd9\xf9_\xc7\xd5kN\xbc'\x92\xa0\xd7\x0dA\xefch\xa8\xa6\x8d\xa8\xf9\x8eW\x13r\x1eu\x16\x99\xbe\xdc\xa0\xc9\xcfF\xb7\x8d\xc3\xee^e\xc1\xa3\xf1\xd3\xe7\xcc!\xc8\xb6\xc6\x06/\x0f\x15\x13\x87\xfa,\xf2\xaaf\xa0\xd7\xec-\xd3\xc6bVmZD\xb2n\xb1\xd6\xc8\x0cY\xe7\xa1e\"\xd6\xfe\\Y4{_Je8\xd2-\xb1\xbe\xdf\xd2N8\xc4\xde.\x99\x7f\xb6\x8da \xd9q\xaf\x19A\x08%Ztex\xb6i*42\xd3N\x0f\xbb\x8e\x07\x9amW\xa5]\x0c\xd5\x15?D>\x13\xaf\x17)G\xfe\xfa\xaaLm7\xb0m\xae\xe7u\x19O\xfbx\xbf\x1b\x91\x80g\xcdy\xd45q\xdc\xf0\xe7\xdd\xfb\x8c\x8a;:\xd3\x0e\x809<3\xdewx\x13 \x19\x93N<==\xb4\x96m\xd6\xab\xf7\x11\xcd\xfb<\x1c\x97\x91\x8fxz\xa2}\x91/\x8f\xee\x88\x98\xc7\x00\xf1\xd3\x0e^J\xb9\xccc\xd9\x92Zi\x8e\x86\xf4b\x86\xb3\x88)\xb1h\x03z\xb9S\xeb:\x84A\xfc4\xa1:z!=D\x11|\x8bI%\xbb\x17\xc2\x0cv]\xbc@Ax\xf9\x0eU\x80\x16\x0d\xa3\xbcu\xbc\xd6\xe6nP\x0bg\xab\x85\xf2\x18\x9e\xaf\xc8\xec\x12\x03K\xf1\xc05,\xf55\xe4\x0b\xf8\xbf\xe8\xa3\x05\xbb\xe0\xfd\xdfH/\x9a\x82Q\xb1\x03\x8a!\xb5A\xac\xf5\xf3\xe8<\xbf\xceHI \x87\xef\xed\x1f\xeeyMX\x89\x04\xd5\xc9\x13 \xf2\x10f6\xae\x98\x16MV,\xb6\xec\xc8\xb7\x1c\xc1\x86#\xdc\xab\xac&e\x16\xa72|\x8b\x8f\xc1%]<`\xc4\xac\x1a\x8cQ3p\xdd\xbb'NPf\xf5\xda\n\x95\xa5\xffF\x8dfK9\xc3&\xa4\x8c\xcb'%\x0b%(?\xea\x03\xc9g\x10\x088\x082r\x0d\x15\x9b\xae/~\xb3\x1a~\x1e\x04\x11\xe7\xb2)\xa3\x83\x87}\xd6zr\x04\x19C4\xbcr\xcb\xe7]r\xc16\xae)7\x99\xc7\x9c\x12\xba9\x89\xdb\x0b\xc3\x9d+s\x0c\x1c\xe1#\xb5G\xec\xd8\xf7\xc2\x86\x02\xb4q\\\xde^\x9c#\x00\xd1p\x8fy\x8f\xcbGk\x96\xc1\x97\xb9)w\xf3+\xd1\x92\xfb\x95\xea\xbf\x98t\x05\x86s\x16\xc9\xa1N0g\x8a\x1a\xe4l\x02\xcd\xadC7\x81,{\xf3uN\x92\xef\xbay\xd6\x94P\x17}\xd4\xfd\xf3\xdb\xd3\x0f=\xc7\x00Z\x9e\xbf}\xfd\xee\xed\xe9\xab\x0f'\x13\xd0\x88\x02'\xaf\xdf}\xf8?\x138\xe8\xbfY\x92\xfa\xc3M\xe1\xc4\xb8\xb7/~;'\x01\xdd\xe8\x11v\x83\xea\xea\xa4\xfak\x9c&s\x11\x15\n\xd1\xd6\xb0 \xf8\xbeN\"9\x05\x98@\x12\xd1\x99\x8a\xa4g\xa5\xef\x1d<\xd2'o\xec\x88\xd4\x067\xf1/\xb5=`\"x\x1f, f\xc68Y\x17\xf5\x8dD\xa4\x97\xf1\xac\xce\xcb\x1b'\x88R\x92o\x9bR\x1f;\xfa\x8d\xb1]\xe7\xd4\xa5\x90\xa7\xed\xb0l`\x90Dl\xa2\x94k8\x82<\xbcS\xd8\x9a7\x07\xdf\x05,Ve\x0f\nm\xf5\xf3\x95\xd6K\xdcpL\xd8\x00\xc5\x81\x94S\x04\xa7Tk\x9fR-\x86\xa9\xdc~\xc4v\xd5\xaf%\x83\x8e\xddb\x82ZK\xfbI\xf5\x01\xdd;\xc6M\xa8\x15\xc8&\x19l_\xac\xb7\xce\xd2\x88\xbd\xfc\x9f$#e2\x93cx\x9e\xc6\x95\xd5! \xf8\xd2j\xb0\xbeO\x9bX?\xad\x89:w\x92\xb8l-\xf9\xeb\xeby\x19\x9aQ\xfb\xe1#\xc6\xe1\xef\xf7rj\x08YB\x97\x81S\xec \xff\xa0\x9fiD\xd1\x94{\x91\xa7\x11,\xbc\x89\xe7.\x08H\x9c\xa1\xfc\x8b\x86\x7fW\xef\xceItIn\xe0\x18\xe2\x88T\xb3\xb8 >>\x08P\xc5T\xe7,G\xaa\x7f\xf8H57\x12\x7f\x8d\x89\xd9\xd51=\xa2\xc7\xc6\x9e\x92+\x9e\xa7\xa9\na\x16\xea\x13q\xd2E)BLr\xc2gQ\x1b\x04 %\xd2\x1e\xe5\x00\xd1\xb7\xcb\xbb`\x92\xaaxD\xf9\xaa\x9a\x13\xa2&\x94\x9a\x88\x94\xd10O\xbc\xae\xc26\x89'\x0dTy\x17u\xf4\xcd7|d\x18\xf4Or\xf83\x7f\x81 \xf1\x85p\xa2\x07\x8b\xc6\x0e\xa3\xf7\x84\x13\x94U\xeb\x05\x86\xda\xf0\xbc\xae\xb9\xc5\x97\xfaA\xb2\xd0\xa9h\xcb\xb2 \xa1\xc2tn3v(\xeeuo\x7f\x17\xec\xf6\xf7Q'\xe0%S\x7f\xe9N\xad\xc2\xec4\xfe\x92\xd7Q\x04lq\n\xf5\x177k\x02\xe4\x98\xf2\xa9\xf5?\xa2G\xbb\xb4!\xf6\x98\x07\x12\x06\x89\x0c\xa2\x92\x14i<#\xfe\x83\xe9\xc7\x8f\x7f\xff&\xfa\xe3\xee\xb1\x1fL?\x9e\xfdr\xfb\xf9\xec\xc12\x04\xef\xe3\xc7o\xeeyJ\xb5vW\x9f\xa5oT\x10\xfd\xf1\xd8?>\xfa\xf8\xf1\xa3\x1f|\xc6m\x1b\xed\xf2\x07g\x01\xb6\xf4\xcd~\xf4\xc7c\x86\x18\xdft\x03\xc2\xeb\xbd`\x85~\x8d\x8fV\xa7n\x96\x06|hF\xdc\x0d\x10?\x184X\xd8,\xef\xb7\xbf\xf9]\xff\xaf\x8e\xb2\xae\xe1*\xd8\x11\xb3(\xf3\xb5Qm\xf2:\xc6T\xde\x85\xff:.Z\x06|\xaf\xe3\xc2AQ\xd3\xaa\x85\xdbL\xb6\xd6y\x1e\x18\xdb8%5\xfb\xe8\x94\xd4\xad!\x9c\x92\xdaa\x08\xadZ\xca\x10\xfa\xcf{q\xa4\xaex\x92r*#\xbc\x8e\x8b>b\xae\xf8\xcbS\xd2am\x9c\x12\x9a\xcd\xa3\x8a\xd4\xecm{\x0d\xc3v\x0e\xea\xa1\xe5\x9fGK\xd2\xd7@\xb3D\xb8\xc3\x0d\xcc\xb9i\xa0\xe6\xe3\xd8\x16T\x8ew\xde\xe0\x8f?g4\xb4g\xa1\x85l\xf2\xf0@VQ<\x9fkF1\xecx\x0e<\x07\x83a\n\xd6\x98\x94\xfd)\xac\xf4Sh6\x94\x8e)\xba\xe2\x99\xe6\xbb\xee\x07\xc0\xb3\xf2\xe9\x9e/\xad\x13\x03Eg\x1a\xe9C\x1ai\xda\xbd\x19\xd3.&~~\x95\xd5>\xe1\x1e\x9b\xfe>ej\xf74\x8a\x8a-P[\\\xdf-\xb5T\xef\x8ae\xc8\xac\xc7c\xbd8s\xf4\xed\n\xab\x8bi}6~?\x0c7\xcd#.\xe9\x9av\xdd-*\xafq\x15D\xeb\xb8\xf0o\xb6\xd8.\xc3\xe3\\\xb3l\xf8\xddD\xf9.\xbb\xc9 \x00k\x0d\x00\\\xf7\x9a\n\x80\xb5\x1e\x00\xbf\xeb\xffE\x87E\x05\x85\xe9\x99\x8e/97\xf3%yo\x1eF\xf3\xa8+\x99\xc2y\xb6J\xd2\xf9\xab\x17:\x99\x0c\xc3Oe\xd2\xab\xfa|\x8c\xb5\xd7\xb5E\xc8\xf6>f\xd8G\xc6B\xd13\xcd\xffO\xd9e\x96_g\xc8s\xf8h\xc2\x0f~\\\x03c\x80\x16I\xca\xa2\xf2H\xd6\xe6\xef\xd1\x1f\xa7\x1f?~|p\xf6\x80Y\x1c\xef\x827au\xd3$#\xccM\x9a>\x0c<\x14<\xb19\xa69\x9b\xc3\xc5\x0d6\x9b\xc9\xf7\xaa\xf3\x87nB'}\xb8k\xf4\x05\xde\xef\xc9\xba\xa8o\xb0\xc1q\xf7\x1b\xde\xefk\xf2\xa96}(\xd4\xd8\xfc\x8f \xff#\x9a'U\x91\xc6hY\xca\xdc\x98\xf0i\xc6\x7fJ\x80\x0e\xce\xec\x93\x01\xa3B\xc4\x90Sz\xde\xbeh\xba\xd1Z\x97\x94\xa2b\xa3\x91\xefW\xcaE\xa5\xb7\xd7\x19)_\xbd\xe8a\xab\xd4\x8b\xa2\xe5\x8c\xae\xef<\x08B\xb8\xc6\xfc\x91\x80\xb1\xc8\xcf\xab|S\xce\xda\x1cE{'\x9d\xf6\xb4\xb6yvJXH\x9d\x92dcL\xab\xf4\xd6\x92\x14\xd03\xdf\xdb\x7f\x88\xd1\x923\xb9\xa1\xe8\xee\xeaW\x97\x92z\xc9$\xf5\xb2\xa5\xbe(\x87-\nY\x8e\xb9\xd2\x90Z\x1f\xb8\x0e/\xf7\x13\x93m\xa1\x1ck+:\x7f\xdc\x8cY\xaf\x8c\x8b#\xc2\x83\xf9(\xcch\xeb!6\xbaO\x1b\x8d\xa3\xa4z\x9do2\xba\xc9Xo\xdf\xed\xb7;+\xe2\x92d57\x90R~\x1ea\x8cr\xe5\x01^\x8e\xca\xd6\x0f<&\xec\xc9\xf7.\x176\x1d\xd5h\xf6\x03Y\xe4%y\xdd\xbaAu3\xe7/}c\xb8H\x0e\x87 h2\xaf\x03FSc\x03\x9e@\xa6\xaf\xc0\xec\x9e\xcc\xf6oby&05\xac\xbd\x84\xb9\xd9V\x8f\xc55\xe4\xc1s\xc6Z#\n\xc8\xfd\xc4\x1b\xd1\x83n\x9b\xddC1JA\x194\xfe\x91\x98\xd5\x8bb\xd5\x1b\x96y)\x87N|\xfd`\xea\xf6V\xae\x95a1\x97Va\xf1\xa6b\xf0\xc6r\x95\x92g\x030\xdbf\x8c\xa8\xc7m\x01\xac\x8e\x94\xb5\xdd\xdd\xb5\x8c&[\xdf)\xc8X\xa4\xc7\x16\xa4\xf6\xf5\x90\xaa|\xa2K\xc7x!\x82\xf7\x0f\x8d\xbb\xd8\x94K\xc2\x87N\xe6r\xf0\x95\xc5\xd5\x14\xc3j\x9eF\xe7EI\xaeHV\xbf\xdb\x94\xcb$3*j[\xc9\x94\xf6\x9e\x02\x81\xef\xe1B\xd2fb\xa6\xcd\xb4\x9c\xfb\x17Sr\xe6\xaa8\x03\x9c\xf8@\xd0\xfa\xe1[\xdaf\xb7\x7f\xc9\xe2 \x85\xcaN\x17\xa9\x86\xfa^\x92\xfa9\x8f\xecW\xc7\xb3\xcbg\xf39\xc9\xe6\x9b\xb5\xebHtVO\x836L\x82~\x9c\x0c\x86\xaf.\x99\xe5$Z\n\xe9\xcf\xbe\x1av\x8f\x18\xeb@\x1a\xae\x81s\x11\xd2*\xcav\x9e\x80\xa2\xe4Z\x88\x08\x87\x06\x8aL\xc1N\x9b\xcf\xa3\xf39\xb9\xd8,_\xbd0\xae\x00\x8e\x0d\x99\x9d\x16L\x7f\xb8y\xf5B\xc4\x9c\x17EcB\xdb\xfd\xc4\xb6\x14\x12\xcd\xf9z\x00y\x1a\xb0!|B\x8e\x9f\x08\xce\xeb\x1d\xdf\xbcC\xc8\xd3\x15i{\xb8\"\x8f.7\xfc\x18\xc4T*\x124\x12\x0b\xa6\xf5\xb4t\xaf0\x8f\xae#\xe8\xf0\xb1\x83\x839q\xf3)n\x1at\x1d\x84\x03\x18\xc4\x19\xe9\xd4=g\xb9]\xbbw\x87\x01\x12\x0e\xb6\xefpT\xecO\x89\xf2n\xa3{'\x19$\xb7\xe19@G\x1e\xcfk$Gi\xff\x15Y&UMJ\xc2\xe8U\xdc\xe5@\xaa\xd5\x9b<;\xad\xe3l\x1e\x97\xf3\xbf\xc5e\x96dK$\xbe\x0e\\\xb0\xf1FB\xa4>,I(\xf2\xc2N\xaat\xd8\xecH\xa2N2\x94;\xb5/\xc6\x86\xda?\xc5\xa7\xdb\x1b\x010G\x97\xeeu\xbf\xde\x9e\x969\x1b\xba\xe9{\xa09gH\x14\xcf\xe7'T\x80\xfc\x91{+2'\xa8\xeeSn\x1e\xb6\xb3\xaf\xb5\xadn\x1a]\xe7Wc\xd2\x8a\x08\xff{C_c1\x90\xc5\x9b\x881\xa4'6\xc9'\xd3<\xf0=\x8a\x00\xbb\x0c4w<\x959\xd1w\xb3\xcd,L~\xb5\xfd\xed?\x8b\x8bzS:\x06\xee\x80\xedW~\xef\xae\xc15\xb0\xf2\x9a\x8bKQ\x06`f\x1f]\xa9\xff\xd8\x05\xcc%\xe7\xa0^\x88$\xba\xeaL\x8d\xe6\xdf\xad\x84kwA\x0d\x1e\x1f\xe8\xc2\xf8\xd1\xe7\xfaP\x11\x87\x8f\xba\x99\x00\xb8[\xddw\x07A\xbb\xfd\x8d.M/\xf3aM\xf2\xecy\\\xc4\x17I\x9a\xd4\x89=u\xc2\xd5\x97&\xa0\x80\x8e\x14\xe6\xb7SQ\xdc\xbb\xc7\xb2Ox<\x8d\x00^\x1b}\xfe\xdcKI\xc1\x9e\x95\x1b\"*\xceXL\xff\x93yR\xc7\x17]\xa7`\x93\x03o\x92g\xaf\xb2E^\xb2(\xf4\x16\x0c\x17\x1a\xb6x`Jz4\xc5\x18\xfb\x04\xdd>\x8c)\xbe+1\xa0\xf7\xccc\x1c\x03\x1cj\x97\xc8G\xb7\x91M\xa4\xce\xc2'Zy\x1el'nI\xaa:/\x89l\xc7i\xf9\xd9\x05[lJ\xda\xc3tZ\xca\x9c\x0d\x13\xc6j\xedi\xeb\x14\xed;G\x9c\xe9\xc7\xab\xb52\x84\xdc7\xe5l`\xa1\xe30!\x90\x19z%\xd6\xd8D\x95\n\xbe2\x84*\x08!\xf1\xcb\xe1\xd0E*\xcc\x9d`\xa5\xd7\x1azr\xda\x18l\x1e\x13Q\x90\x007\x96\x1e\x83*\x16\x93^\x81\x17~\xa8\x87,\xc9\xe6\xad\xaa'\xd9\xbc\x8f\x15\xfd\x81I\xebP ^\xd9B\x7f\xb3\xab\xbb\xd6\xb4\xf1m\x12a\xbf\x1f\xee'\x87\xb8`\xf2\xf5\xcc\xb8\x8eD\x08*\x01\xf7\xb4\x12\x18b>)8\x10\xefg\x11=1\x10\x80\xbe7[\xc5e<\xabI\xe9\x85p\x9f\xa7\xf9\xe2\n\xee\x01\xb1\x04A\xcc\x1b\xa2\xcc\xe3`3\xdaV4Y\xfa\xb9\xddR-\xd2]\xbd\xc5\x98\xf7\xd5\xb0*\xe1\xf3\xe7a\x941\x98\xb8\xe3\x04F\xaa\xef+\x03\xf2[n\xd0\xea\xa82\xe3*3\xbb$\x99&\xd6\x15E\xc5V\xaa\x7f\x91\xb6\x9b2w\x86\x1d\xd4\xdd \xb4v\xd8\xd9\x0bp\x04\xaf\xe3z\x15\xad\x93\xccG\xa7\xad\xd6b\xfd\xc6\xfb\x02\x1dt\xf86\xf8@>\xd5\x83[!\x89fy\x9a\xc6EE|d\xe1\x12\x13bg\xf2e\x0fYs\xb8\xcf_\xb3Y\xe9\x12\xcf\x8aH[\x95\x82\x93CQ\x94\xf4<\x12\xcb/\xb8\x15\x8f\xe4\x96\xe2\xa6\x830>\x01\xee\x8d\xd9q\\\x11\x02\xa2XO8n\xfe\x14\xdcy\xd0\x84\xe2\xeb+B\xf5\xea\xa5\x86\xf7\x9e\xd5\xc9\x15Q\xf2\x08\x91\xe8\"\x9fwRH \x81z(\xbc\x8f\xee\xbb\xdf\xb5\xff\xda\n\x9cW6\xef\xdb\xc7z\x86\xb3\x17f:\xd6\xfb\xea\xb2(\x0e\xfb\xdfv\x1b\xafZ.^}\x0f\xaf\x94\xf5\xf2\xb0+\x15\xcf\xf8\xf3n?\xcc8\xfe\xf0\xdb\xee\xf3\x82\xcf\xad\x1bub\xce\xfa\x17\xe1\xb0\x1f>\xea\x0e`\xc5:z\xdcy|\x85\x8f\x0f\x0e\xba\xe3Z\x8364\xdb\x92u\xdf\xcb\xdfu\xc3\xb9\xf6n3\x17\xaa\x03\xdb\xfe\xc3'\xddQ\x9d\xf3\xee\xbb\xd3\xb9n\x1c\xdb\x92~\x00\xe4N\xe5\x13\x8cQ\xa6\x8b\x1f\xdc\xaa\xf6 \x8e\xba\x9e\xd2\xa7p\x04O\xda\x8f\x9e\xd3Z\x9dj\x97\xc68\xde\xcf\x8c&h\xcc4L&\xcf\xa2\xbb\xf6\x14\x1fu\x93qMZ)\xc8\xba\xac\xae\xce:\xec\xad\xb9Sz\xb6\xca\xa0\x80\x8c\x84\xabO\xfck\x96\x8ew\xd8\xfa\xec\x9d\xd8n!\xf2\xa4\xdd\xbe\x90\x96\xb7\xa9\x06%O\x8b\xa8\x9f5\xdbtv\xc6\xe6\xe8=\xec.\xd1\x14\xf2\x03\x8e\xc0C/~\x16\x8ck\xc2L\x155w$1\x1cC\x0c\x13\x88\xbb\xf6x1\x9a\xe2\x05\xa1T\x95\xd5\xc9\x9a\xf4\xaet{\x13\xa6\xfb~\xd5\x89\xf3@\xc1\x94\x85<6\x01w\xa9D\x07\x98n\xf8\xa8DU\xcd\xd1\xfe\xe8Q\x95`\xc8\x81s\x16\xbdC1\xa0\x88\xcek\x0eD\x1e\x0e\x89e\x87\xffQ\x8d\x88\xf0*\xabsLa\xbd\xc1\x85\"\xb8P\xd9\xb0\xb5\xe4\x07eUuKJ\xc9\xe3:B\xe0\xbe'\xb3<\x9b%)\xf9P\xc6Y\x153\xfeuI\xeawy\x9e\x92\xb9\xbf\x83\xcc\xc1,\xdaT\xe49\x9e\xe6|\x01;\xb3\xce\xa3\x82\x94T\x02\xf5\xdf \xb1\x11\xe4|\x10\xe1`\x7f%I \xe5)\xf2\xe1i\xbd6\xe9\x8d\xf0*d/\x84U\xb4\xc94\xeb\x86\xd6D\x9d\xed)\xf8\xec\x9e\xf4\x15<\x85\xbaI\xfb\xf74\x80\x9a\xab\x81\xf0\xb7\xaf\xbc\x1b\x1e\xec+\xb3\xa5\xf0\xb3\xf1\x96\xc2U\xa4\xcbj\xae\xf3Q\x13f%t\xe9>\x7f\x86\x9d,:_\xe5\x15\xbf\xdb\x18cC\xfc\xb3\x91\xf4\xec\xf8;\xdc\xdeU\x02u\x07\xfd\xde$\x1f)\x9f\x9dj\x9e=\x1f\x06\xdc\x1b3\xe0\x1c$U\x0e^=\x9b\xce.\x88\xef\xdd\x1b\x0fN\xdc\x06mX\xf20{\xfd\x9bW\x93e-\xbb\xf6\xc2\x16\x9e\xe7Y\x1d'\x19)_e\x8b\xbcO\x05z\x07\x83\xf8\x8bN\xf1}\xffl{a\xb3\x88\xc7\x08R%^\xbe\xc2\x11\xbc\xefZ\xa95\xc3}\xa1\xf8(%U;\x88\n\x0f\xe7\xf9\xa2\x15\xd9\x06\xe3\x11\x0d\xf4.\xe6N\x07\xa0\x10\xfdfn\xb4A\xde\xd3\x87\x1e1T#\x82\xd2\xb9\xff\xd8\x93\x8c;\xdfL\xe0E\x87\xeb\x10A\x11\xaa\x1fn\x18\x01B(L\xe0\xb2\xc3\xd4a\xa2\xd4\xd7y\x96\xd4\xb9K\xc4\xc7\xae\x84\xd1\x112\xcf\xd9\xbd8\xedl\xc0\xd2U\x7f\xe8B\x03\xb6\x1f\xa3\xd6\xb8\xfc2\xb4\xab\xaf\xaf\"\x92\xfdcC6\x82T\x8b\x00\x19\x92x\x86L\x08\x95\xf5\x9e\xc7iz\x11\xcf.\xd5\x8a\xb9F~\xa2\x87\xd8\xe0\x9c\x196\xbc!\xd7\xd6ik\xe7\xfc3\xcf\x19R\xfa\xde\xe1w^\x10\xc2&\"Y\xb5)\x89\x92\x14\x97\x03\x02\x93J\xf77\xab\x10=1\xde<\xc6\x13\xee\xd6XG\x17T`!sf\x0dQ\xf9\x1f\xd0\xacY\x8cJ\xdf$\x0b\x8c+1\x89o$#\xad\xb8\x9c\xc6g\xf4\x8bp8\n\x07\x83\xd6\xe9\xe6\xa2. \x9e\xf2\x92(8C\xacc\xc6\x82\\`\x11\xadbT\xaerH>\xa6\x90\xfcQ0\x1f\xba\xee\xd4N\x1c\xd6\xf7\x8bF|\x15]\xc5i\x82&#\x1c\xeb\xfc<\xe4|\xde\x8b\xb7\xaf9A\x11\x96\xec\xad0C\x0dr<\xf1B\x93\xad\x8c\x07\x94\xaa\x93\x18\x83\xa3\x15qU%\xd9\x12b`\x95!M. \xfca\x9e\\\xfd!\xc4\x97\x80\xfdr=\x85\xe8\x07\xdf\x07\x90\x97\xf0\xfd<\xb9\x82\x07\x7f\x8a\xd0-DL\xd0\xb1\xc7YJ\xdb\xc7\x0e_\xe6\xf9@w/\xf3\x9cu\xf62\xcfEg\x99\x1a\x03Z\x89U\xc6\xf9f\xec\xf5\xc3*\xa9`\x1d\xdf\xc0\x05\x81Y\xbc\xa9\x98W\xcd&K\xf0\x02!\xc9\xb38Mo \xcd\xe39\x1dP}\x9dC\x92\xcdIA\xe1\x9b\xd50\xcb\x8b\x84Tt\xc8lL\xdc\x07\xc7\xb0\xa5\x98\x9fX\xdc\x19\xf9\x0b\xd3m\x1bR\xf8 h\xe2\x9ci:\xb0\x9a\x9fRq\xbb\xe0n\xa7\x06\x05\x122H\xe7E\x99\xcfHU!o\xc6\xc3\x99\xfaUt>c\x7f\x1a\x15B\xf4\xeb\xa5~\xe2T\x92\x7f\xe3\xeb\xf2d`\x12\x8c\xa1QSa?\x1d\x12{\x0cSY\x80\x7f\xee\xcf\xd8\x15\x80Y\x07L{X\xb0\x1e\xfaB\x05\xe5\xde7\x17i2\x93\xf1\xbb-\x96)sa,k=[\xd4\x9237\xf3\x85\xf9\"\x14@\xab\xa1\x17E\x9eq\xba\xc3\xd2O1\xac@\x82\xa4d\x1e\x84\xb0\xd0\xb6\xa3\xbfk\xfd\xb1'\x07<\xc3\xd8xvS\x0e\xe0\xc0]!\x1f\x99\x19\x00\xb7\xa6\x12\"r\x84;o}\x93\x82\xfd\x06\x8e\xe0\x95\xb1\x89\x0b*\x82a\x13)\xfe\xab C\x00\\9\"\x89w\xf7d\xa5\"a\x16\xc2E\x08I\xe0\x88\x08\xc6C\x8b\x1bK\xe3\x92^\x07!\\\xdb\x8f.\xb7\xfb\xfcf\x95\x07N Ud\x1c\xce\x08\xa2_X\xdb%\xd6\xcf\xcd\x81\xf8p\xcfD\xe6j\xdc\xed:\"\x83\x8e\x0c\xc6T\xb5\xaf\xd0n{_Q\x96\x7f\xe0\x01\x020\xd4D\xa3\x9191\xd0/!V\xed; '\xaf\xcb\xddc/\xa7u\x8f/9\x0b\xfb\\\xcek\xa1;@\xeb\x98\x9e\xb7n\xeb\xa7F\xf7\xa0;\xde\x93\x10b\x1dD(\xac\x14N\x8e\xb9\xa5\x0d\x86c\xdd\xe0^\x1b\n\xee3\x8ffq\xf6\x9el*\x9e\x19\x8a\x8eb\xd3\xc92C\xc5\x0b2\x8bg+\xc2v:\xad\xa1oQP\xf6M[_6\x8f\x9e\xff\xf9\xe4\xf9\xff:\xfd\xe95\xaa\x16\x99\xf6Q\xdf\xc2\xa6\x97\x93c\xc4\xc7\xe2t\xd8D\xf9\xa6&\xe5\x9f?\xbc\xfe\xd1\xd4Ke\x1b_\x08\xdd\xa8\xbc\xa2\x88\x13b \xb5Q\xe1\xe2Y\xaf\x16\xe9\xba\x90\xa9\x97O\xe2\xce)\x94\x9e\x94A\xa8\xfaWf\xcc\xb1r\xb0e\x10\x8c\x80H\xf5\\\x06\x9c\xe1\x91\xbf\xe5j\x1b\x1c\xec\x85P\xc0.\x1c\xec\xa1S\xf4\xc7\x0c\xfc\x8a\x94W\xa4d\xd5g\xe6\xea\xfa\x99\xe9tWtg\x1dx!h\xaee\xfb4\x03\xb5K\x86F\x0e\x19\xaf\xdd\xd3\xef\x19P\x81\x07\x98r\xd5\x90\xe9'\x94GIV\x91\xb2\xfeP\x12\xc2\x1c\x1b}F\x9d\xe81`\xe4\xd3.X\n\x80P\xb3\xd3kE\xab>\xf2:\xefG|\xfa\x85\xf7O\x87\x8f\xbe\x0d\xf4\xcd\x9b\x8f\xa5\xc6\x0fH\x03$TM*\x1a\xe37|\xed\x98\x95@\xd9DS}\x1a\xa01\x8fN\xb9l\xd0A\xb1\x060\x00\xeb\xb1\xf6;\x98\xc8Z,\xe4+\xcf\xeb\xd7\xb3\xf8\xfb\x82\xab\xbb::?'\xd5\xeb|\xbeI\x89F\xcd\xc3C\xb2f~\xf7\xea\x0d\xc3\xe7b\xbc|4\x7f)\xd5f\x8e\xa1\xd4Z\xd8\xcd\x859\\\xdb\xb4\xeeV\x1d\x0d\xaf\x83r>\xff;\xaaVqA:f\xd3t\xe7\xce\xca\xe4\x82L\x94\x8at\xfa\xa8\xc2\xfa\xc7&)\xc9\xbc=\xe2yR\x15\xf4,v\xfe\x80\xf9\x94\xd5C=4+\x10\xdc\xe1\x12\x84-8\x98\x11W\x7f\x0b\xcd\xaf<\xc0\x14\x16I\\\x89\x90\xb2\xccK\xf5\x8e\x04\x1f\xf4\xb8.\xfd\xddt\xbd*\xf3k\x8c\x80t\xc2\xbfj/\xa9\xde\xbc\xdb O\x95\xcb\xe4\xc7\xdd\x1bJ~\x9b\xdc\xb3S\x14\xa9\xae\xba7\xa41\xaf\xdf\xc5\xde\x0d\x7f\xdem\xbf\xe2\xcf\xbb\x17\xc0\xfc\"\xb9\x97^\x80_$\xf7\xd2\x0b,\xf8\xf3\xee\xc5/\xbbH>x\xa2\xbbH\xce\xfc\xc3\xc7\xddy\xb1\xfb\xe3\xfd\xc3n\xfbW\xbc\xfd\xee\xb5\xfa\x9a_\xabw\xdbY\xf2\xe7\xddy\xb1\x1b\xe4\xde=\xf4\x05\x07\x7fw\xba\xe7\xbc\x99\xeep\xae\xf9\xf05W\xc4\xb4zw\x94\x9f\xf0y\xef\xda\xfa\xb4\xafN\x7f\x0eG\xddh\xda\x97p\x04\x0f\xdb\x8f\x9eQN@\x04\x00|V.\xf1\x12\xa9:\xebD\x18|\xab\xd6\x12\xa1\xeb\xba\x95\xde\xa9\x950\xf4n\\\xe7\xa5\xa9\xf6\x07\xb5\xb6\x88<\xd8\xae\xf2\x9a\xdfb\xcb\xdf\xd3gg\x94g\x9b*\x03.\xe3\x9b3O\xf7\xf4\x87\xcdbA\xca\xde\xbb\x17q\x1d\xff5!\xd7\xbd\x17<\xc7\x87\xee\x03\xd2{\xf82\xcd\xe3\xfa\xf0@\xdf=\xbe|\xf4P\xff\xf2UV?6\xbe\xd9\x7fd|e\xea\xecu\\\xf4\x9e1\x17\x14\xf1\xf8C\xe7-\x8b \xd8\xfb\xe8\x94\xd4\xfdg\xc8\xdf\xf5\x1f\xdf\xac/\xf2\xb4\xf7\xf8\xa7\xc487|\xf5<\x8d\xd7\x05\x99\x9bk\x98\xa6O\xdf\xb5\xe6O\xc9\xbc\xf2\x1e\xc9\xa8\xf8\xeam\xe7\xe3\xbf\x91\xf8R\x02ig?\xd4262,\xef\xab\x10~\x0e\xe1M\x08\xefu\xb7w/B\xbc\xbb\xc9\xe0\x1e\x9c\xf6\x99\xeb\x9f\xf8\xab\xe7\xfdW\xff\xe0\xaf.\xdb\xe7\x03ei_\xe1%\xee\x0b*\xb5\xc31\xbc\xa2\xe3\x90#\x98\xd0\xdfA\x10\xaa\xda\xd3\x17R\x84x\xd1ol\xe7Z\xcd[\xdaa\x9e\xe8\x0c^\xe2\xbdBWJ\xa5\x9f\xbe4\x89\xc1thW~M%\xee\x1fe\xd3\x18\xd5\xf7E\xf7\xe02\xc4\xbf\xa5\x1d\xff\x13\x8e`E[\xe9\xbd\xa5\xe5\x078\xa25\x8e\xe0-\x15\xb8\xf1\xafwz\x05\xc6\x85:\xc1\x8a\x8e\xe2G\x83\xaa\x03[\xf9 \xdb{F\xff\xfa\x01\xb5ToLr\x81\x98\xeeO\xac\xee1\xfcr\x0b\x13Xv'\xff\x13\x1c\xc3\x82v\xbd\xf1_0\x1d\xe7\x04f\xf4w\xcc\x7f\xf7\x1a7\x82F\xf4\xba\xf3z\xfa\xcf3\xd9\xc1\x1b\xee/\xfb\x8bA\xefH\xc7\xb8\xa6\x1d\xfe\x93N\xbf\xdf\xdb\xef\xcc\xbf\xde\xa3\x0d\xde{`!\x18\xcb\xa0\x8f\"\x7f\x85#x\x8f\x9aj\x1d\x9a\xfcU\x0e\xf2\xaf\xfd\x97\xef16#bF\x88~\xed\x0d*\xca\x08`\x92}\xe9\xd9t\x00\xde\xdcbXC\xbf\x14\xbb\xb1D&\xe7}\xd7\x12<\x08u\xe8\x7fn\xeb\xd2p\x9f\xf3\x02\xc7\x9d\x87\xa0t\x9c\xbbvLa\xf6g8\x82\x7f\xc01b\xc6\x1c&P\xc0\x04\xff\xbe$7\xd5\xab\x0c\x03\xe2\xf6:\xfd\x1b\x1c\xc1K8\x16{{\x02\x7f\xee\x01\\h5\xfd\xbf\xd1U\xab\x15\xde\xcf4\x93\xbf!5)1\xc6\x13z\xe8\x9e\xa1%\xfd\x0b\x9c\x8f\xdb\xec\xe4\x93\x91\x1c\xe7\xc1\x93.\x87$8N}\"\xaa\xef\x1e\x8f\x9669<\x12\xe6u\x81W~;\x18Z\xbc\x95\xeb`\xe4\xb8\xf7\x1f\x1b\x92\xc2\x1ety2\xce)?\xd6g\x85=x\xd2}\xbei\xc2\xf62\x0f[\x11A\x97\x1d\xa0\x15%#\x83\n\xdfV\x94\x8d\xe9\x19\x8b\xb2\x81\xce[\x14\x04<\xcc\xc6\xb0{{{}a\x02\xb1\x1e\xe8N\x06\xc1\xeab\xeb\x81v\xd8cX\xb9{\xd4\xf6\xab\x8d\xcb\x9c\xb4\xaeuG\xae\xf0\xe3\xc7z\xcc<\xec\xc9H|\xb0\x8f\x0f\xb7\x1dl\xe2+\xa9\xa0\x99\xc9\x18&\xec\xf7\xbe`\xf0]4\xcc\xa5\xde2\xfed\x1b\xa6\xfeF\xa3Q\xa3@\xaeZi\xd7\xa8L\xe1Z\xc6\xfb\xb0\x0f\x13\xc0\xe0\xfd}\xe2e\xbdc\x93\xa8KA\x1a\x0b\xb9\x82\xc5\xfd\xbc\xbf\xcf\xaebs?i:c\x1d\xa1\x14\xc9\x82\xf7o\x82\xa7\xb0\xbb\x1b\xc3\xf7\xb0y\x1a@\xc5\xcd\x11\xa65\xecB|\xa6?\x17Y\xe3\xfawr@\xa9\xec\x816\xb5/{\xa9\x9f\x06\x90\x8a^L=\x08\xf6\x87\x05\x0c\xcd\xfc\nS\x8a\x11\x96S3\x04\x9d\xdeo\xfb\x85\xefn%a\x0f\xbe\x1f\xf8\xa5\x01A\xbf\xc0\xf7\x91S*\xa6\x15i\x12\xab\x87\xe05*\x16\xaf{Y\xce\xb3\xd3*w1\xb7\x81A\x05c@B\x0d\xd5\xcbzZ\xae\xa6\xf5\xa7=H\x99\xf7$\xea\xe2\xd9\x0dV3\x05\xc9\x1f\x90\xfe1^w\x04N\xd1\x884M\xe9/\xafr\x9b\xc0\xbc^,q\xdayTs\\\x11\xb4\xdedQ}\xc94;3\xd8\xdb)\xb0\xa4k\xd9\x80\xc2\xcf\xfc\xfd'\x07\xc1\x17h\xcf\xbe\xf6\x92\x1bM \xf54\x03\xc3\x88\x18\xbd\xa4\x92l\x91k3\x87\xd1\x92\xe6Km\xee0\xc0\x94\xb5e6\x81C\xfdKT\xdcM\xe0a\xef\xa5\xc659\xb3\x1ao\x82\xb2nSrF\xb9\xb6\xfb\x9a\xfb\xd0~\xd3\xccOs\x96g\x8bdYEi\xbeDs\xc0~=F\x02J5\xdb\x00\xa8f\xa7\x89\x8d\x91`\x97Z\x92 \xcb[\xafDR\xc5\x12\xfe\x04\xfb\xa8\x87f'\x00\xa5\xca\x94\xb0\xee?\x05J&\xcb\xa7\x10\xef\xee\x06\x94F\xd2\ngjkZ\xb2\x89\xa0\xfa\xd3\x91\x12\x92\x95+M\x83)9\x8b\xe2\xa2H\x11\xe5\x06\x0d\xda\xc5\xe9\x1a\xd1\xb5D\xfd6&)f\x17\xee\x1e}\x88\xf7\xb3\\/\xdb}\x8fOY\x05\x8aD\xbd\xf7\xf4!{\x8d\x18\xd8{\x8fO=\xad[>^Vc\x0e\xa8\xca\xe4\x17\x8f\xa8\x99\xf4\x91\xc00]\xa7S\xc2\x9a\x07\x8e21]M\xe3\xd7\xb9vpc\x8f\xc4\xc6\x978\xae\xa5u\xfa\xb3\xc0\xc0`\x90\xce}\xc4:\xbe$\x7f\xae\xeb\xc2\xa7\xc4\x97\xbc\xa4\xaf)Y*\xf2\xaa\xc6\x1f\x06\xd5\xc3\xc5&I\xe7\xef\xc9?6\xa4\xaa\xd5\xe6\xd4\xe7\x06\xd2\xc1r{\xab\x1f\xf1G\xfa\xfa%\xa9\xf2\xf4\xaaU\x9f?\x1a\xac\xcfMM4\x9f\xf17\xfa\xaf+R&q\x9a\xfc\x93\xbc'\x95\xfa\xad\xfa\\\xffe^\xbc\x9a\xab_\xacHZ\x90\xb2\x8a\xe8\xf3\xbbEc7\xdc\x91\xc4\xad\xd6\xeb\x0c\xf0\x84\x9e\x96\x8d\xfa\x84\xfe\x10-\xf7\xe9\xd1\x15w\x1d\xa1\xb5\x8cGQ2\x81\xd2p\xd2\x98\xa3\xe3\xf2.'\xba\xa8<\x1aM\x8e\xe0C\xe8h\x91+\xc8\xc5\xa0Q>W~\xa1\x97N\x94r\xcd\xa7|a\x00=\xf0If\x1anF2\x15k\xceNDx\x0d\x83\xe7wGp\xd0\xb9\xdd\x00^\xb9\xe5\x9c\x7f\xf9\xfc\xd9\xc0A\xb0\xaf\xf5\x90e\xfb\x7fS\xc6\x17)\x19\x00e\xb6Y\x13Q\xc7\xc0\x10,I\x8f.\x01h\x82\x10C\x1d\xd9On\x01\xb0\x1e\xbf\xa8\n\xe9\x96#\x9f\x88-\xd3\x1f\x138Dl\x11\xad\x8c\xc0\x9d:\x9a\xfbY\x08^\xcc\xfd\x8a\xb3\xfe\xd4s\x17\xfb\x18\xde\x9c+\xef\xdaO\xbdRG\x05KL\x05\xb5_Gt?\x1f\x1c*\"\xaf?\x1d\x1c\x82J\x072\xff\xe1\x81\xf2e8<\xf8\xce\x97\xdfn\xfbek\xb4\xe3\xbe\xdc\xba\xcf\xc3\xc3\xc7\xe6O5R{\xfb\xd0o\xbd\x92$\xb2\xd4c\xb7@-\x0dr\x13c@\x1fy\xf6\xdb\x93T\xea\x07\x93\x1b\xf1M\xec\xb6.\x1f\n\x7f\x82\x83\x8e\xb5x\xc3\\\x1e\x9c\xc1q\xfb\xe7\xc4\x98\n\x8d\xb29\xbe\xa6\xf5Cc\xeb\x87\xed\xd6\x0f\xcfP\xff\x1eDW\x07o\x0bRbL\x9aWh^\x12\xd7 \xc6/\xb9y\x9d\xcf5\x1e\x9f*\xa8[\xa9\xddTE\x0b&kP,\x10&\xe8\xf87\x13\xf4#\xf0I\x10\xb0(Qy\xd39s\x84U\xd2r}\xac0\xc7\x96\x174\x86a\xab\xf6'\x01L \xe1W[\xfaE\x1e\x9e\x9e\x9e\xbej\xfd\xc5\xcc\x02\xc9@8K\xdd\x12\x8dC\x00\xfb\x12\x99\xc8\xad\xc0A\xbfnG\x84\x80]\xf0\xce1}P+QZ\xb5\xf3\xff\xfd\xfe\x9b\xff\xf1\xf7{\x7f\xf4\x83\xf3\xdd\xa3\xe9/\x1f\xcfn\x9fN\xbe\xff\xd3\xe7\xe8\xe3\x83\xe3\xf0\xe3\xc7?x\xde}\x96<\xed\\g\x99\x0b\x0df\xb0\\\xe8\xcc\xf3\xb0\xb1\xa1\xdbo\xfa\xad\x95~}\xff<\xf8\xe5 \xbc\x0dD\xd3J\xe6\x12\xff<\xf8\xa3@\x80\xe6\x83\xe9\xf9Y\xf0\xc7o\xf8s\xcb\xc6UF\x851X\xe7~M\x87\xd1\x0f\xa4nX\xdc\xd8v\xa0\xf0\x06\xbd\xfb\xfdtL\xa667\xb66+N\x1fw\xf6\x90\x03q\xc6\xc4\xcaDWA\xdc\xc1\xb1\xe0Vb\xcf\xeel\xb3g?\x7f\x86\x1d\x12\x15q\xbd\xaa\xfa\x8du\xaa\xb3jC\xb1-@Qs\xf1\xea\xfd\nR\xb6\xcf!\xc9\xa0\xd4\x9b\xa8*\xeaXZi\x9a\x1b\xa2\xcc\x03\x87\x85\xf7\xee\xd9\xfbg\xafO>\x9c\xbc?e\x83O\xa2:\xff\xa9(laSD\xb9\xe2\x0eg\xb4\xa7ibP\xa6\x8aB;\x8c\x07\xe9el\x83}\x1cX\x87\x04\xd0\x18j\xdbk\x8aR\x15df\x8c\x13\xa6+t\x95XX\xd1\xdc\xfd\xa35\xa9W9\n]-(\xbb7 i\xfed \x9c\xa8Z4:(]\xc1\x0c4\xbe\xc9\x06]-(\x85\xa1W\xb2D\xe8\xcd\xe0Gz\xa7\x97\xfe\x9b\xf6\xaf\xadT\x96\xa0U[b\xe3\x9a\x0bp*g\x95~\xe6\xef?\xee\x06\xff\x00n\xb6\x86o\xbby(\xea(\xa9\xde>;=t\x125\x98.$/H\x16\x17\x89\x91\x89\xe0Y\x15(\xae\x17\x0d\xae\xd3\xc9\x1ez\x1a\x16<\xa9N\xaf\xe3\xe5\x92\x94\x07#\xc6P\xb1O\xb6\x18\xc3\x81n\x0cy\xf1j\xce\x12\xf0\xd7Q2\x7fY\xe6\xebwq\xbdz\x8d\xf8\xcd\xdcI\xeb(%\xcbxv\xf3\xaa\xff6\xa6o\x97\xa4\x96\xc7\xf9\xfb\xf8z\x84\xf8\xc2\xd9[F}\x8f\xd9Ib\xd7\xd7J\xc9/\x12[\xd7\xbc5\x18!f\xbb\xd5\\+\x11\x8b\xcb&\xa1\xdf;x\xe2$\x83'Nb\xa3z\x89\x12\x19i\xc7p\xef%H^\xa2\xf2\x85\x83\x0c\xca4\xf7\x13\x19\xf0\"\xf6\xf9\x1f\x9b\xb3\xa8\xca\xd7\xc4\xb7\x03\x14\xba+\xc2\xee\x16\xb5uu\x91\xd7\x0c\xd9\x10\xd0>>\x9bK\xdc\x80#\xd8\xd0\x87$\x9e\xad\xd4\x87\x15\x8b\x93Q\xaeQ\xcb\xc5w\xc4\x98\x0dQ\x90\x99~mY\x005D/\xb3\xd4\xa1\xb3\xd9\xc1\xb5F\x96\xaf\x8e\xbe\xf9F\x8emn\xba\x8b\x82\xde\x89m\x0c2+\x0e\xda\xccx\xca\"\x9f\xbd\x17\xc2\xa2uZ\x0e\xac\x9d\xc0\x18\xcc\x92\x15\xafIMJ\x0d\xdb!\x8a\x1cgE\xc7\x19\x07\xb0\xe3\xb0\xe7D\x91r\xe0\x948\xf0\x08;\x9did\x0d\xf6{\xb3<\xab\x93lC4\xa9a\xd4r\xc5]qs\x9f9\x7f\x99\x9cqE\xa1\xddj\x83\x02uK9\xad\xa8tB\xffc\x91\xca3\x8a\xc6\xf8\xf4\x08\xa6\x99ev\xc0\x87\x86\x87\xcb\xb4r\xa8M\x076k\x84\xa6\xfd\x00f}{'\x13\xbd\xd4\x15\x12\x9d\x9f\xe7e\xb2L\xb28U\xc4)\xe6\x96\xa1}\x83\x12\x8cBT\xc2\xf6O\x96\xb7\x9f%L\xe7W\xed\xd6\x81\xe8\\\xab\xbbE\x86\x00Td\xc4\xac-\xf4\xba\xcd\x98\x02\xbc\x80#\x98M\xf7\x1c\x00NKa\x84\x91\xe9\x0d\x15P\xda0*:0\xaa\xac=\x9b\x19%\xfb[\xe4\xe5\x9bm\xcc\xce\x18\xeb\xb6\x04\x0e\x9d\xb9%U\x84ZV\x06\xda\xd7-\x92^\\QzQ\x07\xe0\x15e>\xdf\xcc\x08\x1f\xdc\x15\n\x02\xb3<\xab6\xeb\xf6\xb3\x8a\xcc6eR\xdf\x88g\x9f?\x83\xbf\x9a^\x9d\xa1\xb1\xdb\xd5Y\x08s\xb6\xf3V\xba\x0ca\xddB\x01\xb3A\xc6f\xa5\x909v\xa64\xed\xd0\xbf\xb97\xa0\x03\xc8\x80\x83m\xcd\x14\xf5N\xf5\x81{\x18\x98\x14\xe1\xbar\x03G\\Ab\x9f'X3pt\x8b\\\xa0\x8b\x10\x9d\x16(\xd1M\x1b\xa2;\x0f\x9e\xc2\x8eO\xa7\xe8_\xc0\x11\x9cG\x19\xf9T\xfbA\x10\xcd\xf3\x8c\x04O\xf9\xe4]\xc1%\n\xed\x8f\xb2z\x17,\x00\xa8\xdb\xbcD\x91#>\xa1(um'3\xdd\xc2n\x90N\xce\xc6\x8eZ\x94\xde.\xa3\x0c\xcf\xc9\xb6\xad\x01\x87\xc7\xa7\x91h\xa4+\xa7#QKW\x9e\x8fD7]\x19\x87\x82\xba\"\x17\xf92D\xa7\x95\x0eZ^\xd3\xe5\xa3\x98I\xa1\xe6_\xc2\x11<\xebb\xe6'\x8e\x99;\xf6\xab\x981\xe5\x8a\x87\"\xbf\xdc\x06uu\x85bb\x87\xd7v>\xc5mE\xde\x1be\x1e\x81\xb7\x19*p\xc4\\\n\xc4\xbcq\xfe\xd4q\x9d\xac\xb5\xb6\x150n\xfdJ\x0f\x1b\x8d\xf9K\xef\x89<\x89T\x85\x08G\x8e\xceMQ_E\xbb\xe0J\xd8\x87\xdf\xe9T\xb4\x85P\xd1\xf6\x82Z\x03\xf7\x17\xb6k(\xf8\xf0\x98\x07\xa4b\x11\xa1\\\x15rs\x08\x8d\x06\xab\xdf\xe9jL\xa7D\xb9w\xfc\xfb\xc7\xeb\xb3\x07\xcb\x84]\xfe\x0d\x80u\x9c\xe9\xc1\xe3'\x036\x16\xffo\x98\x1e\xdc\xcd\xd5s\x9a\xc7\xf3S\xa3\xc2\xb0\x94\x9c3\xd3R\xd0\xe6\x0d\xe9\xdb\xf5\xc9\xc6\xe4\xdb\xcb \x90(\xbf43\xf2\x9b2\xa5U6e\xca\\\xc5\x8c\x15\xab:\xae7\x15\xe6$\xc1\xbfl5Y\x8aPQ\x9b\xfe2\x7f\xb1\"\xf1\x9c\x94\xd5\x04\x12\x9fD\xfc\x87\x81B\xe8\x1b\x89\xe1\x08r\xf1\xe5\xd4\xe3y\x84\xee\xd3\x9d\xe7\x19\xf4\x10\x1b\xccC\xf9\xf93\x9c\xfb\xb1\xd9\x0f\xca\xdf\xa0kKM>\xb1\xf8\xe5\x17i~\xc1\x14X\x17\xe8'\x1e\x88\xcd\x1c\xd5+\x929(\xb9)\xc9\xceY{hH\x97G\xf3\xb8\x8e\xd9\xdf\x9b\xc0r\x00]\xf5\"\x01;(\xea\x84\xa63.\x8a4\x99\xa1\x02\xe9\xc1\xcf\x15\x8bO\xc1\\w\xfer\xfa\xf6MT\xc4eE|LA\xb4l\x8c>\xe3\x05\xf91\x8f\xe7C\x0c\xf4-\x1d\x85\x0e\x84\xa2\xe4\x98\x01\x01\x8e(\x85\xc8\xa3\xfc\xe2g0j\xf5\x9dX\x83\x9c\x8d\xf5\x84\xdbl\xeb\xb9\x01\xfd\xe9\xc3a\x91\xf7\xa9\x83\x9b\xe1B2\x9cT\xaaO\x19\xf6\x8c\x94a\xafM\x19\xf6\x18e\xd0\xe3\xaa\xce\xbf\x04\x94\xa5\x15\xe3SC\x8e\x10\xa1\xd6e\xf6@:\x1d\xaf\xf9r@ \xba9\xcd\xe8@\x85\xbf \x9a\xfaGI\xc5\x1d\xa1\xa6\xd9Y\x00\xc7\xac\xd2\x04\xa6\xf4\xff\xb3\x10\x7f\n\xb9\x8b\xe2\x93\xf0U\xd1@\x1d\xf1\xb7\x1b,s\xc0ld\xe0\xa4\xd0Gfy\x99\xf0#C\xc4\x89\x13\xcfd\x9c\xd1\xa3\xadl\xaeVm\xfb\x0dS\xe0\x17\x12\x15I\xf1\xa5\x06,\xcdM\xe3,Oy\xd6\x9a\x97\x98\xf0\xcc||\x90(N\xd3\xfc\xfad]\xd47\x18;\xd8|||\xd9\xcc\x8fE\xf2\x1dJ\x1f\xf5WX\xdd\x04@es\xfdb\xc8\xc8\x1f\xfb9\xcb\xdfp\xc1\xa2k\xa8 \xcd\xe5\xd7y\xff\xe3+\x91~'\x9b\xe5s\xf2\xd3\xfbW\x86\x80P\xa0p\x92\xa8\xcdM\xb8j\xe8\xa6\x99]\x1eX\x1dma\xd0\xfc\x16l\x81\x19\x95\xcf;\xf7\xe4:\xee0\x08\xcdW\xbe\xb9m\xa9rfd\xd4\xde\xbf8C\x97G\x18\xfe\x1d\x8e!\x8f\xd6q\xe1'A\xf4s\x9ed\xbe\x17zt\xf3z\xebMZ'\x0c}\xd4J0\xe9\xd4\xd7\x03`V]M\xc0\x0b\x0d\x06\x99\x15\xbe\xfd\x1f\x07{\x86\xf75{\xbf\xf7\xc4\xf0\x9en\xbfj\x02\xdeg\xaf\x0fP\xa4^\x94\xe9\xc0\x14\xd0\x9e\xe7\xb4M\xab\xe1{\xe0\xceU#\xda\x02\xce73U'7Dx\x85\xd1\xd64\x1b\xb8>\xa1\x9bvg\xa7\x8c\xaa\xcb\xa48\xa1\x88\x9ed\xcba\xab\x82\x9c\x87\xeb\xefo\x0bc\x88V\xe0l\x95\x1d\x83EQ9\xf6/\xa2)\xc6^ny\xe2\xbf\x9d6\x82v\xa3Q\x88\"6\xf84\xa1\xc7\xcf\xc6\x8f\x8d\xeeJ\xa2pc\x1fC\x1a\xd2\x10\xf2 \xd4\x05v\x0e)Oo$0\xeb\x86\x9dB\xa90Y\xa0\xe1\x91~\x14l\x85\xcc\x0e\x0eI6Of\x14\xa3u\xf1R\xbb9o`\x00\x8f\xd3\xdf\x8e\x95Aq\xc3*\xf9\x08\xee\xd4\xf3\xd0\x9d\\[=\xc7\xd6\xfe\xb1!\xa5!\x8203\xa9Y\xe4\xe5Z\x7f\xd0\x0c\x86fM\xfb\xfb9 \xc6X\xb3@\x83\x04\xb1\x9fL\xc9\x19;)\x07\x10|`3\x168\x15\x83\x8c\xc3d\x12\xf9\xf29\x7f\xf9\x01_\x9a\xed;P\xe8{\x80\xf4\xbb\x88\xcb\xfa\xe3\x03\n\xa9\xfbT\"y\x90D5\xa9j\xbf\xb0\x9a|\xf08j\xa6\xf8\x9d\x80J\x04.\x01d\xe4\x1a\xe6\xa1\x06\xa8=\xf6\xd4*\xd6\xb06\xa3\xb8(H6gAu\x92i}\x86\xf6\xbdC\x00\xd6om\xa6\xf4\x94\xe3\xac\xfc\xc40\x1d\x1ez\x98\xe1T\x7f\x07j\x91L\x1bq\x058\xf8V\x98)\xb2*\xd2\xa4\xf6\xbdco\x00\x01\xae\xa0g\x0b\xbc\n\xa1\x1b\x8aB-K\xba\x9b\xa6{\x03G ^ O\xf7\x07j\\\xa0=\x86\x19\x85nl\xf8q\x8e\xe9\x96\x04 db\xe6\xcd\x00\xb2t\x90#\xd7 \x87\xeb\xa6\xe3\x8bu>%f%6e\xab.ZCl\xa8\xf4\xf9PFmP\xa9u?\x0b\xa7(&\x8c3\"\xc4\xb5-\x9d\x8d(\xf2fSG\xb0C\x96\x0c\x08\xcfG\x12\xb0l\xbf{O!\x83\xef\x81<\x85lw7\x10bYC\xb8\x87\xac\x8d\x04gRG\x8b$\xadI9~1\xccZ\xfb[\xc1O\xde3\xb9@@\xd3LI\x8f\x84c\x0fv\xf1(\xf7\xfal\x1d \xa3p\x11BE\x99^}{L\xe1u\x04K\xd8\x85\xeb\xb0\xd9\xd4x\x928\xecj\xed\x94\xbe\xb2\xc1q\x08uT\xad\xf2M:\x7f\x91_gi\x1e\xcf\x9f\xa1Z\x8deg%\xe9\xc2p\xdd.\xed\xc3\xfc\xcc?\xe8eK\xa4Eh\xc5\xf7\x86\x94\xe2Z\xa3\xe6\xb9\xd0\xa7\xeb^\xae\x1a\x8b\xe7\xfe\xcb+\xf1Rc\x0f\xad\xba\x1a\x0b\x9b`\xf9\xec\xcf\xec\x8c\x136\xc1l\x07Ri\xf8m\xf9\xbf\xe9\xea K\xce5)\x97\xe4U\x86\xcf\xde\x96\xb4\x02\x1cA\x8ao\xb8\xc3\xb7C\xc0\x1bh\xd6Zz\xdf\xd8\x11\xdf,\x11\xb2]Y\x7fq3\xda\xfa\xb2E\xad\xfb\xad(B\xf2\xeeg\x90a \xbaK\xab\x9b\x03\xaa\x8c\xf5,2\x08\x82\xaa\x01\xbf_\xf2\xc8\xe85\xfe\x95\xf9\xa4\x97\xa8[6\xd1F}Z\xf9\xe0;\x8d\xc5\xfdZ\xa0\xb5\x169\x97\x02\xc5\xbe\xd5\xbd\xbd\x11\xdf\xf6Ru\x02?\xf5\xe4\xae\xd2\x83\xa3\xed(op\xda\xe8\x83a\x02\x9a\xf4\xee\xdd\x1d\xc0\x8f\"\xdbI \x88?=2\xaf\x14S+y\x94\xad\xe3\xf2RRj f\xae\nUL,!\x17Kn\xa0\x97\x01\xf6\x8d2\xc0~[\x06\xd8?\x1b\x08C(Ng9\xcc\xeb2.\x1c\x0f\x14\x16\x82\xfdi\x00\xd5u\xc2T\xc5QQ\x92+\xe4\x8d3\xf2\xc9\xca6\xce\xe2\x8a\xc0\xded\xb0\x0e\x08\xd3,\x93\x10[\xdb\x84X\x91\xc2\x1e5\x02\x14\x96u@O\x1c\x0c6\xbf\x92\x04\xac\xf9\xfb\xf3gL.\xa7\xdd6q\x10\xc2N\x1c\x95,\xa4\x04\xa6)\x9b\x91\xa2\xce\x07w\xb9Z\x18`\xe0\x08\xf6\x1d\x0d\xb1.J\x12_Zk\xda\xef\x87\xe5\xb5$\xef\xff\x11\x9d~\x7f\x1e\xda\xfb\x17\xb5\xe0\x9a=r[3\x12\xd5{\xcc\x1c\x9fdu\x08\xf4\xe7h8=\xf9u\xc1\xc4\x87\x1c;\x00\xe1\x89\x1d\x08,\xe3lmYjlm\xdfa\x1f(\xa7_<$|\xc6&\xe13\x1c\x96/y8+\xce\x81\x19\xbb\x90<\x9a\xb1\x1f~\xb8\x88\x08z\x92,\xec\x1f\x86\xca\x0ex\x14\x82\x8f\xf9\x1eJ\x8c\xed\x82\x071\x06y\xa1O\xcbt\xf8\"\x0b$\xe0\x1c\x90Q\xb2\xab*2\x8aa<\xa1{]=@|\x16\xaf\xd4\xadw\x07,\xa0[A\xed\x1a HU\xe4YE\xbe\x84\x82\x1c|\xf7\xebn\x8d.\x0598d$\xa47\x13\xa3\x0eP\x14\x84\xdc\xc1\xa1\x1b\xe4HT\xef\xb7\x89\xc8\xfexP=\xfauA\xc5\xc7l\xc9\x0f\xc3\xc0\xe0\x82\xbe\x8c\x8c\x18\x9c\xc3Da\xcd}goN\x82\xe5\xd0\x01\x83\x10$.\x1d;n\x04I\x0b\x0e\x9e\xe0b\x1e\xb0\xbb\xb4\xb8\x9e\xad\xfc\xfd\xc3\xc0\x10\xafFW\x9ai\x1c\xda\xa7\x01w\xb8\xba\xcc\xc4\x8b\x8e\xdd\x01.\x87\x0eh\xce\x1a\xf4s\xae\x94c\x19%J\xc5Z#\x08\xf8\x8f\xe7\xf9\x1c\xc3\xc5\xf2\x9fL]\xc5L@ \x97{Q\xde\xc6G\xf5A\xa8\xbb\x99S\x0b\x1b\xa5\x03\xda \x19\x8b\xf2\xcb\xd1\xeb\xf3\xd0\x02'Q\xeev}\xf0\x16\xd1\x0d\x9c\x89\x0e\x9c\x89\x04'}\x1cv\x93\xcfw\x0b\x82\xf1\xe1\x81\x1d\x8c\x92\x8c\xc6\x17\xe5\xa6\xa8}\x8f=\xf0\xc2^ \xefna]X\xf0 +y$\x9b{#\x86R\xd5y1`\"\xa9\x07\xf9-K\x93\x871S\xa7\xc6o\xa7\xf4\xcc?x\xa2\xd7\xf9i\x02\x18\xdc\xea\xd4D|\xa0v\x85t\x03\\\x16\x92\x10\x07'%![(\x8d\xdbnVB\xa125*{\x06%B>\x98\x07\xfe\xcfU\x9e}\xfe\xb4N?\xdf\xc4\xeb\xf43\xa6\x00\xfdx\xf1\x80\xf1\\_|\xb9\xd3\x8d\x10\xb2\xad9\xe1\xc3\xfd\xffxk\xc2\x81\xc1\xb4/1I\xa0\x06Q\xfe\x1eCi\xe2\xd5\x97\xf7\x00\x83\xa0\xe0M\xba]F\x16\xe6\x04\x99`\x02\xddkTS\xe3\xb3\x01\x13)#\xa3\x85\xbaR\xba9\xd8\xbc\x9b\x00\xcfti\xce\x95\xa5\x19GZ5S\x991+g\x9d9\xaa#i]\x0c3\x19\xeeW\xa4\xfc\x0b\x85\xf1\xd2\x8d\xcaiL\x85\x9d\xf1\x19i\x94ua6\xca2\x0db\xee0\x08Q\xb9e&\xeb\xd4\xfaJ\xdf:zAY\xf6\xb8\x88\x9b4x!\xe1\xc5\xf3\xb9\xb0\x8a\xff\xfc\x99\xb2#\xeb\xfc\x8a\xb4\x9f0\x06\xc5\x10\x99\xc6\xb8/;\xc6Z\xa6 ^\x0d\x82\x0f\xa7\xff\xf93\xd0\xb9\"$\xd7\x9b:\x16\x90D\xc9\xfb\xc6\xd1\xd4x=\xd8\xcf\x15o\xdfo\xe0AA\xd7\x07\x80|\x8a\xb7\x16\xbag/\x08)\x9a\xe7n8\xb4t\xc0\xa1\xaf\x8e\xc87Fcl\xb3\x87\x06\x1f\xe1\xa9\xbc\xd6Z\x92\x1aM\xaf\x7f\xb8y\x97'\x19\xa5\x08\xfd\x18\xb8\x00.n\x0f\x82\xbcw\xb2\x86\x86\xda\x88\xd1\xbf3\xff\xbas\xa3\x84\xbe\xecz1t\xeb\x7f\xce_\x1ej\x0d\x06\xae\x87\xec\x10N\xc4\xa7\xda\xdb\xdcO\xe26W\xf7\xf2T|\xaa\xb5~x>d\xc3p)>\xd5:\x0c>\x13o\x1f\xf7\x8d\x18\x9a+\xdc>4\xe3\xf9|2,'\x8b2(3\x81\x90\x9b\xe8>\x1d0\x1c\x1c\x92\x9b@\x91\x9d\xb4\x154\x08\xd6o\x89\x93\x85 $\xbaw\x94\x8a\xde\xe9|9a\xb6Ny\xfb !\xf5\xba\xab1S\xba\xe8\x1a'\x8a8\x899\x19\xca\x86\xa3\xe5\xdc\x06\xdd %\xad\xb7!L\x87\xb6\xa3\x89\x9a\x9b\x0e\x1ae=\xdb\x8a\x0b\xdd\x9a\xdaV\xf1\xaa!\xb6\xe6\x11f\xcc\xeb\xf85\xa9c\x1c\x1d\xa9\x00\x83}\xadI\x8d\xaa\xcd\xb5_3\xd5B\xc7\x8f\\\xd0\xfc\xcf\x9f[xEk^\xe9)\xd7U\xc8\x9b\x15\xe9l\xafl00\x9e\x85\xf5Y\x10\xde\xf1\xc8m\xc0\\v\x0e\xc7a<\xbb\xd0\x83`)A0\x1ee\x14\x06\xe0\xc2\xc8\x00h\x9f\x8a\xdd\xd7{\xa9a\xcf\x8a\xb8$Y\x8d\xa1\xba5<\xda\x10\x83\xd6\xf1\xf0\xac\xed\xf1\xaa\x95\x84\x9aG\x98B\x17\xf1\x95]\x9b0\xbf\x97\x92\xf9\xbd\x18aE\xfbE\x9f\x18\xd4\xc3\xa2s\xb0\xa5O\xf1\xba\xef\xfd\xa3\x01\xc6\"\x8d\xeb\x9ad\x13\xd0\x04}Yl\xd2\xf4\xe6\x8d\x08g\x84s\x1e\xe1;\xbe\xf0g~\xea\x93\xae\xf6\x1a\xf4\xe3\xc8:\xddh<1\x93\xea]\x99\xaf\x93\x8a\x8c\x18D\xc1\xb5\x86s\x9f`,\x14\xa7\xb1p\xcf\xae7\xe4\xda\x117\x86\xe3\xa3\xf0\xa1\xe0}m\xa5U\xb5\x01\xb8\xa8\xdb`\x08\xcf\xc1U\xc4j&\xf7\xaeL\xd6I\x9d8kA\xdcg\xb9\xf9\xcdg\x99T\x7f\xa9\xf2\x8c\xcb`+\xdd\xfb\xe7L\xde\xed\x89i\x16\x84\x92jn!/\x9b\xb4\xdc`\x1a\x18\xefQ\xe3\x1b\x9fT\xaf\xb9&b\x02W\xba\xd7\xcf\xe6s\\\xb0\xa6\xdaZW\xed\x7f\x92\x8c\x94q\x9d\x97#\xe6\xf5\\\x92d\xe5\xfb\x97\xcd\xd7ns\x13\x1fL@\x93P \xa9\x18\xdb=\x81B\xf7\xf2\x84\xe5\xaeu\x1eq+x\n~\xdc\x1fc\xeb \x95\xdf\x15C\x1f\xa9\x0c\xfd\x9dRap#t\xa3\x8e}A\xae\xb4'\xdb~\xba?\x94fm\xf8\xd3'{\x03\x86M\xb6O\xb7\xcebw\xb0\xf7\x9d\xf9\xd3\xff`s*q\xbfw\x07\xfeJz>\x8c\xe5o\xe8;\xae\xe8k\x97\xbcv\xcfF]_\x9d\x850\xb8N\xea\xd5\xf3\x92\xccIV'qZ\xc11xI6K7s\x82&`U\xbc&\xf7Y\x9cx\x8d+\xb6`\x03\xc4z\xdb\x14yd@hB\xe7\xbe\x81Pm\"p\x9d9\xbd&`G]XML\x01\xecX\xf5\x1e\xb0\x8cyTA\x8d\x177,\xfc=\x9b\xd1\xb6&\x9a\xd0g\xc6\xcf\x06\xd2\x1b\xcd\x9a\xe5\x99h\"\x88\x01\x8aw\xaea\xe0@\x95c/\xf2\xb9>x\xa7.\xcb\xc9\xef\xcc\xbf~\x85\xdb\xbdd\xe8\xb2,\x1e\xf0\xe9]\xc7\x97,\xb7\xf2_N\xdf\xbe\x11N\xbd\xb3\x94\xc4\xe5\xf3x\xb6\"6\xbb\xd6**\xd2\xcd2\xc9\xaa\xa8$\x8bJ\xf9\xb0cB|\xeb\x9aQ\x1eT\xc2R\x9b\x17J\x10\x97z\x95\x18\x92\x99\x9c\xa0X\xd8\x19\xe0<\x9f\xe1\xf0X\x14]\x12\x84\xdd\x19,TX\xf8\xd7C\xeae\xddf2\x84;\x01\xd3f\xba0\xe0\x97~JB\x8c\x9a\xb6\x07m\xd0i\n\xeb \x01N\xd5\xb0cI\x81\x931MM\xd3X\x13\xf2>\x08\xf5\xdf\xad\xf5\xdf1\x9cN\x08~\xc7\x8f.$\xec\x85\xb6~\x9c\xa6o\x17A\xd8\x8d\xf9n\x06\xb55k\x9b\xbc\x11\x1a\xa6<\x17qE^\xe4\xb3 \x9clCi\xf8\xf0\x07IfW[\xa1\xe5\xbdE\xa1\x82\xfe\x8b\xa4\x9aQ1$c\xec\xaa\x86\xebmj\xf3\xd5y\x1d\xcf\xca\\\xcb?\x8b\xb2\xce\xe7$\x15\x94\x86W\xefGE\x01\x854\x9e\xbb\xe4E\x86\x8eos\xdc\xac]b\xf4mv\xd5\x1b&\xdb\xb8\x1d\x8b\xf2\xa5\xee\xc7\xa2\xb8\xba!\x8b\"\xcf\x8a\x9e\x07\x87\xc9\x16\xb4[\x98\xeb\xa0[\x8fc\x1c:D\x91#\xb48v\x882\xac\xf2\xe6\x8e\x1e\xe6f\xb4>\x1b\xa283D\x9d\x0f\x9c}8D1(\xd2\xfd\x00&0\xeb%\x13\xb3\x9d\xe6\xa0\x90^\xc2N\x083\x8b9\x94pl1\x1cd\x8bE\x92\xa2{W\xff~\xde\xc4\x8fT(\x8c\xbe\xee\xaa\x1d\xb0\x0b3\x17\x19R\xdc\xb1]\xd2\xa3E\xfa\xcak9\xc66}\xd1\xd7^\xf2\xa6U\xc2\xa5\xaf\x89\xf1\xe3\x9dy\xf9\x0b^\xdb\x91\x97?g\xebr\x99\x14B\x97\x87<\xa7\xbe\xf25\x8b\xe7U\xd7\x1a\x19\x1d\xb8\xc1\x13\x89\xf8Ibd\xfai\xad\x13tc\x0e\xb1E\xbc\xd5\xbe\xa6\xffl\x04\x9d\x0b1fN\xed\x97\x18\x91\xd1\xcck\x8c\xe03\x1cy\x8c\xdb\xc0?\xe1t\xbf\x9b\xfa\xbd\xcfZn8\xf7\xa8\xb5\xb4\xe2\xd2\xfc\xbe\xe6\x15K\xbbY\x19Rnf\xfe\xd6\xba\x83\x83\xbd\xad\x93\xbb?\xd9Z\xfe\xdfZ\xfa\x1f\x18\xabU\xf6W\xdf\xdc\xb9\x10a\xe2\xc8\x0d\xfaOy\xa2\x9b\xd9\x03TAE\xb3\xb8\xa87%9\xad\xe3\xd9\xe5\x872\x9e\x1186\xbd\xe1\x04\x9d\xfe\x1b\xcd\xf2\xac\xaa\xcb\xcd\x0c\xdd\xdf'\xecYEkR^C\xfan\x06\xec\x99\xe5\xaaA\x1fx+k\x05\xde*Y\xe0\xad\x92\x05\xde*ww\x03\xc8\xa6e;\xf0Vi\xe0\xacqpkRU\xf1\x92`\xae\xc6\xbd\xb3\x90\x99\xd0\xd4\xad\x93J\xa7l7\x11\x8c\xac\xb9\x8bW\x9dUC\xf5\x05\xcf\xedC\x8f`\xf5\xa9\x02:\xfai\xd8q\xa8\x1a\xad\xf5\xfb\xed\xf12\xa9^\x96\x84\xa47o\xe25\xb1\xe7w\x90\x86\xe4S\xd2\xf2\xc7\xd1\xae\x1d;\xc4\xa5\x0b\x9d\x91\x80\x97Q\x92\xcd\xc9\xa7\xb7\x0b\xca\xa5\xfc \xee\xefS\xda\x9d\xcb\x87Y\xf30q\x0d=)WZ4BX#}$\xb1\x12e\xf4i\xf2\x1a\xb9K\x17M?\xc7:\xb80 \x1dX\xe5\x85\xa0f5\x0b\xc1\x13\xe7\x05\xfe\x10\xf9\xf8^\xb4\xbf\x98\x89\x90\xb4\xd5\x83j\xb6\"\xeb\xb8\xfb\xb4\xd5\x88\xf2\xbc\xdd\x95\xda\x0c\xef\xe8\x946\xa7\x1f{\x82cg\xfd= \x9f\xe2u\x91\x12\xefl\x0c\xc6v\xc8\xf7\xc3/ \xc3\xadW\xff\x96*X$G\xc6\xedp\x07\n\xda\xfe6B\xf3\x86~03\n\x87\x8cG\xf9\xc3`\xef\x8c\x9c\xed \xc5T\xef3r%\x91>\xb9F\xab\x8f~'\x1d!TP\xdd~E\xb1g\x90r\x97\xa4\xca\xd3+\xe2w\xb5\x82\x96}[G\xf3\xa4\x8a/R\xc6]-\xe2\x19\xc1\x00Q\xdd1\x84\x18]\xfb\x92<+\x92\xeaC\xbc\x94\xd9C\xfd:\xd0G)\x1e\xa2A\xb34!\x99\\\xc1Nt\xb7\xdfL\xcbxh\xd62\xfah\xed\xffm\x80\x91\xe4\x1e\x05\xba\x8a\x82\xa1\xd4\xa7\xf3\xa9\xc4[\xad\xb7A\x8a\xbb\xf9;\x03SY\xfa\xa9!\x8cb\xe6\xef?2\x06Q\\\x0cEP\xd4\x86\xb0[17\xf9'\x86\x00\x8a\x99\xff\xad\x8e#^s\xbe\xb7\x0d\xd8\x1ce\x0d48\x94\x82A\xae\x06CL\xe5\x8f\xe8\"\xc9\xe6~\xb6I\xd3\x90\x7f\x16\xf0X\x1f\x14\x9f1m\xad\xd2\x04\x7f|\xba\xb9\xa8KB\xdf\xce\xd5\xb7\xe4\x13\x99mj\xb4\xd0\x11\x7f\xd3\xc7\x9d\x18\x8fi\xebA\xabB\x13\xf01\xed=\xa4\x15\xdbJd\xe5g\xc82\x85\xb0\xb3\xe1\x87M\x92\xf2f\xae\xa2w\xcf\xde?{}\xf2\xe1\xe4\xfd\xf9\x0f?\xbd\xfa\xf1\xc5\xc9\xfbS\xd3f\x82#Xi_\xd0\x0f.h\x9b\xef\x99\xd4\x84\xed\xaa\x0f\x10r$-X\x9f\xfd\xdd\x90\x17\xaf\xe6\x13Xc\xe2\xfb\xf6\x86\xc0q+-\xc8\xac\xd1\xe2\xf1\xffY\xd8\x17\xfe\x00\x9d\xfc\x98 \xc5\xfe4\x99\x8e\xdao [\x14\xa5\xbd\xcbm\x17o*n\x0d \x84`\x1d(.\xe8y4\x96fe/l\xf4R\xc8\xc3xt\xef{\x83\xbe\xbb\x94\x08WRi\xcf\x02\x88\xd7\x06\xed/\x89Vy\x85\xbe\xba>\xff\xf3\x082\xfc#@ 3I\x80\xbf\x17\xbf\x8e`\xca\xc5\xdcY\x9e\xca\xe8(\xde\x84\x8a\x13^p\x86_^\xc4\x15y\x17\xd7+\xfe\xa9\xfcy\x04T\xba\xb3/\x80\xaa\x03\xc9\xc7\n\xca\x16e\xd3\xde\x80\xd01\xfc\xe9\xfe\x17\x98\xb8l\xadW{\xb2\xf7h\xdbO\x0f\x1fn\xad\x1f{\xb27` \xf4\xef%\x9a\xa9\xbf\xee\x9c\x1bG\x9bdv\x01\x89\xb8I \xd5\xeb\xb8\x18\x08.\x9e\xc3@\x84\xf0d\xc8\x1dX\x1a\x0chu\xbe\x9b![\x83j\xc8W8\x15\xedj\x87$\x82\xa1\x1fj\x9d\x85\x17C\x9e\xc42C\xa86h\xb4\xe0\xe5\x0f\xf6\x86\xdc\x81\x87Y2E\x14\xbd\xf6I@E\xc1\x02\x8d\xb6\xad\xaa\x1a\x11n\xfdP+5\x89x\xeb\xda\x81\x8b8\xda\x87\xda\xb7\"\x8e\xf6Cm\xc3\"\x8e\xf6C\xed2 o\xf0\x87Z\xafm\xe1\x0e\xfeP\xeb\x98\xed\x94\x08A\xb9\x00\x1e<\x80;\xf9\xb5\x98\x98K\x82^.\x12\xf6b\x98\xcdd,\x92g\xf1'\x99\x93\x8b\xcd\xf2GrE(\xe7\x98d\x8b\xdcR_\xde\xfaO-\xael\xac\xe2\x9f\x93\xaa\xce\xcb\x1b\xb3\xd5\x9a(\x8cy\xb07+|s\x1d\xaa\x16\xcc:|.Y:\xdb\x07U\x1dSi\xc46\xd4\xc2\xb5\xbd\xc6\x0c\xc3\xd2\"\xaf\xf8\xa1$d\x82\x9b\xea\xdc,4\xa9\xa5Z\xe5\xd7/\xe8\x02\x9a31\x89\x12\xa7\xa93\x1c\xd8\xd2Q2M\xa5 FY-h\x91&\x17\xafI\xbd\xca\xe7\xd5\xa4\x8b\xab\x9dd0\x14u\x035\x10\xbcu\xdc\x1d\xc6\\\x93RJ\x14\xca\xc1\x04\xfc\x06eI$\xb7w\xbe$5S\x16\xf0\xceE\x05n\xf3\xad\xd6\xe3\x8f\xfa\xd5Wq\xf5~\x93\xc9\xaa\xecg\xbf\xdau\x19\x17\x05\x99\xbfk\xce&\xfaT\x98\xfa\xac\xe3\xc2\x97\xd5X\x1d\xa5\x89@\x84\xe4\x91\xc0\x89\x1a\x13j\xd1\x01\xc7>fD\xd4T\x8c\xe7s\x7fz\x166\x1cp`\xf9\x80\xe3\\\xf3\x11\x7f \xbf\xdb\x14\xf3\xb8&\x1c\xec\xbe\xda\x94\xde\xd2`\xd0\x11\x87\"\xc1\xbcA\x02\x12\xc2\xd4L\xbd.\xc9\xcd\x04<\xa4L\x03h\xc7Y\x03\xbb\xee@\x14\xe4\xef\xe94\x1a\x9a\xc7\x8c\xf5m\x1f\x82z\x9bV\x87Z-1\xbbBc\x17j\x19\xaa\x8c\x8f!\x83\xfb\xb0\x0f\x13\xd8\x0bBd?\xf6\x9fB\x0e\xdfC\xf6\x14\xf2\xdd\xdd\x00\xcai\x8e73\xadK\xb6\xdc\xc1%\x17\xdd\xbfy\x94\x95 J\xf3e\x13\x86Jc\xbd\xa1\x16\xb39\x8b\xc1Fd\xe8\x90a\xcbtE\xca\x8b\xbc\x1a\x8a\x04\xb1\xd5B\xc9v\x99\xf3_{\xd9l\x0d\xc0\xbf\xcf\x82M\xbd)\x06\xce\x84]\xf0\xce(C\x7ff\x8b\xca&\xcaWX\xcb\x86*\x8dYNKx\x05P\x04dAE\\lk\xd4\x827\xb9\x83*\x13Qr\x83\x08\xd0-B\xfa\x99*\xf4\x99\x9ex\x98F\xb8d\xd70h\xf4\xde\xab\x10\xc0\x04t\x04\xda\xc7\xb0m9\xbf\xc9Qk0\xe9G\xc4\xab\xca\xad\xdcu\xb7\\m\x93P[\x14>\xd1\x9d^\x889\xcc\xc5G\xaeHy3\xce\xb1Y-R\x86<\xe2I\x98\x9d\xbe4$\x1bkU\xb1o*\xde\xb7T\xd4tL-K?\x0f\xc1\x988\xb1[0\x16D\x08\xb3\x10\x16!\x14\xe8\x14\xbf\na\x8d\xee\xab7\xf6\xb1\x80n\x85p\x1a\xc2\xf3\x10.Cx\x16\xc2\xdb\x10\xde\xb9A\xbe[,+\x11o;~\xd0\xadL,V&\xdeje\xbae\xdb\x95\xea\x16\xcch\xdd\xa7A\xf9\xa8\x00\x16C%\x96\xf9r\xb6[\xa4nq\x0fk1T\xec!*l\x85\xa5b\xb8$7x\xd3\xbf\x98.T#\x9a;\x07\xde\xc3\xff,\xe0\xf1\x9d\xd7L\x0f\xe3D\xe3\xd9\xe9\xa3>\xf9\x92\xdc \x0d1%.u-,\xe2\xff\x97o\x93f\xa4\x8f\xbfl@\xe0\x96\x11\xc4V\\\x93H\xd9\n\x9a\x89)\x98\x1b\xa2\xe2m1\x9d\x9f\x85\xa8G[H\xab+\xd5l*\x08Q\x8d\xa6>\xc2\x93\x1dC\xa9\xcc\xf1\xcfu\x88\x87B\xa2\x0dD1\x9b\xe6\xd17\xdf\x94dq\xc6\xb2\x95\xee\xec\x85\xa8=\xdb\xd9gf\xbf\"\xed\x91\xa4\x99\xfb\x0fC\xb4\x0d\xee\xb8\xbe\xd0\x9fU\xf3\xd3\x98 \xd3\xb58\xa7C\xb2\x15J\x1c0\xce\xc5'8\x82\x13\xc4\x1d?\x08\xa2y\x9e91r.Eb\xe4\xe1\x7f\x18m\xc0\xe8&p\x04\x9fD\x10\xf9\xe7p\x04\xf9\xf4\xf4,\xc4\xf8\x95\x0b!\xf7\x9c\x06!\x86\xac\xd4\x9c^\xcf\x83\x10\xdeb\x96\x17\xc4\xb2\x10\x06\xd3\xfa\x8e)\xf1\xd8\x84H\xb6\xf2\xaf\x04\xf5\x9dg\xff\x0d&K\x91^W:\xb2\xf6\x16\xe5\xb6\xd9\xf4\xed\x19\xd2\xb4\x80Y\xb8\xa5d\x19\xd7\xe4\xff$$\x9d\xfb\xa5\xcf\xd8\xd6\"\x08\xc1\xab\xf7\xbc\x10\x0e\x1e\xdd\x05\xcdr\xc9\x81e+\x18x\x9aJ{\xa7,d\x0c=\x83\xef\x1c\x1f\x0e-)\xb8\\\xcb\xbf\n>P\xa0\xbd\xc3\xcc\x06\x19\x8b\xd0\x96a$\xbbw\xff\x0d8K\xe9r\x80\x87\xfb\n\x0b\xf8\x1c%\xbcK\xcc\xddZ\xdc\xc5\xfe8tt\x15\x1c*\x82Q\x89\x9b\xf4\x8b_62\xb8CV\xf0\xf0Ny\\\xc7\xcc\xaaC\xe5\xce&v\x07\x94M\xb2\x91\x87\x98\xb3\x153\x0b\xc6\"c\xde\xc3\x80\xf3\x9e{\x8c\xf7\x8c\xadi\x02m\x85\xc9\x1cw \x9b\xcbq?Ty\xe1\x87\xfb!\xec\\P2s\x12\xf1]\xa4\xfc\xddM\xc05\xb68\xa5Hs)\x9426c>\x0ca\xe7\xfc\xce\x89\xe2\xc3;\xd8\x81\xf0/D\x14Y\xde\xbd\xeb/\x9b\x14[\xc1;\xd86\x92D/\x92,\xa9V\xfe\xc3\xc3;\xc1-\x87D\x89\xb6\xd2\x1b\xd9\xde\x9d\x8c\xec\xf1\x97\x8dl\x1b?sS\x913t\xf4?7\x95\xedp\xf26\x84\xd8\x9e\x98\xd0V\xa6Tj\xa7$\x97\x92\xaf\x87\x8f\x1dB\x1a\x9b\xca\x94\xd2\xbc\x10\xa9\xc8\xc3\xef\xdc\xee\x0e\xba\xc5\x10\x15r\xa8\xdc\xb2\xc4\xf1\x9d\x8b\x83\x9b D\x9b+\x0c\xc9\xcb\xcf\x8d\x82\xeb.\xe6\x8a\xeeBj\xe2\x1f\x852f\xac\xa2\xba\xc8uw\xf8\xdd8mc\xf5\x19\x88\x81[`1\xa5\xd5\x18\x84x\x8d\x1e\x02w\xa1\xae(%\x97\xb4\xa5zb;\x9a<\x1e\xdf\xf9N[\xc2\x11\xac\x85\xc6\xa1\xec\x88m7\xfeR\xbcZ\xf28\xa3K)\xc1\xed\xefo\xb3J\xfb[p\xa4\x02\xdd$l\xb7\xd0En\xc1\x97\xb1\xf1n\xc1`\xcaq\x1el\xc1Pn=\xd0-N>\xb9W\xf7\x1fQ\xe8\xb2\xd4\xd3\x9cA|\x14\xf0\xfd\xbd\xc7\xf6w9\x9a?d\x12\xfa\x16\xfc\xa0\x1c\xd6\x81JO\x0e(\xff\xb7\xa0<\xdfJ\xe1\xffV[\xf2\x7f\xce\x99\xc4\xbb\x85%3\x16c\xa2\xfc\xdd\xd6\xf7}\xe5\x97j\x8b~-Z\xc1\xf8\xb3\xf9\xb8An\xad\xa0\x91\xee\x8c\x9c\xcb9\x18\xcb\x7f9\xe73\xef\x96^\xcfc\xf9+\xd6\xf3\xc8\x93\xe8K\xf8'9\xe2\x91\xfc\x92\x1b\x0e\xdc\x86P\x8e\xe7\x87\xa6\x8fB$(t\xf7\x1e\x8ca\x7f\xa6\x07\xc8\xee\xd0Mu\xe0\xc8\xee8\xb07\x16k\x8a[\x9f\x04}\x03\xe2\x9c\x99\x1d\x96\x81\xcd\x8a\x18\xa4=\xe8\x9bxM&\xc0\xa3.|\xfe<\x14~Q\x94V\xe8Y\x95!\x92\x8f\xfd\xdc2\xfa\xd1Q\x8d\xecVN\x94(\x8d\xb6r\xb2\xd1@\xbbw\x9b(\x8aE\xe4\xaam\x16\xdb1\x1eU\xbc?\x9c\xcc\n\xa4\xf7\xd6\x92\xd4\x82\xd3\xac^\xe6%k\xce\xaf\xd5\x8c\xae\xbf\x0d\xd0U\x83\xec;\x84\xbd4\xec\xecX|\xb72\xd8J\xc9K`\xa1\x0c\xb9\xd2\xfb\xcc-u\xa7Z$\xe8q\xe8\x16\xe0~\x05\xe8. \xc7hno?\x02\xb8\xd6\xf9\xa9Q\x13\"\xd9\x11\xa5\x06>\xb1\x1c\x1f\xaa\xd7n\xcb\x1f`Z\xf3\xfc3_\x11\x14\xef7\xd9\xf3|\x93\x0de\xb0\x1a\x0d\x0buB]\x98\xfbDl\xb0\xaf8)\xde\xd7\x87d\xc8 \x7f\xf4\xb4\xf4K\xdc\xcc\xcbm\x951\xe2\xcf\xb4V\xedeX\xf2\xaa\xaf\x08\x0fA\xe7^es\xf2\xe9W\x03\xc9\x87\xa4\xc0\xe4\xcbj\xe7N0\xf2\xb2\xcd\xfa\x82\x94\x1e\xec4\xbe\xd9p\x0c\xf7\xf7\xc1\x94&\x0d\xee\x04Lt\xb7\xde%t$\xbdkX\x83\xbb\x1f=w@\xd8\x96\xae9\xd8\xc8\xb6\xcc\x92\xc7\x916_C\xd4\xb2\xb3\xb6\xbf\x87\xf2\x9c\xa7TG\x1f\x8c\xa1x\x91_\x08+v\x80}E(\x0d\x03\xa5a\xf1\xda\xe9;\xe8f\xe1y&F\x1e\xach\x8d\xd7\x0b\xec\x1f@\xc6\xbd\xcd\x19Dm\x8bE\x0bf\xd8\x19NY\xa1\x16\xb4\x9b\xd0\x1aqKV\x025\x82\x19sK\xf0\xbb+\x00\xde\xff\xcck\x88!\xcb\xb3\xfb,\x0f0\xf3\x1b\xf3Bp\x19-\xf0!d\x91\xf4\xf1b\xb1\x83\x1b?.1\xf5\xb0\xc5Ys\x1e\xcb'2=\x91\xf0\xd5\xec\xb19\xcd\xf7l\"\xad\xf7\x1fV$s\x82+h\x8cM\xd5\\\x1a\x1a\x88U\xd2\xcd\xca'\\\xed&\x86\xbb]\x7f\xe2\x14\xd0\xf4\xc5\x96E\xb2\xc3\xba\xcc\x15\xdd\xe2\x96\x93D-\xfd\x8c\xc7]\xfc\xb463,\xb0~\x0d\x8e\xbc\x03\x991D\xc3\x06\x97v\xe6\xebvL\x16\xb1\xd2hO\xd1qJP^!\x19\xd5\x19\xe3\x88Z\\\xf5\xae\xc8\xb4\xbf\xdc6xdA$q\xba+\xfesM\xe2)\xe6BW\xc75\xc1\xf0\xbev\x14p\x0c\x1ebY\xe1\xe1\x11\xb3\xc0\x14\xd8\xaet\x81mvp3dJ\xa7\xbf\x02\xb2\xb0\\\xc6\xdb\npV\x84iq[]:\xd5\xc4\x07\xb4\x81\xe8{\xd8\x13!n8U\xfeP&d\x0eu\xce\xf3;C\xdc\xf6\n\x86z\x15\xd7\x90T\xd9\x1fj\xa8W\xa4$;\x9e\x0c\xb7\xd9\x1dFU\xa4 \x95\x18C\xd8\xff\n\x00\xee\x11\xdf\xaf\x05^'>\xb5\xd9c\xfc\xafN\x14\x19''!\x11eN\xb7M]\xb6\x154S\xcd\xac\x95m\xfb\x070\xbe\x81\x06\x8d\xd9\xfe\xe9x\xbb\xda\xdc(\x03~\x890\x0e \xee\xfdkB\xa5\xaa\xe5k\x1c\x07\xaa\xd2h\x0c\xee90\x90\x8d\x97\x18\xa0\xe6p/\xd4\x0bBH\xe1\x04\x15h\xa8\x1c\x93'\x05\x95k\x9eW\xb8\x1f-\x01\xd8\xbf\x00\x1c\xcf7eI\xb2\xad\xa0\xe2\x08\x11!w\xe8\xb4u\xfc\x15\x1f\x04\x7f\xfa\x95tG\xfd\xfeG\xccu\x14\xf5\x89\xf4\x92\xbb\x95\xb6\x9b\x00\xe6\xd7\xb0\xfbU\xe8q\x17\xf4#\x00b\x83\x87:\x97\x99\xda\xc7W\x99\x05')o\x17\x1fn\x8aQ:\x80\x11\x1b[\xd8<|\xa5\x8d\xf8cr1b\xe0\x8e\x83F\xf07a+\xee~\xe0\xe7K\xf25t\x8f\x0d\xcb\x8a\xc9\xf1\xdb\xdc\xeaW\x80\xbf\x12\x14\xe3+\xcc\x86m\x82&\xfc \x9d\xd4\x90\xb8\xb4\xf54\xaa\xadf\xe1\xbe\x07z\x13\xa9\xe8D\xbe\xce\xd9\xc4\x83\x8f\x8c\x99\xc8\x98Y\xf44\xe8\xc6\xc3\x08\xfe\x04>;\xd1\xbf\xc6,gi\x9e\x8d\xa2X\x8e\x93\xfc\xcb\xe9\xdb7<@\x1feMsE6\xfd\x1a\xe7\xab\x88\x8d5b&\xb6\x89H\x97lb\x9f4-\x84 \xce-\x81W\x93\xcc\x97k.\xda\xac( a\xfbH\x14\xd09\xfe\xedW\xc6\x99sM\x19\xc0\xba\xb9\xcf\xb5\x19\xc9\xa0R\xcf\xc9\x11_D\x8ck:h\xf1\xec\x0e\xc2\x06\xed+\x97\xda\xa8\xdc1\xb8v\xb7\x88}i\x8a\xb0\xa6+}\xe9\xe4\xeb\xf6f\x87\x85\x88\x96\xed6\n5\xb6+\x9ekN_\x89\x00b\xf8\x1d\xfba\xfd\xce=\xca\x04\x1b\x8d\xaa\x8a\xf5\x13\x11\x0eI\xa0I\xa3\x9a\x0dB\xf5\x9e\x99\x07\xb3M\xbed\x131]0\xbbV@\x9a\x8c\x11C\xd5\xdfx\xd3\x16\xb6\x1f\xb2\x0c\x1e~\xef\x19Rl\xca8k\xea\xff \xf6\xf7\xb4\xd7\xe5\xd6\x98\xbc\xa2\xb0\xf5\xcb\\\x17O,\x9cT\x99r?P\x99\xf4\xc3\xf7\xfeF\xfepE\xa0$\xf1lE\xe6\x10\xc3*.\xe7\x90&\xeb\xa4\x86|A\xc7\xcbMT\xa0\xdcd\x95g\xa3V\x0eD\xa2DW\xb9>\x87.5\x93zK\x03\x97}&\x92\x08i\x9b\x19oy\x00\xe3\xac\x0f\xc0\x01\x00\x00\xd0_\xfe8M\xfd\xcd\x97\x8e\x0fi\xa0\x88\x97\x13\x82\x0cmfm\xe56p\xcdN\xd0-\xdb\x91\xb4/\xd8\xa9\xbc\xc3Q\x03\xcd:Xv\x04\xa5}\x89\xc4\xb9\x9aE\x1a]\x85o \xab'J\x8e\x0dtu-p\x1f\x1cla\xc7]\xa6\x95\xaa\xd9\x97\x0bPD\x11\x87\xc7P&_]\x89\x99\xf1\xfe\xa8o6\x8e\xd1\xa3\xd4\xe2\x0e\x06Qdh\xb2\x8a\x99 w\\\x08J\xbf\x0e\xd9\xaa\xfe\x98\\\xf8A\x10<\x85\x1d\x9fB\xc0\xaf0\xa9A\xcb\x8c\xff)\x87M\x00\xc4\xaf\xf8\xe5\x87\xf3`\xc6\xdft\x89\x12s\xcbi\n0;\xc5\x11\xe5\x16\x16I\x16\xa7\xe9X\x80\x8d\x071-; %\xd7\x85bL]Hc\xeaQ\x8dm;l\x10\xeer\x01\xb70\xde\x8c\xfa\xdc\xcd\x86\x15\x9ck\xde\xb2;p\xd2G0\xeb\xe7\x12Q\xac\xe2\xb0(\xed+Q\x8ck\xeeO-\x91A\x9d\x8cQEa'\xfe\x04\xfaY\xfeu\xe56p\xb1\xa4\x1d\xb9\xceRTj\x99K\x95cf\xd12!2%\xec\xee\x16\x97\xf8i\xd6\x1a\xd2,\xc0\xf1`\xbc\x1dxo\x90\x8d1&}\xef\xd5\xad\xeel:1J\x07%YT\x13X\x0b4\xd1\xd3sL\xa1<\x81\xe5p\xad&\x05\xd7\x04n,Ue\x04\x9c \\\x88\xaa\xfd\xa9\xb4O 5\x0c\xf9u;By\x93ay\\<\xf8\xc3\x87\x03\xf1\xe0\x87?=x\xfc\xdd\xb6\x9f>\xde:\xa5\xe4\xc1\xf6\x91\xef\xf7\xf7\xb6\xfdt\xff\xbb\xed\x13\x04\xec\x7fIF\xca\xd6+\xa9\x94\xf9\x8d\xe2\xed\xeb\x07\x93\x1b\x95\x98,2LT\x93\x8aY5\xe9\x07\x80\xb5jq\x80Q\x99\xecm\xebV\x9d\xe5Z\x8a\xa1$i\\'W\x04~z\xffc\x08\xd7I\xbd\xca75\xac\xe2\xab$[B\x0c\"\x13E\x84Y\xbe'\xf0\x07\x19\xf4\xf4\x0f\xf2\x1d\x7fZ\xe3S].Bh\xa0\xf8\xa9'\x97\xd6Z\xf5w\x9f2\x89ep\x82^b\x84\x9e \x9f\x0c \xcf\xf3M:\x87,\xaf%DJ\xb2 %\xc9f\x04.\xc8,\xa6X\x93/&\x80\xb3\x16\xb92\x11\xc3:c6\x0d$\x1e\xc4)\x1f!\xe9\x05h\xa3P\xfb\xde\xef=\xb7V7\xc6\xe9 \x9b\xbfwS\xa2\x89o\x8b\xda\x084\xe09\xd5\x98\x9eeA0\xc0\xb1 \xab\x80\x14\x99\x90\xe1U\xa6\x0c\xc2E\xc3 ,{\x8b>\xec\xbfr~\xce\x15\xabz\x1eA\x97\x91\xc6\xca\x10\xf3\x91\xa9C\xe1v\x81\xee\xb8W\xf9\xa4+\xce\xda\xfaKM\xf8\xed\xb6\xd0\x95\xbe\x03!B\xeaWY\x88\xcep\x0c\xbae\xae\x038\x86\x1a&\xd0_\x96:\x80 \xf8\xb4U8\x82W,G\xf8_N\xdf\xbe\xe9\xcf\xdb\xc8O\xf2\xcey\x1b\xb5>U`\x88\xef\xdd@\x90Zq}\xa6\xbd\x85f\x9a7.\x17\x7f\x0f\xfbR5V\xf7\xeb\n\xdc>\xed\xde\xd1\xe91\x1d\xcd\x18\x9b\xac\xe4e\x87\xca\xf6\x89J\x91'YMJNG\xe8\x9e\x87yN*\xacC>%U\x0dI\x06\xf3|\x86\xa1\xa9\xb5\xf9Th\x91\xadh\xce\x14\xcd(\xf9t\xbb\xc9\x16\xf5P\x9e\xe9\x11\xad\x95\xfe\xb21\xf9 \xea\x8c?\xdc\x14\x84\xeb\xfbN>\x15dV\xa3\xaa\x8f}\x14\xc2\x12\xadi\xe9\xbcU\x90\xd1\xc3\xd3\xdbd,\xaf\xcc\xdc\x03\x96|\xe0\xaau\xa3c\x9e\x92\xf7\x80Y(\x92\xe9\xde\x99\xbc!!Q\xb5\xb9\xa8\xea\x12s\xc1\x80\xe7\xc9~\xa6g0\xc1\x0cXHb\x1fx\x01\xd3\x86\xb9a\xdfb\x90~\xeb@\xc3\xd9\x82\x13\x89J\x9b\x8cT\xb3\xb8 >\x91\xc9\x9f\x1e\xfc\xd7\xfe\x83e\x88\xb9\x9d\x94g{\xf8\xec\xbf\xbazP\xd3\xd0\x8a\xc1\xa15\xfdkzg\x1d\xed\xa9\xbd\x7f|\xc0\x1e\xee\xbbv?\x1fdP~\xf6\xeb\xc6\xa4wG\xa3\x95\x11\x9b\x97D\xb3U\\>\xab\xfdZ\xda\x0b\xe9\xe9\n\xcb^\x86\xa6C\xf7u\x1e\xfe\xbc/\x8e_j\xdac\x8a!;\x98\xb9^ \x0e\xfb\xf1{\xfe\x03k\xd0_;t3;M~%\xf8\xcc\x10\xb4:1q\x0d\xf5\x01\xef\xc5K\xcdpsL\xf5\x95\xf3\xc0\x15\x1f\xf0\xda\xb9\x0cA\x1b2Sh\xd2\xec\xa7\x0e\xf4\x01\xc1)\xe01\xdd\x12\x13\x84\x00\xb22q\xe1\x17A\x93@Z\xdb\xda\xad\x9f\x19V#\x86#\xf0\xf1\xee\xc2\xfb\xbe*\xc8l\x1d\x17\xf7);\xf8'/\xa0\xd4\xed\xf7\xd8\x89\x9ep\xd6p\x84\xce\xfc\x1d\xdb\x81\xe9Y\x80i\xcf^\xe43\x0cZ\xea'\x98\xca\xd0\x86B\x1b8\x02\xcf3Q\xffq\x19\xadi[\x1b:|\x84Q\x81\xb7\xaa\xf9t\x83$\x86\xfe\xef\xda\x9c\xd2$n\x92\x18c\xb6\xcf\xfd\xd8h\xe8\xa1\xe3h\x86\xe7\x9eO\x13\xbc\"\xc2\xff\xb9\x93\n\xbf\x7f\x89\xbb\xfbW\xfdu\xe7 \xbd\xdaC\xa3Kr5\x94\x93k=\x94Xk9\x98\xb0K\xa6\x82\xd2~{1\x94X\xeb\x9c%\xba\xd5e\xb3\xbd\x16}jSH\x9d\x88>\xb5\xcd~\x1aL\xf2{:\x94\x13\xeb\xb9\x18\xae\x16J\x97B&\xef\xbfz\xc6\xd3\xea\xbf'\xcb\x93O\x85\xef\xfd\xdd\x9f\xc6\xf7\xffy\xb6;y\xf0\xe0\xf3\x83\x07\x81\x17\x82\x97x\x9a\xef\xder}\xf5\xf3\xe6\x8c\xf5(k\xf7\x9e,\xf0\xf0\xf6\xec2\xb4(x\x03&2M\xe2\xc7,_\x7f\x87\xebGk\x00\xe0\x17\x9c:\x04\xef\x0f\xf2\x1d#\x87\xbd\xe7\x1f\xf8\xa4\x07\x94?\xaf\x8d\x8a(f\xcd\xf1MI\x16\x06K\x0e\xa1\x91\xec\xce\xdf@\xdbE\xc1\x8b\x00\xbc\x86a\xa7\xd2^\x08\xda\x83I\x14\x94\xc8i\xad\xcb(\xa9^\x96\x84\xa47o\xe25\x99\x07~e\x0d\xeeN\xfb\xc2\xb4sJ\xf6#?\x93\x14\xd3~1\xaag\xe2\xda\xc20\x05\xd1\x04\xd6\x9b\xaa\x86\x0b\"Y8\xf0)\x9a\xdc\x7fO\x16\x81\x913U\x0bk\xc5\xe1\xfe\x98\x8f}\x02\x0e\xd9A\x16\x1b\xbc\xa3_\xd9,\xcamW\xa4\x14\x8e\x0b8B\xb1\xdc\xdek\x81\xa1\xb7\xf7\x1c\"E`\xd8\xee)\xf3\x9b\xb5en\xa3\xe5\xca\xf1\xbe\xca\xed\x02\x85\xb6\x96\xd2\xae\x0b8\x86\xdc/BH\xa9 gL.+\xca\xb8\xdb\x01\x8e, =-\xec\xb5A\x15X\xe6v\x88\xc0\x18\xd4\x01\x8e>\x0c%\xae\xdc>p\xc5!\xd0\x1f\xc8\xad\xd7V$[6\x91\xc7\xac\x9d\xdd8\"\x03\x12\x90\x95?\x0f\xe1*\x84\n\xcd\xbb\x1c\x16\x029\xa1M\x9aR\xb6\xeb\n\x8e\xc1\xbfA\x91y.\xfc\x07\x19\x9f\xe8/\x05u\xf1o\x02\xc62/9\xd1\x1dV\x93q\x99\xf6_\x06%\\)\n\x8c\xc6\x88\x80\xee\xa9%OhD\xe9(Bh\xe3_\x850\x0f\x82\x88+\xad\xe0\x18\x96\xf2\xef ,\xbb&]N[\x0ddl\xa3\x11\xbb\x0d\xb6\x00/\x8c\x051l\x01f\x18 j\xb0o@\xe0j\xa4\xa5\xc6\xc5\x98\xd3\xa9\xe9\xa9\xa2\xdeZ\xe7W\x84\n3\xb0t\xc8\xfaE\xf7\xefEK\x1b$\xa4\xe4\n\xd3\xdf\xb8-\xc77\x1c\xae\xd6\xca\xb63\x0b\x84\xc6\x89\xee\xca+\x14R\xd3f\x96\x17\xa12N\x91\x1b\xd0\x9acT\x14\xb9\x94W\xd6\xea\xb7\x81\x03\xe8\xdc\xce+\x10\xc4l\x9c\xc5\xb6Z\x84\xfa@\xab\x005\x15iST\xc4\xf5**\xc9|3#\xfe\xd6C\x00\xf52\x96ytNk\xbc:\x9d\xd6nA\xa2h\xc1\x8c\xfd\xee\xfb\x08F$\xa55\x15>hU7\xcc\x9d\xe4\xb9\xb2$S\xb5'\x7f:\x82=\xd4U\xec\x85\xcdmn\xe0\xd7AG\x1cv\xf2\xa4\xd3\x15q\xb1\xe3\xd7\xd3\xcc\xe1\xb2\xbf[\x86\xe2\xf2\xe8\xca\xad_\x8f1\xb7\xb9\xf5K\xe1\xa5q\xd1\x88\xe4\x17\xd6o\xed7\x12\xdd\"p\xc9\xc6\xb5\x81\x95\x011\xbf5\\\xf8\xf7\x9ejd\xb0W\\\x80T$\xbc\xd7&23\xcfg\xcf\xe3\xd9\x8aL\xe0\x9d\x1e\xb5\xe3\x8b*O75I\x167\x13\xc8\xf5uf)\x89K\xde\x8c\x9b\xd2\x85\xf33;\\\xf1;')\xa9 \xbb\x8a\x98t\xf1\xf7\xdd6\x91-\x94\x16\xcd 6\xa8x\xf4\x93TE\xf0 \xbc\xd5W\xba.\xe3\x82\xd7H\xf45\x96\xa4F2n0\xbfG\xdd\xf7\x04b\xfd[\xf2\xa9.\xe3Y\xfd\xb2\xcc\xd7\xd8\xc8F_M\xde\x06\xb9.\x87r\x19x\xce\xee\x920\x81\xec0\x88W$\x9e\xa3\xa1\x87}\xd3<\x9b\xcdHQO\xc0\x8b\x8b\"Mfh\x8f\xf3\xe0\xe7*\xcfBP\x9f\xdc\xc4\xeb\xd4\x1b\xde/\xc3\xf47\xcd\xe3\xf9)\xdaF\xef\x98\xe3\xaf\xdd:\xdf\x0c\x8a\"\xe8^\x84G\xf6\x80\x91\xce\xb6-_K\x02_\xc5\x0b\xf2c\x1e\xcf\x07=\xb4F\xe1-\xc7\x19#\x0fH\x97\xe1\x1dcF?\xe4\xe8\xa42\x81\x99\xbe\xaa\xb8\x1f\xf9\x8b\xfa\xc9%\xc9&\xb0\xe8\xd3\xa5\xa0k\xb9\xc3\xa7\x08G\xf0\xaa\xaf\x8a\xfc\xd9\xaa4\x17*V\xa2^\x0f\x10\xf5z\xa0cp\xd0\xeeD5J\xa9{\xe6FcMZ\x1enm\x0ds\xf0\xed\xf6\x9f>\xfa\x02C\x1a\xf5\xcd\xaf\xa0Z.\xad\xeb \xdb\x1a\xec\xc0\xb0\xd1\x0e\xe8\x8fI\x93\xc29\x17\n\\3\xba\xf6\x87\xc1\x14\x95h\x12\xa7Q!\x99\xb5\x94 ^1\xe8\xa7\x85lv\x1c\xadI\x1dS\xa4\xe6\x7f\xb24\\6\xe5\xe6f\x1b\xe5f\xdeUnn\xacZ\nf\xd0\xd4Isk\xfb\x08T\x0dl\xfb\x16\x1a!\xd8\xe813\x88i\x9b&\xc3$\xb5\x08;\x8fH\x88\xabL\xb1m\x89\x003\xf8Vhn],\xdag\x98\xee\x04\xb7\xc3\xf0X7[\xf0.\x80\x1d`B,8\x82Y\xcf\xfe\xa2[\xa8x\xcd\xf8\x1d\xfc\xc0\xdfca\xd89\xfb\xf4\xcbm\x08\xb3 \x88\x10\xd6n:\xd7i\"\xe5\xe8M\x08\xbf\xdc\x062c6\xe9\xf8\xa78\nb\x887I;\xc4\x97\xfd+\xe0_624\xe5\xb8\xed\xb8A\x0b.\xa4\xa3\x8b\x81\xa0W]\x13\x89\x94`\xfeqH2h#*\x8b\xbdT\xb9\xe0)(\xe6\x1d\x1d\\\xb5\x9bU;\x9b\x18'\xd1\x9a\x94K\xf2\x82\x90\x82\xae\x98E`\xba\xb5\xc5n\xe2\xad.\x98\xac\xdci|\x16\x04!\xcc\x18]\xa2\x84J\xd6\xe2\xba\x9b\xa9D\x96M\x08\x1eV\xf3\x02\xfaM\x9fG\x10\xc5Y\xd6i=\xc1XTc\x0eu\xeb\x19\xd9z%e\xf7\xdf\xc8\xd8T\xfd\xf5+\x1c\xd8\xf9\xd0\xadl\xd2\\\x90\x8e?&\x1b\x9b\xf0Qgei9+{\xd9\xd6q\x1d\xec^\x82\xe2\xbc\xec8\xa6O\xcf\xec\xea\x9d\xfe\x1d\xa2E\x1c\xe9wC\xa9q\xd2\xb1]+\xa3\xaa \xb3\x10\xaa\xa1})e\x90\xfey\xe2@\x84\xdd\xb4}\x9bi}\xa6,h\x19\xc9\xa5{\x1d\xcf\xca\xdcO\xed\xa4e\x94.E\xe0]\xe3\x87j\x0bR\x03\x0d$\xf2\x0e9\x1dv\xec\x18P\xb4\x04\xea\x8a\x88s/\x0bac\x10\xb3\xb4O%!\xd64d5\\\xfdoJ\xf6oB\xc9\x9a\xa4\xcd\xa3(\x99i/\xd0\xd1\xc6z\x1aa\xda\x08\xd2\xb1qC\xd9\x122d\x06NK<\xdd\xb4w\xf4:\x9f\x93T\xc0\x9d\xedjZ\xc7\x80\xeaN\xbbY\xe5\xed\xed\xbbx\x14\xe3>~\xaf\xc5\xff\x8f\xef5\xfd`\xcc.*\xd2T@\xdf\xf3l\x95\xa4\xf3\x92d\x13]\x8cq\x16e\xb0v3BM\x86l\x95\xe4\xe1&b\"\xca`\x0b$*\xca\xbc\xce\xff\xca\x9fgp\x8c\xbbe\xd3\xde-\x99R\xab\x89P\x8a\xc6\xc4W\xec\x99\xbf\xa7\x04\x8c\x08|\x12\x89\x99i\x94\xcb\xc6\xd3T\xb5\x84e_Ok\xc3\xa5V\xab\n\x1cAB\x913\x13\xa3\xd1\xba\x19t=\xf9~u\xc2\x19\x0fY\xfcm\xf8\xcbC\xdd\xcbJ\x98\xd7i-\xe8RA\x90\xb5\x0d\xcfTM\x91 \xf2\xae\x17i\x9d\xb4\xf6\xcc\xb0M\x86o-\xf3\x9cR\xc1\xdc7\x9a\xba\x81\x8d\xe8t\x1c\xc9I\x08S\xf3hd\\\xac\x11\x81\x89\\\xb8\xb9\xabnP\xf5\xb8$\x19\xc6\xc2\xda\xb1\xa5\x1bB\x1b\x13[\xfb\xa0\x08\xc5dJ\xd4t\x03v\xd5\x08p\xa3\xe3L\xee\x00;K\x17O\xcb38\x86\xc4\xa7\x7f\x0821a\x8fq\xbd\xe8\x83\xc1V\xb8\xe7u\xe2\xcb\x85f\xcdl\xd2t@\x91\xae_\x7f{\xc0\xa9;\x8e;G\x17\xc5\x97\xb1;\xa7g\x81\xd6\x19FL\xccE\xed$\xd9\x04\x19\x15\x92\x81$S\xd3,*\x7fS\x9ei\xef)\xe4\xf0}c\x87~\xef\x1e\xf8\x0c\x03\xf2\xb3\x10|D\xb8\x86lN\xcb\xb3\xe0)\xe4\xbb\xbb\x01\x0b\x911--\xd7\xfbb\x1a\x18\xe0E\xa1\xd7_eu\xd8\x8e\x18\xb3F\x0e\xdb\xaeu\x03A\x945\x82cfi4Q\x9f\x1e\x888\xc9Hu\xd0\xafE\x11\x1cu6\x0dN\xfb\x12Ui\x8dA\xa8\x05\x0f@\xdd\xc9#6\xa4\x98j9\xcd\xd0\xa8\x9eE\x8e-Y\xfe\x85\x1c\xad\xd4\xd0\xe8?\x04\xfalxg*\xc4w\xf4V4\xfa\xb7\x9b\x99\xf7\xd9X\x06o\xf8\xd6\xe5p\xc0\xf1\xf9\xdf\x8b5T\x7f\xfd\n\xdc\x84\x10\xc3\x1e\x0e\x89aZnB\xf0!\xfbZ\x8b{\xc1\x88\xeck\xe5;\xc9\x89<2q\"\x99\xff\xed\x00\xf6\x0cr\"W<\x03Y\x87\x99\x94\xa2\x1bKs\xab\xf2*\x03\x9b\x1a\xb7%f\x0b\x9e\x85\xb0\x08\xa1\x08a\x1e\xc2\nMF\xd7h\xbdv\x03G\x10\x97Kt5T2m\x1d\xa0uYc@!\xabL\x0f\xe8!\xda\xfaI\xf9v\xfdn\x97Z\x141\xf6\xeb\xd29\xf2\x14\x9e.O\x9f\x06P]'L>\x14\xd9, \x86\xce\xb1\xd11LW\xe8\x90\xd5S(\xce\xe1\x08nx\\\x99\x93\xacNJ\xf2\xa1$\x84\xa5\x18\xbe\x11\x86\xf5,\xb50\xad\xf6\x8f\x0d\xa9\xeaWYM\xca\x19)\xea\xbcd\xc9\x86\xe9\x9b\xaa\xc8\xb3\x8a\xb4^\x15\xf8\xaa\xad\xe7b\xd9Jo4\xb22\xcbGl'\xd2\x80\xa10\xea\xd5\x8b\xa4\x9a\x95\xc9:\xc9X~\xbe\xcc\x8d{\x92\xa6~\x06+\x90n\xe9O\xd9x\x83\xdf-\x1a\x98L`\xe1\xf6m\x1bh\x13(\xdc>\xebCu\x02s\xeb\x97\xb7!\xda\xce3\xf6[\xa6\xbe9\xbd\x8e\x97KR\x06\x0e!\xf3\xa0 {h\xadKe\xb15\x86\xf2d\x8aY\"\xb2\xac~\x1bv%\x8cN\xea\x0d*\x8c\xael\x863\xa2\xb0\xe1\xac\xdd\xc0\xd6\xcf\x80\xe1\x1a\xad\xab\xbaL\n\x11\x85\x14\xedl\x06\xadcD\xb1^\x12\xe1&\xfe\xd6y\x13/\x99\xe3/\xc9\xea\x10vJJ\xc2\xda\n|\xe6\xdb\x99\xa9\xcc\xe7\x12\xc1\xcfW]\x91\xf8\x97|Y2\xf4\xd6C\x16\x9f\xaeQ|Qn\x8a\xda\xf7X\x87^\x08K\x97\x19X2\xad\x8e\xc9\xac*\xb5\x18\x96L\xaaF\xc6\x960VI\xebb\xd8\x9f\x8a\xb8\xa5\x93j\x8b\x81\xc3F\x0e\x0d\x93\xb0p\xb9X\x9e\x14V\x9d\x99\x1f\x8ce\xaa\xfe\xbdX#\xfd`\xf2A&@s2\xef\x19O\xe6\xbd\xf6\xc9\xbcg:\x99{kjSE1\x0b\xe97\xf1z\xc0+\x809d\xaf1\n\xbb\xb9\x16\xc6\xe2\x8d(Yf\xe1\xb2\x0c\xb9\x9a\x9dG\x08|\x94\x89\x1eV\xfbFX\xed\xb7a\xb5?\xc4\xc5\x80\x8a\xdb\xe4\x13\x99mj\x16rZa\xcf\x86\x891#\xc2\x04I\x8ay\xc7\x86]\x1aDB\xf0\xfa\xe7\xae\x87O{G*}\xbc\xa9H\xf9\x92\xd4\xb3\x95g\x8d\xc1&V\xd4\xca0\xb0%\x9d@9\\M\x0d\xcaeI)\xac,\xffP\xa8\xb4\xdb\x10\x12\x831\xb7\xf5\xd6\xde\xac\x1f6\xed\xb6\x9a\x1d\x1d\x94\xe6k\xbb\xe4*\xd9\x0b\xfd\xdbF\xcd\xc1\x03\n\x1c\x03\x95\xd4\x0d\xa0\xcd\xb1-\xbe\xcc\x1f\xe2\xa5\xbeV\xd2n3\x87c\xf0\xf87\x1e\x18\xcd\xa4c\x96\xec\xe7\xe0m\x03\xe4\xe7\xf9\xba\x88\xeb\xe4\"I\x93\xfa\xe6u>7\xec\xe2\x8d\xc1\xdb\x96\x96\x05\xbe3\x92\x12\xc6\xaf\x90x\xb6\x92\xdd\x06\xf4\xa8\xb0s\xfa\x8d\xb6\xdbNb\x18\xd8l$&\xc5Z\x12\xc7\xf4[\xdaO\xa3:^Vp\x0c3\xfeg\x00\x13\x98&gc\xcd\xc0[\xce\xb4G\xaa3\xad]\xbb\x8a1\x1cX`\x1c\xfc\x8f\xddF\x0c~\x06\\\x97\xcd\x00\x9e\x17\xaf\xe6\x81\x9f\xe2\xfd_n\xdb\xf0\xa2\x0c\xa3\xc6\x04bk+:W\xedn)PDv\x1b\x11\xe7\x98\xed\x8d\xc2\x18\xba%\x8a\xa0_\x86\xfd\xd2-\x12q\x9c\xfd\xd9Z\xe4\xccL\xdeE\xb1\xf9wQ\x8c\xdaLgg\x01\xd0\x7fwwCH\xa6\x9e\x07\xbb0\x83]|D\xf1\xa5\x18n\x83\xa9\xa9\x9b\xb0D\xf4\xecK\xb0M\xfb\x8aP\xcc\xa4\xa2)\xed\x8a\xa2\xa4C\x04a\xacz\x04s\x16\x8a|\xfcp\x81wK\xe5^:L{m\xeeyA+\xb7:\x9c\xd3\xde\xcc\x89\x9bAQ\xe2\xb31\x17\xc6\xba\x06\x06Z\x7f\xa9\xd66;\xfb\xcaj\xb0\x10\xea\xa8\"\xe9\xc2\xe0'\xac\xde\xb2\x1d\xf6-\x10\xd6\xf1%9aL\x0c\x1cQ\xb2\xc1\x1e=+\x92\xeaC\xbc\x94\xb4\xa1\x92\x7f5\x95\x9d\xf4Vw\xc0\xb2\xea\xf7\x1dj\xce\xd4\xe1\x1b\x9d\xf63^\xb3hMh\x80\x1a\xd9h\xe2v\x07*t8?s\xad\xd9\x85Ic`\xa2\xb5\xa5\xe1@\x96w29$\x99\xe9>KVJh\xa5r\x9a\x9f\x0d*\x9c$\x81\xab\xb47\xf4\xc0x\xb5l\x9a\x9f\x05\xd8Xs\xf8V,,\x8d\xb9i\xceMO\xf0\xebi\xa2W\xf2\x9b\xf9\x0e}\xc3q\x91T\xba`\x81=\x1b\x0d=\xe6\xffK\"\xfaV \xf8\x8f\xd9\x03nK\xd9\x9e*=K\xfa\x84Q(\xf6\xbf\xd5\x9a T\\u\xdf\x7f\x93\xda\xb0\x02\x9a%\xd1\xbalj\xd6z6\xc6}\xa5g\x89\xca\xb4\x12:\xd7CMW\x0b\x16.\x8d\x1d\x1a\xfa~\xba\xf03:\x17*\x88\xa9\x13\xdf\x9a\xa5\x19w\x07\xf6\xe4` \xce\xf1\x7f\x86\xa6\xe7\x0b\x85O\x85\xd14\x1f\n>\x89*2\xdb\x94I\x9d\x90*\x04\"\xee*0JPV\x7f\xb8)\x08{\xca\x14\x08\xcac\xc3I\xc3\xa4\xaej\xb6\"&\xd9\x8c\x89\x9c\x9a;\x11m\xed\x8a\xd7\xee\xdf\x93h\xab\xcf\x98\xdc\xcd\"\x19\xfcT\x1ax\xf2\x05\xd6\x92\xea\x0f}\xa5\x82\x81\x87\x0f\xf4\x87|~\x13\xa2\xb6\xb8\xbc\"\xa5a\xf2s\xaeP\xa6U\xfe\x1a\x97I|\x91\x12\x83S\xed\n\xab\xae\xea\xdapE\xb1\xe4R\xaeP\x93\xe8k\xdd\xb4k\xfd\xb0I\xd2\xb9\xb1\xb2\x08\xe2\xf5)J\xaa\xb7\xcfN\x0f\x03\xbf\xd6\x1c\x147\xe8\xaeO\x1b~\x0b\xc7p.\xef!\x95\x88\xe8\x86 \x83\xef\x8c\xc4bS\xa6\x13cd\xa3YI\xe6$\xab\x938\xad&\x80Z\xf6Ut\x9d\xd4\xab\xe7\xcds8\x06/\xc9f\xe9fN0\x0ca\x15\xaf\xc9}\x16C\xcc\xd0h\xe3\x08l85gy~\x89q\xdeuF\x84\xfd\xf9\xc5\xa8\xfd\x7f\xa7A[z\xb4\x07!T\xb2B\x0fS\xe1\x08*\xca\xf4\xf3\x1a\x12\xed(=7\x80\xf2\x83\\\xaa%\xa9%\x91}\x1f_\x07CQew>\xa8\x91U\x9f\xfb^\xc3\xa4P\x89'\xc3\xd0\xb1Y^\xc3\"\xdfds\x9d\xab\x10\xed\xfb5F\x9e\x94\xd4C\x0f\xbeWmm\xd3k8\x86_na\x02\xaf\xf5\xd5\x7f\xc66\x87t1o\xb0\x86\x10\xd7\xf5\xf3{\x17m\xca\x14v\x8f\x8c\xa6\xa1\x83\xaa\x01F\x93\xcc\x01\x03$\xcd0\xdeT\xb2\x8dm\xbcU\xec\xec{c\x18\x9dF'\xf1\xc6pdr\x1d\xc4\xcf}\xcc\x0cB\xd8\xc9\xa4\xa5\x8d\x88(\x10ql\x0e\xe1]\x1fr\x12joBx\xc7\xd7\x80\xa2\x17J\xc1?\x07Q\x9d\xffT\x14\xa4|\x1eW\xc4\xc7\xa08G\xb0d\xca%=~\xbc\x97*\xfej\xfa\xe6\xccT\xb3\xe4\xd8\xce7b\x14\xa3\xbb=e\xa7\x0ch\xf7\x02\x8e\xe0\x99\xe2\xa9u\xea\xbfR\xc8_\x104\xcf\xdf\xb7\x9ek\x9a{1B+'4\x8a7S\x12%\xd9\x80-ai\x89\xb3\x85\xaa\xbd\x8b|~\xe3\xc9\x18\xb2\x8ca@\xbc\x8b\xd5\xbf\xa3\xc6h_Z\xb4-;\x11\xb5\xd0:\x8a}\x94\xc5k\xfck9e\x7f\x9fQn\xce\xf0>\xc1M\x1e\xb10\xadX\x19&p\xe9\xb3\xbfCx\x11tn;D\xc2\x96\xeb\xb8\xcc|\xef\x9d\x80+\x8f\xd4\xcf\x9a\xc6p\xfdI\x05\xf1\xfa\"Yn\xf2M%\x83\xdb\xd7+\x02<\n3\xee=X\xc5\x15\xac\xf3\x92\xbe\x893\xc83\xd2(\xfa1;\x00~\x91!\xee\xf7z\x88\xb39\xbe.\xe2\xaa\"\xf3\xfbI\xa6|\x8b\xba\x8d\n\xe6 \x8b#\xc6\xfa\x848\x83?$\xd9\x1f\xd8\xdb\xc8\x0bB\x11\\\xebh8\xf6bG\xd5%u\xeb\x8a8\x86\x91\xb9\x1bsCy\xf2\x85\xbd\n\x8cCHJ2\xa7\xbfvH\x84\xb7\xe2'\xeb\xa2\xbe\xf9+3\xf9nH2\xf7\xe2|/>h&\xd8\x06\x06\x856\x9dgQ\xe6W\xc9\x9chI\xb5:\x99\xb7]L\xf3\x98;\xa8@E\x8ev\xf5M\x81\x88\xa2\xd1@\x976\xaf\x0d\xe0[@I\xa3:\x90.\xdf\xcdK\x03d\xa02\x058M\xb48\xec\x85;\xb6vqA\x84\x97\x8c+\x1c\x91!\x041\x18\x15s\x80l\xf2\xbd{\x90Y\xb4\xce%\xf9\x871\x0e\x8d(rl\xd6@h\"3\xc1p-E\xa9\xfcj\xb8\xa6\xcdz\xc4\xd9\x9c\\\xa7f\xa6\xa4\xf1\xc7\xbe\xa9\xc3/\xcc*@\x0f6u\xe8N\x9d\xa0\x9d\xf1;\xcem\xd2\x9e\xae\x9b\x9e~\x0c\xe1]\xc0\x83\xef\x9ct\x1e\x07\xe2\xcc\xc3M\xda\xb6\x80\x97\xe7a`\xf1\xbd\xa43\xfc\xa9\x9f\x8aM\xf9~l\x98/q\x9c\xc8&\x8c\xde\x18\xa0J\x96\xbb\xe0cP\xfb{\xc8\xdeb\x18\xec&goE\xca\x04M\x8b\x06l\xceoC\xfa\x99\xbe\xa7\xe6\x10~\x8ec\x82#\xf8\xa9\xbf6\xfd\x13\x9c\x0d\xee\x9d\n\xe8>\xc3\xc1\x02#\xa17\xf6\xab\xec\x7foHy\xf3\xb6|\x99\x97\xeb\xc0\x7f\x17\x84\xf0\xeew\xed>Z?m\xf7\xac\xcama#\xb20\xb9\x97\x9e\x80ng\xbbMV\x06)/\xdbo\x14K\xa7\x1b\xc5\\\x11\x02\xcd\xb5\x12'A\x15\xa4\xbc\xec$TB+\x99!\x12\xffXp\xe6\x03\x86{\x15\xdf\x02J\x92\xb6:\x84\xa9\x87<\x9e\x87\xf7\x85~\xc9\x82\xd3Rv\xf1\xc7\xfc\xbaa\x17=6\xb0\xca;\x0bD\x9c\xb7\x81f\x1cj75\xcc\x03N1n\xbb\xf9\xfd\x8c\xc7\xd94sj9\xc5fDi\x97,\xae\x14\x91\n*\xc6\x8dL\x85*\xcd@6\xa59*\xdb\xd0\x0d_!c\xe9\xe5\x01\xfc \xee#\xcf\xe6\xa7\xec&\x86\xce\xb2\x9a\xaaUL>\x93;io\xba\xb2\xa1j\xbawF\xc7'\xda\xdb;\x0b(1\x14\x8dz\xbfxM\xcfn3o9zL\xcf\x98\x87\xc7\x83_\xfc\xe9\xdfo\xcfv\x83\xdb\x07K\xd5\xcf\xe3)\x0bs\x81\x862> \x9e\x06T\xb6\xd8T+\xbf\x9c\xee\x9f\xd9}6\x0d*`?\xdd\xe6f~\x16]\x89\xfd\x85\xbcq\xf3sJ\xac\x97\xa1b\xc2\xed\xaf\x86\x8fo\xe0\xc4g\xc3\xef\xf3\xa5\x0d\x9b\xfd\xb3\xb2\x13\xc9\xfd\x17\x99\x1c\xe6\xd6\x0b\xc1[\xda\x02\x81\xd0\xa5O\xa5\x97j9\xe8\xccd\xba\xdb\xd4\xf7\xd0\xb5\xc6\xb2m\xac;\xb9\x1c\xb1\x85\xcd\xae\xef\xc2\xe2\xcb\xd6 ]\xca\x95<\xb6\x19\x93l\x8b\xdfPj\xbe\xa9-\xdf\xd0\x13\xe6\x9d\xcf\x1dLgy\x8a\xb4\xf4\x9d_\xb6\x1f\xd8F\x9b\xe0\xbe[\xe5\x15z\x1e\x96\xf8\xd7\xf0\x17\xcc\x85\x8e\x92s\x14T\x1c\xfap\xc9\xac\xcb\xf1E\x84O\xf3\xe97H\x9e\x138\x86\x9cb\xf4\xe4\x01\xe6\xd4\xf0\x13\xd8\x85\x18\x9d\xf0\x82\xe9F\xf5\x00\x84c\xd8\xb4\\\x99`b\xc8\xbaz\xeb\xa7!hr\xb2\xdf\xfa\xe8\x9bk\xa7\x15\xe3x\x8a!=8H\x8e\xc2\x85\x0b\xc8\xdb\xc7z)R\xb2XX\x8c.j\xe5\x03\xa8E\x97\xb7}oT\xf3 T\x98\xf4K\xfc`;\x0e\xfd\xad\x8cma\xf4/\x8a!1\xc3\xcd\xa4\x83\x9b\xab\xba.\x06p\x87\x19\xf4\n\xdcL\xe4_C\xf8\x96\xe27\"\xb0\xbb\xad\xf6\xcc\x82\x99]\xac\x9caz\x17>\xc9\xae\x99+\x96\xf6\x89\xf0\x1b\x17&\xc6\xf2\xbfy\xf80E\xdd\xc4n\x98e\x8di&i\xa2\xe6nU\x03\x82\x7flH\xf9\x95V\xc86{ &\xb3\x8e\xbd\x8ep|\x08\x03\xf6\x17\x87\xc0\xce>w{\xbbw\x0f\xbc\x8b'?\xbd\x7f\xf5<_\x17yF\xb2\xda\xcf4\xbe\xa7:\xcb\xea\xbc\\\xbf\x88\xeb\xf8_\x12\x00~\xc64\xc1=\x0b\x16F\xa5\xe8\xd8\x11<\xf8\x87D\x13\xfa\xcbiC\x89-a\x1ee\xa7\xe3I\x7f,\xe6o]\xb6\xab\x1ei\x1d\xfc\x05\xfe\x93\x03\x0d\xa8\xbf\xee\x9c\xc5\xe8\xcb\xf9\xf9\x90\x12P\xc4`\xd2\x8a\xc8B-\xf9\xed\xe3q\x81r\xff\x05\x08\x8e\xb9bC\xa9\xcdu\x10*QU\xdf\xa4\x03\x95P/K\xd14\x1d\xf6\xae\xe9\xabr\x86%\x18\x8c_g\x1b!8moZp\x16\x13HP?_%\xeb\x82\"\xd4\xe0\x17|J\x13\xd8\xd0ol\x990X6\xa0 \xec\xec\x1b\xab\x99$\xcb!\xfa\x9f\x0b\xd2\xaf\x0bL\xf2\x1f\xc9\x98\x99\x19\xb06K5\xcc\x88l\xfa\x91\x0e\xbcM\xc6mF=n\xdb\xa5\x04+\xd2\x99\xb6\x8b\xe2\xcd )\xde*\x86\x8d|Op\xc3\xb1\\me\xa4\xb4\x0f\nq\xca\xacY!\xdb\\$\xc5\x8c\xa9\xbc}?\xf3\x86\x0fAQ\xf8n\x19\xb5\x15E\xc1-\xe9\x98r\x95\xf7\xe3\xe8\xce\xcew\xa7\ni\xb7\x0f\xc5\xb6\xe3\x07\xf6{\x82f\xb4\xf0\xd0IP\xcd\xc6\x1dJ\xee;e\xf4\xa1\xd0\xdf\x1e\xad'\xb7}U\x0b]\xdf\xa9\xc7S(K\xe6\x8c\x12\x9e\x9a\xbf\xec\x9ad\x11\x14\xbb\xa6g\xae\xdd\x81\xeat!\xc1\xb0\xff\xa8\xe3\xe5\xac\xdf`[t\xe2\xfd\x0f\x14\xfcM\xed\xfd\x9c'\x99\xefi\x9c\x13\x95w\xd0E\xd8_]#\x9b\x0cid\xe3F#\xdb\xd5\xb9\xb2[\x90\x17I\x85\\!\x99S\xfc\x88g5;\x01\xf3P\x1f\xc3\xdeb\xb8i8_\xb5VF\xf5X/\xb0Krcc\x04\x9cTl\x16M,3\xfd\xb42D\xcc\xafk\x88\x1e\x00W\xeb\xda\xe7(\n\x87\x13\xe6\xd6\xb2Ku\xe2(\x1c\x8e\xe1h8\x8f\xa0\x7f\xe6\x88\xc2\xa2\\2\xa6\x92\xb15M\xb6\xdc\xf1{lc\xca;/7Qhrv\xc1\x81\xa4\xf1\x05I\xbb\xe3`.\xf2_e4\xd1\xe0h\xd6q]&\x9f\xbe2X\xc6&r\xe1M\xb2,2 \x1c\xd3\x83\x84\xb9\xfbQ\x06\xef)\x05U\xcdX=\x0c#2a\xaa\xce\x10\x7f\xe9\xc70\xe0\x8e\x8a``\x8a\xb4#\x9b\xa7\xbe\x90`\x13\xee\x1c\xdb\x8ccB\xfb73\x9e[\xc0\x15\x1c`\x0b\xcaBkn\x02\xc0(\xed\xb3-Q\xc43\xf2\x82\xa4\xc9:\xa9)\x93\xee4\xfd\x94O_\x99\xf8o;o\x0f\x83\x15\x18RX\x0d\xcc\xbeH\x8a\xd1\x93\x9f\xfd\xcbM\xfe3\xc6\x0eu\x9dh\xde\x0d H\xeb\xa1AE\xc7\x1d\x92\xbe}\xc2\x1c\x92\x1e\xe9\x1d\x92\x985\xf9#]~\xff\xd4i%\x05\xec&\x0f\x8e\x7f?=\xfb\xffv\xbe\xb9\xf7\x07?\xf8\xe3n\xf8\xf4\xc8\x93\xf7\x19\xdcp\xb6?\x15\x8d&~L\xa7\x0f\xfe>\x8d\xef\xffs\xef\xfe\x93\x8f\xf7\xa3\xf3\xff:\xdb\xfd\xe6A\x12\xd5\xa4\xaau,\xd7\xb6~\x01O\x0e\xf7\xb7\xb7\xd1?\xd8\xfe\xd3\xc3/0\xefo\xbd\xfa\xb7\xd4\x8a\xca\x00\xa9f\x95\xa6\xdd5\xb5\xec[ a\xcc\x9a\xc1\x84(\x96\x08\x95\x9a|(\xd8\xe6`\"\x14\xb3\xdb\xef\xa2\xef=\x8bw\xa3\x86\xcbbtR\x8c\x84\xc2\x9d\x18\xdc{\xe7\xed1\x16b\x8c\x06\xdfeLx \x80\x89F[q\xeb\xd7\xd4\x10n\xe4\n\xb3-\xdc\xbb\x07;;\x1d\xfd\xea\\D\xc8\xd2\x7f\xb8\xee\xc7\xc6\x8aC\x98z3a\xf6\xac:\xfd\xde\x9c\xb2\xf0\x00<\xb6\xcfP*)\xe5\xa6l\xd1\xbd\\]H\xe3\xb4E\xdb8\xad3\xf42P\x14\xd8W\xf4\x1f\x16\xd3\xa6s}\xd5\xc0\x0bG\xd5\xfc\x94a\x7f\x8e\xc1_il4\x06X\x13\x19\xe0&\x83$\x1bN\xde\"8\x98\xf9t(\xb6$p\xa4^O\xb3\x01{\x0f\xb4\x07\xb0\x9d\xd3R\xa1\xcb\xf3\xd6\x7f\xfel\xbb\x10\x03\x8e\xfd9zN\x0c\x9b\x9b\xb0!X\x9bCy?.\x92\xffEx4\xcc8\x00\x0f\x17\x93\xdf3\xf2\xe0\x98\xfeB8\x19\xc8\xeb\xf0$\x08\xc1c(\xd1\xab+.\xcf;\xb5\xd9\x9dp\xaf\xb6\x08\xc0\xa6\xd6\x1e\x9e\x1d\xa8>\x18\xcc/^\x8c\xde\xce\xf2\x80\x8c\x01\x1aW\xc9L\x8c\x86\x85\xccp\xfd\x1e\x14\xae \xc1@\xc1\xf6[\xcfnAuYT\xc4Uu\x9d\x97\x03a\xcatE\xc8\xb3\x8a\x7f,\x0buA\xd9\xa3\xca\x01z\xa2\xc8\xb5\x8a\x9e\xa9w\x8ep\x04\xde\x0f\x14\xfcN\xf1\xbf\xbc\xe5\x81*-R\xae>R\xa1\xe0r\xf9\xb9\x87a\xdf\xe9\x06\x8eVq\xf5\xf6:\x13'`{x\xb9-_\xb2d\xb3 \xcf)Bi\xfa\xdeS\xa8\xe1{8\xf8\xf6\xd1S\xd8\xdd\xad\x03 ,\xda&\xf3\xca\xa1t\xff{\xd8\x7fD\xb9\xb1=\xc5\xf2\xb1\xe5\x17\xd4q\x0c2\xab\xef:>:\xbeR\xb3\x8ebJ:?\xe4l\xca\xb6\xb3V\x91\x18\x8e\x00s\xce\xd5Q\x91\xc6I\xc6>\xa7\x9c\x1a\x87\xdd\xac$qM\xfcl\x93b|y\xca\x0b\x96l\xda%|/\x1d\xb8\xe8\xdc\xcb@UV\x91iy\x86\xf8\x98\xd1?\xd8\xef\xee\x92sS\xe9f\xcd1)6)\x97\xa43\xfe,\xec;\x92\xa2\xba\xb6IC\xd9\xe1\xc3\xd9\x0d\x99T\x7f \x9d\x9b\xd6\x03\x81\xd6\xed\xc6\x0e\x96\xeb\xa8\xb3\xa5E*gVDk\xfa%r\x9cS:\x1d\x83\xe8\xe5\xe7\xedE\xf8\xfc\x99\x8a(i\x9a_\xbf\x13\x18\x8c\x0fw\xcah\x16\xa7\xa9\xdfEo\xba7\x18\x11 S\x0cv\xbb\xb37b\xc3\x0fy\x809LK&\xcd\xecBLp\x87D\xbb\xfa\xbd\xa0\xcd}\xef\xdf\x8c\xcd)A'\xd0\x16\x9aS\xdc@m\xa7\xae\x95^#\xc7\xe0g}\xc1:\x0b!\xd1*\xc0\x18\x8c \xbe>\x062M\x10\x9f\x15\xad\xb6\x84\x02}\xc5k\xfc\xff\xec\xbdk\x97\x1c\xc7\x95 \xf6]\xbf\"P3KU\x0d\n\x8d\xee\x06@\x11MAt\xa3\xbb\x014\xd4\xe8n\xf6\x03 \x00a\xa0\xac\xcc\xa8\xaaDge&\xf2Q\xdd\x8d\x11\xe6\x90#\x8a\xc2\x83;\xb3\xde\x91\xa8\x91=cy\xd6$H\x00\xb3^\xdb\xeb\xb5\xd7\xf6\x8e\xf7\x1c>\xd6>Gs\xa8\x99\xbf\x80?\xb0\xfe >\x117\"2\xf3\xde\xc8\xac\x02 R\x9c\x1d\xd59\x12\x1by\xe3\x1d7\xee+\xee\xbdqFcp[\xfcSc\xeeB\x81M\xe2o(X%\xf9B\x8e\x97\xbe\x9cjS\xf7\xf8a\xda\x0e\xada4\xd6\xe1j\xd2\x1b^\xf7\xebc6ms\xc2#v\xf4\x88\x01\xe8t1bT\xde.\x01\xbe\x90\xa6\xfe \x9cDs\xd4\x18\xca\xf3\xcb\xa6\x0f\x13\xd2H\n\x88\x9d]\x0foX\x06\xc6\xd1\xc0<.$\x95F'A\xfb\x8b\x93\xaa7\xa8_\xc9\xb1X\xce.|Tf\x17f-\x946\xc0<e\xbe\x9e\x9e5_O\x7f\xc7|\x9d\x9b\x9f\x97q\xc5G\xf5\xc0\xe4\xa0\xd8\x82\x80\xb2\xb9\xf9W40\x12\xd8\x0e_\xe7gO\x96>\xcf\x9d\x9eg\xb2\xd9\xef\xb1\x97o\xb0\xa3\xe2\xcb\xfc+\xecG\xec\xe5\x13\xec%f\xea\x9c:5\x7f\xfae\xd3\xff\xa9\xef\x9c8y\xb2hb~\xfe\xa4nbn\xbe\xdc\x06\xb4\xca^b/\x9f\xb07\xddND\x0bs]\xb9\xb0/\x9f:u\xe2e)S\xcc\xcd\xce\xcb\"\x1d\xf6\xdd\xef\xb2\xb9Y\xf6#\xa6\xbe\xa0\xb5\x97; C89k\x86\xf0\n\x19\xc2\xdc<\x19C\xf3\xd0:\x0d\xac\xc2\xce\xd5\xddh\x14;ns\x14n\xf5\xcd6\x8aaQ\xefV\xdd\xc5Cd\xbdr\xa0\xe2g\x9cD\xf1\x02kE\xd5\x0c{\x96fI\xeef\x91zH\xbb\xf4\xa1\xe8\xab\x16\"4\x85b|\xdfb_VaU3/\x16C \x1bTS=\xfe\xcf\xe6g\x8f\x0f\x8a\x16\xca\xf7\xc4\xd5\xc50\x97\xb2\xad\xadsK'N\xbf\xf22J\x1f\xd3\x97i\x89\xe1m \x8a\xbd[\xe7\x96\xe6\xbes\xe2\x95ib\x8c\x88\x90\x19uY\xeb\xa8-\xf3\x04\xa5\x13jh\xcf\xd1\xcd\xc4+\xe6j'f\x1e-\xf5W\x8b\xc0a\x00f\x95\x9eo_\xf5\x0e\x02E(6P\xbe\xbdF\xb7/l\x9f\x9e\xc3a4\xbe\xfa>\x8f\xbe\x9b0W\xb5\xbd\x93n\xfdY\xe9\x04H\xef\xc8P\xbf{\x02O\xb9H\xc7\xac6/;\x9b,;\x99<\x13\x19\xf9\xf8\x1a\xe33\x03\x9e\xed\xf8#\xde\xee@\xf5\xd2\xbf\x17T\xbc\xfe\x11x\x19\xcf\xa2!Vt\xa6\xe2\xbb\xcc\xf62\x03\xe7@\xca\x9f0\xb0\x05\xf9\x97\xfcc\x9aY2\xb5\xf0A\x97\xb9\xf5t;oC\n\x97\\\x12h\xb52G,~f\xba\x02/\xf6\x0fhp\xf1\xef\xa9\xea\xfb\xd2\x80\xa0\x0b\x1e\xf1\x85\"\xa03\xe3\xe8\xd3\xd1\x01\xf3\x91\xfag\xd6\xe92\xc7\xcc\xb4\x81\x07\xa5\xb2\xe9z&#\xad\"\xe94\x13ef\xb2\xca\xbc\x083E\xbaDSm\xc9\xd0\x02`bA\xc5\x18\x14\x1c=\xda|\xe7);\xbe\x1e\xdcP,.\xb81U\x87\xba\xc8\xb4\xe9\xfeX\xad~\xa7\x7fc\xf5\xe8W4\xf1\x8d\xd4X\x96\xcaj\\\xf6\xb4\xc67M\xd2\x8c\xba\xe4s\xb5{\xde/v\x88\xc5\xd3n\x90\xdc\x9c\xfeL\x1a%Y\xbb\xd3e\xb1\xf9K\x06\xea\x95\x9e\x88\x14{\xf7=\xd8\xc3c\xc7\xeawM\x0e\x04v\x8c\xc5\xd3l\x98\xc1\x8e/\xd8\x99\x8c\xed\xbb\x1e\xdc\xe8\xb2#N\x9b_wotY&\xff?\x9c\x8c\xdbZx\xd14\xa8\x90yi\xfa\xfd\xbb\xc5\xb1\xab\xc0\xee\x96\x1c\xa6\x8c\x7fR\xde,kHu\x9c\x15Y\x17\xcfT\x1e\xce\xbaki0\xadm\xf0H\x1bH\xab\x95\xa8\x8a\xef:\xffV\xe9\xbbA\x0e\xe9\xcc\xa9;\xa9(\xfb3n\x14\xcb\xb7\xf8j\xc0\x92_I\xf1\xa8\xa0\x0c\xea!d[\x8f\xd7go<\xaf\x04\xa49%=(\xc0\x0e\xe8u\xb3\x8d}\x9e8=ka\x9f\x13/\x98\xd5\xe2Fj`H\xad\xbbK\x19o\xd8\x9e?1[1\xb4_L\xa3pS\x1cw\xfd\xa0\x9b3S\xfc\x13\xacN<^\n\xa2P>*=s\xd3\xfc\xb3*\xee\xe5\xd6%p#\xfe[G\xc8s\xa9+\xd4\x11\xa2\\&O\xa9;\xdc\xf9\x8c\xf8o\xf5@\xd9\x14\xaa\xc0*\xa9Kw\x03\xd0K\xean5\xb5\xd5\x9e.\xa7d\x02\xa2w\x0b\x17P\xd4\x1f\x8f\xab\xfcO\xc3i\xe4Mt\x97\x85\xb0q\xa6\x8cM\x8bs\x95\x93JR\xe3\xa7R ~\xd3\xd2\xcf\x91\xb9\"\xbc\xeb\x8cN|.\x1f\x98?2\xdb\xe9\xaa\x82V--a\xaf\xb1Dp\xc2\xd9.\xe3\xf2\xeeDH[l\x81\xc5\xf2\xa3\xcc\xb8\xdcR\x179\x00\xa2\xab4V\x99\x0d\xed\xe8XAE\x8b\xa5\x95\"=x\xb0{\x9e\xee7\x8a\xcd\xce\xb93\xa5\xe6\xe4\x1d\x8a:\n\x16\x9b\x9dlF\x9d\xc7\xe7jJ\x8bl\xe2T\xd6\xb7,\xa5C\xd3\xacT\xa3\x05\x8eO\xd1\x93D\xd4\x10D\x94.\xc3\x0d\x89\xad\xaa\x0c\xa1S?\x06ql\xca\x1d\xdaw@\x9a@\xe4\x11cg\x04\xf75\x88\xd81Od\x01\xb8\xc3\xb2a\x12\xed\x8b-#\xcai\xbb\xb5#\x1a0\xce\xc1\xac\xef\xf8\x01\xf7Z]\xd6\xdaY\xd9\xde\xb9\xb9\xb1\xb9\xb2\xb5\xb8\xb3\xba\xb1~\xf3\xdc\xe2\xea\xda\xcarK\xa2T\xd8e|\x82\x18\x86\x16G\xac8E\x92\xba\xcd\xad\xae]i\xc5\xab[\x88\xb7:\x0f\xecf^\xd9\xaa<\xef\xb4\xcd\xb0\x90\x18j\xeb&\xcd+h\x1e\x81g?\x8c\xe2\x1f\xca\x8bL\x9ed\x87\xccOY\x18eL\xa8\xf9Q\xbfX\xe2\x94\xa9\xa8J\xe6\x87l\xeb\xdc\xd2\xb1\x97O\xcf\xce\x8b\x05/\xd6zc\xf3\xe6\xea\xfa\xe5\xc5\xb5\xd5\xe6\xf5\xd6\xcbR%V\x95\x7fE\xca\x92\x8fT)\x8eU)m\xe6l\x03=`\x90WW2\xd0\xac\xdd:\xde\xb2\xd8>a\x17\xc8\xe7!;\xc3,\x8f\x16\x8cKv>\x0b\xb31!b\x146h\x80\x1d\xd6\x84\xe3J\xd3\xe2\xa1|\x1a\xae\x8e:\nb\xf8\xaa\xf5\xcaWl\xf9@\xda\x16\x877\x14\x95-\x11a\x08\xde.\xc7\xb3]\x1f\xdc`\xaf\xc9)\xf4\xc18\xd6\x9e\xed\xb2\xa1N\xc5z\\f\xe7\x1b\x8a\xee\xc7\xec\x18\xe4\xe2o\x8f\x98\xa1\xbc\x95\x00^\xd9\xf8aA\xb8G\x82R\x0f\x8f\x1e\xc5\xf7\xc8^\xad\x89_\xe2\xfa1@\xf4AG.\x9e\xa7\xad\xee\xd6\n\x0d\xae\x8aL\xe3\xbf\xb4\xf6\x95\xa5\xd2A\xa7\xf9H\xac\x1c\xc4\xdc\xcd\xb8\xc7\x9c\x90\xe5a\xea\x0f\x04\xba\xf7\x9c\x94\x1f\x9b\x9be\xea9d\xa6\x08\xf3\xc8\xd9\xf3\xc3\x01\xcb\x86\\6\x96\xf0>Ox\xe8r\x0f\nH\x80\xf4\xe9c<\xe0\xf2\xa8\xef\xfb\xd9P~\xbe\xc3\x93\xe8\x98h\xd6\x03\x81\xb5z\x8a6\x17w.\xdc\\][[9\xbf\xb8vsqkk\xf1\xea\xcd\xd5\xf5\xe5\x957\xd4\x99\x02\xed\x8e5\xbd\xe5W\x9d\xb2\xdc9\xb1\xa0\x7f\xfc\xc7\x83iu\x1b\xa6\x96p\xc8\xbew\x86\x8d'\xdd\xcb\xc8\x85\xae\xf2H\xf1e\xc0\xbeg6q\x021\x1fr\x19\xc6\xe1\xf7}\xbd&\xec\xd2\xee\xf6\x0e[\xdf\xd8a=\xce\x06\xd2W7a\xd9\xd0 a\xc5\xa5\xc1V\xd0'\xb5\xb8\xa9\xa0Jf\xc9\xab\x0bzyqmw\xe5\xe6\xc6\xee\xce\xcd\x8ds7\xcfn\xec\xae/oO\xbf\x96\xf2\xde \xd8\x92\xb4\xdc\xa7\xd7\xc5\xf4n\xc0\xedV\xd8e^\x97\x0d\x04\x99\xeb|\xfd<\x8b\xd5\xd1R\xfd\xb3\x08\xccE \xc3@\xb9\xc5\x1c9\xc3\x06E\xaa\x83?n\x15\xf8\xe2\xcc\xe4!\xe4\x9a\xdct\xb2a\xe1)8\x90\xa7\xbb\x113\xf0\xaa\xe5\xdf\x9cU\xab]1\xbaZ\x1e\x032Y\xc3\xa8l\x02s\x7fz\x81\xd9&\x16\x13\x07\xe1\xe6\xa5\x91\x7f\xb3\x94\xdf\xce\x05\xe5a\xa3<\xcd\xc4qq\xc2\xe2\x18l\xaf\xbc\xbe\xbb\xb2\xbe\xb4rs}c\xe7\xe6\xe2:\x10\x14\x1c\xe12-\xbb5\x9e>\xf2F\x9f\xef3\x1d\xd6\xa4\x0e\xb9\xf2\x00\xebB>Msk\x9a\xb3\xef\xb2\xf4U\x96\x1f=\xdaa\xfe\xf5\\\x86`\xcau\xba\x9e\x0bN\x05\xf7\xf7\x12R\x16\x8d\xac\xda\x8bO\x054\xbfqC\xe2 \x1bRw\x0bU\xbd\xf6\xa2^\xf4\xd3IVJ\x96rB\xa6\xba\xa9\x10&\xb5%\x1bg/\xae,\xed\xb4\x00k\xc5z\xbcJFy$\xbf\xce\xc5\x01\x9a\xb6\xdf\xafD\xa2\xab\x1f\x9eq\xbe-_\xd9\x81\x826\xe5xEa:b\x87\xa9\x86-\x0cr\x8aa)\x9f(9\x92\x82\xc4\x1d\x07\x12\xa7>\x177\x81\x8dc\xfdv\xfdX\xe5\xa9K3'Q\x1c\xbeu\xbc\xf5\xed/6\xde\xb2\x1a\xc7\xa9\x1a\xc7\xa5\x02 X\xadm\xb9\xa5\x027\xedr\x8b\xc2t\xb9\xe3\x84\xa7\xe2X\xb5U\x88\\/\xe0\x025~(F\xf5C\xe6\x84\x1e\xfb\xa1\x18\xcd\x0fK(\xd4\xa9n\xcd\xb9\xad\x8dK7\xb7V^\xdf]\xddZ\x994W#/\x98\xa9V\xd4c\xf3\xb5P+\xcd\x02\x94o\xa1\xb5Eq\xca\x99\xcb\xd2\xd3O\xdd\xf1\xbc\x1fv\xd9\x0f\xd5\xc8\xd4\"\x88\x115,\x02\xc8\x1b_\xfd*83C'\xdd\xd5\xc9n\xdaz%\xbeyK\xb1\xb4\xb8.H\xdd\xd2\xc6\xfa\xce\xe2\xea\xfa\xcd\xdd\xf5\xe5\x95s\xab\xeb\x13\x96\xc6r%Q6\xc5\xa8e\xa87cB\xa0\xb4<\xe3\x85:\xd8\x98_\x83)kxD+\xd8E 1\x1e_\xd2\x98\x94\x1d\x05\x15I\xfd\xb3y\x0f\x96\x9cP.4OdT\xb2\xa3\x16\xb7$\xe48\x99\x14f=\x9e\xfa \xf7\xa4u\xcfB\x03\xd5\xba..\x97W\xb2I\xe6\xab\xc1\xad\xb2\xe5\xc2|,\x0c\x0fM+\xed\x83W\x99\xa3\xdc\xac\xa2\xe7\x9a\xb8\x98be\xce\x8e\x9c\xa9\x10\xf33\xe6E\x1c\xf0\x91\x1f\xf8if\x99\xfd\xee\xfa\xd6\xca\xf6\xc6\xda\xe5\xc5\xb3k+\xd3\xce\x7f\n\xfaZ\x8fQ\x81\x10\x07\xdb\x16\xff}\xfdk2\xd0\xea\x1f\x18j\x81\\O\xbc\xa3\xab\xc9}.~wo\xd0c\xa3\x7fb\xaa\xd2\xeb\xbdq\xc9\xe4\x9c\x03\x99\xf9\xe2K\xec\x9a\x98\xc7\xd4\xfb&\xd9\xc3\xd4\xfb\xd6(\xd7yZ\xae\xc3;f\xf7\x8b\x93B\xd4\xf3Iq/J\xb8\xd6\xdd\x87\x1d\xd6oW\xe4\xeb\xb0\xd3\xc5\x02\xb7\xd0\x03~\xf4#\xa1\x11\xd0F\x1aL\x1e\x89L\x19\xf6\xa3\x1f\xd5\xe5\x01\xac\x84t(\xd7\xfc\xc2\xab1\x12\x82y\xd2\xe6\xd7\xa3\x1b\xd2\xb79\xd4\xc6\x9dI1\x0b\xcd\xee\x81\x926\x94\xfdn\xf1\x1a\xd7]\x81\x88\x1f\xecLm0\x99\xf9K:\xed\xca\xf7\x92\xcf\x1enF~\x98I\x0f\xfa\xc0Du\x17\xfc\xee\x0cs\xcdW\xd8\xdb3\xaco\xbel\xc9p\xbd\x04\xc7\xe7\xe2y\xe9\x0b2u\x8bb\x91\xd4A\xebM\xbe>\xc5V\xadaR\xd6\x8c\x8a\x85\x12\x13\x1c;\x81\xef9\x99\xf4\xe9\x8aK\x1f\x84\xd6\xe5}K\x15\x9b\xc6\xb3-l\xcf\xbfR\xea\xbd\xd6w\xdb\xa6h\x1dI\x94\xb72\x9f\xb9\x99\x81{\xac^\x9e\x9d\xc3\x98\xab5Y\x0de@U\xe6\x0b\xa9#\xe1.\xf7\xc7<\xe92\xf3\x96\x84L)\"x\xe2\x11|\xcc4*!\x1c\xf9BQ\x0b_(\xad\x0cM)SN'Sr\ni\xcf\xcfw*\x8ew\x96<25\xbe\x93\xf4\x909\xfd\x8c'k\x91\xe3M\x13a \xafk\x93(\xcaVC\x08\xc4>C?\xe9w\xc9\xd1\xf7\x19?\xf4\xb3\x8d\xc5<\x1bB\xb2\x98<\x1b.\xca\xde\xd2\x197\n\xfb\xfe O\xb8\x80Zj\xc6 7)\xdc\x16e*(is\xee\xf9\xa1\xd7\x86\xcb\x0f\xe94\xdeT\x0d\xf2\x1a\x9dan\xb5\x16%O\x94\xa5\xa6\x99\x93\xf1\xcd \x1f\xf8\xa15\x0eD\xfcD?u0&W_\x12\x87t\x81Ez\xb3\xeay\xb7\x03\xcb\xd2\x185\x96\xf2\x80\xbbY$Z\xb4\xbf\x0fY\x93\x95\x16r\xdd\xd4\x0ft?q\xe2E\xdd\xbf\xfdQ\xae\x89\xee!U\xdaa\xdd\x05\x0c(v\xb5\x8a\xf0\x91B\xf8\x13\xa7O\xe2\x9c\x19>\xbc<\xd4\x9e?A\xb2M:\nt\xe2\xf4)\x0c\xca\x0dH\xe6\xd90\xb0&\xb7c`C(\xdbc\xd3\xed{&\xa3J(iWQW6\xbc#\x89\xea&$\xe80\x91D*\x05@\x06\xd1\xdf\xfczX\x93K\xa2L$x9\xff\xa7M6\nj}\xaf\xa7\xcfzY\x93\xf1\xb2Y(s5\x89\xb5\x18\xdb\n\x9d\xacL;\x0c\nQ|/\x1e\x0d\xd9\xd6\xa7\x85\x16\xca\xa5\xcdR\x14\x12\xdc\xd5r\xfaMz5?\xddX\xdc>\xd1\x91 \xcd&>\xb2\xc1\x16\xd8\xf5\x96%\xd3b\xcb\x12\xa6*\xd4\x82\xbc\xdd\x11r\xc8j\xd8\xben\xd2E\xa4]v=\xbbA\xd2\xc1\xc0F\x04\xec5\xe6\xcb\x07\x99\x13\x94\n\xb3![\x99\xfd\xdc\xebdq\xb5\xae5:u\x9c\xcd\xcf\xd2F0\xc5\"8\x0b,\x98\xc9\xa2\x8b\xdb\xe8=gHS+NB#\"\xf4\xeb\x1c\x8d4U\x98\x1a\x0b\xfci\xb0\xc0\x81\xb7[j\xb1 7O ~eX \xc3\x98-X\x907aA\xca^c\xd1\xf3b\x81\x0d\xcb\xd5\x96\xa5So\x19\xfb\xa6\x89F]\xed\n-\xa5#\xca+$\x84d^r\x14d\x8e<\x00\x90Kq\xf5;\xe8+$\x1b\x9e\xc3\x11\x16\x81\x8a\x87\x98\xb7\xf2\x14\xf7\xeb!\xa7\xfa\xaf2\xa9\x97\xfeT:'kT\xca\xc9\xdae\xc1\xcc\xf6\x85\x8d+7\x17ww.\xdc\xdc\xdc\xd8\xdc\xdd\x9c\x90oY\xfb\x95e3\xb1-\x9f\x9f\x9e\xd1L\xca\xb3v+\x1dF\xfbe\x84\x17\xa8Q\xda;\xfbx\xc4P6\xb6V\xaf\xad<\xefH(B'&Op?\x89F\x17\xb7;BW&\xa5\x80\x90\x0c\xc4\x80\x8b\x1c\xc1-x8CV\xbe\xe4\xc4\x1d\x1c\xf8n\xd4%\x1ef\xc9\xe16\xbf\xdd\x9e6\xe3\xba\x96\x0dP\xbaN\xdee8\xb0U\xff\xe4,\xaf\xcf\xd6\xe46H$t\xae\x06\nIe\x159i\xc1 \x17T*\x939\xcfjl\x0c\x95T\xab2\xc7H\xe9\xa5\x1d\xbf#W,\x92[\x1c\xda\xcdG\x85\xa9\xac\x94\xdf\xd4\x9a\x97\x87\x95\xc2}\x8aq\xca\x93.\x86\xa9\xb9R\xebFC\xfca`\xaf\xab\x19\x96u\x9aLm|\xdb\xccET\x0e\xbbL\xd5ot\x9f.xe^?*H3\xb7P\xce\xa6\n\x8f\x93\xf5\xb2\xc8)?\xdaS\xf7Ls\xa7S\x1e\x96\xda\xba\x1b]\x98j[\x7f\x98\x98\x11B\x066\xc3y,\xa1\xb7\x10\xad\xa6?\x8a77\xc4\x9f\xf3/\xe6D\x86\x92Q\xdb\xcfaX\x97,\xd9\xa9\xf1u2\xe7\x10\xde\xeb!o\xfd\n\xaa\x17u \xcfH\x95\x14$z]$\xd6T\x96\xc6\x81\x15\x96\x88\xd7\xb9\xd1-\xe7\x05\xac[\xaa\xb5\x8d\xf3\x1b\xbb;/f\x81,\xc4hf\xdf\xcf\x86\x97\xf2\x0c\xaeG\xa6\xc8\xa8h\xc9\xe4\xd5\xf8\x8c+\x9f\x81\xc0\xb2\xda\x10^\x0b\x9a\xd5\x98N,\xb8\x96L^\xc0\xa5\x8d\xf5s\xab\xe7w\xb7V$/z\xde\x85l\x1a \x18\x16,\xdcG\x8d\xea\xb7+\xc0t\xc1\xf6\xb8\x04\x83\x94s\xf2\xd3E\xb3x\x90\xd4\xad\xfaO\xaf`\xa9\xe7\xa2d\x0bLY\xe0\xbe\xa4\xd2\x0f\x94\x98\xee\xd9\xc3ug\xc4S\\q'2}H\x90`\xd5a\xa9\x9a\xe5\xb8i\xdbS\xde\x0e\xdb'\x89t\x15)\x08\x95\xa1 o\xc3),D9J\xb4z\xbe8\xe2\xafDV\x1a\xab\x04B\xf5\xc7\x8a\x9a\x05\xcb\x967\xcb\xe2\x01\x19\x82\xec\x90Z\xe5\xe8\x08enr\x1f\x8a\xbc#\xd9\xa9\x83p\xa6v/'\xf7\\\xd3\xf1tb\x0b\xd2\xa2l\x0f \xb4\x8d\xec\xe4\x80\xecT\xfb\xcaQh\xe4\xa05?\xcd\x88\x90\xc5\xca\x96\x8b\xe7\x16\xb4\x18\x12\xb6\xa2\xa9\x84-fD\xaa:\x81\x8b)\x9c\xae\x17\xbaXIYt\xac\xe2c\xb9T.\xc9T\xd2\x95/%\x86\xe0\x1b\x9b\xa7\xc3vn#\xb9]\x9c\x17\x91\x92\x12\xeb\xe1o$\xa7S#@H\x11\x80\xce\xcb\x8d\xc24\n\xf8\xcc\xbe\x93\x84\xed\xd6\x95\xc5\xad\xf5\xd5\xf5\xf3\x0b\xcc>2?e\x1e\x8f\x13\xee:\xe00\xeb\xb1}?\x08X\x8f\xeb0\x1e\xed\x91\x19\xf2\x83\x8c\x8d\x9c[Q\xc2\xc6\\g\x9aB7\xe2;\xd3\x04\xbb\x11\xe7\x99\xce`,I\x98?\xa1W\x1b\x8f\xc1\xbf\xca\x9b\x039PF\xa9\xba(\xd7\x95T\xd0\xbc\x97^b\xed6\xbcp\xa1$\xe3(\xe6i\xab\xd3\x99\xd9\xe3_h%\x99\xf4~v\xa30s\xfc0U\x17N\xb2\x87T\x8bI\xdc\"w\xeb\xdf]\xe5\xc1\x98+I(\x08\xa2}\xeem\xc3\xa8\xba,\xed\xa8\xe46\x99\x84\xfb]f9\xe9\xba\x1d\x1f\x9e\n\x95\xb9\xcd\xec\xf4\xc0\xaf\xa3\x07\xddI\xa2B\xfdbh|u\x92\x81\xbc\x08L\x0b\x07\xb79V\xcd\x15f\x8a\\\x9f\xbb\xc1^\xab\xfes\xa1\xe9TMEtT\xa16\x18\xfa\n\xaec\xe7~e\xc6\xa3\xfa\xecL\x9f\x84\xdc\x1c\xf14\x1a\xf1)\xc5fSG \x1e/\xe1\x9b\x9f\xa4Y\xbb\x06G\xac\xb2t\xd3.V\xe4\xbf\xc9\xfc}\x82da3rh\xa2\x84\xb8 \x92D_$\x13\xa9\xeeg1\xa6\x06\xe2\x0b\x9b:\xe3\xa7\xe2?\x10\x1b|\xe4H\xa6\x8c\x95\xcf\xbd\xcf*\x97#2\x9b\xf2\xce\xcc\xc8\x89\xa7h\xa5\xd4\xd2\x91#!\xec\x7f\xddv\x1b\xaf\xd1#s\xb6\xad\xd7\x87\x0b\x99W\x19E\x84\x8a\xa2\xf0\xa5\x11A+F\xe5]\xff\x16\xfbFhD\xfc\x80\xbb\xb9\xf4,\xb0j!]\x95\xe5f\xfe\x94E\xd7\x90\xd6\xceH2\x88\xa4\xaa($\xcd\x8aB5^\xb8\"\xe1\x17\xe3\x99R/\xad\xa0\xb7]\xcd\xcf\x9a\x04)|\x9aj\x9f\x83\x89\x94\x1a\\\xe7\x8e\xe8\xa8\x0c\xd6\xd90\xaayr,\x97%\xa6x\xc1M,C\x968\x0d\xcf\xc9\xd6\x1f\x95\xe2\x80/(\x03\x90>\xeeb\x9f\xaa_\xd4\x89\xae\x97\x1eJ\xd4\x7f\x81%5*\x88\xdc~+hb\xfb\xe5W\xdd\xca\x1d\xe0VMS\xf6s_K\xc8x\x1b[\xa9\xac\x0d\x80\x93_\xcd\x1by\xb0\xa3\x0b\xcc\xb1\x83K\x0f\xde\xd4\xd8(\xcb\xaf\xe6X^\xbf\x95rJ\x1d-\xfa\x86P\x89/\xe3\xf1\xd2\x0f\xebnB\xd3\xa1\x94\xd8Vn\xe7N\xf0}~\x08(\x86\xbe\xd1\xf5\xaa[*j?\x917G\xdf\x80\x15\xa4#K\xdba\xfb$y\xe7:2>\x16\x13\xfd\x8dj\x05I>\xd3\xb7\x10\x16{\x82\x02\xf1\xf3\xa2\xfd0\x98\xd2\x1d\x89Y\xc8emj\n\xfd+\xf4D\x9e$\xea\x02\xb9Y]aZQ\x9at\x8d\x8c\x7f\x8e\xa94u?\x10\xf8Tp\xfb\xc95\x02I\x9f\xfb\xa0\xc4v\xcc\xddv6\x93 ~'\xf4\x8a< \xda\x9d\"\x93\xbf.\xb6\x9b\x04u6\n\xfdk\x1e\xbbL\x14#8\xac\xea\xa2[7\xc6\x00\xfe ,\xdc\x0d\xb8\x934\xbc\x8d\xa1\x7f\xcf\x83dB\xfe\x0f\xa6h3O\x82\x05[\x9e\x16\xfc\x13\x03\xde\x96^\xd1G\x1a\x1e<\xd4?\xf5 \xe9j\x98\xf1\xc4\xe5q\x16%\x0b2=\x0f\xfe*\x96j:\xf9\xb5\xfc#w\x8du\xbf\x1a\xef\xee\xf2/\xe1i\x1c\x85)'C%\x9f\x7f\xfbcu\x13\xee\xf10\xf3\x9d ]`\xad\xd4\x19qEg\x1b\xe2\xe0\xf4O\x91\xb7&\xa7\xf6\xf2OP\xc98[\xa8\xbe\xe2y+\x8d\xc2\xee\x1f\x1c\xff\x83\xc9\xe4\xad\xf9\x94\xdc\xed\xccdC\x1e\xb6\xfb]\xd6o\xb8$\xb0Bj\x96\xc9r\xc8\xa6\xd5\x8c\xb4@x\x1d\xa2\x1d\xcc\xd1\xec\xb2V\x11*\xa4i\x8a\xf9\x08zG\xab\xe1\x0d\xf4\xaa\x1553&Nx\\N\xdf\x01r\x95\x11G\xfcg\x01\xc4p)\x90Ws h\xdf\xa8\x92\x1d6\xebLdT\xd9a,\xa8\x85\x90\xb5n\xc2\x02\xddT\x93\xbb B\xf8\x04\xbcQ\xae#\xb6\x04n\xfaW\xb3I\xe4\xab\xcd\xff\xb9V\xb7\x0d\xaa\xdbh7\xe3N\xb7\xb9\xc6)\xa2\xce\x8c_\xfe\xddm\xb2\x0c\x97\x7fU+qe\xb8pc@\xcc\xd4\xfag\xbb\xd9\xb0\xda5i\xe7\xd3\x04\xd8L\x8a[113\x8d\xd9!u\x10N3v\xd5\xa3\xd5B\xb3\x0d\xd8\xf6S\xb3\xb6\xbc.g<\x98 \xd1)]\xf0nQD\xe6;m&=\xf5\x98\xdc`\xed,\xa2\x88j\x1e\xa0\xa2\x9b\xfa-\xfb\xbf\x90\xb5k\x82\xe7O\xf5\xab \xca\x99\x9f:&\xe7\xab\xf2 \xfa\xed\xda\xe5\xbe\xace\xf3\x85\x9e\xa4\x1a\xf32\xab\xe2M\xdf\x8e7\xf6\xba\xea\xdai\xbaH\xb9t\xe6EG\xca}\xe9x6j7u\xdba\xfb\xf4 \x12\x9c\xa6\xee\xa8N\x9c\xb0\\R\xc9\x00NZ\xc5Q\xa0\x93\xb3\xb3\xb6P\x04\x00\x11\x0bm\xaa\xc6pr\xb6\xe6\xecXB\xb9\xfe\xe9\xc5\xb3}\xcd\x01\x18c\x95T\xb2\xda\xc8\x80gk\x91\xeb\x04 `-4\x9b\x03\xb5\xf7\x834K\xc4N\x92\xf2\xab\xceHU\xed\xb4\x0bi\xa9q,\xbf}bf\xec\xd8g\x0fw\x130Tk\xfb>|op6\x85\xf3S\xb9v\xc0U'^w7_\xa2\x96\x169\x9b\xe9\x87`C\xef`E\xb9\xee\"^O\xe9\xb9\\#\xac\x06*}\x99[\xb9*\xa0\xf2\xb7<\xb7\xe6\x9cFh9\xda\\)\x1f~\x97\xf96\x03\xbf9\x0d~\xfd\x1dIh5\xe2\x87U#>{\x8d\xb5\xa3&\xfb\xbdR!:\x02w\x9f\xab\xd8n\x12\xb4[\xe2CU\x89\x08KV\xfd\xc2\xa8?\x93'\x81@2x\x81]HH\x99\x8a\x84#\xe7%\x04\x03\x89ED\xfd\x06\x9f\x9f2\xe6\x0fx6%\xa6q\x15\x0d\x83\xdf\xdf\x94\xf6\xfc\x05\x19J\xf8\x0d\x9d\xa5v\xef\xe8*\xe1q\xde\xf6\xda\x9f\xf4\xf0\xf0\xbf\xbc\x87\x07e\xb0u\xb1~\x82U\xdb\xef>e\x00\x91\x8e\xad+\xc5sE]\x96\xce\xecn./\xee\xac\xdc\x84\xd8\x86\xed A\x0df\xef\xe0\xb9\xf1j\xb4J\xa1\x04\xd0P\n\xdc\xeb\xce\xc6\xf9\xf3k\xd3\xf6\xfa\\1)8U\x89\x19\xb2\x8a\x05;\x82\x02=\xa2o\xc2=\xf7\xf3\xc9\xd3\xd7\x0d[\xb5\xd9\x1f\xa6\x91\xad\xa7\x90o+ \x16\xea\x8b1e-\xe0\xf8\x15\x8d\xe7\xd09\x9f\xfb\xbe\x91C&\x1b\x95c\xb4[xtNa\xb2f%\x84\xda\xf7C/\xda/.3\x86NZ\x93\x00\x0d\xff\xb2\x99\xc09\x8c\xf2L\xc7uKJ\xbe\xccy\xbc\xe6\x87{\x17\x9ct8\xcd\xfd\xd2\x04\x1b]-\xf4K\x98|\xc4\xae\x9a\xfc\xb6\xb5\x1b[\xf2\xcc\x99\x90\x06\xc4$\x1d\xdaq\x06\x0b\x85\xbb\x10\x1dJ\xe5\xcb\xdd\"\xd1\xacEUq\xa4\x9a`UU\x00\xf4\xb2-|\x07@\xdf\xb1+\x17\xce\xd7'W\xff\xf6 \x89\xbc\xcc\xd8v\x93(\x08v\xc0\xf5.U\xffPw\xe0\xf2[\xc2\x1d\xefp'\x82r\x8a\xb8\"\x1c\xae\xd45!X\xcd\x0e\x8f\xfd\xda\xb8\xf6\xbe5\xf2\n\x0c-'g\xb1\x97d\xaej\x9c>AR\xa34\x86\xb6c\xde(\xdf\xa0l\x07V\xac\xe8\x7f}X\xc1\xd4*\xc5\xe5e\x9cH/\x0b\xc67\xc9\xcf\x06\x9c5\x81&5\xc4\xbdLKp+\xef\xf8c\x0f{\xd8h-\xafU\xde\xc2\xcfT\xee\xe3\x08r\x1f\x17\x9e\xf6y\x8d\x99\x1e\xb2*V\xa9y\xd4\xe9\xb2\xb0\xdd\x91\x8f0\nT\xf4\xc3Ag\x8aG`\xc5\xfeG\x13#D\\Yj\xae\xe1\xd6 0O@k\xa14\x10Bi \x84\xd2\xa0\xa1\x9eV\xa6\x13!\xef\x8b\xe3#+\x9fK\xa2\xd1j\xba=\x8c\xf6\xc3\xef\xf3C\x89\x88u\x0d\xc8\xdca}\xf4:ls\x7f1\x8d&\xeeO\x8e\xa5\xf1\xd8\x19\x16O\\\xa9\xa1,\xd5\xb4Rr\xc0n\xa7\xac\x9e:B\xcc\x12\x93\xef\xc8\xa4\xa2\xf5u\xe7\xe5\x9d\x8cyX\xf65\\\xbb-\xe3\xd0\xe1\xcaA\xd3\xa4M'\x83v\xd9Q\xe6Iw\x16\xf1\xd7P\xaaTs\xd5\xf6^z\xe9\xb9\x1b\xac\x8b\x84\x98\xea.\xbe\xaa\x07N\xff\xb2Z\x95hT7\xc4\xc3\xf4\xb7\xf9j\xa4\xd6\xd8\xca\x8a\x8b( \x107\xa1\xcd\x9bYTs\xfdd\xae\x9dp\x1eIE\x06\xafs\xfaTW\xe3T\x86\xb5\x0cf\xaa95[GX\x85RV\xe4\xb2z\x0c\x9f\x92`2\x85\xe6`z)\xa8p\xa7J\x9f$\xbbh\xc2\x8f\xb1\xc9\x06\x04\x0f\x90\xcc5\x1c\x8d\xd6\x11\xf08\x13\xc4\x8c\xe9\xcc\xf9\x91\xa9\xd8\xe9J\xc4o*\xd1L4|\x9c\xf9w\xfah\x12\xfd\xd3'\x9e\xebwhT\xba\xdd\xf6\xf1\x9b\xc7\x07]\xd6b\xad >\x1c\x13(\x94#\xe9\xa8o\xe8\xa6\xa0\xa2\xbb%\xaa\xda\xf6\x1b\xe6\x18J\xfe\xdav\xba\xf0\xdc@h\x8eP\xdby!\xe7rl\x95\x9f&2\xf3\xa9,l\xac\xe2\xf7\x8b\xd0S\xe0\x9f\x96\xeb\x043\xa9Y\x03\xd7xi\xf9i;\x01\xfd;0Z:\xef\x80\xe1:D\x1a\x0c\x92\x11%g\xc7e*\x92\xa5-t\xacq\xddF5\xb2\xe8\x8b[\xb9f!A\xca\xbd`&\xec\x87\xc5Zn:\x89\x98/\x17\x92\x8cY9u\xd7-\x0b\xc8G\x1eg\xb2\xa8\x96\xac\xff\xd68\xc4@\xae(\x96\xf7\xa7\xb1\xd7O\xc3%d\xbb\x8aWP\x87\x1340\xbb\xe5\xa9\xda\x8d=\x9e\x01m\xc4\x94f\x04M\xf0\x8d\x97\xaf\xfeC\xe1U3\xe5\x97\x84|\x14\xe7\x19\xf7\xb6\xb3\xc3@\xe6#\xae\xad \xd6\xb4\xe5\xf4\xd2(\xc83\x95S;\x99\x89\xa3T\xc6\xea\xd4W\x93\xf1\xf7\xec5v\xbc\xed\xe4Y\xf4#X\xc7\x1f\x0d}\xcf\xe3a\xe78[\xa8\x02:\xc7\xeb\x99O\xab\xef\x1fp\x0f\xf7\\\xbc\x90f\xafidx\x99^\xf0U\xf9\x1fG\xf0\xe0b\x91^\xad\xa7\xd221\xbdm\xa5\x9cN\x97\xb5\x8f\xc8wTZi\xe6d\xbe\x0b\xae\xd3\xe5\x81\xbd\xf4\x12\xf3eZ\xe0v2\x13\x8dy\xd2\x0f\xa2}v\x94\x15\xff\xb8Z\xf9\xd7\x1b\x9d\xc2\xdd\xde>\x17=\xd3IX\x88\x14\xc5 \x960\xc0\xf3\xdaT\xa9\x93\x8d_\x88\x96-\xb0\x86D\xe7\xba\xec\x02\xab\x89q\x13\xbf\xcaQ^`\x83\x06,.\xb3\x9f\x056\xae/I\xa4\xae\x056\xb4\x13\x1f{\x1b\xa5{\xe9\xfa\x95\xa8r\xa6i\x1d\xbf\x18\xc3\x9e\xccM\xef$\xf5UZ\xac\xed\x01\xb4_\xd4{\xa44\x8b&\xa9\x1e^;\xf1\xbb,\xb7SgDX\xb2\xa1\x9fvY\x9d]\xd5\x08\xc1\xa9\xd5\x90\xed\x1aCv\xda\xe9J\xeb\xed\xec\xab\xac\x0f\x8f\xf8\xf5\x8f\x1e\xed0\xf7z\xbfj\xc8\xee7\xbf\x16/\xd8\x9cO3\xa7\xc2 \xe5\xbb\x83\xc1\xcc\xcd\x9b\xd2\xb9\xec\xe6M\xed\x12]\xf2)\x0f:\x1d\xe9a\xa6L\xe2\xbc\xcb\xae\x8b\xba&\xc9\xb2\xdb\xe9\xc8\xf0\x99(\\\x8b\x1co\xa2\xfdL\xff4\x07\xf6g\xe2$\x8a\xd3\"\x93\xc2L\x16\xc1\xc1j\xca5\xc0\x14\x17F\x92G8\x939\x83\xae|\x04U}]\xf5\x1a8*\xbe2\xadH\xb0\x82?\xd4\xe9\xc4p\xc3\x10\x12G\x02{V\"J\x96K\xe6\xe9\xbc\xb4\xd2\xf06<\x92I\x82.\xaby\xf6hO\x88=\xad\x84\x87\x1eOj\xcc\xa6\x8a\xdaL\xbc]a\xc5\xa0Rdq0Q\xaai\xec\x84\x84\x9c\xd1F\xfa\x0b\xf0\x9c\x04\xe0Cm\xe1\xbb\xdd\xda\x9e\xb8z\x90B\"F\x1d?\xa7\xab|\xa3\xd3E)\x19\xee\xb6\x8b.\xcc\x15\xf37\xda\x87\xe7\x1bG\xfaCi\x176\xff\xfc\x1d\xd9/\xfd~G\xf6\xbf8\xd9\xb7\xe8\x85\x9a\x13d\xce\xe0\x0b\xd3\xec\xf0w4\xfbw4\xfb\xab\xa6\xd9\xcf\xe7\x1ag!?\xb5It\xa28='\x13\xb2=\x87\xe3R10\xc4Kt\xba\xaf\x93\xb3\xa7-L\xe3E\xe5\xfb\xfa\xe6\xeeG\xa3\xb7(\xc9{gy/\xa5TA\xbe\xd5~\x86\x85&`\x13\x87\x0f\xfc\x97\x85\xa1\x93\xcc\xd4l\x8a`\xa8)\xed\x19\xcc\x04\xeaB$\xf9tlD\xff\xa6\xf5\x1e\xc2?U/\x91\x0f\xc0w\x1b\xbc7'\xb6f7\x9a\x19h\xb3\n\x03\x13\xbf\x98F!\x9e\xfc\x146L\xf6%\xe6os\xe3jwf\xa2P\x90\xdc\x80g\x96G!m?\xb3\x8c/\xbd\xc4Zz\x10\xe5@\xcdP^\xec\xa6<\xdb\xf1G<\xca\xa5\xbb3<\xb8\x7f\x86\x1d\x99\xeb|\x95+_\x0b\xad1s\x92\xaf\xd3\xd2Y9\x15\xeb\xa1/\xefF\xf9\xbd\xc6\x96\xe7d\xce\x82?r\x06\xfcx:\x1e\x1c=\x18\x05\xaf\xf6\x9c\x94\xbf|\xb2\xbbya}\xfe\xda\xe1\xd9\x13\xce\x95\xadYgy\xd6\xbftkq\xdf\xbd0\xf0W\x97\xceF\xd7\xae\x04\xa1s\xe1\xf5\xd3\xab\xb7V\xf7/]8{r\xd5_\x1c\xf0\xf3si/\xbctzu4\x9c\xf5.,\xbe\xbcvx\xfa\x84w\xc2\xcd\xbd;\x97\xf2\xde\x89\x8b\xe1\xda\x9d\xd5\xfdK\xcb\x8bc\xf7\xc4\xb5p\xd5?;\xef\\\xb9|\xe2\xf5\xd1\xe9\x93\x9b\xdb\xab\xfb\xab\xcb\x8b\x83K;\x8b\xfb\xab\xcb+\xfb\x97\x96V\x07\xee\x85\x8b\x81;\x7f\xf9\xd0\x1b]>\xeb\x9e8\x1b\\=\xb1\xb5}\xf5\x8d\xad\xb8wg\xd6\xe7+s\xf1\xb5s\xc1\xbas\xe5u\x7f\xf5\xfczz\xf5\x8d\xf5;\x9b\xdb\x17\xd3k\x17.e\xee\xe8t\xda;\x1f\xe4\xd7\x0eW\x07\xee\x89\xadS\xbd\xf3\xbb\xa7WG\x17\x87W\xe7\xb3\xd0\x1d\x9d\x9e\xeb\x8d^\xcf\x9c+s\xc3k\xf3\xbb/\xaf\x9e?5\xee\x8dv\xbf\xb3z\xbe\nw\xcf\x9f\xbe\xe3\x88\xbe\xe6O\xbe\xbcz>\xc8\xc5\xdfW\xaf\xec\x0f\x9c+\xa7b\xef|0\xec-\xa7\x83\xab\xa3s\xb7\x9cy\xef\xb0w\xe2r~mi\xee\xf0\xda\x1bg\x83\xabo\xbc^W\xde\xdf\xbcup\xcby\xe3\xe2\xad\xde\xf9\xdd\xc1\xd5\x13\x83\xd3\xab\xb7v\xf7W\xfd\xb3\xb7\xf8\xce\xac\xbf\xbe\xb3\xe8\xaf\x9e\xbf\x16\xf7\xce\xef\x9f^\x1d\xc91\xf9\xab\xe7O\x85kW\xce\xcdz\x17V3\xf7\xc4\xd6ao>\x0b6\xb7/~\x87\xcf\xaf\x8f{\xa3k\xf1\xb5\xc3S\xb7z\xf3\x07c7\x9c;\xbd\xea\x9f\xcd\xaf\x1d\xce\x0d\xbd\x0b[\x87ko\xac\xcf\xba\xa3\xd3\xc9\xb5\xed9\xb3o\xfcDv\xab7\x7fj\xe4\\qso>\xd8\xf3\xce\x0fO\xf7\xb7W\x07\xbd\x91\x9b]}ck\xd6\xf5\xe7\x0eQ\xdb\x87W\xafl\xc5\xde\x1b\xeb\xb8\xdc\x1d\xef\xc2\xc5\xb13\xbf\x9b];\x7f\xee\x8es\xfe\xdc\xa1;:w\n\xd5\xdd\xbb\xfa\xc6zt\xf5\x8d\x8b\x87W\xdf\x08d\xfdb\xfc\xab\xb7\xd6wv\xe7\xc4\xffV\xfd\xb3\xa6-\x18\x93X\x93\x15\xb1&\x87\x9b\xdb\xabw\xd6K\xf5\xd6\xael\x0d\xdd\xf9\xe1\xd0\x0d/\x0e\xc5z]\xda\xb9:\xbbvk\xef\xce\xa5;W\x0f\xd6\x97/\x1d\\\xba\xf3\xfa\xfc\xfa\xf2\xca\xdc\xea\xf2\xee\xfc\xda\xad\xbd\x13\xebw\x06'.\xed\xbc~g\xfd\xce\xe0\xf0\xd2\xce\xa5\x93\xab\xb7N\xber\xf5\xca\xa9\xb8w\xe5\xdc\xec\xb5\xcb[\x87W\xaf\x9c\xbasmt\xfa\xb0\xb7}V\xae\x99s\xe5\xe2\x9cw\xfe\xf2\xc6\xd5+sb\x8dg\xdd\xd1\xb9\xdc\x9d\xbf6vG\xb3\xfe\xea\x85\xadS\xae\xc0\xa1\xf0\xe2\xd8;\x7fn\xf6\xda\xf6\xea\xe0\xea\xfc\xb9\xf4\xea\xec\xdc\xf8\x9a\xc4\xad\x83\xb87\xbau\xf9|\x90]{\xe3\xd2\xe9\xd5[\x8b\xdf\xb9\xb4\xbd:\xb8v\xe1\xb2\x98\xf3\x81{\xb8:\xb8:\xba\x1c:WN\x9e^\xbdu\xf6\x8eX\x0b\xc0\xab\xade\x81g\xde\xf2\xac\xef\\9\xb5w\xed\xca\xb5\xb87\n\xc4X\x8en.\x9d\x1e\xf6F\x81\xd8\x9f\xe0\xf2\x85\x8b\xc3^\xb8>\xea\x9d\xb8\x98m\xde\xda\x1f_\x9d\x0f\x0e\xaf\xce\x1f\x04\xe2oq\xe66\x07\xd1\x99\xd67D\"X\x8a\x82\xc0\x89Sx\xbab\xcd\x0f\xf7\xe4\x1f\xe0\xcb#\xff\\\x0d\xe3\x1c\xfe\xda\xe1\x07\xd9b\xc2!\x0d\xea\xd9<\xcb\"\xe0\x16[\xd2KX6\xa5\xfe+\xb3}\xcb\xb7{\xeb\x82\x11\xa5\xff51Ch\xcf\xecW\xac\xafS\xf6mF\x10G7f3i\xf4mF\x90T\x01H\xef\x81\x02\x10#\x88\xab\x00\x15#\x88\xf4\x13\xb7\x9b\xbf\xbf&\x87m\xdaqLx\xbd\xb10p\xab\x85!3\x16\x06\xae^L\x98}\x95\x85\xec\xbb\x8c\xbf\xca\xc2\xa3G;L\xc5\x0d\x17\x16\x86\x10\xa9\xe1jb\xd9tI\xa3U\xe9#G\xd0\xac:3\xb7\"?l\xb7X\xab3\x93%\xfe\xa8\x8dEg&\xb5\xfc2f\xd5wd\x96#\x9b\x14\nLl \x99R\xdbSb\x1c\xc9\xa8a\xa4|G\xdc\xe9(\x99\x05\x8a\x17\x12K]\xec+\x1aIPj\x0b\x9e\xdfE6\x85\xccj=\x98`9\x98\xd6j\xa0\x11\xa4\xd0\xd6\xebET\x95\x834\x0f\x82\xd4M\xb8\xed\x81)\xfd\x0bM\xc9\xfa2\x96\\q\xbc\xcb\xae\xb7\x8a\xf6e&\x9d<\x08j\xdf\x1e\x93\xc9\xec\x8cg\x8e[k\xf5\xe0 \x88B4\xaf\xad!\xed\x84\xd4J\xf7\x9d\xc1\x80'\xc7\\\x8dn2\xabN\xc8^c\xadcr(l\x81\xb5\xea\xbc\xc6\xa7\x1fG\x9b>3\xe97\x99e\xdc\xc0I\xd3u\xf9XZ\xdc\xf6g\xcc?+\xafj\x95\x7fw'\xbb>\xde\xe8Tb\xfd\xdb\xae\xc5\xceR\xa5\xde\x1e\xf1\x97\x1bE=?\xe0bI\xaa\xfb\x9c9\xbd\x80g\x0b\xacu\x0c\xfeB`\x8f\xa7{Y\x14\x0b\xb8\xfa\x13\x15\x08\x9cd \x9a=6\xf4JW\xb3\xafV\xe8A\xf0;J\x00\xbf\xdf\x1a%\x18\xfa^CV8\xa0\x01{\x9c\xc7K\x90\x8d\xb3\xa1=I\x0b\xf8\x0c\xa0\x93\xd0\x02\x01m\xba\xd2\x9bB\"\x88\xf8Sb\x05\xf1\xdb\x90DC\x0cE\x90\x8brw\xe2\xdf\xd0\xa2|\xabQ!\"k\x19\x94c-\xd9b\x8b< k\x86%\x93\xf1\xbe\xf4\x12;\x12NAe\xc0\xb6*C\xe8\x9b\xa9\xcc\xf5\x1a{\xb6\xe1\xd89\xf3C\xe65\xbb>z(\xedG;\xefL\xd2\xf6\xf5u\x83W\x1b\xec\xa4\x7f\xa2\x83\x1c\x1e\x0d2F\xdc)L :\xc8\xa9\xa85\xb1'\xa6z\x0b\xd8w\xd9\xdc4}0\x99\xd4Q\xbe\xe5\xd2\n\xa3\x90\x0b\x02=mT\xad\xa0\xea~\x98O\x91hob =\x84^\x10\xb9{0\x86\xae\xf9\xe8F\xc11\xf9(\xa5\xfc\xde\xd8\xd6\xf3\xda%t\x0cW\x8c\x0c%\xd7K\\\xc1\\\xca8u\x88=\x11\x97\xbf0\xa7J\xb3\xc3\xa0\xf6yl\xfd\xf3\xfc4\x0e\x9c\xc3\x05\xe9}\xacv\xd1\xf2nG\xf9\xd7`9+1\xc7\x9a\x14J/\x86\x19v\x8d\xc2\xf3;\xb6\xf3\xe2\xd8\xce$T\xf4\xfc\xb1\x1d\x0dK|jZ\xc9\xa9\xa8R\x16\xa1Z\xfb\x89\x13\xc7<\xa9u\xd2{!\xd8S\x1c\xc4vI\x85\xfe\x1d&}}\x98\xd4\x93\x8b\xfeU#\x93\xea\xe5+\xc5\xa5\x8e\xfe&\x98?\xcd\x91Y\x1af\xabF|.\x19t\xeaQp\xd2\x82f\xfc s\x12\xee\xb4*\xb7\xec2\xb5\x936\x1d}\xf1\xc6}\xd1\x02j\xb9r\x86\x8c\xa1j\xaa3Tw\xa1Ws\x80(\xdb\xd4\xe6\xab/z\xb0dV6(-\xc7b\xe9b\x08\x85lo\x81\xeb\xe8\xcc\xba\x17 \xd4jB\x00\xa7<02\x15&\xfc\xb5\xc0\xf8\xcc(\x0f2?\x96V\xa7\xeb\xad\x96\xf4\x0bo\x89S \xaf\xf6j\xb3\xac\xaa\xa3\x17Q\xa4\xedZ/~\xf5\xef\x1bC\x13\x9e_\xa9Q\x0f\x0d^\x16\x1d4\x14\x06\xedF\xafj}\xb9\xa4hte\x14g\x87\xb2\xdd\xfa\xe2\x91\x1e\xab\xdc\x17\xd8?\xf9<\x12{\xcd\xfe\xbd-\xb3u!\xc8\x17\x15\xfa\xc4\x81jt\x0f)Q\x16+\xf9\xab\xad\xa8\x17\xaa1\xab\xac\xc6\xb6\x86\xe5 \x97\x86N8\xe0\xc6?\x05\xfei-/P\x94\xbdV?\xdd(V\"n\xfdt\xd5\x80Z\xf6d\xd6w\xbb\xacu\xecX\xab\xa3DWA\xf6\xaaq\xca\xd3\x054|\x99\x012}R\x1a\xa2 Y1\x91m\x999\xb7)}\xfd\xddnQ\xe8\xb7\xc9\xc2\n|92\x87\xac\xfe\xd5\xa3T\xbd\xd7\xa8\xda\xab\x86\x93BM\xcb\xd4\x81\x9e\x99\n\x8a\x95\x9b\x9a\x18\xf2\xc9'\x91\x1a\x08\x9e\xd6m7\x93\x83p\n*\xe3K\xab\x02\x84\xd7+N3\x939\xc9\x80g3\x80Ei\x83\xf3\xb43\xe1\xa5\x1b\x01\x8f\xd8k\xcc\x9f\xce\xd0\xaf\x7f\xc6\xb7\x06\xe8\n\xb7\xfb\x91\xdd}\x9e\xe0~\xd3\xa4\xc4\xe7\x9a\xf6\x04=\xd4\x93\x97\xe5\xba\x103\x04\x81!\x13\x0f\xbbS\xd3l\x17\xdc\x1a\x12[\x88>\xc2\xff\xeaR\x8f\x85\xd0`.\xd8\x9a':A\xe8g\xbfe\xc1\x9f\x91\xb9\xb2\x17\xc2\xec\xd9d\x86\xcf\x9e\x83\xe9\xb3)\x88\xab\xf3e\xf4\x00\xe8 X`\xad0\x8ab\x1e\xf2\x84\x85Q\xc2\xfb\x9fCe\xd5e\xb0\xce\xb6\xd1\x8c\x98c\xf3\x04\x9d;\xf4\x03/\xe1\x96\x90\xeeIK\x0e\x9a\xbc}U'\x9a\x8d\x86\xdc\x1f\x0c\xe5c\x13ymR\x18\xf1\xebE\x89\xc7\x93\x05eUj\x10H\x9cd\xe0\x87\x0b\xac\xe1\xa1\x92\xd8\xf1\x95\xfa\xf2O\xc9\x04\xb0\x1ee\x8b\xa1?r2\xee} \xc9_\xdfN\x17'\xccO7\xc4Y\xf5\x1a\x84\xc2\xb1\x8e\x19,\x1fL\x85\xf0\x82\xb1\xd4\xe2v\x18\xa5n\xe2\xc7\x99\xbe\x00\x98@6\xef\xda\xce\xc1oO\xe5Q\xab=I\xdb\xd1\x0b8I\xdb\xa9'\x11\xac\xb41\xec5p:\x0e\x95\x8f1,\xfc\xc4\x9dI:F\xe3!\xe8by\xb3\xe3\xc5\x8b\xa6z\x15,\xa2\xa9\x1a\xc6\x82v\x00d\xec\x9b\xe1\xffK\x9dp\xbcZ'\x1c\xcf\xe6j\xe3\xeb*6\x1f\x1c\xcf\xe6j\x93+\x8057\xa2gs\xb5 \x14\x80\xe4\xecw\x15\xe0\xf4+\xa71\xa8\xaf@sd`\xb1\x86\xd8\xfdt\xbc\xaf\xc7OG\xffE\xb4\x91\xe7\xa5\xf5E\xfcQ\xd2\xb5\xa5 \xc1d\xbc\xd6\x8c5!\xee(\xa8\xc4\x1d\xb9\xe0\x15\xe4B\xdc\x91{\xf4h\x87\x05\xd7\xdd\xaaW\x90k\xb9\xe0SK)\xa8\x866\x99\xe5\x84\x11\x81\xdf\x19aF\x115\x9b\xd5\xc5\x1c\x052\xe6(\x99\x19\xf0\xecR\xe4\xf1@HO\x13E\xec\xd2\xf8\x94\x17?7^\xfc\xad\xdf;^z\x15\xfbxKf\x93+2\x87\xfd\xe1\xcc\x1f\xfc\xde\x0f\xca%~p\xfcx\x97\xb5\xa4\x05\xc0\xd6\x96k\xd2\xd8\x1eO\xdd!\x1f9\xa4\xc9\x9aB\xbaQ\xd0\xca\xc8\x14\xee\xaaIo\xf1\xfe\xb6\xac\xf2<\x93N\x14[\xab\xbc\xbf;\xd3\xf7C\xafx\xde\xdbf!\xb8\xdb\x85\x9c\x14\x84\xa1'\xc4 \xa5V8H\xad\xc2\x81\xf3<\xc2\xc1\xd7\xca\x18Uj!\xb9=\xcdJ:\x9f\x98\xff\x94)2\xca\xa7}\xf9\xd8\x81\xc2r\x83\xebK\xe5\xb2T\xc2o\xe7~\xd2\xc4\x99SY.l4\xd2\xb9\x8a\xcbo\xf1~}\xa1\xbe\x99\xc3f\xeds\xf9L\x11`>\xa3nz\x9b\x8d\x832\x8dd\xbb\x05\xecN\x9e\xe4V\x83\xb9b\x08\xa5%\x95\x9aXx\x0c\x857\x13\x7f\xe4g\xfe\x98O\xac0bgX+\x92#i\xd0\x1e\x06\x82\x04\xc2\xab\x902)\xd0\xef\xff~\xc2\xfbuna2 \xa9|\xccx\x00\xe1\x0f\x1a\x07\xcbt\xab=\x10\xb4\xec\x88S\x14sJ\xc5\xccIo\xa7P\xcc\xb8\xa3\x04\xb5\xd6\xdcI\xa1~\xe5[\xa2\x91\x18\x06\x93\xff\x7f,\xf3\xb3\x80\xd7Z<_`\x7f\xd0\xd3\xcd\x9b\x19?\xc8j\xfb\x8b\x05_\x10\xbc\xa8\xb6c\x7f4h\xec7M\xdc\x05\x16\xb6O\xce\xcd5!\x95V/\xe7g\xe3\x83\x86\x8d\xdf\xf7\xbdl8\xb9\xd8Du\x96\x19\x15t\x8d\xf7E\xbfs|4\xe9\xa5=\x95\xbcL\x92\xc2\xc0\x11\xd8<\xa1F/\xca\xb2h\xb4\xc0Zb\xb0\xb5%k\xe2_\xea\\G\x04\x15=\x94\x89\x1a\xfctcq\xfbD\xbbS:\x07\x1e\x8f\x13\xeeJ\xcd\xad\xa6z\xba\xef\xcbL\x84\xae1:J\xbe\xe9\n\xa5\x8c-\xb0#G\x06]y\x06\xcb\xa7+;\x8c9\xbc\x997j2\xf9\xb8N\xca\xcd\xd9]h\\\x99 \x87\xc7\xa3\xb6\xa1\xc6\xe6\x18Bo5\x86\xc6:\xcfelb*\xc0N\x90\xdc\x05\xd6@\x9d\xf5\xaf\xe0F\x8d\xf7)\xfa\x07\\\xa6\xf1\xa12\xfd\x0b\xe5\x14\xa7xL\xbf\xc0\x85\x05v8\xb9\xb8d;\x0b\xccm^\xb4\xa6\xcc\xb1\xb0\xff\x8e\xe0\x0b_n\xfb\x87_r\xfba\x08/v\xf7\xff\xf1m\xa8\x96I\xea\x1e\x8b\xd3\xbf)\xf6T\xbd\xf8X\xbf\xa9P,\xccG=\x9eL,\xe6\x87\x19\x1fLQ\xae\x17E\x01w\xc2\x86rZ\x03\xfc2\xc86\xfe\x92vh\xa6\x91C\xc9\xa9\x13\xef\x02\xd9\x7f\xe9\xd8d\x85O\x8c\xe7\xac\xb5\x0c\x95\xb0s(\xb7d\xe70\xe6\xd4,\xa4\xd7\xa8o\xf6YZ\xa2\xb9w\xc9\x89\xa5Lm\x93\xd0\xab\x1b\x17\x9b\xaaB\x97i\xae\xa46o\xca*\x15\x95\xa3\\\x0b8Um=\xd8\xcd\xa28\x1c\xc4j\x99\x92\x88?\xa9\xa8\xa2\xf1E!q\xc4\xaaE\x8a}n*\xc5\x0fbG(\xac\xb1`\x87EA \x00hx\xd3\x14*\xf1VS.\xf0\xd3\xf2\xc2\x14\xa8Q\x8d\xa6\x87L\xa5\xbf]\xfb\x9e\x18Q\xea\x08\xdd\xfd\x8e\x0c\x90\n\xa8\xc1/\xb7Y\xd6\x84\xe6\xda\xce\xc1J\xd6\x95EN\xce\x9d\xea\xd8\x8c\x7f\xb2\xd0\xec)\xab\xfdO\xc2\xe6N\xd8\x0dm\xf9\xd7kh36\xb0\x19\xc7\xf3.D\xd1^\xbb\xd5\xe3\xfd(\xe1\xdbjy\x14\xd9M\x1b\xd3:\x9a{\xe6a\xc2\xfb0\xcc\x94g\x8bY\x96\xf8\xbd<\xe3m!\x80\xb7\xba\xf6\xdb\xbfN\xb74LlzM\xa7q\x89;\xfe\x87\xd7\x17\x8f]\xfbA:{\xec\xf4\x91\xd7~0s\xe3\xe8\xef\x1f\x1f\xa8d\xc5Ug8\xba\xda\xf5i\x98\x8a\x85\xd1\x88\"\xf0\x94\xae\xf5\xe2\xf2\xf2\xcd\xc5\x9d\x9d\xad\x05v\xbd\x05\x97\xe8\xadj\x86P\x92\xda\x82\xd5\xe6c\xc2C).\x11\xd3(O\\\x8bE\x00\xee\x19\x1a\xfc\x89\xfcBm8s\x06\xee\x0eZ\xd2w\xbc*B\x08\x95;mgE\xd6\xe6\xa4N{\xac\xbb\x94\xach\xabN\xb2\xe7E\xfbaU\xa4\xbbK\x0d\xac\x10\xbbq\x86\x85|\xbf\xb0c\xd6\x08\x8f\xc3l\x14\x88clg}\xd9a\x1c\x0d\x12'\x1e\xf2\xa4\xbeP/\xe1\xce^Z\x0f\x0f\xfcp\xcf\xef\x1f6\x17\xd8\x91\x9b\xbc\xc0Z7{\x81\x13\xeeY\xd2\xa8w\xd4EK;\xb3(\xd0\xae\xcc\x12\x96\xa3\x850w\xff\xafI\x15\x05\xf8\x9fq\x8d\x91\xe3\x8aa\x7fJ\x86\xa6\x01\x04\xb1FN \xd6\xeb\xd9Gx\xd7\x17/m.\xb0\xd6K\xa4|l\xf9\xba\x18J\xccy\xfc\xe7\xb84|\xbf\xf7!\xfd\xae@\x8f\x7fNA\x00\xf8K\nH\x83H>)\xf1\xec\xf1_P\xe0X\x02\xfe\x1b\x02\x90\xb3\xbbGvDz\xa6\xb6\x9e=z\x9f\x02d\x94\xac\xb5\xca(\x85\xf9`,\x02\x90\xe3\xc8\x16?\xb2\x03{\x12\xf8\xd8\x0e\x94\x07\xf2\xd1\x13;P\xf6\xf9\xe8\xa9\x1d\x08\xb3\xf8\x1b;P\xe2\xfc\xa3\x7fm\x07\xca\x85y\xf4?\xda\x81\x12#\x1f\xfd\x1b\nL2\xb9\x02\xbf\xb2A\xc6r\x8e\x0f\x08]\x01\x18L\xe3\xaf(0\x05\xfc\xbfGhE8HEo\x9f\xfc\x84\x02\xee8\x89\xc0\xe7g\xff\xfc?`T\x8c\x06\xd2\xee\xfa)9\xd0\x1a\x80[[\x8c\xe2>\x1c\xf5\x7fO\xaa(\xc8\xcf\xff%\x86\x88S\xf0\xec\xfe=\xf2Y\x10>\x89\x88d\xe9bID\x1fcJ\xe6\x00F\xdf\x7f@\xbe\xfbr\xc1\xee?$\x80(]`\xado\xe3Y\xc4qpxN1#+\xa9s\xe28\x89\x0ej\xc6-@\xfc\xb6u$\x8b\x89\xf4\xac\xb2l\x83\x06|\x80k\xa4.\x10\xcf\x7fI\x0e\xb1\x81\xfco\xa4N\xea\x0f\xe4\xc0\xef\xff\x8cT\x12X\xf0\x07\xe4\xeb\xe1\xa8f\x17\x04DM\xe6\x9f\xe3n2?\xf0$\x8d&L\xd1@\xfe\x07\\'\x17\x02G\xeb\x13\x82Q\xea;!!\xfbn\x14\xfa!\x1c\x14\xcc2\x9d}\x05\xf9\x08S\xf5\x9e\xe3\xee\xb9\x11\xd0\xab\xfb\xefZ\x80Z\xcf\xee\xbdG\xa0\x89\xa4\xbaO1}\xef9\xc9\x98\xcb\xb1<\xc0\xfd\x9du\x92}.1\xfb]\xcc\xbb{\x05\x08\xa3\x1a\x80\x80dS`/\xd9\x13\x80?%\xf3\xee%{\x99\x06\x92%\xab]\xeb\xb3 s\x90\xfd\x81\xcf\x98\xe7\xf6\xbc\xdby$\x97\x1dK\n=\xee:y*W\x0e\x8f\xec\xac\x04q+\xac\xd7\x08\x1b\xc5\xd9\xa1\\\xf4G\x98\x92\xf4\x04~X\x91\x83'a\x94\x8b:oc>qV\x82\x82\xc0Ok\xc0\x99\x9430\xf9\xeb\xa9\xef\xff\x0b\xfd\x0e\xa2\x0c\x1dB\xb6\xcf9\x1co\xd2\x89\x96\xb4\xc8\xbej\x00f6=\x7f\xe0\x02\x05~\x88\x05O\x01\x02\xd1\xf3\xd9/0 \x16\xb0\x1c\xaa\xe1\xc3\xdf\xf3\x07\x91\x17\xc1\xb9\xc4\xb2\x93\x80\xc5\x01l\xe4GX~\x12\xc0\xcc\x1fq\x80ZF\x93\xdeV}~D\xd0\xdd\x1f\xa4\x99#\xb9\xc5_\x90\xa9\xfb\x83,\xf1\xa5,\"\xf4&Q\xe6=rr\x8b2\xd0\xc3{\x98\xd6\xf4\xfcAnF\x8e\xa9W\xcf\x1f\xa83\xfa\xd02)s\xda\x1e\x92\xe5\xd8s\x92h_\x80\xde\xc7\xd4\xa2\x178\xee^\x10\xdd\xe1J\xb8\xfa\x10\xcb,\xb2@z;w\x12 \x7f\x0f\x0b<\x12\xae'%K`5\xa1R\xc2,\x0d\x968*\xa5\x02\xb8\xb5}\xf6\x0b\xb2;\xe5R\x89\xbaT~\xf6\x1e\x96\x02\xa4\xae- \xff\x023\x86^\xb077/\xeb\x90\x03\x12\xec\xcd\x9d\x94\x10BE\x82\xbd\x13\x00\xc1\xc2\xb2LO !\x98\xa1\xf5B\xb1\x18g\x9e\xfd\x183\xda^\xc8o\xe7\xbe$\x07\xf7\xff\xda\x02^\x07\x94~\x8a%\xc0^\x08\x80w\xb1\xbau\xd6\xc8B\xff\x07\xaebd!2nh\xeb\x01\xe9]_i\xdb@\xfb\x99\x0f\xe8E\xe6\x1a\x1d\xf4@J\xf9\xf0>\x05-\xaf \xc8\xcf\x7fa\x81\x04\x12\x82YT/:\xf0\xa0\x0eV4\x04D\xd6\xf9\x19^\x04\xd1\xda\x96\xac\x83%\x11\x01\x91\x07\xd6\xb2\x08\x07\x1e\xd4!\xa8\x10\x1dx\xb2\xce\xcf\x08O\x8f\x0e.\xc8*\x96\x01H2\xfa3r\xf6\xa2\x83\x0b\xcb\xb2\nVo\x05D\xb2\xce\x9fciD4\x06u\xe8.\x1c\x0ce\x9d\x9fa\x92,Z\xdb\x95u\xb0\xbe\" \x92\x95\xfc\x9c\xf0\xfc\xe8`\x08u\xb0\x02$ \xb2\xce\xcf\xc8i\x8e\x0eF~\x08\x04\xea\x01\xa1\xf2\xd1\x81&^\x0f\x08k\x8d\x0e\x0c\xd5}\x80\x15\xb5^t\xb0\x0b{\x8e\x95\x0d\x01\x01<\xc1\x82i/:\xc8\xa1\xce\x7fk\x81\x00\x9e`\xa5S\xb4\x06{\x8e\xb5N\x01\x01<\xf9\xa5\xa55\xa8ci-\x07<\xb1`\xddeY\x85\xd0\x92\xe8@\x9e\xfd\x9f\x11\xca\x16\x1d\\\x06\xd4\xb2\xec\xece\x89[?'\xb49:\x18C\x1dB\x95\xa3\x831\xe0#V\xb6Dk\xb0j\x844F\x07\x97a\xa5\xb1V'Z\x83:XA\x11\x10Xi\x0b\x0e_\x86U\xb3\xec\xf5eXi\x0b\xfa\x8c\xa1\x8e\x05y\xc6\xb0\xd2\x04\x0b\xeae\xe8\xb3\xca\x98\xf6k\xb2o\xf5\x80qO\xb2\xf7\x8f\xf1a=\x0bZ\x10\x95\xb7zF=\xfa\xdf \x84\x8f\x84p\xf7\xec\xad?#\x90:\xc9>Us!R}/\x8d\xc4:\xff\xe0\x07\x96\xefR\x85\xff\x90\xc8#i\x14\x0c\xd3\\\x02\x7fEHv\x1e\xc8m{\x93lu\x1e@j1\x1bH)o\x7fj\x01HM\xf9 \xb6L\x08\x08\xe8\xcax \xce\xe6F\xdf\xb35\xa7@\xb8\xd6\x92\xb6E~\x8a%3\xd7@~J\xea\x80\xfc\x88\x89\xbc\x12G\xefar\xe9:\xb16ta\xf9\xcbu\xe2^\xa2d\xc3\xc7\x98\xd5\xb9N\xac\x9a|\x8c\xf5\x7f\x01R\xb5\xf0\xe8\\'VB\xecc\xcc9\x96\x9c\xd8\xcf\x9c`\xd9\xef\xf7y\xc2\xc3\xccw\x02\xc9\x14~\x82w\xdaubPY\x1e\xff\xe7\x7f\x8f\x1bq\x9d\x04\xb6\xf3-,1\xbaN\"\x15\xd3_\xd3\x05;\x0c\xf8!h\x17X\nqu_\x8f1\x82.\xe9\xf6>\xc5<\xd35\x10Z\x87{\xbe\xd4\xc7\xc9\xb2\x18\x08\xe6YKJW\xf8\x14\xa3\xb4\xab\x01xc\x96J\xaa=V\xc0\\7W\xf3\xa1\xa3\xce\xe34\x95\xc7\xf41f\xf6K\xb0e\x9fb\xb3\x8b\xab\xbe\x93\xfdW\x93\xf9\x18\xcb\xa9K\x02\x1086\x90[R\x1b\xb1\xce\xe6J\x7f\x86\xd6\xc7\xf8\x84.\xf10\xe3\xc9\xb2\x1c\xc4\xc7\x98\x1c\xb9\x12\xe8\xd9\x81K\xfd\xc4\xbe\xdfZ\x9f\xc3D|\xe9\x02\xa8\xd6x{\xdc\xa1\xfc\xfe\x0fdC\x87\x1c$\xe5\xbf\xc4b\x98\x84\x8c\x9c\xc4\x0e]\x1a\n\x12\xfa9\xedF\xaa\xcd\xa4\x17\xb0\xe4\xfd\x82l\x00\xa0\xc6\xaf \xd5\xf0\x13W\x91\x1a,\x9f\nP\xc0\x9d$\x89\xf6\xb56\xf2\xce\xffY_\xc6\xe8\"\xef\xfc_\xd6B\x1eX\xc4\x9e=\xc0\xb2\x8a\x02k\x0d\xf8\x01\x96K\x14\xdcS\x06\x9d\x07X>Z\x92\xf0e%\xd0c\xd9E\xd5\x16L\xf5cL\x9c\x15l[T\xfcs|\x9a\xa0\xd9KF\xd2\xc3B:\xc07\xb5\xb0\x87%u\x00\xef\x18y\xcf\xb2\xba\x92c|\x88\xb5z\xd7\x07=\xd3\xb6\x1f}}\x8c?\xc2\x07\xd2\xf5\x93\x11\xd8^\x9fb\x0b\x82\xeb'\xa9B\x8b\x0f\xb1\xcc\xb5$\xd4\xb7}?\xe5KQ\x98Ey\xb2\x1af|\x908\x923\xde\xc3\x87n)\x88R\xbe\x94'\xc1\xe1r\x94\xf7\x02\xfez\x1ee w\x90-1%\x8b2dc\x82\xbc'\x97\xe6\x97X\x0c\x93\x90\xdc\xcf\xac\xc0\xa5\x08\xac\x89\xcf\xee\x91\xe3\xad \x0b\xb6\x1ap\x03\x83Ey\xd7\x80\x88\xfd\x16@\xb7k`\xa3\x91 Y]\xdbw1\xec\xff\x8a\x02\x80\xd5\x12\x16\x14\x8d\xe2>L\x07Kb\xae|\x19a\xc4\x15\xdd\xb6\xd5\x0c\xf8\x01`\xd7\xdbx_\x8d\x99\x90p\xca(\x1chv\x8bI\xddR\x14\x0e\x92\\ux\x1f\x0b\xbaK\x05\x0f!\x18V\x80\xf0\x11\xb3\xe1\x15-#\xb5t\xdb,\xb4\xfaNw N\"\xb8\xd6\"\xacI\x82r7\xb3C76\xaf\nR@d\x9e(>\xac\xfb\x9e\x02g\xc0\xe7q)\xca\x05?i%\xa2e\xa6\x90\xec!\x99M\xee9I\"W\xe7}26 \x93\xeb\xf3>^\x1f7\xe7\xb1\x84<$s\xcdy*9\xc7C\xacM\xb9y\xa0\x97\x1b\xdbv\x01$\xa7\xf5>\xd6A\x96\x94\xbd\x95\xf0i\xf8~\x0f\xab\x9an.\x84b%\xf9\x126\x92\xc7J\xfe&\xd7:nn\xe4e\xc2\x96s#/\x13\x11+\xd7\xf2\xf2\x03K\x83\x11\\\xe4\x91c\xaf\x84\xbc{O,\x02rn\x90\x92\x90T \x92\"\xe0\xfbX\x8dv\x05y\xe7\xb7\xe3\x84\xbb5\xdb\"\xe1i\xee\xd6mN\x12\x1cjc.\xd6\x80$\xb00\xe7\x12\\\xcd\x93D\x1a\xe6?\xc6J\xb7\x9b'c$\xb3\xd0\xad\xd7E\n\x91\x85N\xbc~d\xea\xba\x87\x0e\xaa|\x83F\x04V}\x83v\x0f_\xc5\xb8\x87\x81\x9b \xda\xf3\xec]L\x90\x97e\xaep\x01z\x13Sc\xaf\x00a\xc1\xd4s\x02}\xa3\x81\x0f\xd8\xb2\xdeh\xd2\xdc\"\x00~\x8aq\xde\xd35(\x00\xc4\xb171QXv\xd2!\\\xb0\xe1\xbd\xf14\xe4\x01f\xea^\xc9>\x8f\x97\xd5\xeb\x05\xd2\xd3\xe0\xd7X\xc8X6Z\x15\xde#\xcf@pc\xcb \xb3cv\xe2\xc1g,\x1e,\xdb\xb5M\xf0\xf5\xf8 >\xb3\x9e\xd7\xb0]z\x1d\x7f\x8a\x8f\xf3\xf2r\x94%\x0e\x984\xdf\xc7\x94\xd7\xf3\xa2,\x05!\xe41FQ\x8f\x0b\x0e\xff1\xd6\xe7\x969p\x1e\xac\x18,\xf3\x00\xae\xbf\xc8\xdc5\x00\xcf\xde+\xe9_\x18i\xbd\xbe\x9f\xc2\xd1\xf9\x00\xbb\xe0,k\x85 \x8f\xc0\xd3\x00\xb28\x17\xe0B\xe9\x03l\xeb\xf5\x86\x0ep\x8a\x9fb!Y@`=\xb1\xcc\xb0\xec;n\xe2g\xbe\xeb\x04\x8bun[\xa52\xa06\xfc\x1a\x0b\xa7\x95\x12B\xd6\xd5mQ,,J\x9eW\x9eT?\xac/\xb2\xa3\xae\xeb\x7f\x8d\x8dx\x9e\xefH2\xfb\x10[\\\x96}g\x14\x815\x86\xc0\xbc\xc90#Gcs\x9e\x80\xa75\x10\xb9h\xd8 N\xad0\xe4\x00\xf8\x03\x07\x04\xe3\xdf\xe0U\xf2\xfc\xd4\x97b\xeeCL\x18=y\x13\xf4 \xc1n\x7f\xec\x83c\x83\x1d\x12\x85\xc6\x94\xfe\x90 \x9a?\x8e\xc2\x03+h\xf9\"\x9ct\x8c5\xde-P\xda\xb1\x1c\xe3\x05n\x94\xc8\x81\xbf\x8b\xf9\x9b\x17\xb8\x89|b\xe0\xd9\xbb\x98\x0f{Q\x10H\x94\xfe}\xdc\xbd\xb9\xa9\xc2:\xb2gD]\xacH*c\x06\xde\x0e\xaf\x06q\xa3Li\xc2?&(\x16eJ\x9f\xc1$[B\x94Pq\x1f\xd3\xa0\xe5([\xb9\x9d\x83>8+:f\x01S\x0c\xae\x01\xd8Z\xc1\xb5\x9d\xf4\xd9}\x8c\x1f+\xb0hX\x0d\xe5\xb0fX\xca\xe1\xcbJ\xd2 \xaa\xc9\x8a\xba\x05\xc2\x83\xd5Fz\"cpU\x01\x1fR8\x9f?\xc1R\x1c\xef\xeb\x860cZ\xd1:\x066\xc3p\x0d\xc07FR\x8bz\xf6\x04o\xc5\x8a \x8b -\x19\x08fy| \x89\xf7\x132\xedA\xaa\x8e\xca\x13l\xe4\x05e\xed \x96\xe2VJ\x86_\xd2\x7f\xe0\x87\x19OdW\x7f\x86 \x13\x87K\xed\xb71\x93\xe2\x01\x0c\x0d\xef8\x0f\xcc\xd0\xf0\xda\xaf\xe8\xe8\x0b\xbc\xc6\\\x03H'B_\x94c\xc6\x04IBR\xb8\x86%@\x99ky{\xe4\x04\xc1\xb6\x91\x08\x7f\x81\xe5\xe3B\x17\xb5\xd7\xbf\xcc\x13\xdc\xc6{\xd8Y\x84\x8fRI{\xdf\xc4\x9cS\x00\xe6NH\x10V\xa3$H\xba\xbe\xbdI\xfa]?\xbf\xc0Z\x9f\x91\x83'-\xef\x9f\xe1\x0b8\x1e\xaa\xce1G^\xd1.\xfe\x0474\x80`\x87\xd1\"\xb0M\x8e\x1b-\x82\xe0`\x0cT\xf4!\xc1\x80\xd8IR\xe0\n\xd8*\xc3\xb5\xf4\xfe\x18Sx\xe5\xb4\xfb9&\xd6+\xc6\xd9\xfbs\xda\x8f\x01\xe1Z\x02$\xb6\xf67\x04p[_\n\x12\xba\xc7o\xd7\x931~[y\x97\xdc\xc7k\xcdo\xa7\x81\x13f\x83,\xb1\x1fT\x00\x07<\xb5\x9f\x16\xa3\x07=\xa6#\xcd\x1dy\xc4\xce\xd8\xaah\xad\xdf6\xa0\x9c\xc3\xb5\xe8}\xcc\x92Vn\xe7~\xe0\xf7\x12?\x97s\xf9)\x16\x18JN\x946\x08\xd8\xae\x1ec\xa5\x81\xdf\x1e\x17\x1b\x8e\xa5h\xaeY\xe0\x07d\xc3\x13Mq\xf1\xa1_\xd1nA\xd8\x10\xc55\x00\xf3m\xaeI\x0e\xd1&W\xd4\xbe=\xc6\xd7&\xbcnCW\xc0tE\xf8\x06|&|i\xe7\x82\xa0\xdb\xb8[\xb0\x96~\x82'\xb0\xa2\"%\xc8IV\xdf y\xc9\x13\xe9R\xff'\xd8A\x8a\x1f\xb8\xa2\xc2\x11\xf2\xd9\x87\xad\xbf\x87\xe9\xd1\x8a\x80\xa4V\x10?\x88\xb9\x9b9:^\x86\xac\xfa\xca\x01${\xf0\x9d@^/S\xdeY\x14\xb03\xd7\xbe\x13\x04\xbe\xbc$T\x96G\xc2d\xcf\x81\x98\x80\xa5\xe6>\x88 \x98\x82\xf6\xf9Hu\xf5K|\xf3\xd0\xef\xfb\x10\xf8\xf8\x9f\xff\x06\xcf\xb3\xdf\xd7\x10Z)\xd0 \xdc\xd59\xcd\xe4\xb1\x9c\xd6\xd7\x00L\xe2\x8a\x01`5\xe2\x9c\x1f\x04\xdc\xc3l \x13\\(ec>X\xec\xea\xdf\x82\x9e\xfa\xb70 p\xc0B\x87\xc5\xaeb\x9e\x18\xeb\xfbA\x16J\xf4x\x0f\x9f\xd3~\x18 \x06\xf0\x9f\xc8\x96\x19\x96\x81\xf5\xb3\xbea\x19\xf8\x10\x9d\x8b\x92E\x10'\xee\x91=\x88\x12\xa7\x1e$\xfdX\x1eb\xc3\x87\x00\xc0\xbd\x00\xe6g\xe7\xa2<\xf1y\x92%p\x0bL\xe6\x14;I\xa6\xfd\x1e\xb0\x10\xdaO\x1cW\xba\xb3\x7fL&& \x92\xa9\xff\x04\xd3, \x12L\xfdc\xbc\x9f\x12rJV\xc2\xc4_\x82^\x96 <\x01 zE\x82\xb0\xe0.@\xf30\n\xb2 \x02\x04}aF$@\xd2\xe1\xfec\xac(I\x08T\xc2\xfb%A0\nl\xfa\x13\xa0\x93P\x0bK\x19\x02t\n\xa6\x85e` \x82\x06\xb1=W\x80\xbe\x03 l\x13\xe8'\x0e\xb0\x97\xb7\x08%HT\xe8\xc3\xbbX\x08?\xa7y\x05\xd9{\xa3\xfbb\x81p\xa0U\xaf\xff\x07\xf3\xe2\xf3\xca\x08\xfd9\xdevm\x9d\xfe\x1c\xb3\x17Y\xc3\x13\x12\x08^\xb8\x81\x81\xe0\x15\x18\xc0\xcd\xed\x13l\x970\xa2\xc9\x13L\xd6\x00$\xf9\xfb\x13L\x8e\x15\x0c\xe6\x8a\x91~\xc0S5Yz\xf3.`0\xc8'\x988\x9c\xd7\x1c\x0b\xab\x17\x03\x0d\xc0\xec\xf7\xbcTd\x1fb\xda4\x00? ,\xac\x0c\x065\xc5\xfd\x11l\xce\xdbXx:\xaf\xaeN0\xa7\x1e\xa8\xab\x13\x82qpc\x80\x9b\x19Hg\xcfgO\xc8\x1e\x83\xbc\xf2\x04s\xaeApK~\xc7\xd3\x1d\x84\xea\x00\x92\x05\n\x8b\x98a\x0b\x10\x10\x98\xec\xc5\x9ckud]\x96U}\xaf\x82\xcf\xb4\xaf\x01X\xc6\xf0G\x0eh^\xb6\xb6\x06~\xe8$\x87\xab\xf6\xd5\x199\x83@\x9d\xe8\xb71j\x0b`\xec@\xca$\xbaw#\x99\xc5\xb4\xf5)\xd6\xd4\xfd\x91\xb4<={\x80Y\xb8?\x8a\xa5\xc3\xec\x7f\xc2\xf8\xb4:\x8a\x03\x1f\xd4\x1f\xe2`\xe2\x87l\xc1v\xf9\xe5\x87\xae2\xb0\xbd\x8d\xafc\xcc\xde\xdd\xc3\x8a\xb7\x84\xa8\xd0\xfd\x0f\xb1\xbe\xec\x87*\x87\x06\x99\xd1\xaa\xc2\x12\x82q\xea;\xd9\x8d0s\x81\xc6<\xc0B\x9c\xca\x08\x0d\xb1\x1a\x98\x81V\x9c\x97,\x8d\xf2\xa4\xae\xd9Uy\x11\xc8M\xf6$\x92X\xc4\x0f\xb3\xc0I\x86\xd2 \xf7\x11\x16\xda\xfc0\xd3A\x14\x1fa!q5\x1c\xfb\xa9/\x1d\xac\xc0fb![\xba\x88\x89qz\x0bK\xe5\xab\x1b@I\xb0m\xd5\x8f@\xf4!X\xabo\xbc0\xc1\xf35\x00\xdf%\xac\x1a\xae\x86\xf9\x92o \xd8\xac\xb5\n'\xf9s\xcc\x07\xd5 \xff\x1c\x0b\x16~\xed*\xf9Z\xca\xfe\x18\xb3\xf9U\xcd\x15\xc9\xe12\\\x11k?\xdaC\x92\xe2|\xea\x87Z\xf0&49\xf5A\xc8}HF\x9d\xfa`#~\x88\xbd_%DZb\x1fb\xca$@c\xfb 2\xfb\x0e\xeb\xfcS\x9f\xe2\xcbp\xdf@\x08\xc1\xcc\xf7\x00-\xb0\xee\xe1+\xc0?`s\xe8\xaa\xbaq\xc1\xac\xdbW\xdf1V\\\xd4\")\x9e\xfa-\x0d\xc0\xeb\xa8l\x1b\x18%\xc0\xb4\xf1\xf7xm/j\x06\x86y\xff-\x0d\xc02\xca-E6\xff_L\x1d/\x1a4\xc5\x87\xe4\x96\x81`}\xea\xa2\xc1!,\x94\xde2\x10\x8c\x90\x17S\x9e\xc0d\xf0\xce\xde\xd2\x90\x7f\xc0\xf2\xc4E\xbdQ\xd8\xa6uKo\x14\xe6\xf8\xdfw\xe2X\x9e!|\xe6\xf64\x00\x930 \x90\x97\xbfX<\xf9\xbe1\x8abo\xa5=\x03\xc1\xab\xf9}\x18/\xe9\x1d>\xe3\xbe\xbf\xafw\x0b\x0b^{\x1a\x80\x91zo\x90@B\xa8O\xb1\x90\xf5}\x15\x0d\x8cwdOE\x03cn\xf5}\x85qX8\xd9S\xd64,\x7f|\xdf`\x03\xa6\xf1{\x06B\xea\x18l\xc0\x82\xd6\x9e\x86\xfc9&\x9b\xc1\xa2\xd6\\\xf0\"\xae\x99\xfc\x02\xf88\x04\x06\x82W8pJ1\x04\xf80\x06\xce q\xe0\x16\x13\xb3\xff5g\xd4\xf3$\xbe`\xdc\x0f\x0c\x04\xabOk*k\xe6\xaf\xb0\xf8\x14h\x00\xdeM\x01\x80\xfc\x8e\x98\x11\x05\xc6\xb3\xccR \xcc\x8exC\xd7\x1c\xf9\xe2\x9a\xbe\xc4\xc23\n\x1cH\xb8\xf61f\xf0kZ\xab\xc7RK\xa0\xed\x00\x98\x85\x98\x986\x1b@\xc6\xf6\xfd\x14\x8b\x18\x12\xd2\x97\xec\xe0}|\xf9 `\n\x84e#\x01\x02\xe1\x81\xa8\xa2\x02\x14\xc8\x95x\x07\xcfH\x06\xd6I\x81\xe5}\x8a)\x89\xb6\xe7|\x80y\x8f\x80e\xb2\xda;\x98\xcb\xa8\x1b\xd2'\xa4\xa7\xc5\xcc\xf1\xa1'\x8a'\x06\x84\x89z\xe0@D\xf2\x13,\xfe\x0b\x00\x98\xa8\xfe5\xb5\x18\x05g\xd5\xb2\xbf\x8f\xa9E\xd0\xd3\x10|\x98\x03\x9d\xe4\xef\xaf\xb0n\x10\xf4\x12\xb0:\xfc\x91\x0d \xea\\\xa7\x80=9\xecGX\xd1\x16\x904\x00D\xc6\x1c\x12`2\x8f\xd1#\xcc\xac\xd6\x8c\xb7!V\xd0\x03\x03\xc1B\xca\x9a!\xbd\xf8\xf8\x05\x06\x82\xa5\xa4\xc0\xe5\xb0\x13\xefb\xd6\x13\xb82\x16\x15\xaf\xc1\x1a\x90F\xb2\xa5\xf0\x99t\xec\xb9R@}\x1f\xb3\x89\xc0\xe48\xc4\x84QB\xc0\xe2AN\x9d\x97x\xda\xe1\x143\xf1\xc0K\xf2T\x03\xc9.x`\xd2x\x87l5\x18!1 \x06\xf2r\x1f\x9fT\xe9\xf2/\x88\xcfY\x81\x07\xe01GhP%.\x80\x90\x81\xb5\xb2\x0d\x89R\x8f\x8a\x85\xc9V\xb7\xec\xedN(\x89)\x80\"\x04\xb0,g\xba\xd1\xc7\x90\x1cj\xd1\xd2\x12\xf7\x03H\xc7J\x91C\xc0\xc1\xf9\xbf\xbc\x14x\x19\xa1\x94t\xd7.\xf9\x8dc\x0b\x85.Ur\x1b\xc7\xb6\x9ej\x11\xed5\x8ei\x87(u.\x88\xa0\x8dw\xb1\xe9VLZy\xe0\xeb,\x7f\xc4\x1f\xbeT\x06\x02|\xdf!\xe7\x85\xf73\xb3|\xa0\x1ec+5\x0d\xf8 FaQ\xa4j+$\xf6\x99\x80\x14!\xadT\x8b\xa4\xb5[-\xcb\xa8iA)r>t\xa9\xf4v\xee\x0f\x8a\x1e1\x11\xb6\x05'`\x8a[\x8a\x9e!\xa1\xa4\nV,\x8c\x0d\x83\xab\xd8\x82%\x1d1\xd4l\x98p^\x84\x98\xe1\xd9\xc8FJ)\x1f\x1f\xe0S_.\xa0\x90\xe9CL\x9c\xcbe\x8c}\xf2\x01\x16\x93D)\x08\x92)\x0d\x19\x0b,P\xa8:-|\xa7\x0feJ\xa1\x1aXG(\x17\xd0\x07\x00\xeb\x04(\xda\x03\xe3.\x8d\xf4 \x82\xd0\n8\\S\xfc\x80\x0bi\xba\x19p\xc1CD\x1a}\xf3C k\xc9'\x80\x9e\xbe\xb4\xee\xbb\xba\x99#\xf2\x9e\xf1 x\x8c\xd7+(\xf9\x04`\xedM\xc1\xe4\x1a<\xc1\xb4&\xe0\xa9\x9a\xacE\xce\xe0\xa9r\\x\x82o\xd4\x03\x9e\xa6\xa5\xab;,\x81\n\xb0\xb6\x13`\x0dZ\xc0\xf8m\xe5\xf7jYc\x01\xd5`\xb25kO\xaa*\x14\xa1U\xa2\x08\x12\xb0 \xe1\x8a\xeeHrA\x94\x80\"\x95\xb8\x0d&\xcdC$\xc7x\x00k\xd9\xb6|\x06\xd7\x92GD\x18\xd0~:T\x1eOJ\x04\x92X{\x12\xa5\xc0R\x01=1\xb4\x91\xec\x00\xa4\x00z\x93X>\x12E3\x1f\x10\xca\x98:Z\xf9\xc6\xf8\xb9\xa6\xafF\x88dh\x8c\x92X\x98ZS\xaa5\xa1\x95\xb5\xdfk\xa4\x81\xc08}ac\x88\x80\x80`J8vz\xbbg\xb3\xc7\xa4z\x82\x041Rc] B\x92vb\xf8\x8c\xc8\x8b\x06\x82\xed\xbbk;\x0b\xac\xf5]\xfcQ\"\x05\xe5\x9a\x99\xa5l\xa0\x9d\xce\x08\xdd6Ng\x84\x86d\xb5\x82\xa4T\x8c\x16l:QP\xa8K\x84=e\x9a\x9d\x7f@hQ\xc9U\x8d\x98v4K&t$K\xe0:\x97hK\x81\x0e1&\x89\xf3\x83,\xd1\xeerdRy\xe2\x19\xc3\x0e9\xb3ybB\x90\xc9\nV|\xd0>\xb2H\xf3\xda\x07\xcd\x02S\xb7\xfa\x1f\xe3\xdb+\x13.\x83g0r\x80\x16\xfc%\xd6\xec\x04\x80\xc3\xe3\x1b\x04v \xc4\x89\xf71\x91\x1e\xc1\xf7w\xf0\x94\n\xfeT\x032\x96\x0dl\x1e\x03\xb0a)Xa\x03\xb0\xb2y\xe0k\x92\x91\x93\xec\x01\xc5z\x0f\xdf\xfd\x8et\xb6\xc5g\x1fa\x99\xf9\x12H\xa0\xd8\xbc7\x82\xcf\x98\xbd\x8eL\xca*l\xe5\x18\xe9H\xe6{\x98\xb1\x8f\xb8\x93\xe6 \xf7\x8a\x07\xb6\xb0\xf2q\x89{~>2Ndoa\x82{\x89\x07\x81\x1f\xeak\x01l\xf4\xbe\xa4\xd5\x01l\x88\x1bi\x00>\xe2\xa3\xa1\xdc\x9c\xb7\xc9\xea\xfb\xae\x0c?\xfb\x18K:*-\xe8=l(\x19\xf9\x9e\xfd\x8d\xa2\x91\xef)\xba\xf0\x14\x13\xd6\x91\xef\xd5\xa4\xcf-\xb2\xc0`\xb2.!\xf0\xc6\x16^\x1b \x82\xd1a \x0e@R]\xf9\x08/\x81\xcc\xc9\xaa\x13\xaf\xde\xc3\x8cq\x14\xb8\x90\xad\x10\xdb\x8fG\x01\xb3\xb4g\x1e\x1a\xa3\xb0\x0c\x1e9\xf8%\xa6M\x12\x02f\x85:\x18\xf8\xfc`\x1f\xbb\xb0'\x9d\x8c?\xc6\xd4:,R\xcc\xd3\xb1\x97r\xc9S\xa0\xce$\x89\x97}]\xdf\xe5|\x86\xb7*4\x10lz_\xd7w9\x9fa\xae\x11\x1a\x08\x96:C\x93r\x96\xf6S\xce9k\x19\xb9Jt\x89Q|\x1d\xc88\xd6\x14B\xf8\x8c\x15\xca\xd0Pw|\xbaT\x82_\xb2\xd4\\{F\xbd\x8fYU\xc8\xf5\xdd+V*D% y\xc7\nQ\xaa\x02\x85\x99\x88g2\xfdu>p2\x7f\xcc\x11\x1fy\x13KW\xba\xdc\xce\xd0w\xf7\xa6*\x16N.u\x99'\x87\xcd%Ko\xf5`KS\xc8S\xaer\"a[AX\x04l[&\x9cf\xdc\xa3A%$\x82\x02\n\x96-\x7fD\xde]\xe7\xfb\xca1\xf9\x07!\x19\x82 \xaf&\xf4\x86\x17\xf1\xd5\x18\xb6\xae\xf9.6\xb8\x85\x1a\x80\x87\x19\xea\x988\x8a\xd9*,\x0e;\x16\x86:\xce\xcd\x06\xb8]\xdfX9\xd6\xcd\x06O\xeb@:4\xccRI\xef\x13\x96\x1aB\x1d\xd6b!\xc9\x03\x00a\xb95\xd4\xc6[\x028\x9f\x01\x06=\xa5\x030\xd1\x0eX\xb7\x0cM\xb8\x03!\xacCexx\x8a\xd5\xbbPj\x0b\xf7\x08\x0e\xc3Cq\x0f1\xf3\x0b}\x10>\x1eb\xa9/\x04\x8c'\x0d\xad+\x93'V\x11Be\xf2\xc4\xea^h|8\xb0\xba\x19\x1a'\x0eZGI)XD\x0e\xf5E2]Du\x97\x8c\xa5\xb5\xb0z\x13L\xc7P\xb9\n&\x03\xb1\xdc \x92M\xb2\\!\x92\xed\xd278dx\xc5\x15\x8emJ\xe5[\x1c\x1b\x19jM\xdbr\x0e@\x1b\xa3\xddh\xb5\xf5!&W\xa1\xd1[\x1fbkZ\xb8\xa6\xce\xc8\x13:8-\xc1c6\xb5\x1e\x9dM\xb8#Y\xd8[\x98\xbb\xadG\xa1\x04\xfa\xe1@\x13w\"l\xac\xebX\x11\"\x9d\x18\x01\x16K\xec\xfam62|\xd0\n\xf0\xe7\xf5(\xab&\x95\xc7\x86\xc9_\x01.\x06\x81)\x7fQ\x06\xc5b\xda\x86b\xe3\x9d\x0d\xe5\x0c\xf7\xc4V\x9e\xa2\x08\x0e\xcclh\xadX&\xcc2\xd6\xa3\x8c\x86\xe2\xd8ZB\xf18\x14\xe1\xa3L\xb9B\x13I\\@\x8c/\xb4\xbd\xa2r\x87\xb6\x03\xc7N}\xbb\xf0\x10\xf4C\xac\xd9\x02\x0cr\x98c\xe3\xd5z\x94aO\x00r\xe8Q\x19\xe3\x0c`[\x19\xabG\x00\xa1\x15\xb2`\x0d\x8dS\xb0by1\xd5U\x05\xca\xc8c\x1dHY\xea\xb2\x0f\x95^\xac\xd6\x95+p\x06\x93\xd7\xf5(\xab\x93\x07\x9f\xfc+[sT(|\xf2\xd7\xb6\xadV\xa2\x00\xf6\xc8\x93\x10\x85\x04v\x18 \x01\xd6\xa9\x01\x06H\x805\x8f\xf5(\xdbL\xb8\xcb=\xf5\xd2\x0b\xb6\xf3\x95\xe0f\xad\x9e\xfc\x1b\xdb\xe4t\xb1\xea\xba>\xb4P\xac->\xe6I\xca\xcbD\x0fOG\x94\x92\x195\xcb\xc8IdlTHc\xa7EOA%\x8b\xe1Y\xa86\xe4\xc1\xd9\xce{*\xe7\xdb\x03+\xb6\x97K\x15\xcdYX\x84.\x18\x8b9C\x83\xd6\x01V\xcb\x15Mb\xd3\x97(Z\x8c\xedO(k7\x05\n\xb7\x1c\xa2#\x8b\"\xae\xcb\xb9\x07\xbb\x8e\x0d\xfa%x\xb1\xeb\xd4XQ*\x86v\x1d\x1b\x1aK%\x8b\xf3\xf4\x1f\xed\x0d\x96\x16\xea\xc75\xb3Ck\xf4\xc0\xc23\x8bn,\x93\x93\xc0\x82\xccXx\xa2,Qeg\xc4Z\xa4J\x15=Y\x86\x81\x99?\xd1\xd6\xe3\x1a\xa9@\x00\x9c P \xf1mPH\xcd\xf1\xf4o\xe9+\xb4\xa1\x8e\x80\xbbG\xa5\x810\x8e\x02\x1d\\\x88M\xc9!?}\xc7Z &Id\xcc4\x8f\x1b\x88\xb2\x02\xabI\xd6T\xd6\x93\xb4\xf4\x9b\xa9|;D\xc8\xd7qx\x9f\x10\x8b\x96\x81\x10;T\xa6\xbc\xd1h/\xe8yr\xaa\xe2\x96K\xc0d\xa8\xaeK\x9e/\xa7\x07\xbfRD\xb5C\x04\x0dy\xa5A\xec\xc3\xf2+1\x0f\xcb,\x9a\xbfG\xbfrH\xda\xf86\xbe\x13\x0es\x9d-\x96\xd8\xb3\xc7\xfa='\xcb.^^\xd6\xcf\x14\x12+\xd8e\xf3\x82!\xb1\x18\x8cM-B\xe6\xc6\xa6\x16Y\xc6\xb1N\xbbe\x19\xc7\x18\xf2\xcf\xd8 \x17t\xb8\n9\xbc\xe3\"\xfe\x1d\xdf\\\x85cm\xcbz\x1f\xdb\xe9\xc3\xb1\x8ee\xb0\xf5\x06. v\x88\xb9\xc4\xb7\x815\x0b{\x9f\xd0\xdd\xb1\xe1\n\x0f\xfe\x9d\xad\xa6~[\xf8?X\x80\xfb\xc6\xe8Oh\xda\xbe\xe6\x99\x04\x15\xf65\xcf\xb4B\x14W\xa3\xb0P\x9b\xc7\xf1\xd5\xe1\x86I\x11\x81\xef*\"\x03\xc1W\x81Q\xdd\xf3\x99\x91\xba\xac%\xeffn\xe8\xf4\x11XF\x894\x00kc*\\\x1b\xef=Dk\xff=\xd6\x89\xa2\xda\x1797\xf4\x9bM\x9f\xe1k\xed\xc8@05\x8a\xe0!\x98g\x1fa\x9a\x13\xe9\xd7\xce\xb0\x93V\xe4\xa5\x91\n{\xc2\x96\xdd\x8d\x15H\xbd\xf0\x19\xde\xff\x88+\x00Y\xf8\xbeZ\xc6G\xd8\x95iC\x1b\xfeI[\x1a\x80\x0f\xa6\nV\xff5\xde\xa9\x0d\x93\xc4\x824e \xd8\xa4\x1d\x81\xb1\xfdC\xcc\xba\"\x9d\xa8\xe7\x116\xc3DC\x81\xfd\x9fc9&\xaa{\xa112\xa6hl\x06\x8f\x02\xbd&d\xeb\x03\xf3(\xe1#\xec\xb4\x13\xe9\xc4\x12o\xd2Z0\x17,\xcbn(O\x98\xcf\xb0\n\x1bi\x006]o\x8c\xf8\xc0\xb1\xceR\x01~\x83\x19\xe8\x86\xf4\x8f\x90\xe9\xa7\xb1M3*@x\xef#%R=\xc2\x86\x9fhT\xfb.\xec\x861\x9e\xe2+\xd2\xc8@\xb0\n`\\)\xb1\xf1i#\xe6\xa1\xf5\xc5U|\xbdo\n\x16E\xb0_Z\x14sx\xf0\xf0\x11\x96\x11\x8c\xef%y\xc5vC\x0e\xeb1\xa1 N\xe2k\xbf\xc8(\x17\x04)\xc0\xb3\xf01\xa6\x14Q\xe2\x81\xb5\xe7mL\x8b$\x04R\x8a\xd8`2\x13\x17\x16>\xa2\xc4\x13\xb8\xff1A\xe4\xc4\x1f\xa8\xec$d#\x13\xf5b\"\xde\xc6(I\x83\x08D\xb9\xc7\xf8>7J$\xa9zLH\xb1\xfd%\xe1\x0d\xa3\\\x90\x01k\xc7\x0fB\x89u\x8a\xa4O\xc8.\x1a\x08!\x94\xeau\x8f\x07\xb8\xca\x86\x11\xf4\xf0\xf6F\x06\x82\xa9\xc8F\xe1s\x8bq\xb2p\xc7%\x8f\x1a\x03\xc8\x81zx\xa97T\xb6\x06\xb2\xd2\xea;\xd9\x9a\xb1\"q\xefbanc\xccu|\x11!2\x12\xa6\x82k\x9f\xfd\x19fe\x1a\xaa\xc2 \xff\x94\xac\xfb\x98'\x9bN\xc2\xc3l\xc8S\xb86\xfc3|\xd4\xb42\x85M\x06B\xd7\x13\xd8\x87\xe7Q\xd1\x01-\x95\x94\xb8\xf2\x14s\xfc\x92}\x82B\x94m\x02\x016\x9d\xc4<\xcfF\x81\xc0\xc61\xf9\x8b\xe13&}1O\\\xc91\xfe\x19\x05\xf82\x1f\xca\x0c\x05\x8c \xd6\xf3Mlt\xd6\x94\xe7\x01\x99>O2\x1eJ\x81\xecM\xac\x85lj\xfe\x8ayu\xac\x01XX\xde\x84\xa7\xd2\xb1\x96\x1b\xc3S\xe9\x98\x1c\xc7Cxu\x00\x1f\x8ax\xa8^q\xa6\xfeX\xf1P=\x17\xfd\x17\xf8&tS\xf6\x8c\xe9z,;\xc6\xfc.\xf63wX\x9b';\x86Q\xe1S\x12\x07N\x08\xef\xc7\x93\xa4i\x00\x82\x84jx\\\x02\x06i\xb7-\xd5$\xd1?j\xf9\xec(\xc6\xff\x11\x16\x92\x05\x104\x7f|\xb2\x04D\xd7\xc2\xa6\x04\x01\xf3\xa4\x9aE\xde\x81\x93 p\xf3#\xb8\x11\xe4\xe0\xd3\xfa\x18\x0bE\x9bA\x9e\xea\x87\xd9?\xc6h#\xaa\x8d\xc2:\x88:l\x1f\x11\x1c \xf24\xdb\x97c\xfc\x08\x8b\xeb\xf1\xc8\xd6\xdaf\x04\xc9\xa8\xc4\n\xcba\x92\xcc\x83\xb1\x90\xb9\xb4\xa1\x10c\xd9\xa6\xbe|\xc5bml\xa4\x04l\xbf\x8a\xa3\\>\xf6\xf81\xde\x95M\xb9\xecO0\xd3\x05S\xe4}\xcc\x0d\xe3DE\x18a\xc2nL\x94\xf7\xb1<\x1d\xc3[\xf5O\xc8y\xd0\x96K\xfa\xdd\xad\xe9\x9b\xbb\xa50&:\x02\xee\xaaw\x83\xad\xe3(\xdf\xb3\x90\xb6-\x97,5%\xaa\x96\xf6\xda^\n\xab4f2e\xe3\xab\x05T\x8e\xd4\xc2\xb2\x96\x84+;\xce\x13\xccu%P\x87Ya\xe9J\x00\xb5\xc5\x10\x0fh3Q\x16\xc37\xe9\x16i\x08>E\x12\x92\xdaq0\xd1Qht\xf8p\xc1j\x19z\xc3\xc0\xd5S\xed\x98\x02m\x96\x1ej'\xd4)\x89\xfaN\xa0\x04\x00\xac\xb3\x08\xa0V3\xde\xc5\xca\x94\x00\xa698\\\xbfKx\x87z\x7f\xed\x1e\x96D7\x93(\x8e\x12\x9dI\xed\x1e\xc6\xcc\x02\xac\x12\xb5\xe1\xfa\xa2a\xf0\x9b\xb7\x80\xea\xb6-N\xf2\x04\x04\x83\x07\x98en\x1a\xa1\x11\xdb\xc6bc\x91\xc6\x86\xc9Mx\x95\x87\xac\xbf\xfc\xfc\x1b,\x96\xc6y\xe8*\x13\x17\x06\xbd\xae9,&\xd7\xb75\x00\xef\xc8\xed\xbal\x8b\xafk:\x87\xcd\x13\xb7\x0d\x9d\xc3\xec\xe2\xb6\xc1\xd9\xb7\xb0\x80\xf9\xbaY\x15\xact\xdf6\xab\x82\xf9\xfc\xed\xdc\xc9x\x12\xfa*3\x01\xc9\x8c*\xe0z\xf4\x98\xeb\xea\xd8\x94\xd7l\xdf\x15\x91\xc2\x02\xd5\xeb\xbb\x1b;\x0b\xec\xdb\xado\xe3*Qf\xf9\x9c\x98\x84KX\x9b\xd0B\xec\xbd\xbf\xfd;\xcc{\xb6\x8c/5\xde\xa0\xc4@0\xc3I\x1c\x0f\x12\x90\xde\xc3;\x91\x94\xb34a\xfa\xb1\xa5c;1\x1a&\x1a\x80u\xf0\xc4\xa4U\xc2'S@\xe4\x94\x1ea^\x9f\x14 \x97hs*s\x12fo[Z\xd9\xc4R\x97\xb9\xfc\xa2\xfd\xab\x1a6\x00\x10\xbc\x0f0]KLR%:\xe6\"\xa9\x12\x19Bq\x97f\x81\xa8JX\x84J\x8atKXQL\x8atK\x18\xf1\x13\x93n\xe9\x03L\x0f\x92R\xba%\xac\xe9l\x99tK\xefc\xa4O\x8aLLX\xd2(]\x03\x92E7 \x97\xb0\xc2\x94\x14\xb9\x98(\xeae>\x10M\xac5IH\xa8\xfd\xe7q\xbd-\x93\x8d [\x18\x13\x03\xc1\x1c%1y\x9a0\x05HL\x9e&\xb2[:O\xd3]\x1b@\xd4\xb9A\x01*O\x13\xa6\x84I)O\x13\x16\xd3\x93R\x9e&<\xa3-\xe3\xa7\x8f\x15\xfb\xc4@0\x03\xdf2~\xfads\x0d\x04\xd3\xd6\xc4\xe4i\xc2\xc6\xb3\x04\xf24\xe15\xd8\x02\xcd\x91\xe0>8\xc3b\xad'\xd1y\x9a0kM\xbc\xc0\xa4\\\"\x87\xdf\xe4p\"\xf8V\xe4p\xa2 \x15\x17Jh\x19\xc8\xe9\x04?9\xf0t+@g\xc9%\xd4\x99;\x81\xc9\x92k\xab\x08\x88K\xc6\xc6A\xdey\x0f\xeb\xae[+\xe7\x05\x91\xc3|5\x81W\xfe\xf1g\x8b\xff\x0fvV\xd6E\xd03r5\xc5vcT\x90<\xb7\x9a\x14\x890\xb0=\")\x12a\x90\xe6U\x0eh\xb2BZ\x90 \xdd\xe8\xc4\x16\xf8\x16\xdb\x84'\x93\x17\x7f\x13\x9d\xd8\xe2\xa7\x04\xe7\x8a\xc4\x16\x98ln\xc98\xba\xcf\xb1\x8e\x95\xc8\xcf\xbf\xa1]DR+'\x8cX\xc6\x88\xe3|]\x18\x8bQ$9\xe6>\xc8}\x820\xa7\xaa\xf7\x84\xb5v%g\x17fTE\x89J\xd4\xfbO\xf1\xfd_\xd1\x91I\xda\x85\xe9\xbfl\xaa\x9c\xb5\x0b\x93\nY\x80\xa6\xed\xc2*\xb5*\x86\xf3v\xe1\xd3b\x8a\x95\x12wa\xb3\x16*\xa3\xf3\x0ea\xf1G\x16;W\x8b\xa7\xe5\x04V:\xc2\x95\"Z\xa9\x10\xf8\x06P\x8c\x13EP\xf6.\xeb:\x97\xf2\x80A)\xc2.D)\x9c{\x8bPf\x9ff\xd4\xb2.\xa2N\x97\x85em\x0d,\xb0\x13[F,\xcfr\x13Z(\x8a\xa0\x8cYx:\xc4\x17\xf1\x01\xa1\xceVG\xc4\xa6B\x85\xf7\x1a\x96\xdad1\x925\x0bK\x04\xaaTur\x98R\xa9B\xa5\xa4WX\x8b\xab\x94\xd0\xf8\x87\x05s\x94\xd3\x8c N \xae\x9b\xc0\xbak\x02\x87\xee\xd7D\x88\xf2\xd3\xea\x83\x8d\xa4\xa2I\xa6CP1\xd0\xe9 \x08\xfa\x05\x90\xf3\x81HQEf\x1bL\x0c\x93jf\x1b\x02\xd6\x81\x0cO \x933 d0WLL\x02\x19\xbc\xe8\x89I \x83iKbn\xd3\xb0&\xb8\xa5uQ\xc2\x95\x8d.J\x04\xde\"/ \x1duqGB\xf0/\xcaC\xaf\x94\xe0\xfe\x03\xac\xde'0\xc6\x8e\xe53\xdc\xf8>\"\x9a]\\r;$<\xc2d\x03!\x04\x19\x85\xf0\x90\xb3[d\xea\xc0\x06\xb5-};E\xebh]\x1b\xfb\xc6l)\xc9\x8b\xec}\xedw\x99\\\x83\x08\xd1&\xb9\x06\x16l\x93\"\xb9\x06\x01\x15\xa9)\x082\x17t \xc7ni\xdf\xc3\xf7\xb0\xa5\xab\xe4db\x81H\xc2zE:\xe2\xc5\x93\xf7d\xbc\xb5\xe8:\xf2a0\xefR\x88\xdc\xc9'd'G*\xaf<65\x08\x00\x84\xaa\xfd\x0d\xcd\x02\xb5\xbdqn\x07\xce*\xa9\x16\xf538\xadX\x9c\x01G\x9f\xe3\xf4\xab$\xe3\x1fb!_\x00\xd4E\x1aa!F\xf0\xc5rQj d\xc9bG]\xc1\xfe\x92\xa0\x99\x04\xe9w\xfd,\xd0\xc4z\xf0\xd3\xdbJ\x96x@\x98\x9f\x80\x80\xaf\xd1\x9f\xd3\xb5Ko\xab\xdc!\x0f\xb0\xb0,!P\xefg\x965\xbf\xad\xfcg\x88\xd4t[\x076`\xb5\xa7\x08\x94x@(\xce\xedR\xf8\x82\xb5^\xe1\xd7o\xab\x0b3 \xb4\xd4D_<\xc04P\x82L \\\x0dPuH\xebJK\xd9{\x98\xd5\x97^\xae'R@=\x08j\xe1g\xa8\xc8.\xd2p\xc0\x86\x02\x85R\x8f\x17\xcb\x16\x06\xd8X\xa4h\x8a\xb0\x11Yn7\xd4#\xa6\xf8\x93;p\x83L\x1e\xf2Oo\xe75\x80\xda\xeb\xa5msk\x89u\xc8\xd4hR\x98#\xa7\x0d\x02I\x03mJ35\xee\x87\x98jogp\xfa\x08 U\x80\xbf\xb0\x01d[\x7fAD\xc6,q\x04\x9f\xe6q\xea\x07r \x7f\x83\x95$]D9_as\\\x9a%\xd2\xeeE\xb2\xdfm\xc3\x01|H\xf0Z\x1dL\xc2r\xf3\x9e~\xb3\x9b\xa8\x0e&\x16\x89\x02\xe0d\x91\x19\xe7=\x9d\xaa\xe7)\xe1\xbayo\x94\x83\x07\xf3S\"[\xe7=\x90\xfa\x9fb\xbb\xa2\x80@_\x84\xc0\xe6=\xcdE\x9f`\xb2\x9c\xe6=\xc3E\xb1^Z\x1c#\xdb\x1a\x990*+H\x11\x05\xcb\xb4\xcb\x11T\xd6\x0e\x8b\xb3d\xaf\xad\x12\n\xdb\xa6 \xd0\xdbu\xeb\xa3\xfd\x1f\xb1-A\x80`\xd3\x9f\x12\xec\x11 \xc8\xf2F8\x86\n\xf6\xa2\xfaj\xee\x96]\x8f\xb0\xd6*\xc0e\xd7#\x8cL\xe5`_\xd2\xb6%\xd2\xb7\xa6\x04r=\xaa\xeb\xa5\x14\xe1k\x19\xa7\x0eY\xb3\x80\xca\xaeGD5\x15p\xedzD\xd4S\x01\xacUPs\xb7^\x0b\xcd\xdd\xe1\xce\xd0\xb1_Bm\xc3e\xd2=\xc2\xf7j\xbf\x83!\xf0\x97\x98\xb8n\xc3v?\xa4\x15\x80}\xd2\xd3\x1a\xcf \xf2\x82OO\x9a\xc7\xf3\xe2;\x91M\xf3\xf8\x84\xf8N\x84\xc7<\xd6\xe4\x05[ \x05H#(\x11XM\x84 \x05\x009\xa0\xd8\x1e\x1b\xd2\x83\x05\xb8j@w\x0d\xb08\xa0\x96\xa6\x87\xca7\xfcWXQ\x9405 |!\x9c\xe6\xb1I\xdbJOSl\xa8!\xa55\xb1\xa2\x86Dp\xcdcE\x0d)\x1d\x8855|J\xc45#\xed\xd8\xb6\xbfn]*b\x90eI\xca\xe1\x94V\xa8\xa6h\x96\xa1\x96)\x9ae\x8e\x9a\xa2\x11\x9e\x9e\xc7z\xad\x89\xc0!@@\xd1\x08\xbb/b\xd6\x88\x19\xc6\xc4\xacachjb\xd6\xac\x90\x9a\xbc\xd7\xe9~\xa8\x8d'D\xba\xb9\x03\x91S\x9f`=q\xc7\x113\xfaA\x86>gN2\x80\x9dy\x17Oh\xc7\x91!\x9aX\xaf\xc8\xe4\xe7\xdf`\xe4\xcf\x94\x9d\x9f\xf8\xea\xef\x18k\"i\xc9@\xb0\xa6\xb1cl\x80\xd8\xfe\x92\x19\x08\x96\xa9\x94zF+H\xdd\x0c#\xbf\xce\x9c\xfcclw\xcdx\xa0\xbcb\xdf\xc5\xeclG\xdb\x8b\xf0 \xcc4\x00\xdb\xcd\xb3!O\xf8I\xd1\xd8=\xb2,\x02\xd4\x8f@b'\xd0\xac\x11\xba3\xe4\xf0\x06*\xa6g\x99\x06`\xb6)\x01\xe9\xa1\xc0\xf7\xdf\xe0\xc3)ac;\xc4w\xf7J\x197\xf1A\x91\xf0:cJ5\x03\xe2[\xbf\xa2/\xf5gC?T\x9e\x8d\x98\xdeU\xb3\x1dbh6\xdcS\xb1\xbdtD\xf5\xe3\xb9\xb0\xb1\xb5.N\x066\xc7d\xc3(\x11X\xf8 \xe6\x1c\x86\xbb\x93\xb6t<\xce\xaf\xb1%\x1a\xa5\xdb\xc0\xc4\xce\x92k\x03\x8bq(\xd1\x06\x99\xa0\xba!\xf9\x84\xe0\xa0\x00\x80\xec\x8d\x15z\x00\x01\xc1\xf8\x88\xa0\xa8\x00\xc2\xbb\xb9XP\xc9\xea\x1e\xe0\xce\"\x0e>B\xd8n\x99\x81\xd7\xee\x03r\xd2\xa3\xb8\x07\xe7\xed],\xd0dQ\xac\xd3\x18\xe3\xa1\xed\x18\xdb\x06\xa6\xed\x99\x81`\xca! *d\xe3)6\x1bdQ\n\xc3\xc6rSVx_\x93\xa3\xb6\xb5\xb8,\x99\xe4\xdb\x84\xb0$\x0e\xec\x91\x05R\\\x9f\xbf\x87\x15.\x0d\xd4\xde\x0b\xefaA\x0d\xc7\xee\x93\xac\xea4t\x9f\xa4W\xd7E@F\xc6HJ\xe2\xfa\xc9\xa5\x9a%\xac\x9f\\\xafe\x89zU\xe5\xd9/\xb0IL_\xc9\xd9z6\xb6\xc1\x8f\xb0\xdc\xbb\x93\xf8q\xc0\x97\xeb\xe8\xb2\x80\xaa\x9a\x96\xe1\x02\xea\x7f\x88]\x06\xb3\xc4\xcf\xd4\xd6~\x84e\xa3,\x89\xf9\x1d\xe5F\xf5gx\x0fw\x8c-\x00k\xbe\x99\xb1\x05\x10\xa2\xa5nz0\xfb\xcf\xd4U\x0f\x96_v\xb4\xf9\x9f\xa0\xb7\xb6\xff\xe3E\xd81\xcf\x0f\xd0>4\x04_\xc0d\xfb>\\\x8c\xdc'\xdb\xb4\x1f\x0d\xb9\xe3U\xf3K\x12\xea\x08\x85\x90w\x13&1\xbb& \x1e\x1f\xba\xdc@\xf0~\xefj\xd1\x07\x8b*\xb9\x96\x960?\xcau\x0d\x0c\x10M\xe9\x00\xfb\x0f\xf0\xb6\xec\xf6\xd4\x93\xca\xf8\xa67W\x80\x7f\xc0s\xde\xed%\\\xc6y\x7f\x86\x97,7\x10L\x13wu\xb4>\xde\xb3\\\x030\xfe\xed\xc2\xa8\xb0\x1c\x93\xc3\x98\xf0\xa9\xcf=\xed:\x809\xc6\xae \xd6\xc7\x04<7\x10LZs\xe3\xca\x89M]y\xe1?\x88\xf9\xe1\xae\x16s\xb0\xd8\x91k\x00V\xd7vM\xc0<\x16as\x03\xc1\x879\xd7\x9e\x85da\x86N\x02\xeen\x98d\xe6& -\x1ern\xde\xc5\xc2\xdaJ.\xdf\xa7\x12\xa0w1\x95\xca\xcbOWY\x80*6\xe5]l\x1e\xcd\xcdC\x18X\xfc\xda\xd5\x11\xf2X\\\xcf5\x00\xbb\xedC\xb0\xed\xc7\x98\xc1\xee\x86\x9e\x8e\xa9\xc5\xef\xe5\x00\xc8\x84\xd4\xe2Ce\xc0:\xa6\x16\xd3sY\x00\x07\xd5\xe2{(c\x8a}\x88\xf1SBt\xb6\xff\x07\xf8\xa8\xed\xaad\x0b\x9fa\x0c\xc95\x00k\xf4\xbb\x86\xc5c\xcd-7\x10L\x04\x9b.\x1cw\xe3\xc2\xb9\x86\xd0\x95\x02f\xa9Wv\xda|\x1f\xdb\x8c\x15\xb8r'KOh\\\xbd\xb3\xc5\x8a\xc5n,\xa4\x81b|\x18\x9eW\xe1\x96\xfa\xd8+\x98\x9c\xeaX91\x9aw?\xc8\x19\xd2%\x8a\xa7\xa4\xc8a\x8ak\xb77\x8e\xf1[MX\x9b\x94E\xd0\xad1\x96awU\x08\x14^\xe4\\}\xc7\xeb*\xbe\x0fm\x15v\x8d\xc1\xfbs, \xe6\x85-\x9cn\x93v\xbf\xc4\x95$\xa4\x187mSa\x10x\x7fb\x99=O\x0c\xa9\xc1\xe7)/?\x02e\x01jRC\x16\\9\x19~F6Z\x03\xb0\xd8\x92k\x0f\xaa_`\x82\xbbkD\x1d\xc2?\x8c\xa8\x83U\xb7\xdc\xbc<\x84\xeb\xecj\xdd\xe83L\xbbr\x03\xc1\xf2w\xae\x9d\xbb0M\xca\x8d\x0b\x17\x96ps-\x0b\x90\xd5\xdeUy\n\x08\xe1V\xdf\xb1.\x97\xef\x1ba\xfd\x11\x96\x9d\xc6N8\x80;\xc8G\xb8\xb9\xb1\x934\\\xab\x8c\x9dD(\xce\xd2c\x01\xaf\xd0\xd8I\xc2H\xe8\xbe\xf0\x9a\x06\xc6\xc2\xb1\x93\xd4\\\xc6\x08\x88o\x0b:\x17\x80\xfa\xb8\xc6\xb1\x16\xa7,\xed%Vz\"\x00\xe0`\x8f\xe5\x86\xb1\x93\x18O\x0clR\x11\xb0\xea\x1d\x03\xbd\xd2-\x97Q7\x0d5\x85*\xa6\xbd\xe62\xca\xc0g-\xa4-\"\xc4\xb6!`H\xd3\"\xaf\x03\x97\xca\x18\xaaH\xfc\xa1/+\xcd\xfa)f\xe1c\xc53\x9e\xe2\x83 \x002\x8a\xef)>\x08\x97A$\xc4\xe4l\x0c\x9f\xf1\xf0\x8a$f\xb8\xeb\"\x87\x19\xee\xa1HaFFe\xea`]H\xb6&%\xaf\xa7\x98\xe3^V\x9e\x9c\xf8\xa6m\x0c\xdfI\xea\x991\xe7j\xb9\x1e`qx\xcc\xb9\xd2W\xb1\n1\xe6A\xe0\xc3\xbd\x02&w\x97y\xa2\xda{\x93\x1c\n\x0d\xfa\x11\xad\x93\xd5\xd5\xc8j\xca\x97\x13\x9bb\xb9T\xc3\xd5\x13\x17u\xd5\xb7y\xec$\x8e\xf2+\xff+,B\xebR\x85\xe5\x07#3}\x04\x04\x13\xe5\xcbZ\x0c\xc7\xc2\xf6X\x030\xee\x8e\xb5\xc4JQ\xdf\xe4\x8e\xb4dz\x1c\x9b\x9c\x8b\x96\x0c\x89\x97\x8dx\x86\x95\xf1\xb1\x81\x10:[\x1b\xef=6o\x17\x92sg\xd8\x16!R\x86ma\xc5z\\\xba\x01\xb6\x90\x8b\xd2-\xb0\x15j\xeeKj\xa0\xbc\x8eZ].\x0e\x17\xd6\x00\xc6w\xfc\xc1\x1dG\xb2\x82G\x18\xf1\xafh\xbfV\xcc\xfd\xf65\x00\xf3\x9d}\xee\xa9\xf3\xf0\x18+\x00W\xb8\x07Q\xbd\x0f\xf1\xe8\xf65\xe4\x1e\xde\x17 \x81C\x89qj\x9f\xfb*[\xcc\xdb\x18\x97\xafht\xc3\xf3\xd9\xd7\x00<\x9f+\x063\xb0\xa0\xb3o \x98\x94\xec\xdb;\xdfO\xac\xa7g?\xe1N6\xb4\x82\xae\x18D\xc2\x87`\xdf \x12\xd6A\x0e\x94'\xd4C\xcc\x04\x0f\xd4\xce<\xfb\x05\x16\xc0\x0e\x94\x13\x14\xd1\x9c\x0e<-\xfe\xe0k\xe67\xf4za\x9b\xc2\x81\x06\xe0\xfd?\xd0\x0f\xb5\x90\xb7o\x0f\xb4\x8eL\x9e\xbb}Cf#\xc06\x90\x03\xf9\x15\xab\x00\x07:\xbd$y\xcb\xf7@\xdfA\x927|\x0f\xd4\xf3d\xe4!\xdd\x03\xfd\xe2\x0bf\x05\x07:\x99\xe0Gx\xaf\xde0\xe8\x80\x95\xef\x03\x03\xc1,\xef\xa0\x88\x0d\xc1l\xea 2\xd6A\xb2\x91:<\x9d\xbc\xdc{\xa0}>\xc8\x83\xbdo\x18L\xc2\xc4\xea\xc0`\x12&\x8a\x07\xc6;\xee#l\x1f<0\n\xd7G\xf8\xb6\xed\xc0\x88\xcc\xa4\xa7q\x0dK>\xd8\xaf%\x00W\x8d\x8d\x0e\x93\xdfC\x03\xc1\xb8yu\x11\x84\x12\x8c\xe6\x87\x0e\xd8\xaf\xf0\xfe\\\xd5$\x0b/\xda\xa1\x06`\xbc\xbc\n\x1d`\xd9\xe6\x10\xda\xc7\xa4\xfd\x90\xcbdBX5\xbb\xaaO\n\x96\xdf\x0f5\x00\x8f\xe7\xea*\xf4\x8b\xef\xa2\x0f}\xe8\x18+\xadW\x0d\xe2a?\x9fC\x03\xc1D\xff\xaaA\x14L \x0f\x0d\xa2`JxU\xd9\x0b\xb1\x08t\xa8\x0c\x86\xa4<\xe8;\x9f\xe1\x83z\xa8\xf4 l\x00\xb8fBQ0\xc2\xdf1\x10LT\xae\x99\x1b\\\x8c\x1ew\x0c\x04\x93\x90k0\x0d\xbc\x8cw\xe03F\x82k\xea\xe5vL\"\xee\xa8\xef\x98\xa6\xdc\xe1\\?\xe2\x89\x19\xc65\x9eDW|/\x1b\xd6?\xa3vM]\x9fb\xc9\xf0\x8e\xfa\x8eq\xe5\x9a\n\x9b\xc6]\xdd\xd1\xc8E\xa6\xa3,\xfe\xa4\x030\xf8\xff=\xee\xe0\x8e?0!c\xf8l^\xd3ar\xf8\xb6\xed\x8e\xc1;|v\xae\x19\xbc\xc3D\xfa\x8e\xc1;|p\xef\xec\xdf\x92k\x85 \xd7\x9d\xfd\x10\x00\xef\xb6\xcc\xf7\xbb\xf2\xaf\xbb]\xd6\xcfC\xe9g\xda\xe6]\x96uY\xd8a\x7fd\n\xb5\xf2\x94\xb34K|7k\xbdj\xbe\x8e\x9d\x84%\xec\x0c\x0b\xdb'\xe7^\xe9T\xbb\x8a\xe4\xf7\xf9\xeftf\xf2\x90\xa7\xae\x13\xf3K^Q\x93\xcf\xf0\x838J\xb2\x94\x9d\xa9\xf6[\xeeTw\x11v\x99\xdfeN\x97\xe5\xec\x0c\xcb\xaa\xdd\x88\x9fh\x84\xcf\xc4Qz\xc99x\xb5\x02\xf5\xfb\xac\xfd\xf2,;sF\x14H\x13w\xc6\x1d:\xc9R\xe4\xf1\xc5\xac\x9dup_\xe2\xd7\x8f\x12\xd6\xce\x8e\x1e}\x95e\xec\xbb,}\xd5VF\xb7<\x07-\xb7Cfo\xbe\xc3\x12\x9e\xe5I\xc8\x8e\xcc\xbdZ\xdb\xc8\xcb\xf3\xb2\x91\xd0\x14v\xd8\x19\x96\xb4\xa36\xb4\x98\x06\xbe\xcb\xdb9;\xca\xe6\xc4\xeat:]v\xe4\x08\x9f\x89\x9d$\xe5\xc9\xcc\xd8 |\xcf\xc9\xf8\x9a\x1f\xee\xb5\x9d\x0e{\xe9%\xd6\x96+!\x16\n\xea\xf0\x99\xc0\x0f\xf7\x96\xa20\xe3a\xc6\xce\x88e<2\xdb\xb1\x8f\xe7\xb4\x1a\x8bhGV\x17K\xc0^\x13\x7f\x9fa\xf3l\x81eG\x8f\x92\x8aw\xc9\x173\xebo\xd5\x97\x93\xeb\xec\xb33lV\xad\xb4\xe8\xf3\xc4<;\xd2\xb4\xa0\xa2\xcc\x91v\xc8\xbe\xc7^\x11\x7f\x86\xec\xbbl\xeed\xe7\xd5\x0e\x19\x81XX\xebd:j.t\xfe\xfe\x83\xf4\xe8\xf1A\x97\xb5X\xab3\x93E\xf2\x0eg\xc9Iy\xfb\x85\xe0\xf0F\xef\x16w\xb3\x19\x8f\xf7\xfd\x90o&Q\xcc\x93\xec\xb0\x9duY\xeb\xe6M\x9e^\x8a\xbc<\xe0\xad.\xc1\xd6 \xe7\x0b\xec\xc8l1\x82N\x97\xc9V\x9c<\xc8\xca\xd3\xac\x99%\xc5\x147\x1a\xc5Q\xc8\xc3,]`\x8en\x89\"\xfb~\xe2\xc4K\xa5\xa2y}\xd14s2\xbe\x19\xe4\x03?L\x17jXA\x1as\xb7\x0e\xc6Tw\xdb<\x90\xb9&\xd2\x05\x96\xd0^\xf4/-J\xf9\xd6Bw\xedu\x9d<\x1b>\xc7\x08\xa2\xe7i;r\xd2\x13Mm;r\x8f\xd2\x05\x96\xd6\xcf+\xe1^\xeer\xd1\xb5[\xbf\xd4\xfaWZ\x84\xc0>P\xf2\xf5n\xcd)\xbcK\xe9l\xdc\x0e\xdb'\xe7\xe7;\x16\xc9\x14@'0\xc87\xa0\x93\x18$\x88W_\x82NaP\xaeA'H\xadT58\x7f\xe2e\x0c\nt_'\xc9\x08]\xdd\xe0\xc9\x13\x9d\xce\xab\xdf20}JX\xbf\x9e\x1c\x08\x02\xc6g\x8a\xc3\xc8^c\x9c\xd96Um\xce\x02\xe3u+j\xe98\xa6\x1d\x0b\x92Mz-\x88t\x95\xd4j\x0e\xfeGw)\xbb \xf3 `G\xce0N\xe59\xc9P$\xcfc~\xc8xG\x93\xa18\x89\xb2(;\x8c\xf9\xcc\xd0I7\xf6CM\x90f\\'\x08\x04Q\x0bA\xd6\xc9\xae\x877\x04S\xb9\x1e\xde@|N\x0d\xb3L\x8b\x04-,-\x02\xfbF\x90J?\xdd\xdew\x06\x03\x9e\xcc\x0b\x8e7\xe3\xa7\x1b\x8b\xdb'\xe4\x9f)O\xc6\xb7\x1b(\x82\x103y\x91\x942\xc5#KtY.\xddJ\xa4\xec\xaa\x93\xe6\xc7\x03&\"\x99\xb0\x90\x00\n\x17^l\xb1\x97{fz\xaek\xcd\x03\xcc\x9f9o0\xefp\xde\xa4=/2+vD\x00\x01 \"\x80$)Y\xd5}\xb0\x96\xad$\"\x10\xd7\x1d;\xf6}'a\x00\x9b*\xfaf\xe7\xbe\x92\x1bl\xbf\x0d\xf1\xed\xd6\x8e\x12\xc6}-\x8cW[\xd1\xde\x07]=\x1d\x13W\x0d\xd8;#\xc5\xe1U^\x10z\x91R\x1c_aP\xfc\xeb\xbb\x9c6\xa2&\xday_\xf6\xa6\x0b!\xdf\x16\xc7\xce\x1cz\xec\xcb\x85\xcdc\xa7\x851\xd5\xf8\xec\xa3\xcc\x94\xf7t\xc8\xb0/\x9fq\x03\xf4\xc5L\xd94s\xb7\x89\x85\xf1o E\xe3\xdf\x12\xfe\xc6\xbfk\xdc\xce\xfe\xac\xd0\xfe\xddLI,e\xffvUw\x8f\x91C\x1d\x82\x83)\x84\x13\xbcXn\x86\x7f\x95\xb8\x17\x87\xed\x85\xf9K\x1f\x89\x15F\xfe\x18\xcee=\xbd\xce=\xfb\xb9MP\x0c\xed6\x93\xc4_\xbf?=#\xe1\x9f\xa3\xe4IY,\x92,\xfc\x99\x18\x88\x8a\x9cR\xd1JZ\x9e\x96\x8c\x1e\xa8Hy\x05!\xe2+ \x91\xd2D\x88\xe4\x9f\x86\xd8\x16\xbf\xe8\x84#\x0d\xaan.\x95-\xee\xceP\x7f7k\x87.\x83}\x7f\xed6\xccvq\xab\x8c'\xdc\x01\xc2+>t\xdf{\x11\xe6\x85\xd3\x06\xfe\xeav#q\x91]\x1d\x92\xbf\xdb\x8e7O\xb2\x03\x7f\xb60\xcc\x0d\xa4[\x93\x1d\x06\xbe\xee\x0e\x1d\xc7\xd8Q3\xa2\x14R\x8a\xe9\xe6\xb1\xba\x14u\x0e\xd3\x91\xa6\x94\xe2\xdf\x92Q\x01\x94\x0d\xb1\x14g\xd8J(\xcb>\xb6P\xbe\x84bn\xfe\xc1c\x7f\xf6}D\xf7|\xd2\x04\x00m\xfdk\x0d\x03\x11#\x03\x92\x96\xf9\xc2\x8e\xc9\x05\xf8\x14\x81\xf3\x1b\xbd\xda\xd6_\xaeQ\x056\xf3\xe6aT\x90l\x00|@}\x88\x18FE\x91-Q\xd6\xbdv\x1cG\xc1v8.X\x8b\xa2H-\xfc\x14!\xd7\xf2\xd3\xf0\xcf\xe4J\xbc\xa1\x84\xc2\n\xc3/;\xfd\xd0>\xe2?\xc8\x7f\xadt\xe5*\x99\xbfJV@o\x8d\x8a\xad\xf2\"\x12\x9f\x15\x0b&2\x7f\x92e\xfe\x95\x9d\xc1c\x18\xc1>d\xb0\x01#\x98\xc0\xa6\xe3\".\x18=\x82\x10\xbe\x82\xec\x11\x84\xeb\xeb\x0e$\xd3\x90V8\x96[\x9b\x86\xc7\xdd\xcd\xa4}\xfaws\xd9\x97\x155\xe3\xd3\xcb=j1\x8b\xd3\xe2\x98\x92\x8b3\xbf\xb0\x13\x87r\x93mV3\xd1^\xff\xac\xe0\xf7\xbf\xff[\xf2\x8c\x9a\x9a\xbdK\xa1\x82\xdc\x06W\x1f\x0f\xe3\xebVe\x91\xef\x84\x8d\\\x99\x81\xbd3\xd6y \x03+\x13%\xf5\x86\xa1Z\xa7GB\xa0\xd5\xe4E\x1d\xde\xd6\xc8\xd7\xe6m\xbev\x18\xf1\xb2\x12\x8f\xe3\xf6*#\xccK[\xe1\x9fB\x89\x7f\xe2\n\xff\x14\x1c\xff\x14\x12\xfe\xc9\x18\xfe\xc9\xe0+(\x1eAF\xf1O<\xcd\xba\xf8'\xd3\xe0\x9f\x04Ug\xb7\xc6?\x127E\xf1\x8f\xdfB/1\xc59]\xd1\x8e\xe9\x88\xaf\x84\xd7?)+E>gV\xa9\x8b\x07\x99\x0e\xa2\xa3MH\xaa\xa2\xfb*N\x88\x15u\x98\xa4Z\xa9\xf1P\xaf\xd4\xd8T)5X\xd1H%\xcdcEz\xa5\xc6\xd6\xef\xab\xd4\x10\xbfd\x91\x7f\xb3\xa1\xa7~\x14\x9d\xfa\xb3\xf7\xf9\xa4&b\x9as\xf9\xb6(\xd2'\xa8\x88\x8b\xd4\x15\xde\x12Lc\xf5u\x12\\Mj\xfa\xbcY\xe7\x90a#\xad\xfa\x92\x97?M\xe2\xc2\x0f\xd1\xdfL\xa3\xbc\x94:;\x08B\xf4V\xc8\xd55_\xa7\x84%\xff\xa9\xfa\xd6(\xe9\x12Q\xf1E\x18\xbf\x9f@(j}\xe6\x87\xc3\xb7c\xbb\xab\x9fKxI\x07\x90C\xbc\xbe\xec\xd8\xa6p\x8cUF\x14l\x91\xa8XQ'\xf1\xd1A\xb4\xff.%\xa8\xf5B\xc0\xedr-\xb1\xb8\x18*ex\xb7\x0e7\x0cI\xc9\xec\x8d_,\xba\xe5LJbU@TA\xa6\xa5\xb0)\x0b\xe7`\xaf\x15\x95\x1e\xb0:\x03\x9cH\xe0\xe9ul+O}J\xf5\xd0\xdb\xc4\x05\xebU\x02\xd5$\xda\xcc4\x9d'SI-\xfd\xb4\xa6-z\x94@\xda\x8e\x83\xf0\xbc\x03e\xe2yO\xae&\x12c\"\x9ekW\xdf\xdcb\\\xcd\"\xc6\xeb\xaf=\xc8\\\xc7\xaa\xf1\x81Z_|\x91\x91\xb9\x10\x13\xecc[0\xb9\xd9\xf8A\xcc!W\x16_\xab\xc6\x17\x99XI\xba\x9b\xf2\x00\xa3jc\xe90\xd5\x8c-\xf0=\x9bUR\xaaa\x02\x83\n\xf7LZ\n\x0c\xf9\xd1q\xd3\xd0\xbf\xf3\xa5\x0b\n\xfe\x94\x98\xd6\x12pX\x13\x98\x99\xc5\x01\xb8\xe4Q\x8f\xc8\x00\xfd\x86,s\xa5%)\x16I\xd0\xdbV\x8a\xee1=\xa2\x15q\x9e\xe9=\xc3\xd8t\x17r\xba\xdd=\x12\x99(J.\x8e\xb2\xab\xe7\xc5\xeb\xb2\x98\xb4\x8d9\xe5\xe7Z!<\xd0\xbdo\xbfko\xe3\xb0C\xcb\x8eY\xfey\x194uo\xa3Pu\xe7\xd0\xcb\xc8\x0e\xc5\x9d\x13\xf6\xdf9\xe1\xe7}\xe7d5\xf1\xa1\xbbu\xa4*\xdf\xd3\x85\xeb\xd6\x0b\x07\xdfNX'\x9e\x87g\n\xa8/\xab\xfb\xabb \xba\x95\x98\xb1\xf8<\xee\x96D\xec\x0ee\x06\x84GW\xa9b\x9c3\xac\x12\xe6\x07\x97dV\x16\x8a\n\xf3\x9e+4\xc5\xf2$~\xba\xf0\xe33\xc5\xf7\x01\x82\x8d\xf5\xd2\xcf\xde\x07\xc9E\xac\x92?.X\x95e\x12\x90\xe8\xe0\xd2_\xa6\x11QU;g\xd5:\xb4\xa1\xaa\xee\x12\xb85q\xc1\xe4\x01\x01\xc9gY\x98\xd2\xad\xb7*]f\xf7\xb3\xb3\xd6g|\xe9\xf8'\xe4\x02\x12\xefu\x16\x90\x8c\x04/\xfd\xb4y\xce\xe9ZG\xb4\xda\x99\xf7\x9e\x08\xe1w\x98\xe5E\x9bu\xa3\x80v\x05{p\x86]\xa8\x90\xd6)\xec\x81\x95\xe0)fw\xd3U\xcd\xef\xa3\n\xdar\x81\xc9f\xdb\xb6?H\xa2\\\x19n2\xbc\xf5(\xeb\x1b\xce\xf0B\xba\x97\xcc\nRl\xe4EF\xfc%\xbf\x08\xe9$\x98\x91k\xe4\x85q@._\xcfm+\\\xfag\xe4\x1e[\x88N\xa1_\x06a\xa2+<\x0f\x03B\x0bu,\xf0 \xdb\xd6\xe7qZ\x16*m\x03\x9f\xcb\x0c\xf6\xeb\x0b\xae\x85DOt7\x1d\x93f[\xf3\x90b\xecK\xf3;\xc1\x0e\xa1\x82V\x98t\n\xb5\xa3)\\lL;(.'\xd0\x8f*/\xae\"b\xb2^\x07\xf4\x1a\x880\x98\x07\x1d\x9d\xb6b\xf72\x026F\xeb\xdf\xfe\xf5\x8f\x96\x90}\xdf\x14\x07\x81\x0e:NN\xf0p\xea:/]\x88(\xc0\xdf|\x85\x1a\xbdfI\xba\xc1O\xb8v\xba\xf6\x17\xfc^p,\xe7#L7 iFf~\xa1\xdb\x0b\xca\x95\x0b\xbcQ\xd5\xa4\x97\x82\xfc\xb7\xd8\x0d\xd3\xf8nw\x88dj\xb8w\x9c\x12\xe1\xec\x1a\xa9\xb0\x06+\xab\xabta\x1a\xf6<6\xf2\xfeA\x98\xa7~1[<\x8f\xc3\"\xf4\xa3\xef9\xcb\xaa`J\xc4\xc3n\xff (\xf8\x12\xf1H\x13\x9c\xa0\x9f\x94\x05\x1b`\xc1\xbaz\x01\xb4\xcd\xc8\x9c\xde\x04B}E\xcehs\x13\x06\x8a\xcf\xe7\xb0\x0f\x01L`\xae\xffhU*\x15\x18\xa5\x8azu\x83\xfd\x86z\xef\x9d\n\x1f(\xa5\x1dZC<\x18p\x07\xc9 \xb24\x9d\xfd@\x05'yRf32\x81es\x04\x86\x83\xb2P5\xd3\xbbW5K>\x01_\xc1p\xcb\xfc\xf8\x04\xcan\x0dr\x99\xfaq\xf0\x8c\xa4\xc5b\x02#\x85t@\xf0\xdbJ\x01\x9c\x80\xda+a\xb8\x83$\xac\x02\xf8jA\xd8\x9c \xc2d\xe2WQ\x9f\x13&z.\xe4\\w:3Y\xfb\xa3!\x12j M\xd5\x15\x90\xd58B\x96L#\x06\xec\xdd\x19\xe8]\xe9 \xefz\x8c\xa7\x15\xe9\xa2\xad\xd2\x90\xbc\xc5\x14\xeb\x95\xb0\xaf\xad\x9e\x18g\xcc\x89\x9d\xee\xed\x05B\x98\xc8\x996\xedh\xd2L\x12\x03VJn\xf8\x17\x0b\x8dW-\xfa\xaf~\xb2\x19\xff\xd4\xd4\x81\\\xc9zS\x818X=f\xaf\xf2\x83\"i!\x04Y\xdbCQd2\x87Z\xd1nY\xbd\x8a\xd1\xc2\xcb\xd3(,l\xeb\xc7\xd8r\x86)\xd3\x15\xad\xc4\xf0\x186a\x9f\x1b\xb3\x11X\x87\x91\xe3\xfd\x94\x84\xb1m\x81\xe5\xc0:\x14`V\xe0\xf2\xcat\x10\xeaM\xa3\xb8\xaa\xa5\xa9\xf5\xc5\x06\x8d\x1d&/\xfa\xe5z\xd8\xb6\xa8\xa8\xf3\xe6=q\xdc4,\xb4#\xafF\x91\xb2\xe5#\xef\n\xf6 \xc5\xb7\x9f\x1b\xf13S\x918 /\xe8\x908!/\xe8\x908>/Pz\xbb\xcfT$N\xce\x0b:*\xcf\x88\xdb\xe9\xd6c\x9d *gf\xa0rf\x9f\x9e\xca1;e\xf6P9x\xa5\xbb=\xc2\x90U\xa1'L\xce\x18\xd3\xd3k\x88M\x9f\xd0\xcbI\xc1\xbe\xaa\xd5Hx\x06\x14gY\xee\xe3{?\x0b\xfd\xd3\x88\xa0\xc8c\x85\x0e\x85R;\xec#\xc8bn\xb3^(\xfa\xd3\x7f\x951O\xfc2\xcbH\xcc\xbf4\xd3j\xd5\xa4\xcfH\xf1\xa4(\xb2\xf0\xb4,\x88m\x05~\xe1o\x9c\xf3>\xfb\xe8\xac\xe6\xc2\xa9\xaf\x06K,\x8d\x05{\xd5\x8d\x82\x91pb\x83\xa9\x0e3\xa66\xc68AZ9\xd1\x97\x9f\xfb\xd1\x04|e\xf1\xb5f\x8f\xabE\x1f\xb4\xa3\x8c\xe3\xc0\xddd_R.\x97\x04\xac\x85\x8e\xe9/\xef\x04\xcd\xdc:\xdc\x00\xfa\xafh\x90\x08\xb4\xbd7T\x9cE8\x8c\xb3\xa8\\\x8b\x9f\x85\xc1\xcb\xa4\x8c\xdb\xc9\xff\xe0\xa32\x19\xdcB^\x0d'\xa4 \xbcH\xf9\xd3\x96\xebcZ\x08%>#\xc7\xcb,\xb2\xfa/^\x15Y\xd7Z\x8b\x1f\xc2(zKf$<\xc7\xcb2\x1f\xb0&\xbd\xa7|\xc8\xa2\xc4\xb2sJ\xdf\xc9^\x15\x1f$\x955{\xe3+\xf5\xdaS\xba\xaf\x1eqk#\xd0\xb5\xab\xf9\xceD\xc4\xd1\x15@/\x19o\x1e\xc6\x81D\xfc\x0d\xa4\xfc\niwyl\xc5F\xdf\xda6LF{h\x8c\x11Vdl\x0b\xb0b\x15`\xe9\x1b\xb3CVO`\xc9\xdc\xaa<>\xa2\x96:zu\xfa7\xb1[\xf3\xc5o>|\x80\xac\xc7\xb0\x11$\xac\xd9n\xa2\xf7Cf\x92\xda_\x0fqj\xa1P\xb7Zz\xe6\x0e\xd4\x08\xb7\xa7Ha\xb31\xf4`\xdf\xa9\xf8\xc4\x8c\xd3\xee\xfc\x98\x0f\xdc7\xcd\xe9\x1e `9\x98\xcf\xc9\xac\x08\xcf\x89\xf8\xd2\x88E\xd0\xfb\xaa}\x92{\xd5\x1d\xb2k\x94|\x92MgW{\x82\x06\x1e5\xb3\x04\x87\xc7\x14\xf4\xf2\xf0g\x0d\n\xe4c\xceo*\x14\x91\xd5|\xc2\x13L\x0d\xd8\xae\xbe\x93\xc8?%\x91\xb1\x9bE\xb1\x8c\xbeA%\xf3\x8d;aa\xd1\x8c\xbd\xd4\xea\x03\x04\xf0&y\xad\xeb0fT 3\xb7k\xda\xa2\x98\x00\xa6o\xe1\x13&p\xeb3\xa0\xe6g[\x8693:C\\!W\xd7\x03\xa7\xdb\xa8\xa7\xb3G\xf6\x8a\x841N\x8e\x905\xf5\x00\x1374\xbe\x0b\x88\xa3\xb4LY\x90`\x83\x8eP\xb7A\xd6S^\x0b\xde\xbd}1\xb1\x0c]7Dg\xa1\x9d\xe1\x8c\xb4\xb5\x17\xdb\xb5d\x8b\xd3\x0c\xd2y5|\xd8\xb4s\xd2Wk\xd89\xf9\xab\xdd\xa9}\xe0\xd5c\x89\x03z\x7f\x0d\xf1\x98\xce\x1a\xda\x06\xd4~\x1bC\xea\xf1\xdb\x95\xc4\xe5\x12\xcd\x11ns\x8e\xe9\xd3\xe2\xe8z\xaf\xf9\xfa\xec\x13\x13\xcfkZ\x8e\xc6\x14V@\x050`\xbf\x06\xa2\x03\xa8\xe2?\x92`B/\xf3\xbd=Hl$\xa6\xfa\xa9\x1c\x86\x1a\xfa\xeb \x9cc\xacH\xb1\x87\x89\xfaq`\xa2\x9fm\x88\x96\xb8}\x93\xe5\xa6\xb5\x05\xb9T\xf1s\xf2\xc3G\xccW\xa2\xcf&\x0e\x86\x83\x83\xb9\x91.\x0c\x9a\x16D\xeb\xf0Q[Ctj\xf4\x88[\xeb\x05\xee\x13\xbb\xce\xf1\xed\xe7&v\x8dtb\xd7H'v\x8dtb\xd7H'v\x8dtb\xd7\x88\x89]\xebQEL\xc0\xaa\x12\xabF\x9f^\xac:\xbb\x8dXU\x12\xac(\xa4\xa7]\xad\xadVy\xdc\x92Z\xdeJy|+\x11\xcf\x9dr?}\xbcM1\xc4)F\x19\xe9\xa3\xa6Q4\xb7\xa5\xeb\xb5\x10\xb2\xa5\x98\x81I\xdbMk\x1f\xa1w\xee1+\xa4p~\xe5\xd8\xed:\x15\xd2\x17\xb0>GI8\x962\x0fE4\xe5a\xf3\xe8\xe3\x9d\xb9\x8b\xdb\x0fYX\x90\xd7qt\xd5\xc0\xbc\xedG\xa7\xabp%\xb0\x1f\x0c\x08\x83\xa1\xb7W\xcc\xc0\x80\x96\xe9\xee\xaa\xd3g\x02\xd9\x85\x1f\x07\x11y\xbd\xea\x88[\xa0;\x14\xd0(\x10\xdf\xfb)O\xe2{\xa1W\x90\xbc\xb0\x0b\x16\xc0^\xb6\x1d\xe0yf`2\xc8\xa6\x00VY\xbe\xf6\xe17m\xaf\xbc\x91vlX\xc1\"9;\x8b\xc8\xf3\xfc \x08\x8b\xaf\x93K0$\x99\x91\x1f\x19\xbf\xb2\xb1\x0f[y\xe9\xdb~\xb9W(F5\x815\x8c'\xc0\xfe2~\xa7\xb6\xc0\x84\x1e\x98\xc7\xa46\x9d\x08W\xf2#\x8fE\xe1|!\x9e\x0e\x82\xd6W\xe5\xa7A\xa3p\xa4\xc3\xea\x14t'w{f\x1bV\xb2\xa9\x80\x15\xf8o\xfa\x08\x05u\xe3\x16\xaa/\xf1\xc1*S\x1d\xf6[\xdd\x02\x02V\xb1\x82\x001\x85\x16\x9e\xe0\xb6\x04\xf5\xdf_~\xa9\x9e\xaa-Ur\\X\x93\x1a\xab\\N\x18\x11\xd8\xf8\xb3\xd2\xeb\x0f@\x0b2d\xae\x8e\xf1o\xbc\xd4\xcf\xc2\xe0]\x1a\xf8\x85.\x08\xc2M\xd7X\xa2\x11\xf8*\xcbo\xb4\xeb\xac\xda\xa5;\x9a\xb2V\x10\x05+\x1e\x86a\xeaxXA%\x0f\x15ie\x88\xb6\"?\x99P\x9f\x0f\x101A\xa5\x9f\x1fx?\x86\x98O\xce\xfa\xba,\n\xb3c#p\xba+\xb3\xad#rY<\xc9\x88\xd2\x15M~JV}\x11\x9e-\xa2\xf0lQ0\xb0\x9a\xf4T\xe1\xee\xab\x97\x9ef\\zz\x13W\xe0\x81\xd2\xd3\x94U\xcc\x0c\xa3@\xf2\xad\x8f\"\x1f\xaa\xf0\xd5SK\x91M\xcer!9\xee\xd9'\xc7\x85s\x13\xa3a-vk\xab\xe7*o^`\x19XS\xbfo\x99fC\xe6%b\x11\xa8\x82R\xf4\xcf\xe9\xc6c\xab|\x13\xf8\x94\xdfqH\x9bX\xb8Rz\xfe\xb4\x15\x01\x15,\x17\xce\xf1_\n\xa2\x06 \x83y8\xbd|\x1e\xacd\x17\x0b\x9ck 3\x12\xe0\xed&\"b\xf6~\xc5\x08\xa2\xfa\xe0\xf5\x7f\xd1q\xae\xe8\x91\xc7\x00\xdb\xbb\xbb\xdc\xbc7~\x9e_$Y\xb0\xf2\xe6\xfd\x11\x9fO\xb1w7\xdb\x0d\xbf,\x12z\xddG\xa4\xa0\xbb\x12\x93\x8b\x8d\x94\xcfu\xc0\xd7\xb1\x08\"8\xf8\x0b\x0ea+|q\xf3\xdd_\xe8\xfdkz\xc2z\x88\xa7\x07\xdd\xe7C\xf6\x85>\x84^\x9e\x83,\xe4\xa1\nf\xda[\xd5\xe0\"\xc8\x8a\x0dF\xf4\xda\x12\x11\xb6\xe4\x94\xf8\x19\xc9\xf8\xbdj\x82\xf7\xdf\xe9\xc6\xc3\xe1\xdd\xea\xca\xbb\xf1u\x87\xd7B\xf0\xd9]u7\xba\xe6\xee\xf6\x8ac\x16\x89\x16.\xcf\xe7\x86\"\x87_m\xab\"\x9c\xbb@6w\x81h\x86#\x99\x01\x08\xc6\xe8\x7fl\xda\xa9a\x08\x81,\xfb\xeb\xd4\x11\xab\x12\x0c\xf6\xfe\xed\xd1\xd1\x1b\xccLK\xe2\x82\xcbR'P\xc6y\x99\xa6IV\x90\x80IR\x08\xa5\x97\xac\xffh\xc1:\xa4\xb0N\x7f\xddN\xfc[\x0f\xaf\x16\x017W8\xed\xb3e\x919\xf6.{\xd1\x002\xb9)c4r\xc6\xab7-\x98\xf4\x1b\xcf\xb4\xab\xccLH_+D\x0b\xb5\x1e\xd5$3c33\xf1e\x95\x82\x92\xaf\x1d\xcf\xe9\xc3\xc4e\xfd\x02$w\xb3\x00\x9d\x99\xa8\xb2\x92\x1b\xb3\xbe\xd1;'O}J\xe3\xd6\xab\xa7\x96\x1e*s\x9d\xd1\x01\x9d\x99\x00\xca\xb4\x9cd\xc8r2Q\xbby9\xd9\xc5=h9\xd9\xeau\x86l\x17\xd5\xec\x15\x06\xb7\xf54\xe5\x15\x87\x9e\x94\xbf\xe2\x11\xa4E\xefT3\x96g\xbe\x17r\xe2\x95\xa7*\x0f\xdbp\xdbK\xd0\x90\xd5\xd0\xa0\x1fL\x15\xe9G\x0d0tM\xb4k\xa9r\xbc\xfa\xf4\x07q\x05LT-\xa7j\xe4\x03\x82\xc8\x19h;\xe5)T\xc7\xa9Q\x07\x8d\xcb\xebxn\xd2\xd5\xe17\x12\x08B\x87\xa0\xba\xbd\xfa\xf2ws\xf6MZY~\xfbp\x03\x85\x82\xde\xaaYGW\xa7\x06 \x96\xf7\x95R>k\xf1\x80$\xa1\xe7\xbc\x8d+u\xe5;pKo\xea\xa2\x11[p\xb8;t\xdb\xa1\xba\x9eT6(\xc2\x9b\xd6\xa3Z4\xa4*U\xef\xfe\x8d\xe2Yw\xe5J\xffhB\x83\xed-\xbd\xd4`\xab\xc3\xd3\x87UQ\xc7\xad\xd9\xaf\x8a\x1e\xe8d\x07\xdb[\x0fu\xd2\x83\xedme\x8ckV\xf4yX\xf2\xc9\xfb\xd9lHX\x8dHym\x9aSyR\x16\x8b\xe7\x05YJ\xb9\xc7\x9b\x15\xea\xec\x0c\x93ZR\xd0\xacR\xa7\xa26\xa6<%3\x1e\xb6\xd0\x9ba?\x98\x90\xeb\xeb\xab\xe7\x01\x89\x8b\xb0\xc0\xa06b\x08\x7f&W\xa8*\xc2\xbe;\x8db`mQ\xf5i\x12\xe7\xe5\x92\xe4?0\x01\xd1JB\xfb\xdea\x17\x8aa\x8b\x0eQX\xe0\xd8Ek\xd0\x9a\xe12_\xcf#\xfft\xd0\x00\x05\n\x97\xd2\xf2\xb1\xbc\x0f\xb0\x8f\xd1\xe0z-%\xea\x0f\xbf\x0f\xf3\x10\x85'k\x9bj*\x8d>\x14FN\xfd\xd9\xfb\xba\xb2:\x1c\x14\xa2QK\xd4^uP\xdd^\x0cCR\xcd\xc00(FO\xab\xd7\xde\xec\xc2\xa5\x98\xbbzT\xca5U\xf6\xa8A\x1f\xf0\xb9j9\xf4\xbb04z\x04\xd3n%\xf1Qv\x95\x94\x05:\x07\xeb+'\xbc2\xf3g\xee\xa9\x1cr\xbd\x99X{}M\x96\xe5\xd2\x8f\xa2\xe4\xe2(\xbbz^\xbc.\x0d\x96P,\x87e\xc1\xeb\x1d\xc4\xfei\xa4\"\xd5\xc4\x83\xf1\x1f\xbc\xb9A\x0b\x12\xad\x10\x0e#\xa8\xebb\x1ag}\xcd\x05\xd6\x1c\x18L\xf6\xbc\xaa\xdc\x1b\x1fv\xc9\xb6`H(\xd9\xb3\xaa\xea\x80!\\UZ\xce\x97\xa8\xc5\xd4\xd7<\xad\x06\xfb\xc6\xa8\x13=a\xdd\x0b\xad\x8e\xbe\xe2\x05\x86e\xaeQf\x8f\xc3\xd8\x01\xab. \xa5?\xd2\xc8%\xfb\x80\x07\x85;BZZ_\xfb\x90\xd5~Z\xa1\xca\x1e\x0f\xb0\xa7\xac\xfe\xdb\xdaM\xbc\xef\x8b\xf7\xb0\x07%\xa5m\x0c>\x7fO(Q\xe5\x859e\xbe\xf4\xb5^\xc3\x1e\x9c0\x16ArS7\xcd\xee\x0d\xec\xc1\xa9\x97G\xe1\x8cP\x9c\xb51rx\x82\xef\xc6\xf7F\xe5\xdf\x8dS\xad\x1a\xb4oZ\xcd\xcd\xc7\xe8\xacO\x05w'}\x0eP\xf5\xdd\xb8\x9f\xd5\x838T>~\x155\xd3\xcc\x1c\xac\xfdX# \x02\xc5l\xc3\x82,\xc1\x82u\x9e}\x8b\xd9\x93v\xae^\n\xf7\x96\x8f\xaa\x1b]2S\xc3\xca\xac\xa0\x13\x1c\xa6\x04\xd5\xf6\xc4#2W>F\xf5ZQv\x86\x1f\xba\x9a\x9er\x0c\xd9x?\xd1~J\x83\xf9h\xdb\xd9\"\xb9\xfe17\xb3F\xedR\xcce\x17\xcd\x9bu-\x1c\x98\x06J\x18\x0d\xa2\x14\x8b\x88\xa7A3\x193=6H1]r 9K\xb3\xf1\xb4\xdd\x02*\xe5\xf5\xaf\x1b\x1e\x10r=\xf4fI\x19\x17\xf6\xad\xceD\x0b\x1c#2\xa0cmg\"7\xcf\xb0\xee$\xc4\xb8zO\x14\xe7W\xa0\xa6\xaf\x96\x0d\xa8\xb3\x18<\xe2Y\x12\xc1,\x89N\xd8\x85\x03\x8d\xdd\x8aN\xd0IK7\x13\xeb\x15\xbap}\x8aq\xc8nO\xda\xe1<\x93}\xa3\x1c\xe3\xb8\x1a\x99\x94\x06\x99P\x82\x8c:%\x9f \xee7\x9fV]\xbd\xf4S/\xcc_\xfa)\xf3\x17R\xd8\x1f\xd2\xe7\xda\x0e\xa5\x8e\x07&o\xd2\xcd\xe7\xa2\xcf\x8fh\x1e\x1bc\x95@G\xcaj\x88ZB\x1fA\xc1O\xe0\x94\xd1\x80}\xd9\x84j\xb6g\x02\x06\xfe\x80>\x99\x7f\x81W\xe6\x04z\xe2T\xa4\xac\xd6\xa2F]?\x84\xc8\x82\xf8\xb5|\xc9\xbe\xc2\xf4%\xc6v\x98\xdb\x94\xec\x94h\xae\xdf\xcc\x04\xd4\xe7\xa3#\x7f!\xa4H\xf2\x97-QV\xff\xbaK\xb2t\x03\x07%jsNo\x02\xe7}\x8b)\xb8\xb7 \xf4\x04\xd7\xaeBEN\xe0\xbd\xb6\xa2.^h#;\x1c\x06\xd8\xbb\x0b,\x7f\x13\xe31m\xc7i}\xdd\xbfJ m\x90o0\x01\xcbj\xdc\x9bm\xb2\xe6\x8e\xee\xad\x8a\"\xab\xef.\xb8\xcbY\x1e\x1a\x07\":\x9f\xf0\xb0\xe2\x98Z\xb2K\xb8\x1a\x0e\x8a\x8c!\x14,c\x1f\xc1y]-\xf5\x13\xdb\xa1\xa4\xe2\xeb:t\xab\x9e9\xb8\x93\x95\xff\x87d/oJ\x0f\xd7\xe0}\x82w=\xa3\xda_\xd7r\x01\x8c7\x80; \xfd\xa9\xbd\x81\xb9$\x03#%\x1a \x83\xa6\x87\xb1\xae\xda\xa5iN\\\xe6y&\xe2\xfb>\xade4\xdc\xff\xe8\xccmk\x8a\xafL + y\xf2 \xf05\x10\xe9\x00\x1c\xef=\xb9\xc2\x1b\xdfH\xa8\xf3\x8b\xa1_\xd8/\x9e\xa5\x97\x93\xe2mg\x06\x03r\x1c\x8bh\xf8fd\x0dm\xdcn\xacmr\x0f\x1e\xc6\xfeI\xd1<\xf9\xd2m\xa0\x06Zw\xcaM@r\x93\x83t\x17\xb8\xf1\xa9\xd1,\xb7Blo\xf4+\xd2\x08\xfc\xf8zP\xbd\xef[\xe0\\\xbd3\x01s\x9d\xf8\xa1/\xf9\xaf|i\xaf\x06\xc1\x03\xdc\xdc\xb5\xa6T\xedG\xa85W\x9be?\x84\x03W0\xcck\xea\xdb\x8e)\x0f\x19C\xe3\n3D\x9d\x12\x0f'\xb5\xe5sY\x0dr\xc0\xa9\x84\xd5h)\xf1\xf0\xc3\x9c\xd0^\x9f\xc7L5\xd4\xfba_\xa4\x90\xc1\x88g\x95 ~Fh\xa7F\x97\xab_\x03Z|t\x03\x8bo\x95\xa5\xf7\xb9\xe8M\x1dD\xb6%\xa9\xe9\xcb\xb5\xd4\x12\x01\xf5Uoi\xb8\xba\xda\xcd\x86\xbe\xac\xab\x92\x95\x94\xdb\x13\x98\xd6!SZ\xf1h\xe9\xaa\x06\x06\x1b\xaf\xf3\xcf\xd0\xa8\xc6e\xa6\x0b\x1d\x03\x16\xcc)\x95\xc1\x1e$H\xecdM\xd3\x91\xccl:\xd2\xf4\x93k\x81\xac_[\xe8\x89W\xab\x98)\x0e4\x94SZ\x83\x85\x83\x84\x9a\xbaZ\\?\xadod\xe9G\xea$\xedyq\x15\x11\x9de)%\xfb\xcf\xb2\xa4\x8c\x83\xa7I\x84\x19\xdc\xff\x7f\x0f\x1e\x9e\xce7\xb7\xbb\xf7t\xeb\xe4\x19\xc6\x92fj\x19\x9dL\"\x9c3\x1bx\xab\xdd\xa8E\x17\xdf\x92O\xfegj\x0d\xd6\x03E\xd9\x10(\xd2\xd8K5\x0dj?\xcf\xe9\x07\xdax\x16\x81\xce\x18.\xd0\x19\xc3\x05:c\xb8@g\x0c\x17\xacf\x0c\x17\xa8\x8d\xe1\x82\xda\x18\xae\xebd\x93r\x0f\x81-\xa5\xb1[\xf0\xe9\x8d\xdd\xcc)\xfe$c7\x15\xed'\x19\xbd(L\xde:\x9e\xc2\x83M\xdbn\x95Q\xf8\xf31\xbf\xe93\xae)jO\xe0\x1es\x11JPO-t\xde\xd98M.\xadc\x03}O!L\xeb%\xcc\xd7i\x8d\xf9M\x88\xe0\xc2\"\xeeX\x9a\x91\x99_\x08i\x80\x1dsI\x8e\\\xc0.\xd7>U\xda0\x86\x8e\xcd\xa7n}\xe3\xc2\xcf\xe20>3\x89\xffE\xdd\x89uW|e\xec\xfd\x94\x84\xb1m\x81^\xe8\x91\xe8{J\xbd\x97t\x16\x1d\xfa\xf3\x97kW\x86\x01\xc3Pd\xb9\xb9\xc9\xb6\x88\xa4\x94#5d\x0b#\x97\xa9\x1f\x07\xcfX\xbd\xbaoOzO\xcf\x9b:\x01\xd4\xcd\x1c!\xfb\x1c \x19_\xa6\xbf\xb3\x16\x9f\xe75\xf4\xef\x0e\x1a\x9f\xad\x83\x86\xc15C\xaf\xa8\x890\x91c\x97\x89\x02~\x93\x87\xde<\xc9\x96\xbe\xa2_\xee\x92\xc1\x03\x9a\xab\xfd1\x84K\xd7\xda\xde\x1eD\x18\xd9\xfb4\x8c\xfd\xec\x8a\xbd\xc1\xecB\xd6\xa9\x9f\x93\xddm\xf1F\xef\xa9\xc1@_\xef\xd2\xa0\xf4\xe4\xe0\x01\x12\xe7\xa12\xdd\x90\x84\xeaJ\x1eS\n\xf6\xc1\n\xe3s?\n\x03\x8b\xc9\xe0\xbbm\x86E\xd4\xfc\xa2\xd4\xd4\\E$\x9a\xdbU\xcaK:\xda|\xba\xa9\x08\xd2\xaf\x90\x07\x04a\xce\xd9\xdc\xc2\x0b\xf3g\xfc\xaf\xe6a\xf8\xcch{\xb7\xca\xbd\xdfL\xef\x0duR~\xe1\xe8\x9e+\xde\xd5u3\x92\xa7I\x9c\x13I\xea\x01R\xa6\\\xcd\xebJ\xde\xc3\xdbnEN\xd2\xb9\xcb\xc6\xf6}\x05\xd6\xd3\"\xb7P\x8b\xdc\x8c\x84R\x15\xf0\xacP\x06<\x8b\xab\x80g\x94\x88\xccX\xc0\xb3\x0c\xbe\x82\xe2\x11d\xeb\xeb\x0e\xc4\xd3\xac\x19\xf0,\xd3\x07<\xab\x15\xf0&\x92\xadJzwx\x95\x17di;M\xdb\\\xfc\xeb\xbb\x9cN\xc7HW1Z\x96\xd9e:v\xc6r\xbf2j\x96\xad8?\xde\x0d^L<\xad\xdb\xf6\x0f\xdd_\x8a\x8d\x0c\xcd\xd1J\x854\xb6\x80}\xc0\xd4\x18\xcd\x06\xacc`\x81t\x9b/\x95x\x0e)\xd5\xe7\xb1\x1d\xf3\xec\x05-XW\xc0]kl\n\x03\x88V\xd3Sag\xfa\xcc/|\x8b}\xe22\x85\x03\xcbZr\x8c}\xb78YWw\x18\xee\xaa\xffn\xe3\xa6\x81\xa8N\xeb\xdd\x8d\xa4\xd3\xba~(j\x84\xd2?\x14q\x1eT\xae\xcc\x98\xb8\xa1\xbe\xf0\x84\x0f\xb3\xd6\xc9:\x91P\x9b\x9are~\x00Ul*\xc59\xc6\x80\xa2\xfb0\x0d\x11|;s\xc2\x98\xcf.\xc4\x02\x94\xf5\x15\x9a\xe7\x0bH\x94\x13\x15S\x8b\xbc\x96\xa6\x9d\xa2\xdb\x8ei\x1b\xb3a{\x93\x0f?\xc8\x9f\xc9\xa6\xc4C6\xc5\xbc#\x03\xb7#6n\xc7\n{\x11W\xaa\xb4\xcc{\x9dq\x17\xf5\xd4\xb1\x1d\xe5\xd6t.\xed!\xfb\xe3Br\xbb\x9d {w\xc6\xef\xdb\x99\x84\xc5\xddeq>\xf7k\x84\xe2\x9b6\x8a%#\x17\xa8G_M\xb5e\x08Mn\x9d\x82\xa8\xa7\x89G\x9de\xa3\xb4}\xa2\xbcrl\xdah\xac\xd9\xb6\x81\xb1\xbai\xeb\xa5\x97\x914\xf2g\xc4\x8e\xc9\x05\xbc%g\x07\x97\xa9m\xfdb\xc1:`D\xc6k\xcb\x05\xeb\xccr:*9\n\x11\xa5\x04\x1f\xf8\xf3\xf7\xa5+\x95\xca\x8e\xd2\x8e\xedqG\n\x1a\xf2\x92Q'4\x0fSX\x8c\xb7v\x95T]\xf9;\xb2\xac\x14\xfb\xfer\xed\xb6\xa5\x82\x99\x0b\xbe\xf7\xee\xcd\xb3'G\x07'\x87\x07/\x0e\x9e\x1e\x1d<;9}\xfd\xea\xe8\xe0\xd5\xd1\xc9\xd1\xdf\xde\xfc\xfbZ\xaa\x88\xe0\xd5\x16\xf5\xf0\xcd\xebW\x87\x07\xbf\xcf\xaa\xeadR\xaa\x98\xac=\xeb\x91\xb8\x10\xeaH\xf1U\x16\x84a\xaf\x93\xef\x9f\xbc}\xfe\xe4\xeb\x17\x07w{du$\xc4 \x0c\x16{\xef\x89\xc2\xa8\xc5\x17K\xad\x069 \xef)\xef\xfe\xcc\x85\xd0H\x11b\x05\xe3V\x94.\xf8\xcd\xf5\xcdnq%\xd72\x8fQ[\xbd\x97\xf0\xd7;\x0f\xa4\xfb6\xa1\xcb\x82y\xf4\x92\xec\xc0\x9f-l\xbdh\x01\xe9>\xef^\x18\x07\xe4\xd2\xfb)gr?-\xd5Gw4\xb1U1\"\x88G.\xd3$+\xf2)#\x80R?\x9f\xf9\xd1S?'\xdf\x84\x11\xa1\xdb\xe8\xd8\x85s\x8c\x1b#.\xd1}\xe9w\xdbAH\xba~\x07-\\loo\xefR\xb2H\x8c\x03\xd7eg\xb43\xe8k\xc3\xb2\x0b\x1b\x8d\xad\xb1L\xd0\xd4\x11\xbd\xecU\x0c5*Z#\x93\xa6W P\xdfd\xc92\xcc\x91r\x89\xed\xed\x9d\xfb\x8e\x0b\x87H\x91\xd7\xa65^^\xf8Y\x91\xff\x102\x0dIlo?\xd8\x1d4\xc3\xd8~8FM\xef\xc3\x07\x9dU\xda\xde\x19\xd6F\x1fpno?TB\xe7\xf6\x8e\xca\xc0%\xb6\xef\xb7_3b\xef\xfeHZ\xe9\xe6H\xc7[\xf7\x1d\x1b\x05n.X\xf8\xaf\xd5\x83\x87P\xbbt\x82\xd2;\x9b\x08'\xb3\x13\xda\xff\xa6\xf8\xe3=ES\xf5~\x18\x92x4T\xa6'\n!|\x15\xac\xe0Da\xd7\x18W\x85\xe1\xfa\xba\x12{\xac\x11\xdcTxL\x19\x94J\x9cm\xd7s\x10\xa2\xb9\xc4\x1e\xa1MzB\x0f\x9bE\x0f;\x8b\xd3\xc6\x8d\x0cYZ\xd9\xfa\x1d\x992\x99C\xec\xe2O\x89;\xbav\xab\xcah]\xf3D\x08*Q\xd7\xc0W:\xb3Y\x17\x0e\xfe\xac\xabg\xb6E\xe2\"\x0b\x890\x9co\xc3\x8f\xbc~\xf2F\xca\x0b\xac\x8e\xd0\xd8\xfb\xa5j\xaf\xf9*\xaaP\x17\x8b\xb9\xda\xdd\x93 \x89)\xdb\xb2f\xa6\xfdoy.F;\xeas\xf1\xb0\x1d\x95\x91\x1d\x8b\x87m\xc1\xb6\x8f\x9c\xc6#\xe9,\xeflb4\xf3\xd8\x1e=tl+,H\xe6\x17\x98CV\x0f\xbb|q(,\xd5\xb3k\xa1\x82>y\x1b\xa9\x11\x11\xc6\xef\xf6U:\x9e\x98\\\x16\x142Gn;u\x00\xed.\xc4\xb6)+\x0b\xcf\xaba\xaf\xb6\xdc\x12\xc2Q\xdf\x86[\xbb\xeau\xdd\xd5\xe2\x95\xedm\x07\xf6\x95\x9coHr\xe81@N\xecv\xa2\xa1Jk\x10\xbb\xb8y!\xaa\x07\x90\xda\xadT\x079S\x16\x94\xf0\x18\xf2G\x0ed\xde\xdc&\\\x182\xcd\xd7\xd7\x8f](\xa6q[\x08!\xa8\x8c\x9b.\xd8\xfd\x91\x9a|\x18\xa9!q{g[\xb3duw\x1a8\xab)\x0e\x96wFGQ\x94l%\xf4q-#$9\x84\xcaES U\xa3\x14\x1c#\x05iBI\x1cv\xa9\xc2\xda\x9e\xde\xb5\x117\xed\x11D\xf0\x18f\x8f\xf46\xc0\xb45\x9bne>\x9d\xad\xaf\x1f;\xb4\xcd\xd2\xa9\xcdU:\x1f2\xe1S\x7f\x970[_\xef\xe9\x16\xaf\x87\x19\x841\xe4Ho\xe4\xd3\xd91\x0b+\xea\xd4r\x0f\xac\xf2\xe1\x03j\xa2\xaak\xe5\xcb/a\xa3\x19\xbbhE\x1c'a\xb3]\xd5\xa9{\xe9\x17\x0bo\xe9_v\xc1\x88\x95\x84q\x1f \xe9\x11\xba\xcd\xb0\x0dq\x1c\xf8\n6a\x9f\x9e8X\xa7C\xdc\xa4\x97 C)7F\"\xea\xf9P\xac\xbds'\xc0\xaf\x83\xfc\x10\x83\xb8SHbD\x9eM k\x0d|\xb3#\xa2\xf3k\x8dPp\xc8\x0e\x88B+\xc1\xc6\x94\xe3\xda}\xf8\x009%/\"\x14\x87\xf1X\xb4\x9c\x9a\x9d\x80\x8dr8o\xb6\xf0\xb3\xa7I@\x9e\x14v\x8ek\xbe\xb33~\xb8K\xbf\x0d\xe11\xec\xecn\x8d\x1e\xb2\x86\xd6a\x84\xe0\x87\xb6\x04\xb6\xdf\xf9\x98V`\x0d\xecn\x8d\xb1s\x9f6p\x7fk{\x8b\xf7\xcf\xeacGt'a\xc2\xdf2/\xbd\xdc\xc5N\xc6\xb4\xcc\x87\x0d\xde\xcc:\x1d\xe7\x06\x1f\xd4W_\xc1h\xd3\x81u\xd8\xdd\xd9\xd9\xda\xbd\x1b\x08\xef\xdc\x1f\x1c vu\xd8\x90\x02\x8b\x83\x12e~\xa5\x0d\x8a*\xdc\xbd7\x90\x19\x13\x1f\xb6\xc4\xf0\xc5\"K.\x802\xef\x98%\x1dO\x80\x05a\x0eqR\x00R\x00\xa7\x11Y\xd3X~dv\xc1\xa2\xf0\x11g\xc5sB/\x81\x07\xc88\x8c\xb7\xb7\xf1\xdf\xed\xdd\x87\xec\xdf\xfb[\xec\xdf\x07\xfc\xfd\x83\x9d\x0eg\xb1\xbb\xe9\x08\xaefHg\xbd\x84\xd4\xaejgd\xd2(\x99\xc6\xf6\xe8\xbec[E\xc2N\xd5\x91\x7ff!\xdbi\xfdlQVn\x9d\x82\xfc\xda\x1eX\xd3\x04o{\xf8\xf9\xd8b\x0c\xd7\xfd-\xc7\xe6\x14@\xed\xc9\x00UCV?mU\xb5\x89\xe9j\x90l\xa7\x90i\x1dK\x1ah\x0c\xa94d-\xe4\x85\\\xa3\x1c\xfe\xa6\xc32\xac\xd8\xa3\xcdQ\xbf\x0d\xf5}:I\xb5(\x9f\xae\xe3\x03\x87Y\x1e:.X\xbe\xd2\xfe\x10\x83ik{i\xf7\xd6)l\x99\x088\x9e_\xaf\xc1\xa0\xf9KDK?\x11\xa2\xb8;0)\x0d\xbb4\xc4\xd5\xf8\xa8s\x0c\xd5z0Le#\x9d\xc3*\x02\xb6\xcdTG\x02$\xd8\x86d6\x13U\x89\xf3U\xf5\xa7\xd2\xb0\xe9\x1bE\x1e\xe5\xf5|\xf56\xd7>\xcep\xdb\xf8\xc6z\xea\xc7\xff\xb1\x80Y\x12\x9f\x93\xac\x00\x0e\xe9E\x02i\x16.\xc3\"<'\x8c\xcdZ\x95\x9a\xef;\xf3\xdb\xbbm\xc91\xc3\xc6\xe3\xed-%\xcd:RJ\x15Z\xec\xd3\x03\xc1>\xdd\xff\xef\x99}\xd2\xb0\xa5\xdb\xbb\xea\x95\x1dw\xc48>\xc7\xca\x94 }~p\xf2\xe6\xed\xeb\xa3\xd7\xed\x80\x15e\x9b\xdfo\x16\xb7\xc5\x01\x9d\xf58g\xb9+\x0b\xde\x15E\\\xe1<3D\xc6@+\x0c-5\x84$w\xe1\xa1S\x90\x17\x84y\x1a\xf9W\xf4v\x88\x93\x18\xf3E\xdb\xe3\x9d\x11\x9a\xf5\x938x\xba\x08\xa3\x00Y\xb7\xc2\xcb3\xcacX?\xf9\xe7>\xf3\xe9\x9dXU\x16J\xee\xfb\xf7C\x18\x07\xc9\x85\x17$3\x14\xa18^\x92\x92\xd8F\x18\xb9\xc8\xc2\x82\xd8\xd6W\xec\xd3\xc7\xa2\x8a\xf7\xcd\x1eC\xd1_\xfdx\x8f\x17\xa1j\xd7\x9bEI\x8e\xe9\x0ds<\xc1\xdf<\x82lc\xe3\x91\x03\x01\x89HA \xaf\x01i\x1aN\xb3c\xbdMYn\xb7`H\x8dI\xf9E\xc1,8)\x9dfD\xad\x889\x95tF\\F\x11J\x90)\x15g\x97-x'\x0ecpcrA\xf9\xbef1s\xff\x8aYZ^\x82\xa6g\x98\xd5\xc2qei\xab\x90p%v|+\x9a\x7f\xa46\x1e\xec\x9c\x08\x0e\xf9\xdb\x0f\xf4\x94\x1f\xbd\x98\xff{\x90\x1d\x8cF\x0f\xd4d\xf1\xb8\x8d\xa0\xb9\xf0`w\xd7\xb1\xd7\xda\x02\x075\xca\xb8\xc1\xfd\xce\x97\xa8\xe4\x84t\x17\x17\xe0\"u_Sfiz\xacX\xf3\x98\xf2\xd5\xa5\xc3\xa4\x04>\x8a\xf31%<^\x9b\x91\x88,\xa4\xf8\xf0\x11\x14BX\xcb\xf7\x03\xbf\xa3\xa8\x01w\x83\xb9\xa8\xfc\xa7\xd0\x8e\xb0\xb5\x0f\x1f\xea\xd6\xd4[\x14\xddt\x8b\x1e>\xd4\xac$\x83N\xdb\xfa\xd9r\xd0\xd5\x82\xd2\x81\xcf\xf3\x83\xb8\\2\xbe\xc1\x96`\x18L\xe6\xd1\x82\xd2=\xac\x93\x83\xd0s\x8d\xe6;y\x1a\x85\x85ma\x8e}\xde!\xb9\xf9 \xed@\x95\xd0ti.\xa7m\xdd\xdc{'\xd3\xe0\xd6\xff]T\xf5\xdf\x92\xa8J\x83\xb2\xb6w\xdb\xef\xc3\x01\x94\x8c__\x94\xd5\xc5e\xbcN\xcfH\xf1FT|=o^\xab\x1aX$\x02\x9d\x01fp\x0e\xf1dMQ\x1b\xad\xa2\xf0)\xa9\x90\xc4y\x91\x95\xb3\"\xc9\xd0\xe4 \xc28/\xfcx\xd6-\xddo\xfe-\xdd\xbe\x93\xe6g\x1c\x0f\xec\x83\xdf6\x00_q\xfdw\xb6nz&9\xfe\xc8V\x17XT\xf7'g\x1f(;P\xb1\x0c\x0f( \xcd\x98\xca-\xc7\x15\xde\xf0[\xfc\x82E\xc6\x80'\x8f\xb5G\x9bc\xc7\xe5>\xb5\x94Z\xc0\x83\x1b\xb5\xb8\x05\xf6\xaa!kp\xd1s6\x17\xba\xb3\xa0\x13m\xe1\xe9\xe1\xe1\xdb2\"/\xc2\\\x11\xec\xe0\xe9\xe1\xe1!%M\x9f\x91Y\xe4\xb3x\xd3\xdd\x80 O\x0f\x0f\xd1\x14\x817\xd1.\x8dB\x12\x17o\xc9\xacP\x97?{\xfd\xd2X\xc8\xe6\xa2->J\xde\x93X=\xf8g~\xe1\x1fe~\x9c\xcfI\xf6\xbc Ku\x1b\xdf\x84\x91f\xe4\xdf\x1e\xbd|\xf1$\x8a\x9e&Q\xc4\"P\xa9\xab\xf4\x95\x7f\x93dK\xee\x85\xa4\xae\xc0\x9c%\xb4U^\x92 \xf4\xd53|\x19. e\x89qs\xbb_\xbe\xf2\x97$x\x95\x04\xe4\xa5\x9f*J\x93@\xb3\xebo\xfc0\x16\xe1O\xd4K\xf3&*\xcfB\xc5|\xd9{\xcdp\x0e\xbf\xff\xd3\x0b\xbc\x8a\xd4m\x1e~\xff\xa7W\xe5\xf2\x94d\xda\xe27\x98%X\x03\x0b\xb4< c\xcd\x80\x0f\xbf\xff\x93 \x90\x0e\xbf\xff\x13\x83\x94$\xd3\x80\xc9!f\\\xfb\xba\x9c\xcf\xb5\x03\xa4\x07\xe5pAH\xa1^\xd5#rY\x1ce\xfe\xec\xfdS\xddQ\xa9jh\x8a\x93rV\xad]Ur\xed\xa2+zb\x07\x945a\x94\xf89|\x05\x0b\xc1s\xc2\xf9\xfa\xba\x8aZ]\xba\x18\xc9~1=W\x18\xbcQ&4\x98\x9e)JN\x91\xacW\x95\x9c\xc0\x1e\x9cR\xa4\x7f\xaa\xba\x90\x80_\xc5'H~\x9e\xd0\xfb\xf7\xc3\x07(\xed\x13\x17f.\xa4\x8e\x0b'\xd3y\xfdn\xee\xc2\x19E~\xd33\xca\x80\xa5.\xa8\xe2\xd2 r]\xd2[=s\xe0d\xba\xc4\xcfC\xfa\xf9\xd2\x85l\xba<\xae\xc5\x9b0\x14a\xf7\n\x804J\xcb\xed\xfbj\xbe\x03\x11w\xe3\xbd_Q\x94:&n\xbc\xbd\xfb\xefv%\xff8v%z\x82\xef\xbec[e\x9c\xcf\x92\x14\xbdU\xda$\\\"\xfc\xf5T\x07\xa6\x123@2\xcd\x8e\x99R`\xe7\x01\x1a\xaff.\xfc\xa2\x97\xf6u\x98\xfaiv<%\xf4\x18\xc9\xf6\xf0\xca\x99\xe8$\xfeF\xd8\xfb\x0c\xed\\\x84\xb1\xa9/(\xa9\xf1v[\xc2\x92W\xc4V\xe35\xa7\xb0\xc6\xaa\xb8%*\x8d\xcf\x9c5\xdf\x16\xd4\xb0p%\xf7\xb7[\xaf\x03\xdez\x1b\x85,8\ni\xd7?\xe7\xef\xdb\xf6\x10K\xd6\xebN\x1b\xb5\x9c\xf1\xf7[\x8e\x97\x93\xd6\xba_\xb1\xb6\x1elvb\xe1\x9dr`m\x8f\xea\x84\xb7\xd6\x1e\xd5\x05\x7f\xdf\x1e\xd5\x01R\x9a\x95\x8c\xbeYx\x89\x85i\x96\xccH\xde\xf2D?\xc4\"\xae\x98k\x16=\x85=\xb0\xf8Gx\xceg\xf6e\xab\xd7\xf7f\x89\xee\x13\xb4\xb0\xdd\x83So\xde,xM\x0f\xc4\x9aY\xda[dW\x1a\x9eW\xe0\xc8C/#y\x12\x9d\x13\xbb\xbdz\xf2\x83\x1e\x1aM\xf6g\x8f\x1ea\xa1\x1e\xccS2C\xfcr<(\x1b\x96x\x88\xfd\xde\x85\xf7z\xd6\xf7\xba\xcb\xd2\x83d\xc7\xf0\x14\xfdQU|\x1c\xdf\x8b\xb7\xe4'F\xd9\x1e\x9c\x93\xb8p\x98\x0fK\xb1 \xb1\xfd\xde\x919\xb4\xa2\xd3\xcd5\xcc\xfcb\xb6\x00\x9cCK\xf9\xd6\x06\xbf7\xbdsF\x15\xb5V\xa8\xbcf\xaf\xa5\xf4\xbb\xe6d*m\xb5\xcd\xe21\xd0a;8\x85\xe6h[\xe0r\xd4\x87\xed@\xe8\xb9\x88w\xa2\x95\x88\xd02\xc4\xb7\xea\x0d8\xe7\xb6\xcb\xc4;\x99\xa9k\\\xe95\xaa\xf2\xd3\xe0.\x89wr\xcex\xcb\x11`\x8c\x9a\x93\x9c\xb1\x97\x9b\x8c\xb5\xac\x05K}p\xc5\x85\x995\x02M`\x1f\n/y\x0f\x13(\xbc\xb9\x1f\xf6\x84@\x87*A\x14?\x1c\xfd\xd5#^\x9d\x02\\\x7fm\x9649H\x96~\x18\xab\x17P<\xfa\x13,?%\xa5?\x124\x1b\x19\xf3\xb5[PP\xf9 \x89)\xfck\x0fF\x8e+\xe2\xff\x94H\x81\xec\xa1I\xb5\x8d\x81*f\x1e\x89\x0b\x92\xd9\\\xa7P\xda\x19\xf2\xe8\x98\xa1\xd8#\x97aas\x06\x7fm\xd3au\xf6\xd0\x1b\x81\xdbX\xefCd\x1f\xd8\x16?w\x1b\xb3\x85\x1f\xc60\xbb\x9aE\xc4B\n\x08Ma\xde\xd8\x14\x82\xf7!d\xda\xd2\x18\xfdK\"Z\x9cc\xc9\x04\"[\x91\x1dP~\x1a\xe7\xb2wYp\xfck>\x9f\x1f\x9fDd\xf7\x84\xdf\xbc6\xe0#\x88k\xd9t\xf8\xc8\x01\xdf\x8e\xa7\xe1\xfaz[9 ?\xf4\x90\xa0\x90\xdc\xad\x8e\xd5\xc8\x05\xd42\xaf\x89}z\xa9\x1b\x93\"z\xe6\xb5\xe9\xf8\xbf\xec\xc5Egl\xf1s\x03\xfd,\x1eD[(\xc4\xe5f\xfbxB\xb5\x13\xa5[\xfc\xbc\xa3\x80\xa9J\xe7\x14\x08(|\xc0C\xe0\xf0\xa3c\xea\xed\xa7\xde\xdeV\x85_54\xca\x80U-\xfa\xb7l7,\x01S\x05\x87\xa9\xaa\x02\xdf.v\x0b\x9b\x92u\x0e\x00'\x01J\xf4L\x0d>\xfa\xc6\x9dz\xd5\xbbv\xc2T\x8er\xaa\xddu)\xbc\x93\x00\xaf\x10\xfcA1\xbd\xcb\xd6\xa0\xf0N.hA\xe1x'\x94\xa2\xa7d\x85wB/\xc81\xfe\xf2\xc5W\xccG\xfdd\xc6\xed\x0d\xe9Eqd\x17(\xc40\x8e\xfc\xed\xb0\x91\xbb\x15o\xaeV\xf5\xac\xc5\xdeI\xa0\x03\x86\xb8\x9e\x14*\xcd\xf9\x9c4\xd7\xaf\xf9\xda\xa5\x9d\xb1\x1b\xb0:X\xf5\xe5\x073\xb4\xec9\xa5\xa7\x19\x89\x87\x00\xc2\"'\xd1\\\x97?\x8f>\xb8\xceo\xd0\xbcj\x7f(\xf1\x04\x12\xaf\xde\x7f\x17\x9e\\L\xc0\x90l\xb1\xaa\x16h\xd3\xb2\x8aGC\x95\x8bg\x18\xc5\"\x0c(\xe9}\xfc\x16/\x98\x11\xde\xcd\xaf\xf8\xef\xbb$\x03^\xb1\xbe\xb2\xde\xc0\xdb\x86\x9b\xdf\xa1wL\x05\xfe1\x03\xff\x11\x85\xef\xd8\x855\xddx\x87\x8d\x93\x8f\xcf<\x91\x01\xfb\xd7\xb3w\xd7\xda\xf9w\xe7\xdd\"2\xea\x1d\x7f\x8dg\xfd\xd0x`\x17<\x82\xe7\xa1\x0b\xe2PX.X'\x0b\xcbq1\xd4\xa9\x0bY\x9d\xc5\xbau*\xd4\xe0Cl\x04\x13\xd6n\x05)\xe2\xcf\x16r1.\xfa\xabf\xfe\xec\xe6\x97\xd5_\xd7.\xbb\xc4\xf5\x93d\xd2>A\xd9\xb1\xbf\xe4\x9b\x97\xbd\xc9e f h?\xfc\xeb\xbcSy!Wf\x84b= \xa7i\xdeco?\x189\xf6\xa1l[\xdb\x1e\x1f\x89\x07\x84\xfa\x17\xac\xdc\x13{)v\xcd\x9cS\xfc=\xec)\xd9T\xa6\x7f\xc6\xb3A\x19\xacf\xad\x9a3G\xba\x97br\xce\xfd \x19C\xefb\xfe\xe7\xa4\xb5&\xb3*\x07U\xb5\xc6\"Y\xcc\x89\xdf.\xcbi\xd9\x11\x9f\xc7\x1a\x05\x93Xp(\xcd}n\x9e#\x04\x97\xbe(v\x92\xc5\"\x13!\x88q\xeaa\x88kG{\xe5\xd41\xb9\x80\xecQ\x17\xba\x04U\xc8n\\\xfa\x86\xdf(\xa8'}\x8b \xd5GNU\x84Z\xe6=v2\xb0D\x86\xe6SoNwy\x88\xb2\x98\xe0\xcdv\x88\xdb\x89?}JA\x93\x0b\x16\xf4m\x82\n\xf5\xc6$\xe7\xf6\xdc\xfb\x13\xac\xc3\xdc\xfb\x01\xff\xff\x0d\xfc\x11\xd6^\xb7\x01\xf2\x8d \x8a\x0e\x1b\x1f3\x13S[\xc6\x15\xdc\xfe}\xec\xd8\xf2+\xa6v\x90L\xe0Y\xc7\x87\x8d.%|\xd3\x9e\x1b]\x9e\xbeM\x16\x04\xd2\x13\x15f\x02I\xf4\xb4\xe9V\xdc\xbe\xc3\x14\x16j@\xeb\xacS=\\\xbb\xa4+\xbc\xf6\xda1\x8e\x1a\xf7\xbbo\xd8|T\x17v)\x0eG\xb5o\x870\x81>\\\xd7\x19\xda\x9a\xfd\x9a\xc9\xeb\xb7\x1fl\x99\xa2\x85\x1ez\xcc\xea\xd9\xc3\x13d\xbf\x97\xc1\xc24-?\x8a\xfa\xa6$\x93\xaa\xea[\x8fa-\x9d\xf1\x10\x8b\x86`\x14\xdf$\xbc\x8a^d\x13\x0e\xe7T\x05\x1e\x9d\x1a\"4\x03o\xd2\x90$\x1f\xb8~m\xa4\xa7\xb1\xce).\xa7\xd7\xc8p9\xeb9\x0f\xb6\x14\xae\xaf\xf7S\x80\xe8!a\xe8\x1f\x90\x98F\xcc\xcbP =\x9b\xeb\xebn--\xa3\x10\x81(r\xf8\x08\x01;\xa6\xa4E.\x88\xf4iy\xcc0\xdf\xc6\x062\x18\x99\x1d\xf7Q\x85Z\xa6\x198\x98KM)\xeb]\xeb\x8f|\xe8\xa1-Ub\x87\xde\xf9\xd0\x8b%\xf3g\xbdg\xf7\xae\x00]\x0f\xc5\xc9\nP\xbc:luw\xbd>v`\x90\xe6i\x93\x08jw a;\x90\xd9\x89i\x07$\x14\x84?o\xa4\"dB\xaf\xf6\xd4\x91\xc7\xb4\x1b\xb6]\x05\x8a\xed\xb9\xaasmo\x0f\x98\x84\x07\xc2\xb8f\x0dk\xa7\x8f\x18\xd6\xc1\x9a@\x18\xcf\x92,\xa3\xb7u\x18\x9f'34K\xd2\xb9\x9a\xdd\xdc\xbe\xb8\xa3\x02\x14z~\xb5;\xf7\xf6}\x95\x9f\xbc\xc2\x86\xbb\xe4f\x01m\xcdc\xce\x9bi\xdb\x02F,\xb0W\xe3\xdd\xac\xe5C\xc2u\x1c\xa6\xdd\x98\xbb\x90\xaa\x08\xa8\xc0\x85\x85\x0b\xe7\xae\xb0\x07Ia\xbf_2\xd4Y\\\xf1\\\xa30Ze\xff|\xc5|Fq E-p\xeb\xd4;E\x13\x96\x0e\xdc(I\xe6\xb3\x9b\xfa!\xa20\xd5>sT\xf3C\x9dJ\x802|a\x9d\xe0<\x82\x00\x1e\xc3\xe9#8\xd5Y\x9a\xa2\x95\xe9\x92\x07\x8c\xbd\xb2}\x9b2#dzz\xecL7\x8f]XLG\x18+\xf0\xca\xc6wN\xed\xa7\xba\xc4\x9f\xb3\xca\x0cu\xd9<\x8ej\x13X\xa6\xf7\xc1da\xdcq\xea\x11\xaca\x97\xe7^L.\x0b\xdbq\xbc \x89\x89\xc6\x1a\xb7\x1alb\x9f\xbbp\xe5\xc2\x82\x07\x82\x82b\xd8\xd0\xae\x1d\xef\xeb\xb7\x07O\xfeL\xc9ezq\xbd=8z\xf7\xf6\x15\xec\xc1l\xb5C\xb6\xd3o%-\xe07\xe90\x90JFW\xe0:\xd8\x87\xc2\xa6\xf7\x14.\x7f\xcc\x97\xbfh_\\\x15\xafk\x8c,I<\xd6\xacB\xe6\x87\xe0'\xe1\xaf\x90\xa1\xd8\xb0rhs\xdb\xfa\xc6?4\x7f\x0d^\xab\xae!QR\x1b\x99Hf\xa0M@7Y\x98\x0c3\x1f\xe1+*\xcd\x11\xaf\x11;cv3L\x8c\x87\x86W\xd3\xe4\x98\x0b\xf5n&:\x8d\x1c/a\x98\xc3NuY\xa1f\x0b?\xf3g\x05\xc9\x9e\xf9\x85?Q\xba\x94q\xfb\x9c\xde\x85H\xbd\xc0/\xd0j\x8aNe\xde\x03\xdfJ$\\\xf5\xa1\x9a\x85'\xde\xdc.\xd0TOA\xf0a\x82\xb4\x12\xb9\xe0\xaeK\n\xac\x1aX\xa5\x90\xe3M\x88\xa7u\x14nLo\x18\x89\xfc\xa4%U\xed\xde\x7f\x82Y\x9b\xde?\x9ef\xc7m,\x1br\x16\xae\xef\xec'M3y`\x13`,\xd4\xac\xd3q H\x04\xe3\xaaB:\x1d\x1c\xc5\xd3\x12t\xfc\x01\xb8\xf3C#t\\fg\xde\x1bX\x87\xcc{kP1\xcd\xc3\xd8\x8f\xa2\xab\xa1\xd2w\x9f+\x8d\x93*j0\xe5\x88\xc5\x1f\x1a\xd1{\xacSr\xab\x92\xd9\xb4\xd5\xc7\xb1,\xa7\xd4\x1ab\xf3\xcfJ\xcchj;m\xbd\x8a\x89\xcc\xeal\xb4\xfc\xa8\x8c\xcb(\xebF\xa9\x8b\x8f<.\x86`V\x1b\x96^u\xf9\x11\x81\xb7\xebP\"\x02\xf7l\xb7\xc0\xf1\xd0\x00\x88E6\x18\x08\xf1\"\\\x84\xb9\x01\xdcB\xa5}\xad\xd0J\xc7\x1eACwn\x0b0\xa9\x953\x8e\x1d\xa3\xd2\xa4_M=dAc{\xfb\xc1}\xae\xa5\x7f\xc0\xff}\xd8\x8cj\xc7\xc3co?\xe4Q\xed\x1e\x8a\xf7;\xfc_\xfe\xfdC\xfe\xfdC\xf6\xfd\x0e%G\xf0\xdf\x11\xffw\xcc\xff\xdd\xe2\xffn\xf3\x7fw\xf8\xbf\xbb\xfc\xdf\xfb\xfc\xdf\x07\xfc_\xde\xde\x88\xb77\xe2\xed\x8dx{#\xde\xdeh[\x19e\x8f9\xdb\x0eY\x8b^0\x1aw\xc2x\x87U\x90J\xbc\x92\x9f\xf2\x10\x8f]\x94(WJ\x02\x82\xfe\xc1-\xc8CD\x88\xe6\x04k\xcc\xd0}\x84\xf1V\xaa\xa0\x19Ul\x91\x0e\x82\x94\x1b\xed\x83\xd0:o\x9f+\xb4\xdc8\xe9n\n?_$\xed{\x0c\xbeVL\xc0\xa2\xc2\xed\xc1z\x9d\xc8\xcf\xc78; \xc5'\xa3\xd1h{4\x1a9\"v>C\x18o\xfd\xf8\x8c\xebH\nYG\xe2\x03\xa6\xb3\x84Y\x12\x10H\xe9dtv\x96\\i]\xc0W,\xba%\xecc4 \x0cy\xca\xa2_\xae\x83m\x17\xb0\xb1\xc7\xca\x1dx\xfc\x18\x10~\n\xf8\x0f0\xda\x1co\xc3:\x8b\x99\xd9\x9b1\x17$\xfc\xcb\xb3\x0c[\xb7\xc3a\xbd`\xa6\x8b\x1b4\xda\xdcR`+\x0dPd\xfe\xc5pP`\xb15\xbc\xcc\xbf\xe0LiX\xcbnM\xe0A\x81\xa7d`\x12\xc3c(\x1f9\xc0-\xb9x\xe4\xd6bZ\xae\xaf\x1f;\x18F\xe2+&kiV\xa8\xc1\xa6<6X\xab\xf9w\xb3\xf4\xea\xeb\x83\xe2\xacM\xc7\xb6\x8a,\\Z&\x85y\x9b\x9bV-\xaa`\x059\x15\xb2u\xbb\x01\xf7\xc2\xca\x8e&\xd6\xdf\xa6:\xbc\xd4\xf6\xc3\xf6{\xba}\xd6\xd4\x82u\xf0YD\xce\xaeXS$\xdb\xfa\xff\xd3Z%\xff\xcf\xfac\x9b/\x8a\xea\xaau\xa5/\xda\xb5f\x03\xb8o\x90\x85\x12\x8aT\xb2\xc0\xc7\x1d\x0e#S\x04k\xb2\xe6O\xc9\xb1\xcd\xbc\xf3~\xfb\xf5\xff\xf8\xb7\xff\xc2\xe2\x9d\xf2\x9fX\xa6l\xe3Zs\x8b\xd3\xb5I\x98;s\x89J\xbe9\x86\xe3\xed0\xca\x807\xfe\x97_\x82\x9dLcZ;GWnA\xfbR\x94_\xca\x07\xb9e\xf9\xd2Z\x809\xec\xc1\xcc\xa3\xb0\xda\xc7\xa0\x81\x04\x8er0eT\x05\x8e\x803\xef6\xe1jE\x96]-w\xc1\xc2\xbc\xeccM\x85HTh\x11\x1ej\xc1\x82Z\x0b+\x8fT\xaem\xfdX\xfc\x18\xffx\xfe\xe3\xfc\xc7\x0c\xfe\xed_\xff\xeb\xff\xf5\xeb\x7f\xfd\xd7\xff\xf3\xb7_\x7f\xfd\xed\xd7\xff\xfc\xdb\xaf\xff\xc3o\xbf\xfe\x8f\xbf\xfd\xfa?\xfd\xf6\xeb\x7f\xf9\xed\xd7\xff\xf9\xb7_\xff\x97\xdf~\xfd_\x7f\xfb\xf5\x7f\xfb\xed\xd7\xff\xfd\xb7_\xff\x9f\xdf\xfe\xf3\xff\xfd\xff\xfe\xfa\xeb\x8f\xe5xs\xfc\x00\xff\xff\xf0\xc7rN\xe6sk\xc8\x19\xbb!M9\xde\xde\xc1(n-vF\x8f\x91g\xe2\x8a~\xd2{I\x0b\xd5q\xafm\xf3 $r\xc3 \xea\x02\x8a\x8d:\xe1%(n\xb1,\x8f\xc4\x01\xe6_Q1x\x14\xc8\xe9\xa7[\x8em\x89z\x96\x81\xa6\x11u\xfaVJ\\_\xa1X*\x17\xe4\xf6\x95\xe76V\xdcg\xf0\x18F\xb0/\xa5#\x1e\x1d\xd7\x06\xcc\xcaV2\x96\xf1\xc7\x1c\xd3\xacl\xe9Iy\xee\x1b\x11\xf9\xddN\xd0\xe493 \x18~j\x0d\xbc\x82O\xc7\xcdM\xe1\xd1\x0f\xb3DM \xf7\xdc)a\x03\xeaK\xbbd6\x15\xf9\xef\x02O\xf7\xc7J\xde_\x06\x8d0\x9eEe\xc0\x82]\xe8@C\xd4\xe9\x03\x8d\n\xed\xff\xa7D\x02\x8e\xba\x07\x0fS;\xbd\xc6\x08\x91\xab\x80\xc3\xed\x0ecc\x99\x06\xe3\x8e\x8c\xa4\xc4/&x\x83\xef:+v\xd9\xb7_\xa3\x91\x96\xb6\xb8\xa9\xb4\xb8\x0e\xdcO\x99`\x05x\xa3\xc0E\x91\x89>\xe4\xf1P[\"S\xf48\xe5a\xfaC\xd8\xdb\x83\x11\xdc\x83M\x05Ca=M\xca\xb8\xa8\x1d\xb7br\xe6\x17\xe19is\x12\x0f/\xc9\xdd\x0f\xbd(>\xc9\xd8\x93\xb8\x98%\xd1\xc78\xb2\xb4i:|\xd1\xfc\xc7<\xb6\xb4\xaf<\xfc\x99|\xbcY\xf0\xd6?\xe6$\xc2\xc2\x8f\xc2Y\xbe\xd2\x1c\x86L!\xfc\x14\x80\xb42\xf2\x19\xb4\xfa\x88\xf6\x17\x19\x99\x7f\xe4\xa5\xcf\x97~\x14\xad4\xfc!\xa3\x17\xad~\xf4\xc5\xa7\xef\xdf\xaf\x06\xfc\x83\xc6/\x9a\xfd\xf8\x13(O\xef~\xf4\xe5'\xc1\xfey\x99~\x84\xa1\xa7w4\xf4\xd8\x1e\x8d)\xb9\xbc\xf4\x8b\xd9\xc2rad\xae.\x0dfZ\xd5S\x8a?\xd5k\"\x1e\xc1\x19\x10\x93\x921\x91e\x0f(z\xa8\xd2\x99\xc5\xd3B\x9f\x19C2\xafO`_\xd8\xe11/\xaa \x9a\xc0q)o\xecL\x8bc!\xc8\xcf:qA >\xbe\xe1jrQ\xa3\xe5\xc2\xf8\x06\xeb\x99)<4`\xd0\x92\x86}K\xea7\x964\x93\x974\x1b\xb8\xa4\x12?\x91a\\\xb3\x04W\x95\xbd\xe1k\x19:,N\xd3\xdd\xadhN\xfc\xec\xdf\x01\xf4\xee\x963\x8d\xc2B \x9e\x1d\x03K\xfd: \x0dGl\x8fw\xda\xbe& D!\xdd\xd7L\xef\x86J\xb4\xae\x90\xc4\x9a\xa1\xf1\x8a\xe5\x9f\x9e\xce,\x9ew\xe2\x9e}\xea\xfc\xf1\x9eC\x99\xe3\x0f\x1f`\x1bu\x1e\x05\xc9\x8b\xba|\x7f\xe2\xdcsac$\xc2:\xd1zc\xac\xe7\x9f\xca\xb5|lH\xaa\xc4\x1a\xf3\xea:\xde\xbeC\xffkT\x92\xcb\x1d[*\xa3\xdc;-\xaf\x8a\xbd\xfd\xaaP\x05r\xe7\xdc\xf7Y\x12\xa8\xde\xb3\x9d\xfd\xfd{\x1e\xb9$3\xdb\xb2\xe8\x1c\x15P3DO\x02\x92\xad\x9a\xd0]\xaa\xe3\x06@\xd3'gOx!\xf14<\x95%\\;\x95\x8a\xfc\xedZ\"\xa7_\xab\x83\xe8\xe1\xe8\xd4\x9f\x9d3K\xff\xdc\x85\x08\xc3T\xcfY8}\x93\x93z\xc0B}\x86gq\x92\x91\xa7>\xc6\xf6\xb3B\x0b&\xf4\xda\x83uZ\xb6,\xa3\"\x8c\xc2\x18\x8b\x96\x8d\xa22\x0eQ\x11\xbf\x0fV\xd9(\xc8\x8bp\xf6\xfe\x8a\xbe\xbf\xe2\xef\xf5CX\x98}\xe4\xcf\x9b\xbbY\xc0>l\x8f\x1fn?\xdc\xbd?~\xb8\x83\xe6\xfe\x8f\x1f?65\x80\xd1g\xeb\x03O\xbc\x1c\x83\xa3\xbb\x10\xc0:Xg:\xfb\x01\x94\xfea\xd0\x06t\x8e\x90Z`J\xce%o\x876\xf2\x85\xbd\xbf\xf6\xe3\x8f\xb9c\xb9\x10\xa84\xd4\xd5\x83\xfe\xeeK\x06\x8b<\xbe\xe7\x9amG\x18y\x0cE\xcd\xb0\x0e\xf9t\xf3\xb8\x82\xf0\xc7\x80\xf1\xd5\xec\x94\x07?\xe12\xa5\x85+>p\x1c\x17\xd6\xd0\xb6\xbf!\xf1\xc2\xa4!\x9b\xc7\x95F.s\xcd\xe4O\xe3\xc1\xa9\xcf1.\x01\xcc\xe1\xab\xae\xe4{\x03\xc6\x8f`\xbe\xbe\xee\xc8;S\x8b\xd8\xe6h\xe8k\xe3\x8f=\xa5D\xbc\xf1\\;nw\xf0|9\xbe\xaaC0\xa2]\x00s\x14J\xe9\x07l%F\x0e\xcf.!-\x1b\x8b1\x1f\xb9\x90V\xad\xee\xc1\xb9\xe3|\x00\xbec,\xa3O{\xfb\xe8\xa0\xeb\xc1\xc19\xecC\xca\xcb6]8\xc7O:#hY.3\x8f\x06kS\xa0F!\xd3\xdct\xa4\x15\xb3\x07a\xb6\xe6\xa5\xd9FW\xb0\x0f\xd3c\x98\x08\x1cT g\xdb\xdc\xa0Z\xcc-\xd1\x08\x1a\xa2\xeb\x06d\xd5\x8d\x08\x01\x89\xac\x8ak\xb2*\xeb\x90U\xb1\x8a\xac\xcaV\xa5\x03\xcc\xf2\xfa\xd4\x8e\xed\xedQ[\xec\x9c\x88\x92q\xbb$\x14%;\xed\x12\x9f\x97\x8c\xee?h\x17\x95\xbchgk\xb3]\x94\xf3\xa2\xadNO\x11/\xb9?\xden\x17\xcdz\x03\xf7U)\x98\x88wrB\xf2\x97IPFD\x97C\x14$\x99\xff/\nW\x10\x8c\xbb\xc7r\xe2\xe9B\x99\xd5\xf9\xdex\x0c\x86v\x8a!o\xe1\xe7\xaf/b\x91\xbe\xb5\nC\x17s\x95\x0d3\xb6 \xdd\x84oP\x83\x10&\xa6\xf3\xcb\xa8\xe0\xa1\x99\x9a\xa0A7e\xbb\xb3Ts\xae|q\x1e\xfd\xa1z/\x96\x0eR-\x8b\xdaY;\xcc\xf4<\x18Y\xa3.E\x92\xd6Y0\xde\xdd\xd9\xdd\x1c\x05-E\x1b\xbdv\xad-o\xf4\xc0\x1b\xb7J\xe8}j\x9d\xfa\xf1OI\xab\xe0\x8c\x16\x1c\xfa\x85\x0b\xe3\x1dxR\x9e\xc1xs\xf4\x006\xefOv\xc6\x93\xf1.\xfc\xe9\xe5\x91t\x10\x86\xe9\ns\xb1\xf4\xde9\xc9\xf20\x89s\xbc*;/?|\x80_\xae]E\x89\x97_\xf8gg${\x17*\x9d\x97x\xb5 (\x02\xdd\x9e\x85\xc5[r\x1e\xb2\xf2\x85\xb2\xfcY\x98\x15W\x13\x08\xba\x85\xa7e\x18\x05G\xe1\x92\xe4\x85\xbfL'p\xd6\xad\xb2\xf4g\x8b0&\x93v\x0c\x85.\x07Ph\x1d\xaf\x82dy\x12\x06,\xcf\x94\x1ao\x06\xc9\xf2U\x12\x10S\x95<%\xb3\x89\xde\x88*\x8b&J5,/\xccMMG\xfeUR\x16\x13\xb0\xbe\xf6s\xf2\x02\xff\xd0\xb4\x14$\xb3\x83\xcb\xd4\x8f\xd9r[Q\x98\xebj.\xfd\xcbg,\xf5( \x8e\xfc3c\xff\xf30*Hf\xaa\x81\xe6\xa4~\x91d\xefp\x9e\x8b\xa2H\xf3\xc9\xbd{IL)^\x01=^\x98\xdc\xab*j\x86\xc5|\x97r\xfdB\xce\xca\xbcH\x96\xfar\x9eO\xf5uJX\xea\xaa\xe7A7\xa9N\xab.\xcfz\xf4\xac\xd4%\xbb\xaa\xea\x13\x92\xbe\x08\xe3\xf7a|\xa6\xaf\x94\xb1\xd6\x9e\xc7\x05\xc9f$-\x92\xacOc[\x7f\xc9\xb0\x97\xb2\x82f\xba\x19\xc9\xd3$\xce\xc9'\xea._$\x17\xe8\xd3M\x02\xbejj\x073\xa8q\xeb\xcb$ \xd1[\x12\x07$\xc3u\xb3\xc8\xa5\xbfL#\xa2\x83`\xe9+\x04\xe5\xe0\x19I\x8b\xc5\x04\xb4{R\xd7\xcf\x87|@\xa7ppY\x10<#\xb9~\x1fi\xbd\xa7\xc9r\x99\xc4\x83j\x97)\xc5\xc3$8,O\x97a\xc1\xa2M\xe4\x13\x98Zg\x04\xd5.i\xc9\xfeIr\xfc\x97e\xd1\xa5\xbf\x92\x94nU\x8e\xfa\x01\xe2\x07X\x89\xcb8\xad\"\xf3g\xc4\xd20\x9eiFrR\xd0>\"\x81\xb0u51C\x17\xad\xa9\xa9\x10\xc6a\x11\xfa\xd1!\xddX\xfd\xd1\x9a\xc7\x86c\x99,\xd3$\xa6|\xcb\xa4\xed<\x05jp\xa2\xfc?%\xd3\xe7^\xeag99D\xb9Y'M p\x82\x89x\x1c\x057\xf1:OF\xac)\xa5X?\xe5\xdd\xf8b\x8d\x1c\x9b\xdeq\x05\xd2\xde\xb1\xa2\xb7+\xed5\x91_\xe5\x05Y\xaa\xc8\x08\xf1T\xd8+\xf5\xf8\xcfU\x0eW\xb5M\xa9\xc7\xf7V\x03kl\x9b\xda\xb3\xd2\x8eJ\\\x1ff~U\xd4J=\xf6K\xdd\xb7x\xc4\x95\x90z\xec\x97\xb6\xb2f\xaeP\xdf\x98\xc6~X\x1d\xdd\xc5)\x1e\xbc]S\xaf\xcc\"\xfd84;\x01\xa9'C\x7f\x97@V\xc4&\xe8\xfb\xa4\xa2\xa7O)=\xdd\xaa\xdd\xfa\xbbEZ\xdb\xa7HRK\xfdS\x15\x9a\x078`\xb2\xdc#\xa5\xc0\x86\xb0\x073\xc7\x85\x13/'\x05\x1bCn\x97\x8e\x0b\x17\x02;=\xc1\x99\xe7^\x94\xf8\x01 0\x8fI\x9d=\x9d6\xb5\x16\xd3CE\x7fZ \xf2\x84\x16KQ\xb0\xe9BX\x8f\xb2\xc4y3^p\xd3\x85\xa4S\"%|ck$:.\xd3\xc0/\xc8\xbb,\xb2-\x0b\x07\xd6-|\x91\xf8A\x18\x9fQ\xe8/s\xdb\xca\xcb\x19\x06~\xd1\xd4>L\xc9\xcc\xa6\x83\xc8:\x83\xc0d)\xcdo\x82\xe4\"\xa6s\x07\x0c\xea\xc1g\xaa\x1d\"\xd6\xe8\xf4+\xda\xe0\xc5\xe8\x81#6\xc0\x81\x0b/C\xd2\xa7\xde\x14\x17\xac'i\xaa\x93\x97V\x91J\xb0\xfeI\xa8\x0d\xcd\x0f\x1c0s9\xb2\xc6\xdfK\x92] \xf8\xab\x9b\xd0\x8bR\xab\xe1\xe5bXj4\xc9\xa3\x89P\xe0\xc0T8\xbceL\x06\xd0x\x89`\xf7\xe1\x03\xf04\x1e\"k\xc7\xe1\xfb0MI\x00YM\x07\xc6 \xfc\x0bk\xe5_ \xc9\xf07\xfd\xf8_\xe0\xc2\xcf\x11\xed\x87\xf3\x90\x04\xbau\xe2x\xe8\xa2\x8b\x18\xba\xe7\xeb\x92bB\x0e\xf2L\xa6\xc8~\xbf\xcb\"\xa5\xac\x0d\xe5\x98\x8dM\xee\xbc\xa0G\x9b\x9d\xa8\xaf\xaf\xdeq\xb0Y3\xd6\xf8\xf0\xc1\xd8\x82\xe2\xfa\xc6K\xed\xb2;\x1d\nlo\xc92)\x08\xfb^M\x81\xab\xd8\x90\xd4\xeb\xbeU}\xa9`)\xe8\xa7\x9d\xd7M\x1c\xec\xc2\x01fb\xb0\x8d\xf3\xbc\xa4\xd5\\\xb8\xa0\x87\xf1@r\x03\xba\x96\x91,\xe9\xa5E\x1c2\xe1\xd8\xde\x19=\xe88\xf0\x8ev\x1c\x8f\x8b\xfd\xde\x93\xab|HC\xf5\xcau\xac\xa0\x99\xb6\xf5\xe1\xae4\xe1\xd8\x1e\xef\xdcwx\xbaM\x03\x95\xd1631\xbb\xed4\xb3s\x03\xacnX\"/C\xb3\xa3J8\x18\xdb;\x9d\xc0\xb0\xb5pq\xd2\x9fb\xb3\xb3\x03\xdc\x83\x1b\x1d\xbe[\xfbp\x7f\xdb\xf1\xe6rL\x94!-\x0e\x9cD{\x9bn7\x89\x9d1\xf3\x07\x1f\xdd\xe7~\xe4c\xeeW>\xbe\xaf\x04\xaf\xc3\xab\xe5i\x12\x0di\xbb\xd7J_\x9d\x8e\xb7\x13\n\x83G\xe9m\xe7\xb2\xe4\x913\xda[\xca\x83\xf4\xee\xb4\x83\xf1\xf2\x19\x8c\xb7\x1d\xef\xcf\x07\x7fk\x96\xb1\xd4\xa1;\xed\xf1\x88\xcc\xa1\xed\x011\x81\xf6\xc3vX\xa1\x94{\x87\xb4\x8d\x13x\xea\xd0\xb6O\xc2\xa2\x82\x94\xe6\xfbs\xfe^\x9d9tg\xdc\xae/2\x87\xb6'\xcc\xb2\x86n\xb5G\xc3R\x86\x8e\xdb\xb5Y\xc6\xd0N\xdc\x87\x0b\xbe\x9a\xed\xb9\x1e\xb0%h\x8f\xf1\x92Wo\xcf\xf5\x90\x8f\xbd]\xff)\x1bL'X\xca{\xb6\xe5\xed\xd7O\x04Bj\xbe~\x0d{\xf0\xb4\x9d$\xf4\x0d\xec\xc1\xfb\xf6\xcb#\xcc\xfb\xd9z\xf9\x12/\x08\x06\xd7\xcd\x92\xe7\xd5\xd5\xd1|\xff\x13\xec\xc1sJ.<\xafQz\xb3\x06\xbd`\x02\xdb:Y\x84A@\xe2\xb6\xca\xff-+-\x927Y\xb8\x0c\x99\xbfM\xb3\xc63\xd4\x03y)g(\x9f\xe7\x07q\xb9d!\x91\x9b\x15_\xd0\x1b\xd2\xb6r\x1c\xfd\x06c\x05\xb3\xabvs\xef\xe4Z\x9dd\xc6\x7fg\xa5I\xba\xa1\xa9\xf0\x0d\xecu\xb4I\xcd\x1a?\xeb\x02\xc2\xbcl\xd6\xfb\x1aW\xf4/\xac\xb1f\xd1\xf7\xb0\x07k_cf\x88\xaf\xa5\x8c/\xad\xbf\xbdy\x18\x07O\x17a\xd4R4|\x0b<\x82odvr\xe6w\xce}X\xdb\x83K\xfb\x0d\xf2fh\xd7\xab&\xd0\x87\xc5\xd8\x82\xba\xe17\xb2\xad\xb0Y*\xc2\x93,\xdf\xd7V\xbav\xbcn\xd0#P\x8aA\xae\x9dv\xddkG\x0eg\xa3\xb1]\x03 !\xbf\xb6\xbfQ\x9b\xd3d\x92\xac\xe2\x9biq\xec\xc2\x9b\xaa=\x1e\x10\x92 \xb7\xf9\x0d\xfd\xf9\x06\x9b\xe9\x04\xc0\xbf\x86 \xbcin\xd9\x0f\xbd|\xbb\xe0\xd9\xdf1\xaf\xf1K\xfbe\x0d\x08&\x1d%fL\xef\xaa'\x9b\xdd\x7f\x07{\xf032\xc5\x0c\xea\x1bP\xeb\x89\x9b\xbb\xb1\x88\x06\x80R4B:\x0b0\xa8\xa5F\x94\xfd\x97\xa6\x19\xfcm`l\x80\xaa\xe1=\xb1I\x7f\xb3\xff^m\xe0\x15\xcb\xe2\x02{p\xc13\xd6\xd1w\xb4$\xb1\xdf\xa1\x91\xc4>\xc6\xd7\xa9\x10\x10f\\\xa5\xfd\xbdby\x85\xa7\xaf\x8e\xa7\x053s\x11\xbf\xf7x\x0e\"\xdc\xb4Xw\x10\xea&)\x17\xb1\x89\x89\x8bT\x90\x0d\x93\xba\xc3\x0f\x1f\x18\xf4\xbdr\xe1\xc0\x1ea6uJ\xa6\xd4\xfd\xd2\xe1\x7f[\xad\x06\xfd\xb6\x86V\xd3b\xfey\x88q\xc8\x95\xd2\xf5\xad\xd6\xbc\xb3\xe0\x1fK\x9e\xe8\xb3\xa0CKXj+\x16e\x97IP\x98\x1fe\xf2\xc8\x81\xbf\xa1\xfe\x1d\xc3\x05&\x18\x06\xa60j\xdf\x8d)7\xfe4\xf88=k\x18\xaf\xe0\xc6\x13\x96\xaaP\xdb\xf3\x1a\xd6\xae\x01\x08A\x83\xe5\xf7\\K(0\x11f\xc1e\xaf\xd9\x05\xa2\xec\xda\x17\x9f\xff\xf9N\xfc\x16%\x0cz\xe8o\xbay\xe4\x18\x0b\xdbv4\xcd)~1d\x8f\x98\xdd\x05]\xff.\\\x0b)\x11\x89\xa9\x9e\x94\xff\xc8\x11{\x82\x87\xcd\x17\xb3\x8a9\x04\x7f#v+dSz7-\x0c\xe70l\xce\xaa\xae\xf73nmi\xdb/M\x81\x0d1\x08\x14=N2\xa2\xef&\xc4\xb0\x18IZ\x87{\x92\x92\xd0w\xf2b\x9c\xf3\x8cj\xa9\xca\xebw\xb3\xe1\xf5\xbb)\xf9\xe6\xbb\x9d)6\"B*\xaf\x13\xe0Y\xdajl\xc0SZ\xfe\x9d](\xcd\x03\xce\xfe\x9a\xbe:\x16\xf8\xc2\xae\x8f\xbc\xb8'\xbe\xad\x0d\xe9\x10\xa9\xab\xd2\x1d]+\xa5|H\xf2}O\xff\xf7-\xdd\xc3N.@\x18\x14I5\xa7T^\x8bXp\\\xf8\xa1\x99\xeeM\xce8h\x15I\xe5\xe3\xdd'\x04)0C\xdf\xfb?\xc8M?\xc5\xa4t_\xb8\x94E\x81=\xf8\x1bF\x90\xdby\xe8\xe0_\x87\xf8\xff\x7fF\xae|\xbc\xc3\xde\xfd\x89\xf1\xe8\xbb\xec\xaf\xbf\xf2\xfc\xc6k\x94\xdf\xdc\xc6e-\xe9\xfc-\x15\xc3`\xb9\xf4kD0\x0b\xfc\xbaWR\xf5\x83\x1d4$2t\xc4\xbe\xedc\xaa;\x1fS\xdd\xf9,[\xda\xcf\xed\xf5f ;\x91\xe8\x16Y\\V\x1d\xe7\xbfPva\xe1\xe7\xcf\xf9\x01p\xc3\xfci\x12\xcf\xfc\xe20\xcd\x88\x1f \x9b#(0\x17\x9d\x85\\n\xbd\xeb2\xd7\x0c\x97\x07\xe8u\xd1\xde\xd3\x958)W\xec\xcc\x91\x7f\xe6\x96q>KR\xda\\.LC-\xd7\xa2\x17\x01a8\xe2/\xf5!!\xe4\x91\x03\x81\xfd\x97)!\xcd\xb4\xe65\x12\"\x98\x8f*\xf0\xf2\"\xc9\xe8\xe5\x12\xf3V\nR7\x13\xd3f\xce\xed\x82L\xe3V;t\x05\x0f\x1bk\xc7Ox7B]\xbf\xfdG%;{Ao\xb5\xf5=\xb47\xdf\x87\x17\xf4TM\xd8?{\xdd\xe4\xea-\x04\xfc\x9e\\}\xd3\xdf\x15Z\xe0\x7f\x87\x16\xf8\xc6\x9c=>0\x1a\xb8\x83\x9b\xa0\x19<-\x8c\xe1\x85ZCA{z\x81t\xdc\x9e\x9c\xba\xc3H\xc6\x9799$\x05\xaa\xb1\x8d|\xda\xf7\xaa\xf0\xc0\x9d\x96\xc2e\x1a\x91!-5\x93\xcd^w\x8eJk\xa3\x19\xc3\xdb\x8dq\x84A\xd4\x07$+\xedZ%\x17\xb0\x0f\x976\xa6\xa5\xfc\xb3}\xc9h\x1d\xe3f\x07d\x1e\xc6D\xa8\xa8'\xf07CqH\xf2 \xfc\xb9Y\xe1\x8c\x14\x92\x8a\xfb\x19\xc9gY\xc8\xd4\n_\x98*\xbe\xf2\x97\xb4\xb1\x7f6\xd5a\xc7 \x9f\xc0_\x1b\xeb\x88\"\x96\xe6b\xdakx\xc5\x1a\x98|q\x11\xbel\xc7<\x16\x8c\xda4.\xa3\xe8\x18c\x99\xfdd\x0b\xba\xd3\xfa\xe5\x9a\xbf\xe9\xae\xbd\xdf1,m}\xc26\xb7\x851\x1d\x17\xac\xef\x0e_\xbfR\x04\x01\xa9\xb4\x0c+\x10?\x9cd#\xc7\x8c\xa3\x18=R\xc5\xe0\xa1,\x05\xa7\xc9\xea\xeb>ib!\xf1\xf0L\xde\x9c \x1a\x1d\xbb`\x9f\xda\x9d\xa4n\x9c\xc4\xffN\xf6\xbf9\xe3\xd5\xecb\x089.\xfaRJ\x87X\x987\xa44;\x06\xf5\x8eK\xfb-\x1c\x0d\x1a\x00\x0e$t\xect\x1a.\xfc\xc4\xb5*\xcf\xbb\xc2\x87\x06XIB\x84\xe9[$\xc6c{g\xd3\x91\x85\x0b.\xbcm\xd4cI\xb6^\xcf1_\xe8\xcb\x1aq\xb3\xbf\xfdb\xe1\x82E\xff\xb1\xf8=;\xe7j\xa6\x1a\x06\xd66\x07\xa9\x00j\xe9xG\xca)\xa2B\xa9\x93\xd8QBaU\xbd\x94\xe0\x073e\xda\xb7\x98\xc5\xe5\xed\x1a\xce(2HV\xa0\xea\xbb\\\x00O\xf1\x11\xed=\xf4\xe6,/\xcb\xe6#(kH\x8d\x1e9\x90W\x16\xe8\x94`/\xa7\x11\x12\xe5HN2\x10V\x1f`Ia\xb8\xda\x8av\x84\xdb\xc2\x9b\x90\x92]\xdd5\xfd\xe5\xda\x13\xa4D\xb3\x10\x83\x03\xd5\x86\x14\x02\x96/\xc28H.P\xc9\\\xfd\xe2BS\x05F\x84}C\xa1\xcdZ\xa0\xb8]v\x8b\xab\xb5\xa3\x83\xa88\x0c\x8akM\xd9H\xe1\x07l\xf2\x18G\\\xe58\xeb\x95n\xe9\x93\xd5T\x04\x88\xca\xda\xaa7\xf9\xbb\x18\"w\xf4Q4\xd1<\xc06\xcf\xbf\xdc\xd4\x14\x0e\x02\x00\xa6K\xb1-?\xbf\x8ag\xcfWR\xc8\x89OY\xfa\x12\xa4\xa5\x07}\xa7\xd6|\x15\xde\xe9UA^\xb0#0\xe4\\F\xdas\x89\xe9\xa5:%\x19\x96\xb4}:\xf9Ro\xd1\xdb\x13\x83/9p\x0f\xb6aC\xe2\xcd\xaf](\xbc\"\xf9\xfa\xaa <3\x9catm\x9e\xfd\xa4\xb0\xe7\xce1|\xf5\x15\x8c\x1e\xc0\x87N\x11\xac\xc3\x88\x17\x8f\xd5\xc5cV\xbc\xab.\xddr\xe8JL\xf3\xf5u\xbc\xa60\xb2\xf2.| \xe3\x9d\x9d\xf6\xfb\x07\x9d\xd7\xe3\x9d\x1d\xf8\x12Z\x89\xa4\xc6<\xc5\xb5\xb8:\xd5\x93\xd1\x0c\x96\xce\xe5\xf1c\xd8\xeev\xd2\xc2\xb6\xa3A\xbd\x8c6\x8dK\xb6\xad_\xb1\xc7\x8fa\xa6\x87wZ\xb0u\xfd\x12v\xb7\xe8\x0bko\xcfB)\xf7\x98\xb7\"\xf6\xcbf\xed\x8cq\x1f\x1e8\xb0\xaemx\xb4)Z\xa6\x80Q\xb5\xcc\xbb\x1aK]Y\xed\xa1\x0b)L7\xdc\xf4\xb5\x82\x7f\x16B\xc7D\x12>Ze\xcc8\x8f@N\x0f\xfb.\x8c\x8b\x07l\x1f\xf7\xe5?&,\x9f\x0b\xdb\x14\xeb\xc9\xd7O\x9f\x1d|\xf3\xa7o\x9f\x7f\xf7\xe7\x17/_\xbd~\xf3\x97\xb7\x87G\xef\xbe\xff\xe1\xaf\x7f\xfbg\xfft\x16\x90\xf9\xd9\"\xfc\xe9}\xb4\x8c\x93\xf4\xefY^\x94\xe7\x17\x97W?o\x8e\xc6[\xdb;\xbb\xf7\x1f<\\\xbfg\xf1h\xdc\x0c\x8f\xf8\x95t\xbe\x84\xaf \x7f\x04\xeb\xeb\xa5\x03\x19K\xc6\xedOK:\xf0\xa9/\x83r\xe9`,c\x95[[\xa4\xc7\xea\x02\xd8\xba\x84U\x01\xff\x01\xb6)\x1a\x13\x8c6E\x9e\\\x16\xf8\xc1vn\xc2\x84!f:^9mfw\x1df:\x8c_g\x8cB\xf7S9:z\xc1v \xa6\xff\xac\xef\xc1\x96\x83\x00c\x13\xba\x13\x14\xe5P\xec9\xda\xbd?\x1a\xed>\xd8d>\xf6\xd3\x92\x9e-\x06\xe9\x14\\w\xc6\xbc\x84\xa1\x0fV>>\xa6\xac\xb9\x80|;\xc4\x8cZ\x08\xff\x0f$\x98\x0f\xf1\xcd\xb8\xfdfWz\xb1\xbb\x05_B\xd8\xe6\xa9*\x8a\xa6{\x14\xaa_\xc9\xd4\xda\xb0d\x08\xdaD\x08\xda\x1dS\xd0\xb2NTE[JzC^\xcd\xc2\xcb\x88\x1f(T\x81<(\x8a\x02\x0cCW\x10\xea\x0f\xe0\x8f\x90PZ\x80b\x06\x85`\x94.\xfc\x88\xaek\xe9\xa8k\xa0\xbf>\xaeY\xb7\x8c^\xcb\x1b\xf7\xbb\xef\xd1~\x06\xf6\xb1\xe3\x11LT\x01\x0bR^e\x83\x96+\x9a\x0e\x10QR2a\xde\"w\xb8\xc3\xfe\xfa\x1e\xa4\x0c\xc3\x04\xf0%\x9f\xc3\xc6\x8cM\x02\x02x\xfcx\x0f6f\x94rX\xa7'\x18f\x18\xd8\x14\xeb\x8fwv\xe1\x8f\x10\"\xc2d\x1d\xb8 \xda\x9b\xc1\xc6\x1e\xcc_\xf9\xaf\xb8\x8c\xa7\xc0\xb6\x18x\xec\x83\x8dY\x04D1o\x92!\xef\x19j\xe9}\xd1\xd6R5\xcf?\x85\x0dX\x1c\xc3\x87=\x18\x8d\xe9\xc1:o\xddp7b\x8a\xb9\x10\xa4)\x9c\xb6\x0b\x17\xac\xda\xac\xb5#B\xe5\x96S\xb2\xb1\xab4bAj^)\xa3G$\xbcd\xac\x8c+\x81%[\xaa\xb8\x12X\xa2\x8a*A\x0b:_\xe4\xbc\xa0\x13l\x82\x99\x9a\x8e\xef\xb7U\xaf\xcc\xd6\xb4mf9\xc7ff\xad\xb7)o\\\x11\xe6\x82\xd9\x9a\xee\xec\xb6\x03]/\xaaO\x1e\xb6?\xe1\xf6\xa6\xe3v\xdfK1\xb7\xce\xac\x99\xc5\xa9&\xa0\xc3\xd5\xa7\x0f\xe8p:D\x1a&%\x1bm\x82\xca\x89IU_M\x8b(UA\x92t\x9e\xb15J\xe5{\xed\n\xb8\xd6\x88\x0d\xb4y\xdc\xd5\xcb\xab\x82\x7f\xb4\xdc\xc9\x84a\x8d\x8b\x05i\xbb@-p\xcb\xcd^\xc1\xbd\xce\xc5+\xb8\xcd\x9a\xbc\xe3L\xde\xc7\xd0\xf1@\xd6\xd7\xcb\x92\xa4x\x1eS\xd4\xd1S\x11\xe7\xfdF\xccN\xe1\xd4\x0c]M\x99xN\x932\x0e\x0e\xc5\xc45\x95\x8a$\x89N\x93K\x8d\xc34bz4\x00\xa8\\\x18\xe9\x1d\x81\x16\x01\xd5\x1b\xef4\x8c\x03\x1e\xf0\x87\x95\xa1\x82\x99\xdd<{p\xeaVn\xd63\x14r|w\xc8\xf6\x9ayUr\xe1[\xb3\x93\xfe\xb0\x85\xe2\xa9\x18s\xda\xfe\x99\xc7\xf6\xf9hQ\xc6\xef_\x86A\x10\x91\x0b?#\x8e\x1d;\x86\xc0i \x06\xf2\x12\xe1FNN\xde\x1e<{\xf7\xd7\x93g\x07\xdf\x1f\xbd~\xfd\xe2\xf0\xe4\xe0\xafG\x07\xaf\x0e\x9f\xbf~u\xf2\xf4\xf5\xcb7\xaf\x0f\x0fNNP\x87\xc7\xbcGsE$\x1c\x90\xc8\xc6M\x97\xd6D=\xe9!\xaa\xdd\xf9\x84\x12;b\xfa\x9ez\x98\\\xffS\xa5*wTf$6?\xaf\x8eXk\x0cO\xc2\xbdK\xd1\x1a\x05\xdfVN\xb5\xf8\x17?\x1e:\xadRk\xbce}$\x89\x0b\xd3\xee\xba\xbf'W\x13\xb0\xe8f\xd1\x19)\xdc\xa2\xf9\x05gTCC\xcb\xc2\x04a\xa6;\xdf\xe6\x90U\xe8\x81\x8dFLx\xc0hz}l\xd7\xd4\xa9\x07txp\xc4t\xb0\xf2\x0b=\xb0\xc9y\x80\x81\xd8&\xd0\x16\x0f\xe5}\x18t\x879\xa37\x1cJ\x91b\xc09\xfe\x1a\xc5JNC\xdb\xa8\x06KU\x9b\xdf\x94\xf1\xac\xf1-\xb1\x0b4\xa0\xd5y\xf9\xaa\x1aQ\x8c\xc0[\xfai-:\xd7jW\xe5\xa7\x1e@\xc7\xde\xb5\xfd\\;^F\x82rF\xec\x0b4\xa35\x0f\x957\xacA\xa0\xc0t4mTg\xeb\x02\x00^p\xfc\xc5qU\x8c,\x01\xb7\x06m\x1cH\x85\xfe\x03\x9a\xd7r\x1f\x00\x08\xfcF\x9b\xd6O\xf1\x9c\x07\x17U\xc0\xedX\x0b\xb7\xe3\xe6\xfd=>\xeeq\x0d\x07Nd&\xde\xc2\xcf_\xa0\xb7\xb6yD(T\xd0W\x19\n\xd3\xa8\x07T\xa9\xdf\x0b\xcf\x9f\x17${\xc1\x9d\xa7\x91\x83X\xdbt\xe1\xc0\x96J\x1cY3\x1f\x9bB:\x9a\xcf\x84\xdc\x0c?\x1e}\x1e\x12\xd52M\x14\xd9\x9f\xc5c\x82\xdc\xbb=`\xcd\x99dB\x18\xd1\x7f*\x07\xcd\x03\x00TY\x80\xeb\"\xfd4\x85\x95\x18\xb0z\xd3\xc5\xbb\xa1\xad\xf0\x18T\xba\xe3\xd13\x02\xceG\x16\x82K\xe2o\x06u\xfe|9\x81\xb9XZ}\xb5\xb7\xc4\x9f\x15\x93:H\xa2\x1as\nn\x8cqi\x12\xcf \x18\xc6\xe5\x96p\xce\xa7u{p\x92\x07\xa9\x8bX5xdw9\xb0\x01\xc2\x82!c\x87\xce\xf8\xbbo\x0c3\xcaW\x99\x91\x96\xb7Q\x0c\x14\xf6\x14q\xf7\x06\x0f\xab\x894\x07\x0c\xcdxE2b\xc4p\xef {(b`\x0bLmW\x97\x18\x9f\x99,.a\xbea\x8c|JN\x7fz\xe9\xa7\x0e\xbdA\xfa\x97\ndZ\x89\xf1\x18\x99fW\xb9\x87V+\xd6\x0f\xa9X\x93\x9a8\x1bB\xe6\xf7RH<\xc6-F\x82&\xd3\xf8x\x85H\xe0\x82\x10Y\x91\x0c\xe9J\xf8br\x013\xef\xa5\x9f\x9a\x19\x05\xe0\x84\x89\xcc\x15\xf7s\x93k\x99)\xc2\xb0\xfc\x08\x93\x80lZx\x94\x1d\x18\xd0x/\xa3\x0d\x12'u`\xc7\x8e\xc9_N~\xf8\x88\xab D \x97\x0c'\xc6/\xf5\xac(\xa8\xc4\xbe\xed\x07aO\x0d\x95\xc8\x0f\xbbm\xa8,\xe4\x08X\x9b.\x04\xde,Y\x9e\x86\xb18M\xb9\xc3r\xea\x9f\xf6&\xc97\xa3\xdf\xa3\xabt\x88L\xa8W\nC\xa6\x9b\xc7^\x91\xbcKS\x92=\xf5sb\xa3\x11P\x15+\xbeW\xec\x86\xa7\x9e\xcd\xcd\xb1\xf5H\xa2\x1aP\xacH\xe7!?\xe7<\xb6y\xac\xcc\xf8-\x1eTT;\xf28\x92&}\x9c\xc1:\xc5u\xa1\x9aU\xba\xcd\xa5L\xc9\x13A+\x0f\xd8\x80!\xb72\xdfN\xdb\xca\xab\x86o7@N\xef\xdfbx\x02\x915\xc7\xe7\xf3v\x07\x82\x05^\x06d\xc5\xcb\xa0\x03T\xc4`\xd6\xa2z\x1a\x02\x06\x8a^\x1c\x13\xa0\x14\x9dL\xe0\xf2\xa3a\xb5o ?j\xeel\xc0n\xf5\x9ef\xba]\xc3\x98\xd1\x06_\xa8\xf2W\x07\xdd\x86\xc6\xcd\xfd\xe8\xbfpi\xaf*\xac0\x8d\xeb\x0c\x0e\x1b\xf7\x9dc\xef\"\xf3S>\xa4\xdeK:\xe3\xf8U\x03h\x03\x04\xbe\xe2\x0e\xca\xa6q\xcf\xb5\xc6\xbbD\xe3K\x14\x10 A\x91\x9d0\x1f\x17\xb4UL\x8e\x1d\n]m\x9ad\xc8P@Z\xaa\xde\xa3\xd9~\xc4\xbd\x88\x87\xa3!\xaci\xa9:\x14Q\xc4t\x8fB\xbf\xd8~\x90\x90\x90\xcfY\xe6\xc8\x16\x89\x92\x87\xb2\xb4\xad\x10\x13\x12\xe4P$\x954\xaa\x96\xd2\x16\x0b\xbf\xe0\xafs\xf0\xb1\x91\xaa\xcc\x0e \x14\x0b\x02\x17\xec\xe4\x00CD\x8e\x0e\x11\xc9\x0f\xef\xe8\xc0\xcez$\xdd<\xf0\xe67\xbcO)\x88\x08\xbd\xafM$\x82\xb6\xf8n\xf1\xc4*\xd7\x8e Q\n\xa2\xce\x8c,\xb26\xb2\xa8%D\xfd\x01\x0e\x9a'S\xce\xa5\xa3J\xe7%?\xe2TN3 9<4)\x16A\xb87)qL\xc2\xd0J5\xf8^\xc4\x12v\x10K\xb1\xc2\xf0A\x16\xcaO\xb3a\x88\xc5\xef\"\x16\x9f!\x16\xb4x\xf5\x99M\xaa\x82\xd9\xe9\x1d\nH\x14\xd5\xca\x88\xa5\xb2\xbe\x0d\x15\x1c\x0d3Mb\x83\x0d\x1dn#\xcdlr\xc3GP\xae\xaf;h\x0e\xdd\xe0M\xca\x9e\xe5\x10\x8f@\xf1\xc8\xcf\x990\xda\x94\xcb\x8b\x9e\xc7v\xe2\x1cS\x8e{\xe6\x17\xb6\xaf \xad\xdb\xcfM\x10\\hBp\x02\xc0~?\x0c\x17\xf6\xa1\xb7\xc2\x80\xde\xd4<\x0e\x08\xf4\xa6a\x81n\x87\xdeP\xca7\x08\x99\x0d\x90\x94fM\x0b\x17\x15.X]^\xd0\x14\x08\x10\njL\xec\xad^\x0e\xf7v\xe2\xbe\xa6|\xfd\x1fg]\x06#\x16\xc1m\xb3C\xabr\x11\x15\xcf\xf5G\\\xe3o\xe2\x01K{c\x99\xe5\xc4+\x93\xc7z\xeaV\x83\x92\xaa\xb05<\xb6\xf9\xbe~\xf4\xd0\x96,\x8b\xb2[m\xce\x9d\xd2jJz\xaa\xd2\x98T\x14\x99\xb3\xa2\x84EEa\xf5RFz6\xb0\x97\xc1\xe1-\xf4\x1e/\xf9ix\x84u\xc9\x8f\xb0\"?2\xa7\x8a\xe6\xe4\xc3W\x90=\x02\x9f\x92\x1f\xe1\xd4o\x92\x1f\xfe\x00\xf2\xe3\x9c\xa7C=\xb0cAl`*$\x0d\xa9\x11\x1a\x93W\xf2\x87O^i\\\x81\x89(m\xd6c\xe9\xd8\x85\xcd\xa2\xca\x1b\xdb4X\xd7|\x14q\xc5] )\x08\xc6\xe6\xfa\xf0\xa1\xa3\xf1\x13jt\xf5R\xcah\xca\xab\x85[\xed\xc8\x1d\xe2Q\x9f\x18\x99\x84\x1f\x80nl4(<\x0d\xc5\xbc\x9ff\xc4\xa7\x07\xcd\xa9\x10\x17\x90\xc1\xa6 \xd2\xc6\xd7\xce\x8b\x85\x99\xcd\xe8k\x1a\xe4\xeb\xb4\xe8\xb3\xe1\x82\x017\x9b\xfc\x08\xe9\x1f\x05\xfd~\xf8\xd6\xbb\xff\xb7\x1f\x94(\xdeB*!\"\x06\x0cZ\x1e\xe0\x1d\x0e\xabI\x1f\xba5\x138\xf7^\x1d\xfcpr\xf4\xed\xdb\xd7?\xbc:9x\xfb\xb6_\x03#\x1e\xcc\x80\xa0\xcf\x92\xa5zR\xff*J\xfc\x80\xa5\xf8Y\xc8j\x84AM\x98\xb5\x1bX\x03\xe6a\xecG\xd1\xd0-\x12@\xd5[\xd9\xdc\xb5\xc9\x02\xb0p\xb42\xd7[b\xaa\x97~\xca(\xe8\xe4M\x96\xa4C\x90\xd5\x10\xf9\xb7\x11\xcf\xf4\xb6\x04M\xac\xd2\xb2\xe3!\x03H\x9a\xdb.\xc93\x8e^\x87\xaf\xca \x92q\xd8\xb2\x0c!\xee\xec\xa6\x87\x02\x8a\xe5\x0dVL\xc8\x81\xd5VG:P\xea[\xb6c\xfam\xf5\xea\xdaV:\xaa\\hCG\xddZ\xc5\xab2\x02-\xd4\x0d\x9b\xac\xa2\x1b\x0d\x8fT\xde!\x0dA\x860\x03\x95\xb4\"\x83\xea\xcbF\x9a\xcd\xea\x05\n\xd8j\x96\x04)\x9a\xd6\xd5\xd6\xaa2\x80Z\x15T*\x91\xc8r\xe6\x1a$\x91\xf0*\xf9\x1a\x067\xe8H\xe9\xf7\xc1n}\x89&\xb6\x9c\x8c\x9b\xc6\x14\x18x\xf4\xea\xf6`\xa7\xd91\x86\x95\xc1yu\x1b\x99&.\xc4\xc7\xc6\xaf\x9bp\xa7\xd0\x19\xb7\xbe\x91\x13\xfdk\x9a\xd5\xba\xee\xcb\x8c}w[\xdb\xbb\xaa\x8a\xa1Y;\xddC\x18\x9b]B\x98\xa261$\xe5ow\x18V\xa9\xa3\x1aoe\xd5\x8f6\xc2.\xc8\xb2\xd5a\xca\xa2j.%\x9d\x8b\xdfG6\x9c\xf3,K~\xaf\xa8\xb2 `9\x93\xd6\xd2O\xa7\xf9\xb1+$\x9fye\xb1\xde\xd8\x96\xee\x9bir\xac|)O\xb2\xb7\x02\xed\x13\xe3z\xf4Ub\xf3\x13\xb0\xdfW\xdd LU_\xf2}\x88W\x8d\xf4I#2\xa1*J\xc4\x81>Z\xc6\xaa\x9e$*\x9c\xe9xQr\x86\x02]\x850$\x96\x93\xa9\xef1Ij\xcb\xf7\xc3D\xec\x0b'F#\xb1\xa0'\xa3\xa5\xb0\x98*N8\xab8\xe1B\x84\x12\x7f\x04 |\x05\xc5#H('\x9cQ\xf8\x92W@wb\x05\x82GcpN\xa7\x13\x17\xa6\xf4\xba\xaf\x00&SY\xae\x0c\x8d\xe5\x85\x11C\x9a\x19\xc3\x08\xcfE\xd7\x036\xd7\x7f\xe8\xfe\x92\x13\x8d\x9f\xe0\xdb\xdeX];[c\x85\x17\xb0\x9c\x14\xa9.U\x07\xc8S{\xca \x9dE\xdbI\x99\xb4\xa3\xca_\x0f\x19g=\xae\xf1\xa64\xdc\xcc\xce0\xcce\xc6b\x86\xb2|7\xda\xb8\xa1\xedX\x9e\x98+\xc5\x9b\xd7#q\x86\x0c\x85.\xd9\xb6)\x87\x94\x9f\xe7\xe1Y<\xa4\xa9\xfeY\xe9'\xc3z\x99`\"\x98-g\xc59\x98\x93\x0c\xc9\xa7\xf2Z\xbd\xfb\xd9\xed{\xa1\xeb\xd8\xf6\x9ef\xb1\x055\xc1\x1a\xb7\xd4\xb9\x8cv\xb6\xdaYyJ\xcc\x1aP\\$O\xf8\x01\x7f\x93$\x11i\xa5{\xc3Yx\xf3\xa4\xccL\xb5\"\xd8\x83{?\xde[\xbfw\xa6\"\x86gZ\xbfi\xdb\xb2`\x1d\xd0\"\x13MG\xed\xc8\x05\xeb\x8b/\xefYf\x94>W\xca>Q\xd0C\xeb\xf0\xfc\x1c\xf4\xcfY\x12\x17\xe4\xb2`1<\xf9\x9b2\xa6\x7fo\x1a{Hu\xe7Ul\x0b\xc1\x9e\xba\x18_\xd0\x9e\xd8m\x0b\xd33_\x99\x84\x19\x0f\xb1\x81\xac\xaf\x9bg\x1aHaI\x94\xf3\xcdH\xce\xf0\x98\x98\xf1{r\xf5&#\xf3\xf0R\x9a3_\x94\xb8\xb3(\xd9J\x8b\xb2\xe8_\x146\x9c\xee\xb2\xf8XZ\x8d\xad[\xa14\xaci.\xafi\xb7\x98\x02_\xc9\xd66o\xadms\x03\x9a\xc4WD\xa9\xfbs\nq\x19\xaeo\xe8\x15\x0b\xbfx\xcb\xd4\xac\x02\xd8)\x05\xcf\x13\x9e\x02\xcb\xe1\x98xa\xfe\xbd\x1f\x85\xc1ADh\x0d\xda\x0e}\x1f1\xc6 Jb\xf2$\x0e\xde2x\xfe3\xb9\xa2\x1d\xf8\xb0\x0e\xf6ZD\xe7\xcf\xe2\x9e MF\xff\xa2T\x01{\xbf\x0f\x96\x05\x13\x98\xd9\xf8\xa7\x03\xeb`\xdd\xb3\x1c\x0cU\xe8\xb8\"\xf0n\xe4\x98\xc1\xe5\xdc\xee\x0f\xcf\x04{`Y\xcd\x85\x113dq\xb9h\x8d\x19e\xc0\xd9\x10\xba\x1c\x03\xdd\xab\x802\xd2\x88\n\x02\xbb\xc0([\xd8a\xb3\xb2O\x87\xb3p\xa1\xa4\\\x92\x97\x91\x88\xf89\xb1K\xf3\x1c\x96=We\xe3\xce\xaf\xef\xf4\xb9\x14P7 \"\x95\x81I\xcd\xd88\x1a(\xaco\x9d\x8e\xc6\xcb\xce\x01\xa1\x9b\xe2\x07\x01]\x830>;J\xec\xb9\x98\xe8\x8d\x06R\x1dd\xa9W\xf9,K\xaf\xefp\xcc\x81\x0by\x8b\xae9\xeb\xc8>\xe7Iv\xe0\xcf\x16\x93^b\x06\x84-7\xb3\xb5\x96\xa2\xac+\xec\xc5\xabk\xb4 I*\xb7f\x84\xa3\x94\x85\x84\x9aWp\xd4\x8e\xc3\xdc\xc4\x0cK?\xfdH\x03\x9e*\xa8`\xfe\x15\x9e\xbf\xcc\x15\xbb\xc0\x9c\x8f\x8diJ\x96~\xfa<.\x92\x1f\xc2b\xf1g\xb1\xdb\x98?5\xf6\xa3 \x9c7+\xe3\x8e\x0e\xd0\x00\xf2\xd1\xe0\xb2-\xd9h\x8ckU$\x88\x12\xfb$y\x82\x95\xe8[\x80B,\x80\x1a\xa5vRg\xd5\xf0\xa9\xa6\xa2\xce\xf0\xed-\xa9\xa8\xd1f\x9b.\xc2\xc0\x7f\xb1\xfd\xc0\xe9\xb34\x16)U<\x91R\x85B+g\xa3\x86H<\x9b\xdf\xa5I\xda\xa3\x83b\xa7\x17\xfdjY(\x16Epr\xdd\x06\xc4\xe4\x02\xbf\xef$gP\xd0\x8a\xe6Y7R\x85\xd1&1)\x8fm\x8dw0\xc7\x85\x84\xdb*\x1fN\xc5\xfaPv\x92\x16\xa5I\x12\x1d\x86?\xd7n\x9d\xcd5\xa1\x97\x9b9\x9d\x04\xa5 \x92.\x01\xdb\x1d\xb7\x8c\xdf\x06\x9c\x15\x90\xc5`\xc6m\x89\x1bc\xe61%\xe3\x1a{\x01g\xf0}\xfa\xb6\x9a/K\xc7T\xfd\xb9\x07#L\xc6$\xb0\x18\xec\xd1\xbbS\x91\x9bIAZ\xc6\xa4I\x83O\xda\x0bB\x9f\x0e=?p\x0dn\x02\xe4 \xad\xddJ\x80\x0e*`\x8fyl~\xd5r\x80\x12\xe6A\x05\xf7\x9dT\x15\xa0^\xceb\x91\x91\xce\x82\x0e\xb90\xe0\x96\xab\x95\xdd\xc9je\xae\xf0\xcb\xeb\\1\xe2\x19\xbe`\xcax\x1e\x8a5\xeb\xf2\x81\xdd%3\x98\x91\xdcf\xd5\x92;Y\xb5\xa4Z5FM\xa8\x9d\xc0VZ\xb8NB\x88n\x0b\x9a{\x8d\x99k|\xac{m\x9b\xa5Z\x1e\xef\xdeW\xc5\xa2\x8b\xed\x9d\xadv\"]\xbf\xbe\x10c{g\xbb\x13^\xaed\xe5\x0f\x1d\x17,\xaf\x9d\xc6\x95N\xc8\x9aX\x9ax\xc5\n\xc4#\x08-\x0c \xd2\xcdx\x80\xef\x05cB8\x8b\xe4{$\x9f\xf9)\xb1 c\x92&\x18Z\x9e\xe5Q\xb0\xb7v\xdb\xd22\xb8\x990\xae\xa2\x06y\xdc\xccj\"\x84\xc7w\x9a\xb90\xd7\x11H\xa9\x8bq\xf2\x84\xb9F\x1761_I#05\x86\x91\xfd\x12\xacSz\xa2\xfcX\xbc\x12YP\x90|sk\x07F\xbcd,\x16\xab\xd9\xc27X\xd7\x8a\xcb\xe5)\xc9\xe47\xf5\xaa\xf2.\n\xef\x8b/\xf8\xc8\xd0\x15\xb2\"wg\x94{)\\\xca\x83\xb2\x00\xcd\xfbP\xc2: \x05\xb2\x89L\xb0\xe3\xc2HM\x13/0\xc6\xa5\xf2\xc8\x9c#\xb3)59\x81\x18\xd6A\xa1y\xa1\xab\xd2\xe4\xcf\x0b\x8d\x06\xa1\x92j/\x99\xc4zII\x8c*\xbc\xf6r}\xdd\x81\x05\xac\xef\x01\xb1S\xba\x0f\xd3\xe5\xb1\x0b\xe78\x97\xd4\x85\xa5\xc3w\xaf;\x02Ml[\x90\xd8\xa2P\x99\x8d\x10\xf8\xf0\xcf\xfaP\xd8\x95\x8b\xd1\x04\xcf8m\xd7\x13Z\xe6\x0c\xc1\xa0\xf0H\\d!\xe91s\xa9\x16\xe5\x84-\xca\x9a}\x05{p\xea\xc5\xe4\xb2\xb0\x1d\xc7\x0b\x12L\x1d&-\xcc\x15K;#\xad\xcd\xc9\xfa\xba~u\xc4CW\xa9\x7f$\xda\x01\xe8\x17H\x91i\xd2\x8e\xe1\xae\xcdSU(\x92P\xdd\xc1\xca4\xc7\xca\x0e\xc2P\x0e_\x0d\xc6\xd6\x9e5\x01koS\x03\xc1\xd6\x04\x8b\xc7V\x17J\xb4\xf2\x02\xeb\x0b\n\x93\x1d5\xc0\xbd\xe9\xde\xe4\xf8\xdeY\x1fc.5TL\xc9q\xb7_#GY\xc6w\xb3(\x9b8m\xdd\xa2\xec\x8di\xf1d\x95Ea\xcba[\x1e;\xccd\xba\x89\x1az\xbaV\xeco\xd4D\x13//O\x19\x15`\x8f\xd1\x97Pz1r\x1ci5\xed\xbd\xcd\x0f{c\xe7\xee\x17\xb4\x86W\xf5\xd9\xb9\x13\xfd\xd7\xfd]\x87\xc7\xe8\xfc\xc6\x9f\x15Iv\xd5=\xc5\n)\xc0\x84\xa2H\xbfM\xa5b\xd1\xe9i\xc6JOO3e\x85 \xc8H\x9e\xb3:\xec\xb7\xb2ZFx/\x19Qw\x94\x15\xe1,\"\xbc\x0e\xfeVV\xcb\xc3\x80W\xa2\xbf\x94U\xca LX\x15\xfaKU\xe5\x14\x8bO\x95E~\xce\xda\xa7?\x94\x15\x82\x90\x95\x07\xa1\xba8\xe1\xc5\xea\x9e\xc33V\x1c\x9e)\x8b\xa3d\xf6\xfe\xefeR\xf01T\x7f*+'\xc1\x15\xab\x96\x04W\xca\nl\xeb\xd4\x1bwZ\x16E\x12\xb3\n\xf8SUi\xe6\xc7\xe7>\xdb\\\xf6S])\xa5\xe0\xcak\xe1oe\xb5\x90\xcf\x8a\xfePVH\xf8\xd6\xd2\x1f\xea\n\x11/\x8f4\xc5gYR\xa6\xa2\x0e\xfe\xa1\xaa\x18\xf8\x05\x03F\xfaCW!\n\xf3\xa2\xaaD\xffPV\x0cX\x95@YH\xd8p\x03\xa2\x1cn@\n?\x8cr^\x05\x7f+\xab\xcd\xd9\xca\x06s\xe5\xaa\x06\xa1\x1f%\x0c\xa6\xd8Ou\xa5s^\xe3\\Y\xcc\xc7\xa9\x1e&_\x05\xe5\xfc\xc9\x12\x0b\xc9R]xJ\x02^~J\x94K4\x0fI\x14`\xd2\xe7\xcc\xb6\xc4\x1f\xea\x8ag2\x98\xd5\x7fj*\x97\x19\x11\x15\xcbL L\xf3$\xc1\\\xb5\xff\x1f{o\xda\x1d7\x92$\x08\xbe\xdd\x8f\xf5+\x9c\xf1\xaa% \x03\x0c1H\x89\x94B\xa2\xd8J%\xb3[\xdd\x99\x92FRVMw0\x8a Fx0PB\x00Q8xdQ\xef\xf5\xcc\xec\xdc\xf7\xee\\=\xf7\xd9\xb3;\xf7\xb1\xc7\xec\xce\xf4\xf4\x87\xce\xfc#\xf3\x07\xf6/\xecs3w\xc0\x017\x07\x10$\x95U\xbbo\xf1\x81D\xf8\x05wssss3s3Q\x08^\xe9B\xc9R\x16I\xc81.\x86\x90\xbd\x18\x92\x99\xdb\x98\xb9Mf\xee`\xe6\x0e\x99y\x1f3\xef\x93\x99\x0f0\xf3\x01\x99\xb9\x8b\x99\xbbd&\xf7qB\xc4\x8b\xad\x80\x04\n\xbe\x92\x85\xcaU\xb6\xb0\xae\xb1\x85l\x85n![\"\xca\x89\x17\xaa\x00\x92X\x92\xc0\x06\xf3\xc4_\xe2\xe4\xe2+Yh\x89K\"X\x92\xeb!\x88V9\xe2\x1c\xbc\xd1ERY\x80\\\x95\xefO\x10\x90\xefOH8\xbe\xe7\x97\xa7\x1cQ\x15_\xa9B\xa1\x7f\")\x04\xbc\x91E\xf8)\x8f\xf0K\xf8J\x16Bh\x85$\xb8\xc2 z/\xb3\xa3\xf7T\x81\xa5\x1f`G\xc5\x0b]`%\xf3\xc9\x89^\xfa\xc9{\x99\x9f\xd0\x1f\xe0Q\x8e\x05x\x94\xdb\n\x04\x99$%\xea\x07]P\xd2m\xf1b) \xb1\x17\xde\xa8\"\x91\x8f\xa40\xf2IR\x18\xc5\x18M\x19\xcb\xc8\x1fTA<0B1y\xac\xa5\n\xe1\xf4\xd2\xdbU\xbc\xca\xca\x85\xa4~X\n*\xba\x17[i^\x9cg\n\xa7\xf1\x95*\x84\xdf\"?\xb2\xf2\x13\x1fg\x00\xde\xc8\"\xc14StU\xbe\x93\xc5T\x11[v|Zp\x8c\xea\x07U\xf0gP\xe2gTV\x82\x03I\xc8\x91$\x08\x85\x84\x84@\x92\x9f \xcf$^\xa8\x02\xd8/\xb2C\xa9\xbf\xc4\xef\x8a\x17\xb2@\x89:v\xc4I\xf9\xb4\x98N\xf9N\x17\x0b\x15~\xe1+Yh\xe9\x87\x88b\xf0F\x16\x89\xf3d\x8a\x13\x82\xafd\xa1\x95/;\xb4\xf2\xe9\xdedI\x1c!I\xc5W\xba\xd0\xa5d\xe0\xe1\x8d,\x92#\xeb\x9d\xe6$\xf3\x9d\xe6\xcb\xa5\x9f\\\xca\"\xf0N\x17\x93\xf3@\xaf\x97\xcc?\x91\xfd\xc80R,Q\xa4\xe0\x9d3\x1b\xf3\x9c!\xd9\xcdH\x92\x9b\xf1\x8b\xac8\xd2\xa8\x1fdA\xc1[`)\xf1F\x16Y`\xfe\x82\xceT[vf\xdb\xb3\xb3@n\x87\xe2\x85.\x90)x\x887\xb2\x08R\xcd\x8c$\x99Y\xe2O\xdf\xcb|\x7fJ\xd2x$\xf0$u\xcf\x11As\x12;\xcf|\xfc\xf0\x99O~\xf9,\x98qW\xfc\xfa\x9c$\x11<\x0c\x83\x95<@\xcaw\xaa\x18\xae$\x9a5Y\xfa\xa7\x92\xbb\x11oT\x910\x88\xb0\x84x\xb1\x15\xf0\x93_K\xfcY\xc0\xa3\xac(Z&Q\x95\x96~\xaa\xf6\xf1\x94\x9c\xe3\x95\x82\xd0\xca\x02\x9d\x95\x9fe<\x89T\x19\xf1N\x16\x8b\xc3\xcbSI\x00\xe5\xbb\xadX1R\xf5\x83*(\xc6\xe4\x87\x95\xd1V\x93\xc8J\x8a\xb8&6\xd2\x9a\xc5\x92\xc8d1M\xec\xcf$=<#\xe7Q\x10\x85\x82:\x90\x05\n\xa2\x9b!\xd5\xad\x94\xb0\xc8\x88P\x05{\x0b2\xa2\xaa]f\xb5w2\x1a\xfb\xae\x1e|\xac\xd2 eMv\xc3~\x18\xc6\xd7\xf8\xe1\xba\xe95j`)\xfdk\xe4\x0c\xeb\xe1\xb5r\xd9\xf7zq\xb4\xa8\x7fp\xff\xbeeL\x8df\x1f\xcal\xe3&\xf2s&\x8doi\x19\xba\xfa\xcaT\x94x\xf2\xc4\x8f\xe2\xe8r\x19\xe7\xe9\xd3\xa7\x84\xa8tn\x95\xaf\xfah\x99v\xe6\xf4\xe0\x8dB;\x06\x82#\xc1\x98\x9e9\x85\x12\xd5RN\x0c\x17\xca\x15\xe3\xb6\x14Dm*\x14\x95\x8aUKA\xc55\x9f5q\xcd\x0c\x19\x8e@0\x1cg\x8eR\xde\xda\n\x02\xd0\xb1 \xbc\xda\n\xfa\xd1\xe5\x88-\x9cD7\xb3{ \xdab;(_\xcd\xdb\xe4\xdd\xeaQ\x9a\x9c\xaa\x7f\x1fk|\xcc\xfaS\xd3wh\xb7\x9a\\\xdd\x94b\xe6\xf4\xd4U\x13\xf6u\x8f\xf5!8j\xefk\x16\xcf\xcbx]\x98\x91`\xc6\xc2OY \x03\x16\x8b\x9a\xef.W\x9cEq\xe6\x83\x8a>\x88\xd2`\xc6\xd5P\x07m~\xb0\xce\xe4\xbd\xc0\xac\xd5\x99#\xdcn\xad;[k\x83\x01\x93\x9f\x00+F\xc7\xef\xee\xf4CBF\x05f\x16\xc3\x8f\xc5\xf0\xeb \x12 \xc5\xb4\x14\xd3\xd2|\xb5\n\x03>cY\xacC\xcdc\xfcb\xc5\xa7\x19\x9f1?B\xe8\x0c\x08g\xb1\xfa\xd3|Q\xbfP8\x87\xa8p\x0e\xd9\x13-\xc8u\xd8\xefw\x05\x0d\xdc\xd6p|\x8f\x85\x05f\x89\x1e\x8fE\xdfC\xf16\xe9y,\xef\x0091AS\xddf\x11.\xe5\x95\x16\x0e7\x18,ey^\x7fl>T\xe8\xa5\xc8q\x93\xea\xe0Q\x80\xdd|%\xae\x89\xe4|\x0d\xc4\xce?>b\xe7\x9d\x11\x9b\xa5At\x1ar\x8c\xbf \xd9\x80\x9ba\xf9M&\xde\x16^Ja\xe8\xf7J\x887\x1cp\xba\xa6\xad\x0e\xdey\x8e\xf1\xeeN\xe4/\xc1\x98\x95\xb8\x9fC=y\xab}\xb1\xedA\x1c\x1cL\xe3\xa8\xb8;qu\xc5\xaa)\xd0\x9bri\xb7c\x9fz\x94\xd1\x99\xd1X\xa7\x16>\x00\x14\x7f)\x90]\xcd\xa4\xa8\x0e%|(\xf1\x8bCw\x0b\x17\x05\xfa\xafk\x12\xb9\xc6\xbbL\xf5\x07\xd0f\xe9\xf0q6q\xeb\x0c\x86>\x01I9\x01\xb1\x05\xd8\x91IY\x80\xa4\xbc\x8cg\xbc\x95\xa3\xb8 \x0cm$\x03\xf9\xca\xef\x95`\xfc\xc2875\xd6V@\xeb\xbbZ;M\xea\xc6\x81UL\xba6*\xf1\xec\xd7_\xcb\xebpd\xf8\xcd\xd61k\\\x17\xf8\xa5h\x1d\xb6\x18\x90?X\xf8\xe9\xab\xf3\xa8\xb8[\x1ev\"\xfd\xac\x99A\x1b\x00\x83\xd6\x8d5c7e\xcf\xd8/\x80t\xc5\xd1\x1a[4q:\xd0<\xe5\x18\x07\xb4\x06\xbb\xbe\x9b-\xdd\x02A\x8a\x95\xa1{X\xe6\x05\x83\x9e\xeb\x17\x8fm\x8f\x18\xd4J\xcc<\x07\x7f\x1e:\x8c\xdb\x97\xa6Xp\xbf\xf1\xf6\xd5\xcb\x01\x9eu\x83\xf9\xa55\\\x80z\xd6\\i`\x1f\xaao~\x1d\x96Z\x1c\xc1\x8eY,\xcf\xa6\xfd\xf2\x1a\xe8\xf2\xee\xb2\xdd\x9cL=\xb7\x862\x157\x1f[\x8fYV\x99\xe9\xac\xfd(\xa6dAb\xef\xec@\x1f\xa9\x9d!*:\x1e8\x1bC\x8f\x15\xb3\xa7\x9c\x87T\xe6\xa6\x80\xd5\x80\x1d\xd6\x8f\xa5\xb0},\xf8\xf4}\x01\xc6\xd4c'y\xc6\x12>\xe5\xc1\x19\x9f\xb1_I\x99\x9f\xb1 \x9a\xf1\x0b\xf6+\xe9\xa0\xe7\xb1\x13\xf4\xed\x05\xf7\xa4k`\xb3\xcf\xee\xf7\xb2\x04\xa5o\xd1r:\xfc\xf6\xe9`\xda\n\xe2\x9d\xbc\x8f\xeaWX\xd3jo\x05\x81v;QG\xd6\x99\xc6vY\x9f\x96\xa5x{\xeb-]t0\xddT\xcf\x0d\xa7\xf4\xff;\xac\xc6\xd7\xf8\xc5\xaf\xd7\xe44:\x1d\xe0\nfa\x1cv\xc4\xd9i\x97f\x99lz\x0en n\x85\x0f\x99\x17\xa0\x9e\xb7\xd6i^\x12\xdd\x16\xcc\xed1%\xfc\x02BK~oX\x9fv\xc6\xfa\x10\xb0\xbe\xee`\xae\xfe\x18X\x1f\xde\x00\xeb\xc3[\xc7z\x85\xc2>:\x93\x04\xfe\xa9\x8dk)V\xca\\\xac\x94N(-J\xaf`\xa5\xcc;\xae\x94\x8d\xd5zpz\xcf\xe5\x99l\xdeL\x8e\x8f\xa2O\xfdY\xa1\xc2\x10\x195\x9e\x0da\x80\xd7\xf9{L^\x139\x8a@\xd3\x06\xb7J\xc8Z\xfa%\x13\xe5\xa7K\xd6\xef\xb0L\xcf\xe4\xa5\xb2\x95\x93zln\xae\xf6y\xb7\xd5.\xe0\xb6(\xc0\xb6\xf8\x05\xadc#\xf5\x83vE\x92\x99>\x87(\xfcQR+y\xfd\xef\xa0pR\x7fu\xc5\x86\xec\x1ed\xc0K\xc6F\x8c\xc3\x85I\xb8\xed\x07\x0cZ\xa5\xb5\x0f\x96o\xcfhJ\x02\x17g\x97J\"\x81\xe8\x84\xe2=\xf0\xd8\x1c`\x92\xa37\x1ep\xb1\x13#+\xfa\xdc\x0f\xc3 :-D\x0e)\x83\x95\x03\x8e\xb9\xd9,H\xf84\x0b/Y\x90\xb2(F65N\x04\xd18\xb9\x84\xc0*_\xaf\x92x\xb5)\x88N\xfa5[\xf9\xd3\xf7\xfe)\x1f\xb0\xafR\xce\xbe.\x1a\x1c\x00\xc3Z\xfct\xdc\xaf\xc5:\x9b\xfaa(\x9aX\x0e\xd8\x1b\xee\xcf\xd82N\xb8\xe0\\\x17Y\xb6\x1a\xdd\xbb7?\x19,\xf9\xbd<\xe5\x9bP{\xb3\xfc\x8eu\x91hx(f<\x19\x07\x13v\x007+\x8b\xcb\xa1*\x0d\x89\xc4\xbb\x05/\xcf:\x15\xa2\x19\xa4`\xe5(\x18\xef\x94%\xfcgy\x90\x80TQ?O!\xdf\x1dd\xa9$\x067b\xdc\xa9\xe0H\xdb\xa5k\xa6+\xe61\xbc3\x92\xa1\x0d*\xb4^\xba\xd6B\x1co\x10\xd7\xdd\xd5#\xc6\x10c,\x91\xa4\xdbm\xee\xa4v\x9b\xbb\x8b\x10\xe11\xdb\x80\x10\x91A\xed\x16ucMV\xeaBb\xbcB\xadM\xe4\xd0\x0e\x9a5nvS}\xea\xc8\xf5\x82\x17\x9f\xae7\xbbAx-\xf0cc\xe9\xf8\xe3\xe1\xa4\xd3@X\x17\xd9\x8e\x0d\xa3\xa5[\xd8\xf6\x05k~\xbf\xeeu\x96&s\xa7\xcdWL\x95\x9e\xc5\xba?\xd5\xe5\x85\xec\x80I\xbb(\xe0\xfc4\xf1\xfa\x1b~zx\xb1*\xef\x81\xf7XGG@\xf2K\xca\xf4\x08\xaf\x9c\x82;\x89\xb7ZJ6\xee\xfd\xea\xaf*\xd7\x1b\xef\xfc\xd3\x1e,\xe0\x16k\xb2L\xef &\x9bpD\xa7W\xa2\xe3\xaa\x07\xf58r6\xe0^\xda\xddwiN\x98a,\x05\xb5+UZx\x07\xd9\x84\xbc\x9a\x9bSR~m8\x01ht\xb0T\x99\xa1\xcf\xfcL\xfb\xfa\xcc\xcfx\x8f\xc6J\xa3&\xcemY7\xe1\xa7\xfcbE\\1\xb6\xa1Q7x\x9e4#+-\xd0/v\xec\xe6\xad\x1a\x91\xb6i\x1bn\xdd\xf6\xd4\xe8\xfd\x088\x9b\xc6=\xb4y+\xc620\x03M\x05$\x98;\xf4\xa8\xa9C]iL\x9b\xd3\xb7\xea/YIs>\xc9\xf6Q\xc5V\xa6xl^;\xa9\xb0}\xc1J\xcf\x07z\xc2\xdc\xd3\xa4b7\xf0C\xd0\xe4x\xa7P\xe9\xdfR\xfb\xbd\xe1\x83\xc1\xee@z\x1e\xb8Vkg\xa5\x8f\xe9\xdd\xfb\xee\xa0\x88\x98@Y\xf3\xb6\x19\x1b\x07\xb2\x9d\x07\xa4}\xef\x83\xfb{\x16\x83]\xdfQ\x92\xb9\xdb\x18\x87aG\x8c\x9d\x1fn\xd3n\xa3\xeb&\xca\xa2\xb3\xbdep\x11Di\xc7I\xad/xuf\x19\x13\xd2\xc3\xd4j\xef\x8b\x9f\x1c\xb1\xdeg\x87\x9f\xbfxyx\xfc\xe5\xb3\x97\xbfe\xf1\xad\x90f~\x16L\xbb\x95])\x0c\xefTZ\xfaS]\xa3\xc2\"\x08g\xcf\xd7\xadu\xca\xb3\xcf\x90\x1a@\x84\x9dj\x9d\xe3/\x0f\xdf\xfc\xda\xe1g\xf6\xaa/\xa2 \x0b\xfc\x10\"\x17\xadY\xf5\xb9\xd6\xddu\xaa&<\x82\xbb\xb4\xaa\xc6\xab\x97\xcf\x0f\xad \x94+\xe8\xc7A\x18~\x89\x8eK;\x80\xa4\xa8\xf6Y0\xbbF-\xf1\xb17\xa8($@j\xc3\xa3E\x9c\x0bp\xc86\xbeZ\xcd*\x10\xed:\xc8z\xbd.\xfd\xfd,\x98]\xa7\x1a|.Zv\x86\xcfW/\xdf>\xfb\xfc\xf0\xf8\x9asB\xd5^\x1b\xc8T#k\x0c=\x87\xa2\xc5\x1c\x8dX\xef\xd5\x8f\x0e\xdf\xbcy\xf1\xd9\xe1\xf1\xa7\xcf\xde\x1e\x12\xbc\x8f\xd9Nh%:\xb0\x10\x93\xe0\x8c\xcf`5}\x9e\xc4\xcb\x86\x15\xd9\xe5[S\xeb\xb7fA\xba\n\xfd\xcb\x97p\xe3\xbb\x13G\xce\x80\xf0j\xf5X]\xac\xab\x1e\x8b\xd6H\xd1\xd4\xce_\x13\x1cgK(\xb9B\xed\x11\xa1\x9a;\xaa\xb8a\x8b\xfa}W\n\xb4\xc7\xd1d-\x15\x17AJ;\xf7\x9b\x0f\x8c\xda\xe2\x88.C\xa6\x19y\xa4\xabP\xd6\xd0\xb5k\xf7\xca\xd2\xa1\x1b\xf4\xc5\xd8;\xd6\xe8N\xad.8\x13\xaa\xa7\xed\xb3\x85c\xa4B\xcb#\xb2\xf4Z\x08\xa9\xed\xc6kt{\xa5q\xa9\n\x84E\xda\xba\xf0+\x98\x87\xce\x1d\xd8\xe8^\x94u[C\xac\xba\x8e\x82\xa8\xbdU\xf5(>\xaf\xdd\xa6_=\xd0\x9f\xba)`\xd4\xd9\x14\x90)\xb1\x97\xe0\x16A\xd3\xd9\xed\xb3\xe2 \x9c\x8d\xd8cw\xc1\x88\xf6y\xe8\xa7\xe9\x88\xfdV\x9c3\x1f\xf4!\x19_\xae\xb2 :eY,C\xcf0\x9f%<\xe5\xc9\x19\x9f\x01\xa6\x88\x9ez\xec\xeb_I\xbf\xf60\x16>n\xd8\xd1\xd1\xdd\x8c\x9dp\x06\x11\xf2A\xb4\x0b3\xdac\xef\xf9\xe5\x80}\x86M\x05\x19\xf3S\xe6G\xa5\xc1\xb4j\x11R\xb8?{,\xca\x9c\x07a\xc8\xd2L\xfc=\xe1\xcc\x9fNy\x9a\x06'a\xd1\xb8n.~\x97vRo{\x94\xd8\x0b\x80\xd6A\xea\xa5\x1e\x90~\xad3;L\xe3\xb9Cs\xa2\xd9\x01\x0b\xc7\xd1D\xca\xe9\xbb\xf7\x83\x95\xa7\xcb\xc0\xa1\xb6C\x10{\xe4\x1e\xebu\x9e_1\x95\x02\xb2\x97q\x9eh\xb6\xc2\xa0 \xcb\x16~\xc4\xe2h\xca\x07\xec\xdd\"H\x05\xe4\xe7a0\xcd\xd8\xd2\xbf\x14s3\xcb\xb9h\xc9\xc7Mm\xd0C\x07\xc8gq0s8\xc6\x95_\xc0\x8b\xc7\xa8\x80S\xb6\xa7Y\xff\xab?\xf2#\xb4\xc7\xe5\xfa\xd3\xde\xac\xbd\xc4\x07\xa42\xeb\xd04?\xcf\xe2\x93 \x9aU-\xee\xd7PA\xd3\x81u\x98f#\x98\xd6\x11+\x13\x88\x95\x8e3;b\x9d\x10U\xee\xdc\x11\xc8Te\xe1\xd0Ml\x05\x8f \x12\xc2\xdc\x9fr\x1bB\xc5g`\x87Q\x9a#\x86eXj\xc9\xb3ENDg\x9f\xe5Y\xfci\x10\xcd^\xfbAb\x89TY\x8dR\x19\xd5\x97\x99\x0f\xcbl:@\xee\x1f\xa6T\xbe\xbb\xa4\xbfw\xf5\xc0\x1c\xd7\x1bC\xbb\x8a\x1cC\"\xb6\xedJg\xf2^h4\xce;X\x8e\xad`\xd8\xc6\xf7\xda\xf5\x80sg\x85!w\xa6fm\x97M\xc7\xf9D\x0c:li\xa9\xc1\xef\xb3\xfe\x881\xcd(\x02\xd8\xd6S\xd6d7\x0d\xc6+\xe0\xac{\x05\xb7\xdc\x86H*\x06\x8a\x92w\xdb\xc1\xc0P\xbfmR\xf4\xe7L\xba\xcfN[\x03\x96\xeaO\xe0\x80\x13q;\x13\xb0\xac\x13@\x99\\_\x81_E\x85\x11\x81 \xd1l\x15\x87\xc1\xf4\x92\xfdJ\n(\xfd\x9e\xc3\xeb\xf9\x82G\xb8\x02O\x81\xdd,\x96\xa6\xa8\x02\xc4x\x89\xb3\xdf\xd0\x9d\x03\x96`\xe4\xd2\x85#^\x042\xb0\x11\xd5C\xf4\xe0\x8be\xcf\x8a\xb2\xdd\xa0/\xddA\xcb\xda\x1d8+(\x1ec\xd0\x93\\|\xc7+*7\xd6m\xe0\x15\xcc-\xbe\x13\xa1\x9fY\xf7\xfb\xea\xb1$p\xa4AY\x83\xaf~\"=\xf3Xo\xc9\x93S\xaeB\x1c\xbd\x8c?\xcbW\xa1\xd8\x90\xf9o\xf2\xcb\xd4qG\xec\xb9\x1f\x89m\x17\x8a\xb1(\x8e6\xb1\x99\x14\x08x\xe62\xe2\xc8\x82Q\xca*:=`\xf8Z\xbf\xf5.\x91\x06-\xf8\xb5\xec<\x96\xf4;\xc5\xed^p\xfa\xa9\xbf\xe4\x18\x06]l\xbd\x9dv\xd6\xc7\x02D+\xf0\xf0*\xf6\x044\x92SE\xa7~\x9eJk\xb2\xf3\xb8.\xb6u\\\xb1\xc5\xd5\x0e\xd3\x8e\xab8\x0e\xc9w\x8b\x15P\xe9\xa7\xd8\x1c\x17\"\xf5=\xbfL\x15\x0b,\x19S\xcb\x0dUeB\xd8 -\x16m\x96\x88:{i\xdd\xf70\xb04F\x83\x15\x10\xf1\xcaH\xb2\x96{\x8e\xe2\x81C\xad\xa5\x96]=\xaaL\xe2\xca{(I{\xe1\xd2\xd6#\xb2\xef\xde\xe0^\x98\xf0\xd5\xcc4\xa5\x9b\x13\xe3\x14\xc0\x0b\x1dV\xa4\xdbz<\xbb1\xe0\xad\x00\xb7\x02\xf5\x9a]]\xb6\x1e\x1524\x9e\xa3\x94\xc4\n\xec\xb5/\xd5[1C\xd1\xa9\x87P\x13\xb4\x82\x86)\x83\xd6\xe3\xe3 \x85J`\xe3\xb7\xb1E\x96&H\xaa\x89\xb4\x97\xed\x1d\xac\x88\xea\xaf\xddG\xda\xde\xa5S\x1fO\xac}\x94\xfe\xc1\xa5\x02\xa9\xb3p\x0b\xfa\x87\xf2\xf8d\xc0\xa3\x9f\xe5<\xe7o\xb4\xa6$\x86\xad}z-\x06\xdc\x11N\xca\x16g\xa3\x0e\xb0\xeb\xc3\xea\xd8\x1e\xd6\x97iF\xa2\xce\xb1\xaeT\xd7y{vB\x90\xb6\x12\xb2M\xe42\xab\xa9T\x93\x06sPV\xa2\x89yXP\x91\xd7\xee\xdc\xe9\xf0e\xf5T.\x11r\xb2]\xcf\"\xeag\xfd}\xb6\xdd\xd6>\xab\xc9,\xdb\x8f\x05L\x9e\x88\xb2q\xc4\xfal\xd8\x81O\x85\xe0\x0b\xfbH\x99\xe2\xeb\xfaA\xf8\x00\xe8\xab\"\xda\xad\xa4t\x9b[C\xe7&|\x0e\x0e\xc4\xbc\xca\xbaP6\xeaQi1\x9fq\x19\xcb\xc7>\x90\xc2\xcaWT\xa9\xb1\n\xec\x80Lv\xdcV\x81^\xe0\x10\xacY\x0evuUs2`\xa6\x7f\x85\xf8\xc4\x88-\xc5\xc9W\xa2\x7fq]]\xf0.\xe2\xd3=\xb1\xb9\xe8\xea)q\n@~_P\xc14\xd0\x14w=\xb7\x06\x91\x9c^\xad-'\xde\x04\x84\xe5\x15c\x97\x88\x9f\xb3cOO\xac\xf8\x10\xc1h\xc8Z&\x85\xe22\xa8_>\x90!O\x9d\x95n\x00\x9e\xb9\xae\xc7VN\xe6\xb1S\xf5\xc2\xd5\xcb%\xec\xb0u\xb5\x08\\EP\xc1\xe6\x0bMI\xbd\x98\xe3\x82\xacB\xef\x1c*\xda=\xd6\xc3\xc0\x07pnr\x06\x83\x81`\x98M\xd1\x16NO\xb0\\\xa15\n\xf3\xd9\xd7\xd8\xc0\xd7\x92\x93\x04f:u\xf5\xf1\xcb@%N-I\x86\x9bj\xe4w\x9a,\x93n`\xd0s\xd6\x12\xd3\x0c\x0co\xca\xe2\x91cs\xe6g\xa7zr\x00F\x0cg\xee\xca\xe0\x96\xc3\xfb;\x10\xdd\xf2v\xc7\xb3\xbdG\xdb\xe2)\x1b\x00\xb1\xd5\xc5.Ek\xfd\x12*5Z\x0b\xc1X\x1f\xeby\x96#$\x8f\xf2%O\xd0\x01\xfe\x86%\xd0\xe8)\xef*]Q[\xf3\x80\x96\xb5\x13b\x82\xc6\xbe\x07\xdf{\xbf\x83[\xe9\xb7D\x93\x8e\x9d'\x1b\xcf\xea\x08\xc4\xf6\xd9\xd0Bv\x18uz\xb8\xc1\xfao\xa3E\x80\xb7\x9e\x14A\xe3M\xa3*\xca\x927\x95\xe0&\xf5 >Iyr&\x86.\xce\xdcp\x0bXK\x1a\xc9\xa0\xbc\xe2P\xad\x12{\x10\xd1]+\xb4\x8fvr\x19:\xc7\xd6\n\x92;\xf0\xf7\x02\x91\x8a\x80\xc7\xf0\xcf\x00Bn\xa4\x98[\x8fYP\x11\xf0\x04\xb4\xcb\xa2\xb3\xc2)N@\xc8f\xb6<\x1a\xc4|\xecO\xf0\xe2\xa7xA\x07G\xb6\xbd\x8ai\"\x11\xbd\xc7u\xeb\xab-\x93\xd8\xa6\x16F\x8a\xe6\xbc6:\x08\xca\xaa +\x04\x04E\xc5F\x91\xe9\x99\xe6a\xabY\xf2\x85\x07C\xec\xbamm\xeaO\x06\x1e\xc7\x04;\xfb\xe2\xe5\x8bw\x8d\xc5?\xb4\\Q\xd5No\xb1\xcb\xb2E\x12\x9f\x83P\x05n\x119w\xdf\xf0Y>\xe5 \xeb\xdde}\x96\x81\x1b\x90\x9e\xc4`>c\xc5V\xc9fy\x82*[\x90 \x05\xdfH\xe3\x9b\x17sT\xaf\x81\xd8g\xe5\xa7)j\xe2DZ\"[\x0e\xd2\xb2\x19\x8f]\xc69\xca5\xf8\xc5*\x0c\xa6A\x16^\x16\x0bf\xc1U\xfb\xd8\xe0\x80\xbd\xab'\x81\xfe-\x8a\xc1B\xb0h\x15\xba!\x1a\x9e\xc5\xd1\xdd\x8c\x9d\xfbQ&:\x91\xf2\x8c\xf9\xd2\x01\x81X'\xa0\xbf\x93\xbd\xc2\x8eL\xfd\x08\x0c?\x80\xb9\x91\x86\x83,\x9ek-7\xb9\x96\x11\xd3\x1f -\x10\xad^\xdc{\xfd\xe6\xd5\xa7\x87\xc7_\xbd\xfc\xcd\x97\xaf~\xfc\xf2\xf8\xd9\xf3w/^\xbd<\xee\xb1>\xfb\xd2\xcf\x16\x83\xc4\x8ff\xf1\xd2q+\xa1\xcd\xb5\xe0\x9e{\xee ]\x85A\xe6\xf4z*\x80o\xe3\xe7k\x93\xdb\x15\xbd\x10\xb5\xe8\xed\x86\x01>\xdd\x00K@\xbb\xbfJ\xe2\x13\xf1\x1ed\x0b\xe63\x1c6|v\xc0>\x83 \x12\xcb5\x8b\xd9\xc2\x8ff!z\x99P\x98\xce\xfa\xec.\x8b\x13\x16g\x0b\x9e0\x1f\xd6 \x88\x18z\x08\xe1Ozh\xd6\xb5\xf2\xd1<\x8a_\x82\x8d\xd54\x06/\xa3 X\x96\x06g\x80:\x85yO\x81q\x1a\x9aM\xf3$\x01\xa3\x03\xc0)\x81\x1c~t\xc9\xf2\xe8}\x14\x9fG\xea\xbb\x1e\xcb\xa3\x90\xa7)\x0b\xb2\x1a\x12\x07\x11;_\x04\xd3\x05\xde \xa4>PAZ\x8f%\xfc\xd4Of\xd0X\x8c+\x06\xbf!\xc1\xd2\x0d\xcd\xd1\xa9\x86\xc0\xd9\x13D\xd9\xc1]\x8b&\x86\xd0\xfe95\xd3\xa0\xca\x01\xd3(\x0e\xc2\xf1\x06\xfa\xddEo)\x96\x87\xd83\x0b\x9d\xa4\xd2`\xc6\xb2\x12\x14\xc9\x80\x8f\xb2\xf8*/\xbd\xbc\x88\xceb4\xdcz\xed'>\x84u\xff\xb2\xf0\xb1\x9b\x15\xac\x84\xf4\xf4@\x124\xf0\x16$\xb6\xae]\x97\xd8\xbbD\xd6\x83]#+(\xb2\xf6\\\xf2X\xeb[\x95\xba\xd2v\xa4\xb2\xfey\xf3\xfa\xb7\x1e\xc0\xb5\x05_\x1bj\xa2\xe6\xd8[\x0bd\xb1^\x8d\x82\xff/1\xe9\x15\xbds\x04\xe5%\xa61P3L\xcdU\xf0}\xcf\x15E\x9c\xed\x8e\x9f\x82\x1a\x89\xa6\x0e\xb5\x1b\x81\xa4\xb9\xa5'\xbb\xb7Y\x9cp6\x8b9zc^\xf8g\x1c%\xf3\xc1L\xc9\x1c\x06\xecK\xff=g\xf2*//#\x8c\x94J\x85\xfa\xe6\x1b\xa4\xday\xf7|\x11\xa7\x1c\xa7&\x05\x99\xb0l7\x1d\x10\xc1k}I'\x0b\x14s\x0d\xed\x13\xba\x0d-\xb6\x84\x17\x19\xaaM\x07A\xaa^\xf5\xb8.\x85\xbbd\x1f$\xd8A\x8aB\x91\xe2\\\x9e\xd5\xa2\xa2\xa8\xc1e18&\x88*\x81\xdf^,\x979\xc4\x83/\xbeZ\xdec\x9a\xc7a\x18\x9f\x07\xd1\xa9rx\x10\x80S\xaa\xbb\xac\xcf\x02T\x1a\xdc\xedy\xacw\x17eL\x83\xbb\xe6\xd8\xe1\xc0%f\xef-\xff\x19(#\xf0\\\xe8\x0e\xe6A\x98\xf1\xa4\xe5\xa8 \xc7\xbba\xdc\xdf\xaa\x1da\xeaZ)Y/\xd7e\xc0\x07\xac\xa7]\x19\x04\x81\x04^\x94,J\x1d\xb0\x9e\xf2\xeb\xd0c\xa3\xe2G\xc0S\x14\x97\xe1\xc0ss\xe0l\x1e\xe7\x118\xa5\xbe\xab&E\x03\x7f\x16\xb3y\x10\x15a\x83\x04\\Q\xf0\xaf\xe4_\x853 \xbcC.\xc5\x1a\x0dp\xd6\xef>\x96\x9dD\xff\x13'\\J\xeaf\x83\xbbuw\xca\xb7\xbf\x1b\xde\x1aE\xf3\xd6\"\x0euo\x9c]tH\xa4d\x13UH\xa0\x1a\x12X\xaed\xa7\x97+)\x0bEQ\xe7\xad\xc8?\xeb\x02(M\xb6y+\x13\xa4W\xacB\xab\xa0\xd0b\xd7\xae\x07\x00/\xe7\xa9:#]>\x199\x8fP\xc4\xfd\xe8\xa1[\xedy\xe4<\xd8\xdb\xead\xe0Y\x1e\xa1\x87\x86\xafC\xe9l\xf0\x91\xeb\xf4\x8a\xd8\xe0\xa4\xad\xf3\xde\x96\xc5\x8a;r\x86\x0f\\\x8d\x8a\xaeq*\xb0\x1d\x084ER6\x8e\xd1c\xad\x16\xbb\x1c\xee\x14@4\x81:\xcdJ\x1c]~\xd7 \xc0\xcdV\x86\xf7~\xe2\xfc\xca\xf6\xd6\xd5Q\xea~\xe2\xfc\xd4?\xf3\xd3i\x12\xac\xb2\xab\x99\x9f\xf9\xee\xbd`i\xc2\xf2\xde\xf8'G\x17\xdb[\x9bG\x17{\x87\x93{\xa7\xf5\"\x01\xb69\xfe\xc9h\xd2wG\xf7N\x97\xe6qk\xdc\x1b\x08Bt\xaf7\xa1\xe1]\x05h\xeaGA\x16|\xc3\xbfJ\xc26a\xd5\x99\xb4\xb5\xf1\xe4\x8e!\xaf\x95\x89cA\x8fRKw\x12\x10j\x05\xfd\x010\xec\xaf\xe6\x0e\x1foM\\\xf6\x94m\x12\xee\x97\x9d\xdc\x95&\xe7N\x04\x12\xc0\xa5\x9fM\x17N\xe0\x8ad4\xd9\x11\x873\x96\x0c2\x9ef\xe8\xb6\xa4\xe7\x9f\xc4y6: \xfd\xe8\xbd\xd86r\xb8\x1d\xae'V\xbe\xb3\xa6\x15e\xb9<\x1e\xd8\xec\xff\x1f\x0e]#\xdci\xc3f\n.\xa2\x07Y\xfcE|\xce\x93\xe7~\xca\x1dpG\x02\xfa\xa3\x03&\x90\x94\x8d\x0c\x1f\x1f\x96\xe5\x15\xaf7\x84]\xca\x9e>r\xb6\x1f\xda\x96\xaf}z\x95\xb0\xdbI\x1c\xeeVG\xb3\xe6\x1a+\xbb\xb7W\x17]|/\xa6\xe4`H\xdelF\xde\x0d$g\xff\xbf1y1\xc7\xf5 \x8e\xba\xd9\x8cw\x03t!d\xb9\x96\xe5\xb8\xbe\xa2)\x84\x13\xeb\xc1r\xa3g\x8f\xf2\xaf\x0b\xcb\xea\x9aCh\x96\xf5\x80\xc5\x03\x19\x94@\x814F\x12\x18 \xd1\x90\xe2y\xa34\x93\xa8\x0e\x96\x91hd\x91\x0d\xa6\x0b?y\x969[\x16%L*\xcb'N\xe4\xb1\xa1\xb2P\x82\x08!\xd9 \x0d\x83)w\x1a\"\xb0\xe4c>\x01\xc5wU\xd8\x7fm\xda\xbb\xfd\xb0\x1d\xc4\xf6cl\x0c;\x9a\x14\xdf\x93\x98T,2\xe9\x02\xea\x80\xc5\x82w\xf7\xd8\x06\x98\x01D\xec\xe9>\x8b\x95Ux\xf1\xa9\xeb\x8e\xe6\xc1^\x9d l\xc1\xbb\x9b\xd0g\x8e\x08\x02\x97\xb4\x92\xf6\xc5b\xe3h[\xbf\xc4Ks\xb65>\xa1\x10\xb97>:\xcag\x0f\xb7\xb66\xc5\xff\xf9|^\xbf\xf4\x96\xa8B[;Xhkgw~t\x94\xcf\xf96\xfc\x9c\xf3m\xf1s{k\x06?\xb7\xb7\xcc&\xe0\xc6\x00|fg:\xc6\xcf\x9c\xd8>\x07\x86~\xe3\x9f\xb4t\n.\xf49\x07#\xbd\xd1\x19\xdf\x85\xe2\xb3\xf9|\xe2\xfe|\xfb\x03y\xc5Oo\xf7d>\x9f@\xc2\xd4\xfe\xa1T~\xa8\x08\xe1sU\x84\x01r\xc5[\xef\xa0V!T\x9f\x99\xf3-\x8e\xff\xe6\x93\x03\x15\xe1\xc9\x91\x9d\xde\xde\xda\x9a\xc9V\xc7\x18\x93)\x9f\xc8\x95~\x85A\xe2\\k\x1b=\xf7\x93\xfaY`\xaa\xf5r\x1c\xa8\xae\x1e\xf4\xf0\x1a<(\x08\xa3z\xfb\xb5~\xcf\xd9\xbe\x0c\x8c\xe0\xc0\xe8\x9c\x83\xfdr\xa40\xe8)F\x8a\xec\x9d\xf6\xae\xbb&\xb8\xe4*\xe7p_t<\xb9\xee2\xde~hc\x08m\xcb\x98\xf2%/G\xdb\x1b\xdf\xfdo\xbf\xf3\xbb\x93\xde\x8dF\xd6\xbc\x9d\xa8\xdd\xdd \x1c\xb1o\x14,\xbe\x0f,\xbe\x0b\xce\x1ez\xbd\x1b\xdd9\xd2h\x9c\x058\x06\x0b\n\x87\x9e\xf1\xd1\xc5T\x1c\x8bf\xbbG\x17\xb3\x87\x9bG\x17\xf3\xdd\xa3\x8b9\xbc\xcc\x8f\xf2\xad\xa1X\x19\xf9\xd6po>\xb9w\xda\x00\xc2u\xc9\xc3M`\xed\x80\xd0\x1a\xa4\x82 \xa9U\xd0\x0c<\x96\xd4a{} \xdew\x9d\xea\xd7{\x7f\xf8;\xbd\x11\xeb=\xab\xad\x9b\xde\x1f\xfe1:\xf9\x8f\xd3\xc9\x7f\x82N\xfe\x1f\xe8\xe4?I'\xffC\x91\xec\x1b\xc9\xff\x88N\xfe\xc7t\xf2?\xa1\x93\xff)\x9d\xfc\xcf\xe8\xe4?-\x92\x9f\x1b\xc9\xff\\$O\x8d\xe4\xbf\"\x92\xeb\xde\xf1{\x7f\xf8\xefD\xf2\xccH\xfe3\"\xb9\xee;\xbe\xf7\x87\x7f\x96N\xfest\xf2\x9f\xa7\x93\xffg\x91\xcc\x8d\xe4\xff\x85N\xfe\x17t\xf2\xbf\xa4\x93\xff\x82H~a$\xffE:\xf9/\xd1\xc9\x7f\x99N\xfeW\"90\x92\xff5\x9d\xfco\xe8\xe4\x7fK'\xffU\x91\xfc\xd2H\xfe\xf7\"92\x92\xffG\x91\xfc\xcaH\xfe\x9f\xe8\xe4\xbfF'\xffu:\xf9o\xd0\xc9\x7f\x8bN\xfe\x0f\"96\x92\xff#\x9d\xfc\xbf\xd2\xc9\xff\x1b\x9d\xfc\xbf\xd3\xc9\xff\x89N\xfe]\x91\xfc\x95\x91\xfc\xb7\xe9\xe4\xbfC'\xff]:\xf9\xff\x14\xc9\xb9\x91\xfc\x7f\xd1\xc9\xff\x99N\xfe/t\xf2\xdf\x13\xc9\xf5\xd8\x01\xbd?\xfc}\x91|i$\xff\x01\x9d\xfc\xa7D\xf23s9\xfc\x9eH\xf7\xcd\xf4\xbf/\xd2\xdf-\x8c\xf4\xff*\xd233\xfd\x1f\x88\xf44\xad\xa7\x7fK\x93\xe5oi\xfa\xfb-Mh\xbf\x05\"n\x90\xb7o\xff\x04\x9d\xfc'\xe9d\x80\x80A\x0c\xbf\xfd3t\xf2\x9f\xa3\x93\xff\x02\x9d\x0c\x84\xd6\xa0\xa8\xdf\xfeY:\xf9\xcf\xd3\xc9\x7f\x91N\x06\x12d\x90\xe5oij\xfd-P&\x83Z\x7f\xfbW\xe9d \x13\x06\xfd\xfd\xf6\xaf\xd1\xc9\x7f\x83N\xfe[t\xf2\xdf\xa6\x93\x81\x04\x19\xf8\xf6\xed_\xa7\x93\xff&\x9d\xfc\xbbt\xf2\xdf\xa1\x93a\xcd\xfe\x9a\x91\xfc\xf7\xe9\xe4\x7fH'\xffc:\x19\x16\xe7\xa9\x91\xfc\x0f\xe8\xe4\x7fD'\xff\x13:\x196\xfb_7\x92\x7f\x8fN\x06\x1e\xc0X\x98\xdf\xfes:\x19\xb6Xc\x07\xfb\xf6_\xd0\xc9\xff\x8aN\xfe7t\xf2\xbf\xa3\x93a\xfb66\xb6o\xff%\x9dLo\x9a\xdf\xd2\xbb\xe3\xb7\xff\x9eN\x86\xed\xe47\x8cd\xd8N~j$\xc3v\xf2\x9bF\xf2\xff!\x92\xdf\x1b\xc9\xff\x89N\x86\x9d\xe0\x0b#\xf9?\xd3\xc9\xbfO'\xff\x01\x99\xfc\xdd\x1f\xa3K\xc3.\x13\x1a\xc9\xff\x85N\xfe\xafd\xf2w\xbfC'\xffq:\x19H\xaf\xc1\x8d|\xf7'\xe9\xe4?M'\xff9:\x196\x01\x83\xa5\xf9\xeeO\xd1\xc9\x7f\x86N\xfe\xf3t2\xd0o\x83I\xf9\xee/\xd1\xc9\x7f\x85N\x06Bm\xf0\x17\xdf\xfde:\xf9\xaf\xd2\xc9@c\xdf\x18\xc9\x7f\x83N\xfe[t2P\xcd\xc4H\xfe\x9bt\xf2\xef\xd2\xc9@\xa8\xdf\x1a\xc9\x7f\x97N\xfe\xfbt\xf2?\xa4\x93\x81\"\x1b\\\xc1w\x7f\x8fN\xfe\x07t\xf2?\xa2\x93\x81\"\xbf3\x92\xff)\x9d\xfc{t2\x90\xde\xccH\xfegt\xf2?\xa7\x93\x81\x98\x1aL\xe1w\xff\x82N\xfeWt\xf2\xbf\xa1\x93\xff\x1d\x9d\xfc\x1f\xe8d\xa0\xb1\x06\x0b\xf9\xdd\xbf\xa4\x93\xff5\x9d\xfco\xe9\xe4\x7fO'\xffG:\x19H\xef\x8f\x8dd \xbd\xe7F2\x90^\x83\xc7\xfd\x0eH\xaf\xc1\xcc~\xf7\x9f\xe8\xd2@z\x7f\xdbH\xfe\xcft\xf2\xef\xd3\xc9@L\xbf1\x92\xff\x0b\x9d\xfc_\xc9\xe4oav^\x98\x1b\x0f\xc0*0v\x9e\xef\xf0\xb8fp.\xdf\x01\xb3\x14\x9b\xe9\xc0X\xde5\xc9\x1b\xec\x1bi\xa9\xd9\xb5)Hi\x8f>\xd7\x16rw\x12\xb0\x11\xce\xd4F`\xa3[\xa9p\x03\xc9Z=\xf6\xa3\x12;R\x96\xdf\x84\xc4M\x9am?l\xf7\xbcG\xabT\n\x0b\xc5}\xd0+x\xba\xea\x04u\xf4\xfa\xc0AA%\xd5\x10~\xa9\x86\x80\x00T(\x87\xcd\xba\xc9a)\xb5\x01\x18Tlmm\x1e]l\xcf\x8f.v\xfc\xcd\xa3\x8b\xfb[G\x17\x0fN6\x8f.v\xb7\x8e.\xf6\xc4\xcb\xde|\xd2\xbfw]%\xa3\xeadt\x93N\xfa\x9b\xdfL\xc6\xcf6\x7f{r\x05\x7f\x7f\xbe\xed}\x80\xb4\xab\xf1\xd6\xe6\xa3\x89x\xc5L\xf9\x02\xa9W\xe3\x9f\xe0\xcf\xad\xcdGlr\xef\x9a\xdd\x8f\xd0Pb-\xb5O\xa1\x939:\xba\xf0\xa7GG\x17'\xc3\xa3\xa3\x8b\xd9\xde\xd1\xd1\xc5\\\xfc\x01\x01\xab\x008B\x1c@\x8e0\x07\xa0#\xd4\x8f.NP\xe0\xba%\x05\xae\xbbsvt\x94\x89\xea'GG\xa2\xae\xbf\x05r\xd9\xf9\xfc\xe8(::J\xa0\xd0\xf6C\xfc\xf7\xe8\xe8(\x1f\xee>\x14%\x86\x0fA\xf9 \x1a\xc2\x7fC\xfc\xb7\x8d\xffv\xf0\xdf}\xfc\xf7\x00\xff\xed\xe2\xbf=\xfc\x87mn=\xc2\x7f>~\x01;\xf7@\xfc\xdb\xd9\xda\xda\xaa\x11\x18\xd46\xf5X\x9fE\xac\xcfz\x16M\xd2\xac\xdf3\x17\x1cH\xa1\xb7\xf7\xe4\xb0\xf7Nh\xa5\x91\x98j\x01\xd4\xb9\x80\xd4|\xf7\x08\xa5\xddG\x17\xa6\xea''5Q\xaak\xa0\x18\xa9}\xd0\xda\xf4\xb3\xcd\xdf>BA;H\xdaQ\xd4~t1\xe36u\xd3\x1az\xad\xf0Zz-\xd0\x18\x8d;\xf7k\xae)\x98\xfcB\x0d\x96S\x8a\xa4\x95Vt\xda\\t&\x8b\xae\xa9>\xb8\xb2\xa9\x12\xdd\xba2naU\xc6\xcd,\xca8R\xf5\xc8R\x8f\x85\x9d\xf4s3Z?wV\xd1\xcf\xd1\xed\x89\xbc\xda}\xcbe\xa9b\x19OQ\xa3\xa7\xe0\xdf\x17`\x03\xc5\x95s0\x9a]\x85\xe1\xd5\xf2*\xe1W\xe9Uvu\xc6]\xf7@\xaa\xef\xc6\x89\xc7\xa6\x1e\xeb\xfd\xb0g\xaa\xff\xd8\xcah\xe8\xb3\xab/\xbe\xb8\xfa\xf2\xea\xcd\xe1\xd5\xdb\xabwW?:\xac5\xc4\xfalnk\xac\xec\xdf\xbcK\xffT\x8d\xb6\xcf\xf79\xc0\x1d\xeb\x87\xd7\xa6\xec\x1b\xce\x06\xd8t \xea\xa6l\x10\xc0\x14\x97\x1d\xb0\x15\x18A#\xe3\xef\x17\x0eG\xd9Z\xa8S\xdc\xb5~d\xbdk}o\xfc\x93\xc1\xa4\xff\xc3{\x03~\xc1\xa7N,z\x10\xc35\xb1\xf2m\xf0\xe2\xf0\xf8\xf5\x9bW\xef^\x81\x91~\x0f\xac\xb8{\xe8\xc8\xd1I\x93\xa9{<\x1c\xa0E\xd3\x88\xf5z\xd7\x85\xc4F >\x18@`\xd6k\x8c\x14\x91~\xcf\x1d\xf7\x8e\x8f\xa7q\xc27\x7f\x9a\x1e\xa7\x0b?\xe1\xb3\xe3c\x9b\x95\xfdu\xa5\nv\xdf6\xed2\x83\xf6s[7\xb0\xa9\xad\x01\x88\xcb\xc2\x87\xcd\xe3\xce\x1de\xde[!JcN{\x05)\xe9\xd2\xe6>\xcb\xd8\x01\x1b\xb2\x11l\xda\xd7\x05\xbf\xa0\x9e\xc4 \xeb\xf88\x8cg~\xba8\x16{\xfdqqg\xe8\xf8\x988v\xb5\xb8OX\x17\xb9*PR\xf0\xa8\x02#\x983\xc7pZ\xcc\xb4\xf3sf\xc0\x8fULN\xf7\xd1\xa6\xb4\x98\xee\xa6@J\xb2VPx\x15\x86\x95.\xbeP\xd8\xfd\xde.\xf0\xbf\x7fx\x16\xc6\xe7\x07\xd5+>0\xc4X\x1b\xf8\xed\x0e\xb4\x01\xcb\xda\x06\xd9\xe4=\xacu\x9c\xe5\"\xeaW\x17#rdC\x8fEb\xe8\xfbh\x8d\xaf\x89\xd82i\x9d\x9c!\x83pS\x02\xd1\xc6\x96\x8c'\xb7\xc4\x88\x0cw(\xf6\x18\x83\xd7h\xcc\xd8*\x0c\xa6\xbc\x0d\xf2\x9d\xd0\x8bf}\x13D\"rN6\x9c\x88=A\xc7\x11N\x04\x9e\xa0\xd4\xd5\xd4M6\x14\xebm\xb0\x8a\xd1WD\x89\x8f`\x1e\xef\xb1\xcd\xcd\x02H\x1e\xdb\xba\xd6\x9e[@\xe9\x174z\x1c\xbb.\xba\x1dG\x93\xf1\xb0m\x0b\xba\xd5\xa1\x146\xaa\xd5\xb1\x08rW\xb91\xf6\x11\xba\xd2u5\x9b\x80\x8d\x01\xb0\x91\x15\xb0\xb1\x04\xac\xd3\xefkH\x12a\xec\xd0\xb1\xf8\xf0\xc4\x85\x08P\xe3X\xc0[F9j_\xdb\x0d\xc3\xddn\x1d\xae\x0d\x89\x12\x15\xf9\xcd\x95G+\xdb-\xa1\xebr\x01\xad\x14\xc9\x8e\xdf\xd2S\x1d\xd9\x9d\x1e\x9e\xe8\xd1\x81\x1b\xf0\x9bQ\xbe<\xe1\x89\x96\x90\x02\xe7\xa9%\x9c\xc4q\xc8}\xe9\xf4M\xf0\xa6\xc7\xc7@\x89\x8e\x8f{2\x10\xc0Hs\xce\xf7}\xceFe\x1d\xc0d\x9c\xf2\x0eb\xfc\x8f\xdc\x07\xdc\xa1>f\x1f\x1a\x16a\xd9\x0fz\x05F\x80\x8c4e\x03\xc1\x034\xeeU7\xdeHnk\xc8\x8a\xc9\x8d\xf7fK\x8f\xb6{7\xae\x8eI\xe5\xdc\xfdV\x90X\xa6\xa5(\x80{\x10\xe9u\xef\xac\xe2w\x9d\xbcI\x06\x8e/b's\xa9\xfa\xaa\x8dT\x11\xb8\x1d\xa2\x05&o\xaa\x05\xe0{(j\xec\xbb\xfe\xc8q\xa4N>\xe6\x13\xb8|\x90wu3k\xa6\x9cI\x8f\xbc\xbc\x00\x87\x95\xf3\x0ea'a\x07,\x1f\xa7\xc0C\x87\x82\xc1\x0c F\x9a\xb1\x1bH\x03w\x87\xf5[ \xf2\x02\x84!`AL\xd8~\xd4*A\xb2\x12\xc6\xd8F\xa3\x87\x15&\xe6\xce\x1d\x96\x8d\xb7&\xe3\xed \xde\x19\x14\xef[\x82\xbd\x13/\xc3\x89\xd8\x82\x8ao5\xdd`\x8e\xa4\x13Q\x88\xb6\x16QAB\xaf\x0d\xb5\xa1qwF]\x8d\xa3\xa064%U\xdbm0\xc4\xaf\x0bd#\x80\x99\x02\x1d\x91n4\x8d\xe1\x0b\x04K\xcd\xe4)\xdbg\x1b\xb9y8,\xce\xf4\x85\xdf\x98\x8dZ\xfc\n\x10\xb0\xf2\x8a\xc7\x03\x96nnZ\xa5\xabs\xd1\xbdqjq}=\x85`\xa18\xbbs\xc1G\xc0\x166\x9e\x8f\xb7&\x02\xb97\x1c\xf1\x06b\x92\xd2\x93\xcdFS\xac\x0f\xe8\xdec\xd6\xef\xa7\xec \x0b\xad\xbdZ\xb1}\xe6\xa8\xae\xb9V\xe7i3\x10\x0d\xaf,\xb9\x0b1IV\xaf\xde\xc5\xd0l\x04\xa5\xe6\x90\x04B\xdco8\xab\xe6\xd1\x8aG\xc6}\xb7\xd3\xbe3\x86Q)\x1bBQ\xe7.\x94\\\xb2}\x96;3\x8f-<\xb6\xc2U\xe1\xb13\x0b\xc5\x04\xba\xabwy f\x12\x0b\x8f\xcd<\x16\xb0+y_\xeeL,\xcae\xf3\x08\x1afP\xd5\xba\xc1\xa1\xad\xf5\xeai}J\xea\x07HT\xd1\xacu\x86\xbc\x01\x8b\xd8~\x04\xca:\xf3\xb5\xa2\xac\xe4\xd5o\xbd\xc3\xfa\xc7T\x7f\xbb\xf1x\xb7\xf4\xad\x9b\xf2r\x16\x8d\xe0C\xea~\x9fH\xaf\x97\x07b\xbd\xd5\xead\xa1\xeb\xa9\x8c \xbfLy\xd9\x8a\xe7ft1\xa6\xb1G\x91\xa5\x15V\xf0Gb\xab+\xdcT=a>\xdbd\xc3bM\xe6\x95\x83\\\x15\xd3\xfb\xfdH\xa2\x90H5\x9b7\xc6!\x17L\xe0\xe4\x1d\\M[\xf8Z\xc5\xd6\xde\x90\x93\xb5n\xc5u1\x9ade\xb7\xa9x\xa7\"\x9d\xd2\x1c \x14\xaa\xab?Sl\xbf\xaeq\x08ew\xea\xcdL%\xdfTO\x9f\x9b\x9c\xc1J\x0f\xac\xfaLy\xf0\xac\x9b\x97\xcc\xaa\xa5\x12\xff\xb2^b\xa1\x97\xc0M\xbb^\xe4\xec\xe6\xc2S\xc5\xa2,=v\xea\xb1K\n\xffO\x04+\xe2PG\xa1c\xc8\xc9\x88\x9cs\xb6\xcfN\xd8\x01\x9b\xb1\x11\xcb\xc9\xba\x87l\x9f\x1d\x17%\xa86.\xc4^/\x1a:\x17\x9c\xcd\x8a\x1d\xb0\x05\x1b\xb1sW\xfc\"8\xa6\xb7\xa2\xb8h\xf5P/~h+\xfe\\5|h.\xe7\xe7bK\x0fA\xd7e\xaedX\xa5!\x9cb\x8a\x8d\xd2\\l'\xe0+\xc5\x83A42>\xc5\xf76.\x8a\x06/A*x\xa964\xd7c'\"e\x8a\"\xdb\x98\x98\xb5\x11\x0bd\xeay%\xc3\x1c\xdb\x86\x13\xb1;lN\x0eM\xcc\xf6{\xb6\xcf.@\x0c\\\xb8\x96\xe9\x1d\x1f\x9f'\xfej\x05\x82jb\xa2\xc4\xf3\x8c\xed\xb3\xb7Z\xb5\xac^\x8d&w\xef\xc5\xb8\x9e5\x9d\x07_\xb1}\xf6\x9e\x1d0>\x00Wr \x11mp\x9a\xfe\x9a\xed\xb3g >-\x8bg4[d\x05\xf6\xa9\xf3\xcac\xaf\x15\x1c/\xdb|^\xd3l\xd0\x06L\xaac\xb6\xee\x9b\xd3w\xfd\xad\xd1\xd8\xea\xe4\xc1o\x9b6\x96\xd9\xdd\x1ev\xf5\xe3zv\xcbf\x1du.M\xb7\xef\x80\x02\xfel\xe6\x80w\xe1\x1a0\xc4\xe3k\xf4\xcd\x9f\xcd\xc0\xabP\x99\"\xb6D4\xca\xf0\x0d\xfb\x8b\xa0jj\xe1\x93\xf0\xad\x037\xba\x99\xae\xa6\x13O$w\xd3\xc8\xed\xb4s~\x9f\x8cX\xfb\xb7\xec\xbae\x00\xbb\x93\xb5}\xc2\x8a\xd06/I\x86\xb9\x93d\xf5\xb6(7\x17\x14\xdf\x90K\xfc\xafo\xf8\xa9L\xaf\xb7\x13\x9a\x1b\xbb\xe0\x01\xb6\xcd\xed\xbf\xd8\xa3?E o}\x93\xae\xf0\x03\x9f\xf9\x99aiZa\x05\xc0\xa3e#+\xf0\xa5\xbf\xa2\xf8\x00-\xd8\xfb\xf2\x84\x1bM,\xf5\"h\x97R/r\xaa\x17y\xcb\x0dn\xe3\xb2\x92\x0f\x12\xf0z\x91\x93J\x11\x10\x81\xd7\x8b\x1c\x1b\x8c\xcf\xa7\xf9|nv\xf8\xbc\x066\xffG\x01?\xaf\x17:,\x9c\xaa\x15\xeb\xde\xe2\x9b\xea\x02\x18\x83\x03v\x88\xfb\xc2\xabyg\xd7k\x8aX'\x1e;\xf4\xd8[\x8f=\xaf\xe3~z\x1e\x80\x0f4R\x8e\x05q\xdc\xceGF:\x93; \x1f\x9c\\f\xfc\x0bd\xf77\xc41P\xfb}u\xc50\xff\xd5|\x9e\xf2\xac\xcc\xc7\xdf\x8d\x1c\x88x8x\xa3:\x01\x00{\xd2\x1b \xfe2\xcbCG\x8f\xe9\x8e\x16:\xcb\xb6\xden\xbcu\x04u\x8f1\x18\x0c\xbce\xaeKl\xfe\xf0\xb5\xb9\xf95H_Y\xd2\xcf\x1a{\x178}\xee\xb1>%y\x86\xda\xb3\xc6\xda|\x10\x81Oq1&x\x03O+K\xe53\x1c\xc2\x9d\xe0\x0fK\xf3KK\xa7/\x9b?\x8b\xfa\xa0~\xc5(\xa9R\x7fA\xd7W\xbcZn\xa9vj\xaf\xf6\x0c5\xfd,\xb4\x8b\x8b\x80/sD\xfb)x{\x85\xb3\xde\x86\x12R\x00\xbb\xfa\xac\x15\xfb\x14\xfb\xf6\\\n\x1b\xec\x9f{U\xb4\xf5\n\xe0aa\xd8\xd8\xd5>\x9bz\xecyy\x14\xb5\x7f\xf858\xb4{\x0f\x88\xf8\x1eC\x15\x94\x0b\xb8\x91!|^\nm<\xf6\xda\x02\xde\x13\xfb\x8a.\xf9\xf8\x0b\xe55P\x0cJ\xfe\xb0J\xaf\x99\xb6\xce\xda\x94\xcf\xed[\xf4\xba\xec\x9c\x0c\xe1\x04\xd3K\xcb\xaa\xb8\x195\x82\n\xa5\x0e\x0d\x8e\xfb\xfdl\xc2\xf6\xc1\x86\x9e\xd7\xee\xa2\xb9\x1fC\xc4\xf5q\x86\xd786\xbe\xf6\xb0\xecv\xb3\x8f(\xf1\xc7\xd0\xe4xn\xe9\xb0\x8f\xf2\xde\x94\x02\"\x08@\xd8\x1d\x16\x9bp\x9c\x82f\x8e:\xcb\x0b6hJ\xf2\xffb=\xcc\x05\xe1H\x9c\xcc\xd5tC\x1b\xa1\x95z\x14\xd1\x8a\x04\xe34\x7f\xccV\x0dJ\n\xc1:M\xc7+\x8b$\x7f\xc3 A\xc0\x00^\x9aG\x9aA\xdb\xcc\xed\xa8\x95\x10\xdfX\x80\x190E\xc1\xc47`4\xa9\x0c\x87R4\xba \xa8\x98\x12\xf0o\xd4\xbc\xab\xa6\xba`-U\xf1P\xea\xdf*\xa0\"\x18\xb9P\x1c\x9eV\xec \x9b[!s\n\x1a\x10\x05\x1f\x8b\"\xe4\x12,\x07g\x16\xf0\xf9n!\xfe \xe1B\xe5%\x1cWg\x80E\x1c\xf0g\xc4|G\x9c`!\x15\xd1+\xb5)~u\x05\xc4 ;\x10=\xdc\xdf\xc7\xd3w.\x1bA\xd4\x84vO\xecJb\x90\xa8\xd0\x14\xfc$\xe1\xfe{#\xc7T\xe1.a{\x03\x9exZ\x1a\x92\x83m\xc6\xac\x89>\x83\xea\x07\xf0wi\x03\xfc1\xb0\\Z\xab4\xe8\xcf\x81\x17\xd3\x8a\x99\x03:\x16\xeb\xe6\\|\xad\xda\xc9@F\xec0R3\xd4D\x91\x01\x06\x8fE\xde\xb1.\xa6\x86\x14\xb2,|\xf3\\/{\x8eF\xdf\x08\xfa\x0e\x1bX\xaao\xa1\xc5\x0f\x81\xe0g?\xa8V\\\x9f\xf4\x13\x87\xcfJ|\xc7\xcd!F\x83\xb5 (\xd0\xdc|\x0b\x03>\x8e'b)E\xec K\xacK\xc9\x87\xa5T\x8fZ(\x9e\xcc\xf1\x01i\xd1\xac\xd9 \xc6q\xbf\x0f\xb1\x0e;\x80(\xf8\xde\x00\xa1\xa23\xaa\x91\xf2\xc7.K0(cf\x04'\x91\xbdKZzg7E\xa0\x05\xf9\xf7\xa9\xfb\xe2\x94\x94\xbcm\x0b\xb3\xc8\x1dbiZ\x9eHf\xeb\xc6\xd0\xb5|\xa7\x953[\x170C\xcbMz\x03`>\x84)-\xc1\xe3\x8f\x0b\xf0}\x1e\xc6~\xb6\xb3-\xb5\x08\x80\x80\xb5\xcc\xdd\xfbt\xe6\x8b({h\xcd\x19\xeeZ\xb3l\x1f\xfb*\xb06\x08Y\xcfC\x7f\xb9\xe23{ \xdb7E^\xe5\xa3\x1b[\x9e\x9e\xafaP\xad&\xdd^E\xf0P\xcb+\xe48\xb5\xf4R\x08afp#Q\nr\xea\xb3!q\xc5\xc8\x00\xa9N-MIrj\xc9J\x17TKVB\x9dZ2\x08r\xeaiRxSK\xfe1\xf7\xdf\x17\xfd\xd8\x18z\xeb-\xc1@.\xc1\xd8\xe1E\x94&\xb1\x1fm\xf8c\xb1*o`\xdaK\xfb\xa0\xd85\xac\xdfn\x81C\xae\x8f\x0dc5\xe9\xf1\x98L\xfb'u\xf6\x18O,,[$6\xe7\xc2\xec\xc6\xd5\x9c\xf6G\xae\xb9\x91o\x00\x03~\x87e\xa8\xea\xb5\x10\xe86\xcb\xd7\x86\xb3\xc6\x9e\xebh\x81\xb6<\xd93\x8b\xe9\x05}\xfd\xc8N\xe5v\\\x07\xae8y\xac\xa7\xd6\x8b\xed\xe2\xd9\x0d\x9a~\x9d\xc4\xcb \xe5\x1f\xa1\xe5\xb7<\xfb\x08\xad\xca\x95uK-o\x1b\x97v\xe5\x8aX\xdf\xc0\xb3\x12\x856.B8gE\x00\xda\xa8\xe1\xf4\x15\xc0\xf1!\xb2\x1c.\x90m\n(\xb6 \x99\x0f\xe9\x06\x96\x95\xd2E0\xcf\x9c\x06D\xd5.\xfe\x03k\xd1\xb64E\xf9\xc0\x89\x8b\xbd\xcb\xde\xb2x\x00\xf8q\xc3\xa2\xa2)-\x99\x8aS\xe1$\xec\xa9\xf4%\xa6\xf6\xbc\x91\xd8\xc0Y\x9f9\xd2\xc8\xfd\x80\xf5\x9e\xdc\x13TM\xfe\xee\xb3\xde\xd3\x9e^Jn\xa0\x82\xa1\x8aD\xe9\xa3Hf\x83\xa6\x10\xe4\xa0\xd4\xc2\xb3\xcfb`\xdf\xc2\xd4)kC\xc7\x138J\x96\xbf\x07\xfej\xc5#\xf0\xef\xe0\xe9\xf84\xc0\xc4\xb8\x92\xa8\xcc\x18\x9c\x0dq\x06\xdd\xd8\xeaB\"\xe0N\x06br\x01\xb5*\xbc4pi\x80*W\xbf2s=`=\x86e\xb5\x072\x0e\xd6\xabN/\x8a3\xe6\xa7ip\x1a\xf1\x19\xcbb\xe6\xb3\x95\x9f\xf0(\xdb\xa0\xf8\x07\xf5\x9ci\xfe\x91\xe8^\xaa\xa7\xf4H\xa3 f\xec\x0d\xe7\x8e\xd6[IT#\xaf\xd2\x02\x8a\x80\xfa\x82\xc1P\x94\xd6\xf5\x9agE\x7f\x14{\xe9P\xbc\xa2zlT\xca\xc2f\x08\x9a\xd7uJ\xb4\x0d\x17\x0d<\xc4\xd0\xe0\x84\xcb\x95\xd7\x1d\xc1\xe7\xaa\x1c\xd1\xd3\xce$\xd3*\xfa\xac]d+~}pK\xc7\xc3\xce\x83\x07\xf2\x80\xdd$\xe8W\xdbyu\x80\xbd;\xbd\x11\xeb\xdd\xf1\x97\xab\xc75\xa2x\xb7wW\xe4\xfc,\x8f\xb3zV\xef.VZ\xc5\xa9\x91\xf5\x04\xb2B\xb3\xceS\xc88\xcd\x1ek\xc1\xfa\xda\x04\xe3\x16\xa9\xb8$^\x92\xb2\x01\xf1*\xc4=\xce\xf8N\xef\xc9\xd3\xbb\x18c\xa1U\xd8\xa6\x04\xccFP>\xe0\xd9\xca\x8e\x92\xd0\xad\x91G}\x08\xf1\xe3\n\xdc\xa5\x19\xc1\xa3\x1dwpx\xc6\xa3\xecp\x19d\x19O(o\x1f\xe6A:\x913\xbd\x08\x0cu\xb5x\"\xe7\xe1\xd0ub\x0f\xfc\x97\xc4\x837%\xc5\x14_\xbc\x0f\x89?N\x82\xacH\xdc\xdd}\x00\x89\x9f\xe5\xab\x90_\xc8\xa4]Hz\x97\xf8Q:\x8f\x93\xa5L\xdd\x83\xd4\xd7~\x9a\xbe[$q~\xba\x90\xe9\x0f!\x1de\xe2x\xb0\x8bu\x97\x1f\xc1\x8a\xb7\xe97\xce4\xdf]6\xc9yL\x9fF\xf9\xe0\\\x0d\x07U \xb8\xd5\x88D.j\x80\xd5\xd8\xca\xcfS\xae\xbd\x1a\xc7&\xfa\x93\x01I\x85\xa2r\x1f\x82\x16\x13\x9e\xe6\xcb\xca{\xe3\xa9,\x1a\xc4Q\xc1\x92\xc5`,\x08 \x89\x1fD=\x8f\x05\x90r\x1c\xa4o\xb3Y\x00r\xfcL\x1b\x18\x1e\x9e\xc1\x119\xd4\x12l\x9c\xc7r`\x88\xc4od\xdb<\x96\xd6\xa5xg\xd2Ztch\x83oN\x0e\xd6\x87\x8f\xf9r\xc7\xe5H\xc7\xbaA/\xed\xd0 y\xa9\x8d\x0ff<\xcd\x92\xf8\x12\x17\xb6\xfc\xd1\xf5\xb3!M\xb7\xc5\x16:u\\OZ\x02$\x830H3\x1e\xf1\xe4\xb9\xd8\x87\xa4\x13\xe1\x1e\x17\x9bi\xcfU\xfbk\x9d\xde\xd2_\x9cZ\xd1d\x19\x9f\xf1/\xe4wjsndj\xf3oV\xd5\xe7\xb9\x9eW\xce9Y\x13F$\x98%\xea\xabz\xae\xed\xab\xd3\xc6\xafN\xc9v\xcb\xdc\x86\x95\xa0\xc8-br\xa5\x9f\xf5\x14\x1d\xdb\xa7\x06\xb6O\x8b:\xd5\x14<\xca\x08\x02\x04gL\xaf\x95\x86\xbb\x10`\xa9\x89\xac\xf7\x04!I\xb3$\x98f=\x92\xaa\xdf\x1f\xba\x03\xbc\xadDZ\x08\xec\xb6z\x9c\xaf\xe3R\x81f\x9cD\xb3\x8d\xf6m\x8d\x15\xa6\x91\x9ci7E3Wg#\xdf]\xae\xb8d%\x9f\xfb\x91\xe0&\xc5>\xc3|6\x0d\xfd4e~\xca\xfc\xe2K\xc4\xb9\xf0C\xe9\x86\x1b\x19\x9e\x05\xf7g\xd2LK\xa6d~\x10VS\xe4y`\xdf\xea\\\x99i\xbb\xbc\xe9E\xaa\x99QS\xbc\xad\xe5h\xe9g\xbe\xd5;Y\xc4/2\x94G\x99\xe34y3}(O\xc1\x16\xa9\x18.\x88}@Q>\xaa@%\xab\x82$\xf3\x98\x8c\x01\x80\xcdT\xa1\xe1U\xc6\x9eG \xfc\xfe\xf8\xc3/\xfa\xdb\x05\x062\x06\x89\x06 \x10\x06\xebc\xac!\xc6:c6Fl#\xf0R\x00V\xb6\xdat`\xe5\xeaH#z4\x10\x10\xa1\xcf3\x12\x01\x87\xc6\x10\x0f\xaa\x03\xaa\xe1x}\xca\x8b/ \xf0\x16\x91A\x949\x05a\xce\xde\x04\x11\x15\xf5\xae\x11\"M\xbdkY\x81\xd5\xaf\xfd4\x0e\xda\x1d\xb8#\xfc\xf7\xeb\xf0\x97\xd0\xa3|\xe6Tn4\x15\x9d\xc5kM=\x14\xc7\xc3\xacHoH\x02n\x8f]\x16\xb1\xfe>\xe8\xc03\xcb\x9c\xd1f\"5\xf8\xc5\xd1\xd4o_D\xcdcJ\x06~\x18\xc6Sg\xcbb\x8an`LQ\xb3\x0d\xedJ\xc8\xc0\xb19F\xb3)\xf9\xbd\xaf\xa2\xd4\x9fs\x87\xb3\xa7O\x9f\x82x\xd2\xaf\x82/\x17\xd3\xf9\x98\xf9\x8f]\x00\x9c\x0f\xdf@\xa8\x06x\xa3>\xf7@\x97\xb6\xbaD\x9b\x1fQ\xa5\xaf\nV\x0c||\x04\xba\x0d\xc4\x81\x01\xe2\"\xe1\x83`\xb5d\xf4\xb7 JW|\x9aU~\x0c\xa6y\x9a\xc5K \x13\xa5t\xa6\x98\xa0q\xbd\xe0\xa4 \xd9\xd5j.*\x11r5\x1c\xd6\x88YI\x8e\xe5\xf2\xa6(\xae]\xfa,to\xa0/\xd2\xc6k=rw6H\xa2\xb6\xef\xea\xeeN+nH\x8eD=\xb0\xefC0\xcb\x17\xcb%\x9f\x05~f\x95jH\x05\x0d\x1a\x19I\xbf3\xe6}7\xfd \xe1\xa2\xbb=\x7f\xda\xa0\x9baRw\xc3\x07\xb3x\n\x922{\xb9Uitt\xca\xb3\xd7\nI^\x81R\x83\xcc\xb0\xba\xb0\x12M\xad\xc0\x92D\xc0\xe4]\xb0\xe4q\x9e\xc9\xe8\x88\xdc+\xfd\x1c\xac\x92x\xca\xd3t\xd2\x835\xfc\xf3\x0fEpIy!x \x0b\xa0\xb1m\x1b\x1dQ\x8f\xa6\x07j\xa4\xdc\xfa\xb3p\x88\x0b_\xea\xb1 \xb8\xd8HG\x9d\xa6O\x80\x12u\xb0\x8a\xd3\xecK\xe9@M\x9c6\xf9 X\x8a%\xf9v\x9a\x04\xab\xccj\xef\xa3\x1eE\xc47\xb6\x9a\xa5\x88LJ\x12\x05\xb3nu\xd1\xa6?\x05\xf3W\x94o\xdb\xf4\xeaOF\xeb\x10\xf4\x07\xf7\x86\x12\x02N\xaf\xe7\xb1\xde'=y\xaa(?\x1c\xd5o\xd9UZ\xa1g\xc2qA\"%\x9b~\xbe\xf0\xa3\x88\x838\xdb\x01{J~\xce\xaaY\xee@\xc0}H\x0f\xb8\x11\xb9\x16\x0e\x07\nn\x93y\xae\x81\xa7\x01tb\xbb\x02\x14\x0b\x16\x82l\x0c\x16b/\x8e\x12\xee\xcf.\xd3\xcc\xcf\xf8t\xe1G\xa7\x1c|\xdd\xcc\x07\xd3\x84\xfb\x19\x97\xa2w\xa7\x97\x02R\xf5\x04`\xc0\x8eq^\x90\x00Yd\x9d\xae*\xd4\xb3~\xc5\x8e`\xd9\xc0\xec\xf1:\xe8%E\xbdt+\xc8d\xc5\xf2d\xfc|\x11\x8430s\xced\x9e\x1d\x8fD-\x94m\xabZv\xc0w\x87SI\xed\x9c\x85\xc7\xb6\x8c\x1bF\xea\x11\xa4\x03\xc43=}\xcf\xf8\xa1\xd8\xed\xe0\x16P\xe2G\xb3x\xe9\xc8@\xb5\xc8m\x14=h4a\xcc\x06i\x9c'S.ob\x08\x8c\xd1\x83sI\x1b\xa5\x812\xe9\x93|\x172%A4\xe3\x17\xaf\xe6\x8e\x0f\x02\xbd\x85\xd3\x97\xe9\xa0pq\x14\xd3b3q\x14\xeb\xd8\x9f\xcd@\xd8\xaad\x14\xb0*\xeb\x89NO.\xba\x1el\x7f\x1bC\x10\xfc\x0e\xfc,\xf3\xa7\x0b(\xe9\xf4\x8a\x85)\x052Ig\x00T\x89\x8c/XX\xa43\x96\xf9\xf5p\x93*&\xa1\xf3\\kR\xb5\x8d\x9a\x19/\x97DGy7q\x80\xd1\xe6MF\x7f\x156\xbd48.\x14\\\xea\x10\xb1 \x11\x0f#\xe4>#\xf6DwM\xd0\xef\xbb\xca\x97@Qo\x0c\xaaA\x8b\xdd>\xd3\xec\xbe\x9aW\xa1\xd8\x8fO\xfc\xe9\xfbF_\xe3\xe2\xf1\x93\xd3\x942\xb8S\x0fq\xacU\x8f\xdc\x86\xc2q:A\x01w\xe2\xa4\xae\xc7\xd2~\xdf\x86p+<\xa2\xe9sG\x1c\xa4\x1b\x8c\x08f\x0d\x16%\x18\x947\xac\xdfhd-M6\x18\xa9\x80t\xd4\xa5\x88\x04\x0d\x94\x86\xe88L#\xca!\x19\xebV=p\x85\xad\x8d\xc8N ?|\xf5'K.;p\x02\x1b\x1dW\x8f\xfe\xa8\x81\xa0RW\xa0Y;\x83\xa3\x9e\x04\xea \xack\xee\xbdz\x94\x91u\xd2\"\xbb\xa0\x1e0\xbc\xde\xb2\x1b\xdfRO\xa3\x01%\xf5\xb4\x98i\xd7\x1f\xe8\xd3p\xdd>%\xe3-\xeajw\xd3s\x9d~m_\xa7_\x1eK\xc6\xc3\xef\xa3w;\xd7\xef\x9d\xf8\xbb\xfd\x91\xfb\xd8j\xebM=\xa0\xb0\x0fA\xe4@\xd8{P\x0f\xcdQWJ\xd8\x98\xa3\xa2\x00\x9b\x07\x91\x1f\x86]\xe8\xc3\x0c\xd8\xb9i\x87\xf3\x825\xb7\xab\xe1oM\xb6\xe7\xf4\x8a\x98\x05:/\x94\xf2p^^aW\xf7W\xb3E\x90\xc2\x0d\xd7\x11\x14\xd0\x94\xc0\xba\x11\xc0\x0e\xec\xc5v[\x80\xee\xd7\xa2\x8a\xed\xc3B6\xed\xc4\x17\xadV\x06a<\xf5\xc3\xb7Y\x9c\xf8\xa7\xbc9\xe6\xda\xd4\x07\x02\xd8\xe6\x15\xa45\xda\x19\xd3U\xca\x95\xef7\xc6^\x97>#\xc0\x9c\xac\x97%9\xc7\xc3?\x9e\xfb\x9d\xc8\x1dd\xf1\x17\xf19O\x9e\xfb\x84\x06Y\xff\xd5\xf9^\x1fS\x97a\x9c^\x14\x7f\xc6W \x9f\x82\xe9ZO\xbb\x97g\xf6Wi\x9b(\xd7\xaa\xf5\x9b\x82M\x1b\xfe\x06ycS/\x119=\xd0\x10\xd5\xbaV7>\xb29\xf7f`\x90\xd0\xcb\x12\x7f\xca+M\xb0\x036\x8d\xa34\x0e\xf9\x002\x1d\xf0w\xa4\x92\xce\xfd$B7\xe0\xb0\xf7w\\SL\x17\x17 \xa9\xc9@%UZb\xb5\xadC\xebR\xea\xb4\x86hA\\\xc5\xf9N\x99\\j\x0cw\x86\x96+\xe5[\xbbd\x00\x98\xc0\\\x1f\xa8\xdc\x03\xc2\xa0\xe9\xf7\x82\x12\x890v\x98\xe1N\xbb4%!\x02\xe8\x8b'\x1e\x04\xd1\x82'A&\x1d\xc1\x0c\xc1\xd2C\xa59\x01\x9a\x99\x04\x9a`\xfd8\xd3\x8cF\x9a\xa0\xc5\x007\xf0\x94\xdc\xea/\xa4\xc1\xb6&r\x86\x8f\x1et\x9a\x9fj\xad\xdd\xebT\x1a>\xba\xef\x96f1\xd7\xac\xaf\x19\xd0ti\xa1M\xe3\xbc3\xa4\x02\xe8\x8bt\x8bK\x82\xbd\xf6[\xea\xf5\x89\x92\xaa\x08\xbc\xac]\x1e\xe0\x0c^H\xa2\x9b?\x88\xe2d\xe9\x87\xc17<\x81k\xa9\xa0\x96s2\xed\x8678.+\x95\x0d\xa5G\x0c\x7f\xe0\xa7\x97\xd1\xd4E\xcf\x04\xfe`\x95\x04\xcb \x0b\xce\xc4\xd6\xa7\x8c`\xd8A\xf5\x13p\xb1z\x0b\x0e\xeb\x19\\\xb3\xc0\xaaF\x89m\x17<\x7f\x8f\xea\xb5\xb5vE\xb1\x1d\x17bQU\x13\xf70Q\xbc>\x84f\x8a\xae\x82\xe5\x8f\xb3\xb7\xf5\xc8\x95Q\x8d\x96\x8146r\xf6\x86\xa0\x9f\x19\xcc\x82t\x15\x97\x89\xbb\x90\xb8\xf4/\x9e\x9d\x16i{*M&lc\xcd\x84\xcf\xc1@\x85'*}[\xac8\x81(\xfe\x9a\xab\xa6\x0d\x91v\xf7(D\x02\xa1\x8f\x7f\x92\x9a\xa8\x049\xf30\xd6\x1dbwC'\xa5>J_\xfa/\xd1_\x05\xba\xe8\x00,\x11Get\xa7\nN?\xee\xdcaA\xfay\x10\x05\xe0\xa2\x1a\x1c\x0dq\xf0\xf2\xe1\xc4\xd2\xdfP\x9bQG'0\xd4\x88\xc3\xde\xb6\x0b\x82[\x18c\x1a\x9cF0\xf5\xbb{;\x9d\x88F\xfb'\xac\xfb\xb3Re\x15\x1f&\x17\x18m6\x05h/\x0d\xe0\x9c!z\xa5\xdbT\xbf7\xb7\xb7\xd6u\xe7\xb1\xc60\xec\xb6\x99\xdadz\xe5\x8c\x03Q\xd0=\xb2pi:\x81>pn\xa3\x9f%b?\xa0\xbd\xd2\x0e\xef\xd7\xfd\xdaH\x02Y\xf7\x98$\x03V\xee\xd1\x01+\x05\x9dm\x86\x0e\xe3\xb4\xb3\x81\x08oCUgX\xec\xe5\xe8\x10\x03n^I\x97\n\x15\x9a\xebjtG\xd1\x1b\xc2\"\xfc\xd5J|\x1d\xf3 l\xe8\xca\x9f\xf4\xb4\xe6\xce\xa8\xe5\xcc\x9bbEt\xd8z\xa0\xda =6\xf7X4\xe6\x13\x88\xe9\x81Nx\xc8K\xe5\xb6\xe3\xea\xad\xe0\xf2\xae%\x16\xe0\xce\x90\xf6K9\xbco\x89 \xfcp\xcf\x1d,y\xb6\x88g)Ejw\x0d\xff\xc0\xa9\xe4\xec\xeaG\xa8\x90^\x0cp,\xac\x96\x9cv]6\xf3re\xa0\xa6\xb1\x9a\xad\xd9(\xa0(G\x12\xcb\x80\xd7\x86\x82!1\xe3\x9a\xdf\x80\x05\xa4\xf2e\x90uXX\xc4Q\n\xec\xbb=vVD*\xf5\xd8\x89\xc7\x8e!\xc8\xec\xa1\xc7.0\x9a\x96\xc7\xde{\xec\x99\xc7^y\x10tk\x0e\xe7/\x9a\xe2c\x00\x11y\xa1\x14i\xb9\xdc\xbd\x0b\xf14\xee\xd6\\#\xe8\x1aW-\x10\xff\x02\x9cu\xea\xc9\xae\x07Qq.\x06\xa7<\xf3 \xf2\xcd\xc5 \x15\xaf\x97\xf0\x8a\x9a\x0d\x0f\x02\xd9\\\xa0\x06\xc5\xf5J\xc1\xcc \xe1i\x1c\x9e\xf1$\x85\xe6_\xc9\xad\xa5H\x15\x8b\xfa\x19SA\xf3\xed\"-Vn\xc0\xd2\xb4\xaa\xa0 &\xf9\x10\x1b\xf2+\xf8\x1e\xf8\xbeq\x02\xb7\xec\xd2>n\xd2K\x91\x08\x8aIb\x9b|-f\xab8\x89C\xe0]_Z&\x9f\xf2\xac\x07\xab6@s<\xd7c\xaf\xc9\xe8%\xa2\x0f\xe8tO\xf0LAi\x808-\xe8 \x9e\xe2\x83\xf1\xd6DP\x80\xb0\x9e\xae\xfa\xbc\x8f\x9e\xa1\xecB!bd\x8a\xb7H\x9c\xde\xf3 \x99\xe6\xa1\x9f\xb0 :\x8b\xa54\xc7c\xbd\xe7/\xde<\xff\xea\x8bgo\x8e_\xbc\xfc\xd1\xab\xe7\xcf\xde\xbdx\xf5\xd2\xa6x\x17\xad\x9e:\x01!\x8bA\xa5\x92\xe8C\x03\x18o\xa9'r6^\xa3J2\xf6\xd8s}^R5/R\x89/\xf8\x90*\xfd\xf4\xd8\x99[x\x15\x14\xeb\xa3Q\xe0\x06\xc7gzV-C\xc5\xbb\x02\x8dh\xa3\xae\x13\x14\xa8[\xe2\x90\xc5\xaa\x10\xf4m:\xb2\x97xT\xc7\x97Rf\xc6F5$s=\x1b\x9a\x17\x9d\xbe\xe5IB\x93\x000\x19&\xa6\xa9\xb8C\x8eV\xad\xa6'l\xdd\x93\xfa\xed\x92\x02\xfd\x8e'lyRT\x0c\xab\xd0\n\xa6\xb8qZ\xe3*5\xa0\xfc\xda\xc12\xbd)5h\xe8\xdc-O\xdf8\x16k,\"'/V\xf3\x16U\x82\xf21\\c>\xa9\xfc\x8f\x93\xe04\x88\xfc\x90T\xf8+n}\xc4\x9e\x99\x99\x92\xd5\x7f \xde\x83`\xb7W?\xcd\xb2\xa7<\xebr\x15T\x0e\xf2U\xc1\xe8\xbdr\xb8\x0b\xbb\xdc\x01[\xa2\xb3\x07\x89\x14\\L\x86I\xf5\xcc//\xfct\x8d/[\xe6\x91r\x12o~\n\xf7\xdb._\xb3\x900\x86\xfd\xa5{\xc00\xaa\xfa\x9d;\xec\x12-\xa5\xd8>{\x0d\xbc\xaa\xb4`\xc0\x1f\xefu\xb4\xc0\x9c\x1e\x86\xa8\xa3\x1cE\x99\x83\x006a\xd4\xae\xf2P\xa2\x15\"N(\x83\x80\xc8w\xee\xb0\x13q\xe6\xd3X#\xaf\xe8\x18|\xa5\xd7\x15\xb0q4j?\xb52M\xa0#\x16\x7f!\x10y\x0bz\x0f6\x02\x1b\xac2\xf9y\x91,\xa1TZRA\xfcW\xf0\xe41\xab\x08\xf5i\xdf\x15f\x7f\xc5\x18Glaf\x14\x87\xe1\x0e\x00\xe6\xc8\xd9\xca\xe5i~\xb6\xbe\xbc\x8fMV\xcd~\x95\x05-\x8b\x1a\x883.A8\xe5\xe1\xf1\xae\xe4d2\xe0d\"\xe4\xd1\xfc2\xc6]\xbdC\xeb\xec\xe9\x85\xa8[\xb6&7\xbfj\x93\xacmi\x11\xe4\xa3\xdcTp\x17\xf1\xcb\x00}\xf5\xfe\x9e\x83\x14\xbd\x95\xf5\xe0\xad\xb0\x93\xdd(\x87.\xf7\xdc\x91\xda\xef4\xb0r9k\x02\xa0%u\x8b\xb0\xb3bE\x9b\x82\x97\xc3\x8f\xd6O\x1f\x82\xd8K\xd8\x93\xdd-\xb1\xa0\xa1\xe3\x1210\xe6\xbe\xd9\xff\x95\xf3\xcc#\xfa\xac\x0b\xbfF,\x00\xd7UV\x12\x1b8\xc7D\xae\xa4]\x81\xe3\xab\xd3\x8e\xf9\x15\xd8\x89\x02\xe7\x9c\xca\x83\xbd\"p\x0e\xcd>\xfbE\xca\xad\x1c\xf1w\x86T \x10q$\xb7h\x99\xea\xe2-\xb1\x97\x83`r0\xf5WY\x9e\xf0\xb7\x99?}\xff.\xf1\xa7\x9a(\xa9\xe2\xab\xa3U#\x15I{D\x94wR\xd1n\xf3\x8aphH\x88\x90\xd2\x9a\x90\x89<\x0b\x07N*\xddm\xe5\xb8\xa9I\x8f\xa4\xca\xa9=hdR\x19\xd50\xc2\x9b\xb8\x81*\x1b\x0d\xa6\xf1L\xe0^\x0eWu \x08D\x84\x8c\xea\x9a\x0e\xa8\xd7\x90\xc7\x93j\x05\xdc\x81\xa5\x90\x02}\x85t\xd7.H\xf7n\x0e\xed\x15e\x1e\xc7#\xd6K\xfcozu\x1ae\x96=\x11\x18\xdf\x9b\x9d\xfb\x1d\xcaf\xc97\x97#\xd6\x13\xffz\x06\x8a\xf3\xc1<\x8eY\x9f\xf1\xc1\x89\x9f\xc0\x7fQ\x0eh\x83\xe8\xca\xec\xdc\x87z\xb7,\xb8\xdd5\xa2B5Hn\xd7\x08\x9c`\xd1\x10\x94\x17q\x02\xc3\xe4\xd6c\xdb5\xbe\x1blu\xb9.\xe9\x04n\xb4b\xa4M\x8a\x1a\xedV<|\x9c@\xfc\xd1qBX\x9b\xb6\x9a\xecD\xe8\xac@\xac\xebV\xf3\x0bd\xf8\x87\x8f\x99\xcf\x9e\xb0\xf41\xeb\xf7}y\x85\xadX\xa0\xfe\xc4\xc3\xf8\xd4\xca=Q\xee\x9a\xea\x13\xcd5KT\xe8EHL\xff\x18\xaa\xc3\x87CT\x1dj\"vT\x1e>\xdc\xfe\xd8\xcaCz\x12\x15\x8f\xa1\xf9\x96\xed\x15Z\xf5\x1ex[\xac\xceC\xe3\xa4\xd26X\xb7-P\xa6\x94#\xda\x00\xda\x96S\xbd\xe3\xb2\xd31x\xc3-\xe6\x06\x8fg\xeb\x1a\x9f\\\xab\xef\x04\xc5\x94\x9f\x18\x91\x97\xa6\xf0\x16\xda\xc8\x98\x9ak\x0e\x1c\x86}\xe7\x0e\x8b\xc7J11\x11\xebr\xdd\x10\xb9\xed\xa8)\xd0\xfc\x01\xe2\xbf\xbc.W\xb9s\x9b\xf9A\xa4V\xc3\xee\x0dV\x83\x82\xb6N\xe6\xd7\\+M{]R\xf6Ulz\x1b\xcae\x88Ju`\xf7R\xbe\xeb\xeby\xf38\xee\xdd\x8e\xaa]\x0d\xd3\x00\xa5\xbc\x0es]l\xa8\x1d\x11+\xcae\xf6\xf46\xf5\xef\xb5\xeb\xa4\x9er\xc8N\xe9\x80\xe6\xb4^t\xd5Y\x953\xeb\xaa\xcaY4\xabr\xce,\xaa\x9c\xda\xe7\x96]5>\xa7\xed\xc1n\xab\x15.I\x8a1\x8d\xa3yp\x9a\x83\xf6\x95\xa6\x1a\xbc\xd0\xce\xd2\xae\xaf\x95\xa7\xa4&\xba\x92\x1b\xdf\x164*i\xe3V\x98\xe2X\xac\x87\xb69\x185\x9c\xea\xb8\xd7;>\xe6\x1c\x0c\x07\x0e4\x07s\x90&\xcer\"\xe9rp\xe6\x87\xb9\xe0h\x16J\"sV\xab\xed\xb1K\xd7\xd3\n\xcab\xd1\x98O\xd8\x01\xe5t]\xe6\x88\x7f\xe8\xb1\x0d\xacO!u\x9f\x8dQ\x9b\x9aM\xca$\xe9\xad\xa3\n\xb1\x1a\x8d\x8f\xa6|\x04\x94\xbe\x1b\x94<\xdd'\x98z*\x80\x8a\x95[>c\xb9F]\xee(J5u\x8c5\xe0*\x992\xdah\xb7\x8a\x05\x07;\x02\xba\xaf\xa2i\xe1\xd4\xe7\xf8\xb8#(\xe6\xf3\x11\xf0\xbe]!!\x89\x04-\xe7F`l\xd0hS\xf1\xa7@\xd7\x97q\x80J\xc4r\xc7|\xd2\xa1\x9e\x896\xe8`T\xd46!\xc6\x14\xeb\x1d\xe0\xed71y\xc98\x98\x08\x1e6pY\\\xfa\xe5\x8d)\xb8b\xae`\x94\xb7\x95s*%\xd2\x97(\x98\x8c\x03i%7\x14\x88\x99\x0c\xd2\x15\xdc|\x0c<6\xa4\xee\xee\x81*-)?\x9b4~V\x8ac\xa3&\xeb\xf8\xb6iG \xa2\xdfzG\xf1\xac\xf0j\xd18\xef\x16:!\xb6\xe3\xb8:\xa1\xf6\x19\xa1\xe7\xb1\xd9\x19<\xccbD(\xc9d\xac6-\xde\n\xdew\xcc\xf0\xc8\x92\xb1',\x12\xd3\x9d\xb9,\x18g\"\xb3z\xd91k\xb8\x08\x07\x1f\x8d\xc1\x81\x05^h\x95\xedn=\x06\xc2\x1b\x8b\xca\xd8\xb4\\\xc5I\xa9\xc9!\x1b\x95\xbaTu\xa3\xac>\x96&\x00t\xb9\xb55+\x88\x0b\xe8\xa9\xec\x03c\xedw\x8b\xba\xdc\xc6\xaa~\xaf\xc6\xb0\xdc\xfc\xeb-\xb7\xad\x9a\xbe\xeeU\x84G7\xebK\xa7[U\xbf\x10\xfc\x14\xcf\xaa\x06\x05\x1b\xe6\xfd\x80\xfe\xf5\x81\xf2\xc6,8\x8b\xa9S\x17z\xe2^:u\xe2z\xba\xd8X\xa6N\xe0R\x84g\xea\xe8\xe6\xd0hG\xb8t~\xfe\x01\x85q:{\xdc\xec\xf5G\x19\x8bi\xa1*\x17N\x88\xce\x88\x8bSc5T\xa4\xc72e\xb4\xc4\xf6Y\xfe\x03vS\x8eY\x9e\xa3\xea\xb1~\x1b\x04\xab\x04\xdb,\xf88\xd2=q\xf9\xbdf\xe7\x01\x1a\xdd\x1f,\xfdU\xbb#hU\x81\x1d\xb0\xcc\xe1\xe3\x08T\xcf\xe2\x7f\x15%\\\xe9|\xc9\xc9+Zi\xf3\n\xff\x07o\xbdc\x0d\xc8\xbd@\xe0\xd516O O\xc5\xbe\xa1Zq\x05\xd7u\x12D\xb3\xf6P\xb6\xddg\x16\x8f=\x8f(S9\x9c\xa8 \x85\xff\xd7<\xd5\xc5(\xda\xe0\x10\xce\xfdv\xba\xdd\xe9 \xadD\xcb\xc8\x98\xe2H\xe6I\\\x0b\xc8\xd5t\xdcF\xff\xed\xe0]\x00\xe6p\x0c\x82d\x0fe\xc4\x13\xd7c\x9f\xc6q\xc8\xfd\xc8\x01V&+}.C\x01\xd4\x05\x81]\xf4m\x8cY\x13\xe4<\xdav\x07A\xc6\x13?\x8big\x8e\xc6\\\xca%\xfa\xc8fAN\x1a\x90\x1bK7\xa5\xe5\xc9!\xbd\xfe\xa7\xf2\x9bur1\xaf\xe3U\xa7c\xb5yX\x9e\xdd\xc6a\x94\xc8\xd7\x0f\xa3f.\x1c\xe6\x08\x1f\x8c\x1f\xac'\xf9\xeaQ}\xddET\xb2\xa5V\x13\xcaV]\xd2\xdbF]\x128Z*%\xf3)J\xe6C\xe7B\x06\x08\xbf\x90\x0e\x12\x99t\x19\x0eh\x0e\x13'R\x02\xf4\xf8\xec\x16\xbe\xf2\xaa\x8d[\xfc1\xc0 \xe8\xc2zF\x9c3y\x89F\xaeN4\xf7tN\xb5\x10\xc5\x82\xa4 \x16\xc9\xdb\xdb\xf2\xc2\x9e8\x9f;\xcb\n\xc71t!b\xd9>\xe3p\x19}i\xe1\x86\xf0T'\xbe\xda\xc2\x85W[\xaft\xaa\xe2f\xe4T\xb05\x91\xcb\x96h\xcc\xc7I\x0bJ\xf5\xc8\x91.\xc9\x02\xe6\xa5R3e !\x03\x7f`/\x040\x9f\x1bzdf*'\x9cs\xe8n2\xb1\xc2\x02\xe0p\x02f\xae\xe7\xf2J*\x1a\xd2\x08\x82\xa9\xe0#\x0e\xc8\xe2l~\x02\xce\xc5\x9c\x128\x1b\xc7\x83Y\x1c\xf1\xc7.(\xe0/\xd8\x81b\xe2\xd0\x1a\xf8\x18%&\xd2\x90\xbd\xf8%\xf6ogVHS\x0e=\xb6p\x96\xb02fp\xddJ\x82\xf9\xb0\xfe\xd1~\xdf\x125K\xcc\x1c\x11\"\xa84\xf7\x9c6`\x03@\xe0\xb4\x123\xdb\x1c=\x8c\xd7\x03\xb9]\x0d'\x0e%B\xc8Py\"GZ%\xed\xb3\xc3\xc1t\xe1'\xcf\xe3\x19\x7f\x969[\xae\xcb\x9e\xee\xb3\x07\x0f\xb6\x1f\xed\x82\xc5\x12{\xb2\xcf\x1e\xec\xee\x0c\x1fA\xf9Cp:9\xee\xf7\xa3\x89\xb4g0\xc0y(\xedG\x0e\xad <+Ax&A\xd8\xef\x9f\xd9\x81v\xd6\x82\x8e\x1a:\x89=\xf0\xd4D\xb8\x02z\xbe\xa3\xad\x9d\x1a\x00\x9dS\x97^P\xe40%4\x15o\xd7\x1d_H~\x00\xbb2\xab\xc8\xee<\xb6,/\x89B\x8c\x90\xa2\xe6\x0d\xf6\xf5\x9a\x96\xe2\xd1\x8e\xd4R\\.O\xe2\x10U\x12\x8f\xee\xdf\x82J\xa2v\xc2)\xf48\xb5-\x1e>[\x91\xc3\xb6\xe9vH\xbe\xcb\xdcb\xc8{(8J\xcd\xf9Bm\xf7`\xfb\xb2\x88\xd3\xcbx\x9a\xc9\xee\xd5\x8d:i\xf5\xa22o\xac\x9b>\xddD\x89\xa8\x97\xd9H\xc6\x95Q\x14,\xd9\x04\x953F~\x16\xbfV\xdaM(B\x95\xc0N\xbf\xf3O'\xb7\xc74\xea\xba\x0e\x8b\x8aC!_\xfdZL\xd8\xac\x90\x98v\xd54\xcc\xbbi.V\x84B\xc2d\xfa\xc2\xfa\xed\x90\x1az\xed\x1b\xe8U;\x97\x14X\xb5\x06\x1a%\x8e+=\xda6i\xa5\xeb\xeaf&\xe7`\x81\x9b\x80\xb3(\xbb\xef50}57\xbb \x92\xc0\xc5\x98c\xac?\x8c\xa1q-wF\xe3\xca)\xb4z\x98\x8f\xbb\\\x8f5\x89[\xbd\xb3\xfc\xd6:\xeb\xc3\xcdrP\x04\x01\xf4CG\xf3j!\xc5h\xda^\x0b\x01\x1a{\xa5\x15\xa1\xe0B\xa6ND[ \xce8\xfa\xa2\x0c\xe2\xe8\xf8x\xc4r\xf0/\x9aQ\xe6|\xc7\x91\xbf\xe4e\x993\xa7n\x02\xfd\xa1*\x1f\x99:q\xfd\x93\xf38\x11\xd5\x9b\xb1L\x0ez\x86\x8a0\xf87\xc2\x7f\xfb,v\n\x8anHE*\xbf\xdf\xf3\xcb\xcf\xbb|\xccb:\x0e\x8b/cA\xc4R`jgv!\xfel\x9cM\xd0\xd6\xb9\xd4\xdc4vm\xe1\xa7/$\x96(X&\xa8\x06\xd1r\xd0\xa2\xaf\xa7\xa5\x18\x01\xd3\x83\xf49\xc8\xaa\xde\xaeT\xc8\x97Zsf\x01\xd9\xaa\x99a6.\xf7\xb1z\x932Y5$\x7f\x1a\xd5\x97\x82\x1c\xd6\xeaB\x9a\xac\x08\xefF-\x19\x19\xa9VO\xc5N\xc2\x9a\xf2\x97Q7\xe5~b|\x12\x13eM\xfcaV\\\xf1i\xc0\xd3zMLUU\xf1\x17Q7\x0c2\xa3f\x18dE\xbd0\xc8\x8cZ\x1a\x0fP\xab\xab\xe5\xc8\x16\xb4\x14\xa2\x9d\x82S0\xda)r\x8av\x8a\x14\xa3\x9dW\xddS\xdfoT!\xeb\xc2_E\x95j+\xae\xd6\xb1\xd8\xde1\xfd\xcb]\xbe\xaa\xc8\xb7\x031\xdcQ\xf01\xa8\x91Q\xd6g=\xd70 \xad\xfc\x863\xc5\xaby\xd7\xaf\xa6\xb5\x98Z\xcc\x1c\xe5\xbc:\xcaXG&\xaf\x0d\xac\xea\xfa\x89\xfc\x0e-\x1e\x95\x8cw-B<8\xc8(0\xce\xd1;E\xf7\xaa@D\xe8\xd5\xb4\xe7)\x98\xf6\xb0B\xd0^!\xae8\xe3\xafp\xcct\x13UHPM\x94l\xf9M\x1cj\xe9\x02\xda\xdd\xb5=\x19\xa1\xdf3\x108P\x9c\x03\xba\xf6/\xf8\x06\xfa\x1c$'\xeb\xd6\x8dG[E\xfc\x1b\x1bx\xd9\x87D\x93\xab+\x91\xaf\xc7*\xc0\xb2o\x8b\xb2\xe0\xc6\xb4\x1e\xca\xe0\xce\x1dV-2\xae\x16\xaa\xce\xfcm\x0cYM\xa0a\x12\xa5>U]\xc6`K\x81\x12\x88.\xcb\xb8\x10\xc0V\x17\xb2\xe3\xae\x8d*Uk9\xee\x02x\xe2_,\x04\"gg\xb8}\xed\xa1\xd8\xdd\x06\xfdR\x0d\xb2\x12\xf2|\xbd\x01\xa6\x86CqX\x18\x88\xe6\xa6)\x88\xf2\xcf\xa1\x1d)\xb0o\xa2R\x0d&\xee\xedY\xcc\x9e\xe9^`\xd6\x1d*\xc1N7O\xef\x01\xb1XR\x9e\x91\xd7g\xe1\xaeQ-\xea\x9d8\x12\xd1\x91\xa4\xa0t\xe2\xf0\xc1)'.\xd3i\x01R\x07)\x071a\x06/\xfbP'\xe5\x10\x9d\\\xdenC\x15\xa0\xfa\x81%\xf0\x07\xdc9\x93\x01\x8f\xb0\x90\n~$\xca\xe0\xad)\x88\xd1\x0d\xfd\x94\x1f\xc8\xd0\xc1Dv;\x14k\x8d\x89)\x04 J\xdej\x1eb\xb5\xa0\xff\xbd\xff\xbeW\xcd\x97\x87\xa2\xfd\xf2\xd20\xc8e'\xeec\xb6\xb9\x99@D\x9f\xfe>\xeb\xfdw V\x00q4\x89 \xd9\xf77j\xb5\x19\xea\xf7%Ik\xbfB\xd8\x12\x95\xc3\xcb\xf0\xd6`\x82\xf2{A\x02\xb8\x18h\xac\xc2<\xe1@\xb3q\xbf\x9f48\xf61\xd0\xb5\xcb>Q\x8b'\x7f\xcb\x17\x18\x86\x86\n8\xae\x8b\xf8Z\x00mc\x1f ]i\x06*)3=\x82\xd3\xbc\xdd\xc5\x8beA7\x9f\xe6\x99f\xc2JwG=\x01\xd8\x8bZ\xb3}\xeb\"QOPD\xdf\xf2\x8b\x15\x13\x8c}\xb8\xba Fe\xaf%>-J\xda\x06\xc0\x14>>f1{\xc2|\xb6\xc9\x86\x8f\x9b\n3\xd9\xb0t\xa7\x07\"\"\xb9?\x04\xa0\xed\xe4\xe3x\xe2j\x0eW\xad\xdd+Z\x83.\x0e'\xa0C\xe9\xf7ckaS\x05\xa9\x1e\xf9\xad\x96>\xb1\x03\x15\x8eN~N\x81\x8fl\x97\xfe\x9a6*#\x9f\xb8M\x9eV\xd0\xc8jo)\xd0(@ao\x03\x1a\xe5\xcdh\x04\xd2\xc4\x8eh\x94\xba,\xc7\x10\x0e\xfd\xbe%\xf0PK`\x03@\x1ah\xe3\xeaJ\xbe\xec\xb3q\xe3DS+\xb3\x9ao\xcd\x9e\xc8\xab{\xe2;\xf2V\x9c\xc4\xd4M\xe9\xfc\xc3 \xcaI\xcfa\xd2c\x81\xf6h(\x1b@\xd5-i\xe4\x0e\x19\xa2\xa2\xc7\xf2\xf1P&~\xc4\xae\x17}\x1fN\xc6\x01\xe0\xb8\xff\xf8F\xfdv=\xd5\x18N\xe05\xf0WJ8\xc9p\x8b\xe6P\xd7\xf3\x8e!\xdd\xc74`\xb2\xdf\x8c\xc9\xb9\xb4/o\xc6\xf5\\\xe9\xc1\xad\xa5B\xd8\x0e:\xac\x05\xc9l\xf9\x02\xbb\xec\x8bAT\x81X\x80\xe3\xb4\x0b=\x0d4,\xedNO5\xee\xdf\x07t\xc8\xc7\x81FO\x9bIi\x88\x88\xe2\xa3\xa7&\xec\xebp2\x8e\x01\xe9\x82k\x10\xd6[\xe9Yq\x15\xb7\xe8\x8c\xa8\xaf\x0c\xf7c\x0f\x10Z\xe4U\x92\x1e\xb3\x0d(&\x15\xe0w\xee\xb0P\x117\x176\xdcp\xb0\x8aW\x8e\xeb\xe1\xa4\xc8_n\x87\x96\xd7X.\xda}\x80.\xeb\xa4\xab\x03\x16\xc9\xa7\xe8|\x89\xd9\xfc\x0f\xe8_7\xe0\xca\xaa\x9a\xff\xbd-y?\x11\xdd\xd2\x0e\xc0\xa9\x9dt\xec|\x93+\x89k1q\xfa\xb7\xd79\xca\x81\xc2\x9b;?\xff\x00\x84\x92;/\xfd\x97x\x0b\x91;;\xf7\xbf\xcf\xb3N\xc1\xf5o\xec\xdf\x8e\x1c\xac\xca:_\x13\xack\xf2\xc6u\"y\x1bl\xb1F.2\x0f,\xe1,fpU\xe6-.\xb9\xb4h\x1cwZuU&\xab\xcd\x7fh\x8642\xc1\x03W\x84\xbf\xfa}\xee~\x9c\xbdP\x93XA\x10)\xd8\xf87`\xa0x\x86\xaf\x12\xab\xa8\xf2\x9b\xa0\n\xb7Ct\x08~\xe5#\xd0\x9b\xdb<\x05\xd2B\x06\x1a\xd5#++j\xe3\xe3\x08x\x10%\x83\x1b\x1e#\xad\xbe\xaf\n\x89@\xc1:\xa1\xa142\x11\xbc\x95\x89h\xdc\xa6\xb3\xca6\xddr \xeb\xc434\xb2\x96-\xfd(\x97\xb7\xfc\x8c\xf5\x10\xd6\xba\xd2\xad\xc7\xa9\x02\x9c\xd2\x00i\x0b\xaf\xdcD\x8fY\xae\x81\xb3\xe0\xc0\xfd\xb2\xa7\xa9\xe4\xc0s\xc5\x81\x8b\xbcT\xe3\xc0surH;\x9c\x1c\x9aN\x0d\x96\x13\x03\x9c\x16R\xf8\xe8p\x02N>\xfa\xfd\xbc\x0b\xdd\xbc\xce(\\O}\x06\xce\x11\x99\xc7\x02\xb0/\x10hHxN\xee@\x0b;a8\x1es\x91\xcb\xc7\xc1\n\xb2\x14\x82\x18 \x93\xc7\xbbk\xe3<\x9e\xa1B8C\xb5\xb3\xa6)B$W\xc1\xbf\xe5)\x0d\x91\xdf_\x03\xf9eo6\x1a{\xd3rd\xc8\xf4\xcf\xe7&#\x9b\x13,r^e\x91\xd3*\x8b\x9c\x16,r^\xfe\"Xd\xb3ekO%G,f\xaa#xn\xb0e\xd9 9\xbb\xe6\xf2\xf2t\"nv\xf5\x07\xf4\xaf[\xda\x03m\xbe\xc1\xe9\xcb3;C\xfa\x82\x9b\xe9K\\\x1aY\x1a\x17_R\xdb\xcd\xb7j\xb1\xf5\\\x84[6m\x88\x16!\xe3\x18\xb4\xdcx\x97B\xd3\xb9\xc7V\x1e\xd8WN\xa5\x81\xa21\x1f\x8b\xa6\xcc3\xd0n(\xc7sf\xfe\x12\xf2\x95\x13\xc6*F\x97\xf5\xc0$\xbc\x99\x97S\x9cF\xe9_\x98\xc4\xad\x04|C\xa9\xa8\x0ep\xaf\xd4*\xa9\xa7\x9d\xad0\xe5\xb1/A3\xbb\xb4`\x9f\xb7<\xb69\x14[\xc3\x99\xbc}2/\x9c\"\xac\xc4\x9b\xa9s\xead\xb1\x1c8\x1a\x00\xd9Y\x83\xe1\xf2\x87\x1a\xf8\xe2H\xb9\xe9m\x87]\xe3\xf5v\xf2\x02%+\xcc\xdd4\x17\x05$\xcct\xc3\xbd}6\x9e\x81\xcb\x8aH\x19\xf1!u\x8f\\\xd4\xc1\x01h \xeeM= nH`\x91\x89tb%}L@\xa8|e\x93\xdfbD\xa3\x1e\xe0?\xect\x94\xf2\x15\xbb\x901\x0d`\xbf^\xa0\xf7\x8d\xd2%2\xac-\xf4\x07\x1b\xe0~%\xbd\x19'\x10M!\x8e2~\x91A,\xa6\xe44u\x0b\xfb\xcd\x04\xe3G\xc4\x88)A\x89BbNlq\xa2[I#\x86\xfb\x96k\xab\xcd\x0d\xc7\x19^\x8c\x94F\xe1\xd6E\x11\x89\xa1\xf3jd-\xe9\xffC5\xcf\xb8\x1da\x14\xff\x8c,\x05\x1f\x043\xbb\xe4O\xfa\xc2d\x8d\xf1\xfc\x01\x03q\xbb\x13\xadaOf\xe3\xb4t\xdb\x8b?\xe2R'ct>\x03W\x9a\xa9t\x80\xc8\x0e\x98\xd2\xec:\xe0P\xdcY\xa0\xe0\xdc\xde \x86\xf6lbnG\xb8\xe2\x1b\x8bbh\xe7\x06Q_\x89Ri\x89R\xa9G\xaf\xaeXF6\x88\x8b;\xc9nCI\x14\xc3\xd5/\xc7C\xf5n\xd7\x90\xf5Gk\x8c\xb7\xdc\xb4gr\\\xe8)\xdc\xc2\xb5\xa1\x087wBy\x9b\xd9\xf4\xfeB\x1d\xb6q+\xa6\xa8\x00\x97\xbc\xb4\x94\xb3\xca\xae.U\xb3\x1c\xe2\x03NOp\xc9E\xb8\x00}\xcd\x05\xf9\xb2\xc5\xfd\xcc\x07OR\xd9\xb4\x03\x95\x85\x95#I\xe1\x1adr0=\xa9Q\xca\xc1\xf4\xc4-\x0d\xa0\xc5\xcf\x02\xd7\xf1G4\x08\xc4\x96)\x9d\xef\x001e\xa3\x12\xa9\x89\xeb\xe38\x8a\xc2\x9bu\xfbvA\xb0\xeb\x14\xb1\x9c\x01\xb1\xbc\xba\x02BY\xec\x9c\x0b\xdd\xabv\x95\x84b\xa2FEU$\x19 \x98 n\xb1\xf5^\xb9\xbcn\xa7r\xa2\x0bD\xff5>\xa6\xe8\x0f4\xaa\xba\x13\x0b\x8cl_\x1d\x92\xce\xc8\x9e\xf3\xa2\xe7&\xea\x1ac)~\xde\n3k2\xad\xc8\xcc\xee\x191\x18\x03\x99^\xbf\xc4\xed\xcb\xf4\xba7]\x15K\x8c\x0epc2\xb9\x1dn\x0c\xc5N/[p\xf0\xd8/\xfe\x8fd$d\xb8X\x1fG\\\xfd/\xd2\xdd:[\xabB\x19val\xb5\x0b7\xc6\xac\xc4M\x99s\xea\xa6\x11S\xa62[\xca\xec_]\x0e\xac\x96)\x14T\x1c\xfc\xa3\n\xf2\xb3\x01\x91\x96\xe8k!w{\xac\x0f\xde\x1eX\x9f\xf5\xee*3\xcf3?\x0cfL\x0dv\x19\xcf\xb8q\xf1\x8d\"I \xee\xeb\xb65\x11Z\x02\xf4\xc2\xb0r\xc7/ES1:X\xf5\xa5\xc9\x14\xb1Q%\xf4\xe14\xc2\x8aC\x8f\xcde\x13f\x19\xd1\x95i\xabS&\xbd4`\xee\x98\xb2\xb7Q\x8f\x18BH\x04\x9c\xfb\x12yj\xce\xb8\xf8=b\x9f\xf1\x8cO3>cy\x14'3\x9e\xf0\x19\x13\x88x%\xb0\x8e\xdd)\"sC\xf8\x9e\\t\xcec\xe7\x8b`\xba`A\xc4\x002K\xff=O\x19F\x1fc3hMpC\xf1\x9c\xa5\xf9t\xca\xd3\xf4\xde\xdc\x0f\xc2<\xe1,X\xae\xe24\x0dNB\xce\x9c\xf3\x05\x8fD\x13wu\xec\xbe\x0b\x13\xeb\x1eE\xcf\xe3(\x0df\x80N\x04m3*?\x1c7\x1f\x1b\xc6 \x15\xbd\xc8\x02\x89\xb5N\x0e\x84'T\x9dc\xac\xf0\x96:\xbbh9S$k\x9d)H\x13\x97\x8fz\x8a\xa8\x8b\xa6\xa5\x90\xe0#\xe9\x89\x9b\x14\xb7JOY\x06\x90k\x06[\x86\xe7\xe3\xfa\xc5\xfc\xea\xe5\xf3\x9b\x03\x88p}\xa5NYm\x91\x96\xad\x86*\xe8\xf9\xfdV\xe7Q\x9c\xca\xd6\xbf\xbd\xd1\xe8\xa2\x1f\xaf\xe28\xe5\x15\x19p\xe8\xa6]\xfc\xd3\xa2\x895H\xad\xcd\x89\xa3\x0eC\xaf\xfd4\xe5\xb3B\x10\xa3\x05\x84\xc6K4\xc1\x9c\xcf\xea\xf1\x8cn\x17~{\x86JG\xcc\xf3\xbd\xf1Qt\x94\x1c\xe5\xdb[\xdb\x0f\xe1\xef\xa3\xc9\xbd\xd3u\xc1\xac\xd0_\xcc:\x89\xfb\x85\xc2\xe2)\x1bnm1\xe5\x80.\x93\x0eX\xb7<\xf6\xe8\x11\x1c\x13\xff\xdb\xef\xfc^O\xde\xff\xcf\xd4=iAq\x9b\x97\x8a\xfc\xcao\xbc}\xf5r\xa0\xc0y\xe9pW6?\x04\xc5Fm\x19\xdd.p\xff_\x83\x9cJ\xcf1~\x19G\x9b\xd3\x98'S<\xc6e\xb1DD\x17o\xf2N>\xea\x85\x8d\xdb\x88\x11o\xd3&\x96\xdf\x0b\x06\xb3 ]\xc5\xa6L\x85p\xa9)\xfaV\xb3\x81\x08 6\xa5\xa2\x9dg\xa7]W\xe0\xcc\x03\xa7B\x1e\xab\xf93\x05\x89#\xf8\xe4AY\x0b\xdbg+\xc5\x96.@\x89P,\xd0\xd4\xb2@\xd3\xe2\xc7\x01\xeb\xe1za#\x06\xbea\ny#\xeb\x8b\xcf\x17\x1d%\xf1u\x86\x0e\xd6R\x9e\xbd\x0b\x96<\xce\xb3\xf6sO!\x00\x8aH\xe1\n\xb7\xe9\xbb\xc4\xa7\x06y\x94\xf0\xb9\x18@\xf9\xcb\x81\x88\xa7\xe0UNt\xe6\xce\x1d\xd6\x8b\xf8E\xf6.\x98\xbe\xef\x81u\x90J\x86\x05\xa4\xba)\x12E\xc5\xf5\xfb/\x8f,\xcb\xbasa\xd9\xff3[\xff\x97\x95\xfe/\xb5\xfe\xb7hpj\xf3@.\xfb\xca\xd8f\x18\xef\xbf\xd0\x98\x8a\xb3\x15B\xc8\x80\x0c\xa7 \xa3\xd7^\x92A\x15\x05.\xf1\xcf\xb9\xd8XE\xb3g\x18\x1ct\x7f\x7f_\xcf\xb9\xba\x92Q\xdb\xcb4\xb1m\x0fvvv\xd8\x88M\x9d\xb9\x83\xa6\xe8z>\x1aGmI\xcc^\xb2}\xf6\xf3\x0f\xd2\xaf\xd6\x90m\xb23\x97}\x82\xd2M%\xaa\xa8\x03\x07t\xde9\x05\"\x18\xec\xd5\x15\x83\x01\xb2}\x0dK<\x16\xb4O\xbbE\xda!\x1e\x0d\xaa\xfb\x1aT\x1d\x0d\x84\x9e\xae\xb0\xabl\xa1h\xbb\xe6\xc4\xae\x8b\nA\x08\xe8W\xb1\xb3\x91\xc6\x03\xd2b\xae\xb2\x8c}'@Hu\x12O\x84\x1e\x0b5 \x05\xfc\xa4$\x9c\xa6\xdf\xa7\xea\x1eT\x839\xbd\x0d\xcd\xdaP\x96\xd5\xd1\x96\xdc\x8b\xd0\\I \x01bp\xec,\xbb4\\Ctn`\xb9\xe5c\x88q\xc6\xf8\x8b\xdf\xb7\xb2\x05\x1a\xbe\x98\xd5\x11\xf3\xd1\xda\\\xb3\xe0\xca\xa4\x01\x87\xd8\x0e\x9e\xb2\xb8\xc9\xb7\x08\xbf\x98r>K\xd9\xd2\xbf\x08\x96\xf9\x92\x15z\x8b\x0c\xa1\xf2}9\x1b\xd9\x1e\xde\xdf\xbb\xffpg\xf7\xfe\xde\xf5\xdbk\x07\xe76\xad\x17\xdd\xd5\xafx\x04bG\xee\xb8\x1d\xcb8R\xc4^\x9c\x14{q.\xdd\xc0Kk\xf258\xe5\xe6\x8d\xd8G\x13\x9bf\xc4\xd7\xdd\xfb\x02\x8b0X\x04\x99\xeaZ\xbb\xc1\xc0i\xf9)b\x0b\x12\xa3W^\x11\x0cr\x00\x99\xd2\x1d\xc2m K\xcb\xe46(\x9f\x83\xf6xW\xeb\xae\xb1\xb32\x044q\xf3\x01\xc2F\x9a\xc9y)\xff23\xd3\xa6\xcc\x10\xda*R\x1f\xed\x15\xa9\xc3\xedm\xb8\x0f\np\x02\x18 \n\x8e]\xae&\x02\xdcz\xff\xf7\x1f\xfc~\xafq\x1d\x9av\xef\x84\x1d\x85\x8e\xb1 \x82\xc178j{\x15D\x96a>\xabK\xb5\xea\xbe;\xd1\x05\x87\x1f\xdc\xe2\xc2N\xe4\xec\x0co\xe2\xdb\x93\xf4]/\x1a\xee\x1d\x1f\xf3\xf4\xcbx\x96\x87\xbcW\xa7\xda2T\x90\x1eJ\xc1EY\x0f\xc4\xd3k\xb2UQF\x00\x89*\xec\xb1X\xbd\x96\x1b\xd0\x07\x93\xdd\x08\x1cq\xb8}Pw\xf3\x1b\xcb\xac\xfb\xdb\x10\x95\xb3\xc8S\x1d\xc0\x90cd\x1f8\x12\x99r\x9c\xd2\xef+\xb5Ca\x9c\xc0\xba\x9f\xbe\xf5\x88\xe9/\xc7\x04\xa8}\x87&\x8b\xd3x\xb9\x8a#A\x0e)8\xa8\xe7\xd9j5b\x97\xc5\x0cZ\xcb\xf9y\xb6\x88\x93\xe0\x1b_\xf4\xe4u\xbc\xcaW#v\xd2\xbd\x1a\xff4\x8bF\xecx\x8d\n\xafV<\x81\x8fA\xcd\xf3n5\xd3\x11;l/\xf9,\xcf\x16/2\xbe\x1c\xb1\x8b\xf6\xc2\xa2\xd9C4{{\xdb^:\x16\xc5\xb7G\xecY{Q\x7f\x15\xfc&\xbf\x14}\x19\xb1\xe7\xed\xc5O\xfc4\x98b\xe9\xf7\xed\xa5\xe5\x91\xe4U{\xc908\xe3ox\xba\x8a\xa3\x94\x8f\xd8\xeb\xf6\nA4\x8fG\xec\x8f\xb4\x17|\x11\xcd\xe3\xe7\x18\xd8\x9d'#\xc6y{\x95\xdf\xc8\x97\xabw\xf1k_\x8c2\xebP>\x8e\xc2 \xe2?\xf2\xc3`\xe6gq\xf2\xa9?;\xe5#\xf6\xaeCE\x85]\xe9\x88}\xb9F\xf1\x11\xfbi{\xe9\x02u\xdf\xe6\xcb\xa5\x9f\\\x8e\xd8\xcb\xf5+} A1G\xec\xcd\xfaU\x11~\x9f\xb5W\\\x04\xa7\x8b08]d\x82\xe1\x18\xb1\x9f\xb5\xd7H$\xa6\xa4#\xf6y\xf7\xd2#\xf6M\xf7\xc2\x9f\xc6\xb3\xcb\x11\xfb\xb4\xbd\xc2\xcaO\xfc%\xcfx\x92\x8e\xd8\x8f\xd6(\xfe&>\x1f\xb1\xdfh\xaf\xc0/\xf84\xcf\xf8\x88\xfdV{\xd9\x05\xf7g\xd0\x91\xdfl/\x0bF\xb4\xe9\x88\xfdZ{Q\xb8\xc5\x17e\x82y\x1d\xb1\x1f\xb6\x97\x8f\xcfxr\x16\xf0\xf3\x11\xfb\xed\xf6\xc2\xf38\xce\xc4\xc2\x8c:,\xb4\xcf\x830\xe3\x89\xb6\x9a\x93\x0e\x95^\x0b\x88\xe3t\xc6\x1d\x8aO\xf3$\x1c\xb1\xa0C\xc9t\xba\xe0K\x81\x83~\x87\xc2o\xb1\xb0\xd6\xf7\xbcC\xade<\xe3\xe1\xe1\x85\xbf\\\x85|\xc4\xc2\x0e5\xbe\x145~\x9c\xf8\xab\x95\xf8\xc6\xb4k\x8d\xe7q\x18\xfa+\xb1F\xd2\xaeUFl\xde\xb5h:b\xab\x0ee\x0f\xa3|)\x9b\x9eu(\x8e\x8c\x8e\xac\xb0\xe8P\x01\xcc6e\xf9\xb3\x0e\xe5\x0bg\xf7\xb2\xce\xb2S\x1dd\xb8F\xec\xb4C\xe9w\xc9\xe5\x8b\xecU\x9e}\x9ag\x99 \xeb\x97\x1d\xea|\xe9'\xefg\xf1y4b\x17\x1dJ\x7f\xea\xa7\xfc\x0b\xff2\xce\xb3\x11{\xdb\xa1\xfc\x8fx\x92\n\xde*\xf1O\x97>\xae\xb7\x11;\xe9^\xf1m\xe6/W#v\xdc\xa1F\xb1a\x1c^d#\xf6\xc5z\x15\x80|~\xd5^\xe7\xb5\xa2\xb7\xf0\x91__\xa3\xc2\x8bh\x1a\xe63~\xb8\\\x89\xd9\xfcq{\xcd\xa2{\x10i\xe4\xc5\x1a\x154\xaap\xda^\xed3\xceW_\x04\xd1\xfb\x11;\xef\x00e\xc1\xff|%H\xda\x1f\x1d\xc8\xd7\xe6\xb2\x02ap\xeb\xc6\n\xeaw\x03i;;}\x96\xa6\\p\xf8\x87E\x87\xc8\xd2\x9d\xe4\xd8\xb4\x9frV;K<\xef\xa4F\x88:\xb5\xf5\x9eh\x8b\xd4\x1c\x8dg\x05\xbc\xd9\xbc|M\xcbW\xbf|\x0d\xcaW\xeal\x8az@\xf9\x8a\x87\xbb\xb0L\x88<6-\x7f\xad\xca\xd7E\xf9zV\xbe.\xd5k\xe3\x89\xf7\x15\x87\xe0\x03\x8f\xa8#/\xe6m\xef\x1a\x11\x8e\x8a\xbc\x9d\xedz\x9e_\xe4\xdd\xdf3\xa2\xe5\x14y\x0f\xef\x1b\xf1\x80\xca<\xe3\xf8\x1d\x96yF_\xa6E\xde\xa3\x9dz\xde\xbc\xcc3\xfa\xb2*\xf3\x1e\xd6\xf3fe\x9e\x01\x97\x85\xca\xbb\xbfe|\xef\xac\xcc3\xda\\\x16y\xc3\xadz\xde\xa9\xca{\xb4c\x8c\xef\xb2\xcc3\xc6pR\xe6\x19\xdf;.\xf3\x8c1\x9c\x17y\xf7\x8d\xbe\x1c\x96y\xc3z\xdeE\x99g\xcc\xfb\xdb2\xcf\x80\xcb\xf32\xcf\x98\xf7\xf7e\x9e1\xef\xcf\xca<\x03.\xaf\xca\xdaq\x07\xdc\xebv\x11G\xab6\xcd5\xd9\x1amW\xc7\xceQzs\xa8\xc5\xe8=}\x10\xa0\xad\x1a\x04D\x10\xa0\xadj3b\x1a5w\xc9\x807\xbfU5\xb2\xf5x\xfd]ugDN48\x81\x1eD\x837\xf0\x03tX7#\xd7\x12\x8e\xa3\x00X)\x8d\xb3\xdb\x87.>\xaa\xdd\x02\xb2\xaaM\xf1\xc1\xaf\xf3\x14Y\x11\x8f\x84)\xc3\xf6\xd4j\x82\x10\xaf\xb4F\xf5\x98\x06z\xc2\xff\x8c\xf9H\xf5-\\j6\xaf\xbe&\x13\xc9\xd0\x19\x14&\xc5\x1b\xd3\xd1\x0c\xc6\xc2\x82D\xff\xda\xaalar\xad\xaf\xb54\xe7\x05ab\x9b\xe7\xac5\xd6\x1a\xec\xe4Y\xe5\xae\x1d\xb1s\xdd\xc7\x01n\x96\x06\xb8\xa9\x0c\x106]\xb7_$\xa9\x86;\xb8\xbfg0\x14.\xe7\xac\xa9\xcc\xb93D|\xc1\x83\x0c\x83\x9b\xd1\x1b\x98\xa3!G\xe2\xac\xf3\x00x\xcf!\x85\x97\xb0|\x0e\xcb^\xcf\x05\x8c\xea\xbe\xec\xc3\n&p\xed\xac\xa7\xcbY\x1f\x96\x8c\x8c\xb0\xaf\x86\x10+\xe6^\x99\xf4-\x0e\xc6\xb5p\xf7\xc7A<\x87\x0e:f,\x06!\xbdM\x1d\xd7E\x0f\n\xcd\x10\x88\xb3@\x17\xadi4\xc0\xab\xe8>\xb0\x01q\x8b)Q\xa4\x19\x944b\x924}\x9f5W\xc9%\xa6\xe0\xfd7!\x1b\xd5\x8d\xcd\xc9\xc6\xb3\x9d/<\xc10{6;\xc9\xe3\xc1B\xd4\x89\x9c!\xab\xc8\xa6NyT\xeb\x07\x12\xef\xd0\x19\xed\xed!)\x15\x14\xf5\xd9\xa6 \xac[\xe2\xef\x9e\xf8\xfbTKh?p\xf3\xc46]Y\xc0\x95\x87\xcd\xec\xcb0\xbf\xb5\x88i\xbc\xcb\x9a\x83A\xa0'\xd0\x92$VI\xe8BO\xb8\xd7\x82u\xa9\x14\xcf\xf9zU\x87r)\x1a\xa9\x96_\xf3N\xb7\xab\xe5+A\xe7\xab\xe5KQ\xbe\xe3\x0e\x12ZQ\xcb\xde Z\xbf\xe3:U^_\xf4^\x9d\xda\xb9h\xad*Y\xde\x88\xf2*;u\x88\xb1ws+\xb3\xf2\xc3[\x1eI;\x8e<\x9aT\x82q\x9e\xe0#\xb1\xee\xe5G\xaf\x18\x05\x17/!\x01\xf7\x9c\xdb*w_1\x0f\xa9(b\x0f`\x1fw\xc9\xc5`Q~p\xcc\xd8\x97\x8e\xdd\x04T\xef\xcf\x0e\x8a\xdd\xc9\xc9\x00\xa3\x8f]S\xa7\x8aG\xea\x87QC\xa7\x9cZ\x17\xed\xa6\xa6\xa13z\xe6*\xb9\xcbg\xad\xac\xfd\xe4\x87:W}\xb82\x1b\xc3\x1b\xa2\xe1\x08\xc2\xe5\xbcb\xf4]{>\x8a\xb5\xf8H\xff\xe0\x11\xd3\x0e\xafi\xc8M\xdb(w;\xbbr\xd5\x94\xa7\x9a\xa0\xf7\xe6 \xc8\x9f\xab\xe8\xf7\xa1q\xce\xd7\xf5\x8c\xa5P\xcc\xa3\xe3t\xd6\x0e\x8fi\xa9\x8b\xea\x84G\x11\x1f\xb6p\xa2)\x0f\xa7<\x98\xd3\xa6`\x85 M\xf0\xe9\xe0\\\xebM\x0bH\x83\xcfCt\xa7\xd4/\xc0\xb5\x08xH\x07\xe7\x9e\xbe\xc6]\xb3\xc5-\xa8\xd2#O\x18z~\xcd\xcd.\xd1\xd0\x91\x0e\xce\x93RZ\x8c\xbcE\xa37\xb9\xfc\x08c\xd8\x82|F\x18\x817\xba\xc2\x98\xa5\x0b\xe2[nq\xe4'\x11\xf1.ps4W\x0fDu\x86p\xcd\xb5=\xac=\x8fV\xc4oH\xede\xde\xc1\xea'c\xf2\x0c\x1at:\x9b\x02v\xe8\x14\xfb\x07\xda\xb5\xe2\xaf}tj\x15\x0e\xb2\xac>\x97\x83\xc6\xe0\xa0\xb9\xbd7\xa0aJcG\xf0\x1f\x19\xba\xbap\xdfPo@o\xfd\xd4\x11\xeed\x9d\xa1\xcb\xeb\xb0\xdd\xa6\xd8\xe2\x07\xce\xa1\xd3\x15\xfbn\xc3\xbb$~\x08\xde\x9d\x17\xd0.\x0fI\xcd\xd6\xf1\x83\x13rk\xd8<1N\"\x9cA\x13\x87\x9f\xd8\x81\x13\x9b\xa9\x01T\xf7e#Xp\xfc\x1d\"\xe6'&\x11\xe8\xdc.\xd5\x8f\xde\x95\x07\x9f\xd4\xf8\x8d\xc8\xb7\x08\xaf\xec\x89 O\xec\xa08uR\x94D\xad#\xff\xd8n\xe4\xfch\xd2\x0f\x9e{\x15\x0e\xce\x8d\x01=\xc3bR(`\x8b9\x19\x8e_\xfb\xb1\x8b:q\x19\x98\x99o\xac\xe2\xf0\x03\x8f\x84\x8f1\x8c\x98`\x1e\xe6\xe0\xa7 \x0d\x16\xb60\xba\x08\xe7\x0f\xe8&=i\xcb<\x81\"Z7\x9f\x85\xe77c\x08\x9b9\x93\xf3\xf9X\xcd\xf1\xaf\xfb\x18\xb8r\xf9i\xc7\xb1\xa4\xf9E@\xe0|\x14\x01\x9e\xd9\xf7#\xf1\xfd[\xb2\x01Gy\xbe\x8c/?\xf9]v\xc6\xe4\xe8\x1fr\xf4\x1f1\xfc\x0e\xfb\xd01\x8d\xb7\xdd8\xc5\xf8\xec\x13i\xb1~\x0dk\xf7\xd98\x7f\x8deQy\xbb*\xfe\x11\xb8\xd7O\xac\x1b\xf6RD.>\xe9\x83\xdc\x14\xdd>t\xcf/\xbbn\x1f\xe6\xdc\xd5Jx\xcc\\\xfaU\x17;=\xfaP\x07\xd1\x84\xb7\x9bc\x8a\xfcY!.V\xa0\x1f\x15=\xd7\xe0\xa1\xa8\xbb\xfa\xfc\x107O\x925Ppv\xfc\x97z\xf2\xf2\x92\x84\x8b/\xfc\xc7\\\xf2~\xf8\xeb\xbaV\xf9R\xad\xcc\x19\xc5b@nq\xa5&\xd4\x1d\xbb\xaes\xa2\xc4\x8c\xaa\x8d\x8f\x86\xe3fQP\x8ar\x07\xceJ\xae\x9ak\xd3\x15FWe\x9dtGI\xce\xca\xcey\xb67\x98\x80e\xd4\\\xe3\xd9\xc9jq\xe9\x07\xd9\x18v\x16\x8b\x9f\xe3\nL\xbc\"\x97\x8f\x841k\x80\x7f\xad>K\xd8\xb3S1\x8f\xceH\x0dTS^\xe7\xf2>Bti\xd2\xdc\xcb\xebH\xd6\x11\xaa\x10\xe48\xcd8$\x82\xe8\x18\x89\xb9\xd4\xc1\x84\xf4\xa6\xea\xb8\x89\xdd\x14\xe9\x07\xa8\x98\xa18Q0\x04\xecG\xbc\xaf\x1a\xb9\xf9#\xc6\xa4\xe0\x93#\xf1D\xc5\xe6\x8b\xc1\x82\xad\xb2\x15\xa5\x8b\x08\x0f\xfb\xfb\x80>r\xfc+a\x1c4\xbd\xe1\xbe[c\x0c-R\x9a\xe4\xc2Y\x0c~\x82\x1e,\x06\xbf\xe1\xffx\xbfr\\E\xc8\x0f\x92):)\xbd\x1c:\xcf\xf6\\G%\x15B\xbb\xba\xeb:j\x11\xa9*Xy\xbf'\xa5\x1e\x15rS\x9d\x1a\x83N\xd3\x1aK\xfe\xe8@G\x98@\xd1<1\xf4\x14\x10w\x1d\x1e\x8aD\x8bg50\x15\xc3u2\x06\xe0\xce\xb1k\x1d5.w\xd3\xb0\xc5\xa8n\x9cL\xee\x8d|\xd9Nro_+\x9aV \xe9\x1c\xb3\x86\x1ao\xc8N\x06x\x84\xbb\x03\xdc@\xce\x95\x8a\x15\xb6i\x91 h\x9a\x92\xca\xa9\xea\x0f=N\xb4R\x83\xd2\x92\xbb\xf2Z\xb57\x91\xa8b\xd6\xd8\xf8\xed\x05UIFm\xb9 A4iI\x90\x0f2\x96\x8b\x99\xc5\xbaf\xa4\x9c\x9d\"\xed\xd5\xac\x18|\x01\xf6\xc1\xef\xf5\x9a\x19\xc0\xc4\x90\xb6C\xfd\x88\xec\xc9\x9c\x02\xb2\xbd\xd9\xeb\xf5\x0be\x19\xc3\x88\x96\xa9\x0e\xd4O\x82\x9cE\x92'q\xc8D\x12\x89\x8d\x0d\x94/b'lb\n\x8d23\x084W\x9a\xd2\xd6\xd3eG\x90.\xc6\x03\x1e}\xc2\xf1\x07\xd7m\xcf\x95\x98x\x8d{\xf7[!\xba\x19\x8b\xa3\x07`\xf1\xc3q\xab\xbe\xea\xc5\xb6\x03\x8b2O#\xdd\x82}\x05\xa2\x81\x08\xc0\x1b\xd9V@!A\xf8\xf5KmMtgu\\\xdcuc\x94\xc1\xf2P\x93\x1b\x1f\xb9\xce4\x8f\\P\x87\x9cG\x12\n\xc3\xb1~%e\xb8\xa1 P\x8c%L\x85\x9aT\x03\x12lg\xd4\xa2\x9dt:\x9c\xa9m\xf5!\xd5gd\xc7\x167[\xb6\xc8Z\x19i\xda\x15\xe5\x86\xd6\xb7\x1e\xd4:\xfb\x7f\xd3\xd8\x87xj\xe8i\xfb\x0bzb\xffo5\xf4'\xea\x180N\xe9B\xc4=\xc66\x94SQ\x8b\x91f\xbb\xb1\xea\x8d\\d\xb9\x1d\xc5\x14\x84\x83\xf7Y\x8a.1\xc7\x17 \x8d\xaf)\x06v\x88\x07\xbf\xd1\x8b_\xfc\xb4\xfa\xac\xfc>O#\xad\xbd\xde\xcc\xf0\x91\xf6z3\xa9^o\x86\xce\xb3-\xd7!M\xd7\xf9ZNX\x1ay\xb5\xca+\x19\xf7ui\x13\xf0> \xa5\x00\x94\xde\x88\x90*\xa4\x06\x16o\x00\x9e\x035&\x98\xe6J\xeeE\xd8G\xbe\x9c\xa2\xdd\xc5\x97(\x88\"M\xd2\x0cPEScl4\xc8\xa3\xd5cl\x1c$\x04\xa9\")\xb6\x8d>V/)\xb5\"\x00\xc2\xaf|\xca\xf8\\\x9e\xaf\xbf\x00'qy\"D\xdb\x9a\x90\x81\x0cv\xe9\x04\xd6\x06\xf3D\x1e\x1d\x9fcgH\xae\xfd%I\xa5n<\xff9HR\x12\xceI\x10\x85\x1a\xad\x05\xc6\x7fC\x83\x1ey\xda\x98\x00z-\xf2\x7f\xe5\x15\x1d\x83\x1a\xaeq\x8a\xf2\xe3\x89\xc8\xa5\xadu)|\xce\xad\xda\x8frU\x95.M\xb5\x06\x92\xfa\xdd\xb1\xe0\\\x94\xb6\x8b5\xec\xc3<\xf2x\x94\x1c\x1e\xff\xeb\x94\xde\xa6G\xd1\x9c:]\x9d\x8e\x92\x8b~\x81;\x888\xe5p\xd6\xba\xb0Q\xec\xe3]\x92\x98x)\x8d_\x93\x94\x8c\xaby2@J|m\x00\xb1\x1e\xccI\x8a\xb7\xbel*\x8b\x06\xfc\xd6\x12\xe1\xbc\x0f\xedf\xbb\x16A\x08\xf5\xdd/\xc21\xc4\x06~\x0cS\xb2\xf2\x9d\xd4\xb4D\x80\xfb\x8e\xc7\xb2b\xef\xc1>\x86\xcf\xa5<\xfe\x0c\xcf\x0e\x1a\xa2\x9e\x1c\x1f\x19\xe6\xd4\xea\xdch2\xbd2\x9c&5\x93J_o\xa8\xc5\xc5\xef\x9a!\x8fLA\xae\xda\x804\xd0\xfe\xdaN\x95,\xb0>\xc1,\x8f\xa8\x15\xf1\x88Zq-D!W\x07\xe1ej\xcaD\x06\x8cf\xbapR\x0c\x93\xaaa\xc0\xa2p\xe1/\xb3\x98\\p#\xdb\xfa\x12/i\xda\"\x0c\xa0\xa2\x0djB\xcd\x07\x9e\xff\x8d\xeb\xa87\xa13\xaccm\xd5\x89\xc1\xf2*\xcbm\xa2\x8aNc'\x1e|\x80\x1e\xc4\x83\x8f\x16i^\xa4\xf7j+\xe8\x10\xa1\x9e\x8b$G\xc1\xf6\x82/\x7f\x18\xa4\x9c\xd0\x84\x1e\x9a\xa0c5E]\x08\x93blF\x93\x17\xf1\x1aOH\xe0\xb8U\x11\xd6v H\xe5\xa8\xb6\x82\xee\x1a\x8f1\x99}\xf8\xee\xe3\x12\x91\xd3\x1e4,\xb3\x96\xe8;\"o\xddt\xcf\xcfM\xf7\xca\xe8xbA\xc44n\x8d\x84\x11#\x11\x987\xda\x88n\xbe\xd6\x92A*\x00\xc3\x01E\x93\"\xa1u\x1d\x17r\xb0\xeb\x84(\x9f6k\x04\xdb\x00T\x82\xce\xba\xde&b\xf4\xd9A\xa32\x99_\xc2\xe9*\x15\xbb5+J\x0c\x01?\x88\xe9\x92\x864f\x0c\xd8\xc7,L\xfd\x15\n\xdd\xc2\xa9gIS\xc5\x95\xe7\x88\xach\xe2\xc4\xee\xc0\x0f\xe7\xf4\xf6x\xc1\xda\xaf\xbe\xdcu\xe1eM\xe3\xe5\x83\x08c\xa7\xeb\xae\x809&{\xd1\x0d\xa8\xe0c\xcb\xd6\xb7{\xec\xd4\xc2\xb4\xec\xfa\xb7\x94\xc8\xf9\xc8;\xd5yx\x11}S\xf7~\xb1p\xc6\xeb%\xeb`\x8b\xf7\xb5\xeb\xae\xb6\xa5\x18u\xd6\xeel\xf4;\x0c\n\xa37tU\xaf\xf8`\xd5\xb1\x9c/v\xd95\xab^\xcb7\x91\xdd\x93\xbb\xd5E\x14\xc0D~\x19\xd7\xccVA\x9c5\xfe\xc0O9@\xd0\xbe\xf1?\xffS\xfe\xec\xd6\xeb\xa3\x8e\x92\x87}}[~\xa9T\xa6y3\xc17e\xb0\xc3S\xb2\x14\xef)%\x9a\xb7\xf0\x92*BX\x95\xce\x94zMOX\xf7\x99\x91\x15\x04\xc2z.\x04\xc8\xf0\xa9\xa8\xe9\xb9\xad8w\xc7\xd4\x0d\xecC\x80\xb9\xa6d\x93\x0c\xde\xee\xe0&&\x8c\x99?\xaf\x93))\x03t\x93,Y\xd3pN\xe7')\x89S\x0d\x0c@H\x04E\xcd\xbf\xfa4\x98\x1bj\xa2C\n\x8f\xa9\xe4\x87:\x90\x820\x06\xefz\xd1j\xcd\xf6\x92\xa9\xa5k\x9ePA\xfbl\xa5qC\xc4\xf2)\x995\xd1Bhb\xce\xf4\xc0Z\x16\xbbfI\xd3\x0fr\xe3\x1c/\xf4#\xbc\x83}X\xb2e^:K\xe7\xbd3\x9d\xb9\xbaKS\xf48\xb9C\xb3(\x14n\x85pw\x87I\xb3ej\x91;\xcd\x8blD\x17h\x9c\xad\xde\xf9\x1e\x96~\x95\x028;+M+\xb7\xa5\xfa\x17\x15\xeb\xed\x93>\x9cT\x8an\xfbp2M\x18\x88o1MW@\x90\xc6\xb3\xe5\xfcIb\xa4(\xbf\xf8\xa5\xcf\xd7mp6\xc3\x83\xd2\x19\xb2\x0fW8m\x8c'\xaeu+\xb5!j$n\xe8\xaf\x9cs\xf5\x0d{dh\xed\xde`\xa7\xf9\x04\"t\xca\xe2\x1e]\x0f\xb9'\xcbU\xcb\"\x9f\x0e\xe5\x8e]Jk\xfa%\xd0\"\xf7+\xc4\x8f\x8b*vuY\xd97 \xb2}\xb8\xc8O\xe3\x074\xd6\x9d\xf2\xd3\x18\xf2\x01Ur\x1e\x82\\\xe0+z\xd7\x9c\x8a\x04\x14R35\xa46\xa8\xf9\xaf\xa7\xd2\xa8\xc4\xba\xbe\xec\x94\xbe\xa6qB\xab\\\xb4\xfa\x91\xa3\x83f;>\x91\xd9@\xde\x1d\x19\x15\xd4\xeaG\xca\x06\xe9`\x1d\xadMZM\xf5\x83\x0c\xb5\x98fn\xd0\xc3\x91\x08\xd3h\x84\x1c\xb5\xb8\x91\x92^l\x94\x1f\xb3\xa5\x1c(\x02q\xde\xde\xd0\xd6\x9e\x96Hx|`l\x91\xdf\xf7\xe1\xb4D\xe8\xf4\xa0Q\x0e\x8c1\x9c\xeaW%\xa6 m\xb4\x02\x91\x1f\xccz\xc1\xedp\xe8\xb5b\x9a%\x14y\xf2gBCy\x81;8\x17?B\xf1L\x81'\xffM\x03\xba$\x18\xa5\x84'\x92\xc4\xd2\x15\x86 \x95\xd9\xc0\xba\xa2\x94\xc4K\xa5\xa54\xbe;\x0c\xd3\xd8\xa7\x89\xcc\x97\xec|p\xfb\xd0i\xb0h,\xa2\x9d\xb3uG\x91\x17\xbaiWxo\x88P\xdbCW\xe1N\xb8v\x86;Kux\xea\xb4\x9eL\n;\x12 \x86X\x1d\xe1[i :z\xf0'i\xb4n\xa1\\\x03i\x00\x95\xa3\x8f\x19\xb7\xa5\x0dU\x05H\xd3\xe1l XP?\xb2\xb8\xd8`*}\xd4\x93p\x98\xd0\x01\x1eJ\xf2\n\x86-\x82\xf9eU\xd3\x14_\x93zb\x020\x83\x821\"L\x8c<\xbc\xf5\xe8:\xc5\xa8\xb4\x0f\xc4J\x06\x9c|\xa0v\x00\x156\xdf\xcd\xb4*vL\xa9\xf6\xd5\x8f\xd4J\x0d\xc4\x96\x140\xecC&\xf0\x16m\xc4\xc5NA\xef\x11\xae\x04\xaf\xa3\xba\xc4s\x86\xcc\x1d\x8b_\x85y\xe4\x12\xc5\xfd:\x1aHg\x9d\x0d\x18=\x07\x1fU\x11\xcfacC\x1b\x17B\xfd\\\x8b\x1c\xffU\xac\xf2\x1b\xcc{@H\xb1\xa4\x15\xf2\x81D\xc08\x8a\xc4\x9e$\xac\xb7w\x91\x97\x13\xe8\xd8\xe9\xd2pn3\x1d\x97\xad\xc8W\xe1\xc5>\xe4d\xabi\xa2 &\x8b\xb9kD6\xf4>tQ\xc3\xf1.\xf2\xba\x96\xd3M\xfd\x04\xe5\xd7\x85J\x18\x1bhw,\xe1\x9dm\xd0f\xb4P\xa3\xcc/0=/\x1f\xb0\x02\xb7\xa2\x10\x1d\x10\x9a\xc7\x01\xda\x96\x8b\xb9\x94\xdaV\x8a\x1b\x1b\xfe\\\\z&\xdfs\x8a\x8d\x0d\x7f6i\x1et\x1f\xbc\xa3\x0d\xd4\xfc\x1b\"\xf7F\x1a\xdfA\x92\x92\x94b\xd6\xf4\x1b?\xbd\x8c\xb2T(\xc5\xa2X\xde\x07\xb4Yy\xf8n\x10\xb7\xd6\xb0\x98\xf9?\x84\x84\x93\x8b8[\xa7-l\xac\xe5G\xe15\xed\x94*\xcc)\x95\xf1Z@~r&\xb0B\xa9B\x03\xbf+?\\\xb9\xaa\xa1\x18\n+\x10W\xb6rny-\x96*.-U3VI\"m\x10\xe8\xd5\xcfEL\xc9\xd57]D@}&\xa6)\xc5\xc6\xc5y\x8f\xfa\x02\x99>\xac+}z\xf0\x16Q\x01\x0e\xc8\xd4%\xbe2el\xcc\x17\xac\x9c\x05\xdb\xe5a\xe2s\xd7\xd7\xfc`@-^#wA\xe4\x11K\xfb@\xc4a\x99\xf6\xb11\xc7\xc2=\x8a\xa3W\x1do\x1f\xae]a\x0e,GA\x1d\xf2 \x06N\xbe\xf6\x00\xa4\xff\x16\x1cVi\xc58<4\xcb\xc6\x1fLJ\xf3\xc7\xf6a\x0c\xe2\xea\xa3R\xd3\xc9Y7\xb9\x83\x04\xf3\xc2\xfe\xd6\x98s\xd1D\x19\xc0\xfctf=\x84Q\xbc\"A\xa9\x07y5\xed\xa8o\xa4n\x1f\x0c\x1e\x7fz\xa0/\xfc\xd0O\x1a\xfd\x13\xf2\xda\x05\xc7o'2iNd\xda\xf9\xd3k\x88L\xda\x82\xc8\x84\xea\x8e\x11\xdbKe\x9csL\x0c\x95\xad\x81\xc9\x89\x17)\x8d\x19e\xe9\xa3\xe3\xb8 h\xf0P\xb2\xdd\xca\xdbC~\xfe\xfd\xa0)\xa8\x92\x80d;\xa2\xcb\x8d\x84\xdb\xb2\xa4\xa0\xd9\xb5\xb1\xd8\xb5\xcd\xfd\x81\xa26\x8b\xed\xbb[\xfd|0\xd9d\xab\x1f\xfb\xb1\x0e\x05\xc10\xcb\x11\xf0\x85GG\x8d\x0b\xf2\x03&\xca\x07\x82\xef!iJW\xeb\xb4\xfb j*\xb5\x01x\xe32\xae\xea%\xad&\x82\xea\x0eR\x94\n\xf6\xe5\x91Woc\x8c7`\xe7\xecc\x9adAzDVt\x0c\x0d\x01-\x18]{\x17yc\x83m\"p\x85\x0e?\x9d\xb8\xe2A\xa1\xab9u,\xc4@\x03q\xac\x95VM\xc0J?sy\xf6\xbcA\xcd+q\x95\x9f\xf1\x8a\x9eI\x89\x0fs(\xf2\xe6\x1d\xea\x01Q\xcb\xa7\xe9D\xaa\x82[\xfb\x0e\x11Z\xe5S\x07\xef8\xa7:[f\xb1\xc8\xfe\xe0\xdc\x0f\xaf#\x8c\x02j\xb3\x15P?\xb9\xdd\x80U\x8b\x99\xb7f\x8a\x95(?\\s\xc8\xd6n\xae\x11\x08rm-\xf8 \x90 \xa6d~\x07q\x16\x86~\xb8\xb4\x89\x01E\xabZc\xf9jU\x95\x1e\xe5\x19\xc6\x0d\xd9\xf0\xe5GL\xf4\xadA9\x0e\xcd\x9a\x85\xb0\xe0\x00\"<\x96\x10O\xfd\xe7\x8d*Z\xc9\xf6\x85\xf9\x06m&\xef\xa4\xa9Q\x10\x0dg\xe8\x14B\x18\x064\xd3W4\x96m\xd32\xc8\xca\x08\xe3\xeb\"\xafns\x1f\xa0(\x85\x1a+\x7f\xa9x\x06\x12\x13\nZ\"\x97\xc7\x85Pjb\xc3B\x0d\xdb|\xfe\xe4\x92\xb9\x8a]E\xa3\xcd0+\x90x!q\x92m\xbc\xcb~\x9b\xde\x01\x9d\xa9j\xba@\x07_m\xf0v\xe2C/1\xb6\xa1BU\xc3\x01\x97O\x9d\x82o\xe5\xad6l\x18\xd8\x87\xb9\xbd\x8a\xd4\x17\xdd\xe4D\xa8\x19\xb1K\xdcq\xd2\x9a\x99\x10\xc0\x957 \x13\xb8\x841\xac\xfb \x8e\x8b\x87\"i\xe3u\xa6\xfa\x11I\xfd\xb0\xabvZ06\xc6\xb1\x18k\xe3\x0b_\xb3\x07T\\MrQ\xc3\xc9\xf1\xae\x90Y\xa4ZV\xd2\xad\xc4\x8eX\x06F\xbaV\xfa\x99-}\xd8\x07\xe2\xf6+\xc97M\xc7\xf0\x8d\xed\xc42;S4\xaeX\x8ai\xb5$z\x99\xd7\x89\xc4\xcb\xdc\xb3\x07\x87\xd1v\xa6\x8d\x11\x1c\xda\x0eQ,E\xc3\x08\xdb\x0e\xab\x15\xd0\x0f1\x9e\xa0\xe1\xe1\xad\xed\xe1\x89\xed\xe1+=0\xa6R\x01\x91c\x9d$=\xb3\xfc\xce\xcal\xd8&?\"hg;\xf1Le\x83\x05\x93\x84v\xb2\xadW\xb7j\xee\xaa\x9f\xf0\x95\xc5\x9a\xb4Nu\xd4\xd1\xa83\xb1\x19\x1a\xe4]\xf9\xad,\x8d\xe9\x8dt\xa7W \xda\xc0\xc3A\xc9\xb2\x90\x07\xbc\x8ey\x90\xbc\xa6\xd7@\xe1:n\x1c:\x0dg\x18a n\xc9{Hr\xd5\xd9\xdf\x177Fm:\x04\xe5\xa8\xc9\xda\x13a\x10\xd7\x11 \xbf@n\x1e!\x14pE\xcb=\x8dE`\xa0(E\x03L\x05\x8bV/]\x17&r\x1dr\xef\xa2` \x9e>\xc8\xb8\xa3\xfaI\x1d\xb9\x99\xa8X\xa2V\xaf~~\x88\xeb\xae\xfaI\x9d|\xd3>\xacC\x17\xc6u\x10|\xd5\xd4\x93\xdc$\x01C\xc9'-\x07\xd2j\xc8\xcd\n\x04\xe2d-x/\xb1w\xd2Z\xb0\xf8R\xad\xb6T\x08\x14J\x06\"K;\x87\xa0\x8f{z\xcc\xa8B\x9dv\xb5\"]\x07\xd6\xc8/<\xec\xa6\xd4\x0bL\xe5\xfd\xacF\x11U\xb0\xb9F\x99\x13or\xea&\x0e*\xb3\x92\xb6`\xac}L:/\xc74\x10\x80\xa9^\x1f\x17\xca\xd8\xc2PB\xcc\xd5\xd0e\xaev\xbc6\xd3\x84T\xc3:\xe5\x1d\x943\xd0\x9f^\xd2\\\xa1\x02\xf3\x88&\x10F)\xac\xe3\xe8\xda\x9fS \xf0\x18\xdf\x7f\x0c\xbcA\x93b\xc8\x86\x0b\x9aH}\xdaE\x8c\x90*\xc7}e%\xc5\xa85\xf4\xb9&H\x0bz,\xf1\xcf\x02\x80Hh\xc5\xebK\xac\x81\xa8\xbc\xeb\x89\xf4B\x90Tm\xe0\x95\x88\xe0\xed\x9dt\x8a4D\xe8\x9dfx}!\xe2\x99\xa7\x85B_\xa8\x9b\n\xee\x02\xcf\x95\xb4\xa4P\xb2\xdb\x19\xe8f\xc0\xb3\xcd\x8f\xcb\xef6\xa0@\xbe\xfc|\xd0\xe0s\x1c !\x88#\xc4\xd4W\xab\x9d{lwa\xd1o \xae\x1d\x1e\x03\x9d\x0egu\xf4\xa9\xaf\xc3\x88\x9b\x9ar\xa0\xc9\xcbd\xcc\xc72\x9a\xb9}\xd8T\x1f\xabz|\xa0\xdc\x1d>\xd7\xd2c\xd1\xd6\xcc\xad\x9b+\xa19]\xdan\xce\x1f\xecs\xa6\xea\xed\xd9\xfd\xbd\xf6\xfa,\xcdMR\xa4L \xbd:R\x8e\xbf\xa5F\xf6\xab\xd1\x94\x0d\x03;\xd5\x0f\xac2W\xd8\x87\xa9}]\xb8\xa9G}e08\xacd\x92\x8f9\x10\x8b\xc8N M\x9d\xea\xfd\xbei\xa4\xef\xf5#E\xaaj\xd3\x16\"|\xa7\xc4p\x07\x81\xb4]\xa1\x12|\x7f R\x9fom\x8fJ\xcf_\x1d\x7f<,?/eU\x1a\xbc>|s\xf0\xe9\xdd\xe9y\xb5\x9fQ\xa5\x1fY\xef\xcd\xa7w\xefJ\xf5\xb6wJ\xf5\x82\x88\xcc\xf1\xc2\x94}\xa9>8\x08\x82\xfc\xd9\x01\xe3 \x8a\xc7 Y\xd0w\xf2]\xf9CWA\xb6\xa1\xfcV\xab\xcd\xb3\xd5\x1a\xb95\xf6\xa5\xfa\xfek\xf9P\xfeP+\xfc\xf5\xe0\xfd\xbb\\q-`\xb0W\x9a\xdb\xfb\xb7Go\xdf\x1f\xbc\xb3-G[0Z \x98x\x84\xbb\xedv\xd9\xb7n\xe9\xd9\x9a\xc4\x18F\xd1w\xba\xf8\xb5\xfc\x14\x93\x19\xcb\xe7\xe2G\xb9\x06\x99\xcf_\x95<\xa5|\xa7[.\xeb~\x93M\xfc\xb4\xea\x06\x1d\x15\x00-\x95\x8b\xb4Z\xdb\xfaDq\x08\xbdRyV\x80\xacT\x9eh\x9cE\xad^\xa1\x01F\xbd-\x15y\x18\x07\xbaL\xaba\x1f\xb6\xcaE\x0c\x81\xb6\xcbE\xf3z[\x97\xf5\xb6\xae\xebm\xad`\x1f\x9eL\xcfn\x87\xc3\x8d\xb3\xdb\xe1\xd3\xb3\xdb\xe1\x8fg\xb7\xc3Wg\xb7\xc3\xc3\x8d\xb3\xdb\xd1\x9b\xb3\xdb\xbd7\x1bg\xb7O\xb7\xcfn\x9f\xeen\x9c\xdd>{s\x96\xbdy\xf3\xe6\x10\xff\x7f3\xbb\x9f\x9ee\xaf\x9f\xb2\x97\xb3\xd7?\xbey3s&\x1dV\xf2\x8a\x97\xb0\x1a\xee\xbd3\x19O\x7f/W\xbb\xff\xdd\xadT{R\x1e\xd6R\x0c\xeb\xe9\xceY\xb69\xdc|\x8a\xff?\xab\xd6\xba\xc3Z\xfd\xb3\xe9\xd9\xec\xec\x1fg\x9f\xab\x8f/\xd8\xe3\xdf\x9d\xc9\xb8s\xdf\xe9\xdcw\xa6d\xe3\xefg\x1b\xb3^\xc7\xfd\xf3\x13\xbf\\\xf3\xbc\xa89\xfd\xbdh\xcfu&\xe3\xff\x98\x0e7\x9e\x91\x8d\xc5\xec\x1f\x9b\x9f\xef\xf9\xf7\xbf\x9fm\xfc_\xcf\xcf\x9e\x9cM\xc6\xff\xf9h\xff\xacw\xf6\xe7\xfe\xf9\xd9\xa0\xf3?g?<>s\xce\\\xf6\xf6\xcc\xfd\xe1\xcfO|\xddYqc<+F\xc3\xc2\x8an\xb4\xc5\xbf+\xd4\xbc\xde\xd4\xa1\xb1\xa9gEK[\x9b-Z\xba}HK8\xbe\x87\x8e\xf5\xc4\xd8\xc3\xf6v\xd1\xd4\xb3\x91\xf2}K\xe9b\xb3\xf4c\xa7E\x87\x1a\xbd\xbaF\xc5,\xc7\xf0\x14^\xec\x0bgI\xf6mg\x0f\x13Zn\xb0\x07cx\xb6\xc7\xca0\xaa\xf8\xd6&\xdc\x0b\x9bF4a\x1c\x0d7\xd1\x9ca\x83U\xea1\xb0\x8cacd\x1d\x98F\xff]\x8c\x82Or\x02\xdd\xb3a\x97\xf7\x9c\x97\xfc\xff\xb0@\xadr\xc1JF\xa3]\xa5(\xc5J\xd5\x82Q\xbe\\\xac(\xe4EjK\xd7X4\xdcT\x8a\x16\xbc\xd6\xb6R\x14\xf3Z\xa3\xa2\xe8\xff\xcfJ\xb6\x94\xd7\x00\x0b\x8a\x97\x1ew\x1f\xc3\x18\xb6\x95i<\xc1\x11\xaa=\x9d\xb1\x92=e8\xff\xe7\x7fc\x9d\x1d\xa5\xe4\xff\xc6:\xeaL\x91*\xb0\xd2\xa7\xc3J\xe93V\xda\xedZ\x17\xe1\xc0\xb8\x08\xb8\xfe\xbb;;[;0\x01\xeet\x87y\x0b_]\x92\xf8U4\xc7\x9c\xa8c\xed\x83\x9d\x9d\xcdg\xbb\xd0\x03\x87!\x0eka\x17^\xbe\x84\x11\xe3uvv\xb76\x87\xe5G\x8f\x18\xbc\xb7\x14o\xd9\x82_\xcb\xed\xe4\x8e\x85\x9a\x043\xee9\x9b;\x8c5\xfb\xa0);\x054\x97;\x85\x17\xb0\xb9\xb3\xfb\x1cN{=\x17\x8e\xa7\xa73\xd8\x87+\xe7\xd4\x85 \x8c`\x0c\xc3>|(\nu\xc4\xe9\xbdV\xc1\xa9\\\x94Dx\xdf\xc7\xc3\x17\x0f\x16~@C\xb2\xa2\xa8,\x0b\xd7Y\x8aN\xb4Q\xe2\xa7huH\x07\x81\x1fR\xb5\x0c6D!:\xd0\x97\xe6^\x1f\xcb[\xedX8\xcf,\xc6i}\xff\x0f\xed\xfbt\x10\x85\xbf\x918\xf4\xc3%w\x8d\xce\x7f\x8a@\x85\xa8U\x12\xed\xeb\x16\x87\xad\xcbQMe\xc4\x18\xb7\x9a\xd1\x99V\xb9{]$\xa4\xab\xcb\x8e\"7\xf0>\xd0\xc15\x8d\x136\x8dG\x8f8$\xba\xf3l\x1d\xf8\x1eF\x1d\x84h\x01\xff\xc1\xba\x84\xb9\x1fS/\xf5\xaf\x91\xc7\xe2IC\xf2\xa4:\xf9\x9b\xe5\x9a@<\xc6`&@o\x89\x97\x06w\xc0d]\x99\x03\x12\xe3E\xb3A\xb0-\x85w\xe0O~w\xd8\xa17\xeb\xb9g\x03\xf9\xed\xcfO\x06\xf4\x96zN8\x1d\xce\xb8\x17\x1b\xef\xc8\x0f\x82\x8dE\x14\xaf\x98\xa4\"\x1a\x04L\xb0I\xa1>Z\xc6\x8e!\x03\xf96L\x9d\x18\xc3B\xe2^\xf1\xcb\xe5\x9b\xb2\x9c\xcf.*z\xcbB>\x13r\x11\x88\xf6%\xccD\x9f20\x1b\xe7?\xe5\xc3}\x081\x12%\x1dx\x97\xd4\xbbz\xe7\x87\xf4\xc7\x98\x92+\x0c{\xc1v\x90\xec\n\x0d\xdc7\x8b\xaf\x7f\x88^\x93l\xcd8Y:o\xe8\xb4\xb4\xba\xd5\xccb\x07?=\x0c]\xea\xb8\xb2iX\xed\xd3\x83\x9f,\x8b\x9d\xdeDE\xc2O\x06\x988\x07\x08\xf2\xc7\xb8\x0e\x17\x83\x94&\xa9\x13\xa3\xa8][\xda\x94,\x81'o\x01g\xe1\xc7I\x9a7\xe8J \x94\xc6\xc0zI\x84\xeef\x90\x92\xe5{\xb2\xc6\xcb[9\xe2\xc7\xe9%\x8d)\x9a\xbb\xc1:\xa6\xd7~\x94%\xc1\x1d\xcc\xa9\x17\x90\x98\xce!\xc9\x16\x0b\xff\x16\xa9b\xf71\xf4 \x86\x1e<\xee*\xc3x\xec\xf6\xe1\x9c\x0f92\x0fy\x1dS\xd6\x8c\x93P/\n\xe7-\xc6,\x07;\x8dg\xb6xr::\xfa\xd1b'\x89\xb7\x0cy>\xb5\xf2\xba\xa2f\x10^\xe8QA\x18\x93Ib+\xdcH\x11q\x8c\xd1\x81\xf1(\x89\xb8\x83\xad\x8fw\xbfB\xed\x06\x11\xbc\x00\x9f\xfd\xe9\xed\xc3\xc8\x15<\x83C\xb0\x8e'\x8e\xb4\x03\x06PW\xf0~/\xf6y|8\x82|\xcfh\xb4=\x1a\x8d\n`\xd3\xdb5\xf5\xd8\x9e\xb8&\x81?\x87\xbf\x9c\x1c\x1f\x15\x11\x0cuv\x8bhp\xb5\xe2\xab\x96)4\x84-E\x92\xc6\x94\xac\xd0\x16\x89\xf8a\x02a\x14n\xacc?\xe4[=o6\xd1\xb6+n=\xd8\xbc2\xd3\x9ai\x96\xecu\xb1d5\x87M\xbc\x7f\xe1\xeb\xd5\x87\xa0\xdc'B8\x1e\xf8 \x17\xfd\x9cP\xc1@\xa1\xaaY\xd1xIaE\xd6k?\\&\xcf\x11\xdb\xc4\xdd\xd6\x1c\x92(\x8b=*.9\xd8&P\xc9\x1aC\xc3\x8c\xaf\x1e\x13\x16\x1d\xc58\xf6\x8a\xdea\xa2\xb7|A3x\x01\x01\xfb\xc3\x17\x14\x9dd\xa6\xd9,\xdf{)\xda&`r!\x1e\x95 \x9c\x12\xb6\xeb\xf9\x0fU#\xae\x03\xcf;\x05\xa3\xd5t\xaa:P\x05}\xf0\xeax\xcd\xb0\x90\xb3MN\xa4\x9e2y\xc4\x11\xf8\x07\xe6\x83N\xc9r|GV\xc1 \x8a\x97\xfd\xcd\xe1ps\x8c\xf0\x13\xa6\xf3u4gm\xf3\xf4\xd2~\xc2\x99\"\xdf\x96\x958\xe0\xe0\xf4\xf0BL\xc2.\x80\x17\xe0\xb1?\x1cv\x12\x17\xfci0\xd3\x9b\xe4!\xf6\xe6\xd5\xeau\xf09\x1d\xfc\x91\xf0\xbb\x95$\x8f\x82\xcc T\xa7X\x13^\xe0p\xbe\x08\xd8\x1e\xc3\x0c_5\xd6i\x1f2\xfe\xa4`\xb0\xca|\x01\x9dK\x14\x83+z\x87!M\xd2i\x84\x17\x7f\xf9\xadM8\x8dfZ\x01(\xb5.\xfe\xa7V\xb2\x94\x102D\x8aMN\xa3\x14JR\x8c\x1c\xf32\x15?{=&Vl d\x98\x80\xa3>\xea\xe7\xa2\xa6\xb5E\xce\xcb\x15\xaf1\x1e\x9d\x83\x87\x00\x02\x16\x9d\x9e\xd8\xf6\x92\x84\x8aSx|\xd6\xc3\xe4C\ng\x8a\x13\x90\x8dY!\xf37\xd3\xd9]J\xc69\x94\x19\xfflSx.\xb2~GZchqyr\xe8D\xees\xd7\xd4Z\xaf\xa7\xb6\xa7\xdd)\xb8\xdb\xb6\xb8he\x08\xf0?\x8f,\x979mz\xd6\xbe\xfc\x19n.}\xc62\x8c\x86\x05#7\xda*\xbe\x8bb\xc3\xb8;7x\x14\xe12\xd6k t>a\xf2\x90f@\xf7!fx\xc5\xd7\xfbm8\xe7\xe6\xcd\xc3\xe7R\x90e\x0b\xa0>d\x95\x1f<\xed\xcf\xba]\xb6!8\xf4b\xba1G\\e$/\xf8c\xcel\xce\xe9\xc2\xf7|V\xec\xe3S\xe4\xfe\x91k\xb3b\xe5\x1b\xc3~\xed\x8bD\xb3r\xc8ZR\xd0q\xb6wpl\xa6\x8d,2\xe7n\xefr[\x01\x0c\xfd$\x84\x96z]\xe81\x82\xdaTe\x93\x13\xc1\x90m\xc5\xad\xbe\x80MC\xff\x9d['u\x1bd\xc8\xbfke\xc0QNjTf\x81\xeb.R\xcc\xda\xcfc\xce\x15\xcf\xe2AL\xd7\x94\xa4N\xf7\x0c\xcdd`\xa3\x94(K\xd7\xf5\x8f\xda\xae\xafE\\A\x89Q)\xd1X\xe2\xf9\xdck2\xf4.\xaby\xb3A\xa8\xa5u\x99Q2M\xae\x11\xeetQ\x08\x95\xbcM1=\xfe\x831\xb8\xf2;;,\x88\x90 \xda\x11+lk\x9b\x93\x13\xfc~\xebX_Dtp5\x97\xbe\x92\xb9\xed\x0c\xfbP\xa6\xffHbY\xf1\xc6\xc8\xad\xef\x96}\x06c\x99\xbb*\x0b\x82v\xa3\xafu\x9f{.\xf0\x0d\xc2O\xdf\xdf\x04q_\xf0<\x1e\x1d\xcc\xce\xc2\xbb\x92\xc8\xe1\x96\xc7\xd7\xa6\xf3~q\xd8#-\xc8\x8f{1\xa5\x97\"^\x8c\x00\xb0+\xce\xb1\x0b2W\x89\x00\x93Z\x08$\xf4o\x19\x0d=\n4Lcm\x94\x80|b\x15\"\x93ji\xa9$\x01\x9dL\xe0\x08\x13\x9c\xd0W'\xc7\x1dd'\xe8\xe0\xca\x0f\xd1\xaaG\x8e\xa0\xdb/6\xd3>\xe3\x0c\x9b\x18\xca_\xcd4*g1\xf95\xbev\x07T1\x9dMq\x8b\x9f&N\xf3\x11P\xd8\x0f\xe8\xdaQ6\x0c\x9b\xbfI\x03C\x84X\xc9\xafv\x18U\xde\x15\x1cP\x9b\xd3\x82\xf1@\xc8\xcfw\xcc\xdcA\xe5\x851lq.)b\xef\x12%\x01g\xb7\xd3\xe9\xb6o\x85\xbf\xd1\xedC\x99\xd11\x98<\x1b\xd9\x816\xdd\xd5^\xcc\xd9\x00\x85\x0b\xd8\xdd4\x1e\xfd\n\xe5(lF\xd8\xecc\x9d \\\xdaem\x86W\xb0\x89Y\x98K\xb04\x9cK\x9d\x80\x10Do\xfc\xf4\xd2\x0f\x81\xc05\x8d/H\xea\xaf\xd8\xcaW\x15<\xa6p \x82sS\xe6\xdb\xb9\xe5\\\\\xbe\x9al\xaf\x11\x98H \x98,\xa5\xceC\x08\x90B\x10\x06z\xeb\x05d\xc5\x11pE\xe2\xab\xa4\x9b\xa7k\xae\xc0\x82\x1dP%\xf1\xa1\x87\xc9\xed\x84bG\x95QCR\xd1\xe9T\xfaL2\xef\xb2$r\xcb\xcc\xe5U\xf4\xe1\xa4\xbd\x1d\xdc\xeb\x0b\xdd\xbc\x9ew\xb9R\xaa\xd0\x15\x18!\xb5\x08\xa2\x1bF.\xd9v\x8d\xe2\xd2\xf8\xcb\xab\xa6#\x7fx\x90u\xce\xf5\xfd1x5\xc0h\x8c\xf6\x1b\xb1\xcb\x03KH\"\x1a\xc3\xb8\xae\x06\x0b]\xa5F\xaep\ng\xa8\xe6\x1a\xb3]*N\x89\xa2\x16+\x93Ou\x8f\xeb\xf2\xb3\xac\xcf\xb5mY\x98k\xd6\x94UG\xcdZ\x88\x9a\xb5\xc7\x98\xda\xdeJ\xbc\x7f6\x13o\x0dY~\xca\xc9r\xf8\x15d\xd9\xcc\xc8\xe8Is\x08\xa2\x86J\x9e\x0d\x03(af\x15\xab\xe5\xc6\x0d\xc5\xc6\xe5\xa2f\xe7\xc4 \xd9\x0en\xd3\xa2\xf6\x84U\xb6M\xae\x03)\xf6cy\na4\xa7\xb0\xca\x92\x02\xdfH\n\x01%I\x8a\xaa{E\xcbV:\xa6\xed\xbb\xa9a\x81\x7fS\xb4a\x9as\x01\xddqQ\x1b\xb6\xea\xc3\xb2\x0fw}\xb8\xe8\xc3y\x1f\xae\xf8e\x94\xe6\xd0~o8\xcc\xff0\x1c\xe6\xcab\x07~\x92\xd2\x90\xe6\xb2\x12\xff\xe5t\xa35\x0d1\xbfx?\xc7~~}\xa3@A\x16\x08~E\xfe\xcc9\x15^\x80jO\xd8Gc\x88u\xc1\x97-\xf8W\x11q\xad\xca\x88:\xefs~\xb5\xcc\xbe\xc1\x84\x03\x01\xd3_\xa9B\xa6\x90:\xf0\xba\xae\xfa\xf0\x85P\x84\x9d\xa2\xf1\xa5\x8b\x17\x1e\xec\x85\xd3\xfa\x19*N\x14\xe4\xa0\xee\xefq3>w\xcb\xc3\x9b\x14\xa3[q~\xec\xbb\x0c\x12\xc6\xd8\xbcn\xfdV \x832\xbfg\x83\xf4\xf3\x1b\x9cS\xf6`-6\x15\x93\xfa\xce1\"w\x0et/'i\x98\n\x80\x1d+}\xb8*\x1f5\xa5{\xc4\x1cR0\x01\xde+\xca^W\x08\x9c\x87\xdc\xb1\xf4\x0b%ob\x96\xce@X\xee\x98%4\xf6YXBr\xcf-\xcf.%Nj\x9f^[\x9f\xae\xacO\x97\x86\x0d\x08\xc2\x8eF\x97\xa7\xf2\x0b\xe4\xc7\x85PY\xb7\x93\x1f3\xa3\xe7\xbf\xf4Vn\x16'\xfbB`\xe6B\x1b\xa9\xf0\xb4\xbb\\(@\x81f\xe7\xa9\xf8~\x7f\xcfhyl\xb5\x84F\xad\x13\xd2\xc1\xb0\x0f^.\x02\x1auP\xea{\x8a\x80\xd7\xe8F\x880n\x03\xb1C'c\xfb\xdcP\xb5\x81\xbfR?l\x84;\xdc\xde\"s\xe1\xd6\xd4y\x85S\xce9F\xc2X\xf8\x94&k\xe2)\xa7\x8f\xaa[\x05td@\x0e\xfa\x8a\xdemp\xd3\xea\x84\xae \xf7\xf0\xc8\xd9\xe9\x8b \xf2\xae\xa4\xd6\x9a\x1d_(l9x\xd7\xb0\xe8\xc3\xbc\x0f\x97}\xb8\xe6w\x05n\x1f\xf7\xc6\xb5\xa0\xd2\xa2\xe8N\x109\x81\xdc\xc8|\xb2\xbf\x97\xf9\xfe\xc57$\xc1\xb7\xc3\xa5e\xf2+\xa6\x04\x88\x97vF\xe9\xba\x91Q2\xe5'a\x80\x17\xe6\xa0\xce\xba\x19\x17\xf8\x9d\xd8\xb3\xad\xbe\xd0\x83sM\xac.P\xbd\x85\xf2\xb1>G\x9b\x9caX\x1beQ\xf9a\x1d\x8e6wD\x8fC\xde\xe3?\xda8\xf4|\x01[\x15\xbb}0\x80\xa1|\xf2\x0b\xfc_[\x19\xab|\xab\xb1\xbd\xda\x06\xbc\xe2\xbe\xb0.\xbe\xf2\x9b4\x8e\xbb\x97%\xdc\xbdVp\x97\xd1\xdb\x1c\x7falR\x1b\xc7\xe6\xc3d^\xf0\x1f\x9c>\x82\x17\xadV\x04.hzC\xa9P\xf8xQ\x10P.\xc0R\xeeD\xc8H\xa3\xc7\xb6\x95H~\xc9\xc5=\x1f\xef\xd99\x9a\x88\x13a\x0dm//@F*%\xf6\xeb\x8a\xd4\xcdU\x0e\xe5\xeb\x84@\xb9N\xf0\n>%Q(h\xa9\x19\xe3\xc2\x97\x05z\x02\xf9\xe5H!\\ \x8ew\x8d\xe4Xj\x9b\xdb\xe0Qe\x04\xba\xb1/\xca$\x9f\xad1\xd2\xb8\x18\xe9\xbc\x874d\xc1]\x81'\x10\xf3{\x13\xac\xc0\x17A\xa9\xc3*\x89\nI\xb5ga\x1e\xde\nI'\xe0\xcc\x1f0G\xd6-\xd6\x1f\xb5\xd8\xb3\x0fQ\x13W\x90\xb1\xaasd-\x9d\xb3\xd1\xa2\xee\x83 \xd9<\xfdn[R]\x15T\xe7f!\xd5$\xf0y\x96g\x0b\x0c\x8a\xab}\xb4\x86Z\xfe9\xf9\xd1\xe9\x01 \xa7\xa9b\x11I\xf3\"\xba\x82\x87\x7f0\xe1\x16\xb7\x08\xa4\x15\xddntP\x04I\xa6\x95\xab.\x8f\x04$.S\xacnW\x12\\b\xf0deC\xdb\xde\xb2N\xbf.h\x89\x1bU\xe22\xfc\xdcg\xe4k\x82+-\x1a\"\xc8\x7f\x8d1\x80\x17\xc7K~=\xcd\x99\x1b\xef2Z!w\xb3B\x86\x92q-\xfe\xc2\xd7[\xe1A\xb3\xd8\x83b\x80\x83\xc4\x83\xbbI\xa0\xbc\xc8\x93ne\xb9\xb3D&\x9d%6F\xbfF\xf1`\xdf\x18\x11\xbe\x8e5\x0c^\x87\x0e1\xea\x16\xac\xe65m0D?\x0ey\xaf\x86]\x9b\xf9\xfe-\x89Y\xc6!X\xc7\x07_3FP\xc7\xd9\xb9q\x88r\xcf\xad\x19\x90aC*\x1b\xce0P\xc5\x1a\xa8j\xe4\xd37\x8d\xbe\x9d\xf2\xc4\xe9x5Y\xe9\x05;\xe4\x1e=\x92\xd6CDc=\xd4\x06b\xe6%\xebxP5{x \x0bdC\x169{\xc1\x1f\xb8}\xb8A\xd4[\xf7z_\xbc\xd9\xeb\xb3\xb3\xe3C\x82\xf3\xbe\xae\x98\xd3TLf\x02\xf4A\xe9\xc1\x1a\xc6\x8c\xb5\x1e\x8b\xb70\xc4\x88\xcc\xf1\xa8\xd8\xe2\x9c\x85M)\x0f\xecA\xed\xcd\xaa\x0fa\x11=\x01\xb6Q\x18\xc7\xb0\xca\xd9\xb8\x96\x83\xe7Zo\xf9\xe6\xc8\xfa\xe6Z\xf0\x8ccA\xed\xd60\xd1M\x17\x90\xee\xd8\xdaix^\x1e!\xb7\x16\xee\x0c%\xe9\xea\x8b\x83\xbbj\xfe\x05\xd5M\xf8\xdc\xfd\n\\e\x9f\x8fB_\xaaj`;\xa3\xb6\xa4\xd3(@W\x8ek\xc9A=P\xbc\xd53'[\xcf\xbe\xfez\x12\xdar\x0bUi!\xc6\xec\xbd\xfb\x9a\x0b\xc76\xe3\xb1\xb0\x1c[\xdc\xa0\xdf\x9a\xf2\x82\xd5\xfb(8\xf6\xd2\x821\xee\xbe\x01,e\x9e\xa5\x00\x8cE\x17\x18\x97\xe6Y\x85D\x19\n\x863\x0e\xa9\xd7\x8d\x83\xb7\xe6\xf9\xd0#1b4\xf6\xe3\xb2\xc3H\x88_u\xf0\xf2}\x94Kt\xfb\xfb\xfb%\xc3\xdfG\x8f\xb8\xf1\xe4\xc4\xca\xefK\x1f\x9f\x82\xe3O\xfcp\x19P\xf8[\x16\xb1\xaab\xedEBJ\xf3,5\x1b\xe9!b\x86\xbe\xd3o\xb1ST\x01\xc3\xb0k\xb69z\xb4P\xd3}\xfb]\x13\xa29\x85v\xd7\xb4\x18\x8fU3\"|W\xb3|\xd0Z\x8a6t\xabC2!>\xaa\xb16e\x9b-\xf6\xa2\xae\xab\x9bvW4\xae\x8a\xfd\xe6}\x98\xeb53\xee/\xca\x90\xfex\x9a\xcd\xdc\xd2\x01\xf3\x01}G\xd4I\xb6h\x11%\x9c\xd1\xa60\x83\xc3`\x93l/m\xa2+\xf1^.\xcal\xc3\x18\x9e\xee\xe4?\x99\xd80t\xe1%\xfb\xaf\xc5]Y\xc4/\xb4}n\xb4\x1d\xb1\xf7\x9eC\xb4\xb1\xe1b\xef\xaf\xda\xc2\x8a )0\xc1f\x1c\x1f^\xbc\x80m\x17z@r\x91*\xdf\x81\x97\xf4\x96\xcc\xa9\xe7\xafH`wiR?*(\x0f\x1c\xbf\x82/f\xbe\x85\xc3RR\x81\xab0\xba \x81&\x1eY\xd3\xdc\xd8\xd3\xd6u}g\xd8)iVPR\xbe\xf5M\x94\xb4\xde\xf0w\xa2\xa4\xf3(\xbbhCI+\x83i\xc1K<\x84\xb4\xeaG\xa1%\xad\x8a\x1aG\xc95o\x0e\xbd\xc6!\xad\xa7\xaa\xdb\\\x87\xd1|\xf1\xdd\x86\xaa\x1a\x1aie\xee\xc4M\xe0n\x85\xf5[\xe7\xc4\x89\x19\xd9l\xd3b}0\x0f2y\n|\x92<\xc8\xe2Ic\xfc\xd8/\x9b:)*\xf5J8\x16\xd5\x10\xf2q\x16\xe6j\x80\xb9\x18G\xc5(N9\x93T5}8\xab\xde]\xd5\xd9U\x86&_j\x8a\x82ZWO\xea[\xd9IiV\xce\x99/\xba\x19z\xdd:^3b1\x88\x9c8\x1ew\xfb\xe4D\x1a\x85\xde\xad\xa7\xc5\xf7\xedM\xa5|\xab\xf8.\x15}\xf8cW\xad\xf4L\xf9\xae\xd4\xd9\xdaS\xea+\xe5\xcfx\xa8\x07\xcf\x8a\xe5x\xe2\xec*\xdd\x0b\xb5\x99\xc7u\xf4\xb7\xcd\xdbHHg\xf7\xf7\xdc\xbe\x8f\xa1y\x8b\x8d\xd5\xcc\xaeD\xe8K^fw\x85\xd5\xba\xd8`\x9e\x95\x0b\x11\xd6\x19\xd6Dp|A\xbfh\x8a\x16\xe1YI\xaf\xb8\xb5\xd3v\x10\xf6\x01\xa0\xafL\x8b>\x9b\xb4\x12\x8dGM1G\xafY\xfb\xc8\xda\xbc\xc1\x8a\xcdV\x10Y\xaef\x91\xd74\x8a\xf1Y\x90\x17p\x95\x89rrn\x8cjw\xd4\xfb\xf6\x04o\xf2C\x14\xf9\xfd\x8b\xb5U\xe2#S:X+\xda\x839\xab\xc0\xe7\xfe\x1f\xdcx\x80\xd1'u%\xc4\xfduI\xe7\x16|{=\x8e\xbe\x14/\xc08/\xc3\xe9gg$y\x191\xde\x0d\xc8\\\xdb\xe6t\xfbp((\x9fS\xae!\x0c\xcd\x0c\xcb\xd1\xe0\xf2`:\x11\xabC\xedtr2\xc2]\x82\x05\x99Y\x94\xe8\xcb\xba\xaeQ\xe1\xacH_ZQr\xf2\xf7\x87@\xa1\xdc\xd1:\xf7f\xc9\x8d\x0d\xba\x93.\xea\xa6,u\x95\x12q\xb3[\xd8\x81\x15gur\x19e\xc1\x1cmu.\xc95\x05\x12\xdeI\xcbk\xbc\x84\x95\xfe\xde\xad\xaf\xbb\xf3{\xc5Buv\x9a\xcf\n\x8d<\x85\x8dg\xa5i1\xean\xa7[\x14\xe8\x9d\xcd\xba\x93n1S\xab&y\xc9ugw|\xed\x85\x11\xd2\xe9\xdd:OZ\xf7\x1c\x96\xf0\x02\xee\xd8\x1f\xf4\x1f\xb7\xd2\x1c\xe7\xa2\xde\xcet9s\x072\xe0\xbb2u;\x9dPp\xe2b\x90'lW]\xd3\xe4:_\xf0\x1b\xe6/\\\x82o\xbb\x7f\x05\xb1/\xb1t\xe7\xb6`T\x0b\x86N\x19\x13\xbfw\x16\xc7\xdb\x91\xf0\xf0;\x9a\x863\xa9cc\xf4\xf4\x0f\xa1q\xe0\xf44W\x82\x15hZ\xd2<\xfc\xc9\xdcy\x99\x1e\x0c\x15\xd1H\xec\xf7\xc2=\xdfN(\xdaV\xe4\xf1\x1c\xdaW\xdet\xcb\x11]D\x84\x07u\xdc\x0c D\xb3W\x13T\xd0\xadH\\\x8b\xdb\xf2[\xc1\xd3\x8bi\xa2\x9d\xc6Z1N+\x03\xa6N\xa4\x1f=\x82%w\xf0,\xaf\xbd_^{\xc8Cq\x84Q\xb8qp\xf2\xea\xed[%\x9eL\x02$\xa6\xe0\x87)\x8d\xd71E\xc7\x87\x04\xc5\xad<\xe8\x9c\\\xda\xa4\x166\xa0\x85<;\x81\xed\xddf \xbb\x82\x15h\x80\xb0RA\xf1\xa4\xdeP\xa9d]\x1f\x1a\xc5\xa8\x0b\x15\xe8Yxp\x94\xd6\xc3z\x18\xff\xd5\xd1Fa,bAQqv\xa0\xcc\xc3\xce\xc8\xa1\xe4\x17\xf2\xb8v2d\x0c-\x03\xa0\x98\x02\x82@\xc4\x92\xb1Wrhn^\xd0\x87\xdd\x9d\xcd=\x11+U}i(k\xb2r\x8e\x15#\xb7J\xfb\xaeE\xde\xe9\x90\xde4\xdf\xaca\xe6 \\B\xc0DL\xf8[F\xcfds/~\x08\x96G\xd4Id\\\xf6T~\xbd\xbfg27>,\x02Y\xb2\xe7\xc5\xafr\x13\x9c\x13\xc1*\xe2\xeb\xfd=W\xeb\xb3\xa7\x18\xa0\x8a=\x93\x91\xaa\xf2'9\xbb\x86o\xca\x1f\xe5\xb6KB\x8cL\xc2\xcd\x07\x8a\x81\xc0\xfd\x80\xce\xdf\x8a:2\x97 \xe7\xdf\x0d\x95O\xf9\xd3|\xe8\xb8v\x052\x88rE\x171\xccG\x8b\xea\x08\xf5\xa7\xd4H\xa8e\xaa!\x10O\xf7,\xf7'\xf2\x17eB\xcb\x97S\xc3\x04\x86b-\x11\x93\x86\xdd\xaev\xe5\x97s\x93t\xf2\xdc$EZ\x12_3#%$V\x11\x82-\x86\x17\x10\xb1?<\x04[\xea\xf8\xd3xf\xa7-?i7\x9c\xdc\x99\x7f\xd5\xad\x1f\x1b\xb1p\xe8\x96\xd9P4\xfb\x95\xd5\x1a\x89%\x95\xb5$X\xa7C\x8dOA\x91\xc9!r\x8a\x8b\xc3\xfc\x86>\xa7\xa0~\xa8P\xd7>\\d),\xa2\x8c\x9drQL\x1f\x94\xc9\xa1He\xf0K\xbf\x9e\xfa\xe0\xa7\xbe1kA\xd3-D\x8b5E\x94\x89\x07\xf46\xa5\xe1\xdc\xa9\x83\x8fo\xea1\x90\xf2|Xg\x95\xe5\x90\xc8\xf7\x85\x8d\xfdI\xf9\xa9M\xe3`\xa5\xccb6?}\xe9l\xea\xf1\x81\xbf>c\x81.\x98h\xe4\x94B/V\xa7\x81tL\x1c$\xf2l\xb9\xc8\x16\x0bN\xba\xeb$3,\x93\xccX\xfc\xf4\xa2 [\x85\xa5@\xa7\x05\xde))\xd8\x07K\x9a\x9e\x84\xfezM\xd3&\x00\xd7\xcc\xd5\xeb{\xb1\xa3\x0c\xd7U\x95\x06:\xd9\x1bD\x00\xf8m\x85c\xd8\xdb\x11\x11p\xc4\xadKi\xb6\xc2:\x80\x1d\xe7\x1b|?w\xcf\x86g\xf1Y\xf8\x7f\xfe\xb7\x9aU\xa0;\xf0\xc39\xbd=^8\xcah\x90\x8a\x1f\xa4N\xc4\xef/\x0c!\xab\"\xd8@2^\x06\xf2\x06\xf6\x9b\xc2\x13\xd8\xe4\x9c\x87^X\xc3q\xc3`0\x00\x1c|o\x1fv\xf4RJ\x1bw3\x04\x91/ A\xea\x90 \xf0B\xc5\x0d\x85\xbd\xfab\xd0\x10#X\x1c\"\xc8\xf8F\x052-\xa0\xe2\xabP!\x0c\xbe_\x01\x15\x81Q\x99\x84\x87\x98\x00\xe7\xea\"\xee\x8aX\x98R\x02\xaa\xa1\x84\xe4\x95\xa1\x01x\x8f\x07\xcc\xefUkAO\xb3\xe6=\xe5\xbc\xe8A\xf7\xf7\xaeJ\xa0\xd4=\x94F\x9c\xfb\xb5\xe6\xe6UB\xf6u\xbb\xda3\xbe\xd8\xfa\x8caE\x0e\xe2\xb1\x1fr\xe1\xb1x\x86\xd1\x92\x1f\xe3U9\xe3XH\xca%\x186)\xa7\xa0\x04(\xd7\xf5\xd8\xdc\x04%(\x9e\x8b\x02~\x05\x82;\x10\x85r|VP\x03G\xa8\xa8x/c\x0e5\xd4]j\xc9tNi\xbe\x92h\x8ev\x953Em\x9d\x9d\xc6\xb1\xa3 \x87\x93\xa4q\xb7_\x81\xf5\x95\x1f\xce\xc7\xc5}n\xe9Y\xae\x90\x1d7\x98w\xd4t\x9e\x98D\xa2\x94\x8b\x00\xca\x07\xbb\xfb/\x82\x00\xfd\x9b\x11\x02\xb9c\xde\xb7\x85A\x95\xb9\xfe\x97\xc3`E\xd6&\x18\xe4\x8e\xb6\xdf\x16\x04\x15\xd7\xd0\x7f=\x08\xd8\x08\x1f\xb4\x13\xc4\xedA\x13\x00|\x19\xbe\x07Ek\xabm\xf0u\x9e\x8cR\xc8\x01&h\xca\x98\x9d\x8f\x1eA\xf7\x7f\xc4\xcd\x1d\xf2\x02E\xb9\xd3\xc5 \x15\xcf\xbaG\xd5\xdf\x9f\xde\xbd\x13\xbf+\xbcv\xf3R7\xac\xb4\xad\xb9uL1\x10Y#\xe0T\xcc\xc1Q\xdaZ\x8d\xe9:\xa6 \x0d\xd3\xb1\xa6%\x8f\x84Q\xe8{$h\x98\x01\x14\xbdv\xffG\x93J\xb3~5\x12D74\xf6HB\x1f\xd02\xaeK\x9b\xc6\xb3\xf5\xfa\xc1\x8d\xe3\xa2\xb6i\xdc#+\x1a<\xb4q\xfd\xc8m\xeb2\xa7\x0b\x92\x05\xe9Iz\x17\xd01tsxu\xff\xe5\xfb\xfd\"\x8a\xfe\xa9\xfb]c?\xd5z\xbf\x97\xf6u\x1agT\xdd\xc7\xa7\xd5\xdf\x1f?\x1d\xca}\xcd\nv\xd4\x97\x17$HJ\xb5\xdf\xd4\n\x0e\xde\x9d\x1c~)]\xb0m\xe4\x87\x0c\xfc[\x12\x90\xeeT\xa4\x13\xf81\x8a\x02J\xc2\x19\xef\xa3\x96\x9cN\xb2\xa12\x03\xed\x17\x93\x1b\x1dQ0&\xc8\x95\xf6\xa00\x91\x00\x1a\x83X\xa56\xdbXG#Z\xf5\xc5\x81=\x96\xeb\xdd\xa6/\x1d\xc9h\xd7\x97\x9c\xd7\x1b\xc3\xbc\xfe\x1d(\x88)C\xe2\xee\x03\x93\x9c\xd6\xb2\xa7\xed\x14\x03\xd54D\xda7\xb4\xa74$\xbfUI]\xa4#u~\x98\xfe;P:\xae\xb4Q5\xd8Z\xcc\x89\xccn\xf5\xba\xa8\xde \x95'q\xa3ylw\x83\x1bB\xf1[\xd4i4C\x19\xad\xdb\x13y\xdesY\x8eN{\xbdh\xe6\xf6\xa1;\x14\x99\xfe\x8d\xe29j=z\x82!\x8b\x1b=\xbfp\x14\x17\xbcQ\xb5+S\xfb\x90\xbby\xf4z\xa4\x9fb\xe6\xb7\x959\x8ev\xddA\x1a}b\x02\xe9+\x92PG@\xa2\xb1\x9a\x0526\x1c\xab\xc8\x85b*\x15I&aO\x0f\x02\x9f$4\xb1\xe1\xe2t\xb3\x0f\xdd\x0b?\xecjR \xe4\x98>\xedC7\xf2R]\x95\x1c\x8e\xd3\xd1\x10\x13Uy\xbaZ%\x88OG\xbb}\xe8^\xd2\xdb\xee\xf7\xbd\x0b0\x8b\xb5\xe5b_\x08\x90\x1f\xe9\xf2\xf0v\xedt\x7fw&\xe3\xe9Fo6q&\xe3\xe1\xfdt\xb4\xf1l\xc6\x8e\xd8\xf3\xd9\x0f\xae3\x19\x9f\x9d\x0d\xe4/VaJ\x0fgXY\xa4\xc4\x9d\xdc\xe7\x15z\xda\xc7\xc5/\xd1\x8c3\x19\x97\x0f\xf2\xa2\x07^\xf9\xecl\xe0L\xc6~\xb8\xb8\x7f\xcb\xfe\x1d\xbdq\xefyQH\xc2\xfb#rt\x7ftp\xe4\xba\x7fV-\xef1.?&\xedU:\xa7O\xcczB\xad\xf0\xbc\x08\"\xf2]\xc4gU\xbf\xcdoF\x18\xa5u:\xbe\xe0`\\\x95\xf9\xa1S\xd5zo\xf6\xcdy\x1am@\x189B\xd8\x07\xc9G\x08\x03\xe4\x1a;2H\xa3w\xd1\x8d\xdc\xd2\x8c\x97\x80 ;\xc8\xc7 b\x00Og}\xe8\xf66\x94+tdX^\x8a\x13\x86\xdf\xa1\x16\xccH\x1fX\xcdE\xc1{\x08\x0b$\x98\x88\xc3l\xf0\xe1\xf8\xe4\xed\xe9\xdb_\x0f\xcf\xdf\x1e\xbdy{\xf4\xf6\xf4\xaf0\x96\x8f\x8e\x0e\x7f:\xa8>\xea\x0eB\x12\x16\xcd\x1d\x91#\x18CZf1\x04is\xd2/\xe33\xa22\x9f\xf1\x86!\x8e\x95\xd3\x10\xb6w1\xe74\xa2\x07t\x95JN#f\xaf\x9b9\x8d\x10~`|\xf3\x18\xbf(\xa3J\xff\x9dx\x0d\x873\x1b\x9d}\xee\x8d\xa1\xe15\xda2\x1b%Bi\xc2\xf8P\xaf\x1c\xf2\x93#r\xc4\xfa\x82\xe4\xc6O\xbdKp\x8c\xca\x03\x8f$T\xd5D\x8e\xb5\xb5@\x01\x0e\"\x9f^<\xe2\x8d\xe5z\xdc6\x8d\x1d\x1d\x1cY\x1b\xcb\x15\xb5\xad\x1a#G\x1a\x8dl\xe1\xf8l\xdcnB\xeb\xf7=\xa0\xc5v\xfe7\x83\xd6\xdb\xa37\xdf\x0eZo\xc3E\x1bh\xd5)\xd0\xf7\x83\xd6\xc67\x05\xd7\xc67\x85\xd7F#\xc0t\xbb\xbdx}8\x18j\xc6\xa2\x9cKe\xbe\xb7\x0f$\xcf\xe95\x810?\xa6\xba\xb4\xcb\x0e\x14\x1e\x083\xb4\x11\x93\x7f\xd6mC\x8d\xff\x8aj\xfcW\xce\x1e)\xff\xb9\x1b\x8e\xe9\xc7\x9f\xbb\x8d\x1c]c\x8b\x93\xca/\xc6\xbb\x9d\xa6\xb3\xfb)\x9c\x9d\xa5\xb3\x9e[z8V{/\xfd\xe0\x0c\"/\xf9\xc1\xe5\x1c\"\xb6\xf0\x83\xf3\xdf\xf7\x0ec\xc6\xdcj7\xa5\xf7\xdd\x89\xebNJ\xac\\\xab\x1b\xdd\xd4_\xd1$%+\xa3)\xcb7\xe7\xd6\x8a\xb0\xe5\xd1\x80\xdeRO0my\xa9/K\xbf\x03\xbf\xa6\x89\x87b\xb85Y\x0b\xf7L\xfd\xb9\x97\xdf\xe0 \x0b\x96\xcf\xc3\xcd\xb9\xb2b\x12j\x9erW1\xf3>\x8c\xe3(v\xba\xafIJs\x9fZ\xca\xcat\xc1\x99|\x91W\xb4\x97NG3\xce\xfc\xf4\xd2\xe9\xe6\x8c{-\x11\xfesk\xd6\x87N:\xdd\x9e\x15f\xb0\xf4\x06X\x07\x0e\xfbo\xf0\xe9\xf4\x95#\xc0\xa0\xf3\xc3\xf3E\x98\x8a\x1ek\x82G\xa9\xe8\xa5\xd3\x9d\x19\x8fO\xd1K\xa7\xbb\xb3>\xa4\xd3\xbd\x99\x89\n\xa3\xca\x15\x03\xdfN\xf7f\x82+\x1d\xf6a\xcb}\x0e\x8b\xc2\xa7r\xeb\xb9\x0b\x0b4\xf0\xd3Q)l\x87u\xb7\xa8\xd3?\x13z\xa5\xd3g3\x04<[\xb3]\xba\x0d?\x80\xb3;\x84\x1f\x10Z\xc3\x19\xf4\xa0\xe7\xa4\xd3\xd1h\xc6\xd0l(\x95\x80\xb8 \xea\x9b\x1bkW\xc4g0\x82M\xc1\x9e\x85\x8bQ\xd5\x1f=\x02o\x90\xd0\xf4\xd4_Q\xc7\x1b,\xc57\x1760\x88\xa6gCa?LR\x12z\xf4x1\xc6\xeeZph\x96M\xc6\x88\xfa\xdb\x93cA\xd7\x8d\x8e\x00\xdf\x8a\x10?\x90\xcc\xf0\x04\xfc\xdf\x8f\xc4t_\xbcP\xac\"L\xe6O\xdf\x0e\x0c\xc5\xcf4\xbe\xab\x0c\x8b\xc3hg\xdb\x1d\xfc\x88\xb6\xc2E\xaf\xe0\x11dd\xd8L>\x97\x1a\xb4(\x18\xba\x07?\xbez}\xf8\xe6\xa7\x9f\xdf\xfe\xe5\x97w\xef\x8f\x8e?\xfc\xd7\xc7\x93\xd3O\xbf\xfe\xf6\xbf\xfe\xfa\xdf\xe4\xc2\x9b\xd3\xc5\xf2\xd2\xff\xe3*X\x85\xd1\xfaoq\x92f\xd77\xb7w\x7f\x1f\x8e6\xb7\xb6wv\xf7\x9e>\xeb=\xd9?\x0b\xcf\xe2\xee\x03%x\xae\xe4\xf9\x1e+\xf6\xc57\xe0\x06J\x1d5^\x8e3\xfa\xe8\x1b\xae\x88B\x1e\x030\xe4\xbeC\xa1\xed\x9e\xa8\xe3 i'\xb9\xfcK\xa5\x19;\x8f\x06\x08\xbb\xdb\x8d7G)\xbc\x80a\xab\xdb\x1f\xd4\x8b\xefj\x1f\x1b)a\x0c\xff\x01OQ\x01]\xc6\xfb\xaf>:\xa3\xb2\x02cz\x16\x9f\x85\xfb3\xa1\xc60\x03=\xb2.K\x86\x91\x80\xb4\x8f\x12\xf3r\x07\x86;\xa1\xdc\xd3{\xf8\x1c\x18\x94\xc9sH{=\x17R\xf8\x0f4\x05\xe3*\x13~\xa5\x13\x88L\x11\xf0\xf2%\x8cv\xe1\x11l\xee\xec\xb8}P\x8b\x9fVK7wv\xe0\x11$\x8c\xec'\x98\x0e\xe4\xc5\x0b\xd8\x85{\xc8rt\x88$:\xa4\xba\xe3U,\xd1\x10dH\\\x82\x03\xfb\x01v\xf1\x9a\xe6\xab\x86\x04c\x18=\xcdu=\xe5\xb6\x86\xda\xb66E)\xbe*|\x0f\x19h\xd4:\xdb\xf9\x9b1\xa6\xdfX\xc4\xd1*\xff\xe2\x04(\x16 \xbd\xc7\xaf\xdf\xd4~\x15C|0)\x87S\xd0\xf67'm\x11:\xe6n.F\x82b@>\xd2Hk2\x0b\xad1`\xe7V\x05;q\xe7g\xd3\x08\x97\x8f-\xfa\xee\x16\xf2|J\xe9\xa6\xaet\xb7R\xb8\xbb\x05\x8f\x00Mr\xd8\x8c\x9c\x88a\xecS\x17z@\xa7\xa9\xf9R\xb5\x8c\xa0[\xfc\x0e\xf1\x1b\x8f\x08\xc6\xb0Y\xa0k\xa9\x9d\xa1\xae\x9d\xedZ\xe1\x8b\x17P\xedqw\x1b\x1b\x1e\x15\xc8\\j\xb9>\xc0\x17/j\x0d\xefn\x97\xdb\xebC\\F\xbc\xfc\xd7Ws\x10f\x89\xb6\xa6\xff+\x87\x9c\xacs\x08F\x85\xe1\x03\x99\xb4\xc8\xe2\xd1`\xf0\xea\xf8\xca3\xdfd\xcf_\x91\xd7\xb8*\xdcx\x1cP\xdb~\xe3\x97\xd2A\xee%\xccv_\xf8\x9c+\x83\xcd\x1ed\"uh0MgE>\xb0\\]\xcb\x01>\xeb\ny\x15\xd5\xb2q\xb3Q\x87\x88\x89\xe3\x87\x10\xdb\xadx\"\xd1$Jj\x16\x8eB\xd6\xcf\x1a\xbb\x96\x9f/\xb2\xd6A\xe6\xa7\xb9\x0fVM\x98!$\xf9\xa1H\x9a\xc1\"\"[\xb4\xca\xdf\x91#Ny[~!\x83S\xd7O\xfc\xb3\\\x8dZ\xec\xfa/\xdc\xc4k\xe2\xc7\xc9\xbf\xd7.\x16\xbe\xbb\x96\x9dJ\xc4\x8c\x0e\xe2\x98\xdc9\x99t\x81\xcco{\xd8\x16\xce\xbel\x0bg\xb8\x85\xf5[7j\xbdu}\xf4\xe7G\xc3!\x85\xe2^\xd1\xbb\x84\xbd]u\xf17\xb5B\xa6\xe9\x8c\xd12\x7f:d\xe7\x0c\xfe\x9d\xcd\xfe\xe9hoXG\x1dW}]\x0d{&R\xd1\x18\xd6\xd1/\xad#\xd1\xae#1\xad#[-\x82\xab\x15\xd5@\xdc\x07_\xc0.\x12\xb0\x8b\x10vF6\xc6\xff7\xd8\xc1\xe5s\xfb\x81\xfb8\xa1\xc6\x0bt\xbdw\xe1\xf7\xdb\xc4\xd6#\xd6\x0f\xc1\x10\x08L9\xc9\xc2\xbe\xb0D\xccIm8Mg\xd6\xfd\xf2mQ\xdeD\xe9\xff\xed<*\xffH\x9ed\xe1\x9c.\xfc\x90\xce\xbfR\xfbb\x81\xc3\xc3\xa1\xea\xd6\xf2\xcd?T\xa6\xbb\x8e\xfc\xb9\x8c/f\xeb]'\xcd\xd94\x7f\xffn\xae\xd1\x7f$Ob\xba\xa4\xb7\xdf\xe5F\xe5\x01\xca3\x1f\x03\xd5`\xbd6\xe7S\xeeW\xa7\xe7\xb3\x19\x11xr\xf6\xc4\x99.\xfd\xd5\xec\x07\xf7\xcfO\xe4\x05\x87\xbez\xac 9\x00\xd2z\xfa\x89\xd4\xbe\x0f\x8dw \xfc\xc2C\x9a\xf2\x86\xd3\x11\xcab\xf2\x16\xe1%\x93K[\x9c\xd8\xac'4\xeb\x9d\xa6\x85!P\\\xb2 *\x9a\xa9\xb5\xf2\xbd\x8f\xe1\x7f\x0e\xc4\xe56Q\x80\xceo\xe1\xaa\xd0-\x19\x13\xf5\xc1\x001\xbc\xd0*.H\xd3~U\x96\xf9J*\x913j\xbc\x83\xb6&1\x0f%(\xd6\x05a\xb0\xea\x01\x1d$Q\x16{\x14z\xac\xc0\x08X:X\x06\xd1\x05 \xc4\xd5_o\x1f\xbaK\x1e\xb9\xaf\xc8D_\x11\xf5\x9fV\xca3\x9b\xd2\xaf\\5i\xd6.\x94_\x08`\x1f\x9eU\xc8 \xec\xc3\xa8r\xad\xb5\x80}\xd8\xda\xac`\x03+\xdb*\x97\xcdY\xd9v\xb9\xec\x92\x95\xed\x94\xcb\xaeY\xd9^\xb9l\xc5\xca\x9e\x96\xcb\x96\xac\xac2\xbe;\xd8\x87\xed\xcaX.XY\xa5\xdfsVV\xe9\xf7\x06\xf6a\xa7\xd2\xc7!\xec\xc3n\xa5\xbd[VV\x99\xdb +\xab\xf4\xf1\x8a\x81\xaf\xe2\x93x\xc5\xca*\xef\x1e\xb0\xb2\xddr\xd91\xe6/\xacT\xfc\x80\x85\x95^N\xb1\xb02\x95\xf7\xb0\xafA\xfa\xe1\x18\xbaggC\xcdQ\xb4\x87O\x88\xe6\xc9S|r\xa1y\xf2\x0c\x9f\xa4\x9a'#\xdeQ\xa8{4\xc2G\xd7\xbaG\x9b\xf8h\xa1{\xb4\x85\x8f\xaa\x0c\x1d\xfbl\xf2\xa1Wu\xd1\xec\xb3\xb5=\x86\xc7gg\xdd\xc7\x9a\xb1\xf3\xbe\xce\xce\xb4\x9d\xf1\xde\x8et\xcfv\xf9\xd4\xceu\x90\xda\xdc\xe2\xad\xbe\xd3?\xe4\xad~\xa8(\x1a\xcaU\xdf\xb2\xf3\xba{\xd7\xedC\xf7\xaf\xec\xbf;\x9a\xe0w\xf1\xe7\xf0\x84\xfdA\xb6\xb7{\xcc\xff?b\xff\xe3W\xfe-\xc2\xaf\xfc\xffc\xac\xbdX`E\xf1\xe7\xcd\x9b\xeeL\x17U\xe3\x8f:\x9d,\xb4\xb6\x95\xabhn\x82\xb2ou-\xeb\xf3\xc8\x19\x9b;;.\xe7\x85n\xbb<\x80\xeff\xb9\xad\xdc\x1a\x19\xab\xef\xee\xecl\xc9\x172\xf1\xc2\xb6\xe6\x05=\xd7\xde\xe1\x8dlo>\xdb~\xb6\xbb\xb7\xf9l\xc7u\xcb\x11q\xbdhNa\x1d\xf9\xa5\x8c\xb9<\x00\xe2\x8a\xdc\xc9L\x0c\xcb\x98\x92\x94\xc6<\x19\xc3\xf0\xf6\x8d\xf8\xe8X\x07\x1c\xe8'1\xd0\xa7\xe5\x95-\xfd\x92\x87\xde\xd9YW\x84u,\xe28\x0e\xf1\xfd\x8d\\Vv\xa1\xa7\x08p\xba\xc8%G\xf5\xc5R\xa2X\xf3x\xe1y\x98n_\x06\xc9\x961\xa7\xdf\x93\xf4r\xb0\"\xb7\x0e\xa6\x0c\x17\xc5\xf7\xf7\xb0\xe9\xcah\xdfW\xfe\xfamxM\x02\x7f\xce\xdbR~\xab\xa1\xb9\x17At\xf3\x8e^\xd3\x00\x99X?9\x8a\x18L\x97\x0e-\x9e\xb8\xd2\x17I)\x93\xbd\xa4w\x81\x08\xc1]:YMLu=%p\x93Ym\xe1\xdb\xff\x8f\xcf\x06\xcds(\x12\xa2pk\x0d\x9e\x845\xae\xdc\x1b\xa4\xf9\xd5\x0c\x8f\x04\xe0?\xe7ARG\x90\x89\x86X?\xac=\x91\xe4!\x18\xa8>\x97}\xc8xg\x19^\\\xab\x8f\xa6\x19\x1b_8%3\xd8\xaf\x06\xc3\x05E\xcd]\xc6gGA1\x868\xd8b\"\x0d%s\xdc\x89\xe2\xf4\x17z\xc7\xb3\xcf\xe4?\xca\x01\xddC\xfa\x9b?\x97\x01\xd5\xf3_\xf7\xf7\xf0T\x86C\x0f\xa3\x8ft\xc1\xdb\x10_\xd5\x16\xc2\xe8U\xb4Z\x93\xf4=\xdb\xce\xbc\x8eR\xa0\xd6\xf4\"\x86\xdd\xe8zu#@\xa9\x14\xa85\xbf \x84\xbcLOd{\xe5\xf0\xb6\x1cu\x1e\xd3`\x85E\xe4\xfaR\xb6F,\x99g\xec\x0d\x92Ra\xaf\xc0K\xb3\x84\xce_\xabOJ\xb1\xfet4\xe2\xa3v3!\xd2\x8b\xdd\x14\xc1~%\x9al\xea\x8at\xc6\xfc~nc\xc4\xf1\x9a\x8d-Q\x83\xa5\x81\x0f/ y\xeeb\xda\x064`\x97\xd9\xfa\x85K\x1f;\xfb\xc1w\xd1\xec\x87\xfb\x8a\x88\xac\x16\xa2\x83\x04\xb3\xbd\x95\x9e\xb0.ydW\x1f\xad\x86\xf8\xf7P\xd5C\x9c Q0\x14x\xdd\xdb\x87\xc8eC\xec\xedW]\xcb\x04\ngV\x10\xbd\xb6\x85\xe3\xd6\x87\xdb\x95\xe4\xf2\x07H]k\xdb\xef\xea$Z\xca\x1c\x08\xb1\x05\xc3>\xfe\xd5\xbe\x8e\x9f\x8c\x0dmm\x96\xa3T\x8d6wQ~\xdf\x1dU\xc3`m>\xdba\xbf\x18\x87RxP0\x96D\xfc\xba\xbf\x87\x9d\xbd\xad\xed\xed\xf2{\xec0\xdeb\xbfx~\x8a\xbc*+\xdf\xadt=\x1am\x8fF#\xebD\xfef\x9c\x08N\xb1\xd2\x0f\xb6\xcc\xbe^\x14__\x15_\xaf\x8a\xaf\xc7\xc5\xd7\xd3\xe2\xebM\xf1\xf5\xd2:\xac7\xc6a=\xf9\xfd,\xfc\x01dT\x13u\xb9\xe57\xb6\x91\xfe^\x0f<\xf2#cs\xcaE\xbf2Y\xa5\\\xf43\xe3m\xcaE\xbf\x01\x06\x99\xae\x0f\xf2/\xf6\xd0\xebl\x1c\xbej\xe7\xd4\xd1\x84B \x0c\xe5\x0b\xdc\xe9<\xeeG\xfd\xe9{N\x07j\xe5\x8cS\xfd$\x12\x92\x96r\x96TV\x12\x83\xf3t\xde9\xfc0\xca\xb0\xec\xbc\xf8z[|\xbd)\xbe^\x14__\x15_\xaf\x8a\xaf\xc7\xc5\xd7\xd3\xe2\xebe\xf1uU|\xbd+\xbe\xae\x8b\xaf\x1f\x8a\xaf\x87\xc5\xd7e\xf1u^|\xbd.\xbe\x9e\x14_\x0f\xc4\xcc\xcc\x89^49\x1f\xd2\xbaJ(7y\x18r\xba\xaaP\xd9^\xcfv\xb3\xd5\xf9$\xc8\xae\xd2\xbf\xafD\x05\xfaM\xaf\x04f+\xf7\x96\x8d\xfdoZc)\x13\x83\xfd\xc5\xc3\xd4\x0e\x12 \x9f\xe7rd\x1d\xf6a\x01hQ\xcdX\x15\xe4Ya\x03\xde\xe3\xe9\xf2\x92[\xf1vA$\xd2\x9c\xbeg'\xc3\xac\x8f\x88\xe9\x1b\xf4\xdc\xb9P\xc1@\xf4\xb5\x00\xd1n$\x1c%\x0e\xbaq\xa8\x7f2\xb7&\xc6\x85\xdcM\x00\x13\x08\xe1%<\x83\"\xed\xd2o0\xc6\xf2\x9fa\x0c\xbf\xc2\x98\x8f\xb2\x13\xf1\x87\x7f\x871\xfch%m\x7fU\xa8Fu\x85\xe8`\x9e\xadJ\xbc\xb7\xe9.\x84\xdf\xfe\xa6\xd5\xdb\xdf\xee\xe3\xc7\x86\x9b\xd9N\x85!\xe3\xa1\xfd\x19H\xde\x16!\x08\x14W\xd3\xc7\x18\xa0\x1dz\xec\x9b\xfeF\xd9\xcf\xb9\x0b;\xe9\x94\xfc\x17'\xed\xf3$\xc6\xbeH\xdeL\x14\x85\xa3\xd1eY\x80\xb0Q~\x92\x1f)G\xe97\x02\x94\xdcYd\xc0H}\xa6\xd9\x90\x87D\xe3\xd9\x82\xccv\xa8 p\xa2\x9ah6\x9c\xe5\x19H\x15T0\xc5n\x04\xeb\xbd\x0d@\x9e$\xa9\xbe{\x8d\x96\xaf\xe8Q\xfd\xf7F?jM\x06{\x90o\xff\xd8\xf8\xb6\xc0\xed\xc2\xe7\xe51z\xbb<~\xdcuM\xf8\x0e\xb2\xf5_\x9b[\xbfg\xad\xff\xc2\xf3\x04r\xbca\xcd\xfe\xe4|dE\xbe)M\"\xb6\xfess\xeb/\x8d\xad\xb7\xc67(\xcb\xee\xb0\x0fO\x9c\xb3\xb0\xe7:\xd3\xdf\xcf\xc2\xd9\x0f\xee\x93\xa5~W\xa9\x1f\x94\xc9\xb3\x9a|\xe1r\xd9DP\x96\x0c&\x90\xa1\x9aA\xb8U@4\x08H\x92\xbeeo\xf0\xfc\xe0\x7f\xce#\xd3\x0d\xfb\x98\x7f;u\x0d{Z\xfd\xa0\xa8~\x16\xcaP0Ct\xffd$^\xfe6c,\x88\xc9k$l\xf5#b\x0c\xc6\xaa\x0b\xb01\xc1\xa7\xfaam'\xc0\xc3\xbc5O\x04\xc4\xc9\x15O7\x1b\xc6\x0cyJ\x18>\xcb\x00o\x80|\xb6\xd3\x13\xe81Y\x0f\x13\xdc38\x88\n0a_\xc7<\x9f\x1d\xf4\xe0\xcfN\xc0\x85I\xbc\xb5\xb0vf\x8ey \x05*\xfa\xc6J\x9f\x19z\x12\xb7 \xdb\x7fk\xc4\xf6\xc7\x98\xac\xa4\xf9~O~rA\xba\xe0\xca\x85\xa4l\xe4\x91\x84\xce\xb4\xc2\x08\xbd\xe4\x02\xda.\xa0\xe7\x0e\x13\xd7v\xb7F\xc8\x04\xd4\x83\x95\xfa(\x15\xf3wv\xb76\x87PD.\xdd\xda\xdeb\xc26*\xa6\xfepF\xc3Mt`Na\x83\xb7\xce\x93\xc9l\x88\xd7z\\\x86c`c\xbc\xdb\x98\xeb\xbc\xde\x0b\xab\xd9\xde>t\x90\x93\xf9\xe4`Zh:\xf5g0\xe6\xa7\xdc\x1fz\xb74\xf5#\xafSmk\xe6\xf2\x8c\xa2\xfa\x86D \x08\xf3\x92\x95t\xba\xfej\x1d%\x89\x7f\x11\x08\xc7\xf71\xf8BU\xc9\x8d@x \xb2n\x13c\xf7\xd9\xb1\xcb\xf3\xbf\x983K\xc1\xbe\xe4\xd7\xa4\x02\x10\xe3\xafin\x01\xe221)\xc5\x95\xd2\xea/B\xb6\xdfx\x8em\xfd{\x9b\x9c\x1e\xe5\xcf\xd8(\xba\xbd..\x97\xdc\x94\x1b\xfc\xb09\x0b\xbb\xd6\x19\xfed\x14\x84MCf\xb8Q\x90\xd4\x8d\x11\xa6\xf7\xb4\xf6\xf1g-\x14\xd1\x1aAq\xbcV\xc9k\xce\x1bTl\x87UE\x96\xe2CY+:\xae2\x90\x85*\x9d\xc0\x0b\x08\xd8\x1f=\x07\x89\xa2\xa3\xe31)oJf\xee\xa0\x88s\xc0P\xc4\x1b\xe4\xf6\x06\\\xcb\xdd\xf1*5\xba\xdc\xbc\x80aR\x9e9\x90\xd3XY/Z\x80\xfaR\xdeN\xder\xa5#F\xfal\x82.\x95\xea]\x98\x80\x87\xdf\xc7\xd0\x9dt\xfb\xe0\x0dr\xbb\x04\xdb\xb1\xc2\xdaXp\x95\xa8\xb8\x1a\x99b33>\x0e5>N\xdfh>\x91\xf1\xbb\x00\xb5K\xee\x13\xa1\x94\xb03sa\xa1\xe2\x06\x0d\x80\xfaA9/\xa9\xf5\x85\x11-\xca\xf4\x99'\xe8\xf7D\x82\xfe\xc7/1k\xbf\xe0\xfdc \x9eG\xd7i\x82Wo\xfc\x04\xe6i\xc2\x10\x02\x8f\x9bN\x9a\xf2\xb4\xa6\x8b\x19\x9f\x99\xf9\xe41OY\x8a\xc3\xb1\xb6\x8a5\xfe\xb4\xc6&K+\xe6w\xec\xfa\xd1\xffU\xd2\xf1\xf1M_\x95\xd9\xd5\xfb\x83|\xc8a\x9fo\xe5\xb0\x0f\x9d\x11F\xc1\xc9\x7f\x0e5\xd9\x82\x13\xc8\xb1\x847Q\xcd\xdb\x9a\x13?U\xa4}\xc1#\xc4\x95\xa5\xdcjVS\xd6|\xd0\x87E\x1f\xed?\xea\xdeR\x0cAQ\xd9\x91?B\x17\x1f\xf9\xa4\xae.C\x85\x9d\xa3h(\xc5\x8dXqI\x92\xcb\x04\xa1\x8b7f\x85o\x06\x02\xeb\xd1#\xb6\x05\x95\x02T\xdb\xdc\xdf\x83P\x84K\xa5\x02\x12\x86\x97 R.\xfb\xa8*u\x85Z\x8aVn_\xa6\xc1\xcc-\xa0\xdf\xfd!\xa6\x8bs\x86\xe3\x15\xf1\xderQ\x8d\xd3\xc2\xb6;\x9a\xc6q\x08\xba\xf2}\x9eR\xdc\x00W\x97\xaf\x1c\xcf*\xab\xde_\x8aU\x96\xc7\xcd\x04\x9cN\xcd\x96I\xa3!\x92\x9f\xb2r\xb9\xaf.\xb0\xc5\xa2\x95\xdf\x1c\xa7\xc4\"\xe0]V\xeeYM\xb9\xf1\x91\xd6H\x1f\x04y\xa5\xe8\xc2%~w\x9aT\x80J\x0e\xd9\xe2$\xd0\xb4\xa3\x145\xb4\xa8\xbe\\\"u\xf9u\xe7*K\xd0\x92\x80\xc0\x05O|\xc3\x13\x98\xdb\x8c\x10\xa1\xa4b\xe5,\xc4e\xe9\xbe\x8d<\xe72\xd8\xc8E\x95=\x135\xc4\x823\xc8\xf8\x0c\xa9\x1d\x0c\x89$\xae\xb5D\x88\x89p\xca\x18\x9c\xcb\xa9?\x9b\xf5\x05\x8d\xe1\x96\x80\x19O\xcb\xce\xffq\xbc\xc7\xdd\xd5b\x07 \xe4\xc7\xbd\xc1\xbe\x15\x1e\x15L\xf0\x90\x89\xe0e\x1dO,\x1d\xd6,\xe77\x9f\x88 N\x13\xc6\xa8\x8a\xaf\xd0\xc5\x8d\xd7\x93\xaf0\x0e\x83S\x81\xd2\xdc\xd4\xa9$|\x1a\xc1\x17\xf4<.z\x1eC\x97\xe1uo_\xed\xdd$\xedHZk\xa2\xee\x89}&g\xe4K\xda\xe2\x14t\xe4QNG\x90\xc9\xe3\x9d3\xd9\xac\xbe[m[\xb5b#\x914\xec\xd3\xa0y\x9fz-\xf7i5\xa7\xb6\x97\xa3o%\xa7vV\xbf\x8a\x9f\xa0\x00\x8eR\x93\xa0`\xfc\x18\xc2\xbb\xddn\x1fq\x02\x95 S\xb6?\xbci\\`3N\xb63\xe2\x87_\x01\xd22N*\x8dq\x04\xcb\x8a%f2\x96q8\xc8x\xa3eF\xbd\x0e\x17\xaf\xb099\x14R\x1e\n\xb2\xe6Y{lR\x8f\xf5\xee?X\xaf \xeb\xbf\x11\xa3\x9a\xd0\xa9\x0b]\x05\xa9\xeac(\xa8\xa5\xf6`.\x1d-e\xf0~\xc9iRx\x00\xdb03\x93\x98i\xc16\xc5l'4\xd9\xe8\xa8\x84\"D[\x1d\x95\xe4)$4B\x12J\xcad\xa6%1\xc1\xb7\xba\x1b\x0c!\xc4W\x9e5\xb8Xy\xfb\xc2g\xca\xc2\x13\xce!\xcd\x9a\x16\xfd\x9fAF\x1a\xd6\x88\xb4X#\x85\"\x84&\x8a\x90\xf3\xbe\xd3xV\xdeA*1\xf091h\xd8\x8c\xae\xd0U\xb6\x82;Q7\xdc\xb4+S-7\xc2\xbe \xf0\xad6\x9cY\x94\xcc\xb7!\xd7(\x89@\x03I\x93\xf4X2\xd5k\xf4m\x84\xaa*-\x0b\xb98F.\x02\x8a\x9eT\x10-\x801/|,i\x048W$Kz!K/'\x95\xf9\x87G\x8f\xf8\xc5\xa4DbT\xe0\xd6\xc1]+i\xe2K\xca\xab\xc1\xc5N*\xc4\xce\xeeKu=\xfed\xee\xa8.\xd2\xe9D\xb5\xff2+\x03sm\x94.\xd4\x8c\xce\x1d\x87\xc7\xbb\x94-\xa3\xfb\x97\x89~*\xb4\xb3\xbe\xa2\xb9\xe5c'O \xa6\xd1\x80\x98}\xec7\x94\xc0\x14\xa1zO[Xy\x15ia|\xdc\x9c1\xf7ui\xbc\x85\x0fy\xbd\xd4\xed\xf3ce\xe0'<\xb4C\xaa\x89\xce.?Uf851\xc3\xd4I\xa7\xfeL@\xcd<\x12{G\xd5X\x11\x15K\xb8\xc8\xd6y\xc4y\xeb\xb0\xee\xc4\xca\xd0$\xe2dZ\xb9R\xf5\x0d\x97\xa8\x90\xaar-\x82,\x9a\xfa\xd3p6\xabL+\xd5\x98\x03\xe6\xe12b\xbb\xd2\x8fR\xab\"\x9b\xb5s\xc43\x02\xb0S\xe8\x1fUOB\xa9\x97V\xcc2q3\x84\xc8\x03\x85}6GZ\x9c\xb0\x13\x08%\x8b\x85\xda\xcbR\x0e\xf2b\xe7\xe5n\x9fr\xfbR\xaadh\x1f$dA_W\xac\x15,\x96{|\x8a\xf1\x80\xde\xa64\x9c;\xf5}\xc4m4\xc7@\xca\xab\x85'~et_\xe4\xf6\xa3z\xb1Z\x07,\x0d\xe9\xd5\xac\x07x\xd9\xd6q(\xecC\x8f\x9aC\xcaX\xa3\x99\xf3h\xe1\x97i\xba\xd6\x04\n\xe7\x0fo\x12C\x0cq\xd1\xdfS\xc1\xec\xd57T\xd1\xb8\xae \xd9zC\xf3\xdb\xdb[\xf6\xf6\x17\xda\xb1+-l\x8e\xec\x0d,\xa3\xf5%\x8d\xedm\xec5Lr\xe1\x07\xa6P\xebzs\x04\xeda\":\xf9\x16\x98%\x1d\xca\x1a\x83\xc4\xd47~d\xbc\xde\x99S/\x9a\xd3O\x1f\xdf\xbe\x8aV\xeb(\xa4a\xea(Q:\xcfzh\xb2\xc0\x18+\xcd\xceM\x07\xdc\x7f\xc2_\xdc5!{NT\xaa\xf1\x05$\xed\xd1\x9e\x8c\xdcQ\xdc\x0f\xa1\xcb;R\x9d\xcd\xf95\x0dZOO\xd0#\xde\x85X(6\xd1H\xf2\xd1#\x10G\x0f\x0dkS\x8cP\xb2\xdbG\xb6\xa0\xfe\x94'\xf03\xd0\xbe\\\xf4I\xd1O\xf2\x8f\xc8\x0f\x9d\xee\xa3\xae[!o}H\xb9go 2U\xb0\x94.\x92\xd1@b\xfa\xfb\xfe\xe4\xd1\xac\xe7\xeeO\x9c\xe9\xef\x8f\xb8\x95\x04\xae\xfa?>?G(\x86V3\x01i0\x159\xe8\xb4i6\x8fb\x156\xabg\x0b \x9b\xe2\x87\xfc\xba\xd7\x89\xa7\xfe\x8c\xb1\xc9-x\xa6\xf8a\x08^\xf8FnU}\x1a\xb9o\xe4\xde\xee\xb6\xd67rk\xb8\xa9\xf1\x8d\xec\x1e\xde\xae\xa9\x97\xd2\xb9\xaag+W\xcb\x14\xdf\x97\xf2\x93$\x7f\xe2\x87-\xc8\xb8\xe1\xcaL\xdc\x94\xf5a\xdd\x87y\x1f.\xfb\xe8\xc9\xa8\x89\x01\xba2X\xe2.\x0d\xe5w\xa8\xf9-\xafSE\xb5Yl\x8a\x92?\xf4\xe9\xdd\x9ar\x9fh\xa2\xe6R\x06\x950\\\xe8\xcf\x10\xb9+\x03=\x02\xe1\xddK\x1du\x04.\x04\xec)\xec\x8bh=\x1c\x10)W\x1a\xd3\x01Y\xaf\x83;'\xeeW#>}6\x0c\xf0\xdc\xech\x8f\x16\x12\xb0\x01\xe6\xfc\xedJ\xbc\xa0Kn\xb7\xf2R\x90\xa1P\xdei\xa0\xe8\xc0Z\xb9f\xcf\x16\xad\xc6t\xa35\x97dC\xa2\xb8\xb3t\xbbj\x01\xce\xb9\x9ac\xe3\x90\xed\xe0Z\xb59\xec\x83\x08\x05\x1fe\xa9s\xd3oa\x94\"A\x91\xc2\x068\x08\x0f{\x00\x88%L a\xdc\xdaB\xbep\xed\xd6\xf3s\x00ga\xabn\xdf\x06\x88\x1cZ\x1d\xad\xe7\n2\xa0Av\x00\x13\xb8`\xaf\x8c\xf9\x9d\x8e\x8a-5 M\xdf\xe3m\xd3\x1a\xe81\x97\x01\xea\\\x0bz\xb6Bl,$^f+\x1a\xa6 \x0f\xe4\x9f^\xfaI\x1fo+\xa8Ei\xc2^V\x90\xad\x10\xbf\x9b\x97\x0f\x14t\xe5\xbd\xd4\x91\x80 $\xab\x02fkmC\x9f\x1d\xd3\xc2\xb3\xd1-]u5\xea\xcd_8\x97m\xe4\xf0\xfa\xc6BSyG\xd7\xa8\xdb\xaf\x8cT{r`\xaa\x0bF\x85\xee\xefQFrB\xae\xfbA:\xd9a\xe7-\x99\xfb\xe1\x92g\xdap\x18\x95\xec\xae\xc8\xedo\xc4O\xbbty\xbb\xb5PS\xe5~p\xa2{#\x97u\xff@ *\xdd\xeb9\xe1-]B\x0f\xab\xac\x05\x82\xe43\xa1\xaf\x0f\x9d\xd8\xa9\xc4\xcd\xccs\x08\x15\x0c\":`\x8c\xc1#\xe1\xe3\x94\xcd\x0dH\x02\xb9|\xd9\xa9\xd8O~\xd6\xef\xd0\x1a\x80\xc6\xa0]\x14\x14-\xba\xe7\xe7\xd8\xfe\xf99R\xe4\x7f|\x86I\x15LZ-\xa89\xe8\x16\x8fC\xe7l?s\x1di\x15\x85\xe2`\x9f\x81vw\xe8\x0e\x16NUp\xee\x832\x0c\\\xbc>l\xba.\xeb\x7f*\xc3\xd9u\x1c\xaa\xda\x8c\xa1\x9aM\xe78\xd5\x14y*\xd5G\xcd6\x9e\xb0*0\x8cl\x87\xa8\xebK%\\\x8aFx\xf9\x9c\xd0\x1cM\xd0@\xf6\xb8\xae\x06\xad\x9a\xc1\xfe\xe33\xbf|\x19\x8b\x83\xa6\x82z\xde%\xf5\xae\xc6\x8aEv\xebM\xab\x92\xf5\x02\xe5\x8b\x8d\xdb\x82\xe8\x1b\x8f\x1d\x0fC6\xf0:\x0f\x1b\xd9\x97\xed}\xde\xdf\x18\xc7\xff\xcc}\xe0~oV\x1a2p\xed|E[\nx\xab2\xb4\x90\xad\xf7\xb4I\x88\x9d\xad\xbd-m\xdc\xa1\xa7\xba\xb0C\xa1\xb3]\xad\xcd\x07\xfft\xbbZ=\x10\xe5\xd5\x83\xc0\x13\xbdVG\xb9\xe0\xf5w\x86\xa5\xd3\xf0\x99\xf2+\x1a\xf8![\x1a\xa7\x82U\xeb\x1a\x19Z\xf8\xe1\xfc\xf5\xf1\xfb\xa3hN\xc7Ui6\xa6\xe1\x9c\xc6c\xf0\x07\xfc[e\x92\xe1*\xca\xc24\xd7\n\x1d\xa4\xbc\x11\x7f\xa0\x7fR~\xfb\x9a\xc6\x89\x1f\x85cH\xaa\xad&x\xc3v~\xc1\xe8\x05\x9d\x7fZ\xcfIJ\x931d\x83r\x89\xe15>\xd2\x93\xec\"\x8d)}\x1b\xa6\xd1\xab(L\x89\x1f\xb2y\x14\xc2\xabB\xa1\xf5\x91\x1a\xcf\xcf?\x1e\x1e\xbc:=\x7f}\xf8\xeb\xe9\xf1\xf1\xbb\x93\xf3\x9f\xde\x1d\xffx\xf0\xee\xfc\xe7\xe3\xe3_\xce\xd1CWk9e\x7fM,\n{\xbbU\xc5\x8ar>\x87\xe7iL\xa9.i\xf8\x92\xa6\xaf\x82(\xa1I\xfaV\x10\xe47q\xb4\xe2\xab\x12\x0f\xccO5\xba\x16\x8aK\xc6*\xc8\xcaM1\xc3@\xb9b\x18\x88e\xa0\xf3|\xcc\xfc\x02\x921\xfbR/\n=?`\xcb_\\h|\xaepH\xeboAL\xf6\xf6\xaa\xd1\xca$5\xa9\xeewNM\xf6\x9e\xea4u\xac\xbc\x1a\xdd,\x13\xe5U\xaa$\x88\xe1\xd3j\xbf\x81(\xaf\xf6\xcb\xe9\xc9\xde3==\xa9\x11\xc35'3\xa3*Y\x9a\xf3\xf2\xcd\xea\xe1w)\xcaG\x95\xf2kQ^\x9d\xeeJ\x94W\xc9\xe4R\x94W\xc1p'\xca\xab`\xb8\xe0\xe5[\xd5\xf6\xcfEy\xb5\xfd\x1bQ^\x9d\xef!*\x18\xdb\xf0n|{6\xc4\xce>D>\xeeP\xb8p/\x07\x87\xd74L\x0fW~\x9a\xd2Xl\xf0\x8f\x94x)\x96\xbf\xf3\x93\x94\x864vVn^\xf7C\x90-\xfd\xf0\xe7\xecB\xd4V\n\x8f\xe39\x8d\x1dR\xad\xfb)\xf5\x83D\xd4.Q\x0bga\xab\xcaj\x9c\xc6\x84\x91d\x12\xa0\x80\xde<\x82\xe4\xc7\xbb#\xb2\xa2\x9a\xfbC\xf69\xf1W\xeb\x80*\xd5\xc7pS\xa72\xecs\x18\xa64~G\xc9u\xb9v\xa6\xaf\xfd\xea\x92\x84\xcbrMCv\xb3\x13\x1a\x94\x07<\x86s}\xcd\x1f\xe9\"\x8a\xe9\xdbp\x9d\x95\xab\xd7]\xb4>#d~\x8e\x92\x02\xb8\x020?\xb1\xb5\xf3\xbd\xbc\xf8U@\x92\xc4\xf1\x8c\xf5O\xe9mZ\xa9|\x89\x95_\x1f\xbf\x97\xd7T\xa2\xaaR\xf2*\n\x17\xfe\x1235\xb4\xab\x99\xb4\xaey\xc1\x17}\xb5f%\xe5\xb1\x96\x0b\xdf\x10/\x8d\xe2\xbb\x16\xb1>\xa5\xc2\x81\xde\xc0\xba\x1a\x98\xb2\x80\xa68\xcd\xf3\x0d!\xc8\xf5iL\xc2\x84\xf0\x1e\xee4\x15\x7fd\xbc\x80\x1f.O\xd2\x98\xa4ty\xe7\\c\xa5\xda\xd8\xc3k?\x8e\xc2\x15\x0dS'0K\xf3\xf8\xed\x8b\xc8\xbf\x99F\x08\x00\xfb\x8cw\xa9\x03\xa8Kb\x9flxY\x1c\xd30\xed\x8eu\xf7 \xbc\xca\x9c\xa6\xc4\x0f\x12k\x15?a\xac\xcf\xdcV\xe7\xd2\x9f\xcfih\xab!\xfc\x02mU\xae\xe8]r\x19\xc5\xa9\x97\xa5\xd6\x01\x05\xe4\x82\x06\xb6\nq\x14\xd09M\xbc\xd8_#\x07e\xa9J\xb24\xf2\"FMRj\xab\x87\x92\x97\x1d\x06\xf4vM\xc2y\x03\x9cH\xb2\x8e\xd6\xd9\xda:=zm\x9f\xde*\x9a\x13{\x05\x19\xb5\xbc\xb1R\x82d\x8c-\xaf\xadj\x14\xfb4LI\x13,\xf1\xce\xfa2\n\xe64\xb6V\x8bi\x92\xd8\xc1\x14S2\x8f\xc2\xe0\xce^\xe7o\x99\x1f\xdb\xdb\xe1\xd3k\xa8\x13\xc5\xd6\x1drM\x82\x8c\xae\xc8ms\x1d\xdf\n\x1d\xac\x13F7\x8duRzk\x1d\x10I\xa3\x95\xef\xd9j\\d\x89\x15t\x81\x7fm]\xef\x98\x06\xf4\x9a4\x10\x0eF\x7f\x16\x0b&\x9f[j-crqa\x87?\xa3\xc2\xd7\xb8]i8o\xe8\xd4\x8b\x02\x8f\xf1\xe1\x0du\xd0P\xae\xa1N\xb2&\xd6\xe5\xf2\xa20\x8d\xa3\x06\xca\x884\xe6\x82\xce/\xac\xe0F\xcf\xe8\x15M\x12\xb2\xb4\x82}\x11D7id]8F\xf9\x82\xa6\xfe\xa2\x9b\xd0:\xecu\x94\xf8aB\xadP\x8c\xa3\x9bFH\xc7\xd1M#\xa4\xe3\xe8\xa6 \xd2 M\x13\xff\xef\x08\x99R\x8d\x8a\x00\xf6\xfa\xf8\xfdA\x9a\xc6\xfeE\x96R\xc6\x1a\xb2s\xaf^E\xf2\x1dy\x8d\xbc\xc2W\x9c\xc2\x8aFgX\x95V\xc4\xd5\x81^\xa3\xb3\xb7W\xad.e\xb0\xaap#e\xb0\xaap\x83q\x08\x9f\xf5a\xb4\xd5\x87\xcd\xbd>lmV,[\x990\xb6\xb9\xa9 \x14\x1d\x0d<\x12~J\xe8\xeb\xe3\xf7\xa8O@\xde%\xf1\xd9\xcc\x91\x0fE\xbd/O\x11Q~\x19\xc5\xb5R\xda\xfcjS\xf3\xc8\xc3+\xda\xf7\xd1\x9cb3\xb2\x00\xa4\xc3\xa0,\x18\xa8U\xab\xca\"~\xd3Zm\x9c\xf1\xae\xd5\x01\xb2\x07\x1d\xee\xb2\xe7\xd4\x0dk1\xf5\xbbHv\xc1V\x9f\xb8F\x05\xcaz \x14C\xac\x06\x9a\x07\xbd\x0dS'/u\xdc>\x8c\x86.\x8f\xe7\xa7\x11?+cu:\x1e\xc8HT\x0b\xc0\xec\xbe\xec\x0b\x86\xe4\xabL\xf6Z\x13\xa6{\x95G-\xc5t\xbc\xaf\x84W\x03\xe35K\xf5\x96\xdax\xd2\x17\x85\\\xa1\xe3\x00\xd9g}I\x12:\xffH\x97~\xc2\xf8X?\n\xe5\xb6\xd0Vg\x9f\x8b\xec\x82\xf1zc\xe8F\xa1\"\xb9X\xbc\x10<\xb2N\xb3\xb8\xfe\xca+^^\xb7\xe5\x87\xfa\xde\x96\x9f9]\xd3pNC\x0f\xd9\xdai7\x8d\xd6*\xda\x86\xf3n\x1fX\xe1/\xf4\xee\x03\xe3\"\xc4O\x862b\x98\xf8\xfb\x03IR\xda\xd5$\xe5\xab\xf7\xea\x95\x9a\xffN\x80\xac\xce\xa1\x1d,\xcbo}#p\xfe\x18d\xb1\x80\x92 \xb2\xaf\xa3\x9bP\x0f\xe7_\xe8\xdd\xa7\xb5\xf8\xfe>\xca\x12\x8aU\x1f\n\xe7\x93\x94\xc4\xdf\x0be_U\xba\xf9\x02X\xe3{\xdf\x15\xdabd\xff,xs\xc9\xf6\xfb\x03\x9c\xf7\xf3\x05\x10\xe7/~W\x90\xcb\xb1}C\x98\x97J*\xe3\xbb\x13\xaa\xbe\xbc07\x9b\xba\xd0^\xa5I{r\xad\xb2\x83[C\xe7C\xb3ZD\xd7r\xf7\xa2G\xc5\xab\xf2\xe1\xabk\x18gim:o {\xd0D\xd3S\x9b\xe3\x105\x19\xa8\x97@k\xa9\x84ki\xb7\x00\xd7\xc4\xac\xb3F0j\xb2\x1c\xd7ymhL \xafe\xde\xb7\x01W\xa0\x94G!:1\x05A\xe9\xceIJ\x90\xbbIa\x02\xe9\x80\xfd\xac\xdeI\x14#b]\xdd\xe4,Y}t\x87\x92\x8f5\x84\xa6\xcd\xfa\xba\xd8\x0e\x1e\x86l\xb3\x99FC\x13^\x82\xbaT5\xf2\xd6\x18\xf3k9\xa8\x9e z\xe39]\x17\xec\xbczX\x07\x87\xe1\xbc}\xf3\x82Z<\xac\x07\xfeR\x13\x9d\xe0\xd7O7\xdc\x96\x10\x85\x8fG\"J|u\xb8h=\xd7df\"1M\xd9\xc4\"\x92\xd3\xa3G\xca\x8e-\x07\xba\x16\x031\xf7\x8e\xab\xe1\xf6AI\x18^\x16\x08\x00\xf9a\xf6.\xc6q\x17\xe1{kMp\x1c\xab>:\x0c\xd1j\x8f\xe7\xa9c\xf2\xcd\xcd`I\xd3\xd7$%\x8e\xcb\x81\xb3\x0f>\xdawEQ@\xe7NTu\x05`X\xbd\xc0,\xc4E\xa5\xac\xd8\x03udO\\X\xf0]V\x8bsbp\x05\x95\x97\xd9\xe7Z\x7f\xfb\xdc\x92GDH\x91m\xb7qn\x8c\x07\xc4\xf3\xb2U\x16\x90\x94\x9e\xdeD\x1f\xd8\xf1\xfb\xdaO\xd6x\xf9\x9c\xe0E\xca\xc2J\x8dn\x1b\xf6;\xa9\xcf\xbf\x83\xd1\xa2\xe6U\x13\x9fo\xb6\xe3[m\xc7s\xa7\x1a\xb0F~\xda\x1c\x1c\xf2\x93\x1fF7\x97\xbew\x89\x8bp\x0d\x13\xbe\"cp\xee\xc4u\xd8\xaa\xa9\xabBd0\xf7\x95\x1bv\xe3\xfa\xea\x1b\x04\xe5&\x02Q\x1dc_\xdf\x15C\n\xf5\xef5\x86\xd9S\xf6]3M\xc1\xad\xdc\x82\\0d\xb81\xad,:5\xd4\x17\xb6\x88\x0c\xd7\xf1\xd8\xdc\x04\x07cj\x05\x14\xc0)\x1b\xbb\x11z\xfe \xa6\x01% un\xdc~~\xe0\xf5\x0d\x01,\xf5\xae\xce\xeda\x06\x0fBu.O\xb6Z\xabo\x8e\xe1\x8f\x1eA\xa7\x85iD\xe5m\x87\x0e\xbc4\x0e~\xa1w\xb8\x1ayJ~\xd8\xd0\xd1\xa2\xcf\xd1s\x80\xf2\x83\xf7\xba\xf9\xbe\xb9t<]XD\xa8\xb1\xa8\xf8*\x1b \xba1\x8b\xdcQ\x1a\xda\xd6HX\x01J\x810\xc1\xaa\xac\x96\xbc\x0d\x1d\x9c\xdf\xc4d\xbd\xa6\xf1I*\xb2~\xa4\xe5\"\xf3\xd5\x01gT0\xd0\x980\xd7\x0d8\xaf\xd3\x0d\xb3\xd5\x05\x8d\xf3\x95c\x0b`\x19\x0b(\xacw\x97\xe7\x8c\xc3\x03\xcc\xdc3`\xf4\xb5%Ms\x93TG\x9cyn\x112\x17\x1d\xefk\x15\xb4+\"?\xfa{\x8dz)\x9eB\x81\xd1\xe1D\xafp}\x8f\xa5_)*\xef=\xd595\xab)\xde#q\xa4\x8a$\xe2V\xb4i\x197\xd5@\xe0\xf8\xe5\\L\x17\xf5\x85\x928\x18\xd60\xd7\xe2\xce\xaf\xcfV\x00\x13\xa0\x0e\x0f8\x92]\x04\xbe\x97SMd\x02\xe2\x01\x99\x17n\xa8\x07\xc9G\xba8\x8d0m_\xbf\x1ab\x0bp\xe1B.\xc8\x0d\xce\xa3\x9b\x90Vc\x96\x16K\xc8\xc4\xb7\xe42\xca\x02!\x06\xb5\x81\xa6\x84I]r\x03\xa9\xae\xac]a\xe4\xd0\xa7\x06\xe8c\xb9\xc8\x86\x16\xd3\x85LL)\x86_\xbf\x0f\x89\x8c\x03\xf0\xb5\x03P.W\xecX\x90\x13\xcb\x94\x8f\xc3\xc7\xafb\x1c}\x08\xf1m\x0c#\x9eG+,\xde\x8e\x90\xc0\xf1\xbdY\x062g\x89\xdb\x80\xf7\xff5\xc8\x8a<;\xe2fLW\xd15-\xa3';\xf9\xbf \x82~\x075\\)\xe2\x80Q\x03iP\x8a\xfc\xe6\xc1^\x0b\x13G\xedR\xa7\x91Xh\xf3\xfb\x1e\xe6\\\x9a@d\x89\xfc\xe2\xac\x8d\xc1V\xd8\xe73_\x81 W8z\xe6!\x8b\xf0\xa0\xfb\xfb\xe0\xb5\xc4\x94\xb9h\x16D\x92\xe4\x04\xc6|\xb05\xf5G`\xb8\x96\x07\x19uD\xb4\xe2Y[\xf1,\xad\\WlZ\xc9\xa0 P\x88\xd0\xb8S\x0ds\xc9ov\xf0\x9d\x80S'V\xcc\x17\x0c\xd3`]WVq_\x17\x95\x17\x04dV\xfa\xd1 \x81\xc60\xca\x96\xd1\x08\xd0\xaf\xca\x83\xa2\x9c\xb6\xb3\xe2\xbc\x7f\xf6\xab:\xa8y\xd9\xce\xa98D\x95{\xa9\xeb>\xac\xf8&w\xfb0e\xbf\x1a \xa9\xfe\x8c\xcf\xb0\xf4+\x0f\xd2Z\xf4\x1bv\x8e\xca\x00+~\x14\x0e\xde\x7f:9=\xfftrx\xfe\xe1\xe3\xf1\x87\xc3\x8f\xa7\x7f\xad\x9f\xafj\xf5\x9f\x0fN\xce\x7f<>~wxpt\xfe\xeb\xc1\xbbO\x87\xf5c\xb7Z\xfd\xe8\xd3\xfb\xc3\x8fo_\xe9\xaag\x9a\xea\x1f\x8eO\xde\x9e\xbe\xfd\xf5\xd0\xf6^\xa2y\xef\xf8\xd7\xc3\x8f\xef\x8e\x0f^\x1f\xbe\xb6\x0d0\xd0\x9eR~\xf2*K\xd2h\x95k;\xc6\xf0\x91.\x0fo\xd7J\x94\xfc\x94&\xe9\xe0\xc2\x0f\xe7NHo\xc4c\xa7\xfb\xbb3')\xb9'\xb1O\xdc\x0d\xcc\x01\x14\x0f\x0eNO?\xbe\xfd\xf1\xd3\xe9\xe1\xf9\xd1\xc1\xfb\xc3\xf3W?\x1f|\xc4\xbc@?\xfc\xb9\xab\xcb\x1ao\x0f\x85\xc1><\xb3\x8e\xd6\x07\xb9x\xfc\xea\x92\xc4\x185\xd1R+I~\xa1w\x96\x1a)\xc6\x1c3=\x0e\x82\xe8\xe6M\x16\x04'^L\xa99\xb6\x0c\xd6\xc3\x08%xjx\x96\x0e\x03\xcbp\x13\xcb\xa3\xbb\xd03w\x9f\xa5\xd1+\x11\x12\xc3\xdcD\x96F\x1f\x02rglE\\\xec\x9b\x9f\xd3 \xf8@\xe6s?\\\x1a;auN\xd6\xc4\xb3\xd6\xb9$\xf1\x89e\xd5\xbcK\x12\x04\x14-\x1c\x8c50\xb4\xc7\x18\"\xb87\x8e\xd6\xb7\xc0\xc2\x0bH\x92\xbc}m\x7f\xceYLS\x8d(H\x8cA\x89\xbc\x88\x01\xc1\x8cV^\x14\xa64\xb4@\x80??\x9c\xfb\x18\xe8\xc3^\xef6}O\xc3\xccZ'\xc6\xc1\x9a\x00%*\xbc\xf3\x13\xdb\x88\xa2xnFO/\x8e\x92\xe48\xf61L\x92\xa1\x0e\xb7\x0c2?\xa4\xa7\xbe\x05\xdey|\\\xc3,\xe6t\x81\x81 \x0dO\xfd\xd8\xdc\xb2\x08\x96c~9\xba \x83\x88\xcck\x91 \xf3\n1Y.\xad\x0bEC\x8f \x04\xc6\xe7\x8b(^Y\x1f\x1e\xd8\xe9\x14\xabr\xd8\xa2\x8f\xf74\xbd\x8c\xe6\xd6*G\xd1\xaf$\xf0\xb9\xff\xa9\x01 \xac\x1a\xe7\x0f\xcc-\xc5dE\x7f\x8cb\x8c\x16i\xa8sI\xc9\x9c\xc6f\xa4\xba\xa4\xfe\xf2\xd2\xdc\x05\x0f`d\x1c\xe4\xa5\xbf\xbc4\xbf\x1b\xd3\x85\xf5\xe1;b!`\x97\xe9*x\x13Y&\x96\xa6\xeb\xc3\xbfe\xfe\xb5\xb1\x86\xefY\x16\xd37/\x10\xden\xbd\xc7\xf0\x8d\xc6\x1a)]\xc6~j>\x81|3\xc4\xaf\xe8\xdd\x07\x12\x93\x95\xb5\x86\x15\xc9\xae\xfc\xd0d\xeet83ov*nd\xd9$e\xba]D(4\x7f2\xec\"~]\x19\x95\xea3\x08a\x08|\xda\xd7\xed\xbe\xca>3$WK\xbe\x052\xd5\xd0C\xe4\x87xVE2\x11\x9b\xf4\x99>?\x84.\xd9L\xac\xac\xe8\xa40\x9d\xe7\x89x\x04\x85r\xbas\xff\xfa\xffa\xefM\xdb\xdb\xc6\x91E\xe1\xef\xf3+`\xde9ij,)\x96\x9d\xc5Q\xe2\xf6u;\xce\xe9\xdc\xc9\xf6\xc6N/\xa3\xf6\xf8\xc0$$\xf1\x84\"8\\d\xbb;\xf9\xef\xef\x83\x02@\x82d\x81\xa4lgf\xeey.?\xd8\"P\x00\xb1\x16\xaa\n\xb58\xfa\xbe\xb7\xb9\xf2\x1e\xfe\xfd\xb7\xf4//\xdc\xdf\xae\xb6\x07\x0f\xf1Q\xe8\xa5\xdbX\xbb\xca\xcf\xc5\x9a\xa2\xee\xd6\x04\xd1DL:\xfd[\x91\x8ab\xf8\x8af\xde\xd2M\xdb/>\x01Ug\xb3\xc9yU\x1f\xbc9\xf1\xa8yVH\x94np\xe0\xd6u'\xe1\x82\x1bkd4\x0e\xa2\x88%b\xbb\x08\x9c<\x9b\x9c\x93m\xc2\xc86 g\xbb\xc8\n/B\x1a{\x00\xbds\xfe\x9cx\xa3\xd1\xf3\x81\xd4\x0c\x1d\x874\xcd`\xe1V\x17\xa6\\\xda\xd5O\xb1\xe6\x90\xce\xb5B\x98\x9a\xf4\xf4\x87\x9b3\xba\x80H\x0d\x8e\xf4\xb7^?a\xe7:`\xb3\x8c\x16\xadgkH\xb8;\x1f\x8c\xe7<9\xa1\xde\xd2\xcd\xeaF\x80E/br \x83~\x81\xfa\x89\x1b\x8d=\xd1x\xb1m\xd3\xc1s\xb3?\xa2\x87Z\xdfQn\xe42\x0f7\x99,\xf1\xfc\xd7\xfb\xd8\x7f\xfb\x96\xcdm_\x82\xaa\x1d\xedkT+7nI\xcd\x1cTC\xb7\xaa\xd0x`\x86#~\xf0\x808r\x06\xc05\x03T\xb2\xe5:)\xcb^G\x19K\xd64\x94\xe9\x83\x8a\xde\xbc\xa9\x13)p\xb3 \xcd\xe1\xf3r*\x82\x14\xfe\x8b\x06\x8bO{4\x0c\x19S\xf5\x83\xa9G\xc6V\xaa\xda\xea2\x13%\x0eI\xa3\x12 \xa2\xc0\xf6\xbf\xdb\x98\xa3\xdc\xaf6\x7f b'\xe1\x0d\xd5c\xb7U\xd5n\xb6\x85r\x86\xc3\x08\x16+20\x99\x91\xad\x0c.\xc1x\x81\x8c\xc8\xa4\x18 ]\x1c\x9d\x9c\xb1\x1c7\xa3\x9ez(\xf9AK\xbc=\xb5.d?\xcb[v\x18F\x15\x87\x1d\xc1Jf\x9c\xbc&UX\xec\xbaH\xef:7\x13[U\xfa\x9e\xe0\xe4\x05\xc9\x9e\x13\xbe\xbd= \xd1\x8c\x9f\x8bI\x98q\x04\x05i\xf5\x9c\xe6\xdcO\xc9\x8c\x9d\xdf\xef\xb6\xb3\x1c{XP\xa4\xbb\x1ec\xa0\x13\x89h\xed\xcd&C\xf2\xdd\x0b\xc9\x1f\x16\x02\xec\x03'Kr\xe6|\xff\xdd\x908/\x1e\xca\xcc\xef\x9d\xf3\xe6\xc1(J;/\x80\xb1\xfc\xde\x01`\xf5\x1b\xf1\xf4=\xdb+a_d\x97\xdc\xbf\xf9\xfeE\x96\xe8b\xc9\xf7/\x1e\xaaDK\x1d^\xd9\xda\xf5\x82\\\xaf\xc2(=\x00\x8eo\xfa\xf0\xe1\xd5\xd5\xd5\xf8jo\xcc\x93\xc5\xc3\xdd\x9d\x9d\x9d\x87\xe9zQ\xb4~\xbdhT5G\xa9x\xe7/\xceT\xf6\xe8\xf0\x85\x1f\xacU\xcb\xe0\xd7y\xf38\xa4 \xa3\n\xfc\xc5\x8a\xc6\n\x1a~!\xd0\x1e\x0f\xa7d\xb6\xdb\x1c\x01\xddi\x8f\x87\x8b\x84\xe7\xba\x9e\xe2\xd56\x1a\xe2 \xd9\x82E\xben\xc4<`\xa1\x9f\xb2L\xd5P\xbe\"%c\x9a\xd0\x95.(1\x8b*\xa6_\x90BY\x82vAM`\xeb\xdc\x11y\xb7\xb0\x90\"wDn\xcacy\xad\x8bdyT\xe5!l\x92\x1e&4\x13\x9a\x84\xe7\xcc9\xcf\xf0\x9c%\xb3\xdcog~#\x08\xa0,0\xad\xbb\xa7,w\xfa\xcc\xf1\x82\xc4\x0b\x81\xc5\xf5\xc2 \xfe@\xb3\xa5\xf8\xed\xb39\xb8n`a\x18\xc4)d/\xc4\x9f`E\xa5\xaf\x07\x08\x80\xa2\xfe\xd3\xe4?\x13\xea\x07,\x02-\xdd\x15M\xc1\x03D\xac\xaaR72\xf0\x93\x877\x0b^\xfc\xd4u\x88\xc244\xebHddJ'\xcd\xb8\xf4\x0d\xc1\xae\xa5\x060\x84;8/(\x1b\xfba6\x07\x0f>\xc4\x1b\x12*\x7f\x99\xc1xk^N:i\x88@\x9c6\\\x9e\"\xf3\xda)\xa2N?p!\xe4\xfcEpV\xd4\x02\x11T\xe8?\xe7/\xa5m\xb5\xf3\"\x0c\xa2\xcf\xe4\xe1\xf7\x0e\x99\x12\xe7\x85\xa3HP\xe7\xfb\x17\x0f\xcb\xdfN\xd9\x95`<\x0f\x12M}\xa9\xe4C\xd9e\xd4\xd3\xed]\x0f\x01T\xc8`Qwoe~q\xe1BO\xeeW\x1f\x9d\xb8\x82(\xe6\x83\x99\x80\xab\n%\xfb\xd0\x0e/\xa2>\xac$Nl\xde\xc1<\xa2S,\xd1p@\xa3\x19\xc9z$=-\x97\xa8\xcfI\x8eK7R5\x85x\x9c\xc1\x86\x02\xa6\n[\xfa\xa4\xce\xbe\xaa0\x83\x0dW>\xb1\xaa\xbe\x9e.\xe3\x0cN\x1e\xd7;+\xe3\x0c\xee=\xae\xc3\xaf\xf1\x15\xa5\xc2\x0c\xee\xd4;\xab\xc2\x0c\xee\xd4 \x91\x1b\xd5\xfc\xfa`\xaa0\x83\x0d\xbb\x8d\x0b)\xb5\xd9{6\x18B\xb8\xc4\x9d\xba\n\xa4\x8a7\xd8\x18\xbe\x13U\xf0\x11\x14\x9c\xf8\xeb\xebB\xa2`r\x0b\xa2\x85\x16{\xf7\xa8\x10\xf9;\xe4l\x19\xa4D\xd0\xf6\x82c%W4%:L,\xb9\xbc!\xff%\xce\xa9H\x9cS\xff5Fn6\xfed\x7f\xd3\x1f(Ka./\xde\xa1'\x83\xb4Z\xfd?36\xbe\xc8\xe8\xe2\\\x1a\xd7(s\xcfl\xac\x97\x85\x1e)\x99jY\x0c\x8a\x1fu&{O\x1dA\x1d\x88\n\x87\xf6\xc1?$\x0e\x81\x0btA\x8f\xa9\x91P\xaa;\x84\xcf \x9c\xda\x96\xb2\xe5\xc0\x8b\xe1\x1a\xc3\x91\x0f\xf6\x89]M\xb4uO6\xfc\xc9\x0eHu\x11\x9b\xd9\xb6\xfa\xce\xc0\xa3\xa4\x15B\x8a\x94\x9fL\x9cA\xa5\x81p\xcf^1\xd158\xf72W\x14\xddu\x86\xb0\xec\x07\xed.M>\xb6x\xdc\x90N\xb6\x133P\xfd\x15\xea!\x19\xf1\x88\xa8m\xa6\xd9\xf8b \xa1!\xda[\xe4\x05\xac\xf2\x07\x0f\xf4\xcfRN#h\xb6\xd7`\x99#a\xa6\xe2W\x87 \xd3\x91\x9b\x0dI\x00>\xb2\x16L\x06\x8e\x85\x88\xc7\x1f\x19\xf5o\xdc\x81v\xa6\xe5\xbe\xc4\xee\x0e\xa0QQ\x9aM \x12\xeb\x99\xa0\xb6v\x16\x97\x9a\xa1:3\xa6\x88\xdf\xe7\xafVKQd\xb6^6\\ \xcd\xc7q^\xc6\xc1\x05\xe7\x92\xa2\xcd\xca\xcfd\xbd\x85*Y\xb7\xa7}i\xbci|l5\x8ey*G\xf0g\xe9\xca\x02\xbe\xd8^\xcd\xa7F5\x97\xb7\xa9\xe6\x1f\x8dj\x16\xdd\xd5\xe8_b5\xbej\x1ca\x19\x8f\x8f.y\x02w\xd3\xe2\x7f\xed\xcc\xcbx|L#i\x0e\xe0x4\x8aCzc\x05)\xfc\xe1h\xc8L&4\x0b\xbc\xcc\xe5|\x1c+\x0f\x85\x8e\xaf\x12<\xcc\xab`\xc6\xe3\x93U\x9c\x05\xe0K\x90\xc9_\x08H\xe4%7q&\x81\xf4o\x0c\xccW >\x9a\x9d$p\xa3\x0e\x91\xfd\x9a\xd9o8\xf5\x99/\xfd\xd6:!\xbc@\xc8\x0f\x0b\xe0[\x96Q\xdf\x04^\xa9\x04\xbc\x80\x8a\x9f\x04\xb0)\x12\xe4\x08\x1c\x96\xe7\xa9\x18\xb0X\xfcG\xb2\xe5L\xe1\xd3$2\x81\x88\x80\xfc Z _$\xa0X\xe6\xc4\xeag\x13\xe8#\xcdX1s \xcd\x98m\xd6N\x19\x03\xf3\x0b'\x85\x1f8\x80lQ*\x7f! \x19\x0d\xa5\xcf\xc9T\xfeB@\xf24\x06I\x8f\x93\xca_M\x90\xb3`\xc5t\xb4$'\x0bV,\xc7B\x1ae<\xfe\x89\x87\xf9\xaa\xec\xdd\x1a^m\xfd\xfb\x99\x06\x99l\xfe\x95\xfce\xd0\x11\x18 \xf6{c\xff^\x8f\xb3\x84z\x9f{\xec\xfd\x1f\x1aeK_\xcb\x82\xe0~\xfdR\x1f\x98{\xf5\x8b\x1a\xb1\xf3\x199 \xea3\xd5\xcc\xc2W\xbe.\xfe\xc8)<\xf4ft\x81\x1du\xd2\xd3{\x00\xba\xfb\xd6 ?\xeap\xc6\xdd\xb5\xcb\xeaMW@\x05>\x06\xb9\xa9/\x86%\xfeA\xba\x1bU\x0e\xdc\xd4\x1e\x01\xb9\x8f\xfc\xcf\x06\x96k\xe0\xcb\x84\xd1\xcf\xcd,\xd9\xb0u\xe03nm6\xcd\xfd\x00\xcb%\xa6\x0c=+]a\xdb\xfbp>$\xaf\x06\xe4U]\x1e\x93\x01\xb1\xd7Vx\x1c\xe7\xe9\xd2E\x86 \x1b\x92W\xb3\xec\\t\xdcB7\xb7v\\j\xac\xdd\xef\x8c\x9cH4Y\xe0\xcb[\xceI\xb0Z|\xf3v\x0d\xc9\xb7\\Us\x9e\xac\xee\xb7\x0b\x1f\x19h\x88\x11'Q?Z\xbap\x9a_\xae\x02)\xb4\xd4\xbfn\xd7\x8d\xc0\x128E\xad \xe9*\xce\x1a\xd7\x8b]g4a\xf4~\xc7\xe1\xb5\n/>\x14\xad\xd3?\x99=$\x01\x82;\x7fj\xe0\xce\x1b\xa0\x9b\xe4\x89\xd0\x87p\xfa\x11\xe5\xfd\xe5%\x07&k\xb8\xa4\xe2\x94Fs\x12<\x1d\xae@\xb0\x0c\xb6\xba\x14\xc7\x1f\x96\xb5\xb4\xd4\x15\xac,\"\x90@\xc6\x14\xc5\xb2>\xb3\x9b\x05\x8b\xf0\xbc0\x88>\xe39\x82\x9e\xc1s\xd4\x1d\n\x96\xa5Ug\xb1<8\x0e\xf1\xac\xab\xcbN\xe1\xcd\xcf\xe84\x89Uf\x95\n\xc5\x89\xad%j5w}\xf3\xff\x80\xff\xbe\xe6WW,\xca\x83\x8c\xad\x90\xf2\xe4\xc7\x9ap\xedW\xd0\xa2\x99\xd1\xd1\xefG\xa3\xbf\x9d\xab\xff\xd3\x8b\xdf\xc6\xbf\x8d~\xf3\xcf\xff\xf2\xe7\x87U\xf0\xbf\"\xb7\x95\xff i\xb5\xd3\x06#B\xfe\x8cJ3\n\xedJ\x1d^\xd0\x199\x03\xf2\xfd\x01\xd9\xa9J0\x02[\xa4\x92\xbfA\xb0\x01\xe4{\xbf\xb4\xc5\xd8\x13|{\x15\x17u\x85\xc4\xf9Oy\x03\xfeW\xf03\xfb\xe5\x0bq\x7f\x05\xf3su\xcf!\x08\x98\xc7\nW\xfeU\xdf\xbd4\xdc\xbc\x16\x04NUFb\x86\x03\xc9\xe8\x824\\C\xea\xcc\x88\xaeX\x1aS\x8f}\xfa\xf8\x9aT\xe3ph\xb9\x94\xbee\xa8e\xc7 [\x07r\x9e\xb9e\x9dRZ[\x1a\xa4\x05,u%\xa99\x17\xb4\xbe\xa5\x9d*\xbcv\xee\xc6\x16\x08\xd5s\x18\x92\xd7Q\x90\x054\xd4t\xbb\xa0%\xe7C\x92\x0c\xc9\xd5@\xfa\xd8o\xfa\xf4\xfb\xda\xe6fP|\xfd\xa4\\\x98\xf0\x8d\xf71\x8b\xce\xe8B\x9a\xdd\x1cE\xfe\x87\xf2\xda*\x85\x0f\xb6,\xf6\xebZ]JA@\xd6\xa5[k\xe9\xa7h\xfe\xd6\xb5@)?\xce\x8a]yN\x0e\xc9\x89X\xdeR\xf3\xebD\xaet\xb2M\xae\xc5/\xb9\xfc\xadKC\x02\xf7@\xe0\x1b\x92\xaf]\x14O\xc7\xc9\xf2\xa68\x82\xe6c\x9ag\x1c\xc2\x88H\xd3\xba\xd6r\xc1x. M\xfe\xe3\x9fr\x14w4\xeb\xd3\xbfSwZ\xa9\" r\x99gY+-\xf7o\xd0\x8dNz\xb3\xa3Q\xff\xe8O\xbc(\x99J\xab\xbeN\x0f\xcc\xd0CCQ+\xd6\xc8\x03l\x83\xb3\xb0\xb8\xd2H\xe0J\x03?\xc7@\xa7\xa7~\x8f\x91t\xc6\x89\x06/\xee\xb3\xa4\xc5T\xcf\x0c)\x11\xd8\xcfP\x0d\xfa\x1ek\x03x\xa7\xfe\xa8N\xa1\x04\xe2\xa2\xd8\x0e\x04\xfdt8\x87\xd5\x8f\x03\xba$\x92\x96\x01\xcb.7P\x7f5&\xc6$6\xdc\xfd\xe3\xebP+\xa2\x08\xa2-\x80x\xf6r\x9a\xe5\xfc\xbe\xe2 \x94H\xdd@-\xa6\x8e\x06\x135\xa29\xc1\xdc\xeccOA'\x9b\xf4\xe4\x9fK,\x0c\xeb\xe8\x90\xbcm\x8e(\xc8\xd4\xc4\x87\xbcz\x9bk~ ]1\xd8\x10(\x01\x85.\xab\x94\xda'\xb9\xd4 \"\xdb\x07\xc4\x01\x15\xa5\xbc}\xc2\xfb\xc6\xcb0\xcc\xc2#\x9f%g\\\xf0\xf9\x81'\xdbA\x0eID\xa6\xfa\xf4\xa9\xd2\x1cf[\x1a\xad\x07\xfa\x03\xf4\x8eZ\x80^\xbfT\x15\x83\xech\xd0\xea\xd3\x1d;\xb5\xfb\xf9s_\x17\xe1Kp\xe2\x80\x93\x16\xb5\xad\xe6J1\xf7\x1c\x1f\x14\x0b\x85\x8f\xa5\xce#\xccRB\xca\x04divP=b\xc1\x7f\x98\x15\x1aYZUL\xd0\x1b\x86\xe2\x98M\x01R?T\xadu\xc0\x0df\x84p]\x83\x9d_)Q\n\x0c\xdc\x89\x1b\xb4\xd1\xc5f \xda\x86\xd3\x12\xbd\xef\xa5\xfcQ\x13\x8aT\xc5[\x18\xff7\x0f\"\xd7qng\xa7O\xca\xa5\xfc\xb3I\xa3 \xce\xf37\x15\x02,\x19{K\x9a\x1ce\xee\x8e\xd8\xbb\x90\xbcM\x1225\xe2^\x10\xeb\xca\xab\xd1\xb7\xbd\xa5\xa6Z\x89\xed~\x97X>\x86\xd3T\x94\x17\x08\xe2\x7f\xc6bs\xa4\x83\x89\xc0\xe8 \x84\x86\x06\x0c\xd8{\x05Z\x1bY\x9c\xd5i\xfbB\x94\xec\xca\xces\x12\x92\x17$\xd5\xb6\x94$\xdc\xde\x1e\xe8fI\x0e6\x19\x92t\x16\x9ew\x912\x8d\xe8\x14\x1e\x0b\x8c\xf0\x14\x9ba1\x8c6i\x0e\x0d\x06e\xdc\xceHv\xb0h\x81\x9b\xc1\xc9\xdf\x8czR7\xe8\xab\x16\xbb\xc5\x16\x00\x19=\xbe\x8c\x82o+\xd7\xefb\x8c\xb8M\xdc\xcb\x15 \x82f\xda\x96%\xb9\x17J\x9a\xdb\xa4\xb3\xbaMh\xe6\x9d\xda\xd4)\xba\xe56\xf1\xacn\x13\x9ay\xa76\xf5\xe0\x03\xb9M\xec\xaa[\x85f\"$\xb3\x9d\x01\x7fW\x14j\x13\xaapE@7`\n,\xa3 \xc4V\x19v\x8b\xf8\xfa-\xde\x95\xda\xd1\x15M\x8c!\xb9\xc6\x83\xe3\xde\x95\x03\xec1\x1f\x97X\x83\xee\xf0\xc9\xcee\xd9\xc1t\xfe\xd4\x8f\xe9\xac\x9f\xfc\xc8\x0co\x80\xade\x8cI\x0b\xcf\x98 >\x00\xf4\x03:\xf3\x08\xc3(Y~4Y\x1f\x7fl\x96 \xe7\x91Yq\x85+\xeb#YN\xed\xecZ;\x1f\x05\xfd\x0cD?\xd3\x01I\xeb\xed\x0e\xa4\xec\x1fX%pU\xf2\xc7\xd7\xc1,8\x07B\xbd\x83\x9d\xb33\x8f\xedW\x8e\x92Z@\xb8`r\x08\x03G L\xad\xdc\xe6\x89`\xcc*\x0c\x1fka\xf8f\xd8A\xecB\x11\xd1\xed9\x90\x81q\xc5dfn\xaa\xd1\xc4\x83M\xd6x\xebZ\x12\xe0\x10\x98\xa6\x87Pb.\xa6\xb0}\xf1\x0dI\xdc\xb5\xa7Hek\xc4\x03\xb2\x15#{\xe3\xcb\x172\x87\xb1\xc0\xf3n\xb5o\xaa_\x9e\x0f\xd0\xca\x1f< \xb1\xa8OL\xc1\\\xfc\xb0\xecR\x91\xd7!\x81\x90\xfbM\x14E\"\xfb\xe9\xa7\xa0\xe0Q\xe9\x94\x98\x1aC85\x07|;\x95k\xa3\xdc\xaa=j\xaf\xc9n\x06\xf6\x9d\x9c\xb2\xacm\x1b\xb7\xdf\x8d\x17\xdf\xdb`\xa3w\xa3`\xdf\xa6|^\x7f\xca\xddrX\xedI\xd1K_u\x81L\xed\xd8\xc5\xdf0\x10k3\x05\x84U\xd4l\x80\x12\xd8\x15\xe3\x98c'\xb2\xf5\xfc\xbd5\xd7]\xb0\xb6\xac\xc2\xda\xb2~\xac\xed\xdd\x99c\nZz-6|\xd6L\xc5\xd1\xe3\xd5\xe6m\x02\x05\xd0\x8f\xbfU\xb5\xa9\xc1\xc6\xf3\x92\x8d/G\x0b/\x16vq\xffx1\xaf\xf25\x03\xbd[\xbc\x07\xcf+\x9f1\xe0\x11\x1aKg\xa5\x05q\xa4B]e\x06\xff\xabIr\x89\xb8#uF{\xa2\xc8\x16 _\x03\xf8\x8c]gJ\xf8\xe8V,>\x03PF(\xe4\x16\xd6\"d\x9b\x04\x03\xe3\x98\xcc\xc9!\xa1P.\xaf\x95SW\x92\x8e\x14\xf2\x1aE\xc2\x1a`\xd1\x81\x10\x0bg]\xdbL\x8a\xffy\x07\x0e\x85\x8b]\x84\xed\x1d%F\xab\x1b\xd5 u\xe6\x91]\x95\x10\xabyC\x9e\xfd\xff\xe9\xe2\x19\x8f\xd6\xf9\x95c\x87[\x01\xd8\x0f\x07iV\xdezvT<\\\xed<'\x11yA\xb2B\xfa\x15mo\x0fH6\x8b\xce\x95\x0e\x87\xcd\xf2\x9c\xf4a\xe7\xda\xf8\xd9\xde<\xe6\xf58\xcdx|\x96P\xefs\x10-\xbaN\xc7\xce6\x81\xc3\x82\xb6&-\x19\xf5\xdboo\xb9\x7f\xd3\xd2\xde\xc4u\x9e6\x1f\xe93\\\xf6\xd9i*C\xea\xa7\x8f&\x8bA6\xe0\x07\xa2\xf4h|\xc7\x03\xf1\xe9\xb3\xba\xcb2\x0e\x86\x87\xa3U:\xea\xf4\xdc]_\xeaj\xeb&n\xe1e\xdd\xe5C\xe2\xac\xd2\x913\xa8\xe3\xda;\xb5\xfb\xe1\xc8\x1d\x0f\x1e.n\xd9\xbe\xb2u\xc9\xb0\x1b\x85kW\xe0\xe3\x8c\x7f\x12\x14$\xe2\x02\xfc\xeb\xbdv\xceF\xa5(\xaa!\x19\x07\xe9\xa7(\xc8B\x96\xa6\xef\xc0\x7f\xd9\xa0k\x1cZ]\x19iQ\x02h@9\x97\x9c\x87\x8cV\\\x17\xcb\x0c\xa5\xc0_z\xe0\xaa\xed\x04\xady\x11\xa4\xef\xe8;7\xab\xa1\x07\xbd2DU \xe80\x9c(s\xc4?\xe5\x83\x07\x84K/\x922\xd2\x05\x99\x82\x08\xbc\x11!\x80HG\xe3`\x96\x99\x04+\xd0\xcf\xca\xc4y\x13_7N\xf7;N\xca\xfe\x0e6)\x0f\xff~\xb7\x8d2\xa8\xec\x94\x11l\x95\xfbl\xf7Cwv4\xfa\xdb\xf9=m\x16g\xf4\xe7\x893\xb08\xc3\xbfCk\xfb\xb5H\xcb\x0b\xfe\xf8\x8a.\xae\xa2 z\xe6\x17\xdb\xb8\xb6\xd8\"y\xf9\x90\xcd\"pq-M\x89\xa5\x14>\x82\xd54\x8b\xec~\x05\xc8m;lpg\x8fw:\xf7\xafej\xbes\xbe#\xdb\xb0\x88\xc8\xb6x\xb9\xe7\x86M\xcc\x86i\x92\xa9\xda\x10q\x08\x87\xecL\xd9\xfcb\xa2l\x8e\xcdE\x97A7\x01?\xa9\xea\xa6\x1b\xdc>\xa4 !(|\xa7B\xda\xff\x07\xf7\xe0[\x13\x84\x9ft\x931\xbb\xce\x12\xeae\xbat\xd9\x1e+s\x8e\xcf\xc2\xbd\x84~\xd9}2\xc0\xec\xe09z\xe8h\x9e\xc1\xb2\xcc\xa3\x19\xabn\xc0s\xcc*=\x9a9?\xb3\xcb\xcfA\x06\xae\xff\x80\x1c\xb9*\xde3\xc8\x7f\xcb\x7f/3W\xf2E\xe6\xac\xd22\xe3\xedi\x99\xfe\xbeL\xe6\x90\xda\xf8jm \x12\xe3`hN3\x8d\x82\x15\xb8\xf8\x02OM\xdcu\x8et\x823$\xe5\xcbI\xe4c|KQ:\xc8\x98\xf4\x14\xd6R\xc7k\x0d\xd3Z\x93\n\xf5g\xad\x05\x9cqa5d\x89\xa0?\xcd\xae\x9c\x15)\xa2\x86\xf2\x0d:S]\x81My\x02\xe6v\xde\\\x0d\xa6k{q\x00\xe6\xfd\x18\xf6\xca\xa0\x8a}\x01Q\x1b\xae\x82\xc8\xe7W\x80\x04\xa5\xa8\x8d\x04csf\xca\x97!i\x02\x14\x83\xdf\x0e\x06#[\xbe\x0e\xaac\x82\xb4\xa5\xa8\xa22\xb4\xc6[o\x9f\xd9\x82\xc6\xa13v^P.\xe2\xe5y\x03d+0a\x90h(\xe2\xe4 \x1aE\x0d\x113\xce)\xa2\\b$5\\D\x91\xbc\xd2.P`\x88\xce\xd1\x8d_qIJ\xee\x8e\x946s\xfc\xdct\xc1,%_\xbb\x93\xba\x0f\xe3\x1c\x97:J\xc7\xcf\x8f\xf6\x8cCE\xbb#~\x86b\xc7\xb0\xdb\xbd\x19h\x13 zY\xc6@5\xeb\xf5\xac\x07\xaa\xe3-\x99\xf7\xf9\x92_\xebHU:,\x1c\xb8\x84\xe7\x95\xd4\xc3R;d\x0c\xc5\x98oj\x8c\x8c!R\x9b\x05\x1d6\xa3)\x98\xaa|\x1b\x88\x95\xe8x\xa1$ nf\x11\xed$\x1a\xecX6\xb2A\x9a\x93\xb2\xff\x98\xcf\x1a\xf1\xc8\xb0\x9aR\xe8f\xb9f\x850\xa8m\x10\x10(\xba\x15\x80^k\x80F\xfeWX\xddx\xe3Tx\x7f\xd5\xbd\xf6o(\xd8\x9fd\xd8\xc16H\x15\x99P\xcfg\xa4\xccFX\xed\x9e*\x90*\xf4P!^\x91\xa7\xdb\xa5\xabJ\xc8!h\xe8[\xaaR\xfd\xc0++\xddc\xd6K\xeb\x9c\xe6\xd0\xb5\x9e6\xa6\xd9\xff\x06\xeb.\x1b\x9b#\xd9\\O\xac\xa7\x8b\x8dj\x9f\xcb1\xca\x8a-uh\xfc\x9e\x96\xdfm\x1d%sR\xcc:aN\xa1F\xf9kJl\xb7\xffU\x8f\x1f]s\xd1M\xcc\x92\xc6m'\xa6\x11\xde.\x9b\x95\xfb\x9d]3/\xcf\xd8{\xf5q7k\xb7mK\xc74\xa5\xb1\x1bv\x1aI\xae\x0b\x85\xf6\x88\xaeZ,\xe4Azh`Ce\xfbk\xe8k\xa2\x14\xbf\xf9\x14G\xa68Xr\xfb=\xd1\x10\xee0\x82\xe7\xc43\xc2\xf7=\x1f@j%\xa9\xdf\xd7\xe6P\xec\x1f9KnNA\xf7\x96'Ga\xe8\xca\x9b\xdb\x99\xe8\xf5\x81\xa0i\xff\xcf\xe9\xfbwc)i\x08\xe67Re\x01D\xd8\xdf\x9d\x83\xda\xcc\x81\xea\xfd\xf9w\x03\xe9\x02`\xe79\x89\xc9\x8b\"\xf4\xd9s\x12oow\x0d\x01Q#\xee\x83\xd6Y\xdc!\xb3$j\xdc\xfdR'\xc3\x1f\xcfy\xb2\x82\x19\x08\xe0g\x9f/\x12\xf5\xd5\xa5\x1ew=\xdeb\xec\xe1\xd2\xb5\x1e;\xcd\xf6,\x95c\xadg\xe0\xe4\xbb\\d\xcbn\xc9*.\xfa\xec\xce\xb5\xe7\xa0\x01\xa8\xf4\xf3u|\x19D>\x1a\x9eO<\x1e\x8f\xb2\x84Ko\xb2\x1e\xa6N\xd0\xaaM]\xa1<\xba\xf0\xc0\xda\xea@\xbfe\xf3Kd\xab\x10`sn\xca\xe3\xe9\xc1\x03\x12\xa0\xdaq\xf8\x06\x13\xdc\xb4\xa3\xaa\x85;\x1b\x88}\x8b\xcc\xbe&\x17\xad\xd5\xe0\xb8\xb1N\x9b4+\xaeZ\x84\xe1x|N\\)'\xe4pG\xa1M\xde\x00{\x0f\xf4\x0f\xc1\x8d\xeeX\xc4\xf2\xc5MD\x11\xd2\xad\xc4Y]\xb8\x1aD\xec4I\xe5]\xa1\xab\xbe6$\x93\x1d\x90\x18\xb5\xdc\xc9\xb8\\\xeai)\x8f1RcK\xb7VbH0\xa9,\xdb/\x91\x0c\xbe\x80e'\xca\xe2\x1a\x1c\xaf\x039\x8b!\xd6\xa3\x16\xf2*x\x03_W\xcfr\xd9\xd4JJ\xf1\xc9&\xa4[\x03E\x01\xb5f\xd9\x81y\xaec\x0d8.\xf3\xca\x8au\xe2\x01\xd9\xda\xaaC\xb6\x926u/\xe8\xdfl\x7f\xda\xb6Fs*\ne\xb1\xd6\x05\xa8\xf4\xab\xa4\xd7\xd66\xed\x1c\xe9\x05\xb6\xc5d\xa5KA\x08\x02\xbd\xb7~\x02\x9a\x06\x1a\x85\xdc\xa3\xed*I+\x1ee\xcbv=\xaa\xae\xaf]1f\xd3n#\x10a\xb5\xdc2C\xe3-\xea\xa0i\xf5\xd32\xaa\xaa\x82>\xdf\x8ej\x0c\xa2~\x9a\xc7\\\xc1\xb0[(3eb*\xdd\x11H \xa99?,\xbbdl\xa2zZ_(\xfc3u\x05\xcd\xe2\xcd\"M\x9dC\xea\xad\x04\x17f5\xce\xe9\xc9\xf1\xc7\x93\xb3\x8b\x97\xef/\xde\xbd?\xbb\xf8ptzzq\xf6\xe3\xeb\xd3\x8b\xf7\x1f/~}\xff\xe9\xe2\xe7\xd7o\xde\\\xfcpr\xf1\xea\xf5\xc7\x93\x97\xce\xed\xbfi\x08K\xeaR\x11\x15o\xb9\x1e\x0d+\xc0\x85\x1f\x94\xe0q\xa0\xf2\xf2^\x0f\x8e\xdf\"\xb3\x90V\xa4\xf6{\x90\xfa\x15\x9c\xe6\xe2\xc7Z\xad\xae\x88K\xc7\x86\x1d\xc8\xaf\x90[\x10\xe9\x9f\xacq\xd3&\xc5 \xe5)Z\xa6\x1f\x92\x8cl\x8b\x92SiN\x01\xd2\xc8\xad\x9d\xba\x9c}0$Y\xb9:*#\x1c\xe2\xee\xd9\xb8\xe9K\xc2\xd0\xa5\x96\x94\x8b2\xf6\xab\x17,d3\x92!\x01\xc4\x03\xea\xd5\xd7\x99[\xbf\xa8 V\xe4\x10\x0c[\xbc\x80\x98=\xb7X@\x08\x90\xc0PDo2\xca\xdbb\xf7OI\xea\x96\xfa\xef\x03\xf9\xd1\xad\xc9\xb0\x16\xe0\xb7]7\xa9\xe0\xc6\x0c{\xf4\xa4b\x8fn-J4\xf7 .\x0ef\xe1\xb9\xe4~\xfa0>rEv\xb36\x80\xda[\xa1,\x8a\x1b\xa5Y\x90l\x9dl\xda\xed\xe5\"r\xbd\x08\xa6$\xefX\x04\xdf\x96\xe8\xb1s\x1c\x06!\x19X\xe8\x9f\x8a\x037\xd7\x01xg\xa8K\xb6\xd2n\xb7\x14\x87&\x16\xf9e9\x9cm\"\xbf2l[\x8b\x14\x12\xa1\xeaJ\x99oU$\xa7\xbf\xaaN\xcc\xe2\xd5\x0ei\xe1\xbf\xc0\xe7\xa3\xb9\xf7\xec\x02\\\xf5-\xaft5\xcd+\xd7r\xa4\xcf!-U\xee\xeez`nt\xbb\xd0\xbcE\xa0\xf8A\x9aoz\x8b\x90\xf6\xbaE\x08;n\x11\xf4/\xfc\xb8\xdap\xb9j\x81E\xc9\xff\xd8\xad\x9e\x12\xd7y6q \x82\xfe\x1fmRp%\xaf\xbe\x1f\xe1w\xb9\x13\x1c\x159nC\xa1\xf7\xbf\x8b\x9c:\xe8\xbe\x1f\xb1\x9c\xf8\xa6fT+\xc5@\x1b\xe2p\xbb\x187$\x07\x9d\x0ed*\x96QnE\xd7V\xac\x85]\xb1\x16\xaa'n(\xc5 \xa1:F\xc9\x8b\x032\xd1\xf2\xb9=G\xf9~ g;\xe7\x03\xe9\xdc\x16\xe644\xb8r\xa9\xc8K5\xd7\x00\xc2\x9b\xe6\xfc4R\xfa\x1efUq\xbc\x94S\xfc_&w\x0f6\x95\xbb\xab-\x9eK\xc9hZ8m\xec\x10Rv\x8c\xfa\xbfD\xfcH7\x92\xfc%\xf5]\xd7E\x92v\x10\xe3\x92\x9e\xc2\x07Z\xda(F%%\xe2\x96\xfc5\xafH\x9d\x1ar\xab\xa8.\xb7B\xa4o\xcd\x15o\x17\x995+\xac\xc9\xc0\xda\xe6\xf1\xb6D\xdbf3#E\xc9Yi\xc1\x89P2\xea\x82\xdb\x8e\xee\xa1\xafY)\xc5\xd8\x90\xfd\xff\x96\x94\xc5\xee.f\xcf\xe4\n\xf8]\x19\xe4X\xda\xf2l\xaeg\xa3A\x9f*v\xc3\xa85\xfd\x90\xf0\xa1\x9dQ\x04Y\xbfv\x90\xd6\xd6\xec\x14\x1cGgC8;i\xdd`\x99\x0dE-\xc5\xe7\xa4\x06\xa9\xbd\x86\xf28B\x17V\xc7\xaa\xe0bU\xd0\x86\x05q\x04\x12T\xd8\x0fQ}M\xf0\"\x9a\xf6d\xdffg\xa5\x95\xbeg\xaer+h_DR\x1d\xca9;\xf9\xe5\xec\xe2\xf8\xfd\xbb\xb3\x93wg\x16G\xacD]1\xc3\xd0X\xa2 \x8bg\x0e\x07\xb8\xcf\xae\xbb\xbcR\xce\xd5M}\x17\\\xc6{UG\xe7\x19K\xca\xfaP\xb8\xaf\x03\xcc\x1d\xa4m14\xdd\xd8\xfe\x8f_\x07\xa7'g\x17o\x8f>\xfe\xf5\xd3\x87\xff\xb7\nH\xdeq\x1c\xdbVCf\xf8\x16\xbc\x1dIp\xdb/\xd7\xcf\xc9\xea\"\xb4\x8f\x1aG\x14\xb5\xcd\x87v\x9c\x809r6W\x89\x19Wz0\xa5\x92\xa0\xb0\x9f\xcf\xe2\x1c\x84\xab\x97V\xe7wp\x0c\x0d\x0b\x973\xed'\x1f(6\xb5\x83\xf8\xdd \xcbn\x90\xb5\xf5\xe6B?\xb0\xe1=\xa9*\xddZ\x15\x0cC}\xcb{\x9d\xe4\x00Qc\xb3\"\xeav3\x99y=\xe8\x02\xf1,\x04E8\xf3z\xa8oIU\xad\x059$\xee\x1c\xa4\xb9su\xe4\x97\xc1cVC\xb2\x1eB$\x9e\xc1@\x86\xe3yK\xb3\xe5xE\xaf\xdd\x95y\xc0\x0b\x80!Y\xd5\xce\xfc\x18|\xf1\xad\x80\xb1h/\xabB:\x95M\xb8(\x11\xe8\x91\x04s\x17CBg\xcbs\xdd\xa2L\xd9B-\xb7\xb7\x07C\x12\x0b\xf2b\xad\xf9|\xed\x81\xc7E\x9c\x7f\x98\x8f]\x7f\xab\x9c`>h\x1a\x03zR\xbaUk\xb2\x89\xf5]\x980\xc2g\xde\xf9\xa0\xcdm>\xf8?\xd2\xe8}^\xfa\x0fi\xd2\xb5\xcdK\x17\x82\xf6\x00\xc3\x7f\x91\x95\\o=\x087<\x05\x9b\xe7^f\xfah\xb5\x84\x9c\xec\xd3\x81bA\xf6vLF\n7\x05\xe6\x92|!\x80\xeb\x96y\x1d\xa8\x98\x94\xf4g\xfb\x9eU'\xef\xdb\xf7?\x9d\\\x9c\xfc\xf2\xfa\xf4\xec\xf5\xbb\xffl9|\x89y\x00w#?\xe3\x1c\xae\xf4\xa9\xbb\x94{\xcd\xae\x11\xaf\xac\xc7E\n\xb1L\xed}\xcd\xeb\xc7\x13\xd8\xc3\xef\xde\xbf<\xe9;\xab\xdd\xe3\x7f\xd7\xfd\xdbB\xa2\x93\xfeT5\xe9IY\x93\x8em\xdbkV\x9bg\xf8-$a\x85\xc5w\x95\xb4H\xd4\xa9b\xe0\x05Qe\xd4\xbbm\xe6Q\xd5s\xcd\xe9\x0b<\xf8\xb0\x19b\x8f\xe1w\xf0\xc4\xde\xfcH\xbaBl\xb6\xf4O\xf8\x9bEt\xedA\xea\xadD\xd7\xa5\x9b'\xd4\xd6W\xb9\x17\xa8\xfb\xe1 \x86\xa7\xae\xfa-8)\xa5\xdb\xbb\xbb{ \x97\xde\xdd\xdd\xad\x0b\xb4\x89\xa1x\xb6_\x1b\xb4\xdau91\x85\xccy\xc7\x81\xbfV\xb6\x1b\x86\x17&\xd60Z$\xe6} \xa8\x89H\xa1\xb7\xb4\xb3\xe7\x82^i*\x89U\xc7FV\xbfu\xa0*x\x0fN \x11\x15\x0f\x81=.N\xde\xfd4%N\x9cp?\x87^ \xe8\xe4\xe7\x93\x1f>\x1c\x1d\xff\xf5\xe2\xf5\xbb7\xaf\xdf\x9d\\\x9c\x9e\xfd\xfa\xe6\xe4tJ\xb6&\xd5F\xd4FJ\x8b\x0b\x9b\xdfE\xa4\xd8\x1b\x13M\xfa\x8e\x8a\x0dL\xb5\x80v\xb9j\xdd0\\?Z\xbc.>\x9d\xcb@\x01\x1b\x88\xf1\xda\xba@\xa1\xc2\x14\xa2U{\xe0k\xd7\xde#\xf0\xe9\xd1y#+\xf8\x9c\x0e\x9e/n\xf1\xbd\xa4\x1f\xd4\xba6\xee\xcd\xf3 \x06\x15\xd8%\xb8\xd8b\xb3\xf8\x1c\xb8\x0d\xbf~G\xda\x8f\x1d\\\x83\xf5n_k\x1e\xbd9@?(p\x97C\xb2\x1e\x0cH2\xae\x07Sq}`\xc3\xf2!\xf8b\xca\xa4\x1f\xa2\x96\xb1\xd3O\x0f\xbfJ\xfa\x91*JTV\x9dT\xa8W\x1f\xdc.\xd4\xbd\xa2\x8a6mM\xfa\xc4(#\x06w\xcd\xdd5l\xfa~\xa5TOW\xfd\xa0\xc57\x16\xd0\xfaZKW\xf5\xa5\xdb\xaf\xbeH\x8a\xcf;\x98Z\xd2\xca\xd8\xb6\xe7\x96k\x9c\x0d\xc8V\xc3\xc7[\x0cV&\x80\xf8\x90\x05.\xcd\xf5\xc1[[|.\x98\xf5\x8d\xa7\x0em\xd7]Y\xdc\x96\x13\xbdj(o\xf1vG\x88\xc5\xe3]\xd4\xb9\xa55r\xc4O\"\xf3A\xc6\x84\xa3\xb4\x8c~\x90Q\xa9\xa4\xd4\xd0\xb1I5\x94\x17|_\x07\xca\xb5\x8c8\xac\x1f?V\x13p+z\xa2\xf3*\xdc\xa2d\xd7PV\xa7\x96\x8bs\xa5dW\xf7\x89\x99*U\xbd\xba#\x80P\xb5\xa5\x9e\xeeU|h\xee=y\\'P\xe68\xe5\x13\xcb\xfa\x1a>9}Y\xdf\xbe\xa2w&\xf5\xea\x96\xaa;\xf5v\xacK%\xfbzO\x05Z\xaa9\xce\x14Xd\x17\xbb\xd2\x07\xc7T\x7f`\xb7\xf2\x97\xe8\xca/\x15H\xcb\xe5rS:\x7fU\xd1 M\xdf\x15\x18u\xc8\xc8\x01 \xc5\xbe\x96:\x89xX\xe8\xc6\x02\x85\xbb\x0b\xe9\x94Z\xaa\xf7(\x12^*\x97Wbf\xd5c\x0d(*B\xf5\xa9\xa2\xb5_]\x82\x17\xcd\xb1\xbbB\xe9$\x8fGi\x96\xe4^\xaf\xebALM\xcb\x88\xf3eq\xf7\xeb\x89\xad\x9c\x06\x19;\xbb\x89YA\xf4\xcb\xbc@i\xc6\xd4\x92\x8d\xd0\x8f\xcd\x8c\xca%l-_\x0e\xdb\x0f4\xf3\x96\xd2\xffZ-?f\x91\x1fD\x8b\xb2\xedH&h\xd6\x80\x03#<\xff\xa3\xf4\xb9\xa5\x15\xeb\xb6&\xb5\xfcW<\xf1\x98\xbc-\xa8dk\xc1\x9f\x18!d(\n\xb9\xa0\xc6|\xb5|\xb5>j\xa9\x80,\xdf'r\xb1\x16C\x9e)\xafOJi \xef\xc71\x0d\xc3K\xea}N\xeb\x1f\xa2ah4\xe3\xe7 \x0c?I\xa4\x0c\xddi\xac\x0c\xabZD[\xe46\xab%z\xbd\xb3\x1c\xed\xe9\xc5\xf66\xbaV\xb2\xd6\x85b'\xdd\xe9\xd0\xb8\xf3\xe9\xaf\x83G\x14\xe6U\xe3\xaa\x14}\n+\x11{!\xcf\xf61\x1ce\xe8g\x0eJ\x82\x0b\x96\xc9\xe5%\xbdl\xb5|\xc6o\xf5\xbeS\x7f\x14v\xd9r\xb7X\x89\n\xc1\xfa\xd8x\x1f\x07)\x04\xbe*f\xb7\xe5lv\xbd\x96\xb6-\xcb!\xd08\xa8B\x08I\xca\xd0F\x13\xfafD\x86%1LV\x97\x1ay\x1f\xf6\xf2eF6\xe8\xf8\x87\x9d\xe9\xb3tl\xb2\xeb\xb6N\x05\xd2\xb8!\x91\x1e\x06b\x1eD\x99-\xa0\x07\xee\xaa^?E\xd4Vl\xa5V\x9b\x83#f\xed\xda>o=\x0e\xc6 \x97\xa4\x91K\x07u\x1c\x86\xee=7o\xd9\xf9\xa0\x96]\xadC#\xa7\n\xdd\xf0\xc1(Y/`2\ne\xaa\xc2\xc2\x83\x016\xbeV\xba\xb2\xc9bo\xed\x808\xa2\xd2\xeb;\x0fu\xdbZ\x0dn\xb9\x1ao\xb5\xf8\x8aq\xd6\xe3f\xa7IZ4_\x83\x12\x83 \x8a\xb8@|.\x96\xe1v,\x87\xa0\xc7\n\x08\xf4\xa4\x07\xe5<\x0f\x86\x15\xc1~\xa1\xaan\xce4\x90\x0543&\xdc\xb5 \x03\xd7\xca\xe5\xbd'\x90\xb78\xecQ\xcf\x18\xa4\xa1flp0H0,b\x08\xe6\xcd\x81\x07a|\x95|\x02i8\xdc\"x\xe3\x93\xb7\x1f\xce~m\xbf>\xb2,hI\x85\xcc\x11\x15\xdeD/\x92*\x81\xbe\x0cB\xdf\xa0\xd2\xb1(\xde\xc8z\xec\x1f\xd2\x8a\x187\xb3\x15\xb1\x9f\xa5\x03\xbd>\xbfi\xf4+\xa2E\xf0\x96ov\\\x02d\x8dmc\x97\xdcII\xbf\x87q\x8c\x0f\x1e\x90\xad\xac\x8d\xa7\xecs\x87\xd0\xc1\x92\xee\x0c\xdb\xef4\xf4S\xb9\xb8, \xbam\xe2\xa0mw\x07\x1d\x01\x05\x08\xe8w\x07\xd1\x9a\x7ff\xff\x99\xd3\xc4g\xbe\xe6\xa9A\x05\x00\xadU\x9a\x93e-!E )\xac\xd6\xf1*\xda\x82a\xd9\xb6\x08\xe8i51\xbf\x05\x1c\xd3W\xba\xa5\xd8\xa2&\xe1\xf9\xf6\x14r%\xdb&\xe3h\x95\x03\xe1\x92\x16\\\xb8e\x93\xb4\x84:p\x99\x8dE\xec\xb3\xe5/V4\xfd\xac\x10U\x9f\xed\xben3\xa7\x04\x1eVuM\xcc\xa3%\xec\x07\xf8\xdb-C \xc4v\xfc\x8e\xf9\xc1\xd6O5~N6 \xd1,9o\x0d`c\xf5\x14\x87\x8dKU\xd2\xb2\xf9\xd0\x18\xe3j=\xf2\xf4\x99\xb3Q\x83\x8c\x93\xa5w\xabL=\xfb\x8d\xa4AM\xca\xc6>\xa5\x81t3[6\x8f\xe8\xe8\x0c\x8d\x1c\x19\xa8\xa1\x0d\xa1VC\xf0 \\\xb5\xf2rpl\xac\xb6\x82\xa5~\xba9K=\x90\x1f\xc2j\xd5B\x8f\xfd\xcdj\x15g\xbe\x1d\x89\x96.w\xbf\x02\xdf\xdb{\x0f\x13\x83\x1d\xeb\xb5n\x80`7;\xd4_\xab\x0f\xf3\x81\xd1H\xaa_X\xf7\xaf~]Q\xbd\xef{\xe5\xceM\xa1\x9e\xe8T\x1b9\xd9\x86\x84\x95\xdeCyP\x011\xc7@I\xaa\x9f\xaa\xa4b\x1f\xe4\xd9\xf0z\xfe\x8e\x89\x0dJ\x93\x9b>\xfb\xb2P\x8e\xc1\xdayH\xe6ME\x80\xcc\xb0\x14\xab\xc2\x0f\xcb\xfb\x11M\xc7\x97\xce\xa8\x0f\xac\xa7\xe1\x97/\xf6\x83\xee\x10\x1f\xa3\xf2;\xd5\xd9jO\xad\\;\x99M\x94 \xb6\x1b\x95>SPk z\x0f\xd0a\xfdI{\xe2\xb8\xc8\xf4\x97 0\xc2\xde\xa6\xa2\xbb\x16\x16i\x08\xbc\xcc\xd6\xa4m1\x17D\xc3\x81\x0c\xd2\x9b\x83\x11\xb8N\x9dJ\xd7[jF\xab\xf7\x04\xc1@\xd5o\xd3\xbeX+\xc7&\x9dW\x11\x10\xe2\xd8\xe6\x1d\x88\xc0\xd5#X\xe5\x03\xeeW\x9f\x1cJ\x17\x98\xb4Ji~\x94\xeb\x1b\xbc\xa6td\xbb\x9e=\xa6\xd9Z\x07\xfe7\xfb]\xe1r\xa1\xb0\xbdGq\x8bw(\xeb\xf6\x80\xf8h\xe3t\xc9\xf3\xb0$K\x8b\xad\x13\xc3\xc4\xa0\xb9\xa25\xf3\xa1\x8c\x82\xacg\xb5\"\n?8 \xd2\x8c\x03\xda\xe5\xbb\xe1\x90x\xb0\xac\xb6|\xf1E\xd1\xa3!\x99\x03\x9f\xde\xbe{\x86$&\x87\x9a7\xeb$e\x01\x91\xd5\xdb\x1aI\x9d\x19\xb8(ab\x17\x81\x95 \xb6\xd5\xc57\x9b\xb4m0$\xb4\x10\xea{\xe2E\xcb$\xe6Cc\xe5\x1e`\xa6=-$\x909\xbb=\xd5O*|Y\x0f)My,5\xd0f\x1fb \xe1,\xect\x93\xb5\x08\xc6m \xcc\xccVii\x11\xb5]dHGo\x0f\x1e\x90\x89r\xa4+\x1d\xc6\x14\x85\x93\xd9\x8e\x85p6\x88\xb1\x03E\xb2\x08\xfc#\n\x88sF~T\xb9\x84\x13\x19\x132%;\xcfI^\xf1\xee\x96\xb7\xfb\xc5^\x1bf\xd9v\xb2\x89\xbbtH\x1c=\xe5\xa6'\xc2\x94\x1c\x92T\xea\xd8H\x8dE\xb9\x1c\xa6$\xbd\x05e\x85\xf8\xbf\xc1\x96#\xbakn\xa1y\xad\xaf\x87\x87\xda\x13A\xdfe*\xb0\xf1\x0f2d\x9b\x1bV\xee?d[,8\xd3#\xda\xe3O\xa8%\x809\xbc(\xf4\x02\xbe:\n\x91\xe0\x90\x845\x19\x81D \xe07\x0b\xc9(\xee\x03p\xaa\xc0\xd4\xe6\xa8\xa0\x8a\xb0@\x15\xd9P\xb7E\xe2\x95\xd0@\x15I\x15\xef}\xac\xcb\x06\\\x18\xe8\xa1\xec#o\xbf2\xc2\x86L\nO\xc2B\xe9Ut\xbf\x1fv\xb24\xe8V\x18\xaa).iEU\xd1m\xc8g\xbb,\xb7\x1d\xc5\xd9\xa4\xd7s\xe2.]\x10\x95\x0f0\xf2URb\xacMP\x9a\xd9\xa4\xc8\x1d\xca\xac\x1a5U%\xa16{Y\xf1 r\xaah\x88\xbb@\xd7OS\x92\x8d\xb9\xdb\xd6Ou\x1a\xbb\xa5\xd9d\x03\x896\xef'\xd1&-\xb2\xba\xd6\x90\xac\x9a\x18\xc4\xc4\xdd\xc5\xfc\x95:1fJ\xcd{E\xdbT\x8bm\xda\xddp8\x0d\xc5\xf0\xfd\x1cdK\xe9]@\x1c\x01!\xca\xa2\x91\xdeR/\xb4\xe2\xfe\x9c+\x1d\xe3-c\x1b\xd8\xd9Y\xf7\x9fy\xb9\xfb>i\x8az\xda0\x08\xeb\xc9\xcb\x14\xc62\xb2\x11\xee\xddZ\xdc\xb7q]4P\x95\x14\x16+|\xd1F2\xe4c\x85\xf4T\xa7[VS\xeb\x95\xafx\xba\xaf\xb8\xd0iA\x06N?_\xc9<\x88h\x18v}\xd9\xec\x05\xca\xf5\xea\xa7\xd5\xf9\xec\xad\xdb\xdf.*\xd5\xdaA\xcc\xd0\x0eb\xa8v\x10+\xb5\x83\x9em\xc8\x16\x0f\xfbI\xb2h\x96Qo\xf9\x91\xcdos\xa2.X\xf6!\xbf\x0c\x03\xafp\x94f\xe9\xb9\xe6\xf2#\xcd\xe5Ov\xda\x18w\x194\xa7w\xedn\xa4\x14\x99\x0e\x0e\x80=\xd3\xaf\xe4\x8f\xaf@I\x8b\xb7\x81\x0c\x04\xd7\xcbv\xc7g\xc8\x98\xd8\x06D\x05\xd5\xb3\x8d\x07||\xc6\xce\xfb|W\xcdl\xdf\x8d\x7f;\xe1s\xf3~\x10\xcc!*)\xe3B9\x86[\xdcQ\x15\xa8\xae\xa6\xae\xa6l+j\xa9\xacPbS\xf9\xfa\xb5\xaf@\xaa1\xb0\x1b\x8fQ/\xcc\x8d!L\xedc\x02\x96\xf0\xb4\xdf\xa6\xb2\x93\x19\x88\xcd\xaa\xc56R*X\xdd\xc9\x96a\x82\xd7l\x1d9\xcd\xb2no\x17\xc9_\xef\xde\n\x94\xb1<\xbdY]rp\xc7*\x7f\x8d\x057\\ys\x9dD\x8c\xdc\x98\xc9U\xed\x00\xba{\xb23\xd9\xd9\xc3{\x95\xfc\xb3Z*\xa3s\xf2\xa4:\xed\xe0W\xf3\x7f\xffo\x9dy\xeb8\xcc*\x04\x0c\xa8\xe6\xcd\x92s\xd8=3~^\xc3|\xe0\xb3\x1dkmy\x01X\x0f\x0cp\xab\x91i\xb1\xb2\x95V\xb2\xcf\x1b\x9d\x90F4\x9b\x19\xc7\xf2\x0e%;0_\x12CR\\Y\x19\xc1\x12\xda\xf6?\x18/\xb53^\x86^\x0e\xb7\x9a9\xed\x0c\xa5\xa9md\x1a\xdf\xba\\\xda\xddvG\xb8\xaa\x0e\xd2\xbf\xca\x04\xd7\x16\xdc\xd5r\xda\xe3\x96\xb4\x08\x02m\xbbS\xd6(\xc5\xd57@-\x8e\xd3\xbf\x891\x17\x1eb\xe4I\xdd3\xba\x0e1\xf2\x14\xb1\xe6*\xcd\xad\xf6'\x0d\x07\xa79x\xa4\xaa~\xbai\xd9\xacd#\xd5S\xabb\x1e_\xfc.6E\xd8D\x12p>%L9\x8f\x0d~g\x10\xef\x97\xaa\x1a\x87:_\x90\xaag\xfc4\xa3Y\xe0I\x1e\xca\x10\x0f\xe5);6\xa3\x19\x9b\xf2\xd0\xbc\xb4NP\xea\xe5\xb4\xd5k{\xd3\xdd\xa9\xe0\xe2\xcb6)\xe5\x8a\xb4\xe3\xb4V\x8b\xa4\xea!\xa8v\xac6EN\xfd*M;*5\x0c2\xfaUX\x1f\xa8\xb6\xfa}\xa6\xa9\xa8\xda\xccW\xc1J\xed\xcfV0\xad\xe6\xd9\xb2\x8a\nP7,\x0d \xc03\xaa7\x18\x12>\xa6\xbe\xff\x81\xf30\x88\x16g\xdc\x0dk\x18\xe1^\x1c \xef\xee>2\x10\xbfD\xfa&\x14o#@\x8a\xb5\xcf\x9a\xe7\x0d\xa9\xc5\xb8o\xe1Q@\x15\xc6eD\xd3|p.\x0eH\xb6L\xf8\x15\xacjA\xd8I\xfd_\xe7\x98F\x11\xcf\x88\xc0<\x84\x12/\xa4iJhJh\xf1%\x07\xc1\xee\xea\xd6\xb8\xd0\xb5\xca\xca%/\xce\x83\xea\x92\xa8\xce\xa1\xa6\x9bM\xf3\x14X\xd3\xac\xdb\xe6G\x9b\xbb\xd4\x10\xfb\xb0R\x9dB5Z\x81\xaa\x8e\xe9-\xf2\x97z7\xc6A\xfa:\xaa`\x17\xe0\xdc\xea\xb5\xe3\xb2\x19\xbcE\xd5k\xb2\xf6\x9en\xd8\x1c\xa3\xea\xba\xc3w\xbc-\xb5\x0b\xa1\xceU\xb5a{\xcc\xea\xdd\xa6\x1e\n\xde\xa6S\x96}\xab\xf6\xe8\xaa-m)1\x88\xc9a\x9b\xa8\x81\xdf\x07j\xb0\x9c\xc5\xfb\xb6\xb3\x189\x8a{\xac\x1a\xe4\x0e\xb5f\x87\xfa\x8e\xfbu\xa5\xc5[\xdb\xad\xfa|%\xf5\n\xab\x83jbbjb\xe2j\xa3\xbb\xcd-\xad\xbeb\xa8\xbc\xa0\x08\xfcc@\x1e\xc9\xf6v\x93\xf8\xaa6\x91\xa2\x9d\xdd\xd4\xf0R\x0b\xec\x1d\x02\xec\xd9\x88\xad\xe2\xecfJ B\xa5\xf1\xb9m\xe2\x10D\x0bW\xfa!\xa8\x93 m\x14|*\xfb\xc9\xaf\"\x96\xbc\xe4^\x0e\x12\x0e\xe55\x89\xaf@HfSb\xd06\x0b\xe38a\x1e\xf5\x96\xacP\xe5\x967P\xdcEn1\x9b\xf2\xc0\x9aT\xb7FX\x1d\xca0^\xceo\xd7{\xde\xd6h$\xc6!\x17\xbd\x1f\x8d~\xbb\xdecNm\xaf\xd5\xce\x02\xab\x8eW\xf3\xf0\xef\xaf\xc4^t\xdb\x1a\x04\xba\xadQ-\xda\xea(\x930\xce\xa3\xea\xd8\xd6j/qK\x8d\xda\xa0\xf7\x82R&\x15b\x03\x0f\x1b\xc0Q4\xea\x14\xb8\xc0\x01\xe7\x19J\xd0\xba\x07\xd1]j\x99\x99\x91Y]k\x86\x07\x0eP.\x06\x86\xf39\xe1\xcfI3\x80\x1d\x89\xea\x9b\xb4\x12\xb5{G\x1a\x03e\xcf }\x0e\xbfh\xb5t\x80\x96~N\"2\"\x01\xf9\x9e\xec<\x1f\x80\xbc\x8bU\xaf\x91\xa2\xd1\x08-\x16\x90\x11\x89T1@\x04\xd5b\x01ZL\xef\xfe\xe89\xc9G\xa3\xe7v^\x1dB\x02\xb71\x8dHK\x1b\xad\xb0\xac$R\x15\xa5\xff\xa9 a\xae\xb3j\x0b\x83\xf4(\xf2XZ\xa5\xc8m\xa7\xacm\x89$\xc9lr\xbe\x89\x96W\xdb\xdc\xf5gIk\xea\n\x06\xea\xb5\x88\x08\xda8\x07i\xe8\x88\xec\x0e\xbcS\x05\xd1\x01*\xf1v\xa6x\x1c\xb1\xeb\xec4\xb8\x0c\x83h\xf1\xdcJ\xa7\x93\xda\xc5X\xa6\x14Z\x9e\x14\xd6q\x12\xe9\x0e\x86d_2A\xe3H\xab)>x@j\xf8\xcc\x80\x90\x11\x0d[\xbeJ\xcaE\\\xc7 \x16c-\xfd\xb4G\xe0\xb6;\xd3\x94\x04\x981,=\x17\x8d\x9e:A\xe1U\x0fx\x1c\xab\x9d[\xcedVWa\xba\x9b\xa8\xe2vD\x81\xc0\xd0\xb7\x15q\xdc\xcb\x85\x8aEj\xfa\x08'\x07\xf1\x1bL\x19h\xb1:x\x16\xef\xcb\xfafqJh\xf3\xb0\x15\x83\xd7\xb5\xd7 (\x02\x07)\xd8\xce\x04\xd1B\x85M\xb4\xb8\xa0k\x9b_Qfv\xdb6\xf2\xf1<\xcc\xd3%\xb4\x82)-\xf4T\xaa\xa1\xf3\x86\x04Gv%+\xbb!e0\xc9`\x08\x85A\x17m\xee\xd6<\x91}%W\xcb d\xc4\xadKT\x8cX\x82 \x97\xe1\xe4E\xa5n-b\xe1 \xa1\x81\xc5Qd\xce\xf8\xf9\x90,\xc7\xcaC\xd7\x99\x9a\x03\x97U\xa6C:\xb53\x87j\xd8\x18;\x1c\x17\xc7v.\xde\xa6\xa9\xd1\x18&lu\x18$Du\x81\x18\x19\xf5\x01h\xde\x19\x96M\x06n\xb1\xa2\xaa!\xf8\xc5qv\xc5\x8f\x92\x05\xf0\xb5\"\xa7\xe2dx\xad\x1c\xefW\x1b|\xc1\"z\x192\x7f*0d5\xa7:\xc4X\xdc\x95\x9f_\xbf{\xf9\xfe\xe7\x8b\x1f\x8f\xde\xbd|s2%\xc1\xd8\xa3\xd1\xa7\x94\xbd|\xff\x96\x1c\x92\xab \xf2\xf9\x15\xc1\xca\xa5,\xfb\xb1Vy\xbb\xe4\xa81\xe1bQT\xc7\xa6\xf1\x85\x13\xdd\xb1\xce\xaa\xd5\x10\x88Sb\xab\xb5\xd6 mV\xdar\xfc\x96U\xb7U\x9a%4\xfeAJ\x1faQ\xf4\x13V\xeb\xdb\x0drH\xf8X\x06\xf0W\xb1\x89\x96\xa0Z-\x0e@\xa8N\x124r\x99\xb1\x81\x16\xd7v5\xe8X\x892o\xdb\"%\n\xbd\xaf&\xadx\x14d<9\xf5\x12\x1e\xca\x88\xe8]\xd3\xaaQf;\x94x\x98\xeb\xb9r\xad\"\x8e\x9b\xbeV\xdb\xda$<\x8a\xc1\x97U\x0c\x89\x93B#\x1dD\x8d\xa2\x8aN\xcc\x11\xe9)\xd3(\x17T\x1b\xd1$0f\x0c\x86\x06\x02\x05\xb4\xc6\xeei\xb7\xcfI\xc7U\"\xce\xf5\xedr\x81\x1eF7\xf18a!\xa3)so+\\(\xde,$\xd7\x12RoEr\xf5S\xc1.\xc4`?K\xe4\x067\x1d\x86\x0eY\x91q\x88\x8c\x03\xc4\xc5\x8a\xe9\x82\xfd\xf2~>O\x99\x0c\xd82\xf6\xb5\xc6\x82\xfe\xa1m4\xe4:z\xc3\xe6\x88\x00\xf5FW\xf5\xeb\x06U\x9d\xf1\xaaX\xf0+\xc1\x82\xceC+;\xbfm\xa9\xf1O\xd5_\xb7\x9a\x89\x92\xf8\xdd\xaf3\xaa\xea\x9acb!~\x1b\xd7\"\xed\x81\x16\xf6\x9e\xe0\x91\x16&\x8f\xeb\xf5\x84\n\xbe\xde\x1e\x0f\xa7\x97q\xbe\xc9\x10B\xd0q\x10\xfd7\x83qi\x8e\xef\xcb\xf7ou\xfc\x8d)I\xda OVqvcT\x9b\xb7\x02\x0b<\xf3!\xcc\x17A\xf4c~)\xb8\xdf~\xc0\x9f\xb2 L\xc5\xd9\xde\x05~\xb2\n\xb2\x8c%S\xf0\x9bg\x05\xfd\x11t\x88\x8a&\x87m\xb0\x05\xef\xe8\x95P\xd5\xf5\xf6/\xe0\xbc\x1e\xd7\x99\xa6\x00g\xb1\xa8e-\xa9\xb5\xf7\xb4\x9e\x9eV\xd4\xc8'\x8f\x9e\xd6\xd5\xc8\x15\x17\xb6[\xff\xbe\xd7-\x03\x01\x8e\xe0\x94\x85r\x08_G\x82\xd9\xa5\xf8\x98+\xd9H>N\x80\x16eE\xa9\xea\xc0c\xf1\xb9\xcd/v\xca\x7f\xb4\xbc\x97\x8e\x0b\xa2\xaa\xc3&\x92\x8eK\xa2\xce\x85X\xe3\xbd\x0c\xad\xea\x02)+\x1dP\xa9\x1f \x94S\x17D\xddu\x04\x94\xa4\xa8\xa2\xb0.F\x9da\xc6\xad=:\xb6\xd1w\"\x9e\x05\xf3\x9b\xa30\xc4\xbeU\xed(*\xf8B\x98\xfbv\xc9W\xbb\xe5Aa^Pk'\xa8Q\x94\x94Ldx\x99D\x8c\x14\x0c-\xd5\xca\x86\x8e\xef\xd5\x06\xc1\xab\xad\x83z\xc5\xb7\xb2A\xc0:\xdf\xf1\x9d\x8d\xcd\x12Z)l\x9b\x81\xc1&\x0d\xae\xf8\xa8n\xfb\x18b\xa6`W\x18hl\x11\xed\xca\xba\xa1\xc6]y\xed\xcd\xae\xf3\x82,\xc5>7\xb0.\xcc&\xcfR.\xbf\x12\x91%\xee\xdc\x14)\xa4C\x12\x0f\x86$\xa8\xf2\xee\xf3\xba\xe1\x15\x14\xbf\xe3\x01\xd6\x90\x05*]\xea\xddz\xdc\xa7@\x1dl{\xa8\x18\x8f\xb6h)\x94\xd78\xdap[*\xa8%\x96\x8d\x98KO\xe6\x85\x90\xe0\xc1\x03\xe2\xa4\xfa\x80\x01\x85/M\xb9\x8a\xac-\xd71\x8f-\xc8W\x8cZ\xf3\xe8l\xce\xeb\x82e\x928N\xa7$'\x87=N\x00\xcd3\x16tt\xd16u}\xff\x91F\x8b\xd6\xa0,`\xdb1\xce\xd8u\xa6d8vP\xb8\xb3\x1d\xfby\x1c\x06\x1e\xcd\xac\xd7\xb5 \x84\xaa?\xe3\n\xcb\x9dI\xb7\xa6C\x92\xc8\xd3\xca\xff\x00\xbb\xcd9\x89|@\xaaI\xe6\xd8\xb9=-rK\xcc\x16\xb6\x9e\xb9-\xbc\xa1\xf8VC\xed\xcf|X\xe4OA\x03\xa5\xe9\xf7\x95\xe0\xcc\x1e\xe9\xc2\x07\xc4\x98$\xb9\x12*\x84\x8dX4H\xb2mh\xe5-\xb1`\x9dv\xd4-k\"\xe6\x174mz\x86\x05\x95\xf3M#o\xc9!\xdep\xd7tKH\xb9,\xed\xb0\xd2\xb7\xc1\x9c{y\xda^iP\x02v\xd5\x99k\x7f \xb0\x86\x8f2\xd7\xe6\x91\xb0]$\x90\x8fa\xe2\x0b+\x80\xe2\xeazH\xf21\x8b\xfcf\x06>\xf9:XC\x9f\xd8=\xa8\x07\x00\x82.!b\x98\x04P\xb723\xf5\xd1\xaf\x8cpu\x14\x07\xe4\x90\xec\x10A\x04g\xfc\x14\xd40\xdcA\xe7~\x0eA\xf2\xee\x85<\xd2h\x02\x1f\xdfPa\x15\xf1]p\x06\x12e)\xec\xe8P\xedh\xb7>\xc6C=\xea\xaau\xf6\xe5\xe8)\x0d\xa7z\xf9\xd0,/^\xcd\x99R\xef\xd5\xae\x87\x9bt]\xf0\xbb\x1e\xd9&-\xee+c\x13\xadV\x90)\xde\x9bX\x0c\x06\xe03W\xb94\x8b\xf5\xf0p\xbb\x03#\xad\xd2\x14\x8f=\x1e\x864N\x99%`k_\xf4\xe6\x8bs\x83L\x89\xd7\x81\xe6\x04\x9c'\xd0W\xcfu\x8a\x90\xf3\xa9\xf5\xb8\xear\xb52\xd4\n\xcb]\xe7V\xf7icX\xbagbQ\x90CIL\x00\xf2\x801!\xd3\xe2\xd7\xf7\x05\x8c+\x01X\xe4\x0f\x15\xa2\x03\x08\xf0Zi\x94\xd5\x99,\xf2\xc1\xd4\x14?\xd9d\xba\x9c{\xc7[\xd2\x84z\x19K\x1ci\x19\xce[\x8e=^\x14\x16\xcb\xa4R4!\xa3\xa2\xb8\x18\x1a\x8c\xeb!=\x84\xb0D\x1d\x1b\xc8)\xd3\x86\xc8\xf4Q\x81\x1eN\xf6\xa5E\xd4\xb9\xc1f\x81;8\xef\xdc\x86DI\x1d\xde\xd2l9^\x05\x91[\x0e{\xc7G\xf2\xaa\x93\x03=\xad\x94L\xcd\xca\xe4\xf4\xb6\xa9\x95\x89\x035\x1a\xb3\xebL\x94\x7f\xf0\x80P\xf2=i\x0d\xc7C\x0c|\xdd\xe2\xa0\x8d\xa86Ri\xff\x92Z\x01\xed\x9aJZ9\x15\xb4\xd6i\xc7xx\x1a\xd0f7FTo\xc1\xe9\x87\xd7\xa7\x87\xf3\x0d\x11\xa0~\xe6%\"\x0c\xe1L\x15\xe8\x9aK\\=\x04\xc7Eb\xc1\x1f\x85!\xd4\x96\xba\x10/\xe8{\xc0 n$\xb8\x0c\xf9\x959\x00\xcb\x99q=U\x91\xa7+\x82\x8d:\xd7\x08\xb6\x91-\x8a\x1a5\xe1\xc2{b\x1d\xfeN\xb1>.\xc5\x93\xb3\xbc\x11\x13T$\x17\xdcKbWB\x00\xe1\xfdx\x1e$\xa9t\x91_(\"\x18I\x95\x82\x9a\xdb)\x12\xb1\xdb{n\xff\xa0\xdd\x16\xca\xd4\xa0+\xf5\x1a+\xea\x86\x8d\x82\xb2\xad\xa5\xeaCuH\xff\xd4\xfc\xd5\xdb\xb3G\xc5`-\x01\x9cl\x18\x9f\xed<'\x91\xb5'{\x92\x13,\x88\xbf6\x1cJ\xc1i\xed6\x89\x80\x1bQ\xa4\x90Fr$ /\x94\xea$%\xdf\x9b\x86b\xf6\xad\x16\x81\x96)\"\xd3\xd4\x8f\\\xceS\x92\x91\x11\x12\xa6\x8a\x90FHi\xfd\x04\x851b\x05\xb8\x91\"\x07\x8c\xbb\xd1\xe0\x9b\x9a\x7f\xec\xef\xedX\x8c\xb0\x8be(\xd5\x9c,\xfc\xfa\x96b{\xb6\"\xb0\x01WVe\x11$%n&\x13\x137\x1a\x14\xfaR\xc6:\x13\xb8\xc2\xf1$\xf1\x98*\xbb\xb6C\x88f#\x93D\xb1)\xd9\xda\x92\xf1mhR(\xda\x7f\xe0i\xa0\xb9\xb4\xad-w\xf2\x84< V 1\x84\x0d\x15\x8d;\x0f\xdb\xa4c\xd8\xac\x17~\x80F\x1e< {\xe0\xe9\xa6\xc9\xdb\xdc\xa1}\xfd\xda\xa1\xb9^\x97\x899\x19W\xec+\xe0\xf2\x8fL\x8b\xe3e0\xf6\xd9\x9c\xe6a\xf6S\xc0\xaeD\xa6$;Pd\xb6\xe5nI\x17\x83\x16_Qc0\xba9\xac\xder\xaa\xd4)\xeak \x84:\x118D\xaf\xa4W\x95\x9c\xa5v{\x13\xe0\x1d]\xb1\xfb\x9dwg\x99e\xf1\xf4\xe1\xc3\xab\xab\xab\xf1\xd5\xde\x98'\x8b\x87\x93g\xcf\x9e=\xbc\x0e\x83\xe8\xb3\xd3\x94\x90!\xf0\xbf\xbc}#\xca\xec?\x8c\xe8\x8a\xa51\xf5\x98\xd3\x94\xa05\xf1\x12\xf5<\x16e?\xb2`\xb1\xcc\xa6\xc4\x91\xaf\xa3%\xbc#>\x9a\xa8\xe7\xe5\xab<\x04O\xd6;H\xb6\xef\x07Y\xb0\xb6d\x86\xc1\"\x12s\xff\x03MY\x18DL|O\xa7\x8d.U\"\xf6\xd10\xe4W\x1f\x19O|\x96@\x99\xf2\x15\x85\x8e\x97\xf4\x92e\x81\x87\xb7b\x15\x87A\x96\xfb\x966&\xf42\xf0^\xf1d%>\x04/\xa39OV\xd8wR\x0fn\x07\xb1Z\xb2, .\xf3\x8cI7\x88N\xe5\x1d\xabJ\xe7\x8b\xa5g\xc2\x8bw\x0c>\xcf\xf8G\x06\xc6\x92\x02\xba|\xc3`\x7f\x0fVy\xb6D\xdb)\xc6\xfcU\xc2\xfe\x91\xb3\xc8\xbb\x99\x12\xa7\xf2\x8e\xd4%\xf2?$|\x1e\x84LA\xab7\x0b\xac\x98\xcf\xd3e0\xcf\x14\xb4x\x1f\xa5\"\x01+p\xc9\xaf\xf1V\xb2E\x10\xe19\x01M\xf1\x8c\x1b4\xd9\xa3\xa1\xf7\x16\x0e`G\xffD\x1a\xe2\xd1\xb8\xd8\x0f\x1e\x8d\xed\x9b\xc1\x0b\x83\x18\xffN\x18\xc4\x1f\xa8\x18tG\xfc\x1c\xc54[Z\xca\x7f\xcca,\x01,\xc9\xd1\x91\xd4\xb5}\x8a\x02\xc1w;\x95w\x0c\x9e\x87\xb3#\x1b?\x98\xcf\xf3\x94\x1ds\xe9\xabsJ\x9cZ\n\xd2\x1b?H$go\xa9\x11\xbc\x9eZ\xf2\xd6\x81m |\xbe\n\"Z\xc1\xef:\xa9\x0d\xbd\xfb\xb9\xa5:|\\}\xbca\xcc_0\xb5\xb7\xf5O\xe4[,dkj\xed\xb8\xd4[\xfb\x81z\x9f\x17 \xcf#_\xd4\x05I\xa3\xcb\"\x0d\xab4\xc2'U\xd0L\x91m\xda\x04\x9b\x9bD4\xfc\xc8R\x9e'\x1eK?\xb2\x7f\xe4A\xc2\xe0\xa3\xb6<\xe4\xe3\xf3 \x0c\xd1\x0f\x88\x8c\xf71\xf5\x02\xf0k#\xdeF\\\xbeZjQ\xa8\x08 -\xa8H\xeew\xdb\xe72\x96|d\xa9\xacB\xfe\xb6V\xa1q\x99\xf1\x86\xc1\x86\x9c\xfb\xc7\x02\x13\x08P\xf12\x02\xbc`\x035\xba\x0b\xc0-\xfd\xe5^\x9e\x8a\x99\xc5\xfb\xc2\xa3\xec\x15]\x05!T\xc5\xa3l4\x877\xb4\xa2(;\x05]\n \x98\x06\xbf\xa3\x03\xa7\xc0\x8e\xfc\xff\xce\xd3\xcc\x04\x1eQH\xb2\x95\xc9\x12\x96y\xcb\xa2\x80|\xb5\x02\xdf\x84eC\xc4\x8b\x05\xf0'\x9a\x04\x12U\x00\xe8Z\xbeZ\x80\x7f\xd6g!\xc0^\xd9\x0eC\xa9\xae\x83\x0fg\xc2Wx\x06\xbe\xc3\xe7\xf8\x0e_L\xf0\xe4]<9\xbc\x89\x97\x8a\xfe\x82\xdf\xa3\x08'\xbe \xf3}\x12\xb0(\x03\xcc\xf0#O\x82\xdf\x05\x9f\x18\x16%y\x99;Z\x16\xd9=\xea\xfa\x89%Y\xe0YjZ\xabL[=\xe0\xb8\xdb\xd1?1\xa8\x84\xfa\xa2:\xd0\x12\x99K\x9a\xb5\x91\xd6RNo\xc2\xca;\x02\xbf\xa4\xd1\x02Ned\x98a8\x8e\xfc\xf5/S\xe2\xc0\xef\x11\xf5\xd7\xa3k\xac\x16\x91\xfb> \x16AT\x02sxG\xe1\x03\x9f\xf1EB\xe3\xa5\x85\x90\x0fVt\xc1L\x92\x01\x12ZI\x86 \"xU\x11\xbe\x86\x80\xd8\xf1X\x8c/\xeb\xcfx*\xbeJ?\xe3_\xf8\xbc\x87'?\xc2\x93Y\x12\xb1\xf0-\xcd\x92\xe0zJ\x1c\xf3\x15\xe9\xad\xcc\x16\x93\xfa\x06\xe4UE\x892\xc9R\xca6\xd9\x9f\xd9\x0d\xdci\xa4P\x95\xfa\x8d\xd6qs\x1a\x8b\xd3^\x01\xaa\x17\x1c\xf2,Xi8\xf8\x89@Iy[\x81;\xcdW\x14:\xcbXr*p?\xac\x0b\xf9>Je\x02V@\xa040\xa6\x95'\x8d~\xb7\x1e6`\x8f\x0e\x05\"v\x14-\x00\xe96\xd2\xb0r\x1cp\x012\xb2+\x9a|f\xc9 \x90\x1c\xf2\xf7\x88\xa1\xb4\x86\xcc|\x1b\x18\x80\xab\xc0\x0ex*\xaf\x085h*o\xa1,\xc0\x05\xd7c\xbeZ\xa15\xf60\xde\xac\xb0?\x07>\xac?\xe3\x0d\x85M\xf1=U\x84\xcb-qV=\xc9R\x9d n\x87\xcb\x96lE\x15\xa2\xc6>\xcf-\xd2\x82(_\xbd\xf72\xba\x86\xf5[\xbe \xdf\xd0R]\xa4\x12\xae\x89\x164O\xbaa\xc73\xa5<\x04\xcd ld\xa7q\x00\xd9\xf2m\xdc6_\xb3d\x1e\xf2+k\xa6\xd8\xe4Z6:%\x8eN\x1a\xc5*\x0d\x1b\x17\x05s\xb6\x0c\xbc\xcf\x11KS\xb3\\\xa6\x13\x91\x821\x0d\xa2\xec\xbd\x92\x08\xc1\xcb\xc8&\x10\x8ai\xc4S6\x018\xf1k4A\x81\xb2e\x81&\xcb\x17\x1cRP\xe7\xb5\xf5\x88\xa4\xda\xcb\x9a\x07v=\xc9^\xaa\xf6)\xeb78\x1c[\xa0\xee\x0e\xe0\xf2}\xc4 \xc1V\x00\x97\xa3\xc8\xac\xa3\xec\x17]\x8f\xf8m\xad\xe2(\xfb\xd5\x80\xfb\xb5\x05\xeeo\x06\xdc\xdf0\xb8\x84\xa5,Y\xb3\xa30^R\xf0\x1bo\xbc\xb7\xc1\xa71\xf3\xb2\x8fby\x9b\xa5\xcaT\xb4,`\xee5+\xc6\xb7\x92\x80\x94\xc07\x9d \xa2r|\x18\x136\x17#(\xfea\xd5\xb1\xf9\xaf2\x17\x1b\xb2\x82\x9ey\x0d+\x0b\x00U\n\x08cP\xba=a1\xa3\x19(\x89A\x81\xe2\xcd\n\xfbR0\xe1N\xf1\x1b\x85\x93<\xe8\xc9u\xc6\xa24\xe0Q\n\x05\xea\x89-%_1\x9a\xe5 3\xcb\xe9$\xb4\x94\xd2oA\x074\xcdCK\x16\xcflR\x94\x04g7\x12\x1c\xf7\xa6\x1e\xb5\xb0\x87)c8\xc3\x9f.i\\!I!\xa1\x95$MC\x1e[\xbe\xa2 \x184\x8fyyH\x13C\xe8SO\xc2\xbe\xa5@N\n\xb9\x84SO\xc2K\xd9\xba\x1b'\x8c\xfaoY\xb6\xe4>\xd4U\xbeb\xf5\x94\xda]\x02\xb8|Ca\xfd\x97l\x1dh\xe1\xa5\xf9\x8aB\xb3\x15.\xe0\x169kKN\x90y\xcb\xb3 \x84\xe5h\xbc\xa1\xf5\xf3X\xd3\x86\xe2\xb7\x95.\x14\x99\xa5\x0c\x02@\xed\"\x884K\x82\xcf,[&<_,\x8dc\xb3\x92\xdevvV\x00\xcd\x03\xb4ZC\xdb)*o\xb8,\x03\x94\xf0\xcf\x96\x95 Y/i\xba\xa4IBeWE\xca\xc8\xd7I\xf8\xa7T!^\xae\x81\xa2\x14\xb7\xaf\x04\x01\xf3&\x88\x98G\xe3\xb2L(\x13Z\x0b\xfc7\x0f\xa2j \x91b-\xf26\xc8\x04\xdd\xb1\n\x8c\xa6\xad\x8a4k1s\xbe\xa1L\xeb\x8c\xf3\xcfL\xd3\xc2\n\xfc\xcaB\x0c\xa7y2\xa7\x1e;\x95X\xc81_1\xe8\x1b\xb1\xd4\xdf\xd0h\x91\xd3\x05\xc0W\x12\x90\x12\x19\xbd\x0c\xa5\xb7&\xb1d\x8c7\x146Y0 \x02\xd4/+\xcc\xaf\x05\x0cv\x96e\xec:;\x02\xfdV\x01\xc6\xae\xb3\x91\xd4v\xb5\x80\xbed\x1eO4\x0e\x00p\xbfH\xb1\x141\x91/\x94h\xc3\xbd\x02\xa0\xa0\xf9\xca\x17\x0c\x92\xa3\x1b!+\xe98$7\xc7%\x019. \xc8E;k\x14t\x91\xd6\x86\x06\n \x13\x05\x94%\xdb\xb6\x7f\x1e\x05\x9e\x8d\xb7Qy?\x04~\x00\xf5\xc1\xdb\xe82\xf0\x03{E\xa0|e@\x83\xaa:\x0e\x9e\xa5\x1fXr\xb2\x92\xc0Y:\x8a\x05\x85\x8a\x11\xbf\xeb#\xe3>\xd7Y\x8f\xca\xeb]\x0c\xf8G-\xaar\xd6#%\xb6\xc2\xc0^\x9b\xb2%g=2dM\x18\xf8\xdb\n\x87\xe8\xacG&\xcb\x88\x15P\xdb\n\x19\xd65\xf32\x9e\x9c\xcc\xe7\xcc\x13xF\xbe\x8e\x18\xbcc5\xb1$\xb5\xb1jk\x96dG\xfe\xfaW\xa8&\xc9@\xf0\x86\xa1\x1d\x91Y\xca\xdd\x00\xb4E\xecVB\xffZ\x83F\xeb\x0e\xd8\xd5\x0f\xfcZ@\xca_\x16\x983\xc0 \nL\xbe\xa0\x90ip\x19\x846n\x18P%>\xacW<\xf1K\x89\x8fxk\x91\xf7\\% \xa9Q\xb7E\xeam\xb4\xc2o\x8cp\x9a\xf1\xba\x90\x95\\\xdb\xef\x87\xafq\x04p\x8d#\x80\xeb\xe3%\x8d\"\x16J\xad[@\x91\xf5$\xec\x1ba\x10}>\xf2\xb2\x1c\x88^\x07^\xa7T\xbe[\xc1\x13/\xe1\xa1\x01.\xdfm\xe0?& \x88\x96\xb0\xcb\x04\x15EC\xe6G\xb3\xd2\xb6\x1aO\x97\xfc\xaa\x00L\x97\xfc\xca\x06x\x16dF\x95\x99x\xb3\x82\xca\xab\\\x05\x89_\xe2^\xaf\xc2\x1f\xc0\xd3\xb6s\xbd\n\xa7\x97\x14U\x98\xb8^\x85\x11\xbe\xc8 \xe7\x17\xf8\x00\xd4\x10\xa5SLAG\x81\x8a\xb3W})\xa4\xe8:\xbc^\x85b\xcd\xea\xf6`J;D\xfa2@\x1as\x83/\xae\x1b|q\xdd4\x17W= \xf9\xf2\xefh]\xbfs\xbe:\x8a\xfc\x0fT\x1cQ\xe5K\xab\x7fT\x8a*\x1f)\x17\x02\x81\xc0\x95\xf5@\x11Dz\x1982Ug`\x84R\xcc!\x04il\x85\xa4Y\x1dil\x806 \xb9\xec\xdb >v\xd6!\x17z\x1b\x84Z\xe1\xad \xb0\xb2m\x10zI[\x8c\xdc\x8a\x85h\xcfWk\xb0WH\xd9\xc6\x8cL\xcd\xc8]\xa4\xaa\x9d*#\x02\x8e?\xb3\x9b\xd4\x0d\x06\xe39ON\xa8\xb7t\xed\n\x84t\\\xae\x08\x19\xe7vgH\x02\xf1\xeb\xc1\x03\xe2\xd2q\xe3\xeb\x12H@\x18\xeax\xdf$@\xc7N\xddu\x02\xc7\xedW[\x82\xfe`\x0e\x15\xa4\xa3\x85Guk\xd7T\x81\xef\xe2>>\x1e\xe3>>vw\xeb\xd5\xcf\xc16\xbdj\xcb\xaa50\xdf\xea\xf8\x05\xa69k\xc3;\x8b\x80\"/\x0e\xc8\xa4\xe6=\xb1i\xaeN@2\x12\x02]\x83o\xd0xIS\xe6\x7fd\x8b \xcd$\x15\xaf\x97\x10\n.\x1e\xe5\xf1~J\x1c\x1eID\x85\xa0)\xfdh\xd7\xf6\x06\xb4r\x11\xe5\xa0e\x90\xf5M@\xd9&\x16LC\xe4\x01^\x9a9\x19\x8f\x7f\x08\xf3\xc4\x19\x12\x07\x04\x01\x10\x1b\xfb-\x8br\x95\xf2\x8a{y\xaa~\xff\x95\xdd\xbc\xe4WQ\xf9\xf6)V\xbf\xdf\xf2\x06\xe8I\xe47'\xab\xa9\xa2\xbf\xa1EV\x8b\x05q\x87\x0b\x12\xfbf*\x0dM\xa7=\x0d\x82Mc\xd4io\xd3\xe0\xc2du\xda\xcfB\xd8\xb0j\x9dV\x8d\\\xf1m\xdb\xb17\x88\x1a\xed\xa6\xa5a\xab\x85b\x0f\xdb\xc4[\x8e\xbb\xb4KP&\x84\xd3\xc2PA\x07\xc7o\xb1\xf3\x92Q\x12\xa4\xf1I\x0b\x14\x8f\x05\xd0%\xcf#\x1f|5\xc4v\xd8\x90\xcd3\x13\xf8\x0d\x9b\xdfn\x94\xbf\xba~m<\xc0\xb2n\x0d\x8a\xfa\x9e\xbb\x16\x07,6\xde\x80~\x9a\x03\xa9\xcd\xfes\xc3\x93J\xac\xe6aH\x96Cbq\x10\xa7\x06\x9fC\xb4xr\xa0]58C\x91\x04|\xa6\x98\xd7!I\xc6\xa5\xea\xba\x8e\xb8\xf3Ry\xb7c\xa9\x0bf\x99\xd5\xfe\xfd \xf9\x8c%N\x93h\xfce3X\xee\x9aE\xa0\x84\x9aNImF\xd8u\x96P/\xd3wtu\xca\xa4%|\xf4\xd6\xa2\xc3\xea_\x0fdF\x0em\xb1\xd3\x06d\x8a\x9a[\x88'\xbd\n\xdam\xde=\x9a2\xe3\xd8\x9bZW\x9a\x1b\xba\x1c\x82\x9d;Y\x923\xe9#\x9e\x8f\x95\xaa\xed\x89\x1f\x80\xc8Q\x9a\xf1\xf82\xb6\xc7R\xfa\xa2\xd5\x07T\x8b\xd1!\xb8\x82\xc7\xb3\x8b\xf6\xc1\x99mo^qd\x96\xc7d\xf1\xe5\xbb}\xb8<\xe9\xed_\x87\xe3\xd6\x12\x17\x8b\xf4\xfc\x8eI\x89\xe0_\xaa6\xe9S\xdc\xd2 \xb5\xa6\x14\x19@n\xa4E{G\x0b\xeaT\x8b\xbdz\xb1t\xe7\x83^\xdd\xd2$TG\x97$m\xd5\xd9!\xd5\x91\x0edFZ\x1c94\\b\xfa\x1f\xf2\xec\x0d\xf8\xd3d\xf5\xe8k\x16\xaf\xa3%\xf1*M\x97a\xd1\x03u\xb5c\xb5\xc1\xc3\x8d\xaf.!\xf5\xae\xcc\x0c\x1e\x99\xc9\xe6\xaf\xbb\xc9\xfbP\x9c\xc9\xc9\x95\x05\xdbc\x94\x9b\xd9\xdf\xab\xf3J!\xce\xfc(\x8f\xdd{u&g\xae\xd2\xeb\xf0\xb1jM=\xdd\x97\xf0\x8f\xea\xbdZ\xaa\xf4\xfa(\xacUz\x9d\xe9Z\xa9A\xab\xc3/\x14|\xdd\x07\xdf\x8d\x1c\xcd\xfa\xe8\\*\x1e\xad>\n\x17e\x84\xaa?\xbe\xd6\xf2\xaej\xe1\xe8g\x0e\xbd\xe4\xe0G\xc0\xa1Q \xdd\xe3\x9dD~\xe5\xfdu\xc6\xf4\x15\x89\x91\xaa\xfd\x0f8\x97\x8a\x95\xf1h\xf4!\xa47\xc6\xcf3ya\x08)a\xe0}\x86\x1fUn\xc7\xe3\xb1,\x91C]>\xcf/Cv\xac\x81\xfd\x84.\xf4\x7f\xd5*\xf9S\xfa7\x90/\xd7A\xa6\x7fC\x8c7\xfd\xf2~]\x02\x15\x8d\xf5\x13\x0e\x1c\x92\x9f\xcb.)<3$\x0e[\xc5Y\x00Q\xcc\x1c\x16y\xc9M\x9c\xe9\x17_\xfdH\x12\x0e\x15\xce5{\x16D\xb1lv\x10\xadi\x18\x00\xd4\xe7\x92_\xfb\xccn>$pO\x02\xbf%k\x16r\xea\xeb\xff\xcc\x7fI3Z\xbe\xbde\x19\xf5\x8d\x94\xa2\xd5+\x93\xd5\x83\x97\xb7\\v\x14^\xde\xe7%\x94\xee\xf5\xaa\xe4\x06c\x9afL\xfe\xc8S\xf9C\xcd\x93\xf8\x0f\x12m\xe2\xc4 _\xe8\xc6&4c\xe5\xc0\x80s>\xc7t\xf1\xeb\xa4\x8c}\x96\x83\"~\xa9\x1a\xd2\x8c\x86\xa1J\xcd/WrV\xd2<\x8d\x99\x9c\xb9,X\xa9P\xd4\xf0\xc6soy,\xc8\x87\xb0xUS\x0c\xbfu\x07\xe1\xa5\x18\x08\xb8\x1f\x0b\x8cE\xba\xe6a\xbe2\x1a{EA\xf6\x0e?\x97\x8c\x85\xcey\x0f)\x91f\x8d\xd8l\xe7|\x9c\xf1Oq\xcc\x92c\x9a2w@\xb6\x05c\x16\x06\x1es\xeb\x9b\x95(\xcbg\x87G\x10\xe3\xb7\x99\x0bv\x98\x19\x8f-\xd9\x1c\x15x\x90;\x8a5Z\x0c\xc1KiFD\xb6\x89s\x0f\x92\x8c\x04\x91*T\x0f\xe3\x0b)P\xe3Cr5K\xce\x8b\x80\xd9\x00Y\xf3\xd2~\xa2PS\x91X\x08\x07\xae\xad\x16\xca\xce\x18\xe2P\x8d/\x12\xce\x81.}\xfd\xb2\xac\x1f\xa9\xe9\xd4^\xd3e\x9ee\xd2\x0c\xf8@\x06\xe0T\xdb\xdbHH\x8d#W\xa6\x08TF\x13FU\x9a\xf1m\xfdK\xf4\xec\xb8\x95\x92\xbf\xd8\x90\x92\xe7(\x13D\x13B\x87pR\\\xcd\xd89.-\xd8\xba\xe9 \xf5\xfb\xd3\xeaGpjtPT\xc7\xeaD\xe8\x07\xa6O\x8b\x0e\xe8\x97U\xcc\xdd\x01}\xa2\xb0z\x17X\x81\xf1;\x01\xfd\x1e@pRt\x00\xbd\x86\xd5\xd5 $\x0f\x96\x0e\xb07\xe2P\xe9\x01\xa3\x0e\x9c^\x90\xc5a\xd4\x03Z\xe2\xe7\x0e\xc0\x0fp\xfat\x01\xf5X/\x1f\xd4\xa9\xd5\x05\xa6O\xb4\x0e\xb8\x8f\xe5i\xd7\x05 'a\x07\xd0\xa9<\x1b{@\xf5\xe8\xc3\xa9:S\xbb\xc0\xe4y\xdb %\xcf\xe2\x0e\xb0\xb3\xf2\x9c\xee\x80\xfc\xc9<|;`\x7fV\x07\xb3\x9d\xbf\x12<\xc0\x1d\x19\xe5\xbfj\x8a\xab\x9do\x94\xfe\x9e.\xdd\xa8M\x82\xac\x9f\xfbf#!\xb8\xd3\xdd\xba\xd9\"\x88(`\xba\x84)\xa2\x19\xde\xdd\x9a!\xc9\xf4\xf6\xa1\xdeU\xaeq\xe4\xe9\xba\xc9p\xbf4X\x81\x8e\xbev\xc9G\xaa\x80@Y\xf6\x01\xb4Nc\x15\xec}7\x1a\x7f[P\xe6\x1d\x80\xdd\x12\x18\xa2\xe6.\xbe\xdb\xdc\xbd\x14\x9cUGc^*\xae\xab\x17X\xd6\xdd\xb9\x97\x9a[\xeb\x01'9\xb9\x1e\x80}F\xf5e\xc1\x01v\x02\xf2\xae\xadkq\xadHz\x8e\xfb\x99\xc1\xf6t\xe1a\xcd\x12\xf5\x81\xeb\xb3\xa8\xcfJV\xaa\xbd\x8f\x16\xef\xb8\xa4g\x1f\x8fLABG\x9b\x8e\x9aB\x86\xbe%\xfa\xf4\xa4\xc5\xbb^\x9f\x9e\x9cU\xd8\xcd\xf6O\xad\xef\xf6)\x19\xe4\xa7\xe3\x1b\xab\xbb}\xe3g\xe0\x88\xdb?\x81\xf8\\\xd3O\x9fO\x1c\xf3\xb8\x93~;\xeeF\x98\x1f@d\xd1\xde\xd2\xa6?\xc4\xa6\x08\x96\n.-q\x9d\xfd'\x0e\x1e\xc8H\xf0M\x17\x10\x90\xa1\xbc%\xba)9\xadf\x01u\x80\x05\xed\xb7?\x17\x83!\xb9\xa8\x94\xbd\x07\xa1/\xdcV\xf3H\x1e\x89\xa5\xdcw\xeb\xd4e\xe3\x8b\x8c.\xd0\xdb1b\x08j\x05\x1fm\x17\x0f\x04z\x18\x90`\x83\xf8\xac\x9f\x08\x96\xfe\xcb\x17\xe2\x9e(\xde^G\x85\n\x0c\x89\xdf\x0d\x16_\xaamh\xae\x820|\xc9B\x961\xcb\xf0\xdc\xfb\xd8Djll\xbd\x8c\xce\x95\xc3Iw0$>4\x0dR\xbb\xfaU\xbcYd\xef\xc7\x90zG\xd9\xfb\xa3}\xd4\x81=o\x11\x18h\xf7nc\x8f\x86\xa1\x8a\xacn@\x97\xcd.~%c\x9aC\xbc\xf8\xe3\x90\xa6\xa9\xcb\xeba@\n\xa9\xb0\xf4\x8f\xd0\xd4\x06a\xd2/\xb1\xe0-\xb0\xec8e\xb9\xcf\xcb\x0b\xed\xca\xadhM\xfd\x8a\xdf\xd3\xa85o,\x9a+\xc4\x0b\x83\xf8\x92\xd3\x04\xf8\xe6>~\xda\xb54\xa9RP\xe9\x94\x1c\x126\xae\xa4\x17\xb7\xa6\xd5\xe4\xaee\x85Mw\xf0-\xa7;\x90^\x86\xcdI\x08\xeec\x12&\x93\xc9\xbf\xc1\xdaM\x98@\xe2\xbeV(\xff\xf6k\xafy\xf1\xc3-79\xb8\x87\xbd\xcf\xecf\n\xf7V\xf5[4\xa2<\x02d\xa0\xe0\xdf\xdce\xe2\xf1\xb2$\xfc+T\x80f\x83/\xb5\x96|\x1a\xb6\xe5\xaeXF[\xb2\xa51\xa8-\x17|\x19\xa0\xd8\x81\xc8\xb8\x16o\xb9\x1f\xcc\x03pA\x90 8wwR\xbf\x18\x14\x8f\xb7\xa4\xc9q5\xf4~\xe7v\xfd\xccnb\x10\x1cH9\xae\xd4\xfd8\x94nm\xa7\xb5x\xa4\x04\x17\x8f\x7ff7\xb7\xf8\xaa/\xb8V\xf3\xa3_\xbe@z\x1e\xd7\x9a\xc2\xc6\xea\x03}\xdbs\xb5\x0c\xbc\xe5\x86\xadi\x19\x83\xfbll%\x05Eg\xf4[b\x00:$\xc1\xb7P\xe9m\xee_\xfcP9I\xbd)qNR\x8f\xa26\x05\xa0=}I\x93)q\x08\x92\xfd\x06\xf4\xad\x9c\xa3$\xe1W\xe27\x02\xf2)\xd6\x00\x9f0\x83\xc6\x8f\xca\xd0\x04 >ZLM^\xf2\xabH\xc3\xc8\x9b\xc7&\x08\x0b\xa7\xc4\x91\xa4\x1a\x92\xfd3\x18K\xbe?E\xb2\xde\xb2(\x9f\x12\xa7\xa2\xf9\xda\x00:\x8a\xe3\xb4\x13H\xb2MS\xe2\xc8\x1fo\xb8\x87\x19O\xbc\xe5\xbf\x7fH\x82\x08\x14\x84\x00?9\x9f\xa2\xc0gQ&\xf0\x89\xdfjg\x80\xa3\xe0\xfd)q~\xa0\xdeg\x9b\x85\xc5\xb3)q\xce\xe8%\x923\xd9\x15}\n\x19\xc5\xcc#&{ba\xc8\xdb\xedf\xe6\x13\xd1M\x8b\xaf\xcb\xc9S5T \xc7\xec\xc7&\xa2\xc1G!ZR\xb4U\xca\xe6\x9b\x99\xbb;S\xb8(L-\x03\xbb\xfb\xb4m%\xef\xedZ\xd6\xf0\xde\x1e|s\xc1\xd0\xf5\xb9\xf7H\xe5Z\xd6\xdd\xdec\x18%\xcc$|O\x8c\xd1\x8f\x1cu\xcb\xb5\xf7\xb4c\xdb\xec\xed\xb7n\x9b\xbdg]{\xe6\xd1N\xc7\x8ey$Z\xfe:J\x19\xea3\xe7\xd1\x93\xb6\xed4\x81\x95\xf3\ns52\x81u\xf3j\x17\xcd\x12\x83\xf9j\x0f\xcd\x12\xady\xf5\x08\xcd\x12My\xf5\x18\xcd\x12\xc3\xf8\xea \x9a%\x06\xf0\xd5S4K\x0c\xde\xab}tC\x88Q{\xf5\x0c\xcd\x9a@\x97w\xd0<9\x1c\xe8x\xec\xc2xL\xd0\x01y$\x06\xe4]\xbe\xb2\xac\xe8 \xccQ+6\xd9\xdd\x15U\xbce\x19\xada\x0e\x9c\xcb\xb3\x9f\xc0\xd2\x0b\xfegvc\xbb\xd1\xcd\x04\xc99\x03\x90s\x19\xec\xf63\xbbir\xa9\xc0\xfcV0\x1ah\xc8\x97\xde\xe3\xab\n\xb9_\x1b\x8d@\xcf~[\xa3\xb4\x7f|\xabld\xa2\xfc\xe1\x93C\x8d\xcc\xc8\x94\xc8\xb0:\xe3y\xc2W\xc7\x8a@\xab\x07DF\x15d7\xa2;\x82YAy\xc0x\xd5\x06eJ\x9cr\xc6\xee\xc1\xc9\xb6\xd4\x11\xfb\xd7s0>\xcd\xa8t\xf7\xc3\x92\x7f\x1d\x03\xd3\\-\xa0\xbb\xc3R\x1bI/\xb5\xa9\xcf\xda\x81<\xb8]\xf4;\xa0\xee\xc4\x96\xdc\x91%\xb2q&\xd5\xb5\xfd?\x86i\xff\xb7X\xf1\xb1\n\x15\xfd\x7f\x8b\xb8\xe9\xdf\x04O\xb00\xa3\xbft\xf1\x84\x1a\xf1JhCv%\x13\x04\x16\x05\xd5\xba\x97\xd5\xfc\x11\x1b\x1b\xc9\x0d\xc6\xaf\x11\xa74\xcc\xe8\xaf\x1b5\xe5\xd7zS~\xad6\xe5W\xbc)5(\x1c\xa8Ws\xff\x86-%\xc8\x91\x86\xff\xdfj\x19 \xce\xf2\xf1\xa0\xb9\xac\x9eu\xd1\x1b\x88\xac\\\x1f\xe0\xcd\xb1\xbe\xc8x\xfc\x86\xadY\xa8\xe2\x02O b`u\x11\xf8\xe0\xf5KdO\x90\xecJ\x84\x8e\xa9\x8a\x91R\x84\xc0\x80 \xa9\" \xc2\xa9U\xa3y\xd8\xb0\xeb\x85\x8co\x83\xe8O^dta~B\xe0\x82q\xc6\xdf\xf0\xabB{\xd3^\xa9\xb6\xfd\xfe\xf4\xf1uQ\x87\x91F\xa6\x88\xda\xfesl{F\xb5}x\xab\x196\xa7\xaf:3\xf5x\xcfS\xb2U3\xa0\xcfS\xf6*\xb8\x14\x13\xb25\xb9\x8f\xb6\x18\x91c\x1e\xd5\x15\xe6\xc51\xff\xf0\xb7\x87\x87\xdf?\xac\xa6\x0b&\xf9\xe1\xdf_\xfc\xb6\xf5\xdb\xe8\xb7Q-\x0f7\xd4?\xfe\xf1\xe4\xf8\xaf\xa7\x9f\xde^\x1c\x9d\x9d}\xbcxw\xf4\xf6dJ\x1cA\xc7\x8c \xe4\xf0\x08b*\xa79\x1a&\xc3\xf7\x8fU\xee\x19\x97\xb1\xb4\xbb\xf0\x081\xe8i\x9ct%\xe6\xd5^\xc6\xd2LTt\x08\x01f\xd88aqH=&\x10\xaaC\x1c\xb2M\xe8\xb8\xd9~\xb2M\xbe;p\xbe#\xdb$\x13?\x9d??\xf8\xae_@s\x1a}dy\xca\x9a=\xe9\x8a\x80\xa8c\x9b\x16\x16\xec.\xd6\xae\xf6\xce\x8aJ 6QL\x93\x94\xbd\x8e \xf0\xe4dg0\x94\xc1\x7f\x80\x8eo\xf6\xc2\xb6/\xeeY\xa4\xf6\xe4\xf1\xe3\xddI\x17\x92\xab\x0fQ\x11\xc7KL\xf6d\x08=\xdc\x91\x91\"wdH/V\x84\xdb\x12ks\xf4\x88< \xc1s\xc2\xc9\x0bB\xd1\x10_E\x8d\xb9\x19f\x90\x93m\xf2h\xe7\xd9\x93!\xa1\x03Y:\x17\xff\xb6\x0f\xc8\xa3\x01\x89\xc4\x7f7\x13\x7f\xd9X\x0b\xa4\x8f2\x97\x0f\x06d\x1b\xcd \xdbd\xd2\x96\xb9\xdb\x96\xb97@f9#\xffq@\x121\x00\xffa\xc6\xa6&\x8d T\x91\xdaD\x17\xc48lo\xab\xf6c\xcdGq\xa0+?5 _\x88\x1b\xa9\x9f/^\x90\xc9\x93\xfb\xc0G\xe6\xac;\x93\xc7\xe3'\xe3]\xe7\xf6\xb5u\xd8,\xb9\x91\xfb\xe8\xc9`(m\x91p\xdb\xa5I\xdd\x9aG{bx40\x8f\xec}\xa8\xe5\xd9\xc6\xa1\xb7\x04;\x1e)kw\xd6\xa2/'\xe0&\x8a\xfb-\xe3\xce)pV\x85\xd5\xbb\x01\xac7\x1b\xe8O\xd4T\x8a\n\xdcL\x06\x11\x1e\x08\xf4\xc7\xed\xe6\x9e\xcd\x16\xa1\xa1\xb4\x04\xf2\x8c|&N\xfd\xc4u\x1e=rDY\xf1\xeb\xb13\xac\xb8\xf3\xb8\xe7\xf8WbB\xf6,\x83\x9f\xa86\x9d\xe6\x97Y\xc2\x04\xd2\xe3EX\xe0\xdb\x7f9\x1b_\\\xb0\xf4-\xf7\xf3\x90\x81!\xdeP\x86\x87\x8b\x98\x97\x01\xa6\xfe\x90\xf0u \x86BG\x1dm\xb6:p#w\xff\xf1n}\xe5\xf1\"\xeb\xd1\x00e#\x02\xabY\x83\x8a\xf7h4M\x1ejM,\xa7\xa2\xa7MIwL\xc5J_\x12\x1dw\xad\xda_\xae\x93\xefyDU\xad-\x83\x18\xb9u\xfb<\x0eK:r'\xd8\x96\x16\x19{O\x1f\x9b\x18T&=\xc1\xc7\x9a\xfes\xc7Z\x9f;-\x07\x9en\x99\n\x1a\x8d|o\xab\x1fU\x016\"n5\xe8\xdd`@\xb2e\xc2\xafH\xc4\xae\x88@2`\xdc\xe0:\xc74\x8axF\x04oJ(\xf1\x04\xc3IhJh\xf1%\x07\xa1~\x14\x17\x8b\x99\xdd\xaf\x95\x95y\xff\x862\xb3e\x1f\xd9\x9c%,\xf2t\xf3\xc4\x87\xc8\x92\xa6\xd1w\x19\xb9d,\"A\x14d\x01\x0d\x83\x94\xf9dD\xd2\xd3\x05\x1b\x93O)+\xeb\x1b\x83\xb4\xa2xu\x07$\xe3\xf2d\xcc\x96l5&\x1f\x19\xf5\xc9J`m\x9a\x11\x15hu~9^\xb1\x87y\xca\xa4\xa8cT~\xc5\xa9\xdf\x8a\xe1\xa3\x91\xb5-~\x1b]A`\xd0\xcb\x95 \xb8\xe1&\xaf\x80\x0b\x08\x95kn\x04C^r\x1e\xa2\x19\xa2\xb1h\x86\x8c\x94\x8bf\xc9\xa3\x15\xcd\xd2\xce\xc5\xb1\xac\x9b\xd5\xa5\xa5\x114\xc2[\x0d\xfdy?Ge\x8bLK\xdb\x90r\x9a:\xb2\x14\x95\xf2Jk\xc7,\xa5xd\xab\x0fr\xa4\xc7F$\x17\xe2\x01\xe0]\xb8\xa6b\x18kW\xbf(\xff\x1e\xd5\x160\x91r\x83\xb1\x99 \x0e\xec\xa2\xec\x1d\xf0F\x83\xa8o\xa2\x14u\x82\xd14\x0d\x16\x10\x9e\xbb\xaf\xb0\xe79\xc9\xc8\x0bB\x93\x05\x88\x94S%\xe6yN\xb2\xedml\xaf\xe8\xa5^\x14\x98e\x88\xe1t\xf1\x89\x84\x04\x91\xe8\xa1j^y,-i\xfa\xfe*R\x8e&o$-')qqN3\xa9\x1b\x1f\xcd\x92\xf3\x1e\xd7\xdd\x86 9~\xe8\xb4\x8d8Q\x9d\xf2\xccN\xa9Q \xdf\x93=\xd1\x1e\xc95\x01\x8e,\xfb\xbdwN\x0e\xab\xaf\xb8\xfb\xd4\x159 ?p\x1e2\x1a\xa1\xa6\x04\x0b\xa2\x0c\xe3\xe7\xcd\xbc\x1b\x84e\xd3\xe9x\x14n}S@\x0e\x89\xbb#\x0e=5\n\x03)\x81\x88\x9b\x88\x0b<\xa2\x80\x8b\xc0\xe6\xf7\x05\xbd\xe3\x8d\xe3H\xf2z\x1dNb\xdc\x99^u\xcd]Y\x8a\xe6\xd58\x00\xe5\xdb\xbdp\xd4\xeeJ\xcb\xd3\xe8\xcb\x17\xb2%\xe8oZ\xd2\xdf\xba\xce\x12j e$\xf5\xb2\x07\x82\x0d\xa8\xbb\xb2\xd5\x0f: \x95\x11\xbd\x8f1\xa9N\xd1\x1d\x87\xc5\xaf\xe0\xad\x96\x91\xa9\x00\x9a\x83\xe3\xd70\xdf\xa6\xe3\xf3\x96%\x0b\xe6\xdfit\xba$OX9\xb1_/\x8b\x02\xed\xacf\x8b\xf3j\xd2\x85\xa1H\xc1N\x1a\xcb\x08\x1b\xd3\xcd\xa6oKV\xb9*\x07O\xcc\xc8)L\x0b>\x81\x06\xa89}f\x0d\x9bL^\x90\x9e\xe6\x97\xa9\x97\x04\x97\xfd\xe7K\xb5\x1d\x97\xa9\x89\xc6\xe4Q\xaa+\xed\xd3\x86,\xb9)\x1a\xd1\xb7\x0d+p\xbeQ\xffZ9\x1ef\xe2\x81q\x1f8.\x92%\xdc\x92F~\xa8\xa8\xe2\xf1e\x10\xf9\x90<\x18\x0cI#\xdbE\xfc\x8c\x10\xb47\x9f*\x1f\xef\xd5\x9f^=qu\xb3\xaa\xbd\x13\xecd\xaf\xa6\x15\x92\x83\x97\x81\xff\x96\xe7Q\xe7]\xab~\xe0\xa3\xe64\xb9\x9b}\xef\xe7 \x0c?2\x8f\x05k\x84\x93h\xfb\xf0U\xcbN\x90[\x0c\xdc\xc3\xa8\xb9j\xf2@M\x7f\xe5\xfaik\xea\xa7hu\x9b\xd1\xf9\x84\xcc\x94)\xb3\xe8\xd5\x8e\x02~\xa3\xaf\xd7\xb17h\xa5\xd7\xcf\xc2jz\x15c\x18\x19\xb6q,\xb2\x9b\xecd5\x7fm\x9c\xf7?0\x16}H\x98GC\x0f\\\x19\xf9\xca[\x7f\xadi\x06H\xc0#\x10\xa3T\x1b%o\xe6\x99\xaf\xb4\xd4\xab\x99v\xa2\x0b\x01\xaa\xf1%\x0d-|\xfd\xd4&\xc6\xc4\x04}\xa7\x06\x14\x1fk\xfb\xb5\xcf\xa1VCY}\xf9[\x02:\xb9\x07\xc6\xd8\x8eK\xe9Z\xfb\xd9\x07\xec\x8b\x14'\x00\xd1\xd9\xd9L]\xe8\xaa\xc4\xc3m\x1c]\x9f\xea\x08&\xcd\xef\xa2\xf2\xebO\x96\xdcl\x00M\xcc\xab \x1a\xc7\xe1\x8dk\x11\xe2`\xcfW\xe2\xd1vo\xc6\xb6G}s9\x06y\x9a<\xb0\x97\xbdk\xb0\xcb\xb3\xccGQ+6r^\xee\x8a\x0e\x8aI?\xb0<\n\xe7\x9a\xfd\xcaDp\xd3\xb5\xc4\xc8o|\xb7\xab\xd1\x18\xf4\xc7#\xedb?\xd2k\xa8z\xe1\xb4T\xef\xc0~\xd3l\xca\xb4q\n\xc8|\xbe\xb6\xaf\xb8\x16\xe9e\x1f\xbc\xb5`\x99\xb4\xb7\xf2\xb5zu_\xec\xa59\x8c\xea\x15\xc7\xf5\x908g\x9cP\xcfci\n\x97\x12W\xb2\xfa\xe2\xf6kHnxN\"\xc6|\x92q\x88\xe0\x1f\xcco\xc8\x1fD]kNI\x96\xe4\x8c|%T\x16\x9f\xf3<\xc9\x96\xc5\xe50\x01\"\x12\xeeF\xe0~q\x00\xf7HcgP\x1c\x04\xf3t|U\xedQ\x9fq\xe8\xa7\xda\xa5\x1f}\xcdi;\x10\xdb\x11qT\x96l\xae\xab\xf6\xa2\x81\xf9\xd1\x96\xe5\xdf^\x0b\xad\x9c\x02\xb6=\xd7^G\xae\xeb\xa8\x1d\xbd\xf6\xdd_\x1cw\x16\nb\xd2AAL\xfa\xef\xfc\xcd(\x08\xaa\xefih\xbb`-\x95{\xbeuX\xc2\x8e0Hp \xe6\x80\xf5R\xad, /e\xba\xce\xc8!\xd4m\xc2\xb6\n\x88:\x84\x84\x1e\x12\x1d\xb1\xfe\xccU\xb4D[~@\x0ee=;dJ\x803u=\xbd*l\xe7\x8a+x\xa7\x10`\xe7UXT\x82\xe2\xb6]\xc5\x16L\xf2\xd6\x96\xeb\x81\xd6\x07\x8c\xe6\xa0\x18\"\xab\xe8\xc1\x95\xbcqN\x0eIN\xa6jY6i\xc8k\xa5\xf9\xc1\xd5\xf5\x99\xca\x01\x1e#q\xff\xf8\xda$\x95\xbb\xee\xd3d\xe0\xe9\x1a~\xc2#`\x10\xc0\xfd\x03\xd1\x88TX\xc7j\xc5\xd5U\xb4l\xac^um^\xb5\xdf\xaf\x16Z\x93\x03\xe5!\xe0~\xb4\x1e\x87v\xa5\xbez'\xc1K\x90ti[\xdcR\xd5\x8f8\xcd\x98U-\xea\x9a\xc7KR\x83\xa9#\x19\xb0>\xd4\x1a\x83\x82\xd3L\xd4K\xf9\xe5\xda\x81T\xa8G\xf2\xb2j\x9bj\xa44\xbf\xddyN\x02\xf2\x82D\x85zf\xb0\xbd\xdd\xc4\x91\xc0\xd3p\xa5\x194$\xd1,8\x07a\x12\x9b\x89\x9f\xe7\xf2\xeeE\xfe\xb6\xb6\xad\x18\xac\xda\x0e\xf9\xb6Sh\xd9\xe7\x05\x00\xca0\x1b\xd4|\x02\x82\xce#\x00\x06\xdb\x7f\x9e\xa4\xf2\xbc\xe9\x89&\x957\xc2\xa7J\xb4\xd6\xd1[(QV\xd0J\x83\xe3#C\x0c\xb9\x08\x8e\x04\x1a\xd6\nv5\x12\xaf\x17\x94\x1aw8v[\xa0\xcaS\xd2\x0e\xb4`\xd9\xcb^\xb5\x01`\x12\xac\x99\x0fd\xd5\xab\x84\xaf:J\xac\x82\xeb j\xc9/\xceS;H\x06\x8a\xdf\x08+\x8dh\xe7f\xd6\xf1\x8fZG@\xee\xc3\xd6f\xca\xed\xdc2k4\x0c\xc1\x05E[~K\xf9B\xf7\xb8\x0d$\xc8n\xfa\x0e\x85\x81\x0b}6\x0f\"V\xa0\xa0\xe6\xce+A\x17,3\xb0\x15\xc4\\k\xc2s\x1b\xfc)\x98 %\x02[\x89\x97,\xf5\x92 \xce0^\x8fV\n\x19\xdaMMPA\xcaPAEP\xa5'\x85[\xe9\x17\xb4H\xea\x86C\xe2\x0d\xc9\x1cCD\xa0['\x0d-L\xcd:\xcf\xc6\x8e\x0bx\xd4\x0eG?\x023\xc4`g\xeb\xb5\xf0\x12\xb1h\x7f\x0cX\x1d\xb83hc,\xda\x88\x16\xc1e+\xe2S>\xb8\xf8\xb0}\x8a\x13\x1d\x1d\xd8\x17\x84\xb1G3\x97\xbb\xde\xc0\xc6\xe5\x14\x87\xdbR\x9e[K\xf2\x82\xf8\xc5\xb9\xb5\xbd\xbd\xec\xea\xb8 \x1b\xfc\xd9\x121+\xd0\x8fRN\x9e\xad\xc1a]\xa6\xfe\xcfE;\xe7\xb3\xf5\xb9\xd5o\xbd~\xc4WV`\x1f\xee\x0d\xc9\xbaC`\xd8O\xfc\x1a\x89\xb1_\x0f\xc9\xaaC\xf2e\xcaW7\x16\x83\xa1\xa9j\xa56%\xfeMp\x14\xd48\x12\xab\xde\x97\x12\xb7\xd7Y\xd8\xed\x81\xa2^\x1aL\xd1\xf8\x90\x04\xb8A\x9a\xd6\xdcn\x0e:\x084\x9a\xb3%\n\x18\x96\x08\xd9@\xc6\xbaeWD)\xaf\xbe\x0d\"\xf0fH\xd8\xb5\xc7b\xd8\xcf\xdc\xf3\xf2$a\xfes\"\x9a\x9f-\x19\x89x4Zi@\x9f\xad \x8b\xd6A\xc2#\xe0\xab\xc5\xa2\x06\xc9^\x1e\x86\x04\x82\x9a\x92\x15KS\xba`\x84F>\xa1\xbe\x0f\x11OhH\x96,\x8c\xe7yH\xaeh\x12\x05\xd1\"\x1dc\xda\xe2,L\x99eQ\x89>\n\xcehV\x1f\xa6s\xbb\xe0\xc3\x83\x9d\x86f\xbb\xd5\xa1\xc8\n\xbf<\x0f\xff#}\xb8\x18\xf6\x13\x1d\xeau3\xf3\xb6\xb7\x9b\x01\x1c\x88d\xfa\x07\xd2\xee\xe1\x808\xaf\xa35M\x02\x1ae\xe4\xa7\x80K\xe1\x15b\x00\xd1H\x91\xf2\xact\xd2\xec\xcc\x1f_\xf1\x1d\x828Hi\x02\xea\xd5\x87\x89\xd0\xa4#\xa8l\xd8A\x95\x13C}L\xbaE\x91\xf6\xd1!\\k\x83<\xb04\xaf\x9a\x0c\x86\x98\x8d\xff`Hr\xd1QO0d\xa0h,\xc5o\xa2\x7f\xdc\x8d\x86\xe4\xe9\x90\xa4\xd8\x01T\x1c>s\xe3;\xcf\xc9|4z> \x01\xa8\xfc\xcd\xe6\xe7-R\xa2\xeaR\xb3\x99\xdd\xa2\x0b\xcf\x1c\x8c\xde\xbe\xe5\x8a\x06\x8b\xae\x8d&C\xa2E\xbc0U\xe4\x90\xec\x80Nvy|F\xe4\x05I\xe0\x86R\xe9\xd2\xb9l\x16\x9dK.~\xf0\x1c\xa7b\xea1V{o\x99\xc6\x9a\x96;\xe6\xc9\xa3.{d\xac\xab\xa6\xec\x06\xd6\x11w\xb3AE\x90u?\xad\xdb{\xba\xffo\xd1\xbcF\x88t\xd9\xbcI#\x02\xbbB7O\xea\x88\x82vK\x07\xba\xfa\x89\x9e\xad\x89\xcb\xca \x8eA\xc3\xb7\x91\xbe(\xe2\xa84D\xac\xd3\xd9\xb9E\x9e\x91\x835\xd0\xc0u\x0c\x1b\x0c\xa0\x88sP\xe0\x83\x8b\x00*\xe5\x13L\x9c\xfc \xd1\x8e\xc6q\x9e.\xdd\x1c_\xbb]\x06\xb4\xdd\xbb\xae>\x06\xba\x7f\xf5^\x14Hr\xeb\xa0.]%\xd5\x9d\x1aDj^` 3\xd9\xfe\xba\xaa\x9e\xc6\x81\x9b-\x9f\x8e\x88\xdb\xdaM\x1321\x1c\xe2j+c\xb3\x83\xaay\x8f\x8c\xebdx\x95\x14i8\xd3\x05\xd4>R\x8f\x14\xb9B=\xacR\x0ff%N\x943\x81\xa0\x9c\x90\x03Q\xf5!I\xc6?\xe4\xf39K\xc8T\x99}\xdaX\xb3CB\xc74\x0c\xb9\xf7)J\xe9\x9c\x15\xf0\xd5A\xee\xbd\xbb \xa9;\xed\xd21\xca\x91\xc3`]h\xa4+e\xe4\x06\x04QL0\xdc\xc6\xb8\x11h\"\xb3+\x02z\xdez\xe1\xa3\xba\xe3\xc5\xc7=\x1e\xdf\xb8\xc9`h\xf52\xf7uP\n\xf2\xdc\xc9\xde\xa3A\xe1\xeek\xf3-\x80\x0c\x88q\xe64\x1bi\xf4\x1d\xd9\xe9\x99TP#\x07\xe4(I\xa8\xe8\xc5\xa08\x99\x9e\x0fH6\x8b\xce!0|t~\x1f;\xa2\x13\xdfO\xf6\xefr\x1c%\"\x13P\x9d)+\xbc\x9f\x96\xed=\xedt\xdcqO-\xab7+\xba\xff\xa3C\xa3M\xfb\xa6H\x14\xabQ\xdd\x05\x16\xc9\x8a4\x82\xd5B\x13\x03\xcf\xccv\xce\xe5\xa9\xa0\x8f '\x88|v\xedH\xcd\xe0d\x0co\xd0\x0e\xf85$\")\xce3\x95\x14\xe7YeSm8\x93\xbb\xbb8\x93\xb0\xff\xb4N\xae\xabS\xfb)\xee\xdap\xff\xe9\x1e\xca%\xec?\xad\x9f\xf2b\xd4\x9d\x99D\xb8\xdaQ\xc0\xb9\xd3d\x19\n\x98\x974cu\x00\xcf\x04xK\xe3z\xfe\xdc\xcc\x7f\x07\x8eD\xea \xb1 \xf2\x91-N\xae\x1b\xb5\xf8&\xc8)\xcb\xea\xf9\xcbJ>Lm\x1dd]\x01\x01\xe9_\x1dde\x82\x00\x86\x91GF\x1dnQ\x1b\x14\xfaS\xc0\xae\xea@7&\xd0\xab\x90\xd3lo\x17\xea\xac\x03^6\x00\x9f\x01\xd4\xb1\xbbA\x1d\xe2\xef\xc4Z\xd3\xde\xc65\x89\xbf\xbb\xbd\xbc\xe7j+a1\xd6\xb7]\xa9\xfb\xb6\x1b\x90G\xf8R\x9d<\xc3tk\x04\x1b\xdbzH\x90\x9aL\xcd\xc9\xb8\x143;-\x91\x0c*^\xf5\x9aHH<}<\xfb)\x83\x07\xc1~\xe0\x00\xa6\xbb\xbf\x06@\xcd\"V\xb0i\x01\xbe\xf3\xf0\x18`\xdd\xbb\xc5\xb2O[93\xbd\x04,\xab\xa4{\xe3j\xd6h\x7f\xa76\xb2bYL\x9e4\x97\xc4K\x9a\xb1q\xc4\xaf6\xc5:\x9a\xdeA&0hj\xbf\xf5\xe9\xfbZ;\x02\xb5\xf9 \xc8\x01{\x8e\x88K\xc9\x08\xf5O+\x98L\x88\x86#\x0e\xa7\xef\xc9\x0e\xf6\x15\x0d\xb7\xbd\x9d\x91\xef\x0fHapnx\x8e\xdei\xaa\xd4}\x95\x1a\x82\x19\xae\xd7W\xdb\xb8\x9a\xcd,j\xbc'\x89\xe1\xe4\x11.\xe3hluEn?\xc3\xc9\xed\x06S\x9a\x93\x03T\x0d&\x85\xf4\x86\x16L\xd8}\x95Y-\xe0\x011\xde\x89G@ \xdb\xcd\xe0\xf0\x92\xb1\xbb\x80\xc6L\x95\xd6Os\xd8\xc5\x94\xa0\xf3[\xd5\x0c\xc9\x06$,\xf1\xb1\xe6|\x80D\xcafQ\x1d#[\xa8+o\xb3\xa9\xda\x7f\x86\xc7\x93\xd8\xdb\xe9\xbe\x1a\xb7R\xbc\x05\x08v\n\x13\xe3\xfb\x18iG\xf4\xbahU\xa1\x90\xfc\xaf$\xbf\xa2YPeL\xec\xbbR\x14\xd9\x85\"\xbb\xe7\x16\xc5\x10\xa2\xe7\x85\x1aW\xd6\xda\x9f;\xea\xe6Ip\xdan0\x1a\x81mu\xd1\x06\xa9Y\xcf]\xf3`\xcd\xe5U\xb4l\xfc\x0b\xb2g2\x06T\xdak\x81^c\xb1p\x05\x95A\xb6\xb7\x13\x08\x16h\xc3\x12\x9aP\x8ef\x89E\xf5\x1d\xcc\x95\x81\xdcNe4\x8f\xa6\x92\x92U\xb8V\x0bip\xeb\x83\xbeyp\xab\x95fa\xc2\xf7\xf6m\x11\xe5\xfap\x83\x81\xab\x83='bS\x92m\xe28\x1b6\xbd+\x12\xcb\xfe3\x1c\xcb\xed?{j \x1bWo+\xd8/\x03j\xf2xH\xaa\x8e\x8aB\x9a.e(\x882\x91\xe6\xd9\xb2\x9a\xb2\xe4i\xcd\xfd\x8f\x18\xa4&\x8cR\xb0\xae86Jku\xa5\x8c&^-\xed\x1f9Knj\x1f\xa0\xd9\xb2Y\x9dH\xad} asRs)T.\xb2l\x0c!P\xc9\x01\xb9\x1c\x92l\x9c\xb0\x94\x87\xebN\x97\xaejr\xc1\xc7\xdd\xd6\x04\xfc\xba\xe9\xa2\xa6\xaf\x9a\xafF\x95r\x1f\xf5\xac\x98\x91C\xb4\xf2b3V<\xac\xc3g\xe6\x0eRIl*y\x16H}.\xad\xd7D\x15\xdf\xf9\x01D\xe0\x96_\x81\x18\xcb\xa6\x1f\x0f\x99\xac\xafZ\xaa\x0d\xfb\x94\x88%\x15TW.\x85\xd0\xc1\xee\x8c\x8e~\xdf\x19=\x1bo\x8f\xce\xb7\xa7\x83\x87A\xf3\x98}8\x9d\xed\x8c\x9e\x9d\xff\xe5\xcf\x0f\x9bG\xed\xc3\xbf\xbb\xbf=\xfc\xed\xe1\xa1{\xb8\xf5\xdb\xc3\xc1\xec\xef\xbf\x1d\xfe\x96\x9e\xffe\xe0\xfev8\xfb;\xfc:\xac\x97\x02\xb3\x04\xe7\x0fgH\x9c\xaf\xe2\xcf\x17\xf1\xe7\xb7\xdf\xc4\xdf\xbf\x8b?\xff\xe5\x9ck\x03\xa1\x99\xf3B\xa4|\xef\x0c\xc9w\xcew\x90\x07q\x80E\x81\x04\xfeF\xf07s\xce\x07\xcd\xd3{\xe6|WV\x15\xd6\x00\xe6\x00\xf0\x1f\xa2\xf8C\xf1\xe7P\xfcy.\xfe\xfc\xaf\xb2\x90W+\x14C\xa1\x12\xfe\x7f95s\n\x1fFd\xb6-\x87\xf4h\xf4\xb7\x8b\xd1\xf9\x1f;\xc3'{_\xeb\xa3\xb0T\x83\x8f\x80\x0e\xdc\xf1_\x06u\xf85ja\xf8\xdftM\xa5!\x1b\xce\x958\x06\x80\xd3\xe0(j\xd6{\xabo\xff\x89\x05\xfa \x88\xcb\x84V.r,\x86\x89s[\x99\x05\x8f\x976\x83\xc8y`\xe3\xdf\x1ch\x84\xd3\x92\x99Zs\xe7-%Uk\xacEE\x83:\x87\xedF\x9d%\xfb\xe8Yri\x93q\xfc\xff\xec\xbd\xeb~\xdbF\x928\xfa}\x9e\xa2\x84\xec8@\x08R\xa4\xe4+mZ\xeb\xc8\xcaF3\x89\xedc\xd93\xbb\x87V\xf4\x87\xc8&\x89\x18\x048\x00\xa8K\xc6\xdeg9\xcfr\x9e\xec\xff\xeb\xaa\xeeF\x03\xe8\x06@\xdb\xc9dv\x07\x1fl\x11\xe8{\xd7\xbd\xab\xab\xe8\xfa:\x17<\x06a\xa6\\\x8d\xc9\xbc\xa2S\x95\xa6\xe4\xb5\xd2\x1b/4R\xa7\x94(\xb7\x1a@\xdde\x0e\xc7\xa1Q)I\xe9\xdb\xec3\xe2\x12\xbaF,-)\x05^\x05i\xb0f9K\xe1\xebm\x1a}M\x19\x05.\x19\x04\"gU-\x81\x80\xc9Q=,<\x01_.\\\xe7\xc81(s[\x94Q\x8b\x14g\\h\xd3\xea|\xe5xp\xc4\xe9\x02\x8c9a\xa8\xd7\x8f(S\xc6&\n\xf3\x9a\x97z4\x1d\x9e\xc3\x04\xff+\xaeV\xbd{\xb7\xbfD\xf2d\x18\xf0%\xa6\xfb\x99@4\xf89 \xe3Z{|\xf5x\x91\xcbA\x9e\x86k\xd7\xf3a\x0fS\x8d\xcb\xb4\xc54\n>\xe6\x06\xf3\x17\xef\xe7\x02&\x90\x91#\xc3\xa5Ew\xbd(\x07\xf0\x16\xcc\xff\xb2\xcc\xf9/\xeb\x02\xc3\x05J\xc1\x17\\\xf8>\x92\x81\xd0\xa4\xd4\xc1\xdfV\xa4\x8e\x1c\x8e\xe0V\x80\x9bV\x18\xc3\x96\xe6\xa9;\xf2T\x10n\xe3\x07(\xa2\xad\xc9N\x1c\xa7\xd2\xc5\xdf?\x8a82e\\\xac-\xfe5\xd7\xd6\xcd\x8b\x82\x91\xffl\x8by\x02\x13py\xe5\xeb\xe9\xf0\xdc\x1b\xe4\xc9\x0f\xc95K\x8f\x83\xcc\xe8>^\x15\x08O|\xa0-\x15\x13\xbb\xaey\x1f@m\xb4x\x19\x81\xab\xa6\x18\xc1\xf0r\xb0\xc6H\xea\xfb?q\x96=\xfd\xe9\xdf\xdf\xed\x9f\xf7\xfe]\xfc\xbfo\xbc\xef\xca\x87\x8dn\x83\xfb\xfb\x0e\xc2\x8e\xea~\xe8\xc3\x81a\xd4{7\xd4\xdd\x9d;\xb0\x9e^\xe3\x8dZ\xb74\xec\x03\xaf&\xd5V#\x91\xd6\xe7\xb0\x87m\xf1-,\x9a\xdf[N\xaf\xcd\x97t\x95&}\xe6\xc3\xb1\x8f\x9e\x87\xfd\x91\x8f\xde\x82\xc3\xc7\xf0\x0c\x9e\xc0F]\x85zfNP\xc6\x1f\x81\xec\xeeK\x1c\xbeD\xf4\xcd\xf4\xd9\xb9\x88/\xdc'tz\xcf\x87\xf4\x12\x9e\xc0{z\xcd\xfb{iP\xaa\xb8^J-\x1e\x13)\xa1\xcaGpY8\xffpJ\xf2\xef\x98\xa9\xbb\xf6\xd2\x87\xf7\xa2\xdf3ZO\xbcw0\xf4\xe1\xd8S\x90\x81\xaf\x8e1\xa1}YM\x98\xb3Y2go_\x9f\xaa E\xee\x99\xe7\xc9\xb5\xb1(\xbd\xda\x82-\xba,\x18_\xf2\x97\x8f\x8bi\x96\x17n\xf1y\x0bG\x15d\xb1K \xfce\xddG[\x95\xf7\x95Uy\xef)\x12\x94f\xec\xfb$\xcb]\xaf\xae\x14\x95\x7f\x7f\xf8\x00\x8e%\xb3\xd6+<\xd7&\x9c(U\x12\x8e\xe7\xce\xb9\xe9[\xe9\x974'\xf4adP\xd5\x11\xec_\x99\xef\x81+\x00\x7fS\x1d\xb2\xa0\xec\xfb\xef\x06\xfb\x9e\x0f?r\x82\x83\xbb\xe8\xc3\x1b\xb9b\xb4\xa1?6\xee$\x88Y\x9e\xc2\x04\xdeL\x9f\xb5\\\xa2?Et<\x15\xd4e\xdezq^\x0d\xffgA\x85_\xd0\x10_\xc3\x04N\x15\xa0\xbd\x80'\xf0\xfa1\xbc\xe0\xa3<\x1d\xccVAz\x9c\xcc\xd9\xb3\xdc}\xe1\xc1S\x18\x1d<\x80#\xf8\x19z\x13pn8\xcf\xc5?O\xa7/\x1a\xc6\nrY\x7f\xee\x97\x8b~ \x19\xc2\x198\x1e\xf4\xe0\xd2\x80\x15\xcf\x8b\x12\xedc\xb9LY\xf0\xbe\xb1T\xdd\xbc\xd4\xfc\xa5\xfe\xd6\x88GO\xe1\xe0\xde=\x99\xeeA\x1b\xbd\xe3H\xc9\xc0\x86\xe8eV\xec\xc3+-vvQ%\x1d\xe4\xc9\xb3\xb3\xe3\xd3\xd3\xf2\x17\xd3\x05b\x0e2\x7f\x93\xbd\xa0\x15\xe6\x08\x9c1\n\xa1\xea\xcd\x98\x83\xbeq\xbe\xdfu%D:\xe9\xfb\x0ez\xf07]\xe8\xeai\x8d\xf0))\x01\xc8\xba\nRb\xf2\xcd\xeb\xdb\x07\xce\xbb9\xccp\xea~)\x08\x9d\x06H\x97^+\x1f\xbf\x9a\x9e\x9c[.E\n:\xc5i\xd6\xac\xe06\xad\xa4\x8a/\xf5/\xbc\x8e\x95L\xf1\x8e\x05//\xb8\xd1/\x8d\xa8\xcf\x1b\xfd\x96\x8b\xd8q\x8dm\xfe\xd2\x80\x02\xdf\"\xc9\xff\x05\x97\x05\xabg\xb3`\xc3x_\x8a\x17!y\xfe\xc5#\x84\xfa\xd6L\xde\xeb\xf0^\x97A\xffR\xe2\xad\\\x92/\x18\xef_\xb4\xbd&\xcb\x9e\x92\xbe\xfeR\xe1\x8aC\x1f\xfeR\x05`\xde\xfc\xf7\xe5\xe6\x8f\xaa\x88\xaf\xad\xe9\xf7u\xf1]u\xf7\xbdW\x11\xb1\x8b/RH)\xc6*\xcb\x94\xa4||\xe9\xd5G\xfd\xfd\x8eb\xfdeQR\xd3A8\xb1[NO\x10\x90\xcb\xb8\xa1\x82w\xab\xd2\xa6\xfa\\9\xabj62\xbb\x18\x0d\xc8\x04e\x05e\xd0\xea\xd8\x04\x8d\xbf\xaa\x88\xb54\xc1&R t\xaf\xbfA\x0f\xfe\xda\x80\x89\xba\xba&\xf43\xfc[\x1a\x16+JP%^p\xdd\xc8i:eU\xd4\x05\x05P\xc3\xa0\x992~\xe2?\x06Lc\x9e\xa7\xc5\x199|\xb6\x1f\xfa\x9c\x88\x92 \x7f\x02\\N\xae\x03\xae\x8aM\xac4'\xec\xbbNhc\xf3&\xd4\x0b\xa6Z\xcc\xe2\x95\xadPh *\x1b @\x96\x87YP\xed#2\xcb\xdd!\xf5\x14+\xe6\x18#\xc1*\x9c\xd1\xb0.\x86\xe0p\xberD\xc0\xc7r]\x0ex\xfc[\x0f\x8f\xad\xb6r\xe2\x18\xa8\xabR\x94/\x14-\xca\x16ij\x0fB>Ht7/phz\xf4\xd5y)ZOSLQ#B\x96\x89\x8a\xc7\xe5E\xec{\xab:q\xber|p\xfexp\xe8\xe0\xd7\xd4FEL\x87<\x96\x83\x18\xdc\xa2\xf2\xe1\x8b~.\xe3)\xba\xd5\xd2\x97\xe1\xf4\xc7du\xac\x18\x1d\xcd6\x91\xdcl\x16\x85\xe24K\x1b\xa1O\xd4\xb0\x81\"\x97\xe2\xb7`\xbb\x14\xc2\xa5\x8aQ\x9e\x8f\x14e\xf8\x18\x02x\xa2\"\x84>\x86\xc0\x9ef\x1d\xfdO\xa6\x81\xc9\x83q\xba=\x17\x086\xdd\x9e7\x8c\x8eB\x93\nQ\x02\xbd&V>\x97\xaa\xc9\x96\xc89H\x11\x0cH\x1d\xf5i\xdc$\xae\xcb\x0eL\xe1\x1c\x85\x82\x90\xd4\xba\xd1\x9c\x93\xd5\xc3\xac\xa2Uu\xf8\x18\"x\x02E\xd6\xf9\xa8Y\\\x9c\xc1\x04\xb2id\x11\x17\x1d9\x16B\xb5\x19\xe1\xf1tF\xd1\x08f\x06\xf1\xd5z\\\xbe\x9c\xc6jf\xe2:zI\xc0\x88\xcb\xd2E\xacNN\xeb2\x86ya[6\xadXW@g_\xf5\x8bHU\xd3\xa2\xa3\xb4\xbe\x9c\x16u\xcem+Z\n\x96T\xdd\x9e\x0dm\xcf\xa6dB\xda\xb4\x1b\x1e0\x04\xf1t\xd3\xa0\xcc\xc7\xd39\xed\xc8\xdc\x12K\xcc\xf8\xb6\x11L;l,\xa1\x82f\x95-\x16\xc8\xe7\xb8\xc09\xf8\x87\x0f\xb0./\\i?\x99\xfaQ\x9f\\CD\xb7R@D\x97U\xc4\x16O\x9a\xf4\xf7\xb9\"\xb0\xd2X\xee\x9e\xcb\xa4\x8a\xb8\x1a\x90=\xc0\xabEx\x92O1\x83\xa2\x162*V\xd2E]V\xd6\xaf=$\x07\x1c\xa8VB+\\)\xe3\x03~]\xe9\xfe\xf8\xf5\xcf\xa5\xf5Y c\xc3\xbe!\xdf\xbbmC\x94\xf0\xcf\xc4\x9f\xbcM)\xff3\xfa\xcb\x17\xd8G4LL\x93+\x0b\xb14\x922\xfc\xc3\xd7\xb1tR\x999\x13\xeat,}+\x18\xfeQ\x9a\xc2\x87\x0f\x107H\xff @\xfc\xaa\x8c\xe8\x16\xc1R>x\x04\xd8\xa2\x03\xf0G\xd1\x90+\xe8\xc1m\x87\x05T\x18\xa1y\x99\xe8\x02\x91\xa2\xd4\x9f@\x83\xe4IU\x99\xce9\xe2(\xa1x[H3\xf5\x05\xb8(\xed\x173\xb6\xc4:\xb5t\x0d\x13\xb8\xe0\x8d\\\xd2\x16a\x9bD\x17E\xedz\x9d\x13\x98\xc0u\xfd\xf5MmR\xdad\nL\xe4\xfdL\x0d\x11\x17\xcf8\n\xafJ\xb4\xa0<\x90z\x1b\x1a\xb9\x06:\xfc\xd0X\x8bA9?\x13\x1c\xa5\x84\xa7\x1a\xdc\x92sN\xb1\x08\xae\xe0\xe77\x1c\x81\x8f\xe8\xbf\x89\xfc>\x86\x1b\x85\xb0\xf4\xca\xf34t\xe2\x0d\x97YM\x99@P_\xac\xdc5\xabu\xbd\xa2\xaeW\xd45\x93]\x17\xb4\x82\xa9\xae\x15q\xc2\x0c\x7f>n\xedu\xad-D\x135+^\xef\xc23\x13\x01)\xca\x90R\xa6\xba\x8e\x15\xb6[ B\xa9.\xbe<\xd2\x7f\x8c\xb5\xba>t%T\x1c\xbc*WY\x903\xf0\x8d]\xa9\x13[<\nso\xe8*\x8b\x0f7\x83M\xb2\xe1\x18\xc9\xdf\xdcH\x17\x96\x95\xd7\xb5[K\x7fx\x08\xffb\x1bE/\xd3\xb71Et\x9e\xbb\xb2\x19\xa3|\x8c\xe0\xe7\x95\x17M\xad\xfa\x8d\xe4A>\xb8\xaf\xb8\xd2\xbc\xe7\x16@H\x7f\x15\n\xed\xbf;\x1eyD\x17\xdf\x04b\xfc\xbb#\x8e\x92\x14\xf1~U4\xac:+\x0d\xe1U\xc1\xfd\x1a\x88`\x87\x85\xf2A.\x89[`=\x8eF{/\xe9?\xdf\"E\x93\xb5\xf2p\xa4\x13\x901g\xa2\xa8\xb1\xc9\x11\x1c\x15\x83\xc1\x8f\x9f*\x02\xee\xdd(xQ\x93\xdcT\xbd\xf6J\xbd\x8a\xb1\n\xad\xb5\x18D!\x9dJ\xd2\xd1*\xe9+\x99\xe5\x98v\x1e\x8dw\xfd\x91\x87^\xb0\xefiA\n\xca.\xff\xba)\x0c\xfaB_w\x06\x84e\xc7\x88q\x03\xf9\xcb\xd3\x10\xf0X\x9c\xef\xfa\xf0\x12\xfb\x92\xb2\xe6Kx\x8a\x12\xe8\xcb~\xdf\x03\xd9\x0e\x1e\xc0\xdeL_\x9e{\x9c\xd4!L\xcd\x98\xfbR\xdc\x7f+:\xe0J\x7f\xf9\xb3O\xa6\xe81<\xc3\x81\xd5>\xf6\xfb\x06Z\xbcG\xe7\xd5'\x16\xc3\xf7c^\xed1<\xf34*\xcb\xc7Pi\x89\xb2\x10\xead\x9a\xaf\x95\xb8\xfb\xf0\xf0\xfe\xdd\x07fM\x8ck\xfc\x87\xf7\xcd\xdff\x18f\xdc\xf8\x89\x83\xf9\x81\xa5\xda\x867\xf9\xd0\xfcm\x0e\x13xP\xbd\x13'\x1f\x8ez\x0f\x0e\xcc\xdf\xb8n9:\xb0\xb4\x8a\x91\xf1\xfa\x16]s\x89~\xc97q\xbf\xbfo.\xc0\x05\xa1\xfd\xe9O\xefn\x0e\x86\xfdw7\x0fN\xce-\xe5.\xb1\xdc\xbb\x9b\x83\x93w\xdb\xc3\xe1\xf0\xe0\xdd\xf6\xbb\xef\x86'\xfc\xdf\xfb\xa3\xf3\xfd\xa5\xb9\xd2\x855\x8f\n\x7f\x92+\x96.\xa2\xe4z\x0c\xceK\xf5'Em\x8c\x19\x9bgp\x1d\xceY\na\x9c\xb3%K3\xc8\x13\xd8\xa4\xc9\x8ceY\x83b\xed\xc4I\xde\xbf\x0c\xb2p\xe6\x8c\xc19\x8d\"\xb6\x0c\"\xd1*\x17\x1dn\x1e\x0e\xc1\x8d\x93\x1c\x02\xc0R\x80h\xb4I\xc28\xf7\x9a\x9a\x0d\xe3\xab \n\xe7}l \x9b\xa6\x17\xd4\xb49\xf1\x9d!\x9d\n\x08\xc55\x82>\xcc\xcc\x9f\xb9\x8e\xfac\x90\xaf\x06\x8b(\xb1\xe5\xae\xe4:\x01\x19\xb5\x07\x8b4Y\x1f\x0bo\x1a\xcd\x9dX>\xca\xad\xf8\xcc|<\x00*\xc6\xfe\xeb ^\n/\xdc\x8b)3\xdaE\xed\xad\x1f[o\xd4A\xd5\x1e\xaeB\x85\xa2I|z\xfe\x18b\x0c\xc4\x9eR\x84X\n]n1hI?\xe5\x9d\xc6\xf6\xbeql\xc5\xb0\n\x89\xc2\x0e\x07\xa9\xe1\x00P}\x93\x02y!\xef\x82<\xf8\x89\xb98\xd5\x03\xf4\xfbC\xceON=)\xf4\xe0\xd8\xa5\x13Su\xe6r\xe9s\xc9\xd6S6@\xca \xeb\x15N;;\xcd\xfe\x99}\xdf\xd5\xb6P\xac\x06\xda\x0e\x1f\xaf:\x0d}\xe1D-\x05\xef\x84\xae\xa9\xb9\xa4jk\xee[I\xaf\xe7y\x1c\xb5\xee\xdd;xt\x9f8\xc7\x93 \xdc\xbb\x7f8z\x84R\x0b\xaf\x08G\xfc\xc5\xc1\x10\xe3\xa2\xdc\xbf{ot\x00\xe24\xad\xde\x96G\x01\xce\xb8\xbc\xea\xba\xa3\xe1\xc1!\xdc\xe1\xbb\xf7\xe4 \x8c\x86(\xc5\x88w1\xffq\xff\xde\xbd\xc3\xfb(X\x89*9\x17\xa0\xb8r0\x06\xf5\xe6\x0b\xc2\xd2K\xfbj\x8a\xf6\x10\x13\x9a\x8f\xe4\xe4#O\x9el\x00\x05\xfa\xbd\xa1\xa78\xd7{\xa0\x0e}\n\xa3!\xdc\x01\\\x9e\x0f\xb4\x1dB\xa0\xa1\xb5\xff\x00b\xe5\x18\x1d*\xf2&\x0c!\xcd\x01\xcf\x02\x05\xb4\xed\x08l\xaf\x1aQM\xcd\xa5\x07\x07\x07\xd0\x83\x07\xf7\xe0\x1bp\x19<\x81\x83\xfb\x1e\xf4\xc1u\x87\x18\xcd\x0c7\xfb\xden=\xbf\xb1\xdd<\x90\xcf\x95\xb8\xfd`I\x89\x82\xb8\x80\x98 Gp\xe22\xd8\x879\x06\x95\x03\xbe\xae\xc2G\x81\xde\xe7\xdec\xdc\x8fk\xf8\x06\x16\xf8\xf91G\xe4 D\x1e\xae6\x95\xban\x06\xbb\x13\x97\xe3\xbe{\x8d~3\xf0\x0d\xf0*._\x99\x8d\xb7\xdb\xc4\x7f\xb4\xc3\x98\x86\xdaz\xce\x18L\x075\xf7a\xe9\xc3-9\xe2\x98\x8c\x9a\xf2\xb9\xd0I\xb6\xb5\xd4\xb5\xf9\x16\xbe|8\xbf\xba\xb2\x7f>\xae\x1b\xc8\xe4\x83\xfb\"(\x85\xeeA\xbd\xf6f\x82\x82\xd0\xf3\xe1\xc4\xbdF<\x86\xa7\xc0'xc\xe8\xea\x86\xf0\x9d\xca\xf1\x89\xfe\x11\xb3\x03_J\x0b\xd1u\xaf\x87\xa1\xa7n\xba\xfa\xfcA\x81\xfb/\xdd\xcb\xddp\xfc\xf4sq\xdc\x87\x0b\x9fC\x9b\xb8>QMr!\x1f\x04\xccK\xe9\xc3\xf5\x0c]\xb6\xa4\xb0\x96#\n\xa3\xa8$\x84\x83U\xc9{\xe1\x92c\\\xe0\x11tN\x83s\x8e\x9e\x02\xd5\xde\x13j\xdd\xb85\xaf\xa0R\xc7)\x06{\x99\xc0{\xd5g\xa2\xd5^{\x84\xd9\x97\xed\xa8\xc5\x91)k\x19\xdcS\x91\x81\xfc\x16\x9e\x88,\xe6\xbc\xd6m\x837\xa8h\xba\x0fy\x81\x1a1G\x0d\xf7\x02c\x82pBn\x02\xda\x98C\x12U\xe4\x84\xfe\x82\x96rk\x1a\x9f\xb5o\x10\xa6\xc7\xd2\xea\xe2\xf8{\xbd\x18\xa1\xb8\xde\xef-P\xda3\xfbb\xc9\x07g\xc6IK\xec\xa3\x8e\x1a=\x96\xc8\xcc\xd1q\xce\x919\x14\xc8<\xe7\x0b\x17j\xc8<\xc70(\xdec\x98\x0bd\xe68\xb8\x81>\x87<\xa9\xe8,\xfd\x02\x04^\xb9K.\xf3\xc2\x1f98\x0e=O8\x15\x9c\xb8\xc7\x0dF(O\xf9\xb4\x13OAj\xafW\x97\xf0\xf4\xe7c\xaf\x17\xf3R\xf5\x84S\xd0\x86\xc7\xef\x9b\x84\xa4\xea\x9b\xadU\x17\xbebi\x16&\xf1\x18\x1c4\xe6X\xb4\xd0\xed,;0\xe5\xb2\x96\x0f] \x1a\xc33;\x9b%\x1f\xb01\xbc4O\xd5b\xb4\x10\xed\xfeh\xfe,\xdb<5\x7f\x16.\xf6\xe3\x8e\x12\xb1\\\xd8\xee2\xb4V\xebv\x90\xb3,\xa7\x98|\xceM\xdc\xef;\xd0#\xd2iJ\x99-\x9f\x8f\x16\x02n\x9b\xcf\xdb8\xa4\x19w\x1b\xdfg\xcdh\xa9\xcd\xe8GW\xe6\xa6\xb9[\xb9k\xf8i\xf3\xab\x83\xac\x0fZ\xbeD\x94n\xac\xa6Y\xf9\x88qn\xeb\x8d\x15\xc1nP,g\x14\x02\xd3\xd5c}$\x15\xffC\xdd\xe3\xcf\x90\xe6\x86\xffy8\xb2d\xbb\xe9\x14\xdfC\xef\xbc<\x1f\xe9\"\xd8\xb6\xabb\xbe\xa6\x0c%\xe5\xb9\xf8\x95\xe6\xc9\x91\xaak\xf3\x16K\xab\x88\xf58i\xeb\xec\xc56\x8a:v%\"\x85vjR;1\xde\xad\xf5\x1dC\x89u\xda\xcb|@\x84 \x0d\xf8\xf2\x16z\xec>|\xf4\x88+\xb7\x03\"Kd\xdd\x97\xde\xc9@q\xaa\xba%\xf3.\xf7\xaa^+\x91,m\x8a5\xd2\x12\x99J%\xb1\xa9e\xf0\x81\x96\xb0\x87>\xd4l\xf8x\x84\x81G\x89w\x1cbzxC\xd8\x99\x18\xf2\x8a\x07\x86L\x90\xa19M1zC\x0c\x853D\xe5\xc89\xa8\xb7\x8cqE\xde\xf5\xf6+\xc29\xd3\x0ckU;\x8ct\x01\x1d\xb1\xc3\xca\x888\xac;1\xe6\xa3\xd1q \x1c\xac\x83\x9b?\xb3[\x14v0\x85\xa9zch:\xd2\xcdW\xa5\xaf\x99\x0c\xf5\x19I\xc9 \x13PV\x1bQ\xd61J\xa4\n3\x8c,\n\xbd\x9e1\x833zLJ\xa9{\xe5\xa3\xc9\x9eMg\xc5\xfd\xff-\xfaQ\x0fm\xc6\xc55\x17\xaf\xd5\x81\xa7)5\xc6\x1a\xed\xd7p\x04\xee\x02\xcb\x16gTk!D\xa9wk!\x8c\x8eEY\xfa\x8c\xc7\x94s\xf3\xed\xe1\x85\xe7\x83\xe5b\xf1\x86k\xd6n\xe0\xc3\xdc\xa3\xb0\xd3\xd39\x1e\xb4\xf3\xffI\x16[a\x1cTr\xe0\x9c\xf2\xff}X\x9d\x17\xafV\x16\xec\x87\x02a\x82\x02\x0f\x8a\x89\xe3\xf9\x97\xcc'6\x083\xfc\x9f\x83e\xab\x8by9Q\x90\xb8\xba[CJ\x19&\xb2\x1ecgw\x02\xa1\x8f9m\xf4IWYld\xf8\n\x030atO\x89\x94\xcdA>\xebpB\x95/)gTKm.)\xe5\xe9\x96a\x94\x8bE\x10e\xcc`\x8a\xa4\x06\x05>6\xe7B\xc9\xbe\x0b\xe30g$\xb1\xd0\xc1s\xbd\xbd9[\x04\xdb(ol\xc9q,@\xf3\xd1\xcc\xce\xeb\x84\xb2\x16sX\xb4l\xa7\x97\xbe\xc6\x0dA\xdef\"\x91\xc8\xb3\x1c\x7f\x1eA\xe8\x06(\x9b\xa8\x01\x046\xea\xc0I\xa4\xe1\x16F\xea\x06x\xb5\xc2\x90wW\x8c8qI\xe3\xe3\x9d\xf1\xbf\xba\x08\x92R0\x83\x9e\xb9Of\xb22\n\xa3/\x86\xc2\xb2\xd7\xe4c\xa9\xde\x1c)U<2W\xdc\xd24\x1bF\x84\xf0\xf2\xfb\xa2\x04\xe6`o&\xd6O\x0e\xfa\xeb`\xa3\xe5\x92\\\x07\x9b\x1a\xdb+\x9d\x85M\xcfKV\xcb\xe2\xb8%\xed\xf5<\x99\x035w\xd94\xe5\x05-\xfe*\xd5d\xa8\xa0q{\xcd\x81\xbfy\xbd\xae,\xf9O\xcba,\x99\xd7Y\xb6\xa1 \x97\xbfR\x1a\xd4\xda\xea\xef5\xeb*fb-\x9fn!0\xe5#\xc6\xee\x96\x82.\xe5\x82\xde\xc5\xec\x1ar\xb7\x80(\x97S\x8e\xcb0\x0e\xd2[\xc7\xf3\x8a\xd7\xcee\x90\xb1\xfbw[-\x07V\xa5\xe8\xde]O$M\xed$\xce^iY)\xcdA\xdd\x0f, \xcf\x0f\x87\xe6\x84\xe7\xf7;\x05\xf47\x1c\xc8(\xde3\x01\"\x9d1\x14\x19\x0bb\x91\xb1 uC7\xf6\xd0\xc2\xaa\xc4O_$ \xc6P\xacB\x17\x8e\xd1\xbeV\xb8\xe6 un\x81*}@\x9f6p\xc9 \x84\xbe\x8c\xd7o\x14\xc7`\xf0\x84\xe6\x81\xf0\xe0)\xad\x1a\xaf.j\xa5\x9eN\x14\xd4\x90\x13\xf4n\xc8p\xa5%\xfe5E\x84\x1f\xd57\xf3n\xdb\x86YfL\xb9\x16\xe0\x03\x84m2\x92\xde\xc0^C\xc3\x16\xed\nt2\x9b\x9bQ\xd0\xaa\xaf\xc8\x95-.\xfb\xf9\xb0?\xfd\x89\x02\xf2\xbd\xeb\x7f\xf5o\x7f\xbc\xf3\xf57\xbd\xc1\xbb\x9f.\xfe\xcf\x87\xff>\xdf\x0f\xa5m\xc5\x12\x88L\xfaw\xccVA\x1a\xccrtD\x81\x15\x0b\xe6,\x85E\xc8\xa29\xc4\xc1\x9a\x99\"h(\xf2_\xb2\xd2\x94\xd1\xda2\xe7\x8ef\x87\xb6iW\xf5msg\xa9\xb93\xc9 \xcc\xd4/f7\xba\x19\xc3F$Ak\x88I\x7fK\xbbqWL\xd0\xde\x16\x7f\xe6I\xcc\xc6\xba\x8d\xca\xe0\x10\xa8?\"6\xbb\xd9\xb0\x0b5Rk\x7fkH'%\x06\xbc\x1a\x849\x85\x88\xa7s\xf9)%/\xa5\xb7y\x92\x9e\xef`D\xab\x8f\x13\xe3\x97u\xda\xca\xc4\xbc\x95\xe8\x9f\xb8\x0e6\xa8\xf6\xfb\xe50\x81\x89\x0c>z\x12\xccV\xed\x81\xb1Us\xc1f\xc3\xe29%\xbb\xa9\x8f\x98n`\xa3G\xb5.\xab \x85\xc0\xd0]\x97\xbe\x18:\x98\xb3\xe9\xc8\xe4\x94T\xf4\x88{ \xc4\x93%\xcb5\xa1\xe4E\xb0f\x99\xcb\xbcz\xff\x9d\xe7:\xcd\x1b:\xef\xb4G\xa1\x9d\x9e\xb1\xc1e2\xbf}\x9b\xb1\xb9\x12\x1e_\xa5\xc9:\xcc\xd8 exC\xbaB\x9c\x9eE)\x0b\xe6\xb7\xc0\xffuL\x87jE\x8b\x18\x90\xad\xd3\x00\x83f[\x1e\xbb\x96\x83j\x0f\x02\x0e8\x84$\x8e\x92`\xde\x05\x05\xf8\xc3\xc5\xa6\x94e\xdb(\xb7Y\xe4\xb1I\xc6W\xa0k\x9b\xb1\xcb\x06X\xa1\xb3\x11\xbc\xdb^n\x9bI'_\xab\xef\xc2\x88\xbdFva\xa6R1\xca?&\xe7$I\x0f\x06|w\x9feZ\xb2c\x12\x97:\x8d0k\x826\x94\x9dj9\xef\xabn\xfdP\x99Q\x91b\xd8-\xa5\xe9l\x98A\xc6\x08t\xf5\xaa\x18\x82B\xa4j\xec4\x95\xa8)K\x05\xe2\xa9\x0e\xeb2\xdc\xd1E\x18\x87\xf9\xb7\xc9\xfc\xb6\x93P\xcf\xd7\x85\xaa\xf1\xb6N\xe3\x10\x19\x97\x91\xc6\xe9UL\x07\x01\x1e\x14\x0d\xbda7\xd8\x90\x9d\xf3i\x17\xc1.\xa3\x04\xc3\xda|\x1b%\x97\x9a~\x15f\xaf\xe4\xdf/\x17B^\x91\xed\xf3\xa2\x9d\xdb_$\xe9\xfay\x90\xa3\xf3\xf4w\xe2\xef\x8e\xfd\xc8\xe2\x9d\xfb\xa2\xcb\x05\x18\xcc\x15-\xaco_\xffp\xa6\xbd\xea\xd8\xad\\>M\x9d\xea\xd4{P\xa0\x0c\xe0\xf5d\xb9\xb4\xebJ\x07\x1an\xc1\x84\xe3\x8cL'\xeaC\x0d\x1a8\x1c\xf3\xf5v\xa7\xc6\xfa6\x97Uh\xbe\x07.\x1f\xbcXT\x1e\xf9\x87\x0f\xb0\xa7u\xd0\xb0f\x80WH+\xb2\xac`\x15\xdb8\xdbn\xb8\xa8\xcf\xe6\xf0\xad\x9c\x0d\xaf\xd9\x16\xfc\xada\x95\xecH!s\x94T\xb7\xd0\xe6\xe2H7(\x90Lf\x9ci\xbb\xce,\x89s\x16\xe7}\x1a\"\x1e\x1a\x9a\xb0LE\xc6\x11u\xb3Z]\x1f\x9c\x9c\xdd\xe4\xfb\x9b(\x08\xe3\xc7\\\x8c\xcfX>y\xfb\xe6\xbb\xfeCG\x05\x97-\xb0H\x86\x8cRo\x06\xbc\x95.\xdd\x18\xaayx\xd1\xf5\xd3\x91@\x8d\xa6qz\xc1f\x13\x85\xb3\x80S\xb6\xfd\x9b\xfe\xf5\xf5u\x9f\xa3x\x7f\x9bFda\x9bWgm\x94`\n\xec \nxI4\xa5\x95\xbf\xca\xeb9!\x8521\xef/\xf2\x1b[@j\xbdPy\x11\x0db\x90\xc8\x04P.\xd6\xa5=\x0dz\xad\xcd\xb6\xe2v\xa7\x9e$\x954`\xe1,\xd9r\x8d1\xc9QdS\xe4\x17x5\x082\xe0\x8bnC\xc8\x1d\xc6\xcc\xb1\xadj\x9d\x85BP-\x91\x97\x0e[\xac\xf3\xd8\x1a%8\x92;\xcfq\xd4\xbeO\xa5\xe5\x17X\xc7g\xebz\x83|\xc5bwk2D\x8b\xe1\xe6D\xfeZh\xd2m \x8ak\x05\x06\xc1Q\xda\xfb\xd85i\x88n^\x98\xf74Kx^\xb1\x84OQ\x956\\yq\xf3i#\xeb\x95\xda\x8b\xddU\x0b*+\xa6/D\xa7\x95\xfb\x0c\xb4\xe7\x00\xbe#\xda\x97\x91\xddB\xd1uQ\x8fj,\n \xae\x15\x9dt\xb4\xe7#\x94\xa8\xbah@\xd5\x9f\xb3$\xfe\x9c\xb6\xfft\xf6\xf2\x05\xf9qX\xa9W\xe9\xbdMY\x98Y-\x18\xf2\xcc\xc5U'\x80\x7f\xff\xe8\xa1\xeaP_\x7f\xa4\x15\xba\xb5\xc4x\xe6\x0f\x06\xf5\xddhK,\xab\xeb\x0d\x92\xd06%\xb7\x85m*S\xed\xccR6gq\x1e\x06QFn\xdf\xc5o\xaeF \xf9\x00\x8a\x00\xb7\xe2\x05\xa1X\xe22\xf9FE\xfe[\xb3|\x95\xcc\xb11\xfaS\xbe'\x87\x19\x86\x7f\xf8t*\xaa\x1cx4I\x18\xef\x1cC\xe9\x9d_\xb57\x18\xf6P\x13\x0ci\x96\xca`i^~\xc3\xec\xf3\xd2o\x19\x98\xb3\xf2\xceI\xd6a\xee\xf8\xb0W,NE\x98\xb2/Vn_\xacv\xd2W\x98;\xf3\xe4\xedfc\xcf\x04\x00\x05\x1a\xdc*\x8f\x0ftF\xef\x8f\xb8\xbcit\xe7\xfb\xe8\xe6r0r\xe2\xc5O\xe7?N\xde\xa8\xe8\x87k\xe9\xf8\x84\x7f\xa8\xc2\xe2\x87\x96\xc5)e\x0b\x96\xa6( \xd0[\x17\xdb)BRj\x1d|\x7f\xf2\xecy\xed\x0b]\xc7\xb7\xc0<\xaa\xdex\xd12\x8a\x92k6G\xb6\xf0\x1f'o I\x81\xb7\x06)\xfb\xdb\x96eyfB\x08\"rR\x83w\xe3nV\x99E\x07\xab\x8c \x83MV{L\xb1!/\xdf\xddq\x0cV\xc3F3B\xabxP\xbam8i\xbam\xc8\x9f\x94.\xdd\x93\x05]\xcb&\xd2\xc3l\"\xd0V\x1d\x0f\xf7\x04\xf3\x9b8\xc6\x06\xec\xcc3\x97\x16P\x83[\x10\xd7\x91\x0d\xaf\x13\x83\xf4 \x16S[W\xeb\xf6\xa6}_\x93\x86\x0d\x951\xf4\xd3\xa3w\xf1\xfe.\xbbY\xdb\xacq\xdb\xd5\xd0b\xa3\x08\x8a\xec\xe2C\xed\xb6\xbf\xfeH\x7f\x07\xb9qc\xa7\xb9A\xd0\xf7*\xf5\xab\x9e\xb5\xf2\xf9\x9c=\x98[\xf9*q\x84\\O\xb8B\xaa\xf3\x04\x1c\xe1\xea#\x95\xe4,\x0f\xf2-'\xb7\x0e\xfd\xe5`jLN\xf3\xe4\xa71\x1c\x0c\x87\xa2t\xf2^\xc5\x8b\xa5\x8fO'\xfc\xab\"\xe7\xe2\xed\x138TU\xe8\x95\xb49\x14\xbfj\x1da\x9118/\xff,\xc7f\xe7\x05\xbe\xce\xb5r\xfc_\x84\x9a\xab\x90\xa9j@\xd5\xd2/4\xf0\xb0\xc1\x82\xe5\xe68rW\"\x16\xa0\x19*tS\xc2\x18\x9c\x8a%\x01\xa7g\x08w\xc6\x1fy@5\x06\x87\x0e\xa7\xa80\xfaX\xcac*|E_\xcd\x8dp\x85m\x0cN\xa1\xd0h\x8dp\x0d\xa3\xf8\xd9*\x00\xf2'Oo[\xcca\xda\xa1\x03o\xdf7eO\x96\xcfG\x98\x05\xe8R\xd7\xd5\xad~odo\xcb\x8c8\xb6l\xc0R\xaa\xe6k#\xfel\xda\x0bM\xfd\x1e\x83\xa3)\x1aT\xa9\x8e\x9ef\xd1\xa8d&\xf4\x10r\xae0\x95\x9dtv:\x95\xfa\xd6\xb9\xe3\x17.P\x85\x1aV\x7f}\x1c\x05\xeb\x0d\x9b\xd7\xbf\x9e\xc6\xf9\xe8\xbe\xb9\x92\xe9\xfdi\x9c\x1f\x1e\x98\x8b\x9b\xde\x7f\x17%\x81\xfd\xc3\xfd\xbb\xe2\x83\xe5z\xea\xba\x93\\\x06\xba\xeb\xc6\x9d;\xc07\xe9/!\xbbn0\xbf\x99\x81\xc0<\x88\xa5\xf4K\x13V\xda0\xe3\x8d7;[\xe9\x8f>\xb4\xc2\x01\xb8\xd5E\x8d\xc4E\xf3@\xebP\x93h-\x11\x9b\xa8\xf8\xbbX\xd9\x11\xa3\x90\x0cB;\x8f\xdd\xd4\xc2\x82$\xcb\"\xf10\xd8L\x99\xe5\x8e\xa1V@$wO\xa0\x07\x8e\x8f\x81\xb1al\xba\x8f\xef\x97\xc6?g\x11\xcbY\xa7\xad\x17EU\x97|\"\x86\xbc\xda\xe5\xf6\x97,\xef\xd4\xb8\xda8\xb9@\xc4F\x82\x8c\x0e\xbb\xf5y\x8e\xcb\xa9R-\x1d\xaf\x82\x9d\x1c\xd0d\x07\x15\x07<77;w\x96\xfb\xca*\x93l\x80\x80\xf2\xea hk_\x08Ym\xb9Y\xe5SI\x96-z\xf4\xacs$\xe7B\xa6\xfc\xe1\xd4\x18\xe3s\xbaqT;\x957\x8c\x11\x9d\";\x98,\xa4u\xd1vkV\xdf\x8f\xba\x83A\xc3 9\xe0)\xb9p\x904\xa32\xfa\xde\x9bM\"\xfaT\xd0\xd5\xe57\x98L\x87\x99\xd8N\xef;\xce\x84\xc5y\x1a\xfe\x16S\xe9\xb6/S\x0eL\x06\xcf\x0fh\x99R\xc51H\x9b\xa1\xc9E\xc8\xb0\x00\x96\xb3\xf8[\xe4\xf3\xcfO~8ys\xc2\xf9%W\xd8}\xa1\x9e\xfb\xe0\xbc|\xf5\xe6\xf4\xe5\x8b3\xfe\xe7\xab\x97g\xf8\xe9\xd5\xdb7\x8ea\x81fZ\x97\xb3(\x89Y\x97\x15\xd7\xa4\xb2\x19ZP\xfc\x86\x15\xbcL\xe6\xb7\xfa)\xdbi\x1cZ\xee\xd8\x1aWP\xa4\xcb\xd7\xc6\xe9\xa9\x97\xf3\xd2\xcb\xf9gNe^9\xf9o\x9a\x14i\x0fc]\xdb\xb0k\x84\x85\xaa1\xae\xaa'\xf6JB\xeb\x18K5D\xd3M\x1a\x94\xcfm\x1a\x8d\x95\x9a\xb2\xc3*\xcf\x07\x9d\xfdi$\xba\xd1\x92\x91\xc5\xa8}\xa1\x1a\x82\x82\xe8\xcb\xe3X\"h5\x9b\xcf\x98R4q\x16N\xd5\xf3\x11\xcc\xd2\xd0\x95\x88c==\x1c\x8e|8\x1c\x1e\xf0\x7f\x0e\xf9?\x0f\xf8?\x0f\x0d\xe82\x1f\xa4l\x1e\xa6\x1d\xd2\x8d\xcb'\\\xa8\xfc.\x97\x9a\x95O\xb7\x96i\x11\xb7\x94\xbb\xa9Pjg\xc9\xdcz@_\x02\xdd\xae\xfb\xd0\x05\xe2\x9a\x95\xa7(\xa1\xa3\xe6\xc6\xcb\xc6;\x80\x1e\x1b|\xafT\xee\x84\xff|M\x06A\x98\xc0\x8c~f\x9b$\xc6{\x9ds\xfe\x1b5\xe7\xae\xab\xaf\xadQ\xcdi-n\x10v@\xb7\xbe \x99\xc3^\x9aml\xa1(\xfc\x9f?\xfe\xf0}\x9eo\xc4<\xec\xa6\x9apG\xcf8\xd0\xb0\xaf\xb9\x14h;\x1e\xb6\xd2\xa7r\x0dB\xc4\xb0\x13\x91\x92\x8f\x02\x9d\x8d\x1br\xc1\xf9Y\x14\xc9m\x13\x9b\xeb\x8a\xa8\xbev\x97\x110#\xa9\xfe0a|qR\xd1\xf8\xdb\xd7?\xa0\xca\x1c\xc2\x11\x84\x03\xed-\x8c\x81\x95\xfdI\xfe\xb3/\xf6\xa3\xcf+\xb5\xf8\xbcH\x93\xa2\xea\xc8\xd0\x0b\xe6\xe9\x97?\xf8257\x19\xbb\x82\xc7\xe0%x;\xe6\xf8\x08\x16\x9d\xa9\xb1|\xd2\xaak\xe8\x0b\x96_'\xe9{i^\x87E\x10Fln\xf2\xfd\x90\x8f\xe8:\x0f\xd7,\xd9v:o\x97\xcf\x17\xeb|\xc3b7Q\xc7Q \x9d\x7fa\xaa\x1d'\x8cg\xd1v\xce\xe8\xf0!)\x9d\xf6p\xc9*\x1c\\\x87\xf9\xea\xb8tND\x15\xd5\x16\xddn\xe46\x96|\xc1\\m\x17\x05\x17!/\x0c>\x00 B;\xf9G\xcb'\xe4\xea\x95\x80:B\x03\x8b\xbb\xb4|\xb8$\xc9+\xc5sWsoO\xb4C\xb7#:\x8a\x1b\xeb/mR\xa9\x99\xd8\"\xf9\x1cl\x92\xe8v\x11F\x91\xc9+X\xfd\xe5:[y\xd1_\xbfk\x90\xb1h\x01G\xf4\xdfXS\xb1>\xeb\xa2l\xec>\x1a\x9a\xae\xaf\xf0\xf7\x0f\xcd\x17\x92\x1e>\xb2\xdc<*\xef\n\x85!\xe6\x84\xb0\xdc\n\x1e2\x8f!)\xbfUQ\x02\xc6\xb5\x9c\xf7\x9f9\xbf\xc3\x87\xd5y$j\x1e\xf5\xf9\xd5!\xeb2\x0df\xef\x19\x9fHg\xd3\x00f\x84\x9b\x9e\xd7e*\x83\x0d+\x8c\xe7\xe1\x8c\x95Zo\xe7\xab\xd4\x01f\x96\xa3\xe4s]zJ\xd9\x86\x05\xad10@\xeb\xa5\xdej\x19d\xeb\xf7\xd2\x9e\x079+Y\xcdN\xcf^\x92\xe1\xac\\\xd6\x1c\x8dg\xce\xa2p\xcd\x15\xb31\xde\x0e\xae}\x97\xc1n\xf6\x0cR-}K\xc7\x90\x8a\xe0\x13\xb6\"\x7fA]\xfde\x1c\xdd\x8e\x8d9\x063\x96\x86A\x14\xfe\xc2\xf8\\vX\xad\xa0v{U>\x86\xbd\xc8\xde\x87\x9b\x17\xdb(\xca,c@p\xe6\x05\xbe\x0f\xe2y\x84\x91Q*V\xf3J\xa3\xba\xc6\x0eL\x04~Q\xf1\xc82\x1f\"\x9f\x8buE\x88\x04\xd3l\xa4%\xdb\xc0R\xd1\xdbZv\xa0{\x82F\x1eV\x89\xb8Xwe\xba !\xdd\x82\xaft\x7f\x0e\xbe\xb6Tq\xe36\xd6RW\xc2\xaf\x9a\x04\xfdP\xb9LQ\x06\xb4\x15\xa7\x93|D[\x01\x0c\xe8\xfbf\xb8\xe2\xcd\x9f+\xf4\x8fm\x81u\xb0{\x9c_\xa1\x84U\x8f\x97A\xefe \x80\xea\x87t\x10f\xe2V\xc1\x95\xa7\x0d\xff\x08\xa6s\x17#\xc4\xc3\xb8:\x07\x8f#\xfb\x84\xa3\xfd\xdc\xcd\xdc\xab\xd2\xa7s\x18\xf3\x9a\xb1^F\xb8x\\y\x9eA\xa5\xe2\x9b\xbd\xf6\xd1~n\xb2\xe0\xe0\x96\x15\xcc\xf0J\x0d\xd1\x10\xff\x8f\x97-\xdf7\x8a<\x0f\x8f\x07\"\xcb\xd6\xdaU\xdc\xdbJ\xda3\x13t\x808|\x98\xc1\x11\xdc\x0e\xb2$\xcd\xdd\x19\xdf\xe0. \x9a\x94\xa9\xf3\x92\xbc\xdd.\xe1 \xac\x95\xb7[\xafw\xd9\xa4\x7f_\xc0\x04\xd6\xd3K\x8b\xc1\x0b\xdd\xbd\n\x80\x9d^`&\x07wY\xbd9\xef^yp\x04K\x99S\x86\xb9\xbc\xa8\x0f FP\xf3Z\xd0\x96\xcf\xb3V5\x86\x1e\xb8\\8p\x06|\xe7/T\x9e\xd2\x0b\x95\x9b\xb4\xb9Q\x03\xd1\xaa\xbd\x91\xfb_&CfQ\xa0\x91\x99\xa9s\xfd:\xe1\x0b\x80n\xe5\xa6\x83 \xcb\xc2e\xec\xfe\xfd#606\xc6\xcdQ\x01\x99\x02\x89\x07x\x8aS\xdc\xf7-\xbd\xd7\xc8W!T\x05\x05\x810\xba\xd1\x9c\x88\xfa\xab\x00\x03\xa0_2\x08\xd4\xe4j9E\xaeD\xdc\x1b\x0do\x82\x81bjp\x04[\xed\xd7X\xffV_\x89\x19\n\xc4u\xe2\x11\x0c\xea\xcc\x01\x8e\xcc\xaf\xc7\xb05\xbc\xae\xf7\xb5\xb0\xf7%\xf9\x14u\xa1~a\xcb\xf2W\xbd\xc1\x8d\xb5A\x11\x18\xea\xa8\xf8s\xac\xa8X\xbd\x1d\xae\xa2\x1b\xb9N\xb1\xb1G\xda\xdfES\x86\x05]\xd9\xdb\xca(\xa5\xbc\xf8\x83N\x8b\xea\x0d\\\x15;K\xb0\x85\x9eU\xcf\x93\x1cy\x8e\xf6\xb3^u\xdd\xd0\xb7.n\xd0 Jop\xa5\xf57\xf5\xd6\x97-\xab]H<\xdaji/\x8be+^\xd6\x91\xad\x04\xd4$\xdc{\xea/4\xa2\x0bo\x93r\xd5\"\xf3U\xa7\xc8\x15\x89h0gi\xe6\x17\x1dY\xb0\xf3m\xfc>N\xaec\xa1k@\xb2A\xf1g\x93&W\xe1\x9c\xcd\x8d\xf8)\xc2\xb1\xe2\x80\x8b\xae\xa6\xb2\xa7\ni\xb7l\xda\"\x8c\x08\xa1\xd1\xa1\x95s\x12\xf9\xces1/\\\xfd\x06\xae*\x80\xba/&o\xd7\xab\xd5\x07z\xedc*\x82*oF!D\xc6\xc2)\xe8\x98\xee.:\xe1\xfd\x0bj]\xbd\xf8s\x8d\x9d\xa2\xff\xc2w\xb4h\xc2\xc0R~9\xe6\x8a?*&\xa8\xba\x07X@\xbc\xe1lF}\x1csE\x9f\xeb\x15\x8e^\xa7>\x9b\x1b\x98@8\xbd\xaeL\x06\x83\xc8\xb8U\x96\x1f{\x18\x0d\xeb\xce\x1d\xc9\xdc\xabw\x1c\x15\x0f?#\x1e~\x06O\xe0V\xe3\xe1g6\xe1\xf6\x18&p;=3\xf0\xefE\x89w\xc7\xd3c\xe2\xdd|\x07N$\xb7\xcd\\\xfe\x1e\xa3\xf8\xde(\x0e\nG0\x97$\x83C\xd6\xca\x87+\x9f\x0bV\x17>,\xab\x8c\xf5cm]\xdec\x07\xe8f\x16\x19\xcc\x9c\xcf\xd0P \x90.\x98\xcf\xff\x9f-Ko_\xa5l\x11\xde\xf0m8r\x0c1\x9e\xc4\xce\xbf/\xf2 \x0c\xe1\x08\x9eA\x0f\xdeW\"\xfc\xe0_\xbf\x8az\xdd\x82\xeb]\xf4nEN\xcd*\x12~Vn#\xb6B\x1c\xa4\x7f\xe0,v\x0c\x07\x06\xa5\x91\x1c(Qi\xa4?ME\x9au\xd29\xdb\xe4\xab1\xdc30\xc1 \x0d\xd6,g\xa9\x18\xc0\x88\x1d\x1a\nEA\x18\xd3j}1]0\xe8\x10L\x05\xda\xbce\xd5\x0ekl\xeeH\xcb\x92\xb1\xffn\xe0N\x7f\x1aL\xcf{\x1e:\xb2N\xffmt\x8e\xf7\xfa,\xbeW 6z\xdf}7\x9d\xfe4}w~\xfe\xcd\xb9gK\\\x03b\x16\xe5\xc2\x94h*m:\x86\xe3\xd4\x0d\xc5Gq\xa5\xda'\xb2\xc5n0!\x85\xbdb\xd6p\x8e\xcd\x97\xa9\x16\xcd\xacZ`/\x1e\xe8[ \x98/\x0c9Z\x15\x1504\x1a\xa5\xab\xae\xc0\xb0$\xdav\x83vF\xa7\xe2\x86;[`=\xfdQ\xc4R\xe4\xf6VB\xb3\x1b`\x08G\xb1\xa88\xa6\x08\x9e@<@\x90n\x0c\xf3\xcdg\x1cA\x0fC\xe7\xef2\xf3`::\x17[3\xf2\xa1/\x02v\x7f\xc6J\x04\xc6\xa0\x14`]\x0ci\xab\xe1\xdd\x8a&HQ\x92\x10\xa3\xc0E\xe8M\xd6\x01tA\xb0Ry\xb9\x0d\x1c\xa9r\xca\xf2\xa2%7\x1b\x89\xe4\x03\xc3\xc7\xd0\xef'm\x8d\x81@\xd0\x90\xa2\x98\xb3i\xd2\x90\xda[>(9LE\x0c\xb6\xc0Cl\xc44\x08\xd3sO\xb28\x9b{\x99\xfet\xb8M-\x1f\xb4\x18\x97\xc1\xe3H\xf2\x86Y\xca\x82\x9c\xa1\x0eg\xd2\xefl\xcf\x95\x08\xe5\xc7\xb7\x8d\xd8b\x91\x9f\x91+y\xe7\x95\xd7\x81\xb6\xc6\x1e\xc9\xd7\x1a\xfcq-\xcc\xbe\xc7\xd5\x87S 4_\x9f\xc6\xb9\xbb\xf5ad\n\xd9`z\xf6\xc2\xecE\xf0\xc2\xcdp\x88\x01b\x1f\x06\xbd\x17\x06\x9a\xcc\xc31\xe3\xab\x8c\xc2\x8c\x8a\x1c\xc8i\xc6P|\xcc\xe8\xd3\x13\xa4\xc7\x8a\xa9\xc1\x91\xda\xc0iv\x8eQ\xf0\xc7\x10N\xb7\xf8g\xeb\xc0\xcc\x18\xa2?\x1cT\xc3\xc6R\xcdm\x08l\xb3\x0f\xe5\xa3\x9b \xec\xa9\x15\xa9\x98\x9a?\xc3\xcc\xf0 \xf6\x84X\x88\x03U{B\xe9\xbd\xd1\x9e\xa0JX4\x96\xe7l\x07{\x02\x8ei\x10.\xe3$e\xba\xe4\xa7dB\xc3G\x1f\x87 \x8d\x0c\x13S\xacl\xbd\x80\xb0D\xbef\xcb\x93\x9b\x8d\xab}\xf10I\xa5n\xae\x085s\x85\xe4\x12\xbc\x83\xba\xe5S~\xc3?eI\x8c\x83=\x11\x9eZ\xc1\xa0\xf8\xe9#f\xb1\xcd\xb1\xf0B\x0e\x06\x17\xea'f\xa5\xc8f\xc1\x86\xbd\n\xf2\x95\xba0\x8b\xa5\x0c\xefy\xf1ml\xab`\xfcR\x1e\xfe\xd6\x90\xd7\xaf\xd5\xad^\xc0c\xbb\xcf\x01]\xd0\xbc\xccXzE\x1e\x9c\xd3syk\xf3\xf2g\xa8f\xfc\x80\xba<]\xbdQ\x17\xed<\xb4\xb6@\x95\x9cv]\x06\xb3\xf7\x14\xc8\xad4`\x98\x98\xa2mV\x07h\x8a\xfd=\xab/I)\x8b*\xe5\x9cJ1-\xb9\xa471<\x81\xf41\xc4\xbd^]\xcb@\xdb\xce4>\xa7e\xc3H\x0bd[\xb7N\x0d\x19VlQ\xb7/S\x16\xbco\x99\xd9\xc2\xcd\xe9\xbe\x88\xaf:\xe3\x7fm8\x14s\x11\x0b\xd3D\xa8\xdfR{E\xabJ\x81\xaaz\x1b\xa2\xa4\xe1\x08\x81R\xc8\x8a\xefF#q\xa8\x1b\x891\x94\xad,.`\x8a\x15\xfb\xa8n\xfc\xf0_n\x88\x89\xbf4jY\xdf\xac\x85\xab\xb2\x01\xd4,\x1a\x18b\x82\x92\xe9\x98\x96\xda(\xa4\xe7\x83<\xf9\xd3\xd9\xcb\x17@9X'\xea\x85k\n\x14\xa3\xe0\"D\x9epAK\xfdg\xce\x9ar\x8f\x84\xa1\xf2[\xe6\x91\x98\xb37\"\xde\x17\x94\xac3\x99\xb0\xced\xfd~\xa3X\x83\xe6\x18\xe4T\xd3\xec\xbc\xc1\xa2\xb8\x97\xd6.\x8e\xf9\xb0\xf1*\xd2g>\xdd\x9cWt\xd0\x08Mf$\xc0\x94\x8f\x98rO\xc5\xac\xb7\x9bg\x92\x0d\x1e\xd9\xac\x93+\xd6\x90o{\x13\xe4\xab1\xdd\x0c\xdc'\xf3\x98\x81\xe0\xb9\x1b\xfb\xc5\x1c\\HK\xae\xd7\x16\x03\xd2\x95\xc8\xf9\xc2\xe7n7\xaf\x18\xf2ADP$i\xa2\x1f\x86B3\xbd\xd0\x8c\x0b\x89.\x89\xa2\x1cJ[\xe7\xcb\x85\x1d2\x11`;\xee\xde\xd0o_r(\x96\x1d\x05\xf3\x86u\x87\x1d\xd6\xbe\xb9\x15\x11}9\xd5X\xa0;kr\x81\xedjF5\xfbEm9\xe0*j\xb2W`\x8f\xb9YDNMm\x08\x15\xb5\xcez\xbd&\xeb'\x07\x8e\x0d\x9e%f\x0d\xc0Q\xc3-f\xc3-\xae\xfau\xde\xbf`>\xff\x87\xed\x1d\x1fm\xd3\xf6u\xd8=\xcd\xc5X\xfd\xc5\xa5\x1c\xc1\x96\xdb\xeciZQ=+\x02\x97\x94:\xb6\x80\n,\x99\xbe\x9bE\x9cR\x08\xb3!\xf1\xf5\x82\xa1\xe7\x94`871tPL=\xd7\x98\xba\xd2\xe1\xf9\xeb\xf2\x9a\xd4\x02 \xf1\xda\x898\xdao\x95vJz\xb9\x90?\xb9bq\xfeC\x98\xe5,F\xfb\xa3\xed\x93\xeb\xac\x93m\xc6\xb6\x1b\x87\xac.\xd6b\xef\xd9m{!lk\x9e\\\xc7m\x05\xdf\xb3\xdb.\xc5f\xab ^2,\x85\x807Of\xdb5\x8b\xf3\x81\xfc\xe3$b\xf8;\xc8\xf3`\xb6\xc2\xda\xae\x93\xc4\xe59u\xad\xa5O\xb1k\x9d\xea\x8c\xbb\xd6+/@\xd7Z\xfazt0A\xc4\x15\xb9;\x16\xaa\x01iO\xb1\x99J\x9b\x80z\x86y[\x8c m\x84\xddV\x12\xa7\n~!R'\x1f\x03\xc9+\xf4\xc3\x12\xc9C\x9e\xadw%r\x80\xc7>\x8c,\x08\xc9 _\x87\xaehH\x02\xb1\x0d\x13\x0d_-\xc8h,i\xc0G{\x8bu\\\xb3\xb5\xa9J6\xe3\xdb\x9c}\n\xbeUju\xc27SO]0\xa7\xdeW1\xb5\n\xeap\x8eT\xc0\x01\x85n`\xd7@I\x99\xbcRD\xd6\x8fd\xad\x8aYJ&\xa8\x19\xff\x8dv\xbe\xb4\x9b\xa0bp \x91F\x90B\xb1Em\xbd\x9a\x01\xac\xc9h\xa8\xb4\xe3\xcfI\x02\xd69\xadW)\xe1\xafQ\xa9\xd63\x94\x1d\x95~\x8d!\xf6\x06\xd9*\\s\xf6\xdd:/\xb9dZ\xc6\xb7%\xeer\x86'\xf2v\xa2%\x06\xdd\x12q'\x90\xadi\x92\xa7\xd9DdH\xab#}!-Ck\x0d\xf6\xa3mo\xbd?C\xee\x17uK\xcb\xac\x82\xd2\xfb\xfa\xb1\x19\xd3\x8c=\x9d\x9ce\x99\x0f\x0e\xff\x831\x87\x1cij\xb56\xa2\xfciv\x12o\xd7\x14\x11\xc3P\xf7\xc3\x07\xdd\xa5\xec\xa3Kq4\x0b\xc8\x89\xe1\x08}\x0b\x12oPD\xb3\x9f@JVR\xfdUb\x04\x94\x9d|\n\x8d`JQ;p\xe12\x11F\xad\xfaQ\x85\xf4(\x1d\xa8Y\xf6F.y1ih\xba\xebU\xda\xd1\xe6\xf1\xb1\xc1,\x89\xb3<\xdd\xce\xd0\xc0=\x99\xe8\xdf\xd0t \x86\xabv \x8e\x8aI\x8d\x0d#3A\xb9\x1d\xea\xb4\x93\xcc#\x0ee\x11\xb6\xaa\x9fh\xf2\xf7\x1a_\x1c\xeb0:)9z\xd7\x8bR\xa2\xc8#Sz!\x07\xcf\xe5K\xed\xb5\xf4\x9b\xb6\xe1\x96!g\x8f\xc4e}\xc8 \x0d\x00\xb3\xc2\x8c\xd58\xb4/\x81[\xc9Bo\xea\xcc\x90\x7fG\xe9\\\xeb`\xe3\x86\xcdc5\xe4\xa4\x91\xf4\xdcz$,\xe9y\x15\xbdE\x80%7\x9f\xc6\xe7\x18W\x9dM\xe3Z\x10\xfc:\xb57\x8c\xca\x90\x87\xa6\xa4\\+\xbaZ\x18\x82G\x15\x83\xa3*2\x1d\x9d\xf3\xb5\xd4\x7f\x8eIX5;\xf0bT6\xb6\n\xae\xc2d\x9b\x8e\xc15\xf4u`\xed\xeb\xa0\xdc\xd7\xc19\x1e3z\x83r\xabx\xc5N\x9a\xd5J#Pg\xe4|\xeb\x9a\xad\x0d\n\xb91&u\xb9\x15\xcf'+:}\xf3\xa5\x13e\xc4\x85\\%\xf2F&Y\xb7\x94\xbf:\x9dF\xe7t\xda\xad\x1f\x91\xceD\xe2\xe8\xe1c\xd8\xc0\x13X\xa8\x067v#\x18o\x11#WL7\x0d\xa7\xe6+.\xf0L\xe7\x0d%\xae0\x97\xe3\xaa\xc1\x12\xb5\xc6\x12\xe1tn\x8b\xef^\xba\x8a\x80W\xde\xec\x12?\x96- \xe3\x13X7\xa9\x1b \xe6\x8a\x0e z'k8\x02>\xa8\x0e>\x83!%\xc0\xce\xd0\xebk\xba\xf4a\xeb\xae\xbcs\xa3\xbb\x99|D\x9clQs[\xbbz \x1fu\xadE\xa76m\xf3\xd7\x8av\x9a\xfb-\x1ex\xdb\x86 \x1f1V\x07O\xbd\x1d\xe1\x17VA\x13Z2\xe9+pk\xbe,)\x9f\xf2\x1a\xd8\x07\xa0\x97Z\xd5J\x18\xd5\\\xfd\xc0H5\xd3)\x17f#\xd5\"\x12$NA\x90\x84\x1dA\x8en\x1ecL\x1e\xcd)\xc1Hd6(R\x1a\xf0\x02\xe7zk\xd3\xd4,\xefg\xe4\x16Q\x8c\xdd/\x06=\x88\x93\x1f\xb7y\x907*\xe6j\xf0\xcc8\xf8\\\x0d^\xe6g\x18\x92\x1e\xcdH\x8f\x06\xc1\x07\x8a\x81V\x0f \xd5@\xc9\xbf\xd1<\xd2\xeb0_\xbd\xc4+R5\xdfI{\xba\xd5L}\xafl]\x8b\x8cg\x0f\x0c!\xf3\x8fC\xec>\x1a\xdd\xab\x10\xa0\x8b\x0b\x96\xfd\x98\xcc\xb7\x11^\xf3\xdf\xad\xcb\xd8\x1d=x\xc0\x17\xd0}t@\xff\x8d\xee\x8b\x9f#\xf1\xff\xa1\xe7\x97\x05[wt\xcf\x1b\xfc\x95\x05\xef\x7f\x0c6]\xfah\x10]}\x99\xc9\xf7p\xe4\xb9U?\x8ePtV\xbd,C^\x0e\xa3\x83\xbb\x95\xf7[j\xea~5Y0\x0d\xfa\xd1\xa8\x1a\xbb\"\xa2\xf2\xd5\xe6g\xf8\xfa^\xd5{d!\xbcG\x0e*\xef\xf1\xdcr\xb0d9_\x91\xf2\xa7y\xc1\xbb\xc2\xec\xe4&gq\x16^F\x95\xcb\x1e\x9c\xedd\x83\xed\"\xcb\x93\xb4\xf2\xe9\x8a,\xca\xa5w\xed\x01d\xab^\x076\xaa)Y\xb8\x88\x8ag\x904\x86%qbx\xaed\xd3V\xd7\xe3\xf2\x98\x97FYg\xc9:\x05\xd6\xc0{\x13(A\xdb\x89\xbf\xa4q\x1bcj\x06\xf9\x88 \x0b?\xe0\x1c\x8e`\xe5.\xc4\xec\x1d\x01\xcf\x8e\xe7a\x0c&\x94}1\xfa\xb6HU\x14\x16\xb37v`8\xf4\xab\x8b Yy\xca\xedAK\xb2\xc1\x9c-\x0c\x83\xf4\xd1?d\xc7m\xb8\xadj\xa8\xee\xa3\x83\xa1\xe7\xaaV\xf1\n\xde\x12o\xbb\xef\x0d1\x96Q\xb1\x963\xb7\xcd\x18\xf1\x00\xf6&\x80\x96\xa5[\x0fs\x7f\xc9\xbb,\x8b\x94\xb1_P\x18\xa4\x17\x9e{\xe5\xf9\xf0\x80\xd6Yc\xff\x1fI~\xdf\xba.\xa6l\xe3\x9f\x8f\x0b\xad\xd0]\x977I\xbb!\xb3\xf4|\x08\x06/NN\x9e\xe3\x01\xba\x0f\x89;u(\x8e\xae\xe3\x83\xb3\n2\xfe\xdf\x92\xe5\xfc\xbf\x8c\xe5\xce\xb9\xdf\x00w\x12\x96n\xb5.j\xeb\x8c>\xf2\xb5x\xc1!\xc6L\xd2\x1a\xcf\x0d^\x1c\xa0`:'\x03\xc4\x1c\x9d\x10\xcc`@\xb0\xb7(\xd2\x7f\\,\xc4\xe1TSP\xe3P\x065\xbeXL\xd99\x8d\xc2\\Zj\x86|U@\xe8\x9b\xbc&\x8c\x0d\x97\x18\xec\x0e\x91\"\xa8-\x02i^\x8b\xe5\xffQ\xdfc\xfa\xbbs\xa2\xf0G\xa3\x87\x96\xc8I\x8dh$\x07\xc6\xae]\xd4\xbe\xf5\x10\xaf\x9d\xf8b1\x82\x1a\x7f\x10\x1c\xab\xc6\x96\x04\xbbz\xe4\xb9N\xb6a\xb3\x90\x95\xd2\x84t\x93\xd8\x10\xf8\x8cb\nj\xe5\x1c?LW(\x84\xf1I3\xa2\xa0}\x8a\x9c\x85PJBHK\\\xcd\xce\xe5\xa9\x1c\x08\x82\xa6\xfb\x90\n\x90T\xe6\x10\xf2\x18\x9a\x86\xe7\x9e\xf2\x1f\x12\x85\x8b\x1c\xf1\x92\x96R7\xe3\xd6T\xf6\xdd\x85\x03Z\xe7\xe1}\xe3\xfas\xf6o\xe6\xba\xc2\xcd\xb3Z-0\xef\xa6\x10\x1a\x86UaBH:w\xab\xef#%\xaf\x18\xa5\x86\xaat\xd0$5DnU\x92\x9b\xe3\xdb\xea\xc8WxxT\x86\x93\xaeR\x00\x1b\\`\xea\x07\x17\xff \xd2\xb1\xae\x1e\x10\x94~\xae\xdbN\xcb\x90\xb2\x04hrojg\xd9\x86\xa3P\x8cr\xe3\xb2A\xd0D\x94+\xe5\x19\x17F\x10\xf0j\xa5\xaa\xd9\x90\x0b\x98Zk\xd6\xc3\xaa<\xd2A\x16\x91|a)\xe8\x9c5 \x94:\x83\xcb\xa7\xa3\xc6\x15Z\x05\xad\x01\xd2\xa4\xc8\xb2W\xf4\xda\xd4b7\xf9B\x1e;4\xcd$F\xe7yT\xf5r\x99\x021\x10\xf1\xa5Y=\xbete\x1c\xc4|\xdb&'WT\x043\xd6\x01\xa0M.\xca%\x00\x18\x9cv\x0d\xb3\x11\xb5\xfe;\x07\x99\x88%\x90\x07\xa2\xb9\x8f\x97\x08\xf6\xf6\xfe\xbb\x9aTF\xfd\xe57(fe!e\\#u>\x84\xb6\xa9\xa3\xdbc)J\xa35\xc4\xeb\x96\x7f\x8d\xb0E\xe7\"$g\xd7\x8b\x9c\xdcE\xd8\xe0\x82S\xbcU\xaf\xe7\x83@r\xa2\xcc~a$\x04\xbc|\x97\xb9)\x8e\x88M\xc3ss*|\xfb\xd2\xa5n\xa4\x8b\\\xe6av\xdbv\xf9\xa0Gg\x80\x92\xbd\x04\xf3\x91]x\x97@\x9b\xec \xe2s \xbeR\xd2s\xeey\"\x11\x03I\xf71_\x93\x99\x1b\xab\x9c8\xc8\xe4D\xfe\x85X\x89\xfd\xc6\xbe,\xee3\x1d0Z>\xff\x88\xd9\x8bD\x0f\xa6\xa9\x9bgi\x80\x10\x1f\xa2f\xcc_\xd4\x91\xc0\x86\x01)YK\xd1\xb7x\xcft/\xb8<\xa1\x14'\xc4H\xbb\xc8\xc5\xa5\x9bt\xcaP9\x9b d7\x0dM\xa8\xd8c\xb8*P\xfb\x0f\xf0\x05$\x94\xaa( \x04D\x8b9\xa3f\xb6\x08\xcc\xf6\x06\x12L\xeeU[\xc9,RQd\x91Wf\x16\xf9fa\x16\x876$uW\xc3\x9b\xce\xf1\xf5\xdd\xa17X\xd4e\x13\x8b\xf9\xe6\x8a\xea\xdcm\x15\x82%\xa5$\xed\xf3\xd6$\x13_\xe2y\x003\xd8\xe6/`\x02\x97\xf5\xd7\xd7\x9c\xbf\xe1!!\xa30;f?\xd4\x13\x98\xc0\x05G\x86\x8b&m\xef\xc6p\x1e%@\xf3\xcaz\xba\x89\xcd\xba\x18\xad\xe7D\xe5\x16\xe1Rx`W\xa5\xf9\x83*\xf4\x85'\x93*\xb8\x1ez\"\xb9U\x95\xca\x83#p/0\x91\x8b\xaen\x1aqm\xc6\xbf\\\xa0j\xea\\\xcc0\xeb\xe2\xe0b&\xa4\xc1K\x9dO a\xc0\xebsK\x1f\xf2\xe9\xf5y\xcd\xca\xc0)\xc0\xca\xe5\xcb\xe9\xa3\xc3\x94O\x04\xd3\x173\xf4\x97,\xf7WA\xe6g,\xf7\xdf\xb3\xdb\xcc\xa7<\x1f\xbe\x98\x8eO\xb7\x0f\x1c\x99\x9e\xce\xe7\xa3\xe9&&\xe0\x16\x82\xbcnZ\xa8\xacu\xb2\xc1 \x8c\xe1\x84\x9c\xcdq\x03\x1c\x1c**L\xa4Em]}\xc3K:{S\xa8uN\xb4e\x16 \xbe\x9e\x9cn\xa1LA\xfa\xd5\xc2\x8d\x0br\x8e\x06\x07\x1a\xae:\xaf\xb3\xab\xec*\x0f\xd1\xc5\x8c\xab\xec\x05\x05\x1frr\xed[\xd5})\x0f\x15z{R+W\x15\x89=\x9f\x82H\xcd\xcb\x8b\xe0d\xe1/\xcc1\xf1\xf6\xb2t\xdc&\x9a\xd1,\x06\xbc\xb5\xfaPjP<&(^W\xcd=dIY\xfap\xed\xf9\x90\x95G\x1a\xe3\xadOe\xf0\xf1|\xd8\xb8b;n(G\xd3\x85\x0f\x89\x9b\x0c\xfe\x03z\x90\x0c\xfe\x8a\xff~\xe7\xc3\x8d\x9c\xf9\x9a\xb3\x90\xb3\xc9J\x98\xa4\xcd\xb0\x16\xa1\x1eTy\xaf\xec#\xe72=O\xb5\xe7\xc3\xfe\xf4\xa7\xa0\xff\xcb\xb0\xff\xe8]\xff\xab\x7f\xfb\xe3\x9d\xaf\xbf\xe9\x0d\xde\xfdt\xf1\x7f>\xfc\xf7\xf9~8\xc8Y\x86\xb9\xd7\xcc\x81Wd\x82\x97\xd9*H\x83Y\xceR\xceW)\xcd\x00,B\x16\xcd!\x0e\xd6\xc6\x9c/\xca\xfa\x94'?$\xd72\xaftyq-sn\xb6\x84t\x9e6\xeb\xd4\x99\xc1\xf1\x11t'$#p\xc5\x98u\xa4\x95\xac\x82\xd6\x10\x93Iv[\x957{[\xfc\x99'1+9\x88\xb5$<\x11\xb7\xa2\xccI\xac\xc0\xa8\xe2\x99\xdf\x1a\xbcF\xc4\x80+i\xc3rS\xb2\xb0\xd6\xb5\x92\xb2C\xbd\xdf\xce\xd9~\x0d\xde}\xa0\xa5\x02\x14\x97sJ\x19\xf2\x13\x0c\xfd\xb1S\xbe\x0c2\x1eQ\xd6bs\x82\x0c\x91\xf9\xbf\x1e\xcd\x14\xbd\xeaL\xddu\xe9\x8bM\x87\xe7>0c\xe86\xadG\xdc\x03q\xee\xb6d\xb9\xe6\x1e\xf7\"X3\xae\xfd\xef\x90!\xaf:\xd7\xa9)\xab\xdcGS\xe6B\xdb\x1e\x19|\x13A]k\x90\xd9\xf8\x95\x04-\xb2 \x0dR\xc6\xe7S\xcd\xdb\xf2,JY0\xbf\x05\xfe\xafc\xba\xcc\\\xc9\xef\xdfi\x80\x06\x7fF(K0\xb5\xd4LM\x81\xec\xd8\x8eY\x93r\x97\xcf6\xdbF\xb6D)x\xff}\xb7\x8c;\xb1\xcb(aZw\x1bO\xa7\xa52\xf8n\x82F\xf1\xf8Z\x15\xb9\x97\xcdT*FW\xa9\xdc\xce?\xf2\x01\xdf\xddg\x99\x96\xac\x96\xdc}:\x8d\xd0\xe0\xc7 \n\xda0\x86\x8cvCP\x04\x9f1\x8cE\x9fQ\x91\x8f\x98\x03\xecm\xce~\xa0\x0b\xbb\x0d3\xc8\x18\x81\xae^\xd5C\x15\xfc\x12'\xd4i*QS| \xc4S\x1d\xd6G\xd54\xdf\xad\xa7E \x0f/JY\x05\xe9\"UC\x12\xa0\xd0\x9c\xdd\x81yZ\x0eE\x91\xd9\xdc\xa0\xa6\xcbG\xf9\x05\x16\x89\x8e\xbe\x8d\x92K\xcd%\xbf\x9a\xecXo\x9f\x17\xed\xdc\xbeL~\xcd\xfb\x90\xe1g:\xf6#\x8bw\xeeK\xcf\x7f\xce\xfb\xab$@\xef\xd8\xad\\>u\xc1\xa2I\x86\xd0z\xd7\xd2mC)\x87\xd4\xba\xd2\x81\x86[\xe8\xf7\xc9\x04\\\xca\xec\xc0:4\xc4\"\xb7\xb9;5\xd6\xb79\xbdB{\x00\x03\x90&\xf1\xf2\xc8?|\x80==S\xb5}\xcd\xd0\x00\xb3\xac\xc8\xb2\x82U\xe8\xd7-\xbe\x95\xb3\xe15\xdbr\xab5\xac\x92\x1d)\x84+hm\x0b\xab1\xa7\xe5\x83\x05K\xf9\xdffI\x9c\xb38\xef\xd3\x10\xf1\xf8\xd6\x12\x04\xadT7\xab\xd5\xf5\xc1\xc9\xd9M\xbe\x8f\x01\xa9\x1es1>c\xf9\xe4\xed\x9b\xef\xfa\x0f1\x04W\x05\x8b\xe4\xe1\x98z3\x10W-Z\xbb1T\xe3\xed\x7f\x0e\x12\xa8\xd14N/\xd8l\xa2\x90\x92<\xee\xdf\xf4\xaf\xaf\xaf\xfb\x1c\xc5\xfb\xdb4\xa2\xe8\xfc\xf3\xea\xac\x8d\x12\x8c\x96a\x8d\x88)\xd1\x94V\xfe*\x8d&!i\xcc\xe6\xfd\x0d)d\xb4\xe44\xf6B\xe5E4\x88AY\x12]\xb1j\xb1.\xedi\xd0km\xb6\x15\xb7;\xf5$\xa9\xa4\x01\x0bg\xc9\x96k\x8cI\x8e\"\x9b\"\xbf\x98t\x17\x82\x0c(\x93]\xa3e\xa2\xcb\x989\xb6\x9d\x9b\xb7\x99\x04\xda\x12&\xb7nq\xc9\xaaY\xa5\x04Gr\xe79\x8e\xda\xf7\xa9\xb4\xfc\x02\xeb\xf8l]o\x90\xafXl\x8aM\xfdQ\x92\xdf\x9c\x88G\xeb8\x7f\x13Pl\x17\"`G\x11P>vQP>\x15\x91\x90o\xb3A\x16\x94\xcf\xc7_\x0bM\xba-A\xc9\xf3\xbe&\xfd\x91\xbfzaS\xcde\xdc\x17\xf2\xba\x1f\n\xaf{u\xb5E:\xdf\x9f+\x1b\xc7`\x91&\xeb\xe3U\x90\x1e's\xe6\xe6\xd3F\xd6+\xb5\x17J\x99`\xcbk\xfa\xd1\xb2\x10\x9dV\xee3\xd0\x9e\x03\xf8\x8eh_Fv\x0bE\xd7E=\xaa\xb1($\xb8Vt\xd2\xd1>\xc7\xf37B\xd5E\x03\xaa\xfe\x9c%\xf1\xe7\xb4\xfd\xa7\xb3\x97/(\x06\xaf\x95z\x95\xde\xdb\x94\x85Y\xab\xe7\x0f\xf9\xf5\xd1\xfd,\x0fU\x87\xfa\xfa#\xad\xd0\xad%\xc6\x08\x94`P\xdf\x8d\xb6\xc4\xb2\xba\xde Q\xda\\F\xf9T\xf1\xcd\xac\x94)\x95\xe9\xbf\xb9\x1a%\xe4\x83\xc2Gv\xa5r4\xc7\x98\x8f\\e\xd7\xf5\xe4NQ\xd6VlL&p\xa5\xf7\xc9\x9c\xd1\xdbd\xce\xfcR\x82\x18`\x9a$\xcc\xbb\xc2l\\z\x06\xf6\x8a\xbd\xc1\xb0\x87\x9a`H\xb3T\x06K\xf3\xf2\x1bf\x9f\x97~\x7f\xf8P_\xa1\x0f\x1f\xc0I\xd6a\xee\xf8\xb0W,NE\x98\xb2/Vn_\xacv\xd2W\x98;\xf3\xe4\xedf#\xed\xbe\x8d\xc8}\xabe\x1a\x87\xa7\xd0\xa7{H\xa6\x8c\xdd\x1f\xdd\\\x0eFN\xbc\xf8\xe9\xfc\xc7\xc9\x1b\xc7+\xefcN\x7f\xa8\xc2\xe2\x07\xe5\x9d\xc1W)[\xb04EI\x80\xde\xba\xd8\x0e\x99V+\x1d|\x7f\xf2\xecy\xed\x0b\xf9\xcbZ`\x1eUoN\xf90&4\x9b#[\xf8\x8f\x937\x90\xa4\xc0[\x939\x873\x13B\x10\x91\x93\x1a|5\x8e\x8f\x0d\xf7\x17\x1d\xac2\x82\x0c6Y\xed\xd3p\xedz\xf2\x8c\xfe\x8ec\xb0\x1a6\x9a\x11Z\xc5\x03B\x1e\xd1~cxb\xfe\xe0\xf6H\x0b\xba\x96M\xa5\x87YT\xa0\xad:\x1e\xdc \xe67q\x8c\x0d\xd8\x99g.-\xa0\x14d\xf8\xed\xeb\xd3\"&\x19\xd7\x91\x0d\xaf\x93\xeeq\xe1:[\xb77\xed\xfb\x9a4l(\xad\xf4\xfe\xbb\xf4\xe8]\xbc\xbf\xcbn\xd66k\xdc\xb4\xda\xe5\x8d\"(\xb2\x8b\x0f\xdd2\xda\x8b\x8d\x1b;\xcd\x0d\x82\xbeWi\xed\x0e\x82|>g\x0f\xe6V\xbe\x9a+_\xfa\xbf\x17\x82\xbbH\xd0-\xae\xeeI%\x99R\xd5SXs\xfe\x17\xe6\nC\xf7\x0d\xf9i\x0c\x07\xc3\xa1\x8c\xfe\xfa^\xfa\x85\x88\x8fO'\xfc\xab\"\xe7\xe2\xed\x138TU\x8a\\\xf8E'\xfcW\xad#,2\x06\xe7\xe5\x9f\xe5\xd8\xec\xbc\xc0\xd7\xb9V\x8e\xffc\x8a\xfc\xaa\xa1\xb1j\x17)/7\x1axDZo\x1b4\xaf\xac\xc7n\xba)a\x0cN\xc5\x92\x80\xd3\xb3\xe4Q\x92\x07Tcp\xceD\xcc\x88P\x06\xa6\x90\xc7T\xf8\x8a\xbe\x9a\x1b\xe1\n\xdb\x18\x9cB\xa1\xd1\x1a\xe1\x1aF\xf1\xb3U\x00\xe4O\x9e\xde\xb6\x98\xc3\xb4C\x07\xde\xbe_=\xc3\xd0\x9f\x8f0\xc3\xe0\xd4\xcd\x94\x174\x97\xca\x91\xbd-3\xe2T\xa3\x1f\xcbGJ\xd5|m\xc4\x9fM{\xa1\xa9\xdfcp4E\x83*\xd5\xd1\xd3,\x1a\x95\xcc\x84\x1eB\xce\x15L`\xaa\xe2\xd5\x9cJ}\xeb\xdc\xf1\x8b(6\x85\x1aV\x7f}\x1c\x05\xeb\x0d\x9b\xd7\xbf\x9e\xc6\xf9\xe8\xbe\xb9\x92\xe9\xfdi\x9c\x1f\x1e\x98\x8b\x9b\xde\x7f\x17%\x81\xfd\xc3\xfd\xbb\xe2\x83%,A\xfbuP\xf9H^\xc0!\x94o\xd2_Bv\xdd`~3\x03\x81y\x10*[\xaf\xb0\xd2\x86\x19o\x9cS\x88\xdd\x87v\xa5\xc4\xc1\xd6\x10C$.\x9a\x07Z\x87\x9aDk\x89\xd8D\xc5 \xd5\xca\x8eP\x94D\xb5\x9d<\x83\x9a\xae\xde)?\xbeu\xb0\xb1:Di\x05`\x82\xa7\xd0\x18\xfd\xd4\xc7\xe8\xa706$\xff\xc1 ^\xc5\xf8\x85\x93z\x97\xad\x17EU\x97|\"u\x9f\xf6J\xfbK\x96wj\\m\x9c\\ b#\xe4f~T\x9a'\xa5{l\xebx\x154\xfbFU:\x96\x1d\xd4\xc2Bs\xe8h\xeb+\xabL\xb2\x01\x02\xca\xab'\x80\xa0\xad}\xe9\xf3\xdb\xe1\x1a\x14\xd4\x02\xdc\xc8\x1e=\xeb\x1c)\xdc\x8d\x88L\x95\xfb\xc5\x18\xe3st\xfc\xcak\xa7\xf2\x861b\xd0\xb2\x0e&\x0bi]\xb4\xe5\xfb\xd3\xf7\xa3\xee`\xd0\x92\xea\x8d\xc9\xc8lfT\xc6\x8b\x89f\x93\x88>\x15\xf23\xfe\xf5'\xd3a&\xb6\xd3\xfb\x8e3\x11\xae\xd2\xbf\xfeT\xba\xed\xcb4\xae\xdf\xf7\x92O\xd3\x94*\x8eA\xda\x0cM.B\x86\x05\xb0\x9c\xc5\xdf\"\x9f\x7f~\xf2\xc3\xc9\x9b\x13\xce/\xb9\xc2\xee\x0b\xf5\xdc\x07\xe7\xe5\xab7\xa7/_\x9c\xf1?_\xbd<\xc3O\xaf\xde\xbeq\x0c\x0b4\xd3\xba\x9c\x89\xf4\x17\xad+\xaeIe\xd2\x13\xdc\xbe\x82\x97\xc9\xfcV?e;\x8dC\xb3+\x96!\x16\xf5G\x1f\"Bnm\x9c\x9ez9/\xbd\x9c\x7f\xe6T\xe6\x95\x93\xff\xa6I\x91\xf60\xd6\xb5\x0d\xbbFX\xa8\x1a\xe3\xaazb\xaf$\xb4\x8e\xb1TC4\xdd\xa4A\xf9\xdc\xa6\xd1X\xa9);\xac\xf2|\xd0\xd9\x9fF\xa2\x1b-\x19Y\x8c\xda\x17\xca\x90D\xb7\\\x84\x96\xc7q,\x83nDm\xa6\x14M\x9c\x85S\xf5|\x04\xb34$/\xd5L\x0f\x87#\x1f\x0e\x87\x07\xfc\x9fC\xfe\xcf\x03\xfe\xcfC\x03\xba\xcc\x07)\x9b\x87)\x05\xd8\xed\xc4\xd2\xb8\xa0.RK]jV>\xddZ\xf6:\x88\x97UwS\xa1\xd4\xce\x92\xb9\xf5\x80\xbe\x04\xba]\xf7\xa1\x0b\xc45+OQBG\xcd&\xeb\xa4|,\xea\x93\x11\xf4\xd8\xe0{\xa5r'\xfc\xe7k2\x08\x02\x86^\xe5?\xb3M\x12g|{\xe7\xfc7j\xce]W_[\xa3\x9a\xd3Z\xd3%\x17\xd0\xad/H\xe6\xb0\x97f\x1b[(\n\xff\xe7\x8f?|\x9f\xe7\x1b1\x0f\xbb\xa9&\xdc\xd13\x0e4\xeck.\x05\xda\x8e\x87\xad\xf4\xa9\\\x83\x101\xecD\xa4\xe4\xa3@g\xe3bN\xa7gQ$\xb7Ml\xae\xeb\x91\xb1\xc4\xee2\x02f$\xd5\x1f&\x8c/N*\x1a\x7f\xfb\xfa\x07G&\xa2\x0f\x07\xda[\x18\x03+\xfb\x93\xfcg_\xecG\x9fWj\xf1y\x91&E\xd5\x91\xa1\x17L\x0f(\x7f\xf0ejn2v\x05\x8f\xf1\xc1$\x97\xcb\xe7\xa3\x8f`\xd1\x99\x1a\xcb'\xad\xba\x86\xbe`\xf9u\x92\xbe\x97\xe6uX\x04a\xc4\xe6&\xdf\x0f\xf9\x88\xaes\x8a\xfe\xfd\x0f\xe9|\xc3b7Q\xc7Q \x9d\x7f\xe1\xe5&'\x8cg\xd1v.\xe2\xd4%\xa5\xd3\x1e.Y\x85\x18\xa5\xec\xb8tND\x15\xd5\x16\xddn\xe46\x96|\xc1\\m\x17\x05\x17!/\x0c>\x00 B;\xf9G\xcb'\xe4\xea\x95\x80:B\x03\x8b\xbb\xb4|0j\xe4 c\xf1\\\x0f\xa6\x9ah\x87n*}\xa0\xf6\xd2&\x95\x9a\x89-\x92\xcf\xc1&\x89n\x17a\x14\x99\xbc\x82\xd5_\xae\x9e\xc1\x163[\x90lQ\x8d\x85\xf6\x07\xd1xiqv\xbai\x94\x9bn\x19\xdd\xbb\xeb\x0d\xc8\x98b\nd\x1b\x1a\xb7\xc0lQ\x14\\\xc0pLQ5\xd5J\x13\xa2Q'\x10\xcd\xa4*\x8d\x9b\xf4\xc6\xe5\x03\xd1|\x13m\xeb\xa9\xfe\xaa\xb6\xd0\xc6\xcd\n\xb5\x18\xef2\x89\xec\xdd\xf2`W\xf9Ml\xe9\x9eQF\xffE*KN\x910\xdc\x9a&\xe7J\xc4\x1b\xcd\xe0I\x11N\xfa\x88k\xd6\xc2\xbf\xe2Y\xee\xa2s\xfd\x8b\xe0E\x9d\xcee\xd7!\xae\x9a5\xdb\xfd,\xc8\x18\x0c\xc7V\xc0\x97\x0dX\x8f\xd7\xe5\x83\x0d\x1d>\xb0\xb7$\x1f-\xd9\x80\xb8z\xd5\x10Y@>\x98\x86\xad\xb9\x18\x0e\xe0\xeea\xfb\x00\xf0J\xac\xcb\xd7\xf4\xf0\xa0\x85\xdb\xc8\xc0\x86\xadm\x06\xd3\xa8\xd73'\xea\x94\x8fY\xf2\x82\xe6\xc9\xe1\xa4F\xf6\xfe\xb9\x0c\x1b\x92<6\x83\xa7\x13\xb8\xfb\x90On\xc6!\xeb\xde\x03\x0f\xd7z\x06}\xb8\xfb\xd0>O\xe5\x95\x8b\x0d\xdc\xbf\xa7\x1ax0,\x1a\xb8\x7f\x0fz0\xb2\xdc\x10\x86\x1d\x1ch\xa9\x97G\x0fT/\xa3\xe1Ac\xf0<\xf9\xa8\x15>|\xe0k\xcb-p\xab#\x045\x96\xb2o\x10\x08\xb0\xe5+\xf1\xe8\x01\xae\xc4'l3\x1f\xe8\x81}\xa0mPp\xd0\x0c\x05\x82\xc4\x98\xa0 \xfd\\(H\x7f\xe7P\x10\xea\x10\xf1\xeb\x83B\xfa\xd9\xa0\xa0F;\xba\x0f\xdf@\x0c=\x93Q\xfd\x0f\xf6_\x82\xdf\x05ER\xe2\x08\xfaz\xea\x94\x8f\xbe\xc6\xca\xf8\n\x15\xab\xa2XVP\xf2\xf2;\xb8w_2\xaa\xc7\xb0\x85'pp\xef\xfec\xe8\xf5\xb6\x1e\x04\xd3-\x86#\xfe\xa3\x03=p]\xfeqt\x1f\x8e\xc0\x19:\"]r\x0f\xb6\x05\x97\x1d\xdd\xf7<\x9b\x87\x8d\xcc\x9e\xd6hFo\xb8E\xd9\x9b\xf0\xfe\xca[\\\xf2ft\x9cR\xceP\xe1\xac\xc8\xb4T\xc5F\xcdRj\x94%\xb6j:I!\xf0=<$\xf9\x8fkNw\xefi\x7f\xdf/\xfe~\xa4\xbd\x1f\x1dh\x1f\x12\x0e\xfb\x87\x8f\xf8\x8c\x12\x0e\xfbw\x0f\xd4[B\xdc\x84\x10W\xbd%l\xc4\xb7\x8f\x86\xea-a\x0f\xbe\x1d\x1d\x1cX\x04xtd\x80>\xc4*\x1dh\xce\xd7P^(BE\x9b\x8b\xd3|K\x0f\x1e\x12\xbdO9T\xfb\x80\x05\x83ib\xb1\xdd*\x82\xc1\xeb\x1e\x0c\xef\x1a+\x8f\x1e\x1d\x00\x0e\xf7)\xdc?\x87\x1e\x7fs\xf0\x10>\xc0\xfdC\xb8\x03\x9dZ\xbew\xef\xe0\xd1}5\xe7{\x0f\x0e\xef\xde5utppWv4:\xd0{\xa2\xbe\xe1\x0e\xdc?\xdcm\x00\xcd\xd6\x87\xb0\xc1v\x80\x10\xd2\xeb\xe9pW2*\xbd}}*\x94\xb1\xb7\xafOa\x1dD\x8b$]3\xab\xdb!\x08\xfb\xc5hx\xc0\x07]\x81P\xdf\xb4\x18w\x87\xf0\x81\x12\xc5\xdd\xbfw\xef\xf0>b\xad\xa8\x9ex\xf0\xe4 \x8cx\x81\xd0\xf3p\xbd\x1e\xd6\xd6ktP[\xb0\xe6u4\x0e\xbc\x03\x01+\x02\x890\x8c\xfbT\x12qs\xe8\x15\x80\xea\x95c7\x96\x15\x95\x96\x88\x05\xd4\x97\xe5\x8e\n\xef\xd8\x94\xb9\x85#K\x98}\x17\xc6!E\xe4:\x02\x87\x93?,~\x99$\x11\x0b\xe2zSG\xe0\xe4\xe9\x96!Y\\\x04QF\x7f9\xfa\xb8\x0b:,\xf5\xa5hw}\xc9\xae\x1e5\xc51,8\x02F\x1e\x18vQ\x87h\xd1\xc2\xc5-&\x0c\xa4[+U\xa5\xc8\x9c\x0fX9\xf1:w\x04MF\x87UgR\xb9ht\xa5\x12\xfa\xd2\xd8\xca_\x89\x0e\xd8\xa2\x18%bD\xba\xe6H\x96\x03<\xb3\xa9\x7f\xe4\xf8B\x99b'\xf6d>\xa6%,qM=\xe3\x83\xcc1\x1c\xa8\x88$\\\xbd\xdbrvL\xd9\xf29GZ\x10+Z\xc0\x13\xd8r\x1e\xb4h2\xe1S\xaa\xe1EC\xa6\x879\xa5$n\xc9\x16\x11\xba\x19\xe6\xb7\xedU\xd3A\xca\x87\xafm\xf9\x12\xf8\xbcQ\x08Skp\x05\x13\x98\xab\xf9\xaea\x02W4\xdf%\xcds O\xe0\x8a\xcfs\xe9\xc1\x8c\xd3\xa4\x15\xf4p8\xf3\xe9\xf2\x9c\xf3\x1b^`-\xd4\xb0\xde\x04\x9a.V`\x08+\xbep\x91^\xdeLp\x88r\x97{\xe4\xdd\xb5W\xaf\x8bj\x02gf\xedDL\xc7o.v\xa1\x8f<\x024\x995\xbe<\xba\x04\x86\x88_\xa1-\xea\xc6\x87\x0f2[\x8fdFJ|,\xb7`\xa8\x9d\x17\"CM\xec\xba\x12)\xf1c \x08\xb5%$\x8fp\xdbW\x8e\x1b#vXn\x94P\xbdN\x8e\x93\xc1:\xb8\xf93\xbb\xcd\x94\xee\xae\xde\x18\x86\xc5\xd1m\x04\xfbU\xb5p\xa6\x84 ^`f\xa8\xb8\xc1m\x93T\xd2443\x15\xaa\xdb\xaf\xb0\x9b\x0d\x8e\xb3\xfe\xd1&\xc0r\xbc\xde m\n}D\xe1\xe9\xb9\x8f\xc86$,\x1b\n\x0c\xf3\xf1\x94\x99\x13\x96K\xf1\xff\x05\x9d\xc1\\\xd3\x7f'T\xe8\x86\xb0\xf1\xa6\"\x00\xdf\xd8\x04\xe0\xb3\xaa\x00|c\x11\x80\xcfp\x8c\xb9^tm\xa5\x1c\xbc\x82\x18<:]\xb9\x87\x0f\x10\x1c\xcf\xe0\x08\x07:\x821\x9c\xa8\x9d9+\xc4\xe0\xb3B\x0c>+\xc4\xe03RJ\xd5[\x12\x83\xcf\xa4\x12 G\xc0es\xe8\xf5(\xc2\xda5Y\x9b\xb1\x8f \x86\x91\xe6\xb4\xc7j\x0e\x035CJ\xba\xa2\xcdp\xd9\xaa\xa0\xf2\x8a\xbd\xde\x12\xabn=\xb8\x82'\xe0\xbe\x87 \xdc@\x1f\x96\\B\xa38\xd5\xb7\xba\x04~\xe5\xc3{N\xa2\xc4\x96]a\xf1^\x9bIl\x96\xc4y\x18ow=\xe6\x03\xe1\x0d7\xe4\x00\xf3\x9bo\xc5Ee+\xcc4\xdc\xf8\xf6\xee\xa1\x18'o\x077\x10\x8e\xc0\xe5\xebz\xa5\x86[]\xd6\x1b\x0f\xe3\xa9q\xd2\xf5\xc7\x83\xa1\xc0\x11\xea\xbfR\xf3\xd2T\xf3R\xaby-\x8f,\xd4\xf6\x188H\xa1\xb7\xf4zk\x1cn\xd6\xc4\xe5\x8f}\x90\xb0\xb1\xb6o8oN\xce\x97\xc3\xd3{\x1b\x04\xc1X\xfb^\x9d\x10B\x98\x8c\xf88\x81\xc8\xbd\xf5a\xc3\xdf]\x8b\xe2\xfc\xdd\xa5x'\x8e\xc4W\xeaH\xfc\xd6\xf3 \x98\xde\x9ec(KXMW\x82\x96\xf0\x17\x86\x9bY 4(\xf7\x18\xe5\x98\xdbsO\xbf\xa6\x85r\x06\x1c\xc1\xf1\xf4Xk\xe6\x12\xc6\xb2\x8b\xe9\xb1\x0f\x97\x16\xc5\x8c\xaf\x06\x06\xf5\xea\xf7\x17^\x93\xc1\x8cou\x99\x16\xdeb/D,\xd5.\x12UE\x8c\xa8\xef\xe7\x1f\xec\xbf\x16\nt\xaet\x95\xe5\xc3\x07X\xf2/^\xfd\x93\x0e\xb7\xe5\xdd\xe3;\xb7\x86'\x90\x19v\xce\xfb\xcc}\xe3Hb\xdd9D\x84\xcf\xd9\xa3\ns\x90B\xc5\x1f\xcak\xd69\x93\xc1#K*\x83\xc3\x87#\xaf\xfdtO\xba\x13\xc8\xebpp\x04\x7f\xffH \x0dAB\x8b\x91\xeb\xc7e\x9d2]\xea\x03\xaeF\xd5\x13\x03\x1e\xb6GI\xb4'\x85HE\xa7\xad~p\xa2|\xe2\xb2Z\xfa\xb3\xd6\xc8p\xd69\x8d\x0e-s\xba[M[D\x81\x05\x1f<\xea2U\xc3\x0cJ\xfaT\x7fD:\x94\x12\x16Qt\xfc\xfbG.\xad\x04\xa83\xd9D\x16\xbc\xf01\x0d,\x9a\x10\xe6\xe9\xe3#\x88\x0c\x82L\xec\xce\xf8\x07\xa0\x98\x81>\x84nDA:g6\xbd\x18\x8aU\xcfv[`\xf3\x19\xeb\xfe7{E\xdb\xdf\xc0,I\xde\x87L\x7fs\x9cln\xd3p\xb9\xca\xdd\x99\x07\x07\xc3\xd1A\xff`8\xba\x0b\xaf\x93u\x10\xc3\xd9*\xbf\x8d\xd6A\xdcT\xe1\x1e\x1d\x9e#\x0f\x99\xa3*O\xfcf\xc4\x99H)w\n\xc4\xd3\x0d\x95\xc3?&\xb0u\xe7>d\xed\xa1)M8SI\xe4\x9d\xb14\x0c\xa2\xf0\x17\x93~\\],E\xa0\xc4v\xd7WZ7O}\xf8P\xbdm\x88pY\xa8n\x05d\x86\x16\xc8L0\xa9\x1e\x88\x06\xc3\x0cB\xf2\xfe\xab\xee2\xeep\xd0\x12\xa8R\x81y\x1c\xac\x9b\x1a\x93\x1auX\x8b4A\x07|\x18\x9e\x9b\xfa\xda\xb6\xf6u\x15D-]\xe1uu\xe8\x813q\xa0\x07\xdbz\x8f\xc2R\x06)W\xb5\x9f-\xadW<#(\xca@\xdft\x18\x8b\xc7\xd4\xd9\x8b\xe0\x85\x1b\x99\" \x89\xaa\xd9\n\x831 \x0dxA&\x00\x03\x14g(\x98?\x86\x1f\x83\x9b\xfe\xb3%\xc3\xc1\xff\x18\xe4\xab\xc1\"J\x92\xd4\x8d\x9a\xa87\x1e\x87\x0c\xe6\xc9:\x08\x8d=\xe8o\xb0\xd7\xe4\x15$'(\xfa\x98\x9cUe\x9b\xea\xd3\xe6\xdd\xe0D\xc1\x8d\xb3C\x87?\x047\x9f\xd3\x9b\x90\xc5v\xe8\xf0sf\xd8\xeaF\xd4\x04\xf4j\xbfu\xa8\xaf\xb5\xd4\x81\xffj2k1L\xc9Y\xebF\xca\xba\x1aP?N\xa9\xab\x04\xfb\x8f\xe1\x9b\xfd\xf2k.\x9a\xed\xff4}\xb7\x1d\x0e\x87\x8f\xf8\xbf\x07\xc3>\xff\xef\x01\xe3\xff>\xa4\x1f\x8b\xc5y\xef\xdf\xf6M\xc7c\xdb\xdf\xeax\xac\x1a\x93\xb9\xfc\xd7'I\xf8\x1dC\xaa\x8b\xfek\xcb\xeb2-\x1c\xc4t\xefk\xd7\xfb\xe6|\x7f\xd9\x16\x8b\\\x1eK\xa0\xbbF\xc9\x9e;\xf4J^\x1ae'\x8d\xf2\xec\xdb4H\xbd\xe3n\xb3,\xb9i\xc8\x1c\xf32+\xb2\x92\xc7c\xbb<\x9eV\xcd\xd3\xb1E\xe4N\xd1U\x00\x1d\x07\xee\xdc\x81\x14m\x97\xf7\x0fG\xe8q\x11C\x0fF\xfa\xc9|\x83X^s\x08\xc1\xca\x16\xc1\x9a\x0e*\x9fbW\x07h\x1c\x12n\x1c\\un0\x1c\xcb\xe3\xcf\xd1\xf0\xe0.|C\xde\x1a8v\x0fz\x90\xf0\x1f\xd8^\x8f\x8e\xf2\xed\xe4'\xa7\xebp\x07w\x87ey(\x84}\xb8\x7f\xb7\xf8\xc7\xf3at\xf0\xd0Z\xc6\x83?\xc2\xfd\xbb\xd62\xe5\xcf!\xfeB\x1f\x84^\xa3\x1bg\xa3\xbd\xban\xf25\x9c\xc6Qh\x89\xbb\x0f1B\x04\xcd\xf4\xe0ny\x84i\xf3$S\xc3\x04R\x9a\x00\xe7\x97\xbc\x03\xfeR\xb5?zt`l\xa0^WTH;\xd8\x0d\xda\xd2O\xea\x90\xb2gw\xf3\xe7@\xc3la\xf9\xedF\xb2J\x91\x86\x0b\x96(\\\xa6z\xfe/\xcb\x19\xb2\xc4\x93\x86[d\xa1\xddAs\x9e\xb4`F\x80V!v\xc3f\x8d\xa9\xc5\x94\xb62\x99L h4\x0d\x83\xd2\xcbCx\x02\\\xbao)\x9c\x90S\xcd\xf0\\\x19\xa7\xc2^\xcf\x0c\xc8p\xbd\n#\xa6\x14'>\x14s\xbb\xd2v\xc7\x81N\xf3x\xe9\x8f\xcc\x19r\xfe`\xdfIK\x8a\x00\xd0\x9d\x04\x85v\xbaS\xbb\xc2\xach\xa3\x8eZz\x8d;\"\xbd\xc1\xd4\x99\xfet\xee\x9c\x97\xcd\x07d;\xe0\xa2l\xcd\x9e\xa3\xda\x12\xa4\xbd\xed\x92\xf0\x0ea\x81\xb0\x1a!%\x1bd\xc96\x9d\xd9\"Fx\xbe,\x18\xca\x82\xe48\x98\x0efI<\x0bD\x10Gv\x0d\xaf\xd9\xf2\xe4f\xe3\xa6\"\xe0\xcf\x07\xc7\xab\x99]\xc1H\xba\xd8`\x11\xc6\xf3\xe3U\x90\x9e\xc6sv\xd3fB\x93\x0f\x87\xd1\\\x87\x0f\x85\x89\xfd\x86\xb3\xa22\xceZ.>\x95,i\x89\xeb\xf9\x02E\x0b\xd7\x98X\xa2\x1c\xda\x1c\xdcx\x10\x05YN\xc3\x7f\n\xb9\xf7\xd8\xe38\xd0\xb8]\x86\xfc\xcc\xbeX\x8aoos\xb6\xd3R\xc8\xd9\xf0\xd5\xc0\x1b\xb4\xb4 \xe4\x95\x858\x83\xf5q&\xe6x\x8b\xc4\xc5\x9fu\xbe\x1a*\x17\x87n\xa6\xebc\xa6j\xf6\x0d\xe0\xd2\x0c\x9e\x88\xc6\xc6\xbd\xb3EY.\xe4\x1b\xe5\x98\xc9\x85\x8d\xea\x89\x88\xfe$\xe8t\x84\xfb\xd4\x92~KQ\xc6\x84\xeb\x8c\x94)?\x99\x0e\x8dq6tyg\x97\xd5j\xbd)\xa3?r\\Hc\n\xdc\x92(\xe8#\xb50\xee%\x7f>\xb6\xedA\x8a\x06W\xd9\x8b\xf1^\x0c\xd8D\xbc\x96\xa5$\xa9\xf2\xc9\x84\xbcA\x92B\xb4+\xcd\x89\x8f\x15}?\x87\x9e\xafdN\xe95\xca<\xa7\xd0=\xa8\x07\xee\xa2Q\xe0\x10\xde$\x9c\xf4\xbdJ\xc2\xb8\xc5\xe6!\x9f.\xb6\x0f\\\xdb\x99lW\xae\xb1\xc6=DjIU\xc4\x13\xd6\x12\xa1~j\xef\x1b\xa7o\xe1\xfajBo\x84\x85\xe8\x8bM\xac?\xb9\xcf\xd7\xf2\xf9w\xdf\x9d\x1b_\xeek\xbb\xfeQ\x1c\x16t=\x13\xf8\xba\xdf\xef\xbf\x8b1\x00\x96\xb3\xca\xf3M6\xde\xdf\xdf\xb0\x1c\xf3\xdd\x0f\xb2\xeb`\xb9d\xe9 L\xf6\xaf\x0e\xf6\xe5\xaf\x9f\xb3$v\xde\xc5\xf3d}\x11\xce\xc7\xe0|%>\xf4\xb7\xa1\xf3\x8e\x0e\xc1\x82\xd2>\xab\xa60\xf2\xc15-\x07\xf4a\xe6\xc1>$\x1dg\xa5?ie{\xb4\xa3\xc0\x0cz\x10\xc17d\xee\x1d\xdc\x83#8\xc08\x0e\xdf`$&\xfe\xbf{\x17\xfa\xf4\xd2C\x95\xd2\xa6\xe0\xd8\x9e\x02Py\x17#\x0e\xac\x08\\\xdf3t\xef\xf5\xf0\x00\xf2 \x10`\x0f\x88L\xd37.\xb1\xa0\x0b\x90\xbe\xd2\x81\x0f\x8f\x1eiPo\xc7\xce\xea\xf3\xd1\x87G\x1d\x8b\x7ft\x9b\xcb\xd9/%5\x90\x84h\x07S\x85|2wK\xf1\x9e\x8dG4\xf2\xb1\x84\xb4\x93\x8c\xc8N\xa4X\xbe\xdd\x8c\xbb[\xbb\xa1h\xd4\x1571\x91*y\xeap\x8c\x8fU|B\x87\xe6\xdcS\xc6\x9d\xdck\x8a\x1d)\x1f\xe1`\xf4|\x9b\x8a\x00\x90q;\xb8\xb3\xf9\x92\xbd\\,2\x96\x9bBz\xeb\xcf'\xed[\x9e\x8c\xc1\x92\xab\x80>\xff\xd7\xb8\x89\xd6\x85q\x9e\xfc%d\xd7\xe5u6]\x9c\xad>\x92Wc\x9c\xf0o\x93m<\x0f\xe3\xe5q\x14\xb28\x7f\xcdf\xb9\xeb\x0dV\x88'\xed+\x14H\x8a\xae\xf8Z\x0f\xc2\xf6j3YM\xe2j{\x95\xc5N\xbcc\xc3Q\x02zm\xa1n0\x05\xf2\x13Xp\x88\n\x91^<\x85\x19\x1cQ\xbc\x01Z\xc91\x04\xe2\xc3\x06\x8e s\x03N/\xf9\x9b\xa2\x00\xb1\xd2\x06\xccn\x80\x81\x19\x8bs\x96\xd6\xb60\xed\xb0\x8b\x99\xdb$]\x94I\xe1>\x1c@\x8f\xa3\x0b\xc7\xaa\x96]\xe7\x85=OL\xefS\xe6\x94\xe5\xc9f\x0c\x81\xbd\xc0:\xb9\n\xe3e\xc7\x0c\xfcP\xd0\x86\xbd\xbd\xfa!\x90|\x1a\xc6\xc3\x81f,\x80\xa7\xb1\x14.\xdfX[Jca\x833N\xbdUN\xb3\xa4\x14?\x90\x7f\x9cDl]s \x04\xc1G[\x17C,\x82\xd0E\x88\x9f\xfd\x17\x1a\x91\xc5\x8f7\xc9\xa6\xcb\xd0\xd0j\xef\x9a\xfb\xa0x\xd7j\xe0\xd4n\x18/\xc5\xc8yo\xea#/k^N\xa4\\\xddd\xe5\xd2l\xde$\x1c\x92wL]\x81\x9bkIN\xa9P\xa0#\xac\x95\x978\x8cc\x96\n\x89\x01\x97y\x86\xc8Bov\x1c\xa3\x00\xadn\x8b\"\xf5T+\xa2\xe6\xc9\x86\x93 \x14\xde\xe2A\x82,\xca\xb4\xfb`\x06W\x83\xb75\x06%\x0drv\x86\x1bQ\x8b\xeah\xa3G\xd2N\xd5\x08N\x96D2e(i \xcb\xaf \x9c\x03\xef\x8ek\xff_\xbb\xed>k@'h\xec\xe8S`M\xc9\xe7\xac\x04^~' \xdc\x15S>\x0d\nw\x86/\x01/\x7f\xa8\xbct\x82\xf9\xfc\xe4\x8a\xc5\xf9\x0fa\x96\xb3Xd\x0c*L.{b\xcaq\xf2\xff\xb2\x98\xcc/\xf8\x9a\xb9%\x9ac\xbc'&E\x1ag\x15fy\x92\xdeV\xad9\x9bm\xb6:\xcb\x83\x9c\xcc<\xa2\x90y\x9d\xb8L\x13\x92 \x08\xe1\xe05\xe3\x85Qj\xd4+\xd7%\x0b\xcaT*>\x0fj\x95\xf9\xe8\x82m\x9e8\x9e\xda\xdc\xea\x82\xb8N\x94\x04s\xc7o\x87 \xeakWE\xb1ql\xeb \xde\x06\x91%\x86=Wq\x1a\x86\xbdI6\x19\xaen\x9b\xe7\xb5|\x18\x86\xe8&K\xdc/,\x16\xdc\x8cRH\x15\x9f\x12T\xf1\xc4\x8bAQ\xce\x06\xf7\xb0\x87\x97\xf3\xc40e\xb0\xf7\xc1*\xc8\x10\x92v].iUL\x06\xa8\xd0\xb8\xde\xa0\xd0\x08\x9aO\x0dZ\xedC\xd2h\xa7 {\xc9\xa4x\xf0\xed\xed\xe9\xdc\xadM!e\x0b\x99\xc1\xef+\xc7\x9b\x8e\x9a\xf2\x05\x83t\x8ek\x1b\x05\xd4\x0c\x05$L&\x850\x99s\x1e\xc3:\x88\xdc \xe4\x98D\x08\xe9\x9c5\xb5+\xf4Cx2\x81\x14\xc8 \x1d\xd0\xff\xdc \x124\xa8\xa8\xd0\xac}\xd9\xa1\xd9D\xb6\xf6L\xae\xebW2\x8aO\xe1\x86\xe5\xb8?}x\xf7.\xf34J\xe5\xbe{\x97}\xf87\xcf\xe4\xc2i\xc5\x9aY\x14\xce\xdewB\x99\xd2\xb1!\x1b\xe4A\xbad\xf9c:\x89q\x9e9\"\xd8L\x1e,_\x04k\xf6\xd8\x13G\x9f\x9b eq\xfe\"\x997$\n\xdfs\xf7\x90\xb1\x8c(\xe0\xd7\xe0z\x15\xceV\xa4&`\x1a\xc8?\xb3[\xfa\xb5fy\xa0~\xcc\xf24R?\x82\x88\x97j\x8c\xfd\x82\x16\xc86h\x94\x90\xa8\xa8\x94\xa2\x10\xf5\x08d\xe52G\x95\xdf\xe3\x9a\x91\xbc\xfa\xc4\x1a5\xd1\x80\xb6\xb9R{\xca?\xd0\x88\xac\xb8\x96\x82\\\xc7\x8d\xeb\xe7k\xd5\xa7\x94\x02pW\x90\x06\xdd\xc5\x0b\xb3\x18\xe4y\x1a^ns\xe6:\x9cv8\"\x85A3\xd9\x12\xc6\xfe\xe2\xce\xf6W\x0e\xf9\xb7n\xc9C:\x1f\xcc\xa2 \xcb8\x90\xb5\x86\xfa\x91\x06\xdf\x06\xb7w\xf9D\x0d\x840-\xdcZ\xdcQ\x9b\x89\x10\x8fW\xber\xc4\xd1j\x87\xbdB\x0c\x88\xe4\xd1J;\xb9\xca$\xac\x10q\x8c>\x95.\x01egJ\x19'\x08\xcf\xc94\xd5\x06}W\xe2\xcac'\xd6\xa5?\x15^\x02\x93\x16c\x164\xab\xd3\xf2Y\xec\xcc\x19\xa9\x16]\xff,3\x9c\x0c\xfa\xb0@/\xeb;\"x\xd9N\xb3\x94(\xa7\xa4<\xf7\xef\\\xdet\x8c>^\xfa\xf3\x11C\xbb\xa2\x94\x91\xf9\"\x83\xf4\xac\xc1\xe8af'\x16V\xf2\x07{!\xe9\x07\xa7^~t\xcb\xdea\x18\x9e\xd1\x18J-\xc5[\xad\xc1f\x13\xdd\x92\xa7 \x8c9\xac\x7f\xf8\x00\xae~\xa2\x1c\x9a\x0f\xa0;\xdd\xc9\x13\xc1\x1b\xe9\x94\xb2\xc8\xc9\xe7\x83sq\xc1\xb2\x1f\x93\xf96\xe2\x92^y_0}\xdbX\xcf\xc8\xa0\xeb\x99\x926m\xdc\xd8\xbd\xeb\x19\x02\xa8\xf0\x0f\x07\xd5\x0f\xa1\xf8pX\xfd\x10\x88\x0f\xf7\xaa\x1f\xb6\xe2\xc3\xfd\xea\x07L\xf6\xe0\x0e+o#,^MJ\x85'G\xbc\x15\x94&\xf1\x0f\xb2\x88\xb9\x87\x0f\x1fT\x1b^P\x94\x17\xcft1\xd3\x90\xf4Y?\x83f\x83b=E\x9c\xd5:\xac\xcb\x9b\xb1-\x97/A,2E\xbdX\xb1h\xc3\xd2l\x90lN\xe7\xe5\xe1\xb6;\x02\xaa\xd1\x0b\x7f:\x0b\xfe\x91\x9c(F\xe7\x89Lj6\xcf:\xa9\x9e\xf1JA\xb5\x92\x9b\x0f..0\xfd\xd9\x05\xc5\\\x1b\xfa\x18\x19R\x16\xf2<\x91#\x11K\x93{g\xe3\xc1D8\xc8\x93\xe52bg\xab\xe4:\xeeJK\xa4\xb0\x1f\x0e6i\xb2i9c\xcc\x85\xd3\xeem\xb2\xcd\x9fa\xdb-\x15b!\xb7-\x9b\x8b\x91\x97\x1cG8$\xd5\xd5\xcd\xab>\xc25;\xc3\x896\x17E\xad\x96s\xae\xd7,K\xa2+6?\xdb^\xe6)k<\x0f\xc53P\xcd?'@;\xf9@$\xc6\xa95\x84!KV\xc9\xb5;u\xd4\x0c2\x87\xec\xd9\xe7>\xec\xd9\x9c\x9a)u\xcfq\x10\xcfXt\xccE\xe2\xae[\x869j\x04\xbdo\xde\xae\xf4\xf64\x7f\xb9\xcdO\xe2\xe02b\xf31\xec\x85B\xa7\xac|\xb1\xb6b\xc8H\x03\xc5\xd8\xdf\xa4\x1c\x10v\x1a\xfb'\x80[\xb6a\xb3\x1d\x80m\x13\x98b\x8a\xea\x0fA\x1be,j\x10\x0c\x7f\xcbU\xe60\x84.\x1b\x7f!\xbf$F\xc9\xc11\x87ejs\xab\xa3M8\xb9a\xb3m\xde)q\"\xec2-F\xed\x9e\xc6\xaf\xd2d\x99\xb2,\x1b7&\xf2n\x18c\x1d\xfb\xba\x0e\xf6\x13\xa1\xe5\x8cEl\x96'\xe9\xaf\x00/]\x08\x13\x1f\xc2\xab _\xd9aK\xdd\x07\xc0\xac\xf6\x1b6\xab\x12\x15.\x9b\xfd\xe9\xcc\xf5\xe8\x12\xb1\xa9\xc4\xd4\xe1\x03Wt\xa6a\xf9\xcdt\xebW\xde\x82_\x0da\x7f\x85\x0d\xb0\x10\xf6\xf2\x1eX\nu\xdf\x06R\xd1\x9b\xb2\x00\xd6 \xc9\xc8>[\x13zZr\x8a\xfb\xa6;\x97\xb57\xca\x11\xc1\x87\xad&\x85\xf8\xc2\x07\x81OA\x7f;5\xcf\xe3=\xbb\x1d\x83\xb3\x0e6Hb\xde$\\\x8c\xce\x1c\xf34\x84\xe8\xdc\xd9]B\x1aJ\xf2A\xb2i\x07\x98\\\xc8)\x1d\x89A\"\xc4\xb4\x9c\xdc\x1d\xe3E\xb8\xcc\xbc\xb63w\n&?Of'7\x9b \xce\xc2\xa4\x834\xc2\x85G\xb6\xf9!\x8c\xdf\x87q\x8bX\xb4\xa5\xe2a\xb6\x89\x82\xdb\x97]\xa5\xa3L\xaf%R\xd9I\xff\x8f\xe6\x9a\x11\xa9\xb6\xdb\x0d\xd7\xa6\x10\xc6\xd7a\xfe#\xa2]\xcb\xeaa'OO\x16\x83\x1f\x83M\xab\xd2\xfe\xb3\xd0\xf4\x17x\x13\xfcOg^\x0b\x8b\x03T4\xc6p\xda\xdc,\x7f\xf2`\xd9\xe9\x86\x05\xa7\xdfV\xef]\xfd\xc9\xa4\xee\x91[\x14-\xfa.\xf4,\xc7\xc2\xdd\xf4g\xce6)\x9b\x059\x17\xf1OI\xf3-^9B]3\xf6\xa5\x15\xa3\xee\x9a\xccS\xf2!\x0e4\x86\xa4\xbdh\xa1\xa7t\xb8JQ\xd6UZTi\xa8\xaa\x8a-j\x19\x96\xaf\xdb \xc4\x82u\xb7X\xb4\xf7R\xd2/;\\\xf0SzU\x8b.\ne\x15\xaaE\xf6\x80\xbaN\xd9B\xf2AW\x81Z\xf4O\xb0\xe8\xc6-\xda(4\xe8\xc7-B\x12X\xd5\xfd\x16\xce\x0ff\x89\x96\x04b<\xd2\xa9}mo\xb0f\xd6\xd5\x9a\xebzB\x04P\xf7_\xd7\x1fa-\x89\xa4\x89V\xb8\xb5\x0b\x8f\"\xf7\xc7\xb6\xabb\n\x9c\xc7\xf0s\xf3\x8c\nm\xba\xcdh\xdf\x11<\xba\x82\xb4v\xb6-\x96P{\xd3\\\xb5tR)*\x97\xde\xb5U\xd7\x0eiUu\xed][uqD\xa7\xaa\x8a\xdf\xcd\xd5\xa4<5\x86\xcb\xf6\x82\x82\x95\x8f\xe1\xba\xbd\xac\xe2\xe3c\xb8h\x19y!$\x8c\xe1e{Y\xad\xe5W\xcd\xa5K\xf2\xd0\x18\x8e\xbb\x94\xd6Z?k.\xaf Och\xd9\x9d\x92\xe44\x86g\xcd\xa5u\xc1r\x0c'\x1d\n\xa3T9\x86\x9b\xe6\xa2\x8bx\x0co\xac%l\x87\xab\xb5\xb7\x1f\xcf=\xbfrO\xe4\xa3\x9b\x0d^mSfJ1\xb9\x92\xe4\x02-\x1d\xb5\xb3\xa9\x12s\xda\xab84\x16t\x00\xdd\xc7J\xdf*\xbc\xa4Z\xd5\xc4\x0c\xaa\xb2\x84\x8d\xf2k\xc6\x05\xcc\x15#&\x00\x13\xa0\\\x14\xbf7\xc7\xaf\xc8\xe6\xf8\x15\xd9\x1c\xbf\"\x9b\xe3Wds\xfc\x8al\x8e_\xfc\xc3Pw\x1a\x8a\xc8\xb9\xcb\x92k\xfa\xb7\xf6\xd9\x9a5\xfadi\xfeX&k\x8cv\\ip\xc7\xf2?\xd9\xe5Jx\x18bq\x992\xa7\x9a\xd6\xc8\xe8\xd4\xf8\x19\x07\xa7d\xa0Z\xb2\xfc\x07$t\x06)\xbe\xab}j\x17\xdbT\xbe\x83\xaa\x1c\x9b\x14\xdf\xc1l\x9b\xa6\\\xbch\x10t\xd1>\xe9\xc6\x98T\xbc\xd1y\x0d\xef\xe8\xb6\xceO\xab\x90Yd\x1dg5r\xa4O\xeb\xd7\xf0\"\x11\xdc\x03D\xf0\x19\xbcS\xe0|\x8d\xe7\xf5_;\xf0ug\xd2Z\x86\x00\x93@\xd5bg\xfc\xa4=T@a\xb3\xe6\xb6\xac\x06\xa3\xa50\\\xfb(\xcf\xa7\xcc88\xd3\x90\xed\x99\x18\x87Nwg>\xccj|\x84Z\xff\x171\x16\xcf\xfftb\x8c \x8b(\x15\xfa\xd5|a\xb0\x8b\xd3\xac\xba\xf0\xc3WL\x91_\x15_?\x82 \xe5 u3\x8fr\xe8\x0f\x1f\xc3\x0c\x9e@\xf6\x18f\xbd\x9e\x07\xd1tv\xae\xd7\x9c\xce\x0ca\x01\xc5R\xc6x\xe1\xd1\xe6\x9c\x8b\x18\xd8\xca-fA\x14 \x96\xc1|\x98\xf2\xba\xe72\xf4b\x84IZ\xc3\xc1,J\xb2N\xeeV\xc2\xc5J\xb7\xfd\xa11\xfc9G\x85\x10\x7f\xbbU\xffz 4\xc3\x8bZ5\xa6\xc77\xe3\xb7\xe0\\_\x96\xe4ub[\x1d\x0d\x9eqwcj\xba\x03;\xa4\xd3\x15\x96\xa6\x1d\x86\x10\xeeb\xf1\x0e\x84\xf1t\xf0\xec\xec\x8d\xbd\x14\xdfm\xed\x04-\x90)m\x1b\xcc`\x98\x0e\x15\xa1)\xd6\xc1\xa9\x81sS\x8aT\x87\xaf]f\xcb\xd0\xd0\xc6\x8a\xe7\xe1U\x8dT\xeb\x8f\xbaV5\x06g\x1e\x06Q\xb2\xecoo\xacWq\xbfH7\x97\xc1\xec\xfd\x1f\xea\xe57Z<9\xa5>^\xcf\xff\x8d\xfaZ\xb1`\xfe)\x9d\xad\x0e\x95\x1c\xe8<\xbb\n\xc2(\xb8\x8c\x18\xea\xfbI\x1a\xfe\"\\\xb8\x9a6\xfbr\x9b\xe7h\xe0\xb5\x0f8\xbf\xdd P\x89\x92\x9d&\x86\xfc\xa0\x8f\xd3k\xa8\x91\xc4\xba\xb9 \xeb\xec\xbc\x02\xd9\xd5\xb2q\xf4\xd7\xe1<_\x8d\xc19\x186\x0cd%\xa2;\xf0R;\x8f`\x9b\xd5e5\xfdY\xa5l1\x06\xe7+\x9c_\xc3 n\xa20~\xff}\xa9\xb0\x05y\x91\xe9~Y\x00\x9c%q\xce\xe2\xdc:\xfbh\x80|\xee\x8c\xfd\xcd\xf5\x06\xeb`S\xcaI\xdex\xfd\xb7\x85~\xce\xda\xcc\xb6\xc8~[\x0e?\x9e\x9d\xbdi=\xf0\x98\x17,\xc1\x1a\xb7D>e\x13X\xcb\x19\x96\xce\"[\x0f\x81*\xa6\xb8\x96\x93\xdb\x92\x91\xaf\xc5\x00\\1{\xd6\xdd\xa1\xe5c\xb3\xb4y\xf8\xd4\xbe}9%\n\xdf\xfeK_\x12\xcf\xbf\xf4\xa5\xff\xc5\xfa\x92\xe0|]4\xa6\xce\x97S\xf2\xeez@\\\xd7/\x06\x1a}|\x93\xa8\x83g\x9bI&\xafim\xe6\xd4\x15\xffR\xda\xccO,\x80\xac\xac\x8dy\xa4\x8b(\xd9\xedU\xb2\xd9n\x1c4,6+u{{\xbb)>\x89\xa8\x13\x14\xee\xce\xde \x0b\x7f\xb1D\x13\xf9\x92:\x10\xef\xb2\x7f\x9d\x06\x9b\xcd\xa7\x08\xbc\x1d\xe4U\xad\xb3\x04\x8e\xc0\xb9\xccc%\x113\x88\x92\xd9{6w`\\\xfd\xb0\x8d\xc5\xa7\xae\xf2\xaa\xf8\xb5\xf3\x14\xb2M\x10kR\xbb\x1c@\xa3\x98\xfe\xcf\"\xe5\xe2\x82\x7f\xa5\xad\xf1W\x1d\x96U\x13|\x1b\xea\x9bG\x8c\xf4\x14\xddkm#\x8f\x85u\xf8_\x92\x0d\xfcK\xb2\x81\x7fI6\xbf\xbddc\xbd7\xc0\x06Y\x9el8\xd4\x07\xcb\x80\xf8\xb0\x99\xff\xc8\xcb\x05\xd2z,:\xb1\x88&\xe8lop\xa9\xff\x9f(\x8e\x94\x1c\xd5?\x8dy\xef\xc6R9\n\x96\x85\x94\x8b\x0b\xceH5\x9am\xf8\xda\x81\x0b8A\x1a\x06\xfd(\xb8d\x91c\xea\x06h\x9c\xd6\x8e\xe4\xf7\x0e]}!>\xfeO\xc2\x93\xd9g\xf2\xe4\x86\xfa\xe6\x11\xff/\xb4\"\xcc8K\xad\xf1\xd4D|\xa9q\xe1PV11\xdb\x99\x89\x0bo\xc5\x87\x1a\x17\xce\xc4\x87\x1a\x17\x8e\xc4\x87\x12\x17\x9e\xc9\xc8G3\x11\xf9\xc8\xc4\x8fg\xbf=?^t\xe5\xc7\xb6\xb0EU*l\xe5\xb9W\"\xafz\x95\x98[}g\x92:\x0fl W$\x16+\x18$1\xa7\xcd\xc7\xab ^\xb6g0\x02\x8d\xcf\xb1A\x1c\xac-\xbaXP\\[\xab\xb0\xe8\xbf\x7fDL`&\xf4\xe3\xfc.\xc3\xbb\xee|H\x9d\x06S\x0fb\xc7\x1b\xa9\x1f\xdf*\x15\xca\x0d\xc8\xe3\xd7\xd2}\x94,M\x91tv\xe8\xbfY8\x08\xda\x14t\x8a\xab\xd0\xc9@B\xc1\x154\x93H\xcd\xe6\xdd\x1a\x80U@\x819\xa25 \x1d\x19\xe4 \xc9w\x96\x99\xc5b\xcd\\s:\xd3\xa0~\xec\xbe\xc3b\x9a7\xb3\xe3Y|P\x84\xfa\xe0\xbf,8\x0ee\xd9)3\xcaN\xc1?@vj6\xe2t1\xf6\xc4U\x00i\x83\xa5\xee\x87\xeeyW\x1bR\x88\x85\xbb\x9d\xd0\x07t\xd2\xcd\x91\xff4g\xeb\xa6\xabH[*Jy\xe0\xda\x8cO\x19\x15\xfe\x96d\xc8\x96\xa3\xf6\xa4do\xb2\x97\xa5\xc0\x19\x8b0\xcaY\xfaIH\xb7\xb77\xc3k?\x96(\xea\x80\xd8g\xef\x7fc\xee\xbfc\xe7r\xe5D\xd4]\xbc~\x94\xdfnXC\x8c\xd8\xa6\xc1\xcc\xbf\xcc`&;\x0c\xa6Q\x8f\xb0\xdd\xbf\xd8\xdd\x088K\xe2<\x08\x9b\x0e\xd9\xf7\xf66h\x95\xe4b\x87\xb5\xdfE\x92\xae\x1b;Nb\x8a\xf2\"o\xa5(6h\xebvS\xa6\xf6mI\x97Z\x16&\xe8t\xc2\xd9v\xba7[\xb1u\xd0z`\x18\xe3\xf2\xb6\xb4\xb5\xd3\xe9\xa6.\xc3\x8c\x81\x95d\x9a\xe6\x9a\x81vy\xad\xe5\xdeK\xf9\x08\xf5\x13\x8e.\x0bN\xea\x7fA\x00\xbd\xcc\xe3VK\xb5\x00P\x8e^\x0b\xfa\xf3\xc8:\x82\xack\xef\\e\xa6\xa3yi\xa3\xee\xac\xcdjR\x96m\xc8\xce\x0fX\xc6\xf1`\xfciC\x15\x1e!\x84H\x1d=B\xeaS*\x00\xc4\xba\xb8e\xeb\xf8'\x8d\xb5e\x0c|\x8b\xe7I\xdc\xe4\x97\xb1\x83\x97\x8as\x8cn\x1bh\n\x9bs\xa25o\x03 \x01\x94t\x18\xf0E 7\x9b%\x1b\xd6\x9f\xb3E\x83/\x87\xa5\x9bMq,q\xc6[\xc9 H\x19l36\x87<\x81e\x1a\xc49\x041\x04\x9bM\x14\x8a\x80\xd3\xf3p\xb1`)\x8bs\x88\xd8\x15\x8b2H\x16\x10\xccf,\xcbx\x95y\x90\x07\x90\xc4p\xc9VA\xb4\xe0\xdf\xf2\x15\x03\x16\xcfy\xa3\xe9\x00N\x82\xd9\n\x9e\xbd:\x85up\x0bs6\x8bx\x7fI\xcc Ia\x9d\xa4\x0cp2\xd9\xa0i\xf7\xf5Q\xf3\xa6R\xf6\xb7m\x98\xb2\x0c\xbbZ$Q\x94\\\x87\xf1R\xb6\x04Dg\x80b\xe1'1\xcb\xe06\xd9\xc25\x9f\x9a\x9ac\x9e\xc0\x19\xa5\xd1\x85\xb7\xa7\x03\x07\xe3\x03\xef\xc6\x81?\x8d\xfb~\xac\xbb\xd64J<\x9f\xcb\x91A2\x9f\x06%\xc5\xbe\xf0\xdb\xb6\xa6w`\x00\x92\xbd\xb5\x05\x8dA\x10oR\xa9\xda\x19\x04\xa7z\x9ft] \xeal\xa3\xa2\xe4b\xbf7\x1b\xd5\xef\xf2<\xc8\xa7?,\x96\xa8\x7f\xb6\x93\xa1\xffy\x17\xb6\xbe\xa8\xda\xdd\xa6T\x8b\xd0\xaaH\x0b\x9aUo2\x905\xeb\xdc\xbb9\xbaw\x93kC\xe5\xe3\xd1\x16\x1a(\xd8\xc1}^h\xdc\xc1&\xfc3\xbb\xe5\xc3hR\xa4#*|\x19d\xe1\xac\xad\xecL9\xd17+\xdb\xb9\xce\x9a\xcc\xda_v\x1db\x06\x93E\x13C\x9a\x05\x19\x031\x0fgl-\x06bh\xb6\x83\x8dV\xce\x02\x1d\xb5&\xe8\xae9AW\xed j\xfaJ\x87\xc8\x1c:+\xec\x10\xf9c'\x0d\x0dHF\x15\x1a\x9a=\x8d&4\xe8\xf6\xf2\xb9LY`9V\x05\xb5\xbf\x08z\x9f\xb1\xbd\xd1\xbf\xb6\xf7\xf7\xb9\xbd\x92U~\xf2\xcev\x928A\xedn\xf3\\|p\xde\xc6\xef\xe3\xe4:Vas4'nTB\xc1\xf1a\xd1\xf5v+t8\x0bo\x1b?\x8d\x1bz\xe0\xf4\x7f\xde\xae7V\x15\xcb\x90h\xe6\x7f\xf8 \xe8\xefR\xba\xfc\x97L\xf9\xbfD\xa6\xe4\x82V\xd2@HU\x1c\x00\xd7A;E\x93\xd0\x14\x17e\xd7,\xcb\x82%k*\x9d\x16\xa5\xb3d\x9b\xce\xac\x02\xd4\xe7\x92\x1e\xdd\xc6\x83\xb3\xb5\x85m\x05\xcc\xd3}\x1b1\x13\xe4\xea\xcfe0{\xbfL\x93m\xd4)\xd5\xe7\xfbm\x80\x1e\xf5\x07\x97\xe7\x1f\x16\x98\xbay\xa7\xa1t#\xaa\xc9\x95\x16t\x7f\xea;w\x8a\xd4\x10\x9c\xe0\xe14\x1c[z\x9c\xfa\x92\xdbX\xd8\xef\"\x94w\x1b\xdc\x83.(u0\xb2\x81\x12\x95\xba\x99\xc4@\x19\xe6\xda\xf7.\xc44\x8d\xcei\xbc\xd9\xe6m1v\x03*\xfb:\xb9n+\xb9\xa5\x92\xc7I\xa3\xb0\x08*\xff$\x1e\x19\x9fp\xc1\xac\xad\xfc\x8c\xca\xff\x18\xa4\xef\xe7\xc9ukX`\xcaB\xe9\xfc C\x9d\xbe\n\xf2U\x9bO\x0e\x08\x17\x96\\\x04W\x12\xa4\xa9\xb9\xc2\x1c Y\x10E8\x85\xcc\xf5v;\xf0\x92\x8fdo$\x11\xf3%9\x9d;\x1e\x9e\x7f}\xba\xe9\xa2\xdb9W\xcb\x19\xea\xean{\x99Y2g\xaaT\xa2\xe2\x04\xbb\x0e\x07B<\x07t\xfe\xff\xff\x0f\\2pz\x8e\xbd\xa5E\x9b\x11\x84\xa2#OU\x16\x19\xcd\xe7\xce\xf1!9\xb7V\xc6\xb4\xb6\x9bF\x87\x98\xd5}\xc3\xf5\xb2y\xd3\x19j\xd0\xb62\xad\xb7\xf4I\xf7\x19\xcb\xf5\x9a\xb3l\x96\x86\x9b\x1c\xa3^7\xcf\xe5\x93\xc7\xa4\x1f\xfc\n\xbd\xa8\xeb\xd6\x96w\xf5\x8b\x8d\xe24\xde}\x0ca\xfc\xd9#\xa0;\x13j\x14\x88\xeec\x07\xc1\xa4\xc1\xf1\xa04\x18\x07\xbe\xc1\x07\x1a\x9dB\xb6mC \xdb\xc0Dx\x8ep\xe5\xabE\xcd*L\x9e\xf2\x92\x06\xfel\x82%\xcf\x87yS\x98\x8a\xae\xde\x83\x9f\xe4g\"\x1fT\xcd[\x0f\xb2\xa1\xfd\xe4\x1d\xc0\xea\xefD\x9f:\x0b\x1a\xa6\x80\xa9\xa6\xc3\xec\xf2\x907m\x97\xd3u\xc1\xa2N\xbbK\xbb\xa67e\xdd\x85+\x91\xfa\x8e\x15\x97\xbcZN\xe3\xc8[6\x0f\xd2%\xcbi\xe3\xede\xe5\xdd\xb7\x8a\xbf<#\x91\xbcmg\x85\xc0ega6\xf6\xc5\no\xfd\x10\xd3L\x87\xadz\xfc\xbf|\n\x8a\xe7\x93\xac\xbe\xffd>\x05\xb0\x9bN\xde\xe9f)\x88\x9e\x7f\x83\xc4\xdc\x0b*\x186\x8cb\xdb%|\x05\xdf\xd1m\xab\xde\x11a\xa9f\x9d`&\xf3a\x0b\xc1w\xb0\xcdXj\xbfP#v\xbfK\xf6RR\xce\x1b4o\xa9\x9c7\xccS*\xe7p\xd4Bs\xe4\xa8m\x8a<\x7f>r\xf0\xb4\x9a\x19\x7f\xeb\x94\xa8\xffp=\xbf\x8bc\x06\x94\\HZ\x95\x0e\xbaM,\xf5\xfcX\xd3\xf39\xda\xd8\xd6\xbe\xbe\xf0\xffK\xb5\xfdv\xed}\x978\x93\xf0;\xd0\xf6\xa3O\xd3\xf6wS\xdf\x17\xbb\x99\x08\x0c\xda\xbe\"z\xedj\x7f\xf2\xab\xaa\xfduc\xa3\xfetP\xfb[N\xccH#\xb1GH,\xd4~\xe7\xdb \x0bg\xe5\xe8\x88\x8e\xbdj\xab\xce\xdb\xac\xc3\xa7]tx\xfb\xb0\xad:\xbc\xadJ\xd0\xb6\x14\xad6\x89O\xd7\xe1?yLU\xdd\xf5\xad\xe4yR}\xb5V\xac\xa8\xaf\x8e\x0f\x1b\xfc\x9f\xeb\xaf\x0d~e\xcd\xc3\xf9\x82\xfa\xabpC\x9f#q\xa7?[j\x10\xafw$\xde\xfe*\xfa\xf1\x17\xdb\xa8WA\x96]'\xe9|\xe7\x8d\xd2\xed\x0c\xbf\xde>\xed\xbe\xfa\xc16O8g\x8bX\xcew!f\xd7\xfd\x8d\x98c\xb7}\xebXZ@P\xc7\xd2\x9f\xb6\xcb_\xc4\n\xf2Y\xde{\xff$V\x10\xd3\x11yy\xc8\x8b\xdf\xbf\x15$\xd5\xac \xf6R \xda\xf7;\x18I\xd2\x16\x99\x8d\x1c\x9b)\xb5\x176gf\xe0\xc14<\xe7\xb2\x85\xaf\x9b@\x9a\xe4V\x94q\x03\xf3n\xa2\xe5\x84Y\xa3\x0b\x94w\xf5\x9f\xc9\xc7aa\x8d\x1b\xb2\xb0\xf98,l>\x0e\x0b\x9b\x8f\xc3\xc2\xe6\xe3\xb0\xb0\xf98,\xc8\xb2R\xfe\xc0\x05Yw!M,\xfc\x8fGw\x1fxf#\xcb\xe2\xb77\xb2l\xbe\xa4\x91\xe5\xf7\xe6\xf80\xff]:>\x04\x9d\x14\xee\x85*\xd9A\xc3\xe3\xbb8\xe3 B\x17\xf8\xb3\x06\xc5\x07\xa3\x98\x0c\x8a\x04d\xae\xd0\xc8\xed5\xae`Bb\xf7\x86$\\%j\xb5f\x16]Wj\xce\xa2\x90\xc5\xf9\xa9H&\xba\x1a\xc8\xdfm\xed,\x8d\xed\x9c\xb1Y\xca\xf2r[\xf4\xae\xad\xbd\xdbJ{R\xacx\x8379\xb0\xb6\xc8Q\xd8\xbfL\xe6\xb7\xceg\xbb\xa7\x04\x9b\x0d\x9d\xb5\xad\x06\xe2O\xfb\xe0\xbe\x84+\x0b]\xdb\x1c\xc3\xf4\xbc\x01\x14\xc5\xe27\xa6\xdb\xd4W\xb51\xb9favkH\xea(\xd7y\xdc\xb8;\xfan\x8c\xe1\xd6X\xee\x1f\xe0\x8e\xf3\xab\x18\x9b\x9a%\xbd\xaeaU@\x85Vi\xa3?\x00\xbbEV\x81]\xa3\xab\xc0\x8e\x11V@\xb0\xe1\xbc\x83\xcdkKS\xec\x96/\x05\x8a0+\x9d\x8c^\"\xa9I\x07\xa3\xd7\x82Jv0zm\xba\x86y\x01\xe9J\xb2\x83\x85lE\xe5w\xb3\x90]Q\xa5\xae\x16\xb25\x9e\x1b\x84\xd9\xcbgg\x87\xcd%9\x89^\xbb^-\xfe\xe01\xd7c1\xea ^o\xc7\x9f\xcd-\xdd\x16-\x11\xf59N\xd9\x9c\xc5y\x18D\x19\xb5T\\\xa4oi\xea\xff\xb2\xf7\xef\xebm\x1b\xc9\xa28\xfa\xffz\x8a\x12fN\x06\x1c\x93\xb0(\xdf\x99(>\x89-\xef8c\xc7\xde\x96\x9d\xcc\xda\x1ao} \xd0$\x11\x83\x00\x02\x80\x944\x89\xdfe?\xcbz\xb2\xdf\xd7\xd5\xdd\xb8\xf6\x0d\x94l\xcb\x19c\xd6r(\xa0\x80\xbeUW\xd7\xbd\xe6\x98\x04\x06I\xfc\"6/\xeci\x0d\x8eu*I\xc8\xe2\xf9\xd9\x91\xc0\x9f\x14\xfc\x96\xfeSg\x98)\xba\x9d\xb9\x07\xdf\xf7\x0d/\x1e\xa1\x15\xe6Cj\x16\xe5\xc2\x82\xb8t9u\x80W\xc5\xdf;\xbaT\xa7\x9c\xad\x1fG![\xbff\x88\xbf\x08\x040\xf4\x0fsC\xe8;y\\/dK\x1dgT\x9a^\x99\xaf\x94?\x06\x07\xdc\x17\xdfm\xca\xd5\xc1\x18\xe8\xed\x16\x1a\x823\xd2\xb9\xbc\xacL\xca\x02\xbd\x0e\xd57\xe8P\xcb\xba\xca4\xe7Ft\x1e/\xab;\x0d\x9dj\xbd\xf5\xd0g\xa7\xff\xa5J\x9b\xc8\xde8\xd6\xb9\\mM\xc3\x14\xaaU\xd9Zj\x868\x86\xb3\x1d=\xbd\\'Z\xd3\x11F%\xc3\xcc9\xdd\xf8s\xfc\xb9\x1ci\xbf\x99\xf5?\xc9R}\xbcy\xf5l\x80{SRo\xd8\xea\x13o\xf2\x98\xe5F\xa9\x19\xd5~\xef\xea\x9f\x17\xd6\x1d}\x9d\xbe#\xac\x83\xd6\xfds\x1a\xb8\\\xd2\xd7\xab\xcei\x1b\xd4/s3F\x077\x88zm\xc7\xe0<\x89\xd3\xb3\xe13\xca6\x1e\xfa\"\xd6\x93\xb8\x87\x93\xf8\x10!5\x0e\\\x81i\xe7\x1b\x01*=\xb0~\"V\xe5:~\x82AB\x98\x01\xe5\xb4\x92\xb4\xb4\x13\xb2ij\xff\xcf\x068\xaf\xb57pe\xf9\x12;X\xf5\x19\xa3E\xa4\xf4\xe71\x15\x17\xa6\x9a\xf8y@UE\xf1\xaeL3\n\xa8\x1b\xa0r8\x11\xf2u\xa6\xdeDa\x7f>\x0dl\xb7\xb5\xb9\xc2 \xfd\xd2\x9f\xe0'/a\x83@\xfe\xd4JE\xfd\xb1\x11\xb0\xda*Z\x04\xcc\x9aV\x8d!\x08h\xe3=\xf9\xf9b\x9b\xa5\xb1b\x98i\xa3\x8dq\x96/}\x16\x18'\xc6r\x8a\xf94\xb4\x08\x87S6\x14\xd9\xda\xd4\xae\xa9d\xf8|(^\x81r\xafqR\x11 \xdb\xf3\xb9\x0bV\xbd6\xbf\xb8\x1bfiF\x98f\xdc\xbf@?B\xaeoi\xab\xe9\xb48\xf3\x8aA\x02B\xea\xf8\x95\x81=`i=\xb4M\xd7\x0e\x14W\xd9\xf0o\x1b\x92\x1b\xc6\xfc\xbf)\x08d~\xee\xafII\xf2\x02}\xe6)#\xc99E\xd4t\xaa9^|\xdce9\xbf\xfaJ\x8c\x19\xd9'\xc5\x96B\x1e\xd4\xdd;\xa3\x9f@f\xbc\x01'\x14\x8fZ>\xf5\xea\xe9\x0bk\xf642\x1cf\x15\xd8`\x02\xf3g=\xcd\xea\x89\xb3:\xc8,\xd8\xa6\x86\x9fA\x07\xbd\x0c\xda+\x86\xfa\x12\\\x1aB\xde*+\xc4\x87 m\xbd\xfduE{\xe9\xa3\xef\x93\x82YWl\xf6\n\x03\xfd\xb2_\xda\xfb\x85O\xe0n\x18\xcd,.W\xb5\xdfd\xf8\x7fl\xd3\xbdK\xec\x81=$\xfb\xa7\xf8\x8fe:W{-\x01W\xc2\xee\xb4\x92\x98\x9d\x9d\xe3 \xd3\xef\"\xe6\x9e\x0e\xcb^\x0df\xa5\xa1\xd1\x13\x12\xacS:]j\xe2\xa03y\xc1\x8a\x04\xef\xe6\xa9\xa2 \xb8\xb84\xadZEt1\x9cc^\xdfV\xe9\xc3\xe8\xdea9\xa2\x1c\xb8\x01s\xfc%\xba\x8a\xb7\x84\xfb\x8c\xd9PD\xaf0*(i\x08gpf\x06\xe6[\xa9\x9a\x19\xf3\x1b\xf5\xce ^\x9a \x1e\x19\xb6\x05p\xdd\xe4% 54\x89\xb5\xf5|\xed\xba\xd4\"\x9d\x8a\xb9OM\x0c\x8bJ]~\x170M\xc4.H\x8dTp\xe7Q\x9au\x94\xd0iO\xaf\x96\x03\xd6^r9\xbd(t\xdal\xea\xbfMM\x97\xf2\xb2\xd4\x15\x84$\xb5\xef\x18\x8e\xae\xc2\x03R5\xe0\xd0f\xb8\x1f\xcf\x03\xf2\x92\xf87<\xeb=\xb0\x859G\xc9H\xc7'eC\xda\xd6&\x887\x1e\xee\xbd\x0c\xf8\xba\x9e\xdb$\xc0\xff4}\xaf\xde\xd2v\xbf\x91\x15_\xb3\xfa\x97\x1d\x81Ej|\x18\x90\x1e\x1fx\xe7\xab\x14\xf9R(K\xc7\xddz\xcc*\xc7\xdd\xf0\n\x1cw{\xe5\x95\x94\x94\xa3\x94\x94W\"\xbb\x97Wj\xe3\x82i$\xc0GS\xd6n\xc3\xea%\x1b\\\x04\x8b\xe4\xb9\x112\xad\x1dq\xd0\x15O\x0d\x19\x0dq\xc1\xf1\xe1\x10R]\xe2\x92\x8d\x88\xf4\xac\\\x00\x15\x0en^\x10\x13?\xd7\xf8\x1f3\xc7\x82\x19\xe8Y2\xce]\xf9\xfa\x82\x1c\xc2\xd8\xcb\xe0\xe4h\xce\xbd\xb6\x02\x81\xc7#C\xdffU\xa4\xba\x16\x8c\xaf\x94\x96M\xad\x17T\x9b{6`S\xaa\xcd\x7fK\x9b|$\xe06\x8a\x91*\x11\xbc\xc5mZm3\xe1\x1covw\xcf\xd1q\x02\xb9H\x9doj\x8a`\x94\xc1/D\n\x019\x06E\x0bp\xb1\xcc\xf4d\xca==\x18K\xca\xcbJDIH\xce_,\xdctd\xf2\x97\x8b\xa0\xf72\xaf\xa0{\x92\xbe\xd5\xf8uXy\xd1C\xc3crx\x15\x1d qA`/g\x1e\xda\x8a\xf1\xc1\xb7t\n\x18\x84\xb9C\xa23\x9d\xcf\x0dv\xba\xa9\x9c\xc7\xf7\xb4\x89\x84\x94\xf5\x8148\xd8P\x04\\1\x0e\xb6\x91KOY0\xaa\xd5\x14\x9e\xe1\xcbsX\xa4cPE\xdf7\x16\xc9WO\x02\xe3\x98\xacF\xdf?\xe8\xd4\x1e\xe9\x89\xcdy\xc46\xaa\xd5y\xc4\xe6\xd3\xe6_\xfb\xe7\xca\xbf\xbe\xf2\xb2M\xb1r\x9d\x9c\x14Y\x9a\x14\x04\xed\xca\x87\xa8\xd3WP3E\xde|\xd6^ev\x1c\xd2\x1a\xba\x9c\xed\xd4\\\xdf\x95\xf8C\xcca\xcf\xf3y\xc8\xe0\xd8T\xb6^hS0\x87R\xa0d\xe9\xc0\xe1!\x92\xd1t\xc1\xa2X\xc4\xe7*C\xdd!\xaa\xff\x12\xfa\xc17\xaf\x9eV\xb2\x9e\x9bu\x03\xa5(A\xd9b.\x03Vr\xeb\x15 \xa3\x9c\x04\xe5\x9bZ\x9f\xd1\x13\xe8t\x0c+\xfe\xd1\xaf\x9c\xd1[\xf6\x93\x8bS\xa7\x95\x84\xe1\x8b\"9\xa6@\xb09\x8b\xe5\xd4\x19\x89\xba\x06\xa2y\x99Lp\xee \xcd\xe6q\x1a\xbc\xc3\x12\xeey\x1a\x9f\x9e\xceK]\x08c\xdbF\xc4\xff\x92B3\x0b\x11\xf1sI\\\x94\xb1\xde\x89\xa9\xce\xc9\xf5\xcc\xa1\x8aD_\x9a\x03\xe4Z\xd69\x19\xb3\x1f\x07X\x15\xd9\xbd\xf7y\x9c\x05\xd0\xd29\xad\x88\x1f\x92\\b\xf53\xed\x19\xbb\xe0\xc9F\x98\xa1\xa0=\xc0\x9b\xd4\x17\xb2\xce\x1b\xd9\xc1\xbb\x12L{\x81\xcc\xc9N\xea\xd1\x86\\d\xfc(\xc3e\xae\xe9\xa2I\xfb\xe1\x8e\xc1\x81u\xe1\xe8G\x1d\x1aGm8\xf3\xa1M\xa0%Y^\xc6;gr\xb1\xa9\xa7\x06=*\x06W\x9c\xdb\xa1X\xa5\x9b8\xac\x08\xe1\x9b,\xf4K\xdb|\xac6\x15\xcd\xeb$\x0e\x9e\xd0\xf9\xa0tI\xea?\xff\xf8\xa3 E\x0fq\x0e\x81?\xdbO\xd9\xf1\xcd\x9f\xf3?\xda\x10aTd\xb1\x7f\xc11\xeb\xb1P\x7f\xb07\xe4\x0f\xa5c\xf8\xdcR\xb2\x8a\xe9\xd4\xc3\x0eM\xca\x9a\xd6\xf0\x06C=T\xd5\x8e\xe5\x93\xac\x7f\xd3\xafx=\x0b3?T\xcax=\xc7\x07\xfc\xc8\x12\x98\xa2\x87\x0c\x98\xf3\x00\xba\\<\xdfPi8\x14\xe4\xe9!\xf8\xde\xbau\xebI\x9a\xbb\x9b1\x14#\x98\x81\xef\xe5\x9d\x9b\xfa\x86B\xa8\n(S\xa1{cL\xa9\xb0\xa2\xa7+\xcf@$\xd7\x974\xafm\xfd\xf9\xea\x10\xf1\xca\xf4\xc7cSE\x97u\xfdb\x92\x96\x8f\xd3\x00I\x12\x86\x87k\xdf[\xd6\xef\x11\x9b\xf4\x1d\x175<\xfa.\x1a\xc0\xe75x\xe3\x98\xd0\xber\xda\xb7{n-\xd2VlO\x1c\xca\x9f\x92\xa4\x9c`\xe4\xd8[JZ\xb6'\xce#~\x13\xa3\xc24y\x85\x80\xeb\x94\x12\xd7 ,\x16\xea\x9c\x81\x8a\x8d\xfb=\x0b\xcf\xd2\xber\x0c\x87]wm\xa3)\x1c,\x0enk_W\xe8p\xf9\x0c\xc3\xe2\xc8\xe8\xf5%.\xa4\x95z\xa7\\\xe0l=8\x98\xe3\xcc\xc1\x90\xf7\xed y\xcb\xa2\x15\xb5\xef\x9a\x92x<\xa2\xe24\x1e\x06\xc7\\\xe0\x96\x8b\x82`1iMn'\xd0E\xaa\x1c\x99f\x96\xd3\x0fm\xe2\xf6\xd1\x18V\xda\xf4\x06v\xcc\xd7\xed>\xf3\xf5\xe6\xd53-\xdf5\xd4)TD&\xd2-\xa0\x1e\x8f%\xa3\xb7\xd2\xa7Xh\x8e\xe7\x98\xe4[\x92\x83\xd8O\xda1a\xf0\xcc\xc0Q\xb1\xcf\x16\x13\xf6\xeeN#+\xe9~1\xafR\x99\xef\xd85\xb6\x1dw\xec[8\xa8\xd1 \x8d!H\xe3S\xd6d5\xeb\x13z\x8f\x1fk\xban8h$\xd4.\xd1\xd5\xf5\xc7\xca}\x9cv\xea1)\xfd(.\x0cy=J\x8c\xa4\xfdP\xab\xf8\xd1Vo\xe8\x92\x85cX_e(S\xd5\xfe& kfc\xa7\xd1G\x8d\xe0\xba7\x8d\xaf\x81S\xf9\xf8_1\xaa\xed\x84_K\xdd\xf4\xb5\xca\xf7\xb6\n\x8e\xc1\x0d<\x04\xe1\x86\xb8]\x95\x99\xae\x03\x18.4\x9f>7\x0e\x8e183\xb80\xb0\xc8\x0c\x8e\xa5'4\x04\x17m\xf2x\x06\x06\xe6\x9c\xf3\xa7\xda\xcc\x89\xf4j\xca+\xba\x98\xb1\xf7\xf5|<\xd2\xcc\x871\xb4\xb2\xea\xd7\xb1MS\x11=\x96\xe7\x97 k\x10|\xed\x0c\xe6\xe6\x06\xd5\xe1-\x97\xf0\x85\x97\xeb?C\xbc{\xdd\xf4\x9f+\xa5\xfe\x13\x9f\xf4\xb4\x96\x91x\"S\x80\xaed\x9a\xd1\x0d\x7f\xd0\xd3\x8c\x16\xfcA\xaf\x8d\x98?\xe8iF\x03\xfe\xa0\x97\x1dy!\x1a\xdf\x7f\xd0}\x94Q\xf1e%\xb4\xa7h}\xec@\x84\xa2\x83\x8a\x9aU\xab\x8f\xafO\xdd\xda\xda\xd6T\xa9\x94\xa5&*\x99\xfd\xac\x99B\xb9\xb0Q\xbcEm\xc5\x9bE\ne\xac\xd0\\\xc7]\xbc\xc9\xe3!\x96-\x9eU\xb9\xad\xce\x90\xcb\x19\xc2LG\xce`!z\xe9\x12o\x93\xc7.\xe6\xe5\x17;5N\x99\xa3\x00\x95\xe4\x99;\x87+\xd1\x14\xca\xe7*\xe5s\xd5\xd4\xe3\x8c\xdc\x91\xc7\x1d\x8f\xd2\xbc\xe7\xf3\x04`\x9d\xe3\x17\xc9|\x7f\xbaT\xba\x86f\x9b\xb3\xa6\xabd\n\x0f\xc1Y\x95eV\xccn\xdeL\x13*Q\n\xbf\x06/JoV\xef9 \xab\xaa\xd7K\x8a\xab\xb4\xb1\xc5\x0d\\\xa8\x15\xa6m\xcb\x9b\xd2\xc6\x16\x08z\xf9K\x14\xc7\xafH@\xa2-\xd2\xb6\xc2\xc2\xec\xa6\x94\xd3\x85\xe2}\xf8\x12\x81\x88;\xb2p\xac\xc7uB`\xdb\xa5\x02\xddr\x95\x03\x96K\x1eZ'\xf3\xb1o/\xa1\xec\xd4\xbc\"[\xa7\xd8\xa9t\xce\x1b\xba\xe3\xf6\xe4\xd3\xed\xab\x9e\x1a\xb1d\x99W\xf8t.\xffM\xde\xe41\xa3Bu\xb1\x83j\xf2TqF^\xb0\xc9s\x92\x94OXj\x08s\x85\x93-%I{\xcc\xf9\x03\x7f\xbb\x1b,4\x97f\x05\xff\xc6f\x0c\x18\x9f\x88~\x16{Q\xf1\x93\xff\x93\xbbB\xfd\xca\x8a)0\xc4K\x1b\xaf\x88\xa3\x80\xd0M\xb2\xd2U\xc9m\xf9dlzy\xc5|\x13\x9fDw\xc3F \x87\xeb\xa4\xd5:\xea\n\xba@=dU\xbf\xac\x12\x92\xb1\x9d]\xb5\x89\x89\xf5\x0c\xf5\xb5\x00\xb5 \xcb\x17\xf3_\xad\x12\x99\x95\xfeR\x9b-F\\\x9d\xdd\xa7\xcdB\xd3~\xa7\xca[\x93\x9a\xdf\xa8\xf7\x9f6\x8bC\x0b\xdc\xc2& \x8c\xe7\xe8\xae\xbei\xe9\xa1!,\xf0\xe5\xcf|L\xa3m|\x0d*\xb2\xc5\x8d\xc5\xe5*5:\xf1\x89+\xc5@M\x816\xcf\xa2\x82\x9e\x8b\xb4ez\x98&c\xc8u9g\xc4\xc5\xd1\x8f\xc7j\xba%\xaf\xa3\x85\xa5\xad2\x98\xc1bTi \xf3Q\xad\x16\xdc\xb9\xb0\xba\xb8XJ\xd1*3\xa4\x05\x9a\xd0\x8b\x9e\x1e/\xb1\xac\x90\x05\x96\xd0+\xcd\xac\xd0\x1b\xaarE\x169@\x01\x83\xb9\xe9JY\xa17T\xdb\xc7\x08\xaa\x91\x8c\xd8\xe3F>D%d\x13\x8a\"3\xa6\xb5\xfd\x06\xa6\xbaB\xde\xab[\x0d\xaf\x8c\x9fR\xa8\xc9\x17p\x856D \xce\xfe^]8\xe9R\x96mYy\xe6\xcf\xc9\xb2-\xad\xe1\x9b\xaaj\xf8F\xaa\x1a\xbe\xbe\xaa\x86\xefFU\xc3\xb7P\xd5\xf0\x8d{5|Y \xcf\x82K\x05m\xe8@\x04\xcb~\x16%~\x0d\\\xfb\xa7\xe4\xd8\xafi\x88\xe0\x10\xee\x9cq\xe6\x8c\x1bPC%\x02J\x0d\xc2\x8e\xb2`\x15\xc5aN4\x944\x1d\xc6\xa9GC\xb8t\xdf\x9aC\xdf\x0c\x90/\xb0p\xb2\x8e%_\xb0\xc38\x0d\x8e\xce3?)\xb4Q\x14\x19?\xb8I\xf6,J\xdeE\x89fFCQ\x04\xd8Y\xf8qAX\n\xfeL\x0dO\xb9\xf4\x0d\x96\xfd\x8c\xfd\x0c\x1dk\x95\xa0[\x06jSes\xcd@\x1f\xf3\x1e\xeb@\x97\x0c\xd4\x04V\x05\x164\xa1\x1aJ1\x9cb\xab\xb7\x15\xb5r\xc8\xe7yz\xa6\x19\xdcY\x14R\xd2\xe0\x1c\xec\xeb\xbccH\xb4\\\x95\x0cjpo7\x85>\x14\x88\xed\x08\\\xab\xbf\xc4\x14\xcf&\xd8\xe7 r8t\xa9\x9aw5\x9d<\x8f\xa3\xe4\xdd\x0f\x83>\xa6\"6:\xad\xa3\xb6\x86rT\xbc\xc8HB \xf6\x91j\x9er\xa3\xf9@\x92JC'xg\xe2)\x1a\xe6{\xce'BcX\xab\x9d\x16y\xba\xfe\xf1\xd8\xfd\xbd\x1b\xcd\x87\x1a\x0f\xa7\x9e\x94\xf7\xe3k\x97\xd0\xb4/\xd4g*\xa1>S \xf5\x99J\xa8\xcfTB}6,GS\xe6vc\x94\xa9\xe4\xeef:\x97\xf3\x05~\xed^sY\xb96@&\xecg\x1f_\xd8\xd7\x9b\xe9\xbe\x08\xfb\xe2\xfap\xc2\xbeP\xa4\xaa\xe1r\xcbT\x05)\x87\xc3@R\x0dc\xc9\xb4\x07\xe9r\x19\x13d1\xd5\xa0L\x82O\x93\xd79\x15\xf8\xf1\xb8T\x03o8\xf0#? Hl\x00.8\xf0\xd19 6\xba|\xfb\x0b\xa3\xe1.\x1b\xa0<\x08\xadU\x12\xabjq\x8cz\x8e\xed\x10s\xea\x1a\x81\xad2q/+P\x8b\xef^\xb0 \xf5\x8b[\xc6\xef\xce+P\x8b\xef\x9e\xb6\xdd\xce*\xc6J\xc3z`\xb8\xbd)w\x02\x15\x9f\xcf\xbc\x90d9 \xfcRW=\xe0\x1c!\xb98\xa4\x06;F0}n\x8bG\x08c\xcak\xf1\x0e\xa1R\x8dn\xe7;\x84\xd0*\xe0^\xf0\x8f\xf0\xe9\xd2\x95\x9c|\x89\xa0~\x1c\xa7g\xaf\xf3\x8b\xa7\xe5\x8b\x8d\x06\x83_\xb3y\x1b\x98-\xe49\xeb0\xff\xfa\x11\x13?\xd5\xe0O\x11\x9c\xb0\xbd\xf94y\x99\xa7\xcb\x9c\x14\x1a,\xf9\x15\x0e\xe1\x9d\xd7P\xea\xa8A\x7fB\xd0\xa6\xeeF\x0d\xfb\na1\xdd\xb7,\xa3\xb7\xb8\x1e#\xc6 %Q\x9ai\xb5@\xcf\xe0\x10\x1e3#_\x15\x02\xae\xd3\x8f\xbd\xa9\xe1\xb3<\x0d7\x81\x1e\xfc7\xee\x8f\x8c\xa9G\x9eEE9r\x1f\x8f\xe1\xc4iT\xd5\xd5\xf5\xee \x1c\xc2\xb6F\x9bc\x1c\xba{<\x86G\x9a\x97\xfe\xddQl9c\xf8n\x0c/4\xca\xab\xef\x9b\xbd<:/ \xeaI\x8b\x91\xfbX\xd3\xcc\xcf\xc8\x04\xd9\xcd\xda\x0f\x0c\xb6YKX\x0d\xfc\x0b\x03\xe6\xf8\xa6\x83\xfc\x91A\x06,w\x9d\x1a\xee\xbf\x19\x9c\x8d\xf2\xf5\x1f\x0c\xd4F\xf9\xfa\xbf\x18(\xc7G\x1d\xe4_\x19d\xe5\xd5\xc1\xb2,h_\xf9?\x9dW\x8e\xf4I^\xfe\xd9ma\xb3^\xfb\xb96\x17\xca\xfff\xaf\x98\x14\xc2\x84\xf2/!\xcf\xe9S\xe3\x86\xda\xa5\xf7\x19f\x8fe)d\xd1\xc4\xf9-\xec\x9b\xdc\x95\xd0\x9d~\xef\x19\xee+\x1e\x9a\x97{\xad\xec>,F\x87\x838\x9c{\xd3\xb9p\xe4\xe8\xe0R\xf43\xf1\x8c\xa1$\xb6\x16R\x10\x1e\x04\xb4\x7f't\xdfI\xd2\x84\x02\xd8\xe69\xb1\x12\xe6\x9b\xaa\xdb*\xe7c}2R\xf9\xf6\\\x06\xe2\xc0\x0dx\x047\xc0\x91\xe9x\xdbP\xea\xd5\x8e\xc2\x99F\x03\xfe\xefZ\x01\xaa\xd4\x80\xaa\xa6\xe0\x9fZ-\xb1\xc0[\x94ngp\xaa\xeea\x83S\xd5\xfa\x98\xb4}K4\xa7w\xab\x84\xd3Z\x0f\xd7\xf0\x9f\xd1\x1c\xf6\xb53\x84\xca!W=M\xffm\xa7x8\x1f:\xfdC0\xb0R\x8d\xab\xeb\xe2\xbf\x1f\xc3c\xba!\x1f\xb3-\xfe\xc7\x1f\xcc\xff\xe4\xf0\xf0\x10\x1e\xd7\xce(\xea\\\x13\x06?\xe8J\x15u\xeb \xd3\xd5S\x15z-\x03\x18\xbaU'\xee\xed\xe9TC\xe8d\x13\x10\xa7~\x18%\xcb\x89\x9fDk_c\x1f\x19\x8d\xe1H\x9bX\xc8`%\x91\xb5\x8d\xea\xcd\xd3$\xcd\xd7\xbe\"\x07\x10&x\xfa\xc5\xcf\x93(Y\xce\xe0qM\"Fc\xf8\xd5\"\xcf\xd1\xb0\xfe4\xd89}\xa9\xca\xab\xc6Bcf\x10M\x83\xff\xb01G\xfc\xaaX\xd4\xd1h\x0c?\xd1y\xfc \xc3=/\x91\xb6E6,\xc1\xf3N\xc24(v\x9f\xd1\x0f\x86YO\xa2$\x84u\x9a\x13\x08EF\x9f+^\xd8\xd6\x0c\x0c\x1f\xb91\xd0\xd5\xd8\xe6\xa99\xeb\xcceq\xeb\xa7\xa6\x18\xa4\xc23u\x1b\xff[\xd7\x86}\xb0\xac\xc5L\xc4\x91\xf6\x0bJ\x8b\xd6O\xda\xe8X\xf6\xb4\x91c\xa7yj\xa87\xd4\x0f\xbaa\xd7R\xc4\x0c~\xb3:\x85yA\x10;\xf1\xa3\xe2Ef\xf0X\x03\xc5+x\xff\x03\xdd%uj\xb8\xa6\xbaL\xeb\xaa\xdb\xd2\x95I\xeb]\x89\xab#\xb9\xcf\xe0\xb9\x86mi*\x12f\xf0R\x0d\xb9H\xa4Ev\xc4e\xcdP5\xb4d\xda\xecE-\x15\x996\x7fQ\xe6\x97\xab\xe7\xdc\xb1\x93q\xe1\x86nr\x17\xe4P\xb1\xe1*l|\xae\xc1\xc1\xbf\xeap\xd0z2\x98M\xfeX\x0d \x1cV5Ly\xda\x91\x1bgB\x03Q\x98\xe5H\xda~\xf5\xda\x16\x15b\x85;\x12\xda\x91\xe31T\x1f\xd1\xe9!\x96\x84\xbb\x83\x91\x90}l\x06s\xafh\xdd\xd1\xacs\xff\xe5\x0b\xafw\xd3\xf0>\x05\xf9\xd9\xcf#\x8a\xf0?3\xed;\xffH\xef\x89a\x18Mx6\x8ca_8Z,HPF[\">\x85\x9d\x11\xdf\xa9\x9e\xe2}3\xfe}\xf5\x15\xbc\xa4\xff\xbc\xc2\x7fLtq\xa7cV((T4Z\xd5\xd8\xff\xd2\x9eo\xec\xa33x\xf5aq\xdf\x96\x98\xf0H\x16\xa6!\x9b\xc1\x13\xc5\xcc\xd7S\x7f\x15S\xfc\xbcRu\xbc\xa4\x12\xf9\xbcL&\xcb<\xddd(ys\xfd\x95\x91\xb3{.\xdeW\xf5\xe8\x17+\xc9Y{Z\xd9\xce\xe20\x92|\xd9\xb5\xad\xec=3(\xacvJn\x9a\xaa\x1f\xb5(k9 \xf6C\xd3wz4\x86\xa7W\xb5\x97\x85 \x1aT\xc1dCw\xf3.\xcd)]'\xaaey\xa6\x19\xe0\xcf\xba\xd6*\xb5\xf1\x0c\x9e\xa9g\xbaJ\xea\xab\x89*\x11\xcc\x90(\xfb\xa0\x8d\xfd\xb0>\xb7[l\xc4Ul\x98\x86-N\x9b#\xd2\x1aK\xb9\xf5a\x06o\xcc@\xfc\x90\xda\x8a\x80\xbf\x97\xfc\xfe\x934w\x19C\xa59\xfc\xfb\x8c\xb4\x95\xce\xdf~\x1b\xa9A\xe4\x86\xad\x19\xbcV\xbf\x82\\\xac\x89\x9a\x10\xf4\xa0\xf8\xdet\xdc\xfe\x1f\x1d\x06\x93J\x17>\x83\xef\xad1\xce@2vq\x1bz\xb9\xc9\x89\xcce\xa8\xca|'w\x19j\x9c\x1c8)\xad\x87y\xb5\x99d\xcf\xf8\xa6\xec?\xaaQ\x85J\x8a\x0b\x8fY\xbc\xba>5\xcc6\xa1\xf3B\xfa\x12Z\xd4\x9e1\xa5\x17\xd2B\xee\x85\xb4\xa8\xbd\x90\xee5S\x19-4\xeeF_b\x8b\xfe\x03\xdd\x8d\xac\xfc~\x86\xc4\xfb\xe7\xf6\x0e-\xe9\x10\x87\x16\xe6\xa6\xd4\xb6\x13\xa9\xa1}K_\xaa\x0d\xd6\xd039\xa7\x14,\\\x9d\x91-5X\x80`QQ\x95=\xd5\xf0\x0d\x0b\x845\xb9\x9ed\x08\xa5s= Y\xd7V\xe9\xd9\xb1\xa9{+\xfe1\x0b\x17\x94-\x03\xcd\xa3e\x94\xf8\xf1\x0b\x9bW0\x12I8\xa2X\xbd\xb1\x84C\xc8\xcc\xb3z\x81K\xc4\xd5\x1d\xc1&\x8fJ\xadU{\xce\x12(Tu`\xab\xae|_j\x8d\xf9\xa7\x9d\xc4\x0b|:\x9f\x1b\x03\xbf\xcf\xe4/\xbe4\x04\x9a\xf3\x1a'?n\xd6\xd9\xeb\x14\x811;\xc4\x07\xb7.\xd7Z\x01\xd6O\xe8\xfc\x8d\x06b\x8d\x16\xb0\xae*(\x05\xd1\x08 \xa7\xba\x1e\n^P\xc5\xb9\xa9?{f\xaf\xa6\xd3\x05>v\x0c\xd0\x1a\xc3r\xcd\xe3\xc8\xe3\xc6ig\xc3\xab\x92\xfb\xba\xabcc\xafX\xd2\x83\xad\xa8\x99],\x8a\xedn\xe9\xdd\xd5\xc8\"{\xfen=\xab\x93\\D\x8a\x02\x04\xef\xc7 :Qg\xdc\xff\xea+\xb8\xf0\x82t\x93\x94\xae\xaeos\xbdY\xbc&\xb93\xd0d\xcc\x1a\x1e\xe3!N\xd4\x941\x94\x98\xef\x97JMT\"\x89r\xec[\xe1^\x982\x89 \x81\xae\x13\x06\x17\xae\xc2\x01\x05z\xacEu\xd7\xac\xb8\xd2V\xc8\xc9\xb4\x08{\x85B\x87!N\xa1\xbb\xcfL\"D\xb0\xb3\x08q=\x03\x19>i\xa6\xb2\x01\xc5\xa6?\xa32\xa3_\xc4\x04q\xed.&hK:\x9b\xb8\x8fK\x1d\x1b<\xb3\x8e\xf4\xdd\xf7c\x94P\xded\x19\xc9\x1f\xf9\x05\x91%W\xd9\x99P-\x86\x13\xaa\xfa\xbb\xe3\xcf\xa0\xc4\xf1g\xaa\xad\x10\x91S_\x94\x16\xff\xb1\xd4H\xcd\xc0\x95\x034\x11\x89Dc`\x14\xf5\xe9\xc6I\xac\xe2PR\x844\xc6\xa1D\x08\xa6\x8fC\xf1\x11F\x1b?\x82u\xf1\xed\x84\xf7\x82w\xecq\x9d\xc6\xc4\x18\xe1AO\xd8\xb2\x99G\xe4\xc3\x9f\x04y3'\x838\x0d\xe8<\x9d\x9e\xb6\x9d\x9d\xa5@\x83\xcd_\xdazUU\x02\x06\x9d\x02J$`\xd0\x98\xa2\xb2\x06\xdf\xca\x9ao\xfbO\xfbXy\x80J\xd8\x1b\x0d\x0e\xb2,\x0d\x91|\x84Wy\x04^7v\x99\x9e\xaa\xcd\x80\x078\xe4\xe5R\xfa\x87[D\xcf\x84\xfb\xb2\xd3-\xea\x96\xd0\x8f\xd8\xe9\";=\xa2\x8f\x7fz\xf8\x98\xc1\xa63J\xf5q\xb2\xad*\xca\xd7\xe6\xa6>\xe6$\xed\xd27b\xa5\xdb\xe1#\xaf\xd2\xb3\xee\xbe\xe6\x83M\x87j*\xa4\x0c\x9d,\x81\xcc\xfb\xf1\x95~\\Z\x9bS\xd7F\xb3\xb4i\x1d\xbb\xe2P^\xe3R\xfd\xc2\xf2\xa5*c\xbc\xaeC\xa2f*\xeb\x93\x1a\xacU\xe3T\x0d\x96[\xc0\xc8\xeb2\xaa\xcb~\xf6\x06\xe3<\x89H\x8cN\xe5\x1f\xb2\x114Q\xb3\xa2\xa1\xeafZECK\x8f$e~qL~\xc3\xec\xb7\xa6\xcc\xa0\xdbF\x8d\xa8f\x9d\x9f1\x1c(\x881=\xbb\xcb\x93}\x85\xb3!\xee\xe4\x93\xa9$ \xc8\xb0\xad\x12\xd5Q\x84\x0cUT\xa5\xdeT\xb8\x8a\x9e\xa3\xcb\xa9BAy\xfe\xb3\x1f\xcb\xf4<\x9d\x04\x96\xef\xdb\x05\x10\xdf\xcb\xcf\x04\xf6\x99\xebu&\xbcJ\xcf\x0c\xc7\xc2\xed\xe9\x9f\xe2X`\x03\xb59\x19(B\xc8\xcf\x04\xe2Q|\xe8?C\xa6\x14\x1eR\xa63\xfd\xf1\xb8\xfa\xe1\xa2\x92\x91+\x1a\x87\x9d\x14\xd6\x94\x88o]#1ap\x9d\xbd\x1a}&H\xdbG\xcc?Q\x02\x13\n\xf0\xe0\xee\xfe\x9f#g \n\x9f\x98\x949\x1a\xc3\xa6O\xca\x15\x82z\x1fp\x91\xe6\xe0\xd2\xaf\xd1 \xaf$p^Bn\x8c\x13\xceR\xff\x16\xa31N\xf4\xfe\xd7\x10\xc07P|\x0d\xc1\x8d\x1b#\x88O\x82\xb7\xcd7O\x02\xf5\xc1B\xb7v\xc4O\xb2\xbe\xb2\x00ei\xa3\xc2 \xf0\xe3\x98k\x0d\xc8\x18N\xe8\xbboE\x11\x87\x18O\xe1\xc8Cs\x85\x1fG\xff\xae\xa5\x07c\x19\x07zE\x1e\xa1\xe3\xed{?\xbfG\xadBz\x865y^\x936\xef\xab\xfa\x1a\xf3$\xaai\x00\xd7X\xe2\xbe\xa3\xdfc\x7f.\xa2\x98PN\x03S-\n\xef%\xaf|\x0b)Z\x0dY E\xac\xce\x9c\xc07\xacVa\n7 \x82o\x0f\x99;n\xc2\xe2\xbbqs\xf39}\xcc\xd6JV]u\xcc4\x19=E\x17\xdd}\x1fC[u\x95\xb5\xcf\x98\x9c\xbf\x8a\x96\xab\x98\xce9\xaf[I$\xc1P\x1d ]\xc6\xff\xf5\xbb\xf7&\x0b\xfd\x92\\\xaf\xfe}\x02e\xdfV\x1f\x90\xc1vV%h\xe87\x14\xa9\x88\x0f\x15\xc3\xb4:.,0\x86\xc4\xc4\xb9\"\x9f\xeaj!&A\x1a\xaa\xca2\x8eQ/v%\xed\x89\xa1Nx\xc5yY57q\xd5^\x1dt]\x9a\x14Z\xd5M\xe71\x07r\xcc\x96i'\xcb\xf5\xc9\x01YYN\xda\xb4\xe4\xc8\xd1\xf5\xfa\x97\x15!qU\x04KG\xd0\xd5_i\xcc\x19\x96=\x80uD\xbf\xa0\xae{\xfa\x9er\x00\xc6M\xd4W\xc3\x99Tpr\xa7\xd7\xe6N\"\x1e9\xcf\xd2\xbc,Z\xc7S\x9f\xbd\x85\x06\xe7\x99\x903\xf8>N\xe7\xee y+[\x83\xf2\"\xc3\x91ST\xa7\xfc@\xc4\x8ad\xdfL\x83\x92\x94\x93\xa2\xcc\x89\xbf\xeeH\xeb\x1d\xf6'ZT\xf5v\xf7\x0e\x0f\xe1,J\xc2\xf4\xccK\xfcm\xb4\xf4\xcb4\xf7\xd6\xc5\xb1\xbf%\xb4\x0f#\xddC7\xefsV$.\x88\x82k\xa3\x87\x1e\xff\xda\x9bW\xcf8\xc61\x0e\xfe\xcd\xabgn\xae\x91\xe9C\x9e\x0c\xa4\x8b\xa6\xbeL\xef\x1dyX/W\xb8\xb6\xc1!8I\x9aP|\x8e\xbcUN(G\x9c\xd2\xdf\x05)\xbf+\xcb<\x9aoJ\xe2V\x9b\xcfa\xb2N\xa3\x1cq\xcd\x00\xd13\xb3\xfb\x1ec$\x9cq\x15\xd3;\x1a\xd7\xdd\x9d\xa7\xe1\x05\xe5\xd9H\x12>ZEq\xe8F\xc8\xa6\x05t\xeb\xba=\xc0\x9c\xac\xd3-\xa9\x01\x1b\x93\x95\x93m\xfa\xae1Y\xa9\xea\xe8}/E\xc9\xeb L\xc9\x95\xbfR1+R\x89Y\xbeJ\xcc\xda\xa8\xc4\xacB%f\xc5\xfcAOb\nx\xca\xc7\xbe\x1cUKZYU\x12B\x98>+\xe0?\x81`\x95\x8f\xc1\x97\x0bV\xd1u\x14\xacr.Xml\x05\xabt\xa8`\x95{\"x\\\x84\xe1\xfc\xc2B\x04\xad\x84\x0e\xde\xd5\\T\x88\xac\xc3\x85\xbc\xa0\xf5QT\xa8\xba'\x02\x10M\x90\xd5k\xcc\xed\xe2-\xe5\x9f{\xad\xbcg]\x14\xf1T\x8f\x18\xfb\xf0\xfa\"#\xac\xd7V\xdd\xace#\xca~\xe4i\\|\x17\x04$+\x7f@\xf5\xaf\x89\x9f30})\xe6v2\xb0\x8f\x11\xba\xedY\xa5@\xf4\x11To\xa4\xdd \x8c\xceO\xa6\xac\x08\xbad\xea4EZ9\xd1\xd3\xe5\xb4d\xde{j\x00\xe1>\xbb\x91BH\xaa\x17\xbd\x1f3\xabs\xafp4\xdd\xad\x96\x82X!\x15\xc4|;A\xacX\xa5\x9b8\xacX\"ka\xc7\xb4/\x1a>M\xdd\xc0@\xe4NH\xff\xb6(\xbf\xcf\xde\xaab\xdb8x\xfdw\x1bN\x84\xd6q\xb0\xeaO9\x14n\xc6\x0e(\xbb\xd7\x86\x97\x07\xbc\xf1\x17\x15\x0f;-\xfa\xe5J4D\x7f\xb6\x9f2D\xe1\xcf\xd9\x1f}\xdch/\xffG\x92\x06\xf5$\xc1F^d\x1e\x19\xd5z\xe9)C\xd2\xc3\x03=yH,\xbdN65\xac!\xa5,\xf3\xd3\xb0\xcc\x13\x8bl\x841\xefm\xd2\xc6-5p\xc8\xdc\\\x06\xa6\x0d]U=\xd6G\xd5l\xf9\x11Zi\xed\x8e1\x89\xdf\xa34$#7\xd5x>\xac\xb1\x98\x8f\x13\xd4d\xd3T\xd1\xc6w\x9d8\xda\x12\xb1\x86\xa6\xca6~\x1d\xbbj\n\"\x91m\xf5\xaf\xbe\x92\xdd\x16Q\xa4\xb27f\xb5\x84\xf7\xb2\xf5D\xdd\xf8)\x1cB\xd1\xac\xf6\xc7\xa6rIJv\x82>b\xe7)\x95p\xc5\xb0\xe9\xacJ\xcd6\xe229\xee\x0c\xd1+T\x1b\xcc\x98\xd9\xe0J\x9a\xb3q\x01\x10\x971O\x16w\x05x\xd5\x88_n\xcf\xb5)q]\xec\xcfI]3\xc4\xe4\x08\xd5i\x0e8b\xa3\xcc\xad\xcb\xa6\xa5\xad\x16\xc3\x89\xab&(L\xb0\x97\\1\xa2\xe065\xc4\xa6\xde\x7f\xc5\x0c\xe6\x1a\xc0\xc6:\x89t\x17\xfc\xe5 \x8eQ\xbeJ#]\xc6\xabA\xc8Q\xe3b\x94\xe8\x92\"Df\xa5\x9a~E\xb5\xd5^\xea`i\xeb|\x94\x1a^\xae\x99y@\x93\x03\xaa\x93y@CP\x18\xf7\xd8a\x11\xcc\xbcd\x8fk\xd0\x1c'\x8a0}U\xfe\xa5\xe1\xdb\xd4B\xc9(\\k\x86b\x0e{o0=i\xbb\xe8\xa8\xc1\xf2\x1d\xba\xb4+\x8dS\xb8\xe1\x88K\xed\x8eS\xa1\xf0\x84\xde\xe39wU\xcd;\xf4 \xd7&\x03\xbc\xa2~\xd8\x04\xbb9\x8f\x1b@]j\xfe\xa1;\x18G\xc9;\xcd<=\xc3\xc7un\x07\xdd\x8c\xb5<\x9bR\xa5gS\xa9b\xa5\x81\xb3\xd3I\xdf\xc3\xa9T{8\x89\x0bYg\xa5\xa7\x93\xb8\xb0|\xc9\xc9\xd4\x00\x15\x027\x18F\xed\x0c\xcepx\x08)<\xac\xf1\xfc\x94'#A'_G\xce\xb8\x80\x99y\xb9\xd0\xad$\x08a\xc5P\x96\xb8\x8e:[\xb1\x1c':6\x15\xd0\x1d\xf8\xb1\xd0\xa6mQ\xafkh`\x91h#\x13\xa1\x8du\x1aZ\x8b\x90iH\x8cC\xaaO%M8/\x0c:I\x803\x07]u\xce\x8c\xa2\xc6\xe1\xa1.m30\xbe\xa4\xabK\x9aa\xd9\x0f\xa5\xaa\xc9\xdc\x15\x0e\xae\xe5\x87\xc0\xfeT\x85\xfeI\xad\x84U\x14\x85n\x15\x83\xde!\xa1K\x8d\xe7;$u\xe9'C\xeaGX\xd6\x99\x83\x98\x85\x98U\x8a\x1a\xb9'-\xfb\xcf\xaf\x85\xa4\x16\xa7\xea\xa0\xdf\x9b\xd6\x03\xf8\x1c2\xb9\x84*w\xacP\xe5\x8e\x15\xaa\xdc\xb1B\x95;V\xa8r\xc7\n\xa5\xe6\x8b\x98?\x91Z\x10\xdcP\xd8\n\xc2\xcaV\x80\xbf\xa6\xb7z\x05\xa4\x17R\x8b\x03\xaa\x07Te\xa5\xc3\x8fo\\X\xd9\x1a\x17\x88\xc4\xb6 C<\xb3hkjo);O)\x0e\x8d}\x914\xc1'+\xf2N%$n\x90\xba<2)\xb9\x12\xe6\xeb\xd3oF\xfd\ns%\x92\xd1m\xf9\x99\x8b*\xec\xe3\xd2/uJ\xeb\xbcO\xb2\xbbK/\xae\xf7h\xd82\n\xb4\x9a\x11\xc8\xcf\x9c\\\xd1Z\xef6\xfa{Q6\x84\xf4\xe8\xa5\xb8\xa4\xc3q\xfa\xac\x1d\xfd\x94\x02\xbf\xe1\n\xdd\x94\xaeF\xb3\xca\x08-Z\xe0RK\x1d*3\x9aP\xfeB\x0d\xc3\xac%\xe6\x02d\xccbb\xe1\x9a\x13\"\xa0Y\xaf\xb8B8\x9d\x12t\x8b\x10v\x9a\xdau\x0dk\xd0\xd4.\xab\xfeYhj/\xf8\x0cVx\xa4\x06\x9dW\xa0\xf6\xf6\xb1S8\x84\x95\x17%\x0b\x92c\xaeS\x8d\"\xe1\x0c\x0ea\xc9\xc5!5\xd4\x11\x1c\x82\xcf8u&\xe2h\x93\xfa\x9d\xd7\xd0\xe4\xdc_g\xb1>\x07\xe0q\x0d\xced%\x0d\xec#8\x84\xadU'\xdeqH\xe1P\xc5\xe5Q%\xfcw\x0c~\x9d\x86$>b\xbd\xd6\x81\xbf`\xe06%\x80^2\xd0*.\xd3TL\xe75\x83\xb7Tp?\x17\x9b\x16i\x97'\xa1Q\xf4\xc8\xbaPP\xf1\x05\xb8g\xee\xc8$/>\x15+\x84\xc5\xb2x\xc7\x9c1<\x7f;\xe6\x8a\xe7\xe7~6r\x7f\x7f\xdfe3\xba\xd7\xafp\x08O\xb9\xc4\x87\x88\xe9\xf4>\xa0\x16\xf1\xeaP?4M=ma\x98#\x94\xe0\x99W`m\xa0hq1r\xbb0T\xccf@KR\x1e\xe3M\xb6AF\xee\xaf\"\xec\xd70\x9b&A2J\x82x\x13\x92W\xc4\x0f_$\xf1E\x8b\xcb\xec^\xf4\xd0\xa3\xc7\xcd\xaf\xf0\x10\xcaJy\x95\xf0;\xa7U\x9fj\xc5V\xce\x9f\xb9\x8d\xcc\x89\xcd\x151\xf5]L\xfb[\xfaI\x85\xe6\x8d9T\xd1^\x9c\xba\xbe\xe8\x01k\xda\xf7V~Q\xad\x1d\x9d\xf2\x90g\xfb\xacnQ\xb9\x14\x07\x95T\x0b\xd2\x9b\xebd\x0c\xcfu\xf3(\x99C\xcdi\xc4\x80\x7f\xc9\xa3\x92hg\xfc\xbd\xde\xfcq\x8e\xbe\xcc\x94v\x9d[\x04\x8a\x89K\xb0\xc0\x94\x1d\xa2l/+&\xf5\xd7\xbf\xe6d\xe1\x08\x97.\xda\xae\x8a\xebQ\xe0;\xddu?Y8\xf05/a\xdcF\x0bTeo\x1a\x16\xff\xd6\xbc\x9a\xb1p\x0d3\xbe&\x16\xaey\xe5\xda\xb8\xb8\xe6\x95\xf2\x1893\xa4\xe0\xd0[{<5%V\xba\xa4YK\\\xc8t\xc9\xd9IqiMKw*\xcd]\xaeQ\xf2)\xe3\xfe\x9aW\xdb\xa4\xc2h\x9by\xf68[(\x8f\x19\x17\x97,v\xbc~V+-(J_\xd6^b\x1c\xeb\xf0q\n1A3\x06A\x05\xe4\x1b\x92\xa2\xf7\xf9\x18\xde\xed\x98\xdc`\x07M>8p\x03\xdc\x0ds#\xd7l,'\xf4K\x9f\xb9\x85+\x03\xff\xafN\xdd>D\xd7\x1f]\xa1\x9a\x7f\xb0n\x7f\xe7}-[\x8bn\xab\xa7\xa7z\x93\xa1\xaa\xf1\x17\xba\x86E\xd5\x1f_\x94)l\xd8&T\xa7\xc4\x18\xce\xcc\xbb\xcdj\xacL\x9dWQ\xf3\xe6\xd0\x1b6Y\xd3\xcet\x84@2\xf1Q\"\x11\xd6\xa8\x19\xcc5[o\xe84\xbe\xb60q\x1b8\x1e\xf5\x94\xb4\xec\xd7|-\x04#E9\x9b\xee-\xef\x1da\xc7(\x88\xc4\xd5\xc7\xe4\xb7^\xd2\xb9\xe6\xd51\xb1\xcb\xf4>\x8a\xf5\x1e\xc3\\\x9b\x83q\xed\xc7\xb5\x83\x81\xc3\x9d=\n\xd0E\xa1 \xe1\xa8^ar\xa43\x1a\x83\x03l\xe9\xbc\xda\x06Uq\x9b?i:\xf1\x9d\x16\xc5+K\x89u\x9a}MV\xfc\xa6Z^S{\xb1c\xa2\xd0\xd5^D>T\x88\x02L\xb5\xfd\"\x0fIN\xc2\x91\x9bhV\x94\x1fB3\xf8I\xb1p\xd5\xd4\x1di\xa6\xee\x91n\xea\xb8h;\x83#\xeb\x99\xd3\xf7e4\xae\x04\xfc+\xb5w\x0e0r\x1e\xc3C8\xf6\xcaT\xc6\x85v\xa2W\xba\x97\xe1\xc0}i\"T\xc8\xb5i\x14<\xf4JpP\x06 :B\xad\xfe\x11,\x17\x064\xa4p\xa4\xad\x87Yo\xdf\x9fR\xe0\xaa\x92j\x95{\x1f\xbc\x94\x05i\xa5\xb7 \xd5fCF \x85u\xe8\xf7\xf7]s\x89\xcc\x9a\xd7TL6T\xffm\x9b\xd0\xea\xbf\xf8\xcdke\x13Z)sG\xacTQ%+UT\xc9J\x15U\xb2RE\x95\xacTQ%+\xa5Mh%lB+\x8c\xc8\xbf-\xb5\x04\xb1g\xbd/W\xe6\xa0\xf6\xedP\xf4]\x91no\xf5\xf1\x0dE[[C\xd1\x97(\x94\x8e\xd1\xca\x14\x85\xa2\xb7\x88d~^\x90\x90oq\x85X\x85\x91\"\x1bt\xdd\x7f\xd9\x04\x1fd\xf2\x12!)\x9c\x1bSk3\x99\xff|\xa9\x16b)\x10S\x91@\x94\x14\xa5\x9f\x04$]\x00\x0b<4\xebC\x12\x1e,\xf9$\x8aQ=\xa52\x8f\x89+\xf1R\x16\xc6g\x91\xc3\xa0y\xe56\xe6\xb5\xe6\xd5] \xca\x0cobydn\xf3R\x9cD\xd5\xe31~\xca\x0f\xbf+^\x93\xf3\xd2\xd5L,\xd7\x1bZ\xf7\xbc\xd3\xe3\x92\xf2\x07\xac\xaa\xbbN\x03!C\xafO\x1b\xa4r\x95\xd9\x02PN\x90\xec\x15\xd7\xea\x88W\x07a\xec\x942@\xb9)\x95\xbd$b\x7f^\xa2\xabWc\xd5\xb4\xb4d\xd6\xc1g\x16YB\xad\xccu\xac^\xc9&\x97$T\x12\x17\xabR\xc2\xf9|5\x98_\x9b;Xz\x8d\x87\xf0\xfb{\xd0\xba\x0fo\x06d>-\xdav\xa3\xd6nT\xbf\x85\xf5A\x06X\xd5\xe8\xc1\\\xfb\xf2\xa1\xa6\x8b\x92\xcf\xc7~I\xb0\xbe\xe8\xebhMt\"\xf4\xba\x9a\x04\x8d4$\xc9\xf5\xd5\xbc(\xc5\xa7\xcb\x92\x8aL\x0d7\xffo\xc3\x87\xe9_\xad \xf6\x9b\x91W\x92\xa2t\x93\x11\x05\xf6O\x1c>#\x93\xc7Q\x91\xa5\x05f\xe6w\xde\xd2\xe3\xe3\xa6_\x96~\xb0\xa2\x07\xb5xI\x05.\xbe%4,\xa1\xdd\xb7\xa4\xe0\xbd~5\xb4G\xec[\xf4h\x82\xd7\xb9\x9f\x14\x0b\x92\xcb\xba\xd6|\xa3\xd75\xeb\xcfI\xdf\xd0(\x8f\xe9*8\xf4\x98u Jx\x9c\xb9\xe9$\xa4[\xf9\xa2\xca\xb1Q\x92\xf3\xf2\xe6\xaa\\\xc7\x16\xban\x0c\xce\xe9\x1e\xf0\xc2\xcaV%;(\xa5\xc9\x0ed\x17K\x80pa\x84\xed\xca?\xb2\xebT\x9f\x94`n\xf1\x8938\x84\x93\x0b\xca\xd0\x15\x9byQ\xe6n\xea\xc5~Q>MBr\xfeb\xe1:7\x9d\x11\xdc\x80\xe9h\x0c\xa7o\xbd_\xd3(q\x9d\x99n\x9b\x8a\x0b\xed\xfc*D\xd5l\x08=\x13\xd4\xc9\xfdpdZv\xe0K\x7f^\x99{\xc8y\x99\xfbA\xf9\x84\xe7oz\x92\xa7k\xde\x8fF7\x98W\xc4\xc8=2\x18\x84\xe8\x85!<\xb43\xcc\xeaG\xe7\xf3\xdc\xc0 i\x9fR\x1aTy]\xd6\x99+\xe8\xc7%\xb7yB\x8b\x17\xf9\x8b\x8c$\x1c3/eIq|\xa3\xc6\x16\xaa\xfa\xec\x06\x07\\\xd8\xa9\x06\x8a\xb88We3hw>\x863\xfd\xa4\x83q\xe2\x9bYf`\x11 #\xff\xb5\x9aM\x91\xcbc\x06g\x83\xc7\xa2|\x81\xb3\xdb\x14\xf1\x94\xe3`)u\xb8\xce\xa8\xfa2\xe7< $%\x96\xd6\x86\xf9\xa6\x84\x8bt\x93\xc3\xd7r/\xda\x99f\x96k\xda\xe7\x06'\x84\xa2\x81\xdbN~\xc8x\xd7\x9b\x14\xe8_7\xb3\xd8\x8f\x92\x9b\x8d\xd9\xff\xc8\x036\xf0k\xc2\x88\xa7\x181\xcc\xe0\xe6\xff\x8d\xd6\xfe\x92\xfc\xebf\x0b\x87\x12\x8f\xbb\xfd\x14\xaeSl\x97\x8e\xd6\xb0\xd1\xa4\xf9\x0e8\xa8Fv\xc0\xd1+\xdb\xd7K\xed!\x80\xf9\x9ed\x9a\xcb\xe6\xb5\xf6\xcf\x7f\x89\xc2r5\x03g\xba\xbf\xff\xff\x93c\" \xe5W7\x94\x073\x1d\xbb\xa8\xd0\xc8\xf0\xb9\xf37a\x94v\xe6\xce\xea\xb8P\x9f\x8d\xf4\x8bzC\x117G\xaa\x1d\xb1tA\xd1h\x1c\xd7O=\x9d\x11]\xado\x96\xacL\xb5\x89\xe8\xc48\xcc\x7f\x88n\x1f\x04O\x17P~\xfc\xbdQ\x9e\xcbtE\xe22o\x0d\xee\xe4\xf5-\xec\xc3C(lw\x80z\xf9\xad\xcd\x7f\x91:\x9c\xf1M\x92\x93 ]&\xd1\xbfIX\x99\x89p\x8e\xbf\x16\x81A\x94\x89\x10A\xee~\x81\xd4\xdd\xd3E\x8a~\xca\xd9/4\xa4\xf8\xd3M\xe4\x06K\x91@\x99\x8a)\xad\x8d\xf7Z\xb7\xa5\xe5\xa5q\xa4\xe1\xc5Vg,\xc0\xb0Tz\x9e*]\xab\xacm\x916UH\x98Yu'\xcb`\x95\xef\xd0}p\xf7\x8e\xc4\x88\xa7\xd7}\xd6\xbe\x9eY\x1c\x95\xeeM\xf7\x9b\x7f\xdd|x\xf2\x7f\xbf}{\xe3\xdb\xd1\xcd\xe5\xc8[DqIr\x0b\x0fK\xfe!\xc7\xa9\xb2\x0dEkY\"\xdc\x8e\xfa\xba\xdd\xdf\xc8\xb6\xbf7\xbf\xf9\xd7\xcd\x1b\xac\x9b\x9c\x11 \xda\x0f\xfb\xf6\x1f\xc6\xaf\xfe\xeb\xa6\xddw7\xb6\xdf\xb5\x9e@\xec\xc0\x9er\\\x80\xc8E0\xef\xf0^$~\xf8\xbdn\xd6\xf8!\xcf\x9d\xd9\xed\x850JuM|\xf0-Li\x13\x0d]Gm\xcb\x9b\xbe\x85\x87\xed?g\xf0\xbb\xe4\xdcg\xb1[\x82\x83\xed?G\xbd\xad'a\x89\xfb\xa01\x1c\xca\xf4\xa6\x01\x1c\xc2IGeSg\xb2\xa5\x7fu\xe2\xac\xe9x\x17c4\x07\xbb\x0b8\x042\x86\xd4]\xd8\xb8\x13\xf3uR)\xeau!]\xec\x14wK\xd6^\xe4\x96\x94uq\x1e\xc5i\x11%\xcb\xd7\xfe\xd2\x81\x19l\xf8\xdd\x17\x19I\xea\xbb>\xbf{L\xe2E\x1b\xdeyM\xe4\xb9\xbe\xe5\x01\x81\xed\xa3\xf7\xfdH\xe2\xba2\x86TeR\x8eLI\xeaX\xfdq\xa4\xe8\xbd\xe7\xad\x81R\x1e\xdf\xa7\x88\x15O&\xf2\x9e\xd2\xad\x95\xbb\xc9\x18b\x85\x92\x0fK\x89\xc3\x0d\x88\xfa\xef\xa3b\xb69\x83us7n\x8c\xa1\xd0\xd9Y(J\xa4'%L@\xe7\xbe\x1dVP\x07\nM\xa1|\xb8l\xb9\xf0\xef\x0c\xe7 ov\xbb\x1aV\x8f\x109\x1d\xac\x9c\x057 ds\x0f7 \xab~ET\xe8\xc4\x80\x05\xec\xcd\x18\xb0\xeb\xc6\xf0kh\xd0\xa6\x0eN\xb4\xc7\xc3\x81\x02o\x91\xe6G~\xb0\xb2\xdb\x1e\xd9 yK\xf7_\xf7\xe4\xa42jfw\xaa\xf0/\xed\xedu\xfc%F\\\xfb\xfb\xaf\xa6o\xe9%\x12\xb6\xde\xfc\xfb^\xdd\xc0\xdf!'\x19\xf1\xd1vB\x99\xbaoVe\x99\x15\xb3\x9b7\x97Q\xb9\xda\xcc\xbd ]\xdf\xfc5M\x8a`\x15G\xc9;\x92\x977[\xf0\xdf6\xbe\xd4\xfc\xe8\xa34\xbb\xc8\xa3\xe5\xaa\x047\x18\xc1\xc1\xfe\xf4\xf6\xe4`\x7fzg\x0c?\xa6 \x1cW\x1f\xf3\x9a\xef<\x8b\x02\x92\x14$\x84M\x12\x92\x1c\xca\x15\x81\xe7O_\x8b\xdbM\xd0\x9b\xd5od\x06X\xd4c3\xb3\x842\x7frw\xdeq\xe3\x08Ab\xaf\x12$\xc8\x08\xcaU\x9e\x9e\xa1\x9d\xe1\xf5EF\x8e\xf2<\xcd]\x87\x9cgL\xdd\xe6\x03\x7fI\x92\"y\x8a(]\x8e*^\xa3\x0fr\xd0\x05\x81\x1b]0\xe1\xa9@\xc4\xc1\xf4w(\xfb\x1f\xca\x19\xf7A\xa9~\xc3\xce\x98\x8fX\x16\xf4\xfe\xc4@S\x9d\x97Vg\xde!\xc5\x1b\xde\x97\xca\x1e\xb1O\xb1\xa9\xfd*z\xc7|\x8d\xa5\x00\xaa\x97\xd1\x0d\xe3[\x98~=\xa2''\x0b]qS\xb8q\x88F\xf8\x12\xbe\xfd\xf6\x10\xa6c:\xc4\xc3\xee\x18E\x8b\xf4P\xe2o\xb4\x1a\x1f\x86\xed5cxw:2\xe1\x82\xc2\xbb)w\xc9\xc8+\xd3g\xe9\x99\xa8D;\xac\x0f\x1f\xdd\x99\xed3,\xfe\xba\xa82\x1b\xd0_\xf7F\x7f\x8e\x82\xaf\xdb/\x05f\xd4\x05f\x84\x17\xfd\x80h8\x81\xe0\xb9\xaa\x8a\xf6\xa8\xe2\xa8\x8e\xceKM1\xef\xb4[\xb2;U\x97\xecN?\xbeZ\x88 t\x9d\xb1\x98-\x8b\xe6z\xddReh>t\xb7Jy\xa7\xd3Sr^\x92\xa4\xe8\x1d\xf6\xef\x99\xe7\xd4\x0c\x9c1\xf0\xa3)1\xd7\xda\x8e\xae\x1bB=e\x9ecG\xeb\xac\xbc0\x94\x89\xef\xc5\xd4\x8a*\xf1\x98S\xb5~'\x12\xfa\xc9\x88\xeb'\xafU\xc5x\xd5\xc8m\xf0\x10\xb1B\x85\x88Q\xc1\xbf(9\xea\x98\xf9S}\x02\xfb\xfc\x0b\x8f\xa3\x02)\x9d\x14\xa1\xf9\xb9\x8f4\x0f{\x8d\xda-\xf4\xf6\xbb\x0c\xaew\xf4\xa9-\xd4\xa7\xad\x9c\"\x0e\x9d\x96\xe9r\xa9\x11>B\xdesY\xfa\xe7\x9e\xeb\x86\xba\xbfQ\x92mJi#\xcc\x04\xee\x04+\x12\xbc\x9b\xa7\xe7\x12MY\xa3\x0b\xfd\x87\xf8\x1e\x1e!\xa8t\x90(tj^\xc9\xac\x9c\x8c\\Q\xc1\xda\xe3\x1f6\x1e\xb7\xa318\xc7$ \x01'\x95mL\xa7\xe7#\xf4Y\x95\xe8\xff\xa49\xa1\xe5&\x93Pj2Q\x94\x93T\xa4\x88\xbeu\xd0\xcb\x0b\xf0%\x17\xb4\xdc\xb0ag\xd4\xb0\xcd\x05-v\xe0.f\x82\xa1\xeeG_}\xd5\xfa[-F$&\x1bD\xc3\x02\x90TC\x18\xb9\x89'$\xc618\xcc9\x03\xad\xcb\x88\x13\xcc\xbaLD^\xc2\x84\xd5PB\x91\xbfOG\x9a\x96\x14\xebCK\\\xdbai\xb2\xad\x94\xc8y\xad\xc2W\x03\xa5\xd6\x9af\x1fS\x1aX\xc9\xb4\x9b\x1a\x94\x8a\xc4\xda\x05IxT6\xce\x15.\x04N\x1e\xe5\xe4\xdct\x0c\xfe\x186*S\x10\xe6\xf3\xe6\xd5*X\xcdA\x8b\x8c\x05\xc2\x00c\x9ci\xc6KX\xea\xf6\x13\x10u M\xd3\xc8\xca\xb5WHg\\\x18\xb5r\"\x19C\xae\x98\xdbF\xf4\"\x96\xf0`k!\x0e\xb3\xaf\xbe\x02\x07\xb5Y\xb8\xdf\xd2z\xa1t\xfa$\xc1\x9a\xe9\xa2\x96\x01\xcf\xc3\xa88>\xf3\x97K\x92\x1f\xa0N\xd6\x87\xaa\x8d\xf3I\x9d\xf9\xf6\x8f?\xd8]L\xcf\xcbi\x11\x8f\xed\xad\xefW w\xabT\x8aj\x88\xc67f\xd8\x0b\x9e=\xea\xab\xaf\xc0m\xf4A\xd1\x83\xddZ\xaa+`\xef \x07\xb0\x1e}tY8h\xb2Y\xcfI\xfe\x9a\xeb\xc7F\xae\xaf\x88\x93\xeb{q\xc90\xdd\x1d}\x9c|\xedU\x12\x86_\xa28~E\x02\x12m\x91;\x91\xd5\xdc\xb7\xce\xc5Ps\xea\x9fxw\x99R\x88G\x97\xda\x83Hd\xa2\x02 \x1b\xee\x84\x1cf*3\x9a\xcd\xeeJ\xab\xed\xe4F\xad|\xd4#q\xa8\x07,%\xf5h\xc4Q=\xd9\xac\x91w\xf5\x81\xe5b\x88:\xf7u\xad \x17\xcd\xc6{53lJoP\x18\x86\xd2\xd84\x1b\x8c\x03\xa1\xff\x9d\x893#'\xbfm\xa2\x9c\x84\x8cT\xe1\xae\xf2\xd9\x19L\xf72\xba\x89x\x8b(/J\xb7\xb3\x01\xb1\x90e\xc1?+jZ\xdam\xc7bTe\xd1\xee\xee\xb4\xfe\x86lo<\x99\x18\xf4\x01\xbc\x05\xec\xce+\xc3q\x9fX\xee\x8f|@V\x8e\xb4\x865\x98\xcb#.?sm\xaf\x9e\xd7 Z{\xfe\xa6%\xaa\x0b\x95\xb7\x1e#\xad\xe9M`Mo\xc2\xea\xb3\xe6\n\x0f\x85\x91\xde`\x95\x07cj\x11\xafX\xa5gGB\xdde(\xef\xc0\xa0\x1f\xa5\xebu\x9a\xd8\xbcs\x81^\xd9\xce\x8fE\x9a\xb0\xcc\xe7O\xd2|m*)\x9b\xbb\xcc\x98\xfc=\x0b\xaaQ\xc2\x9e\n\xc7\n\xc6n\xa8\x01\xcf\xe0\xb0\xc9\xa2\x9c\x9a\x0b\x98\xceM\xf6\xac\xb6\xc1\xc9`\x15Y$Zk6\xd4\xf6#\x83\x95)\xa8\xec3\x85W\x15S\x10\xd8\xea\x06\x06\xbbP\xd0\xf4\x8f\xa2\x9fh\xa4\xf3\xc1{\xf4\x135\xcd$E\xd9\xc8\\hot\x92\x91I\xbbwk\xf3\x93\xa1\xf4X\xc3\xc2\xa3\xc9\x05\x04\x83\x8b\xb65\x8dL\x81\x12R\x97\xe1\xe4\x88\xe1\xafm\x0d\x8ds\x06nSC\xe3\xb8\xb13\xb8\"\xddT&\xa4 \xde\x94!MEC\n-\x93\x12P\x89^\xfd\x81\xef\xea]\xb9H\xf3\xb5\xaf\xed\xe5\x0b8\x04\xf4\x81^!7Rv\x18\x11\xed\x86x \x87\xf0\x82\xbdP\x1a\x10\xf45%\x00\xb47\x8f\xfd\xd2wL5\xf8\x9eS\xe8'\x15t\x94\xd4\xa1\xe5\xea\x97\x9e\xd6\xc3\xae\x19\x0e5\xf8\xaf\xa2\xf3(\x0cD%Y\x17T\x16\xc0\x81t\xab\xc95\xaf\x9f\xe0\x10\xde\xc1Cx\xd7\xe5\xa1\x1cM$\xe7+8\xc4\xc0GW\xd4\xa2\xe8\x12\xf0\x91[Vy{\x95_y\x0c\x87\xb0n~e\xe0\xfb\xcf,\x12Y\xbd\xb1\x80\xf9\xcd\x02\xe6 \x1c\xc2\xdeT\xab)h0z\xcc\xe9\xfeY\x8dOl=:\xec\xe03:v\xda\xc1gM\xbew\x8c\xfd\xe1\xb7\x84(\x87\x86\xe37\xf5\xf7\x04h\xe3koh\x9bo\xea\xf0e\xda\x03\xec\xf5~\x1b\x8e\xf5\xed\xb7\xfa[U\x1b\xe3f\xccB\xd9\x15G\xb1\x02FWL\xd6z\xa4\xe8\xf3\xf6\xb3\xdc\xfbH\x17&\xa8\xb0\x99\xd9\xba$4\xdf\x8c\x12\xa7\xe5\xde }\xe9\ns\xf8\x0fq&\xba\nC\xffSx\xd82#\xd2\x06\xa1\xa2\x070\xeb=T\xf6\xa6=\xb9\xf8au\xc6\x00VF]\xddC\xabT\x0dA\x1ac\xbe\x10\xdaS\xf5\xd9\xa7\xea\xaf\xf3?\xff\xef\xefN\xc3\x8f\xee*f\xb39Y\x9a:\xe9cx9\x86_Q\x0fu\xe2\xc0\x0d\xf8\x15n\x80\xf3\xd6\x19\xc3w\x18\xc2\xb7\xf3\xac\xb5z\x92\xa7\xd9\x84\x9fg\xca)p\xffJ\x1b\x1d\x833\xd2o\xb5\x1d\xa7 $YN\x02\xbfT\xad\xcf\xfbq}\x96\xd6\xdb\xbf\xf1\x16\xc6\x846\xfe\xfep\xab\x15i\x9c\xe4\\g\xdcb\xdbq\xba\xc6\xb0\xa4}~%\x94\xe3\xaf\xae4G\xfa\xb1\x89\x9dgnW\x14o&\x14\x83\x0c\xeeR\xe7\xff\xb0H\xa9~\xfe\xb3\x1f\xeb\xcb\xb0\xc8g\xa8N\xa0\xbf\xa63\xf2X\xcc\xc8\xe3\xff\xf8\x19\xb9\xc2\x1a+;8wV\xdb\xa9\xe1\xe2\xa9!\xca\xe7Zz\xcc\xeb\x9f\xc8\xbei\xc2\x8a\xbd3\xd4\x0b\xc3\x1f\x7f\xc0\xde\x13\xb3$\xab\xed\x87\xca\xf9\x85\xb2+\xea\xb5\x14\xbdw\xbe\x89\xbe\xfdn\xebG1\xa6\xe2@V\xb4\xf8\xe6f\xf4-=\xe6\xe0\x06\xbc\xb1\x88\x8eo^\xc2|\xaa\xc1\x8f\xda7\x8f\x07\xf5\x8eU\xc9\xcd\xde\x8fZ3\xd5\xe0\x94~\xfb0s&\xd82\xbbi\xe3*A6i\x8d9\xfbM9\x98\xd7t,{\xcf\xb5'Z+\xcb\x13\xc6\xdc\xce\x0cY\xed*)\x07\xcb\xebP\x94\x8a\xcc\xd3\xa3\xad$o\xd0uX\xebM\xb8N\xf3'5\x84`\xabf\xf0T\x0d\xd4\xd8Z\xf2\xedVK\x9d\x8c\xd5\xa2\x14\x0f&\xd0p\xb9m\x83\xcfXx\xbd%\xef\xbb\xabV\x84\xd0\xc5+fB\xccc\x7f\xea\x1a\x12\xf5\\^(\x11\x087\xc3\x0b\x0d\xc5:\xd2-\xab\xf5\xba\xd5\x0e\x96\xdd\xba\x88\x06\xa4\xe0\x0e\xd9\x9a\xacVvZ\x1f{\x8d\x8f\x98\xb3\x8e\xd6A\xb3*\xa2\xf6\x8d<\x89\xa5\x84H\xefX\x01G\x816M\x1d\x8en\x9a\x84K\xda\xac\xa9\xc9\xa9\xec\xe0\xc7\xa4,\xa3d\xf9$\xcd\xdd\xa0'g4\x183\xcdD\xd4>k3\xf8\x89\xb96PY\xf5'\xe4U\xd4\xaf %\xa7~\xf6\xae\xca\x89\xf9\xfa\x97R T\xaeT\x81\xca\x95*P\xb9R\x05*W\xaa`\x98+U\xe0\x16\x8d\x8e\x06jO\xe2\xe0\xe3\xfb?-l\xfd\x9f\xbe\x04\x98\x0b@\xfb\x00\xf38\n\xde}j\x87\x17k?$R[?4goevS\xc30\xcb\xe0\x1aU\xferma\xe2m\xfd8\xe2\x85\x1e\xfcu\xe1\x9e\xa4c\xf0\x91\x02UO\xbe'\x8b4'\xfcp\x12\x00\xa8\xb7\xe3\xb3\xe4\xa5 \x7f\xca|::7\xdd\xd1\x18\x12\x8f\xf0?4\xc7\x82\x18\xb4\xf6\x04\xce\xf0\xf4\xd5\x9c\xa3kn\xe1\xe8\xfb\xec\x02\x12*\x837\xda\xcb<\x0d7\xc1\xb0\xb8\xfe\xca\xdb\x8f\x8d\\\x92r\x80\x7f\x94\x19\xc9O\x04 \xae^\xf5\x1a\xeb\xf8\xdb?i,\xbf)\xf6y\xce\xa2\xabme\x93y\x99\x00G)\x10\xe1G\xfc\xd8f\xa9\xa6\xae\xdb\xb1\x8d\x19X\xee\xab\xb2\xc6H+\xa0I\xd3\xc9\xf8\xaat2\x1bU:\x99B\x95N&\xe6\x0f\xe4\x15\xd0Z\xb9c\xaeY\xc6\x98\xfeG\x84\x1e\xfa/\x0f\x1e<\x90 \xe9\"M\xcac\xa6\xcfv\xa2\xd2\x8f\xa3\xa0\x1b\xa2\xd3\xfa34\xd2'\x03\xe3\x00m\x1a!)\x83\xd6\xab\xbb\xa4\xf6\x93\xee\x94\x1fc\xc72\x03\xaf\x18\x02#\xff\xdb\xe9\xd1\x8e\xa5\x9b\xc0L\xb9`\x00\xf5\x82\x81\xfeEP\xb1\x08\xc62@\xc0\x19\x04:\xac\xb6\x17\xd1\xc8u\xc4\xd6V\xf9\x05C#\x94\x06\x9ae\xe1wVyC\x87\xd0\xf2\xfe\xeb\xe39\x01\xf46&C>\x06\x90\xb7yz\xaaI\xca\x00\x9c>\xff\xc0\xcb\xa9\xea\xe3\xe4\x8dI\x06@\xde\x85\xdd\x86;$\xd3\xc0\xd0.M\xf2\xf4l\xd7^\xed\xd2\\\x90\xc6\xfa\x05\xb8l\x92\x02\xd8\xb1\xddV6\x82\x8f\xdf<\xf3\x1a\x1a\x90\x05\xa1\xf4HR\xe6\x17\xb2\x12\xb9&\xdd\xb1\xf0\x01\xee\xc8?d\x0c\x07\x06\xbf%\x10\xee\xbb'\xfb\x9ax\x10q\xa1\x0b\xef\xc9\xd4\xa2\xda\xcf\x9e$\x1f\x83\x1b\x8d\xaa<\x81\xeaL\xd5\xe2\x12N\xbc\x91\xd7\xf1\x19\x7f;\x12N\xb4\x1dOr\xee=\x02\xb3\xc6S\xa3G\x89\xb86\xb2\xa6Z\x0e\xec\xfa\xee\x9a\xd8W\x8b\xbd\x0c\xe2HJ\xb5`\x97\xf0\x0f\x10\xd7P|\x06\xd6lz \x13\x94\xb8vl:\x92(\xa3?]o|^Fb\xa39H\x13\x9b\xf6)\x97\x80\xb6CGx\xcb\x991\x95\xbe\x83\xa6D\x83\x97\xa0\x80\xe5\xdcb\xa6\x1f\x94F\xfdX\xc3t\x93CHS\xbd\x83\x94c\xeb\x88?x\xcbP\x82\xba)\n\x85x\xf7\xba\x89B\x9fT\x83\x19\xc8\x04\x1e* \xb9\x81\x10xP\xdc\xf93\xa8/\x1b\xfc\xbeDK\xd9g\xf9m#5m$\x90k\xaa/\x19\"m0I\x83\x84Q\x99\xe6F\x0d#SF\x92<\xb7P\\2md\xec_\xa4\x9b\xd2\x02\xbf\xb3p\xb9#\xcc \x884\xdcH\x18\xe55\xf8\xf3\xd5\x07\x84\xcaL\x04\x82gv\x8a\x8c\x04\xe6\xe1\x84W9\x9c+\xeb<\xf3\x0b\x93#\xc8h\xa7tj\xb6\xfc\xfc\xa2\xcdL\xeb\x93\xa7C+\xcc\x19gA>\x05\x0c?u\xc7;\x9e\x95\xa5\xe1h\x14\xec}\xd9<\xa2\x94V\xea\x9d\xf6jo\x9f\xaa\x8f\x9f\xf7c,Mgh\x86\xe9\x90\xf4\xa7\x87\xd031\x7f\x1fVg\xaf\xe9+\xcd\x99\x0fx\x08+\xb7\x03\xc5\x1c\xc3\x1a\xae_\x02\x16Co\xc4\xcd\xcc/W\xf8\xbe\xb2\x1f\xc5\xda\x8f\xe3F-F\xbf\x84\xee\xeb\x0d\x7fW\xf5gt\xce\xebFw\xff\xb3UT\x92\xe3\xcc\x0f\x98k;\x99\xe0\n\xabw\x95U\x15Gi\xaa\x01>\xb05)\n\x7fI\xb4\x07\x8b\x16]\x8cC\xc2\x8a\xa0\x93\x90\x04)3\x91;3p\xb0\x12\x8aah\xc1&/\xd0\xdc\x94\xa5QR*\xb9\x1f\xd9\xd8\xb0\xb6\xb5\x8e\xe6i\xaa(W\x07\x7f\xe2\xcd\xa3$t\x19:\xe4R\xbb\xb6\xf3\xe3f\x9dA\x99\x02\x1d\n\xc5\x96\xbc\xd6U\x88\x1fm\xb24\xd4\x04\xb6\x13m\x91C\xe5\xbc\x8c\x8f\x92ZtwJ\x8e%h\x9fEE\xe9E\x05\xfd\x8f\xdb\xd9\x0c\xf6\x9bI\xb2\x97\xb8\x9f\xb0\xc7v\xd5%>\xc4\xd2\x804\xc8!\xfa\xe3&\xe8\xe5\x91c\xcc\xa4\xdd\xa7\xd3\xa4Z\xc6\xd6\xe7v\xde\x19\x9f\x90\x90Z\x13I\x0c\x0fB\xc4\xfd\xc8$\xcd~3\xff\x99 \xd5\x95\xd2\xa86\xd6Z\xd1\xab\xf6+\x06\xda%\xd3\xd6\xad\x94\xda:\x17\xd3k9\xce\x88W\xa4t\xc0\xb1\xb1\x1d \x11\xfcd\xff\xadW\xa6o\xe8va\xf5\x8a\xe0\x06\x10\xaf\x88\xa3\x80\xb8\xd3N\xc7\x04-\x81^\x1d10\xa7\xccm\xf2\xa4-\xa51\xfb\xc2\x17\xbd.\xbf,\xf5\xbaA\x95\xbb\xefO\xa3\xe1\xfd\xe2\xa0jQ\x01\xe9\x12>\x87\xe2\x13u\x12O\xdc\n\xd7\xd0\x93\xb0\xca\x92\xf58\n\x9f\xa7\x9bD\x16Td\xab$\xaf\x95\xe3\xcdl\x1fE\x95\xce\xa837\n\xf0*?R\x7f\xb2\xda\xf3!;J>`\xea/\xd2\x1bT\xfbN\x9d\xe6\xa9s\xbf*\x9d\xcf+)0\x9dH\x13G\xa4\xc3\xbf\xc4\xf8?\x81\xb9\xa39\x04\x93\xb5\xa3\xe2\"M\xa6\x0e\xec\xaeV%\xddv\xb3\xda\x89\x89\x82^\xc8&\x8edR^dD\xb0\xb7\xc8f\xba ?\xfe\xa5\x9f\xd1\xe9\x11\x0b4\xd6\xec\xd4\x03s\xcd\xf4\x9c\xf5J\xab\xf7\xd5\xc4\x85\xa9\x06SZp6\xe22\xe9fR\xe6C`\xa5\x953\xe8\xdb\xf8\xa05\x81\x9bR\x8fm\x80\xaeE}\xc7\xda\xe9z\xa5\xdbB\xcf\x98I\x12@\x8fzU\xa9\xf9\x08\x93^~\x93\xe6\x16cI\xb5co\x91\xa7\xeb\x1f\x8fG\xee\x89C\x0f\xb5(@.\xff\xe6\xafE\x9a8o\x1b\x9c\xe3\xf8\xday:\xd3\x1e\xbd\x10!\x06\xcf\xa2\xe4\x9d&5\xfcug\x10\x13\xf7\xb6* \xfdg\xc9\x18^\x05?\x98H\xf9\xc1\xa8\xe2\x07\x93\x11\xe3|\xf6\xbf\x86\x0d|\x03\xc9\xd7\xb0\xa1\xfc`t\xb2i\xf3\x83\x1b ?(\xf8\xcd\x0f\xc5\x08F#M\x12i\xcc\xb2\xf8\xda_\xa2\x05\x17u1\xa7\x8d\x1bLx\xa5\xccn\xa1X,\xb8B\xe6\xad\xd9\xb2\xc5i\xaf3:5\x98\xb1\x96\xc7\x003\xfd)\xf2F\xb7\x87\xa8\xe6G\xe87^d\xd7\xb9\x87\x9f\x80c\x1a\x14\xadf\xed\xf4\x91\x0fq\xfaH\x07\xa4\xcad eK\x7f\xb9$aE\xb8\x0b]\xc6G\xcc\\lv 11\x0f\xf6\x8aB;\xee*\xdd\x92|\x1b\x913S\x8d\xc1\x17\x1c\xceA\xa1p\xb0\xf56\xad\xad\xb7U(\x9d6\xaa\x1e\xf8$\x9f4z\xe8/\x0bg\x0c\xa5\xc1Y\x98y\xcf\x08\xa7\x92\x08\x1dI\x8c\xb6\xe2\x9dye\xa86M\xd5OT\xc2*_\xb8\x84\x9f\x05\xec\xe4\xb6\x00\xf5(sF\x1d\xe8\x9cl\xd4\xee\n\x00=;F\xf7jbPL\xd9\x95\xe6\"\xe9}\xd3\x85\xef\xaa3A\xa7\x87\x1b\x0e\xf3\xa2S\xcd\x89o\x9a\x90\xda\xef\xc1\xe0\x93j\xf4}\x00\xd6\xc3t\x00\xab\x0f-\x0bN\x992\x86PG\x06\xc4U\xa7\xeb7\xc32b\xb36d\xb0\x15\x17\xf33\x8b, \xe9N1$G\x05\xce\xde%\x0d/\xad\xc6\x06\x1e\xc3\xc6\xd29}g_\x0b\x10\x1b\xcc\xa2\xa7\xc6\xf8[q\x898\\C\nSzE\xe1\x0c\xd2*\x19\x93\xc5\x0bt\x8b%Z/\x9c&\xe4\x8b\xec\xa9\x19u\x9b\xc0/s\xb2\x88\xce\xb1\xb0]\xbd\x0c\xc6\xb7W9Y\xcc\xc0\xf9K\xf5\x12\x8e\xc6\xa2\xd9\x8a\xde0\xda\xa1'\x1a\xb6\xfe\xdbR\xb0&\x08&\xca\x8f\xfeM\xe0\x1bVUDM1o5\x0c\xfa?\xa5u\x9cv\x01L*\x0b!J01\xc9\x1eHm&\xad;\x03\xe5[\x83SI_\xa4\xb3\x12D\xa4\x04\xc7Z\xe4\x10\xd2\xc6\xae^\xc9\xcd\xfa1\x1a\xbe?i$.H\xbcS\xfe\x077VQ!\xb0=\xaf\xff%\xf9\xc4\xe5\xf9}\xde\xea\xc7\xe5S\xf964\xb1\xa8\xed\xed*'\x91\xcc\xc3\x98\x8fb\xe4\x9e$\xc8\xdc\xc0\x1e{[V\xe4\xbf=\xab\xd7\x8a\x81\xd7\x1d8I#\xd7\x83\x89Y\xc7\xa1\x9b\x98tJ\xcev\xe2\x9fc\x8fnE\xdd\x99\xc3(\xa5\xe6\x0c1\x9a\x99\x81\x87J\xffB\xa2\xe5\xaa\x9cAN\xb9\x9dy\x1a\xb3,\xa4I\x9a\xaf}m\xfc\x9ez\xec\xb2\xe4\x00j\xf0\x96wl\x9c\x06\xef\xaad\x04\x94e\x1b\xee\x05l%z\x08\x9f\x0b;\xe9\x83\xce\xca$\xf6\xe7$\xc6\xf3HQ#|\x0cI\xdbT\xbc\xb3/\x03(\xdbW'\x1f\xb4\xb0=\xd8\x1c\x1b\xff\x05\xd7B\xcb\xf84Y\xa4o\xf2\x18\x8f'\xfa\xfb{\xbf /\xfdr\xa5Q8JS+\xa4\xaa\xd4\n\x91*\xb5\x82\xafJ\xad\xb0Q\xa5V(T\xa9\x15\xe2Vj\x05\xb4C\xb7\x01\xea\xdc\x0b\xdcR=\xdd\xbf\x16\xa9\x17zsn\xc5\x11h\xdc(\xbeD%5\xe1\x86\x9eY\xab\xb4\xd0\xe8x\xd8\xa95\xe7\x8b\xb5\xd3q3(\x16\x84\xb64\xd9\xe4jR\xe4\x9c\x00E\x1dx\xf3\xea\x19\x96\xc1-\xd1g\xc1\x81\xb7\xbb$\x80\xd11\xb6vn\xd1\x06\x0c\x85O\x8c\xa5\xd0\x9b\x05\xb8\x12l\x053\xc6\xc2\x00\xac\x85\x81\x98\x0b\x15\xf6\x86~i\x90\x89\x93\x01\x1aM\x00h:\x9e\xf3\x94\x9c\x7f\xfc\x01N\xb9\"\x10\x92-\x89\xe9\xc9c\x905\xd3\xfa\x0b\x14\x93-\x14|\x1c\x9a\xac\xfd\xc8\x08\xefc\xf2<\x87\xb2p\x16\xf1\x1fV\x8cL\xaa\x15/mX\x1e\xa3\x86\x8aq\x94.\x96\xf5*\xfc$*\xa3\x7f\x937y\x99%r\x90\xfb\xbb\x9d8\xc5\x14\x9e\x945\xd4\xb1\xf3L\xb5\xb9\xc9c\x1d\x10\xb3\xd3\x08\xee\xc4\xe4\xe5^\xa2\x0c\xa9\x83bR[S\xca\xd3A\xc7\xcc\xea\x83L\xee\x15x\xcdc\xee\x98\xbc\xcaV\xa8\xa6\xe1\xb1\x8e\x86\xd3\xdeh\xf99\xe4\x984\x829c\x085\x06\xbc\x9a\x19\xd4\x9cZ\xcd9\xd4\xba\x91\xb6\xcfA\x85\xa3\x8d\xfa\xa4\xb8\x949\xb9y8\xb0\xda\xfe\xd7\xedp(T\x87C\xa1:\x1c\n\xd5\xe1P\xa8\x0e\x87\x82\x1d\x0e2\x92_||\x92\xaf\xd7\xa0\x7f!\xf9\xe2\xb2%\xf9\xc2/v\x97 Z\xc6\x1cXo\xa1\xf8Zn\xa1\xeb\xc1_\xf5\xf7\xd6\x17v\xea\xcf\xb2\xb7v\xd6/4u\x0b\x8b4Ugp\xfa\x8f;\xf7\xae\xc7\xa6\x157\xffDB\xd1\x97\x94B\xda\x94BO0\x9f9K\xff`4\xe5\x03\x9fO\x1ed\xd7\xc8 $\x17\x06\"i\\\xf4&\x0b\xfd\x92\xb0\x86e\xc6\xdbO\x9e{\xe8\xd2d\xf2\x03K\x9d\x83\x82\xae\xa5\x96\xfdG\xa9\xd6\x90B\xe9\x8e\x13\xa7~\x18%K\x96\xd5\xb8\xf4\xf8\x9f\xc7\xa5_n\xb4B\"\xc5[g\xe1G1 \x07\xbf\x8bn\x85^\xb0\xc9s\x92\x94\x1cC\x0c\xd2\xeb\xef\xef\xb5\x82(\xba\xde\xb9\x1b\x0f\x0b\xea\xd1\x9e\xe5$tF\xdc\xdb\xb0y\xff/\xbe\xefk\xb3\xa07%W\xfa/\x8e\x0dmw{S\xfe\xbb\xaa\x1a\x7f5\x07$\x8e\x1f\xebU\xfaQ\xb2CN\xfa|XK rf\xaa'|\x9d\xce\xa3\x98\xcc`z0\xb4/N\x94d\x1b\xfbTCut$\x9f\x05\xfe\xba\xf2\xe5,\xf6\x03\xb2J\xe3\x90\xe43p\x18\xea\xc0\xfc\x02J\x7f\xa9y\xab\xbc\xc8\xd0\xbeE\xceu\xdf\xee%*j\x12M\xf5k\xd5\xc1_c\x8aS\xe6\x1b\xe2T\xd8\xe28\xa0U<\x84U\x81qs\x14\x94\xdcn\xf6\x81\x13x_O^*S\xf1R\x99\x8a\x97\xcaT\xbcT\xa6\xe2\xa5\xb2a%\xc53\xca\x15\xb4\xeeb`L\xa6\x89\x9cY\xe0\xc7\xa6\xfbR.,\xfb\xf8\\X\x08\x87\xf0\x84\xb7\xef!\xebAwO\xbb\xcf\xfa@\x1a\xe8\x84\xd7v\xf0\xa4yYse\xc0{\xa7\xe6\x96\xec8%\x11iK\xfb\xa4Wmn\x19|\xc4B\xa3K\xbf$\xd2\n\xae\xe2\x8a\x8a\xa30*\xbfO\xcfg\xb075\x12\x0bGI\xe4#\xc3.\x86+a\x80`P\x02F\x18\xc0\x13\x81H\x95\xc3\xd8?\xacq]4\xa7\xbef\x96\xac\xcdc\xaa\xd3dx\xb6E\x90\x8cD\x9boB;\x14U\xa2\xb7\xa1#\xf8d\xfel\x8c\xcf\x14\xe7\xde\xa34)6k]\xfeD\xa8\x9c\xd62?\xf7\xd7z@\xe6\xb5\x16\x15\xbcf\xb6\x1e8\x1a\xc2\x1eC\xe5\xb7\x96\xf9\xe5\xea\xb9E\x9a\x8e\xcd\x003\x0ep\n\xbfq\x9d\xefYE\x1c\x0dk\n\x9c\x82o\\\xe759/\xbf\xcb\x89o\x02\xcf\x18\xf8*Z\xae\xe2h\xb9*\x1f\xa5\xa1\xd1\x81,d\xef4R\xf0\x99\xde@\xef\xed\x08\x8bg\xe2Z\x91\x92\xe4\xbfD8[\xfe\xf7\x17OC\x92\x94Qy\xe1\xfa\xdc\xe7<\x1fyu\xd9\x94\xc2\x19s\xd3\xf7\xb3\xa8(Gn\xf7\xc8\xea^[,\xa7\xd9\xe8\x1c\xdb*\xae\xcf?\x9a\x93\xdf6\xa4(\x1f\xd9\xf7~\xddBb\xfai\xc4\xccN*Wq[\xf8,\xc8\xde\x98\xd5\x8c\x0c%\n\xd5\x03}\xfbK\xd1>\x12~=\xec\x05\x1c\xc2\x92\x89\xc7z\xc09\x02V\x07\x85\xd1[\xed\xca\xaa6\xcf\xd3\xf0b\x82X`\xf0zpB\xbf\xf4\x19\xe4\x04c6f\x907#8\xec\xdf\x8e\x92\xfa\xdd(\xd1\xd5\xfc\x1a\xc3\x9c.k\xaa\xa9\xae\xb9\xd8m\xb0\xa7\xa7\xc8\xf0\xc3\x0dpW\x0d\xeb\xa3\x03Q\xb2\xf5\xe3\x88e\x070\x0d\x8a\x93\xdf\x0b\x03\xadk\x8b\x0e+? c\xf2\x82\xdfT\x8f\x9d\xee\xbc\x0b:z\xd5\xc8\x8d\xce@\xaa\x91\x13\xab\n\xa3bp\x9a\x1ej\xca\xae\xee\x8e\x86\x13\x96\x91U_P[\x87\x11\x97i\x9b\x84Q\xa9mX\xd5h1\xa0\xc19\xa6\xa0(\x13\x08\xfc$ 1H\xd6\x86u\x04D%\xb50*\xd5PF\xeck\xa4\xa9(\xd3\xe52&O\x05\x99\xd1\xef\xbc\x87\xe0<\xc2\x1ebG\xe8+u\xd5\x02\xcd\xd2\xb3\x0c\x0e\xa6\xf9X\x95\xeb\xf8 \xd6q\xd8i\xbe\xdb\xf1N\xceKq\x8c\x89L\xb4\xc0\xca\x92\xa9?`\xf4U\xe3\xf8\xbf\xd5Oo;\xf1\xad\x89\xeb\xa9(\x81\xc1\xf9Z\x81\x9d\xad\xe4\xcb\x9a}\xa9L\xea\xd4\xbb\xab\xf0.k\xc7\x9c\xd4\x87\xd1\xaay\\\xf6D\x1eq|\n\xdf8m\x02\xe0\xf6\x04\xe0\xf8\xba\xef\xfd\xfe\xbe+\xbfW\xf3\x17\xca\x1f<\xaaz\x10V\xcf\xdf\xb7\x95\x03\xdb\xa6x\xda\xe5\x97\x9b\x98y\x05\x89\xd9\xfdY\xcdLDU\xde\x10T/\xa5B\xbd\xa4\xd0\x1cQ6\xf9\xe6\xf9:\xbe\x19y%)J*\xceJ\xe1(\x83\x8c\xcbf\x02D\xab\x08<\x84\x84\xc7\x80\xd0\x9e\x9e\x9e\xafYu\xb0\xe6M\x99\xe7P\xb4\x00\x97w~\xef\xf0\x10\n\x9db=\x86C\xd8C\x8e\x0f\x93\x17\xfe\xfe\x9e\x8e\xb2\x903M\xc4+HyLY5W'\x1c\xe1fW\xd4\xb0\x1e\x8d\x9b9\xf1\xf5\x9eH\xc5?\xd7\xb1V\xa1\xd7P\x06(\x12\x9cK\x94u@\xe2\x82\xe0\xdc\xb6\x92\xf3\x17x\x0c\xb8\x0e\xce\xb1\xaa[\xfa.i\xbb\x83L\x88\xacEMc\xda\xcf\xb5)\x0d\x17\xf8\xd97\xad7\x14\xd1I\xafXvK\xb7\xe3R\xae$J\xbcE\xe2E\xc9\x82\xe4\xc7X\xe2\x7f\xe4\xe6<\xdaF\x9dg\x8d\xbe\xb7\xa0h|\x8c=\x16/\xa6\xa8\xefT\xcc\x07+\xb0\xf0K\x1e\x95\xe4E\x12_H\xf3]*\xe6EL{kf\x14\n3\xa1\xf7Lj\x19B=~\n\xf4\xcf\xb5\xa44\x99q\xaf\xf0}\xa2\x90\x90\x0d\x8bOw\xd1i]bc\x0c\xa9|\xdc\xa7C\x06\xee\x92N\xed\x0e\xf8\xe3\x0f\x08G\x0c^\xfa\xf96\x03>\x14\xedl\xe8p\xde%\x98\x89\x82`\xa6\x1d\n\xac\x82\xa3\x84=\xa7Bl\xcb\xe0\xea\x95y\xb4vYA6\xbd!\xb6\xb1\x85\x95ek9\x99\xe8\xc7\xba(\xb0\xb3\xc3J\xea\x8eUh\xa8\xa6k\x0c3+\xd9\xf8;v\x8aURc\xbe\x14^\xc2\xfc\xa8\x0c\xc9\xef\xe5\x96\x8e\xeb\xe9J\x7f\xdd+\x10\xd0\x1f\x0f\xee\xdf\x1a\xfd9\x8a\x10\xfc\xf9\x1c\xc2\x189|\x92\x06\x9bK\x96 \xe2$\x88\x15\x94\xa1\x1cB\x98\x068\x0e\x8f\x9c\x93\xe0Q\xba^\xfbI\xe8:A\x9a]\x98Sd\xc9\xa8\xd4\x07\xf3\xcc\xf0\xb8\x12R\xcd\xb4\x95\x9ck\x88\xeb9%W\xe0\xfd\xae\x0e\xce\xac\x8bK:\x8fX\xee&\xd3\x17\xd5T\xb2]\xbf'\xa3\xd2dQ\xaa\xb3\xcb+\xdb)\xc9y\xe9\xe7D](\x11P\x14CTj)\xbb\xf0\x8ezrs\xe2\x87\x8c7b\xb6q5dk$tZ\xd4\xa0V\x89A[\xc52/\x91\x0bT\xb0E\xf2)\xfd\xa0\xe6\xf7\xebP0\xa7\x7f(m\xe8\xa14\x95\x9dJ\xf4\xc9\xf4\xbe\xecX\xa2O\x1eLUqlj\n$\xbc\xd1N$\xa5\x08(\xe3&\xab?U\xd9|\\gE\xfc\x90\xe4EW$\xa5\xe2h\xe9e\x9bb\xe52T\xc3\x84\x9d\xec\xef\xc9?\x9d\xb1x\x9d\xe5\xd1\xc5\x18N\xfe\xf8o\xce\xdf\xb0zf\x9d\xa1\x08n\xc0\xdf\x9c\xbf\x8dx|\xf4\x06M\x12*V\x93\x9e\xaa{\xfbrTC\xb1Wa@\x0e$9C\xc5U\xe6\x17\x8a\x8dP94.\xc6h{\xea\x9c\x1b\xdd)\xf2HR\xe6\x11)\xa8\x90\x04{.\x16\xba\xa1\xc7i\xe6%\xe4\xbctG#/L\x132\xfa\x9a\x8f\xc2d\x8e\xc4L`6\xd6\x91\x15\xefZ\xe3\xc8\x0d\xc7p`R\xcfS\x9e\xedd\xdfP\xa1b\x8dPS\x89#\xa6\xb8(\x12\xad\x1b\xab\xff\x038\xdd\xd5\xde\xc2\x0dpf\x98?m\xcdW[N\x0b\xfa\x84\x00\x02\xbf\x0cV\xa0>Yc\x86\x11\xb8\xc2}{\xc1{XD\x89\x1f\xc7\xaa\x15V\xaf=\xbd\x98\x12%\xf3\xf8\xa1\xd5\xf8\xed*\x06`h\x0e\xf8\xd6\x89GP\xae\xf2\xf4\x8c\xbb\x07u/\xc9<\xfc\x97\xfa/\xfaA\x8e\x8a\xf34\xbc\x90\xa5\xd6\xa1 \xcez\x13\x97Q\xe6\xe7\xe5\xcdE\x9a\xaf'\xa1_\xfa\xcc\xd1\nG\xe6\xbc|q\xfc\x9a\xfd\xdd\xdd\xbb\x1aNa\xa9\xd9\x8f\xc0-|:\xa7\x8e\xb9f_\x82q}\xaa\xfdy:\xc6\x8c\x1c\xf2\xfd\xc9&\x057\xe7\xc51\xf9\x8d\xefN\xdas\xf7\x14\x0e\xe1\xac\xbb;\x97\xc6\xdd |\xf4G\xfd\x8dw\xca7\xacq\xfb\x01\xcf\xf5qd\xdc\x82\xc0\xb7\xe1\x91v\x1b\x02\x9e\x08|\x0f>q0h>J\x8a\xd2O\x02\x92.j\xae\xdb{\x12\xa1\xb0\xd0\xda\xa0\xe7t\x83\x1e\xfe\xffq\x83z\x89\xbf&\xf4\xef\xaf\xcb\x8b\x8c\x1c\xb2{\xf4'\xdf\xb9(P\xf7\xde5\xeem\x90\xe25X\xedq\x10\x98\xb4?F\x8c\x91\xdb\x05m6\x9f\x1e\x9f\xe8\xb5\x87\xc1\xfcg\x8d=\x7f\xa6\xdf\xf3`\xd94\xf0}x!\xf6\xfe|\xe8\xabe\x0f\x1b\x94\xb7#E\xb5 \x84\x97\x13t\x07uo\xfe\xeb_\xc9\xcd\xe5\x18\x1c\xa7\xab\xd8\xe3\xe3/e\xe5\xac\xdb\x1c\x8d\xcf\xb9\x93[\x8aJz\x9b\x8f'\xc4^7F\xefK\xcc\xca\x97\x98\x95O\x11\xb32 Z%B\x95c\xb0\"k\xab\x9a\xd7\x0dp\xab\xcf\x0b\xf1#29\xd5 c\xa0.K\x1b\xb3\x072\xbeD\xc1/\xa0#\\U_\xb0\x1e\x19\xe2J~\x0dCiZ>\x98\x97\xad\xe3-Q\xde\x148\x01\n\xeb\x1f305\xd6\xff\x9aV\xf0n\xba\xa7\xb1\xd0\x17\x8e\x82H\x9b\xf8\x10\xebr\xdd*p\xcc\xa3\xdb\x1b\xb3x\xfd\xf2c\xff\x00\xca7\xbd\xd2\xad\xea\xbc~_\x91\xf64\xec\xa6\x993;\xae\xd4N+\xbcW\xc3\x95h\xc6\x94\xa3M\x1d\x17o\xc5T\x0e\xf2\x98wF[\x89\xc5\\\xe7[Q\x8c\xdb\xa8\xf6R\x16\x8a\xe1d\x16E\x92\x01u\xfcL\xebdY\xb2\x9b\xf7\xce\xa0Z`\x85\xbd\x95 \xb6%\xbbM[jw\x05\xdf\xf5\x8c\xaf\xf9\xc2\xf7} \xbe\xef\xcfg`\xfa\x14gF\xcd\"\x99\xce\x0d\xcb\xb0\x82|@\x90\x00s\xb1\xa8\xc2\x17\xf91\xac\xd1\x96D\xf8\x02'\xf6\xe6\xd8\xd8\x82\x04\x9b<*/\x1e\xd3}\x1d\x95\xa6Z\xc7t+\xe5\xc6x\xdf\x98A\xf9\x9br\x95\xe6\xd1\xbf\xc9\xf7%\xa5\xb0{\xdd@\xb6\xe6\x15\xb0W\xc4Qx\x05\xf60\x8c\xd4\xe5\xc5&\xff\xf8\x03\xfd\x9d\xae\xc4\xea\xc5\xbax\x890\xda\xcd\xb0\x96\x8a+\x89\xa3m\xce\x86z\"\x02m\xd7\x9a\\\x91>\x84\x94u\\\x9b\xdf\xaa\xb1\xad\xd4\xc6\xae\xcaAX\xb7z<~\xbaJq\xf5\x1f\x9b\xeb\xea\x93zo\xc8\xe3T\x03\xb7ht4P\x1f\xad\xd7\xd9wC\x15Xj\xad6\xd9~\xf8\x80\xd2\x88\xfbP\x89*\xf4\xa1\xc9\x87\n\x1a\xf94\xd2\xe45\xbe\xcchD\xfb\x9e+n\xac\xd3\x90\xc4\x942\x8da\x8f\x07\xaaz\xe4<\xf3\x93\x90\x84#\xa1\xea0\xb8\xc6\n\xf8Y\xff\x13\n\n\xd0\xdf\xc3\xf2\xe9\xdd\x98\xb4&\x18iW\xb5&\x87\x89\x11&\x10S\xc8\xe3\xc8\x94\x1a*S\xb8n=ZE\x9f\xba-\xcd F\x99[\xac\xfeK\xee$\xd8\x86\xeaOI7\x9a\xf7\xc3\xf0^6\x11\xbc\x1f\x8e\x0d[E!9&\xf1\xe2Er\x84\xd3j\xe2\xc5\xf4+\x0d\x15\x1bV\xa1\xb5B\xe7C\xf7D\xd2\x89\x07\xac\xf6F\xdes\x0c\x85!\x1a\x90\x0f\xad\xfd\x11s\x80N\xf0\xf5\x94T\xa3\x19\xb4cw\xd8\xaa\xb6\xf3\xf0 \xb8z\xd4\x82\x98p\x08\x991\x956P\x98|\xaa\xe8\xcd\xfe\xfc\xb2U\xe8b\xae.\xdcl\x88F'\xc1\x0c \xea\xf2\xb6\x0d\xb5\xde*\x8a\xc3\x9c$\x943\xfa(M\xebB\x0d\xcd\x0d\xc9\xc2\xcc\xaasM\xc3Q\xdaxi\x05\x9b\xbc@\xa5[\x96F\x892_\x1c\xf4\xb0\xb7\xba\xcb$\xe7?\xed\xe0v\x1fX\xab\x92\x04%\xaa\x1368\x8c\x8b\x95\xed\x12\x1eP\xe4\xd4\xc7\xa0\"|\x17S\xf6\xcb\xbf Ar\x985a\xbb\x87\xa7\x91J\xf5\x85\x02\x990\xb0h\x1d\xd1\x92\xe8\xb5\xee\xc1\xee\xfc\xeey\xde\xfb\x0e\x89k\xb0C\x1d\xaf\x0f$O\\\xf8i=\x10GO\x9b(v\xdc \xbb\x14\x87~\xbf\x1e\xd2\xf83\xf0\xf9\xbb\x96*\xc11\xfb\xa10\xdc_g\xe5\xe0\xe7!\xc1\xf8A\x19m\xc9k\x7f>\xc8VZ\x99aC\xbf\xf4\x0bR\xa2G\x8e\xfc\xc8\xb6\x92Q\xaa^\xa8\xd5\x12\xbd\xdb\x97\x13JP\x13\x98,\xa2\xa5\x02\x8a\x89%\x86\xc0\xce\x00\x13QW\xb9\x86\x9fS\n\xfc\n\xf9\xaa(Y*E\x18G\xc4\xef#\x8b\x18\xa0k\x1b\x12\xef\xc6\x0d\x97~\xba\x02\xb4HS\xd4\x98\xc1\x98R\xf9\xaa\x8d\x99\xc4\x83\xefc\x0b/W\xc9j7\xb2\xce\xb0-^\xffIg\xafq8\xb5\xe0ly\xef\xc6XG\xee\xc4\xd1\x90\xefG%Y#\x9fY\xd3\x9a\xc3\xc3ff\x9d\xc6\xd9\xf2\x10\x1c\xbe\xb3x^\x96\xc1}\xd3\x07\xadt\xba\x16G\xc9;U\x860\xa8\x92\xd9\xf0$8\x8e9\x9dJ[~\xa8\x86\xa5\x1aDD\xc7{\x14F%`\x8c)\xcb\xbe\xc1\x1a\xe1wX\x154\x8dqd\xd7\xa5\xe0\xe7\xc8\xf5Z\x08\xda\xb3\x88'\xe7i5n\xbbBlTW\xb6>l\xc7\xd6\xb9P\xcc\xb1Y<\x92\xcb\x8c\xe8_}\x05\xe9\x18\x8c\xcb\xa0\xa9\x84\xa65\x071b\xab\xad\x94\xd2.M\xa2\xa1\xf55 \xd5\xa6;h\x1d\x06\xda\xc4'\xa4\xa6\x993\xd0\x14\xb3\x14\x14Y\x97\xef\xb4\xf7\xc0(1~\xdef\xa4\x05\x15\xb1z\x12S\xca\x9f\xf4\xa4\xb2H\xbc\"\x13\xbe\x162\xa9l\xc3\x1f\xf4\xda(\xf8\x83\x9eT\x16K\x0dL(\xfe\xb8qS,W\x1b\x98\x16\x1f_<\xcbl\xc53\xbd\xcfn>\x06\xbf\x7f\x92wy\xdfk\xe3\xb3+\x92\x84ozb\xa2\xc2g7\xed\x8b\x8az\x9f\xdd\xbc6X\x1d\xb6\xb7\x8e\x8aG\xcde\x89\xe3\x01\xabE\xc92\xca\x17\xab\xf4\xcc=a\x94\xb3p\xc6@\xde\xd2o\xf7\xe9\xc0\x989Q\x8c\xbb\xe3\xa5+f\xe9\x0dSH\x85\x1a\xdfN\xa8\xb9\xe6\xbc\xbb\x0dc\x9c6\xf8V\xdd!\x1c\x19B\x9f\x9a\xda\xf8\xe6\x92V\xc7\x05J\xb2Q\xdb\xdb\xb7\x03\xe2E\xc5\xf1*=K\x9aK\xdf\x80\xa6\x1c\xc0[\xccB\xa0?\xa0\xed8\x12\xa6\"\x9d\xa7\xe7J\xdeX\xd5L:\xeejX~o\xa9\xfbu=h\x1e\xb4\xc6\xe3\x93\x84Z\x0f\x8e\x90\x9d\xae\x9ax\xb5ZYY2'P\xf6\xa7\xa9]~l\x97]C\x16\xde\xa7T\xa3\x9f\xf5\x06v<\xabc\xe3\x19\x9d\xe1]\xc3\x19\xed\xea\x1e\x82\xf2\x10\x07\xbe\xad\xd0^\xe2\xf06)g\n%\xc6\x9c\x89^\xcc\xa0c\x84\x16G5\xe7\x02\xfc\xa2\x88\x96h\x931\xeb,\xaa\xe3\x806<\xfd\x1aJ\xf8\xa6w*|\x0d%\xa5\xfcj4\xda\xf2<6\xf5\xa1Pj\x82\xed\xaa&s:\xb4d$\xba]%\xfd\xf6V~\xf1\xe2,\x11l\x0c\xd3\x16b\x04\x02\xeeZr\x92\xd3\x13(9\xc9\xdf\xdaF\xc2B\xe3x\xef\xe3D\x1f\x01S\x1bw\x89\xea\xc4&\xda\xc3\x06\x9aCN\xd8\x81\x9a\xc07PV\xb3\x9b\xe8g\x17\x1a+\\\x9e$\x860\xc6\xdc#\xc9fMr\x7f\x8e\xe7a\xebO,&1\xc6\x9a\x88t\xd3o\x04\xd0\xde\xfe\x18x\xf64\xba$X8\xd1\xcd\xbd\xb3<*+\x88\xd1X\xc1d\x12\xfa\xc1w\xe4B\x1a!\".\xdb\xa0<\xa8\x17\xaa\x9a\xff\x92\x87\x9fh\xa6\xa8\xe27(\xeb\xe66P\x89\xee=^ \x12\xd3B\xe5\xbd\x9c\x84\xe2\xea\xf7\xe5\xbd;\xeao\xb3\xc8\xa8\x8c\xae\xd0\"2\xd5\xb9\xb2\xe2U\x80G>\xee\xb9\xa4\x19\x92Z\x8eD$dB\xce\xe0\xf5EF\x8e\xf2<\xcd]\xe7\x91\x9f$i t\xcf\x80\xcf\x8e\x18\xf0\x0b\xf0\xab\xd6T\x825g\xcbT \xf8\xa014c\x87At\x9a4{\xf9\x8a,HN\x92@t\x956\x08+\xbfH\xfeV\xc2\x9c\x90\x04\xd0\xe5\xd4\x8f\xa3\x82\x840\x81b\x93\x91\xdc\x1d\xb5 \xe8\xb0H\xa8+\xb9\x0f\xf5\xfc\xee\x95h\x97N\x11m\x1d\xd8;\xc4\xcc\x9dt\xf2\x90\xc0V\x13\xd2z\xc2\x98}9\x8e@c\x9e\xdc\xa8\xcd\xba\xf2\xcd\xb1$\xe5K\x81|/\x16nd\xe9\x1e\x0dR\x0c\x1c\x82'\x18\xa5.\x1f\xd2W_\xb1\xc21\xa8\x84V\xa0\xcd1\x9dlz\xe0\xe6\xa4((\xf6\xae7E $*W$\x879a\x1fH\xf3\x06\x1e\x8d\x81\xe2\x99\x037\xaa\x86\x14\xabB\xea\xedX\x9fQ\x8c\x87q\xb1s\xad\xfd\xaaa\x97\xd2\xa4(\xf3\x0d\xe5\xcdL\x96o\xbb\xf8\x8c\x9a2\xea\x8b'\xd0K\xd0\xc2\x996b\x1fX7+\xda*M\xc9'.\x05M\x1cq\x87 \x97\xcfT\xd1\xc2(x\x08\xd2\xfb\x1c7f(\xb9\n\xb4<\x94\x8a)n4\x86\xa62b\x0c)\xbd\xa5-\xd7P\xac\xd2M\x1cV\xef\xbc\xc1l\xa5\x96\x95\x03\xb4\x019\x82\xf5\xc0\xed\xa1\x9d\xd7T\"\xaf\xc2\xb70\xa5s\xd5H\xeeY\xf3 \xd3\xb7\xf0\xb0\xfd\xe7\xacg\x1a\xef^Q+\x01;\xdd\xd7\xaa\x02P\xd0\xa03\xcc\x9f\x81\xa5p}\x910\x1f\x80\x9a$\xbc#\x17\x85\x9b#WNZu(F#\x8flI~Q\xb3\x8b\xdaC\xae\xd1b\xe2E\x05\xf2Ac\xb6y\xb2B\xc9\x0c\x01\xe2\x14\x1e\xfd\xedn\xa2\xb9I\xd1\xcf\x94\x9e\x03\xfd\xeeiW\x12:\xddKO\xa8\x9c\x1c\x9d\x10m\xc7\xe4{\xa0\x8f\xb4\x94S\xef\x18\x06\xbb\xc73\xf1\x9e\xae\xd7\x1b\xdc\xa5\xad$\xc3p\x08\xd1\x18H\x83\x89\x8f4\xbc\x8cNa\x06R\xa5\x19\xb4\x07\xf2\x9e%\x88t\xf7E\xdd\x1d|r\xdd\xb4z\xa14WR\xca\x9f\xdc\xef)\xe9\"\xfe\xa4\xa7\xef\xf3\xf9\x83\x9e\xbeo\xc3\x1f\xf4>U\xf0\x07=}_\xcc\x1f\xf4\xf4}\x81T\xdf\xb7@\xf0\xa0s7\xe3\x1f\xb9\xd7t*\x08\xd5\x8a\xc0\xf0\xe3+\x02\xf5e\x8c\x86(\x02\x15\xc1\xfb=\x97\x0c\xad\"0\x96*\x02\x83J\x11\x18\x8f\xc68\xd7\xfb_\xc3\x02\xbe\x81\xf8kXP\x81%8Y\xb4\x15\x81\x0b;E`a\xab\x08\x8c\xec\x15\x81\x01W\x04.yd\xb2\xff=\xaf\xa9n#\xc7\xf1>\n\xdd_\xcb\xaa\xe0E\xc5\x8b\xef\x8eoa\x01\x87\x93\xdak\xa0p\xc6<\x1e\xc7/\x1cz\xae\x9c8a\x1d1\xe5\xbc\xed\xb5\xf3\x9e\xf7\xeeQ\xc7\x13l@\xff\x1c\xe8\xab\x86\xf0\xb3,\x11\xde\x15h@\x15\x8aN\xce\x8f4\xe7G\xbc\xc0\x93\x1b\xbe\"E\x1aoIx\xbc\x99\x979!\xeeI\xb50\x1d\x85\xaed\x85\\\xbar\xf4\x900\xa5\x17(Z\nU\xdb\xf4\x02\xb1T\xa1\xba\xf9\x04\nU\xbd*\xd5F\xe5\xca\xb2\x1d:\xfaa3<\xcf\xfd\x80\xa0\x8d\x18\xb8#\xb9\xaa=F\xb8,\xa9\x90\x1dE\xb4\xebb\x94$$\x9f\x18z\xa7l\n\x1d&\xad\xdb\xda\x0d\xe1\x9c\x12k' z}\xa4\x99#\xa7\xcc\xb5\x9d\xb1\xcb|\x96\xc6\x98\xf8\xec/w\xef\xde5h\\\x17iR\x1e\xb3o:Q\xe9\xc7Q\xb0C\x9a4\xf5`\xc2\xfa\x90jp\x893GG\x99\x1a/\xa9`^h\xa7(\xdd\xe4\x01\x99\xc1\x91\xbc\xbb\xa3Q\x8d\x80\xe7\x94H\x9f\x8b<\xd0\xe7J\xc3\xb4\x95\x0fw\xc7i\xcf\xa2\x8e\x1b\x0bi2\xd9\xae\xd1=\xe9dj\x80\xa2\xf2\xe4\xa9\x8b\xa7\x8e/\xd8\xf2,'\x81_\xea\x99X\xe0\x02\xe6\nm\xa9^T\xa0I\xf5\x1d~\xe8\x9d\xc7\xad&\x85\x9b\x1b>\x91)\xf3\x1f5\xaf-\xe5\xdc\x03?\xfe.\x8e\x96\xc9\x0c\x9c2\xcd\x0c\xf8I\xaf\x8cr\xff\xc9\xf2\x15\xf7\x9c\xd8\xf7\x0e\xc8\xda\xc03\x1amQ,\x026\xf3(\xfe\xff\x82>\x19p\x08\xce<\x8dC=n\xeaw'\x08\xad\x84&\x0d\x04\xb4I\xca\x86G;Vk\xa5\xde~\xa6=\xa3\xef\x17\xa7\x1c\x99\xee\xfb9\xe7dv'\xcc`K\xa3\xa0A\xa7r\xdd\xb0AIy\x80\x1f<\x7f\xd7s:\xf6sc\xee\xb1\x0c\x81w\xef\xb9\xaa\xcb/\xc7\xddT\x00\x16(\xc7\x03\xbd\xd0V\x99\xc0\x0dp\xf0WN\x7f\x9d\xd2_\xbe\xae'F7\x07!\x0f\x1b-\xf1m\xbf\x00\x83\xd5\xab!\x9b\xf1:\x84\x0d\xcd\x00\x86+\x9a\xdb\xe2\x0e\x02\x81\xa1%\xeeIa\xf0 \xe0Q\xdc\x0b\xb8\xa1\xb3\xa8\x8dd\xd62\xf6\xa46\xa8U\x87\xcc\x99\xf1\xb8\xe7'\xe4\xff\xfc?\xa7\xfdV\xf9\xb1\x0f\xa4\xc4\xea@J\xf9\x81\xa4&\xb2\x18\x8dw>\xe1%b\xbd\"\x8e\x02B{s\xa0,\x08+\xae-/\n\x99\xc2CH\xbd2\xfd\xf1\xb8\xfa\x81S\x9a\xf2 \xb2\x8a\x80\xbc\x0c\x19\x07\xb1\xaf,\x1cU\xac\xc9\x074\x99\xb3{\xf7\xee\xe9i\x07h\xe9\x07\xd8\x1c \x0c\x97\x92K\x92G\x18:\xc6\xc1d\x12l\x86\xda\xf1\xfc\xf3U\xbb\x10\xd4\xbc\xaal\x7f\x1e\xd3\x13\xefX0\x816;\xd5f\xce\x9do\xe0\xef\xf0\xed\xa59]\xc9Q`\"\xd75\xa9\xd6EuZ\xd3\xe9>\x8d\x1e\xaa\x8c\xb5$\xd3\x82D\x1f\xabA\x8c\xe4\x19Is\xb5\xb2\xbf^\xe5z\xa2\x0e\x0c&\xdf\xda\xae\xe8\xaf\x1d\x8am\x88\x197\x91,\x1b\x1f)\xa4W\x9a\xd8\xed+E3\xb0F5\x18\x82n G9T@\xa2\x89\xd2\xdc\x8c\x19\xd5\xa0\x81n\x06\xa7 #\xca\x01(\x92\xad@W\xda\xfc\xe9*\xd1\x11U\xaa\x03\xd0\xf1\xa7/\xe8\xd8\xb8.\x89\x8eL\x9f\xfd\x99\xa3\xe3\xab\xabD\xc7$-\x07 \xa3\x01\xad>\xbf#\x11\x0d\x14Wv\x02\xbe\xba\xec XW\xff\xba\x94 \xa0\xaf\x08\x0e\xe2\xb4\xd0\x94K}\xef\xec\xe0G\x98\x19\xfd\x08\x99\xe1\xee\xba9Pe\xca\xcc\x90\x99\xd4M*\xe2O\xa41\xe4\x99*\x86^z\x971\xa8\xdc\xbc\xac\xdc\xc6\xa0\xf2\xf42\xbbR\x01W\xe1G\x83E\xffd&\xf4\xb7^\x94\x84\xe4\xfc\xc5\xc2\x95\xa4\x12j^\xa6\xd8\xa0%\xcf\xeci\xe1\xfa\x03\xdci\xac\x1c\xe0\xd6\x03\xdcw\xcc&y(p\xe7\xb1\xd2u\xc4\x81h\x02?\x83C\xd8R\xd2~\xb98\x17\xd8\xc5\xbb\x02\xe0\n\"l`wg\x06`\xedo/\x13\xe0d\xd5GK;3\xe8\xe7C\x1b\x9d\x0b\xb5\xeb\x82!\xc4\xaf\xf6L\xf0\xe1\x9bC\xd8\x18\xc8L\xbf\xc2\xd3\x89\xe7yo\xb5#pN\x9c1\xac\x85\xdem\xbd\x9b\xae\x1b:\xfa\xeef\x90\xa9Y\xdf\x0d\xd6:o\xa8\xcc\xb5:\xbd7\x98q\xc1\x18\x97\x05\x95\xe2\xb96\xe2\x98\xfbF\x8f\xd0\x7fX\xaa\xab)\xec\xcf~l\xb4R\nX\xceB\xc9+\x1d\x8aK\x91\xcb\x8a=\xaad\xce\x0c\x1e\xee\x1ej+\x0c\xfb\x1a\x13&m\xa9B\xa9K\xc5\x1b\xb6v\xa3\xa0\xda6C4\x11\x01=\xd4\xfc\x12\xe9\x8c\xc1>\xa51\xb4\xa4\xd8\x80K\xb1V\x078\x0bvN\xb4\x9ex\xd0\x10f\x0d\\\x87\x9dh\x0e\xb5\xe8\xeb\x1bU\x1fcpZ\xf17\xad\xe7\xbd\xbb\x1dy\x14o}\xb6\xb1mr\xc93UI\x9e\x91J\xf2\xf4U\x92\xe7F%y\x16*\xc9S]\xad \xeb\xc5qRy\xd4\xcd\xea0\x9c\xe9\xfe\xe7\"\x80\xde\x9d\xd3\xff]?\x19TR\x14\xa1/\xf4)e\xd0\xf4\x03\xc8\xa0;\xe6\xf8\x87\xeb\"\x83\xdaH\x89\xc9@i5\xddAZ5\xcb\x8a\xfe0Yqc+\xda\x16\x18D\xdb\x0d\x15\xd1{\x03\xb0d\xc4{\xe8\x9f\\E\xa4\x18J\x07\xa0\x06S\x9f\x0d$n\xc4yP\x81\xce\xc2K\x8d\x83/\xd2|\xedk\x95\xb6\xc0\xb7#\x7f\xe1|m\x94\xaa\xb654F\xaa\x1a\xc0\xd7\xd2 \x15\x9f\xfec\xc8\xa7\xb1\x1c\x1c|\x03\\\xa8d\xe1vKR\xd6\x0bG\xf7\xb6\xfeE\x94,\xafL\xf2\xc6\xa9\x19C%\x81\xf3\x95\xb8\x02\x11\x9cw\xf1\xa7\xb4\xdc\xb9\x97\x17\xde\xca/\xcc-\xe9\xe7\xeb\x14\x8fe\x18\x83i.)Y<_\xc7\xe8\xfa\xb7\xfa\x0f\xd9\x13vS\x07;m\x0c\xe3\x84\x83\x81\xf1h\xae\xbd\xf3?\xff\x8f\xfe\xcf\xc1\x14\xe2\xce\x0c\x9c1\x1c\x97y\x94,\xddT\xe7M\xdaL\x94T!\xe8Vw\xe6\x9e\x99&\x83K\xaa[\x03\xa7\xdf\xf2II4=\xbc\x9c\xc2\xcb\\\xfa\xeb:(\xbc\xc6Pz\xe2}I <}\x86\xa7k\x91\xe0I\x14Qj\x8d\xc3&\xd3\x13?\x1e\xfa\xd8\x92T\x8f\x7f\xf6%*\xd9\xb4z\x8c\x87\xc0\x15ef\xe2{\xb2\x97\x0d\xc9*\x05S\xd9\xd9yI3W\x92\x1c\xf9\xa2k\x80|}<\x8be:\xd5\x94?\xe8\xe9T#\xfe\xa0\xa7S\xf5\xf9\x83\x9eNu\xc3\x1f\xf4t\xaa\x05\x7f\xd0B\xf2X\x8d\xe4\xf1\xc7G\xf2\xe0\x8a\xb2\x14\xa5*\x05f\xcf\xbbF\xa6\xc0\xcc\x87+0\x95Y\x8a6R\xc5edR\\~\xb2,Ei\xf2:\xbfH7%\xa6\xdfV\x03'\x1c\xf8\x91\x9f\x04$6\x00\xe7\xcc\xab%\xf1\xe71 \xb5\x01\xfe\x86\xba\xdd\xea\xb3\xb1U\xa8<\xbf\x98\xa4\x1buT\xb7\xb6R\xfb|S\x96\xf6Y\xd1\x9dy\x99\x00o\xef\xf4\x94\xfe\x11\xe0\x84\xd8\x147\x97\x1f\xcb\x94\x0fd\x93\x8aa]\x1f\xaa\x9f6\x1dT\xd4\xfc\x1b\x83\xf3:\xbf\x80\xa8\x84tS\x82\xccdfp\xdd\xd4\x17\xf7\xaeX#V\x12\xaak?i\xe1\xe7\x0c\x9e\xf0\x1d\xd0\xa8\x86\xd6\x01o`\xa8\x19\x9c\xe3\xe8\x0c\xf6jc!&\xc8\xa8\x0f\x95\xebYp\xfc\xcb\xa1\xf2\xe5P\xb9\xbe\x87\xca\xfc\"\xf3\x0bC\x91\x16\xe2E\xc5\xf1\x99\xbf\\\x92\xfc\xc0t\x94\xb0\\?\x1a\x12\x86P~\\\xa4\xc7\xab\xf4L{\xe2\x94\xba\xc3\xa0\x19XP\x8f\xd6\x0bVQ\x1c\xe6$A\xa1\x0e\xcb\xfc\x98?bG\xa6\xb7$/\xa24\x99d\xb9\xbf\\\xfb\xca\x13,\x1d\x7f\x88\xe6NO\xd7\xa4(\xfc%\x01\xc5\xfd\xc9\xc4_\xcf\xa3\xe5&\xdd\xa8\x0b~X\xcd\xa5\x12hu\xab\x0e\x0ey\x83\xb4\x18\xca\x14\x18\xc6\xe2\n@]\xea\x06\x13\xc7\xa8>\x94\x99\xdb\n\xd2\x90\xd4\xad\x15\x0c\xf5X\"V? \xa9\xa4a\xf9j\x9a\x91\xc4\xcf\"\xf6\xea\"\"qXP6 IK\x98\x13\xc8rR\x90\xa4\xc4\x8a\xd4+\x02\x85\xbf&\xc0\xf1\x1c\xd2\x1c^d$\xf9\xee\xe5\xd3\xc6\xb8\xeeY\x8e\xdc9\xdedY\x9a\x97$\x14\x0b*z\xe7\xe7d\xc0\xf8\xf8\xd4\xa0\xf0\xf57\xe7\xc0\xdbw\xfeV\xcdR\xb9J\x0b\x02\xe5\xca/a\xed\x97\xc1j\xc0g\xf9\xb4\xcd\xe0\x96\xb7\xef%l\xf6\xdcE\x9a\x039\xf7\xd7YL\xc6\xbb~k\x1f\xbf5\xf2\x1c\x11\xd3BI\xb0\xc5\x16\xd5\xee\xf3\x0f\xb0\xdf\xae\xdf\xf6^GE\x11%\xcb\xcfgs;\xafWt\x87\xa5\xdb($a\xe3u\x08SR`\xad\xdd\"#A\xb4\xb8\x00\x9f\x1eoQg'X\xef$\xbe#\xa3$\x8c\x02\xbf$\xd5\xd7$\x1b\xb9\xdd\x00|\xd9\x83\x97\x11\x10Z5I\xed\x85\x04q\xf2\xcb<\x0e\xc5\xa6\x96=c|\xca\xe7\xc7\xfd_c\xd5\xe5\xe0\xdc\xf4l\x97\x0c\xd48\xae\xfd8\xae0Q \x96\xe5\xf2\x9cm\x12\x9a\xd9u\xb7\x03\x07\x13\xb6\xe3\x7f\xafY\x92v\x8a\xa0\x8f \xc9\x9eE\xc9\xbb\xcf]\xbd\xdd\x18\x87\x0d\xb2pq]\xa9\xde\x96F/1\xe1\xa0$\xe7\xe50$\xf3\x8d\xb8\x93\xa4\xa8\xe1\x96\x88V\xb5N\x05\x1e\x1a<5\xa11\xd9^\x96\x93-I\xca\xc7\xacG\xae\x84\x92*\xf3\x9b\xae\xb0\xa2[\x89\x15\xddn\xb2\xf4N\x0c\xb4\x8b\xd9&=>\xdbT\xe9g\xa9n\x1f\xe3j\xf7\x1d\x89)\xb6\xb9\xb8+F\xacLk\x0b\xa1s=B\xe7\xed\x19\x94O\x86R\x8a\xe6k\x1b\xd9\xb0RJ UU\xc1\xf3u\x9c\x143pVe\x99\xcdn\xde<;;\xf3\xcenyi\xbe\xbcy\xb0\xbf\xbf\x7f\x13_\x93\xbf\xf4\xcf8J\xdeI\xdf\x9c>x\xf0\xe0&\x16 \x94\xbc\xabM\xf0\x93\xa5\x05rc3p\xfcy\x91\xc6\x1be\xf9{^\x05QQ\xbcF\x94?\xdc\xef\xa3\x7f\x17\x99\xd5\xd3J\x16\x85\xc5\xbc^\xac\xe7i,\x9d\xdamD\xce\xbeO\xcfg\xe0\xec\xc3>\x1c\xd0\xff\x93\x0c\x06\x0bNm\x928\x0d\xdeu\xd3\xd3\xe9z\x97\xb1<\xe0\x12\xa4\x9b\x81\xf3|z\xc7\xbb\x0f\xf7\x7f\x98\xde\xfe\xf9\x8ew\xf7\xd1\xf46\x1cx\xf7\xf6o\xc1\xf4\xc0\xbb{\xf7\x0eLa\xba\x0fS\xb8\xe7\xdd\xbau\x1b\xa6p\x97?\xbd\x0bw\xbc\xbb?\xdf]\x1dl'\xde\xfd\xfd\xe9\xa3\xfbp\xcb\xbbw\xe76\xdc\xf7\xee=\xb8\x07\xb7\xe8K\xb7\x82\xa9w\xb0\x7f\x8b\x0e\x07\xf0\xd9\x01\x1cx\xd3\x07\x0f~\xbe\xff\xc3\xed`\xe2\xdd\xb9s\x0b\xf6'S\xf0\xee\xde\xbe;\x99\xc2\x14\x1fM\xef\x05\xfb\xe0\xdd\xb9\xfd\xc0\xbb}p\x9f\xde\xbb\xf5\xc0{p\x87>\xbd\xb5\x7f/\xa60\xf7\xbc[\xf7\xef=\xba\xe3\xdd\xbdw\x00\xd3\xfb\xde\xfd\xbbS\xb8\xeb\xdd\xb9\x03\xd3\x07p\xcf\x9b\xc2\xf4\xc1\xea\x8ew?\xa0\x9f\x80}\x98\xd2\xcfL\xe8W`J\xbf3\xa9>swB\xbf\x13xw\x0enO\xbc\xe9\xdd{\xde\x83;\xb7&\xde\xbd;\xec\x07m\xee\xee\xcf\x0fh\x97\x1eM\xef\xc1}\xdaG\x98\xde\xf5n\xdd9\x80\xfb\xc0&\xec\xdf\x9d\xf9\x1f\x8d>\xf8\xca_\x9bu\xff\x93\xac\xe0\xf3\xe9\x01\xdc\xff\xe1\xfe\xcfw\x10l\x10\n\x7f\x82\xd5\x97\xe4\xb9\xb8\xc4\xe2\xdf\xf6n\xdd\xbe\x0f\xd3\xdb\xde\xfd\xdb\x0f\x82\x89w\xfb\xee\x03\xfa\xff\x93\xa9wp ~\xdd}p\x0f\xf6\x9fQ4\x98z\xf7\xa7\x0f\xe2\xc9\x81w\xf7\xce\x94\n`\x07\xdaW\xf0Q\xe3\x1f\x04\xa0\x98B\x1f\xc7\x07\xde\xbd;\xf7'\xb7\xbc\xe9\x9d \xfd\xf9\x00\x7f\x1e\x04\xb2\x97\xee\x8b\x97\xaa\xdb\x80\xb7\xc5\xcf\xaa\x83\xf7\xbd\xe9\xfd[1vor\xcb\xdb\xbf5\x0dto\x80\xe8z\xf5\x9ca\x1a\xed\x1d\xf6\x89b\xc2\xf4\x0e]k\xf1;P\xbe\xf2)0AY,\xf7\x12\xf8p\xcb;\xb8\x03\xd3\xfdgw\xbd\xe9\xfe\x038\xf0\xee\xdc\x0f&\xde\xc1\xdd\xfb\x13\xef\xe0\x1e\xffqo\x1f\x17\xf7\xc1\xbd\x07\xe2\x81wo\x7f\x8a\xff}p\xf7\x01\xec\xc7\xf7\xbc\xfb\xb7\xe0\x9e\xf7`\xff~@!\xbc\x83{S\xfc\xef\xbd}:[\xf4\xc5x\xd2\x80\x99\x08 \xfa\xe9)\xb6\x83\xdf\x11\xed\xd2\x15\xec4\xfcL\xf4\xf3\xd3\xce\xfa\xa4\x1fyy\x89\xa9\xbf\xe7\xdd\x9e\xde\x07\x9c\xf8\xc0;\xb8w0\x11\x93\xc6~<\xb8\xf7\x00\xf6\x0b\x9c\xcc{\xfbS\x9c\xc8\xbb8\x91\x0f\xf6\xef\x03\x9d\xce\x00\x97@\xcc\x14\xfb\x81/q\xa0I\x05\xd4XQ\xfc\x14N8[\x81~\x93\xb8\xf3\xe9t\xc7\xd8\xc1\xc9=oz{\xfa\x81\xe6\xfd6\x1c\xdcV\xcd;/\xcbqe\xd3\xfd\x00\xeemo\xffp\xc7\xbb\x7f+\xbe\xe5!)\xba\xf3\xe0\xd9}\xb8\x1bO\xee\x02\xfb\xdf\xd4\xbb=\x9d\xd0\x7f\x9eQ(\x98\xde\xfa\xe1`\xfa\xf3\xbdO0t\x16\xf1~e#\xdf\x87\xe9\xfd\xd5\xed\xed\xe4`5\xb9\xbd=\xf8\xf7\xf3[pw{\xb0\x9a\xde\xff\xf9\xee\x0f\xb7\xfe\xbd\xbe\x05\xf7V\xd3\x83\xed\xe4\xe0\x87\xbb\xdb\xff\x8f\xbdw[r\xe4F\x16\x04\xdf\xfb+\x90l\x9d*\xb2x\xc9d\xd6E\x123\xb3\xb2\xd5j\xe9\xb4\xd6T\xdd2\xa9\xfa\xcc\xce\x90\xacj0\x08\x92\xa1\x8c\x9b\x10\x08ff 5\xd6\x0fk\xfb\x03\xbb\x0f;f\xbb/\xfb0k\xf3\xb2f\xfb\x0b\xf3)\xfd%kp\x07\x107D0\x98U\xea\xd3\xe7LS\xb2\xca\x08\x04.\x0e\xc0\xe1\xeep8\xdc\xcf\xeb\x9d\x1d|\x1c\xc5\x84Q\x18D\xfd\xf3O\x07\x13\x9a\xa6\xfe6\xaa\x9f+G\xfd\xe9\xd9Y\xd5\xa6\xd47\x1f\x9e9\xce\x95\xd5\x87\xe9s\xc7\xb9\xb2\xfa\xf0\xb4\xbaCK\xf1\xc3\xf3j\x13\x81\xf3F\xa5\xdd\x9b\xa9\xba\x9e}\xee0u\xdddA\x80\x9f\x9f\xbb\x82\xedxq\x18\xc6QH\xf9\x8d\xce4\xad\x1c\xc5\xba\xd4$\x9ekP\xd5\x0f\xce\x10R\xee\x91+\xf5\x19\xdeX\x04\xd1\xbb\xf5[\x0c\xd7\x95\xd0}\x8b~\xd6_D|\xc3\xe0\xc3|\xa9S\xfc(\xf0#\xf6*^3rEN\xa6\xa5T<\x0d\x85G\x9d\xbeR\"(\x1e\xba\xaa'\x9d\x8aJv\x86\xa7\xa7\xe6\xc5\xb4x\x9f\xc4[N\x93\x9d\xfe\\x/\xa0S\xbd\xf7\x1b\xe7-\xa9^\n\xe6y=rrE\xc4}\xc2\xe2\x0d\xea\x8c\xfa\xa0\xb1\x19\xc1\xc1qOOWoP\xedL\xc4nIV\xe9\x89J\xa3:\xcd\x8b\xb9\xc9\xe6\xd7\xbb\xa6\x92c\x93\x9c\x056-\xad\x8d\xba\xbd\x1e\xef\xc1\xd5\xc9\x8c\xb3~0gK\x03O\xcaD\x1f\xae\x1e\xfe\xfc\xbe\xba\xa4`\x08r\xf3\x11\x95\xb5UY\xc5\xfb\xc5\xa6G\x84\x15*\x1c\x95j\xb2\xa0tR~\xa9Z\xcb\xfa+\xb80\xc9\x06D\xecx|\x0b\xfd\xfe\x8a\xf3\x98\xf7{\xff\x81\xc7\xd1\x96\xfc\x993\x85\xdet\x15\xb0?\xe3\xa1\xa4\x18\x11o\xc7\xbc\x1b\xb8\x9c\x7f\xea\xa1\x13\x8e\xea\xbd0\x8b\x9f\x18\xabF\x8d\x8cM\x1a\x8c\x88\x02[\xab\xe7!\x87V\xe4\xdc\xb0\xfb\xb4_\xfc6\x98lb\xfe\x15\xf5v\xb9-{m\xd5`sy\x99y\xb4\x84i\xc4\xa6\xcd\x1b\xd7Z\xbf\xbe3+\xc4\xd2\xaa\x10\xc6\xa6\x01W\xd4\xef\x8a\xb4\xde\xf93\x8a\xb8\x82\xc1\x87zj\xaa1\xa1\xfcp\x9dj\x06#\x8d\x99\x9e\xae\x18\xf29\xd5\x91\x16\xedU3\x1eK\xd3~4\x18\x91H\xd3\x89&@\xf4\xa1Z\xb7\xde\x01:!\xb6W\xd6\x94~@\x14\x86\xcea=\xe5\xf5\xa4RZG\xe4\x1b\xb3\xbc?\xe2\xb8D\x15\xbax6\xfa\xa0\xa1\xea\x06\xe2\x03\x06\x0c+\xee2l\xe0\xf7+\xe6B\xd1\xa7M\xe1u\x92 ?H\x0dC\xfe\x15\xf9(|\xbd\x81\xa1?u\x1e\x07\xf85%\xa6%\xb1)D\xfeE!\x01\x9c\x8e\xc4\xa6\x97[&~\xcb\x19U\x14<\xb6/\x0ebZ\xec\xb6\xaf$\xa7nS\xe3\xe0\xba\x9b\x98\x93\xbe\xe9e\x0e\xe1Hk\xfc\x03\x16m\xc5n\x04B\xca\xd9\x08D\x92^\xef\x82\xc4\xe3\xf1\xc5\x80P2\xbc\"|\xce\xe6\xfeR1@\xb6T\x8d\xf8\xc3!\xb6\x84]r#\"-\xcea\x1d\xfa\x8f\x0b\xf7x\x9a\x03>\x1c\xfa\xe4\x92\xc4\x17\x03\xd2\xc3\xa5\x80\x8e\xf3m\x17\xc85\xf6\xaa\x80\xa0\x06\x19U\x16s\x0ej`\x9a5\x8c\xc1Q#\xf0\x91\xb0s\xb2\xa3\xa9\x0bC\xd5\xa7,b\xa9G\x13\xf6j\xed\x92=U\x0e\xce\x92\x80z\xec\xabH\xf8\xc2g\xa9K\x12U\xd9\xb0\x9a\xdf\x8b0\xa8\x8b\xa4?\x17\xb4\xfa\x19J\"?e\xb1`o!\xa6\xd5a\xed~\xef2/\xf3rQ\xd8\x88\xbe\x1f\x95\xeb\x03\x95QG\xb2\xd3\xbb<-\xd4\xda#C\x92b\xf6r\xed\x1eR\xc4.5\xb2\xb9Xj9\xeb\x9a\xf4.\x13\xce^^\xaa\xe2P9\xed\xc3g-\x17\xc0u\xe6\xcbS\xf8zy\xaar\x16\x00 3\xd2\xebR\xb02\x0e\x1b\x16y\xae\x85=R2`\xe0\xe2\x0f\xdeH\x91F\x08\x1d;\x17\x8ekjkX\x1b\x8e\xc305\xeb\x93\x80F\xdb\xef8\xdb\xf8wu\xc9)Q\xe4\x9a\x86\xa9K(Q\xdf\xc1\xc9\x0c\xf8\x9f\xd1\x19'i\x12\xf8\xa2\x7f\xbaH\x87\xa7\xdb\xc1@\x87\xf2\x86H\xde\xbc\x1f\xe0\x12\xc6\x1e\xbe\xf5\xb2T\xc4\xe1\x88x\xf3\xb3\xe5\xc0\xfa\xb1p\xe5\x99\xab,\xcb\xca8\xd4\xed\x17U7\x1f\xe3\xd1\xe3U\xef1\x19\x92\x1d\x0c\xbb\xdf\x8f\xfb\x9b\xc1@\x8d\xf8\xe3\xde\xe3R)\xa7)ia\xc6\xd5\xbc\xad\xd5L\xc1\x0c\xf6\xa3\xc9\xce\xdf\xee\x02\x88p\xf4\xe8\x11)\xbcj\xc3\xd5B\xca\x88\xcc\x133\xd90\xeb\x1e\x15}o\x80n)\xfa\xf6\xd3\xa0\x15\x83\x1c\x88\xa1\x87DK\xeb\xd9d\xc7\xe8\xda\x8f\xb6\xb5%\xd8\xbabv\xaa\x0d@\xc7\xdd\xb7l\xcf\x02\xecb\xb95S\xf1\x91k\xd1Yum\xad\xef\xbap\x00c\xda\x1bM\xeev\"\x0c\xfe\x98\xc1\xb1\xed\xe5\x8e\x93\xd3\x97=X\\;\xfe\x12<\n8\x87k\x95\x05\x01\x13o\x03?\x15\xdd T\x168\x08S\xa1\xa2#G#\x0b\x9a\xa7\x13\xea\xf3\x05\x0b\xbbC\x17\xf8\xd5Y\xca+\xa9A\xd6\x0cU\xe0\xd7;\x19s%\xaa\xad\xdd\xc3\xd5&\x98\xaa\xb9v2\xc0\xdee\x1c\xe8e\x03\x95\x93\x97dJ\xae\xc9c\x92\n\xca\x05\xaeP\xf3 \x96&FTu#L \xbc#'!n\x99\x04E\xb5`[\xdf\xa9\xcfE\x06!\x80\x0c\\\x93\x1e\xa2bR\x9d\x99\xbc\xe6N\xe0\x9a\xe1<\xe9\x17jW;\xe659\x07\xe1\xf1%\x05\x1b\x10\x03\x07R*\xce6\x06\x06\x0c\xf3\x15\xbb(\"\x8c\xc1\x11\xcb\x8cV+\xf0C\xba\xed\"\xb2\x9b\x01|LR\xee\x95 M\xb9\xa7\x01\xad\x8fS\xf6\xd0!oX\xbd~\xb85Q\xcf\xfa\x8f \x0d\xf4hc-4P\xf3\x80\xcc\xd5$\xa0]1.\xe1\xc7\xbd\xc7\xeaO\x86\xeb\xbfH\xbf\xc9i\xaf\xb0\xd0+#\x04\x11D\xbb\xd3C\xc8^'\x16X\xcb\x113\xd5T\x8f\xe2\x81G@\xa3\xb27\xd5r\x0c4\x0d\xf5\xac\xe2\xf5\xfd\x11\xd0\xa8\xecM\xb5\x1c\x03MC=\xfc\x08Pxm\x9e\xf9Q p\xd7\xa8v\xa2\xd8\x1d\xb8\x94\xd8i.E\x03\x7f\x1bi\x0eu\xaf\xd6\x8d`wb\x0c\xa93\xa43\x98\xa3\xca\xac\xea\x90\x1d\xd3\xb7]\xad|\x1d\xe5\x1e\xda\xb3\xf5G\xee\xd9qh\xbc\xae\x96O\x05\x8f\x1d\xa2jc\x15\x98\xbf\xa1\x96# q\xd7s\x8c\xe0\xc5BG\xe9# \xa8\x97_\xb3\xa0{\xf3k\x16\xb8\xca\x1f\x01\x80\xa3\x06?J\xbbC\xe0G\xa9\xab\xfc\x11\x108j\x08)\xaf\x0b\x15\x8d5\xa8\xdc\xce\x1a\x8e\x00\xc2UG\x9a\xad\x0e\xad\xb5\x1c#\xb3U\xf3f\x1e>V\xebN\x8e\xa8;i\xab\xbb&`\xee(_\xaf\xb4.\xf1\x90D\xa1\x1b\xa9\xec\xa4Vj'\xb5\x88P\x12\\9\x88l\x1ao\xc4\xd1M@\x81\x94\\whM=\xd6);\xbb\x13\x1d\x07\xad2T\x95\xf1\x11a`N\xcb\xbaTV\xac\xaa^\x93\xa0\xdb\x0f\xae\x87\xaeVu\xae\xd9R\xd3\xe3KU\xe2\xa0\x14\xf7\xf2\xb1\xa3\x99#\x16\x85\xca_SB\xc5\xb1\x88b\xc1\xder\xb69\x04\xad\xe1D\x7f\xc8\xc2\x15\xe3\x08\x9f\xbf&C2\x1dLD\xac\x1d\x938N\x97\x95\x88\xdb\xdbD\x9cm\xc0\x10\xdb\xc9\xc4P\xea\xcdV\xdf\xac\xc9Kr\x06G\xa6\x9c\x0c\xafHof\xf5\x0c\xf0u0\"\x8f\xd5\n2\xea\x1f\x03\xffX\xd5\xfe\xd2\n\xfd\xbf\xdeD\x8fuL\xdf\xc7=\xe2\xaf\xaf\xac\xc4\xff\xb8\xf7rn>\xf5\x96Jxw.:;.\x80Y]wD\xba3eI\xf8\xf1\xe5\x8eW\xc1M\xc7)Kz\xb0N\x14\x1fn\xce\xa22\xc0\xec_\xa6\x0c\x9a\xaeeSY.\xe3\xa0^\\m\xa1\xa1|k\xcf\x8e\xc0\x9f8PM\x9dj@\xeaT\xc4\xd6|\x14\xea\x07>\xcc\x0fNX;j\xe1l\xd6\xa6\xde\x17,\xac-\x0e\x0b\xcc\x11\x1dt\xe9Kl=4\xf2v\xf1\xc1CE\xb3Fr|o\xefR\xd7\xc5\x105-\x06\x92\xe3|\x01\xe3\xabC\xb4\xa2\xde\x0d\xac\x90\xbf\xfe\xaf\xffM\xe1|e\xb0\xd6\xc7\xc8(\x0e\xcd\xd9\xfa\x08\xcd\xdbZ\xd4D\x9c#\xf6^\xeb\x9a\xb0\xb9>N>rC\x7fL\x0d\xc2Q\xc3Q\x02\xf3\xba\xb2\xe9+\x1f\x03\xa5\xe4\x8ad\xc5\xf3\xc3.\xcb\xa8_\xe4\xa4\x84\xf5]\xc4\xa9\x90}8\x8c\xc8\xcb+\"\xf4\xe9\x1a\x19\x93s\xc5\xc7\x15\x9b.+\xcaP\x13\x05\xd6\x07F\x0b\x85/FmU\xd2X\x89\xb9B\xbf\x82\xc6\xea\xac\x9c\xac\x99\xa5iU\x15\xafh\xcf\x8a\xf5\x9c\x97\xda\xd4 Z\xab\x85=Tip\xc5\xb9\xd4\xcf\xf78P\x03ri\x8f\x0f\xa1\xa9\x8a\n\xd5*\xd9\xecya\xaf.\xa7\xe4SS<\xa8\xcd \xf5\x03\x0f\xfa\xea\xc6]1\xb9\"\xf3\xda\x94\xcd{@\xa8{\xe8\xdb\xff\xec\xf9\xc0q\xf03\xef)\xden\xb2\xbcpg\xe1l\xc38\x8b<\x08\x13\x0f\x19?ug\xd4S\xaa3}\xe6\xced\xe9\xa2\xa0~`\xf2~\xde\x0c\xdc\xb9\xce3=k\x82\x0e\x8e-C\x16 \x03\xdft\xea\xce\x9a\x86\x94\x0b8\x06\xb49\xcf\xdd9\x03?\xba\xf17\xf7&\xd7\xd3\xc1\xb2\x94iy\xc4q\xbf\xc3z\xaahd\xc5\xcb\x84\xdc\x1ej+\x92pvA\x18\xb9$\xb1F\xc6\x0b\xc2\x86\xc3A\xa1\n\x8c$\x12\xcf\xd9r~\xb6\x1c\x11x\x98.]\xa6W\xc5\x03vm\xe5Q\"\x10.n\x84Gi.\xf8\x04\x9a\x02D\xe66X\x01\xa2-\x13\xdfg\x01K\xfb\xbd\xde``\xe1\x16\xe4\x92D\x17D(\xf0\xf9\\,\xfb\xac\xd1\x84\xe3\x03n\xc3\x95,A\x1a\xbb\xc6\x8a\x160\xd7\x84i;\x17\x1c\xcb:\xe1SC6\xb3\xd4\xcae\x01\xa9\x830\xb1I\xca=s\x88\xde?]D\xa7[\xbc\xf6:\x11\xdc\x0f]\xe2m\xc0\xf6,p\xde\xdeRm\xa532?\x1b\x91\xa9\x03?\xf3\xbb\xd8\xf32^\x82CWm\xc2h\x0c\x8f\x14X\xa3\xa2\xbd$\x9b\xb0h?\xb2\x1d\xff\xd8\xc6\xafO\xab\xb6\xaa\xdaJ\xe6y\x93\x91\x0c3\xa7\xb6\xbe\x0b\x0b)\x9c\xe6\xa6#\x12\x8c\xe0\x18\xbb~\x04\xfd\xec\x9c\x9c(\x82<\xf1v\x94\x7f\x19\xaf\xd9\x17\xa2\x7f\x96\x9f\x17\x8f\xa7\xf5\"\x9fO\xebE\xa6\xedE\xb4G}f\x1d\xe4\xf7\x96\xb3^{\x11j\x96x\xa1\x8b#2_\x0eF\xa4\x9f\xc1\xd5b:\"S\xe07gDJ\xf2\xfc\xb3:T\x19\xc8}\x8d\xcd\xc0r\x0c\xc8\x15\xa1\x93$N_\xd1\xbb\x11\x8a\x01\x8a\xc1]\x90\x94\\\x92@\xb1\xb0\xe9\x19\xd4L\x01E\x0b\xb5\xa7\x83\x0b\x92\x0e\x87naR\x873\x0c|\x8f\xf5\xcfG$\x1b\x8c4[\x86C}\xf3\x05\x9a\x1a\x91\xd4\xa0\xb9Y\xf4\xe4\x9a\x8c\xa7dF\xfa>l7\xd9\xde\xa7H\x07\xa5\xac\xa7)\xda8\x18\xe9;\xd8\xd0F%\xc7\x1c%Xo 2m\xe3\xc7+\xb2\x19(X\x1c\x14\xb0\x1bq(\xd0=\xf0'\x82Q=p\xa1\xb8\xccF\x0b\xb4\xa4~\xc9\xd8\xd2\xca)\xd2J\x9aKM\xd3\x12M\xac\x954\x0d8\x85*Z=\xde+\x89R\xd4\xca%\x8dR\x92\xaa\xc0J[.a\xcf\xfc\xa0\x03jY\xd3\x82\xc6\xe2\x82\xf0\x82pt\xd2\xef\xab\xf5\xed\xf7\xf9\xa8`R]\xa56\x88\xe3\x83\x8b\x01\x10 \xaeQ'68S\xb7\xd40\xbfb\xc3\xaa\xe4(o\\\xe1Q>\x14 \xde\xa1=c\xde=\x9bx\xc8[\xef/N\xf9\\6W\xcf\xa6U{B\xaa\xd3\xab\x86\xf8h\xed\xff\xec\xfc\xccIA\xd3\x9c\xbc\xd4\xccp\x14t\x9apB\xe4\x80\xf5\x88\xecFd?\"\xe1\x88l\xbb\xd1\xc5\x03\xa4\xf4\x01t1\xa8\xd3\xc5\xd4\xd0E\x0f\xe8b0\"g\xedt\xd1\xeb@\x17\x13rE\x02K\x17\x15\xd1\xf2\x90.n\xc8%\xc6p\xe8?=G\x8a\xb6\x86\xac\x15\xea\xb8Ac\x9c)R\xa4\xf5\xe0\x82lj\xb4\x12\xc8\x80\xaf\x00\xde\x1c\x80f\x0fM(\xc1R\xc7m\x1ca\xfc)\x03\xa4\x82px\xa5(\xc3G\x04\x0fZ\xb6\xf5\xed`\x1c7\xea\x91\"\xc8\xe4\x9a\xf4\xc3:`\x16(%O@\x86^\x0fSw\x83\x02|\x1a<\x07d\x17\x03\x05\x8c\x93\xad\xd8\xd2\x9a)9J[\xde\xb1U\xbc\xacoX\xcdtD\xbcA\x99M\xa4\x93|s2\xdf\"w\xa8\xa6\xb9.\xbe\xe8\xb8\x9c\xa1\xc3\xe4\x0d\xfc?\xecK\xe9\x8a7m>\x1eS\xf1[\x99\n\x10\xccB\x17\xb4\xc7\x8eR\x92\xb6\xa1>\x92\xff\xf8\xc7\xf3\x9f\"g\xf1\x1b8K\xce\x99\xfc\x1agr\xf2\x1f\xffh\xfe\xe3\x1f\xe2?\xe9/\xc4\x7f\xfcv\xfe\xe3\xbb\xf8\x8f\xff7\xe5?\x0fA\xc1F\xfc\x83\x01\x8fpw\x07n>\xec\x0e.\"\x97\x84_\x90H\xed\xe0JX\x01\x08\x16\xcf\xa3\xe5\xc0\xce\xba\x99\x07\xbd\x03\x11f\x00]\xbb\x10\x91{\x8b\xfb\xd7\x1a\x0d\x90\xcaK\xdb\x0c\x18\x80\xfar\xc2{d\xb5\xf4\xa4b\xf8LJ\x0b\xd9\xaa\xd5\x816\xb1\xfc\xa2\x9a\xddx\xd6B}\xb5\xe8\xdfz\xc5c\x17\xa4\x06\x85\xf5\xc7\x8cB\n$t\x85\x8b\xe6F\x1cF2\x0f\xe8\x8a\x05#r2\x053\x1cGUE\xfdV\xb9\xae\xe9\x88$Z\xce\x0e\x14IMM5}`'z\xfb\xcc\x06#r\xb2\xa9^$\xd2\x93\x9d\x0f\x05\x18%\x0e\\\xdd\x04\x04\xa4\x96\xe4\x95K\x8c\x0en\xd6I\xbeaw\x9c\xc348Q\xd1\xdbpo8\xac}\x06/Q\xb9\xb2\x83:\x15\x1an0\xa0']\xe0%\x0e\x98[\xa0%\xfa\nmK\x90\xc3\x96\x0e\x11\xdd)\xdc% *^\x93>lG\xe7\xcbAG8+\xb4\xbf\x19\x12\x81\x0eh\xda\x82\xcdv\x006\xeb\x08V\xa3\x8e\xc6\xfc\xac\xae\xc6eEh~\x06\xa0\x96j\xac\xfa\xa50\x8c\x1f\x0c}\x95U~\x8cQ\x1d\x8f\xbd\x06\xb8\xe0\xe2\x8a\x82\x1eh\x02\xd0&\x886\xab\xd7x\xfei9\xc8\x97]\x91ji\x83\xf5l\x80\xf2\x8c\x9b\xd3\x9b\xdcs[,\x97@\xac\xf6<_$q\xd2\xcf\x03\xbe\xc4\xf9\xbe3\x8b\x04\x9cg]\x17\x13fJ\xac\xe1\xa8%\xe5p\xa3\x87p\xb5\x1c\x1f\xba\xe6\xf0\x98\xee\xe1\xab\x0e\x0e\xd6Z\xc3|\x1b\xccj\x98\x12\xb7\x14\xe2#G-\xf6\xc9\x1ft\xa3\x84\xc4\xd1\xcbC\xb8u\x10q\xea4\xb2\x96\xd2\x0567\x95n\x83\xae\x05\xb2\nT\x1f$W\xd9d\xbb\xbf\xe6\xcd^\xfdruo\x7f>\xee\x0f\x16\xf3\xc5\xf2\xe7\xf7\xc3\xeb'\x93O\x16o\xe4h\xf6\xeb\xcb\x93\xc5b9\x00E\xf0b\xf1\xc9\xb4\xf71\xf6\x10\x0ey\xa5\xb8\xbb\xef\xb0\xb7()\xcf\x1a\xb6\x0dy\xce\xef\xd9\xf6\xab\xbb\x04\xc4]\xb8&\xd4\x7f#\xe7=\x08\xd2\xb8\x88\xfa\x83\xf9\xf2\xf1\xa27\x19\x9d\\\x8f{\xfafO\xaf\x87\xc1\xb7\xb8\xb9\xdb\x83\xa6\x82\xcbA_\x95*_t\xaeC\xd31n\x97\x9d\x804[\xa5\x82\xf7\xa7\x0e\xbc\x1cL\xd2\x98w\x0cN\xaa\xeb+\x9ck\x9a\x13@W\xbd\xa5\xeeI\xec\xdf\xa0\xff\xc9\x03\xc7\xa5g\xe4\xa3\xc2h\xa3\x82\x04_\xfa\xeb\x11\xe9m{j\xe7\xbb\xb1\x92Q\x9e\x17E\x933$\x98\xbb\x92\xc0\x1e\xa3\xc0\xee\xa6+\xd5\xed\xdd\xce\x9c\xd5\xba\xf3\x93\xe2\x86\xb2\xafH>\x14\xb0\xd2{eo\xf9\x12\xe8\xb2\x18\x8f\x9bk#\x06\n\xc1\xee\x84\xdeLP\xbd\xd9\x1b\x1c\xdc\x1b\x9a\x9f\xd5\x80\x9f\x8d@OF\xf3\xdd\xc6f\x12\xd0T|\x13\xad\xd9\x1d~\xf7\xb4\x0c\xb7g\x81\x11\x8d/@|\xdfL\xd8\x1d\xf3\xfa\x19\xe8-\n\xa5^\xa2\xfa\xfc \x95-\xfe4e\x83N5\xd3\xd9\xe2\xcf\x8a%\x99\xde\x98\x06#\x92\xa0>\x8d\x0cI2\x9f.\xf5\xe0v\x08EG\x0e\xf1\x99\xe2\xef=\xb8q>\xbeo\xd6L\xadc\x07\xb5\xb6\xc5\xb1\xde\xb5\xb8\x91\xcc\xcf\x97\x1d\xa2\xe7\x91\xc3\xf2b\xf1\xf7\xd0\xee=d\xeaT\x0f\xba\x15\xf9\xdb\xcc\xce!>_\xfc\x1d\xe0\xf9\xc5\x9f\x82)\x80\x05\x93/\x921I\xe6O\x0d\x8a6\xabR\xcc/-ho\xfa\x01\xb9$Y!\xe1!\xfd}\xc8t\xd9\x95\xf6K,\xa9\x12aT\x04\x0d(\x8d\x91\x98}\xdd\xf4\xd9\x08\\\x1b\xa4#bR\x04\xea\xb4\xdb)\xe6\x07 7&\xd5\x1cZ\x9c.\x86c\xb9\x98,&rq\x8d\xff\xc9\x93\x93\x93\x139\x1a\xc9\xf1\xf8\xb4~\x98q\xba\xe8\xf7=)B\xc9e2X\x0cN\xb7~\xfd`\xa3>w\xde\x8c\xf4\xfe\xfb\x7fsL\x11W\x1f\xfe_\xc7\x87D}\xf8\x7f\x1c\x1fD8#\xbd\xbf\xfe/\xffw\xaf\xf4\xa5\xc1\xda\xa6\x8b4\x95\xcbQ.iIk\xab\x8a\xbe}\x1a\xe4\xa5\xd2\xde\xa8\xc8\nS\xcd\n\xd3&VXc\xc4v\xd3\x94v\xe7\xc7\x19)\x97;\xcc\x96I\x91\xed*,\xcd,\xdb\x85\x95 gQ9/U\xafx\xd0<\xc8Oz\xfa=<\xa3\xb9&\x01\x99\x91\xc0J\xc3\xf1\xa8\xdd\xf6\xac\xfa\xd3\xd2\x97?\x17\x13\x11\x7f\x1b\xdf2\xfe%MY\xbfbtS\xfc\xa9e\xc6'\x82\xa5\xa2O\x07\x16^Z0\xbf\x18\x8eA\xec\xfe\xef\xff_oPH\x9d\xfc|>z\x0f\x1f\xfe\xfa\x97\xffZ\xfc\xd2\x9f_\x9f,\x07\x7f\xfd\xcb\x7f\x85\x8f\x9fL'\x93\xfa\xd7\x9f\x9f\xe9\xb2\x9fL\xd5\x7f\xc5\x0c#[\xef\xa8T\xee\x8d\x9c\xbf\x19/\x07\xe3\xf1\xb8\xaf\x1e\xe4'\x83\xd3m\x085\xfc\xf5/\xff\xfb'\xe7\x95\xbc\x8bt0\x1e\xf7\x17i)\xdb\xffV\xcb6\x7f3^\xa4\xaa\xd2>>\xd5\xb3\x83\xff\x96\\mM?\x8an\xd5\x12\x8d\xf9\xe3\xde\xd2E\x1c }[\xa7\x08\xa7\xf3\xf1\"\xc5\xdd\xd1\xf2\xd4\xb5\xc3\xa2m\x16\x8a'}a\x0e\x02\x01\x7f\x8d`\x0e\xd3~\xe2#\x120\x85\xbc\x85N\xd6\xdb\xc8\x0e\x98^\xdb\xad\x04\xd0em\x10k\x13\x914WF\x91<\x80\xde\xf8\xceM\x9b=\x92\x1d\x91\xfb\x11Y\x8d\xc8\xdb\x11\xb9\xfd0\x82t\xab5\xbf\xab&\xc2\xb4\xd2\xc4`u.\xc5\x9a\xccFaK\xaer\x88a\xe8\xb60tx\xfct;\xdf\xea\x9c\xe4\xf2\x8al\x06\x17d;\x1e\xb7\x9c(\x99_a\x0c\xb6\n\xb9P\xae\xd2\x9b\x14\xd8_\xd9\x15<\xe8,[\xb1\x19v\xe1\x82(\xc1\xca\x03\xc2\x18\x97vAz\xe3\x13\xe3\x86\xc7\x1f\x0c.\xda\x87\xd9\xfc\xc0\xd7\x07\xb9\"'\xb4\xafPX\xefN\xc6d\xaa\x05\xc2\xd4\xeeW\xa6#rO\xaeH\xef1NL\n\xa6\x89\xa0:\xc0\xb2\x01\x1e[']\xe6\xc3\xfcT\xeb{U\xc3zDB\xf57\xe9\x06\xb5\xf9\xc1\xa0\xb4\xcdc_\xcd\x83\x9a\xcaQeJ\xc9f\xa0\xa7\xf4\xa8\x06\x89\x06z7I\xfdh\x1b0\x18\x8a{\xd5R\xa1r\x95\xb69f\x18\x8a\xbf\x1c\xe0{rM\xfao\xe7;\\j\xc5\xe3\xca\xcc\x91<\";\xb46\xc8\x89 Z\xc4\xce\xcf\x97\x15\xb6\x91\xf5\x0b\x02\x80\x9e`G\xb9\xa7K\xd0&\x7f\x0c\x10\xce\x1e\x08\xc2t\xa9X^qI\x1d^+\xae\x9fj\xca\x8f2V \xbe\xd1\xe5WW\x836\xfd\xf6\xe4\x9a\xdc\x1e\xb3\xcf1?\x18\xc5V\x1d\xb4\xeb\x97\xc4\xe9\xcc\x0e\xddQ%\x11ug\xc4\x11\x07\xbb\xed\xa7\xf7J\x9b\xce\x85\xc0j5T\x8b\x03VH\xff0\x02\xf4\xfe\xfa\x97\xff\xe2\x8a\xa0\xea\xfa\xbd',H\xd9G\xad\xfa\xa3\xee\xc1\xc0\xc0\xbc\xea\xf8\x15\xe4\xa9\xdb\xdb[\xf9\x1b\xb9\x98-N\x17\xa7N\xb9\xc9o\xd4L\x9f\xbe\xb9\\\x9c\xd2E\xfa\xe4\xe5\xa9\x91\x90\xda\xc5#Z3^7F\xe8s\x87^CX\x0b.7\x06\xab\xce&\xe82\xaa\xf9\x9c*\xe3\xc1\x8c\x9c4\xc4\xae`!\xf5[>\x8b[_\x08\xc6\x9b+\xd7\xf2\xf2\xd7Q!0g\xd3\xdd\x16\xf3Ko}\xe1\xed\x14\x92l\x99x}\x9f\xb0\xfeA\xa1\xc1\xa3)#\xbd\x8c\x07\xbd\xd9Add\xc7\xacy%\xb2\xccH4\x81\xc8dl\xfd\x9a\xddu\\\xf60\xaa\xd0\x83?\xf1\xc0\x11\xf9\xa6\xfak:w*\xfe\xe0\xc2n{6\x1c\x08\x98\xb5\xbf\xaf\xa1\xe8)\x90D\x0cjF\x18\x96\xafTB\xbf\xb0\xa3z\xa3s\x9c\xfa\xa3\x92[\x9b\xa6\x9f\xe3\x0c\xcc~j\xfcb63Sg\x8ez\xb9\xea\xb4\xe8\xf2\xf5\x11\x0b\xfc\xe8&\x9d\x11V\x1f\x12\x9a\x89X}U\xcb\xa4\x1c\x93\xda\x15L\xea\xd8\x8d\x0co:\x80*\xeee\n;\x80:|jg\x12eA\xab\xe2E\xdf\xc3i\xd8\xe3\x14,\x95\xee]\x96J\xce\xb1\xaemk\xee;\x1e|\x14\xb6+\xa0o\xb9\xffX\xe7\x1f\xb9\xdb\xa0\x1eXD\x822);\xea\x14\x04\xea\xd1\xb7\xd0\xb5\xdc\x9d\xabr\xb6 \x9f[Vw\xfa\xe6\x92\xce_.\xd2\xa5a\x0d\xdb\x01\x1a\x87\xea+\xa3\xbb\xf1xD\xfc~\x9a;\x18P\x89\xc3\xe1@\xc9\xc6\x90\x0bR\n\x9b\xaf\xbc\xad\x18k\xcc\xcbv\x01\x9e\xe8\x0e\xac\xe0\x90Q\xc9\xf9}\x85\x1b\x14.\x13(\xf4F\xa1\x7f5\xc91\xda\xee:l\xaf\xf6\xa5=e\x08\x05\xfb\x81\x82yo\x15\x06F\xbc;L\xf1\x88\x99tOo\xa3\xd7\xd0\x9a\xde\x11np\xc7\xba!\x97\xb6Y4\xbe\xcdM\xdf \xce%\x15\xec[\x05\xc6~\xbeYN2\x1e\xa0\xa6J\xdb%\x1b-\x1a|\xd4;T\xf5Y\xb5\xb4\x1e\x11\xef\x18\x12I\x1e\xa4\x0d'E\x8dx\x90\xab\xa5\x93\x8eJq\x92\x0b{\xebN\x05 \xb2\xc0C;f\x1d\x8c\x1d\xd1;m\xcc\xab\x87\xbf{9}`\xd5f&T\xfd\x99\x81\xe8p.E\xb4\x02\xf3\xa1#\xf1\xd0)\xb6\x98\xd6\xbd\xec\x91\xd3\xfb\xf0>\x15h\xe0\xd1\xd0\x8d\xc7\xdd\xe1\x0b\xd0\x92\x1eP=!\xc3|L\x0c\x91\xe8 \x0e\xa9_P8\xb4zh\x9f\x1f:\x8fG \xf2\xd1\xf3w_9\xbb\xcaJgWY\xf9\xec\xca\x1b\xd9\x834}vu\xb0\x9d\xf6m2\xee\xd5\x0eV\x82\xe7\x1e\xe3\xf1\x05pI\xadM9\xb9\xb2\x14\x9a\xe0\xadmC/\xe0Sf\xac\xd7/\x06\x8a-\xdb6:\xed\xe0\xf6:(\xe2\x88\xf89z\xc4\xfa\xe6+\x1a\xc0\xd9\xe2U\x8ew\xfa\xe4\xa4\xdc\xa1'\xe4\x0b\xcb\xc7&?\xa6\xd5\x8fg\x93\xe9\xf3\xc9\xd3Jj5\xd3\x97qr\xcf\xfd\xedN\xf4\xbd\x019?\x9b>'\xff\xcc\xd96\xe6\xf7\xe4\x7f\xa2^\xbcJ\xc9\xe5\x96\xb3\xedo\xd4?\xe3\x1f!e\xe2\xc5\xe1\xcbj5\xaf\xbeyM\xbe\xf5=\x16\xa5l=!\x85\x18\x86j\xdc\xd28\xe3\x1e\x83X\x86\x01\xe6IOC_\x8c\xf5\xcb$\xd9%\x07\xa0T\x15\xa6\xb3\xd3\xd3\xad/v\xd9JAp\xaa B\x80N\xdbF\xe1\xb4\xf4\x0e[\xd1Q\xd9\x80\xbd\xddF(\x9e\xfcI\xf8\x81q\xb0\xae\x9d\xe2W\xac\xc4\x9c\x02v\x9c_\x94v\x9fe\xc6Q*x\xe6\x89\x98\xcfH\\_\x88\x19\x0fR\xf7\xb6\xb5eG\x9b\xeff\x1d\x1f#v\xfb\x1f\xfch\x1d\xdf\xba?\x97\xb7\xda\xae\xcay\xa6\xd6.\x9b\xe9{3\xf5\x1c\xc5X\xac.'\xd0\"\x0c\xbe\xa3\x14\x9d\xf8\xe9\x97A\x9c\xa2\x13\x9ck\x18\x89WT\xec&!\xbd\xebGj\xaf2R\xd2\xfc\x0cvK#\xa2\x1d\nT\xfd\xd5\x17\x7f\xa0KC0\"\xe1\x8b{\x0b\xc51e\xf1\xeeV\xab.\x86\x98\xcb\x8bfz\xf5N\xf0\x07\xc1[\xdbP?\x0dJ\xd0\xb2OGX,\xcc\xce\x8cnV\xa5\xe9\x04\xb7F|\xb5\\\xef\xddX\x8d\xc0w\xc1mc\x8c\xa8\xb1\xfaU\xbe\xb6\nj\x0bf\x02w@\xa0,\xc8\xf3=\x94\xfb\x17\x1a\xe8\xa8\x03] s\x15\xef\x02#,=\xf74\x14\xc1\xb7j8bb\x19\x95\x93'\x1e\x0d\x02\x13%FS\xe9\xc1(\x8f\x86te\xa3! rM\x04\x99\x91\x13\xbco\n\xbe\\\xec\xe8\xa0V\x08\x8c\xc7\x05\xf1\xa3T\xd0\xc8S\x85\xe2\x89\" \xaf\xe9V\x15.\xfa\x83\x9a\xd9\xd1}m\x89R\x7f0Y\xa9\xa7>+\xfaY\xea2\x88%\xd23k\x16\x05\xcc\xcf\xa8V\x01\x86\x9c\xbc\xb6\x0e'\x83\xcd\xb1\xa3\x94 \xe0TH\x9a\xe4\xd0\x0cF\x8e\xb3\x0cw\x17^\x15i\xf8q}(\x90\xffc:Q(f{QH\x9b\x141\xbf\x99T \xcb\x85\n\xd5c3\xa9\xd5\x1c\x18r\xc2ssV\xcb\x91!\xb3~k\xce^b\xc2P\xa4\x90\xe2&.\x83#f\xe6u\x81q\x1e719\xcb=f^\xf2RvZ\xbe\x80\xdb\x11\x85\xc5\xd2<\x1f\x05\x81\x05j\xb3\xef-\xc3me\x14l_\xbf6\x17(\x88,H\x05\xcd\xfbQ\x83]Jy?\"1p\x99C\x9e\xb3H>n06}\x81j\xaa~U\xc0\x1c\x19t\xd6\xbe\x7f\xe2\xf2\xaa\xfd9\xcfPIS\xb2\xabS\xfa\xa4\xabTp\xea\x89WL\xec\xe2u\x07d\xc0\xa0f=S\xae\xd7\x05\xe1Ph\x9e\x1d\x1e\x04R\x94\xc3\"\xe2G*\x9b\x98\xech\xfa\xc7\xdb\xc8F\xa3\x8fP\x14a\xf3hI\xd0#X\x03\xfb6\xb8\xd8\x05Fv'X\xb4\xee\x08#\x80\x87\xf2\x1f\xcb\xc5\xfbf\xe4\xaan\xe7\xde7\xdc\xcc)m\x15\x1a\x16\x98\x91\x18AW]\x1b\x9b^a;\xd1\x1b\x00\x93*\xa4\x90\x0e\x13L@\xde)\x14\xd2\x81F\x90\x99R\xbe\xcd\xc01V\x83\x843(u\x01\xc2\x03\xb6\xce\x0d-\x81\x07q\x19\xe9$\xcd\x12\xc6a\x01\xe2\x0d\xe95\x0b\x98`\xe5\xae\x8c*;2\x8a\n\x84\xa8\xd3\\\x07\x81\x9f\xa4~:k\xdd\xa2\x17\x7f\xd6\xa4K\xebh^b\x90\x04\x98\x83(\x0b\x02%VD\xe4\x9a\xf4&\x93\x9e\x12~1\xbc\xa21\xf6Rl\x1f\xf4\xfcc\x12Y\xd5\xf1\x90D] \xb6V\xecvDN%\x0f\x7f\xc19\xbd/x\xe8\xd25\x0c\xf2\x8e\x18eq5r\x83\xf9\x15\x96\xa1\xdd\xeb\xb0\xceG\"\xc4\x9c\xbb\xc0\x1aU\xd2\x95m:j\xc5\x87q\xfd8\xcb1 p\xff\xe5\x8bh\xfd%MD\xc6\xd9\x11\x03s\"&\xdb ^\xd1\xc0\x11\x9e\xf1\xcfP\xed\xf7l\xcb\xee\xfeL\xc2,\x15dG\xf7\x8c\x88\x1d#\x8f\xb7\x8f\xc9&\xa0[\x92\xb2Z`F\xf3\xcbG\xac\xb23\xbc \xb8T\xc1@\x8a\x81\xcf\x00}\xb9\xb9\x80\x1f\xf1\x08\"\xe9\xad\xd9\xdd \xdf7Eh\xbf\x82\xe1(\x8c9\x94Jl\xb5\xdf\xb2\x1b\x8az#Pw}\x84\xeb\\\xc6H\xb9Wf\x99!}\xec\xe3m+W\xdc\xdc\xdb\x9d/X\x9aP\x8f\xc1\x08\xce\x08\x04dr\xec\x0f\x8a\xfa\x8e\xc3\xdb\x02\xb7\xde\xc5\x86+\x8d\x18W\xa0\x1a9#O\x90\xb2\x98\xf2\xfa\xd5\xb7\x9d\xf0\xcanw\xbb\x80V\xdc\x96\x08,\x86\xa1UE12\xa5\xf95\nb\x95\xe6\x8eiMJ\xd2\xeb\xc4\x81S&\xbe\x10\xe5\xbdb\x87\xbbkzC\xa3J\xa6\xfd\xc1\x9c-\xf30\xba]\x1a\xdd\xd6\x1b=\xba\xc5.\xed\xe8\xce\xa5]\x1a\xaa*xtK\xad\x0b\xa9\x82\x829\xfeu\x01n[\x07\xae\xcb PU\x06d\xe8\xc2\xebU)\x0c\xae\xf9\xb9G\xe4K\xc5>\xbb\x8cH\xb1U=\x92\xfd\x1e0\xdf^M\xc3I\x1a\xe4\xbb\xf5\xbass\xb9\x9a\x0d\xd5hf\"\xa0\x82\xfe`\x94\xc7^\xac\x10\x14\xd4\xaf\xe9\xb9\xd0\xdc\x0bo\x11D\xe0\xf8\x1d\xefDr\xb5\x13W\x94\x17\xef/\x98\xc4\x0b\x98\xf4l\x92\xee\xfc\x8d\xe8+\x12<&\xb8\xed\xf7QrP\xdc\x9c\"\xc1l\xe2\x88n\x1c\x9d\x189\x85\x16\x03\xcfu\xc5\x0e\xce\xc2x\xcf\xfe\xee\x07\x8f\x16oX\x95FR\x0de\xbbv\x13\\p\xe2 _\xc0\xa8\xc3\xb1\n\x8e\xb7j\xc1c\xfdtD\x1c\xd7m\xc9!\x8d\xd9G\x9d\x89m}\xc9tY1\xb5\xe6;\x93\xe4\x1dM;\xcf\xbb\x15\x8e\xd0\x9a\xa3GzdX\x9d|\xb8(\xdc+\xdc\xa5\x81LL'w\x81(e\xe2\x1b\xc3?\x8f\x80\xaa\xc6\x89\x8f\xe3\x80\xae&\x8fk\xb1\xf3\x90\x1b\x1d\\\x87\x96J:\x8f\xa2\x16\xbcE\xe5`\xb2\x83\xce\x0f\xb0\xe2\x07\xc1\x0f\xf0\x96y\xef\xb2\x87\xd1\x95 \xaa \xf5\xdcb`2\xd2{\xd9\xcb\xa3\xf8\xda\x91R+\xbdwy\x8a\x05{/{\xcb\xa3T\xc7%\xf0:\x0c\x05\x8a\xcd\x96\x0bYA\xbe\x1a\xc5\xcb\xfc\xaaC\xa7\xd7G\xfb\xc0\xcd\x97\x87\x84j\xe2G\x84\x0d\x08sk\x03\x84\x16\x98\xc9\x90<\xc6\x08\x0b\xb0\xf5\xc0\xa8`\xed\xf4<\xa7\x16\xf5\xd1+\xa5\xbcW\xa2xMou\x84\x88\xfcQD\xdf\xceS\xdc\xa5\x89\xa2\xd6\xc9\xc8\xfcm\xbe?\x8c\xb4\xda\xa3-f\x06\x14\xe5\x1d\x98\x7f<\x0d@\x14`\x85\xd3+T\xb5\xe3X\xfe\x9e\xb3M\x7f\xd0\x82 ~N\"\xa0R\xedoZ\xcf\x04\xbb\x13\xfdBm\xa8\xb7oROt\x19\xbd\x02\xcc\x1d\x05f\xb3On\x1e9bm\x87Dc\x1e\x07(\xe6g\xf9:\xc2\xf6e\x8a\xbcC\xed&\xdb\xe6\x95\x1b\x13u\xa3K1\x1b'\xabA\xd5\x190\xb6!\xb9\"\xbd\xb7\xab\x80F7\xbd\xae\xaa\x942<]P\xae$\x81[-k\xfb\x12\x85\x93\x9a\xa1\xa5\x8dC\xd2\x1b#s\x9bu\xa4\xfc5\x8c\xe9\x02\xa9Uek`\xd7\xf1k\xadF\xae*f\x89\xbb\xd5\xbc\xc0\x11\xcd\x19b\xa2uT\xf6X\xce\xa8\xb0\x15\xbb\xc3@\x1e\x93\xef\xfe\xf8\xc37\xaf\xbf\xf9\x97\xaf\xde~\xf3\x87\xaf\xbf\xf9\xc37\xaf\xffc7\n\xe6<\xd69\x82\x8c\xa9\xf2z\x8f\x0f\x1a\xfe\xd3\xfe\xf5\xac7\x7f\xd3[>\xb9\xee\xc9\xc7\xf37\x8f\x97O\xae\x1f\xcb\xf9\x9b\xc7\xbd\xab\xcb\x97\x7f^\xa4\xcb\xe1\xe0\x14\x19\xdc\xe9\xfc\xcd\"]\x9c\xf5\x1e\xbf\\\x9c^-\xee\xce\xa6\xe3\xc5\xdd\xf4\xeb\xc5\xdd\xa7_/\x87\xa7\x134\x0fQ\xb3\xdb\xbf\x9e-\x16\xe9\x93+\xf5O\x0foM\xdao\x83\xeb\xde\xa8\xe8\xcbd\xaer+Vy\xd9?\xf9\xdd\x1f\xbf|\xfd\x1f\xbf\xfbj\xa0^u\xeab\x91\x0e\xf3W1\"= \xeeQ\n\x15\xaa\xcf\x83'\x86\xdb\xe2\xbb,Tq\xd9?\x85F{\xe0o\xe6t~6\xfe\x9c\x8e\xdf}1\xfeO\xcb\xfcq\xb6|rZ\xad\xb3\x0c\x81\xb0\xad\xa8^\x9d^\x17\xda\xcb\xf9\xf7\x88\xf4\xb6~\xcfE\x0b\xd5\xa0\x7f\xb9\xa3\x9cz\x82q\x13Q\xddhZ\xfa\x8f\xa2U\x9a\\\xc8G\xbf\x9e\xbe8\xbb\x90\x8f\x02\xa1\x9e\xe1q\x8b\x8f\xe7\x17\xf2\xd1OY\x0c/O\x9f\xc1\xbf\x9f_\xd4\xaf\xdb\xab\x1f\x989tA\xd8\xd2n\xa4\xb0\xf7\xb0\xf8Q\xb2\x8c\x98//PUzb|]\x82\xf2g\xfe\xf4@nE\x10ON\xc4A7\x1bAE\x93\x1b\x8f\x88\xd0\x9a\xbaf\xab\x81\xc0\xaa\x87\x91c\xa91Ut\xe7\x8bh\x0d\x93w\xff\x87x\xcdR0'\xf6At\xd1Zv\x7fD\xa2\x81M\xec\x17h\xfeWh\xa4\xa1\xca\xf5\xb5\x8f\x81\x81\xd6\x0d\n\xab\x1b\xa4M>\x86H\xe3fJ\x89wq!@\xc9\xa1\xa9\xf0\xaa\xc3\xd12\n^\xb7Q\xf0\xdc\xa3pD'4\xed\xf4\xbbP\xe5\x06(\x8e\xc3x\xad\xdf\x8dr\xb2Y\xd1I[\xba\xdd\xbcp\xf5~]\xaf\x8f\xc8*\xd79Z\x0eA\xd0\xb1\xf3C\xd3\x01{\xf89\xef\xb02\xa29\x07/\xb2\xcd\xd3E\x0b\x92t\x01\xf3\xd4X!\xda)\x84\xcb\xdc\x99\xf2\x91\xecg\x0f\x99\xba\xbaX\xd4(m\x14V\xc2\xd1'85\xc3\x86\xe2\xb2j\x11|Adh9\xe1\xb3\x92q\xc5\xe1Ds \x0f\xad\xa8\xaa!\x83\xcc\xef\x18Q5\x1f\xfb.H\xdc8\x12\xf9\x0c\x1e\x1c\x88\x0f\x06\xd9\xe0\xd4\x87\x00l\xf1\xf2\xe3\x81\xfb\xabr\x06\x87\xb4\xa4\x1a^\x9e\x8e\xb4S\xb0I\xffz\xe6G\x82\xf1\x08\xbc\xf4\xd1@Z\xf2\xe7\xc7\x91z\x01\x92\x14\xf3T2\x95-\xe1~\xcaR\x99\xecb\x81^i\xeee\xc2\xe35fO\xe5&\xce\xa25\xd4$\xfd0\x8cW~\xe0\xb3H\xfa\xd1:S}`\xa9\x0ciD\xb7\xb0VU\xb9\x84q%tI\xc1\xbc]\x14\x07\xf1\xf6^z;\xee\xa7\"\xa4\xa9\xf4\xe20\xcc\"_\xdc\xcb\xb5\xcf\x99\x82\xe1^\xb2u\xe6a\xf5\xec\xa7\xccO\xa0\x1e?J\x85/2\xc1dH\xf9\x0d\x13~\xb4\x95i\x1cd\x08\xd1\x9eb\x81T\xae(\xdfR_=\xc4\x99\xf0\x7f\xca\x98\\\xa1\xa20\x95j\xfb\xaedf\xe9\x05\x8cF\xf8\x10\x8b\x1d<\xc4a\x92 \xc6\xe5\x9a\x85\xb1\xc7\xa9\x90k\x9f\x86q\xb4N%\xf4\xdf\xf7R\xb9\x8b\x83\xb5\x1fmS\x19\xf8\xdb\x1d\xb4\x9fP.\"Us\x12d\xe1\n \xca\x92$\x80\xber\xeaC\x13{\x16)y4\x95\xd4\xa3k\x16\xdeK\x8fr\x06\xd0\xc4aB\xa3{\xe9\xf1\x0c\x06{\x1d\x87\x007\xbbK\xe2\x94\xad\xe5\x06\x9aI\xe5&\x88\xd5X\xc9-\x0d\x02\xc6\xef\xe56\xf3\x05\xe5\x00\x8e\xbf\xa6\xf7\xf2\xc6WX\x11\xc9\x88e\xa9\xa0\\\xc67~Do\xa9\xe4\xcc\xf3\x13\x96J\xce\"A\x03\xf5w\xef\xb3\xdbT\xa6;\xff&\xddQ\x89\xce R\x009\xe6B\xa6\xf7\xa9`a*\xe9\x96E\xde\xbd\\1\x1e\xf8\x91\xf4h\xc88\x95\x1e\xa0\x85\xf4\xe2\xcd\x861\x85/\xeb8\x95\n\x05\xa2\xadd\xa9\xa0\x82I\xa6z\n\xe03.\xe4&\x13\xab8\x9074\xdb\xb0H\x06\xd9]\xc6\xefeH\xfd4\x8ed\x18G4\xdd\xc90KY\x16\xca\x88n\xe3{\x8a\xb8\xa6\xa0L\xa8\xcf\xd5\x1f\x80)\xf6|\x1a\xe0\xa8\xdeKA\x85\x88c)|\x16\xad\xa9\x1a\xe1=\x0b\xe4\xde\xa7?\xb2T\xee\xfd \xa0\xeaO\xaa\xd0f\x1f\x03d\xfb\xf8\x9en\x99\x04\xccF4P\xa3\xbfN\xa5\xb7c4\x91\x9e\xdaw\xc85\x8d<&a\xd1\xcam@S5\xb2Y\xaa\xd0,\xda\xc62\xf2\xa3\x1f)L\xb4^\x0e2\xdd\xc5j\xd4\xe2\x80r)b5\x03\"\xbe\xb9\x8f\xa5\x88\xe3 \x95\xb7j\x8d\xca\xdb\x98\xdf\xa4\x922\x1eK\xca\x13*i\xeaS\xb9b\xa9\x90+\xff\x86\xc9U\x00h\xf9\xee\x9d\x1a\xdeDzA\xb6\x92^\x1c\xabU\x19'rCy(7~\xba\x93[\x7f#\xe46\xe3\x99\xf4\xa3M,\x7f\x8cW\xa9\xbc\xf1o}y\xc3\xd9Z\x064Z\xcb\xc0\x0fc\x19\xf8\xd1\x8d\x0cY\x94I\xb5\x18e\x18\xaf\xa9\x8ch\xc8d\xa2\xf06Q_\x938\x15\xf2\xa7$\x8e$\xf7\xbd\x9d\xe4\xd9\x8e\xcb\x94\xdd\xddK\xe1'\xa9\x1a/\xa6\xfe\x89\xe5-\x8d\xb6\xf2V-\xe7[\xff\xc6\x97\xef\xe2\x88\xa9%%W\xfeZ\xae|\x05\xf0J\xad#\xe9\xb1Xa\xb0Z\xaar\x1b\xef\xa5\x1f y\xe3\x872\xf4\x03\x191!\xe3(\x901\xdf\xaa\xe5/\x93l%\x15\xc0\x82\x052\x8bby\xcb\xd6\xf2\xee\xeeN\xde\xdd\xbf\x93\xd4\x93t-)\x93t#\xe9VR_\xd2@\xd2P\xd2H\xd2X\xd2\x9f$\xe5\x92\xa6\x92\nI3Io%\xbd\x93\xf4\x9d\\Q\xb9Z\xc9\xd5Z\xae\x98\\m\xe4j+W;\xb9\xf2\xe5\xeaG\xb9\n\xe5*\x92\xabX\xae\xb8\\\xa5r%\xe4j/W\xb7ru/W\n|\xe9y\xd2[Ko#\xbd\xad\xf4v\xd2\xf3\xa5w#\xbd@z\xa1\xf4\x14)\x94\x1e\x97^&\xbd\xbd\xf4n\xa5w'\xbd{\xe9\xbd\x93k&\xd7?\xca\xf5\x8d\\\x87r\x1d\xcb\xf5;\xc9<\xc9\x98d[\xc9\xb8d\xa9dB\xb2Ln|\xb9\xf9Qnn\xe4&\x94\x9bXn\xb8\xdcR\xb9]\xc9\xedZn\x99\xdcn\xe4v+\xb7jb\xe56\x90\xdbPn#\xb9M\xe4\xf6'\xb9\xe5r\x9b\xca\xad\x9an\xb9\xbd\x95\xdb{\xb9\xbb\x91\xbbP\xee\"\xb9\xe3r'\xe4.\x93\xfeZ\xfaL\xfa\x81\xf4C\xe9G\xd2\x8f\xa5\xff\x93\xf4\xb9\xf4S\xe9\x0b\xf9#\x93?\x86\xf2\xc7X\xfe\x98\xc8\x1b&o\xb6\xf2f'o|y\x13\xca\x9bH\xde$\xf2\x86\xcb\x9b[ys/o\xde\xc9\x80\xca`%\x03O\x06\xbe\x0cnd\xc0e\x90\xca@\xc8 \x93\xc1^\x06j\xa9\xca\xd0\x93\xe1Z\x86L\x86[\x19\xeedx#\xc3@\x86\xa1\x0c\xd5\n\x96a\"\xc3\x9fd\xc8e\x98\xcaP\xc80\x93\xe1^\x86\xb72\xbc\x93\xe1\xbd\x0c\xdf\xc9\x88\xca\xc8\x93\x11\x93\xd1FF[\x19\xf92\nd\x14\xcb(\x91\x11\x97Q&\xa3w2\x0eeBe\xc2d\xb2\x91\xc9V&;\x99\xdc\xc8$\x90I(\x93H&\\&\xa9L\x84Lner/\x7fR4M\xf2X\xf2T\xf2L\xf2[\x99R\x99\xaed\xea\xc9t-S&\xd3\xadLw2\xf5e\xfa\xa3Lod\x1a\xc84\x94i$\xd3X\xa6\\\xa6B\xa6\x99L\xf72\xbd\x93\xe9\xbdL\xdfI\xe1I\xb1\x96b#\xc5V\x8a\x9d\x14?Jq#E E(E$E,E\"\x05\x97BH\xb1\x97\xe2V\x8aw2\xa32\xdb\xca\xecFf\xa9\xcc\xeee\xf6N\xee\xa9\xdc{r\xcf\xe4~+\xf7\xbe\xdcGr\x9f\xc9\xdb\x8d\xbcM\xe5=\x93\xf7B\xbe\xa3\xf2](\xdf\xdd\x0e\x16\xab\xd3\xaa\xe6\xb47\"\xe8\xffoq\xbb\x1c\xfc\xa6\xbf\xb8\xfdy:\x9a>\x7f?0\xba\xcc\xb2:\x14r_\xcf\xe6\x8b\xf1\xc5\xec\xd1\xd5b\xb8\xf8d\xb4\xb8]L\x96\xc3\xdf\x14\nD\xf6\x897Ub4\xa3\xb6B\x94\x19\x96\xf3\xf1dh\xc5\x87\xe5p\xd6\xbf>i\xfa\xb48]\x9c\x0e\xfa\xd7'\x8b\xf5pqz=\xe8_c\xca\xb5\x13\x90\xbaJ\xb7?\xb9>E\xa5\xaej\xff\xf6\xf6v19\xbadsG\xad\xf6\x17\xd4\xc5\x8b\xb1\x05|\xf8\xe87\xbf^\x9c\xfe\xd3\xd5\x7f~\xdb\x1f\xc8\xc7\x9f\x80@Tg\xe1O\xbc\x0du\xc8\x11\xb3@\x8c\x0f\xaf\x03y\x12=\x1a\x7f\xe2\x81&-''Y\xb7\"\xdf\xb3\x80\n\x7f\xcfl\xb9\xcd\x81S\xc8\xa3/\xfa\x117\x99$\x87NX\x9a\x87\xd0\xd2\xf7\x19I\x9a\xa1\xb54\x7fF\x1cZc\xf3\x0b\xb1\xdf\x0d\xc1~\xba\x10\xf7vj\xd4E\x08\x81\xdb\xe4\x03\xe3bX!\xf9\x17\xa2_\"W\x87\xf8\xb4\x00$\xc6\x95r\xba\xe8\x9fn\x0f\xdc\xb7\x8fJ\xf9\x07\xa7\xdb\x03<\x1b\xb9\x80\x0d\x0e#%9\x1b\x90K\xd2\x07\xf2\x14\x95\x92-!?9\xeb8\xa6$\x9fs\x87w8\x976\xf2UU0\xeb\xaa\x84\xf4#pK\xd5(X\xce\x17\xb7\xcb\x06\xc1rG\xd3\xaf\xb3 \xc8\x8b\x9a\"-\x12\xbf\xa3\x9a\x8c\xfb?x;\x16\xb2\x83\x15\xb8a\xf8\x0f1_\x7f\xa90d#\x18\xaf\x023\x9b\xbfY\xa4\xcb'\xd7\xa6JG\x15E\xe6\xdb]\x1e5\xd3S\x94\x06tM\x7f2\x1dR\xec\xca\xdcb\xc94!\xfa]\xcc\xd2?\xc4\xe2\xf7to)\xf6\x1f\xf9\xefb\xa1\xad\xd3Z\xb2\x7f!\xbee4\x15\x7f\x8c\x98\xe9q\xa5\x8c\x9f~S\x9b\xcc\x9c\x92\xf5]\xe7\xf1\xce\x13\x89r'\xba,\xd7\xea\x82\xd3](\xce\xeb`~\xb6,\x1f\xac\xb6J\xf1\xbd\x1f\xe9\x9e\xa6\x1e\xf7\x131Cg=0\xce\xbd\xfd\xaa\x9c\xd8\xa5G\x87\x86\xbe\xa3\x89\xa0\x9d\xf1\x13\x86\x8e\xe7\xd5\xfa\x07\xfb\x00\xc7:@\x9fw89c\x13A\xdb\x1avO\\\xded\xbbA^\xc7\x82\x87\x81\x7f\x827&NL\x0f\x9aWQ\xcdW\xac\xf99\x91\xa7\x0d\x05\xbb\xa0\x92\x01\xf3\x84\xd9\xf1m#Q\xcd\xc09\x88$\n#P\xf8\x08\n\xf9Q\xf6\xcf]\x06\xef\x01\xc7\xbc\xaf\x8abS\xd7C\xae\xc2\xbe\x18Jv\x84-7\xf5=\x06\xc2\xa2\xc1\xa6\xb3T\xe3<\xc1\x8e\xc3q\xf6W\x98\xc5\x8fs\xe6\x87\x1ej;\x8e\xc2W\xb8\x7f\xe9Zy\xbe\x1f\xecX\x7fq\x94\xbb6R\xf4g\xfb\xc0\x06\x1f\x80A\x0d\x8d4\xce\xa7\xde\x8a\xfd-fT\xef\xd5\xba\xce\xe9\xeb\xf2\xd6\xaek3E\x0d\x00\x96\xed\xd8\xde\x83\xe6\xd88N\xd3\x0d\x82\xe74;\xe1\x0f\x87\xe2\xb8\x89\xef\xfd\xa6k\x93\x8dh\xf0'\xfe\x80E\x9d\xf1\x00\xf7S\xb9\xc2\x13\xc6\xc3(\x8d\xfb\xa8\x00\xbe>uY\xc3VX\x91\xad\xa2A\x1e5\xf9\xbf\xe3,a\xd1\x9a\xad?\x96\xedI\xc6;S\x99?\xf1.4\xa6tO'\xe3\x0dJ\xa2\"\xb6:\xf7\xb8V\x80\xacn\x9ak\x1f\xec\x90\x94}\xc3d0\xa5=\xed+\x10\xcc\xbdGM\x05!\xf4}G\xaf \x0f\\*\xd0\xb2qv\x9e\xfb\xf4~D\xc3\xe4\x02\xe21=\xeav\xcd\xea\xd85R\xbd6\x05\xed?tN\x8c\xbe\xae\xa8P(\xe7\xc3\x05\xd1\x07\xe7XU\xb5\x83\xa3\xf8\x9f\xcc\x12\xc2\x12\xf6#^`}\xcd\xa9\x1f\xf8\xd1\xf6\x87\x80B\xcc\xf6.\xe3S\xae\xb6\x8bl\xe4V\xd1\x97\x17\xb7\xdb\xe1zS\xf3\xeeAy8,Nb\xd1\x19$\xc7X\x1e\x01J\xef\xb4M\xe1Q\xd4\xe0\x1a\x87\xab\xe3i'/F\x8a\xfa\xda\x94\xf7#\xedh\x11c$\xf16?\xa5\x1a\xb0x\x92\xfb\xe5\x84\xbb\xc0\xf9`\xbc7\xbeeFd\xbe\xc4(>\xfd\xa2\xdbx\x1d\x8a\xeaC\xa3a\x1b\x8c\xc8<\x0fa\xde\x1b\x91\x1e\x04\xa4\x86\xf02\xea-\xf0S\xd1s\x85(\x9d\x973Bm\x9f\x7f@m;\xaek9?\xfb\x80Z\xe0\x93\xaeg\xdaZ\x8f\xbb\xbc \xcbm\xea8\xaf\xd4\xd1\x00;\xa3k?\xda\x9aBO\x1f\xd0pP\xa9\xe3\x99{\xf6v\"\x0c\xa0.\x93\xef\xf9\x03\xda\x12t\x15\xd8\x1e~\xda\xa9\x87k\xb6)\x0em\x15m\xdc\x85\x8aPA\xb1\xcf+\x81\x0d\x97\xee\x98x\xd5\x05\x8a\x14<\x0b\xacW\xb6\x8a\xcb){\xdd\x81\xa1\x1b\x1bF.\x89o\xaf)\xb0\xe1pP\xa8BG\x92\x9f\xb3%\xc4\xe7\x82\x87\xe9\xd2%\x8e\xd1@\xcc\x08\xe6<\x87\xf3\x85\xf9r\xa0\xa9\xd2\xa0BzrJa\x9fh\xc1\xad\x11\x04\x82\xf0\xdf\xb1\xaa\x835\x87\xe6\xcd\xf6E{\xfb-\x00\xbee\xe2\xfb,`)\x1e\xa3\xa3\xa3\x04\xec$\xbaH\x10\xe8\x10\xe1dzA(\xb9\xd4GHl\x12\xf8\x91j\x98\"Q\xbd\xf1\x93\xaf\xc2D\xdc\x7f\xebG,\xedS\x08m@\xc9\xcb+\x12\xa1\x17\xfe\x93>\x9b\x88\x1fv\xfeF\xcc\xe9\x12\xae\xdb\xac\x82\x9bo\xa25\x8b\x84\xfb\xfa\x13\x00\xccq\xe0\xe1F\x08\xd4\x12\xcf\xf9Ru\x91\xc2\xf1\xe6\xc9tpA\xf8p\xe8\x90\x130\xea\x85\xf0\xb7;\xa1`\xcfF\x84M\xfc\x14@4\xb0[\xbe\x90\x19\xb9\xaa\x8f\x9dQ_\x07\xa6\xa7y1\xda\xa86W\x8da%#2\x1c\xdaAB\xaa\xa1\xb9RB9\x8b@\xe8\xad\xd7\xda\x12\x0e&\x1f\xe7\xda\xe7\n\x9f\xcaq\xa5\xcc\x0420S]D\x0bQ\x8b%\x99\x82q*W\x1f\xb3\xb3\xb3\xcf\x9e/\xe5|\x91\x9d?;\x7f\xb6\xc8\xce\xcf\xce?\xd3\x89\xd5R\x01\x94\xca\xce\xce\xe8\xd9i!,X\x111\xe1\x8e\x91\x03+G\x84W\xc7P\x81\xe8#\xa2\xb9<)\x03\x02\x94\x92\xe1>>\xb3\xc7\x02\xd5\x9b\xf3\xc0\xe55\xab7\xc2I0\x02'\x10\xb98\x9b\x8eHo\x11\xa9\x14\xabU\\\x88\xde \x8f^W.\x9f\x15\x18p\x93Z\x1b\xd6V}\x0e5\x94\xd3\xb3\x82p\xf2e\xbcf_\x88~4 \xd7:,,F\xf9\xf3t<\x14\x08\xfe\xa6P\xbf\xa7j\xe8i\xda\x00\xee\x85)\x19\x13o@\xfe\x89<3\xc7\xb5\x90\x08\xc5y\x95z\xe8\xd5\x8c>\x15\x99\xf1\x07k\xe6\xc1\xdc\xab\xd54\xa4\xef\x8f\x14q\xf3#f\xfe\xbe\xa2w\x05\x024*\x05\xb4Al\x1fz\x1epZ\x86U?@e\x18kM\x9a\xeb\xae\xae\x96\xab\xdf\x8a\x00\x9c\x0dj\xa8X\xac;\xdf7\xfd\xaa\x0e\x08/\xbaUD\x1e\xd6\x1a<\xa0\xb8Y\xc7\xfa\xe7li\xd5`(\x11\xb0\xa5\xa2\xbc\x85.\x14=\x9f\xbd\x1f\x95\xda,K\x1a\xadM\xd7]\xda\xeb\xfe\xa2(\x87g\x8f\xfdC\x90]V\x00\x1b\xa0\xe8w\xe1\xea%k\x83\xfa\x87\x84zGC\x9cr/\x978\x0d\xd0z\x15\xd9\x0c\x85%\xc8\x1e\x0c\xde\x97;\xca\xd3C\xaezKn1\x9d\x00F\xf6\xe4\xa9\x06\x19\x02\xfdA\xf0\xfd\x96z5w\xc2\x0e\x86\x0c\xd2\x1f\xb9\x04\x97\xf8\xa6n\x07\xdfP\x10\xbf$\x91#b/Z\xaa\x9d4\x0c\xf2x\xccr\xbb\x04\xa6\x96\xedq\xdd\xd92Q\xc7\xdeV \xa9j\x19\xa98]],b\xb0\x8c\x1a=\x14\xa9,\x81\x82\xb6\xe2\x92\xd4/\xaf\xffy\xa0V\x01F5\xf0\xf1\x10\xce,\x87`9\x02\xb7\xad\x8acpr]Z\x19Pjj\x1c\xc1\xdb\xc4Q>\x82(\xc7\xa8~\x0c\x1c\x93\x91iQ\x05|\xb7\xf6\x05\x19\x83\xe1\xac\xf6 \x1a(\xd4\xbf \x81\xa2\xbc\xf1p8\x80\x88ne\xc8\x06j*Ax\x03&?\x18\x01\x07;\xb3)gZ\x1c\xaa\xf54\xc5\xfe\xe0\xc8\xa8\x15&e\xf7\xcee\xf3xY\\\n\x8d}\xd4c\x9d\xd5}UUD+\xb4\x8d;J\xb42\xa9\xee\x90\x83\xee%b\xf6\x82\x0e,2c*\x96j\x12\n\"\xcd%y\x96\x9b\xe3L\x1ds\x18\x03^\\\x81\x8f\x9a)\xee\xdb\x9aVW\xbe\x03\xe2j-\xb9x~\x8b\xdd\x1fl\x02rHy\x15\xd2\x97W\xe4Y\xfb\xc6J\x81:\x1c\x1er\x06k\xf5\x9cZ\x86\xe3\xa3<\xf6{C\x8c*\x1d\x8b\nUf\xb5\xaf6\xe6TN\x05\xd4\x96\"\x1e\x91g\xe0\xe8\xc5va\x04[\xd2ZyP\xc2\xb8\xaf'*\x10\xd3\x19\x99\x8b\x91\x86\xd7\xa1<\xd1\xe1\xab\x18\xca\x8c\xa5\xcf\xef\x95\xf0\x96\x8bI\xef\x7f\x194\xecN\xdf\\\xc7F\xe8|C/^\xb1\x84\x11\xb3\xc8Z\xcf\xbe\x81\xec\xccd\xaf\xa3\xbaG\x86\xe4)yI6\x8dh\xadrM\xcf_\xa0\xd7\x96\x18u\x1def\xe0\xa1\x82\xe3s\xcc\x13\xb7\xd6\x04\x92\xf7\x08%\xe7\xbeg5'\xc0\xda\xfa\x9e\xda\x03\x0d\xc8\x98\xa4\x03rI\x9e\xb6V\xa45\x159\xc5\x01C\xf9\x89\xe0~\xd8/\xeej\xff\xac7\xb5\xad\x95\xf1\x82\x8d]\x03a\x16\x17\xe4\xa4?\x1cf\xa8\xd1A\xc1 :\x90\x16g$+\xcdH\xb6\x04\x9b\xbe\xd2$\xa84P\x7f\xd8<5]P\x03\xb5\xa8\x8d:0\xb1\xb8\xa2[\xca\\\x84\x00\x04\xf8\xe6\xd1\x06\xe5R9\x0b\x8aj0\xb5\x10\xb0\xbe\x81\n\x01\x9a\x9e\xb9\xe9\x0b\x90\x9en\xd4\xc5\x87vs<\xce\xc9MF\x86\x8ae_\x03\xeb\x81\x93\xbfn\xc4\x07\x94\xf1\x0e\xea\x93PN\xc3tFhG\xc2\x84\x8a\x85\x0c\x16\xa7\x93\x1c\xfd{\xa29\xf5\xb0\xbb\xc7Q\x9b\xf0\x10\xb5\xd9\x93\x97$l]\x89/\xce\xb5\xb1[\x05\xdb\xf7\xc3\xe1\xa0\xb5\xa0\x1e\\\x85\xeey\xac\xdf\x90\xde\xfd\x81\xa5\xc2\x8f\xb6\x1f\xb2\xfc\xf5f\xa3\x0e\x13\xac\xe4\xbd\x92\xc84\x11\xc8Y\x17\xab\xeaA \xeaaa,\x01\xc9\xf3\x91\xbd\"{\x14\xce X\xed\x9e\\\x92\x10\xc2\x11\x15\xd6\xe2~@fd\x0f\xd4,D\x81m^\x98\x0d\xa8/\x17[T\x1d\xe3b\x0b#\xcd\x0bP-TS|\x17\x8e6\x8cO)\x94`b\xb3\xa39\xe9\xf7K\xe8\x10\x97\xd0!^\x02`\xfd\x12\n\xc4\xcb\xc1\x00\x03\xa09IZ\xfb\\7\x8b=~\xabXc\x03+\x9fLGpW\xe7\x0c\xaf\xa6l\xec&-!\x97d}A\x92C\xb1\x0b6\xf3d\xa9/eE\xb0\xfa\xdbt6\x04\xaeA4SC\xf3sSE\xf3k\xf6\xd0\xb5k\xedtf\\\xfd\xdb\xc9Q{\x14\x93\x98\xcf\xd1\xa88c\xa0A{\xfa\xf4\xd3:\x8dF\xc1\xb3\x03\xde;\xdb-\xa2\xc8\xf1x}\x18\xe8\x12f\xc7K\xc7\x8a\x0dH\xf9\xc0aT>~\xb8\xaa\x9c{v\xe4)y\x99\xa6\xa0\xc1\x9a\x19@\x84g1\".wue^P \xed\xfb~0\xca\x97\xa8\xd5K#\x11\x8f\xbb3\xbf\x02\xa0M\xf1om\x9c\xdb&\xa6T\x190\xc5\x1b\xe6\xd3\xa5=\x1d\xd2K\x0b\x17\x13\xcd\x97\x16F\xac\xd6s\x93\x90!\x01Z\x94\xcd\x93\"}\xb2\xe9t\x9e,\xdd\x8a\x83\x12\xf9L\xff.xd\x99\x17:\x0cJ\x0eq\xbf~F\x86%9Gm\xd8\xd3V\xce\xf4\xec\xbcE\xee\xce\x80N>zD\x9e=G\xc9\x1b\xa4\xf0\xe7\x07\xa4pX jEN/HF.I\xea<|\xac\x88\xd8\xb5Vm{O\x11B\xda\xd8\x1e\x01\xbfrVT\xf5\xab(\xef\x9a\xfe\x93\xbe\x8f\x1b\x80G\x8fH\xff\xe4\x84k\xbb\x10-\x13j\xa1\xac\xe3b\xd8\xf1\xe6\x85\xfaaR\xdb\xa0z:}\x14N\xda\xe4\xcai\x90\x0b \xf5\xf9\x90s\xa9\xf4y\x9b\x90\x86\\9.\xa3\xe6\x80\\\x93\xb1\x12\xa8\x0dzE\xae\x89\xe6\x15\xf4\x02)\xe0\xd9S\xfd\xack\xe0\xe4\xb2\x84\x07\xf5Zlc\xbc0Z\xf5\xce\xc7\xad\x9d?N\x0e\x8d\x0f\xadD\xf0\x83\xa8F&_&c\xd7\x1e\xb3e\\.\xc9\xb3\xcf\x14ZF\xe4%y\xfeic5\xa8em\\b\xbc\x1d\x08b\x15=m\xa0\xa8\x1d\xdegj\x0e\"ry\xa5\x80i\x13\x9e\x9e\xa1\xee3R\xb0?{a\xa2\xa6\xb6\x88\x16\x16\xb4\xda\xd7\xa6\xe3\xf7B\xa9\x07\xa2\x87yj\xa7\xd7\xb534p\x87\xd9\xb2\x9b\x19)\x01c;\"\xf7#\xb2\x1a\x91\xb7#r;\"_\x8d\xc8\xdd\x88\xfc0\"_\x8e\xc8\xcd\x88|\xe1\x10\xe1\x00\x15\x94\x08\xa9q\xd4(\x14\xb6\x8e\xbc\x0d\x1a;=\x89\xaa\x12^\xaa\xa4\x95lB\x03\xd3\x96Q\xfe\xd0\x8dO\xe8B\xaa\xb5\xbe\xcf\xed\xb7\xef\x8aV\xb8gG\x12l\xace\xb6\xe4\x1a\xef\x017\xafV\xd8T\xa2\xffj\xad\xd4\xd07\xca\xd5<\x911I\xf0~fg\xfa\x1e\xf35\xe3l\xfd6\xf0S\xd1$\x97A\x9e\x19\xd972\x82\xdb\x87KlJz\xed\x08\xea*\x0b\x02&Z!\xfdpx\xac\xc9\xd2[\xbd\x07\xbak\xdb\xf7\x81\x81\xce\xe0\x82\x9c\xf4O\xfa`\xb6\x836\x98\xb0\x81\xea\xdfW\xd5AkD[K[\xe9Rkf\xee\xc9\x98\xac\x958\xf3\x0cX\xb6*\xadPhG.\xc9\xb4\x94\xa2\xa4\xa8uQ~\xa7\n?v\x9dg\x1b\xc6\xce\x17,<0\x80_}\xc8\x00\x06\xd5\xdd<\xea\xc5\xc0H\xc1\xec\xf5\x0b\x08\xbdq\xec6\x8a;\xf1\xfb\xeaN\xbc,\xdd\x82e\x965\x808\xab\xefU\xb4}`\xd3\xc6\x00\xf7\xa6y%j\xaf\xfe\x16f\x11\x88\x99\x1a\xf5\xb7Vn'c\"\xc8K\x9c\x14\xa7=X\x15\xba\xa0\xda\x9b\xb4\x08\xaeW\x83v\xf3\x80\xa9|\xf0&\x050\xbd\xb0'\xf9\n\xb7(tD\xee+\xd2:\xd1\xa6xj\\\x8a\xa6g\xf8~\xbc]\xde\x8d^\\?\xa0\x82\xe1KrE\xee\xec.\xe8\x07rI\xbe\xbc ?4)\x18\x14\xe9\xbd\x9b\xffP\xb4\xe3kW.\xdc\x1cP,4+\x15\xea\n\x05\xd5\xf8M#\xc7W_\xb7m\xf2C\xce\x08)HAg\x83&Eo\xeev#\xe7{\xe52\xee\xe6C\xb7\xa4\xb0\xd6\xf7\xf6\xeb\xad5\x1cXuAB\xc5\xaf\xca\x1c\x04q\x91T\xa8\xf5\x831\xf4\xd6bdn\xc7\xa8\xa4\x8cG\x8f\xda\xcd\x0cHY\xf2G\x1c\x07>?$\xe7\xf5q\x03\x9c\x8c\xf4\xde\xe8\xdc\x08\xcc%\xe6L\xc6\xe4\xbc\x14\xb7\xd3f\x98GKcAevi\xb9\x851\xd2Y\xad\x08\xca\xf3\x0bm\xc6\xd9\xcf\x13U\xcb\xcb\n!+\x14(\xa4G\xe8\xd8\xbc1k\x97\x82\xa1\x7fO\x9b\x8bv$\x08\x99\xb6g\x1b\x92sT+\xf43\xb3\x0b\xf4\x14\x17x\xfe\x99{\x08\x87\xc3lPVDd\xc3\xa1\xc2m\x16\xed'\xe6VCjn\xae\x94\xd2 \\c-\xeb\x84\xb3\x8d3?~\xd0\x85R+\x9a\xe3\xf1f\x80\x0b;S\xcb\xb8\xa1\xcey\x0f\xae\xf0\xa6Km\x1a\xd9\x8d\x04\xda\x9b\x19o9\xdb0\xce\"\xafY\xbdIW\x8a\xda9\xe2\xe1\x1f\x14\xa9\xe2*?\xae\x1d\xf9\xd1\x03RTI\x10\xcd\x06d\x8c\x82S\xf1\x08%+\x0b/\xc3+\xf2\xac.M\x15.\xa2\x14\x1b(1~C\xd9\xec\xd7\xe1U\xedx\xc7\xb6;.}k\xd1\xe0\xe6\x82Z \"Z\x86z\xac\xa1.\xf6\xdd\xaf\xf64\xfe\x90\xd9}03SR\xca\x07\xe9\xbcL\xea\x07Q\xe7\xe3\xe8\xf2A\xad,\x9c\xe8\xb7ka\x9f>o\xd3\xc2\xe2\xb5\xb5\x03\xd5\xe4ZW\xb3\x16\x1cd\xe6\x82<}\x9e\xf3`P\xce\x82\xca\x94\\^\x91\x17\x17\x03\xe2\x83\xf1Wci\x17\xd5;\xe9\xfb\xe4%y\x81\x10\xea\xfa\xb4.&.S\xb5\xd4\xae1kg\xd8OG\xe4\xa9\":\xf9\xcd\x90\xfa\xf7\xe7\xea\xbb\xda\xfae$7\xcc\xac\x01H\xf3\xcb&`=?(\x08DG\xeas\xf1:W\x13\x8d\xda}\x8bX\xec\xb8\xc9\xfd\x11\x94\xbev\x0c;\x02\xebG\xaa\x9dv+\xa8\x9c\xc6CH\x1fm\xc2r\x084\x18\xb3\x07u\xd1\xdb\xf9\xc1\x1a\x1ci\xcd\x97\xb5\x0ev\xec\x97\x99\x84&R\xd26\x0b\xbf\xacZ\xdd\xa4>\xc4\x12pd\xee\xe1\x88F\x8bV{\xa7K\xcb\x10\xcd{GG\x86\x8aa\x8e=\xe0\xe8\xf7K\xec\x91\x96\x88\x1a\xd5:|\xbfH\xc8\xe8R\xcb$\xfdg\xcf\xf3\x8b\xb8\xb5U\x17#mz\x81:_\x8eE\xe2\xf2B\xee\xc7x\x17\xc6BQ`\xb31l\xd7\xfcb\xb9F\xb5^\xe1>\xdc/\xb0\x9cM\x17\xb4\xbe\xe9\xfca\xa8\x7f\x00\xf7:\x82|\xdc\xa2\x06V\x9d\x1f\xbd|\xdc\xe5\xad\xa8\xea\xbf\xf2\x12\xef03\x87W\xfc\xe0# \x16\x85;\xdfg\xe7\xd5\xbb\xdd\n\x81O\xdf\\\xf6\xe7:x\x9fvu=_\xa4\x8b\xd3\x97U\xd7n>f^\x9c:\xb2\xbf\\\x9ev#4#B]\xb4&?\xa0\xa8H\xc5\xb5\xa1\xab\xd8o\xd63$e1\xba.\xbbxJvMF\xe4$\xdf\xdc\xedD\x18\xb4\xca;\x89\xa2M\x8apx\xb0[zyu\xc0<\xf4\xc5\x99{\xeb\xe4\xb5\xef<\x9f\xe2\xa6\xae\x9f\xb9H\x97\xa7w\xae\x8a|a\xbe\xaci_Y8{._rz\xdfv\x1c\xf3\xecS\x00\x1a\xa4\x96\x93\x96\x1b)\xe6g.\xa5<='\xb2z\xf5\xc0\xfc4\x18`t\xf9\xf9\xa7\xaaf\xa1d\xb7\xe9\xf9y-\xfb\xfb.\xdb\xdeg\x9f6\xf7\x9c\xd8c\xa5\xeaV\x11-a\xd1\x95\x9e?(\xb6R\x87\"W\xd2\xb5\xd7\x13\x0f\x0eC{\x82h\xc0\xe7\xe9|Zq\xd6\xb7o\x0b\xd5m\xfcm\xc6\xa1U\xb5\xb3e\x1c\x9fx\xa8\xfe\xee\xa6\xf0\xef9\xfc\xfb\x14\xfe}\x06\xff>\x87\x7f_\xc0\xbf\x8c\xae\xb1\xd4\xce\xc2\x03\x1e2z\xfe\x86\xd3P\xbb\xc1P\xff\x86\x14>\xc6\xe0\xd9\x0f\x9e\x00\xd28\x13I\x06\xef\xf09A`\x12\x1eo9K\xa1\xf3\xe8b\x12\x9e\x98g\xe0N\xc5=\x8e\xa6\xf1\x11\xd1\x13f\xd8\x04tY\xb0;A9\xa3\xf0\xbc\xc1\x0b\xaf=\x01~'\x04\xc7gF!g\x06p\xec\xfd5\x8b{\xcb\xc9&\xe6_Qo\xd7o\xb9\x808g\xcb\xf2\x0dP\xad\x95\xfa\x90\x1b76\xb9\x8b\xf9\x8aCr\xcc\x95)\xb5u\xc0\xdb\xb6\xecv\xf9\x16N\x8e\xc1BdL\"\x97\xb7\x88v\xf6\xdc\xf5\xcau\xd1\x8a\xa0\xce\xc8\x04\xb2\xc9\xc2];\x17\xbb\x0bJ[]\xe4\xd8Am\xd7\xd0RA\xbf\xa4\xfa\x08J\x12x\xb0,\x9f\xcc\x06\xcd\x14\xd7\x87\x0b\x1d\xa80\xd6\xbb\n\x87J#\xb7\xfb\x81\x1b\xbfZ;\xea\xb7\xd6J\xady\x030\xef\x1199}3\x1f\xcf$Y\x0e?9EW\x9b\xb4]$\x80\x1b\x08\x14C\xa9\xf6{\xb2\xa7\xf6\x1f\x10\x03\xb5M\xad\x92\xe8\xeb\xe7)Z$\xa6\xe4\x92\xe472[no\x9f\xc0\xb9\x947O\x97\xe6\xdaH\x1b\x9fE\xff\x05\xa0\xb8M\xe1\xd1+\xb9W2\xd7\xb2[\x05\x83\x83\xde\x98\x89\x01\xed\xf4\xcd\xecz<\x9c]\x9bq[\xb7\xb3\xdf\xe7\x9f\x01H\xeb\xd2\x81Y \xbek\x92 {se=S\xdf{\x18b\x0b\xce\xbe\xb8\xbf\xdd\x89\xde\x80\xcc\x9c5\x9f\x15\xaa\xeb\x05l\x839MB\xaf\xed\x06\xb7\xea\xdc\x18w\x0c\x05tq\xdc\xdb\x81\xb9o\xc1\x14D\x14\xeb\x9d\xed\xcdB\xca\x85\xfc\x04\xfc\xb3\xf5\x06\x05\x04\x1a\x91\xc4\x8c\xc3Ia\xd2Z\xeb\x8e\xdb-_:\x8a\x0b@\xe8\x0f\x98)\xec>\xc4L\xa1+\x1c\x8ao\x1c\x80C\xc1\x00\x8b\xf6\x97\x84\x83\xff\x92@4/\xfe\xae\xe0\xed\x9a\xc0\xa3\x81\xbf\x8df$\x99\xa7.\xc0>\x02\xec\x1d!<\xacw(\xd0\xb2\x8f\x00\xe9/\xa3W\x10\xbb\x87\x1e@|\xc0R\xe4\x0fm\xf3\x88n\xa9U\xf6\x8b\xb7\xa2d\xc6\x03\xcbh\x0f4\x05\x8f\x0b\x1fDW\x8c\xa0r\x8e\xdb+}\xfb\xa7Efy\xf4\xc88)\xcfiz\xe0\xa6\xe9p\x83\xbd\xd1\xaa\xa6;Q?4^\xa4\x0b\xdd!\x87F\x83|0q!\x058\x1a\x8909DdHW@7F\xa0\xc9\xc3\xf3+Q\x0f\xc4\x15\x95\\e\xe2p\xabrD\x9a\xf2\xc0{Y\x8a\xa8$\x91Y1\xc5j7\x8f\x19\x97F\xb2F\x8a\xa4\xad!\x8a\xca!\x8aE\xda\xa8\x16\xe9\xb8\xf8Hi\x12\x9b\xd689\xb4\xce\x89\x83\x8a\x11\xd8\xa2to\xbe\x99\x90\x91n\xcd\x97W{\xe9\xcdn\xad\x8e E\xbf8\xc1\x03!\xea\xc1\xad\xec\xd0\xfcj\x8f\x7f\x82QI\xed\xf3a\xea\x13\x9b\xdce\x03\\\xb0\xe2\xea|r\xedw\xd8\x06\xc7j\xd3\xe7\x1b\x13z{M\xdf}\x18d\xees\xe8\xbd\x1c7\xc5b\x14\xc7#\xd7\xe9\x8f\xce\x12\x95\xda\x89*\xe3F~\x91}\xb6\xb5\xd6o\x15\xd0\xfb,\xf7\x08\x06\x96\x85\x8f\x1e\xd9\x89x\xe9t\x9d\xb7)\xee\xc3\x8d\xaep\x03\x05\x87\xc3\xcd\xc1m\xbc\x9d\xb3\xcdQ{w\xdf0\xc6\x8d1\x81lm\x03\xd0\xf9h\x9b,m\xa7\\4\xfb\xeb\xbc\xd2\xd6\xc1\x01\xb9\"\xf8\x90\xbdJ\x866\xe9J<\xa8\xf8\xafc\xb3\xb6K2\xf0\xe9^\xdb\x0dn\xb5\xd1\xed\xa1\x1e\x91B\xaf\x1a-\xedIA$\xceF$\xfb\x10\xb6{\x04@\xdd\xb8]A\x03\xac`3\xd8Z\xf4\x8d2m>J$\x1d\x8f\x13I\xb7!\xf8\x98\xfcs\xddlKK\x0e\x11t\x82\xfc\xd3\x89'$_\x9d\x07A!\x05pZe2\x92\x8f\x8f\"k\xf3\x8d\x1b\xf9m\xd6C\xa8B\xf4x\xe1\xb5\x1b}\x9d`\x0d/\x86\x86\x8d\xf4\x89^a\xa6\xf7\xc5#>\xba\x1c\x81\xd2\xa0j)W4\xd9gE\x1f\x89E\xfb\x03\xd8\x12\x14\x13\x14M/\xdd\xc5\x18\x91\xf6\xab\x08\xb9\xb7b\xa7\x91\x1bu\xdfF\xd8\x82\x81\xd1\xbd\xb9\x8d\xb0\x05\xb0\xf4\xf15=x\x1b\xa1\x08\xee\xbe\x08`X\x83oW\x1d\x8adT\x1e\x8du7d%%\x0ciCX\xd2\x05i\x89\xd9F\xa0\x18\xb2\xb1\xfdW\x02\xfb\xcb\xfc\x02^\xd3\xb1\xe2\x01\xb6s\xb0\xac\x83\xf9\xb4\\\xf8\x03\x1a]_x\xb5\x14\xe4\xa5/\xdb\xee\x0f\xfa\xda-\xf0\xa6\xc8j\xb3f\xb7T\xa5\x8e\xd6<\xe3\xb4\x95\x82\x8d'\xd0\xc9\xc1a\x90J\x17@\x1e=\"t8\xcc/\x88t\x01\xadn\xec\xd3\x06\x9a\xef\xbe\xfdP\xca\xfc!\x92\xf8:x\xb8\x80\x1ch\x94,H\xc6\x9b\x11\xb9\xff\xc7\xfd\x04\xe7\xfd\x04\xef\xa3\x1d\xba6\x8a\xcb-\xdb\x87\xe2\xfd\x04\xb7\x91\x9a\x0f\x1e\xb6.\x8d,\xaf\x8f\xc5\x07\x95s\xf1\xd4\x11=\xceZ\xf37\xde\x14\xcc}\xce\x0fP\x13\x12\xd5\xaaE\x9dH#\x19*\xe8\x90R\x971\\\xdb\x0d(\xeb\\O\xc9\x7f>^\xba\x82%o\xd51>\xb9$\xf4\x82\xf8m^]\x88\xa1Is\x1f._\xa5]._\x99_\xdc\xc1\xbb\x0b9\xe8\xe1\x858i\xa9\xf9\xe9\xcdM\xd7\xfb\\\x9aN\xe0j*\xda\x0c\xa4\xcd\xd2b\xbe\xd0\xd3\x11\xe1f\xf1\x15\x97\xca\x01rSYzu\xa2\x03K\xc9\x1d\xf5\xa8\x8b\x19DY\x8c\xaaQ\xac\x8eP\x1eV\x96\xf3CMw\xb4\xc1\xfb\x85\xec\xef\xf2an\"\xeem\xe3\xdc6\x86\x1f\x8d\x88\x1d\x8e\xb0r\xfe\xf4\xb9#\xc0J\xd4?\xff\xb4\x92L\x1b\xe2\xae\x08vgbc<\x9d\xba#wD\xec\x16\xa7\x1as\x9d\xbbs\xb1\xd4\xa3\x89\xcd\xf4\xd4\x9diE\xbd\x1b\xe1{7&\x8a\xcb\xd3\x86`!k\x16\x98\x1c\xcf\xdd9\xfc\xc8\xd6\xf1\xc2\x9d#\xa4\xdc\xc4\x1ay\xda\x10Q\x86\x85\xc9\x8e\xa6\xbe\xad\xe93w\xb64[\x99\x1c\x9f7\xe5Ht\x8egg\xee\x1c\x81\x1f\xd9^?k\x18h{\x95\xc4\xac-\xcc\xdd0\xe0\xc5\x8b'&k\xc3\xb0S\x1d\x1e\xc8dk \xd1\"\xa8 \xe4\xf2\xaca\\Y$|qo2}\xd6%0J\xf6Q\x02\xa3\xe4^\x90\x9c\x81Q\xa8 \x8cB10JE\x11\x0c\xd9\xf7\x18\x81\x99}\xebG7\x8a@\x17\x16i\x1d\xea\xb4n\xe9\xb3\xb7\x81t\x91\xd8\xb7E\xcc\xd5\xbc\xc3\x1c\xc6\xabb\xbe9z\xf9J\x8d\xa1\xafXI\xf1\xf8f\xd63\xf1hU\x89\xb9\x0d\xa6\xdb\x1b\x15\xe3\xed\xf6\xc0H\x0bM\x9c\xd6T\xd0\xde\xd2\xd6 \xcc\x11\xce\xac7\x98\x9f-]\xe6:Y\xc5\xe7\xf5kE*[=\x86C\x9fG\xc6KLa\xd4KQ]j\x88\x02\x8ez\x8d\x8e\xac\xf6\x15u\xafI\x9c:4y([y\xd4\xdb\xb1\x7ff\xa2\xef\xc3\xe5\x97\xb3\x01\xe6W\xe8R\xd1o\xb9MP1l\x03b\x8f \x97$\xbe \xa2Mx\xe2s\x01\"\xcbI\xc1g\x08\x04\xe2\xd2\xa0\xfc\xa0@\x19!\x10\xce3\x86$N\xf1\xdeb={)w>\x17\xefG\xa5\xe90\x1b\xfd\x8e\xfe\xdb\x0fNIy\n\xf2!G\xf7\xf40\x98\x97\xc4o\xd6\nF8x\x91q1s\x02\xc3\xc9\xe7\x11\x8e\xd3t0\xc0}\x84{W\xd6\x18\xe8\x187z\xaa\xf5\x97`\xef\xd4z\xbb\x9dM\x12\x16\xad\xfdh\x8b7\x04S\xee\xcd\xf5H/\x1b\x06\x95\xe0d\xe8R\xa0\xf7P\xe4\xe1;L\xe8\x0f\x9aF\xff\xd8\x802\xcdaO\x1ct\xc7\xeap\xfcF\xa7\xdc\xd9\xaf\xc8\xb1bB\x9dd\xf1:\xc2\xa4\xb7\xbe\xf0v\xc4mw\xed\xd1\x94\x91\xe9\xd9\xcc\xfd\xe1\xf3\xf3\xa6\x0f/\x1a>m\x1a\xad\xa7\x9f65\xdf4(\xd3\xf3\xc6\x91o\x82\xebE\xd38>w\x8c\n)\x98\xd29vbk\xb6\xa1Y \xda\xcb5\xf9S\xeap\x94\xd5H\xec\"\xcb.\x80\x1c\x192\x06T\x89\xd7]7G\x83\xc1\xc5@\xd1&'G\x8e\xf4e\nE\x82\xd4\xb6L\xe8\xbb\xe2UJ\xa3\xad\xf4!\xa3Z\x87\x83Q\xce\x82\xca\xf6\xe2\x1f \xe2w\x1e\x8b\xaa2\xc8\xc9;\xa7\x0d\x17E\xe2v[?=\xbc\xd8\xff\x82\xf1\x81\xd1#\xe1h\x8f\xc8\x89p;\x9a\x85\xd3\xcb\xb3\xd2\xf5TSYyV\x9c\x88ck\x98\x1e\xacA\xbb(9\xa0\xc6\xb0\xf4\x19U^>\x9eS\x12\x7f<>\xac\xb9\xb0~\xd4\x1c\xcd\xfb\x9d\xd4\x189\"\x15\xab\xc9\xedE\xce\x14+\x1e\x92iC\xe8\xd9\xe2\xefC4\x1d\xec\x90\xfe\x9d\xe4[\xe1\x1d\xe5kh\xabE O\xdaw\xbd\xc5\xdf{\xf70\xd7Xzi|\n1SG\x87\x81\xd7\x80\xa7\xf1F\x1c\x02\xbc\x03\xd0N\xa3\x11\x0d\xeb\xc1\x13\xb7C0\x1ch\xdfiv\x17\x0f\x87\xe8\x19\x9a\x93\x96;\xdf\xb1\xa2rq\xe3\xfd\x1b$U\xf1\xc7RF\xd8\xa5\xc5\xb59\xb8\x0e\x9c\xa2\xc0<\x7f\xfe\x02\xfdP\x13\xbd\x19;+\xf4\xaa\xb7X\x9c,z\xbf\xfe\xe4\x9f\x1e=\xee\x0f\x9e\x0cG\x93\xd3\xd9\xc5\xe5\xd5\xcb\xeb\xdf\xcc\x97o\xde\xfe\xf9g\xf9\xfe?\x8f{f\xe3\xd2\x1bt\xbboQ6\xb4Z\x92\xabb$\xa9\xca\xe5\x8b.d\xd5\xd2\xd4\x96\xad\x8a\x92\x9bk\xa4\xf3\xf3\x06\xbf\x8b\x07(\xeep\x18\xe3\xc5\xdf:j\xf9\x8d\x8e1\xf1\xb6\xf0\xf9\xf3\x17\n)\xcc]\xb0(\xbf\x88\xd0\xc4\xc8\x8c\x8fg\x85\x10\xc3+r>r2w\xcd?\xb4\xc3J7\xca\xebM\x15\xf8\xf4\xea\xb6B\xbb\x90\x96N+\x14\xa2\xf2 \xb6\xf9\xc7/\n\xf3k]\x1c\xb6\xb1_5\xbf5\x0fuo\xb1\xe8\x99aV\x1b\xc1\x8f\xb3\xea\x8eE\xe4\xd29F\xb3\xa0\xa0c\x89\x1c\xe3*\xc8\xee \xb3\x11\x01\x0f=\xbc\xb4\xa1\xcc\x0c\xb5\xfa\xfcE\x93+\xa1\x8b\x81*\xe8\"w\xa4,rE\xe8\x12\xc3\xd7\xc1_\xb3\x0b\xb0\x84\xac\xdc\xa7)D \x81\x93\xbf\xe6\x8d,\x85sx\xb8\xceH\x0fAIU=\xd4\x85>>\\\xc0\x19+\xa8\xae\xf2\x00\xb6\xe5\xc5\xd7\x85_4\x84\xed!\xa4\xd9i\x85_\x08\x93?'\x8bh9\x04\x93]\xd2k7Q1\x91|\x9a,S\x0e1\xa6\\\xde\xa5\xb5u\xd2uU\xc4E\xca\x93G\xfd\xfd;Z\x1cJ\xb2\xadu>m\x91\xb1\xcf\x1b\xd6N\xdaN\xf2\xdb\xed\xd7R\xf4^\x06w\x91[\xb257\xfe\xcb9\"\xf3u \xce\x94\xbc$g\x18\\\xa0\xda6\xd8.\xcf\xc0)\x96\xd3\xa7\xb9\x82\xee|0\x02\x03\xca\xab\x83\xd7\xdcL\xaef\x9f\xe7~\xee\xed\x8c*\x9c\xd3|\xab\xb9\x00\xd0\x01\xaeC`\x9ec\xdc0\xb8\x99n\xda\xaa\x81\xcc\x15!\xa8\x05\x0d\xf3\xd1\xa74T\x93\xc7O\xb2\x08\xce\xc9\x98\xa4\xa3FF\xacWt:\"\x1c\x0f\x89\x1c@\x9a%\x97\xe2A~\x8c\x8e\xe4u\x0b\x10>.k\xf4v\xdd\xd8\x19TC\xb6\xf6\xd7\xb6\x80\xceH\x9c\xf7\x161\x0f\xda\x0dY[Xj\x96\n\\\xd2T\xc3\xea@\x11\x9b\x01\xd1\xc4\x82b\xef?\x9a\x8d\x17\xbc\xd8P\xa8\xd7$\x1e\x8f\xc9\xcc:\xc1/|\x84\xe7\x18\x1d6]\x82\xa7\xe7&\xa1%\xfa\xc0\x18J\x04wSxjou\xe6}\xd6\xc1\xd4;\"\xd7zF1\x06\xaa\xd6%T\xe6\xd8\xa2K\xbb\x15\nk6 m3\x8c{\xef\xf6\x98\xd6\xb6\xcb*\xb4\xf8@\xc3\x97\x02\xef\xb0\xdd\xd7\xd6qv02P\xa2\x90Y\x01\xe7A\xad\xfco\x963h\xdf\xfd\xff*\x8c\xa1\xb1\xed\x7f\x13|\xe1\xd9\xd3\x0elAg\xfa[p\x85g\x0d\xee0\xdb\x98\xc2\xc9\x95\xae\xe7\xef\x8e-4\xf5&\xe7\n\xad9\x8e`\n\x1a\x0b\x1f\xce\x13t\x05\xff` \x9dX\x82\x1f\xa5\x7fc\x96\xa0Z\xfc\x07K\xa8\xfcZX\xc2\x8b\x06w\xc3\x7f\x0b\x96\xd0\xd8\xf6\xbf \x96\xa0\xdd\x9e\xb5\xb3\x04\x9d\xe9o\xc1\x12tS\xffNXBSor\x96\xd0\x9a\xe3\x08\x96\xf0b\xfa\x81,AW\xf0\x0f\x96\xd0\x89%\x84\x94\xdf\xfc\x8dy\x024\xf9o\x8c)\xd8\xe46\xd3 \xb3f\x89\x0d\x00\xc50\x00\x14\xa8\xfaT\xea\x8b\xe76\xf5\xf33\x9b\x8a\x9e\xe9X\xd53\xdd\xd1Q\xb9\n\xfeR\xeb\x03\x9b\xa1-}-=mH\x0fZY\x98\xe7Z\xc6\xc2u4\x85\x97\x0c\x1a\xc8\xbb\xc8\xc9;\xeaZ\x03\x18\x89j6\x8a\xa1\x95=\x97\xaaU\x0f:\xdc\x16\x81\xd2`5\x0f\xf7\x9a\xfa\xa8\x10\x1e\xeb\xab\xa7\xcf\xc85\x8c\x02\xf4x\xaa\xf0\xe3i!\x9a\x1f\xb6\xee\x80\x91\x16U\x10H%bt;o\xda\xd1\xd5D\x85\x1c\x91u\xe1\x0c9>G\xa7\xb0\x1e\xc0\xc7\xfb\xda[\xad\xad\x80\xf7\xe3\xdc\x15\xf3\xc9t\xa0\xd0\xbc\xbe|<\x1a\xc1J\x9d\x91\xcc1!4\xc25\xe5t\x07\xbff\x81\x1f\xa63\xe27\x10\x97\x07\xd8Z\xe4RO\xf5\xdap+\xe2l\x9a\x0f\xce\x12\x17Nm\x06uF\xa9C*&\xb0\x01\xc0\xb1O>@\\\xfb\xbb\xdcW>z\x84\xfd\xd3s\xa4\xbax]7\xb7\xb0\x01\x05\x90\xad\xa3C\xea\xd3\xfe\x1b9\x7f\xb3X,\x07\xfd\xc5b\xb1\x18\x00\x83>9\xcc\xf9U\xb6(?K\xd5\xb1\xf8\x80\xcc\x18s\x08\xe3\xdc\xd4\xde\x07}p\xfc\xe1\xc0O\x9du\xe0\x87+2_\x0e\xcc\xee\xac\xfe\xbd\xe0V\xd4E\x0e\xe2\xc3\xe8Xv\x0cR\xa7\xcb\xeb\x87\x84\x8d\xac\xac\x1b\xdc=\xd6\x1c\xa1\xba\x17S\xbd\x93s\x7f\xa9\x06\xaf\xde\x03\xa8p\x96W\x9d&\xb8\x9d\xa9H\xfe\x95%ZXCqm\x07\x90\xd9\x08x\x1fc1\x1d\xbbhJa/\x9b\x17M\xcbU\x1d\xc5\xba\x9e\x92\x97\x07\x8c\\N\x1c\xf8ZM\x83 \xd6\xad\xb54EGo\xb9\x16\xd4\xa60\xc8~9K#k\xa7\x93\xe5v:\xf4\x82\xf0\xe3\xa3\xa3\xf3\xc3\x81\xd7\xa6\x0d\x02}\x87\xa2M\x81\xd5y\xf7\xc0\xeahG\x04\xfd\xd4\xe4\x8e\xab\xe1B\xd7\x8a}\xae\x96cT\x11k2\xe3\x05\x10\x05#-\x12\xe1\x1c5\xc65\x8f\x96\xcd\xe4\xaf\x1bMk\xaf\xfc\x12D9\xad\xaah%|\x0e\x82\x11\xbb \x86\x8e\x98\x1e\xb9\xb4\x08Y$f\xe4\xacN8\xda`\x84\xa8\xcd3\xe2\x82\xb1\x94\xb1\x99~\xcf\xe3\xe5\x04\xdan\xec\x08~\xd6\xd2\xc7\x87R\xf2\xd8\xc1\x80\xb3\xd57\x0f\xa0\xf1\x05\"\xcaK\x04\x94~\xc4\xc0\xe4\x05Y\xe4\xecY\xd5u\x99\xd1\x99|\xe6\xd0\x99\x14\xe2\x8a\x9e\x8d?\x9f\x9c\x80\xf2\xf4\xc9pqzum\x15\xa6\xc3\xdf\xe49\x96\xfd\xebY\xfe6^\xfe|6z1}_\xf8>\xb8\xee_\xcf\x16\x93\xa3J\x0c\x9e\x0c^\x9e\xd6\xf56\x05\xd8&\x8b\xf1\xf2\xe7\xe9\xe8\xfc\xf9\xfb\xc1\xac?\x7fs\xf9rqwv6^\xdc\x9d\x9f-U\xd9\x87\xf3\x91\x92n\xa7U\xc2z\xd1\xa8}\xd0\xd4\xa3_\xa5\x16\x9b\xa2\x13\xaa\x97\xbd\x82(\x04\xaa\x90H\xab\x0f)\xb8\xab?\xe9s\x9b9\xab\xc5\xa1,\x94U\xbb\xa1l~\xb6\xd4\x8dL\xf5\xd5~\x0f\xac\x08\x02\xb5\xe7:\xb1\x02C\xd1/W?(\x8ba\x1dd\xef\xd6\xfd\xc3\xc1]Be\x1d\x1c^\x96\x02|\xe69(\x8e\xd6[\xba\xc2S\xb2\xaa\xe3\xc3\xa3[\xed\xb2\xcb8\xb0\xb2\x87zF\xf2[\x98\x03E\xedN04i\x94\x874\xb5\x13\x986M`/\xa4~ b \x87m\x93\xe9\xfdc2K\xbf\x8f:\x99iu2?\x0e\x91.\xd2\xa6y\xcf\x8b1N\xe7:\xf6\xeb\x8e\xe8(\xa5\xfa\x0fD\xe6\xa4\xab\x18CwR\x0f\x0b\x99?>\x04\xd6\xf48\xfe\x05\xb7u\xf0\x17#\x94\xfa\x18\xffs\x0d>\x1d\xads\xbb\x8d\x80\xb2[\x16\xc3\x1f\xfdo\xb2\xd3\xd1E\x9f\x9ec\x04R\x81\xd9\xd4_(\xee\xd3;\xf8\xa3\x9b\xf6C\xfcW\xbfE\x1b\xa8\xc7O\xf0\x95\xfb\xa9\xf9;Y1f\x13'w\x89W|\xces\x05\xb7\xef\xd4s\xb0\xc6\nq\x19\xc0\x13\xf6-Lyb\xfeB\xa9P\xfc\x84 Y\xa2V\x85z\x8c\xd8-|\x8a6\xf8\xc7\xc7\x7f!\x16i\x14a\x7f\xe2\x84\xfe\x94\xb1 \xf6n`+\xa4\x92\x92\xd8DD\x85b\\\xa4\xf0\x9e2\xbe\xf7=\x86\x8fij\xe2\xa1\x9a\x81I}\xb6\xc7\x8f\xbe~G\xb8\xd2\x10\xffD!&\xc74\xb1C`_ \x0b\xfa\x84\xec p\xca\xa9\xfeD\x188V\xe8\x19\x12;?\x0dY\x9a\x82\x06\x8a\xf4D\xf4\xf4\xfc\xd33x\xc2\x16\x05\xccr\xc6\x01\xae=\x0bC\xe8/\x0e\xc1-\x86t\xbd\xf3\x10j\xf5w\x9c\xa5L#\xca]\x18\xf0\xc4\xb3`\x15^\xb1T\x88\xd3\xf8\xee\xe9\xe7\x93\xe7g<\x7fDd\\\xfbYx'8b\xe8&\xc1?\xf8 \xb1\x82j$\x16\x82z\xbb\x90E\xf8v\xab\xfe]\xb1tG1\xf4\xec\xca\x17^\xeccX\xde8\x80\xb9\xf6h\xa0g\xdd\xdb\xf1\x18\x83\xda\xe2\xd3\x98\xdd \x16\xa566o8f{\x16\x89\x15\xf7\x05\x1bS!X\xb4f\x98\x1d \x0c<\xee\x01\xa8u\x10\xd1q\x12\xd0\xfb\xd4\x8f\xb6\xda\xbf\xa3IR\xb9\xa9\x1f!\xea\xaf\x05T\xbe\xde\xaf\xd4\x1f\xb6>\xbfQ\x7f7\xd4c\xc2GX6\xcc\x84\xf9\x8d\xb6:\x84\xaf\x9f\x02zma*\xb7\xbe\xc0?\xef\xc28\xe1\xb1 \xc0\xbb\x154\x80\xbav\x1e\xae\x04=+~\x82\x7f\xb8^\x13\xde\x0b\xfd\x17\x97\x85@L\xfa\x91BK?\xe2\xdb\x0d\xbbO(\x16\x08h*60\xe0j\xd5\xe0\xa2\xa0[\x8dD\xa1M\xe17:%G\xa5\x10\xeb\n\xd3\xf1\x8e\x05zYE8wa\x16\xea8\xbf\xe1\x1e\xa0\x03\x19[=\xc4\x88; \x0dB\xfc\x9bPN\xdf\xbd\x03\xa4K\x02*L4\xe3\x84\xc7w\x10\x1f8I\xef\x01\xce\x9f2\xc6!\xc1,0\x96\xc6\x19\xc7\x95\xc5\x11iyz\x1fA^.\xf4\xb2a^\x1c\xad\x03\x7f\x83KL\xaf\x88t\x8bk\xf0\xe6>\xc1\xf4\x10\xa6*\x8d\x835\xc5\xc0\xc5I,\xfc\x0d4\x96\xe2\xc4\xa4\x82Q\x00+\xc5\xee\xa8\xd74\x01\xc7)\xb0\xc2\xa2-\xc0\x94\xad\xa1\x81,\xe2\x8c\xc2r\xcc\xc4\xf9\xd9\x19DaVx\xc6}D\xd0\xbd\xcfn\xc79\xf4\xb7l\xe5a\xf6[Aq\xf5\xdd{\xfe\xed= \xc3\xdd\xc6GD\xbf\xe3\xf0\xe9>L\xb7\xbc\xb7|8\xff( \xf9\x9f\x0e&\xbf\x7f\xfd\xea\xdb\xb7\xaf\xbf\xf8\xe7\xb7\xdf\x7f\xf5p\x01\xb8\xa2Eq+\x17+A\xf8I~CE+^\xc8Ic0}\n\xc7\x1aE3\x05\x14\x97\x9f\xea;\x8dN\x97\x0e\x06\x17\xa7\x15\x8d\\\x8a\xe5@u\x04\x98\xac3?\x9d\xbeW\x99\x1f\xce*\x8b\x97v\x1c\x04\xab\xc0\x0f\xeb\xfa\xf8\xa7\x9f\xb9\xb9\xa3w(Z8\xde8\xdd\xb8/\xa9<}\xee\xd6Iy\x9a}\xbai\xa6\xbf1f(9\x93\xf1\x0c'+\x1cI\xa0rA\xf1\xe7\xde\x1dF\xaa \xe6\xd3\xa5b %\xdd\x14\xb9&\xa0\xa1\xf8&\x12}\x95\xc1\xe85\x06#2}\x01\x01\xd6\x8b_Gd\x8aa\xb6\n\x97\x81\xfc~\xa4j\xa1}\xa0\xcc\xb4\xff\xe2\xf9\xf3\xa7OK;\xf2\xa0\xcc\xb6\xea\xc4\x1am6\xc0p\xa8\xb1k)2\xe9X\xf1\x01\x05J\xb5\xa7%\x98\xf8\\eY\xb6\x00\xe1\x14\x95\\\x0e\xec\x1e\xfd\xc2\xfe\xeb\xca\xb3\xac\x05\xb5\x99c\xf2\x95\xe0\xe1\xf6[v\xa7>\xfd1k\x88\xca\x01\x07*iC\xc4\x0e\x1am\xbf\xe3l\xe3\xdf\xcd\xd4\x8e$\xdaft\xcb\xc6.\xed\x8b\x1f\xdd\xf8\x9b\xfb\xc6\xf8*7\xaf)\xdf21sJ\x03\xe2>\x89!\xa8\x08\xe3\xee\n\x809\xa63\xd2\xfb\xeb_\xfe\xcf\xbf\xfe\xe5\xff\xfa\xeb_\xfe\x8f\xbf\xfe\xe5\xbf\xb8\xd4]\xfev\x17`\xfc\x91(\x0b\x1cJ\xa8\xfc\x8clF\xce\xab\xa7\x1c\xa5W/\x0e\x938b\x91p\x8e\xb5\x17s\xe6JW?\x9e\x05\x10\x8a\xa5\x07\x9e\xe4z\xa3<\xea\x8b\xda\x1c\x19+\x19|\x03\xc9E1\"x\xd7\x83\x88{\x1f\xca\x05v\xbb^\x8e\xaeV\xfc\\=\xd8\xa3\x0eA\xfd\xa0\xe7\x08\x83\xe8\x98mto\xd7\x05th\xbe72\xce\xf7\xd4\x06\xd9@`\x1aV\xcf;F\xd7\xc8 {;T2\x890\xb0}\x0f\n\x9fu\x90\xbeB\xd0\xa6\x91\x8e\xa5\xdb\x0dv\x1c\xc7\x83\xc0\x17\x02w\x94b\xa7\xe8\x00)\xc5\x00&y\\\x8e<\x14K5FH!\xc2\x87\x0dHR\x08\xef\x82\xbaP\x07\xfc\xbfr\xbf\xfd\x83,\x14?\xfe\xbb$\x0b-\xcb\xae\x0d\xab\xff\xce0\xc6q\x1d\xbe\x801\x8e\xaf\xff\xc0\x18\xf8=\x04cj\xe9\xe4(F\x82\x0c\xa1\x13\x0d\xfd8\xf4\xffCh~'0?\x94\xd4\x1f\xa2\xf1\xff\n4\x1d\xb6]\xf9\xd2\xe4\xc5}IU\x98w\xaffS\x0b\x83#&jf\x1e\xfez<\x8e\xeeQ?\xbf^s\x86\x07\x04\x943\xcc\xc5\x85\xef\xa1\xde\x97\xa6>N&\xcd\xd6>h=A\xc9\xbaZ\xfb\xf8\x07\x93|\x18\x99\x95\x1d\xda\x12:\xac\xe25\x8c&\xb6\xbc\xca\x84\xd0z{\x1a\xed\xf1D\xcb\xa3\x890\xca|\x16 T\xa6{~\x19\x9b\xbc8\xd0\x7f\xb6<\xce\xf0\xc4+W\xef\xe7\xa7]\x82\x1a\x1cZ\xe39\x18\xf3bNE\x8cZ}d\xe9k\xa6$ d\xf2\x1b\xd4\xf3\xfb\xf8\xdd\xc7\xc32\xcc\x05\xb5\xb0\x80\x99S\x0b\x06\x03\xb6\xf1Y\xb0N\x99\x8e\x11\xb5-\x00\xbf\xf1\xb7\x19\xd72\x01\x96P\xb2\x81>\x1b\xd0\n\xf1\xdd\x14\xfe\x05yl\x87\x87k\xa0X\xde=\x87\x7fA\xe9\xaf\xd6\x83\xf9\xab\x0f\xe2l\x9f\xf3\xf5\xa3\xfe\xc2,\xf8!\x0c\xbf\x1f%x.\x88a\xdbz7+\xa8\x04\xacw\xe0\x81mY\x84IP,\xa4x\xde\x12\x9aC6\x08\xe5\xa6\xfe\xfe\x94\xe1\xf1I\xc8\xa2\xcc\xfc\xf5\x05\xf6>d\xbaC\x11\x9e+F1\xce+\xceN\x9c\x08\x0bil\xc7%\xce\x84\x06\xcd\x9c\xad\xe1\x9fxk0\xef'\xf5\x0f\x9e\xe9q\xc8\xc8\xb3\x15\n\xb6\xf0\x0f\xb5\xe7\x00\xa6\xca\x94\x05\xfa<%\xdd\xd1u\x0c\xc7IiH\x03\x80\"\xd7\xc9\xa7 \xf5\x10\xdc4\xa1XPp\xff\x86\xe9\xa7\x18\x89N*\xee\x11\xdb1\x08]/\xcd\xc2\x90\xe2)\x05\x06\x9d\xd3R\xa7z0\xd8,`$\x05\x0b\x93@\x1f8*\"`V\x90P\x13\x0f\x0f(\xb4\x9a\x195gG\x82\xe3\xbf\x14)\xa0\x80\xbc0\xd6\x19\xf4`\x8f\xc7<{\x7f\x8d\x07\xb3\xb7+\xdes\x04\x8a\x03\xa3\xb0^\xba\x87^\xe0\xd2\x0d\xc46\xb8GQ\xd9<\xafQ.5\xaff&i\xe4\x87T0/\x0epm\xe8\xf706c\xac\x13\x04\xa7Qj\xd0\xd7\x92\x81\xc2\xea\xf5\xb9&\x16^\xe0' \xc5.\xaf\xd9F\x0b\xd1)\x9c\xe5\xb0 \xf0\x93\x14\x17\x87\x1f\xd8E\x81\xcb\x04\xcf\xcb\x0c\xdc\xf0`\x84\xe9\x1b\x86G\x9a\xda\xf6\x1e\xe8\xaf\xfdK\xf9\x96\xd3\xb5\xaf\x97'\x9cnq|J\x11\x97\x99\xa0\x862\x84\x06\xb2\xc2_\xa1+O\xe2\xe0~\x1b\xdbG\xcb5\xe9\xda\xa7A\xb1 n\x90N\xe01q\x8e9\x10\x01\n\x9e\xee\xc3U\xac\x0fq\xef\x84\xf9k\x1a\x05\xabzx\xd0\x1d\x14\x061\xed\\\xef}\x06\xe8\xbc\x87\xae;f=\x82Y\xdf\xb0\xdf\x06z=o\xd8\x97j\x12_Q\xc1\xfd;\x93\xa0\xc5\x88\xd70{z\xb819\xd5\x94U\xbdF\xfb8\xd8\xb3b\xc9\xdf\xf9\x9bM\x96\xb2o\x958\xa3\x99\xb2JL\xed\xde\xf3\x15\xd2\x0bH\x144\x12\x90\x13S\xbe\x0e\xe2XC\xf4u\x16y_\xe4\x8f\xbf\xcd\x1f\xff9\x7f\xfc\x1e\x1f\xff\x99fi\xea\xd3\xe8\xb7A\xa6\xe1|\xc5\xf8\x96\x15\x1e\xff`E\x8aW1Ovq\x10o\xef\xf1\xfd\x8f\x9b\x8d\xa1\xc5\xa87,\x80\xf3C\xc2\xbc,\xa0\xbc\xdc\x97\x1f\x92\xb8\x98\xe9\xb5\xb1\x84`\xaf3\xbe\xca\x02%\xb4\xb8F\x1d\"r\xf4B=\x8f!\x8b\xb4e\x89z\xe6\x1c\x97P\x08\"\x0f\x9a(l8\x05\xc4\x0f-^\xe3\xe9f\x08\x04\x99\xad\x91\x04\x84a\x16\xf8h\xea\x81\xa7\xb0H\x92\xd1\xd8!\xdektN\xe8z\xad\xabMv4\x121\x92b\xae\x89L\xc8\x91\x00\xea\x83\xdc\x04\xa8\x1e&\xfc\x84\xe44\xbc\xb7\x98\x1aj\"\x17j\xd2\xa6\xde\xcd\xa3%s!\x92\xb7\xd0\xa0p\xa8\xa1\xcd\"\xcd\x90\xf0 \x00t\x8cU\x0cc\xf5k\x14\x8b\x1c\xd2\x1a\n$\x9e\xc7\xb4m\x80%\xeb4\xf0\xb7\xfa\x01\xbfd\"V\x12q\xc0\xb4,A\xbd\x1b\xc5`\x10\xefW[K\xbcV1\xd7\x90y,\x08\xd4x\xe9\xf9V\xafj<\xcc\xeb\x8ey78\x94V\xc0\x08(2!/`Hvm\xad^\x8cB\x82\xfa\xab\x97\xa9\x17\xc7|\x8d\x89\x9a:A3\x8a!\x8cW4e\x86g\xd2\xd436>\xe6L\xcf \x84M00\xd3w~\x98!`\xaa\x8a\x8d\x9a \x16y\xf7&A\xd59Nw\xfe\x06\xea[1\xbd\xd2V>\n\x1e(!\x16\x96/ZB\xa9\xbfc\xc3o\xe1E\xed\xffz\x95u\x1d\xf3\xb1Z <\x89\x03j7\x1f\xf5\xe41\n+i\xfe9\xe1\xb11\x9e\xc3\x04\xce\x14)4\xf4\x05f\x07\xbb\x80\x8b\x1d\x12Pf\\#k\xf5\xe2\x08\x18'&\xf1\\\xa8]\x03\x97\xd5Y\xf7~\xaa\xf7,\xc8\x14\xd9z\xcbB\xcd\x06Y\xc0\xf6\x16j#\x04\xf8(\xfc\xaa\xbf\xe3XQ<\\\xf9\xf0nF\xa0 z)V=\xb6#\x82\xaf\xc5bq$\xc6\x1b\x1a\xfaA\xfejP\xdb\xbe\x8c\xe9\xfa\xc7,\x15y\x9a\xe0L\x8bA\xfa]c1\xbc\xed)\xf7i\x94\xe7\xbe\xb5h\xb6A\xd9\x03Z\xda\xc2\x06i\x0b\x1b$`\x9dc\x83?E\xb9\xd0\x08eY\xe4#\xe34 %i\xb5@8u9M\x1a\x950Y\x9e8D-?\x82va\x99\xdf\x00 7\x98\x00;\xb5\x1b\xd8\xa9)\xb1L\x17\xbaa\xf7\x89\x929R\xfd\x92&\x10X]\xbf)n\x00\xcf\x96\xd4\x02%\xcd\xc7,`\x8a\xd6\x8d\x0b\xecI\xd5\xcd\x82\xd0\x8ac\xf8\xae:\x99S\xe1@K3\xf9\xe4\x05\xb16P\x1c\xb3\x84\xef\xbc\x1d\x8d\"\x16\xa0\x00\x84=\xbdw\xa4Asw\xd0\x8f;\xe8\x07\xca\x1f*7\xfc\x03_\xee\xe1\x0b\x18|\xbf\x8b\xe3\x90Fk%09d\x94\xac \xa3\xf4P8\x81U\xaa\x97\xb4\x15{Vl\xcf\x02-k\xdbM\x9a\x17\x07Y\x18\xa56\x13\xbe[r\xad?kQm\xcd\xa28\xb4Y\xd7,\xd1:\x0d+\xcb\xe7l\x1a\x1es>\x07\xbbG\xf5\xc05ykbA\x81\xc2\x1f-q\x17H{\xc4\xc4\xce\xf7n\"\xad\x17\x0b\xecV.\xb0\xfaT\xb5\x05-\xef\x83T\x8a]g\xea\xc50j\xf5\\\xe0\xba!\xbd\xb3_\xfc\xc8>\xc6{\xb55\x81U\x03\x8dFqNL\xa3,\x1f\x07#\xad\xf3\xf8\xd6\xa6\xf1\xf8\xd6\x8e!\n\xcc\x06w\n\xe23\xb7\xbd\xe0\xb6\x17\xb8\xe7\x05\x03\xc5\xfc\xb5\x00\x95\xde\x13\xfb\xef\x98\xde[\xf8Z\x8f\x07\xe8e\xb5\x80 \xb5L\xc2\xbeh\xe2\x03\xa2\x88V\xe2\xe9 \xffV\x96L\xb3\xa4\x9ar\x1f\x86Lp\x1f\xe4\xf1}N}\x0e\x8b\xcex\x83\xe3.\xf0\xa3\x9b\x99\x99\xe3\xbb0\x98i\xebzH\xb7\xe2\xba\xfa`G\x03\xaa\x9cA\x8e\xde\xb2`?I\x8a&\x8f\x81\xd3\n\x89T#7\x9b\xab\x9d\x17$\x1a\x8f/\x06\xa8\xe8\x8c\xb6=ru\x05\xa6\xa6\xf1\x86\x88\xb9\xb9}:\x87[\x98\xeaO\xe5f\xd9\x88\xb0\xb9J^6x\xdf2\xa6\x9b\x95\x83\x0d7\xe4^\xbb-\xae\xebp\x93h\xf5\x16^\xa6\xad\xb7\xaf\xbdc\xfb\x11a\x03\xf2\xc7\xd5\x8f\xcc\x13\x85\xf0\xf2;\x9a\xfe\xf16\xfa\x8e+\xd1A\xdcO<\x1a\xc0\xe0i\xcf\xd1\xba\xd7l\x1e-\x1d\x9eT\x8c\xc9N\xc3\x91\x0d\xd1\x80o\xc0\xbb\xdc\xcf\x8b\x9f\xe7\x8bt\xf1\xc3\xf2\x89\xd4\x7f\x17\xef\x17\xefO\xb7a\xbdG\x89*p\xf9O\x95\xec\xff\xf4\xd2\x99y\x0d\xd6jk*\xe8x\xbe\x18/n'\x8b\xec\xec\xec\xb7\x9f\x8e\x17\xd9\xd7_\x7f\xfd\xf5\xf2\xd4q\xf2\x08%\xd4\x12\xc7\x12\xcb\xe1'\x8e\\{\xc8\xd5\xbf\x9e\xe1\xff\x1b\xb9\x13\x03\x91\xa4\xd7\x12o\xd6H\xc1\x02\x89\xd7-\xa4\xe7\xaf\xe5]\x98$\x83\x99\x9c\xbf\xa1\xe3wK9\xa7\xe3w\xc3\xc9b\xbc\x1c\xf6\xafg\x90\xa6\xdefK\xf9\xc9`P5\xb7#\xda\xb3\x154\xb6\xb8\x1d\xe2\"\x93`\x829se\xde\xaa\xccs\xd5\xcd\xb3\xb3\xb1\xfas~\xa6\xfe\xfd\xe2l\x91M_|\xa6\xfe\xfd\xec\xec\xabEv\x8e\x9f\xcf\xcf\xce?W\xff>\xdf,\xb2\xa7ggg\xcb\xd3m\xbd\xca{rEz\x06 \x8b\xf8\xff\x03hf\x15.\x18%m\xed\xe3D\xc9\x0f\x8a\x86\x90\xeb\x03\x16\xe5\xa4\x803XC\xdd\xa9\xee{2\xeb^\x0b\x03\xc0\xda\xe1f\x13\x10\xd1x\xa6\x18,\x18\xe1\x15\xbe\x81M\xa1\xee\x86]\x13\xe4:\xef\xec\xac\x05\xd2&\xea\xb3r\xc3\xedoH\xff\x0b%\xb5M\xfc\x14\xfe\xf6Y\xa3\x85\xa1%Sj\xd1\x9f\xe1=z]\xc6\x98\xb0_\x10\x01\x11\xe7\x0d \x13\xc3\xe1\x80Ds\x81\xebU,\xeb\xcb\x95\x14\xdc\xf5\xd5{\xd3\xb4\xba\x11\xe4\x0d\x8f\xc3vG\x80\n\xda\xb7m\x07\xae\x85:{J\x00\xd9\xf8\x11[\x17\xe7\xec\xd6\x8f\xd6\xf1-\xb9\x06{\x002\xd3\xef\xe5&\x9d6\x83v\xe4o\x9d\x8d*\xc8\xbe\"W\x84\xf2m\x06\x86`&\x92\xfcK\x8c\x0d_\xf0B`\xb3\xcc\xcf\x96\xe4\xba\xfc:#o\x9b\x02\x9a\xde\x95\x0c`\x9b&\x95\xe4\x10\xdfV\xc7\xd2\xfc\xde\xbb\xbd5\xdcM\xf6\x8c\xa7\xaa\x8bW\xa47\x9d\x9cM\xd4\xae\xfan\xc2Y\x18\xef\xd9Z\xc7\xbd>\xf9\n\x9ck|5Y\xc7\x1e\x80\xad^?\x87~\xe5i\x93(^\xb3\xd7\xf7 \xb3\xb6\x9bw\x13?\xfd!K\x92\x98\x0b\xa8\xead:\"wu0\xd4(\xfe@\x8aU\xb9\xc7\xe2\xcb\x06\xbf~\xeaw\xd3\xf2\xed\x8b\x0eu\xff\x11\xf2\xfcN\xe7\xf9\x9a\xd3ms\xde\xef \xef\xef_\xbf\xfa\xf6\xb5>p\xfc\nO\xa5\xdd\xd9_C\xf6?\xd4,\xad\xcd\xef\x95\xfd\xfe5\xe8\x83\xdc\xb9\xbe\xc1\\4dk\x95\xf5\x15M\xdc\xf9~\xb4\xfc\x1a(\xd27\xe4\xbaRLM\xddW\x93W\xf1;H\xfcB\x08\xae\x12g\xe4\x1bw}\x7f\x80v_\xb3\xbb\x86\xde}\x0f\xdf\xbfD\x8b|w\x96\xdf\xe1\xd8\xfe\xf1\xd5wp[\xda\x9d\xe9[\xc8\xf4?\xbf\xfa\xf6\xf7B$\xdf\xb3\x9f2\x966T\xf7\xa7r\x0f\xbf\x85\x1e\x96\x0b\x92\x19\xf9\xd6]\xf8'h\x86Ej\xff\xf6\xa7\xef\x1b\xfa\xfcu\xb9\x85\x9f\xa0\x05[\x86\xcc\xc8O\xee\xb5\xe4\xe4\x17\xdf5-Z\x85\xf6\xef\x14\xf5\xfd\xff\xd9\xfb\xda\xae\xb8m%\xe0\xef\xf7W\x0c~zR\xfb\xe05\x90\xa4\xb7\xed\x06\xc2!\xb0ii\x03\xe4\x02i\xdaK\xf3p\xcc\xaev\xd7\xc1k\xed\xe3\x17^z\xcb\x7f\x7f\x8eF\x92-\xdb\x92\xec%iz?\\\x7fHXk$K\xa3\x91\xe6E\xa3\x99`\x9c\x92\x8a\x88\xdc\xea\x18\xdb\x10\xc4\xff\x8f@\x98D\xd8\x16S\xfe\x08\xe8mBRI\xc1(c1\xc27\x94\xdb.\xd5\xc8\x87u\xf0\x15\xeb\xa0\x1eK\xbf\xc0\x0e\xbc\n\xa2\xc5\x92\xf7\x1b\x95\x14=\xe4\x8f\x08\xc9G\xc9\xa8\xf0P\xb0u=\xf4{\x84\x9e\x91\\ ${u\x7f\x1e\xce\x18\xb5\xea\xe1\x7fRZ\xef\xb7\x80\x7f\x83\x1d8c=\xa7in^\x97?\xa3T\xdc\x9e\x82\xe6\xae\xf6Kc\xa7\xffE\xf4\x85m\x10\xeat\xf0\xfdr\xaf\xdc\x88\x8e\xe8Ds\xf7\x8d!\xfd\x07\x8c\x8c\xa6\xed\xd4W\xb0\x03\x86\x95\xffo\xd8\x81\x89\xbe\xe8W\xd8\x81\xb9\xbe\xe8_\x18wM[D\x08\xec\x80F\xa4cON0(\xa0\xb6,aez\xcf;@F\x05;\x10\xbb\xffy\xf0\xe1\xe2\x03\xa3\xceq\x98\xbbW\x188\xeb\xca\xcd\xf1\xdf\x04\xffM\xf1_\xeay\x06\xdeH\xed\xdf\x89\xf4\xdf\x89\xb0\xd5\x10\xff-\xf0\xdf\xcc\xf8\x85\xd0\xfe\x85\xc2^\x9c\x11Cb\"\xc0[\x81\x96\xc21\xb1\xb0\xb3\xa9\xadpi+\x9c\xd8\n\xe7\xb6\xc2\x1b[\xe1\xc2V8\xb3\x15\xde\xdb\n\xafl\x18\xba\xb4\x15\xde\x12\x8bB;R\xc8\xa2r\xa0\x91.A\xd2\xa3\xa0\x8a\xf7PZ\x93T\xef\"\xe1\xe4\xc3\xbdD>\x98d7\xed\x97J\xcf\x12\xe1(V\xb9Gq\xa7\x1aSkg\xb5\xd6\xb8a\xb99}uh\xf8\x98R\xc6*\xb1\x97\x85ZI\xfb)\xa5LVB\xfaw\xde\x9d\x8d.\xdf\x9e\x9e\xbc>|3\x92\x9fz\xf2\x04\xa6\x81\xfa\xde\x17\x9b\x14\x0f\x82'\xfa}\xb9wz\xb8\x87\x0d\xfab\x9b\xaa\x17\x1f\xec\x9d\xcbb\xdc\xa8\xe4\xfbw\xc7?\x1f\x9f\xbc?f\x8d\x9f\x9f\xec\x9f\xbc9C\xa5a\xcb\xe7;\xd648\xdb{=\xba|}rz\xf9\xd3\xbf\xde\x8dN\x7f\x93\xa5\xcbF\xe9\xf9\xe8\xe8\xed\x9b\xbd\xf3QY}\xc2\x01\xde\xffx\xf2ftyp\xb2\xff\xeeht|.\x0b\x17\xbc\xf0tt\xfe\xee\xf4\xf8\xf2\xe0\xe4H\x16\xcc\x9a\x05\x97\xafO\xf7~P\xab\xde\xb7 \x0e\x8f\xde\x9e\x9c\x96\xe57\xbc\xfc\xf5\xc9\xe9\xfe\xe8\xf2\xd5\xc9A\xd9\xe3\xab\x1aR\xce\xf6\x8e\x0f\xcf\x0f\xff\xcd\xbav\xe4\x8b\x8dI\x96\xfd<\x1a\xbd\xbd\xdc?9>\x1f\x1d\x9f\xfb\x9ciV\xc4\xf1\xee\xf4\xf0\xf2t\xf4\xc3\xe8\xd7\xb7\xac\xe1\x9c *0\x0c\x11\x91i\xd5f\xfc\x05\xdfa7=\x9cZ\x0c\xecI\xb4\xbc\x0dy%\xa7OT\xdb\xf8Z\xb8%Uh\x80\xd8M\x88\x0f\x8c\xd7\xc6.%>D<\xb3\x97\x84\xcbnf\nX^\x82\x85\xe5_Y\xab\x02\xd7Z2\xa5^\xd2]\x8f\xed\xb3Gj\x97\xd2\x12\xb2P\xebx\xb8\x9a\x0e\xf8\xa2(\x87\xbe\xb3\xc3\xa4\x88\x12\x11c7!\x1e\xd6b-U\xf0UmF\xad\x08Oy\xed\x88\x94\xbf`\xecRQ\x9b\x12\x15\xbe\xaa\xcd&\n\xc9S6\x13\xbbgD[\xe8!\x01\xf0\x8e\x95.Wr\xee\xb8\x85\x94\x1b\x96RB\xfe \xb8*\xab\xb7\xc2\x82\xca\xcb\xdc\xa9\xe7\xf3\xadu\xaa\xdd\xfd\x0c\xdc\xed\x84\xf46\x18\x94J\xbe)&\x82\xfa\x08\xbf\xeb\xa1\xc6Z%\x9f\x07K\xce\xb1<\xbd\xb7\xf4\x04dv\x08\x92\xa0<.:\xb6?\x8f\xe2\x89\xc9\x9c\x01h\xd1\x1b\x87\xf9x\x8ey8\xbaZ\xa7ENR&\x92c\xe8rs\x93\xab \xfb-\xe9\xba\x9e\xac>\xdd8XiF\xd8S\xfa\xf0\x0c!g\x1a\xd3\x9e\xfc\xcd\xb0\xc8$\xea\xce\x16\xa6)]\x0c\x1bv\xf6\xe6\xf3\xd0c\x06\xac\x94\x06\x9f86\xb3p\xa1>\x9f:\x14\xf3\xc4\x89\xae\x97\xd85\x9a\xd8\xf4\x9d<\xef\xbf&\xa5a\x96K2\xf61\xdbNf\xe4\x13M\xc1\xbd\xe1\x1b\x12\xca\x04\xdb|$/\xb77\xc4\x1f\x0e\xac#7\xb8\xee\x9a\xbfn\xeae\x0f\xfb\xc8k\xdb\x92\x85&\xd1\x98\xd1\x0ej\xb4\x03r\x0b\xef\xcc\xc3dO\x1a\xa4$[\xd2$C\x1b$\x1b\xacT\xb4\x1d\x1f\xd2\x80.I\xe2:?\x8c\xce\x1dq/e\xc86\xe7\x0d\xc6\x18_\x8c\xe7a\x9a\x91|\xa7\xc8\xa7\x83\xef|D\x89/\xd2\x9a\x06\x19I&.#@\x8fGE\xa9>\xf3\x08Jb\xd3\xb1\xef\xf5\xc0%\xfb\x92\xcb\x06}\xe0\xf1\x18\x83\xafS\xba8\xc33D\xb6\xcf8e\xdf\x9d\x9ek\xd3\xdc\xa7\xf2v\xfc\x93'\x90\x97\xc6 !\xa8\xe3\x95y\x9e^\x94uIg\xdap\x1d\xc7\xf3\x82+:\xb9\xf7L[x\xa2\x16L\xa34\x93\xcdc1\x13\xc4k\xdb3\xa3\xc7\xf7\xfc\xbc0G\xe9oW\\\xb1\x81\xa1\xb8\xbf\xe4]l\xb6\xefw\x81\xde\xc8]7\xd70 \xd8v\x8c\x00\xca-\xads\xe2~\xbd\x9d\xdd\xcc^n\xcf\x80\xa2\x8f\xf0\x0e\x06~k\x0f\xd3\xf5\x9c\x97\xdb\x1b\xb3\x97\xdb\x1b\x0c\xfck\x03#$\x01\x86\xdb:\x13.\x19.j\x91\x18\x82\xc9\xbd\xe62\x82\xbe\x9e\x9d\\\xdczW\x97/\xb7Qo{\xb9\x1d-f\x90\xa5\xe3\x1dg{\xa3\xf1\xe6\x0eh\x82^\xf2;aL\xd2\xdc\xdd\xf266\x9c\x97_{\x9e\xa6\x83\xc0\xd4T\xae7\xed\xf3N\xea\x11o'\xb6\x07W36\x86\xe7\xa3\xfe{\xa3 \xd4\x1f\xc5Ir\xc3\xde\xf9\xe7\x9fl\xd1\x12\x1f\x8e\x82\xb3\x1fO\xde_\x8e\xde\x8c\xb8\xac/_\xec\x9f\x1c\xd5_\x9c\x8f~=\xf7\xbb\xa9\xa1\xf1\xf9\xa3\xe0\xf5\xe1\x9b\xf3\xd1\xe9\xe5\xde\xfe\xfe\xe8\xed\xb9y\xf5\xd5s.\xd5\x8b\xb4\xaf\x0fWFE\xa9\xfd\xee4\xb4\xdfs\x8d\xf6{\x8e\xb1l D\xe8U6&t\n\xe70\x14\x07\x9d\xa6\x86\x88\xa6!\xc2\xd5h')\x16W$UM\xdd\xa4<\x02\xe2\xc7\xba-\x9f\x07\x0ep\x1c.\x0c)O\xf5\x88\xf9\xd8\x12\xb3\x1a\x973\x9b\xcf\xcf\x17\x04]+\xd8\xff\xc1\x94\xa6\xa3pN<\x95\x0c\x8eQ\xfdT\xdf\x9cb\xe8/\x8d\xcfJ9\x7f\x86 \xce\x03\xc6\x99\xf6\xab\xe3 \xed\x91H\xaer\x07\xcewJi/S\xfb\xf1\xb1\xb3\x89R&\xb3@f\x8a`\\\x05\x969\xe1\xb9\x1al\xf9\x7f\xa5\xf4Q\x91m\xddA\xa7{J\x8a%M\x1a\x13\xc2\xe7\xa3\x83\xfd\xf3\xf3\x8e!\x18\x8eH\xe4\x13\xc61\xbd%\x93\xf3p\x96\x0d!\xb1\xa9f>\xac%\xe4\"\xfd\x80\x01\xff\xd8\x1f]\x8b\x80\x8d\x80\xab\xb2k#\xach\xc2/ \xa2$#i\xbe7\xf9\x18\x8eI\x923&\xdeG\xc4\x01\\i\xed\xba\xae\xb37\xcdI:Bg:\x06\x90p\xc1\xe0\xb3\xc9\x94\xcd\xf97c\xadk\xff]\x9b\x12\x1eT\xb0%\xd3\xf0\xd7\xca1]\xf9C\x0f\xbb\xb6\xb1\xbd1\x0br\x92\xe5.Q\x97\x10\x97\x0eV\xd2\x9d*M=\x18\xc74\xe1\xaa\xa0m\x03\xaba\x99'9\xa9:P\x06\xe8c\x1d\xf4\xc1y\x12\xe7/\x1c\xcf\x93\xa6*\x99\xeaA\xdd\xf7\xb9\xb8X\xfeS\x1fO\xd9\xde\x0f>8\xc0$G\xf9\xe2+\xfe\xc2\xafW\xa8\x82J~\x01,\xa8\xdf\xdd\x81\x84\x0d\x93-\xe2\x90\xd1\xa3}[\xddZ\x85\x0b\x9c\xae\xc8\x05V\xd6\x07\xedpiO8\xda\x13.\xea \x17\xf6\x84+\x1e\xcd\xf2\xca]\xbe>;<\x82j\xc5a\xba\xb6>\x86\xf4v\xcc\x15\xdd\xc3\xda\xe4\x1b\xb5.\xa0\x89\x0e\xfa\x970.z\x82_\x13\xb2d#\xd2\xc7ki>\x82\x15T(\x18\x0253\x04\xd0\xebJ\xea\x83\x8ebl.\xc2\xd2\x11\xac@_\xd6n\xb4\xc8\xec\x92(k\x84\x17\xc5\x07/H\xc2\x05\xf1\x91\xf4\xf2\x00\x0f\x98\x82<\x8d\x16\xae\xe7\xf3\xa0\x85u\xbe\xeaC\x16H\xd4\xf2\x04P\xfc7\"\x8f'\xeb\xc8\x02\x89\x1e\x91J\xb3\xc9m\xf7\x94\x18\x96hJ\xe6_W\x1a\x92\x07d\xb8\x85Q\xe4o\x87G?8\xca\x8e&\x05\x9d0\x88&\x1e\xd29\xfb\x8b\x13\x14w^\xab\xbc]1\xa0]\x10.\x97\xf1=\x1e.\xbf%.?\x8e#\xfcG\xc2\xff\n\xcbL\x12\x91\x07/\xa1\xe0\xbcA\x95PD\xb5\x88\xa3\xc9\"c\xc8\xc7\x90\x12Q\xf7\xa0\x93\xca\xe1\xf1\xdbw\xe7\xbaa\xf2\xbb\x0e\n:\xf0f\x1d\xb7\xb6\x0bs\xf9\x05E b\xad`\x7fy\x1eF\xc5\x8d\x92B\xe3\xc7\xa0{\xd8\xc8\xb0\xb9D3\xec\xc4\x07\xc7Qp\xd5\xd9\xa2\x9d\xcb\x83\x18\xaeB(\x18)\xf8\nY6\xf6d\xad\x1c(\xa7\x03\xfe\x9b\x0d\xcfM!J`\x8f\xfd\x8d\x7f]\x13\xcf\xe8P\xd9|\xd8G\x05#d\x04\x87\xff\xa4\x9dl\xcf\xc3\xa3\xb6'O\xe0\xdf\\\n\xa0^\x8f\x99\x079\xfb8P\xac\xfe\xebc\xaa\xf7\x1b\x18\x88\xc1\xad\x95d\xc0\xa9`E\"\x00\xd1\xcc\x19V\xee_\xa7\x1chN\xf8\x18+\xa4\x12\x82\xb4\xd3w\xcc\xa0\xb6\x86\x97~\x15RPn\x0eT\x04\xc1\x1d{\xaa,0\xdc\x80\xc8m7kw\xe4\xc2\xa4 |\xe8\xa6b\xf5\xc1\xb0\xa2\\\xe6\xfe\xd7g\x18#\xa8\xe3L\xaby\xea\xd5@\xf7\xea\x82N\xd3T\xf3i\xaf\xf8\xd4\xf3\xd5\x93\x01\xba\xb4\xc8h\xea\xb3\x82\xb8\x0f\x9d\x83\xb1\x97\xb6$@\xad\x94alb\xa5\x03\xa5\x03U2\x04b?\xd7\x92wM\xfa\xc8Tl\x13:b\xed\x99\xa9\x07\xf9}[\xa6:\xc3\x80>\x07'G\x0e7\x87\xb0\xc1\xbe\xc0\xef\xa6AB\xeer.X\xbf\xf0Z\x0c\x98W\x14\xa1B\x92R\x18;&n\xc2\xb5\x9a\xa4\xd4\x8f\x14\x8d\xff\x049CU\xe6\xf9p\xcajX:\xde\x9a ]\x97\xf5\xb3`\xbcxr\x17d\xa2\xb1\xbe'|}g\xa3\x8f\xf4\xddG\xf2\xee#u\x87\x1d\x924f#\xe4Qqa\x07\x9c\xdf\xef\x9e\x8d\xd7\x06\x83\xdf\xef\x9e\x11\xc6\x88K\xf3\xceZ\xa5\xeb\xe3\xdetH,\xf7\x0b\xa0\xed\x0b\xab\xd4\x0fr\xcaO1<\xc8\xe7)\xbd\xc5\x83\x1d\xa68\x8e\xd2\x94\xa6\xae#\x8b!\xca \xa19\x84%\xf2M\xce\xb0\xe5\xf7Z\xbd\xc5AU_t\x19\x0b\xd7~t\x12\xa5\xf9}\xf5E\xde\x90\x0f\xe1\x15M1N\x8d\x81x\x8c(]\xab\x1d9t\"J\xb5\xbd\xde\xbb#\xecp\x98GcnHa\xc2\x8a\xce\xec\xd2\x84\xeb\xb6\xe6\xe8\xec\xb1\xa55\xac\xde\x9c\xdb%w\xb2\xf6\x04\x19\x18\x1a\xa8NtV\xdd\x1b\xc1t\xb3M>f\xcc\xcf\x91\x9a\xf7\x08\xba\x916/1\xd4M\xdf\x1e\xf0,\xbb\\HK\xf8\x19J} x\xf5#\x06\xc5a\x98\xed\x04k\x9b\x9eW\xb7w\xbf:9\xf8M\x88\xcb\x95\\\xbd\xcb\xf7J\x18B\xc2\xb4\x03\x92L\xf8\x99Xj:$\xb2\x0bdH_\\\\_\x9b\xe0\x7f\x03\x99-\xb8\x14N\xb6\x1d%\x7f\xb7}\xd5\xac\xc9\x91\xa3\x80+\xea\xf0^\xf3\x9b2\x06W \xfd\x14\xf0\x93\xe6\x13\xb6}\xa3\x95\x8b\x1f\xef\xe9{P\xdeC*8kJ\xbc\x17\xb8\xef\x15u\xae\xc2\x0dL\xb4\x86h\xca]x\xd8T\x1f\x13\x97rnB\x8d\xdc\xe4\x80T\x85\x9c\x9dP\x91\x8c\x98\x1a\xfa\xc60\xb3\xb0\xdae\x18\xc4\xacCG\xc1\x11\xb2-\xf8'~\x9e\x904<\xf0_\x80\x8a\xa6\x17\x1e\x845\x02\xe9\x81C\x90\xf4\x82A\xfb\xcd0b^\xef\xb9V\xc2\x80\x7f\xe3]:\xf3e\xaaK\x1f\xc2\x15&Z4\x88G\xb3\xea\xd9-#\xf2\xd2\x94\xd8\xaa\xf9\xc0\xd6dF\xf2}\x9aL\xa3Y/\x1b\xd8\x1e7\xd2r\xdfdMly\xd6\"\x06\x8aj\xb7ij\xb2rW\x95.\xcf\xfaf\xc3\xc9\xe4GJ\xaf\xfb\xf2\x7f\xfd\xd9\x03\"\x1c\x8f\xa3v\xf8\xa9\xd4\x9f\x7f\xe2^\x84'Sh\xc6\xcc=\xcdU\x8cj\xf3ju\xc1\xf4\xfd\xda\x99\x97^\x90n4\x9b\xad\xd4\xae\x1c\xc5\x85F\xa7Q\x1a\xde\x8b\xe3V\xdb\xc6\xa6\xd1\x0fW\xdbZ\xed\xe5\x832\x16\x9e\xce\xb6\x0c\x8b\x9c\x8a\xa2G\xc5W\x16\xfev\xfcpS\xdeSvs\x1f\x9c\xcbK\x92\x1d\xd1 \x0f\xd3S\xef\xfc\x0d7\xe0\xa9\xa9\x02\x94\xd5)O\x8cb7q\x9f7o\x15PQ\xf0\xb4Y\x10\x89\x82g\xcd\x82P\x14|\xd3,(D\xc1?\x9b\x05\x99(\xd8T%f\xf6b\x8b\xbd(\xdf\x94:F\xdc\x9ey\xf5\x06, *T\xe0\xe9\xb1.\xa8\xaf\x88\xaf\xd6\xf4\x0dlF\xd8\x05\x81\x9f\xb1\x95\xee\xca\x9e\xe5\xb6k\x9e\xee\xa6\x0f4\x10\x1f\xf6\xdc|\x1ee\xdc]\x95\x15\x84\xcd\x027\x0f./\xd1Twy\x89\xccb\xd3\x87T\x01\xf2;\xd3\x88P\xd0%\xbb>\xba\xaf\xab\xe0\xc5\x82\x93\xb4\xb4\x88\x99 \"[/\xaa\x8554]\xc3\xe4`lM\x0dM7<\x01\x0f\x0e3z6\xa7\xb7f\x92[Zmh\xe6\x01,;\x87\x18\xf7Et\x94Li\xba\xe01 ;\x88\xc2\xd2\xa1\xb1\xeds\x0bz\x15\xc5d\x08[OWm\x96\x8aqz\x96\x91N:q1\xed\x84\x98wB\xc4rg\xf8D\x0cXx\x08\xc9\xaes\xba|\x0c\x9a\xc2\x1eh\xfa\xaf\x1e@Q\x0e@\xa7\xb3\xd5\xde<|\xf0|\xe5*\xc2\x83[\xb5Y\nS\n\xa3\xcbe)\xec\xc0\x18\xdf\xfe\xbd\n\x8d\x0fy\xf0SF\x13\x14\x15\xc2Kn\xa1D&\xad\xbc\xbd\xa24&a\xd2|\x8d\xe1\x03\x9b/\xb9\xe9\xb1\xf1\xf65M\x17\x1a.-u\xa8{\xa6*\xb5T\"*KZ:Q$JZzW(\xab\xe8\xb4\xa8{\x9d\xde\x95\x89\x82\xd67bQ\xd0\xd2\xbb\xb8\x94\xd7\x14\x88\xa6\x08>n\xbc]\x8aF\xb6\x9a\x8dp\x01\xed\xdb\xc6\xdb\xb9\x04\xdfj\xf5\xf3F\x16\xb5\x86\xb6\x90%\x9b\xdf\xb4\x061\x13\x89\x8a\xb5\n\xe1\xfd\x97U\x08\x97\xe5\xba`=\x08\xa2\xecT\x84\x85\xf6\x95\xa20\xb9\xf7\x1b\x90\x96bN\xad\x86\xa6x\xa1\x0f7\xe5\x9b8\xcar\x15\x82\x91\xb5\xedw\x98\xdc\xd7i\xf5\xaa\xe5*t\xa3w\xf2\xa1\xc9\xfe\xf9\x86\xb6]\xcd:\xff\x1c:\x7fK\xb5\x97:\x7f\xd6,\xd0\xe9\xfc\xaaF\xfe\xa9:\x7f\xac\xb4U\xe9\xfcuK\x80Q\xe7/\xd3J\x1dD\x93#\x1eG\xb6\x05\xf9\xd7\xa9\xff\x93([\x86\xf9x~\xc8t\x860\xe6\xceP\xc6:\xdc\npc\x07\xe2^\xd2\x92\xc0\xf5\x1a\x17\x1aCS7\xe9\xe4\x9d:\x16\xff\xf7\xd9J\x90\x84\xbb\xd0\xc3\x97Z\x17~:\x90\x18\xd5\x90h\x91\xd8W\xb0\xcb\x14\x08;5\x1c\x0e\xe4AN\x7f\xe2\xd7\xaa9{g?]\xd3a\xbb\xf4\x8b\xb4|.F\x17\xbb\xfc~i\xe9\xfe\x18a\xb8\x9a\xbf\xe0\xa6\x80>*\xa9\x0f\xb4=\xe3\x06\xc6\xd3\x06\xac\x9di6c\x02\xfa\xb88x\xa8\xc5\xc2\xe3\xf9\xaa7_\xc0\x18\xb6\xa1x\x01\xe3\xf5u\x0f\xe2\x8b\xf1\x07\xb5\xe6\xc5X\x13kQ\xc6Y\xc4S\xe5\x1d\x03\xf3\xc3=\xae\x93\x01\x8e\xc38\x16\\\x90\xf8p\xc1\xea\x96\xc1$\xb8\x9e\x96\x96\xdbQ\xaf\xc3\"\xe9\xae\xaez\x8er\x92\x17\xfbh \xa2`\x92\x80G\xec\x0e\x18\xa0\x88\x81X\xbeC\xba4,<\xd1\x9a\xec\x15\xe3\xb2\xf2\x9d\x90\x90\xb4\xc7Sl\x1c\xa3\xa4X\xac0\x16\x81\xe7\xd6\x17\xf5\x1f@\x9bvK\x14a\xf4\xf4%\xe4\x89\xbf\x81/\xf6c?+\x08\x0f]\x8c\x96\xf6b\xb4\x9c\x87J\x99\xb8\x8b\x87N\x08\x8f\xf3d\x8c\\\x07\x82\x85\xa6\x01I\x8a\x85\xd92\xcd:G93\xdd\x15\x7f\xb8\x1e\x0c\xf1\xac\xb7\xe82U#Ou\x1d~\"c\xf3s\xea`;V\xbe\x02u\x8b\x1a\x95\x91Jw\xc1\x89\x12\xcc\x07\x84\xd7\xab;\xee%`\x90\xa8Zm\xda\xa3\x96\xb8\x9b\x80\x82ff\xe5]P\xd1\xaceF@\xb69Z,\xf3{q\xa5b\xcd\xc2\xa2\xa0\xc6\xcb\x90\xc8\xd5\xfd\xc0X\xcft\xbb\xd3\xb8\x86b\xdc\xfch\xba8\x08\xf3Pn\x80\x11\xba\xbb\xaf\xb9\xce\xeb\xb2 JD\x0c\xda\x8e\x83\xa3\xdcu\x0e1\x91\xa4]\x10\xa9\xed\xb7b\x8b5Q\x89\xd5\x82\xc6\xea\x0eEs\x96\x9e}\x12\x1d\xadNC\xad\xa9\xeb\x92\x90e~\xaf!\xc4\xfa dk\xd3\x84\xa0\x85|\xdf\x03Q\xcb0\xcbni:\x91\xb8\xe7R-CFU2\x94\xb9\x07\xffk\xf0\xd9\xbd\xc2\x16Q\xf2\x06[\x1b\xda\xfcK'\xe4\x8a\x16\xc9\x98\x9cG\x0bB\x8b|\x08\xcf\xbe\xb1@+\xa1\xe7\xacb\xe9_0\xdb\xad\xd7\x9fU\x02\x95\x16\xcf^\x02(1\xdc]\xef-dJ\xf3\xe8c\xad\x1e<\xae\x06Bc_\xcc\xd1\xf7\xf5\xc2\xdf\xaa\xf2R\x1ady\x98\x0b!\xc0(\x9c\x1d\xe6D'\x9cY\x1c\xae\xd2 #\xf9\x19k\xba\xba\xdao\x8d\n :hg\x91ri\x88Kj\x19\xc9\xb98f\xacd\xf2\xefW\xb0g\x184w\x98b\x03\xef'\x8fj\xc6k\xbd\x1f\xb0\xcax\xe5\xa5<\x11\xce\xe4/\x19o8\x994\x07\xbb\xcaX\xfb\x04\xc4\x10T\x06;p\xe9J\x8a\xeb\x12\x8a\x04\x06\x048w\xcaslau\x1e\x8d\x80\xd5U\x10\x0d\x1az`\xa1\xdfx\xff\x82\x01\xe2B7^\x9c\x15\x1f\xaefF\xdbH\xed\xe5_\xa3-\x95\xd6\xd7\xf7Q\x1c\x9f\x921\x89n\xf0\xb4,\xeb\xa1@\x19\xe7J\x92\xde\xda\x8e\xd0\xa2\x94]\x8f\x89\x7f\xfc\x9d\x9cN\x9bB\xa0\x92\xa3~*:\xf9\xd9\x17\xb2\xa0\xdau\xc4>\xba$?=\xec\xa7KR\x84\xedV\xed\"\x84\xebR'C\x84\xeaR'\x0b\x842\x99OC\xbc\x11,\xb4\xbeP\xd5\xfa\xec\x06\xd4\"\x88\x92)I\xb9\xf8\xe0FA\x94\x93E\xd6\xedhV?Q\xe9\xe1s\xf6\x8ag\xf7\xef\xf0\x1f\xcbP\xb7\xb5\x88W\xd0\xa6h\xb3&\xbc\xec\xd2v\xe7\xd2\xd3\xed\x13\xb5\xddy\xd7\xc6\xaeH\xd5\xe1\xeaR5T\x92\xb5R;\xecQKf\xdf\xed\xbe\xb7/\xd6\x9c\x85\x96\xa1\xad=\x1b\xa2\xbf\xd7\xa0kz1\xfd\x9b\xf5\xe2\x8ey\x14\x0eW\xdc\xedc\x8dGC\x99\x04\x98]\x91\xfd-\xfet=\xd8\x86\xad\xea^\xca$X\x84KE\x10\xf2\x81v\x11^$\x84\xe6\xb4n\x96\xcf:.\x96\xc9\xd9\xb75\x0f\xe2\x13K\xdc\x10xZ\xd7\x9e\x92\x8b|J \x06\xaf\xf1\xf0[/\xd6J\xb6p\xab\x80'\xeb\x82j\xe5\x9d\x8f\x8b\xe5\xc5\xe6\x07\xbe\xe3\xc1:P\xcb\xdd\xe4\xce{Y\x1dsi\x1f-2\xa2\x0e\xa2T}\xbf>f4\x19\xf0\xed|\xc0\xf4\xeb\x01\xdb.\xad\x0e\x81\xa6\xeeY\xdd\xcd\xa0\xfbd\x05Z\xa7+\x1dF*)]\xf7]\x81\xfd\x04{\xf9\x94$\xa3\xaaO|)\xd8)\xc7\xde\x1dy\x9e\x13Y\x96\xbf\x19\xc7V\xf3\x124\xa6\xf6*O\xe0*O\x06\xd9\x02\xb4\xb3<\xe0\xfaH\xc7\x86K\x93\xfd8\x1a_\xf7\x10^\xd4\xa7\xc4^\xa5\x87\xb9]\x88\xb3\x11\x9d\x03\x03pL\x9e\xa8^\x90S~\xf4\xf3X\xd4\xad\x84\xb6p2\x01\x07\xd6\xab\xcd\xab\xc1\xf8\xb8\x1b\xa1\xf1[%B\x91#\x08\xbdM?06\xee\xbd\xc9\x04\xd8g\xb5\xc3\xef\xb4\xb4\xbc-R\xb2\x8a\xb5\xa5r;\xebeo\xf9\xdf\x81\xdf\xca\x07~\xabj\xa9\xff;(\xd3?\x7f\xd1AY\x97\xceB{\x1d\xa7\xd5\x0f\xca\x0c\xa7\x0bx\xf2%\xf4\x9b\xb4\x9f~\x13\xf69\xcc\xea\x10#\xc2\x9e\x1ba\xba\xbaX/Dz\xa5f\xda\xcfX.\x82\x08$\xb6\xdbFuA\x9d\xbb\xc6MS\xba\xf8\xe9\xccs)jYx\xff\xd3\xc9S\x9e`e\x1a\xc6\x999\xe1\x0b\xe8\xa5\xf9\xb2\x1d\xdb\x81\xd7\xaaB}\xb7I\xe1\xd3L\xe4\xa5\x07\xf1\xa3\xf7\xec\xde{\xb2\\\xa1\x9fl\x1f\xb7X\xc6\xd9\xc2\xc9H\x8esrN\xcf\xc2\xc52\xeee#\xaf\xbc\xbb\\\xf6\xe5\x19\xdb\x1cxm\x8e'\xcf%5w \xfd\xdd`\xa2\xb5\xcb\x1bEF\xd2\xf2\x990\xb4:\x0f\x93ILNVi\xfb\xa6\xccw\xdc\xed\xbb\xa1\x0c^\xe7\x03\xe8\x1b\xbd\x85\xe132\x80\xcf\xe9y\xb9V1\x81\x86\x9dO\x9d\xc3\xf2e\x9bdtw\xb4\xeb8\xf8B\x86\xbc\xffbN\x96\xbb\xce9\xb9\xcb\xf7R\x12>\x92\x9b\xd4\x0c\x0c& \xda\x93\xe50R\x9b+\x06\x04c\x1d\xf6\x08\x9e\xc4\xd8M\x16\xfda\x0d\xcfkF\xbddX\xac\x05d\xc3\x1fi\x94\xb8\x8c}x\xfd8\x97EGm\xb0\x89\xfa\x06\xa0\xad\xf5(w\xbe.\x11\x1f\x81\x1fu\xe3E\x1e\x86\xe2E\x87\x7fz\xc1\x818\x91F\xa7\x89\n,\xad\x17\xf0\x10\x92\xb58\x02\x8f\xef\xc2g\xbdt\xd3\xec\xa6\xe9n\x8c\xf8h\x98e\xd1,a\x8c\xcc.\xa6\xd7\x92>o\xf1\xfc\xceMuE\xe4y\xb6\xef\xf3\x95\xa6bJ\x03]~\n\x03'&=\xf3\xc2c(8\xb4Ta\xac\xe9\x1dH.R]\xa0\x89\xd6\x1b\xc9\x90\xeb$X\xa7x\xda\xc5\x9aK\xd1\x83XO\x9ck\x19\xfe7_@\x02\xdbj\xa2\x7f3\xf6@\x99\xb9\xfc\"1`\x0e\x90P\x99tG\xd2\xf0\n\x05\x8a\xdaO\x91|,e\n\xdb4\x9a\x15\x12hm\xb3L\xda\xc7P\xce\xe3\\\xa6\xc1m\x1a\xe5%D\x99}\xaaI\xa7\x845xM\xee\x19\xfe\xf5\x0b\xbe\xff$\xa8\xd6X>\xa1V\x85\x91\x07\x01u\x15\xd2\xe0\x99\xc3R\xf1\x9eG\x07l{\x157\xb6\x9b\xe6\xc5r\xa6\xd8\x14<\x02F\xbd \x14\x05[\x9b\xdf|\xab\x0f\x86Q|\x91\xbbOn{\x99\xf7\x92\x8a\xb5+{\xad\x9f\xb3\x04\x8f\xf5T\x8b\x80\x95\x9b\xc2\xa1\xed\x87IBs`\xeb\x12B\xce\xfb \xccj\xa1\xd8\xdas\xd2!\x90'}\xbd:\xb0\xa3D\xed\xd9)\x99\x92\x94$\xe32D\xdc<\xca`\x1ef\xc9\xd79\\\x11\x92@\xc4\xaf\xb1D\x19\x99\xc0\x00\xb2bIR\xd7\xabA\xb0\xa1\x90I\x87\xf8\xb0\x86\xc7\x0dJB\xc9Z\x10\x1fm8\xbb\\P\x81\x86F\x0d\xfa\x86X\x843\xc2\x98\x1f'\xfa\x93i\xcb-\xc7\xa2y$\xab9d\x93`I\xd2,\xcarSX\x05\xc9\x14\x92\xee\xd3\xbdd\xa5\xe3kU\x1f\xd0o,=s\xaf\xb0\x1e\xd2~=dO\xe9\x06\xf7\x92U\xe1\x82x\xe9\xcd\x86\xe1\xaa\x12\x9aGS\xbc\xe68,\xb7oxYU|\xf2\xa4\x02J\xf1\x88\xa8G\xbe\x066\xd8!\x08p1\xf8\xaeZP\xe1\xcb\x92\x91\x0e\xf4\xeayUd29\xb7\x89\x12\x13-%?\x93\xfb\x03zk7\xa0\xca\xa7\"\x0f\xa9C\x8a\xda\xfa pFI\xceS\xc20\xf1\xfe\x9a\xdcsdNi:&\xc7\x12\xed\xbe\xc85e0\x10\xb2.\xbe\x8a\x8b\xf4\x91\xfdcUM\xf4\xbbb?\xb8\x86\x80\xf0\x11\xe9\xd7\x1f\x1eQs\x1b6\xbd\x92\x86\xba\x84\x0f\xf9\xc8\x05^\xc4\x06/F\x83V-\x03\xfc\x8a\x84=\xb5\x0f'\xc1\x84\xf2\xf1Z*\xdb\x97^.L)\x8a\xed\xa5\x1b\x0d\xf2I\x82(\x13\xbc\x8e\xdf\xd1a\x02L\xd5)\xab\x9f\x19\xdb\x07\xcd\xcb\\\x87\xddGtg\xd3\xd7\xcf\xbf|\x90\x0e\xa6q\x91\xcd\xfbN#TS\x99\xf3\x9a\xb6\xb4\x13Hf\x8c!\xc7\xab\xb4\xafEk.\x1a\xb2}NOXz\xea\x97\x93\xd4\xa7cI\xc3\xc4$\xce\x18D|Z\xe5r\xad\xfeS\xca\xba\xec5\x9f\x98_\xa0\x86\x03\x1b\xc6J\x0c\xe3^$\x91d&--K\xec8\x81\x04\x0d\xb31\x7f!Wx\x14E\x9e\xa4\xac\x08\x0c\xa2X\xfe\xfeR\x0c\xe8\xf1i3{\x07\xdf\xc1\xa9\xee\xe5\"(\xdd\xe6\x98<\xd6f\x8c\xd8\x8en_\xa9Aj\xcd\x87\x9d\"\xa81r1\xb2\n\xf4=A\x07?\x83\xe8|\xc6\x84O w\xcb\x94d\x19\x93\xda\x17E\x96\x03\x89\xf29I\xe1\x8a\xf0\x06h\xaa\xc8\xd2>\x06\x1dv`\xbd\xfc\x90\x862I\xa5\"U\xba?\xe7N\xae\xc8\xdb\xa8\xe8Pz\xd4\x8ei\x92\xe5i1\xcei\xaaS[\xe4#g\xc0L\xef\x95F\xda\x8e8\xa0>R\xff\xb4\xbbA\xa9\xba\xec\xd0\x94\x8cICK\x92{\xbb\x02\x1bYM\xa2\x86]\xd0\xbe\x17\xf3>DUN\x8a\xe5l:\xeb\xa4\xc3t\xcf\xf2T\xa0a\xbd\xf2\x81\xf630\xbf\x8f\xe2\xf8S-\xcch\x95\xab\x8b!\xaeb`n\xdc\xbf\xe8\xb2\x97X\xac\xc9\x7f\x89K\xac\xdcH;\xb7\xd0D\\\xc6\xab\x8dF\xbf}\xe2\xe8k\x8b\xff\xcf?\xcb\x8c\x85\xb84+g[\xc5\x01\xb7Q\xd2[\x8f1\xddi\xf6!\xa9<}\xb5\x93Q~\xac1}I\xb7\x01\xb5\xe74\xbdK\x16\x9f\x83\xbc\xb8t#{k\x92Xzw\xf1o8\x97\x10\xb9\xbe\xec\xf4\xe5*\x91\x15J\x8a\x04R\xb1k\xbfM\x82\xec\x95\"\x9b\xbc\xbaG\xf5\xc6\xe68\xc3\xa3-TUNP\x1f\xb1\x9c\xef\x8a\x90\x0fB\xab2\x03\x16\x02\xd0\xde\\\x86PQ\xb2,\xf2S25\xc3\xc5}\xcd1\xf2\x916\x9c\xff\xf4I\x1aUZ\x7f\x89\x07y\x19\x96<\xf5\x98\xb8\xb3\xa9XA\xec&aR\x9a\x84\x13n\x12\xc6\xac\x85\xf6\xcfK\x1d\xca\x08\xf4\x80~/\x8e\xa0\x18\xc7\x07G\x12\x85S\x1aQ}pJ\xa2\xc0d\xd1u\xa2\xc0\x83\xfb\x16Q4\xde\xf2y\xe7\xed\x8b\xb9\xe5?\xe4k9G\xd6\xd3\xffqG\x0cKt\xf3\x86]\xcb\xdc\x95_/\x1d\x01\xc4o\xfd\xbe\x06C\x08\xfb\xb6g\x88\x17\x0eC#\x910\xba\x98v\x0c\x89\x95\xd3\x8e.0\x1c\x96\xe3a?\x8c=)z\xb5T\xadB\x99\xba\xb4(r\xaeueb\xe8\xba\"\xf3=\xd8\xd6\xdd\xd7\xad\xcd\x06D{\x93h\x8b\xc2\xad-\xa3\x0d\"w\n\xd9\xc1\n\x97\xf8W\xc7\x99\xa5\xe5\xae\xa0\xdc\xd3\x9d\xd1\xdd\x92\x8cs2QM\xfcmBIa\x07\x8e\xc3\xe3v\x01cz\xce\x85\xf0\xf09\xbb_\\\xd1\xf8\x83\xa6~\x04;\xb0\xf1\x7f\x7f\xcf\xd6\xff\xfc=[\xffjc\xd6\x86\x08\x11\xe2b\xb0\xfea\xf3\xeebs\xf0}8\x98~X\xffjC\xe3\xe6T \xe4\xe6\xd5\xc5\xe6\x96\x01\"\xe3\x10\xf4bs\xf0\xad\x01\x841A\xcc\xad\x7f\xa8\x93\x1d\xd8\xde\xaa\xa4f\xa9\xe9\x81B\xe7:\x11NM;R'\xc3\xd7\xed\xa6\xa6\xfa\xa62\x12OY\x0d\xf5\x7f}\x9b\xac\xa4\xdd,\xdb\x80\xc6x\xf6\xcb\xfey-\xe7\xd9\x91\xd6\xa7y\x949\x9e.\xec\xf2\xa4R\"+\x16,\xd3\xe4\xb4\xc1\xe7\xb0\x03Ga>\x0f\x16\xe1\x9dF\xac+K#\x8d\xf8\xd2\xef\xb6'\xef\xf028`\xdbNBou\xf2\xa7r^\x07\xea\xb9\xd8L\xaf\x7fH\xddC&\xba1\x1e\xa8\xac\xad\xf1\xac\x18\xb5 \xd2d\xddiz\xa7\xea{\xa3\x89\x9e\x08\xd2\xac\xa0\xc9\x97nK\xd3\xc2\xeat\xebX\xa2\xbe\x93\xe1\xba\xab5\xde\xed\x16\xd0hD\xa0BC\xaa\x066\xc0Z}\xf2\x04&B`\xf3@{i\xe5AM\x13\xa4\xb1\xcdc.\x15KF\xa9\x9b2\xa8PmBdF)\xdc\xbdQ\xe5/\xffF'U\x93\x17\x1a\xec\xc0\x8cm\x86\xbb\x90\xc3:\x8f)\xd6u\xc6\x0c\xcd\x0cJk\x9a)\xac\x12\xe6\x13\x18\xc2\xba\xe6\xf3D\xb8\xdc\xf2\x84~\x11\xe6\xf33\x1f\x97\x16\"\x1d\xb4\xe5,\x90\xcdp&\xc1`\x17bW\xe4!u\x9f\xa2\x86\xba\x0bOa\x08\xdf1l\x84\nX\x8a\xfdk\xd0\xb3\xfaK\xf5\x8ci0\x17\xed\xa1>\x1e\xd1\xf9\x10a6\x99\xc2\x87\x0c\x85\x13\xf4w\xd7\x0b\x1cSn\xb2\xd3\x96--e\x13\xb4\xd9\xebIH\x9fpLo\xa8K\xbc\xc6v\x02\xea\"\xbe\xea\xf6w\xb4\\_b|2\xb2Jv\x8ca*\xe9\xdbx\xa0\x17_\xa8x\xdcr\x9e26\xae\xa1Js\xa75\x91;\xe5#;M`\x00\xb1\xb5gJ\xc0\xbd\x98\x11W\xc2T\xb6\x9c\xff\xb5\xcdu\xb7%zB\xc0\x00\xc6\xac\xac\xad\x04\xd8\xfax\xdb\xa9\xf4/l\xe1\xff/k\xf9\xc6\x8c9\xca\x18\xd5f$\x17\x82\x99{\xeb\xf7\xdc\x05K_V\x18\x80\x8b\xb8\xea\xbe\x9c\xba\x84]\xb8q\x13\x1fBYi\xec\xa1\x05\xdf\xb8a\xae6\xab\xa3\xce\x9d?S\x08i\x02\x98\x1dk\x17\xae\xf89\x82\xdb\xa4\xb4b\xb5\xaf\xdf\xf5\x99/\xf3JHx\x1c\x06\xcb\x8cR\xd5\xa5\x8c\xe7\xe4\xe2.\x10L63EJQ\x1bP\x086\xf3\xdaV\xfe.\xb3\x86\xa80\xe6_k\x13N\xee\xf90\xad\xf0\xa9W\x14\x01g\xd6F,\xe2^\xb42c\xed\xcf\\\xb9\xa6\x00\xfb=\x17l\x86b\x8c\xaeq\xcf\xd7\xf4\xdc\xe8\xc5\x95c\xe4\xe8\x1ccbn\xfa0s\x85\x15\x06\xf7\xec\xb54\x88 \xe6f\xe0Y\xb0]\xb6[;\x8b\xf0\xee}\x18\xe5\xdc\xfd\x8cq\x98\xb9{\xef\xa6\x81x-[B\xc3{\xe8\xe3&\xee\xe4i\x18\xc5\xc8K\xd1em\x17\x9b\x96/a\x08\x13L\xe0\xd7\xffhT\xb1\x00#\"0)\x98\xc4B&o_\xf1\xebG\xb1X\x15\xd5\xd2ic\x87}\xbd\xf7\xb9\xafn2v\xa1\x80!\x8c\xdc\x85kH\xf0U{\xa9\xb8\x87IW \x1f\x12\xf7\xd9\x96\xa8\xdc\xa1\xe5I\xe7\xc2z\xf7\x9c`#\x8c\xe3\xe0c\xe6\x0c\xe1\xf9\xf3\xe7~\xab\xb0\xc8\xe7\x1b!6\x9aq\xa8\xa7\xcf\x9e\xea\xa1\xd0\x88\xc7a\x9e}\xffL\x0f\x93\x92I1&i&\xc1\x0c\x1f\xccd\xe2! \xf7\x8d\x01nI\xc6\x83\xdb4\\\x0ej]|\xf6\xfd?[\xf0\xfc\x10)k\x8e\xa5\xdd\x01 8'\xf1\xb2\xec\xe9\xd3g\xed\x01I\xc0\xda\xb8\xbf7\x82\xd5\x87\xfe|\xb3\x8dE \xd9\x18\xfd\xf3\xcd-3(C@mH\xcf\x9b&\x06'\xd8\x98\x10\xb2\x1c\xc4Qr\x1d%\xb3\xfa\xb8\x9eo\xb61[\x83V\x06\xf7|\xb3\x8d\x83\x1al\x1c\xde\xd3\"\x97\xc0m\xcc\xd6\x80\xcb|K\x83<\x9c\xe1\x1c.I\x1a|\xcc\xee\xb0\xf2\xb7}+7+\xb6'~Bo\x93\x98\x86\x93A\x91\xc6r\x96\xbekA\x914\xad\x93\xc6\xd6\xd3v\x1f\x18\x10\xdeG\x18\xe4i\x98dS\x9a.H\x9am\xcc)\xbd\x16-?mO\x95\xa1R\xedGB\xf3\x01\x9d\x0eP\xc9\x16\x0d\xb5\xc9\xa3OC\xcb0\x0d\x17$'\xe9\x80&\x84Nec\xed\x89\xeb\xd3\x18\xd3d\x96\x03\xe9\x0e*\xdbj\xcf+kK]\x04[\xedE\xc0@\x1ak\xffi\x9bN\x19Ts\xe9?m\x13(\x8f\x9dP'\xcd\xf6\x8c\n(\xba\xccxV* \xd9\xee\x1c\xa7\xdb\xc6\xce\xa0YF\x02N\x1d\xea\xd36\xbd \xa8\xe6h\xdb\xd4$\x00[\x03n\x0f%\xa6\x8dm\xe6\xbb6Rh\x98=knn\xed\xceq\xa8\"\x9f\x0f\xc8]N\x92\x8cAo\xe0\x06\xda\xdct44\x83\x95\xcb\xe3\xc5l\x83\xf1\xa0\xabp|\x9d\xc9\xd5\xa7\xc1F\xb3\xce<\xcf\x97\x03\xd6\x01YG\xc3M\x9au\xd4\x89\xd6\x90C\x13\xbc\xda\x1c\xd8vQ\xf6\xad\x8dVs\xc5\x8c\xa7X+\xfb\xd8\x8d\x8b\x94\xfc\xbf\x82d\xf9\xe0\x8aN\xee\x07d\x12\xe5\xb4\xdc\x93\x9e\xb5\xf7\x04[\xed\xb2\xc3m\x8aiV\x13\xdd\xac\xb2\x1d\x95\x9fl\x13\xaf\xa1n\xf9\xb5\xf6\xb2\xc0\x1a5n\xf1\xcc\x80\xfc\xda\x04\x19F\xdb`\x7f\xcf\x0d(m\x92\xe1s\x03y \xe3Sh\xb8E\xbe\xedmJ[OO\xfb\x86\x8f\"\xb0\x82C\\HQN\x16%\xde\x0d\x0b\xa0YQE\x98F\x04\xd1\xd6Q\xa38p\x1b\x93D\x91\x01\xe3\xcd\x06\x16az\xcd\x98\xa1\xfc\xaea2[\xd5\xe8\x84\xc4r\x80\xcf\x0d\x84\xd5\xacD\x938J\xc8\x00\xaf\xb6\x859M\x07W\xe1dF\xe4\x97\x0d\xb4\xd6l\xa4df\xd5B4\xac\x89f\xcd\x1b\x9e\x02r\x90\xe5\xe1bYV\xd6\xec\x00 \xd6\x8aINjs\xb2\xd5\x1ef\x86\xb71\xb3\x8d\xa9\xc0\xdf\xd6\xf7m\"\x910\xb5\xad\xba=\xbd\x8c\x06\x9b\xdcF\xd3\x18\x83R[\xd2\xec\x94\x08\xd3\xe04\x9a\xcd\n\xc1\x1aD\xfeT#U\"\x9cF\x9c~\xde&k\x99\xd5\xeecc\xb4m\xc8\"\x8f\xe2\xba\x8c\xdc\x9e\xc4\x9b\x88\xdc\xd6`\x9e\x1b`RJ\xf3A\x94|$\xe3\xbc\xec\xdcw%\xa46]\x0d5^\xd8I\xdc\xa8fly\xd0\xd4\x8e\xda\xb5\xa5\xad9\xbd \x8d[Z\xfc\x06M\x0e\xeb\xb0U\xbb8S\xbf43\x8d\x92 ,\xf8\x0d\xa1\xaf\x1dX\x07\x02\xeb\xe0|\x1d4\x0d\xbdR\xd7V\xfa'\xff\xa2\xc15\xb9\xb7\xe6O\x16\x95\xc5\x11\x0e\x83v\x95\xcb[\x0f>\xd0 %\x19\x8do\x08St\xeb\x17\x1d)+\x8d\x98\n\xbe\xb5\xf9\x0d\xc7\xee\xc3\x07\xef\x1f\x0f\xde\x8b\x7fll\xfc\x1f\xc8h\x91\x8e\xc9Q\xb8\\F\xc9\xec\xdd\xe9\x9b\x9d*\xc3\xe1\xe0\xaaH&1[\xe7\xc1\"\\\xfe\xff\x00\x00\x00\xff\xffPK\x07\x08-\xe3\xb5\x97=9\x05\x00\xf7\x0c\x1b\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00 \x00swagger-ui-standalone-preset.jsUT\x05\x00\x01\x80Cm8\xec\xbdys\xdc6\x9a0\xfe\xff|\x8aG|w\x152M\xd1\xdd\xad\xc3:,k\x1d\xc7\x9e\xf5\xbb\xf1Q\x963\xf3\x9b\xb7\xa3UQl\xb4\x9a1\x9b\xec\xe1!Y\x13i?\xfb\xaf\xf0\x00 \x01\x10 \xd9\xb2\xb33\xbb5\xacT\xac\x06A\xdcx\xeec\x0b\x16U\x1a\x95q\x96\xba\xa5\x0f\xc4\x83\xdf\xfe\x00\x00\xe0dW\xbf\x92\xa8t\xe0\xf4\x14\xca\xbb5\xc9\x16@\xbe\xac\xb3\xbc,`{\xdb\xf4v\x95\xcd\xab\x84\xc0\x19\xff#\x10\xb5O\x81\xb8\x1e\x1c\x83#\xba\x91?\x9a\x93E\x9c\x12\xda\"\xfb+\x08Ws8\xe3?\xdc\xd9\x05\x0e\xe8\xb8k0g\xe2\xaf\xe0\xfc6\xbc\xbe&\xf9\xcfo\xce\xcb0\x9d\x87I\x96\x92\x0f9)HY\x0f\xa1\xec\xab\xf3\x87\x07\xb7\\\xc6\x85\xdf,\x89X\x8e\x9c\x94U\x9eJK%^\xd0\xe7&\xcc\x81\xc0)\xfc\xf6p\xf2\x87\xbaPT\x85\xd4\xcd\xe5\xca\xf4\x89\x17\xe0\x92Y~\xe1\x89v\xe9\x0f\xb1b'JU\xdavLG7\xcb/h\x17\xcaKl\xeb\x18r\xbfU\x9a\x1c\xc3\xd6\xa4]\xcc\xbb8\x86\xdf\x1e\x94w\x0fj\xa7|T%\x1dU\x14&\x89\x1b\x8b\xc1\xf9\x10\xfb \xfdJ=\xfa3\x81S\xd8\x1aK/\xea\xd6\x9anx\x9bi\xb0\x82S(}H\x83\x88N\x8b\xfe1\x87S\xf5\x10\xfa\xd0Z\xb24\xc8\xf8\xf9\xbc\xbf\x87\xf7x\x1c\x02vL>\xe4\xd9\x9a\xe4\xe5\x1d\xff\xb2\xbdBQ\x96.\xe2\xeb*\x0f\xaf\x12bY\x96\xb4Z\x11\xf1~\xdc~\x7fM\xcac\xc8\xd5\x15\xf3\x9a9\xd29\xa4\xca\x1c\xf4\xd1\x8b\x13R\xd2\xa3^\x06\x97\x97\xa4x+\xeeK\xeb\xac\xc9\x8f\xd8 :\xd7\xb0JJu\x0cp<\xec\xeb\x01{\x9d\x06s\x97\xf8\xe0\x84\x0e]d\x1f\x88:\xbdL\xdf\"\xbd;\xde\x0c\xdf\x99u\x9e\x95\x19\xbd\xa9\xc12,\xde\xdf\xa6b\x8f\xd8i\xc2\xef\xd5\xf6\xd7p\n\xce\x93y\\\x94\x8e\x0f\xa9\x9b\x06\x14pL\xc7\x07\xac\xda\x83;\xd3\xceG*\xf7\xefT\x05\x81\xa2\xcc\xe3\xa8tN\x94[\x99\xc3)\xa4\xee\xfe\xd4S\xf7\x94^\xa8\x99\xf39N\xe7\x8e\x0fNN\x8a,\xb9!\xf4\xcf(K\x8b2\xaf\":\n'N\x8b2L#\xf2~A\x7f\xads2\x8f\xa3\xb0$\xec\x935\x05\x1b)\xd6\xe3[s^\xde%\xf8\xb2\xa0\x7f\xbcH\xe2\xb0 \x85s\xa1\xf6\x9ca\xcfE\x14&a\x8eu\xc9_+\x92F\xf8\xdd*\\\xaf\xe3\xf4\xda\xb9h\xe6PJ`\xb4s\xf9\xe9dS\x1f\xaa\x936\x9c\xa1\xb7\x8c^\x9a\xdf\x1e|\xb1=\x9f\xc9]\xe1\x12/Xd\xf9\xab0Z\xbau\xd3\xadvE+;\x138==\x858\x88\xd39\xf9\xf2~\xe1\x12\xcf\x83r\x99g\xb7\x90\x92[\xc8\xdd\xef~N?\xa7\xd9m\n\xd9\x1a\xa1\x9e\xf3\x1d\x8c\x80\xc0\x08\xbes .`EJ\x88S\x06\xd8c\xac\x90-X\x9d\x92\xd5\xf9\xcb\x8b\xb7?!l\x0f\xbe\xf3\xb4\x8b\xe6\x03\x05\xcaA\x19^3\xc8\x81\xbf\xe8\xe6\xd1\x99\xb1?\xee\xef!\xad\x92\x84\xbf\xe3\x1b\x8a\xaf\xc5\xdf\xf7\xf7\x83\xae\xca\xd6X\xed\x9c\xb7X\x9f\x0bl\xb3\xf9%\xb7\xda\xba\xf4`\xbd\x81\xbc\xd5\xe6\x80a\xb3\xd2Ou>\xf5\xd1\xc3j\xcd/}\xd6\xfcL\xf2y\x8b_j-\xf9\xb0bE\xa5@\xad+\x1fd8\x057\xc5\x0f\x94\xd2\xfa\x83\n\xf1\x9f\x8f\xbf`\xeb\xf4\x14R\n\xea\xe4\xf3\x96\x1a\xce\x9bq\xcd\xd2Yy1\xf0h\xd2\xa7\x9a\x9d\x97y\x9c^\xbb\xc4\xa3\x18\xb2lUzh\x1f\xa8\xca\xf3\x81\x1f\xe9\xac>\xd2\xf5\xb9\xb2\x1dm\xd0F%\x1e:\xba\xc8\x87\x85\x0f\x89\x0fk\x1f\x96\x8c\x06\x81\"x\xdd\xa6r\xe83\xaf+\xfc\xd1\\\xe1\xa6\xaepn\xaepWW\xf8`\xaep]W\xf8\xc1\\\x81\x12\x88\x94\x0b\xc8\xe1\x18n\xe8\xbf3\"N\x17A\x1a\xf8\x81\x12\xf3\xae(\xfe\xed\xc1k\xe8\x0ds\x8b\x97\xbc\xc5\x98\x9eB\xd1Z\\\xb7f\xfe\xe8\nN\xe1\xb2i\x19\xbf\x91\x7f\xe3\xa7'\xadO\xe9\xf5w#Dvx\x98\x10hz\xb8?\x94Lv]\n\xec\xb7\x96\xf4\xdd\x8a\xfe\xef&\x8b\xe70F\x90\xb9\x9aE\x17\x1e\xe5\xa0\xe0\x18Ro\x16]\xf8@\xe9\xa2kZm\x01g\x10\xba R\xc6\xc7p\x87L\x98\xe9\x0e'X\xef5\x7f\x83\xf4\x96\x0f \xfd&\xf1Y\x87\x95\xbb\xf2\xe9\xa1\xa0P\x1e\xb7\xe1g\xcf\x87\xcbYt\x01[\xa7\x90\xe0\xcdu/\xb1\xc6\xda\xf3YOW\xf2[\x17\x7f\x9dB\xa2\x81\xd5f)\xf2 bw9\xf6\xe9I\x83S\x98\xd0?\xfeHI:\xfa\xc79\x9c\xc2\x1e\xfd\xe3\x03\x9c\xc2!\xfd\xe3\x07Z\xe7\x80\xfe\xf5g8\x85]\xac\xf53\x9c\xc2\x01V\xfbH\xdfN\x0f}\xe5\xc6\x17\x9b\xdd\xce]\xe3\xed\xdc\xd3\x8b\xf9\xed\xd4\xef\x1b\xbd\x9dO\x9c'\xd7\xed\xcb\xa9\xf7n`]@b\xe38\xaa\xca\xdc\xd2\xb3\x1c;\xda\xa8\xf3\x8c\x02H\xd2>\\\x1c\xde:N\x83b\xdd\x10F\xa7\xe0\x00\xfd\"\xa5\x18\xe7\x14\x91\x0f\xef(\xf7(%\x90\x84\x11q+\x1f\x9c\xed\xbfVYy\xe2x\x88\x99\xbe\xf3|\x08a\x04\xces\xfamL\xffz\xf6\xc4\xe1d\x9b\xf3\xdc\xb1m\xeffD)\xe7\x8b\xe5\xf2\x94a \xe2\x86\x9e\x0f\xb9\x9b\x07\x1f`\x04y\xf0\x1a\xbe\x87\xd8\xed\xa4\xd2\x04\x1f\xe580+/\\:\x07\xeb\"\x11\\#\x12\x94\xd9O\xd9-\xc9_\x86\x05q\x91{$A\xb1N\xe2\x12\xbf\x0e\x12\x92^\x97Kx\x0e\xbb\xeat=\x1f\x1c\xb6\x86\x94!\xe9C\xdc}\xe8\xc9\xa9R\xc6\xac\xce\xe9\xce\x89\xbbz\x1b\xa7\xf3\xec\x96n\"\xfb+x\x1b\x96Kz\x97\xf1\xdf3\xf1\xfe\xd8\xf2yA\x92\x05\xfd\x98\xfe\xab\x7f\x8a\xef\x8eA\xc0\x01\xd7\x11\x84\xe82.\x1c\xcf\xf5z\xf0\xe05\xc7\x83\xd7\x8f\xc0\x83G\x9d\xa4\xca\xbe\x8e&\xd9\x8d;\xfa\xdfC\xaa\xd8\x89\xb8\x03\x9d\x16\xa0Kb\x90m\xc9\x1b[o0#\xa5\x91d\xe5\x7f\xf27\xed\xe5\xcc\xe9\\b\xfa\xbf\x01\xfb/\xaf^6\xf8p\xbf\xc8\xf3\xf0.\x88\x0b\xfc\xd7\xdcX:\xb8\xb1\xff\xe57E\x9e\xf2\xb0\xb3J9nN\x17\xd0\xbe\x04;\xf2\xe9nM^\xe5y\x96\xbb\xce\xcb0\xfd\xae\x04\x8a\xdd)k\xbd\xcc\xe6\x90\xa5\x00\xec\xac\x9aey\x9bB\xb0\xa6\x15E\xb4e\xb9Vt\xb5\x9a\x1e\x94\xf3\x95\xdfi\x9f\xd0\xf6\xd2\xce\xd3\x89wq\xec\x03\xb9 \x13\xcfuXq\xd3\xfee\xd9\xc7\xbf\xcc\xfb\xf8\x97\x9b>\xfe\xe5\xae\x8f\x7fi\x18\x9c?\xdb\x19\x9c\xe5\xa6\xec\x08\xe5aV}\x8c\xce\x15o\x99\xb2Ns\xc1:\xd9x\xa5.\xdee\xa9\xf1.\x8ckY#3\xa0q-W\xc8\xb5loC\x88\x8c\x05\xbb\xbc\x94\xd5\xa1,\x0b\xf2\n\xc7\x90\"3\xb3b\x8c\xc3Rc^\x9a\xd3\x8f\xb5\xcf\xb0\xb6`rh#Y\xcd\xf7\\\xd7\xdc\xc8\xe9)\xb2:\xdd\x92$\x90H\xc6F\x90d\xa7\xd2\xc5C\xaf'\x05: Dr\xecf\xda?\xa0Oq\x1b#T\n\xf3\xebjE\xd2\xb2\xe0\xb4e\xdfw\xf4\x89\xc2\x82\xc0\xf8\xb8\xb7\x1eH\x02{r\x0be{\x0b\xf5\x07[\x9el\xde\xb2K\x0c\x94\xb5\xfe`\xe3\xd3\xc74\xae\xd0\xd4\xa6\xe7\xa1\xf3m\xab1\xba\xa1\xd6/\xecm\xd5\xea\x95p\xbdN\xee\xb8\xf2\xaf\xde@s\x8b\x0f\xe6u\x11\\\x87\"!\x904!\xb2J\xa5n\xcaE\xce\xfc\xa6\x93\x9b\xcfl\xdc<~\xe6\xba\xab\xe0&\xce\xcb*L\xf0\xe25\xbf\x10\x96x\x9cW\x17\xbc\xfeG\xfa\xcd%\xfd\xdf\x16\xb2\xfc(\x0f`\xdc~\xe2yV\x8e\xfe\x1f\x85\x8b\x9f\xeab3.dk\x953\x1cu\xa8#4\x8a\xa2\x8c\xca\xc3f\xaa$X\xb06\xf7=83W\x96\xd5n\x16\xccE!H\xee\x96\x9e\x8f\xb0'\xa3gtk\x8c\xdc.jL=\x03Y\x04\xcd!\xaa\xeaf\xd5\x0d\x91 \x9f\x87V\x7f\xce5)\x1d\n\xbc\x91\xb8r\n\xf1\xcb@>\xbe\x88\"R\x14Y\xce\x08\x8a\xa2Z\xd3\xfd \xf3-\x0bA\xe1\xdc\x84IEx\xdb\xf4\xd0\x95\x0cY\xa5\x01\xbe\xf0\xfcMI\x0e\xf9\x08l\xa5\xee\xf4\xc8\xb3\xf3\xfd|\x0cO)\x9e0+~\x7f{\xe0\x8a\xcb\xf6\x82\xa2\xe6\xb6S\xa4 w\xd1\xbe\xa0\xea\xfa{A\xd8\xcc\xb3\x9f\xd8o\xe4\x1f\x9a\x1a\xb4\x8f\\\xb4\xebWS\xa3\x06u\xc8\x92K\x82j\xcb%\xda\xdd\xb3\xb0\x85\xa9\xbb7\xf5\x14dk>\xf4\x82\xc5\x0e\x16\xbcF\xecNh5\x99t\xef\xbf:\xb5\xf1\x01;b\x1b\x9f-I\xe67\xb1L\xa8\x9b0\xdf\xa2\x17\xb7}iT\x1a<\x05\xc6k\xd8\xaeL\xdf\xa0\xfb\xf8`uX\xff\x8d\n\x8dne\xba\xb2rCd\x82\x88\x9bc\x1f2\x1f*\x1fB\x1f\n3\xa8\xa4@d\xcbHc!\x03\xd0\xc6\xb9\n\x8fL\xc9T\x88\xe8\x1c\xc9-p\x18\xf76N\x99B\x8e|\x89\x08SJgQT\xe59\x99\x9f\x00\x9dd\xb9$\x90f\xe9\xceJT\x9c\x93\x1b \xe9M\x9cg)\xc5\xffH\x0e\xd3J\x8b*I\x80\xd0VaE\x8a\"\xbc&\x10\xa6s\x08\xe7sTe\x87 ,I\xb2^T \xdc\x86y\x1a\xa7\xd7E\xa0\x9f\n\xfa\x90\xa4 \x1dD*E;3}\xb1.\xcct>}(\x86\x1f\x9bi\x11W]\nR\xcb\x80\x9f\xfck\xf1\xe4\xda`\xdedz\xf8A^\xcc\x92\xd1\xe8\xc2X\xeb\xc1\xf3\xbc \x0dW(\x91}\x93\xde\x84y\x1c\xa6%\xfc)\xce\x92\x10)\x99\xd6WmJ\x8c\xdd\xb2(X\xe4\xe1\x8a\x14\x9f\xb2\x0f\xd9\x9aQ\x1a\xd1\x1f\xcc\x1f\x0e\x82\x01}\x16!OM\x9c\xae\xa4\xac\xeeW\xec\x0b\xb6bvaa\xa3\xd8\xa5\x8eS\xca8\x90`]\x15K7\xed\x10V\xab\xb35_\xacD\x9d\nW\xf2\xca@.\x0b\xe2tI\xf2\x98\x83\xed\xdd}O\xfd\x84\xb1\xe8\x93C\x1d\x03p\x1e}\xf2\xd4\xd8\x16e\xbf*\xe9M=?\xdaK\xec\x86\x0d\x91\xeb\xf9x\x0b\xc7'\x10\xc13\x10\x1c\xd0 D\xa3\x91\xbe\x88\xe2\xc8\x17\xb3H[\xc2\xa4io\xb6`\xcc\xb1Vt\n\xa1R \xa3\xc2f\x94|\xff \xb1\x80\xf9\x16\x8b\x97x\x9e\xccY\xd0\xef\xd4\x91U\x1c\xfb\"\x9b@\x89\xbbP/@\xa9\xec\x16\xb3,(\x83\x9c\x84\xf3\xf0*a@\x98\x1bi\xf0\x92S\xd8\x9a\xb4\xea\xdf\xe6q\xa9\xd6\xafKD}Z\x18&Iv\xfb\xefa\xb2x\xbf&)7\xbdS\x1bRk\xd4\xad\xb5>\xac\x9b\xcc\xd2\x88\xb8\x0eA\x83\xa8u\xf7r\xae[P\xc3\xd0\xf6\xfd=+\xbd\x14\x138/\xc3\x92\x04$\x9d\x13\xb4\xd6\xc9\x83\x94|)?\xc5\xd1gw\xc9\x86\xd0\xdd\xe9\xb2\xbd\x87%m\xcd5\x89\xf2\xccTb\"\xf3b\x8e\x18\xd7\xbf\xc7\xd7\xcb?\x87%\xc9\xdf\x86\xf9\xe7\x16 \xa9\x18\x06j\x86\x83\xfd\xa4\xa5$\xd5\xd4\x17b)w\xab\xde\xfdfB\x9e?h*sR\x94yvG\xe6\xad\xe1\x0f\x1e\xa2$\xcea\xa3\x15\xe7\x14G\xab |\x0c\xf3i\x8e\x98\xfaeP\x8f\x8d\xd60-D]Acu4a\xa12\x113@\xfe\xfd\xa7\xd0X\x9f\xd9&A\xabx\x1d\xdb)m\\p\xc9\xbf\xea\xa3\xfc\xb1C\x86?\xaa$\x11\x17\x16\xcf\xbe/\xdf#\xe2\xcb}\x7f\x13499\xda\xb3\xea\x8a\xec\xbb!\x8e=\xaetN\xd7\xb56\n\xeb\xa3\x8a7\x1c\xdf\xde\xc1\x9e\x01\x8f\xbf\x0d\xcbe\xb0\n\xbfv\xeds7\xde|\x02\xd2\x80\xcc\xe3\xd9\xb73\x88LZ2\x90\xb5\xfb\x87a\x10\xa7\x87\x1b/\xf0\xdf\x85A\x1c64!\xaci+\xc1J8\x93\xee\xa0\xcd\x19\xe3\xdb\x8f\xa8S\xc8\xb5\xb5U\xba\x1d\xf2-\xebg\x9a\x85\xeec\xf7\xdeb\xaeg\x16$\xee\xeb\x06\x96\x8c\x90>:\xf4\\\xa7\xc8#\xdd\xd4\x81\x92\xd3\xb5\xd0\xb6\xcc\x98\x1dI[\xfd\xe5:\x0e\x8c \xf4\xb8=\x8a#j\xca'\x06-\x08\x838-\xd6$*\xcf\xb3*\x8f\xc8\x90C \x08S\xe9f\xf96K \xc1\xa5\x87&\x12=\xb2Y`\xa4\xea\xa9\x8e\x10\x7ffn\xea\x83CYB\x07\xf5@q\xf3\x9b\x1e \x8a\xbc\xe8\xadm\x8c\x97\xa4\xcf\xaa\xe6\x8b\x8a\xd7;\x03\\\xa1\x92i\xb1\x8a\xe0\xd7,N\xdd\xda\xda\xd7\xc3\xf6\x90\xe2\xcd\xe1\xac\x86\x07p\x0c\xa1\xf8\xa9\x94\xc6\xcd\x818\x06wN\x12R\x12|\xefK\xaf\x14K\x8fF\xf2.\xd3[\xf56u0\xd2\xe2.\x1a\xef\x19e;894\xab\x90\xc1\x91\xf8\x08\xb9\xffot\x0d\x7fo\xc0\xb01\xd66_\xbd\x03\x93\xa2\xd9M\xdd\x83\x03\xcf\xc7\xf7\xe3\x86 \xb69\x98\x18\xaf\xe9\xe4@7\xf3\x0b\x8d\xaeT\x9f\xc9\x9d\xd9\xff''\x0b\xf3\x8b\xcb\xcb\x82$\xf6wx]\x8f[ \xcb\xe4%VX\xb7M&[\x83\x9c,\xa4\xcdh7\x13\x0dk\xe63\xb9\xd3\xf6\x14$\x96\xbc\x0d\x1ar!\x962\xc2\x88\xb6\xbc\x92>\xff\xf2/\xec\xf8\x1cC\xd5^\x1c\xfa\xea\x18\xca\xf6\x0b\xdc\x03\x83v\x1b\xb7 m\x97\xaf\xf3l]\x1cChX\xff\xec6%\xf917j\x12\x8f\xd9\xfbI\xb2]\x91\xc4\x1cA\x94\x93\xb0$\xaf\x12\xb2bn\x15}\x94 \x9e\xf1\xda\x17\xa25\xa2\x84\x9e\xc6*I\x0c\xb3\xe0o\xd4\xc1QZ\x83\xdfNY\xdc/\x1e\x14\xc3\xe4\x10\xd3\xc3CP\x03\xef\xae\xb9\xef\xc7\xc2\xf3!\x12\x85 3\x98\x1c\x01\xa1\xfb\xee\xf9 \x8bM\x03v\x84\x05\x1c8\xaeK\xda\xd5\x18\xf2Q+b\x19\x02\xa5\x8c\x810\xe6\xbb\xb7\xbd\x0d[\xa1v5]V\xeeV\xcc\x93\x11\xfd\x1fOZ\xcb\xb7\x84S\xd05\xe8\xb0\x03\xd3\xf6\xca0Y\xc7\xd2\x83*\x88\x96q2\xcfQ\xa4\xa1\xa1%\x94\xb9\xd2\xdaKx\x0e\x13\x13YQ\x0b\xb3\xe6\xc2\xac\xcd]\xd25bb\xac\x1bx\x06\xcb\x13\xb8\x19\x8d<\x98\xcfn.\xe4\xd1\xcdn`\x04S\x83\xfco\xec\xabc\x9a\xab'\xb05\x13\xee\x15\xc8=q\xe8z\xb5\x84\xe4\xc0\x97\x07\x8dO\x94\x9a\x16\xf1#\x9e\x8b;O\xdeD\\xi\x07\xee\xe8\x0et\x0cM\x08\x80\xe9ig\xee\x03c\xfc/\x0eP\x8a\x9e\x96\x14g7\x17\xc7\xaf/\xcc\xeb0*\xb3\xfcn\x90G\xa4v\xc9\x82\xab8\x9d\xbb\xdc\x07\xc9L8\x93@(\xd75/\xc5E\x10%YJ^\xa4\xf3\x8fL\xdc\xfd\x1f\xa4\x97\xb9n\xe6\x18p%\xbd\xcf\xa0,\xfd\x87\xdf\x03\xfa\x07?\xe7e\xc0\xa0\x8a\xcf4\xfb\xebB\x9f?\x1d\xc0f\xf0\xa2\xaa\x0d\x9brTd\x8a\x86\xdb@\x02m\x9b\xe8\x15n\xbfB\xc1\x03\x0e\xbb}j(\x12\xed\x9a\x8b\xb79\xd0\xa9\x14\xa03\x17@\x87\xdd\x9a\xfax\xc80h\xa9\xc3 \xb6\xde\xec\xe0#\x1e\x97\xcft\x0d\xb6\x0c\xef<\x0d\xdaT\x16h\xc3\xca\x15\x15\x11%\xb6T9P\x02g\xb0\xa6\xc5\xa7\x90\xd0\x7f\x8e\xc5/Z\xd7\x00\x9d\xee6\x84Nw\x1e\xac\x87@\xa7\xbb^\xe8t]C'\xbaz+\x06\x9dV\xf0\x0c\xeeN`E\xa1\xd3\xf5l\xa5B\xa7\x95\x05:)\x03\xba\x1et\xff\xf9\xddX\xfa0\x17@\xe0F\x95\x13\xd3\xc3\x1f\x17\x7f\n\x93xn:\xfe\x9bP\xa4\x8a\xbc\x88\x1d\x10AJ00&\xf7\xaa\x10\xc0\x7f\x80~\xe2T\xd2\x0e\x1f\x98Y\xc0\xdd\x83~\xa9@\x87\xb3\x03c%\xcc\xa0+wS\x8f\"P8\xe6\x87\xb0\x99\x8aq\xec\xfa\xc09%\xa6\xab\x8a\x8d\x04ef\x10\xd3\x0b\xc3R\xae!-H\xf9)^\x91\xac*a\x192\xb1\xc5\x15!\xdcK\x97\xcc\x9dn\x91|\xd5\xdfA\x94\x900\xff\x8a.B\xb3\xfc%\xc5s\xd0\x8c\xbe\xd6\xda4Et\xf9\xc6\x06\xc8\xc6\xbf\xcd(\xd3\xb5\x95\"\x880\xb4C\xf7\xb1)\xf6{\xda\xed\x94r\xa4\xec\x0b\xf5\x9a 9\x87\xd1\xa7\xd5\xdc\x1c\xb4l@8\x92l\xb5\x0e\xbd=\xb4\xdb\xe2\n,s[\x16\x10\xf1\xb0eg\x7f\xcdsHm\xb2\x04\xe9 \x9e\xc9?Z\xc4{\xa7\x80(\xad=\x18\xea\xfa\x03\x06\x95\xdb\x06\xa5\x1c\xde3\xf5\xe7\xb1\x04\x85\xa0w`\xb4\x8b\xca\xb6\x8a\xae\xa6\xa2-\x98\nu\xa6i\xfe\xd1\xfeV\xd3@Q\x0c\xb931]\xfe\xb6\x8e\x8e\xf9? J\xe4M\xd5\xeaY:9z\xe0\x83(K\xa3\xb0t#\xb4/\xc4\xb6}\x88D\xa5\xedmX\xba^\x9f\x96\xcet]\xb7\x166j\x96\"\x89\xd0]\x1b\xd4\xe28F\x83uC\x8d\x0f)\x01\x18\xd5\xfaerb;\xe7\xf8\x01\x85\x92\x91X\xd7\x13\x18\x8d\x12x\x86\xdf\xe0\x82\x14\xb3\xe4\"\xc8\xab\xd4\xb5X\xbc\x8a\xa5\x90\xbb\xec\xb9%\xc0%|\xec\x8e\x9a\xf6N\x865\xbc\x92\x0b[Jk\xbd\x1d\xdeP\x85 \x90\xf1d\xc6F\xe9\xa9\x95_\xf8\xc3\xbb\xb1\x830\xf1\xe4n\xd9\x864\xe2\xe9\x87^\xe2\xe9\xef\x08d\xb5\x83\x0c7\xed\xdd\xc3FC\x80V\x07\xc2\x1a\xa0\xbb\x03\xfb\xec\x8do\x1e\xf4\x05{\xe8\xbc\x89s\xbb*qQ\xa5\x92&3\xa44%%x;\x9b\xbbq\x15\x8b\xd3\xb8\xd6:\x0e\xe2\xf1(E\xc0hW\x03\xed<1`\xe9V5J\x1d\xdba\x01\x9d\xcf\xe4\x04Rx\xd6\"\xceO \xa5\xc41\x99\xa5\xb4+\x95@N5\xe28\xe2ZVr+\x96\xcf\xf3a\x82th\x0d\x05\xef\xef\x01\xa3s\x84\xeeR\xa1~\xe7\x92D2\xaf:=\xa6\xc4&p\x9bs)\xde\x06\xee\x85\xd2l\x1c\x94q\x89\xd6\x1f\xceU\x9e\xdd\x16$wh!\xff\xbb\x89\xba\x94\xde\xf0\xf0\x1bq\x10\xe6\xd77\x0c\x7f@\x1cp\xbbAd\xbe\xa4\xdfE]\x1b\xdf\xdd\xe0w\xf3\xf9OqQ\x92\x14\xdb\xbda/Q\xd9\xc0\xfe^,\xc4\x9f9Ye7D\xaf\xccJ_$\x89xQ\x887d\x15\x97\xe2\xefuN\xd6$m\xf5\xc4\x8b\xdf\xa7Q\xab\xddDj\xae\x97\xa1\x98]\xa8\xabw\x15\xa7\xf38\xbd\xeeVR\xe9T\xeb:\xcf\"R\x14\xf5\xc7\xb1f%\xedh[\x14\xdd\xce\x07x\xc89O\x1c\xed\xb3\xe5\x0f\x18\xd9&\\\x88\x91R\xe22y&\xc8\x81\xb3\xe1\xbd\xf9\xd3\xab\xcb7\xef^\xbfy\xf7\xe6\xd3_\xb0\xc6\x04\x9e\xd8V\x9a|)I\xda\x8a\x8bh[\x02\xa6\x9dk\xd3Q6\xf9-.\x0d[:7S-\x9f]\xe2y\x0d\xed\x04\xcf o\xd6\xae\x9c\xc5\x94\xc5\x9e\xa5\x17LD\x1a_|\xfb+$J%9\x9d\xd9]\xa5\x15\xd4\x8fYj\x8c=\xd35\xac:5v\x063n1\x95 N\xa3\xa4\x9a\x93\xa1\xa1\xcb(\xa7_\xf7\xa5\xbc~\xe0\xc6\x0fC[2D@3\x8c_<\x84\x85\xc7C\xe5.\xfdk{[\x84\xc6ce\xf8\xe7\xf66\xe4\xc2\x12\xbd\xd5\n\x1d_\xca\xde\xea\x9c\x06\xbeY\xc4IIr\xb7\xf3-IN(\x11\x17\xa2\x17\n\xfb\x06\xc11z\x0d, \xd4\xe3\xa740d\x0b\x08\xa1\x88\x96d\x15\x06\xf0F\xbcb\xf1\x0d)>\xc8\x16PT\xd1\x12[(Z\xc4a\xe0\x18\x8e\xe3\x12C\x1b\xae\xd6qB\xe6o\x9a\x95\xab8\x0b\xeb\x88\x018>\xcc.\xf4\x0f^}i\x7f \xd6\xd3\xf8\x01E\xcco\xc3u\x17E\nB0\xc4n\x90\xd1\xae\x80>l\xb1\x8e\x8dZv|\xcf\xc3j\xdak\xf0`\x9b\xf6\n\x8b0I\xae\xc2\xe8s+V.}d\x89{\xfdA\x07\xce\x17O:cW\xf1b\x86\xd7\x94\xf9P\x8a\x9e\x9a2C\x0c\xc3vw\x14\x90\x97\x0c\x90\x13\x83Z\xea\x04J\x86\xf9J\x0e\xbd\x1b\xc6W\n\xaf\xa8k\xff@\x12\x0d\xab\xe7\xc55\x9e\x16\xcb\x99\x90/\xb7\xf8+\x0c~|\xf5\xfa\xc5\xcf?}\xaa\xe5b\xa1`\x19:N\x848\x0d\xea07\xf1\xb5\xef\xf2\x80G\x01\xa4\x18\x97\xb6\x8e\xb3\xb1AyF\x9f\xab\x9c\x84\x9f\xdb\xaf\xba\x9c\xe1K\xada\xbd\xab\xc9f]q}\xa8\xa5/\x19\xc8\xfc9\xcf\xd2k`\x9e\x81\x08AD\x97x~\xce\x194\xe1\xbbP\xb3v]F\x01\xcc^\x81\x02vN\x0c\xd6N\xceM \xf3\xe5\x0b\xc8\x0d\xc9\xefz\x80\xa7\xc0\xb3\xb2\x1bN\xa8\x01*\x0dn\x9e\xd7\x916\x05XDn\x88\x83\xc6\x02\xdc,\xa7\x802N\xaf\x13\xc2g\xc8Mq=\xca\xa0\x95a\x9c\n\x98\xab\xbcm\xf9\xec!wA\x1e=\x8dl\xd3i\xd4\x81B\xb59P\xb8i\x9b\x81\xf4\xae5~q\x8f\xc9-\x84\xae\x01o1\xf4id\x89\x05\x1c?\xd6\x1d\xd3\x14\x11\x83\xcc\xa4\xb1M\x1bj\xab\xf8\xdb \xcaP2Ho\x05\xc6\xe4\x81Om\x16\xe9\x83}\xf9j\xcdl\xe9C\xac\x83\xad^},s\xee\x16\x06\xa1\x9b\xb2\xaf\x9a\x0e\xce\x0b\x8a$\x8e\x88{\xe8\xc3\xce\xa4o(\xdd\x0e\xf5{\xbb\xff+\x1d\xea\x87-\xeb?\x80\xd5\xf9\xb7:\xf7\xfb&?U\xe6\xdf\x12\xa7\x8f\xa3\xec\xb3\x9eC:@/+\xb7=\\7+\xf5\xf1\xa3&F\x1d4z\xfaQ\xcf\xd8\x91\x86\xda\xb8a\xfcJj\x19\xc3\xc1\xc8\xb21\xac`\xeaO8\xdc\x0e\xeeR\x81\x9e]G\xe6C\x1e\xaf\xe22\xbe\x19\xbcL*\xa1i\x04\x1d\xf8\xc2p\xbdX\xfc\xc5\xf6\x05a\xe5\xed#\xaeS\xb2FPW-\x16x\xe9\xcb\xfaG]\xed\xc1\xab\xddaR\xf7\xe0\xd0\x0b\xd8{\xb3@es\x0b\x06\x03\xe9\x8e\x1b(9-s=\x80\x08\x06\xf6\x97\x17o\x7fz%\xc2\xae9u\x82\xaa\xb0\xc8d\xdb\xc3U\x98\x7f\xe6\xa6?\xf8\x93\xc7V;mb%\xd1\xfat\xcd\xdc\x8a\xa7`be\x1ef\xb0p\x9bF\xcex\x02\x8c\xba\xa4\xc6b,\xf7\xa4\xe3\xf9\xf5\x90\xd7e\x95\x93\xf32\x8c>\x7f\xcaCth\xb4\xbc\x11\x86\x9cK9\x01X\x86q\x88\xb1\xac\xa05\xd1EYXhy\xbc\x8c\x0eY\xb2\xf6\xaa\xff\xca;,\x9c\xd8 \xe4HZ\xb9\xd5\xf2&W_\x8a\xb9\x0e\xa3U\xea}\x1a\x81s\x0c\x8e\x91f!h%\xd1\xb7 >l1\x07\x9dz\x1f(\x85C\x9a|$\xa6\xed\xd0s\x0b\xca\x94\xd6\xa0\x84\n\xbd\xf6\x026\xf7\x1d\x96\xcdK]\x95Z\x08>K\xdd\xe9x\xeaiV\xf7B\x01\x8a\xef\xf7w'\xe8\x88\xbe\xbf\xdb\xaa\xd7\xc8\xcb\xb1\xde.\xaf\xb7\xc7\xff\xdd\xe7\xff\x1ex\x92\xc5\xcbc\xc5\x9dv/\xc66(S\xcc\xda\xdc lCip,\xd4\xcc\xd6\xdc\xa9\xa5\x9ed\x00\xe7\xeeY\xeap3;Mm\xa0\xdd\x85!ru\xcd\xc4.\x17\x82\xcf\xb8\xa3Q\n#\xc8\xbd\xe6\x00\xef\x1e<>\xae\xce\xe3\x03\xfapV\xea\x11a\x89$%\x8a\x1e\xc4\x84\x87\xf7oE\x1f\xcax\xb9\xce\xb0n\x10=\x99\x05\x8c\xfdg\xf4\xe4\xea\x9bDO6\xdd\x8f\xbfOPa\xd3H\xf0ZF$N,7v\x91dY\xde7:\xcb\xd0\xe2\xe2]\xf8\x0e\x15\xce#\x14#\x8c\xe1\x18\\\xa1\xc1\xc81OZ\xbfD\xc1.\xaa\xe9\x0f\x10\xdcw@\xd5\x10\xb4|\xd4\x9a @X+\x18\xad\xb7\xba\xcc\x13xs\xf5h\xac\xe6_R\xe5\xb2!\x05\xdb\xf27\xfa\x18D\xd7]\xa6\x0b\xad1\xf4\xe4Nh\x0f\xc3\x1a\x9b\xdf6\x92\xdd\xe1#Ah\xb0\xe1`\x14E\xaf\xfc\x0c\x90N\xd6\x9dw0\x0e\"\x9b\x00\xb1\xa6\x12\xd8\x04\x1f\x0e\xbb.qoB\x99\xded2\x8f\x0dTf\x8f\xaefQ\xdaO\xc6\xbd\xb7\xce\x02\x0d\x1e\x15\xd6\xae\x8f^l\x85\xfc\xe2\xf2Z}\xf0\x0c+\xb62\x06VbNm\x19m\xea>\x16\xbe\xdc\xf0\xa8:\xa1k\xa4\xd7\xb0\xed\xca\x87\xc2\xe7\x99\xf0\x0c\x95(\x1e\x8efcC\x00\xe9\x04\xdf\xe8&G\xd9\xb0\xcc{\x1d\x9a/2+.\xba4\x9fZu\x83q\x80\xcf\x8c\x12xv\xbf\x96\xc5(\"\xcf\x98\x07\x00S\x1c\x17|X y\xc0\xe41\xf2\xab\xc2\x87)\x93\xb5\x9eu\xe3BhF\x96\xd4\xf8\x90q\x80\xfa@\xa0/\x16\xa9\xb1\x1d}6}\xc7Xn\x98\x91U\xbf=\x18\x15\xd0\x8f\xbf\x04\xc3.\x9f\xa2\xeb5y\xf01\xedo\x13p\xfd# \xa3\x92\x07L\xff?\x0e\xcf\x84\xec\x9c\xc0M\\\xc4%,\xcbr}\xfc\xe4\xc9\"\x8c\xc8U\x96}\x0e\xae\xe3rY]\x05q\xf6$\xa7\xdf=\x99gQ\xf1\x04?\xde\x99\x93(\x9b\x93>\x81\x9c\x999\xe6\xa3\x91\xc7,\xd5\x9d\xed0\xbf.f\x17X\x8f\xa4\xb4\x89\x9f?\xbey\x99\xad\xd6YJRY\xaf\x96\xc3\x08&\xba\xf2\x8c\xb5\xa1\x06\x7f\x17\xa2\x89,\x1f\x1e9\xbe\x89\x1a_\xf4\x87\x8b?i]\xff\x18\xe4\x10\xee\xba\xaa\x8e\xc1\xf4\xb83\xfa\xba\x0fq;\xacz\xdcs\xea\x06\x9d\x1b\x89\x82\xb2q4\x8f`\xe5\xebb\xf1I\x87\xf7\xcc <\xac^\xb8?\xb4\xff\x12\xeb,\xb7&\xc1\xb78(\x97a\xf9\x11[+\x98\xd8E)z\x1d&\x05Z>\xba\x18H[y\xf7)\xaf\xf8\xab\xb1\xfe\x8a+\x17r\x11\xcfW\xfdn\x19w\x9a\x8f\x88\xb9)\xf9\xf6\xb46^\xf0\x03>\x04\xa5\x9a\xfdO\xe0\x94\x1f\x94\x8d6P\x94v(\xa5\x9e|\xbf\xa5n\xd7\xf7\xf0iI\xe0\x8a 7W\xd9\xbcJ\x08,\xf2l\x05i6'\xc1\xaf\x85__D\xee\xf4\x1ah\xdf\xeb\xcd\xfd[X\x95\xcb,\x07\x80\xd7$\xcf\x8a\x02^\\e\xd5\xe7e8\x8f\x7f%Kx\xb6\xc0\xc2\x7fc\xff\x04Y~\xfd\x1c\x9e \x88\xd4\x94\xb5\x1a\x15\xf6H\x8aA\x12{\xf9\xa4uu\xb9\x1c\xaa\xc5?CC\\\xb4\xb2\xe4A\x93X\x0f\xef\x94\xf2\xb2\xbe\x10\xed\x98+\xd0le\x11|\xfa\xcb\x87W?^\xbe\xf8\xf8\xf1\xc5_.\xcf\x7f\xfe\xf0\xe1\xfd\xc7Op\x06\xd3\xc9\xde\xd3\xbd\xc3\xdd\x83\xbd\xa7p\x0c\x93\xf1\xd3\xdd\xa7{\x93\xc3\xa9\x96\xef\xd6\xd2ah\xc5\x95\x94\xe2\xa4\xc3yF_7\x86\x17\x1f\xc3\xf4Z\xf0\xc9\x14(%\xf1\x1cI\xd190Os\x865:\xcc+l\xb3p\x85\xbd\xd3\xcfqZ\x1e\nCc/\xb8\xbcDl\x7fy\x89!,\x1a\xf9\xea\xb1b*\x82l7o\x00}\x9c\xe8a\xe7\x18\x8c\xe5\xb8\xd3\xa1\x85y=\n\x1b\xc5\x06\xc2\x88\xcb5O\x80\x07\xc4\x97\x95 \x85\x9an\xa0i\xba\xbd6H\xde\x1b\x14\x0d6\x12\x0b\xeb\xb7\x15\x10\xcaN\x89MZ0\x1c\xc9=\x9d\x8b\xda,\xb9\\\x12\xe6\x86\xb2\x88\xf3\xa2\xac\x11?\xac\xaa\x02\xedgB(Z\xd1j\xe5G\x10A\xf6x\x08\x0f\xb63\x105\x01i\x0cr\x1c\xcb\xd6Db\xfd,\x0c\xaae\x0d\x89\xd9l\xe8;!\xb5Q\xe7\xcdm\x87BnR\xdf\x91~\xda\x9c\x89\x16\xcf-W\xe5lo\x03\x91\xcf\x83\xfc\xae\x1dK\xbb\x83\xedFW\xbf\xe0\xea\xae$?\xe1\x89\xf6\xd1\x0co\x0c\x98\xeb\xba)\x86g\x8d4K\xbf\xaa\xdfe\x8bEA\xca\xef\xe8\x11\xc8*4G\xbf\xca\xaat^\xd8vW\xef\x936\x0e#p1\xf7\xf0\xd8\xb3\xf6\xc3\xee\xdc\xf0~0\x00A#cI\xa5\x00n\xa7<\xf0o\x0b(\xd4F.\xd6*x\x81\x8fM\xc5t\x99\xcd#\xe9\x04L\xa4\x0b\x10\xd1\nk\x06H;\xaf\x8a\xc1\xd0O\xd9\xfdc\x93R\xb1\xc5\xd8tx \x1a>\xc7\x05\xad\xf3\xc9\xdf\xdf3\xe7P\xa7*\x17\x87][\xbfU\x04q\xf1\x8a\xc3\x0d7\xb58`\x7f\xe7\x08\xd0\xe2H`\x83!\x056\x94\x1a\xf6\x98n\x12H\xf8t\x0c\xf70g\x1bg\xf6\xd7\x02\x8e\\]\x16T\xa8d\x86\x8e\xb7y\\\x12\xd7\x02U\xd9'u\x96\x02\x97\xf9\x042#\xfc\xb1\x0f\xb1\xf7\xe8\xed\xf2\xfaL\x1f\xc5C\xd7\xb2\xa8\x15\xba\x141uH\xb3j\xd5\x08\xdc\xc3\xd2%\xc2\xe7\xc9\x166c\x08\x906\x9a]Iu\x82\xb8\xf8SLX\xda\xfdv\xb1\xc9\"L\xaa%\x8f\xb4!0\xdb\xa3\xad\xa9\x99-\xd5R\x0e\x11\x1dK\x1caX\xe2\x9b:\xd9f\xd7*pj\xb3\x1eIW(\xc2\x1c\xc3\xfb\x9d\x9cx\xb5\xa2\xcf\x8a Q\xbd\xe5\x84E\x14\xc7\x8eY\xc9\xc5j$a\x19\xa7\x93\xce*Wq\x1a\xe6w\x96* )w\xcd\xe8\x845\x82d^W/U\xb9\xd8\xe9\xac\xc1\x08\xed\xdeQ\xfc\xec\x96\x9eu\xc1\xa1\xe9.*\xa6\xdd\xe3\x89\x8a\x9d\x9e\x1a\xe5br\x90\x90\xbe:;\x1d\x95\xa0\x19\xf7\x14\xbe\xef^\xc1%\xf9\xd2\xdfJ\n\xcf\x9f?\x07\x83?\x114\xdb\x19\x16\xe4`\xaf\xbf\xa9\x1f\xfa\x16\xb2\xd37\x1c\xa0v\x0c\x19\xba1\xc0\x990\x96\xac\x86Ph\xf6SvK\xf2\x97aA0\x03\x19F\xa1k}\xaa\xebR\xcd\xe0\xeb\xa6\x8bc\x11w\xab\x9c\x11\x03\xec\xe7F\x14\x14\xfd\xf9\x02 \xe6\x83:\xbd\x93\x98*\x8b\xfe\xb8\x01\x01eM1\xf2\x05\xdb1l\xa3E\xdc\x92R\xee\x10\x85\x81\xdc?\x0eyNx.K\xe4\xce\xf0\x8d\"\xa2\xa3\xd8}\xa7.9D\x90F+Ie\x1ekp\x94\xfa\xdcB\x82\x852\xc6j1G\xce\xa5\x1ccQ\x88\x04D\xa5\xfa\xe5\x08i\xfd\x94\"\xc0\xb2#\x88\x82\x98e\xdc\xb9\x0e\xc0C\xe0\xc8]\xb7OF\x13\xf6h\\\x99\xc2J\x91\x86}\xda\x99\xc01\\k'\xcarB\x8c\xc2'\xde0\x81m\xa4u|\x8b\x9c\xc1\x86t\x1b\xf1\x85d\x10\xcac\xee\xc0\x19\x1e\x86\xae*\x8d\xe5\x0f\xe7Z\x8d\x95\x93\xb0(\xdfX>\xc0\xb9c\x12%\xfb\xec\x8d\xbc\xcbM\x98\xd4\x84\xbd`WD\xa0\x8a\x9c\x93W\xadP\x14\xe6\x1b\xad\xaf\xbf\x05\x98d,5\x8b%\xbc_(\x1d\\s\x8dB\xa2\x82\xcd[,\xa5\x16`\"\x05\x86\xd1\x18\xffM!\x01'\x04s\x0d\x8c\"=\xc4\x91\x1b\x17Za\x01\xc7ej\xd1\x8eTf\x95\x17\xc4,*\x91\xa0\xd8\xa7L\x18\xd8\xfc\xee\xbdWt\xa5\xa6>\x84\xf0\x04\xff-\xf8\xbf)\xfek\xb8o\xad\"M0k\x1b(\x1f\x06\x0b\x17U\x89\x8c]\xc7<{\xee\xcfo\xd2rr\xf0\xc3+\x97\xc0\xf7r\xb6\x11\xf1\x98\xef\xb9\xd5&H85\xda&\x8d4\x1d\xaaaN \x83g\x10\x9e@6\x1a\x99\x992\xe0\x9d\xe1\xf42\x0f\xc7\x1fQ\xf0\xc1C_-8\x1c\xce`\x07\x16\x9dr\x1d\xd1R\xfd\xa1\x88\xd2\x9dy>\xfb\x1cF|\x81\x8az\xdf\x16tA\xacMr \xbb\xc3\xc2\xd7\xb2\x163\xd89\xe5\xa3\xf1\xf9*X\x80\xb3}mR\x18A\x01\xcf!\xac1I\x08;P\xe08\xf9\xaa=Gf.\xdb\xd9\xe9\x9arM<'<\x88\xed\x9a\xf1\x80kx\x06\xc5 \xac\xbb\x16\x1d\x94\x85\x87\x11\xac=\x16\xa4\x97.\xfe\xbaw\xa5\x81\x9b\xc0\x98\xfc\xbb\xf5\x07\xe3\xeft\xd62\xcbq\x80\x0f1\xa9\xb7+3\xd6\xb3j@vt7k3\xe0[\xf5h\x07\xe8\x061o1J!\xdc\xdf\x9b\xf8\x18\xa1\x04\x97\x90\xb6\x81\xe2\xcd\x05-\xc3\x9b\xa3\x90\xe79\xc4x\x0chqLq\x01\xfea\xee!\xeb\x85\x9d\x19\xfc+L)/7\xb68r\x0bu\xe2\x92|\xe9P=\xe5\xf0\x1c2x\x02\xd3zh\xf8\xabK\xfeP\xb1\xb3W\xb1h\x87\xa3Q\xd5\x05>(\x9aX\x87yA\xde\xa4\xa5K\x82\xa2\xba*\xca\xdc\xa5|B\xe5\xc3\xd4\xf3ar\xd0!7g\xd4\x9a$(\xac\xccu\xcb\x19\xbdi\x98\x8a&\x1c\x00\xf4Dc\x83\x0e\xcde\xcf\xa1\xe1\x8d\xfd\xd5\xfd\x19s\nK\xc7\xc2C\x95\\\xdb\xa0\xd3\xd6\xd3\xd5\xd0\x9e\xec\x06\x03u\x9b\xb2\x11\xd2\xecB 8Q\xb3\xf2L\"\xc6\xb3\xed3\xc1Q\x19D<\xe4\xc4\x8b\xd2M{$\xfam\xc0\xf7\xc0dy\x9bL\xfav\xd8\xa4\x95\xb5\x19\xd4\xf0\x97a\x0d\xff\xd5\xfda\xf3A\x9f\x0fm{\x90VC\x0e\xec\xc0\x83\x93\xf2]\x93\xaeZ}\xb0\xb6\xb7a\xcbu \xc5NS\x0f9\x02~ \x19+!\xed_\xc5\xf9M\xcaO\xc3!\xcb\x84\x93R\xb0\xb1\x7f\xe0C\xc6\xb6=\xf6\xea?m\x9a<+H~\xf8\xda\x03\xff\xaa\x8b\x9fUY\x08\xf4\xe9TXL\xf4\xd5\xa7<\xc8\x0fw%\x91<\xa2[\x85\\E\x85\xfd\x0c\x1b\xd7\x8b\xaeq\xa5RL\xa1\x9af\x1c \xb2\xc5\x10\xf3\x18\x83\x1ab\x14\xddv\x81\xcd\x8c\x85\xf8\xf0E~\x93r\x16\x1bLS\xc5\x83N$\xc6L\x89\xe2A#V\xcaJ\xef\x1e\xc1\x19\xec\xc11\xfb5\xdd\x853\xd8\xe5\xbf&G\x138\x83)\x1c\xdbD/\x08\x91a\x04 \xad\x87[|\x83\xe1Z\x8c\xf8\xc5#\x8f\x8f\x81\x05\xf6kz\xe1kS\xc9p\xf4jY%\xcdh\xb2_\xcfh2\x85{p\xc5\x9c\xe4)Vt\x8a\xd3\xf1\xdeS\xfe\xdd3\xd8\xdf\x9f\x1e\x1dP\x92\x88\x92\xb3\xfbOw\xf7v\xbdo:\xff\xbd\xc7\xcf?\xac\x7f\xedn\xb0\x1ajYhY\xa1Cm\x85\xa4%\xab\xd4%\x0b\xe9\x92\x1d\xec\xef\xef\xee\x03\x06\xf4x\x06\x93\xc9do2\x99J\xcbd\x9c\xa2\x99$\xae\x8d\xb1(_\x84\x9f\xd3\xb6w}\xbc\xc9\x18tl!\xf7\xe7.(>\xa0?\x0f|\x11\xb5x\xc1\xc4\xa8c\xd8\x86\xc9x\xba\x0b\xf7l\x1397\xb3\x7f\xb0;\x1d\xc3={\xb5\xcd\x0c\xc2\xf9w\x1e\x05T\xa3SH\xda\x10\xdf\x06\xa5\xfb)\x12A\x8c\xd8\x15 \x14\xe3\x14\xbc\xbc\xafI>C8,\xee1\xc2\x13\x85\x1b\xf5\x16 \xe9.\x1c\xc7\x0e\x18s\xb32\x10\x04\xf4\x16\x06\xd3\xdcXz\xc0`8\xba\xc9}\xa6\x9a{\xdfCD\xa5\xedEv[\xe8S\xfeE\x82\xda\xb7\xbd\xf0\x81\x04\xe7Iv[\x97t\xef\xc3\xa8l\"\xab`,\xdc.\xbbBT\xdd\xb9#S\xa0\x837\xef\xce?\xbcz\xf9\xe9\xf2\xed\x8b\xff\xef\xf2\x87\xbf|zuN\xcf\xd3\xd8&\x8b;U\x93)\x9b\xcd\x82\xcc\xe5=\xb1\x13\xed\xf9\x8cn\xa4\x88o\x92\xc9\x92\x9e=G<\xb5\x02M\xb6J\xb2\xe3\xb4\xba\x96Y\x00\xd8\x81\xa8\xb3l@8H\xf1\xf0Q\xed\xb5\xe5G\xe21\xc3\x8e\x07\x1f\xf6\xa6\x9cVZd\x99\xebY\xc5\xa1%e\xc8\x98\xa5\xe9\xf6\xb6p\xeb\xad\xcb\xdc\x89\x0f\x13OR*\xb6\x8fjg\x0c4h\xe6\xb0e\x90\x9d\xa8\xe7\xca\xf5\xe8\xc9\xfa\xfc6\xfc\xc2-\xe4P\xc5L\xcf\xd4:\xcb\x92\xf3\xf8o\x14x\x1cN\x8e\xa6\xb4\xe82\xac\xae{M\xb6\xc1\xb6\xb1\x85\xe2\x0c\xa3\x1fo&\xd8\x1e\xe0u$\xb5\x1f5\xe9\x05\x0d\x16\x98\x1dBjW\x1a\x8b2F\xe3\xb9\xa237\xd6\xf1-\xf6\x93<\x9c\xcc\xf66\xff+@{U\xc2\xf3\xb8\xa9e\x17LbF_\x99\xc3\x9c\x16\xbe\xd6\x8a)\xe0)wh7S\xa3\x9d _\x1e\x98\x1a\x01\xc1\xcef\xab\xbf\x81\xed\xa7\xf8\x02Y>D4ca\xd6$\x1bB2\xf3\xbe3\x93\x05`\xde\xd4\x0f\x161\x0b\xea\x86\xc6\x86j\xa1Tb\x00\xf0}\xa7\x05\x17\xe1\xe7\xb4\x08\x17\x83\xe3\xafX2\xb5\xe9\xcdQl\xf1-\x9a\x94\"\xac\x0cjk\xcbmb\xa1\xdd\xdf\xc3V\x19\\\x8a&\x0c\xadG\xd9j\x1d\xe6\xa4\xcf!\x1bd\xf3\xca\xdar\x03\xdb\xd7\xf4QF \xd9\x8b:\xba\xb7P\xac\xb0/\x8c\xb6&\xcc\xf0Eu\\\xee2s\x90\x15{\x8c\x0d'\xf5\xaf\x98\xc5\xa1\xcfdN\x92\x99\xd2\"k\x98Q\x86\xde\xe2t\x8b\xc3\x98\xc5\x17xD\xc9,\xbe\xe8B\"\xa9\xe0\x1cY\xff\xad\x0c$\xf2c\x97\xddZ\x89>\xccw\"\x94zh\x8e\x04g0Q\xe2\xe1Bs^\x84\xf9k\xef\x89\x11l%W\xfe\x94-\xe5\x8fy\xc2}\x06\x06\xdf\xca\x84\xe3\xbf\xc1\x1ee\x80\x8d\xc3?\xa8\x01\x88) )\x0c1\xb3\x18L'\xf8u\xe6\xd5\xc1\xd0!\xb3\xa6\xbc\xfa\xceI\xe2\xa24\x99N\xf2\xe0{\x90-\x04P\xb0YQZ\x0c\x1f\x04\x01m\xa2\xb1\x11>\x98[S\x02$\x18W\x0b!\x0ca\x10\xa4C\xaa\x8b!\x89f\xe9\x85\x95\xdd\x12r)\x05=P\xbch\x86;f>IO\x1d\xa5\x8d\xc2N\x9cW\xdc\x18\xc5\xce\x06\xca \xbc\xfa\x9d\xf6\x8f>\x153\xe6FM8g|E\xf4\xd6\x9e\xb3\x08\xcd\xb9mEg+dg\x8fS\x98\xfb\xa0Pz\x12\xfa\xdc\x1a\xab\xef\x8a\xdbp=9\xe8\xf3\x0c\x17\x0c\x0e\xc6\x8c\xea\xd2\x13\x95F=\x91l\xae\xc9GRP\x12\xbb1\x1d^UI\x19\xaf\x13BWpr\xb0s\x15\x97F\xb4\xa8(\x1a\xc6'h\xbe[\x9e\xb0\xe37\xf5\xe0\x86\xbb&\x11Jm\x8dZ\xd9KA\"\xd1e\x17M\x10\x8b\xa8.\xcb\xee\xf4\x9b.\xcb\xdeW.\xcb\xee\xf4Q\xcb\xb2\xd7Z\x96]\xcfo\x8a\xe82\xb1\x7fLZ\xb8\x0dV\xeb`\xef\x9b\xae\xd6\xe1W\xae\xd6\xc1\xde\xa3V\xeb\xb0\xb5ZO\xcd\xabu\xa0\x15O\xd9?\xfbZ\xf1.\xfbg\xef\xf1kk\x8a\x1f\xd7\xb5\xbah\x9e\xdc\xb5\xc2\x8a\xa6\xa3\x8e\xaa\xc5~\xb6\x02\x08\x9c\xc1\x0b>\x9b1\xa5\xcc\x07\x84\x87\x92\xc7\x93wh\xf2\xe9F+\xf8\x07\x8d`\x98\xcd\x99\xb0\xfa\x1a#\xdb\xf4\\\x9eO\xe3Q\xe2\x0ck\x17\xfd\xa6R\xbd\x91\xda\xd4N*D3<\x8a7\xcda\xb69Y\xc1\x10j\x15\x06Q\xac\xe2\xe1\x9d\xbf\xd8\xa4\xf3.:W<\xbc\xdd_7i\xb7\x93:\x86a\x14\xb2xx\xff\x9f7\xe9\xbf\xd7v\x18\x9a\x86_m\xd2p\x075\x0e\x83(r\x18H\x95\xc3&\x9494\xb3y;l6\xbd\xc4:4v\xd1F\xc6\xfag\x1e\xf9Rx+\x1e\x83\xcd\xbd@~J\xe6\x8e8\x02\xc7\x19j6\x0dF\x9a\xec\x81\x8b\xe4\xd9dmA\xa5T\xa0N\xfeZ\x85Iw`\x170J\x1bzd\x0b\x122\x146\x9a\x9d\x88\x87\xe3\x80\xfb{\x0e,kY\x88\xd9/\\\x9bE\x9c\x16k-xr\x17f\xb2)F\x98\xffRK\xca\xdf9p\x81\x9f\x9es\xb3\xe9\x9a\xae\xa8\xddy\x10Fr\x7f\xc9`\x15\x96\xd1\xd2}\x12\xfc6}xr-2l\x80#\"\xe3\xd6\x8d\xf1\x10\x80,\xc8L\x10\x04\xe0x\x9e\x0f\xce3No\xd4\xe1r\x9e;]\xebb\x91'\xf5\x1a\xb5\x7f\xfb\xad\xd6y<\x05\xb3\xea\x9e\xdb\x0c!\xa2v\x84/\xc8\xb1^/\xaf\xed\xb6\xb4\x17\xcc\xd6,naT\"|\xdd\x11\x03\x8bv\xef\xefQ\x80\x83/b\x1d5\x9b)>\xee\x8f\x9e\xd3\"@\xfbh\xdb|sx\xce\xc7C\xe8_\x9dnBM\xfd^\x17\x02\xad1{-\xa4\x03|H\xeb\xbf\xf2\xfa\xaf\xb8\xfe\xab\xb9|\x83\xc4{\x19\xba\x0e\xec\xd0\xd3\x83!\xcd`\x87\x1e\xa7P\x96\xe8e>T\x1e7\xdf\xc0\x00\xc8B/\x18s\x15\xacb\x99\xc24\xbb\xe3\x13H\x98!\xedh\x94\xd8%\x80\xd1,a\x12\xc0\xc5,\xe9\x94\x00f\x18\xbc,\xe1:sZ\xdb\x0e\x83\x1f!\x01\xcc\xe0\x19\x1a!\xa3\x04\xb0\x82g\x90\xd9%\x802\x94\xc2(\xc2C\"\xbbI}q\xe3\\\\J\x91%\xd7.Ao[\xf7o\xd4\xd9\x9d\x1aR\x03\x03\xaavu\"\x99\xfc\x7fmG\x93\xce\x8e\xd0C\xdf\x0c\xc7l@L\x8b\xb9Y\x93\xb8L|$\xddt\x9f\xf3_\xadVj\x0f\x14\x1d@\x99\x83\xa6\xe4,J\xf9F\xad\x9b\x8f0\xc2\xe0\xb8x\x1d\xa7\x18\x97\xc03\x04d\xe1\xae\x92,r\x81p\x8c\x10\x84\x87\x0f,P\xc7\xcc\xe7\x91t.<\x16\xc9\x11\x92,\xbd\xa6\xfc\xaa\x88Fk\x0f\xa8q\xcf\x00\x85\x18D\xea\xc1\x19\x05\xcc\xac\xd8\x08\x899\x07Ay3\xd9\x9f\x89\xd5\x1db\x94_\xdb\x18K\xa8pGO\xea\n]\xacU,98\xc9\xc1{\x9e\xd7NM\"\xe2 \xe3\xef\xf0\xafA`_r\xeeeg1\xab\xca\"\x9e\xd7A\xa9\xec\xf1I\xf2:\xae\x805^\x86\x02^U'Q\xabJo\x08\xff\xc5/\xdbJ\x0b\x94c\xde\xf2^\xd6k\x18\xdb\xc5\xfb\xbc\xdc\xa0\xcf>\x8e\x8b7y\xb5A\x93_\xab\x8a\x80\xa6\xdb\xdb\x0d\xba\xed\xe5\xb1x\x9b_6h\xf3\x1fN\xd9q>h\xf0\xbd\xdc\x14Z\xf3o\xc4I\xd9,u\x01\x98A\x13s>\xd5\xbd\xa6\x98\xc2\xb1\xdf\xf9T\x97v\xfd\xdf\xf3\xf7\xef\xfa8\n\xbe\"\xe6\x1bJ\xdb9\x06\x11\x0c\xc4\xccr\xcc\xc32<\x06\xdd\x93\x0e\xe9\xa3&oFp\x19\xe6\xb9\x88\x0d\xe6\xf7\xc3R-\xf8*\x05,\xef\xe1\x14\xf6\xc6G\x07\xb6\x90q\xbfv\xe1l!A3I\x92\x1ec\x16\xac\x98\x03\xa3\xce\x97\xd9\x8c\x992@\xa2\xc1)js\xed\x0c\xe40\x87\xde\xcf\xff\xa8S\xfc\x16\x93{3drv\x1bDw\xcb&\xf5t\xb78r\x95\xd8\xa7\xbc\xc1\xb2\xa6+\xa9,\x82\xe3\xb0\xfbG\x98\xab\x1c.F\xe61}\xd3k\xb7\x9ce\x1dS\x8f\x07M\xfdm\xd7\xd4\x15St\x8d\xf1\x90\x877f\xc3\xcbk=^\xc659\xb1m\xd7\xf2Yv\x01#\x98\xee\x1f\xc0\xf7\x90\xcf2S\x90X\xd8t.\x9f\xba\xe6\"4\x12\x13\xd4H\xb0\xd8\x18\xf6H6\x0e#\x01E\x04\xef*NK\xbb}\xc7\x08\xc9 k\xdc\xb7O\xf9]\x9c^c`\x13Lj\x00W\xe4.K\xe7\x82\xf6ak6\xd0\x0b\xf7\xa5*\x82@\xa7\xc8\xc7K!\xbes\xd8\x18\x8ca\x80\xb8\xb0D\xc4\x0f\xb1i\xb2 \xba\xa8\xf1\xe3\x9fY\x03\x03\xe9\x91\xfe\xf4\xd8t\xb6\xe615\x88$t\xb0\xc7\xc1\x9c\x93/ \x8b\x17\x06\xae\xe8\x87\x1ef\x88\xd4>\xfd\x84\xdbS\xef\xe3\x86\x9b\xf5\x92\xca\xed\xd5\xadud\xaf\x17\x1f\xa6\xaa\xe1\x0ewG\x8b/\x00\xf5\x10\xdb\x18\x94\xe7\xd938\x84\xef)\xfd{\x061\x1c\xc3\x04v \xf6<\xb4\xd16\xbc\x184\xe1\x8f\x1bMxoz\xb4wt\xf0tz\xf4\x8df\xbdg\x9f5iOk\x17\xa7\xc5\x16c\xd0\xe4\xde\x0d\xbe\x1f_s\xb0lG\xb5\x03\x9e<\xfa|\xfe\xa4\xcc\xc88\x9dZ\xaer\x7f\xcf\x16`\xec\xb3\xa5\xf6!\xe6<\xae\xdc\xc6t\x97\xbd\xa3+\xb07h\x0c?>z\x0c\x87\x961\xecO\xd9;:\x86Cm\x0c\xf2\xafB\xa7\xeb\x86\xd8\xef\x08\xaf\xb8aJ\xeaS\xf8\xaf\xff*}=\x08&\xe1\xb9O\xfe\xeb\xbf\x88\xcf0\x05\x0bC9\xa2X\xbb\xbe!\xa5\x888RR\xc4^\x17\xe5^\x13\x92\x8c\xe5\xea\x92\xbe!\xe2\x1bR\x7fC\xa4o\xca\xba\x04\x93\x1d\x1b\x03\x985:\xcf\xda\xea\x1a\xd7\xc2\x1a s#\xf9IM\x81\xc1\x8e\x9eeE3\x86\x11\xec\xec\x101\xef\x13<\xda\xe3\x9e\xe9\xd2\x0f\xbe~\xc2\x87C\x00\x02o\x90\xd4s\x9c\xf8\x9a\x82\x83o\xdc\x90\x1e'\x07\xedc5\xa8\xd3\xa9\xa5Sn\xe9\x81\x8b2\xb9@\x9c?l\x1c\xed\xcd\xfe\xbaq \xb5\xa1\x0cf\xc88v\xa7\x8f\\\x8f=}\x1c\xae}A\xe4\xa2)\x16\xb18\x7f\x93\x83\xa7O\x9fN'\x94\x8b\xa8\xdf\xef\x0e\x1c\xf6#\x97\xaf5\xec\xd6\x18.D\xe2Li\x06\x93\x83\xf6\x14\x94Y\xed^t\x8a\xf0\xe9\xb0\xff\xd7A4x~\xca?\x9fL\x0f=.\n\xdf\xe1\xb4\xe3:\xbbu)\x95\x00\xdf\x03\x06\xf3\xec\x05\x07\x7f\x0f\xf0G\x94\x85\x91`[~q\x82\xe4e\x1b\nf\x1a\x14\xcc\xbb\x17)3,Rf]\xa4l\xc0\"}#\x90\x89\xbe\xd7\xf5\x89Gu\xde\xf7\x80\x11!v\xa4{0\x11\xa9\\\x07@\xd7\x0d\x80\xab\x15\x9a\xb5\xd7\xf1F\xf8UX\x81\x8bu\xedw\xa7O\x0f\xe8$S8c\x8c\xd0x\xf2\xf4`\x0c\xf7\x90\xc2q?\x05\xb2\x01\x8c~\xf4t\xd8$\xee\x15\x10\xfe\xfbM\xe7\xdb\x81\xfa\xcd \xbd\n'i\xd9to\xd0p\x87\xad\xfe\xf0\xe1b\xcf\xedA\x0f\x00\xee}\xc3}\x9dd\xa1\x01\xba?n\xb816\xd9(\x1a\xb6\xc6\x82\xeb\x1b4\x8co\xb5j\xadaL\x86\x0e\xe3\xc7\xac\xbaJ\xc8#\x97\xe3\xb0w\x1cc\xc1\x80\x0e\x1b\xc7#\xd7\xa3\x7f\x1c\x93!\xe3@\xe6\xd9\xca\xcdX\x848<\x9d\xa7\x82\xe0\x98\x15\x0b\xaam_\xea\x06\x04:2I=\x96t\xcc\xe6\x88\x12\xdbc\xfce\x1dN\x1fx!H\x13r\xba\x14\x94D\xdaB\x93\xac*#\"N\xa1\x84'\x1039\x90\x15\xbc\xd1\xca\x9dP\xac^I#\x99\xf0w\\\xc9\x14\xabXW\xd3`\xa4$\xad\xa6\x10\x9f\xd5+\xba\xb3\x13c\x808N*\x18\x964\x16K\x9a}\xb3%m\x11\x15\xdd\x16,\x86E\xd5\xd7\x92\x02\x8b\xfd}\x1f\xf5(\xd6|?\xb8;M\x06\\\xb7\xf4\x04\xb4\x96O\x197\xf9\x1f4\x11\x13\x05\xf2\xd5s\x99\xfaLr\xdc5\x9b3\xc3\xf5\xf0\x9b=\x9b\xb0=C\x11)\xa5\xa9>(\x1dl1\x1b\xfb\x91\x166\xd2>\xc9\xc1\x94\xf2\xef8I>\x1b}\x92|\xee\x86IN6\x9a\xa4\x89Z\xf9\xeaI\xee\xf9\x92H|\xd0L\x19\xcd\"f;\xdd\x93\xa6;m\xca'\x07\x96\xbd6\x1cg\xba2\x1f\xcd\xdb\xdfI\x16I+\xf3;l\xff\xe6+cY\x95\x89eU\xa6\xe63\xb3\xdb\xbd2\x93\xc1+\xb3!\x8a\x15\xd2cyY\xb6\xac\x06G\x02\xd4\xb7\xd0\x03\x86\x8e6\xcbN[\xb8%f\xa8d\xc7\xe0\xe6m\xb6\x07C\\lF,=Qz\x1f\x89\xc1+\x19\xdd\x08\x917wJb\x7f\nsL\x86\xdb\xe9\x84.\xf0\xcb\x10C\x14\xf9\x1a\xdew)\x96\xaa\xe0\xf9s\x18S<\x1a~\x13|\xb5!\x05\xf0?e\xa3;\xa8\x88\xaf\xdal\xb1\x17\x12\x81\x915\x04\xc6\xc6;>\xfa\xfb\xec\xf8\xefB\xa0L\xa6O}\xd8\x99L\x0f7\xa7Q\x14\x1d\x12]Z\xe6\x930\xf9\x1a\xfa\xe5w$_v\xa7O\x0f\xe8\\Q\x860\x0c\xb4\xff\x8e4\xcc\xefH\xc2\x04_K{0`\xca\xdd{;\x80\xc4QH\xa2\xaf\"h~Gz\xc6\xbeD\xea\xf5U\x8c$\xc4-\x1e\xb0\x8a\xff@\xc4\x8fE\xfe\xd4\xbd\x8a?i{\xd6\xe7U\xd1\xf4\xb4\xe9~i=M\x06\xf5d\x93\"uw\xf5\xe3c&e\x13\x14m\xd4U\xef\xac\xa2l}\xb7\x19\xdd\xd2\xa4\x9b\x1c\xa3Cd\xed\"\xd8\xd8\xd5\x97\x9a\xa7\x97\x94\xa5\xa41E\x90+\xd0\x0fI\xdd\"Wq\xe45 \x88\xce\x0b\xcc\xfb\xb2/\xbdS\xdc\x8a\x84\xd2\x0cP\x1eVO\x13\xa4\xcb\xf0\xa6\x0c\xf3kR\x9e\x97a^\xf6gC\xad\xcdx\x80\x19kj\xc30\xf7PdU\x1e\x91\x0dz\xc8\xbb\xc6\xcbZ{\x95\xce\xfb\xdb\xcaU\xe7\x8bz\xf5\xd5\x1d\x95\xec\xaf\x08\xc6^\xda\x916Jy92Z\xe5\"A\xcb\xf4[\xb99n=\x12\xc8\x8d\x1b*\x06]\xe6\xcaA\xec\xb1#$M\x0c,]\xc2\xe4\x04b\x9e\xd5`g\x07\xcd\xc2b\x18\x01\x03\x92\x14\xd6\xd1_\xa6\xb8/\xb5\x93\x11eA&d\x17X\x18\xaf\xcd\xb2\xfe\xb105\x9aY\xda\x06\xfd\x1b\xf3\xb9\x14\xa4\xac\xf3\xb8\x94\x8a\xa9N\xca\xcc\x9e2\xcf\x9c\x0bS\xe8\xfd\xba\x00\xc1\"\xc6\xf4\xf6\x1b\x00\x02\x83\xd3\xd5\xc6\x99\xadEz\x02\x0c\xa9\xc1\xd1\xa6vC\x8c\xe9s%\xb8\xd0\xfe\xc4\xe7Y7\xfa2#\x81\xec\xe2$\x07,\xb7Y\x1e\xd1\x87n\xe9t\xff\xa0F\xd4\x96\xf8h\xf6|\xabz\xb2\x19C><\x9b?{\x9d\xf1{h2o\xcb\xb2c\xbfj.\xe0\xdc\xe6Ul\xf3\xfch\xf5\xc7s\x97\x98\xf2\x9d\xf3\xc5b\xa9\x92\xacF\xbf\x1cF\xca\xe0\xe7\x19\xc3\x0dj\x91\xd5*\xfa\xfd`O`\x0c\xe7\xd1\xc4\xcf\xa3\xed\x9b\xa1Tf\x1bl\xe3\xcc\xab%\xba>SF{\xcc\x93\xc8\x8d}h\"{P,gL\x0bo\x87'\x06\x8b}\x04\"L\x93a\x01\"viB\x85\xb6|r\xacB\x96Q\xf8g7\x15)\xeds)\x01\xa6\xd7\x91\xbc\x99\xb2\xdc\"N\x95\xf9\x10\xd6\x13\xe0\xb6z\xe8\xa3\xacLB\xc0\xc5j\x96\xc1\xbfB\xb8\x81\xcd^\xd9\x8a\x91\xa3\x8e\x81N\xf6op\nOf\xff9\xfa\xe5\xc9x\xe7\xe8\xc5\xce\xff\x0bw\xfe\xb6sy\xf1\xe4\xda\xe6z\xf3\xba;\x84+\xa0r\xf6\x0c\x9c1:\xfd\xabiB\x8f\xb5\x02ul\x96\x0e\x7f\xb6*\x00o\xcc\x01\xda\x08\xf0\xa88\x13x\xd2\x9b\xe3\xb2q\x90\x89Ex~S^\x87\xee\x14*1\x0bl\xd3J\xec\xe0\xc1s\x8c\xe6\xbd/P\xf4\xfe\xd3\xdd\xbd\xbd.\x80\x1b\xf3\xfcp\xf6\x1aP_\xd2\xe7\xb0\x7f\xb0;9\xea\xabL\x1f\x96\x88b\x97\x8eggB\x07\xc3\x93ILw\x8f|\x98\x1cM|\x98\x1c\x1eu\x80u\xf1DYZ\xc6ie\xce\xa5$\x1e{\xf6 \xe0c\xaf@\xa4~\xb2J\xf5\xe4\xe7\x1fi\xf4\x98\x10\xaa\xb3Jo/\xdd\xd9\x95\xf0\x98\x1c\xecN\xad)\x04\xc53lU\xfc\xdfy\xc8)\xf7\xd18\x80\x11\xa5\xebvx\n\x82g\xcf`\xc2\x0c]v\xf8l\x8c-\x88\xb4\x89\x9c\xef\x190\x1f;&o\xeeo\xca\x12U\xf4\xdd3\xd6\xe1\x84eg\xe9K\x7f\xc0\x07\x93v\xcf\x83\xef\xdft\xbc7\xb0\xf7\xe9f\xbd\xc3\xf3\xe7\x98\xcb\x00\x03lcB\x83\x94\xfe\x9a\x1e\x0e\x1a\x16\xee\xd3\xb0q\xedn>.L\xba0\x9d\xee\xb1\x10\x1ep\x00\xdbt\x848\xba\x0d\xc6\xda\x03\x1aq\x1e(\x14!\x92\xb4&V\xd2\xdar\xf6\x99p\x86\x19X(i+\x93\xab\xfbu\xd6\x7fy\x8cw\xa6\xe3t'\x13>\xb5\x07\xbfS\xb8&h\xa8\xd4}\xea\x05,\xe8|\xd3q\x19\x90/\xeb,/\x8b:\x85\xf1\xe0\xd6\xf6\x0e5\x8a:f\xc5GZ1\xa5\xd3\x9cY\x86a\xf0y\xd0\xfb\x0b\xc7<\x02\xfb\x89\x15'\xa7\xc0\xefU\xc6\x8c\xae6\xfdb{\x1b\x90\x0d8=\x95\xee\xdd\xc3f\x93\xda\xdd\xf5\\\x16\xb1\xdf\x07'\xcaIX*~m_\xb1\\\xbbOw\x8d\xeb\xb5\xfbt\xcf\xb0`\xb4|_+\xafx\xf9\x81V\x1e\xf2\xf2\xa7\x9e\xc4\x0d\xd4\x07\xbbh/\xe6\x0d\x8f\x0e\xbac\xd0}\xa6\x1c?\x03\x0f\x9f)\xa7sV\xcfk\xad\n\x0d\xa2\x84\x84\xb9\x8b\x87\x9cX\xb3q\xddt\xa7\xd4FQ\x10)\xdd|6\xbe\xf0!\x9fMt\xbb\xff?\xb4\xffRd\xc0t\x0ctWT\x89\xd0\x9c$\x04c\xfc\xc4j\xf95\xa1\x102S\x0b\x97!\xdd\xd7J-,\xb0f\xe8+{_l\xb6\xf7O\xf7,gH\xf9\\_5c\xf8\xfb\x13HwvN\xda\xf0\x17\x05\xa8n9K/p\x01\xa5\xbc\xd1\x1aU\xc9K\xa5,\x9f\xe6+\"\x8ff\xf0\x90\x1b5\x92\x88y\xdad\xc9!\xf4/\xf2\xe8\x8b\xf9\xf4\xe81k\xd8,\xdf\xe5\xe5<,\xc3\xcbK\xe3j\xe4.\xf1\xe0\x0c\xd2\x99E\xbeW\x17\x1f\x83\xb3\x0c\x8b\xa5s\x01\xc7\x90\x06\xabp\xfd\xd8\xf9\xec\x8d-\xe0s\xa2_{\x06\x0e\xf0v\x8b\xa2\x8d`f\xc6D#9\xcb\xe8G!\xe5c\xc7<\xb1\x80\xb0\xc9d\xf7\xb1\x83CP#NH\xec6\xd2N\x8aY\xf3\xaf\x18\xeb\xd3\xb1a\xa8\x9a\xa8a\xd8Hmbbz\xbaY\x0c\x01q\xea\xdbb\x1bT\x12a\x14N\xe3\xb1s\xc6\xd8\"\xaa\x04\xe8\xd8\xe8\xbd\x81\x9d\x98\x1e\xb8\x9d1=l\x1b^\x17\xa7*XB\xf3\xa8\x94:lh\xc6\xd6\xf5\xd8\"\xc1\x0d\xc9\x0b\x8a'j\x0dS]TG\x86sn\xc6\x81\xe3u\xd7\x98\xd0\x1a\xb5]\x8b\xb9\xc6!\xads\xa6,{\x1bO\xa4\xe4K\xf9)\x8e>\xab\xb1\x98;bK\x82\xd8#Q_\x96B\x97\xb6\x08\x0f\x94\x8e\xba\n\xa3\xcf\xc6\x18\x0f\xa2%[\x98\xfb\x9b&\xab$\xb4\xc3J\x9b\xbf\x11\xb1\xb7\xc2.b\x1c\xa3&\x8d{\x02\xd5\xf6$\x80\x14\x16@\x81XI\xb7+X,\xb6\xd8\x93\xdf\xb1\xddb\xbd5}\xe2\x0f\xc0k\x86D+\xe7\xfa\xcd\xac\x83x\x1e\xfa\x86\xda\x93\xdb\xf1\x9b\x0e\xb5\x95{U\x7fzG\xdb\x93\x89\xf1[\x8f\xd6\xb7ir\xc4\xd35\xe0\xde\xd8Z \xcb\xc1\xe9}b\x1ci\x88\x16|\x8a\x1c6\x137\xc1\x83lV\x8dF\x17\xf2-\x99U\x1dq3\xe1[\xac\n\x8bX\xcc\xa5\xc4}\x0bb|\xdd\xc7\xe2? U\xdc\x801 N\xcb,\xda\xee\xde\xa6,\xda\x81\x89*\xc8y\x96B\x13y\x9f\xf5\x91\x8eqJ\x81 \x99q\xae3m\x14\x13\x0f\x86\xe6*\x9by\x86\xe0L\xeb\xf7R3\xe2\xaf\x98e{\xa3\x98\x9c\xa7\x1ek\xfe\xe4 \xb8\xf4\x02L\xa1\xa5\xa2\x84\x1c\x8e\xc1\xcd\xdc\x9cN\xcb\x9734V\x9e\x0f\x99\x1b\xb3H\xb0\xd5\xd0\xccr\x88\x1aL\x8a\xaa!\x01\x88\xd3\x8cc\x04\xde\x80gD\xe3\xa6E\xa1#\x1c\x9a~M\x19b/\xee2\xc5H6\x0fO\x1c\xab\xb8\x85\x01\xf8\xc0%5.1ghKYf\xe8\x98\x9fh\x9e\x13\x1a\x7fJ\x7f\x8f\x15?\xe4f\xee\x03\xb2\xae\xfd^so\xb6\xc6\xb4)\x03\xf3\xb7\xfd\xce\x83\xcb\xa5|\xa3\x1b\x93\xbafZO\xbeH\xa9\xbbwp\xe4\xb9\xce\"\xcb_\x85\x91\x08\xa5\xf5\xa8f%\x1e\xe0H\x17?p\x1e\xe0H\xe7\x0d2\xce\x1b\xe8\x10\x8d\x891\xf6\x9e\x1eJ\x8b\xe2n\xc6\xd0\xf9\x94\xfa\xe2 \xbd\x8d+\xdb\xca\xf4\xf1\x0c\xa6\x94~5\xd8)\x94p\xc6r\x15s\xf3\x8d\xd2g\xc9N\xab$\xa1'\xbcPP\xd7\xf4\xc2W\xa4#\xa8N\x0cy\xe2!\x16g\x15#\xd5\xa6\xa8P\x16v.N\xe4\xf0\x80\x91R\x19\xa1e\xa1Zv\x8b\x01\xd9##]\xcc\x93A\x1a\x12\xa2\xaa\x99 \xd3v\x05\x92V+\xc2_g\xed\xd7\xb7y\\\xb2\x97\xa1\xf2\xee\xc1\x87\x02\x19\xc7\xd8-\xe8\xb0\xe8\xcc\xa2\xe6\x90z\xc1\xf5\x90\xa8\xd3t\xc3\xf8V\xf9\xb00\xb3A\x96]\x89\x1a\xd3\x18\xf3\xe6D\xca\xe6\xecJ\x9bC\xc1\x99\x14\xba\xe8\x182\xce\xe1\xf3\xf7\x14\xae\xa5\xea\xfb\x149\x1c\xb9S\x1e\xc1\x87nh\xd4\x8cAz\xa3\x1d\x06q\x10\x8a\xe6 \x84\x86\x83P\xb4\x0e\x02\x8fa\xde\xde\xf4kR\x1a\xb7\xbc\xa0\xe5\x86\x9dV\x8fB\xd8}\x14Z\x89y\"\xbe\xdb\x11\x1d\x0ff\xc3\xf9\x16 I\x92\xe1\x1c\xdaD\xa9\xc1\x8f\xaf^\xbf\xf8\xf9\xa7O\x9c\xb0\xcc]\x0d\x0e\xb3 \xe7\xc70K\xdd\xfd]O\xcb\xdeO\xbe\xac\x938\x8aK\xfe\xfa)\xdd\x16w\x7f\xf7\x90\xff{\xe4I$\xcf \x18hgP\x05\x8d\x0c\xa9;m p./I\xf16\x9bWZ>\xd6AKG\xdb\x93\x05\\\x8a\xf5C\xea\xd6\x1abwz\xc0AI\xea\xee\x1eq\xaa;u\x0f<\xd7\x11&\x1b\x9f\xc2k\x01Z\x9c\x97\xe7\xe7\x1f\xab\x84\xfc\x14\x17\xa5\xff\xf2\xfc\xfc\xbc\xbcK\xc8\x8f$J\xc2<\xa4#\xa1e\x7f\xa2p\x85UHb\x92\x96\x1fIT\xe2\xcf\x1f\xdf\xbf\x95\xfff\x8d\x8b_\x9f\xb2\xcf$e?\xc22\xfc\x94\x87i\xb1 \xf9\x9b\x92\xac\xb0\xf0u\xcc;\xfd\xf7Oo\x7fz\x91$/\xb3$!8y,\xd1~\xbe\xce\xf2\xd5\xab\x84\xd0[\x8c\xbf\xcf }+J\xde\x92y\x1cbco\xe3\x15\xa1\xe8\x96\xa5\xe9}\x17\xae\xc8\xfc]6'o\xc3\xb5O\xff\xc5:\x1f\xc2\x98\xce\xe1\xaf\x15)\xd8\xd0?$\xd5u\x9c\xf2\x7f\xd8\x97\xe7\x7f\xfa#K&\x87\x15\xce\xff\xf4\xc7w\x88\xa5\xc5\xaf\x0fa\xb9<'\xd7\xf5\xcf,NK\xf1CZ\x85\xf3?\xfd\x91\xcd;\xcb\xd9\xa4\xcf\xd1D\x95\xa1sV@\x97\xfb|I\x08\xfb\xfc\x13eg\xf20\xfa\xfc\x92/x]\xc0~eU\x84#r\x82b\x9d\xc4\xa5\xeb\xf8\x02Z\x8cO0 ~X\xcb\x80\x8b\xd1\xc8\x04g\x11\x1e\xce\x8a\x8b\xf6\xbd\xa7\xe0%\x9fE\x867h0I\xe9\xf2E#\xf4V\xa14\xe6<\xdeJf\xd5\x05\x13\xd2%(\xf9\xa0@\"\x9bE\x94\xab\xc8\x02\\\xd7\x9e\x13\xaf3<\x14\x8e\xfe\xf6P[\x1am*\x96\x13\x02D\x0eH=\x1e\x86\xf5\xd0\x87\x9dI\x1f)e\xbb\xec\xdd\x94`m\"\xd7\x10\x80\x12\xf1\xf72L\xbf+\x81\x0e\x06V\xa4\\fs\xc8R0\xe6\xeaii+7\x1b$\x07-\x83Y\xca\xa9\x0d\xeav\xd2Y\xa8\xc7\xef\x13o\xa6\xbe\x1e\xa1\x87\x19\x16ZR\xa4s\xe3+\xb1\xe3B\xc8\x8b\x80Mlc\xd3\x9f\xa1\xe5\x8eF\x91\xbe\xff\xf4\xde1h\x1aeY\xcc\x83\xfa\xba\xd0^\xb7`\x0d\x1dl\xc9\xa9(w2=\xf4\\'^\xe4\xe1\x8a\xe8\x1d\x89'G\xe8b\x13\xab\"\x92$AA\xc1l0\x8f\x8bu\x12\xdeQ\xac\x97f)q|\x9c\xfb\xa1\x17\x84\xeb5I\xe7/\x97q2g\x99\xca\x83\"\xa7\x80\xd2\xf95\xbc \x8b(\x8f\xd7\xe5\xb1\xe33\xabV\x12DYZ\x92\xb4\xfcs\x9c\xce\xb3\xdb`\x9eEH\\zA\xb6&\xa9\x8bn\x03,j\xa7\xf3\x8c}\xfa\\T ^\x9f2\xc5\xf1\xb3_\x9e\xf0W\x98\x81)\x88\x92\x8cE\x8c/\xf08\xbd>\x81|g\xe7\xc4\x03\xae\x9a\x94t\x8d\xb3l\x96_\xd8\xad\x02\nWS\x89\x9a\xaf5O8\xcf\x94\xd7\x94\xa4\xed\xe7\xa7\x8c\xf0\x89\xabf\x04m\xdb\x0c\x93\xa2\x12\xb7\xf4\xfc:\xdce\xe8\x83\xfa\x9aK$)\xc68e\x0eX\xb4j\xe1\xaaY\x95\x08\xd2\xe0\xc7\x10\xbb\xa9/'\xe8\xed\x07\x87\x02}\xa0\xf7hDb-=~\xae8\x96\xf6\x01?\x9b\xa4\xabx\x17\xbe\xe3\x0e\xce\x1eW\x84\xbb%\xfa\xf5\xb0\x10\xa8\xa9\xb71\xcf.\x11t\xbb\x9e\xeb|&w\x85~\xf2\xd9\xa5U,\xcc7\x1av\x8e\xe1\xa3\xee\xc1\xc5?\x98\xec\xe7\xf1\xa34 #g\xce\xe5e\x94\xe5d\xe7\xd7\xe2\xb2X\x869\x99_^:\xa2O\xf3;\x8a\xe8\x1f;\xa1XL(f\x13\xfa\xed\xa1o:6\xc4\xe9DYZ\x94y\x15\x95Y\xee/\xc3\xe2\xfdm\xfa!\xcf\xd6$/\xef\xfc\xb8\xf8 \xce\xef\xfb\x85\xbf\xe6\xc5o\x8aW5\xbf\xe4\x97\xd9OY\x14&\x84a\x03_\xa0\x05\x9fc\x1e\x99j\xdbl\x95'{^\xb00\xcaTtQKf&\xf6\xfbV\xd6\xcc\x98\xa3\xcau+\xc6#\x9er\xdb\xf9\xb2\xb9\xc6\x18\xd0\x98\x99\xd4\xa0\xb8\xa5\x0d\xcdUfs\xcb\x10PA\xc8,\x94\x17\xbd\xfb\xb7!W9\x9d\x1cy\xee\x96\xec\xeeBq\xcb\xbe\xc7s\xde\xfb\xe0\xb0?\x1c\xbf\xe3\xb0\xa1\xfd\xc9%]\x8a:S>\xf7O\xbaD\x83\xaff\xc8\xbe\x1d\xc5I\xe8\x8d\xb7g\xb6\xaf\xe1\xed\x9a\xa1\xaebHvf\x17\x041@\xda\xee`\x9e\xa5*\xffI\x9f\x07\x06\xbc(\xe0\xc6\xe5m\xe66\x92\x8d\xeb\xad\x9d\x19&\xc2\xfb\x99X\xf7v\xc3[\xb071\xcb\x15[\x9cm\xebF\xd4r\xd7\x02\x89\xb7\xbc[]\xa4K\x08\xd5\xf1\xbb^\xefm2\xed:A\xfd[\xd5%d\xaf\xf3\x11\xff\x9c\xce\xc9\"N\xc9\xdc\xa1H\x84\xc9\x8f\xf8\xabwU\x928Fg1\xa4E;\x119\x0e8\xbf3\x94Jc)g\xc4\xe0\x98\x02QX\xa7\xe6\xd5\xf4\\\xe8\xd1\xca(\n\xbc\x12\xb1\xe7q\xac\x9d\xa1\xb0\x08\xb5\x00\x0e\xab\x80\xc3u+v\xca<\xcfFV\x03KBCP\xe3 m\xdd1T=\x80\xc1D\x02\x8c-\xa8?\x0f\xd3y\xb6r7\xdeM!\x92d\x86\x8a\xaeC \xc2(,]}\x17\xe9xK\x1f\x1c\xef\x92\xd2\x8e\xa3Q*\x92\x04q\xf8\xb1{\xf0x\xb4\xbbk\xbe\n\xfb^M\x8f\xb6/A\xee\xc6\x1c\\\xc7\x9c\xf4\xe3\xf2\x93\xc7\xae\x00\xdd_\xad)fA\xf4\x9bn\x8a7x^\x93\xddn\xaa\xe7\xa8\x9fS\xfd\xef\xa0z\xf6\x9fZ\xf0\xf1\xbe.\xf1\xcb\xcc \xaao\x12\xff\xbb\xf1\xf1\xc1\xc4\xb4\x00\xc1b\xc8>Rn\xc2^ $h\xdb\xe6\x92\x10\xa3\xad\xf3l\x15\x17\x843&\xa5+O\xc4\xea\xc5\xa4y\xb4\"\xd3$\xfdN\x0d\xd2\x9e\x1f\xc29|\xe0}Id\xa5=\xf3!\xea.\xd2\xdalX~\x1e\x04:\xceI\x91%7\x84\x03\xd0\xba\xf0W\x96\x858\xd7\xddZ\x1e\xbe\x82\xff\x98\xec\x99\xa5\x05\x93\xf1#O/\xb3?m\xb2JJk\xc5n\xc6\xffq\xd0L~\x04\x0e\xcc3R\xa4\xdf\x95\x98\xf7g]BN\xae\xc9\x97-\x8b\x8e\x94\x83\xd3\xaf\xba\xd0\xf4\x82b\x8e\xe4\xfe\xabiD\xeep\nO\x82'\x9a|\xc7\x88j\x9d'\xc1\x13\x07f\xe5\x85K\xb4\xbd\x128\xb6\xb5p0\x04o\x93Y~\x81J%\x1f\xb6\xac}@\x0f.7-\xef\xa6z\n\xf3\xe5'A\xa3\xfb@ e\x1b.Tn\xeaN\x0f\x0ft/\xdc\xb8~u\xa8\xbfB\xd2\xceD?\xc4\x01W\xc3 \x85\xd1\xf6\x08\xc8\xeb\xf7g=\xc0DPE\\\xe7\xa8\xed\xd8\xf1\xc0\xaf\xad\x84\x8e2\xd02\x90\xe0\x04\xcb*\xad\xbcFPS\x17I\xe2\x94\xb3f\x8e\xc7\x96\xa1\x9a\x0c\x83*+\x90\xe5\xc3\x91\xb6\x8c!\x9b\xf6\x0ckuWi9I\x0f\xd2\x11\x10\x93\xd9p\xd7N!s\xeb\x1d\xf3:\xb7\xccBPW2A\x9d)@\xb1s\x0f\xff\x1e\xfb\xb7\xc1\xd8\x87\\G\x82h5u\x0f6d\xb6L\x82\x9d\xd4\x9d\x1a\xc9\x9bC\xb3\x01\xc7dl\xf6CAi\xc6c\xc1l\xcc\x1d\x94\x98\xc0G\xfc8Eb\xf4\xb7\x0748j*\xfc\xa6[3:\x97l\xf7\xd0\xbd\x1bC`0\x0f\x84\x98\x87\x9f\x0e)\xf3[v\xb0\xb9U\xb0p\xb5\x08\x06\xbd\xd4Q{;\xb8\x00\xf6\x9a\x94\x92\x84\x89\x0d{C\xbf\x91\xdd\x03}K\x84\xcf\x90\x99\x12\xdd=\xd4\xad\xde\xb9\xcf\xd0\xa1\xceQp\x9f\xa1\xc3\xe9?}\x86\xfeA}\x86(\xaf\x94\xbaO=\x1f\x9c\xb7\xe1\xfa[9\xa1\x1d\xea\xde%\xdc\xebdj\xf6:\xd9\xdb\xd5\x0f ;P\xfa\xf1\x0by\xedG\xfb\x81\x18\xe1o\xc9\x11\x93|\xb628\x06'k\xe4\x0dR\xd5\x8a9\xba\xc4n\x89\xe7\xa1\xa4\xe7\x81\x82\x0c\xc6\xb6\x86\xfd\xc0U_3z\xae\x8f\xc6\xe3\xa7\x93\xa3\xa3\xe9\xfe\xde\xd3\xbd\xf1\xd1\xd1\xa4-nx\xf2\x9f\xee\xd9\xf1\xf8~6\xd99\xba\xf8e\xfe\xbd\xf7/O\xfa\xd6\xc0\xa2\x86\xc1\x10>|:FZxk\xcb%\xd2U\x13\xfa\x13\xc2\xb2\x9f\xc8F\xae13v\xe3hg\xeb\x94\xf9\xee\xe7AI\x8a\x12u\xba\x88\xb1\x84\x0b?\xcb\xffy\xcaC\x97\x96\xf0\xac\xd7\xefd\xc8J\xf5\xad\x82\xed$Xb\xeft\x0c\xf7T\nu:\x08m6\x17\xc2\xec\x84\xd5r\x1e\xa2\xb7\xe1\xc9/\xc1\xfd/3\xf7\xecx\xf6\x9f\xb3_..\xbe\xbfwg\xcew\x17\x9e{v\xec\x9em\xfd2\xf1f\xff\xf9\xcb/\x17\xf7\xbf\xfc\x12x\xdf\x9f\xfd2\xf1~\xb9x\xd2\xbe9O\xfe\xf3\x97\xdb\xef\x1fu@\xb8\x7f_\xa3o\xde\xd2\xc2\xdf\x8bm\xe8>A\x8a9k\xaa\x90bu\xc1U\x96%$L\x9b\x12\xc5Ik\x0bY1z\xbe*q\x9c0\xbaX&\xff\x12_\x10\xb6Cq*d\x88\x1b\xa9\xf9j|\xd4\x96\xe42\xf15\xb9!).\x9d\xf2\x13I\x03!\xe1^\x85_~\x8a\x8b\x92\xa4$o**\x855\xb3/\x8d\xac=\x84|C\xd0\xd5\xd9Xlo\xcc\x04\xda\x9a-8\xedi8\x1bD4k[\x00\xda9L}H\x83Wt-_\xad\xe2\xb2D\xdb{,k\x10\\\xb3\xf2\\\x0d\xa1\xbe\xd5\x16\xbd\xa9\xc3\xa9\xe3\xb7\xea\xfb\x89\xf6}A\xf4\x1av\xa8a3\xd1\x06\x91\xc9\x18\xdd\xc3\x99.\xd7$\x9cH%c\xeduV0K\x8cN\xabm\xf3\xb9\xf2\xd50N\x0f\xea\x8c\xc8*\xee\x8e\xc8 )\x11,\x96\xcd1\x8f&(\x1fsW\xbb\x06\xbf=Pr\x81\xd0\x999M\xd4AwK\xae\x16\xe0k\xee4\xdf*gF.\xedr\xe1\x97i\xa2\xd2x|\x0e\xd9\x14\x97b^\x91!9[\xb0\xb0\x1fb\xf1\x0dY7\xe9\xec\x17\\f\xc7\x1d\xf4~N\xa3\xb0\xba^\x96>Ti\xb1&Q\xbc\x88\xc9\xbc\x9e\x1b\x0e-\x00\xf7;\x9e}\xd7\xf1L\x927\xd6\xdf\x82\xd9t|)\x99 \xefB\xa9\xf6\xd0Z\xe3\xac\xc9\"\xcaW`V^\xd8\xc1.\x83\xcb\xa9\xe75\x0e~\x9a\xed\xb9i\xc9\xba\xfc\xf8\xd2&G\xbfE\x9ah \x7f\xd2\xe5\xca'5\xea\xab\xfb\xb4y\x17\x16\x17r\x82\xde\xb8\xaa}\x92\xb7,\"\xdcD4\xdb\xf6\x91\xed\x84\x92=\xa0J\x813)\xb9\xadG\xbf\xcd2\xe8!\xdct\x1d\xe9\x8d\x83\x0c|\xee\x92@\x0c\x89\x92\xfc\xcd/$\x87}\xfd\xfa2\xae@\xbb\xd2\"\xcaaS\xc4\xc2\x06\x11\x91\x9aOn\xe0\x14fZ\x91\x0f\xe4\xc2X\x91\xf8\xa6\xcet\xb0J\xbb\xbb\x0d\xf3\x94\xcc\x81\xa5\x0b8\xa5\xc8\xbb\x85ZP\xdbjD\x9b\xc7\x06D\x84\xddT\"\xf6\xb0\xde\x1d\xb7)x\x0e\x15vi\x19\x0dsa\x88\xb2\xb4\xc8\x12\xc2\x80\xbf\xeb\xb8i6'\x1e\xd0*\x18>s\x9d\x15E|\x95\x10P\xc8\x84\x15Ye\xf9\x1d$$\xfc\x0csR\x92\xa8$\xf3\x00\xfeu\x0eI=\xeap>\xa7e?\x17\x04\x08\xfbJ\xc7\xf6\xae\x07e\x06q\x1a\xe5\x84\x02\x9b$^\xc5e\xe0\xb4\xb6\xb4\x89\x93j\xa4\xbf\xc4\xf8\xcb<\x8c\x90\x08U\n\\\x91\x0e\xc9v\x932\x14i\x98\xaf\x96^\xb3?\xf9\xf67\xbaY\x82\xc2\xa7(Hy!\xd1\x95&dS25\xd2*\xbb!b\x0et\x98\xb1\xc7\xe3\xbb#\xc2\xa3\x9bNT\xf0#\xa0Y+\x82\x92\xfcKXi57\x10o\x00\xf6\xc9\x96#\xeeYkud}kyS\xfb\x7fQB\xe9w\x81`\xd8\x8c\x0e\xbf\xf4\xcb\xdb\x11w5^\xb0\xfbl$$j\x0c\x901a\x1a\xddQ\xa1s\xcc\xddT\x02k\x94\xea\x97V\xf5\x14\x83\xbdr\xd9T\x0b\x16)\x90T[Q\x15\x98\xaa/\x19<\xd5\xe3-\xab\xb8\xd0p\xa4jlX\x9d@\xb8\xb3C!\x8e!&\x0d\xf0\xc5Hg\xe1E3K\xfa\xab\x99\x17\x9d\xa5R\xc0'\xda\xeeS\xf5\xdf\xc4\xfe\xab\xf6\"I\x86\xf1Vf]{\xebz\xf4\\\x85\xad\x8e97!\xecYf\x1c\xddm\xf3Lg\xf4Q \xa0\xe3\xdc\xed\xed\xce{\xd1\x1e\x92\xb97\xebA'\xe8D\xaf\xccX\xdf\x1en8 \xb6\xb0\xbd\xd0nGLs\xdb'z'\xda\xf9\xc1\xe5\xd0`+\x18y\x9a\xdc\xc2\xd3X0\x83\x1e\xee\xbe Oi\xa1\x8bO\xea\xbbqbotV\xdf\x99\x1dh\xf1\x1d|%\xba\xb6\xd1v\xa8\x93Ag\xd9D\x96\xb6i$\x16'I\xbf\xc6g-\xe2\xcf@\xf9 \x1a\x1f\x8eav\xd17\xd6\x97Y\x95v\x0b\x04tv\xdf\xa6\x1e!\xed\x8dm\x9f\xb3\xc68\x83/\x83!u&z\xee\xd4\x15\x84\x05j?\xbc\xd1\xb8\x11\xfb\x0c;\xc2\x85\xa9_\xf5\x0b 5q.\xcf\xc5!{\xbeO\x0e\x9fz^p^\xe6$\\q\xd7\xdd\xe0# \xe7\xe1\x15Z(\xe0\xef?s\xbfg\xf6\xc1\xe4)\xfa\x86\xfcX\xad\x13\xf2\x85\xa9C1MLP;\xf9\xb1zGS,\xfd\x10\x16\xc5\xa7e\x9eU\xd7K\xa6\xfb\xd8?\x1c\xa4\x83\xed\x0d\xd1d\x0ett#\x92\x99\xb9\x18\x07MyW\x93\x7f\x06\x95?h\xc7\xc4$$\x89\x0b\x8c\xb4\x02\xc2o\x83!\xa1\xb4\xcc\xef\xd4\xa2E\x9c\xc6\xc5\xb2\xcf\xc7\x87>[\x9dK\xa0?\xb5\x96\x8fujG\xed\xa52*{=\x0e\x93r\xa3NQ~\x84\xd6%\x0fD8({\xa3\x80\xfa\xdd5I\xe7qz\x1d]\xed\xecP6\x8f't\x81\x1cW\xd0\xfam\x9b\xf2\x10\x0f \xa2,\xffL\xe6\xdcc\xb5x\x9d\xa3]\xac\xa9XlRIy\\\xd3g\xa7\x86\x00\xa8\xf4y@\xb5\xb7\xc1V\xa8\xe3r\xcb\xb7i\xd5fCB\xee\xe4N\x82\xab<\xbb-\x18\xf12sn\xc6\xc1d\xec\xf8@\xff8\n\x9c\x8b:\xfaW\x13\x0f\x8cA\xc9\xb1\x0f\xfb\x1e\x8f!\xcd\xbci\xb2:\xda\x8f\xda\xdb\xaa\xbe\xa6\xe7e\x88Z\xd9\xeb\xf6pP\xc8\xe2\xee\xeby\x04\xa3 N\x97$\x8f9L\xd8\xd5\xd36\x08\xb1\xa3\xf9\x90\xcc\xc9:'QX\x92c\xbc\xdeO\x0d\x0b\xd8V\x85'\x1c\xfa\xe8z%\xfa\xac\x99\xc6i\xec\xf1\x906\xed\x1aK4\x81h\xf2\xa6(\xde[\x1e\xfcfH\x0c0\xf7\xe1\x86\xf7i\x07\x0cw\xf8\xb1\xe5\xe5\xb5\x114\x03\x97\xaf\x85H\xb23X\xc8N\x1f\xaaW\xda\xf7D\xdcb\"\x0b~\x0dt:\x82\x12\xa6\xe5x\x9b\xcd\xd1\\l\xab\x94\n|\x16V\xd7m\xd7\xd3K(W\xb6\xc5\xfc\xf1\xe8\xf9x_\xbf1PZ\xb5~5X\xc6\xd7\xcb?\x87%\xc9\xdf\x86\xf9\xe7\xf6\x16\xd0'\xc2\x8a\xa2\xdd\x7f\xef\xff`a\x18\xdd\x19L\x0e\xe0\x18&\x07\xbb\x87{\x96UP\x86\x02\\k\xcbh\xd3\x18\xce \x86c\xbe\x16Q\xf3\"\xa2\xe4H\x04\xc7\xb0\xf0\xcd\x8d\xc8\x19\x15[\xef\xbd\x06\x94\x87\xc9\xcb0I\x98\xc0g\xe2\x0b4@\xe6?\xe6a\x9c\xca\x85\x0c\xe2i%\xeaw\x0c3\xa8esR\x94yv\xc7\x0b\xcd;\x92\xe0;\x9e\xe7fN\xa2l\xce\xbd\xablxJ\xa9C?N\xea\xdePB&R\xc1\x00kP-\xbb\xbf\x07\xa7*\x17\x87B\x98$spX@w\\\x9b*\x03\xb3R\x9d\xe2.\x8d\xb8\xb8\x04\x7f_\xe1U\xfe\x90g\x11)\n\xed\xe3,E_\xd1N:O<[\xdd\x94\x92\xfc\xdc41Moe\xd8h>\x9b\xe2\xc9\x99 \xfa.\x8d\xba\xeb1\xf7f\x1cxteG\x87\x94\\\xec\x9f\x95xJ}mE\x07\x0d\x85Q3\x07\xe2\xee\x91\x84\xa4\xbe\xf4\xb7\xe2\x86\xa5?\x0f\x88\x8a\x89g =\xba#G\x8aggGB\xee>\x1a\xe0\xbb\x0dNrc\x1fr\xcf\x97\xb0\x94\xfb\x8as\xe4~k\x1f\x98\xd0\x94 E\x85<\xb5\xe4\\=\xd3_\xd1\xc60f\xbfO\xc5\x1b\xcf\xf3!\x91T\xc5\x83\xf6\xf4R\x05\x8aL\x8en\xdae\"\x1f{\n>\xa4\xbbQ\x89\x9f\x1c\x9e\xa3\xe6@\xc2\x8b\xe8\xbc$V\x8aBN\"0!K*\xc1\xde\xb8\xac\xf7\xe6\x9d\xdc\xcad\xd0l\xae\xa4\xd9\x98&\x91B_\xf4\x03\xf1\x88\xb8\xc6\x1c\x07moc\xf4QA\x0ca\xda\x9b6q\xc4!\xf2\x9c\x969\x06(\xfc\xe0\x96\"\x86\xa5\xc26\xe6n\x03\xbb\x07\xcd\xf3\xd6:vb\xa4?\x0c\xd9\xb4\x04\xcd@t\xd0a\x16\x04\xd5\xdb\x87\xf2y\xa6\x8a\xa0\x98\xcf\xb6~5\xf1o\x84Lv\x82#\x069\x92ln\x89\x02\x02\\\xeao\xe2z\xcd\x98(k$\x05\xe6\nu|\xad\x90\x81\xcd\x82\xad\x1b\xda!\xc7\xa8\xae`&O\x98^\x0e\x95d\x05\x0b\xea\xc6\xa3^\xe0j\xf8\x10\xc2\xe8\xd4$L\xa3\x0f\xc69e\x88\x00\xcd\x7f\xfd\xfa\xf6\xb1\x1bSg4\xf3\xc1q(i\xc1\x10\x80z^F#\xac\xda\x81R\x18IB\xc9\x15\x8bP \xe3c\xcdd)\x8fg\x17\"0<\xc1\xce\xad\x0d\xcf\xb4\xcfz\x17\x05!d\xc4\x9d\xf2\x98\x9a\x8f\x0f\xa2e\x95Z\x18-\xf1\xa0\xb1P \xd29v\xd7M@\xc4\xeb\xe9\x16\xf0\xd0s_\xef\xd0\x04!\x93\xc2\xcd\xc11D\xf5\xa6E>e\xc0\x12\xed8\x98\x17\x8c\xde\xf9\x1a`z\x1b)\xa8\xe8S\xbb\x88\x0b@d?\x0d}2\x1e\x90@\x86\xf2\xado\x81$\xc3\xe0\xf0\x97n\xff(\xc1Abtx%\xab\xb10ld\x85\xfa\xb8\xd0d\xa2\xe1-\xd9O\xbe\x8c\x83\xc6un\x85\x9b%G\xa7\x0d\x0bc\x95Pj\xc0\x1b7A'\xc6SviU\x1aN\"\xda\xeb7\x8e\x05\xf2\xd3\xe7a\x182xe\x9d\x94\x80\xf1_\xbatM\xec\x10\x0d\xe46\xd59\xdd\xdf\x03Q$\x07\x14,Z\x88\x17N\xad T\xd2\x80\x99&{\x18+\\\xd59\xe7\xaa\x90;\x1a\xb8\xa4]\xa8W \xf6\x86\xe6fw\xc8\xd2j\xd3\xa4/\xd9\x94C\xeb\"5\x92EJ\xf2R0p\xad:\x8a\xd4A\xab;e\xe55\x16*\x85\x00I\xbb\x03,\x98\xc8\xec\xe2\x04\xca\x13\x8fN\xa3*\x96,4 \x12\x82t\xd9\xac;\xadyy\xb7\x81d\xaf\x18\xdf\xee\x96J\x1f\xee\xe6\xc4\xfc\xd7\x84\x9b\x93{-{\xac;l:\x8e\xc9\xe5J~0\xcc\xe9\"\xa8%\xae\x9b\x05|\x97U{\xf5\xd2\xbbv\xde\x10\x18\xc7\xe7hL7\x1b+\xc4E#\xf9\xe5\x96JZ\xc5f{)wC\xc2y\xe0\xf8\xe0\xfc\xf8\xea\xc3x<\xde\xb5\xa4F\x83\xf6\x05\xaf\x8b\xed.\xbb\xf8\xda\xb5\xb1\x08\xdc\x13n{\x9b\xff\x15,\xc3\xe2\x0d\xe7\xb7\xc0\xe6\xd3\xf8\x9a\x97IQ\xc7\xda__\xd0\x8bK\xef\xc6\xb0\xda\xbe\xe5,\xac|\xc3\xc8:\xdc\xef\xfa\xe5I\xb5#\xcc\\66-\x1b~\x93\xde\xf6\x15\xf0T\xcd\xdb-\xc9\x8a\xcc\x8f^\xf7a\xcb\x07\x84B\xf3^\xf1]\xedG*5^\xb6\x94\xf2>\xac$\x10\xb1\x8e\xd7\xa4\x0f:0 \x80\x8ah\x9a\x1c\x8a/\xc34\xcdJ\xa0\x0d\xf9\x18\xa7>\xe7\xeaM\x9d\x15\xd1zn\x8b$\xed\x1a:$\xebY\xe4Y\x03cn&\xbb*\xc6\x1e\x19\xdfa\x80\xe4X\xa6\xab\xea\x84\xfb>\xac\x9b\\\xce9nh./\xe8\xd2\x8e\xd2B$\x0d\xd6J*h\x91\xd9|\xf0\x91Zc>\x01\xdd\xfb\x13\x80\xe7\x10\xb4\\A6\x81T\n\x0eM\xa90\xca\x17\xb0\xf0\xd3\x02\x00Rj\x1b\xd1%sr\xd5$\xd3j\xeb[R\xf0}\xd1\xfa\x9d\xe7C\xcc\xe5\xeeg\xc3p\xb7\xa0\x06\xa4#\xc3\xb6>\\\x94$\x07\x92\xcem\xc1*L\xd4\x8d\x84\xa2\xf1\xb0\x98V \xefb\xca\xc3^\xeb\x9c\xb7\x9dK\x07I=c\nZ\"\x9e\xca\xa2H\x00\x89\xb8iH\xe53\xe6\xa9\xa8\x06\xe8\x7f\x1b\xde\xe1Ua\x0b\x81\xb5\x11\xf4\x14PfP\xa0\xb1\x80cM\xd6\xdf\x04\x05a= 9\xa4\xaa\xa3\\C\x9f\"\xd7i\x9a\xa5;\xac\xd9'\x1c\xd3 \x9f\x83\xc1\xbf\xb9A\xae\xb6\xee\x95\xba\xee9+\x89\x05\x1f\x1a[\xf7 f2S\xe6\xe6\xe7\xc6*\x01V\x19\xee~-\x0d\xb2\xed\x0f\xdaq\xf5*\xf1MM\xf7!\xf0R\xd7\xe8\x19\xd5A`\x8e\xdd\xdf\xdc)~}\xb1\xc7\x1e\xe9\xb4\x91<\x92\x9f\x87\xda\x08\xc3\xdeP\x8e\x06_U}A)\x11\x19K\x17\x9e\x99\x05T\x16\x8co\xbd\x03!J9Z|g\xde\x99Y\xaa\x16[\x8d\xac\x86\x91\xb4\xed\x02$ \xd73 \xaaf\xd0\xfc\x1d3\xdd\xd7d_c\xcb\xba\xa0\x05Q-\x18\xc4\xeb\xc1\x04\x0c}\xe7&b#k\xb3\xb5\x1d\xfa\n\x0b\x17\xdc}\xd8\xf0\xc6\x1d\x83A\xf3.?B\xacp\x0cq\x8f\xaa\x8c\"\x1cc\x1c~\xf9\x11\x92\x07c\xee\x05\xf9\xa17\x9d9;\xdb\x8f&\x0b\xd2\x1f Q\x8ey\x19\x8e\x8dL\xbe\xb1\xaeU\xc83:\x85\x89\xf9\xf02I\x8f,) \x1b\xf8\xd1 \x9e\x8b.\x88\x152\xce\x0f/\xb0/\x85\x82\x836 CO\xd5 \xe2I#\xdc\xd9i\x1c\x8d\xba\xda\xae\xd2!\xad+<\x9b\xda\x8bA\xa7!4a\x0c\xc8\xb3\x1f;;\xbe\xa4\x15\xa5\xe4\xab\xa4/\x93\xa4\x1e\xf8\xcb\xa8=k\x0bL\x98\xf6\x8c\x93\xc4\x9dD`A\xca\x1f[\x1a\xf3nZ)\xb6\xa5A\x14\xa4V\x19\x94\xd9O\xd9-\xc9_\x86\x05\xf3\xb0\xd8rg\xce\x92|\xa1\xdc\x11\xd7\xbb\xd3\x7fw\xf0\x8f\xb0\x88\xe2\x98\xfeq\x15\xa7a~\x87\x7f\x85\x059\xd8\xc3ZQ1\xe5\xff\xeeL\xf9g\x93\x83\x84\x88\x16\xc4\xdfyx+\x19\x19\xb9,\xd3\xa2\xa7\x8d\x03\xad\x8cp0\xb59\xe2\x90\xbbm\x8d[\xc1,\xae\x9bt5\x12{@ \xccM\x98 )\x10\xf7\xf6\xb6\x1c\x98\x8e\xb1\xb8\xb5\x8eZ\xc8\xbcr\x19\xde\xe4\x8d \x8bP\x1e3\x10\x8774\x17\xb2Y\xcan)@g\xc8J\x01\"\xe2\xc6>h\\\x0b7\xfdZX]\xb7y&\xd3\xb2)\xd3\x04fiDj\xa1[\x07\xe9F\x1a\x93\xa3\xb1/\x99f\xb5E\xd4 !\x95\xbc\xc5\xa8\x0c\xbc\x82\xb5\xe9\x92\xf1\xdamt\xad\xe4\xdd2\xa8\xb6k\x0bt\x1d\xa0\xf0\x01\xb4\xe7\xd6\xbe\xe6\x852\x1e+\x9fk\xe9\xde\xed\xec\x9f\x9e\xe1~1\x89z\xd3\x1a%\xf7\x8d\xf8[\xbb\xa6U*\xd7\xa9\x7fi\xb5\x9a:\xbd\xfc.\x93\x94\xa4s\xd7\xf3\x81\xb4\"8\xfd\xa1\x19\xa9\x9a\x9b\x11\xb3\xe8\x1f\x8d=\x8a\x0e\xdf\xacVd\x1e\x87%\xd9$\xb5~\x7f\x0e6\xfb\xbe\xf0\x03\xd2\x1b=\xe2\x9b\x0c#u\xf7\x0e\xf7<\xd7\x833\xee\xbf\x8c\xc9\x13\xd1\xb0\xf5p\xff+\xa6z\xd3\x84o>2\x87R\x99\x9a\xd3\xc2\xed\xea\xc1\xc3*\x83k5G\xec\xedPC\xfc\x1275\xb5h\xee\xca\x07\x850\x8a\x0c\xaf\n\xf5M\xf4Uy\x02n\xea\x90\x0d\x0b\x1f4k\xf4\xb8\x95=\xa5\xb2\xf8V\xaa\xdf\xa1B \xc5\x00\xb6\xcc\x1b\xd8k\xfc\\\x17Z\x84\x05\x86#h)\x0bo\xb1\x10Y\n\x16\xf0\xfc\x14\xb3\x14D\xee\x82\xa7\xfc^\xc6\x8d\x93\xd3\x0eDn\xe1.<\xef\x04X\xe4-\x18\x8d\x0c\xea(\xb4\xf3\x91\xa5\xac<\xccP\xc2Q\xe3\x8c\\\xf8\x90\xbb\x89\x94\x02E\xc3\x8f\xbc\xb47\xd3\xfc\xa0\x93\xa6xH\xb4\xb0\x91\x10Tj\x03\x18F\xd4\x9aDo\x96\x14\x8fHa\n\xc2\xc4\xeeA\n\x12]\xa5\xbcx`R\x82\xeeA5\x07\x8b\xd6\xad\xf3\x8b\xb0P\xcc\x9f\xc8\x97\xf2]6'\xaec\xcb\x99\x92ah\x01\xdbx\xb4\xb0\xb8]\x029\x0b\xfb\xcd\x1d\x858\x82g\xcau\x16#\x9bX\xf1w\xb7u\xa1\x90.\xb1!v0\xfdp\xaai\xe5\xc4c\x96\xa8\xa0\xcb\x9aJNY\xe4\xb8i\xe3\xc3\x08u\xfa?V\x1f1x\xe9Zf\x86\x176\x0e\xe6a\x19b\x98\xc2S\x18\x8d2\xf8W\x982s\x07l-(\x96\xf1\xa2t1\x04\x05\x17\xbf\x08\xafkN\xe1\x95\x06m\xd5\x83\x17dW\x05\xc9o\xd0R\xca\xbcx\xd12\xcc\xc3\xa8$\xf9\x8fa\x19\xb6\x82\xfe\xb3V,\x16\xeb\xbd\xf4\x02}X\x9a\x17\x0cai&X\x99\x94{F|(/P\xec\xc0\x15\x94\xa8\xbde\x04\xb0iq\x86\x88\xc5\x1e|3\x1c\xb6^\xe3v\xe4$$p\xec\xaa\xb0&\xc1\xb4\xe4\xf6f\xf6B\xe9\xe8D\xdcO\xdaM\x9d.\xa8C\x8cj\x1c\xca\xdb\xaa\xc4\x84|\xef\xd9\x8e7~\xb1\xb1\xdbze\xbf\x95\xc6\xa6\xffL\xae\xfe#.;:\xb0Th\x1f%\x1bH1\xdf\xa8\xde\xe0\xbb\x80\x8c_\xee\xea\xa2\n\x00\x16\xb8\xd5\xd8lA\xcaO\xf1\x8ad\x15J;\x0c\xdb!U\x182\x80\xa6\xba\xcb\x0e\xfb\xd8<\x98\x96T\xeeA\xba\xb2\x83\xe8\xcaoBeY3h\x9a\xb2f\xaay1\xa7l\\\xfb\xd3}\xfe\xef\xc1\xc6y1;F'\xd2S\x1e\x9a\x92\x8d\xa1\x86\x8f\xa7'P\xc3\x0e\xe7\xdda\x87\xd5X\xe9\x96|WV\xc8 \x84t\xed\x0e\x92,\xc2\xc3~\xdcJaF\x9fe\\\x94Y~g~\x99\xadI\xaa\xb2\x7f\x86J\x98\xf2\xab\xb7\xd6\xeb8\xd1+\xd9\xe6\x0b\xe2\x86K\xf1\x82\x9b3\x7f\x8b\xc9\xcal\x89\xfa\xccV\x1cta\xd8wmxr\xc3\x1dFm\xda\xb8\xb4C\xc5\x9b\xd7\xf1\xde\x0c\x82P\xab=Im\x08\x13\xf3\xb0Ih\x15$\x82B\xbb3\x87\xae\x95\xe3\x83\xf3C\x92]\xd1\x7f_g\xf9\x8a\"=\xe7\xc2;\x01\x16\x16\x13\x13\xf3U\x08\xc0]\xcf\x0b\xe6YJ\x90\xc4E\x8dE\x07\x92\x13z\x97\x98\xe5\x10\xb4\x93\x1f!\xc4)_3\xc693;QV2\x0b/\x86`5,\x91\x0d>\xec\x0b\x93;\x8c\xee\xe0P`\xe0\xd0k\xcb\x0b]=\xc9@\xaf;\xbb$\x1eW\xcf\\\x9f\xb8@h\xd6\xe7>\xdc\xf8p\xe7\xc3\xb5\xde|\x81y\x0f}\x98\x1b\xdc\x92W>\\\xfap\xe5\xc3m/\xbb\x08\x82\x83Z\x83\x08\xb6\xfa\xa2\xc6\x05/\x8c\xf1 \xe8#\xc2\x15v2\x00\x18\xef\x8fe\xec1\x87\xe0k*1C\x8a\x8ej\xd0\xacf/\xfbi\xf8\x86R8i\xad\xdd\xea\xfc\xca\xe2\xfce,\xdddD\xc3Gb\x00vmt\xf9\x05\xbd\xa5G\xe0\xc0\x1bq\xa0\xdb\x95\xce\xe1\xb4^[\n&n\xdaU^Y\xd0\xf1\x0bT\xca5\x82\xedV\x85\xf7p\n/f fNz1s\xfe\xed\xdf\xea\x8b\x85E\xe8\xfc\xf1bvcH\x1a\xfd+\x05\x86L\xdfxc\xe00?S\"\x00\xce\xe0\x1c\xce\xe0\xd6uHZ\xe61)\x10\xa2\xfd\n\xf6\xd4uoX2\xb7<\xbc\xc3\xa9\"\xa2z\x11\xf0\xafio\xef\xdb\x14\xd1\x1bD\xc5W\xf4\x96\xb8o\x18\x19\x8e\"\x0e\xcf\xf3P\xea\xae\x8b\ni\xf5+\xa6>G\xcfj\xf7\xca\x87/>%\x11(\xba\xa5<\x85\x89\xed\xb8\xe2\xabT\xd1\xea\x89\x0fK\xcf\xf3\xe1\x9c\xb6\xf0\x1e\xe1\x8c\xd8 \xec1H\xc3\x15\x93\xad\xbf\xe2x\xfc\xd7\x81P\xe6\xbd\xd5\x9f\xcb\xe3n\xf1[L\xf7\x8bW}\xeb\x15\xdb 1\xb4\x178\xb4_=\x1f\xc2\x19\xa1\x94\xc9\xaf\xf4\xaf/\xf4\xaf\xa5\x0f7f\x11\xdf\xcaj4\xc1\xe6t\x8c\x9bHw\xed\xd6\x15\xd3\xb4\xc8\x14(\x988\x86\xbb\xa6\xba)\xd3\x97x\xf8\xae\x1e\x83A\xb1\xe8\x9bl3A\x90\x89\x97\x14\xc2\xad<\xc0\x7f_\xd0\xa9gt\xea\x97>\xacf\x97\xa6\xf0\xa2,|\x91\x1b\x07\x1f`\x04q\xf0\x1a\xbe\x07wM\xbf{\xe5!\xfc]\x99c\x11\xad\xea\xc2A8\xf7FJH9\xb5\xd0\x0f]\xdfC\x1d\xa7\xa7\xd4\xd2\xe4\xda\x08{\x01\xc1\x8d\xba\xb9\xae\x08\xb3:\xcc\xeb4\xd2\x12}7,\xae\x05\xe4\xb5\x17\xbe+ mk\x0c\x1d\xd6\x81`\x1c\x06\xfd`\xa3\x91X\xe2\xd6\x9aF\xd2\xe30n\x1c\x8c\xd5\x1f\xb9+\xce\xca\x10\xf4S\xf7\xc64\x08DV\x1fX\x9a\x1etb\xe5\x93\xb9\x95\xba\x93}\x16\xa54u\xa7G\x9e]B\xccG\xf3\x14\xb6N-\xcaT\x91\xda{\x1e\xdf8\x9e\x0fN\xf8\xf5j\xd4\xa7m \xa1\xce\xdc\x0b\xc2f\xf2\x1b\x92\xfbS35|\xf4?3\xdd\xa2\xaa\xf6\x9bn\x9a\x19\xa8\x95s\x98\xab\xf1\xcc\xf9A\xa6\x93}\xcf\xdd\xd2)uc&\xf9\xbeu\xb1\xc7\xfa\x0cyB\xc76\")\xda @\x813\x163\x8d\xec\xe5\x9a\xb58\x85\xd0\x83\x94\x1e\xde\x8a\xed_\x88K\xb1\xbd\x0d\x11\x13^\xeb\xc1\x0d\xb8\xf3\"i\xc2\xe7\x16'\x1e\xff\x8e\x12p\xb3b4b\xf1}\xdd\xff\xca\xdc\x08[\xbb\xbfoZ3#\x97h\xb3M\xed\xdd\x9f}s\xaa\xe8\xcel\xfe\x95A\x93\xda\xc5\xf7\x06\xd7\xa4\x94\xb2d\xabV\"\x96c]\x8a\xbd\xe3y+\x91\xc5\x9de\x176\xf9\xae\x9ae\x8b\xf33\x8dW\x85\xf2\xf6L\xfd-\xd1x\xc7\xeag\x9c!?\x83J\x97\xe4n\xb8\xf8\x87\xe6\xc5o%\xe4no\xc5?s\x14\xd7\x03\xee\xcbu\xf8?;G\xb1\xf5\xec\x98\x12/\xfd\xcf\xcd\xa5\xdf\xb9\xcd\xbc\xb7\xf6.+\x16\x8b\xee\x04\xb6\xc1\x04\xd5\xb5<\xb6\xee\xd4RO\xd8,\xd1:{\x96:\xe6\x8c\xb7\x9b\xeda\x9f4m\xb2{\xd0N@\xbf\xfb\xf4\x9f \xe8\xa5\xe7\x7f@\x02\xfa}sR\xc4\x01\x19q-\xe7\xbf\xae`\xb3\x9f\xa4}\xf3@\xe6\xcd\xbe\xc7\x14.\x99y\xe6\x82g\x016\xbf\xa5TOhu\x14\xe1c*DJ\x9c\x82ns\x84 \xd6x6s\x8e\x03\x8e\xc1\xc5\x08\xdb\x98D\xf1e6'/J\xb7\xf0\xe4\xee\x9d\xe7\xc3\xdd\x1f\xa4\xa2e\xe7t\xa5\xdd\x91?r\xf8\x15\xc0!\xa4\xee\xde\xc4s\x13\x0f-i\xbb\x1aK\x1a\xd7\xcb\n\x83\xf4\xfa0\x91\xcc\xae\x1f(eI\xf7\xe1&H\xb3\xdb\xde\xd6\xb0\x96\xb5\xa19\x86\xce\x16\x06\x99\x94\xa2\x9c{\x01\x05zS\x1fb\xfcc\x12d\xe9\x8a]68\xa5\xd4\x07\xc6\xcap\xb3`\x9d\x15%\xbf\x85\x08h&\x18\x81i\x11\x84\xf39&\x1a\x94Se\x197Cj\x00\xc9\xbcE\x10\xafh\x8f\xe7Q\x1e\xaf\xcb\x82\x8e\xac{j\x0by\x0c\xdc\xa1\xdc\x07\xe7{)\xac\x17\x85\x94\xad\x11\xb9\x0e\x9f\x90\x83\xe4\xd4\x16\x1b9\xed\xcb\xc9\xd2\x9c\x84\xf3\xbb\xa2\x0cK\x12-\xc3\xf4\x9a [\x1d\xb9N\x81\xa3r\xbcNK\xf5\"\x08\xd7k\x92\xce_.\xe3d\xeeJ_yA\xbb\xe5\xbe3,\x123\xb1\xc6J\x16MY\xdcS\xab2\xb9\xd3\x94Q\xb2\xa0oN\x84bG\x8f\x99>%\xc4\xd7\xfa\xfe\x18\xd6\x1af\xa0\xb0\xfa\x18\x9a\xecC\x9b\xd1)\xf6\xc1\x9a\x95\x0fVy5},\xce\xf5\xf4\xb996{\xee\xa8\xeb\xd8i\xd7\xda\xdb\xb5\xc5\x04\x9bv\xdd\xd7q\xcf\xeamJ\xe9\xb4\x0c29\xa53\x1ed\xed\xa2O\xbe1u\x89]\xe6YH\x14\xe5\x1e\xea\x9bl\x9e\x857<\xb6U\x16,ZQ\xc4\x05!\x8c9\xc5sRd\xc9\x0d\xf10\x9c-F\xb1[\xc5\x05y\xec\xc2\xb4V\x80-\xcc\x9e\x9d\x04\\\xd1\xad\xef'\x00M\xd4\x9f\xd9\x99\xb2\x0en&9\x963O+N\xdemmQ\x02\xcf\xf9H\xae_}Y#h\x8c\x15\x0f\x9bAS\xb6\xdf\xd6\xda5#u\xa7\x87:A\xd7\xb8v(\xf2\xffA]\xca\x12V\xe3*\xeb\x9dq\x03\x84\xa3\xde\xc5\xb5Q\xd7\x88\xa1\x02\xae\x1b\xc6\xa46\x1eW\x8f\xb12J\x16\xb5\xaeX\x85\x84\x9d\xba5\x15\xcf\xfb\xcb\xb2A\xb9yp\x0e#\xc8\x91Y\xce\xba\xf5\xbc\xf4\x90(\x85\x98\xbf\x9dk*}9|\xd4\xa054\xcb\xae\x89\xecr#\xc2\xb5\xf3}\xec[(\x14\x8e\xba\x8a2\x9d\xd8B\xa9\xf0\x80\x84\x14\x97@\x08Q\x12\x16\x05\x84\x85\xe2%\xfb\xbbLG\x93\xd2\x0bO\xa4\xc9\xbe\xe9\xc4|{W$\xe3Z\xb6\xc8\n\xfe\x02J\xab^\xbc&oS\x96\x1a<\xc5\x18]\\\x9d\x03\xe9h\xd4E\xe8\xe7h\x89\x92Z\x08\xfd\"\xd2\x84\xac\xa0s\x01\x0f\xad\xaeB\xf6\x89\xe4\x95\xbd\x95\x07\x0b\xce\x97\xb1\x80J\xe5\x8c\\l\xb8_\x8f\x03%8WJY\x1d\xea\x1a\xdf\x98\xbf\xda\x1dO\xf5W\x19\x7fE\xe1\x8f\x9c\x86\xb0F|\x86\xdc\xa4\xb5\x89 \x0b\xd4,\x83\xa5\xb2\x1b,iA5\xfe\xd0\xfek#\xf8d\xb9\xea\";\xc1\x163\xc27\x12=\xe7\x14:\x01\xf9\xb2\xceIQ`\xd6\xa4\xaa(\x81\xc4\xe5\x92\xe4p\xc5c\xccf\xb9D\x05\xb1`\xcd\x0e\x8c6\x86J\x1a\xb8\x935s\xccc6\x96\xaa3\x8eJ\xc2\x8d\xed\xe5\x94\xd8-\xd3jC\xa7\xf5\x0d\x0c\x08@\x07\xaa\x91\x96\x85\x95\xd5\xcc\xbd\x0c1,\xd4\xdd\xc6\xfb\xc8\xa8\x11\xb1\xc7g8\xfd\\\xa1CD\xb2\xa1K\\\x83\xcbKJ!}\x93\xfb\xa3\x1aX\xef\x8e\xbfM\xfc\xa4\x03\x93}`\xea\xee\x99\xedz'-\xc5\x12zMS\xe09f\xe1\x07\x0e&\x9eb\x906e\xe5\xbb\xe3\x03\xe3\xf5\x0cMc\x06a\x97\xb6\xce\xb3u\xd1\x845\xa4\x98\xaa\xe4\x01HyIN\x16\x05K\x0d\xc5B\xcc\xad\xe7a\x89\xf9\x0f0Nr&\xad{\xbb\xef\xe2\xef\xd8w\xa4\xba\xdd\x87r\xf4\xa9\xe2# \xa3\xf2e\xb6Zg)\xc1\xbc7\xbf=\xf8J\x95\x82\x94\"EY'\x90\x91\x88\x11%n\xa69\xf4\x90\x04x\xd8\x8f\xdcu\x0e\xf7\xeb\xec\xef|~\x01I\xffZ\x91\x8a\x9c\xf31\xd4V\x15\xbe\x94\x87^\xab\xfb\x92\x87\xa2\x15\x11\x9d|p\xc4\x14T\x01\xa7<\xc9E\x96G\xe4gl\xa8[\xb6f\xe8\xf0u\xf3\xad\x906\x96\x03\x07W\xfa\xe0H]\xab\xe3\x8b\x14\xd8\x17\xcap\xaeP^Qp\x1d)\x85\xaa\x94 \n\x1fb\xb7\x90\x1b\x90Z\xf3\xd4/\xe3\xe2C\x95\x93\xd6\xa9\xe0 D,\x8cB]\xf3\x18B\xf5\xca\xd2\xc6\xa4\xb7\xc5\xb7\x00N\xa9{ ;\xaf\x0b\xf8\xa2\xe1\xbc\xe2mV\xa5%\x99\xf7\xc5\x0d\x14\x14\xb5fc\xa9NC\xdb\xbe6ae\xae/\x1d\x0dm\x18\xe6\xfa\x1f\xc9: #\x16\xa0ph\x1f\xe2n\x18\xea7\x8bm\x86\xec\xf9\xe3\xf7@,\xba\x1c\xac\xfe\x1b7\xfd\xdb\xb7\x1f\xb5\xfd\x04GU\x9e\xe3 \xdd\xdcu\xa2{\x16\xc3\xb2\x9a,\x98#H\xf3\xcburz\x05\x03\xc2\xd4\xf8\x0e\xfa\xdb\x1c\x8c'\xe3\xdd\xdfuQ\x9c\xf3W/?\xbe\xfat\xf9\xe3\xfb\xcbw\xef?]~xq~~\xf9\xe9\xdf\xdf\x9c_\xbe\xffx\xf9\x97\xf7?_\xfe\xf9\xcdO?]\xfe\xf0\xea\xf2\xf5\x9b\x8f\xaf~t\x86\xf4\xa9Q\x12\xd3\x897L*\xd1\x17!\xafu\x97\xcd~z\x14\xfc7T\xb7\xd1I\x8f\xd3\x7f\xba17\xa6\xbb\xba&\x14\n\xae\xb2\xf4\xd5\x97\x92\xa4\x94\xf8-0\xca\xf85)\xb5\x12RD\xe1\x9a\xfcH\xc8\xfa\xa78\xfd\xfc!\xc4\xa4\xcb\x84;\xbb\xb5\x8a\x8be\x98$\xd9\xed\xab\xbfVa\xf2\x1f\xe4\xae\xe0i\x05\xe3d.\x82\xbe\xb0jY^\xb2\xccz$\xb8*3^H\xf28L\xe2\xbf\x91s\x12\xe6\x11ko\x1d\xe6\x85\xfc\xfb\x9a\x94\xe7\xe1j\x9d\x90\xf3hIV\xec;L\xd1\x10\x96\xe4C\x98\x87+\xad\xa4,I\x9e*eo\xe3\xf4'\x91;Z*\x0d\xbf\x18J\xffX\xc5s\xa5\xe0\xc7\xb0$\x9f\xe2\x15Q\n\x99%\x8cR\xf4C\x96%$T;~\x1d'\xeawo\xd2\x92\\#\xad\xd3\x94\xbd\xabVWZ\xd1\xdb8\x8dW\xd5J\x1fn]Fi\xac\x97K\x12}\xe6\xdf\xad\xc8*\x8b\xff\xc6\xba\x8a\x8b7\xabU%\x84~\xa6\xd0>\xe2:_Q\xd6p\xfa\xd4d\xbd\x1e\xd7\xaf\x8fL\xaf3\xfe\xfap\xcf\xf4\xb6\x12\x1f\xef\xee\x9a^\x87\xf5kc\xd7\x05\x7f\xcd9S\xf9\x15\x9d\xdc\xff=\x7f\xff\x8e\xeb\x00\xfa\xec\x19\xec\x9eK\xc2*\x816\xc6\xce\x9b1\xb9-p~\x93\x85\xa4kb\x97\x0d\x11P\x15*+X+\xc6Z\x9d\xf4\xa4\x93\xb2\xa1\xf4:\xedD\xbc\xb8\xeb] \xde\xc8+\x17C\xd6|qy\xe4\x9a2\xfb\xbf\xe7.\xb2]\xaa\xdfj\xdd\xc3\xff\xcf\xde\x9fw\xb7\x8d#\x0f\xa3\xf0\xff\xcf\xa7(\xeb\xc9/C\xb6i\xc5r\x96N\x9c(\x9et\xe2\xa4\xdd\xd9z\xb2\xf42\x8a\xc6\x87\x96 \x8b\x1d\x89TH\xd0\xb62\xf2\xfb\xd9\xdf\x83\x02@\x82$\x00\x82\x8e\xbbg~\xf7^\x9e\xd3\x1d\x8b\x0b\x96B\xa1P{\x85i\x1a\xae;t@E\xb3\xe8\xd8\xaa\xfe\x8d\xbd\xbc\xf70@v4nv4K\x93\xe5O\xef\xdf\xa6S\x92\x125\xef7PO\xab|g\xabr\xe1\x11c*S(VN\xb1\x84,\xe5\x92\xf4\xd9\xbe\xb4}Z\xc0\x8b\x94\x19x\xa3\x8c\xcf\x04oM\x8a\xa6\xde\x93/\x1e\xf1\xfb\xcbp\xe5Q\xccd\x1fe\x14g[\xbe\"\xa6\xf5:\\\x95oB#\xc6 +;D\xf1\xf4C\xe2$\xa2\x80b\x16\xab\x1b\xb8\xa0jV\x0d\x159\xdb\xef\xcf\xa2\x05%J<\xa3\xb1 \x91hA\xefD\xa3\x8d\xf9\xf3\xd9i\x7f\x18N\xe6e\xeb\xc6\x1c\x01\xd2*0J\xc7h\x0dM\xc78{O\xe4^\xd7X#\x9a%\xfe\x18\xc8\xe2$]\xe2 \xc2qn\x08\xef\x03\xa4\x13\xcfcW\xa4m\xc9\xe8\\\xf4\x14e\x05\xdd9\x14}\xe4X\xfd\xf8\x9a{\x91\x13qj\xb6\x8a\x9bu\x97\x10A%^\x87+\x17t2\xa2LJ\xa6\xf9D)\xf2g\xcb\xfdP]W\xe2\xb1\x95\xe5\xa6\x9df&\xd8\xcb\xa0\x12\xd1\x08\xca\x90\xdfa\x97\x7f\xd9\xa8\xcfD=\xabr\xbc\x06\xcb\x9cP\xf7Z\x0f\x84\xa8\xed@\x88D\xa5\xa7\xdd\x00\xf2\xf2n\x1c@\xd4 L\xd9:\xa3d\xf9a\x9e\xc7\x9f_G\xd3\xe9\x82\x9c\x87\xa9]\xe4\x07\x9d\xe5\xce\x04\x13\xd2\x9fJ\xf7I\xc1\x85\xe9K*@\x97Fu/7\xf4H\x86\x0f\x8cyKc\x8fz\xe8\xbfE\x9c$\x8b\xe9\xc3\x1e/_\x8f\xff\xa9\xaf\xe2\xbd\xf1h\x05\x07\xb8v\xb7\xe1\x00\xf6`\x1f!|\x0f\x0e\xe0\x8e\xf8\x9b\xdd\xbf\x0d\xfb\xb0}\xeb_^\xe8\x9dd4\x0d't\xb3\x88\xc2l\x13O7\xd2y{\xc3\xf6\xec&\xf3\x96\x9b\x8c\xa4\xd4?\xd8\xe44\xf17'^\x98\x91\x0d9\x8d\xe2M\x92,<\x12\xc6\xfe\xc1&%\xe1\xe7\xcd\x9a\x12\x7f3\xc1\xc7\xec\xc0\xd9\xcc\xc3t\x83\xf2\xedt\xb3\x08\xb3l\xb3Hb\xb2I\x96\xab\xc5&\x893\xbaIb\x1a\xc59\xf17S\xe2\x9d\xe4\xa7\xa7$\xddL\xa2e\xb8\xd8L\x16aJ63\x8f\xed\xf1\x0dI\xfd\x83M\x14Gt\xb3\xf0\xc8iH\xc9\x86P\xe2\x1f\xf8\x9bi\xb2\x99&\xf9\xc9\x82l\x887\x99'\x9bEv\x10\xcd6\x8b\x8cx\xd1\xcc?`\xf3\x88\xb3<%\x9b8_n\xceHL7\x17\xde\x84\xac\xe8\x86L6+\x0fS4o\x92\x94\xfa\x1bJ\xbcx\x9amPs\xb2Ic\xdf\xf7Y\xd7\x8b\x05\x9d\xa7I~:\xdf\x84\x8b\x8cl\xb0l\xf9b\xcd\x86r\xc1\xa6\x93\x84\xeck\x8f\x84\x939\x9b}D\x18\xd8\x92\xe5&\x8f'\x1e\xdb\xbdl\x80\xa7\x8b\xe4$\\lN\x13\x9alN\xf30\x9dn\"o\xb6Y\xae<\x8e\x03\xd9F\x19D\xecEt3Y\xe4S\xe2\x1d'\xf1\x84\xf8\x07\x9bE\xc4\xa0\x95\xd3\x8d\x14}6\xd4#\xe9,\x9c\x90\x0dI\xe3p\xe1\x1f\xf8\x07\x9b\xcc\xdf,\xbcpy2\x0d7\x84n\x92\xc9\xe7M\x12\x9f\xfa\x9b\xa5\x17M\xd2\x04I\xe0\x06\xf5L\x1b\xaeK\xf07o\xc27\x9b\xd8\x0b\x97$[\xb1\x96B\x1a\x9d\x91\x0d\xb9\xa0\x1br\xbe\x89\x16\x9b\x84n\xf2\xc5\xc2\xdf$\x1e\xb2E\x9b\x15\x8f\xaf\xdc\xa4\x9b\x9cn\xceH\x9aFS\xe2oV^8\xf9\x1c\x9e\x92M\x98\x86\xcbl\x93Fgl]\xd2\x84\x92 %\x0c\x104\x99$\x8bM~\xb2\x88&\xfe&\xf5\xc2\x88a\x8c\x17N\x93x\xb1f\x0b7\xdb\x9cF\x19%\xe9fEB\xba\xf9\x92Gi9\xefl\x92\x93\x0d\xd7\xb3mh\xba\xde0\xaa\xe8\xfb\x9b\xcc;Y\xb3\xc5\x0f\x17d\xba!\x8b\xd9f\x9e\xa4t\x13\x9d\xc6d\xba\x89\xbe\"xB\x1aM6\xa8\xd3\xd9\xa0\xa9a\x93\x9fp\x97\x84M\xbe\"\xe9f\x1dO\xe6i\x12G_\xc9t\x83\xb1\xc4>\x83\xe8r\xb5`\x83\x9f\x93x3\x8f\xb2\xcd\xf7|L\xd1\xce\x06\x87\x11^\xf3z\x8a\xf6\xcc)E\xfb\x14\xab\xfc\xa2AB\xefGR\xbc\xdc\xf4\x86\x99\x06Pw\x06\xae_X\x8b\x8c1\xa6\xd6\xb7N\xf1\xadA\xcb[K\xc6\xd3z\xa7\x01\xc4\"\x83\xc9\x00K\xede\x84za\x00k[\x81\xe2&*H\xa1c\xc9\x84\x8e\\: .1\x19\n\x0fq[\xea\xb9A\x0d\xb1hMU\xdb(\x9a([0\x11\xa7\xc2\x9b\x8d{\x87\x95\x84\xbe$U\xa3\x81\x86\xb8H%\\\xa3\x08J\x80\xf6\xb5l\x12.\x9e\x86\x19\x1b\xd6\x93\xea\x9d\xe7b\x90\xad\xa0\x91\xeaG\x8f\xf6Sn\xe8\xf7n}\xea\x8f\xfe\xd5\xbf5\xfe\xee\xc6-&J4K\x7f\x92~\x16\xc6\x11\x8d\xbe\x92\x8f\xe9\xa2\xb5\x87H\xad_\xabz\xdb0a\xadW\x8b7\xd2\xc9\xd6\x8abp\xa6\xf6\xeck\x8f\xe0SB\x9fL\x18\x97\xcf\xb0%M\x16\x8b(>}G\xb2U\x12g\xed\xd0\xa8\x9dd\xa5\xc2\xbf\x1fe\x8a\xf6_Q\x87\xb0\xa51i\x0c\xaa\xc7\x9e\xfe\xcdR\xbf4\x8b\xe2\xa9\xd7\xaa\xac\x91Wq\xc2e4Li\xf6kD\xe7^o\xafW\xe8#U\x15*\x83\x89\xd7\x9b\xf0\xdd\xc3\xad\xf6\xff\xbe\xf4K,lz\xfe\x01\x98+X\x15\xaa\x1d\xaf'\xba\xe8\x89\xc4\x9b\x1a;\x89\xa1\x8d\x14\x9d\xe64\xe3\xd27\xe2\x17\xca7a\xea*\xb3\xa4\xc5\"O\xa2Y+\xc7\x9aM\x9bx2%d\xb5X\xbf\xa7i\xb4zI\xd65~\xcd\x927\xecZX\xaab\x99[\x94\x81:\xa7L=\xb6ut\xbb\xafZ51\x99N]K\xb7\xd9\xa8\xe4\x8f\xf1q\xb1\xcd\xd4&5\xef5e\xf8\xbf\x19\xb05d\xb1\x86\xa3\x91\xc6\xe4dVh\xe3\x98b\xee\xa1\x17a=D\xd4*\x8a\xc8mv\x87 5<\xa1\x0c\x15o\xe8\xd3V_\x9aU\x90\x91\x86\xec!\x15s\xb1\xa3F\x86\xa2\xdd\xa6\x94\xe2\x80^)\x0c\xb9A-\xeb\xcdp\xddp\xa6\x18\xad\x16\xb4m\xc1)\xb7Z\x94\xd5\x8dMn\xf5P%\xbeU7_n\xdf\xd3T\x94+\x98\x9d6\x83d\x91o\xb1\xd9\x84iM\x18L\xc4g\x1a\xd2\x1f\xa3\x03\xc6\x87\xa4p\xeapX#\xfe\x8da\x8d\x94\xde\x8chR3\xfdU\xdfc\x9bb\"\xfd \xee5\xfc\xfa\xa1\xc8\xbaq\xfbN=<\x05D\xee\x0d\xf4\xb0\xb83\xd0}\xba\x92-\x7f\xbf\xab{\xaa\x0f\x89\xaf\x16_e\x0f\xcf*\x07\x89\n-\xa3\x05\x19\xb3\x16\xf4\xa3\x18\xf5\xe3\x99\x17\x97\x0c\xb8N\xb7\x02\xaa'\x809:\xd7m\xa3\xc1\x01(\"A\x84A\x13\x11\x16Z5\xf2\\.hm\x8d\x95t\xf1<\xc0C\x9c\xe2\xa7Q\x93\x18p\xfe\xad\x9f%K\xd5s\xa2\x8d\xddd\xbd\xac\x95a\x8eb\xc6[\x8db\x8d\xdd\xeb\xb2\xbe%\x9a'\xdf[\x83\xdfc\xeb\xfe\x80\"\x10\xf01\x94\x02T\xef\x97p\x91\x13\x1e\xe8uB`A\xb2\x0c\xe8<\x8cA\xb4\xdck\x8e\xb1\xb9;\xfe0\xf8gv\x18\xd3#\xf3\x98NQ\xe5\x9e\x8aa\xf1\xc6\x9d\x86\xf5Y\xefI\xda~Z\xa0\xa4y\xeb_;\x07\x9f\xa6\xdb\xde\xa7>\xfb\xc7?\x90\xb6\x01EN\xad\x0d4\x04\xc1\xf8\xb8\x0c\xee\xc8\xe0\xfa\xdamt\x0e\x83\x8a!\xe2\x8d;\x0d\xeb\xb5\xceE\xd7mLx*\xd5\xf2+\xd4\xbc\n\xcd\x90\x9bE\x0b\xe24\xc0\x0f\x06\xbfb\xb71\xf6h\x9a\x13N\x1aD\xccR\xb8\xc8\xd4\x1b[\xbb\xca\xdf\x03\xc9\xca\x9bF}\xc2\xbbw\x1a\xf8S\xbd\x8f\xb4\xdb\xb8\xf9`5\n\x1f\xf3\xd8\xc4\xcb.C\xfb\xd9\xe4\xd3\xed68^\xb1\x9f}V\xb8\x0b[VZ6\xef4\xb2w:\xf7s\xb7QIqO\n\x1b}\x9a\xbcJ\xceI\xfa4\xcc\x88\xe7\x07\xb0u\xeb_\xa3\x7f{\xe3\x83\xd1\xee\xce\x83pg6\xfe\xf7\xfd\xcb\x9d\xe2\xef;\x0e\x7f\x0f\xf6.G\xfe\xe5\xd8\x890\xb0\x91;M\xf8\x8d\xd1\x0b\xdf\x9d\x98\x96\xbc\x89\x1b\x9d\xe7]8\x0d\xef\x951t\xa0\xfb\xf0:\x90\xfc\x0e#|f\x08xp\x1e\xdf\x16O\xebpzx\x81\x1e\xc9\xb6\xa5\x9d%\x8bEr\x0e+\xd1I\x0f\xb6u.\xec\xd53\xbc\x19\x9e\xd1:\xb2\xabr\xb67oV~\x9b\xb9Z\x13\xc7\x8b\xac\x1eR\x9e\x93d\xba\x16je\xae`\x8c\xe2\x1ew\x93\xc7_h\xc8:\xbeX.z\xc7\xd0\xf9LyS\xb0\x1e\x867\x17\xe5\x9b<\xc9\x85\xfe\xb5U\xf9\xda,I\x97!5\xbd8\xaf\x8cQ\xec\x00\xc3\xbb\xd3\xca(\xed\xef\x9e\x95\xef\n\xc4\xad\xa7\x1e\x01\x01G\xeet\x950\xa67\xb2f\xe6\\3\x91\xbdT\xcc\x0d\x01\xbf\x8c\xf4\xfd\x83Pe\xf4B\x99\xe0[\xbc_\x15\x9ay\x82\x97H\x16\xd306u\xackJot\x94MN\x92<\xa6&-:\xbbN0\x9c\x8fq$\xcal\xccl\x8d\xb9!\xd4eH&\xa1l\xcb\x8bx\xa6\".\x96X\x06r\xc1\xbe/\xb5i\x95\xcfw[\xbf\xc6\x94\xf1\x92\xf9\xeb\xfe\xf9\xa1\xc1\xc8\x0e\xd2\x00\xd7\xd0B,\xcc\x9e|V\xed\xaa\x9bdvhp\x08\x90\x17O\xef\xad\xd7\x11G6u\xac\xbc\x94\x80\xa7\xc8\x0fD\x7f\xc6/\xda\xed\xcf\xf2\x92\xb4\x88\x1b\xb8{H\xf7 ;\xde\xf88y\\bq\xf6\xe1\xf1\x80c\xe9\xf9\x81\xa1\xfc8h\xf5\xb9 \xb6\xe3\x13F\xd2\xd7\x01\x9c\x16\xb5#0\xb5\xfd\xfb\x00\x0e\xc75\xe1\xd5:\xf6R\xdf\xa4}E\xa7\xe6\x07\xb1\xd4 \xf2\xcfe\xf9 9\xf7w\x82\xd6\xc3,\"\x8b)D\x19\xe6\x0fY\xa5\xc9Y4\xc5\x13@G\xb1e\xa3g\xb6\xc1\xb2\x89\x7f\x85!<\xf3\xa2\x00\xce,N _\xd1\xc4\xc1\xc7\xf3\xd5\xd5\xd9\x00\xc4\x10\xe6\xe5\xd6\x99\xb7\x8d\xe69\x0c\xe1\x0d\x1b\xcd\xdc2\x9a\xe7\xcah\x9ew\x1d\xcd\xb4m\x08\x1fa\x08\xaf\xd8\x10\xea\xa5E\xd4\xeb\xa32\x84\x8f]\x87\x10\x96\x00 \xdbF\xf3\x03\x0c\xe1-\x1bMh\x19\xcd\x0f\xcah~\xe8:\x9aY9\x9aY\xdbh\xbe\xc0\x10\xfe`\xa3\x99YF\xf3E\x19\xcd\x97\xae\xa3\xa9\x1e\x89m\xe3\xf9\xdd\xe2\xb7$/\xe4n\xbc\xdfQC\x1eR\xb2C\x99\x1c\x85\xcd\xaf\xe0\x00~\xf6P\x85\xd6\xcb\x99\xb0Q\xdc}\xc7\xef>\xe5D\xd4\xcc\x17\xc9K\xcc\xf6w\x93\x1bKIf\xab\x07[\xdb\xfc~\x85!|\xf0\"\x0b\xb0qv\xbfv\x18\xe3\xaf\xedc\xac\x1c\x9emC\xfc\x05\x86\xf0\xb9}\x88\xbft\x18\xe2/\xedC\xac\x9e\xd0mc| C8j\x1f\xe3\xcb\x0ec|\xd9>F\x95\xc1j\x1b\xe1\x8b\x96\xa1\x1d#\xf3S\xb0a.\x03}!y\xd6\xa3\xd8\x1b\xf5\"J\x96Y/\x00\xceg\x8f\xfd\x00\xa2\xa6\xa1\xbb\xcd\xd7\x03\x14\xc1\xaam\xdb\xb1\xab\x82I/\xd0I\x82!\x0b\x06\xabV\x97P><\x12\x0fU*\xf0\x02\x190\xf6\xf4)\x13*\x03ap\xe7\xeb`\x1f,\xbb\xa2xJ.\xf6\xa1\xc5g\x90]$M\x93t_\x13/\xa7^\x97\x96x\xb0v\x9cP\x18\xe46\x94\xb8\x01Cx\xdd\x8e\xb47\\pA\x00\xeb\x86+56\xda\xbd5\xfe+\xcdl\nvNI:\x1a}\xbb\xbb\xb1\xc6\xd2 \xc2/\xa8\xab\xd8\xdf0h\xe9\"\xa0\x19\xbco],\x17BwE\x8c\xf2]\xc4\xbd\xae.\x96\x0b\xdc\xb6\xf8\x17\x166\xb2\xad9\xd7\xf3\xb0o\x98\x94/\xbe\xfd\xf7e\xc0\xbe\xbfq#%3\xd5\x1d`\xbdBO\x18\xda\xc7}\xcd\xff\x14%WD\xb9'\xda\x0f\xa7S\xf4M\x0c\x17?\x97O\x0e\xe0o\x8f\x0eX\xe3g$\xcd\xa2$\x1e\xf6\x06\xfd\xdd\x1e\x90x\x92L\xa3\xf8t\xd8\xfb\xf8\xe1\xf9\xce\xfd\xde\xc1\xe3O\xb1pl\x87\xdf^\xbf\x02r\x81K\x0c\x13\x9e\xe2\xf7\x84\xc0)\x89I\x1aR2\x05\x1e\xa4\xf47\xa3\xff\x93\xbc\xa4!LL\xa7\x8f\xa9\xb1\xbd[\x9f\xde\x7f\xf7\xe9\x96\xf7\xe9\xfd\xb6\x7f\xe3\x96\x05\xd9K \xc2\x10\xa2\xd1\xa0\x19\x8c\x08F\xc6B1\x16\x9eJK\xed\xf4)\xea\xcb~{\xfd\xea\x90\xcf\x8d;\x93\xb8\xf8\x80\xb0\x89$\xc2\xc3\xa8l\x8fo\x82\xe7i\xb2\xe4\x1bA\xb4\xd7\x9c\x91T\x8a\x99$\xbb\xa4M\xb2K\xb0\xbcm\xcd\x13&)=a`_\xc9y\x06Pxi\xaaYP\xac\x8e_g\xa2\x0eI=\xa9\x92\xbc\xd8\x12\x94\xe2\xfc\"\x99\x84\xac\xa9~\x86\x8d\x1b\xf4K\xa5\xde\xd2\xb4\xb5z\xa8\xa47\xee\x11y\xf0\x90~\x96\x9fd4\xf5\x06\xbe\xac\x17tS\xa7\x8d\x01\xd5C=\x85(\x86\xd8\x87\xb8^>%\xe5\x8e\x8a\x18g8J\xc7\xb2\xc5!&[\x1bM\xc9$\x99\x92\x8f\xef\x8e\x8a,]^:\xda\x1d\xfbc,\xdd;@u\xa1\xf6\x9d\xc1\x98\xdbU{.\xf8$\xb7us\xcd\x9a\xd9l\xec\xb4\xd5h\x15_\x86+\x07\x7f6\xf19\x12\x83\xea\x8c\x88\x0f\xdb\xd0\x1b\xa2\xb6\xb6\xf9\xb4\x9a\x99T^\x97~\xff\x8f$\x8aqy\x9aS\x13\x19{\xec\x83\x92\xf3\xa9d\xdd\xa0\"n\x17K\xd5yD1W\x04\xd0\xcb\xe9l\xe7~\xcf\xf7\xcb\xbb\xbd\x930#\xf7\xee\xe8\xc6Pf\x10jv\x9d`\xb8Y\x94\xc4\xd9{|\xcb\xe4\xb5\x13.V\xf3\xb0%\x97\xacz\x154\\j\x13\xe7=\x1f\xb7\xd0\x02S\xc1\x85)\xf1\x88\xfa\xccpd\xeb7\xe6\x92\xd0y2\xbd\xf2h\xf8\xe7\xa6\xf1\xc8\xa7\xceLDs\x8c4<\xfd\xb3\xc0Y\x1b\xb2\xf3 5\x98Y\xcb4\xe5\xc6\xce\xe8\x9cT\x94\x8c\xeeQ\x0cF\xbd\x91\xf4\xe6\xa5F\x0f\x11\x85m\xe1\xa5oz\xe5\xdf\xa2\xcc\xd1(\x0e\xd8\x06\x0dt\xfb3\xf5K\x9f\xfa\xff\xd9\xdb\xbdu\x1a@o\xbb\xe7\x8f\xc5\xfe\xd4-\xa9\x91J\x11\xdb\xa6\xd6d\xee\xaa\xac\xa4\xc1\xb1\xa6P\x9a1\xc25- W\xac8\xe5\xb4\xb9\x8ct\xf2\x18\xa9\x8e\xbc\ns\xa9\x143\xa4's\"\xc0:\x8f[d\xcaT:&\xcc\xd9\x98\xd4(\x8d\x96\x9e\xb2H\x9f2\\\xa3c\xb4\xd8\xf4z\xb6\xe1\x1a\x92\xab9\x0d\x93\xc1\xec\xb8\x84\xd9\xd7\xa6{Y\xa0I\xe7\xe6\xd44m\xe6\x9b\xb0\xecd\xf1\xd1\xad\x7f]\xec\x14\xccu\xeb\xb2\x05\xc6\x14t\x7f\xe6\x08\x85\xfdgS\xd8\x976\x85\xf5h#\xecb\x1ba\xf5r\x9f\xca\xff)\x1f\xf0\x94\xdfl\xa7x\xf7\xee\xfb\xfd\x1f\xf2\xd9\x8c\x08\x7fq[\xf5\xa3\xb3\"sSq\xf2\x95x\xa2\xa6\x19\xacX\x8c\xc0%S|o\xc49U\xfe\xe9\x18\x91:nT\x8cr\xca\x06\x89\x94\xae\x1cWjcD\xf59\x0eAaO\xf9T\x94d\xbc\x8bhBL^\x97\xc4\xb8\xbc<\xa4\xaa\x9aL[\xe4K\xe4\x14@-1\xe1c)+S.\xd9zZr\xfdP\xecx\x99\x97\xbe\xaf/\x9b%\xb9\xf4-\xa6\xd6\x16\xc3\xb2\xc5\x17\xae-F\xd6\x16\xb3\xb2\xc5\x1b\xae-&\xed\xb3\xbey\x13\xb6&e\xd3?\xba6\xadI-\xaf4\xbd\xe5mQ.\x87\x8f\x16c\xb7\x06C\xd7\x06\xeb\x898L\x0df\xae\x0d\xce\x1d\x1b\x9c\xb4\xaf\xf8f\x83\xdd:57s\x1d\xdf\xb41>\xf5\x17\xf1R^\x83\x85x\x91\xfc#\xe1\x7f\xc4\x8a3+\xcf\xd5\xcd\xee\xbc$kL\xcf\x17\x8a\x17\xe2)\xb9\xc0\x1b\x19\xbf\xf1$\xcb\x92I\x84\x99!\x00s\xb8\xc4e\x00\x1c`x~\xdc\x97m\xb0\xae\xfbe\x0bl\x00\xfd\xf7\x04k84\xe9\x07\xa6\x19\xf8\xfb\xdf\x8f\x8f\x8f^\xbf\xfe\xf8\xe1\xc9\x0f\xaf\x0e\x8f\x8f>\x1c\xbe\xc3?\x8e\xff\xfew\x8dji\xd5\xfc\xe2\xe5\xe1\xef\x87\xcf\x0c\xaf\xcf5\x1d\xbcyv\xf8\x9b\xf1\x83i\xf3\x83\xb7\xef\x9e\x1d\xbe3~p\x06C\xb8\xdb\xbc\xbd\x86!\x0c\xe0\xd1#]\xb5\xf3S\x18\xc2\x1av@\x93\xaa\x7fi\x90\xf7\x8f\xed5\xae\xf7\xeb\x89$A\xcf\xf9\x9f\\\xa5\x19\x13-?o9\xd8\xb9q\x18\x0b\xbb;\x92\xe4\x0b}\x8bT\x1c\x0dE\x83\xbbn\xdb\xe9=O*\xaf\x7fxh9\x89D\x84\x9bF\xaf^\xa9\x0e%\x0bH{\x98x\\\xa88w\xb0JH*r\x9e\xcb\x94\x05<\xd3\xc6\xeeCLw\x11?\x84h{\xdb\x87t\x14\xf1$\x89\x11\x13\xe8\xcd\xee\xf5\xa9\xd3l\xed\x01\x0d\xaa;:\x06\xa2\n\x98f<\\\x82\xf6\x8f\x8fy\xe9|\xe2\xfd\xc1OW\xf6\xc4\xa9\xe3\xb7\xd6Tb\x85\xf5A)\xe9a\x13\xc1P\xb9\x04\x8f\x1f?6\x995\x84\x92j\x1bb\x11C\xbd\xd9\xc0\x9d\xbd\x07w\x1e\xdc\xfb~\xef\xc1]\x9ca\x19\x99\xf8&|\xa3o\x85MZ\x93\x92\xcf\x04>\"\xcax#\x90\xb7Q\xf1\xe1\x06\x9c?l\xc5\xf2\xeb\xf9\x9c\x0dm|v\x90\xda<\x19jP\x16\x9d\xde\x92Q\x91\x14\x1e\x0da'\xae\x14,\x1cJ\xd0\xd5_&\xf0xXW\xc0\x9a\x06v\xd4\x96\xbd\xf1\x83\x18\xb9\xe3\x86}\xed\xda^\xbd\xaa\x8f\xa1\xbd\x0f\x0e\x80\xab\xc5i\xc4\x986\x97/\xb6\xba\xbf l\x03\x1a\xc5j\xb1\xb4\x8cC\x92\xe5\xe2\x99\xbc`\xac\xde\n\x02\xbf\x9f6\xabT\x83pd\xd6\x9c\x07\xef`\x08{\xcd\xdbo\x9c\xb3\xb6\xf3M\x9d\xa4\xcd6^\xf1\x93N\xbe\xa09\xda\x9e\xc1\x10\xde0\x1cye:\x02\xbe\x1a\x08\xf6<\xca0\xbb\x8833\xfe\\\xae\x94!\x99\xa7\xb4Z\x94\x0b\xc5\xb6\xe0\xa0\xb2l#\xf6\xbd\x85\x8a\xc2\x01\xa4\xc5\x19\x12\x89\xb2\xc0\xd6\xd3\xd0\xe0\x078Mb\xd3\x89\xebH\xab?\xda\xa8\x82uH\x1c\xfd\xac\xe3j\xad\xdcc\x18\xd4\x0fv\xees\xebWW6\xf6\x8b\x9d1\x00S\xd5h\x8a8\xe3\xd4\xc5\xefv5\xe0\xaf\xda\xf4\x1d\x05-\xe7Un\xb5\xc5\x96\xf5\xdd\xfdj\xef\x8e3(o\x90\xd6\x8e\xde`\xedR:ze\xcaM\xa4\x9d\xbb\x92\xb7\xdaiD\xbf8\xc0X\x13\xcc,\xb8\x14\xa7.^Z\xbb(\x92\x01\xa8G\x8e\xdc\x8e \xcf\x95-\x85\xe8>M0]\x83\xb5\x80\xb5\xbc$P\xd1y\xbd\x12\x167\xac\xd5\xe6!\xe7@\xa85\xc3\xfb\x96\xa9^\xd8\xe1\xc5\n3\xd3q\x06\x0d\x92\x14\")\x15 5K2\xe3[.\x0b\xd8\xd3\xcf(\xdd\xf0G\xfb\xe8.o\xeaV\xbb\x8a\xecj\xa6\x083\xc0\xfd\xc5\xb7\xc1\xbdO\x13\x94\xc5$\xc4\xc5\"\x84\xcd\xb5\xa0\x98\x9f\xfd0\xa6\xe9\xbax\x99\xba\x8e\xf2\xc6\xb7\x8dR30\xa2\x0e\x84\x8dSH\x91\xf2V\xe8<\xb6\x1f\xadc\xf3\xbe}pr4h\xe0\"\x14\xef\xd7F\xa6\xfe\xfa\xaa\xa8\xaa\xa8&\x1f\x81e\xb0\xbd\xd1\x918\xa0\xc75\x05t\x00_\xfb/\x0f\x7f\x7f\x0fCx\xca\xfe\xfe\xe5\xc9\xab\x8f\x87\xec\xd7\xcf\xec\xd7\xe1\x9b\x0f\xef\x8e\xf0\xe7\xbb\xa0\xd2\x7f\x14g+\x9e\xed\xbc6\xaa$O\xab\x99\xb9m\xf4\x85\x1d\xf0\xe6\xdc\x0bJ\xcb\xa3g\xe3\x0em\xd6\x1b\"\xdeK\xae\xb7x\xd9Of\x8e\xed\xbc\xf4\n'\x92\xc6\xc0^V\xa7L\xbe8\xb6\xa9\x1b\xdb\xcb\xab/*\x82\xef\xf8\xb84\x8e\xb2\x91\xfc\xbb\x17@\xef\xb2i\xcfQ\xfb\x99\x84\x939yG\xb2\x962\xc7JW[\xbc/\xfc\x10d\xc5\xafB\xd6\xfb\x18\xe3\x83)\x17\x06\x957\x87\xfc\xc5\x12\xeb\xcb\x8a\x0f\xa2\xfc\x99\x14\x1c\xcb\x8f\xc4\xd9\"^\xb0M\xa3\xe8\xdf%\x86HLdB\xcb\x82d\xbc\x02\xa8K\x0f\x89S\x00\xbe\xe8b\xd6\xda\x05\xf1^\x04\xf0\xd2\x0f\xe0Ee\xf1%\xbdu\\\x13=\xa6\xdf\xe0-\xdfp\xc7\xf4\x1b\x16L\xbfQ\x19`II\x1d\x9b\xd6\x0d\xf1\xc65#\xfc\x88!\xfc\xb8\x89\xf07\xae\x19S\xea\xb5\xdd\xf5=|\x13\xa64\xbb \xde\x8f|=\x7ft_\xcf\x1f-\xeb\xf9c\x8dr\xd1o[\xcb\x97\xfd(\xe3-D\x94\xfd\x92\xda[\x86\xdeB]\xcb\xc6\xaf(ro4\xb5\xb7?\x05\xf0\xcf\x00~\x0b\xe0\x1fM\xa5\xe9\xfb\xc3\x7f\xa0\xc2\xd4$9Rj\x11\x1d\x8fCQ+\x83\xd6\x88M\x17\xf6\x95\x18z\x90\xfc\xa50.}&\xebL\xcbC\xf2\x91$\xb26\x88\x1c\xca\xf1gQ\x0b\xab:4\xd2eh\xb1u\xf2Q\xa9\x9f7\xcc\x9f{\x16:+\xe8\xd2\xf6\xee\x84\xe1,\xa8\xdd{*\x0e\x83zm\x1fCG\x91\xa1#y\x16\x95\x06\x8c\x7f8\x1aX\x90\x1b36\xf8\x13k\xcd\xfbI\xe8Z)\xf5F\xe3Ff\x16}\xbby\x0brh\xd2\xe0\x88.\xa8\xdf\xe4\x9a\xbf\x94o\xa4\xfa7~(\xdf\x88\xf5oh\xa5\x9c\x83R\xc8)TOf\xcf\xbe\xabK:\xa3\xcf\x01\x9c\x8dAd\x8a\xed \xf1t\x92Y\xc3\x16\xa0gza\xee\xdb\xa7\xc7\x05\xb9k\x9aEfG\xf2_j\xd8\xa2A\x0f\x0d>\x14\xab\xeb4\x04v\xc29\xa9\xcb\xa8`\xcd\xf4@\x8dL\"xa\xe5H\xd8\x01QZ6\x06\x01\x864\xef>\x84\x1c\x1e\x0d!y\x08\xf9\xf6\xb6\xa9\x11\x10\xe3\x08\xd1S8f\xa2\x15\xec@\xced+\x83\x7f\x15\xc8\xc5\xe6z=\xe2\x85\xa3\xc18@\xc5]8\xda\x1d\xb3/\x03P\x02\xdas\xd8\x86\xa6\x12\x0e\x1a\xe2\x97\xbc\xe4g\x8d\x87\x96\x04s\x0dV\x99g\x83tZ\xa6\xd9\x9f\xbcL\xda\x152B\x96\xaf\x9c\x0d0\x0c\x1b\xbfzV\x96B^\xd2\xf9\xc3}a%\xf0\xb7\xb7\xe11:W\x9b\x1b\x077u\xa7\xbc\x8cjOy]\xc2>\xc7\xcc\xb9P\x1f\xa9i8s\xfbp\xa4E\xbe\xe2w5\x94r}\x8e\xf4z\xa8\xe9\x93j\xbe,\x03\xb8\x05\xbb\x85?\x8b\xf0{\xf1\x03\x89\xce\xf2C\xdb\xc1\xf6\xcfbh\xff\xd4#\xce?\x85\xcd\xa0e\xab\x99\xa0u\xda\x02-\xaa\xaa \xb8\x8a\xc0\xd1WhIm\xceB\xfa\xa5X\xd6\x96BiC\xbf\x1a\xa7\xd4\x13\xaeV\x01\xf4\x9e\xf2(\xde\x8c\x92\x15\x84\xf0.\x8cO \x9c\xaca\x17\x83\x1eAX'w\x83\xea*\xc9\xba#\xb8V~\xa0$\x01\xe0\x9eo\xa2\x1a#.ax\x92\xa1\xeb!\x81G\x82cco\xef\xc4\xd2\x84s\x8c\xc5\"T\xbd\x1f\x89\xa7\x8aj\xf3\x18\x87\x86\x83U\xb1FE\x0f\xfc{B\xa2\x85\xe7\x11\xd8a\x04\xf8\x16\xc4L\xb4\xf2\x99l\xde\x0dw~+`\xf9\x9b\x1ew~\xfb6\xdc9\xd6\xeb\x129\xbe(*\xa5'\xa2\xfaa\xdd2ah\xf6\x84\xda\xdcL\xcf\xadO/\xc4S\xf5\xa1b\xc6\x1a\xfdc,\n\x01\x11\x8f\xd2\x00n\xb0\x95S\xe3\x1eN\x89SIW\xc9\xb5\xb3U`\xe4\x91\xdb\xb4KM\xfb\xe8\xad4g\xf8c]\x05\xf3J\x9f\x9dL2\x15\x7fY\xa5G\xe1![Q-\x95\x1e\xb2CH\xb9\x8b\xac\x11W\x84\x8a\x88z\xf1\x88Q\xae\x14v\xd0\xa3+\x1a\xa3\xf0\xc7:*wf\xc4P\xd1H\xb5\x1bu\x1d\xb4\x93u\xb3\x0e\xe9&\xaa\x9dBc\xf2\xfa\x89\xea56\xdd\xb45\x05\x10\x1e\xa3\xfa\xc3\xc6\x819i\\\xac\xda\x16\xaei\xa1\\\x02/Wf{\x9b\xad\xcd\xf6\xb6C\x14 CuB\x03x\xc1\xe8\xd6\xd5Q\xbd\xee\xe5\xaaC}\xae\x1f\x1eQ-\xcaW\xfa\x9e\x87\xee\xf1lJ\xd3\xf5(wM}\xa2\xeb\xdcX\xbcS\xbe\xb3JSU \xd8ju\xa7%|\xa7%l\xa7E\x0f!1+q\xcfDY\xbc\x14\x173\x82\x1dH`\x1f\x12\x83\x9e\xaf\xb63\xf31V!\xae\xee\xc6D\xab\xb45\n\xa3\xcd\x14\n\xd7\xb5=\x05\xb8\x8c\xfbS\x01\xa1qw\xa6\xad{8\xb9\x8e=\xdcm\x15$\xe4P\xd3\x1a\xfdu{>g{>w\xdb\xe3\xca\"\x8e\xa6\xe5!\x17\x8bC.\xd6\xee\x8b\xc2[\xc5a\xad\x19*\x96\x121\xaeeEhR\x84\x0c\x03\xf7,\xb1\xe5w\xafj\x96\xb5\xd4\xb02\xe8$\xbex\xb1A\x06-vq\xf4\x10\xb6\xbc\x08O\x05\xb5*#(\xb9\xbc\xbdHT]\x84t{[\xec*]\xfdR1\xe5F\x8e -LK}\xf5\xb5\x025I;C\xd5\xa0\xce\xf9\xa2j\x89\xf9v\xf9hh\xd6\xb0\x02\xdd\xb7\x1aQ\xd6\xa1E\xcb\x81\x8b\xc4\x9d\xd1q\x0f\xe0\xd2\x08\x15\x9e\xd3F\xf0R\x81\xf2\xe9\x7f\x01\xcaW\xea\xc8\x17$\xb0\x08!\xe0\xb6\xaa\xa6\x83\x80z\xa0\x14\xc6\xa8\x87\x0e\xcc[4J\xc6\x01#T\x8dC\xc206\xb6KbEK\xc4w\x89\xb1\xf2\xbc\xa4\x9b\xb1M\x9b\x84&\xb6Q2\xe6\xe1\x90\xc5\xd8\xf2\xea\xc0NR\x12~n.\xa8 \xdb\x1a\xc7\x96vy\xffc\xbb\xaf\xb6\xb0F\x82\xa6[l=\x10\xafc\xef\xe1J\xc0\xe3\xf2XmS\x18\xb6oT\x90p\xe3En\x8b\x8dkQ,\xf2\xa0<\xb1\x87\xb5\xafY\xad\xcb\x92\xfdMG\xee\x0c\xefZ\xd0\x805\xbd\xba\x8b]M\xd0\x86\x03\xe8\xbd#+\x12R\x18\x8d{\xb0_\xfe\xe2^\x10\x8aZh\x1bz\xe5=\xfc\x96\xdd\xa1\xd1\x92d\xd0t:^_\x9d)\xd71\xe1|\x08\x1a\x06\xbc\xd2\x8f\xac\xf4\xe3\xca\x85O\xa9\xaa\xf8jFe\xd5\x9a\xc7\x94\x05.\x13\xa9\xec\x1f\x06*#\xca+1{|\xaa\"U\xd2\xba6\xb2\xd7\xa2\xba\xe4\x0e\x0f\xa6\xab3\n\xf5\x91\xa6\xe4\x8c\xa4Y\x177\xed\x16\xb8N\xc9\xc5\xdb\xd9\xd5\xc1\n\x07\xa81\xdc\x19X\xbbY\x84\x19=\xba\x86\xaeJ\x0cm\xed\xf2\xea\xc2\xd4\xeeC\x88\xe1\x91\xb2\xc4\x10;i\"*\xc3\x8d\xeb'ZlUB\xc4Ns\xe9.\xe5tbU\xbb\x11k\xc9f\xc2#\x88%\xc5)Y\xa0X@\xc27\xd6\xd9\x83\xeb\x12?\x1c(l\x05\x9a\xc2H\xe9\x88\x87\xb4\xaaz\x87\x83&f*S=k\xda\xfb\x19}_\n\xfa\xbe\xbcf\xfa\x8e*cI\xde\xf9\x0f\x85\xbas\xed\xee6\xf4\xfa\xfd~y\x97\xc4S\xd8\x06O\x08\x15\xf3B\xcd{\x00=8YW>'+\xcc{\x84I\xe74'\xc1\xf2zO\x029\xdcR\x17 \xdfU\x87\xd28#\x96W:#$\xe7\xe0Q\xd8Q\xfb\xf6\xe1\x96\xd2\x9fq\x7f`\x80\xf4.7\xc8+d\x82\xdf`k\x84:\xf1\xd9\"\xd1\xd8\x1ejCv>wj\x87J\xd1\xa9r\xb8\xa0K\x01\x9e!\xe5\xd3\x80\xdb\n\xf0\x8c)\xef\xfa\xf0hX\xf8\x96.\xa9\xb7\x1b\xc0\xae/\x8e\xa7\xa5@\xeeSB=\xd5* M\x06\xec>\xd1\xdcG\x905\xcf\xae\xe5U\x0e\x9b\xb3\"\xaa\xb2\xb2B\x0d\x85/\x18\x031.\xc3\x1c\xd4r\x07V\x87\x03\xe1Z\x89N\x96\xece\xeeSa\x19((x\xba\x0b\x1b\x93s\x14\x1e\xa1qY\x8d\xd3\x8b\xe1_C5G\xd1w@\xfd\x87\x0c1\x94\x9b\x0f}\xc0\xd7(\xdcR\xdf\xb5\x12\xdcC\xea9\xa5J\x8f\xea%]\x145b\x99\x9a\xffg\xaax\x99\xeb1\x0d\x94UxEG\xd4\x9e(\xb7\xea\xb1\xf2\x96ao\x00o8\xac\xdf\x89\x9c\x19\x14\xd3\xe1\xc0+\x9e\xe8\x1c\x9f3*\x8e\x8d\xb3\x83\xef*Y\x16`\x9fw\xd6 \xc7\xe7a6\x7f\x9aLU\xc8\xc8[:\xe5bT\xaf\nV~\xe8\x08B3\xe3\xf9\x9a\xd6\\M\x11~G\xdccM\xadPji\xa3\xfe5\x1d=\xa5c\xa7/\xb7>\x1b\xc7\x0d\xa6\xc6\xfb\xa2\xea\xc1\xfa(;\x8c\xf3\xa5\x08\xc0Bw8\xdd\x13\xa7\xb1\x98:k\x07\xaf\xfa\xb5p\x98\x8c\x93)\xf9\xb0^\x11@\xd2\x9e\x9dG\xbc\xfeYq\xbf\xad)vM\xc2\x8c\xc0`\xbf\xf5=Ph\x7f?\x8f\xa3/99zf\x9e\xa3\xbc\xb0\xf9\x07\x1d\x9b\x9f&\x13\x0c\x18>\\\x10\xf6\x0f\x9fl\xedf1\x06k\xd3z\xa56\x88-\xa5\xac\x96\xf6=\xfd\xd7l\xb9\xb6\xb7?\xd0@=\xfan\xc2\x07\xbe\xf7?\xe0\xde\xb7\x84\x88\xbc\xa6>\xc3\xfa\x8c\x18=\x1c\xc1\xc1\xd1\xb5\x8aB\x7f\xc8\xfa\xc8C\xfc\x81.\xcfu\x8f\xc1\xde\x9b$\xde!<\x95q\x19H\x98A\x98\x12,\xfa\x86\xd9\xb5\xc9\x14\xc2\x0c>\x93u\xd67\xd5=\x90\xdd\xb3\x0d%\xa2\x8dy9\x89\xd2#$\x80\xa7\xd4\x14W\"/R\xec\x9b}\xd8\xb2\x04x\xb1k\x92\xc4\xb3\xe84w|\xfb<\x8d\xa8\xdb\x9b\x82O\xd7/>\x80\xb9\xa4\x1e\xa8\xe5\x0d+N\xf5\xddH\x86`\x93\x95H\x12\x85\x83\xd7}\xe0\x1b\x1b\xb2\xab\xdb\xd4K\x95\xb5\xdd{\xee\x87\xab\xd5b-\xd8xCD\xbfz]\x06\x162\xc9\xce\xc0\x16\xc8\xb6\x13\xc1\x8aSzI\xf2\x1ax\xff1F\x08\xd1\x042B!\x84\x98\xed\x83\x12rr\x8c\x90\xc4bOXQ\x9f]T\xce\xc1<\xfb\x0e\xf4\xc4z\xeaw:\xed\xa5\xf2\xb5 k\x8caP2\xdah\xf3\x01\xd4\xa0\xc5\xcb)\xb3&y\xfddT\x93\x96\xa5y\x18\xf7@\xa6}G/\xd2\xb7\x06\xde\xbeP\xc7\x10\xce(\xa9\x16\niiG\x03\x05\xbep{\x00\xdf\xf1T\x85\xfd\xc9\x829\xf3Ld\x15\x16\xd6\x97)\xdc\xbdu\x9d\x11\xfcW6_r\x85\xa7\x92\x01\xeau\xb82\xa6<\xfb\xfa\x8d\x96\xc5\xe34IJ\xcd,\xfb\x81\xa2s\x11K\xc3\xf36\xf9:\x93b\xa5\xeb\xacS\xd7\xffP\x93B\xd9\xe7\x94\x11z\x14wh\x1a'\x92\xaf\xa6!%G\xf8\xf22h?c\xcd\xdc\x92}p)Y&g\xed\x92\xb6f\xd6K{\xc3S\xb2 l\x02\xaeM7f\xed:\xe5e\xd7)\xf3N\xea\x0bbO\x1c\xcdE\xc8F\x89\xcb\x03\xe1\n\xe2K\xe3L1\x81\x11\x1d\x8bF\x1d\xc6\xd2D\x0f\xc3h0\xd8\x15\x9d\"E,&Gq\x8b\x8flA\xa2]\x12I\x9c\x898P.\x80-\xcd:\xd1\xbc\xd5\x17\x8f\x91\xbb\\\xf8\xe1\x99\x89\xe2\x99H\x19\x93`\xf0Hk\xc5\xd8\x0c\x86\x10y\xb6\xb2\xdcb\xb92\xbe\\\xc2Y\xb7\x19C\x06F\xa9\xe3\x94z \x03\xb2\xc8\x1b\x9c\x11\x1a@/\x8ay\xb5\xfb\xcfd\xfd3V\x883Cf\x82%\x80-\x1e\xa8\xec\xa5\x99\x98\xf2\x92M\x19\xa9\xd5\x84\xed'\xf3\x07X\xa0\xd4\x9b\x95\x0bhU\x94r\xd6e&f\xcf\x7f-\xd9/\xb1\xdb\xbd \xc3W/)y\x19\xe2\xe3\xd91 `\xa1\xe1\x01\xc4\x9e\x8fc\xd4\xe9\x1a\"\x1eE\xdfi\xd1\x9b\xe0\x9a\xea\x96\xd9\xfa\x0e\x98,Hh-J\xa44\xdet\x8b\xa1\xdc\x1fB\x1c8\xc9yL\xd2\xa3gp BaE\x0c\xe3n\xa0\x9e\x14CQ\xb4S|\x83\xc1\xfb\xc3\xf2\xac\xe0w\xc3\x05\x15\xf5N\xb6\xc4M_pw\xd6\xc9,Iz\xda\xaat\x90\x90\"\x02\xae\xb2ks>\xc0f\x1f\xbfF\xd5\x92c\xb6\xf3\xa4\xe8\x08\xfd\x97\xea|\xd2\xa0\xe9\xc8\xd1\xec\xaeJ\xa0\xec\x86pM\x0fFl\xa9\xd2L\x12 \x84\x03\x07\xad\xaf\xf8\xde \xf0\xf3e8\x90\x7fI\x1d\x0d\x12\xd5}\x88Gj4^\xb3\xa8m\xcb\xf1\x81M>#\x18,\xdbi\x9d#\xd2m\x8dY\x1fN\xeb|%\xd0\x17\xc3J\x88\x87b\x85\xe3\x88\xfe7\xa2\x02\xae\xd6\x81\xfa\xebzQ\"KR\xea\xca\xe7\x1c\x11\xef\x17R\x98\xfd\xdb\xdb\xfda\xdd\x81uT\x1b'\xed\xedWd\xa0\xd6 \x14\xb2\x16[\xa90{\xcdu\x11:\x06@.)\"\x16\xe9\x9f\x87\xd9\x13NO=\x1f\x8f\xa1\xe3c\x12gyJ\xde2z\xedU\x89\xb7d\xa5\xac\x03/zw\xdc\x83\x8d\xf3\xa1zn\xa8\xa3a\xa2\xd8{;\xd8\xc2\xecHjb\xba\xf5\xaf\xf6\xd3\xb22\x05\xc8\xba\xf5 \xce-k\xdb\xdd\x1c\x9c\xa4F\x84\x9c\xc3\x0dw\x99\xa7\x93\x17\xda\xb7:1+\x87{\xe1m\x83r`3\xb3H\x0b\x11\xe1\xc1v\x1e\xc1\x043\x043\xca\xe8l\xee\x01/\xfb\xd4\x02\x01e\xb5[\xf7\x96\x9cI\xc9\xe0\xe8\xb0\x15\x0e\xe0\x9f\xb4dmT\xb6&(\xf3: K\x83\x1c^\xad!%\xf7\x83\xca\xe0\x0c\x04\x83\xa3\x99N\x941\xc9}\x08\xcf5\x9eC\x1fi\x00?\xd0f2\xe0\xd7O~6TO\xfb\xc2\xdeV\x81dR\x0f\xfenN\xfc\x81\xc3oNH$*j\x18\x1f\x8c5>\xac @\x0c\x9d\x9cDt\x89\xe0\x90\x90\x8f\x13\xee\x82\x1c;\xf5\xf9\xcbU\xfa\x9c$yL\xaf\xdc\xe5\xcb\xabt\xf9\x99\xac\x7f\xe4L1i@\xd7\xad\xdb\x17\xd7\xd7\xed\xda\xb9\xd3\x1b\xed\x9d\x1eS^j\xb4\xdc9E\x84M\\\xfa6\x87\x93\xcf\xc8\xbc\x14\x14\xe5'\xea\x89_n\xda\xd0\x1f[S<\xf2\nH\xa6}\xac\x0b\x025!\x0f\xad\xa9,$fGAA}\x10u\xa9FM\xd1\xd4Q\xf8X\xe4\x0c9\x84\x08w\x9bN_a\xc0G\x11%^\xe8\x97\xf8\x82\x06\x10Zy\x15&Qq\x89\xcd\xd3~\xba\xcf\x10Q\xac'e\xfc\xc8\x85\x17\xfa\x01\\x\x0cU\x18\xc4_\xc8\x1c\xae#\xf6\x99k:wB\xec;\xbeVy6\xf74\x9eEF\xf2\x92K\xa0En@\x8e\xac@.v=zm\x95j\x95\x9b7\x01\xb3\xb0V\xd4+<'c\x91\xd8\x97o\x7f7\xce<\xb1\xef\xeeR\x9433\x15\x002\\\x0cu\xf8Ue\x1a\x8e\xb7\x92\x8c\xba\xf2\x9c\xab\x84\xcc\x9ax<\xb9\x8a\xce\xadjx\x9e\x8d2\xf2\x85\x1e>jY9\x13@r\x97e\xe1\xdb\x1c-Cq\x7f\x16\xb1\x93\xc1\x01\xfd\x8a\x8f\xcb\xc4\xb9\xcdA\xfa\xbeb\xedb\x07\xb2\x9af\x17\xe9jy\x8am\x18\xa9\xc0\x94\x87\xca7W7\xb5\xa7\"\x1a\xaa\xf8\xc4\xb6\xe2\x80&pq\x1e\xa5U\xabi\xab\xf7pE\xfe^\x8a\x1a\xa3\x08x\xec\xd2\xf8\xad\xc6e\x02o\xabA0\xa6\xa5\x93\x17\x95n\x19\x86\xf4\xb1\x97\xd5z\xd2\x05A\xc3\xb2\xd2\xf1(\x1a\x17\x0e!\x9a\x81bf\xf2\xca\xd1\xe7\xc5\xa3]G\x89#l9iA\x84\x86x\xf7\xef\xde\x7f\xf0\xe0\xf6\x9d\xbb\x0fx,\xcf\xce\x10\x03ax\x1c\xcc\x9d\xdb\x83{w\xef~\x7f\xef\xae\xef3f\x0f\x1f\xec\xc1M(\xbeQ\xee\xdfa'\xd3\xde\xdd\xbd{w\xee\x0en\xdf\x0d\x80\xc2\xb6h\xea~\x00\x83\xbd\xefy\xf3\xf2\xde\xe0\x9e\xdb42\xe2(\x85\xa4\x02\xc5\x0fm\x15E\xa3\x11\x19\x0b\x01\xa3\xd6\xbb\xfa\xeb\x0b\xba\xba\x08\xde\xec\x0b\x15\xe6p\x18\xb2\xbf\xb9\x15.(\xffD\x9dz\xf1\xd2Q\x1c\xc0\xef-N\x11\xe6\xb9T\x0eCUz\x17\xc7\"g.\xa2\xf2X\x84G\x90\xf3\xd3\xd1HH\xa7\x88\x9e\xd1(\x193\xd4)s-\xb2\x1b\x03\xe7R\xe6\xb5Y\x19\xcd\xf0*\x1fi\x9d!\x16\x1b\xe1;6\xc0\xd3\xb9:\xdd \x9f\xee\x0c\xcfc9\xdd <\x02\x8cm\xda\x9abB\xe0l4\xc1I=\x84\xc9\xf6\xb6\x81![\xc0\x90\x7f\xa7\x17\xc8\x16p\xc0\x9b\x19\x8cq0\x11\xec3\xeeWQN\xea\xbf\xe3|\xb0\x17\xa2g\xd4\x02]\xc9.\xbc\x84IQaIH\xb3\x96\xec8\x18\xc4\x81\x0e~[!\xfb\x7f\xe1\x9a\xf0x\x08\x13]\x98\x8a\x15y\xe4\xc5\xa5Z\xe9\xb1\xf8\xdebp\xaf\xa0\x9b\xe0\xfah\x00\xe8\x88\x1a\xc0\x88u4\xf6+\x1c\x19q\xe1\xc8\xe4%\x9d\x0d\xc8\xc8\x94\x00O^\x11b\xb5 \xff\xb4\"\xa2\xe6\xa8h\xc9\x8d\xd5?@\xcbE\xc9K\"\xbb\x9e6\xb3\xae2\xabQ\x9eMa\x05\":LQ\xf0J9\xd3\xd81\x93\xf7V\x0c\xb7\x90\"em6\xff\x03\xe4\xaf'\xc2\xf6\xbf\x03\x038\x80y\x7f\x95\xf0J\x10\xf3\xd1\x84Q\xa3\xc6\x8d\x11\x1b9\xe3\xc7\xe7\x9c\xc1\xe4\xbf\xfd\x00{\xf6j\xda\xbfyi\n\x97\x02s\x00\xf36\x96\xf42\x80_\xafL\xce\xb4\xd1e\x88]\x86\xcd\x8aB=\x13W<\xafZ?\x9cG~R\x94}\x0c\x9a\x91D\xd2\x10\xae\xe95\x126\xd60\x93snr\xee\xae\x08\xcdF\xe5\xec($\xfc\x11fF\x1e\xf38..#\x11\x1d;Q\x07\xcf\x95\xe9b%3\xb4L\x00\xfd\x84z\xa9 T\x8a\x80H\x04\xcb\x13#\x90\x88E\xaa\xcc$|C\xfd\xf3I\x15\x86\xfa\x97f\x18S\xb95\x04o\x027A\x87\xdaH\xd7\x90PGue\x8e\x96\xa0J:\x1d\x12\xde$\x02_\xdf\xf9J\x8e\x10\x97K\xff\x0e\x1a\xdd\xe1\x00V\xa3\xc5\x18Z\n\xb1sE\xd9\x9c\x9b\xc5\xf8BW\xd7J?;\x1e%>w8(8\x1c0\x94|\xa5\x90\xf7\x99\x95\xbc[\xdc\xbc*\x15\xbf\x04C\xc0\xf63\xaf7\xb3\xf6\x03\xc4\x8c\xdd\x87\x82\xd5\x8f\x1fB\x88i~\x18n\x0ca\xe0C>\n\xc7\x88\x067Q\xb3@F\xc9\xf6\xf6\xd8R\xb3\x0e\x14\xa1t\x94\x8e\xb9\x8a\x8b\xf5\xc8M\"\x98\xe3A\x1f\xcc\xcf\x1e\xaf\x02\x98\x04\x10\x0605@R\x9c\xe7\xec\xffj\xb9z\xb5H\x7f\x93*\x11\xb4x\xb2\x04\xb6\"\x12\x0df\x81c\\\xeaWxS^q\x0eRQp.W\x88?{k\xe03V4\x1fc\x9ck\x0e\xdb\xc6\xd4\xb8\xd0~xs\xa8iA\xd6\xc2!\x15\x1c\xb6\x84\x9a1M \x14\nu\x84\xda\xb6@\xaa\xa8\x84\\!P\xb8\x80.\xa9\x80\x8e\xab\xd6\x10tb\xcf\x86\xf0\x08\"\xdc\xb1>\xbb%h\xbb\x97\xf0-\x1b\xf3\xd7w\x06\xa8\x9d\xe5\xf7\xe8(\x84m\x97rn\x86\xc2\x1f*\xee\x19\x8f\xcc\xe3\x82\x9d(\xac\xa8'5\x93\xe6y\x95\xbb\xe0&\xda\x93\x00\xce\x1b\xe7\xe5/\x7f-;aa$Z\xf8\x08\xce\x10Df\x11)\x81\x03Ht,\x82\xceo\xf2\x97\xffel\x82\x94\xcd\xb4/L\x1cNa\xc6&LF\xa1\x81Lg<\xf8\xc6\x911\xa0\xc4\x9bu=\xa2\x85#\xadC\x0f\x05O\x81\xf6z\xc3\xb1\xd2.\xc3\xed\xec\xac\xe0\x11,\xae,\xb7U\x08\xecn\xa0?\xe0cy\xc0s\xa1y\xc0%\xe5R,c\x14d\"\xce\xfc\x0c\x1e=\xc2#\xbf]L\x9b\xa1\x98\xa6[\xac\xca\x9beT0\x1e\xb3!\xfe\x89\xb4\xd1\x8b`3d\xc2T\xce\xf9 \x06yc[\xad\xf2ZIB\"-k\x01\x92\xbd\x98 \x87\x11\x1a\xcd\x8c\xab\xedm\xfd\x9a\xcf\xbb\x9e\xf2\x8cS\xcc\x88\xc7\x99\x99\x05\x93\x9c\x8cta^\x90K\xe9\x00\xb2\xaaQ\xcbi\x95ZrNj\xc5\x98\xa4:\xd9xyej\xf9\xdf\xacKz\xf9\x9f#\x86\x82\xae\xe9wy\\\xe6Z\x14\x86\xbab\x8e\xa1\x92\xc0\x8f+\x7f\xb8\xbe'&\x8a_\x1d\x0eZH\xe1\x9a1\x14K\xf2\xff }WXr\xee\xb3\x8a\xd5\xf4E\x99\x97P\xc0\x92M\x80\xb1\xee\x13\x93\xf1\xb4\xb3\xa6\xa5]\xcb\xf2\x1f\xd4\xb0\xbc\xd4\x00`\xde\xd8\xe0/\xae\xbc\xc1\xa5\x18\xc3\xa3B\x0b\x9f+\x86 2\xa2\x8e\xdf\x18\x8cu\x0c\xc9\x8b\xeb\xd9\x835U\xaev\x99\x90\xe4!\x06W\x87i\\./\xc3\xea\x19\x05\x12(\xf3\x08\xfd\xc6F\x0ce\xc0\n\xc3H\xd8\x87\x0c-\x01Z4\xaa\xac\x1a\xb68,\xca\x10\x89e\xd3\xe1\xadXv\xde\xa5f\xd7#\xd1)w~c\x91+\xba\xf3\xd2\xb9\xf6\xa5\xfeve\x0d\xac\xa4=n\xd0\x91\x94\xd3\x91\xa8V\xb6\xe8!\xa4\xa2\x84L\xea\x94\"9.\xea\x97\xa0\xe7\xc1X\xadwY\x9f\xdc\xaf\xfaY\xfcrm\x93\xe3L\xa6\xdb\xd4\x0c\xbcN!|\xd5\xe6\xa5\xe7w\x18(\x12(\xb3\xcf$\xfdJ9\x06\x13,@\xa7=}qE0H\x8a\xac\xa0k\x03\xad\x88w\x83\x06\xf0\xd5\x0f\xe0\x86\xdaKL.ZS;\x14P\xa6\x12\xca\xe8_\x19\x94A\x02\xdc\x99\xf2!\xd8\x8b6\x88\xfa\x13\x04\x17\xc9\xac\x0e\xc7\xd4\x98<\x0b\xaa\x8e#\x03)f\x8b\x89Z8\xd6\xa8\xa8\xadZ\n\xe1\xdcg3\xd5AI^\x97en\x9bT\xee\x96\xb6n\xb0\xbe\x99\xa8b!>Q\xf0\xce\xd7v\x1f\x91l\xc4\xc1'\xddS\x0f\xb0\xcc\x1e\xafy\xd6:6\xb5KD\xfbj\x87v\x95FR~f\x19\x83]\xd1\x91\xb4I\x0b\xf8\x92\\\xa6\n\x00\xe4]\xbb\x0cQ\xc3/\x18\xc2O\xd4K\x8c\xf6s\xb0\x8a\x0b\x93$\xa6Q\xdc\xa9\xf8C\xb3\x7f\xe5W\x9f\xfb\xcc\xb6\xecj(\xb7\xa7ic\xb4\xe6J5\xe6I\xad\x11\x90*0\xd9*c\x1e\xea5\xdc\x82;\xcd\x96g\xf2\xd9^\xf3\xd9\xa2\xf8\xce\xe4\xb9\xbf2x\x0c\x9c\x89\xd8\xa1\x0bc~=\x87<\x96\x9a\x88Z\xf6\xe5\x9cxJ\xcaI\x8d\xf0-O\x82\xc8\xa3\x96\x0c\xa3\xb1\xbd\xc6\x03\x1fL*t@\xde3~\\\xa7\xf0\x98g\x8dN\xe1\x11\xac\xe1\x00\xce\x89\xb7\x8b\x0c\xcfY \xe2L\xb1\x10\x04\xf1\xe2>M\xb8\xfc\xedcYZ\xd2\xd9-\x06\xfdD\xdeG_ \xf6\xacI\x03\xd2\xa6\xe9-4\xb5-\xfe&:/\x127O\x8b\xb9\xddaD\xc9\x032%-y@\xd8ArN\x19\x9bL\x1c\xf2\x80(\xc2\x87g\x8e\xb1\xe49\xbc\xc4\x11\xf7\xad9-^E\x19\x85Q/\x80\xde\xb8\x99\xd4\xa2\xd2\x93cR\x8bH\xd6\x8a/\x93\xe2\xfbEVrZ\xcdJn9M\x99\x00[\xb0\x96\xe8+\x83#O\xd2\xe842y\xb6I\x99\x8b\xf5\x14\xf7y\x99P\n7\xe1T\x13\ni\x02P#\xbbF\x05\x06\xdd\xb2k\xb8\xda/\x10d\x84\x83\x8c\xb3U\x95\xaa\xf9&\xbfo\xf4\x0d|\xac:\xb1\x11x\xa4d\x83\xed\xee\xb2\x06x,<\x82]8\x80\xb7\x82\xc7\xc3m\xb6+\"L\xdfJ\xa7\x04\xb4\x00\xf0gD\x1b]\x06`N\xb0Gp=\xe5b\xea\xdf)\xed9\xc74\x8c\x16v\x86J\xba\xf7\x1b_J\xac\x81\x02\x08\xc5\xcf\x18%0 W\xe1$\xa2kn\x10\x1f\xc2{t\xc2\xabG\x0dpy\x10E\xac\x88\xbf\x14\xd5^\xa2\xfd\xe3\x059#\x8b\xf2]\xf3\"n%\x8e\xe1\x06Q\xfa\xd0Z\xee\x00\xf8\xd8\xd6\xba\xd0\x13\x8e\xc6\xec$\xd3w\x13 \xbf\x0b\xae\x8a\xd4\xf7\"\xaa^\x98)y\x0e\xea(F6\x03\x16\x16\xa9\xcf\x19\xdd\xca+`F\xd8\xc2\x0e\xea8}\x1fG\x83o%\x15P5\xa9\xb2v\xc0\xdcJ\x169@9\x84!\x1c\x96\xb9\xb3\xf4\xf3\xdfJ\xf4*\x95\x8a\xe3\xc4\xeeC\xc8\xb8\x8bi\x86~\x92\x02\x16\xd9\xb8\x10\xbf\x8c\x049B7\x91\xb0\x80\x1e\xa3\xf1~\x00a\x9d\x82ip\xf4\xc9\x8c\x92\xc6\xf1\xde\x8a\xa2^\x15G1\xc8\xf8\x1b0UX?Q\xa8oA\xd8\xc8\x8e\xb0\xfaN\x9cp0\xa9\xe2\xa0\xc9\xa2\x848\x98b\xb2L\x86]*\x185(\x88/Ez\xc8\xa0\xf1\xab#r\xca\xcdbE9\xd1d.z\x13\xca\x8a\x08\x95|\x81\xf0k\xcb\x8bi2&\xca\x0f \xaf\"K\xf3x;%\x01,I\xc0\x98\x06[\x1a\xf5\x13\xf3iU\xf2\xea\xf2\x10\xd7BX(\n\x8b\x93]\xbf\x0c\x80J\xbe\xd4\x165\xc3\x0f}3|*\x89D\x04\xe3\xb0\xeb\xd7&\x06\x95\xb8g6\xb70\x00\xa3\x8d\xb5\xa2\xc7 +\xe5\xac\x0c\x9e&\xf2\x92\xc4$\x17\xfeK\x07\x12\xc1\xf8\xf1\xbe/\xa3\xdc\xf1\xa7\x99G\x05\xe1\x97\x92\x8b\xca\x87\xbb\xe8\x19\xbb\x03\xb9\xfd\x93 F\x9a\xee@n\xe0\x1b\xf1\x95\xc7\xb0F\xdca/\xdb\xec\xa1\x02\x08\xad<\xbc\xbc\"t\x9ce\xd3\x9e\x14\xfb\xe1\xd8Rt\x04\x14\xb5\x04V{\xdc\x99\xc0>\xa3\x9a\xf6OD\xcb\xe8\xd9\x15\x8e\xa8>W\nh\xb7\x1d\x80\x0c\xab\xab\xbb\xe5G\xa89nYV\x11 \xea\xbc\x80\x13$/\xd5\x05L\xe0\xf1c\x88\xec\xdf\xcd0\x00f\x9b\x1d\xeb\xf2\x03\xcb2\xcd\x8a\x05\x9d]\xf3\x82\xe2\xb9\xf6\xd0\xe8`\xa1^l\xed\xb5\x19]tW\xa1\x8b2 }\xf5+\x12E\xf6\x98\xa8\xd3\xa6\x90\xaf_\xa1P\x85\xb6\xbel\xb6\xe3\xcb\x8b\x0dcR\xf3%lCpP\x08&G\xf2\x19\xec\xc3\xa4\x0d\xc9A\x8c<\xe7\xae\xe8\x19f\xde\x8f\xf8\xa1\x940\xd4\x88\xd9\xa9\x1d\xf9f\xb7\x04\xb0N\xc9\xb27\x90.6\x1e\xbb%\x948\xd7&\xfb1\x1d\"a#;\xd7\x99E\xa3\x10J59;\x9b\xd98UU9\xfeTT\xe5\x04oH=y\x8c\xbf\xca\xacGa\xa1$\x8f\xf0\x87\"5&\xfc\x86\xd0\x97\xe7\xfcW5\xb9W\xe8\x04\x8a\x0bb\xd3\xa8\x9d\xa2i\xd0C\xc5\"\xb7\xeb3\xf1\xcd\xd1\x14\xfe\xbe e\x13\x88s\xee\x8f/\x92\xf3\xd8c*(w\x9a\x7f$\x89\x9bT\xcc6>@^\x18\xf1R\xf1\xa5\x88l\x1b\x93\xb3\x9c-\x9c\xdb\xa4F\\G\xa1%c\xce\x8c\x9b\xf8&\x1c\x0e|cHXX5I3~B\xc9\xbcQ\x9ed\xc3\xd0\xc6[t\xccXi}\xd8\xa0iE\xb3\xea\xc8\x8b\xe3\x9f\x96n\x99jWA\x05v\x1c\xf2(\xec4xK8(nJ\x13Y\xae\x8e\xb3\x19\x83`\xc2\x9bC3OW\xa8\xd9\xd0\x1f\xa0\x88\xc1\xa3\x8ag*\x15\x1e\xa8k\xe2\xf1\xfc\\\x82-E\xae\x94\x8d\x8a\x89\x97\x8d\x02P\xfa\x91<1\x8f\xa4\xb0\xa0\xd7l\xbf\xaaeU\xcf\x0f\xf2/\x1fq\x81F\xb2\x82\xb0\x0dg&\xa4\xab\xfarJ&R\xf0\xad\xf8\xf5C\xee\xb7\x80\xae8XXuX\xf80\xf0P\xad\x14=\x19\xd8G;C8\xb3\"^[\x99wcE/k\x92\x1e%\xe8EF\x9d\xf1r\xc7\xea\x13\x19\x7f`(o\xac\x98\xf5\xd5t;\x98\x9f\xc1\xcc\xb6\xb7\xb0\xff\x89\x0b\xfb\x8f1\x1e\xb0m*\xce\x10\x1623bc\x8c\xdc\xf4>\x9a\x8dv\xf1\xefm\x0c\x19c-h<\x16\x18>\xe4\xf5\xfd\x95\xb4\x91\xa9\x9c\xe1\x9e\x12s\xc0\x0d\xbf:N\xa5\x1a/Q\x88\x1e\x13\x15\x99f2\xe8t\x1bfl\xd4\x0f}|.\xf6\xd1\x84\x8dkR\xdd\xf1\x070\x92\xc6\xa3\xc9X\xec*&\xd8\xcd`[f\x1f\xc8\xd8\x9fg\xba\x11q\x99\x90=\x9e\x05\xbc\x8c\xfa\x8c\x1d\x00\xfc\xdf\x04\xff\xb5Md\xc1\xa5\xb1\x04#\x08\xf0\xcf\xd0\x7f\x08+\x06\x11\xec9c\xbb\xc9i\n\x95\xa1\xf3\xf1\xea\xf1n\xde\xe6N2\xc5 \x8aG\x18#\xc1\xc9F\xc8%\xee}60\xbc\xad\xa8\xb70\xba\xd1pda\x905\xff\xe6\xe6M\x8c\x03F\xd1l^SA\xb4\xd0\x8a5F\xb0 !\x9f\xf0\xe9-a\x08\xd9CX\xc2c8c\xff0J\xd0&K\x1c\xc3\x10\x16HA\x96z%\x89\xbcXwkAr\x8e\xc7\xbc\xdf\xf2\xb71\x81\x94\x9e\xbf\x93\x1f\xf2\x9e\xcf\x90v\xc1\x10\xe6-\x94 $\x83/A\xe6\xb1E\xc1(\xf6iEq\x92\"\x1b\x13\xfax\xd6=\x1e\xc2\xca\x87\x9c\x81c\x85\x8b\x86\xfff\xdcmaR8(4\x9a\x12z@\xde\x96.|\xb2pGf\xc2q\xc4(\x15\xe2\x87u\xe5\xc4>\x9cX\x85\x19\xb60'\\\xe8~\xfc\x98\x1d\xe8\xb6\x85a\x038A\xea\xba*_\xf7\xe1$%\xe1g\xf3W'BP\xdb\x1e\x82\xc7\xb7\x94\x0f\xdf\xc1 n\x92\x9d\x022b?\x8dN\xf4\xc2\xad~q'\x1c\xab\x1f\x0b5\"o\xa7\x0e\xd2\x8c\xad\xcc\x0e\xcc\xd8\x12M\xf8~x\xc4\xf7C\xe5\x83b93F \xc4\xfb\x92\xba\xec\x08\xaa\xb2\xa3\x8d\xa2\xec\x9c\x924D\xb5Fy\x9cp\xb6\x9bV\xd8\xf9\xb0\xd4\xed\x00\xc6q\x96\xeeU\x13\xd5\xbdj\xea\xea^\xc5\xc8\xc49\xf1r.\xee`\xa4f=\xba\xd1p\x1c\xff\xe1\x96/2U\xf3EV\"\xe8\xcb,k\xa1=\"\x04\x93b[\x99\xe0 Z\x01M\xe9{&\x1c\xc2\x8f\xc5\x9eMp}E\xa5\xbf\xdc\xcbxJI\xbe\xea\xd7\x9dR2\xe5\xf1h\x93\x0e\xe8\x91\xc0c\xe94y\xf3&O\x10Uz%'HR$\xe4\xebYn\x0c+\xf5\xb9-\xc5\x1cw\xab\xdeE\xa5\x9c\xd4Y\x9f\xb1My\xe6\xd4\xfe\x91\xbd}k\xa1\xc7\xa7\x9ce~M\xca\xfa\x8e\xecVg\xbf\x9b\xb3\xff\xf5\xf5\x1d_\xdb\xa1X\x94\xc2\x9c\xd5\x11\xce\xd4\xe0\x07\xd7\x94|U\xd5\xc3\x91bT1+!\xca\x14\xe1(\x02\xe1\x8f}\xb4\xdb\xf7\x8fy\xea \x9e;|\xc1\xed\xcb\x0e\xb9\xc3\x9d\xe6\xf4\xd4\xaaLXre\xc2\x92\x8d\xeb\x03\xf1xu\x9b\x0b\xe25B\xfd\x0c\xad\xffl\x970\x84i'\x90,\xbd1\xf5R.\xf8\xe0(3x\xfdb=6LIA\x0c\n\xff\xac\xe4\xf8\xd9\xd1\x1a\x9aT C\x9e\xb7I\x8f\xb7\\?\xd1\xa6(\xcc\x05y\x1cr\xedi\xf9s\x0f\xbe\x83D:n\xa2\x8d\x88\x1b+\x9b\xc9O\x0d\"\xac\xbcD\xff\xca|\x84\x8a\x05\xa55\xc3>\xf2\xfb4yI\xd6d\xfa\x9e|\xf1\xfc\xee\x94\x99\x8ev\x0d\\\x83\xdf\x9f-\xa2\x95\xc7:x\x1d\xf2|:\nn2\xa2\x9bVp\xb5\x8a\xb9\xaa\x933:\\\xa0\xf1L\x96}c\xd4%\xc2\xc3\x9c+1\x14\xe7\xde\\Q[0\"\x12J\xd1T\xa3\xbcTb\xcd\x8c\xb6\x99\x12\x01rD\xa5\xd0\x1f\x0d\xc6m\x8b\x9dr\xd5\x1e_G1\n\x9ej\xdd8\x08>?\xe1L\x9fK\x12Z\xb6\x90\x8bB)\xa2\x19#\xc90\xf1=\xa9,\xb4\")\x07\xf7\x0d\x17\x94#\xd2s2\x0c\x8c\x1f\x90\x93s\xcc\xbc\xfc\xae\xc5\xeb\x04\xdd\x95\x14\xaf\x93\xe3<#/\xc9:SJYH\x8a\xd7L\xe2k\xea\xf4\x8d\x81\xa6k{\xec\xde\xfc\xab?\xb7\xf9g\x7fn\xf3_[\xe2\xd8\xfeAl)b\x89:\x02R\xed\x9e\xdd`[\xbc\xcd\xabSi\x8e6\xb1?\xc0b\x8e\xb2xIkCgE\x99d\xf1\x91\xac\x7f\x86\xdeg\xb6\xbe\xdd\x07\x0b\xean\x12\xddx\x06F$\xd0U\x14as\x9a\x87Y\xab\x1b*\xa8\x1dE\xf1d\x91OIV\xafj_\xb4(_\xe8\xd6\xec<4\xb78 's\xf2\x8ed\xf9\x02\xf9\xdf8\x00\xc5\xa3\xf0c\x8c\x8f+e\xbbl\x11L\x85ZO\xebL\x01U\n\xd5\xa8g\xe5\xc8\x18\n\xafC\xf4\xb5\xa7fu\x84\xb1\xd8\x95\xe2\x9d\xdau~\\\xdf\xcb\x0e\x82wmR\xbd\xd4n\xca\xaex\xbbf1]\xb2\xf0nN\xac\xf2\x92v\xcd\xd4Z\xbeV^\xc8\xa5\xd0\xd6:\xb6\xf2*\xf7\x19\xba\xb9\x8ev[\xb2!\x01\x86u\xcaw\x95\x0f\x07\xe3@\xf9\xbb\xe1^X\xbf\xecfQ#\x19\x91\x97)\x8b\xb9\x1b>\xb2\x95\xc2\x15\xfe\x99\xc9L\xb0\x0f?\x1b\x11\xa9r\xd3D{\x9f\xb7s\xba\xad\x148\xad\x13\xdd\xb4;i1\xd3\x80\xb4\x1e\xd2\xe9RT\x99\x97%O\xcd\x85~\x0b\x19{(r\xd0G\x18&\x8c\xbe\xf6\xbc\xc4N\xaa\x15\xedp@V\x02\xe44\xbc\xab\x12\xa0\xa8\xc5\xd9\xa6J\x83R\xaf\x9c\x91\xfcXX\x04MD)j\x99\xb2\x9e(9\xcdY\xc5\xe1w\xe6\x14\xce\xdd)\x8d\x14_\x93V*\x83\x8ev\x82\xc0H\xf9\xd5\xfc\xf6\x99\xf0I\x8b8m\xb0\xbb\xa8\xa0o\x82\x95\x06I\xf9\x9dA+\x0c\x14d\xcb\x91\x02\x85\x0c\xdf\xb4\x0b\x00\x06uB\xa3*\xa2a\x8f\x7fl\xf7\\\xb3o\xf0Xe\xb1\xe2\xfan\x8f\xbb0G6.\x8br\xf6\x07-s\xce\x9c\x90<\x05\xbe\xeag\x00*w\xd5a\x9c\xa0\xeeE.%\x9a\xb6\x8c\xae\x8c\x07\x83J\x8dl\xd9\xd2 \x16=\xa1&@\xe4}\xdc\x19\xc0\x8e&\x855\x08\xee\xa1Nc\x8d\\A\x95\xc6V\x1a7\xb4|56\xae\x85;\x8c5\xbc\\\xac\x8f\x0e\xf9\x8f\xf3p-\xc5H.\x03\xd82\xc1N\x1f[d\x9b\x91\xf6\x8c7\xf7\xe0\xb4\xe5\x7fpU\xf9\xb5\x9c\xec\xb8\x19\xa3:\xaa\x19\xf1\xf8\xacH\xd4\xebv\xfcFxL-Y/[[%A\x8c,\xa7o\xf4\xe7\xb2\x03\xc5x\x9a\xbc\x80\xb0\xb5kJ\x0b\xf9\\\x87ia\nl\xde\x94gJ\x9c\x80\xf9\x8c \xf5Uy\xa1\x1d\xe1\x13\x8b[/H\xa9A\xe5\x13\xf0\x832\x91\xe2\xf6v\x00\x91\x87~ \x1c\x02hn6\xe7\xf9dS\xad\xfb\x84\x81\\<;\x1f\xe1\x04\xa6\x1a\x1f\x91X*/\xb6\x03\xad\x03\x9b\xe1\xe8\xfc)q.o\xe5F@\x06eT9\x92\xc4\xfe\x854\x84%.\\ \x08\x9bX6\xda\xb5X\xcd\xe4\x85\xd9,\xb5\x89A\xd5\xab\x8a/34\x15*9\x81\x9ecED\x91[\x1d\x91gfd8\xc1(\xf8\xe8\xf9\x1d7\xdb\xc0\x17W\xe2G\x0d\x11\xa7l\x86\x9d\xdc\x88\x98\x101\x80[\xe8\x83\x83\x81\x88\xe8\x93#\xde\xff,*\x98E\xady\x93\x18\xda\x1c\xf1:ff{\xc2k\xa4\x90\x86\x80\x1cF\xc0 \x81\xcd\x06r\xf6W^\xf4\xc8`\xd2\xa7 W\xa1+\x07\xb1\xe7\x97\x90\xd2\x0fJ8y\xe7\xb0\xa3\xc3\xcc\x0c\x86C\xee\xe9\xe7\xb1\xcd\x96 G\xa4]\xd8\xd7V\x9a8\x13^\x8d\xf6cg\"Y\xcc2\xdc \xc4\xcaZ\xd2\x18\x1a\x96\x06\xc4\x00\xb6\xf0\x94\x8a\xa4Y,,\xd2\xf8x\x93\xfaY\xe1p\x0c\xcb\x0c7\"\xdc\xb4L\nDDQE\xc9\xa4m3:\x89\xe9f4~l~\x00\x93o\xd3SEV\x1e'*\xb2\xea\x95\x8eY\x06B\x87\xd6\x81J8Nu\xfd\x95S\xc3\xa2\x03\x92\xd4\xd7\x12E\x9cqW\x02\xe3\xf3I+1\xbe\x12\xcb&|o7\x1b\xd8\xc2r\x90\xf9\xf66<\x82\xa4\xdcl\x13F\x83\n\xad\x9c8\xc7b,\xf8\x80\xe7X\x84h3\xe1\xe65\x031\n`\xa2\xa3G\x93oT\xd6 \x9b\x1e\xeb\xdfi\x89\xecz:\x896J\xabM\x15\x9fy}\x1c\x96\xf7\x9a\xcfR\xb9V\x0f}\x88ZOK\x06\xaf\xed\xed\x0c\x1e+(\xdfv\x12;E\xbfC[\x04<\xbb.\xedj\x024P\xb5N\xa1\xe0\xaa1 \x96\xd4\xe2Q\x0c\xb0'\x01\xaf\xa3\x13\x88'Oe\x92\\\xf4\xc6P5\x95]\x14\x04U\xac5\x1d\x98\xbf\xbb\x1e\x98v\xb2}M<\xb0\x99\x8c%.{\x84x\x16\x97\xf73\x11da\xa3S\xed\x88n\xe1\xb4'\xad\xa4\x8a\xa7\xe4\xc6\xd3\xb2\xceuO\xfc\x92je\x0d\xb6;\xb3\xb3\xdd~\x00\x9a@\xcbk\xe2\xb9\xbf}Y\x92\xd4e]\xba0\xf7\xdf~\xdet X\xb8\xc9q\x914\x89\xda\xe55MZ(R$\xb3\x0e\x86\x82V\xf8U\xd6\x1f)CT\xa3\x0cQ\xc0\x8f\xb0\xa8\x8d.\xb4\xcb\x0d\x8b\xd2\xeaa\x7f\x99q\xa2\x0b\xac\xe47\xc3\xbfX\x07\x9c\xcb\xcb*x;\x13\xf1L\x16\xf6\x1e\xce\xe7\xd1\x82\x80\xd1)\x0fTu\x00\xda\xae\xd4\x99'\xd8G'\x9a\xe7&$\xfcz-\x86\x8fo\xb6\x04X\xf0\x17\xe9\x94\xa1\xce\x91\x18@1\x1b\xeae-\xb4\xe7LT\x0d1oeve:\xca\x16\xb5(\x10@\xe1\x9e\xb7\xd0\xf3j\x02\x8f\xb0`\xcdM\xc8=\xac\xda\x87e\xf2'\x18\xa8\x0d\xfb2M7R\x84X\x94\x03HPR\xf4\x0bIbk\x17\x8bs\x9a\xf1\xca\xac*g\x0b\xcb\xben\x96P\xfa3L\x19\xa9Y\\\x03\xb1\x8a\xa3\x96B\xe7\xd7F\xa5\x04[\x958))\xa8\x93\xc9\x04\xe4\xb9%R\xcdw2\xcfN\\\xe9\x0d\x88^RA\x01\n\xf7\xeb\xd1`\xcc$T\xd4\x10z\xa1\x8c\xa7@\xecb\xc7h\xeeM\xca#3.\x08G\x1a\xf0\xf3s\xd2N\x16\xd9\x15r\xe7\xdcD\x94F\x9b4\x96\xd7\xda\x82\xf0\x8eJ\x90\xac\xa3g\x97\x19i\xdb(`\xdb\xaa]#C\xdb\x81\xa2\xba\x99\x99~\xb1RT\xee\x91\x89\xd1\xaa:\xf9E\x12\xdc\xd0\x986:2SK\xbe'\xa5v\xa3\xe2 HZ\x8a8 \xb8\x8fR\x1cy\xc4K/\x1e\x00\xffP\xb8\x97\x11\xa3\xfb`\x91e\xdaxD$\xfd,I\xa9\x9b4+>!\x1e\x1d\xdd\x1e\x07\x10\x8fn\x8f\x11\xcb\xe9ho\x0c;\x10\x8f\xf64\x19\x82\xfd\xb2 y-+\x83q\x97\x96;i\x08{\xcd6\xeb\x15\xfal\x0d1\xd0\x8f\x06\xba\x81q\xce\xf5\x85\xa8\xf1\xc1\xdd\xbao\xf0_?z5\x85\xa0 \xa7^Zq\x8a\xfb\xbb(x\xe5b7\xfa6\xed\x82,u\xe0\xdcRG\xe0\xcaK\x02\x99\xad\x0f;\x99\xe0w\x0fC\xd8K\x9fK\x86\xef\x96\x03\xff\xea\xfa6\x07\xf6\xbf\x03g\x88\xab\xd9*\x80\xa1n\x02\x973\xb9\"\xa0\x04\x16\xd8\x00\xc2\x13\x90\xf4\xb3dI\xae\xd2\x01C/K\xf3\xa2\xbe\xd4_\xc8H\xc9\xfc\x989\xe6\xc7\x14\xce\xbe\xa2\x1c\xc5U\xa1\x88\x03\xb4\xcd\xf2\xfa\x05\xe2\x1f[s!p\x13\x0b\xaf\xc9A\xfb\x93$\xceh\x9aOP\xb3\xecF\xdf\x7f28zGE6\x1b\x1e\x81\x84%F\xe8(6j\x0d\x810\x01\xc9\xcd\x818mI\x9c\xcc9\x88\x82\x04Zs\x8aq\x0bv\x14g4\x8c'$\x99)\x15\xcf-N\x11\x089D\x8f\xea\xa7\x95d\x9f\xa9gR=\x17MX9tv\xc5\xa8\x96j\xd7\xb2\xe6e(\xe5g\xb2\xce\x8c~\x89\xf2\xdar\xe3\xca\xd4\x8b\xa6k\x87\xb7\xd8E\xb4\x11\xaeN\x9d\xc8K\xcceJfQL~N\x93\x15I\xe9Zp\xbe\xee\xad\xb0\xeb\x94PE\xb4\xec2\x06y\xa9$\x88\x87Mvj\xe2\xb2\xdd F\xbd\xb2\xcax[\x8fo\xdduJk\x89\x98\x03\xe8=\x0d\xe38\xa1\xacuHb\x08c\x88\x8a\xf4\xbc)\x99$\xe9\xb4\xdf+H&\x8f\xb6\xb3\xb0\x98\xba\xab=s\x9b\xbc\x0c\xd1\x08\xf5\xeb\xb2\x7f\x12\xc5S\xaf\x8c\xbak\xff\xec\x12&!\x9d\xcc\x01\xc1f\x1f\xd0\xa5']\xd3\xe5\x11\x91\x0b\xfd\x04r\xfdq\x88\x81\xbcK\x93\xe5aL\xd35\xd7\x95*\xca\x9fv\\\xe9V(\x81\x0b\x7f\xc3F\x95\x04\x87\xfc\xda\xa4B\x14*\xdd\x1a\xcd\x08%!\x11KT\xfd\xc8\xbc\xacp\x00\x1f\x88p\xe5\xecPmA\x1e-D\xdd\xd9<\xef\x85F\xa2AHF\x99BH\x87\xf0\x9aT\xe1;\x9a\xca\xea\x06\x15\xa8\x17u\x0e4\xfb6\x00\xe2\xbd#\x01\xbc\xf0\x03xw\x05\n\xdc\x14\xfc\x90\x02\xeb0\xa1\xd2|-n\xa0\xb5\\\x1ao\x9b\x17M\xb36\x8c\xfa\x91\xf7\xe4K'\x9a\x81\x8d\xcb/\x9bt\xe1]\x15nN\xa1BgJEf=\xbe\xb1&>Jr\xb8\xa5K6X\x19\xa3L6\x80F\x0d\xe7i\xaa\xcd\x88yJ+\x8798\xfc\xd2o\x04\x89\xd6\x80\xc01\xb7\x15;T\xb2\xa8\x07\x02\xa3\x02\xcf+\x87M\x070\xa4W\x01C\\\x03\xc32\\i\xf0\x15\x04\x18\x1a\x85_\xde}\xdb\x19\x11XB\x94\x9a(Y\x1e\x13\xd5\xc9+\xe6<\x07\xc7e\xea\x11S\xcc\xd2%#P2\xdf\xf2?y7>\xcf\xd2S\xf4`T\x9d\x17\xcdG\x81\xc8\xd7\x1c\xc3>/\x06\xa4\xeb\xcao%\n\xdd\x8e&<\x1eT\xb0\xf8\x16\x08\xca\xe3I\x7f\\\xc4U\xddS\xc3\xa0aD\xdd:\xd8\x8c\x8b\xea\xa8\x90\x97\x96\xa1\xd8\xea}Q\x88 hP\xe1JCT4\xf3U\xc0\x82\xf8\xe8\x17V\x98Wt\xcba[\x8a\xf2$!\xde\x1b\x12\xc0\x0d?\x807\xeaR\xe9\x02\x01\x1d\x89x\x11\x0d\xd8\xa4\xe4o\xbems\xb5R\x1a\xf3\xfah7\x9d3o\x86;\x0cA\xee\xca\x92ig\xea\x86\xf7\xdf\x84\xb0\xd7\x82\xa1\xc4\x15C\x89\xc4P\"14\xe5\xa6\x10\x81\x97N5\xc3\x88\xf7\x8a\x04\xf0\xa3\x1f\xc0\xabo\xe7 ,\xc8\xf7\xeaZ\x90\xef\xcf\xc40\xe2\x8e_\xda\xc9\\\x1b~\xfd\x87\x91\xa8\xc4\x9f\x8e\x88\xf4Lp\xba\xcfT\xe8\x10!\xcc\xb4\xf1\x10\xcdu\x14,D\xbd\x9fg\xff\x95\x88\x84.1\xa6\x87\xec\xfa\x89x\xc6\"z\x8a\x93En}\xab@W,\xd1\x8f\xc2\x00:vr\xb1\xb5\xbc\xb9\xcbo\x1a\xa4Xv5\xf5rZD\xd7\x02\xfb\xbf\x06\xd1\x1d\"C\xdd\xf6\x02\x14\xe1\x95\x15\xb7p\x8b\xf3\xa4\\/\xd2\xe6e\x89\xde\x95\xb6\x11\x02G\x0e]\x18\xa0zI\xde%o}S\x0c\x1e\xf7r\x04\x07<\x91\x0bG\x89\x14Q\xa2\xbc9\xe07\x07\xcd|\xf9\xeaepYt\xa0 \x95s\xb8\x9a\x86\xe0\x9d\xf9\xd1+\xf3\xa3g\xe6G\x98\xa3\xcaK\xe3\x00N(\x13-b\xe5\xcdoT\xb0\x86\xb1\xe0A\xb7\xa1g\xd4\xb0V:\xec||V4\xea\xec\xf3\xb7\xe7qi\xf2\xb1w\xe6\xa8L\xe0i\x9e\xe6Eut\x1b\x9aW7oep#\xaa\x89S\xae\xcc\x85\x89\xaf\x07\xe5\xdfRg\xa1\x89\xd9\xac\xcf\xc4I\xf9[J&Z\x95\x15\xef\xff\xe6Me\x00\x15}\xae~\xb2R\x99\xa0\xda\x06\xcc\xd3\xec\x1f\x93\xe5\x8a\xaeQL.~\x0c!\x8f\x85\xa8\xfd\x1bm\xa6<\xadM\xd5Qc\xdc\\\xb4\xd2J\xcd-\xd4\x7fS\xacZy\xfc9N\xcec\xf8L\xd6\xd0\xfb\x1bl\x03\x85m\xf8[\x0f\x92\x18\xd8/\x89\xc7\x06#y\x05z[%\xf8D1\xfd\xb2\x16\x87\x16)\x1c\xf4\x86\x15cBu\x892\xa9\xd7j\xc1\xadJY\x08e4%\xce\xc1~\xb9\x0e\xcd:\xcc\x955pT\xae\x1b7\x8ey\xa6\xc48\xfb({\x8f\x9a\xf8I\xdcT\x01\xcd\xe2\x00\x16\x0c\xc7z\x7f\xff\xfb\xf1\xf1\xd1\xeb\xd7\x1f?<\xf9\xe1\xd5\xe1\xf1\xfb\xc3\x0f\xc7\xc7\x7f\xff{\xaf\xe9\x08\xb2bog\x0eJ\xa3y;\"\x18\xaa5\x91z\xb5& \x05Y([j\x88\x91\xcd\xe5\x87\xa6\xf4\x8eg\xa0^\xae\xe8\x9a\x87O\x17`tSDL\xdb\xf7bU\xc9\xb5\xb2\x04a\x94\xd9\xeck\xe5\xebb9-\xca\xb3z\x97kJ\\\x93p\x9fY\xe9\xd2\x0c\xf3\x0ex36\xdei\xec\xe9L5\x86v\xd7\xdf\xa0\xd2:\xe7*\xad\xd3\xb8\xd4d\x9d\xff\xbfM\x93uj\x87_\xa1\xee\xd3\x14XT\x7f\xad\xe2\xd1\"\x96\x0et+E\xa9\xb5*\x95Z\xab\xaa\x82I\xfe\xac>\x10\xac\xc1*VuV+\x17\x85\xcf\xca\xa6\xf0Y\xb5)|V\xb1\xdc\x870\x84\xb3X\xdc`[\x11Q2\x00\xe2\xadcF\x9c\xfc\x00\xd6\xd7\xa7\x11Z\xff)\x1a\xa1\xf5uj\x84\x84\xff\xbdM1\xb4\x8eK?}N\xb9O5\x94{\x19\x07p\xcc\xf6\xc9\xda\x81\x16\x9ft%l\xc7\xff!\xc2vn\x85\xe6\x92\x13\xb6%\x1b\xefI\xec=u/\xbby\xf1\x0d\x84\xed3'l\xef\x15\xc2\xc6n\xf5\xf38\x9bG3\xfad\xb1p\x8d\xe6\x7f\xef\xac\xe8~bWt\x1f\xc7\xa5\x83\xed\xb1\xba\xd7\xcecqC\xec\xb5\x13\xdck\x17q\x00\xe7\xd4\x0f\xe0\xe2\xfa\xf6\xda\xc5u\xee\x8a\xf74\x9c|\x86\x11\xdb\x10\xe3\xe6\x86\xb8\xb8\x82+H\xd5\x18?'\xe1\xb4\x89\xcf\xa8\xb7\xa2JRn\xea?\xe4\x89\xd7\xe9\xce\xceC\x1f\xbf\xe7^U\xe6\xbd\x00\x07 \x92\xd0\xe8\xe2\xfe*#_\x11\xf2\xb9\x13\x80\xd8\xa8K\xc3!\xfb\xa5\xc9\xde\xd1\xe8%\xcf\xe6m\xbd(9\xbe\xe5\xfa\xbai\x1d\nM_\xe1L\x82\xbb\x7f\xbb\xd1N\xa00\xc0l\xe0\x01\x02\xb3\xfe\x16\xec\xc0\x80A\xfc1W\x1b\xee\xec\xf8\xf8\x99\x89/\xc0\xcc*E\x1b\xa3\xd8\x90\xfb\x90-X}-\xd8\xa5I\xb4\\\xc5GC0e\xc1i\xe3z(\xf1V\x8d\x8a\xa1\xfcn\xad\xfc\xb9p\xed\xff#\xd6\x8b'\x8d\xc5{\xc2H\x91\x83`\"\xd4\xc9\x98\x1f\xda\xa3\xbe\xcf9\"\xfb\xfa\x959HZ\xa4\x16d\xc0\xf5\xd0m\xd9T\x05o_\x84\x07u\xe0\xd0\x08\xcf\x92gB\x01(\xd1\xc0P\xf5\x18\x8a\xf5o\xa6\xce\x87\x06\x19\xc5;E`\xaci\xfdIm\xfd\xe3\xab\xae\x7f\xd3\xfd\xba\xb1\xfeIke*\x15e\xb3E4!\xde\xc0\xde\xa68\xa6\xba\xb4\xcb\xd0\xd0Q\x1d\xa5\xeb\xca\x05\x83\xeb\xdd\xe9N\xd1Z\xeb\xdd\xa7\x91\xac\xae2\x8b.V\xa6o\x8d\xcf\x16(U\xc3\xa0.x\xc5X\x11;\xd8\x18\x92\xb8\x1c\x99\x8c\xa8|\x16\x8e\x1e\xc5`]\\\xc1b,.\xa2V\xe95h\xb8_{\x95\xa6\xab\x16\xaa\xa2\xa3sZ\x1f}\x99\xa6\xc7\x18\xe3W\x9cLi\xe5d\xc22gQ\x95d\xb1\x83\xe6\xa1\x8fw#\xfb\xe9n_\xc4\xb4\xb6\x88\xd1\x95\xd6\xef\x8fXWa\xba\xb6\x86\xdd\xd4V\x85.\xa9\xa9\xb9R\x10\x14\x0e\xf0L*\xa8\xbd2\x99\x8ea\xc8\xea\xcc\x06\x06=\xd4\xc5\x95\xb5\xa0\"\xee@]\x92\xf2hQ<\xbflH\x11\xf3=\x97\xd6\x10!\xad$\x13Le0H\xac$\x13\xc4o\xd2\x16&\xd0i\xb2n:R\xa7\xd9&z\x1db9S\xed\xd9\x97\xba\x9d\xdc\x8e\x91 \xad^\xff\x92\x9fH\xdb\xe2\x07D\xbf%\xa0\x03\xee\xd9\x8f\xcb`\xb2\xfa\xeag\xc8[je\x1e\xda\xb2\xf3Y3\xf3\xb9D\x05\\\xa0\xd6\x15\x85\x9a!\xbc\xd7H\xef\x87q\x00Otz\xd7\x0fO\x9e\xbe4h^\xdf\xb2\xf7/\x1c\xa4\xfd?\nw\xbd\x96\xfc\xa15\x8f=kF\x99\x92\x19\x8eTN8\xaa;\xeaE%\xfdK\xf9\xaf*upK\x19\xf8\xd9z\xea\x1er=\xc0!\x03\xc8\x1f\xb1\xd7pO14z\xd4..\x16ho4K*\x87\xd3\x08ut\xec\x9f&J\x18!\xa9\xa6\xef\"%o\x1c\xfb\x01\x94.\x93Jh\xc4\xfb\xf5\xf2$Y`\x85\x04\xdb\xf3z[\xb4\x06\x11\xf5\xd7\xdbx\xf4\xa4P/\xbeu\xd1\x06\xbe\xb5i\x03\xdf\xb6i\x03Y\x17\xaam\xed\x8b\x9aE%\x80\xb8\x7fT\x12\xc8\xaf\x01[\xa6X\x97\xfeK\xa4\xc4vH\xf3\xf5\x8cz6V\x04\xc4\x82S\x91\x1b\x97g\xda.\x8f\xf6\xcdFk\xa3\x87\x1acP\xe6{0\x98\xde\xac\xa6m*\xb0GOc\x1a+\x88w\x9b4\x81&G\xf1\x94\\\x90\xe9{\xf2\xc5\x010\n\x89\x7f#\xa2\xce\xddz\xf9\xe9\xbd{\xeb\x08\x1cm*l\x17\xcd\"W\x87pa\x84p\xefn\x1d{!\xa7,\xd2\x94]\xd2I!\x17;\xf6\xde\xa9\xdb\xec:\xbb\xed\xbcI^u\"\xa6\x9d\x9a\xcf\xaa\xb3R >\xce,\xac?/WY\xaa!\xe4\x9c\\ \x052\xae\xee#\xbc\xb86\xd0\xbf\x8a\xb2\x0eK\xbe\"\xd7\xd5/7C\xb8\xf7\xdc\x1b!\xc7r\xb2 \xe3\x9eK\x0f\xa5\xa9\xc3\xb1\xfc\x85Y\xbb\x04\xdb&\xc6\xf2\xba\x9f\xbe\xf2\x12\xc3\xcc\xb91\x8f\x97\xd9e\x94?\xc5\xb0\xc7}\xce\x14\xc2\x01\xe4\x98\x92|\x1fB\xea!\x7f\xd8\x8f2\xc1'J#\xe0\x88\x8e\xb5\x94[\xbd.}wOo\xf5*\x10\xc0\xe2\xf5\xad^\xa6\x8a\x1dP1\x16D\x0d+\x8f\xfd\xabA\xed+\xfb\xb8\xcfD%\x84h\xb4\xebP\xe79)\xed\xad\xb8\x08\xa1\x97\xa0\xc7\xae\x0c\xc4\xcd<\xa5\xd0j\xb3\xde\x96\xbc\xcc\xd9W\xcfD\x95(Q\xfdBW\xd7X^\x92\x92ci\xe9!L\xeaT\x14\xc7\xc4$N\xf9T\xd2S?\x90\xf7f\x8b\x90R\x12{[\xbb\xc2\x12\x83\xdaEM\xd1\x13\xebV\x00\x01\x1c%\xcd\xa8\x13\xba\xc8-\xc4\xfd\xa0\xec\xc0\x87f\x1fJ\x85X\xd86XN\xe4e\x06\xf8%\xaf\x8d\xd6,g\x8b\x0f\xa5\xfaV\xe3\x0e\xed\xc6\x8eH\x8f^\x97\xb4\xc9*\xbbV\xf5 v\x897\x98\xda\x12#k\x0b!4n\x91\x98\xa6Qe\xac.CU\xf4{\xef\xdc\xba9#\xe9\xda\xf1Lq\xe4\x82cK*\xf2\x16.8\x0d\xc0V\xf2\x13\x8a@s\x8e\x03\xbc\xd6\x11~\xa1\x14Z\xe3Z\xa2\xad\x81\x01\xf8uG\x12\xd0\x03\x86\x13]G\xc8\xd4O\xae\x1f\xd4|\x82\x9a\xf0'0\xf5\x19Ok=\xbaT\x8db\xc0d\x9fbNT\xcf`\xde\x00UOz\x80 M\xf4\xe5\xc15\xc3\xe2Z\xa1n\xb0\xa8 KP_q\xeei\x89y\xbb\x89\xaf/S\xa3\x19\x08\xe3@\\6o\xbd\xef\xc2\x92\xc2\xe9!\x1c@\x0f\x19\x1f\xd8\x87^\xd03c2#\xc1=\x8d\x1eU^\xdf\x82\xe96\x1c\x8fE\xa9\xfe\xad\x01\xba\xacn\xa3\xd2\x14\xffE7\xa3-YBJ\x99\x14\xaei\xe1E\x83gN\xaf\xc9Y\x82\xd8\x01N|\xdbg\xb2\xfe\x06\xf2\xf3\xd4iE\x97\x159\xd4\x01\xad\x8a-VM\xd9\xe9\xd4\x19?K;n\xb0\x00\"\xeb\x02\xd7p\xad\xe1\xa0\xf2\x08\xf60?\"\xc3\x14\xd8\xe7\xf9\x90\x1a\xdbAU\x03`\xcdZ\x1b\x01\x84\x03\xf0\"A\xe5\xb09_\xb4K\x8b\xd2\xb7\xbcb`b-\xc8\x9c\xba\x83\xec]t:\xa7\x1d\xe1& \x93\xca\x08\x95\x86(;}\x12\\\x8f0\xbd\xa7F\xbb;\x98\x06\x8d\xbd\xb8\xe3n\x81Tj2\\\xa7\xae\xd0\xb8|E\x0c\xfer\xb5C\x82q#\xddz\xe4yYx\xac\xdc\xbb\x18K\x85\xe9\xb2`\xe8\xbaJ\x9djL\xd4gf\x0c\xc8\x01}?(u\x7f\x03\xad\xf9\xd9\xa9\x97\x93\x9c\xbe\n\xbb\xa8\x07\xf8\xbeF\x0f\x99\xdd\x00v\x06N\xbdD\xd9\xe1rE]l\x0c\xa2\x17\xf5dR\xe4\xf4\xba\xe4\xbe/\x96\xb1\xca\x8c:\xf0\xa2&#\xa4\xd3l&I\x1e\xd7w~\xcb|\x9ex\xb4T%\xf1m/\x04X\xfeq\x07\xbd\n\xf6\xfe\x83+{*\xfaw\xa5R\xa0P\xaa\xaf\xd4\xf3K\x83\x94-\x03\x9eD\x0d\x1d\xf1nc]\xf1{\x917\xc1+\xeb\x94\xf3J\xe2lW\xaa9\x8f\x9d\xa46E\xe6\xd2\xb3\xbb\xf2\xb2\x94R\xc1\xb3@5\xb7\x19*\xe4]\xaa\xe7\xad\xcb\xea\x91/y\xb8\xe8\"l\x9d\xd1\x82l8\xb5/\xb2f:l5\xd5\xe1T\xbf\xb6\x18\xa8\xd5?\xc6ty\x95\xe2L\x94\x96\xf7\xed\x9cb\xb5z\xeb\xcf\xb1_S\xb5Z\xcf$\x0e\xc6A\x0b\x1d3\xc3@\xa2\xa0\x1b\x05\x8e\xaa\x94\xb7\xd5\xfc\xa4P\xb0\x00\x12OG\"\xe5e\x18\x7fgQc\x1ev\x913\x90\x0e\x89\x84\xcbK\x1eC\xb0t\xec\xe5\xa8\x0b\x0d\x97\xfdp\xaf\xd1.=E\xd9\xfb\xfc\xc4\xb1\xc0g!\x03\x0eM>aE\xa5\x14nu\xe6<\xba\xa2\x13r[\xda\xe2<.\x12\xe3t\xc8\xa7\xa5\x9f\xe2\x8a\xf1B]&\xe9\xd9f)`\xa6\xcc\xd2/n\xba\x9fj\x9f\xc9\xfa\xed\xac\xc3\x90\x8aC\x8d1s\x9d y\x0dFB\x1eq\xee~\xc4W\xb42lW?mH\xa9.\xdd.\xba\xab\xd1\x1a%\xbf\xfa\xc8\xcf\xba\xf7\xf7\xf2*\xebb\xe0\xbdq\x8d\xb5\xb9\xac\x9a}/\xc3\x8b\x0e\xbd\xbe$\x9dT\x18\xcb\xf0\xa2\xeb\x99\xfa\xb2\x92\x8f\xc8\xa9\x137\xa3Yc\x06p\x00ob\xee\xc2\xf2\xd5MPZF\xf1\xd5\xa7\xc3\xbb#\xbc;\xd7\xb9\xa5\xa43&jC\x1eA\xdf|\xf69Zu\x80\x9d\xd2\xfe\xeb\x90\xce\xfb\xcb\xf0\xc23T$6tV\x17\xbe]\xa5\x04\xc3\x1ecMzT\xb9\xe3<\x90_\xe7\xd1\xa2\xa3\x99\xa1\x18\xcc\xefW4l|\x8eV\x1fc\x1a-\xbau\xcb\x81.\x87\xdcM\x05\xc5\x13\x82u\xeb\xafi\xe5\xd0d\x06\x03}\x7f4\xfcL:,/\xad\x18 \xae\x80R\xac\xbfkF)\xd6dw\x94b_}\x0bJ]E\x92\xf8\x87\x13w\xab\x940\xfa\x18\xa3\x9a\xb7\x92\xbc\x0d#+[\x18^\xc9NS\xa3vY^L\xa4\x8b\xaa\xb1yJ\x81\x96J\x18\x08vlo\xedL\xd4\xf3o)\xfb_0n\x1a\xc1\x87\xa2J$l\x9b\xa1\xd2L)\xfd\x14\xdf\xde\xbc \xdb\xdb9\n\xa9\xa2AC\xa1ry]\xfa\x01\xe4\xc67.\x03P\xcb \xfd\x17\xadJ\x92vY\x16Z\xf1\xc6b\xdf\xd9\xe5Zv\x85\x16\x8f\x12y\x89q:FY\xaa\x17\xfaN\x85\xc5L\xdb?\x00\xf7\x88G\xf5\xb2F?\xaa\x97!VB\xbd\xa4\xe9&o-N%/\xae\xc3\xaf\x14\xa9\xb2x\xa9\xcaKF4R\x11\xc3\xdb\xfa\x01\xbb2\xe1\xac\xea\xf6\xf6\x04\xdf\x1e\xb4\xb8\xb6\x82n\xafM\x02\xc8P\xe3y\xc0H\xdbp\x08\xef\x84\x98\xf3\x9cad\x86/\xf04\x7f\xa1\xf0\x0c\xf9/X\xdc6\"`\xa5\x00\xda\x87\xdd5\xaf\xec\xe0\xb9*SQ\x1cZ\xdd\x98\n\x19C\xd0\x91/\xed.\x86\xcd\xc3l\xfe4\x99vpt\xa1\xf32\xbb\x00\xd6e\x9a\xab\xd9\x06\xday\x04(\xb6\x17wP\x1e\x0ea\x00\xb7`\xb7\xd8h\x16\xd2%\xcd\xa4\xb3V\x05\x9f\x9b+\x7f*\x8a\xdf\x0e\xf4Uo\x8b\xd7\xf8\xc0\x9c\x16\xbf\xf6\x0d\x1b\xed{\x14\xd2o\xdf\xb9\xbd\xf7`p\xff\xf6\xdd\xdb~P\xdc\x86G\x8f`p\x176@\xe0\xf1\xe3\xc7\xb03\xb8\x1b\xc0\x9d{\x83\xfbw\xee>\xd8\xfd\xbe\xfe\xdem\xe5\xbd\xdb\x01\xdc-\x9fc:w\x8f\xc06\xdc\xbe\x7f\xef\xce\xde\x83\xbd\xc1\x83{\xb0a0\xfd\x17\xdb\xd2\xff\x12\x9f\x0d\xee\x05\xb0\xb7w\xe7\xde\xfd\xbd\xbd\xbbE\xf3\x87\xe2s\xec\xa6x\xf3v\x00\xb7\xf7\xee\xdd\xbbs\xff\xc1\x83\xdd\x07\xbe\xda\x84e\xcby*\x7f\x10c\xad\xcb\x83\x8eP\x83!\xdc\x1e\xc0w\x90\xc26<\x8f\xbd'\x147\xcd\x13\xea\x11\xdfg32w\x0e\x8e\xbbS^\\+~\x85^\xaa\x93r\xe9\xa6\x98\x11v\xd4\xdaA\xb7\xc6\x1d\xdb\xf5\xb5\xe5\xac\xa1 \x88:RX\xb9SW\x06\xb3\xbd\xf8\x9a''Sr\x01\xa8o\xbc\x8eG\x0b\x19\xe0\xfd:\x1e=c\x7f\xbf\x16&\x8b\x8c\xdd\x12\xa1\xa3\xfc\xb6\x08\xac.\xee\xab\x81C0\x84W1>\x89\xe2l\xc5s\xe3\xe3'\xef\x93<\xad\xe6\x95\xd1\x81\xac\xa6D\x12\xee\xad\xd5\xd9a\xeb\x93y\x18\xc5\xbcma\xcb\xe4\xb7\x93\x98\x86\x11F\xa5\xe3\x10\xb8\xee\x12c\xc4S\xdd)9[D\x1dB#\x0b\x01\xe5+1\xae\x84N\xed\xb3:l\xb8\xf7\xbbZ\xff\xcdT15\xcb\x02V\xe1\xae\x93a\xb5\x90&\xa4\x93\xc4( \x1a\x9b\x8bO\x03p\xa3\xaab\x93t\x14\x1a\x97\xe1\xeae\xd5\x07\xd9\x15FW\x00\x02[\xf7:,\xda\xc4\x8c\x06,x4\x82\x05\x08\xd8\xc9Uv\xeb\x87\x18\x93\x9b\xb4f\xeexj\x06\x92<\xd5\xaa}\x19\xda\xf9\xb9\xb5\x9d\x11 \x80\x8e\x9d\x1a{g \x87\xf5\xb3\xb9e\xb3mQ\x97d\\\xd0\x84\xa7aXo\xaegX;\xd7<\xacW\xf6a\xf52\xa4\x81\x15\xe3\x07\x1c\xc0O\xef\xdf\xbe\xe9\xf3G\xd1l\xcd\xd5\xb6\x82Z:\xe6\x16}f%\xc0\x87\xc6L\x9e\x86\xe6\xbe\xb6b\x10\x85G\x05\x07G\xe11\xfe\xbd\x83\xec\x9cS\x07\xcf\x1d:`\xac\xcf6\xec\xdd\xbb{\xe7\xce\xed\xbb\xdf\xdf{\x00\xdb\xe0Q\xc6\x90\xdd\xf3\xf9\x9f\x8f\x1f\xc3^\xf3\xf4\xad.\x94h\xedCT\xaf\xc2h`\x95\xcb\xe5\x95|\xb3\xad\xaeu@J\x1b\xdeV\x82\xa5\x00\xf8\xba\xf2\xd0R&\xa2G\xbe\xaf$-\xc5f\xc5}k\xcb\x97\xac\xf7\xc0\x96GC\x85\xa8\xdel\xe7\x0c\xd2\x80[\xee*1~\xd8\x7f\xeb\xe4\xdd\xed\xa1W\xb0\x9f\x15\x90\x8d\x18ds\xf8\x1f&;\xb0\xad\xc7p \xa9\xb8\x00c\xcc\xef>\x7f\x07\x0e\xe09\x9b{\xce\xd3\x91\xa2\xd5F\xfe\x8cd\xca\xd86\xf0[\xad%\x86T\xe5%\x95p\xde\xc6\x0b\x12\x9e\xb9p^\xd2,7b]\x8c5\x87\xb2oY,\xb6/op\x02 \xf5/\x01\xdc\xe8'3t\xa65~\xc6\xf3\x93(\xde\xf9\xd6s\x96\x14\x1b\xdf+\x88\x81\xb8\xc7\xe8\x80\xc8H\x13\x94\x94\xc8\xcd\xc7\xa9\xab\xcb\xdd\x92z\xbbj\xcaj\x97>\xae\xe0_\xc7\x0e|\xc7\x08\xd5\xebv\xefq<\xf9\xbf^I\xafzC\xfe\xf1,\x0el\xc8\xe6<\x86_#:w9\xa7\xa4\xcc\xa3\xf6b\xc77\xc6\xd3\xc9\x00\x81\xe6\xf8M&\xcb\xca\x9dK\x9fQ\x842=\xec\\\xea\x1b\xd4\x9bE\xdd\x96#t\\o\x0e\xbf3\x8f\x85\x18\xc4kA\x0b\xb3\xb2\x93\x9cv\xd5|:\x9a\xaa\xd3p=\x9b\x0d\x9b/s\xb89@;Q\xf2l\xf3\x12\xda\x15+\x81\xfaX\xb1$\xa8\xb7+&\x85\x17\x81\xaa\xa4\xf5\xf1\xde\x8d\xca\xf2\xf1{?V\x9a\xe6\xf7N\xa8\xe6\xe3s\xaa\xf9\xfa\x82\xd6?oBE\xe6\x97\xdb\x87\xb8 W\x04\xea\xcb\xe6\xfd\xa7\xc9bA\x10\xd2\xfbp\xac)\x90\x81\x01b_5\x0f\xd4\xb4\x92G\x1a\xe7 \x9e\x97o\xa5y\"R\x05^hGI\xf7!\xd3\xe5{\xbb\xbb\xd3O\x9f\xf2\xe9\xfd\xdd\xdd\x1d\xf6\xefl6\xfb\xf4)\xdf\xbd\xcd\x7f\xee\xde\xbe\xc7~\xce\xc8\x1e\xfe\x9c\x91\xbd\x19~3\xc5\x9f{\xbb3\xfet\x97\xf0\x7ffc\xd3\xe0\xcc\x14\xad\x100(\xc9\xa8J\xc7.\xbb\xc1i\xb0\xfb\xa0\xc6\xeb0.\xb2wx\xb1\"\x13J\xa6\x10\x16\xed\xf4\x14c\x8f\xbc\x07\x89\x96\xb0G3\xf0\x94\xf8\x88-\xc5D\xb0\xd9\xc8\xecA\x1cE\xb4\xaf\x11\x1f\xe8\x9e\x864<>\x16\xd9F\x9bX\xa9h\xf1\x84\x14[\x83\x0c\xbb&\x9a\x1aTQP\xb9]\x14\x82M\xaa\xf7yQ\xc4\xbcz\x933\xc4a\xf5f\x86ofUB4\xe9\xb6:\xb7\x1f\xe8\x97\xe7\xce\x83\x96\xe3\x18\xa8\xc8\xcb\xc1Co\x1b\x8e\xeb\xca\xe6\x15\xc6\x0eOT\xe6\x04R\x9c\x80\xf2\xd1V\xc4\xb8\xab\x9b7\xd9\x1f\xb1\x8fJay8\xc6\xec\xaf\x98\x1dA\x95\xfe(\xeb\xf2\xca'\xfe\xed\x07\xb7\xb5\xb3\x1e|_G>\x81\x94\x0f\xeei\x90r\xd0\xc4\xc7\xbd6\xd2!k\xb9pG\xe1\x99\x0e\x15\x17\x98\xb5\xf8&\xe4\xcd\x03\x17\x0b\xb2\xca\xb2\x8c\x8d\xa7s\xc4H\x9dY\x8a\x11\xa8\x15\x03\xe4\x1c\x81\xec-\xd8?sx\x0c+;]F\x9d!\x0f\xd0\xf5\x9b-bAK\xfeX\xa9-6\xc5%n\xb6u\x06C\xd8\x194G\xbd\xe62t\xe3\xfe\xa9\x00C\x08\x07|'\x82\xf4\x8e\xae\xb6\x8dy\x01fx\xfc#\xa9\x0f\x80\xff \xbc\x06\xe8\xf6\xf6\x19<\x82\x956\x11\x00\x1b\xd6\x92\x81ttf\xe0n\x8e\xb1(\xcc\x99\xc6Q\x9c\x01 \xf3\xb1\x89\x13\x18\xc2\x02\x0e \xf3\x8e\x03X\x06p\xc6\x03\x91py\xf7!\xf3\x96\x01\x1c\xe3]\xbe\xfa3\x0d?SK\xe2{b\x92\xae\xd9{'>0\x018\x8aM)\x0b\x10\xa2\x03\xfd\xb3\x93\x94\x84\x9f\x1bO\x9a\xe7\n\xeb\xe8\xd46\n\xb6e;\xd8\x0c\xf0\x93\xc4;\xc5\xd7n\xde\x04oY\xe6\x8c\x9e0\x08Q\xb9-f~\x89K\xa7<\x16\xdf\x18\xdel\xeb\xd1\x06\x050B\x02\xb4\xd0\xb8\x04\xb2\xc8\x08Nb\x89\x0bt\x8c\xfbh\"\x96\xb6\x18\xb8a8\xdf\xba \xda\x13y&N\x10t\xba-~0\xfc_\xff\x9f\xea\x876n\xc8H\xa5\xeas\xa9\xd4_\xdb\x11 /%\x11\xa7\x98&o\xbf\xa0Ml\xdb\xc5\xf0\x08\xd2\x87\xcd\x95C\xd3\xb8GG\xf1\x18\x01\xa7r\x86\xbbZ\xfeOI\xef\xd4\x91\xcc\xdf\x19\xd4y\x83\xe2pkRyQ\x91\xa98^\x9b\xf4\x1e%\x19\xa5\\S\x93\xfc\xa3*\x08\x9f\x1de\x87q\xbe\xe4\x8a\x9f&{\x92\xda\xad\x1db\xe2\x85\xb8VE\x06\xcf\xf7\x85 \xde\xae\xec\x13\xad0\xe6\x9bak.X\xcc\x00z\xec\x0fBz\xfc\xc4\x0d\x9b\xf7\xab\xfd\xe9\x8f\xb4\xcce),\x99\xf2\x15\x06Qch\x10\xeb4\x18h\x9e%m*\x97-\xd2\x8f\x93)aB3\xdek6\x81\xab\x89\xa2w\xb3\x1d\xca\x8d\xd4\xac\x1dZiG\xa3sbk\x9es\xe0\x16\x90A\xc1\xe4\x00\xd2\xfe\x0f\xf9lF\xcaS\xab\xf95\x03\xa3\xc7\x8e\xb7\xb0\x1fe\xb5\xb7Q\x8a\x8d\xccJ\"E\xe2\xa9(\x89\xee\x0f\xfc\xc2X\xdc}\xdf\x1b\x988\xda?''\xabp\xf2\xf9\xe7d\xb1\x9eE\x8b\x05\x0fY\xe9O\xc9*%\x93Z\xedG&O0\x96t\x15\xd29k}4\xc6L\xf1\xf3h1MI,\xbe,~\xb2\xe7e\xb9\xb4)\x99E1\x91\xfb\x0bqr\x91\x84S2\xed\xe9\x14\xab\xa4\xd8a\xfbz\x0e\xa2K\xd1\x19\xda_4\x1e7\x95\xd4\xe6qF\x7f\xc9\x18#\x8716Wk\x08\x83J\x02\x9b\xced\xd4 #\x0c\xea\\t\"\xee\xdf\xd1p\xcb\xb8\xdf\x92~\x94\xb1\xfd4\xe5Q\n\x95\x97\xf8f:\x80\xc8\xcbQ\xe5\xa4\xa7;a\xb7\xb1\xdf\xdd\xbd\xaaZ\x91\xf2\x83\x8d\xd1\x81\xb4]\xb9\xd8\xbe\xb74g\xaa<\xc9\xe5;Z\x87\x17\xa9!\x10\xfa\x05\x91E\x90\x8e\x85;_\xcd\xdf\x84p\x8f\x92H\x16'\xf4\xe2\x9a\xa9\xeb\xf2\xaaX0\xb8_\x97\x818\x16|\x7f\xbf\x15\xc2m\xec\xc4.\xf72\xf0\xb8\x1a\x88\x07\xf1\x17\x9cD\xa1X\xe1\xd2\xe0#H\x1e\xfa<\x85\xe8(\xf2\xc8(\xde\xde\x1e\xfbc\xbdv\x8f\x7f!\x082-h\xebU!\xa0\xd7\xd9\x0d\x1a\xd8.v\xc1^\xfd`\xe3\x8a\x8c;\xdf_\x05^bJii\x18\x8c\xc4{\x07\xc0\x90a\x1f\x12/\xaf\xb8 9M\xae\x97g\x042\x9aF\x13\xaa\xa8\xf6*^X\x0d?\x11\xe9j\x13{\xdf?\xa8\xebF\x94\xe9\x1c7E@&\xbas\x98\xdd\xfb\xbe\xf6\xe5q\xff\x1d \xa7\x8cN\xbe\xa7\xfc@YV_`\x80\xbe\xeb\xf7\x0f\xcfHL\x0f\x97\x11\xa5$mv\x10\xb6\x81Q^%\xd1\x8f2Jb\x92b\xd1M\x8er\x8d\x0ft\x96{\xb1%\xea(\x01\"\xb88\xf6\xee\xef\xfa\x82\x03h\xbe1CA\xfdc\x14\xd3\xfbH\x07\xd9\x9e\xad\x9c\x9f\xcd\x99-85\x1b\xd4\xc0\xb6\xe8G\xf1\x9c\xa4\x11\x15J\xaf\xbb\x1a\xf3\xc0\x8a\xa3\xdd\xdd:\xb1\x06\xa12\xd0 \xd5\xec\xfe\x8am\x9fU\x7fJN\xf2\xd3Er\n\x07\xca\x0f\xaf\x97\xd1\x94\x84\xcb\x9e\x0f\xfbmC\x9f\x06(\xfb\xb3!\xd4w\n\x08\xe1\x88\x81\xb2\x8eK\xe5\xd4\x98X]7\xf9\xb3\x86O\x19\xf7\xd0#i\x9a\xa4=\xc6\xbd.\x92\x8c\xb0?\xa6$\xa3i\xb2f\x7f\xae\xc2\x9c\xdfKI\x96/Iol\x8a\xd6Y\x1a\xd1%\x01\xa1i\x8e\xbd\xbd\x81\xa8a\x81b\xab\xae\xbe\xa0$\x16\x04\xa28\xa3a\x94w\x86\xe5S\xdf\x0f \x13j\x85F\xb6?\x13 OJ\xe5\xb8)\xdaS\xe1!h\x0d\"M\xb0 \xdd\x147i{ym\x8f9q \xa8\xaa\xe2{X\xae\x93^\x89\xc7_\x14xfSJ\x9e\x15\xc5\xdd\xc4\xcb\xacu[*\x15\xce\xc3J\xaa\xc4\xa0N\x04\xdd\xe2\xaa\xd1\xd8\x0f\n\x9d?l\xb3\x86\xab\xd4\x17\xf6\x8b\xaf\x0dJT\xed]RR\xae\xdd\x00\x0e\xb5\x86I\x06\xba\x1c\xeb,zH\xb3\x11\xdf\x9d\xe0\x8aP\xd0\xcf9\xe5Uy&\x85F\xc4KQ\x15\x92\xaa\xdbf\x86\x94\xa6\x19}I\x94\xb8\x83a!\x0c\xd5NK\xcc\x12\\u\xaa\xe8\x1d\xc5g\xe1\"\x9aB\x9c\xc4;\xbc\xd9[\xe2p\x98\xcc\xf3\xf8s\xcf\xb7\xc5\xd3\x18&\"\xb6\xb5\x06n9: \x06\\*A\x02\xee\x15\\L\xc2\xe0\x99\xd7\x86,\x1c\x89\xc4*?\xc6\xc8\x1f\xcf4\xff\xfa\xc7e\xa5\xf9\x9f\xa5j\xf3\xed\xcc#<]\xb1bND\xd8\x10\xa7\xe4#bn\x13\x0c%\xd7\xe3\x06N0e\xa7\xb4z\xe45\xe7\xcb\x16B,\x02\xe7(\xfby\x9c\xcd\xa3\x19\xf5|\x08g\x94\xa4@\xe2)\x10\xc6\xf5\xf7\x10\xd7\xce\x11\xedd:;\x04\x16GU\x97\xb6q\xcb\xc8\x86\x0f\xdf>\xe7M6\x88C^\x1c\x19L\xfa\x8f\x19\xb4 &>\x92\x9b\xf6<\x8d\x84\xae\xbd\x0em!\x85\xcb\xb5:\xa8\x8cw\xc0z{[\xee\x9b\xea3\x9fW\x8fb\xcbP\x1d\x90\x0e\xfb\xea\xaa\x83\xb6\xb5\xda\xa2\x02LH\xb8\xab\xdc\x04n\x92\xa2HV\x8d9,\x99.j\xa4#\x97^\xeeF\xe3\xcf\x15\x1a\xaf\x1b0)\xb8\xa8\x9b7\xe5\x1eVh\xdf\x16\xe1l\xd1\x01\x9b\x02_\xebiHC\xb6\xd4\xa8\xf7b@\xf3v\xf9\x9a:\x12E\x8e\xa4\x05M\x95\xc8\x17\xb36t\x94\xb6\x02\xb8\xff?{\xff\xbe\xdc6\x924\n\xe2\xff\x7fO\x91\xc2o\xc6\x03|\x84h\x92\xba\xd8\xa6M\xeb\x93e\xb9\xc7\xd3\xed\xcbH\xb6\xbb{\xd8\xfa\xa9!\xb2H\xa2\x05\x02l\\(\xab\xc7:\xd1gw\xcf^#\xf6\x01\xf6\x9f=o\xb0O\xb0\xb1\x11\xe7MN\xef\x03\xec+lTV\x15P(T\x01\xa0,\xf7\xec9\xdf\x87\x88nS\xa8B]\xb2\xb2\xb22\xb3\xf2r\xef\x1e\x92F\xc7e\x8bJL\x9a\x16\xfa\xe85\x87\xe7\xd2}C.\xb8\x18\xd4\x9d\x1b\xa9\nU\x17$\x85\x7f\xb8wO\xf7\xba\xe0\xfc\xaaK\xac\x91\x81\xdb\x05\x0c6to\xd7\xf6OO\xf86F\xc3\xe7%\x83\n\xc1\x88\\\x8b\xdf\xe5\n\xe7Y(\xd7\xc9\xffRj\x15u\x1a\x0f3&\x0d vdA@\x11D\xe3\x06.7N\xeb\xb6ix]\x8es\xdf\xc8\xec\x08\xf5P\x19\xd1C\x91\xebN\x1b\xa9\x80.\x02\xd25f\xf1\xa6r\xf3,Hv\\f\xb8\xa9\xc0#\xc8>\xbbl'\x98\x99\xd1qyg\x8eK\x19\xb9\x92SB\xc5\x9fC\x81 \xdfs\x8d'\x0f\x9f\xa3\xd4<\x93 (\x87\xa2z\xc4+]\xf8\xc9[/K\xca.P5]l\xf5\x8b\x94_\n\x86r\xfaT\xd7YBd)\xa9\xd5\x9c\xda\xc91\x95\xcd\xa2\x885\x86z\xb2p\xc3j\x94G_U\xac|\x84\x11<\xdcy\xf8p\xbf\xf7\xd0\xa4/95\xa2n\xae>\x7f2b\xfe\x8dU:N\xf2#\xbb\x87d\xb6B\x9dS\xa6\xf0=(\x1f\x08\xd2\xa9\x9a\x93\xe6\x05\xf1\xa6]z\x08\x88\xb2aQm\x88a%\x80(\x07\x1ac\xa2U\x8dA3!\xcb'\xf6t\x04\x1fQ K\xff\xa5\x9dloSY\xeb\x13\x1d2F\xf7*\xfd5(\xfd\xb5[\xfa\xeba\xf9\xbb}\x17\xd2NG\x9bk\xe0\x86\x9d3\x08U \x0e\xe8!\x92CS\x9e9\xa9h\x0cz\x98\x9f\xb9\xd59}\xac\x87Bn(\xd7H\x8f\xaa\xbd\xf7\xe9\xe9\xa9*+(\xd6/l\x8b\xbe\x16\xef,\xb7XtG\xf7\x0d\x9bI\xce \xb0|\x1f\xef\xfc\xc9\xa5}\xc8#/\x1eV\xdceM\xf3<\xd4\xcf\x93\x0f \xc4$-\xe4.\x18\xc3!\xbf{\xd56\xa0\xcb\x1b\xe3n!%}\x08\xb2\xe0\xaa\x86\x04\x9d\x8e\xf2I\xfe\xa4u`2u\xfc\x93\xb1\xe3\xd2\x05Ln5FY,\xc1z2\x86K\xda\x7f[\xa4\xe0!I\xc10\xea\xf6\xd7\xc2\xb6\x96\xde\xf5\x05\xa1\xab\x86\xf3@\xf5B\xcf\x92\xd94\x17m\xfb\x8a\xce\x9d\xc7Ny0\x0d\xc0\x1a\xa9\x89\xbfL@\xb84\xaer\xae/\xa1\xe0M\xfd\xc9\xa5n\x9c\xad\xfax\xd9\xbc\xc2\x02\xdb\x99\xe6M\xd7\x13\xe2\xbb^1G\xaa\xca\xb4\x1c!Q\xb3\xcd\xd1\xd1\x05u\xc9\xa4\xe5\xdclJ\xaf>\x97\x08 \x8a-l\x8b\x8e\xa7\xb4\xad\x1f\x97\x07\x99\xa7R\xe6\xe3s\x1e+\x02\x8fi\x84\xef\x9a\x0e!\xe5\xe89`]!u\xac0J\xf9\x91\"\xc4\xcf!l\xa5\xec6\xf5i\xa9\x0d\xbb\xa4\xc0\x91\x0f\xa3\x9f\"?\xb4-\xbc\x13\xe9\xf3\x9eyI\xcd\xc1%\x0b\x1a\xdc\x9f\x92\x14>\xb1EQ@\xbc\xd8F\xd9&\xd4X\x94\xd6\xa9Z\x0c\x1a\x8a\x94\xed]\xf5\x00=\x00Lu$\x97H\x91B\\\xb9@[-u\xf2,\xc8\x1c\x06\x9a.\x88\x04\xe5p\x93\xf0\x96\x05\xc5\xa2\xad\xea/\"\xc4\x13Wmt\xd5\x07\xef1qlf\x15\\\n\xdb#\xf0\x8dDI<\x88\xed\x8f\x81\xc5r\xa4\xf4\xa46\xf7\x14\x08uf>\x80\xfa\x81\x82\xb8\x91\x81\xa7\x10\x15p\x8c\x8a\x13\xbf!\xb2\xb2?\x03;c\xd6I\xc5\xe7>\x95\x8e#\x18\xf2\x1f\xe5\x85f\x9b\xc7\xc6\xe9g\xb5\xa6\x96\xe2\xa9\xb4ow:\xb1\xcb\xc1\x81\xab\xbe`Zf\xfefX\xbc!\xdd\xd4\xf3\x03\xae\xe7\xe7\x02\xbc\xa8\xecr\x08A1\xc4\xcc\xa4\x91\x93\x1f\xb3\x85\xa7xn:\x1d}xc0jFA\xb2m\x17\x13\xddFw\xa0\xaam\x0e\x085)q6\x89\xab*p|\xd2\xf5\x82 \x9a\xbc\x0f\x13oF\xdaE\xe1m\xb1+(\xca\xd7\x98\xc5\xc6l\xa7N\xa2\xd55\xaa\xde\x04\xe7c\x97\x83\xe4\x8b\xe0\xbc\x1eSaS\x9c\xf7k\xc2]\xb8M\xc1\x974\xb9\xee\xf0+~\xde\xb9\xc5 K\x19E\xc3ev\xb9{\x13\x9bp\xf4\xb9\x8c\x0c\xbb\xde\xe1\x13\x7f\n=\xd95\x93)\x98\xffd\x910\x17Ql\xc7\x024\xa5\x9dB\x14\xe2\x9d\x02Y\xae\xd2k`J\xe8?i\xe6Bd%9\x13\x02\xe4\xfb\x17\x89\xfd\x7f\xabMrb\x8c\x1dj\xd6\\)=rU\xa1\x98$\xb3\xd2,_V\xf7\\\xce\xcbVD:\x9b\xce\xdej9\xa6\x93v\"I\x8fk\xbfr\xc9\x84\xd9\x93C\xd8\xe9\xe8/\xb20\x1a\xfa8\xe4vq\xc5\xbd\xaaQY\xb6\xadJ\x0f\xf2_\xb2B'f{\xb2^C\xc0\xa5 \x8b\x9d\x9d)\x8c`\xe5\xc5 y\x19\xa2[J_\x17\"e]\xf2;+\xe1\xa0\x9e\x12b\xa43=z\xf2\xf5\xe3\xca\x0d\x9dQ@N\xdd\x98\xffyE\x93-a\xf8\xa8\"\xd3}\xfa$\xd4\x0c\xc5\x8d5\x9f\xf1\x10*\xe2;k\xc7\xcd?qku@G\xec\x92\x18\x86pl\xf3\xcblJ\x10M\xf3\xe4\x04z$TP\x8e\xd4\x9ac`\xfc\xef\xdd\x13\xbd\x98\xdaF>\x99\xa5\x13-\x83\xc6\x88>\x0b\xdb\xa2\xf5\n%\x01\xe6\x15\x11#$\xd2N\"\xd2IS\x95\x97q\xfc\x0b\xdb\xe2u\x02\x92$\x90.\xbc\x10\xaeh\x8d\xa5\x17_Zl\\\xa8\\\x15`\xc3f\x85hw \xd6\x82\xfe\x11\xe1\x95\x19\xde!\xf8l\xe1\x91\xbf\xe3R\xf94\xc2\x01[\x8e+}_R\xa9pMQ\x05\x80:\x8dRI\xe3\xa8*\xd5\x1c\xb9\xc9\xbe\xab\x08\xc2l\x05C\\A\xbe*lic~\xc4\xf7\xe0 \x17\xf0\x86\xfc\x88<0\xe8\xb5\xd0\x0e\xc7\x91u\x7f\xdb\xa8\xec\xd4\xce\"\x07\xa0aFa\xb1\x95$\x85\x07\xc7\x1f1T\xd4\x8d\xe7\xd7(\xa5\xbb\xa8\xb8\x92w\\Q\x10\x9f\xb7\"(R\xc3\x9a\x0bM\x06q\x07\xfc\x04\xc2(\x05\x7f\xb9\n\xc8\x92\x84)\xa9\xd2a\xe5\x06\xc2_\x91\xd67\x10\xb5\x01\xd5\xa2\xb6\x97\x13\xc9\x95\x8f\xae\xc6\x91d8eb\xad&^B\xa07\xd4\x96\x01:\xe0\x0b{\xac\x1af\x0f\x99 }1\xb6\xdfo\xd3\xfe\x98\xfft!\xad\xc9\x13S\xd3\x15\xbfOi\xec\x8b] 5^wI_0\xd3\xb3\x0e\x95n\xe9\xce\xc7%\xc5 \xa0\xa3?N!Z\xa5\xc9\xe8\x8f?Yn\xa9\xb6\x9e\x1f\xa3\x8b\x8c^([\xcc\x90\xb0\xcf\x15r$\x9c\"YJ\xf9\x1dP\x92N\xa3,U\xde\x908\xa6\x92;\x0c\xe1\\\xb9%\x80\xb2\xc3\xb5\xce\x88X<\x0b\xdb\x8a\xc2,\xa4\x03\xb5\xd8m\x92\x08\x88\xca.\xdf\x99\x1e%\xee.\xbc\xe4=\xd6b7\xd8\xa5\x17\x8c\x06,lk\x12\x10/\xccVB\xa7\xb6\x8c\xd6\xdc\xf6\x8d\xc4vn\x1e:\xd7\x96\xce\xfc\xd0O\x16\x96\x0bKm\xf14\xf6\xfc\xd0r!\xd0\x96\x8a\xfdy\xad-\xe5\xb3saB\x89G\xf5\xe3\x90\x92\xeaYM\xd9\xb9\xb6\x8cS\x9b\xb5\xe3\xa2\x85/\xde\x82E\xb2\x96\x10\xaf\xf5\xcf\xafb?-]\xbcn\xa9/\x91\x08\xe6\x9f\x04\xfa\xa8\xf8\xe6\xf5\x9d\x19\xaf\xa2qm\x913d\x86{\xd3\xc68P\x808^2\x18\x91x_\xe4\x11\xc2n\x14N\x88\x00\x0dZ\xbeu\xa3\xb0\x04e=\x9e\x07\x8d\x14\x174v\x15Mrz;\x01B<|\xb3\xbe \x9fs|\x92\xd5\xba\x8e\xa2\xe5\xc5\xf3\xa7\xf8{{\xbb8\xcf\xca\xb58\xfc\x8c+\x8cQ1m\x886~(h\xc1\x7fc\xeb\x84-\x06\xe3b\x17\xe8A\x8cx\xa8\xd1-\xac\xb9+9-3#\xd2\xda\x9c\xab\x171\x89M\xd0\x05\xa1\x12\xe7\xd4*\xcd\xadq(\xfa\xb2\x83\xdd\xees\xa9\\\"\x97\xe8}\xc4\x89\xbb\xf0<.Ux\n}Z\x89\x87_=\xb1\x0b\xfa\xcf\xe3t\xae\x04\x135\xf3\x82\x84\x00v\x0b1IVQ\x98\x10\x17\x84\xady\xa8^\xc0\x96\x96\xb8\xa6\xb4\xd3\xe1\x93C.\xa4\x8b\xedm\xba\x1b\xaf\x1b\x80(H\x15q\\8\xb7\x1b\xa9\x19C8\x86`\xec=;\x17\x14\xc6D\x17L\xb1f\x90s\xe3\xb6j \xcc\xe7Z\nb\xeehYO\x9bx\xdb\x8d\xc7\xc5\xa6\xdd\x9e\xd7u[\x1cva\x97\xfdnw\xf6\x0by\x96\xed\xc4\x9c\xf8k\xbbi{;\x00P T%\x1b\xfb\xaeb\xb2\"\xe1T\x00\xa5\x08P\xae\x96\xb0h\xcd5*\xf4\xee9\x9a\xf0%\x0cy\xf8\x1fcr\x06\x07\x90\xd9\xf2\x0b\xf4n\x92\xfe.[d\x95>\x1d\xc18tK\xaf\xce\xb0\x8a\x08\x1e\xad'x\x12*\x8b\x03\x9b\x1d(e\xfe\x80\xbdS\xb8\x02\x86\xf4\xfc\x9c 1f\xa1 \xb4\xfcn\x0fY\xb1\xe2F.\xe4\xb7y\xb6S\xb9\xd4\xaf\x18\xc1T\x18\xf3Z\x9d\xd5&*\x03\xf3\xda\x17L\xd4P\xbdL\x15\x8f\xc6\xc9\xa5\x90\xc3I\x89\xa3\x17\xd8\xa1\x0d_O?\xea\xd7|T0\x97\xbc\x9c\x07\xccfV\x1cBb\xe4exT\x96\x1d3H\xc5+\xa3t\n\xf6\xb95\xbcX\xc4\x9c]Hy\xc4YnH\xaf\x1f\xf8Vmp\xd2\xb8\x18\x98Y\x83\xedCy\xe6\xfa\xcd\xb2\xe9\xac\xf4\xad\xe4\x8a4\x16\xe7\x1a\"x\x02\xfec\x88:\x1d\x07\xe2qtf\x82A\xad\xc2\xb6b8\x04Z2\xb5\xe61\xdcNlR\x9c\x9f5:8D\x89LZl\xfeY\x97eg\xb03\x17\x9d\x97K\x80\xd8F\xc9\xa7\x8aM\x9c\xf9\x11 \xe4\xbf\xc6\xbd3i\xf7\x9a\x16\xbensF\x95\x1b\xd7:\x899)}Y\xb8Ap\xc3\x0d=\x861\x8a\xce8\x13'gm\xcc\x06h\xb9\xeaA\x10\x18\x8dRY\x84,)lVD\xfb\xf5\xb8\xdcJ\xa8\x07\xbc+*+\x91c\x8d\xcb\x11\xdd\xb9\xba\xf7\xecB\xa4\xa2\xc9\x89\x0d\x0eM\xb1\xa4\xec\x8a%}\xceq\xae<\x94\x04\x85K\xbe\xa6\x9b\x1c\xabu\xeb\xefM\xf3\x93\x0eF\nf\xb8\x8a\xaa\x18m;Z\xc4cL\xdb\x02:?s\x95\xa3\xa68eR\x85\xddo\xc4T\xe0f)eC\x13a|T1?)\xdf@\xbc4GP.\xa2\x9c\xeb\xec\x0c\x15=\x14\xe5n\x9b\x00U\xa8Z\xe9.b\x1c6\xf0\xc92\x1dG\xcd\x16q\xdc\x96\xfb\x08\x0fnd\xde\x0d\x16\x94\xca9R(\xe6\xf8W-\xa6{\x15{\xab\x8dN\xf7\x9a\x1b\x80\xb6g\x7fl8\"\xf2\xe3\xc1\x07?\xe4\xa2\x1d\xd7B4\x89\xbd\x94\x9c,l\x8b\xcefE\xa6\xc0\x85\xfb\xb0\xec/!t\xf1\xf5\x92s\xca,\x1f\xda\xb9A\xf1\xb3[\xbe>0i\xcd\xc0x\x8dI$S\xed*\xf2\xe6\x9a\x04\xce[\xe7\xb00&\x1e\x94!!\x84\xd3\x12(l\xbf4G&\xa7\xfa\x14]\xb6B\xc5o$W*\xa3\xa6^\xb2\xde\xf7\x99Ho\xab\x1f`=a\x95\"\xc4~\x9c\x9f\xef0\xa2+t\xe3\xb9 \xa9\xdb\xb2\x0e\xdaLJ>S\x14\xbb\xc6\xfe\x19\x94\xe3\xd2JR\x01/\xb4EE \xa9\x9b\xdc\xed\x1b\xd1K\xaa\x9bR\xe6\x9f\x87\x81\xadM\xe5\x07\x065\x86\xaf\xbb.\xd7qF\xf3\xfc\x8a\x11\x19$D\x82\xf98:\x93vz\xf7\xc2\x0f\xa7\x9c\xba\xd1\xa2\x1a\x8f\x9cT\xf6\xa6l\x86\x8c\x84B\xe7\xfc\xfe\x908\xc2\xfb;\x16\x14\xa7\x10#\xaa\x13\xd5\xd3\x9e6\xee&\x82\x84\x94|\xbb\x9b\xa3\xd8hL\xaa6rM\xd1Q\xd8\xd2\xc5Qu\x8e\xe5\xd9\xa1\xdf\xc7\xf9,\x8e\x96\xf4T\x86\x11\xbc\xfb\xa7\xa2\xac\x1c1\xdb\xc50\xd8\xed\x02g\x97bpW\xa3M\xb4iB\x1fNc]\x84\xbaz\xa4\x8dI\xeakO\xea\x1a%\xcb\x8dv\xd0\xe5\xcf\xb9\x1bK\x0b\xbb\xa3[_\xf5@\x93\x1bQMd\x01\xfc\xac\xa2\x9c\xd6\xbc.Z3\xee9t\xb2\xce\x98\x9b\xde\x01\xfa\xe0\x14\xc6\x9b\xed\xfbA8\x97\xb8\xd9\x9c\xe7\xf1\x85\xb8 |,\xd0Z\xc7\x00\x91F\xcf&\xe9\xde\xb420\xbb\x16\x02\xe5\x8f\xf9k;\x8f(\xee\xb6Ppo\xf1$\\\x07\x94-\x97'\x18\xb2\xd9\x85\xbaA\xa9/\xcb\xb0\xc2A\xe1\xed+\x9e\xccZu\x96A\xcc*\xfd\x99;d5\xd0\x92[\xc3\xbd\xafg\xef\xe2j\xf4\x85\x8a\x0b\xcd\xb4\xb6\x05%\xaa\xc3\xe7,o_\xfb\xadf\x04\x95ru\n\xe5\nL\x95U\xdf\x86\xb2\xa8\xaaO\x95B~>?\xf6\x9f\xec\xa4\xc8\xb0\x12#H\x84\xec\xd4\x9a\xca\xe1\xf0\x13\x12\xcch\x15\xfc\xf7\xd3'\xb8\xf2\xc3itU\xa5/\xbe>\xb272\x12&_&}\x00\x7f\xc81\xcd\x9f\x16\xaeS\xdds4\xc4~\x816\xc8\x06\xf0\x00\xf2\x9a I\xdf\xf9K\x12eiK)'$W\x10\xd9>;\xc0\x8a\xaf1\x1cB\xc1\xff\xb8\x80\x03\xe0\x85\x15\xb5\x05\xf6\xfb2LI\xbc\xf6\x82[v,>\xd7\xf7,J5]\xcb#C\xfdK\xe9\x83F\xf1\x873\xf9\xa8\x88\xad&\x96\x8fJ\xda\xd2\x98\xcc\x94\xec/\xec\x8d<_\xe5#l\xb7 $\xa55f\x10\x89\xdd\x1c\x0f4s&a\x1c\x05A\x1b\xfd\x90\x0c\x1d;\xa5\xcd\x05\x84\xff\xf9r\x8a\xd2\x87\xfc\xaa\x8a_\xb4\xb7,\xd4\xf4w'\x9d\xa9\xd6p\xb4\xb7s\x84\xf3\xe1$\xf5\xd7\xe8'\xda\xf5\xc4\xcf\xcf\xe9\\\x7f?\xc8/R\xa5\xaa\x1a\x8dV\x91bQm\x15FPl\x99\xe6\\ri\xf7<\n\xc5\xe4\xd9\x9dD\xfe\xb7\xee\xb2G\xe3q\xe5bD\xab}G\xec\xb9\xe5\x92L}\x16\x9b\xa5\x99\x84\x95\xbfP\xb2e\xb2\x01\xa95(\x0e\xe6\xac\x8b\\\x98\xef\xbc\x0d\x87\xa0|\xa3\x1dD\xb5Ni\x18\xe5\xe2\xe2|\xb8M\xde\x9a&\xde\xd9\x14P\xcdGU\xa2\x9f\xc8Q\x88\xea\xd1S\xd8#\xe1\x8d\x82eA\x07R~\xab\x99F\xdfDW,W\x8em\xb4\xfeF\x13\"kA>Zz\xd3\x1eV\x8eq\x90\x1a*l\xd7\xd7\xf0\x92\x89\xef\xd7\xd6\xb8\xf0C/\xbe\xae\xaf\xe2%d\x7f\xb7~$\x93d\xd0Ta\xbb\xa1F:\xeb\xef\x07\xa4\xa9\xcevc\xa5\xd8\xbb2\x94\x83\xe4\x9fm\xc8+\xd9hq\x95\xfbwWwxys\x1b\xf2\xfc\xe8\x18\x19Ee+\x90\x0b\xf7\x07i\xeb\x07.(`3\xff.\xae\xa3\xf8T\x18\x9e5\\\x03\x91\xc7\x8f\x9db`u\xca\x97F\xdc\x85V\xf8+\x9e\x16\x83\x846h\x08\xadP\x11Z\xa2#\xb4EI\xf1H\xd3\xc0\xdaM3 \xbc\xd4\x0f\xfb\x8d\xbd\xd7\xee^\xf1\x88\xbey\x9bM]\xd7nwhEZ\xa0\x05\x8d\x13\x8fP\xe9\x98\x87\xd5\xb8'A8X\xd4\x87\xd8\x12\x0f\xa5\xd96'\xdaez\xcdbQl\xf5\xb4\x9f\xeb4\x84\xba{I\xbc/\x13\xd12\xb6\xca\xc1\xc5\xed\xd213\x1a\xf1X\x85,\xbdQ\xd5'\xc4z\x1f^\x86\xd1U\x08\x82\n\x0c\x81\x0d\xdb\xa8\xc7`\x07l\x99\x12\x15a\x1d\xf2\xb8t:\x8e\xab\x05\xdac#)\xf9(\x92\xc6\xb06)\xe74a\xa0\xd3Dh\x04\xb3\x89k#\xa9\xc0\x0ef~\x10|\xe3\xa1\x96\xce\xbb}/\xb5X-\xcfkV\x9aW\xc0z\xdc\xd9\xa8\xc7Z\x84\x95U\x98\xcc\xfek\x04+\x96f\xdc\x96:^\x98$g\x10\xe3\x0d\xbc$}MP\xce\x16\x81\x11\xe9\xabwQ\x8a\x82\x92\xfc\xeeh\xe11\x8f:\xd9\x1b\xb0\xa4\x0c\xcc\x7f\xe6gUV\x13\xd6\xfa\xc9\x08\xfa\x83\x07\"c\x03<}\n{0\x1a\xc1>\x1c\xc0@\xbc\xd9\xa5o\xfa\xbbp\x00;\xe2\xd5\x0e}\xb5\xd3\x83\x03\xd8\x15\xaf\xf6\xe9\xab\x01\x1c\xc0v\x1f\x86\xb0=\xa8\x1d\x92g8>\x852\xb0\x98\xfev\x19DU!\x7f\x13\x07h\xb4;\x19<\xa4{\xd9\xee?\x1a\xc0=L\x0f\xebH\xb6L\xe5\xa5\xb0\xfe\x9f\xff\xeb\xff4PY\xf40*\xaas{A\xc91\xac_w\xb4\xea\x06\xd27\x0d\xa4_;\x10\xd0\x0df\xa0\x0c\x06\xffV;\x1c\x98:\x1c\xf0\x0e\xdb\x13O\xae\x0f}\xacC2I\x90\x08\xd1\xbd~\xa8`\xfd\x13\xc9\xd7\x0c\xa3y\xa1Wf \xe5qY\xe5}@?t\x94}\x91\xa7l+\xf3[nuS\xb1\xa8`\xb5\x1d\x89\xcb4y?\xe7#\xde\x96\x02\xa0\xd5\xef\xbdD\xab\x01\xa0\xebe\xa7\x85'\x10q0!\xf9\x08\x1dWjt\xf2\xc5\x0cs\xf2n\xb6\"\xa9\x0f\x03\x80\x97\x91\x93\x85\x17\x1fESr\x98\xda\x92\x07\xac\x1aWZ<\xb4\xd1\x98J\xdd{{\x83G\xfb\x80f\xf9OF\xb0\xb7\xbf\xd3\x7fT2\xf8Rp\xa9B\xd0v\x95\x85\xe3)\x9a\xc7\x12D\x06gj\x9d~\xa5N\xff\xcc\x85\xb0pS\xd7\xe6\xd9\xae\xbc\xd1\x9bxh\x89\xa32\x93\xbef&\x83\xe6\x99\xf41\xe5\x85v\xe1\n4C\xa8\xd7\"R]\xaa:\x90\xef\xc3\x0f\xa4\x03\x89]~X\n\xe5@jQ\xdaH\x0d\xf7@fr\\\xc3\xbdtL\x9bS\x82@\xaf\x1a\x0eL\xb7\x12\xa4\x1623\xed\x16\x13\xe3\xafl\xb3\x1d-\x91\xeaq_\x93\x83\xd2ZqV\x83\xbb\x9d\xd9*F\xec\xc06\xde\x94\xa8X\xb1#\xec\xd1B\xb1\x1a\xb5\xf8Qj\xfa\xb3\xf6\x83\xe3\x1a\x86_\xc2\xb4\xb0\x81f\x05w\x87j\xda\xadtP\x8b\x1d\xf9\xa0{.\x02X\xc1\xd4a\x036\xac\xcc\xcc\x8e\xe1|\xa8\x07\xc6\xa2\x86yj\x82\x85\xd4\xb0\xf8E\xca\xd1\xdcX\xc6\xc7\xa8d\x1b\xe4\xa7\xf5\xc2\x7faq\x9b\x9fA\xb9`\xa8\x80\x1f\x97\xcdU\xdd\x9e[\xed\x7f\xbfHB\x87\x9e\x989k&\x98x&\xe7\x18:\x06\xd9\xba\xf12u\xbd\x84\x02>\x1e}\xae\x9a\xdeJ4\xb2\xbd\x8d\x83\xa1\xab\xb7=`bv\xdd\xc0\x90\xb1\x92F\xe6\xb4\x1e\xc3\xe0\xf7\x1f\x03o\x0bC\xef\x8cD\xca\xbc\xf2\xa8v\xf4\xa3\x12\x9d\x97\xb7\x8f\xd9\xb0\x98\xe9 \xcb[\xbeJ\x15E\xb8~\xf5\xeb\xca\xf9\x16V\xa9\x8c\x1c\x9e\x01\xb6\xc1\x0e+\x94[\xbf1\xb4,x\x8f\xf9M\xeb\x86FKL\x1bFR/\xd4S\xcf\xf2v|\xa2!\xa4\xfaq\xd5\xf3Bw*\xa0(+p\xeb\xe1\x14bLy\xd2\x92\x04\xa3\x9cR\xb7\xba\x99)e?/^\x17\x176\x035y\x1f\xcfq\xae\xcf\xcb\xac\xd1\xae#\n#\x04J\xd9T\xca9\x13\xa2j\xda\xf0\x92\xc9}n\x8b\x91\xc6^\x98\xcc\xa2x\xc9\x8c1tn1\x18\x17\xfc\x9d\xa8\xd7\xc2r\nT\xaeY\xe9E/T\x85\xdd\xbcV\xbd\x1fG!\xb5\xe1y3\xb90\x0bi[qY\x1c3\x06\x0e`\xcc\x06\x85\xd0\x857\xb9\x14qj\x96Y\x90\xfa\xab\x80@\xea/Ib\x8cw/\x06\xb2\xc8\xc2\xcb\xdcG%\x1f]\xf1\x86\xa7\xec*L\xadx\x1aWW\x93O[<\xe2\x80apl\xe1}\xe0+\x86;\xb6_ k.\xecc\xe1 \xf8\x9a\xa8\x1bEW\xb6Z\\\xe9\xf1\xa6\xb0\x01\xd58\xdd\xd1\x8e%\xc4\xd1\xd9H\xcak\xae\xaf\xc1\xc1\xc8\x82]\x98\x8a)\xe8kk\x14\xdafZ\xa9|\\\xe8\xad\x97t\x0154\xd5\xa4P\x1e\xb5\x89E\xf2\x89J\x06O\xc5\xbb\x91\\\xc3\x9cgd\x16d\xc9Bj\x80\xfd=\x12%\xc2\xe4\x1e\x0d\xb6W1\xc9\x1d\xf5\xb2&\xbd\xa8\x8e\x9d\x12\xbe\x18e<\xd3\x8fL\x1a\xcd\x81\xfcW)g\x9a\x96\x19\xf3r\xdaZ^\x14\xcaDz\x9c\\\x15\xfb\xa7~\x1e\x9e\x89\xeb+\xdd\xa4hLH\xabLB)\xb1`Z\xc4\xba\xaf\x84 \x10\xe7e\xe5\x9e\xe3\xc8\x0b\x02\xba\x0d\x8bE\x9eF!\x81\xab\x05 \xe1*\xcf\xa8\xb45\x82\x9e\xa5\xe9?U\x89f\x89:n\xd8]\x92\xfaAP\xdajj\x979d4\xbe\x00\x85\xcc\xe6W\xf2\xaa\xb9\xd2;;b\xdcJ\xb4adw\x99@\xab\x93.Q\x90\xdc\xe9\xa9\xdc~\xc5\x97\xac\x18yy0\xa5\xfd\xd6$(T\x00\\|m\x080c\xec\xb6*\xc9\xea\xbb,{\x9a\xd5\x9d\x99(\x9b\xc8\x07\x0c\x85J\xe9\x10J\xf37\xd2m;qa+V\x10I/\x1e\xb5>r\xecXY#<_\xbe\xd0\x89sc\x04\xb1\xeaYP\x7f\xa9R\x0b\xdb\xdc\xe7\x84\xc8\x10\xc5[\x04\x01p\x16B\xb8\xc4\xae`\x0c&\x95\x81\xe9U\xb8,[n\xd4\x15M\x16\xfc/\xe9\x96\xb9-f@\\\xdd\x06=#$Z\xe6i\x90\xf93\x95Q\xac\xb6\xa6l\xb1z{\x0c\x96{=\xe4D\x969\x90\xab\xc4]!.\xb7b\xb5%\x9eZ\x97\x89\x17sH\xcaBQ\x14\x1f{\x93E\xb9\xa2\x94\xe2|\x12\x93\x12.\xb4K\x8b+\xf0*bDSKU\xb9\x0din3\xda\x04@Lgz\xef\xde\x06\x8c\xb6\x9e\x15DK\x97\x10\xbd\xd9\x1c \x18\x04\x10\xd2qxV\xa9|c\xf3\xb4\xb8\x18\xc9X]+\xb7\xa4h\x84\xdb.\x97\x16\x9e\x0e\xfc\xfd3\x9a\x940`\xc7iZ93\xcd\xf5\xf5\xab\x96\xbc\xf6^\xdb\x98X\x16\x95\x18\x84\xa9/\xf0\xe2\xee\xde=\xae\xad\xd8\xc6\xc4\x0c>\x86\xb6\x1e\xe6\x8e\x95x#\xd4\x9c\x1d\xb9\xd5\x1c\xcb\xfe7\xbb\x0f\x06\x8eM\x87\xc4\x91\xd6K\x12\x7f\x1e\xc2\x10\x8bv>\xd7\xa2\xd0\x05\xdf\xc5Tr.x.\xcf\xe6:P\x13\xa4N\x9aH\x0b\xe8\xee+\xe8#\xe7\xcc\x8f\xaf\x95\xaf\xf4\xaeY\x13\x17x\x08@\xad\x07\xd6$\ng\xfe<\xab\xc9$.\x985\xbdl\xd1\xe4\xc1\xb5\xf6\x82\x8c\x0cA1\x02\x96\xd6\x15&^n>V\x9cN\xec\xcec\"]\xe5\xc6\x15\xc9\xba~\xe8\xe6a\x97\x87\\\x8c\x84\xc55\xd4B\xd1\xdd8\xa12\xa5h J\xa6\xb9*k\xc4s\x06\xa60\xa4\x87>B\x86\xb1\x14\xe8\xa7U\xacR,_\xaa\xe0m\x11\xcfn\xfc\xe8\xa1\xe3b:\xd4\xf1\x19\xcbl\xdd@U]\x9d\x02\x9cr>\xde8=\xcb\x99y\xfaG\xb9\n\x92=\x82\xfd<\x86t{\xfb\xb1#|\\-\xcf\x82\x0e\xd8\x9dN\xe8\x14\x1a\xa8\x9d}U\xae\x97\xf4(\xc2i\xc2\xb6f!K\x98\x8bE\xb9\xc4a\xd3\x06 \x0fq\xef\x82\xe5@\x87\xfe\xef\xef\xa2\x8dY(\xbc5\xf1\xec,\xdc\x06\x1e\xc3\xcd\xe32\xcb\xd8z\x8d4\x14\x1f\xe5\x1b\xc3\x9a\x15b\x8f\xc2\xe7\xe0\xa9E\x9c\x8a\xea\xa1\xba7\xe9\x93\xd9\xe8\nU\xde z\xf4\x07\xdd\xed\xf2\xcd\xe7\x12'&r\xe8\xb2\xad\xeb\x91\xbeTM:\xe7\xe7$}s\x15\x8aj\xcfI2\x89\xfdU\x1a)\xf6\xd3\x99\xe9\x83\xd7\xdeR\x0dh\xe2\x99\xea\x9e^//\xa2 iq2i\xd7\x98\x91`~4\xc76Q\xf1\x14\xe5D\xb9\x06\x86\x18\xc8\xec\xc4\x11\xccN!~kC\x0d\xeaW\x1a\x9b\xb6\x99\x87M\xc4\xc2\x14j\x14?\xf2\xd2k\x9b@\xee\xb2\xfa]\x19\x81L\xaa\x0e\x0f0\x82\xdb\x7fY3\x91\xed{r ]/g\xffS\xb9\x95\xcf\xdc\x15}\x1d\xff\x1b\xda\x0fUUs\xa4w\x03\xa3\xdc\xe9mq\x94\x9ek\x9a,xt\xfb\xe4\xc4n<8\xd3B!Fj\x85\x0b$w\xc4\xd8\x10O\xb7\x1a\xe18>C\x07'\xe1H\x91\xa1<\"\xbe\xa8\xacH\xd8\x00g\xb9\x8fv\xfc>\x1f\xfa\xd6\x16W\xf6\xb1\xf0\x03\xe5\x14r\x9f>\x19\xb4d\xc8\xd5\x9b\xf4\x83\x0b\xd24\xdaVX\xa1\xe7\xa3\x88\x0b\xd6\xf99I^E\xd3\x0c\x0dN\xd4\xa5D>G\x16+Yt!/N\xc8\xf7\xde28BnE\x93\x16\x7f]D\x88\x0e\xed\xbdAO\x83q\xc8\xfc\xb0\x80\x0dq\xb7\x18\x04\x1c@\x0cC\xcd\"\x0bSS5\\p\xd1\xa9n`\xb5\xa8\xaa'\x0f|-#\x91\xe3\xaf\x9bx3\xf2M\xe4M+ \xacjID\xce3\xb1\xd0\xc8q|\x88\x03I\xba!\xb9zG\x89@x\x1c\xc7v\xa1IB*\xad\x1c\x97\x1bz\x916\x11\x84\x9d\x87\x06q\x88\x8e\"\xb6\xcbs\xf0\xc3I\x90M\xc9\x10\xc6\xa1=\xe8\xed8g\x12\x12\xfcC\x07\xd3\x1f\x0c\x9c3\x85\xb0-W\x81?\xf1S,\xdf\x1b<\xc0P\x06{\x83\x87\xfc\xdfG\xec\xdf\x9d\xde\x1dM\xe2N7S\x10y\xcc[\x99t\xdf\xbd\xf9\xea\xabo\x8e\xcf\x8f\xde\xbc~\xf1\xf2\xabS|\xf5\xfe\xed\xf3\xc3w\xf2\xab\xda\x9d6\xe8\xed\xfdN;-[M\xbd\xaa\xf6\xd2@\x165\x07\xf3\xf5\x8a\x0c!\xab\x9e\x10+\xef\x9a\x02d\x08v\xcf-\xb6\xa0c\xff\xfdF\xd5\xe2\x02(\x9a?\xd2M\xa3\xf9<\xa87\x0ej\x18\x91&\xabJ>\xa2\xd4\xd4uy12\xfd\xbaYL\xb2K\xce\x19\xe4\xac*\xaf\xa8Y\xff\xfc#63K^\x81\x1cod\xad\x89n\xaeU\xad\n|\x1eA!2\x12\x8dJ\x0ef%l\xec\xef\xa9\x0c\xc8\x97\xc2F^\xa7\x85b'\xa7\xca~\xc8\xe2:\x94\xd1\x8c}U\x1d\x04\xdf\xbca\x83\xae@\xa3i\xd8H\x17\xa1\x18\xac\xa0\xa9\x16\x8b\xde\x19\xba\x9br\x87\x94\x1a\x10\xf9\x1c\x18\xdeQy\xa1\x8f\xb7\">\xdd\xd1\xd6%\xb9N\x90\x91&\xdc\xa3\xc2\xc2\x1d\\\xbc\xc3\xe47C\x16\x14w\x1c\x9e\x9d\x95t.\xa22\xdeZ\x1e\ny\x05%\x0c\x0e\xe9\xd8f]\xa0\x91\x86T\x1d\xc3\xd0\xa7\xb1O\xff\xd2\xe2O\xa3haT}7~\xb9\xd1\x01\xcc \x9a&\x18\xde4\n))\xda2\x1ew\xb7\x1c\x9d:4\xbf\x1cJyK\x96\x87\x98\x90\xfc\xeezE8o\x0c\x1d\xb0\xc4\xed\xaa\x977\xbae\xba\xafn\x18\xec\x86\x9b\xf8\x91~\x0f\xef\xedj\xb7\xf0#\x95\x05\xcbP\x18.\x1a\x0e\xed\xc1\xbecg\x94\xf2\xec;\xb6\xe5\xa7$\xf6\xd2(\xa6\xe8\xd3t\x94\xa7r\xf0\xb2\x1b\xa7F;\xa8\xbb\xba.h&\x8c \xa6#\xa8\xe2EH>\xa6t\x13i\x12\x91\xd3\xdd\x80m\xe3b\xbc\xcc\x87\xbd\x19\xb0%\xf5\x84\n?N\x1a\x1fh\xc1\xba\xdb3\x93\xc0=\xe9\xea\xa3\xc4\x94\xfb$i\xca%\xe8W\x14\x9dEf-\x17\xd7.B}\x04\xe5\xd02N\x81\x98\x06\xae\xf7\x18\x85\xbd\x07;\xbb;\xbc\x7fV\x1f;\xa2\xc8\x82\xce\xdf\xf4-\xf3\xc2L\\\xecd@\xcb2\xd8\xe6\xcdt\xe88\xb7\xf9\xa0\x9e<\x81~\xcf\x81\x0e\xec\xef\xed\xed\xec\xdf\xcd\xa6\xaf\x1c\xa9\xfc\xe0\x18\xf4\x8dg\xea\xc0\xe9\xceI*\x0e\xf9\xe6[Y\xa4\xf3\xeaIjd\xf1H\x03\x8b\x87<\xd1E@L\x0c^l\x13n{\xe4\xdcz'\xf6w\xf4\xd7#\nOV\xa10(\xa4\xb5\x03\xdb+\x92.\xa2z\x034\xc9\x8dl\x0b\xa3\xcd\x0b\x9a:\xf6\xcf0\xc0\xc5\xd8\xfa\x97\x7f\xc9\x87\x83\xaf\xa21\xa5Ng\x9b\xcd\x9b\xae\xf6\x0eJ\xbb\xfd\x1d&\xf5\x0evv\xf9\xbfLM:\xd8ej\xd2\xc1^\xaf\"\x0e\xf7\x1f9B\x14o\xd3Y#C\xad\xc3G\x99E\xf6\xc7\xa1\xddwlK\xdc\xc6\xbf\xf3\xe6\x96s\x06#\xb0~\xc1L\x8d\x1d\xba\xcf\xb7F`\x8d\xd9E\x0b\xfcrf1\x1d\xc1N\xcf\xe1VK\xa5\xe8\xbd\xa2\xa1\xba\xb0\xdd\x1c\xf2y\x9b\x16t\xe89\x80\x01L;`\x9d\x95\x9c\xe3\xb6\xda\xe9\x07d0n\x85\xf6\xee\x80%G\n\xed\xdd\x1d\xc7\x1cx\x8d\x8f\xe4\x01\x9d\xa2^\xd7\x1c\xda\x8f\x1e9\xb65\xf5\xd7Tl\xb0<\xad\x19\xccF\x81\x86\x1fT\n\xd5\x9b\xcc\xaeW\x00\xa0\xd5\xe4%]\xbf\x89\xd0\xd4\xb3\xe6\xe8\xaa\x81'\xb1\xdeV\x813\xe9~\x95\xea\x10\xd3\x95\x9a]\x8e\x13\xc0\x96#\xe6\xb1\xc7\x05I)|\xd1j\xe9\x99\xda(\xca\xd4of\x9b\xb7\xb9\xf5e\x86\xab\x92X\xeb\xc8\x0b\xff\x94\xc2$\n\xd7$N\x81\xa3y\x1a\xc1*\xf6\x97>\x06+\xc4)l*\xd25m\xf7\x81\xe1\xfc\xe9\xef\xe8%\xe8~O\xe5_\xaa\"t\xff\x01\x17\xa1\xfb\xff\xaaE\xe8\x87\x86\x83]}\xcf\x01\xbb\xab\x03,\x05x\xcf\xb1\xad\x97\xc7\xe7oO\xde\xbc{\xa3\x1ez\x9e\xaa\x9e*\x17\xab\xda\xab\n\x15U\xba/F\x8c>?\xf9\xe1>/b9FxXV&\x1e\xa7\xdd\x17\x8f!F\x8b\xb3) HJ\xe4\xac7\xe3h\x1c\x9fir\xa6\n.W\x8d\xed\xaa\xa7\xa3%c\xe5rP\xc7v\xa6b\xbc\xbb\xdc\xca\x1d\xefF<\x05\xdd\xd1\x80\x1b\xd8\x0d\xad\xe7B\xb9\x98{\xe3\x8c3\xb4'\xc6\xec\x93hzVX\xc0\x8c$}\xac\xcf\xb2\x19\xdf\x16\xf1\xf7\x0c\x14\xc5\x80\xf75\x1c\x1b=\x92\xff5(\x8f\xf6\xf4\xa4b_wEG\x99\xc2\xbeco\xb5\xa3\x16\xb78\xd99\x80<.5T\xe9\x00\x82\xa8\xfaz\xc2\xcc7\xab\x10Gsv\xcfaJ\xa2\x8c\x19Z{\x08\x8b{\xf7`\"\xfc\xb44\x1f>\x96\xa3@\xe1j\xe0w\x94,\xe0Z\xb0d!\xff.\xb2'\xd8\xda\xa7OEk\xfa\x05\x9a\xdcv\x81vM<\x12\xb7\xe3\xb3~\xb1\x1c\xba\xe1\x90\x01|\x99\x1c\xe7\xf7\x8ev\xaf\xc0\xe0\x12\xc2\x9a\x18\\\xce\nS.#f\x96\xec)&\x10Km\xcb\xa2\xfb6\xb7\xfa\xbf\xedT*H\xc5pmWg\x9c@ \xb6I\xb5\xdb8\x95\x92^\xe2\xdf\xf4\x94\xff\x15\xe9)\x0d\xe4j\xb0\xa3\xfa\x1dD-8\x18\xc9j7?\xb1j\xcf\xd19I\xdf\x8a\x8aof\xf5A\x92s\x90pZF\xf7\x94\x0b\x11n\xabqt\x06C\x93i\xdf$\n\x934\xce&i\xc4r\xe3\x83\xe4\xb7_.=(\xff-\x1d\xbb\xc3\xf2g\x9c\x08\x1c@\x06\x8aG\xf3\x86\xe0\xef\xdfzK\xcaV\xc7\x9b\xf5\x9e\x1f\x9d\xc2w\x07\xfdH\xf3\x03\xdc\x15\xda\x97\x9e\xe3\xf2\x93h\x8f\x1f\xad(\x0e\x08\xcf\x94\xdd]\xc7\xc5\xfdLe\x03\x177\xed\xa4,\"\x04\xecUI\xb9\xc0\xf2\x82'\xe2~wQq\xcc8:==\xc9XN\xbe\xaa\x19\xc7\xd1\xe9\xe9)eH\x9f\x93I\xe0\xc5\x1e\x9da\xd5E\xe3\xe8\xf4\xf4\x03\x15\xafx\x13ji\xe0\x930=!\x93T_\xfe\xfc\xcd\xab\xdaB6\x17c\xf1\xbb\xe8\x92\x84\xfa\xc1?\xf7R\x8fy\x11\x92\xf8eJ\x96\xfa6^\xf8\x81a\xe4\x7f~\xf7\xea\x9b\xc3 8\x8a\x82\x80L\xf4S\xa7U\x9a\xca_D\xf1\x92k\xbb\xf5\x15N \xfd\xdeX\xe5\x15\x99\xfa\x9e~\x86\xaf\xfc%\xa1b0.n\xf5\xcb\xd7\xde\x92L_GS\xf2\xca[iJ\xa3\xa9a\xd5\xdfz>]\xb1\x9f3\x92\x18\xd6\xe5m\x90\xcd}\xcd|\xd9{\xc3pN?|\xf5\x0d\x1eC\xfa6O?|\xf5:[^\x90\xd8X\xfc\xd6K\x17\xa7\xc4\x80\x0b\xb4<\xf2C\xc3\x80O?|U\x87H\xa7\x1f\xbe\xca\xfdM\x0d5\xa2,\x9e\x10\x16z\xdeP\x83n\x94\xd3\x05!\xa9\x1e\xaa\xef\xc8\xc7\xf4]\xecM.\x8fL[%\xafa(\x8e\xb2I\x0e\xbb\xbc\xe4\x86\xa5\x0b\xf7m\x0cY\xc98\xf05<\x81\xa9\x904a\xdd\xe9\xe8\xf8\xd4k\x17\xe60\x82\xe9x\xad\x18\x9d\xd2g #X\x8c\xe7\x9a\x92sd\xe7u%\x170\x82sJ\xf1\xcfu\xa7\x11\xf0c\x18\xdd\x89\xed\x0bz\xf6~\xfa\x04\x9e}\xe1\xc2\xcc\x85\x95\xe3\xc2\xc58(\xde\x05,\x07s2\x9e\x9f\xb1\xe8\xbaK\x8d/\x03R\xd6kz\xa2\xc7\x0e\\\x8c\xaf\x99\x1a\x99~~\xedB<\xbe>+\xf4\x99\xd0\x96Z7*}\xb4>9\xf4\xbd\xe1~_\xd5\x05e\x82\x954In\xfd\x9d\x07\xfff\xf9\xf4_\x8e\xe5\x93\x99\xd7pl+\x0b\x93I\xb4\xa2\xd2L\xa22o\x1a\xa7m \xdf\x84f\x01\xfcq|\xc6\xae\x00\xfa\x0f\x1c\xdbG\xef\x8f\xbf\x9b\xf5{\x15I~\x1c\x9f\x8d\xd33\xc5\x89^;\x11\x93~\xbf\x16\xf5\xf8\xa2\xea\xc4\x93\xbb5\xc4j\xbfMe\xb7^\xbe\xa1T\xa6;\x11lV\xe9-c\xae\xf6U\xab\xa8\x19\xbe\xae\xdc\xed\x04\x8ckS\xde\xae\xd8[U\xc3\xb0`M\xab\xaf\xa7\x9ct\xa8\xd6\x91k\xf6~W\x1d\xca5\x17,\xd5^\xe7\xfc\xfd\xae\xd3M\x88\xb2e\x97\xbc\xad=\xc7V\xbe:\xe7,\xb1*\xd5^\xf0\xd6T\xf8\\\xf1\xf7*\x01\xfc\x88\x1cf\xae\x8fW\x8eE\x91\x0c{B\x12\xc5\x91\xf0\x18\x8b\xf8\xfd[\xb9\xe8\x10F`\xf1\x8fp\x87\xcf\xecS\xa5\xd77\xf5\xea\xdb\x9f0\x92\xde\x08\xce\xbb\xb3r\x01\xa5\x84[[\xf5\xaa]\xb3\x7f\x9d\xa0\x8e\xc7\xdd\x98$Q\xb0&\xb6\xba\xa6\xf2CX ZY\xe6\x19\xd1\xdd\xcb\xaf\x01\x93\x15\x99 a9\xab\xdd\xc3\xea\x93\xdao\\xc\x96v5\xd9\xfaA\xb2\x0394zl\xf1\xa58!?1\x86\x163_\x8a\xac8\x0b\x12\xdao\x1cY*\xab\x8a\xe55\x1e\xb27*\xf6\xbdl\x9c\xf3\xba\x9aX\x05\xa4s\xc4\xde\xc2\x98\xaf\xe5\xc9\xe4w\xf1,p)\x0e\xdb\xc1)\xa8\x89\xb4J\x7f\xbej\xa2s \xae\xb4\xd2\xee\xb9Q B\xcb\x14\xc7\x01\xf9Y\xe7\xe1\xbc\xcf'\xfa\x1a\xcb\xe6\xa4U\xa0J\x94i\xf7|\xcd\xe4\xc9>.e\xf7\x1c\x00\xe9F\x97\x18\x94e\xe6\xf9\x9ahc\xea\x93\xe0\xc5\x03\xdf\x1b\xcd\xd5'\xbc:E\xb8\xe6\xda3\xac=\x8d\x96\x9e\xdf\x94 \xc4\xb8\x81\xe5\xc7c\xc1.>}b19)\xec0\xdc\xd8[\xc6E\xd1\xbfF\x18\xa4t\x8b)\xf9=d=Fh\xedoc\x0e\xadY\x97\x84)\x89m~\x81\xe0\xd91\x8a\xe6\x94\xc5\x9du\xc9G?\xb5\xb9P\xbf\xd5sX\x1d\x8c\xb4\xb3\xe2\xe6\xff\x070\xb1?\xda\x16\xdfw\xdb\x93\x85\xe7\x870\xb9\x9e\x04\xc4b\xa1\xea\xe9:\xbe\xb4)\x06\x1f\x087\xd0\xd0\x85\xc4\x85 -N\xb0d\x08\x13;6S\x03P\xf7e#Xp\xfc[\x19\x9f\x1f\x9f\xc4\xc4\x94f[<75\xf4\x08\xc2B\x19\x1d=v \xb3\xc3q\xd4\xe9\xe8\"\xc8\x8a\x87n\x12\x1e\xe1&p\xd4p\xad\x9a\xde\xde6\xf6\xb6)\xfe\xea\xb1QF\xac\x1c\xe8\x7ff\xaba \x9c\"\x1c\xa7\xf2\n|\xb9\xd8)\\\x83Rm\xd0I\xa0\x12\xddS\xad\xb7~\xedJ\x9d4\xc2n-\x05S\xab\xc2\x85t\xcf1S\xb4\x8d?X\x184\x84\x01\xe9\x9e_\xd1\x02\xe2t\xcf\xd7,F\x1d\xe9\x9e',{\x04\xe1+l\x13\x86y\xa4{>\xe1\xc6\x94\xf4\xa0xe\x13\xd4]\xd4\x8e\xfcu\xbb\x91\xbb\x86\xc8g X\x9a\xb0{\xae\x0d\x05\x0f\x18\xec5\x9f\x14\xde\x90\xf39\x19\x8e\xdf\xfac\x17\x03M\xb2\x00\xf6bc\x15\x87\x1fL\xd0\x88\xe7\x82\xeefd\x1e\xa6\xe0\xa7 f\xaa\xa9\xa4\xfc \x9c_\xa2%\xd5A[\xe6 $!\xbd\xf9,<\xbf\xd2zGV\xaaM\x87\xba\x84\x82\xf2c\xe0\xca\xc5\xd3\x8ec\x11\xe6\xa1\xf4<~\x8d\x07L\x1f\xcf\xe6\x13\xfe\xfb.\xd9\x80\x93\"\xf3\xed\xadO~g\x88y\xc39\xfa\x87\x0c\xfd\xfb\x14\xbfC\x17\xb6L\xe3m7N>\xbe\xfa\x89\xb4X\xbf\x86\xb5\xbb1\xce\xbf:o\x85\xc9(V\xfc\x12\xf7\xfaq\xed\x86\x9d\xf2\xa8I\xc7.\x88Ma\xb9`\x9d/,\xc7\xc5t\x14\xae\x1c\xd5\xbaU\x14\xa3\xd4F4a\xed\xe6\x98\"\xfeT\x88K-\xd0O\xca\xf1\xb4\xcb_\xe6\x7f\xdd\xb8\xec\x107O\x92\xa9\xf9r\xce\x0e\xff\x92O^\xf6&\x91U\x97\xe5l\xe5\xebJ\xe5\x85\\\x991\x8a\xc5\x80\x9c\xb2-\x8f=\xd8\xddw\xecc\xd9\x86V\x1d\x1f [\xc4\xfc\x16\xa2\xdcO\xb6\x88uu\xac\x0b\x97-\xac\x8f\xa8\x0c5\xd2\x8a\xa9\xec\xca\x19\xf7\x06\x15\xb0\xca\xb5F\xe5\xd4\x83\x94\x92s\xe9\x07\xd9\x18z\x16\xf3?\x87\nL&R\x08_\x0e\xe3<\xf0\xa8\xa7\x96a*\xdfW|\x1e\x98\xb8>\x14\x12Jy\x9d\xcb\xfb\x08\xd1\xa5\xce.\x03\xca\xd6\x89L\x85\x90\x8f\xd3\x88C\x8e\x12.\xcd\xa4\xa0\xc6x\x1a\x8f\xab\xd8%\xb8\xc2\"];?Q\xf0z\xf45\xc6[\xc8\xb3\xf33&\x05KNx\x89\x8c\xcd\xe7]*s\xfe\xd4\xe6\x828\xc5\x93\xed\x18\x97\x13\x7ff\x94\x83\xe6\xc1\xe9Q\x8d-\x1b\x9e8.\x04v\xd0\xfd\n:\x10t\xbf\xc5\xff\xbf\x80\x7f\x86\xadK\x15!\xdf\n\xa6\xe8\xb8\xf41\xb3&\xb5eZ\xc1\xad\xdd\x1f8\xb6\xfcJD\xa3\xcb\x0d\xddY\xc7\xa7\xa5.%z\xa3\xce\x8d\x82\xa7i\x91\x05\x83\xf4\x93\x8e2\x81\xa4z\xea\xb9\xb9\xb4\xef\xb0\xe8\x9bzD\xab\xc0\xa9\x18\xae\x8dl\xd3\xd6\xa5S;j\\\xef\xa6a\xf3Q]\xd9\xf9\xe6\xc8\xd7\xed\x98'\x93i\xc0S\x05\x92\xf6%\xd3\xd4\x0fv\x1fJV\xf0\x95\xbe\x8f\xbb\xcc\xc0\xb9\x8b;\xc8~#\xa3E\xdd\xb4\xbc h\x9a\x92\xcc\xaa\xeaO=F\xb5L\xf6BxsQ\xaf\xbe\xf1y\x15\xb3\xca&j/\xa9\n::\xd6\xdc'\xcaO\xa4\xb7\x9b\x93\x1f\x8a\xe8\x86\x14\n\xf4YSZN\x8f\x91\xf6zV\xb4\xb0\x82\x11D\x9dN3\x07\x98\xd4\xa4p\x10O\xc8(/#\x81tov:n\xa1-\xa3\x18\x81$\xb2\xfd\x08\x01;\xa6\xacE\"\x98\xf4\xb1w\xc6(\xdf\xf6vFKb;l\xe2\n\x8dB3p4\x97\x9a\xd2\xd6\xbb1o\xf9\xa8\x8bG\x97oG\xddu\xdb\x83%\xf6&\x8d{\xf7\xae\x10\xdd\x8c\xc5\xfe\x06X\xbc9nUW\xbd\xd8vP\xa3\xcd\xd3\x88\xb7P\xbf\x02>[\x81\xd8\xf6\xebV@\"A\xf8\xf3V\x97\x83L\xe9\xa5N\x9dgp)\xdd\x1c\xa0\xda^\n \xc84<S l\xc4\xe5\xb6\xa6m\xef\x97m\xe2\x81\x8d\x9fIN\xb38Z\xdaQ\x83\xad\x0c;7\x07F\x90\xe8ma[[\xd6\x17\x01T\xb6\x8a\xb4\xe3\xaa\x86Y\xe8\xcf\xd5\xf7z~A\x02\x9c\x9e\xd8\xa0g\xbf\x06\xa6\x90\x1f\xb9MP\x85:\x9f\x00\xf10\x0f\x80\xb0\xba\x00\xe2\xd1\x9cj.\x0el\x83\xee3]\x1b\xa9\x1d\xd5\xdczk\xe9\xfa\x9d\xa4\xa9\x90\xc8\xa5\x9e\xcbV=\x00\"-u\xe2\xf4\xa6\xa2.\xe4~\x0e\xbb\xfb\xd2\xba\xc5v\xdc}\x0b\x1d\x88\xbb'5wJ3?\xf4\x82\xe0\xba\xad\xba=\xe3\xb7\xc4~\x1e\xc1\x9aJ\xc2\xe2\x0f\x83\xae=4\xddjk\x98\xdd\xca}q(\xab&\x8d\x96\xd7\xfc3\x8fRGT\x84\x95/R\xea\xf8\xab\xca2\xcb\x8f\xce\x9a\x8c\x8al\x94\xad\xf8\xc2\xe3\xe2 u6\x1a\x96\xf9\xae\xf2\x0b\xa2n\xc5\x7fD\x84?\xd8S\xb0\xf1\xb4\x06\x0f\xd3\xb85\x0e\xd2C0\xd5g\xe0\x86<\xd1\x97\xce\x9eV\xdcB\x87]\x82\x86\xed\xfc\xee\x7fX\\\xc68v\x88\x97$\xcd\xd7\xd2m\xe0\x19\xda\x83\xbd\x01\x8f=\xb7\xc3\xff\xdd-\xc7\xaa\xdb{\xc0\xff\xe5\xb1\xea\xf6x\xac\xba\xfd\x1e\xff\x97\x7f\xbf\xcf\xbf\xdf\xe7\xb1\xed\xf6\xf9\xf7\xfb\xfb\xfc_\xde\xce>og\x9f\xb7\xf3\x80\xb7\xf3\xa0\xcf\xff\xe5\xed=\xe0\xed=\xe0\xed=\xe0\xed=\xe0\xed=\xe0\xed=\xe0\xed=x\xa4\x8d\x9d\xc7|j\xdb\xc0\xa2\x11\x8b*\xbeNQ\x1ep\x13\x8f\xe3#\x1e\xae\xb2J\x10\xe5J\xd1\x94\xa0\x17\xb0\x82xH\x06\xd1z`\x8b\xd9\xb5\xf71\x9eJ\x1e\x16#\x8f\x1dR!\x8fr\xa3M\x08\x9a3\xb4\xdc\xe4r|\xe6\xe2\x9c\xf3\xccPy\xa4\x9c\x8c\xf9\xe9\xc6\xf0\x142\xb3v\x80g\xb9\xeb\x14\x99\xa52\x8c\xa2\xe3Sj\xd2\xef\xf7w\xfb\xfd\xbe\xc3r\xf7\x8a;\x91\x13/\x9c\xf3K\x11R\x8e-\xbe\xf6\x02\x7f\n\x93hJ`E'c2\xab\xe4w\xd4\x04\x9e\xb0H\x9dp\x80\xb1~0B,\x8b\xe4\xd9\x01\xdb&\xb0=b\xe5\x0e<}\n\xfd\x1e\xca\x14\x7f\x84~o\xb0\x0b\x1d\x16\xffS\x97|\xcc\xb4'C\x9eSP\xcd\x9c\xbb\xe1\x8ek\xc22CT -\xa52`D\xec]\xb5\xc7\x03\x16;\xa3\x1b{W\\\x10\x8d\num\x1dnP\xcc\xf1\x18\x8e\x84\xf0\x14\xbc\xc7\x0edl]x\x08Z2\xf6:\x9d3\x07\xe3D\xdc\x87\x9eF\x8a\xb0\x8e\xa2,L\x0b\xe7\xac\x90\xcc\xbd\xd4_\x13U|\xe0\xc1\xf8\"x\xaa\x1ar\xf1\xc7\x8e\xe0\xe9\xd3\xa7#\xe8;\xdc\x9b\xb53B\xc3#zb2\x07\xd7\x90\xbdz\xac\xac\xd3\xef\xa7\x84\xdb\x948\x17 \xda\x9a6aQ\xb3n\x1b\x16\xb5\x9a6\xa2\x8eD\x97\xfa\xd0\xad\x00\xe2\x88o\xe7\x84r\x93\x1d\xea\xe6\xe1DM\x99/\xe2[\x10\xd6\x18\x97\xad \xac!\x15\x92(\xec\x84E\x0b%\xac\xf1g\x11\x07\x93dBW\xc5\x0b'\x8b(\xdeH2\xa9\xe5\x06\xf9b`\xd4z+\xf4\x96\xc4\xaaK\xec\xf9\xd9\xc3\xbf\xf0\xe7\x1b\x8d\xbd\xcd\xd0Y\x9b\x16\xfe\xf7\x05G\x1e\xf8\xe1\xe5\xdd\x8f\x9d\xb7\xfa\xc5G\x1f\x05\xd3\xbb\x1f\xfc\xef0\xf0\x99\xff\x91\xdc\xfd\xc8\xd3\xf4\xf7\x18z\x14\xa6\x93(\xf8\x12\xbb\x956MG/\x9a\xff\x82;\x96v\x95\xf8\xbf\x90/7 \xde\xfa\x17\x9c\x83\x9fz\x81?I6\x9aB\x9b\x19\xf8\xbf\x03\x16mLvZ\xc1\x1e\xc9\xfd\"&\xb3/\x0b\xf8d\xe9\x05\xc1F\xa3o3x\xd1\xea\x97\x06=}}\xb9\x19\xe2\xb7\x1a\xbeh\xf6\x8b\x8f?\xbb\xb8\xfb\xc1g\xbf\x07\xd5O\xb2\xd5\x17\x18\xf9\xea\x8eF\x1e\xda\xfb;\x8em-\xbdt\xb2\xb0\\\xe8\xd7\xd7\x96\xc62\xce\xebi\x15\x9dz\x88\x88GH\x02i\xddE\xa2/+\x1aP\xcf\x90\xe7_\x0b\xc7\xc4\x9c\xdaB2\x9b\xf7\xe1@\xd8\xd81\xcf\xa8!\x9a\xb7q}n\xe8\x8c\xc9\x99P\xd8\xc7\x95X\x1f\x10n\x9a\xd5\x9f\x03\x93\xeb\x14-\x17\x06\xb7\x00g\xecV\xdd.\xa0\x15D\xa3&\x88f%\x88\xc62D\xe3\x96\x10\x95\x04\x88\x18C\x95\xf9\x08T\xf6\x86\x832rX\xe8\xa5;\x03hB\xbc\xf8\xdf\xd0\xf3\xce\xa0\xb9\n\xfcT\x8b\x9c\x15\xcbI3\x98\xc4EFh\xf7wUc=\x10z\x8f\xeakv\xb9\x867eU\x8d\x885A\xe3\x14\xcb\xbb\xb8\x98X\x92\x89mYt\x8e\x1a\xa4is\x1d\x02\x92%\x9a\xd0\x01\xe8\x03\x01@\xd9\xd7f$\\\x8bx\x12\x9d\xdc\xceMM\x86\"\x7f\xbb\xe5\xcb\xa9\xd3\x8a\xa8x8:\xfdgkf\xc2\x9f\xb80\xc1p\xd3\x01\x0b\x8b_\xe7u\xbe`\xa1;\xfdy\x18\xc5\xe4\xc8\xc3`}\x96o\xc1\x90\x1ey\xd0\xa1e\xcb,H\xfd\xc0\x0f\xb1hY*\xcaB\x1f\xaf\xda\x0f\xc0\xcaJ\x05I\xeaO.\xaf\xe9\xfbk\xfe\xde<\x84i\xbd\xd3\xfb\xba\xbc\x9a\xb4\xb3\xdd\xc1\xa3\xddG\xfb\x0f\x06\x8f\xf6\xd0\x8e\xff\xe9\xd3\xa7u\x0d`4\xd9b\xbf\xa7\xdd\x04\x83\x9c\xbb\xb0\x80\x0eXs\x93\x85\x00\xaa\xfaX\xf0\xaa\xb8\xdc\x02\xbb\xcb\xbc\xe6\xed\xd0F\xfe`\x1fl\xfd\xf0C\xe2X.,t\xd7\xd0\xf9\x83\x0e\xec\xd7\x0c\x17y\xc0\xce-\xdb\x9e`(1\xd4*C\x07\x92q\xef,\xc7\xf0\xa70E\xad\xe1\x8aG3\xe1*\xa4\xa9+>p\x1c\x17\xb6\xd0h\xbf\xa4\xe0\xc2\xc4\x1f\xbd\xb3\xfc\xe2-v\xebY\x9f\xd2\x83S\x0f0\xd0\x00\x04\xf0\xa4\xaa\xe4\xde\x86\xc1c\x08:\x1dG^\x99B\xa3\x16\xa0\x15\xaf\x8d?FZ\xe5w\xe9\xb9q\xdc\xea\xe098\x9e\x141\x15\xf1\xf2\x9f9\x00\xad\xe8\x07\x0c\x12}\x87g\x89\x90\xc0\xc6b\xc5O\\X\xe5\xad\x8e`\xed8\x8f\x1d\xb8\xee\x06^\x92\xbe\xc4\xb6\xf1>\x83\xf7s\xef\x9e\\\xa4\xc6\xf4\x16\x0f\xdf\x8cSv%S\x84\xf5\xde\x9a\xb1\x06(\xc9\xc4,<\x9f>\x01_1\x96\x93G]>:\xe8bp\xb0\x86\x03X\xf1\xb2\x9e\x0bk\xfc\xa42\x02\xc5,\x99\xb9*X=A\x1a\x85\n\xb3\xe7H\x10\xb3[Q\xb6\xf2\x99\xa9\x92+8\x80\xf1\x19\x0c\x05\x0d\xcau\xb1\xaa\x14\xa8\xd7iK,\x82\x81\xe5\xba\x05Su+>@b\xaa\xc2\x82\xa9\x8a+LU\xa8c\xaa\xe2M\xd9\x80z\xe5|f\x87\xf6\xe0a_U3\xfb\xbchg0P\x8b\"^\xb4\xd7\x7fHIL^&\xc6\x80A\xf1\xf5\\\x1a.f\xda=?'\xc9\xabh\x9a\x05\x18G\x1e\x86\x9a\xa5\x98\x92\x99\x97\x05\xe9P\xbd\x9f\xff\xa7\xea/q\xd2\x8e\xfd.\xff\xca\x85\xa8\xf8i\xa46|L\xd5\xbe'\xd1r\x15\x85\x94\x80\xe8F\x06\x98{B\xf8.}\xe3]GYJ\x17\x8fw\xd8\xb4Y\x8a H\xa8\"_Ny\xb7_S}\x8eW\xe2\x82U@\xbcr\x0b\xc2\x03\xc7\xcb\xe1\xea\x9d*\x9aLl\xca\xf9=\xd4\xa1 \x16\xed\xf5th\xc2\x8a*\xc8\x95\xe5E;j\x91\x97\x17\xed\xabEI^\xf4@>\xda\xf0\xd5\xfe\x9e\x1e\x15'\xbf?*\xcej/\x18\xf3\x91\x91:\xc1\x9f\xd2\xde\x1c\x9b\x1dN\xe8\x88\xe3bA\xa6\x16\xd8\xa4{~\x8e\xce\xe7\xe7\xe7\xc8&\xf4\xdc\x02\x1f\x1d\x9b8\x0e?\xadX\xf5\xfcxTE\x0c\x1d\x98h[\x9e\xd4\x96\x0b)\x1fFTz;\xae\xce\xe5\x92\\\x0f\xc1\x8aI8%\xb1\xe6\xa6\x94\xe3]#3\xb0\x96\xf3c\xac\xe2he\x88?\x03\"UFwN\xd2#\xb1\x85\xcduYd\xf0dE&,!P\x14\xd74\x1c\xb3\xd0\x1fq\xdc\xa2.\xdd\x13\xc4\xb6\x8e\xa20\xf5\xfc\x90T\x1cn\xe4'buO\xa2\xab\xbaZ\x99h1\xa8\xab\xe5\xb1Z\x18\xb57\xb10\x9c\xa9\xb9\xf2\x84U~\x17\xad.\xbc\xb8\xa9\xf2\x8cU~\xe6%\x9c\xde5}\x10\xb0\x0f\xa2\x90r\xeb\x1f\xbc\xc0\x9fzi\x14?\xf3\xa6s\xd2\xf4)&t\xe8\x06\x917\xf5\xc3\xf9i\xea\xa5Y\xa2F\xb2\x97\x9f\x05z/S~\x89\xdd\x9f7\xb0\xf7\x94GZP\x04\xb1\xad%I\x12oN\x90+\xb24J\x01(6A\"P\x9d;T\xf2\xdcQ\xb6o\xf2\x94\xa4\xcf$\xf0\x92\xe4\xb5\xb7$C\xb0\x92+o>'\xf1v\xe6[\xda\xfa7.L\xe0\xc0\xd8\xcf\xc4\xc5$l\x0eO\xc6\xe6\x82\xc5\xe1c!_\xb4b|\xaa\xfe[\xcc\xed\xddv\x9c~8\x8b\x8c#\xbc\x93\x1e\xf8\xc0\xb7'\xf9\xee\xf8=\xba3t\xe2`\xf8\xb7\x99\xe7\x07d\xfa\xaf\x12\x94\x8b\xdd\xd6\xbd\xa5~\x1a\x10c\x0f\xd6\x0b\x04\"\xa4\x11\xd0a\xc1\xe1\xdb\x97\x80l\x88Oi{\xd7r\xcc\x83\xf08rKkq\x84\xae\x95_dE\xcc\xe4\x013A\x9b\x18>\xf1,\xbd\x8f\xdf\xfa\xd3t1\x04\xeb\xe1\xc3\xde\xeacM{\xacz<\xf7\xc3o\xc8,\x1d\x82\xe5ei]\xffE\xfd\x13\x7f\xbeh\xf9AJ>\xa6\x87\x81?\x0f\x87`M\xd0\xdf_\xbfDP9\xdf\xf3\xb7\xff\n\xb01&\xcb(%\x85\xc7n#NZ+\xcb\xe5\xa4v\x8a\x88\xb9\xb5B\xe5_\x92MD,\x8c\x06\xcc\x9cq\xac6\xf7\x11\x89\x1eL\x15\xb2\xa6\nA\xbes\xaa:\x0dE\xea8+\x85H\xba\xb1\x8b&sNIb\xa9\x89(m\x1bl\x8a\x8a\x90;\x15\x8f\xa5\x81\xd3\xd5\xe6Am\xd3\xa2d\xdc\xa7\xcf\xff\xd6\xdf\x91\xad\x96\xa9p\xf2\xc8\xb1\xadrGV\xb3\xf4g\xe6\xd4\xa5J\xbe\x92\x86\x14\xe06\x17o\x83\x87{\x1a\xc1J\x02\x93^\x1ely\x01\x12\xabb\x9f\xa8^\x8c\xb3\xcd0\x8ba\xf5U\xeb\xce\xc2\xabk\x8b\na\x94\\\xb3qWvmy$C\\\x1d\xa7;\xdb\x10b2\x10*\xed3\x89\x8c\x02U\xbd\x8d($\xbaas\x0e\xb6\xca\"=b\x0ey\x0f\xf7\xaa\xfew\xbd}\xa7;\x93\xfd\xe8\xdb\xb4\xd8r\x12\xaa\x01\xeb\xe7Mb\xf0\x88\xbb!>\xe2n\x86|V\x83G\x0ft\x9b\xf4\xf4zy\x11\x05m\x9an\xb2\xf34\xd8\xe1\xaa;\x98\xdby\x1a\xbc\xad\x0d\xce\xd6\x03\xb5q>\xfeG}\xa7\xfb\xf5\xf1\xf7\xe5\xb2 /S>\xe1\xa9\xe5\xd4\x1eXj\xb9G\xeaxXn\xb9=\xf55\xcf-\xa7\xbc\x9d\xe6HR~\xbf\xe6\xefU4\xbd\xe6#T=\xe4\xe6\xfc\xbd:F\x9eV\xae\x82\xed\xec\xb5\x1a\xfe\x92\xa5\x94\x1b\xe83\xcaU\xb0\xed#\x9b\xa8\x1a\xfb\xee\x94\x81E\x95\xd6\x8e\xf9\x08\xd5\xea\x87|U\xd5N\xdf\xb0\xf7j\xf5\x9f\xf0u\xc5\x0d\xf5\x12Fp\xa8\xe6\x90{ #x\xa3\xbe|\x85i\xe1\x94\x97\xefP\x1ed\x18].9\xc2\x92\xbf\x9c\xbey]~\xff\x16FpD\x8f\xf2\xa3n\x82\xaaW\x7fv]\xaeqB\x05G\xdb:_\xf8\xd3) U\x11\xfc5+M\xa3\xb7\xb1\xbf\xf4\x99\xadv\xb9\xc67\xe8\x00\xa6\xcd\xb9_\xae\xf8\x9c\x92{\xdbJp\xf4\xdb1\x99\xfbI\x1a_\xab\xcd\xfd\"\xd7\xaa\xa4\xb9|\xc1J\xa3\xd5\xb6\xa1\xc2{M\x12\xf3r\x8dg\xa6\xf8\x01\xef\xca\xf5~F\x88\xfe\x955V.\xfa\x1eF\xb0\xf53F\x0e\xffY\xca\x08\xa0\xfc\xdd\x9d\xf9\xe1\xf4h\xe1\x07\xd3\xf2\xd7\xdf\x02\x8f\xf18\xa9w\x8d\xe3G\xdf\x03\xd8\x1a\xc1\xa9\xfd\xd2\xfe\xfb\x0d7\x0f\xd33\x91\xed\xe2\xb1@\xd1\xf0K\xd9\xe4\xac^0\xe0\xda\xac\x07\xc6J7N\xd7\xd3\x16V\xd9\xf2\x1bG\xad{\xe3\xc8\xd1\x0f\x0c\x8c\x00H\xa4\xf8\xd2~\xaf\xbf\x9dE\xd7\xd5) HJ\xe0\xfd\x98\x9c\xb9t\x92\xbc=\x1e8,\xc5;\x8a\xf7\xf4\xe7Kl\xa6\x12 \xf9\x06\x86\xf0\xb2\xbcd\x1fj\xb5\x9e \xd9\xd0\xff\xc2|\x0dO\xedw\x05\"\x98\x0d\xd8 K\xa5\x9bV\"|\x96\xbb\xff\x1aF\xf0\x8c\x8e\x98o\x8b\x12\xd6v\xc5\x91]\x02b\x0dBi\x1aI+\x00h\xd5R)\n\xf3\xbb\xba\x19|\xd5\x82\xd5+5<\x12\x8b\xf4\x95\xfd\"_\xc0%\x8b\xf2\x0f#\xb8\xe2\x19\x8d\xe8;Z\xe2\xdb\xbf\xe0\x9d\xdb\x01\xc6c\xc8 \x10f\xe4\xa3\xfd\x9d\xb0\xbc\x93\xe3\x93\xb31a\xb7\xa6\xe2\xf7\x88\xe7\xa8\xc0E\x0bM\x1b\xa1hr\x08\x1f\xed\x1e&\xb6\xd0a6\x0c\x8b\x0e?}b\xd8w\xe2\xc2G\xbb\x8fyv)\x7fR\xf4K\x87\xffm\x0e\x0d\xfa\xed\xcb*_\x0bU`\xfe\xa1\xcd]\xe3R\xeb8\x91;\x93\x87\xcca\xfc\x9a'\x82#th>K}\xc2\xa21\x8a|\xdf\x11<\x05\xff\xb1\x03_\xd9)\x83R<\xf61n\x00\x19\x87\xba\x10\x96b\x05\xeb&\xf0\xe7\xd6\xdb\xe9\x9b\xd2](.|\xcaRY\x19{\xde\xc2\xda\x05\x02!j\xb0\xbc\xa3[>E\xa6\x94\x19\x04\xd8[6#\xd9\x85\x0b'\xff\xf3\x17\xf1[\x94p\xecY\xf8 ]\xbc\xf4\x0c\x0b\xd5k\xd9\xf2\x14\xff\xd2f\x8d\xfc\x19s\xdc\xbd\xd0\xe0\xb5\xa0S\xf9\x90\x08\x1f\xd2\x0b\x16bY\x8f\xa7\xc2n\xe6\xd2\xae\xb1_\x11\x80\n\xab\x8dW\xb6\xca\xa7O\xca\x8e\xe2x[\x8d$sS\x07\x8e\xbf5\xae\xb8\x1a\xee\xe2\x95}\xc1\x9c\xa0c\x1e\xc1 \xe2\x11\x0c\xba\xa5\xdc\x8fl\xf4\x94\xd9b) qe(e;\xc9\x7f%,T#\x0bDa\xc6\x9b\xb8n\xfc\xdfm<~N\xc2\xd8\xf8_a\xe0\xa1\x170\x04>\xa9\x88OJ\x84\xee(&\x95=v\xc4\x9a\xe0f\xcb\xc4\xacB\x8e\xc1\xef\xc5jElJ\xbf\x8cI\xcd>\x8c\xca\xb3*\xea=\xc3\xa5\xf5l\xfb]]\x14,\xc4P\xba\x9ddB_\x0d\x99n1\x96\xb4\x88\x0f\"\xe5(\xaeDN\x17W^+\x9d\xcfX\xaf\xe43\xd6\x93\xbc:\xdd\xca\x14\x89\x94\xd3\x01\xc9\x19\xa9\xac4\xca=\x04\x9b\xf4E)K\xc4\xffOr\xd3\x87\x98\xb4\xe8/.\x15Q`\x04_a\xc4\xa1\xbd]\x07\xff:\xc6\xff\xff\x8d\xbe\xdb\xe7\xaf\xfe\x8c\x15z\x0f\xd9_\xdf\xf1\xf4\x97[\xa1\xfd\xf0!\x02\xd5\xa3\xb3\xb7t\xe2\x82\xe5\xd2\x8f\x91\xbcL\xbb\xf5\x17\xcd|\xbc\x1f\xecEIuE\xc7\x9b\xd9\x19&B\xca0\x11R\xc6T:\xcfTh3\x84\x1dJ\\\x8bl\x17\x90o\xe6\xbfRaa\xe1%/9\xfa\xbb~r\x14\x85\x13/=]\xc5\xc4\x9b\xa2\x90#\xf8/\x17\xcd\xce]n\n\xe623_\x97\x87rt\xd1x\xc8\x95\xe4(W\xac\xcb;o\xee\xca\x99\xfd\xb9\x9d\x91\xe5Z\xf4\x18H\x19\x85\xf8k\xb1E\xd2\xf4\xb1\x03\x0b\xfb\xaf\xe34-'\xbd-HP\x8a\xd9J\x16\xdd$\x8dbB\xa95o\x85\xa4E3!mfm\x93t\x1c*\xedP\x08\x9e\x96`\xc7\xf7w5\xa0Q\x14\xb7d\x15}\xfb9=\xd3:#4^<\x80\xe7tO\x0d\xd9?\xa3j\xea]\x85\xfc^\x92\xeb\x17\xcd]\xa19\xe7\xd7h\xceY\x9b\xd3\xc1\x03\xc6\x01W(\x13\x94\xc3\xed\xf8!<\xd7\xdb\xd3\xd1\x9e\x9e#\x177\x92\xe3\xbb\xd72\xf1YBNI\x9a\x92\xb8AJ\xfb^\x17I\xb2\xd2\x92\xbf\\\x05M\xf6\x05\xdf\x97\xb3\xd7\x01\x94\xf5\xba\xaen\xa1\x0d:O\xa6\x9ao\x91\xca\xaej\xe2F\x99\xf0S\x1b\x93\x96\xfd\xc1>e\x9cN\xedb\xab\xfa\xd5\xafj\x8a}\x92\x0c\xe1\x0f\xe5\ns\x92\xbe\xb9\n\xc5\xf7\xcfI2\x89\xfdUJ\xd1\xe7/u\x15_{K\xda\xd8\xdf\xea\xea\xb0m\x90\x0c\xe1\xbb\x12\x1cQ\xc1R\x06\xa6\xbd\x85\x07l\x8d\x88/\x8e\xc1wjxL!\xa6\x8d\xc3,\x08\xce0\xfe\xcd[[p\x9d\xd6\xdfo\xf8\x9b*\xec\xbd\x8a\x11\x8f\xf2 [\\\x85b:.X\x7f9}\xf3Z\xe3@\xce\xf5EM\xfb\xae\xc4\xfap\x86-=\xe3Y\xe4\x1f\xebb7P\x81\x82sd\xc5a\xef\xebSx\xf3<\xaf\x9c\x1d\xea\x9f\xb9`\x9f\xdb\x95\x94?\x9c\xc1\xffZ6\xe6\x9e\xf3j6i\xc3\x8c\x8b\xbe\xb4\xba!\x16\x1a\x08\xf9\xcc\x8au\xa6\xe3\xd2~\x89c \x03\xc0\x91\x84\x8e\x9dN\xc3\x85\xb7\xdc`\xe9\xa8\xaaz(\xa1\x95\xa4B\x18\xbfFV<\xb4\x07\xfb\x8e\xacZp\xe1u\xa9\x1eK\xc2\xf2f\x86\xd9\xe4\xde\x15\x84\x1b\xff~\xe5\xa5\x0b\x17,\xfa\x0f\xb7S\x81\xc0\xe6J\xc3\x1c\x07\xb6z\xad4\xff\xd2\x0d\xd6\x9ec[K\x92z\xba\xd0\xbb\x1a\xe5m\xa4\xd7\x9a\x8b`\xa4\x8e\xaa\xf3\xf4\xaav\xebI\xa1\xe4\xf3\x93\xe3\x8f) \x13\x9f\xca&\x9f>\xd5\x13D!\xf8\xd4R\xd7 \xa5\x9a\xa8]o\xa5\x9eK\xec\\\xddH\xd6$L\xf9p\xa20\xb1\xa9\xc0\xaf\xec\xc7rW\xf5<\x0e\xe0Q\x9c\xa2\xf7\x91I\xdaC\xb5\x9c\xbe\x90>\xfe\x10\xac7\x16t\xa0\xd3\xf1\xaa\xbc\xa4x\xae\x86j\xb0Z\xf1\xe8\xb4wu\xb0\x0b\x94\x1cR\xd5\x91}}\xfc\xbd68\xf9\xeb\xe3\xe3\xe7C\xd8\xeaWKf^\x92~M\xae[\x9c=\xa0u\xe9\xd0\xa9\xbb\xb85$s$e\x86Fr\x99u\x8a\xde\x14o\xd1\xcd\xc2\x90C\x81e\x01\xc0\xe51J\xe3y\xbd\xa44\xa0\x17\x06{\xac\xbcz\xe1\xb9b\x1d\xd7\xd4\x9d\xa9\\\x93x\xf4\x8b)x\xfcq|\xd6\xad\xe6\xce\xd7\x84p\x9b\x93\xf4[\xe2]n\x02\xf9[\x01dK\x1f\xe3\xa5\xa8M\x8c\x11\xab\xe5\xe73\xc0q\xd5\x06\x1cQ\xf8\"&\xe4\x97\xc6d\x82P4>\xa1\xc7F\xd0\xa5\xc8\x8d\xe6\x146?\xa68\x98\xe8\xef\x19rD\xed\x0c\xab[\xd3\xe4\xca\xbd\x93\x08\x19\xa4'\xc6\xfb\xa6\xe4G\xe6\x89\n\x05]\xac\xcd\xd4\x16\xb2\xc0\xba\xe5\xb5\xc2\x83\xbc\xbaB9\xf7\x90\xb9\xfc2\x94\x02\x84\xf6\x1eug,\xa1J\xef1x\x05\xf30y\xec@\x92g.\xa7\xe7\x867\x9e\xa0\x96\x04\xe5{\xe4*2=O%\x19\x89l\x06\xd0\x87\xfb\x06\x08\xb1\x08\xef~\xc2RY\xc9\x07\x90If\xb5\xb0*\x92\x9c\xd8\xbe}\xa6\xab\xca\xed'_\xe2\xbd\xea \x1a\xb1\x1b:!oV\xcf]+b\\\xbfD\x06\xaf\xfcp\x1a]Q\x88\x16\xbf\ns\x17\x95m\x86\x83\x9aB\x9b\xb5@\x05\x80\xb1\xce+\xa0\x9d\xa8\x8f\x81v\xad1\x1b)|\x8bM\x9e\xe1\x88\xf3Di\x8d\x17 \xe6\xbc7\xb9\x94\xaa!!\xcd\xf9\xe3\xc5\x10\xb9kQ\xa3\xbd\x92\xcdS8\x97\xedn\xf4\x08\xe0\xc0\xdf\x1b-\"\xfa\xbd\x07\x8emy\xc9u8y\xb9\x91\xfd\x86\xf8\x94%GA\x1dL\xab\xef\xda\xd9}<\xba[\xbb\x8f\x9d^\xaf\xc6\x08+\xf9\x0c#\xac\xaa1\x90Y\x12.\xf73\xc4q\xf51\xa7U1\x9fV0\x94\xb6\xb2J\x95}\xbd5D\xd4F\x8c\xa1T\xd6G\x12\xba\x15S\xf9\xe7\xde=4\xa3+\x07v.\x14#\x84eCe\x11\xd9\x12\x92\x82\x97@.Ml\xa9\xe1\x18\xf44\xb0\x02\xa0!h\x17\x05e1+w\xe6\xb0\xc0\x0f\xe1\xef7\xd5\xbb_m\xca\x1b\xf3\xde\xb5\xf9\"R\xd1\xe8\x05o I\x82\xcb\x0d6\xba3\xbbb\x12\x00\xd28XF2\x188\x0e\x1d\xc0\xf8\x8c\xdf\xc5(Yf\x91l\xdf\x86:\x10}f\x8a*W\xc2\xc9\x88\x0c\x0d\xa3V[(\x95Y%\x96\x0f5\x95\x1ceF\x10\xc2\x90\xe5\xc0 \xdb\xf0\x17h]\xb0\xd5wL\xfa\xf6\xc9\x82L.\x87\xd2uB\xabM\xdb\x8aN\xecT\"\xe2}.\x9d\xd8\xfdlKD\xc3!\x14s\x1bUVg\xb3\x81\xdd\x8e\xdc\x08\xc5\x1bZ*\x15\x1d\xb6\xa20M\xf6l\xbb\x06\xdb\xd3==\x97\xb8S\xb1\xf2b2\xfbN_\xb5\xf2bl\xdc\x8e\xfa:\xe1\xd5u\xe9\x89\xe9{\xb5\xf9\x19\x7f\xaf\x0e'\xe0\xcd\xab8\xba\xc2Li%+\xe2r\x85\x85T\xe1\x857I\xa3X\xb1\x85\x9a\xb2\nA\x14\xea\x1bXW\xe3@\\7\xca\xf0mn\xc4\xe7Za\x19\x8d\x87b\x12\x9aD\xfc\xa5\xb7\x1aB\xd4]z+\xbdp?\x8b\xe2co\xb2\xa0u\xf8O}\xbdI\x94\x85):\x1e\xd3\x1f\xfa:i\x84\x04\x90\xd6\xe2?\xf5\xf5\xa20\xb8\x1e\x82&\xe7Y\xb5zn\x9c=\x04\xbf[\xe3\xd3\xf66\x8bI\xa9n\xe9E\xb5~ \x03\x86\xa0\x01\x8e\xbc\xc2C\x98V+\xf8 \xfau\xe5U\xbcn\xf9\x8df\x90q\xb4\xa2\xc7j2\x04\x8d\xf7\x1c\x1b\xd2Q\xe0%\xc9\x10f\xa6r\x8e\x93C\xd0\xac\x13\xab\xf1\xca\xff\xe8\x87C\xd0\xc0\xfe\xf9\x9bWC\xc8\xaa\xef\xd7$N\xfc(\x1c\xc2\xa4Zv~\x9e\xe05\xd6\x10\xd6e\xe4\xd4S\xc8V\xa99\xea\x89\x8e\xacQ3\xf4\x12\x7f~/\x94V\xe9y\xaa\nM\xe2\x02\xb0\x81\xb2\xf5T\x0e\x96\xa5\x13M\xaf\xa2C\xae\xb6~\x1bE\x81\x9a\x8e\x14g\xd1\x9dEY\\W\x8bR\xbd\xfb?\xdc\xef\xdc\x9f\xeb\\{gFA\xc8\xb6,\xe8@\xea\x94\x82\xbd\xff\xe1\xde}K>\x8f\xaa\x0d\x06\xdas\x0d/|i\x1df\x85\x86\x7fN\xa20e\xb9\xb9H\xfe&c7\x88\xb5=\xact\x0b\x05\xd2\xb2\xa4\xd8\x93f\xb3a\x19\xefV\x91\xdb\x99l\xe7c\xc3)\x1b\x88\x9c?]7\x8e\x85\x18\x87\x86\x93\xc4\xe9\xc4$a\xde\x1fb\xc6\x97\xe4\xfamLf\xfeGi\xce\x1c(a\x05(\xf1F@\x996\x03\x85\x0d\xa7\n\x96\x0cK\xf3\xb1U+x50Md\x98j\xa8 ;\xe8(l\x13\x05\xb6\xe5\x05(\xe97\xec \x95\xb1\xd7\x14\xe3b\x84o\xd4M\x17^z\x82\x88\x99\x08d\x17\x8e\x9c\xb05b\n0\xdbW\xa8'm\x87\xbe\x9f\xa0\x9a\x08\x89\xf1a8=a\xf8\xfc5\xb9\xa6\x1dd\xd0\x01{kB\xe7\xcf,yP\xb9C\xff\xc2\xe4\xf2\xf8\xeb\x00,\x0b\x860\xb3\xf1O\x87\x8a2\xf7Qg\x1b\xa2\xe1\x10S\x05M\x9cztYK\xe8\xe2V#g\xacy\xd4\x0c\xd5\x89V\xcc\x90\xdd\x0c\xa1hf\x87b\x08U\x83\x17\xbaV\xe8\x9a\x8b\xa4`j\x13\x8c\x8c\x81\x1d\x96+\xa3\xc6\x7f\xea\x82\xe7\xb8\xb0\xe8\xc6$ ^Bl\xaf~\x0e\xd7&,\xe34\x83\x0eVj@\xfc\n\xa4\x8b\xa3)\x11\x06;u\xf6@\xa5\xad\x81\xee[\xca\xee(\xbd\xacl\x10\xba(\xdetJa\xe0\x87\xf3w\x91\x1d\x88\x89\xdej \xf9F\x96z\x95\xf7\xb2\xf4\xfa\x0e\xc7\xbcp!Q\x04\x8c*\xfb\x96\xb3^u\xa7\x98xP3J\xf1\xa9dM\xa0\xb9x\x10D#(c\x92.\xc9:\xe2\xd1\nS\x17@\x90\xe3\x91z\xdfX\xa6\x0c\xc8O~\x91\x01\xeb\"p S\x01\x9b]q\xb1U\x10\xa6\xda\x0d\xc3|\x19\xa6\xd1\xb7~\xba\xf8Z\xac\xf6\xcb0%q\xe8\x05CX+\xc7,\xe3m\x1b\xf5&B\x87G+\\s\xd7\xc3\xbaA\xe4\xfcp=\xf3/\xf4\xe4M\x00 \x02\x00z\x92Z1\x10/\xf0\xf3\x8b\xf1j\xa1\xbd\xaf\xd31\xdb\xa1M%\xaf\x86y\x0b\xc3\xc1\xae\xd0\xa0Pl\xad (\x07\x12\xac\xaa\xdf\xad\xa2\x95)\xf3\xb5\xc0=\xdc\xbd<\x12|\x15^P\xa7p \xc9\x15~_1B\xaa\xd5\xbfi\x95T\xb2\xc2\x08\x0d\x0f?}\x82\xd8\xb6\x06{h\xcb%\xd16\xdbq5\xf3\xe4w\x1cOx8\x90(\nN\xfd_\x880>V`B\x0f\xb7z\xb3\xa9\x0c\x934\x97^yZAS\xa6o-\xf6\nH\x96\xc6\x86\xebQ\x01\xda\xd2\x98\xb9\xd1kXP/\xb4\xeb\xf8\xf4 2\xfa6\x9f/3:\xce\xff\x1c\xb1\x8cp\xa1\xa0b0\xa2g\xa7\xc6\x02\xb9\xca\xe7P\xce\xa2\xc4\x83\x0fU\x80\xd0\xa7\xc2\xcf\xb7\x84\xc1m\x90\x1cd\xd8m\x82\xe8\xa0Cv\x11\xa8P\x07\x0e\xd0\xe2<\xe8\xf0\xbeb\x92\x05zp\xa6\x8b\x98T\x00\xda\xe6\xc0\x80\xcf\x84V|'\xd0\x8a\x19\xb4tG\x8cx\xda\x03\xac\xe2\xa5\x01z\x98U\xe5\xc0*\xc8\x0c:o\xf8L\xa8\xf9w\x025?\x87\x1a\xe3&\xaa\xb6\x03\xb0)\xe0*\x86O\xd5\x16\x0c\xe7\xdag\xc4\x0fk>\xd7\xfa\x05\x1f\x15?f${\x1f^\xd7\n\xb3\xe5\x05\x89\xe57\x05Ty\x17\xa4\xfb\x87?\xf0\x91\xd1wE\xfe\xf4\x99\xcd8V\xcb\xca\x93\x87y\xd0\x81 \x9dp\x0f\xc5`\xc7\x05\x8d\xc5\n\x9dqM8\xd65\x8a\x9bR\x93CLd\x93\xe8\xa1R\x96\xd0\x89\xc6\x1f\x01d+\x8bkfOq\x0dO\xf2$<\x8f\xe1\xba\xd3q`\n\x9d\x11\xa4\xf6\x8a\x9e\xc9\xe3\xeb3\x17\xd68\x97\x95\x0b\xd7\x0e_\xbd\xea\x0808\xa6\x99C\x98\xb3,\xa5\x06rC\x87?o\"bK\x17\xdd\xc0\xe7\x9c\xbb\xab\xa1\\\xd8\x1c\xbb\xe8\xec\x920\x8d}\x92\xe8\x81!\x9e\x1c(\x17\x0c([\xf6\x12Fp\x8e\xa9\xe9m\xc7\xe9N\xa3\x90<.\x01f\xc9\x0c,%\xd8\\t:f\xe8\x88\x87B\xa9y$\xc6\x01\x98\x01$\x1e:\x89\xabb|\xe6\x91\x88\x07\x0d:lifWhZ\xbbF\x03fN.\xae\xc6\xbd3\x87\"\x9e\x98kO\xcc\xb4\x1e\xac\x06[B\x86+\xb8\x91K[\xac \x01>\x1a\x92\x91\xc9\xcfi\x11+\xba\x0eCb\xdb\xda\xe9[naG\xc2n\xdd\xce\xd8HN\xe1@\xec~\xb8\xf2\xd3\x05\\\x92\xeb\x04\xfenAG\xdcg\xd3\x176qx\x9a[\x17P\xd9d\xddX0\x84S\x17>\xb65?3J\"\xd3R\xc1\x0d\xa5\xb8\x96\xa5\xf2\x1a\xadn\x1b\xeb\x8f@\xad\x8d3\xf7\xe1\xbaw\x8f\xff\xca\x1d\x8b\xabg\xa5\xf5/\xff\x92\x07\n\xd1\x9f\xd3f9)\x97\xf2\x80\xc5\xcdEg\xc3\x18\xcd\x9b\xd3\xb1\xafZ\x80\x1b-\xb2\x89\xc6\xdc\xfa\x0e S\x1e+\xdb\x08me|=\x1a[#k\x08\xd6\xa8g\xc0`k\x88\xc5\x83j\xb8\xa7\x1b\xa3\xc6\xc0\xfa\x03\xc5\xc9\xcaE\xc0\xfd\xf1hxv\x7f\xde$\x9aK\x0d\x91qzV\xed\xb7^\xa6\x0c\xef\x06(=\x9c\xb6 (\xa3\x01-\x1en\x02\x14\x06\x0e\xdb\xea\xb2\xcd\x9c\x8e{\xe8\xe8Ma\xc5\xfe\xee\x9f\xa1\x8dD\x92]0.\xc0\x1e\xd0#Z~\xd1w\x1c \x9a\xf6\xa8\xf7i4p\xee\x1e\xa0\x05\xbe\xea\xf7\xce\xdd\xdc\x80\x0d\x9c\xba\x9bn_\xaf\x07\x18R\x12Y\xb1\xe4\xc7\xa2\x8b\x8b\x98\x95^\\h\x83~z\xd3iL\x92\x84\xd5a\xbf\xb5\xd5b\xc2{\x89\x89\xbe\xa38\xf5'\x01\xe1u\xf0\xb7\xb6Z\xe2Oy%\xfaK[%\x9b\xfa\x11\xabB\x7f\xe9\xaa\\`\xf1\x85\xb6\xc8KX\xfb\xf4\x87\xb6\xc2\xd4g\xe5S__\x1c\xf1b}\xcf\xfe\x9c\x15\xfbsmq\x10M.\x7f\xce\xa2\x94\x8f!\xffS[9\x9a^\xb3j\xd1\xb4\x12P\x05+\xb0\xa5\xd3/\xdcE\x96\xa6Q\xc8*\xe0O]\xa5\x89\x17\xae=\xb6\xb8\xec\xa7\xbe\xd2*\xf5yS\xfc\xb7\xb6\x9a\xcfgE\x7fh+D|i\xe9\x0f}\x85\x80\x97kc\xc6N\xa2`\x1eG\xd9J\xd4\xc1?t\x15\xa7^\xca\x90\x91\xfe0U\x08\xfc$\xcd+\xd1?\xb4\x15\xa7\xac\xcaT[H\xd8p\xa7D;\xdc)I=?Hx\x15\xfc\xad\xad6c\x90\x9d\xce\xb4P\x9d\xfa^\x101\x9cb?\xf5\x95\xd6\xbc\xc6Z[\xcc\xc7\xa9\x1f&\x87\x82v\xfed\x89\x85d\xa9/\xbc S^~A\xb4 \x9a\xf9$\x98\xa2\xe9`l[\xe2\x0f}\xc5\xb9\x8cf\xc5\x9f\x86\xcaYLD\xc5,\xd6\"\xd3,\x8a\xd0+\x93V\xc2\x9f\xfaJ\xf1\x92W\x89\xb5s\\\xf4\xb1x\xd1\xd7\x16\x0eX\xe1@[\xb8\xc3\nw\xb4\x85\xbb\xacpW[\xb8\xc7\n\xf7\xb4\x85\xfb\xacp_[\x88V\x1f\xb4\x98x\xda\xf5\xa0\xef9P\xd8Om\xa5b\x97-\x8c{l\xc1[\xd1\xb7\x90.\x19\xca\xd1\x1f\xba\n\x8c\xc4j \xac?\x8b1\\&-\xc7\x9f\xdaJK\xb6%\xfc\xa5v?\xf8\xe1*c8\x87\xbf\xf4U\x12^A\xbb+//\x18 //\xb4p\xbc$\xd7s\xc2P\x95\xfd\xd4U\n\xbc\x0bN!\xf0\x97\xb6\n\x99\x93\x90\xf5\xc4~j+1h\x05Zp\x05~x\xc9\x8b\xc3K]\x85\xa5\xe7\xb3\x81\xd2\x1f\xfa\n+^\xae]\xe8\xa5\x17_\xf2\xf2X\xdf\x01 3V\x81\x84\x99\xa9\x82\x9frR\"\xfe\xd0W\xe4t[\xe7w\xc8+p\xec\xc5_\xba*\xa1\xc7Ha\xe8iIa\x181\xbfaV\x87\xff\xa1\xab\xc8\x04F\xac\xc6\xc5Z]%\xb6\xbc\xfa\xe3*Z\xa5\xc5F\x12\x7f\x18*\n\xba\x17\x19i^\x94\xa5\x02\xa7\xd9O]%\xd6\x97\xb6\x93\x95\x17{l\x05\xf0\x97\xb6\x8a?I\x05]\xe5\xbf\xb5\xd5D\x15Sq4\xcf9F\xf1\x87\xae\xe2\xcfX\xe3g]Q\xcc&\x12kg\x123(\xc4Z\x08\xc4\xd9\x05\xe3\x99\xe8\x0f]\x056.\xed\x80\x12o\xc9\xfa\xa5?\xb4\x15\n\xd41#NB&\xf9r\xf2\xdf\xfaj\x81\xc0/\xf6S[i\xe9\x05\x0c\xc5X\nN]\x15L\xa3\xc4\xea\xe0Om\xa5\x95\xc7\x07\xb4\xf2\xf4\xa3I\xe3(d$\x95\xfd\xd4W\xba\xe6\x0c<\xfe\xd2V\xc9\x18\xeb\x9ddZ\xe6;\xc9\x96K/\xbe\xe6U\xf0\xb7\xbe\x1a_\x07\xfd~IY\x1c\x95\xd8\xb6R\xe6\xdb\xa2\xa9\x92\xf3\xce\xa9\x89yN\x19\xd9M\xb5$7%\x1f\xd3\\\xa4\x11\x7fh+R\xde\x82\xd5\xa2\xbf\xb4U\x16\xac\\\x9br=\xcd\x8f\xec\xd4tf\xa7>?\x0e\xe9\x0f}\x85T\xc0\x03#L\xeb\xaa0\xaa\x99jIf\x1a{\x93K^\xeeM\xb44\x9e\x11x-u\xcf\x18\x82fZ\xec\\{\xac\xe3\xb5\xa7\xedy\xedO \x13\xa7\xf0\x97\xae\xca\x15\x17r\xae\xf4R\xce\xc4\x8f\x85T\xc9~j+\x05\xfe\xea\xad\xc7\xd7A\xfc\xa1\xab8%3\xc1\xaf\xcf\xb4$\x82\x04\x81\xbf\xe2\x02$\xff\xad\xab\xc6v\x92\x9e5Yzs\xce\xdd,1\x93C\xb5J\xe0\x87\xac\x06\xfda\xaa\xe0\xc5_\xc5\xde\xd4G3f^\xb5x\xa5\xfbh\xe9%\xe2\x1cO\xb4k\xbc\x12\x10Z\x19\xa0\xb3\xf2\xd2\x94\xc4\xa1\xa8C\x7fk\xabE\xc1\xf5\x9c\x13@\xfe\xdbT-\x9f\xa9\xf8CW\x91\xce\xc9\x0bJ\xb3-\xbf\xd2~$\x88kl\"\xadi\xc4\x89L\x1a\xe9\x89\xfd\x9a\xd3\xc3\xb5v\x1d)Q\xc8\xa9\x83\xb6BNtSFuK5\x0c:\"v {\x07:\xa2:\xbbvn3\xdd7\xb9\x07\xfb\xc2\x9e\xecs\xc7\xd1\xdf\xdb\xd8\x01Yx\xe4\xd0\xfe\xe4`\x8cw\xa0\x03\xd6\xd8\x83s\x8f<\xf5\xf6\x97[\x8f\xebcYT\xdckx\xa8\xe7}5V\xb0\xf0\x8b1\xf9\x18\xd7\xda\xa2\x08[\x92\xcfQ\xe9\x03\xb7\x08\xd6\xab\xf5E/3Z\xe3\xc9\x13/\x8c\xc2\xebe\x94%O\x9fj\xb4\xb7\x81Q\xe5\xeb1s\xb9\xb5m\xe1/\xddN\x00\xd4eQ^ym\xe7\xf7\xba\x86zt\xbaX/\x9f\xb7\xa1\"\xbb\xe0\xc5\xaa\xfc\xae\xd7PQ0\xf2\xeb:F\x1e\xf2\xc08X\x91\xdf'\x9b*\xf2 ck\x11\xcf\xd8T\xd1\x0b\xaf\x870\xb5c\xd9\xf6\xef5^`\x9bA\xf9f\xd6\xa4\x82\x17\x8f\xb8\\*\xe2\x99\x14\xe6\xce.DM\xf7\x8b\xca\x15\xccVal\xe0\xc8\xf6\x1d\x0b\xdb\x12n\xdf\xf0\xa3\x05\x1d\x88\xa0\x03\xd6\x8f\x10\xcd\x8a\x94s\xac f\x05\x0b/\x01?\\S\xea\x93{\xcf@\x18\xa5\x98\xc0\x82\x8a\xdd\xfe\x94\x88\xa9vM\xe9C\xc5C\x11\x14\x13I\x8dCC\xb2W\xf1`D\x89\xf2\xa5yV\x1b\xb0B<\xb4\x0b4\xad\xacD\x17\xd0=e\xc8\xbc\xe4\xf3\xa4\xd3\xf71\x16\x99\x02\"\x0c \x8d\xef\x12\xf6.\xc9V\xab\xc0gi>$\xa8\xb9@>\xae\xc8$%S\xf0B\x06\x9d\xaeu\x9b\xebX\xf1\xe4w\xe0<\xd0\xc2\x04\x9e@\x96\x1b\x06L:\x9d\xb6\xa0\x99aj\xc9\x0c\x93\xe2r\xcc\xa2#\x1e\xd3\xb1O\xe8\xaf3\xcb\x05\xaf\x05\xe4\xe8\x02\xcddCJ\xf4T.\x8c.>c\xb2:sx\xf5\xb91\xdc\xe2\xea\xb7\"\x11\x1eb\xf9\xde\xfa\x82;qC$O7@l\xef\xcb#\xb6\xd7\x1a\xb1!\xf1\xc3y@\xe0\x84x\x93\x94s&\x9f\x87\xe5\x9f\xb3\xf0\xa6\xack\x02C\x7fWB\xbce\xd3\xc5/\x99\x19\xb7^c\xe6P\x14zK\x16)K?+\xf5\xf1\x1a\x8d\x9eM\x0f\xc3\xc1\xae\x14\n\x16\xe3\x0d\x97\xde\xe0h\x8a\xad\xdd\x8c}\xe2\x11vp\x95\xc6Z\xb5pc\x1b\xa2W\xab\xcf\x97Gv\xb1\x92\xf4s\xac\x91a\x8d\x7f\x1c\xba\x1b\xb8(\xbc\x92\xbb%\x91\xabu\xb0R\x1fD\x9bk;\x1d\x933Ge0\xe4\x05\x88\x8b\x05\xf0\x0d\xc0\x0e\xab\x94\x05I\xca\xebhJ\x1a9\x8a\xcf\x81\xa1\x89d0\xbe\xf2w%\x18\xff0\xceM\xcc\xb5\x11\xd0\xf2\xa9\xd6L\x93\xdaq`%+\xb3\xad\xd1\x08\x92:T\xbaC\x8e\x8c\xf5\xd98g\x89\xeb\xf2C\xc8\xea\xf7:\xf0 e\xdd\x85\x97H\xd1\x95\xecI+\xd2\x0f\xf5\x0cZ\x17\x19\xb4v\xac\x19|.{\x06\xff\x00\xd2\x15\x85\x1b\x1c\xd1\x1a\xe9@\x8aTW\x11\xd0jL\x0d?o\xeb\x16Q\xd1\xc4\xce`\x810\x1f\x83\x07O \xcd\x19tO\xf6\x866=tR+\xba\xf2\xe9\xd8\x93\x89j\xed\x04@\x12y\xfer\xfa\xe6u\x91?H\x9bYB~6\xdcih\xb2*\x1f~-\xb6Z\x14\xe2\x89\x99o\xcf\xba\xf3\xf2\x16\xe8B)\xda\xef\x8e2R\xe8i\x16\xad\xbb\xb4\xd2\xa4Y\x14\x13\xba\xa0T\x9b\xa9_~\x8c'C\x98\x0f<\xb2\xb7\xfa.\xe4\xab'\xe2\xf4\x96\xd6&\x87U\x17\x8eU\xb1\x14\x8f\x8f\x05\x99\\\xe6`L\\\xb8\xc8R\x88\xc9\x84\xf8k2\x85?&\xe0\xa5\xe0\x87S\xf2\x11\xfe\x98t-\x17\xce1\x99\x0bA\xe7m\x05l\xe6\xd5\xfd]\xb6`\xef1d\xa5\xe5\xc8\x9a\x97\x03\xa4\x1d\x94\x8e\xb3\x86%\x01(\xfb\xd5&\xe5\xd1R\x02\xed\xb4\xa2\x8e\xd0\x9a\xc6\xb6\xd9\x9f\x86\xadxw\xfb-Y\xb4\xb0&\x15\xcfg.\xe9\x7f=\xac\xc6\x8f\xac\xc7\x1f7\xe44Z p9\xb30\x9e\xb4\xc4\xd9Y\x9bf\x817\x1d`\xac\x84;\xe1C\x82\x1c\xd4\xf5\xdb\x01\x1a\xb7D\xbb\x0dswL \xf9\xe8M\xd2\xdf\x11\xeb\x93\xd6X?A\xacO6\xc5\xfa\xc9g`\xfd\xe4\xce\xb1^\xa0p\x86q\xed\x18\xff\xd4\xc4\xb5\xe4;%\xa0;\xa5\x15J\xd3\xda+\xdc)A\xcb\x9d\xb2\xb5\xda\x0cN\x97\x84\xcbdA=9\xfe!|\xe6M\xf3+\x0cZ\xa0\xf0l\x0c\x06,\xc6\x80\x05\xdcs\xe5\x87\x10/\xff\xd0\xd1E\xfb\x95\xec\xf7\x92:\xa5\xef[l\xd35\xf7s[\xd9\x89\x0bAu\xb7\x07\xedv;\x85\xdb4\x07\xdb\xf4\x1f\xb4\x8f+oo$\xafM\xa8\x06B\xd2\xe1\x8f\xd0Z\xe5\x891x\xf2\x02\xf8\xf4 \xfap\x1f\x0b\xf0\x07\x81!f\x00c^2\x84\xfeR\x03@\xe8\xfb^\x18\x02\x13,\xfc\xa4\xbb$I\xe2\xcd\x89\x14\xf8(I\xbd\xc9%\xbaW\xb5j|j\xc8\xff \xcaC\x9b\x11\xa5\xc8\x85\xcc\x85\x04)\xbc\xd6\xe5\x93>6=\x883\xa6\x89D\xa23\xc1\xa4V.\xb0X\xa5\x9e\xc3S.`b&dE\x8f\xbc \xf0\xc3y\x11j\x0dp\xe7xi\x14'0\xf5c2I\x83k\x91\xe4\x85n\x94(\xa6D\xe3\xe2\x1a\xd2\x05\x81\x1fWq\xb4\xda\xa6D'\xf9\x11V\xde\xe4\xd2\x9b\x93.\xbcO\x08\xfc\x987\xd8E\x865\xff\xd3v~\xa4\xfbl\xe2\x05\x01mb\xd9\x85\x13\xe2Ma\x19\xc5\x84r\xae\x8b4]\x0d\xef\xdf\x9f]t\x97\xe4~\x96\x90m\xfcz\xbb\xe8\xc7\xb8I$<\xc48\xd0\xe3\xe8\x0c\x0e\xd0\xd93\xf7W\x15\xef\x18\x91x\xb7 \x85\xacS\"\x9a~\x82\x86\x97\x94\xf1N &?g~\x8cZEY\x9eb|\xb7\x9f&\\\xd4\xf2\x13\xf8\x91vD\xe9(\x0c\xbf\\\x1f\xb9\xbf\xae\xe8\x88Nn\x08\xa9]\xc2\x91&Op\x90\xaf\xe6\xbb\x17~8\xb5\x19\x19\xda\xeak\xc0\x9b\x8b]~r\"F\xaa~\xd7\xabF\x981`\xfc\xba6\xa4\xa3\xe9@v!3a\xbd\xb8k1_\xe1\xf0\xb6\xe7\xb6\xe7p\xe2p\xd0\xee\xa8(\x1d\xa9K\xfay\xdbS\x95\xbeM\x05[\xcf\xd7\xa9\xba(\xaa\x17\x93\x1eb\xd7\xb6\x96\xf2%W>\x8b\x92\x9b{\xef\xe9\xe13\xf1\x12\x92;e\x0fk\xaa\xf0\x9b\xf7\xba*\x85\xbb\xb8\xbe\x16\x14\xd06\xa5 `\x0d S\x84\xe6f\x0c\x9e\xb7\xac\x19\xce.\x99[\xd1\xbas\x8b\xb6I\x97\xacI|m_7x@\x97=\xdeS\xb9\x89\xbaD\x0bk5Bc\xa3\xa8\xb0.9r\x86\xcc\x913\xe4\x8e\x9c\x93\xa6\xdb\x95\x8d\x1c;\xd5\xe7\xa6\xd1\x0f|+n\x953\x82\xce\xc1\x17)O[9\x98\xc7\x8a\x83y\x1b%\xc2c\xd8\xb2}LhPv\xec\xae\xfd\x12\x8a\xbb\x10\x9fyuK\x0b\xd97\x83f\x03gs\xdd\x98Zr\xbd\x18Z\xa8\xad\xb39*\xaf1\xf1\xc5\xb5\x9d\x8d\xfbg\xad&\x02mt;&\x8c\x16\xe1\xa5\x1b\xbf\xaf\xf6\x7f\xd3\x8a\xcc\xcd\xeb\xbd^\xc5=\x8b\xf1|R\xf5\x85p\x00\xdc.\n9?I\xbd~B\xe6\xc7\x1fW\x85k\xba\x05-\xa3\x13\xf1\x9e\xa4\xfc7\x9c\xd3\x14I\xa1\x18\x95\x18[\xff\xf2/R*B\x0b7p\x835\x19\x91\x07\xc8^W\xe1\xc8\"q\xd1\x81\x8b\x11T2W\x1a\x80\xbb4\xc7\x14\x93\x12\xcb\xe1\\rjW\\i1\xb7\xe8*\xe4\xc5\xda\xcc\xb5\xfa\xebJ\\\x82\xfa\xa8O2\x00\x9e{\xa9\x94\xb1g\xea\xa5\xc4\x90\xb4\xa7\xf2%[\xdb\xe2\xdb\x98\xcc\xc9\xc7\x95\xc6\xeb\xd9\x84F\xed\xe0y^\x8f\xac\xfaT\xd1\xe2\xc4n8\xaa\x19\xd2\xd6\x1d\xc3\x8d\xc7\x9e\x98\xbd\x17\"gS{\x86\xd6\x1f\xc5\xac\x0e\xae@]\x05\x0e\xe6\x16#\xaa\x1bP[\x1a\xd3\x14\x89\xae\xfc\x17\xffH\x8a\x88 #v\xc5&g/\x08\x14I\x05F\x94\x95\x0e\xba\xf2\x8b\xc0\x055\xe8\xe7\xad\xccb\xebb\x01\xe5W\xfaw\xd4\xbe\xd5\xdf\xeb\xeewy0\x84[\xb5\xb6.\xc2\xec\xef=tLa\xc5\xfdV\xf6\xcf>\x7fu\xf8\xfa{C\xbc\x87$\xf5R\x7f\xd2\xae\xee\xaa\x08\xb4\xde\xa26\x8f\xf2\xba\xc1\x07\x0b?\x98\x1em\xfa\xd5\x9c\xa4\xcf\x199\xa0;P\xf9\xe6\xfc\xd5\xf1\xc9W\xc7\xcf\xcd\x9f\xbe\x0c\xfd\xd4\xf7\x82\xd3\x14S=l\xf4\xe9\x914\xdcM>\x8dI\x88\xfe\xbd\xe2\x8b7\xaf\x8f\x8e\x8d \xe4[\xe8[?\x08^\xb1p\xaa-@\x92\x7f\xf6\xdc\x9f\xde\xe2+\xda\xd9 \xbb)\xd4\x80\xd4\x84G\x8b(\xa3\xe0\xe0m\xbc_MK\x10m;I\xf5\xbb6\xe3}\xeeOo\xf3\x19v\x17.[\xc3\xe7\xfd\xeb\xd3\xc3\x17\xc7\xe7\xb7\\\x13\xdd\xd7\x1b\x03Y\xd7\xc8\x06S\xcf\xb0\xaa\x94\xcf\xc1z\xf3\xe1\xf8\xe4\xe4\xe5\xf3\xe3\xf3g\x87\xa7\xc7\x1a\xe6\xa7\xda\xce\xc4Htp#\xc6\xfe\x9aLq7\xbd\x88\xa3e\xcd\x8el\xd3\xd7\xcc\xd8\xd7\xd4OV\x81\x87I\xceZ\xb2\xe4\x80\x84W\xfa\x0eT\xbd\xaex\x0c\xd7F\x82\xa6\xb6\xee\x8d\xb2\x9c\x9a\xd8\x9e\xf2\x93\xdf{\x84\xec\x9e;,\x85\x86\x0b;\x1d\x87k\xb4\xc7\xe1\xd9Fw\\\x1aR\xdaz\xdci\xb7\xf25f\x1b\xfc\xfb\x8d\xab+\xd3\x060\x85\x9a\xa1\xddzT\x86\x01}\xc6X*g\xc7\x06\xc3Q\xbe\xc5\x00G\xea\xbb\x11L\xed\xca[ly\xa8\xad\xbd\x11BJ\xa7\xf1\x06\xc3^Il\xaa\x00a\xfenS\xf8\xe5\xccC\xeb\x01l\xb5\xaf\n\xed\xf6\x10\x94\xf7\x91\x1f6\xb7*\x1e\xc1\xe85\x1b\xf5\x8b\x07\xc7\xa3\xda\x02\x86\xadm\x01A\xe8\xbd(\xbb\x88W\x9d\xed\xba\xa5Odo\xf9.\xfc \xadhy6\x9b\xef\xa3\x0c<\xbc\x10I\xc9r\x95\xfa\xe1\x1c\xd2\x88gi\x07\x0fb\x92\x90xM\xa6\x88)t\xa4.\xfc\xf8\xc7\xe4G\x17\xd2\x85\x97\xf2\x03;\xfc\xe1O)\\\x10\x88B\xbc\xa9\xb1\xf8\x8aZpI\xae\xbb\xf0\x9c5\xe5cn:/,,\xa6E\x8b\xf8\x86x\xd3\xc7\xb4\xce\x95\x1f\x04\x90\xa4\xf4\xff\x17\x04\xbc\xc9\x84$,94o\\\xb6\x17\xff\x93>t\xbe\xe9\x11z/\x04\x9a!\xee\xb5\xeeA\xf5\xd7&\xab\x03\x12\xcf=\xa9.4\x1c\xc0d\x1c\x9eqE}\xfbq@!^F\xb6\xee8D\xbd\x87\xe7\x82\xd5z}\xe9RR\xc8^GY,\x19\x0b\xe3\x0dY\xba\xf0B\x88\xc2 \xe9\xc2\xbb\x85\x9fP\xc8\xcf\x02\x7f\x92\xc2\xd2\xbb\xa6k3\xcd\x08m\xc9c\x87Z\xd7ba\x99\xd7\x91?\xb5Q\x8f\x8ct\x0bo\xad\xe3\x86\x80\x93\xf2S\x7f\x01,?\xbc\x13}\x1ch\xf5in\xd6\\\xe3\x86Q\x99Mh\x9a\x97\xa5\xd1\x85\x1fN\xcb&\xf7\x1b\xdcA\xeb\xd3\xfd\x80d$\x98\xa8\x88E(b%cbF\xacs\xcd'\xf7\xeeQd*\xb3p,tm \x8f0?\xc3\xcc\x9b\x10\x13BEk\x12\xc7\xfe\x94\xa3\xd4,\x8e\x96\x1c\xa9\xe8\xd7\x90\xac\xc8\xc4\x9f\xf9\x13\xb40\xef\xc2q\x98d\x0c\xc3RVkI\xd2E4\x85\x10\x93\xd1N#\xbc\x01\xa6-\x06\xde\x8a\x85\xf2\xc4\x91\xf0jhjH\x1c\x97\xdd\\\x94\xb7\x82\x08\xbb\xfb\xe9\x93\x96a\xbc\xcd\xcc\xbe\xc8V!\xedn\xe3\x90q3\xa7\xf00\x11\xa5\xc8`\x1cZ%\x0d\x7f\xaaL7K(\xd9/&\xc8\x160\x8a\x8bAQ2\xceg\x02/\x19\xe9v\xe1\xa7,I\xf9\xb71\x99g\x81\x17\x17\xb6\xf4.=w\x08\xda\x86n\xde\xff\xc6\xbd\xe9 \xea:\xcf\xd7T\xa8\xe1\x8c;\xde\xc7\xfb\xa4\xf3\xf3\x98\x0e\xf60K\xa3g~8}\xeb\xf9\xb1&\x863\xc8\xac\x83G\x8f\x96P\xddf\x19\xcb\x14\xdee\xdc?.)\xff\xedh\xa3\xd0\x8b\x07\xd7Xm\x8c\x19Vxx\x8d\xd5x*\xad\xb9ch8\xf6Z\x98\x8e\xadp\xda\x95\xfe\x9a/\x02\x03{\xc5\x12\x01\xcd\xaa_;0\x1b{gt\xd2\x93\x86\x96jbQ\xcb\x0f\x9d\xd3BG\x00\x9bF\nu\x86\xd3h\xbd\x82\x01\xc4W\xe8\xe6\xd6g\xa4\xa2+(y\xbb\x13\x0c-\xf5\x9b\x16E~\xd6<\xa4w2\xf6Zr\x8f\x80\xfb\x1b\x03\x9b\x9b\x99\x80k\x95\x00\xf2\xd7\xea\x0e|\x1f\xe6V\x04\x94D\xc3*\n\xfc\xc95\xfc1A\x94\xbe$\xf8\xf3jAB\xb6\x03\xe7\x14\xbd\x8b\xadI?Ab|\xcdV\xbff8\x07\x10\x8f=\xc6\x13\xd0\x1f\x14\x19`\xa8\x1b!\x8b*\xcc\xea\xae\xf3\xba\xed\xa0\xcfCT\xf3\xaf'\xcd\xf0d\x11\xadY*\x16\x8f\xf6\xe3\xe6\x1f\xd7~[\xc3+T\x8f\xf8V\x84~a<\xef\xcbbIds\x8b\xb2\x9a\xfc\x01\x9a\xf7\xc4\x05kI\xe29\x11\x89\x97^G\xcf\xb3U@\x0fd\xf25\xb9Nlg\x08G^H\x8f]\xac\x06a\x14n\xb3f\x12$\xe0\xc4\x01\x8d\xc8\xc2r\xa7\x95.\xf5\x90\xe1k\xec\xeb]\xcc-ZXo\xe9U\xc4\xe9w\xc2\x8e{\xca\xe9'\xde\x92P\x14\x1c\xe2\xd1\xdb\xead}LA\xb4\xc2\xa8\xb3\xf4L`Vr\xa2\xea\xc4\xcb\x12nNv\x15\xa9j[\xdb\xa1G\x9c\"L\xdb\x8e\xe088\xdfMw@i\x9c\xf4p\\\xd0\xb7\x97\xe4:\x11,0gL\x0d.\xaa\xc2\x86\xb0\x15ZL\x9bL\x11e\xf6\xd2x\xee\xa1OI\xd7[\xad\x82k\xccE\xe2\xe6\xde \x89\xc1\xd1\x91>(\xd4\x1a\xbe2\xdf\x8f\n\x9b\xb8\xc2\x11%n\xae\\\x18{\x84\xe6\xd3\x1bC\x1ek\xe2G\x83t\xebf\xfbl \xf0\x87>\xd9I\xbb\xfd\xb8\xfel\xc0\x1b\x01n\x04\xea-\x87z\xdd(*\x10f=\xa7\xbb%\x16`WzR[\xd1\xe77\x06\xfd5A#h@X\xb4\x9e\x9f\xfb ~\x84F~\x9a$\xeb\xa0'\xa9U\xa4]6\x0f\xb0\xa4\xaa\xbf\xf5\x18\xf5\x06/\xad\xc6xn\x1c#\x8fY\xce/\x90Z+\xb7p|L\x1f\x1fwI\xf8sF2r\"5\xc51lc\xe95\x9fpK8 c\x9c-\x15`\xb7\x87\xd5\x859\xd90HV\xa2\xf6\x85|\xab.\xf3\xf6p\xae!m\x05d\xeb\xc8%Q\xaeT\xe3\x1a{P(\xd0\xa4*,\x88|p\x94\xf9o\xecY<%/\xc2T\xdb\xaekP\xf5Cg\x04\x83\xa6\xf6A\xd1Y6\x8b\x05\xc0%\"2\x0e\xa1\x03\xfd\x16|*&\x84\x181\xca\xe4\xdf6\x10\xc2\x0d\xa2\xaf\xc8\xb3\xb7\xe2\xda\xedj\x96c\x91\xd07&3\x0cj\xe6\x96\xf6\x850R\x0f\x0b\x93\xf9T\xe4\x172ODh\xef\xf0\x13\x85U\x80\x03\xedk\xdbiT\xe8E\xb6\x865\xf3\xd0\xb0\xaelO\x86\xcc\xf4\x1f5]\x0caI%_\x8e\xfe\xb9\xbf:\xe5]h\xd7\x16=\\\xe4\xeb)*\x050~\x9fR\xc1\xc4\x97.\xee,G\x81\x88\xa7\xdf\xad\x0d\x12o\x8c\xca\xf2\x92\xb5KH\xae\xe0\xc2\x95_\x96\x82\x88`\x8ef\xb9P\x87\xe2<\xd5\xa0'\x12\xdf\xdb+\xd9\x02\x9c8\x8e\x0b+\x9b\xb80\x17?R\xf1c\x89'\xacz-\x82\xbe\x08\xdd\xa9rS\xa2V\xb3\x1d\xd4U\xc8\x83c\x17\xed.XR\nx\xbb\xdb\xedR\x86\xb9\xaa\xdab\xcb\xe3/W\xcc\x1c\x05<\xf8\x915\xf0#\xe7$\x91\x99N\x1cy\xfe\xd3E\xa64'\x13\x8fJ\xb4\xfc\x83A\x14\x92\xffJ\xcb~ \xca\xad\x8d`p5\x80e\xd1\n5\xa9\xd3Y\x80BM\xc1\x0c#\x12j\nD\x04BM\x91p\xd8\xd3\x14\x89(\x83\xba\"\x1eWPS\x84\x91\x04u\xefE\xc8@\x8d\xd62\x8fa\xa6\xf9N\x0er\xa5\xf9\x94\x85\x052N\xcc\xf0\x15\x8f\xc8a*a\xc1\x174\xa5\xdcU\\7\x05\xe6N\xab\x98\xc3jy\xbe\xb0j:\x19\xbb\x10\x96L'C9\x9f\xeag\x10\x0e\xee>\xc9n\x00\x8a[\x13\x17\xac\xf3s\x92\xbc\x8a\xa6Y@,WA?4\xaa\x1f\xca\xd2\xcc\x0d\x1eI\xfc\xf0\xa9\xa3\x1e|\x8aUt\xce\x85\x98dh`\xef\xdeE\xab\x0b/\x1eB$\xfa\xa9\xd42Y\xad\xde(\x84\xd2\xcd\x89\xfc\x8e\x86*\xda\x94\x90\xfa\xa8\xf9\x89\xbb\x05\x14\xe0\x00b\xd0\x8dMX\xd9V\x1c\xb6\xe0\x1f\xbe(\xd5\x03be\x87v\x7f\xf7\xa1\x9a\x03\xd4\x17E{=]^QVT\xc9\x1c\x9a\xe5E\x95l\xa4^^\xb4\xaf\x16%\xdcfU=\xa8&\xcc\x0fWy;\xa3+\x82-\xed\xef1\x9e\x88\xae\xdb\xae\xa3\xb6\x1a\xf0\xf3l\xdf\xd1\xa5*]\x19\xcfg\xd4'\xa6\xe5uN\xeb\xd7\xd9D\xcdoJ\xd0^\xd4r\x07\xd2\xb9a\xba\xff\xb2{.\xf8\x02\xd7\x1d.\xe9\xea\x9c\x7fho\x88\xb8=\x172\xf5\x03\x9br\x9f\xc8v\x9d\x9f#\x13\xd6s!.*\x11\xc7a^E\xb9 \x1d\xea\\B\xc5\xa5|7\n\xdf\xc7\xc1\xd1\xc2\x0b\xe7\xa4\x95+V!\xe6\xa5^<'i\x9dCN\xd4MH\xca\xc4\x00\xb3\x80\x97\xc5\x81JE\xc5\xa3\xf1\x8b\xbeq!\xea\x06\x917=]\x91I\xab\x01GL\x0e\xebR\xa6\xf7\x10\xeb\nA\xeb}\x1c\xa0\x87\xb9\xae\xc64\xba\ni7j\xba\xf3|\x0c\x08\xb7S\xcc\x8e\xd0j\x18z\xb8\xa1\xe7\x9ax\xb3\x88\x89\xc1.\xa6\x98\xb2Mp\xc0\x14\xae\xd87\x99\xd2Y\xe0\xcdrw\x15\x935 \x85t`\x1b\x06.f\xf6>\x0eZ\x0d\\\xea;b\x82W7\x8b\x83\x0d:\xc4\xb1z\xf1\xa4~\xff\x88G\xc0\x89\xa2u\xd0]yqB\xd8\xd7\x8e)\x834\x19[Y\x1cPq\xdb_z1\n\x91\xd6Y\x1ew\xd2\xac\x9c\xa5\\\xd8\x95\x1fN\xa3\xabn\x10\xf1k~\xdcW\x93\x08#\x1f\xdc\xbfoA\xa7Rc\x11%\xa9\xe6\xf5\xcaK\x17\xe6\xeeXmJ\x98\xf8w\x0b?I\xa3\xf8\xba\xfa\x06/v\x98\xcc^-\x93un\\\xac\xb4,\x97\xc5\x1c<\xa0\x83e@KH\xec{\x81\xffK\x0e8]\x86\xde\x9b*\x1am\xb4>b\xd3\xccIz\x14\x853\x7f\x9e\xd8\x0eE\x8c\x84\xa2\xf4\xd8\xa0p\xc1I\x11I\xc7\xc4n\x86r\x899\xef^\xe7\x12Pj\x88v\xc5]\xb2\xf0B\xa7\x0d\xa5\x81<\xb5 \x99\xbe\x0c\xa7\xe4\xe3\xd0\x90\xc2\x1e8\x03$\xe1\xae1\xcb\xb1\x89FE\xe1\x0b?HI\xfc\xc5H+\x03\x7f\xe0]GYZ\xa6k\xacc\x9d\xfd [t\xae<\xd1\x0f\x02\xc9q\x8a\xb4\x90\xa1F\x14'\x14\xd8\xa6\xf8\x92\n@\xab\xfap\xdag\xe9\xa5\xd6\xf9\x88b\xae'\x9dbL;B\xdfF\xa5\xb7\xe3\xea\xa8\xf1\xbe\xcd2\x1a\x98kl\xc29g\xd5\xbc\"L\xd9\xd4\x8cYf\xa0\xb5\xc6\x992\x88T^\x10\xf4\xf3D\x9du\x8b \xd6a\\\xcau\x86f\xa5*\x11Z\xc5\xea\x8e7\x7f\xc4.q\x9a\x08\x02\xde\xa8\xd1\x1d\x1cr\xa2P\xb7\xe9\x0b\x15\xb0\x86\xe0\x9bU\x981k\x7fc\x1a\x03Hg0v1F\xc7`|e\x0bl\x10OkZ\x03z\x9ch(j\xbc\xb7o\x81D\xe2\x06\xec\x8ep\xe86g\x02\xe7\xd7\xa53\x816\x94\xf3\x1c\xe9\xb8\xd0\xf8vK\x10=C>\xe4\xf6@`Z\xce;\x9dy\xc3\x1eb\x80\xd1z\x07\xca\x0f\xbb\xfb.\x11\x13s\xe5\xb8h\x18!n\xae\x89\xf7!\xb6\xf5\xcc\x98pU<\x11\xab\xf8\x8d!i\x9fx\xd0\xc9\x8f\xae\x93\x1f\xce\xb9\x95b\x97\xffIwHVK\x1e\xbc\x9a\x9bqk\xe6\xf9\x01\x99\x1a\xda\xc4\xf3\xde\xebN\xa2\x00\x15\xf3V\x8c\xd9=!S\xdf\xff\xff<\xcf\xab\xb3\xac\x0b\xd0\x11\x80\xe1\xa7y\x9c+\x83\x0f\xa2x\x16\xb5\xf72<`\\=I\x9bb\x17f\xfa\x15TIW\xd3-+}\xa6\xccFh\"\x8eO\x9e\x9aYh\xadE:?\xdd\xfeP\x1f\xdc/5\xb6\x87\xe2\xe1\x1b'\xa50\xad'v.\xe7\xcek\xac\xa4(\x03\xb6j\x98\x03\xcb]\xd94\x054\x07e.S<\x9f\xdd6\xff\xb0\xf6\xb3E\xba\x0c^Dq\xfeQ\xd5uK<7.\x18\x87\x88\xf9\x95\xf2(f\\`\xf4\xf0\n\x86\xa2\xad\xf9;\xd6g\xd3\xdc\xfci1\xbe\xfa\xe9L\xfd\xc4\xbb\x08\xc8t\x08Y}\xc5(d<\xeb\x90\x116I\xd0\xad\xff\x8e\xaf~PO\xb0\xeb\x808uLL63{[\x08b+\xc9\xb0\xcdH\xc2\xd2\xac\xd6\x01RF\x10\xd1\xf4v\x16\x07\xdb\xfcS\xe3\x87)\xaa\x8dY\x9a\xad\x1az\xaa\x01({c\xfeFl\xa5\x02\x94Y\x1c\x98\xab\xb7Z\\\x9e#\xd1pi\xea4\xef7\xffV@\xe4\x19\xbek\xe1\x13\xf8\x93\xcbaem\xf5\x03u\xc1:\xfe\xb8\n\xa2\x984\x05;3\xa2\xc4\xd4_\xb7F\x88\x14\xb5\xd4\xfa\xcd_\xb7\xf17\xe9\xe3*\xf6V+\xf2\x85;a\x13\xd9\xbem_\x91 b\xe6\x8d\xb6\x9c\xd7\x0efA\xfc\xf9\"\x1d\x82\xb5\xd3\xab\xc1\x86+\x7f\x9a.\x9a*%\xf1d\x0831\x90\x1a6#\xa0\xfd\x9d^y\xf39\x89\xe1\xfdK\xc3\xack q\x89\x80'\xac)\xcb\xa9\xfb\x04\x13v\xb7]\x96\xd2^\x11\x8bS\xb7YN\xb3\x8b\xa5\x9f\x0eaaZ\xc1Uw\xe9\xad\xda3\x0b\x92\x04\x9et'A\x14\x8a\x898\xf4\xd3\xfa\xe3\x87q\x06f\x9an\x92\x7f\x1d\x1d\xa5W8\xf73\xc7\x95\x9a\xbe\x91\xa8R\xceCK\xdb_\xbe\xacb\x90Qojd\x18\x94\x02\x80`J~\xccxy\x7f\x15\xce\x1f_x \xd9\xdfu\xfd\x0f\xcf\xde\x9c\\\xf5\xbe\xfej\x1e\x1d\x1e\x1e\x1e\xbe>}\xbf8~??<<|\xb6K\xff&G\x87\xaf\xe8\xbf\xaf\x1e\x04\xfb\x7f\xa5?\xbe\x7f\xf1\xec\xd5\x87\xe3\xf7\xb4\xc2\xfb\xd9\xd5\xad\xfe\xeb\x05\xbf<\xbb\x1f\xf6\x9e\xcd\x16\x1f\x9f\xad~\xba>\xea}\xdc\xbd\x7f\xff\xfe\xfd\xce\xcf\xeb\xdd\xa3\xbf\xac\xfa\xcf{\x8f:\x9dY\xbast\xff\x97\xbd\xfb_\xf7\xf7\xef\xbf\xdfy\xf0\xe8\xfd\xec\xea\xf9l\xef\xe1\xfd\x9f\x1f<\xea\xbc\x8f\x07\xcf\x07'G\x97\x8f\xe8x\xfe\xfc\xdd\xc9\xe9\xbb\xe0\xd5\xe1\xf1\xf1\xe1U\xf8\xe8\xfe\xfd_v\x0e\xe7\xeb\xdd\xfb\xeb\xef_>\xbf\xaf>\xef_\x91\x9f\xfc\xfe\xe5\xe1\xe1\xe1\xf3\x87\xa7\xefO\x9e}\xf8\xf3\xfcY\xf0\xb7W/\x0e\xa3\xbf^=?|w\xf2\xf1\xe2\xbbg\x0ff\x9d\xf5\xdb\xaf\xc3\xe0\xbb\xc3\xbf\x85\xfb\x97\x83\xc9l\xe7\xf0\xd1/\xf7\xdf\xce\xde\x1c=|\xf9\xf2\xfb\xd0\xdf{\xb1\\\x1e>{\xf5\xf0\xc5\xab\xc5\xd5\xbb\xfe\x83\xc9\xa3E\xb8\xf0\xff\xf6M\xff\xe8j}\xfcM?]\xbe}\xde\xfb\xf9\xf4\xeb\x9f\xf7\xe7\xdei\xfa\xed\xfd\xcbW\xdfy\xe1\x87\xe5\xe1\x87\x93\xe7\xef\x83?\xf7\xdf\xac\xb3\xec\xdd\xcb\xd7\xd1\xfe\xe5\xa3\xde\xe9\xc7\xd9\xc3\x9f\x937\xe9\x8b\xfd\xf9\xeel\xd6\x8f\x92\xb7;o\xc2W\x93\x0f\x0f\xa6\xbb\xab_\xa6/\xdf\xa7Y?:\xdc\xfd\xd0{\xfe\xb7\xe8\xeb\xe5\xc7ep\xfc\xfd:}\xfe\xfe\xa7\x9fNw\xd2\xe5\xd7\xcb\x9f\x9fuV\xdf_?\\=\xef\x7fx;{\xf0\xd3\xdb\xe3\xde\xcb\xdd\xde\x9f\xff<\xf1\x9e]\x85\x19\xd9\x9f}\xf5\xcb\xfc\xfat/\xfd\xee\xe5\xfbG\xfbo?<\x88/\x9f\x7f\xfb\xe7\xd7\xdf|\xe8=\xffz\xf7\xc5e\xf4\xf5\xf2\xc5\xea\xf5^\xf4>\\\xfb\x0f\xbf\x8e\xc8\xe1\xe0\xfe_\xbeK\x96\xdf\xfd5\x8b.?\xf6\x12\xff\xa4\xff\xd5\xc3\xf4\x9b\xcb\xd7\xfb\xe4\xd9\xa3\xe4\x9b\xab\xbf\xac\xee__/'\xd7\xde\xdb\xfb\xef\xe2\xb7\x9d\x93\xb7\xcb\x8bW\xaf\xfc\x8f\x93\xbf|\x98\xbf;\xe9{\xef\xff\xf6h'\xfa\xea\xbbd\xfe\xdd_\x0f\xbd\xaf\xf6\x8f\xaf\xe8\xb2\x1c\x9e\xbe\xff\xf0\xe6\xe4\xeb\xbd\xa3\xef_\xbe\x1c}F\xd0\x19\xd2\xbd\xb8N\xc97Lj\xae\xd3.\n\xad\xe2\xc4N5\xf2\x18\xaai\xc6=\x8d\x84\xc34-\xaa\xe9\x1c'\x16;\xf0\xcf`\x87\xd0\x81\xd8\x81\xfb\xb0\x0b\xdb\xd2]\xe9\x8d\x0b\xa4\x9bF\xcf\xaeS\x82\xa6a\xf5\xd7f\xb9\xe9 \xb3\x10\xc4Q2\xcb\x17:*\xe6\xfc:\xee\xf3\\\x14!\xb9\x82\xa8\x92\xe4\xa7\xc6N\x03\xc7I\xa0C+\xb1q*f\xc3x{\xe6BF\xe99%\x06=\x97\x05q\x86\xa7\xd0\xc3\x0b\xe2m\xd8\x85!\xad\x120\xfb\xc5\x00\x9e\xc0\x8c\xfe\xd3\x19\xc1\xae\x83\x90\xf5\xc7iw\xb2\xf0\xe2\xa3hJ\x0eS;p\xce\xe0\xc9\x13\xe8?\x84O\x95\"\xe8@\x9f\x17\x0f\xf4\xc5\x03V\xbc\xaf/\xddq($\xc6I\xa7\x83\xe6\xfa\xf0\xf4)\xf4\xf7\xe1\x1e\x0c\xf6\xf6\xd4\xf7\x0f+\xaf\x07{{pO\x0d-5@)\x9bI\xcf\xe6\xc9\x18\x06K\xe7\xf2\xf4)\xecV;Q\x18\xb3~\xab^\xfa\xbdZ\x90\xed\x9a!\xf6\xf4)\x0cZ\x03\xc0\xd1\xa2\xb4WF\xe0Y\x1c-o\x87\xc2B\x97\xc5\x8d\x12\xe0\x8f\xb0\xc3\xc2=\x8e9>\xf782\xc36\xf8,\xc7\x83G\xff\xe9\x8c\xa0\xbf\xbf\xf3p\xc7\x81\x88\xb1\xe13\x8a\xe0\x99\x8b\xd1n\xb1\x04\x9e\x82\x07\x07\xe0\xc1\xb0x\xa7\xb2\xc0\x0c\xd2>\x1c0@\xa7c\xda\x0d\xdd?\xbc\xd1x\x8c\xc0\x19\x9c\xd1\xcd;&\x0c\xae\xf7`\x7f\x87\xbe\xb0F#\xcbq`\xc8\xb1\xc2\xcf\xd7\xcbf\xed\x0cp\x1d\x1e:\xd016\xdc\xef\x89\x96)b\xe4-\xf3\xae\x06RW\x15\xee=\xbf\x93\xfe)\xf2C\xdb\x92\xec\xb4$E\x91d\xc5\xc9 \xea\xf3\x7f)\x84\xa5\xf8\xab\x92\x9f\xdc{?L\x1f\xb2u<\x90\xff\x18\xb2\x90\x88lQ\xac\xc3gG\xcf\x8f_|\xf5\xe7\x97\x7f\xf9\xfa\x9bW\xaf\xdf\xbc\xfd\xeb\xc9\xe9\xbb\xf7\x1f\xbe\xfd\xee\xfb\xbfy\x17\x93)\x99\xcd\x17\xfeO\x97\xc12\x8cV?\xc7I\x9a\xad\xaf\xfe_\xea\xde\xb4\xc9\x91d9\x0c\xb4\xdd/k\xf6\xfe\xc2~q\xa4\x86\xdd\x99\x83\x04\n@\xdd\xa8F\xd7\xeb\xd7\xd3#55\xd3\xfdl\xaa\x1f\x9fH\x00S\xcaJ\x04\n9\x0dd\x82yTW\xcdT\xafQ\xd2R\xa2H]\xdc\x95(R\x07\x0f\x1d\xe4.IQ\xa4\xb4\x07wy\x99\xed\x9b\xf9#\xfa\x03\xfb\x17\xd6\xc2#\"32#\"\x13\xa8\xaay\xd4\xc2\xac\xbb\x00\xcf\xc88=\xdc=\xdc=\xdc\xafo\xbe\xec\xf5\x07\xbb{\xfb\x07\x87G\xc7\xed\x1d\x8b\xa7\xcbat\xa4\xc8g\xe9\xc1\x13HN\xa0\xdd\xf6\x1cqS+\xc3+b\xc18\x93Q\xd9s\xe8#O\xe7\xec\xe0\x9b\xa9z\x9e\x1d\xa4\xf4\x14\xc35\xc0O\xc0\x1e%c\x0e\xa4\x8b8z\x87\xc4\x13\xa3\xba\x15Q}\x99\xc3W\x178\x1bAO\xd0\x0b\x02\x1e\xac\xb2e\x1a\xac\x97\x98\xf0f\xaf\xaaE\xbb\xca\xef\xe7`\"\x95\xd7s\x9b.\xa6v-;\xfcN\"\xb0x\xad#\xbc\x03=\x0eq\xa3\xe4\xf1\xc8\x87\x8c0\xd3\xfeN\x8b%\xd7\xcc\xc3\xdcD\xf1s\xa4\xe0\xa1\x90\x85+.m\x90\xad@H\xff\xb4G\xb0\xeb \xc2\xd8)] Jr(\xf5\xec\x1f\x1c\xf6\xfb\x07G=\x8a\xd7\xf4 \xba\x8c#\xa6St\xdd\x1f\xf0'\x8c|\xb0\xe7\x03*\x9df\x02\xf3\xed\x88y\x18Q\xfc?\x92p>B\xc8\xa0\n9\x90\x00\x07\xbb\xf0\x08\xa2\xea\xad+>}\x99f+\xe4\xdf\x82\xb1\xd5\xb1d\x0c\xea!\x06\x1d\x0c(jY\xe7\xbaG\xbbZyC\x9eM\xd2\x8d\x897\xab\x0b\xbb\xa7\xa0\x02\x0b\xabM\xe7\xfa\x08>\x84\x80\xca\x02\x942\xa8\x12\x05\xdd\x17v\x9f\xce\xab\xe7\xe8K\xf80\x82\x04\xe7L}F\xd9r\xe7P\x85\xa3\x9f\x10\x9cb\xc3}\x18BO-\xb2\xe6E:\xf4\xb9\xa6\xea\x05K`\x04m\xa8\xe6T@\xc4B^\xbff\x14f\x01\x8f\xf8\x18:s6\x08X\xc0\xd3\xa7#\xe8\xcc\xa9\xe4\xd0\xa6;\x18\xe6t\xdb\x9d`\xf9\xc1\xfe\x01|\x88\xe1\xb2E\x03.\x88\xfa\xe6\xd0\x19\xc1\x91\xa3i\x91\"p\xa4\xb6\x14\x95[\x8a\xf3\x96\xb2\xbc\xa5l\xf3\x96(\x91`7 #\x07\xfb\xda\x87N\xf5\x06\xaa\xe1~3}5\xc2W\x8b\xcc3\x19\x9c\xc2+\xef\x15\x9da\xd8\x81\x1e\x15\xbc\x16\xf9\x9ck\xf44\xc8\xf0>\xf5\xd2Ew\x1d\xbd\xb3\x07\xec\xee[D;Z\xbe\xc8\xaa7\x17KU\xe3\xa8?,U\x15Q$\x94\xf6\x0ce\xe8\xef\xe2 \xad^\x93\xa9\xcdiBq\x9b\"6\x0b\x19\xcf\xd1\x9b\xd6\x1c\xe8\x91w\x9e\xa3\xb7o@o\xf4\xb00\xa07\xc5\xd1\xc1n\xce\xbc\xe5\xd1t\x06{\xb4\xc2\x12\xe8\xf0\xd0\xd1\xe3:\xc5\xe5\x98\x93\xd5H\xdf\x8d\x19/B\xa7\xaf\xa3y~\x85\x12\xd4\x13\xe8\xc1\xed-\xbf#\x8b\x8e\x1b,K\xc4\x13\x14\x8cq\xa7i0\x97\xce0v\xd4\xbbH\xd0-)H^y\xafl\x82>\xf2\xcc\x90\xca\xd0\xe3\x14lJ2\xf2\xc7\xbcJF\xbc\xe7tp\xb8\x0b\xb0\xae\xf92\x8ab\x1b\xbf.\xa3KZz\x87=\xf8\xe4\xd5\xc0q\x81P\\K\xa0\x8cM\x9d\xccq\xe0 \xf4\x91\xf3d\x9d\x0ee\xcb\x1f\x8e\x80\x96\xa7\x07\x82\x11\xee\x94%<\xa5\xfd9\x855\xec@\x02CXW\x10\x89n\x89\xa5CQ,\xa1E\x07\xac\xb6v\x9b\xd6\xb6\xc3j\xcb\xeb\x99\x8b1\xc9\x83(\xb5\x82Om\x82\xb5u\x18\xe6\xca\x8d\x05\xac\xb6\x11,q\xf8\xc8\xbd*E\x96\xe6\xf7F\xd0s\x9c\x13\x08hcG'(\x9f\xb5aQ\x88\xbd\x1e\xa5T\xed\x11\xcc(\xad\xdeAzA\x85\xa7:\x12\x94Qd\x0e\xe0\x96\xbe\xeb\xd3w\x83\x13\xf0\x19\xc5Q\xaa\xcf\x8a\xea\xb3\xbcz_W=\x7f\x15:0\x9b\xc2\xed\x08\xfa\x03\xba\xb1\xae*\x1c\xae\xe1P,+p\xca\xdb6\xf7\xea\x0c\xed\xdd\xc1Q\xe5\xc8[x\x85\x96\x1dk7i\xb2\xb8\x921\xd08\xdb\xc6\xdd\x9f<{\xfd\n\x1d2\xf9W\x9d\x87M\x9e\xe6fXI{S&yMW8\xccwS\xf2\n\xf9\x85\xdd@{[w\xa3\xf1\x9a\xf4\x0e\x92g\xed\xa8\x14\x0d]LPd\x87\xf6\xee\xae\xe2w\x1c\xf0GG{\x8e\xd6\xa57\xfa\xf1\xba\xf4n\xe3\xdd\xde\xa8KU\xd3(H\xf9\x185q\xbbh\xf9\x8a\xe3.\xf3\x11\xa7\xef9\x1b7\x0b\x924^g\xa5\x8eq\xa5j\x94\xcaxM\xd8\xfc\x9c\x12\x03\x161\xc1\xe0\xc3\x11\xdf\xd4(\x8a\x8bP3\xeclT\xf5\x83vN\xa0\x85>\xfaH\xf2\x92Rv\x00f\xee\x0fy\xbc\x0b\x9e\x94\xc0\x85\x16z\xce\n\xa7!\x96\x1f\xc19\xe1\xe34\x18\x85\xde\x83\xef\xb1\x84 u\xda\xf0\x88M\x15\xcb\\n\xa8g\x1e\x84\xderY7\xe4\xfa \xa1\x9f\x16\xfa\x13%]\xbe\xd4\xd2w\x83\xd3\x18l\xd84\x08\xf9L\x9c\xfb2su\xfa\xf1i\xa1\xda[\xf7X\x9ca\xa7:\xe7\xc5\xa9\xf3\xcd\xcd\x9aTN\x9e<\x80\x12\x0bV\xc5\xeeYf1\x8b\xe1\x11\xa4$\xf6.\x96E\xc0\x7f\xe5\xc2V\xd14{\xf2 \xbcb\xb7\x1a\xdb\xfa>\xbc\"\xb4\x8f\xf6\x1d\x17B\xfb\xf8\x00=\xa5\x8b\x0e\xd0\x96\x06\x1bu\xbb\xe07\xfd]\x1d\xc7 \xed\x03\xc7\xb6p\xb6\xd2(\xaez\xea\xb0\xeb\x80\xbb\xa6x\xe1\x94\x89u\x83\xe4\xa5\x98\xebM4\xc89\x85\xd2\x9eUyD\x15\xdc\x8a\xe3\x80\xa5t\xf8\xeew\xf3\xee\xe1\x9d[L\xb7U\x8d\xc9\x12\x97|k7\x9a\xde\x0dWt\xefAWtww_Y\xcb\x81\xd3\xe5w{\xbc$ .\xc3Mj\x92\xd7U\x9a\xca\xd8\x8e\xbbg\xd0\x86\xb8\xfb\xb1\x0b\x16\xabU1\"\xb2V\xd8\xe8\x0e\xa4I\xdb\x08\xa1\x9an\x9a\xeeU\xaf\x94\xf2\xa8\xef\xbd\xaa\x14\xc5p\xeb\xa0:\xbd,F\xfd~5v\xbc\xc7j\x19T\x8b'9J\xf1\xc9\xd3cj\x0b\xbd\x07C{p\xec\xd8F>-\\\xf1\xbe\xd2\xc4e \x068e\x9a,\x91\x88\xceQ\x0d}\xc8t\x9a?K\x8b\xfd<\x80\xce!e\xe9\xc9z\x19\xa4\xb6e9\x1a\xc7-\x1d\xeb!\xe3t\xaap\x9b\xf7\x8e\x0b\x87\xd0\x1aA\xc2\x82\xd5:<\xcf\x91\x9c\x1e\x91=\"\x8e\x93\xab\x89\xe8\x0b\x92%\x86\x1e\xabj\x85\x88R \xe6\x0cm/t\xces\x911We\xd3\xf3o\x9f\xd9F\x82\xee\x9cYC\xa2\xee\xfc\x84\x9e\x8b\xc0\xd7\xe4\x15\xcak^\xbbx&\xf5\xec\xbc\xd2\xb1\xdfnO\x1d\x17\xcf\xa1\xf4\xd0\x14\xdb\x0b\xa7\xebG\xa1\xef\xa5\xf6\xdc^\xa0\x02\x9a\xc2\\<\x89\xce\xf2>\xdc0\x0b\xcc\x15<\x85\x9b\x13\x07\x96\xec\x9e\xd3\xc2\xc5\xb3\xf3l|Cke\xe2\xc2xM't1^\x1b\xf4j\xd2MK\x18B\xb2\xc9\xe6\xd9\x90\xe4<\xe4\x81\x83\xd6w\\Cr(\x0elRO\xb1\xc3\x95\xbd\x19\x88\x8d\x7f\"\xb5\xda\xdf;vl\x8b\xd6n\xb9[\x88\xc65f\xb8\xc0\x8e\xa9`[Fp M7\x19E=\xf5\xda\xf9\xdc\xfe\x89A\xefv\x928\x1f\xda_xW^\xe2\xc7\xc1:\xbd\x9dy\xa9\xe7\xec\x04+u\xd4;\xe3\xcf'\xd7\x83^gr}\xf8b\xbasY-\x12\xb1:\xc7\x9f\x0f\xa7mg\xb8s\xb9RI\xdd\xd8\xeaZ.X;\xb2\xef\xb9\x19K\x12/\x0c\xd2\xe0K\xf2\x83x\xd9t\xf3@\xd8\x92\x98R5\x15\xd7~\xe8Y\xce\xd2y\xb4n\xb4\x12 k\x95\x85\xde>\x1d\xf7\xa6\x0e<\x85\x8e&'\x95\xed9\xdc\xd6\x84\x8a{\xaf\xbb\xa2\xd2\xb3\x1d9\x8e\xb0-1\x0bm\xdcMI\x922\x15\x8e\xe5]DY:\xbcXz\xe1[\x0b\x86\xe0a\xc4<\x19hB\x81M0\xa0\xc0\xe3\xdd=\xbd@\xb4\xbb\xbf\xeblc\x1e\xc6`\xf8\xdd4\xfa$zG\xe2\xe7^Bl\x0c\xd1\xda\xa6C\xa6t \x03\x96W\xe3\x9e\x1a$\xaa`\xbb!\xec\xe9\xc3:\xf4\x0f\xef\x1e\x98\x027Yy4[\xcaUE\xf7\x0e\xaa h\xf8\x04\xefU\xb98\x93\x05\xaad\x8f\x89\x02\x87U\x81\xc2\x03\xae\xfeS%\x81\x98N\xb8\x14\x93e\xc8\x05\xcarIf 8\x85\xa4+\xf2\x87\xe5\x05\xebg\x0d\xb3\x12V\xe6\x0d\x03k\xf2\xa4\x8e\xfal\x80\xaa\xc2<\x92\x93\x1b\x06<\xdfX\x1b,K-\x9a\xc9E}8\x05_\xa4\xfb\xa3\x9b\xa2\xf2\x82\xe0\xc1DS\x19\xaf\xc2\xeaa/\xc3B\x15;\x1aA\xc7\xa3\xdb\xae\xd3\xa3\xbb\xad)~\x80\x89\x9dm.!t\xfa\xdc7\x83\x07\xc1K\xb9\xa2\xb9l\xf2f\n\x90\xd89\x81v;\x84'\x10\x9f8\x10\xf0\x00\x83<\xbcv\xa8\xe6\xc6\x16s\xfa\xa0\x18\xcb9\xa5!~.Z\xed*\xc7\x11\x15\x8f\x83\x1c\xd7TdfX+\xe5\xb2\xdb\x10\x1d\xcd\x87\xac\x88\xdf\xde\xc6\xf0\xa4\xa5\x12 \xae\x86(qW\xf5\xda\x86\x94G$5\xe8m\xc4\xccUB\xd8\x95\xb4$\xef\x95.\x06h\xdbf]\xd4/`\xcc\x9d\x06NE\x07B\x18\xc2\x8c,IJ\x10R\x8ap\xd8\x8c\xa8\x02\xf5\xaa+\x99O\xfa\xb6\x13-D@1\x88\xbb\xe2\xdb\xee^\x95\xe8 \n\xaeO\x92\xb5\xbb\xaf\xcb\x92\x85\x8c\xe0\x8eC\xc8\x0bhu\x83\x04%zSx\x01:\xa5\x01c\xda\x11\xa3H:r+>\xcc]\xe5\x149>\xe5\x88hZF\xb3\xb2\xbe|\xc2\xcb\xc7v\xe8B_:\x9e\xd0w\x93e\xe0\x13\xbb&\x91\xb27N\xa76\xa5\xaaI\x193\xef\xbeR&-H\x93\xa8 0^\xefe!0)\xdfd\xdc\xd7\xe1\x14\x02J\x8dQK\xf9\xe8\x11\x84\xf0\x94\xd9\xf4R<\xd7\x88\xa6\xb6\xd8\x03\xdbv9f\xa4Z\x99_\xf3P\x98YOx\xfbt\x08<\xc5\x1eS\xda\x1e@\x1b\xbd6P\n\x0c\xf9\x03\x1c\xa0\x93\xbf\x84a\xfc\x02\x87\x91\x7f\xfar\xc8_\x0e\xa1\x83\xceXO\xa1\xe7\xb2/#\xad\xd9\xf0\x8aG\xbc`\xac#@\xd6\x11\xc3\x13\x08N\x1c\x88Xh\xb1t\x1c\xd3\x9e\xe8\xfd\x11\xa3;\xe3\xc6~u\xb76\xed\xe2A#.\x19\xe5\xb3\x94m\xb7\x94\x1dp\x1bIO3\n\x18ZJ\x0b\x15\xc4\x16M\x08\xb2`\x8d'\x93lv\xd4\xebu\xe8\xdf\xf9|>\xad\xb8\xa3\xc7\xa2Po\x97\x15\xea\xed\x1e\xcc'\x93lN\x06\xf8sN\x06\xf4\xe7\xa07\xc3\x9f\x83\x9eZ\x05\x9dd\x0b\x9b\xd9\xf5\xc7\xac\x99\x0bSs\xe8\xd85\xfe\xbc\xa1S\xe8\xc3e\x9f\x0e\xe5Jg\xe4\x00\x8b\xcf\xe6\xf3\xa9\xf3\xd5\xe0\xbd\xa52\xf0\xf2`/\xe6\xf3)\x02|sC o(\xcfk~\x9b\xe7Fw,\x16\x89A\x95Y\xb1\x999\xe9\x11\xf6g>=\x15i\xefm\xde\xe9A\xaf7\xe3\xb5\x8e\xb9G\xcd\x94\xd3\xcd[\x0bEL\xc7X\x87\xe5|XU\xff\xce\xa5^\x8e#\xd1\xd5S+\x0f\xed\xe6BX\xad\xbf\xd2\xef%\x8cx\xb6X\x1bGg\x9f\x8e\x8a\x91\xe2\xa0\xe7\xd0\x06\xdf\x05\xeb\xd2\xba\xeb\x9eH\xf9\xa9r\xe9\xb0+\xc2w\xdf\xc6\xd5s\x898\x10V\xa3\x01\x8am\xac;\xb1\xf0\xd1Z\xe3\xc7\xff\xe5\xe7~mj\xddkd\xf5\xccY\xc8JvdS.\x9c\x1f\xf13<\xe2;\x18\xb7\xc72\xdb=\x1a\xf7rC\x02U\x13\x9f\xd31\x8d\xa8F\xde\xd7Pr\x14\xff\xa2\xdc\xdf/\x1d\xb7\xdb\xc1\x14\xe9y\x00O :q\xd81\x87\n\x06\xe98\x98\xa2\xeb\x8dA\x92l:\xcf\xd4`\x83A\xcfU=s\xa3\x96g<\xb9\xf6{\x9d\xc9\xf5\xec`r=;\xeaL\xae\xe7\x07\x93\xeb9~\x99O\xb2^\x9f\x92\x82\xac\xd7?\x9cOw.kpf[zx\x1f\xe4\xb2S\x14\xdfR\xc7a\x96q\x81>\x11]\xdb\n2\xdd}\x12\x0f\x9dJ\x90\x03\xebG?g\x0d\xc1zV!\x14\xd6\x8f\xfe\x96\x1e\xfc\xb7\xf5\xe0\xbf\xa3\x07\xff\x8fz\xf0\xcf\xeb\xc1\xbfI\xc1\x9e\x02\xfe-=\xf8\xdf\xe8\xc1\xffV\x0f\xfewz\xf0\xbf\xd7\x83\xff\x1e\x05?W\xc0\xbfC\xc1\xbe\x02\xfe'\x14\\M\x91j\xfd\xe8\x0f)x\xa6\x80\x7f\x81\x82\xab D\xad\x1f\xfd}=\xf8\x17\xf5\xe0_\xd2\x83\xff\x17\n&\n\xf8\x7f\xd5\x83\x7fW\x0f\xfe==\xf8\x1fP\xf0K\x05\xfc\x0f\xf5\xe0\x7f\xa4\x07\xffc=\xf8\xf7)8P\xc0\xffA\x0f\xfe\x03=\xf8?\xea\xc1\xbfL\xc1\xaf\x14\xf0\x1fQp\xf5\n\xab\xf5\xa3\xff\x89\x82_+\xe0\xffY\x0f\xfe\xa7z\xf0?\xd3\x83\x7fE\x0f\xfeU=\xf8?Qp\xa4\x80\xff\xb3\x1e\xfc\xbf\xe9\xc1\xff\xbb\x1e\xfc\x7f\xe8\xc1\x7f\xac\x07\xff\x1a\x05\xff@\x01\xff\x0b=\xf8_\xea\xc1\xffJ\x0f\xfe\xbf(8S\xc0\xff\xb7\x1e\xfc'z\xf0\x9f\xea\xc1\xff\x9a\x82\xab d\xad\x1f\xfd\x19\x05\xdf(\xe0\xbf\xd0\x83\xff.\x05?S\xb7\xc3oS\xb8\xa7\xc2\x7f\x9d\xc2\xdf,\x14\xf8\x9fSx\xaa\xc2\x7f\x83\xc2\x93jH#\xebk=Y\xfeZO\x7f\xbf\xd6\x13\xda\xaf\x91\x88+\xe4\xed\xeb\xbf\xa3\x07\xff\xbc\x1e\x8c3\xa0\x10\xc3\xaf\x7fA\x0f\xfeE=\xf8\x1f\xe8\xc1Hh\x15\x8a\xfa\xf5\xdf\xd7\x83\x7fI\x0f\xfe\x87z0\x92 \x85,\x7f\xad\xa7\xd6_#eR\xa8\xf5\xd7\xbf\xac\x07#\x99P\xe8\xef\xd7\xffT\x0f\xfe\x15=\xf8W\xf5\xe0\x7f\xa1\x07# R\xf0\xed\xeb\x7f\xa6\x07\xffs=\xf8\xd7\xf4\xe0\x7f\xa9\x07\xe3\x9e\xfd\xab\n\xf8\xd7\xf5\xe0\xdf\xd4\x83\xff\x8d\x1e\x8c\x9b\xf3R\x01\xff\x86\x1e\xfc[z\xf0\xbf\xd5\x83\x91\xd9\xff5\x05\xfc\xdbz0\xca\x00\xca\xc6\xfc\xfaw\xf4`d\xb1\n\x07\xfb\xfaw\xf5\xe0\xdf\xd7\x83\xff@\x0f\xfeC=\x18\xd9\xb7\xc2\xd8\xbe\xfe==X\xcf4\xbf\xd6s\xc7\xaf\xffH\x0fFv\xf2\x93\n\x18\xd9\xc9\x17\n\x18\xd9\xc9_W\xc0\xff'\x05\xbfU\xc0\x7f\xac\x07#'\xf8D\x01\xff\x89\x1e\xfcgz\xf0_h\xc1\xdf\xfc-}i\xe42\xd5\x981\xd6\xd7\x7f\xaa\x07\xff\xb9\x16\xfc\xcd\xcf\xe9\xc1\x7f[\x0fF\xd2\xabH#\xdf\xfc\xbc\x1e\xfc\xf7\xf4\xe0_\xd4\x83\x91 (\"\xcd7\x7fW\x0f\xfe\x05=\xf8\x97\xf4`\xa4\xdf\x8a\x90\xf2\xcd?\xd2\x83\xff\x89\x1e\x8c\x84Z\x91/\xbe\xf9\xc7z\xf0/\xeb\xc1Hc?S\xc0\xbf\xa2\x07\xff\xaa\x1e\x8cT\xb3\x1a\x93\xc1\xfa\xe6\x9f\xeb\xc1\xbf\xa6\x07#\xa1>S\xc0\xffJ\x0f\xfeu=\xf87\xf5`\xa4\xc8\x8aT\xf0\xcd\xbf\xd6\x83\x7fC\x0f\xfe-=\x18)\xf2\x1b\x05\xfc\xef\xf4\xe0\xdf\xd6\x83\x91\xf4VC\xe4X\xdf\xfc{=\xf8w\xf4`$\xa6\x8aP\xf8\xcd\xef\xea\xc1\xbf\xaf\x07\xff\x81\x1e\xfc\x87z\xf0\x7f\xd2\x83\x91\xc6*\"\xe47\xbf\xa7\x07\xff\x07=\xf8?\xea\xc1\x7f\xa4\x07\xffg=\x18I\xef\x0f\x150\x92\xdew\n\x18I\xaf\"\xe3~\x83\xa4W\x11f\xbf\xf9c}i$\xbd?\xa3\x80\xffD\x0f\xfe3=\x18\x89\xe9\x97\n\xf8O\xf5\xe0?\xd7\x82\xbf\xc6\xd5y\xa92\x1e\x9c\xab@\xe1<\xdf\xb0\xe3\x9a\"\xb9|\x83\xc2R\xa4\xc2Q\xb0|\xac\x927\xe4\x1bI\xe1\xcab\xf2\x08a\x8ex\xdb\xab\xe9\xee\xa3Q\x945u\xdc(5\x84tL\xa6\xa5\x17\x9aT\x895J!\x83_\xc8\x81>\x1d\x89\xa2q\xcbx\xf1~\xa3\xeaKo\xde\x12zc\xbcK\x92\xf2\xe4\xdd\xdc\xf2\xc6\x9c\x92\xe4\x81\xa3}\x93\xdb]\xb2\xc2\xee\x82\x1aL\xa6x&\x9b)\x9euv\x12\xf4 \xeb\xf5:\x93\xeb\xc1|r\xbd\xebu&\xd7{\xbd\xc9\xf5\xfeEgr}\xd0\x9b\\\x1f\xd2/\x87\xf3i{\xe7\xae6j\xd1\xc9\xf0>\x9d\xf4:_N\xc7\xcf:?3\xbd\xc5\xff\xbf\x1a\xb8\xef\x11v;\xeeu\x8e\xa7\xf4+{\xc8\xbf \xf4v\xfc9\xfb\xd9\xeb\x1c\xc3t\xe7\x8e\xdd\x0f\x99g\xd8Vv\xae\xdc\x085\x99\\{\xfedr}\xd1\x9fL\xaeg\x87\x93\xc9\xf5\x9c\xfe\x87\nV:\xe1l\xc6q\xca\xd9\x9c\xe3\xa4\xb3Y\x9f\\_0\x85k\x8f+\\\x0f\xe60\x99\xa4\xf4\xf5\x8b\xc9\x84\xbe\xeb\xf5P/;\x9fO&\xe1d\x12c\xa1\xc1\x11\xfbs<\x99d\xfd\x83#Z\xa2\x7f\x84\xd6\x16Z\x11\xfb\xd3g\x7f\x06\xec\xcf.\xfb\xb3\xc7\xfe\xec\xb3?\x07\xec\xcf!\xfb\xc3\xea\xec\x1d\xb3?\x1ek\x81un\x9f\xfe\xd9\xed\xf5\xaaq\xae\x98y\xcd\x826\x0b\xecm0\x9d\xcd\xda\x96\xba\xe1P\x0b=8\xe4\xc3>\xbc\xd0[\xc9\xe8R\xd3I\x9d\xd3\x99\x9a\x1fL\x98\xb6{r\xad\xda\xba<\xad\xe9Mt\x0d-A\x95\x06\x8dU?\xeb\xfc\xcc\x84)\xdaQ\xd3\xceT\xed\x93\xeb\x191\xd9\xd7\xb60\xe4\xf9w2\xe4\xa1\x89l\xbcq\xbf\x96\x92E-\xcb\xed~\x9e\xcer\xb6\x96\x8a\xce\xeb\x8b.x\xd1-\xcd\x07\xb7&\xdb\xa9S\xb5>\xce\x8c\xd6\xc7\x85\xc1\xfa\xa8\xb5\xb5\xe2\x1d\xe8\x8d\x0c\x92\x0b\xbdA\xf2\xaad\x90\xd4\xd7G\x9f\xcd\xca\xaf\xdd\x14&\x96\xf1<\x8fs\x8f\xf3\xdf\xa6\xd3\x86\x96:\xfbt8\xbb].oW\xb71\xb9Mn\xd3\xdb+\xe28\xa7\xdc^9\x8e]\x98\xbb`}`\xa9\xf6NX+\x15}t\xfb\xc9'\xb7\x9f\xde~\xf6\xe2\xf6\xec\xf6\xcd\xedO\xbd\xa8T\x04mX\x9a*+\xfa\xb7\xdc\xa4\x7f\xe2\x8d\xa6\xe6-\x17\xf7\xfb\x87\xf6\xe9\xb0\x7f\xf6\xe6v\xf0\xea\xa3\xdb\xdd\xcf>\xba\xb5O[\xe3\xfe`w\xeaL&\xb37\x7f\xcd\xb1OG\x93\xc9\x05\x92\xf1\xf3\xa9#\xbf\x93\xa4\xb7\x83pv\xbb\x1b\xcfJ\xef\xa4\x8b\xfc\x9dg\x9d\x9fa\xef\x04.\\I\x03\xbb\x97\x8dJ0\xaf\x9b\xcd\x98\x97Y\xe48\xa8\xe6\xf4a\"\xc7a\xd5\x05\x98'@\xeb7:\xd0V;\xcc\x82l\x06_\x12vw\x9b\xe7\xc6\x9cy\xa9w\xae\xcf\x7f\xba\xf0\x92\xc5\x10o\xb6\xc5\xae\xf2p\xe5\xad\xf1\x99\x1d\xd1q\x07\x1a\x0f)\x91f\x0b+(=\xbd\xbb\\\xa6\\\xc6\x11rYU^\xe3\xf6o\xc55\x97\x0bf\x8a\xdb\x8b\xc7\xe1\x03\xed\x9d\xdd\xc4\xec\xc8\xa8\xb3%\x87\xdb\xd9\x92Y\xd6\xcc%\xf1b\x1b-\xc8\x04\x03\xb9\xe8\xa4_1\x13T\xd2U\xfd\xcaD\x18\x7f;f\x1e\xeb\xe3\xfe\xb4\xde\xb4N?\x89\x9c\x0b\x92\xf6\x81e\xed\x92\xc1\xdc\xab\x11\x13x\xca\xf0K\x82\xf2i\x19\xb8\xf0(\x12fe`\x82%\xbd\xf2\x1d\x8f-/u\x1c6\xca\xd2Z\x84\x970\xb5\x9d\xf1d\xfa\xd5\xfb\xdb\xe9\xce%\xd2\xf1\x0f\x1eYR\xb1r3\xb7\xf9}\x07\xa7\xfb\xe1)R\xf4\x89\xed\xdc\xe2\x06\xea\xb69`\xea`M\x1f\xf4\xbb\x1f\x9e2~\xf5\xc1\x9d\xe9z\xcbn\xa1\x0b\x1b%n\xc2\x03\x01o\x1e`\x18\x8d!x\x0e\x13\xfb\xb3\xd2\x8d\x9f\xcdQ'\xcf\xe5\xa6$\xbe\xccs\xb9\xed\x8c?\xefN\xdb\x1f\xect\xc95\xf1m\x8cR\x16\xe0m\xa8\xe2[\xf7\xe5\x8b\xf3\xef\x7f\xf6\xfa\xcdk\xbc\x87j\xe1\xa5\x15\x8b\xdf\xf6Kb\xdf9\xefw\x99\x03W\xd9\x15\x7f\xbb\x99hE\xcc\xd9%\x08\xb7M\xfa)\xed^gl\x9d\x9f\xfbQL:_$\xe7\xc9\xc2\x8b\xc9\xec\xfc\xdct\xa7\xe8\xae*\x05\x8dc\xff\xc6\n\x83\xe6C\xdbf\xb3&\x18\x03\xd2\x96\x85\x87\xac\xe3\xd1\xa3\xdc5\\\xa6I\xe3T\xef\xe6Y\x90\xa5\x0e\x0b\x1e\xc6c\xc6\x90;\xcf\xbe\xce\xfb\xd3:?_F3/Y\x9cSF\x7f\x9e\xc7\x94;?\xd7\x1c\xb9\x14\xbf\xf4\xf2\xf6\xdc\x16\xb5J\x93$\xa6\xa3<\x17\xc1\x1cl\xc5\x83\x0b\xa4\xb33Q\xa6\x0fJ\xde\xca<\xc4P\xbe\xdau\x99\xf4\x85\x7f-\xbf\xba\x82\xd7]N\xd9\x8dU\xe12\xfe\xa0s\xff\xe3\x9f\xce\xfc\xda\xc2i\xf9\n;\x8e0\x90\xc6\xfd\xa0\xe3\xac\xc1\xb1\xa61j\xf6\xb2X\xf9\xe6a\x16;\xa8]\xde\x89L\x18\xeb\xbb\x10\xb2\xdb\xc8\xe8\xc7')\xd7\x08\xf7\xfa&L8\xb8/uh\x12I\xc6\xd3\x07\x12B\xb42\x08\x0b\xd5\"\x89a\xebe\xe0\x93\xa6\x89\xdf\x08\xb9\xf4Bo\xccPH\xbb$-;\x14\xc1\xb6l\xba;\x8b\x04i\x1d\x8c\x1aE\xba\xebh\x8d\xa9\xda\x0bl\xc4k\x15.t:\xf9\x1c\xb9\xd0\xbb\x13\xbb\x15\x93\xf4\x974\xf8\x90\xc7\x13+T\xb6\xe3p:\xee7q\x9f\x87\x1cI\xee\x8b[\x1e\n\xa5t\xa5\x9b\xb1\x0f\xdf\x93Mw\xb2:\xad\x18q\xca\xae\xb9E\xc7\xa7\xd5n\xb7%\x0c\xe1at\xc6\xb4\xe1)^\xb3\x0f\xc7\x01\x9dm\x96\xe0~\x83}m\x1e\xed~\xe3hM\x18\x14\x8bT\xa5\x0e?P\x99n\x96\xdd\x95\xfb7\x12#3r\xb3\x1b\xa1\xa9\xb6;\xf2\xd5Q\x8clb\xb1\xac\xdb\x12\x80e\xcd\x96\x00\x17Q\xb4$^\xc8!\xa7\x94\x0d\xf0T\xae\x16\xb2\x9d\x94\xae \x93\xc8F\xf7\x90)\xb7_\x8c\xd2&\xc0\xb5\xb8$\x1b\xa8\xee\xbf\xdd.0\xd6\xf4-v\xa1f\x03\x16\xdd\xd0\xef\xbe\x101QO\xd3P\xd7\x80\x95\xbbe\x86\x1brv6\xcaoW\xf5\xef\xb7\xedv\x8f\xf6\x1c;\xb4\xf7v\x0f\x9c\xad\x8c\x90\xe63{_\x7f\x1f\xeaPw\x18\x0b\xed\xc3\x83\xc696,s^\x80q\xb3\xcc$\xd0zE\xe0!\xdd]F*\x0c\xb7\x02\xbci\xad\xbe/\xeaH\x04\xb5\xdc\xd5\xd4\x00\xfc\xaed\x84\xe1*\xc3\xda\xbe\xcb\x1f>\x8e\xc4\xf6\xc6\xe9\x14/lx\x86l\x17\nT\x85\xd0^\xfa\x94\xe0\xe4\xd3a\x14\xe0}\xe4Jp\n\xde8AQ\xdc\xa7\x82\xaa\xaf\x91\xc7\x01\xee\xa3Q<2\xdc\xa1P\xe2\xf8p\xbd\xeb\xd1\xde\xd6\xa8 \xc8l`\xa2\xf8\xfd\x928\xf4\xe8\x11\xa6*\x18\x0f\xa6\xec\xd6*\xfd\xde\x9b\xba\x0c\xd8\x9fR~\x96\xb7\xa5\x18\x8e\xa1z\x04J)Af<\xd4Ub<\xdcu\xd6\xfa\x87\xd5\xfbF\xe2:\xa1N\xe5\xd5W\xd5]\x83\xa69\x14wx<\xddd&H\x98\xf8]|e\xf8\x18\xba+`i3b=\xe5\xa3\x0d{\x0e\x96\xbc\xc1(M\x0b\x17f.\xac\xd9\xaep\xe1\xca@1\x91\xee\xca]\xbeAO\x8b\x99\x0b\x0b\x17\"\xb8\xe5w\x0c\xaf\xe8\xa6\xbc\xa9\x1fA\xcd\n\x8a\xb7\xee~\xfak\xbc\xad[]\x91\xeaA\x94Yy\xb6:\x8b\xdeC\xdel>L\x91\x8d\x85dZ\x96\xcb\xfd\x0f\xdea\xb91\xd1\xdf\xcd$\xc6\x07j\xeb\x9e\xa2\xa1>|P\xbf\xaf\xf7b\xea\xf7\xaaV4$\xd5\xbd\xc6 \x1f\x9b\x1e\xf04\xc4\x17D\xf4\xcbh\xae\xde\xd7\x04I8\n\x0d\xb5@.\x1dQF\xe7 &\xfa\x042\x16C\x9aO\xabW:\x13\x96\x11\xbd\xdd\x0e9\x06Q\xa8Z\xbd2\x0e\x10)z<\x13?\x85F1YH\xc9\xf7\x13\x8c\xcd\x8cX/\xc8\xee\x1e\xeb=\xd5\xf6zz\x83\xe8^\xbf\x8a\x12\xc8{\x95@H>\x17\x8e\xaa\x885\xe7\xf0*\".U\xb1\x00\xbdI\x84\xad\xeb\x99\x08\xa2WuOY\x94K\xc5\xdeM\xb5\xc4L.\xc18v\xb5\xc8\xd5\xfd5\xb0B>\xb9q\xe1\xd2\x85\x95\x0e\xfd)\x9a$\xdalT\x17\xf8\x84h\x9e\xbc\x83\x11\x9c\xc3),`\x08\x9e\xf6\xddk\x18\xc1E^BW\xc7\x19e\xf4\xb4\xa2wT\xacY\xc3)\xcc`\x08\xef\x1c\xfak\xa6\x16\x7fA\x8b\xd3Z\xaf\xe5\xe2\xd7\xa6\xe2\xcfD\xc5\xd7\xean~F\xf9\xb9\x8f\xd62u#\xe3&\xf5\xe5`Q\xad\xbe\xba\xd7\xcey\\\xe23\x0c\xd5\\\xb3\xbb\xf2\xf6Zgy\x85+T.\xae\x04;s\\8\xa7\x909S\xfc\x06\x9aU\x1bB\xc4\xa1\xefJ\x0f\xd4\xb1\xb5\xec\x10\x1ea\x90|=\x8dz\x0d#8Cer\x1e\xd9\xc8:?g\x89\x0eg\xe7\xe7\xa6\x0c\xd3_\xc0\x08^H\xaf\x91\xeakzj\x87\xf6\xbe/\xea\x0e\x83o)\x8e\xc3)\xa4,\x984*Vk2H\xbe\x84\x11|\x81Z\xd8\xa28\xd1\xcbD\xc6\xc9\xbe\xb4\xdf\xba\xf0R\xcc\xe3J=&n\"\x03\xb5pQm\xb5\xf6L]\xbe;3F\x95\xd3qc\xec\xb1\xfe\xd4\xb7{\xbc\xaf\xf5\x0b\xc9\xbe}\xbf\x90\xaa\x8c&;\x88`\x01o6\xb3\xd31\x99V'\x83~2\x89\xbey\xb3\x19\x06\xb5* \x94#2\xaf\x8eLq\xe0\x88\xca\xbe\x1a\x99v~\xab\x93\x1b\xde\xcf\xe2\xb3\x91D\xc4\x99i\xe8l\xc48\x7f\x9cbXs[f\xf3t\x8aM\x90\xa6&\x8c\x08m\x8acx\xac\x8fi\xac\xb8\x9ad\x06\xa9\x81\xbbE\x1d\xeb\xa5\x80\xbd^\x95\xdf\xfb*_\xa7\"\xc0@\xe5\xfe9\x8b\xfe\x1e\xd3\x15WytI\x1c\xf8\xc8K\x15G\xd5\x92$\x80a\xd7k%\x81O\xbd\xb5N\x0c\xc8\x9f\xbfB\xa5v\xb5\xc8\x8d\\\x849\xb6T\x8b\\\xcaE\xce\x88\"l\xacJ\xcfQ\x97^-r^*\x82\xca\xf4j\x91\x0bE\xee\xf9^6\x9f\xab\x1d~W\x996\xef\xa7\x02\xf2\xaeZ\xe8z\xe3@\x94g(\x17\x9c\xc25c\x0b\xaf\xe7\x1b\x07\xfe\x13\xb4:v\xe1\xda\x85\x17.<\xab\xa2~\xf2.\xc0\x08|Z\x1d\x96\xef%\x04\xde\x0d\x158p\x06\x98\xcayA[\xa3r\x9e\xd0\xdb[`\xcf_\xcf\xe7 I\x8b\xe7\xecw\xad\x00B?)\x06\x10\xbb\xc0 vy\xf4T\xf6K-\x8f\x1d\xbd\xd0w4\xb7|6\xf5\xb6\xf5\xc2\xa6\xc4=\xc0\xab\x1e\xec\x1bqtY\xbf\xb1\xb5\xa5\xda\x1a\xc2\xd7\x06\xf8Um\xef\"\xbb\x9d\xba\xd0\xd6i\x9d\xf1\xedE\xed\xdbi7\xf4V\x84\xe9/\xf1\x1b\x06jY\x91$\xf1.9\x98\xff0T\x7fc\xe8\xf4\xaa\xbeYfYR\x83\x88\xe6\xef\xcf\xf4\xef\x0bQ\xcd3\xbcvi~\xed\x0b\xe6.P\xcd\x1d&>\xb9Xf\xd3\xfa\x13\x0ch\x8d'\xbd\x96\xd0P\xa0\xb4\xfaE#\xf6 \xe9\xed\x19\xd74\x98\x9b{\x9b\xd7\xf5\x16\xe7\xc3 \xaf\xc1\xed\x08\xe6.<+\x0e\xa2\xe6\x86_b8\xc5\xd7\x88\x88\xaf\xd1T m\xe0Zy\xf0Y\xa1\xb1q\xe1\xa5az\xcf\xcd;\xba\x10\xe3\xcfD\xccJ:\xa83\x11M\xb6\xf4\xa2^v\xbc\xbb\x11\xdb\xe9\x16 3\xf5\x94\xed\xae.i\xdb\xca\x87<\xad\x0e\"\x8cA\xf5\xa5\x89\xb7\xaf v\x85\x15\x8e\xdbm2\x85\x11:\xf5\xa7\x95\xcbq\xce\xb7\xa11\xfbv\x86W;65\xa1@\xd3\xb0\x8cx\xb0\xd7\xd3i\xcc\xfa\xaa\x08\xf5@\xda\x03\x9ewO7\x89\xa8Q\x81G\x10\xa8\xf38gv[\xcd\x89\x123\xef\x19S\xa5.1m\x82M\x1c\xc9\xd2\xd4\xf2\x8d\xf4\xa8Hm\x00#X\x9e\xc0\xba\xc6\xe4\x81\xb9\xb9\xc7k\x83]\xa0e\xfb\xa8\xb1\xc0\xdc(C\xc9\xcbn\xe1lh\xe3\xa0m\xcc\xd03YG\x13i\x1b3\x96[\x88>\x96T\x0c3\x0d]\x14\xe6\x82V%Bg\"+\xea\xd8\x0f\x8dCO>+T4\xf4\xe9il\x0dO`i\x9c\x99K\xb4\xa7\x88\xf91\x98UV\xe8\xce\xb80L_\xe6\xe4\xfa$\x1fox\xae\xf0\xfc\xbb@,J\x11\x7f\x86\x90\xd9\xf4H\x8cP\x86^\x89\xc9\x8c,\x9b3\xce\xe1\x94\xf6p4b\xc7y\x8fW\xc2P\x13\xeb=7\x9b\x9cQE\xa3\xe7 \x171\xf1\xde*OT\x83\xf0\x0d2L\x94\xb2\xfd\xc2\xb7\x1d\xfdF\x16u\x14\x1f\x0dI\x88\xbf7\xa6\x89\xbf@!N\xaaU?\xf5\xefP\xba\x93\x8a\xa9\x03\xba\xa0\xfb\xe6\x1dm\xad\xdc\xc9\x80\xa7lS\xa0\x8c\xd3\xdb\x96\xd8\xf0r\xd8\xf5\x0b\xfa\xecBV{#D[\x16\xdb|'\x97}\xc7\xfc\xd0\xd9\xd4o\xc0\x12\x13\x99)\xe7?(\x82o\x99\x88P\xa6\x91\xfa\xeb\x0e{=}\x0c\xca\xbb\xfbN`\x10\xe1\xc8\x85\xe0\xce\xc7\xe2\xbd\x9e\xfe\xbe\xd0Qc\x97\xd4ZE\xcd\x11\x8b\xefnpHc\xaa\xc6\x08o`G.\x84\x1b\xdc\x0ehf\xb2\x1a\xbd\x816^=)\xc5\xa7\xcf5KR|\xfat\x1c@\x1bX\x8c\xfaqh\xf0>\xbf\xfbl\x9b\xf2\xae\xe8\x8c\x11\n\x0b]s\xe6\xf92y\x11f+\x96\xb0K\xd5R\xf0\xd7.I*\xf1[vfNT\xddEV\xca\x0c\xa4#\x15\xc2J#\xa9\xe5\xc6S\x18V\x0c\xfe.\xc46\xcb\x1b\x94\xd7\xa6\x0dO \xd5XD\xb8'\x1aMh5K\x0c\x0c!\xd0\xe3\xa4\xf7-#M}\x92\x83\x9e\xc8\xe9/c\x91\x9e\xe0f,\x0f\xbf\x86\x89a\x8cN\xf4\xe2D\xea\x15\x8d\x83v\x1b\x13\xc4o@\xc1\x9aB^7N\x84\x81\xb8\xdc\xfd\xa6\xe6\x9eAy\xdc?\xd4_B\xd4'\x0dQme<\x81X\xbf*\x82&\x06\x1b\x9a\xee.\xd7\xf6r\xa8\x8e\xc4\x85\"\xec\x84\xb2\x92\xe8D\x83\xa99\x02\xa3\x00\xca\x9e\xb7\xd0\x19$\xd3\x96ZWJ\xb5\x96(\xbci\xcb.P\x0e\xbe\xbd\x859\xfdoI\xff[\xab\xa5f\x98\xb3\xfc\x94\xb2\x8c\x1c}\x99\xae\x8d\xca0\xba\x9c\xa1r\xce-\xa3\x84\x87~)<\xbe}\xcb\xcf74\xbb\xeb\x8b\xf2\xb3m\xb1*\x90m\xdf\xb0.\"8BUS\x01\xb6\xd6^LB\x0e\xc0\xf7\xd7\xac S,I\x05\x0b\xd5P\x05\xf8Z\xaa\xd2a\xe2\xda\x8d\x0bW\x0e~\x9f1\x03\xf7\x8d\x9e/\xcd\xee\xbb\x8b6&'\"-\xac\xa0\x17\xe9\x89\x03\xb1\xc8\x8a\x12\xea{\x17\xdfy+\xeasS\xec\xe96\xa2\xce\xb6\xdc\xb4?\x0c\xb4#\xe0w\xbab\xae\xa3\xf8\xb6h\xd4\xdd\x15\x1a\xa6\xa4\x1d\xfd\xaa\xec\x16\xe9',\xc3d\x82\xc5\xf4d\xe3|\xfa>^F^\xba;\xe0\xb6w$\xe3\x95\x87\x07{\xfa\x87/\x85\x86E\xf7\xa4\x7f`|dj\xacP\xd9\xe8\x1f=_z\xab5\x99\x99K\x98\xda\xa4\xcfJ\x8db\xa6\xdc\xb1\x0e\x83*o\xea\xeb+\xe9\xeb+\xcfr\xf3G\x05^\xe8\xee\xd5\x07D\x01r\xfbGu58\xae(\x0f\xd0\x18R\x81 \x03H\x05,<(*`a\x0b\xa9\x80\xd1\xfeQ\x85q\x9bG\x05\xfcC\xe2\xbd\xcd\xfb\xd1\xea\xbb\xdbm\xc1\x88o\xc1 '\xf8\xf8\xb3\xd5\xca\xc6tW61\xf7\xc6\x1d\xd9\xec\xcf]#L\xa6fu\xe5F\xfb\xb8F\xf3Ul\xf1\xbeb\xf3\x03\xbe\xcf-6\xc3\xa5d_tr\x18\x1b#\xdd0\x9a\x9177k\x06S\xab\xc0tQx&U\xeba)\xca\xb1\x9e\xb4T\x8f\xc6\xb5\x80\xd2\x10vs\xb8\x98\xe0\x11\xaf\x1a-O>I4~\xba^\x1da\x14\x9f\xfa\xc4\xd3W\xb6+\\Q\x95\xfe\xb1\x98S\\\x8b\xb3\xfbG}'?Zn\xce\x15\xfa\x86\x03Z\x7f\xa3\x03\xdav\xb2eu\xe9P\xf7\x14\xcb \xe3U\x7fx\xa1=\x1eO\x0d\"YHE\xb2\"\x85\xbct\xc8\nq\xff\x97U1-\x9eF\x8e\xb9:\x98\xa4\x8fm\xeeU]\x19\xd2tm;\x19b\xa0<\xe5\xbfQ\xfd$\x99\xbbF\xa0W(\x11>\xc2\xdc\x92{{\xdb\x9cv\xa9\x06E\x8eD\x8e~\x0c0\xe0\xf2\xa1nu\xed\xa6\x99\xba\x9a=!\xf22uW\x1bR\x9b\xca\x92\xf7\xa2\xb1\xd2\x90\x07\x86\x84\xd0\x067\xd9\xbdA\xd5W\x92\xfbP\x0e\xaa'4\xeeC9\xa8\n]\x89^F\xe3N\x94\x8as\x06=t\xf9v\\\x81b0\x0e\xbb\x1axg\x8d\xd0\xa8\x02] 4\xab@g\x08\xad\xe6\xdf\xa3\x07#\x89 \xb2L'\x1a\xb1\x84\xee\xae+4[\xc7\xf8\xbf$\xe4\xd8}\x87\x1dJ\x82\xd2\xbb\xc8\xed\x8b\xd7\x02,\x12\x95\x8a|?\x8eVABD1J\xae\x93hyElV_V*\x8c\xc2FQ_\xc6\xceD\xa5\"\xb9\x90Q\x14\xf3\x9cB\x87\xda\xbcA\xf5\x87\xd2P\xe7c*.;\x96\xb6sM\xc69\xc4>8\x05\x9f\xa2\xba\x9a*\x93\xc7?\x10^\x12Z\xfb\x1e\xdaT\xe7\xb5\x96r\xcd\xca\xa9\xdc\xce\xe4V\xa0\xab\x07\xa7\xd3P\x85\xc6\x03AWE\xbe\xca\x86j\xea]\x0e\xca\xebo\xa8\xc2`\xfe\xafV\x91\xe3\x87\x81\x94\x80\x96MT\x92U_mGovw\x1d;\xb4\x0f\x1d\x17,\xb1&\xa6(5[\xdej\x94j\xe6S\xfc\xf0\x15\x9f\x91\xf4\xe1+\xe5\xcb\xf0@\x15\xf7\x8f\x0c\xa1\xd4\xb6\xb7D\xe4\x82\x87\xb8\xbf\xe7\xf2\xdb)B\xb5\x1e\xd6\x18E#\xaeeW\xb7>p\xa6\x91\x8e#\x9d\xba\x94\xa9Kx~\xb4\xd8\xce\x1cSX[\xd8\\\x8a\xa9\xb9B`\xba\x01\xa9\x0f_\xb57\xd0)\x0b(\xbb\xd4\xc5\xaf\xd2\xad\x86PhV\xcb3\xfewXe\x8bs\xd5\x04\xbf\xdc\xf0\n\xa1A\xc6\xc8\xf8\xe1\xd1c\x99A\x13\xdb\xc7\x95%\xcdW+\x85\x9e;\xd0\x05%\x90Z\x90L\xac\xec\xd4\x90\x07\x17\x89\xd8\x9bh \"\xb8\xc0s\xb8\x85\xe5\x03\xc92\xfd\xa3\x8dn\x83\x1bL[\xb8\xf0\xba@I,\x9d\xa7^|\x96\x86\x1a\xc0)\xa6\xc1mJ|k\xe8\xfe\xce\xf8\xf3\xeex2\x9d\xb6o'c\xfbthwN'\xb3\xb6}:\x9ct'\xb3\xb6s\xea\xdc\xdac\xeb\xf1\xd4\xb1\xe9\xb3\xd3\xd6d\xe0\x8c?\x9fL\xa6\xb7\x93I\xd7\xf9\xf0\xd4\x99\x0c\x9c\xc9\xf4\xd6>\x1d\xe1\x1b\xb7\x93\xf1d\xea\x14_o?p\x9cj^3:\xdc\x9d\xc9\xc4\x9eL\x9c\xd3\xea3\x81\xebGN\x83\x1b\x8a\xe9\xc8\x02\xc5\x0c\xed\x1d\xb0\x9b\xb8\x98N\xf6y4#\x98RV:\x98X\x16r\x14\x11\xfa,.O\x17s\xa2\x8cLGa^GLq\xab\x94C\xff\x83>f\xa2E\xe5y\xaa3A\xc9!%\x18D\x8f:\xd16\x8bH \x8a\xce\x89f\xbf\xf9\x1a\x99I\x06C\xec\xab_\x05\x90,y\"\xf8\x00W5\x84\"\xb4\xa2[\xf1\x14\x026 \n\x8c\x11x\xdf\xf3\x17\xfa\xb8\x07w\xa6\xb4{\xbb\xfa\x83\xc6\xdench\xc3\x1ab\x86\x1b\xb6\xc5\x8f\x92\xe2\x8eK\xdct\x00\xbc\xcf\x11\xad\xd4\")\x9d\xc8\xef:5}\xc35\xfc-mj\x8a\xedL\xd8\xd4\xf4,\xe8\xf0\xae~\x00\xb9X\xe0s\xcb\x07\xe5Q6)\x82\x009\xb9\x15j\xc9\xbcd\xa0\xdd\xf6\xe1 \xcck\xafg'6\x19\xfbS\xa3\xdf\xceR\x90g1\xf7\xd8\xbf5=k\xa1\xbf\x8d\xfa^\xca/s\x97\x1eh\xc5\x074\xac\xd1>\xb6F0\x87SX\xc2\x10Z-{\x0ef\x031g\xa1s\xfc\x9b\xd9k\x17\xe6\xdc\xbekKq\x13\xef\x8d\x87\x06$\xbc\xbb\x97\xc2\xae\xde'doW\xef\xbf\xa2\xca5\xd9\xa6\xc8c\xe8z\xc4\x9cD\x98G\x01\x06\xbcj\xde9w\x9e\xa7\xbc@\x9d\xc2Z,1)\x87\xa8\xaaz\x8c\xdeu\xca7\x91J\xee\xd3\xfd\xb8\x12\xb9\x0e\xee\xd3\xd9\xbd\xdd\xaa2T\xa8\x83\xf4\xa9\xb2\xf7vu\xc4\xe8S/]tW\xdeu\xd3\xb0\xcd\xc2\x98W\xb3\xf5TMA\xcb\xcb\xd5\xaa\x9d\x8aO\xde\x95\x88\x98\xc1+\x13I\xcb#\x93B4\xc9\x13\x9e'\xe8\x0d\xeeA\x1b\x12\x0c\xbc\xe62^\x1c\xd0\xf9\xdeu\\H\xee\x8f\xb6\xc2\x15V\xd1o\xe44V\xf6eb\xde(!\xb4\x01\x05\x9e>\x0c\xa1\xd3wN\xf06K\xd4\xe9\xc0\x10\xda\xed\x88%TW\x90\x85N\x13\xb1\xe9\x91\x0b\xbd\xca$Et\xa4\x9d\x86\xbb\xc7D\xdb\xdbm\xce\xc4_#\xec\x98d\x12\xf8 \xe8\xeb%\x12\xb1w\xe9\xd2\x12\xe8\xa0\x10N`\xd8\x18\xc2\xc1<\x82=\x9d\xa8\xd2\x87\x9d\xaa\"\x0b\xe3\xbbt\x0f\x8f\x0f\x0f\x8ew\xfb\xbb{G\x07\x83\xdd\xfe\xfe!\xd9\xed\x1dm;\x01\xb9\xaa\xfb\x94\xf9^1S\x01\x13\xe3\xa8\x04\x8b_;\x01{\xcc\xc2\xbeu\xe8\xfa\xf7\x1d\xf8\x10\x1d\xeeR\xb1SR:r\xfc7\x92!w\x9d\x0b%^3\xd7&\xe8\xb4\xc3\xaf\xbcW*-\xd8\xf9|\x92\xb4o'I\xfb\x83\xea)\x83Ex\x1ew\xda\xd3\xde\xf5\xb8\xd79\xf6:\xf3i\xfb\x83\x9d@\x15Vv>\xef]\x8c{}\xcdS\x9f=\x8d\xc6\xbd\xce\xa1\xe61\xe5\xe0k/N\xc8\xcb0\xddvI\xe8\x8e\x91\xa3\xbd #`\xbeqR\x95\x10\x05\xb6yc\xa1J\xd3p=\\\xe0\xbf\xd6\xc6\x91\xe6\xd7\xcfN\x8b\xef\xecJ\xb3^\xe8\x89\xd9\xc9\x9e\xdd\x10\xa2\x9b\xa1T\xea\xbd:J\x11\xe4\xae\xa5\x19e\x19\x8f\xda\x95&\xd9e\xb1r2j\x95\x00\x87,\xac6K\x14\xa3\xdd\xc4xN\xf3E\x118\x85\xb9\x9dv\x93e\xe0\x13{\x80j\xa7S\x18\xc0\x10\x8e\xe8\xa8=\xa9X\x84}\xba+r\xf7\x15uK\x03\xb7\xdb\xab\x8a\xd8\x99V \xe7\xa6\x8f\xbdf!\xc9\xcc\x01\x19\xf7a\xb2\x12\xe5W\x86iC)4\xaf\x86\xb2-\x8aGL\x8c\xa1VE\xf1\xfcc\xd3\x172.\xdaf\xf0\x04\"\xe6\xe8\xd4\xc7\xb8q\x81\xed\x8d\xb3)\xbbH\xe6\x9c\x98\xf5\xd1\xa6\xd8\xe7\xdb\xae\x84\x9eN\x18\x82\x0d\xa9\xea\x98L\x08T\x1b\xac\xa7\x86)\xe0\nd\xf2\nT\xef\x1f\x89\x83\x93\xf0\x8d\xd0\xd2\xdeV\xab$\xd5x\x18\x1b\x86\xb1\x8e\x08\xf7e\xae\xe0\x18\x96\xa2\xdfz\xb9\xbe+\xe4\xee\x9f\xe1\x98L\xb7\x8f\x99ne \xc1\xec8~*\x99/\xb9\xd3\x05\x0b\x97!\x9clx<\x18\x92|\x1a\xcd\xb2%\xb1\\\x85\xc1,32,E\x8es\\\xbcs\xbd\x8a\x82/\xc9\xec\xcc[\xad\x97\xe4\xe38Z\x9d\xf9\x0b\xb2\xf2`$=|\x1e\x13/%\x7f\xe3\xd3O^\\c1\x16J\x0d\xbf\xfe\x8d\xd5\xb2\xf2R\x10\xceI,\xfdN\xd4\x9a\xb9\xa1\x1bH\xd7Wk^\x9eh\xf0\xa9\xaf\xa4H \x90\xe7\x87\xf6\xde>=n*H\x85\x8f\x0ev\x9dM\xa3\xb1\xc8|\"\xed\x16\x13\xc9e9\x95\x1a\xcc\xc8\xdc\xcb\x96\xe9\xb0z\xab\xf4;\xea7\x81kj%\"\xf3Q\x8e\x04&\xaa\xcc\xbb'\x90L)\xf3^= \xb2\xa2\xe7d\xe5\x05\xcb-Z\xc8\x12\x12\x7f\x97\xb0\xd5\xe8\xfa\xd1j\xa3\xb6x\xbf\xceg^J:i\xb0\"\xd6\xe6-\xa2\xaf\xc5G^J\x9cn\x1a\xbd<{\xcd\xbc@m\x8d\x1dBs\xda\xc5\xcd\xb9y[\xbd\xcd+=\x9f/#/}\xe0\xaa\x830%\x97\x0f\xdea\x1eD{X#T\x88\x8fX\xe5<\xee\xb6t\x8c\xe9r\x94fQ1\xf8\x0f\xb5\xfd2\xba\xab\x07\xd0\xfaN\\\xe5\xfel#\xb0{.\xc4]\xe6`\x11\xcco\x1c\xadB\x03rC\x8b\x9a\x82H|\x02|>\x8f\xe2\x95g\x88\\EI\x827\xc6\xfc\x91\xe7\x16\xb4!\x98\xa2\x0b\x90\xf6\x12\x92\xc0K\xec]\x90|\x9c\x85\xbecGx\x82\xb2\xd1\x1ek\xfd |\x1bF\xefBxs\xb3&C\xa0\xf5\xa5\xd8\xbb\xba\xa9\xf1M\xc40\xa7J\xa9^u)\x0e\x85\x9e\xf0%\x17\x97\xb2\x9fB\x1f\x8a\x9c\x14\x94\xc9\xe7E\xc6\xfd)\x15\xde\xe4\x9f\x98\xc7\xca8{\xcaR\xe8\xe2\xc5\x81\xf0\xf9\xadY\n\xb4yw9\xfd\xd0\x17\xf1\xb0\x08\xbf\xc4\x17\x10\x8dg/\xf0\xf9\n\xba\xdel\x16\xd0\xc9\xf1\x96\xdfo(?\xc7\xf2AJV\x86\x02h\x14\xe9\x06\xa1\xbf\xccf\xe43\xe2\xcd^\x87\xcb\x1b}\xd1\xb5\\\xf4\x87q\x90\x12ZV/\xe8I\xd3\x9f9e\xdc\x99\x11\xb2^\xdePz\xb6\xfe\xeb\xe4\xc6\xc1#\xff\x07\x1f\xc4dnma\xa5\x94\xe5\x8a\x92ou7\x08g\xe4\xfa\xf5\xdc\xb6\xfe\x8aU\xc9\xcc >\xefM\x16\xa2H\xef\x7f\x1c\xb0\xe0\xb7\x91\xe4\x1a\xae\x176kb\xec\x82hc.f\xc3 \xaf\x8a\xdb6^\x1c{7*\x97\x01\xedy\x01U0\x85\xb7\xf9\xc8l\xed\xbe\xe2\xc1\x06\x14\xcc\xae\xba1\xca\x9fY\xe56\x8b\xfc\xc9E\xf5+*\xd8-\x1cX\x8c\xaf\xa6t%\xe8\xdf\xee\x8c\xacc\xe2{)\x99\xe1\x8d/\xf9Q\xccq\x0d\xd8\x05\xb6\xea\xe3w\x02\xbf\xf0\xf9\x1a\xef\xb9\xcfh\x81\x11\xa46-A\x85B\x83\xd0\x8f\x13\xcd\xb4N\xbe\x03\xb3\xcav\xe9\xd7\x8c\x06W\x90\xbe\xee\xebQ\x01\xaa\x11\x0c\x94y\xf4\x1d\x97\xc5,\xb0o\\\x8c\xb2\xb6\x82\x11\xf4O`\x05O`\xef\x04V\xed\xb6\x03\xb3\xb1U\xee\x12\xa5\x95+:\xb4K}\xb78\xd2\xcfTT6\x91i\x8e?\x0c\x19\xe0\x94\xa7\xb2 \x12v\xbdl\xde\xf5\xc2\x9b\xd7s\xd4\x92\xb1\xaf\xdd\x95\xb7.<5\x9a\xee\xe6\xb2\xf8\xf3:\x9f\x08\x18*ME!\x11M\xe1\xd7\x07lj\x9c\xdas\xfa\x94\xd2q\xd2%a\xb6\xc2\x10\x8c\x82c\xcb\xdf\x87|\xa9B\xca\x0e\x97\xc1\x97\x04\xbb\xe7\xd8\xec5g\xdc\xa3uX\xf3`IX\x8a\x8d\x08\x1d\x9b\xd0\xa5I\x17/_U\x12\xdbU\x19\xbf\x9e\x96\x89\xe1u\x13V\xfe\xd1#\xa6\xb6\x17\x00\xf4h)\xb8\x01{\x8e\x1cF\"C\x8aO\xc6{\xd7x\x04\xd9\x88\xa1\xb2K\xcb\xdf\x1aO\x8d\xb6\xe1\xa9x\xff\xa5\x86\xa7z\xf8|\x13\x86\x19m\xc90\xa3&\x86\x19\xd5\xb3\xf25c\xba\x9b\xf0\xd4\x85\\4\xe7\xa9\xfa\xb23l\x99#\xb4\xbe\xc8\x15\xd26\xfd\xb3\x9b\x9ag\x97(\x86]\xaf\x96\xfa\xc7\x94\x86]b|2\xfd\xf3s|\xbe\x8e\xc9<\xb8\xd6\x97\xb8\xc8kH\xd6\x9eo\xa8\xe6\x1d\x9b\xda0[\xe9\x9f_\xe7\x87d\x03\x03\xcfj\x188\x9a\x07\x1c\x96\xda\xfc\xc7\xc1\xc5\xb3&.\x8e\xd1Y1l\x8c\x15F\xa9wI'\xc7b\xfe\xb1\xf69\x9c\xc29\x15\xcb\x87\x16\xba\xb6;\x94A\xb8p\xc1\xf4\xf37c\xfa\xdc\xba^-\xc3\x043e\x9f\xd3B\xf8\x13o\x03^\x18\x04\x1c\x99)\xa0[\xe5\xdcD|i\xe99\xc5\x07J8\xf0\xef\xed-\\\xd2\xff\xbez\xef2\x08\x0f\\'\xff\xa0e\x18\x96\xc0e\x97\xc7\xe0\xcd\x85\xbf+\xee\x95;u+\x1cbIy\xc3R\x8dZe\xe4\x0c\xf43\x17;\x90\xe5\xa4\xa2\x953?>\xe4\x08U\xfd\xbe\xf8h\xf8\xd3\x8c\xb6>\xdb\xbau\xc1V\xb6n]L\x03/9u\x01%\x9c\xa2\ns\xab\xe7^\x9a\xc6C\xb81T\xee\xc2\x95\x1e\x1b)e?3\xb8XB\xc1\x8a4\xabb\xdfsY\xce6\x9a\x15\x17\xce\x0c\xebb\xdfsa\xb6j\x9f\x97R\nm nk\xd3\x12\x01\x9f\xfa\x17zq\xbbA\x9c~F\xc5ii\xcf\xd0\x9d\xb8\x14\x1b\xf0\x85Y:\xa5}{Q\xb9jh?ct\xa3\xf5b\xfcL\x12\xbcooa-?(Dn*\x8c\x1b\xa6\xab\xd4\x0e}\x8b\x11\x89\xfc\xab\xe8!\xff\xdd\xa58\x1b\\di\xed\xb2\x89\xcf\x15\x8f.YF\x05\xac\x0b\xa54\xda\xd9\xfc\x971\x05K\xf5\xf3\x85\xe8_-\xd3\xae~\xde\x8a\xb78F\x99)\xbd\xf8\xdc\x8c\xf3Q\x0br\xf8l\x9a\xb3,\x14\x9b\xbe\xa0#\xf8\x82>\x91\x80\xcb\xf13<\xf7\xe0\xdf\xf2\xa3\xb7\x14\xfe\x96\x0214f\x82sQ\xbf0\xb5\xa9^\xe4O\xb9\xb3#P;\xef\xca\xce\xe9\xf2\x0cV\x84A1\x00\xbbT\x86\xc1Mv\x19\xe9s\xc5\xe3f\xa6lt\xcd/\x94\xd1\xe3%\xa5\x14|\xa7 \x19\xf5\xa3\xd0\xf7R\n\x1fJt\xf5e\xc3\xb4\xd5\x91Fq\x98\xe4\x0d5\x11\xea\xb2\xb49\x04\xebYx\x93.\x82\xf0\x12|/\x84\x0b\x02\x0b\x12\x13\x83T@;\xedo\xca\x11\xaa\x0d%\xa6s+%r\x0f\xc8g6\xa0\x91|\xe6\xae\xcb\xf8\xbf\xe4\xae\xb1\x12h\xc63&\x94\x17\xf5\x1d]\xd4w\xecT\x96\xb0\x80kl\x85o\xe0\x14\xc6\xfa\xbe\x1b\xfb\xfd\xde\x85kZ\xd1u\xb5\xeb\xef\xb5v\x90\xa5\xd9\x17\x81\xca;\xeci\x19K\xd1\x08Z\xd2s\x05\x82n8vX\xb5:\x01\x1aJ\xfc\xa5\x17{\xb4\xc1!\xb44\xd7\x1b\x83pF\xc2t\x08\xd6$\xad\xdc\xae\xab\x9a\xcb\x00o1\xd4X\xa5h\x7f\xa2\xa2?\xcb&\x13W\xa5<\xc7\xa9\x06\xab\\\x0d\x87\x96<\x05\xf6\xabn1PxK\xec\x0f\x9c\xeeY\x1a\x13O#\xfe\xa3N\x8c~\xb1\xa4\x15\x83\x8a\xf5Jo\xf5\x04\x919\x80\xd24\xcd\xc9\x01=\x05\xd0\xa5\x11\xc7\x1e0\xd1!\xbf\x92k\xb3\xf7\x9c\xee\x17Q\x10\xda\xe8KgYU\xdb\x9a\xf8$\x94\x8c\x19\x84oC4\x08\x1b\xbdD\xd3\xb1\x142\xe0-\xb9I\xec\xd4\x19\xf7\xa6SdyI\xf7\x9c,\xc9\xaa0\xdbr\x80\xa0\xdc\x91\x9bC\x02?\xcaB*\xfd\x84\x12\x0c1\x89\x0d\xab\x0c\xa3-{20%q\x9c\xadS\xcc\x00'\xc0\xfa\x19\xf3\x99\xd3\xbe.4\x14\xf0S2\x957\x95\x87\xf9z\xad\xcd:\xde\xf24l-\x02\"y\xab\xf5m\xa8~r3g\x1b\x1e\x8f\xac\xc7\xd0f\x0epmxl=6\xbe\xf8\x1e\xbd\xa6\xc7dj\x14,7 \x93\xe2z2\xc7\x08%\x94\xad\xf8\xe0\xa5\\\x81B\xfa\xbb\xb9Pv\xc6\x18\xd1\xca\x0c\xf7\x1a\xc4'\xe9\"\xcd\xa48\xb6\xb6\xf9\x0f\x0cty\xee\xcf\xbc\x14\x95RK6\x9d\xb6\xf5\xa45~\xfe\xd1\xb37\xcf\xc6\xf4\xc0)J8\xb9\xe3\xde\xced:\x99>\xdd\xb9t\xc1\x9aN\xa7\xd3\xa7y\xf1\xa7xx\xb5\xa6\xd3\xa7\x16V\xcdW\x13Q\xdf\xe7\xa1k\x96\xd2=\xaed\xc3\xf8\xc5\xf2G\xbb\xb7N\xc1\xc2\x01!T\xd9YpJ1\x90\x0f\x19\x86\xa2\x0b9\x15\x816\xf4\xf1r\x81\xbdd\x89\xb5]T%\xb5zyo\xd1\x13\xd3,T\xbc\xc77no\xa5\xc1\xd5\x8865\x0b%L\xea\xc6w\xf3\xfe$\x9a\xee\x189\xb3~F)E\x19B\xa4\xdf\xd49}\x18\xd2U\xd3\x16\xc9\xc5\xfdd\x08s\x83F.\nS\xe4l\x06e\x13#aC\x08M\x9d@\xca5\x04\xaf\xeey\xd5e\x15\x94\xa9xo\xe0#^\x1d\x1f)\x11\xf2\xc2HL$\x97&\x8a\xcf\xba\x08\xf1\x82 \x12\x89\xcc2\x0f|\x0c\x9fK\xa7$\xbf\x9d`\xa6\x9a\x81\xd14\xce\xd3X*\x95\xd5\xed\x1d\xe1$W\xbc\x94,\x82yZ\x0d\xa8#\x7f*\xc6=\xadKX\xb5|d\x07N\xb3\xc2\x8c~p\xf25gp\xf1\xd1K\xe9z([\n;F\xed\xf5)\xce;\xe3yB\xa1f\xf3\x94\x0b\xa7`=\xd9\xa1T\x8d\xffn\x83\xf5\xd4\x92Kq\x06\xfa\xe8\x11\xb4BZz\x12\xf2\xc7\xe8W\x8c\x17\xc9t\x1b\xcf\xbc\x8aQ\xa3\xd9\xa3\xd5\x92\xf1\x04\x9dr\x8b\xdf]o\xbd&\xe1\x8c\x8a\x0d\xae\x8cO]\x06\x0cJ@\x11\x1d\xccn\xf5\x1c\x17Z\xbdMH\x04]4\x8e\xc9\xf9\xac\x95\xe7K\x9a.i\xa2\x8a\xdd/,\x07\xa7`\x01++=CI\xca\x02\xcb)\xde\x8dq\x85D\xf5|\xfaqo\x08\xd8\x8eiM\xc4\x02\x97\x96\xa5\x15W\xb7\xa4xC.\xa8\"#\xae\x0c\xde\xbd3]\x87\x82\x1a\xa7;-\xcd\xd0\xd0\x0bD\x1a\xf4H6\xa8_9\x0d\x0b\xd5\xb52Q\x16\xf41\xc5\x08\x00\xdd\x04eh8e\x99Px\xaax\xb3\xb5\xc3\xb2\xcc\"\x9c\x89\xcc\x0bW\x00>\xa3\xfc|,A\"\xda\xac\xf894\xb6\xb1\xe0q\xe4\xcd[ef\xe6\xfe\x0b\x863\xe4:}\x13\xf8o\x99\x13J\xba\xe5N\xbc\xaa\x95\x0f+\xc4\x0e\xf5\x1e\xf6\x1c\xda#\x96\x8c\x12\xf2\xd8\xab(\xc9 \xb7\xc79\xe7\xd7V{\xa2\xd0\xb2\x89\x08\xe3\xc1\xd2L\x1agv\xa3g\x94\xf8\xf8]\xb2\nR\xdb\xa2\xd2\x99\xa5\xb5\x9c\x8a\x0f\x15P\xd8\xfaoHT\xeb\xe6\xf1\xa6v\x1e=\xfb\x8a'\xa0[\xbb\x98\"\x91\xb2\xbd\x9e\xa3\x0f\xed\\\xd3\xca\xa5q\xf8\xccf\xdf0\xcb\xe9\xb75\xcb)\x95\xf58\x88\x843\x0b\x7f\xc6\xc4\x9by\x17x\x00\xa7\x04H<\xf7\x97QB\x0c\x91\xee@\x7fl\x00\xc3rT!\xc2M\xa0y\x1c\x0b5=$p\x94\x08\xbb\x92j\x02q\x1b\x8f\xee2\xd4\xc5s\xae\xbe\xe6+\x12'\xa8\xd3\xb0\xfa\xdd\x9ea\xd7\x93\xd0\x8ff\xe8\xe1\x19w\xc5wFr)\xbd\xfa^\x8a\xd9\xd4%K\xb2b*\x85\x02\xf6\"\x87\xd5b\x9f\xd8\x87\xfa\xe1\xa2\xc2a\x08\x99\xcd\xb4\x81E\xecD\xbc\xc8\xc5\x82\x15\xe6\xbe\x06&%\x0c=\x0dm\xe2\xf5 \xc2\x9a\xcb\xf2@\xa2L\xe5@\xba\x88\xa3wH\xc61(\xacm\x85Q\n^\x92\x04\x97!\x99A\x1a\x81\x07,\x14uK'?\x88\xcf\x95\x94\xaa\xbb\xde\xdePdG\x96\x143\xe6\x8a=[\xea-'\xaa\xa1[\xaa\x81\xa9\x80\xdaT\xc0\x10\x94V\x0e\xbc\xdfD\xdb\x08\xaf\xdc\xd6\xc9\x8a\xe2c\xa2R\x86#\x1f\xa5y\x9b.\x89\xc4p\xd9\xee\xa1Ccv<\x91\x01\x9a\xca\xb9\xe2 \xed\xe9\xc6$S\x9dW!$\x96\x91=\xffU\x8a\x1a\xba\xbbg\x88\x18*\x0fG\xb0\xf3\xf2\x00\xadG\xd6\x10\xacG\xdej}R!\x8a\x8f\xad\xc7\xf4\xc9\xcffQZ}d=f/\xad\xa3Dy\xf4\x04\x1f-\xd5w\x9e\xe2\x83\xcb\xf4\xa4\xa0\xa3\xd2\xb0\xb7\xbal\xc5\x89\x17\xa7lH\xbcru\x8f=~d=y\xfax\xea\xec\\\xd6LF\xa5\xc2pL\xaaI\xb4`\xb8m(\x8a\xd2%\xba\x93\xd2\xbc\xf3[\x11\xfd}\xa7\xfb\xe2\x8a\x84\xe9\x8bU\x90\xa6$\xd6)\xf9\xd5\x83t\xccc\xa1.\x02\xe5Z>\xfd\x84\xf6\xee\xbec\x07.&\xd3\x0d\xba\x9f\x15\x14\x93\xb6x\x80\xc0\x1f\xc6A\x9a\x03\xf7\xf6\x8f\x11\xf8Q\xb6^\x92k\x06:\xe8!\xe8M\xec\x85\xc9<\x8aW\x1c\xdaG\xe8\xf7\xbd$y\xb3\x88\xa3\xecr\xc1\xe1\x03\x843\x9d8;\xd8\x05r\xc2\x8f\x00\x9d\xc1j'\xffJ\xca#o\xd2\x9c\x07\xfa\xd3h\x8a\x06a\x1c\x0e\xbb0\xc5X\x0dZ\x89\xe9\x1b\x18\x1bh\xede \x91\xbe*\xc7&}\x93\x91\x96\n\x85\x05\x1f\xc2\x1ac\x92d\xab\xd2\xf7\xdaSY\xd8\x8d\xc2\\$\x0b\xd0\x81\x0e\x01\xb1\x17\x84\x96\x0b\x11B\xce\x83\xe4,\x9d\x05\x11\x957\xe4\x81\x11$*\xb7\xb7`\xb3j\xa8\x18\xe7\x82\x87\x02\x11\xfd\xcd\xc46\x17\x92\xaa\x16\xef\x8a\x874k\xf5M\xf3\xebi\x07\x9bac\x19\xe7\xb8)\xa3c\x9b\xcd^\xb2A\x85\x86{\xe03\x92\xa4qt\xc366\xff\xb1i\xb3\xbe\x9en\xa3\xaf\x90\xed\xb8\xdcN\x1cw\x97A\x92\x92\x90\xc4\xcf)\x1f\xc2\xfd\xe4\x82E(3\xb5\x1c\xc1_\xab\xf4V\xdf\xe2\xdc\x88&\xab\xe8\x8a|\xc2\xdb\xa9\xac\xb9\xf2PZ\x7f\xf5Uy\x9d\xab\xcf\x8a5\xd7\xbe\x89#\xa2\xc2\x92\xaeU\xf9\xa9\xa9\xd5ym\xabsm\xbd\xc5\xd3\x9a\x9d \xc8-\xc3\xe4R?\xab\x10\x19\xdb\xe7\n\xb6\xcf\xf3w\xca\x10v\x94\xa1\x04\xc8b^\xceM4\xdca\x8ec5d]\x7f\xab\xaf\xa0\xeaG=\xa7\xcb\xc2\xe3\x96\x19\x9e0\x1e6\x86\xc8\xa9\xa2R\x8ee\xa9\x16\xcbZ\xcd\\\x0d\x84\x00i\xa7 %\x19#\x8e,E\xbe\xb9Y\x13.I>\xf7B*LR6\x03\x1e\xf8K/I\xc0K\xc0\xcb[\xd2\x1c\x0b\xdf\xf3\x0d\x94\xcb>\x0b\xe2\xcd\x80E\xa3\xe1\x90\xd4\x0b\x96e\x08?\x0e\x8c\xaa^\xcb:$I\xd5\x8c\xe6\xf5r\x9a\x10m\xf5\xf3A\xb7\xa21S~H\xaeS\xa6\x8eR\xc7\xa9\x8af\xf2P\x9eb\xc0\x92|\xb8\xa8\xf5\xc1\xdb\xc0\xc3\xd2\xac\x90\xf2\x94\x10\x17\xdam\xa9\x9a\xf2l\xb8\xa5\xb1g!\xea\xbe\xbf\xfd\xe1\xe7\xfd\xddd\x0ex\xec\x0ci&\xd0\x11\\\x1ec\x051\xb6\x19\xb32b\x13}\xe7\xe2xQk\xddy5\x15'\x1a\xda\xa3.\x9d\x91Z\xbf\xc3\xbe2\xc4\xd3\xd2\x80\xaa8^Y\xf2\xa2%:\xbd.t:RU\xda\x98\x85u3\x82\xb1\x0e\x9bf\xa4\xaew\x0d;\xb0\xdc\xda\x17Q\x106\"\x1c\x9b\xffQu\xfe\xc5E\x0f\x8d\x17s)\xean\xdeY\xe6Zl1m<\xae\nO\xcdM\xe7\xed\xc4\x81\x10\xda#4\x81\x13\xc3\x9a \xaeR;\x7f\xe8{u\xcf1\xc5]o\xb9\x8c|\xbbg\xf0cV0\xa6\xd0\xf57\xa0]13xj\x0eXl\x08\xde\xde\x0f\xc2\xc4\x9b\x13;\x85\xa7O\x9f\xa2v2+O\x9fG\x97\xf3\x04\xb2\x13\x07'.\xc36\xd8\xacF\xfc\xe2\x04^\xde\x8e\xd67,\xb0\x01}\xa5-\n\x96\xa2\x18dl\xd2MS\x1c)S\x9c\x03\xdeSI\x0b\x03s\x06\xdd L\xd6\xc4OK?\xba~\x96\xa4\xd1\x8a\x91\x89\\9\x93/\xd0\xb8ZpZ\x87\xecb7\xe7/i\xd4jlXC0\x92\x1c}\xb8\x1e,.\x05z\xcfMo\xec\xe2h1^\xe3\x89{c\x7f$\x1d\xfb.sw\xbd\xddF+\x90\x88\x0fS\x1cu\x13\x92\xbe\\\xad\xc8,\xf0\xcc\x1e\xae\xdc>\xc3|\x8cx\xcab5&\xb3\xfc\xf1k\xaej\x007\xdb\x98L3\xc0M7iw\x16\xf9\xa8(3\x97[\x97\x12B~_ \xc9k\xcc*\xa7}`\xcc\xa7N\xab\xc2\x8clk:'o\x82\x15\x89\xb2\x14NaM\xc9\xb5[D\x8c\xe7yk\xa6\xccq\xfa\xab\xf7\xdd4bW\xdb\xf9\xe9[$\xb6aQ\x8b\x9a\xe8\x88\xf8Hf\xa0Z\xca-\x7ff\xb6&\xaa\xaf\xf8\x98\xf4[0\x94Q\xa7\xae \xb4\xa1v\xd7Q\x92~\xca\xb3\xf9\xb3\xac?\xc1\x8an\xc93?\x0e\xd6\xa9\xd1\xddG|\x04\x11\xd79\x08V?x\xcc\xefF\xe1\x8a5Woh\xcf\x85\xbf\xbc|\x13\xd3\xab~\x88\xde\x84 \x7f\x18o(f\xc0\xb6,\x17\xac\x0f-~\xa8(\x1a\x0e\xab\xa1\x94K\xb5\xe8W\xc2vP!\xc5\xab~\xbe\xf0\xc2\x90,\xe1\x14l\x1b\xa3\xa7\x90wP~\xe4t\xe9\xbc\xf7\xf5\x03\xaeE\xae\x99\x9d\"\x057\xa9<\xb7\xc0\xd3\x08;1(M\x8a\x01\x0bQ5\x86\xc6E+\nc\xe2\xcdn\x92\xd4K\x89\xbf\xf0\xc2K\x82i\x92\x97\xa3\xddvD\xbe\x8b\xe2\x0e.Z\x06\x0d\x97\xbd@r\xfb\xaa\xdf\x85\x94\x1f_x\xfe[\xe3qV|\xbc\xf82\xd1\xf9\xdb\x89\x8f\xe1\xae=\x14l\xc8\x1f'S\xa6\xdf\x8e\xed\xc4q!i\xb7M\x08\xb7fG4y\xed\x16J\xd9:\x1f\x82\x85y\x89Yzw\xf0\xab\x81\x9b\xa1\xa1\xca\x1a\x1f\x15T\x8e::\"\xa1\x9f\x94\x86\xbb;\x02[h\x17\xeb}\xf4\x1a}\x9e\xe7\xdc\xf5\xa6\xaeL}\x9a@\xf1im\xb8{\xe4O~:\xed\n4k\x16p\xc4'\xc6\xf7(\xd6\xd5\xf7^|\xf2\x14P\x0d\xba\x0b\xdd\x07\xfd\xae{f\xdf[\xdd\x87\xd4\xf9O\xea>\x0d^\xda\xd5\x0f\xf6\xa9\xbfm\x9f\xe2qo\x93\xbbU\xf2\xe7.\xfd\x1a\xdc\xa5_.\xc4\xe3\xfe\x8f\xa3w\xbbw\xef\x1d\xfd\x7f\xf0-\xf7\xb1\xd1\xd5[\xf7A{\xfd\x12U\x0e\x1aw\x0f\xddG/Q\x97J\x98\x84\xa3\xbc\x00\xcc\x83\xd0[.7\xa1\x0f\xccp?\xdf\xe0\xbc`|\xba\xa9\xdfoE\xb7g[Y\xc8\x02\x02\xcedY(!\xcby\x11\xa9?\x0fN\xbc\x08\x12\x0c\x83=\xc4\x02\x92\x0d\xb8\x949\x14y\xb1\xd9\x15`\xf3[Q9\xfb0\x90M3\xf1E\xdd\x03\xe9.#\xdf[\x9e\xa5Q\xec]\x12)\xa2\xa3:)r\xfeTm\x855\xef*\x10aQ.\xb7\xaf\xe5GBa\xc8sn\xa07\x99\x95\xc6\x19a\x87\x7f\x1e\xd2.t\xbai\xf4I\xf4\x8e\xc4\xcf=\x8d\x01Y\xfe\xb5q\xf0R\x10wal+\x8c>\xe2A\x88\xd0\xc0b\x8a\xbd\x0d\x92\xb1\xa9\x1a\x15\x13\x8a\xb14\x9eapm\xb4ai\xe5\x12\xa1m\xa1\x85\xa8\xd2\xb5\xaa\xef\x91\xee\x1e\x81\xf8\xd0*b\xcf'\xa5*\xe0\x14\xfc(L\xa2%\xe9\xe2C\x16\xc0F\x80\xdeyq\x88g%\x1c\xa4\x1aD\x0f\x8c;-W\x170R\x93\xa2I\xaap\xc4j\xda\x87\xc6\xad\xb4\xd1\x1e\xd2+\xe2J\x19\x96\n\xb0\xe4\x06r\xac\xcb\xa3\x14\xda\xfb}\xed\xad\xcfH\xdd\x1e\xdc\xb6G\xe9\x82d\xde\x8b\n\x1c\xa2+\x15\xa9\x01\xc9\x0bG\x12MpS\xac\xb8\x1b\x84\x0b\x12\x07\xd8yt,q%\x98\x1d1'\x93H\xd2\xab\x9f\xa7\x92\xcbH\xddd\x01\xa2\x06\xb7DT\xdb\xde\xc2\xb3\x86.\xcf\xe1F\xcbS~k\xd0\xbf\xc3K\xfd\xfe\x81S8\xc5\xdc\xf1}\xc9}f\x93\x1a\x9a\xec\xcd\xfdc}\x16\xc4\xfe\xb1>\xcf\xcd\xdeAs\xac\xf6\xeaBqK\x04\x0bH-\xc7P\xd2\xeb\xcc\xb3\"zU\x8c\x97R\xd1*g\x13)\x8a5\xe6\xd6\xcb\n\xebWau\xe8z\xc9M\xe8\xf3\xe4\xadYw\x1d\x07\xab \x0d\xae\x08\x9c\xe6.0pZn\x02\x87u\xbc\xef`6\x0c\x1e\x03\xca\xd6\x948pl\x82w\xe5*\xcf\xa4zi\xb1C\x07S\x0e\xc8\xc0\xfd^\x9f\x01\xe9\xd7\x01V\x93w\x15\xfd~\xec\xfd\xde.\x82\xd6,!\xa7\x00\xee!p\x16$\xeb(\x07\xf6\xd1f\xd3]y\xd7\xcf.sX_\xc0\x04\x80\xbd\x19\x939\xba\xa7\x90X\xc0\x0f\xe8\x8e\xa3\x88\x92m\xb9k\x9a\x10i\xef@\x17\xb9\x1du>\xdeE\xa2\xa2\x12>\x99/#9\x97\xf5f\xe8\xc4\xd1$H^y\xafl\x8c\xfb\xcf\xd2x \x96\xa40\x82W\x18\xc3\x153H\x0d\xd8\x9e\x92\x07\xc6\xcb\xc9l\xfd\xe4\xe8\x02\xd9]\xb1 v\x89\x0b~y\x81\x03L\x9dBe\x1f\xbb\xc8?_&\xb9\x8eDv\x04\xb9\xd1\xb8\x83\xbf^\xd3\xc6\x13x\x8c\xa5\x1f\x83\x17\xce\xe01/\xfe\x18|\xe6\xe2sA K\xd0]\xfc\x92\xa4\x0b\x12W\xb5\xe5|\x19\xcbazr\xd1\xc8:?\x17\xd1\x19\xce\xcf-\x16\xaf>\xec\xce\xa3\x18\x9dp \x0cYf)\xcf.B\xe3\x93\xfc[X\x0c#\xe24\x9f]\x0c\xcbh\xd5 s\xd7\n\xa8\x8c\xd1(A\x87c\x82q]R\x1e\xa8\xddW\xee\x13\xb1T\xce\xe7\xe7\xeb8\x9a\x07K\x12\x9f\x9f\x03\x8f\x14^@0$\xa6\xdf\xcd\xd63/%/\xc2+\xbcJ\x9d\x87\x9fx\x90\xbd\xd3\x88\x93\xbb\xba\\\xbcBU+\x89Y\x17A8S\xb1TS\x90.\x95\x8a\xb6r\xe2\xff\xd2\xc3\xa4x(y[\xf1u\x7f\x99\xbc\x08\xb3\x15\x89\xbd\x8b%i\xa2\x07\x9b%j\xd0\xde\x84\xa2\x934g7\xd3\n\xbc\x1f\x18\xe27\xacK\xa5vk\x0ew\xc5n\n\xec\x90\xa58\xf3\xf9q\xdf\xb3)\xae\xa1Ux\xdeM\xa28\xb5\xb5\x04v\x8d\xa9W\x11\xf9\xd7\xb8\xdc\xc3\"\xfbL\x83\xc6}>N\xa7\xc8\xcf\x99\xc4\xed\xd2\x01\xca\x93e<\x88\xf1\xde'\xecE\x96R\xf8T\xd4\xe3\xbb\xb0t!\x1c\xa7S\x17R\x91gD{\xa3\xdctX}\x10\\\xde;\xacRR!\x81\xea\xf3E\x1c\xe9\xd3E\xec\x1d\xf5\x9d\xee\x8a\xa4\x8bh\x96\xe8(\xed\x9e\xf2\x1eg\xd6\xc7\xba\x04\xd3\x9a\xbd\x80g\xc2r\xc9\xf9\xa6\xbbfYl\x0cff,?\x96\x1c\x14J\x89\x1d\x94\xf0\x9d\x0b\x94\x81\xa3J\xcc\x80\x19B\xc9*hL\xdd\xa5?H\xa1o\xb7\x0bW.\xdc\xb8p\xe9\xc2\xca\x85s\x17.\\x\xe7\xc2\xb5\x0bg.\xbcp\xe1\x99\x0b\xaf]\xf8\xc2\x85\xb7.\x86\xb1Z\xe2\xe9KO\xf0\xaf\x98T\xdc\xe2\x020%\xe5\x9cw\xe7\xbai\xc6\xabS\x89\x9eK25\xc5\xfb3\xcct*\x831\xb8\xd3\x08\xce\xba\x97$e\xd1\x87\xcf\xba \xfd\xba\xc2\xaf\xcc\xac\xe1b\x94\xce3f>q\xdcB+\xd3\x8dI\x12-\xafH\xcc\x82\xcc\xbe\xe5\x9c%\x87\xd2=\xfd\x05\x8f\xbc\x144\x04a\xe1\xfc\x97\xfbU\xe5\x04D\xa5\x1e\x94\x1fcp3\xb4\xd6\xbf\xb5#\xa7\xe8\xd2\x88\xf1\xe8\x1b\n\xa4Et\\\xf2%]\xad\xfc\x1c\xfe\x82\x16\xcb\xb8W\xf2%I-\xdc\xb4\x11\xf3\xc5s\\x\xa9\x8dhO\xfb\xc0\xd2\xf2a\x94\xe4\xc2\xfbp\x9e\x93\x13v\x86\x8f\xc6\xbd)\xeaQ\xaap\xd1\xe7\x11\xcb}c\xd6\x08iF&D\x8b\xd8\xb6\x9e\x07\xb1\x9f-\xbd\x18\x82\xf0*\xe2\xaa\x1c\x17\xac\xe7/?{\xfe\x83O\x9e}v\xfe\xf2\xd5O\xbd~\xfe\xec\xcd\xcb\xd7\xafLVwZ\xeb\xa5\xad\x89_\xfe\xbe\x08i]3\x8d\x0f\xd4\x13\xbe\x1a/\x99=2p\xe1\x99\xbc.\x89X\x17n\xc1\xa7bH\x99|\xbap\xe5\xe4y\x07\xe9\xfe\xa8\xd5\xb6\xe1\xe1Y\xbf\xaa\x86\xa1\xb2{\x02\xb5h#\xae\x12\xe4\xa8[\xe0\x90\xc1\xa5\x10\x8dm\xba\xa0\xc9\xa7\n\xbe\x14\n3\x18V\x90\xccqMh\x9ew\xfa\x81\x17\x89\xf9\x03\xa0\xbf\xb0f\x99\xf2\xfb\xe3\xb8VD\xcdu.\xa7\xfa\x7fXR \xdf\xefD\x8e\xc7\xf5\xc4\xb8\x0b\x8d\xd3\x14\xd4.kP\xa6\x06\xba\xcc]\xb8M\xefK\x0dj:\xf7\xc0\xcb7\x0e\xe8\x1e\x0b\xb5\x8b\x17\x88u\xa3\xe2\x97\xe2\xae\x9bi-\xffQ\x1c\\\x06\xa1\xb7\xd4Z\xfb\x85\xb0>\x84/\xd4\x87\\\xd2\x7f\x85\x91\x83\x90\xdb\x8b\x9fj\xd9K\x92nr\x0d\x94\x0f\xf2m.\xe7\xbd\xb5S\x07\xb9\xdc)\xdc\xb0@\x0f\x1c)R\xba\x18*\xd5S[^x\xc9\x16-\x1b\xd6Q\xe3\xda\xa3i\x8a\xf1\xdbMZ3\x900`\xfd\xd5\xf7\x00\xe7\x04\xfd{W\xccM\nF\xf0\x12EU\xee\xbe\xc0~\xbc\x96\xd1\x82=\xb1P\x9a%\xba Q\xea PL\xd8 #\x8fP\xac\xbc\xd4\x0f\x03\xcf\x83\xe7\xf4\xc8'\x89Fn\xde1l\xc5\xdatb\xa3R2\x9f\x9aK9B\x9dC7\x7f\xae\x0ey\x81F\x0f\xccI&\x83\x9f\xe5`>K\x85\x1b\x95\xfdZD\xf1X\x94T\xfa\xfa\xb8\x15j\x7f\xe9\x18\x870S\x1f\xe4g\xe1\x0d&8e\x92-\xdf\x9ej\xb3\xd5\xed}\xa1\x8aj\xe6{,n9\x87\x8e\xba\x86l\x0b\x86\xb8\x05\xc3\xb2\x8cFP\x92 \x99\x8c\x96q)\xb3j7\xde\x92\xa7\xe7\x8an^\x1bg~\xe5*\xa1iki\xc8G\xc1T\x18\x17\xc9[\xa8\xa6=w1\n}P\xefF\x8cH\xdf8w\xbc\x1b\xc5\xd09\xcf\x1d\n~'Mk\xcaW\x8dNhA\xddB\xd6Y\xba\xa3U\xbd\xcb\xf5\xb7\xd6\xcf\xac\xbb\xf0\x121\xf7\xda\xee\x16XP\xd3q\x8e\x18\xb4\xaeT\x93pum\x7f\xa1\x0b\x8c*\xeb\xbe\x86\x10a\xd8*#\x89\x8d\xec\x0b\xcdSN\xbb\";\x13\xa7\x1d\xb5\x15\xe4D\x91\xfdN\xf7\x0cyEd_\xab}\xcer\xc8\x83\x9c\xf0\xfb\xc7\xba\xfc}\xf4\xe4\xaf?\xe1\x0ft'|\xd4Kv}o\x9df19K=\xff\xed\x9b\xd8\xf3%\xb6B\xe48\x1d\x8d\xf6\xa8\x90;#2u\xa7.\xf7\x98\x07\xe5\xfc\x1fj\x89\xa4\xa2c\xd2\x9e\x85#;\xe1\xa1\xb6<\xc6\xd4x4R\x91\xb8\x1f\xed1\x89\xc8\x14\xc9n\xe1F\xa2l\xd8\xf5\xa3\x19\x8a\xddxO\x87\"\x1a-CJ\x02\xcf=\xd6hs\xa3\x02\xe3\xc0\\I\xc1\xe2\x84ln[`\xb1l\x88\xad\x8f\x882\x8f\xa2!X\xb1\xf7\xa5U\xa5Qj\xd9\x0b\x8a\xf1\xd6\xec\x9d\xb7A\xd94\xfe\xf2f\x08\x16\xfdS\x0d-\xecb\x80\x9a\x08s\xb7]x1\xcb\xe1\x16\x7fy\x83\xb4\x81ve\xf6\xce\xc3\xf7\x1eXo\xbbgH\x8d\xaaU\xdc\xa2\x11g\xe5]o\xa0\xd41\x18\x08\x8a[8\x91\xe2o\xeb\xc2\xa0\"w\xa3\xa3n*+:Q\x1a-yhk5\x8df\x17\x9et\x1cS\xf9\x9d\x8cc\x8d\xabi\xa3\xbfN\xc8\x02\x15\xd0}\xdd\xe8{\xc1\x04\xfe\xfe d\xf0\x04\x92\x13h\xb73v\x7f\xad\xd8\xa0\xd9\xd4\xc5\x80\xb7yh\xa2jv\x82J\x1c\xb407\x8bh1\xfd\xdb0\x1c\x1e\xee3\xc3\xa1\xa4ag\xa6\xc3\xc3\x83o\xdbt\xa8_D>V9\xae\xac\x95\xdb\xd4-\x8c\xb4X^\x87\xdaE\xd5;`=\xb0>Y\xe1\x1eA\xd9d\xd1\xb4\x9d\xaa\x1d\x17\xe6f\x8c\x84\x9b\xaf\x0d;\x9em\xebzr\xa7\xbek(&oB\x1fR\x9d]A\x1b*Ks\xc7\x81\xe3\xb0\x1f=\x82`,\xec\x12\x98\xbe\xa1\xf5 f\xd6*\xfe\x1f3\xfc\xe7w\xe5J\x17nS/\x08\xf9n8\xea\xddc7\x88\xd9\x96\xc9\xfc\x96{\xa5\x8e\xd7\xc5E_1\xe7\x88\x08\x17\"\xa06r/\x91\x9d\xbb\xfal\x1eE\xd6\xc3\x18\xda\xc50\x95\xa9\xe4wa\xee\x8a\x0d\x95#b\xc9\xb6\\NDy\xdf\xceW\xee\x92\xba\"\x18\xbb\xc6\x04\xb4\xd4[E\xd7\x1b[r\x16\x9bZrf\xf5\x96\x9c+\x83%\xa7\xd2\xdc\xcd\xa6\x06\x9fK\x9dE\xb5\xac4)\xbf\xb0\xd2\x12\x0c?\n\xe7\xc1e\x86\xb6W=\xd1 \xb9mV\x1f\xf5Z\x04I\xaa#+j\x9akJ\xa2\xe2&a\x05\x84\xc0b<\xb3-\xd1\xa5\xe1RF=\xeb\xfc\x9c\x10t\x1b8\x95b\xcb!\x8c\x1e\xe5(h\xd5\xc5\xbc\xe70\x82\x99P\xc8\\U\xdeva\xe5\xb8RA^,\x1c\xa7S8\xd5\xc5[\xe7O\xe8\x1f\x16\xac\x0d=O\x11:\x821\xb3\xa5\x92i\x01\xe2\x91:\xca3V\x11\xf5B\x9f\x0c\x91\xd0o6K\xae\x1c\x0eL|J\x13\x15\x88\x88|\xcan\x0d7\xb9\x9f\xc8\x8d\xd4\x01{\x03\xaf\x91 \x97\x8df\x8fX\x8c\xadCg\xf7u\xe8\xe7\xf1|\xce\xcf7\x9c\x8a\xf9|\x88\xa2\xef\xa63\xc1i\x84^\xcd\xcd&\xa3\xa5G\x9bR,\x05\xfd\xfb-\xbb\x82X\xce8\x9dn\xf0\x9e\x8a6,\xb6(}[\x9d1\x10\x92w\xc4n\xbe\xd1\xc5\x8b\xc7\xd1\x94\x8a\xb0\x91\x03A\x11\x927\xd0\xcd+{J\xe5\xe4\x81\x88K%4\xfa\x1c\x05\xe3q\xc4]\xe40ie\xdcM\xd6x\xeb1r\xa1\xaf\xbb\xb7\x87\x96\xb4\xb8h6\xaem\x96kc\xc3:\xcf\xf8\xa6eg\n\xc4\xac\xf1~\xe2U\x1e\xd1\xa2v\xdd\x0dt\x82r\xe3\xa0\xbc\xa0\xe6\x15\xd1\xafc}\x1cx\\\xc5Pc#c\xb6!9\xd5\n\xbb\xebH\xd8\x89\x85\xc0\x13\x08\xe9r\x13\x07\xa21\xa1\x0f\xcb\x17\x1dI\xcd%8l4\xc0\xe0\x15\xec2+\xaf\xb7w\x82\x847\xa0/\xb3\xaa\xf9.\x8e\x0bC\x8e\xb6RnJ\x15\xb7\xc9\xaac\xa9\x9b\x80Mnl-\n\xe2\xb2\x08\x92\x86{F\x0d\xf7\x8a6\xb9\x89Un\xaf\"\xaf\xdc\xbf\xf5\x86\x9bVu\xad\xbb%\xdd\xd1\xfd\xfa\xb2\xd1\x8d\xaa\xbf\x14\xfc\xa4\x9fue\x16L\x98\xf7\x1d\xfd\xaf\xf7\xba@\xcch$\xb1\xab:O\xc6K\xe7vP\x85S\xc62\xb7#GGx\xe6\xb6\xec\x0b\xcd\xbc\x08o\xec\xaf\xde3]\x9c,\x1d\xd7_\xa1\x16\xaeb\xccU\x02\xad.3\xdbgq\x88\xf3C#\xadTn\x8c\x08\x9f%:\xa3\xdf\x81\xfb\n\xcc\xdc\xd5\xa9\xea\xd3_\xa3W\xd5\x88\xcd^\x9e\x9b\xb0\x12\x99\xb8h\xaf>p\x80D\xf7+i\xb05\xdeG\xd2\x0b\xe8,d\xa7\xe3\x10-\xcf\xf4o\x19%\x1c\x91\xf4\xce+\x19\xa5\xd5\xeb\xfb\xef\xdd\xedN5\xa8\xf6B}\xd7\x86iy\"~(\xce\x14\xcb\x8aC\xa5\xae\x8b ,\xc5]\xb9\xefQ\x88\xadS\xffX\xa3\x1d(%\x94\xbb\xe3\xa1.`\x9a\x8d\x94\x8a\x07\x0f\xd4\xed\x8d\xce\xd1B\xb3\xcc\x04S6\x92y\x1cUrq\xd5\x9d\xb6Y\xe8v\x14\xddq\x0d\xc7\xa8Gv\x99\x8ax\xea\xb8\xf0\xbd(Z\x12/\xb4Q\x94!E\xb8e,\xc0LA\xe8\x15\xfd\x10c\x96\xf4\xbcG\x07N7HI\xec\xa5\x91>\x90\xe3\xb1\xde}|O\xb9\xcd\xc5\xf6\xe8\xa0\xba\xa3=\xfd\xd6M\xf4\xead_\xbf\xff\xe7\xbc\xcdj\xe5\xcb*^mt\xacV\x0f\xcb\x8b\x878\x8cj\x9e\xcb\x87Q\xf5)\x1e\xe64\xf1\x17\xdf\x1bO\xf2\xe5\xa3\xfa\xb6\x9b\xa8\x10K\x8d\x1e\x94\x8d\xa6\xa4\x17\xb5\xa6$\x0c\xb2T(\xe6\x13\xa6\x98\xf7\xed3\xa4A\x9e}\xc6\x83#\x02\x8f\x16\x8eh\x8e\x0bG!\x11\x0b\xf6\xec\xe4q\xf2\xca\x95\x1bb1\xe0 \xe8\xcc$\xee\xa1S!\xde\xa0\xe1\xbb\x93y{\xda\x97P\xc4\xe9\xa7$\x85a\x11\xbf\xb9\xcdo\xeb\xd1\xf3\xb9}S\x928\xfa\x0e&+\x1bA\x8a\x17\xd1o\x0c\xd2\x10;\xd5\xd1V\x1b\xa4\xf0r\xed\xa5N\x95B\x8c\\R\xb1&t\xe0\x86\xf9\xf2\xa5Z\x07J\xf1\xe1#5$\x0cU\xa0*\xe4\x06\xb3\x05~\xc7\\\x08\xe7|\xa9\x98\x91A\xb5M\xd8\xef\xb0\xbb\xf1\xd48\x178\x0f\xe7\xe8\xe5\xfa\x8e_Ge~4\x94`\x8a\xf9\xa1\x07\xe4\x0b\x18\xc19\x06\x16\xb3\x8b\xc9i]tgQHN\x1c\xb4\xbf\x9f\xc1\xa9\x10\xe2\x983\xf0\x05\xd3\x98p7\xf6\xfc\x17\xe5\xdf\xf6\"\xd7\xa6\\\xbb0\xb3opg,\xf0\xae\x15\x9f\xe6\xebj\xa3\xed\xb6!a\x16]9Mv\xa0\xc2\xdbs^\x83\x0d8\x03\xf2\xda\xebF\x8f\xe3uQoW\xc1\x89k\x8e\x10\xbfz7\xa4\x82]#\x05\xbb*\xc7\x92\x1c\xa9\xb6\xc0\xa2\xd8vx0\xdb:\x9bt\xd5\xd8\x0c| f\x8c\x07\xd8\xb3\xa2\xfbn\x8d\xccW\x89\xb0\x1b3\n8\x1b\xa7,\xcb\x1f\xcb\x9e<=q\xa0\xdd\x8e\xb5\xd4\x0b\x8b\x8e\x80\x17\x9d\x8a\x9c\xab\xf6\x9a\xa9]\xac\xef~\x17\x03\xab\xb9\xe0u/\x13.:\xd5\x1fI\x0bo V\x13\xd3\xb5\x10\x17<&.\xe2\x93~\xf5\xb4Zry\x97\x83\xd8F\xb52/J\xa4J\xc4\x08}y\xfa\xf9\xf9\x8c\xb00\x94A\x14\x9e\x9f\x0f\xc1\xc3\xd0\xa2D\xe7\xccw\x1ez+R\x94\xb9\xb2\xab\x0e\xd0\xef\xcb\xea\x91\xb9\x1dT\x9b\x9cG1}\xbd\x1e\xcb\xf8\xa0\x17\xcc\x0e\x86\x7f\x86\xec\xcf\x08\x02;'\xe8\x8aR\xa4\xf4\xfb-\xb9\xf9x\x93\xc6\x0c\x8e\xe3\xb8\xf9\x08\x04!$(\xd3.\xcc:\xfc\xc5\x98L\x99\xa7s\xce\xc1Hm\xd7\x16^\xf2\x92c\x89\x98\xcb\x98YA\xa4'\xcc\x9f\xcf\x92 J\xaa\xf4 y\x8e\xaa\xaa\xb3\xb5H\xf6R\xa9N-\xc0kU\x1f\xa8\x95s6V\xad\x92\x83EE\xfc\xa7\xf2\xfa\x8a\x92\xc3\xca\xbb\x08\xe3/\xe2w\xe5-\x9e\x13\xa9\xf2\x9e\xc8\x9a\xc4\xde\xe4\xbf\x94w\x13\xe2\xc5J\x93\x0c\xc8\xdfd?\xd4\x17\xd7\xc4\x0fHR}\x93A\xc5\xab\xec\x97\xe6\xdde\x90*o.\x834\x7fo\x19\xa4\xca[\x92\x08PyWz\xc2k\x90 \x9azrAA\xa9'\x7f\x92\xd7\x93C\x94z\xb20\xf1\xa35E\x83\xea,HOx=\x12\xa4\xe4E\x82$F\xa2J\xd5\x9d/\x119\xdaFU{.\xba'\xda\xaf\xb5 \xcb\xba_A\x95*;\xae\xd2\xb1\xc0\xdc1\xb9\xe5MZ\x15\xe4\xdb\xc6\xec\xedL\xef\xd1\xad\x90Qh\x83\xe5(\x0e\xa1\xa5\xdfx\xa4x=\xdf\xb4\xd5\xa4\x92M\x0b\xd4Q.\xcb\xa3\x0cddr\x9b\xa6U\\>\xe1\xed\xe8\xb5\xa3\\\xee\xae\xe4\x86\xc7\xe0\x189\xc6\xd9r\xa7\xf4\xbd\xca\x11\x11{\xe5[\xae\x98S\x8b\xbd\x105\xbf\x10\x94\xe2\xf0\x97\x04f}\x15\xe5\x99\xd0UQH\xe5\xf7\x89\xa5%\xe9g\x8f{[G1b!\xcfP\xdf\xa0\x93\x1cR\x8c\xea\x9f\xcb\x0d\xfac\x90\xd8\x1c\xc52\xdc}4\x9b\xf5:?\n\xb1\xab>Z4\xb9\xbd\xa5\xcf\xe54\x05\xac\xecY^\x16#\x98V\xb3\x18\x9e\xf2\x8b{\xb4\x1d~'\x8ecj\x87\x87\xfe\xb0\xa3b\xd1=\\\xf4\x80\xa2=\xf3\x93\xc5X&\xe3\x1e\xf7q\xc7\x07\xf4E\x17\xbcq\x9f\x03\xbf\xc5\xae\xe7}\xefO\xc7\x11\xe2xvr\xaf~;\xae\xa8\x8c-\xe0\x1d\xf0\x97k8\xb5\x99\x16\xd5\xa1n\x17\x1b\x83\x07\x8f\xa9\xc1\xe4\xac\x1e\x93=\xee^^\x8f\xebyn>c)\x1f\xd9\xc1\x06{\x81\x0b[\x19\xc5.\xf3f\xa0\xaf`\x1a\xc0q\xb2 =\x8d$,\xdd\x9c\x9eJ\xd2\x7f\x86\xe8\xe0\x8d#\x89\x9e\xd6\x93R\x9f!J\xc6\xe24\xb1\xbe\xf6\xa7\xe3\x00\x91.\xba\x03a}\x90\x9e\xe5\x17q\xf3\xce\xd0\xf7\x85\xdf~\xe0\"B\xd3g%\xd0 \xb4\xb0\x18\xb7\x7f?z\x04\xbe n\x0e2\\\xbf\xbb\x8e\xd6\xb6\xe3\xb2E\xe1\xbf\x9c\x0dj\xdeb\xbbH\xd7\x016\xd9'\x9b\x86_\xe1r\x8a,\x97\xa8\xd5\x7fG\xff\xeb\x1eRY\xc5\xf0\x7f\xcco'\xb2\x90\xb4]\x0ci\xc7\x83:\xdf\xe7B\xe2VB\x9c\xdc\xf66G9\xb4w\xa7\xf6W\xef\x91P\xa6\xf6+\xef\x15\xbb\x83\x98\x16I\x1e\xe0\xe1fk\x03\xa9\xbf5z\x18=XYt\xbe\xe3\xb4n)\x1bW\x89\xe4C\x88\xc5\x12\xb9 .:\xc2\x19\xbc\xe0\xca\xc2[PHi\xe18\xd8h\xd7\x95\x85\xac\xa6\xe0\xa1,_6K\xac\xe3B\xc8~\xb5\xdb\xa9\xf3\xed\xf0BIc\x85\xf9\xa3\x90\xf1\xb7p\xa0\xec\x0c_&Va\xe9\xb7\x86*<\x0c\xd1\xd1\xc8+\xdf\x02\xbdy\xc8S\xa0^\xc9\xa0G\xf5\xd0(\x8a\x9a\xe48\xcd|hJF\xf7\n\xc7\x15\xcd\xe09\x82\xb8\x10\xa1\x7f\x01ECM\xd8\xe4\x0dh\xe1F\x18\xce\x8e\xb9L\xcag\x83\xa5d\xc9G5\x00\xe1\xc7\xbb;\xe3<;C\xf9x\x86j\x16M\x136#\x9e\xcb\xf3~\xf3S\x1aC\xfel\x0b\xe4\xe7\xbdi\xd5\xf6\xa6\xe1\xc8@\xe4\xe6=U\x90\xf54\"\xb2W\x16\x91\x93\xb2\x88\x9c\xe4\"\xb2W\xfc\xd2\x88\xc8j\xcd\xc6\x9er\x89\x98\xae\xd4\x86\xd3s\x0f\x96e&\xe4p\xc7\xed\xe5\xcaD\\\xed\xeaw\xf4\xbf\x1e\x86\x07j\xef;\x85v\xff\xb8\n\x8f8\xfcH\x7f\xbfM $..\xcfT\xef\xe0$\xa6\x8bo\xe5b\xdb\x05\x0870mL\x15\xc1\x93\x184\\x\xe7J\xd3\xa5\x0bk\x17\xfd+\xe7\xdcAQ\xa5/u\x0f\xaf\xd0\xba!\xc2\xce\xa9\xcfo\xf0\xb9\x08\xc1X\xc6\xe8\xe2=\xf4\x08\xaf\x97\xe5\x84\xa4QD\x17\xd6\xe2V\x8c\x91\xa1DJ\x07\xbcVj\xd4\xd4\xebC\xad\x80\x88\xd7\x1737\xbb$\x17\x9f{.t\xfa\x945\\\xf1\xcb'\xcb<&\xc2\x9a6\xab\xda\x9c6rX\x8eli\x02\xe1\xaa\xc6o\xf9}e\xfa\xa2P\x04\xe9m\x9e\xbb\xda\xdb\xed\xda\xfb\x93\x90\xbb\xbbI\x11\n\xb4s&;\xee\x8d`\xbc\xc0\x88\x15\xa1p\xe2c\xd4=t\x98\x0d\x0e\xa7V#\xbd\x89O\xcc\x18\x12\xdd\x95KF'\xd6LZ^b\x96|\xe1\x92\xdf\xe0D#>(\x7f\x98\xe9\xa8.R\xec\x8c'4@~=c\xc17\x8a\x80\xc8\xb8\xb7X4\xd8\x88\xf1+\x1e\xcb8\xc6T\nQ\x98\x92\xeb\x14\xf30\xc5\x97\x89\x93\xfbo\xc6,yD\xc00%*P\x88\xae\x89)Et#id\x99\xbe\xf9\xdej\x8a\xc2q\xc5\xeeEr\x9fp\xe3\xa6\x08\xe9\xd0\xd3rV-\x1e\xfeCT\x0f\xa9\x19a\x84\xfc\xccD\x8a\xb4\x1b\xcc\xcc\x9a?\x1e \x13jS\xf9\xd3\x82\x9c\xdd\xd1\xdaXO\x16\xe3\xa4\x08\xda\xcb~\x04\x85MF\xe9>\xbf3\x86X\xa1\xf4\x8a\xffX\xe2\x8f\x9cq\xc5\xdb\xf5e\x81\x0eZZ\x94\xc6\x1b 6-\xc0\x88\x8e\xc3\xa9\x0es*^8\x90u\xe9\xcf\x0dD\xa1\xc4\x9esa\x85\x8b\x14Z \xa5qJ\x12{\xad\xe3\x0fj\xefs\x1a\xc2\xa8\xa2\xe8\xaf\xf9x\xa6\xbd`\x9b\xe1M\xfb\x0d6\xc5g$\x8d\x03rE\n\x8a3\x8b\x08#D\xc1j\xbd$T(\x12h(\x90\xf8\xb1\x96*\x89\x0fk\xda\x9e\xbb\xa0\x1bqe|9\xb5\xff\xafq\x9c\xe5\xcdj\x1aoM\xdf\xf8\xfb\x0f\xd6\xbd\xbc?\xdb\xf5P\xac\x08\xe6n\xe0oh\xd1\xb1\x04)\x04\xaf\xaa\x8a\x81\x85\xca3q\x1a\x93\x8a\x01\xf9`\xbb\xad\x0f\xeaW\xe3\xe7D\x19\xc0R\xfb\x12\x88\x03\xfe\xa64I\x7f\x8e\xc7\xc1\xe8\xe9\x8e\xbeM\xcf\x8e\x1c\x93\x8c\x1f\xe1\\cVF\x9ct\x84x\xb3\x03I\x1elH\xf2\x7f\xd5\xefa\xe9\"\x1asj*\xee\x84y\xccO\xb1\xd5\xe9x\xe2\xe4R:\xac\xb4z\x98\x9fP{]L\xc3\xbf.I\xfa\x19G\xd0\x1f\xd38z\xc5 <\x16LV\xb3\xfd\xef\xa7\xd4\x92\xd2\x0f\xe96X\xe8B%DsXD\xecm\xf1\x88\xbd\x04\x86\"\xa5b#s@\xaf\xb2\xee\xf3\xb33\xba\x1c\xf8\xa5K\x12\xdf[\x17\xfaT\x19\xa8N\x95`,\xcd,H\xc4dP2z\x19\xbc\xd8\xfef\xd1\xec\xdf\x84\x98\xfcl\x16\xc4$\x01\xaf\x08}g\xf4X*\xc5\xbb\x96\x82L\xf1\x10La\x9ea\x81\x12\xcfN\x9f\x1d\x83)ya\xa2t)[\xc2 \xb4\xdb\x01<\x81\xf8\xc4\xc1\x19\xe6\xf9{\xe4B\x01\xde{\x8c\xa0Mg\xff\xe9\x08\xfa(\x05S\x01d\xb7\x8ftgp\x08\"\x03!N@\xc0\n<\x1d\xc1\xdeQ^v\xff\x10\xcb\xd6=\x7f\xf4\x08\xf6\xf6i\x81\x8c\x12\xc6\xc9\x04\x83F\x15\x96\x89\xfe\x01Zr\x80\x12K\x1b\xfb\x1a\xb0*[\xfdJ\xd8\x01\x82uup\xc4\x1f\x88\x0e\x1e\x17_\xf5=D\xe8\xc1~\x0e=\xee\xe5\xd0\xe3\xc3\x1c\xda\x1f\x0c\xf02(\xce\x13\xce\x11\xa5\xe0\xac\xcbe \xce\x9b\xf5\xff\xfe\xc5\x9fY\xb5\xfbPuz\xd78Q\xc8\x18\x8b\x1a\x18\xf6\x0dO\xdan \x91Y\x8a\xcfJt\xe5r\xec\xeeX\xd6\x1b\xbew\xf2\xdb:\xa1\xdd\xef\xdf'\xb0\xa76p=\xad\xd8:?'\xc9\xa7\xd1,[\x12\xabJ\xb5y\x9a 9\x8d\x82\xc3T=\x98K\xaf\xceQ\xc5x}9I\xbd\x94|\x7f\x99]\x06a24l\xdadM|\xd33\xfa\xf1\xb0\xcdd\x08\x99Y\xc8O\xc8\x92\xf8i\x14'C0\x04c\xd2\xbf\xcbR/\x19\xbb\x068\xb6Y\xe6\x13Zs\"\xa6\xc2\xdc\x8f\xbc\xaf\xd1F}\xf5\xf4}U\xf1\xf0;\xfa_\xefU\xf9mn\x87\xf6~\xffX\x89\x90\xcd\xed\x0c:\xbb\x84o\xd3'{J\xa0e\xfeh\x7f\xaf_}\xe4\xe5\x8f\x06J\x90i\xd1\x87\xbd]\xc79\xf9N\xfeL\xe0\x0e\xf8z\xc5O\xca\x98C\x81\x9f\x05s8\xa9\xa0)\xe3\x06_U6\xa7|+G\xa3\x10\x93b\xe6\x05!=\xb65\x1c\xac\x0bC\x1d\xa7eEF$\x93\x19\xbc\xd8(i\xd9\x8fC\x9d\x84\xb9\xd1\xbdB\x99\x07\x1e\xb4X'a\xb1\x1c\x97\xd5 \x93\xdfQ\xbf\xd1q/\x95[B\x97$\xfd$\xf2\xbd\xe5s\xdc\x04\x9b\xc5\xfa\xb3{\x18\x8c\xd8\x8b\x13\xf2\xd3\xde\x8a\xbf\xea\xd8\xb1\x18\xfcv^\x0erC2]|\xdc\xe9t&a\x16/\x87`-\xd2t\x9d\x0cwv\xd6$M\xd2(&\xdd\xe4\x9dwyI\xe2n\x10\xed\\\x0dv\xc4\xaf/\x92(\xb4&\xe1,Z\x9d\x07\xb3!X\x7f\x85?\xe8d\x815 \xd11\xddK\xa3\xf8\x07\xa5:\xa3p\x19\x84\xe5\x1aEAk\x12F^\x96.\x06\x9f\x91Y\x10\x13?-\xde\x1c\xee\xec,\xe9\xbc-\xa2$\x1d\xee\x0ez\xbd\x1dV\xb2\x13\xf3\xa2\xddE\xbaZZ\x93\xf0\xb1v\xd0\x1bQp\xc9\xb5c\xd07hR\xe3\x87\xa9^\x7f\xdc\xdb\xdf\xebi\xb7od\xc4\xdcZ\xf4Q\xbcH\x85\xb5\x120\xfe\xa6\x88\x15=#\xeb\x98\xf8^Jf\xe0\x853\xc9\x91&K\xc8\xac\xdb\xe0C\x03\xf2\xfct\xa9\x98\x87#\xe9\xc9IK\xbbg\xfe\x82\xac\x98uu\xf7\xa8\xf4\xe4\xe3g/?9{\xf6\xf1\x8b\xf3\xb3\xe7\x7f\xed\xc5\xa7\xcf\xb8\xc1vP*\xf3\x93g\xaf_\xc9\xcf\x07\xbd\xdd\xd2\xf3\xe7\xaf?{Q~^~\xff\xa3\x17\x1f?\xfb\xc1'o\xce\xab\xed\xec\xefj\x8b}\xfc\x83O>\x91\x8b\x1d\x95\x8b-#o\x86\xa1\x02\xe8\x97\xea\x83g\xf4P\xc1\x9f=c\x17\xce\xc4\xe3\xc4\x9b\x93O\xc4\xbb\xe2\x87\xae\x80\xa8C\xfa-\x17\x9be\xab5\xc6\x0c\xa4_\xaa\xef\x7f$\x1e\x8a\x1fr\x81\x9f~\xf6\xe9'/\xae}\x82!\xe89\x1e\x96\x86\xf6\xe9\xcbW/?}\xf6I\xddZl8\x87\xe6\xe9K|/D\xd5\x81E\xbfY\xa5gH\xe1\xd8C\xfcZ~\xeaG+\xee{\x12\xd9\x16\xffQ.\xe1\xcdf\xcf\xa5\xf0\xe1X\xb0\x0c\xb3\xee!\xdfI\xfe}\xd5\xab\xfcA>\x9b%0\xbfD\xa5h\xa0\xb3|\xeaJ`/\x9f\xaf\x128iVH\x97_\xf0U\x85\xf2\x1cF0(\x83(\x92\xed\x96A\x14u\xf6\xca\xa0\x85Z\xd7L\xad\xebJ\xad\xeb\x86\xb9\xc2]\xf7z\x9d\xc9u\xefhr\xdd\xfb\xde\xe4\xba\xf7|r\xdd{\xd1\x99\\\xf7?\x9e\\\x1f~\xdc\x99\\\x1f\xedM\xae\x8f\x0e:\x93\xeb\xe3\x8f'\xd9\xc7\x1f\x7f\xfc\x02\xff\xffxz;\x9ed\x1f\x1d\xd1\x97\xb3\x8f\xbe\xf7\xf1\xc7S\xfb\xb4E!\xcf\x19\x84\x96pn\xed\xd3\xe1\xf8\xf3r\xb1\xdb\xcf\x9dJ\xb1\x9dr\xb7.y\xb7\x8e\xf6\xcb\x1ez\xe5R+,\xe5N\xc6\x93\xe9\xe4\xab\xc9\xfb\xea\xe3s\xfa\xf8s\xfbt\xd8\xbam\xb5n[c\xaf\xf3\xe5\xa43m\xb7\x9c\x0fv\x82r\xc9\x8b\xa2\xe4\xf8\xf3\xa2>\xc7>\x1d\xfe\xc4\xb8\xd79\xf6:\xf3\xe9W\x83\xf7\xb7\xec\xfb\x97\x93\xce_9\x99\xecLN\x87\xdf}4\x9a\xb4'\x1f\xb8\xe7\x93n\xeb\x7f\x98|\xf8xbO\x1c\xfa\xf6\xd4\xf9\xf0\x83\x9d@\xc7\"\xde\x19YD\x9f_B\xc33\xe3.\xfb.\x11q\xb5\xaakcU\xc7EM\xbb\x83\x0dj:\xdb\xa6&\xec\xdf\xb6}}alao\xaf\xa8\xea\xb8/}\xdf\x95\x9a\x18\x94~\xeco\xd0\xe03\x83yG+\x9e\xee\x1d\xa1\xb9\x02\xa5K~\xd2>\xc5 9{G0\xa4\xc7\xea'\\\xef\xb0;\x80[`\xc9\x9c\xd91\xbb7@}O\x87\x16j\xd3i\x19B\xa7_\xdb\xb1\xd7\xe6\x998\xca\x15]\xd6\xa4g\xb1\x96s\xc8\x7f\x87\x00\xb9\xc8\x05\x85\xf4\xfb\x07\x12(\xc5BU@?_.\n\n\x19H\xae\xe9\nA\xbd\x81\x04\x9a\xb3R{\x12(f\xa5\xfa\x05\xe8\xbf\xa7\x90]\xe95\xd4}\xec\x16/=\xb6\x1e\xc3\x10\xf6\xa4a\xec`\x0f\xe5\x96&\x14r(u\xe7\xff\xf9y,\xb3/A~\x13\xcb\xc8#E\xaa@\xa1G\xbd\n\xf4\x98)\xabk\x17\xe1\x8b\x9a#\xc6\x93\x11\x1c\xec\xef\xef\xee\xc3)W\\a\x96\xe9\xe7\\\xdfd\xa7\x85\x03j\xf9\x01K\xe9\xd9\xa6\xa7\xb5\x0e\xd6p\x00O\x9fB\x9fJX\xfb\x07\xbb\x83^\xf9\xd1#:\xdf\xbb\x8a\x11\x15\xe4\xd3\xd8[\x90\x13\xd3\x0e\xf6\x0f\x1c\x17^j`\x9f\xb2\x84r\x9f\xc2\x13\x18\xec\x1f\x9c\xc0\xa7\xed\xb6\x03o\xc7\x9f\xd23\xd9k\xfbS\x87\xc7\x19\xe8\xb9\xf0\xb2\x00\xea\x88\xd3\x1b\xad\x1e_hb\xc9;\x08P\x01C\xdeQI\xb7;\x0f\x96$\xf4V\x84\xb2\xf6 \\g)\xde\xdb\x8f\x92 \xc5;\x96i\x97\x9e\x1fd\x18t8\xf0,\xf5\xe2\xb2\x9b\xbc\xda\x97\xe7\xda\xbe0Q\x99\xf7\xb3\xf6\xfd\xef\xeb\xdf\xefF\xe1\x0f\xbd8\x0c\xc2Kv\x96\xcc\x7f\xf2\xeb\xea\xe8y\xca\xeb\xd7-\x0e]\x97\xcf\x94\xd3\"\x15\xd9\x86\x8d\x16\x1a\xf1\xbe1d\x0b?\xa2\x8f \xed^\x918\xa1\xc3x\xf4\x88\xcd\x845\xcb\xd6\xcb\xc0\xf7R~3\xf5'h\x93\xc0\x8eT\x98Q\xca\xe5\x91\x0fC)`\x15{\xb3\\\x12<\x9f\x8a\x96 \x90k\xcfO\xf1b*\xc9U\xba\xb4\x9a\\\xe3n\xc7\x8c+R\xa67m;\x93\xae\xf8\xf6\xc1N\x97\\\x13\xdf\x0e\xc7=\x1e\x03\x8d5\x14,\x97\x9dy\x14\xafdw\xffh\x0e\xe9\x82\x80\xda[*\x8b\xa1\xf4\xf82L\xedx\xdc\x9f\xbal\xafDe\xf8@\xc0\xa5\xb8\x8e\xac\xb5,d#\xc1lhX\xbf\x983\xde\xe6,\xf2\xf3A\x15\x13:\x82\x90E-\xef\xfa\x0b\xe2\xbf\xfd$\x08\xc9\xf7b\xe2\xbd\xa5\xe2[Dw\x90h\n\xef\xdc\x0e\x8a\xaf\xdf\xe7\xad&\xd9\x9a\x8a\xb1d\xd6\xd0hiu+*\xb67\xcf\xfe\xeav\xe8\xa2\xe2\xca\xc0\xb0\xdao\x9e\xfd\xd5\x9a\xc5N\xdfE\x85\xfe\xdf\x12\ny\x16\xd1\x0e\xbf\xd1u8\xef\xa6$I\xed\x18\x03@(K\x9bz\x97\xb0\xf0\xc2\xd9\x92\x80=\x0f\xe2$\xcd+t\xc4$\x94\xfa@[\xc9C*\xa4\xde\xe5\xa7\xde\xda\x85\xb8@\x9b\xc7\xe9\x82\xc4\x84\x1ep=X\xc7\xe4*\x88\xb2dy\x033\xe2/\xbd\x98\xcc \xc9\xe6\xf3\xe0\x1a\xa9\xa2\xf5\x18\xda\x10C\x1b\x1e[R7\x1e;.\\\xb0.\x07\xe6.\xafcB\xab\xb1\x13\xe2G\xe1l\x83>\x8b\xce2\xbf\x87r\xe0\xfc\x92\x96Q\xa5=\xaf\xc4\x92\xe2@U)\xa4\xc8\xdf\xaa\xaa\xe9\x08<\xd1\xa3\x02\xbac\xb0\xd8;\x94\xd8\xf2+\x1e\x888\xb4\x19\xa5<\x08V\x120sz$E\xf5f\xf9\x08\"\xfa\xa7=\x82\xbe\xc3e\x06t\x0e\xf0\xaa\xb6\x15&\xfb=\x19AF\xd7,C\xb9\xa7\xdf\xdf\xeb\xf7\xfb\xc5d\x93\xeb5\xbb\x83\xcf\xa2\x1c\xfc\xe4\xd9\xebW@\xab\xf1\xfc\x94(\xb90A\xdc4\xbca\xab\xe6I4\x84.E\x92\xc6\xc4[\xa1\xc3\x81\x17\x84 \x84Q\xd8Y\xc7A\xc8\xb6z^m\xa2\xab7\xed\xc6$\xc9\x96\x98/\xd53\xad\x99f\xc9>)\x96Lqo\xb9\xe2 \x04\xd0-\xac\xe2,\x833\x1cw\x83\x84\xa7\xdb\x0f%\x0c\xe4\x1a\x9a\x15\x89/ \xac\xbc\xf5:\x08/\x93\x13\xc4\xb6u\x1c]\x053\x8a\xddQ\x16\xfb\x84\xe7o\xa6\x9b@&k\x96\x93\x87\xd8\xa4\x87E[\xf2*xKn\x12;t\x9c|A=x\x02>\xfd\xc3\x164\xc3\x80\x8f\xde\xd4\x95\xe2\x9ce\xd87\x9b\xb0\x90\x94!\xfa\xdb\x04\xecG\xabW\xcfM?\x920Z\xce?\xac\x9b*\xdf\x85\xb9\x8a\xd7Aa\x08\x0cd.\xc3S\xf2\x08#\x91\x95z\x97\xc3\x1bo\xb5\xecF\xf1\xa5;\xe8\xf5\x06C\x9c?\xe6q\xabAsZ7\xbb\xeb\x18$L(2E>\xc0\xa5\xe2\xae0\xf4\xa0\x1d\xe5s\xe7\xc3\x13\x98\xd3?l\xee\x04.Dc\x1fS\x90\x1b\xb07/\xa6\x96\xc1\xe7)\xea]\xe9\x94'y\x8cb\x9e\xde\xa9X\x13\x06\xb0\x99\\\x04t\x8f\xdd\xde\xeaD\xa7\x11x\xecI!`\x95\xe5\x022\x13(\x06o\xc9\x0d&\xe0#\xe3`\xcaB$\xe5\x97~\x83\xe6D>\xea\xe2\x7f\xb9\xd1Y\x8a\x1f2p)\x05\x8d\x92(I\xd1s\x87\xdd\xe8\x12?\xdbmz\xac\xd8\xe5\xc8p\n\xb6\xfc\xc8\xcd\x8f\x9a\xb552Y\xaex\x8d\xca\xe8lz<\xc0\x89\xbd\xa0,\x9en/A\xa8\x18\x85\xc7gmt3\x92$S\x1c\x80\xa8\xacvf>6\xf1\xee\\\x86\x97s\x0e\xd5\x0e\xe1\x84;\x10\x04\xda\xb8\xac\xdc+\xeb\xda\x0e\x1c\x1e}TS[\xbb-\xd7\xa7\xdd)\xb8\xdbv\xd9\xd1\xca\xe0!7\x8bj\x0c~\x9b\xb4\xac}\xf9=\xbc[\x04Td\xe8\xf7\nA\xae\xbf[|\xe7`C\xbf[\xef\x90\x15\xe12\xaa%pv\xbeD\x07\x83\xe6\x89v!\xa6x\xc5\xd6\xfbe8\xa3R*\x9e\x9f\xf8A\x96.\x80\xfc\x90\x16\xdez\xd8\xefu\xbb\x8c\x87\xb0\x0d\x8b\xe1\xc6\x0cq\xa5\x9e\xcd\x0c\x99\x06\x8f{\xc16\x08\xe3\xbe?\xc5\x89\xfb\xd2\x85V\x1f\xbd\xe3\\\xd1\x94@\x0e\xa7\xdc\xbfM\x1aw\x0bf\x8f\xb4 g\xf7|HO\xb9\x83\x10\x9f`\x87\xf3\xb1\x0bo&\x13\x01zj\xf1 !?\x9b\x91\xd0'@\xc24\xbe1\x8a\xd9\xcc\xc7\xacDd\x88\x96\x96\n\x12\xd0\xf28\x8e\xd0\x83\x13Kd$p\x07\xc5\x89\xb4\xfb6\x08g0\x02K\xf4\xc0r\x8b\xcd\x841\xc6\x9a\x04\xca\x9f6\xd3\xa8\\\xc4D\x8c\xd6\xef\x80*\xa6\xd3!\xee\xee\x16\x11\xc2\x1b\x04\x90\xdc\x7fBW\x8f\xb4a\xe8\xf8M\x1a\x18\x8f\x1f+\x99i\x87R\xe5\x03.\x01m\xc2-0\x12m\xc41~\xb3\x17\x86\xb0\xcb\xa4\xa4@D\xb1\xc58\\t\x19Z-k\xf3Z\xd8\x1b\x16\x0b6 \x0b\x94\x91N\xf20\x8a\x03\x9b4\xa7\xbc\x98\x8b\x01\x92\x14p00\xb2~\x89r<\xc9\xb3\xf8\xd1\xd1\xc7\xba\x83pi\x97m\xd2\xbdBL\xcc\xc2\xfc\x04K\xc2\x99\xd0 \xf0\x83\xe8\xbb ]\x04!xpE\xe2\x0b/\x0dVt\xe5\xab\n\x1eS\xa8#.\xb9I\xe3m\x9d1)._M\x96D\xe0T\x9c\x80\xbdK\xa1\xf3\xe0\x07H~\x10\x06r\xed/\xbd\x15C\xc0\x95\x17\xbfM\xac<\x0eqe.X\x16\x85\n\xdd\xcd\x15;\xf2\x195\xf4*:\x9dJ\x9bI\xe6/JGn\xe6\xa5I1\xaf\x8c>\x8c\xb4o6\xef\xeaB7\xaf\xe7*WJ\x15\xba\x02\xe3L\xcd\x97\xd1;J.\xe9v\x8d\xe2R\xff\xcb\xab\xa6#\x7f\xc8\xc8Z\x17\xfa\xf60\x99u\xfd\x1c\x0d\xd1m#F]\xe6)\x08\"\x1a\xc3PU\x83\x85\x8eT\"W8\x85STs\x0d\xe9.\xe5\\\xa2(Ea\xe2\xa9\xee\xb1z~\x16\xe5\x99\xb6-\x0bs\xcd\x9a\xb4\xea\xa8Y\x0bQ\xb3\xf6\x18=\xc1k\x89\xf7\x0f\xcd\xc4[C\x96\x8f\x18Y\x0e\xefA\x96\xcd\x82\x8c\x9e4\x87\xc0K\xc8\xe4\xd9\xd0\x81\x12fV\xb1Zl\xdc\x90o\\v\xd4l\xbd\xb0C\x07\x93\xc76\xd7\xa8\xe5\xb0\xd2\xb6\xc9u \xc5~,\x0f!\x8cf\x04VYR\xe0\x9b\x97\xc2\x92xI\x8a\xaa{I\xcbVb\xd3\xf5\xbb\xa9a\x81\x7fJ\xd2\x86i\xf8\xc2U~I\xf2\xc6\x85K\x17V.\x9c\xbbp\xe1\xc2kf\x8c\xd20\xed7\x06f\xfe}\x033\x97\x16{\x19$) I~Vb\xbfl+Zc\xd4\xd9T\xe8j\xa1\x88\x1e\x9d\xcf\x82\x00pyE\xfc\xcc%\x15\x06@\xb5'\x8c\xd0\x19b]\xc8eLA\x85A\xeb\x1f=R\x04Q\xfbM.\xaf\x96\xc578e\x93\x00\xc3\xca!\x93\x9f:\xd0\\W}\xf8\x84+\xc2>E\x97x\x07\x0d\x1e\xf4\x85O\x0d\xde\x9a'L\x82\xba\xbd\xc5\xcdx\xe2\x94\xbbwZ\xf4\xee\x86\xc9c\xdfJ'a\x88\xd5\xeb\xd6\x8f\x07j\x80\x11\xbc\xa1\x9d\x8cr\x0b\xce\xa7\xf4\xc1\x9ao*z\xea\xbb\x80\x11\xf8\xc5\xa4\xcfs\x92F\xf0<\xd6\xa6\x9c\xecu\x99\xd5\x94\xec\x88\xf9L\xc1)\xbf:\x8eg\xaf\xd789\xdb\xd8X\xdcB\xc9\x9b\x98Og\xc0=w\xcc'4\xe0^;_\xd5\x8475=\xcb\x91T\xfb\xf4\xaa\xf6\xe9M\xed\xd3K\xc3\x06\x04\xeeG\xa3\x0b\"|\x87\xf3\xe3\x92\xab\xac7;?z\xc6$D\x18\x84\xa8\xa9\x1e.\xd6D\xd2\xa1-\xab\xc8\xb4\x07\xecP\x80\x07\x9a\xfd#\xfe\xfd\xf6\x96\xd2\xf2\xb8\xf9\n%\xd2\xc1\xd0\xc5[\xaf\xec\x08h\xd4A\xc9\xefI\x07<\xadL-\x7fX\xaa\xdf\xa6\x91:'pm{t\x9f\x1b\x8a6\xc8W\xf2\x87\xf6p\x9f\xf9[x\x0e\x9c\x99\x1a\xafH\xca\xb9\xc4\xe8Q\x11\xfe\xffc\xee[\xbb\xdb\xb6\x95E\xbf\xf7W\x8cx{\x1c2\x92\x15I~$Qlk\xa5i\xd2z7ur\x9a\xa4\xfbt\xcbj\x16-A6\x1b\x89T\xf9\x88\xed\xbd\xdd\xf3\xed\xfe\xb1\xfb\xcb\xee\xc2\x0c\x00\x82$@\xd2N\xd2\xd6k\xb5\xa1@\x10\xcf\xc1`\xde\x93\xb2d\xe3\xcf\xb5\xdbG\x97\xad\x82\xbf\xe4%\x9c\x82\xfe\xc0\xae\xb7\xd1w\x02\x12\xb6\xf1c\xa4\xc6\x149}\xb6\x8a\xe6\x1f\xa4\xd4\x9a__\xc8l\xb9\xa8kX\xf5\xf2\xa88Z\xc4\x9b\x8f\x02K\x8b\xa2\xb5@r\x02\xb8\x91\xf8\xe4\xff.\xd4\xf9\xc5/$\xc2\xaf_\x97\x86\x9c\xcc\xf2\x0f\x01c\xad\xb9g\xd1\xd5\x93\x14\xee\x9d9\x07\x96\xfa\xee\xf8\x9f\xd2\x13aD\xd8\x98\xf9\x0b~\xf1\x07kN\xcd\x04\xa9\x12\xe8o\xfc ~\x02>\xcc\xa3U\x14\xf2\x95^\x07IR \x9bW\xfe3\xbbKC\x1d\xb3\xa2\xff}\xaey\x9a\xe6X\xdcz\x12_\xf0 \xae\xb3U\x1a\xe0\xd9\xf9\xc0\xaea\xed_\x830q\xd6W\x05\xd5\x1b\xf6\xb9\x19\xdf\x88\x19\xef\x13\xcb\xe5\xf3\x0b\xf2\xd3\x80Mp\xed\xe42yN\xedi08\xc8Y\xcb \x9cG\xeb\x0d\xea_\xd8\x95ec\xf9l\x91\xceS{\xfb\x04\xa2\x18\x96\xd1j\x15]\xb2\x05\x9c]\x83\x8fj\xd0\xd4?\xcbV\xa8\xeca\xebMz\x8d\xca\x0d\"\xfcr\x9c\xa8\xbc\xa6c\xf3\xc6P(\x11\x0dEYeP\xae\xa4\x037DZ\x04T\xca\xa7\xab\x1f+A\x06hB\xb1s\xbc\xd9+k{-b\xd9\x1b\x97\xb7(Hk\xc6\x88\x9e\x81\xa8Qr3\xbfVnV\x80;\x9b\x17c\x93\xe8\xac\xf2Q\x15\xf2\xc4\xd1AH\xb3\x01\xda\xba j\xab\x9c\xae\\\xd4&\xf1d\x81~\xc5\x16\n\xfd\xfe\x81\xc4O\x0f\xce\xbc*\x01d\xa3~\xcaZ]\xccY\xb3\xd4\x93\x88u,\xf9\xc6\x17\xf5\x84\xd2\xc7FB\xe9\xda\xe0\xad\x04\x02H\x859\xa8\xbbi\x86\x05\xd2\x89=\xde\xe9 98IbM\xe9\xc9k0\x1f\xefs8\"\x82ac\xe5EUmN>\x8f\xf6D\x8f\x03\xea\xf1?M\xfeip7\xb2*\xf6(\xc3T\xd3=- \xabM-a\xa5\x8e\x1a\xf3z\xad\x96W\xe8\x0b\xab\xec+i\xd2\x08v\x17\x05\xd8\xfd\xa8\xc1.\xc7\xb7\n~al\x13\x1b\xc7\xf6\xcb\xe4\"\xa7?\x08?\xc2>9\xc5\x9f\x04\xe1\xf9\x8a\xc1\xefY\xc4\xab\x8a\xbdGZ\xa2n\x96\x86\x83t\x1b6\xc3\xdc\xe9\xe78):\x83a95\xbb\x04\x1e-\xc4t\x9f\xff\xd4`\xe2m\xf3\xa9i1\x9eZ\xc9\x88\xf0]\xf5\xd5\xa0\x8d\x18m\xe0\x95\x87d\x03|\x14c\x8dd\x9b-\xce\xa2\xa9\xab\xcbv*\x1aO\x87~\xfb9TrM\x9f\xfcE9\xd0\x7f\x98\xfa3\xafp\xc1\x1c\xa3\xef\x88>\xc9\x16-Rp\xd1\x910\x83\xe3\x1c\x8b\xcf\xcf\xd2\x08]\x89\x1f*Vf\x17\xc6\xf0hO\xfd\xe4l\xc3\xc0\x83#\xfe\xbf\x16\xba\xb2\x80\x14\xda\x11\x19m\x07\xfc\xbb'\x10lo{\xd8\xfb\xd3\xb6k\xc5\x99\x14\x0c\x1b\x87~5\x07\x07\xb0\xebA\x172\xc5R\xa9\x13x\xc1\xae\xfc\x05\x9b\x07k\x7fU\xef\xd2\xa4\xff\xe9K\xf9\x9b\x1b\x95\xe0\xc5N\xb7\xd0ZJ,\xf0!\x8c.C\x10\x11\xd3\x94\xcc\xac\xa6\xeb\xea\xc9\xa8\xc7\xa4~\x8eI\xe9\xe8\xdb0i\xb5\xe1/\x84I\x17Qv\xd6\x06\x93\x96\x06\xd3\x82\x96\xb8\x0dj5\x8f\xc2\x88Z51NGC\xb26\x0c+\x0c\\\xcdXu\x97d\x18\xcd\x8a\xef6X\xd5\xd2H+s'2\x81{#\xac\xdf:\xcf\xdd\x98\xa3\xcd6-V\x07s+\x93\xa7U\xe0'\xb7\xb2x2\x18?\xf6\x8a\xa6N\x9aH\xbd\x14\x8eE7\x84\xbc\x97\x85J\x0c\xb0\x10\xe3(\x19\xc5iw\x92.\xa6\x0fge\xddU\x95\\\xe5`rWS\x14\x94\xba.\xa5\xbc\x95\xdf\x94v\xe1\x9c]\xd1\xcd\xc1\xeb\x8d\xbbl\x06,\xbe\"\xcf\xdd%\xb9}\x12\x92F\xa6w\xe7Q\xfe\xbc;\xd2\xcaw\xf2g)\xe8\xc3\x1f\xfbz\xa5\xc7\xda\xb3Vg\xe7\xa1V_+\x7fL\xa1\x1e\x96\xb5P\x8e7\xce\xbe\xd6\xbd\x10\x9b-IF\xff\xa6\xf9\x18 \xee\xec\xe6\x86\xec\xfb8\x98\xb78X\xcd\xe4J\x80\xbe\xe4ErWX\xad\x8b\x03\xb6\xac\xa5B\x84u\xc6\xb2\x89b\xb8\xe3\x14k\x98g-\x8f\xef\xce^\xdbA\xd4\x0f\x00}eZ\xf4\xd9$\x95h\xbcj\xf29.\x9b\xa5\x8f\xbc\xcdK\xac\xd8l\x05\xe1+1\x8bT\xd3h\xc6gsU@\"\x13\xed\xe6DdP\x14\xdc\x1c\xda\xb3t\xe9\x7f\x99\xc6\xbf\xdfYZ%\xfej\xe3\xb6\xcb?\xbb\xc0\x04\x8af\xf8\xc2\xff\x83\x8c\x078~\xd2wB\xe8\xaf\x0b27Kr\x01\xf9w\x179\x8e\xb9\x14\x15`D\xcb\x10\xfe\xec\x0c%-#\xc6\xbb\x0d\xbeWw8\xbd\x1e\\ \xcc\xe7\x16k\x08C3\xcbv4\xb8<\xd8n\xc4\xf2P;\x1d\x85F\xc8%X\xa0\x99\xa2\xc5\xea\xa6*Q!R\xa4'\xad( \xfd\xbd\x16 \x94\x07\xd0\x96\xde,\xca\xd8\xc0\x998(\x9b\xaa\xa9\xab\x95\x08\xcdnn\x07\x96\xdf\xd5\xc9E\x94\xad\x16h\xabs\xe1\x7fd\xe0\x87\xd7\xd2\xf2\x1a\x95\xb0\xd2\xdf\xbb\xb5\xba[\xe9\x15s\xd1\xd9\x8fjVh\xe4)l\xe1h\xf5\x91\xb9\xda\xd4\xeb\xf1\x84\x06\x13\xef\xfbs\x19;OwM\x93\xfb\xfc\x9e4\xccw\xdc\x82\xcf{~\x05\xb2\xcf=!\xae7\x8c\xbaFh\xbf\xb9\x01g\xe9\xafVg\xfe\xfc\x833\xeb\xc9\xed\x99\x80X\xb7\xda\xeaS\xac=+\xccT\xac\xd1\xd6\x16\xbc\xa7O\xa8\x18\x1f\xcd\xa1d\x10\xa2\xf1=\xdf\xfe\xce\x01\xc6\xe0\xc4\x95\xec\xc2\xbd#H\xfds\xd4< \x98?\x13\xbe\x13\xa2uN+\xf6\xf0 `i\x9a\x97\xdeC\xff\x9b\xca.\x93\xc3{\xd3N\xdeq\xebr#4\xa1'\x13\xdd\xa31\xd9\x82!\xbfS\x9a\xa1s\x94+\xe1\xd0\xcbI\xf7\x91\"~\x94W,\x7fdI(\xd5\xc2\x8a\x7f\xbe\x8a\x12&\xcc\xf8K'\x99_\xe8\x95\x89\xdf\xdc\xc0\xeb\xafr\xf8R\x8f\xcaw\xe1\x87v\x9e\x85\x1a\xfa\xaf\x00\xa9\xc9\xc3P\x90~Z\x18!\xe1KP\x0d#\x94\xf6W\xec\xdc\x9f_\xf7\x94K\x8f\xc8l\xa6m\x18\x99=I\xb1U\x0b\x97E\xdc\xf1\"\x9f\xd1\xfcU\x0f:nIs4\x10tw\x07-z\xcc\xd20\x9ck\x06\xed\x9d\x13m|d\xc1\xdf\xadMC5\xbc\xect\xd63\xfa\xba\x15\xd8=\x19\x0f\x05\x0e\xc8\x8d[\xb8\x07\xa9xH\xc8k\"kiR\x1b\xeb\xe6\xcc!PKNCd\x06\xf8L\xd1\x19\xa0\xa8\xa1\xad\xcd\xb1\xd4\xa8\xa3m3\x04;\xd26\xf8hR\xfc\x05\xfbUPC\xdd[gZ\x1b\xd2\x01\xe4\xb2~1\xc0\xe2\x7f\xb1t\xe7\xae\x81\xa8\x16\x04\x9d6&\xd2;\x8b\xeb\xed'\xe1\xe1\xf7\xd34\x9cI\x19\x1b\xc7\xa7\xaf\x85\xc4\x81\xf0\xa9\x12\x82\xe5`Z\x90<|e\xef\xbc\x88\x0f\x06\x1ak$\xce{\xee\x9e_\x8f(\xdaV\xa4x\x0e\xed+\x8f\xbcbD\x17\x11\xe1A\x1f7_\x90\xccpV\x13\x14\xd0\xad\xfd\xb8\x12\xb7\xe5\xe7\x9c\xa6\x17\xd3D;\x8d\x8df\x9cV\\\x98*\x92\xde\xda\x82sr\xf0,\xee}T\xdc{P\xa18\xc2(\xdc~\xfa\xe6\xd9\xf1\xb1\x16O&\x01?f\x10\x84)\x8b71C\xc7\x87\x04\xd9-\x15tNnmR \x1b\xd0\x82\x9f\x9d\xc0\xee~\xf3\"{\x82\x14hXa\xad\x82\xe6I\xbd\xadc\xc9\xaa<4\x8aQ\x16*\xc03\xf7\xe0(\xecG\xede\xfc\x9dk\x8c\xc2XL\n\xc3d\x86(~G\x0e$\xbd\xa0\xe2\xda\xc9\x901\xa5\x05\xc8\xa7\x80K b\xc9\xd4Wrs\xf3\x82\x1e\xec\xef\x8d\x1e\x8aX\xa9\xfaG\x03Y\x93\x97\x8b<\xfa^\x19\xf7Q\xb2\x04\n\xc5\xd9\xa8YK/\x82\x84\xb6\x100\xfd\x01\xfe\x96\xd131!\x92\xfa!H\x1eQ'\x91\xf1\xd8\x99|\xbc\xb9A\x9e\x9b\xbf\xcc\x03Y\x1eb\xda*\xf9\xab\xd8\x04Q\"XE<\xde\xdc\x90\xd5\x02\x7f\x8b\x01\xaa\xf8;\x19\xa9J\xbdQ\xe4\x1a~)\x7f\x14\xdb.01|j\xf9\x981\nx\xb0b\x8bcQG|\"\xe8wK\xe5\xb7\xf4V\x0d\x1d\xf7.\x07\x06Q\xae\xc9\"\x06j\xb4(\x8e\xd0\x7fJ\x89\x84^\xa6\x1b\x02a\xa1:\x9fH_\x14\x11-m\xa7\x81\x08\x0c\xc5^\"$\x0d\x1c\x158(\xac\x1e\xd3P\xbb\x80<\x08\xf5A\x90\x9bFX8\xb7&\x92\xf3\x89^\xe7 \x0f\xf8\xb8\x0d\xc3'\x1e\xfc\xe0Z<\x8c\xc3|n\xb5\x07\xf4k\x9b8Z\x13E\xc3!\x9d\xe3rW\xc8G\xcb\x96\x1c\xcc-B\xf9\x88\xf3\xfc$\x91aFZH\xac<\x04[\x0c\x07\x10\xf0\x7f(\x04\x1bs\xa3i<\xab\xc7-\xdf\x1b\x0f\x9c<\x99\xdf\x99\xf6/XJ\xaa&T\xc9\xaf\xaa\xe7\x95\xd7\x1a\x8a-\x95\xb5\xe4\xb2N\x07\x06\x9f\x82<\x81C\xe0\xe6\x8aC\xa5\xa1W\x184\x085\xec\xda\x83\xb3,\x85e\x94\xf1[.\x8a\xd9\xad\x128\xe4I\x0c\xbe\xeeU\x93\x1e|\xdf\xb3\xe6+h\xd2B\xb4\xd8S\x04\x99\xb8\xcf\xaeR\x16.\xdc\xea\xf2\xd1\xa1\x1eCV\x9c\x0f\xef\xac\xb4\x1d\x12\xf8\xee\xd8\xd8W\xdaOc\x02\x87Z\xcc,f\xf3\xfd]gS\x8d\x0f\xfc\xe9\xe9\nL\xc1D\x03\xb7\x10z\xb1r\x97r<&.\x12\x89e\xcf\xb2\xe5\x92Pw\x15e\x86E\x94\x19\x8b\x9f\xf3h\x95\xad\xc3B\xa0\xd3\x1c\xee\x02-\xa3\xc19K\xdf\x84\xc1f\xc3\xd2\xa6\x05\xae\x98\xabW\xcfbG\x1b\xae\xa7\x0b\x0dL\xbc7\x88\x00\xf0\xbb\x1a\xc5\xf0pOD\xc0\x91\xf1o\xf4\xd9\n\xeb\x00~\x9do\xd3yvN\x07\xa7\xf1i\xf8\xff\xfe\xaf\x9eU\xc0\xe9\x07\xe1\x82]\xbdZ\xba\xdah\x10\x8b?M\xdd\x80\xf4\x17\x96\x90U\x01lS\xf0\xc0\xc2\"oc\xbf\x0c\x1e\xc0\x88(\x0f3\xb3\x86\xe3\x86~\xbf\x0f8\xf8\xee!\xec\x99\xb9\x946\xeef\xb8Dz\x1e\xbd\xd2Jd\x9c\xec\xd3\xa6\x97\x93Ww^\x9a\xcc\xba,n&\xd0\xf8vieZ\xacJ\xa4\xafJ\xc6\xd7\xf7\x13VE@\x94/\xd7CL\x80\xa8\xba\x80\\\x11sSJ@1\x94\xe0\xbc|4\x00\xefR\xc0\xfcn\xb9\x16t\x0d{\xde\xd5\xee\x8b.8\xbf::\x82\xd2\xcf\x90L\x19\xd86\x1b\xb5\xe3\x18\xef\xf8\xfc\xe8s\x82\x15)\x88{A($\x8f\xea\x1dFK\xbe\x87\xaarN\xb1\xf8)q0\x0e\xc6\xa3W\x98\x00\xf9\xba.\x9f\x9b\xc0\x04\xf9{Q@*\x10\xd2M0\xb9\xa096p\x85\x88\x8az\x19\xd3\xaa1\xde\xad\x11M+L\xf3\x89Hs\xa0])z\xe3\xfc2\x8e]C4\x9c$\x8d+\xd9\xfd>\x04\xe1b\x9c\xabs\x0b\xef\x94\xf7\xd7lu\xdb\xc6\xcd#\xaf\xdb\x17\x91\xe7\xf1Mz\xbdbcp\xd4z9\x7f\xf5q?\x8b\xa2?\xf5\xb8\x1bL\xa7Z\x1f\xf7\xc2\xb1N\xe3\x8c\xe9\xc7\xf8m\xf9\xf7O\xef\x9e\xcbc\xcd\x0b\xf6\xf4\x8f\x97\xfe*)\xd4~Q)x\xfa\xf2\xcd\xf3\xbb\xa2\x85\xbas|\x9b\x81\x7fN\xfc\xe1LE&\x81o\xa2h\xc5\xfcpF}T\xf2\xd2I\nT\xa8\xe1k\xe7^\x8bmL8\xc1\x9a\x82\\\xd2\xad0\x91\x0b4\x06\xb1KmN\xb1 E\xb4\xea\x8b\x16{,\xf7\xbbM_&\x8c\xd1\xae/9\xaf\x17\x96y\xfd\x1d\x10\x88%3\xe2m\xb3\x9aV\xf2\xa6\xed\xe5\xe344\x94\xb5o\xe8\xa1\xd6\x90|*c\xba\xc0\x84\xe9\x820\xfd; :\x12\xd7\xe8\xb2k#\xe0\x04v\x87zS\xc3\xca\"\x17\xee\xe4FU\xe8\x1a_\xe7\xbfD3\xeed\\\xbc\xc7\xf3\x1e\xa8\xf2\xe9i\xdf\x9d\x8c\x83pys\xcc\xff;y\xe1\xddPQ\xe8\x877'\xfe\xc9\xcd\xc9\xd3\x13\xcf\xfbZ7\xb9\xc7\x80\xfc\x98\xadW\xeb\x9c=\xb0K \x8d\xbc\xf3r\x15\xf9_\x84{\xd6\x85\xdb\xa4\x15\xe1\x88\xd6\xedD\x82\x80\xf1t\xda'\x9d\xeaf{\xb3\xcfN\xd2\x18#\xc1\xc8\x11\xc2!H2BX\x1eW\xa8\x91~\x1a\xbd\x8c.\xe5\x89\xe6\xa4\x04L\xf8=>\x06\x11\xfcw:\xeb\x81\xd3\xdd\xceu\xe7\x0c\xe9\x95#q\xc1\xb8d\xf2\xa7h\x91\x1e\xf0\x9a\xcb\x9c\xf4\x10\xa6G0\x11wY\xff\xf5\xab7\xc7o\x8f\x7f~\xfe\xfe\xf8\xe4\xc5\xf1\xc9\xf1\xdb_`,_\x9d<\xff\xeei\xf9\x95\xd3\x0f\xfd0o\xee\xc4?\x811\xb0\"\x85!0\x9b\xcb\xeeFf\x04E2\xe3\x05\x07\x9cZBCX\xe7\xc5Dh\x04\xb7\xe8\x8aIB#\xe6\x9f\xdb \x8d\x10\xees\xb2y\x8c\x0f\xda\xa8\xd8\xdf\x89\xd4p\x89\xd6\xe8\x1c\x92\x1b\x86\x81\xd4hKk\x14\xf0\xa4\x0d\xe2C\xb3l(HN\xfc\x13\xde\x17$\x97A:\xbf\x00\xd7*;\x98\xfb \xd3\xe5\x90cc-\xd0\x16\x07\x81\xcf\xcc\x1dQcJ\x8a\xdb\xa6\xb1\x93\xa7'\xb5\x8d)1m\xab\xc6\xfc\x13\x83<6\xf7x\xb6\x1e7!\xf4\xfb\x12\xab\xc5O\xfeg[\xad\xe3\x93\x17\x9fo\xb5\x8e\xc3e\x9b\xd5\xaab\xa0/\xb7Z\xdb\x9fu\xb9\xb6?\xebzm7.\x98\xe9\xb4\xe7\x9f\x0f\xfa\x03\xc3X\xb4{\xa9H\xf6\xf6 S\xc9\xbc&\x10\xaak\xcaa\x0e\xbfP(\x02fX\x87L\xfe,]C\x99\xfc\n*\xe4\x97\xa2\x8e\xb4\xffy\xdb\xae\xed\xc7\xd7N#A\xd7\xd8\xe2\xa4\xf4\x8b\x93no\xd3\xd9\xcd\x14NO\xd3Y\xd7+\xbc\x1c\xeb\xbd\x17~\x10}H%\xf7=\"\x10\xb1\x85\xfb\xee\xbfn\\N\x8by\xe5n\n\xdf{\x13\xcf\x9b\x14(\xb9V\xea\xdc4X\xb3$\xf5\xd7V+\x96\xcfN\xac\xe5\xe1\xca\x83>\xbbbsA\xb3\xa9\xd2H\x96~\x01r\xcd\x10\x07\xc5\xa23\xd9\x08\xb7L\xf3\xb5\xa7\xf47H\x81\xa9yx\x8a(\xcb'\xa1\xe7'\xf74\xf3\xee\xe7q\x1c\xc5\xae\xf3\xad\x9f2\xe5K\xcbx\x99)(S \xf2\x89v\xd9t8#\xda\xa7\xcb\xa6\xa3\x19y+e\xf4sg\xd6\x83\x0e\x9b\xee\xcer\xf3Wv \xbc\x03\x97\xff\xaf\xff\xee\xed3W,\x83\xc9\xff.\x10\xe1)\xba\xbc \x8aN\xd1e\xd3\xbd\x19\xc5\xa5\xe8\xb2\xe9\xfe\xac\x07l\xfapfC\xc2(p\xc5\x80\xb7\xd3\x873A\x94\x0ez\xb0\xe3=\x81U\xeeK\xb9\xf3\xc4\x83\x15\x1a\xf6\x99\x90\x14\x88\xa8\xd1\xddU\x15\xfd\xd9\xc0\x8bM\x1f\xcfp\xe1\xf9\x9e\xed\xb3]\xb8\x0f\xee\xfe\x00\xee\xe3j\x0df\xd0\x85\xae\xcb\xa6\xc3\xe1\x8c\x83\xd9@\x8a\x00qC\xf4/\xb77\x9e\x88\xcb`]6\x0dzV\x1eFS\xdf\xda\x82e?a\xe9\xdb`\xcd\xdce\xff\\\x93?\n\x0d\xda\xa5\x0b\xce\xd3o\x9e}\xfb\xfc\xc5w\xdf\x1f\xff\xe3\x87\x97?\x9e\xbcz\xfd\xdf?\xbdy\xfb\xee\xe7\x7f\xfe\xcf/\xff\xf2\xcf\xe6\x0b\xb6<\xbf\x08~\xfb\xb0Z\x87\xd1\xe6\xf78I\xb3\x8f\x97W\xd7\xff\x1e\x0cG;\xbb{\xfb\x0f\x1f=\xee>8<\x0dOc\xe7\x96\xec; x\xbe\xc4\x86\xddY\xfbm\xc1\xd3A\xa3b\x9cc\xc7\xc8\xa2\x1e\n)\xf2_H\x1eCa\x9d\x8e\xa8\xe3\"b\xcfr3vi\xbcN1\x00a\x7f\xb7Qk\xc4\xe0\x00\x06\xad4?(\x13\xdf7\xbe\xb6\xe2\xc1\x18\xfe\x0b\x1e\xa1\xf0\xb9\x08\xf6\x9f|q\x06E\xe9\xc5\xf44>\x0d\x0fgB\x86a_\xf4\xa0v[|\x8c\xffc|\x95\xd8\xb7{n\xd1\x07)\xff\xee\xc1\x13\xe0\xab\x9c=\x01\xd6\xedz\xc0\xe0\xbf\xd0\n\x8c\xe4%\xa4\xce\x99\x8b\xfc\x10pt\x04\xc3}\xd8\x82\xd1\xde\x9e\xd7\x03\xbd\xf8Q\xb9t\xb4\xb7\x07[\x90p\xa4\x9f`\x12\x90\x83\x03\xd8\x87\x1b\xf0\x158\x04\x12\x1c\x98\xe9r\x15[4\x00\x19\x087\xc3\x81\xdd\x87}T\xd1|\xd2\x90`\x0c\xc3GJ\xd0Slk`lk$J\xf1S\xe1q\xc8\x97F\xaf\xb3\xab\xbe\x8c1\xe9\xc62\x8e\xd6\xea\xc1\x9d#O\x80\xe8\x1e\x1f\xe7u w[\xa9\x08\x06\xf6\xe0,\x0e!\xd0\xf6Z\x93\xb6\x00\x1d\x93s\x8b\x15\xa1X\x80/k\xc45~\x0d\xae\xb1@\xe7N :\xf1\xe4\xfb\xd3\x00\xb7\x8fo\xfa\xfe\x0eR|Z\xe9\xc8T\xba_*\xdc\xdf\x81-@s\x1c>#7\xe0\x10\xfb\xc8\x83.\xa4SfW\xa8\x16\x01t\x87\xf4\x87\x9fyD0\x86Q\x0e\xae\x85v\x06\xa6vv+\x85\x07\x07P\xeeq\x7f\x17\x1b\x1e\xe6\xc0\\h\xb9:\xc0\x83\x83J\xc3\xfb\xbb\xc5\xf6z\x10\x17\x01O\xfd\xfad\x02\xc2\xca\xceVd\x7f\xc58\x93U\x02\xc1*,\xbc%\x89\x16\xd5x2X\x9c9>\xf1\xca\xb7\x19\xf2\x97\x985\x12\x83[o\x03C\x80\xca\xfc\xb8\x91>z\xae\\\x83\xf9\xe1\x0b\x9f\x90 \xd8\xea6\x16\x88|\xa1\xf3)\x9b\xe5I\xc0\x94\xa8\x96\x16|\xe6\x08f\x15E\xb2q\xb3=\x87\x08\x84\x13\x84\x10\xd7\x1b\xf0\x04\xa2Id\xd3j\x08\nY\xdfo\xecZ\xfe\xdd\xc9P\x07i\x9f\xe6>x5a\x81\x90\xa8;1k^\x16\x11\xce\xa2U\xd2\x0e\x058\xc5SyG\xfa\xa6*\x9c\xf8\x93<\x8cZ\x1c\xfa;\x9e\xe1\x8d\x1f\xc4\xc9\xdf\xeb\x10\x0b\x7f\xdd\x9a\x83\x9a\x89\x19=\x8dc\xff\xda\xf5\xa5\xdb\xa3R\xf4\xf0\x13\xec\xdf\xed\x04\xfbx\x82\xcd'7h}r\x03\xf4\xe1G\x93!\x0d\xe1~`\xd7 \xff\xba\xec\xd6ok%\x9b\xb2\x19Ge\xd1t\xc0o\x19\xfcw6\xfb\xd3\xa1\xde\xb2\x8f&\x9a\xfac9\xd4\x99\xf0\x06\xb6\xeccT\xd8\xc7\xcc\xb8\x8f\x99m\x1f\xf9ne\xb8[Ae\x89{\x10\x89\xb5\x0b\xc4\xda\x05\xb8vV\"&\xfa\xeb\x0fp\xf1\xd6\xbe\xe51N\x98Uun\xf6)\xfcrg\xb8\xf6\x82\x0dB\xb0\xc4\xfe\xd2\xee\xb1\xb0'L\x10\x15\xa2\x0d\xa7lV{\\>/\xc4\xdb\xf0\xfc\xdf\xcd\x8f\xf2\xb7\xe4A\x16.\xd82\x08\xd9\xe2\x13%/5\xcbp\xfbE\xf5*\x19\xe6o\xcb\xcf}\x8c\x82\x85\x8c(V\xd7\xbb\x89\x93\xab\x13\xfa\xfd\xcd\xbc\xa1\x7fK\x1e\xc4\xec\x9c]}\x11U\xca-\xe4f\x01F\xa6\xc1zm.'\xe5Mg\xa6\xb19\nxp\xfa\xc0\x9d\x9e\x07\xeb\xd9}\xef\xeb\x07R\xb3a\xae\x1e\x1bb\x0c\x80\x18\x94\xf3@\x8a\xdd\x07V%\x02i:\xa4\x05o8\x1d\"\x1b&\xd5\x07G\x9c%mq]\xf3\x9e\xd0\x9aw\xcar\x03\xa0\xb8`\x0b\x947Si\xe5K\xdf\xc1\x7f\xce\x8a\xcbS\xa2-:\xa9\xdf\xca\xab[0\"\xea\x81e\xc5P\x93\x95kFY\xaf\xcc\xc7|\"\x92PT\x1au\xd0\xd6\x14\xe6\xb6\xf8\xa4vC\xf8Zu!\xed'Q\x16\xcf\x19ty\x81ua\xd3\xfe\xf9*:\xf3WB\xe7\xd7=\x04\xe7\x9cB\xf5\xe5\xa9\xe7\xf3Wkz\x15\x9c\x87Q\xcc\x9e\xf9\x89\xfe.\xe0\xef\xd8\x97BfO\xb4J\xea~\xd1\xa21]\x06\xe1\"\xbaT@A?\xfb,\xd9\xc4\xc1\xda/\x19\x06\x06\x8d\x98\xd1\xa8N\xf8-y \x07\xff\x17\xe3\xc6\xaa\xbaF\xfe)\x18p\x11\x06\xf8\xe6{\x16\x11!\xc8\xf48}4\x0e\xe3g\xa1\x9eM\x8f\xfd\xf0\x9c\x8dkyo[TQq8^\xc7\xd1y\xec\xaf\xe9P\x84\x18\xfb\x8e\xef\x98\x0c-v\x16-\xae\xb58<\xce\xf3+\x0e\xf9I\x10\x85oR?ek\x16\xa6\x8eVu:\x98\xa9&\\\xe7i\x1cG\x97/\xc4\n\xe7_\x96?`\xea\x0d}\x8bN\xcf\xb7\xfd\xca\xc0\xe6\xebZ\xb1\xba5hD\xd4\x9f\x84\x8eEt\x9c\xe6\xcd\x0f\xb4\x8d\x0f\xeb6\xbe~\xd3\xff\xb0`s\x9b\xc3\x0b\xdej\n\n\x88\x81\x95\xdb0\x14\xbfu(\xe0\xbbc\x84\x82\xbc\xaa\x82\x02^\xd7\n\x04\xc5\xfae \xe0\xc0v\xeb\xaf\x0cf\x10/\xfc`\xc5\x16\x90F\xca\x16B!\x0c\xbb6\xc5\xd8\xc1\xc6\x8f\xfdur\x0b\xab\xd0H\x06T\x0d\xfd\xb5 >\xc5\x0di\xec\x0cW\x1c7\xba\x07\xce7\xabh\xfe\xa1t\xde\xec_\xe1\xf2Mp\x0d\xe4\x02\xbaQ\x0fB\x199x\x8a\x96\x0b\xfc>\x9e\x0egt\x01\x0b\x95\x8b^\xdd\x91\x08\x02#F\xe5\x9f\xd2g\xf5&4w\xbe\xa1\xe5\x00\xfe\xd4;Z\xdd\xba\xcat\xed\xcb\xda8X<\x00\xf6F&\x8b1\xf7\xd1N\xa98\xa3\xda\xe5b\xbfN\xdaW\xac\x9a4\xcb\x15J\x08\x0f\x0e\xe1q\xb1h \x870,i\xb3Vp\x08;\xa3\x12(\xf0\xb2\x9db\xd9\x05/\xdb-\x96-x\xd9^\xb1\xec#/{X,\xbb\xe6e\x8f\x8ae\xe7\xbc\xac4\xbe5\x1c\xc2ni,\xefyY\xa9\xdf3^V\xea\xf7\x12\x0ea\xaf\xd4\xc7\x15\x1c\xc2~\xa9\xbd7\xbc\xac4\xb7\xe7\xbc\xac\xd4\xc7S\xbe|%7\xc4W\xbc\xac\xf4\xedo\xbcl\xbfX\xf6\x01\x93\x15\x96*\x1eca\xa9\x97\x1f\xb1\xb04\x95\xb7ph\x80\xf8\xc1\x18\x9c\xd3\xd3\x81\xe1\x1ez\x88o|\xc3\x9bG\xf8\xe6\xcc\xf0\xe61\xbeI\x0do\x86\xd4Qhz5\xc4W\x1fM\xafF\xf8jiz\xb5\x83\xaf\xca\xd4\x1c\xff\x1b\xd1\xd0\xcbBh\xfe\xb7\xb3;\x86{\xa7\xa7\xce=\xc3\xd8\xa9\xaf\xd3Scg\xd4\xdb\x89\xe9\xdd>M\xed\xbdi\xa5F;\xd4\xeaK\xf3Kj\xf5uI\xc6P\xac\xfa\x8c_\xd6\xce\xb5\xd3\x03\xe7\x17\xfe\xbfk\x96\xe0\xb3\xf8\xe7\xf9\x1b\xfe\x0f\xd2\xbc\xce+\xfa\xff \xff?>\xd2S\x84\x8f\xf4\xffWX{\xb9\xc4\x8a\xe2\x9f\x17/\x9c\x99)\x90\xc6\xeb*\x92\xcc\xc5\xb5%\x0d4Y\x9e\x1c\xd6z\x93\xf5(X\xc6ho\xcf#B\xe8\xca\xa1h\xbd\xa3b[\xca\x02\x19\xab\xef\xef\xed\xed\xc8\x0f2\xf1\xc1\xae\xe1\x033\xc9\xde\xa1FvG\x8fw\x1f\xef?\x1c=\xde\xf3\xbcb\xf8\xdby\xb4`\xb0\x89\x82Bz\\\x8av\xb8\xf6\xafe\xda\x85\xf3\x98\xf9)\x8b)\xf3\xc2\xe0\xea\x85\xf83\xd1\x0d8\xd0wb\xa0\x8f\x8a;[\xf8%o\xbc\xd3SG\xc4p\xcc\x836\x0e\xf0\xfbm\xc5'{\xd0\xd5\x987S\xb0\x92\x9f\xaa\x9b\xa5\x85\xac\xc6\x9d\xc9crG2\"\xb6\x0c0\xfd\xa3\x9f^\xf4\xd7\xfe\x95\x8b\xf9\xc1E\xf1\xcd\x0d\x8c<\x19\xda\xfbC\xb09\x0e?\xfa\xab`Ami\xbf\xf58\xdc\xcbUt\xf9\x92}d+\xa4`\x83\xe4$\xe2kz\xee\xa6\xf9\x1bO\xfa\x1fie\xb2\x97\xf4z%\xe2m\x17\xaeU\x1bE]\xcd\xffkH\xdfU\xe0\xdcrw\xfe\xff\xfca\x919\x87\"\xfb \x19iP\xc6\xd5\xb8\xa40`J'C\xce\xff\xd1\x13\x8a\x88:\xa4\x8c\xe4\xf14\x10Z]q\x16\xd84C\x0f\xeeN\x87\xc8\x99,7]\x1d\x91A/\xff\xcc\xc0\xd5r\xd0\xc8\x94\xff\xb6\xd7\x03\x97\x12\xb8\x95B\x90\xf7eV!\xde\x0foOdt\x98\xf7u7\xcb\x1e\xf8\xd4\x99\x8f\nk\xfd\xd5\xd4\xe7\xe3\x0b\xa7\xd9\x0c\x0e\xcb\x91oA\x13p\x17\xe1\xd9\xd5@\x8c\x03\x0e\xb6\x98H\xf3H\x05;Q\x9c\xfe\xc0\xae)\xd5\x8c\xfaQ\x8c\xde\x1e\xb2\x7f\x06\x0b\x19=]\xfd\xba\xb9\x81G2\xf6y\x18\xfd\xc4\x96\xd4\x86x\xd4[\x08\xa3g\xd1z\xe3\xa7?\xf2\xe3Lu\xb4\x02\xbd\xe6<\xe2\xd0\x8d\xeeV\x97b)\xb5\x02\xbd\xe6\x1d\xe2\xc5\xcb\\Du\x9f<\xbf*\x86\x98\xc7\x9cWa\x1e\xa6\xbe\x98I\x9a\x97,2\xfe\x85\x9f2a\xa7@\xa5Y\xc2\x16\xdf\xeao\n\xc1\xfdL8\xe2\xc4x\x98\x10\xe8\xc5i\n\xe0\xb0\x14:\x96y\"w1)\xe6\xb6\x87\x04\xd7|l\x89f\xaa\xf4\x04\"8\x80\xe4\x89\x879\x1a\xd0j]\xa6\xe6\x17n|\x98\xf8?\xf2\xd0\xda\x87\xfcCD\n\x0b\xd1A\x82\xa9\xdd\nox\x97\x14\xc65Bc!z\x0eu!\xc4\xa9\xe0\x03C\x01\xd7\xddC\x08<>\xc4\xeea\xd9\x9dL\x80\xb0_\xbbD/\xebbo\x9bc\xebJty\x1f4\xce\xce\xd4\xf6\xb7U\x14-\x19\x0e\\\xb1\x15\x87>z\x9c\xd76\xf4okC;\xa3b`\xaa\xe1h\x1f\x99\xf7\xfda9\xf2\xd5\xe8\xf1\x1e\xff\xc5)\x94\xdcm\x82\x93$\xe2\xd7\xcd\x0d\xec=\xdc\xd9\xdd-~\xc7/\xe3\x1d\xfe\x8b\x92Q\xa8\xaa\xbc|\xbf\xd4\xf5p\xb8;\x1c\x0ek'\xf2\xc2:\x11\x9cb\xa9\x1fl\x99?\xbe\xcf\x1f\x9f\xe6\x8f\xaf\xf2\xc7\x0f\xf9\xe3\x8f\xf9\xe3e\xfe\xb8\xa8\x1d\xd6;\xeb\xb0\x1e\xfcz\x1a\xde\x07\x19\xc8D\xdfn\xf9\xc4\x0f\xd27\xd5X#\xbfs2\xa7X\xf4\x0b\xe7U\x8aE\xff\xe4\xb4M\xb1\xe8g\xc0\x88\xd2\xd5A\xfeP\x1fg\x9d\x8f#\xd2\xed\x9b:\x86\xe8'sK\xf9\nO:\x85\xfa\xa8\xbe}Kx\xa0R\xce)\xd5\x7f\x8b\xec\xa3\x85\x04%\xa5\x9d\xc4x<\x9do]\xba\x8c|,;\xcb\x1f\xdf\xe4\x8f\x97\xf9\xe3\xfb\xfc\xf1i\xfe\xf8*\x7f\xfc\x90?\xfe\x98?.\xf2\xc7\xeb\xfcq\x9d?n\xf2\xc7\xe3\xfc\xf1*\x7f<\xcf\x1f/\xf2\xc7\x8f\xf9\xe3\xf3\xfc\xf1713{V\x17C\x82\x07\x839\x8a\x97\xbf\xed\x10\x0bb\xf2\x06\x0e[\xff\x13a\x05c\xdd\xef\xd7\x9a\xcdS\xff\xe3m'@\x91\xdd\x9a'\x02\xe2\xe6\x8a\xa7\xa3\x861\x83\xca\xffB\xb3\x9c\xa3\xfa'\xe2'=\x81.\xe7\xf50\x9b=_\x07Q\x01&\xfcqL\xc9\xeb\xa0\x0b\xffp\xe7\xc4L\xa2\xd2\xa2\xb63{\x98K\xc8A1\xb2V\xfa\x83\x83g\xe65A\xfb\xcf\x8d\xd0~\x0f3\x934+\xf7\xe4\x9fb\xa4s\xaa\\p\xcaV\x1aI\xc8LK\x84\xd0\x111h\xfb\x80\x0e;\x9c]\xdb\xdf\x19\"\x11P\x8dO\x1a!WL\xdf\xec\xef\x8c\x06\x90\x07+\xdd\xd9\xdd\xe1\xcc6\n\xa6^\xbb\xc3\xc1\x08\xbd\x96\x19lS\xeb\x949f[|\xd6%\x1e\x8e/\x1b\xa7\xdd\xc6$\xf3z+\xcce\xbb\x87\xd0AJ\xe6\xdf\xfc\xe2\x99@:\x8df0\xa6[\xee\xb5\xd9\x1bM\xff\x93\xba\xd4\xba=\xf3(}\xa8\xb9!\x11\xfc\xc1\xbee\x05\x99n\xb0\xdeDI\x12\x9c\xad\x84\xb7\xfb\x18\x02!\xaa$\x0b\x10\x8a=\xe64\x11v\x7f\xb8\xf5\xfc\xfc\xd7\xf64Rp(\xe95)\x00\xc4\x90k\x06-@\\D&\x85XRF\xf9E\xc8\xcf\x1b%\xd46\x7f7\"|\xa4\xde\xf1Q8]\x07\xb7K\x1e\xcam\xbalNC\xa7v\x86\xdf[\x19a\xdb\x909l\xe4(u{\x88\xb9/\xa9\xf4\x85a,\x8a\xf8\x99\xb2\xf1/E6\xfe{G\x98\xa2_\xd0\xfe1\xf8\xf39\xdb\xa4 \xaa\xde\xf0\x06^QN0\\\x81{M7MqZ\xd3\xd5\x8cff\xbfy\xecW\x8ad\x87cc\x95\xda\x90\xd3\x06\x83,#\x9b\xdf\xa9\x97\x8f\xfeOA\xc6G\x87\xbe\xcc\xb3\x17\xf4\x07r\xc8a\x8f\x8er\xd8\x83\xce\x10C\xdf\xa8\x9f\x03Cj\xe0\x04\x14\x94P\x13\xe5$\xad\n\xf9\xe9,\xed\x01E\x85+r\xb9\xe5\x14\xa6\xbc\xf9y\x0fV=\xb4\xff\xa8\xbaIq\x00Ea\x87z\x85\xbe=\xf2MU\\\x86\x02;W\x93P\n\x8dX\xae$Q\xbbM\"@-al~\x13\x18\xda\xd1\x8a\x1aZ\xd4?.\xa0:\xa5\xee\\g Z\x12\xf8pF\xa9n([y\x9d\x05\"\x14D\xacDB,\n\xfa\xb6\xec \xf1`C\x0fE\xf6\x9c\xd5\x10\x1b\xceW&\xe2@\xedb\x1c$\xa1\xd6\x12\x91%\xc2)'p\x16\xd3h6\xeb \x1cCf\x80>\xe5`\xa7\xff\x08\xee\xf1t\xb58A\x02\xf8\xf1l\xf0\xa7\xdc\x9b\x823\x1e2\xeb\xbb\xac\xb3\x14[\x875\x8b\xc9\xcc'\"r\xd3\x84\x13\xaa\xe2\x11\x1c\xe5\xf1MS-\x1d{?\xf1\x97\xec\xdb\x92\xb5B\x8d\xe5\x1eM1\xee\xb3\xab\x94\x85\x0b\xb7z\x8e\xc8Fs\x0cYq\xb7\xf0\xc6/\x8d\xeeN>?\x02\x90\xc85V\xba\xd6\xf0\x83\xed\xbc\x7f\xcf\x92\x1f\xa3E\xb6\xaa\xc6.\xfd\xe8\xaf\xb2\xa2w\x1f:\x8a\xf5\xcfY\xfa,\n\x97\xc1\xf97\xd7\xefb\x0c\x86\xdb_D\x97\xe1*\xf2\x17T\x0e\x87\"\x1eB>\x80\xdc\xe9h4\x18j;h\xf8\xd4\xae\xf1*\xdb\x16\x18\x15\xbd\xa2\x92;\xe0C]\x86\xfd%K\xe7\x17^\xc5E+\x9f\x93qJmvU\xd51\x92-\xca\x97\xb8\x9fl\xd8\xfc)\xd6L\xccH2\xf7\xe7\x0dJ\xcb\xe1\xa6^?\xbd`\xe8\x07\x17\xe9\xe9F\xe5\x9f:E\x91y\x14\x80\x9aSM\xbe\x8c\xce\x88\xa8.\xed'\xa9\x9ff \x1c\x1d\xc2\xee\x00\xd3[\x04\xfdl\xb3\xf0S\xf62\xf2\x17Ax\xfe\x06\xdf\xbb\xce\x12\x1d\x17i@\x9c\xb3\xb8e\xb5w\xf1\xcaux\xc1<\n\x93h\xc5\xfa\xa8\x14se\xffo\xd9U\xaa\x91'Y\xbc\xe2@\x86\x17\x07R\x89\xcc\xe5[)\xdcQ\x7f\xf1\xd7+\xea\xc1s\xc3~\xca\xae\xca!\xb4\xa1\xaaF\xfb[\x9d\x1f\x1d\xf2\xcfY\xda\x12\xd2R^\xf78t\xcbw\x15L\x80\xc1\x18\xa6l\xf6\xf7\xc2\x12\xa5s\xaf\x08w~\xfa\xf7\x0c^\x84H\x91\xcb\x1b<\xef\x0b&\x10\x83)9\x93\xd4\xc7\x96\x83\x17\x16[F5\x9a;\xdc\x7fT\xea1\x11#\xd9-\xe2!j\x93\x02I\x92\x0b\x06\x07\xbcL\xbe\xf0\xdc\xa0\x07I\xff\xdd\xebo\x9f\xbe}\xfe\xfe\xd9\xab\x93\x17\xc7\xdf\xbd\xe9\xb5\xdc>\x0c\x0e\x8d\x80\xeccp\xd1\x7f\xbc\xf1\\\xd6\xdf\xf8\xd7\xfc\xa8\xeb(\xde3\xf7\xfa\xf6\xd5w\xdf\xbdl\xdb\xab\xbc9U\x07f\xb5/\x02UEt\xa2\x86\x9c\xf0\x97=\xe8\xc4\xc5\xd1\x05\xc2\xf3t\xe6}\xc5\xf7\xf9\xc1\x83\xff\x03\x14J\xe2G\n\xdb\xf4\xee\xa7\x97\x87\xc9\xa5\x7f~\xce\xe2\xed,\xd8\xe6xg\xe1\xaf\xa2\x90m\xa3N$\xed\xff\x96\xf4\xd7\xfe\xe6\xff\x07\x00\x00\xff\xffPK\x07\x08v\xf2\x8aA\x86\xba\x01\x00\xc5\x87\x08\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00 \x00swagger-ui.cssUT\x05\x00\x01\x80Cm8\xec\xfd{s\xdb8\xb27\x8e\xff\xff\xbc\n=\xbb\x95\x9a\x99\x1dS!EQ\x17\xabf\xeb\xc8\xb1\x93q6r\xc6\xcem\x92\xad\xad)\x8a\x84$\xda\xe0\xe5\x90\xd4\xcdz\xf6\xbd\xff\x8aw\\\x1a $;s\xf6\xf7\xad\xb3\xd9dl\xe2\xd3\x8dFw\x03h4\x00\xb2\x9bl\xed\xe5\x12\xc5\xda\xda;\xfc\x9fN\xe7\xe5\xdf\xfeo'\x08c\xdf\xc6\xde#\xea:I\xd2\xd9\x0c\xbbzW\xef\xfc\xbf\xce\xec\xfac\xe7\x9d\xe7\xa0 A\x9d\xff\xd7Yz\xe9j=\xef:\xa1\xff2@N\x88\xed\xe4%M\xf7\xb7\x97\x8b0H\xb5\x85\xed{x\x7f\x9e\xd8A\xa2%(\xf6\x16\x13'\xc4a|\xfeWs\xde7,\xe3\xdfD\xfd\x9dU\xea\xe3\x03\xf6\x02\xa4\xad\x90\xb7\\\xa5\xe7F\xd7\xb0&\x9a\x9fh)\xda\xa5Z\xe2=\"\xcdv\xef\xd7Izn\xe8\xfa\x8b\x89\xb6E\xf3\x07/\x85K)\xce\xf3\xd0\xdd\x1f|;^z\xc1\xb9N\x95\xd8q\xea9\x18\x9dQ\xcf\x12\xcf\xa5\x9f,\xc20E1\xf5h\x85l\x97y\x14\xd8\x1b\xea\xf7\x049\xa9\x17\x06\x07\xd7K\"l\xef\xcf\xe78t\x1e\xe8\x16\x1b\x87\\K\x99\xf0\xe7=\xe4OJ\x19\xbb\x83!\xf2;\xb4\xa4\x0bo\xe9\xd8Q\xc6\xf0\x8cy\xbc\x8eii}\xdb\x93UZPT\xea0\x90\xdf\xe9\xeb\xd1\x8e\x96+>T\xca\x9d\x87\xbbL\xe4\xdd2\x1f:\x16a\xec\xf3\xca\xfbg\xba\x8f\xd0/1JP\xfa\xaf3\xbe Y\xcf}\x8f)\x01*\xcbf\xb5\x92\xa2(\xfdW=\xb6\xdaQ\x84\xec\xd8\x0e\x1ct^\x14\x01\xd5\x974\xe7\xe7\x9a\x1f>j\x8b\xd0Y'\x9a\x17\x04\xcc\xd4C\x8a\xaa\x04-\x85o\xc1\x16\x95\xf3 \xde\xeb&\x91\xed\xba\xd9l\xa0K\xda\xd0\xb0\x89\xbd`)n@+\xae\x92^\x02,E\xa7\x11\x87p\x9df\xbevnD\xbbr\xec\xed\\\xe4\xc0\x8fh\x972\xb3$\xc2n\x82\xd2C\xd5\xb0\xaei!\xbf\xd3\x1d\xe6\xff\x0e\xb8a\x01\xa3%\n\\h\xda\xac\xe7\x14j\xd6$\x9e\x16\x83a5\xacW\xdd>\xb5\xe7\x18M|{\xa7m=7]\x15\x1d\xa5\xd6\xf2d\xbb\xf2R\xa4\xe5\x83\xf4y\x11y1Sl\xb8\x8cQ\x92\x80\x83\x8f\xd2(Xw\xe1\xbaw\xd9\xeb4\x04\xac\xeb\xac\x90\xf30\x0fwP\x1f\x89m\xd7\x0b\xffu\x92Vd\x0e\x15\xac\xfd9\x8a3\xef-\x19\xe7^\xa9%\x91\x17h@\x17\x14\x10\x85\xeb\x94&:\x94C\x90\xa0\xa1 \xb2cg\x05v\xdfLY\xb9\xc7LJ\x0f\xd3\xc2\xc5\"A\xe9\xb9\xd6cB+\x8aU#K\xf1@s2nX\xdc\x06\x11]\x13\\@\xd2q#[C\xbf\xf00\xd2\xd6\x11\x0em\xb7R\x82pt\xcaG\xed\xcaO\xe9X\x00\xa5\xb6\x87\x13:\nE\xc1Z\x12\x85&k\xdf\xb7\xe3}\x8d\xc0^\x92j^\xca\xf4*\xc7\x0e66\xec\xc4\xb4V\x8b \xed_\xcc$\xe4G\xd8N\x115\x93Rd]\x17\xcd\xd7\xcb\xce\xdf\xa8q! \xb1\xe7v\x96!v\x01\xac\x96\xf7;\x90\xe2\xaf\x8b\xc5\x02\xa2\x98c\xdby\x80)\xd8\xf8\xa7\xa4X\xc6\x9eK\x04Ndx\xdbY\xc7\xf8G\xd7N\xeds\xcf\xb7\x97\xe8e\x14,'Y\xf7\x1d\xf4\xcf\xbc\xcf\x17\xef\xef\xb6\xfa?\xde,\xc3\xe9t:\xbd\xf9\xf0iu\xf5i\x99\xfd\x98\xffs\xfdj\xfau:\x9d^^]\x0e\x07\xef\xb2\x07o~\xbf{\xfd\xe5\xd7\xbb\x8f\xf3\xde7\xdd\xed\xbd\xde\x7f\xbb\xbd\xb8\xf8\xf6f\xec}\xfbp\xf1v\xfe\xe5u\xf0\xed\xf3[\xfc\xf5\xcb\x9d\xe58\x18\xff\x96\x11\xecW\xd1\xe7\xd7+\xfd\xcb\x951{\xef\xdfl\xe6\x1f\xacU\x81\xb7\xfa\xf3\xdf\xa7\xc5\xff.\xb7/\xd1\xaf\x17\xab\xaf\xbd\x14\xbb\xaf.\xbco_\xdch~\xaf{\xc3\xe1\xfa\xe5\xb5w\x11}\xbb\xd4\xbd\xcf\x8f\x9fofW\xc6\xf6\xb6\xf79\xb4?\xad\x06\x8e\xff\xf9#z\xb0>}5\xa3\xf8\xeb#~\xb8\xbe\x1f\xfd|}\xb9\xeb\xbf\x0fV\xa9\xf3\xc6\xc0\xee\x9b\xab%zc$\xf3`6@\x97\xba\xf7\xf5\xcb\xdd\xe6\xab\xffi\x90\xfd>\xff\xf2Y\xff\xfaa\xe4]\xff\xba\x1c\xa07\xc6\xd6}\x93\x8c\xaf\x1f^?\xcc{o\xf1\xf5\xeb\xd5\xcd\xa7W\x17\x97s\xf3-\xbe\xbe\xfc\xb4\xbe\xf1\x8c\xfb\xd9\xc7\xab\xdd\xf5\xa5c\xbd\xbb\xbf2\xde_\xce\xf67\x1f\xb6\xcb\xd9\xfdtw\xf3a\xb4}\xffa\xb4\x9b\xbd\xd2\xb7\xb3\x8f\xe1nv\x19\xeeg\xaf\xa6\xcb\xeb\xea\xef}\x7f\xf9\xdb\xafo\x1f\xbe\xddG\x1f\xee\xae\xbe\xd6\xf28\xfe\x9d\xff\xdb\x87\xb7\xa1\xfb\xeb\xdd\xf6\xbd7\xda\xb8\xa6k\xbe\x0b\x9c\xc7w\xfex\xffm?\xda\xbd\xff\xf8`\xbd{\x9c\xee\xdf=^\xef\xdf\xfd\xfe\xf6\xe1\x9bg<\xa2/\x96\xfe\xf5\xf7e:\x0ff\xf7\x04\xdf\xabo\xbf\xdf\xdc;>\xde\xbao\xf0f\xee]\xec\xbf\xbd\xf9:\xf8\xfa\xe5\xed\xc6\xfd\xfdv|\xed]7:xcl?~\xd2\xc7\xd7\xfeJw\x7f\x9d\x0e\xde\xed\xc7kg_\xdb\xe2~\xde\xd37\xe8\xcd\xeb\xed\xbb\xc7\xab\xf5\xec\xd58\x9d\xe7\xfaY\xa5\xf37\xd6\xe3\xfb\xe0F\xff\xe4\x7f\xa6d\x9e\x07\xb3u\xa9\xd3\xf5\xd7\xde8}g\xaeV\xce\xab\xd1\xee\xdd\xfdt\xe3\x18w\x96\xf3\xe6\xd3\xe6\x93\xff\xf9qn~\xde\x7f\xed}\xfe\xf0\xed\xcb\xd7\xfbk\xef\xa2?\xff\xb2[;\x8fQf{EY\n9\x9c+\xe3\xe6\xfd\xc3\xdd\xe6\xab\xf99\xfd\xf6\xc5\xd2?|\xba\x1d_g\xb6~e=\xd8_n\x07\xb3\x8fw\x97\xef?~\xed\xdf\xe8\x9fz7\xfa\xe7\xd7\xb3\x8f\xaf_\xdf\xdc/{\xb3\xc7o\x97\xb7\xf7\x0f\xdb\x9b\x87\xdb\xfe\xec~\xb9\x9d]]\x13\xfc\xf0\xda1\xefVs\xff\x06\x13\xfc\"\x9a\xdf\xad\x1a\xbf\xcb\xe8\xd2\xf1?\xaf\xdc7\xe3\xfd\xe77\xe3\xcd\xfcR\xf7n\x0b\xfd,?\xbdYm\xdc7\xe3G\xfb\xcdx{}usy}y\xbd\x9d}\xfc\xb4\xfc\xc7\x95\xb1\xfa\xda\xc3\xeb\xbc\xec\xd5\x83\xf7\x9b7\x1d\x95v\x1a\xdc\xbd\xf9\xbc\xb7\x7f\xff\x86\xbf]}\xdb\xcf{\xfa\xd21\xef2\x1d\x0e\xec/\xd6\xa3\xfb\xe6\xf5\xfak\xef\xf3\xdb\xbbK\xdd\xcb\xf0\xef|\x1c}\xbb\x0c\xcd\x9b{g\x7f\xfbpk\xde\xdc\x7f5o\x1f?\xedf\x9f>\xf5n\xef\xdf\xbe\xba\xd5?\xedo.\xa7\xfd\xd9\xc7\xe9vv\x7fe\xce>\\\xd7\xfc\xbe\xbd\x19\xdf\xbb_\x0c<\x0f\xee\x08~w4\xbf\xc7V~\x9bL\xf6w&\xe0\x93\x99\xaf\xbe\x1a\xe7~\xf9\xe9\xe1\xeeM\x81+\xfa]\xde\x0f?\xf6\x97\xbf]\x8e\xfb\xce\x9b\xd7\xf7v\xef\xb3~\xfd\xe6\xf3:\xeb\xef\x8ew\xfd\xf2\xb7\xe4\xe2\xc3\xcfof\xd9\x08q\xff\xe1\xd3\xdd\xc5\xe7_\xef\xed\xaf\x9b\xc7\x97/\x1fG\x97\xef\x92\xcb\xfe\xd2y\xf3\xbb\xf7\xf5j\xfa\xe6\xe2\xfa\x1fo.\x02\xf4\xf2\xe5\xe2u\xb4\x9d.\xb7\xd3\x8b\xf1hj\xbf\xeeE\xf7\xf8\xd3mF~\xf1\xf6\xee\x93u\x15?\xbc].\x97\xbf\xfc\xf2S'F\x11\xb2\xd3\x8e\xde\x11\x8e\xa4\x9a1x\xc6\xc1\xf4\"\x1f\xe6n\x8b\xc1t\xba\x18\xbd\x1c\xaf\xfew0\xfd\xdf\xc1\xf4?u0}\x7f\xf9u\x7fw\xbf\xba\xba\xbb\xcc\x06\xd3\xaf\xfb\xd6\xc1\xafe0m\xf8\xdd\xaa\xf1\xfb\x0f\x1aLo?\xb6\x0e~G\x0d\xa6\xb7\xed\x83\xf3\xf7\x19L7\xaf>\xe8\xc6u6\x18\xcd\xea\xc1\xd4\xbf\xeb\xbf\xb4~\xbex\xfd\xdb\xc5b:{\xed\xbf\x9c],w\xa3\xbb\xe9\x9b/\xaf\x02c:\xf5?,\xcd\xfe\xed\xe0\xe1\xe2\xf2\x1f\xb37\xb3\xcbW\xdb\xebWhv\x8d\xfc\xd7/\xad[{{\xe5E\xd3/\xdbO\xab\xed\xd5\xfd\xecr3\x9f~\xc1_\x1e6\x9f/\xb6\xeb\xd1\xe6\xf6zz1\xbd\xda^\xbc\x8aV\xa3O\x03G\xcf\xc7\xa5+\xfc\xfa\xe3\xc3\x87\xf5\xad\xff\xea\x95\xd2\x00<\xd2\xf2x\x97\x1c\x85\xb3`\x99\x1d~\xef#T\x8f\xbf/\xc7\xf7/\xfb\xb7\xd3\xafw\xbf\xaf\xa2o\xcb\xe9\xf4\xc3\xa7\x87\xff.\x03\xd9\xe6\x7f\xbf\xbdL\xa6\x17\xaf\xaf\xdc/71\xba\xcdF\xe6\xdbj\xe0|\xd9\xbf\x9d\xed\xec_\xeft\xe72\xdc\xbc\xebY\x8f\xef\xfcb\x1c{\x97\x8f\xb5\xe3\xfe\xd7\xdf\xa7\x9b\xd9\x87\xfe\xf6\xddv:\xfa\xcd\\m\xbf~\xb9\x89\xbf\xfd~\xbb\xfc\xea\x7f\x0e\xec/\xfd\xf1\xf5\xfa\xe7\xe1f\x7f\xbd\xb4\xbf\xdc\x8e\xaf\xb1c|\xfcxq\xe3\\\xdd`\xfb\x0d\xbeF\xc1[\xfc\xc9\x8c\xde\x7f~s3\xb0{3\xeb\xdb\xab\xeb\x97\xb9\x8f^f\xfd\xf7\"\xfd\xf6\xfb\xdd\xaa\x19#\x96\xe3\xeb\xb2\xee\xf7\xbe\xf5\xf8\xde\xcf\xc7\xe0M\xd6\xe7\xf31\xf9\xd7\xbb\xf8\xb7\x0fo\xab\xb9\xe2\xeb\xc7\xcf\xd3\xe5mo\xbc\xff\xf6aj\xbc\xbb\xff\x9a~}\xbc\xda\xcd>L\xcd\xf7\x1f\xfa\xbb\x9b\x8f\xcb\xc7\xd9\xfd\xa7\xa4\xec'\x9b\xd9\xe5\xc3f\xf6q\x9a\xce.\xaf\x06\xb3\x8f\xd3\xc1\xec\x9e\x18c_]g\xe3~\xed_\x8d<\x99/\xea^\xad\x1b\xd35\xdd\xbde\xce\xf6\xd6\xc6\xf1\x9d\xcd\xec\xe3\x83\xf5\xfe\xc3h;\xf3F\xfb\x99gd\xf4\xa9cf}\xf1u\xff\xdd\x17\xeb\xf1z\xdf\xf0\xbd{\xf3\xf9\xf1\xab\xf96r~\xbd\x8b\xe6\xbd\xfe2\x1b\xbf\xdf\xfb\xaf\xbd\xb9\xf9Y\xff\xed\xc351Nf\xe3\x00Q\xa7\xcc\x1e\xfb\xff\xc0\xb1\xf9\xf7\xe9\xe0\xd6|\x8b\xbf\xfe~\xb7q\xf0\xddf\xde\xdb\x12\xf3\xe2E87\xef6No\xb5q^]\\\xde\xee\xa7\xfb\xd9\xe5\x95q\xfdju\xf3\xf5\xcbM4\x0f\xb2\xb2eT\xf0\xb9\xb8\xf9\xf81z;\x0fn\xf4\xaf_\xac\xfbo\x9f\xf0\xd5o\x1f\xdef\xfc\xd7\xf6\x17\xfc\xf0\xfe\xe1z7\xbb\xbf\xd6\xdf\x7ft\x1eo\xee\xddW\xb3\xc7\xab\xdd\xdd\xc7o\xaff\x0fo/\xef>^\xeb\xb3\xcb\xe5nv9\xdd\xcf>:;\x82\xdf\xd5\xbcwc\xcc\xbf|^\xbbW\x0d\xbfoo(~z+\xbf|\xee\xac\xe7\x13\xec\xf8\xb8\xf7\xed\xcb\xdd\x1b\xc7\x1f\xa7\xd7\xbf\x16\xba|\xef\x8b\xe7\x85\xdb\xfb\xab\xfd\xec\xfe\xd6\xbay\xbc\xea\xdd\xe8\xd7\x8f\xf9\xbc\xf0p\xbd\xbf}\xb8y=\xbb\xbf\xdd\xbe\xbf\xbc\xda\xce.\xafw7\x8fW^\xc3O\xde\xfa7\x97\xa3\xf0\x1f\x97\xe3_\x7f{\xfc\xf4\xb2\x8d\xa6\xfd\xef\xe2\xe5v:\xbd{5\x9d^O\xa7\xcb\xcb\xe9\x87\xeb\xe9tuu1\xdd]]\xbc\x1c\xddN\xbfd\xe3\xe6\xed\x14\xf8\xdf\xd7\x8b\xe9\xed\x15\xf0\xfc\xfa\xeajzu1\x9d\xce.\x98\x82\x8b\xe9\xe5\xd5\xab\xa9~u7\x9d^]^\xf0<\xef\xae?\xbe\xbe\xf8\xf4\xe5\xea\xc3\xf5\xe6\xa5=\x9dn/\xa7\xb7\xd3WW\xb7\xb3\xbb\xe9\xe5h\x1a\xbe\x0f>~6n?^\x0e\xdf\xbeMV\xbf\x99\x9b\x0f3\xf3\xb7\x97/\xbf)\xcd/\xc6@m\x829*\xbe\xcf\xe6\xd7W\xb7\x0f_\x96\xbd\xe9\xff\xc6\xf7\xff\x7f\x1d\xdf\xab\xce\x01t\x1c\x9e\x8d\xad\x8asV\xcfH\xc9y\xab\x8c!U\xe7\xad\xc7\xcf\xbf\xe2\xed\xb7\x0f\xe3\x0f\xdf~\xbf\xd9\xb8\xbf\xbf\xbd\xcf|\xe9\x9b7{\xb6\xf8Y%\xae\xbfy\xfcj\xce\x1e\xde^\x15I\x97\x99!\x1f\xbf\xdb\xd7\x1d\x0d\xbf\xaf\xad\xfc\x9e-\xbeoOn\x1c\x15\xdf\xdf]\xb6\xf2\xfbN\xf1=\x1a\xbc5\x1f\xb2\x11\xe2\x91M\x96\xe8\x9f.\x93\xd9vv\xff\xe1.\xfc\xfa\x9b\xf5\xe6\xbf\xfb\x1f~\xbb\x99\xdf\xdd\x7f\x9e]\xdd\x1a\x8bWw\x97\xcb\x9f\xbd\xe0\xe5\xe0\xe7\xb7\xc6\xf4\xed\xa7]\xb2\x9c^\xbd\x99NM\xe3b\xfav\xf6A\x7f\xf3\xb5\x18\xcf?|\xfa\xfc\xfe\xee\x1f\xd6\xab\xaf\xd7\xd7\x92\x04J\xb3\x15C\x1f\x8e\xa1\x7f\x03\x8e\xcf\xccCwO=\xe0N\"\xb8\xf4A\x04\xd7\xa3\xcf\xcd\xb8\x98\xfe\x95\xdeZ\xae6\xe6\xe8\x87\xfc\x01\x9dE\x18\xfb\xf4F\xacA\xff\xda\xa3\x7f5\xe9_\xfb\xf4\xaf\x16\xfd\xeb\x80\xfe\x95?\x0b\xb4J}\xba\x15\xf9Nu\xb1\x89\x83|\xdb\xc3\xff\x12\x95\x96\xdbT\xa2\xe2\xc8N\x92m\x18\xbbB@\x8a\xc4\xbcS\xb4K\x85\x85\xeb\x98!,\xb64\xe9G\x1e\xbd\xc7c{\xf4.UH7\x9a>'\x101\xe7\x94\xca\xf3Q\xd4\xb3|\xd7\x93~BKPmK\xd2\x0fW\xf4\xaf\xb4-\xd6\xf8\x94\x0dH\xba7\xd8I\x84\x9cT\xcb\xf7\xd8\x0e\xe2\xf3%b\"M3\x06\xbbq\xb5\x9b\\\x9d0\xb2\x06\xdd\x9e\xf5BF5\xde\x19\x03\x96\xca\x18\x0e\xbb\xc3\xa1\x94\xac\xbf3Y\xaa\xa1\xbc\"s\xd7\xe7\xea1\xcd\xaeiJ\xa9\x06<\xd5`\xd0\x1d\xb4\xc8\xc6\xb7\xc8\xd2\xa5$\xa3\x9d\xc5U\xd3\xeb\xca\x1bd\xedF\\5\x03y5C\xbe\x9a\xa1\xd1\xed\xf7Z\xea\x19r\xf5\xf4\xe5\xf5\x18;\x83#a\xcf,2$\xc5\xc9\xb5C\xedq\xf6< \xf1:E\x934\x8c\xce\xf5I\\zd\xc9M\x9f`\xb4\xc8~'\xce\x0eT\xe7k\xb2\x9f\x1f5/p\xd1.\xfb\xe5\xdf\xff\xe5#\xd7\xb3;\x89\x13#\x14t\xec\xc0\xed\xfc\xe8{Ay\xea\xc0\xd4\x91\xff\xd3A,W\x90<\xa17d\xd4'u\x08\x80P\xadO\x00\x84\xed\xdd\x02\xaaM\xa9g\x00\x84*\x9d\x03\xaa\xaf\xbd\x7f@\x95)t\x11\xa8\xb2\xf6^\x02\xe9Q\xa5\xa3@\xb5\xb5\xf7\x15\x88J\xa9\xbb\xe4\x84\xcf\xdfc\x14\xbaL\xf9\xb0>\xbd3h\xe9G\xfeS\xba\x91\x7fb/\xe2\xe8\x14;\x11G\xa7\xd0\x87\xf8\xba\xd4\xba\x10G\xa7\xd4\x83\xf8\xda\x14:\x10_\x95J\xff\xe1\xabR\xe8>\xbc\x06\x95z\x0f_\x97B\xe7\xe1\x89\xd4\xfa\x8e\xff\xe7w\x9d\xb6^\x82\x9f\xd2K\xf0\x89\xbd\x84\xa3S\xec%\x1c\x9dB/\xe1\xebR\xeb%\x1c\x9dR/\xe1kS\xe8%|U*\xbd\x84\xafJ\xa1\x97\xf0\x1aT\xea%|]\n\xbd\x84'R\xeb%\xf8\xbb\xf4\x12\xb2^\xcf_\x1e\xe8c\xa0\xb4XN\xb8A1y\xce>?W\x9d?\xfd\xbf\x9e\x1f\x85qj\x07)K\x12\xa4\xb6\x17\x00D\xf9s\x82\xac}\xa6;\xf0\xc2d\xd3\xee)\xf2\xc0t\xacH\n2)\xcc\xbe\x85\xa0\xfeirBd\xc7\x89)\x94\x08\x9f&\x11D\xc6IDQ\xce\x97\x9a\x83\x82\x94v\x9d\"\x19t\x1e\x84\xe5O\x13\xa2\xac\xf6sn\x90\x98/\xb54\x8c\x8e\xe6\x93\x86\x11\xc7'\xef4Gs\xe2;\xc5\xbc\xea\xc7G\xf3*\xc88nY\xe7=\x9a\xd7\xf1\x8b\xab\xda*L_P\xaaN`\x98SX ms\n3\x89yNa'\xb1\xd0)\xec\xda\x82\x12\xd5\x11\xa51\xdd\xf1N'\xb2\xdc\xf1\x9c\xc4\x86;\x9e\x97\xccn\xc7s\x93\x99\xedxnmV\x93\x1a\x08\x1f]\x9d\xc8@\xc7s\x12\x1b\xe8x^2\x03\x1d\xcfMf\xa0\xe3\xb91QL\xb7<\xfe\xce\x1f\x83\x07a\x1aqL\x1389O\x94\xc2\xe4zMt\xfc\x18\\\xf1\x08\x92\x13\x84\x05\xa9\x14\xe4%\xe9\xda|[uD\xaa\x98\xfb\xa7\xb4\x03 Ri\x86\xaf\xdc\n\x89\xc0\xf8\x14\x81\x01\"\x15\x811)0\xed\xfb6}\xcf-g9)\x1f\x95\xd18s\xbb\xa7;O+\x9alt\x00\xe8\xb2\xc7\"\xda\xfa^]1\x1e\x00\xd4E\x81\x88~N\xdf_\x86\x18\x94%\"\x0e\xb8\xe2\x90wz\x80>\x7f.\xa2\x0e\x80{\x81\x94\xba\x8e\xef\x8bs;\x9f\xd2\x8f7\x03Av\x8a%\x08\xf2S\x8dA\xb08\xdd\x1e\x04\x93\xd3L\xc2\xa9\x0f\xb2\x8a\x82Y\x14\x86\x9b\xb9\x9d\xcd\xe3'\x98\xca\x7f\x92\xa5\xfc'\x1b\xca\x7f\x06;\xf9O4\x93\xffT+\xc1\x06\xc1'\x19\x04?\xc9 \xf8\xc9\x06\xc1\xcf`\x90'\x0ee\xac\xe6@\x83\xd04Zq\xd5\xaf\xa2\x13\xbc\xe3 \xc3\x05\xc8\x8eA\xb0a\x18\x1c\xd8\xb5\xe3\x07m\x19\xdb{\x06k\x9a&\x87\xf5=\x17\x82Z\x96\xc5A\x01\xd8p8\xe4`\x89\x877\xcd\x85\xef\x128\x1e\x8f9 .\x8c\x0d\xc1m\xdb\xe6%\x0d\xc3\x00\x92\xc1q\x1c\x01k\x00\x8c\x10\x82u\x9b\xdf\xd2d\xc0\x8b~\xf6\x87\xc3\x83P\xf6&g\x85\xd3\xc6:\x0d]%\xd8\xfeQ?\xd3_\x9ce\xb1\xf8Yw\xfc\x93\x80p\xd4B8\x12\x11\x0e[\x08\x87\"\xc2A\x0b\xe1@Dh\xb5\x10Z\"\xc2~\x0ba_Dh\xb6\x10\x9a\"\xc2^\x0baODh\xb4\x10\x1a\"B\xdd\x92\x13\xeaB\xed\xe8\xbd6\xd2\x9e\x98\xd6h%6 \xea|\x8c\xe1\x9c6^\xces\xda3\x1dt\xd8\x82\x88uX\x92\x08p\xd6\x82\x88uV\x92\x08p\xd4\x82\x88uT\x92\x08p\xd2\x82\x88uR\x92H\xa8\x08\xd6AI\"\xc09\x0b\"\xd69I\"\xc01\x0b\"\xd61I\"\xc0)\x0b\"\xd6)I\"\xc0!\x0b\"\xd6!I\"\xc8\x19K*\xd6\x9f(2\xb1+\xf1\x8eH\x11\x82N\x98O`1r\xd9\xc1{\xa8\xf7u~\x9c\xe5\x81\x8bE\xdf0\x07\x82Y\x01\x82\x0f{\x16?\x89\x84\xb1\x1d,\xf9\x81~`\x02\xf3\xf32\xc4<\xd7\xf9\x10@\xee\x11\xc6\xe1\x96\xc6\xf2\xaf\x0e\xa8\xa5\x85\xe0\x7f]\xcc\x17\x86\xcdO\xa8\xd1:\x8e0+\xb0\x85z\x8e\xcdO\xe6\x05w\x90\xc2\xee\x0f\xccE\x0f6J\xe4\x05l\x04\xe2Z\xba>\xe2\xad\xb2\nS\x08\x9d\x99f\xce\xcf\xa9 r\xa4\x0b\xa7v\x10o\x9b.\x1f\x8e\x94\xc1\x10B\x01\x837\xcc\xe1\xd0\xe2\x9b B\xc7\xf6x\xc8\x0b]E\x19<\xc1\x18\xa1\xb9\xc3\xeb$\xb07l@\xa2\xeb\xc6\xbc\xcf\xb3\xce\xa5\x9e\xe35k\x1b]\xef\xf7\xc7|\x08\x03 Mk\x88\\\x91W\x01\xf8\xf1\xc0q\x80 &\xc7\xa3\x04$q\\\x04\x91l\xedd\x85\\\x88`1X,\x16\xbc\xf4%\x01\xa4H4Z\xb8\x0b\xde{K\n\xb8s,\x16\x0e\x9a\x8bH\xa0\xde\xef.\\\xbe\x15d:\x91\"\x10f\x88\xe6\x9aV\xbe\xea\x84&\x80\xde\x7f\xd2\x9d\xc7\xf5\xd0\x1d\xdb\xae\xb7N\xce\xd9\xa1\"6\x18@\xd7\xe8Y1b\xd3\xadq\x8f\x85\x81(\x93EA\xa0>\x032\x00\x8cf\xe8\xac\xe4@R9\xd6\"\x0fc\x067\x1e\x8f\xc7\xc0\xea\xaf\xdew+\xc0y\x92<[iUz!\xd7\x90\xc5:P\xa41\xad\xd8U,\xe0UV\x1bbU\x96\xb5q+\xf7\x16[\xe4\x82*\xe2y\x15\xdb\x81\xa2\x96\xc8\x05kO\xb6\x1cX\xe7\"\xd3Q\"\xff\xe21\"\x17\x03\x90\xb0\x97\x01@\xd0\xd1x\x9c\xc8\xd7\x00\xa4\xc8\xddx\xa8\xdc\xe3\x98\x8c\xdfS\x9c\x8eO\xdd=\xd9\xefT\xa4Sw=\x86\xdb1\xde\xa7\xe0~*\xb9\xbeX'\x12oB\x97d!B\x8f\xe4\x80\x02\x87\xe4p\xb0?\xb20\xa1;r@\xa17\xb2\xc8\x16g|\xb6\x01\x90\xcbN>\xdd\x15\xdbe;\xc2\x13\xfd\xef\xe3\x88\x02\x9fc'!\xc0\xe7X\x88\xd0\xe78\xa0\xc0\xe78\x1c\xecs,L\xe8s\x1cP\xe8s\xc7M\xb9,\xbc6oc \xa2\xa0<\x9e\x06\xfb\x1c\x9b\x80}\xba\xcf\xe1\xe7\xf49|\xb2\xcf\xd1\xfc4\xadx d\xc5\xaeH\xf5\x02/\xe5-\x82\xf8,\xe4d\xa0\xf93\x0eZ\xdeF&\x91\xc0&f\xb6\x84\x08\x03D\xe3\xf2w\xd4\xb5\x0f\xd1\x07\xb8!\xdcn\x8f\xb4-\xd8\x92a\xb5\xc8(\x1cDd\x17\x1e\x08\x9b\x86\xc7\x81\xd6\xe1`\xa0\x818\x14l#&\xee\x15\x9a\x89\xdb\xbe\x17Z\x8a\x0f\xf5\x85\xc6b\xf7\xe2\xebm\xc0v\x83\xa9\x0cl[\"\x1a\x15\x1a\xd1W\xb4!\x8b\x13\x98\x90\x85\xc1\x16\xf4U\x0c\xe8+\xd9\xcfW3\x9f\xafj=68\x16\x1b\xcf?\xc1v\x023\xe1V3aE3\xb18\x81\x99X\x18l&\xacb&\xacd&\xacf&\xacj&6\x9e\x14\x9b \xc3f\xa2\x80\xc9\xcav\xc3\xadf\xd0\xd7\xba\xf3\x87\xe7zG\xef\xf4\xa3]\xa7\x17\xed:\xf4\xa6\xcbD \x05\xd6\xd4\x13\xd54R\xaa F\x815\x99PM\xbd\x92\xbe\xbd]r$Xc_Vc&\xb9\xaeP\x1f\x84\x03k\xb3\xa0\xda\xfa\xa5\xc4m\xb5\xc9p\n\x83\xf0\x01t\xa2lT\xff\xd3\xfcHR\xd9\xf3\xbb\x92\xa0\xb2\xef\xebM-\x95\xb6\x99\xf8x\x87\x12T\xf8,>\xa5\xe0T\n3{\xedi\xfe\x9f\xe8h\xc2\xba\xbe\x83\x9f\x81u}g7\x93\xd6\xd9f\xf4\x13\xbc\x0c\xac\xefOp2\x99?\xe1?\xd1\x9f\x84u}\x07\x7f\x02\xeb\xfa\xce\xfe$\xad\xb3\xcd\xbe'\xf8\x13X\xdf\xf3\xf8\x13Ua\x14\xa3\xfa\x0b\x1e\xda.\xff\xb4E\xfdq.m_~\x08\xa8\xf9\\W\xe2\xc4!\xa6?%\xd2\xcdb@=\xff\xe6\x11\x13\xb0\x15Q\x9f~\x80S\x89E\xa4\xa7W\x9fRb\x8a\xf3\xf0N?\x14\xe9I\xbe>#\xaf\x8f\x0fa\x8b*\x8d\xb2J \xc4-j5\xaaZyD^\xb1QT\xcc\x97fu\xf7\xf2\xba\xf9\xc8\xb8\xa8\xbbW\xd6\x0dD\xceE\xdd\xbd\xaan\x1e\x91\xd7\xdd+\xea\xe6K\xb3\xba\xcb\x86k\xa2\x96\xd7M\x07\x10e\xfdM\xe3\x01L.A\xd5|\xa0<\x97\xa1P\x80&\xd2@\xad\x02\x00Q\xc9P+\x01\xc0\x142\x94j\x00\xca\xab{\xd4\x9a\xb6\xf00>HoS+\xcc\xd0\x07\xde\x99\xb3\x98\x01\xf0\xe7\xc2'\xb3B\xc8-Ko\xcf\x8a\xa5\x0e_\xa4 \x9f\xcf\x1d\xbb\xaa[\xe4\x99u\xf5B\xe7o$\x10\xfb?!\x84\xc0\xc9+9D^Z\xcb!\xec\x08\x8d\x1c\xe2\xbe@\xc8!r\xf8J\x10\x89\xcf75\xc9\xdc\x9e\xa8K\xec\xf9u\xb3\x84\xce_\xcb#\xf6\x7fB\x1eI\x17 \xe5\x11\xf6\x82F\x9e\xb6\x8eP;\xad\xb0/(t\x06\x85p\xb5\xe8!\xbe\xa4\x83\xf8\xd2\xfe\xe1\xb7t\x0f_\xda;|y\xe7\xf0\xdb\xfa\x86\xdf\xde5\xfc\xb6\x9e\xe1\xcb;\x86\xdf\xd6/\xfc\xf6n\xe1\xb7\xf6\n\xbf\xb5S\xf8*}\xc2W\xe8\x12~[\x8f\xf0[;\x84\xaf\xd2\x1f|\x85\xee\xe0\xab\xf6\x06\xffI\x9dA\xe8\xf7X\xe2\xf7X\xea\xf7\xb8\xc5\xef\xb1\xd4\xef\xb1\xdc\xefq\x9b\xdf\xe3v\xbf\xc7m~\x8f\xe5~\x8f\xdb\xfc\x1e\xb7\xfb=n\xf5{\xdc\xea\xf7X\xc5\xef\xb1\x82\xdf\xe36\xbf\xc7\xad~\x8fU\xfc\x1e+\xf8=V\xf5\xfb\xb6\x80\x88&v\x16\xe7\xf6\x82}5j\xf6t\x8e\x16a\x8c\x0e\xe5\xc7{\xcf\xff\xd2\xf9\x0b\xfd\xe5A\x98\xcd\xc1\xc1\xc8\x8e\xcf\xe7a\xbab\x01\x87\xbf=\x86\x99o1\xcfqI\x92I\xc7\x14U\xdc\xf2\x960esqMAYt\xd2N\xb9\x93O\xa3b\x91\x9aRP\xaa\xa6\x18\x12\xac)U\xd8 V\x9d\x8e\x9dl\xa8\x93\x08\xecK\xe5\xf5e\xe2\xfa\xea\xd2\xc2\x82\xc9\x8c[\x17\xc2\x82a\x99`\x98\x12\x8c*u\x03\xd9\xe7\xfc<\xe6S\x81L\xf1\\\xf2A\xc2\xae\xeb\xcd\xdb?4\xd8u\xbd\x94E\x01\xfd\xc5m@`\xa9C\x17k\x0eb\x17\xddn\xaa\xc5\xe1\x96\x81\xc5\xe1\x16Bi\xcb8\\G<\xb6x\xceQ8!^\xfb\x01+A\xfeP\x80\x05+ \x8b8:m\xe1\xed\x90{(\x90\xd8\xde\x87\xeb\xf4<\x7fD\xbc\xfeJ\xa1\x7f\x1c\x18\xdbg=Lf~\xb2\x1c\xf6\x00\x12\x01;\x01\xcfC\xe0\x07\x00\x1046\x89\x83\xbd\x81C\x08\x1d\x82GJ}\x02\x84K\xdd\x02\x10\xa5\xdd3DDR\xe7\xc8\xd73R\xffPp\x10\x85\x01\xd4\xcd\x06:\xa9\xd3\xf8m>\xe3\xb7\xb9\x0c\xcbA\xe41\x1c\x0ev\x18\xbf\xcd_|Uwa\x81ro\x01\xd0rg\xe1\xe4P\xf0\x15\x98F\xee*\xfe\x93<\x05v\n,w\n\xdc\xe6\x14\xb8\xcd)X\x0e\"\xa7\xe0p\xb0S\xe06\xa7\xc0\xaaN\xc1\x02\xe5N\x01\xa0\xe5N\xc1\xc9\xa1\xe0\x140\x8d\xdc)p\x9bSPt\x0b\x8cvu%D\xee\xbd\x0e{5?\xd12\x10\xf9,\xfb\x9dfS\x9a\x08\xe4V\x99\x99aJ\x90\x90E\xc4c^R\xcd^\xa7!\xb5E\x90==7&\x95\x94\xe7F\xc7\xe8\xe4\xd9|\xfa\xb7\xc6\xeb\xf5\xfc\xe7\xea\x85\xa9@\x15\xf9\xe1S\xae\n\xbd\xa9\"\x7f\xe7A\xfd\x13\xc0\xa1\x8c$H\x1ea\xece\xeb\x89\xea\x0b\xe3\x13\xb2\xcc\xf5\xe2\xe2\x95\xff\xe5\x17\xcb\xeb\x9a\x88\x92\x82\xe5\x04|\nH\x90\xc5H@\xf5\xab0\xf6\x1e\xc3 =A\x808\xdc\xb2\xb5s\xfd#/\xdf\xc6vt\xa8\x19d\xbf\x9dg\xffL\xe8_A\xbd\x03\xa4\xc5\xc3 \xfb@P\xaf\x16\xa3\x0d\x8a\x13\x04\xd4_\x15M\xe0\xc7B+6,\x8f\xb6fU\xa3\xd0\x9c\xb4L\xa2R\xd8\xbc2\xb9Z\xcd,\x91\x8c`\x0d\xd8\x1b\x96\xc9K\x91\x9fhIj\xc7)%N\xf1\x19\xfd\xfcyS\x15\xf90\xff9\xff\xbcy\x92\x8f)\x05\x0f\x889\n\\\x805\n\\\x96q\xf6\x88c\x8b\x02\x17bZ\xbe\xe8\x93\xe7[\x14\xb0\xac\xcb\xa7$\xf7\xe2\x11\xc4{n'(\x1b\xc8\x00\xeeU\x11\xcb\xbf~N\xd6P=\x845\x1e\xa3\xd4Y\x81:\xcfKx\xad\x17\x8f\xc9\n\xcag4\xff\x04\xe1Ee\xd0\x8aE\x06\x07\xac\x97A\x85\xc6\xcb\xf9\xe4\xb6\x03\xb84\xa6jxp\x96\xca9T\x86\x02\x98PF\xc9\xf9@6\xc9\xb94&\x01\xf80\xca\xcf9\xc1\xba/uS\xaa\x1e\xd4\x0e\xa9\xe5\x9c\x13\xa8\xe4\xfbu\x92z\x8b=\xd0q\"\xdby`\xfb\x0d\xf1\xac\"\xac\xb2T\"\xedW8\xb6\xf3\xe4\xac\xa8\xbeS?\x01YsF\xa9Q|\x07\xca9\xb1\xfd\x87|\xc8\xd6\x00\x99\xab\xc2\xccQ\xbaE(\xe0+(\x01L\x0d\xd5S\xb6\x8a$\xb2\x1dT1\x83k\xb2\xf3\xd74\x1eh~\xae\x97\xa4\xb17_\xa7H\xc0\xb2\xa0\xa29\x96\x08\xb6\xf7\xe4A\x0da\xc3\xc29\xda,X1\xa3\xbaP\xc3\xaa\xe9Ar{Ul\xd8~\xd4p\xa2\xba\x91\xcc4\x15\xab\xda4<\xaf\xca\x0c43\x89\x11*\x9e\xac\x11\x1a\x96\x84% \xaer;0=\x95\xb4\x04\xd9Qk\x96P_-\x0e\xdf\xea\xccl\xebz\x81\x8d\x8bh\x9c\x88A\xb5\x1c|\xaeO\xca\xffB\x9c\x0c \xa7\x1e\xcb\xc9(9\x19\x10\xa7\x9e\x84\x93\xc9r\xea\x95\x9cz\x10'S\xc2\xa9\xcfr2KN&\xc4\xa9/\xe1d\xb1\x9c\xfa%\xa7>\xc4\xc9\x92p\x1a\xb0\x9c\xac\x92\x93\x05q\x1aH8\x0dYN\x83\x92\xd3\x00\xe24\x94p\x1a\xb1\x9c\x86%\xa7!\xc4i$\xe14f9\x8dJN#\x88\x13\xb6\x93T\xe6\x9cz\xf6?\x96\xe38\xfb\xdf\x84\xf8\x19\x085\x97Y\xd4\xa7\xcb\xd6C\xe5\xbbm7\xe8\\\x9f\xd4$\xe0\xca*\xe7e\xc8\x96o\x0d/\x83\xe0e\x00\xbc\x92U\xec\x05\x0f\x99d\x15i\x80\x966)F\x81\x00\x05)\x89\x0d\x80\xd8\xa0\x88\x0d\x85\\\xdb\x81\xe7O\xe4\xfd\x88\xc6\x9e\xbe\xa4\x86\x18>\xf7\xaaZc\x0e\x0c/\xbe\xcb\xc2\x1a\xac\xe5\xf8\xb55\xcbFmA\xf6\x9c\xcbk\x81\x04\xadK\xafgZa\xe7\xd5W<\x8e^d\xf3\xd4\xa7\xad\xb3a)\x9e\xba\xd4>\xcd\xb8\x7f\xcaj\xfbT\xab\x7f\xbf\x057+\xd1\xf3\xae\xb9a\xee\xcf\xb2\xec\x86Y?\xe3\xca\x1b\xae\xe0\xb9\x17\xdf\"\xfd?\xd7\xfa\x9b\xeabOY\x82\x8b\x18\x1d\xbb\n\x17\xf19a!.bu\xdaZ\\\xac\xa9\x13\x96\xe3\xacY\x9f\x7fE\x0e\xd6\xf0|\x8br\x90\xfd3\xaf\xcb\xc1:\xbe\xd3\xd2\x9c\xb2\xee3\xad\xce)\x9eO^\xa0\x0b\xb8\x9d\xb6F\x170;u\x99.`\xf7\xc4\x95\xba\x80\xeb\xd3\x17\xebB\xc3\x1c\xbb^\xe7\xe7\xeb',\xd9\xe5\xcc\x8e\\\xb5\xcb\x99\x1d\xb9p\x973;r\xed.gv\xe4\xf2]\xce\xec\xc8\x15\xbc\x9c\xd9\x91\x8bx9\xb3#\xd7\xf1rf\xc7/\xe5[\xfc\xf6\x89\xaby\x96\xfb\xe2i\x0bz\x90\xddS\xd6\xf4T\xf7?aY\x0f\xd3\xb3+{\x85\xa5\xbd\xc21\x9a\x9c\xa7\xff\xcc\xcb}\x9e\xdf\xb3\xaf\xf6\xfd?c\xb1\x0fTr\xc2Z\xdf?a5\xf8\xacK}P\x80\xd65\xdfs\xad\xf4\xfd\xa7,\xf4Y\xe2\x13\xd7\xf9\x90\x0cO^\xe6\x9fb\xd7?g\x95\x7f\x9a\xc1\xbf\xe3\"\xdf\xff\x9ek|\x88\xf9\xf3,\xf1!\xce\xcf\xb9\xc2\x87\xf8?\xfb\x02\x1f\xd6\xfd\xb3\xad\xef\xfdgZ\xde\xc3|\x8e^\xdd\xc3lNY\xdc\xc3\x9cN\\\xdb\x8b\xb4t\xca\xd2\xde\xff\xde+{\xa0\x82g\\\xd8\x03\xdc\x9f{]\x0fT\xf1\xbd\x96\xf5\xfe\xf3\xaf\xea\xfd\xe7\\\xd4\x83\xccN\\\xd3\x83\xbcN^\xd2\x83\xdc\x9e\xba\xa2\x07\x99>\xc3\x82^`\x93\xa3\xd7\xf3\xec\xcc\xfc\x94\xe5\xbc\x8c\xd7\xb1\xaby\x19\xafc\x17\xf32^\xc7\xae\xe5e\xbc\x8e]\xca\xcbx\x1d\xbb\x92\x97\xf1:v!/\xe3u\xec:^\xc6\xeb\x84e\xbc\xd4]\x9f\xba\x8a\x97\xae\xae\x8e^\xc4K\x17\x84'\xac\xe1\xfd\xa7-\xe1!\xf2\xe3V\xf0\xa2\xc5:~\xe6\xc5:\xcf\xef\xd9\x17\xeb\xf8\xcfX\xac\x03\x95\x9c\xb0X\xc7',\xea\x9eu\xb1\x0e\n\xd0\xbav{\xae\xc5:~\xcab\x9d%>q\xb1\x0e\xc9\xf0\xe4\xc5\xfa)v\xfds\x16\xeb\xa7\x19\xfc;.\xd6\xf1\xf7\\\xacC\xcc\x9fg\xb1\x0eq~\xce\xc5:\xc4\xff\xd9\x17\xeb\xb0\xee\x9fm\xb1\x8e\x9fi\xb1\x0e\xf39z\xb1\x0e\xb39e\xb1\x0es:q\xb1.\xd2\xd2)\x8bu\xfc\xbd\x17\xeb@\x05\xcf\xb8X\x07\xb8?\xf7b\x1d\xa8\xe2{-\xd6\xf1\xf3/\xd6\xf1s.\xd6Af'.\xd6A^'/\xd6AnO]\xac\x83L\x9fa\xb1.\xb0\xc9\xd1\x8buvf~\xcab]\xc6\xeb\xd8\xc5\xba\x8c\xd7\xb1\x8bu\x19\xafc\x17\xeb2^\xc7.\xd6e\xbc\x8e]\xac\xcbx\x1d\xbbX\x97\xf1:v\xb1.\xe3u\xc2b]\xea\xaeO]\xacKWWG/\xd6\xa5\x0b\xc2\x13\x16\xeb\xf8i\x8bu\x88\x9c[\xac3\xf4\x87\x05\x0e\xed4\x7fG\xce\xe4\x0fz-\xcc@\xe3\x12\x9a\xbf1\xa7\x05\x1b\x94\xd8\x93\xde\x82\xb4\xc8\xdf\x82\xa4.W\x83V\x12\xad\x81+\xbcYH\xfd\xfc\x81\xe6\x1f#\xb2\x7f\x94\xc4\xbe\xba\xc0\xb0l\xc7\x98\xb9\x06\xab\xc9\x86)\xd9\xa8\xd2\xc4\x0e\x12-A\xb1\xb78,\xc2 \xd5\x16\xb6\xef\xe1\xfd\xb9fG\x11FZ\xb2OR\xe4\x9f]`/x\x98\xd9\xce\x87\xfc\xd7\xd7a\x90\x9e\xd9\x1b\x14xq'@\xbb\xea\xe7\xb3\x15\xc2\x1b\x94-r\x9b\x9f:\x01Z\xa3\xb3\xf5|\x1d\xa4\xeb\xb38\x9c\x87ix\x16d\xff$h\x19\xa2\xce\xda;\xb3c\xcf\xc6g\x8d\x14\x8ct\x9c`K\x14\xc6K\xcf>\x83\xc0\xb9t\x9a\xa0E\xc2*J*\x9e\x80\xc7:\xa1\x8b\xa8\xf7\xa0e\x0f(\xa2Wa\x90\x84\xd8N\xce\xfc0\xb0\x9d0\xfbO\x98G\x13,\xa3u\xec\xa1\x98!\xcd\x9fun2\x95\x96\x00\x11}\xad`\x8a\x03\xa3\xf6\xc6\x1e\xa2\xb6\x17\x86\xa3x\x00v\x15R\xa7+\x84\xed\x84&/\x9e\x9dI\xccT\x16\xa9Z5\xf5|D\xd7\x91?\x81\xa0\xf3\xd0\x0d\x03\x8f\xc2^\xe4\x8f:\xb3\x8f\x10\xde\xb1\xb1\x97\xa4!m\x85\xe2\x99\x80bi\xc7\xb6\x1f\x06.-|\xf9\x10\x14\xc9N\x1eP\xbc\xf10\xa6\xfd\x84x\x0e\x91\x95\x8d(>\xa1\xe5\xa56\xf6\x98\x0f_/\x12\xad\xc8\xc3\x91\xc0\xe2\x89\xc2`I\x8f=\xf9;\xafT\xebc\xb0e\x95\nu*\x0c\xd0^6\x88\xaa\xca\xe1\x1f-\x06X#V\xaf\x11\xd25\x8d%M\xb2-r\xc8}\xee\x93\xefT1\xf7E\xf8\xc5\xd6\xa0\x00\x06\x0f\xe8Q\x80\x1e\x0f0)\x00\xf7y\xfa\xc5\xb6/\x17q\xb1\xb5(\x80\xc5\x03\x06\x14`\xc0\x03\x86m\xcd\x1cQ\x80\x11\x0f\x18S\x80\xb1~\xfc\x9b\xba\x19\x8f\x15Z\x84E@Fa1\x90]X\x0cd\x1a\x16\x03Y\xa7U\xe2E\xf1\xb9\xb36\x1b\xb1\x18\xc8L\nm\x1f\xb1\x18\xc8X,&\xb3\x97\x82\xc1\x14F\x05\xba\xbf\x8b\x8d\xe8\xb7\xb5\xc3` \xa0 \xfdv\x0b\xfa\xed\x06l\x11v\x91\x7f\xed\xac\xd5|~\xbb\xf5Z\x1b=b \xa0\xed\xfc#M'\xb6R\xdb\xe0\xc7\x00@+\xe1v+\xe1v+\xe1v+\xb5\x08\xbb\xc8?v\xd6j%\xdcn\xa5\xd6F\x8f\x18\x08h%\xcc[\x89\xc2xA\xb4N\xb5\x18%\xa8\xb9\xdfnG\x11\xb2c;p\x8a/qN4?|d\x1f2&Z\xa7i\x18\x14l\xce\xcfs\xfc\"t\xd6\x89\xe6\x05\x01\xfb\x16`\xa2F\x1eZ~\x86\xed\\\x9fD\xb6\xebz\xc1\x92]\x18\xaf\x8cC\xb9\xd1\xca\xbf>y\xd5\xab\xca\xf8\xd7\x19\xaf\xcc\xaa\xac\xcf\x97\xf5\xab\xb2\x11_f\xd5\xf5\x0d\xf8B\xadW\x17\xf7\xac\x17l\xa1\xa5W\x85\x16\xfb\xa9\xe5\x956\xac)\x87<\xa5\xa1\xd7\xa4\xfcg\x9a\xf3\xcd\xe6\x1cBl;\xf3\xb0\x0d-\xddf\xc5\x15\x93\xf2\x01\xc5\xa4\x84@1-#\x0b\xc8D\xdb@R\xb2\xc0U\xf1\xce\xb9\x12\x90\xfd\xcc\x96{\xc1\n\xc5^ZA\xca_\x15\xe6\x89\x03\xe39\xd9t#q\x1e\xa2\x18\xf2\x1f\xa2\x18r!\xa2\x18\xf2\"\xb2n\xd8\x91\xc8\xea!_\"\xcaAw\"\xcaa\x8f\"E\x10;U\x86j\xf7+JX\xd0\xb5(qA\xef\xa2\x04\x86\x1d\x8c\x16Y\xecc\xbc\xd0\xb0\x9b\x11\xfc$\x9eF\xa0*gS\xf06\x85\xa8d\x95E\x132\x0f\xf4\xa5\x0e\xe8K\xfd\xcf\x97\xba\x9f\xdf\xe6}\xbe\xdc\xf9|\xb9\xef\xf9-\xae\xe7\xabx\x9e\xaf\xe2x~\x9b\xdf\xf9mn\xe7\xb7z\x9d\xaf\xe6t\xac\xbc\x02\x9f\xf3U\\\xce?\xce\xe3`\xe7\xc2R\xe7\xc2R\xe7\xc2R\xe7\xc2R\xe7\xc2m\xce\x85\xe5\xce\x85\xe5\xce\x85[\x9c\x0b\xab8\x17Vq.\xdc\xe6\\\xb8\xcd\xb9p\xabsa5\xe7b\xe5\x158\x17Vq.\xcc9\x17\x05Lc\xdby@\xee\x01\xa34E\xb1\x96D\xb6\x93E^]\x83\xfb>E\x01\xd4\xd2\x8c\x19\x0b\xd7\xba\xba%\"\xf0\xd1\xd2\xe6\xd8\xf72x\xfb\xb8z\x009\xe6\xdf/:F\\\x80\xa2Mb\xa8\x92\\h\x05\xa9\x15f\x83\xba\xaac[\xc2\x11\xb46\x84\xafB\xa1\x1d\x12\x91\xf1\xb1\"s\x04\xad\"\xf3U\x14\"S\x14x\xa5%!\xf6\xdcC\xbe\x8f^u\x16\x0e\x93z)F4\xa6\xdb\xb38\x98\x13F{\x06e)\x98\xfa\x00\x8a\x94;O\xbbT\x1cL$\x18\x0f\xb4\x9e\xc9\x0fk\x89}%\x81}EyY\\\x9b\xb82\xc9\xb0\x92dXQ2\x16g\xb1^\xe5\x05\x0f\x87\x14\xedR\xcdEN\x18\xdb\xe5 Vv\xd1\x9b\xc1\xce\xb8'\xe7\xb6\x93z\x1b\x04\x14\xe4\xcb\\\xe0\xf9*\xdc\xb0k\xe4\xfc\xb9\x80\xff\xc6K\xbc\x145o\x1cMc;H\xbc\xea\\g\x18w\xba\x86\x95t\x90\x9d \xcd\x0b&\xd2R\xbe=\x85\x90\x87p\x9df*:7\xa2]\xc7\x0d\xd3\x14\xb9\x1dg\x1d\xc7(H_eLX\xba$=d\xff\x14Yn-\xddGP\x8e\xc0\xdf\x16\xab\xc1\xda\x15\x81\xd9zk\x90\xe5\\,\xe1o{D9\x1f\xc6\xf8[\x93(\xe7\x03\x19\x7f\xdb'\xca\xf9P\xc6\xdfZd\xfd|0\xe3o\x07\x04\xc0\x84$\x18\x92\x12@U\x8c\x08\xc0\x00\x92qL\x00\xc6\x90\x0c\xc5+\xd4\x1b\xd0I\x9b\xf1\x859\xf2\x85\x93\xdc\"\x0c\x042\n\x0d\x01\xedBC@\xd3\xd0\x10\xd0:\x8c,\xa0\x81h\x0cl#F\x1a\xd0L4\x06\xb6\x14\x8d\x11\x1b\x8b\xc6)\xec\xf6\xab\x8e\xdd\xa5\x15\xfdV#\xfa\xad6\xf4[M\xe8\xb7Z\xd0o5\xa0\xdfn?\xbf\xdd|~\xbb\xf5\xfcv\xe3\xf9j\xb6\xf3\x8f3\x9d\xd8J\xb8\xd5J\xb8\xd5J\xb8\xd5J\xb8\xd5J\xb8\xd5J\xb8\xddJ\xb8\xddJ\xb8\xddJ\xb8\xddJX\xcdJ\x98\xb3\x12\x05\xdb\x1a\x07\x91Z\xb7\xbd\x83H\x9f[\xf3 R\xe4\xb6\x7f\x10ipk\x1d\x84\xaa\xcb<\xa1*e=`\xab\xf5\xaa\xb2\x1ePVq\xe5\xd6\xd0[\xcd\xac\xe8L\x9e\xce\xac\xda`\x9a|Y\xd5\x08\xb3\xcf\x95\xf5+\x9e}\x9e\xa7U\x95q\x0b\xf6\xad6\xa8\xca\x06|\xd9\xb0*\x1b\x02eU\xfb\xb8U\xfeV\x1bUt#\x9en\\\x95\x8d\xf9\xb2,\xe0\x10\xf5\xb7\xad\x96\xae\xbc\xd8\xad\x95\xd35\xb3\xff\xf1\xa0mX\x00\x93\xaaY\x83\xee`0\x18\x0c9d\x9e\xc7.0\xf9b\xbc}\x80?0.\x9aM\x13b/mJ!GmJ!_mJ!w%\xea\x85=\x96\x00@NKH\x06\xf9-Q\x0c\xb9nS\x0cz/Q\x0c90Q\x0c\xf90\xa1\x16\xc8\x8d\x9bb\xd0\x93\x9bb\xd0\x99\x9bb\xd0\x9f\x89b\xc8\xa5 \x9b@^\xdd\x14\xc3\x8eM\xdaD\xe0\xdb\xa4\xeaZ\xdd\x9bh\xab\xcc\xc3\x1bX\xee\xe4\n^\xae\x10\xc6\xe4\x01\x8a\xc4\xf3}\x99\xe3\xfb2\xbf\xf7en\xef\xb7x\xbd/uz_\xea\xf3\xbe\xd4\xe5}\xa9\xc7\xfbR\x87\xf7\xa5\xfe\xeeK\xdd\xdd\x97z\xbb/uv_\xea\xeb\xbe\xd4\xd5}\xa9\xa7\xfbrG\xf7[\xfd\xdc?\xc2\xcd}%/\xf7\xd5\x9d\x1c\xf6g,\xf3g,\xf3g,\xf3g,\xf3g\xdc\xe2\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xee\xcf\xb8\xd5\x9f\xf1\x11\xfe\x8c\x95\xfc\x19S\xfeL!\xc2\x0d\x8a\x178\xdcj\x1b/\xf1\xe6\x18\x1d\xaa\x07\xe7\xe5\x03\x01|\xe5\xb9.\n\x1at\xf1\xbb\x00\x9c8q\x88q\x03.~\x17\x80\xf3H\xaa\x86\xf2;\x1b5p\xc7\xc9\xac\xedZ\xa4\xde\xb1rk;\xb9\xe4;Vvm'\x97~G\xcb\xaf\xedd-\xd8\xf3-\xd8\xb7\xb4`\xcf\xb5`/o\xc1\x9ek\xc1^\xde\x82=\xd3\x82\xfdi\x01-\xebXY\xe8p\x94oQ\x04\n\xeeE\xe1[=\x8cB\xab8\x19I\xa0\xecg\x0c\x91\x92\xab14\n\xde\xc6P\xa88\x1cE\xa2\xeas\x0c\x91\x92\xdb14\n\x9e\xc7P(\xcc\xc1\xaa\x81&\xe7\x92\xfe\x91\x1e\xe9\x1f\xe7\x90\xfe1\xfe\xe8\x1f\xe9\x8e\xfe \xde\xe8\x1f\xef\x8c\xfe\xb1\xbe\xe8\x1f\xed\x8a\xfe \x9e\xe8\x1f\xef\x88\xfe\xb1~\xe8\x1f\xe9\x86*\x1e\x87\x8f\xf48|\x9c\xc7\x1d3\xc7\x92`%\x8f\xc3'x\x1c>\xde\xe3\x8e\x9dki\x02%\x8f\xc3'x\x1c>\xde\xe3\x8e\x9dsi\x02 XKR;\xf5\x9cCq\x055\xcc\xdf\x8d\x91\xb2\xb7Ob\x84\xf3;\xa2\x0d\xaazB\xe3\xecy\x12\xe2uJ\xe0\xaa'4\xae\xf8\xa8~\x0d\xca\x7fU\x18\x8e\x0f\x80\xe0\xd9\xc8\xae$;\x05\x94\x8bOA%-\xa0pE#\x14Z\xa10\xa9\x94M\xf3\x15[\xe6+7\xccWk\x97\x7f\\\xb3\xc4-\xc0\x8a-\xc0\xca-\xc0j-\xc0\\\x0b\xe8N\x92'r\xc3\xc8v\xbct\xcf\xbdu@\x1b7e\xdd1[8\"\n\xd9\xbb\xe9\xda\x90(d/\xc1k\x03\xa2\x90\xbdm\xafYD!{\xad_\xeb\x13\x85\xec\xfb\x034\x93(d_T\xa0\xf5\x88B\xf6\x8d\x08\x9aA\x14rJ\xd0\xad\xa6P\xe7$\xd2{d1{0\"\xd4\x1a\xce\xccy\xfb8L\xed\x14i}\x8b>o\xb0\x08c\xff\xbc(\xfb\xb1o\xb9h\xf9\xd3D\xf0\x1cd7\xd6\xc5\xec\xc6:\xcc\xaex\x0e\xb23L\x89x\x86)\x90\xaf,\x809\x8e$\x12\x1a#\x81\x88e\x01\xc8\xb1\xd7\x93\xc8\xd8\xeb d,\x0b`\x8eC\x89\x8c\xbd\xa1@\xc6\xb2\x00\xe4h\x1a\x12\x19MC cY\xa00\x96\x1e`\xd7\xd2\x88\x0f\x1c<\x8fwI9\x9e\xe6`R\x96\xa7\xfa\x98\x9c\xe9\x89n&ez\xaa\xa7\xc9\x99\x9e\xe8lR\xa6\xad\xfe\xa6\xe0p\n\x93w\xe3\x85\xfes;\xa1\x84\xe1\x89>(\xe1x\xb2\x0b\xcax\x9e\xea\x81\x12\x9e';\xa0\x8c\xe7\xa9\xfe'\xe1\xf9D\xf7\x93z\x1a~nO\x930<\xd1\xd3$\x1cO\xf64\x19\xcfS=M\xc2\xf3dO\x93\xf1<\xd5\xd3$<\xdb=\x8db:\xc7\xb6\xf3\x90EP\xf9y\xce\xf3x9\xb7\x7f\xd4\xcf\xb2?\xdd\xf1O\x10t\x04AG t\x08A\x87 t\x00A\x07 \xd4\x82\xa0\x16\x08\xedC\xd0>\x085!\xa8 B{\x10\xb4\x07B\x0d\x08j\x80P\xdd\x02\xa0:\xdb\xae\xed\xca+\x02\xde\x02\xbbJp\x8e}qf\xe8\xfa\x0b\xded\x05|$\x82\xb3f+\xe0C\x11\x9c5]\x01\x1f\x88\xe0\xac\xf9\n\xb8%\x82\xc3M\xed\x8b\xe0\xac\x19\x0b\xb8)\x82\xb3\xa6,\xe0=\x11\x9c5g\x017Dp\xd0\xa4%\xf6\xaf:{\x93:@v\xacQ\x10\xc3`V`\xae\x1d?h\xcb\xd8\xdeW\x08\xd3dVw\xbe\xe7R\x00\xcbb\x96ad\xe1p\xc8\xacG\x13\x0foP\\\x15s\xefB\xc3\xf95\x0b\x1ad\xdb6#A\x18\x06\x94\x08\x8e\xe3@lH\x08B\x08\xd0E\xae\xdd\n\xb2\xe8g\x7f\x00\xf5\xd7\x80\xc5\x02PV\x8c\xdc\xba\x92\xa1\xde\xd7\x19\x0cQ\xbcX\xf4\x0ds\x00IJ\x81\x86=\x8biN\x18\xdb\xc1\x92\x10c\xc0]\xe9_\x86\x98\xe00\xe7\xae\xd9\xef\x11\xc6\xe1\xb6Dd`H\n\n\xf4\xd7\xc5|a\xd8\x8cy\xa2u\x1c\xe1Z\x10\x0b\xf5\x1c\x9b\xbd\x9c\x90s\xa2qv\x7f`.z\x80\xea\"/\xa8=\xd1\xb5t}\xc4\xe8n\x15\xa6\x14&S\xe0\x9c\xb1\x10]>\xd2aW\xa0Q\xb6\xe9\x0eA\xb7G(\xa8{\x869\x1cZ=\xd6\xb3I\xc0\xd8\x1e\x0f\xfb\xb0\xdf\x11\xb01Bs\x87iW`o\xf6M'5\xe6\xfd> \xcd\x1c\xafQ\x03\xea\xf7\xc7\xec\xcb\n\x88r\xd3\x1a\"\x17\xb4)\x89\x1a\x0f\x1c\x87u\xe1\x1c\x85\x12\x1a\xe8\xb8\x88\x03n\xedd\x85\\\n\xb6\x18,\x16\x0b\x04\xc2(\x15\xa0\xd1\xc2]X \x8eq\xb9\xc5\xc2As\x10H\xf5\x10w\xe1ro'\xc3a\\_\xb1/\x80\xd5-AZkK\xad\x8e<\xe6\xb6\xf3\xb0,\xde\x91ZPH\x83\x90\x8ap\xd4B\xc8\x85$\x15\xe1\xb0\x85\x90\x0bP*\xc2A\x0b!\x17\xaeT\x84V\x0b!\x17\xbcT\x84\xfd\x16B.\x94\xa9\x08\xcd\x16B.\xb0\xa9\x08{-\x84\\\x98S\x11\x1a-\x84\xdc\x0cY\x11\xea\x96\x9c\x90\x0b\x81\xe6K\xad\x8e\x828\xca\xb6\x80\xa8&\x86\xdc\xa7-<\xaa\x89!\x17j\x0b\x96jb\xc8\x8d\xdaB\xa7\x9a\x18r\xa5\xb6@\xaa&\x86\xdc\xa9-\xac\xaa\x89!\x97j\x0b\xb2jb\xc8\xad\xdaB\xae\x9a\x18r\xad\xd6\x00\xact/\x9e\x92\x0f\xc7\xe6K\x8d\x88\xc8x\x02.8\x9b/\xb5&>\xe3\xf1\\\xa86_ju\xb4\xc6\xc3\xb9\xc0m\xbe\x14A\xb90n\xbe\xac\x824\x1e\xcc\x05u\xf3\xa5F\xc5u< \x17\xe2e\x92\xd7Q\x1e\x8f\xe7\x02\xbe\xba\n\x01\x01\x17\xfeU\xba/\x02<\x9e\x00\n\x06+\xc7\x80\xe0\xect9_\x16+\xe4\xc8\x8eQ\x90\xf2\x14D!l\xe3l\xc2\x03\xda\x01D\x98\xf3\xa5\x00\x0c\xc5\x9b\xb5\xa2D$|\xf49_je\x00\n\xe1\xf9X4s\xa3,\x1c\x85\xd0|d:_VA\x00\x87\xe7\xe3\xd4Zz\x11 \x18\xb5\xce\x97U@\nt\x02 \x86\xadk\x11RA\x11me\xb8<\xd4\xe4I\xa0\xf8v\xbe\xd4\xea\x10\x176\x1f\x1b\xedfM\x11\xa1\xf9\xd8\xb7i\x88\x88\x86\x8f\x84\x9b1&\x8b\xe0\x80A \x88\x8b\xf3\x81C\x00\x07\xa2d\xa2\xb3\xc2DP\xcc\x9cu\xd8,l\x86\xc6U>\x82\xaeZ\x91\x87\xab\x10 \x10O/Eh(\xba\xae\xdb \xa0\x81b\xed\x8a\xa6\x0e\xb7\x81\x81\x0d\x88\xbc\xb3a\x87\x08\xbe\x013\x02qxC$R2\x14\x957T\xe2\x0e\x06\xc4\xe8\x0d\x99hT\xe1#\xf6\xf9\xb2\x0e\xd79\x020r\xcf\xef\x97\x17s%t\x07\x9d,\xce\x7fn\xd6N\xec\xbb\xd7rd3\xf3\x8a\xb9\x11\x18\x8a%71\x17\xf0zn\x16sl \x14Cn\xe6.\xd0\xd5\xe4-\xe6W#(v\xdc\xcc^\x80\xe5\xacx6\xdc\xac_\x00\x8bY\\\xcc\xa8,\xa7Xq1A\x01%\xc3\x021C\nE\xb1\xe5\xe2\x86R+U\xe8 Q\\\x0d\xa1\x18r\x81\x05)\x81\x9c#\x81\xa1Xr\xa1\x07\xe1[y8\xd1\xe2\x7f\x05\x86b \x05'\x05E\x0bC\x88\x17;\xdc\x10\x1dI\x1b\xeb-]-C\x90\xecd+h\x92l\xd4\xcax$f\xcc.\x8fH\xb2a+\xe3\xa1\x981\xbbt\"\xc9\x06\xad\x8c\x07b\xc6\xec\xb2\x8a$\xb3Z\x19[b\xc6\xec\x92\x8b$\xeb\xb72\xee\x8b\x19\xb3\xcb1\x92\xcclel\x8a\x19\xb3K5\x92\xac\xd7\xca\xb8'f\xcc.\xe3H2\xa3\x95\xb1!f\xcc.\xf1\x88\xae$\xed 5\x82d\xdc\x96' Ie\x9d\xa4F\xc8\x98\xc3\x1d\xa5J%\xb41\x1f\xca\x99\xc3\x9d\xa5J5\xb41\x1f\xc8\x99\xc3\x1d\xa6JE\xb41\xb7\xe4\xcc\xe1NS\xa5*\xda\x98\xf7\xe5\xcc\xe1\x8eS\xa52\xda\x98\x9br\xe6p\xe7\xa9R\x1dm\xcc{r\xe6p\x07\xaaR!m\xcc\x0d9s\xb8\x13\x95\x81\x9e\x98w\x05 Y\xcb\xa2\xc3e[HW#\n\x8e\xd0\xd2\x00\x0c\x17\xa9\\\x8d\x94=\x174\x02\x8b\"8~$\xd3;\xd2*\xd8(\x12X\xb2\xc0\x01%\x91\x10\x92V\xc0\x84\x95\xc0\xb2\x19\x8e0\xcb\x0c\x92\x94\xb7\x94\xaf \xe4\xac\xd3MR\xceT\x84\x08,\xc9\xe0\x18\x94\xc9NIk\x00\"Q 9\x00\x07\xa5dJK\xae|&4\x05V\x89p\x94J%\xc1\x14\xda!\xadC\x10\xb6Ry\xb3\xf6~@\x06\x9c\xc0\xbaP\x18\xc7V\xa96i\x0d-\xcc\x05\x81-\x95\x98\x93\xf2'q\x82Z\x84i\xbc\x9a\x89B \xbddci\xae\x1a\x85\xb0z\xa9\x12Y/\xd9\xe0ZZ\x93 \xce^\xaa\x84\xdaK6\xda\x96\xd6$\x08\xbc\x97*\xb1\xf7\x92\x0d\xbf\xa55 \"\xf1\xa5J0\xbed\xe3qiM\x82\xd0|\xa9\x12\x9d/\xd9\x00]Z\x93 V_\xaa\x84\xebK6b\x97\xd6$\x08\xde\x97*\xf1\xfb\x92\x0d\xe1\xa55 \xa2\xf9\xa5J@\xbfdcziMpdBl\xf6\xb5\x8fA\x92\x9e\xab\x16\xef\x13\xbb\x83\n\xb5\x89{\xaf\xda\x02\x80\xd8NT\xa8M\xdc\x83\xd5V\x04\xc4\xfe\xa3Bm\xe2^\xac\xb6D 6,\x15j\x13\xf7d\xb55\x03\xb1\xc3\xa9P\x9b\xb87\xab-\"\x88-Q\x85\xda\xc4=ZmUA\xec\xa1*\xd4&\xee\xd5j\xcb\x0cb\xd3U\xa16q\xcfV[wT;l\xe2\xaajDQO\x15\x14\x01\xdbo\x05^\xca\x8c\xe3\x03\xed\xcc\x15\xd0zsN\xcc\xad\x810<\xf9\xad\xbb\x82\xa0\xd8\xbd\x133,\xcb\x19n\xfc\xc6^\x81^\x86X\"\\^\xcap\xe27\xfd\nl\xb1\xc7 \xe6U\x96\x93\xdc\xf8-AR'm\x0c)\x14-$\xb0mX\xd0\x14{\x80b\x9ee9\xc5\x0d\xdaT$%h\xe3I\xa1(\xce\xd0\xc6#\xe1\xb0\x91\xe0\x05\xbd,\x84\xe2 \x9f\xbc\xcb\x08\xaa\xcdI1\xcb\x1a\xc1\xb97\xbbsYjK\xca\x0d\xe2\xc4\xefjR:\x92\xf2#0\x0cW~\xdf\x93PQ\xbec\xd6\xa2\xc6\x02Cq\x85vF\xcbN!g\x08\xf1\x02\xb6M\xc96\xb5p$A\x14_hg\xb5 \xec\x8dd\xcd\x98\x97R\x9c\xa0]WB?s\xbc\x968x\x03ax\xf2\xdb\xb2\x05\x81\x9c\x1d\xcf \xda\xb2%U#\xe7G`h\xed\x01\x9b\xba\x04E\xb5\xaf\xdb\xc2\xb8\x86Q\xbc\xa1\x9d\xdf\x82\x88\xd8\xfc\x15s&A\xb4\xaf\x03\x9b\xc3\x14I\x8b+Q(\x8a3\xb4\x81L\xd1\xb4\x0d\xc74\x8c\x96\x1a\xd8e\xa6\x88\xa43$\x81a\xb8\xf2\xfb\xd0\xa5\x07-\x15b\x02\x12T\xf0\x05\xd2&\xc2\x08\xa18\xa6#\xe5.c,\x0e\x19\xc8#=R\xf6l\xe0\x00U\"\x8a!\xeaC@\xd2\x1a\xa8H\x02b/\n*\xca3CR\xe6Dh\x01\xb1\x16E\x19\xf5\x01#)s\xca 9\xf6\xa2\xb0\x839\x8f\xa4\xa0}y=\x928\xa4>\xc4$\xad\x84\x8a\x19x\xf6\xe2\xc0\x849\xf3\xa4\xd0\x92\x96\xaa\xc4\x91\nyP\xaa\xbd\xb3\x11\xb37_\x898t!\x8eVI\xeb`\x02\x18\xb8\xdf\xc1\xb1Ly\x16Kn\x0f9kQpC\x1d\xdcR\xb1\x85\xbc\x1aQ\xb4C\x9d\xf5j7\x059\x07\xf0\xd5\x88\xc3\x9f\xeax\x98\xbcw\xcb\x99\x0b\xe3!\xfa0\x99\x82\xae\xe4\x15\x89\x03\xa4\xf2\x00\x9a\xb4\x06\"L\xe2Y\x8b#&\xf2\xb4Z\xbb\x19\x889\x1e\xaaD\x18B-\xdb\xf9KY\x8bc*\xea0\x9c\x82 \xa4\xd5\x88\x83,\xf6\xfc\\{ML\xa8\xc5W&\x8e\xba\xe8Sw\xd2\xaa\xf8\xd8\x0b\xe8\x84\xc20\x8c9\xa9\xa7R\x93\xdc\x85\xc5q\x19{\xbcO\xa5\xae\xb6 K\x18\xa8Q\x87\x02Uj\x92\x07&\x92\xc8\xadu\x17\x99\xc0\x08*\x00\xf7\x94#[?\x08\xbe\xdf\x1a\xd9F]\xd4\xedY\xdc{j#\xbb\xd7\x94C\xc5f]\xcc\xbfY7\xb2\xfbu)\xffj\xdd\xc8\xb6\xeaR\xfe\xdd\xba\x91=\xa8K\xf9\x97\xebF\xf6\xb0\xa9\x97\x7f\xbbn\x84\xeb\x06k\x18-R\xae\xd5\xd8\xa0\xcb\xc1\xa6\xe3\x1e\x03\x820&\x8d\x01\x94\x80\xfb4\x04\xd0\x04\xb6h\x08\xa0\x0e<\xa0!\x80N\xf0\x90\x91\x05PL\xdc(&\xce\x06\x16N3\xb1\xc1\x00@\xd5\xc4=\x16\x05\x81L\x06\x04('\xee3\x18@;\xb1\xc5`\x00\xf5\xc4\x03\x06\x03\xe8'\x1e\xb2\xf2\x00\n\x9a7\n\x9a\x87i\x1a\xfa\x9c\x86\xe6\x06\x8b\x00U4\xefq0\x08e\xb2(@I\xf3>\x0b\x02\xb44\xb7X\x10\xa0\xa6\xf9\x80\x05\x01z\x9a\x0f9\x99\x00E\xa5\x8d\xa2\xd20\xe2\xb4\x94\x1aT1\xa8\xa2\xb4Gc \x88IA\x00\xe5\xa4}\n\x01h&\xb5(\x04\xa0\x96t@!\x00\x9d\xa4CZ\x0e@!\x1bF!\x93\x16?\xda@\x1ab\x89@\xbdm\x00\xbdq\x84\x10\x1d\xafL\x96\x0cP\xf0\x86W0K\x05(}\xc3+\x9d\xa5\x02\x0c\xb1\xe1\x0d\xc1R\x01\xc6\xd9\x00\xc6\xe1\x1a\x06Xl\xc5\xce\x125\x11<6\xae\xc0Y\x83!\x02-\xb6\x82\xa6\x12\x96\x10\xa2\x03\xa6\x17\x86\x0c\xb0\xd8\n\x98q\x18*\xc0b+`\x12b\xa8\x00\x8b\xad\x80y\x89\xa1\x02,\xb6\x82\xa6*\xb6a\xc0\xc7\x85l\xfd\xe0\xdb\xf1\xd2\x0bX\xdb\xf8\xb6Q\x95@\x06\xf0\xed^]\x0c\x95\x9aU)\xf0\x95'\xbb_\x15\x02\x9fU\xb2\xad\xaa\x10\xf8Z\x92=\xa8\n\x81\xaf-\xd9\xc3\xbaN\xa0\xa1\xb8j(\x18\xbf\xf8\xd8\xa0\x8a\xc1&\xe3\x1e\x8d\x81 &\x05\x01\x1a\x8f\xfb\x14\x02\xd0\x00\xb6(\x04\xa0\x06<\xa0\x10\x80.\xf0\x90\x96\x03PH\\+\x04\xec\x9b~l\xd0\xe5\xa0J\xe2\x1e\x03\x820&\x8d\x01\x94\x12\xf7i\x08\xa0\x95\xd8\xa2!\x80Z\xe2\x01\x0d\x01\xf4\x12\x0f\x19Y\x00\xc5\xcck\xc5\xc0\xf3\x8c?7\x18\x00\xa8\x9ay\x8fEA \x93\x01\x01\xca\x99\xf7\x19\x0c\xa0\x9d\xb9\xc5`\x00\xf5\xcc\x07\x0c\x06\xd0\xcf|\xc8\xca\x03((\xad\x15\x04\xc4)~j\x90\xa5\xa0j\xd2\x1e\x05\x81\x10&\x89\x00\x94\x92\xf6I\x00\xa0\x91\xd4\"\x01\x80:\xd2\x01 \x00t\x91\x0e)\x19\x00ElhEL\xe4n\xb3\x01\x143Qp\xa4\x0d\xaf-\x96\x0c\xa2\xe248i\xf5\xb4\x0d\xa7\xd4I\xab\xe7m8=OZ=q\xc3\xa9~\xd2\xea\x99\x1b\xde\x1al\x83\x00\x0b\xad\x98Q\xbf\"\x81\x87\xbc\x154 \xd0$\xa0\x85V\xc0\xc4\xc0\x90AT\xfc\\A\x13\x01\x16Z\xf1\xb3\x07M\x03Xh\xc5\xcf'4\x0d`\xa1\x15?\xc3\xd04\x80\x85V\xc0\x9c\xc34(\xb7P\xfb[-\xe9\xd7\nFv\xfer\xce2\x96\x01\xf2-d\xa9 \xe5BA \x84I\"\xc0\xc4\x0b \x00s/$\x00L\xbf\x90\x000\x03C\xc9\x00&a\x08\x84(\x0f\xc3A\x04\xa9\x18\x1e\x07\xc1L\x0e\x06&d8\x14\x98\x93\xe1P`Z\x86C\x81\x99\x19^.09C\xc2D\xf9\x19\x1e#H\xd1\x00@\x08g\xf280Q\xc3\xc3\xc0\\\x0d\x0f\x03\xd35<\x0c\xcc\xd8\x00\xb2\x81I\x1b\x12'\xcc\xdb\x00 A\xea\x06BB@\x13\x00\x82 \x1c\x00\x07\xe6p\x00\x1c\x98\xc6\x01p`&\x07\x92\x0fL\xe6\x90@8\x9f\xc3\"\x04)\x1d\x0e\x06\xa1L\x16\x05&vX\x10\x98\xdbaA`z\x87\x05\x81\x19\x1eN&0\xc9\xc3)\xaa=\xcf\x03kN1\xd5\x03\xeaS-\xdb\x03)Y)\xe1\x03)^)\xe7\x03\x19C)\xed\x03\x19H)\xf3\x03\x1aM-\xf9C\x92*\xe6\x7f8\x92cR@<1D\x0b\x91\xc2\xd3\x9aJ\"\x88#T\xcd\x05q\x84\xaa\xe9 \x8eP5#\xc4\xb7Q9)\xa4\xe5\xdfs\x8f\xe1\xbc\x10Q(H\x0d\x91\x08\x08`\x12\x000AD\x94\x839\"\xa2\x1cL\x13\x11\xe5`\xa6\x88\xac\x1fL\x165\x00Q\xbe\x88E\x08RF\x1c\x0cB\x99,\nL\x1c\xb1 0w\xc4\x82\xc0\xf4\x11\x0b\x023H\x9cL`\x12\x89@\x89\xf2H\x1cD\x90J\xe2q\x10\xcc\xe4``B\x89C\x819%\x0e\x05\xa6\x958\x14\x98Y\xe2\xe5\x02\x93K\x04L\x98_\xe21\x82\x14\x13\x00\x84p&\x8f\x03\x13M<\x0c\xcc5\xf100\xdd\xc4\xc3\xc0\x8c\x13 \x1b\x98t\"pp\xde\x89\x01\x08RO,\n\x02\x99\x0c\x08L@1\x180\x07\xc5`\xc04\x14\x83\x013Q\xac<`2\x8aUPk>\nT\x98ZJ\n\xd2\xa2RV\n\xd0\xacJb\nP\xb6Jn\n\xd0\xbfJz\n0\x89J\x86\n\xb2\x92R\x92\x8a T\xcbS\xb1\x04G\xa4\xaa8R\x80\x12\"\x04\xe7(\x85\x84\x15K\xa6\x98\xb3b\xc9\x14\xd3V,\x99b\xe6\x8ak\x9b(y\xa5\x90\xbdR\xf8&Kd\xeb\x9a_\xc5fPF\xab)\x14%\xb4\x08\x04\x040 \x00\x9c\xcej\xca\xe1lVS\x0e'\xb3\x9ar8\x97E\xd4\x0f\xa7\xb2|f\xad\xc0\"\x0c\x16!Jd\xb10\x08e\xb2(8\x8d\xe5\xf3\xb1=\x0b\xb2X\x10\x9c\xc4\xf2\xf9\x98\x9d\x05\x0d9\x99\xe0\x14V\x83\x12f\xb0X\x88(\x81\xc5\xe1 \x98\xc9\xc1\xe0\xf4\x15\x8b\x82\xb3W,\nN^\xb1(8w\xc5\xc9\x05\xa7\xae\x1a\x988s\xc5aD\x89+\x1e\x08\xe1L\x1e\x07\xa7\xad8\x18\x9c\xb5\xe2`p\xd2\x8a\x83\xc19+^68e\xd5\xe0\x04\x19+\x1a JX1(\x08d2 8]Ec\xe0l\x15\x8d\x81\x93U4\x06\xceU1\xf2\xc0\xa9*FA\n\x99*Hc\xaa\x89*@\x8f\x8ay*^\xb9ji*^\xe1jY*\xde\x08jI*\xde0j9*\xc0X\x8a)\xaa\x86R5C\xc5P\x1c\x95\xa0bi!R\x88\x12\x9c\xae\x94\xd2S\x0c\x9drv\x8a\xa1SNN1t\xca\xb9)\xb6}\xea\xa9)\xbf\x8c\xd4\xa0\xccT]&JL5\x00\xa8\xdcl\xca\xe1\xb4T]\x0cg\xa5\xeab8)U\x17\xc39\xa9\xa6n8%\xe5\xd3k\x04\x16`0\x00QB\xca\xe7\xc3\x7f\x16d2 8\x1d\xe5sq=\x8b\xb1\x18\x0c\x9c\x8c\xf2\xb9\x88\x9d\xc5\x0cYy\xe0TT\x0d\x12f\xa2\x18\x84(\x11\xc5\xc2 \x94\xc9\xa2\xe04\x14\x03\x82\xb3P\x0c\x08NB1 8\x07\xc5\xca\x04\xa7\xa0j\x948\x03\xc5BD (\x0e\x07\xc1L\x0e\x06\xa7\x9fX\x14\x9c}bQp\xf2\x89E\xc1\xb9'N.8\xf5T\xc3\x04\x99'\xaa\\\x94x\xa2A\x10\xc6\xa41p\xda\x89\x82\xc0Y'\n\x02'\x9d(\x08\x9cs\xa2e\x81SN\xb4b\xda3N\x80\xa2\x14\x13N\xbc\xf6\xd4\xf2M\x9cF\x95\xd2M\x9c\x92\x95\xb2M\x9c\xde\x95\x92M\x9c)\x94rM\xbcu\xd4RM5\x9db\xa6\x89\xc6\x1f\x93hb(\x01B\x88\x0e\x9a{T\xd2L4\x95j\x96\x89\xa6RM2\xd1T\xaa9&\xa6]\xa7\xa5\x98\x04\xd9$\\\x85SP6\xa9)\x14e\x93\x08\x04\x040 \x00\x9cMj\xca\xe1lRS\x0eg\x93\x9ar8\x9bD\xd4\x0fg\x930\x13\xd7\xb3\x08\x83E\x88\xb2I,\x0cB\x99,\n\xce&a>\x16gA\x16\x0b\x82\xb3I\x98\x8f\xb2Y\xd0\x90\x93 \xce&5(a6\x89\x85\x88\xb2I\x1c\x0e\x82\x99\x1c\x0c\xce&\xb1(8\x9b\xc4\xa2\xe0l\x12\x8b\x82\xb3I\x9c\\p6\xa9\x81\x89\xb3I\x1cF\x94M\xe2\x81\x10\xce\xe4qp6\x89\x83\xc1\xd9$\x0e\x06g\x938\x18\x9cM\xe2e\x83\xb3I\x0dN\x90M\xa2\x01\xa2l\x12\x83\x82@&\x03\x82\xb3I4\x06\xce&\xd1\x188\x9bDc\xe0l\x12#\x0f\x9cMb\x14\xa4\x90M\x824\xa6\x9aM\x02\xf4\xa8\x98M\xe2\x95\xab\x96M\xe2\x15\xae\x96M\xe2\x8d\xa0\x96M\xe2\x0d\xa3\x96M\x02\x8c\xa5\x98Mj(U\xb3I\x0c\xc5Q\xd9$\x96\x16\"\x85(\xc1\xe9J)\x9b\xc4\xd0)g\x93\x18:\xe5l\x12C\xa7\x9cMb\xdb\xa7\x9eM\xc2eP\x06e\x93\xea2Q6\xa9\x01@\xe5fS\x0eg\x93\xeab8\x9bT\x17\xc3\xd9\xa4\xba\x18\xce&5u\xc3\xd9$L\xaf\x03X\x80\xc1\x00D\xd9$\xcc\x07\xf9,\xc8d@p6 s\xf1;\x8b\xb1\x18\x0c\x9cM\xc2\\l\xceb\x86\xac{U\x1fl?w\x15\x1fV\x00w\x17\x1f\xd4\x00w\x19\x1fR\x01w\x1b\x1f\xd2\x01w\x1d\x1fR\x02w\x1f\x1f\xd2\x02w!\x1fT\x03}\xe7\x1e\xd6\x01}\xe9\x1eT\x00}\xeb\x1ej=}\xed\x1ej:}\xef\x1ej7}\xf1\x1ej4}\xf3\xbelq\xfb\xc1\xcb\x033f\x90\x17UD\xa3\x1d\x05\x01\x07<\x12\x01\x8ey$\x00\x1c\xf6H\x008\xf2\x91\x00p\xf0\xa3d\x00\xc7?\xf6\x00\xabh\x08\xe4q\xe0(\xc8\xc1\xc0\x81\x90C\x81c!\x87\x02\x87C\x0e\x05\x8e\x88\xbc\\\xe0\xa0H\xc0\xe4\xe3\"\x00\x04\x87F\x1e\x07\x8e\x8e<\x0c\x1c y\x188F\xf20p\x98\x04d\x03GJ\x02\xd72XBHp\xbc\x04\x80\xe0\x90 \xe0\xc0Q\x13\xc0\x81\x03'\x80\x03\xc7NH>p\xf8$\x80\xb2\x11\x94\x83\x81\x83(\x8b\x02\xc7Q\x16\x04\x0e\xa5,\x08\x1cMY\x108\xa0r2)l5\xaa\x9ef\x0f\xc8\x83W\xc2\x81\x96@\xc0\xe3l\x03\x80\x87\xd9\xa6\x1c\x1ee\x9brx\x90m\xca\xe11\x96\xa8\x1f\x1eb\xe9\xfd[\xe1\x08\xcb\xc2\xe0\x01\x96A\xc1\xe3+\x03\x82\x87W\x06\x04\x8f\xae\x0c\x08\x1e\\Y\x99\xe0\xb1\xd5gF\x1b\xd1\xd0\xca\xe1\xe0\x91\x95\x85\xc1\x03+\x8b\x82\xc7U\x16\x05\x0f\xab,\n\x1eU9\xb9\xe0A\xd5g\x07\x18\xd1\x98\xca\x03\xe1!\x95\xc3\xc1#*\x07\x83\x07T\x0e\x06\x8f\xa7\x1c\x0c\x1eNy\xd9\xe0\xd1\xd4\xa7\xc6\x1a\xd1`\xca\xa0\xe0\xb1\x94\x06\xc1C)\x8d\x81GR\x1a\x03\x0f\xa44\x06\x1eG\x19y\x14\x86Q\xc1\x88\x89\xeb\xe1F4b\x12\x08x\xc4l\x00\xf0\x88\xd9\x94\xc3#fS\x0e\x8f\x98M9\x96\xdc\xca\x05\xfajr\xc1\xa8\x10\xa6\x95C\xdb7\x12Kf\xae\x1d?\xb4\xf2\x92}I5\xe3\xf3\x80\x0e)\xda\xa5\x9a\x8b\x9c0\xb6S/\x0c\xce\xb1\x17 -]\xc5\xe1z\xb9\xa2 \xd6\x81\x8b\xe2\xac\x98\xa3\xa9K\x18\xc7\x0b51M\x10\x06Ha\xe9s\x00d\xce\xd6Q'\x88\x0d\x91)H\x0e\x91\xe5\xc2+H\xaf\xb0p+\x9b\xe4\x9f\xd4\"\x9eJ\xa5A<\x95B{\xc4\xa2\xe3\x93D\xe7\xa9TD\xe7\xa9\n\xd1)\x8a\xb4D\xd9\xd8[\x06\xe7YT\xc0\x94\xc7dy>Q2\x00\x87\x048(HQ\xac`\xed\x03#E\xed9bA\x18\x08(\x0b\x83)\xc5Q\x90G\xc1\xfbR\\y\x83DF\xbf]D\xffh aaZ-G#`a0$\x0c\x0d\xaa,\x9c\x7f!~\x11\xc6\xfe\xb9cG^jc\xef\x11\xb1P\xccBq\xb8E\xb1c'\x1cr\xcd\"\xd7Q\x04#\x03\x16y\xd2p\x98\x12\xce\xa1\xd4\x12\x00-n\x0c\x00\x16\xb7\x07\x00+\x0c*\xcan\xda\xb8\x98Z;9\xb0\xa4\x99\x1cV\xd2J\x0e\xab\xd0HA{8\xb7\x92\xb5\xe7\x08\x1f\xe4\xb1\x92\xf6pX`8]h\x833\xe6\xc1\n\xd9n>\xab/\xc2 \x8b\xf5\x1e\xd19\x1fR/4\x8b\xa5K\xd6s\x80\x94\x0f\xa1\x17\x06Ql\xf2\xc5=\xa2\xb8\x07\x05\xea\x0b\x93@\x18@\x90\xbe\xe8S\x00\x88\x85E\"\xf8\xe2\x01Q\xdc\x1d\x0d\x01\x06C\x12Q\x00\xda{\xc3\x81\xd5\xbd\x16$\"\xf5g\x9d\xae\xc5\x02\x005a\x04\x9a\x01d\x07\x1a\x01\x99\x82F\x08\xacA\x83`\x83\xb0\x18\xd0&\x0c\x080\x0b\x8d\x10X\x86\x01\x15\x18\x05\xeb(\x8cU\x99\xc9|\xa1\xc5\xfcV\x83q\xb4\xa4\xbd\xfc6s\xf9m\xd6\xf2\x15\x8c\xe5\xb7\xdb\xcaW0\x95\xdff)_\xc1P\xfe\xb1v\x12\x98\x04\x0bM\x82[M\xc2\xd1\x92&\xc1m&\xc1m&\xc1\n&\xc1\xed&\xc1\n&\xc1m&\xc1\n&\xc1\x80I(\x8c\x8f\xecd\x1d\xa3C\xd3O\xb2\xce\x03b\xb2r\n\xd8\x17\x01\x03;\x8e\xc3-\x01\xedq<\xbd\xc0EAZLi\xc5\xcf\xe7Fs\"+m?\xcf\x98\xf86\xc6\x9acG\xe5\xe8\xb0\xb1c\xcf\x0e\xd2\xf3\xe69\x8dO\xe3u\xe0\xd8):\xe4\xc9\x81<5\x82\xce\x83p\x1b\xdb\xd1$\xdc\xa0x\x91\x7f\x9c\xcfs]\x14Lr\xa9\xea\x87\x08c/J\xbcDa\xcc9\xc0\xeaH\x94\xd5\xcb`[4L\xa3EJ\xae\xe3\xbd'\xea\xb9\x1e\x88UU\x9d\x11\x9c\xaem\x05u+\x0c\xf1\x95\xc2|u\x13\xf8\xc7X\xc0W1\x80\xff<\xfa\xf7\x8fT\xbf\xff\xdd\xb4/Q4VW4>F\xd1XE\xd1\xf8y\x14\x8d\x8fT4~\x8a\xa2)\x96U\xb9\xe6\x84Aj{\x01\x8a\x0f\xf5\xa3\xfdy\xe2\xc4!\xc64E\xb1h\xa6\xb7\x12\xecu\x1aN\xc8\x9d\x96\xec\x01\xa3\xddX\xcb\x1e\xf2t\x0c\x0cS\xb0\x86Y{\xe7<\x00bj\xec\xd9\x1buIARPX\x8d9\xf4\x94\x03\x15\x04V\x18M\xcaV\xf8'7\x02\xa0\x84\xdb\xe0\x1f\xdb\x04\xb1\xb4\xf8di\x01JXZ\x0cHK\x8b\x82\xbd\xe8\x10\x85\x89\x97'\x02\x17\xde\x0e\xb9\xff\xd7\xf3\xa30N\xed \x9d\xfcQ\x97\xd8\xf3$\xc4\xeb\x14\x11\x85\x19\xe9y\x8c\x9c\xf4G#\xdau\x88\xbf?\xd1Eg\xc4\xdf\x9f\x14\xcc}\xe0\x04\xcc\x1c\xe7\xcf\x94QAH\x15\x9f\xcc$\xf7\xff\x83\x04\x17\xc9\x88\xff\\\x19)\x01\xb6\x89\x16\x84\xb1o\xb3#u\xf6\x88F\x16\xa370\xa0\xd3\xb0(\xa6#\xc9(>>'X\x0b\xc5\x07J\"\xb9\xe0\x90\x8a\x13\x8d\x85e\xd2)\x88\xa7\xe0m\x8d\xcclt!\x14\x19\nCx\x89\xfd#\x05\x96\xca\xa6jfp\xe6\xe6e\xc3\xbcl\x14f\xa3\xcd\xed\x04\x1d6(N=\xc7\xc6e:;{\xc6\xef\x91l4\xdfsY\xa8\xef\xb9.\xe6\x80i\x18\xb1\xc04\x8c\xb8\xaaS\x9f\xab9\x0fp\x14\x0c~\x00\x9a\x91\xf9\x8ezK\x00\xb4\xb01\x00\x16n\x0f$B\xd1$\x856)8q\xd9P^o\x92vr`q39\xa8\xa0\x95\"\xbb\x1d\xed\xf8e{\xf01\xed\xe1\xc0\xe2\xf6pPA{\xf8\xfa\xcb\xf6PX\xd7\xf3\x0fad;^\xba?7\xb8\xa23\xf6\x01\xf41\xfa\xecq\xf1\xfdym\x8b\xe6\x0f^\x99\x15/f\x90\x92w\xa7kXI\x07ez\xf1\x82IK9'\x86\xbc\xd6J\xfc\xae\xc5\x13\xdaN\xeamP\x03\x19M\x94d\x0c\xd7\xa9\\\xc8p\xcd\xec\x9e-q\xb8=\xe3\x9e@\x82\xe7\xcf\xbf\xa3\xbe\x14\xea\x15\x18|\x95-\x03\xf3S\x11\x9dn\xfe\x9f\x1a\xa8\xab\xa9\xedXQ\x9b\nKC\x95\xf5\x9e\x89Py\xb3\xda@y\x1b\xd9\x16\x18\xdf\xa7\x05\xcd\x06{^+\xa4w\x16R\x98 _\x7f\xb6\xef\xe1/\xe3p{\xd0\xfc\xf0Q\x0b\x93\x9dVd\x0f\xfd0LW^\xb0<_\xc6\xf6>ql\x8c\xea\xb6\xcdm\xe7aa;H\xdbx\x897\xf7p\xd6\xf2r\xc1+)\xa24\x93of\xe5?a;E\xdf~\xd4\x7f\x9a\x88\x9e\x03\x1a\xe5Xu\xba=A\xa7:\x02z:\xe4\xac\xa5\x16^\xdb`\xd7\x89\xe1.\x9b\xeb$\xb7\xc0\x8fFW\xb7HM\x11O\x81:\xcaaI\xc4\xac;\xe6Yu\xc7\x00#\x0d\xdb\xf1\x12\xfd\x7f\xc5A\xbc\xe0\x18\x1f\xe1\xd1OEI\x9d\xa5\x80\x88L \xf2\x9a\xb2\xb4\xcdwz\x90\xeb\xf4\x84\x06o\xf7\x1f\xc0\x17\xb3\x87L0\x1dzAZ\x8fH\xce:N\xc2\xf8\xbc|H#\x93\x95\xed\x86[\x0d\x02N\xea\xc5b\x8c\xb0\x9d\x89\x05\x99\xdd\xc6\xb8\xd3\xb5\x92\x8e\xb3\x9e{\x8e6G\x8f\x1e\x8a\x7f\xec\x1a\x03\xeb\xac;\xea\x9fu\xfb\xfd3\xe3\xa7\xc9\x91x\xb1\x88\xe7\xf6\"\xcd\x04\x0d\x83\x14\x05\xe9\xf9_\xfe\xd2\xf8\x7f\xb8\xd3\n\xe4\xb9\xde\xd1;\xc6 \xdauz\xd1\xaeC\x9e\xf7\xeb\xfd4Q\x86\xe5\x07;c\xdb\xf5\xd6\xc9\xb9\x17\xacP\xec\xa5\x93f\xd2\xe4\xd6\xd1\x93\"\xf3\x99\xe7e\xf4I\x11A\x1a\xba\xfeb\xb2ByN'\xff\xf91\xcf\x98\xee\xce5\xf9\x9cu\x846Ui$\x1a\xcd\xfd\xbb\xd0\xeb\x99\x18Ej_\x10d\xcc\x97\x9a\x1dx\xbe\x9d\xa23\xc1s\xa8/\x11\xa5\xc2\xd0\x89=\xc4IM\xdb\xec(\xd0\n\xa6\xa5~\xd4\xf4Ce\x17\x9d-2\xea\"\x83-\xea\xd5E=\xb6\xc8\xac\x8bL\xb6\xa8_\x17\xf5\xd9\"\xab.\xb2\xd8\xa2\xf1x\\\x17\x8e\xc7c\xa0\x98*\xe7\x00\xbe\xbdk\xa45\xfa\xc3\xfe\xc8\x1c\xf4\x87,\xaa\xf4\xf2\x1aY\xfe\xce\xc3\xbc\xd4\xb3q\x0d\xe3\xb3\x95\x8f\xda:HP\xc3(\xff\x8d\x86\x04(IQf\xa0h\xaf\x15\x11T\xdeM:!\xb3\xaf,\xc2Ej\xb05>\x10\xbf\x9e\x1b\xecB\xa2\xa4k6\xae \xda\x95\x01\xd6\x01c{G`\xcd#\xb0\xfd#\xb0\xd6\x11\xd8\x01\xa3\x17\xe8`\x7fA\x8f\xbd$\xd5b\x94 \xa1q\x08\xc4\x9a{\xf1\x1c\x99\xaf\xd6'94I\xf7\x18i\xe9>B\xc5\xd1*\xa1%\x8b\xed\xa5N\xf4sDm7u\x8f\xdbo\"9&(B\xb1\x9d\x86q\xce\x94\xe0at-A\xfb=\x7f\xd9\xf1\xfc\xe5\x81\x18\xd2\x9b\x9cG\xfe\xab\xeb%\x11\xb6\xf7\xe7s\x1c:\x0f\x02\x1d\x06\x0fI\xc7>\x94\xe7\xe1Mk\x88\\\x17\x9a\x02\xf8\x01k\"-\x95\xd5\x06\x0d\xb6\x0c\xa2\x9c\xf5\x0b\xa9\xc6\x03\xc7Y,\x9e_\xaamlG\x11\x8a\x05\n\xec\x0f\xf4hW\x1a\xf0\\\xef\xe4\x9b&\xa5\x0b\x9d\xeb\x9d^VH\xcd\xf0\xdecVRN\xcf\xf3p7\x01\x9f\xd2\x12\x84Qn\x1a-\xb5\x97Z\x82\x9cL\xeaCe4\x82ymH\xcdO\xb4\x05F;\xf2Y\xf6;%I\x18{\x993V\x99\x18\xaa\xcc\xf5\xe2\xa2\x9a2%:\xa98\x12%N\x88\xd7~0\x01\x9f\n\xc5\x7f\xba\xd8\xe4 \xe0F,\xeai\xfe\x8b\xe6\xa5\xc8O\xaaG\x95E\x0c=\x0b\x97\xb2\x7f\x8c\xea\x9f \x134\x8aB\xc4^\xc2E\x81\xbddR\x9b,\xef\xb9F\xb4\xeb$!\xf6\xdc\"\x1c\xb3\xc6g\x03\xebld\x9cu\xcd\x9f\x84*)\x9d\xb8\x99\xf5\xa9\x1b\x1e:\x1bj\x93\xca$\x8e\x18\xf5I'\xd4;V\xb4\x9b\xe4\xa5\x0b\xdb\xf7\xf0\xfe<\xb1\x83DKP\xec-&U\x1f\x9e\xf7\x0d\xcb\x10\xf2\xee\x06\xa1\xe6\xa2\xc4\xe9$\x91\x1d\x1cH\x03d\xfa>7j\xd5\x9f\x1b\x93\xe2?BV\x9dd\xb3\x84\x82\xa2\\\x85}^\xab\xfdD\xc2\xca\xb71u\xde\xa9_5t[\xcc\x04}]\x9f\xa8HK\xf4\xd1\xdc \x8eWVd\xc7\xb6\x8fR\x14\xff\xf1G6\x15\x90B\xf5\xa2]\xcd\xdf\x8av\x1d\x9db\xef\x87A\x98o\x10P\x82\x0ft]V\xdb\xc6C[\xad\x9a\x06\x1f\x0e\xfc\xca&\x9b\x04\xcch7\xa9\x0e>\x90\xfe`\xa9{\xb9\xc5\xdb\xc3\x82\xedq \xdc\xcd\xc8j(\xba\x02\xd1\x07\xfe\xaa\xeb:\xb3\x10\xe9\xb3\xc3a\xb3\x921\x99E\x8c1\xe6\x16;\x00\x04\x14\xad\xd3M\xedy\x1e8\xa0\xf8\xe9#\xceQ\x0eOV]\xfc\x9c\x8dC\x87\xc6\xdb\xfa\xfc\x90s\x04\xa3\xf3\x85\x17'\xa9\x16.\xf2\xf0\x83a\xdb\xd1;\xfa\x11\xbc\xbaebs\xd5/:9\xe7S\xa7\xf3*\xd7Y\xfc\"\xb3\xbe\xad\x999L\x1eSY\xfa\x8bj\xb5\xd9kV\x9b\x99\x9f\x00kd \x9b\xf3\xfb\x8f\x9a\xa5\xbf\x00\x13=U\x111\xb4.c{\x0f6\xab\xeb%Z\x18\xa1\xa0\x19n\x92\xb5\xef\xdb\xf1\xfe \x1a\xe13\xef\x16h\xa8fQL\x8a\x95'V\xd6\x1a\x95s\xd0\xc4\xf7\x82*\x82\xb5\xb2\xdf A\xd9\x1b\x83\xa3\x9f\xe0~c\x00\xcb\x7f\x83\xe980\xe6(\xd9\xcf\x8e\x01w\xb0=G\xf8\xe9\x1d\xef\xa4\xa9\xfe\xa8f\x95\x922C79,\x0fu\xbd\x1eG\xb9\xc30'\xcc\x1aJ\x02\x95\xfd\x91\x9a\xa1$\x9d[\xc0j\xd5g'J\x95Q\xadi\xeds4\xae\xe8C\x9a\x8f\xd2U\xe8\xca\xe6\xed\\\xcf\xf5\xd6\xe5H'f\xd0A\x16\xa8e\xe3\x05w\x03\x8c\x99\\L\xba\x0b\xe5\xd3ONC\xf5\x04\x9d\xed+\xf2v.\x16\x0b\xc5F\x86\xf9\xd2,3\x80\xe7\xb6\xf5\x97\x92$\xb2\xd3\xd5\x11\xd0?\xfepQ\x14#\xc7N\x11\xa5\xccAD\xf4\xacS{[n~\xbdq\x08\xbdc\x16\xab\x19\xfa\xb7'w\xd0\xc96\x8c]m\x1e#\xfb\xe1<\xffW\xb31\x96\x85c\xaa\xf1R\xb9\x19N\xec\xe8\x0f\x07\xa3h\xc7l\x81\xff\x07\x9a\xaf\x17\xed\xd8\xd3\x9d\xcal\xd8\xcd:,\xbc\xa6\xab\xd4p\xa6\x8b*r\xc8\x16\n\xb1\x17\xe5\xebR\x82\x81\xa9:\xe4<\xdfH\xf3?4\xe9\x90\xd1\xbeZp\xc7\xc8\xad\x18\xe0\xf7\xea\x00\x9f\x98\x95\x9e=\xb2\xe7\xa4\xab\xf6\xad\x19\x19\xcb\xb0m\xc4,5\xe0\xf8\xaab\x19\x85IJ\xbc\x8f\"3p\x7f\xec8c}\xc2\xae\x80\x87\xe6YO\xef\x9f\x19\xfd\xbe0\\\xa1\xb8\n\xa7\x1drN(\xea:\x81\x19(\xb3\n\x1f\xf5p\xf9h9\xd7\xac&\x17\x8em\x98\xbc&{V\xef\xcc\x18\x18g\xfd\x91\x82&\xd7j\x8a,\xaa:\x9e\x17(\xb1\x02\x9b\xd3\xd4\xa8\xc2\xdeE\x18\xa5\x88\x95kl\"\x13\xf1\x9a\xec\x8f\xcf\x06\xbd\xec\xff\xad\x8a,\xd8\xaa\xe92\xaf\xec$v\xa0\xd8j\x9cN\xd4\xa8B\x0dK\xc4:\xe6\xc0\xb0\x17\x0b^\x9d\xe3\xe1\x991\xb4\xcez\x96B\x17_\"5\xc7,\xaa:\x9e\x17(\xb1\x02\x9b\xd3\xd4\xa8\xc2>\xb2Sg\xc5\x88e\xe9\xc8tz\x9c\"G\xfaY\xaf7<3\xc6\n\x8a\xcc\xd9*\xa9\xb2\xa8\xec\x14n\xa0\xd4J\x8cNS\xa7J\x05\x19WF\xae\xb1n\xf4\x00\xb7\xcc\xa6\x1cc\xa4\xe6\x96\x19W%e\x16u\x9d\xc0\x0c\x94Y\x85\xcfi\xaaT\xe1\x1f\xe6\xb1^\xc2H\xa6\xbb\x96m\x0fym\x9agc\xfd\xcc\x18\x0c\xdb\x95Y\xf2U\xd2gQ\xdbi\xfc@\xc1\x15Y\x9d\xa6U\x95*\x88\xb0\xbe>\x15:\x98\xd0\xa2\xa2y\xf6\x07\xce\x14\x8d{\xc0\xab\xa5\xc4\x95(i\xb9\xa8\xefd\x96\x07Hzun\xa7\xe9ZR\x0b!\xa0\xb3B>J\xb8\xa4\x9c\x1aY\xa7[\xfe\xa0\xa5^\x8aQk\xaef\xe1\xe14kD\xb3\xd6*\x9eh^\x90Eq\xd4\xd6b\x1eI\xe7{T:\xb5oU%\xd8{M\n\xd2\x1d\xb9.b\xbc*\xb5\xe7\xa7\xad\x82\xa8\x9a\x8bex\xdd,b\xe3\x1b\xd8\xf3N\xedy\x07{l\x1a\x8d<\x89N\xf1b\x16,\xc7\xaf\xfe\x8a\xfa\xd8\\8\xb7bbv\xf2\x99\xcf\x96\xf5X[C\\\x85\x89\xecb\xdf\xbe`5\xa8WeF\xb4\xa3\xceK\x11)l\xc1\xfe\x1e\xbb\xbdW\x08Q\xfa\xf8\x81\xc9\x90\x81\xbeI\xae\xbe\xb5r\xaf\x1aLJhh\x97\xa28\xb0\xb1\xe6\x86N\"\x87\xe6^\xfdGy\x13\x8a\xb5+\xbd\xcdX\xbb\xa8U\xa5\xb5\x8f7\xa8\xa4)\xdc\x11\x12ik\x84h\xb2ALf\x14h\xd3\xf3\xb6 :\xa6\x01\x020%\x7f\xc4fR\x9f\x9e\xb3\x15\xaa\x939\x0fC\x13\xa3\x1dr\xd6)\xaa\xe0\xf50\x98\xbb\x81\xfc\x9d^\x0ci\xa7;O\x03r\x1c$\xc7\xe5>7.\xcfCw\xaf\xe5;\xb0u,r\xd2\x98\xf7?s \x82\x97\x9ez\x86\\/=P'\x16\xf4V\xfab#\x83T\x9a\"M'A\x189i\xb5\x9bkB\xb3W\x8c\x92(\x0c\x12\x94h^\x100f\x96\"\xb9\xee\xc8\x95[\x82\x9eXN\xa3\xa7u\xc6\xaa\x96,\xec\xf8#I\xedt\x9d\x80{\x0fOeJ<\\\x07n\xe8\xac}\x140\xb9]\xe3\xd8d\xf6X\xcf\xfeH\xaa\xce\xcf>1\x9f\x0f\xcd\xcf\x93UY\xef\xbe\x8e\xfc\xc9\xf36\xb78o\xf5?\xd1Zb<\xfd\xe3\x8f\xc2g\\o\xd3\xf5\xed\xf8\xc1\x0d\xb7\x01\xec]2\xca\x18\x05.\x8a\x91;+9\x80\x9b\x7fE\xa0\x93\xbf\xb9\xcd\xa1\x8f\xc75C-\x10\x9a\x91\xa7\x1c\xa8d\x9e\xd1\xef\xf7\xd1q\x9a\xe1\xf6\x9dT\x1aW\xa9\x85\x9dEThY\xc5t\xa2\x038\xad|g\xc9\xedg\x90\xdc>\x1c%\xf0h<_\xe8\xfd\x89\xe2\xbd'\x15\x89\x9a\xd6\x14\xa9\xf3\xe7h\x13}\xd8qd\xcc\x0d\xddy\x82d\xec\xce\x95\n1'T\xba:N\xd3\x8b\xc5BxbN\xb8\xd3\xaaeSW\xf3\x1b\x0e\xed|\xe4+\x0e\xdd\x93G!\xa9\x0ej6gl\x9b\xfd\xfa\x96\xb7TP\x15F1w\xa6\x0b\xee\xfb\xcc\x95\xef<\xa2)69\xb3\x9f\xca=\xce\xecwx\xe7\x93{\x98C\xab\xe0c\xb5\x8fV(H\n\xf1\xb3\xa0\x83z@\xfd\xa24\x06\xd5/\x89ae;\xd6\x8er\xcd\x15'\x18\x1at\xf3\x96\x86\x16\xban\xb1\xdc\xcf\xba\xddAr.y\xe5-W\xc5{\xc0\x9d\xd0\x05\xd6~2\xf4\xdf\xbb\xbe\xe7\xc4a\xfe\x80|iN\xe9!\xbb\xeaHN_g\xce\xe8\x0c\xd8\x13\xd6Y\x1f\xc8\xdcQ+\xd7y\x89\xf8\xc4S\xee)\xe5\xca\x138tJZj\xe8\x8ezc\x138\xed@n2\xf2\xc6&\x0d\xf8\xd1K=\x8c\xbd\xb5\xdf\xf9\x82\xe6g\xc4\x84/\xe9\x97L\xc4P\xb6\xd9\xd4\xeb\xc5\xed\x90\xdb\xdb+r \xc4+\x88\x88eT\x8f\\\xf3\x9bE6\x83\xdaG \x8ej\x83\xa7\x95\x98s\x1a\x96\xe0P\x13\x07\x93\x8bX'n\x9e\xbe^8i\xa7XQ\xba\xbf+\x1dLzr\x13\xbe\xe7\x92\xa7\x1a-\xb5\xe2\xb8\xb5U,,N\x88D[\x94T/`\xeat\x93a\xd6\xcb\xcf\xe6T\xa0\xe0\x85\xb9\xd5l\xd2\xf8p\xe5\xb3\xe5\x89J\xe2x\x7fq\xd1\"\x9bW\x9a1\xc1x\x8e\xa37\x91\xed\xbc_'\xa9\xb7\xd8W\xe3L\x8d}\xaa7\xfei\xce\xd0\xa2\xf4\xfaQ\xdbH.\xa6,3uD\x8f\xd1\x81\x1e\x03'\xf2,\xfdEs\x18\xb5\xce\xd9\x95\x8c\xa5\xa7O\xf3\x13\xa6g\xc2\x13\xa8T\xb1\xc0\x1fO\xe8\x11\x12-\xcc\xd1\"\x8c\x91 aI\xb5\x93\x8e\x9a\x88Dm5\xdb\x11G\xc8\xb5\xbcG\x01\x07r\xeb \xec<\x0e\xd3\xfc\x87\x8e\x91t\xbc`\xe1\x05^\x8a:\xd94n\xc7g\xc4%\xcf\xc9\xf1\x14\xcd{\x12\xb8\x04x\xb1\xf7i\x9d\x15\xff/\x0e\xbe\xe6\xf3b\x1aF\xe5\x9e\x039;\x0c\xd8{\xb1y\xa6\xa9\xf6\xf3S.\xa0\xff\xfb\xbf*\xf2\x07\xb4_\xc4\xb6\x8f\x92N\xd5\xb0C\x1a\x02\xf7\xa0\xf3R\xf4\xa3\x91\xae\xe3\x80t\x1a\xea\xf9\xbf\xff\xfd_\xcf\xccO\x14\xec\xe7&\xa5N\x93W\xc3\x9c\x02I7\xfb%\x0eq\xa2\xd9\x8e\x83\xa2\xb4\xda\xac)\x87dj\xf3g\x19#\x14<\x85g~\xf5\x83\xe0ED,\xdd!\xf2!K\xcc\xb1\x17<\xa0\xf8`\xe9/\x9a\x17\x86P\xba\x15 H1\xcbc\xb5\x9d\x95y8\xba\xab\xda\xdd \xcc\x93 u\xb8\xe1\x05\xdc\x92\xb2\x06\x9d\x81O\xcf3\xa7\x83\xce\xfaU\xb7\xba\x8b\xea\xeb\xdf$\xc7\xcf6(N\xbc0\xd0\xa2\xd8^\xfa\xf6\x81\xdc\xaa\xa8\x83K\xe4\xb3\xe9?\x9a\xea\x8f?|\x94$\xf6\x12==\x82:u\xde#\xe5&\x06\xfcn\x0f\xf9@\xd8\xcc\\\xa0E>q\xd8\xb4\xcb\xc5\xf4\x82\xc6\xfe\xdd\xf56\xc4\x8bE-\xcbY)\x9dmTb\xde\xc9\x171Mt\\m\x97\xba(\xfbS\x8b\xdb\x8fv\x9d~\x11\xf6\xb2\x8bN\xba\x9ay\x1a\xb4\x9d\xb5&\xaf'\xf5\xc8\x83\x9a\xec\x19A\x93?6h&\xfcH\xbc\x8c\xed\xbd|\x05\x9as\x89\xec\x18\x05\xe9s_e8a\n\x9d\xa7A\xf6WK|\xd1\xc5\xad~\xa9\x19\x8e\xee\x9f\xae\x97\xd8s\x8c\xdc\x7fU\xef\x9b\x08\xc2\xcc\xe5p\xb8En=[uM\x8e\x90y?\x00s\xb9\xc9b\x9aer\xd7\x9fx\x04\xdf&\xc7\x0e\x1c\x84\xd9Sa\x8b\x81> \x97_e\x01i\x12\xb9\n\x0b\x0e|u\xf6:]\x85\xb1\xf7\x88\xe8\xeb\xd8\x13z\xb4\xab\xb8T\x07=\xe5\xa7?y\xe1$\xf5\x16\x89\x86\x05\x0e\xed4\xff\xb6\x0cm>p/\x9e\xa1\xdf,\x0f\x0b\x0fc\xf8\xc8e\x86-w\xaa\x80\xfe\xd9\x1f\x8fu\xd4\x03\x92[T9\xc7Q\xcb\xb8D\xa7\x0d\x9f\xe4\x8aZ\xc0\xb8\xe8\xff\xc7\x0fN4\x83r\x1f\xbcxU\x15\xd7\xb13\xadv\xb8\x03\xe2\x0c\x07l\x0b\x18\xe4\xa4\xf9_F\xdd\x95Y\xec\"\xf3\x98\xb5\x83\xb9\x18P\x0e\x0e\xca\xa2\xd3\\3\x0f\x95s\xce}\x98\xb8\xf7Y\xf6B~w\x8ef\xcc\xa8V\x06-\x0f\x80\x13}E\xcf\xfe\xb4\x89-\xbc\xf5\x0bO*\x05\xeb\xa1\x9e\xfd\xa1X\xcf\xd7i\x1a\x06\xec\xdb}\xc2u\x9a\x0d.\xbc\x02\x0bx\xd7\x0b66\xf6\xdc\x03\xbfVIV\xf6\x03\xeat\xfbI\xc7\x98\xc0O\xdb\x0e\x03\xffu\x81\xb83Fe\xd0{\xc4\xc4\x9b\xa7\x18\xac\xea\x1e:\x7f\xbc\xa7\xcc\xd9\xca\x13\xbb\x8ba\xf6\xa7\xb3\x8e\xf1\x8f\xae\x9d\xda\xe7\x9eo/\xd1\xcbd\xb3\xfcy\xe7\xe3\xc9\xdcN\xd0\xa0\x7f\xf6\xdb\xaf7\xbdo\xfb\x8b\xfe\xfc\xcbn\xed<\xea\x9e\xfd\xeb\x9d\xee\\\x86\x9bw\xa6k\xba{\xcb\x9c\xed\xad\x8d\xe3;\x9b\xd9\xfdt;{5~t}\xc7\xbb\xfe\xf5[\xf4\xedw\xf7\xd5\xdc\\\x8e\xaf\xef\xa7\xcb\xd9\xab\xe9\xbe\xf8{\xfd\xf3\xf5\xab\xe9\xf2\xfar\xb7\xfd\xfa\xfb]x\xfd\xe6v|\xfd\xa0\xeff\xfb\xbe>\xfb\xb8\\\xde\xec\xfb\xfd\x9b\x8f\xf8\xfe\xdd\xfd\xb59\xfb\xa0\xafg\xf7_\xfb\xef\xee\x9d\xed\xfb\xfa\xe7\x07\xf3\xfd\xab\xe9\xf6\xfaU\x7f\x7f\xb3\xef\xefo\xee\x97\xeb\xd9\xbd\xb3\xcf0\xb3\x0f\xf9s\xeb\xe6\x1e'\xef>\xce\xd6\xef?N\xfb\xd7\x97\xb3\xf5\xfb\xcb\x9b\xfbw\x1fj|\x9aa\x9b\x9f\x1f\xcc\xf7\x1f\xa6\xdb\xf9+\xfd\xf1\xdd\xfd\xc3\xf6}\xfe\xdf\xe5\xe3\xd7}V\x9f\x93\xbe\xbb\xbf\xee\xdd\xd4?\x17u\xbc\xfb\x90\xd5\xf1\x90=\xdb\xe5|\xef\x97\xeb\x9b\xc7\xa9U\xfd\xfc\xfe\xa3\xd3\xbf\xbe\xbc\x98\xcd>N\x97\xb3\x8f\xaf\x93\xb2m\xe9l\xdf\xdf\xdd\\\xbe\x1e\\{\xa3\x9f\x7f+\xf4\xf4\xf3O\x9d<\xaf[\x9c\xfc*b\xceN\x10j1\x8a\x90\x9d\x92\xf3ZqS\x9f{#\x84<\xa3\xd9SK|f0\x95(\xa8Y\xb9G\x11\xb2\xe3,Z(F\xa4\xfcEm\xecC\xe6w\xc0\xdd\xff\xe9\xafq\xeaE\x18\xfd\xabJ\xfeZ\xd4\xc15\x0b\xf4V\x80\xd1\x9f\xde]\xe9\xbd\x07.\x89\xd8\xcbg\xd8\xa3\xee\x94 8\x19#\x9d\xbd\xe0\xa5\x94\xdd}\xea\x99\xa4\xfch\xe1?\xb3%\xf5/\xc8\xb7=\xfc\xaf3A\xe9\xc2\xc3HX\x18\xd9I\xb2\x0dcW\x08H\x90\x1d;+aq\xb6\x1e\xa3\x0b\xb3'v\x8clRE:\x91l\xa2\x1dh\xc4\x0c\x8f\xc4\x86\xa1;\xce\xfe\xb4\x0d\x8f\x8b\x85\x9a\x15\xff\xf3\xd5\xd5\xbct&\xdf\x8a\x91\x1b\xbb\xeaO\xd2V\xb4\x81\xea\xd6\xb4\x01\xcbV\xb5\xc1\xf2\xd6\x81\xa0\xaa\x95\x7f\xca0\x00d\x8ar6\x07C\x7fq6\xd6_\x00Y\xb6:\xa5k\xba?jF\xb4\xcbF]0\xe5K\x96\xff\xbb\xa7\xbf8\x1b\xb5\xf2\xeb\xc9\xd9U\xc5\xff6\xf5\x17g\x96\xfe\xe2l\xd8\xcaQ\xeb\xb7HX\x95\xff\xbb\xaf\xbf8\x1b\xb4\xf2kaWs#3k\xff\xab\xd1g\xd1(8\x1403\x07y|\xbc\xd9\x9a\xeaQ\xb7\xe8\xf9\xd5\x137l\x92\x01u\xcb\xbb(\x8e:-\x00\xccMUK\x8aw|\x1d\xf8\xd0\x17\xb8\x1fU\x0f\x11\xce:\xe6\x0f%\x13[r\xe4d\xc2\x9c\xd5\x88QN\"P\xc0\xb3\x9f\xd9rV\xc8y\x98\x87\xbb\x03\x19\xf5\x97+Y`mD\xeez\x08\x1eW*\xd5\xb3?peOx\xfd\x86\x80aD\x1dD\xef\xeb:\xf1\xd1\x8d\xc2\x0e\xe4y\xb9J\xf3,HU\x8bP\xba\xae\x16\x85\x98L\xaag\xff\xaa\x9b\xca/\xa5\xa5t?\xe7\x8a\xfa{\xb7xC\x8f\xf0\x8dJt.K#\xf7\xcb\xf27/Tn7 \xcf\x91\x8f\xca\xedn2\x0ef\xcf|\xd0[Q\x8c\xff\xa1Q\xf6G\xf4\xb2$=_\x02T i!\x97\x08\"\xde\xf1\x90\xf7\x83\xfa\xa7\x13U\xd7\xfe\xca_\x85WFKk;\xcf\x7fB.e0^Y\xf9\x1a\xf8/\xc0\"\xd8Y\xd9q\x82\xd2_\xd6\xe9B\x1b\x9d\xbd0_%\x9be'\xb7\xe0/?\x18\xfa\x0f\x9d\xc2\x82\xbf\xfc0\xfa\xa1\xb3\xf1\xd0\xf6\"\xdc\xfd\xf2\x83\xd9\x19v\x0c\xbd3\xfa\xa1\xb3\xf3q\x90\xfc\xf2\xc3*M\xa3\xf3\x97/\xb7\xdbmwkv\xc3x\xf9\xb2\xa7\xebzV\xc7\x0f/\xcc\xab\x17\xe6\xab\xc8NW\x9d\x85\x87\xf1/?\xbc\xe8\x99}\xa3?\xec_\xfd\x90?\xd0\xe25F\xbf\xfc\x806(\x08]\xf7\x87\x8e\xfb\xcb\x0f\xb3A\xd74\xcd\x8ea\xbd3;\x86\xd1\x1d\x0c\x86\xd8\xc8\x9eh\xd9\xbf\xfdN\xaf\xd3{W<\xce\xc40;\xa3\xac\xec\xf1\x87\x97EMY\xa5/\xcc\xab\xbf\xfc\xd4\xb1\xf4\x17\xcdZ\x93\xd6\xa8\xeb\xd98\\j\xeb\x1d\xf35\x9d \xf9\xa2U\xea\x1e\x8b^\x1dV\xaa^\x03,`\xd8\xe9f\xbaw\xe30\x02\xb8K\x19\x8an\xc1\x8c~\x12V\xe5\x87\xae\x8d\xa9z\xea-m\xae!\xd4\xfe63)\x16\xbf\x9a\xe5\xdcP\x7f\xf3\xc3\xe2\x86\xe2\x937\xf8\xf9\x05JuY\xafm\x81\"\xc8\x07\xe8\xd1\xaeS\x9c\x9c\x92\xbe\x04Z\x8ckUj\xb5\xb1&;\x06g\xf5\xc90\x82O*J\xd8\xd2\x17U\x80{6U\x9e\x9c\x9fk\x95V\xb8\xd2\xba\xe9K>#f\x81=h\x16\xd8O8\x9a\x04\xd5\xff\x94\xd7\xce\xd5\xb1J\xaf8/':*[:\x16\xe96'\x9d\xffQmM\xa7\xeb\xe00AZ\xfe\xf8\x88\x94\xfc\xf3e\x9bd\xc2\xad\xc8\x0f\x83\xf7\xd8c?\x03\xf2\x0d^\x8d\xe8\\\x1eN\xb4Ir\x82[\xf8\xa1+O\xef\x98\xfa\x91g\xea\x85\xb5t\xba\xc4}\xd9$\xb2\x99\x1b\x11<&u\xabc\xb9\xb6\x9e\xfd\x11\x9d\xcc\xe5(\xff\x9e\xba\xcc\x8dK\xf5w\x0f\xe5\xcc\xb44\\.1b\x8fh\xc1\x81\xd7@\x14x\x95\xa6\xccF\xa9N\xd7D\xbe\xc2\xebo\xb8\xe1]\xf8*`u\xe4\xa9\x08\xe8C\x0e$\x03~**\xcf\xf1\x8cu\x17-\x81\xf3=\xe5s\x8eN\x0bc/\xcf\xa6\xe9/\xb2(a\"*\x10\x1b\xaa\xeb\x84\x18\xdbQ\x82\\\xf1\xa9#\x81P\xf9c1\xe7\xf2\xac\x1et\x02\x8d\xdd\xc0\x12\\\xa1=*\xd2k\x0f\xe0\xaa`\xb0\xd7o\x82\xc1\xec\xe7:\x1a\xcc\x83\xea~\xa7\xd7'c\xbd,\x8c3\xf4\xce\xe0\xdd\xa8k\x8d;\xc3n\xdf\xe8\x18f\xd7\x18v\x8c\x1e\xd6\xfa]k\xd4\xe9w\xad\xf1;C\xef\x18#<\xd0\x06m\xf1\x1b\xb7W\x90\x05/\x90\x16\xef\xd7~\xa4\xa5a\xfe60`\xe1\";\x01\xc43\x10\xbfz\x8a:;\xa8u\xfb\\g\x03-\\\xdc\x87\x97\x1f\xe3$\xa0\xd5\xbb\xa5\x8aG+/H\x0f\xc4!\xbb\xfcG\xf6cc\x04T \xab\xd1\x1d!\x7f\xc2\x9f\xe3\xab\x86\xff\xae\x81\xfcN~\x14\x08\xf8\x1eo9<\xaa\x04od\xb85\x84\x1c\x9e\xb8D\x95\xad\xfb\x99\xc3F\xe5\xc9\xb2\x02\x9a\xd4W0ub\xf2\x97\xbdR\x9a\x97M\xc2\xbdz\xc1)1{\xeb\xfc\x0b\x0f`\x9a,\x96b\"7Qh\"\x7f\xef5\xcd\x9e \xd1\x9e\xe5-\x86'\x85Ap\xb2\xe8Y\xdf\x13.\x0f\"\x06:w\xbc\x86S\xd5\x13_\xa3\x0d\xf0;\xe9\xcd\xde\x1c\x9f\xe3\xde_\xce\x92[\xac\x07\x90\xddEo\xdd\xf6\x02\x0e\x0b05\xa8\x0d\x99\xf9\xeaQ\xda\x17*F\xc0e\x97\xfa\x82\xc3Q\x1f\x1c\x02\xde\xc6\xa7>\xd8\xb0\xdf\xeej\x91\xb5\xc5F\xc3\xe3\x98\xd1Q \xf1\xda\x90\xa3\xb8\xe4\xa7\x83\x18&\xad#\x12\xc7\xa6|\x90\x08\x0cLM\x0b\xa3\xfa\nVf\xab\xe6\x15;\x96B\x85\xf3pw\x90\x1e\xdai`T\xc2\x19\x8ca\x95\xcd\xcc\xbe\xcc\xa7\xae\xe4\x08\xb7\xe6Ni\xd5L\xba\xd0\x0b\x87,\xf1\xa4\xce\xf4Ty\xcf\xb4\xf4\xec\x0f\xc4\xac\xa9U\xdb\xdaq\xe0\x05K\x903\xb7|\xab^\xdcR\xddn\x17\x1fV\xe4_Q\x97\x8du\x7f\xcf\xfe)\xa7\xe5\xee<\xb6\x1d\xa4\xe5\xabZjF\x84\xceBEq\x18i\x81\xed\xb3\x87\xb8\xa9\x15I#\x1d@\x9c\xfbx\xa5\x18\xcb\x06\x10(X\xfb\xb2\x0b\x8f9(\x0b\xb1\xed\xf4 \x9e4\xba \x8a7(\x16\\\x1f{\xb6\x0bYd%\xa2\xebW\xf47f@\x06\x9dU\xbf[\x9d%\xaf\xee\x1e\x94\x01E\x8fUcE\x92\xdas\x8c:i\xf55\x16So\x01\xba\"\x9b\xd5\xd2eQ \xf8\x85\xdb u\x1f\x82H\x82i\xc4\x9dNy\xe5\xf0\xeb\xfaKWik\xa3\xdb\xe1^\x0eE\x1c|\x87I\xbbN\xe8G\xeb\xack\xadc\\\x0f\xcd\xfc\x91~\x10_\x1cC\x07\xf5E\x9c\xaa\x9d\x88&l\xce\xf5\x978\x9c\xdbX+\xea\xfa\x8f\xbe%*\x90\xb4\xd6S9\x00\x92g\x9c{\xd50$~=S\xf5\xaa/\xc0\xdd\xcb1C\xe0\xed\xb9\x03@/\xc3\xa12nZ\xb5>?\xaf~\xe0\x99\x94\xc3]\x9a\x9fLJ\xe3\xac?\xd4\xbcX\xafg?\xd6,`\xc0\xf8tu\"\xa5O\xbe\xe2\xab\xd8\x84\x82ZU\xde\xefN2IZ\x12dp\xa7|j\xda\xac\xec\\\x80B\xaa7\xb7)\xe9E\xa2\x91fl\xe9Q{\x0f\x03\xe2\xe6 \xf0V\x9f\x92m\xfe\xea\xc6\x9c\xed\x99\xact\xd5vz\x8cI%\x13\xd7b\xf2c\xf2\x8a\xeb\xb7\x9e\xda\xa9Bf\xae\xaa\xbe\x8c\x93\xb0/\x93\xe0\xce\x02\xc1\x1f\xd52\xf9\x17>Ix\xd2\x97\xcdJ\x86B\xfa?\xfe\xc8grI\xc4\xd1\xd7O\x99\x14\x99\n\xba1\xfa\xef\xb5\x17W\xaf\xc7\x11\x0d\x12\"*\xf86+\x1c\xe0i\x03\xfasCM\xca\xac\xe2\xf6\x97R\xf0\xf2e\xd0V1\n\x0e\xd8o\xae6\xb2\xa0]\x8a\x82\xc4\x0b\x99l2\x81\xf0\x14^\x9csLW\xe5?\xccBT&|m\xfe\x13+\x8d\x91+V\x81\x1f\xa5\xfb?66^\xa3?\xf8\xc4\xb5ID\x03\xe5\xda\x91\x8b\x0e\xb8\x17\x0cJ\xb9\x97\x93=\x15L\x0e\x8f\xe2\xd0\xad\xee%5\xc1<\xffjH\x8c\x80\xab\xee\xfc\xa6^\x1aFs\x9b\xfeb\x0dpE\xa7|s\x0eDZ\xfd\x17~\xcd`\x89\xb1O\xdb%{r\xbe\x07\x14\x98:U\x95\xe7\x06\xd9!U%WB\x8eb\xf9^3\xbbIR\x1c\xb9\x90\xaf_\xd8cD\x95\x84E\xca\x06\xd8\xcc\xe2#\xd1\xca\n\xf5+J\xd61\xae_\xd3\xf7d\xad\xe7m5\x9b\xd6\x9b\x93\xea \x01\xca/r\xa2\xc0e\xaevfO\xd8{\x9dy)\n\\\xf56\xb4\xcc$\xa5\x86\xf8seV\x7f\xb8\x80\xbeJV]h\x12\xdf*\x91\x8b\xd3-f!\xed\xf4\xb3WOw\xeb 8\x99\x0e\xa8\xe3p\xa76\xa9\xbcgG\xcf\x9aJ\x1d\x82\xf6\xd2<\xc0\x92\xbf\x19\xf2\x18\xa1\x8a\xa9\x9f\x93\xa3\xd7\xc8\xd1\x9b\x94\xff!\x94#t\x0b\xea\x04$\xb0\xee(\xcf\x0dR\xbf\x1f#<\xf5\xb4\xbc\xd5$\x89D\xc88\xae_\x1e\xf2\x90\x9c\xe1$\xae\xd5Q\x8b\xa8\xb2qG\x0e:^\xb0\x08\xeb;\x1d\xc0K(\xb3\xf2\xce*\xbf\xee\xd7\xf5m/`\x97urt\x87=\xc4\n\xc0\xb1w\xc6?\x8c\x80g\xc5z\x89\xe0w\xda+\x0f\x0b\x19\x0d\xa0\x02\xf6\xf3\xc8\xc5C\x13z\xd8\x87\x1eZ\xc7\xbf9\xa0\xa0,\xdenU\xad\x8f\x8b\xdbb\xea\xe9C\xdd:\xf2\xa4.\xf4\xee\xf7\\\x0e\x9b\xd5\xeeQ\x1b\x11-\xb6\x80\xae\xc9\x16\xb5\xd2\xef\xbc3\x16\x83\xb1\x03xay7\x9f\xdc\x9f\x02\x98u\xe7v\x824\xe0\xe80\xa9\x0b\x93:\xdbZ\xcf#G)Qh\xcc.\x9bF5\x07O{w/\xc1\x95\xff2\xaad\xc1`\xb5\x1c\xae(\xd6\xef\xe4\xcb\x9d{\xc5\xc0\xc2.\x8d\x93u\xc4\x1dd\xb5\x86\xcc\x01\xb7\xa1;\xea\x8f!\xf3\x92\x92\xe7\xaf\xdbST\x057T\xd9\xebt\xa5\xcd\xd3\xe0i\x01\x0e\xbd6\x7f\x8e\x17U\xc8\xa5,\xeeK\xbba\x80\x0e\xf2\x14rN\xf8\xa4\xa6)M\xd4\xcf\x1a\xbb\x912w\x88\xd7\x040)\xd0&4\xd1\x9a\x97\xe3\x01\x9c\xc0\xe4\xa1\xc1\xdeo(\xd2\x89-\xa7\xe6d\xdc\xe1M)a\x1dl8E3#v\xcd\xcbc\xffV\xb4\x13\x1d\xb7bH\xeb\x8f\x8e\xf3\xc1\xbe\x94\xae\xf5&\x9a\x84\xa0\x08\xa3\xd9\x1b\x90R)Q\x1c\x87q\xc2\x0e\xa8\xd4\x06\x18?Y=y0M\x9c0BIg\xd5{\xfa\x94\x9f\xb3\xd2\\\xb4\x90\x1f\x8b(\x1b\xaa1V\xe9\xc1\x0eXu$\xe2\x92\x9acc\xf4)b^\x80E>\xe5C\xd2\xea\xfaZ\xebd/\xf9&\x15-v\xf9;\xdb\nx\xd3\x0b$e\x8fl\x08\xdf=\x7f\x92]\x05U&\xc4\x8b\x9f\xc0M/\x86\xae\x882\x9f>P\x9e\xb4\x06S\x90\x8c\xd6a\x8f\xba\xac\xa44P+\xb99t\xc7\xb1\xf0\xb7\x03x9\xad\xbc\x971\x02\xeej\x8c~\x9a4\xaf\xc6\x02\xdfAV\x00\x0d\x9e\xd6hH\x0d\xfav\xe0\xff\xb4,\x94\x9d\xee\xf2kaq\xb7\no\x9aTZ\xe5\x1d\xf9J\xef\xff\xbc\xfc\xdb_;I\xb8\x8e\x1d4\xb3\xa3\xc8\x0b\x96\x9f\xee\xde\xfd\xd20\xea:I\xd2\xf5\xed\xe8o/\xff\x7f\x01\x00\x00\xff\xffPK\x07\x08_;\x94/\xe8Y\x00\x00\xa8X\x02\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00 \x00swagger.yamlUT\x05\x00\x01\x80Cm8\xec\xbd]w\xdc6\x927~\xefOQ\xab\x8b\x91<\xa3\xb4bgf\xf6<\xda\xf5\x9eu\xec8\xd1\xacck\xfd\xb2\xfb\x99\xe3\x1a=9CYF\x9a\xb2\xe6\x0f\x00\xacp-\xfe\x01@\x9b\xcd\x06U\xbbsx.\x9f\x80\n\xd7MUR@E\x01\xf5\x1a\x03\xfe\x9c\xd3:/W\xa0x\xc87{b\x1f\xbf\xcf\xcb\x0c\x9f\x83h\xfb+\xba\xb8\x81\xafg\x7f\xfc\xe6X>K\xb6\xb8\xe2\x82_,\xf6-\xc9\xdf*L\xb7\xa4\xa4\x98*\x91\x00\x8e\x9f~\xfd\xf5\xf1\xfe\x7f\x07m=\x07\xdad\x19\xa6t\xd9\x14\xed\xdb\xb3\xce\xd34[\xe3\x0d\xea\xbe\x0fP\xef\xb6\xf8\x1c\xc8\xfc\x7fpV\xf7~\xd8VL\xb8:\xef\xb6/\xa8\xaf\xb3. f\xa8\xaa\xd0\xee\xe0\xb7\xbc\xc6\x1b\xcd+\x16 \x04\x99\xe5\xd8\xbf~\xd5T\x85\xfeW\xc5\x9e\xd6U^\xae\x0c\x8f\xf4\xb4\xf8o_\x19\x9e\x02x\x0e\x1f\xdf\xbd>\xab0%M\x95a(\xd1\x06C\xbdF54e\xfes\x83\x8b\x1d\xe4\x0b\\\xd6\xf92\xc7\x94\x0f\x11\xd66\x90\xa5\x91!{\x86\xe2*GE\xfe7\xbcxd|n[\x91\x9ad\xa4\x80y\xb3\\\xe2\n6\x98R\xb4\xc23\xf8\xb0\xce\xa9\xec\x1bl\x1aZCF\xca\x9aM\x0c\xa4S\xa5\xa0\x02#Z\x9b\xdb\"%\x86\xa3\xb3#\xc8\xd6\xa8BY\x8d+\xd6\n\x86\x02\xd1\x1a(^mpY\x03Yr\xd1?\xbe{}L\x81M0#7.T\x85\xb7\x15\xa6\xb8\xb4\xb4\xca\xd8-\x9b\xa2\xd8\xc1\xcf\x0d*\x98\x06\x17B\xbf\xb2)\xae\xc9\x13D!/\xcdL\xae\x99(g+BV\x05\x9eq\x9d\xcd\x9b\xe5\xece#\xa6\xd8\xf5c\xd1\x13\xce\x96\xaeIS,`\x8e\x81\x9b\x1a=!\xc8PI\xca\x15\xff\xa5\xd7\xa7@*(\x89\xfc\xf5\x94\x8f\xc6\x0c\x95@\xf8\xecd\x1a13\xc454[@\xbc\xef\x96vqu\x8b+\xa1\x9a\x0d\xdaR1\xb4\xb8\xe45Q3\x0b\x16x\x99\x979k\x93\x02\xa2FfKR\x14\xe4\x13=\xb7|\xdb\xdf\xc3\xc5r\xdf#6,\xb6\x15\xb9\xcd\x17x\xd1v\x9a\xfd\x11Q\xdal\xf0bfc\xf4\xbc\x84\x1f>|\xb8\x84\xef\xbf\xfb\x00\xa4TSP\xcc\xb1]\x8e\x8b\x05 \xe3\xdb\x7f\x1dN\x8b\x0f\xbb-\xfe\xe9\xaf?\x19_\x00\xb8EE\xc3\xc7\x83\x18o|\n\xa0\x9a\x7f\xa1mE\x16M\x86\x01\x95\x80\xab\x8aT3\x9b\xd4\xfb\xe5\x99\x02\xaa0\x1b\x9f\xe4\x13^0ug(c\xb6\x85\x90\x9bf\xcb\x96\xac\xa6\xa8)\xcc\x11\xc5\x0b\x8b}\xe2\xe3\xca\xf43\x1f\x84\\\xc65\xba\xe5Cp\xd3\x99C\x0b1\x89\x90\xea\x12\xfb\xf7-\xc9\x17\x80J\xf3\xc0\x02) 7\x1f\x15^\x92\n\x9f*\x06\x8c/\xaa\xf3y^\xe4\xf5\x0eJ\x8c\x17|\x18\xcd1p\x93W\xddZz\xc2\xfb\x92\xadQ\xb9\xc2\xfc%>ggp\xf2\x91b\xe5\xc50-\xb1\xe1\xc9l\x96\x18\x9f\xa8D+[\xef\xe7\x15F7\xcc\x06I\xc6\xb3\xc7\xe6\x11\xf5\x86\xd4\xf8\x1cj\xb6\x86,\x9b2\x133\x8c\xf5C\xda\xae\xac\xa9*\\\xd6\xc5\x0e\xd0-\xca\x0b4/\xac\xe6\x92\x8dG\xb2\\\xe6Y\x8e\n\xc7Z6o\x96Pa\xb6\x12\xe1S@\xe5\x82\xd9\x1f\xd9hC\xf1\x82\x0d\xb5\xfd\xbc4\xb2\x9a\xe3U^\x96\xac\xb3\x9f\xf2zmY\\v[<\x13\xe3\x1fms:\xcb\xc8\xc6f\x8d\xdf\xf3\x99J\x81\xd4ka(\xca\xa1\x95\x82\x13&\x1f\xf3\x017\xdbz'\xa7\xf6c\xf3\"\x98\xaf\xd65\xcc-F\x89w\x9au\x02\xf2\xcd\xb6\xc0l\x91\xe5\x13\x06\xe8\x16g\xf92\xcf\x80\xe2\x0d*\xeb<\xa3\xfa\xa9\xc6\xe7\xea\x08\x17H\xcc\xeds\x98\xefj\xd3\xe8\xf2\xf5\x92~d\xe6h\x8e\x011\xa1\xf2E\xc7\xc19\xf0c\xe4\xe2\x8e\xe6\xe4\xd6<\xa6\xa5\n\xe4T\xd0u\xdfG\xb2\xeb\xe7\xe5\xeeZ\xb9G\x94\x19.T\xcd\xf3\xbab\x93\xd8,\xa1\x96\x95Z#PA\xe4\xd0\x03\xa4\xff\xb4\xcc:\xf3\x85FH8\xef\xbb\x85\x03\xf7\xaf\xf5\xea\x0cC\xf3RM\x9c\"\x9fs\xb1\xe5:B\x816\xdb-\xa9\xf8\n\xbeE\xd9\xcdYS\xb2\xff\xb0u[\x8c\x0b\xfd\x0c\x92\x0b\xbd\xd9\xb1!Khja\xd8\x94y\xa0\xcc\xb0\xa2\xc5\"\x17\xb6\x02V\xb8d\x1b\x18.|\xbd&\x0b*\xbb\xa5\xe5\xc7\xe4\x11\x9fP\xdf\xdew\x9f\x11\x1b\xfc\xf0\xe4\x1c.\x99\xfc\xcc.\xc8\xae\xa0V\xe9y /\xfe\xf0\x07\xcb2\xf9\x8a\x10X\x12\x02\xcf`6\x9b\xfd\x8b\xf11&\x0c*w\xe6\x07P\xb9\x9b11^Uds\xb2$\xe4\xb1\xf9\xd1\xd9\xcc\xbc\xfe\xe5K8a\xac>\xf2\x8e| '\xbfc\xbc\x1e\xc3/\x16\x1bn\xe3\xf7\xab]wO\x1d\xba\xfb\x0b\xbaE\x93)\x0f\x9eq\xdf\x90\xb52\x81\x86rz\xf2\x8a\x90YV J\x1d\n\x12\"\xb2\x97D\x1f;/\x9ae0h\xaeU\xdd7\x0e\xd5]\xee\xea5)-\xca\x13R\xbd\"\xe4d6\x9b\x99W\x83Vq'\xd6g\xf8\xe0\xe3j\x8d\xd5*cr!\x94\xfa\xf2\xbb\xf7/\xde]\\~x\xfb\xee\xb1i\x91\x00\xd9\xac\x18\xa8\xf6\x86E\xd3vu\xfe\xd1\xa1\xce\xef\x89Y\x93\\\x95\xe7\xcf\xe0w\xdb\xf9\xec\x15!\xbf\xccf\xb3_\xcd\x0f\xa3rw\xca\xdcP\xf6\xc6V8Q?\xa2\x8a\xaeQ\xc1\x94l\xef\x88M\x85C),\"\xe4\xcb\x81\x00\x1f\xcb\xcd^\x04. \x9f \xfc\xa9\x7fz\x06e^X\x07\xb8].\xc3Hf\x9b[\xaege\x8b\xd5F\x03\xe6\xbb\xbd\xdb\xa5V\x8fOyQ\xc0\\\xef\xf5.\xf0\x125\x05\xf7\xc5\xf4M\x1dk\\\xaa3\xb6\x7f\x9f\xf1\x1f\x98\xbbz\x0c\xa8\xb3\xda\xb1\x95\x90\x8d\x04\xd3\xda F\x88\xbe\xb1vi)\x8b\x9d\xdaW\x1e\x04\x0bZ7\x19\xd0\xb2\xe6n\x9b\xbe!\x1e\xc78>;\xd67%\xd7D%2\xdf\xed\x02\x96#\xfahI\xc8l\x8e*\xde\xd9\xcfg\xbb\xd9\xdf\x8e\x84\x16\xf9\xdeK\xcb\xcf\xbc\x15\xe5\xa2\x1e1\x1el9\xd4>\xf2\x97\xf7o\xdf\xe8\x7fy\xf6\xec\xd93\xf3\x18`\xef\xedc.\xc2\x8f$\xcc\x1cH'H\xec\xeb\x1a*\xbd\x91\n\xaf\x9a\x02Uz~\x87l\xd8+\x0b\xbcw[N\x01o\xe6x\xb1\xd8;0\xa7\xc2\x1d\xd7\xb1C\x86\xe8M\xc7\xa5X\xf2\x8d\xec\xf5\xbf3\xd5]\xcb`B\xeb\xb6u?\x8e~\x82H\xf3sn\xd9\x80\xa0\xec\x86\xd9\xa0\xfd\x86x\x99\x17\xd8\xbcn(\x9bu\x89+JJ\xeb\xb4\x95\x91\xb8e^\xd1\xfa\x8a\x7f\xe1g\xf0\xc4\xcc\xb9}\x81\x0dJ\xf5\xfc\xd3\xf0\x15\x0c\xc0*\xd5\x11\xd7\xe5\xd19\x1c\xe9fm_\x0d3\xd1\xcb\xa3S\x1b?\xde\xbf7h\xc3x\xfe\xab\xe8\xc2\xbfY_`\xfd\x1b<\x1f\xda\xc9\x8b\xa5\xdcp\xf5\xc7\x9a\x18\x0d9\x85O\xb8(\xbe\xba)\xc9\xa7\x92\xdb\x995\xa2\x80 khM6\x81\x93\xab?\xe4O\x85\x03?\x98\x07\xc2xv\xc4a\x03\xd8\xb0\xb9BbH\xeb\x1b\xbb\xe6\x93Q\x8d\xf35)\x16b\x90\x0b\xc9\xc5T\xce\xcbv~\x80\x88\x00\xeaY\x89)\xa3o\x87\x8b0k\x17\xe7\x13f\xd7\x94\n\x0fBC*b\xfa\xd3_\x7fzl\x99HS\x8c\xb9~\x83\xf6a\xc7U\xc5X>\x99=}\xf2\x94\x1eY\x86\xd0\xf0/\x12iR\xc8\x05\x0fC\xd90\x1cE[\xb4\xcaK.\xda\xa1\xdb\xd4\xdbu\xee\x1f\x14\xa1Ci[;\x7f\x96K\x8b\x0e\x97\x11d\xc5A\xec(H\x89?\xd7W7xgFH\xac\xdb\x7f\xe7\xe6\xbf\xd7\xd5\xff5m\xfd\x95\x14l:\xb2\xae\xb2\x7f\xcaX\x18\xa2T\x04\xfc.\xd1\n\xbf\xc3?7\x98\xd63\xf1\xbb\x81\xd9\xcf\x0d\xaev\x9c\x0dc\xcb\x14\x89aCh\x0d\x98G\x98xXj\x06\x17ug*n\xeb\x1d\xe4&\xb0\xa5^\xe3\n\xf3\xef^\x12\xd8\x90\n\xabp\xa3n\x19\xa9I\x8d\x0c\x80\x92\xb72\x9b\xbc\xac\xff\xfcG=\x0f1\x1a\x8d1\x14\xde<\xd7\"\xffG\xd9l\xe6\"X\xa2\"\xa4\x9dp\x9c\xa9\xbf]E\xf3\xa1}\xc5\x99\x99f\xf3'D\x81\xe2\xfa\x14\xf2\x9a\xaa\xc0/\x85\xa6\x14\x83y!ba\x9fr\xda\x1f\x1f\xf6\xc0\xcb\x7f\xb2O\xa8@\xcdwr\xdc\xab\xc1\xa1\xe6\x810\xe0K\x19O\xe3\xaf\x9c=\xd7O\xc6w\x97/\xa4/x\x18A\xd0\xc3\xac\xedC\xd2\xa15C\xa7%4\xa5\x80B\xf0B\x84\xb4\xef\x12A\xe5\x0d\x98\xe0S\xed\xd8\xca\xc8B\x13\xde\x13/\xe4e\x8dW\x9aX\x95\x1a\x89yY\x7f\xf3t\xf0\xab\xb4\xfdA2,p\x8d\xf2\"\xc1\xbe \xf6M\xb0\xaf\xa0\x04\xfbrJ\xb0\xef!%\xd87\xc1\xbe&J\xb0o\x82}9%\xd87\xc1\xbe \xf6M\xb0\xaf\xa0\x04\xfb&\xd87\xc1\xbe \xf65Q\x82}\x13\xec\x9b`\xdf\x04\xfbvh\n\x08.\xc1\xbe\x9c\x12\xec\xfb[\x81}\xb7\xa8B\x1b\\\xe3\xaa\x03\x1d|\xc5-o\x17\xa7\x9d\xdd\xe0\xee*h\x838%\xa6\x89\xa4 \x15\x07\x01yPI\xc2l\x12(j\x01P\x1e\nZ\x0db&\x1c\xd1d\xd3\xde\x05j\xbee\x0b\x1e)\xf9^\x91,\x97\x14\xd7l\xfb\xd5\x17\x17:\xa1l\x8a\xeb\xaeQ\xcc\xcbs\xd1V\xe7o\x15\xfe\xb9\xc9+\xbc8\x87%*z \x9e!H\xa0\x0d\x0ch\x94(\xe43\xe9q\xb0)\x97\x9d\xe1\xaa,\x9b\x0d\xae\xf2L\xfd\x8d\xcf\xb6\x0c\x95\xac?\"*\xb2\xc6\xa5R|S\xb6\x81\xa8\x81\xfby\xc1\xb9\x15\x98\xd2\xbd\nE\xe8\xa6\xa1L\xd578P\x9f}\xf6w\xac\xdc\x01D\xacQo\x91or_\xed\xf2g\x15\xbcjB\x8eE\x90\xb2;\x82%\x18\xdb\x14\x03\xf0R\x84$\xba\x7f\xbaXB\x81\x97\xb5B\xda%\xf4\xae\x9cF\x1e_\x15\x13D4\xc2\xf4<\xdf\x01F\xd9\x1a\xd0v{\x8fZ\xec\xe2\xdf\xfb\xf7m\xba\xec\xbc\xc14\xcaG(\x81\xbaj0\xb0\x7f\xe4\xe5\"\xcfP\x8d[\xa4Ej\x90?(\x07R\x97]^fE\xb3\x18\xb8\x84H\xb4\xd2B]\x83/\xc6\x81\xd3N\x04\x96\x99\xee^:I\x8f\xd9\xc7\x0b:\xf8Z\x83.p/\xba\xc2TB\xdc|z\xed\xe7#\x9br39\x9b\xf2UI\xaaA\xfcZ\xcd\xc6~\x13B3c?\xec\x9c\x90\x02\xa3\xd2\xf6\x01+|\x8b\xab\xde\xab\xb6\x8f'\x9f\x1e~\xb8\xbc\x93?Qa\xfdL\xe8\xf1am\xe0\x92#{\xa4Z\xe0j\x18\xc0r\xe4\x1cL\xa1\x8d\x1a\xadz\x8b\xd8\x7fJn\xc7\xd6\xc3\xecg\xbf\xa0\xc5\xa2\xc2\x94\xfez\xec<\xd7\xbe?\xd6.\xff_\xa2\xfc\x02\x8e\x01\xc2\x9d\x10\xc6K}f\xdd\x91\xf5G\xaao\x0f\xfb\xc4\xba)s!*\xe7\xaa\xb6& 8c\xef>\xc1\xeb\xc9S\x0f\xfc\x12\x0fb\xd2\x0e\xec\xe9\x05Q\xc9\x05\xbc \x03Cgj\xc1\x04\x89\x05\x91i\x05F0\xd6/\xa9`TJATB\x01\xa0\xa20i\xd1/\x9d &\x99\xc0\x06\xf1y\xa5\x12L\x9cH\xe0\x95F0a\x12\x813\x85`\xa2\x04\x821\xe9\x03\xc1\xc9\x03\x13\xa4\x0eL\x9c8\xe0H\x1b\x98 \xe0\x1f\xa2,_\xb0\xdf\xad\x13o\xa0?\x02\xe6\xd7\xa3\x07\x13A\xfc^\x00\xbf\x1b\xde\xf7\x01\xf7\xadZ\x0c\x05\xf6}a}\x13\xa8?\x01\xa4\x1f\x00\xe8\xc7\xc3\xf9\x16\xd0\xdc\x17\xca\x9f\x18\xc8\xb7H\xa4\x1d\xa9Q\x10\xbe\x8a\xbcj\xf8\x19\x00\xfc\x89\xe1{3x\x1f\x0b\xdd\xf3\x88\x80Np=p?-lo\xda\xf89!{\x13\xa6h\x82\xeb\xa7\x05\xeb\xe3\xa1z\x03,\x1f\x05\xca;\x01\xf80\xf8\xdd\x1b|\x0f\x84\xdeC\x80w#\xecn\x96\xc6\x17\xfe\xf4\x83\xdc\x03\x01\xf7\x00\xb8]\xdb\xb5i\xa1v\xd3\xa4\x18\x01\xb3k\xe3\x14F\x90=\x0eb\xb7\xc1\xe9\xd3\x83\xe9\xe3G\x927\x90\xee\x0b\xa3\xf7\x97H\xff\x03\x9e\x11\xe7;\x07\xdc\xba\xc7;\xf7\xed\xa7s\x9b=J\xe76]\x83rO\x13\x83'\xbe\xf0I\x1c\x80bd\x96\xcem\xa6s\x9b{\x8a\x81Z\x8c\xcc\xd2\xb9\xcdC\x9a\x08v\x19\x07\xbcD@/\x93\x80/\x93\xc3/N\x00\xe6\x0e \x98\xbb\x02a\xee\x00\x86 \x01bb\xa1\x18\xab\x0dw\x811\x13\xc21\xbe\x80L $39(\xe3\x86eF\x033\xe9\xdc\xa6S\xb28\xa0F\xcb*\x9d\xdb\x8c\x81l\\\xa0\xcd4\xb0\x8d'\x16\xe1\x84n\x02\xc0\x1b\xe7\xf9\xb9@\x00'\x9d\xdbL\xe76}\xa0\x1d\xa7VC\xe1\x1d\x7f\x80'\x9d\xdb\x1c\xd0\xc4pO:\xb7\xd9\xa5X\xf0G\xcb,\x9d\xdb\x0c\x80\x82\xc6\x80AZv\xe9\xdc\xa6\xf6\x05/\xf8(\x9d\xdb\x9c\x0eLJ\xe76GCM\xd3\x8c9o\xb8\xc9\x1fp\xf2;\xb7)\x0f\xa1tx\xf4v\x91\xf2\xe7^\xed]\xf5\xb7\x9a\xc8b\xb2\xcb~\xe0+\xe7e{{\x96o\x7f8\xa7\xae\x1a\xc7\x11\xb4\xc0\xa39B\x98\xab\xf9\xee*_\x9c\xfd\x92/<\x8e\xe6<\x17\xef|\xbb\xbbxypJGun\x7fJG\xfe \x0e\x90\xa9~z\xdcE\xf9\xe7\xd9S\xdbm\x94\x1d!\x1e)%=\xecS>WR7\x01\x18Y\x98\x9a$3C\x11\xdf.(\xdaQ\x9e\x13\x1f\x1d\xb0\xd1\x8d\x81m&\x9d\xab\xf6\xd9\x84\x8f\x0e~M\xf8\xa8_|\x0d\x12>\x9a\xf0Q\xe3\x93 \x1f\xe5\x94\xf0\xd1CJ\xf8h\xc2GM\x94\xf0\xd1\x84\x8frJ\xf8h\xc2G\x13>\x9a\xf0QA \x1fM\xf8h\xc2G\x13>j\xa2\x84\x8f&|4\xe1\xa3 \x1f\xed\xd0\x14XU\xc2G9%|\xf4\x1f\x01\x1f\xcd\xbb\x06\xdbV\xcb6_(\x04\xaa\x8f\x18\xb6\x9b\xd7=j:\xc7\x1c8\xcd\xf1BF\xe6\x97\x07\x0b\x83\x8c;\xf3\xd8\xd4\x1cc6Xe=N8)\xf2\x1b\x1e\x1c\x1b4D\x1f\x8b\x10\x89\x1cs=v\xcdv\xc1w_5Q\\\xd8`\x81eS7U\x1bDj%m\xea\xb5@v'Du\xa1\x8f/\xb5uEMh\xaf\x16\xec\x9d\xe3l\xfd\xcdS3\xbe\xfb-\xff\xfd\xb2\xc2\xcb\xfc\xb3T1\x85y\xe7\x8f\xf2\x05\x1fXR\x8b\xdd~{\xc8\xeb\xc1\xa2\xb6\xa2\xdfW[.l\x00ng\x8f\x92t5\xe0\x84^\xfb\x1f\xa4\xdaf\x03fA\x97\x8e\xfe\xb9}(\x81\xb3\x07zL\xe0\xacOp\x0f\x128\x9b\xc0Y\xe3\x93 \x9c\xe5\x94\xc0\xd9CJ\xe0l\x02gM\x94\xc0\xd9\x04\xcerJ\xe0l\x02g\x138\x9b\xc0YA \x9cM\xe0l\x02g\x138k\xa2\x04\xce&p6\x81\xb3 \x9c\xed\xd0\x14@Y\x02g9%p\xf6\xb7\x02\xce\x86\x1d\x07\x15\xa0W{O\xdb\x15\xdb\n\xdbokS\xe7\x00kL?\x90\xf7b*g\xa4\xbc\xc5UM\xdb\xbb\xdc\x9e\xabS\xa1\xec1\xf6\xfdz\xfb\xf0h8Q\xd7\xb8|\xee\xc1\xc2\x8aJ\xb3B\x03\x01\x90\x93}\x83\xafS\x85\x13_\xd4\xf3\x90\x1fq\x7f\xaa3\x01\x8c\xdd\x17\x12\xc0h\xf8=\x01\x8c\x1dJ\x00c\x02\x18\xf7\x94\x00\xc6:\x01\x8czJ\x00\xa3\xa2\x040&\x801\x01\x8c\x9e^R\x02\x18[J\x00c\x97\x12\xc0\x98\x00F\x0d%\x80Q\xfbL\x02\x18\x13\xc0h\xa0\x040&\x801\x01\x8c `\xec\xd0\x14`O\x02\x189%\x80\xf1\xb7\x020zT\xc7\x150b\x87\xd3\x84\x87!;{\xe9qP\xa7h\xc1\x03\xeb\x14\xf8\xd8\x07\xc2\xb1\xb6\x0e\xd6)1Nimj\x02\xdd^\x8f\xc58{\x8d\xca\xe7\x1e<\xc6\xc9\x15\x10\x04zYb$^\xf8gOMN\xfc\xb3\x87\x9a\x0e\x18&\xf8S\xffB\x82?\x0d\xbf'\xf8\xb3C \xfeL\xf0\xe7\x9e\x12\xfcY'\xf8SO \xfeT\x94\xe0\xcf\x04\x7f&\xf8\xd3\xd3KJ\xf0gK \xfe\xecR\x82?\x13\xfc\xa9\xa1\x04\x7fj\x9fI\xf0g\x82?\x0d\x94\xe0\xcf\x04\x7f&\xf83\xc1\x9f\x1d\x9a\x02\x8aJ\xf0'\xa7\x04\x7f\xfe#\xc1\x9f\x07\xbb\xe2\x91\xf8gP\xd1\xd7\x0dY4\x05\xbe\x92\xc5n\xa9\x19\xde\xfc\x91?(\x8fl\xd2\xfd\xcd\x9eE!f\xc5\xe7\x9c\xd6\x1c\x03\xe0\xcf\xa9\xea\xb9m\xac \x1a\xe1\xec\xb7\xfbH\xa9\xe3\xa1b\x9b==v\xa9N\xc0T\x02\xa6z\x94\x80\xa9\x04L\xe9)\x01S \x98J\xc0T\x02\xa6\x120\x95\x80\xa9\x04L%`*\x01S-%`*\x01S \x98J\xc0\x94\x89\x120\x95\x80\xa9\x04L%`\xaaCS\x80\x04 \x98\xe2\x94\x80\xa9\xdf\n0%\xc8\xbe;\xe4\xc0Q\x1f\x7fq\x1f\x9a:\xfc\x04\x9c\xcd\xd9\x00?zw\xf9\"\x1d\xa0\xd2\xbc\x90\x0eP\x19~O8U\x87\x12N\x95p\xaa=%\x9c\xaaN8\x95\x9e\x12N\xa5(\xe1T \xa7J8\x95\xa7\x97\x94p\xaa\x96\x12N\xd5\xa5\x84S%\x9cJC \xa7\xd2>\x93p\xaa\x84S\x19(\xe1T \xa7J8U\xc2\xa9:4\x05f\x90p*N \xa7\xfa\xad\xe0TaU\x1b\x07\xa7\x99\xce~a\x13\xd2V\xb4\xb1\x87J}\xbbc\x03\xbc=\xda\xc4\xbea\xff4\x13\xe4\xe5\x92\xb0\x95X\xfe\x99q\x97\x1c\xcdg\x96\x04W\xf9\xd8C?\xb8d\xc2\x83\x0c\xd0\x8e\x1d\xd8\xa9\xad\xb0\x8e3\xa2\xe1\x13\x12\x98\x1c\xd0\xf1\x83sb\xc0\x1c;h\x13\x05\xd9\xf0&\x0c\x0c\x9d\x80\xcd\x04pM$Xc\x0cq\xfbA5\xa3\x80\x9a(\x98\x06PQ\x98\xb4\xe8\x07\xd2\xc4@4\xb6\xc0\xa9\x17@31<\xe3\x05\xceL\x08\xcd8\x81\x99\x89`\x991\xa0L0$3\x01 31\x1c\xe3\x00c&\x87b\xee\x06\x88\x99\x1c\x86\xf1\x07a\xe2 \x18\x8b\xd2]\x00\xccd\xf0\x8b\x1f\xf8\xa2\xf1\xfe\xcc\xf6ub\xe0\xc5\x05\xbb\x8c\x04],\x90\x8b\xd3=q\xc2-~\xfe\xcb\xb4P\x8b\x0bhq\xcb\x14\x07\xb2(\xcb\xaea\xe8\x82X&\x04XF\xc0+zP\xd4\x06\xaeL\x0b\xad\xd8\x81\x95)`\x15/\\\xc0\x01\xa9x\x03*\xe6\xd8g8\x98b\xe6\xa5\x8d3L\x02\xa3\x84(\xcb\x17Bq\xeb\xc4\x1b>\x89\x00O\xf41\x99\x89\x80\x13/\xd8\xc4\x0d\x9a\xf8@&V-\x86\xc2%\xbe`\x89 *\x99\x00( \x80I\xe2A\x12\x0b\x14\xe1\x0b\x90L\x0c\x8fX$\xd2\x8e\xd4(`D\x81 \x1a~\x06XdbP\xc4\x0c\x89\xc4\x02\"<\"\xa0\x13\\\x0f\x87L\x0b\x86\x986~N \xc4\x14\xa95\x81 \xd3B \xf1\x00\x88\x01\xec\x88\x82:\x9c\xb0F\x18\xa8\xe1\x0di\x04\x02\x1a!p\x86\x11\xcc0K\xe3\x1bT\xf6\x032\x02a\x8c\x00\x10C\xdb\xb5i\x01\x0c\xd3\xa4\x18\x01^h\xe3\x14F\xe8\"\x0e\xb8\xb0\x81\x14\xd3C\x14\xe3G\x927<\xe1\x0bN\x8c;B#\xe0\x80\x89\xce\xd1H\xc4\xa2{\x98f/W:%\xd3\xa3tJ\xc65X\xf741\xa8\xe2\x0b\xab\xc4\x01+Ff\xe9\x94L:%\xb3\xa7\x18\x08\xc6\xc8,\x9d\x929\xa4\x89\xe0\x98q\x80L\x04$3 (39,\xe3\x04f\xee\x00\x9a\xb9+p\xe6\x0e\xe0\x99\x10\x80&\x16\xa2\xb1\xdap\x17H3!L\xe3\x0b\xd4\x04B5\x93\x835n\xb8f4`\x93N\xc98%\x8b\x03p\xb4\xac\xd2)\x99\x18(\xc7\x05\xe6L\x03\xe7xb\x14NH'\x00\xd4q\x9eV\x08\x04v\xd2)\x99tJ\xc6\x07\xf2qj5\x14\xf6\xf1\x07~\xd2)\x99\x01M\x0c\x03\xa5S2]\x8a\x05\x85\xb4\xcc\xd2)\x99\x00\x88h\x0cH\xa4e\x97N\xc9h_\xf0\x82\x95\xd2)\x99\xe9@\xa6tJf4\x045\xcd\x98\xf3\x86\xa1\xfc\x81(\xbfk\x86:\x87T\xe0\x0b_.\xc4\xc5\xb2\xdc)t\xc9\x7f\x87\x9f\x1b\\\xe5X\xdc%\xb4\xef\x89\xb2z\xbd\x935\xcf\x9bz-\xdez\xa4d\x7f\xa0\x07j\xba}\xefRO\x18\xf1\x90\x88\xc1\xcaEj\xaf\x00\xb5\x06\x89\xc3F\x87\x8b\x80Q(p\x82I\x1b\xf4\xf9j\x837\xe4\xaa\xc5>,\x98\x93W\x16l\x93\x97\xf5\x9f\xff\xa8y\xa4\xfe|E\xf3\xd5U\x91or\xcd\xe9\"\x98\xae\x8d\xbf\xe1\xab\x8c\xd0\xfaj\x8b\xab\xab\xf9\xae\x1e\x9b\xd6kl\x8c\xf5\xe6\x16W\xf9r'\xda\xc3\x8b\xa7\x7f\xfa\xd3\x93\xff\xf3\xa5\x9a\xa38\xdb>\xfd\xd3\x9fo\x9eL\xdf\xa0\x07\x94-\xe6\x9e\x17z-\xf1j9\xc7\xdf]\xbe\x18\xf0K\x80u\x02\xac\x9d\xd1Z\x9f\x80'$\xc0:\x01\xd6\xc6'\x13`\xcd)\x01\xd6\x87\x94\x00\xeb\x04X\x9b(\x01\xd6 \xb0\xe6\x94\x00\xeb\x04X'\xc0:\x01\xd6\x82\x12`\x9d\x00\xeb\x04X'\xc0\xdaD \xb0N\x80u\x02\xac\x13`\xdd\xa1)\xc0\xc3\x04XsJ\x80\xf5o\x05\xb0v\x96u\x9c\xa3\xf2\xa6\xc5\x91\xe7\xa8@e\x86\xe9\xd9/h\xb1\xa80\xa5\xb6\x92\x8e\xcf\x8b\xe2[\xf9|\x8b,\xf3\xd0\xac\xf8#\xb7\xb7E\x01\x19aV\x8b)\x1b\x01\xcd\xcb\xd5\xbe\xca\xa3\x1ex\xdes\x95??X\xe4Yi\xeba\x80@\x0b\\\x92Mt\xf8\x03m\xf4U)\x059^\xf7 >\xbc y\xd9\xe2\xef\x08jr\x83K\x198\x10\xa2\xe7\xa5\xb0+\xcct\xa1R\nd\xda'\xbfy\xfb\xe1\xbbs\xbe\xd2\x8b\xe7\xe4\x92\x99\xf3\xa8\xc6EYKc\xd2F\x92\xba\x16E\xcbP8X\xfa\xc6h\xbe*Q\xddT\x98\xb6\x19\x1c\xcc\x9d\\\x91\x15\xe1\xd3\xf7p\x15\xee)D\x0d\x13\x05\xe2\xb6\xff/'\x88\x80\x1a\xf2\xf2 \xac\xb5E+\xa9\x14gv\x83zp\x90\xe1\xd0\xfeY:u\xba\xc9 \xc8:\xf8\xecC\xaf\xc4\x9f\xeb\xab\x1b\xbc\x1b\x89\x93\xfb\x95\xf7\xfa_S\xd0MI\xa1\xb4\xcc\xfe)\xa3\xd0\x88R\x11j\xbfD+\xfc\x0e\xff\xdc`Z\xcf\xc4\xef\x06f\xcc\x9c\xed8\x1b\xc6\x96)\x12\xc3\x86\xd0\x1a0\x8f\xed\xf2\x80\xf0\x0c.\xea\xce\"\xb8\xadw\x90[\x8a\x9dV\x98\x07\xfeK\x02\x1bRa\x15\xe8\xd79p5\xa9Ql)Wg\x96\x03@\x9d\xd7\x05\xb6D/y\xf3\\\x8b\xfc\x1fe\xb3\x99\x8b0\xa5\xc2&:\x81pS\x7f\xbb\x8a\xe6\xa6\xfe\x8a33\xad\xa3\x9f\x10\x05\x8a\xebS\xc8k\xaa \x17\nM)\x06\xf3BD\xa1?\xe5\xb4?><\xb2(:\x8b\x89W*\x85\x8e\xc7Yw\x9d{w\xf9b\xd8\x89\x94^\x91\xd2+\x1c\xb3\xf2\x0e\x90 [\"\xa2\xf4\xddL\x03P\xfe\xac&\x82\xfa\xdf\x9aH\xb3\xd7\xaeO\xcb>\xa892\xa1q/\xdf~]bV\xd8$\xe6\xc0\xd2K\xd3\x8e\xa4}\x10\xb5\xc39\x98)\xad\x8d\x9cA\xed:\xc0\x8d\xffj\x80\xd5\xf1\x1e\xb2\xed\xa6\xcb\xb6\xbf-\x8b\x1dG\xde\xc9\x12\xc8rIq\x0d\xa4\x82\xbe\xb8\xd0I\xa1\xa0\xb8\x1e\xea\xeag\xe9j+\xda+k\x89\n\xea\xd4\x96ae\xd4(Q\xc8g\xd2\xe3\xc02\xca\xcepU\x96\xcd\x06Wy\xa6\xfe\xc6wy\x19*Y\x7f\x04\x1a\xb7\xc6\xa5R|S\xb6v\x7f\x10\xf6\xbc\xe0\xdc\n6\x86Z\x15\n\xc8\xb0a\xfe>c\x10\xa6\xcf>\xfb;V\xee`\xa5\xd4\xa8\x97gTzj\x97?\xabf\x96i\x01\x15nIw\x04\xcb\x05iXAm+\xa0\xb0\xee\x9f.\x96P\xe0e\xad\x1c\x0e\xe9\x81\xa8`%\xc7\xf5\xc5\x04\x11\x8d0=\xcfw\x80Q\xb6\x06\xb4\xdd\xde\xa3\x16\xbbn\xc0\xfe}\x9b.;o0\x8d\xf2\x11J\xb8\xa1aN\n\xe4\xe5\"\xcfP\x8d\xdb\x0c\x1f\xa9A\xfe\xa0\x1cH]vy\x99\x15\xcdb\x10\x8aD\xa2\x956\xc5j\xf0\xc5\xf8\x8a\xd2qx\x98\x9b\xd0\xf3\xaa{\xcc>^\xd0\xc1\xd7\x1at\x81Go\xd9Z/\xd6~>\xbd\xf6\xf3\x91M\xb9\x99\x9cM\xf9\xaa$\xd5 oB\xcd\xc6~\x13B3c?\xec\x9c\x90\x02\xa3\xd2\xf6\x01+|\x8b\xab\xde\xab\xb6\x8f'\x9f\x1e~\xb8\xbc\xe3FVX?\x13z|X\x1b\xb8\xe4\x19e\xa4Z\xe0j\xb8!\xd4^a\xfe\xcd\xa4\xda\x18\x1bB9\x9b\xef\xae\xf8\x0e\xd7\x12K\x91\x0e\xa61\x8e\xa2\"'l\x9f\xe8\x1fK\x91\\\x1f\xa9^?\xec8\x8a\xc9\xd9\x8b\xda\x94Z\xa2!\x0e\x87\xcb\x16 \xb1\xbe\xea\x8e\x82L\x19\x03\x998\x02b\x8e\x7f\x84E?<6erTzm\xc8\xe4\x16\xac?\x90\x15\xa5jl\x92\xd2\xee+\xed\xbe\x9c\xce\x9a\x92\x8f\x1b\x1a\x93t\xfcG%[&\xcc\x15\xfb\x8b\x97x\xa1\xcblO\xc0\xb0U\x96KuE>\x95\xb8\xa2g\xbf\xf0\xff\xb3A\x15\x1dC\xf4\x92=\xfb\x96\xbf\xd8\xae\xb6|A-\x8a\xf6\xf6)\xf9QT\xf68\x07\xd4\xd87\xaf\xf3\x8c\xc3\xc4m7\x98\x01\xdf\xdb\xcc\xae W\xba\xe9i\xf7X\xeb\xad\xfc\xf9X>\xdb[\xbb;r\xca\x9f\x1f\xec\xfa\xdd\xfd\x18\x0f\xc3f\xc8\x0f8b\xd2\xbb\x17sAj\xfev\xe3\xee\xed\x9c\x96\x83\x87\xeaG\xcf\x90t\xc3\xa7OFGI\x90Ci\xe0\xa18p\x01I\xe0\xa7>\x17\xa0\x04~l|\xbf\xc2\x94\x8e\x95\xa0(\xf7\xca\xc8MB\xd96\x90 \x82]-A>*\xda\xdb\x92VI\xb4\xae\x9a\x8c5\xb5\x87\xf5yY\xe3\xb25\x82l\xf4j\xb9\xf1\x11M*\x89\xd4\x1b\xd2~\xf7\xe3}\xff\x018\x08s\x83K\xbe\xc9\xed\xe5\xb3\xc86msY|:\xf9\x9cmv\xec\x13t\x86\xcd\x1a>\xb7\xde(\x0f\x1eMX\x1c\xa7\x84\xc5\xed\x7f\xf2Uf\xc2\xe2\xb8/\xd7qh\xda\xad_w\x86\xb0\xdd\\\xbb\x07\xe41\x8fC\x17H\x11{\x96\x8f\x8b\xc39\xed\x98\xcdi\x7f8\xf85\xed\x0f\xbf\xf0\xfe\xd0\xba\xff\x1aL\x1d\xb1\xf9\xea\xce\x92\xfd\x9eL-'\xed\xd6\xac\xbb\x83ak\xf3p\xda\xdc\xd5\x862\xc1y\xee\xdd-$8O\xd2\x1d+\xd7\x0dD%8o\n-&8/\xc1y\xbf-8\xcf\x1cg\xa4W\x1b\\\xa3\x05\xaa\x919\xc2\xf8\xbf\x83\x08#\xfdQ\xbe\xd2\x83\xf4\xb2\x82[S\xc5\xae\x0d[\xe0[V\xdd\x85^_\xa7\xab\xdf\x98|\xe2\xc1F\nU\xc7\x1f\x88\xeb\xd8\xd5\x81\xf6\x01\x0f\xffO\x04?\x9b2\xaf\x1d\xe1F}\xd7\x04\x19;(\xc8#\xb0\xe7\xee\xac gp\xcf\xa3\xcb\x8a|BO\xdd\xa7\x99/\xdbF\x9b\xc4\x84\x10\xad\xf4\xca\xdc\xac\xf2[l.\xb0\xd0\xe5\xc5\x94\xce\xcb\xce@\x83j\xb2y\xac\x0f\x9a\n\xc2\x9f\xb7\xa4\xc4\xf6\x80\xa4k\x1b\xd5\xa5\xce\x96\xca\xb0\xb7W\x14\xa6%%gWQ[\xf2I\xac_O\xbe\xde\xff.\x82\xcb\xa59\xe6(h\xd3\xd8\n\x1d \xaaP.O\x14\xce\x11\xc5W-\xe2\x93\x97\xc2H\xb3\x7f\xe3\x9f\x1bTXc\x9c\x82\xf8\xd7\x13V\xe9c\x99\xd7\xc7T\xeew\x1c\xaf=\x91\x9f\xf4\x19<\xf9\xfa\xffk\xfb\xb8\x17\xc7\xf5>/>\xa4\"\xbe\x9d^\x90\xa5\x18\x1b\xfb*:Y\x85\x99\x97a\xae\x12#\xa8\xed\x01cq\xccX\x1c;*l\x08je\x7f\x06\x7f>\x85z\xdd\xd0sx\x02\xec}\xd1\xb7?{\x8cUT\xe4\x88\xda\xa7\xb1\x8fQ\x11\xe40-\x82\xbc\xa7\xbc+`\xa5HvA\xecK\x8a\x9cr5\xca\xc9\xae~S\x10\xbf\xf7\x847>\xe3\x17\x95\x84\xdeg\xed\xcc/$\xa3\xe1\xc3:\x15\xc8)Zo#\xde\xc8\xd1\"'R\x9e\xa9\xb0\xb3\xfe]\xb7*;K\xcb\xd0n*\x95v'\x1aw&\x8c\xcc\xc4\xc4\xecy\x17]bs&z\x0d\xf47r\xac\x99a_\xf8\xdf\xc4t=\xd9\xef\xc0\xd9/m\xef\x8c\xfc\xf8\x8c\xef\xcc\xb9\xaf\x0d\x13k\x91\xd3m\x81\x0ca\xf1\xf0\x1eZ\xc6\x98l\xa9\xdd\xcf\xc8u\xaeY\xad0e\xbb\x04i]\xd9Hk;\xebb&|i\xe1<\x1a\x8a\xdfp\xff~\xa2\xde\xe9a\xeao\x8e\xed\x03\xf9\x98/\xe2\xddXU\xafx\x1d\x87\xe4N\xf0\xea\x1c^p\xb6\xf0\x9c\x99A=O\xba\xdb\xccI|@\xcf\x7f8\x8a\x86\xf6\x1b|&\xa3\xfc\x9b*>G\xd7\xe4S \xa4\x04\xfcY\x96\x932r\xe3\xbd{\xfe\xe1\xed\x8f\x8feU\xc2\xcct\xd6\x1bD \x84\x8f\x0c~^_\x08 ?\xb7\xb50\x93k\x83\xb4\xa7\xa6\xca\xbf\x80\n?\xbe\xbb\x10q\x8a\x05\xc9\x1a^\x19\xf1\x840\xbf\x81\xed\x84\xbf\xca\xd6(/\x1f\xcbH\x94\x04\xfc\x8c\x9c:\x07\xe0\xf3R\xf8Wl\xa3\no\xb7\xe2\xaf\xc1j\x19bx\x82\x9a*\xbfZ#\xba\xfe2\xba\xf9\x01\xd1\xb5X\x05\xe9\x1a=\xfd\xd3\x9f\x815-\xe0\x8dVa[\xc2\xac\xfd\xfd!\xe1\xc3 N\x0d\xc3\xaa\x12\x9cz\x87\xcau\x03\x81 N\x9dB\x8b NMp\xeao\x0bN\xb5\x9c\xdb\xd8\xe3\xa9\x1eG7\x02\xd0T\xbe\xab\xde\xc79\xb5 \xf5\x87\xe0\xe9\xdf\x1bvj\xf2\xaa\xa2\xf6\x80N\x04\xd4\xe1\xdf8\xd1O\x17La\x85'\x1c\x9e\x1f8\xbb'\xc8\x89yz8q\xe0\xdc\xb8\x0c\x9f\x0d@;\x1d\xf0\x06\xf0\xe2\xa0\xbeX\xa7\x0f\xd2i\xdf\x0e\xec\xc9\x17\xe5\x0c\xd1\xcc\xb4\x08\xa7\x1b\xdf\x0cB7\x9d\x88\x0f\x84b\x9b\xe3\x90\xcd\x89q\xcd8Ts<\xa6\xe9\x81h\xba\x0c\x85 \x0f4\xd3s2\xbb\xc2)\x82\xa6\xc51m\xd3\xdc/Z61\x82\x19\x8f_\xba\xd4\x17\x83]Z\x855 \x97f\xdc\xd29\x0e\xfc\x8c\xd6\xb4\x88\xa5\x1f^iE+\xc3\xfae\x1cI\x13\xe2\x94\xbe(\xa5\x19\xa3\x0c\xebS\x10>95:i\xc3&\xc3\xbaa\x1crQ\xa8$\x97\xde\xc0\xd0\x13\x93\x8cA$\x9d;\x0cEF4r\"\xa5M\x85C\xc6\xa3\x90\xde`\x99\x1d\x81\x9cN\x1f\xd3a\x8f\xa1\xc8\xe38\xdc\xd1S\x91n;7\x0eq\xb4\xe0\x8d\xbe\x10\xc6D\x08F\x020\x12\x80\xf1\xf7\x03`\x0c\xbd\xdc\xde\xf0\x13n\x85\xa5\xc0\x08\xfb{7?~\x18\xbb\x1bqd-(\xf3?\xf0J\xed\xfa\xe0F\xe9\xcf\x9c\xdf\xe0R\xe9Ay\xae\xf2\xe6\xef\xfb\x8e\xed\x11\x91'\x8a\xcb\xc5\x15.\xd1\xbc\xc0\x0b\xdbB\xf8[\x08\x1dY\xbb)H\x17Z\x1d\x92\xdf\xc2\x0f\xf0\x1e\x97\x8b\xefD\x93\xe2z\xcf\xfe\xad1\xbf\x04\x10\xb5s\xd7\x1c-`\x8c\x19WS\x16\xa6W\x07^\xb2\xc5\x9amI\x16\xe7\xf0Q\x9c\x05\xef\xf6(/\xd5m\xee9\x85E\xfb\xac\xf1R\xaaW\xa4\xe2w\xb4\xd1\x9c\x9e\xb6W\xe7\x94\xf8S\xb1c\xbe\x1eS@W\x1b\xb2\xcc\x87\xed\x82M\xc9L\x0e.c\xbb\xefkR\xf1\x9b`\xc4\xcd\x99\xe2\x8e\xc7\x0d*\xf3mS\x88\x0d7\xdf|\xe4\xb4\x97z\x94\x9b\xbeDI>),\xec\x06\xe3\xeda\xac\x7fO\xcf\xb9\xfd\xe9\xbbO\xff|*\xda\xe2 \x0b\xfe\x9c\xd3Zl\xba\xe7(\xbb\xf9\x84\xaa\x85\xa9\xd9\xfe\xe5\x9ed\xd9\xf6~\x99\x17X\xbb\xcb\x93\x0e\xc8\x95\xef\x047\x8d\xf9\xde`\xb9\xb4\xde\xdf\xaf\"0\x1a\x83\xab\xe1\xa5\xf7\xd7\x06\x17\xaew[:\xf4\xd6Z\xa0\\\x18y\x9d\xb5\xe4\x92%\xb7,\xb9e_\xd2-\x0b\xf2p\x0e'\xa8\xce\xcf\xe9\x9a\xden\xbd\xb4\xee\xdfqY\xb3\xbf\xabqj\x9am<\xfe \xbc<\x89\xf5\xd6MU\xd2\xfe)G\xb1/\xe3\x17\x17\xb7\x17\xbfv\xdaj\x99Q\\\xd7y\xb9\xeab\xca\xcf\xcb\xdd\xa04\x02\xdf\xe2\x11,n\xd5\xe5\xee\xced\xbe\x9a\xb7\x9ff\xf4\xd1R\xda2'wX R\xda\xb2\xa4\x94\xb6\xac\x88\xaf\xb4\x9d\xc9\xecY\xe2j\xff\xc6\x80_Hu\xab\x7fn\x1fJ^\xe6\xe0\xd7\xe4e\xde\x81\x97\xe9\x0c\xfe\x19\x8b\x1f\x0f\xa6\x8dxX\x99\xd0\xd61\x93\x7f\xde\x91\x06>\xa1\xb2\xe6\x1bhh\xb63x\x8d\x99\x0b'\x8c_M\x98\xbb\xdac\x87\x8ab\xe8\x8aBd\x8e\xdb\xf0#k>\xafAw\x19)\n\xcc/\x8a\x7f%5\xb7i\x8a:?PTJ\xf3\x0e\xcb\xa1Mi\xdew\xa8\\w\x82rJ\xf3\x9eB\x8b)\xcd;\xa5y\xff#\xa4y\xd3\xad\xdcd^\x05\xdd(\xdcY\xed\xde+\x0e\xda+\x86\x19\x7f>\x16\x9dw\x0d\xb7\x0c\x07\xf7\xe4x$\x14\xe9\xeb\xf2\x1f\x08&\x1fz\xb0A\x99tK\xb1$\x9fp\xcc\xb4\x85\xe4\xa3\x8a\xc8?\x90[\x8a\xdb)\x9c\xee+\xeeS\n \x0d\x1fN\x01\xa4\xc9\x02H\xc3\xc5E\x1bFZ\xf5\xe2H\xedM\x0e\x03\x86]\x9cr\xd8\x91\xfd5\x0f\xc7T3\xd3=\xe3N\xa9\xaaz\x8a;=\x94\xb8\xd3\xc8[\xb74\xab\xdd\xb4\xb9g)\xf0\x13\xbb\xabN\x81\x9f;T\xae;d\x91\x02?Sh1\x05~R\xe0\xe7\xb7\x15\xf8\xd1\xc7}\x9a\xed\xb6\xd8\x99\x03<\x1f\xd8Xx\xcf\x1f\xea\xc5s\xc4\x18\x11o\xf7b9\xda\xbc\xe9\x0e\x97G\xaaK\x0f4\xf8\xd2UH\x97\xee\xc3\xa5K\xa1\x97\x87\x13z\x91\x9bZ9\xe2U\xc8\xa5\x1d\xffm\x88e\xf0\xa2w\x84E\x1bv\x88\x08\xbbh\xb88m\x8e\xa0\x14\xa0\xe9R\n\xd0\xfc\x1d\x04hL\x1d\xe1K^g\xd1\x89?\xcd\xd7]\xff\x8cg\xf9\xda?\xa6\xa8\xca\x81~RT\xe5\x8bFUR\xac\"l#\x98b\x15w\xa8\\\xf7.;\xc5*\xa6\xd0b\x8aU\xa4X\xc5?L\xac\xe2l\xbe\xbb\xeal\x8c\xb5'\xa0\xf8\x83o\x97\xfd\x0c\x94}\xacBf\x9a\xf0\x0d\x9b6^\xa1\x18\xf2\x8e| '\xbfc\xbc\x1e\xc3/\x16\x1bn\xe3\xf7\xab]wO\x1d\xba\xfb\x0b\xbaE\x93)\x0f\x9eq\xdf\x90\xb52\x81\x86rz\xf2\x8a\x90YV J\x1d\n\x12\"\xb2\x97D\x1f;/\x9ae0h\xaeU\xdd7\x0e\xd5]\xee\xea5)-\xca\x13R\xbd\"\xe4d6\x9b\x99W\x83Vq'\xd6g\xf8\xe0\xe3j\x8d\xd5*cr!\x94\xfa\xf2\xbb\xf7/\xde]\\~x\xfb\xee\xb1-\x94\xbb\x1f\xa8\xf6\x86E\xd3vu\xfe\xd1\xa1\xce\xef\x89Y\x93\\\x95\xe7\xcf\xe0w\xdb\xf9\xec\x15!\xbf\xccf\xb3_\xcd\x0f\xa3rw\xca\xdcP\xf6\xc6V8Q?\xa2\x8a\xaeQ\xc1\x94l\xef\x88M\x85C),\"\xe4\xcb\x81\x00\x1f\xcb\xcd^\x04. \x9f \xfc\xa9\x7fz\x06e^X\x07\xb8].\xc3H\xfe\xc0\x8f'd7\xad-V\x1b\x0d\x98\xef\xf6n\x97Z=D\xee\xbc\xde\xebUyw\x0d5\xf8,\xc7\x1a\x97\xea\x8c\xed\xdfg\xfc\x07\xe6\xae\x1e\xab[\x91\x94\x1f\xc7G\x82im\x10#D\xdfX\xbb\xb4\x94\xc5\xae\xadI?\x0c\x16\xb4n2\xa0e\x8duI\xe5\x82x\x1c\xe3\xf8\xecX\xdf\x94\\\x13\x95\xc8|\xb7\xdbF\xe9\x8f\x96\x84\xcc\xe6\xa8\xe2\x9d\xfd|\xb6\x9b\xfd\xedHh\x91\xef\xbd\xb4\xfc\xcc[Q.\xea\x11\xe3\xc1\x96C\xed#\x7fy\xff\xf6\x8d\xfe\x97g\xcf\x9e=3\x8f\x01\xf6\xde>\xe6\"\xfcH\xc2\xcc\x81t\x82\xc4\xbe\xae\xa1m\xacw\xd5\x14\xa8\xd2\xf3;d\xc3^Y\xe0\xbd\xdbr\nx3\xc7\xfcn\x009\xbbO\x85;\xaecg\xbaN\xab\xe3R\x88\x94\xa0\xeb\x7fg\xaa\xbb\x96\xc1\x84\xd6m\xeb~\x1c\xfd\x04\x91\xe6\xe7\xdc\xb2\x01A\xd9\x0d\xb3A\xfb\x0d\xf12/\xb0y\xddP6\xeb\x12W\x94\x94\xd6i+#q\xcb\xbc\xa2\xf5\x15\xff\xc2\xcf\xe0\x89\x99s\xfb\x02\x1b\x94\xea\xf9\xa7\xe1+\x18\x80U\xaa#\xae\xcb\xa3s8\xd2\xcd\xda\xbe\x1af\xa2\x97G\xa76~\xbc\x7fo\xd0\x86\xf1\xfcW\xd1\x85\x7f\xb3\xbe\xc0\xfa7x>\xb4\x93\x17K\xb9\xe1\xea\x8f\xb5\x16@\xf8\x84\x8b\xe2\xab\x9b\x92|\x12\x89hkD\x01\xd9N\xa8\x99'W\x7f\xc8\x9f\n\x07~0\x0f\xf6\x07\x8f\xa48l\x00\x9b\xce\x02\x8a!\xado\xec\x9aOF5\xce\xd7\xa4Xts\xde\xc4T\xce\xcbv~\x80\x88\x00\xeaY\x89)\xa3o\x87\x8b0k\x17\xe7\x13f\xd7\x94\n\x0fBC*b\xfa\xd3_\x7fzl\x99HS\x8c\xb9~\x83\xf6a\xc7U\xc5X>\x99=}\xf2\x94\x1eY\x86\x90\xf8\xaf5\xdfi\x7f\x895L\x91\xa8\x04\xee\x83\x0d=\x9b8A\x83\x8a\xb3@\x1b\xa7\xe5m\x82#\xdb\xdeT\xfd-{L\x93\xf6t\xe4\xf7\xb8\xba\xcd3\xec\x91\xeb5/HvC\xcf\nTc*\x91=]\xba\xd7\xf7\xb8~\xcd\x1f\xf9\x96=\xdf\xde\xa7\xc0\x91e\xfew\xe0\x8c\xb4I\xc9\xfdw\x1f\xa9\xfe=\xd0\x8c'\xde\x8f\xab\xdcx\xab@TN\xcd\x88;\x18\x1d\xc1\xa5-\xaa\xea+\x8a\xeb\xab5F\x0b\xac\x81C\xc1%98\xa5gd9\xd9 N\x0cU\x91\x0d\xa5WdV\x15\xf8\xa8\x0b\xdc*k\x8f_^\xa2\xaa\xa6\xb8\xfe\x81k\xee\xf0s\x8b\x87\xf8\xa0\xbdx\xa9\x1b%\x9a!\"\xaf\x80\xed^,\xb5\xe5\x91Z\xee{_\xd3\xc5\xcd\x15\x7f\xf5\x1a\xf2\x92\xd6\x18-\x0e/~\x1d3\xcc\xeev\x0c\xc8\xb8\xba\xe5\xeb\x88\xee\x8b\xcb,y?y\x02\x9f\xe3kZ\x93\xc2|\xe4\x02\xf3\x07\xe9\x92\xd7\xd8\x01\xbf\x93\xc8\x82\xd0v\xfbe\x9b\xf4 \xd6\nz\xc1\xcccI\x1b\n\x19\xda\x8ad~\x89L\xca?WM!\xef\xd7\xd9V\x84\xd9U\xbb\x88\xa8\xfd\x9e\x02Ee\xff\xc3\xef\xba=\xb5D\"\xbaY\xb5\xcc\xe7k_\x12\xd9]m~\x0cU\xdb[!\x95\x85\xa1\nz\xefa\xb1c\xcb%q\xc0\xaf1\xc1PW\xa8\xa4\xc2\x07\xdc\xa0l\x9d\x97\xdaH8#.\x9d\xd6\xfa+\xf2\xf8\xa4\xa6\x0c&E\x1e,\xfcle\x9d\x9b\xae\xb9\x86\xd0f\x16\xa8\xc6_1~\x86'\xf9.\xcb\xbc6*\x9apF\xdb\x17\x02\xf0\xed \xf8,\x08\x82\xbc\xd6RE^=\x05\xef\xde\x82{\x8dm\x1f\xf3Zk\x15\xf9\x8c#En\x95C\x88\xda\xc1_\xf5\x9ek\xf2\xe0a\xfd\xda\xac\x88\x0f\xd9\x8cl6ym\xb9\x04\x1b|{\xe4\xd5\x13)\x18kN\x94\xb1\x15Vs\xb0a\xea\x12\xfb\xe9\x0b\x89\xc7!ET\x93\x8a~\xa1\x06\x87\xfaXV\xfc\xa2\x7fn\xbf\x814\xf5\xb6\xa9\xf7\x7f\xdbV\xf8V\xa8\xcb\xc0\x8d\x9f\xe2\xff\xe2}hW\xcc/\xd4\x1e\xdan\xbfPK|~\xc8\xbc\x91/\xd4$\xbe\xcd\x17\xb8\xcc\xf0\x17j\xae\x1d\x7f{\xb7\xc7\xe2\x8f2CM(\xae\xaed\xcd\xce\xbb\x96\xaf\xe7\xcc \x83\xd7\xcb#\xde\xa7\x0f\xf3\xb3\xbf\xc2\x92\x88\xa5I\x0b\xe4\x1b\x8f\x1a8\x97*\xf7\x12U\x7fv\xaa\xc3|0\x04\\\x87C\xc0W\xa7\x1eZ\x85 7\xf9\xc3g\x99\xe8\xa7\x82\x92\xdc\xb3\x14\xa8\x8f\xf0\x1d\xff]F6~\xe0N\xdd\x1f\x9eX0LuF\x97\xa7.\x16\x05S\x1a\xb4\xd5\x93\xb8\xe1\x9a\x01\xfc7>\xae0\xfcOCk@\xab\nc{we\xa6\x1a?\xe6.B\xe3\xd6\xf6yJ\xeb\x06#u\xb9\xa5\x10\xfd\xf9v\xfb\x03\xa2\xeb\xfd\xfd\x94\xb2\xec\x01cMm\xabr\xfd\xd9\x901\xe3*\xc4\xf4R\x9c\x04\xe9\x00\x0f\xbc\xb8\xc7Rx\xe2H\xa6CH1\x1c\x97 \x9b\x96\x04eK\xeel\xcc\xdb\x1b\x80i\x07\xbe\xd3\x91t\xcb+\xa8=\npuKj|\xe5\xee\x84 O) @\x12F\\\x06\xeb (E\x01\x02@\xa0\x10\xa0\xd8{=\xe9m\x8a\xba\x84\xcb\xc6zy{\x9f\xbe\x82\xf7\x17\xdf\xbf\xf9\xee\xe5\xd5\x8f\xef\xbf\xbf\xfa\xf0\xff.\xbf\xbb\xfa\xf8\xe6?\xde\xbc\xfd\xef7#8\\\xbe\xfb\xee\xbf\xde~\xf8n\x1c\x87\x17o\x7f\xfc\xf1\xe2\xc3(\x1eo/\xdf\xbe\x7f\xfe\xda\x93\x85:Q2R\x1f\xfe\xf6\xbeO\xef\xf3U\x89\x17?\xd2\xd5\x87\xf6$M-\xd3\xcb(\xff\xc9\x9bS'7\xa3\x17f\xb1Z\xeb\x01\x19\xbf\xe99\xfc\x17\xa9\xada\x91\x01\x99\xbf\xcb9\\r/\x07\x15~\xec\\\xa1\x8c>EL\x9c\x90\xed\xa9\xa0\x8a4\xa5%\x02\xd1\xa7\xb0\xbd\xb2 \xdb\x89\x1f=\xb9\xe3\"}\n\xb4u\x10a\xef\xc0{;\xbf\xa7\x88\xcf\x07\xbe.\xd9\x90\x82b-}\x8a\xd0\x1eDj\x90\x91gT\xa6O1\xe3NQ\xf8\x94P\x14\xfa\xc1!\xfe\xa3C\xec\x87\x0f\x8c\xf4\xf4\xc9+\xee\xd3\xa7:\xdf`Z\xa3\x8d#R\xbf\xa7\x08\x85\xf8\x06Q\xfb\xd4\x862\xdc\x1b\xce>\x8d\x90\xd0\xfbS\xed\x853TT\xd0S\xcc\xb8\x0f\xb7\xb7m\x85\xa20\xb1\xeeRc1.\x08[\xd5\xf7\xc91\xcc\xfb\xd8V\x98y\xcc\xa7\xf2\xd4\xcd&\xe7'\x86\xbc\x98\x89\xc7\xb9\xc3-bj\xfbH\x19\xeb\x8b\x9f\x1f\xd2\xf1[\\\x8fr\xcf~\xee\xf3\x01\x02\xedu\xa8\x9dN\x9e\xfd\x80\x92g\x9f<{7%\xcf\xde\xf54$\xcf>l\x0d\x14\x94<{#\x85O E\xa1\x1f\x1c\xe2?:\xc4~\xf8\xe4\xd9+J\x9e\xbd\xa0p{\x9b<\xfbC\xfa\xd2\x9e=7\x8bW\xb7\xa4\xce\xcb\xd5\xd5\x96|\xf2\xb3\xe1\x81\x1f\"\xcc\x14\xee\xc7\xec\xc3\x90'\xc8\xf4DJ\xe2krB\xc7\xe7K\x05\x10\xb1A\xfa\x9d\x84\x87\xf6`\x9d\x02\x8c8\xe8\xecd\xd6~\x18\xe9VC\xfd\x890^\xcb\"\xcfx\xf186j\x1dc\xae`\x8e\xe8UV\xe4\xb8\xac\xafP]\xa3\xec\xe6\xbea\xabN\x0f\xae<2K\x05\x05\xc8\x02\x81\xf2\x804\x8dx\x11\xe8U\x05\xca\x04\x11r\x81#\xf1XO\x11\x82A\xa4p\xe0\x93\xba\xac\xa7\xd0\x84f=E\xf6\x15F\xf4\x17\xfcR\xa2\xf5\x14h\xb0\x86\xa4\x0c\x983}ZO\xce\xa4j=\xdd\xa7\xd0\xa1&xH~\xc9\xda\xc1l\xfb\xc9\xdd\x83\x14\xee`n\x9e)\xdfz\xf2M\x04\x0ff\xdcK\x1c\x0fN\x0f\xd7Sh\xd2\xb8\x9e\xdc\xa9\xe4z\x8a\x1e\xc8a\x11\x1dE\xd1\xcd\x85\xfaL]\xb2'\xae\xebi\x02A}]\xaa>y\xa6\xbe\xeb\xe9\x9el\x7fL\xbc\x00\xc6)\x19\xc2\xb7V}\x1a\x118R4B\xdb0R\xe3\x10\x1bPR\x14\xb3\xc1\x1eR\xfc\x8cT\x14;p`\xfc\xe0\x81\xb1\x03hT\x00JQD J\x91\xff\x81\x03=\x8d\xd0\xdf\x08\xbd\x85\x1fY\xd0\x93\xc7A\x06=\xddG\xb7\xbd\x8f\x11\xe8\xe9>Dvg\xdd\x9a\xc9\xf7\xfcE0c\xdby\x0d=\x85\x9d\xe2\xd0\xd3}\xa8\xdf\xf7\x1c\x88\x9e\xeeCb\xf7I\x12=\xdd\x87\xac\x01gQ\xf4t\x1fB{\x9ef\xd1\xd3}\x08\x1cv\x1eFO\xfe\xa7d\xf4\xf4\xe5\xfb=fw\xeey,'\x88\xa7\xf9\x08\x8f\x9e\x84G\x11\xa2\xe7H78\xd6\xfd\xfd;\xdax\x06\xa5\x18(\x1a\xe3\x99\x87C`\x8a\xd2\x8e\xd3\x87\"m\x82\xa0\xb4\xe3\x8c\x1e\xd7\x8a\xe2\xa7\xa2\xa2\xd8\x81\x03\xe3\x07\x0f\x8c\x1d@\xf7\xbd\xe3\xdc_\xd5\x19\xaaA\xa19\xfb\xd15=9\x0f\xb4\xe9i\xd4D\x197M\x94%\xbdZ\x16Hs\xfd\x9c\x0f\x8d\x1ehai\xa4}\xfa\n\xbe}\xfd\xf6\xc5\x7f\\]\xbc\xbcz\xf5\xfa\xf9\xf7\x81)\x94C\x1ar{\xfe\xed\xfb\xef\xde\xf8g\x86\xf6i\xc8,0\xcd\xb4OCfo.|\xb3M\xfb\xd4\xe6\x9eN\xa7\xb6\xf8\xad\xb7 1\xb9\x17\xaf\n\xb4j/)W\xb7}|[d\xe4\xe6\xe2e\x14(#\xa85\x03\x90\x8b\x94\x10\xd7\x0b\x87\x14\x9d\xb2\xd4\xa7\xd1\xf3d\xb49\x0eH\xcc8\xa4\xc9\xc4\x8fC\x1a\x04\x05\xe7@\xf5i\xb2>D}\x821\x9b-A/\xf8v\xe7}\xbe\x12i\xdb\xccGS\xb8\x1eO\xabR\xa7\xc6#X\xf3;`\x05\x7f\xff\xed\xd7\xb8>\x89\xd6\xfag\xe1\xdb\x14\x1b~F?,\xca*\x82\xb3\x9f\x10\x95\xdb\xc2Z\xd4*@\xf2\x80}\x10\xaf}\x14\xceO\x1b{\x1bA\xb1\xf7v/b\xdd\x8fY\xed\xf7}\xf1\x7f'\xd6\x0b\x8a\xf0\x80\"\xb4 (F\x17\x82\xa2\xed\xf8(\x032\xc2xl\x9b\xf9\x95\xe3jV=E+\x17F)\x98\x11^<\xfd\xd3\x9f\x9e\xfc\x9f\x98WG*\x1a\xc6)\x1b\xf8m^\xd9\xf6\xe9\x9f\xfe|\xf3\xe4\xefQ\xfc1\x1e\xd9e3/\xf2\xec?\xf0\xae\x17\xd8\xbb\xc1;\xda\xb9\xac)\xce\x8fj(\x16\xb7\xda\xfcWk\x90\x02\xb9\x84&\xde\xf6i\xd4G\x19\xb3\x99o\xe3\xc2\xdb*'U^G\xcf\xe3/*\xbb\x92:D\xd8Hs\x13kh\"\xed\xf8\x08eF\xcf\xcbH\x0b\x1e\xa9P\x18\xa1T\x18g\xbbG(\x17\xc6(\x18\xc6Z\xed\xfb\x13<\xde^\xdf\x99\xb5\x1ek\xab\xc7X\xea\x11\x1f\"\xce\xd2\xc1\x146\xfa\x8bK\x1dw\x16EQ\xa4\xb8a\xa2\xb2}\x18)\xaf\xfcA\xb0@\xa9\xc2\xa4\x99\xef\xfe\x86\xca:/\xf1U\xd8\x8e(l'\x14\xb0\x03\n6\xee\xe1&=x\x85\x0c\xfc\x02\x82\"l_\xf0z\x18\xac,\x88R\x18\xc4\xae~Q\x8a\x838\xe5A\xfcZ\xf7e\xc5\x8cY\xd9\xee`M\x8b_\xcd\xe2\xcck\x94\x92\xc3\x8c\x99\xa0\x11\xab\xd6\x17\x901n\x85\n\x14,P\xa4\x90 x\xa4$\xbe\x01\xee\xd0\xd8\xe9k\xb6\x8a\xbe\xe0g\x01\x9f\xf3\xa3\x80cO(\xca\xc2\xa3\x9d3\xb3\xa8\xae\xf1f\xcbO'\xd6\x0469-0Z\x00\x12\xe7\x10\x9d\xfc\xc49E]\xc0\xb4\x93\xfe\xabW\xbc\xd3\xba\xbb-\xb9\xcb\xcb\xf0\xfa\x9a>\xa3\xc9\x91(#\xdaq\xa5\x0e\xf8$\xbf\xb8\xd3\\\x9cj\x03/\xd5\x81W\xae\x81\x97\x02\xc1\x7f\xa9\x08J1\xf1\xea)x\xf7\x16\xc0;A\xc4\xef{*\xf2\x19@\x8a\xdc*\x87\x10\xb5\x83\xbf\xea\x03\x933\xbc\xd20|\x12.|\\i\xa7\x03\xed9\x14|\x07BP\nD\xc0\xc7\xf0Kk\x88M`\x88LU\x88LJ\x08O?\x18\x95h\xe0\xef6N\x95<\x10\x94&\x10\x9c\x10\x100j\x02&\xb0\xa7'\x13\xd1\xb8\x9f\x07\xe3\x0d\xc6GH\xe0\xe8~\x88\xeb\xe4\x0b\x9as\x08\xdc\x83\x93\x1e\x10\xf6\x93\xc8\x03\xf26\xc0\xd8\x06\x86\xe8\xd0\x89\x1b\xca\xd7^\xacv\xf8\x99\xd4\x9dl\xef\xf32\xe3\xa9\xeftC\xe8Wtq\x03_\xcf\xfe\xf8\xcf\xe9\xfe5\xd7\"\xe3\x96\x0b\xfc\x8a\x0dx\xcf\x105?\x9cg\xf1\x9d\xa5\x02\xa6n\xd2o\x02\x80\xf7\x91~H\xf7\xaf\xf5\xc9}h\xde\xe3\x93~\xb1MR\xba\x7f\xcd\xddA\xf0_\xf0\xd2^\xc9\xa9r\x08Q;\xf8\xab\xfe.\xf6J\xfe\xc7\xa1\xbdz\xe4\xd5\x93\xf0\xc3\xcc\x1e\xc7\x96\xa7\x12\xcf\xfb\xd4\xebT\x0d\x0e\xf5\xe1<\xff\x9b\xee_\xf385;UK\x01g^\xa7j\xd2\xf3\xc4\xeaT\xcd\x85\x9d7\xf5?Y\xea%\x9f\xbf\xaf6l\x98\xed\xa1\xc4\xad[\xf9*/\x91t\x9c\xda\xc7,\x9c$\x83S\xa9\x1c\xb6\xabAl?\xf6-\xce\xd6\xdf<\x95\x12[+G_\x94\xf0\x01\xab[\xdaO\xa1^3Yd\x0d\xebk\xa6jz}\n\xf3\xa6V\x8e\xe0\xfb\x97\xffa\xbb\xf7\xff\x13w9oqUC^CM\x86\xb2\xd8Da\xbe\xe9\x1c\xd75\xae\xe0\xe3\xff\xf5\xd8\x10\xba\xcf\xca\xee{\x96n\xb3\xe3\xe45\x87\xfc\x07r\xba\xcd\xce\xe6\xe3\xa4\xdb\xec\xd2mv\x87\xe4+ \xa4\xdb\xec\x0c\x94\xee\xbc\x90\x94\xee\xbc\xe8P\xba\xf3\xc2\xeb\x9d\xa0\x82\x14a\x91\x07A>\x18|\x9f\xdcQ\xa6>\x05\xda:\x88\xb0w\xe0\x1d\x1c\xd9S\xc4\xe7\x03_\x97lHA\x91\xab>Eh\x0f\"5\xc8\xc83\xc6\xd5\xa7\x98q\xa7(|J(\n\xfd\xe0\x10\xff\xd1!\xf6\xc3\x07\xc6\xcd\xfa\xe4\x15E\xeb\x937 \xaa(B!\xbe!\xe9>\x05\xe3\xc5\x8aFH\xe8\xfd\xa9\xd2\x9d\x17a\x1a\x8bqA\xfe\xae\xef\xbcH\xb7\xd9\xe9(y\xf6\x92\x92g\xdf\xa1\xe4\xd9{\xbd\x93<{\xd7\xb3\x8aB\x1d\xbd\x88\xcf\x07\xe1k\xa0\xa0\xe4\xd9\x1b)|J(\n\xfd\xe0\x10\xff\xd1!\xf6\xc3'\xcf^Q\xf2\xec\x05\x85\xdb\xdb\xe4\xd9\x1f\xd2\x97\xf6\xec\x1f\xe0\xf9\xact\x9b\x9d\x99B\xc7g\xba\xcd\xce\xc54\xddf\x17\"\x17\xa4\xdb\xec\x1c\x14\xd9W\x18\xd1_\xf0K0\xd7S\xa0\xc1\x1a\x922`\xcedt=9S\xd4\xf5t\x9fB\x87\x9a\xe0!\xf9\xa5\xbe\x07\xb3M\xb7\xd9\xb9\x92\xed\xf5\x14\x9a\x82\xaf'wb\xbe\x9e\xa2\x07rXDGQts\xa1>S\x97\xd2mv\x1e4\xc6\xf6\xc7\xc4\x0b`\x9c\x92!|k\xd5\xa7\x11\x81#E#\xb4\x0d#5\x0e\xb1\x01%E1\x1b\xec!\xc5\xcfHE\xb1\x03\x07\xc6\x0f\x1e\x18;\x80F\x05\xa0\x14E\x04\xa2\x14\xf9\x1f\xdf\xd0\xd3\x08\xfd\x8d\xd0[\xf8\x01\x10=y\x1c\x0b\xd1\xd3}t\xdb\xfbP\x86\x9e\xeeCdw\xd6\xad\x99|O\xb3\x043\x0e\xbf\xcb)\xecL\x8c\x9e\xeeC\xfd\xbe\xa7j\xf4t\x1f\x12\xbb\xcf\xe5\xe8\xe9>d\x0d8\xd9\xa3\xa7\xfb\x10\xda\xf3l\x90\x9e\xeeC\xe0\xb0\xd3Ez\xf2?s\xa4\xa7/\xdf\xef1\xbbs\xf7 \x9dp\x0b\x98n\xb3\xe34\xc1@\x08ws\x83R\x0c\x14\x8d\xf1\xcc\xc3!0Ei\xc7\xe9C\x916AP\xdaqF\x8fkE\xf1SQQ\xec\xc0\x81\xf1\x83\x07\xc6\x0e\xa0\xfb\xdeq\xfa\x14W\xd3\x93\xd0\x9co\xf5\xe2.9\x0f\xb4\xe9i\xd4D\x197M\x82J\xb9\xe9i\xf4@\x0bK#\xedSl18=E\x96\x88\xd3Sd\xe18=\x85\x97\x93\xd3\xd3\xa8\"sz\x8a\xdfz\x0b\x9a\xaa \x9d\x9e\x82\xca\xd4\xe9):e\xa9O\xa3\xe7\xc9hs\x1c\x90\x98qH\x93\x89\x1f\x874\x08\n\xce\x81\xea\xd3d}\x88\xfa\x04c6[\x82|\x0b\xf3E\xb0N\xb7\xd9\xf5(\xddf\x17\xe7\x05Ex@\x11Z\x10\x14\xa3\x0bA\xd1v|\x94\x01\x19a<\x82\xef~P\x14\xad\\\x18\xa5`\x88\xbd\x13B\xd1(E\xc38eC\xfc]\x11\x8a\xeeW\xfc1\x1e\xd9\x1d\xdc%\xa1(\xfeN E\xa1\x89\xb7}\x1a\xf5Q\xc6l\xe6\xb7\xf1wN(\xba\x07\xd9\xb7\xe96\xbbC\x8a\x9e\x97\x91\x16D\x9c\xa5\x83)l\xf4\x17\x97:\xee,\x8a\xa2Hq\xc3DM\xb7\xd9Y)\xd8\xb8\x87\x9b\xf4\xe0\x152\xf0\x0b\x08\x8a\xb0}\xc1\xeba\xb0\xb2 Ja\x10\xbb\xfaE)\x0e\xe2\x94\x07\xf1k\xdd\x97\x153fe\xbb\x835-~5\x8b3\xafQJ\x0e3f\x82F\xacZ_@\xc6\xb8\x15*P\xb0@\x91B\x82\xe0\x91\x92\xf8\x06\xb8Cc\xa7\xe96;\xab%wy\x19^_\xd3g4\xa5\xdb\xec\x1c\xd3\xc1s\xa9\x08J1\xf1\xea)x\xf7\x16\xc0;A\xc4\xef{*\xf2\x19@\x8a\xdc*\x87\x10\xb5\x83\xbf\xea\x03\x933\xbc\xd20|\x12.|\\i\xa7\x03\xed9\x14|\x07BP\nD\xc0\xc7\xf0Kk\x88M`\x88LU\x88LJ\x08O?\x18\x95h\xe0\xef6N\x95<\x10\x94&\x10\x9c\x10\x100j\x02&\xb0\xa7'\x13\xd1\xb8\x9f\x07\xe3\x0d\xc6GH\xe0\xe8~\x88\xeb\xe4\x0b\x9a\xffVo\xb3\xf3\x91\xed[q\xfa\x99B\x8d\xdbK\x15\xd8W\x13?\x9c\x8a\xcd\x13\x13V&}o\xcd\xb7W\xc8)\xa1;Z\xbc\xccq\xb1PWH\xe0\x05s1\xe7\xfd\xbb,\xbaO\xdb\xa5\xfe\x1e\xd7\xaf\xd9\\\xaf\xb9\x84\xef0\xdd\x92\x92bu\xd9F\xa5\xfe\x9fwbI*\x8d\x11\xf8\xcf\x06W\xbb\xb3>\x1fxw\xf9b(\xf9\x06\xd7k\xb2\xd8\xcb\xa6,[\xe7\xb1\x9e\xa8\xcfKhJ\xfcy\x8b3\xd6G\\U\xa4j\xe5\xe9\xf6\x90fk\xbc\x19\xd4\x7f7.m\xe6\xc5\x8c7p8\x03-s.#\x0b\xcd\x94\xb5\xbb96wU\x96\x01\x0c\x92a\x81k\x94\x17\x1a\xc3i\xf3\x10\x8c\x9e\x81\xc3#py\x02\xec\xf5\xab\xa62\xfa\x82\x1e\xf6\xcb=\xc5\x04=\x87\x8f\xef^\x9fU\x98\x92\xa6\xca0\x94h#\xe7}S\xe6?7\xb8\xd8\x013\x06u\xbe\xcce\xdc\xa3\x16\xa5\x19\x8d\x0c\xc5\x05\x12U\x8e\x8a\xfcoxa>\xd0\xbf\xadHM2R\xc0\xbcY.q\xa5>\xdaL\xdc\x8b!\xfa\x06\x9b\x86\xb6\xc6 \x90\xd9\xb9*0\xa2\xb5\xb9-Rb8:;\x82l\x8d*\x94\xd5\xb8b\xad`\xbe \x04\x8aW\x1b\\\xb6\x16\xf8\xe3\xbb\xd7\xc7\x14\xb6\xa8^\x1b\xb9q\xa1\xda\x82Q\xe6V\x19\xbbeS\x14;\xf8\xb9A\x05\xd3\xe0B\xe8W6\xc55y\x82(\xe4\xa5\x99\xc95\x13\xe5lE\xc8\xaa\xc03\xae\xb3y\xb3\x9c\xbdl*^1\xe1\xfa\xb1\xe8 gK\xd7\xa4)\x160g+\x88\x91\x1f\x82\x0c\x95\xa4\xcc3T\xf09dn\xf9\x04\xcfV\xb3S\xa6Z^\x0b\xe2hv\xc4\x8c\x19\xbf/%\xcb\xf0\xb6\xc6\x8b\xc7\xb3G\xe6\xd7/J\xd82e\xe7\x19>\x85\x1a\xa3\x0d\x85\x866\x88\xa9C\x94\xd5\xda\xe6\x05\x93\xb4&\\\x19\xf3\xbcD\x95\xd9\x07\xe7W\xb4\xec\xb6X\xde\x95R\xaf\xf1\xce\xdc\xb4\xb0u\xf2J\xa0\x86v\xeb\x81\xd6\xf83\xff\xd4\xcf\xcb\xdd\x0c~ \x9f\xf0-\xaeN\xad\xfe\xd5\xc7w\xaf\x95\xff\xc6X1\xb3m|\x96[P\x0c\xd7\xeb\xba\xde^\x9f\x8a\xff\xd2\xebS \x15\x94D\xfez\xcaGc\x86J |v2\x8d\x98\x19\xe2\x1a\x9a\xad,\x88ji\x17W\xb7\xb8\x12\xaa\xd9\xa0\xad\xbcS\x89K^\x93\xb6**\x8f^\xe6\xe2:\x17d\x8e7.IQ\x90O\xf4\xdc\xf2m\x7f\x0f\x17\xcb}\x8f\xd8\xb0\xd8V\x84y\x0d\x8b\xb6\xd3\xdc\xb7\xa1\xb4\xd9\xe0\x85\xa5\xfa\xea\xef\xd9\xe2\xf4\xc3\x87\x0f\x97\xf0\xfdw\x1f\xd4\x05:\x1f\xdf\xbd\x16sl\xc7\x97g\xb3\x0b\xf4\xd7\xe1\xb4\xf8\xb0\xdb\xe2\x9f\xfe\xfa\x93\xf1\x05\xee)7|<\x88\xf1&\x97\x11\xfe\x85\xb6\x15Y4\x19\x06T\x8a%\xcc\x9cg\xf7{x\xbe/ZB\xf9\x8dA\x88\xe9Lx\x10\x19\xca\x98m!\xe4\xa6\xd9\x82<& sD-Y\x90\xc4U\xe5\xe5\xe3\xbb\xd7\\\xc65\xba\xe5Cp\xd3\x99C\x0b1\x89\x90\xea\x12\xfb\xf7-\xc9\x17\x80J\x1b<$\x04\xe4\xe6\xa3\xc2KR\xe1S\xc5\x80\xf1Eu>\xcf\x8b\xbc\xdeA\x89\xf1\x82\n\xcf\x08\xb8\xc9\xabn\xad\xf9\x9c\xa4df\xb6\\a\xfe\x12\x9f\xb338\xf9H\xb1\xaa\xeb\xc4\xb4\xc4\x86'\xb3Yb|\xa2\x12\xadl\xbd\x9fW\x18\xdd0\x1b$\x19\xcf\x1e\x9bG\xd4\x1bR\xe3sq\xb1\xd8\xb2)31\xc3X?\xa4\xed\xca\x9a\xaa\xc2e]\xec:\xb1{\x8b\xb9\xe4\x17:-\x97y\x96\xa3\xc2\xb1\x96\xcd\x9b%T\x98\xadD\xf8\x94\x97\xba\xc9k\xd5hC\xf1B8}j^\x1aY\xcd\xf1*/K\xd6Y\xe6\xe2Z\x16\x97\xdd\x16\xcf\xc4\xf8G\xdb\x9c\xce2\xb2\xb1Y\xe3\xf7|\xa6R \xf5Z\x18\x8arh\xa5\xe0D8\xa5\x807\xdbz'\xa7\xf6c\xf3\"\x98\xaf\xd65\xcc-F\x89w\x9a\xfb\xe9\xf9f[`\xb6\xc8\xf2 \x03t\x8b\xb3|\x99g@\xf1\x06\x95u\x9e\x19RZ\xf9\\\x1d\xe1\x02yl\xdd|\xbd\xa4\x1f\x999\x9ac@b[\xd3qp\x0e\xfc\x18U\xd6hNn\xcdcZ\xaa@N\x05\xed\x0dq\x1e\x92]?/w\xd7\xfb\xbd\x1b*\x01U\xf3\xbc\xae\xd8$6K\xa8e\xa5\xd6\x08T\x109\xf4\x00\xe9?-\xb3\xce|\xa1\x11\x12\xce\xfbn\xe1\xc0\xfdk\xbd:\xc3\xd0\xbcT\x13\xa7\xc8\xe7\\l\xb9\x8eP\xa0\xcdvK*\xbe\x82oQvs\xd6\x94\xec?l\xdd\x16\xe3B?\x83\xe4Bovl\xc8\x12\x9aZ\x186e\x1e(3\xach\xb1\xc8\x85\xad\x80\x15.q\x85j.<\xdbh\xa9JUZ~L\x1e\xf1 \xf5\xed}\xf7\x19\xb1\xc1\x0fO\xce\xe1\x92\xc9\xcf\xec\x82\xec\n\xea\x96+\x7f\xf1\x87?X\x96\xc9W\x84\xc0\x92\x10x\x06\xb3\xd9\xec_\x8c\x8f1aP\xb93?\x80\xca\xdd\x8c\x89\xf1\xaa\"\x9b\x93%!\x8f\xcd\x8f\xcef\xe6\xf5/_\xc2 c\xf5\x91w\xe4\x039\xf9\x1d\xe3\xf5\x18~\xb1\xd8p\x1b\xbf_\xed\xba{\xea\xd0\xdd_\xd0-\x9aLy\xf0\x8c\xfb\x86\xac\x95 4\x94\xd3\x93W\x84\xcc\xb2\x02Q\xeaP\x90\x10\x91\xbd$\xfa\xd8y\xd1,\x83As\xad\xea\xbeq\xa8\xeerW\xafIiQ\x9e\x90\xea\x15!'\xb3\xd9\xcc\xbc\x1a\xb4\x8a;\xb1>\xc3\x07\x1fWk\xacV\x19\x93\x0b\xa1\xd4\x97\xdf\xbd\x7f\xf1\xee\xe2\xf2\xc3\xdbw\x8fm\xf1\xbe\xfd@\xb57,\x9a\xb6\xab\xf3\x8f\x0eu~O,\x05\xec\x98*\xcf\x9f\xc1\xef\xb6\xf3\xd9+B~\x99\xcdf\xbf\x9a\x1fF\xe5\xee\x94\xb9\xa1\xec\x8d\xadp\xa2~D\x15]\xa3\x82)\xd9\xde\x11\x9b\n\x87RXD\xc8\x97\x03\x01>\x96\x9b\xbd\x08\\@>A\xf8S\xff\xf4\x0c\xca\xbc\xb0\x0ep\xbb\\\x86\x91\xcc6\xb7\\\xcf\xca\x16\xab\x8d\x06\xccw{\xb7K\xad\x1e\xe2bP\xbd\xd7+\xa3d\xcc-\xd17u\xacq\xa9\xce\xd8\xfe}\xc6\x7f`\xee\xea1\xa0\xcej\xc7VBY\xd2P\xcbP\x8c\x10}c\xed\xd2R\x16;\xb5\xaf<\x08\x16\xb4n2\xa0e\x8du!CA<\x8eq|v\xacoJ\xae\x89Jd\xbe\xdb\x05,G\xf4\xd1\x92\x90\xd9\x1cU\xbc\xb3\x9f\xcfv\xb3\xbf\x1d -\xf2\xbd\x97\x96\x9fy+\xcaE=b<\xd8r\xa8}\xe4/\xef\xdf\xbe\xd1\xff\xf2\xec\xd9\xb3g\xe61\xc0\xde\xdb\xc7\\\x84\x1fI\x989\x90N\x90\xd8\xd75\x14\xabp\xeb\xaa)\x90\xa1\xdc\xf6!\x1b\xf6\xca\x02\xef\xdd\x96S\xc0\x9b9^,\xf6\x0e\x8c\x888k\xd9!C\xf4\xa6\xe3R\x888\xf3\xf5\xbf3\xd5]\xcb`B/\xe4\xae>\x8e~\x82H\xf3sn\xd9\x80\xa0\xec\x86\xd9\xa0\xfd\x86x\x99\x17\xd8\xbcn(\x9bu\x89+JJ\xeb\xb4\x95\x918~\x9f\xed\x15\xff\xc2\xcf\xe0\x89\x99s\xfb\x02\xcf\xb0\x90\xcf?\x0d_\xc1\x00\xacR\x1dq]\x1e\x9d\xc3\x91n\xd6\xf6\xd50\x13\xbd<\xb2\xdd$}\xc4\xfb\xf7\x06m\x18\xcf\x7f\x15]\xf87\xeb\x0b\xac\x7f\x83\xe7C;y\xb1\x94\x1b\xae\xfeX\x13\xa3!\xa7\xf0 \x17\xc5W7%\xf9Tr;\xb3\xe6\x97mg\x0d\xad\xc9&pr\xf5\x87\xfc\xa9p\xe0\x07\xf3@\xdd\xaa\xdc\x8a\xc3\x06\xb0as\x85\xc4\x90\xd67v\xcd'\xa3\x1a\xe7kR,d\x89\\.\xb9\x98\xcay\xd9\xce\x0f\x10\x11@=+1e\xf4\xedp\x11f\xed\xe2|\xc2\xec\x9aR\xe1AhHEL\x7f\xfa\xebO\x8f-\x13i\x8a1\xd7o\xd0>\xec\xb8\xaa\x18\xcb'\xb3\xa7O\x9e\xd2#\xcb\x10\x12\xff\xad\xd1\xaa\x03\x1a|\x05\xefqu\x9bgL{\xc7g\x19\xa1\x1bB\xcf\xe6\x88\xe2\xb3=nvv\xfbd\x8ek\xf4\xe4\x8c\xe3x\xf4\xec\x17\x91\x97\xf4\xeb\xb1`\xb3\xda\x1f\x0f\xa5\xcdf\x83\xaa\xdd9|\x8f\x05\xfa\xf4\xedN\xdc\xa4\x0d?7\xb8\xca1\x95P S\xf4*\xbf\xc5\xa5LqRV\x8bl\xb1\xe8\xf3\xc5\xe2\x90\x87|F\x01O\x9dN\x1c?\xfd\xfa\xebc3z\x05\xb4\xc92L\xe9\xb2)\xee\x12\xb62\xe76\x19\x99\x81\x13\xca1'\xd48\xe3\x17\x8e\xe8\x85W\xae\x92UrpJ\x0f\xee\x9c\xa4\xda+\x17\xc9'\x07\xc9\x9e{\xe4T\x17\xb8U\xe6\x99cd\xcd-2\xd4i\x97\xef\x1c\xbfd&5C5^\x9c\xc3\x96\x87\x02\xb9swM\x177\xa2*\xf15\xe4%\xad1Z\x1cO:\xcc\xeev\x0c8\x0b\xf2\x87\x96\xdew\xca\x04^r\x81_\xe1|\xaf\xb1\x03\x9d\xf1\xe3\xac+\xef,{?u\x93>\xd1@A~\xe5\xe9\xa1_n\xde\xd6\xd5^!z\xdf\xc2\xf2\xbe%\xe4\xfb%\xe1-\x0cC\x8b\xc5\x87\x96\x85w\x17\x80\xf7\xf8\xa4_,\xe1\xd7^\x92=\xa8\x19Wf\x95gA\xf5 g\xb4; \xd5\xab\x83\xe0\xb3 \x08\xf2ZK\x15y\xf5\x14\xbc{\x0b\xee5\xb6}\xcck\xadU\xe43\x8e\x14\xb9U\x0e!j\x07\x7f\xd5{\xae\xc9\x83\x87\xedy\xbf\xfe\xa5\xbd\xbdz\xe4\xd5\x93\xf0\xc2\xdc\x1e%\xb8\xa7\x12\xcf\xbb\x82\xf3T\x0d\x0e\xf5\xe1\xacem\xad\xcc\x1aV\x85z\xaa>\xf8\xd6\x90\x9e\xaa=w\x05\xe8\xa9Z\n\xa8\xdfEL\x9c\x90\xed\xa9\xa0\xa0\xeb\x00\xc2\xf6\xca\x82lGJ\xf4\xe4\x8e\x8b\xf4)\xd0\xd6A\x84\xbd\x03\xef\xed\xfc\x9e\">\x1f\xf8\xbadC\n\x8a\xb5\xf4)B{\x10\xa9AF\x9eQ\x99>\xc5\x8c;E\xe1SBQ\xe8\x07\x87\xf8\x8f\x0e\xb1\x1f>0\xd2\xd3'\xaf\xb8O\x9f\xbc\x8f\xa3*\x8aP\x88o\x10\xb5O\xc1\xa7u\x15\x8d\x90\xd0\xfbS\xed\x85\xcb\xcb\x05\xfe\x1c&Z\xd8\xb8\x0f\xb7\xb7\xde\x07|\x15\xdd\xbd\xc6b\\\x10~\xca\xb7\xcd\xbe\xe0g\x80+\xcc<\xe6Sy\xacc\x93\xf3#)^\xcc\xc4\xe3\xdc\xe1\x161\xb5N\xf5\x94%1\xa4@\x0d\xa9\xe3\xb7\xb8\x1e\xe5\x9e\xfd\xdc\xe7\x03\x04\xda\xebP;\x9d<\xfb\x01%\xcf>y\xf6nJ\x9e\xbd\xebiH\x9e}\xd8\x1a((y\xf6F\n\x9f\x12\x8aB?8\xc4\x7ft\x88\xfd\xf0\xc9\xb3W\x94<{A\xe1\xf66y\xf6\x87\xf4\xa5=\xfb\x07X\x1ds?f\x1f\x86_*\x80\x88\x0d\xd2\xb1\x95:\xdb\x0f#\xddj\xa8?\x11\xc6kY\xe4\x19\xaf\xd6\xc9F\xadc\xcc\xf1\x1a\x9eW\xa2F\xe7\x15\xe2\x05D\xef\x1b\xb6\xea\xf4\xe0\xca#\xb3TP\x80,\x10(\x0fH\xd3\x88\x17\x81^U\xa0L\x10!\x178\x12\x8f\xf5\x14!\x18D\n\x07>\xa9\xcbz\nMh\xd6Sd_aD\x7f\xc1/%ZO\x81\x06kH\xca\x809\xd3\xa7\xf5\xe4L\xaa\xd6\xd3}\n\x1dj\x82\x87\xe4\x97\xac\x1d\xcc\xb6\x9f\xdc=H\xe1\x0e\xe6\xe6\x99\xf2\xad'\xdfD\xf0`\xc6\xbd\xc4\xf1\xe0\xf4p=\x85&\x8d\xeb\xc9\x9dJ\xae\xa7\xe8\x81\x1c\x16\xd1Q\x14\xdd\\\xa8\xcf\xd4%{\xe2\xba\x9e&\x10\xd4\xd7\xa5\xea\x93g\xea\xbb\x9e\xee\xc9\xf6\xc7\xc4\x0b`\x9c\x92!|k\xd5\xa7\x11\x81#E#\xb4\x0d#5\x0e\xb1\x01%E1\x1b\xec!\xc5\xcfHE\xb1\x03\x07\xc6\x0f\x1e\x18;\x80F\x05\xa0\x14E\x04\xa2\x14\xf9\x1f8\xd0\xd3\x08\xfd\x8d\xd0[\xf8\x91\x05=y\x1cd\xd0\xd3}t\xdb\xfb\x18\x81\x9e\xeeCdw\xd6\xad\x99|\xcf_\x043\xb6\x9d\xd7\xd0S\xd8)\x0e=\xdd\x87\xfa}\xcf\x81\xe8\xe9>$v\x9f$\xd1\xd3}\xc8\x1ap\x16EO\xf7!\xb4\xe7i\x16=\xdd\x87\xc0a\xe7a\xf4\xe4\x7fJFO_\xbe\xdfcv\xe7\x9e\xc7r\x82x\x9a\x8f\xf0\xe8\xc9v=\x91\x9e\"\xdd\xe0X\xf7\xf7\xefh\xe3\x19\x94b\xa0h\x8cg\x1e\x0e\x81)J;N\x1f\x8a\xb4 \x82\xd2\x8e3z\\+\x8a\x9f\x8a\x8ab\x07\x0e\x8c\x1f<0v\x00\xdd\xf7\x8e\xd3\xe7j+= \xcd\xf9\xde\x1d\xdb%\xe7\x816=\x8d\x9a(\xe3\xa6I\xd0EZz\x1a=\xd0\xc2\xd2H\xfb\x14{\x15\x97\x9e\"/\xe8\xd2S\xe4\xb5]z\n\xbf\xccKO\xa3\xae\xf8\xd2S\xfc\xd6[\xd0T\xd7\x81\xe9)\xe8\x920=E\xa7,\xf5i\xf4<\x19m\x8e\x03\x123\x0ei2\xf1\xe3\x90\x06A\xc19P}\x9a\xac\x0fQ\x9f`\xccfK\x90\xef\xb5h\x11\xac\xf3\x12\x90\xf5\xb24=\x8d\xe9\x93\xc7\xc5jA\xfc\xf4\x97\xb0\xb5\xd7\xad\x05\xf1\xb2]\xcd\xa6\xa3\xbd\x8d\xa0\xd8{\xbb\x17\xb1\xee\xc7\xac\xf6a7\xe7+\x8a\xf3\x82\"<\xa0\x08-\x08\x8a\xd1\x85\xa0h;>\xca\x80\x8c0\x1e\xc17\xef+\x8aV.\x8cR0\xc4\xde\xc8\xafh\x94\xa2a\x9c\xb2!\xfe\xa6~E\xf7+\xfe\x18\x8f\xec\x0en\xf2W\x14\x7f\xa3\xbf\xa2\xd0\xc4\xdb>\x8d\xfa(c6\xf3\xdb\xf8\x1b\xff\x15\xdd\x83\xecJ\xea\x10a#\xcdM\xac\xa1\x89\xb4\xe3#\x94\x19=/#-x\xa4Ba\x84Ra\x9c\xed\x1e\xa1\\\x18\xa3`\x18k\xb5\xefO\xf0x{}g\xd6z\xac\xad\x1ec\xa9G|\x888K\x07S\xd8\xe8/.u\xdcY\x14E\x91\xe2\x86\x89\xca\xf6a\xa4\xbc\xf2\x07\xc1\x02\xa5\n\x93f\xbe\xfb\x1b*\xeb\xbc\xc4Wa;\xa2\xb0\x9dP\xc0\x0e(\xd8\xb8\x87\x9b\xf4\xe0\x152\xf0\x0b\x08\x8a\xb0}\xc1\xeba\xb0\xb2 Ja\x10\xbb\xfaE)\x0e\xe2\x94\x07\xf1k\xdd\x97\x153fe\xbb\x835-~5\x8b3\xafQJ\x0e3f\x82F\xacZ_@\xc6\xb8\x15*P\xb0@\x91B\x82\xe0\x91\x92\xf8\x06\xb8Cc\xa7\xaf\xd9*\xfa\x82\x9f\x05|\xce\x8f\x02\x8e=\xa1(\x0b\x8fv\xce\xcc\xa2\xba\xc6\x9b-?\x9dX\x13\xd8\xe4\xb4\xc0h\x01H\x9cCt\xf2\x13\xe7\x14u\x01\xd3N\xfa\xaf^\xf1N\xeb\xee\xb6\xe4./\xc3\xebk\xfa\x8c&G\xa2\x8ch\xc7\x95:\xe0\x93\xfc\xe2Nsq\xaa\x0d\xbcT\x07^\xb9\x06^\n\x04\xff\xa5\"(\xc5\xc4\xab\xa7\xe0\xdd[\x00\xef\x04\x11\xbf\xef\xa9\xc8g\x00)r\xab\x1cB\xd4\x0e\xfe\xaa\x0fL\xce\xf0J\xc3\xf0I\xb8\xf0q\xa5\x9d\x0e\xb4\xe7P\xf0\x1d\x08A)\x10\x01\x1f\xc3/\xad!6\x81!2U!2)!<\xfd`T\xa2\x81\xbf\xdb8U\xf2@P\x9a@pB@\xc0\xa8 \x98\xc0\x9e\x9eLD\xe3~\x1e\x8c7\x18\x1f!\x81\xa3\xfb!\xae\x93/h\xce!p\x0fNz@\xd8O\"\x0f\xc8\xdb\x00c\x1b\x18\xa2C'n(_{\xb1\xda\xe1gRw\xb2\xbd\xcf\xcb\x8c\xa7\xbe\xd3\x0d\xa1_\xd1\xc5\x0d|=\xfb\xe3?\xa7\xfb\xd7\\\x8b\x8c[.\xf0+6\xe0=C\xd4\xfcp\x9e\xc5w\x96\n\x98\xbaI\xbf \x00\xdeG\xfa!\xdd\xbf\xd6'\xf7\xa1y\x8fO\xfa\xc56I\xe9\xfe5w\x07\xc1\x7f\xc1K{%\xa7\xca!D\xed\xe0\xaf\xfa\xbb\xd8+\xf9\x1f\x87\xf6\xea\x91WO\xc2\x0f3{\x1c[\x9eJ<\xefS\xafS58\xd4\x87\xf3\xfco\xba\x7f\xcd\xe3\xd4\xecT-\x05\x9cy\x9d\xaaI\xcf\x13\xabS5\x17v\xde\xd4\xffd\xa9\x97|\xfe\xbe\xda\xb0a\xb6\x87\x12\xb7n\xe5\xab\xbcD\xd2qj\x1f\xb3p\x92\x0cN\xa5r\xd8\xae\x86_\xd6\xff-\xce\xd6\xdf<\x95\x12[+G_\x94\xf0\x01\xab+\xdbO\xa1^3Yd\x0d\xebk\xa6jz}\n\xf3\xa6V\x8e\xe0\xfb\x97\xffa\xbbX\xfe\x13w9oqUC^CM\x86\xb2\xd8Da\xbe\xe9\x1c\xd75\xae\xe0\xe3\xff\xf5\xd8\x10\xba\xcf\xca\xee{\x96n\xb3\xe3\xe45\x87\xfc\x07r\xba\xcd\xce\xe6\xe3\xa4\xdb\xec\xd2mv\x87\xe4+ \xa4\xdb\xec\x0c\x94\xee\xbc\x90\x94\xee\xbc\xe8P\xba\xf3\xc2\xeb\x9d\xa0\x82\x14a\x91\x07A>\x18|\x9f\xdcQ\xa6>\x05\xda:\x88\xb0w\xe0\x1d\x1c\xd9S\xc4\xe7\x03_\x97lHA\x91\xab>Eh\x0f\"5\xc8\xc83\xc6\xd5\xa7\x98q\xa7(|J(\n\xfd\xe0\x10\xff\xd1!\xf6\xc3\x07\xc6\xcd\xfa\xe4\x15E\xeb\x937 \xaa(B!\xbe!\xe9>\x05\xe3\xc5\x8aFH\xe8\xfd\xa9\xd2\x9d\x17a\x1a\x8bqA\xfe\xae\xef\xbcH\xb7\xd9\xe9(y\xf6\x92\x92g\xdf\xa1\xe4\xd9{\xbd\x93<{\xd7\xb3\x8aB\x1d\xbd\x88\xcf\x07\xe1k\xa0\xa0\xe4\xd9\x1b)|J(\n\xfd\xe0\x10\xff\xd1!\xf6\xc3'\xcf^Q\xf2\xec\x05\x85\xdb\xdb\xe4\xd9\x1f\xd2\x97\xf6\xec\x1f\xe0\xf9\xact\x9b\x9d\x99B\xc7g\xba\xcd\xce\xc54\xddf\x17\"\x17\xa4\xdb\xec\x1c\x14\xd9W\x18\xd1_\xf0K0\xd7S\xa0\xc1\x1a\x922`\xcedt=9S\xd4\xf5t\x9fB\x87\x9a\xe0!\xf9\xa5\xbe\x07\xb3M\xb7\xd9\xb9\x92\xed\xf5\x14\x9a\x82\xaf'wb\xbe\x9e\xa2\x07rXDGQts\xa1>S\x97\xd2mv\x1e4\xc6\xf6\xc7\xc4\x0b`\x9c\x92!|k\xd5\xa7\x11\x81#E#\xb4\x0d#5\x0e\xb1\x01%E1\x1b\xec!\xc5\xcfHE\xb1\x03\x07\xc6\x0f\x1e\x18;\x80F\x05\xa0\x14E\x04\xa2\x14\xf9\x1f\xdf\xd0\xd3\x08\xfd\x8d\xd0[\xf8\x01\x10=y\x1c\x0b\xd1\xd3}t\xdb\xfbP\x86\x9e\xeeCdw\xd6\xad\x99|O\xb3\x043\x0e\xbf\xcb)\xecL\x8c\x9e\xeeC\xfd\xbe\xa7j\xf4t\x1f\x12\xbb\xcf\xe5\xe8\xe9>d\x0d8\xd9\xa3\xa7\xfb\x10\xda\xf3l\x90\x9e\xeeC\xe0\xb0\xd3Ez\xf2?s\xa4\xa7/\xdf\xef1\xbbs\xf7 \x9dp\x0b\x98n\xb3\xe34\xc1@\x08ws\x83R\x0c\x14\x8d\xf1\xcc\xc3!0Ei\xc7\xe9C\x916AP\xdaqF\x8fkE\xf1SQQ\xec\xc0\x81\xf1\x83\x07\xc6\x0e\xa0\xfb\xdeq\xfa\x14W\xd3\x93\xd0\x9co\xf5\xe2.9\x0f\xb4\xe9i\xd4D\x197M\x82J\xb9\xe9i\xf4@\x0bK#\xedSl18=E\x96\x88\xd3Sd\xe18=\x85\x97\x93\xd3\xd3\xa8\"sz\x8a\xdfz\x0b\x9a\xaa \x9d\x9e\x82\xca\xd4\xe9):e\xa9O\xa3\xe7\xc9hs\x1c\x90\x98qH\x93\x89\x1f\x874\x08\n\xce\x81\xea\xd3d}\x88\xfa\x04c6[\x82|\x0b\xf3E\xb0N\xb7\xd9\xf5(\xddf\x17\xe7\x05Ex@\x11Z\x10\x14\xa3\x0bA\xd1v|\x94\x01\x19a<\x82\xef~P\x14\xad\\\x18\xa5`\x88\xbd\x13B\xd1(E\xc38eC\xfc]\x11\x8a\xeeW\xfc1\x1e\xd9\x1d\xdc%\xa1(\xfeN E\xa1\x89\xb7}\x1a\xf5Q\xc6l\xe6\xb7\xf1wN(\xba\x07\xd9\xb7\xe96\xbbC\x8a\x9e\x97\x91\x16D\x9c\xa5\x83)l\xf4\x17\x97:\xee,\x8a\xa2Hq\xc3DM\xb7\xd9Y)\xd8\xb8\x87\x9b\xf4\xe0\x152\xf0\x0b\x08\x8a\xb0}\xc1\xeba\xb0\xb2 Ja\x10\xbb\xfaE)\x0e\xe2\x94\x07\xf1k\xdd\x97\x153fe\xbb\x835-~5\x8b3\xafQJ\x0e3f\x82F\xacZ_@\xc6\xb8\x15*P\xb0@\x91B\x82\xe0\x91\x92\xf8\x06\xb8Cc\xa7\xe96;\xab%wy\x19^_\xd3g4\xa5\xdb\xec\x1c\xd3\xc1s\xa9\x08J1\xf1\xea)x\xf7\x16\xc0;A\xc4\xef{*\xf2\x19@\x8a\xdc*\x87\x10\xb5\x83\xbf\xea\x03\x933\xbc\xd20|\x12.|\\i\xa7\x03\xed9\x14|\x07BP\nD\xc0\xc7\xf0Kk\x88M`\x88LU\x88LJ\x08O?\x18\x95h\xe0\xef6N\x95<\x10\x94&\x10\x9c\x10\x100j\x02&\xb0\xa7'\x13\xd1\xb8\x9f\x07\xe3\x0d\xc6GH\xe0\xe8~\x88\xeb\xe4\x0b\x9a\xffVo\xb3\xf3\x91\xed[q\xfa\x99B\x8d\xdbK\x15\xd8W\x13?\x9c\x8a\xcd\x13\x13V&}o\xcd\xb7W\xc8)\xa1;Z\xbc\xccq\xb1PWH\xe0\x05s1\xe7\xfd\xbb,\xbaO\xdb\xa5\xfe\x1e\xd7\\\xb6ow\xe2\xf6\x81w\x98nII\xb1\xban\xa3R\xff\xcf\xbb\xb1$\x95\xc6\x0c\xfcg\x83\xab\xdd\xd9\x90\xd3P\xf2w\x97/`\x83\xeb5Y\xec\xe5S\xd6\xad\xf3hO\xdc\xe7%4%\xfe\xbc\xc5\x19\xeb'\xae*R\xb5\x12u{I\xb35\xde\x0cj\xc0\x1b\x977\xf3\x82\xc6\x1b8\x9c\x85\x96y\x97\x91\x85f\xda\xda]\x1d\x9b\xcb*K\x01\x06\xc9\xb0\xc05\xca\x0b\x8d\xf1\xb4y F\xef\xc0\xe1\x15\xb8\xbc\x01\xf6\xfaUS\x19\xfdA\x0f\x1b\xe6\x9ef\x82\x9e\xc3\xc7w\xaf\xcf*LISe\x18J\xb4\x91s\xbf)\xf3\x9f\x1b\\\xec\x80\x19\x84:_\xe62\xf6Q\x8b\xf2\x8cF\x86\xe2\x12\x89*GE\xfe7\xbc0\x1f\xea\xdfV\xa4&\x19)`\xde,\x97\xb8R\x1fm&\xee\xc6\x10}\x83MC[\x03\x05\xc8\xec`\x15\x18\xd1\x83\xb9\xb2'Rb8:;\x82l\x8d*\x94\xd5\xb8b\xad`\xbe\x11\x04\x8aW\x1b\\\xb6V\xf8\xe3\xbb\xd7\xc7\x14\xb6\xa8^\x1b\xb9q\xa1\xda\xa2Q\xe6V\x19\xbbeS\x14;\xf8\xb9A\x05\xd3\xe0B\xe8W6\xc55y\x82(\xe4\xa5\x99\xc95\x13\xe5lE\xc8\xaa\xc03\xae\xb3y\xb3\x9c\xbdl*^5\xe1\xfa\xb1\xe8 gK\xd7\xa4)\x160g\xab\x88\x91\x1f\x82\x0c\x95\xa4\xcc3T\xf09dn\xf9\x04\xcfV\xb3S\xa6Z^\x0f\xe2hv\xc4\xcc\x19\xbf3%\xcb\xf0\xb6\xc6\x8b\xc7\xb3G\xe6\xd7/J\xd82e\xe7\x19>\x85\x1a\xa3\x0d\x85\x866\x88\xa9C\x94\xd6\xda\xe6\x05\x93\xb4&\\\x19\xf3\xbcD\x95\xd9\x0f\xe7\xd7\xb4\xec\xb6X\xde\x97R\xaf\xf1\xce\xdc\xb4\xb0u\xf2Z\xa0\x86vk\x82\xd6\xf83\xff\xd4\xcf\xcb\xdd\x0c~ \x9f\xf0-\xaeN\xad>\xd6\xc7w\xaf\x95\x0f\xc7X1\xc3m|\x96[P\x0c\xd7\xeb\xba\xde^\x9f\x8a\xff\xd2\xebS \x15\x94D\xfez\xcaGc\x86J |v2\x8d\x98\x19\xe2\x1a\x9a\xad,\x8aji\x17W\xb7\xb8\x12\xaa\xd9\xa0\xad\xbcW\x89K^\x93\xb62*\x8f`\xe6\xe2J\x17d\x8e9.IQ\x90O\xf4\xdc\xf2m\x7f\x0f\x17\xcb}\x8f\xd8\xb0\xd8V\x84y\x0e\x8b\xb6\xd3\xdc\xbf\xa1\xb4\xd9\xe0\x85\xa5\x02\xeb\xef\xd9\xe2\xf4\xc3\x87\x0f\x97\xf0\xfdw\x1f\xd4%:\x1f\xdf\xbd\x16sl\xc7\x97h\xb3\x1b\xf4\xd7\xe1\xb4\xf8\xb0\xdb\xe2\x9f\xfe\xfa\x93\xf1\x05\xee-7|<\x88\xf1&\x97\x11\xfe\x85\xb6\x15Y4\x19\x06T\x8a%\xcc\x9ck\xf7{x\xbe/\\B\xf9\xadA\x88\xe9Lx\x11\x19\xca\x98m!\xe4\xa6\xd9\x82<* sD-\x99\x90\xc4U\xe9\xe5\xe3\xbb\xd7\\\xc65\xba\xe5Cp\xd3\x99C\x0b1\x89\x90\xea\x12\xfb\xf7-\xc9\x17\x80J\x1bD$\x04\xe4\xe6\xa3\xc2KR\xe1S\xc5\x80\xf1Eu>\xcf\x8b\xbc\xdeA\x89\xf1\x82\n\xef\x08\xb8\xc9\xabn\xad9\x9d\xa4df\xb6\\a\xfe\x12\x9f\xb338\xf9H\xb1\xaa\xed\xc4\xb4\xc4\x86'\xb3Yb|\xa2\x12\xadl\xbd\x9fW\x18\xdd0\x1b$\x19\xcf\x1e\x9bG\xd4\x1bR\xe3sq\xb9\xd8\xb2)31\xc3X?\xa4\xed\xca\x9a\xaa\xc2e]\xec:\xf1{\x8b\xb9\xe4\x97:-\x97y\x96\xa3\xc2\xb1\x96\xcd\x9b%T\x98\xadD\xf8\x94\x97\xbb\xc9k\xd5hC\xf1B\xb8}j^\x1aY\xcd\xf1*/K\xd6Y\xe6\xe6Z\x16\x97\xdd\x16\xcf\xc4\xf8G\xdb\x9c\xce2\xb2\xb1Y\xe3\xf7|\xa6R \xf5Z\x18\x8arh\xa5\xe0D\xb8\xa5\x807\xdbz'\xa7\xf6c\xf3\"\xc8\xbcS\x98[\x8c\x12\xef4\xf7\xd5\xf3\xcd\xb6\xc0l\x91\xe5\x13\x06\xe8\x16g\xf92\xcf\x80\xe2\x0d*\xeb<3\xa4\xb5\xf2\xb9:\xc2\x05\xf2\xd8\xbe\xf9zI?2s4\xc7\x80\xc4\xd6\xa6\xe3\xe0\x1c\xf81\xaa\xb4\xd1\x9c\xdc\x9a\xc7\xb4T\x81\x9c\n\xda[\xe2<$\xbb~^\xee\xae\xf7\xfb7T\x02\xaa\xe6y]\xb1Il\x96P\xcbJ\xad\x11\xa8 r\xe8\x01\xd2\x7fZf\x9d\xf9B#$\x9c\xf7\xdd\xc2\x81\xfb\xd7zu\x86\xa1y\xa9&N\x91\xcf\xb9\xd8r\x1d\xa1@\x9b\xed\x96T|\x05\xdf\xa2\xec\xe6\xac)\xd9\x7f\xd8\xba-\xc6\x85~\x06\xc9\x85\xde\xec\xd8\x90%4\xb50l\xca\xae\xd6X\xad2&\x17B\xa9/\xbf{\xff\xe2\xdd\xc5\xe5\x87\xb7\xef\x1e\xdbb~\xfb\x81joX4mW\xe7\x1f\x1d\xea\xfc\x9eX\x8a\xd81U\x9e?\x83\xdfm\xe7\xb3W\x84\xfc2\x9b\xcd~5?\x8c\xca\xdd)sC\xd9\x1b[\xe1D\xfd\x88*\xbaF\x05S\xb2\xbd#6\x15\x0e\xa5\xb0\x88\x90/\x07\x02|,7{\x11\xb8\x80|\x82\xf0\xa7\xfe\xe9\x19\x94ya\x1d\xe0v\xb9\x0c#\x99mn\xb9\x9e\x95-V\x1b\x0d\x98\xef\xf6n\x97Z=\xc4\xe5\xa0z\xafWF\xc9\x98[\xa2o\xeaX\xe3R\x9d\xb1\xfd\xfb\x8c\xff\xc0\xdc\xd5c@\x9d\xd5\x8e\xad\x84\xb2\xac\xa1\x96\xa1\x18!\xfa\xc6\xda\xa5\xa5,vj_y\x10,h\xddd@\xcb\x1a\xeb\x82\x86\x82x\x1c\xe3\xf8\xecX\xdf\x94\\\x13\x95\xc8|\xb7\x0bX\x8e\xe8\xa3%!\xb39\xaaxg?\x9f\xedf\x7f;\x12Z\xe4{/-?\xf3V\x94\x8bz\xc4x\xb0\xe5P\xfb\xc8_\xde\xbf}\xa3\xff\xe5\xd9\xb3g\xcf\xccc\x80\xbd\xb7\x8f\xb9\x08?\x920s \x9d \xb1\xafk(V\x01\xd7US C\xc9\xedC6\xec\x95\x05\xde\xbb-\xa7\x807s\xbcX\xec\x1d\x18\x11u\xd6\xb2C\x86\xe8M\xc7\xa5\x10\xb1\xe6\xeb\x7fg\xaa\xbb\x96\xc1\x84^\xd8]}\x1c\xfd\x04\x91\xe6\xe7\xdc\xb2\x01A\xd9\x0d\xb3A\xfb\x0d\xf12/\xb0y\xddP6\xeb\x12W\x94\x94\xd6i+#q\xfcN\xdb+\xfe\x85\x9f\xc1\x133\xe7\xf6\x05\x9ee!\x9f\x7f\x1a\xbe\x82\x01X\xa5:\xe2\xba<:\x87#\xdd\xac\xed\xaba&zyd\xbbM\xfa\x88\xf7\xef\x0d\xda0\x9e\xff*\xba\xf0o\xd6\x17X\xff\x06\xcf\x87v\xf2b)7\\\xfd\xb1&FCN\xe1\x13.\x8a\xafnJ\xf2\xa9\xe4vf\xcd/\xdc\xce\x1aZ\x93M\xe0\xe4\xea\x0f\xf9S\xe1\xc0\x0f\xe6\x81\xbaY\xb9\x15\x87\x0d`\xc3\xe6\n\x89!\xado\xec\x9aOF5\xce\xd7\xa4X\xc82\xb9\\r1\x95\xf3\xb2\x9d\x1f \"\x80zVb\xca\xe8\xdb\xe1\"\xcc\xda\xc5\xf9\x84\xd95\xa5\xc2\x83\xd0\x90\x8a\x98\xfe\xf4\xd7\x9f\x1e[&\xd2\x14c\xae\xdf\xa0}\xd8qU1\x96OfO\x9f<\xa5G\x96!$\xfe\xbbE\x15\xda\xe0\x1aw\xb3\x85\xbf\xe2\x96\xf7\\&\x1duX\xe4\xe5\xf90\x94]\xe1\x9f\x9b\xbc\xc2\x8bs\xa8\xab\xa6\xabt\xc3\x86Z\x97BR\xa3U\xaf\xf5\xf7\xb8\xba\xcd3\xc6\xec,#tC\xe8\xd9\x1cQ|\xb6\x87\xef\xcen\x9f\xccq\x8d\x9e\x9c\x95d\x81\xaf\xf2rI\xc4\xeb\xab\xfd\xf1T\xdal6\xa8\xda\x9d\xc3\xf7\xb8~C\x16\xf8\xa2\\\x12\xf8\xb9\xc1\x95\xc2\x1dd\xe0\x06\x18\x0b^\x0dK\xd9J\xb2\xc5B\xd3\x17\x8b\xde\xdb\x8fT\x87\x05\xd2\xd5\x91\xf8\xf8\xe9\xd7_\x1f\x9b\xe12\xa0M\x96aJ\x97Mq\x978\x99tH\xae\x06:\xe9\x92\x91+8A$\xb5\xe9\xbf\xb2VT\xb76\x00\xceF\xf8\x13O-\xa0\xbfG\x94f?\xc4\xac%\xbd\x1dE\xd2\xa7k\xc8Z\xd6|\x9af\xfa_\xde\x90Q\xe7h\xaa\xc8i\x8dK\x9e\xf3\x11\xf5~\x89\xebO\xa42h\xd4\xf1\xae\xc7\x902\xbe\x9b\xadQYb\x1d\xd0\xea\xf1\xb23\xa6\xb7!e~c\xca\xdes0\xe7\xa1\xd1;\x9b&\xf5g\xd75>\xce\xbe\x03T\xdb\xcc\x9d\xe5c\xe1\xd3\xa9\xb7n\xb6\x0b#\x8c\x0e_\x86bt\x8f\xb6\xdb\xab\xe8\x97\xc7\x0c\xc7U\xee\x93\nl|}\xde\xe4\xc5\xe2\xaa\xbf\x1e\x06\xbc\xbe\">\xe6\xd9\xd1\xfa\x02o\xad\xad\x9b\x13\x1c\xad\xc9\x8d\xce1\xef\x1a\x0c\xf2\x19T[\xd3J=F=\x7fL$\xe1m\xc8\xa2)\xb0\x1d\x9e\xf7\xb8B$\xaaQ\xc9\xd7\xf8<\xb5gX\x86\xb5\x99\xadqvC\x1b\xbd\x9f\xdf>\xf5\xa3\x90,\xefl\"\x99\x13\xfc_B\xd2\x0b}\xc9P\xe1\xa2]\xd1\xc5\xcd\xa8\xc1\xd7\nq\xfc>/3^\xab\x94\xf1\xfd\x8a.n\xe0\xeb\xd9\x1f\xbf9>x\xa7\xe7eud<\x90\x9f\xfdO\xc7\x91k\xb3\xa9Z0\xc1\xc8U\x97\x99u\xc0\xc6\x96\x94\xb5O\xc3\x1a\xb8\x91\x8a\xde]\xbe\x18\xee\x1eR>V\xca\xc7r\xcem\x1f<\x0fR>V\xca\xc72>\x99\xf2\xb18\xa5|\xacCJ\xf9X)\x1f\xcbD)\x1f+\xe5cqJ\xf9X)\x1f+\xe5c\xa5|,A)\x1f+\xe5c\xa5|\xac\x94\x8fe\xa2\x94\x8f\x95\xf2\xb1R>V\xca\xc7\xea\xd0\x14\xb91)\x1f\x8bS\xca\xc7\xfa\xad\xe4c\xc5\xe7B\xd1]\x99\xe5\xa5,\xf2b\xc8\x84z/\x9ei\x13\xa1x\xf2\x93|\xd1\x94\xff$\xdf\x91\xbf>\xd8\xf4\xa7^\xf7\xbb$x\xcd )0\xea\x07\x82\x9cP\x9f\xecz \xd2\xd7W\x98\xa2TiARB\xf6\x12\xb2\xb7\xa7\x84\xec%doO \xd9\xab\x13\xb2\xa7\xa7\x84\xec)J\xc8^B\xf6\x12\xb2\xe7\xe9%%d\xaf\xa5\x84\xecu)!{ \xd9\xd3PB\xf6\xb4\xcf$d/!{\x06J\xc8^B\xf6\x12\xb2\x97\x90\xbd\x0eM\x81\xb2$d\x8fSB\xf6\x12\xb2\xb7/\x7f\x8ekzV\xa0\x1a\xd3\xda\n\xf3\xbd\xe6\x8f\xb4\xf7A\xbd\xc7u\x8b\xf8\x89\xb7\xf7\x15\xd5\xbf\xa2\xb86!\x7f\x87l\xe4\x83\x0f\x16\x04\x14\xd7]\x98.\xcb\xb1F\x1elw\x9b\xd8\xae\xd4\xab\xef\x01er\x9c\xd9vFX\x1c\xf7\xd39\xa4\x03\x0f \xc1\x03\x0b\x03\x1fQ\x05\xf9\xc4T\x14\x85\xe2bVf\xfa\xe0\x88e\xaf05>\x06n\x8c\x0c\"p2{\x07P\xbd\xf6\xc6\xca`*\xbc\x0c\"13+C\xa6\\o\xdc\x0c\xc6cg\x10\x8c\x9fYY\xc9\xb8~\x10\x86\x06S\xe3h\x10\x88\xa5A(\x9ef\x1f\xd9-\xd6\xe6\x8b\xa9\xc1\xd4\xb8\x1a\xf8ak0%\xbe\x06\xa316\x88\xc3\xd9`*\xac\x0d\xa2\xf06\xfbt@\x14/\xdc\x98\x1b\xdc\x0d\xee\x06w\x88\xbd\xc1\xdd\xe0o\x10\x88\xc1A\x1c\x0e\xe72\xc1~X\x1cL\x8b\xc7A\x00&\x07\xe1\xb8\x1cD`s\x1e&\xf3\xb1\x07>\x07S`t\xe0\xc2\xe9\xc0\xdf=\xf3\xc0\xeb \xd0\x8b\x0b\xc6\xed\xac\xdc8\xa6\xe7\x81\xddA\x80\x94\x13bx\x10\x84\xe3\xc1\xd4X\x1eD\xe2y\xf6qE\xdd\x98\x1e\xc4\xe3zF~\xacE\x17\xb6\x07\x93\xe1{\xe0\x0fS\x81\x0f\xce\x07aX\x1f\xb8\x82\xf3\x91\x98\x1fx\xf0\xb5\xc4\xff&\xc2\xff J\xb9\xfe8 x\xf42\x02\x0f\x84XL\x10\xecZ\x9d\x0e\x1b\x04\x7f|\x10<1B\xf0\xc6 \xc1O\xeb\xe1x!\x04a\x86`\xc5\x0da*\xec\x10B\xf1C\x18\x89!\x82\x87z\x03\xb0D\xb8\x0b<\x11|d\xb4\xcc\x84\xe9\xb0E\xf0\xc1\x17a\x04\xc6hd\xc8\x1e\xb4\xe1\x8c05\xd6\x08N\xbc\x11b1G#7\xb1G\xb5o\xd7=\xb0G\xb0B$`\xc5 !\n\x874\xb2\xb2\xe2\x93\x10\x8bQ\x1a\xb9 ?\xd0\x125\x9b\x0e\xab\x04/\xbc\x12\"0K\x08\xc3-!\x06\xbb\x84`\xfc\x12\x1c\xab\xad\x03S\x82\x00\\\xc9\x17\xcb\x84\x18<\x13B1M\xb0w<\x06\xdb42\xeb \x87\xbeS\xc6\x0f\xe3\xb4N\x88re\xc79aZ\xac\x13\\x'\xd81O\xe3;\xb1X(L8v\x030Q\x08\xc2E\xa1\x83\x8d\xf6\xe9\x96\xd4y\xb9\xba\xda\x92O\xa6\x02\xca^\x91 \x1b\xa2'H\xdd\xf4|\xb5\xadrR\xe5\xb5\x03\x11\x1b\xd5Z\xbfZ\xa9\x02\x14\xb5\xb5J\xb5\xe0\xac\xa2-Z\xe5%\xff\x16\x87\xc2\xf6\xda\xd8?(\xe2\xdc\x98\xc7(:\x7fU\xcd\xe9\xf0XA\xb5\x0d\x08t\x94n\xc6\x9fk3\xc6\xe8\xd4\xa73\xa2\xd4\xeb\xea\xff\x9a\xa24J\n\xa5f\xf6O\x19\x94E\x94\x8a(\xf4%Z\xe1w\xf8\xe7\x06\xd3z&~70\xfb\xb9\xc1\xd5\x8e\xb3al\x99&1l\x08\xad\x01\xf3\xd0&\x8f\x89\xce\xe0\xa2\xee\xd8\xaam\xbd\x83\xdc\x14\xab\xa8\xd7\xb8\xc2<&^\x12\xd8\x90\n\xab\x18\xb8n}\xaeI\x8d\x0cH\xaa\xb72-u\xeae\xed]c\xb8\x8b7\xcf\xb5\xc8\xffQ6\x9b\xb9\x88\xbc\xa9c2\x9d3\x19\xa6\xfev\x15\x9d\x91\xa6\xac\xaf83\x93\x19\xfb\x84(P\\\x9fB^S\x85HPhJ1\x98\x17\"\xe8\xfa)\xa7\xfd\xf1a\x1f\x17\xda\xa4\x06\xaf\xe3\xbd\x03F\xeda\xdf.#u\xc9z:\xeb\xab(\x9d\xf5Mg}\xf7\x94\xce\xfa\xa6\xb3\xbe{\x9a4G!$?!(7!\x9d\xf5\x1d\x9b\x87\x10\x91\x830I\xfeAx\xeeA:\xeb;&\xd7 $\xcf \"\xc7 \x9d\xf5Mg}\xd3Y_\xdf\x1c\x81I\xf3\x03br\x03\xd2Y_\xd3c\xce\x1c\x80\x00\xfc\xdf\xe7$k\x08\xee\x9f\xce\xfa\xa6\xb3\xbe>\x18~:\xeb\xcbi\x0cN\x9f\xce\xfa\xea89\xb1\xf8X\x1c\xde\xb86\xa4\xb3\xbe\x87\x94\xce\xfaF\xe0\xe7n\xec<\x147\x0f\xc0\xcc\x83\xf1\xf20\xac<\x9d\xf5\x0d\xc3\xc3\xd3Y\xdf\x96\xfe!\xcf\xfa\xdanU\xdf#\xb0\xb3\x1b\xdc]\x05m\xd0\x95\xc40\x914\xa1\x15\xae\x9b\xaa\xe4A% \xabI\xa0\xa8\x05\xb2)7\x93\xb3)_\x95\xa4\x1a\xc4\xaf\xd5l\xec7!43\xf6\xc3\x0e\x8b\x83k>`\x85oq\xd5{\xd5\xf6\xf1\xe4\xd3\xc3\x0f\x97w\xf2%*\xac\x9f =>\xac\x0d\\rd\x8fT\x0b\\\x0d\x03X\xfa\xab\x92'\xd5\x86\xb9l\xc5qX\xdd\x8a_DY\x86_e\x95\x08C\xe9\nm>\x85*^\xd1K\x8c\x02T\x03\x82U~\x8bK\x10\xacMu,t<\x1f)]\xa4J\x16\x8a\xea{\xc8\xa1H\x95,,\x14\x9a\xf5ae\xa6\x0f\xfd[\"aSg\x7f\x80;\x03\x04\"\xb2@\xec\x1dH\x95,b3C 8;\xc4\xca*U\xb2H\x95,b3H .\x8b\x04\xa6\xca$\x81\xa8l\x12\xfbtH\x95,\xc2\xb2K 0\xc3\x04\xe2\xb2L\\&\xd8/\xd3\x04\xa6\xcd6\x81\x80\x8c\x13\x08\xcf:\x81\x88\xcc\x13\x0f\x93\x99*Y\x08\n\xceJ\xb1rK\x95,R%\x8b\x01M\x93\xbd\x02\xfeI\x18\xe0\x93\xc5\x02a\x99,\xe0\x82\x9e#3Z\xc0\x83o\xaada\xa1\xa8\x8c\x17H\x95,$Ee\xc3@PF\x0c\xa4J\x16>\x992p\x17\xd92\xe0#c\xaad1m&\x0d8\xb3i 6\xa3\xc6\xc8-U\xb2\xf0\xcb\xc01rK\x95,<3s 8;\x07R%\x0b-\xc5d\xee\x18\x99\xa5J\x16\x8aR%\x0b\x0d\xa5J\x16\xa9\x92\x85\xf6\x01gD)U\xb2\xd8\xff\xe4\xab\xccT\xc9\xc2\x90\xd6\x90jY\xec\xc92\x9aR-\x0b\xcd\xeb\xa9\x96E@6C\xaae\x91jY\xeci\xd2,\x85\x90\x0c\x85\xa0\xec\x84T\xcbbl&BD\x16\xc2$\x19\x08\xe1\xd9\x07\xa9\x96\xc5\x98l\x83\x90L\x83\x88,\x83T\xcb\"\xd5\xb2H\xb5,|\xb3\x04&\xcd\x10\x88\xc9\x0eH\xb5,L\x8f9\xb3\x00\x022\x00|*5\x84 \xff\xa9\x96E\xaae\xe1\x83\xe2\xa7Z\x16\x9c\xc6 \xf5\xa9\x96\x85\x8e\x93\x13\x8d\x8fE\xe2\x8dkC\xaaeqH\xa9\x96E\x04\x82\xeeF\xcfC\x91\xf3\x00\xd4<\x181\x0fC\xcbS-\x8b0D<\xd5\xb2h)\xd5\xb2\x90\xa4\x0eH\xaf\xbbgiA\x9e7\x1e\x84\xb2\xf7\xc7\x8d\xeb\xaa 8T\xef\xfbS\x96o\x90>\x01^\x88k\x8a8\xbfy\xfb\xe1\xbbs\xbeg\x16\xcf\xc9\xcdg\xce\xf1\x81\x978\x93ny\x8b\xc9t}s-C\x11\xaa\xd07F\xf3U\x89\xea\xa6\xc2\xb4\x9d\x89l\x91Z\x91\x15\xe1\x8e\xf0\xe1~\xb6\x9fJ\xc8&\x83RJ\x7f\x8e\x1c\xd3\xfe,\xd1\xbc}\xa0l\x9e:\xd5\x9bA\xf1 X\xfd\x898x$eaIJYX6\xbbr\x07\x00\xe6p\x05\xfbO\xb9\x1e\x1e[\x17\xb0\x05.\xf0\x8a\xd7\x19:\xfb\xa5\xfd\xf7\x95\xac\xf6\xf3\xebY\x85?\xa1jA-\xa5\xa0:;\xb8\x97\xe2\xfd\x9c\x94\x1f\x983\xf4N\xbc\xdb[\xed\x84\x97$\xb9\x02\xca\xb2\xaa\x11v\x01q\xf7\xb5e\xd5\xe6#k\x97B}C\xf2\xc9\x07\xbb&\xca^?\x8c\x11\xdc*\xf8jle'\xd1-\xfb\xeb\xfa\xbe 2\xf6P\x90\xa3\x9f\x82\\\xbd\x15du\x05\x049{+\xc8\xee\x16\x08\xf2b\xe5\xe3\"\x08\nv\x14\xac\x8a\xe0.\x84\xc3]\x10\x14\xe54X\xf8\xc9P\x9f\xcdu\x10\x14\xee@\xb8R\xb1\x05\xed\x8d\xc7Ke\xef\x84\x01\xd9G=\x85\xad\xda\x0f*-\x1f\xb2\xe4j\x97,\x8e\xa9\xfa7\xfb\x00bN8\\\x1ce\x05\xdb/Z\x14\xd2\x13\xd1X\xc7\xb6\xa1!SC\x96\xfe}\xd8\x94\xe4j\xb7\x145k\xee\xc9\xd5\x16k\xb2\xd2\n\xcf\xd2i6|x\xf7\x07\xa4\xc5\xdf>\x98j\xdc\xf9\xd0/\xd3\xf1\x8e\xb7\xc1\xbfH.\xb7\xa4\xe4r\x7fa\x97\xdb\x86\xb8\x1c8\xd3\xa6\xa1x\xf0`o*\xb6\xbf\xaa\n\x9cP\x13y,l\xd9_\x0cF\":w\xb9}8\xfb\xe5\xc0\xdd\xb4U\x97\xdd\xcf\xf3\xc0-\xc4~\x05vl\x1b\xd2\x8e\xe1\xf0g\xd7\x04L\xab{K\x7fO\xab\xfb\xd0\xcbty\x98\x9d\xc9\xa3\xe1\xe6Z\xe8'\\\xe3\xd3\xf2>\xa0\xb4\xbc\xa7\xe5=zy\xdfK~\xb0\x14\x9b$?x\xb0'y\xfb\xeb]K~'\x8e\xc9\xbe\xbc\xba\xdb\x0f!U{\xde\xba\xef\x89\xec\x99\x0c\x82\x016\xef\xa3\xcb\xed\x91\xd2\xc6\x03\xf5?\xee\xa8\x08\xbda\x1e\xbaW\xf9\x8e\xc2\xb5C\x91v?\x02[{\xd4\x9a\xa6\x9d\xf5\xcba0\xc5\x7f\xa5\xeb~\xc4\xd1k]o|\xa5\xd5NRZ\xed\xd2j\xf7\xb0\xd6\x0c\xb6\x93XT\xe8\x93\xfa\x8b\xcf\xca\xf1\xdf\xf2\x9d\xe7\xb2\xbfj\xf9P\xbcZE\x84, \x03\xa6\x8f\x94~\x1e\xe8*2\xd4[\xd0\xb4\x1d\xc8<\xe4\x159\xbc\xc2l\xfd@\xdf\x13\x18\xfc\xe1\xb0HV_R\xb2\xfa\xc9\xeaOn\xf5\xadF\x9f+\x80\x9aM\xf9%\xff\xbd5\xdc\xe2\xf1\xf6dP\x87#l\xc8\xa2)\xb0\xdetw\x9e\x13\x0c\x1f\xa9\xee=P\xa3\xddUK\x97\xfaIZB\x19\xdd\x8f\xbd\x1fOJG}\xb5\xec\xc9:\x99\xecSi\x9fuY\xa3\xcf\xe6\xb9f\x99\x0dsD\xf1U[\x8b\xcf\x96\xc2\xe0bD\xca\x86N\xc2\xa9\xb7\xb4]\xe1\x12\xcd\x0bl\xe54L\xe7\x05\xe7\x1e\x8aO\x081\xfe\xbc\x161\xb9l\xc9)\xf0\xee\xf2\xc5\x80_Z\xb2\xd2\x92\xf5\xe5\x97\xac8\xe7~\x1f$\xd0\xa1Q<\x91;\xa74'\xa5\xc5\xado\xf7\xe9/\xda\xa7\xdb\x85\x01eY\xb3i\n^\x1db\xcf\x8c\xcf$\xe4Hg\xd3\xb0\x95\x8f=\xd8\xe5a\xdfC\xc7\x12\xd1QEw\x99\x10\x7f\xceeY\xa7},\xb1\xc2\x19\xceo\xb1&uf\xe4ra\x12\x16\x1c\x13\x0b\\\x19j\x8e \x06N\xe9\x049s\xd3<\xe6\x8a\x0be\x03?6\xee8\x9c\xa0`\xc4\xcd\xd2y\xdf|\xb4(\xe4\xcd\xc8\xcd3\x17-\x0c\x81\x93uM\xf5{J\xcdT\xf7Z\x89u\xbc\xcet\xe6h\xbf\x93l_I\xab\xf2\xe0\xd7\xb4*\xdf\xc1\xaal\xdbH\xfec@N\x0e\x0f\x8345\xad\x11?\xe5v\xe5N\xabo\xe7\xf6\xdb\xfdk\xc3l\x18\x05\xe3\xf3\xd8\xe1\x81r\xec\xae\xc6!\xdbGJO\x0f\xd4\xe5p$\xc3Dy\x06F\x9e\x90\xdc\x82!%\xb7`@an\x01x\xa9\xd06\xed;\xe9\xe9\x1d[\xa2ar\xd2\x94_\xa9hB\xf9X\x0dr]G\x06\xbb\x13\xc8\xb9\x1fP\xd2\xfc\x16\x8b\xd3\xbc(\xbb9\x15\x15L)P\xae>\xa0\xa8\xcc\xb5\xf7Jfk\x9c\xdd\x84\x9f\xca\xb3t\xd9\xe9\x1a\x0d\xf8i*\xf0\x0e<%\x8dZS\xec]Rr\x99\x92\xcb\xf4\xb0\\&Z \xba\xc6^n\xd2{\xf1h\xeb\x1b\xf1W\x01\xdf\ns\xb9\xf4\x8f\xc1HF\x8f\x94\n\x1e\xa87$U\xf30&\xd7\xfe\xd3mq\x95\x13\xc7a@\xafib\xb98d\xc9k\xc7\x9b\x829\xceV\xdc\x8b0\x0cF\xc3wl\x14u\x97\xdf\xee\x92\xd9\x19h\x86\x1a\x91\xf2~\x8e\x9cr\xff#\xcfx=\x97z-+\xc2\xd0\x9aT\xfc\x96\x18\xc3\xdb\xfc\xce\x83\x9c\xf2b\xdb\xaa\x8ax\x91\xf1@#\xa0-\xfb2U\xce\xff\xbdQ5Nh\xcdKbk\xb9qWL\xeb\n\x08g`\x9f\x04\xac*\xdb\xa3\n\xb7\xd0D)\xab\x0d\"\xd9\xeb\xb5\xa1V<\xc9x-m\xd7\x0189\x86\xfbG\x8f\xe4\xdf\xfc\xc2\x82cn&\x12hU\xa7\xccK\xcf\xc5\x984\x00\x99n&J7\x13\x0d\xe8\x01\xdcL\xd4\xf7\xbe\xe5\xba\xe7t\xb9\xdd\xd1H\xb5\x14'\xc7ZRr\xac\x93c\x1d\xedX\xef%\xa75\xaa\xf8-\x85\x07\xc5\x1cm.\xcd\xe0\xad^'\xd453\xedC\xb2N\xe4\xbe/\xc3\xc9.\x97\xe7a\xffBko\x1d|PG\x899Q\x1c\xec\x0e:.\xab\x8e=\xd0n\xa7\x1a\x9aajL54\xefP\xb9\xee1\x9ajhN\xa1\xc5TC3\xd5\xd0\xfcm\xd4\xd0\x1c\xe6\xdf\xe2\xdb|\x81\xcb\x0c\xb7\xe1?\xf5\x07s|\xefyQ|'\x1f\xdagZ\x15\x05\xa87\xb5\xb1\xbc\xceK\x8fT?\x1eh\x18\xaf\xaf\x81.\xfd]\xfa\xf2~\xd15H\x97\x8b\xa6\xcbE\x8dO\xa6\xcbE9\xa5\xcbE\x0f)].\x9a.\x175Q\xba\\4].\xca)].j\x1e\xd3\xe9rQA\xe9r\xd1t\xb9h\xba\\\x94S\xba\\\x94S\xba\\\x94S\xba\\TP\xba\\4].\x9a.\x17M\x97\x8b\x0e\xc9\xf7\xa2\xc7t\xb9(\xa7t\xb9\xe8o\xe5r\xd1=\xf5v\x88\n\xbf\x90\xd0T\x1f\x9c9\xd8_\xa7tMN)]s\xff\x93\xaf2\xff\xe1\xd25\xf5\x87\xa5:\xd0f|\xa6f\x17T}w\xf9b\xd8\x89\x94\xb3\x99r6\x9dAN\x9f8!$\x9c7\xe1\xbc\xc6'\x13\xce\xcb)\xe1\xbc\x87\x94p\xde\x84\xf3\x9a(\xe1\xbc \xe7\xe5\x94p\xde\x84\xf3&\x9c7\xe1\xbc\x82\x12\xce\x9bp\xde\x84\xf3&\x9c\xd7D \xe7M8o\xc2y\x13\xce\xdb\xa1)0\xb7\x84\xf3rJ8\xefo\x05\xe7\xb5\x95NH\xc7\xd1\xc3\xce\xfa\xa6\xe3\xe8w\xa8\\\xf7A\xeat\x1c}\n-\xa6\xe3\xe8\xe98\xfao\xeb8\xfa\xb1\xf3<\xfa\xd9/\xea_WkD\xd7\xb6\xbb\xea\x0fN\xa7\xb7\x19Q\x1c\x92\x01R\xee\xff\xc2xi\x0f\xac\xffVN\xabGe[\xd5\xd6d\x04g\x1c\xde'\x90=y\x1a\x82_\x12BL\n\x82=\xd5 *\xd1\x807a`\xe8L3\x98 \xc9 2\xc5\xc0\x08\xcc\xfa%\x18\x8cJ/\x88J.\x00T\x14&-\xfa\xa5\x16\xc4$\x16\xd8\xe0>\xaf\xb4\x82\x89\x93\n\xbcR\n&L(p\xa6\x13L\x94L0&\x95 8\x91`\x824\x82\x89\x93\x08\x1c)\x04\x93'\x10\xdcM\xfa\xc0\xe4\xc9\x03\xfe\xa9\x03q\x89\x03\x16\xa5\xbb\xd2\x06&K\x1a\xf0K\x19\xd0\xc4,\xcc\xf6u\xe2t\x01W\xb2\xc0\xc8T\x01K\xa2\x80\xd3=q& \xf8\xf9/\xd3&\x08\xb8\xd2\x03\xdc2\xc5\xa5\x06(\xcb\xaea\xe8J\x0c\x980-`DR\x80>\x95\xc7\x96\x120mB\x80=\x1d`\x8ad\x00/4\xdb\x91\x08\xe0\x9d\x06`F\xec\xc2S\x00\xcc\xbc\xb4\xd1\xf1I\xc0\xff\x10e\xf9\x02\xffn\x9dx\x83\xfe\x11\x90\xbf\x1eI\x98\x08\xee\xf7\x02\xfb\xddP\xbf\x0f\xd0o\xd5b(\xc8\xef\x0b\xf1\x9b\x00\xfe \xe0\xfd\x00p?\x1e\xda\xb7\x00\xe8\xbe\xb0\xfe\xc4\xa0\xbeE\"\xedH\x8d\x82\xf3U\x14V\xc3\xcf\x00\xe6O\x0c\xe5\x9b\x81\xfcX\x18\x9fG\x04t\x82\xebA\xfci!|\xd3\xc6\xcf \xdf\x9b\xf0E\x13t?-p\x1f\x0f\xdb\x1b \xfa(\x80\xde \xc6\x87A\xf1\xde@| \x0c\x1f\x02\xc2\x1b!x\xb34\xbeP\xa8\x1f\xfc\x1e\x08\xbe\x07@\xef\xda\xaeM\x0b\xbb\x9b&\xc5\x08\xc8]\x1b\xa70\x02\xeeqp\xbb\x0dZ\x9f\x1eX\x1f?\x92\xbcAu_H\xbd\xbfDz\x1c\xed\x0c:\xd7)Or\x0e\x80\x03E\xe9\xe6\x0dI\xe9\x14g:\xc5\xb9\xa7t\x8a3\x9d\xe2\xdcS\x0c\xd8bd\x96Nq\x1e\xd2D\xc0\xcb8\xe8%\x02|\x99\x04~\x99\x1c\x80qB0w\x00\xc2\xdc\x15\x0cs\x07@L\x08\x14\x13\x0b\xc6Xm\xb8\x0b\x8e\x99\x10\x90\xf1\x85d\x02A\x99\xc9a\x19703\x1a\x9aI\xa78\x9d\x92\xc5A5ZV\xe9\x14g\x0ch\xe3\x82m\xa6\x01n<\xd1\x08'x\x13\x00\xdf8O\xd3\x05B8\xe9\x14g:\xc5\xe9\x03\xee8\xb5\x1a\n\xf0\xf8C<\xe9\x14\xe7\x80&\x06|\xd2)\xce.\xc5\xc2?Zf\xe9\x14g\x00\x184\x06\x0e\xd2\xb2K\xa78\xb5/x\x01H\xe9\x14\xe7tpR:\xc59\x1al\x9af\xccy\x03N\xfe\x90\x93\xdf)\xce\xde\xd1\x96\x0e'm]_\xfeP\xaf\x06/\xff\x83\xb4\x86\x95\xa8\x88\x8a\x17\x07\xf71\x02Lp\xf1\xb5vs\xee<\xd5\xb3\"\xb7\xed\x81\x1e\xae\x07z\xf6\x8b\xf8\xef\x15k\xc5v\x98\xe7\x92?\xd6\xbbhr\xafI\xd5\xe9\x15\xb9\x85\x0dY4\x85\xfe\xf2\xc9\xef\xc9\xad`\xf3Hu\xf9\x81\x1e\xe6\xb9%\xfcvl\xa1\x9aC\xdf\xb8'S\xef\xd9AI\xe6V?\x15.\xf8\x86\xb8&\xf2\xf9\xc3u\xca(&8\xf1.%\x02\xaer\xb2\x88L\xc6\xed\xf5\xe95.Wu;\x94\x05{\x10\xec\x87\x82/\xf0\x96\xd0\xbc\xf6\xd3U\xffa\x0fe\xc9\x17&\xd5\xd6&/\xaf$_\x9b\xae\xf4\x80%\xd8@Kp &\xc8\x05^\x02W[I6\xb6]\x99\xf3\x832B\x1b\xd2\x94\x86^\n\xf2`\xe3\x13Hc\xf4\x82\xe4\xfb\x92\xe4\x08jr\x83K\x19\x08\x13\xddQGL\xd9R\x8cJ)\x9c-\xe6\xfa\xe6\xed\x87\xef\xce\xb9\xf7*\x9e\x95n`\xce#u\x17e-\x17\xc86:J\xad \x80\\=\xc5\xc6\xc1\xdc(\xcdW%\xaa\x9b\n\xd3\xd6$\xb3\xad\xd2\x8a\xac\x08_\x9a\xf4\x1efOI?\xe6e\xbei6j\xf4\xf2\xfd\x02\xe2\x9f\x9dPT\xb0q\x8dK\xb6\x1d\xb1N,F\x1b\xf4\xf9\xaa\x9d3\x93\xcdns&=\xfa\xcc\xe5\x16Mq\xb1\x9f3\x951\x0f\x84M\xcc\xfd\x84\x04\xf6\x19\xdb\x1e\x99v\xa5\x17e^\xe7\xa8\x90qh\x18f\x19\xb4\xb4!e\xbd>\x88a\xd7\xa8(v~v\xa5\xfb\xa8\x87U\xe1\x8fOjS~nH\xd5\x18\xe6\xab\xf3\xe38\xc3\xec\x9e_O\x8e\xba-\xae2\xe6\x93\xaeD8\x99\x1f\xd4\xa65\xba\xc1\x1c\x89j\x17!\x91/c\xc2\x06\xe5\xb9s\x8eZ\x99>[FJ\x9a/0\x9b <\xa8\xaf\x1b\x06\xf5\xba\xc2\x94\x8d\x9f\x07\xa2\x1b6b+\xb5k\xfd\x7f\x98rMPq\x08\xbe3?\xb7\x88\x1a0\x15\x80\x972\x0e\"G\xf5\xd7\xb3?\xe9\x9e\xbc\xc55\xb9z`\xbd\x17{|\xb2\x84\xff\xc2r\x0c\xf09\xfd\x81\x0f\x11\xf1\xbf\xdcY\xb3\xc0\xf9]%\x99G\x06\xeb<^\xcc\x86\xaazr\xf6M_U\x1e\xa9_\xc2a\x0cI\xfc\x92\x9e\xea\xbb\xcb\x17\x03~)\xed+\xa5}M\xe6\xed\xa4\xb4\xaf\x94\xf6\xa5\xa7\x94\xf6\xc5)\xa5}\x1dRJ\xfbJi_&Ji_)\xed\x8bSJ\xfbJi_)\xed+\xa5} Ji_)\xed+\xa5}\xa5\xb4/\x13\xa5\xb4\xaf\x94\xf6\x95\xd2\xbeR\xdaW\x87\xa6H\xc1Ii_\x9cR\xda\xd7?B\xdaW'\x05\xaa\xc3\xc7\xb6\x93\xec\xbc\xd1\xe2\xcd\xe2\xb3u\x10\xe7\x9a\xc8\xbb\xc5\x97\xa4:UE\xe8E\xbd\xf8\x1e\xb3#\x91\x07pt\xdaW\xef\x11\x87\xa9\xd9\x0fl\xa3u$\x91\xf7\xa3 \xd3\xc8L\xf9b\xdat1 \xfc\xc9\x17\xb4)b\xea\x91~\x96X\xfb\xd7\xb6\xea\xf3*\xbf\xc5%\xd0\x1a\xd5\x0d\xd5&\x8a\xb5\x9c\x1e\xa9N=\xd0D\xb1\x81V\xbaT\xdf\x03\xfe\xa5\xc4\xb9\xca\x0d\x88sP\xfc\xc7r\xaf\x85`\x94\x07\x82aT\x98\x14J\xf5\x16\x15\x0c\xb1\xc2\xd40\xab\xb7\xb4\x80\x08\xbb\xb2|\xe1V@\x85\\aB\xd8\x15\xd2B\xaf0%\xfc\n\xc9!X\x88\xbcm#a1H\x08\x8da\xc3\xb10%$\x0b\xa9aY\x087|Jx\xd6[\x98\x15\xfc\xc4>2\xb80m\xf0\x81\xa8\x96\xe1P-\xe4\x0d\xd7B,d\x0b\xe1\xb0\xad\xf7\x9c\xa9\xe1\\\xc8\xd8w\x13\xc2\xba\x90\x14\xda\x85\x1d)yc*\xb4\xb8G\x08\x8cU\xbel^\x90\x91\xd2\x8b\x8f\x1f.>\\\xbe\xfdavy\xf5\xf6\xea\xd3\xe5\xec\xd3\xfb\xcb\x8b\xb3\xd3\xf3\xef\xcf\xcf\xde%\x9c\xf5\xee\xec\xe2\xc3\xe5\xf9\xd5\xec\xe2\xec\xe3\xf9\x87\x94\x13\x7f\xfapu\xfe\xfe/\xe9\xe7]\xbc\xbd\xbcL\xaa\xe1\xc7\xb3\x7f;;\xbdJ:\xe5\xfb\xb7\xe7?xO0)\x97\x13\x1c\x88]U1\xb1\xe3K\xd9\x07\xe4\x9d\x94\xdf\xfe\xea\xe1\xd4kA\xf27& e\xff\xd0\xd3\xa7\xb7\xfb\xbb`\xb0+\x04\x9b9\xc8S73\xc8.\xbdv\x18\x1cG]x\xd8\x9bv\xaf=\xfc\xddRj\x18]\x14\x16[\xf9\x16T\xf5\x92\xd0\x81\xdfE^\xe9\x02m\x91\xbe\xbb[\xcb\xc1\xcf\xb8J*|\"g\x1d\xd5s\xb2[9\xf5\xf7@\xadD\x7f\xb2r\xa4\xc5k\xe86\x10\x17\xda\x14m\xcb\xd2\xaaf\x9e\xc8\xdd\xca\x99_\xf2U\xef\x9a\xb1\n\x1a\xf6w\x99\x00\x9dTK5\x08\xec\xd6Q\xfd=_\x0do\x8ar\xe5\xab\xdaMY\x15\xab\x99\x92\x86P\xb1!\xdf\x88\x8e\x1dZvK4\xf9\xe8\xf2\x17\xa5+a\xa4\x13\"\x8b\xca\xdd\xe0\x02?\xdf\xb2\xc0\xf4\\\x12Dj>\xd2\xb9\xe5\xae,`\xf9\xf1\xe2\xf4H\x07Y\x8c*JU\x07\x9e\xd4z\xb3U\x02\x18\xdb\x8a\x97\xabp\x00\xd6\\\xe9e;\xd4(\x91s5V-\xfc\xbd\x81\xe7!?^>\xb2\xf6\xe5\xdeq\x85\xe2\xba\x15\xdf.{\x97\xf3\xb2\xaa\xf7\xafLU\xcf\xc4\xb7\xd8\xec\x8e\xf1z\xaf\xc2\xda\xed\xf5\xba\xe43^\xaes$y-\n\xce^\x8b\xb2\x9c\xc7\x19\xf1\x19V-\x9e\xe6\x82R\xae$,\xcf\x14F\xab\x94\x05%\x9aP\xbd\x14\xd7O\x11BM(\xe7\x00J\xac Y\x14v8K\x96l\x8aV>\x1c\xee\xcc+\xe7\x84\x16t\x9a*\xe9\xa4U\xd5Z^4O\xf4\xb4\xe9+>\xd1\xc3\x86\xe9&f2=\x98\xac\xce\xeb\x86\xe9{\xb7\x16_\xd3\x8d~m/\xeb;\xd6T\xc5\xee\x9e[\xca|jQ\x9bb\xa9\xbb\xd9n\x83\x07u\xec\x0f\x1ci\xd4s\n\xf1<\x81v&\xe1$\x12N\"\xe1$,\xad\x9c\x95T\x9eB)\x93p\x92\xef\xb0(\x8d\x9c@\"cd\x81R\x08d\x12N\"\xe1$\x0cML\xc2I\xd2\xf6!\x86I8\xc9UR\x94\n\x9eJ\x04{\xdf\x0d$\x9c\xb4k$\x9c4\x81\xe4\x8dS\xbc\xa9\x04o\x02\xbd\x9bL\xee\xa6Q\xbb$\x9c\x94F\xe6\x92pRg$\x9c\xa4\xad\x13N2z3\x8a\xde\xb2\xca\nE3Gg\x0d\x82\xb9=\x06&C\xbb&*5\x9e'=)v\xf9\xe5PK'\xba\xf8\x85\x90Jd]\x0e\x89N:q\xc9\xa7G$CX\xe4\xd3\xa2\x90\xbb\xf8cY\xbdQa}\xebo\xbd$\xd8M\xb1j#\x9a`\xe0\xa4\xdf\xf1\xc4{\xeca\x89\x1c\xec'\xdbQ4;\x92`GP\xebI\xa4\xba\x19\x0f\xefj>X\xee\x1a\x8c\x82\xf2\xc7\xc1\xf0\xa3\xfeR,\x16\x0dk[\x13\x9e\xb7\x07\xbd\xbe\xa4\x0c\xf7\xd5TR\x0f4\xb5\xb7\xa2\xdd\x01\xa3\xb1R\xed\xfc\xa6\xab\xcbZ\xb8i\xea\xf5\x93\xd4\xb8'|\x8e?\xb3G_\xb5Go\x19\x0d\xc9\x14z\xa6\xde0\xbem*\x19\xbb\xd0\xdc\x86\xe6\x11:\xa2FF\x1c\x96\xa3\xa5\xf9\x0e\xbf\xe5\x11J\xe6\x83\xf8\xaeR\xe2~P\xdf\xdc\xb4\x8cC\xdd\xc0\xb0\xba`EL[\xc63{\xcb\xb3\xfe\xecp\xa2\xaa\x9f\xcf\x8f\xa3\xb5_\xdd\x18\xe9J\x99KQ\xce\xcd\xdf\xe4\x98\xa4u\x0d\xd5\xe2\xfb-\xab\x8c\xe3\xb7U\x17\xef\x18\xbd\xbd\xcfei+\xd1\xe7;\x17\xaa\x08\xc1\xb6\x15\xae\xfe\xcc\x12\xfd9,\xfe\xc0\xce\x1d1G\x0e\xf7\xae\xcau\x89\xf5\xae<\xd6\xe0:>\x14I\xc5\xc2\xec\x1e\xac>9\xc5\xaf\x83\xd26j\xe5\xdb\xfe\xd3\xf9\x0d\xac\xd8\x0d7\xe8\x96f\xb9\xcc\xc4G\x86\xf1\xd4\x03\xa2.\"\xfc|\xfd\x08\xac\x98\xdfB\xb1\xd9|A/\xda@U\x7f~\xc8\x97\xd6\x19\xc2\xa3\xb2\x87\xd6R\x06\x13\xc4\x7f\x94\xd5\xa2\x9c\x17\x9cu\x01}C\xea\x8b\x03uG\xb2\x8b+\xab\xf9j\xbb\x18\xad<\x14\xea*\x1dQ1\xbac\x92\xcf\xb1\x02}r_A\x9bO\x1c\x14\xf6\xe9|<\xb1\x1d5A.\xd64\xac\xd5$\x95|\xbc\xfa\xe7Qm\xcd~\nk\xff5A8\x10\xa5\x1b8R\n\x1c\x95\xf7\xf1\xe2\x94t\x02\x81t\x02\xb3\x8d\x87\x93XC\xd2 \xc4\x10\x86Y\xf8\xc2)t!\xe9\x04fd\nS\x88\xc2$\x9e\x90t\x02\xf7\xa5\x08'0\x84Y\x08\xc2t~\x90t\x02\xf7\xe1\x06S\xa8\xc1\xcc\xcc \x8e\x18\xcc\xc8\x0bbiAG \x80t\x02\x87\x86\xe0\x03\xb1\xb3\xa4d6\x90t\x02QD\xe0\x14\x1e\x90t\x02}\x87E\x19\xc0\x04\x02\x10\xa3\x82\x97B\xff\x91N \xe9\x04b\x18?\xd2 \x94\xb6\x0f\xd5G:\x81\xae\x92\xa2\x1c\xdfT\x8a\xcf\xfbn \x9d\xc0]#\x9d\xc0 \xb4^\x9c\xd5K%\xf5\x128\xbddJ/\x8d\xd1#\x9d\xc04*\x8ft\x02;;\x04\x89\x97\xa3\xcf%Pxx\x06/I'\xb0\xb4G\xee\xe1\xe6n\xfd!\x83\x80\x98Z\xed\x87r\x01#M@{D*\xe5\xeep\x83a\xb0\x97\xec\xe0\xcdv\xaa0M\x0e%\x8f\x13\x1d,o\x03\x92\x1e\xef\xf4!\x9d\xa4G!5z\xf4\x1fe\x00\xb0-\xab\xe5j\xb7\xf5\x03=\x0fS\xcc\x0b\xe3\x82g\xaa\xe7aZ\xf6h4 1F|\x906\xe2\x83\x88\x0f\xea\x8d\xf8 \xe2\x83z#>\x88\x13\x1f\xe46\xe2\x83\x8c\x11\x1fD|\x10\xf1A\xc8Y\x12\xf1A\x9d\x11\x1fd\x1b\xf1A\xc4\x079\x8c\xf8 \xe71\xc4\x07\x11\x1f\xe41\xe2\x83\x88\x0f\">\x88\xf8 \xcbr\xb0\x1a\xc4\x07I#>\x88\xf8\xa0\xe7\xcb\x07\xd1\x16uS\xf7\xff\xa2-\xea\x0e\xe8\xdcx\x1f\xa5-\xearx\x91\xb6\xa8\xa3-\xea\xfe\xb8[\xd4u`\xeb\xc9\xaf\x1d\xd9\x18\xda\xb7\xce\xf2\xa6!\xc4\x0c\xee\xaa\xe1V\xb3\xc5lY\xa9gP\xf4G\xb5\x8b\x8d\xb9\xee\xf9\xbb~R\xa2\x8f~\xbbX4!\x1aV\xff\xf4\xdcaX\x1f\xbf1 )\x8b\"\xad\xd1 D\x14\x84\x8a\xc0\xac\x91\x0b\x84\xa0N\x1e\xc1XI?\xcci\x89\xd0*\x02M\xcd\x0b\xa6\"\xb1\xd4t(5\xee\xa0\xcc@j\x10G\x9d\xac\xc5\xa5+9\x01\xb5\x1b\x95F\xa4\x9d6\"\xed\xf2\x8c,D\xda\x11i\xe76\"\xed\xa4\x11i\xb7kD\xda\x11i\xe73\"\xed\x88\xb4\x93F\xa4\x1d\x91vD\xda\x11i\xa7\x8cH;\"\xed\x88\xb4#\xd2\xcegD\xda\x11iG\xa4\x1d\x91v\x96\xe5\xa0\x9e\x88\xb4\x93F\xa4\x1d\x91v\xbf\x05\xd2\xae\x0b<\xfb\xea\xdf\x1d0\xda}Q\xc5\x00ud\x8f\xb5p\xd3\xd4\xebA;\xda\x8c\x0d\xc9\x02X\xc8]\xe8\x02D\xc5\x95\xf8\xfd\xa3\"\x84\x0cJ!_\x15r }\xb8\xf1\xde]\xcd\x99\x13\x93\xb0\nya\xda\xf9LQ \xd9\xae\xdd\x89\xfc\xa0.\xaa\xed\xf6\x9do\x94\x90\x8a\x98,\x89\xdfv\xdf\x9f\xde\x9a@4\x0e\x17\xd8\xeb.\xb2\xfa\x14\xdc\xe3.r\xae\x7fo\xbb\xc8\x89\xf1=\xed\xbc\x05 \"\xd8VOJ\x89b_9\xc2\x1b\x14\xc3\xd6F1l\x8aa\xf7F1l\x8aa\xf7F1lN1l\xb7Q\x0c\xdb\x18\xc5\xb0)\x86M1l\xe4,\x89b\xd8\x9dQ\x0c\xdb6\x8aaS\x0c\xdba\x14\xc3v\x1eC1l\x8aa{\x8cb\xd8\x14\xc3\xa6\x186\xc5\xb0-\xcb\x11O\xa4\x18\xb64\x8aaS\x0c\xfb\xf9\xc6\xb0\xb3\xc4\x84\xefj\xceB[I\xfd$~\xef\xa2\xc1\xf2h\x15 ^\x96w\xac\xdai\xef \x14,\xcf}aZ\xfcL\x83\xc0\xb2I\xcf#~\x15M\xb3OZ\xbf \xec9\"\xda<}\xd7(\xb5\x00\xef;\x1d\xb3\x02\x03*\x89\xbaa\xf3\x82\x8b\x87\xe0\xa2a7b\xa2\xa6\xc2\x0e\xbf\xa8\x0b\xb4\xbf@Y\xb5\x9c\x15\x0b\x1d\xdc\xba\xf1\xce\xad\xa0\x13\xf9\x10#\xa4\xee\xac\xfe\xd1J~\x10-\xd4$\xb2\xbc\x81_V\xacz\xa5\xaf\xf95|\xfb-|\xf3\x8b\x9e\xa0\x16\\7V\xbcD\xbc\xc5\xdd3\xb9\xa8\xf7\xcd1\x9cWP\xac\x02\x0b\x9ej\x19q^\xb4\xac=\xd2\x8b\xadr\x8a3\x12\xa8\xf1\x9e\xff\xd3\x87\xab\xb3\xd9\x87\x8b\xab\xf3\x0f\xefg\x9f\xde_^\x9c\x9d\x9e\x7f\x7f~\xf6\xce\xf7}\x10\xbd\x93\x00\xac\xda\x06t ^\xfb\xae\x88<\xe3og\x97\xc8#\xdf~wy\xf5\xf6\xfc=\xf2\xe8\xf7\x1f\xd0\x07\xce~>\xbf\xfa\xeb\xec\xa7\xb3+\xdf)\x06\x03Hj\xaa\xee-\xe1G\xe8y\xec\x9c\x16~Z\x95!:\x8a\xb2pwQ\x96\xdei\\\xe7\x85\xba\x8e\xeb\xf8X\x07r\x9d\x13\xe8F\xee\xc3\xa3\x9dI\xd9\xa4.\xd5\x9f\x8c\x1b?\x95\x89W\xec\x075D1)UVp=5Q+\xeeb\xa47\xbdU~\xc1\xfa\xc3\x82\xca\xd4k}Y\xdf\xb1\xa6*\xaa\xb9=\xa3\x89\x9c\xe9\xbf\xf1^GX\xfa\"U\xfd\xba\xde\xd8\xd5\x0d,z\xb8.\xf7\xb7\xb3\xcb7\xe3?X\xc5?\xea\xc9\xcb\xb4\xc2u\xe7z\xe3\xfa\xe3@\x05DAX{\\\xe9\xfd\x877\xa3\x7f\x0f|\xb4W\xc9}\xef\x1d_\xa3\xffex5\x19\xd0\xb8c\x1c}]\xf5*\xcc0\xd8\xe0\x9f\x82\x9f\xe5%\xd9\xc2z\x12\xfa6l\xabRr\x0b\xb2\xfa\xa2\xfb\x8b\xff\x08\x14\xd6nVed{\xc5\xa8\x9a\x96mzs\xb7\x97\xee\x93^:\xce\xc24\\4\xd5j\xa4\xba7\x95\x98\x93\xa3\x1f\xdb\xb7\xaa\x90y]\xb5ek\xf6\x84\xed\xe0\xce\xf3wGj\x0c\x11\xf3\xc4#\xb3Z\xe7w\x9e\xafc\x0c\x1a\xa3\xbe\x1d\xcc\x1er\xa2<5QS\x83\xd4N0\x93v[\x94\xb6\x08\xe8}\xf6F\xbb-\x02\xee\xcb\xe7\x8f\xb4\xdb\xa2\xfc\xfcN!g\xd5\xb7\xfe\xc7\x8b\xd3QiD\xce\x129\x1b}gc^\\@\xe4,\x91\xb3\xde#\x89\x9c\x95F\xe4\xec\xae\x119K\xe4\xac\xcf\x88\x9c%rV\x1a\x91\xb3D\xce\x129K\xe4\xac2\"g\x89\x9c%r\x96\xc8Y\x9f\x119K\xe4,\x91\xb3D\xceZ\x96\x83b$rV\x1a\x91\xb3D\xce>_r\xd6\xb9y\x17\xed\xb3\x08q7\xd2>\x8b\x07tn\xbc\x8f\xd2>\x8b9\xbcH\xfb,\xd2>\x8b\x7f\xe0}\x16%nu\xf2\xab\x84\xbbB\x1b,\xbe\x94\x88\x98\x9d\xfa\xb1pl\xa5X\xf7I \xe7\xef\x8e\x142&\xf7P4t\xdbNJ\xc8\x0b\xd3\xf2g\x9c\x11\xe2\xc32&1b\xd1\xbc\x8ehl!J6\x052:\"\x85\x87\xf8pLL`r&\x07\x94\xbe\xa0\x14.\x8f#k\x16\x076\x87c\xbf\x0c\x8e\xa4\xfc\x8dh\xa7\x08\xc1\xf8\xa9\x10>\x16\xbeO\x81\xee\x91\xb0}\"d?\x01\xae\x0ffk\xf0H\xae\xc6Sl\x0f\x1a\xcf\xd1\x88v\x06e\xf1\xfc\x8c\xd4\x8e\xe1:+\x96\x9b\x91\xd2I\\gD\xf22\x12;\x8c\xb2 \xdd\xa6?\x153\n*\xcb\x9c\x8f19\x1b\xe3Is1\x0e\x98\x89\xf1Ty\x18\x87\xca\xc2x\xf2\x1c\x8cx\x06\x06b(\xc1\xf6\xf8\x8c\xb9\x17\xd1\xcc\x8b\xe8\xec\xba\xb7\xd4\xac\x8bxs\xf7\xce\xb8\xc8\x99o\xe1\xea\x02H\x1c<\x95\x06'\x18\x9c`p\xe7\xef\x99\x06\x10\x82\xc1 \x06w\x1b\xc1\xe0\xd2\x08\x06\xdf5\x82\xc1 \x06\xf7\x19\xc1\xe0\x04\x83K#\x18\x9c`p\x82\xc1 \x06WF08\xc1\xe0\x04\x83\x13\x0c\xee3\x82\xc1 \x06'\x18\x9c`p\xcbr\x80\xb9\x04\x83K#\x18\x9c`\xf0\xdf\x02\x0c.\xc3o\xbe\xba\xcb\x1f\x07\xb5V\x7f\xd1\x1b\x00w\xc1\xb3\xcd\x97\xdb\xfd\xf7D\xde\x9e\xf6\xe4W\xf5\xff3QR\x08\xf0\xbb\x90\x87u\x84_\xb1ZY7\xd8\xdc\x8ae}\x07\xebz\xb1]\xb9\xb7\xfb\xfdK}\xf7\xd37\xaa\xa0\x17\xa6a\xcf\x97\xeb+\xab\xe5L9gw\xd2>\xbe\xdb\xfd\xb1#]\xb9\xceC\x0d[\xc9/u\x15\x9b/\xab\xe5\xee\x0b\xd4[M\x88\x06\xe2L\x15XS\xd6S\xb9\xc1A\x9b~`\xd5\x92\xdf\x9a\xfb\xaa\x8a\x07U\xfc\xb8\xe2zwk\x9c\xaf\x86\x07#\x9c\xa5O\xc8\xea\xaduY\xcdt\xb9\xcf\x97\xf3Z\xb0\xaa\x0e\xf2Y\xd1\x1b*\xacX\xd7\xdb\xea\xa9\x10\x8f\xd3\xba\xb4\xa1\x0e^\x7ff\x95^\xa1S\xcd1\x00\xba\x98#\x14\x95\xae\\h1\xf8\xfd\x87\xab\xb37rZ\xad\x8e\xedhPq\xfay\xc5\xf5\x9b\xbb[\xb6m\x83\xd1 \xfdZW_4\xfe\x8b\xb6\xe5\xb2*\xf8\xb6am7\xf0\x8ao\xb8e\xbd\xac\xe5;\xd3=\xf5\x1d8\xe9\xc7\xb2*\xd7\xdbu\xb7\xf3\xbb\xa4\xb7z\x86\x83\xd7\xc0*\xf1B\x08>X\xc2\xd6\xc5\xc3\xac{f\xb2=\xdd\xde{\xf8c\xf1 \xeb\xad.%\xab\xfdV\xb8LL\x8d\xc4\x83\xd9?\x90\niq\xbd{m;\xafJ^\x16+\xbd@\x0ec\xfc\xa1\xb3u]\xf1\xdb\x9d\xc5u\xb9g:n\\\xb1\x0fE\x8c*\xf9wc\xff\xc7\xb6n|\xf9O\xc7\xff\xe2:\xf2\x8e\xf1z\xf6\xb4\x8dS+\x03\xf5\x0d\xfcd\x08>\xf9\xc0]\xc9\x1b\xac\xfe)\xe7R\x01\x08\xc0\xf6\x81\xff\xbe\x8a\xb6\xb1\xc5\xf1\xd8\x13\xdf\x9c\xfc9\x19\x14S\xb3\xb9\x14TLO$ \x16#X\xcc\xf5{\xa6\xa9\x08\xc1b\x04\x8b\xb9\x8d`1i\x04\x8b\xed\x1a\xc1b\x04\x8b\xf9\x8c`1\x82\xc5\xa4\x11,F\xb0\x18\xc1b\x04\x8b)#X\x8c`1\x82\xc5\x08\x16\xf3\x19\xc1b\x04\x8b\x11,F\xb0\x98e9\xc0\x1d\x82\xc5\xa4\x11,\xf6\x87\x80\xc5zB\xc9*'\xf4%i\x9d\xd1\x05\x83\xd5m\xb3\xc2\xc1\xbc\xd6\xbb\x17\xde\xd4\xcd\x91\xd1\x8fTR\x8f\x83\xc2\xbeRA\xfa\xaf\x8e\x86\xee\xfdJ\xc6\x90\xc5\x0f\xe2C\xeb+\x1d\x16\xff\xea H\xae1\xc8e 2u\xac\x13\xde2\x87\x0c\xf9\xad\xee\xaf\x9d:\x9b\xd2\x90iy\xc1\xb7m\x00\xe1\xd2'\xbe0mz\xa6\x10\xd7\xc83\xb6\xf1/\x10\xfe:\xf4N\xfdz\x90\x88\x80I\xcfc\xa7q\x1e \x05\xf6GE\x11'@.+\xf5\x96\x1a\x1c\x8c\x14\xe7^#\n|2IK\x0e\x14F\xca+x,\\\xa8,5h\x18kF\xc1o\xd1\xa1CeY\x02\x88\xca\x92\xc3\x88\x91\xf2d\x901!\x98\xa8l\xcf\x90\xa2\xb2\xb4\xc0b\xac\x1d*\xe4\x91\x14^T\x96\x1ad\x8c\x14'&/)\xa1FeI\x01\xc7X\x7f\xef\xc2\x91\xd8\xb0\xa3\xb2\xe4\xe0c\xec\xf9l1!He\xd9\x02\x91\xa6\xb8}\xc2\x91\xca&\x04%\x95e M*K\x0fP\xc6\x1e\x133\xef\x89?Q\x07 V*;T\xc8R\xd9\x01\x02\x97\xcaR\xc2\x97\xca\xd0A\xcc\xd8\xa3d\x858\x91\xa1Le\x19\x03\x9a\xca\xb0aMe\x8e\xef\xab\xf8{!5\xc4\x19\x1b\xd6T\x00\x14\x11\xe8T\xb6w\xb8SY0\xe8\xa9\x0c=\xd9C\x04@\x95\xa5\xcd\n\x93\x83\xa1\xb1^z]\xdf1DHT\x19\xbe\xae\x19\xc3\xa3\xca\xf0AReYC\xa5\xca\xa6\x04Lc\xbd\xad\x8d\x87M\x95M \x9e\x86\x8b\x13W\x8d\x85P\x95\xe5 \xa4*CF\x04\x95E\x83\xaa\xca\x12B\xab\xca\x82\xd1\x10iS\xc2\xac\xca\xe2e\x07\x96]\xb3\x05^\x95Mq6>\x08\xab,\xde\xde \x01Ye\x93\xc2\xb2\xca\x82>\xce\x17\xa2U\x86\x0c\xd4*\xc3\x84k\xbb#\x11A[e\xa8\xbb\x90\x1e\xc0U\x86\x0f\xe3*\xf3\x07s\x95e \xe9*K\n\xec*\xdb'\xbc\xab,\xee\xec\x84P\xaf\xb2\xec\x01_e\x88\x9a\x06\x9f\x94|!`e\xd1@\xb0\xb2)\xe1\xe0@qF~8\x14\x14V6%4\x1c(\x8e\x9b5+o\x80X\xd9\xa40q\xa0<\xf5\xb5\x1c[@@\x84\x8c\x95\xf9c[\xca\xfc\xe1ce\xe9A\xe4@a\xc1\xf0\xb29dB\x909P\x9e\x9am\x06\xd7\xf9\xf2\x05\x9c\x95\xc5\xc3\xce\xcaR\x83\xcf\xca\x12B\xd0\xca\x92\x03\xd1\x83\xd3\x90\xe1he\xe17w$L\xa8\x0c\x1b,\xc4\x06\xa8u\xa9\xa9aj}ZJ\xb0ZY\xd0\x05S\x02\xd7\x81\xe2\xac\xc00\xfe\x91\xc2\x05\xb1#\x8fK\xb5\x0c\x87\xb2\x95e\x0ch\xeb\x02Came\x81\xe0v\xe0\xac\xa9aoe\xf9zuB\x08\\\x17\x8c\x0e\x84+sOfTf\xdd\x9e\xf4\xe3-\xef\xbb\xed\xd9{6\xc8\xd7^\xdb\xd1\xf6\xf6\xfe\x9e\xc3\xef\x0b\xb9\x17\xfc\x11\x94\xbc54[\x0b\xdbJu\xe6\x85\x02s\xee\xcbv\xd8?0\xdaY\x06OG\xc9g\xb9J8\xe9\xc1\xfe\x8f\x17\xa7\xe3\x06\x90\xa0\x16 je{\xbb\xa52\xf3\xaa\xef\x92\xa0\x16\x82\x8aw\x05B\x93y\xf8d\x12\x9e\x04\xb5\x92Yw\x12\xd4\xb2\x0dA\xb3g\xe3\xd8\xf7#\xd8'\xb0\xebY\xa8\xf5t^\x9d\x04\xb5\xf6\xe1\xd2S\x88t4\x8bN\x82Z$\xa8\x85\x9e%%3\xe4$\xa8\x85\xa2\xc4\xa7\xf0\xe1$\xa8\xe5;,\xca~'P\xdf\x18\xb9\xa8\x14\xd2\x9b\x04\xb5HP\x0b\xc3f\x93\xa0\x96\xb4}\x88k\x12\xd4r\x95\x14\xe5\xa8\xa7\x10\xd4$\xa8e\x1b\x82\x8e&A\xad |s\x9clNe\x9a\x13h\xe6d\x8e9\x8d`&A\xad4.\x99\x04\xb5:#A-m;\xbb/*J\xcd*+\x14\xcd\x1c\x9d5\x08\xe6\xf6\xb8\x1b\x1fnk8\xac\xe7\x93\xe2\xa5_\x0e)u\"\x9a_\x08\x1dE\xd6\xe5\x90\x88\xa8\x13\x0b}z\x144\x84\x7f>-\xf2\xb9\x8by\x96\xd5\x1b\x15\xd6\xb7\xfe\xd6K\xc5\xdd\x14\xab6\xa2\x15\x07N\xca\x1fO\xf6\xc7\x1e\x96\xc8\xc1~\x82\x1fE\xed#I}\x04\x9d\x9fD\xe4?\xcd^\xae{\xdeWSI=\xd0\xd4\xde\x8av\x07\x8c\xc6J\xb5]\x9f\xae.k\xe1\xa6\xa9\xd7OR\xe3\x9e\xf09\xfe\xcc\x1e}\xd5\x1e\xbde4$S\xe8\x99z\xc3\xf8\xb6\xa9d\xecBs\x1b\x9aG\xe8\x88\x1a\x19qX\x8e\x96\xe6;\xcc\x98G(\x99\x0f\xe2\xbbJ\x89>B}s\xd32\x0eu\x03\xc3\xea\x82\x151m\x19\xcf\xec-\xcf\xfa\xb3\xc3\x89\xaa~>?\x8e\xd6~uc\xa4+e\xceH97\x7f\x93c\x92\xd6\xbbT\x8b\xef\xb7\xac2\x8e\xdfV]\xbcc\xf4\xf6>\x97\xa5\xadD\x9f\xef\\\xa8\"\x04\xdbV\xb8\xfa3K\xf4\xe7\xb0\xf8\x03;\xd7\xb3\x8d\xb3\xe5\xdeU\xb9.\xb1\xde\x95\xc7\x1a\\\xc7\x87\"\xa9X\x98\xdd\x83\xd5'\xa7\xf8uP\xdaF\xad|\xdb\x7f:\xbf\x81\x15\xbb\xe1\x06\xdd\xd2,\x97\x99\xf8\xc80\x9ez@\xd4E\x84\x9f\xaf\x1f\x81\x15\xf3[(6\x9b/\xe8E\x1b\xa8\xea\xcf\x0f\xf9\xd2:CxT\xf6\xd0Z\xca\xa3\x82\xf8\x8f\xb2Z\x94\xf3\x82\xb3.\xa0o2\x12\xc4\x81\x0eQ\xbf\xb2\x9a\xaf\xb6\x8b\xd1\xcaC\xa1\xae\xd2\x11\x15\xa3;&\xf9\x1c+\xd0'\xb7\x93\xb4\xf9\xc4Aa\x9f\xce\xc7\x13\xdbQ\x13\xe4bM\xc3ZMR\xc9\xc7\xab\x7f\x1e\xc5#w\xac\x9f\xa6rY\xd5\xcd(Lj\x9e\xc6\xe1%\x94g\xf6\xbd\xb1\xd7u\xbdb\x16\xe5\xec\xb8\x81\x0d\xbbc\xcd\xe0\xd4\xd0\xcd\xd3G\x8fo\\i\x01y\x0ds? \x83r\xc45X%\x01\x92\xbaY\xb0f\x1c'\xb9,\xab9{\x03J\xf9\xf6u\xbb\xf8\x0c\xfft\xfc\xcf\x7f\xce\xea\x0d\xf4\xc6\xe9\xe6\xb5y\xf2k\xf7IT.\x82{\xa7\x9b\xd9\xa0Q\xdf\xdd\xf4|\xae\xa4\xc9z!:s\xe8\xf9;s\xab\x7f\xfb\xf2\xbb>Vn\x12\xcf\xeb\x13\xd1\x8d\xc6y\xa3\xf0iX>\x97G\x122\x9ebKp\x8e\x10\xcd\x8d\xfaA\x19&ll,\x15\xfd\x0b\x16\xc6'\x88\xe5fE\x00!\x8e\x01\xc2\x04\x140\xdc\x80T\x91\\\xd7*\x7f2\x12\x08\x13\xb1\xc0`\x81\xc9\xf2\xb8{\xe2\x81\x90\x8c\x08\x06\x8b\x9a*\x8c\x9b\x15\x15\x84D\\\x10R\x91\xc1p\xcf\x9e$\x88\x9b\x15\x1d\x04\x1c>\x089\x11B\xd8\x1b#\x84i(!\xe4\xc2 a\x12R\x18~\x1c\xf0\x02\xb8\x07@\x0b\xe1\x80x!\x1c\x061\x84D\xcc\x10\xa6\xa1\x86\xb1!\x98\xa3pC\xc8\x8b\x1cB\x02v\x08\xe9\xe8!L\xc0\x0f\x11C&V\xeavo\x0c\x11b(\"\xe0\xa7g\x08$\x11\x12gq\xc9hb\xb0\xb4\x14q[l-3b\x8a\x90\x84*Bn\\\x11&\"\x8b\xe1~\x85\x14\xb4\x9d\x88.z\xcb\xe3H1\xdb<\x08#\xe0I<\xc0\xa0\x8c\x90\x863B\x8c?\x9a\x885\x02\xa2\xdc\x00\xe2\x90 q\x84I\xce\xc5\xa3\x8e\x80h\xe5\x04\xe4\x11\xa6b\x8f\x10Q|\xcb\x86?\x02\x1e\x81\x04$\x06 h\x14\x12p^OG\"! \x8b\x84\xa8=\x0b\xdc\x1bN\x14\xf6i\xf9\xe0\xde\xfc\x82\xb0X9X\xdc\xe0\x91O\n\xb6\x1b>\x82B\xb0\x892\xb0\xbd\xdc\xab\xa7\xbc\xa8\x08\xac\xb9J\xaa\x04,\x8f\x91\x1bqn\x03!\xfe\x1a}\x0b\xa3\x85_\x11%\xc5E_q\x85\xa0\x05_#\xc5E\xc5^\xa3\xd5\xc1HA\xe2d^\xb3\\\n!\xf0\xca\x9f\x01M\x14\x95u\x8d:\x03P\x92\xae\x88bp\x83T\xb2\x98+B\xb25\xaf`+R\xaeu\x8aX+R\xaa5\xealL\x07F\x89\xb4f\xb9RX\x9e5z \\\xbf\xc9&\xcc\xdaO\x8d\xc7\xbf\xc4+\x92O\x92\xd5]\x8b\x04\xbdI\x94\xdc\xe4H`rT\xde\xc7\x8bS\x92\x97\x04\x92\x97\xcc6\xb2\xa72\xa6&0\xef-\x10\xcb\x97feKI^\x92\xe4%{\xcb\xca\x8c\xa6\xf0\xa2I\xac(\xc9K\xee\xcb\x85N`B\xb3\xf0\xa0\xe9,(\xc9K\xee\xc3~\xa6p\x9f\x13\x98O\x92\x97$yI\x92\x97\xc42\x9bYy\xcd)\xac&\xc9K\xfa\x0e\x8b2\x99 <&F<1\x85\xc3$yI\x92\x97\xc40\x95$/)m\x1fn\x92\xe4%]%E\xd9\xc8\xa9\\\xa4\xf7\xdd@\xf2\x92\xbbF\xf2\x92\x13x\xc68\xcb\x98\xca1&0\x8c\xc9\xfcb\x1a\xbbH\xf2\x92i|\"\xc9KvF\xf2\x92\xdav\xe4%K{\xe4\x1e|IZ\x87\x0c\x02bj\xb5\x1f\xca\x05\x8c\xa4$\xed\x11\xa9\x94\x9b\n\x0e\x86\xc1^\xe9\x857\xdb]\xa1\x17\x9c\x9e\xd1\x9e\x020':\xe2\xdf\x06\x94`\xde\xe9C:%\x98B\xaa:\xe9?\xca\xd8_[V\xcb\xd5n\xc3we`\xde\x0dH\xb5g\xab\x02cZ\xf7#\xd2\x8eH;iD\xda\x11iG\xa4\x1d\x91v\xca\x88\xb4#\xd2\x8eH;\"\xed|F\xa4\x1d\x91vD\xda\x11igY\x0e\xea\x89H;iD\xda\x11i\xf7[ \xed\xba\xc0\xb3\xaf\xfe\xdd\x01\xa3\xad%U\x0cPG\xf6X\x0b7M\xbd\x1e\xb4\xa3\xcd\xd8\x90}\x01\x0b\xb9\xbb^\x80\xa8\xb8\x12\xbf\x7fTp\x90A)\xe4[B\xae\x9e\x0f7\x14\xbc\xab9\xf3c\x12VI/L;\x9f)*!\x1b\xb7;\x91\x1f\xd4E9\xc0\xbe\xf3\x8d\x12R\x11\x93%\xf1\xdb\xee\xfb\xd3[\x13\x88\xc6\xe1\"\x1b\xf9EV\xa0\x10\x1b\xf8EJ\x08o\xdc\x17?\x19\xb5a\x9f\xb7\x18DT\xdb\xea])\x91\xed+G\xc8\x83\xe2\xda\xda(\xaeMq\xed\xde(\xaeMq\xed\xde(\xae\xcd)\xae\xed6\x8ak\x1b\xa3\xb86\xc5\xb5)\xae\x8d\x9c%Q\\\xbb3\x8ak\xdbFqm\x8ak;\x8c\xe2\xda\xcec(\xaeMqm\x8fQ\\\x9b\xe2\xda\x14\xd7\xa6\xb8\xb6e9b\x8c\x14\xd7\x96Fqm\x8ak?\xdf\xb8\xf6\xbeq\xe2\xbb\x9a\xb3\xd0\xeeR?\x89\xdf\xbb\x08\xb1\xf9\xfa\xe0\xa01j\xa2f\xf6\xef\x12\xe5\xa9)\x9c\x1a\x0fw\x82F\xb4\xd3\x9d\xb4E@k\xb17\xda\xe9\x0ep3\xcc?\xd2Nw\xf23'\x85PT\x1fV\x1f/NG\xa5\x11\xa1H\x84b\xb6\xf7(\x11\x8aD(\xba\x8d\x08EiD(\xee\x1a\x11\x8aD(\xfa\x8c\x08E\"\x14\xa5\x11\xa1H\x84\"\x11\x8aD(*#B\x91\x08E\"\x14\x89P\xf4\x19\x11\x8aD(\x12\xa1H\x84\xa2e9h1\"\x14\xa5\x11\xa1H\x84\xe2\xf3%\x14i\x8f\xbb\xa9\x1b\x88\xd1\x1ew\x07tn\xbc\x8f\xd2\x1ew9\xbcH{\xdc\xd1\x1ew\x7f\xcc=\xee$iu\xf2\xab\xe4\xbaB\x9b\xdb\xbd\x94t\x98\x8d\xd8/\x1c\xdb\xd8\xd5=l\x7f\xfe\xeeH\xd1br\xff\xba\x97\xba<7z\xff\xc2\xb4\xfc\x19\x93\xf7>,c\x12#\x16\xe5\xe7\xa3\xb1\x85(\xd9\x14 \xe7#\x85\x07\xa9y\x1ea\xe6\x9fb\xdf\xba8+\x1fu\x9f\xb28'?\x8d\x92Oc\xe4\xd3 \xf9$>~\x12\x1d\xbf\x07\x1b\x8f \x1b\x19\xcb\xcc\xc5O\xa6\xe2\x9f\x94\x89? \x11\xffT<\xfc\xa1h\xf8'g\xe1\xe3$#\x1c\x97p\\i\x84\xe3\x12\x8eK8.\xe1\xb8\xca\x08\xc7%\x1c\x97p\\\xc2q}F8.\xe1\xb8\x84\xe3\x12\x8ekY\x0e4\x92p\\i\x84\xe3\x12\x8e\xfb[\xc0qe\xf8\xcdWw\xf9\xe3\xa0\xd6\xea/z\xfb\xcb.x\xb6y\xc2\xbd/\x0dw\xb5.+~r\xf7\xcd5\xe3\xc57'EUm\x8b\xd5LNU\xda\x1e\xaeq\x81Vo\xe5\xa1\x17\xdd\x91f}\nD\x81b\x18UeA_\x96z\xd5\x99\xa6\x0d\x08\xabqa/L;\x9f)h\xe5\xf1\x93m\xc1e\x9e\xe0\x02O|\x01e\xe7\xf2&&\x1b\xbd\x07\x8e\xc2\x06wEYX\xbaK\xf6\xa0\xf1\x1dCE\x87]\x05\x9d\xect$\xdaZR\x1bE\x8aC\xcb\xa4\x07XdM\x1a+\xcb\xeafe\xa9\xfc\xb9\xc6\xc8ss\x88\xa6s\xdd\x0fiWP`\x80\xecJ\xd2?>\xdb\x91q\xe4\x15\xdb2\x8d\x88\xbd\xbb<\x83\x9e\xc7\x9f\x8e\xa2\xdc\xb0K\xe7\xeb\xe9cZ\x7f\xe3?^\x9c\x8e\xe7l4\xb6\xd1\xd8\xf6\xcc\xc769W\x0fL\xfe.\xe4\xef\x83QM\xe5g\xc8\x94\xa4\x9b\xeeQ\xec\xe7\xfc\xceQ\xed\xc7\xb2\xe2\xaa(\xfd\xeb\xb3\x1d\xd6l\x87\xd86\xfcDQ^\x19j\xb9\x1a\x07\x98\xcf\x93u\xbd\xd8\xae\xf2*\xb9\no\xcf\x16\xac\xaa=\x90x\xb4\xc3hmQ\xae#a\xf3\xba\x94\xdf\xb6\xa2\\\xc7\xf1\xdd\xf8:k\n\xcef*\xb4\xbd\xdf\x95\xd7\xc5C\xb9\xde\xae\xcd|U\x15)>\xb5\xfb\xb1\\\\+X\x99u\xf1\x90\xa7\x12)\xd7,=x?\xfa\x9ae\x85\xbb\xe6\xb2.V\xb3\xeb\xbaZ\xb0\xa9\x99\x18\xfa\x8a\xa2 q\x937\xac\x99\x8b\xb7\xa6*\x13\n^\xaf]_\x07\xd7\xabz\xfe\xb9\x9dmX3{d\xc5\xb4D\x0dD\x16HW\xbd\xee=\xa7.,\xaa \xe2\xc2\x83s\x10/q5\xac\xa0\xde\xe0\xfa\x9d\xad\xc74\xa2V\xe9}\xed\xfa\xfd\xb9\xbd\xaf\xd5\xcb\x06\xfd\xc6\xb6\xbe\xe2uG79rE\x8f\xb5to+\xc5\x98\xabW\xd5\x91NW)y\x0b\xed\xf6\xba\xdd\x14\x125\xeb\x17\xea>\xb3G\xe7\xdb\xfd7\xf4f\xc7\xbc\xd8\x07\xefu#\x00\xdfy,\xeb\x0b\xdd\xb8y\xd2h;U\xd2=\xd0\x87\xbdgN\x1a\x88i\x10\x06\x1a\x84\xdd\xa7?\xb7A8\x14\xb30\x0f\xa9\xaf\x07vc\xa5=n\xa8!UL\xae\xfb\xbd\n\xfaQ\xf7f\xc8\xeeN\xc9\xba\x1e\xb5\xd0T6 \x11\xf2\x99=\x0e\xaa(\xfemB)]\xcdt(\xde\xb4)k-\x13\xdft\xa6\x0e\x81\x97\x9d5\x12]\x9a\xa3\xbb\xf7\x9dL\xcf\\\x89w\xca\xb2l9k\xd8\xa2kV+\xa3\x95\xe2\xc7\xcf\xecq\x9c\xc89n\xfb\xc0\x89/\x9d)\xf4\xff\xeaL'\xef\xaa\xa4\x7f|\xb6\xaf\xc6\x91\xa7m\xfb\x12\x03D\xf8\xad\x88x\xc4\xc5M\x0d\x9f\xbc\xf7\x1ek\xde\xab\xc7\x83:}_\xb5\xb2\x05\xfb'\xb0\x1bN\x14V\xa3\xfb\xa9~`\xddL\xa9\x8c\x95\xb3\x87\xb2\xe52O\xc4s\x8c\xfdT\xbb\x8fq\xf7\xed\xc1\xa1\x88\x89@\xd7\xed\xbb\xb9\x80=\xea\x0c&\x05\xea\xd1\xebd\x86v\x93\\\x8a\xd5\x0e\xd3\x8e|\x9c\x03\x8d\x8d4\x93f\x1c\xa3_i\xc6q\x80\x19GT'\xe5\x1f\xdb\xa2)*^V\xac\x8f\xda\xcfyy\xc7N~\xe5\xf5L\x13\x05!\x85\x94\xf3\xf6\x7fuE,`~\xcb\xe6\x9f[\x05\xa8B1WB>\xb7E\x0b\xf5\x86\x9b\x04\x9d\xfe\x92\xee\xd8\x94]\xa0>\xe0\xd9\xbe\xd3\xcav\xd67\xc7\xb1\x94\xa6\x8a\x1c+\xf9\x18\x8b\x8f\xe2\xc3\xf2\xe5\xba\x93\x96\xecQ\x8b\xe4\xe6\x0e\x8d|\xec(i\xd7\xeb\xca\x10C\xed\xe0\x8e8\x87\xdb\x8f\x17\xa7\xfd\x90+q\xc4Q9\xc3^\"\x87b\xfa\xf8\xa2\xa1\xf0)\x87\xc2\xd0\xc7W\xff \xf9\xfa\xa0\xf5\xa8\xe9\xc5_\xf3O^\xabQ\xef \xab\xe0\xa8\xbd\xe55v\xcc~\xbb\xe5\xb5y\x8e[q\x84n\xd1\x96\xd7\xaf\xbb\xc7\xb8e\x9c\x97\xd5\xd2Lt\xecaH\x0f\xec\xceO\x17k\x08\xb9\x1a\x0eQr\xadF9\xe1\x18\xceo\xa0\x80\x9b\xa6^\xdb?wL\xbb\xa4\xc1M\x95^\xf4.\xd4US\x8b<\x05\x1f\x16\xa0\xb9\xcd\x17\xd6\xf1J\xc3\xecXg\xe9\xfa.w$'u\xfe\xd6w\x05\x8a\x1a\xa9\xd5K\xabY\x06\x17\xed\xae\xa6\x0f\x1f\x12i\xb6\xc7_\x0c\x9b\xf3\xec^j\xc2\x133G\xed\x8c}\x91\x11\xa5s\xf8\x1e\xa3B\xe4\x89n\xd8\x9c\x95w\x12tS?\xec\xae\xc2\n\xb3\xbbQ\xae\xca\x8c\xbb\xa6\xfc\x8e\xd2\x1a{\xc1\xca\x98\xdb\xe4\xabH|\x8a\xa1\xac\x19\x85\xb6\x9cO\x83Y]\x15\xdfU\xf7\xbe]3\xc1Txw+Sc\x08\xef\x845\xca^\xc3\xdbOW\x1ff\x1f\xcf./>\xbc\xbf\xfc\xdd\xd9\xe9\x0f\xe7\xef\xcf\xbcn\xd6\xc2a)\xf5\xc2\xdc\x1b{\xe08\xabx3\\\\\x13w\xc9\xfa\xd6\xad\xa5\xa4\xc2\xee\x9b\xac7\xd1\xcf\xa0\xa8d\xf6\xf3\xee\xdd\x89Wh84(\xbd\xc8\x9d\xde\xc2*\xae\x96\xc6\xc4\xc54\x88,\x07ZG\x81\xa3\xa9\xa0\xb2\x8c[\xde\xee\x12\x13\xae\x81SYp\x88\n\x0fP\xb4\xfb-\xed~;\xb2\xe7\xb2\xfb\xed`\xea\x81\xfbz\x1b\x153\x9c.\xd2\xc7\x1b}\xbc\xfd\xe6?\xde\x1c_3bp\\2.\xe5}a(j\xb7\xe7\xe7\\_U{\x9a\xe7\xab\xecx*h $\xc9)\xa1\x95\xe8\xc3k-n\xee\x18I\xf7\x8e\xa39\x05\xa7io\x00\xf0\xf7a\xda\x1b\x00ho\x00\xda\x1b\x80\xf6\x06PF{\x03<\xd3\xbd\x010\xab\xa7'\xbf\xda\xefaZL}\xb6\x8b\xa9f\xfaM\xab\xa9\x96E\xa7\xf8\xb4\x9a\xea,\x87VS\xe3\xab\x96\xeesh5u\xe7\x88x\x85h5\x95VSi5\x95VSi5\x95VSi5\xf5\xb9\xaf\xa6f\xa93\xad\xa5\xa6-T\xd1Z\xea\x01\x9d\x1b_\x05\xa4\xb5\xd4\x1c^\xa4\xb5TZK\xfd}\xad\xa5\x06\x96Ro\xb6\xd5\xa2\xf5/\x97Z\xbe\xb2\x90\xf0\xef\xc5Ij\xf1\xd4\xde^\xb5\xb8\xae\xb7\x1cd\x89\xaa\xab\xcb}?\xae\x19\xeb\xef\x965\xf7\x88-\xa1\x9e\xdf\xc0u\xcdo\xa1\xb0\xa70E\xb5\x18N\x12\xc4\xbd\xb1\x163\xabGh\xb7\xf3[\xfb:]\x81\xaab\xe35Kh\xd8\xb2h\x16\xf2\xa5P\xdf\xf4\xb7\xec\xfe\x96\xe9M'\xd8\xe3K\xdd\x0cX\xb0\xf9\xaa4\xcb\xaa\xf2\x19)F\x13\xac\xbe.v\xff\xd8Vf\xf3!-\xc2m\n2\x95*J\xb9\xc6$\x9ch/\xe2\x9aE_\xb9\x98\x10Xw\x95\xab\xbc\xac\x94\x15\x1e\xd4\xa8\xb2\x96p\xc3k\xc0U]\xbd6\xb5z\xe1\xba_\xba\xaarI\xbcz\x04\x7f]\xae\xe4\x00'?\xfe\xa0\xec\xa7ze\xa56\xa1(;\xcf\xf9*\xe4\\F\x1ew\xbf\x17C\x1f=\xbb\x85\xe4\x7f\x8c\xea\xfbL\xbeo\xb2/%\x07\xd6Uw?=\xc47\xa5T\x9ci\xd8\xbc\xdc\x94\xd6\x06f\xf3Z\x89J\x07vK\xebF\x13\xd7(2\xb4\xfey\x9b\xd9\x9d\xcc\xe7\x95\xf0\xbdP\xb6_\x06($\xb8\xcd[\xf9n\xc9Q}\n\xd9\x83lU\xfb\xfd&]\xb6)\x1a\xae\xa5\xc7U\xe9\xf0\xc8\xb8\xb5\xa6-\xfd\xefv\xa6\xfc\xe9\xc0\x8e\xf3vhe\xb1n\xad,\xa0\x82e\x0cq\x9b\x84\x15k1\x1d\xcaP\x14\xf6\x9e\x03\x9c\xd6\xa5\xbd\xd3*\xaf?\xb3J\xefh\xa3\x1af\xa6r2\xad\xd5\xbf\xf5\x93\xa9|h[%\x80\xf7\x1f\xae\xce\xde\xc8\x91Z\x1d\xad\xb5\xde\xd5'\xf7y\xc5\xb5\nv\xb7\x05R\xbb3\xdd\x19\x9a\x16\xc9VB\x1d\xa1\x0b\xb7\xe5\xb2*\xf8\xb6a}DTL\xfb\x97\xf5\xb2\x96\x1a\xd4\xbe\xb0 \xd6\x91j\x1c1\x81\x1c\xd5\xb6~\xf3\xac\xb2\x1a\xbe\xd1\xb4\xfc\xc8\x1e\x01\x1d\xf3\xca\x0c?\x1f\xbe\x94Be\xd8\xb6u\x93\x86ab\xa1\x98 \xa8Y\x04\x93\x93!vW\xd6\xdb6\xb0=_7\x8bq\x1c\x81\xa9\xcb\xceL\xd0t\xdb\x967\xdb\xb9\xb8\xb9#\xa5\xf6V\xdd\x16ga\xc3\x89bxh\x1fTn\xfc\x82\x95\x1f#\xd5M\xad\xa7\xa1\xaa#\xb4%\xd7\xb2\xa0\xde\x94J\n\xb9\xec\x1a\x85\\\xfa\x9f\xb0\xce\xa4\x90\x8b\xfc\xdc\x1c\x0f\x0e\x93\xa2.;#\x0c\x05^(\xf0\xf2\x1b\x0b\xbc\x8c\x1e\x8fi\x1fC\x8e%\x14p/\xa3\xc0\xc4\x15\xa4=\xa25\xa3\x16\xfa8\x9e~wI\xf5\x91\x01\xe77\xf6\xda\x83\xdb\x95z\xbf\xeab\xd5\xea-\\\x87K\x03\x90\xb7\xb5\x14\xe7\xc1x\x8b\xe2<\xca\x0e\xec\xdcx\x84\x82\xe2<9\xbcHq\x1e\x8a\xf3\xfc\xbe\xe2@q\x1fm\x14\xf71Fq\x9f\xa1Q\xdc\xa7\xb7\xd8\x0c\x8e\xe2>\x99\xe2>Y\xf2](\xea#\x8d\xa2>\x14\xf5\xa1\xa8Oo\x14\xf5\xa1\xa8Oo\x14\xf5\xf9-G}\xf0RI\x14\x04\xa2 \xd0\xf3\x0c\x02\x99\xe7\x8a\x82@\x03\x8b~kQ\x10\xc8i\x99b\x19Y\xdcFA\xa0\x1d\x8buke\x14\x04\x92FA \n\x02Q\x10\x88\x82@\xf1\xb8\x05\x05\x81\xcc\xc1\x14\x04\xa2 \x90\xb6 s}\n\x02E\xe7q\xb1\x19\x1c\x05\x81(\x08d\x17\x87\n\x02ei+\x85\x80\xd2\xd6\xd7)\x04t@\xe7\xc6\x83\x17\x14\x02\xca\xe1E\n\x01Q\x08\xe8\xf7\x15\x022\x11\xa0vU\xb4\xb7e\xb5\xec\xe2?r\x16\xa3Oq\xc5x.\xe4\xef\xb2Ve\xf7Ak\x7f\xc5\x9a\x12a]/\xb6+S\xbf\xc1\xea\xfc\xa5>D\x95\xf5\xc2\xb4\xeb\x99.\xcd\xdb\x1e\xb1m\x8f\x0f\xf2\xb6\\Vl1\xbb^\xd5\xf3\xcf\xed\xec\xbe\xac\x16\xf5\xfd\x9e\xdf\x93\xbe\xcf\xc9uY\xcd\xf4\xe56\xac\xc9s-\xcfB\xc0\xa2\xbe\xafx\xb9f\xb3\xbf\x17\xe5j\xb6\xd86\x9eE\x12\x88_Kv\xa2\xd9MS\xccE\x11\xb3E\xbd\xbd^1\xd9\x8eI\xc5E\xab\xbes=\xd5\x92C\\,\xbed\xa6\x1f1k=l\xf4\x94\xc9\xf9\x8d\x18\xce\xaf\xd5\x12\x88y\xe4\\\xb7_>\x84\xc3E\x0c\xdf\xfa\x82\x1c\x1b\xd4\xc5\xbb\xcf\xf0n\xb3\x0f\xfdo\xd1\xf6n!T\x9ep\xa2\xab\xfb\xf1\xe2tT\x9e^\xde5\xff\xa4\xaf\xf0\x1d\xff\xd0Wx\xf6\xaf\xf0\xe4\x17\x9e\x18T\xcaj9+\xab\x9b:\xf0\xde\xbbT\x87\x9d\x8b\xa3\xba\xb7\x9f>W\xab\xa0\xdf\xc8\xd0\xb8\x8c[\x17\xbcn\xcc\x8bm\xf8\xea\xb3\x8a\xd1\xbf?\xdb\x17\x9fh\xd5\xf3\xe8h\xfb\x06\xa1[^4|v\xcb\xca\xe5\xad7L\x17-$\xfe\xae\x85\xc0\xe0\xda\xdb_e5\xa00Q\xb3\xae\xc3\xc8U\xd6\x9b\xb2i\xb9\x98\xda\x17\xd5B\xfc\x99\xc1\x87\x8f\xe2\x07oq\xdbJ\xbcn\x9d\xdb\xc5\x88[\xb8`\x0f35M?p\xc3\xe3\xef5e\xe7\xa2J&`\xd8\x8a/\x9bF\xc6\x0c\xc5\x98/\xbe\xf4\xc4{W\xbe]\x86~\x19\xaf\xf9\xf6v]W\x0b\x16\x88\"\x96\x15\x14 \xa7Z2\x18\xba.\x1e\xd52\x99\x9a\x15A\x01\x9b\x86\xcd\xeb\xb5\xf8\xf6\xad\x1b\xa8j~\x0cW\xb7\xa5\xdf\xe1e\x05\xf3\xba\xfa\xfb\xb6\x92s\x05\x15t\x153\x7f\xef \xbf\\\xca+}'g{?\xcb \xd8/\xeau.\x86s\xd6\xac\xbb\x85ny\xbb\\_\x12\x83\xe2~,\xdb\xd6\x14\xf7]\xc9\xdf\x8aG\xf1\x17w\xbcQu\x8d\xd9\xb6\xe2\xe5\xf4Q\xba\xbf\xff\xa2?\xbe\x167h\xcf>pU\xaeY\xcb\x8b\xf5\x06d\xcdto\x18\xde\xf4\xb2\xd5\xb5\x87\x85\\\x11\xf0\x16\xb6*\xefX\xc5\xda\xb6\x9b\x7f\xba]\xc1\xeb\xf5u\xcb\xeb'\n\xbe\xfe\xac\x810\xd5\xa3\xd4\xc2\x86n\xd8m\xd1\xaa\xe0e_#x\xf5\xb9\\\xf9\x9eaa\xf5V~\xe6\xf7\x85\xb4\x8c\x7fm\xbe\xb7[\xc6\xfd\x9d\xaf\xae\xe6\xe3\xc7Iuv\x19\xbb\x17\xdf\x92w\xf5\\\xad\x08\xd4\x8d\xe1\xb5\xfc\xa5\xc9F\xcd\xeb\xea\xa6\\n\x1b\xb6\x80u\xd9^\xb3\xdb\xb2\xb8\xab\x1d;d\x81\xfc\xfe\x10\x9d\xd5|\xea\xc85\x05\xe6\x98v)K\xe8\x8a9\x86\xa2\xb7\xa0\xeb\x03\x9f\xd9F-@\xdd\xd5\xe5\x02\xb6U\xc5\xc4\xfb\xb5h\x1e\xd5\x8b\x0e\x1aV,\xc6\xeb#\xb6\xbd\xaf\xcdZ\xce/\x97\xdb\xf5+\xd7\x13\xfa\xf5/P\xac\xee\x8b\xc7V8\xbdX\xf9\xc7\x97\xc1\xf3}\xaa*\xe8|\xbc1\xcd\xfc\xc9\xdcuk\xe2aq#]\xa7x9\x9a\xcc\xd8;9\xda\xb6\xae\xab\x92\xd7\x8d^\x85.\x1b\xb7K\xbaGR|O\xdd\x95|g\xcf\xb2\xee=)/f\xd6\xff1\xb3)c\xa1(\xfc\x1e_\xe6\x14*\xa7P\xf9\xc8\x9e$T\x0e\xa8\xe7\xd9\x0e\x16\xa9V\xa9\xfbu\xcd\x16\x0b\xb5\x04\xb9\x1c\xc4\xca\xf5\x17f\x0b\xf7\xe2\x9e\xb9n\x8f{\xde2\xaf\x1bU\x86\\\xbb4x\xae.M\xbe\xc0\xe4\x12\x84\xed\x19\xa7;\xcc\x19\x97\xf5\xba\xaf\xf7\xaf\xde\x91O~\x0cmX!\xe6\x82\xdf\x15Mw\x93\xbe\x85o\xfe{\xe8\xa4\x81[d\xff\xfe\x16\xfe\xe4<\xe3?\x07\x7f\x0c.\x84\xd8_j\xa8\xe5\x10W!'\x83\xcf\xc6\x8f\x17\xa7c7\xd1\x1a \xad\x91<\xf9\x1aI\x88T\xa0\xf8vZ\xf0\x90\xe2\xdb\x07tn<2K\xf1\xed\x1c^\xa4\xf86\xc5\xb7\x7f_\xf1\xed\x97\xb8\xf5\xfe\x93_\xe7u\xd5br\x1b\xed\xefg;\xf8=\xfeh]\x8aO_\x10\xa5\x8e\xb6\x16\xf7\x85\x01^\x98f?\xd3(\xc0]\xb1\x9a\xd9>\xcb\xfa\xb9\x1d\\\xd8\x8fLn\xe2\x8b\xfa\xd1\xd9Q|1)\xf6\x81\x99u1?\xb0\x94\x1f_\xc8\xcf\xd0\xd8\xf8\x07(\xe4_\xc0\x0f/\xdfO\\\xbc\x1f\x8f\xfe\xbd\xa5,\xddg]\xb8\xc7/\xdb\xc7\x17\xed\xd1\xf7:\xbc`\x8f\xbb\xdf\x19\x17\xebQK\xf5\xb1\x85\xfa\xf82=\xae]\xd3\x96\xe8\xa1\xde\xfa\x12\x0f\xa7,\xd0\xe7\\\x9eO]\x9cOX\x9aGw\xb8}\x07\x97\\K\xf2\xf9\x16\xe4\x91\xcb\xf1\xf1\xe6\xe5]\x8a\xc7,\xc4\xe3\x97\xe1\x9d\x15\x1e\xbf\xf9}\xeb\xf4z}\x90-\xc4\x19r\xea\xe3(\xccE\xedc\xd7\xdf\xb2,\xbf\xd1\xea\x1b\xad\xbe9\x7f\x7fN\xabo\xf6\xd7\x88\xaf\x17\xda\xc7\x98'\xc2\xfc\x93\xd7:\xdc3zF\xad\xb2\xf6\xcc\x84\x89\x7fp\xf1\xe2\xb3\xfd\xbd\xb5`+\xb6\x94o\xb0\xf6\xe4W\xfd\x8f\xba\x91\x0d@j\xc9\xbc3'\xbd\xeb\x8b\xea\xbe\xc1\n\xb9\xc2\xd2\xff\xbd\xbe\x81B\x7f\x84u\x17\xebJ\xd2n2\xcf\xea\xe0\x93\xccu\x15}\xdc\xb3\xfd6\xeb\x1b>s\xd4\xd1\xd8\x97x\xf0\xfa\x9a\x85\x1f\x9e\xa0\x92C\xec\"\xd0_H\xf7\xa8\x00$\x06\xb8\x07\x16P\xefr\xfb\xd8\xd1\xf5\xcd#y\xcd\xe6\xb7\x7f\xfe\xd3kV\x89Ay\xd1=\xa1\xb5/\xcc\xa8L\x9c\xd9\x15\xe9&*@\x0dKj\xb2\xf0\x85Z\xbds\xfd\xfd[\xdd\x15\xe9ou{[4aM\x8f)MU\xa5\xea\x99\x98}\x03\xc4\xa4[\xff\xd8\xb09+\xef|\xfa1x\xdf\xf5c\xcb\x18\xef\x16\x9f\xc1\xfa\x83\xb4\xfe\xcc\xaa\x16n\xd9Jj]\x04\xc4;\x8a\xb9\x9c,\xeb\xcf\x8c\x00\x03t_)\xdd\x8c\xba\xb2z\xd7\x91\xfc\xa2.[(\xda\xb6\x9e\x972\xea\xda}\x11\xfb\x8a\xba\xab\xa5@\xc2\xa6\xbeW\x8b\xb0u\x15\x00\xdf\"\xb7\xf4\xbaX\x15\xd5<\xf2v\xcd0@Dd^P}&.\xf1\x82(\x06\xdbK\x12\xc5]\x10\x12.y\x05\\\x90\xf2-S\xc4[0.\xea\x1f\"\xfb\xc3@~+\x17+\x19\xdf\xaa\xed\x07\x8d=H\x01#\xafXT\xc9\xc5\x94\x8a\x17e\xd5\xdazj\xb6\xe9\x9e*W\xa3\x16\x8bR\x16\xcbk38tKa\x12\x86i\xb7%7\xf1\x08ga\xf3\x95\x0c\xc2u/\xec)\xdf\x92\xae\x17\x7f\xdfcV\xab\xd10\xd6\xbe\xec\xa1*\x7fi\xbb\xcfiF\xa9\x93>\x90F\xfa&\x04m\xfd>\xa0\xadp\xbf\x90_F\xae\xaf\n{\xd4\xc2/e8\xbf\x82>^\x9c\xeaa\xb8\xbf\x8f\xb4\x8a1\xfa\x95V10\xaf\x14eo\xe1\xd3\xc7\x1fN\x1a\xd6\xd6\xdbf\xce\xe4\xaa\x84ZF\xddV\xe5?\xb6l\xf5\x08\xe5\x82U\xbc\xbc1\xf1O\xd9o\x03\xd3{\xb9`\xc8\x9a\xb2X\x95\xff'\x94\xa5!g\x04\xf3z\x05\xd7\xdb\x9b\x1b\xd6\x98\x9b\xa6\xc39\xaamJ\xe6C\xbf\xac!\xa0\xfd\xb8bE\x1bd\xe2\x19|u\xf2\x15\xcco\x8b\xa6\x98s\xd6(\x19\xd2U\xd1rh\xd9rmi\xab|\xfa\xf8\xc3\xcbv\xbc^24Y\xa9n6\xef\xbf\xaa(\xeef\xbbZ=\xc2?\xb6\xc5Jxp\xa1\xfck\xf8\x06\xe1\xc9W\x85\x8c]y\x0b\xf9ET\xe5dY\xd7\xcb\x15;\x96>\xbb\xde\xde\x1c\xbf\xd3\xb9\xb6\xbf|\xadZ\"\x8b\xed\xf9'o0\x0cTl\xb2\xae\xcay\xb1\x92\xcf\x90\xff\xca\xaf\xd8\xf1\xf2\xf8H\xb8V\x06\xfc\xbf:\xfeJ\x8c_2r\xa2\xf5'\xbf\x0e\xcd@\xcf+\xd8\xc8\xf4\xda9;\x02\xce\x8au\x0b\xdbv[\x08w\xa8\x10\xde\xa6\\I\x91\x9bZ}\x0e\x95U\xd1\xf8\xf3\x10\xe4\x14\xe7q\xc3\xda\x0e0y\xf4_Z\x8dub\x9e\xc7k\xd8\xb6\xcc\xccEDG\x12/\xd7\xfa\x06\xdeV\x8f\xc7\xf0\xd7\xfa\x9e\xdd\xb1\xe6\xc8;s\x13\xf6\xe9\xe3\x0ff\xc6'\x8a\n&\xfe\xc8\x11\x94\xc1/\xb7\x9co~9R\xff\xdf\xfer\xa4\"N\xfa\xd7#\xd9\x1b\xe7E\x05\xb5|:\x85G\xfc\x052\x0e\xdb\x8d\xf8$x\xdc\x84\xae\xcb\x9a;)F\\pX\x17\x9bVu-Ys^wP\xae|\xb7\x95\xea=\x12\xc8/\xbb\xa9W\xab\xfa\xbe}\x13\xb8\xb7\xffU*\n\x9b\x16\xc1@-X7Z\x7fgn\xd7\xe2\x1b:P\xd0\xdb\n\xfezuu\x01\x7f9\xbb\x82\xba2\x8f\xa0z\xc6\x1e\xe57\x8b?\x11\xec\x7f\x8f\x1f\x8b\xab\xc7\x0d\xfb\xf7\xff\xfd\xef\xde\x13\xc0\xbc\xea+\xdd\xdf\xf4kD\xde\xa1MS/\xb6s&\xc3p\xe2\x15\xe6_\x96\xf8\xaf\xf0v\xb3Y\x95s\xfdN\x16\x93\xacB\xf8LM\xf9\xe6\xc5\\\x8c-u\xfdy\xbb\xe9f0\xd7E\x1b\xca\xf6 '\x80\xc9N(\xeb(C\xe1\xfc\x96\xad\xadgh\xa1\x1e\xa2\xc24\xa9\x0b\xa1\x85\xf2z@WP\x0e\x1f\x0d\xbb\xa9\x1bvd\n\x10\xe5\x16\xbc\xbc.W%\x7f\x84\x8a\xb1\x85\xa1\xe4\xe4\x90\xd7\xdc\x05Z\"\xdb2\xbf-\xaa%\x93'\xc9g\xf6\x18^}j\x19\xdc\xb1\xa6-ehUvO1f\xa9\xfeYT\xc52\xd4\xfa\xeb\x86\xc9%fS\xf0\xf1\xd7\x81\x0f\xdf\x9a\xb37\xc0\xc5;\xe4F\x87\xfd\x0b\xd9\x0e=v\xf5b\x9f\xd6\xa4\xd2?\\\x8a\xfeX\xcb\x19\xb8\x7f.\xa9\xdfe\xd7[1a\x15o\"\xa6WZ\xb8\xb9h\x97\xbf\xdf=\x97\xde\xa2$%+W\xf2\xc5\xf7\x7f\xe0\xe5\xf2\xb8a\xc7\xaa\xff\x17\x9b\xb2=\x9e\xd7\xeb\xd0h|)\x9f\xd4V\x87\x8c%\x0f:\x1a\xa5\xe0\x95N\xf2W\x1f\x16\xea\xd1\xfe\xda\xff\x12\x94\x18\xccu`PR\xd0j\xc9\xadU\x06\xbd\xc4\xb6a\xf3\xf2\xa6\x9cC\xcb\xd6E\xc5\xcb\xb9G;\xf5\x00\xa1\x98\xb1agI?\x8a\xe1\xe8\x9a\x99\xa8\xad5\xc1\xd9\x99\xc7\xe8\x97{q]\xdf\xf9\xfb\xb4v\x81~\x14\xa6\xae\x8b\xfc\xf2\xb6z\xfc\xc5Z\xcb\xa8\xa0h\xaeK\xde\x88\x87\xd8_CgQ\xe6\x1dQ\xacj\xdd\xf5|K#bt\x96/\x1aU\xc3\xeb\xe1\xb4p4\xfd\xebfu\x9e\xaeya\x1e\x9cUy-\xab\xad\xdf#-\xb4\xdb\xcd\xa6n\xe4\x1b|S\xcc?\x9fl+\xf1\x7f\xe2\xbd\xad\xfa\x85\xfb \xd2/z\xff\xc4\xa6\xbe\x81-W\x03\x9b\x19\x1eZ1\xb0\x9aU\x9eb\x05KV\xb1F.\x8e\xaa\x0f-#\xd5\xea,O\xd4G\xddB\xf7\xf5\xce\x1e\n\xd1\xf9\xe1\x9b7pQh\xa6I7\xa5\xe8\x9c^Vp\xfa\xdf\xfe[\xe05\xf9}]\xc3M]\xc3\xb7p||\xec\xcf\x8c\x11\x95)\xaaG\xff\x01E\xf5x,\xaa\xf1}S\xaf_\xdd\xd4\xf5\xd7\xfeC\x8f\x8f\xfd\xef\xbf\xf2\x06^\x89\xa2>\xc9\x86\\\xd5\xaf\xfe\x8b(\xeb\xeb`\xa2O\xa8\xbc\xff\x0c\xfb\xeeO\x11\xdf\xfd[qWds\x1e|+\xe7\x86\xe2*\x19\xe9\xd6\xa9^\x15\x85\x9c+\xa7\xbe;\xbb<\xfdx~q\xf5\xe1\xe3\xd7\xa1e\xfa\xbe\xa3\x86/\xac.\x1dv\xe7?G\xdc\xf9\x97\xda\xefI\xe9\xca7\xdf\xc2\x7f\xd9\\\x1f\x7f_\xd7\xbf\x1e\x1f\x1f\xff\xa7\xff\xe0\xa2z<\x12\xd3Pq\xc6FM\xa2~,\x9a\xf6\xb6X '\x87\x1b\x12r\xe1\xb8\x16\x81*\x947\xa3\n|\xaa\xd6}\x15d\x05\xe5\x03\"\x8f\xfa\xbf\xbe\x85\xaa\\\x85S\xfd\x82\xf5\xf2\xf4\xe4+\xb9\xa8<\xff\xdc\x8d\xc5\xe6C\x03\xae\x1f\xfbi\x97y{\xa8\xb5Q\xf7\xac\xd7del[\xcf\x9c\xe5\xa5cJu\"\xbe\xdf\x8f\xe5\x0fb\xba\xfa\x12\n\xebm'\xde\x84\xa2'\xf8\xde\x0d\xaa\x87\xb8/\xd6\xbdZ\xaa\xd5\xa3\xf9\xae\xdcY,\xe8\xa6\xc9P\xdcp\xe6Z)T&\xd71^\x9e\xbct_J\xbf\x13M\x95\xe5\xd7.0\xdd\xa3\xbf\xba\xa9\xeb\xe3\xeb\xa2\x91\x8d}8y<\xfe?_)/\xcao/gy\xfeOQY\xd5\xafD\x19\xe2u\xe8<\xe4\xdf.?\xbcw\xff\xf2\xed\xb7\xdf~\xeb\xef\x03\xe2\xbc~\xcdE\xf3\x91r\xbb\n5 R\xdfu\xdb\x96\x19`l\xb9]\x15\x9ed\xf5\xddbT\xf0\xa4\x9f\xb6\x1c\xf5)\xb6\xfa\xe9>R\xd3qWq\x85g\xf5\xc6\x9aR\xa8\xe0\xdb/\xff\x9fp\xdd/z1\xa1\x9b\xb6\xd97\xc7\xfd\x80\xe8\xe1\xe7M\xe0\x03\xa4\x98\x7f\x16cP\xffA|S\xae\x98\xff\xbda\xc6\xac\x0b\xd6\xb4u\x15|l\xf5J\x9c$\xdbg\xf2\x0eG\xb2s\xf5 \xa2S\x9a\xe3\xdd\xb9\xb9\xd2|o0\x80`\xad\xbe\x92\xbe\xfc\xea\x0d|\xe5zj\x87n8V\xad\xfc\xea(T\x9el\xdf\xfbb-\xca\xfc\x7fU\x13\xfeG\xf0\x04\xd1\xbe\xd1\xf1\xa9\x8d<\xbf\xd1\x1f\\\xc3\xbe\xa6zC\xd9\xc2=[\xad^\x7f\xae\xea{\x15\xf8\xbd-Z(t,6\xf1\xe1\x1av\xf9\xa3\xd1\xfe\x0b\xea9\xe8\x03K\xba:\xa2\x03{>\xae\n\xd5\xa5\xdd\x17\xfbE>\x8c\xa6\x9f\xdf\xd6\xab\x85\xea\xe4:\x8a,\x1f\xe5Q`\xd57\xb2\xe9G\xc6}\x1dY\x85\xe3\xee\xe5\xfcJ\x8ck\xc6\x85;KCf\xc5\xf4\xdf\xff\xf7\xbf\x7f\x1dx\x90r\xf4\xb9\xe1\x05\xc3\xddN\xbaJ\x14\xf9\xcd\xf1\x9f\xbe\xf9S\xfbU\xa0\x0b\xa9\xff\x0f\xc1\x84C4\xc9*j\xf019\xf3eE\xfcR\xfd\xb6MrbL\xf1\xb6\xb7 \x8e\x1c\xb6`\xe0\xc7mU>(%\x85\x10\x8c\xd4\xdb\xc0\x89}\xc11'J\x00\xa8X\xcd\"\xa9\x18\xbd%91\xdd%\xa3\xfa\x0c\\\xa2\x7f30>\xa249+\x1exFj\x86\xf8\xd3x\x8c)\xb0_\x0cq_\xdc#}U`\xb8Mm\xe4\xc5\xaa\xcc\x1a^_[\xda\x04*oA>\xc82t\x82(\xc9vc\xd8}im\xb4\xe7\xa0g\x15o\x1e\xad\xc4\x97\xc1\xadC\x8c\xc0\xa0H\x85\x86\xad\xd8]QqX3^,\n^\x84\xea;\xa8\xad~\xaft;g\x0f*\xa0\x7f\xf4\x15\x86m\xf6\xc7\xe1\x93j\xad\xfa\xaf\xcaVm\xba-\xb7\xdf.\xe72d\xe1+\xa6\x9b4\xbd\xb4\xa7\xfe\xd5R&\x92\x05r\xc1n\x9az=\xb8\x82\x99\x87\xf4\xddC\xae\x14\xa0\xaa\xd0\xbf\xbb#\xa9^\x91\x17v\xfce\xfdt\xdb\x82\x0f\xbe3D\xbd=i%\xc6P\x97\x06\xf4\xe5\x81\xe6\x07#\xa3\xf9\x01\xcd\x0f\x8c\xd1\xfc`l4?8\xec\xfc\x00\xd5\xf3Q\xf7\x18\xdf\xee\x9d6\xfb\xf3\\\xfd4\xbc\xcbuV*,\x94\x81T\x15\xb0(\xd2\x94\xe4\xd7@\x81vZ\xacN\x81\x0d]?\x90\x1d\x8b\xf3\xa4\xdd\xf8\x80\xfb\x86\xd31\xe5\x1fgy\xdag\xad\x99\xca\xb8+\xdf\xe5&\xed\xed5\xb4\xbf\xbc\x9e\xa2\xe4]i\xe1$Mc\x94\xbc\x0b\x7f\xe4\xe4\xdd\x9d\xe6\xc8@\xce >2-kw\x18\xab\xf1J\x90\xf57\x92\xb2ww\x1cD\xd9\xbb\x98\x17\x1eP\xf6.e\xefz\x8f\xa4\xec]i\x94\xbd\xbbk\x94\xbdK\xd9\xbb>\xa3\xec]\xca\xde\x95F\xd9\xbb\x94\xbdK\xd9\xbb\x94\xbd\xab\x8c\xb2w){\x97\xb2w){\xd7g\x94\xbdK\xd9\xbb\x94\xbd\xeb~@({w\xc7\xb0\x99\x94\x94\xbd+\x8d\xb2w){\xf7yf\xef\xb6\xcd|6\xdc\x1a\xc0W\xef\xdd#\x07u\xefY\x14\xab\xee\x1d\xee\xc1$\xc58nAj&\x97\xa7\x0d\x8b\x96#\xdb\xb0{dR\x1b\x86\x02\xe8\x19[@9\xd4\x18oQ\x0e\xb5\xb2\x03;7\x9e\xfdK9\xd49\xbcH9\xd4\x94CM9\xd4'\xdb\xea\xba\x96\xcd\x99Y\xdcM\xe26c\x9fL\x19\xbe\xfd\xc6\xba\x8b\xec\xec<\xd6\x15&\x93\xaf{W\xee\xcc\xc5L\x87roA\xe6\xaa\x81>\xe1\xd9\xe6g\xf7\x9e\x7ff\xc9\xd9\xc3.\x12H\x99ED\xa10\x81\x1cp]3\x9a%\x1d\x8c\x19w\xc5yck\xb8\xdd\xc02\xb60}\x07\xb0`\x0b\x7f?\xf9CI\xc9;\x88\x1b\xa2\x0c\x9f\xb4\x83\xbd\x81\xca\xc6\xc9:\xa5;M\xa7\x7f\xba#\xe5\xe1\xf2s\x92rs\x92}\x84\xcb\xc9I\xf4\xd3(\x17\xa7t\xa4\xe1\xa0\x9d\x84\xcb\xbfI\xca\xbdA;)\xad\xd9\xa1|\x1b\xbdO\x9d>$\xc0=)\x13\xaf\x99\xc5v\xa5\xb0\x1a\xbd\x9f\x1e\x88\x0f\"\x943\xbe\xa4\x13\x02\x8d\x1f4%\xc3]\xc7W\xcc1E\x18\xa5\xc7T\xd6<%\x9a\x1c\x93\x96\x183\xa8\xe68i\xd65;\n%\xcfb\xda\xech-\xb4\xbcn\xf4\x84L&\xcd\x8a\x8f\xe1\x15\xb3\x13c\x9dE\xf5\xd5\x0bd\xca\xca\x8d\xd6M\x89\xd66\xf7\x95p\xabx\xde_\xcb\xd92[\xc8\xa4]\xca\xbcp\x1e@\x99\x17\xfdOXgR\xe6\x85\xfc\xfc\x1b?\xef\xf8\xfd\xd3F\xa59\x08_\x95\x98\x11\xbc\x02\xed\xabf\x8c23\xf2|.Qf\x06ef\xb8\x8d23\xa4Qf\xc6\xaeQf\x06ef\xf8\x8c23(3C\x1aefPf\x06efPf\x862\xca\xcc\xa0\xcc\x0c\xca\xcc\xa0\xcc\x0c\x9fQf\x06efPf\x06efX\x96\x83\x92\xa7\xcc\x0ci\x94\x99A\x99\x19\xcf33\x83r\x02\xd2\x80k\xca 8\xa0s\xe34;\xe5\x04\xe4\xf0\"\xe5\x04PN\x00\xe5\x04\x9ct\xfcV(\x11\xe0?\x1c\x89\x00?u'\x0e\xf0\xff\xbe<(\xab\x9bZ\xf6P\xb5\xd9Zw\xe5\xae\xac\x10\xee\x7f\xa9\xda\xe0\xb8\x9c>\xfa\xd9\xb2\xfe\xbd\x07\x9e\x07\xc8av\x8ezB\xfe}|\xc9\xc1\x04o\x08\xbd\xf7\xee\xf2 \x89vy\xff]\x02\xf4`\xf0\xf9\xb2\x92\x13y\xf7\x07\xeb\\\xdc\xde\xaa\xdd\xb6\xb3\xcd\xf6\xda\x0b\xf2E\xbd\x0b\x08\x0f\x03\x02\x97\x01\x9c\x87!\xc1\xcb0\x01\x9d \x16\xe6\x8e\x9f\x04\x96\x13s#4\x10\xc7h`\x02J\x13n@\xc1o\xd18\x0d\xe4Bj`\"V\x13,Pj\xabc\xd1\x1a\xd8\x1f\xaf\x81d\xc4&X\x94\x0e\xfd'a6\x90\x1b\xb5\x81D\xdc\x06R\x91\x9bp\xcf\xeep\x1c,v\x03\xb9\xd1\x1b\xc0\xe17\x90\x13\xc1\x81\xbd1\x1c\x98\x86\xe2@.\x1c\x07&!9\xe1\xc7\xa1\x10\x9f\xbcQ,\x07\x0e\x83\xe6\xc0\x01\xf1\x1c8\x0c\xa2\x03\x89\x98\x0eLCubC0\x0e\xd7\x81\xbc\xc8\x0e$`;\x90\x8e\xee\xc0\x04|\x071d~\x8d@x \x07\xc6\x031\x94\x07\xf0\xd33\x04\xd2\x03\x89\xb3\xb8d\xb4'X\x9a\xc4~\x10x\x0f$\xd42#\xe6\x03I\xa8\x0f\xe4\xc6}`\"\xf2\x13\xeeWm\x1c\xfb\x81\xe9\xe8\x8f\xb7\x9c PH\x13L\xc0\x9a \x0dm\x82)x\x13$#N\x10y\xdbF\xb0\x13H@O\xb0\xb8\x13LA\x9e \x15{\x82p\xc3\xa7\xe0O\xde\xc2,\xb8\x08\xfb\xc8\xe00\xa8\xe0\x03Q-\xc3(\x14\xe4\xc5\xa1 \x86DA\x18\x8b\xf2\x9e3\x15\x97\x82\x8c}7\x01\x9b\x82$t\n,|jh\x7f/\xca\x15[\x84\xa3S\xe3h\xf8\xd0\xb0\xdf\xf0\xeaJ`\xf4\x07\xeeo\x99^\xc9\xb1U\x05\xb1\x16\xc4\xaa\xed\xda?C\x7f\x0d\xdf}x\xffnvy\xf5\xf6\xea\xd3\xe5\xec\xd3\xfb\xcb\x8b\xb3\xd3\xf3\xef\xcf\xcf\xde\xa1\xcf\x10\xffJ<\xfc\xfc\xfd_\x90\xc7\x07\x0b7b\x0eIMP\xa2?O\x10\\\xd6\xeaB\xaa\x1b\x83\x85\x0c\xcauv\xf9\xdb\xab\xb2\x9a\xaf\xfc/\xfe\x96\xadn^\xf7\xfa;\x9eN\xd0\x93\x12j_\xe2'h\xda\xf8\x92}\xdc\\\x12;\xfa\x8fe\xdbnUL\xc1\xff*\xb2\xa2\xea}\xa9\x9e\xc5Q\xbbv{7\xa0;n\x04u\xf6\x7f\xe7\xacY\xb7\xe0\xde\x17\xb6\xb7\x88\xe6]\xb6\xb8\xfd\xba\xae\xca\xcf\xcc\xa1\x15\xd2\x1b\xe2\x06C\x82\x8f\xa0\xbf\xaa\xb5]\xfb\xedv]T\xaf\x1bV,$\xd8%gt1\x1f\x01\xc6O\xa0 \x00\xee% \xe00m4\x97\xb5E\xb7L|\xb3\xff\xb1-\x97U\xc1\xb7\x0d\x83W\xec!\xfc\x91\xfc\xe9\xa2n$\xa5\xf9?\xd9\xe3u\xd12\xef\xe0\x0dp\xcf\xae\xdb\x92\xe7X\xed\x1f\xb4X\x17\xebl\x91\xf9mUV\x9fCC\xcf|\xdb\x94\xfcq&\xbfU\xe6A\x05\xc4)5\x8c\xdc\x93\xf1\xe5\x9d-a\xeb\xa2\\E\x83\xe4\xa6(\xd0E\xf9\xdb\xec\x95\xb5\xe9mJSu\xb1\xe6E\xa0\xc2R]#\xf4\xaf\xeeZ\xf5r\xaca\x1dJD\xb50\xda\x93\xd8;4\xae\x96\xb9;GP\xde\xf4?\x1e\xc9\x11U\x1f\x11\x80i\x8c>e\xd9\xeeL\xef\x96[K\xff.\xe6\xa2\x90\x08e\x82\x83b\xc2\x93\xe9N\x92\xd2\x92\x01\x17\xad\xcb\xaa\x93\x9f\xf4\x167\x9cH\xf2\xda(\x11\xb2\x98\x83\xe6\xf5z]\xb6-\xf6\xa5\xd9\x1f>x1Z\x7f\xee\xb3*\x0e\xfc\xca\xeb\xaf9k\n\x1e\x16l\xc5\xde\x15p\x94;h\xa8V\xc2\xb4\x0e\n\x96\xa5\nPHA\x17\x06Wz\xac\xf2{\x17\xf1\xdaC\xf9\x0b\x90>\x03]\xa7\xd8\x92<\xe2\x810\x96\xe2Z\xd0\x977\x9f>V\xb7\x91\x7f\x9e\xdf\x16\xcdR\xce \xa3\xc5\xf4\xb3\xc1#\x90\xc9\\7\x12\xdf\n+\x8c\x02\xac\x8b\x87\xd9\x17v\x80\xa9\xc2\xa0_\xad\x8b\x87r\xbd]\x8f=\x12-L\x8d\x8e\xfd\x93?/*`w\xac\xd1\xaeL\xf6\x8d\xe2M\x9e\x83\x8b\xac\x9a8=\xb5(\xca\xa8\x1e\xad\xccNhX\xd1\xb2\x1dL\xd7\xf2\xb4rR\xb4\xa8\xb8\x13\xb7\x1b\xf1v\x88\xca\x1d#\x1d\x87}\xe3@\xa2\x83\xadZ\x9a\xe7P\x86\x18\xe4\x1f\\O\xe5}\x04\xb3Swj\xe1\xf6\xcb\xba\xacf\xe2C\xd5\xd2\xea\xdf\xe3-\x8cm\xa7\xe3\xaa;\xcb-/[\xf9\x05\x0d\x0b6_\x15\xe3\xa4\x90Qa\xb2\xc7\xe9\xa3M\x81A\x8d W\xfa\xc4\xbf:\x0e\xc74\xa8\xcb\x19\xb0\xbe\xb3\xbaV\x1c\x01\xaf\x97j\x05L\xc6\x1d\xb8I\xb7q\x16U\xac\xad\xb4\x1cw\xf5\x7f\xb2\xfc#f\x0e\xe6s]\xa7\xe7\x97\x0d\xb0\x07u\xc7U\xef\x90s\x8d\xb2\xf2\xbc\xed/WE{+\xdeu&=\xc5G\x13\x17\xe2>\xa8GU\x87\xb3\x06\x979R\x0c\xa2D\xe2\xea\xa6\xf1\xbd \xe7\xc5j\xbe]uQ\x9c\x9b\xad\xf8$s_p[\xd9\x1b;\x08\xdf\xd5[\x0e%\x97\xc9\x1b\xd5\x12\xea;\xf9a\xdb\xad9\xc0\xcf\xb7\xacRMu7\xa0\x19\xae\xe0\xb8\xaf:\x9c\xc7\x1e\x8d\xc6\xa4\xb2\x15\x93\x83E\xc9\x0d'VX\xfd\xcdY\xde\xfdm\xdd\xb2>\xb3\xca}Q\xfb6\x96\xed\x80\xc8\xb4z\x84lZ\xdf\x04gI\x8b\xb2\x0b\xd9\xaauw\x89\x15\xba\xaf:\xb8\x7f\xc7\xf0S-\xdd\xba\xa9\xefYc\xb2!\xcd\xedb\x0b\x19\x86\xf5v[\xb3@+\x9b\xe0\xbe\xdaz\xbb\xe2\xe5fU\xaa\xca\x0d\xaf\xbds\xc2\xe0\xa9\xb3R}\x9c[{\xb5/U\n\x90\x14\xa0\xf6\xee\xd2@z\xd0\xd2\x06M%=h\x943\xffpz\xd0;\xfdB\xa6\xfe9r\xe4\x82*\xd0\x8e5E%\xfb\xec\xca\xed#\xb1gm$\xf6\x9cg\x86\x99\x9a\xb1\xa6\xba-\x89=#\xb2\xd3x\x8e\xcc\xb4)Yi$\xf6\x9c1\x03-%\xfb,)\xf3\x8c\xc4\x9e\xf7\xcd2\x9b\x90a\x96%\xbb,=\xb3\x8c\xc4\x9e\xf7\xc9$K\xc9\"\x9b\x90AFb\xcf$\xf6\x1c\x99%%g\x84\x91\xd83*\xfbkJ\xe6\x17\x89=\xfb\x0e\x8bfx%dwa\xa4\x8cS\xb2\xbaH\xec\x99\xc4\x9e1\x19Z$\xf6,m\x9f,,\x12{v\x95\x14\xcd\xb4\x9a\x9ae\xe5}7\x90\xd8\xf3\xae\x91\xd8\xf3\x84\xec\xa8xfTjVTBFTr6TZ&\x14\x89=\xa7e;\x91\xd8sg$\xf6\xac\x8d\xc4\x9eI\xec\x99\xc4\x9e\xfb\xbfMvn\\\xa6\x98\xc4\x9esx\x91\xc4\x9eI\xec\x99\xc4\x9e-\xb1\xe7\x93_\xbb\xffV\xbfMU\x7f\xee\xc4\x9f-\x18\xcf\xa3\xfb\xdc\x1f\xd2\x95\xb7)\xca\xee-?\x90\x7f\xde\xbd\x90>\xea\xf9\xcb>\xfb\x88\x8eI\xd8\x19N\xbc9\x1a\x9b\xc0,\xee\xe7\x96mN\x17m\xc6I6\x07\xbd Q\x8f\x02\x02wAx\x14\xd0^\x85\xa9\xd0K\xa0\xbc$\x91\xe6\xac\xe0K\x14}\xc9\x0d\xbf\xe0\xf1\x97L\x00\xcc4\x04&P\\\xa2,\xf3\x9e\x18Ln\x10&\x11\x85\xc9\x0c\xc3\xa4\xe10\x89@L\xa8\x0fw\xa8\x0c\x16\x89\xc9\x0c\xc5\xa0\xb0\x98\x8c`\xcc\xbeh\xcc$8&\x13\x1e3\x05\x90 \x14\x86\x16^>\x00$s8L\xe6 \xa0L\x1a*\x93\x1d\x96\xc1\xe22Y\x81\x19<2\x93\x0c\xcd\xa4c3\xd1\xa1\x10'\xb1\xbc7:\x13\x95WFM\xa8\x10\x00M\xca\xac+\x19\xa2 \xbd\x04\xd1\xa2\xca\xb8\xfaeDiR`\x9a\xcc8\xcd4\xa0&\xd4\x83PB\xca\x13\xa1\x1aOi\x1c%\xa2\x9c\x07\xacA\xd3!\x08\xb8& \xaf\x89i\x90NAlbezCm\x99@\x9btg\xe2a\x9bX\xdb&\x007\x13\x91\x9bP\xc82\x1bv\x83\x06op\xe8\x0d\x16\xbeAx9\x1d\xc0IAp\xc2\xb2\xc8Y0\x9cD\x10g?\x14'\xe6\xd0\x04\x1c\xe7\x00@N\xb4v\xde\x9e\x9e\x0f\xcbA\x809\xd3\xd1\x1cOq<*\x7f\x9c\x15\xcf\x89\x01:\x13\x11\x1dOYq\xd9c\x04\xa6\x13\x96<\x0e \x1e\xe7\x86u\xb2\xe3:~`''\xb2\x83\x81v\xd2\xb1\x9d$pg\x02\xba\x93\n\xefDD\x8c\xc3\xb5\xc3\xe2\x14X\x84g\x02\xc4\x93\x88\xf1\x04\x9a;\x05\xe5\xf1\x14\x85\x90-\x9e\x82\xf3\x04\xba|\\\xb28#\xd2\x13\x95+>\x04\xd6\x93\xab/&\xa0=)p\x8f[\x8c8$E\xcc\x9d\x91Z\xdbp\xdf\xbdSD\x88\xa5\xd8\xb0\xa7<\xac\x04qH\x80\x18W\xf3I\xe2\xc3\xc9\xd2\xc3\xd1u\x92\x90\xecp\xaa\xe8p\x92\xe4p\x9a\xe00Znx\x82\xd8pHj8\xea?\xdc\xcd\xdeWd\x18#1\x8c\x13\x18\xce\xd4\xa0\\\xd2\xc2xaa\xbb^{U\xbc;j/I\xe1\x88b \xdf?\xaa\x1c\x95\x12\x8e\xdeJ@{\x052\x8a\x08G\xb5\x14\xe3\x02\xc2\x99[\x96Y:\x18-\x1c\x1c\x95\x0dNo\xe7\xbe\x92\xc1x\xc1\xe0\xf4\xba\x05\xefA6\xa9`\xacPpT&8\xbd\x81\x93%\x82q\x02\xc1\xd1\n\xc5\xc5\x81q\xf7#\xa70\xf0>\xb2\xc0\x18Q`\xb4S\xc2\xf2\x8c\xa9\x8eI\x11\x03\xc6\xbc%\x00-\x05\x1c\x13\x02\x1e\xb4d\x7f\x19\xe0\x0c/*\xbc\x000\xee.@\xba\xf8\xaf\x12\xf8\x0d\x94\xb7\x97\xf4o\xd4G\x80\xf2\x13\xa0D\x7f\xa3\x1d\xde\x18\xde\x99\x80\x96\xfb\xb5\xe5|#\x05\xa2\x05mqR\xbf\x07jv\x8a\xc8\xaf\x1a\xcb\"\x05\xe6\x90\xf8M\x12\xf8=\xa0cP\xd2\xbe\x9dto\xa4\xc0\xa8\xb0o\xd45(\xd9^\x94;p\xef\x04HrZf\xb9\xde\x80X/Z\xaa7\xea\x0b\\\xeb2\x8a\xf4&K\xf4\"\x05z\xe3\x0d\x99&\xce\xabeW\x1d\xe5\xf9\xa5ys\n\xf3\"ey\x93Eym\x01^w\xe3\xfc\x92\xbcy\x05y1r\xbcy\xc5x\x11R\xbc\x93\x84x\x8d\xe8\xae\xab\xbc\xa8\x0c\xef4\x11^\xbd\x1c\xe9(\xcf/\xc1\x8b\x16\xe0\x1d\xb4\x02\xe7\xef\x94N\xb3\xaf\xf0\xbc2\x8c\xfc\xbc2\x9c\x08\xbd\xb2\x0347\x97,\xbd2\xbc8\xbd2\xbb\xa6\xd9\x1a\xd4\x1d\xbb\x97\\\xbd2\x84\xd6mv~$*c\xaf\x0c\xd9\x19 \xd1\x7f\x90Q\xd8^\x19\xca\x87\x18\x91{e\x07lw\xa2\xec}\xb4\xbcW\xec\xe1\x18/~\xaf,*\x81\xafl\xaa\x17\xf6\x95\xc3W\x86\x17\xc5W6\xb5\xb6\x88{\x96M&\x7fX\\L,_YT2_\xd9\xd4\xe6O\x96\xcfW\x86\x13\xd1W\x86\xac\"\x96aO\xb9\x8b\xe9\xe2\xfa\xc1\xe2\n\xbe\x97\xc4\xbe2\x8c\xd0\xbe\xb2D\xc7\xc5\x92A`\xb2\xf3\xb0\x02\xfc\xc1\xe2\xf4\x98>E\x86_YL\x8c_\xd9\xa0\x85\xfbK\xf2+\xcb\xfc\x1a\xc6\x8b\xf4+K\xb9k\x90.\xd8\x1f-O\x15\xb2\x97l\xbf2\xa4\x1f!\xc1\x97\x80\x12\xf2W\x86|\xa0\x8c\xa5\xba\x1d\xd0\xd2\xfe\xa8\xa2,\xf9\x7f\x9cf{o8\x99\x7feO\xe0\x94\x14\xe1\x7fT\x81j\x14\x8e\xc8\xff\xa3JJ\xf3)z\xa3\x00eO\xe4Z\xd4\xd6\x01\xa8\xd2\xcc\xf6\x02\xf1\x0d\x04P\xc5\xe1\x9c\x8b\xdaj@Y\x82CS\xde\x8a0\xc1\xf9\xa9[\x10D\x0b\xbc/\xda\xd0F\x04\xca\xd0\xdb\x11(C\xfa+\xa5\xed\x197(\xe8\nL\xdb\xa6@\x19r\xb3\x02e\xd8\x06N\xdb\xb8\xc0[\\/_\x1f\xa4\xb3\xd371\xf0\x16\xa567\xc0ne\xa0,yC\x03\xff\xd5\xfb\x8d\x0e\xc2\xdb\x1a(\x9b\xb4\xb9A\xb8\xe9\xa8-\x0e\x94\xa5nt\xe0-\xc8z\x10\x10\xdb\x1d(\x9b\xb4\xe9\x81\xbf\x06e\x87\x0f\xf0\xf0\xd6\x07\xca\xa6m\x80\x10l\x0c[\x04\xb6AP6m3\x84\x9d\xa7U\x8a\xa4\x0cUH\xba\x8d\x10\xca6a/\x84\x912\xca\xc7\x8b\xd3q\xddi[\x84\xe1w9m\x8b\xe00\xec\xbb%5qNu`\xda\x16\x01\x91\x10\xe7\xe2\x88\x92\x13\xe1\xf0 p\xb4-\x82\xb1\xd4\xc4\xb6\xe0:iJB[R\"\x1bm\x8b\xb0o\x82\xda\x84\xc4\xb4, i\xf8D4\xda\x16!G\xa2YJ\x82\x19:\xb1\x8c\xb6E\xa0m\x11\xd0\xb3\xa4\xa4\xc4/\xf5\xb9D\xdb\"\xd0\xb6\x08\xb9\x92\xb1\x90y@\xd1\xe4\xab\x84\xa4+\x8c\xe8\x7fJ\x92\x15m\x8b@\xdb\"`\x92\x9fh[\x04i\xfb$1\xd1\xb6\x08\xae\x92\xa2\xc9HS\x92\x90h[\x04\xdb\x10IE\xb4-\x02m\x8b00\xda\x16!-Q\x87\xb6E\xe8\x8c\xb6E\xd0f\xb6\x18\xd8a\x05\x07\x1f\x91C\xd6\xb0\xa7\x06\xf5\xdf\xbb\xcd\x04\xf8\xae\xd8\xbc=,\xed\xb9\xb9\x00x\x98J\xdf\xa6\x03\xbe=\x07\xa4\x1f\xf4\x19\xae\x0d\x06.:?\x0d6\x17\xd0\xc58\x00\xbb\xc1v\x02\x97\xea0Y\x88\xf9\xd2|\xb6\xbb \xd8\xae\xb0mP\x19u\x90~\x1a\xe5j\xba\x9cYuN\x92o\xc1\xb2\x85u\xbd\xd8\xae\x1c+\x12\xdezA4\x9e\x85AK\xa3\xcb9\x83\xc6\x8c\x18P\x0dz(\x1eT?v\xf2K?Do\xae\x8b\x07K\xf87T\xab\x90\xdcu\x1c\x0f\x1eT|xQSqCDu\x11zKU\xd9WuV\xf1&\x18?\xccZo\xef\xd2\x98U\x15\xab5`\xfe$\xa7\x9a\xa5\\s\xecn\x86\xa7$\x8bW\x90\xf1i\xeb\xdf\xaf6\xac\x81MQ6'\xbc)kg:\x81\xb5\x05\xc93\xf1\xccn\x8d\x8c\x83\xfa\xdbl\x0d\xb3\xe6\x18/E\xb3aM[\xb6N<_8v\xb6`U\xed\xc9\xb1K{\xb8\xfa\xd2\x06\xd8\x9e\xf8\xb3\\-\x9f\xd7e\x05\xf2\xf7\xb2\xd2L\xd4n\x89\xeb\xb2\x9a\x8dx\xdf\x89u\xd3j\xcb\xfe\x1e\xb8{\xa5\x8et\xbd-\xca\xea\xf5}\xb9`\x1d\xce\x15\xc5\x8b\x15\xca$\xa6G\xc5\x88\xe4T\x10'(\xf4\xa9\xe7\x7f\x06\xe5 \x18\x11\xf5N\x89\xb2!\x9a\x06QGK\ndX\x16A \x04\x81D# \xb8\x91\x8a \x10\x82@|G\x12\x04\"\x8d \x90]#\x08\x84 \x10\x9f\x11\x04B\x10\x884\x82@\x08\x02!\x08\x84 \x10e\x04\x81\x10\x04B\x10\x08A >#\x08\x84 \x10\x82@\x08\x02\xb1,G@\x9e \x10i\x04\x81\xfc^ \x90dX\xa2\xaeW\x01T\xa2\xaeW\x03HB\x1c>\xe0>\x06l\x848\\\xff\xfd\xf9\"\x11]\x83m\x1b\x02\x11\xa2\x91v\x80s\xa7\xd5\xbdyk\x00\xd18NU\xf3\x99\xcaD\x9f\x85\xd4=#+\x19\xfb\x940h\xb5\nA\xd6\xf5\n\x1d\x80\x14^\xf9xqJ\x01G\xa0\x80ct\xb5\x0d\xb3`\x05\x14p\xa4\x80\xa3\xf7H\n8J\xa3\x80\xe3\xaeQ\xc0\x91\x02\x8e>\xa3\x80#\x05\x1c\xa5Q\xc0\x91\x02\x8e\x14p\xa4\x80\xa32\n8R\xc0\x91\x02\x8e\x14p\xf4\x19\x05\x1c)\xe0H\x01G\n8Z\x96#\xf8C\x01Gi\x14p\xfc\xa3\x06\x1c\xc7\x89\xb5\xae\xb0\xe3O}\x1e\xac >\x16\xab\x95\x95\xfajV\x16\xf9\\\xa9\x9a/\xcb;V\xe9\xbd\x0c\x9d\x91\xc9\xbeD\xfd\xeb\xb3\x8dO\x86\x12\x8f\xf9\x17\x08\xfd(7\xd6\xcd\xacX,\x1a\xd6z\x8eB\xad\x7f`\x96\x10\xc0q\xc9A,\xd6\xfcm\xbc\xe1\xc3K\xff\x12\x96)\xef\xbf\xc35\x9b\xdf\x02\xab\xe6\xf5B\xaeR\xcaG\xdf\xfd\x8a\x9b\x8b\xdb[\xb5\xdbv\xb6\xd9^\x7ff\xde\x9d\xd8\"\xde\x05\x84\x87\x01\x11`\x03\x9c\x87!\xc1\xcb0!\xd8\x16,\xcc\xbd\xe2\x12\xf8\x00\xc9\x1dt\x83x\xe0\x0d&\x04\xdf\xc2\x0d(\xf8-:\x00\x07\xb9\x82p01\x10\x17,P8\x17\x1d\x8c\x83\xfd\x03r\x90\x1c\x94\x0b\x16\xa5\x83\x05I\x819\xc8\x1d\x9c\x83\xc4\x00\x1d\xa4\x06\xe9\xc2=\xbb\x0b\xe0a\x03u\x90;X\x07\xb8\x80\x1d\xe4\x0c\xda\xc1\xde\x81;\x98\x16\xbc\x83\\\x01<\x98\x14\xc4\x0b?\x0e\xd6\x0e\x1d\x91&\x1c \x98\x07\x07\x0c\xe8\xc1a\x82z\x90\x18\xd8\x83i\xc1\xbd\xd8\x10\x8c\x0b\xf0A\xde \x1f$\x04\xfa =\xd8\x07\x13\x02~\x88!\xf3kD\xd0\x0fr\x04\xfe \x16\xfc\x03\xfc\xf4\x0c\x11\x04\x84\xc4Y\\r00X\x9a\x0c\x14\"\x02\x82\x90P\xcb\x8c\x81AH\n\x0eB\xee\x00!L\x0c\x12\x86\xfbU\x1b\x0f\x14\xc2\xf4`\xa1\xb7z[\x17\x0f\x81\x9d>z;\xa0\x03L\x15\x06\xfd\xca\xec\xaf3\xf2H\xb405:\x0ew\x01aw\xac\xd1\xaeL\xf6\x8d\xe2M\x9e\x83\x8b\xac\x9a8=\xb5(\xca\x80>\x84\xb1\xb2\x12O[\xcbv0]\xcb\xd3\xcaI\xd1\xa2\xe2N\xdcn\xc4\xdb!\xf8\xa6\x01\xbc\xe3\xb0o\x1cHt\xb0UK\xf3\x1c\xca\x10\x83\xfc\x83\xeb\xa9\xbc\x8f`v\xeaN-\xdc~Y\x97\xd5L|\xa8\xce\xfa\x0f\xd5=\xde\xc2\xd8v:\xae\xba\xb3\xdc\xf2\xb2\x95_\xd0\xb0`\xf3U\xd1\x04\xd6\x88\xcc\xc6<\xfahS`PU\xa0\xac\xe6L\xbc-\xdbu\xdd\xben\x17\x9f\xe1\x9f\x8e\xff\xf9_\x1d\x87c\x1a\xd4\xe5\x0cX\xdfY]+\x8e\x80\xd7K\xb5\x02&\xe3\x0e2f >\xb3\x9dE\x15\xebz\xdbQ\xc5\xee\xea\xffd\xf9G\xcc\x1c\xcc\xe7\xbaN\xe8+\x1b`\x0f\xea\x8e\xeb\x8d\x88j\xb9\xdd\x92\xe7m\x7f\xb9*\xda[\xf1\xae3z\x1f>\x9a\xb8\x10\xf7A=\xaa:\x9c5\xb8\xcc\x91b\x10%\x12W7\x8d\xefM8/V\xf3\xed\xaa\x8b\xe2\xdcl\xc5'\x99\xfb\x82\xdb\xaa\xbf\x95\xad\xf4]\xbd\xe5Pr\x99\xacQ-\xa1\xbec\xf6\x1eJ\xc7\xf0\xf3-\xabTS\xdd\x0dh\x86+8\xee\xab\x0e\xe7\xb1G\xa31\xa9l\xc5\xe4`Qr\xc3\x89\x15V\x7fs\x96w\x7f[\xb7\xd6v]\xee\x8b\xda\xb7\xb1l\x07D\xa6\xd5#d\xd3\xfa&8KZ\x94]\xc8V\xad\xbbK\xac\xd0}\xd5\xc1\xfd;\x86\x9fj\xe9\xd6M}\xcf\xd4k\xea\x9au\xb7\x8b-d\x18\xd6\xdbm\xcd\x02\xadl\x82\xfbj\xeb\xed\x8a\x97\x9bU\xa9*7\xbc\xf6\xce \x83\xa7\xce\xca\xe6\xe9\xc10\xbd\xfb\xa0\xca\xfaY\x80\x7f\xb3\xbbM\xb1\xd4{\x8c\xed\x0ek\x83\xcb\xf4\x07\x0e%\xfd\xfa?\xeb~\xef\xca\xe9Q\x16\x9c\x08F4\xfe\xd8\x03\x9fy\xd3G\xa2#o\x14\x16\x1c4\xf5?|\x03\xb3\xa9\x85\x19\x8b\xc5\x7fj\xde\xb6h[\xb5\x18xQ,\xd9G\xf6\x8f-k\xf9\xb1\xfa\xddSX\xbf\x15\xa8(V8\x92\xc1\xban90I\xadJ\xdc\xf5\x18\xce\xb9\x15\x86\xda\xf0G(}\x18\x9a\x18C\x99|\x8c\xab\x1a\xd6u\xc3\xcc\x98\xe5\x1a\xd9d_\xdd\xd3\x99[\xff\xc7vlG9\xb5\x90Z\x9a\x15\xd5~\xbb>3\xccZ\x1a>\xbe\xf6\xda\x8e\x9e\x8bA`\xa6\x1e@\xcf\xe1\xf7\x85x_\xf2#(yk`\xf3V\x8e\xa2*\x0e#\x17.\xee\xcbv\xd8?|\x0d\x91\xd9y}2\x1cV\xeaq'}\xceX/\xff\xd8\xfd@\xea\x8f\xa3_I\xfd\x117\xdf\x82 i\xaa\xb3\x92\xfa#\"\xf9\x8c\xe7H<\x9b\x92tF\xea\x8f\x19\x13\xccR\x92\xcb\x92\x12\xcbH\xfdq\xdf$\xb2 dY\x92\xc7\xd2\x13\xc7H\xfdq\x9fD\xb1\x94$\xb1 b\xa4\xfeH\xea\x8f\xa4\xfe\x88M\xf0\xca\x9a\xdc5%\xb1\x8b\xd4\x1f}\x87E\x13\xb8\x12\x92\xb70\xda\x86)I[\xa4\xfeH\xea\x8f\x98\x04,R\x7f\x94\xb6O\x92\x15\xa9?\xbaJ\x8a&RMM\xa2\xf2\xbe\x1bH\xfdq\xd7H\xfdqB\xf2S<\xf1)5\xe9)!\xe1)9\xd9)-\xd1\x89\xd4\x1f\xd3\x92\x99H\xfd\xb1\xb3?\xa4\xfacO\x1d\xf6\x93\x9e\xd7r\xe4}\xb3\x9bV3\xf8\x88\xd4\xc98\xac*\xaeWj\xe1E\x05\x16\x85\xdb\xac\xd0\xb0\xd4xT\xd0\x9eK\xe4\x11\xe4\x1b\xe5\x8d:\xd7\xfa[\xc3\xfe\xb1-\x1b\xb6x\x037\xc5j\x10\x1bs~\xa9\x9b*\xf7Q\xe1\xe3\xcf\xec\xd1W\xf5Q\x9cU\x07V\x0b=\xea7\x8co\x9bJ\xe9\x0b\xaaX\x9f\x8emuQX\xb9z\xb5\x1c-\xf3\xc8\x16\x88\x86\xc6\"\xab\x1f\xc4;\xba\xae\xe4\xe7m}s\xd32 \xb5\x0f\xab\x0b\xd6\xea{\xcbxfoy\xd62\x1cNT\xf5\xf3\xf9q\xb4\x8e\xa0\x1b#]Ym\xd7\xac)\xe7\xe6or\x80\xd0\xf0\x82Z\xc8\xb9e\x95q\xfc\xb6\xea\xd6\xceF3\xe6sY\xda\x8a\xb5m\xefB\xb5\xda\xb4m\x85\xab?\xb3D\x7f\x0e\x8b?\xb0sGqj\x87{W\xe5\xba\xc4zW\x1ek\x00\x00_\xf8Z\xad\xab\xda=X\x93\x11\xdb\xd5(\xde\xaaVQ\xec?\x9d\xdf\xc0\x8a\xddp\x13\xee\xd7\xf1\x7f3\xcf\x95K\xc2\xea\x01Q\x17\x11~\xbe~\x04V\xcco\xa1\xd8l\xbe\xa0\x17\xed |\x7f~\xc8\x97\xd6\x19\xc2\xa3\xb2\x87\xd6\xc0\x9b-\x03\xf1\x1fe\xb5(\xe7\x92\xd1\xd2\xc1!\xedAy\xa0\xeeHvqe5_m\x17\xa3Yl\xa1\xae\xd2E\xe7FwL\xc6z\xadEc1l\x0e\x98\x96Aa\x9f\xce\xdb\xd1\xdd\x1a5AN\xfc\x1b\xd6\xea\xa8\xbc|\xbc\xfa\xe7Q#^\x8fx=\xe2\xf5\x88\xd7\xb3,\x07;E\xbc\x9e4\xe2\xf5\x88\xd7{N\xbc\x1e\xed\xab=u\xd3b\xdaW\xfb\x80\xce\x8d\xef\x08M\xfbj\xe7\xf0\"\xed\xabM\xfbj\xd3\xbe\xda6h~\xf2\xeb\x90\xe2\x0dm\xbam\x81eh\xde\xbcg.aS\x94n\xfc\xfc\xddX\xb5\xeb\xb7\x04\x9d\xfb\xc0\x8dI`Y\x0c\x1d\x0f\x16\x0d\xd1\xe2!\x0d\x1aG\x84:p\xf1\x02sd*.\x1e NFa\xf1\x04T\xf4[\xea\xe3\x1c\xd3\x17\x15X\x13Q\x9b\xc5\xcf\"R\x9c\x80wLJcGW\x1f\xbc\x985\xec\xa6\x0f \x049A\x0f\xf1\x8b\xedJE\xd04\x8e\x07\x05G\xb9\xe0@M\x0f\xb4)\xb5\x8eX\x97:^\xaag\x15o\x1e{\x96\xac\xb2^\xe3\x91\x1d\xfb\xe5\"~\xc3V\xec\xae\xa88\xac\x19/\x16\x05/\x10\x88\xa3\x1e$\x15Un?\xc569\xa9\x0f\x9a\x02\x82\xb9&)-\xaf\xe5&\xd3\xab\x95\xfc\xac\x87\xb6\xac\x96+kr\xf7\xd2\xb5\xde\xdfWL\xfc\xbf\x93\x93\x14\x0f[WZ?\xd1\x11\x9f\x1f\x95|\x1c_K\x14\x9e-`U\xb6\xfc\x90d\x97\xeb\xf4\x13\xd7,jt \x11_\xda\x88\xf8\"\xe2\xab7\"\xbe\x88\xf8\xea\x8d\x88/N\xc4\x97\xdb\x88\xf82F\xc4\x17\x11_D|!gID|uF\xc4\x97mD|\x11\xf1\xe50\"\xbe\x9c\xc7\x10\xf1E\xc4\x97\xc7\x88\xf8\"\xe2\x8b\x88/\"\xbe,\xcbA\xdf\x10\xf1%\x8d\x88/\"\xbe\x88\xf8\xda\xaf\xceY\x89/\x17\xd4\x15\xda \xd9Z\x01\xe8v2s\x84\x0b[\x07\xdfe]@\xbe2\xbb\x82v\xd8\x18\xf7\xfe\xc9\xae\xcb\xe8\x13\x9e9\xdc%\xdc\xfb<\xf7Q\x8e\xa1T\xa8\xf5-\xcc\x12\x11\xe4\x06\xc5\"\xa8\x18\x1a\x16\xcb\xda\xc2\xac\xc8XT\x97*\x88\x8d\xc5\xc1\xb1\x08:\x16\xedb\xcab\x1dMY\x02@\x86\xba!\xca\xb0\x10\x19\xfe\x06*\xcb\x0b\x92\xe1P\xb2$\x98l\x82\x8f0@Y\xb2\x9f\xf2Ae8\xac, ,KpRZ\xb3\xb3\xe1e\xfb\x01f(\xc4\xec`N\xc0rh\x19\xee:\xbebYI\xb54V-+\xad\x86ks6b\x0d\xc5\xac\xedG\xad\xd1\xf6\xd3\xda\x06M\xa5\xed\xa7Q\xce\xfc#l?\x8d\xc0:\x83_BA\xd2sT\x9a\x83\x1d\x1amN\xed\xfc\xa4#\xe6S\x1b1\x9fy>\x97\x88\xf9$\xe6\xd3m\xc4|J#\xe6s\xd7\x88\xf9$\xe6\xd3g\xc4|\x12\xf3)\x8d\x98Ob>\x89\xf9$\xe6S\x191\x9f\xc4|\x12\xf3I\xcc\xa7\xcf\x88\xf9$\xe6\x93\x98Ob>-\xcb\xc1\xdf\x11\xf3)\x8d\x98Ob>\x9f'\xf3I\xdbT\xa7\xed\x01L\xdbT\x1f\xd0\xb9\xf1\x0d\x96i\x9b\xea\x1c^\xa4m\xaai\x9b\xea\xdf\xd76\xd5\x86\xfb\xe7\x0f\x1d\xf2\xdf\x96\xeb\xed\xaa\xe0z\x05{S\xb7\xbb$\xff\xa5>\x04\xcc\xb1-\xb0\x076\xdfr\xd1\xc4\x02xSTm!\x17)\xd5G[\xcb\xcbu!\x7f\\\x16\xa2\x8f\xc8!A\x959\xe0\xf5M\xb9/L\x13\x9f)\x9a\xbf,\xdaYY\xdd\xd4\x11\x1a\xcd\x1cf\xc6R\xf1\xdfb\xa8\x91\xfb\xa4^\xd7[\xae\xdd\xd1\x8f\x9f\xda\x9fN\xa4\xd1[O\x88\x92\x19\xa2\"\xf7E\xc5\x99C+\x160A \x04M\x85Y\xf8\x07\xf8K\xd1\xfe,+b|\xb2.\x1e\xca\xf5v\x0d\xdb\xaa\xe4r\xc5\xfa\xben>\xc3\xbd\x8eL\xaa\x80\x18\x7f\xf0\x03j\x1b\xd6\x88\xca\xb9>@E\xab\x85s\x9f\xa8\xcd\x7f)\xdaOm\xdf0\xbd\xafm}#or1\xe7\x8a!\x98\xd7\x95\x0e.\x0f\x8bR#J\xa4C\xe9\x97C\xd9\xda\xaf\n\x13\xbc8L\xd7Y\x14\xbc\xd8\xd3\x818\x9e\xd1\xdbe\xde\x15\xbc\x90S\xbd\xeaQ\xd6\xa6\x1fjo\x1a\xb9\xed\xaf\xfar\x92Q\xe6j\xb1\xf2\x04\x8a\xc0\x8cPu%\xdfO?~\xba\xbc\n\x84\x01W\xacZ\xf2[\xd84\xec\xa6|P\xcf\xa7\x1c\xba\xc5h\xdf2\xf11\xc3\x99\xaa\x8d\xaa\xc4v\xc5\xcb\xcd\xca\x1783u\xec\xaa0~\xc7\xf6\xf6N|\xdc\x8a)\xc1B\x13E\xdd\xc6\xc8-\x17\xb3\xa4M\xbd\x91C\xe4\xe2\x08\xae\xb7\\V\xd0\xdb\xe2u\xdb\xe7\x0eAY\xb5\x9c\x15^\xb0\xe9\x9a\xcd\x0b\x89\x9cp(Vm=\\\xe6\xf9\xb1]\x0e\xc1@\xcf\x92\xcf\xaa^N\xec+\xb8\xae\xf0C\xbd\x1cVlU/\x07\xa3\xea\xd4\x1e\xe18\x80\xdd\xb1\x8a?c]eY\x8c\xf7W\x84\xcb\x85\x15\x9c7\xe5\xf5\x96\x873pb\xcdU\x16\xc9\x08\x02\\\xd3\x95a\x1c\xa0\xcc\x8b[\xdb\x86\xf2\x85\xb1\xe8\xb0\xd5[0\xd2\xdf\xdb\xa1._V\x0b\xf6\x80\xbd\xfcxv\xec2\xdcSh\xecL\xf7\x90\xd33\xe4'^\xca,.\x19\xf4\x0e\x96&!p\x04\xec\x0d \xb5\xcc\x08}C\x12\xf8\x0d\xb9\xe1o\x98\x08\x80\x87\xfbU\x1b\x87\xc0a:\x08\xee-O\\1\x06\x83C6 \x1c\xf0\\3`\xc0pH\x83\xc3!FsN\x84\xc4\x01Qn\x00\x18\xcb\x04\x8c\xc3$\xe7\xe2\xc1q@\xb4r\x02@\x0eS!r\x08{5\x1fL\x0ex\xa0\x1c\x90P9\xa0\xc1r\xc0y=\x1d0\x87$\xc8\x1c\x82\xa09\xe4\x82\xcd!\x158\x87=\xa1s@\xb87\x01>\x87C\x00\xe8\x80\xa9c\xe0I\xc8\x07\xa3\x03\x06H\x87=\xa0to\x81\\\n\xe1\xf8\xc1t\xc8\x0d\xa7C\x14P\x87\xa9\x90\xba\xb74\xf5\x8d\x1a\xfe\\G\xc0\xea\x10dj!\x08\xad\xc3$p\xdd[T\x10h\x87\xa9P\xbb\xb74\x1d>\xf0_.\x1f\xdc\x0e(\xc0\x1d&@\xee\x90\x06\xba\xc3\x14\xd8\x1d\x92\x81w\x88\xbcm#\x102$\x80\xc8X\xf8\x1d\xa6\x00\xf0\x90\n\xc1C\xb8\xe1S`xoa\x16j\x8e}dpP|\xf0\x81\x90\xd0V\x00\x8c\x87\xbcp<\xc4\x00y\x08C\xf2\xdes\xa6\xc2\xf3\x90\xb1\xef&@\xf4\x90\x04\xd2\x83\x05\xd3\x0f\x0d\xf7\x05>\x88\x82\xed\xe2\x1d&\x92\xd6\x1f\"_C\xee\xb2\xc4\x88\xa6\xa0\x98\xb7\xd5c\xeb\xfflt\x82\x8d\xff:88\xacif\x80@[\xa1\x8a[\xf2m\xea)s\xefGz\xc9\x9a\xbbr\xce\x8e\xbb2H\x82J\x19IP\x91\x04Uo$AE\x12T\xbde\x8d\x84\xa6DA\x93\"\xa0$A\xb5o\xb4sB\xa43K\x943=\xc2I\x12T\xfbD4S\xa2\x99\x13\"\x99$AE\x12T$A\x85\x8dDf\x8dBN\x89@\x92\x04\x95\xef\xb0h\xa41!\xca\x88\x11XJ\x89.\x92\x04\x15IPa\"\x85$A%m\x9fh IP\xb9J\x8aF\xfc\xa6F\xfb\xbc\xef\x06\x92\xa0\xda5\x92\xa0\x9a\x10\xa5\x8bG\xe8R\xa3s \x91\xb9\xe4\xa8\\ZD\x8e$\xa8\xd2\xa2n$A\xd5\x19IPi3\x1a!\xd7\xf5\xc2~\xf7\x95\xd5\xce\x9f\xbc2Q\xae\xd8\xcf\xff\xd3\xb0\x9b7\xf0\xf2\xff>\xb1\x16\x0e\xb5\xd4\xc61\x7f8\xd6R\x1b}hJ\xed\x10\xf3R\x971\x16\xeb\xd0\x91,\xb7\\\x07\x7f\xd0\xc7\xba\xb6\xdc\xfc\x0b\xe3W\x0f\xadJ\xc2\xbba|~+\x06\xf9\x87V\xca\xe9\xd8\x19\x96\x03\x15\x0e\xeb$\xfd\xf3\xd3\x08q \x9dfU\xcf\x84\x05_\xbe\xe8kA\x01\xbd\x81Q@\x0f\xb7 \x04\x14\xd0\xa3\x80\x9e\xf7H\n\xe8I\xa3\x80\xde\xaeQ@\x8f\x02z>\xa3\x80\x1e\x05\xf4\xa4Q@\x8f\x02z\x14\xd0\xa3\x80\x9e2\n\xe8Q@\x8f\x02z\x14\xd0\xf3\x19\x05\xf4(\xa0G\x01=\n\xe8Y\x96#\xb8B\x01=i\x14\xd0\xfb#\x04\xf4\x94\xaa\xa5U\xc4\xe0#R\xfdj\xb2\xc9Ve\xab\xd6\xbc-\xddzy\xc4\xce\x17\xe9\x14!\xfeq\xb8\xc4\x11(\xf1|\xc0\xcf\xeb\xd5\x8a\xc9\xea|\xaf?\xdf\xa5\xce\xf4N[i/\x9a\xb4\x8d>h/\x9a\x03:7\xbe\x8b\n\xedE\x93\xc3\x8b\xb4\x17\x0d\xedE\xf3\xdb\xdc\x8b\xa6\xf7\x86\xac\xc1\xec\xda\xff\xca\xfa\xd3h\xb1\xf75|\xf8\xf8\xee\xec\xe3\xec\xbb\xbf\xcd>\xbd\xbf\xbc8;=\xff\xfe\xfc\xec\xdd\x1b\xe7_\xbb\x85e\xb9\x1a\xbc\xad\xd4\xe4\xb1\xad\x1b\xde7\x1e>\x88\xff\xfb\xee\xd1<\xf0r,y{y\xaa\xe3\xdd)\xd6\xedr\x16\xddo&\xde1\x95\xc5\xbb'\x84v\x93R\x16\xed\x04\x10\xd9\xc5IY\xcc\xb9\xca\xbe\xcc\xe6F\xb2\xc8\xc81HG(\xc3\xed\xf7\xa4\x0c\xe7\x18e\x08\xf7(C;I\x19\xdeU\xcaP\xbbA)K\xf0\x9a\xb2\xa8\xfa\xb7m\x89\xa5\xe3\xc6\xab\xa1\xf5;/u\x1f\x1dU\x7f\x83\xe1\xbe)6\x1b\xd6\x88\xaf\xdc&\x04\x94\xf5&\x06\xc0\xcf\xecQ\xae\xaf\xab)]\xd1Dd\xd5\x8d\xa9\x86\xb6f\x9399\xf7,\xee\xd50\x1f\x08t&\xb7\xfcR^G\xe53\x98V\xb3j\xb0\xcbPR\xc3%J+\xc6\xfd\xee\xb9\x88\xb5\xd7\x00\xd8\x9f\xd9\xe3I\xbf\xc3\x95Fq\xc5'\xf8\xc8\x15\x91\xe2\xb0\x8eJqS\xfavL\xc1\xe2\xf4VMfS\xa6\xb6^G:Ep3&Hh\xcc\xdb\xefN\xcf\x7fTA\x8c\x1f\xeae\xdf\xcd\x85\x8f\xb7s\xbem\x98i\xa4\x940\xac\xd4\x86h\x01\"\x93?\xc82\xbb\xc8\xc8\xaa^\xba\xeb\x88\xab!v\xfe \x06\x83\x85\x9a:\xf87\xe0A\xcd\x11\xdc\x9b\xce\x02f\xc0\x19~\x86\xf4\xf1XkO3SC\\]\xb2m*\x8b\x9c\xa4\xbe\x1dl\xa8\xda\xa8\x94)\xc3\x92\xee~\xb9\xd9\x96i'\xd8I\x155\x1b\xbe\x8a\xaf\xe2H5\xb9gr\x15}i\xc6_\x93<\xba\xb9X\xd4\x0b\x80~.`j.M\xa0\xbc\xa4M\xc5\xb2\xe6\xd3D3jr\xe7\xd4\xe0\xb3j2\xe5\xd5L\xcb\xac \x14\x97\xb8\x8d\xd8\x9e\xd95\xb9\xf3k\x123l2\xe7\xd8\xa4e\xd9$\xe6\xd9\x84\xfap\x97\x81\x83\xcd\xb4\xc9\x9ck\x83\xca\xb6\xc9\x98o\xb3o\xc6\xcd\xa4\x9c\x9bLY7S\xf2n\x02\x85\xa17\n;@\xee\xcd\xe1\xb2o\x0e\x92\x7f\x93\x96\x81\x93=\x07\x07\x9b\x85\x935\x0f\x07\x9f\x89\xa3\xa6\x11 \xb98\xe9\xd98\xd1\xa1\x10\xb7%\xd8\xde\x199\xd1\x05\x01\xd4\x84\n\x91\x97\x932\xebJ\xce\xcd \xbd\x04\xd1\x9b\x80\xe1\xea\x971C'%G's\x96\xce\xb4<\x9dP\x0fBm\xfc51W\xc7S\x1aGm\xfa\x95'_\x07\x9dt\x82\xc8\xd9I\xca\xda\x89\xed\x993%s'V\xa6\x97\xe0\xcd\x94\xbf\x93\xeeL|\x0eO\xacm\x13\xf2x&f\xf2\x84H\xe8l\xd9<\xe8|\x1e\\F\x0f6\xa7\x07\xe1\xe5\xf4\xbc\x9e\x94\xcc\x9e\xf06^Y\xb2{\x12\xf3{\xf6\xcb\xf0\x8994!\xcb\xe7\x00y>\xd1\xday{z\xbel\x1fD\xbe\xcf\xf4\x8c\x1fOq<\xba]W\xd6\xac\x9fX\xde\xcf\xc4\xcc\x1fOY\xf1m\xba\x10\xd9?\xe1-\xbaB\x1bt\xe5\xce\x01\xca\x9e\x05\xe4\xcf\x03\xca\x99 \x84\xc9\x05J\xcf\x06J\xca\x07\x9a\x90\x11\x94\x9a\x13\x14\xd9t+\\;l\x96\x0663hBnPbvP\xa0\xb9S2\x84\x89\xed1X\xb4m=/\xe5\xca\x90\xde\x1fu(\xfe96l5\x15L\"\xa3\x0e\xadM>\xc0\x82\xdd\xb1\x95\xe8arI\xbd\xe0\\\xc2\xe0\xdd\xc4\xcf[\xa0\x85\"\x00\x0f|`\x1a\\\xf5;\xb6,\xab\xef\xc4\xe8u\xd4\xfd\xed\xacZ\x8c\xferz\xcb\xe6\x9f;b{\xd7\xc4d\xc1\x1c\xfb\x8e\xad\xca;\xd6\\=\x04\x16\x0e~(8k\x8e\xecX~\x0bk\xc5M\xfcc\xcb\x1a\xf1\xd9\xa2\xf2\xf4\xf8-k\x99\x1e\xe8\xf6Y8\xd5sKUOF\xa5\xc4u\xa4\x1e\x8b\x1a\xd7VR\xd2\xf8\xbe(y\xeb %\xc8w\xfc\x83^E\x9dK,\x8d+\xe5\xc2B\xf3j\xa8\xea\x8a;\xbeS[\xf1\xc7\xac\x95-@3&\xbd\xf4I\x1f\xab\x16\x13V\\e\xdf:k\xfbvRu\x95\xd6\xa3k\xcd\xa7\\Kr\x90\xb3q\xb5\xc2\x9dz\x10\x96\x97\xd2\x1b}T^\xfd\x93'\x05\xe5u!\xe3>lwo\xf5\x17\xbf\xb6\xe3K\xf7n\xc8'\xb2\x7f\x9c\xfc\xaaP\xc5\xff\xd4Z\x8c\x9e\xdd\x91%+\xf4s\xc9o\xaf\x1e\xdan\x87d\xdd\xc5\xd4\xaa\x9eF\x18\x80?t\xab\xaeCI;'\xd8\xf1/\xc7\x7f2\xaav\xe3\xbd\x95\xedK\xeaC\x9eFF\x12\xbf\xbf\xb2]E\xdacY\x1a\xe1\x13\x84O\xf4F\xf8\x04\xe1\x13\xbd\x11>\xc1 \x9fp\x1b\xe1\x13\xc6\x08\x9f |\x82\xf0 \xe4,\x89\xf0\x89\xce\x08\x9f\xb0\x8d\xf0 \xc2'\x1cF\xf8\x84\xf3\x18\xc2'\x08\x9f\xf0\x18\xe1\x13\x84O\x10>A\xf8\x84e9B\xd9\x84OH#|\xe2\x8f\x80O\xecl\xce5\xf8\x88\xd4R!:J\xa8\xff\xa5\x07?\x15_3\xbb\xf4\xd9\xa3PY\xbd\x19\xaf\x86{\x19\x0c\xcf7\xb9[\xae\x9f6K\x9e\xb6\xd1\x1em\x96|@\xe7\xc6\xb7\xf9\xa5\xbd sx\x916K\xa6\xcd\x92\x7f\x9b\x9b%\xa7\xe3/\xbf\xde\x16\xedm\x04{\xb9z\xb0`\x17\xfe \x9eR{\xdb\xcb1\xb2\xf2\xc4\xfb\x9d\xe2A\x95^#\x84\xf0\x14\xc2S\x08O\xd1Fx\n\xe1)\xbd\x11\x9e\xc2 Oq\x1b\xe1)\xc6\x08O!<\x85\xf0\x14\xe4,\x89\xf0\x94\xce\x08O\xb1\x8d\xf0\x14\xc2S\x1cFx\x8a\xf3\x18\xc2S\x08O\xf1\x18\xe1)\x84\xa7\x10\x9eBx\x8ae9P\x01\xc2S\xa4\x11\x9e\xf2\x87\xc0S\x8a\xd6\x1e\xa2\x86Y\xdb\xe2\xc7.T\xfe \x8f\xedx\x94#`\x95J\xfa\x96]\xec\x96=\xe8\xa7\xf8\xf8\xa5U\xde\x9e\xa4\n\"\xb0\xb7\xdd,\x9bb\xc1\xba\xe8\x9e\xdcc\x83-f\x9bUQ\x9d\xfc*\x1a\x19\n\xf3\xbdUG_\xac\x8aJoL!\x1ac\xf6\x1fZ=\x82.\x0e\xf4e@\x14+\xe6\x11%o\xa5\x07\x9d\xd1@\xab\xd4\x17\xa6\xd1O\x11\x13\xe4\xe9\x917\x85\x1c%\x85\xbd|\xf0\x90\xb1\x00\xe0\xa4\x90&\xfd\xb7\x82\xeb\xc7]\xfc\"\xfdz_\xb4\xc6\xe1\xc3\xb7Zxe\xe3\x7f\x89\xfeh\xf9|\xca\x96\x08\xb2\x8c\x13\xbb;|\xbc8\x1d?\xf3\xb4G\x02EQ\xa3K\x88\x98U8\xa0(*EQ\xbdGR\x14U\x1aEQw\x8d\xa2\xa8\x14E\xf5\x19EQ)\x8a*\x8d\xa2\xa8\x14E\xa5(*EQ\x95Q\x14\x95\xa2\xa8\x14E\xa5(\xaa\xcf(\x8aJQT\x8a\xa2R\x14\xd5\xb2\x1c\x11-\x8a\xa2J\xa3(\xea\x1f!\x8a*\xfe\xd7*`\xf0 )\x9fD\x1d\x89\xb2\x17\xa1MdQF\xbeLTU\xbc\"\xc6\x99\xa2\x19\xe3\xa7\xffK\xa7Yz\x83\xa7[~[7%\x7f\xf4GL?*\xd1t\xd5\x84\xb9\xca\xcdU_\xad\xe6\\\xb9\x14WW\x8b\xed\x9c\x9b\xb0\xa9\xf9fD\x88\x82\xff\xabS\x12\xfc\xad)\xfc\x85q\xc23\x8d\xa7\x16\x8bE\xc3\xda\x94\xad*R|\xa2\x8b*\xf9\x8a\xbd\xd1aO\xe3\x98x\xd0S\x878G\x9e\xa4(&E1\x1d\x86Y\x05\x03\x8abR\x14\xd3{$E1\xa5Q\x14s\xd7(\x8aIQL\x9fQ\x14\x93\xa2\x98\xd2(\x8aIQL\x8abR\x14S\x19E1)\x8aIQL\x8ab\xfa\x8c\xa2\x98\x14\xc5\xa4(&E1-\xcb\x11Q\xa2(\xa64\x8ab\xfe^\xa2\x98\xa9\x11A\xbd\xde!\xd3)\xfdA\xc1Su\xd4 \x8dR\xdd2\xf9\xf7A\xf6\xa43c\xd2*@\xff\xfcl#|\xbd'l\x1bTE\x86t\xcb\xb8\x0bz\xf3\xd6\x03\xa2\xe1\x1e\x19\x81v\xde\xf7\xe8\x1a\x07f\x91\x00\xe0\x92q+z\xad\xd3'M{t\xbcE\xfedF\x00\xb9r\xe3\x99F\x81}\xb27\xbc\xa3\x97\xfa\xba\x05\x81\xfa\x86\xdfk5\xe2b\xb3Y\xa9/-\xb9\x08R\xac\xe0\xab\xbaz\xad\x0b\xf4\xf5\xffy\xbd^\x17\xd5\xa2\x85\xc5V:#P59\x90\xc3wlYVr\x87g=\xd5\xea\xa7P\xdd\x9d,=\xc9\xaa\xbd)\xa5\xe8b\xd5\xca0\x82\xb7\xb5\xbc\xfe\xff\xd9{\xf7\xee6\x8edO\xf0\x7f}\x8aX\xef9#\xe9\x0eE\xdan\xb7o_\xedz\xcf\xd2\x12\xed\xe6\\Y\xd2\x92\x94\xbd\xbds\xeeRE A\xd4\xa8P\x05W\x15(\xb15\xf7\xbb\xcf\xc9W\xbd\x90\x8f\xc8DB\x82\xad\x88?\xba-\xa2**3\xf2\x1d\xbf_D\xc2\x9c\xb5l\xd6\xc2\xfb%\x13\x0e\xb6\xac\xaf\xb26\xc5,+a\x99\x95\xf3\x82A\x06\xb7\xf9\x1d\xb39\x1d\xbb\x86\x11>v\xdb7u%\xa4\xcaZ\xbal\xda\xae)\xf9Zq\xc3X)\x10\x04C\xd2\xe7^tA\x8f\xbaT\xe7v\xa7\x9er\xe8\xf7\xf9\xc0\xbbZ\xe6\x0dT\x9b\xf6I\xb5x2\xcfZ&\x13i\x0flm\xd1w\x95\xaf\x18T5\xfc]\x07\x19\xdb>\\\xb3l\xb6\xe4K\x92\xdcn\xf7\xdf\x15\x05f\x1f\xf2\xd6\xd4\x84m\x1e=\xb2\xb4\xef\x90\xd7\xe6 \xd7\xb3\xc3\xf8{\xce\xd7\xd9Y\xd6\xb2\xf9SYc\x01\x06t\xd4\n\xe9\xd4\x17\x8d5\xef\x9e<\x16OZ\x14\x8e\xde\x87\xa2\xba\xcdg6\xc3u\xfd\xa0f\xab\xea\x8e\xcdaQW+a\xc2\xcb\xe7\xffn=\xf7\x8b\x8dI\xde\xa8}\xabrb\x0b\xbf\xf0Q\x07\x8ft\xb3E\xbb\xac\xab\xf7\x86)\xd1\x1e\x9b\x0e!-`\x8eQ\x87\xa9\xf5\xff\xa7\xcd\xfaW\xfd\xbd\x1c\xa3\x80um\xbe\x95r\xed\xaeY\xcd?i\x9f\x0c^\xa9\xa3\xda\x9c\x9f\x8cE;*\xc3L\x92\xc3k\xc9\xcbE\x15YwE\x10\xb1\xf6(\xe1\xab\xea!\xa8\xde\xa3\xdeMm\xe5\xa2R\x98\x8dJ\xcao\x83l\xaa\xf2\xc9l\x99\xd9\x11\xe8f3[\xca\x0c\x11\xb7y+\xe6\xe1\\]c!<\xe1Y[\xd5\x0d\xcc\x04\xf0\x9cm\xdaj\x95\xb5\xf9\xcc\x01i\xea\x02\xb6\x95\xe1\x01\xbd\xaa\\\xcf\x8a\x9c\xefY\x9a6k\x9d\x03\xd8J\xb0\xf0\xad\xb9\x80 Y \xda \xd0s\x00\xc4R-\x1c\xfa\xb6\xbc\xed\x0e\xe7SZ\xba\x85\x97p\x91\x9ar\x81']$\xa2]\xc4\x11/\x1c\xea\xb8A\xd1\xd4\x8b\x9d\xc9\x17\xa9\xe9\x17\x81\x04\x8c\xc4\x14\x8c0\x12F \x0d\xc3\xd5\x87;\x82\x06\x96\x88\x91\x98\x8a\x81\"c$\xa4c\xecJ\xc8\x88\xa2d$\"e\xc4\xd02\x1c\xca\xe4\x1e\xcbK\xcc\xd8\x0b5c\x7f\xe4\x8c\xbd\xd03\xc2\x08\x1a\xc9)\x1aX\x92FR\x9a\x06\x9e\xa8\x11L\xd5\x08'kx\xa7\xc2\xc7\x08\xbaF\x02\xc2\x86\x87\xb2\x81\xdcP!h\x1b!\xbb\xae`\xea\x86k\x11\xbc\xa9\xee\x18\x82\xbc\x81-_B\x02G\x08\x85#1\x89#\x8e\xc6\xe1\xeaA\x8d\x9f\xc8\x11M\xe5\xb0h\xe3_\xf3\x919R\xd19\xd0\x9c\x04\x04\xa5#\x88\xd4\xe1\xc1`\xa3\x88\x1d>\x9dV\x80'\x11\xbd#\xdc\x98x\x8a\x87\xafn\x114\x8fH\xa2\x87\x0b(KF\xf6@\xd3=p\x84\x0f,\xe5\x03a\xe5p\xdaG\x08\xf1\xc3E\xfdHD\xfe\x08\xa4\x7f\xecF\x00\xf1\x194\x80\x04\xb2\x07\x1a\x88\xb7t\xd6\x9e\x9e\x8e\x0c\x82\xa0\x83\xc4\x13B,\xea\x84\xd3\xd1A IL\n\xf1\xd1B\"\x89!\x16]\xf2d\xe8:\x1c#\xc8!.\x04\xdbE\x10IO\x11IN\x12\xb1\xd3DR\x12E0T\x91p\xb2H\x10]$\x820\x12J\x19q\x92F\xdc\x10>\x1e\xc4\xc7\x12G\"\xa8#\x81\xe4\x11Guc\x08$\x16U\x03r\x06nH\xe0H$\x8e._\xde\xbai$I\x89$\x1e*\xc9~\xc8$\xa9\xfab\x00\xa1$\x84R\xd2\x93J\xa4 \x924\x0fh\x1e\xfexe[\x92\xe6!\xd9\x84\x924Sx\xb3\xf1w\no\x1e\x08\x857Sxs/I\xb1\xb5\x10d-\x08W\xa3\xf0\xe6]\xd1\xb4\x08,- \x92\x16\x8e\xa3Qx\xf3.\xf8Y\x08z\x96\x18;\xc3!g q3,jf\xd8\x10Sx\xf3X\x108\x19v\x97\x14\x8c\x91Qx3\n\x19\x8b\xc1\xc5(\xbc\xd9\xf6\x98\x17\x0b\x0b@\xc20\xc1\xbb!(\x18\x857Sx3\x06\xeb\xa2\xf0f!\xbb\xa0[\x14\xdel\xd2\xe4\xc5\xb3b\xd1,\xeb\xda@\xe1\xcd\xdbB\xe1\xcd\x11\xa8\x95\x1f\xb3\nE\xac\x02\xf0\xaa`\xb4*\x0c\xab\xa2\xf0\xe60t\x8a\xc2\x9b;\xd9\x07\"\x95\xa2\xcf\x05\xa0Qx,*6\xbcyU\xcd7\x05\xbbV\xbe\x97\xc6\x1e\xe1\xfc\x8bx\xf0W\xf5\xdc(\xc8\xb9\xc8\x1b\xe14\x95\xba\xb4\x1f\xa7\x91\xa1r\"\x06I\xcf[\x88T\xbf\x7f1\xa6?\x1e\x7f]=q\xb0\x11\xd2F\xa3\x0e\xa5\xfd\x0c\xa0\x91=L\x1a\xe5-Q!u\xe58\x81\xb7\xaa\xaa\xf1\x1dU\xfd\x1d\xbe\xa9=4\x1b{\x0ccW\xb0\x19o\xcb\xb2\xd94\xd3\xd8ig)1!\x90\xa3\xbe\xd7\xb9f\xf8\\\xab:\xbct\xe65\xdbE\xb0\x9c\x17\xcc\xfd~\xebQ\xbf\x8bg\xd2\xcf\x84O}:\x1a\xa5\x1bUE7\xb3\xbc\xeeKiP\xa85\x8d\xf7\x19\x08\\{<>\xe3\xa1\xed\xc98\x9f\x9d\xfa\xde{\x84\xbd\xad7\x1e\x80\x1d\xc0|W\xab/\x92?\xdb\xb4\xcb\x7fv\xa8\xfbm\x9d\x95\xad#|\xff\xe1\x05k7u\xd9t\xf4\x81\xb7\xa7\x9bvY\xd5\xf9?\xa5O\xfe\x08\x84\x06\xe9\x0d\xe4\x16\x91\xffd|\xcb\xd2\xff\xb3>6F\xe7\xff,>\xfe@\xd7\xfd@\xf1\xda\xa1\x89\x86\xd2~\x06\xb0,\x1b\x1a\xdf\xed.r\xdc\xd1\xea\xff\x0e \x809\xc0y\xa6\x00\xe9\xe0\xd1\x12\n\xd29\x95\x99=5\x8e\x83Kj\xb0\x0e\xfc\x80\x1dD\x80v\xee\nd\xed\x12\x0d\xdcA*\xf0\x0e\"\x01<\xa7\xc2\xc0\xdb[w\x06\xf2 \x18\xccs\xaaR C\x10\xa0\x07\xa9A=\x08\x04\xf6 \x14\xdcs\xf7\xec\x88\xbb\\\x13\x83|\x80\x03\xfa %\xd8\x07;\x03~\x10\x07\xfaA*\xe0\x0f\xa2\xc0?\xf7p\xc0\xde\xed\xba\x17\x10\x10\xf6\x08\x04\xc2~\xc0@\x08\x04\x04!\x0e\x14\xf4M\xc18`\x10\xd2\x82\x83\x10\x00\x10B8H\x08\x11@!b\xca\xc4\xdd\xf8\x9a\x000\x04\x1fh\x08\xf8\xed\x19\x02<\x84\xc0]\\0\x88\xe8\xd4\x86\xbf\xff\x15_\xca\x84\x80\"\x04\x81\x8a\x90\x1aX\x84Hp\xd1\xdd\xafPw\xc1F\x83\x8cV}-\xea>\xd8T`#\xe013\xc0\x80\x8e\x10\x06<\x82\x0f)\x88\x04 \x01\xa1\xd7\xe1\x8cL\x04FB\x94q\xf1\xa0$ j\x19\x01NB,@ \x9e\x1b\xf2\x92\x01\x95\x80\x07+\x01 X\x02\x1a\xb4\x04\x9c\xd5\xc3\xc1K\x08\x020\xc1s{l\" \x13B\xc1L\xd8\x11\xd0\x04\x84y\x03\x80M\xd8\x07\xb8 \x982:FB:\xa0\x130`'\xec\x00xZ\x15\xb6\xde[e\x13\x03\x9f\xe0\x05?!\x16\x00\xb5j\xf3\xdf.\x8b\x02B\xc1s\xc3\xac\xfb\x8e\xd9\x18P\xd4\xaa\xcas\xfbl$`j\xd5&\xf7\x81\x0e\xafY:\xe0\x14P\xe0)D\x00\xa8\x10\x06\xa2B\x0c\x90\n\xc1`*\xf8\xee\xa3\xf5\xdd\x02\x8a\x07\xb9\xb0\xc0*\xc4\x80\xab\x10\n\xb0\x82\xbb\xe21@\xabU\x19\xe2v\xda8\xc0\xd59 \xfc7\xd4&\x05^\xc1\x07\xbe\x82\x1b\x80\xb5\xbe\x13\x0b\xccB\xc2\xbe\x1b\x00\xd0B\x10H\x0b[\xf7\xd8ja\x1f\xd6y\x8d@\xa8P\x84\xe6y\xd6\xb2'm\xbe\xb2YYE::\x0e\xfa\xfcex\xbfde\x0fH\xca\xee(\xca)\xd30\xeb\xee\xb9\xae7%\x9b\x1f\xc3\xb9\xfd\x80Zn\x8a\x82\xf7\xf1\xa1>{S\xce+\xd6\x94\x0f[\xe9\xc7\xccdYz\xfb\xc0#\xe9z\x9aU\xe5\\\xf9\xb1]\x04\xfd\xb7#\xf8\xcf\xd1YW\xd9=d\xebu!|\xa3y)\\02\xc8Y\x15\xd8\xb4\x01\xc5\xe4\xb4\x16\x88-\xdc\xe6w\xac\x815\xabWy#\x83\xc8\xdb\n\xd8\x076\xdbX\xfcG\xfc\xbbj\x8f\xa76Ere\x1cX\x82[f{a\xf3;sF&\x99\xa4\xb2\x96\xc8m\x87P\xf3!\xdb\xc3\xd3\x06]\x1a\xac\x9e\xfc\xb4\xcen\xf3\xd2\xd2\x9fG\x05\xec\x1f\x94\xd0\x04\x13n\xa5\xc1_u\xdc\xb8 \xad\x96\xd2\xba\xb0[7j[\xb2\x0f\xed\xf5;vo\x0f\xb3t\x0e9\xaf\x13\x10\xd3?\xfaRh\x86\x04\xffO\xe5G\xcf\x9aF\x02\x07\xaf\xb3[v!\xc9\x15\xc7\xf2w\x8b2\x99(\xa1\xd5\x91\xf6k\xbe\x8e\xad\xaa\xa6\x05&\xbc\xd1\xc2\x8d\xddG\xda\xdf\xe8x\x8e\xdc6z\xf9`c\x02\xc6(+XU5\xd3\xb0\x85iK\xd5Vmf\x01\xbf\xd1\xc6td\xa0\xf7M\\\xe2\xf3\xc2\x8a\xe2?\xca\xcd\xeaF:Ku\x98\xd5 \xa6\xc7V\xdf\xa1\xa1g\xd5\xa6l\xaf\x852\xdb\xc4\xf1>k\xa0a\xed\x91H\x01\xa0@\xa4F\x90[xg\x9eK?\xf9\xfb\xbc\x19\xf7\x0fD\xfc\xb8$z\xc4\xc7\x8b\x9f\x8e\x079\x05\x86+\xa1\xc0p\n\x0c\xef\x85\x02\xc3)0\xbc\x97\xa4\x1c\x92\x10\xfeH\x10w\x84\x02\xc3w\xe5\x89DpD\x92\xf0C\xc2\xb9!\x14\x18\xbe\x0b\x17$\x84\x07\x12\xc1\x01\xa1\xc0p\n\x0c\xa7\xc0p,\x87#)\x7f#\x86\xbbA\x81\xe1\xb6\xc7\xbc\x1c\x8d\x00~\x06&\xec9\x84\x97A\x81\xe1\x14\x18\x8e\xe1XP`\xb8\x90]x\x14\x14\x18n\xd2\xe4\xe5J\xc4\xf2$\xack\x03\x05\x86o\x0b\x05\x86G\xf0\x1b\xfc\xdc\x86P^C\x00\xa7!\x98\xcf\x10\xc6e\xa0\xc0\xf00\xbe\x02\x05\x86wB\x81\xe1Jt`\xb8\x02q\x07:v\xcee>U\xce\xf6\xa3|\xd5\xdc^\xb7\n\xf4\x18\xbc\xe3:\x04\xbfR\xde\xd3\xa3\xd1\xcbG\x92\xee \xa0<\xd1\xa1%\x8e*\x96Y\x85\x89\xaf\xb2v\xb6\x9cv\xec\xdb\xfc\x8e\x95\\\xd3\xd6\x81 \xaf\xd4\xcc<\x12Fz\xf87X)\x90\xd7\xaa\x9e\xb3z\xea`\xf4\\\xde\x9f\xc2\x1a\xde{LL\xf9TN\xd4>\xe1\xe4\xa3\xfa\x0f\xd7\xbd%?\xcbG$GF\x99c\xc8b{+~\x19\xa7]\xe1\xc3Z\xa9\xd6\x8d?j\x0f\xf3\xc5\x0f\xdf\xdb\xb3\xb0\xe8\xef?\xd06\xa2d,^\x82\x8a\xdaiF\x833\xaa\x01\xa3\xdf\xa7d0\x94\x0c\x86\x92\xc18dG\xf2\x0e\x04\x13x\x9c\xaa(\x19\x0c%\x83\x89%\xf9@\x1c\xd1\x07R\x91} \x8a\xf0\xe3\x1e\x0e\x94\x0c&\x8c\x00\x04\x81$ \x88#\x02\xf9\xa6`\x1c\x19\x08\xd2\x12\x82 \x80\x14\x04\xe1\xc4 \x88 \x07!\xa6LJ\x06#%\x988\xe4\xd4F\xc9`(\x19\xccD\xd2\x10\x8c\x00\xcf\x93\x01\x0c\xd1\x08\xc2\xc8F\xe0c\x07D\x92\x8e\x00\xa1\x97\x92\xc18$\x8a\x94\x04\x94\x0cFI\x14a \x82HK@\xc9`0d&\xd8\x07\xa1 0e\xa4d0i\xc9N\xe0%\x8e\x81\xe2\xdb\x19\x832\xb5\x98\x8e~ADKt\xe7\xde\xf8H\x89\xfe(NQ\x12J(J\x82\xa2$z\xa1( \x8a\x92\xe8%\xe9i(\xe4,\x14t\x12\xa2(\x89]\xcf?\x11\xa7\x9f$g\x9f\xf0\x93\x0fEI\xecr\xe2 9\xef$>\xed\xe0\xce: O:\xd8sN\xe0)'\xf4\x8cCQ\x12# >\xd5P\x94\x04EI\x00EI\xf4BQ\x12\x14%AQ\x12\x14%a\x13\x8a\x92\xa0( \x8a\x92\xa0(\x89\x81\xa4`\xacS\x94\x84\x10\x8a\x92\xf82\xa3$\\\xa7\xc8XZ_G\xe0\x1bi\xdbb\xba%\x0b\xd0PL\xbd\xa0Z\x05\xf1\xfaFU\x1b)\xf3\xb1\xf9v\xac\xe3\x0e\\\xde&\x8c\xc2\xdb_\xaf'\x1c\xe9\x9a\xa9\xdb\x88\xbd\x80\xb2\x91\x9b\xb4{\xf0\x17\xe3\xf5\xa69\x0c\x0c\xcf\xc9\xccE\xf9\xaf0. \xd8a [\x15\xfa\x19\xba\xbb\xdf\xdd\x17Z\xbd\xa0\x11m\xd5\x15\xc5\xd5\xf5\xb2u\xf1uI\xc8\xd8\xf5rv\xbd=\x18\x10\xbd\x18\x10h4\xe0\xda\x1b\x02\xec\x04\x11\xc8\xb4S\x99\xd9=\xe98\xad\xa7F\xa8\xc1\x8fRC\x04R\xed\xae\x00]}\x18\x8b^C0\x82\xedTEW\x1fF\xb1{\x13#\xdb\x80C\xb7!%\xc2\x0d;\xa3\xdc\x10\x87tC*\xb4\x1b\xa2\x10o\xf7p\xc0\xb2}\xf7\x82|\xc3\x1e\xd1o\xd8\x0f\x02\x0e\x81(8\xc4!\xe1\xbe)\x18\x87\x86CZD\x1c\x02Pq\x08G\xc6!\x02\x1dGL\x998\x0ep\x02\x94\x1c|H9\xe0\xb7g\x08\xc4\x1c\x02wq\xc1\xc8\xb9S\x1b\x8e\x11\xec\xcb\xc6\x9e\x94\x15l\xe6\x05\x835Xt0{\xf2\x7f>l\x02\xc3E\xe9\xfa\x02!\xae\xec\x1c\xbd\xd0\xf5\x05@\xd7\x17\x0cdL\xc8\xdf\xe1\xee\x82\x81g\x8d(\xf9J\x88\x92\x9f\xc6\xe1\x15\xea\xf8\xd0\xce\x0d\xabB\xac\xd3#\xa9\xc3\x83(\xf9D\xc9\xef%\xa9##\xc4\x89\x11\xe4\xc0 J\xfe\xae\xce\x8a\x08GE\x12'E\xb8\x83\x82(\xf9\xbb8$B\x9c\x11\x11\x8e\x08\xa2\xe4\x13%\x9f(\xf9D\xc9o\x89\x92\xdf\x0b\x86pN\x94|\xdboD\xc97>C\x94|\xa2\xe4[\x84(\xf9D\xc9'J>Q\xf2\x07\x92\x82\x1eM\x94|!D\xc9\xffr(\xf9CK\xee\xc8\x19\xa7\x8b\x0bb\xb3\xc2\xd3\xc5\x05{4\xae?\xe5>]\\\x90\xc2\x8atq\x01]\\\xf0\xa5\\\\\x907\xcd\x86\xcd\xfb[\x0bP\x81N?\xde\xff\xac\xc2a,\x11O\xb7\xf9\x1d+\xf9h\xceJ\x1dK\xa2\xb4\x8d\x1a\xe2\xa1\xd1$\xdf?T\xcfZ\x82\xa4\xba\xaf?\xd0&\xa2h)!>z\x05EK%\xad\x1eEK\xd9\x95Q\xb4\x94\x07\xd9\xa6h)?\x91\x08R\x91\x89 \x92P\xe4TH\xd1R\xbb\x93\x8c \x90h\x04\xa1d#w\xcf\xa6h\xa98\x02\x12\xc4\x91\x90 \x15\x11 \xa2\xc8H\xee\xe1@\xd1Ra\xe4$\x08$(A\x1cI\xc97\x05\xe3\x88J\x90\x96\xac\x04\x01\x84%\x08'-A\x04q 1eR\xb4\x94\x94`R\x93S\xdb\x9f\"ZJ,fb>\xbba\xac\x04\xe9\x14\xb0\xdc7\xa8\x85b\xa4\x84P\x8cT\xff\x13\xd6\x98\x14#5\x89\x91\xea\\Z)\x82\xa5z\xef\xdc0jjZ;\xb3\xeb\xad{\x88\x82\xab&\xbfRp\x95\xafO\xf7\x12\xea'\xd1\xbe\x10\xabB\xac\x8f$\xa9\x7f\x84\x82\xab(\xb8\xaa\x97\xa4~\x8f\x10\x9fG\x90\xbf\x83\x82\xabv\xf5mD\xf85\x92\xf84\xc2\xfd\x19\x14\\\xb5\x8b\xff\"\xc4w\x11\xe1\xb7\xa0\xe0*\n\xae\xa2\xe0*\n\xaej)\xb8\xaa\x17L\xe8\x10\x05W\xd9~\xa3\xe0*\xe33\x14\\E\xc1U\x16\xa1\xe0*\n\xae\xa2\xe0*\n\xae\x1aH\x8a@\x17\n\xae\x12B\xc1U_Np\xd5\xf0\x80G\xc1U\x14\\E\xc1U\x14\\\x15gE\n\xae\xa2\xe0\xaa?ip\xd5m]m\xd6'w\xdf\xc8\xff\xb8\xce\xcbEu\xf2Q\xfd\xf7\xdc\x15Y\xf53\x7f\xe6\xbc\\T\xa2\x849k@\xbc\x05\\C\xcfLU\x7f\xeb2\xe0\x8e\xc2\xa3:\x15\x0ft\xd5\x0e4(\x8a\xd7\xc9C\x18\x13\xd5Vsgo\x1a\xcd\x12\x13fHJ\x11\xcb\xe7\xfb\xe33\x8d+6\xd7\xd5\x92D\x088\x7f\xaeg\x1cK\xad\xf8\x89o\x95\x1b\x18v\x80)\xdf\x98\xee\xc7\x15u!K39\xdfMB\x97D)\x1e6\xf2YSiV\xac\xcd\xe6Y\x9b\xa5(\x90\x15\x1a\xd1\x1f\x11\x9b\x93\xf2~\x00@t\xbf\xf0\x05\xaem\xb3\xd9R\xc2\xa3v\xa0\xd3jW\x05\x1cFV\x04\xd1\xf2>&\x9b*\x80\x9e\xf1\xe5\xd4\x98\xcd\xde\x0d\x91\xce\xack\x91\x15?A\xd5\xcd2_[\xd45m\xbd\x99\xb5\x9bZ\xae\x83\xb6\xc3\xc8{\xb9O\xadY\xf6\x0e\xd8\x87\xbc\x11\x11x|tTMV4\xc7\xf0\xdb\x92\x95|\xc6\x16vW\xdf\xb4\xa9b\x02\xad\xcb\x1bU\xe0\xf9\x91\xed\xa3\xd5P\x9dhTq\x14\x14\x8c\xb1Uu\xc7\xeb\xbe\xcc\x9b\x81A,z\xf2rV\x0b\xc8OD\xd4\xcd\xc5\x96\xc7\xf6\xcdY\xb6iX_\xb3~\x16\xad\x8a9\xab\xf5\xb7T\xc7\xcf\xf5\x84k\xa5\\.\xb2\xdc\xc4\xb4\x15+\xfa\xb5\xb4DdO\x1a\x0d\x89\xa1>=T\x9b\xcdj4\x0f\x9b)\xcd\xc6\xbeWwQ\x08\xa4\xc4\x14\xcb\x93\x08m\xcc3\xe8\n}W V\x88\xfaU,k\xb8\x8c\xa8\xb7\x02\x9bW\xae\xd0\xaf\xed\x95u\x93\x88\xa4\xec\xa1\xba\x08Z\x11\x8eS\xa4\xd5q{\xd9k)H.V\x02\x86\x14d-qd\x0c)!\x16\xd1E\xf4\xd32\xd4\x08\xb5\xf32\x06\nc]\xaf\x83\x85\xa2\xdfG\xe9\xb3G!\x81\xa1e\xbe\x86\x1b\xd6\xbeg\x03\xb2HV\x9a\x0fU\x99\xb5\x89\x0cS\x91\xe2\xa1v\xe3`L\x89\x93\xde[\x99\xd5^\xcf\x90S\xb5\xbb\xe4\xc3k\xc5\x91|\xc0'V\xb5\xa6|x\x94\x0f\xef\xcb\xc9\x877\xdc):x[\x93'&\x9a\x88\xd0\xd5\x0b\x11\xba\x88\xd0\xd5\x0b\x11\xba\x88\xd0\xd5\x0b\x11\xbaZ\"t\x99\x85\x08]Z\x88\xd0E\x84.\"t!wID\xe8\xea\x84\x08]C!B\x17\x11\xba\x0cB\x84.\xe33D\xe8\"B\x97E\x88\xd0E\x84.\"t\x11\xa1k )\xc85D\xe8\x12B\x84.\"t\xc5\x10`\xf6E\xe8\xa2Dy\xb1Y\xc8(Q\xde\x1e\x8d\xeb\xef\xa3\x94(/\x85\x15)Q\x1e%\xca\xfb2\x12\xe5\xad\xab\x82O\x82\xcd\xf5\xcd\xfd\xb5\xc8)\xd8\x1d+\xa0*\x9f\xcc\x96\x99%R4/\xa5\xb1x7\xe7\xdd2\xf3@\xb4\xfe\xa2\x8dY\x10]\x02\x9f D\xdc&(V8\xbd\x7f3\xa82\"\xe0\x94\xd1G\x08e\xf4\xe9\x7f\xc2\x1a\x932\xfa\xf4\x19}&\xdc\xaaIf\x1f\xd3k'\xa6\xf7(\xb3\xcf@(\xb3O\x1a\xceQ(\x8dC\xfb\xbe\xad\n\xb1\x14\x8e\xa4\xf4\x0d\xca\xecC\x99}zIJ\xcb\x08\xa1d\x04\xd11(\xb3\xcf\xae\xd4\x8b\x08\xdaE\x12\xcaE8\xdd\x822\xfb\xecB\xaf\x08\xa1VD\xd0*(\xb3\x0fe\xf6\xa1\xcc>XZDRJD\x0c\x1d\x822\xfb\xd8\x1e\xf3\xd2\x1e\x02(\x0f\x98\xbc5!T\x07\xca\xecC\x99}0\xb4\x05\xca\xec#d\x17j\x02e\xf61i\xf2\xd2\x0fb\xa9\x07\xd6\xb5\x812\xfbl\x0be\xf6\x89\xa0\x0c\xf8\xe9\x02\xa1T\x81\x00\x9a@0E \x8c\x1e@\x99}\xc2(\x00\x94\xd9\xa7\x13\xca\xec\xa3D\xa7^\xc8\x06\x91\xfb\xe0\x0e\x86\x1e&\x05\x18\x87Bo\xa3\xd1;\xa7\xf7\xa1,>q)R(\x8b\xcf\x1e\x8d\xeb\xcf?CY|RX\x91\xb2\xf8P\x16\x9f//\x8b\x8f\xfc \x7f\xb3hO\xf5\x10\xfft\xe4\xf3\x91\x7f\xe9o_s\xa5\xf2\x11\xff|\xa0\xebN\xa9|:\xf1\x110(\x95OK\xa9|L\x82\x81e\x80R\xf9P*\x9f-\xf1M9\x80\xe0}\x01\xae)!\xa0\xa5 \x82\x03\xe6T\xd6R*\x1fJ\xe5\x13\xc0\x13\x83`\xae\x98S\x15\xa5\xf2\xa1T>\xb1|2\x88\xe3\x94A*^\x19Dq\xcb\xdc\xc3\x81R\xf9\x84q\xcd \x90o\x06q\x9c3\xdf\x14\x8c\xe3\x9dAZ\xee\x19\x04\xf0\xcf \x9c\x83\x06\x11<4\xc4\x94I\xa9|\xa4\x04s\xd4\x9c\xda(\x95\x0f\xa5\xf2\x99H\x1a.\x1b\xe0)Y\x80\xe1\xb4A\x18\xaf\x0d|D\x94H~\x1b \xf4R*\x1f\x87D\xf1\xdf\x80R\xf9(\x89\xe2\xc6A\x10?\x0e(\x95\x0f\x867\x07\xfb\xe0\xce\x01\xa6\x8c\x94\xca'-\xaf\x0e\xbc\xdc:\x88\xe5\xd7Y\xb5Q*\x1f\x1c\x1f\xcf\xaa\x8dR\xf9 yz\x10\xcc\xd5\x03J\xe5c\x94\x18\x1e\x9fU\x19\xa5\xf2\xd1B\xa9|\x0cB\xa9|\xbe\xf8T>Y\xd3T\xb3\\\x9c\xee\xc5bg\x1ed\xdd\x06\xcf\x08\xd1Sf\x1f!\x94\xd9\xa7\xff kL\xca\xecc\xca\xec#\xfe\x19\x91\xd9G\xd2\xbd(\xb3O/\x94\xd9'\x0d\x05)\x94\xd5\xa1]\xe1V\x85XFGR6\x07e\xf6\xa1\xcc>\xbd$ei\x8404\x82\xd8\x19\x94\xd9gW&F\x04\x0b# \x03#\x9c}A\x99}va[\x840-\"X\x16\x94\xd9\x872\xfbPf\x1f,K\")C\"\x86\x1dA\x99}l\x8fyY\x10\x01\x0c\x08L\xde\x9a\x10\xe6\x03e\xf6\xa1\xcc>\x18\x16\x03e\xf6\x11\xb2\x0bS\x812\xfb\x984y\xd9\x08\xb1L\x04\xeb\xda@\x99}\xb6\x852\xfbD0\x08\xfc\xec\x81P\xe6@\x00k \x981\x10\xc6\x16\xa0\xcc>a\x8c\x00\xca\xec\xd3 e\xf6Q\xa2\xd31\xe88\xec\x81\x92\x888m\x05O?l\xb61\xe1\x9ds\xfc\x84\xa4\x04\xa1\xe4?\xe07#%\xff\xd9\xa3q\xfd}\x94\x92\xff\xa4\xb0\"%\xff\xa1\xe4?_P\xf2\x9f\xfb\xeb\xbc\\T'\x1fUb\x12W\xd6\x9f\x81\xe5\xa6\x9c4C\xee\x9f{\xc9\xfc\xea\xa2y\xb73\xa0t\xdaLD4K\x8e \xf19\xf5\xc8\xc1\xa6\x07\xe2\x15\xb7q7\xa28e\xce\xdc>^\xf0!q^\x1fwV\x1foi\x10\x9c\xac\x88}\xa2\xa9\xa0\x8el>\xdeR\xa6\xce\xe4\xe3\xce\xe3\x13V\x1c+4\x942\x83\x8f\xaf\x178\xb3\xf7x\xeb\x13\xda \xacUN\x91\xb5'.gO\xb2\x8c=\xa8|=\xce\xc9\x03\xbc\x13\x08 x\\\x88f\x03t\xbb@,\x9b\xcb\xa1/(CORF\x97\x97\xd3\x95\x9a\xd5\x85\xe7u%bv\xc5q\xbb\x1c\xea\x02s\xf2\xec\xc8\xefJ\xcd\xf0\n\xe4x%fy\x85\xf1\xbc\x02\x99^\xae>\x1c\x91\x85')\xdb\x0b\xc5\xf7J\xc8\xf8\xda\x95\xf3\x15\xc5\xfaJ\xc4\xfb\x8aa~9\x94\xa1\xb3\xee\xec\x81\xfd\xb5?\xfe\xd7^\x18`a\x1c\xb0\xe4,0,\x0f,)\x13\x0c\xcf\x05\x0bf\x83\x85\xf3\xc1\xbcS!.\xbf\xce\xce\x9c0on\x1d\xd4\x86\n\xc1\x0c\x0b\xd9u\x05\xb3\xc3\\\x8b :\xa3\x0e\xae| 9b!,\xb1\xc4<\xb18\xa6\x98\xab\x07\xa1\xb2\xe8D\xb2\xc5,\xdaZT\x06\x9d4\x8c14\xed \xc1\x1a\x0b\xe2\x8d\xf9\x12P\xc4p\xc7|:\xad\x18r\"\x06Y\xb81\xf1,2_\xdd\"\x98d\x91\\2\x17\x16\x9f\x8cO\x86f\x94\xe18eXV\x19\xc2\xca\xe1\xcc\xb2\x10n\x99;'N\x12~Y \xc3l7\x8e\x99\xcf\xa0\x01<\xb3=0\xcd\xbc\xa5\xb3\xf6\xf4t|3\x04\xe3,\x9esfQ\xd7zs\xdf$\xe5\x9d\xf9\x98g\x91\xdc3\x8b.\x7f\xce\x1b\x04\xff\xcc\x9d\xef\xc6\x95\xed&5\x0b-9\x0f\xcd\xceDK\xc9E\xc3\xb0\xd1\xc2\xf9hA\x8c\xb4\x08NZ(+\xcd\x93\xc1\xc6]:,O\x08\xcbM\x8b`\xa7\x05\xf2\xd3\x1c\xd5\x8d\xe1\xa8YT!r\xd6\xc4\xf0\xd4\x1c]\xde\x9f\xaf&!W\xcd\x9b\xabf\x1f|\xb5T}1\x80\xb3\x16\xc2Z3g\xa2\xf1\xe5\xa1\xf1\x9e\xe1q9hp\x07\xe4\xc4\xf9g\x9c\xd9g\xfc%\n\xcf<3\xcc2cP\xe8\xcb;\x13\x94\xc3B\x94i\x92\xbeb+a\x85.\xb8|j\xa2\xb0\xa5\xcc\x15\x94\xb9\x822W\xf4B\x99+(sE/I1\xcd\x10D3\x08\xcf\xa4\xcc\x15\xbb\xa2\x98\x11\x18f\x12\x043\x1c\xbf\xa4\xcc\x15\xbb\xe0\x96!\xa8eb\xcc\x12\x87X&\xc4+\xb1h\xa5\xe1 B\x99+\xc6\x82\xc0'\xb1\xbb\xa4`l\x922W\xa0\x10\xc9\x18<\x922W\xd8\x1e\xf3b\x90\x01\x08$&/C\x08\xfaH\x99+(s\x05\x06c\xa4\xcc\x15BvA\x15)s\x85I\x93\x17G\x8cE\x11\xadk\x03e\xae\xd8\x16\xca\\\x11\x81\x16\xfa\xb1\xc2P\xa40\x00'\x0cF \xc30B\xca\\\x11\x86\nR\xe6\x8aN\xf6\x81\x04\xa6\xe8s\x01( \x1e\x03\xc4d\xaeP\x91}\x03\x1d\x81\x11\x94\xed${\xc54\xe8y\x87\xac\x15\xb6\x18_s\x88\xafz\xd4\x14\xcc+ \xb2\xa6\x0b\xdc\xcd\x8aB\x16YPQ\x9b6k\xbb\xe3\xf4\xa8\xf2\x0f\x8dq\xcc\xffz\xfc\xcdC\xf5\xf4v\xdc\xae6\xe4\xc1\x86\xeb\x0eM5\x94\xf63@V\xb6\xb0\xda O\x8d#\xaarb\xe2\x98\xb0Zg`-\xaa\x9cq\xc1\xb5\x0f\x1b{x\xad/\xc06\xbcX\x0e/V\xca0[p\xd9\xd9\x19c\x8b\xaa\x12\xaaG\xf8\xae\xc0\x08\x8c\xb2\x85\x15_\xfa\xeaf\x99\xaf\xad\nq!\xb6:\xc8V8\xae\x81}\xc8\x9bv\x18L\xdb\x1c\xc3oKV\xb2;V\x8bvP\xdf\xb5+c\xc2\xd9\x9a7\xaa\xd8\xf3#\xfb\x87\xab\xa1J\xd1\xd0b-\x17\x90\xff\xaa\xba\xe36X\xe6\xcd\xc00VMy9\xab\x85\xd7\x96o\x05\xca\xb9$\x15Z\x9f\x9ee\xfc\x14\xd3\xd5\xb0\x0f\xcb\xaa\x8a9\xab\xf5\xf7\xd4\xc0\xc8u\xc2\x05\xeb\xcd-\x00\x8b,7s\x18E\xb2\x8fki\x93\x1dz\xd8h\xd0\x0cu\xea!\xddlV\xe35Q5\xd3C\xd5\x1e\x16\xaf\xb5\x8f\x96\x83*\x1c\x8e\x9a\x83\x1f\xf9\xe1\xf4\x9c\xf7\x0e\x840\xc5\xbdP\x11\xbc\x1c+L\x9b\xd9\xe6\xa2\xf1\x0e\xe0\xad\\/%4Y\x14}\xcb6\xa0\x8a\xd1o!\x1eNt\xd1\x1dNBFU\xa5;\x9cP\xc6\xfc\xe2\xeep\xda\xea\x17=\xff\xadq\xd0\xde\x9a\xe9\xcdL\x13-\xb6\x0d\xfc\x83\xbe\x10\xc4\x83\x1b \xf1\xe0p\x0b\x12\x10\x0f\x8exp\xd6'\x89\x07'\x84xp\xdbB<8\xe2\xc1\xd9\x84xp\xc4\x83\x13B<8\xe2\xc1\x11\x0f\x8expR\x88\x07G<8\xe2\xc1\x11\x0f\xce&\xc4\x83#\x1e\x1c\xf1\xe0\x88\x077\x90\x14\x9c$\xe2\xc1 !\x1e\xdc\x97\xc0\x83\xa3\xeb\x8f\xc2\xee\x96\xa1\xeb\x8f\xf6h\\\xff\xc5=t\xfdQ\n+\xd2\xf5Gt\xfd\xd1\x17q\xfdQs}s\x7f-\x08\xa5'\x1f\xc5\xff\xb9\xae>\x92\xcc\x8a\x1f\xefO\x05Yut\xdbQ\xc3\x07\xae$\xb1*\xd6\xaand\x03\x15Zix\xa0\xebG\x8ch/y\x81\x18\xd1\xc4\x88\xee\x84\x18\xd1\xc4\x88&F\xf4\x0e\x85#FtzF\xb4\xda\x06\x88\x8dR\xcf\x85\x16\xb7\x1f\n\xe4\x93\xff\xb1\xf33\x1b'l\xa2E\x0b!Zt\xff\x13\xd6\x98_\x1c-\xda\x91\x16To\xaf]\xec\xe8\xc9#\x13]\x13\xeat_\x00\xa2D\x8f\x84(\xd1\xbe^\xd9\x0bQ\xa2\x89\x12m\x16\xa2D\x0b!J\xf4\xb6\x10%\x9a(\xd16!J4Q\xa2\x85\x10%\x9a(\xd1D\x89&J\xb4\x14\xa2D\x13%\x9a(\xd1D\x89\xb6 Q\xa2\x89\x12M\x94h\xa2D\x0f$\x05=\x95(\xd1B\x88\x12\xfd%P\xa2\xb3\x01{\x0bBy;\x99\x9d\xb5\xb3cVP\xe2l\xc7\x12b\x89\xb3\xbdG\xe3\xfa\xd9\xc6\xc4\xd9NaE\xe2l\x13g\xfb\xcb\xe1lK\xb6\xdc\xc9G\xb5\xb2bh\xdb\xbfH\xd2\xe26o[\xb1\x191\xc4m\xa9\xe3\x81\xae%1\xb7\xbd\x1c\x0bbn\x13s\xbb\x13bn\x13s\x9b\x98\xdb;\x14\x8e\x98\xdb\x9f\x99\xb9=lq\"p\x1b\x1f \x02w\xff\x13\xd6\x98D\xe0\x1e\x11\xb8\xe56\xdb\xcd\xe0V\xdb\xf9\xda\xcc\xe1n\x89\xbaM\xd4\xed\x80\x85\x9e\xa8\xdbD\xddVB\xd4m\xa2n\x13u\x9b\xa8\xdbD\xdd&\xea6Q\xb7\xb1\xbb$\xa2nwB\xd4\xed\xa1\x10u\x9b\xa8\xdb\x06!\xea\xb6\xf1\x19\xa2n\x13u\xdb\"D\xdd&\xea6Q\xb7\x89\xba=\x90\x144Z\xa2n\x0b!\xea\xf6\x97A\xdd\x164\x9c\x81\x8e uG\xb2t\x14\xe04\x04{\xa7\xf40 \xc26\x11\xb6\x89\xb0-\x84\x08\xdb\xfd\x9f\xf1V$\xc26\x11\xb6\xff\xec\x84m\xcd\xfb;\xf9\xa8\xff\xeb:\x9f\xbb\xc8\xda\xaf\xd5c\x1dM;\xeb\xb8\x83=u\xb0\xfbK>\xb7\xb3\xb5\xb5\xa6\x07\xba\x86\x07J\xd6\xd6\x95\xf1q\xc6\xba:+\xb2X\xf7\xefrQ%\xe5\x87\xd9\x18\xdb^<\x01Afrs\xb5\xf3\xb9\x9evt\xedL\xa7=\xd1\xb9\xae\xd7U\x91\xcf\xee\xaf\xd5\xae,\xb2\xc4\x18\x08\xc1\xfcA\x07\xbf[\xbb\x9f\x15lk9\x86\xf32o\xf3\xac\xe8Y\xbf\x97\xae\xa9\x03\xd1\x99Y\xb9Y\xd9\xa6\xfa'\xf0\xfa\xe2\xd5\xebW\x97\xa7/\xae/\xafN\xaf\xde\\^\xbfyy\xf9\xfa\xec\xd9\xf9O\xe7g\xcf\xd1\xef\\\xbe\xf9\xf1\x97\xf3\xab\xab\x807N\x9f=;{\x1d\xf2\xc2\xc5\xd9\x7f;{\x16\xf2\x82\xeaY\xe8\xe7\x7f;\xbf\xfa\xfb\xf3\x8b\xd3\xdf^Zz\x87\xa47\x07\x9ak\x91\x97Yq\xddfEq\x7f-\xcf \xbbt\xbemmc\x07~\xb3Y\xc9\x04\x08E\xa1\x02e\xd8\x1c\xee\xaa\xd6\xc2S\x00\xc9\x06Z\xe6\x8dm\xd4u\x13\x8d@k\xf8y\x9ekS\x9cA}l\x94^\x81\xac\x1d\xccF6ocV\xce\xc5\xd8\xb4~\xafZo\n\xc1zPX\x13\xafj^\xde\x1eq\xf5w\x95\x9c\xa4X\x9dWs`\xa5\x88\xac\xb2\xd2q\xbb\xa2\xb3\x0fl\xb6iE\xa1,\x8f\n\x17\xb1\x88\x08[f\xeb5+\x1b\x893\xb8F\x9b\x95T\xed\xdb\xf9s\xb9g\xcd\xb5\xd8 \xd9\x11c\xef\x98\x86i\xa7\xe9\x94\xeams\xd7\x01T0\xd5=kd_\xb0\xe1\x9f\xd9M\xc3\xbbR\xfa\xa2\x8d\x14\xdb\x8a\xa7\x1e\xb2\xec\x0d\xb9\x94U\xfa\xa2i\x9d\xb6R\x95\x95\xdbfeu\xcd\xd7\xb2\xeb;\xd6\xee\xa7t\x13\xed\xb6r\xf2'Le\x94c\xe6Z\x8e\x99kV\xeez\xeeL\xb1\xb3\xde*S\xe7\xba\xecv\xd87\x82\x0b\xaa\xb0\x1b\xf9\xbcE\xd9Jq\xe2\xe6U9\xf5Z\xf6\xf2\xa6\x14\x8e\xe2l\xe0n(\xe0\x97\xe6\xf6\xec\x03\x9b\x89\x9dPV\x14l\xae\xbf\xfa\xa8\xad\xd4\xa4a[\xb8\x87\x1b\xfeee\x83\xf2AN_\xc2\x1b\xd5\xbb9\xd4G\xc4\xdeo:\xa15\x8fm\x13\xa7\x9e\x08;|\x8c\xd7\xd7\xf6Y\xe1\x99\xcc\xf9\x06-/\xdb#\xcd\x0e\x80\xb7\xdb\xcb\xc7[\xfe\xdb[\xb9\xa7yk]tX1\xef\xb6q\xd6o\xcefU=\xcf\xcb\xdb\xe2\x1e6\xeb\xb9-FQ\xda\xb5\xaa\x13,\x86\x13U\xba\x13\x89Jj\xaf\xecdo\xe5\xd4$W\xb4\xe9\xde\xebe\xd5^l,\xe1\xe3\xdeQ\x83\xdcx\x9d\xfd\xbfg\xcf\xde\\\xbd\xba\xb8\xbe8\xbb|\xf3\xe2*l\x076}\xf9\xe5\xab\xab\xeb\x8b7\xe6M\x8c\xf3\xc5\xcb7\xcf\x9e\x9d]^\x86\xbf\xf8\xd3\xe9\xf9\x8b7\x17g\x96\xa6\x9cn\x9b\xc2*\xab\x90\xd4\xfd99\x1c\x0b9f)\xf7GI\x01\xa6\xa3H\xc1\xf5{)\xa1\x11SNe\xad\x916k\xc7\xa7!u\xe4\x14\xf8\xa3\xa7 \"\x82\xca]\x81\xac]\xa2\xa3\xa8\xb8\xb4)\"\xa9 2\x9a\xca\xa9\x90\x1b\x17\x1dQ\x05\xbbGUApd\x95S\x95\x8a\xf8\x08\x8a\xae\x82\xd4\x11V\x10\x18e\x05\xa1\x91V\xee\x9e\xddEaa\xa3\xad u\xc4\x15\xe0\xa2\xae e\xe4\x15\xec\x1c}\x05q\x11X\x90*\n\x0b\xa2\"\xb1\xdc\xc3Acp\xbeq\xb3\x97\x88,\xd8cT\x16\xec'2\x0b\x02\xa3\xb3 .B\xcb7\x05\xb7\xa8(-H\x1b\xa9\x05\x01\xd1Z\x00\xc1\x11[\x10\x11\xb5\x85\x982\x1f#\"\xb7 E\xf4\x16\xf8\"\xb8\x00\xbf=CDrA\xe0..8\xa2\xcb\xa9MD{!\xa2\xba \xa0\x94 \xa3\xbb (\xc2\x0bRGyAd\xa4\x97\xbb_5\xfeh/\x88\x8f\xf8\xb2\xea\xe3_\xf4E}A\xb2\xc8/\xc0\x070\x01&\x02\x0c\xc2\xa2\xc0\xc0\x17\xb6\x11\x19\x0d\x06\x08\xbd\x0efx\xa2\xc80\x882.>B\x0c\x10\xb5\x8c\x88\x14\x83\xd8h1p[5]\xd4\x18\xe0#\xc7\x00\x19=\x06\xe8\x082\xc0Y=<\x92\x0c\x82\xa2\xc9\xc0\x19Q\x06\xa9\xa2\xca 4\xb2\x0cv\x8c.\x03\x84y\x03\xa2\xcc`\x1f\x91f\x80)\xa3c$\xa4\x8b:\x03L\xe4\x19\xec\x10}fU\xd8\n\xe6\x99=\x02\x0dRG\xa1\x817\x12\x0db\xa3\xd1\xac\xda\xe4\x19\xd5}\\GD\xa5\x813x\x06\x9c\xd1i\x10\x15\xa1fU\xe5\x8c\\\x83\xd8\xe85\xab6\xb9\x0ftx\xcd\xd2E\xb1\x01*\x92\x0d\"\xa2\xd9 ,\xa2\x0db\xa2\xda 8\xb2\x0d<\xab\xad'\xda\x08\x02\"\x8e\xb0Qn\x10\x13\xe9\x06\xa1\xd1n\xe0\xaexL\xd4\x9bU\xd9 \xa6\x0c;dp\xd1o\xce\x01Q\xde\xba#\xe0 m\x14\x1c\xf8\"\xe1\xc0\x1d\x0dg}'6J\x0e\x12\xf6\xdd\x80h9\x08\x8a\x98\x83A\xd4\xdcXp'p\x0d\x80I\x0ej\x917\xc2\xad\xfd\xb6\x99\xbf;\xfe\xa5\xb9}\xab|\xe5]\xbf\x91\xb0\xf5\xdc\x99v\xb3\x07\xafE\x1a\xd0\x89\xe7dT,\x11u\xa0Y\xfe\xe6\\\x91]4\x01]\xe9\xdf\x0b\xe5\x85\xa4\xbc\x90\xbdP^H\xca\x0b\xd9KR\xd42\x04\xb1\x0cB+)/\xe4\xae\xc8d\x04*\x99\x04\x91\x0cG#)/\xe4.\xe8c\x08\xf2\x18\x81:R^H\xca\x0bIy!\xb1\xa8aR\xc40\x06-\xa4\xbc\x90\xb6\xc7\xbc\xa8`\x00\"\x88\xc9z\x18\x82\x04R^H\xca\x0b\x89A\xf5(/\xa4\x90]\x90;\xca\x0bi\xd2\xe4E\xe7b\x919\xeb\xda@y!\xb7\x85\xf2BF j~4-\x14I\x0b@\xd1\x82\x11\xb40\xf4\x8c\xf2B\x86!d\x94\x17\xb2\x13\xca\x0b\xa9\xa4\xcb\xea\xd5g\xa7\x1a\xe81&`\xba\xb6\\1\xdb\xc7]\x0e\xa7\xa2\x9dSDZ\xd2}\x84\xa7\xe0j\xc69\xb8ND\xa8\xa5#\x13\xd7\xe0\xe4|\xc5\x1f\xbd\x90Q\x8b25\x99\x9a{\x85O\xba\x0fg\xcc\x86\x89\"\xc6\xd1\x8d0\x8c\xe3oZ>`\xf2r\x1cd\xca\xc7\x16+\xa5\x03J^\xe87\xabV\xebM\xcb\xf4\x18\x10\xee'\xf9\xd1\x81\xaa\xace\x83\xe0y9r\xa4\x8b\xa5\xac\x84\xe7A\x04]\x1e\xc3+E\x82\x17n\x9ceV\xce\x8f\xf8>mRF\xf9\xf0@\x9f(R\xdeB\x93\xaf\xd6\xa2\xae}\xedM!\xab\xb2<\xd0\xb4U\xddgo\xeb\x94\xf5\x1fj\x1bV,t?\xd9\xce]60\xb8z\xe6`\xd3\x97\x89\xeao\x9f\"Fe\x91\xfddx\xd5e-/\x1b\xe4;5\xfe\xdb\xf6\xe2m- xA@O\x06\x03\xaf\xfb+a\xe6\x02D\xd6\x82\xb0\xe2$\xc8V\xe0\xceT\x10V\x9c]2\x14`\xb3\x13\x04\x97(:+\xc16\xbb`0\x0e\xcd\x04\x83\xf1\xccH\x1c\x03-\xc41 \x8eA/\xc41 \x8eA/\xc41h\x89c`\x16\xe2\x18h!\x8e\x01q\x0c\x88c\x80\xdc%\x11\xc7\xa0\x13\xe2\x18\x0c\x858\x06\xc410\x08q\x0c\x8c\xcf\x10\xc7\x808\x06\x16!\x8e\x01q\x0c\x88c@\x1c\x83\x81\xa4\xc0{\x89c \x848\x06\xc41\xf0p\x0c\xe4\xd5H\x07\xcf1\xb8\xbe\xb9\xbf\x1e\xde*r\xf2Q]\xb3\xe3\xba\xf3kp~\xd6\x01\xbb\xcd\x8f\xf7\xf2\x12/y!\x88\xbe\x0c\xac\xbf\x03\xa4\xcbC\xb8}\x01S\xa7L\xde\x013\xberi\x84\xb5\x9b\xbf\xa6\x9e\x11\x08\x10@\xf8\x97\xb2#\xed_J\x18\xf9\xdfW\x8f\xc8\x0b\xd2\xc2\x03\x01<\xea\xf8 +$\x1c@JPP\x80\xaf\xbfw!\x03\xd8\xd0\x00)\xc1\x01\x02\xbe\xf1\xd9`\xc2\x04\xa4$\x0b\x16\xd0\xeav \x19\x90\x12\x118 %I\xf8\x80\x94\xf0 \x02\xdf0\xc1_\x9c\xb6\x97\x80\x02)\xfb\n+\x90\xb2\x87\xe0\x02)!!\x06R\xd0\x81\x06\xbe\xa14\x08C\x10}\xd9\x1fn %a\xd0\x81\x14l\xe8\x81\x946,\x00AJh\x18\x82oZ\x0b\xbaN-AH\x82\x14\xef\x95j\x01\x9b=D\x90\x82\x94\xb0]ap\xc0\x82\xaf\x97\x06\\\xae\x16R\xd6\x84!\x0cR\xf0\x81\x0cR\x92\x863H\x89 j\xf0\xf56\xe4Ekq\x01\x0enu-\xf2\xb2\xb5T\xc1\x0eR\x90\xac})\xde\xc0\x07)\x01\xe1\x0fR\xbc\xf7\x17E\x85BH\xf1\xebv^\\\x92(8BJ\x8c\xb1\xf1\x81\x12R\xfc\xf5\x8d\x08\x9a\x90\x12\x15:!\xc5}9L\xb20\n)\xc8`\n)\x98\x90\x8a\xeeID`\x85\x14T+\x84\x07YH\xc1\x87ZHq_\xcd\x96(\xecBJP\xf0\x85\x94]B0\xa4\xf8\x8d\x1d\x10\x8e!%yP\x86\x14DI\x9d#%]\x98\x86\x14o\xb0\x86\x94\x98\x90\x0d\x87:\xedK\xf7]\xd9\x16\x17\xbe\xe1P\xd7j\x9f\x95\xf3\xda\xb6\xc8P\x0e\x87>\xcc\xd5m\xa8\xb0\x0e)\xee\x1b\xa0|\x17\xb8\xc5\x04z8\x94y/q\x8b\x0c\x04q\xe8\xf3^\xe4\x962(D\x8a?4DJh\x80\x88\x94\x800\x11)\xc1\xc1\"\xa3\xd7\x90!#R<\xd7\xba\xf9/\xc7\xc2\x13\xfa\xb1A$Jkh(\x89z-$\xa0D\x8a\xd3\x041\xc1%\x0eu\xa8K\xde\xe2\x02M<\xc3\x05s\xd1[\xd2\xa0\x13\xa5\xd0w\xd9[\xdcuo\xbb]\xf8\x96\xb2W\x07\x84\xa9(\xc5\xe8`\x15)\xb6\xcd\x0c\xd6;\xa0!@\x08\xba\xfc\xcd\xaan\x9a\x8b\xd1t\xfd\x9b\x14L\x01\xbb\x8b\xdft\xd2\xc1LG\x83t\x04B~PZ\xf1N^\xcb\xc8\x1a\xf9\xbb\xcd\xc9/)\xccC\xc2\xb3\xf11\xde\xc7\xb2Q\xe0 \xef\xcas6\xcb\xe7\x0c6k\xeb)\xe4\xb4\xaf\xf8\xac*\x9b\xbci%\x15M\xe0\x08v\xb3\x1au\x0d\xee\xd9\x1b\xe3\xee\xc6\xa7\xa5\x9d\xf96\x8cO9\xfc\xff\x9bj\xc5:(\xa3g\xf4gMS\xcdr\xe1\x98\xd0\x14U\xb3B+\x9d\xdf\x18\xea\xd4\xb3\xe8\xfb\xbf\x08\xfd\xb7\xf9\x1d+\x8d1<\xdd\x97\xb2\xdb\xbc\x14\xc3c\xfb\xf03\xfeV\xf7\xe0( \xe5\xe0\xcfj\xebf\x8a\xda\x91\xe2\xc4\xa3\xdd8t\xc9>\xb4\xd7\xef\x98!I&`\\\x91^\x17\xe4\xa8\xaa\xff\xd36`u)4C\x83\xff\xa7\xf2\xef\xf3\x1e `\x8d\xd7\xd9-\xbb\x90\x999\x8f\xe5\xef\x16e2=+W\xc3\xd5rC2XUM\x0bLx\xc7\x85c]\x90\x1b\xfb\xc5d\xdd\xde;/~\xac\x99\xe8\x08e\x05\xab\xaaf\x1aT1\x8d\xff\xb6j3\x0b\x98\x8f6\xa6\x83\xc7\xde\xe6m\xc1\x1c3\x9f\xf8\xbc\xb0\xa2\xf8\x8fr\xa3\xa7\x10\x9dLj\x00\x19\xd8\xea;4\xb4`\x9c]\x0be\xb6\xd5E\xc4P\xb0\xf6\x08\xf2\xb6\xe9)1\x9bRv\xe6\xb9\xf4\xd7\xbf\xcf\x9bq\xffp\xcf\x93\xa3\xdb3\xc7\x01p\x93T\x97\xa6\x17\xbb\x9b5\xc7qz\x94\x02\xb3\x17J\x81\x19\x10SE)0\xc3\x982\x94\x02sw.L\x18\x0b\x86R`\x9a\x15\xe2x.\xc1\x0c\x17J\x81\xb9\x0b\x87%\x9c\xbdB)0wa\xa9\x84\xf0S\xd0\xcc\x14J\x81I)0\xd1\xbb\xa4`F \xa5\xc0DqFb\xd8\"\x94\x02\xd3\xf6\x98\x97 \x12\xc0\x01\xc1$x\x0c\xe1}P\nLJ\x81\x89ajP\nL!\xbb\xf0/(\x05\xa6I\x93\x97U\x11\xc3\xa7\xa0\x14\x98CAp%(\x05f\x04\xdb\xc1\xcfs\x08e8\x04p\x1b\x82Y\x0da|\x06J\x81\x19\xc6R\xa0\x14\x98\x9dP\nL%:\x05\xa6J\x906\xd0\xe1:E\xfa\xb3\xe5\xb5]\x02\xae\xb5\x06\xa0d\xe2\x93 r\xd9A\xcb\xc3\xe9k\xe7\xb4\x99]b\xcf\x0eG>~\xc7\xeem\x95\x9b \xb3\n\x8a\xcd\xd4\xc2 /\x96\x94wFJtP\xc1_\x1dn+\x1c\\\xb7\x13O\x90\x00b\xf9d\xe6\xc3b_\xf1e\xbc*\xc5 \xb8Z,\x04\xad\xa0\x86qqa\xe0\xa0oX;\xb5\xd5\xef*\xfd\xa7\x96\xdeX\x8b\xach\xbc\xd6\xb2\xb8;\x0cF\x94\xe5Cv\x12U\x19a\xcar\xb3bu>\xd3\x7f\x13s\xc8,+y}\xa4\xafg\xc9Jm\xf8M\xd9\xb9\xd7&\x9bj\x99\x93Gd\xaf\xe8L(\x1dR\x9b\x86\x9b\xfa\x1d\x0b\xb4\xe7X\xfd\x9e\x8d;A\xb6\x0d\xe6-\xf2U\x8e\xb5\xaex\xb6\xcb\x17b\x01\xbc\xa5\xebu\xd8\x83\x15\x97\xa2\xbf\xbeT\xcaZ:Z\x86\x7f:_@\xc1\x16\xad&\x08(\xc6\x80\xde\n\x0b\xaf\xb1\x1c \xf2#\xdc\xce7\xf72\x91R\xb6^\x7fF+\x0ea\xfb\xfe}\x97-\x07o\x88\xdc$L\xd4\x8fO4\xc0\xff#/\xe7\xf9,kY\x87\x1f\xe9\xf4\x19\xfcACDy^\xce\x8a\xcd|\xb2\xd1\xcd\xe4W:\x00o\xd2b\x02\x0e\x1e\xf8\x95\xf9\x824b\xc1\x8c\x94\xbd9\x9f\xe6\x9c\x9bTA\x9c\x0dj\xd6(\xe0^\x0c\xaf~<\xf2!\xa73\\\xe5\xb7eUO\xbc\xf2z4\x8e?!-\xb3k\xc3\xdeTU\xc1\x06T.C\x03\xd6\xec\x8e\xd5\xa3W]\x8d\xa7\x9e\x9e6\\>\xa0}\xd4\xcc<\x12Fz\xf87X)\xf0\xca\xaa\x9e\xb3z\xea\x96\xbb\xcc\xcb\x19{\n2\xc1\xf2\x93f\xfe\x0e\xbe>\xfe\xee/I\xad\x81O\xeb|W\xb5\xec\xfa\xe6\xfeZ/\x9f\xd7\xfc\x0f\xf5\xe4\x1e\xe9\x8f\xe2\x8f\xae\xfc\xce\xbfV-\xfb\xb1\xe3\x9a\xf0\x7f\xd5]F\xe7L\xe6C\x93geI\x80\x13\xc0\xd0\\\xfc\xbd\x83\xb8Fi\x9b\x0d\xfa\x1ehC\x1ch\xcef^\x19\x0fIM\xd8A\xcd\xb4\xe2\xbf\x07,\xb8\x81\xc1\xb7m\xd3\x8b\xb5`\xe0\xa5t\x0c\xbe\xb0?j\x95\x91\x00\x08\xa6\xcb\xd4[O:ga\x80\xc8\x82nY\xbd\xf6\xec0-\xd6\xd6\xf9\x1e\xcc\xc5\x18}E>8l\xdd\xfaa\x03\xb3e\x95\xcf\x98\x06\x95]\xd5E\xd4\xca\x95\xdf\xe8 \xfc\xfa\xea\xea\xec\xfa\xd5\xeb\xab\xf3W/\xbd\xb9}\xa6\xcf\xff\xe3\xcc\x96\x82h\xfc\xdc\xe9\x8f\x97W\xa7\xe7\xb6LG\xe3g_\xbeB>&\x12L^\xffzve~\xa1\xcbd\x84\xaf\xa0;O\xb6\xd7\xce\x18\xd8+m\x86l\xde]L\x9d\xc2\x9b=\xda[\x17\\\xa6\xb9Q\x8d'\xc9\xa1\xdbQ\x1a\xb9\xf7\xf2\xfe~=\x7f\xd93?#\xa8\x89\x86I\xde|\x05\xb7}5\xd0B\xac\xc4^\x88\x95H\xac\xc4^\x88\x95H\xac\xc4^\x88\x95\xd8\x12+\xd1,\xc4J\xd4B\xacDb%\x12+\x11\xb9K\"Vb'\xc4J\x1c\n\xb1\x12\x89\x95h\x10b%\x1a\x9f!V\"\xb1\x12-B\xacDb%\x12+\x91X\x89\x03I\xc1\x10#V\xa2\x10b%~ \xac\xc4\x01\xf6=\xd0c\x04\xab\xb7/\xe6\x96x\xf5\xa7\xbd\x98\xbb/\xfa\xdd\x04\xee2c\xdb\x83\x9b\x93\xe4\xdf&@w\xc22\x87\x91K\x9a!\xbbd\xcc+\xf1\xf0I\x9a\x1e\xf2spI\xac\xf4\x91\xc1\xeb\x0ft%\x0f\x98:r \xf8\x9a\x97$\x12\xe4_\xc2\xdf%\xbb\x13U\xc4I\x16A\x158\x1da\xc4M\x19IN\x1aA\xd5\xcew1\x16\x9eYaz\xc3N\x1e \xa3\x8f\xa0 $\xc1\x14\x92(\x12\x89\x8fF\x82\xb2<\xc6W \x89\xc9$v: \x82P\x82\xaa\x15\x8eT\x82\xaf}\x18\xe3\xc4\xa3\xc6r-\xde\xa8,|}\x18^^\xac\x96\x15\x99\xda\x0f\x99\xddN^\x0f\xab3\xdb\xe9\xe4\x88w\xdd\xa5\xb1[\xb4\xbe\xa9>\xcam'\xc4\x15A\xd1\x0b\xe5\xb6\x03\xdc\xba\xfa%\xe5\xb6\x9bl\xf3\xec\xe4\xb1\xe1VR\x0f\x92\x89\xc2\x96\x88cD\x1cK\xb3\x98\x13q\x8c\x88cf!\xe2\x98\x10\"\x8em\x0b\x11\xc7\x888f\x13\"\x8e\x11qL\x08\x11\xc7\x888F\xc41\"\x8eI!\xe2\x18\x11\xc7\x888F\xc41\x9b\x10q\x8c\x88cD\x1c#\xe2\xd8@R\x90x\x888&\x84\x88cD\x1c;h\xe2\x18\xe5\xac\x0bK\x08F9\xeb\xf6h\\\x7f\x1f\xa5\x9cu)\xacH9\xeb(g\xdd\x97\x90\xb3N\xd0\x8aU\xae:Tv\xba\xe6\xc7{[Z:w\x1a:\xfd\xe2\x03])\"\x11\xfb\xb8\x16D\"&\x121\x91\x88\xa5\x10\x89\xd83\xbc\x89Dl!\x11\xdf\xdc+\x0e\xb1q\x9a \xf6\xb0\x10b\x0f\xf7?a\x8d\xf9\xc5\xb1\x87\x87daO\x9a\xc9n\x9bH\x19%{!bp\x9a\x05\x9a\x88\xc1D\x0c6\x0b\x11\x83\x85\x101x[\x88\x18L\xc4`\x9b\x101\x98\x88\xc1B\x88\x18L\xc4`\"\x06\x131X\n\x11\x83\x89\x18L\xc4`\"\x06\xdb\x84\x88\xc1D\x0c&b0\x11\x83\x07\x92\x82\xa4I\xc4`!D\x0c\xfe\x12\x88\xc1\x07\x97\x96\x91\x88\xbf\xb1\xacJ\"\xfe\xee\xd1\xb8~\xca*\x11\x7fSX\x91\x88\xbfD\xfc\xfd\xb3\x11\x7f\xf9\xf9\x93\x95Y9c'Y\xdb\xd6\xf9\xcd\xa6e'w\xdf\x9c\xa8Et\x9e\xb5\xd9\xc9G\xf5\x0f\x17\xff\xf7T>\xf2\xcf\x88\xbc\xa3\x0ba\xd2q2\xec\x1a\xd2O\x90\x86\xeb\x93\xbe%\x89\xea\xf3\xf9\xa9>{@\xc9\\g\x0c52l\xfdO\x9f%T\xa7\xbfa\xb3\xe5_\xbe\x9dr\xcc\xf5Cm\xc5\xa7F\xe9}Q\xf3\xdd@\xef\x8eg\x8f\xe8)\xbc9\xf9\xd8\xfdY80\x9c\xd3\xb8~R\x0d\xda\xa6\x8f\xe5\xd0\x7f\xa8J\xc8\x14k\xb6\x9f\x8a\xb8^\xa5k<\x8dO\xf5=\xd05?\xd0\xc9\\W3\xe9h\xb2tX\xc5\x04\xd5\x94\xe4\xc9\xb9Us\x83\x04G\xa2\xb3t#w\xe1b\xaa\x1fZ]\xcb.\x9c\xe5\x01\x87g\xf8\xf3d\x81!\xee2q\x97\xff\\\xdce\xcb\xdeg:u\xed\xb0\x03\xda\x9aUi\x1fD\xfb\xa0\xc3\xda\x07\x8d\xb6\x08\xb6n(\xf0\x0f}&\x19-\xfe|\x8a\x943_\xd2]\x0fy\\c\xddY\xe4q\xdd\xa3q\xfd\xbeB\xf2\xb8\xa6\xb0\"y\\\xffH\x1e\xd7\xc1\xe4C\xbeX!av\x8a8\xe2w\xff\xe8|\xb4'\xcd,+O>6\x9b\xc5\"\xff\xe0:\xea\x0f,|\xc9\xe7\xef\xee\xa0?8j\x0e\x8e\xfa\xeap\xfaH\x9dN\x1fKony\xaf\xa34\xdaY\xbf\xc6\xb72\x1c^\x12\x9bdI\xd4o#\xe7\x00\xff\xac\xfa\xfb\xa1\xfb\x03\x826\xa2\xeesR\xa6\x19%\x8a1\xa37\x166\xa7\xd2p\xabeb \x8bp\x8d\xa6\xc9oE$B\xf5\xc3\xb4\x02\xdd\xbb\x87\xb11\x16\x13\x81\xf1\x17\xd4\xb6v\xd4 \xae\xb6\xb6\xa1\x87\xc1\xbc\x1f\x97K|\xdd\\\xb0~\xd7-J\x11\xa9[T\xc1\xc6EDT\xcf\x97v\xe2\xf4\xea\xea\xe2\xfc\xc77Wg\xd7W\xffx}\xe6M\xc7`~\xe9\xcdy\xc0\xd3\x82\xf9\x83~\xfa\xf2\xea\xe2\xfc\xe5\xcf\xf8\xe7\xdf\\\x9c\xe3\x1f>\x7fy\x85\x7f\xf8\xa7\x17\xafN\x03\x1e\x7f}\xf1\xca\x9a \xc3\xf0\xf8\x8f\xff\xb8\xb2\xa6\xf0\xe8\x12g\x047\x96\xcf\xa7\x03\xbd\x13\xf7\xea~\xcdF\x19\x0e\xdaA\x94\x84py7-\xdf\x81\x98V\xde\xa1L\xfa\xafyh\xc8\xe9p\x87Q\xab*v5\x98[\xc7\xe7\xd6\xbc\x81\x9bjS\xcem\xbe<\xf6a\x9d\xcb\xd5\xeaz\x9e\xb5)&\x90\xa0D\x1cW\xb9\x8e]\xce\x86\xaev\xb1w\x17E3\x8eywsvM\xa9xl2\x12s\xcew\x88'r.i\xdaz3k7\xb5\xdc\xb7\xce{\xfcu,Y\xd3T\xb3\\\x84\x95\xc8\xe8\x9a\xd2\x00\xa5HQ%\xcazG\xf7\xb8\xf9\xa7\x8b\x1a\xb9\xb0GB.\xec\xfe'\xac1\xbf8\x17\xb68*\xf0\xfd4\xcaI\xad\xdc\xd2b\xdbO\x9eh\xf2D\x1f\x96'\x1a\x87\xc8\x0f\xb7!\xdd\xf2>\xf0A\xef\x85\xf6;:\xcb\x82\xc7g!\xdc\xe2\xe3|Pu\x9bg\x85\xc1s\xde\xb0\xac\x9e-\xc5\xf0\xbc\xc9\x1a\x91RN\x84\x93\x8f\xf4\xdd0~T\xccK\xb8x\xfe\xf2RYu/\xb5$W{\x98\x1f\x93\\\xed{4\xae\xdfIL\xae\xf6\x14V$W;\xb9\xda\x85\x90\xab\x1d\xedj\xff\xe8\xa3\xd3\x0dL\xdb\x9f\xbcc\x1c\xed|f~4\x00\xd6\xf9!\xea\xb1\xb78\xb9\xdc\xc9\xe5N.w%\xe4rG\x8dtr\xb9o\xe9\"\x97\xbb\xa5\xe7\x90\xcb\x1d\xbed\x97\xbb\x875\x8er\xc4\x9b4\xf4lq\xf2\xcd\x93o\x9e|\xf3\x83\x1f\x9c^k\xe2\xad\x933\x9d\x9c\xe9\xbb\xba\x81\xc9\x99\x9e\xc2\x8a\xe4L'g\xba\x10r\xa6\xbb\x9d\xe9\x0d*\xb9\x88\xc9\x8d\xdeD\xf9\xd1\x879\xe4\xf5^\x7f\xcb\xd5kv\x9e\xffQB\xd7\x83v\xc3DU'\xbf9\xf9\xcd\xc9o\xee}\x9c\xfc\xe6\xe47\x1f \xf9\xcd\xc9o>\x15\xf2\x9b\xf7?a\x8dI~\xf3\xb1\xdf\x98\xd2\xe9\xe6s\x17$\xd8\x9d\x88@\xbe\x065\x9bU\xf5\\B\x80S\x07\xdf\xf4f\x01\xd6\x1c\xfct\xbb\x97<\xd4\xd1\xbdkW\x0f\xf7\x9a\xd5\xab\xbci\xf2\xaa\xf4\xe80WK\x8a\xb5rR\xbc\x85\x00/~$\x80\x8cg\xcf\xce./\x11`\xc4\xe8\xf1_\\\xd8\xcb\xe0\xb9\x1f\xdf\\\xd8\xd1\xa2\xc1s\xcf\xcf^\xbf\xba\x8aJL-\xfaZ1\x87\x12c?2Y\xa0G\x03\xa0\xac\x9eTkq\x85\x99\xc2\x05l0\xe9\xf8\x03\xbc\xe7=\x1d\xfe\xa3\xe39\xaa\x0b\xef\xc5\xd1lV\xb3L\xdd\xca\xc9\xeb\xc5\xcf\xfe\x8b\xae\xaa\xa8\xef\xf0\x9e\xfbt\xf8\x0f\xc3w\xe6\xcc\xf8\x1d\xb1\xe9\x93F\x95\x1e\x88Y\x95\x97\xb0d\x85\xb8\xf5\xb4\xff\x15Wa54\x9eN\xfem(\x0e?\xcet\xcdY3\xd1)f\xe2(#\xee\xd3W?\xa8\xe3\xfe\x8a\xb5\x99\xb8\xed\xa6\x99U\xbc\xd7\xc8]*\xaaDz\x04>\x9d\xfe\xc1P\xa6\x9a\xad\xaa;\xb6U\xa8\xc6P\xaaE]\xad\xb6\x8aU\xb9Z\xab\xad\xb3\xb2Y\xb0Z\x1aX\xbc?\xd49\xb8\xa7\"+\x85\xa7\xde5\x0e\xe4\xc3\xd86\xe1\xf3\xca\xd3\xf1?\x0d\xb5\x17u\xd7\x17\xf6\xb1\xf9\x11,\xf2R\xdd\xac.V\xf26\xefm\x93\x97\xf2\"E\x98\xf1}BQ\xb094m\xd6\xb2c\xb8Z\xe6\xe6\xeb\xd2\xa5\xa8\x0dAV4j7\xde\x0cTH\xe5\xda\xe7#\xfe5\x9fP\x7f\xa72g\x05\xe3\xe3\x11e 1o>\x1d\xfd\xcb`\x87l>\xd7\xc5\x14\xb3K\xa3w2b\xcd\x07Uq\x0dB*\x83\x0c&1\\\xab\xe8\xa9\xf9\xe9\xf4\x0f\xc6)\xe2\xaez\xc7\xdb\xa6a\xe5\xbc\xdf0\xa9\x01;\x18\xc1r\\\xf0W\x16\xd9\x8c\xbf\x9e\xb5\x0c\xd8\x87\xd92+o\xad\xcc\x0e\x10\xd7\xde\xe6M\xb7Y\xcbo\x97m\xe7\x81Q3-\xef\x04%\\\x9c]^]\x9c?\xbbr\xae\xbb\xaa\x15M\x9f\xc3,\x1dr\xd1\xf8\x99[\xbeG\x87\xc5e\xa4UQ0q\x8b\xfet\xd5P\xc8\xb1Q\x9dv\xd4\x0b\x14\x9a\x15\xecV\xde\xb6?\x1etj_;.2\xe2\x10(\xcb\x8a:\x04\x9a^?\xf9E\x14C-\x93t\x0c\xa4c\xa0o\x93\x8c\x19@\\N\xe1\xcd\xc5\x8b\x93\x9a5\xd5\xa6\x9e\xe9`\x8ae\xd6\xc2\xa6\xcc\x7f\xdf\xb0\xe2\x1e\xf29+\xdb|\x91\x8f\x198V\x85b\xd3\xd0\xddYm\xdf^\x89+igU\x017\x1b\xbe\xd1\xd3\x8d&\xd7\x05\xcdX\\m\x9aV\xd3\x16\xc1\xb1\xe1+X\xd6\xb4\xf6oU%\x83\xafN\xbe\x82\xd92\xab\xb3Y\xcb\xeacA\xd2\x117\x947\xecv\xc5z_\xf5\x9b\x8b\x17\x0f\x9b) 7\x16Q\xa8\xee\x9ab\xfbW[\xc3\xe5\xe9\xc2\xbe\xda-\xce-\xf9(k \xb7\xdc\xd5\xcd\xe5-/\x8a\xf5\x1a\xdf\xb7\x8feM\x84\xda\x1e6\xcb\xed\xab\\\xc6W\xd1\xaa\xccgY!\xc6\x90\xfd\xcb\x8f\xd8\xf1\xed\xf1\x117\xad\xf0\x06\x7fu\xfc\x15\x9f\xb6\xca\xaa\x15\xd3\xff\xbae\xf3\xc7S\xe7\xf0P\xceKXsc\xe73v\x04-\xcbV\x0dl\x9aM\xc6\xcd\xb1\xe6\xa7\xfc\xd5:/X\xbf=\xb8\xc9\xcb\xac\xb6\x9d\x19\x81\xef\x00\xd49@\xe3\x12\xf7\xf6O\xcb\xa9\x0er\xb1G\xda\xf0\xe9Vn\x0eyGb\x1fDS\x9f\x96\xf7\xc7\xf0\xf7\xea=\xbbc\xf5\x91s\xe7\xf0\xe6\xe2E\xa3.\x8dV7\xe2\xdb?,fP\x06o\x97m\xbb~{$\xff\xbfy{\xc4wEe\xa5~=\x12\xbdq6\xe0\x0d\x15\xf6j\xf3\xbd\xeff\xadHS\x8e\xef\xb2\xfaN\xdc\xc4/rx\xaf\x1b\xd9\xb5D\xc9\xf9vM\xdd>-\x0e)\xe2r\xeb\x062\xfb\xd6kQ\x89\xdd\xd6SG\xdb\xfe\x0b\x9c/\xfa\x1a\xf1n\xa1\xf3\x84w\x95\x16Ph\xd3lVl\xee8c\xfd\x0b_\x9b\xfe~u\xf5\x1a~>\xbb\xe2\xfb\x065\x04\xe5\x18\x13\xb7\xfd\x83\x99\x0e\xc6e\xeb:\xed\xab\xfb5\xfb\x8f\xff\xfe\x1f\xd6\x17\xba\xfb\x18K\xd5\xdf\xd42\"Zh]W\xf3\xcd\x8c\xf1\xc3\xa1X\xc2\xec\xbb\xa0\x7f\x81\xd3\xf5\xba\xc8g\x99\xb2e\xcd\xe4\x0eU\x92\x9ef\xd9\x8c\xcf-U\xf5n\xb3\xee\xc0\x97\x9b\xac1^\xe9(\xa5r\xb3\x17E'\x14e\x14\xb7\x88\xb5K\xb6\x1a\x8c\xa1\xb9\x1cD\x99\xae\x12\xff\xef\xbb*\x9f\x8fX\xf2\xdb\"\x0b(\xa6\x8f\x9a-\xaa\x9a\x1di\x05\\o\xd6\xe6jWY26\xd7\x1bm1\xe5\xd5w\x8e\x9a\x88\xba\xc8]\xa4\xdc\x00\xf31{\x0c\x8f\xde4\x0c\xeeX\xcdwa\xdcJ\xbc{\x8a\xd4x\xa2\x7ffev\xeb\xaa\xfdM\xcd\xb2w\xe2\xc4)\x15\x1f?\xb6\xf7\xa8\x97U\xcb\x9e\xca\xf3\xd2bS\xce\xe4\x08\xe3\xf5Ps\xd7lS\xd7\x82\x8c0\xa4U\xd9\xa7K\xde\x1f+\x01\xbf\xdb\xd9Tj-\xbb\xd9,\xa0f|%bG\xe2f| Y\xf3\x8f\n\xd8_l\xef\xf4\xb8\xb4\xaa\x12\xe4\nA\xd1\xe7{U\xc7\xe2\xb2}\xd9\xbbk6\xbe\x14#\xb5\x91\xcc.I#\x98\xccR\xf0Ha\x10\x12\x03\x97C\xfb\xb1}\x11\x14{\xff\x1b\xc7\xa4$\xb9\x0e|\xc3\x9d\xaf\xd6\x05[uW\xfa\xab\xabcg\xd0\xb0UV\xb6\xf9\xccr\x10\xda\x83/{*\xd8]\xd2/|:\xbaa\x92\x06\x90\xcf\x07\x1b\x9c\xad}\x8c\x06\x0fo\xaa;{\x9f\xeeo\xcfm-LzL\xc9\xde\x9e\x96\xf7o\xf5\xf6HPQ\xb3\xfa&ok>\x88\xed%4\xaa\xd2kDVT\xaa\xebAfnZ>;\x8b\x85F\x96\xf0\xc6@\xcc\x1e|[\xef\xea,]\xf3\xb5\x1e8E~#\x8a\xad\xd6\x91F\x9f&\xf9\xfc\xb0\xcef\xefN6%\xff?\xben\x9b9\xbcR\xd4Bo\xdf\xd8T\x0b\xd8\xb4rb\xd3\xd3C#\x9c\x16\xf3y.\xe7\n\xb8e%?6\x8b\xc2\xf3s\x96F<\x8d\xfaxyd\x13\x9a\xbfw\xf6!\xe3\x9d\x1f\xbey\n\xafy\xf9\xf9\xbc\xa0\xaa\x92uF\xcfKx\xf6_\xff\xabc\x99\xfc\xa9\xaa`QU\xf0\x03\x1c\x1f\x1f\xff\x1f\xd6\xc7xa\xb2\xf2\xde\xfe@V\xde\x1f\xf3b\xfcTW\xabG\x8b\xaazl\x7f\xf4\xf8\xd8\xbe\xfe\xe5\x0bx\xc4U\xbd\x11\x15\xb9\xaa\x1e\xfd\x17\xae\xeb1|t\xcc\xe1.}\xff\xe9\xb6\xdd\xb7\x1e\xdb\xfd\xb7\xec.Kf<\xf8A\xec\x0d\xf9W\x12X(o\x1e\xfdTU\xc7\xb3\"k\x1a\x8f\x81d\x11\xf9K\xb2\x8e\x83\x17\xede\xb0X\xae3\xdd_<\xa6{}\xdf.\xab\xd2a\xbb8\x7f}\xf5\xea\xe2\xb1\x0b\x0b\xea;\xaa\xfb\xc3\xf2\xd3ns~\xe71\xe7\xcf\x95\x03N\xe0\xa6|\xfa\x03\xfc\x97\xf5\xcd\xf1OU\xf5\xf1\xf8\xf8\xf8?\xed\x0fg\xe5\xfd\x11\xdf\x86\xf27\xd6r\x13\xf5KV7\xcb\xac\xe0FvW\xc4e\xc2i)\x1cE\xc8\x17\x93\x02\xbc)W}\x11D\x01\xc5\x00\x11O\xfdo?@\x99\x17\xce\x0e\xee.\x97\xa5'_ \xee\xc9\xec]7\x17w\x17\x12\xdd\xdc\xf7\xdb.\xbdz\xc8\xe8\x00\xf3\xaeW\x93\xf96\x8de\xcf\xf2\xd0\xb0\xa5:\xe1\xe7\xf7c\xf1\x03\xdf\xae>\x84l\xb0\xda\xf1\x95\x90\xf7\x04\xdb\xda {\x88\xf9c\xdd\xd2R\x16\xf7\xfa\\\xb9\xe5,\xe8\xb6\xc9\x90-Zfr\x0eJ\x11~\x8c\x87'\x0f\xcd\x9fRk\xa2.\xb28\xed\x02S=\xfa\xabEU\x1d\xdfd\xb5\xa8\xec\x87\x93\xfb\xe3\x7f~%\xad(\xce^F}\xf6\xa3\xa8(\xeaW\\\x07_\x0e\x8d\x8f\x888;\xe3/?\xfc\xf0\xc3\x0f\xf6>\xc0\xdf\xeb}.\x99\xf6\xed\xf2m\xac\xd8\x04\xc9s\xdd\xa6a\xda\xb1z\xbb)\xb2\xda\xaco[\x0d\x7fe\xce\xfam\xcb\x11\xb0\xd5\x0d\x9b\xcf\xfb\x0d\xcc\x91\xdc\x8e\x9b\xd4e\x16\xef\xcd`K\xb1\x10\x07\xd9\xb7\xff77\xdd[\xe5L\xe8\xb6m\xc3\xc61\x0f\x105\xfd\x07\xf5\x07\xe2E^0\xfb\xba\xa1\xe7\xac\xd7\xacn\xaa\xd29l\x95'n\x91\xd7M+n\x15\x85\x1f\xe0\x1b\xbb\xe6\xee\x05\xde)\xf5\xf3\xdf\x86\xaf`\x00\xceR}%l\xf9\xd5S\xf8\xca4j\xc7f8\x96\xb5\xfc\xea\xc8\xa5O\xd4\xefe\xb6\xe2:\xffOY\x85\xff\xcb\xf9\x02\xaf\xdf\xe4\xf9\xd0J\x9e/\xd4\x81k\xdc\xd7do\xc8\x1bx\xcf\x8a\xe2\xc9\xbb\xb2z_\x8ayf\x99 pc\xd3\xb4\xd5*pp\x8d\xbb\xfc\x91\xdc\xc0O\xc6A\x1fZ\xa5\x8a\xc3;\xb0\xe5p\x95\xc9.m\xfe\xd8[1\x18u?\x97a\x7f\xc2\xfd&J.\x87r^v\xe3C\xe1dfUr\xc8\x98\xbf#\x8ap\xdc-\xce\x8f\xf8\xbc\xa6M\xb8\xe5\x1a\xd2\x1e\xd3\xff\xf8\xef\xff\xf1\xd81\x90R\xf4\xb9\xf1\x07\xdd\xddN\x98\x8a\xab\xfc\xe6\xf8\xdbo\xbem\xbert!\xf9\xff\xae\xe8\x94|8a\x8f\x03St\xa8\x7f\x0dsVV\xab\x8ep\xb8E\x1d\xd81\x1c%\x9c,Vm\xcaV@\xe3\x1fE\xc9\xd0l1\x19h\x93\xb5\xd9vd\xa8\xac\xa3z{\xc4\x1a\xeb`4\xfe\xf6\xf3>\xd4\xf4` d\x16\x1f\x88\xd3\xfb\xb1\x1d\xbd\xdf[y\x1a\x98\xca\xe6\xd2X\xe3\x85\xc7\x16\xb2\xa8\xa1Lm\xbdx \xd8l\xa5\xe1\xda\x03\x8ciA1\x8b\xc2\x0eZ^\x88dK\xcd0\x1bmQt\x01\xac\xcaWxST\xb3w\xb3e\xd6\xb9p\xc6\xb9.\x8aB\x02\x97\x07\x9f\xefBU\x8b\xe0'\x82\x9f\x08~\x12B\xf0\x93\x10\x82\x9f\xb6\x85\xe0'\x82\x9flB\xf0\x13\xc1OB\x08~\"\xf8\x89\xe0'\x82\x9f\xa4\x10\xfcD\xf0\x13\xc1O\x04?\xd9\x84\xe0'\x82\x9f\x08~\"\xf8i )\xa0\x00\x82\x9f\x84\x10\xfc\xf4g\x81\x9f\xb4\xd0\xcd\x7f#\x19U\x99n\xfeC\x19\x93n\xfe\x93Q\x81\x1dF\xb9CP`\xa7\x83R\x9cRl\xa0\xd73\x89q\xee\x01\x81\xb3\x04\xceZ\x9f$pV\x08\x81\xb3\xdbB\xe0,\x81\xb36!p\x96\xc0Y!\x04\xce\x128K\xe0,\x81\xb3R\x08\x9c%p\x96\xc0Y\x02gmB\xe0,\x81\xb3\x04\xce\x128;\x90\x14@\x19\x81\xb3B\x08\x9c\xfd\xb3\x80\xb3\xae\xd8\xc0\xa6\xcd\xda\xcd\xf0`\xe4\x82,_ixV\xbe\xc5[a\x91\x17\xad\xb8\xb1S\x81\xb3\xe3\xc2<\x81_N/\xfe\xfd\xec\xe2\xfa\xf2\xea\xf4\xea\xcd\xe4\xdaW\xebO\xf0\x04\xde\x94\xa2\x0f\x9f\x9c\x97\xf2\xf0,a,\xb8\x9c\x96\xd6\xf0\x8d\xd7\x17\xaf^\xbf\xba\xdc\xfe\x80\xfe;<\x81\xf32o\xf3\xac\xe0\xf3\xe9\"\xbfUF\x875\xab\xf3j~\x04\x9b\xf5\\\xde{(\xfdsG\xd0V\xefX\xa9/n\x15\x0e\xa1\x9a\xf1\xc3\xde\xb1\xbb ?\x9d\xbf<}q\xfe\xffm\x97\xa4\xfb\x01\x9e\xc0\xb3Q\x11\xba{?\x8f\xa0f\xd9\\\xe6fU\x1f\x16\x1f\xdd\xbaos\xfa\xd1\xd3gW\xe7\xbf\x9eM\xbf(\xff\nO\xe0R\xea\xca\x1b]\x87#\xa87\x05\x93\x0e\xc9\\\x80\xed\xb3\xa9oa\xfa\x89g\xa7/\x9f\x9d\xbdx\xb1]\xaf\xee\x07\xfe\x8al0>\xff\xdc0V\xf6W\x8d\x1e\xc1\x9a\x95\x02\x1d\x98\xb3\xa6\xad\xab{\xf7\xc7\x9e\x9f]^]\xbc\xfa\xc7\xf6\xc7\xba\x1f\xfa\x8f)C\x899O\xccK\xac\x84\x9a\xcd2\xf9Y}\x7fj\xc3\x9b\xbd\xc9\xe7\xac\x16\xb9ZE\x19\xf8\xccU\xce\xa1\xac`\xb1\xa9\xb7n\x80\xcd\xd4\x99_u\x88iX\xec\xef*\x96UK\x1f\x17\xbb\xc8\x8a\xc6\x13\x18\x0b\xc6\xdb\xb7\x1d\xe3\xc6\xf9\x9c\xee\xe3\xce\x87\xba\xee\xe7|Jv\x19\xe7#]{;\x9f\xea\x1aj4\xc9\xa8\xcb\xaf\xfd\xd5\xd4\x13U\xcf\x089~\xc7\xee\x91\x13\x96\"Udj\xafW\x8b\xd0a\xe1\xfdV8\xbf\n\xdc\xed\x18\x18\xc2g};q\xee\x8a\x06\xd6\xf7\xbd\xbaX\x15\xaf\xf8\xce\xbc*\x85S\xabZ,\x1a\xd6BU\xc3\xb8\xb80\xc0\xdc\x1a\xd6&\xefLF\x0f\xa6\xc1\x88\xb2|6;N\xbc\x87\xaa2\xc2\x94\xe5f\xc5\xea|\xa6\xff&\xb6\x05\xb3\xac\xe4\xf5\x91\xee\xdb%+\xb5\xe17e\xe71\x9f,\x0d\xe7B[\xc1\x9a\xa67\xa1\xf41\xcb\xabu\xdf\xb1@{\x8e\xd5\xef\xd9\xb8\x13\x8e\x8a\xc1\xbcE\xbe\xca\xb1\xd6\x15\xcfjv\x87\x8d\xba\"\xd1\x94a\x0fV\\\x90M1aYH\xdf\xe9\xf0O\xe7\x0b(\xd8\xa2\xd5T\x1f\xc5\xfd\xd1\xa7[\x01\x04\xc9\x01\"?\xc2\xed|s\x0f,\x9b-![o\xa5\x01\xf8\x84V\x1c\x12p\xfa\xf7]\xb6\x1c\xbc\xc1-*zh%\x12\x14\x00\x88\xfb\x9c\xe7\xf9,kY\x07 +\x0b\x8a\x07UG\x1a\xaa\xcb\xcbY\xb1\x99O\xce\xae\x99\xfcJ\x87\xc9OZL0<\x06P\x11_\xc3\x07|\xb6\xc9\xe4\xf2\xe6|zg\xfe\xa4\n\xe2\xb8_\xb3FQq\xc4\xf0\xea\xc7#\x1fr\xc7j4\xe5\xb7eUO\x806=\x1a\xc7\x9f\x90\x96\xd9\xb5ao\xaa\xaa`\xa3\xcb\x9f\xbb\xc9g\xf2\x8b\xa1ikv\xc7\xea\x91RW\xb3\xaa\xa7\xa7M\x9a\x0f\xa8]53\x8f\x91\x91\x1e\xfe\x0d\xb5\xfd\xa8\xea9\xab\xa7>\xf8\xcb\xbc\x9c\xb1\xa70\xab\x9aU\xd5\xe6sd\x8e\x7f\xe03k\xd1]Y~s\xaf\xee.\x90XF\xcd\x1a\xbd\xb36\xa4\xf8\x7f\xa0\xabz\xd0\x892lT\xac(rh\xebdTy\xc1D\x0c\x1a\x97\x9cK\x85cR\xc5\xf0\xa8\xdc|\xa9(\xb6\x94\xf8\x84E\xa1\x97+\x95\x80)\x15\xc9\x93\xb2\xb2Kp,\xa9\x9d8RQ\x0c)~p\xb1Y\x11\xc7\x8f\x8aaG\xb98\x0b(nTbf\x14\x8a\x17\x95\x90\x15\xe5\xe5D%bD\xed\xc2\x87\nfC%\xe0B%fByxP\xc9YP\xfb\xe1@%g@\xe1\xf9Oq\xec'\x87\xd1}\xdc\xa7d\xcc'\x1c\xef\xc9\xe0x\xb5\xcf\xaf\x899O>\xc6\xd3\x8e|'\x07\xdb\xc9\xbb=\xf12\x9dp\xfb\x97\xb4,'\x1f\xc7\xc9_\xa68~\x93\x9e\xd9\x0d\n}\xec\xa6\x84\xdc\xa6\x1d\x98Mf>\xa2\x8b\xd7\x94\x96\xd5\xe4\xe64\xa5`4\xa1(9\x1e6\x13\x9a\xcbd\xa7\x1d\x84\xf3\x98\xec\xba\x8c\x10_\x12\x06S\x88\xb1\xb0\xec%\xbfM\xd0\xcc\xa5\x08\xde\x92\x19\x0eM\xc4YB1\x96\xfc|%\x0c[\xc9i\xc5P\xa6\x12\x96\xa7dc)%\xe0(\x050\x94\xe2\xf9I\x0e\x16\x10\x96\x9b\x94\x98\x99\xe4(\x91\xb1\xa7Fq\x92\xb4\x87\xd6\xa0\xcf\xc2HJ\xccG\xb2\xb3\x91b\xb9H\xc2#`*\xb8\x99\x89\x94\x96\x87d;\xf8y9H6\x92\x84\x8d\x7f\x94\x96}\x14\xcf=\xb2\xf0\x8c\xa2XF^FQ\x18\x9f\x08\xcd&\n\xe4\x12\x850\x89\xac<\"{i\xb0|\x0e\x1c\x87(\x90A\x14\xc0\x1f2V--w\xc86(v\xe0\x0d\x19\xfd\x14V\xd6P\x1cg\xc8\xc5\x0fJ\xcf\x0e\xda\xbd'\xa1\x99AX^\xd0x\x89DD\xa2K\x00\x00\x15\x85\xae\xe2\xce\x7f\x99^\x82\x0c\xdd\x92A\x11\xe8\x14\x81N\x11\xe8R(\x02\x9d\"\xd0{\x89\xc1X\xac\xca(\x02}[\x12\xe1-\xbb!.\x11\x98K\x12\xd4%9\xee\xe2E^\xf6\x80\xbd\xec\x0b}\xd9\x03\xfe\x12\x82\xc0\xc4b0\xce9\xdc\x87\xc2$\xc4a\xb0HL \x16\x93\x1c\x8d\xf1\xe31;#2\x14\x81\xee-Y\x1cBcTE\x11\xe81X\x8d\x0f\xadI\x83\xd7 A\x08/f\x13\x80\xdax#\x81\x03\x91\x1b\x8a@\xa7\x08t\x0c\xa6\xe3\xb5j(\xae\x83Gv(\x02}\"\x89q\x1e\x8a@\x1fJ,\xeacTF\x11\xe8\x01\x18\xd0.(\x90Q\x1dE\xa0\x1b_@\xe1F\x14\x81\x9e\x0eE\xa2\x08\xf4\x9d1\xa64}\x0e\x8d3\xe1\x91&\\\x04z>\x9c\xb0G\x07Hq\"\x95\x81)\xfc\xb4\xa3\x82Ud\xf7[M!\xa6=]Pk\x89\xbd\xe1\xc5\xac\xde\xe3cof\x15\x9f4\xab\x92\xef\xbcd\xf4M6\x13q^\xea\x95Q\xc4\xcd\x99P\xfe@W\xe2@#n\xa4 \x0e\x03z\x12]#\xda\xe9\x92\xadxKD\xbe\x8eqy<\xab\xf2A\x9a{\x95O@\xba+\xc6w/\xf3 3+U\x81l\xa7\xf3\x97\xaf\xae\xce\xe4\xbd\xcd\xf29\xb5P\xe7\xc2\x97r^\xb6j\n\xeb\xfcW\xc3y\xcc\xa8Pn\xeb\xcc\x1fk\xf2\xdb2k75k\xba\xe1\xc47\xb1\xb7\xd5m%&\x8d\xf1\xda\x8f@\x93e\xe7F\xa1\xc9\xa6\xd7\x15\xb6,\xb5\x10\xa6L\x98\xb2\xd7\xa1\x8a\x19\xa0@\x982a\xca\xd6' S\x16B\x98\xf2\xb6\x10\xa6L\x98\xb2M\x08S&LY\x08a\xca\x84)\x13\xa6L\x98\xb2\x14\xc2\x94 S&L\x990e\x9b\x10\xa6L\x982a\xca\x84)\x0f$\x05\xbeG\x98\xb2\x10\xc2\x94\xbflLy\x0bO\xd6\x91\x8b\x9f\x19P\xbee\xad(\xcf\x8a\xb5\xd9\xb4\xbaiU\xcezj9\x1c\x02?\xb7\xf9\x1d+\xe5\xd3N}\xdc\xc4\x02\x86\x81M\xd6V\xab\xc7\xf6\xed3\xfb\xb0\xaeJfG\xf3\xc1\x8b\xa7\xf62@V-\x17\x92K \xb1\x8c.\xdf\xd08\xeb\xea\xbdt\x97}\xf3u\xff\xbbX\x98\xaa\xd2\xe5\x0d\x96\x00\x9c\xe3\xa0\xc5\x07t\x96\xab=\xf5M\xd6\xb0k\xd90\"\x11\xb2L\x80\xcb\xff\x9b\xf1\x8dv\xdf\x1eN}b\xd2xS\xe6\xed\xc3F\xb5\x9b\xf3\xf1oTW\xf8\x01\xbe\xf9\xfa\xff\xef\xea\xd6\x17\xc5\xfd\xb6\x00\xde4QbP\xfej!\xfbA\x8f \xc9\xdb\x0c\x1c\x08 \x0c\xcb\xce\x15<\xe4\n\x1ez|\xcb0l\xb1\x1f\xe0{\xbe_\xd84O\xe1\x1b\xe0o\xcbZ}\xef\xed\x93Y\x91g\x8dk\x88\xfa'\n)\xce\xe9B\nr0\xfbn\xd2\x97\xa2\n.\xf3\xbe\x17y#\x0c\xa7\x86\xb1\xfeM\xaf\x9f\xfe\xae\xe3\x1a\xe6\xae$\xfeC\xe9\x9bp0~2^\xa6\xcd\xac\x9dzc3O\xa1F\xd4\x9b\x8d\xea\x17j\xa8\xe43I\xd31\xb7\xaa\xcf|\x83\x85a:\x0bj3\x0e\x07\x92\xcc\xce\xec*\xec\xac2B3|LD\xad\\\xd8I\x8b\x7f`Z\x03\xf179\x10\x1f\xf5H;\xff\xa5\xab\x93E\x9b\x18\xc9\x83\xf1\xf4\xb5q\xd0\xcc\xf3f]d\xf7)\xeae\xedI\xea\x1b]>x\xb5BmnoY\xd3\x8a{A\xc4<\xc9\xfbSWE\xb7*\x99o|V\xe4\xdcN\xa6j\x89\x9di\x82:=4\xe7(\x7f\xe8\xea\xa8\x0f\xc5\xa2\xab\x19h\xad\xe6It \xec\xdf\xb1\x12\x1e\xb1\xdb\xa7\xf0L(\x85S>\xa1\x9946\xf7\xab\x9bj\xbf\x19\xb1\xe5'\xfa\xeb\x10\xc4\xdd;\xf2o\x9a6\xd1,\xf9I\xb0*\x81}\xd0\x08+/\xbdE\xe1\xe9\xd5\xab_\x1e+6\xcd\xcc\xe6\xa3\x00\xdd\x87\x1b\xe1e\x92\x1fWM\xebH\x17\xeaK\x17\xafeS\xe7{5\xda\x9b\x8bs\x01w\xc3\xbc\x9am\x04\x8b\xe7Q\xc5Wx\xa8\x16\x8b'\xb3e\x96\x97\x8f\xd5M\x1d\xca\xd9b\xd13p\xd5\xe4\xa5\xdc\xff\xe4Uy\xdc\xdd\xfc\x14h\x8a\xef\xcd\xa6\xb8^f\xcdr\xdf\xf6\xf8{\xd6,\xe5\xca\xd5,\xb3o\xff\xfa=\xf0\x8f\nWYo\xa4u\xc5w\x81\xc2\x85\xfa\xe6\xe2\xdc\xb6\x82\x9f\xf39Z\x80\xb8m\x05w\xac\xce\x17\xf7\xc2\x986S\x88\xae\xa3?1\xcf\xe7\xe5\xc3V\x01\xf6 \x0d\xe9\x9f\xe7\xf4\xd1\x0e\xb1T\x1a\xde\xce\xec\xab\xa0m\xf5\x13G\xdc\xd1\xa92\x9e\xe6i:\x9c\x12\xbf\x93\xf8\x9d\xdb\x82\x9b\x10\x88\xdfI\xfcN\xdb\x93\xc4\xef\x14B\xfc\xcem!~'\xf1;mB\xfcN\xe2w\n!~'\xf1;\x89\xdfI\xfcN)\xc4\xef$~'\xf1;\x89\xdfi\x13\xe2w\x12\xbf\x93\xf8\x9d\xc4\xef\x1cH\n\xae\x1d\xf1;\x85\x10\xbf\xf3K\xe0wN\xa9$\x9f\x96\xab\xc9\x1b:/o\xf1\xd9\x7f\xb2\xa2\xd0t\xcc\x06\xd4\xdb\xa2\xa3H\xfe\x9b\xca\x08$r\x04)%#\xba\xe6\xdf\xe5\x1b\x0ft\xc5\x0e\x94\xa2y\x93\x15\xdcP\x07\x82,)\xa2\xef\x0e^\x15#oX\x01\x99\x1d\x8dX\x13\x86D\xd5E\xdb2\x8b?U4\xaf\xbb8.\xfe\x97\x87\xfb\xe5\xb1\x96\x14\x9f\xcd\xa4x \xa3(\xeb\x817\xb1\x92\x14\x94*\x8c\x8fHJ`\xaa%o\xe1]\xae\xc6\xc8TL\x0e}j\xe1p%d\x92\x12\x96\x96\xa9\x97\x91!eR\xb2!+h\x9e/\x16\xacfe\xab~\x13\xde\xe5a\xe76:0\xddl\xb8\x1f\xd5\xeb]\xa3t\xd4\xf4n\x14\xf1\xb6\xd0_Ygy-=\xaa\x16g5\x9fVsE<\xdc\x9aT\xb3m\x1e?\x88\xb5\xe4V\xb5\xfbvg\x1c\x99\xa4\x7fpX^\x8d_\x0d\x7f\xd6\xbcGn~\xd6\xb4\xdbvq\x8eH\xf7H,\xd9\x87\xf6\xfa\x1d\x8b\xa5\xc0y\xdd\xc1~\xee\x08\x0cJ\xa1\xe7<\xfe\x9f\n\x1d\xc9\x1a\xc5\x86y\x9d\xdd\xb2\x0be\x01\xf9\xbbE\x99\\\x0c\xb9\x1a\xae\x96\x1b\x92\xc1\xaajZ`\x02s\x10@\xc51\x9c\xb7\x83\xcd\xd9\xba\xbd\x87\xdc\x06\xbf\xb7KV3\x01H\x95\x15\xac\xaa\x9ai\x00\xca\xd8A\xab6\x8b%\xadicn\xec,p\x1f\x1bT|^XQ\xfcG\xb9Y\xddH\xf7\xb9\xc6\xcc\x06\x00\x8d\xad\xbeCC\x8b>\x7f-\x94\xd9\xe6\x88\xf7Y\x03\x0dk\x8f o\x1b\x0d\x056\xb0)e\xa7\x9eKt\xe4}\xde\x8c\xfb\x07\"}\x9b\xda\x8b\xc4\x13{d\xec\xc9\xdf\xc5B\xd9P\x027\"\xf8x\x97\x7f\xec\xe2O\x04\x1f\"\xf8\x98\x85\x08>B\x88\xe0\xb3-D\xf0!\x82\x8fM\x88\xe0C\x04\x1f!D\xf0!\x82\x0f\x11|\x88\xe0#\x85\x08>D\xf0!\x82\x0f\x11|lB\x04\x1f\"\xf8\x10\xc1\x87\x08>\x03IA\xb6 \x82\x8f\x10\"\xf8| \x04\x9f\xc3\xb8\x14\xac/O\x0f\xfc\x1e\xbfc\xf7\xb6\xb2M\xa0T\x85\x9dfjJ\xafY\xbb\xa9K\x99'D\xc2y\x8a\x8a\xd3\x01\xad\xc25u;\xf1\xe1\x08\xe4T\x13\x86\\\xe0\xe9+\xbe\x00W\xa58\xbbV\x8bE\xc3Zn\xa0qqa\xe0Zo\xd8\x08\xb5\xe6\xb6\xfa]\x11\x9f\xb4\xf4\xc6ZdE\xe3\xb5\x96\xc5Qa0\xa2,\x9f\xcd\x8e\x13'\x81\xaa\x8c0e\xb9Y\xb1:\x9f\xe9\xbf\xc9\x9c\x16Y\xc9\xeb#\xbd4KVj\xc3o\xca\xce16\xd9\x0e\x9f\x0bm\x05\xefC\x9d \xa5+i\xd3pS\xbfc\x81\xf6\x1c\xab\xdf\xb3q'P\xb4\xc1\xbcE\xbe\xca\xb1\xd6\x15\xcf\xf6\xe9V\xcc\x08\xb5t\x9a\x0e{\xb0B}7\xc5\x04L\x95.\x92\xe1\x9f\xce\x17P\xb0E\xab\x11}\x05\xf1\xebM\xac\xf0\xf7\xca\x01\"?\xc2\xed|s\x0f,\x9b-![\xaf?\xa3\x15\x878{\xff\xbe\xcb\x96\x837\xb8EE\x0f\xad\xc4D\x032\xe9\x9a\xcc6\xd4!?\xca\x82\xe2A\xd5\x91\x86\xea\xf2rVl\xe6\x93-j&\xbf\xd2'\xef\x19\xb7\x98\x00r\x07\x1ea\xbe\x94\x0ch+\x93\xc9\xe5\xcdy3i\xadI\x15\xc4\xae\xbef\x8dB\xdc\xc5\xf0\xea\xc7#\x1fr\xc7j4\xe5\xb7eUO\xfc\xe9z4\x8e?!-\xb3k\xc3\xdeTU\xc1F\x0c\xaen\xf2\x99\xfcbh\xda\x9a\xdd\xb1z\xa4\xd4\xd5\xac\xea\xe9i\x93\xe6\x03\x06G\xcd\xcccd\xa4\x87\x7f\x83\x95\x02\x83\x14\xc9\xf7\xa6\xae6o:\xa1}\xda)\x90\x04[\xb26k\x1a\xd6J\x17\xa6\x8f\x0b\xfb\x92\xb5\xa7\xfc\xe9_\xc5\xd3\xcaH\x0d\x94\xac\x05\xa1EyBEo\x1d\xad\xe2#\"\xecX\xcb\x03]\xff\x03\xe5\xc3\x96\xac\xbd\x16\x95\xbb\x96\x95;\x0cB\xc6\xba\xceg\x1e(\xe2@R\x8e\xfa\xd9\xa3\x085\x18\xf0\x01\xc2y\xa3\x08vhZn(\x92\x19\x1a\xcb\x0bU\x146\xd19\xf4^`V\xf1\xa2\xb5L\xad\xce\x1a\x0d\xe2=\xfaa\x03wU\xb1Y\x99\xcb+\x7fJ\x00w98w~\xd6\x9d.\x87\xaeN\xbfD\x8a\xc6m\xc6\x87\x06\xb9$\xbfg\xb5\xbd \xd6\x9bz\xb6\xcc:\xfcs\xc9\xa4\xb9\x8c\xcfo\xd6\xf3\xace\xf3\xeb\x9b\xa2\x9a\xbd\xbb^\xb2\xfcv\x19{\xc1,\x04\x1a\xc4\xf4im\x03\xf17P\x7f\xab\x16\xd2\xdd&_0\xcd4R\xe1h\xd6\x1d\x8c\x11i\xb8\x87[\xb3\xf8\xf64'\xf58&{CnP\x04\x17q\xbc\x1c(vd8#q\xb26\x11%\x91(\x89\xbe\xf1\x88]U\x88\x92H\x94D\xb3\x10%Q\x08Q\x12\xb7\x85(\x89DI\xb4 Q\x12\x89\x92(\x84(\x89DI$J\"Q\x12\xa5\x10%\x91(\x89DI$J\xa2M\x88\x92H\x94D\xa2$\x12%q )\xe8aDI\x14B\x94\xc4/\x9b\x92\xf8\xd9\xef\x94534De\x1a;'\xe3\xb5\xf8\xbd\xcb\xa7#\x00\xcd\xae\xfe|\xe2\xfepr\x93\x95\xef`U\xcd7Ew\x8c6\\$+\x15=\xd0\xf59P:\xc6\xd0\x1cC\x99\xe4\xff\x116\x19\xe6D\x1a\xdbD4\xeb\xc8 \xbdX\x0b\x05^Tj\x95}\x90\x9c\xabk~4/v\xcd\xfc\xb3C\xb2\x9a\xe7|\xde\x9a\xf1\x13\xfaSx]\xb3\x85\xbc\"\x94o\xa2\xde\xf2B\xca\xe2\xbd\x85\xbclZ\x96\xcd\x8f\xe1\x97\xecC\xbe\xda\xd8\xeeuT\xd4\x87j\x01\xf2E\xe1?-\x8a\xea}\x87\x1f+\xe7\xa7\xbc\xc0sn\xdb\x08\xb02\xbb)\xd8\xf5mu\xc7j\xd1\xcf]\x06\xdaf<\xf5b\xe3\x88\x0d\x05\xe3\x1d\x82\xc1\xd5\x81\xf9\x02\xfa\x82Ig\xb4\xd8\x80\xd4U!\xfa\x8c\xach\x03\xb9\xf5\xd65\xe9\xdf6\xedO6e\xcdx\x83\xcfZ6\x97\xb7\x9f^\xd7\xec\x96}\x88\xec \xbe\xd6\xcf\xf4\xe6\x0e\xd8\x07\xbe~5\xe2jL}\xf5YV\xe4\xf3\xace#\xd0\xde\xe50\x02X\xd4\xd5\nJn\xf3B\xdf\xd1\xaa\x12f5\xf0\xa87\x9am\xc9\xe8\x9e\xcdj&w\xf3\xcdF\x0c/\xe1\xb9*\xb2\x967\xa7\xc8U\xa6\x0b\x97W6w\xb3,m_-\xd3\x19\xaf\xef\xe1{\xb2\xefJ\x8e\x96\xe0qaQ\xb75Z\x10\xac 9[\xa3\x128)\x82\x84Z(.^?\x9b\xe8#\xaa\x04Q%|8\x01n2%\xaa\x04Q%lO\x12UB\x08Q%\xb6\x85\xa8\x12D\x95\xb0 Q%\x88*!\x84\xa8\x12D\x95 \xaa\x04Q%\xa4\x10U\x82\xa8\x12D\x95 \xaa\x84M\x88*AT \xa2J\x10Ub )`k\xa2J\x08!\xaa\xc4\x9f\x85*\x11\x98.B\xc2\\\xbe4\x11\xfd\x95i\n\x16\xab\x16\x12\xdd\xab\xca\x1e\x18S\x97\xfe\xa8wG|\x84K\xf1\x96\xfa\xe5`\x99\x08\xb6\xc4\x06VU\xe0\x85j\x1c\x19\x17<\x0e\nW\x96\x05\xe7\xab~\xc7@\xca\x9c\nQ\xd9\x14\xecs\x94=\x8fBX\x06\x05\x04\xde*\xfb$\no5\xbd\xae.\xcc\x91Z\x08q%\xc4\xd5\xebn\xf4\x0fL)\x84\xb8\x12\xe2j\x16B\\\x85\x10\xe2\xba-\x84\xb8\x12\xe2j\x13B\\ q\x15B\x88+!\xae\x84\xb8\x12\xe2*\x85\x10WB\\ q%\xc4\xd5&\x84\xb8\x12\xe2J\x88+!\xae\x03I\x81~\x11\xe2*\x84\x10\xd7?\x0b\xe2\xfa\x07\x0dN\x1f\x03\xc2\xac\xcd\xe6Y\x9b\x9d\xdc}s\xa2p\\\xf1\xcf\x8f\xfa\x87k^^\x17D<\xb8c\xe7T*x\x9e\xb5\x19\x7fTv5\xa5\x15\xb82\xc8\x9a\xa6\x9a\xe5]`\xb1\xd8\xb2\xc9\xefh\xbb\xf4\xd3\xee3\xed\x1e:R\x91\xa6\xb3\x8a\x8f\xc5\xb9\xf4\xf2\xa9Ci\x1f\xa4;\x8e\x83WZ\x07%R\x8f\x1d,\x08mq\xb18\x9d+\xa3\x82^\xf5\xc6\x16\x06\xd5\xfdJ\xc5\xec\xcaC\xb4\xc5\xd6[\xca\xb6\xdc\x1c\x03K\xa2\xa0S\x05\x96n7\x80\x96\x8b\xd7\xcf\x08=%\xf4\xd4\xe3:\xdc\x83\xe3\xd15m\x8ff=[/\x9c\xdc*\xc6\xc7\xddtd\xf1\x85\xb4\xa8\xaaw\xb0Y\x8f\x07Y\xe0\xa4\x06\xbb/\x03`\xb6B\xf8\xea0\x9f\xd7'\x1f\xf9\xff6\xae\xe5\xe0g\xd6\xfex\x7f:\x9f\xf3\x81\xd9\xd69\xbb\x93\x19\xfc\xa5un\xf3;V\n\x0f\x85\xb2\xd3#\xd6<6N\xe0\x9d\x9a\x07\xba\xc6\x07:m\x8b\x06<\x90\xd1&\xcar\x9d\xcf\xd3\x8e\x97\xa9\xf8R*\x00\xbc\x11|\x058\x7f\xae\x16\x83\xbc\x91E;\x068\xef\xf9@\xcd\xfc\xdd\xf1\xa9\xec\x06VM|\xd2\xac\x17\xd9L.+\xfcp\xfe~\xc9\xea\xee\xc2A&\x87M\xc7\x0b\xcaKx&\xeej2\x9bG\x81\x1d\xa2\x93\x1d\x80\x99\x84\x7f^\x8c\xffQ\xc9\xd4\x1dz\xc33\xe9\xe8w\x17\\\\\xcbm\x16\xd36\xd6X(\xdfl \xe7\x83n\n\xa3\x8e\xea}9\x9a\x15\xc7\xe2\xea\xd1R\xac\xfdZ\x8a\xa7wK\xf1\xf5q)\xaa\xfd]\x8f\xa0\xdaQ=(\x9b\xaa\xdb\x99/F\x1b\xd7G\x12F\xcd\x8d J\xb4\xd4Ua]\xa6\xa4\xf8\xfb\x83\x94L\xe8\xeaG\x8e.F\xd7\x84#\xf2\x84\x1b\xab\x06i\xd0\x99\x1c*\x1b\x17\xee\x0d!\x06c\xe5\xc6y\x9d\x14\x88%\xe5\xf5\xe9\xc5\xd5?\xae\xaf\xfe\xf1\xfa\xec\xfa\xcd\xcb\xcb\xd7g\xcf\xce\x7f:?{\x1e\xf2\xda\xab\x8b\xf3\x9f\xcf_\x9e^\xbd\xba\x08y\xeb\xf2\xec\xe2\xd7\xf3ggA\xef\x9c\xbf\xfc\xf5\xec2\xf0;\xcf\xde\\^\xbdz~~\xfa2\xe4\xa5W\xbf\xbd\x0c+\xd9\xe9O?\x9d\xbf8?\xbd:\x0by\xe9\xd5//\xcf\x7f|s\x19\xf2\xca\xeb\x8bW\xbf\x9e\xbd<}\xf9,\xe8C\xcf^\xbd\xbc\xbax\xf5\xe2EX\x9d~=}q\xfe\xdc\xdb\xac\xfa4\x10\xd3\x910\x98m/\xb6\xbe\x1a\xf5iP$Xy<\x99U\xa5\xf4\xadx\xde\xb1u\xfc\xa7\xe6?\xabo\xc8\xeb\x9b\xaa:\x17\x17):\xa8S\xa6\x8f\xe8q\xf2\xd4\xf4\xc7\x1e\xfe\x9d\xb3\x9bV\xd0\x9a\xf2\x99\xf0>j\xac6\xe8[z|=5\xfdQTF\x82\xbd\xf9\x0c\xf2\xf2\x8e5\xa1u\xe9\xc6\xe2S\xe3_u\x93\x94m\xde\xde\xcb\x05\xb6\xab\x9f\xf0\x85\xcd\xf3\xacT\x95T\x17a \xd3\x86UR\x8c\xed\xa7[\x7f\x19d&\x133\xfa:\xab% &+\xe5\x92\xabW\x1c\xbe~\x06}\xb0\x9b\x1b\x9e\x1a\xff*\xed*?'\xdd-%d\x8bE^\xe4Y\xcb \xbb\xad\x99\xd8&\x04}R\xcd,O\x0d\x7f\x93\x9f\x13{\x95\xac\xe8X\x08\xf2\xf3\xdd\x9eF\xe4`\xe3\x15_\x95\xf9\xcd\xa6\x01\x91\xd1p\x1cA\x80+H?_=5\xff\x19\xd4}\xa80\xbe\xe8\xb5k\x80\xce\xe7:\xb8\x8aM\xac\xf3\x82\x84\x00\xcd*\xabU\x91\xb3Y\x0b\xd9,x\x10\xf7s\xe3S\xf3\x9f\xc7\xbd\xb2\xc7\"\xa4\x8dzr\x8e\x1a\xe5j\x1b\x02\x8fr\x06\xac\xacZ\x07%\xc8T\x9cn\xd2}j\xfc\xab\xa90:\xc1\\\xa3OnbLt%q|^\x13*1;4W\xea?-\x984\x81Z\xd4N\xeb\xfd\x92)\x86\x95n\xf2\x87M\x1fM\xc0\xab\xab\x0bi\xd5\x86\xd9\xb3\x9d\xc2\xeb\xc1x\xd6{H>\xdcN\xf2\x12\xf4\x99W\xec\xe9z\xef\xa7C\x9f\xf2\x8b\xea\x8egy\x12\xbb\xbe]-Y#Rf\xf2\x1du\xdf\xe5\xa1\xad\xd6P\xb0;V\xa8M\xbf\x9e\x82j6\xab\xea\xb9}\xda\x93\xbb\xd0\xe3\xa9b\xc1\x11\xe5\xb6\x15\x87\xfb.K \x1f\xf5VU\xabj\x9e/\x1c\xd4a\x81\x04\xf2\xd3\xcc`\xe7\xab\x8f\x91\xf2\xeb\xe3S`^\xc2\xa6t\x8dQaY]\xe2\"\x17.Q\xc5smd\nB\x0b\xfbMz\x84\x847a\xcf\x87\xa34q\x04\x9dY\xf4\x91\x8fWVrc7\xed\xb2\xaa\x05\x1d\xac\xadxc\xb3\xdcA\x8d\xab\x16\x8b'r\xca1:\xef\x07\x07{\xa3\n\xe1\xbb\xbb\x16\xfd\xeb\xdas`KY\xfb~\x10\x8e\x17\x1d\xde\xce\x12\x9b6U\xc4\xaaOw\xb9\xcb6+\xe7Y=\x1f.\x14j\xddj\x04&8\x8e\x86k\xec\x1d{\xe4\xe9\xeb\x0fz\xa2l\xa2o\xf3\xa9\xa4m\xeb\xfcf#r{\xde\x0bW\xa1U\xdd\x0dSd\\A\xc4\x10(\xa6\x9c\xc3\xf5\xa2\xc7{\xf9\x8c)\x0e\xec\xe4\xa2\xef^\x94\x13\xe5Z\xcc\x94\xd7uU\x14\x9b\xb5\xbb\xb9\xdcs1n\xc6\xc6\xb6\xeaoj2\xcf\x8a\xa2\x1b\xc2#\x87\x86\xa2\xf96\xddP\x16s\x92\xcbhz*\x1c\xa9y\xd8\xe8 QB\x9a\xf6V\x94\xcdT4\x95\xca\x85+6\x1b|\x1a\xd5\xab\xca\x0f\xe2NrY8\xa53+\xed3\xbf*\xb7\xae],\x11\xf5R|\xaf\x0f\xf2\xab\xab\xaa\x05\x916\x98\x95\xca\x8d\xc6\x17\x97\xa2`3M\xb9p\xcd\xf9\xbc\xe0\xa2_U%\xe3\xfb\xa2UU3{\x11G\x05\x94\x9e\xd1!'\xf6^\xff\xad\xbb\xdbw\x00N\xf1\xf6[T\x9br+\xeb\xaen\xd0\x03\xf1\xb1\xca\xd2\xec\xd9}\xf8i]\x95\x98~\x05\x82E%\xfc\xba\xf9\\\xef\x16\xba\xdd\xb1\xc1\x81\xf9>\xeb\xb6\xdfV\x85*\x03\xb1\x1c~\xd2\xb0\xe6\xc9I\xf5\xb8=\xaf\xbe\xe4\x9at=E\xaeI\xb3+&\xe4-rM\x92k\x12;\xdbJ!\xd7\xe4\xd6\x1f\xc95I\xaeI\xd3'\xc95\x89.!\xb9&\xc95\xb9/\xd7\xa4\xbfd\xdd\x01^\xb1\x1b\x98\xd8/\xca\xe4\x16m\xae\xcf\x87\xdc\x1e|\x88\xb8\x9c3\xce\xa2\x08J\x97\xad\x90\xfe\xfd\xa4\xbf\x1e2CG\xd5\x1bo\xe8\x17S\xc7y\xf6\x81\xcd6\xe2T\xa4\xb6\xc6v\x0er{\xbf\xceg\"+\x83\x885\xe3\xda\x8d\xcf*E;T-\xe1\x99Po\xf8\xc5\x04+\x83+D\xba\xa8\xb6\x12\xa1\x07\xb3\"ge\x7f>\x14\x8eD\xab\xaemv\xb0\xf3P\x98m\xe6\xb9\xd5\n\xd8\xf2?S\xf7\x1c\xdd\xdc\x1f\xc1f=\xef\xfe\xbb\xcdW\xaci\xb3\xd5\xba9\xd2\xd1\xf2PnV7\xac>r\xbaqjV\x08\x1dy\xb9\xa8l1V\x88\xd3&\xe6\xac\xa9\xae\xa2\xb9\xe6\x85vMf\x88\x0e\x01\x83N\xc1\xd5=\xe1\xd5w\xa9\x94cC\xb9\xe8\xd9 \x7f\x9c\xcfj\xca\x95\xc6\xca\xb6\xbe\x17\x1e\x00\xf7u9}\x1dn,\x17\xfcHA\xd6`P*\xcb\x81Y\xae\xf5\xaa\xc9EQ\xa5\xf3\xcb\xaaTu\x89\x83\xb6\xb1\x889S\x05\xf5V\xe4S\x1aZ <9\xd2\xd2j\x90\xf9Kg#R\xf72\xa0T[.]\x93\xe2\x9f\xe1A\x04\x95u\xab\xf1d&\x90\xb5\xcc\x1b\xc8\xcbY-\xf6\xab\xf6\x0b\xd3za\xd9l\xa9\x9a\xc3\xfa\x9c\x95\xf8\xddKXS\x05\xd4\xb1\xcb?\xb0\x85\x92\xb8\xfd9bX\xe5Uy\"\xeb\x06\xec\xce\xbe\x81\xf7\x97\xea\x94O\xee?\xf1\xf5\xa4\x81Y\xb6\x96\xfb\xa2R\xb6\xacHQwSmD\xf2!\xd9\xfd}\xdbt\x91\xdb\xe4\x1dS]\xb2K\x17S\xce\xf5\x88b\xf7\xd2!\xbd\xca\xe6\xa6jb\x96\x93K\xb5\xe4w\xcew\xc3\xea\x0f\xd9m\x96\x97\xbc\xc0\xdd\x9ah\xd45v\xa8\xf27\xb2r\xc6,p\xc4\xd5\xc0\xf1&B\xc7Dn\x9al\xa2\x84WVm\xb5\xd4\x06\xcc\xa8,/\xef\xaa\xe2N\xa4\n2\xfe.\x13\xff\xfd\xa6\xacV3\x01g\x95\x95D\x06.$\x94\xa0=\x82\x99\x82#\xac8p\x0fLd\xda|G\xa2Q\xb5-\xc5\xbc\xb1\xaa\xeeL\x97\xf1\x8d\xd1\x06\x0d\xfb\x8c\xf1\x06\xfd\xd7 \xc4A\x01\"\x87\x018|\x92]\xecI\x97Y\xae\xee\xdd\xbb\xd2\x0c\xc7\x00\xbf\n\xd4t\xa5\x12\xafH\x14\xc0\xaa\xce\x80\xd6\x9f\x16M\x05*x\x92o\x13\x7f\xe2\xa7V\xeb\xf6\xf6\xd3 ,\x18\xcb\xf4@\x87\xde\xcd\xebQ6E8\x86H\x86U\x9d\xb4'<\x124v>\xa3.\xf2\xa2e5\x9b\xc3\xbb\xbbu\xcd\x16\xf9\x07\xde\x89\xea\xac\xadj\xf3\xf1Y\xf9\xcb\xadf\xf1VH)\xe8\xd2\xf8\x0d'T]\x93i\xb2\xc1{p\x9ci\xfb\xf9\xad\x12\xbe\x89j\xb1P\x9e\x00a\xa1>\x7f\x8b\x7f\x07\x90hS\x8c\x80_\x10\xdd\x07P\xe6\x14O\x8d\xf7@[\xce\x99>\xdf\x94\xabk@\xdf8\xd6g\x96Y\xb3LW-^n\xaeQ\x85\xee\xf7T\x0f\xddG\x9c\x90\x8ckR\x82\xe0\xb2\xf86'*\xf1\xc2\xf6~\xa4+\xec#\xfeA\x11\xe2\xee\n\xf1\x85\xfe`\xcd;\xeb\xa4\xa5f\xd5j\xc5\xb7v\x993\xd7\x96\x88X\xfcdU\x97\x9f\x93\x87k]\xec~\xcd\x14\xa9\xe6\x9c\xdb\x08-]l\x15<\x92*\x1f\xf7K\xb4\xb0\xc9Ig\x06=\xb59\xf5\xf1M\xc2;K\xff\xc8\xcb\xf5\xa6\xfdC@\xb5\xbe^\x8cnL\xdc\xfe\xb0\x97\x97\xbc)u\xfe\xc3Y\xb1\x99\xcb\xa9\xb7\xc8\xcbwp\x93\xcd\xde\xa98\xfcA\x9aH\x8fB\xde \x1c\x19]\xd4\xcc\xebXR\xa5\xa0\xab\x8bX^\xa5\xe0\xba\xb9a\x1aU\x8be\xefY\xfe\xa9\xaa\xe1\xaci\xb3\x9b\"o\x96\x1e\xb4\x18\xf4F\xd4\xe5\x86\xf6\xcd\xa7\x01\xe6\x08\xa9\xa5y\xd2\xcd\xd9L@\x12\xc3\x15\x99W\xd9\xa3\xf0u]\xad+\xbe\xc2 \xea\xdbM\x91i+-n\x94^\xebr,\x04\x1b\xa4\xad73qb\x13\xfb \x95\xaa\xc8\xa1\xaci\xb3v\xe3\xa3L m|>\xbc\x06\\l;\xc4\x84$vk\xba[\xe9\x82z\xc7U\x07\xba\xfc\x8f\x8d8\xb3I\x8f9oB\xe1\xe2[o\xdc\xc0\x10\xda\x968\xd6\xc2\xc5\xd9\xb3W\x17\xcf\xaf\xcf_\xbe~su}yuz\xf5\xe62\x00\xfa5\xbf\xff\xfa\xe2\xd5\xebW\x97\x91/\xcb\xbfy&F\x85\x94\xefR\xf8\xb0\xd9\xd5k(\x9f\xa5=\xaf\x0f@S\x0f\xdc#\x16\xca\xac\xc8\xe7'\x9bR\x9e\x7fd_\xe4\xfd\xc2\xf3\xa2\xa7\xb9\xcc\xf6\xd4\xbfNa\xdd\xc1\x08\x18&\xa6\xecf\x1dA\xef\xed\xce4\xb2{\xc7\x95O\xfe\xcd\\:\xf97s\xd9r98G;\x9bu\xcd\xee\xf2j\xd3\x14\xf7[\xc3v\x00\x89Z\xcb\xa9&\x8c\xab:\x9b\xbd\x93@\x93\xdc\x99t'\x1d\xa6W\x13\xcc\xe9\xc4;\xfb\x0c\x95o\xedOy}f\xcb\x9c\xc9\x94\xc1PmZ^i\xc7\xf4\xe3+\x91\xd4\xf0\x87\xd8e}\x86u\xf6\xefj\x8d\xedB\x08\x94\xc1\xbb..\xff}\xd2\x9dO=\xfa&.\x91O\xb4\x92]\n]\xba\x1e\xba+\xf5G\xee\xc9\x81\xc8\xa3M\x94_\xd9A\x0d\xc1\xbc\xbc\xd5Y\x12\x8e\x16Y^lj&\xd3o\xb3\xd2\x9an\xaaS\x87m3\xec\xd2v\xf9\xe6E\xf0\xba\xb0\xfd\xe6\xeb\xd3K?\xb5l\xfc\xca\xe5\xbf\x9f\xbf\x0e|\xe5\xa7\xd3\xf3\x17\xd8U/\xa6^\xe1\xeb\x9d\xe5+>\xc3[_\x1c\xadq\xb0)\x1b\xe6\xa3\xc0\x04\xb0\xc9\xb6\x9blj(\xfe\xb7\xd1B\xc1\x06\xfd\x9e\x0f\xe0>\xb9G\xe8\xe7xsO?\xc7\xff6\xf8\\W\x15~*n\xf2\xb9>;\x8b\x0f\xbf\xcb\xd7k6\x87\xf9F\xacR\xab\xbci\xf80R\xb3\xbfH\xeaUd\xf7l\xde\x977\xb4\x80\xbcsM\x0b\xc8\xfff\xb5GO\xc66\x96\x9a\x8fl\x93\xfb\\\x0bf:\x92\xc7\x8bWr\xf6`\xe5,[7\x9b\xa2+\x8a^\xce\x16b\xd1v{\xb1\xa0_\xcb=\\\x1d\x7f\xb9\xe4w\x1f5\x8f\x07\x99\xa8D\x8a\xffj\xa1\xcdS\xde\x8e&OOv\x7f\xb9\xbd\xd7\x0f\xf7\xf13:D\xc41\xfd\x1f&c\x7fZ*m\xa8\xde\xb3\xad\xce\xd9\xa3\x07\xed]\xc5\xe1\xf6\xd6p\x81\xe1ewK\x9ev\x8e\xf1j!\xf6w\xa29\xb3\xb6\xcdfK\xf9\x95\x8e\x07\xc2\xc7\x17\xcbf\xe6\xa5n\xdc\xebU\x9f\x14\xa7S\x07eidH\x85\xfb\x8c!$\xfd\xc7 \x04If\xe2\xe1F=\x10\x14\xe9\xd3vO\xd5\xe0]\xbc\xc86\xb64\x86&\x9d3\xc1\xb0\x890\xc3\xe1g\xb1\xa9+\x8chq\x17-6\xfa\xfe\x9ey:>\x9f\x0b\xc2\xe6\xb0\x9d\xdeJ\xf8\x10;{\xb6\xcb\xbc\xbc\xb5\xcf\xf1\x08\x1bBtI\x06\xff\x94\xb3\x8a\xbf<\xef\xd9M\x93\xb7\xce\xd4p\x10Y\x1eq)B\x05\x8b\xbc\x9c\x0b\n\x82\x04\xa6]t,\x80|V\x95\xfb*\x8bt\xf5\xf1/\x98\xbf?\x8a\x8f\xdd{\x88\x13>\xc4\xb6\x8f\"V+\xc50P\xd8}\x9a\xbd\xdf\xd9>)I\xdd\x0e\x85\x011>{\x8f\xef\xf9T\xb1=\xfb\x8c\xeb\xf9\xec1=\x9f4\x9e\xe7\x13\xc7\xf2\x1cD\x1c\xcfa\xc7\xf0\x1cP\xfc\xce\xe7\x8a\xdd\xf1{\x04@F\xbc\\\xf1\x8e\x91\xd5\xd2\xd9>\xcf\x17\x02VhE\xa4K\xd3E\x9f\xf0c\x81CQ\xd74\xabL\\\x0bb\xdb\xb5x\xcbt*\xd3\xaf\xf4\xb4KE\x8eVt:\x9d\x86B\xdc\xa8-7U\x8f\\\x01\x06\xed\x92\xe5\xf5\xd0\xf7+jen/]\x07q\x12\xbd\xceM\x94F)\x9ff\xef\x8589b\x17\xcc\xde\xa8\x96\x1c\x04\"\xa5\xb2\xca~*\x93^\xd8\xf3\x9er\x11\xb7BJ\xff\xd0\xf4x\x1a\xee\xd2\x10\xac\xd7\xcbQy4)\xb8]\x0e\xf2\xd5\xaa\x0eq\x04\xfa\xd2\xdf\xc6L_\xeaV\xf1F^ghru4]n\x0f\x8c\xab\xc3X\xf0\x81\xd7\x02\xb6\xd3vL\xb2\xd0\xf6\x0e\x11\x83&\x84\x8bd\xd47\xc9K\xf2 \xbc$\xea{\xcf\x07\xc7\xf5\xa1\x8f\xa4\xfb\xaayD9MA>\x92\xa9\x90\x8f\xc4\xfeX\xb0\x8f\x043yI\x19vny\xdd\xcd\xad\xc1\x1d\xa8\xe3v\x96Y9\xbfw%\xa8\x99Dvf=\xb5\xe9\xe0\x9d9j\xb4\x9f:\xe2\xb3\xaa\xf7e3\x99h\xf2\xd62\xd2\xc9\x8b\xe3~\x83\xbc8\xae\xc7\xc9\x8bc\xfe;yq|\xdf!/\x0eyq\xc8\x8bC^\x9c\xcf\xed\xc5\xc9F^\x9c{U\x8a\x91'g\xb1)\x8aE^\x14L\x06\x15[U5\xf9m\xc9'\xc3\x0c\xda:+\x1b\xd9i\xa2\x8e_\xdam\xf0\xa9\x8e\xaa\x0e\xfbx\x038\xc6\xf7\\6\xeex8g^\x17\x17\xa1\x14Q]C\x00\\g\xf2\x9b\xbc\xcc\xea{x\xd4Gf\xe8\xd0k\x8b\x83\xad\xc8\x9a\xc6\x19T\x81/\x90\x8c\xefR\\.\x11\x93\xd5v\x17\xf9\x0f\xbb\x85d6e7\x85\xa9\xb1\xdc\x0d\xf5L\xe9\x08pM9\xfb]s\x14\xe2\x9a2[0\xde]5\xf6 \x8d=V\xe6A\xb4\xb3\xd3J\xc5Q\x91\xcb*\x85\xcb*\xcd\xb0q\xccI/\xd5\x88\x92\xecE\xd5\xf6\xeaj]\x9d\x04\xa6K\xa8b\xd52\xf1+;\"c\xbf\xc8\xc8\xc7\xe1\xec\xd5\xf5\x02_hT\xbb\x9fX\xb4\xacW,B\x82U:-\xc1Jm\xab.\x82\xb6\x9f\xb5\x9dys)frK>C,\xc7g\x8c\x99\xc4\x14\xf1\x9c\xf7t\xf3z*C\xb6\x8f\xfa>\xe9\xce\x1c\x06 \x17\xd5A\x18\xd4#\x96\x8b<\x81\x93\xe4\n\x8f\xf9iE~\xc5\xed=\x90\xc3\xd0\xf1@w\x17j\xf4\x1c{\xda\xa5\xef\x93\x0c\xf8\xd1\x8e\xb8\xc9\xda\xbcQ)\xa0*\xc8\xd6\xeb\xe2\xde=\xd7\xca&\xb9\xdc\xca[\xa3x\xc6\x99z\xc0\xf8\xbewNAtML}\xc5\xae\xac\x9b\xee\xec7cxb\x85F\xf3\x93\x98\x91D\xe8M7Q\x99\xbb\xa5d\xb8_\x8b\xaaD\xd7\xe1J\xed+\xa5\xb2it\xd3d\xc1{\xa4\x1a\xd3\xdef2\xac\xaf\xaau\xd0\xdf\x8b\xf3\xcb+\xdb\xa0B\xb4\x81\xdb\xb7\xfb\x04\x9e\x9f\xfdt\xfe\xf2\xfc\xea\xfc\xd5K\xbc\x07o\xfb-o\xdc\xeb\xf6+\x9ehW\xdb\x0b\xc2\x1e\x96\xb7:\x8fdx\xa5\xfc\xfbc)N\x83\xd9\xed\xecxi\x1a\x1b$\"[OT\xa4\xabU\xa1Hw`\x9f\x8a\xec-\xb4m\x1cC\x98k\xd6\x87\xa0\xab\xbc\n\xca\xaf#RT=\xea\xd6F\x8d\xfc\x94U\x0bU\xf9\xc4\x97<\xdf\xd6\xa6\xdbe2\x04\xb7\xea\x14\x0fz6\xdc\np\x9d\xac\xd7\xe1\xc5\x10]\xcbV\x16\xf1\xa3\xb9@\xd9\xbd\xa98\xe3[A\xc4\xa3\xbd;\x07\x96\xd9\x9dkI\xe7\xda\x1b>)Z\xf3:\xa9K\x98\xf3\x9bB^2\xb3\xff\xfb\x1c\x08D\"\x10\x89@$\x02\x91\x08D\"\x10\x89@$\x02\x91>\x0d\x88t5\xea\x8a\x83]\x0f\"\x8d\x84\xbbZ\xa6\xd3\xf1\xd0y?>\xb8I:\xa8\xe3\xc0,S\x8f $J\x12eO:\xef\xbf<\xcc\x9f(\xf7\xfd\xd6\xdb\xfe\xb5l\xe8(\x07C\x96\xd4\xdb\x8e2\xccY\x9b\xe5\xc5\x81 B\xc2\x0f\xe6 jz\xcfF\xe2\xe4\x17\xfd\xb6\xd1\xd1\xdd\xf9\x19\x07e~\"Q\x089Tl\xbdO\xd0\xf8\xd5\xec\xbdbm&\xddm\xd3\xd0T\xc1\xa4\x15Y\x96\x8a\xaaz\x07\x9b\xf5p\x84\xe5\xe5SXg\xa3\xfc8z\x9e{\nm=rJ\x98\xdb\xcb\xd0R\x163\xf4G\xe8\x9f\x94\x11f\xcd\xdd\xe0\xf7U^\x9e\x0be\xf0\x8d\xfak\x9b\xdd\x8el\"\x06\xf0\x03\x80\x87'|\x9b\xc8\xca\xac\x9c\xb1\x13]\xf3\x93\xbbo\xbaD\x9d|\x02=\xf98\x85\xff\xfe\xf3\xa1Tv\xcb\xba\x11\xddlV\xab\xac\xbe\x1f\xcd-f \xb8f\xed\xa6\xe6\x13\x98-\xa8B\xcf2b\xad\xee\x94M\x03/\xb4\xf1m\x13\xdb\xd5t\xa9\xba\xce\xe70\xe3;\x03\xe9\xed\x16\xbe\x89\xcd&\x9f\x1f\x01;\xbe\x1d\xcdT\xdf\xdf\xcc\xbe\xce\x9e\xcc\xfem>\x7f\xf2\xdd\xdf\xfe\xf5\xbb'\xff\xf6\xdd\xdf\x16O\xfe\xfa\xed\xd7\xdf\xb3\xef\xbf\xfe\xfe\xeb\xeco\xdf\x1dA\x067l\xb6\xfc\xcb\xb7=\xb2l)\xa8\xeaE\x93\x8f\x0c\xcd\xfb\xcd\xef_\x7f\xfd\xf5\xfc\xeb\xdf\xbfe\x7f{\xff\xd7&\xbb\xff\xeb_\xb2\xc5\xefs\xd6|X\x7f\xfb\xcf\xdf\xff\xf9\xae\xfenQ\x1e\xc9-\x9c\xfa\xe4d]\x1f\x138\xba?\x9b?\\\xb3\x99\xfc\xe6\xd2\xfa\xcd\xf7\xdf};\xff\xfd\xdb\xff1\xbf[\xcd\xb3\x7fn\xde\xffs\x96\xcd\xe7\xcb\xe5\xdfnW\x9b\xdf\x97\xec\x9f\xdf}w\x0c\xe7\x8bN_\xde%U3$\xf8\xd8.K\xab\x12\xba\xdb\x9a\xbe\xdbR\x8b%N\x919\xec\xb5\xe5\xdf\xe6c\x91\xcd\xc5h\xec?\xf8\xe3\xbd^q\x8e\xac\xf9G\x1a4\x19F\xe6\x9eo\xbb,\xaa\x83\x04\xf9\x97\xac\xfb\xf3\xf5h7\xd2Vb\xd4\xcb\xcd\xba\xf8\x9d\x17d\x05*i\xb9t\x8a\xebv\xe9\x12\xe7\x9e\xcf\x9f\x9aG\x8dzP/\x89\x83\xc1\xfc\xf0\xdb\xaf\xbf~h_W\x079\x8c\xba\xb7\x87\xf3V\xa2\x15u\xc4\x92\xe8J\xbd=\xbd\x8f\ng~Io=\xde\xd7\x99\xc8\x81dn\x9cmf\xbcsAs/g\x9eB\x83O;x\xbf\x00\x01$\n\xd4\xf2\x87Dz\x93R)\x90\xd1+\x98S\x13:<\xc8\xa1\xc3\xf1\x02(H\xc3'\x0e2\xc2\x87\x19}\xcaJ\xfa\x91\xa2(\xac(\x06-\n\xc7\x8b\xc2\x11\xa3\x08\xcc(\x105\x8a\xc0\x8d\x82\x91\xa3\x18\xec(\x06=\n\xc6\x8f\xe2\x11\xa4\x90\xe9*\nEr\xea\x0b\xbd6z\xefH\xd2\xa7\xc3\x92\xf6\x8b&\x1d\x00\x9e\xf4\x89\x11\xa5O\x8e)\x1d\x08\xaat\xe8\xb8\xd2A!K\x9f\x0f[\xc2\x9d\x93\x12\xe2Kh\x84 W\xb2\x84\xa1J)\x83\x95P\xe1J\xc8-Y\xc8 \xdbi+/\x87:(l\xc9\x13\xb8\xe4\xe3O\xa3\xaa\x9e2| \x13\xc0\x14V\xac$AL\xfe\xa6K\x15\xc8\x944\x94\xc9\xd3\xfa\xb8\xcd\x9b\xd1\xe1\xa6\x99\x8aa\xc3M!Y\xa6\xe3\xe4\xc8\xbfw\x9d\xcf\xaf\xf9!\xd6\xdc\x03p\xc56*\xec\xfd\xc5\xc6\x9b[-\xaa\xf2\xf9\xc9\xe4Ra\xac\x9f\x110N%\xbfCiZ\x97\x1dG\x06j\xae\xc2Y\x19\x0c\xa5\xeb\xf0\xe0\xec\xbd\xf8\xc4\x96\xd5\\\x93\xbb \x10\xb1\xb9\x1f\xb6\x9aX\xdeYy\xd8\xd6\xb9\xd6\x17kJ#\xa9\x7f\xad\xab\xbaw\xa0\xb9\x82q\xa6\xea\xd0\xb6\x19\xffa\xb39\xf4^d(\xb0\xb2\x19Z\x89xgl\xd9H\xfb\xf1\x8e\xb8\xa3\xb9b-\xc1?\xad;\x8b\x02\xf5\xe4\x97\xba\xab\xbf\xf7\xd4g\x12t\x91\xd8:\x0f\x1a[\xc0\xae\xb0d\x1ft\xad\x83kk\xe8H\x8e \xda\x1e\xf2\xdb\xae\xfb\xe7\xe0N4~\xb8\xc9\xfb\x0d@|\x07\x82@'d\xfb#'\x89\xe4\xd0\x93\x1f~A\x16\xdf\xb7\xeb\x93\x92&\xaa\x17\x82\"{\xc1\x1b\xdd\x0bH\x974\xc2)\x8d\xeaaR0\xfdL\x8a\xaf\x89\xa4 \x1bJ=\x1c\x13\xf9\x0b\xea+\x88\xe8_\x88,\x907\xa4\x14\xc4!\xb9E\x84\n#\x14\xe1\x82\x89\x01\x1fP\x0c\xa1\xd5F\x8ez)!6\xf2\x1e\x8e\x11\xd1\xb6\\\x06!\xc8\xfe\xa0[)\xfe@c\x085Sh\xcd\xd1A\xc7\x08}\"\x12\x1b\x1fx\x0c\x01\xc5M\x19\x80\x0c\x81A\xc8\x1eU]\x88\xb2?\x10\x19\x10\xc1\xc8\x80\xb6JhP\xb2\xbb!\xf8\xb4\xb6K`2`\xa7\xfe\xe3\x80w\x17\x8ft\x04c\xed\x0d[e\xb4r\x0d\x10.\xa4&\x84k\xb0\xd5\xd8>\xae\xc1\xe1\xd8hG\xc6\xc1T\x1d\xdaB!p\xf2\x01Y+\x86w\x80a\x1eDZq\x995K6O\xe5\xa9\xdd\xbf\xf9\x06\xe5\xd5=N\xfe\xc9\xed\x8e\xe0\x92\xc0`n\xa2\x06\xdaL\xb1\xf5O@\xd6\xc0\x0f\xb7\x00\xca\x9c\x94\x90Z\x99\xe9sy\xe8\xb26T%&\\\xc8\xe7\xbd\xc3k\xde\x878\xe3\xaa\x8cZ\xe9\x00\xb9\xdaA\x10\xb3N\n\xb2\xff\x00~\xa8A`\xc3@\x00\xe3\x0e\xad(t-\x84\x18\xf6\x9d\x94\xc3\xb4\xa0o\x8d\xf4*\x0c\xe1\x96IA\xf0\x94\xfe\xa0\xb6\x8c[A\xb7U\x19V\xd1@\x0dam\xe0[< \xcc\xe4\xbb\xd9\xd1\xbb\x94x\xf5\x052\x1e\xa5\xec\xb5\x13\xeef\x91\x9dX\x81&\x85(n \xcbm8\x81\xff&\xb2R\xd4\xfd\x910\x83&/o\x0b\xe7\xe9\xa4\xd9B\xf4\x9bj\xc5\x80}h\xebl\xc0\x8c\x93\xfe\xc3\xbc\xdd.\xae\xbf\xb0F\xba\xa3t\xcd\xdcC\xb9Y\xdd\xb0\x1a\xaa\xbaK\xaaa-\xecD\x81\x11\xca\x1f\x19\xb8?\xc1\x1b\xb4=\xca\x17}z\xb2\xc7\xdb\x14M\xf1\xcbvg\x1cUW=%\x9d\x84\xb3j}\xdf\xef%\xe5\x0f\xc2E(\xe3\xe4y9\xdb%kt\x82\x15\xc3r\xe7\xdcl\x04\xa4 \xb1n,\xbcc\xc7\xdf\x98`\xf8\x18&w\xd1X0\x99\x8cF\x89\x85t\xbbbJd\xa3\xd9\x98\xd3\x0e\x8dE\x7f\xa7\xc1e?:\xb6}\xeb\xbc\x15F\xc9\x8a\xa6\xd2@\xb0\x81\xa11.\x91EU\x8a\xbcH\x06\xd5\xa6\xd4@\xae^sSU\x05\xb30\x01\xba\xd5\xde\xf1\x0c\xaek\x19\x13\x16\x89\xe1\xb5(\xb2[1 \xbd_2\xd1\xd3\xc6\xe9\x8b,\xfal\x9e\xadf;\"\x0b\xd5\x87\xfa\x1cI[\xe9\x8a\xb4\xb0\x0f\xb2\n\xce\x13\xd2\xa7\xb4\xe9\xa4@vs\xaa\x07E\x0d\xad\x89\xec\x85\x8aE]\xad\xbc\x86\xe8\xdb\xd22\x97\xc2'6\xc4\xa4@\x98~e%Pw:J\xb8\xaf6\xf5$k\x96\x14w\xa9\x8c!|\xa8$\x97\x13E2\xe5\xa59\"\xf0\xe2\xf53JxI /=\xcb\xfe'Nx9\xdd?\xd8:\xe2d\xc8\xc4n;\xd2l6\x1c\xcb\xc3h\x19\xdfq\x0b\xb1\xf3\xc6!\xf1v!\xaf\x05&\xcb8\xd2\x02Q\x8b\xff`\x99\x1f)\xb3,\xf9\xfb\xac\x7f\xea<\xb9'r\\\xc8_\xb9a]\x99sMx\x7f\x9f,\xd78E\x8dR\xe5J\x08_\x9bj\x94S\xd4\xa0Z/\xd0\x07\x9bR\xd4\xe4\xd1\xb0m\x05v?\xce\xbb6\xce;\xc5\xf9\xe3\x9c\x06\x80\xdb\x03 }\xbe\x89C/\xd3%\xeep\xfa\x07S\x85\\6S\xf2\x89#\xe0\xd2\x17n\xe9\xdaij\xf1\x12\xa4\xbd\xbdH\x8a\xbf/I\xf1\xc1\xc6\x80m\x14\xf5h\\\x80e\x8b\x895\x82\xa8\xa2 \xbc\xc8\x89\x02+\xb1a\x95j.J\xea8G\x8dd)x\xbb\xb4I\x82)\x83C)1\x81\x94\x01\xa6 \xabo\xba\x10\xca\xe0\x00J\\A\xd3\x06O\x86\x84N\xaa\xef8\xf5\xe1\x02'\xfda\x93\x18[\x84\x86L\xfa\xa6\xfa\xdd\x02&\x11S\x18\xaa\xd3\xe2j\xde\xa6\n\x94\x8c\x0d\x93D\x05Ib\xea\x12\x1e \xe9\xba\xd95\xf0nWd\x9b\xf8\"\x8db\x03#\xa3\xc2\"#\x82\"\xe3B\"w\n\x88\xc4\xf9w\xc1g<\x97\xd5\x13\x84B\x06\x07B\x1ej\x18\xe4\xc1\x04A\xfe!B [D\x00dP\xf8c\xc2\x9d\xbdwO\xe3\x9b\x8c\xec\x81d\xf8\x97\x90!\x8f\xe1\x01\x8f\xe1\xe1\x8e\x11\xc1\x8e\x81\xa1\x8e\x11\x81\x8e\xc1a\x8e1A\x8e1!\x8e\xc1\x01\x8e\xf1\xe1\x8d\xf8\x19\xde\xde#)\xb3wDP\xe3~C\x1a\x0f \xa0\xf1\x13\x873~\xf2`\xc6\x03 e<\xf4@\xc6\x83\nc\xfc|A\x8c\x98CT\xd2\x00\xc6\xae\x91|\xe1\x8bI\x82\x17}\xd5\x8b\x08\\t\x1f\xd4w\x0b[\xc4\xadz[^\xe362d\xd1\x11\xb08 \xab\xd95\x1d\xb2A]\xcfK\x0d\x89\xe9\xd8%P\xd1\xeb\xdc\xf6\xbb\xb5\xc7\xf5\xf0m\xd9S\x00$8\xfbB\xc2\xd0D#\xb5\xc0F\x90\x9c4\xac/\x08\xe3\xf3\xdb\xc5\x17j\xe1!r\xe3B\x85&\x9f\x0ca\xb5\x1f\x80\x85b\x02(\xbc\xa1\x13q\x96C\x85\x1f\x1e\x80\xc9\xdca\x87\xda\x14\x0e}\xd1F\xfa\xa4\xb9\xa1\xa7\x1fvF\x88\xa4\x1aL[Q\x13\xee@C|m\x8c\x8a!\"\xc8pD\x14\xda-\xc4\xd0\xbbF\x01j\x9d\x82\xe0\xe0BTO\x01\xec@\x82\xa0\x86\x80\x80\xa0B\x1f\xf5_K\xd0*\x06\xb1\x01\x85\x87f5\xdf\xea\xe6Q\xd7\x06\x06l!\x02\x9a\xfe`\xf6\x8bY\xfb\xb6\x15\x85\x87\x0e\xc6[\xdd\x1f6\x886\xf2.\xb6\xc3,\x07{\xed} ;\xdb.v\xd89L\x10\xd1%c\xa2\xee\xd2\x04\x086[\xf8uPx \x85\xd1a\x8f\xf5\x01|\xf6^\xd20\xdb{i\xec\xbc\xe7^(\x8cn(\xf6\x93J\xa2^2C\xc4\xabu\xbe:\x98\x98W\xeb\xdc\xf8y\xa2^m\x93\xee\xa7\x88{\xc5\x1e\xd2\x92\xc6\xbe\x06D\xbf&\x8a\x7f\xc5T3\"\x06\xd6\xa3k\xc7(X\xfc\xea\xb8\xe5\xf3n##a\xe9\xf2\xce]\x01\xa0q}\xfc\xc7\x01\xef.\x1e\xe9\x08\xc6\xda\x1b\x12F\xc8\x06PO\xa5L\x1a\xdb\x1fYt(6\xf2\xc5\x139\xfb\xe5\xb6:\xb4\x85\xc2B;\x0e\xc6Zq\xd1C\xde\xd8\xa1X+\xa2\xa2g\x0f\xc8|ty\xe7n\xf1\xb4\xf8\xe1\xb6\x15dD\x97wv\x82Y\xed 8\xbe\x16\xdd\x7f\x00?\xd4 \xb0a \xce\x16\xad(t-\x84\xd8X\xdbC\xb5\xa0o\x8d\xf4*l\x03\xe3\x1eQ1\x82\x7fP[\xc6\xad\xa0\xdb\xaa\xc2#p\xc3bp\xc3\xa3p\x83L\xbe\x9b\x1d\xbdK\x89W\x1f]\xde\xe9Q\x88\x8c\xcb\xc5\x95;Ml\xee\xce\xd1\xb9\x98\xc2\x1a\xe9\x8e\xd25\xd3_\xde\xb9\xd8\xe7\xe5\x9d\xd3R\x9b\x9f\xb2t?g\x87\xf3\xd7\xde\xf1\xadQ\xef\x9a\xde \xe43\x00\xb2N\xe6If?u\x1a\xce!\xe6\xa5\xde\xa0K\xbb!\xb2\xa6\xe7D\xb3rV\xcd\xd9\\\x15pZS\x8a\x08w\xccf\xd3\x8faH\xf2c\xc1P\xe6\xfd\xc1V\xbd\x8c;\x80\xe5!\x8a\x08\x1f\n\x05\x1fwOR\xf0qX\xf0\xb15(b\x12\x95\xbc5\xa7\x9ab\x94'\x0fY\x83\x94\xedAA\x14\xbd,\x85\xa2\x97)zy\xb7\x05\xd5\xb1\x8cR\xf4\xb2\x14cO\xa1(Z\x8a\xa2\x1dN\x01:\x8a\x16\x13D\xdb\x9cd\x85\x9a\xd5L\x91\xb2\xc6%\xaf9-\n\xa8Y[\xe7\xec\x8e5\x90\x15\x85\xe5,\xd4\x18\xe3\x1c\xad:\x1fh\xb3\x1chP\xa3\xf9Lx +\xd4\xa8L\xee\x85fgT\x1bw\xdc\x93\x82X\xdb\x00\xefMM\x1e\xd68\xecH\xfe\xefz\xbf\x82o\xc9l\xd9\xf1%q&q\xd5>\xf8d\x92]\xd7\xce\xc6>\xb63S\xfb\xe4bKl7c\xb5\xd4\xd6\xc5v\xfb\xec\xfe\xf7\xafx\x15%\xf1\x02\xa9\xd5v\xfb\x9b\xe6\xc39\x19\xb9 \x02 \x80 \x00\xea\xe9\x02'+\x04\xf0\xc3(\xa1Eid3p\x8c\x1a\xf5\x0c\xa6U\x92Li\x92\xc8\xb27^p\x05\xbdIy\x8e\x01*s\x9c\x16bQ\xad\xe5\x08\x9a\x13QH\xe2%<\x00\x01\x9e\x96\xa1\xb2&\xb2T\x97\xda\x9e\x01`\x10\xbe\x85\xca\x99\x00\xd9 \xe9+\x8d\xea$z\xc5Mh\x8a\xf3%z[\xd7*\xa1iQ\xe24\xf2l\xd3\xbav\xcdx\xc8\x99\xf5.9\xfc=%\x89\x1b\xacB\xe4\x91DU\x89'\x89kB\xc3\x93i\xbfs3\x13qt\x0e\x8d\xdc\xc3;\xc1\xf5Z\xec\xd4&S\xb1#j|diAc\xe9\xa4D2\x0d'\x90\xd5\x13X\x11P\xf3\xb0\xe9\xb0jg\xeb\x80\x83LD\xf3\xe6\xebtb\xb5\xc6\xc8\xd8\xb1\x02\xad\x03\xb4\xfa\x057wsv\xec\xf4\xbb\x8e\xf0\x00\x97\x18\xc4\x1d\xd6\xa6i\x84\x9d\x03\x94nP\xae\xa3\x1e1\xcb\xa1\x85*\x9a\xf5\xf6\xabG\xa8|8Vys8\x15\x8aM\x0eF>\xb6\x01\xf6\xe0S\xf3C(\x1ct\x83x\xd6E\x1c\x12\x83l\xe9\xd5JJ\x19\xca\xcbPl1\x90u\xc3y\x12\x8c'\x0eE\xb9\xafB\xfdH\x0bg8\xf5+\xc6\x0e\x03\x96\xd7\xd0\xb8a\xab\xc9\xe0\x8e\x1c\xf6J\xc7\x96T\\G\xecp\x83\xf0\xfaVQ\x1fUT\xd0\xb0\x07\xcf\xc2z\xe9\xaa\xdak\x0b&\xdd\x86\x06\xea_\xfe\xcdC\x03E[`\xeeyt^\x04\xc2p\xaf\xa1\xe8\xd3C\xc6\xfb\xe0\xc4\xfc[\x1dT\x85\xec\xcf\x98\xa8&\xf1\xb7\xcb\xeb\xa0\x1d\x1a\xb6Bo\xc9\xd2-\xdfA\xd2\x1dd\x14\xc0\x98\x87\x04Bb\xc2E\xad-Q\x0b]$\x1c\x9e\xe3\x1b\xa2\x82/\xdf\xa5\xe4\xb1\xbcf?\xf6\xd6\x8e\x9c\x90\x1b\xea\x0c\x95f\x8d\x07\xcb0e\xc2v \x83\xc9f\x89\xa0yV\x94\x88L\xa74\xa2$-\x93\xe5;t\x96&K\x94\xa5\xc4\xafm\xb3\xe9T\xb8\xb1\x19\x1d\xbeq\x8bYV%1/\"I\x1c\xb3\xab\xa0\x8d4?\x15M\xcb\x8f\x1fF\x98!I#\x9f\xa4\xb4\x9asO\xb7\xfc&|\xa38etq\x87%\xaf\x9e\xccX\xe1\x06G\x0bT\xa5\xf8\x1e\xd3\x84\x9d\xe0\x9d\x01\xe2H\xbf3\x91\xb0s\x9b\x9e\x1b6f\x8a*\xa6e\xd9@\x83&J\xce\x86oh\xffD%tN7n\x9e8R\xcav*\xb3\x12'F\x8e\x90\xd4\xa8L\x8aN\x1a\xbb\xcc\x03P*\x92*\x11\x9b\xc4?US\x94\x90i\x89\xc8|Q.\x11\x95u\xb5\xe5=\x98\xa8\x97'6\xb8@\x80\xcd\xc5\xc4\xb7J\x08\x8ef\x08/\x16\xee\x94\x86*-\xaf9\x95\xa1y\xf0)(\x04Td\xa8\xd7L\x18\xc8\xb1\xf9\xe0;%\xe31\x97\xa8\xe5\xd2\xc7\xa1\xacl\xc9\x7f\x06B\x8a\x10\xa9\x04}\x93\x81\x05\x06\xca\xd4j\xaf\x05~\x9f\x8e\xf4\x16d\x0c\xf0\x003\xb4\x18M\xd1\x8fS\xf7Ss\xa8Kz\xc66'3FDH:\x17\x0f\xb5<\xe1o\x16\xa0S\x9f\x07\xc1xgF\xcb\x16\xff\xcf=\xdb6'\xec\x08\x15|\xdc<\xb4\x10\xc6_0\x12\xb1\xf6b\xa1\xf5\xbe\xe5f{c\xef\xfa\xb8 \x92\xf5\xd9\xf8$\xe5\x95\x81\xb2<&\xf9\xbb_|\xac\xbb\xa4iD\x8eQ\x94\x15\xf3\xac\xd8-\xe2[\xb4\xff\xee\xc3{k\x87\x90\x7fU(nm\xcc \xbc\xc9|B\xe2Xh\xf6\x9b\x8b\xf3/\xdaT\x93\xce\xc5\xc2\xb3\x0e\xb5\xf4w\x11P/\xd2w\xe8k\xc7]\xe43\xf5\xc2\x93d\x9aq\xea\xe6\x95\xfbw\xcc\x8dQ\xbb#\xa5\xbb\xda\x02\xc8m\x94\xafp\xacQv\x91\xcf\x8c\xf7h\xa2\xa05\xd7\xe0\xcf\xff\xbaf\\[gR\xfd\x08C\x8dM\xfb\x02\x17\xf2&\xd3X\x15\xef\xbc\x86\x1c\xb7\xd1\x82\x06\xdai\xfdv\x83T:.\xfd_\xf2\xe2\xb6l\x07\xa5\x99\x08xs\x9e/\x11\xf2\xa8\x1403=*=\xb4u\xb4\xe4t)\xf0Zl;\xe95\x19m\xc8c\xd7\xdey\xc0\\\xea\xec Z\x16FU\xe0T\x9cgb\x941\xfe=\xd0\xd6\x95\xa4\x7f\xdf8\x83\xb1\x87?\xf1\xe8\x8e\x19\xdf\xa6J\x89\xb6M\x95\xda\xa0T\xa9m\xd6\xcc\xdf3k\xa6\xa6\xdf\xb0I\x9a'b\x9fJ\x1d\xe0\x0e\x91N\x0f\x13\xca\x10/G\xcb\x97a\x82s:/\x860\xb2\xb3\xc9\xac&\x88\x85\x89\x02?\xe02\x1a\xe2\xb4\xf0\xbb&\xfa:#\x82\xfcl\x82_3s[&\x89\x85\xbd\xdc}\x00\xe4\xee0W\x83\xe1Ph@\xb38\x17\x06\xb9\x13\xacN\x83g\xe6\xa2io\xd5\xfd}\xbc\xec\xeb,\xe8\xba\x04\x1a\xe0\xac\xee\x81\xfe\x0e\x81\xe6\xe9\xa6\x01\xac\xeb\x02\x18t\xe87\x8e\xf6\x0d`\xf6c~\xf7`?dbW\x12\xe2\xf2\x90\x0e\x9c\xd6!Gz\xb9G\x1apB\x87\xf8\xe0\xb1}\x9d|\xea\x9b\"\x9ad\x11.\xb3|\x8f\x1bN\xb2g\xe0%\xd5\xb3\xcb?D\xa7s\xdeG?\x96\x8a\x93\xc4r\xab\"\xedKT\x94\xec\x98'\x87CE5\xd1\xf0\xe6Y\\%\xda\x1ajd\x90\xb6\x86\x92?\xd9\xd8\xbcQ\x93\x89fk #~\xd4\x88l3\xb8&\xe5A\x93'u[\xc1-1\xc7\x8f\xd7UN\xaf\x13\x92\xde\x94\x8e\x00F\xffi\x065O4\x9d\xf3\xf4k\xbb\x7f\xfe\xdb_\xbb\xfa\xb1jm\xbe\xe1~\x82\xb6\xc0\xd8z\x07D\xdbz\x076\xc8;\xf0\xf7<\x1b\xf7~\x97]\xd9\x0bE\x94-\xc8\xde\xbf\xf8\xff]\xd3\xf8\xff`\x8f\xb0kIP\xfccy\xc9\xba6\x8c\x873\xbe\xd6.\x99\xa1 \x7f\x86HZ\xe6T%Q\x89\xffMj3\xbe\xa07)\xc9\xdf\x14HF\xf7\xab\x13\x8d\x0c\x93\"1\xe2\xf8\xf9M\x0b\x85\x8a\xfc\xd1\xc6\x1a\x17\x92\xf3\x1b\xb2\xbdy\xae\xd9\n\xbbSz\xddU^\xb3\n\x06g\xb3G\xd2x\x91\xd1\x94o&6L\xec\xba\x97\x96\x1caV\xcd\xea\x98$z\xc5\xc9\xd1\xab\x9cZ{\x904\xca\x97|\xee\xdd\x97<}\x06\xe6\x9c,\x0c\xb0\xdc\xf1!\x19b\xe9\n1%~W\x8f\xc6\xa6H\xed$\xd3\x0c\xaf\xd3cvdrV A\x06\x0b,\xdf\x9a\x89m\x12\xc3_;\xc5K\xb1\xe6\xa1\xfd~\xa3\xca)\x12Q\x94o\x1a\x95OM\x0c_{h\xa3\x92\x8b>\x9b\xd2\xb98\xb6v)\xcc.U\x92{\x04\xcbT\xeb\xa3\xadm*\xda\xd66\xdd \xdbTI\x13\x03\xdah\xb5\xe4\xb6vo/\xbb\xb7\xca\xe9\xde\xbf\xaa\x9c\x0e0x\x7f\\\x9c\xf63w\xe5\x98\x1aX\x95\xd3\xa0)\xfb\xe3\xe2T\xfedk\xc8\xf2\x16\x92\x05[Cvk\xc8n\x0dY\xe7\x06q.\xc9\xad\x0d\xbbMiq\xc9\x8cmJK\xff`\x0f\x0f\xb8mJ\xcb6\xa5\xa5\xdb^f\x9e\x86\xc5\x99x\x00nSZ\x1a\x0d>\x13}\xa3T<\xa0\xb6)-\xdb\x94\x96mJ\xcb6\xa5e\x9b\xd2\xb2MiyE)--\xbf\xd78\xd7\x00?.N\xb7\x97\x00\xb2m/\x016\xe8\x12\xa0\xe9\xc6\xdb\xfa\xffW\xf2\xffosB\x86\x06\xdcosB\xd6\xc8\xdcp6\xc36'd\x0c.nsB\xa0\xa7f\xb4\xcd \xf9\xff6'$x\xd9\xfd/~Y\xe6\xbb\xea\xd6\x96s}\xb3\x9d\xda.\xb6'Kn\xf8sxo\x8av\xd5Q\xfbm\xf6/\x8a\x0b\x9b}\x8f\xed\xb2f\x07\x1d\xb4=\xb7\xd1A\x8br\xf5\x9b\xe8\xe0=4\x14\x07\xe0\x1d4\xe4\x06\x1a:d\xbf\xdb\xe7\xb0Cf\x85\x9bgk)\xe9\xd1\xee\x9d\xff\x0e\xb7\xce\xc3\xb7\xc1\xdf\xfe\xde\x19\xe82\x019K\xda\xee\x11tq\xfe\xa5\x05p\xeb&\xd9\xbaI6\xcaM\xc2E\x87\x01j\xeb(Y\x8b\xed\x180\x1d\x03\xaf\xcd\x9e]\x9e$\x89\xf2\xb9\x02\x83\"\x1d\xc6\xa2\x01\xe8\x17E\xfaf\x1b\x8c\x1b\xb2\xb1\xb7\x81\x8f\xdb\xc0\xc7A\x06\xe8\xdf\xc3\x04\xfd\xdb\x1b\x92\xa2m\x03\x18;\x0d \x18\x00W\xde\x08\xcc<4\xec\xc2\xc1\x03m\x1b\xc0\x08\xb9EG\xbdfh\xc8U\x86\x07\xdc6\x80Q\xb4\xd1\xe7i\xd8\xa5\x88\x07\xe06\x80\xb1\xd1\xe03\xd1\xf7J\xc5\x03j\x1b\xc0\xb8\x0d`\xdc\x060n\x03\x18\xb7\x01\x8c\xdb\x00\xc6W\x15\xc0hx\xaf@\x1e\xf9\x16\x00\xe5\x9f7\xbdi\xdb\xe0E\xd1\xb6^\xf9\x0d\xf2\xca\xff=\x9d\xe7\xdb(\xc3\xa1!\\\xdb(\xc3527\x1c\x1f\xb7\x8d2\x1c\x83\x8b\xdb(C\xe8\xd1\x16m\xa3\x0c\xff~Q\x86\xe2\xdalF\x17{\xff\x927Q\xd0\xa2:\xaa\xa3\xbe9f\x0b\x99\x17Ij<\xd5\xcbwIB\x0b\xb1Un\xe8=\xd1w^\x08\xd7GOB\xb9\x19\x81\x11C\x8c\x89h!M8z\xf6Kg5\xbe\xfc\xe3\xc6^8\x8b\xbaQUE\xe3q\xcdV\x87e\xd8\xa2\x91s>\x9b\xea\x89)\xd0[\x86J\xf7\xba\xb13;\xaf\xfd\xeeP\x921\xe8|\xbc\xbdw\xe4m{\xef\xd8i\x80S\x19\xc0S\x85\xc0\xccC\xc3\x8e \x1eh\xdb{G\x88\xf3\x0b\xf5\x9a\xa1!\x87\x1b\x0f\xb8\xed\xbd\xa3h\xa3\xcf\xd3\xb0c\x92\x07\xe0\xf6\xde\xb1\xd1\xe03\xd1\xf7\x90\xe5\x01\xb5\xbdw\xdc\xde;n\xef\x1d\xb7\xf7\x8e\xdb{\xc7\xed\xbd\xe3k\xbawT\x0e\x0c\xd0\x9d\xa3\xbae\xd4^\x97m\x16\xd0\xf6\xbe\xd1\xf2\xf7M\xbao\xec\x06\xdfo\xf3\x80V\xf2\xeen\xaf2\x87\xde\x13m\xaf2\xd7\xc8\xdc\xf0%\xdc\xf6*s\x0c.n\xaf2\xa1\xa7\xe5\xedU\xe6\xff\xbfW\x99\xae\x9b\xcc\xd0\xe3\xb9\xf2\xddK\x86.\xb5>\xf9\xfa\xa8\xc1\xf9^\xc3\xfdo\xf9\x9b\xedc\xb8\xa2yl\xda\xd7vc\xf8\xb7\xbf\xf5\xf3c\xc57`\x8fgh\xe5\x91\xb5~o\xb6\x05o{^\xdd\x9eW7\xea\xbc\xfa\xf7\x98\xec\x1f\x1d\x1c\x92}\xc4\x1f\x1d\x130\x9at4\x86=\xb8{rRq\xf7\x98\xdc>\x90\xe4\x1d\xba\xa4s\x9a\xe0\xa3\xcb\xacz\x93\x1b\xe7\x0e&NsR\x94\xdc\xac5\xad\xd6\x7fc\xa7;~F\xc0\xe6\xc2a\xa0\xebu\xc3\x0d\nv\xaeN\xf4\x00\xd2Mn\xac\x15a5\xbf\xb3\x83Vo\x016\xe1\xe2$Q\x0b[X\xdbl\x18>\x93}\xc1\xd7\x13\xf4\x96M\x86\x1cm\xaf\xfe\xfe\xabu\xb4\xf6\xfch\xef\xbfk<\xac\x19\xaf\xa8P\xb5'4\x858\x8d\xf7\xea\x05qM\x9b\xfckm.\xdb\x80\xfa''h\x82c\xd3\xdc\xab\x8f&\xd3c\x13\xb1\xab\xe6\x1a\xe5\x02\x95/L\x9c\xc6(\xcd\xac\xdc\x7f\xd7\x02 o3\xca\x87\x8c-g~\xa3\xc1\xdd\x07\x86,1Ib\x90\x15X\xfe_\xe6\x86Y\xa28K\xdf\x94\x92\xe3S!\xcd\xf9Fb\xcc\x93\xaf[\x1b\xc3\x9ft\xb8\xba\x83&U\x89\xd2\xac\xb4p\xd6\x9cn\x1b\x18\x86\x9a\xb9\x94\x19Yz\xb6\xd8\x1f\xf5\x9e\x893R0<\xe7\xb8\x8cf\x8d\xc3\x9e\xd1\xdf\x9c\x91\x7f,\x95\xd1\xb5\x83\xea\xe8M\xce\x0b\x81R!\xaf\x84J\xa5\xbcL\x0c/\x89\xfe|\xad{2\x8a\xf4G\x05D\x1dP\x1bJP\x0e\xa7\xc15\x17\x9a\xbd\xd2\x90\xd4%\xbf(\xaa6\xf4\xd4\xc5 \x0b\x1c{\xacF\xbf\x8c\xd2\x14r\xf0!\xc7\x8b\x85z>]l\xbaY\x96\xc4\x85>\x01\x89\xbd\xff\xd6z\xc9%\xb7\x19\x89\x7f\x1d\xf5\x88\xe4\xa0\x0c\x85\xa0\xa2 d\x84B\x0f6#\x98\xd1\x07\xb8\xb0e-t\xcb\xc8\xda\x8f\x94\xdeU\x04\x9d\xfe.\x8f4\xb4\x90\xbb\x1d\xa1\xd3\xf9\"!s^h\xb2\x88o\xdf\x9d8\xeb\x0c\xa9\xc6U\xd6\x14G\xe2xT\x15\x84\x99\x86\xb9V\xf2Dl5e\xd4\xf9\xa3\x8e\xbep\x87\x8c\x8b\x89\xe2\xdd\xfd\x88o\x9a\x0dbf-`\x1a\x18J\x97y\x96\x96\x98\x8a r\x0f\x8cFOaIr\x7f\x0dQs\x81\x93${\x90Z\x8c+E\xe7y\x18\xa9\xf5\xe6\xf8\xbb\x08\x8c\x0f1\xcf~\xa0R\xcdy\xb0R-\xb8gD\x0b\xef\x1c\xd1\xbc!\xcfu\x03\xcd\xbb\xfc\xa9\xac/&\x03\xf6\xa5U\xa6\xca\x8d\xbdU\xf5\xaf~\xf5\x82\xc9\xb3\xc4\x13P$\x1ad\x05\x89\x869\xbczG*d\xf4\x94\x13\xbe\x9a\xc8c\xe9\x0fw\x14\x8d;\xbb\xf2,\x12[\xb0*\xbcAC\xa8\x1f\xf3HZ\xcdCt\xb3\x13\xd7\xf9\xc9\xc5\xd5\xff\\_\xfd\xcf\xf9\xd7\xeb\x1f\xdf/\xcf\xbf~9\xfdv\xfa\xf5\xf7~\x1d\xcf.N\xff\xe3\xf4\xfb\xc9\xd5\xd9E\xbf~\x97_/\xfe<\xfd\xf2\xb5g\xaf\xd3\xef\x7f~\xbd\xec=\xd6\x97\x1f\x97Wg\xbf\x9f\x9e|\xef\xd7\xed\xec\xaf\xef}\xf1;\xf9\xf6\xed\xf4\x8f\xd3\x93\xab\xaf\xfd\xba\x9d\xfd\xf7\xf7\xd3\x7f\xfc\xb8\xec\xd7\xe9\xfc\xe2\xec\xcf\xaf\xdfO\xbe\x7f\xe99\xd8\x97\xb3\xefW\x17g\x7f\xfc\xd1\x97\xb6?O\xfe8\xfd\x1d0\xd1\xca\x9d6ly\x85m\x95fs\xad\xe2\x81\xc3\xf3K\x19v6\xe5^\xbe(Kc\xcaP \xf6rm\x8ac\xfbg9\n.\xc4e*\xe5\xf71\xdePO\xdb0j\x0f\x1d\xdb>\xd6\x01q1\x99\x94\xa8 \xf9=\x8dhz\x83\xa6U\x1aq5\xd6s4\xb5\xf7\x8em\x1f\xc5a\x89;\xcbi\x84hzO\x8a\xfe\xf4\xe8}zl\xfd\xaa\xa6&-i\xb9\x14\xea[\xd3\x18UE\x99\xc5\x14\xa7\x92P\xf5\x8c:cp_B\xf9\xbe?\xee|\xd1\xb7\x96\x85\x90\xff\x0b\x9c\x97K\x89\x13W\xdaJK1\xed\xdbsH-7\x8e\xad_\x05w\xc5\x80\xe2\x9c\x9c\"<\x9d\xd2\x84\xe2\x92 |\x93\x13n\x86\xf4\x1cTJ\x9dc\xcb71 \xb7zp\"\x9c\xeb\xd9T\"\xa0\xed\xa6i\x1bR\xedzi\xc2\xa0\xb7\x94 \x92f%\xf1\x1b3]\x84\xb4@>\xb6~\xb5\xa1s\x8f\x13\x1a\xf3%,S\xee\xf8\xfe\xd0\xb8x\x11P\xb9S!\xcb\"|QU7\xc8\x95U\xdd\xa4\xad\xa6]\xdbz\xfa\xdf\x14\xa8\xa07).\xab\x9c\x9fb\x15\xaa\x1ex0\xbb\xef\x04\x9d\x1b\xbb\\Y\xa3l\x0b\xee\xd1\x94 <\xceEf\x17z\xc1tK\xac\xaa\xa5\xe8\xec\x06\xd7\x83W\xfcT\xce\xf8@IQo\x04Tf\x0b\x94\x90{\x92\xa8\x12\xb5B \xf9\x99\x93\x88\xd0{\xdfB\xc8\xa6\xd3]!\x9c\xf8\xbctSv\xb5C\xc2\x01\x84_\xa0]\xf3uw\x1d<\x1c\x8e\xcd\x87\xd4(Gm\xaa*6\xfb\"\xc0\xaaE\x90wm\x1a\xbe\x97\xcb\x12\xa71\xce\xe3F\x19e\xa1\xef\n\xee\xbd\x9c\xe3\xfc\xd6r\xd9Z7\xf5ko\x86PNPQ-\x16Y\xce\xf0\xd3\xc7M\x8e9\xdf\x11L\x18\x95eN'UI\xd0\x1c/\xb9\xf7\xde\x03pB\x98tOox%n\xce\x05\xa9\x13\x94\"e{#bv\x8b%\"\xc9l\xd2It\xcde\xeeu\x9e%I\xb5\x08MkH\xb2C5\x00|\xfe\xff\x92\xea\x01'\x89\x16\x01jG\xd4\x8efZ\x16Z\x14x\x80\xa9\xab>%T\x1b\x80\xde\x14J\xb4N)Ibo\xf2\x95\x98\xb6\xa4\xc8\x10I\xf1$\x11g~\x1eQ)u\xd5\xbfsO\xb5@P@\xf5\xad\x91\xdaU\xaeh\xb4O\x1a\x8cm\x97|T\x15\x87\x83Q\x9ee\xa5\xb8q \xa9t\x1c2\x95\x95$\x84\x1bN\xf5\x8d\x86\x0bGU\xf7]\x84\x8d\xca{\x10\x0f\xa2\xca\x01{M\xd3if_R0J\x1a\x80j\xef\x9e\x99\x04\x84'Y%\x82\x0ei\xbcW;\x98\x1c\x00ked\xfdA\xd0\x91\x16v\xa1=\xa7\xf3\xb9\xc1D\xf3:\x8b\xdfy\xe2\x07\xde[\xbb\xdb\x1a\xb7\xc1.\xa1\xa09\xbe\xc8\xc9\x94v\xf2\xbb\xea\xb6f*\xe4\xf8\x8a\x18\xf9_L\x8a\xea\xcc/\xa2\x7f\x1c$F\xfc\xa3\xaa^`Z\xbc\xc2\xcd\x82\xa0\xa2\xd8\xf8\xd2\xa4\x1a\x00\xce\xcf\x0f\xb6\x02VdC_\n\xd5\xe59?\xcc\x89 \x0b1\x02\xba'y1\"m#Lq_\xda\xccI\xe3\xff\x9e\x91GE\x1d\x98*c\x01\xb8\x05*;r\x8e'UMh\x01\xd1\xea\x80\xd3\x10\xb8\xae\x8b\x995\x0bZI\xc5\x8as\xbe\x96m-Q\xf3\xcbc\x1f\x94\xc6\xfd\x16Hn\xab9}!\xe1\x0dg\nD\xbcCa\xc1Xb\xfc\xd7\x08Bb\xad\xbci\xa1\xda\xd4\x08\xfa\xb3\x07\x9cUM\xc2\xb9\xf5\x02\nB\x8f;\x8a\x96\x80\x93:\xc2R\x18D\xea\x08J\xc3\xb1Z\xdaD\xabSQ\x97J\x9f\xd7c]\xb1\xbc\x12\x1b\x17\xcb\x83j!<\x02\xaaG\xf1\xea\x05\xe0\xec\x02\xb7:aC\xcf\xd7\xc2\x93\x10_3\"B\x82\x13\xb8\x80\x90\xb1\x88\x18\xd8]\xc6\x96\x10h\xb1\xc7d\x1c\x13\xd9c]D\xc9\x17\xbe\x81HZ\xe6K^\xcaO\xe2\x0b\xa2i\xe2)\x06.Z\x0f\x8a`R\x00\xc9\x13\xb7\xc3\xaf L\x14\xb9z\x82\x02\x0e\xe9\x802\xef\xcf\xe4\n|5S\x98\xe0\xa2TH\x83\x08\xdb\xd0y\xe4ayt\xc4\x89\x94\"\x03F\xac\xab,E\xb3\x19E*\x81\x01eVpQ\x8c\xe6\x90\x01\xa6W\x00\x97\"\x02O\x03B\x9d\x1b\xf6N0$\x9f\x0e\xb5e\x83\xd0\xe4\xc4z\x7f7\xc3\xc5l|R\x19\x1d\x0c2'\"5j\xbf\xa8\xb5\x16L\xbb\x08 E4\x08/\xc0\x14\xc82\xe2\x96[l\x8d\xfc[6\xf05\xfb\x99?\x8a\x895\x1d\xa8\xc6+\xd17g3\xca\xe6\xf3,\xe5\xe3\xf9\xa3I\xc5\xeb\"/\xc2\x0e1\xb4\x08TSd\xd4\xb7\xa6\xfc\x81\xb1\xe0\xe5\xb2j\xba\xfc5z+\xc0\xfeZ_\xd6r>\xedi\xd6xomUc\xa2\x97\xa6\xf7\xd9\xadg-\xd1tQ\x95\xaf6\xd5\x0b\xb2\x0bzM<\xc4\xe8k\xb7\xefl\xda\xe5C\xb1\xb2r:\x9b\xf8\x84\xa6\xb7h\x82\xa3[YA\x1e\x00\x89G+\xf0\xa4\x0d\xbeh\xfc\xbe.]\xe9=\xec\xec\xea\xc9\x02\xa0y \x1a|\xabX\xc4\xb7T\xf4u\xf4\xf87P\xca\xc5\xd7\xa2\xc4\x93\x84\x163\x12\xab\xb0\x86P\xb89D\x8e\xf7dS_\xca\xed\x02\x9f\x92\x88\xa7$\x18V\x05\x00\x1ec\x14:\xcf\xb3EV\xc0y\xa0\xc5\xf2z\x181\xcd\xb39\xdf\xbb\x1c\xa7)\xf7\xed\x95y\x15\xf1\xb8\"n#\xcdq^\xcc\x02\xe1\xeb\x08\x15%.\xab\xe0\xd6\xef\xc7\xffS\x9d\xf5B\xa7\xc2l\xe2\x82\x8f\x8bH\xb5\x0c\x15\xd2\xa0 \xd0 \x19?+\x1eA$\xa2\xe5\xd9\x14\xf3\xfb\xb2E\x15\x16\xcf\xbdx\x0c\xcf\x9a\xbc\xf8\xfa\xe5\xec\xe2\xf7\xeb\xd3\xef\xe7?\xae\xae/\xafN\xae~\\\xf6\xcads\xc18\xbf8;?\xbb\\\x01\x80\xf8\x16\xec\xae3\xf1V%\xa4\xbf$\x0f2\x0f2\x03\x01\x10F\x02\x16 5\x84\xc7}\xe1\x84\xc6{U*\xce\x8bb\xdd\xb2\xb5\x03\xe8\x1c\x98J;\x8f\xd5_\xdb\xa9b\xc6\x8eI\x11\xce'\xb4\xccq\xbe\xac%\x18\xaf[\xa8\xcf|b+\x0c\xc7Q|\xb3c(\xbe\xd9\xf1\xa3bC7,\xb0EN\xeeiV\x15\xfc9\xd1\xe6V7\xd2\xab\xbc\xb8Jas\x95\xe3\xe8V\xf8\xc6\x84\xe5\xa4O\x80D\xe9$\xe8\xa9\x0c$\xbd\xccA:v6\xa3-\x9aQr/\x9eX\xc9\xaa2$v\xb2\x14\x84\x9d\x80\xf4j\xad\xc2\x17\xd6\xf5\xff)\xf5|\xa9j\xac\nv\xc2\n6\x88&z\xec\xd5\x0fa\xb6\xdcP/\xa0A/9LE\x97Z\x82\xb5\x1b\xa3>\x10\x02\x80\xd5\x99N\x927r+\xd3\xf4F=\xdb\xb33\xc54\xa9r\xc0\x11\x1215\xbc\x10o\xdb\x06\x7f\xdck\xd6\xfb(\xdd\xcb\x1f\x7f\x0c\xd2R\xdd\xde\xe7'\x97\xb0\xe4\xfaf\xb7\xcb\xff:=\x1f\xd0\xed\xdb\xc9\xe9\x1f\xc1n\x86N\x1eJ\xe70m\xec\x18\x0d2)\xce\xce\x0d\x0d\x8c\xaa\xb4\xf9\x18\xbc\xab\xf5\xcc\xa7\xefNi\x9by\xec[C\x85\x11c71\x0dZ\xbf_5dH\xb6\x1c\xdaC\xb2o\xc6\x90\x9a\xa4\xbd(K\x0b\x1a+\xef\x03\x1f\xfc\x96\xf2\xfb\x8bX<\xe55\xa7\x05\x7f\x9c^\xea\xa3,G1I\xf0\x92\xc4\xc0\x14E\x07\x92l\xf1\xb5\x91d\xdf\x9c|\xa9\xaf\x16\xad\x983\x99\xe1\nEW\x0d*\xf4\xc4\xa1\xeaL\xc8'\x92FxQT\x89FI\xca-~\x8a\x85\xf8\x11Qmy\x00\xb2\x93a8\n\x1c\xde\x16\xbf\xa2\xfai^\xfe\"x6U,Ko\x1a\xe2:0K\xec\xa7\xe2 \xa3:\xd4\xe5\x8a)\xcctx\x1d5\x96\xdaX*\x06\xd6w\x19\x00=[X\xcb1YK.\xc9\xcb#\x07\xb8\xf0l\x9f\xe8+\x91l\xca-W>\xe5\xb8,q4\x13\xa3\xe9\x8cV\xb67 \x8e\xdc\x05\xb6\x9b;F\xaec~v\x0f&n7X,1\x92\x9c\xcbR\xe9\xd0\x90\x9fe\xb6\x93\x9dd\xed\xb8\x1a%P\xa9 -\x10\xa1dD\"9\x016\x96\xc0\x9a#\x92@>\xbc5n\x14=\xbe3\xc8HNh0p\xa8\x9e\x86M\x8e\x18jc9\xc9\x10R.\x8f\x99\na\x019^\"\x84\xb4\xaf\x9f3\x0d\xa2I\xcf\x08\x96\x05Pt@\xf9\x8d:8\x06N'\xfe}\xd0/\x01\xa25\xd9a\x95\xb4)\x1e=\x1e%\xc9\xfd\xd1c\xf4\xf9\xa9,\x1e>\x1c\xc6w\x87?\xe3\xfby\x8c\x9f\xaa\x87\xa7\x08\xc7\xf1l\xf6\xe9f^\xbd\x9fGO\xe4\xbd\x0d\xb2\xdf\xdb;\x12\xf5\xda\x8f\x1b\xe1\x14\x11\xca+\x90O\x08\xc2|\xeby\x89\xfe|\xf0\xf9\xb7O\x13|\xb8{4}\x7f\xb4\xfb\xe1\xe83\xde\xfd\xf4\x11\xff\xb6;%\x11>\x98\xec\x1f\x1d\x1c\x92}Q\xb2^\xc9\xb5\xc8\xfd\x96^\x93\xcb>\\\x0f\xee\x9e\x9c\\\xbe{Ln\x1fHbef \x81r,v\xd6\x15\xf6z3\xf4\xe8\xd3\xfe\xfb\xe9\xa7I\xb4\xfbq\xff\xe3o\xbb\x1f\xc8\xe4h\xf7\xf3\xd1\xc1t\xf7\xf0\xe0\xf0\xe0\xe3o\x07\xd1!\x89Z\x0c\x15\x83\xad\xc4R\x01\xe2\xe0\xee\xd1\xc9\xd4\xcf\xc5]\x12\xcd\xde\x17\x8f\x0f\xe9\x87\x0f?\x8f\xf6\x7f>\xdd\x94\x9f\xf2bv\x7f\xb7\x9c\xe6?\xa3\xdcE\x0e\x7f~\x991!K\x93e\xcd\x02Dy\xe2\x9eq}\x80\x93\"s\xe1'_\xc7\xb0\x8ak\xf7Y98\x992\xe4\xc2<\xbd\x8a\xec\xad\x86O\xae\xccP\x92e\xb7L:[\xa0\xc8d\x1f\xe1\x90\xf4\xe1\xe1{\x07@\xdbk\x9e\xdf\xc0V^\x03\x1d!\xc8\xa6 \xbe\xe1\xaaE?-\x90\xa9\x9fq2\xdd\xaaT\x00\x91\xdaMF\x11\x15\xa4\xd6@\xea8W,\xb2\xb4\xb0Fyhtd\x02\xfc\x061\xc8L\xda\x0f\xf1\xc8\xbfm\x8a\x95XD\x1e\x05B\xde#\xfdsr\xa8\x85\x90\x9bA\xf2\x87\x9cB\xe7\xc5)\x07\xc1\x03\x8bB\x8cP3\xe3\xb4P\xd033\xa2\x85\x10d\xa58\xf3\x8d5\x8c\x14-\xb3*\x97\xa6R\x93\x0d~\xacdz\xd7\x85\xe4 \xaa\xa3\xfd\xc4\x7f3\xce\xc8\x08q\x82\xfeY\x91|\xb9\xa7\x8a\xfd^\x9c\x7fi\x01\x13\x99\xa5\xf5\xf0*\xb4\xd7\xf8Y\x03\x9b\x93\x14U)y\\\x90\x88Y{\xe2m)5\xb2\xd1\xa7\x88fd\x8e\x9b3\xe7\xb4\xfd\xdcv\x1f\x87\xdf\x9d\x7f\x8f<\x8f\xb2\xd8\"|E\x07W\xc9s\xb5PhZ\xbe?\xec\xf0\xc7Q.\xdc\x83CLJL\x93\x0d\xa9\xc9\xc1\xba_W\xb9\xf3U\x9b\xa0n\xe4\xa9\xac\x83{[]\x10\x0b\x9c\xe39)In\xe0\xbc+\xf4\xb7i~\xbb\xd6`kG\x0c6\xd8\xc75\xd3iz\x8c\x16\xb84#-\xd9^\xa79\x89\x8fQ\x99W\xe6\xf6\xb0\xb2Mq@\xd9B@\xf2\xfbZ\xec\xc3\xect\x9f\xf98\xc4\x0eg\xdc\xbac\xb2\xc9\xca\xae)N\n8\xbf\xb4\x8d\x0d\xe5Xo\xa3|\xa8)\xae\xb9\xd6\x80f\xe1\xe0\xd8f\xf7\n\xc6\xb6\xcd\xc4\x1eq\xb6Z\xd7A\x8dy\x82\xdb\xdfkB\xaea4\x03W\xd30C\xdbr\xfa\x1db;\x0e\xa1\xbdk\x0f\xb9,\xa5\x0e[\xa4\x99\xdb\x973},ld\x19d\x93y\xd32\x92\x81\xac\x19hZ\xd7Ft\x03\xda\xcbr\xa0e\x1d\x0390\xc8\xa66\xac\xe7\x060\x87%\xbdN\xfaK|\xd3\xb0^\xfe)\xc7y\xb3\xc7\x04(Iq\x1a\x91\xbd9)q\x8cK\xbcw\x7f\xb0'V\xec\xde\xbf\x0cs\xe5\xff\xf6\xb8\x14x#\xe0\xdc\x10mv\x17\xd5|\x8e\xf3\xe5\xb1|\x94\xa3 8\x8ff\xea\x01P!:\x14\x9d.\x1e_\x19\xa1\xac;L\xee+\xb9\xbe#\x8a\x95\xd5ZO\xfc\xca\xa2\xf9\xe0\x96B\xd3D\xa8\xf5 \x03\x077\x0f\x04\xce\x86\xe6dxkX-\xfc\xb9Nsi\xdb\x06\x02\xfa?\xc6\xd0\xb2\x0cG\x0d\xd0\x98J\x1b\x8b\x19\x8a5*\x10\xabt\x0c\x8b\xb4\x1e\xf1\xdf\xd0\xe9T\x18\x00M\x9d_\xa3)\xdeKT\x8a+'e\x95\xa7\xfcM\x19+\x88\xd63\x00&\x18bs\x111\xd8-\x97dx\x94\x965\x0f\x19D\xb2\xd48r\xbb\xc6\xe0/\xdf\x943n\x17q\xebB\xb1e\xa7A\xdd\x8e|Q\xaf\xc6\x04\x1bo\x14\xd5(\xb1_\xf17\x8c\xe2,}S\x8a\x00\x07Y\xde\x07\x15\xcc~\xe1\xe0\x8d\xe5\x88\xd1\x04\xc7\xe6\x9d\x8a\x81\xa8\xfe\xd19\x87\xcf\xa8\xc3\x8d\x95-jI\xe9\xd5\x83\xe2\x8c\x14(\xcdJ\x94\xd09-\x1b\xba\x93a\xd6ZT\xf5X\x9d\x8a\x92\x86\xcc\xc8\nb\x9c\xce\xf8\x83A|^T\x16\xdb\x94\n\x92\x95\x0c\xaa\xfb\xfec\xa9|\x07;\x0d4\xf4\xf0\xe2\xf1\xa1R\x17E2\xc6\xbd$e\xd7X\xc0i\xbc\x97\xe5\x86\x9e\x10`\x18{s\x91\x85\xaa4C\xc7b\x90]e\x17\xb5\xbdt=\xad\xd3X\n\xd6\xf7\xbf(\x0e \xddi\x08\xf47\x87\xfb\xfbo\xdc^\x10#\x1b\xd7\xaayG\xf2\x7f8|\xc9\x0d\\\xf4\xf6e\xd3\xa2\xeb\x8c\xf2\x8f6\xdf\x12\n\xb9\x14\xfc\x0e\x85\xa0w\xdb\x13\xa3\x13rU\x84\xae\xb8\x10\xcc\xe1\x00\x8cz\x08\xa7Y\"\xf4C\x94\xec<\xfd\x1d\xe9\x8a\x0bb\xe1#t:_$\xfc\x81\xba\x02\x15\xf1\xed\xbb\x93@\x12!\xe2\xde\xa7|\x8a#\xe1\x9b\xe3\xb5,\xc5\xa3\\\xa2'\x11;D\x99'm\x0b\xa7\xd9\xbed\xc5\x0e\xbb\xac\xed\xa6\xd4\x1b\xb5\xa9\xaa\xd8\xec\x8b\x02\xe9-\x82\xbck\xd3\xf0\xbd\\\x968\x8dq\x1e\x9b\xeaE\xea;\xe1s\x9c\xe3\xfc\xd6\x12\xe8S7\xf5k\xdfv`\x13VT\x8bE\x967Jnr\xcc\xdf\xc9+v\\\x969\x9dT%As\xbc\xe4\xaeR\x0f\xc0 a\xd2=\xbd!1\x9a,9\x17\xa4N\xa8\xcb\xaaei\xc4\xec\x16&\xe7\nb\xf1\xdc\x89&\x9dD\xd7\\\xe6^\xe7Y\x92T\x8b\xd0\xb4\x86$;T\x03\xc0\xe7\xff/\xa9\x1ep\x92h\x11\xa0v\x84\xbc0JcD\xcb\xc2~\xe5\xdbl\xeaBE \xd5\x06\xa07\x85\x12\xadSJ\x92\xd8\xf3\x0e\xbf\x9a\xb6\xa4\xc8\x10I\xf1$\x11g~&\x92\x95\xae\xfaw\xee^\x16\x08\n\xa8\xbe5\x92\xea7\x9d\x14\x8d\xf6I\x83\xb1M\\\xf7\xf1\xba\xff\x84\x17.\xce\xb2\xd2\xa8^,\xae\xff\xa2,I\x087\x9c\x18\xdaRW8\x002\x02\xf8j\xe3\xf7\x1d\xb9\xb8\x00\xf1 \xaa\x1c\xb0\x9e\x08T\x18%\x0d@\xb5w/T\x84\xcd\xed\xb11.\x1d\xac\x7f\x0e9\xd2\xc2.\xb4\xe7t>w=\xf8+\xd7\xce\xd1\x1c\x0f%d\xae\x99\x8aPNeM\x91'+M\x03\x13\xff\xf0'\xa4\xad\x89 \xafp\xb3 \x88|%lB~m\x10?\xdc\xb9O\x08\xca\x86\xbe\x14\xe2P>\xe2X\xb4\x8d0\xc5}i[)\xb3\xd0\xb2\x00\xdc\x025\x9c\xaa\x0f\xc3\xbd\x0b- Z\x1dp\xbaeg,\x173k\x16\xb4\xc1\xd4{\xd0\x9c\xafe[\x07\xf2\xeb\xbd\xd7F\xa8}K\x05\x93\xdb\xd0l\xfa\x17g\nD\xbcCa\xc1Xb\xfc\xd7\x08Bb\xad\xbci\xa1\xda\xd4\x08\xfa\xb3\x07\x9cUM\xc2\xb9\xf5\x02\n\x02\x96\xb5>\xfe\xc2\x18a)\x0c\"u\x04\xa5\xe1X-m\xa2\xdd\xa9r>\xaf\xc7\xba\xf2H$6.\x96\x07\xd5Bx\x04\x84\xc2i\xb9\xa2\x81f\x17\xb8\xd5\xe1\xd1\x12\xe3\x8e\xdbg\xf9\xe9\xb7c\xeb\x82\xe4\x91\xbd\x84\x0b\xe8Q\x11[Ir\xc9x\xd7\xc6C\xea\xd0\x18f\xcff>\x02\x13t~\xa9\x06\x9cc\xf9\xe31\xa2#`\xf1\x11PO\xb9h\xe3\xc6H\x0c\x88\x92\xe8\xc9HX\xa4\xc4\n\xb1\x12\xc3\xa3%\x86\xc6K\x0c\x8d\x98\x18\x1c31(jbp\xdc\xc4\xc0\xc8\x89\xe1\xb1\x13\xc3\xa3'\x06\xc6O\xac\x1aA\xd1O\xc2\x8b\xe6\x8c\xa2\x00\xf4\xb5\xf7\x1c\x1aG\xf1l\x91\x14\xcf\x1dK\xf1\x1c\xd1\x14\x1b\x13O\xf1\"\x11\x15/\x14S\xb1QQ\x15\xaf#\xaeb\x03#+^6\xb6\x02\x1e]\x01\xbdWS\x0dz\xbf\xa6\xda\x981\x16p\xdbq\xa48\x8bA\x91\x16P,\xf5]\xa2t\xae\x10\xfe\xee\x96(\x9f\xc6\xff 2\x8f\xe8M\xca\xeb\x91\x05^\xfe\x0f\xa25R ^\x18m<\xeb\xb9\xccj\xf6\x99\x17\xfd\xf2\x9e\x11\xf6\xc4\x9a4\xefw\x18~4\xc2I\xb2\xac\x1f\xd7wv\x94\x9dF w\x0dg`u`\x11\x99\xa2\x94$\xe2R\x9eidf\x02D %i}\x1e\xe6\x11\x13^x\xdd%\x1a<\x04\xe3*\xa6^\xee\xf4\xa1\xe7\x0b?~\xc7h\xb2\xdcA\xd5\"\xd6\xff.\xe9\x9c\x14%\x9e/\x8a\x1d\xed>\x13\xc5\xfd\xfc\x8f_\x8ad\xabD>\xcf6\xcd\xdcd\x80O\xd8\xd0\xf3\xb5\xf0$\xc4\xd7\x8c\x88\x90\xe0\x04. d,\"\x06v\x97\xb1%\x04Z\xec1\x19\xc7D\xf6X\x17&Ae\xac\x00I\xcb|\xc9\x1fe\x93\xf8\x82h\x9a,G\xa4\x08&\x05\x10\xea<\x8eo\xfa\x15\x84\x89\"WOP\xc0!\x1dP\xe6\xfd\x99\\\x81\xaff\n\x13\\\x94\ni\x10a\x1b:\x8f<,\x8f\x8e8\x91Rd\xc0\x88u\x95Dj6\xa3@\xd2\xc7\x0f!\xb8`\xce0\xdbZ\xda.-9'8\xc3\xab\x8eG9\xb7\xfe\x01\xfe&.\xc3 \x8efr\xc6\xbd\x1d\x9ce\x9d\x9am-+\xc2\xa4[\xe2a\x89\xec\x0bm\x14)\xa0h\x96\xee z\x11\xb9\xf7\x1f\x93`\x18\x9e0\x15\xf7\x8di\xd7\x02Ex!\xacLk\xd4\x0e\xdb\x81^Pz\x9dgh\x8eo\x89\\\xea*w\x8f\xa9+\xb9\xb1\xc9\x12=\x90<0c8v\xfd\x00\xaat/\xa5\x01\xa5\xe3\xab\x0c[J[\x17\xf8\x06\xd3\xb4(\x8d\xd3\x8c\x13^\xd3\xf5\xcez\xe14\"\x9e\xe8\xb3+\xc3\xed\xfa@\x93\x04\xcd\xf0=1F\x92\x1c\xe61e\xa5:\x92R\xe2\x16\x0b4\xbd\xcf\x92\xfbf\xfax\xbb}?c'\xef\xbf$\xa7s\"\x13\xb1E(\xd8E#\xd2\x18a\x19\x81\xc6\xf6\x9f\x13`\x1d\x8f\x86\x15KEv\xbe\xe2/\xcfk\x9fg\xf7\xae\xa7w\xebK\x9eQ\x9e\xe3i\x81\x1b\x16\x11a\x8d\x89\xf0\x9b\x83\x00\x1b\nb?=\xff\x95W;pK#\xe0\x0exh\x16\xd8p[\x96\xc6\\\x84\"\x19\xd6JS@\xc8u\xd0\x1c\x1a\xce`\x82\x02q\x05\x12\xec\xb4!\x9c\xe9\x06I\x19_\x80o$\x18\x8b\x07\xcc!\xf9\xcf\xd7\xc1#\x03\xd9\xda!a|\x93|\xf2\xc2kn2\x10\x9f\xfc\x81\x1e`\xe6\x0c\xa1z\x84X\x8f~\xbbf\xa4\x850\x84\xd6\x95\x82=\x9c\x0b\xc5C\xb2\n*\x1c\xf3M\x9f\x06P4\xf6\xbb\xa7\x90\x99\x04\xe9J\x04\xd4\x97\x08AB\xb5E\x03.\x0d\x04\x97\x13h=\xc1\xdb\xa2\xe9\x99\n\xebN\xf4<\xd4\x01\x8e3-\xa4\x87kR\x13\x18\x90O0m\x8a6\x8fWc\xe8V\x84\xb6\x0f\x9f\xc2)\x1fy\x91\x0c\xa3|E}\x82\xecK\xc8N~\xe7\xd1\x9b1\xceZV\xa0\xe3\x9d\xb8\xecQmk>\x80\xb5i\x1a\xc1\xd6\x00\x8a\x10(\xd7Q\x8f\xc7\xdf\xc2\x17Z\xa8\xe3\xba\x08*\xa7\xce\xb4\x87\x15\xd4\xe6pje\xc5\xd4\x06\xd8\x83OQ\xafw\xb56\x88g]\xc4\x11\xe017K\xafVD\xfbP^\x86\x14\x17\x90u\xc3y\x02QZ^p\xabP?\xd2\xc2\x19N\xfd\x8a\x8a\x0b\xb0\xbcl\xac\x80\xe0+\x9d\x8e\xee\x17\xd8\xb4\xefj\x0dO\xb0Yq\x92\x03z\x1ef\xab\x7f\xd1H\x87~Km+\xc8\xac\xd4\xd9\xfe\x0bw\xdev\x17\x86X\x0e\xcf\x9b\x1f \x90q-\xd2\x91T\xf5\xb3\xc7b\xec\xd5K\xa2\x0e\xa6\x16\xa4\xbeC\xe8O^\xc4@\xa6\xa6{a\xc9\xf8}Ka\x8d\x93\xa4\xc8\xd0m\x9a=\xa4\x08\xb3U\xfb\x8d\x99?\xde \x8d\x97p\x18\xc38V\xa7'\xa8M\xa7n;T^B+\xf7 05\xaf\xd1[^/\x93\x9634\xa5IIr\x12\xa3\xdb{\xa5\xd7K\x92\xe32\xcb\xdd\xc1e2r\xdd\xcb.\x10\x81\x12\x90\x922\x0d{WQ&\xa6\xda[\x08BK\x9ae\xf3\x0e*\xe3\xd1|\xd9t*\xe3\xe6\x9ao+B\x99\x15\\\x14\xa3\xb9a\x80I\x15\xc0\xa5\x88\xc0\xd3\x80P\xe7^\xbd\x13\x02\xc9\xa7Cm\xd9 49\xb1\xde\xdf\xcdp1\x1b\x9f\xd4R\xbe\x94\xce\x89H\x8d\x8a/j\xad\x05\x93-BB\x11\x0d\xc2\x0b0\x05X\x84\xabY\xee\xae5\xf2o\xd9\xc0\xfcUx\x7f\xec\x12k:<\x8dm\x82\xd6lF\xd9|\x9e\xa5|<\x7f\x0c\xa9x\xcf\xeaE\xd8!\x86\x16\xe1i\x8a\x8c\xfa\xae\xb4\xcc W\xca\xaa\xe9Z\xd5\xe8\xad\x00\xfbk}E\xcb\xf9\xb4\xa7Y\xe3\xbd\xabU\x8d\x89^\x9a\xdeg\xb7\x9e\xb5D\xd3EU\xbe\xda\x04/\xc8.\xe85\xf1\x10S\xaf\xdd\xbe\xb3i\x17\xa5\x8dT\x99s\xfej\x0dMo\xd1\x04G\xb7\xb28<\x00\x12\x8fQ\xe0\xa9\x1a|\xd1\xf8=\\\xfa%\x82\xb0\x8b\xab'\x0b\x80\xe6\x81h\xf0\xadb\x11\xdfR\xd1\xd71\xe3\xdf@\x89\x16_\x8b\x12O\x12Z\xccH\xac\x82\x19BA\xe6\x109\xde\x93M})\xb7\x0b|J\"\x9e\x88`X\x15\x00x\x8cQ\xe8<\xcf\x16Y\x01\xe7\x81\x16\xcb\xeba\x04\x7f\xe6q\xa1p\x9ar\x8f^\x99W\x11\x8f&\xe26\xd2\x1c\xe7\xc5,\x10\xb4\x8ePQ\xe2\xb2\nn\xfd~\xfc?\xd5\xb9.t*\xcc&.\xf8\xb8\x88T\xcbP!\x0d\x9a\x00\x9d\x86\xf1\xb3\xe2qC\"F\x9eM1\xbf%[Ta\xf1\xdc\x8b\xc7\xf0\\\xc9\x8b\xaf_\xce.~\xbf>\xfd~\xfe\xe3\xea\xfa\xf2\xea\xe4\xea\xc7e\xaf\xfc5\x17\x8c\xf3\x8b\xb3\xf3\xb3\xcb\x15\x00\x88o\xc1\xee:\xffnUB\xfaK\xf2 \xf3 3\x10\x00a\xa4]\x01\x12Bx\xb4\x17Nh\xbcW\xa5\xe2\xbc(\xd6-[;\x80\xce\x81\xa9\xb4\xf3X\xfd\xb5\x9d f\xec\x98\x14\xe1|B\xcb\x1c\xe7\xcbZ\x82\xf1j\x85\xfa\xcc'\xb6\xc2p\x1c\xc57;\x86\xe2\x9b\x1d?*6t\xc3\x02[\xe4\xe4\x9efU\x91,;[\xddH\xaa\xf2\xe2*\x85\xcdU\x8e\xa3[\xe1\x11\x13\x96\x93>\x01\x12\xa5\x93\xa0\xa72\x90\xf42\x07\xe9\xd8\xd9\x8c\xb6hF\xc9\xbdx),\xab\xca\x90\xd8\xc9R\x10v\x02\xd2\xab\xb5\n_X\xd7\xff\xa7\xd4\xf3\xa5\xaa\xac*\xd8 +\xd3 \x9a\xe8\xb1\xa7\xbd\x00m7\xd4\x0bh\xd0K\x0eS\xd1\xa5\x96`\xed\xc6\xa8\x0f\x84\x00`u~\x93\xe4\x8d\xdc\xca4\xbdQ\x8f\xec\xecL1M\xaa\x1cp\x84DL\x0d/H\x1a\x83&\xb2\xcf\xac\xf7Q\xba\x97?\xfe\x18\xa4\xa5\xba\xbd\xcfO.a)\xf5\xcdn\x97\xffuz>\xa0\xdb\xb7\x93\xd3?\x82\xdd\x0c\x9d<\x94\xcea\xda\xd81\x1adR\x9c\x9d\x1b\x1a\x18UiA\xc2\xb6\x1a\xbf\xcd\xe8\x93E\xdf\x9d\xd26\xf3\xd8\xb7\x86\n#\xc6nb\x1a\xb4~mj\xc8\x90l9\xb4\x87d\xdf\x8c!5I{Q\x96\x164V\xde\x07>\xf8-\xe5\xb7\x16\xb1xukN\x8b\x82mN\xa9\x8f\xb2\x1c\xc5$\xc1K\x12\x03\x13\x13\x1dH\xb2\xc5\xd7F\x92}s\xf2\xa5\xbeP\xb4b\xced\x86+\x00]5\xa8\xd0\x13\x87\xaa3!\x9fH\x1a\xe1EQ%\x1a%)\xb7\xf8)\x16\xe2GD\xb5\xe5\x01\xc8I\x86\xe1(px[\xfc\x8a\xea\xd7\xe0\xab\xa4\xe4BZ\xb2L<\x9d\xaa\xc5u`\x96\xd8O\xc5AFu\xa8\x8b\x14S\x98\xe9\xf0:*+\xb5\xb1T\x0c\xac\xef2\x00z\xb6\xb0\x16a\xb2\x16Z\x92\x97G\x0ep\xe1\xd9>\xd1W\"\xd9\x94[\xae|\xcaqY\xe2h&F\xab_\x0d\xccr\x9e\x10\xe5\x84\xd5\xdc1r\x1d\xf3\xb3{0]\xbb\xc1b\xfd\x00\xa3\xd8\x0f\xa9th\xc8\xcf2\xc7\xc9N\xb2v\\\x8d\x12\x9e\xd4\x84\x16\x88K2\xe2\x8f\x9c\x00\x1bK`\xcdqH \x1f\xde\x1a7\x8a\x1e\xdf\x19Z$'4\x18.TO\xc3&\xc7 \xb5\xb1\x1c\x1e \xa4!AX\x02\x0bG\xdc\x04\xb6\x8c\x11\xa4Z/\x1b {\x84\xb3 \xe8\x9b\xdc\x04\xfe\x18\xa8*\x06I\xaf\x1f\xff\xb4\x16\x0e\xbd@\x1c\x941r(\x00j\x8c\x9d\xf2\ns \x00T\x81\xb4\x04\x02j\n\xb4M\x81\x90\xed\x19\xa8\x0b\x9e\x0e\xb6)\x10\x83x5\x86vAh\x9b\x02\x01\xa7|\xe4E2\x8c\xf2\x15#IQ\x9f\x14\x08)\x97\xc7L\x80\xb0\x80\x1c/\xfdA\xda\xd7\xcf\x99\xfc\xd0\xa4g\x04\xcb\x02(:\xa0\xfcF\x1d\x1c\x03\xa7\x13\xff>\xe8\x97\xf6\xd0\x9a\xec\xb0J\xda\x14\x1e\xad\xac\x88\x9a\xe0\xc0\x1cZK\xd4\xfa\xfa\xb9\xb5\xaed\x87\x81\\|-G \x0b\xbe\x96s\x90\x17\xd2\x08\x0c\x0b\xe9s \x9b\x86\xd2?\x8a&\x87\xd2\xdd\xc9\xa3\x18\xf3\x8cd\x05\xee<+\x81@q\x81\x8bh\xec>3\x05I\x06i:\x04\xd4v\xa8g\xba\x1f\x82\xaf\x1f\x04\xdfj\xa8\xe7\xc4\xa0\x1e\xe9\x7f`@}u!\x1a\x96\x06\x886\x96\x83!\x1d\x19\x04X\xf6J\xe8B\xb04\xa8W\xca\xcba\x1a\xb4\x0b\xca\xa2E{B\xe87\x07!\xe5\x81\xfa\xb1|5>\x06UI\x10^\xef$K\xd4;\xd5\x10='GF8,\x02\x96\xea\xd0\xd4Cq-\xec\xce<\x94\xc7\xb9gK<\x14\xe3y\xf2\x0e\xf5\x0fVL;\xe4\x7f\xe9.\x92\xd6m\x12\xff\x95\x08\xc2\x8b\xb2\xc5\xb2\xb6\xf1\xc4\x1f\x9a\xc9M\xecO\x85\xbe\xb6\xeeR\xef5\x02\xfc\xaa\xdf\xef)\x0d\xae\xe50\xdf\x911\x08\x8ap\x8a\x08\xe55\xbb'\x04a\xbelw\x10yw\xe3\xda\x84\x9f\x0f>\xff\xf6i\x82\x0fw\x8f\xa6\xef\x8fv?\x1c}\xc6\xbb\x9f>\xe2\xdfv\xa7$\xc2\x07\x93\xfd\xa3\x83C\xb2/\x8a\xbc+\x99\x10\xb9_\x9f\x93j\\\x0e\xe8\xc3\xf5\xe0\xee\xe9\x86\xec?\xe1\xa7\xb2\xfaxT>\x1e=\x1e%\xc9\xfd\xd1c\xf4\xf9\xa9,\xee\x1e\x93\xdb\x07\x92\xd80\x0e\x17\xa0\x1a\x8b\xa1\xadrS5\xf9\xcdZt\x01R\xc5\x8f\x0f\xee\x1e\x9d\xc4~.\xee\x92h\xf6\xbex|H?|\xf8y\xb4\xff\xf3\xe9\xa6\xfc\x94\x17\xb3\xfb\xbb\xe54\xff\x19\xe56.\x04/cFbB\xeb\xeaE\xf3@I\x96\x06\x0b| \x0e\xee\x0e\x9d\x0cx\xf8p\x18\xdf\x1d\xfe\x8c\xef\xe71~\xaa\x1e\x9e\"\x1c\xc7\xb3\xd9\xa7\x9by\xf5~\x1e=\x91\xf76\xc82\xbbE\x95\xb5r\x98\xdc\x82\x0b\xbe\x8a\xf7\xdaF\xf1\xfc\x06\xc6\xaa6F\x82_\xd3\x04\xdfp\x89\xaa\xeb\xe8g\xea\x97\x1e\xed\xa1a4\xee/\xf4\xc1\xa5Xdia\x8dgPHHA\xbb9\\1U\xc3@\xa6(\x10}yB\x1e\x05\n\xde\xd3\xeas\xf2\xa4\x85\x90\x9b'\xf2\x87\x9cB\xe7\x9d \x07\xc1cfB\x8c\xa8\xe7\xc2\xa1?\xd133\xa2\x85\x10dq8Si5\x8c\x14-\xb3*\x97\x8a\xbc\xc9\x86\x06V\xbc\xd0\xec\x85d\x17\xaa\xa3\xd6\xc4\x7f36\xc8Hg\x82\xfeY\x91|\xb9'*\xd3^\x9c\x7f\x91\x89\x965l\x15\x92\xfa\x8bc\xa8\x93\x14U)y\\\x90\x88\x19\x1a\xe2%$5\x92\xd1\xa7\x88fd\x8e\x9b\xd3\xe24;\xdc&\x07\x87\xdf\x9d\\\x8fF\x88\xb2\xd8\xe2\xe8\x13\x1d\\\x05\xba\xd5*\xa0i\xf9\xfe\xb0\xf5Wgqk\x0f\x0e1)1M6\xa4\x96\x04\xeb~]\xe5\xce7X\x82\xda\x95\xa7`\x0e\xeem=:/p\x8e\xe7\xa4$\xb9\x81\xf3\xae\xc8:5\xd5\xb4k\x0d\xb66\xe1`\xc5>\xae:\xa7\xe91Z\xe0\xd2\x8c\x10d\x1b\x99\xe6$>Fe^\x99\xdb\xc3\xca6\xc5\x01e\xfe\x02\xc9\xefk-\x0f\xb3\x91}&\xe2\x10\x1b\x98q\xeb\x8e\xc9\"+\xbb\xa68)\xe0\xfc2\xac[\x17\xcf\xfe\xb7\xc5\xb3~\x06\xb1\xa5\xebXV\xf0\x88|h[m\xc0\xf53\xc8\xd83\xcd\xba\x064\x9093\x84\xe8\xae\x8av)\xef6?\xa4\xb1\xd5\x93\x1d}\xac\xbc\x96=\xd7\xdd\x1c/\xcf\x8c\x96\xa1\x06d\xc6@\xf3\xae6\xe4:k\xe3\xe58\xd0\xb2\xd0\x80\x1c\x18d\xd7\x19\x16\\\x03\x98\xc3\x9a['\xfd%\xbei(\xd9\x7f\xcaq\xde\xec-\xf2\xec\x9e\xa48\x8d\xc8\xde\x9c\x948\xc6%\xde\xbb?\xd8\x13Ky\xef_\x86V\xfd\xbf=\xb9\xdb\xdf\x08H7D\xdb\x87E5\x9f\xe3|y\xacJ\x87\x15\xa8 8\x8ff\xf2iE%%\x14\xb5.N_\x19qG-=&\xf6\x8fE\x97\xc1\xf5\x98W\x81\x81\x95\x17\xba\xa4s\x9a\xe0\x06n\xbf6\x90\xbbz\xc8\xd8\xd4\xf0wo\xb2\xa9gF\x9b\xdb\xcb\x9cJs#k\xd2\xd8\x8f\xe2,}Sr\x11\xc0#\x1dd\x9d\x1fT\xe09i\x89m;\xbf8\xf4\xb7L\xec*n\x1bC\xf3\xaaW\x1dn\"\xdc\x16\xc6\xaa\x9b\xd8vr5\xc5\x19)P\x9a\x95jY\xa1\xa2\x8af]~y\xd0ja\xc2\xdf\x0f\x9ad\xe5\xccX\xc2\x93\xaa\xacC<4\xa0\x98N\xf9'\xb5\x84\x0bs\xc9\xfec\xa9\xfc6;\xc6\x02\xa8\xc7*\xe4;E\xa5\xae\xa4d\xa0xI\xca\xfa\x0c\xa0:\xeeey\xc7\x12f\xf3\x90\x8b\xbc\xd5\xc6I\xa0q\x85\"\xfbj$\xc5W]\x82\xeb4\xaeM\x05\xf5\xfa\x992 \x0d;\xe5\xcd\xe1\xfe\xfe\x1b\xb7\x0f\xca\xc8\xe1\xb5\x1a\x94#y\x9f8e\x81\xab9\xabCP\xda\xfc\xc2\x0f\xa7\x0b\x95F\xea5(4\xcb\x92\xb8\x90\xb7tZ\xcd\xf5\xbd:\\\xfd\x1e\xcf\xe75\xf5\x84\x08\x85\x8a\xd3$\x8f\xe8\xa2\x11\xa2\xe9=)\xfa\xd3\xa3\xf7\xe9\xb1\xf5\xab\x9a\x9a\xb4\xa4\xe5R\xa8oMcT\x15e\x16S\x9cJB\xa5?\x8f3\xb8/\xa1|\xdf\x1fw\xbe\xb4\xeb\xcc-p^.%N\\i+-\xc5\xb4o\xcf!\xb5\xdc8\xb6~\x15\xdc\x15\x03\x8a\xe3_\x8a\xf0tJ\x13\x8aK\x82\xf0MN\xb8\x19\xd2sP)u\x8e-\xdf\xc4\x80\xdc\xea\xc1\x89\xb8\x90\x97/\xd2.k\xbb)\xcf\x12A\xfc<\xa5\x93\xaa@\x13\x9c\xde*\xad\xd8\x13\x95Z\x96\x1d\xdb?3\x84TE\x155\x0f\xe64\xe4d\x91\x93\x82\x9bbl\n\xeaj~\xd2\xa3\xda\xac(\x8d\xa3\x01\x9b\xba\x96\x9b\xc7\xf6\xcf\xcd\xf5\xf90\xa3\xd1\xcc\xe0\x93\xb6!\xd5\xae\xd7\xc5n)A$\xcd\xca@q\xeb.BZ \x1f[\xbf\xda\xd0\xe15$\xf9\x12\x16\xf5}\xc4\xfe\x80\x94\"B\xfa\xdd\xea\x90e\x11\x8eb\xa9\x1b$\x9e\xa5n\xd2V\xd3\x97\x18z\xfa\xdf\x14\xa8\xa07)\x16\x0fV\x17\x1aU\x0f<\x98\xddw\x82\xce\x8d]\xae\xacQ\xb6\x05\xf7x9n\xc1Ef\x17z\xc1\xb4\xab\xa4\x07+\xec\xf4\xd1\x83W\xfcX._\x8b\xae7\x02*\xb3\x05J\xc8=I\xe4\x91\"\x1c4\xad\x1c'\xc2\xa2}\xd7\x06\xcd}\x99\x8c\xcf\xdc\x91'\x0f\xfa>\xe1Z\xbfq\xbft?Q-La~\x9a\xb2\xbc\x90!0h\x9eU\xbd\xeb\xb4J\xd9\xd9\x8esY\xe1\x9d\xd0\xa2\x14\xf5\xb6\xcc\x8b`W\xcc$\xc3\xe4\x1aG\xfe7#F<\x84\x05,z\xf8:8\xd1LR!\xde\x8cp\xce1\\\x95\xb3,\xa7OB~\xe6$\"\xf4\xde\xb7\x10\xea\xba\xd4|^\xbaE\xfe\xb5C\xc2\x01\x84\xc7\xe0\\\xf3uw\x1d<\x1c\x8e\xcd\x87z\xa36U\x15\x9b}Q\x9f\xbdE\x90wm\x1a\xbe\x97\xcb\x12\xa71\xcecS\xbdH}'\x9e\xb4\x9f\xe3\xfc\xd6\x12\xafU7\xf5k\xdfv`\x13VT\x8bE\x967*~r\xcc\xf9\x8e\x10%\xbfr:\xa9J\x82\xe6x\xc9\xafL<\x00'\x84I\xf7\xf4\x86\xc4h\"\x1c\xeeR'\xd4U\xdd\xb24bv\x0b\x93s\x05\xb1\xe4\x1f\x88&\x9dD\xd7\\\xe6^\xe7Y\x92T\x8b\xd0\xb4\x86$;T\x03\xc0\xe7\xff/\xa9\x1ep\x92h\x11\xd0Lz\xe0\xf7\x1de\x81,\x81'\xed\xa6\xee\xdd\x94Pm\x00zS(\xd1:\xa5$\x89=\x8f\xff\xabiK\x8a\x0c\x91\x14O\x12q\xe6g\"Y\xe9\xaa\x7f\xe7\xbej\x81\xa0\x80\xea[#\xa9~HJ\xd1h\x9f4\x18\xdbDx%\x7fv\x80\xf0\xba\xc9Yf\xf8\xf4\x85%\x8d\xa2,I\x087\x9c\x18\xda\xdd \x15\xb31\x02\xf8j\xcbR\xa2\xef]<\x88*\x07\xac'J\x18FI\x03P\xed\xdd\x0b\xd5\x80s{ljed\xfdA\xd0\x91\x16v\xa1=\xa7\xf3\xb9\xc1D\xf3*\xbb\x1c\\\xbaGs<\x94\x0f\xbaf*B)\x9d5E\x9e\xa48\x0dL\xfc\xc3\x9f\x0f\xb7&\x82\xbc\xc2\xcd\x82 \xf2U\xd0 \xf9\xb5A\xfc\xc0\xdeDI\x10\x1b\xfaR\x88C\xe9\x90c\xd16\xc2\x14\xf7\xa5m\xa5\xc4F\xcb\x02p\x0b\xd4p\xa5\x00\x18\xee]h\x01\xd1\xea\x80\xd3\xadzc\xb9\x98Y\xb3\xa0\x0df\xfe\x83\xe6|-\xdb:\x90\xde\xef\xbd6B\xed[*\x98\xdc\x86&\xf3\xbf8S \xe2\x1d\n\x0b\xc6\x12\xe3\xbfF\x10\x12k\xe5M\x0b\xd5\xa6F\xd0\x9f=\xe0\xacj\x12\xce\xad\x17P\x10\xb0\xa4\xf9\xf1\x17\xc6\x08Ka\x10\xa9#(\x0d\xc7ji\x13\xedN\xeb\xf4y=\xd6\x95\x0e$\xb1q\xb1<\xa8\x16\xc2# \xf4\x12\xcf\xaa\xc2\xa3%\xc6\x1d\xb7\xcf\xf2\xd3O\xd7\xd6\xf5\xd0\x1d\x15d@o\x9a\xd8*\xa2K\xc6\xbb6\x1eR\x87\xc60{6\xf3\x0d\x9a\xa0\xf3K5\xe0\x1c\xcb\x1f\x8f\x11\x1d\x01\x8b\x8f\x80z\xcaE\x1b7Fb@\x94DOF\xc2\"%V\x88\x95\x18\x1e-14^bh\xc4\xc4\xe0\x98\x89AQ\x13\x83\xe3&\x06FN\x0c\x8f\x9d\x18\x1e=10~b\xd5\x08\x8a~\x12^4g\x14\x05\xa0\xaf\xbd\xe7\xd08\x8ag\x8b\xa4x\xeeX\x8a\xe7\x88\xa6\xd8\x98x\x8a\x17\x89\xa8x\xa1\x98\x8a\x8d\x8a\xaax\x1dq\x15\x1b\x18Y\xf1\xb2\xb1\x15\xf0\xe8\n\xe8\xbd\x9aj\xd0\xfb5\xd5\xc6\x8c\xb1\x80\xdb\x8e#\xc5Y\x0c\x8a\xb4\x80b\xa9\xef\x12\xa5s\x85\xf0g\xbfD\"\x13\xff\x83\xccx\xbbIy\x02\x997\x00\x1c\x85\xd1\x1a\xa9\x020\x8c6\x9e\xdbSf5\xfb\xcc\x8b~y\xcf\x08{\xe1M\x9a\xf7;\x0c?\x1a\xe1$Y\xd6o\xfb;;\xcaN#\x90\xbb\x863\xb0:\xb0\x88LjJ\x12q)\xcf423\x01\xa2\x84\x92\xb4>\x0f\xf3\x88 /\xbc\xee\x12\x0d\x1e\x82q\x15S/w\xfa\xd0\xf3\x85\x1f\xbfc4Y\xee\xa0j\x11\xeb\x7f\x97tN\x8a\x12\xcf\x17\xc5\x8ev\x9f\x892\x82\xfe\xb77E\xdaV\"_\x87\x9bfn2\xc0'l\xe8\xf9Zx\x12\xe2kFDHp\x02\x17\x102\x16\x11\x03\xbb\xcb\xd8\x12\x02-\xf6\x98\x8cc\"{\xac\x0b\x93\xa02V\x80\xa4e\xbe\xe4o\xc2I|A4M\x96#R\x04\x93\x02\x08u\xde\xe67\xfd\n\xc2D\x91\xab'(\xe0\x90\x0e(\xf3\xfeL\xae\xc0W3\x85 .J\x854\x88\xb0\x0d\x9dG\x1e\x96GG\x9cH)2`\xc4\xba*[5\x9bQ\xe7\xea\xe3\x87\x10\\0g\x98m-m\x97\x96\x9c\x13\x9c\xe1E\xcf\xa3\x9c[\xff\x00\x7f\x13\x97\xe1\x04G39\xe3\xde\x0e\xce\xea\\\xcd\xb6\x96\x15a\xd2-\xf1\xb0D\xf6\x856\x8a\x14P4K\xf7\x04\xbd\x88\xdc\xfb\x8fI0\x0cO\x98\x8a\xfb\xc6\xb4k\x81\"\xbc\x10V\xa65j\x87\xed@/(\xbd\xce34\xc7\xb7D.u\x95\xbb\xc7\xd4\x95\xdc\xd8d\x89\x1eH\x1e\x981\x1c\xbb~\x00U\xba\x97*\x1b[\xc5W\x19\xb6\x94\xb6.\xf0\x0d\xa6iQ\x1a\xa7\x19'\xbc\xa6\xeb\x9d\xf5\xc2iD<\xd1gW\x86\xdb\x95W\x12\x98\xe1{b\x8c$9\xccc\xcaJu$\xa5\xc4-\x16hz\x9f%\xf7\xcd2\x04\xed\xf6\xfd\x8c\x9d\xbc\xff\x92\x9c\xce\x89L\xe9\x16\xa1`\x17\x8dHc\x84e\x04Z\xa3\x9aE\xbb\xd5\xf1hX\xb1Td\x8f_\x9a\xf5.\xe6\xd9\xbd\xeb\xe5\xdf\xfa\x92g\x94\xd7\x80Z\xe0\x86EDXc\"\xfc\xe6 \xc0\x86\x82\xd8O\xcf\x7f\xe5\xd5\x0e\xdcj\x94\xa2\xb0\x07<\xc8\x99\x0d\x8424\xe6\"\x14\xc9\xb0V\x9a\x02B\xae\x83\xe6\xd0p\x06\x13\x14\x88+\x90`\xa7\x0d\xe1L7H\xca\xf8\x02|\xa2\xa1l\x94:\x81qH\xfe\xf3u\xf0\xc8@\xb6vH\x18\xdf$\x9f\xbc\xf0\xb2v\x1d\xa00\x9f\xfc\x81\x1e`\xe6\x0c\xa1z\x84X\x8f~\xbbf\xa4\x850\x84\xd6\x95\x82=\x9c\x0b\xc5C\xb2\n*\x1c\xf3I\xa1\x06P4\xf6\xb3\xab\x90\x99\x04\xe9J\x04\xd4\x97\x08AB\xb5E\x03.\x0d\x04\x97\x13h=\xc1\xdb\xa2\xe9\x99\n\xebN\xf4<\xd4\x01\x8e3-\xa4\x87kR\x13\x18\x90O0m\x8a6\x8fWc\xe8V\x84\xb6\xef\xae\xc2)\x1fy\x91\x0c\xa3|E}\x82\xecK\xc8N~\xe7\xcd\x9d1\xceZV\xa0\xe3\x9d\xb8\xecQmk>\x80\xb5i\x1a\xc1\xd6\x00\x8a\x10(\xd7Q\x8f\xb7\xe7\xc2\x17Z\xa8\xe3\xba\x08*\xa7\xce\xb4\x87\x15\xd4\xe6pje\xc5\xd4\x06\xd8\x83OQ\xafg\xbd6\x88g]\xc4\x11\xe0-9K\xafVD\xfbP^\x86\x14\x17\x90u\xc3y\x02QZ^p\xabP?\xd2\xc2\x19N\xfd\x8a\x8a\x0b\xb0\xbcl\xac\x80\xe0+\x9d\x8e\xee\x07\xe0\xb4\xefj\x0d/\xc0I\xd8\x9e\xd7\xde\xd4\xe8\x8e7\xd5d\xf6nwn\xc5\x8c>o\x88\xbf@\xc6\xb5\xceF\xd2\xb6\xcf\x1eN\xb1W\xcfj\x1d\x0f-\xeb\xad\"\xf4'\xafC \xb3\xcb\xbd\xb0d\x08\xbe\xa56\xc6IRd\xe86\xcd\x1eR^\xba\x16}c\x16\x8c7\xce\xe2%|\xbe0\x8e\xd5\x19\x06j\xe5\xaa\x0b\x0b\x95Z\xd0J\x1f\x08\xc8<\xcdk\xf4\x96\x97\xbc\xa4\xe5\x0cMiR\x92\x9c\xc4\xe8\xf6^\xa9\xe6\x92\xe4\xb8\xccrw|\x98\x0c>\xf7\xb2\x0bD\xa0\x04\xa4\x04E\xc3dU\x94\x89\xa9\xf6\xd6r@u\xe5\xe4\xc65R\xc6\x03\xf2\xb2\xe9T\x86\xbe5\x1fX\x842+\xb8(F\xf3\xa4\x00\xf3\"\x80K\x11\x81\xa7\x01\xa1\xce\xd5x'\x8a\x91O\x87\xda\xb2Ahrb\xbd\xbf\x9b\xe1b6>\xa9\xa5|k\x9d\x13\x91\x1aE[\xd4Z\x0b\xe6K\x84\x84\"\x1a\x84\x17`\n\xb0\x888\xb3\\?k\xe4\xdf\xb2\x81y\xd1i\x7f\xf8\x11k:\xc2\x8c?^\xd0\x9c\xcd(\x9b\xcf\xb3\x94\x8f\xe7\x0f\x03\x15/\x8b\xbd\x08;\xc4\xd0\"\xc2L\x91Q_w\x96\x19\xe4VX5]\xba\x1a\xbd\x15`\x7f\xadoY9\x9f\xf64k\xbc\xd7\xad\xaa1\xd1K\xd3\xfb\xec\xd6\xb3\x96h\xba\xa8\xcaW\x9b\xa3\x05\xd9\x05\xbd&\x1efA6\xdbw6\xed\xa2:\x91\xaay\xce&>\xa1\xe9-\x9a\xe0\xe8VV\x95\x07@\xe2a\x06<\xdb\x82/\x1a\xbf\x93J\x97\xf3\x0f{\xa9z\xb2\x00h\x1e\x88\x06\xdf*\x16\xf1-\x15}\x1d\xf6\xfd\x0d\x94+\xf1\xb5(\xf1$\xa1\xc5\x8c\xc4*\x1e!\x14'\x0e\x91\xe3=\xd9\xd4\x97r\xbb\xc0\xa7$\xe2\xb9\x04\x86U\x01\x80\xc7\x18\x85\xce\xf3l\x91\x15p\x1eh\xb1\xbc\x1eF\xf0\xd74\x17\n\xa7)w\xca\x95y\x15\xf1\x80 n#\xcdq^\xcc\x02q\xe7\x08\x15%.\xab\xe0\xd6\xef\xc7\xffS\x9d\xaeB\xa7\xc2l\xe2\x82\x8f\x8bH\xb5\x0c\x15\xd2\xa0 \xd0\x99\x14?\xf93!2\xcc\x9dM1\xbf\xe8ZTa\xf1\xdc\x8b\xc7\xf0t\xc7\x8b\xaf_\xce.~\xbf>\xfd~\xfe\xe3\xea\xfa\xf2\xea\xe4\xea\xc7e\xaf\x144\x17\x8c\xf3\x8b\xb3\xf3\xb3\xcb\x15\x00\x88o\xc1\xee:\x85nUB\xfaK\xf2 \xf3 3\x10\x00adN\x01r:x\xc0\x16Nh\xbcW\xa5\xe2\xbc(\xd6-[;\x80\xce\x81\xa9\xb4\xf3X\xfd\xb5\x9d\xe3e\xec\x98\x14\xe1|B\xcb\x1c\xe7\xcbZ\x82\xf1\x82\x83\xfa\xcc'\xb6\xc2p\x1c\xc57;\x86\xe2\x9b\x1d?*6t\xc3\x02[\xe4\xe4\x9efU\x91,;[\xdd\xc8\x8b\xf2\xe2*\x85\xcdU\x8e\xa3[\xe1\xd4\x12\x96\x93>\x01\x12\xa5\x93\xa0\xa72\x90\xf42\x07\xe9\xd8\xd9\x8c\xb6hF\xc9\xbdx\x1d%\xab\xca\x90\xd8\xc9R\x10v\x02\xd2\xab\xb5\n_X\xd7\xff\xa7\xd4\xf3\xa5*\x8e*\xd8 \xab\xb4 \x9a\xe8\xb1\xa7\xbd\x00m7\xd4\x0bh\xd0K\x0eS\xd1\xa5\x96`\xed\xc6\xa8\x0f\x84\x00`u\x8a\x92\xe4\x8d\xdc\xca4\xbdQ\x0f\xee\xecL1M\xaa\x1cp\x84DL\x0d/H\x1a\x83&\xb2\xcf\xac\xf7Q\xba\x97?\xfe\x18\xa4\xa5\xba\xbd\xcfO.aY\xf1\xcdn\x97\xffuz>\xa0\xdb\xb7\x93\xd3?\x82\xdd\x0c\x9d<\x94\xcea\xda\xd81\x1adR\x9c\x9d\x1b\x1a\x18UiA\xc2\xb6\x1a\xbf\x90\xe8\x93\x08\xdf\x9d\xd26\xf3\xd8\xb7\x86\n#\xc6nb\x1a\xb4~yj\xc8\x90l9\xb4\x87d\xdf\x8c!5I{Q\x96\x164V\xde\x07>\xf8-\xe5\xb7\x11\xb1x\x85kN\x8b\x82mN\xa9\x8f\xb2\x1c\xc5$\xc1K\x12\x03s\x0b\x1dH\xb2\xc5\xd7F\x92}s\xf2\xa5\xbe\x13\xb4b\xced\x86+\x86\\5\xa8\xd0\x13\x87\xaa3!\x9fH\x1a\xe1EQ%\x1a%)\xb7\xf8)\x16\xe2GD\xb5\xe5\x01H+\x86\xe1(px[\xfc\x8a\xeaw\xf8\xab\xa4\xe4BZ\xb2\x8c?FX\x8b\xeb\xc0,\xb1\x9f\x8a\x83\x8c\xeaP\xd7\x19\xa60\xd3\xe1u\x14Gjc\xa9\x18X\xdfe\x00\xf4la\xad\xa3d\xad\x95\xa4\x1e\xeb\xb3\x03\n\xcf\xf6\x89\xbe\x12\xc9\xa6\xdcr\xe5S\x8e\xcb\x12G31\x9aNEe{\x93\xe0\xc8]\x19\xbb\xb9c\xe4:\xe6g\xf7`\xc6u\x83\xc5\x12#\xc9\xb9,\x95\x0e\x0d\xf9Y\xa6)\xd9I\xd6\x8e\xabQ\"\x8c\x9a\xd0\x02\xa1EF\x08\x91\x13`c \xac9\x94\x08\xe4\xc3[\xe3F\xd1\xe3;\xa3\x83\xe4\x84\x06#~\xeai\xd8\xe4P\x9f6\x96\xc3c|4$\x08K`\x11\x85\x9b\xc0\x961\xe2L\xebe\x03d\x8fp\x16\x04}\x93\x9b\xc0\x1f\x03U\xc5 \xe9\xf5\xe3\x9f\xd6\xc2\xa1\x17\x08e2F\x0e\xc50\x8d\xb1S^a\x1a\x03\x80*\x90\x96@@M\x81\xb6Y\x0c\xb2=\x03u\xc1\xd3\xc16\x8ba\x10\xaf\xc6\xd0.\x08m\xb3\x18\xe0\x94\x8f\xbcH\x86Q\xbeb0(\xea\x93\xc5 \xe5\xf2\x989\x0c\x16\x90\xe3e0H\xfb\xfa9\xf3\x17\x9a\xf4\x8c`Y\x00E\x07\x94\xdf\xa8\x83c\xe0t\xe2\xdf\x07\xfd2\x17Z\x93\x1dVI\x9b\xc2\xa3\x95\x15Q\x13\x1c\x98Ck <_?\xb7\xd6\x95\xaf0\x90\x8b\xaf\xe5\x08d\xc1\xd7r\x0e\xf2B\x1a\x81a!}\x0ed\xd3P\xfaG\xd1\xe4P\xba;\xa9\x10c\x9e\x91\xac\xc0\x9dg%\x10(.p\x11\x8d\xddg\xa6 \xc9 M\x87\x80\xda\x0e\xf5\xcc\xd8C\xf0\xf5\x83\xe0[\x0d\xf5\x9c\x18\xd4#\x83\x0f\x0c\xa8\xaf.D\xc32\xf9\xd0\xc6r0\xa4#\x83\x00\xcb^9Y\x08\x96\xc9\xf4Jy9L\x83vAY\xb4hO\x08\xfd\xe6 \xa4\xa1\xfa\x81DU+\xe3\xc2\xc6F\x9d\x9e\xf8\x96N\xd5;\xd1$\xfe\xb5\x9b\x84\xc8\xff\xd2]2\xad\xbb%\xfe+\x11\x92\x17e\x8bem\xf1\x89?4S\x9d\x04R\xae\xbc\xc7\x80I\xe07\x04\xfc~\xd3\xe0\xca\x0e\xcf\x022\x06A\x11N\x11\xa1\xbc\x08\xf7\x84 \xcc\x17\xf1\x0e\"\xefn\\[\xf2\xf3\xc1\xe7\xdf>M\xf0\xe1\xee\xd1\xf4\xfd\xd1\xee\x87\xa3\xcfx\xf7\xd3G\xfc\xdb\xee\x94D\xf8`\xb2\x7ftpH\xf6E\xd5v%!\"\xf7srR\xa9\xcb\x01}\xb8\x1e\xdc=\xdd\x90\xfd'\xfcTV\x1f\x8f\xca\xc7\xa3\xc7\xa3$\xb9?z\x8c>?\x95\xc5\xddcr\xfb@\x12\x1b\xc6\xa1T\xc4\xb1\xd8Y\x17\x99\xeb\xcd\xd0\xa3O\xfb\xef\xa7\x9f&\xd1\xee\xc7\xfd\x8f\xbf\xed~ \x93\xa3\xdd\xcfG\x07\xd3\xdd\xc3\x83\xc3\x83\x8f\xbf\x1dD\x87$j1T\x0c\xb6\x12K\x05\x88\x83\xbbG'S?\x17wI4{_<>\xa4\x1f>\xfc<\xda\xff\xf9tS~\xca\x8b\xd9\xfd\xddr\x9a\xff\x8cr\x179\xfc\x05b\xc6\x84,M\x965\x0b\x10\xe5)p\x86#\x1e'E\xe6\xc2O>\x10a\x15|\xc1{\xa6\x91\xe6\xb4u\xab\xa4\xd9\xaf\x84f\x83\xcf>\x10\x07w\x87N.?|8\x8c\xef\x0e\x7f\xc6\xf7\xf3\x18?U\x0fO\x11\x8e\xe3\xd9\xec\xd3\xcd\xbcz?\x8f\x9e\xc8{\x0f\x03\xdc\xa7\xefq\x19`\x9e\x9aE\xd6X\xc3\x17XfhJS.\x10\x03+\x93\xeb\x8dT\xda\xb2b~e\x92\xb1\xa5\x83\xccN\x12\x1eT\x1f\x99\xbe\x17\x07\xb4\x81\xe9\xf9\x0d\x8c\x17\x0dt\xc4r\x98&\xf8\x86\xd3\xa4\x1f1\xc8\xd4\xcfP\x19xFS\xea7\x19\xf2T\x10#\xd5^\x1d>\x8bE\x96\x16^\xd6He\xb99\xcc1\xd5{\x88=\xde5\xa7\xce\x8a=\x19C\x1e\x05\x1e^\xb7\xc3s2\xa6\x85\x90\x9b1\xf2\x87\x9cB\xe7\xe5.\x07\xc1\x83\x9fB\x8c\xa8'\xc4a\xfa\xa0gfD\x0b!\xc8\nq\xe6Dk\x18)ZfU.m\xb0&\x1b\xfcX\xc9\xa2\x1a\xc5\x85d!\xaaC\x12\xc5\x7f3\xd6\xc80v\x82\xfeY\x91|\xb9\xa7\xfa\xa0\x8b\xf3/-p\"\xff\xb5F@\x05 \x1b?k\xe0s\x92\xa2*%\x8f\x0b\x121CR<]\xa5\x866\xfa\x14\xd1\x8c\xccqs\xee\x9cf\xa5\xdb\xa4\xe4\xf0\xbb+\xc0\xa3#\xa2,\xb6H\\\xd1\xc1UQ]-\x15\x9a\x96\xef\x0f;\xfcqT#\xf7\xe0\x10\x93\x12\xd3dC*\x87\xb0\xee\xd7U\xee|4'\xa8oy\xc2\xed\xe0\xdeVG\xc9\x02\xe7xNJ\x92\x1b8\xef\x8a\x1cc\xd3rq\xad\xc1\xd6\x9e\x18l\xeb\x8ck\xe1\xd0\xf4\x18-pi\xc6\x83\xb2\xddNs\x12\x1f\xa32\xaf\xcc\xedae\x9b\xe2\x80\xb23\x81\xe4\xf7=\x0d\x0d;\x03\xf9L\xf3!g\x1c\xc6\xad;&\x9c\xac\xec\x9a\xe2\xa4\x80\xf3K\x9f_\xa0\x1c\xeb}\xe0\x19z\xcc\xd1\\k@\xb3pp\xec#\xcd\n\x07\x19\xdb\xf1e\xc4\xd92\x0cs\xe0t\x0d6\xe5\x0d\xa3\xddJa\xd7\x80\x1f\x91\xce\x86\xd1\x0d\xa4t\xa8\xa1n\x98\xe4\x0dxp+t\x08\xe1]\xcb\xcaes\xb5y\"\x0d\xe5\x9e\\\xe9c\xa1\x1b\xb6\xf8Fs\xa4ed\x0392\xd04\xaf\x8d\xf06O^\x90\x03-\xeb\x1a\xc8\x81A6\xb9a}7\x809,\xf1u\xd2_\xe2\x9b\x86\xed\xf3O9\xce\x1e\x93M$\xc5iD\xf6\xe6\xa4\xc41.\xf1\xde\xfd\xc1\x9e\\\xce{8\x91\x86\xdc\x0d\xd16zQ\xcd\xe78_\x1e\xab\xea\x14'I\x82rR\xe6\x94\xdc\x13&\xe0\x13\xb5\x19\x14q\xba\xfc\xcdilv\xfaE\x91(\x96\x82\x81\xdf\x9b\xc3\xfd\xfd7\xee#\x81\x91@g]H#\x1d\x06\x9c.\x83\x970\xab\x052~\xb3x\xe5\xf0\xb1\x91bT\xc2iOh[\x90\xaf\xd1`\x1c\xdb\x16\xe4\xdb\x16\xe4\xb34\xe0RD\xe0i@h[\x90\xafn!\xa1\x88\x06\xe1\x05\x98\x82mA\xbeF\xdb\x16\xe4\x13\xedeJ\xaf@vA\xaf\x89\x0f\xf9\x9dmm[\x90\x0f\xb6U\xb6\x05\xf9\xb6\x05\xf9\xb6\x05\xf9\xea\xd6\x8b\xc7}j\x03\xadV\xc7.T\xc5m0\x80mA\xbemA>8\x8e\xdb\x82|h[\x90\xcflp\xab\xf0\x85u\xfd\xb6 _\xb0m\x0b\xf2yzo\x0b\xf2\x89\xb6-\xc8\xb7-\xc8\xb7-\xc8\xe7i\xdb\x82|f\xeb#`\xdaX\xa2mA\xbemA>\xdd\xb6\x05\xf9\xb6\x05\xf9\xc2\x89\x7f\x1a\x12\x84%\xb0\xe28\x9b\xc0\x961J&\x95\xb0ze\xf5\xa0\xaf\xa5\x1aE\xb0\x10\xc5z8\x14\xca \x06\xf2e\x00\xc1\xc1t\xe1\x91v\x8a.\xb74f\xb1\x89\x06P\xb4-\xc8gm\xc0\xcd\x82\xda\xbc7\xc3g\xadz\xa3\x110\xed7\xc9\xf5D\x855\x07z\x1e\xea\x82\xa7\x83mA\xbeA\xbc\x1aC\xbb \xb4-\xc8\x07\xa7|\xe4E2\x8c\xf2\x11j,X\x96\x90\xf7p\x01\xa9^\x04%\xc6\x022p\xccp\x82\xda\x16\xe4\x03\xf0\x1bup\x0c\x9cN\xfc\xfb\xa0_\x11\xa2\xd6d\x87U\xd2\xa6\xf0heE\xd4\x04\x07\xe6P\xbf\xda.\x1b\xc3\xada\xe5\x84\x82\xa5\x84\x86r\xf1\xb5\x1c\x81,\xf8Z\xceA^H#0,\xa4\xcf\x81l\x1aJ\xff(\x9a\x1cJw\xa7\xba\xd0\x98g$+p\xe7Y \x04j[\x90\xcf\xd5\xfaL\x0c\xda\x16\xe4\xb3\xb4U9\x18\xd2\x91A\x80\xe5\x8a\xa5\xcfz\xebK\xb4\xb9\xbc\x1c\xa6A\xbb\xa0\xb6\x05\xf9\xb6\x05\xf9\xb6\x05\xf9\xc2\x05\xf9\x04\xe8\x02\xe1\\\x04|\xb5J\xee\xb5{\xbf\xb6\xaay\xdb\xda?\xfa\x97\x7f\xf3\xda?\xa2-\xf0\x0dM\xb9\xa1b\xe7\x00\x0c\xf7\x1a\x8aH\xaf`F\x13\xef\x83\x13\xf3ou1\x16\x19k\xe7\x122\x12\x7f\xbbt\x0dZ\xada{\xf5\x96,\xdd\x92\x18$\x83A&\x00\x8cyH $&\\\xa4\xb8\xe4\xa4\xacrY\xd6\xf3\x1c\xdf\x10U\x81\xe9]J\x1e\xcbk\xf6\xe32\xf3@\x9b\x90\x1b\x9a\xbaj\x06\xb2\xc6s\xcdU\xdc\n\x83\xc9f\x89\xa0yV\x94\x88L\xa74\xa2$-\x93\xe5;t\x96&K\x94\xa5\xc4o\xb5e\xd3iAJ\x94\xe5\x8c\x0e\xdf\xb8\xc5,\xab\x92\x18M\x08*\x88cv\x15\xb4\x91\xe6\xa7\xa2i\xf9\xf1\xc3\x083$i\x149h\xd5\x9c\xe44R\xdf\xb8\xf0\x8ep\xca\xe8\x12A\xc43\x92rV\xb8\xc1\xd1\x02U)\xbe\xc74\xc1\x93\x848\x0bP\xb2v\xcaGMx$\xa8\x9a\x1b6f\x8a*\x1e.vK\x86M\x94\x9c\x0d\xdf\xd0\xfe\x89J\xe8\x9cn\xdc\xbc\xb7v\x08E\x14\n\xc5\xad\x8d9\x817\x99OH\x1c\x0b\xcd~sq\xfeE\x9bj2\x96\xaf\xf0\xacC-\xfd\x9d\xe5\x80\xf5\"}\x87\xbev\x9cG>S/\x18\xe40\xb0\xba\x05\x9e\x99\x8b\xa6EU\xf7\xf7\xf1\xb2\xaf;\xa0{\xe8o\x80\xb3:\x00\xfa\x1f\xf9\x9b\xe7\x97\x06\xb0\xee!\x7f\xd0\xb1\xde8\xbc7\x80\xd9\x0f\xf2\xdd\xa3\xfb\x90\x89]I\x88\xcbc8pZ\x87\x1c\xdam\x95\x92C\xc7\xf4\xe0\xc1|\x9d|r\xd5P}\x13(\xa2\xba \xd1\xde\xbf\x8aV\x1e\xe4\xff\xc9\xfa\xa6\xee\xba\xaa\x97\x8d\x00\x12\xc1\xb5\xba\x84N+\xbeD\x1d\x1eDN)\xcf\x1dV\xcb\xc7R}\xb5\x01\xfa\x17\xc5\xa5\x8d.\xc3z\xdd\xa0\xd7e\xa5\x0fr\x1b\x04 \xa3\x10t\x14\x1c\x01\xf5J\xd7\x05\x98\xe7\x00g\x05\xaaO\xd8\xb2:\xaa\xad\xd6js\x15\x05\xf2\xa5\xfd\x11\x93 \xb4Cg~\xa4\xaa\xbceS\xb9Ve\xba\xafto\x88T_)f\x03\xa5'\x9b\xb4Qo\xa9\xc7Pq>\xdf!P\xb5` \x96\xe0*\x12-\xbc\x96D\x0b\x05\xb0\"\xe8\xa4\xc8\x9f\x8a\xa9\xe1a\xa6z}\x84\xebk\x95\xd0Rg\xfdQ\x01\xc4\xb3\xe0z|^\x0c\x93F8I\x96\xa2R@\x99\xa9:\x91A0\xbc\xe6\xe3u\xb8\xfc\xa5\xceD\x1b\x91X\xd0N\x16\x0d\xce\x97r\x94\xfa\x7f\xbd\xab\xffA\xea\x01\xf5`M?z\xc7\xab\xfa\xd7\xbb\xe6\x1f\x0c\xd1S\xb6\x99\x9aj]\x05J\x88\x92\xa6;\xf5j\xdeA8\x0d$[dU\x1e\x11\xa3\x00\xd7[\xf9\xb0H\xab\xcc\xf1\xaf\xcc\xee\x12\xe3x\xe1ai7\xf8.F\x0c/\xd0J\"\xfe\x84\x9b\x89|Vx\x95\x13.\xe3e\xa1oT\xe0\x92\x16LO\x89r\x0b\x8bE\xb2\x0c\x89z\x9b\xc5\x84\xd3XU\x86\xc0\xf2\x07\x0e\x08\x00\x11\x06Z\xb40\xca\x19\xa8Z\xca\xf2\xb2U\xdd\x12\xbf\xe1\xf2\x1f\xa8)\xef\xb8\x04\xe3\xe5\x99\xb4\xe0s-Xa\x92_s\x92V\xa2\xe5\x8aQ\xa2\xcf\xc0\xa8U9\xab\xa5\x7f\xdf\x86\xeb\xb8\xcbbsY\xaeJ\xd1\xfdqzy\xe5\xdev\xa09 \x15\x96\xdaE\xbf\x7f\xfdv\xfa\xfd\xf4\xea\xf4\xec\xfb\xf5\xd5\xff\x9c\x7f\x05\x96Z\xea\xf6\x03Tm\xecv\nVjtu\xe1\xbcq\xf6\xd3U\xa4\x86\x10\x17\xbe\x06T\xcd\xcb<\x1f\xd7=\xdd\xa0U\x1be\x95\xc6=Y\xb5QxH|\xa2\xcb=c]6Y\n3\xe2\xba\xe0j}1\xc4\xfe\x8b\xdf\xa8\xbd\xd5\x1a\x96\x8b2Z\xa04+u\xa9\x16\xaf\xd2t\xcdp\x17+K1FU\x0eY\xc9\xceNA\xc6\x96\xde\x1f\x82\x08_j.l\xf8\x1f\xed(\xe1\xa5\x0d\xa1(K\x12\x12\x95\xf2z\x98\xff\xb4\xa8\xcd\x92\x19\xbe\xf7\x9b\x05\x0c~\xc1\x84\xa7'\xf7I\x1eF\xe9$!\xd7\x0b\x1c\xb0\xa4G\xb4\xec\x836MH\x18\xb198?\xb9\xb8\xfa\x1f\xf8f\xedt:\xbb8\xfd\x8f\xd3\xef'Wg\x17\xf0>\x97_/\xfe<\xfd\xf2\xb5G\x8f\xd3\xef\x7f~\xbd\xec5\xc6\x97\x1f\x97Wg\xbf\x9f\x9e|\x87w9\xfb\xeb{\x1f\x9cN\xbe};\xfd\xe3\xf4\xe4\xea+\xbc\xcb\xd9\x7f\x7f?\xfd\xc7\x0f\x7f\x0d\xc0F\x87\xf3\x8b\xb3?\xbf~?\xf9\xfe\xa5\xc7 _\xce\xbe_]\x9c\xfd\xf1G\x1fZ\xfe<\xf9\xe3\xf4\xf7\xc0$j!\xdf{\xc9\xc0%\xbc{E\xba\x86E\x81\xe7 p*/\xa0\x81e\xfe\x1c\x8b\xfb\xd8\xfe\x99\xdf\x81\xf0\x12\x99\xfc2\x82r\x7ff\xe8\xa4c\xdd\x0b\xc7\xb6\x8fu\xd4KL&%*H~O#\x9a\xde\xa0i\x95r\x99\x16(Mg\xddC\xc7\xb6\x8f\xe22\x87'\x03\xd0\x08\xd1\xf4\x9e\x14\xfd\xe8\xd0\xfb\xed\xd8\xfaU2\x8a\xa4%-\x97Bii\xda\xa2\xaa(\xb3\x98\xe2T\x12(\xe2\x91\x04S\xfb\x10\xc8\xf7\xefq\xe7K\xbb\x9a0\x13\xd1K\x89O\xf6\x90\n/=\xbf\xea-\xc9\xbc\xc7pz\xff\x1f[\xbf\n\x8e\x8a\xc1\xb8\x95\xcd\xd6\xc9tJ\x13\xca\x8f\x0879!s\x92\x06\nT\xda\xa4\xc7\xb1\xe5\x9b\x18\x8c[\xc08\x11&\x7f6\x95\x83\x8b+\xb9,-\xf3,\x11D\xcfS:\xa9\n4\xc1\xe9-\xc2\x11\xbf`\xe8\x81F-\x93\x8e\xed\x9f\xb5\xa9\xd2\xbca\xd1\xac\xcf\xc9\"'\x05IK\x99\x1b\xa5\xeb4\x8b'&[o\x85\xe0\xa8\xe7\xa6\xad\xe5\xdf\xb1\xfdss->\xcch43\xf8\xa3\xcf\x11jWk\x17\x06%\x88\xa4Y\x19\xf0\xd98\x04\xeb\xb1\xf5\xab\x0d\x15n_\xf2\xe5*\xbc\xebb\x1f@L*\xc8!\n\xa1s6 \xfc,\xa5\xd2\x8db:\xe56S\x89\xf2,!b\x9a\xa80\x93\xec N\xaa\xe9I\x9a\xe3%\x9br\xb7\xd5#\x8bl7\x16\xa6a7\xb5\x0fsV@!\xf2l\xa7r\xe5\xf0\xe0v\\\xe7\n#pP\x17\xd7}L\xde\xe2$\xc9\x1eH\xbc\xa7\xeev\xa4#aO\xd6\xd3\xb6\xf6\x87i\xbd\x8e\xd7\xb8\xb4\xd4\\,,\x17(\xed\xe6\xa9\xcbh\xa9rb7\x0ba([\xc0\x0d+\x9a\xb2J\xc9\x94\xa0s;\xec\xd6n\xd2\x112\xd9\xc7\xb8 \x81\xf1\x17upC\x83\x8b\xa4\x14}\xd2\xc2\xadUG6\x99/\xa1\xa4o\xcf\xda\xeb\x02\x03r\xa5O~\xed\x06phH*w0\x89{\x18\xe7@\x85P6\x80e\xb0B\x90\x1ex\x83\x99\xe4\xcf_\x07\xb1f\x18\xd5\xc1\\\xf5\xb16S\xcf\x92'pjF+w2b\xb1\x93\xa0\x8eB =\x85z\x979\x01\xad\x14\x04\xddH\xa8\xd7D\xa0\x1e\xe5M\xf4\x0f\x03\xf0zi14\xb4\xb4\xc9\xa6q-\xa4\xdd\x02\xe0\xca\x9e\xa5#\x00\xa5\x15^\x19\xff\x86\xe8\xbe.\xa0\xfeEL\x86s=\\\xc0\x04\xcc\xe4Ux\x07Q\x07k]}#.\xb6U\xf8\xb0r\xc1\x12\xc0\x92\xec2%\x8c\xb1\xe5\xa4\x1b\xaa[b\x81Rt\xee\xaf\xeb\xba&\x80\x1a&\xaf\xad\nI\x01\x8a\x88\x0b\xae\xab\xf0\xec \xdb;\x14\x11N\x91\x0c\x98\xe0\xd7Tl\xeaw\x10yw\xe3\xda\x141\x99~\x9cD\xfbx7\xfa\x1c\xc7\xbb\x1f>\xfd\xf6a\xf7\xf3\x87O\xd3\xdd\xa3\xc3\xfd\x8f\xe4\xe3\xfe\xc7}\xfc\xe9\x83p\x98\xc9\x1d\x1a\xd0\xa5\x0d\x8c\\A\x17R\xb5z\x11S\xe30\x80\x07w\xfb\xfb\xfb\xf1\xfe\xdd!\xf9\xf4pT\xe0\xe5\xd1{<\xbd\x8bI\xf1\xb88|\xba{\xba\xcd?LSg\xce\xf2\xa9H\x14\xc0I\x91\xa9\xabD\xcb=~\x13#\x07\xa8\x9cD\x02\x9b\x99\x13\x9b\x87\x0f\x87\xf1\xdd\xe1\xcf\xf8~\x1e\xe3\xa7\xea\xe1)\xc2q<\x9b}\xba\x99Ww3\xf2\xf4\xe1\x83\x8dZ\xf7Ie\xa4Ub\x1e1R\x19\x86h\xb8A\xca\x0c%Yv\x8b\xaa\x85\x8f\x892CG\xb8\xa5\xa6]O\x97\xb4\xbf\xc4\x92sG\xa75\xec^\x8b\xb1\xe5E@%\x8a{\xc7\xf7:+\xc0&\xde\xb6\x98\x90\xfe\xe5\xdf\xbc\x98\x90\x1f+\x8b\x8e\x04%\xc4\xb6\xc0\x98\xe9\xb1M\xd7\xf263V\xb4mf\xec\x06e\xc6\xb6%\xafk!\xb6\xb6\xcbPse\x1c#\xc5c\x9a4\xd4\xff\x8a\xa6\xc7\xca\x06\xc7\xc8f\x06M\x8f\xd1\x02\x97\xe6\x83^u\xeeP\x99W\xc1\xdc95\xe9\xad\xd8$\xdfD\x0f\xb58\x06\xd8\x19\x80\xb9vX\x17\xbdm\n\xcf\x05\x99\xc5\x92\x18\x92\xb4\xe5\xe0\xfc6\x11\xfd\xef\x99\x88\xee\xca\xcd\x0b\xa7\xe6\x15{8\x91\xda\x04\x98\x8bW\x9c$ \xcaI\x99SrO\n\x84\xf9\x7fuW}\x01M\xc03\x8a|\xbc\xaa$\xbc\x0d\xb1\x08\x1a8\xf9\x15\xfb\xca\xcfy\xc0\\\x16\xa2\x01l \x04\xf7\xbd\x8e\x9c\xca\x17\xce\x1e\x03\xa2\x1f\x8a\x01\x11m\xac\x94\xbe~I}\xe1\xb4>\xff\x8a\xad[0\x00\x18\xb4\xc2D\x83\xac3\xd1BS$\x1ap\xa2\xe4\x8f\x87%\xf9I\xbb{}\x08\x05\xfd\xc0h\xbcd?x\xba_\x8f\x84\xbf\x9ed\x03w\xbdh}xT\x8e\x92\xf87 \xf5\x0f\x96\xfc\xd7\x93M})\x1f/\x05p@\x12 \x1c\xddq\x13\x01\xfb\xa5\x02\x06@ <\x80\xc9\x80\x90t@(W\xfa\xa6\x04\xfa'\x82\x89\xb5\xd5\x92\x02\x81\"\x0f\xb8\x9c\xa1<(\xc7J\x0e\x1c\x9e\x1e\x08L\x10\x84\xd2\xd4?I\xd0\x0bnB\xfa\xa6 \x82\xe7\x08\x92\x9d3$\x9f\xce\xde\x13\x90.\xe8N\xc9\x1a\xd4\xc9\x9f2\x88VK\x1a\x0cy\x7f\x9bmp\xe2\xe0(\xa9\x83\x03\x92\x07}3\xf8\xb2\xe9\x83\xee\xf9~\xf6\x04\xc2\xc0\xd2\xdb\x94\x14BX\x12a\xcf4\xc2\xd1O\x12Aq\x05\x11X\xee\xf4\xad>\xdd\x80)\x85\x8eD\xaa>}@i\x85\xaeD\xa7>\x9d\xc2\xa9\x85\xae\xe4\xa2>\x9d \xe9\x85\xced\x9e>\xbd\x80)\x86\xae\\\x18o\xa7\xe1i\x86\xfdt\x82{\xad\x0e\x1a\x1a\xa9T\x9e>\xc9\x86\xce\x85?f\xba\xa1c\x9f\xac%\xe1\xd0\xb1\xbfFK9t\xed\xc5gM:\xb4\xed\xed\xb5\xa6\x1d\xbad\xc3\x1a\x13\x0f\xed\x92\xe5\x05R\x0f\x9d\xf2jc\x92\x0f\x9d\xb2\xf1e\xd2\x0f]B\xf79\x12\x10\xa1\x87\xb4Q\x93\x10{\xa4!\x8e\x94\x88\x08!s@2b\x00\xd6\x8a\xe9\x88p\xed\xd8\xf1y\x97\x03S\x12\xbdI\x89=\xd2\x12\xe1\xa8[@\xd6\x11\xb1\xfd2AVIN\x04\xb9\xe7!\x8e\xf9&=\xe1\xe3@\xd0\x8a\x07:\x82\xa1\xfcF#\xa6*\xf6\x88\x01\x14\xad5\xd9\xe1\x14\x8fM\xe1Q(\xb1#\x18X\x0eM@j\x0d\xdb/\xc6~c\xb85,\x8d#\x98\xc41\x94\x8b\xa04\xc6\x0db\xdf\xf6=\xf7\xd5\x12\x1b\xe1\xdb\xad\x93\xed\xb1}\xcf]7\x88\xb6C\xbd\x13\x1d\xc1\xeb\x07\xc1\xb7\x1a\xea91\xa8G\xc2#\x18P_]\x88\x86&=n*\x07C:2\x08\xb0\xec\x99\x80\x06J\xd6z\xa5\xbc\x1c\xa6A\xbb\xa0\xfa\xa7B\xf6K\x86\xec\x9f\x0e\xd9\x8b\xe5\xab\xf11\xa8J\x82\xf0\xb6\xef\xb9\x07\x00\x02\x13$ax\x8f\x93$\xb9r\x9a$j\xe7CF\xdd\xc8F\xc7\xc3\xef\x8e\x00O\xd5^[\x02\xe66sK\xff\xf2o\x9e\xb9%\xda\xf6\x19\xf8N\x03\x89n\x90\xe5\x00c\x1e\x1a\xf6*\x9a\x07\xda\xf6\x19x=?\xa3=/>\xe4\xbd5\x0f\xb8\xed3\xf0\xa2\x8d>O\xc3^n\xf3\x00,\xb7\xcf\xc0\x9b\x0d>\x13}\xdf}\xf3\x80\xda>\x03\xbf}\x06~\xfb\x0c\xfc\xf6\x19\xf8\xed3\xf0\xdbg\xe0_\xdd3\xf0\x9d\xc4\xcfQK`\xf0\xcc\xd4m\x19\x0c\xd1\xb6e0|\x1b\xf3\x99\xcb`l\xf3\xf2\xff\x9ey\xf9h\xfb@|\xaf:\x16\x0e\xf3c\xfb@\xfc\xb8\xcc\x0d?m\xbe} ~\x0c.n\x1f\x88\x87\x1e\xf8\xd1\xf6\x81\xf8\xbf\xdf\x03\xf1E\x94-\xc8\xde\xbf\xf8\xff\x05\xde\x84\xbfd\xbfA\x05\xc1y4S\xa9\x11\x88wT\xab\xc0\xc5\xfe\xab\x19\x11?D4\xdea\x1c\x97G\xf6xG$\xfd\xd5U\xc5\xc4\xaf,\x95\xc5>\x1f|\xfe\xed\xd3\x04\x1f\xee\x1eM\xdf\x1f\xed~8\xfa\x8cw?}\xc4\xbf\xedNI\x84\x0f&\xfbG\x07\x87d\x1f\xd5\x18\xe9\x92]\xf5\xdc\xf0ja\xfc\x8f\x07wO7d\xff ?\x95\xd5\xc7\xa3\xf2\xf1\xe8\xf1(I\xee\x8f\x1e\xa3\xcfOeq\xf7\x98\xdc>\x90\xe4\x9d\xc0\x99\x14\x85\n\xb5\xd91\x8fv-\xfcy\x86\xa0\xba\xff7\xfat\x11\xd0\xff!\x7fvp\xf7\xe8D\xe5sq\x97D\xb3\xf7\xc5\xe3C\xfa\xe1\xc3\xcf\xa3\xfd\x9fO7\xe5\xa7\xbc\x98\xdd\xdf-\xa7\xf9\xcf(\xe78j\x80\xf2\x12Y\xa1jC\xb1FE\xe2*\xaf\x94\xed\x15^\xc5\x1f\x0f\xee\x0e\x9d\x08\xba\x0b\x9b\xbd\x9fGO\xe4\xbd\xb93\xfe\x8di\x10.\x87\xe4\x0c\xc9\x88\xa8\x1aM.N\xe5*)\xf4\x0e|\xe7\x00!\xd8\xa7\x030L0j\x15\xc9\x1b}au1\xd8\xa2\x8f\x86\x17\x1e\xc5\xe0)t\x10\xc9R\xc3\xd5\xe4\x1a\x83\xfb<\xb8\xb9\"\xcd\x13\xc5\x96\x9d\x06u\xbc\xf8B\x03\x13\x9c\x13\xcbBd\xbf*gd\x89\xe2,}S\x8a\xf0=.\xe4T\xa2)\x07o,G\x8c&86C\x01\x0cD\xf5\x8f\xce9|\x9e_\xd0X\xd9\x88\x9f\xce\xf5\xeaAqFD\xc2\xb00D\xcaz\xf3\x14\x8d\xe2\x11\xa2O=\x16z\xcb\xe5/\xc7\x81\xc4\xbf\x1a<\xba\x9ae\x051\xafh\x99t\xe6\xf3\xa2Ry\xa6T\x90\xacdP\xdd\xf7\x1fKe\x99\xec4\xd0\xd0\xc3\x0b\xb7S\xa9t\xb397\x97D\x7f\xbe6\xfb\xeee\xb9q\x14\x12`\x94\x0e1\x0e?\xaaK\xcdf\xd1UvQ\xdb\xabQ\x90\x8b\x0b\xd6_\x14\x836\xb4\xfc\x16gr\xc0S\xabw\xaf\x19\xb7\">\xba\xae\xfcWp\xb2:0B!\xa8(\x08\x19)\xe8\xcf\xf4\x06T\xc8\xff\xc8\xda\x0fQv\xeb\xf4wT\x97\xdb\xe2\xeb\x1e\xa1\xd3\xf9\"\xe1\xf9{\x05*\xe2\xdbw'B\x9c{`\xd1\xb4$\xf9\x14G\xc2\xb0\xac\n\xc2l\xbc\\+M\"6\x88\x91I\xe4\x81\xf5\x85\xdb9.&\x82\x8b\x95=/3k\xf9\xdd\x8c\xe4\xd2\xc9\x89<\x12\xcc\x7f \xd8\n\xd7\xd2\xc5R\x88\x9a\x0b\x99\x91\xc5\x13.i\xb0\xaeXa\x08\x81v\xe3\xb9\xa1\x9e i\x9f\xfbO\xb5`\xd6\x7fp\xcf\x88\x16\xde9\xa2\xc9\xb5\xe4\xff\x11p\xde\xe5O\xc5\xc4\xb62\xb0d\xc6(z\xabr\x11\xfd\xb9\x90y\x96\x04\xeb\x87AV\x90h\x98\xc3\xabw\xa4BFO\xb90\x10\xc8c \x0dO^\xe4Y$\xb6\xa0\xb7\xaa\x9c\xec\x00g\x1e\xa4P\xc3\xe0R\x0dC\x8b5\x0c+\xd70\xac`\xc3\xc0\x92\x0d\x03\x8a6\x0c,\xdb0\xa8p\xc3\xd0\xd2\x0dC\x8b7\x0c*\xdf\x80V*\xe0\x10\xba\xd5\xea\xb6\x91\x8b8\x0c,\xe3\xf0L\x85\x1c\x9e\xb7\x94\xc3\xfa\x8b9lH9\x87\x17(\xe8\xf0\"%\x1d6\xa8\xa8\xc3k(\xeb\xb0q\x85\x1d^\xb6\xb4\x83\x8a\xaa\x0eY\x16\xb0\x10.\xd1\xa0\x81\\\xa2I[M_C\xea\xe9\x7fS\xa0\x82\xde\xa4\xb8\xacr~$U\xa8z\xe0\xc1\xec\xbe\x13Q\xa8B\x89ki\x8d\xb2-\xb8GS&\xf08\x17\x99]\xe8\x05\xd3\xae\xa9\x88\xf5Rtv\x83\xeb\xc1+\x9e\xb0\xa1\xcae\xe8\x8d\x80\xcal\x81\x12rO\x12y\xa4\x08\xa7\xe4(W\x87\xb0h\xdf\xb5As\xdf\"\xe33\xc2\xe9R9r|\xc2U\xd4\xd4\xccb:\xf5\x86\x0c\xf2H9v\x9a2,iu\xdc\x15\x184\xcf\xaa\xdeuZ\xa5\xecl\xc7\xb9\xac\xf0Nh\xc18\x9f\xa5\x0dW\x95+D\x91ar\x8d\xb9\x97\xe5Y\x0ea\x01\x8b\x1e\xbe\x0eN4\x93\xe4\x01\x94\x13\xce9\x86\xabr\x96\xe5\xf4I\xc8\xcf\x9cD\x84\xde\xfb\x16B]\xcc\xd6]\x12\xb4q\x19\xd0n\xfcz\xf0\x9a\xaf\xbb\xeb\xe0\xe1pl>\xd4\x1b\xb5\xa9\xaaJ]\xe8\xafE\x90wm\x1a\xbe\x97\xcb\x12\xa71\xcecS\xbdH}'\\\x8es\x9c\xdfzk\xd3\xaa_{c\x87s\x82\x8aj\xb1\xc8r\x86\x9f>nr\xcc\xf9\x8e`\xc2\xa8,s:\xa9J^\xb3\x90{J=\x00'\x84I\xf7\xf4\x86\xc4h\"\xa2\xfa\xa4NP\x8a\x94\xed\x8d\x88\xd9-\x96\x8b>\xb3I'\x11\xafN\xb8\xbc\xce\xb3$\xa9\x16\xa1i\x0dIv\xa8\x06\x80\xcf\xff_R=\xe0$\xd1\"@\xed\x08y_\x94\xc6<\xd4\xae\xe3\xbf\xed6u\x9f\xa2\x84j\x03\xd0\x9bB\x89\xd6)%I\xec\x0d\xcb\x16\xd3\x96\x14\x19\")\x9e$\xe2\xcc\xcf\x03\x15\xa4\xae\xfaw\xee]\x16\x08\n\xa8\xbe5\x92\xc6\xdaE/i\xb4O\x1a\x8cm\xe2\xb6\xaf\xae \x9dgYiT\xc1\x14\xb7\x7f\xcd2\x98RW8\x002\x02\xf8j\xe3\xd7\x1d\xb9\xb8\xff\xf0 \xaa\x1c\xb0\x9e\xc4@\x18%\x0d@\x81\xd2?\xcd\x12?\x0e\x80\xc6\x9d\x83\xf5\xcf!GZ\xd8\x85\xf6\x9c\xce\xe7\xae\x07\xdfW\xb2\xa7q\xbb\xea\x12\n\x9a\xe3\xa1j\x03k\xa6\"T0\xa0\xa6\xc8\x93r\xad\x81\x89\x7fl\xde\xeb\xfe\x16\x04\x15\xc5\xc6\x17p\x8a>\x8c\x1f\xfe4|\x10\x1b\xfaR\x18L\xb6\x1f\x8b\xb6\x11\xa6\xb8/m+\xa5\xcd[\x16\x80[\xa0\x16\xc1:40\xdc\xbb\xd0\x02\xa2\xd5\x01\xa7[S\xcdr1\xb3fA\x1b\xac+\x03\x9a\xf3\xb5l\xeb@\xf1\x18\xef\xb5\x11\xea[?\xad9\xa7/$\xbc\xe1L\x81\x88w(,\x18K\x8c\xff\x1aAH\xac\x957-T\x9b\x1aA\x7f\xf6\x80\xb3\xaaI8\xb7^@A\xc0J\xb2\x8c\xbf0FX\n\x83H\x1dAi8VK\x9bhu*\xeaR\xe9\xf3z\xac+\xf3Db\xe3byP-\x84G@\xf5(\xcfXc\x13\x1e-1\xee\xb8}\x96\x1f\xaa\xdf\x7fk\xd7.\xb2\xc5R<`\xffM\x8b\xf2\xdf\xcb\x07q\xc49Z0\xde\xb5\xf1\x10z\xfe\x97\x10\x82Q\x11\xb0%%Z\xd0\xf9\xa5\x1ap\x8e\xe5\x8f\xc7\x88\x8e\x80\xc5G@=\xe5\xa2\x8d\x1b#1 J\xa2'#a\x91\x12+\xc4J\x0c\x8f\x96\x18\x1a/14bbp\xcc\xc4\xa0\xa8\x89\xc1q\x13\x03#'\x86\xc7N\x0c\x8f\x9e\x18\x18?\xb1j\x04E? /\x9a3\x8a\x02\xd0\xd7\xdesh\x1c\xc5\xb3ER\xcf8^6\x00\xf5%\xcf(o\xcd\xb5\xc0\x0d\x8b\x88\xb0\xc6D\xf8\xcdA\x80\x0d\x05\xb1\x9f\x9e\xff\xca\xab\x1d\xb8\xa5\x11p\x07<4\xebk\xb8-Kc.B\x91\x0ck\xa5) \xe4:h\x0e\x0dg0A\x81\xb8\x02 v\xda\x10\xcet\x83\xa4\x8c/\xc0\x07\x80\x8c\xc5\x03\xe6\x90\xfc\xe7\xeb\xe0\x91\x81l\xed\x900\xbeI>y\xe157\x19\x88O\xfe@\x0f0s\x86P=B\xacG\xbf]3\xd2B\x18B\xebJ\xc1\x1e\xce\x85\xe2!Y\x05\x15\x8e\xf9`]\x03(r=T'#\x00\xbd\xd1I\xc8\xf2@\x1dd&A\xba\x12\x01\xf5%B\x90Pm\xd1\x80K\x03\xc1\xe5\x04ZO\xf0\xb6hz\xa6\xc2\xba\x13=\x0fu\x80\xe3L\x0b\xe9\xe1\x9a\xd4\x04\x06\xe4\x13L\x9b\xa2\xcd\xe3\xd5\x18\xba\x15!#+\x02\xc8\xb3\x90\xde@\xfdX5\x8c\xfeQ4\x08\xea\xb9ZF^$\xc3(_Q\x9f \xfb\x12\xb2\x93\xdfy\xd1m\x8c\xb3\x96\x15\xe8x'.{T\xdb\x9a\x0f`m\x9aF\xb05\x80\"\x04\xcau\xd4\xe3e\xd3\xf0\x85\x16\xea\xb8.\x82\xca\xa93\xeda\x05\xb59\x9cZY1\xb5\x01\xf6\xe0S\xd4\xeb\xd1\xc8\x0d\xe2Y\x17q\xc9\xbf\x1e`,)P\x83y\x19R\\@\xd6\x0d\xe7 Diy\xc1\xadB\xfdH\x0bg8\xf5+*.\xc0\xf2\xb2\xb1\x02\x82\xaft:\xba_\x14\xd5\xbe\xab\xc6\x83\xa1VX\xbd\x1f\x11\xb5\xe2$\x07,D\x10\xc5\xd2\xa80\xad\xab3\xea_4\xd2\xa1\xdfZ\xdf11\x0bu\xb6\xff\xc2\x9d\xb7\xdd\x85!\x96\xc3\xf3\xe6\x07\x08d\\\x8bt$U\xfd\xec\xb1\x18{\xf5\x92\xa8\x83\xa9\x05\xa9\xef\x10\xfa\x93\x171\x90\xa9\xe9^X2~\xdfRX\xe3$)2t\x9bf\x0f)\xc2l\xd5~c\xe6\x8f7H\xe3%\x1c\xc60\x8e\xd5\xe9 j\xd3\xa9\xdb\x0e\x95\x97\xd0\xca=\x08\x08L\xcdk\xf4\x96\xd7\xcb\xa4\xe5\x0cMiR\x92\x9c\xc4\xe8\xf6^\xe9\xf5\x92\xe4\xb8\xccrwp\x99\x8c\\\xf7\xb2\x0bD\xa0\x04\xa4\xa4L\xc3\xdeU\x94\x89\xa9\xf6\x16\x82\xd0\x92f\xd9\xbc\x83\xcax4_6\x9d\xca\xb8\xb9\xe6\x8b\xc0Pf\x05\x17\xc5hn\x18`R\x05p)\"\xf04 \xd4\xb9W\xef\x84@\xf2\xe9P[6\x08MN\xac\xf7w3\\\xcc\xc6'\x95\xd1\xc1 s\"R\xa3\xe2\x8bZk\xc1d\x8b\x90PD\x83\xf0\x02L\x01\x16\xe1j\x96\xbbk\x8d\xfc[6\xf05\xfb\x99?v\x895\x1d\x9e\xc66Ak6\xa3l>\xcfR>\x9e?\x86T\xbc\x80\xf5\"\xec\x10C\x8b\xf04EF}W\xca_\xeb\x08^)\xab\xa6KU\xa3\xb7\x02\xec\xaf\xf5\x15-\xe7\xd3\x9ef\x8d\xf7\xaeV5&ziz\x9f\xddz\xd6\x12M\x17U\xf9j\x13\xbc \xbb\xa0\xd7\xc4CL\xbdv\xfb\xce\xa6]>\x1c'\xab\x9c\xb3\x89Ohz\x8b&8\xba\x95\xb5\xe1\x01\x90x\x8c\x02O\xd5\xe0\x8b\xc6\xef\xe1\xd2\x0f\x11\x84]\\=Y\x004\x0fD\x83o\x15\x8b\xf8\x96\x8a\xbe\x8e\x19\xff\x06J\xb4\xf8Z\x94x\x92\xd0bFb\x15\xcc\x10\n2\x87\xc8\xf1\x9el\xeaK\xb9]\xe0S\x12\xf1D\x04\xc3\xaa\x00\xc0c\x8cB\xe7y\xb6\xc8\n8\x0f\xb4X^\x0f#\xf8\xeb\xfb\x0b\x85\xd3\x94{\xf4\xca\xbc\x8ax4\x11\xb7\x91\xe68/f\x81\xa0u\x84\x8a\x12\x97Up\xeb\xf7\xe3\xff\xa9\xceu\xa1Sa6q\xc1\xc7E\xa4Z\x86\ni\xd0\x04\xe84\x8c\x9f\x15\x8f\x1b\x121\xf2l\x8a\xf9-\xd9\xa2\n\x8b\xe7^<\x86\xe7J^|\xfdrv\xf1\xfb\xf5\xe9\xf7\xf3\x1fW\xd7\x97W'W?.{\xe5\xaf\xb9`\x9c_\x9c\x9d\x9f]\xae\x00@|\x0bv\xd7\xf9w\xab\x12\xd2_\x92\x07\x99\x07\x99\x81\x00\x08#\xed\n\x90\x10\xc2\xa3\xbdpB\xe3\xbd*\x15\xe7E\xb1n\xd9\xda\x01t\x0eL\xa5\x9d\xc7\xea\xaf\xed\x041c\xc7\xa4\x08\xe7\x13Z\xe68_\xd6\x12\x8cW+\xd4g>\xb1\x15\x86\xe3(\xbe\xd91\x14\xdf\xec\xf8Q\xb1\xa1\x1b\x16\xd8\"'\xf74\xab\n\xfe6Ws\xab\x1bIU^\\\xa5\xb0\xb9\xcaqt+?\xb9\x84\xa5\xd47\xbb]\xfe\xd7\xe9\xf9\x80n\xdfNN\xff\x08v3t\xf2P:\x87ic\xc7h\x90Iqvnh`T\xa5\xcd\x97U]\xadg\x16}wJ\xdb\xccc\xdf\x1a*\x8c\x18\xbb\x89?J\xae_\x9b\x1a2$[\x0e\xed!\xd97cHM\xd2^\x94\xa5\x05\x8d\x95\xf7\x81\x0f~K\xf9\xadE,\x1e\xdd\x9a\xd3\x82\xbf\xf4*\xf5Q\x96\xa3\x98$xIb`b\xa2\x03I\xb6\xf8\xdaH\xb2oN\xbe\xd4\x17\x8aV\xcc\x99\xccp\x05\xa0\xab\x06\x15z\xe2Pu&\xe4\x13I#\xbc(\xaaD\xa3$\xe5\x16?\xc5B\xfc\x88\xa8\xb6<\x009\xc90\x1c\x05\x0eo\x8b_Q\xfd\x9e<\x7f^3\x9b*\x96\xc9w\x90\x95\xb8\x0e\xcc\x12\xfb\xa98\xc8\xa8\x0eu\x91b\n3\x1d^Ge\xa56\x96\x8a\x81\xf5]\x06@\xcf\x16\xd6\"L\xd6BK\xf2\xf2\xc8\x01.<\xdb'\xfaJ$\x9br\xcb\x95O9.K\x1c\xcd\xc4h\xf5\xa3\x81Y\xce\x13\xa2\x9c\xb0\x9a;F\xaec~v\x0f\xa6k7X\xac\xdf_\x14\xfb!\x95\x0e\x0d\xf9Y\xe68\xd9I\xd6\x8e\xabQ\xc2\x93\x9a\xd0\x02qIF\xfc\x91\x13`c \xac9\x0e \xe4\xc3[\xe3F\xd1\xe3;C\x8b\xe4\x84\x06\xc3\x85\xeai\xd8\xe48\xa16\x96\xc3\x03\x844$\x08K`\xe1\x88\x9b\xc0\x961\x82T\xebe\x03d\x8fp\x16\x04}\x93\x9b\xc0\x1f\x03U\xc5 \xe9\xf5\xe3\x9f\xd6\xc2\xa1\x17\x88\x832F\x0e\x05@\x8d\xb1S^a\x0e\x04\x80*\x90\x96@@M\x81\xb6)\x10\xb2=\x03u\xc1\xd3\xc16\x05b\x10\xaf\xc6\xd0.\x08mS \xe0\x94\x8f\xbcH\x86Q\xbeb$)\xea\x93\x02!\xe5\xf2\x98 \x10\x16\x90\xe3\xa5?H\xfb\xfa9\x93\x1f\x9a\xf4\x8c`Y\x00E\x07\x94\xdf\xa8\x83c\xe0t\xe2\xdf\x07\xfd\xd2\x1eZ\x93\x1dVI\x9b\xc2\xa3\x95\x15Q\x13\x1c\x98Ck\x89Z_?\xb7\xd6\x95\xec0\x90\x8b\xaf\xe5\x08d\xc1\xd7r\x0e\xf2B\x1a\x81a!}\x0ed\xd3P\xfaG\xd1\xe4P\xba;y\x14c\x9e\x91\xac\xc0\x9dg%\x10(.p\x11\x8d\xddg\xa6 \xc9 M\x87\x80\xda\x0e\xf5L\xf7C\xf0\xf5\x83\xe0[\x0d\xf5\x9c\x18\xd4#\xfd\x0f\x0c\xa8\xaf.D\xc3\xd2\x00\xd1\xc6r0\xa4#\x83\x00\xcb^ ]\x08\x96\x06\xf5Jy9L\x83vAY\xb4hO\x08\xfd\xe6 \xa4M\xf0\xe1\xee\xd1\xf4\xfd\xd1\xee\x87\xa3\xcfx\xf7\xd3G\xfc\xdb\xee\x94D\xf8`\xb2\x7ftpH\xf6E\x91w%\x13\"\xf7\xebsR\x8d\xcb\x01}\xb8\x1e\xdc=\xdd\x90\xfd'\xfcTV\x1f\x8f\xca\xc7\xa3\xc7\xa3$\xb9?z\x8c>?\x95\xc5\xddcr\xfb@\x12\x1b\xc6\xe1\x02Tc1\xb4Un\xaa&\xbfY\x8b.@\xaa\xf8\xf1\xc1\xdd\xa3\x93\xd8\xcf\xc5]\x12\xcd\xde\x17\x8f\x0f\xe9\x87\x0f?\x8f\xf6\x7f>\xdd\x94\x9f\xf2bv\x7f\xb7\x9c\xe6?\xa3\xdc\xc6\x85\xe0e\xccHLh]\xbdh\x1e(\xc9\xd2`\x81\x0f\xc4\xc1\xdd\xa1\x93\x01\x0f\x1f\x0e\xe3\xbb\xc3\x9f\xf1\xfd<\xc6O\xd5\xc3S\x84\xe3x6\xfbt3\xaf\xde\xcf\xa3'\xf2\xde\x06Yf\xb7\xa8\xb2V\x0e\x93[p\xc1W\xf1^\xdb(\x9e\xdf\xc0X\xd5\xc6H\xf0k\x9a\xe0\x1b.Qu\x1d\xfdL\xfd\xd2\xa3=4\x8c\xc6\xfd\x85>\xb8\x14\x8b,-\xac\xf1\x0c\n )h7\x87+\xa6j\x18\xc8\x14\x05\xa2/O\xc8\xa3@\xc1{Z}N\x9e\xb4\x10r\xf3D\xfe\x90S\xe8\xbc\x13\xe4 x\xccL\x88\x11\xf5\\8\xf4'zfF\xb4\x10\x82,\x0eg*\xad\x86\x91\xa2eV\xe5R\x917\xd9\xd0\xc0\x8a\x17\x9a\xbd\x90\xecBu\xd4\x9a\xf8o\xc6\x06\x19\xe9L\xd0?+\x92/\xf7De\xda\x8b\xf3/2\xd1\xb2\x86\xadBR\x7fq\x0cu\x92\xa2*%\x8f\x0b\x121CC\xbc\x84\xa4F2\xfa\x14\xd1\x8c\xccqsZ\x9cf\x87\xdb\xe4\xe0\xf0\xbb\x93\xeb\xd1\x08Q\x16[\x1c}\xa2\x83\xab@\xb7Z\x054-\xdf\x1f\xb6\xfe\xea,n\xed\xc1!&%\xa6\xc9\x86\xd4\x92`\xdd\xaf\xab\xdc\xf9\x06KP\xbb\xf2\x14\xcc\xc1\xbd\xadG\xe7\x05\xce\xf1\x9c\x94$7p\xde\x15Y\xa7\xda\xf8s-\xc0\xd6\x0e\xeck+\x0e\xb3\x10}\x06\xd2\x10\x0b\x90\xa6\xc7h\x81K3\x9a\x90mz\x9a\x93\xf8\x18\x95yen%+\x8b5\xb7\x0c\xcb\xce\xc5\xb1\xffmq\xac\x9f1h\xe9:\x96\x05\xc8\xb8p\xc7\xe4\x91\x95\x0dS\x9c\x14`>\x18\xc6\x1dp\xe1\x0c6\x07\xc75\x02G\xe4A\xdbj\x032b\x90\xb1g\x9au\x0dh sf\x08\xd1]\x15\xedR\xdem~Hc\xab';\xfaXy-{\xae+\x1e^\x9e\x19-C\x0d\xc8\x8c\x81\xe6]m\xc8u\xd6\xc6\xcbq\xa0e\xa1\x0190\xc8\xae3,\xb8\x060\x875\xb7N\xfaK|\xd3P\xb2\xff\x94\xe3\xbc\xd9[\xe4\xd9=Iq\x1a\x91\xbd9)q\x8cK\xbcw\x7f\xb0\xc7\xd7\xeb\xde\xbf\x94^\xfd\xbf=\xb1\xb6\xf7\xfe\xc5\xb8\xf8\x7fo\x04\xac\x1b\xa2-\xc4\xa2\x9a\xcfq\xbe<\xd6\xcf\x1f\x14\x04\xe7\xd1L\xbe\xad(7\x86\xa2\xd6\xc5\xe9\xab\xfa\x12\x8b \xde\x1dD\xa7\xea\xd9\xc6xG\x15\x85\n\xc8h\x0dl\x04\x01\xcd\x10\xd2\xf08+v;&\x86\xd8\xd8\x86\x99\xa1;\xc0m\x0c\x9bq\xd1\x18\x16bW\xa0K:\xa7 \xce\x93\xe5N\x8dC\xd9('m\xb5\x8exi\x18\xbf\x03h\x0c\x85\xdf\xe0\xa5y\xaf-\xea\xba4\xa2u\x96Y\xf5&'\xf5\xf8\xccb\xcf\xb9\xe7\x14\xd1\xd4|\x98\xe3\xdf\xd0\xe9\x14ei\xb2\xd4e\x06\xb4&\xaf\xd7\x8d|\xbb\xce\xf4\x1f\xf3\x97B\x8c\xfab9)\xab<\xe5\x8f~\xd8@k\xeb\xb2\x01\x17'I\xd3\xfd\xcb\x86\xe13\xd9\x17|=Ao\xd9d\xc8\xd1\xf6\xea\xef\xbfZGk\xcf\x8f\x1a\xd79\x1e\xd6\x8cWT\xe8g\xda\x14\x858\x8d\xf7\xea\x05qM\x9b\xfckm.\xdb\x80\xfa''h\x82c\xd3g\xad~\x84\xe8\xf4\xd8D\xec\xaa\xb9F\xb9h\xe5\x0b\x13\xa71J3+\xf7\xdf\xb5\x00\xc87X\xca\x87\x8c-g\xfe\x0eK6m\xca\x12\x93$~\x7f \xc1\xf2\xff27\xcc\x12\xc5Y\xfa\xa6\x94\x1c\x9f\n\xb9\xce7\x12c\x1e\xef\xd6\x18\xfe\xa4\xc3\xd5\x1d4\xa9J\x94f\xa5\x85\xb3\xb8Q\xde\xbd\x0bF<9W/eF\x96\x9e-\xf6G\xbdg\xe2\x8c\x14\x0c\xcf9.\xa3\xe6\xf3FF\x7fsF\xfe\xb1T\xe7\xfa\x1d\xc3@\x13o\xe3H3N\x9c\x06\x88\xd0\x0fQR\xf2\xf4w\xa4+\x02\x88\xed\x8e\xd0\xe9|\x91\xf0g\xd3\nT\xc4\xb7\xefN\x02InRgMq$|n\xbc\xd6\xa2\x10S\xa2'\x11{M\xd9wm\x13\xb1\xd9\xbed\xc5%T\x1b\x80\xde\x14J\xb4N)Ib\xcf\xeb\xf0j\xda\x92\"C$\xc5\x93D\x9c\xf9\x99HV\xba\xea\xdf\xb9\xabZ (\xa0\xfa\xd6H\xed+W4\xda'\x0d\xc66\x11\x7f\xa7\xdf\xceGy\x96\x95Fu]nI\xa3(K\x12\xc2\x0d\xa7\xfaJ\xc3\x85##\x80\xaf\xb6,%\xfa\"\xc4\x83\xa8r\xc0z\xc2Ha\x944\x00\xc1\x8b\x84\xb9=6\xb52\xb2\xfe \xe8H\x0b\xbb\xd0\x9e\xd3\xf9\xdc`\xa2y\x9fU\x0e\xae\xed\xa29\x1eJ\x18\\3\x15\xa1\x9c\xbf\x9a\"O\xd6\x94\x06&\xfe\xe1O\x98Z\x13A^\xe1fA\x10\xf9J\xac\x84\xfc\xda ~\xb8s!\x10\x94\x0d})\xc4\xa1|\xb9\xb1h\x1ba\x8a\xfb\xd2\xb6R\xe6\x9be\x01\xb8\x05j8\x95\x1c\x86{\x17\xda\xb0\xc2(\xdd\xb2(\x96\x8b\x995\x0b\xda`j8h\xce\xd7\xb2\xad\x03\xf9\xdf\xdek#\xd4\xbe\xa5\x82\xc9mh\xb6\xf7\x8b3\x05\"\xde\xa1\xb0`,1\xfek\x04!\xb1V\xde\xb4Pmj\x04\xfd\xd9\x03\xce\xaa&\xe1\xdcz\x01\x05\x01\xcb\xaa\x1e\x7fa\x8c\xb0\x14\x06\x91:\x82\xd2p\xac\x966\xd1\xee\xbc?\x9f\xd7c]\xf9\"\x12\x1b\x17\xcb\x83j!<\x02B/\xf1\xee&\x88\xd6H%ba\xb4\xf1<\x9e2\xab\xd9g^\xf4\xcb{F\xd8\x13`\xd2\xbc\xdfa\xf8\xd1\x08'\xc9\xb2~\xfc\xdd\xd9Qv\x1a\x81\xdc5\x9c\x81\xd5\x81E\xa4\xdaR\x92\x88Ky\xa6\x91\x99 \x10%\x94\xa4\xf5y\x98GLx\xe1u\x97h\xf0\x10\x8c\xab\x98z\xb9\xd3\x87\x9e/\xfc\xf8\x1d\xa3\xc9r\x07U\x8bX\xff\xbb\xa4sR\x94x\xbe(v\xb4\xfbL\xd4\x99\xf3?\xce(\x12\xc5\x12\xf9|\xd84s\x93\x01>aC\xcf\xd7\xc2\x93\x10_3\"B\x82\x13\xb8\x80\x90\xb1\x88\x18\xd8]\xc6\x96\x10h\xb1\xc7d\x1c\x13\xd9c]\x98\x04\x95\xb1\x02$-\xf3%\x7f4L\xe2\x0b\xa2i\xb2\x1c\x91\"\x98\x14@\xa8\xf3x\xbb\xe9W\x10&\x8a\\=A\x01\x87t@\x99\xf7gr\x05\xbe\x9a)LpQ*\xa4A\x84m\xe8<\xf2\xb0<:\xe2DJ\x91\x01#\xd6U\xfa\xa8\xd9\x8cBH\x1f?\x84\xe0\x829\xc3lki\xbb\xb4\xe4\x9c\xe0\x0c\xaf\x8a\x1d\xe5\xdc\xfa\x07\xf8\x9b\xb8\x0c'8\x9a\xc9\x19\xf7vp\x96oj\xb6\xb5\xac\x08\x93n\x89\x87%\xb2/\xb4Q\xa4\x80\xa2Y\xba'\xe8E\xe4\xde\x7fL\x82ax\xc2T\xdc7\xa6]\x0b\x14\xe1\x85\xb02\xadQ;l\x07zA\xe9u\x9e\xa19\xbe%r\xa9\xab\xdc=\xa6\xae\xe4\xc6&K\xf4@\xf2\xc0\x8c\xe1\xd8\xf5\x03\xa8\xd2\xbd\x94\x06\x94\x8e\xaf2l)m]\xe0\x1bL\xd3\xa24N3NxM\xd7;\xeb\x85\xd3\x88x\xa2\xcf\xae\x0c\xb7+\xcf\x94\x9f\xe1{b\x8c$9\xccc\xcaJu$\xa5\xc4-\x16hz\x9f%\xf7\xcd4\xfbv\xfb~\xc6N\xde\x7fIN\xcb\xbc\xf84\x13\xa1`\x17\x8dHc\x84e\x04Z\xa3\x98@\xbb\xd5\xf1hX\xb1T\xa4\x8f+\xfe\xf2\xd4\xfeyv\xefz\x1a\xb6\xbe\xe4\x19\xe5\xb9\x98\x16\xb8a\x11\x11\xd6\x98\x08\xbf9\x08\xb0\xa1 \xf6\xd3\xf3_y\xb5\x03\xb7\x1a\xa5\x16\xec\x01\x0f\xcdr n\xcb\xd2\x98\x8bP$\xc3Zi\n\x08\xb9\x0e\x9aC\xc3\x19P\xa3xC\xf87\xa0x\xb6\x0d\xe1L7H\xca\xf8\x02\xac\xe1_6Jy\xc08$\xff\xf9:xd [;$\x8co\x92O^x\xcdM\xf6\xff\xd8\xfb\xb6\xa6\xb6\x95e\xff\xf7\xff\xa7\x98\xb7\xb0NA\x0c$\xe4B\xd5y\xf0\xceJ\xcev\xed\x1c`\x01\xc9\xaa\xf3D\x8d\xa51V\x90%#\x8d\x0cf\xd7\xfe\xee\xff\x9a\xabnsi\xc92\x98\xaa\xe8)\x11VO\xf7of\xba{f\xba{@8\xb9\x03=\xc0\xe0\xf4\x91z\x80X\x8fn\xb3f\xa0\x81\xd0G\xd6\x8d\x82=\xac\x03\xc5!\xb2\n*\x1c\xf2\xce\x99\x1aQ4\xf4\xbd\x9c\x90\x9e\x04\xd9J\x04\xb4\x97\x08AB\xb5\xc5\x03\x1c\x1a\x08\xae'\xd0v\x82\xb7\xc5\xa3{\xcao;\xd1\xf3H\x07X\xce4\x98\xeeoI\xab\xc4\x808\xc1\xac)\xda=\xac\x86\xb0\xad\x08\xfd\xbe\x98\x13.\xf9\xc0\x83\xa4\x9f\xe4\x1b\xda\x13d\x1eBf\xf1[\x97\xb2\x0c\xb1\xd62\x12\x1dn\xc5e\x8ej\xdb\xf2\x02\xac)\xd3\x00\xbe\x06P\x85@QG\x1d.'\xf3\x1fh\xa1\xd6\xd6\x85\xd78\xb5\xba\xddo\xa0v\x07\xa9\x8d\x0dS\x93`\x07\x9c\x82N\xf7>\xed\x10fm\xc6\x11\xe0\xb21\xc3W\x8d\x88\xf6\xbeX\xfa\x0c\x17\x10\xba\xfe\x98@\x8c\x96\x93\xdc&\xd2\x0f4p\xfaK\xbf\xa1\xe1\x02\x0c/\x13\x14\x10~\xe5\xa6\xa3\xfd\x860\xbdw\xf5\\W\x84\xc9\x06\x1dw\x84\xe9_\xe8\x9a\x8e\x88\x97t4\x10\xab\x14yt^\x17\x16\x98\xef\xc2\x11\x03\xe3y3\x05\x043\xb6\xe1:\x90\xd1~\xf6\xa8\x8cQ98\xca\xb0j!\xea[\x84~\xf2r\x062I\xddIKF\xf2\x1bJl\x8c\xe3'\xa1\nk\xf0\x85\x9bC\xf4xG\x98\xbaJnV\xf8\x11 xJB\xc5\xab\x00\xd0c@\xa1\x8b,]\xa69\x1c\x03\xad\x96\xb7\x03\x04\xbf\xb5q\xa9x\x9a\xf1\xbd=\x9a\x15\x01\x8f+\xe2>\xd2\x02g\xf9\xdc\x13\xbe\x8ePN1-\xbcS\xbf\x1b\xfe\x13\x9d\xf5\x12\xcd\x84\xdb\xc4\x15\x1fW\x91j\x18*\xa6A\x1d\xa0\x132~\x15<\x82HD\xcb\xb3.\xe6\xe7e\xcb\xc2\xaf\x9e;a\x0c\xcf\x9a\xbc\xfc\xfa\xe5\xfc\xf2\xcf\x9b\xc9\xd9\xc5\x8f\xeb\x9b\xab\xeb\xf1\xf5\x8f\xabN\x99l6\x1a\x17\x97\xe7\x17\xe7W\x1b\x10\x10\xef\xbc\x9f\xebL\xbcM\x05\xe9\xae\xc9\xbd\xe0Az\xc0C\xa2\x92\x80\x05H\x0d\xe1q_8\x8e\xc2Q\x91\x88\xf5\xa2\x18\xb7l\xec\x00>\xf6t\xa5\x19c\xf5\xd7f\xaaXe\xc6$\x08g\xd3\x88f8[\x97\x1a\x8c\xd7-\xd4k>1\x15\xfa\xf3(\xde\x999\x14\xef\xcc\xfcEbB\xd7<\xb0eFVQZ\xe4\xf1\xba5\xd5+\xe9UN^\xa5\xb2\xb9\xcepp'\xf6\xc6\x84\xe7\xa4W\x80D\xd9$\xe8\xaa\x0c\xa4\xbd\xaa\x8d\xb4\xfcl&[0\x8f\xc8J\xdc\xb1\x92\x16\xd4\xa7v\xd2\x04\xc4\x9d\xa0\xf4j\xbd\xc2\x17\xb6\xf5\xff\x94v\x9e\xaa\x1a\xab\x02NX\xc1\x06\xf1\x88/F\xe5u\xfe\x8dm\xa8\x17\xb0\xa0W\x9c\xa6\x92K\x0d\xc1r\x1b\xa3\\\x10\x02\x88\x95\x99N\x12\x1b9\x95\xa3\xe4V]\xdb\xb3?\xc3Q\\d\x80%$bfxI\x92\x10\xd4\x91]z\xbd\x8b\xd1\xbd\xfa\xf1\xbd\x97\x95j\x7f}1\xbe\x82%\xd7\xd7?\xbb\xfa\xd7\xe4\xa2\xc7g\xdf\xc6\x93\xef\xde\xcf*6\xb9\xaf\x9c\xfd\xac\xb1\xa55H\xa7X?\xaeY`T$9\xf1\xfbj\xfc\\\xa3K>}\xbbK\x9b\xe0\xb1w5\x13F*\xb3\x89Y\xd0\xf2\xfe\xaa>M\xb2\xe1\xd0l\x92\xbd\xab4\xa9E\x1a\x05i\x92G\xa1\xda}\xe0\x8d\xdfE\xfc\xfc\"\x14wy-\xa2c\xe48\xe6kwo\xe2v\x0db\xc9\x91D.M\xe4\x86\x86|-\xb3\x9d\xcc\"\xeb\x8d\xabA\x02\x95\xea\xd4<\x11J\x95H$+\xc1\xda\x10\xd8rD\x12h\x0fo\x8b\x13E\xb7o\x0d2\x92\x1d\xea\x0d\x1c*\xbba\x97#\x86\x9a\\\xf6\x0f\x15\xd2\x94 \x90\xc0\x02\x13w\x01\x96!\xc2U\xcba\x03\x84Gl\x16x\xf7&w\x01\x9f\n\xab\n \xb9\xeb\xc7_m\x05\xa1\x17\x88\x88\xaa\xb4\xec\x0b\x85\x1ab\xa6\xbc\xc2l\x08\x80T +\x81\x80\x96\x02\xfdN\x86\x90\xcf3H\xe7]\x1d\xfcN\x86\xe8\x85\xd5\x10\xd6\x05\xa1\xdf\xc9\x10p\xc9\x07\x1e$\xfd$\xdf0\xa6\x14uI\x86\x90zy\xc8T\x08\x03\xc9\xe1\x12!\xa4\x7f\xfd\x9ci\x10uy\x06\xf0,\x80\xaa\x03\x8a7j\xf1\xe8Y\x9d\xb8\xe7A\xb7\x04\x88Fg\xfbM\xd2\xae`\xb4\xb1!\xaa\x93\x03#\xb4\x95\xf8\xf5\xed\xa3\xb5\xad\xb4\x87\x9e(\xbe\x96%\x90\x81_\xc3:\xc8Ii\x00\xc0|\xf6\x1c\x08S_\xf9\x07\xb1\xe4P\xb9[\x19\x15C\xae\x91\x8c\xc4\xadk%\x10)\xaepQ\x14\xda\xd7L^\x91A\x96\x0e\x01\xad\x1d\xea\x98\xf8\x87\xe0\xe3\x07\xc1\xa7\x1a\xea\xd81\xa8C\" \x98PW[\x88\xfa%\x04\xa2\x9dE\xd0g#\xbd\x04i\xa7\xd4.\x04K\x88z\xa5X\xf6\xb3\xa0mR\x06+\xda\x91B\xb7>\xf0\x19\x0f\xd4\x0d\xf2\xcdp\xf4\x9a\x12/\xbd\xce\xe9\x96\xa8s\xd2!zND\x06X,\x02\x86j\xdf$Dq,l\xcfA\x94\xcb\xb9m\xa4 \n\xd2\x8edC\xd9\xb6<\x06ng\x0e\xf2\x9c\xc2v?7\x1a\xe1\xbf\x12qtA\xba\\\x97n\x9a\xf8C=? \xf1lF[\x93\x1e;\xee\xb6\xde\xde\xcdo\xef\x88\x84\xf4gk\xab\x1b\xabi\xa8zR\x18\xc9}D\xde\xde\xda\xe6\x94\xf8\xe9\xd1\xfd\xf1-9|\xc2O\xb4\xf8pB\x1fO\x1eO\xe2xu\xf2\x18|~\xa2\xf9\xc3\xfb\xe3\xf0\xfe\xf8W\xb8Z\x84\xf8\xa9xx\np\x18\xce\xe7\x9fn\x17\xc5\xbbE\xf0D\xde\x99(\xbbw{\x07\x92^\xef\xe3\x068A$\xe2\x15\xc8\xa7\x04a>\xf5\x9cB\x7f>\xfa\xfc\xf1\xd3\x14\x1f\x1f\x9c\xcc\xde\x9d\x1c\xbc?\xf9\x8c\x0f>}\xc0\x1f\x0ff$\xc0G\xd3\xc3\x93\xa3cr(J\xd6+\xbd\x16\xd8\xef\xd2\xab\xa3\xec\xe2\xf5\xe8\xfe\xc9\x8a\xf2\xfdc|\xf7@b#\x98\x9e\x04\xca\xa1\xe0,+\xecu\x06\xf4\xe4\xd3\xe1\xbb\xd9\xa7ip\xf0\xe1\xf0\xc3\xc7\x83\xf7dzr\xf0\xf9\xe4hvp|t|\xf4\xe1\xe3QpL\x82\x06\xa0\xa2\xb1\x8d \x15$\x8e\xee\x1f\xad\xa0~\xce\xef\xe3`\xfe.\x7f|H\xde\xbf\xffur\xf8\xeb\xe9\x96~\xca\xf2\xf9\xea~=\xcb~\x05\x99M\x1c~\xfd2\x03!M\xe2u \x01\x8ax\xe2^\xe5\xf8\x00\xc7yj\xe3O\xde\x8eaT\xd7\xf6\xb5\xb2\xb73e\xc8Eu\xf5*\xb2\xb7j{r4Eq\x9a\xde1\xedl\xa0\"\x93}\xc4\x86\xa4\x8b\x0f\xd7=\x00\xda_s\xfc\x066\xf2j\xec\x08E6\x8b\xf1-7-\xfaj\x81T\xfd\x8c\x8bi7\xa5\x82\x88\xb4n2\x8a('\xa5\x05R\xcb\xb9|\x99&\xb91\xcaC\xb3#\x13\xe0w\x08\xa0j\xd2\xbe\x0f#\xf7\xb4\xc97\x82\x88<\n\x86\x9cK\xfa\xe7D\xa8\xc1\x90\x1d \xf9C.\xa1\xf5\xe0\x94\x93\xe0\x81E> T\xcfX=\x14\xf4\xcc@4\x18\x82\x8c\x14k\xbe\xb1\xa6\x91\xa0uZd\xd2U\xaa\xc3\xe0\xe6J\xa6w]J\x04Q\x19\xed'\xfe\xcf\x90\x91\x11\xe2\x04\xfdU\x90l=R\xc5~//\xbe4\x88\x89\xcc\xd2\xb2y\x15\xda[\xf9Y\x8d\x9bq\x82\x8a\x84<.I\xc0\xbc=q\xb7\x94j\xb9\xf2M\x1e\xcc\xc9\x02\xd7{\xce\xea\xfb\xd9\xfd>N\xbf\xdd\xff\x0e}\x1e\xa4\xa1A\xf9\x8a\x0fl%\xcf\xd5@\x89\x12\xfa\xee\xb8\x85\x8f\xa5\\\xb8\x83\x87\x90P\x1c\xc5;R\x93\x83}~Sd\xd6[m\xbc\xb6\x91\xa7\xb2\xf6\xfe\xda\xb8\x05\xb1\xc4\x19^\x10J\xb2\n\xcf\x07\xc2~kO\xc06\x00\x1b\xd3\xa1\xab\xbf\xda\xcfKu9O}\xbc\xd0(9EKL\xabQ\x99L/D\x19 O\x11\xcd\x8a\xeaT2B\xac\xd0jl\xae\xd7\x90\xea\xe5\xcd\x0c\xc6Ye\x19\x05\xec\xca\xde\x0b\xafa\x97[\x0c\x81{\xa65\x8d\x10\xccp\x9c\x831(\xbd\x7f \x04\xdd\x97\x0b}\x17 \x1a\xce\x1a5\xc3\xe8\x1ezA\xb0\xc12\xc0\xe4\xfc\x0f\xd8[5\x8f\x19\xd8a\xfd\xbcl\xc3\xd2\xb7\x8f\xe3\xd8G\xf6\xb63ds\x93Z\xb0H\x1f\xb7+2]\xdckdhd\x97\xb1ix\xc8@hz\xfa\xd5\xa5\x07]\xa3\xf6\xb2\x084\\c \x02\xbd\x1c\xea\x8a\xeb\\#fq\xa3\xb7)?\xc5\xb75\xd7\xe5/\xd9\xce\x9b\x11\xd3Q$\xc1I@F\x0bBq\x88)\x1e\xad\x8eF|\xc2\x8f\xfe\xad\xf4\xdb\x7fFb\x08\x8f\xfe-\x8d\x1e\x03\xf3?#9\xfa\xdf\x08\xd2\xb7D\xbb\xe1y\xb1X\xe0l}\xaaj\xde\xe5('8\x0b\xe6\xf2NP5k\x94\xf86\xe8\xaf+ar\x0d\x03#\x14\x98\xc1\xc8\xc0\x1d&\xa7\xa7\x04\xf6\x92\xd0U\xb4\x88b\x9c\xc5\xeb\xfd\xaaF\x90\x1ckzM\xd3\x88J\x18\xac,\x0c`\xca\x18\x84\x9a`\xc5a\xd9g\xf6K\xd9\xa7}U\x0e\x0e\xe1\xb2u\x8833\x84#S\xb6\xf8_h2\x13V\xb6nXK6q\x1cWj\x16\xca\xa4%\xd9\x8dYUNZd \xbfk\xc6H\xbb\xec\xa1\x1a\xf5\x88\x968T:\x05'a\xbd^Q\xa3Z#\x9f\xe8\xe6\x06\x1b\xe3\x81k\x0d1d\xe9\x9c\xe8\xd1\\\x91\x96/N\xdb~(g\x8b\xf9\x17\x8c\xb7\n\x1c\x92\xb3\x06G%\x13\x0e\xd6$\x16\x0d\x1f\xb6\xa4M\x1b\x05\xf1\xb4=\xc3T~\xa4\xe9\xf1\xfb\x81\xa6\xc4\xda\x94nE9\xf8\xaa\x15\x0b\n\xfbU\xccp\xad\xce\x84\xee\x94:P5\x80\xca\xd6\x9bH\xf9$2I\xa2\x89\x8d\xd1\x14\x87\xd5\xe3%\xf5#\x14\xcdN\xab\x12_[:\x9d3Q\x95\xfe!\xa2\xf3\xb4\xa0\xf5\xfe7M\xd6\x1a\xa0c+\x9a\x9a^\xb2F\x0fx\xcd\xcd\x91*\xd9\xa7\x9d8\xb4W\x9b\xc1U\xd4q\x0b\xf7*\xde\xff\xaf\xf2M\x85\xb7?j\xcc]?\xa4\xack\xf8\x85M\xe9\xcc\xd1\xa3\xf5\xe9U\xed\xca\xeaD\xd6\xa2\xb1\x1f\x85i\xf2\x86r\x15\xc0\x03sdY*\x94\xb3\x15e]m\x9b\xf1\xe2\xd4\xf7\x98\xdaUhW\x9a\xe6E\xdaZh\"\xdcT\xc6\xea31\xed\xe4h\nS\x92\xa3$\xa5jX\xa1\xbc\x08\xe6m\xbc\x1cl58\xe1\x17_MS:\xaf\x0c\xe1iA\xcb\x88$M(\x8cf\xfc\x95\x1a\xc2yu\xc8\xfec\xad\xb6\xc7\xf6+\x03\xa0l+\x97\x17lQ]\xf8\xab\xc2\xe2\x15\xa1\x8d\xd5\x02N\xc2Q\x9aU|!A\x83\xf5C&\xd2\xack\x9eqm\xc9 \xbf\xd5L\x8a\xb7\xbab\xdc$,]\x85\x13\xf9G\xe5#V\x1c\x977\xc7\x87\x87o\xec[}\x95\x94s\xa3\x879\xd0&\x9f\xe5\xc0\xc4\xbf5\xab\x17]\x0c\x1f]a7P\xd7\x98\xf1\xf2\xba\xb9\\2h3\xb7\x17\x99\xa2z\xec\x05v7:\xb3\xb6\x88\x86|T\x91\x972\xf2\x1e\x08#\xd8\xf6\x1c0F\xc8\x9f\x94\x8c\xd0\x0fQ\xe0v\xf2'\xd2\xf5I\x84\xfe@h\xb2X\xc6\xfc:\xc7\x1c\xe5\xe1\xdd\xdb\xb1'\xe5\x16\xf1\xbd\xdal\x86\x03\xb1\x93\xcd+\xbf\x8a+\xec\xc4\x97D\xcc5\xe5\xcf7\x97\x04\xf5\xe7K\x9a/R[c98C\xfdy\xc1,\x15L\xdeN,\xd7q&\xee\xe8\x97\xda\x97b\xa9\xc0+\xd0\x10\xd5\x178\x8e\xd3\x07\xa9\x9ceF\xbf\x8b\\k\xd7\xa2|\xd2\x87\xa4\xb6\x9d\xdb|\\[\xdf\xea\xf1\x96\x07\xf2\xce\x19\xf1\xf8g\x8ex\xf0\xb6J\xd7b\xf3\xad\xaf{\xaa\xd4\x83\xbb2_\x96\xc6\xde\x92|\x90\x11$\x1e\xcc\xe9\x953R1S)C\x1d\xc8\x02\xcd\xc0xGY\x0c\x82\x00\xca\xd2v\x02\x0fV^\xe7\x00]\x8c/\xaf\xff\xef\xe6\xfa\xff.\xbev\xaa8S\xfb\xf0\xfcr\xf2?\x93\xb3\xf1\xf5\xf9e\xb7\xef\xae\xbe^\xfe\x9c|\xf9\xda\xf1\xab\xc9\xd9\xcf\xafW\x9d\xdb\xfa\xf2\xe3\xea\xfa\xfc\xcf\xc9\xf8\xac\xdbg\xe7\x7f\x9fu\xe5o\xfc\xed\xdb\xe4\xfbd|\xfd\xb5\xdbg\xe7\xff{6\xf9\xc7\x0f\x7fq\xa2\xdaG\x17\x97\xe7?\xbf\x9e\x8d\xcf\xbetl\xec\xcb\xf9\xd9\xf5\xe5\xf9\xf7\xef]e\xfb9\xfe>\xf9\x13\xd0\xd1\xba\xa6Q\xaf\xe1\xe5wV\xea\x8fm\x14\xf7l\x1e\x89 @y\x1e\xdb\xa16\x91eR\x9c\x9a_\xcbVp\xce/\xa9\xcd\xa2\xdb(\xc1\x14R\xa2\xd58\x87NM/\x95\x87\x9e\xa3\x90L\xd9\x92 [E\x01[W\xce\x8a$\xa0\xad\xada\x7fkj\xee\x9d\x9a^\x8a\xd5$\x8f^\x8c\x02\x14%+\x92w\x97G\xcf\xd3S\xe3[\xd55 \x8d\xe8Z\x98o-cP\xe44\x0d#\x9cHA\xe5~\x1e\x07\xb8\xab\xa0|\xde\x9f\xb6\xde4\xcb\".qF\xd7\x92'n\xb4\x95\x95b\xd6\xb7c\x93Zo\x9c\x1a\xdf\ntE\x83b\xf9\x97 <\x9bEq\x84)A\xf86#\xdc\x0d\xe9\xd8\xa8\xd4:\xa7\x86w\xa2A\xee\xf5\xe0X\xc4A\xc8\xab\x94\xd7\xa5\xdf\x94\xa5\xb1\x10~\x91D\xd3\"GS\x9c\xdc)\xab\xd8\x91\x95R\x97\x9d\x9a_3\x86T\x01 \xd5\x0f\xd5n\xc8\xc82#9w\xc5X\x17\x94\xc5'\xe5\x8ej\xbd\x00:\x0ezL\xeaRo\x9e\x9a_\xd7\xc7\xe7\xc3<\n\xe6\x15\x9c\xb4\x0f\xa9f\xbd\xae\xcd\x1c\x11D\x92\x94zj\xb1\xb7\x19\xd2\n\xf9\xd4\xf8\xd6\xc4\x0e/y\xca\x87\xb0(G%\xe6\x07\xa4r\x16\xd2\x17\xae\xfb<\x0b\x7f\xcd\x0e\xb5\x00\xf0\xfe\xff[\x9a\x07\x1c\xc7Z\x05\xa8\x19Qn4G47\xc7H\xd4\x1fu\xee\xa6\x94j\x8d\xd0\x9b\\\xa9\xd6YD\xe2\xd0\x9a\x0b\x80T\xb4\x0e?\x08\" \x9e\xc6b\xcd\xcfT\xb2\xb2U\xff\xcd\xf7\xaa\x05\x83\x82\xaak\x8c$\xfa\x064%\xa3\xb9\xd3`\xb0]\xf1V\xf9-\x19\x84\x97\xf9N\xd3\xca\x9e\xbe\xf0\xa4Q\x90\xc61\xe1\x8e\x13c[\xda\n\x0bA&\x00\x1fmiB\xf4\xb9\x8b\x83Q\xb5\x01\xeb\x88\xd7\x86IR#T\xee\xee\xf9J\x16\xdawlJcd\xfc\x81w#\xcd\xbf\x85\xf6\x9c\x9b\xcf5\x10\xabG\xd9\xb4w\xa5)\x8d\xb8/}y\xcbR\xf82\x90K\x89\x1c9\x9c\x9a\x98\xf8\x87;}sK\x029\x95\x9b\x81A\xe4*\xf8\xe4\xdb\xd7\x06\xe1\x81\x9dy\xbd \x18\xbaJ\x88}\xd9\xbbC\xc96@\x17w\x95m\xa3<\\\xc3\x00\xb0+T\x7fa\x0b\x18\xefmj\x1e\xd5j\xa1\xd3.\xd2d8\x98\xd9\xb2\xa2\xf5\x16\xaa\x00\xf5\xf9V\xa6\xb5\xa7\x1a\x85\xf3\xd8\x085O\xa9`z\x1bZ{\xe2\xc5A\x81\xa8w(-\x18$\x95\xff\x0d\xa0$\xb6\x8aM\x83\xd5\xbaE\xd0\xaf\x1d\xe4\x8cf\x12\x8e\xd6\x0b\x18\x08X\x8d\x87\xe1\x07\xc6\x00C\xa1\x97\xa8\x03\x18\x0d\xcbhi\nmO,u\xedzl+\xebJrc\x83\xdck\x16\xfc- \xe4Ob\x17\x0f\xa8w\x81S\x1d\x1e-1l\xbb]\x86\x9f\xbei\xb9,\xdf\x1f\x98\x0b\x1e\x81\xae\xe01\x15\xf0\x97\xc0\xdb&\x1eR\x8bF?<\xbbye\x92w\xf3K=\xc0>\x96?\x1e\":\x02\x16\x1f\x01\xdd)\x17\xcf\xb01\x12=\xa2$:\x02 \x8b\x94\xd8 V\xa2\x7f\xb4D\xdfx\x89\xbe\x11\x13\xbdc&zEM\xf4\x8e\x9b\xe8\x199\xd1?v\xa2\x7f\xf4D\xcf\xf8\x89M#(\xbaix\xf1X\xa3(\x00\xdf\x9a\xbf\xec\x1bG\xf1l\x91\x14\xcf\x1dK\xf1\x1c\xd1\x14;\x13O\xf1\"\x11\x15/\x14S\xb1SQ\x15\xaf#\xaeb\x07#+^6\xb6\x02\x1e]\x01=WS\x0f\xf4|M=C\xc6X\xc0}\xc7\x81\xe2,zEZ@\xb9\xd4g\x89rs\x85\xf0[\xeaD\"\x13\xff\x83\xccx\xbbMx\x02\x993\x00\x1c\xf9\xd9\x1a\xa8`5L6\x9e\xdbC\xd3\x12\xbe\xeaA\xbf\xbb\xael\xbb\xf2J\x02s\xbc\"\x95\x96$\xc2<\xa6\x8c\xaa%iD\xecj!JVi\xbc\xaa\x97!h>g\xe7l\xe5\xfd\xb7D:#2\xa5[\x84\x82]\xd6\"\x8d\x11\x96\x11h\xb5j\x16\xcd\xa7\x8cG\xc3\nR\x91=~U\xadw\xb1HW\xb6\x8b\xaa\xcbC\x9eA.\xafj\x90\xeb\x17\x11a\x8c\x89p\xbb\x83\x00\x1f\n\xe2?=\xff\x91W3p\xabV\x8a\xc2\x1c\xf0 {\xd6\x13\xcaP\xeb\x0b_$\xc3Ve\xf2(\xb9\x16\x9b}\xc3\x19\xaa\xa4@\xa8@\x82\x9dv\x04\x99v\x90T\xe5\x0d\xf0F\x11Z+u\x02CH\xfe\xf3u`Ta\xb6\xdc\x90\xa8\xbc\x9389\xe9\xa5\xcd:@~\x9c\xdc\x81\x1e`p\xfaH=@\xacG\xb7Y3\xd0@\xe8#\xebF\xc1\x1e\xd6\x81\xe2\x10Y\x05\x15\x0ey\x03V\x8d(\x1a\xfa\x96`HO\x82l%\x02\xdaK\x84 \xa1\xda\xe2\x01\x0e\x0d\x04\xd7\x13h;\xc1\xdb\xe2\xd1=\xe5\xb7\x9d\xe8y\xa4\x03,g\x1aL\xf7\xb7\xa4Ub@\x9c`\xd6\x14\xed\x1eVC\xd8V\x84~_\x13\x0c\x97|\xe0A\xd2O\xf2\x0d\xed 2\x0f!\xb3\xf8\xad+\xa2\x86Xk\x19\x89\x0e\xb7\xe22G\xb5my\x01\xd6\x94i\x00_\x03\xa8B\xa0\xa8\xa3\x0eW%\xfa\x0f\xb4Pk\xeb\xc2k\x9cZ\xdd\xee7P\xbb\x83\xd4\xc6\x86\xa9I\xb0\x03NA\xa7[\xe8v\x08\xb36\xe3\x08p\xf5\xa1\xe1\xabFD{_,}\x86\x0b\x08]\x7fL F\xcbIn\x13\xe9\x07\x1a8\xfd\xa5\xdf\xd0p\x01\x86\x97 \n\x08\xbfr\xd3\xd1~_\xa1\xde\xbb\xda\xc2\x85\x85\x92\xb6\xe3\xc6B\xd5\xba\xf5\xcaB\xbe\xff\xda\xee[\xd1\xa3\xcf\x1b\xe2/\x98\xb1\x8d\xb3\x81\xac\xed\xb3\x87S\x8c\xca^-\xe3\xa1e\xbdU\x84~\xf2:\x042\xbb\xdcIK\x86\xe0\x1bjc\x8c\xe3\xa5\x88z\xf1\x05\xe8\x02,\"\xce\x0c\xc7\xcf\x9a\xf9=\xd60/:\xed\x0e?b\x8f\x8e0\xe3\x97\x17\xd4{3H\x17\x8b4\xe1\xed\xb9\xc3@\xc5\x05n/\x02\x87hZD\x98)1\xca\xe3N\x9aBN\x85\xd5\xa3KW\xa3=A\xf6\x8f\xf2\x94\x95\xe34\xd2\xd08\x8f[\xd5\xc3To\x94\xac\xd2;\xc7X\x8a\x92eA_m\x8e\x16d\x16t\xeax\x98\x07Y\x7f\xceX\xb7\x8b\xeaD\xaa\xe69\xbfX,J\xee\xd0\x14\x07w\xb2\xaa<\x80\x12\x0f3\xe0\xd9\x16|\xd0\xb87\xa9t9\x7f\xff.UG\x08\x80\xee\x81x\xe0S\xc5\xa0\xbe\xa5\xa1/\xc3\xbe\xbf\x81r%\xbe\xe6\x14O\xe3(\x9f\x93P\xc5#\xf8\xe2\xc4!z\xbc#L]%7+\xfc\x88\x04<\x97\xa0\xe2U\x00\xe81\xa0\xd0E\x96.\xd3\x1c\x8e\x81V\xcb\xdb\x01\x82\xdfk\xbaT<\xcd\xf8\xa6\x1c\xcd\x8a\x80\x07\x04q\x1fi\x81\xb3|\xee\x89;G(\xa7\x98\x16\xde\xa9\xdf\x0d\xff\x89NW\x89f\xc2m\xe2\x8a\x8f\xabH5\x0c\x15\xd3\xa0\x0e\xd0\x99\x14\xbf\xf85!2\xcc\x9du1?\xe8Z\x16~\xf5\xdc cx\xba\xe3\xe5\xd7/\xe7\x97\x7f\xdeL\xce.~\\\xdf\\]\x8f\xaf\x7f\\uJA\xb3\xd1\xb8\xb8<\xbf8\xbf\xda\x80\x80x\xe7\xfd\\\xa7\xd0m*HwM\xee\x05\x0f\xd2\x03\x1e\x12\x95\xcc)@N\x07\x0f\xd8\xc2q\x14\x8e\x8aD\xac\x17\xc5\xb8ec\x07\xf0\xb1\xa7+\xcd\x18\xab\xbf6s\xbc*3&A8\x9bF4\xc3\xd9\xba\xd4`\xbc\xe0\xa0^\xf3\x89\xa9\xd0\x9fG\xf1\xce\xcc\xa1xg\xe6/\x12\x13\xba\xe6\x81-3\xb2\x8a\xd2\"\x8f\xd7\xad\xa9^\xc9\x8br\xf2*\x95\xcdu\x86\x83;\xb1\xa9%<'\xbd\x02$\xca&AWe \xedUm\xa4\xe5g3\xd9\x82yDV\xe2v\x94\xb4\xa0>\xb5\x93& \xee\x04\xa5W\xeb\x15\xbe\xb0\xad\xff\xa7\xb4\xf3T\x15G\x15p\xc2*-\x88G|1\xd2\xbb\x00\xcdm\xa8\x17\xb0\xa0W\x9c\xa6\x92K\x0d\xc1r\x1b\xa3\\\x10\x02\x88\x95)J\x12\x1b9\x95\xa3\xe4V]\xb8\xb3?\xc3Q\\d\x80%$bfxI\x92\x10\xd4\x91]z\xbd\x8b\xd1\xbd\xfa\xf1\xbd\x97\x95j\x7f}1\xbe\x82e\xc5\xd7?\xbb\xfa\xd7\xe4\xa2\xc7g\xdf\xc6\x93\xef\xde\xcf*6\xb9\xaf\x9c\xfd\xac\xb1\xa55H\xa7X?\xaeY`T$9\xf1\xfbj\xfc@\xa2K\"|\xbbK\x9b\xe0\xb1w5\x13F*\xb3\x89Y\xd0\xf2\xe6\xa9>M\xb2\xe1\xd0l\x92\xbd\xab4\xa9E\x1a\x05i\x92G\xa1\xda}\xe0\x8d\xdfE\xfc4\"\x14\xb7p-\xa2\xcf \x9dwu\xf0;\x8b\xa1\x17VCX\x17\x84~g1\xc0%\x1fx\x90\xf4\x93|\xc3`P\xd4%\x8bA\xea\xe5!s\x18\x0c$\x87\xcb`\x90\xfe\xf5s\xe6/\xd4\xe5\x19\xc0\xb3\x00\xaa\x0e(\xde\xa8\xc5\xa3gu\xe2\x9e\x07\xdd2\x17\x1a\x9d\xed7I\xbb\x82\xd1\xc6\x86\xa8N\x0e\x8c\xd0V\x02\xcf\xb7\x8f\xd6\xb6\xf2\x15z\xa2\xf8Z\x96@\x06~\x0d\xeb '\xa5\x01\x00\xf3\xd9s L}\xe5\x1f\xc4\x92C\xe5n\xa5B\x0c\xb9F2\x12\xb7\xae\x95@\xa4\xb8\xc2EQh_3yE\x06Y:\x04\xb4v\xa8c\xc6\x1e\x82\x8f\x1f\x04\x9fj\xa8c\xc7\xa0\x0e\x19|`B]m!\xea\x97\xc9\x87v\x16A\x9f\x8d\xf4\x12\xa4\x9dr\xb2\x10,\x93\xe9\x95b\xd9\xcf\x82\xb6I\x19\xachG\n\xdd\xfa\xc0g\xf8\xf4\x01\x7f<\x98\x91\x00\x1fM\x0fO\x8e\x8e\xc9\xa1\xa8\xda\xae4D`\xbfNN\x1au\xd9\xa0\x8b\xd7\xa3\xfb\xa7[r\xf8\x84\x9fh\xf1\xe1\x84>\x9e<\x9e\xc4\xf1\xea\xe41\xf8\xfcD\xf3\xfb\xc7\xf8\xee\x81\xc4&\x8e}\xa9\x88C\xc1Y\x16\x99\xeb\x0c\xe8\xc9\xa7\xc3w\xb3O\xd3\xe0\xe0\xc3\xe1\x87\x8f\x07\xef\xc9\xf4\xe4\xe0\xf3\xc9\xd1\xec\xe0\xf8\xe8\xf8\xe8\xc3\xc7\xa3\xe0\x98\x04\x0d@Ec\x1bA*H\x1c\xdd?ZA\xfd\x9c\xdf\xc7\xc1\xfc]\xfe\xf8\x90\xbc\x7f\xff\xeb\xe4\xf0\xd7\xd3-\xfd\x94\xe5\xf3\xd5\xfdz\x96\xfd\n2\x9b8\xfc\x06b\x06B\x9a\xc4\xeb\x12\x02\x14\xf1\x14\xb8\xcaF<\x8e\xf3\xd4\xc6\x9f\xbc \xc2\xa8\xf8\xbc\xe7L\x03\xf5i\xe3TI\xc3\xaf\x94f\x0dg\x17\x89\xa3\xfbc+\xca\x0f\xef\x8f\xc3\xfb\xe3_\xe1j\x11\xe2\xa7\xe2\xe1)\xc0a8\x9f\x7f\xba]\x14\xef\x16\xc1\x13y\xe7\x00\xc0\xbe\xfa\x1e\x16\x80\xea\xaaYd\x8d\xd5\xf6\x02i\x8afQ\xc2\x15\xa2gdr\xbb\x91H_V\xf4\xafL26| \xb3\x93\xc4\x0e\xaaKL\xd7\x8d\x03\xda\xc1t\xfc\x06\x86E\x8d\x1d1\x1cf1\xbe\xe52\xe9K\x0cR\xf53D=\xd7hJ\xfb&C\x9erRI\xb5W\x8b\xcf|\x99&\xb9\x13\x1ai,w\x07\x9c\xaay\xf7\xc1\xe3\x1csj\xad\xd8\x11\x18\xf2(\xf8pn;<'0\x0d\x86\xec\xc0\xc8\x1fr \xad\x87\xbb\x9c\x04\x0f~\xf2\x01Qv\x88\xc5\xf5A\xcf\x0cD\x83!\xc8\x08\xb1\xe6Dk\x1a Z\xa7E&}\xb0:\x0cn\xaedQ\x8d\xfcRB\x88\xca\x90D\xf1\x7f\x06\x8d\x0cc'\xe8\xaf\x82d\xeb\x91\xfa\x06]^|i\x90\x13\xf9\xaf%\x03*\x00\xb9\xf2\xb3\x1a?\xe3\x04\x15 y\\\x92\x809\x92\xe2\xea*\xd5t\xe5\x9b<\x98\x93\x05\xae\xf7\x9d\xd5\xad\xb4\xbb\x94\x9c~{\x048lD\x90\x86\x06\x8d+>\xb0UTWC%J\xe8\xbb\xe3\x16>\x96j\xe4\x0e\x1eBBq\x14\xefH\xe5\x10\xf6\xf9M\x91Y/\xcd\xf1\xda[\x9ep\xdb\xfbk\xe3F\xc9\x12gxA(\xc9*<\x1f\x88\x1cc\xede\xd9\x06`cBt]\x0b\xf4[\x01\xb8\x1c\xd3>\x1e~\x94\x9c\xa2%\xa6\xd5\xd8Q\xa6\x19\xa2\x8c\x84\xa7\x88fEu*\x19!VhU\xdc\x1c `\xbd\x1d\xa3\x8a\x0b\xd4\x9c\xbd\x16wh0)\xcb\x15\nP\xc8\xeeK\x9a\xbe\x0b\x19=2j\xd4\x0c\xa3d\xe8E\xcb\x06K\x15\xd3\x02\x85\xf5\xd5=3\x16\xc6\xce\x9a\xe18\x07\xf7Ve\xed\x01\xec\xae\xde\xab\x95a\xd7(\x03bPs\xb9\x81(\xf4u\xd3+\x0ey\x8d\x1e\xdc\x07\xed#x\xdb\xaf\xb2y\\ML\xa4\x9b\xdc\x11\x95.\xfey\xc5\x13\xdfiD\x1a.6\x10\x91\x9e\x8ey\xe9\x8271yA\x04\x1a\xbe5\x10\x81^\x1ey\xc5\xf7\xae\x11\xb3\xf8\xe1\xdb\x94\x9f\xe2\xdb\x9a\xe7\xf3\x97l\xe7\xcd\x88\xa9f\x92\xe0$ \xa3\x05\xa18\xc4\x14\x8fVG#>\xcfG\xffVj\xfd?#9\xc0\xdf\x08*\xb7D;\xecy\xb1X\xe0l}\xaa\xaf\x0f\xc9 \xce\x82\xb9\xbc\x9bT~\xa6\xe4\xb4a|]\x9e 3\xdd\xbb\xcf\x8c\x8a2\x1a\xfb\xaa\"\x9bGMkb\x03\xe8h\xc6\x90\xa6\xc7A8hYv\xa1\n+\xd6]\x7f\x00w\xf9L\xbe^\xadY\x88\x9b\x87\xae\xa2E\x14\xe3,^\xef\x97<\xd0Z9v\xa3S\xc2\xeb25\x9c\x8a\x86$C8\x115,=^\xe0:-\xded\xa4l\x9f-\xa02~P\x81\xa2\xa4z\xb1\xcd\x7f\xa1\xc9L8\"\xb8i\xcc\xcbq#\xef~\xac\x1e\xde\xf0\x9bv*\xc5\xfd2B\x8b,\xe1\x97\xe6\x98HW\xdd\x9a\x92.\x8e\xe3\xf2pF\xe6\x87\x89\x9e\xecJ\xbe\xec\xa0=\xd6\x19\xb2\xb5Q\xf9\xfe\x0fck\xcd\xfeQ\xedZ\xdb\xc3\x1ax\xed<\xabk\x0e\x95\x848 G\xe5\x80\xb8\x89\xea\xf85&\x97\xa9A\xfd\x931\x9a\xe2\xb0zD\xa4~\x84\xa2\xd9i\x95\xb1\xeb\xfa\x18\xe5J\x95\x0fL\x9c\x84(I\x8d\xe8\xbfm\x10\x90w\x18\xd1\x87\x94\x0dg~\x8fQ:\xab\xeb\x92\xaaH\xfc\xf0N\x92\xe5\xff\xabN\x985\n\xd3\xe4\x0d\x95\x88\xcf\x84F\xe7\x13\x89\x81'W\x1b\x95\xe6\xc7-T\xf7\xd1\xb4\xa0(I\xa9\x01Y\\\xbb\x1e\xa1MF\\\xd9X\x0ee&\x96\xee-,\x97F\xfc\x97aJr\xc6\xe7\x02\xd3\xa0~=X\xe5\xfbj\x8f\xfcc\xad\xb6Y\xf6+^\x9c\xb8[J\xba&\xe2\"(\xaa\xeb\\U8\xbc\"\xb4\xe1*J\x89\xf4KE\x84\xc1\x95\x89\xb4b\xfb\xden}\xa0i\xd3\xa0K\xa4MBmK\xd4\xde\x88r\x17*6\xec\xcd\xf1\xe1\xe1\x1b\xfb\xa6Q%\xc5\xda\xe8l\x0c\xb4]d\xd9o\xf7o\xf3i\x97\x9b\xe1\xa3\xeb\xc3\x06\xea\xbe-4O\xe30G\xea\xd8TL\xfe\xbd\xc8\x14\xc4b?\xca\xdd\xfc\\\xd5\xb5\x15\xea\x08\xe0\xf2\xed\x10\xf9Nm\x11l\x9f\x07\x18\x12\xe3\xcf\xc1E\xe8\x87\xa8\xe7:\xf9\x13\xe9r\x1cb\xba#4Y,c~\xed`\x8e\xf2\xf0\xee\xed\xd8\x93a*m\xd6\x0c\x07bO\x94\x17:\x15jJ|I\xc4\\S\x9e]\xd39\xac?_\xd2|\x91\xda\x1a\xcb\xc1 \xd9\xcf\x0bf\xa9a\xf2v\x1e\xb5\x0e\xabp\x07{\xd4\xbe\x14\xae$/\xb8BT_\xe08N\x1f\xa4\x19\x93 \xec.r\x81\xfdt\x89_j\xef\x082t\xed\xa1\xaa\xc7[\x0d\xc7;g\xc4\xe3\x9f9\xe2\xc1\xdb\xaa\xd4\x8a\xcd\xb7\x93\xee\xa9\xca\x06\xeeBtY\x1a{+\xd0AF\x90x0\xa7W\xceH\xc5L\xa5\xear \xeb\x11\x03\xc3\xfbd\xed\x03\x02\xa8\xc2\xda <\xbf\x9c\xfc\xcf\xe4l|}~\xd9\xed\xbb\xab\xaf\x97?'_\xbev\xfcjr\xf6\xf3\xebU\xe7\xb6\xbe\xfc\xb8\xba>\xffs2>\xeb\xf6\xd9\xf9\xdfg]\xf9\x1b\x7f\xfb6\xf9>\x19_\x7f\xed\xf6\xd9\xf9\xff\x9eM\xfe\xf1\xc3_\x8b\xa7\xf6\xd1\xc5\xe5\xf9\xcf\xafg\xe3\xb3/\x1d\x1b\xfbr~v}y\xfe\xfd{W\xd9~\x8e\xbfO\xfe\x04t\xb4.\xe1\xd3kx\xf9}\x95\xfac\x1b\xc5=\x9bG\"\xbcM\x1e\xecu(\xc5c\x99\x14\xa7\xe6\xd7\xb2\x15\x9c\xf3\xcbT\xb3\xe86J0\x85T$5\xce\xa1S\xd3K\xe5\xac\xe7($S\x8ar\x92\xad\xa2 Jn\xd1\xacH\x02n\xc6:\xb6\xa6\xe6\xde\xa9\xe9\xa5X-\xf1\x08\xbb(@Q\xb2\"ywy\xf4<=5\xbeU]\x93\xd0\x88\xae\x85\xf9\xd62\x06EN\xd30\xc2\x89\x14T\xee\xf7p\x80\xbb\n\xca\xe7\xfdi\xebM\xb3\n\xe0\x12gt-y\xe2F[Y)f};6\xa9\xf5\xc6\xa9\xf1\xad@W4(\x16\xca \xc2\xb3Y\x14G\x98\x12\x84o3\xc2\xdd\x90\x8e\x8dJ\xadsjx'\x1a\xe4^\x0f\x8e\xc5\x89\xba\xbc\xf2w]\xfaMY\x1a\x0b\xe1\x17I4-r4\xc5\xc9\x9d\xb2\x8a\x1dY)u\xd9\xa9\xf95cH\xd5\xbbQ\xfdP\xed\x86\x8c,3\x92sW\x8cuAYkQnl\xd5\xeb}\xe3\xa0\xc7\xa4.\xf5\xe6\xa9\xf9u}|>\xcc\xa3`^\xc1I\xfb\x90j\xd6\xebR\xc4\x11A$I\xa9\xa7\xf4x\x9b!\xad\x90O\x8doM\xec\xf0\n\x9f|\x08\x8b\xeaKb~@\nE!}1\xb8\xcf\xb3\xf0\x87\xa6\x94\x0f$H\xa5|\xa4\xaf\xa6\xf7\xb7u\xf7\xbf\xc9Q\x1e\xdd&X\xdc\x08\x9ekV\x1d\xf4`~\xdf\x18]Tf\xb9\xf2F\xd9\x14\x1c\xf1b\xe9\x02E\xe6\x17:\xc94k\xd8{\xeb\x1fu\xb1\x83\xd7|U.\xaf\xe3.'\x02\xa2\xe9\x12\xc5dEb\xb9\xa4\xf0\x87\xb4\xab\x95\xbd\xf0h\xdf6I\xf3}o\x863\x8f\xff\x96\x0b}\x97r\xe5\x9a\x82\xdf\xac\xbe\xb6\xdf\x01.\\a\xbe\x9a2\xdc_\"8\xa8\xafU\x9d\xe3\xb4H\xc4\x06$\x9dk\xbe\xe3\x88\xef\xd8\xa6\xb5KA\xad\xb9H\x8c\x93\x1b\x1c\xb8o\xf4\x18p\x11\xe6\xf1\xe8\xe1\xe3`\xacAR\x15\xd4\x98\xe0\x1c1\\\xd0y\x9aEOB\x7ff$ \xd1\xca5\x10\xca\xaa\xe1\xbc_\xdaW0\xe8\x0d \x0b\x11\x1e3s\xc3\xc7\xdd\x8dwq84\x0e\xe5D\xad\x9b*\xd6\xfb\xa2z~C \xe7\xd8\xac\xec\xbd\\Q\x9c\x848\x0b\xab\xe6E\xda\xbb\x9co_.pvg\x88\xaf*\x1f\xf5k\xd7t`\x1d\x96\x17\xcbe\x9a\xd5\xea\xb1r\xce\xdf\xca\xb8\x07Li\x16M\x0bJ\xd0\x02\xaf\xf9\xf6\xbd\x83\xe0\x940\xed\x9e\xdc\x92\x10M\xd7\x1c\x05i\x13\xca\x9a{i\x120\xbf\x85\xe9\xb9\x9c\x18\xb2C\xc4#7\x89n\xb8\xce\xbd\xc9\xd28.\x96\xben\xf5iv\xa8\x05\x80\xf7\xff\xdf\xd2<\xe08\xd6*@\xcd\x88r\xa79\xa2\xb9V\x05\x0eb\xea\xacO)\xd5\x1a\xa17\xb9R\xad\xb3\x88\xc4\xa15`\x1f\xa9p\x15\x1c\xe7)\" \x9e\xc6b\xcd\xcfT\xb2\xb2U\xff\xcd\xb7\xaa\x05\x83\x82\xaak\x8c\x94{\xe5JFs\xa7\xc1`\xbb\xe2\xad\xf2K!\x08\xafj\x9d\xa6\xb4R\xda\x9a{\xd2(H\xe3\x98p\xc7\xa9<\xd2\xb0\xf1\xc8\x04\xe0\xa3-M\x88>\x08q0\xaa6`\x1d\xa1\xbf0Ij\x84\xca\xdd=_\x85>\xfb\x8eMi\x8c\x8c?\xf0n\xa4\xf9\xb7\xd0\x9es\xf3\xb9\x06b\xf5<\x8b\xf6.\xac\xa4\x11\xf7e\xebnY\n_\xc2m)\x91#eQ\x13\x13\xffpg+nI \xa7r30\x88\\\xf5\x8d|\xfb\xda <\xdci\xac \x18\xbaJ\xe8MV\x1dJ\xb6\x01\xba\xb8\xabl\x1b\xa5\x9d\x1a\x06\x80]\xa1\xfa\xeb8\xc0xoS\xf3\xa8V\x0b\x9dvM\"\xc3\xc1\xcc\x96\x15\xad\xb7.\x03\xa8\xcf\xb72\xad=\xc5\x17\x9c\xc7F\xa8yJ\x05\xd3\xdb\xd0R\x0b/\x0e\nD\xbdCi\xc1 \xa9\xfco\x00%\xb1Ul\x1a\xac\xd6-\x82~\xed g4\x93p\xb4^\xc0@\xc0J\x1a\x0c?0\x06\x18\n\xbdD\x1d\xc0hXFKSh\xb5*jK\xe9\xda\xf5\xd8V\xfa\x8e\xe4\xc6\x06\xb9\xd7,\xf8[@\xc8\x9fi.\x1eP\xef\x02\xa7:K\x94\x9b+\x84_\xca&j\xeb\xf1?\xc8\xfc\xa4\xdb\x84W\x98s\x06\x80#?[\x03\xd5g\x86\xc9\xc6\xf3xhZ\xc2W=\xe8\x97\xe7\x8c\xb0\xfb\xf7\xa4{\xbf\xcf\xf8\x8b\x02\x1c\xc7k\x14\xc48\xcf\x9d\xb5\x9a\xe5G\x03\x88\xbb\x855\xb0Z\xb0\x88$\xdb\x88\xc4\xe2P\x9eYd\xe6\x02\x04qD\x92r=\xcc#&\x9c\xf4\xdaC\xd4\xbb\x08\xc6E\x189\xd1\xe9\"\xcf\x17\xbe\xfc\x0e\xd1t\xbd\x8f\x8ae\xa8\xffM\xa3\x05\xc9)^,\xf3}\xbd}&\x8a<\xbaoF\x15\x89b\xb1\xbc\xbbo\x96\xda\xc5\x00\xaf\xb0\xa1\xebk\xb1\x93\x10\xde0!|\x8a\x138\x80Pe\x101\xb2\x07\x0c\x16\x1fi1\xc7d\x1c\x13\x19\xb1O\x98\x06\x95\xb1\x02$\xa1\xd9\x9a\xdf\xd8'\xf9\x05\xc94]\x0f(\x11L\x0b \xb9\xe2\xb6\xec+\x08\x17E\x8e\x1e\xaf\x82C:\xa0\xcc\xf939\x02_M\x17\xc68\xa7\x8ai\x90`;\xda\x8f<,/\x1a\xb0#\xa5\xca\x80 k\xabDU\x7f*u\xa9>\xbc\xf7\xd1\x05#\xc3|k\xe9\xbb4\xf4\x9c@\x86\x97\xa4\x0f2\xee\xfd\x03\xf6\x9b\xb8\x0e'8\x98\xcb\x1ew~`\xad\xa6U\x7f\xb62\"\xaarK>\x0c\x91}\xbe\x89\"\x15T\x94&#!/\"+\xf72 \xc6\xe1\x98\x99\xb8o\xcc\xba\xe6(\xc0K\xe1e\x1a\xa3v\xd8\x0ct\x92\xd2\xe3\xbc\xa8T^\xa8\xcf\x85H*\xb5_\xd4X\xf3\xa6]\xf8\x94\"\xea\xc5\x17\xa0\x0bd\x1dq\xc3)\xb6f~\x8f5\xcco\xfbqG1\xb1G\x07\xaa\xf1R\xf4\xf5\xde\x0c\xd2\xc5\"Mx{\xeehRq\xa1\xd8\x8b\xc0!\x9a\x16\x81jJ\x8c\xf2\xd4\x94\xa6\x90\xc3e\xf5\xe8\xfa\xd7hO\x90\xfd\xa3<\xac\xe58\x8d44\xceS[\xf50\xd5\x1b%\xab\xf4\xce1\x96\xa2dY\xd0W\x9b\xea\x05\x99\x05\x9d:\x1e\xe2\xf45\x9f3\xd6\xed\xa2\xc8\x91*\x9d\xce:>\x8e\x92;4\xc5\xc1\x9d,!\x0f\xa0\xc4\xa3\x15x\xd2\x06\x1f4\xee\xbd.]\xea\xdd\xbf\xd9\xd5\x11\x02\xa0{ \x1e\xf8T1\xa8oi\xe8\xcb\xe8\xf1o\xa0\x94\x8b\xaf9\xc5\xd38\xca\xe7$Ta\x0d\xbeps\x88\x1e\xef\x08SW\xc9\xcd\n?\"\x01OI\xa8x\x15\x00z\x0c(t\x91\xa5\xcb4\x87c\xa0\xd5\xf2v\x80\xe07m.\x15O3\xbe\xb7G\xb3\"\xe0qE\xdcGZ\xe0,\x9f{\xc2\xd7\x11\xca)\xa6\x85w\xeaw\xc3\x7f\xa2\xb3^\xa2\x99p\x9b\xb8\xe2\xe3*R\x0dC\xc54\xa8\x03tB\xc6\xaf\x82G\x10\x89hy\xd6\xc5\xfc\xbclY\xf8\xd5s'\x8c\xe1Y\x93\x97_\xbf\x9c_\xfey39\xbb\xf8q}su=\xbe\xfeq\xd5)\x93\xcdF\xe3\xe2\xf2\xfc\xe2\xfcj\x03\x02\xe2\x9d\xf7s\x9d\x89\xb7\xa9 \xdd5\xb9\x17(\xcb\x15G0\xd7\xe1u\xd4Xjr\xa9\x00,\xcf2\x00v67\x96c2\x96\\\x92\x87G\x16r\xfe\xde\x1e\xeb#\x91t\xc6=W\xde\xe5\x98R\x1c\xccEk:\xa3\x95\xcdM\x82\x03{\x81\xed\xfa\x8c\x91\xe3\x98\xaf\xdd\xbd\x89\xdb5\x88%G\x12\xb94\x91\x1b\x1a\xf2\xb5\xccv2\x8b\xac7\xae\x06 T\xaaS\xf3D(U\"\x91\xac\x04kC`\xcb\x11I\xa0=\xbc-N\x14\xdd\xbe5\xc8Hv\xa87p\xa8\xec\x86]\x8e\x18jr\xd9?THS\x82@\x02\x0bL\xdc\x05X\x86\x08W-\x87\x0d\x10\x1e\xb1Y\xe0\xdd\x9b\xdc\x05|*\xac*\x80\xe4\xae\x1f\x7f\xb5\x15\x84^ \"\xaa\xd2\xb2/\x14j\x88\x99\xf2\n\xb3!\x00R\x81\xac\x04\x02Z\n\xf4;\x19B>\xcf \x9dwu\xf0;\x19\xa2\x17VCX\x17\x84~'C\xc0%\x1fx\x90\xf4\x93|\xc3\x98R\xd4%\x19B\xea\xe5!S!\x0c$\x87K\x84\x90\xfe\xf5s\xa6A\xd4\xe5\x19\xc0\xb3\x00\xaa\x0e(\xde\xa8\xc5\xa3gu\xe2\x9e\x07\xdd\x12 \x1a\x9d\xed7I\xbb\x82\xd1\xc6\x86\xa8N\x0e\x8c\xd0V\xe2\xd7\xb7\x8f\xd6\xb6\xd2\x1ez\xa2\xf8Z\x96@\x06~\x0d\xeb '\xa5\x01\x00\xf3\xd9s L}\xe5\x1f\xc4\x92C\xe5neT\x0c\xb9F2\x12\xb7\xae\x95@\xa4\xb8\xc2EQh_3yE\x06Y:\x04\xb4v\xa8c\xe2\x1f\x82\x8f\x1f\x04\x9fj\xa8c\xc7\xa0\x0e\x89\x80`B]m!\xea\x97\x10\x88v\x16A\x9f\x8d\xf4\x12\xa4\x9dR\xbb\x10,!\xea\x95b\xd9\xcf\x82\xb6I\x19\xachG\n\xdd\xfa\xc0g\xf8\xf4\x01\x7f<\x98\x91\x00\x1fM\x0fO\x8e\x8e\xc9\xa1(Y\xaf\xf4Z`\xbfK\xaf\x8e\xb2\x8b\xd7\xa3\xfb'+\xca\xf7\x8f\xf1\xdd\x03\x89\x8d`z\x12(\x87\x82\xb3\xac\xb0\xd7\x19\xd0\x93O\x87\xeff\x9f\xa6\xc1\xc1\x87\xc3\x0f\x1f\x0f\xde\x93\xe9\xc9\xc1\xe7\x93\xa3\xd9\xc1\xf1\xd1\xf1\xd1\x87\x8fG\xc11 \x1a\x80\x8a\xc66\x82T\x908\xba\x7f\xb4\x82\xfa9\xbf\x8f\x83\xf9\xbb\xfc\xf1!y\xff\xfe\xd7\xc9\xe1\xaf\xa7[\xfa)\xcb\xe7\xab\xfb\xf5,\xfb\x15d6q\xf8\xf5\xcb\x0c\x844\x89\xd7%\x04(\xe2\x89{\x95\xe3\x03\x1c\xe7\xa9\x8d?y;\x86Q]\xdb\xd7\xca\xde\xce\x94!\x17\xd5\xd5\xab\xc8\xde\xaa\xed\xc9\xd1\x14\xc5iz\xc7\xb4\xb3\x81\x8aL\xf6\x11\x1b\x92.>\\\xf7\x00h\x7f\xcd\xf1\x1b\xd8\xc8\xab\xb1#\x14\xd9,\xc6\xb7\xdc\xb4\xe8\xab\x05R\xf53.\xa6\xdd\x94\n\"\xd2\xba\xc9(\xa2\x9c\x94\x16H-\xe7\xf2e\x9a\xe4\xc6(\x0f\xcd\x8eL\x80\xdf!\x80\xaaI\xfb>\x8c\xdc\xd3&\xdf\x08\"\xf2(\x18r.\xe9\x9f\x13\xa1\x06Cv\x80\xe4\x0f\xb9\x84\xd6\x83SN\x82\x07\x16\xf9\x80P=c\xf5P\xd03\x03\xd1`\x082R\xac\xf9\xc6\x9aF\x82\xd6i\x91IW\xa9\x0e\x83\x9b+\x99\xdeu)\x11De\xb4\x9f\xf8?CFF\x88\x13\xf4WA\xb2\xf5H\x15\xfb\xbd\xbc\xf8\xd2 &2K\xcb\xe6Uho\xe5g5n\xc6 *\x12\xf2\xb8$\x01\xf3\xf6\xc4\xddR\xaa\xe5\xca7y0'\x0b\\\xef9\xab\xefg\xf7\xfb8\xfdv\xff;\xf4y\x90\x86\x06\xe5+>\xb0\x95\x96r\xe1\x0e\x1eBBq\x14\xefHM\x0e\xf6\xf9M\x91Yo\xb5\xf1\xdaF\x9e\xca\xda\xfbk\xe3\x16\xc4\x12gxA(\xc9*<\x1f\x08\xfb\xad=\x01\xdb\x00lL\x87\xae\xfej?/\xd5\xe5<\xf5\xf1B\xa3\xe4\x14-1\xadFe2\xbd\x10e$U\xe5\xeer\x94\x13\x9c\x05sy\x1d\xa8\x9a-Jl\x1b\xe4\xd7\x95\x08\xb9\x86\xd5\x13\x8a\xcb`\xf9\xe0\xbe\x92\xd3I\x02;H\xe8*ZD1\xce\xe2\xf5~U\x13H\x8e5\xbd\xa6\xbdF%\x0cV\x16\x06\xb0\xaf\x0cBM\xb0\xe2E\xed3\xa3\xaa\x8c\xe6\xbe\xaa\x04\x87p\xd9:\xc4\xc3\x1a\xc2\xbb*[\xfc/4\x99 \xd3_\xb7\xf6%\x9b8\x8e+\xe5\ne\xbe\x92\xec\xc6\xac*'-\xb2\x84_3c\xa4]\xf6P\x8dzDK\x1c*\x9d\x82\x93\xb0^\xaa\xa8Q\xa8\x91Ops\x83\x8d\xf1\xc0\xb5\x85\x18\xb2tN\xf4h\xaeH\xcb\xd7\xa5m\xe7\x98\xb3\xc5\x9c\x1e\xc6[\x05\x0e\xc9Y\x83\xa3\x92 \x07k\x12\x8b\x86c]\xd2\xa6\x8dZx\xda\x8ea*?\xd2\xf4\xf8\xd5@SbmJ\xb7\xa2<%\xd5\x8a\x05\x85\xfd*f\xb8VbBwJ\x1d\xa8\x1a@e\xebM\xa4|\x12\x99$\xd1\xc4\xc6h\x8a\xc3\xea\xc9\x92\xfa\x11\x8af\xa7U\x89\xaf-\x9d\xce\x99\xa8J\xff\x10\xd1yZ\xd0z\xff\x9b&k\x0d\xd0\xb1\x15MM/Y\xa3\x07\xbc\xe6fHU\xeb\xd3\xce\x1b\xda\xab\xcd\xe0*\xea\xb8\x85{\x15\xef\xffW\xf9\xa6\xc2\xdb\x1f5\xe6\xae\x1fR\xd65\xfc\xae\xa6t\xe6\xe8\xd1\xfa\xf4\xaaveu\"k\xd1\xd8\x8f\xc24yC\xb9\n\xe019\xb2\"\x15\xca\x99k^W\xdbf\xbc8\xf5=\xa6v\x15\xda\x95\xa6y}\xb6\x16\x9a\x087\x95\xb1\xfaLL;9\x9a\xc2\x94\xe4(I\xa9\x1aV(/\x82y\x1b/\x07[\x0dN\xf8\x9dW\xd3\x94\xce+CxZ\xd02\x18I\x13\n\xa3\x19\x7f\xa5\x86p^\x1d\xb2\xffX\xab\x9d\xb1\xfd\xca\x00(\xdb\xca\xe5\xddZT\xd7\xfc\xaa\xb0xEhc\x95\x80\x93p\x94f\x15\x1fH\xd0`\xfd\x90\x89\x0c\xeb\x9aG\\[*\xc8o5\x93\xe2\xad.\x167 KW\xe1\x9d\xfc\xa3\xf2\x0d+\x0e\xcb\x9b\xe3\xc3\xc37\xf6]\xbeJ\xb6\xb9\xd1\xb3\x1ch\x7f\xcfrV\xe2\xdf\x95\xd5\x8b-\x86\x8f.\xae\x1b\xa8\x1b\xccxe\xdd\\.\x15\xb4\x99\xdb\x8bL\x01=\xf6\xda\xba\x1b\x1dW[DC>\xaa\xc8K\x19y\xcf\x82\x11lg\x0e\x18\x1e\xe4\xcfGF\xe8\x87\xa8m;\xf9\x13\xe9\xd2$B\x7f 4Y,c~\x93c\x8e\xf2\xf0\xee\xed\xd8\x93m\x8b\xf86m6\xc3\x81\xd8\xc4\xe6E_\xc5\xedu\xe2K\"\xe6\x9a\xf2\xe3\x9bK\x81\xfa\xf3%\xcd\x17\xa9\xad\xb1\x1c\x9c\x9c\xfe\xbc`\x96\n&o\xe7\x94\xeb\x10\x13w\xe0K\xedK\xb1T\xe0\xc5g\x88\xea\x0b\x1c\xc7\xe9\x83T\xce2\x99\xdfE\xae\xb5[Q>\xe9CR\xdb\xc9m>\xae]o\xf5x+\x03y\xe7\x8cx\xfc3G\xc8\x08\x12\x0f\xe6\xf4\xca\x19\xa9\x98\xa9T\xa0\x0edmf`\xa8\xa3\xac\x03A\x00\x15i;\x81\x07\xab\xacs\x80.\xc6\x97\xd7\xffws\xfd\x7f\x17_;\x15\x9b\xa9}x~9\xf9\x9f\xc9\xd9\xf8\xfa\xfc\xb2\xdbwW_/\x7fN\xbe|\xed\xf8\xd5\xe4\xec\xe7\xd7\xab\xcem}\xf9qu}\xfe\xe7d|\xd6\xed\xb3\xf3\xbf\xcf\xba\xf27\xfe\xf6m\xf2}2\xbe\xfe\xda\xed\xb3\xf3\xff=\x9b\xfc\xe3\x87\xbf.Q\xed\xa3\x8b\xcb\xf3\x9f_\xcf\xc6g_:6\xf6\xe5\xfc\xec\xfa\xf2\xfc\xfb\xf7\xae\xb2\xfd\x1c\x7f\x9f\xfc \xe8h]\xce\xa8\xd7\xf0\xf2;+\xf5\xc76\x8a{6\x8fD\xfc\x9f<\x8a\xedP\x96\xc82)N\xcd\xafe+8\xe7\xf7\xd3f\xd1m\x94`\n\xa9\xcej\x9cC\xa7\xa6\x97\xcaC\xcfQH\xa6lI\x90\xad\xa2\x80\xad+gE\x12\xd0\xd6\x96\xb0\xbf55\xf7NM/\xc5j\x92\x07.F\x01\x8a\x92\x15\xc9\xbb\xcb\xa3\xe7\xe9\xa9\xf1\xad\xea\x9a\x84Ft-\xcc\xb7\x961(r\x9a\x86\x11N\xa4\xa0r?\x8f\x03\xdcUP>\xefO[o\x9a\x15\x11\x978\xa3k\xc9\x137\xda\xcaJ1\xeb\xdb\xb1I\xad7N\x8do\x05\xba\xa2A\xb1\xfcK\x10\x9e\xcd\xa28\xc2\x94 |\x9b\x11\xee\x86tlTj\x9dS\xc3;\xd1 \xf7zp,B \xe4-\xca\xeb\xd2o\xca\xd2X\x08\xbfH\xa2i\x91\xa3)N\xee\x94U\xec\xc8J\xa9\xcbN\xcd\xaf\x19C\xaa\xf6\x8f\xea\x87j7dd\x99\x91\x9c\xbbb\xac\x0b\xca\xba\x93rG\xb5^\xfb\x1c\x07=&u\xa97O\xcd\xaf\xeb\xe3\xf3a\x1e\x05\xf3\nN\xda\x87T\xb3^\x97e\x8e\x08\"IJ=e\xd8\xdb\x0ci\x85|j|kb\x87W;\xe5CXT\xa2\x12\xf3\x03R4\x0b\xe9\xbb\xd6}\x9e\x85?\x94\xa8| AE\xe5#}5}\x9a\xa1\xbb\xffM\x8e\xf2\xe86\xc1\xe2\x92\xf5\\\xb3\xea\xa0\x07\xf3\xfb\xc6\xe8\xa22\xcb\x957\xca\xa6\xe0\x88\x17\x8e\x17(2\xbf\xd0I\xa6Y\xcf\xdf[\x0b\xaa\x8b\x1d\xbc\xe6\xcbry\xc3y9\x11\x10M\x97(&+\x12\xcb%\x85?\xbc_m\x9c\x08\x8f\xf6m\x934\xdf\xcbd8\xf3\x8d<\xb9\xd0w)W\xae)\xf8e\xf5k\xfb\xb5\xea\xc2\x15\xe6\xab)\xc3].\x82\x83\xfaZ\xd59N\x8b\x84\x17\xd9c(+\xbe\xe3(\xa7\xa22\\\xf5@\xd4\x16X\xcb8\xb9\xc1\x81\xfbv\x93\x01\x17a\x1e\x8f\x1e>\x0e\xc6\x1a$UM\x8e \xce\x11\xc3\x05\x9d\xa7Y\xf4$\xf4gF\x02\x12\xad\\\x03\xa1\xac\xa0\xce\xfb\xa5}\x1d\x85\xde\x90\xb0\x10\xe1QN7|\xdc\xddx\x17\x87C\xe3PN\xd4\xba\xa9b\xbd/n\x12h\x08\xe4\x1c\x9b\x95\xbd\x97+\x8a\x93\x10ga\xd5\xbcH{\x97\xf3\xdd\xcb\x05\xce\xee\x0c\x11q\xe5\xa3~\xed\x9a\x0e\xac\xc3\xf2b\xb9L\xb3ZmZ\xce\xf9[\x19\x8b\x82)\xcd\xa2iA Z\xe05?2q\x10\x9c\x12\xa6\xdd\x93[\x12\xa2\xa9\xd8p\x976\xa1\xac?\x98&\x01\xf3[\x98\x9e\xcb\x89!SF\xbc\xf7\xd1\x05#\xc3|k\xe9\xbb4\xf4\x9c@\x86\x97\xe7\x0f2\xee\xfd\x03\xf6\x9b\xb8\x0e'8\x98\xcb\x1ew~`\xad\x7fV\x7f\xb62\"\xaarK>\x0c\x91}\xbe\x89\"\x15T\x94&#!/\"+\xf72 \xc6\xe1\x98\x99\xb8o\xcc\xba\xe6(\xc0K\xe1e\x1a\xa3v\xd8\x0ct\x92\xd2\xe3\xf6t\xa5\x19c\xf5\xd7f\x8eWe\xc6$\x08g\xd3\x88f8[\x97\x1a\x8c\x17\x1c\xd4k>1\x15\xfa\xf3(\xde\x999\x14\xef\xcc\xfcEbB\xd7<\xb0eFVQZ\xe4\xf1\xba5\xd5+yQN^\xa5\xb2\xb9\xcepp'6\xb5\x84\xe7\xa4W\x80D\xd9$\xe8\xaa\x0c\xa4\xbd\xaa\x8d\xb4\xfcl&[0\x8f\xc8J\xdc\x8e\x92\x16\xd4\xa7v\xd2\x04\xc4\x9d\xa0\xf4j\xbd\xc2\x17\xb6\xf5\xff\x94v\x9e\xaa\xe2\xa8\x02NX\xa5\x05\xf1\x88/Fz\x17\xa0\xb9\x0d\xf5\x02\x16\xf4\x8a\xd3Tr\xa9!Xnc\x94\x0bB\x00\xb12EIb#\xa7r\x94\xdc\xaa\x0bw\xf6g8\x8a\x8b\x0c\xb0\x84D\xcc\x0c/I\x12\x82:\xb2K\xafw1\xbaW?\xbe\xf7\xb2R\xed\xaf/\xc6W\xb0\xac\xf8\xfagW\xff\x9a\\\xf4\xf8\xec\xdbx\xf2\xdd\xfbY\xc5&\xf7\x95\xb3\x9f5\xb6\xb4\x06\xe9\x14\xeb\xc75\x0b\x8c\x8a$'~_\x8d\x1fHtI\x84owi\x13<\xf6\xaef\xc2He61\x0bZ\xde<\xd5\xa7I6\x1c\x9aM\xb2w\x95&\xb5H\xa3 M\xf2(T\xbb\x0f\xbc\xf1\xbb\x88\x9fF\x84\xe2\x16\xaeE\x94\xe7lrJ{\x94f($1^\x93\x10\x98[ha\x92\x0d\xbe&\x93\xec\x9d\x15\x97\xf2L\xd0\xc89\xd3\x19\xb6\x18r\xf5@\x95\x9eXT\x9d\x0b\xfdD\x92\x00/\xf3\"\xd6,I\xbd\xc5W\xb1\x90}DTz\x1e\x80\xb4b\x18\x8f\x82\x87\xbd\xfc\x0f}\xb2/\x8e\x8c\x18W\x122qM\xb0V\xd7\x9e^b?\x15\x0b\x19\xf5AYg8\x82\xb9\x0e\xaf\xa38R\x93K\x05`y\x96\x01\xb0\xb3\xb9\xb1\x8e\x92\xb1V\x92\xba\xac\xcfL\xc8\xdf\xdbc}$\x92\xce\xb8\xe7\xca\xbb\x1cS\x8a\x83\xb9hM\xa7\xa2\xb2\xb9Ip`\xaf\x8c]\x9f1r\x1c\xf3\xb5\xbb7\xe3\xba\x06\xb1\xe4H\"\x97&rCC\xbe\x96iJf\x91\xf5\xc6\xd5 \x11Fuj\x9e\xd0\xa2J\x08\x91\x95`m\x08l9\x94\x08\xb4\x87\xb7\xc5\x89\xa2\xdb\xb7F\x07\xc9\x0e\xf5F\xfc\x94\xdd\xb0\xcb\xa1>M.\xfb\xc7\xf8hJ\x10H`\x11\x85\xbb\x00\xcb\x10q\xa6\xe5\xb0\x01\xc2#6\x0b\xbc{\x93\xbb\x80O\x85U\x05\x90\xdc\xf5\xe3\xaf\xb6\x82\xd0\x0b\x842UZ\xf6\xc50\x0d1S^a\x1a\x03@*\x90\x95@@K\x81~g1\xc8\xe7\x19\xa4\xf3\xae\x0e~g1\xf4\xc2j\x08\xeb\x82\xd0\xef,\x06\xb8\xe4\x03\x0f\x92~\x92o\x18\x0c\x8a\xbad1H\xbd{\x0e\x84\xa9\xaf\xfc\x83Xr\xa8\xdc\xadT\x88!\xd7HF\xe2\xd6\xb5\x12\x88\x14W\xb8(\n\xedk&\xaf\xc8 K\x87\x80\xd6\x0eu\xcc\xd8C\xf0\xf1\x83\xe0S\x0du\xec\x18\xd4!\x83\x0fL\xa8\xab-D\xfd2\xf9\xd0\xce\"\xe8\xb3\x91^\x82\xb4SN\x16\x82e2\xbdR,\xfbY\xd06)\x83\x15\xedH\xa1[\x1f\xf8\x8c\x07\xea\x06\xf9f8zM\x89\x97^\xe7\xe0\x8f\x073\x12\xe0\xa3\xe9\xe1\xc9\xd119\x14U\xdb\x95\x86\x08\xec\xd7\xc9I\xa3.\x1bt\xf1zt\xfftK\x0e\x9f\xf0\x13->\x9c\xd0\xc7\x93\xc7\x938^\x9d<\x06\x9f\x9fh~\xff\x18\xdf=\x90\xd8\xc4\xb1/\x15q(8\xcb\"s\x9d\x01=\xf9t\xf8n\xf6i\x1a\x1c|8\xfc\xf0\xf1\xe0=\x99\x9e\x1c|>9\x9a\x1d\x1c\x1f\x1d\x1f}\xf8x\x14\x1c\x93\xa0\x01\xa8hl#H\x05\x89\xa3\xfbG+\xa8\x9f\xf3\xfb8\x98\xbf\xcb\x1f\x1f\x92\xf7\xef\x7f\x9d\x1c\xfez\xba\xa5\x9f\xb2|\xbe\xba_\xcf\xb2_Af\x13\x87\xdf@\xcc@H\x93x]B\x80\"\x9e\x02W\xd9\x88\xc7q\x9e\xda\xf8\x93\x17D\x18\x15\x9f\xf7\x9ci\xa0>m\x9c*i\xf8\x95\xd2\xac\xe1\xec\"qt\x7flE\xf9\xe1\xfdqx\x7f\xfc+\\-B\xfcT<<\x058\x0c\xe7\xf3O\xb7\x8b\xe2\xdd\"x\"\xef\x1c\x00\xd8W\xdf\xc3\x02P]5\x8b\xac\xb1\xda^ M\xd1,J\xb8B\xf4\x8cLn7\x12\xe9\xcb\x8a\xfe\x95I\xc6\x86\x0fdv\x92\xd8Au\x89\xe9\xbaq@;\x98\x8e\xdf\xc0\xb0\xa8\xb1#\x86\xc3,\xc6\xb7\\&}\x89A\xaa~\x86\xa8\xe7\x1aMi\xdfd\xc8SN*\xa9\xf6j\xf1\x99/\xd3$wB#\x8d\xe5\xee\x80S5\xef>x\x9ccN\xad\x15;\x02C\x1e\x05\x1f\xcem\x87\xe7\x04\xa6\xc1\x90\x1d\x18\xf9C.\xa1\xf5p\x97\x93\xe0\xc1O> \xca\x0e\xb1\xb8>\xe8\x99\x81h0\x04\x19!\xd6\x9chM#A\xeb\xb4\xc8\xa4\x0fV\x87\xc1\xcd\x95,\xaa\x91_J\x08Q\x19\x92(\xfe\xcf\xa0\x91a\xec\x04\xfdU\x90l=R\xdf\xa0\xcb\x8b/\x0dr\"\xff\xb5d@\x05 W~V\xe3g\x9c\xa0\"!\x8fK\x120GR\\]\xa5\x9a\xae|\x93\x07s\xb2\xc0\xf5\xbe\xb3\xba\x95v\x97\x92\xd3o\x8f\x00\x87\x8d\x08\xd2\xd0\xa0q\xc5\x07\xb6\x8a\xeaj\xa8D }w\xdc\xc2\xc7R\x8d\xdc\xc1CH(\x8e\xe2\x1d\xa9\x1c\xc2>\xbf)2\xeb\xa59^{\xcb\x13n{\x7fm\xdc(Y\xe2\x0c/\x08%Y\x85\xe7\x03\x91c\xac\xbd,\xdb\x00lL\x88\xaek\x81~+\x00\x97c\xda\xc7\xc3\x8f\x92S\xb4\xc4\xb4\x1a;\xca4C\x94\x91\xf0\x14\xd1\xac\xa8N%#\xc4\x1a-\xed\xbbC\xf1\xea\xec\xec\xf7u\xf15f5j\x06\xfc\x86v\xe77p\xe2M\xae\xfb`}U\xf1\xc9\x81\x9d\xd5\xdb\x8b\x1f\xd6wg\x08\xdc3Cb\x84`\x86\xe3\xbc+\x06\x8d\x93+\x00\x06\xdd\x1d\xf9\x8a\xcbn\xec\xe3\xb6\xfb>\xa0\x9c5\x97\x1b(i_7\xbd\xe2\x90\xd7\xe8\xc1}\xd0>\x82\xb7\xfd*\x9b\xc7\xd5\xc4D\xba\xc9\x1dQ\xe9\xe2\x9fW<\xf1\x9dF\xa4\xe1b\x03\x11\xe9\xe9\x98\x97.x\x13\x93\x17D\xa0\xe1[\x03\x11\xe8\xe5\x91W|\xef\x1a1\x8b\x1f\xbeM\xf9)\xbe\xady>\x7f\xc9v\xde\x8c\x98r\" N\x022Z\x10\x8aCL\xf1hu4\xe2\xf3|\xf4oe\xbc\xfe3\x92cx\xf4\xef\xd2\x9e\xffg$F\xfd\xe8\xdf\x0c\xda\xff\xbc\x11\x0d\xdc\x12\xed\xcb\xe7\xc5b\x81\xb3\xf5\xa9\xbeY$'8\x0b\xe6\xf2\xdaR9e\x14\x046\xf8\xaf\xcb\xc3ef~\xf6\x99UU\x1au_\x15k\xf3X*Ml\x003\xc5\x18\xd2\xf48>\x07-\xd7Fh\xc9\x8a{\xa3?\x80{\x83&7\xb0\xd6,\xc4\x03DW\xd1\"\x8aq\x16\xaf\xf7K\x1eh\xadR\xbb\xd1+\xe3%\x9b\x1a^UC\x92!\xbc\xa8\x1a\x96\x1e\x83\xbbN\x8b7\x19)\xdbgk\xab\x8c\x9fa\xa0(\xa9\xdey\xf3_h2\x13\x9e\x18n\xfa3\xe5\xb8\x91\xd7BV\xcfu\xf8%<\x95\xba\x7f\x19\xa1E\x96\xf0\xfbtL\xa4\xab~]I\x17\xc7qyn#S\xc7DOv%_v\xd0\x1e\xeb\x0c\xd9\xda\xa8|\xff\x87\xb1\xb5f\xff\xa8v\xad\xeda\x0d\xbc\xf6S\xd4\x0d\x88JB\x9c\x84\xa3r@\xdcDu\xfc\x1a\x93\xcb\xd4\xa0\xfe\xc9\x18MqX==R?B\xd1\xec\xb4\xca\xd8u}\x8cr}\xcb\x07&NB\x94\xa4F\xf4\xdf6\x08\xc8\xeb\x8d\xe8C\xca\x863\xbf\xe2(\x9d\xd5uIU$~\xae'\xc9\xf2\xffU'\xcc\x1a\x85i\xf2\x86J\xc4gB\xd9\xf3\x89\xc4\xc0\x93\x8e]\xa5\xf9q\x0b\xd5}4-(JRj@\x16\xd7nNh\x93\x11\xb79\x96C\x99\x89\xa5{\x0bK/\x94\xff2LI\xce\xf8\\`\x1a\xd4o\x0e\xab|_\xed\x91\x7f\xac\xd5\x0e\xcc~\xc5\xc1\x13\xd7NI\xafE\xdc\x11Eu \xac\n\x87W\x846\xbcH)\x91~\xa9\x880\xb82\x91ql\xdf\xf6\xad\x0f4m\x1at\xf5\xb4I\xa8m\xc9\xc9\xffSb O\xa2b\xde\xde\x1c\x1f\x1e\xbe\xb1\xef'U\xb2\xaf\x8d~\xc8@;I\x96\xadx\xff\x0e\xa0\xf6\xc6\x19>\xbatl\xa0\xae\xe2B\xf34\x0es\xa4NT\xc5\xe4\xdf\x8bL\xf1-\xf6S\xde\xcd\x8f\\]\xbb\xa4\x8e\xd8.\xdf\xe6\x91\xef@\x17\xc1\xb6\x80\x80\xd12\xfe\xf4\\\x84~\x88R\xaf\x93?\x91\xae\xd4!\xa6;B\x93\xc52\xe67\x12\xe6(\x0f\xef\xde\x8e=\xc9\xa7\xd2f\xcdp \xb6Ky\x0dT\xa1\xa6\xc4\x97D\xcc5\xe5\xf45\xfd\xc6\xfa\xf3%\xcd\x17\xa9\xad\xb1\x1c\x9c\xab\xfd\xbc`\x96\x1a&o\xa7X\xeb\x88\x0bw\x1cH\xedK\xe1J\xf2Z,D\xf5\x05\x8e\xe3\xf4A\x9a1\x99\xdb\xee\"\x17\xd8\x0f\x9e\xf8}\xf7\x8e\xf8C\xd7\xf6\xaaz\xbc\x85r\xbcsF<\xfe\x99#\x1e\xbc\xad\"\xae\xd8|q\xe9\x9e*z\xe0\xaeQ\x97\xa5\xb1\xb78\x1dd\x04\x89\x07sz\xe5\x8cT\xccT\n2\x07\xb2T10\xf2O\x96E \x80\x02\xad\x9d\xc0\x83\x15\x9a\xa9]s\xdf\xa5\xf6J\xed\xc3\xf3\xcb\xc9\xffL\xce\xc6\xd7\xe7\x97\xdd\xbe\xbb\xfaz\xf9s\xf2\xe5k\xc7\xaf&g?\xbf^un\xeb\xcb\x8f\xab\xeb\xf3?'\xe3\xb3n\x9f\x9d\xff}\xd6\x95\xbf\xf1\xb7o\x93\xef\x93\xf1\xf5\xd7n\x9f\x9d\xff\xef\xd9\xe4\x1f?\xfcezj\x1f]\\\x9e\xff\xfcz6>\xfb\xd2\xb1\xb1/\xe7g\xd7\x97\xe7\xdf\xbfw\x95\xed\xe7\xf8\xfb\xe4O@G\xeb\xea>\xbd\x86\x97\xdfW\xa9?\xb6Q\xdc\xb3y$\"\xdf\xe4\x99_\x87*=\x96Iqj~-[\xc19\xbfg5\x8bn\xa3\x04SH\xb1R\xe3\x1c:5\xbdT\xcez\x8eB2\xa5('\xd9*\n\xa2\xe4\x16\xcd\x8a$\xe0f\xacckj\xee\x9d\x9a^\x8a\xd5\x12\x0f\xbe\x8b\x02\x14%+\x92w\x97G\xcf\xd3S\xe3[\xd55 \x8d\xe8Z\x98o-cP\xe44\x0d#\x9cHA\xe5~\x0f\x07\xb8\xab\xa0|\xde\x9f\xb6\xde4\x0b\x04.qF\xd7\x92'n\xb4\x95\x95b\xd6\xb7c\x93Zo\x9c\x1a\xdf\ntE\x83b\xa1\x9c <\x9bEq\x84)A\xf86#\xdc\x0d\xe9\xd8\xa8\xd4:\xa7\x86w\xa2A\xee\xf5\xe0X\x1c\xb6\xcb\xdb\x80\xd7\xa5\xdf\x94\xa5\xb1\x10~\x91D\xd3\"GS\x9c\xdc)\xab\xd8\x91\x95R\x97\x9d\x9a_3\x86T)\x1c\xd5\x0f\xd5n\xc8\xc82#9w\xc5X\x17\x94e\x18\xe5\xc6V\xbd\x148\x0ezL\xeaRo\x9e\x9a_\xd7\xc7\xe7\xc3<\n\xe6\x15\x9c\xb4\x0f\xa9f\xbd\xaeR\x1c\x11D\x92\x94z\xaa\x92\xb7\x19\xd2\n\xf9\xd4\xf8\xd6\xc4\x0e/\xfe\xc9\x87\xb0(\xcc$\xe6\x07\xa4\x86\x14\xd2w\x86\xfb<\x0b\x7f\xd4J\xf9@\xe2W\xcaG\xfajz\xeb[w\xff\x9b\x1c\xe5\xd1m\x82\xc5e\xe1\xb9f\xd5A\x0f\xe6\xf7\x8d\xd1Ee\x96+o\x94M\xc1\x11\xaf\xa3.Pd~\xa1\x93L\xb3\xbc\xbd\xb74R\x17;x\xcdW\xe5\xf2\xa6\xeer\" \x9a.QLV$\x96K\n\x7f\xb4\xbbZ\xd9\x0b\x8f\xf6m\x934\xdf\xf7f8\xf3\xd0p\xb9\xd0w)W\xae)\xf8\xa5\xebk\xfb\xf5\xe0\xc2\x15\xe6\xab)\xc3\xd5&\x82\x83\xfaZ\xd59N\x8bDl@\xd2\xb9\xe6;\x8e\xf8\x8emZ\xbb/\xd4\x9a\xa6\xc48\xb9\xc1\x81\xfb\xb2\x8f\x01\x17a\x1e\x8f\x1e>\x0e\xc6\x1a$U\\\x8d \xce\x11\xc3\x05\x9d\xa7Y\xf4$\xf4gF\x02\x12\xad\\\x03\xa1,(\xce\xfb\xa5};\x83\xde\x90\xb0\x10\xe1\xe147|\xdc\xddx\x17\x87C\xe3PN\xd4\xba\xa9b\xbd/\n\xeb7\x04r\x8e\xcd\xca\xde\xcb\x15\xc5I\x88\xb3\xb0j^\xa4\xbd\xcb\xf9\xf6\xe5\x02gw\x86\xd0\xab\xf2Q\xbfvM\x07\xd6ay\xb1\\\xa6Y\xadT+\xe7\xfc\xad\x0c\xfc\xc0\x94f\xd1\xb4\xa0\x04-\xf0\x9ao\xdf;\x08N \xd3\xee\xc9- \xd1t\xcdQ\x906\xa1,\xc7\x97&\x01\xf3[\x98\x9e\xcb\x89!qD\xda\xd2\x84\xe8\x83\x10\x07\xa3j\x03\xd6\x11\x15\x0c\x93\xa4F\xa8\xdc\xdd\xf3\x15\xef\xb3\xef\xd8\x94\xc6\xc8\xf8\x03\xefF\x9a\x7f\x0b\xed97\x9fk V\xcf\xb3h\xef\x9aK\x1aq_\"\xef\x96\xa5\xf0\xe5\xe2\x96\x129\xb2\x1951\xf1\x0fw\"\xe3\x96\x04r*7\x03\x83\xc8U\xfa\xc8\xb7\xaf\x0d\xc2\xc3\x9d\xe1\n\x82\xa1\xab\x84\xde<\xd6\xa1d\x1b\xa0\x8b\xbb\xca\xb6QF\xaaa\x00\xd8\x15\xaa\xbf\xc4\x03\x8c\xf765\x8fj\xb5\xd0i\x97+2\x1c\xcclY\xd1zK6\x80\xfa|+\xd3\xdaS\x97\xc1yl\x84\x9a\xa7T0\xbd\x0d\xad\xc2\xf0\xe2\xa0@\xd4;\x94\x16\x0c\x92\xca\xff\x06P\x12[\xc5\xa6\xc1j\xdd\"\xe8\xd7\x0erF3 G\xeb\x05\x0c\x04\xac\xda\xc1\xf0\x03c\x80\xa1\xd0K\xd4\x01\x8c\x86e\xb44\x85V\xab\xa2\xb6\x94\xae]\x8fme\xf6Hnl\x90{\xcd\x82\xbf\x05\x84\xfcI\xe8\xe2\x01\xf5.p\xaa\xc3\xa3%\x86m\xb7\xcb\xf0\xd3w\x0e\x97\x85\xec-\xa5\x7f@\x97\xd1\x98J\xd9K\xe0m\x13\x0f\xa9E\xa3\x1f\x9e\xdd\xbc<\xc8\xbb\xf9\xa5\x1e`\x1f\xcb\x1f\x0f\x11\x1d\x01\x8b\x8f\x80\xee\x94\x8bg\xd8\x18\x89\x1eQ\x12\x1d\x81\x84EJl\x10+\xd1?Z\xa2o\xbcD\xdf\x88\x89\xde1\x13\xbd\xa2&z\xc7M\xf4\x8c\x9c\xe8\x1f;\xd1?z\xa2g\xfc\xc4\xa6\x11\x14\xdd4\xbcx\xacQ\x14\x80o\xcd_\xf6\x8d\xa3x\xb6H\x8a\xe7\x8e\xa5x\x8eh\x8a\x9d\x89\xa7x\x91\x88\x8a\x17\x8a\xa9\xd8\xa9\xa8\x8a\xd7\x11W\xb1\x83\x91\x15/\x1b[\x01\x8f\xae\x80\x9e\xab\xa9\x07z\xbe\xa6\x9e!c,\xe0\xbe\xe3@q\x16\xbd\"-\xa0\\\xea\xb3D\xb9\xb9B\xf8}m\xa2\xec\x1e\xff\x83\xccO\xbaMx\xf19g\x008\xf2\xb35P\xe9f\x98l<\x8f\x87\xa6%|\xd5\x83~y\xce\x08\xbb\x9aO\xba\xf7\xfb\x8c\xbf(\xc0q\xbcFA\x8c\xf3\xdcY\xc6Y~4\x80\xb8[X\x03\xab\x05\x8b\xc8\xbf\x8dH,\x0e\xe5\x99Ef.@\x10G$)\xd7\xc3\xf0 \xe9'\xf9\x86\xf6\x04\x99\x87\x90Y\xfc\xd6eIC\xac\xb5\x8cD\x87[q\x99\xa3\xda\xb6\xbc\x00k\xca4\x80\xaf\x01T!P\xd4Q\x87K\x03\xfd\x07Z\xa8\xb5u\xe15N\xadn\xf7\x1b\xa8\xddAjc\xc3\xd4$\xd8\x01\xa7\xa0\xd3}l;\x84Y\x9bq\x04\xb8\x04\xd0\xf0U#\xa2\xbd/\x96>\xc3\x05\x84\xae?&\x10\xa3\xe5$\xb7\x89\xf4\x03\x0d\x9c\xfe\xd2oh\xb8\x00\xc3\xcb\x04\x05\x84_\xb9\xe9h\xbf\xb9O\xef]=\xd7\xd5}\xb2A\xc7\xdd}\xfa\x17\xba\xa6#\xb2\xdd\xdcW)\xf2\xe8\xbc\xb8/0\xdfo$\x06\xc6\xf3f\n\x08fl\xc3u \xa3\xfd\xecQ\x19\xa3rp\x94a\xd5B\xd4\xb7\x08\xfd\xe4\xe5\x0cd\x92\xba\x93\x96\x8c\xe47\x94\xd8\x18\xc7y\x8a\xee\x92\xf4!A\x98\x8d\xdfo\xcc\x11r\x86k\xbc\xc4\xd61\x0c\xb12QAM?u\xee\xa12\x14\x1aY\x08\x1e\xd5\xa9\xb1F{\xbcrfD\xe7h\x16\xc5\x94d$Dw+e\xe1)\xc90M3{\x98\x99\x8caw\xc2\x05\x12P\x12R\xfa\xa6\xe6\xf9*\xc9DW;KBh\x9d\xb3\xae\x9fF\xa5<\xae/\x9d\xcdd\x04]\xfdJM(X\xdeA1\xd8\x86\x0c0\xbd\x028\x14\x11\xb8\x1b\x10j\x9d\xb0\xb7\x82!yw\xa8)\xeb\xa5&;\xd6\xf9\xbb9\xce\xe7\xc3\x8bJ\xe5]\xfb\\\x88\xa4R\xfbE\x8d5o\xda\x85O)\xa2^|\x01\xba@\xd6\x117\x9cbk\xe6\xf7X\xc3\xfcb\x15w\x14\x13{t\xa0\x1a/E_\xef\xcd ],\xd2\x84\xb7\xe7\x8e&\x15w\x8d\xbd\x08\x1c\xa2i\x11\xa8\xa6\xc4(OMi\n9\\V\x8f\xae\x7f\x8d\xf6\x04\xd9?\xca\xc3Z\x8e\xd3HC\xe3<\xb5U\x0fS\xbdQ\xb2J\xef\x1cc)J\x96\x05}\xb5\xa9^\x90Y\xd0\xa9\xe3!N_\xf39c\xdd.\x8a\x1c\xa9\xd2\xe9\xac\xe3\xe3(\xb9CS\x1c\xdc\xc9\x12\xf2\x00J\xae\"\xd50TL\x83:@'d\xfc*x\x04\x91\x88\x96g]\xcc\xcf\xcb\x96\x85_=w\xc2\x18\x9e5y\xf9\xf5\xcb\xf9\xe5\x9f7\x93\xb3\x8b\x1f\xd77W\xd7\xe3\xeb\x1fW\x9d2\xd9l4..\xcf/\xce\xaf6 \xdey?\xd7\x99x\x9b\n\xd2]\x93{\xc1\x83\xf4\x80\x87D%\x01\x0b\x90\x1a\xc2\xe3\xbep\x1c\x85\xa3\"\x11\xebE1n\xd9\xd8\x01|\xec\xe9J3\xc6\xea\xaf\xcdT\xb1\xca\x8cI\x10\xce\xa6\x11\xcdp\xb6.5\x18\xaf[\xa8\xd7|b*\xf4\xe7Q\xbc3s(\xde\x99\xf9\x8b\xc4\x84\xaey`\xcb\x8c\xac\xa2\xb4\xc8\xe3uk\xaaW\xd2\xab\x9c\xbcJes\x9d\xe1\xe0N\xec\x8d \xcfI\xaf\x00\x89\xb2I\xd0U\x19H{U\x1bi\xf9\xd9L\xb6`\x1e\x91\x95\xb8c%-\xa8O\xed\xa4 \x88;A\xe9\xd5z\x85/l\xeb\xff)\xed\x12Ig\xdcs\xe5]\x8e)\xc5\xc1\\\xb4\xa63Z\xd9\xdc$8\xb0\x17\xd8\xae\xcf\x189\x8e\xf9\xda\xdd\x9b\xb8]\x83Xr$\x91K\x13\xb9\xa1!_\xcbl'\xb3\xc8z\xe3j\x90@\xa5:5O\x84R%\x12\xc9J\xb06\x04\xb6\x1c\x91\x04\xda\xc3\xdb\xe2D\xd1\xed[\x83\x8cd\x87z\x03\x87\xcan\xd8\xe5\x88\xa1&\x97\xfdC\x854%\x08$\xb0\xc0\xc4]\x80e\x88p\xd5r\xd8\x00\xe1\x11\x9b\x05\xde\xbd\xc9]\xc0\xa7\xc2\xaa\x02H\xee\xfa\xf1W[A\xe8\x05\"\xa2*-\xfbB\xa1\x86\x98)\xaf0\x1b\x02 \x15\xc8J \xa0\xa5@\xbf\x93!\xe4\xf3\x0c\xd2yW\x07\xbf\x93!za5\x84uA\xe8w2\x04\\\xf2\x81\x07I?\xc97\x8c)E]\x92!\xa4^\x1e2\x15\xc2@r\xb8D\x08\xe9_?g\x1aD]\x9e\x01<\x0b\xa0\xea\x80\xe2\x8dZ\x9c\xd0\xc7\x93\xc7\x938^\x9d<\x06\x9f\x9fh\xfe\xf0\xfe8\xbc?\xfe\x15\xae\x16!~*\x1e\x9e\x02\x1c\x86\xf3\xf9\xa7\xdbE\xf1n\x11<\x91w&\xca\xee\xdd\xde\x81\xa4\xd7\xfb\xb8\x01N\x10\x89x\x05\xf2)A\x98O=\xa7\xd0\x9f\x8f>\x7f\xfc4\xc5\xc7\x07'\xb3w'\x07\xefO>\xe3\x83O\x1f\xf0\xc7\x83\x19 \xf0\xd1\xf4\xf0\xe4\xe8\x98\x1c\x8a\x92\xf5J\xaf\x05\xf6\xbb\xf4\xea(\xbbx=\xba\x7f\xb2\xa2|\xff\x18\xdf=\x90\xd8\x08\xa6'\x81r(8\xcb\n{\x9d\x01=\xf9t\xf8n\xf6i\x1a\x1c|8\xfc\xf0\xf1\xe0=\x99\x9e\x1c|>9\x9a\x1d\x1c\x1f\x1d\x1f}\xf8x\x14\x1c\x93\xa0\x01\xa8hl#H\x05\x89\xa3\xfbG+\xa8\x9f\xf3\xfb8\x98\xbf\xcb\x1f\x1f\x92\xf7\xef\x7f\x9d\x1c\xfez\xba\xa5\x9f\xb2|\xbe\xba_\xcf\xb2_Af\x13\x87_\xbf\xcc@H\x93x]B\x80\"\x9e\xb8W9>\xc0q\x9e\xda\xf8\x93\xb7c\x18\xd5\xb5}\xad\xec\xedL\x19rQ]\xbd\x8a\xec\xad\xda\x9e\x1cMQ\x9c\xa6wL;\x1b\xa8\xc8d\x1f\xb1!\xe9\xe2\xc3u\x0f\x80\xf6\xd7\x1c\xbf\x81\x8d\xbc\x1a;B\x91\xcdb|\xcbM\x8b\xbeZ U?\xe3b\xdaM\xa9 \"\xad\x9b\x8c\"\xcaIi\x81\xd4r._\xa6In\x8c\xf2\xd0\xec\xc8\x04\xf8\x1d\x02\xa8\x9a\xb4\xef\xc3\xc8=m\xf2\x8d \"\x8f\x82!\xe7\x92\xfe9\x11j0d\x07H\xfe\x90Kh=8\xe5$x`\x91\x0f\x08\xd53V\x0f\x05=3\x10\x0d\x86 #\xc5\x9ao\xaci$h\x9d\x16\x99t\x95\xea0\xb8\xb9\x92\xe9]\x97\x12ATF\xfb\x89\xff3dd\x848A\x7f\x15$[\x8fT\xb1\xdf\xcb\x8b/\x0db\"\xb3\xb4l^\x85\xf6V~V\xe3f\x9c\xa0\"!\x8fK\x120oO\xdc-\xa5Z\xae|\x93\x07s\xb2\xc0\xf5\x9e\xb3\xfa~v\xbf\x8f\xd3o\xf7\xbfC\x9f\x07ihP\xbe\xe2\x03[\xc9s5P\xa2\x84\xbe;n\xe1c)\x17\xee\xe0!$\x14G\xf1\x8e\xd4\xe4`\x9f\xdf\x14\x99\xf5V\x1b\xafm\xe4\xa9\xac\xbd\xbf6nA,q\x86\x17\x84\x92\xac\xc2\xf3\x81\xb0\xdf\xda\x13\xb0\x0d\xc0\xc6t\xe8\xea\xaf\xf6\xf3R]\xceS\x1f/4JN\xd1\x12\xd3jT&\xd3\x0bQF\xc2SD\xb3\xa2:\x95\x8c\x10k\xb4\xb4\x7f \xc5\xab\xb3C\xda\xd7\x0d\xd5\x98\xd5\xa8\x19\xf0\x1b\xda\xe5\xdc\xc0\xd14\xb9\x97\x83\xf5U\xe3 \xa4\xd6K\xbd<\xcf\xc18\xab,y\x81\xc3\xa8\xf7\"y\xd8\xa51C\xe0\x9eY8#\x043\x1c\xe7`\x0cj\xfe2\x10\x85~>\xb6a\xe1\xdb\xc7m\xec#{\xdb\x15\xb29I-X\xa4\x87\xdb\x15\x99.\xce524\xb2\xcb\xd84\xfcc 4=\xbd\xea\xd2\x7f\xaeQ{Y\x04\x1a\x8e1\x10\x81^\xeet\xc5q\xae\x11\xb38\xd1\xdb\x94\x9f\xe2\xdb\x9a\xe3\xf2\x97l\xe7\xcd\x88\xd9\x0f\x92\xe0$ \xa3\x05\xa18\xc4\x14\x8fVG#>\xe1G\xffV\xb6\xe7?#9\xd0G\xff.\xcd\xf1\x7fFr\\\xbf\x11\xa4o\x89v\xc2\xf3b\xb1\xc0\xd9\xfaT\xdf\xd9\x91\x13\x9c\x05sy!\xa8\xfcL o\x03\xfe\xba\xd0\xb4i\xd0\x05\xc5&\xa1\xb6%\xeaR7\xe5CT\x0c\xdb\x9b\xe3\xc3\xc37\xf6\x8d\xa0JB\xb2\xd1\x03\x19h\x0b\xc8\xb2\x9d\xee\xdf\xb8\xd3N9\xc3G\xd7_\x0d\xd4%W\xbc\xf8j\xdep)\xf7\"S\xc8\x87\xbd\xfa\xeaF\x07\x9a\xde\x83\x02G\xb8\x93o\xd7\xc7wZ\x88`{7\xc0\x00\x12\x7f\xc6*B?D\xf5\xd3\xc9\x9fH\x17\xaf\x10\xd3\x1d\xa1\xc9b\x19\xf3\xbb\xfer\x94\x87wo\xc7\x9e|Li\xb3f8\x10\xdb\x9c\xbc,\xa8PS\xe2K\"\xe6\x9ar\xf7\x9a\x1ec\xfd\xf9\x92\xe6\x8b\xd4\xd6X\x0eN_~^0K\x0d\x93\xb7\xb3\x8eu\x10\x82;4\xa2\xf6\xa5p%yy\x12\xa2\xfa\x02\xc7q\xfa \xcd\x98L\xf7v\x91k-j\xcb\x87\xdf$\xef\x08\xc9s\xed\x8b\xaa\xc7[;\xc6;g\xc4\xe3\x9f9\xe2\xc1\xdb\xaak\x8a\xcdW\x82\xee\xa9:\x00\xee\xb2mY\x1a{\xeb\xb5AF\x90x0\xa7W\xceH\xc5L\xa5Fq \xab\xf7\x02\x83\xe1d\xa5\x00\x02\xa8Y\xda <\xbf\x9c\xfc\xcf\xe4l|}~\xd9\xed\xbb\xab\xaf\x97?'_\xbev\xfcjr\xf6\xf3\xebU\xe7\xb6\xbe\xfc\xb8\xba>\xffs2>\xeb\xf6\xd9\xf9\xdfg]\xf9\x1b\x7f\xfb6\xf9>\x19_\x7f\xed\xf6\xd9\xf9\xff\x9eM\xfe\xf1\xc3_\xb9\xa6\xf6\xd1\xc5\xe5\xf9\xcf\xafg\xe3\xb3/\x1d\x1b\xfbr~v}y\xfe\xfd{W\xd9~\x8e\xbfO\xfe\x04t\xb4.x\xd3kx\xf9}\x95\xfac\x1b\xc5=\x9bG\"BL\x1e\xd6u(\\c\x99\x14\xa7\xe6\xd7\xb2\x15\x9c\xf3\x1bL\xb3\xe86J0\x85\xd4\xef4\xce\xa1S\xd3K\xe5\xac\xe7($S\x8ar\x92\xad\xa2 Jn\xd1\xacH\x02\xda\xda9\xf4\xb7\xa6\xe6\xde\xa9\xe9\xa5X-\xf1\xd0\xb6(@Q\xb2\"ywy\xf4<=5\xbeU]\x93\xd0\x88\xae\x85\xf9\xd62\x06EN\xd30\xc2\x89\x14T\xee\xf7p\x80\xbb\n\xca\xe7\xfdi\xebM\xb3f\xde\x12gt-y\xe2F[Y)f};6\xa9\xf5\xc6\xa9\xf1\xad@W4(\x16\xca \xc2\xb3Y\x14G\x98\x12\x84o3\xc2\xdd\x90\x8e\x8dJ\xadsjx'\x1a\xe4^\x0f\x8e\xc5!\xb9\xbcgw]\xfaMY\x1a\x0b\xe1\x17I4-r4\xc5\xc9\x9d\xb2\x8a\x1dY)u\xd9\xa9\xf95cHU\x87Q\xfdP\xed\x86\x8c,3\x92sW\x8cuAY\x99Pnl\xd5\xabc\xe3\xa0\xc7\xa4.\xf5\xe6\xa9\xf9u}|>\xcc\xa3`^\xc1I\xfb\x90j\xd6\xeb\xc2\xbd\x11A$I\xa9\xa7Pw\x9b!\xad\x90O\x8doM\xec\xf0z\x98|\x08\x8bZEb~@\xca*!}\x1b\xb7\xcf\xb3\xf0\x07\x9b\x94\x0f$\xec\xa4|\xa4\xaf\xa67\xbdu\xf7\xbf\xc9Q\x1e\xdd&X\\\xc3\x9dkV\x1d\xf4`~\xdf\x18]Tf\xb9\xf2F\xd9\x14\x1c\xf1\xd2\xe2\x02E\xe6\x17:\xc94+\xbe{\xab\x05u\xb1\x83\xd7|U.\xef\xc0.'\x02\xa2\xe9\x12\xc5dEb\xb9\xa4\xf0\x07\x80\xab\x95\xbd\xf0h\xdf6I\xf3}o\x863\x0f\xa1\x96\x0b}\x97r-o\xee_\xdb/\xde\x16\xae0_M\x19n\xfb\x10\x1c\xd4\xd7\xaa\xceqZ$b\x03\x92\xce5\xdfq\xc4wl\xd3\xdaM\x9c\xd6\xcc\x1d\xc6\xc9\x0d\x0e\xdc\xf7_\x0c\xb8\x08\xf3x\xf4\xf0q0\xd6 \xa9zcLp\x8e\x18.\xe8<\xcd\xa2'\xa1?3\x12\x90h\xe5\x1a\x08e\x8dm\xde/\xed\x0b\x0b\xf4\x86\x84\x85\x08\x8f\x83\xb9\xe1\xe3\xee\xc6\xbb8\x1c\x1a\x87r\xa2\xd6M\x15\xeb}Qk\xbe!\x90slV\xf6^\xae(NB\x9c\x85U\xf3\"\xed\x9d\xb8\xa8\x7f\x81\xb3;C\xccT\xf9\xa8_\xbb\xa6\x03\xeb\xb0\xbcX.\xd3\xacV\xbd\x94s\xfeVFl`J\xb3hZP\x82\x16x\xcd\xb7\xef\x1d\x04\xa7\x84i\xf7\xe4\x96\x84h\xba\xe6(H\x9bPV\xa8K\x93\x80\xf9-L\xcf\xe5\xc4\x90K!\x1e\xb9It\xc3u\xeeM\x96\xc6q\xb1\xf4u\xabO\xb3C-\x00\xbc\xff\xff\x96\xe6\x01\xc7\xb1V\x01jF\x94;\xcd\x11\xcd\xcdG\xe8\xf5G\x9d\xf5)\xa5Z#\xf4&W\xaau\x16\x918\xb4\x06\x8a#\x15h\x83\xe3\xd7@\xac\x9eg\xd1\xdee\x884\xe2\xbe\xdc\xd6-K\xe1KO-%r$\xf8ib\xe2\x1f\xee\xdc\xbe- \xe4Tn\x06\x06\x91\xab\x1a\x90o_\x1b\x84\x87;\xe9\x13\x04CW \xbd\xa9\x9dC\xc96@\x17w\x95m\xa3$M\xc3\x00\xb0+T\x7f\xd5\x03\x18\xefmj\x1e\xd5j\xa1\xd3\xae\xe0c8\x98\xd9\xb2\xa2\xf5V1\x00\xf5\xf9V\xa6\xb5\xa7T\x81\xf3\xd8\x085O\xa9`z\x1bZ\x98\xe0\xc5A\x81\xa8w(-\x18$\x95\xff\x0d\xa0$\xb6\x8aM\x83\xd5\xbaE\xd0\xaf\x1d\xe4\x8cf\x12\x8e\xd6\x0b\x18\x08X\x01\x80\xe1\x07\xc6\x00C\xa1\x97\xa8\x03\x18\x0d\xcbhi\nm\xcf:t\xedzl+%Grc\x83\xdck\x16\xfc- \xe4\xcfp\x16\x0f\xa8w\x81S\x1d\x1e-1l\xbb]\x86\x9f\xbe\x86\xb7\xac\xedn\xa9\x86\x03\xba\x9f\xc5T\xdd]\x02o\x9bxH-\x1a\xfd\xf0\xec\xe6}:\xde\xcd/\xf5\x00\xfbX\xfex\x88\xe8\x08X|\x04t\xa7\\<\xc3\xc6H\xf4\x88\x92\xe8\x08$,Rb\x83X\x89\xfe\xd1\x12}\xe3%\xfaFL\xf4\x8e\x99\xe8\x155\xd1;n\xa2g\xe4D\xff\xd8\x89\xfe\xd1\x13=\xe3'6\x8d\xa0\xe8\xa6\xe1\xc5c\x8d\xa2\x00|k\xfe\xb2o\x1c\xc5\xb3ER$\x08\xb3\xf1\xfb\x8d9B\xcep\x8d\x97\xd8:\x86!V&*\xa8\xe9\xa7\xce=T\x86B#\x0b\xc1\xa3:5\xd6h\x8fW\xce\x8c\xe8\x1c\xcd\xa2\x98\x92\x8c\x84\xe8n\xa5,<%\x19\xa6if\x0f3\x931\xecN\xb8@\x02JBJ\xdf\xd4<_%\x99\xe8jgI\x08\xads\xd6\xf5\xd3\xa8\x94\xc7\xf5\xa5\xb3\x99\x8c\xa0\xab_X \x05\xcb;(\x06\xdb\x90\x01\xa6W\x00\x87\"\x02w\x03B\xad\x13\xf6V0$\xef\x0e5e\xbd\xd4d\xc7:\x7f7\xc7\xf9|xQ\xa9\xbc~\x9e\x0b\x91Tj\xbf\xa8\xb1\xe6M\xbb\xf0)E\xd4\x8b/@\x17\xc8:\xe2\x86Sl\xcd\xfc\x1ek\x98_\xb5\xef\x8ebb\x8f\x0eT\xe3\xa5\xe8\xeb\xbd\x19\xa4\x8bE\x9a\xf0\xf6\xdc\xd1\xa4\xe2\x92\xb0\x17\x81C4-\x02\xd5\x94\x18\xe5\xa9)M!\x87\xcb\xea\xd1\xf5\xaf\xd1\x9e \xfbGyX\xcbq\x1aih\x9c\xa7\xb6\xeaa\xaa7JV\xe9\x9dc,E\xc9\xb2\xa0\xaf6\xd5\x0b2\x0b:u<\xc4\xe9k>g\xac\xdbE\x91#U:\x9d_\x88\x14%wh\x8a\x83;YB\x1e@\x89G+\xf0\xa4\x0d>h\xdc{]\xba\xd4\xbb\x7f\xb3\xab#\x04@\xf7@<\xf0\xa9bP\xdf\xd2\xd0\x97\xd1\xe3\xdf@)\x17_s\x8a\xa7q\x94\xcfI\xa8\xc2\x1a|\xe1\xe6\x10=\xde\x11\xa6\xae\x92\x9b\x15~D\x02\x9e\x92P\xf1*\x00\xf4\x18P\xe8\"K\x97i\x0e\xc7@\xab\xe5\xed\x00\xc1\xef\xce\\*\x9ef|o\x8ffE\xc0\xe3\x8a\xb8\x8f\xb4\xc0Y>\xf7\x84\xaf#\x94SL\x0b\xef\xd4\xef\x86\xffDg\xbdD3\xe16q\xc5\xc7U\xa4\x1a\x86\x8aiP\x07\xe8\x84\x8c_\x05\x8f \x12\xd1\xf2\xac\x8b\xf9y\xd9\xb2\xf0\xab\xe7N\x18\xc3\xb3&/\xbf~9\xbf\xfc\xf3frv\xf1\xe3\xfa\xe6\xeaz|\xfd\xe3\xaaS&\x9b\x8d\xc6\xc5\xe5\xf9\xc5\xf9\xd5\x06\x04\xc4;\xef\xe7:\x13oSA\xbakr/x\x90\x1e\xf0\x90\xa8$`\x01RCx\xdc\x17\x8e\xa3pT$b\xbd(\xc6-\x1b;\x80\x8f=]i\xc6X\xfd\xb5\x99*V\x991 \xc2\xd94\xa2\x19\xce\xd6\xa5\x06\xe3u\x0b\xf5\x9aOL\x85\xfe<\x8awf\x0e\xc5;3\x7f\x91\x98\xd05\x0fl\x99\x91U\x94\x16y\xbcnM\xf5Jz\x95\x93W\xa9l\xae3\x1c\xdc\x89\xbd1\xe19\xe9\x15 Q6 \xba*\x03i\xafj#-?\x9b\xc9\x16\xcc#\xb2\x12w\xac\xa4\x05\xf5\xa9\x9d4\x01q'(\xbdZ\xaf\xf0\x85m\xfd?\xa5\x9d\xa7\xaa\xc6\xaa\x80\x13V\xb0A<\xe2\x8b\x91\xde\x05hnC\xbd\x80\x05\xbd\xe24\x95\\j\x08\x96\xdb\x18\xe5\x82\x10@\xac\xcct\x92\xd8\xc8\xa9\x1c%\xb7\xea\xda\x9e\xfd\x19\x8e\xe2\"\x03,!\x113\xc3K\x92\x84\xa0\x8e\xec\xd2\xeb]\x8c\xee\xd5\x8f\xef\xbd\xacT\xfb\xeb\x8b\xf1\x15,\xb9\xbe\xfe\xd9\xd5\xbf&\x17=>\xfb6\x9e|\xf7~V\xb1\xc9}\xe5\xecg\x8d-\xadA:\xc5\xfaq\xcd\x02\xa3\"\xc9\x89\xdfW\xe3\xe7\x1a]\xf2\xe9\xdb]\xda\x04\x8f\xbd\xab\x990R\x99M\xcc\x82\x96\xf7W\xf5i\x92\x0d\x87f\x93\xec]\xa5I-\xd2(H\x93<\n\xd5\xee\x03o\xfc.\xe2\xe7\x17\xa1\xb8\xcbk\x11\xe59\x9b\x9c\xd2\x1e\xa5\x19\nI\x8c\xd7$\x04\xa6(Z\x98d\x83\xaf\xc9${g\xc5\xa5(\xcb\x15G0\xd7\xe1u\xd4Xjr\xa9\x00,\xcf2\x00v67\x96c2\x96\\\x92\x87G\x16r\xfe\xde\x1e\xeb#\x91t\xc6=W\xde\xe5\x98R\x1c\xccEk:\xa3\x95\xcdM\x82\x03{\x81\xed\xfa\x8c\x91\xe3\x98\xaf\xdd\xbd\x89\xdb5\x88%G\x12\xb94\x91\x1b\x1a\xf2\xb5\xccv2\x8b\xac7\xae\x06 T\xaaS\xf3D(U\"\x91\xac\x04kC`\xcb\x11I\xa0=\xbc-N\x14\xdd\xbe5\xc8Hv\xa87p\xa8\xec\x86]\x8e\x18jr\xd9?THS\x82@\x02\x0bL\xdc\x05X\x86\x08W-\x87\x0d\x10\x1e\xb1Y\xe0\xdd\x9b\xdc\x05|*\xac*\x80\xe4\xae\x1f\x7f\xb5\x15\x84^ \"\xaa\xd2\xb2/\x14j\x88\x99\xf2\n\xb3!\x00R\x81\xac\x04\x02Z\n\xf4;\x19B>\xcf \x9dwu\xf0;\x19\xa2\x17VCX\x17\x84~'C\xc0%\x1fx\x90\xf4\x93|\xc3\x98R\xd4%\x19B\xea\xe5!S!\x0c$\x87K\x84\x90\xfe\xf5s\xa6A\xd4\xe5\x19\xc0\xb3\x00\xaa\x0e(\xde\xa8\xc5\xa3gu\xe2\x9e\x07\xdd\x12 \x1a\x9d\xed7I\xbb\x82\xd1\xc6\x86\xa8N\x0e\x8c\xd0V\xe2\xd7\xb7\x8f\xd6\xb6\xd2\x1ez\xa2\xf8Z\x96@\x06~\x0d\xeb '\xa5\x01\x00\xf3\xd9s L}\xe5\x1f\xc4\x92C\xe5neT\x0c\xb9F2\x12\xb7\xae\x95@\xa4\xb8\xc2EQh_3yE\x06Y:\x04\xb4v\xa8c\xe2\x1f\x82\x8f\x1f\x04\x9fj\xa8c\xc7\xa0\x0e\x89\x80`B]m!\xea\x97\x10\x88v\x16A\x9f\x8d\xf4\x12\xa4\x9dR\xbb\x10,!\xea\x95b\xd9\xcf\x82\xb6I\x19\xachG\n\xdd\xfa\xc0g\xf8\xf4\x01\x7f<\x98\x91\x00\x1fM\x0fO\x8e\x8e\xc9\xa1(Y\xaf\xf4Z`\xbfK\xaf\x8e\xb2\x8b\xd7\xa3\xfb'+\xca\xf7\x8f\xf1\xdd\x03\x89\x8d`z\x12(\x87\x82\xb3\xac\xb0\xd7\x19\xd0\x93O\x87\xeff\x9f\xa6\xc1\xc1\x87\xc3\x0f\x1f\x0f\xde\x93\xe9\xc9\xc1\xe7\x93\xa3\xd9\xc1\xf1\xd1\xf1\xd1\x87\x8fG\xc11 \x1a\x80\x8a\xc66\x82T\x908\xba\x7f\xb4\x82\xfa9\xbf\x8f\x83\xf9\xbb\xfc\xf1!y\xff\xfe\xd7\xc9\xe1\xaf\xa7[\xfa)\xcb\xe7\xab\xfb\xf5,\xfb\x15d6q\xf8\xf5\xcb\x0c\x844\x89\xd7%\x04(\xe2\x89{\x95\xe3\x03\x1c\xe7\xa9\x8d?y;\x86Q]\xdb\xd7\xca\xde\xce\x94!\x17\xd5\xd5\xab\xc8\xde\xaa\xed\xc9\xd1\x14\xc5iz\xc7\xb4\xb3\x81\x8aL\xf6\x11\x1b\x92.>\\\xf7\x00h\x7f\xcd\xf1\x1b\xd8\xc8\xab\xb1#\x14\xd9,\xc6\xb7\xdc\xb4\xe8\xab\x05R\xf53.\xa6\xdd\x94\n\"\xd2\xba\xc9(\xa2\x9c\x94\x16H-\xe7\xf2e\x9a\xe4\xc6(\x0f\xcd\x8eL\x80\xdf!\x80\xaaI\xfb>\x8c\xdc\xd3&\xdf\x08\"\xf2(\x18r.\xe9\x9f\x13\xa1\x06Cv\x80\xe4\x0f\xb9\x84\xd6\x83SN\x82\x07\x16\xf9\x80P=c\xf5P\xd03\x03\xd1`\x082R\xac\xf9\xc6\x9aF\x82\xd6i\x91IW\xa9\x0e\x83\x9b+\x99\xdeu)\x11De\xb4\x9f\xf8?CFF\x88\x13\xf4WA\xb2\xf5H\x15\xfb\xbd\xbc\xf8\xd2 &2K\xcb\xe6Uho\xe5g5n\xc6 *\x12\xf2\xb8$\x01\xf3\xf6\xc4\xddR\xaa\xe5\xca7y0'\x0b\\\xef9\xab\xefg\xf7\xfb8\xfdv\xff;\xf4y\x90\x86\x06\xe5+>\xb0\x95\x96r\xe1\x0e\x1eBBq\x14\xefHM\x0e\xf6\xf9M\x91Yo\xb5\xf1\xdaF\x9e\xca\xda\xfbk\xe3\x16\xc4\x12gxA(\xc9*<\x1f\x08\xfb\xad=\x01\xdb\x00lL\x87\xae\xfej?/\xd5\xe5<\xf5\xf1B\xa3\xe4\x14-1\xadFe2\xbd\x10e$\xda\x1e\x0f\x0c\xb5\xf8)1\xe3VwK\xean\xb5\xfa\x83oj\xf7\x80\xad\xf5\xc0%\x84X\xb2tF\xf4j6\xa8\xe5'\xd1\xae\x89\xc5\xd1b\xa6\x1e\xc3\xcd`\x87\xc4\xac\x85Q\x83D\x005\xc9\x8b\x96y\xd6\xc0\xa6\xad\xeawZwa*?\xd2\xf0x3\xa0 \xf1\x0e\xa5GQ\xd6\x91\x1a\xc5\xc3\x85\x1d\x93g\xd8**\xa1'\xc5f\x94\xc5\xa0f\xf46\xa7b\x14\xb9(\xd1\xc0\x8e\xd0\x04\xa7\xe6]\x92\xfa\x11\xca\xa6\x87&\xc5\x97\x9eI\xe7H\x98\xd4\xdfetV.\xa9=\xff\xae\xcdj1\xf4\xc8\xcbM\x0d\xafX\xa1;\xbc\xe2\xaaG\xd5\xe7\xd3\x06\x1bzm\xed`\x93\xeb\xb8\xc3w\x93\xdf\x7f3\xbe1p\xfb\xcdB\xee\xf2\xaedS\xc3\xbb3\x95\xd3\xc0\x8c\xda\xdb\xcb\x9cJs#k\xd2\xd8\x8f\xd2\xb2xE\xb9\x08\xe0Q8\xb2\x06\x15\xaa\x999n\x8bm7\xbf8\xf4\xd7L\xec*n\x1bC\xf3\x8al\x1dn\"\xdc\x16\xc6\xea3\xb1\xed\xe4jJKR\xa3\xa2\xa4jY\xa1z\x99\xcc\xba\xfc\n\xa0\xd5\xc2\x84w\xb9\x9a\x94tf,\xe1\xc9\x926\xe1G\x1aP\x9aM\xf9+\xb5\x84ks\xc9\xfec\xa5|a;\xc6\x02h\xc6\xaae7-\xaa\xab|\x19(^\x10\xda:\x19\xe0\"\xdd++\xc3\xee\x110\xd8\xf9\xf3\xebE\xef\xb1\xbe\xfc\xb8\xb8<\xfd\xe3\xf8\xe8\xa4\xdfg\xa7\x7f\x9d\xf4\xc5\xef\xe8\xdb\xb7\xe3\xef\xc7G\x97_\xfb}v\xfa\x7f'\xc7\xff\xf8\x11\xafDd}tv~\xfa\xe7\xd7\x93\xa3\x93/=\x07\xfbrzry~\xfa\xfd{_\xda\xfe<\xfa~\xfc\x07`\xa2u\x01\xa3A\xcb+n\xac\xd8\x8fo\x15\x0f\x1c\x1e\x89\x88?y\xf9\xda\xa3\x10\x91gS\x1c\xba_\xcbQp\xcd;\xd2V\xd9uV`\n\xa9\xc7\xea\xdcC\x87\xae\x97\xcaB\xafQJ&\xecHP\xddf ;WN\x97EB;n\xe0\xf8hj\xef\x1d\xba^\x8a\xd3$\x0fU\xcc\x12\x94\x15\xb7\xa4\xeeO\x8f\xde\xa7\x87\xce\xb7jj\n\x9a\xd1\x95P\xdf\x9a\xc6dY\xd32\xcdp! \x95\xfe<\xce\xe0\xbe\x84\xf2}\x7f\xd8y\xd3\xae\x81\xb8\xc0\x15]I\x9c\xb8\xd2VZ\x8ai\xdf\x9eCj\xb9q\xe8|+\xb8+\x06\x14\xc7\xbf\x02\xe1\xe94\xcb3L \xc2\xd7\x15\xe1fH\xcfA\xa5\xd49t\xbc\x13\x03r\xab\x07\xe7\"\xe8A\xf6M^5vSU\xe6\x82\xf8y\x91M\x965\x9a\xe0\xe2Fi\xc5\x9e\xa84\xb2\xec\xd0\xfd\x9a!\xa4\xaa\xfd\xa8y0\xa7\xa1\"\x8b\x8a\xd4\xdc\x14cS\xd0T\x9a\x94\x1eU\xbb\xda9N\x06l\xeaFn\x1e\xba_\xdb\xeb\xf3n\x96%3\x83O\xda\x86T\xbb^\x17b\xce\x08\"EI#\x85\xd7\xbb\x08i\x81|\xe8|\xebB\x87\xd77\xe5KX\xd4\x9e\x12\xfb\x03R&\x0b\xe9\xee\xea1\xcb\"\x1e<\xd4<\x900\xa2\xe6\x91\xb6\x9a\xbe\xc1\xd0\xd3\xff\xaaFuv]`\xd1V\xbd\xd6\xa8\x06\xe0\xc1\xec\xbe#tf\xecre\x8d\xb2-\xb8\xc7K\xc5\x0b.2\xbb0\x08\xa6]\xc1?Z\xfd\xa9\x8f\x1e\xbc\xe4\xc7r\xd9\xd3\xbc\xd9\x08\x88\x96\x0b\x94\x93[\x92\xcb#E<\xa0_9N\x84E\xfb\xa6\x0d\x9a\xfb2\x19\x9f\xb9#O\x1e\xf4C\xc2\x95K\n\xde\x9e~\xe5o\xa4.La~\x9arto\x11\x18\xd8g\xd5\xe0:]\x16\xbc\xac\x1e\xe3\xb2\xc2;\xcfj*j\xc1\x99\x97\xa0\xbePZ\x86\xc9\x15N\xc2\xfdLF<\x84E,z\xf8:8\xd2LR\xf5\xe3\x18\xe1\x9ccxIge\x95=\x08\xf9Y\x91\x84d\xb7\xa1\x85\xd0\xd4L\xe7\xf3\xd2m@\xa1\x1d\x12\x1e <\xae\xe9\x8a\xaf\xbb\xab\xe8\xe1pl>4\x1b\xd5VUl\xf6E\xef\x80\x16A\xc1\xb5i\xf8^.(.R\\\xa5\xa6z\x91\xfa\xae\xe6\xde\xcb9\xaen\x1c1p\xcd\xa3~\x1d\xda\x0el\xc2\xea\xe5bQVV5Z\x8e\xf9\x1b\x19\x81\x83)\xad\xb2\xc9\x92\x124\xc7+~e\x12\x008!L\xba\x17\xd7$E\x13\xe1p\x97:\xa1\xa98X\x16 \xb3[\x98\x9c\xab\x89#7F<\xd2It\xc5e\xeeUU\xe6\xf9r\x11\x9b\xd6\x98d\x87j\x00\xf8\xfc\xff%\xd5\x03\xces-\x02\xd4\x8eh\x1c\xcd\x19\xad\xdd\xf1\x10\xf6\xa3\xee\xdd\x94P\xb5\x00\xbd\xaa\x95h\x9df$O\xbd\x81\xffH\x05N\xf1\x8b R\xe0I.\xce\xfcL$+]\xf5w\xee\xab\x16\x08\n\xa8\xa15R\xe8vg\x8aF\xf7\xa4\xc1\xd8v\xc1G\xe5-1\x08\xaf\xe9]\x96\x86O_X\xd2()\xf3\x9cp\xc3\x89\xa1-u\x85\x07 #\x80\xaf\xb6\xb2 \xfa\xde%\x80\xa8r\xc0\x06\x82\xb3a\x94X\x80\x1a\xef^\xac>\xa1\xdfc\xd3(#\xe7\x0f\xa2\x8e\xb4\xb8\x0b\xed1\x9d\xcf\x16\x13\xcd\xabl:\xb8\xac\x94\xe6x,Wy\xc3T\xc4\xd2\x8d\x1b\x8a\x02 \x9b\x1a\x98\xf8G8WsC\x04\x05\x85\x9b\x03A\x14\xaa\xee\x14\xf3k\x83\xf8\x81\x83I\xbc 6\xf4\xa5\x10\xc7Ru\xc7\xa2m\x84)\xeeK\xdbZI\xb7\x8e\x05\xe0\x17\xa8\xf1*\x160\xdc\xbb\xd0\"\xa2\xd5\x03\xa7[\x91\xc9q1\xb3aA\x1b\xadJ\x01\x9a\xf3\x8dl\xebH\xe9\x89\xe0\xb5\x11j\xdfR\xc1\xe46\xb4\xd0\xc4\x933\x05\"\xde\xa1\xb0`,1\xfe7\x82\x90\xd8(oZ\xa8\xda\x1aA\xbf\x0e\x80s\xaaI8\xb7\x9e@A\xc0\n:\x8c\xbf0FX\n\x83H\x1dAixVK\x9bh\x7f\x16i\xc8\xeb\xb1\xa9\x14+\x89\x8d\x8f\xe5Q\xb5\x10\x1f\x01\xa1x\xc6\xbax@\xb3\x0b\xdc\xea\xf0h\x89q\xc7\xed\xb3\xfct[\xe5\xa6V\x7f\xe2\xaen\x04\xea\xb7\xe3\xaa\xd6/\x19\xef\xdbxH\x1d\x1a\xe3\xec\xd9\xce\xfeHQ\xe7\x97z\x80s,\x7f\x02\xea)\x17\xcf\xb81\x12\x03\xa2$z2\x12\x16)\xb1F\xac\xc4\xf0h\x89\xa1\xf1\x12C#&\x06\xc7L\x0c\x8a\x9a\x18\x1c710rbx\xec\xc4\xf0\xe8\x89\x81\xf1\x13\xebFP\xf4\x93\xf0\xe2\xf1FQ\x00\xbeu\x7f94\x8e\xe2\xd1\")\x1e;\x96\xe21\xa2)\xb6&\x9e\xe2I\"*\x9e(\xa6b\xab\xa2*\x9eG\\\xc5\x16FV\xf3\xa2_\xde3\xc2\xba\x0fJ\xf3~\x87\xe1\x97%8\xcfW(\xc9q]\x07+U\xcb\x8fF w\x03g`u`\x11i\xd4\x19\xc9\xc5\xa5<\xd3\xc8\xcc\x04H\xf2\x8c\x14\xcdy\x98GL\x04\xe1u\x97h\xf4\x10\x8c\x97i\x16\xe4N\x1fz\xbe\xf0\xe3w\x8a&\xab\x1d\xb4\\\xa4\xfa\xdf4\x9b\x93\x9a\xe2\xf9\xa2\xde\xd1\xee3Q\xf72\xdc\x17V\xa4m\xe5\xb2s\xe1\xb4\xf4\x93\x01>aC\xcf\xd7\xc2\x93\x90^1\"b\x82\x13\xb8\x80\x90\xb1\x88\x18\xd8]\xc6\x96\x18h\xb1\xc7d\x1c\x13\xd9c\x9f0 *c\x05HA\xab\x15\xefW(\xf1\x05\xd14Y\x8dH\x11L\n y\xe2\xf6\xf8\x15\x84\x89\"WOT\xc0!\x1dP\x16\xfc\x99\\\x81\xcff\ns\\S\x854\x88\xb0-\x9dG\x1e\x96\x97\x8d8\x91Rd\xc0\x88\xf5U\x0b\xb3\x1f\xa3v\xd8\xc7\x0f1\xb8`\xce0\xdbZ\xda.-9'8\xc3\x0b\xf2'\x15\xb7\xfe\x01\xfe&.\xc3 Nfr\xc6\x83\x1fx+\x9e\xd9\xcfFV\x84I\xb7\xc4\xc3\x11\xd9\x17\xdb(R@ee\xb1'\xe8E\xe46|L\x82ax\xc4T\xdc7\xa6]k\x94\xe0\x85\xb02\x9dQ;l\x07\x06A\xe9u^\xa29\xbe!r\xa9\xab\xdc=\xa6\xae\xe4\xc6&+tG\xaa\xc8\x8c\xe1\xd4\xf7\x03\xa8\xd2\xbdP\xd9\xd8*\xbe\xca\xb0\xa5\xb4u\x81\xafqV\xd4\xd48\xcdx\xe1\xd9\xaew\xf6\x15.\x12\x12\x88>\xbb4\xdc\xae\xbc\x92\xc0\x0c\xdf\x12c$\xc9a\x1eSF\xd5\x914#~\xb1\x90\x15\xb7e~k\x97!h?'\xa7\xec\xe4\xfd\x97\xe4tEdJ\xb7\x08\x05;\xb7\"\x8d\x11\x96\x11hV5\x8b\xf6\xd3\xc4\xa3a\xc5R\x91=~a\xd6\xbb\x98\x97\xb7\xbe\xae\xd4\xcd%\xcf(\x9d\xaaZ\xe0\x86ED8c\"\xc2\xe6 \xc0\x86\x82\xd8O\x8f\x7f\xe5\xd5\x0e\xdc\xb2JQ\xb8\x03\x1e\xe4\xccFB\x19\xac\xb9\x88E2l\x94\xa6\x88\x90\xeb\xa094\x9c\xc1\x04\x05\xe2\n$\xd8iK8\xd3\x0d\x922\xde\x00\xdb\x87P\xab\xd4 \x8cC\xf2\x9f\xcf\x83G\x06\xb2\x8dC\xc2x'\xf9\x14\x84W\xb6\xeb\x00\xc5\xf9\x14\x0e\xf4\x003g\x08\xd5#\xc4z\xf4\xdb5#-\x84!\xb4\xae\x15\xec\xe1](\x01\x92UP\xe1\x98\xed\xae,\xa0h\xec\x96\xc0\x90\x99\x04\xe9J\x04\xd4\x97\x08AB\xb5\xc5\x03\\\x1a\x08.'\xd0f\x82\xb7\xc5\xa3g*\xae;\xd1\xe3P\x078\xce\xb4\x90\x1e\xaeIM`@>\xc1\xb4)\xda>^\x8d\xa1[\x11z\xe9 \x0c\xa7|\xe4E2\x8c\xf25\xf5 r/!7\xf9\x9d~Pc\x9c\xb5\x9c@\xc7;q\xb9\xa3\xda6|\x00k\xd34\x82\xad\x01\x14!P\xae\xa3\x1e}\x11\xe3\x17Z\xa8\xe3\xba\x88*\xa7\xce\xb4\xc7\x15\xd4\xf6pjm\xc5\xd4\x06\xd8\x83OI\xaf\x96s[\xc4\xb3.\xe2\x08\xd0\xe7\xd0\xf1U+\xa2}(/c\x8a\x0b\xc8\xba\xe1<\x81(\xad \xb8u\xa8\x1fi\xe1\x0c\xa7~M\xc5\x05X^.V@\xf0\x95NG\x7fsB\xed\xbb\xda@wB ;\xd0\x9eP\x8d\xee\xedO\xc8\xfd\xaf\xdd\xb9\x153\xfa\xb8!\xfe\x02\x19\xdf:\x1bI\xdb>z8\xc5^3\xabM<\xb4\xac\xb7\x8a\xd0\x9f\xbc\x0e\x81\xcc.\x0f\xc2\x92!\xf8\x8e\xda\x18Gy]\xa2\x9b\xa2\xbc+x\xe9Z\xf4\x8dY0\xc18\x8b\xa7\xf0\xf9\xc28\xd6d\x18\xa8\x95\xab.,TjA+} \"\xf34\xaf\xd1k^\xf22\xa334\xcdrJ*\x92\xa2\x9b[\xa5\x9a)\xa90-+\x7f|\x98\x0c>\x0f\xb2\x0bD\xa0\x04\xa4\x04\x85e\xb2*\xca\xc4T\x07k9\xa0\xa6r\xb2u\x8dT\xf2\x80\xbcr:\x95\xa1ov\xe7P(\xb3\xa2\x8bb4O\n0/\x02\xb8\x14\x11x\x1a\x10\xea\\\x8dw\xa2\x18\xf9t\xa8-\x1b\x85&'6\xf8\xbb\x19\xaeg\xe3\x93\xca\xe8`\x909\x11\x85Q\xb4E\xad\xb5h\xbeDL(\xa2Ax\x01\xa6\x00\x8b\x883\xc7\xf5\xb3F\xfe5\x1b\x98\x17\x9d\x0e\x87\x1f\xb1GG\x98\xf1\xe6\x05\xf6l&\xe5|^\x16|\xbcp\x18\xa8\xe8\xd6\xf6$\xec\x10C\x8b\x083EFs\xddIK\xc8\xad\xb0zt\xe9j\xf4Z\x80\xfd\xad\xb9e\xe5|\xda\xd3\xac ^\xb7\xaa\x87\x89\xde\xac\xb8-o\x02k)+\x16K\xfals\xb4 \xbb\xa0\xd7\xc4\xc3,H\xfb9a\xd3.\xaa\x13\xa9\x9a\xe7\xbc+SV\xdc\xa0 NndUy\x00$\x1ef\xc0\xb3-\xf8\xa2 ;\xa9t9\xff\xb8\x97\xaa'\x0b\x80\xe6\x81x\xe0[\xc5!\xbe\xa5\xa2o\xc2\xbe\xbf\x81r%\xbe\xd6\x14O\xf2\xac\x9e\x91T\xc5#\xc4\xe2\xc4!r\xbc'\x9b\xfaR\xee\x16\xf8\x19Ix.\x81aU\x00\xe01F\xa1\xb3\xaa\\\x945\x9c\x07Z,o\x86\x11\xbc\x89\xe9B\xe14\xe5N9Z-\x13\x1e\x10\xc4m\xa49\xae\xeaY$\xee\x1c\xa1\x9ab\xba\x8cn\xfd~\xfc?\xd6\xe9*\xd9T\x98M\\\xf0q\x11\xa9\x96\xa1B\x1a4\x01:\x93\xe2'o\x13\"\xc3\xdc\xd9\x14\xf3\x8b\xae\xc52.\x9e{\xf1\x18\x9e\xeex\xfe\xf5\xcb\xe9\xf9\x1fW\xc7'g?.\xaf..\x8f.\x7f\\\xf4JA\xf3\xc18;?=;\xbdX\x03\x80x\x17\xfd\\\xa7\xd0\xadKH\x7fI\x1ee\x1ed\x06\" \x8c\xcc)@N\x07\x0f\xd8\xc2y\x96\xee-\x0bq^\x14\xeb\x96\xad\x1d\xc0\xc7\x91\xa9t\xf3X\xfd\xb5\x9d\xe3e\xec\x98\x02\xe1j\x92\xd1\nW\xabF\x82\xf1\x82\x83\xfa\xcc'\xb6\xc2p\x1c\xc5;7\x86\xe2\x9d\x1b\xbfLlh\xcb\x02[T\xe46+\x97u\xbe\xealu#/*\x88\xab\x146\x97\x15Nn\x84SKXN\xfa\x04H\x94N\x82\x9e\xca@\xd2\xcb\x1c\xa4cg3\xda\x92YFnEw\x94rIcb\xa7,@\xd8 H\xcf\xd6*|b]\xff?R\xcfSU\x1cU\xb0\x13ViA<\xe2\x8b=\xed\x05h\xbb\xa1\x9e@\x83^p\x98\x8a.\xb5\x04\x1b7Fs \x04\x00kR\x94$o\xe4V\xce\x8ak\xd5pgg\x8a\xb3|Y\x01\x8e\x90\x88\xa9\xe1\x05)R\xd0D\xf6\x99\xf5>J\xf7\xe2\xc7\xf7AZ\xaa\xfb\xf5\xd9\xd1\x05,+\xde\xfe\xec\xe2\x7f\x8f\xcf\x06|\xf6\xed\xe8\xf8{\xf43C'\x0f\xa5s\x986\xf6\x8c\x06\x99\x14\xef\xc7\x96\x06F\xcb\xa2&q[\x8d_H\xf4I\x84\xefNi\x9by\xec\x9d\xa5\xc2\x88\xb1\x9b\x98\x06m:O\x0d\x19\x92-\x87\xf6\x90\xec\x9d1\xa4&i/)\x8b:K\x95\xf7\x81\x0f~\x93\xf1\xdb\x88Tt\xe1\x9agu\xcd6\xa7\xd4Ge\x85R\x92\xe3\x15I\x81\xb9\x85\x1e$\xd9\xe2k#\xc9\xdey\xf9\xd2\xdc :1g2\xc3\x17C\xae\x1e\xa8\xd0\x13\x87\xaaS!\x9fH\x91\xe0E\xbd\xcc5JRn\xf1S,\xc4\x8f\x88\x1a\xcb\x03\x90V\x0c\xc3Q\xe0\xf0\xba\xfeM\xdf\xec\x8b+#\x86\x95d\x99h\x0d\xac\xc5ud\x96\xd8O\xc5AF}\xd0\xd4\x19\xce`\xa6\xc3\xf3(\x8e\xd4\xc6R1\xb0\xb9\xcb\x00\xe8\xd9\xdaYG\xc9Y+I5\xebs\x03\x8a\xcf\xf6\x91\xbe\x12)\xa7\xdcr\xe5S\x8e)\xc5\xc9L\x8c\xa6SQ\xd9\xde$8\xf1W\xc6\xb6w\x8c\\\xc7\xfc\xec\x1e\xcd\xb8\xb6X,1\x92\x9c+\x0b\xe9\xd0\x90\xafe\x9a\x92\x9bd\xed\xb8\x1a%\xc2\xc8\x86\x16 -2B\x88\xbc\x00\xad%\xb0\xe1P\"\x90\x0fo\x83\x1bE\x8f\xef\x8d\x0e\x92\x13\x1a\x8d\xf8i\xa6a\x9bC}\xdaX\x0e\x8f\xf1\xd1\x90 ,\x81E\x14n\x03[\xc6\x883m\x96\x0d\x90=\xc2Y\x10\xf5Mn\x03\x7f\x0cT\x15\x83\xa4\xd7\x8f\xbf\xda\x08\x87\x9e \x94\xc9\x189\x16\xc34\xc6Ny\x86i\x0c\x00\xaa@Z\x02\x015\x05z\xc9b\x90\xcf#P\x17=\x1d\xbcd1\x0c\xe2\xd5\x18\xda\x05\xa1\x97,\x068\xe5#/\x92a\x94\xaf\x19\x0c\x8a\xfad1H\xb9?\xd0\xfa\xd7}~sGr\x17\xc6\xb1T\xc4\xb1\xd8\xd9\x14\x99\xeb\xcd\xd0\x83Oo\xdfO?M\x92\xdd\x8fo?\xfe\xbe\xfb\x81L\x0ev?\x1f\xecOw\xdf\xed\xbf\xdb\xff\xf8\xfb~\xf2\x8e$-\x86\x8a\xc1\xd6b\xa9\x00\xb1\xff\xeb\xde\xcb\xd4\xcf\xf5\xaf<\x99\xbd\xaf\xef\xef\x8a\x0f\x1f~\x1e\xbc\xfd\xf9pM?U\xf5\xec\xf6\xd7jZ\xfdL*\x1f9\xbc\x031cBY\xe4\xab\x86\x05(\xe3)p\x86#\x1e\xe7u\xe9\xc3O6\x88p\n\xbe\xe8=\xd3Hs\xda\xbaU\xd2\xecWB\xd3\xe2s\x08\xc4\xfe\xafw^.\xdf}x\x97\xfez\xf73\xbd\x9d\xa7\xf8ay\xf7\x90\xe04\x9d\xcd>]\xcf\x97\xef\xe7\xc9\x03y\x1f`\x80\xff\xf4=.\x03\xccS\xb3\xc8\x1a\xb3|\x81\xb4D\xd3\xac\xe0\x021\xb22\xb9\xde(\xa4-+\xe6W&\x19;>\x90\xd9I\xc2\x83\x1a\"3\xd4q@\x1b\x98\x81\xdf\xc0xa\xa1#\x96\xc34\xc7\xd7\x9c&\xdd\xc4\xa0T?C4\xd2FS\xea7\x19\xf2T\x13#\xd5^\x1d>\xebEY\xd4A\xd6He\xb9=\xcc1\xd5{\x8c=\xc15\xa7\xce\x8a=\x19C\xee\x05\x1eA\xb7\xc3c2\xa6\x85\x90\x9f1\xf2\x87\x9cB\xef\xe5.\x07\xc1\x83\x9fb\x8ch&\xc4c\xfa\xa0GfD\x0b!\xc8\n\xf1\xe6Dk\x18\x05Z\x95\xcbJ\xda`6\x1b\xc2X\xc9\xa2\x1a\xf5\xb9d!jB\x12\xc5\xff\x19kd\x18;A\xff\\\x92j\xb5\xa7\xbeA\xe7g_Z\xe0D\xfek\x83\x80\n@6~f\xe1sT\xa0eA\xee\x17$a\x86\xa4h]\xa5\x866\xbe\xa9\x93\x19\x99c{\xee\xbcf\xa5\xdf\xa4\xe4\xf0\xbb+ \xa0#\x922uH\\\xf1\x81\xaf\xa2\xbaZ*YA\xdf\xbf\xeb\xf0\xc7S\x8d<\x80CJ(\xce\xf2-\xa9\x1c\xc2>\xbfZV\xde\xa69Q}\xcb\x13n\x07\x7f\xedt\x94,p\x85\xe7\x84\x92\xca\xc0yW\xe4\x18k+\xcb\xb7\x00[\x1b\xa2\xefY`\xd8 d\x98\x0e\xb1\xf0\xb3\xe2\x10-05cG\x99d\xc8*\x92\x1e\"Z-\xcd\xad\xe4d\xb1\xe6\x96\xb6\xdd\xa1\xfc\xeam\xec\x0f5\xf15\xcf,h\x0e\xfe\x8dm\xce\xafa\xc4\xbbLw6W\xbf\x98\x18uN\xd6\x14\xe75x\xb6\x0c\xab\x1c8]\x83\xed\xf8q\xad\xf7\xf1y\xd0\xba\xbb\x02\xf0\xa0\xbf)o\x18\xed\xceY\xee\x1a\xf0#\xd2i\x19\xdd@J\x87\x1a\xea\x86In\xc1\x83[\xa1C\x08\xefZV>\x9b\xab\xcd\x13i(\xf7\xe4J\x1f\x0b\xdd\xb0\xc5\xb7\x9a#-#\x1b\xc8\x91\x81\xa6yc\x84\xb7y\xf2\x84\x1chY\xd7@\x0e\x0c\xb2\xc9\x0d\xeb\xdb\x02\xe6\xb1\xc47I?\xc5\xd7\x96\xed\xf3O9\xce\x1e\x93M\xa4\xc0EB\xf6\xe6\x84\xe2\x14S\xbcw\xbb\xbf\xc7\xb7y\xbd\x87si\xc7]\x13m\xa2\xd7\xcb\xf9\x1cW\xabC\xd1\x1f\xa4>\xcasT\x11Ze\xe4\x960\x15\x97\x0b\x11\xa1\xdd\xbc\xba\xf6\xcdqj|\xf27E\x9eX\x06\x06n\xaf\xde\xbd}\xfb\xca\x7f\x1c0\x92\xe7\x9c\x8bh\xa4\x83\x80\xa0a;,\xea\x80W'\n\x1b\x01\xe0\xa3\xa8\x1f]<\x00\xdb\x1b\xc1/*\xe3\xb9Q\xec\xf9!*\xed\x1d\xff\x81t\xa2\xb4P\xa2\x08\x1d\xcf\x179o\x08U\xa3:\xbdys\x14\xc9\xfdA|\x87QRMq\"\xce\xab\xbc\x08\x9dh\x83#\xbee\xeb\xb7\"z\xcf\xb5\xb7m\xfb\xf9R\xd6\xf3\xd2?`\xfdd s0\xd66\xda\xbc\xee\xe6\xbb\xe9\xeb\xaf\xd8\xa5\x9c\xf5\xad\xb8\xee\xe2\xa9\xf1D\xcd\x0d\xce\xf3\xf2N\x169\x90\xa9\x86a\x80\x01O \x12-\x88\x9fm\xfd\x02`\x85=\xf0z\x90?\x16\xd3\x8d\xdd\x9d\xe5^\xab\xac\xd4X\x11\xa1\xaa\xcc!\xf5\x83@kK<\x98\xc3lv\xaeB\xc9\xa8\x9b\x99\xc8\x8a\x92\x80\x00\x0d\xa4\x9c\xe1\x89\xd8\xaa\xd1Jz\xa8/#\xe1U\x01\x066\x81o\xb7\xa5\xd6\x1d\xd3\xfb~\xa9\x9a\xa0\xf7\xfdN53\xef\xfb\x9d\xeeO\xde\xf7C\xdeS\xbc\xefG\xba\x1fx\xdf\x0feO\xef\xbe\x9f5\xad\xb7\xfb~\xd9\xb4\xc3\xee\xfb\xa5n[\x1d\xfdP\x97h\x18\xb8\xe8`Ng\xf3\xf1\xado\xc8\xdep\x7f\x89don\xee\xbe\xedYp\xc1\xb9a\x0e\xdd\xaf\xe58\xb2\xe7x\x95]g\x05\x1e\xd2\xa5_\xed\xafC\xd7K\xa4;\xf2\xa7dBe3\xfe\xac\xb8F\xd3e\xc1\x9b\xad\xc7\n\x06t\xc7S\xfb\xf2\xd0\xf5R\x1c6x4E\x96\xa0\xac\xb8%\xf5\x10\x9a\xf4\x1e>t\xbeEV\xfbtn\x04h:\x93eM\xcb4\xc3\x85$\x96\x08E/\xba\xa9\xf7F\x84\xcb\x84\xc3\xce\x9bv\xd5\xa7\x85\xd1}\x9c\xab}\xa5\xd9\x98\xee\xee=\xa8\x96)\x87\xce\xb7\x82\xc7bH\xd1\x01\xba@x:\xcdr\xdex\x1b_W\x84\x9b3\xbd\x87\x95\x12\xe9\xd0\xf1N\x0c\xc9\xed'\x9c\x8b[\x14\xd9\xe6q\xd5\xd8`\xa2\x9b~\x81\xcay\x91M\x965\x9a\xe0\xe2F\xe9\xd1\xde\xc84r\xee\xd0\xfd\x9a\xa1\xa4\xaa\x1c\xa8\xd90'\xa3\"\x8b\x8a\xd4\xdc\xac\xe3Qm\xba\xc2\x96\xf0\xc7\xb6\xaa\xbc\xe2d\xd0&od\xea\xa1\xfb5r4\xfaox\xd5t\xfd\x96R@\x97\xa0\xcc\x08\"EI#%g](ia}\xe8|\xebB\x88\xd7v\xe3\x8bY\xd4\xdd\x10{\x05R\"\x04\xf1C3\xe5Ma\xe3\x127~1i>\x90KJ\xf3\x91\xd6\x9e\xf6o\xe8\xa5\xf0\xaa\xe6]\xf5\xb1\xe8 [k\x84\x83\x10\xa1\xb6\xe3\x11:3\xf6\xbe\xb2k\xd9\xb6\xdc\xe3%s\x05G\x99m\x19\x01\xd4m\xeb\x9e\x84\xeb`\xa0\x9e\xfa\xf2\x92\xbb\xf7dk\xd6f{ Z.PNnI.\x0f,\x90\xf0F\xe5A\x14\xd6\xf1\x9b6p^\x97\x9e\xf1\x1c\xe1\"\\\x8d\\z\x9dj\xa3\xa9\xf4\xca\xdf\x13\x16I\xc3\x9a\x9f\xda\x1c\x15\xed\x05\x16\xf6 9\xb2~\x97\x05/6\xc48\xae\xb0\xcf\xb3\x9a\x8a\n9\x86\xbf<\x10\x9f\xce\xb0\xb9\xc2I\xb4\xce\xfb\xb8\x87\xbd\xe89\xa1\xcf\xda8\xd2,S\x15v\x18\x138\xff\xf0\x92\xce\xca*{\x10\x92\xb6\" \xc9n\xc3\x8b\xa3\xa9+\xcb\xe7\xa9[\xa4[;F\xbc`\xf8\xbd\xea\x15_\x8fW\x80\x83\xe8&8\xd2lf[\xc1\xb15!\xea,\xb7\x08\x8b\xacZ\xc3\x1btAq\x91\xe2*5\x95\x92\xd4\x93\xa2\xbf\xf4\x1cW7\x91\xee\xe6\xea\xf7\xe1\xcd\xc2&\xb0^.\x16ee\xd5\xef\xe3\xf8\xbf\x91\xb7\x81\x98\xd2*\x9b,)A\xf3\xe0\xcaD\xfa\xce0\x99\xe1\xe2\x9a\xa4h\xb2\xe2\xdc\x90\xba\xa4\xa9\xd2T\x16 \xb3|P\xcc{B\x1c\xb1\xc6\xea\x91\x8e\xad+.\xc1\xaf\xaa2\xcf\x97\x8b\xf8\x12\x88\xeb\n\xb8V\xe9\xb3Z\xfe\x92J\x07\xe7\xb9\x16#j'\xc9K\xf2\"E\x19\xad\xb58 \x82\x93}=\x90\x12\xd1\x16\xa8W\xb5\x12\xd4\xd3\x8c\xe4i\xa0o7R\x17\xbe8\xafKD\n<\xc9\x85/\x82\x89x\xa5\x05\xffN\xab\xa5r\xae \xb8\xe1\x15U4\xcda$\xa5\xbeI\x842P\xf4\xee\xd6\xdd\xcdQU\x96\xd4(\x9e\xca\xadw\x94\x94yN\xb8\x91\x16\xf2\xbf\xe8\x9b\xad\xbbB\xac\xcf\xb2\xe0\xc5\xf0y\xd3\xf0 \xba\xca\xa9\x16\x90%\xfdi\x8d\xe6^\x8dI\xe5H\x13\xdf\x9f\xca5\xf3\xa9\x1c\xcb\"$\xc7!I\xcaP\x1a\xba\x10#\x12\xdd\x0b\xa9[v\xc3q\xa7\xf5(\xf2\x1d\x90\x80\x0c\\ \x1b\x13\x01\x91l\xe3\xc8\xdd\x1bj_\xf7AU\x06<\xbfxK\x184\x92\xf2\x88\xe6k\xda\x83\x1a\xff\x1bI\xa4l\x9cO-\x94m\xad\xa2_\x07\x01\xb6jF\xf4\xe7\xdc\x93)\x19X\x96\xeff\x96\xcbH\x0bd \xd1\xa3(\x1e\xcf\x1ar\x91o\xa1\x19\xef\xf2\x99\x98\x87C7\xcc\xae\x81,<[\x8c\"\xdd\xaf31\xe3\x98\xd4\xf3\xdc2d_\xf2|\xf4/\xff\xc3\xf3|\xc4\xb3\xc0\xfcR\x94\xa1\xb6\x06\xee\x0d\x14\xeddP\xce\x0f\xf3oM\xe2\x85t\x94\xf9\x04\x83\xc4\xdf-\xfb\xa2fZ\xdcH\xbb!+\xbf\xb8\x04 K\x90.\x851\x0f \x84\xc4\x84\x0b\xbfgE\xe8\xb2\x92)\xfcg\xf8\x9a\xa8l\xab7\x05\xb9\xa7W\xec\xc7\xb4\x0c@\x9b\x90\xeb\xac\x089\xadx\\\xa9*\x1a\xcf`\xb2Y\"h^\xd6\x14\x91\xe94K2R\xd0|\xf5\x06\x9d\x16\xb9\xf4\xf0\x84\"r\xca\xe9T\xdc\xb03:B\xe3\xd6\xb3r\x99\xa7hBB\x9eI\x01m\xa4\xf9Yf\x05\xfd\xf8a\x84\x19\x924\xf2I*\x96s~\xf9.\xdf \xef5.\x18]\xa2\x83\xcf\x8c\x14\x9c\x15~pY\x8d\x96\x05\xbe\xc5Y\x8e'9 z\x18\x8f\xf9\xa89o\xc3\xa2\xe6\x86\x8dY\xa0%\xef\xd5pC\x86M\x94\x9c\x8d\xd0\xd0\xe1\x89\xca\xb3y\xb6u\xf3\xc4\x91R\xd6\x08-)\xce\x8dZ\x19\xaaq\x02-\x19i\xc6.\x0b\x00\xa4\xba\xe1\x02\xdf$\xe1\xa9\x9a\xa2\x9cL)\"\xf3\x05]\xa1\x8c\xa2\xbb,\xcfU\xc8\x8e(\xdf/6\xb8@\x80\xcd\xc5$\xb4J\x08Nf\x08/\x16\xbe\x19\xe0\x17\x14W\x9c\xca\xd8<\xc4\xbc\xf1\x10E\x86z\xcd\x84\x81\x1c\x9b\x0f\xbeSJ\x9e^\x86Z\x11\x058VtL\xf2\x9f\x81\x90\"D*\xc1\xd0d`\x81\x812\xb5\xdak\x81_\x03\"\xbd\x05\x19\x03\x02\xc0\x0c-\x96\x15\xe8\xc7q\x1d\\\x07-\xd2\xf9\xad\x0e3FD\xda*\x17\x0f\x8d\xd5\x82\x91\x88\xb5\x17K\xd6\xec[\\\x91\xd6\xde\x0dqA\xdc.\xb3\xf1E\xd3(TV)\xa9\xde\xfc-\xc4\xba\x8b\xacH\xc8!Jx0\xf7n\x9d\xde\xa0\xb7o>\xbcw~\x10\x8b_\x10\x8a[\x1bs\x02o2\x9f\x904\x15\x9a\xfd\xfa\xfc\xec\x8b6\xd5d\xa6o\x1dX\x87Z\xfa{K\x7f\xe8E\xfa\x06}\xedxVB\xa6^|\x92L3N\x05\x82qo\x88\xb91\x1aW\x1d\xdftN\xfb\xd4o\x94\xafq\xacQvQ\xc8\x8c\x0fh\xa2\xa85g\xf1\xe7\xff\xf9f\\[gR\xfd\x08C\x8d_3\xe2Z\x06R\x19\xab\xe2M\xd0\x90\xe36Z\xd4@;\x96:\x86/-\xaet|\xfa\x9f\x8a\xdc\x86\x8a\xa0\xa2\x14\xb7u\xde\xf3%B\x01\x95\x02ff@\xa5\xc7\xb6\x8e\x96\x9c>\x05\xde\x88m/\xbd&\xa3\x0dy\xec\xdb;\xbc\x19\x16\xa1;\xdcK \xbb<3\x1bM\x9cgRT2\xfe\xdde\xadjZ\xe1}\xa3\xf3\x9bzUKP\x1f\xbd\x94Kx)\x97\xe0\xfa\xfb6\x95KxI \xfd\xcfL m\xe87l\x0e\xfb\xc4\x1bR\x99\x03\xdc\x1d\xd2\xa9aB\x19\xe2\xc5h\xf9*Lp^\xe7\xc4\x10Fv6\x99\xd3\xc4p0Q\xe0\x07\\FC\x9c\x12a\xd7C_gC\x94\x9f6\xf8\x0d3\xb7er8\xd8\xcb\xdd\x03@\xee\x0es%\x18\x0e\x03\x0b\x9a\xc3y0\xc8]\xe0t\n<2\x17M{\xaa\xf9>\xc4\xcb\xbe\xce\x80\xee\x91\xdf\x02\xe7<\xfe\xf7?\xf0\xdb\xa7\x17\x0bX\xf7\x88?\xe8Po\x1c\xdd-`\xeec|\xf7\xe0>db\xd7\x12\xe2\xf2\x10\x0e\x9c\xd6!GvWM\x94\xd8!=z,\xdf$\x9f|\xd5\x12^\x85\xcb%,H\xb2\xf7\xafv\xda\xf7\xbfe1\x03W\x05\x05\x83\xb7\xfc\x10paEW\x08\xf6\xd5\xaa\x88P+\xf6B\x9d!x2\x84\x06c\xff\xa6\xb9\xf9\xf4M\xe9\xe5\x8ct{\xa6\x02\n5\xa5\xc9\xa7\xf7\x04\xff\xfev\x97\xe0$\xdd\xfd\xf0vJv?\xe3t\xba\xbb\x9fL?\xee\x7f\xf84\x99~\xc2\xef\x1c\x85m=x\xba\xcb\ni\xa6\xee\xff*\xee\xae?},jL\xef\x0f\x16\xf9\xc1\xc7\xf9\xf2\xee\xed\xed\xe7\x15\xcd\x1f\xde\xffZ\xbe\xffy\xffqn.\x9f\x7f\xac\x94D\xdd\xe1\x1cj\x12\x92\nU4\xb9\x9d\xb6.\x0e\xc9TI\x193\x00\xf8\x82\xe8\xd7v\xb1i\x1eV\xbe\xc7+\xfe\x98\xf5b\xe4\xdf\xd4\xc60N\xfb\xca\xb23\xf1\xd9\xe3\xe73\x07J\x8a\x0d\xdd\xf2\x19\xd6\"\x91\xbf\xda\xee:\x1aW\x16i\x11_\x94\xe3\x0b\xa5\x91\xad\x8b\xf9X\x84\xd7\x1a\xbe\xa5\x08\xba(\x06\x1dEG@\xedQ\x82\x81c\x80S\x1c\xc0\xa3\x85\x1a7\xccRT\xd5h\xea\x94\xa3\xa6\xba\x86\xc5\xf8H\xba\x9a9q\xde1\xad\xd9\xfdo\x1e\xf1\x90;\x83\xfct\\\x7fdf\xd5\x13\x9d\x03\x04\x9a\x07\xf6\xc4\x9a\xf3\x00\xe7\x00u\xb7\xd9 \x9e\x1b\x15\x10\xe8,+\xbc\x05\x90\xdb_o\x04\x1f\xe3\xbf\xcae\x1b\xc7\xea\x8eL\xea\x8c\x06\xfd\x14\xe2\x19\x86\xd5\x8f\xf3\xef\xba\x9c\x1a\xb9%\x85pV\xb2%\x12B*K\xcab\x93\x18\x95S\x84\x0b>\x8a\xf7\xde\xda\xcc\xa5\n-\xb0\x90cI=\xd1$5\x10%\xf6l\xeb\x0c4i \x9b\xc9\x88\xfdv\x9b\xcc\xf3\xb8\xca\x8a\xdb2\xbf%Qa\xf5\x18\xb4B\nv\x0c*\xd51\xa4HG\xff\xf2\x1c\xfd\x0bs\x0c(\xc9\xd1\xb3\x18\xc7\x802\x1c\xbd\x0bp\x0c)\xbd1\xa4\xe8F\xefr\x1b\xc3\x0bm\xc4/\xd3\x9a\xc7[\\\xc3\xf3>\x96\xf1\xd7\xb7\xac\xc6#\x14\xd4x\xbcR\x1a\x9b-\xa2\xb1\x05\xe53\x1e\xb9p\xc6\xa3\x97\xcc\xd8\x92b\x19\xdb^&c\xab\nd<]i\x8c\xd8-\xb2xx\xf1\x88K\xb6DTDw\x9aMy\xe6+\xe5E#j\x9dN\xcc\x0e\x1fAPz\x92\xe6x\xc5\xa6\xdco\xf5\x000;\x12\x05\x00\xe4\xba\xcd\x88*\xd0\xd0JLfHI\xd3\xec5.B\xf1.tF\xb2\xcaL\x99\xe7\xd4\xf9f\xaf\xdd:k+\xecU\xe0\xb9\x15\xae`\x1b&'\xee\xd6\x88Y\xda\xd4$\xe4R8\x00L\xcc\xc2\x04\xd7\xaatE\xfb\x80\xec\x89W\x8e,\x06\x87\xafO\x05Q\xab\x14\x01^\x7fR.\x93\x1d\x0f\x98\x8a\xd4\xe5\xb2JH\xbd\xd3\xe8\xfez\x87{\xb9\x92\xb2\xa8\xb3Tz\x8eP\xb9\xa4\x8b%\xad\x83\xe4\x06\x0b\xa3\xd8\x0e\x1a\x97k\xa6,dy\n\x97\xabR^\xd2\xbb\x98e\xa7\x82\xf8\x13\x14`\x0b\xa0\x0bmXZ\xe1\x1aI\x85Q\xa7D\xdc!aQ\x11\xdb\xa4cx\x85`\xccE\xa3\xa5\x11:\x9d\xbf\xbe\x93\xa7=\xa7\xb1\x14\xc2'g\xca\x9a\xa9\x83\xa0L\xb0`\xfe\xd46\xf3\xc6\x93B\xd6z\x1d\x007$Y\xd0\xf8Q8U\x10\xc4\x9fA\xa4\x8f\x90$\xd8\x97\xd4\x11\x96\xc2 R\xd7J\x0d\x0c\xae\x966\xd1\xf6eL\x97\xd6\x90\x11\xb3\xa9h\xae:~s\x10\x1d\x03\x01\xc6A\xbd\xee\x0f\x80\xf3\x0d\xdc\xfc\xa3\xdf\"\x80\xbd\xde1\xbbJ<\x7f\x18\x1enS\xe9k\xe4\x005\xe0Z\xdc\x8dr6Z%\x1a2\xa1\x08t\x11\x01\x9eL\xb4\xf6e\x04xb\xd0ZX\x0d\xba\x92\xe8q)1\x1c\xb7a\x17\x13\xd0\xab\x89\xf5\xf0\x8a_Ot>\xeb\xb1qfe\x9e\xd62?\xd9\xbe\xad\xe3g\xd6\xacF3\\\xa4\xe1\xd2f\xb4l\xce\xa6\xaa\xfebM\xabeB\x97U\xa0N\x1d\xf8V\x05vN\x05\x9cT\xc1\x13!\x05\xd0\x91\xbb\xd2:\xe7LyW\xd4-\x19\x98\xd1\x80\x04\x81_\xac<>\xb9\x90\x0b\x96\x81W,\xc3.Y\x86\\\xb3\x0c\xb9h\x19t\xd5\xd2\xfb\xb2e\xd0u\xcb\x80\x0b\x97aW.\xc3.]\x06\\\xbb\xacs\xf1\xd2O\xc0\xf5\xbf|\x89\x80\x1bT\xd5\xfc\x11.`\x1e\xf3\nf\xd3\x970[q\x0d\xf3\xe8\x171Op\x15\xb35\x971\xdb\x7f\x1d\xb3e\x172Oy%\x03=\x95\x8dz-\xd3\xe3b\x06\x8a\x1f\xb6.gV\x12#\xeb\x82f\xba\xcc\xf3i\x96\xe72\xf6;\x08\xae\xce\xaey\xc3r\x8ch\x85\x8bZ,\xaa\x8d\x1cA\xd5-\xc0Sx\x00\"<\xe5\xc6qc+c\x15\xe9\xaa\x178go\xb3=#\xc0 |\x9b\xe1z6\x02\x1b$}\x8c\x00\x06\xd1\xbaN\x9ad\x05\xaeV\xe8uS\x92;+j\x8a\x8b$\xb0M\x93\x1c\xd7\xf5U\xecp\xdf\x0f9\xb3')\x87\xbf\xa7$\xb1\xc5*D\xeeI\xb2\xa4x\xe2\xad\x96\x1f\x9f\xcc/\x12\xd6\xfawV#\xdfZE\xee\xad\xe0\xe6a\x1d\xbc\xd4r_%z\x81\x05\xee\xbb\xba\xd7\xb0\xa3\xd4\xd3t\x02\x1dv\xf7\xe5\xba\xfdr\xd3\xef;\xc2\x03\\b\x10wX\x9b\xa6\x11v\x0eP\xbaA\xb9\x8e\x1cXz/\xc5\xa2\xa2\x0b\xf5\xbd\x19sL{\xeczl\x9b8\xb5\xe6MY\x17`\x0f>\xd9/F*\xa6\xf8\x18<\xeb\".\xf9\xd7\x03\x8c\xa3\xda\xf3`^>A\xf1\xcd.\x02\xb1\xab\xb5 \xb8u\xa8\x1fi\xe1\x0c\xa7~\xad\xdb6\xd0\xf2r\xb1\x02\x82\xaf\xd3d\xf0\xd7\xec\x0cJ\xc7\x96T,RT\x97s\x82\xc8=\xad\xb0q\xfd$L\x85\xccQ\x06)\x8e\xb0E\xb8h\xf9]\xac\x8cTR\x95z\x14\xc0\xd3\xc2\xb2\xdd\xaa\xc2&\xe15O\x97\xe4\xa9\xfd$\xfd\xad\x8d\xaf\x99G\xf6r\xad9\xc2\xfe\x02\n\xe6\xd1\xaf5\xc7\xb5\xf7#\x82\xe1D\x1e\x07\xce\xc5\x19\x8b\x1f\xadT\xa1\xa0\xa4\"X\xa7,\x07\xaa\x88\x8a\xa7c\x0b\x07;\xadf\x05\xb3\xd2\xe3d\x8evm\x11\xbdk\x85\xad3\xf1\xc4\xa6H<\xc0\x89\x92?6\x8egz\xe5p.E>e\xa3D\xcf\x88\xcdO\xfb\"\x14u\xd0 \xee\x06\xd1X\xa0\xd7t\xb5\xc8\x12\x9c\xe7+\x84\x19Ki)\xcf\x9cQ\x8f\xadu\xe2\x8d9\xaf\xa4\xb8\x0b\xeft\xf1\xf4\"\x1b\xb8\xeb\xc5\xd3\x87GQ\xb7\xc6\xebo \x16}\xad\xd9\xa9<\xabg$\x95\xbb\xd6\x1b\xbb\xab\x9e\x98{C<\xbd\xd8\xd4\x97r\xe5\x0f\xc1\x85\xd1\x94l\x91\x91\x84K\x1f\xe3\xa4 \x80\xc7\x18\x85\xce\xaarQ\xd6P\x1e@\xd1=f\x1b\xce\xed\xaf\xc0|\x19\xef4+\x9d{\x1f\"\xf0\x84\xd3\xc2h\xa1\xf4Zf\xc8\x97\xdc\x8d\\N\xa7\xb2\xcbv\xb8P/\x12\xd7]YA\xf8\xed=\x97\n\x91\x9f\xebjLk\xab\x88#^\xb2\x81\xcf\x12\xf7\xacX\xde\xcd\x1a\xd3\xacf\xfa\x8e\xfb\xd1\xf1b\x91Gn\xf7\x99X\x13Sv\xd1\xb1\xcf\x84\xc2AX\xfe\xc0\x0b\x08$\xf2\x80\xcb\x19\xca\x03\xee%\xd3\xd2\xd9\xdfN\xaf\n\xa3\xce\x1eKBr\x89\xc7\xd6B#*\xfdKY\x14\xcb\xb8\xe2\xa4\xadM\xd3\xa5\xf4\xfb\xc9\n*Z\xe9\xb8\n\x0b\xa0\xd7l\xc2\x83\xe0&\x04\x9d\x7f\xfdrz\xfe\x07#F\xfc\xeb\xea\xfb\xf1\xc5ehc\x02\xe7(\x1eO\xb0\x8b\xfe\xf8\xfa\xed\xf8\xe4\xf8\xf2\xf8\xf4\xa4\xdful\xf7\xcb\xb3\xf3\xd3\xb3\xd3\x8b\xde\x9f \x92\x07}\xc4\xf9\x14\xf8R\xdft\x0f#2~\x8ei\x9e #\xc3s\x10\xf8\xd0\xb8\xe2\x8c\xb4\x8f]\x167EyW\xece\x05\xbf\\\x12u\x8d\xc2\xa2\xce?\x83]\x86\xa9\xbf\x98\x08q\xbb\x8f\xab\x92\xa6\x9c#\xfb\x1f\xaf\x83\xf9Z\xebi\x15\xc1T\x94T\xbbx\xa3)O\xee\xf9\xee\xe2%w\x8ey\x13\xacz[*I\x8b\x0d-\xc2K>\xb5\xec\x87a\xa8\xf0\xa5\xe7\xc3\x87\xff\xd1\x8d\x14^\xb9P\xb2Z\x03\x8a\x9f6\xd7\x81h\x86oc\xa6\x05\x0f\xcffB\x96\xc9\xc1\x90\x18\\\x94E\x9dMr\xd1\x952b\xb1\x8f|\x92\x88\x8a+\x88\xc0z \x80\xf2|\xf4\x12\x00\xf5\x12\x00\xe5|^\x02\xa0^\x02\xa0^\x02\xa0\xe0\x88\xbc\x04@E}H/\x01P!\xabI\xe0wi-S\xc3\xf2j\x1f\x18=\xa0\xe2d\xba\xbc\x01f\x90\x88}\x10\x0d\xc7uH7\xb6\xa8\x99\xc7#\xa9D\xce\xf6\x9e\x8e4\x11N\x8c=\x19&\xe2\x01\x04\xd5\x8e\x1d\x9f\xb7\x15\xff\xe1:H{A\x05\xa3?\x8c\xbb\x9d\xb1b?\x1c \xc7\x8b\xfcpQ\xbe\xe1\xb8\x0f\x9b\x9e\xf8q j\xc5\x03\x1d\xc1P~\xa3\x0e\x8e\xde\x98\x0f\x80\x0b\xa9\xee\x17\xf1\xd1\x9a\xecm\x8e\xf7p\xa2:<\xda\xc3\x06\x07\xe6\xd0F.\xec7\xcf\xadM\xc5y\x0c\xe4\xe2\x0c\xd73\x92\x8e\xe5\xa9\xdd<\xfb\x0c|\xd5\x8a\x13\xaf\xc2\xee\x08\xd4M1\x1f\xc2\xb0'\x08\x8ai\x0f\x1f\x0b\x89\x89.%(\xdd\x9d\x10\x92\x90JC=\xa9r\x02GY_\xb5f\x82\xe2\x02\x17ei\xe3\xf02J\xb8\xc3H\x06i:\x04\xd4v\xa8g\xa4#\x82\xaf\x1f\x04\xdfj\xa8\xe7\xc4\xa0\x1e\x91\x8f`@}u!\x1a\x16\x01\x89\xb6\x96\x831\x1d\x19\x05\xd8/\x96\x0d\xc1\"\xc0\x9e)/\x87i\xd0.(\x87\x16\xed \xa1\xdf\x1c\xc4\x94\x07\xea\xc7\xf2\xf5\xf8\x18U%Qx\xbd\xe3KQ\xef(K\xf4\x98\x1cY3\xe2\xb2\x0bp\xd4\xa8K\xc7 \xdc\x1fs\x198\x9d\xd4\x9b\x8f\xb8\xb4\xaa\xe5{\xe3-\x9d\xb5\xf1\xdd\xed[\x07G[>\xaf~\xee5(>2\xba\x1f\xe2\x13\x84\x1c\x83AZC\xd8\xcf\xc0F\x11n`\x16:\xbe\xb8\x19w3 \xfb\xe1\x83\xd4\xd0\xd6\x12\xdd\xef\xdd\x0d!BS\xf1\xb8-\xe5\x93n8s\xa8\xd9\x98\x07\x1a\xf5\xa6\xfe\x84\xea\xe2!\x7f'r\nn\xd1\x9f\x04\"\xa0\xd1\x131\xb5#\xb1\"\xfd\xdb|lu\xcb\xb4 C94/8?C[}\xf7\x9e\x9e\x97\x03\x1b\x01zgF\xb6\x07\xec\xb1\xb2<\xd2\x1e=2#\x06\xf5\x03\xf4\xae\x00\xd5%\xd0\xd7\x190\x86U\xb7\")\xa8\x81i\x0b\x8a\xd1\xce\xd4\xbe\x048?\xfb\xf2\xd2\xc9\xf4\xa5\x93i\xc4$y\xe4N\xa6m\xdb\xc6\xb7\x10[\x9be\xa8I4\xcc\x10\n\x99<\x96\xa13\xd8\xa4\xc9\x8aC\xb4\xc0tf\xbcj\xda\xa8\xd1\xca\x8a\xd9sNB\xbb/\xaam{\x00\xb9:\xd0p\xb1L\x14\xff4\xf52W|\xaad\x93\x0d\xe7\xba\\4\x8d\x8d\x9e<\xecm\xa7\xb8N\x82}\xf8\xd7\xb5N\x9e\x8e\x7f/\xfd\x89\xff3\xfb\x13\xfbZ6F;6\xd6{8\x97:\xcb\xd5\xa2\xb1k\xcd\xf0N\xed\x15\xa1UFn \xaf%\xef\xda\x16\xd0\x06~\x0c\xda\xdf\x14+\x9eO\x13\xbf-\xb19,\x9c\xc2\xa6\xc3\xdaq 0\xc7\x8bx\x00\xd6\n\x82\xfb\xc3GOL5WQ`\\k\xb1\x8d\xd9\xb9\x0f4'\x088/\x08\x98\xc6 \x9c\x13\xd4\xdde/es\xfb\xe1\xf6\xdc\xcb\xe6nq\x05Z{\x0d\x8c\xd7\xdf\xef\xa5\x10m;\xee\xbc\xcfW/y\x18/y\x180\xb7\x9fz^\xf20:/_\xf20^\xf20\\C\xbe\xe4a\x801|\xc9\xc3x\x06y\x18\x11\xfczw \x0cB{\x8d\x0b\x1e\x8c\xd0\xa3O`7@e\xab,`\xf0\xa9\xb9\x8fB\x1e\xb5k\xe0\xe0\xbe\x81\x90%2R\xef\xc0\xe7Y\x87\xd5u%\xe2\x83\x14L\xc3\xb1\x1bM\x8d\x91\x85\xd3\x85\xd8D\\\xf5\x8bV^\xa3\xf9 \xc8\xaf\x02\xf1\xa9X\xd4\xc4w\x7ft\xd3n`\xcbZ(\xfab\x8f\x01\x12\xd2^q\xd1\xa0c{\x9e\xe3\xd1\xc6[\xc2\xa0XhqpEv\xa0A\xd9\x03\xeeG\xb7=|j\xa1\x8cz\xb7(\x1c\xd6\xa4\xb0G\x9bB0\xaf\x062!\x1a\xf3\xbb\x99\xe52\xd2\x02\x19H\xf4\x9aa\xbd\xc15\xe4\"\x1f\x82fW\xd5\xfbcy\xfdr\xae%\xdf\xd6\x0c\xe5u]\x05i3Z\x05\xed\x86\xae\xc3\xd4\xf3\xdc\xc2n_\"\xe9\xf4/\xff\xc3#\xe9\xc4\xb3\xc0\xdc\x01\xe6\xbd\xe1\x81\xe1\xde@\xd1V|\xc9\xbf\xc1\xb9\xf9\xb7&\x8c\xc9\x13y\xa7\x1e\x89\xbf[\xe0Fm\xc3\xb8exCV~\x19\x0d\x92\xd0 \x05\x0ec\x1e\x12\x08\x89 \x17\xd5\xac*B\x97U!R\xfa\xce\xf05QQ\x8co\nrO\xaf\xd8\x8f\x83\xd5\x19'\xe4\xda\x1f\xbc\xcc\x1e\x1e\xbd\xc0t\x02\xdb%\x0c&\x9b%\x82\xe6eM\x11\x99N\xb3$#\x05\xcdWo\xd0i\x91\xafPY\x90p\xd6V9\x9d\no*\xa3#4n=+\x97y\xca\xcb4\x12\xcf\xec*h#\xcd\xcf2+\xe8\xc7\x0f#\xcc\x90\xa4\x91OR\xb1\x9cs\x87\xab|'\\t\xb8`tq\xbf\x19\xafO\xccX\xe1\x07\x97\xd5hY\xe0[\x9c\xe5x\x92\x937!\xa6\x1d\xf3Qsv\x90\xd2s\xc3\xc6,\xd0\x92iM6\xd0\xa0\x89\x92\xb3\x11\x1a:\xbcw~\x10sr\n\xc5\xad\x8d9\x817\x99OH\x9a\n\xcd~}~\xf6E\x9bj\xd2\xcfW\x07\xd6\xa1\x96\xfe>\x02\x9aE\xfa\x06}\xed\xb8sB\xa6^|\x92L3N]\x00r\x17\x8c\xb91\x1a\xff\xa0\x8c\xddp\x00\xf2\x1b\xe5k\x1ck\x94]\x142\xe3\x03\x9a(j\xcdY\xfc\xf9\x7f\xbe\x19\xd7\xd6\x99T?\xc2P\xe3\xd7\x1d\xb8\x96\x17j\xc6\xaax\x134\xe4\xb8\x8d\x165\xd0\x8e\x9b\xee\x08R\xe9\xf8\xf4?\xe5\xe5c\xd9\x0e*J\x11\xc9\xe4=_\"\x14P)`f\x06Tzl\xebh\xc9\xe9S\xe0\x8d\xd8\xf6\xd2k2\xda\x90\xc7\xbe\xbds\x87\xb9\xd4\xd9A\x19\xad\x8d\xba\xbb\x858\xcf\xa4\xa8d\xfc\xbb\xcbZ7c\xe1}\xe3\x0e\x93\x1d3%\x89\x07\xf1\xbe\xa4%\x89\xe7%-i\x8b\xd2\x92^\x12\x18\xfe3\x13\x18\x1a\xfa\x0dk\xc4>\x0b\x87\x94\xe9\x00G\x88tw\x98P\x86\xf87Z^\x0c\x13\x9c\xd7m1\x84\x91\x9dM\xe64>\x1cL\x14\xf8\x01\x97\xd1\x10wE\xd8)\xd1\xd7\x0d\x11\xe5\xa7\x0d~\xc3\xccm\x19#\x0e\xf6r\xc7\x01\x90\xbb\xc3\x9c\x0c\x86+\xc1\x82\xe6p+\x0cr$8\xdd\x05\x8f\xccE\xd3\xd2j\xbe\x0f\xf1\xb2\xaf\x9b\xa0\xeb\x0c\xb0\xc09\x1d\x03\xfd]\x01\xf6\xb9\xc6\x02\xd6=\xfc\x0f:\xee\x1b\x87z\x0b\x98\xfb\x80\xdf=\xd2\x0f\x99\xd8\xb5\x84\xb8<\x9e\x03\xa7u\xc8a^\xe5`\x9apb\xc7\xf7\xe8\x81}\x93|\xf2e\xeb\xbd\xf2\xa6\xeb\x91\xba\xce\xcab\xef_\xf2\x1f\xfc\x96\xfa\xdf\"\x8bO&\xc8y\xb3\xf7PMp\x95\xccT\x94\xad\xb8\x9eT+\xc27\x15\x97:\xee&Kw\x18\xf7\xe5\xe1=\xdd\x11\xa1xM\xc6\xb7\xf8\x95#\xeb\xfb\xf3\xfe\xe7\xdf?M\xf0\xbb\xdd\x83\xe9\xfb\x83\xdd\x0f\x07\x9f\xf1\xee\xa7\x8f\xf8\xf7\xdd)I\xf0\xfe\xe4\xed\xc1\xfe;\xf2\x165\x18\xe9|\xeef\x9e\x9a\xc4\xee\xfd_\x0f\xd7\xe4\xed\x03~\xa0\xcb\x8f\x07\xf4\xfe\xe0\xfe \xcfo\x0f\xee\x93\xcf\x0f\xb4\xfeu\x9f\xdf\xdc\x91\xfc\x8d\xc0Y\xf0\x87\x03\xdb1\x0fy-\xfcy\xaa\xba\xba\xde7\xbe\xe9\"\xa0\xff#\x7f\xb6\xff\xeb\xde\x8b\xca\xe7\xfaW\x9e\xcc\xde\xd7\xf7w\xc5\x87\x0f?\x0f\xde\xfe|\xb8\xa6\x9f\xaazv\xfbk5\xad~&\x15\xc7Q\x03\x94\x89\xca\nU\x17\x8a\x0d*\x12WY<\xc5]\xe4G\xfcq\xff\xd7;/\x82w\x1f\xde\xa5\xbf\xde\xfdLo\xe7)~X\xde=$8Mg\xb3O\xd7\xf3\xe5\xfby\xf2@\xde\x9b\xbb\xe4\xbf\x986\xe12I\xce\x90\x0c4j\xd0\xe4\xa2U\xae\x92Z\xef\xc67\x1e\x10\xc6\xcam\x83Q\xabH^\xec\x0b\x0b\x8c\xc1\x16\xdfhx\xf1Q\x0c\x9eB\x07\x91,5\x9cN\xbe1\xb8\xf7\x83\x9b.\xd2TQl\xd9\xb1\xa8\x13Q\x84&&\xb8\"\x8e\x85(cSW(-\x8bWTT\xe4\xe4\x02O\xf5\x8e\xe1\xe0\x8d\xe5\x88\xd1\x04\xa7fP\x80\x81\xa8\xfe\xd1\x19\x87\xcfK\x86[+\x1b\xf1\x93\xba^=(-\x89\xe8\x01$\x8c\x12\xdal\x9e\xda\xea\x07'\xbei\xc6\xea\xd4\x133dFY\x13\xf3\xb2\x96Ij>/**\x9c\xa7\xc4\xe9\x990\xb1\xfe\xc7JY);\x16\x1azx\xe1\x80\xa2JO\x9bssA\xf4\xeb+\xf3\xdb\xbd\xb2j\x15\x05\xa8\xb5>1\x0eB\xea\x93\x86\xcd\xe2S\xf9\x89\xda^\xddDf\xe5+\xd8\xee\xc4\xe5\x88\xd3Vo\xdfn\x04\x8b\xef\xf6\x7f\x0d\x7f\xab\x07#\x14\x83\x8a\xa2\x90\x91\x0eC\n\xc5Q\x01\xfc\x15\x00\xaf.\x02\xb8\"\xd9\xf3Cd,\x1f\xff\x81\x9aLe\xbe\xf0\x11:\x9e/r\x9e\x0bR\xa3:\xbdy#S\x19\x03\xb0\xb2\x82\x92j\x8a\x13ae.k\xc2\x0c\xbeJkM\"vH\xd5t\x07\x08\xc0\xfa\xc2\x8d\x1e\x1f\x13\xc1y\xde\x8f\xcbL\xea\xa9\xbf\xa1\x13]xDX\xf8>\xb0\x15\xb8\xa5\x1b \x125\x17*\xc6\xfd.\xa3\xb3,\xda+\xd8\x1fu&\x93h\x03!\xc6!_\xa0z\xa2Y\x03\xd1=#\x9e\xf8\xce\x11\x8f\\K\xe1\x1f\x01\xe7]\xfeTLl+\xa0[f\x1f\xa1\xd7*\xaf%\x9cWS\x95y<\xbb\x1d\xb0\x82\xc4\x839\xbcfG*d\xf4\x94\x0b\x0b\x81\xdcSh\xc9\xe1EU&b\x0b\x06;E\xcb\x0f\xe0\xcc\x83$\xfd\x0eN\xfb\x1d\x9a\xf8;,\xf5wX\xf2\xef\xc0\xf4\xdf\x01 \xc0\x03S\x80\x07%\x01\x0fM\x03\x1e\x9a\x08<(\x15\x18\xad\x95\x0c\x1c\xbb\xe0\xea>#'\x04\x0fL ~\xa4\xa4\xe0\xc7M\x0b\xde|b\xf0\x96\xa4\x06?Ar\xf0\x93\xa4\x07oQ\x82\xf0sH\x11\xde\xba$\xe1\xa7M\x13V\x01\xd61\xcb\x02\x16\xcd%\x1ehL\x97x\xa4\xad\xa6\xef$\xf5\xf4\xbf\xaaQ\x9d]\x17\x98.+~$U\xa8\x06\xe0\xc1\xec\xbe#\x91\xf4\xac\xc4\xb5\xb4F\xd9\x16\xdc\xcb\n&\xf08\x17\x99]\x18\x04\xd3\xee\x93\x8eci\x9f}\xf4\xe0%\xcf\xddPI\xcfz# Z.PNnIn\x95\xb7 G=\n_\x87\xb0h\xdf\xb4As\xe7\"\xe33\xaf9/=9!\xe1*\x12\xb0\xcb4\x9b\x06\xa3\x07y\xd0\x1c;M\x19\x96\xb4:\xee\n\x0c\xec\xb3jp\x9d.\x0bv\xb6\xe3\\Vx\xe7YMUBq\xe3\xb5\xf1E+2L\xae0\xf7\xb2<\xca!,b\xd1\xc3\xd7AS\xd5H\x1e@En6\xe3\x18^\xd2YYe\x0fB~V$!\xd9mh!\x94\xd3\xa9\xcc\x1c\xf6\xb7\xf9\xb7n\x03\xda\x0f\xbf+\xbc\xb2\xeaA\xc5\x989\x1e\x1f\x9a\x8dj\xab*\xaa\x9bw\xb7\x08\n\xaeM\xc3\xf7rAq\x91\xe2*5\xd5\x8b\xd4w\xc2\xe78\xc7\xd5\x8d#N\xa8y\xd4\xaf\x83a\xc4\x15A\xf5r\xb1(+*\x93\xe69\x12\x1cs\xbe#\x980\xa2\xb4\xca&K\xca\xfb\x90sWi\x00\xe0\x840\xe9^\\\x93\x14MD\x80\x9f\xd4 J\x91\xb2\xbd\x910\xbb\xc5q\xebg>\xd2I\xc4;\x8e\xaf\xae\xaa2\xcf\x97\x8b\xd8\xb4\xc6$;T\x03\xc0\xe7\xff/\xa9\x1ep\x9ek\x11\xa0v\x84\xbc0*R\x1eu\xd7q\xe0v\x9fv\xf9\x08\x0b\xd0\xabZ\x89\xd6iF\xf24\x18\xa1-\xa6-\xafKD\n<\xc9\xc5\x99\x9fG-H]\xf5w\xee^\x16\x08\n\xa8\xa15R\xa4\xdaG/itO\x1a\x8cm\xe2\xbaOeRaT\x95%5:\xdb\x8b\xeb?\xbb\xb5\xbd\xd4\x15\x1e\x80\x8c\x00\xbe\xda\xf8}G%.@\x02\x88*\x07l G\x10F\x89\x05(RI\xc0\xae\x18\xe0\x01h\\:8\xff\x1cs\xa4\xc5]h\x8f\xe9|\xeez\xf0\xa3E\x00b\xa9\xfd\x9a\xe3\xb1\x9c\xfe\x0dS\x01\xc9\xd4\xd7?\x8e\x12#\xfe\x11\xce\xad\xde\x10AA\xe1\xe6@\x10Y\xa9\xf6\xfd\xdan\xc1\xf8\x11\xce\xab\x07\xb1\xa1/\x85#$\xd3\xc3h\x1ba\x8a\xfb\xd2\xb6V\xce\xbcc\x01\xf8\x05j\xbcP\x0b\x0c\xf7.\xb4aEZ\xd6(\xd12\x9a\xa0\x8d\x16g\x01\xcd\xf9F\xb6\xf5\xbaEYz\x95d\xb1\xe7\xf4\x89\x847\x9c)\x10\xf1\x0e\x85\x05c\x89\xf1\xbf\x11\x84\xc4Fy\xd3B\x15\xf5.\xbe2\xa4\xf4\x8a\xf1\xa3'P\x10c\x95\\\xe9K\xea\x08Ka\x10\xa9#(\x0d`\x99\x15u*\xeaR\x19\xf2zl*\x0dEb\xe3cyT-\xc4G@\xcd(\x8fX\xb4\x0b\x1e-1\xee\xb8}\x96\x1fjJ\xe7+\xc9\xe0)\xf3\xc7\x1d-w8|\xd3\xa2\xfc\xf7IE\xb4\xf7^2\xde\xb7\xf1ty\xed8{F\xab\xa4\x18\x8d\x8a\x80-)\xf1D\x9d_\xea\x01\xce\xb1\xfc\xf1\x18\xd1\x11\xb0\xf8\x08\xa8\xa7\\<\xe3\xc6H\x0c\x88\x92\xe8\xc9HX\xa4\xc4\x1a\xb1\x12\xc3\xa3%\x86\xc6K\x0c\x8d\x98\x18\x1c31(jbp\xdc\xc4\xc0\xc8\x89\xe1\xb1\x13\xc3\xa3'\x06\xc6O\xac\x1bA\xd1O\xc2\x8b\xc7\x1bE\x01\xf8\xd6\xfd\xe5\xd08\x8aG\x8b\xa4x\xecX\x8a\xc7\x88\xa6\xd8\x9ax\x8a'\x89\xa8x\xa2\x98\x8a\xad\x8a\xaax\x1eq\x15[\x18Y\xf1\xb4\xb1\x15\xf0\xe8\n\xe8\xbd\x9az\xa0\xf7k\xea\x193\xc6\x02n;\x8e\x14g1(\xd2\x02\x8a\xa5\xbeK\x94\xce\x15\xc2\xd3;E%\xd0\xa6\xde<\xe3\x11/\xa8\x19\x0c\x00\x8f\xd6\xfd\x8e\xb7\xe6\x02\xda\xb90\xda\xd8`<\xcbW\xb1\xcf\xbc\xe8\x97\xf7\x8c\xe4\x9e$\xcb\xe8\xfe\x96\xe6\xfd\x0e\xc3/Kp\x9e\xafP\x92\xe3\xbaf#x?\x94\x1f\x8d@\xee\x06\xce\xc0\xea\xc0\"\n#d$\x17\x97\xf2L#3\x13 \xc9yb\xba\x96A)\xa6\xe1\"\xf7\xdd%\x1a=\x04\xe3e\x1a*|\xd7\x8f\x9e/\xfc\xf8\x9d\xa2\xc9j\x07-\x17\xa9\xfe7\xcd\xe6\xa4\xa6x\xbe\xa8w\xb4\xfbL\xa4,\xfb\x8b\xe1#y\x17]\x91\x9c\xc3\x89\xf5*\x03\x9e\xb0\xa1\xe7k\xe1IH\xaf\x18\x111\xc1 \\@\xc8XD\x0c\xec.cK\x0c\xb4\xd8c2\x8e\x89\xec\xb1ODZ5\xdf@\xa4\xa0\xd5\x8a\xd7\xfb\x91\xf8\x82h\x9a\x04*\x86\x8a\xa7\x07E0)\x80\xe4\x89\xdb\xe3W\x10&\x8a\\=Q\x01\x87t@Y\xf0gr\x05>\x9b)\xccqM\x15\xd2 \xc2\xb6t\x1eyX^6\xe2DJ\x91\x01#\xd6WQ\xc9~\x8c\xfaJ\x81R\x9d\xe2\x81s\x86\xd9\xd6\xd2vi\xc99\xc1\x99\xacFY\x91T\xdc\xfa\x07\xf8\x9b\xb8\x0c\xe7\x850\xc4\x8c\x07?\xf0V\x85\xb2\x9f\x8d\xac\x08\x93n\x89\x87#\xb2/\xb6Q\xa4\x80\xca\xcabO\xd0\xcb;U\xaeo]\x1d1\x15\xf7\x8di\xd7\x1a%x!\xacLg\xd4\x0e\xdb\x81APz\x9d\x97h\x8eo\x88\\\xea\xba\xe8z\x91\xaa\x8dMV\xe8\x8eT\x91\x19\xc3\xa9\xef\x07P\xa5{!\x0d(\x1d_e\xd8R\xda\xba\xc0\xd78+jj\x9cf\xbc\xf0l\xd7;\xfb\n\x17I\xa8N\xec\xa5\xe1v\xe5u]f\xf8\x96\x18#I\x0e\xf3\x982\xb3\x83\x92\x17\xa0\xeau\x19*myr\xcaN\xde\x7fIN\xb7*\x01\x9e[\x91\xc6\x08\xcb\x084\xb6\xff\xbc\x00\x9bx4\xacX*\xb2\xf3\x15\x7fy^\xfb\xbc\xe4x\xb9\x004\x97<\xe3t\xae\xb1\xc1\x0d\x8b\x88p\xc6D\x84\xcdA\x80\x0d\x05\xb1\x9f\x1e\xff\xca\xab\x1d\xb8\xa5\x11\xf0\x07<\xd8\x056\xfc\x96\xa51\x17\xb1H\x86\x8d\xd2\x14\x11r\x1d4\x87\x863\x98\xa0@\\\x81\x04;m g\xbaAR\xc6\x9b\x86CAp\xc6\xe2\x01sH\xfe\xf3y\xf0\xc8@\xb6qH\x18\xef$\x9f\x82\xf0\xecM\x06\xe2S8\xd0\x03\xcc\x9c!T\x8f\x10\xeb\xd1o\xd7\x8c\xb4\x10\x86\xd0\xbaV\xb0\x87w\xa1\x04HVA\x85!\xbd\x88\xfa\x12c\x02\x15V\xb5\xcb\x9c\x13\x11\x80\x91\xd6PZ\xf7\xeb\xb2v\x90\x99\x04\xe9J\x04\xd4\x97\x08AB\xb5\xc5\x03\\\x1a\x08.'\xd0f\x82\xb7\xc5\xa3g*\xae;\xd1\xe3P\x078\xce\xb4\x90\x1e\xaeIM`@>\xc1\xb4)\xda>^\x8d\xa1[\x112\xb2\"\x80<\x8b\xe9\x0d\xd4\x8fU\xc3\xe8\x1fE\x83\xa0\x9e\xabe\xe4E2\x8c\xf25\xf5 r/!7\xf9\xed&\xb8\xa3\x9c\xb5\x9c@\xc7;q\xb9\xa3\xda6|\x00k\xd34\x82\xad\x01\x14!P\xae#\x07\x96^}\x13\xbd\xd0B\x1d\xd7ET9u\xa6=\xae\xa0\xb6\x87Sk+\xa66\xc0\x1e|\xb2_\xc4d\xd0\x16\xf1\xac\x8b\xb8\xe4_\x0f0\x8e\x14\xa8\xc1\xbc\x8c). \xeb\x86\xf3\x04\xa2\xb4\x82\xe0\xd6\xa1~\xa4\x853\x9c\xfa5\x15\x17`y\xb9X\x01\xc1W:\x1d\x03\x9dE\x95\xef\xca\xea\x1c\xea\x84\xd5\xbb\x9b\xa8\x13'9`-\x82(VF\xb9i]\x9dQ\xff\xc2J\x87~\xedlibV\xeal\xff\x85;o\xbb\x0bC,\x87\xc7\xcd\x0f\x10\xc8\xf8\x16\xe9H\xaa\xfa\xd1c1\xf6\x9a%\xd1\x04S\x0bR\xdf \xf4'/b S\xd3\x83\xb0d\xfc\xbe\xa3\xb0\xc6Q^\x97\xe8\xa6(\xef\n\x84\xd9\xaa\xfd\xc6\xcc\x9f`\x90\xc6S8\x8ca\x1ck\xd2\x13\xd4\xa6S\xb7\x1d*/\xa1\x95{\x10\x11\x98\x9a\xd7\xe85\xaf\x97\x99\xd1\x19\x9af9%\x15I\xd1\xcd\xad\xd2\xeb\x94T\x98\x96\x95?\xb8LF\xae\x07\xd9\x05\"P\x02RR\xc6\xb2w\x15eb\xaa\x83\x85 \xb4\xa4Y\xd9wP%\x8f\xe6+\xa7S\x197g7\x07\x862+\xba(Fs\xc3\x00\x93*\x80K\x11\x81\xa7\x01\xa1\xce\xbdz'\x04\x92O\x87\xda\xb2Qhrb\x83\xbf\x9b\xe1z6>\xa9\x8c\x0e\x06\x99\x13Q\x18\x15_\xd4Z\x8b&[\xc4\x84\"\x1a\x84\x17`\n\xb0\x08Ws\xdc]k\xe4_\xb3\x81\xaf\xd8\xcf\xc2\xb1K\xec\xd1\xe1il\x13\xb4f3)\xe7\xf3\xb2\xe0\xe3\x85cHE;\xac'a\x87\x18Z\x84\xa7)2\x9a\xbbR\xde\xba#z\xa5\xac\x1e]\xab\x1a\xbd\x16`\x7fk\xaeh9\x9f\xf64k\x82w\xb5\xeaa\xa27+n\xcb\x9b\xc0Z\xca\x8a\xc5\x92>\xdb\x04/\xc8.\xe85\xf1\x10S\xaf\xfd\x9c\xb0i\x97=\xe4d\x99s6\xf1yV\xdc\xa0 Nndqx\x00$\x1e\xa3\xc0S5\xf8\xa2 {\xb8t'\x82\xb8\x8b\xab'\x0b\x80\xe6\x81x\xe0[\xc5!\xbe\xa5\xa2ob\xc6\xbf\x81\x12-\xbe\xd6\x14O\xf2\xac\x9e\x91T\x053\xc4\x82\xcc!r\xbc'\x9b\xfaR\xee\x16\xf8\x19Ix\"\x82aU\x00\xe01F\xa1\xb3\xaa\\\x945\x9c\x07Z,o\x86\x11\xbc\x11\xffB\xe14\xe5\x1e=Z-\x13\x1eM\xc4m\xa49\xae\xeaY$h\x1d\xa1\x9ab\xba\x8cn\xfd~\xfc?\xd6\xb9.\xd9T\x98M\\\xf0q\x11\xa9\x96\xa1B\x1a4\x01:\x0d\xe3\xe7\x92\xc7\x0d\x89\x18y6\xc5\xfc\x96l\xb1\x8c\x8b\xe7^<\x86\xe7J\x9e\x7f\xfdrz\xfe\xc7\xd5\xf1\xc9\xd9\x8f\xcb\xab\x8b\xcb\xa3\xcb\x1f\x17\xbd\xf2\xd7|0\xce\xceO\xcfN/\xd6\x00 \xdeE?\xd7\xf9w\xeb\x12\xd2_\x92G\x99\x07\x99\x81\x08\x08#\xed\n\x90\x10\xc2\xa3\xbdp\x9e\xa5{\xcbB\x9c\x17\xc5\xbaek\x07\xf0qd*\xdd\xf3\x89\xad0\x1cG\xf1\xce\x8d\xa1x\xe7\xc6/\x13\x1b\xda\xb2\xc0\x16\x15\xb9\xcd\xcae\xcd\x1bu\xd9[\xddH\xaa\n\xe2*\x85\xcde\x85\x93\x1b\xe1\x11\x13\x96\x93>\x01\x12\xa5\x93\xa0\xa72\x90\xf42\x07\xe9\xd8\xd9\x8c\xb6d\x96\x91[\xd1\x18\xb3\\\xd2\x98\xd8)\x0b\x10v\x02\xd2\xb3\xb5\n\x9fX\xd7\xff\x8f\xd4\xf3TUV\x15\xec\x84\x95i\x10\x8f\xf8bO{\x01\xdan\xa8'\xd0\xa0\x17\x1c\xa6\xa2K-\xc1\xc6\x8d\xd1\x1c\x08\x01\xc0\x9a\xfc&\xc9\x1b\xb9\x95\xb3\xe2Z5\xd9\xd9\x99\xe2,_V\x80#$bjx!\xba\xc6E\x7f\xdck\xd6\xfb(\xdd\x8b\x1f\xdf\x07i\xa9\xee\xd7gG\x17\xb0\x94z\xfb\xb3\x8b\xff=>\x1b\xf0\xd9\xb7\xa3\xe3\xef\xd1\xcf\x0c\x9d<\x94\xcea\xda\xd83\x1adR\xbc\x1f[\x1a\x18-\x0b\xbb\xcd\xaa\xef\xe9\x99E\xdf\x9d\xd26\xf3\xd8;K\x85\x11c7\xf1\xfe\xe4\xba\xdb\xd4\x90!\xd9rh\x0f\xc9\xde\x19Cj\x92\xf6\x92\xb2\xa8\xb3Ty\x1f\xf8\xe07\x19\xbf\xb5HE\xd7\xadyV\xf3\xb6\xafR\x1f\x95\x15JI\x8eW$\x05&&z\x90d\x8b\xaf\x8d${\xe7\xe5Ks\xa1\xe8\xc4\x9c\xc9\x0c_\x00\xbaz\xa0BO\x1c\xaaN\x85|\"E\x82\x17\xf52\xd7(I\xb9\xc5O\xb1\x10?\"j,\x0f@N2\x0cG\x81\xc3\xeb\xfa7\xd4\xf4\x96\xe7\xbd6\xcb\xa9b\x99l\x8a\xac\xc4ud\x96\xd8O\xc5AF}\xd0\x14)\xce`\xa6\xc3\xf3\xa8\xac\xd4\xc6R1\xb0\xb9\xcb\x00\xe8\xd9\xdaY\x84\xc9YhI^\x1ey\xc0\xc5g\xfbH_\x89\x94Sn\xb9\xf2)\xc7\x94\xe2d&Fk\xba\x06\x96\x15O\x88\xf2\xc2\xb2w\x8c\\\xc7\xfc\xec\x1eM\xd7\xb6X\xac\x1b0\x8a\xfdPH\x87\x86|-s\x9c\xdc$k\xc7\xd5(\xe1I6\xb4H\\\x92\x11\x7f\xe4\x05h-\x81\x0d\xc7!\x81|x\x1b\xdc(z|oh\x91\x9c\xd0h\xb8P3\x0d\xdb\x1c'\xd4\xc6rx\x80\x90\x86\x04a ,\x1cq\x1b\xd82F\x90j\xb3l\x80\xec\x11\xce\x82\xa8or\x1b\xf8c\xa0\xaa\x18$\xbd~\xfc\xd5F8\xf4\x04qP\xc6\xc8\xb1\x00\xa81v\xca3\xcc\x81\x00P\x05\xd2\x12\x08\xa8)\xd0K\n\x84|\x1e\x81\xba\xe8\xe9\xe0%\x05b\x10\xaf\xc6\xd0.\x08\xbd\xa4@\xc0)\x1fy\x91\x0c\xa3|\xcdHR\xd4'\x05B\xca\xe51\x13 \x1c \xc7K\x7f\x90\xf6\xf5c&?\xd8\xf4\x8c`Y\x00E\x07\x94\xdf\xa8\x83c\xe4t\x12\xde\x07\xfd\xd2\x1eZ\x93\x1dWI\xdb\xc2\xa3\xb5\x15\x91\x0d\x0e\xcc\xa1\x8dD\xado\x9e[\x9bJv\x18\xc8\xc5\xe7r\x04r\xe0\xeb8\x07\x05!\x8d\xc0\xb0\x98>\x07\xb2i(\xfd\xa3hr(\xdd\x9d<\x8a1\xcfHN\xe0\xde\xb3\x12\x08\x14\x17\xb8(K\xfdg\xa6(\xc9 M\x87\x80\xda\x0e\xf5L\xf7C\xf0\xf5\x83\xe0[\x0d\xf5\x9c\x18\xd4#\xfd\x0f\x0c\xa8\xaf.D\xc3\xd2\x00\xd1\xd6r0\xa6#\xa3\x00i\xaf\x84.\x04K\x83z\xa6\xbc\x1c\xa6A\xbb\xa0\x1cZ\xb4'\x84~s\x10S\x1e\xa8\x1f\xcb\xd7\xe3cT\x95D\xe1\xf5N\xb2D\xbdS\x0d\xd1crd\x84\xc3\"`\xa9\x0eM=\x14\xd7\xc2\xfe\xccCy\x9c{\xb4\xc4C1^ \xefP\xff`\xcd\xb4C\xfe\x97\xee\"i\xdd&\xf1_\x89 \xbc\xa4\\\xac\x1a\x1bO\xfc\xc1Nnb\x7f\xaa\xf5\xb5u\x97\xfa\xa0\x11\x10V\xfdaOit-\xc7\xf9\x8e\x8cAP\x82\x0bD2^\xb3{B\x10\xe6\xcbv\x07\x917\xd7\xbeM\xf8y\xff\xf3\xef\x9f&\xf8\xdd\xee\xc1\xf4\xfd\xc1\xee\x87\x83\xcfx\xf7\xd3G\xfc\xfb\xee\x94$x\x7f\xf2\xf6`\xff\x1dy+\x8a\xbc+\x99\x90\xf8\xbb\xcfI5.\x07\x0c\xe1\xba\xff\xeb\xe1\x9a\xbc}\xc0\x0ft\xf9\xf1\x80\xde\x1f\xdc\x1f\xe4\xf9\xed\xc1}\xf2\xf9\x81\xd6\xbf\xee\xf3\x9b;\x92\xbb0\x8e\x17\xa0\x1a\x8b\xa1\xadrS\x0d\xf9v-\xba\x08\xa9\xe2\xc7\xfb\xbf\xee\xbd\xc4~\xae\x7f\xe5\xc9\xec}}\x7fW|\xf8\xf0\xf3\xe0\xed\xcf\x87k\xfa\xa9\xaag\xb7\xbfV\xd3\xeagR\xb9\xb8\x10\xbd\x8c\x19\x89 \xad\xab\x17\xcd\x03%Y,\x16\x84@\xec\xffz\xe7e\xc0\xdd\x87w\xe9\xafw?\xd3\xdby\x8a\x1f\x96w\x0f N\xd3\xd9\xec\xd3\xf5|\xf9~\x9e<\x90\xf7.\xc82\xbbE\x95\xb5\xf2\x98\xdc\x82\x0b\xa1\x8a\xf7\xdaF \xfc\x06\xc6\xaa6F\x82_\xd3\x1c_s\x89\xaa\xeb\xe8\x97\xea\x97\x01\xed\xa1aX\xf7\x17\xfa\xe0R/\xca\xa2v\xc63($\xa4\xa0\xdd\x1e\xae\x98\xaaa S\x14\x88\xbe/\xdd\xda\xfe\xda!\xe1\x01\xc2Co\xae\xf8\xba\xbb\x8a\x1e\x0e\xc7\xe6C\xb3QmU\xc5f_\x94eo\x11\x14\\\x9b\x86\xef\xe5\x82\xe2\"\xc5Uj\xaa\x17\xa9\xefD'\xfb9\xaen\x1caZ\xcd\xa3~\x1d\xda\x0el\xc2\xea\xe5bQVV\xa1O\x8e9\xdf\x11\xa2\xd2W\x95M\x96\x94\xa09^\xf1\x1b\x93\x00\xc0 a\xd2\xbd\xb8&)\x9a\x08\x7f\xbb\xd4 M1\xb7\xb2H\x98\xdd\xc2\xe4\\M\x1ci\x07\xe2\x91N\xa2+.s\xaf\xaa2\xcf\x97\x8b\xd8\xb4\xc6$;T\x03\xc0\xe7\xff/\xa9\x1ep\x9ek\x11`\xe7:\xf0\xeb\x0eZ#G\xbcI\xfbQ\xd7nJ\xa8Z\x80^\xd5J\xb4N3\x92\xa7\x81\x9e\xffj\xda\xf2\xbaD\xa4\xc0\x93\\\x9c\xf9\x99HV\xba\xea\xef\xdcU-\x10\x14PCk\xa4\xd0\xfd\xa3\x14\x8d\xeeI\x83\xb1MDU\xf2n\x03\x84\x97K.K\xc3\xa5/,i\x94\x94yN\xb8\xe1\xc4\xd0\xee\xc6\xa6\x98\x0f#\x80\xaf\xb6\xb2 \xfa\xda%\x80\xa8r\xc0\x06\x82\x83a\x94X\x80\x1a\xef^\xac\xf4\x9b\xdfc\xd3(#\xe7\x0f\xa2\x8e\xb4\xb8\x0b\xed1\x9d\xcf\x16\x13\xcd\x9bl:\xb8b\x8f\xe6x,\x0dt\xc3T\xc429\x1b\x8a\x02\xb9p\x1a\x98\xf8G8\x0dnC\x04\x05\x85\x9b\x03A\x14*\x9c\x13\xf3k\x83\xf8\x81\x83\xf9\x91 6\xf4\xa5\x10\xc7\xb2 \xc7\xa2m\x84)\xeeK\xdbZ\xf9\x8c\x8e\x05\xe0\x17\xa8\xf1\x02\x010\xdc\xbb\xd0\"\xa2\xd5\x03\xa7[\xec\xc6q1\xb3aA\x1bM\xf8\x07\xcd\xf9F\xb6u$\xab?xm\x84\xda\xb7T0\xb9\x0d\xcd\xe1\x7fr\xa6@\xc4;\x14\x16\x8c%\xc6\xffF\x10\x12\x1b\xe5M\x0bU[#\xe8\xd7\x01pN5 \xe7\xd6\x13(\x08X\xae\xfc\xf8\x0bc\x84\xa50\x88\xd4\x11\x94\x86g\xb5\xb4\x89V\xa7\xa2.\x95!\xaf\xc7\xa6\xb2\x80$6>\x96G\xd5B|\x04\x84\x9e\xa2\x9b*\x02\xea)\x17\xcf\xb81\x12\x03\xa2$z2\x12\x16)\xb1F\xac\xc4\xf0h\x89\xa1\xf1\x12C#&\x06\xc7L\x0c\x8a\x9a\x18\x1c710rbx\xec\xc4\xf0\xe8\x89\x81\xf1\x13\xebFP\xf4\x93\xf0\xe2\xf1FQ\x00\xbeu\x7f94\x8e\xe2\xd1\")\x1e;\x96\xe21\xa2)\xb6&\x9e\xe2I\"*\x9e(\xa6b\xab\xa2*\x9eG\\\xc5\x16FV\xf3\xa2_\xde3\xc2\x1a\xbbI\xf3~\x87\xe1\x97%8\xcfWMK\x7f\xef\x87\xf2\xa3\x11\xc8\xdd\xc0\x19X\x1dXD\x02uFrq)\xcf423\x01\x92<#Es\x1e\xe6\x11\x13Ax\xdd%\x1a=\x04\xe3e\x9a\x05\xb9\xd3\x87\x9e/\xfc\xf8\x9d\xa2\xc9j\x07-\x17\xa9\xfe7\xcd\xe6\xa4\xa6x\xbe\xa8w\xb4\xfbLT\x0f\x0c\xb7\xdc\x14Y[\xb9l\n7-\xfdd\x80O\xd8\xd0\xf3\xb5\xf0$\xa4W\x8c\x88\x98\xe0\x04. d,\"\x06v\x97\xb1%\x06Z\xec1\x19\xc7D\xf6\xd8'L\x82\xcaX\x01R\xd0j\xc5[\xc1I|A4MV#R\x04\x93\x02\x08uZ\xf2\x9b~\x05a\xa2\xc8\xd5\x13\x15pH\x07\x94\x05\x7f&W\xe0\xb3\x99\xc2\x1c\xd7T!\x0d\"lK\xe7\x91\x87\xe5e#N\xa4\x14\x190b}\x05\xad\xec\xc7(o\xf5\xf1C\x0c.\x983\xcc\xb6\x96\xb6KK\xce \xce\xf0Z\xe7I\xc5\xad\x7f\x80\xbf\x89\xcbp\x82\x93\x99\x9c\xf1\xe0\x07\xde\xa2\\\xf6\xb3\x91\x15a\xd2-\xf1pD\xf6\xc56\x8a\x14PYY\xec z\x11\xb9\x0d\x1f\x93`\x18\x1e1\x15\xf7\x8di\xd7\x1a%x!\xacLg\xd4\x0e\xdb\x81APz\x9d\x97h\x8eo\x88\\\xea*w\x8f\xa9+\xb9\xb1\xc9\n\xdd\x91*2c8\xf5\xfd\x00\xaat/T2\xb6\x8a\xaf2l)m]\xe0k\x9c\x1555N3^x\xb6\xeb\x9d}\x85\x8b\x84\x04\xa2\xcf.\x0d\xb7+/$0\xc3\xb7\xc4\x18Ir\x98\xc7\x94Qu$\xcd\x88_,d\xc5m\x99\xdf\xdaU\x08\xda\xcf\xc9);y\xff%9]\x11\x99\xd1-B\xc1\xce\xadHc\x84e\x04\x9aU\xcc\xa2\xfd4\xf1hX\xb1T$\x8f_\x98\xe5.\xe6\xe5\xad\xaf\xe1os\xc93J\x13\xa0\x16\xb8a\x11\x11\xce\x98\x88\xb09\x08\xb0\xa1 \xf6\xd3\xe3_y\xb5\x03\xb7\xacJ\x14\xee\x80\x079\xb3\x91P\x06k.b\x91\x0c\x1b\xa5)\"\xe4:h\x0e\x0dg0A\x81\xb8\x02 v\xda\x12\xcet\x83\xa4\x8c7\xc0\xce\x0c\xd4\xaat\x02\xe3\x90\xfc\xe7\xf3\xe0\x91\x81l\xe3\x900\xdeI>\x05\xe1\x95\xed2@q>\x85\x03=\xc0\xcc\x19B\xf5\x08\xb1\x1e\xfdv\xcdH\x0ba\x08\xadk\x05{x\x17J\x80d\x15T8f'!\x0b(\x1a\xbb\xdb*d&A\xba\x12\x01\xf5%B\x90Pm\xf1\x00\x97\x06\x82\xcb \xb4\x99\xe0m\xf1\xe8\x99\x8a\xebN\xf48\xd4\x01\x8e3-\xa4\x87kR\x13\x18\x90O0m\x8a\xb6\x8fWc\xe8V\x84^\xda\xad\xc2)\x1fy\x91\x0c\xa3|M}\x82\xdcK\xc8M~\xa7\xd5\xce\x18g-'\xd0\xf1N\\\xee\xa8\xb6\x0d\x1f\xc0\xda4\x8d`k\x00E\x08\x94\xeb\xa8G\xcb\xb9\xf8\x85\x16\xea\xb8.\xa2\xca\xa93\xedq\x05\xb5=\x9cZ[1\xb5\x01\xf6\xe0S\xd2\xab\x9b\xd7\x16\xf1\xac\x8b8\x02\xb4\x90s|\xd5\x8ah\x1f\xca\xcb\x98\xe2\x02\xb2n8O J+\x08n\x1d\xeaGZ8\xc3\xa9_Sq\x01\x96\x97\x8b\x15\x10|\xa5\xd3\xd1\xdf\xf7M\xfb\xae6\xd0\xf8M\xc2\x0e4yS\xa3{Z\xa9\xc9\xec\xdd\xee\xdc\x8a\x19}\xdc\x10\x7f\x81\x8co\x9d\x8d\xa4m\x1f=\x9cb\xaf\x99\xd5&\x1eZ\x96[E\xe8O^\x87@f\x97\x07a\xc9\x10|Gm\x8c\xa3\xbc.\xd1MQ\xde\x15\xbcr-\xfa\xc6,\x98`\x9c\xc5S\xf8|a\x1ck2\x0c\xd4\xcaU\x17\x16*\xb5\xa0\x95>\x10\x91y\x9a\xd7\xe85/y\x99\xd1\x19\x9af9%\x15I\xd1\xcd\xadR\xcd\x94T\x98\x96\x95?>L\x06\x9f\x07\xd9\x05\"P\x02R\x82\xc22Y\x15eb\xaa\x83\xb5\x1cPS8\xd9\xbaF*y@^9\x9d\xca\xd07\xbb\xaf\"\x94Y\xd1E1\x9a'\x05\x98\x17\x01\\\x8a\x08<\x0d\x08u\xae\xc6;Q\x8c|:\xd4\x96\x8dB\x93\x13\x1b\xfc\xdd\x0c\xd7\xb3\xf1I\xa5\xb2\xc5:'\xa20\x8a\xb6\xa8\xb5\x16\xcd\x97\x88 E4\x08/\xc0\x14`\x11q\xe6\xb8~\xd6\xc8\xbff\x03\xf3\x9a\xd3\xe1\xf0#\xf6\xe8\x083\xde\xbb\xc0\x9e\xcd\xa4\x9c\xcf\xcb\x82\x8f\x17\x0e\x03\x15\x0d\xc5\x9e\x84\x1dbh\x11a\xa6\xc8h\xae;i \xb9\x15V\x8f\xae\\\x8d^\x0b\xb0\xbf5\xb7\xac\x9cO{\x9a5\xc1\xebV\xf50\xd1\x9b\x15\xb7\xe5M`-e\xc5bI\x9fm\x8e\x16d\x17\xf4\x9ax\x98\x05i?'l\xdaEu\"U\xf2\x9cM|\x9e\x157h\x82\x93\x1bYT\x1e\x00\x89\x87\x19\xf0l\x0b\xbeh\xc2N*]\xcd?\xee\xa5\xea\xc9\x02\xa0y \x1e\xf8Vq\x88o\xa9\xe8\x9b\xb0\xefo\xa0\\\x89\xaf5\xc5\x93<\xabg$U\xf1\x08\xb18q\x88\x1c\xef\xc9\xa6\xbe\x94\xbb\x05~F\x12\x9eK`X\x15\x00x\x8cQ\xe8\xac*\x17e\x0d\xe7\x81\x16\xcb\x9ba\x04o\xa2\xb9P8M\xb9S\x8eV\xcb\x84\x07\x04q\x1bi\x8e\xabz\x16\x89;G\xa8\xa6\x98.\xa3[\xbf\x1f\xff\x8fu\xbaJ6\x15f\x13\x17|\\D\xaae\xa8\x90\x06M\x80\xce\xa4\xf8\xc9\xbb\x84\xc80w6\xc5\xfc\xa2k\xb1\x8c\x8b\xe7^<\x86\xa7;\x9e\x7f\xfdrz\xfe\xc7\xd5\xf1\xc9\xd9\x8f\xcb\xab\x8b\xcb\xa3\xcb\x1f\x17\xbdR\xd0|0\xce\xceO\xcfN/\xd6\x00 \xdeE?\xd7)t\xeb\x12\xd2_\x92G\x99\x07\x99\x81\x08\x08#s\n\x90\xd3\xc1\x03\xb6p\x9e\xa5{\xcbB\x9c\x17\xc5\xbaek\x07\xf0qd*\xdd\xf3\x89\xad0\x1cG\xf1\xce\x8d\xa1x\xe7\xc6/\x13\x1b\xda\xb2\xc0\x16\x15\xb9\xcd\xcae\x9d\xaf:[\xdd\xc8\x8b\n\xe2*\x85\xcde\x85\x93\x1b\xe1\xd4\x12\x96\x93>\x01\x12\xa5\x93\xa0\xa72\x90\xf42\x07\xe9\xd8\xd9\x8c\xb6d\x96\x91[\xd1\x1c\xa5\\\xd2\x98\xd8)\x0b\x10v\x02\xd2\xb3\xb5\n\x9fX\xd7\xff\x8f\xd4\xf3T\x15G\x15\xec\x84UZ\x10\x8f\xf8bO{\x01\xdan\xa8'\xd0\xa0\x17\x1c\xa6\xa2K-\xc1\xc6\x8d\xd1\x1c\x08\x01\xc0\x9a\x14%\xc9\x1b\xb9\x95\xb3\xe2Z5\xdc\xd9\x99\xe2,_V\x80#$bjxA\x8a\x144\x91}f\xbd\x8f\xd2\xbd\xf8\xf1}\x90\x96\xea~}vt\x01\xcb\x8a\xb7?\xbb\xf8\xdf\xe3\xb3\x01\x9f};:\xfe\x1e\xfd\xcc\xd0\xc9C\xe9\x1c\xa6\x8d=\xa3A&\xc5\xfb\xb1\xa5\x81\xd1\xb2\xa8I\xdcV\xe3\x17\x12}\x12\xe1\xbbS\xdaf\x1e{g\xa90b\xec&\xa6A\x9b\xceSC\x86d\xcb\xa1=${g\x0c\xa9I\xdaK\xca\xa2\xceR\xe5}\xe0\x83\xdfd\xfc6\"\x15M\xb8\xe6Y]\xb3\xcd)\xf5QY\xa1\x94\xe4xER`n\xa1\x07I\xb6\xf8\xdaH\xb2w^\xbe4w\x82N\xcc\x99\xcc\xf0\xc5\x90\xab\x07*\xf4\xc4\xa1\xeaT\xc8'R$xQ/s\x8d\x92\x94[\xfc\x14\x0b\xf1#\xa2\xc6\xf2\x00\xa4\x15\xc3p\x148\xbc\xae\x7fCM\xfb\xfdeN\xb9\x90\x96,\xe3\xbd\x08\x1bq\x1d\x99%\xf6Sq\x90Q\x1f4u\x863\x98\xe9\xf0<\x8a#\xb5\xb1T\x0cl\xee2\x00z\xb6v\xd6Qr\xd6JR\xbd\xfa\xdc\x80\xe2\xb3}\xa4\xafD\xca)\xb7\\\xf9\x94cJq2\x13\xa3\xe9TT\xb67 N\xfc\x95\xb1\xed\x1d#\xd71?\xbbG3\xae-\x16K\x8c$\xe7\xcaB:4\xe4k\x99\xa6\xe4&Y;\xaeF\x890\xb2\xa1EB\x8b\x8c\x10\"/@k l8\x94\x08\xe4\xc3\xdb\xe0F\xd1\xe3{\xa3\x83\xe4\x84F#~\x9ai\xd8\xe6P\x9f6\x96\xc3c|4$\x08K`\x11\x85\xdb\xc0\x961\xe2L\x9be\x03d\x8fp\x16D}\x93\xdb\xc0\x1f\x03U\xc5 \xe9\xf5\xe3\xaf6\xc2\xa1'\x08e2F\x8e\xc50\x8d\xb1S\x9ea\x1a\x03\x80*\x90\x96@@M\x81^\xb2\x18\xe4\xf3\x08\xd4EO\x07/Y\x0c\x83x5\x86vA\xe8%\x8b\x01N\xf9\xc8\x8bd\x18\xe5k\x06\x83\xa2>Y\x0cR.\x8f\x99\xc3\xe0\x009^\x06\x83\xb4\xaf\x1f3\x7f\xc1\xa6g\x04\xcb\x02(:\xa0\xfcF\x1d\x1c#\xa7\x93\xf0>\xe8\x97\xb9\xd0\x9a\xec\xb8J\xda\x16\x1e\xad\xad\x88lp`\x0em$\xf0|\xf3\xdc\xdaT\xbe\xc2@.>\x97#\x90\x03_\xc79(\x08i\x04\x86\xc5\xf49\x90MC\xe9\x1fE\x93C\xe9\xee\xa4B\x8cyFr\x02\xf7\x9e\x95@\xa0\xb8\xc0EY\xea?3EI\x06i:\x04\xd4v\xa8g\xc6\x1e\x82\xaf\x1f\x04\xdfj\xa8\xe7\xc4\xa0\x1e\x19|`@}u!\x1a\x96\xc9\x87\xb6\x96\x831\x1d\x19\x05H{\xe5d!X&\xd33\xe5\xe50\x0d\xda\x05\xe5\xd0\xa2=!\xf4\x9b\x83\x98\xf2@\xfdX\xbe\x1e\x1f\xa3\xaa$\n\xafw\x9e$\xea\x9d-\x88\x1e\x93##\x1c\x16\x01Kuh\xf6\xa0\xb8\x16\xf6'\x0f\xca\xe3\xdc&r\x07\x9d\x18\x89\xf1\x02\xf9\x84\xea\x07\x12U\xad\x8ck\x17\x1buz\xe2\xebl\xaa\xfaD\x93\xf4\xb7n\x12\"\xffKw\xc9\xb4\xee\x96\xf8\xafDH^R.V\x8d\xc5'\xfe`\xa7: \xa4|y\x8f\x11\x93 l\x08\x84\xfd\xa6\xd1\x95\x1d\x9f\x05d\x0c\x82\x12\\ \x92\xf1\"\xdc\x13\x820_\xc4;\x88\xbc\xb9\xf6m\xc9\xcf\xfb\x9f\x7f\xff4\xc1\xefv\x0f\xa6\xef\x0fv?\x1c|\xc6\xbb\x9f>\xe2\xdfw\xa7$\xc1\xfb\x93\xb7\x07\xfb\xef\xc8[Q\xb5]I\x88\xc4\xdfNN*u9`\x08\xd7\xfd_\x0f\xd7\xe4\xed\x03~\xa0\xcb\x8f\x07\xf4\xfe\xe0\xfe \xcfo\x0f\xee\x93\xcf\x0f\xb4\xfeu\x9f\xdf\xdc\x91\xdc\x85q,\x15q,v6E\xe6z3\xf4\xe0\xd3\xdb\xf7\xd3O\x93d\xf7\xe3\xdb\x8f\xbf\xef~ \x93\x83\xdd\xcf\x07\xfb\xd3\xddw\xfb\xef\xf6?\xfe\xbe\x9f\xbc#I\x8b\xa1b\xb0\xb5X*@\xec\xff\xba\xf72\xf5s\xfd+Of\xef\xeb\xfb\xbb\xe2\xc3\x87\x9f\x07o\x7f>\\\xd3OU=\xbb\xfd\xb5\x9aV?\x93\xcaG\x0e\xef@\xcc\x98P\x16\xf9\xaaa\x01\xcax\n\x9c\xe1\x88\xc7y]\xfa\xf0\x93\x0d\"\x9c\x82/z\xcf4\xd2\x9c\xb6n\x954\xfb\x95\xd0\xb4\xf8\x1c\x02\xb1\xff\xeb\x9d\x97\xcbw\x1f\xde\xa5\xbf\xde\xfdLo\xe7)~X\xde=$8Mg\xb3O\xd7\xf3\xe5\xfby\xf2@\xde\x07\x18\xe0?}\x8f\xcb\x00\xf3\xd4,\xb2\xc6,_ -\xd14+\xb8@\x8c\xacL\xae7\ni\xcb\x8a\xf9\x95I\xc6\x8e\x0fdv\x92\xf0\xa0\x86\xc8\x0cu\x1c\xd0\x06f\xe070^X\xe8\x88\xe50\xcd\xf15\xa7I71(\xd5\xcf\x10\x8d\xb4\xd1\x94\xfaM\x86<\xd5\xc4H\xb5W\x87\xcfzQ\x16u\x905RYn\x0fsL\xf5\x1ecOp\xcd\xa9\xb3bO\xc6\x90{\x81G\xd0\xed\xf0\x98\x8ci!\xe4g\x8c\xfc!\xa7\xd0{\xb9\xcbA\xf0\xe0\xa7\x18#\x9a \xf1\x98>\xe8\x91\x19\xd1B\x08\xb2B\xbc9\xd1\x1aF\x81V\xe5\xb2\x926\x98\xcd\x860V\xb2\xa8F}.Y\x88\x9a\x90D\xf1\x7f\xc6\x1a\x19\xc6N\xd0?\x97\xa4Z\xed\xa9o\xd0\xf9\xd9\x97\x168\x91\xff\xda \xa0\x02\x90\x8d\x9fY\xf8\x1c\x15hY\x90\xfb\x05I\x98!)ZW\xa9\xa1\x8do\xeadF\xe6\xd8\x9e;\xafY\xe97)9\xfc\xee\n\x08\xe8\x88\xa4L\x1d\x12W|\xe0\xab\xa8\xae\x96JV\xd0\xf7\xef:\xfc\xf1T#\x0f\xe0\x90\x12\x8a\xb3|K*\x87\xb0\xcf\xaf\x96\x95\xb7iNT\xdf\xf2\x84\xdb\xc1_;\x1d%\x0b\\\xe19\xa1\xa42p\xde\x159\xc6\x865\xea[\x82\xad-\xd1\xdf|\x1dj\xb4j\xb3\xc9\x82\xd65UG7P\xd70K]\xc6hV\x1c\xa2\x05\xa6f\xe4*\x93KYE\xd2CD\xab\xa5\xb9\x91\x9d\x13\xac\xe7J\x0e\x0d\x9d\xa9\x9e\xe7\xb6a\xa7\xb5\xd0!b\xc8i\x8cq\xeb\x17\x13\xa3NvMq^\x83\xf9eX\xe5@\x96\x0d\xb6\xe3\xc7\xb5\xde\xc7\xe7A\xeb\xee\n\xc0\x83\xfe\xa6\xbca\xb4;\xf7D\xd7\x80\x1f\x91N\xcb\xe8\x06R:\xd4P7Lr\x0b\x1e\xdc\n\x1dBx\xd7\xb2\xf2\xd9\\m\x9eHC\xb9'W\xfaX\xe8\x86-\xbe\xd5\x1ci\x19\xd9@\x8e\x0c4\xcd\x1b#\xbc\xcd\x93'\xe4@\xcb\xba\x06r`\x90MnX\xdf\x160\x8f%\xbeI\xfa)\xbe\xb6l\x9f\x7f\xcaq^\xed1\xe1D\n\\$doN(N1\xc5{\xb7\xfb{r\xc9\xee\xfd\xab1w\xfe\xbd'\x16\xf9\xde\xbf\x18'\xff\xfdJ\xc0\xbb&\xdax\xaf\x97\xf39\xaeV\x87\xba\x95HMp\x95\xccd\x9fR\xb9C\x14\xc5>n_6\xb7\xc9L\xdb\xec0\xa3C \xd0\x1dU\x9d-\xa2\x984\xb0\x11\xb4\x12CH\xc3\xe3\xd2o\xb7cQ\x08\xa1hX\x15\xfa\x03\xb8I\xe1\xb2%\xaca!f\x04\xba\xc8\xe6Y\x8e\xab|\xb5\xd3\xe0@\xad\xd2\xecNc\x88\xd7hj\x19\x9d-J\xc602-^F\xf4\xeb\xaa\\\xbe\xaaH3>;LU\xfc\xd2\x02e\x85\xd9\xe4\xe6\xbf\xd0\xf1T\x18\xaa\xb8m\xbe4\xebF\xf6\x814/rx\xd7\x1d\xa3\xd0_E\xe8\xb2*x\x03\x1d\x17h\xd3\xecm\xe0\xe2\x15\xb2\x9do$\xc6\x1f`xL<\x1f\x17\xa1\x1f\xa2\xb6\xeb\xf1\x1fH\x97\xe6\x10\xdb\x1d\xa1\xe3\xf9\"\xe7-\x08kT\xa77o\x8e\"\xd9\xa6RgMq\"\xfc\xa3\xbc\xe8\xa9\x10S\xe2K\"\xf6\x9a\xb2\xf1\xdaf\xa2\xfd|)\xeby\xe9\x1b\xac\x06'g?.3\x1b Sws\xaau\x88E8\xf0\xc3\xfaR\x98\x92\xbc\xf8\nQs\x81\xf3\xbc\xbc\x93jL&\xb3\x87\xc0\x05n\x9ax\x83\xfb@\xc0a\xc8\x9f\xaa\x9ehe\x9c\xe8\x9e\x11O|\xe7\x88\x07o\xaaj+vw*}\xad\xaa\x1c\x84\x8b\xd2Ue\x1e\xadF\x07YA\xe2\xc1\x1c^\xb3#\x152F\x05\xe6D\xd6&\x06\x86\xfa\xc9:\x08\x04P\x91\xb5\x17\xf3`\x95e\xac\xbe\xf6}\x8a\xadX\x1f\x9e\x9e\x1f\xff\xf7\xf1\xc9\xd1\xe5\xe9y\xbf\xef.\xbe\x9e\xffy\xfc\xe5k\xcf\xaf\x8eO\xfe\xfcz\xd1{\xac/?..O\xff8>:\xe9\xf7\xd9\xe9_'}\xf1;\xfa\xf6\xed\xf8\xfb\xf1\xd1\xe5\xd7~\x9f\x9d\xfe\xdf\xc9\xf1?~\xc4\xeb\xf2X\x1f\x9d\x9d\x9f\xfe\xf9\xf5\xe4\xe8\xe4K\xcf\xc1\xbe\x9c\x9e\\\x9e\x9f~\xff\xde\x97\xb6?\x8f\xbe\x1f\xff\x01\x98h]\xceg\xd0\xf2\x8a\xdb*\xf6\xe3[\xc5\x03\x87G\"\xd4M^\xf2\xf5(\xcb\xe3\xd9\x14\x87\xee\xd7r\x14\\\xf3\xc6\xaaUv\x9d\x15\x98B\xaa\x93:\xf7\xd0\xa1\xeb\xa52\xd6k\x94\x92 E5\xa9n\xb3$+\xae\xd1tY$\\\x8d\xf5\x1cM\xed\xbdC\xd7KqZ\xe2\xd1vY\x82\xb2\xe2\x96\xd4\xfd\xe9\xd1\xfb\xf4\xd0\xf9VMMA3\xba\x12\xea[\xd3\x98,kZ\xa6\x19.$\xa1\xd2\xdf\xc3\x19\xdc\x97P\xbe\xef\x0f;o\xda\x15\x01\x17\xb8\xa2+\x89\x13W\xdaJK1\xed\xdbsH-7\x0e\x9do\x05w\xc5\x80\xe2\xa0\\ <\x9dfy\x86)A\xf8\xba\"\xdc\x0c\xe99\xa8\x94:\x87\x8ewb@n\xf5\xe0\\\xdc\xae\xcb\xf6\xbf\xab\xc6n\xaa\xca\\\x10?/\xb2\xc9\xb2F\x13\\\xdc(\xad\xd8\x13\x95F\x96\x1d\xba_3\x84T\xed\x1b5\x0f\xe64TdQ\x91\x9a\x9bbl\n\x9a\xba\x8b\xd2\xb1e\xd7\xfe\xc6\xc9\x80M\xdd\xc8\xcdC\xf7k{}\xde\xcd\xb2df\xf0I\xdb\x90j\xd7\xeb\xb2\xc4\x19A\xa4(i\xa4\x0cy\x17!-\x90\x0f\x9do]\xe8\xf0j\x9f| \x8bJLb\x7f@\x8aF!\xdd$&\xc10\xae\xf8\xcbS\xfb\xe7\xe5\xad\xafGss\xc93J\xdf\xa6\x16\xb8a\x11\x11\xce\x98\x88\xb09\x08\xb0\xa1 \xf6\xd3\xe3_y\xb5\x03\xb7\xacR\x0b\xee\x80\x07\xbb\x1c\x88\xdf\xb24\xe6\"\x16\xc9\xb0Q\x9a\"B\xae\x83\xe6\xd0p\x06d\x15o\x88\xff\x06\x14\xcf\xb6%\x9c\xe9\x06I\x19o\x80\xcd4\xa8U\xca\x03\xc6!\xf9\xcf\xe7\xc1#\x03\xd9\xc6!a\xbc\x93|\n\xc2\xb37\x19\x88O\xe1@\x0f0s\x86P=B\xacG\xbf]3\xd2B\x18B\xebZ\xc1\x1e\xde\x85\x12 Y\x05\x15\x8e\xd9\xfc\xc9\x02\x8a\xc6n\x90\x0b\x99I\x90\xaeD@}\x89\x10$T[<\xc0\xa5\x81\xe0r\x02m&x[\x07\xb2i(\xfd\xa3hr(\xdd\x9d\x8c\x8a1\xcfHN\xe0\xde\xb3\x12\x08\x14\x17\xb8(K\xfdg\xa6(\xc9 M\x87\x80\xda\x0e\xf5L\xfcC\xf0\xf5\x83\xe0[\x0d\xf5\x9c\x18\xd4#\x11\x10\x0c\xa8\xaf.D\xc3\x12\x02\xd1\xd6r0\xa6#\xa3\x00i\xaf\xd4.\x04K\x88z\xa6\xbc\x1c\xa6A\xbb\xa0\x1cZ\xb4'\x84~s\x10S\x1e\xa8\x1f\xcb\xd7\xe3cT\x95D\xe1\xf5N\xb7D\xbd\x93\x0e\xd1crd\x84\xc3\"`\xa9\x0eMB\x14\xd7\xc2\xfe\x1cDy\x9c\xdbD\n\xa2\x00\x1dH6\x94c\xcbk\xe0n\xe6 \xcf)\xec\xcesk\x10\xfe+\x11G\x97\x94\x8bUc\xa6\x89?\xd8\xf9I\x88g3\xfa\x86\x8c\xe8\xf1\xb0\xf6\x8e:\xbf\xa3+\x122\x9f\x1dW7V\xdbP\xcd\xa4P\x92;\x88\xbc\xb9\xf6\xed)\xf1\xd3\xfd_\xef\xae\xc9\xdb\x07\xfc@\x97\x1f\x0f\xe8\xfd\xc1\xfdA\x9e\xdf\x1e\xdc'\x9f\x1fh}\xf7\xe1]\xfa\xeb\xdd\xcf\xf4v\x9e\xe2\x87\xe5\xddC\x82\xd3t6\xfbt=_\xbe\x9f'\x0f\xe4\xbd\x0br\xd8\xdb;\x12\xf5\xda\x8f\x9b\xe0\x02\x91\x8cW \x9f\x10\x84\xf9\xd6\x0b\x12\xfdy\xff\xf3\xef\x9f&\xf8\xdd\xee\xc1\xf4\xfd\xc1\xee\x87\x83\xcfx\xf7\xd3G\xfc\xfb\xee\x94$x\x7f\xf2\xf6`\xff\x1dy+J\xd6+\xb9\x96\xf8{\xe9\xd9\\\x0e\xe1\xba\xff\xeb\xc1\xcb\xe5_\xf7\xf9\xcd\x1d\xc9\x9d\xcc\x8c$P\x8e\xc5\xce\xa6\xc2^o\x86\x1e|z\xfb~\xfai\x92\xec~|\xfb\xf1\xf7\xdd\x0fdr\xb0\xfb\xf9`\x7f\xba\xfbn\xff\xdd\xfe\xc7\xdf\xf7\x93w$i1T\x0c\xb6\x16K\x05\x88\xfd_\xf7^\xa6~\xae\x7f\xe5\xc9\xec}}\x7fW|\xf8\xf0\xf3\xe0\xed\xcf\x87k\xfa\xa9\xaag\xb7\xbfV\xd3\xeagR\xf9\xc8\xe1\xed\x97\x19\x13\xca\"_5,@\x19O\xdc3\xae\x0fp^\x97>\xfcdw\x0c\xa7\xb8\xf6\x9f\x95\xa3\x93)C.\xcc\xd3\xab\xc8\xde\xb2|r\xb4DyY\xde0\xe9\xec\x80\"\x93}\x84C2\x84G\xa8\x0f\x80\xb6\xd7\x02\xbf\x81\xad<\x0b\x1d!\xc8\xa69\xbe\xe6\xaaE\xb7\x16(\xd5\xcf8\x99~U*\x80H\xed&\xa3\x88j\xd2h u\x9c\xab\x17eQ;\xa3<4:2\x01~\x8b\x18d&\xed\xc7x\x14\xde6\xf5Z,\"\xf7\x02\xa1\xe0\x91\xfe19\xd4B\xc8\xcf \xf9CN\xa1\xf7\xe2\x94\x83\xe0\x81E1F\xa8\x99\xf1Z(\xe8\x91\x19\xd1B\x08\xb2R\xbc\xf9\xc6\x1aF\x81V\xe5\xb2\x92\xa6\x92\xcd\x860V2\xbd\xeb\\r\x105\xd1~\xe2\xff\x8c32B\x9c\xa0\x7f.I\xb5\xdaS\xc5~\xcf\xcf\xbe\xb4\x80\x89\xcc\xd2fx\x15\xdak\xfc\xcc\xc2\xe6\xa8@\xcb\x82\xdc/H\xc2\xac=\xd1[J\x8dl|S'32\xc7\xf6\xccym?\xbf\xdd\xc7\xe1w\xe7? \xcf\x932u\x08_\xf1\x81\xaf\xe4\xb9Z(YA\xdf\xbf\xeb\xf0\xc7S.<\x80CJ(\xce\xf2-\xa9\xc9\xc1>\xbfZV\xde\xae6Q\xdd\xc8SY\x07\x7f\xedtA,p\x85\xe7\x84\x92\xca\xc0yW\xe8o\xc3b\xf2-\xc1\xd6\x86\xe8ob\x0d5\xac\xb4 eA\xeb\x9aS\xa3\x1bQk\x98N.\x83)+\x0e\xd1\x02S3&\x94I\xa5\xac\"\xe9!\xa2\xd5\xd2\xdc\xc8\xce Vs\xd5r\xed[\xb34\xc8\x96\x1a\x0d3\xe3\x10\x07\\F\x83\x8f}\xe3\x1e\xf6\x18\x07~1\x99\xedd\xc1\x14\xe75\x98\x07ja\x00\x19\xd0\xf7\xe47\xec\xbc\x17:\x86\x0c9\xcf\x8d\xc8/\xcbb\x062m\x98\x95\xed8\xfa\x0e1\x1c\x87\xd0\xde5\x86|fR\x87-\xd2\xc6\xed\xcb\x99>\xe65r\x0c\xb2\xcd\xbciY\xc8@\xd6\x0c\xb4\xab\x1b\x0b\xda\x82\xf6\xb4\x1ch\x99\xc6@\x0e\x0c2\xa8\x0d\xd3\xd9\x02\xe61\xa37I?\xc5\xd7\x96\xe9\xf2O9\xce\xab=\xa6oI\x81\x8b\x84\xec\xcd \xc5)\xa6x\xefv\x7fO\xae\xeb\xbd\x7f5\xd6\xca\xbf\xf7\xe42~% ]\x13mu\xd7\xcb\xf9\x1cW\xabC\xdd\xa4\xa3&\xb8Jf\xb2\x03\xa8\xfcL\xd1\xea\xe3\xf3es\xc1\xcad\xee\x0e\xb3\x16\x945\xb0\xa3\n\x96E\xf4\x9c\x066\x82\x92c\x08ix\\\x00\xeev\x94\x8d\x10\xa3\x86\xc2\xd1\x1f\xc0\xb5\x8dK\xcdX\xc3B4\x0c\xba\xc8\xe6Y\x8e\xab|\xb5\xd3\xe0@\xad\xa2\xe7N=\xc9\xcb\x16\xb5\xac\xc5\x16%cX\x87\x16/#\x96\xd6\xaa\\\xbe\xaaH3>;\x05U\xbc\x98 \xca\n\xb3}\xcc\x7f\xa1\xe3\xa9\xb00q\xdb\x1aj\xd6\x8d\xec\xb0h\xdem\xf0~6F\xed\xbb\x8a\xd0eU\xf0\xd64.\xd0\xa6\xbd\xda\xc0\xc5yn\x8bu6\x0c\x9f\xc9\xbe\xe0\x9b z\xcd&C\x8e\xb6\xd7\xbc\xff\xcd9Z{~\xd4\xb8\xde\xf1\xb0f\xbc\xa2B7\x13T\x14\xe2\"\xddk\x16\xc4Uf\xf3\xaf\xb5\xb9\\\x03\xea\x9f\x1c\xa1 N\xcd\xcb\x18\xf5#\x94M\x0fM\xc4.\xed5\xca\x05+_\x98\xb8HQQ:\xb9\xff\xa6\x05@v\n\xa2w%[\xce\xbc[P9\xb5e\x89I\x12\xbf\xdb\x92`\xf9\xff\xcc\x0d\xb3BiY\xbc\xa2\x92\xe3S!\xd5\xf9Fb\xcc\xe3\x9fY\xc3\x1fu\xb8\xba\x83&K\x8a\x8a\x92:8\x8b\xad&\x04]0\xa21b\xb3\x94\x19Yz\xb6\xd8\x1f\xf5\x9eIKR3<\xe7\x98&v\x13.\xe3{sF\xfe\xb1R\xbe\x92\x9d\xc6\xa4\x93\x1d\x9c\xa4\xc5#\xda-Q]\x06\xca\xc0\xf0\x82\xd0\x96\xed()\xea\x98M\x8c]\x95\xc8\xba\xb5\x0c%\xcb\x82\xb4\x17\x9aV\x0d\xba\x82\xd8q\xaau\xc9\xc7\xbf)\xb2\x84\xc9`\xe8\xb1W\xef\xde\xbe}\xe5\xf7\xfc\x18\x19\xc8N\x83c$\x9f\x8f\xc7\x7f\x1e\xf7\xd4i\x1b\x9c\xf1G\x17\\MTW+^m\xb5nY\x90\xaf3W\x8c\x87\xbf\xdc\xeaZ7\x98\xd1\x9b\x81@|S\xcc\xcd\x13\xbb\x1eD0g\x0d0b$\x9e\xa2\x8a\xd0\x0fQ\xee\xf4\xf8\x0f\xa4\xabU\x88\xed\x8e\xd0\xf1|\x91\xf3\xe6~5\xaa\xd3\x9b7G\x91\x04L\xa9\xb3\xa68\x11~M^\x07T\x88)\xf1%\x11{MYwm\x03\xd1~\xbe\x94\xf5\xbc\xf4\x0dV\x83\xf3\x95\x1f\x97\x99\x8d\x84\xa9\xbbi\xc6:\xea \x1c\x0ba})LI^\x8f\x84\xa8\xb9\xc0y^\xdeI5&\xf3\xbbC\xe0:g\xd8\xe6\xe1\xad\xe3\x031x!G\xa8z\xa2\xc5b\xa2{F<\xf1\x9d#\x1e\xbc\xa9B\xa6\xd8\xdd\x03\xf4\xb5J\xfc\x0f\xd7i\xab\xca<=?\xfe\xef\xe3\x93\xa3\xcb\xd3\xf3~\xdf]|=\xff\xf3\xf8\xcb\xd7\x9e_\x1d\x9f\xfc\xf9\xf5\xa2\xf7X_~\\\\\x9e\xfeq|t\xd2\xef\xb3\xd3\xbfN\xfa\xe2w\xf4\xed\xdb\xf1\xf7\xe3\xa3\xcb\xaf\xfd>;\xfd\xbf\x93\xe3\x7f\xfc\x88\x97\xaa\xb1>:;?\xfd\xf3\xeb\xc9\xd1\xc9\x97\x9e\x83}9=\xb9n=\xfd\xafjTg\xd7\x05\x16}\xb7k\x8dj\x00\x1e\xcc\xee;Bg\xc6.W\xd6(\xdb\x82{\xbc\x96\xb8\xe0\"\xb3\x0b\x83`\xda%\xde\xa3\xe5\x81\xfa\xe8\xc1K~*\x97M\xaf\x9b\x8d\x80h\xb9@9\xb9%\xb94\x1b\xd5VUl\xf6Eq\xf9\x16A\xc1\xb5i\xf8^.(.R\\\xa5\xa6z\x91\xfaNt\xe6\x9f\xe3\xea\xc6\x11$\xd5<\xea\xd7\xa1\xed\xc0&\xac^.\x16ee\x95+\xe5\x98\xbf\x91\x01-\x98\xd2*\x9b,)As\xbc\xe2\xee\xfb\x00\xc0 a\xd2\xbd\xb8&)\x9a\xac8\x17\xa4NhJ\xd2\x95E\xc2\xec\x16&\xe7j\xe2H\x9e\x10\x8ft\x12]q\x99{U\x95y\xbe\\\xc4\xa65&\xd9\xa1\x1a\x00>\xff\x7fI\xf5\x80\xf3\\\x8b\x00\xb5#\x1aOsFk\xf7\x8d\xb9\xfd\xa8\xbb>%T-@\xafj%Z\xa7\x19\xc9Sod8RqH8\xafKD\n<\xc9\xc5\x99\x9f\x89d\xa5\xab\xfe\xce]\xd5\x02A\x015\xb4F\x1a_\xb9\xa2\xd1=i0\xb6]\xf0Qy\xcf\x04\xc2\x8b>\x97%5*?sK\x1a%e\x9e\x13n85W\x1a>\x1c\x19\x01|\xb5\x95\x05\xd1\x17!\x01D\x95\x036\x10\xbd\x0b\xa3\xc4\x02\xd4x\xf7b\x05\xec\xfc\x1e\x9bF\x199\x7f\x10u\xa4\xc5]h\x8f\xe9|\xb6\x98h\xdeg\xd1\xc1u\x874\xc7c\xc9\xac\x1b\xa6\"\x96\x8f\xdaP\x14\xc8\xe8\xd3\xc0\xc4?\xc2\xc9|\x1b\"((\xdc\x1c\x08\xa2P\xf9\x9f\x98_\x1b\xc4\x8fp\x96'\x88\x0d})\x8c\xe6r\x8eE\xdb\x08S\xdc\x97\xb6\xb5\xb22\x1d\x0b\xc0/P\xe3e\x0e`\xb8w\xa1ED\xab\x07N\xb7d\x8f\xe3bf\xc3\x826Z\xb6\x004\xe7\x1b\xd9\xd6\x91\xda\x04\xc1k#\xd4\xbe\xa5\x82\xc9mh%\x82'g\nD\xbcCa\xc1Xb\xfco\x04!\xb1Q\xde\xb4P\xb55\x82~\x1d\x00\xe7T\x93pn=\x81\x82\x80e\xfc\x8f\xbf0FX\n\x83H\x1dAixVK\x9bh\x7f\x9aa\xc8\xeb\xb1\xa9\x1c\x1c\x89\x8d\x8f\xe5Q\xb5\x10\x1f\x01\xa1xJ\xb3x@\xb3\x0b\xdc\xea\xf0h\x89q\xc7\xed\xb3\xfct\xdf\xdd\xa6\x98\xbb\xa7\xfc\x0d\xa8!\x8b\xab\x9c\xbbd\xbco\xe3!uh\x8c\xb3g;\x1b\xe8D\x9d_\xea\x01\xce\xb1\xfc\xf1\x18\xd1\x11\xb0\xf8\x08\xa8\xa7\\<\xe3\xc6H\x0c\x88\x92\xe8\xc9HX\xa4\xc4\x1a\xb1\x12\xc3\xa3%\x86\xc6K\x0c\x8d\x98\x18\x1c31(jbp\xdc\xc4\xc0\xc8\x89\xe1\xb1\x13\xc3\xa3'\x06\xc6O\xac\x1bA\xd1O\xc2\x8b\xc7\x1bE\x01\xf8\xd6\xfd\xe5\xd08\x8aG\x8b\xa4x\xecX\x8a\xc7\x88\xa6\xd8\x9ax\x8a'\x89\xa8x\xa2\x98\x8a\xad\x8a\xaax\x1eq\x15[\x18Y\xf1\xb4\xb1\x15\xf0\xe8\n\xe8\xbd\x9az\xa0\xf7k\xea\x193\xc6\x02n;\x8e\x14g1(\xd2\x02\x8a\xa5\xbeK\x94\xce\x15\xc2{\x96\x89\xd2s\xfc\x0f2?\xe9\xba\xe0\xb5\xdc\x82\x01\xe0(\x8e\xd6H\xe5\x8ba\xb4\xf1<\x1eZ6\xec3/\xfa\xe5=#\xac=\x9d4\xefw\x18~Y\x82\xf3|%\x1a\xee\x07K\x19\xcb\x8fF w\x03g`u`\x11\x89\xb6\x19\xc9\xc5\xa5<\xd3\xc8\xcc\x04H\xf2\x8c\x14\xcdy\x98GL\x04\xe1u\x97h\xf4\x10\x8c\x97i\x16\xe4N\x1fz\xbe\xf0\xe3w\x8a&\xab\x1d\xb4\\\xa4\xfa\xdf4\x9b\x93\x9a\xe2\xf9\xa2\xde\xd1\xee3Q\x181\xdc8T$\x8a\xe5\xb2\xb5\xdd\xb4\xf4\x93\x01>aC\xcf\xd7\xc2\x93\x90^1\"b\x82\x13\xb8\x80\x90\xb1\x88\x18\xd8]\xc6\x96\x18h\xb1\xc7d\x1c\x13\xd9c\x9f0 *c\x05HA\xab\x15oh'\xf1\x05\xd14Y\x8dH\x11L\n y\xe2\xf6\xf8\x15\x84\x89\"WOT\xc0!\x1dP\x16\xfc\x99\\\x81\xcff\ns\\S\x854\x88\xb0-\x9dG\x1e\x96\x97\x8d8\x91Rd\xc0\x88\xf5\x95\x93\xb2\x1f\xa3\xb8\xd4\xc7\x0f1\xb8`\xce0\xdbZ\xda.-9'8\xc3+\xb6'\x15\xb7\xfe\x01\xfe&.\xc3 Nfr\xc6\x83\x1fxKb\xd9\xcfFV\x84I\xb7\xc4\xc3\x11\xd9\x17\xdb(R@ee\xb1'\xe8E\xe46|L\x82ax\xc4T\xdc7\xa6]k\x94\xe0\x85\xb02\x9dQ;l\x07\x06A\xe9u^\xa29\xbe!r\xa9\xab\xdc=\xa6\xae\xe4\xc6&+tG\xaa\xc8\x8c\xe1\xd4\xf7\x03\xa8\xd2\xbd\x90\x06\x94\x8e\xaf2l)m]\xe0k\x9c\x1555N3^x\xb6\xeb\x9d}\x85\x8b\x84\x04\xa2\xcf.\x0d\xb7+\xcf\x94\x9f\xe1[b\x8c$9\xccc\xca\xa8:\x92f\xc4/\x16\xb2\xe2\xb6\xcco\xed4\xfb\xf6sr\xcaN\xde\x7fIN\xcb\xbc\xf8\xa2\x14\xa1`\xe7V\xa41\xc22\x02\xcd*&\xd0~\x9ax4\xacX*\xd2\xc7\x15\x7fyj\xff\xbc\xbc\xf5\xb5-n.yFie\xd4\x027,\"\xc2\x19\x13\x116\x07\x016\x14\xc4~z\xfc+\xafv\xe0\x96Uj\xc1\x1d\xf0`\x97\x03\xf1[\x96\xc6\\\xc4\"\x196JSD\xc8u\xd0\x1c\x1a\xce\x80\xac\xe2\x0d\xf1\xdf\x80\xe2\xd9\xb6\x843\xdd )\xe3\x0d\xb0\xbf\x04\xb5Jy\xc08$\xff\xf9E\xfd\xfcbmo\xe4\xf6\xd8sI\xe2\xe7\xf1sV\xbe\xcc\x8c6\xbe(\x96\x9c\xdd=9\xf9\xb7\xd9\xdd\xe8\x16#6\xd9C\xb2%k\xb2\xf9\xee\xff\x83\x1bI\x90\xb8\x14@\xb6F\x9eA\xbd\x99\xb1\x9a(\x14\n@U\xa1PUh\xea_\xc3\x03\x8e\xf6a{Y\xcb\xf8\xf4\xb8a\x8d\xf5\xd6V\x02\x15\xbdi~U\\Z\xd6R\x9a\xef\xf6\xf5\x17\x9b\xea\x85\xd9\x05^\x13\x8f1\xfa\xfa\xf0\x8eN;/r$K\xa7\xb3\xf7\xa2\xd2\xfc\x12\x16\xc9\xf2R\x94\x90G`b\xd1\n,i\x83-\x1a\xbb\xaf\xab)\xf5\xeevvy\xb2\x00i\x1ep\xc0o\x15\x8d\xf8\x16\x8a\xbe\x8d\x1e\xff\x1e\x95r\xf1\xba\xaa\x93E\x96V\x17d%\xc3\x1a\\\xe1\xe6\x189\xee\xc9&\xdf\x91\xeb\x05~J\x96,%\xa1cU \xf0QF\xc1iY\xec\x8a\n\xcf\x83F,\x1f\x86\x11\xec\xb1\xcc\x9d\xa4i\xcd|{u\xb9_\xb2\xb8\"f#m\x93\xb2\xbap\x84\xaf\x03TuR\xef\x9d[\xdf\x8f\xff'M\xd6K\xba\xe6f\x13\x13|LD\xcae(\x89FM@\x93\x90\xf1\x8f=\x8b \xe2\xd1\xf2t\x8a\xd9}\xd9n\xef\x16\xcf^<\xc6gM~x\xfd\xf2\xfd\x87W\xf3\x93w\xa7\x1f\xcf\xe7g\xe7\xc7\xe7\x1f\xcf\xbc2\xd9L8N?\xbc?}\x7f6\x02\x01\xff\x9b\xb3y\x93\x897v \xfe\x92\xdc\xc9<\xcc\x0c8Pt\x12\xb0\x10\xa9!,\xee+\xc9\xd2\xd5\xe3}\xce\xcf\x8b|\xdd\xd2\xb5\x83h\xec\x98J=\x8f\xe5\xaf\xfdT\xb1\xce\x8e\xc9!)\x17i]&\xe5M+\xc1X\xdd\xc2\xe6\xcc\xc7\xb7B8\x8d\xfcoz\n\xf9\xdf\xf4\xf4\xa5|C+\x16\xd8\xae$Wi\xb1\xaf\xb2\x9b\xc1V\xef\xa4WYi\x15\xc2\xe6\xbcL\x96\x97\xdc7\xc6-\xa7\xe6\x04H\xa4N\xc2\x9e\xcaP\xd2\xab\xdb\xc9\xc0\xce\xa6c[^\xa4\xe4\x8a\xbf\xb1R\xeck\x97\xd8)r\x14u\x1c\xd3\x17k\x15\xfe\xc2\xba\xfeG\xa1\xe7kYc\x95\xb3\x13W\xb0\x81\x03o\xf1\xb8\xf1\x02\xf4\xddP\xbf\x80\x06=c8\xe5\xb8\xe4\x12l\xdd\x18\xed\x81\x10\x81\xac\xcdt\x12\xbc\x11[9\xcd7\xf2\xd9\x9e\xa3u\x92f\xfb\x12q\x84\x04\xaa\x86w$_\xa1&\xd2g\xd6}\x94\xee\xd9\xc77AZj\xd8\xfa\xf4\xf8\x0c\x97\\\xaf6;\xfb\xf3\xc9i@\xb3\xef\x8fO\xde8\x9butr\xe88\xc3\xb4\xb1\xa17\xcc\xa4\x18\x1b+\x1a\x18\xf6yE\xdc\xb6\x1a\xbb\xd7\xf0\xc9\xa7\x1fNi\x9fy\xf4o\x8a\n#\x9d\xddD5h\xfb~UH\x97t9\xf4\xbb\xa4\x7f\xebt\xd9\x0c\xe9\xf1\xb2\xc8\xabt%\xbd\x0f\xac\xf3\xcb\x94\xdd_\xac\xf8[^\xdb\xb4\xaa\xe8\xe6\x14\xfa\xa8(aE\xb2\xe4\x86\xac\x90)\x8a\x06\"\xe9\xe2\xeb\x13I\xfff\xe4K{\xb5\xa8\xa5\x9c\xca\x0cS(\xba\x04\xac\xd0\xe3\x87\xaa\xf7\\>\x91|\x99\xec\xaa}\xd6\x90$\xe4\x16;\xc5b\xfc\x88\xd0Z\x1e\x88\xecd\x1c\x8d\x9c\x86\x07\xd5Ch\xdf\xd4\xdfg5\x13\xd2\x82e\xfc\x0d\xdaF\\;f\x89~\xca\x0f2\xb2A[\xae8\xc5\x99\x0e_F\x8d\xa5>\x95\x92\x81\xed]\x06B\xcfV\xdarL\xda\x92K\xe2\xf2\xc8\x80\xce=\xdb\xc7\xcd\x95H\xb1f\x96+\x9b\xf2\xa4\xae\x93\xe5\x05\xef\xad\xc9h\xa5{\x93$Ks\x81mu\xc7\x88u\xcc\xce\xee\xce\xc4m\x85\xc5\x82\"\xc1\xb9\"\x17\x0e\x0d\xf1g\x91\xed\xa4\x1fr\xe3\xb8\x9a$PI\xc5\xe6\x88P\xeaD\"\x19\x11*K\xe0\xc0\x11I(\x1f\xde\x017J\xd3\xbf1\xc8HL\xa83p\xa8\x9d\x86\xbb\x1c1\xd4\xa72G\xb2)t\xfc\x93hr\xec\xb8\x07\x19\x15S\x9e\x91\xb4\xc8\x8dg%\x14*&p!]\x99\xcfL\xce!\xa34\x1d \xb5\x1dx&\xfe\x01~\xfd\x00~\xab\x81\xe7\xc4\x80G\" \x1a\x91\xaf.\x84\xb0\x84@\xb8\xb3\x1ct\xe9H'\xc2\xda+\xb5\x0bp Q_(/\xc34\xe8\x10\x95F\x8bzb\xf0\x9b\x03\x97\xf2\x00?\x96\x8f\xe3\xa3S\x958\xf1y\xa7[\x82w\xd2!\xdc&G&8,\"\x96jh\x12\"\xbf\x166\xe7 \x8a\xe3\xdc!R\x109jK\xb2\xa1\xe8[\\\x03\x0f3\x07YN\xe1p\x9e{\x9d\xb0\xafx\x1c\xdd\xb2\xd8\xdd\xb4f\x1a\xffA\xcdO\x02\x96\xcdh\xea\xd2\xa1\xc7\xed\xda\xdb\xe9\xfcv\xaeH\xcc|\x0e\\\xdd\x89\xdc\x86r&\xb9\x92<\x022\xdb\x98\xf6\x14\xff\xf4\xc9OO7\xe4\xab\x9f\x93\x9f\xeb\xfdw\xdf\xd6\x9f\xbf\xfd\xfcm\x96]}\xfby\xf9\xa7\x9f\xeb\xea\xfa\x9b\xa7\xab\x9f\x9e\xfecu\xb5]%?\xef\xaf\x7f^&\xab\xd5\xc5\xc5\x1f7\xdb\xfd\xd7\xdb\xe5\xcf\xe4k\x1df\xbb\xb7w\xa2\xd17~\xdce\x92\x03IY\x05\xf2\x05\x81\x84m=\xeb\xa0\xff\xf4\xe4O\x7f\xf8\xe3\"y\xfa\xe8\xdb\xf5\xd7\xdf>\xfa\xe6\xdb?%\x8f\xfe\xf8]\xf2\x87Gk\xb2L\x9e,\xbe\xfa\xf6\xc9S\xf2\x15/Y/\xe5\xda\xd2\xfc\x96\x9e\xcae\x1b\xadO~\xfa\xd9\xc8\xe5\x9f>g\x97\xd7$\xd32\xd3\x91@9\x15;\xdb\n{\xde\x0c\xfd\xf6\x8f_}\xbd\xfe\xe3b\xf9\xe8\xbb\xaf\xbe\xfb\xc3\xa3o\xc8\xe2\xdbG\x7f\xfa\xf6\xc9\xfa\xd1\xd3'O\x9f|\xf7\x87'\xcb\xa7d\xd9c(\xefl\x14K9\x8a'?}62\xf5O\xd5O\xd9\xf2\xe2\xeb\xea\xf3u\xfe\xcd7\xff\xf8\xf6\xab\x7f\xfc\xbc\xa9\xffXV\x17W?\xdd\xac\xcb\x7f,K\xd3p\xd8\xf3\xcb\x94 E\x9e\xdd\xb4,\x80\x94%\xeeu\xae\x0f\x92\xac*L\xf4\x89\xd71\xb4\xe2\xda|VvN\xa6\x08\xb9\xe8\x9e^y\xf6\x96\xe2\x93\xab\x0b\xc8\x8a\xe2\x92Jg\x0d\x16\x91\xec\xc3\x1d\x926:l\xef\x004\xf6\x9a\xe5\x1b\xdc\xcaS\xc8\xe1\x82l\x9d%\x1b\xa6Z\x9a\xa7\x05\n\xf9\x19\x1b\xa6Y\x95r$B\xbb\x89(\xa2\x8a\xb4\x1aH\x1e\xe7\xaa]\x91W\xda(\x8f\x86\x1c\x91\x00\x7f\x87\x18\xd4M\xdaw\xf1\xc8\xbem\xaaQ,\"\x9f9A\xd6#\xfdmr\xa8G\x90\x99A\xe2C6B\xe3\xc5)C\xc1\x02\x8b\\\x8c\x903c\xb4P\xe0\x96\x19\xd1#\x08\xb3R\x8c\xf9\xc6\x0d\x8e\x1cn\x8a})L%\x95\x0dv\xaaDz\xd7\x07\xc1Ah\xa3\xfd\xf8\xbf)gD\x848\x81\xbf\xecIy\xf3X\x16\xfb\xfdp\xfa\xb2\x87\x8cg\x96\xb6\xdd\xcb\xd0\xde\xceg\n5\xc79\xecs\xf2yG\x96\xd4\xda\xe3oK\xc9\x9e;m\xaa\xe5\x05\xd9&\xea\xcc\x19m?\xb3\xdd\xc7\xf0\x0f\xe7\xdf\"\xcf\x97\xc5J#|y\x03S\xc9s\xb9P\xd2\xbc\xfe\xfa\xe9\x80?\x86r\xe1\x16\x1aV\xa4N\xd2\xec\x8e\xd4\xe4\xa0\xcd\xe7\xfb\xd2\xf8\xaa\x8dS7\xb2T\xd6\xe0\xd6Z\x17\xc4.)\x93-\xa9I\xd9\xa1\xf9\x11\xd7\xdf\x1d\x8b\xc9\xb4\x04{\x1b\xc2\xdf\xc4\n5\xac\x1a\x13J\xc164\xa7&7\xa2F\x98N:\x83)\xcd\x9f\xc1.\xa9\xbb1\xa1T*\xa5%Y=\x83\xba\xdcw7\xb2v\x82\xe5\\u\x8eJ\xc8\xc9\n>\\M{\xa4\xa2\x1c\xf8\x89JF-\x0b\xd6IV\xa1y \xd9\x8fd\x80\xef\xf9*\xecTe3\xf6CNM\x13\xf2\xabw\x1d\xa4\xf0\no\x7f\x1f\x888\xc5hF\xceh\x98\xa1\xad9\xfd\x86\xd8\x8e!c\x1f\xdaC&Ki\xc0\x16a\xe6\xfar\xc6\xc7\xc2\x06M'w\x997=#\x19\xc9\x9a@\xd3\xba5\xa2\x15l\xbf,\x07z\xd61\x92\x03A6u\xc7zV\x90\x19,\xe9C\x8e\xbfN6\x8a\xf5\xf2\x17\xd1\xcfc\xaaqI\x9e\xe4K\xf2xK\xead\x95\xd4\xc9\xe3\xab'\x8f\xe5\xb2~\x9cd\xc2\x16\xdb\x90\xc6\xcc\xae\xf6\xdbmR\xde<\x935\xed\xaa\xe3,\x83\x92\xd4eJ\xae\x08U\xebY\xb3-\xe4\xf8\x9a\xea0'+\xa5\xd9\xef\xe40\xf9r\xe8\xd0x\xff\xe9W_\xdd7\x1b\xf6\x9d\x043\xedb\x9a\xc8\xa47;\x00~ \xebXPc7oG\x07X\xb9\x9c\x8d\x1c\x10\xb64`\xaf\xf4\xaa/\"\xef*\xbem\xaf\x05\xcc\x92\xe2\x80,\xbe\x86\x9ec\xf1q|\xdb\xde\xdc\xc4\x87\x91\xf8\x84\xf1\xc0g\xc6\xfb\x0f\x1f\xc7\xb7\xed\xb5\x8d\xe2\xdb\xf6]\x88o\xdbk6L|\xdb\xde\xbb\x8f\xf8\xb6}|\xdb>\xbem\x1f\xdf\xb6w\xdd\xb1u!\xbemo\x82\xf8\xb6=\xa6p\xcc2\xbem\x1f\xdf\xb67\x02\xf2\x84\x8d=_\xc7\xb7\xed\x91#\xc2I\x01\x00\xf7\x9b\xe8\xf1m\xfb\xf8\xb6\xbd\x16\xe2\xdb\xf6\xf1m{-\xe0(\x8co\xdb\xc7\xb7\xed\xe3\xdb\xf6\xdd\xfbu\x93\x1d\x85\xb0\xa10\xf6\xd3\xed_y\xf5X\xdb \x0b3\x96\xa4Q\xc3\xba\xcc\x96eg.\xdc\xd9\x8e\x07\x1c\x93C\xc8\x0d\xc8\x84\xe0\xdc\xff\x16\x15\x8a+\xb8\xda\"w\x823S\x94\x9c\xa9\x91\xaf_w\xbb\x15\xff\xfbe\xf0\xa8Cl\xeb\x90\xe8\xfc-\xbemoE\x86\x19\xebD\x0b!d\xac#3*\x0d\x0b\xc52\xe4\xe5\x17W\xcd\x0d3\x93(] H} \xe0J\xf0k\x01\xb94\x00/'`\xa0=\x97\xb1\x9c[,\xe7\xe6\xcb\xab)t+@,\xe7\x86\x1f\xf9\xc4\x8b$l\xe4#\xf5 \xe8\x97\x90~\xf8\x83T\xfe)\xceZZ\xa4\xd3\x9d\xb8\xf4Qm\x07>\x80\xf5\xc74\x81\xad\x81\x14!X\xae\x83GI\x1b\xf7\x85\x16\x0c\\\x17N\xe54\x98v\xb7\x82\xba;\x9c\x1a\xad\x98\xf0\x05B\x10\xe53\xbe\x10\x9e\x0d \x07D\x89\x1aM\xab\xf8\xb6\xbd\x801\xa3\x9fh\xe1\x84\x8f~\xa4\xe2B,/\x1d+0\xf4\xfe\xa2o\xdb\x0b\xdc\x15\xf3\xa9R\xde\xf4_\xaf\xef\xb7\xff\xd2J\xc9\xc4L\xfb\xe6\xcb\xdfx\xa6=\x87]\xc2\xe2:\x8d\xf7\x8b8\xda[,\xcd]Ls#\xd6\xf9\xadM~\x16\x81\xec&!#\xe8\xd7KS\xa7\x95\xe8\xb6\x11/\x89\xe5\xee\x18%{Q*\x1b\xc7<\xe0\x04\xf1 \xe7\xaf'\x97\xa4\xde\x979\xf7\xc9\x9c&\x1b\"\x0b\x1e\xccr\xf2\xb9\x9e\xd3\x8f\xeb\xc2\x82mA6in\xbe@\x02\x9e\x19&\x9fD\xa28\xe9,\x11\xd8\x16U\x0dd\xbdN\x97)\xc9\xeb\xecf\x06\xef\xf3\xec\x06\x8a\x9c\xd8ua\xb1^\xf3 a:\x0e[\xbf\xd5E\xb1\xcfV\xb0`A^&]\xc9\xb1M4?{\xfb\x157~\x86\xc4\x18\xd9$\xe5\xfb-\x8b\x1f\x16\x7f\xe3\xe1\x1bIN\xc7\xc5\xdf\xa7\xbc 9c\x85\x19]Z\xc1>O\xae\x924K\x16\x99\xed\x8a\x11\xe0\x84\xf5\x9a\xb1G\x06\xe5\xdc\xd0>s\xd8\xb3\x97\xc8.I\xd8D\x89\xd9\xb0um\x9f\xa8,\xdd\xda\x82\xa7~\x99ybDI\xcb\xa6.\xea$\xeb\x94\x87\x93\xcf\x82\xd5\x05\x1dZg\x97Y\x10\xd6\xcdsbl\x93\xd8\xa7j\x0d\x19Y\xd7@\xb6\xbb\xfa\x06Rq),\xb2\x0e\xf8\xe3T|\x83s\x02\xe8\\,l\xab\x84E?$\xbb\x9di\x06\xd8u\xfc\x9c\x8d\xd25\x0f\xae0U\x8c\"\x03\xaf\x99\xe8\x10\x07,\xda\x91q\xa0.\xf7\x04zA\xd1\x89\xab\xa4\xae\xe0?E!D\x88P\x82\xb6\xc9H8\x05\xd2\xd4\xea\xaf\x05\x96\xb6\x06\xcd\x16\x04}i+ \x1d-\x96\xe6\xf0\xf1\xa4\xb2\xae\x83\xde\xd0Y\xed\nj\x8c\xf0\xd21L<\xb4\xf2\x84\x8a\x8c\x19\x9c\xd8\xce\xf7i\x05\xe9&/\xca\xael\xb1\x7fn\xd9\xb6%\xa1\x07\x1cK\xa8\xcc/\xb5`\x04a\xfd\xc5\x92\xb6\xfb6)Io\xef\xda\xb8\xc0\xef3h\xff\xfcIT(\xca\x15)m\xa1\x15\x00gi\xbe$\xcf`YT\xdb\xa2zT\xad.\xe1\xab\xd97_k\x1b\xb8\xc2o\xb8\xe2n\x8c9N7\xd9.\xc8j\xc55\xfb\xe6\xc3\xe9\xcb\xc6T\x13\x81C\x95e\x1d6\xd2\xdfX\"\xaeY\xa43x=p\xe6\xd8L=\xf7$u\xcd8\x99\xcb\xc2\xbc/\xdd\x8d\xd1:\x0b\xd9\xa6\xd3\xda\xa7f\xa3|\xc4\xb1F\xdaE63\xde\xa2\x89\x9c\xd6\x9c\xc2\x9f\xff5\xcdxc\x9d \xf5\xc3\x0d5:\xed\xbb\xa4\x12\xb9 \x9dU1\xb3\x1ar\xccFs\x1ah'B\xc7\xb0\xa5\xc5\x94\x8eI\xff\x0fc\x85\x8c\xe7K\x00\x8bJA3\xd3\xa2\xd2][\xa7\x91\x9c&\x05\xde\x8am\xe3x\xbb\x8c\xee\xc8c\xd3\xdeaO\xbd\x92\xfa\x08\xd2\xba\x12J\x9a\xd9h\xfc<\xb3\x82\x82\xf2\xef:\xad\xd4\xf5a\xdf7\x9d\xda\x04>%\xcb\x86%\x0d$P\x81\x11\xcb\x96\xc5\xb2e\xf6\x0dx\xcbe\xcbb\x19\x98\xdff\x19\x98v\xfc\x1d\xbbC=\xf5\xda\xd4f\x80\xcbC86\xbaXB<\x19=\x7fE\x17\x9d\xd1A\x11\xc2\xc8\xc1&\xd3\x9a\x19\x1a&r\xfa\x90\xcb(\xc41aw?\xf8:\x1c\x9c\xfcT\xd1\x1f\x98\xb9=\xb3C\xc3^\xe6\"@r7\xcc\x9d\xd0q\x1a(\xd84\x0e\x84 \x97\x81\xd61p\xcb\\\xec\xdaTm{\x1b/}\x1d\x02\xc3c\xbf\x82N\xeb\x02\xf0?\xf4\xab'\x18\x05\xd9\xf0\x98\x1ft\xb0\xef\x1c\xdf\x15d\xfa\xa3\xfc\xf0\xf0\x1e2\xb1\xa3\x84\xb88\x88#\xa75\xe4\xd8.\xf6\x88\x82\xc7uPw\x1e\xcd\x0f\xc9'S\xcd\xb3\xfb\xa6\xa2gl\xd3\xb2Z\x0b\xd5E\xba{\xfcO\x11\x92\xf1/Q\x86LW\x01\xad\xc3\xd7\xbf\xd2\xd6\xefek\xc1\xb6N\\\x99r\xb3\xc9\xf6K\x96V|\xd3\x88\x1c\xf9\xde\xcb\xee o\xccE #K\xae0\xa5\x9a\x9a\xda\xaf\xf8\xe2\xee\x16Tk\"\xa6\xa65\xdd\x0d\xd6qo\x8c\x8c\xe3\xc5\xba\x99\x90\n\x1ePR\x1e\xear\xbf\xd4Y\xf9\xd2/\x91\xad\xb5\xb7\x1c\x07\x8cx\xef\xca \xde\xbb\x0e\x00q2Ex\xea\x00\xcd<\x08;\x84X\xb0\xc5{W\x8c\xf3\x0f\xbcf(\xe4xcA\x17\xef]9L>Oa\x07%\x0b\xc2x\xef\xaa\x00~&|\x8fY\x16T\xf1\xde5\xde\xbb\xc6{\xd7x\xef\x1a\xef]\xe3\xbd\xeb\x17t\xef\xaaz1PW\xaf=\x0c\xfc\"\xb6\xe7\x85\x89\xb7\xaf\x1c\xe2\xed\xeb\x1d\xba}\xed;\xfa&|\xe3(^k\xc6kM\x9f;\xa3x\xady@\xe6\xba/\xe4\xe2\xb5\xe6\x14\\\x8c\xd7\x9a\xd8ss\xbc\xd6\xfc\x8d\\kRF>\xbez\xf28+\x8a\xcb=\xee*\xf3\x03g\xde\x1b\xd6\x82Q\x9f\xcaB\xf7Y\xc6f\xa6\x82E\xb1\xcfW\x9d\xf2|\xba\x8bK\xe5rRA\xfa;\xc9\x88;z7\xa9\xaf2=\xc2\xa25\x18\x8d\xf2\xc5\x9a\x9cc\xa5\xd2\x00\xcf^ h\x17\x85\xe6\xc2\xa9[\xfd\xb2\xf3\xb3\x0ci5^)E\xafC\x17\xa2\xd7\xe1\x8b\xf7:0\xd9\xa9\xc8\xa8p\xcf\xc3\x07R\x15\xd9\x15\x89\xee\x86\xe8n\xb8\xeb\xee\x06e\xfd\xc9\xf2?u\x01\xeb4_\xf1\xe7\x0f\xe4\x83\xa5\xaa\x87}27E<\xa6\xfb\x9d\x81\xe21\xfd\x80\xccu\x1f0\xe31}\n.\xc6cz<\xa63\xf8M\x1f\xd3u\xa7t\xa6\xbd+\xf3\xd1\xfc\x94\xfd\xde\x9c\xc9\xf9\xe7r\xc52}\xbd-V\xfb\x8ch\xe3\x83\xdf%[\xc2\x11\xfcN\x8e\xf0\x8e\x9e\xbf\xbbl\xe8B\xef8\xcb\x06/\x8f\xb2\xfc^\xbd\x89\x95\x14\xa7\xdb\xfd\x16xw\xd2M\x00\x82\n&\xea\xb3\xac\xb8\xd6\x11\x9b\xe6\xb7Nl\x9a\x87\x12\x9b|\x9e\xd3o\xe7\x19\xb9\":#\x1f\xa6\xa5\xd4x\xa8\x95\xfcn\xa5\x7fw\x08U3\x86\x19\xc0\xeb\xcf\xc9vg~0\xf8\xd3\xba(f\x8b\xa4\x9c-\x92\x9f?\xc1\xb54\x11u\xc11\x0c\xe3|\x9f\x97\x84\xaaV\xaa\x15\x183\xacl\x18\x1be-\x18\xb1\xa2{f\xcb\xb6Q\xba\x86. |\xe0\x97\xe4\x86\x8bbF#=b\x97\x90\x17\xea\xc6A\x9c\xaf\xb9\xf0A\x1d\xac\xc5QZ\xc8\xbb\x0f\xa7/{\xf8\xe2\xd9:\x9e\xado\xfdl\xed\xe5p/\xb9\x17\xe8\xf1?\xe9\x1f\xec\xeev\xee.\xea:\xda\xeb\xce;U\xc3\x97\xf1\xb878o\xdf\x0e\xecy\xda\x19>\xf1\xd3\x9d\xd5\xf1\xc6<\x14\xeb\xdcH\x8f\xb9\xf8]V\x98\x94\xc7\xf8\xc6\x85!\xcd!1\x0b\xd5\xd0y\xdb\xca8\x13 &\xc1\xe9\x12\xac\n;\xffK\xc4L\xf0\x97{\x95\xb7B\xa5\x81\xc9\xd4\xc9j\x05\xd5~\xf1\x88I|\xd5\\A\x08V1\xe5>\x92U]%\x12\xa2X\x8db\xf5\xd6\xc5\xaa\xcde\x99\xab\x0f\xa4*\x8b/\x17\xcf\xb3\x8a=\xae\x08\x80I\xdd\x95\x98\x13\xdc\xb6\xda\xac \xa9\xa8\xe4O\xb2\xcc,\xedY[v\x91\xfa\xb6\xdaT\xe2\x91d\xf6\xf2\x18m\xaeK?\xdcj\x0fs\x0c\xcfq\x96\xbd\xad6\xdf\x13r\xe7Ot\xdbj3\xa7\x03\xbc\x1b\xbb\x80R3z'$+\xfez\x7f\x92\xd1\x91\x19\xd18\xdf\xf9S\xf10\xa7\xeb.\xb9\x814\x87$\xbf\x81\x97E\x9a?\x80ER\x89g\x83\x13xE\xf2\xc2\xfc\xee{\x92\xaf\xe0x[\xec\xf3\xfaH\xfcW\xbaq\x7f&eaz\xf2\xdb\xc1O@\xf0\x14\xd8\xd2\xca\x8b\xad\xf9g\x04S)$\x8c\xec\x91h\xecZ\xb3\x05\xca\xdf\xf6:\x1c\xea\xe2\x92\xe4\xd2\xceb\xc3\x91\x0e>\xca\xd8$\x17\xc4\xb9\x9f\xf8;\xa7\xc2\x88\xf3\x9f\xbf\x8e\xcc\x1f\xf1>\xc9k\xb1\xe7Szl\xdc\xca7\xe4\x8d\xd8\x96\xfb\xaa.\xb6B1\x9b;m\x0c\x8b\x8eY\xb1\xb8\x81M\xb1)veQ\x1b\x9e\x1f.\xc92\xdd\xa5\xc4\xccl'\xa3\x1b\x0cs\xbaD\xab\xf9\xaeH\xf3\xda\xb8B\xec\x8a\x97\x83\xeb\xf4n\xdfP\\\x1cJ\x03hY\x94\xec\x96\xe1\x9a'W\xd7\x15{\x9f\x9a\x9e_\xb9\x9f\xbd}\xb8_\xcf\xd9\x94\xbd\xe2_\xa5U\xcd\x1cJ\xebb_\xb2W +\xfd\xe7Of\xdc\xe7Tm\xb8\xc5\xb5/\xb3#Hgd\x06\x8f\xb9\xebq\xb6H\xf2\xcb\xd9\xd5\x93\x05\xa9\x93'\xb3\xb7\xd5\xe6\x8c\xe4\x86I}:k\x1c)\xad\x80`:\xe2\x81\xd8\xcf\xc5\x9a \x08\xb6H\x1f\xea\x91|=k\xc3;\x9a\x99b#!\x84\xca\x14\xc6 m\xcbO\xfa\x89\xfd\xa4\xef\xe7\x9b\x19\xf7\xd9\xca\x1e\xd2\nVd\x99%%\xcfd\xbf\xe17\x13\xf4w\"\xf44C\xaa\xc5\xc5;\x92\xfe;J\xe8\x83\xaf\x1e=\xf9\xea\xe8\xab\xaf\xbe\xeaK\xaf\x18\xf9\xa2@\x8c|i\x7f\xc22\xf3\xb7\x10\xf9\"\x06\xd2\x9c\x06\xe9\x1ao.\xbf\xa9%\xba\xad6\xf7\xf9\xab\xb3f#\xb4\xc1\x18\x8f\x84\xeao\xf1Hx\xcbG\xc2\x182\xe2w\x1f\x1fCF\x0e\xc8\\w\xb0C\x0c\x19\x99\x82\x8b1d$\x86\x8c0\x88!#C\x87\xa3g\xd4H/R\x82\xae\xca\xcf\x12\x9dh\xaa\xb8\x1a\xf9\x99:F\x8ep\x18q\x00\\gEQ\xce7I5\xdf\x95\xe9\xd2`2\xb9\x8e\x1d\xcb\"\xaf\xea$\xaf\xb9\x8e\xad\x0bX&\xd9r\x9fQQ\xc6\x8cw\xb6\xcf7I\xc5\xffU]$%\xa9\xb8\x93\xc0\x80\x90\x99\xf8\xdbjC\x1b\xe8)r9\x07]\x16\xa6\xd31\x880\x12]NA'\n\x9cCpjw\xe0\x94\xce@\x94+0\xc4\x11\x98_$\xd5\xc5|G\xca\xf9\xbeZ\xcd\xb7\xe9\xe1\x0f\xd5BS\xd2~aGJ\xd8W+\xd8\xa6\x19\x93E\xcb\"\xbf\xa2\xab)\xdf\xb0?\xd7\x05\xffN\x83\x8e\x7f\xc9\x1e<_\x132\xb7,2'\xe9\x82\xae\x16#\xf3<1\x8c\xd2\x1e\xe3\xff\xa0$\xa5\x95$\x92\xed@\x05!\xe2\xb66\x86\xc1\xc4\xc3\xb9\x04\x97\xe8\xbc\x8b\x87s\x8cuR\x7f\xa6\x86I\xa3\x9a\xe6\xdbj3g\x0e\xe7\xf6\xaelWT\xd62\xba/e\xe3\xf3\xcf\xdf3U\x96n\xd9?+ \x9f\xc9r\xcf\x04D\x02u\x99\xe4U\xb2l\x9c\xb7\xa4\xaa\xd3m\xc2~\xdc$\xad\x87yO\x97\x01\x17\xda\xaa7]{\xb9\xda\xebZ|rg\x8d\x1e\xf5\x06\xf1\x8e\xac\\\xab\xcaw.<\xbb\xbaw4\xc7\xa8\xfai\x15}\x90\x9a\x17\n]\x8b\xd0\xa6\xe4\xfdU\xbc\xcd\xa6\xec-\x9e\xe6\xf1\\1\x90b\x0d\xcb\"\xcd\xa5\xdb\x81e\x81\x8b\x16\xd4h\xd4 \xec\x1c#$0u\x1f\xd7&\xc4\xb59\\\x9bn\x86\xb4\xab\xa7Y\x9c\xdc~l\x97(\xfb1'dEV`\xae\x92\xdc\xd1\x15\x15<\x10G\x9e\n~/\x8fK\xda[\xd4\xbc\xa8\xa9\xc1zA\xe4W\xcc\xfc\x93\n\xa2S%\x9c\x9d\xee\xd8G\xect\xa7A\xc5\x8f\x94\xfc\xac\xd9g\x84\xd0[dE\xcf\x87^\xf6\x8a\xd3\xee\x16\xbb_\xe9AZ\x9d-\x0b\xe9\x1fU\x0ev\xf9\xa5 \xb5OYOyz\xd8\xb9\xd1\xc0\x8d\x06\xae\xee\xf7\xdb6pm\xb7O\x8bb\xd5eN\x9a\x0f\xfed\x8c,\x9ch\xd1\xd5\x9f\xe7\x94\xd6P!\xa1\x0d\x1fP\xf6\x90\xec\xa0\xb9\x88\xe8\x98\xd8u\xd1\xd8\xe1} &\xb6&3\xf3Mgq+\x85\xee\xf8\x85a\x17\xd2\xf3\xcd\x08#\xb5rP\x17\x92L\xfa\xc24\xf8\xd25\x15\xef\xac\xa1\xe6.\x84\xb9\x1d\xfa\xa3\xdc$\xd5ENF\x17~ U\xa4\x90\x99j\xd2B8x\xe8\x14k\x81~e*\xce\xe5*\x90\x8b']\x99B6\x19\xfbve\xb1)\x93\xedm\x93i \xa4=\xe7O<6\xcf\xdaP2A\x90)/YY\xd2\x0b\xfbaA\xb2\"\xdf\x98\x9eb\xa16\xbf~\xf8\xfc, 6\xfa|W\x14\xd9|]\x94\xf3\xee\xd4\x99\x18\xe23\x90j\xbfe\xf1\x95Y\xc6-\xff2\xc9\xa9=)\xfa\x15\xa2\x8b\x9e\x03h\xbfFL\x9c\x1e\xfd@\x9c\xab\x15\x10+\x16\\gY\xc0-\x04\xd7\x99\x160h\xba\x93\x82\x9a\x03\xca\xe8u\x9a\xd3\x83\\\x96\x15\xcb\xc4\xcc\xe1\xdf\x18'\xf9\"\xe7Ww\x13l\xef\xa9vw?\xbcA\xee\nq\xc78\xc1\xa6P$0\xc9W\xba\xdc<\x0e|\xf8\xf6g\x14\\yz\x1c\xb0\x1c8\xe6u\x90K\xb2+IEr\xe6aN\xd7\xad\xfa\x96\x82\xbbaCO\xfd\xf7\xe1\"\xa9\x80\x0dQ\xc7\n\x0cQV\x9b\xe0\x88\xd3@\xad\xe3\xdd\x8a\xed,\x11\xe5B\xf2\x95\xe9\xb9\x1brE\x0d1\xfb\x86S\x08{#\xac\x1e*&\xad\xd4\x88\xbd)C\x10\x18\x93\xfa\xb8\x0f\x1a\xcc=4m8X%G\x8c\xe6\x8e\xd1\xdcw6\x9a[\x7f\xb5l?'4\xae\xb8\xee\x93\x94\xe2\xe8\xa2\xdf\x8dd\x85;zD\xc7\\t\xcc\xf5\xe1\x96\x1ds1,\xdc/\xe66\x86\x85\x1f\x90\xb9\xee\x80\xe6\x18\x16>\x05\x17cXx\x0c\x0bg\xf0\x9b\x0e\x0b\xbf\x1f\xea\x04\x7f\xfcO\xf1\xd7t\xf5/\xe5\xeb\xea\xf1?{\xdeP\xe4\xdb\xe7vk\xf1\xc5\xcd\xc9\xab\x8e\xd7\x1c\xeb-W\x9d\xe5'\xaf\x02\\\xe4\xb4g\xd1\xea\x8bu\x93\x9b\xec\xc2\xa0\xe3+\xca\xd9\xed\xb4\xd2\x10\x07\xaf\x1077\xd2\xc9}\x10\xf2\xbc}\xd8\x06_u\xb0\xa7z@\x91\x973\xda\xea_s:J]\x87\x02\xa7\x93\xd49%n\x07\xa9\x03\x85\xdb\xcd|\x08'\xf3\xaf\x81sn\xb7\xb2\x93\x06\xb7Ky\xc0|/\xaf\xb1e\x0e\xb0\xbea\xb7g\x18\xe3\x17\xc68`\xa7\xf6 [<\xc2nr&\xf6\x06\xbb|\xc1#\xbcPT\x15\xeb\xae\x08y\xf0\xe2\x12\xc2~\xeeRO^|\xf7\x99Mr\x97Y\xc5\x81\xdb\xb0\xf2\xd8\xc0P\x8a\xe0Rz(#Y\xbaI\x17\x99*\xc0\xaa\xbe\x1f\xaf\x0bbd\xbf\x95x\x16\xae(\xb8LAM\x05;\xf5\xb2\xcf\xe5\x92`(\xec\xfc\xb2\x8e\x83\xe4{\x0b+\x1e\xc1\xcb7\xc7'o\xe7g\xe7\xc7\xe7\x1f\xcf\xe6\x1f\xdf\x9d\x9d\xbe~y\xf2\xfd\xc9\xebW\xf8&\xec\x9f\xc7/\xde\xbc\xc66 k\x80'\xe9\xf5\x7f\x9f\x9e|0~.\xcde\xdf\x91\x0b\xc3\x9f\xd9\xe4g|\x8a\xc4\xc5\x04\x13f\x8dW/5n\x00\xbe\x1a~\x01\x01c\xb3:\xec2\x05#'\xb8^<\xe6\x92\xbd\x9b\xf4\x924\x0fw\xdeW\xd44\x97\x1f\x16a,\xc5\xc4}\xd7\xe1X\x1b\x94\xa3\xd0\x13#oZ\x88\x917\xfd\x8fc\xe4\x0d*\xf2\xc6.\x05D!~\xb3q\xac\x8d\xbe\xd1:\x84\x84\x15\xcf\xe4C\xaeZ\x9b\xcch\x07S\xce@\xf4\x82D/H\x1fn\xd9\x0b\xe2xcT{|\xb2\xa9\xe0\xd1\x87y\xd5[\xc1\xf7\x8b\x89\xba\xff\x1d\xba*\x84\x19j:\xeam\xf7U\xcd\xca\xf7\xf7\xee\xe8-&\xe5\xb3\x8e\xa0a6\x93\xa3ac'\xd2\x86l\x14L\x16\x0eFb\xb10\xd5\x96\xcd\xff\xb9\x1bsj{M\xfbYez\xbbSmH>\xefR5\xf4 \xe4\xa2|\xb0P\x87f=\xd2\x94G\x9a\xef(\x93\x1da\xa6;Ms\xbc9\x1e\xe3\xdd\xfc\x96H\x8cw\x83\x18\xef\x16\xe3\xddb\xbc\x1b\x87\x18\xef\xa6\xe0\xf9\"\xe3\xdd\xec\xf7-\xc2k\"Z\xa2\xaeYNE\x13\xf3\xc5\x8aD\xaa\xde\x9a\xa8\x03\xa7\x83\xb3\\\x99\xc8N\xc4\x17w\xf6\x92D\xcb\xc6.\xfc\x12\xc7\xa0[v\x17\x1e\xe7\xf2\xb4I\xf7\xd0>O\x7f\xda\x93\xec\x06\xd2\x15\xc9\xebt}\xa3\xb9\xa50\\E0\xdfG8\xe5.\xdf \xb0G\x9d)\x8d\x17$\xdb\xa9\xf4}\xd0\xb9\x16Z\x10\xcbq\xf6\xe0m\xf2\xf9\x9cv\xf3\x86\xbd\xd7\xfb\xfc\xc97\x83wV8t\x19t\xc8\x11\x9d]\x14e-7\xab\xe8u!w\x9c\xc7\xa0^\xb5\xf4\xca\xa1}\xa5yD\x86Cs\xcfL\xe6\xeb\xb2\xd8\xce\x8d7\x85\x1c\x10\x03U\xd6\x93r\xe4\xa5\xd2+]\x91\n\xd6\xfb|\xd5\xbe\xf7\xd9\xbdd\x82]Qd\x88\xdb-\xaf\\\xebA\xd1\xac=\x17\xfc\xfc9QqC\xac\xc8\xaa\xdf\xca\x05UI\xb6\xfc\x11Q\x1e\xa7\xb9H2\xaa[P\x8cu\xe4L7\x98\xc5d\xb7VP]t\x16\x1d$\xeb\x9a\x94`\xbe1T\xaf\x16\xd9{\x81\xdc\xcd\xfc[\x99\"\xe1y\x98\xdbQ\xf9LM\x7f;$Y&&I^\xf0.n\xd8k_\xe92\xdd%ym.V!/\x82wIU\xab3\xf5[\x99\x9dm\xf2Y\x8a\xa4\xc5\x8dKx*\x93\xf4V<\xb5.\xe5\x1e)\xd5{:\xfa\x07\x81\xef7\xc3L\xfe\xee\xdb\xbc,\xb2\xac\xb8\"\xa5\xcf\x9a\x7f+\xdf\x8c\xebU\xefdK\xb4\x8d\xa8(@\"\xff\xad0\x95\xbb\x7fE\xb0ZE\x96E\xbe\x1a\xa3\xdd\xfdM\xcaw\xcdyX\xf4\xce\x8d\x81^\x16L\x96T\xfaK\xba&hd^\xd5IY\xcf\xebt;\xc6\xb0\x94\xf4\xaf\x92\x9a<\xa2\xb80C\xb0 \xd4tK\xe4\x80\x14\x13Bz\xf7\x18\xd5,\xee\xac\xbaN\xd9\x1d\x96\xe9.\x14\xe0\xec\xfc\xf8\xc3\xf9\xebW\xdc;\xaeg\x86\xbc\xc2j\x02\x96H\xbe\xfarx\x92V\xcd\x00\xe8^$\xf9\xea\xc8\xfe\x1c#\x85UR'\xdc}\x91\xd6\xf2\x06s\xbf\xb3\xaf\x15\xc9\x95\xf96\xf9|\xdb\x9c1\x0f\xff\xed\xc7\xb3s:h=\xed\xcaN\xbd\x83\xd3\xfa\xb1b\xae\x94\x9a\x949{\x91Wy\x82\x83.\xf0\xbaL\x96\x97\xfcbk_\x96$\x176\x81\x11!\x1f\xea}\x16\xfc\xcf\xce9\xe9\xd6\xb0\xea\x93e\xbdO\xb2/`\xcd\x93\xbe\x10`{\x9e\xaf\xf6\xefO\xde\x9d\x9c\xfdh\xdf\xde\x14N\xf2\xb4N\x19\x87+RCRqo\xad{\xcd\xfcRb]5\xd1Y\xbc\xa2\xd4\xb7\xfc\xf9\xd3}\xae\x8b\xbe\xe1 V\n\"=\xec@\xe3\xb0\xcc\xe9\xcb\xee*\x96\x8aJ\xf8z\xd5\xe3\"\xdd\x1b\x15\xc9\xd2\xf5M\x9ao\xe6\xa2\x16\xbc\x9d\xdfzG7\x07\xa3\xbb\x9b\x03\xe2\x10\x85;F\xd19\xcb\xc8\xc6\"\x0c8\xa0:\x04t\xa7\xd09\x8d:x\xd5\x02b\x95\xb6\x80Z\xaf-h\xcf\xba\x9du\xd5^\x90H~\x19\xbd\x06\x12\x844\xbf\x8b\xe3\x1b\xe8\x99\xa0\xf1\x89\xf9\x13\x0d\xa8\x94q\x1f\xa79`\xe5D\x0b=\xf7Cs\xa9\xbc\xafH\xd9\xc6J!\x10 {(\xbbi\x06\xda<\xda\x7f\x95d\xe9*\xa9M\x96T\x17\xd0\xdb\x01\xbc\xb6\x04`\x1c\x1b-x-\x18\x8c\xb3\xa3\x05\x0f\xd4r\x9d\xdf\xce:\x10K7\xae\x83\x0exL\x16\x1cp\x1dt\xe4\xf9\x15\x99WurI\xa8\x85\xbd$T\x19;\xf4\x0b\xf8\xf5\x15\xb4t\x84\x08iIRB\xaa\x16$+\xae\xad\xb6A\x0b\xcd\n\xb9_\xc1\xae\xb8&%\x94I~iS\xbaR\xeb\x19\xc8;\x02\xf9\xa0\xc80\x1c\x7f^w\xc2\xf0%\xc4\x12\x07*(c\x8e%\x0eP\xcc\xfc-\x948\xb0\x96uT\xd3X\x9ar\x06\xcb\"\xaf\x93T}MD\x9f\xf2\"\xc1\x92C\x13\xeb\x17\xa8\xbf\xc5\xfa\x05\xb7\\\xbf\xa0\xd5!\xa6%\xd8\x13\x97\xc7\xb0N\xb3\x9ang~-\xc1\x94\xa8&slX \xe0/\x1f_\x7f\xf8\x9f\xf9\xf9\xff\x9c\xbe\xee\x97\x07\xa8vd\x99\xae\x99e\xa8\x12\xd2ow\xfc\xe6\xcd3v@\xe9\xa7\xa9\xb1\xfb\xefAu\x80NK\x11\x80\xf1\x0cv\"\xea\xac\x17\xb3\xc2\x10<\xb7u\xfd\xf2\xfc\xe4\xaf\xaf\x9f\x01\xf7\x91k\x9b[Z\xbf\xffx~v~\xdc#\x81=goFg\x1b\x8e\x8cCx\x06\xeb4O\xab\x8b\xa6\xca\xb3\x91\xa2\x90\xc4\xc5\xc1B\xd3\xd5\x1f\xd0O\xab\xf9\xa3\xe37o\xcc?\xea\xe2d4\xd3`\xfe\xbd\xc3h\xf3G\x9a\xe8\x99&\x12\xc61\x9cX\x8e\xc0o\xc5\xc4r\x04\x10\xcb\x11\xc4r\x04\xb1\x1c\x01\x87X\x8e@\xc1\xb3\xfa\x12\xcb\x118\xea?K\x9b\xec\xf1?\xed\xaf\xe8(G\xa6\xdeK9=[\x06\xf3\x1e\xce\x00\xdd\xef$7\xbe\x88\xba\x03\xa6\xd3K\x90;\xe5\xd6\x1e\x90\x99\xa6n\x80\xa5j\x80\x93b\x97+\"\xb0^\x80g\xb5\x80.S\x0e3\x8e\xf0*\x01\x815\x02\xbc*\x048\x87\xa7\xac\x9a\xc9\xaa\x03 k\x03(\x9dOY\x19\xc0\xe1+pmSp\xdfN9\x19\xeb\xbe\x85r\xa0\xf0\xa9\x05\x80\xbb\x1b\x08\xa8\x03`\xcd\x18CW\x01\xf85L\x07&\xef\x1f?\x0d\xde9\xfft\xff\x99\x06\xe6\x91\xf1\xffk\x98 \x8f\x1c\x7fm\x1cAp\x86\xff\xaf\x82y>9\xfd\xda\x10\x93\x91\x19\xfd\xbf\x06&\xe2s\xf8\x9d\xb4\xf8\x1av#\xb3\xf7\xb1\xb9\xfbh\xba\xed\xf9\xbaH\x818]\xce\xbe3c\xdf3_\xff\x8e\xf0! O\x1f\x9f\xa5\x8f\xcf\xd1?\x047\xc2\xb2\xf3=r\xf3\x0fA\xb4q\n'\xce\xc9\xc7f\xe4{\xe5\xe3\xdf*C&\xcb\xc3\xc7g\xe1#r\xb5\xd1,\xf0\x17\xcb#\xb2\xef\xf1\xb9\xf7\x93\xd3o\x9c\xbfis\xeem\x19\xf7\xbe\xf9\xf6\xb7\xc7\x83 \xf3\xecm\xb6\x91%\xc7^\xa134\xc3\xde\xc90[v=>\xb7\x1e\x97Y\x8f\xcb\xabGf\xd5cr\xea=3\xea\xc7\xe7\xd3#\xb3\xe9\x9d\xb32\xd92\x9e4\x8b\xde?\x87\xde\x95+o\x94\xfe\xd8\xe0f\x03|\xd0\x8e\xccoG\xf7\x13\xb0H\xa6\xcal\x0f\xcc\xd9\x1e\x99\xd5~H\xceL\x95\xcf\x1e\xc4\x19\x1fj\xa7\xcdd\x1f\x91\x8a\x89\xc9bG\x8a\x02\xac\x18\xf8\xe2,\x1edf\xf7\x17g\xf1`\xc7u\xdb\x16\x8f;\xf5\x12W\xf6\x05\x99\xa9.v\x99\x13\x9dW\xea%r\xcf\x80\xc7\xbe\x81/G}\xfa\xcc\xfc\xb4y\xe9#D\xa1+'\x1d9\xa5\xd8\xe9\xfc\xe2\xc4 \"W\xfb\x8b\x13\x81\x981\xfdJ\xc5_7\xdb\x97\x896'\xc2(\xfaPhC\xf2\xcc\xd1\xc8=w\x82+\xfc\xad\x85 \xf3\xcb'\xca.\xf7\xc9-\x9f.\xb3|\xda\xbc\xf2\x11Y\xe5\xce\x9cr\x1f\xf92e>y\x90zU\x88\x9d&\x97\\A\x19\x98I\xee\x9fG~>\xb8<\x10\xa2\x8ew}\x9dX\xde\xc8\xc7\xa7\xc5\xbe\xb89y\xa5\xcf\x8c-y\xee\xee\xe0\x8eN?\x84\x98\x07\xab\xfe\x16\xf3`o9\x0f6]\x99\x96\x1e\xddH\xe9J\x7f\x1fG5\x0dK\xbf\xe8\xee\x9f\xd1Ox\x1bt\xa7>\x1f\x83%\xb8\xa7\x8d\x19\xcbSEf\xc9\xbe\xbe\x98]=Y\x90:y2\x13\x0f\xf2\xbf\xb8\xa9Iu^\x9c\xb1\xce\xe4\xa6\xe5\x08\x07\x93=\x9c^\x11\xb28\xe7\xb4\xb6t\x0cF\xa0\x17\xf76\x1a\xe4\xc5\xa7\xdc\xd0\x0c'\xb3\xffD+\xfe9\x94\xbb\xa5\xc0\xb6%\xf5E\xb1j\xb3e\xb4y2\xdf\xd9\xd9\xc1\x91\x9e\x17\x8c\xa4`v\xd0\xe5UY\xb8\x01\xba\xa5he\x91\x96.'\x8b\xd8\xd7Sq\xe8\x05Y^|\xfd\xf4\xb4$\xeb\xf4\xb37g\x16\xac\xf1|\xc7Zc\xd7I\x93(\xab\xeb\xda<\xf8\xee\xd7t\xf0\xed\xb0\x03F}\x9a\xb4\xef\x7f\"\xc6\xb9M>\xcf\xb7d[\xcc\x97\x17I\x99,U\xb1b]\x07\xea\xae\xfe<\xaf\xd2\xcd\x9c%]\x06\xb7\xff\x99\xcc\x97EU\xcfw\xa4d+2\x04\x11\xa5\xe2\x8a\x94\xe9\xfa\x86\xe3\"\xab\xa7\xdf~\xfb\xe4OS\xa0\xaa\xc8r\xf7\xf4\xdb\xef.\x9f\xf8#SV \x9f\xa1\xa6R\x0d]\x13\xadLo\x0e\x8ctRa[\xac\xf6\x19\xbb\xe7\xd7M5\x13\x9d\xc7\xfc\xa4\xdal\xa0\xd6\x92A\xaf\x01q\xd8\x1dF\x87\xdbW\xfb}\xfd\xba\x9c=\xbd\xcf;\xee\x99_\x0ej\x8d\xdbC4\x1f\xb6\xecl\x15\x04\x83B\xb9\xd2\xe7F\xcf\x96\xd0[\x10z\xbb\xc1\xa8\xef\xedg\x8bc\xf8\xf8\xe1\xcd\xe3\x92T\xc5\xbe\\\x12\xa6\xe9\xb9\xf5\xdb\xcfTK\xc5jb\x9c\xe3\x9a\xbe\x87\xaa\xa2\x87\x86,\xfd\x99\xac\xfa'\xa2]Y\xd4\xc5\xb2\xc8`\xb1_\xafI)M\xb7\x19\x9c_\xa4\x95\xa0\x99{C\x84}\x0cI\x0d\x19I\xaa\xba\x8f\x89\x1e+\xee=\xbe\x07\x8d@\x991\xe3#K\xaa\x1a*\xb2\xd9\x926\xc9\xf7\xe3\x877\xf7+fe\xe8J\xfc5G\xa7~\x0f\xb4\xe9z\x9fe7\"\xda\x85Z\xff\x8c+2w\x98\x8e\xffARA\x9a\xf7\x9b~\xa2\x9d=\xde\x14\xc5$\xe6\xc5~={\xb5\xe7\xb1G\x9f\x1erZ\x19\xb2\xb6\xa2\x00\x1dl\x0f\xcf2\xc9\x8b<]&\x19\xdb\xed\xfd^\x1e\x90\xd9fvD\xd9\xc3\x02Q\xef\xcd\xee\xd1\xd5\x9d\x175]TdW\x93\xd5\xc3~\x8e,\xc0I\x0e;\xca\xb0tI\x8e\xa0&TD\xec\xab=\x0b\xe2\xdc\x95dYlwiFi\x11\x89]\x8b4O\xca\x1b\xe6o\xa2\xe3\xed\x1f\"\xe5e\xf6M\xbf\x1b~\xfc\x80\x94\xe5\xfb\xee+\"\x93\xde\xe9\xb4\x92\xcflj\x8e\xf3\x9b\x19\xfcX\\\x93+Rr\x0f\xd6\xc7\x0fo*\xb8\xbeH\x97\x17=l\x14\x01]f\xfduFO0\x04>]\xd4\xf5\xee\xd3\x11\xffo\xf5\xe9\x08\x98\xbf@\xfcz\xc4V\xca\xb2S\xa8K\x84\xab\xeew\x03v\xd3\x11\x0e\xfa \xe5\x95\xf4\x9al\x93]\xc5\xa7\x9dQZ\x17r\xfdB\xc7\xb0\xa4g\xfcu\x91e\xc5u\xf5l\xc0\xfd\x7f\x87\x93uK\x1b\xf0@\xd4\xabtEV\x0d\xf9\xcc?QU\xfb-Y\xf5\xcb\xc2\xc0\xbf\xd3\xb3\xdd\x8f\xe7\xe7\xa7\xf0\xc3\xebsy\xfb\xff\xf1\xc3\x1b\xbeenR\x92\xad \x81\xbf\xf5\x17\xde\xf9\xcd\x8e\xfc\xfdo\x7f\xef!\x03Y\xfa)\x97\xb3\xccU\n\xe3\xdf\xae,V\xfb%\x81$\xe7\x07\xbc\xfe)\xfb\xdf\xe1x\xb7\xcb\xd2e\"\xc6\\\x12\xbaF\x8ak\xeePZ&K\xba\x17\x8b\xe2r\xbfk\xb2\xbcep\xbc\xd6c\xf3\xf1\xc3\x1b\xd6\xaf\xf4\xa7l;\xabq\xc5\x97c\"\xc9\xa4\xff\x7fU\xa4+H\xf2\xe1\x19\x8dw\xca6XI\xd6EI\x8ed3\x8a-\xa9\xa5o#'d%+30\x01P^iBd\x8b\x9c\n\x98|C\xd8\xa7l\x07\xcc\xe0\xc1\xc7\x8a\xc0\x15)\xab\xb4\xc8E\x19\x1e\xb6\x97\xf9\x8aH\xf2d3\x1c\xdf\xa2$\xc9%\xdd\xa5\x02\xdd\xeca\x7fn\xdf\x155y\xc6\x83\x93\xd7\xfb|\xc9\xd7*\xa5T\xec\xe9\xf6\xb5\x83NA0\x1d3\x0bV\x93cX\xfdK.\x07( \x95\xa8\xe4H8\x86d\x07\xac\x02\x08\xb3\x0b\x9a\x15\xce\xea\xa9\xb0\xcc\xc4\xeb\xb4\xbe\x18\x08\xc8\x9b\x1d\x99\xf1\xb5\x96\xec\xd2j\xb6,\xb6Cys\xc6Vz\xc5k\x8a\xf1*!\xbd\xfd\n\x0f\x84-\xc2K\\\xf0\xad\xf1\x10\xb6\xe9\xe6\xa2\x86\xc5`C\xf2B%i}\x01\xe9v\x97\x11*\xe8\xb9_]\x14FZBE\xb6I^\xa7K\xc5\xc5\xa49\"\x1b\x15\xa5\xe68\xcc\xc1\xaeA\xdf\xd2M\xb8 \xd2\xbb\xd8Q\x83\x03\xbd'TH\xb2(\xae\x084\x15\x9dt\xfc\xfd\x9d\xbb\xefO\xc7\xf9\xcd\xa7\xd6\xa1\x94\xe4\x90\x94\x8b\xb4.\xe9\xa2\xb7\xd0 eW\x92\x15\xca\xf8\x19o\x93.\xdb\xa9\x84a\x02\x90\xd3\xb0\x18\x1a\x00\xdd~\xa4NW\x96\xc2\xa9\\|Y\xba`\x845\xa9\xcc\xd5~\xb7+J\xa6'v\xc9\xf2\xf2\xf1>\xa7\xff\xa1\xda\x81\xcfY5\\\xe5}eX\xaca_\xf3m-\xb7NE\x85I\xb2\xe2n\xe2$\x83\x0d\xc9I\xc9b\xa6\xb8)WI\xc2i?\x9c\xd1]\x8c\xaf?'tq\xc1\x13jG//\xd9N\x11\x84%\x0d\xe3\xd2\x1c^\xfe\xfe\xf7\x03!\xfd}Q\xc0\xba(\xe09\xccf\xb3\xff\xd3\xfb\x91v\x97\xe47\xfd?'\xf9\xcd\x8cv\xf4}Yl\x1f\xac\x8b\xe2a\xff\x83\xd9\xac/\x81\xd35<\xa0\xcd>2\xb2\xce\x8b\x07\xffF\xdb=\x84\x7f\x0ed\xcf\xb0\xed\xbftc}\xea\x18\xeb\x7f&WI\xd0`\xe19\xd3\xf5\x14\xa3\xe7\xd8\xd2\xea\xc1\xf7E1[fIUi\x87\xc6\xbb\xa6\x9fr\x8a;\x9f\xf7{Q\xc6\xdc\x0c\xfak\xc7\xa0Oo\xea\x8b\"\x1f\x0c\x9b\xf7\xfb}Q<\x98\xcdf\xfd\x8a\x00\xcd\x90\x1fh~a\xd3\xcc\xd8\x80\xe1\x02mp\xc2\x99\xf0\xea\xf5\xd9\xcb\x0f'\xa7\xe7\xef?<\x1cz\xfa\xda\x85\xa0C\xcd\x91\xeb\x86\xff\x8dc\xf8?\x14\x83;\x0d:\xf4g\xcf\xe1\xdfv\x8b\xd9\xf7E\xf1\xcf\xd9l\xf6\xaf\xfe'I~sD\xcd\x06\xfa\xdd\x8e+\xcd\xb7IY]$\x19e\x8a\x8e\xc0\xe1\xe0\xfb\xfd\x0c:I\xd7\xbd.>\xe6\xdb\xb6\x13F\x02[l\xec\xab\xff\xef9\xe4i\xa6Y@\xba\x9e\x95\x95r\xce\xce\xcd\xcb\xcbFnH\x83\x8de\x83\xf7\xa5\x1a\xbf\x15\xbai\xea3\xed+E\x7f\xdd\xd7\xa8\xcc\xc7\xf4\x8c1c?P#\xe2>\xb5\x1f\x1b\xe9J%/KQ\xbb b~\xba\xe8\x1aQ\x96g7\xd2F\x1e\x1cY\x1a\xf3D\xe4\x17\xd4\xf2\x94t\xff\xf1\xfd.\xb24W\xba\xe5\x169\x11\xeb\xe4\xde\xba(f\x8b\xa4d\x04\x7f~|3\xfb\xf9\x1e\x1f+\xb79\xfb\x863\xeb\xee\x1e\xfd\x8a\x8a\xd5\xce\x0f\xffy\xf6\xfe]\xf7\xdf\xcf\x9f?\x7f\xde\xe76\xfd\xa6=\x95q\xdd^\xd0\xad \x14\x1d\xb7Z\xf7\x15\x91'\xf9\xcd>K\xca.\x96a\xe3\x9a\x05\xe0\xb5J\xea\x08\xc8vAV\xabV]\x1d \xbd\xa7\x9c\xe5:\nd\xcd\x06\xfa\xe9?\xe8P?\xf1C\x8az\x87#\x197\x93\x9b\xeb\xd9\xc0\x00K\x96\x97t_\xb5\xe6\xf9:\xcdH_N\xc9\xddwJ\xca\xaa\xc85KV\x9c\x92\xd7iY\xd5s\xc6\xe9\xe7\xf0\xa4\x8f\xa5\xf9\x8cN\xb5\xfc\xea\xa9]&\x02hz\xbb\xc7F|\xef\x19\xdc\xd3\xad]u(3N\xf3\xbd\xa3!\x16F\xed\xbbdK1\xfd_N\xda\xff\xd3|F\xa9\xed}e#\xf9d-\x0cGu.\xf9\\\xa4\x15\\\x93,{t\x99\x17\xd7\xfc\x12\xf4\"\xa9 \x81\xe5\xbe\xaa\x8b\xed`)\xaa\x8b\xe6\x88\x1b<\xbd\x95\xd4\x96\x1f\x16\x1d\xd2\x05\x92o \xe1\xcb\xa3\x8b\xee\x13[\xa6r\xa5\\\x14\x99\xb8\xb4\xec\xf4\xceN\xfcb\x85\xc9B*b\x81u11\xd4\xcd\xaa\x02v\x1d.\x07:8\xeaI\x1f\xc3\xdf\xff\xf6\xf7\x87\x83\x05\x18>\xbb*r\xdd\x04\xb3\xe1RDOfO\x9f<\xad\xee\x0d\xa6\x0d\x8c6\xac\xce\x7ffvc\xd3\xbf\xb2\x16\x8fE\x13\xf8p\xfaR`\x92\x1em\xb7\x9f.\xe0\x12C4\xec{\xea\xd4\xab\xc2\xc1\x15\xe1\x003\x07\xd3\x95`m\xb8\n4\x9eU\\\x87\x92I\x1d{6\xd7\xde\x94\xce\xbd \xdd{\x16\x07\xdf(\x17\xdftN>\x97\x9b/\xd0\xd17\xb5\xab\xcf\xe2\xec\x9b\xda\xddgt\xf8\x8dv\xf9\x0d\xf0%Z\xa7\xdf\xd4n\xbf\xd1\x8e\xbf\xc9]\x7f\xa3\x9c\x7f\xd3\xbb\xff&t\x00N\xed\x02\x9c\xd0 \x88q\x03N\xe8\x084\xbb\x02\xc79\x03\x07\xc8t\xceA\xa4{p\xac\x83p\x80n\xe80\x0cv\x19\x1a\xe2j,\xaa\xd8\xe88tk\xe9@\xe7\xe1Pp)\xe5\xe1\xbb\xc3\xb1Q0\xb1\x0bQ\xe7D\x9c\xc4\x8d8\xb1#q\xe8J\x1c\xedLTp\xd5\x03\xc7\xe28\xd7\xa2\xc3\xdfft/\"\x1c\x8cZO\x88\x87\x93Q\xdf\xfe_\xfa\xb1\x07\xb9\x1a\xb1\x83w\xb9\x1b\xed#u\xba\x1c\xbd\x9c\x8e\xc3#\xf6H\xc7\xa3\xc3\xf5hs>\xda\xdd\x8fF\xae`]\x90n'\xe4\xd0\x0d9\xca\x11\x89rE\x868#\xf5\xacp:$'sI\x1a\xfa\xef\xad\xa4I\x1d\x93\x93\xbb&'vNN\xeb\x9e\xb48(\x87.\xca\xa1\x93r*7\xe5\x84\x8e\xca\xa9]\x95Xg%\xc2]\x89vX\xe2\\\x96\x1a\xa7\xa5\xce\xb1\x85wm\xd9\x1d\x97h\xd7%\xcay9 ~J\x07\xe6\xe4.\xcc)\x9d\x98S\xba1\xc7\xcd\xb7\xd3\x95\xe9vfJw&\xb4\xf1\x87\xd2;\xc8\xdf2\xa0S\xfa9\xadD\x12\x0e\xffE\xb4\xd0\xbd6\xa8X\xe6\x9aW\x06k&\xea;\xafP@\xad}\\P\xebk\xd4{\x1a\xf5\x8f\x08\x1a\x8f6\xb8\x88\x88\xc1c\x81\x93<\x128\xd5\xe3\x80\xa8G\x015\x8f\x01:\x99\xa2I\xda3%\x05\x8dy\xec\x0f\xf9\xc8\x9f\xdf\xe3~n\x8f<\"\xac~\xe0\x92\xaf4>yI\xa7\xf1\x99\x0e\xa3\xcb\xfe-\x0bO\x16\xa8_\xdcP\x11\x1b\xea\xbd\x8fa\xb6\x9e\x9e\xf8\xc9\xfc\xf01\xccv\x84\xef=\x86\xd9\xfa\xf9\xdbGz\xdb'\xf6\xb5\x8f\xf0\xb4O\xedg\x9f\xcc\xcb>\xad\x8f}2\x0f\xbb\xdb\xbf>\x99w=\x86\xd9\xc60\xdb1>\xf2\x18f;\xce\x17\x8e\x89<\x8da\xb6\x1d\x88a\xb6\x10\xc3lc\x98\xad\xbf/{RO\xf6\x94~\xec\x18f\x1b\xc3lU,8_5\xc2S\x1d\xc3l\xc3\xfd\xd3\xe1\xb3\xeb\xf4M\xbb<\xd3\xce0[\x8b/\xcd\xea\xdf\xeb\xe2x\xacA\x02\x1fN_b\xc2o\x95\xa61\x087\xc4\xed\x17\x83p;0\x95\x0b\xd0\xe5\x04\x0ct\x03N\xed\x08\x8cA\xb8\xe1N\xc1\xd1n\xc1\xc9\x1d\x83\xa3\\\x83\xd3;\x07't\x0fN\xed \x9c\xd0E\x88q\x12N\xe8&\x8cA\xb8\x02\x02\x1d\x8a1\x087\xd4\xc1\x18\x83p9\xd41\x08W;\xf6 G$v\xf0.gd\x0c\xc2\x8dA\xb81\x087\x06\xe1\xc6 \xdc\x18\x84\xebtk\xa2\x1d\x9b(\xd7f\x0c\xc2\x9d\xc2\xc99n\xbe\x9d\x8eN\xb7\xab\xd3\xd7\xd9\x89\x8bc\xec6W\xfd\x9c\x95\xe2\xe2\x94\xec\xf0*\x9a\xcb\xb0\xf2\xba\xac\xde\x9e\xcf]\xa7\xe0\xee`\xd8;k\xadW!\xd1\xda*\xaf\x1c\x06\x9d\xea;\x06W\xf9^\xc0\xc4x\x0cbd\xf5\xa5|G\xe02\x97\xf5\x0dC\xea,\xf1;\x0dZM\xb9__\xc4\xcaR\xd0\xac1L\xe0\xae\xa8\x16\xac\xf5\xe2/\x92\x8a\xccx\xe5\xf6\xb6\xfcs\x13\x8b\x8c^\xc1J\xb4\xb9v|\xda\x93\xbb9\xc2\\\x04\x96'B\xcb\x97\xa4\xde\x979\xf3S\x89Xi\x11\x1f\xdfD\xa13/\xd3&\xed\xbeR\xc2\x86\xc5\x9e\xf1w\xc4\x97\xbf\xa7\x16\x94x\xcb\x82\xbf\x1dM\xcf\xa3\x97\xa4{\xd6l=\xcc\x15\xa9\xe5F\xeb?4m\x1dyo\xb9\x98\xc7.H`\xc3\xcf\xf7[R\xa6K\xf9\xb7\xee\xeb\x88\xdc\xe9rAr\xc9\xac}\xde\xf8\xab\xba\xd6-\x7fZ:#U\xd5\x8e\x9a\xbb\x81\xf6\x15\xe5\xce%\xb1\xb1@\x0c\xbc\x83OeAp\xbdn\x9b\xbf\x84!\x95K\xdb\x14N\xcf\xfd\x8a\xdd\x95!6\xc2>S.\x82\xb8\x8f\xa3\xfd\xc3\xc9\x1a2\xb2\xaee*\x81\xc8-\x90v;s\x83\xf2E\xc7;\xa0\xacX\xdc\x00I\x96\x17\x90\xecvr\xdc\x9d(\xfd\xfe\xe8\x17E\x91\x91\xce\x93\xf4\xe6qv\x90\xd0\xd1\xb2\x19.\xd8\xd3\x08\xec\xf5\xba4_\xa5\xcb\xa4&\xed\xbb\xb2|t\xec\xc3\xc1\xbc\xa4\xf92\xdb\xaf\x14;<\x11/\xab\xc8\xdb\x96\x1e'\xd9]Z\xc7\xc9I\xc5\x86\x9a\x8b\xf2\xf1\xa4Rx\xd7#\x98\x1d=\xa8\xe8\xe1\xcf\x91\xb0\xc5\xd8\xae^\xba@\xe5\xcb\xe6\xe9&/J\xf1\x89\xba\xcd\xbb\xe89\x0ffC\xa1\xa1p\xb4$W\xa4\xac\x06\xb5\xdd\xf1l\x17\x08\xfa,O;\xa9\x1a%1\xac/\x8a\x95\xe4+Ut\x17\xe5\x8a\x94\xaa\x1f\xca\x98\n\xc1A72\xbdD\x90&\xdcY\xb1\x95byh\x9e}_\x14P\x15[2oL\x03\xed\x11\xa0#\xda\xbb\x13\xdd=\x05p\xc3K\xe4\xb84Dt\x1b\xa6r\xeb5\x16n\x9a\xc3\x86j\x17\xf1\xd4\x8d$\x99W\xa2od\x8e@\xd5\xf6Km\xd5g\x08U\xe4iM\x0d\xd3\x9f\xa6PH\xa3\xd3\x9d\xa6Hur\xa69i\xe5\x11N\x1a\xf7\xa7\xbcA\x87Mi2&1\xb5\xf8F\xe4/\xf5\x96b\xc7\xfa1\xadE\xf1I\xb3\x18\xaf\x19\xf3\xdas\xc0\xb2(\xf9G\xecx\xd6[\xbb\xec$\xc84lwT\xcd\x06W7\xa5\xe8i\xb0+K\xb2#\xcc\x8b\xfd\")\x1b\x96\x99\xf7\xa5@\xc3VF\x7fK\xf6\x8fW\xc7\xf9\x0dzC\xd4\x83\x90\x07\xed\xba0\xcb\xcb\x89\x02\x1c\xf4\xa1\x0d\xd3\x045\x84\x873\xe8\xc3\x17t\xbe4D\xe0Bp\xc8B\x1b\xa2\xd0\xc1\xd6\xbf;\xf1\x0eS\x18\x19\xa0\xc0&Ye\x8a\xa2\xaeG\x06%0\x1f\xa9\x82\xbd\x8b|d \x82&\xf0`\xba\x90\x83\x11\xc1\x06\x13\x86\x19\x04\x06\x18L\x19Z0IP\xc1t\xe1\x04\x93\x04\x12\xd8C\x08\xc2\x83\x07\xb4\xc1\x02c\xc2\x04\x06a\x01\x1a\xbf\\\xcf(\x1d\x15\n\xd0\xbb\xfa\x0f\xb8\xf4\xef]\xf7\x07\xdae\x8an\n\xbc\xd6\xd7f\x0c\xd5\xcd\x85\xbe\xbe\xbf).\xf1\xf9\xa4q|\xcd\xf5\xfd\xc8\x8b\xfb\xf1W\xf6\x8a\xaa\x19yA/\xd8(\xb1\x85^\xc7\x1b\xef\xa25W\xf0\xd6\xcbw\xf5\xae\x0fw\xe1\xae\xb6\xf9W\x7f,\xde\xd7\xeb\xae\xc1\xd8\xae\xd4\xf5\xf4[\xaf\xd1\x91\x17\xe8\xed]\xc9\x88Ks\xe3u\xb9\xfe\xa2\xdctE>\x18%\xe6Z\xdcv!\xde\xbd\n\x0f\xbc\x04w\\\x7f\xfb]|\xf7\xae\x9cm\x97\xdd\x13\\s\xf7zkfz\xb2K\xed \xaf\xb3'\xbb\xc8\x9e\xea\n[{y\xdd\xbd\x0d\xec^X\x8f\xbf\xaa\x9e\xe4\x92z\xba\xebi\xf7\xc5\xb4\xf5J\x1aq\x19\xed\xba\x86V\xeepU\xec\xd8KH\xf3\xa53\xe2\xba\xd9q\xd1\xdc\x907\xd5\xe5\xf2\x84\xd7\xca\xd3\\(Os\x95\x1c6s\xd6\xebc\xdb\xc51s\x93\x94\xbb\xe5l\x93\xd4\xe4:\xb9\x99\x95\xfb\xbcN\xb7d\xf6\xba}\xc5\x1a\xe1-\xe9\xbdy\xad\xb1Q\xd57\xae\xf9\x07\xfd\xb7\xad\xa5\x15\xdb}\xd3Zp\xd0\x8a{\xf0vu\x1ds`\x14\x98\xc6]\xc4!\xdci4@\x15s`\xc2\\L\x1an\xc5\x1c\x18\x7f\x87\x14\x87\x11n)\x89`2\xe7\x14\x87@\x17UC\xcdd\x8e*\x0e\x93\xb8\xab8L\xe7\xb4\xe20\x89\xeb\x8aC\xcc\x81\x19|6\xce\xf15@\x17s`b\x0eL\xcc\x81\x19\xfc\x10s`:\x10s`b\x0eL\xcc\x81Q\xd1M\xe84\xe40\x99\xeb\x90\xc3T\x0eD\x0e1\x07&\xe6\xc08\xdd\x91\x02\x8f\xdb))>\x8c90\xbf\xa9\x1c\x98&62\xbfl\xa2\"_\x91\xbc\xd8\xbe\xbf\xce \xde\xa9\x99\xacV%\xa9\x06nE[\x04\x98h\xa2$\x9e\xc8\xbf\xb19/\xae\xa9\x11\x0f\xbb\xa4\xac\xd3%\x15;\xb0\xa2\x84\xc9\xe0N\x81u\x91dI\xbe\x1c8=Q\xa9)\x0c\xdf\xd0u\xa99\x17%[\xb5\x02\xb8\xf1Ss\x80\xe7\xcb\"m\xab\xf2'P\x17\x97$\x17RP\x19\x18\xdb#\xf4\xec\xc2\xbaTL\x8dw\xef\xcf_?cr\x9b\xff(\x84d\xca\xce:'y-\x96xs>T\xd69We\x1dlU\xba\xc9\x93z_\x92\x8a\x05'\xa6%\xd7\xba\x9bbS\xb0\x15\xa6\x89Th\x06\xd4.\x91fHU]\xee\x97\x14]\xbbY\xd9\x96\xcce\xe1\xa5\xce\xac\x16\xa5\xd8\x87\xd2\x8d\xa8\x9bd\xe6\x86\xb9$9\x0bZUD\xbfD(\x97\x0b\xe3\x18\xff\x9b\xba*Ze\xd4\xc7)\xd9\xea\xca\xc1\x1an\x8c\x8fy\x8a\xcf[\xe9\xad0\xf7\x92a\x0dZ\x0e\xf2\xf1\n\x95\xd2u\xean\xd2+\x92\x8b\xaf\xf7yZ3\xf7*\xec\x93\xba\xd8>\x94[\x83|\xde\x159\x19\x16\xae\xb7\xdc%\xa0\xb2'$\xde.\x99\xbb\xe2\x9a\x1f\xea\x9f|\xd5\xfe\xce'<'\xdc\xa5\xdd\xc1P&\xa9\xb0\xa5\x16IE\xe6|\x18,!\x81\x87\xba\xd3\xff'\xd4\xc0\xea\x8c\xb5a\xfe\xfd\x8a\x8f\xbb\x8b\xf0\x89`\xc5sx\xf2\xd5\xff\xaf\xe9\xbfE\xde\xfd\x969\xa2\xe5\xce\xeb\xf4O\xcf\xcc\x94\x7f\xad\xe7uY\x92\xa4&\x90\xb4}\xd3\x8f\xee\xd3\x8f\xeew\x10\xf6=GM\xff\xcf\xe1;\xaa\xbf\xf6\xd53x\x02\xb4\x15\xa7\xef\xbb\xde<%Y\x9aT$\xecN\xa6\xb7\x96\xe4\x8b\"\x1c#O\xf1\xc9\xd2\x8a\xd1-V\x91\xfcM\xe6puV\x92k\xc33\x0et&=\x11\x9b\xbe\xef\x10I8\xd2\xee\x1e\x90\xc2m/\x98(\xe6>]\xca\xfd\xa8\xdfqoI\x9d\xac\x92:\xf1\xd8p-\xf5\xb6m\xc7\x88\x9aSj\xc2\xf8\x8e\xbe\x0b\xd3\xe8\x98Q\x17a\xa3\x04\xc4\x00\x9b\xba\x109\x0c\xc5\x06\x07\xbd\xf0\xe0\xa0\x17!\x1c\\#\x1a/N8\x8c\x14*zN\x0f\xbb\xf1\x115\x1cF\n\x9c\x01>.\x80\x0c\x0ek_\xe1\xc3a \x828\xe86\x04\x87\xc1\xb6\xe86\xd0.\xec D\x13\x07\xb3\x895JLI\xe48a\xa5\x8c\xa9#L\xfa;S\x8e\xb0\xab\xbd\xe8\xf0D\xdf\xb0,\x9a\x94R\xba2|L\x05e+\xd1\xc6\xfd\xbe\xd9\xdf\xf8R{\xd0^\xde\xd2_Z6\xb1U)\xd7L\x07\xdfs\xf8\xaaY\"\xab\xb4\xdae\x89+\xdf\xc9<-\xa2}\x93n(d\xd6~\xb3!\x15\xb5\xc8\xc4N\xa5\x93\xd3\x909l\xceso\x96YJ\xc7'I\xa3\x12\xcf\x87\xae\xfb\xfa\xb4\xb9\xfb\xfd\x19\xbd\xcfDi\xf7h\xa2\xdc\xa83\xdb\xfd\x01\xd9<\x83\x97\x0c\x11\x1c\xd3-%\xb1T7\xdbE\x11\x9e\x0d\xc3\x9b\xb7\xd9\xa9\xb4/\xf17y\xf3]]\xd0\xc3t\x91\x03\xf9,/\xf5\x185\xc7\xe7\xef\xdf>\xe4\xa1\n\x1d\x84K\xd5\xcd\"\xd6@\xc5\x1cV\xbc\x13\xc1b\x8f\x0c\xc3}\x99\x06\x0f\xf0\xe3\x87\x13\x9e\x06\xbb*\x96{\x16\x04\xf1\xa0\xa02\x1a\x8a\xf5\xfa\xd1\xf2\"I\xf3\x87\"\x11\xb9\xb9\xcai\xfcB\x1d4i\xce\xb5\x0e\xcb\xf4{/.\xc9\x11C\xf8\xae\x1d\xc2\xfc\"\xa9.\xc6\x8c\xe3\xc7\xa4\xba\xe0\xa2\xac\xbaH\x9e~\xfb\x1dP\x84\xcc\x83\xd6\x0enWP\x9d\xc9\xceV\x1f?\x9c\xd0\xd3\xcc}\x91\xfa\xa5\xe4\xef\xf1\xc4~\x9e\x12\xd3\xf93\x9b\x1e\x89j\x95\xae\xf2\xfb\xb5\xb8w\x0d\x18\xb4~\x8fJ#\x0b!3E\x8b\x04c\xbb\x9dv\xaaN ,\xb7\x8a\xe4\xab9\xc9\x93EFVw\xc9$\x1b\x90\xd4\xfd\xbe\x9f\x13l\xb7v\xceH\xbez\xcd\xd1\xf1x\x8c%\xf7 \x08C%Qx\x00U\x9d\xd4\xfb\n\x1e\\_\x10v\xab\x9c\x0c\x07\x01i\xa5Z\x00\x14\x01m\xddQ\xeff\x82^\xd1\xe9\xa6\x02y\xf5\x0c>VL\xbau)LsY\x1b$\xa5'.\xf9m/\xee\xbcd\x17\x89UZ\x1d5\xfe\xf1\x9c\\g7t\xcf\xd2AtG$\xdc\x15\xdc .\x9au\x90\xf1 T\xf0\x9f\xd5E\xc9\xdc\xc5<\x1c\x82_\xf0o\x93<\xdd\xed\xb3\x8e\xbb9\xad\xba\xd2\x80_\xff_\x0foJ/ \xd9\xf5\xf3\xa8\x8f\xd9\xbd\xa7\xbaY\xfep\xc4q2\xaf?{\x8c\x91+\xedE\xb2\xbc\xbcN\xcaU\xd5\x8b\xc1Pl41.X\xa7\x19i\xf4\x94\xb8\xb1\x98\xdb\x16\xb91U\xfb\xd4Z\xa1E\x1aKt\xefu\xea\xb4\xe8v$\x7f\x9a/\xcb^p\xef\x88\x7f!\x19\xe1V\xb9SG&\x9dk\xce\xf8\xb9\xcdv\x9c\xc0A7\xb5\x8b\x0e\xeb\xa4\x1b\x0cMN\x94\xb4\"\x9a\x7fS\xd5\x94\xf1\x13\x10\x15>\xcd\x02\x8dO\x8b\xfa\xe4\xda\xc3D\xf9\xf6\x00\x88\x9c{\xf6Q|Z\xb4yZt(\xbf\x00Q\xa4\xa8\xd3L`\xfbp\xfaR\x12\xa9)[\xd4\x17\x9b\xa2q\xa8\xc8\xecKL\xd4n\xd0\x08B\xc3\xac\xc7\xfb \xcdJ\xe9M\x19f\x95\x88&\x9a'h-K\x83\x9d\xa6\xa5\x11\xed\xbd@\xb6\x8a\x8bS\xdbH\xdf\xb0?z\xf7\xe4\x1b\xdc\x9e`\xd0\xe3`\xf2\xf3\x18\xf49Xt:\x98\xf4:\xd8D\x99lgw \x02\x84\xbbE\xb5\xc84w)-\x98\xdc\xa3\xd0\x0cD\xef\"\x05\x87\x9b\x14\x90#\x9d\xca]\na.S-\x1e\xeb\xddL\x0b\xfe\xaeS\x18\xe9>\xd5\"l\xeep\x0c.T\x08v\xa3\x82\xd9\x95\n\x96M\xc6\xc1\xe0R\x05\xf7\x06\x99\xcc\xb5\nN\xd3i\x02\x17+\x80\xaf\x9b\x15`bW+\x0c\xdc\xad`\xe3\xb2}[\x8eq\xbd\xf6PuV\xddW\xca\xd2\x1a\xb8`\x01M\xef`\nG\xbac;(\xb4.Y\x18\xb8e\x01M\xab\xd3=\x0b0\x8d\x8b\x164nZ@\x939X\x02\xa3]\xb6=|,\xd7l\xd9\x8f\x8e\xc3\xbbn\xc1\xe5\xbe\x85\xbe\x0b\x17\xc2\x07?\x91;\x17p.]p\xb9uA\xeb\xda\x85Q\xe3\x1b\xe5\xe6\xed\xa1\xab\x0b\x93\xab\x17 \xc4\xdd\x0bn\x86\x98\xe5\x81\xbf\xeb\x17t\xee_\xab\x19\xae5\x8f\xad\xc6x\xb7\xf5c\xa5\xb9\xe7\xa1\xad\x0dS\xf2\xf7uqMS\xb0\xc6}\xdb|Z\x7f\xd7 x\xae\x8b\xc5\x1d$0Q \x1d\x87\xc1q\xb5K\xcb-Y\xfcz\x8f\x9e\xa3\x99\xcbt\x0e>\xde\x0e0\x8d9\xe5\x0e\x90\x89S\xef\xe0\xef\xb8\xc3/\x07\xdb\xd0G\x07\xea)\xd8d\xd0\x9e\xf2\xc7\xd1\xb1{\xeaf\x10q|\xbaUi\x0b\xe7S\xbe\xb7\xca\xc3\xe8\xea\x8c\xae\xce/\xc3\xd5\xa9Q_\xca\x8aT\xea\x902k\xa4\xd3\xa2\xe3\xc3\xe2Eo\xd1\xef\x0e\x18\xf4h5\xda\xc5u`=jt\x86YM?\x83C\xccvZ\xb7\x06?i\x95\xa4]MZ\x14\xa5\xf3\xec\xef\xd6|\xf2\xab \x1dd\x0e\x17\x99\xddI\xe6v\x93\xb9\x1de\xd8QO\xe9,\x9b\xce]\x86v\x98\x85\xba\xccB\x9cf.JQn\xb31\x8e3\xab\xeb\xcc\xe5<\xb3\xba\xcf\x10\x9bhR\x17\x9aK)\xc3Tn\xb4\x10G\xda\x01\\i:g\xda(c}B\x97\x9a\xc5\xa9fp\xab\xe1)\xd7L\xec\x04\xce5\xb7{M\xe7`\xc3S\x8dr\xb2M\xe7f\xd3;\xda\xf0\xe4j\x96G\xb8\xbbm\x80\xaa\x131\xa9s\xb8\xf9\xb9\xdc\x10N7\x8d\xdbm\x14+&t\xbe\xa1\xddoN\x7f\x13\x18]pc\xc7:\xa5#\xce\xe5\x8a\x0bu\xc6!\xd8c\x93\"a.9\xadSn\xd0\x95\xc2Ti\x95\xb7\xc5G\x98\x9f\x82\x89\x1b%\x9c\x8eI~\x114T\x92MZ\xd5\xa4T\xeb\x1a\xd3.c(Q<_\x7f\x89\xe7\xeb\xc1\xb1\xd6\xd35]\x05\xfa\xa6'z\xcb\xcfcG\xe8\x83O\xc1b[\xff\xf2\xd1\x1f\x86\xe8\xeb\xb6\xdd0\x02\x1b\x10\xaad\xcaHlhN\xd7\xfdhl\xd0Gd\x83\x93\xc0 \"\xb3a\xea\xe8l\xd0Gh\xc3\xf8(\xed\xfe\xfabbt\x18\xa9\x0d\xa1\xd1\xda=\x1c\xfd\xd8m}\xc46\xb0I\xb2Em\x83q\x0d*\x93\x1b\x14\xbdm\x15[\xbd\x17\x1a\xbb\x98\x872\xaby\xa4\xf0\xf3c\xdaGW\x8a0\n\xac2\xaa\xb3\xe8\xbc\x05U\xcc\xe7\xf0\x92\"z 2\x90\x1e\xd1\xb8\x8a\xc6\xd5]5\xae\xfeW\x91R\x1a\xd9\xe1\xb8\xbch[47\x16\x1c\x9f\xf6<\xf5\x07\xab\xe0\xda\x89\x8d\x13S]t[+\xf82\xbc\x8bd\xcc=\xf8\x01S]*9\xf51\xe9E\x81(L\xfb4\xdcia\xaa\x9a|Fy\xa6\xacK\xf5a\xca6\xcec\xdd\x84qHkP\x12\xda\x06}\xdc\xaf4;g\xcc\x9d\xf1\xd9~\xb7\xcbn\xde\xaf\xbd%o_\xcay\xec$\x8d85\xac\x98\x980\xa3[e\xbd)\xc3d\xcc\xc86~)3\xe7t\xb7\xf0\xa6\xde\xeb\xa3b\xcd\xa2^\xbe{zYHZ>A\x8d>\xe6\xff\x12\x17FL\xff\x8a\x06N\xf5\xabp'@\x17wZ;.f\xfe\xb7\xc1\xec\xe6rG\xb1\"[\xe3\x91\xdd\xcd\xb7T\x0b\xb6p\x01.\xf1\x05\x17\xb0\xaa?\x8f?\xd9je/\xce\xef:X\x14\xe7\x9f+\x1ed-]\xa2\xcc\x8e\xe0g\x0dn5\xfc\x07g\xc0\xecG\xa6\xf2\x7f\xffd0\xc7\xdc\x9d\xc3\x1e6\xce2:@h\xdc\xa7L\xc4\xcc\x00\xfe\x8b\xdc/ \xfcc_\xd5\x90lJB\xa8\xc9\xa3}T\x96\xa7\x95\xb1\xc7\x9b\x06\xfd0\x1b}K\x92\\P\xcc\xc9:\xde\xedX\x90\xfc\xaa \xfc\x11TnT\xb1\xd8\xbf\x8aPr\x06\xfe\xfaW\xfcl\xdaI\xd2\xae\x08\xcf#\xa2\x06R\"^\x8f\x14x\x9a\x08\xc1\xae\x0c\x95\xc2 h \x0c\x1bC\xd8:\xf0\xf6p4\xc7\x9a\xf9UQ\x93\xb9\x9e\x10\x0eV\xbb\xc4e\x99\x000\xfcZ/\x06\xb8\x91\x03\xa2\x03\x90h\x8c\xbf\",\x15\x0e$\xdf\x1bRQ%<\x82\xb3\x93\x1f\xde\xbd~5\x7f{\xf6\xc3\xfc\xfc\x7fN_\xcf?\xbe\xfb\xf3\xbb\xf7\xff\xf5\xce\xb3\xd5\xe9\x87\xd7\x7f}\x7f\xfe\xda\xbf\xd5\xcb\xf7o\xdf\x9e\x9c{\xb7{\x7f\xfa\xfe\xec\xf8\x8d\xa5\x99\x08\xf8|\x160>\x97O\xa9\x0bg\xe9&'\xab\xb7\xd5\xe6\xbc9a\xd7\xe2\xfd\xd8\x8a\xfd\xd4}4R\xff\xd2t\x0b\x8d\xd2\xd4<\xb3\xa9\x80\x91\xff\xcf\xe0\xafE\xady*\xdd\xd9\x9a\xf3\xf3\x19\x9c2\x15\x99df\x14\xba\xf3\x91\n\xc8\xc5\xe9\xb2\xd39\x94\xc5>\xd7\xc6\x8cKp\x1f\x088t\xba\xfb\xfa\xa9\xf1;\xfdaK\x05\xc4\x1e\x07\xe4>\x07\xeby\xa4\x05$K\xc1uF\xe9\x82\xf3\x00\xa7\x02r\xd4\xe01r\x00\xebQ\xaf\xf7!r\x9e%\xe0\x96\x97\x04\xcc$\x80\xdfD\x80\xcfd \x8e\x8f\xda\xcf\x87\x87\xc9\xfeg[R\xd5\xc9V\xe3f\xea|\x84\x1b\x94\xcdS\xa1Bs\xfe\xd2[\xf9*x\xf6nee\xdbq\xef&i\x08\xd8\xf5\x84\x93\x1b\xcdu\x9d\xbb\xcb)F\x8aUQT\x1b\xa8\xd9\x87\xf4\xd0\\\xd4\xe4\x88\xfe\x0fw9\x1cAQ\x02\xff_f\xdc\x18\x91\xb1\x83w{\xb4\xa6t\x9a\xf5TG\x97\xe9~fV\xd4\"ZQ\xd1\x8a\x82hEi\x01\xb98qj.ZQ\x80g)\xe0d0\x87hEu\x003 \xe07\x11\xe03\x19\xd1\x8a\xc2\xf6\x1e\xad(\x0f\x15ug\xad(\xb6\xed\xe7WE\x9d\xe6\x9b9+\x86e\xb7\xa8\xacLso\xf5v]\x1c\xbe/\xe7\x96\xf3\xe8\xc5\xb6\xd50k\xe0\x95t.\xd2\x85\xf0Z\xb8\x16[7\xabt6r\xf7z\xc3$-*a\xce\xd4\xd7\x05m\xbf\xce\xd2%\x0b:\xa2+F3\xc7\x195\x12\xe6\xbc\x8e\xc7<\xa9\xebdyyH\xd7f\x87\xa2\xb9!\xd4\x81\x03B\x8b\xb9\xfa\x82\x86\x19\x08\xcd\x89\xe8\x0f\x90}\x82&\xdaD\x0f\xc8N\xc1\xa3c0\xc5\xab\xe8\x01\x13\xc5bh\x89\xa7\x1d<\xe9\x07s\x1c\x8c\x1e\x10\x1b\xb5\x0fr\xe3jcf\xf4\xa0\x8d\xa4\xd1\xc3m\x10\x84\x11+}\xc0G\xe8\xa0\xd0i\xa3x\\q;z\xf0\x89\xe6A!\xecG\xfc\xd8c|\xf4\x80\x89\xfc\xd1\x83>\x1eH\x0f^\x8b\xc5}\xaa\x93\xe0\x85\x16\xa3/\xbb0\x8c>2|\x17F\x84\xdbr\x95`\x89_\xd2\xc3\x81\xe5\x16\xf6|\x02\xfe\xcc\x01\x9c\xc9\xa9\x82\xe7\xe1Q\x82'\x97 \x80S\xe0s\xa8\x94\x80=\x0c\xf4\xc1ouK\xf0\x99L\x08\x9bP\x08\x99T\xefCh\xaf\x99\xeb0*\xc1\x1e5\xa6\x07O\x1ex\x8e\x1d\x17w\xa6\x07C4\x9a\x1e\x0e9\x0ck\xec\x98\x1e\x0eI\x8e9\x8dN\x0f\xd8\xc09\x14\xb2~p\x9d\x1e\xdc!wz8$\xdblA{z8$5\xfa\xb0?=\x1c\x92\x0eG\xe0\xa0\x1e\x0eI\x90%\xf4P\x0f\x87$\xc6\x1d\xbc\xa8\x07{H\xa3\x1e\x0e7\x0e\xcfh@]\x08\xa0\x1e\xb8\xaaq\x0d\xce\xc36\xf1\xb1I\xee\x88e\xed\xbcG\x91\xe0k\n\xe1\xfc\xa3\x12\xa2Im\x84hR3\xf0[\xd6\x12|&\x13\xc2&\x14B&\xf5\xb6L\xea6\xe9\x1a\xc3\x05>z\xf33\x06*X\x1f5P\xc1{1\xfa/E)A\xe6\xeb,\xd1\xd4R7A\xd0\x84\xbb\xe3'Tx\x04/\xde\xbc\x7f\xf9\xe7\xf9\xc9\xab\xf9\xf7o\x8e\x7f@\xc4\x1a\xf4\xa1\x8f\xe1\xf8\xc5\xd9\xebw\xf6\xf0\x08\x15\xfa\x08\x10\xf1\x15*\xf4\x11\xbc;\xb1\x85Y\xa8\xd0\x04]\x8cc\x83\xdf\xb9\x81\x03\xdf(\xab\xef\xb3d\xd3y\xf7\x80\x17-x\x91-\x8b\xcb\x93W\xce\xf8\x0b\x15\x9a-\x05)\xdeu\xe9y\xf7\xa9B\xd0\x1a\x0d\x12I\x88Ka\x15F\x91\x86w\xbbq@]\xa6\xaa0\x8a>4\xebB|\xe3\x00/\x99\xf9y\x96nx|\x10\xd5\xf3\xd2y\xcc\xeegeR\x03\x12]\x9aC\"p\xba\x1c\xc6\xbe\xf4r\xacj\x1aFsG\xc8\xf2;\xa4\xd9}\x9d8\x82\x8c\x1a\xb3[\xbc\x82\x90\xc8|\x8e\xf6\xb8m\xa6\xbe\xddC\x15A\x84\x1a9\xf5\x0dV\xcb\xb4\xb4\xb9V\x1e^\x83\"\xb5'r$\x1c\xb0\xe3\xe1\xe0%\x87\xbc7\x92\xe7&\xda\xed\x17\xc3\xc2U&\xf0b\nx3\x86\x02Y=\xfd\xf6\xdb'\x7f\xc2~\x1e\xc0 \xf0g\x12\xb0\xb2C\xcb\xdd\xd3o\xbf\xbb|r\xd7H\xf3\xd5\xce\xa7\xfbE\x96.\xffLn\x94\xc3\xfc%\xb9\xe9\x96\xcb\xc2\xeb\xd7}ExY\xa3\xbf6\x9b\x15\xd1\x12\x13i\xa2\x8273}\x0f.\x8d\xf3eW\xa6E\x99\xd6^{\xe2 tI\x8a\\\x84xlK\x9f\x0d\xe9!\xa7<\x99\xe0\xb5\xc6=$\x94\x07#\xc0\x93\x19\xe0/\x9b<\x99\x02\xbe\x8c\x81\x10\xa9tx\xa2\xfc\xe4\x11V\x1aQ9\x83B\xe8+\x8b|%\x91'\x03\xf1\xbb\x1dBe\xd0\xc1(\xc2\x07\x056-\xf0\xa4\xb8\xc9\xa0\xf6j\x91\xcf\xed\x0e[D\x8f\xee\x9e\x167?'y\x9d\xe6d\xee\xb68\xdd\x96\xa6\xc3\xc2D )\x9chBIh\x04\x878 \xf79J\x1e\xa3\x06 \xe8\x81\x82\x8f\xf4E\x0f\x18\xf0\x83\x06?Y{\x18\x12\xb0\x92\xd5G\xa6\xea\x9f\xc7T\x01%M\xf1b\x02\xcd\x1c\xf7\xc6\xe5\xe0)5'\xec\x1f/!\x11\x9d\"\xbas9\x8a\x17,\x1eW)\x1e\x97&\xb8\xeb\x11\xef\x8b\x10e\x1e\x9d\xb7\x18\xee\xbb \xd4-\x84c\x1a\x1c+\xd3\xa2\x1a\x90\x88\xcd*\xc1z\x03\x80\xc4\xae![\xd13X\xb7\xbc\xde\xe1nVY^Nt\xbd\x93\xbc\x83\xad\xef.\xd7\x17c}\xc1\x83\xe4+h\xeb\x9926\xf1\x1f\x8e\xf8!\x95\x12\"\xc2jv\xbdzb\x02\x0d\xaf\xf9-\xea|\xf1\xfa\xfe\x0b\xb5\x94\x98\xb3z\xea\x0f\xa4f}\xbe\xb8\xe1\xb5\xa9\xbc+x\x0fU\x93\x8f\x86\x1d]\x07\xce\xaa\x82\x02\x14\xa4A\xed\xd8U\x8eM\xddL\xa9q\xad\xeaE\xabZz\xf93\xe2\x9b\xfb\xdd\xb7Bw\x19I*\xc2l\xfdO\xd5\xea\x92G\xd1\x7f\x824\xafj\x92\xac\xee\x07M\xeb4saL`\xc2\xa6+\x8d0?,\x89Gh\x13\xc1\x98\xc3cL!\x1a\x8b\xdam\x98\xe3S\x7f4i=\x1a|\xb8D\x1f\x9f\xb4\x1eW\x91^p\x15\xea\x05\x8f\x94\x1ds\x82\x8ee*L\x1e'\xd4\xa6\xd6M\x9c>\x8d\x06\x85\xce\xa4\x93\x1d 1#v\xc6\xa1lg\xc4q\x02\\\x94\x83\x93z0\xcb\xf8\xe6g\xe7\xf1\x02\x10G\x0cpF\xb99\xd9\x05n\x96!\x8e\x1c`\xd2\x0d\x12\xdc\xe9\x1d\xa8\x95h;\x10!\x125,i\x19\xa1\xdd;3\x05B\x11\xf7\xc7\xe5\xcc\x81\xd0\x8aO\\6C(\x8d\xae\xbc\x84P\xbc\xe6\x0c\x83P\x8c\x88\\\x81P\xd4\x8e\xa8\xffP\xb4\xb8\xf8}w\xb4~X\xff\x13\xc5\xdd\x0f\x9fH 0\xd1\x06\xa5x\xa1\xc1\xa3\xbf\x021^~\x84\x9f\x121f\xcf\x04%za\xe22\xbd\xe0(\xd5\x0b\x93\x94\xeb\x85v\xb1\x8e/\xd9\x0b\x8e\xca\xbb^K\xc7\\^b\xc4\xfa \xaeH\xe1Q\xd6\x17\xdc\xbd\x01\xa2Gp\x96\xf8\x05\\G\x80\xec\x0c$:\xeb\x17\xce\x9d\xd8\x05\\\xc0uX\xd1:}KL\xe1:cKdpuP\x01;\x18W\xc4\x0ePbL\x85I\x8b\xd9\x81OA;\x18]\xd4\xce\x84\x01_\xd8\x0e\xd0\xc9Z\x1e\x0b\x1ac\xd5s@%h\xe1\x8e\x12\x1cLw?C\xc0&e!e\x07x\xc8\x0fp\x9ejZ\xf0`;\xb84|\x1fPGE\x15<\xb8\x01\x9e\x1c\xa1\xe0\x95p\xe5\xb3.$\xe0\x97\xa6\x04\xecD\x81\xffd\x81\xef\x84!\x0f\xaa\xda&\x98\xa4*\xe7\xd5w\xf3!~\xa0.\x1f\x8b\n\xa8+\x9c.\x04P\xe2d\xb5GA=\xf0\\\x87x\xf9\x84\xce\x07\x99\x9e\x03>*t\xd2b{\x14\xbc\n\xee\x81\xbb\xe8\x1e8\xcb\x17\x03^\xaea\xe5Y\xb4\x10\xa3\x85h\x86h!\xe2\xd5p\xb4\x10]\xe0\xc1v\xc0\xeb\x00\x0e\xd1BD\x00v\xa2\xc0\x7f\xb2\xc0w\xc2\xa2\x858\x80\x00J\x9c\xac\x8e\x16\"^\x85~\x11\x16\">\xc6\x18\xf0\x0c\xc5\x89\x12d\x99f\x98\xba_\xd4V\xf6\xec\xd1\xb5\x85\xb1\xebf\xc2\x12\xce\xe0]\xc6\x19\x02J9\x03N\xf3a\xb4\x1d\xba\xac3\xe0\xfa\x04d\xbf\xe0S\xe2\x19\xf0}\x83G\xff\x80.\xf7\x0c~\x04\x80'\x11\xe0W\xfa\x19\x00\x1fO\xa5\x07\xcf\xb1@\xc0x\xc0\xb7\x144\xe0\x05@\x1f\xa4@@W`\xe6\xe0Q\x16\x1an\x998\xac\xe8\xea\x03.V\x0c\x8d\xce\xf8\xe0;\xea\xe4)\xc1\x11i\xa6\x07l\xfc\x19\x1a\xa1.N\xcd\x1d\x95\xa6\x07l\xac\x9a\x1e|JLC\xc8\xc2\xc3\x9d\xa0%x\xa3\xc7\xea\xfc.`\xcbN\xc38\x82\\&\x81\n\xde%\xa8\xe1\xf6d\xa7\xcf9\x0f\xc2\x98\x06x\x13\\\x85\x80\x83\xba\x84\x00\xeeA \x07\xc1\xf7\x00/\xc1\xe7\x00\xd5\x07\xff\x9d!\xc1w\xc2!|\xd2!t\xe2\x83\x0e\xfc\x12<\x0e\xfe\x12\xdcq\x8ez\x08\xe0K\x00?\xf0\x91\x92z\xf0*k\x0d\xb74,gt\xa3\x1en\x834|\xbat\x0b\xd8pO4BST\xfd\x10p\xc1\xa2z\xb8\x0dv\xba\xc2M\xf5p\x1b\x94\xf9\x94\xc4\x86[\xa2 \x11\xf2\xaa\x87\xdb \xce\xbbT6\xdc\x12a\xb8\xb0[=\xb8\x83q\xf5p\xf8q\x85\x9c\xce\x90Q\xbf(\\\x98\x8a\xdc\x80\xae\xca\x0d\xfef\x99\xaf9v\x07\x0f$\xa8\xab> !\x16!\xde\xb5.!\x9eD<\xf6 \x87x\x121\x82\xff\x96\x90\xe0;\xe1\x10>\xe9\x10:\xf1\xb7}\x12\xf1\xab\xfe\x0d\x0dG0\xf5K% \xeb\x98J\x08Z\xc0a\xcb\xd7Yn\xc2\x0c\xc1\x0b\x03\x17\x86\xa4\x82O\xf1\n3x\x94\xb50\x83G\xc1\x0b3\xe0Ja\x98\xc1\xbbH\x86\x19\xfc\x8f^\x1c\x90\xf56<\xb1:\xabs\x98\xc1\xfbJ_\x85\xe0\xf5\x1c,\xe6\x10\x17\xa7C\x18M\xa6\x9f'\x95\x03:F@\x85\xd1\xb4z\xb14\xc4x\xe7\x80-g\xe2\x81\xd2\xa3\xd28\x04\xd2\x8e.\x96\x82\xc2f,\xa8\x82\xaa:\x0e>\x95\xc7\xc1O\xc7\xf9h6wM\xc8.\xf8ir\x0f-\xee1:\x0e>c\xe4\xe0-\xe3\x826c\xc0FD\xd5\x9c\xec\x827\xb3 \x88a\xe0S\x97\xb2\x0bA\x8c\x830\xe6\x81_\xed\xca.\xdc.\x99!V\x03\xb6\xd6\xa5\x07\xca\xb0\x8a\xe6\xe0U\x03\xb3\x0bAL\x0e9\xb0\xed\xfc\xeadv\xe1\x16h\xdc!\xab\x9d\x83\xff\xf6\xf6\xdd\xd8\x9er0\x809\xde\xfb\xc3S\x02z2\x08\x02\x98\x04a\xb2/\x80Y\x10\xc20\x08\x95z\xb7G\xa0\xbf\xbc\x9b\\\xda\x85\xca\xba\x10I\x17\xc0X? \x02cd\xdc\xc1\xa9\xf3\x8b\xe3mZ\xf9\x91\x85# UY\x1d\xf0\xbd\xe3z\xf5\xa9\xb2\x0ehK\x1aaA\xa3\x85!^\x04\xa25\x04\x92\x83\x1c\x00\xe7\xb2\x92\x8b*\x16\x0do\xbf\xc2%m\xc5\xa2\xe1\xdd\xbfb\xd0\xc5\xa2\xe1}p\x19-\xd1\xaa\xd4-\x95X4\xbc\xff\xa3\xc7\xb8\x9cY$\xb1h\xb8\x05#\"\x83\"\x14\xf5\x97\\4\xdc\xad\xcb\xfb\x1d\xb0W\x88.\x08\x14e\xbaI\xf3D(\xda\xe63\xe9\xc9>\xd2\xa0\xe2\x83\xa5\xd6dBm\xdb\x17\xea\xd3C\x9a\x06'9\x9c\x13\xf9\x04\xd1\x11\xd4\x17\xb4oQ\x99\xec\x13eY\xf5\xe9\x08\x16\xfbZ\x1a\x08g\xaf\xfe|\x04\xd7:\x11&\xde<\x82\xb4\x86\xba\xe8\xf7\xad\xeb\x9a\xda&\x0bR\xd7\xa4\x84\x8f\xffm1\xb0\xdd\x99\x15\xed\x08biu\x88\xa5\xd5ciu\x97\xed\x14K\xab\xab\xdf\xb8\xac7\x0e\xb8\x88\xf5X8\x137^\xb7\x18S!\x16\xcet_\xfe\x83\xdf\x82\xc6\x9c}8\xa0\xb2\xe9p\x07.\x0e\xb6K\x14\x15\xb0YsH\xd9\x01\x1e\xf2\x03\x9cg\xbf\x16<\xd8\x0e.\x0d\xdf\x07\xd4\x81Z\x05\x0fn\x80'G(xe\xbb\xf9\xac\x0b \xf8\xa5)\x01;Q\xe0?Y\xe0;a\xc8\xe3\xbc\xb6 &k\xcd\xe9\x9co>\xc4\x0f\xd4\xe5\x89R\x01}\x17!!\x80\x12'\xabc\xe1L\xbc\n\xfd\"\ng\xc6\xd2\xea\xbe-\xa3\x85\xd8B\xb4\x10\xa3\x85h\x03\xa4\xec\x00\x0f\xf9\x01\x1e\x86\x87\x07\xdb\x01\xaf\x038D\x0b\x11\x01\xd8\x89\x02\xff\xc9\x02\xdf \x8b\x16\xe2\x00\x02(q\xb2:Z\x88x\x15\xfaEX\x88\xbf`\xc0o,\xad>\x84XZ=\x96Vw}\xe7E\x00x\x12\x01\xb1\xb4:\x07\xa4\x00\xe8\x83\x14\x08^\xd5\xcbciu7\xc4\xd2\xeaz\xc0F\xf4\xe9!\x96V\xb7\xc3\x08\x82\\&\x81\n\xb1\xb4:\xd6\x04W!\xe0\xa0.!\x80{\x10\xc8A\xf0=\xc0K\xf09@\xf5\xc1\x7fgH\xf0\x9dp\x08\x9ft\x08\x9d\xf8\xa0\x03\xbf\x04\x8f\x83\xbf\x04w4\xa8\x1e\x02\xf8\x12\xc0\x0f|<\xa9\x1ebiu?\xd2\xfc\xf2\x9b9`\x83b\xd1\x08\xf1U\xc8p!\xb5z\xb8\x0dv\xba\x82r\xf5p\x1b\x94\xc5\xd2\xea\x9e\xc4\xc5\xd2\xea\x1d8\xfc\xb8BNg\xee\x00`\xbcd\x89\xa5\xd5\xfb\xe0ov\xa1\xae\xfa$\x84X\x84x\xd7\xba\x84x\x12\xf1\xd8\x83\x1c\xe2I\xc4\x08\xfe[B\x82\xef\x84C\xf8\xa4C\xe8\xc4\xdf\xf6I$\x96Vw\xd6V\xd0C\xf0\xc2\xc0\x85!\xa9\xe0[\x9dA\x0f\x9e5\x1b\xf4\xe0Y\xc9A\x0f\xf8\xfa\x0ez\x08\xaa\xfa\xa0\x07\xff\xa3\x17\x07d\xf1\x08O\xac\xa8R\x13z\xf0\xbe\xd2W!x=\x07\x8b9\xc4\xc5\xe9\x10F\x93\xe9\xe7I\xe5\x80\x8e\x11Pa4\xad^,\x0d1\xde9`knx\xa0\x8c\xa5\xd5m\x9fz\xe98\x1f\xcd\x86+\x04)\xc1O\x93{hq\x8f\xd1q\xf0\x19#\x07o\x19\x17\xb4\x19\x036\"\xba\x90\xa4\x04ofA\x10\xc3\xc0\xb7\xc0\xa4\x84 \xc6A\x18\xf3\xc0\xbf\xf0\xa4\x84\xdb%3\xc4j\xc0\x16\xa6\xf4@\x19Zn\xd8\xb7P\xa5\x84 &\x87\x1c\xd8v\xfe\x05,%\xdc\x02\x8d\xbbXZ\xdd\x02\x9e\x12\xd0\x93A\x10\xc0$\x08\x93}\x01\xcc\x82\x10\x86A\xa8\xd4\xbb=\x02\xfd\xe5\xdd\xe4\xd2.T\xd6\x85H\xba\x00\xc6\xfaI\x10\x18#\xe3\x0eN\x9d_\x1co\xd3\xca\x8f,\x1cI\xb1\xb4\xba\x1e\xf0\"\x10\xad!\x90\x1c\xe4\xe0!C\xd0\xfa\x00=x\xf0b\x00\xf8J\x7f/F\x80\x1f3\xc0_\xd6\x1f\x96\x1c\x1f\xc9\x8e\x95\xe9\xb1\xb4\xba\x02\x07\xa0\xc5OB# @v\x8dq\xdcy\xf6\xe8r\xcaa\xfdC\xb1\xb4z\x0b\xb6\xd9\x8c\xa5\xd5\x11\xd7\xabNu\xe4VA\xb1\x08\xa6\x02\xb1\xb4:\xe6\x96\xca\xf3\x9a\xce\xf3B\x0e\x7f\xf5\x16t\xc9\x16K\xab\xa3\xd5\x9a\xf3V\xc9\xa3'\xc3p~u\xa5\xd5{4\xfco\x97\x86\x17<\x87\xa7\x82\x9a4\x15')\x0b\xf9\x0fG\xdc\xdc\xa4D\x89\x90\xb5]\xafdg\x07\xd5:%\xd9J\x16\xcd$+\xaa\xf1\x17j\xb5\xce\x01)\x0d;~ 5\xeb\xf0\xc5\x0d\xaf\xdf\xf8\x81T\xbb\"\xaf\x88,\x1cZ\xca\x7f3\xda\xa8\xb5\xdc^\x13\xffeO\xca\x9b\xc7}\x0c2\x7f\xe7\xc3\xe9K\xd8\x92\xfa\xa2X\xd1\xfey\x0d\xf9\xd9\"\xa9\xc8\xac\x1d\xf1\xec\xea\xc9\x82\xd4\xc9\x93\xd9\x0f\xa4~C7\x1dG%\x89\xe0Km 6\x87\x82r\xa8\xd0\xb5\xb2V/a\x87\xda\xc8\xb8\x94\x0d\x0b\xd8\xaa\xa0\x03,(\x83\"F\x1b<\x03\xc5;e\xad[\xabb\xd5*\xd4^\x02\xa2|]\xe0\x15\xd9\x95\x84\x8a\xda\xd53\xd8e$\xa9\x08\xf3\x9a}j\x1e$\xf8\x04i^\xd5$Y\xc5\x17\x07\x06\xe0\x94\xb6r\xfe\xe2\x8b\x03\xedW\xb8\\\xc6\xf8\xe2@\xf7\xaf\x18t\xf1\xc5\x81>\xb8l\xf9x\xd8\xd2-\x95\xf8\xe2@\xffG\x8fq9\x93\xab\xe2\x8b\x03\x16\x8c\x88\xc4\xa2P\xd4_\xf2\x8b\x03\xc6\xfe=\xcb\xea\xf3\x1d\x17k\xe9\xc7Z\xfa\xb1\x96\xbe\xd9*\x88\xb5\xf4\xd5o\\v \x07\\\x8aB\xac\x94\x8a\x1b\xaf[\x8c\xa9\x10+\xa5\xba\xa3=\xc0oAc\xacz\x0e\xa8\xf4I\xdcQ\x82\x83\xed\xd6L\x05l\x9a$Rv\x80\x87\xfc\x00\xe7\xa9\xa6\x05\x0f\xb6\x83K\xc3\xf7\x01uTT\xc1\x83\x1b\xe0\xc9\x11\n^\xe9\x8d>\xebB\x02~iJ\xc0N\x14\xf8O\x16\xf8N\x18\xf2\xa0\xaam\x82ISt\xde\xc64\x1f\xe2\x07\xea\xf2\xb1\xa8\x80\xbe|\x92\x10@\x89\x93\xd5\xb1R*^\x85~\x11\x95Rc-}\xdf\x96\xd1Bl!Z\x88\xd1B\xb4\x01Rv\x80\x87\xfc\x00\x0f\xc3\xc3\x83\xed\x80\xd7\x01\x1c\xa2\x85\x88\x00\xecD\x81\xffd\x81\xef\x84E\x0bq\x00\x01\x948Y\x1d-D\xbc\n\xfd\",\xc4_0\xc2;\xd6\xd2\x1fB\xac\xa5\x1fk\xe9\xbb\xbe\xf3\"\x00<\x89\x80XK\x9f\x03R\x00\xf4A\n\x04\xafr\xf5\xb1\x96\xbe\x1bb-}=`c\xd5\xf4\x10k\xe9\xdba\x04A.\x93@\x85XK\x1fk\x82\xab\x10pP\x97\x10\xc0=\x08\xe4 \xf8\x1e\xe0%\xf8\x1c\xa0\xfa\xe0\xbf3$\xf8N8\x84O:\x84N|\xd0\x81_\x82\xc7\xc1_\x82;\xceQ\x0f\x01| \xe0\x07>RR\x0f\xb1\x96\xbe\x1fi~ \xed\x1c\xb0\xe1\x9eh\x84\xf8\xb2s\xb8`Q=\xdc\x06;]\xe1\xa6z\xb8\x0d\xcab-}O\xe2b-\xfd\x0e\x1c~\\!\xa73d\xd4/\nW\xac\xa5\xdf\x07\x7f\xb3\x0bu\xd5'!\xc4\"\xc4\xbb\xd6%\xc4\x93\x88\xc7\x1e\xe4\x10O\"F\xf0\xdf\x12\x12|'\x1c\xc2'\x1dB'\xfe\xb6O\"\xb1\x96\xbe\xb3\x98\x86\x1e\x82\x17\x06.\x0cI\x05\xdfr\x1cz\xf0,\xd2\xa1\x07\xcf\xd2\x1dz\xc0\x17\xf4\xd0CP\x99\x0f=\xf8\x1f\xbd8 \xab\x85xbE\xd5\x16\xd1\x83\xf7\x95\xbe\n\xc1\xeb9X\xcc!.N\x870\x9aL?O*\x07t\x8c\x80\n\xa3i\xf5bi\x88\xf1\xce\x01[d\xc5\x03e\xac\xa5o\xfb\xd4K\xc7\xf9h6\\\xe5O ~\x9a\xdcC\x8b{\x8c\x8e\x83\xcf\x189x\xcb\xb8\xa0\xcd\x18\xb0\x11\xd1\x95C%x3\x0b\x82\x18\x06\xbe\x15E%\x041\x0e\xc2\x98\x07\xfe\x95F%\xdc.\x99!V\x03\xb6\x12\xa9\x07\xca\xd0\xfa\xd2\xbe\x95I%\x0419\xe4\xc0\xb6\xf3\xafX*\xe1\x16h\xdc\xc5Z\xfa\x16\xf0\x94\x80\x9e\x0c\x82\x00&A\x98\xec\x0b`\x16\x840\x0cB\xa5\xde\xed\x11\xe8/\xef&\x97v\xa1\xb2.D\xd2\x050\xd6O\x82\xc0\x18\x19wp\xea\xfc\xe2x\x9bV~d\xe1H\x8a\xb5\xf4\xf5\x80\x17\x81h\x0d\x81\xe4 \x07\x0f\x19\x82\xd6\x07\xe8\xc1\x83\x17\x03\xc0W\xfa{1\x02\xfc\x98\x01\xfe\xb2\xfe\xb0\xe4\xf8Hv\xacL\x8f\xb5\xf4\x158\x00-~\x12\x1aI\x00\xb2k\x8c\xe3\xce\xb3G\x97S\x0e\xeb\x1f\x8a\xb5\xf4[\xb0\xcdf\xac\xa5\x8f\xb8^u\xaa#\xb7\n\x8a\xe5\x1d\x15\x88\xb5\xf41\xb7T\x9e\xd7t\x9e\x17r\xf8\xab\xb7\xa0K\xb6XK\x1f\xad\xd6\x9c\xb7J\x1e=\x19\x86\xf3\xab\xab\xa5\xdfT\x05oy&\x0b\x89\x9f\xa5\xf9\x92\x05\xe4U\xdb\xa2zT\xad.\xe1\xab\xd97\x7f\x88E\xc3\x07\xe0\\VrQ\xc5\xa2\xe1\xedW\xb8\xa4\xadX4\xbc\xfbW\x0c\xbaX4\xbc\x0f.\xa3%Z\x95\xba\xa5\x12\x8b\x86\xf7\x7f\xf4\x18\x973\x8b$\x16\x0d\xb7`DdP\x84\xa2\xfe\x92\x8b\x86\xbbuy\xbf\x03\xf9\xdaPQ\xa6\x9b4O\x84\xa2m>\x93\x9e\xec#\x0d*>XjM&\xd4\xb6}\xa1>|\xa4ip\x92\xc39\x91\x0f\x10\x1dA}A\xfb\x16\x95\xc9>Q\x96U\x9f\x8e`\xb1\xaf\xa5\x81p\xf6\xea\xcfGp\xad\x13a\xe2\xc5%Hk\xa8\x8b~\xdf\xba\xae\xa9m\xb2 uMJ\xf8\xf8\xdf\x16\x03\xdb\x9dY\xd1\x8e \x96V\x87XZ=\x96Vw\xd9N\xb1\xb4\xba\xfa\x8d\xcbz\xe3\x80\x8bX\x8f\x853q\xe3u\x8b1\x15b\xe1L\xf7\xe5?\xf8-h\xcc\xd9\x87\x03*\x9b\x0ew\xe0\xe2`\xbbDQ\x01\x9b5\x87\x94\x1d\xe0!?\xc0y\xf6k\xc1\x83\xed\xe0\xd2\xf0}@\x1d\xa8U\xf0\xe0\x06xr\x84\x82W\xb6\x9b\xcf\xba\x90\x80_\x9a\x12\xb0\x13\x05\xfe\x93\x05\xbe\x13\x86<\xcek\x9b`\xb2\xd6\x9c\xce\xf9\xe6C\xfc@]\x9e(\x15\xd0w\x11\x12\x02(q\xb2:\x16\xce\xc4\xab\xd0/\xa2pf,\xad\xee\xdb2Z\x88-D\x0b1Z\x886@\xca\x0e\xf0\x90\x1f\xe0axx\xb0\x1d\xf0:\x80C\xb4\x10\x11\x80\x9d(\xf0\x9f,\xf0\x9d\xb0h!\x0e \x80\x12'\xab\xa3\x85\x88W\xa1_\x84\x85\xf8\x0b\x06\xfc\xc6\xd2\xeaC\x88\xa5\xd5ciu\xd7w^\x04\x80'\x11\x10K\xabs@\n\x80>H\x81\xe0U\xbd<\x96VwC,\xad\xae\x07lD\x9f\x1ebiu;\x8c \xc8e\x12\xa8\x10K\xabcMp\x15\x02\x0e\xea\x12\x02\xb8\x07\x81\x1c\x04\xdf\x03\xbc\x04\x9f\x03T\x1f\xfcw\x86\x04\xdf \x87\xf0I\x87\xd0\x89\x0f:\xf0K\xf08\xf8KpG\x83\xea!\x80/\x01\xfc\xc0\xc7\x93\xea!\x96V\xf7#\xcd/\xbf\x99\x036(\x16\x8d\x10_\x85\x0c\x17R\xab\x87\xdb`\xa7+(W\x0f\xb7AY,\xad\xeeI\\,\xad\xde\x81\xc3\x8f+\xe4t\xe6\x0e\x00\xc6K\x96XZ\xbd\x0f\xfef\x17\xea\xaaOB\x88E\x88w\xadK\x88'\x11\x8f=\xc8!\x9eD\x8c\xe0\xbf%$\xf8N8\x84O:\x84N\xfcm\x9fDbiugm\x05=\x04/\x0c\\\x18\x92\n\xbe\xd5\x19\xf4\xe0Y\xb3A\x0f\x9e\x95\x1c\xf4\x80\xaf\xef\xa0\x87\xa0\xaa\x0fz\xf0?zq@\x16\x8f\xf0\xc4\x8a*5\xa1\x07\xef+}\x15\x82\xd7s\xb0\x98C\\\x9c\x0ea4\x99~\x9eT\x0e\xe8\x18\x01\x15F\xd3\xea\xc5\xd2\x10\xe3\x9d\x03\xb6\xe6\x86\x07\xcaXZ\xdd\xf6\xa9\x97\x8e\xf3\xd1l\xb8B\x90\x12\xfc4\xb9\x87\x16\xf7\x18\x1d\x07\x9f1r\xf0\x96qA\x9b1`#\xa2\x0bIJ\xf0f\x16\x041\x0c|\x0bLJ\x08b\x1c\x841\x0f\xfc\x0bOJ\xb8]2C\xac\x06laJ\x0f\x94\xa1\xe5\x86}\x0bUJ\x08br\xc8\x81m\xe7_\xc0R\xc2-\xd0\xb8\x8b\xa5\xd5-\xe0)\x01=\x19\x04\x01L\x820\xd9\x17\xc0,\x08a\x18\x84J\xbd\xdb#\xd0_\xdeM.\xedBe]\x88\xa4\x0b`\xac\x9f\x04\x8112\xee\xe0\xd4\xf9\xc5\xf16\xad\xfc\xc8\xc2\x91\x14K\xab\xeb\x01/\x02\xd1\x1a\x02\xc9A\x0e\x1e2\x04\xad\x0f\xd0\x83\x07/\x06\x80\xaf\xf4\xf7b\x04\xf81\x03\xfce\xfda\xc9\xf1\x91\xecX\x99\x1eK\xab+p\x00Z\xfc$4\x92\x00d\xd7\x18\xc7\x9dg\x8f.\xa7\x1c\xd6?\x14K\xab\xb7`\x9b\xcdXZ\x1dq\xbd\xeaTGn\x15\x14\x8b`*\x10K\xabcn\xa9<\xaf\xe9\xd1\xee\x1fo\x8ab\x93\x91\x19\xe3\xc5b\xbf\x9e\xbd\xda\x97,\x95\xee\xd3CN=CY]\x14\xfbl\x05\x0bV\xb0F\x9f\x18\xb1L\xf2\"O\x97I\xc66\x88\xbe\xc7\x07d\xb6\x99\x1dQ\x16\xb2\xa4\xc0{\xb3{TT\xb0\xba\xae\xcb%\xd9\xd5d\xf5p\xf6;}\xd3\x93\x1cv\x94\xa9\xe9\x92\x1cAM\x92m\x05\xfbj\x9f\xd0\xe1\xf3\x9a\x00\xbb4\xa3\xd4\xd5\x05/\x9f\x9a\xe6Iy\x03I\x96\xe9yw\xb3#\xa2\xb6k}An\xf4]\x92\xcf;\xb2\x94e\x84\xf7\x95,\xd6\xc3\x16\x03\xf9\xcc\xa6\xf28\xbf\x99\xc1\x8f\xc55\xb9\"\xe5\x11\x93p\x1f?\xbc\xd1\x1f\xee\xb9=@\xd1\xd0\xe5\xaa_\xaf\xcb\x0b\xb2%\xf0\xe9\xa2\xaew\x9f\x8e\xf8\x7f\xabO\xac\xd2A^\x88_\x8f\xd8*[&9\x14l71\x0e\x0c\x15\n\x87\xfdNT\x1e2\xf4G\xca+Rr6l\x93\x9d\xa8\xb7LG\xc0\x8e}\xa2D\x11s\x7f\xa4\xbcJm\xa2\x1f\xdb\xba\xc8\xb2\xe2\xbazf\x98\xbb\x7f\x87\x93u;\x02:\xe5\xbb\xb2\xa0\xcar\xd5\x0c\x92\xa9\xe9\xaa\xdao\xc9\xcaP\xe2\xe8\xdf\xe18\x87\x1f\xcf\xcfO\xe1\x87\xd7\xe7\xa2\xb4/\xa5\x95o\xd0\x1b\xa6\xc4\xf4+\xf3o\xfd%~~\xb3#\x7f\xff\xdb\xdf\xb5\x1f3Y\xbegs-\xd6\x10\x97\xf7l\x16ve\xb1\xda/ $9\x90\xb2,\x0c\xd1\xdb\xff\x0e\xc7mFj\xc5\xaa\x14'\x94?\\\xb7.\x93%\x95 Eq\xb9\xdf\x81\xc8E\x00\xaa\xdcVP\xe4\xa6\x8dn \xf5\xe3\x877\x8c\xae\x8b\xe4\x8a-\xabmg/\xac\xf8fH\xe40\xe8\xff_\x15\xe9\n\x92\xdc\xe4\xb7\xe5D\xb1m_\x92uQ\x92#\xd9\x98\xe2L\xeat\x91fi}\x039!\xab\x8a\xdb \xc0DTye\x0c\x98)r*\x0e\xf3\x0da\x0d\xd8\xbe\x9b\xc1\x83\x8f\x15\x91\xc9\xf2\x94+t\xd9Q9\xc3\xd7]\x92'\x1b\xd3\x88\x17%I.\xa9\xec\x10Hg\x0f\xf5\xab\xe5]Q\x93g\xbc\x88\xf8z\x9f/\xf9N\xa1\xb4\x0by\xb3\xdc\x97%\xc9\xeb\xec\xa6\xe3\xcc\xb3dD\x17\xebu\xbaL\x93\xcc\xa2G\x16\xfb5\x94\x84j\x07r\xc4\xf2\x94\xd3Zv\xb6\xa7\x93\xcb\xcc\x9ff\x7f-\xc8&\xcds\x93\xadK\xad7\x83\xd0\xbf\xd9\x91\x19_\xcf\xc9.\xadf\xcbbk\x92\x98gl\xb7UP\xd4\x17|\x93\xe7}\xc9\x02\x0f\x84IF\xb6\xbb\xfaFl\xcf\x87Zd[\xe6\xf9Y\x18\x04 \x1b 39\xd3\xed.#T\xd1\xb1\xc5\x0f\xd5\x8e,\xd3u\xba\x84\x8al\x93\xbcN\x97\x9a\xb8\"\xb6\xdf\x02L\n\x9f\xe3\x80\xc1\xe2xKE\xc7\x82\xc8\x8a \x1d\x83a`\x1b\xc8\\\xf3Eqe06\xf8P\xc5r\xee\x0f\xd3E\xcd\xa7\xe3\xfc\xe6S{\x9cHrH\xcaEZ\x97t\xf3Y\xa8\x122z\x80.\xc9\x8a|\xc3g$\x19N\x19\x95\x9aL\xe8s\xaa\x16Cs\xaa\xdb\xa7\xb4\x8a4\xcb\xecT.\xfc,]0R\x85\\\xaf\xa0\xda\xefvE\xc94\xe7.Y^>\xde\xe7\xf4?T_\xf2\xf9fVI\x1f\x1d\xb3h\xb4\xc6C\xb1\x86}\xcd\x85\x8f\xdc\xce\x15\x15|\xc9j\x95\xf2\xbd\x0d\x1b\x92\x932\xa9\x19\xc1\xf4\xe8\xd0\x94\x078\xd6\xc8;>E\xc3~^\x7fN\xe8\x02\x86'\xcf\xe0\x94\xd2K\xf7\xb1 =\xe9\xd6\xec{\xf9\xfb\xdf\x1b\xd4\xd4\xf7E\x01\xeb\xa2\x80\xe70\x9b\xcd\xfe\x8f\xf6\x13\xca\x84$\xbf\xd1\xff\x98\xe473\xda\xf5\xf7e\xb1}\xb0.\x8a\x87\xfa\xcff3\xbd\xeeI\xd7\xf0\x80\xa2\xf8\xc8\x88>/\x1e\xfc\x1b\xc5\xf1\x10\xfei\x90\xa7&<\xff2\xf3\xe6\xa9\x837\xff\x99\\%\xa3\x99\x03\xcf\x99mE\xb1\x8f\xe0BZ=\xf8\xbe(f\xcb,\xa9*\x0b\x138I\xb4\x01\x1fO\xa7\x91\xbe_\x0dw\x1a\xf6|\xed`\xcf\xe9M}Q\xe4\x06\x06qJ\xbe/\x8a\x07\xb3\xd9L/\x89\x1b\xe6<0\xfe\xce\x16\x10c\x9b/\xd7h\xe3\x13\xce\xb4W\xaf\xcf^~89=\x7f\xff\xe1\xa1\xc9g\xd3.4sg\xbc;3\xbb\xbeq\xb0\xeb\x87\xc2P\xd1\x83\xb2\xea\xd9s\xf8\xb7\xddb\xf6}Q\xfcs6\x9b\xfdK\xffa\x92\xdf\x1cQs\x8d~\xbd\xe3\x06\xc8\xdb\xa4\xac.\x92\x8c2\xd1L\xb8\x89M\xfd\x9e\x0d\xdd\xa6\xeb^\xa7\x1f\xf3m\xdb-#\x8a-l\xf6\xd5\xff\xf7\x1c\xf243.P3-\x9a\x95H\x0fm\x8c\x8fR\x0eJc\x1b\x167\xad\xa9\"%6\x7f\x98\xe3F:@\x07\xd8\xf6\x95F\xe7\xdf\xd7\x98!\x8f\xe9Yt\xc6~\xa0\xa6\xdc}H:Z\x85j\x1cQ\xbbe\xd8\x03\x9b\xf5a'\x8d\x18\xcf\xb3\x1byn\x1a\x1cx\x1b\xd3\x11\x92uM\xb85C\xcf\xdbC\x92\x1f\xdf\x1fv!\x0et\x92D~\x82#be\xde[\x17\xc5l\x91\x94lp\x9f\x1f\xdf\xcc~\xbe\xc7\xb9\xc5\xcf\x1a\xfac\x15#\xe5\x1e\xfd\x96\xaa\x97\xc1\xcf\xffy\xf6\xfe\xdd\xf0\xaf\xcf\x9f?\x7f\xae\x9fG\xfa}\xeb\x07\xe06UA\xb7\xa90\x18\xf8Ye_\x11\xe9p\xdb\xec\xb3DS\xdbn\x88\x82~\xbe\"\xad\x9a?\x02\xb2]\x90\xd5\xaaU\xf8\xc2\x89\x98h\xbc\x07\x1d\xb5\xcb\xbd\x87\x9f\xfe\x83\xb2\xe3\x938\xe4*^Q\xc9\xdc\x99\xdc\xf2\xcf\x0cFt\xb2\xbc\xa4{\xbe=\xac\xad\xd3\x8c\xe8\xe5\xaf\x94\x0f\xa7\xa4\xac\x8a\xdc\xb8m\x84\x07\x87\xbd\xf32g3\xf3\x1c\x9e\xe816\x1f\xb3\xabL\xf1\xedS\xbc\xf4\x070Rq\x8f\xf1\xe6\xde3\xb8\xa7\xdb5\xeapg|D\xf7t/*1\\l,\xef\x92-\xc5\xf7\x7f9\xc9\xff\xcf\xf81\x1dK\xef[\xec\x80N\xd6\xe2`\xa0\xae >\x9bi\x05\xd7$\xcb\x1e]\xe6\xc5u\xce\xf6\xf5\x05{\xe4i\xb9\xaf\xeabkX\xe4\xea\x12<\xe2\x06ho]\xcaW\x82\x9an\xe9B\xcb7\x9as=[v\xc3N>\xb1\x0d!\xd7\xe1E\x91\xadD\xed\xac\x962\xe6\xc1\x12\xeb\x17\x84\xb7H,\xdf!>\xd6M\xb3r\xe1\x01\x95\x0f\x92\x15\x03\xb7\x82\xf4\x9c\xfd\xfdo\x7f\x7fhX\xe4c\xd7\x88\xda\x91y\x9906PtOfO\x9f<\xad\xee\x19\xa6\xbd\xfb/[|\x85\xe5$f\xbe\xb0u\x06\x8f\x04`U\x8eQ\x8d\xc3_\xde7\xd4\xddk\x86\xc6%\xff\xa8\"\xcd\xd5\xce.\xd9\xa49\xe3]K\x8c\x82\xb3\xfd\xa0 \xfbI\xf2\xee_%zy\xb7\xd1\xea\xe5Z\xe7T\xd7\xbb\xd3YU\x91\x81_\xde\xc8\x0f\xe3I\xd7|?\x04\x9d^${\xe8\xff\n\xa7MRU\xdc\x1bu\x9al\xc8\x07\xf2\xd3\x9eT\xf5\x8c\xff\xdeC\xf2\xd3\x9e\x947\xac9EG9A`[T5\x10\xe6\x12a>\x94\x19\x9c\xd4\x9d\xbd\xbb\xabo \xed\xc7\x99\xd4\xcd\x8b]y\x01\xdb\xa2$\xd2\xf7\xd55l4!\x04N\xa6hJ\xf2\x99\xc2\xbc\x18z\xc6\x0d\xf6?\xf9~\xbb\xe0'{\xe9\x86\xeb\xf8\x82\xfa\xf4w\x19\xb5,\xf6y=gH\xfa\xdb\xfc:\xa9\xa0\"\xf5\x11\xa4u%=\x89\x15\xecs\xbe\x98V\xdc\x19s\x9d\x8a\x901\xfd\xfcYo\xb4\xacwk\x02As\xc3\xd6E\xf0\xe2\x86?\xb1\x06\x1fN_z\xdd\xae\xbd+V\xe4$_\x17\xdewj\xc2\xa4\x9c\xe7\xc5\x8a\xcc\xd3|]\xf4\xef\xc6P{E\xba<\xe6\xda\xe2\x9aZDfd\xec\x97\xa7\x9a\xebn\xab\xaf\xc9\xb2\xd4\x8c\xf51\xc3\x11j+Z\x86\xa1Sg`\x85\xdaYYZ\xd5$g\xd1\x07\xa8\xefsR_\x17\xe5%\xea[\xcb\x14\x0e\xbe]^$yN\xb2\n\xf5\xb1QFn\x8b<\xbd\xd4\xbf\x1c>@\xc2\xf6\xe6\xe8\xe5U\x7f6U\xf8\xb6\xcca\xb9[z=\xea\xd9)y9\xdc\x17\x1e\x9b\x8b\x1a\xba(\xde$\xbb\xdd\x1c\xfd\xb1\xcf4oR[\xe4\xe0\xe0\xf3\xc5>\xcdV\xf3:\xd9\xe0\xd6\xc5\xa6\xb0\x89\x0d\x03\xf6\x15\xd9i\xb1\x0f\xe3\xb5\xb4\xb1Z\xc65c\xbf\\\xdf%\xb56\xaa\xcd\xba\xf3\x1b]\xb7-V\xfb\x8c0$\x83\x8f,U\x89\xbd\x90\x0b<\x83\xef*}@\x18\x0e\xf7\xf2\x82,/\xab\xfd\xb6\xcfD\xfe\xeb[\xdes\xdf\xbe\xfb+\xa7\xe4D\xadB\xc5U\xd9\xbcZ]zMz\xd3\xd9\xfd\xb34_\xb22W\x14\xcf\xa3ju _\xcd\xbe\xf9\xfa~\xf3\xadj}\xb64h\xedO\x8d\xe6l]\xe9\x03l]\xe5?h\x86\x0b\xa7\x91\xcdX\x14\x0dG\xe6\xa3\xe6\xcfn\xf2e\x9ao\xbc\xb5|\xc5\xdb\xf5\xc5\xcf\xa2(2\x92\xe4\xc3\x91vc\x87z}\"\x07*Z\x05\x8eSg\x13y\x0f:\x86\x0b\xc9\x161\\(\x86\x0b\xc5p!\x88\xe1B1\\(\x86\x0b\xc5p!\x0e1\\(\x86\x0b1N\xc5p\xa1.\xc4p\xa1\x01\xe0#bb\xb8\x90\xee\x93\x18.\x14\xc3\x85b\xb8P\x1fb\xb8P\x0c\x17\x8a\xe1B\x0d\xc4p\xa1\x18.\x14\xc3\x85b\xb8P\x0c\x17\x8a\xe1B\xbf\xcap!\xdb\x9d\xd6-\x07\x0c\xfd\xd8)23X\xe2\xc3\xe5=\x0c^p\xbc\x89\xeb\xb1m4\x919\xfe\xcbc\x10\x8d\xe3\x8b\xc2\xec\xca\xc1\xbd\xf3\n\xd8\xf7[\x0d/\xb4b\xdf`\xd5\xbe\xad\xaa2B\xff\xca\xaa\xeb\x1d\xd5\xe1K\xa9\x1a\x16\x06_\xa5\xaao\x92Z\x9b\xf5+\x94\x18^\x0e\xf5Xa\xc3JD\xce\xd5\xd1\x13\xca\xd6\xe2LZJ\xc0\x1e}\xa4/\xc2\xc41!\x8a]\x0d\xa2\xd1\xf4\xc5\x96,\xba\xd8\xa2\x7f\xacE\x95\xb4\xc5\x94\xccoGZgZ\xe9\xd9\xfd\xc2\xa3\xe6\x0dG,z\xe3\xd3\x80X\x04}\xfa\x9c\x8f\x1c*u\xfe\xed\xcf\x13bi0=\"\x88m?|\xea\x0f\xdb\xd2\xf2 \x1f\x16\x85\xe1\xd9\x1d\xc1b_K\x7f\xc6\xd9\xab?\x1f\xc15\x91%\x80\xf8\x0d\xb6\xb9\xafnWT7-H]\x93\x12>\xfe\xb7&\xac\xc9\xfd`^K\xa9`A\xfb.\x9e\xc3\xd2\xe0\xb1ahKC\x8d\xaf\xd3N\x9f1\x96\xce\x10bim\xaeF\xcb)1r\xb6\xd6J<\x9cO\x14\x9c\x83[\xa7eQ\xac\xdf\xef\xd0\xecb\x9f\xd8(V\xce#\xd8\xddF%\xado+\xfd\xa6\x12\xe3\xe9\x1e\xbb\xe8\x10\xf8\xa9\xab\xb9A]&\xd9r\x9f%\xac\x92\xe5[R^f\x04\xca\xa2\xa8y\xa8JG\x03,\x8b}\xeb\x1fX\x90\xcem\x9a\xbc\xce\xe7^Q\x8a''UE\x96\xf47fAu}\x809\xb5b\x16\xc5\xbe3\xa0\xbcX5~?*\xa1\xdak$~\xe7|\xde\xdd\x9d \xac\xf6\xdc\xcajn\xd6\xe48\xd9i\x9e\x7f(\x0f\x07m\xa0N\xbb\x89\x10\xfbF`\xac\xd0K\xa1\xd8\x1d8LM]k]\x14\x1a\x0b\xc3\x18\x9ff\xf5\x0c\x0c\xcf\xc2\xcaJ\x0c\xc5c\xbb\xa7\x1d\xbfB\x07\x04\xab\xeb\x94C\xf0jU\xb0tW\xae\xb2f9\xa8+\x97\xc3\x84\xeb\x97\x83\xba\x8aM\xc7]\xb9|i\x7f\x82a;\xfa\xa7\x06\xed\x82\xfb\x1f\xb2\xb4b1W\xf2\xfbY\xd0\xbe\xab\x0c\x84\xeb(v\xec\xbb\xe6l\x8d\xdex\x18\xa3c\x10\xb2\xa9\xdd{\xfa\x9dWk\x034\x8d\xbb\xc0\x1e\x940a8\xa69\x10s\xba\x10\xcc\xc9\x82/\x8da\x97\xba\xfb'd\xc0\xe5T\xa1\x96\xf6 \xcb\xa0\xf0\xca\xe0\xc0J6\xde~<\xa01\xa428\x98\x92\xfb\xf2{\xd8\x0ca\x94c\x02(Y\xb0d\x7f4\x9a;\xbe\x90\xa0Is\x80\xe4\xc8\xd0HTP$>\x00rD\xe8\xe3\x88\xa0G\x8d\xc0\x980\xb4q\xda\xa0\xc6\xc9\xc2\x19\xdd\x81\x8c\x93\x850\x9a\x82\x17\xc7\x84-jC\x14kLpbhX\xa21\x0410\xf8P\x13vhT\x94\xb8\x0b\x98\x81\x06\x0d\x0c/lC u\xfc\xfd\x9d\xbb\xefq\xc1\x84\xc2\xdco\xe9\nuh>\xd3\x15\xe8\xd0a\xd3\x14\xe60bS\x0br\xd4\x07\xb8\x81\xd0\x15\xdf0\x1e\xef\x9cE7\x0c\x057\xd0\x08u\x8564E6\xdc\xf84\xc55|\xae\x14\xc1QTCK\x00\xa6\x98\xc6\x94\x854:\x1biY\xde\xec\xeab\xd6\xbc\xdc\x88\xdeE\x83\x173\xad\xc2\xa0s\xac\xd7\xbcn\x89k*Ca<\x1e\x99\xd4\x93\x818,i?\x1f\x1e\x9d\xfa\x9fmIU'[\x8dS\xa5\xf3\x11nP\xb6s\xb9\n\xcdi\xc3\\\x93\xbf\x05\xcf\xde\xad\xacl;6\xc8\x8d\xab\x8d\x10\xd1\x836nD\x0f\xb7A\x10F\xac\xf4\x01\x1f\x8f\x82Bg,yg\x8bR\xd1\x83O\xec\n\na?\xbe\xc5\x1e\xd1\xa2\x07L\x9c\x8b\x1e\xf4\xd1/z\xf0Z,\xeeS\x9d\x04/\xb4\x18}\xd9\x85a\xac\x8d\xe1\xbb0\"\xdc\x96\xab\x04K\xb4\x8e\x1e\x0e,\xb7\xb0\xe7\x13\xf0g\x0e\xe0LN\x15<\x0f\x8f\x12<\xb9\x04\x01\x9c\x02\x9fC\xa5\x04\xeca\xa0\x0f~\xab[\x82\xcfdB\xd8\x84B\xc8\xa4z\x1fB{\xcd\\\x87Q \xf6\x18)=x\xf2\xc0s\xec\xb8(+=\x18b\xaf\xf4p\xc8aX#\xa5\xf4pHr\xc0X]Y\x0f\xd801\x14\xb2~(\x99\x1e\xdc\x01fz8$\xdbl!jz8$5\xfa 7=\x1c\x92\x0eG\x98\x9c\x1e\x0eI\x90%\xd0N\x0f\x87$\xc6\x1d\xaa\xa7\x07{\x00\x9f\x1e\x0e7\x8e B\x00\xf5\xa0{d|\x08\x1e\xb6\x89\x8fMrG,k\xe7=\x8a\x04_S\x08\xe7\x1f\x95\x10Mj#D\x93\x9a\x81\xdf\xb2\x96\xe03\x99\x106\xa1\x102\xa9\xb7eR7\xf7\x0f\xa8)\xe5\xa3\x1f\xc6\xb5\xeaA\x1b\xed\xaa\x07\xef\xc5\xe8\xbf\x14\xa5\x04\x99\xaf\xb3d\x83m\x148\xe1\xee\xf8 \x15\x1e\xc1\x8b7\xef_\xfey~\xf2j\xfe\xfd\x9b\xe3\x1f\x10\xb1\x06}\xe8c8~q\xf6\xfa\x9d=\x02D|\x85\n}\x04\xefNla\x16*4A\x17\xe3\xd8\xe0wn\xe0\xc07\xca\xea\xfb,\xd9@\x9a\xaf\xd8\xa5\x87\xa8\x08 /\xb2eqy\xf2\xca\x19\x7f\xa1B\xb3\xa5 \xc5\xbb.=\xef>U\x08Z\xa3A\" q)\xac\xc2(\xd2\xf0n7\x0e\xa8\xcbT\x15F\xd1\x87f]\x88o\x1c\xe0%3?\xcf\xd2\x0d\x8f\x0f\xa2z^:\x8f\xd9\xfd\xacLj@\xa2KsH\x04N\x97\xc3\xd8\x97^\x8eUM\xc3h\xee\x08Y~\x874\xbb\xaf\x0d\xef\xbbw\x81\x9b\xdd5\xcf[Id>G{\xdc6S\xdf\xee\xa1\x8a B\x8d\x9c\xfa\x06\xabeZ\xda\\+\x0f\xafA\x91\xda\x139\x12\x0e\xd8\xf1p\xf0\x92C\xde\x1b\xc9s\x13\x0d\x8aV\xdb\xc0\x8b)\xe0\xcd\x18\xd0\xd5\xffp\x817\x83\xc0\x9fI\xa0\xaf/\xe2\x82\xdb!\xcdW;c\x0b\x9d \xd1\xe9\xcb\xa1\xb8\x00\x13i\xa2\x8273}\x0f.;\xd7\xab\x87z8 ]\x92\"\x17!\x1e\xdb\xd2gCz\xc8)O&x\xadq\x0f \xe5\xc1\x08\xf0d\x06\xf8\xcb&O\xa6\x80/c D*\x1d\x9e(?y\x84\x95F\xac\xfe\xa6\x0b\x19\xc8\x9a\xc7\x1e\xb2\xc8W\x12y2\x10\xbf\xdb!T\x06\x1d\x8c\"|P`\xd3\x02O\x8a\x9b\x0cj\xaf\x16\xf9\xdc\xee\xb0E\xf4\xe8\xeeiq\xf3s\x92\xd7iN\xe6n\x8b\xd3mi:,L\x94\x90\xc2\x89&\x94\x84Fp\x88\x03r\x9f\xa3\xe41j\x90\x80\x1e(\xf8H_\xf4\x80\x01?h\xf0\x93\xb5\x87!\x01+Y}d*\x7f\x0e\xc0\x8e\x0f%M\xf1b\x02\xcd\x1c\xf7\xc6\xe5\xe0)5'\xec\x1f/!\x11\x9d\"\xbas9\x8a\xe59\x9f\xb8\xf0P\xe0\xbaG,\x02\xa4\xeb\xd0\x0e\x00\x97H1\xddJcm\x02\xfd\xb7\xe6\xc2\xc9x\xdb\xc0\xf4\xb5\xa1H\xb2\x97\x8d\x00N;\x01&\xb6\x15 \xc0^0\xb5q\x9556\xc5z\xa2\xd4\xacNe\x19\xd2\xec\xedz\xd0$\x7f\xc1\x9a0?Bc\x1fJ\xa9\"\xec\x0cpQ\x0eN\xea\xc1ns\x80\x93\xdf\x12\\\xb6\x078\x13\xcb\x9d\xec\x027\xcb\x10\xb6\x08\xd8\xec\x110\xdb$\xe0\"\xd1~c\xe2\xb4O\x00\x8b_3tg\xa9\xde\xf0=c\x89\x1e \xa3\xd6\x1e\x9f\x18l\xc7\x988\xa2-\xad\xab-\xa8\xab{\x8c j\xc3\xa8\x0d\xa36T j\xc3\xa8\x0d\xc5O\x98\xa5\x1c\xb5!|\x91\xda\xd0\x95\x1da\x1c\xaei\xe9[\xcb\xc6{c\x9b\xceqd\x9e\x81\xa9<\x01`\xf2\x06\x00\xba\xb8\xbbV\xae\xe9e\x99\xa3|{\x80E\xe3(\xd0>F[\x1cV\x94;\x8b\xac\xfb\x96Vw\xd2\x04(\xba\x00W<\x1d\xa5\x02\xa0\xb3\xb8\x9du\xc9\x9d\xe5\xd1\xa7\xee\x12\x93\x8c\xc4\x01_\xf6\x1c\xda\x92\xe6\xb6\xa1\x06\x15;\xf7)q\xde\x96/\xb7 \xf4-l\xee[\xce\xdc]\xc4\x1c1\xa5\xae\xb2\x8a\x08\x148\x93\xc7^\x92\xdc\xab\x1bW\x1d$d\xd1\xf1 w\xb4\xbbl\x1ej\x80\x80\xb1\xeb8\xa0Lb \xa8\x91\x02z\xb4\xe06\x95\x9b\xcfP&\xb3\x04\xcc:\x92\xe0f9\xf8\xb0\x1d\xf0\xacG\x9a\xd6\xbd\x8f\xed\x15\x08\xf1\xa5\xbcQ#B\x8d\xc4\xbfX7\xa2D\xf7T\xe4\xa1\xabIO\xd5a\x9f\x1f\xce\xfa\xd9\xd6\xda\xd8~\x15\xb1\xa7\x1a\x03\xb6\xe6\xf5T\xfd\xb9\xabZO\xd5\x93G\xdd\xea\xa9\xbaDV\xa6\x9e\xaa;\xf0\xaa=\xbdCW\x9c\x9e\x86\xbe\x89kJ\xdb*I;\x95\x95[I\xdd\x9aQ\xe3\xa8\x06\x8d\xd3~6\xa7\x83\x84h\xcd\xb4\x80\x1a)\xa0G\x0b\xd1\x9a\x99\xda\x9a\xc1TQ\xe6c\xb1W~t\xd6|D.\x05\xecBp&\xdcw\xc1c2p\xf5\x8f\xdd\x99=\xb8v\xc8Z\xc7\xce, \\3w]cd\xc6\x90\x1e\xb0\xb5s\xa6\xab\\\xec\xac \xd0\x05\x84+[\x05\x8fU\xe3\xb1\x81\x91u\x88\x03:w\x9d\xb59\xa0+\x0d\x07P\xe0\x18>\xde\xcd\x84\xaf \xcc\xb2\xb0\x10\x98\xf4\x9e\x19\x1cE\xe1\xe9\x89\x06\x84\x88\xca\xc0\x8ez\xc0Vij\x97\xa1\xae\xfak.io\x95\xf4\x08)\x8f\x91\xf0\x88\x1d\x8a\\\x9e\xa8\xa5\x89\xa8\xb7\x86\x18\x18 \x07\x07\xf8Jk\xc8A\x02v\xa0\xe0Scm\xfa\xceq\x1ab\xda\xdaj\x88\xcaj\x98\xe2b\x80g\x08\xce\x98\xdc\xf9TT\x9b\xa8\xe7\x9d\xb5\xda\xads\x89\xbb\x17\xb7s\xdf\xa2\x06\x82XM\xce\x1d\xeb\x1c\x0c\xa0\x06\x04\xd8\xbd\x8a\x1a\x18\xe0\x06\x07\xf8]:m\xb7\x98\xfd9\xe5\xeet\xeeM\xdc\xceD1\xc1\xb57\xc0oON\xd2\xa7\xeb\xf6\xbe\xf9\xce\xd5\x99\xa9#K}W#N\x13.w\x05W\x93\xe50e\xa1\xb5\x03\xd5\xf6\xb2\xc8\x13\x87$q\xc9\x10\x87\xf4pN-b\xeb:e\xc5\xf8N\xa4\xe3\xc1w\xf3\xdb\xb6\xb8k\xdd#\xe7S\xbf\xb9P[9\xb0\x07\xd7\xb6\xf5\xdeZ\xb7\x11&\x13XLT\x9fx\x84-!jL\xa4y\x93V\xf8\"Z\xa6@\x1bT\x81\x8c\xc1\xc65mXg\xca\x8e\x15\xab\x0d3\x18\x92x8\x8c\x94/\xacy\xf8\xbe\xb7\xfb\xbb|B\x9bM-\xcc\x01\xce\x96\x16\x0e\x0f\x97G\xb03\x07\xef\x90g\xd9\x0c\xe3\x1f\xf0\x0f\x7f6\"r\x84Es\x08 \x8e6\xb7t\x85Hs\xb8\x85\x12\xf1\xd6\x1b\x1a\xde\x83\xdd\x9f\xef\xbe\x9dq\xdd\xcd N\x0d\xae\xfd\x08\x88+\x02\x04\xbb\xc0\xad\x939x\xdc\xc7 F\x07\xc8\x11\x02 ob0\xf3&\xc1\xbdD$\xb8\x18\x0cx&\x03\x96\xd1^\xb7/\x88\xbb\x17\xa7+\x181\x00\x9c\xeb\x17\xed\xf9\xf6\xe8\xd1\xc8*gX7\x07\xcc\xbap\xefg\xa73{\xfc\x880*`\xaa\x00p\x0e\xf60p\x0e\xda`p\xd1\\\x93 \xc5!Z\x1a\xd1\xd2h Z\x1a\x07\x95L\xd1\xd2\x88\x96\x86\x06P\x8c\x8e\x96\x06`X\x15-\x8d_\xd8\xd2p\xf9\xc1\xc4Wv\xc6\xd8\xb7\xa25\x1d\x8d\xc3(\xfc\x87|\xd3\xc15\x9f\xa3\x12\xd7\x86#\xb9.\xcc\xe9k\x1cpIl\x02]\x98\x9b\xcb\x91\xd6\xc6a\xa4\x1d\xeaHt\xe3\x80\xd0@\xae~\xc0\x91\xfa\xc6\x01\xd1\x11 ;\x03L2\x1c\x07\xdf\x948\xd1\nG+x\xd0\x0b\xb8$9\x0e\x08\x19\xd9\x05\xb9\xc1\x9c s\x1c\x9cis\x1c\x0eI\x84k\xcb\xf7\x01\x9fN\xe7D\xd5\xa6\xdb\x05&\xd5q\xf0M\xads\"\xe4#\xf0M\xb0\xe3\xe0\x9bf\xc7\xc1\x9dl\xc7\x01\xbd\x10\\1\xea\x1c\xd0\xe8\\:\xa9\x0b\xf6T<\xf1\x8d\x7f\xc7\xb8PAdr\x1e\x87\x03\xc9\x16\x8c\xfd\x0d~L\x00\xb7)\xa6\x82\xc7\xc1G\x82\x077\xc0\x93#\x80=\x10I\xc0\x18\xc0}\xc0\xafP \xd8\x89\x02\xff\xc9\x02\xdf \xf3:@\xf5\x9a\xd8\xc3\xe59\xe0S\x009x\x8c\xd7c\x9c\xfeI\x81\x1c\x10\xa9\x81\x1c\x0eA6:\xd1\x8e\xc3!H\x986}\x90\x83_\x12!\x87C\x8c\x0d\x9bV\xc8\xe1\x10\x14\xb8\x13\x0d9\x1c\xa2o\x8f\xd4C\x0e\x87 \x02\x99\x8c\xc8\xe1\x10\x04\xf8\xa5'r\xc0')r\x98\x9e\xee\x89\xd3\x169\xd8\x92\x179 \x155VA\xff\x82\xa6\xa2#\xc1\x91\x83\x8f-\xe0v\x8aI\x886b\xb4\x11]_\x83\xc7D\x81\xffd\x81\xef\x84\x1d\xdaF\xc4$Vr\xe0#\xb5\xa7Wrp&Yr\xf0ZX~\xcb\xca+\xed\x92\x83\xf7D\xe2R09\x84&br\x08L\xc7\xe4\x10\x98\x94\xc9\xc1?5\x93\xc3\xa8\x04M\x0e\x98 \xff.L\x95\xac\xc9\xc1+e\x93\x03\xfaRI\x05\xefu\xe7->\x90\xa9\x9c\x1c\x82\xc9\xc1\xf9j88o\xa9T\x08\xa6 \xc5\"_g\xe8\x94I\x9f]|6o\xa1\x0f\x8d^i\xa0VL\xda\x17,-\xc9\xa0\x1c\x1c)\xa1\x1c\x10\xb2\x1f#\xf1]I\xa2\x1cp\x9a\x0b\xa1\xb5\x10Ts\xc0\xd0\xce\x01-+\xbc6\x81\xc7\x06p\xa6\xa8I@\x0f\x1e\xbc\x18\x00\xee\xe4\x13\x15\xbc\x18\x01~\xcc\x00L\x9a\x8a\n\x87%\xc7G\x0b\xfad\xbd \xd0\xb9\xf2bTp]\xaf\xab\xe0\xc54\x1f\x03\x1e\x95O\xa3\xc2\x01h\xb1'\xb0r@n'\xecFB\xca\x11\x8f\xc1\xa2\xd7)R\x82 \x07\x0c\x1e\x83\x06?\xd9\xe11x\xf0a\x00\xf8J\x8d\xc3\x11\x82\x97\x17\x93J\x0b\x1fY\xe1#)<\x18\x85\xdb\x99\x10\"#&\xa7\x02\x17\x91\xd4|\x8d\xeb\xde\xde\xb5%\xc5\x96\x83\xa3\x17;vw\xd2-\x07\xbb%f\xb1\xc0\x9c\xc2\xc3-2\x9c\x12\x12\xc5g\xc4^t\xcaC\xe7`\x005 \xc0J?\xd4\xc0\x0078\xc0\xcb\xbai\xbb\xc5H6\x1f\x99\x16\x93\xfem}\xe2$\xd4(\x99\xf1K\xc64\x06f\x19\x0f\xf0t\"S\x83s\x8d\x7f\xecx\xe2\x07\x92a(\x05\x06\x91w\xae\x18;\xad\xb4\xd1K\x17c\x85\x7f\xcdD\xc8)\x18\x04\x9c\x0d\xe2\xdb|Q\x98'\x0f\x1f\x87f\x8e3\xeb\x0fw\x18q\xe6\x1b[\xa6\xc4\x8e\xa9\x8c\xd0G\x91\xb9\xe2\xc5\x86\x91a\x1a\x16\xf65\xb9\x96\xcb\xba\x1d\xa8\xc6jY\x9b\xfd\xff\xd9\xfb\xd7\xe68\x8e$M\x14\xfe\xde\xbf\xc2\x97\xefkCr\x06*\xae43\xc7\xecpWm\x0b\x91\x94\x84\x1e\x89\xc4\x92\xa0z\xc7\xda\xda\x80DU\x00\xc8aVf)3\x0b$f\xa7\xff\xfb\xb1\xb8e\xc6\xc5\xe3\x96\x11\x85V\xcfT~\x91\x88\xca\xf4\xb8yxxx\xf8\xf3\x849\xbd\x1c\x99V \x1af\x1f\xad\x04\xb5\xc3X\x15\xbc'X\xce\x95\xd5\xbd\x9a:\xce\x9f\xb8\xa4\x88[],\xcb\x86\x9f\x1ey\x0d\x9as\x01\xf4\x9e\xfd\xa0\xa7<\xee\x9c\x1f\xefHk%\x873v\x90\xdc\x9cX\xf1\xce\xec\x93X\x01yy1\xfe\x0c\x98\xd8:\xb8\xf2Wb\xbf\xb7\xb3Ob\xbf\xf4\xe4\x8e\xc4\x8apd~\xc4~\xee\xcf\xdbpgh\xc4\xc9\xcf\xcc\xaf\xb0VW\xb6\xd2\x7f7\xafk\x11+\xac#q?\xc5\xca\x951M\xce$\xfb\xd0\x92?\xbd\xe7\xdbh\xf87\x18\x9ed\xf9\xa0{\xe9t\x0d\xe4\xe3L\x81\xcf\x15\x1d\xf2\xfb2]\x08D^\\\xf2z)\x97B>\xc1\x04\xf5\x90\x8b!\x1fw\x12\xbag(\x8a\xdfF\x88\xa7\x8fG\x89s\xed\x00\x02I\xe1\x193\xc3\x9d\x1d\x12\xad\xbd\x8e\x8dnTr\x8e\xb7\xe6\x10\xac=\xb8]\x9e\xe9g\xaf\xeb#\x9f\x98=\xab?\x91&\xd8]\x10\xee\xb2\xc8\xf4\x18oBL8M:J\x13\xd1\x1a\x86])\xf9x\xd2\x9d\x97\x16\x1fL\xf4]*8\xcf\x05\x93O\\2\xf2\xd2:\x86R\x8c\x97\xcau'\x0e/\x95\x18\x91\x0e\xbcTt \xc9w\xa9\xd8\xb8\xd4\xddp\xb2\xee\xb2\xf23]D\xf9`I\xb7\x0b\x9c\xb4\xe2\x8b\xe1\xf1j\xde\xe3*h>\x8f\xb0\n\xfa\x12Ay\x1d\xf1c\x1b\xe7\xa1M`\x88B\x03\x14\x95\xc2\x19\xd1y!>\x9f\xb4\x0c\xc5\xc4T\xcc\xc4\xe4\xcb\xf8t\xcbE \x96\xda\xf8\x073%\xe3r \xa3\xb3\x1e#\x86*B\xcb\x1f\x8b!$\x98\xa1\x98P\x92\xa39\xda2\x96\x94R\x88&\x0c\xfa\xb7\xbeII\x81H\xe2\x9f!\x0eM\x03t$\xfe\xa1v\x00\x9f\xfdG\xc6f\xf5\xcb\x80\x81<26\xff\xa7cl\xc6\xf3\xc6\x168\xa5\x85=n\x8f\x1ag\xb8\x90^\x05\x0ejV@\xaf\x02\xaa\x9b'\xbe\xbc\xd2\xfaU6j\xd40u\x8dP\xd6E\xb2C\xe9\x01N\xa1\xa6@<^\x8f\x9d\xcc\xf3\x02,u\xb3U\xcc\xc3\xed\x93\xb0\x14y\xd8{\x16\xcc\xc7\x03\xdcv\xefe\xe0\x89= \x80P\x1d X\x0f\x083\xeb\x04'\x1b(\x9a\xe1%\xb0\xf1r\xe7\x94*&|\x88\x00\xcb\x0f\x12|=\x18y\x98\x009\x07\n\x0ey\xd1\xac7\xb1\x07\x0b\x10d\xb8 \x0c\x97\x0f\xa2\x1c\x1c\xe9\xd0\xb6\xdc\xcdW\x13-\xda\xb7\x7f\x88`\xa4)0\xeb\x1e#\xae\x10\x19\x8e\x81\x98\x16AT\xab\x00\x82\xa1\x19\x98J\x0b\x85g B\x17\xe4\x13F\xfdFu)\xc4ukd\xc8Fy\xd1\x8d\xe6\x0d\x1f`@L\xed\x83\xb5\x8e?\xcc\x800\x7fKnu\x82\x07\x07P\xa0\x902\x07\x1d\x10}\xd8\x01\x05\xea\x1c:\xf8\x80\x02e\xf8\xd9Sr\xa5G\x1c\x88@\x81b\"\x18Pr\x8b\x88;(\x81\xa8\xc3\x12\xc8\xaeO\xa1\x83\x13\xf00\x96d8\xb4\x07]\xf0=\xac#\xe1\x95D\x11\x8e2\x8c\x1cWz\xe4 \xb7\n\x8e+=\xfe\xa2{\xa5\x0f\xb1u\xf0z\xffu/\xc5\x8d:\xb8\x81\xf8N\x0e\xf3l\xa4\x1e\xe2`\xdfDpj$\x1e\xe6`\x9f\xf8\xf93\x16\x1d\xea\x00\xc4ac\xa2\x192\xb4s\x9f\x90\xf2;\x7f\x8f>\x17\x82xM\x88\x9cl\x11|\x17\x89\x05\x86\xb9-\x82\xe7E\x90^\xaa\xa7\x99\x85\xcf\x8e,\x91\x8e\xd4\xc9\xe5gH\x98-\xf3\xd1IxH$\x9cf\xca\x93H\xeb\x01%\xfaL\xe6\xa1\xce\xb6\x1f\xe1\xa84\x00B,d\xeb#\xe0\x87\x11\x8d\x81\x98\x06A8\xa8\xcf\x9fr\x05\x86\xcdj\xf2 \x80SR\x00n\x18:\xd2\x82\xb8\x86\x87=\xa4\x88\x13\x03\xfed\x96\x86\x1fuAH5\xfdJy\xc0\xbdS`F\x05\xe7Sx6\x05\xe7R\xb0\x01\x10n\x04\xc4\xcd\xa22E\x85\xe6O\xa9\xd9\xe3\x9d;\xe1\x99\x13llh\xd6D\xce\x99\xacrB'n\x10*\x00\x13\xee \x05@\xe5`\xdf\xfba\xff\xd8\xca\x9a{\xfb\xadc\x86{Z\xee\xd4R\xc7|\xf6\xccd\xdf\x1c\xf6\xcc\xde\xc0\xb8{\xa7\x91w\xae.\x17\\\xfa\x00\xdb\xa7\x97\x11cc+|pJ%J\xf5M\x9fhuG\xb6\x16\xdeo\xf5m\x03\xee`/D\xa2\xdb\xf7]/\xc6\x9f\x9fW\xfd\xf8A\x86%x\xcb\xac9`k\xbe\x11\xb8\xe1_\x98A\x1a\xac\x13\x97\x80\x0e\x1d\x01\x14\xab)\xfc\xd6\xc1\xc4\x96d\xe0\xfc\xd03\xf8\x98\xb3w\xa7\x91q\x9b\x18\xc7\x19{\x94\xcdG\x0f\xbb\xd1\xb3\xf4\xa5\xe2B\xd9\x87\x8b\xce\xca\xa7\xde3\xc4\x05\xce\xc8\x17\x9f\x8d#`;\xef\x99x\xccY8~\x06\xee\xe8f,\x04\xee\x1c\x11\x97\xf1\xb4\xcf\xb6\x83\"\xb0\xf8\x86\xe7\x0c{\x81\xf6\x96\xc5\x9f\x04#\xd5^w\xdc\xef\x8a{\"\xd2\xb8\x91S\x9f\x90\xa7x\xa8\x93\xa6`\x84\xd9\x19Y\xf6\x9f\x1d\x075\xc7\xaaM\xdc\x19\xb1\xe3l8\xb58\xefyj\xaa\xb0\xfc\xb3\xde\xf0\x19oj\x9d|g\xb9\xa9\xb2\xf03\xdbT)\x81\xb3\xd9Tq\x9e3\xd8TQ\xe1\xb3V\xff\x19kZy\x05\xceR\xcd3\xd4\x04\xc7\xa3\xc8B\x81\x9c\x89\xba-\x9c\"D;\x03=\xae\x10\xdaO\xff W\x08\xd7\x99#\xaf\xcfaA\x19\xc1\xb3\xc4@\xa7\xb8\xcf\x0eS\xce\x0c\x13\xce\n\x13\xce\x08\xe3\xce\x06\x93\xcf\x04K\x83\xbc\xa2\x0e\xf2\x02\xc3\x10\xd0\xcc\xc3\xf1\xc0\x05\x0e\xe6\"\xa5#\xd5.x\x00\xe7\xa3;[~\xe0f3\xd6\xe9\x07m\x8e\xbd\xb3\xb8\xb1\x9fw\x96\xd5=\xeat\xb2o\xce\xd7u\xd2y'\xbf\xefwm\xba\xb8\xef\xe6\xff\x1d\xa83\xc3W\x0f\xads\xffCv\xae\xd6V>h\xb4\xadt\xd0x\xde?l\xc90T\xb7Dfd+\xb7K\xcb\x1eu\xb6\xef%\x1b\xf4\xc1\xf7\x1eo\xc5K8g\x0eI\xd5\x0c\xd8\x80L\xa16u0\xbc\x91\x8c\x85\x94KH(4\xc1\x15A\x03\x9fA_\xc4\x98R\x8e0g\x9a\x98rA\xcd\xcc8\xa1'x\x19%\xc3\xad\n\x1f\xe4ax\x846\x1cC\xf2\xc7\x90\xfco9$o\x9f\x00'X\x9d\x02;8\x87\xba9\x95\xcd\xadjNE\xf3j\x83G\x17<*\xb6LdY\xe5*\x80\xfb\x93O@\xa9\x92\xe4e\x9f\xf1\xd8v\xb7\x1b\xe3q\x87\xe3\xe479\x0b4\xb7#!\x1f\n\x7fG\xf7\xa3\x9c\xefX[\x8f\x80?\x05\xb1>\x158\xfd*(\xe4[9\xdb\x84\xf8W\xaew1\x1f\x0brxs\x8dp \xff\xces\xe25\x85I\xec\x10I\x82\xa1\xcb\x8f\x8b\x1d s\x8d\x1f\xf5\x10G\xde\xa1\xaew\x7f\xec\x95\xa3T}\x16Q\xb7\x1b\xf2e\x89\x8e!\xdb\xdc\xb8\xd2\xf1\x89\xcc\xb6\xad=\xd9\xf5d \xed\xc86\xb5=\xb9\xefFrB\xff\x87\xef4O\xa0\xeb\xc5\xa6\x93\xae\x08\x84\x07\xc7\x15F\xf49\x9a\xa0Lq\xcd\xca\x8a\xf3\xd3\xd5t>\x18mlQ`\xb7\xa3\xad\xdaq\xa5v\xea\x19\xfb\x19\xbeE\xcf=\xd5T\x8e\x1b\x91\xd3\xcc\x12\xa7\x98\xe8\xe9\xa5\xef\xd4r\xdd\x0d\xdbnX]W\x03Y\xdd\x7f}M\xc6\xea\xeb\xd5k\xb2~\xd5\xd5m\xf4\xd0lH\xdbm\xbd}\\m\xbb}\xeb3\xc1\xb8R\x8a\x8aL\xbeK\x05c\xf7\x89\xb4\xdcQ\xa9x\xb9u\xcbZ\xcb:\x85\xfei]o\xabF\x148\xad/o\xd9*rqG\xc4\x0fpS\x93f\xc3\xd6\xaa\x96\x96\"\xc2d\xf5v\xd7\x90-\xd3\x7f6\xae\xfba\xec\xb6\xb0%\xe3]\xb71\xa7\xdd\x00=\xf9u_\xf7<\xfar\xdb\xddv\xbb\xbe\x1b;\xa5O75m\xe0\xf5\x9eVO\xe9\xdb\x86\xdc\xb2\x1a\x8b\xff\xeb\xfa\xf7\xe4s\xd5o\xa2{;\xcd\xfc\xf4\x8a\xf0\xf9\x95\xb2\xfbAc\xfcU\x19\x8857u\xc1\xfb\xba\xdb\xeb(\xa8\x1d\xfc)\xa7#\xfc\x89\xd1\x14\xb7\xe2;\xb4D5\xd1\xfcTR\x0e\x89\xf8\x92\x9d=m\xe4GO\x07\xf9\xff\xb4\x13\xb8.\x844\xf4\xbc\xea\xabm\xbc]\xa6\xeb\xc1\xbe\xad\xc7\x87\xcb\xb1\xb2\x162m<\xa9\x8d\xb9\x9c\xdc\x7f\\1\xf5\x0f\xbav?$}A\x87~\xd3W\x9f\xd9\xd4\xb8$-\xdd\xdeX_\\w]C\xaa\xd6\xee~\xder\xfdP\x8f\xe7^\xed\xf8/\xd4\xb0\xd3\xbf\xaa\xfd\x06\xdbn\xb3oH\xa8W\xff\xf7\x9e\xf4\x0f\xafd_\x9dw]\xf3\x9e\x0c;\xbapD\xf7\xf4\xae\xeb\xac\xfc\xab\xe3D\xfe\xadLd\xab\x91t\xb8\xa6&M\xb3\x84\xfd\xf9)\xfdC\xdd\x0e\x88 \x98|\x0d\xb7\xc2\xd0\x96\xb0\xe5_\xfe\x9bv\xb9\xd4M\xf5\xeb\x17\xda\xe7\xb2\x93\xde\x9f\xbf\x12\xad\x8c\xd2\xd9\xd9\x14q\x0b4$\xeb-\x9f\xb7\x07\x8eI\x1eU\xb7\xa0\xea\x8a\x11\xd3,\xa1\xfc[\xb5^\xf7{y\xe44/.\xbe\xd5\xcc\xafG\xf1\xeal\x89X\xae\xcb\x17tK\xfb\x9bV\xe8\xe0)\xacS\xb9\xcduR}\x1dC\xbd90o\x8e\xfa\xf2\xc7\x9f/\x81NF\xfe8k\xcd\x1f|bF|\x1a\x063.\x9e\xaa\xcej\xae~\x87q&\xe5\xccaD\x9c\x98\xd5\xc8/\xf1\x93;d\xc3\x96:\x9b\xfc\x89s9\x91z\x98f\x86n{C\xa6\xa6\x9b\x92\xa9\xd0|\xf4\xb2s\xf0\xb8\xa8\x14\\T\xd8x\xe9\xce\xf5~\xcb\x94G\x1f\xf6\x84\x95\x04\xb3\xe2K\x96\x13U\xce\xc25\xa5\xeb\xe7\x83\x93\xe4\x05%\xf3\xe0\xd6P.\xad\xeb\x94\xe8\x9c\xda\xf5\xea5\x86\xf3\xd4\xa2}''o{K;-v0\xb0\xe6'\x8f\x84*d\xf10\xfcQl\x01O\xf9\xaa\x99<\x16\xda\x162\x14]1\xba\xc6\xfcV\xeb\xf2\xb9\x97\xe5\xafc\x07\xbf\xd2\xba\xa7u\xb4\xa3\x81\x0bz\xdb\x90\x94\xda\xe5|\xbb\x9c\xdc\xc1;%\xbe`\xb5ygo\xc1\xd9\x9f\xc8Hz\x19\xeaTv\xdc\xea\xc0D\x1d\xae8\xa2\x15\xe06\xd4\xfe\xa8\x85\xefC\x7f\xf4\xc2\xf3e \x8a1\x7f\xe9\x8cd \xe3\xe3\xd5\x0f\xa1\x19\"\x00\x92\xa8\x07\xd3\xa4e\xd9k\xc3\xc0\xfc\xf3D\xa5XO\x9f:\x14c~AS\x0e\xfeg\xfaW\xcd\xaaAO\xd6\xa4\xbe'\x9b\xc5JbV\x06<.t\xc9\xfcW\xa7\xeb\xec\xf5~\xddn\xb3\xe7\xb3\x90\xcb\xbc\xd8\x0dqT\x0fs\x97s|\x13K\x98\xcbU\x8esX\xc4Y\xa1nu=\xba\x1dop\x11!\xca\x1cK\x9ab\xef\xf6\xe30V\xed\xa6no\x0b\xef^\xa3f\x86\xf5-\x1c\xa7\xc5\x7f\xf2i\x01\xde.\xf1(\xa6\xba{\xec\xe6_\xe1\xd9\xbe\xfdJ\xaep\xeds[\xb9\xd4\x8a\xd2\xd9T)f\xbdn\xc9\x97\x1di\x87\xfa\x9eP\xefi\xec\xab\xf5\xa7\x13\xba\x81\xe8>\x0f0\xb0\xee\x80\xa1bq\xd6\xf5\x1dY\x7f\no&\"f\xd6\x82\x89\x8e\xf4\xc6\xd2E\xf5C\xc3\xc0i\xc9\xd3|\xe0\xdf\x99\xd3\x9b\xfb\xeb\xae\x81\xeb\xfd\xcd\x0d\xe9%\x14c\xc5\x95\x83\xd7\x1d\xb6\xfba\x02\x1d\xd3%\xa3!\xd50\xda\xb2\xba\x96\xc0\x93\x17O`}WQ\xfd'\xfd\x8a\xd9\xaf\xa6\x1aF\x18\xc8-\xb5R2D\xff\xf1\xfdOO\x07\xd8U\xe3\x1d\x13n\x89\x9at\xde.\x85~~\xb3o\x9a\x07\xf8u_5\xb4\x076\xbc\x7f\x84h\xd6\x13\xcf\xaa\x01\xea\xd6\xfe\xf8\x8a\x16\xf9\xe2\xb6\xebn\x1b\xb2bm\xbf\xde\xdf\xac^\xef{\xa6\xbbW\xcfy\x8d\x99\xb8\xe1\xae\xdb7\x1b\xba\\\xd2F[\x92\xd6U\xdb\xb5\xf5\xbaj\x98\x05\xb0KzFV\xb7\xab\x13\xdaU\xcc\x1e>Y=aS\xad\x1b\xe9bNv#\xd9<\xc7\";g-\xec\x98\xf1X\x93\x13\x18I\xb5\x1d`?\xec+\xda\\\x9eg\xbf\xab\xa9\xe3\xdcR3yG\xe0\xban\xab\xfe\x81\x1f\x91>\xec\x88Mw\xc9\x94f\xbc#\x0fvQ\xd4\xe6\xaeG\xa8G:\xf3\xf7\x83\n\xbf\x19\xe9.\xa4\xbb\x81\xd3\xf6a\x05?v\x9f\xc9=\xf5\x1e\xe8D\xff\xf8\xfe'1\xc3-yT\x04U?[\xff\xd6wdK\xe0\xean\x1cwW'\xfc\xbf\xc3\x15C\x0b\xb4\x9d\xf8\xf5\x84i\x0f\xf5O:6+X\x8b\x072\xc2~g\xc9\xe3\xf8!\xa4\x1c\xd2\xdf\x93\x9e7y[\xed\x06\xae\n\xac\xc6c7\x81\x8c\xd8\xfa\\sSU\x0dp\xd3\xb1e\xe6%2\x16\x7f\x0fg7s\x0d\xe9\xf0\xed\xfa\x8eZ\x94\xcd\xd4\x08\xb6\x1e\x0f\xc3~K\x0d\x19\"\xe0\xb4\x85\x1f/.\xce\xe1\x877\x17 \x0e!>\xbe\xff\x89O\xa8\x07\xb6\xa0W\xf0'S\x1d/\x1ev\xe4\xcf\x7f\xfa\xb3%\x0e\xe4^\xa7\x95\xe3\xce\x17\x1f\xd6\x93\xbb\xbe\xdb\xec\xd7\x84z\x07\xa4\xef;\xeb~\x0eV\x9b9\xfb\x7f`\x06\x9a-\xb1\xd2\xf4\xaf\xe9\\\xed\xbaO\xfb\xdd\xb4U\xbb\xae\xe8\x0e\xb6kQ\xb3\x02\xb4)\xac\xec\xbb\xea\x9e\x0d\xfdV\xd1\xd1\x0dW\xd2JV\x95\xfe\xff}Wo\xa8\xbf\x89\x88\xe2\x05\xb3\xe9\xd7\x93\x9b\xae''\xf2C*\xaf\x1a\xeb\xeb\xba\xa1\xeb?]\xab\x06\xb9\xc1\xa6&\xa2\xbf'\x1bD^\xd7R3\xd4\xde\x12\xf62\x9b\x1b+x\xf6q \x92\xa2\x8f\xb6\x9a\xaa\x07\x9d\xeb\\?\xaa\xb6\xba\xc5Zy\xdd\x13\xbe\xea \x81\xab\xe7H@\xb6\x1b\xc9K\x18\xa9\xcd\xbc\xd9\xb7k\xae\xc1\xb4\xbeb\xce\xb3\xb5\x8e\xee\xe2\xd5\xdd/\xde\xad\x1d\xdb\xf2\xdb\x9b^a\xab\xaf\xf7t'M-09a~d=\xcaB\xf6t\xb0\xd8&u\xd2\xfbkr[\xb3=\x80%\x8c\xb1\xe7\xda\xe6\xe2aGV\\\x1f\xab]=\xac\xd6\xdd\x16\xb3R\x1f\xd8\x8c\x18\xf8F\x9bN\xb8\xd6\x9c\xdd\xf0L\x9c\x1c\xf2H\x05\x9fB\xcfaK\x1d\x14K\xdc52\x99Yc\x98\xef<\xb9\xb8\xdcw\x1evd]\xdf\xd4k\x18\xc8\xb6j\xc7z=\xe8\xaa\xce\xe6H\xc2R\xecA\xdd\x86V\xe9\x9f\xe94\xbe&\xd2-S\x16Zk]\x15\x8bSu\xdd\xdd#\x0b4o\x92PI\xb59\xbe\x1a\\\x9d\xb6\x0fW3\x17H\xd5B\xd5_\xd7cO'\x8d\xa7&\xd2\x0eVMg\xf4\x05\xdf\xa9\xe8CA\xad\x153\xa8\xbc&\xd7\xb6\xbb\xa1\x96%\xbd\x07Ce\xce\xa5\xe26\xf55\xab\x9e\xb0\xa3\x03\x0c\xfb\xdd\xae\xeb\xd9\n\xb4\xab\xd6\x9f^\xec[\xfa\x1f\xba\xee\xf0q\x1c\xb0Yb/\xb8\xdd\x0d\xecGn \xe4\xf4\x1b\x80gR\xd4|.\xc2-iI\xcf6\xd0|s4e\x0b\x9c\x1a\xf6\x88\x0f\x81.\xff\xcd\x97\x8amB\xbe~ \xe7\xb4~t\xde\x89\xaaV*z\xf5\xd5?\xfc\x03\xb2\x0c|\xdfup\xd3u\xf0-\xacV\xab\xffa\xfdL\x1b[\xb5\x0f\xf6\x0fU\xfb\xb0\xa2\xc5}\xdfw\xdbg7]\xf7\xdc~e\xb5\xb2\xed|}\x03\xcf\xe8\xa7\x1fY\x05/\xbag\x7fG\xbf}\x0e\xff\x17\xb1m\xd8\xf7\x7f\xc1\xdb\xfeM\xa0\xed\x7f\xa8\xee\xab\xc5\x8d\x87o\x99\xafA\xa5.hi=<\xfb\xbe\xebV\xeb\xa6\x1a\x06GCy\x15\xe8\xcb\xbc\xee\xca\x07vYF\x0fL]\xf0\x8f\x81.8\x7f\x18\xef\xba\x16\xe9\x04^\xfa\xf7]\xf7l\xb5Z=\xc7\x06\x9aw\xc03\xf47\xa6\x04\xac[b{\x85~t\xc6;\xe5\xf5\x9b\x0f\xaf\xde\x9f\x9d_\xbc{\xff\xdc4\x8a \xc4sE\xc1\x0b\xe0E\xe0\xdd\xf1O\x81\xee\xf8\xa1\xb3{\x82u\xc5\xcbo\xe1\xefv\xd7\xab\xef\xbb\xee\xff\xaeV\xab\xbf\xd8/U\xed\xc3 uc\xe8\x9b;\xbex\xff\\\xf5\xc3]\xd5\xd0N\xc2+\x8au\x85Y\x1aRT}c\x14\xf4\xb1\xdd\xceE\xb1\x8a0\x85do\xfd\xb7o\xa1\xad\x1bT\xc1\xf0\xf2\x0dM\xba`\xa7\x1c\xebO\x93\x0d\x92\x0e%\\?\xcc\xcb\xbb\xb4\x92<\xd8\xff 1\xf8tI\xd4\xc5=E\x96\xeb\x17to\xc4\xd8\nV\xd4\xb5yJ}\xdc\xc9bSk.\xd1\xa1|\xc4t\x81\x93il\x9b\x07\xe9\xcf[\x9b\xad\xc9m\x12\xbb\xfaQ\xee\xf1\x9e\xbex\xaa\x8b\x13\x1b\nY4\xdfA\x10\xa1=On\xbanu]\xf5\xac\xd2_^<\xac\xfe\xfd o1\xf7\x8bm\x17\x9f\x15\xf9\x84\xbeG\xcd\xb3\xf6\xd3\x1f>\xbc{\xab\xff\xe5\xdbo\xbf\xfd\xd6\xee{\xfa\xde\xbc\xb7\xe4\xfeDG\xa7\x8bXL\xb9\x7f\xbd\x1f&L\xc3\xed\xbe\xa9z]\x8e\xfd\xf9\xc8\xb2\xf7\xe6e\xf0\x04\xc8\xf6\x9al6\xf3\x82x\"\xd6VcG\xaa,O<\xbaw\xf5\xbfh\xb3\xafD\x08E\xa3\xf9\x92\x9d\xb8\x92\xd3\xef%\xe2 V\xebOt\xee\xcd\x1b\x8a\x9b\xba!\xb6}\x93s\xf4\x9c\xf4C\xd7\xa2\xea,v\xfe7u?\x8c\x97\xac\xe7\xbf\x85\xafmI\xd3\x8b\x8c\x05T\xbc\xf7M\xd8\xa2\x02\xa0\xa5>a\xed\x7f\xf2\x12\x9e`\x9a\xad7k\xc5k\xff\xe4\x04\x93\xc3\xea\xfd\xb6\xdaRY\xff\x93W\xf1\xf7\xe8\x8b\xb4\xde\xc6{\xa1\xca\x9f\xdd\x08\xc7V\x1fc>B\xf5\x00\x9fI\xd3|\xf5\xa9\xed>\xf38\xef\x1d\x0b\xc5\x8b\xc0\xac\xad\xa8\xba:\x9dpg\xcb\xd0\xb1\xf9\xd4O\x14I\x15\x87!\xc7\x99\xda\xe8\x02\xaf\x98\x12K\x1d\xba\xeb\x9a\x8d\x16\x1afS\xa0n'\xdd\x03\x11I\x10\xaa\xa7\xcbb\xe2'\x8d\x83gt\xfe\xca\xe6Z\xdbV\x19E\xf9\xf3\x9f\xfe\xfc\x1cQ\xce\x9c\xf1\xd6\x0b\xc0\x87\x9c5\x9b\x8a\xfaz\xf5\xcd\xd7\xdf\x0cO\x90a\x94\xff\xa7y\xd5\x13\x7f^O\xc6}\xdfr\x98\x81\xfc\xe3p<\xd3>\x9ei\xff\xd6\xcf\xb4uL(\x12/\x8f\xc9\xedU>\x13\xd2\xde\x9f\xbf\x92\x95\xb4\x0e\xb4\xf1P}\xf18}\xd4\x9c\x18\xd1\xf0\xbbs\xf8\xfd\x9b\xfa\x82\x81ww\xd8\xbd\\\xd0\xbdX\xc8\xdd\x19p\xcf\x08\xb7\x97\n\xb6\xfbC\xed\x8b\x02\xede\xc3\xec\xce {\xd9\x10\xbb#\xc0\x9e\x19^\xb7\xba\xdb\xf6\xbcK\x87\xd63\x03\xeb\x85\xc3\xea\x19A\xf5\xd2!\xf5b\x01\xf5\xb2\xe1\xf4b\xc1\xf4p(\xbdX \xdd\x15F\xcf \xa2\xa3As\xc4s\xb5\xedM^\xc0\x1c \x90/\x0c\x8f#\xc1\xf1\xa0\x9f\xe4w\x1e\xad\x15taP|\x0e\x82c\xfd\xfb\xbbp\xd9\x85\xc3\xe1v0\xbc@(\xbch \xdc\\\x0c3\x83\xe0H\xe0;'\xec\xed\x8d\xfb:B\xde\xc1\x80\xb7\x1dc\x8b\x0fv\xdb\xdf\xfe\x05k\xeb\xa20wLcC!nw\xdb\x82\xe1\xed\x84\xe0\xb6\x1e\xcb\xc8\x0cl{\xc3\xda\xee\xa0\xb6/\xa4\x8d\xf6Bl8;\x14\xcc6C\xd9\x19\x81\xec\x880vz\x10\x1b !\x87\x02\xd8\x85\xc2\xd7H\xc9\x9a\xa6\x14\x0d\\\x17\x0e[\x17\x0dZ\x97\x0cY;\x03\xd6f\x14\xd0\x0cV\x97 U\x17\x0bT\x97\x0dS\xc7\x05\xa9\x83!\xea\xc8\x00uLx\xda\nN\xdb\xa5\xc5\x06*\xfd\x81\xe9\xc8\xb0tDPZ\xabr\xc9\x80t\xe1pt\xb9`t\xb9P\xf4\xf2\xd1\x0d\x86\xa1CAhn\xbe=\xc1\xbb%\x91\xbb\xe9~\xd8\xf7\xe7\xaf\x84,+^w\xdb\xdd+\x14\xa3\xbbn\xa8\xe3\x13\xa2w\x82/\x1b!\xac\x8e\xc9\x85\xde\xf0\xe2:?\xf7:\xce\x00[6O\xf7\xaf\x04\x0cHC\x05\xb4\x0b\xe0\x00g\xed\xf8\x98p\x00\xa1@s\x9bd\x9d\xe5X\x0bN\xa6\x16\xaa5\x8bx\xab\x8c&\xec\xafc=eAI\xed\xf2+k\"\xe9\xe8\xb6n/7\xaa\x9a\xc3Q\xa5~#*e5\xed\xe7\xba\xad\xb7\xfb\xad\xd4\x1d\x01\x0e\x91jAU\x86\xb4\xd4c\xe3\xd78\x00\x07eHY\xdb\xea\x8b\x1c\xe88\xb8\x86;~\xf0s\xf5\x85\xd5\x83\x8ba\xd58\xa5-\xa5\x8b\x16\xe9\x99\xee\xca*\xd2\x8e\x9d\x15\x17\xce\xdaz\xac5\xfc>\x0f\xb3\x80z-\"l\xbbv\xbc\xc3\x00\xe7\x9a\x8a\xdb\xd46\x83\x00X\xb0\x97\xa8\x97\x0b\xb7\xdd=\xe9\xdb\x8a\x9a|Y\x89\xc11}\xe4]\x07\xd13'\xd3\xd8\xb3\xc0\xb6m\xc7\x8f\xe78\xc7s\x9c\xe39\x8e|\x8e\xe78\xf2\xf3\xe39\xce\xf1\x1c\xe7x\x8es<\xc79\x9e\xe3(\xff>\x9e\xe3\x1c\xcfq\x8e\xe78\xc7s\x9c\xe39\xce\xf1\x1c\x07\x8e\xe78\xdak\xc7s\x9c\xe39\x8e|\x96\x8fn\xa1s\x1c~s\xde\xde\xa232|p\xfb\x8eWy\x17\xe9\xe5\x87\x8b\xd3\x8b\x8f\x1f.?\xbe\xfdp\xfe\xe6\xd5\xd9\xf7go^{\xdf{\xfd\xe6\xfc\xdd\x87\xb3\x8b\xcb\xf37\xef\xcf\xde\xf9_\xfd\xe5\xdd\xc5\xd9\xdb\x1fb\xde\x11\xe5*\x8e\xfbn$B=\xc4\xf1\x8a\xb5\xb6_[\x89\xb9\xeb\xae\x1d\xea\x0d\xa1\n\xc9\xe2t\xda\xe2|\xd7\x93\x81\x8e\xe7#\xb5\x91jH?\x8a\x8c\x95\x7f%\xec\x94\x86p0\x8b\xaa\xef\xd4\x97Y\xc1k\x1e\xe8\xc3\xb5\xe8\xbf\xaf\xfeYm\xc8=\x19\xbb\xcbGn\x0dw\x05\xba\x1b\xf8\x85\x88\xb1as\x82]\x0c/\xfe\xc9\x92 \xcc\xe6\x05G\x8c6\x86l\xa6\x0e\x90M\xfe\xfa\xc5?\";\xe3\x03\\\xdbl\xfb\x13\xa2\xf2\xc9\x1e\x85l\xf5\xcb%\xb3\xaf`8\xcb\x82 \x81o\x8dt\xaf\x90\xa3\xe3V\x97\xec\x1dcAh\x12\x04nw) Q\x82\x920%\xf0\xdf\xf1\x92\x05W\x82\x82\x90%\x08\xc2\x96`)t r\xe0KX\x8f=\xec\x98\xbe8\xef{\xc9\x801!\xb2\xb8#\xe5\xbc\xf3%\x0f\xce\x84\x88\xdb\xef\x9c\xf7\xbe\x94\x865A>\xb4 \xca\xc3\x9b \x0f\xe2\x04y0'|\x8a\xa2\x95,\x06~\x82\xe2\x00(( \x82\x82( \x14\x94\x04C\x81\xf7~\x98\xf2\xa7\xa7x>v\xa6\xa8D\x1d\x00c\xa9\x01\xa1D\x95\x0c\xc1\x9et\x15w\xc2\xca\x91$.9\x85\xa5p\x12K)\x92\xb8`*K\x86jy\x13Z\x16\xcb=@Z\xcb\x91\x9f\xee\xc8O\xf7\xdb\xe5\xa7\xfb\x0f4\xcf*\x8dHF~\x94\x94iuA\xbd\x96\xf7\xac\x0b\x92s\xad\x98\xc7\xe3\x98K\xdc\x1bR\xa7Q\xcf;\x90\xaa\xdeb\x1e\xa8\xbf\x9d\xfb\x1f\x0d\xd1X\x1d#\xfc\x13z\x01\x9c\xf8\xef\xb6\xbe'x\x1a\x18\xea\x83\xa2o\xba\x87\xc7\xd9@e\xa7\xd2v_u;\xb5j\x8e\xa3GkT_\x9a\x7fP\x84>\x082\xc0\x14\x91B\x05^b\x7f\x04\x95~\x9a/\xca\xc9\xf2\xdf\xbe{i\xfc[\xeb\x85\x05\xf2f\x1d3%\xcf\xbf\xe8e\xb0\x0d\xe1\xbd$\x18t\x96\xc6\x0dx\xe2\x14\x0eE\x93\xff\xc8\x84\x92\x8d\xa2\xa5s\xdd\xf6m\xcd\xe2M\x13'%\xfb\x9fa\xd7\xd4\xe6\x0dG\xf4\xf9P\xb7k\xf2R\xb8\x0c_\x0d\x9bO\xf0\xdfW\xff\xf4\x8f\xba\xa5\xe3\x1e\xf4S\xfc\xd5\xa7\xd3\xbb\xeeZ\xd3z*5\xe4\x1d\xd6\xfawg\xc0\xf2\xca\xd9\x97\x8c(s\x18\xcdx\xdf\xd9\xeb\x13\x19\xa1%\xfd\xc9tW\xad5\x1c6+\xa3\xea\x08\xc5\xb8Z\xac\x16q\x8c\x8c\xf4\xd5t\x94\x02#\xa54\xbd\xac\xdf8B\x01q\xca<\xa2\xf0\xa5,d\x8a\xf3\x9c3K\\\xdd\xba\xdc\xb3\xe2\x0e\x9a\xcbE+\xec\xa4y\x07\xcfu\xce\x17\xbbT\x86W\xff\xb8\x95?\xb8\xeaG\xaf\xf8I\xab=\xea\xb6=n|\xd3\xed\xc0\x05]8\x9f\x13\x972\x88\xd8\xfb.G.v@\xb1w\x1d\xce\\\xc2\xe0\xf2'i\x88\xe7\x8f\xc2.]a\xa7.\xc9\xad{$\xc7\xee \xae\xdd\xe1\x9d\xbb\xf2\xee\xdd#:x>\x17/\xe5(\x02q\xf3\x8a:zQ\xae^\xbc\xb3\xe7\xaf\xfdR\x87\xaf\x94\xcbg\xd5\x8f\xb3~\xcb\xc0+\xfd\x86;\x02|\xd2\x0f\xc7#\x81\xe3\x91\xc0o\xfdH\xc0\xde^\xc4n]\"\xd8\xe4Y0\xf9\\\xb9\xdb!b\xd3b^\xc2\x80\x0e\x05\xaa\x9bn\xbd\xcc\xbdt\xc1 \xedw]\xb1\x80\\H\xb0\xb0\xf6\xbfGk\xdf/\xb8N\xc1\x86S)W)\xb8\xaeQXXk\xb4\xcf\x97^\x9b`\xf4y\xd2% \x8a\xde\x19\x06\x95\xfd\x89\xe92}\xa5noE\x15\xba\x16[D\x06\x9f^\xbfW\xf2{\"\xf4\xda8TB\xba\xd8:LB\xde\xd1\x0f\x91\x90\x17\xf0\xc3#\xebE\xbb\xbbxs\x94\xe5u\x18\xabvS\xf5\xe2 Mz\xae\xd1\x170\xff2\x9d\xf0Dt\x0e\x1aF\xf0j\xa1f\x80\x8d\xa0\x01\xf2\xa1\xb9er\xcf\xb6\xbc\xe0\x80~r\x83\x84\x05r\x03\x02p\xa6\x16`\x84\x02\x8a\x05\x01\xd0\xae7w\x8e1\xbbE\xdf\x0e1\xb4+\xf4\xec\x04#v\x7f\x91;>k;\x7f\x88(\x19\xbea?\xc6X\"3\x9f\x91\xadw\xb1Mw\xe4v\xfb\xe0\x1b\xed\xc2[\xecCn\xaeKn\xab\x1feC\x8do\xa5\x1d\xd3\xcf\xb7\xbb)\xb4q\x0el\x99c6\xcbx-\x97l\x90Kl\x8d\x1d\xcb\xff;\xc5\xecY\xbd\xad\x9a\xb8\x90as\x993\x9f\x11s\x98\xae\x80\xc1\x8a2S\xb8qZj\x92\x94\xc1\x11\x92lCt \xf3S\xc4\xe8\x9475\xf9\x06\xe6\x80f\x05\xd7\xf5\xba\xbdM\xdc\xe7:\xae;E\xad\x92\xa6p\xc1kN\xb5\xb7\xd5\xba\xb9\xf6BBF\xda\x1e\xc8\xb6\x85\xd1M7\xdd\xa1\xff:\xde\xa6\xcf\xb1)g?\x00\xb5!vK\x8a\xd9\x11[\xf4B[b\x0b*aOl\xa9Kl\n&\xa5\xa0]\x01\xc4Q\xf1o\xdb\xa7e?\xdb-q:$\xda\xbc_\xbdV\xa1\x1d\x11\xf3\xba\xbd\x92\xe8\x8e\xe8I\xb3\xcc\xb2\x0b\xca\x84\x03G\xddF\x07=\xad'\xee\x16\n;\x15\xa5\xa4\xf5\xd1\xd1\x96\xa4\xa2-HC\xeb\xa1\xa0\x1ds\xe8g\xcbQ\xcf\x86hg\x17R\xce.\xa6\x9be\xed\xb6\xc1\xe3\x1e\xaa\xd9\xc54\xb3\xdc\xf6Z\xf2\x9c\x14\xb39\xf4\xb2\xb0\xdfY\xf2\\\xd4\xb2Khe}\x14\xb2\xd9\xf4\xb1Q\xd4\xb1)4\xb1Y\x14\xb1\x19\xf4\xb0\xa8Y)J\x03[\x9a\x02\xb6 \xfdk\x0c\xf5kA\xdaW7\xe5kQ\xbaW\x9c\xeau\x8c\xa3y]J\xf1\xea\xb8k\x17\xa3w]L\xed\x8a\xd2\xbaz\x96b\x0f\x9dkh\x95.E\xe3\xea\xa6p\xf5\xd5 \x8f\xba\x95S\xb5j\x021\xda\xd6\"\x94\xadyt\xad\xd6,\xb1\x17\xdc\x924\xad#B\xd1\x9aG\xcf\x1a`\x1fu\xd2\xb2FP\xb2b\xfc\x8c)T\xac\xd8\xf7\x7f\xc1\xdb\xbe\x90~5\xae\xf1a\xdaU_K#\xe8V\x93\xa8VM^\xbal\x8a\xd5\x00\xbd\xaa\x8fZ\xd5O\xab\xea\xe8\x95x:\xd50\x95\xaaM\xa3\x9aE\xa1\x1aE\x9f\xba\x84:\x15\xa5*\x0dS\xa6\x16\xa3KE\xcb74)\x8b\"\xd5\xa6D\xcd\xa1CE\xe9O\xb3\xa8Om\xaa\xd3\xb24\xa7\x1e\x8aS\x9b\xf9\xd1\xa66-EkZ\x90\xd2\xb44\x9di,\x95i\x04\x8d\xa9\xd8\xf9\x87)L\xc5\x8b\x01\xfaR\x84\xe9\x13+5\x96\x8e2DY\x1aMW\x1aEUjT\xbe,Ei\x16=)FGZ\x92\x8a\xb4$\x0di\xcexG\xd0\x8f\x86\xa9G\xa5\xf17 G\xd1\xbd\x81}*\x1c\xc7\xa6\x19\xa2\x9a\xf4\xbe\xea\xa2\x14\x8d \x13\x8d\xa2\x11\x0d\x12\x88&Q\x87\xba\xcf\x00J\xd1\x85>\"Qhh\xdc\x0eE\x11\x8a\x10q\xfeUhA\xa3\xeaq8*P\x84\x04\xf4\xb1\xe9?\xdd\xc4\x9f\x8fI\xf9\xa9\x93}\xfah>\xdd\xd3/\x8b\xda\x93Sy\xce\xc229<\x8d\xcd\xbb\x14\x11C\xd69b\xa70\xf8\x19\xcc\x03\x19.\xd7\xf6\xf1\xa5#\xea#R8\x12\xbeh\xbb\xb4\x97'dD\xe4g(\xfd&*\xdeE3\xe8&\xdbL\x12\xe3\xa0\xd6\x1c\x0fpjv<\x9d\x9e\x9f\xd8\xd3i\x0f\xf1e\xd20;i.\x93\xa4l\xc9Xm\xaa\xb1J9\xd5\x96\xdf\xf0\xbezPB\x96\xd3/\xd58V\xeb;~*1Z\xac\xc3\x9a\xb4\x12\\\x9a\xf8\xc1\xf4\x07\xc5I\xb4Z\xa5\xa7\x14\x87}\xa5\x90s\xe1|\x0ds\x08\x03\xce`\xd0\x11\xf4:\x81\xd1\x0e >\xc5J8~\x8f\xe4\xf4\x85\xc6\xe4\x10\x0e\x9f\xe1d=\xba\xa3\x17,\xff0\x0e\x9e\xe1\xdc=\xa6c\x87;u\x8f\xe5\xd0\xcd\xce\x9cnd\x18\xb6Yd\xd6$\x93-9\x16\xe7(?IV\xb4\x00\xa9%\x924\xe9\x11\x84-\xdf\x98S\x01\x85)\x08\x8fW\xcb\x87X\xb9\xf3\xaf\x96w{[E\xd30\xc1H\xc5t\xf586\xb9b\xa8\x06de\x83\x94\xb4\xda\xf4M'K\x93\xc9r\x87\xf5\xae\x9d3=\xea,\x1ca\x9dp\xcc\xf7\x85.\xfb\xe3\xf0W\x1d\xf9\xf9S,A)~~_\x17dZ\x04]\xbf\xb8u@\xd5^\xd6\xe7\xc8\x88sd\xc4\xf9\xad2\xe2\xa0+V\x12;\x8e\xfc&v\xcd\xe2Y\xdc\xc9+\x96D\x10*0\x07\xab1\xda;v\x9e8\x19\xe96\xbc'<28v\xe2\xfdE\x93\xc9\x01h\x04\x9f\xf2\xa4\x01\x1b\xe7p\x9a\xb7\xcd\xfaK\x11\x8d\x16\x1f,j5\x8a\x1a\x81\xa3\x1b\xbd|\xf9,\xbax\xe6\xbb\xd1`6>\x0b{\x02\x01\xfc D\xcf\x16k\x0cJbQ\xc0\x81G\x01\x03\x93\"n\xe7\xf0\xcfG\xf5\x95\x88\xd9\xb8\xfc\xde\x0e\x93:\x0c2:3\x931\xcc\x10wm%\x97\xba8\xc4\x00P\x1e1(\xd0\x94~\x01}\x18\xae\x13\n\x85\x18xh\xc4 \xbf\xd2K\xd9\xc3\x82\xfd\x9f\xc4'\x86\xac\xd21~\x80@gyh\xf2V\xdaE<\xc9\x1e\x80l\xf0\xcb%\xb3%?\xd6d\xc3\xb8\xe0\x91\x16\xbe\xd1\x01\xed\x82\xd0\xda\x17\xd2;\xfe\x14\x85yA\x00\xea\x05P\x16\xee\x05e!_\xe0\x87}\x01dB\xbf\xa0(\xfc\x0b\" `\xb0\x1c\x06\x069P0\xbc\xef\x1evL\x87\x9cp0\xc8\x81\x84\xa1\xd2\xb8\xff\xe4\x84\x85A&4\x0c\x15\xb8\xdf9\xe1a\xb0\x10\"\x86\n\xf2\xc0\xc6\xa0\x04t\x0cb\xe1c\xe8\x97 \x902\xc8\x85\x95A\x1e\xb4\xcc5\xd1\x1dU-\x089\x83\x03\xc0\xce\xa0,\xf4\x0c\"\xe1gP\x16\x82\x06^\x18\x1a@a(\x1a8\xe1h \x0cW\x04$\x0d2`i\xa80\x06UC\xa1i\x90\x03O\x03\x17D\x0d\xc2.\x85\x07\xaa\x06\x91\x1eG)\xc8\x1axak\x10Q\x9b<\xf8\x9a%\x8e\xc1\xd9P\x08\x1b\x94\x82\xb1A6\x94\xcd\x12\xc7<\x1a\xd4y( i\x039\x8d\x90\x06\xe5A\xdb \x8c\xf0\x02\x1f\xc4\x0d\xe2`n\xe0\xc2\xd7$\xc2\xdd\xc0#\x07\x81(dB\xdf \xa9s\xc2\x108\x88\xe8\x85\x08(\x1c\xa4\xc2\xe1\x00\xef\x9d|X\x1c\x84\xa1q\x10\x80\xc7A\x10\"\x07\xfe^\x8b\x87\xcaA\x14\\\x0eP\xc8\x1c\xe4\xc2\xe6 \x16:\x07\x0b\xe1s\xe0\xe9\xa6\x08\x18\x1d\x94\x84\xd2\x81\xaf.\x88&f\xc1\xea,i\x16\xcc\x0e2\xa1vv \x08\xf4\x0er\xe1wv\x95M8\x1e\x14\x87\xe4\x81\x1f\x96\x07(4\x0fPx\x1e\x14\x84\xe8AY\x98\x1e\x1c\x00\xaa\x07\x10\x0f\xd7\x838\xc8\x1e\xa4\xc0\xf6 \x1a\xba\x07\x0e\xeb\xef\x80tA\x02\xac+\x04\xe3\x83\x14(\x1f\xc4\xc2\xf9\x00oPYX\x1f\xe4B\xfb,i\x08\xd4\x0f\n\xc3\xfd\xa00\xe4\x0f\n\xe8H\x04\xf4\x0f\xa2\xe0\x7f\xa0@\x00\x01\x81\x01\x82o\xff\x85_\x1a\x1dJ\x92\xf6\xbd\xeb\x84\x05\xe2\xaf\xbb\xa0\x81\xf8\xdb\x16<\x10\x7f\x0d\x81\x08\xe2/\x1a0AH\xc9\x14\x9f?\xf0\xe5\xae\x94\xc8\x1c\x97\xcf#e\x90\xbb\x8b;|&\xb9|\x10\xd8\x1e^\xa5\x03g\x96'\xd7\xe70\x99\xe6S5l8!^\x8d\xc3e\x9e\xcb\xc7\x0d+\xc4kt\x98Lt\xf9\xe8\xf0B\x08@\x0c!\x18\xc6)\x0054$f\xe2\x0d\x0di\xc8\xc1\x83\x94\x1a\x03A\x04\xdf\xc9\xa1\xfb\xdc\xd0\x01G\x04\x7f\x84\xcf\x03K\x0c|\x89\xc3\x13\xc3\x1fya\x8a\x9e\xcfQ\xb8\xa2\xe7}7\x84\x0c\x94\\+\x1b\x8d\xb6X\xa4\x03\xc6\x08\x8ftj|L\x97z\x8ct)\x0f\x18\x12\x96*\x8e\x13\x18\xb9X\xa2\x0d\x92\xf4\x8a*\x0b\x96\xb4$\x96\x00Lz\xd3X\xcd\x14\x93\xa8\xf4\x15Y\xa9\xd84V\xf1~z&\xabl\xc6\x81\xc1\x17\xa50\x17x\xde\x8b\xdb\x86\x1d\x02C1z2`\x82\x07Va\xfb\xc4\x9f\xa5y0\x0eq\xfaq\x8e#\x9aZ<\x17\xa6x6L0\x1f\xa6@FL\xe9\x9c\x98\xb8\xac\x98\xac\xbc\x98\xd2\x991\x91\xb91\xa5\xb3c\xa2\xf2c\x96g\xc8\xb8\xc41Jeo\x8eL\xc1,\x99`\x9eL\xa1L\x99\x9c\\\x99\xe4l\x99\x02\xf92\xcb3f\x9c\x96\x07=h\x90O\xe1\xac\x99\xc3\xe4\xcd\x14\xcf\x9c\x89\xcf\x9d)\x9e=\x13\xca\x9fY\x92A\xe3\x104\xe5\xd5xrh\x92\xb2h\n\xe7\xd1\x842i2si<\xd94\x11\xeeI \xa3&\xd6\x7f)\x99U\x13\xca\xab\x89\xa9S\xe1\xdc\x9aPvM\xc1\xfc\x9a\xe2\x196\xbe\x1c\x9b\xac,\x1bD\x1a\xad\xc9\xe8\xc8\xb3)\x91i\x13\x95N\x12\xc8\xb6\x89\xce\xb7\xf1\x1c\xc1'\xe7\xdc\xf8d\xa1g\x8b\x052o\xd2:+.\xfb&\xa6O\"3p\x16\xe4\xe0\xb8\xcea\x8b\xe4\xe1De\xe2\x84sqb\xb2q\x02\xbd\x98\x96\x91\x13\x9b\x93\xe3\xca\xca)\x90\x97\x93\x90\x99\xb3<7\xc7\xd7i\x91\xf99\x853t\xbc5B5\xb5l\x9e\x8e#S\xa7p\xae\x8e;[\xa7t\xbe\x8e#c'+g\x07\x91\xe6\xda\xf8\x05\xf2x\\\x99<\xae\\\x9e\xb2\xd9<\xc5\xf3y\x0e\x93\xd1\x93\x96\xd3\x13\x9d\xd5\x93\x98\xd7\x93\x92\xd9\xe3\xcc\xedqgn\xc4\xe7n\xc4\xe4\xf7$f\xf8$\xe4\xf88\x9a\x96\x91\xe7\x930)\xca\xe6\xfe8\xb3\x7f\xca\xe7\xff\x94\xcf\x00*\xa1I\x91Y@\xb1y@z&\x10\x9e\x0b\xe4\xdd=b\xf9@i\x19A\xe1\xbc\x91\x88\x0f|YA\xd1yA \x99A\x91\xb9A\x0b\xb2\x83B\xf9Ae3\x84\x1e=G(<\xda\x87\xcc\x12r\xe6\xe5\x04u\xeap\x99BIu:l\xb6\x903_\xe8\xaf\x931\x14\xca\x19z\xfc\xac!,o(\x9c9\x14\x0eTee\x0fY\xd2\xb0l\xa2\xd2\xf9DhFQzN\xd1\xe2\xcc\x12OfQ(\xbb\xc4\x9f]\x14\xf8\xda\x9da\x14\xfe0\x98e\xe4\x11\xe1\xcc4\x8a:R\xc7\xf21B\xf9F\x19\x82=YG\x8f{f\x7f\xe4=L\xc9E*\xc5{\x18\xccH\xcaP-o^R\x86\\<;)>\xe3\xacH\x86\xd2Ar\x94\x8e\xdc\x8fG\xee\xc7\xdf.\xf7\xe3\x7f\xa0Isi\xa4O\xf2\xa3\xd8\xb4\xb9\x0b\xeaM\xbdg\xadON\x9cc\x9e\x98c\x1aq/M\x9dA=\xef;\xaau\x8b9\xd6\xfe\xb6\xaft\xf1dH\"\xc3\x103\xdc\xec\xb3\xd8\xa1\xfe\xa5\x1bI\xf2\x18\xdfw\xa3u\x13H\xd4PI\xc3[\x80U\x9e\xd6!\x8eQ\x9eg:\xfd\x15(\xc1x\xc1I^,\x7f\xf0\x98\x11\xb0\xfd\xdc/\xef.\xde\\\xbe;\xbf8{\xf7\xd6\x1b3\xb2\xdf\xfe\xd77\x1f\"\xde:\xfd\xee\xc3\xc5\xe9\xd9\xdb\x887\xdf\xbe\x8bz\xe9\xf2\x8fg\x17?^\xfe\xf2\xe6\x02{}\x8a\xfe\xc47+\xec?\x02P\xad~\xc7^\xc1\xa3?\x8c\x1fQ\xe8\x05p\x9e\xc4\xdb\xfa\x9e\xe0\x14C\xa8\xe3\x80\xbe\xe9\x1e\x1eg\x03\x15?\xb7\xed\xbe\xeavj\xd5\x1cGf\xd6\xa8\xbe4\xff\xa0\x08}\x10\xa4\x8a)\"\x85\n\xbc\xc4\xfe\x08*\x1f77\xa5\xc9\xf2\xdf\xbe{i\xfc[\xeb\x85\x05\xf2f\x1d3%\xcf\xbf\xe8e\xb0\xed\xc4\xbddpt\x96\xf6\x99\xd4\xb7wi\x1bQCC\xff\xc8$\x90\x8d\xa2\x92sE\xf6m\xcd\xc2\x15\x13_'\xfb\x9fa\xd7\xa8\xd4\xbfe\x91\x01\x98\xe3=v\x96\xefM+2W\xc1\xedQ\xd2f)\x0d\xe2\x9d\xd9\xfa\xddm`y\xdc\xecK\xc69:\x8cft\xe9\xec\xf5\xc9T\x8b\xfed\xba\x88\xd8\x1a*\x9b\x12S]\xcfb\x16KV\x8b \x1d&}+\x1dC\xc0\xc8@\xcdu\xb2,~\xc0\xb9\xa0F\xed\xf4\x10'\x19YV=\xa2\xd0\xa5\xf5q#\x18\xeeE6\xb8\xcc\xfa\x16\xda\xd4\xa56v\xb1MYn#\x17\xdc\xc4%w\xd1\xa2\x1b\xb7\xec\x16^x\x93\x96\xdeGZ|\x0f\xb2\xfc\x1e~\x01.\xbf\x04?\xe2\"\xec[\x86S\x82\x8d%\x96\xe2\xf2\x81\xb0E\x0b\xb2?\xc8\xb3tQ.\xb5,[\xf5\xe3\xb4\xd82\xdaA\xbf\xf9uO\xfa\x9ap\x830\x1c\xe3p\xc78\xdco=\x0eg\xfb\x81\xb1\xee\xa5\x9fn\x9dEk\xce\x95\xcb\n\"\x1cK\xf3V\x01t\x14\xdc\xda\x97{\x93\x80\xc1]\xef\xba7\x00\xa1\xdf\x0fW\xf4\xf7hE\xfb\x05\xf7\x04\xd8h\x0f\xe5\x8e\x00\xd7\xfd\x00\xe1\n\xa2=\xb9\xf4N\x00\xa3'\x93n\x00P\x14\xc70\x86\xecOL\x0f\xe9+u{+\xaa\xd0\xb5\xd8\x0208t\xf2\xbdrP\x1f\xa1\x93H\x14\x16\xe9LG\xf4\x15y\xd3\x8e\xba\xe2/9\xa3\xad\xd6\xebv\xef\xf1&*+\xe50V\xed\xa6\xeaE4Z:\xa81\xb7\x01\xff2\x85D#\xfa\n\xdd\xb5\xa1\xaa\x87\x9aQc\x8f\x86|h\xed\xcb\x0e\xb1\xf7\xc4w^\x1e7\xc8\x95\n\x17\xbb\x0d \xef\xb0\xe2\xf6V\xc1]U\xf4~*q'\x15\xdaC\x15\xdb=E\xee\x9b\x0e\xbec*\xbcW:\xe4.\xa9\xe4\xfe\xe8QvF\xf8\x9e\xc81\xfd\x8a\xee\x83J\xdc\xe6\x9e\xbc\xeb\xc1\x17\xe1%;\x9d\x12{\x1c\xdb\xf8\xbfS\xac\xa1\xd5\x1d\xfa5\xf0~c\xe1\xb2r>\xdb\xe6\xb0h\x01;\x16e\xbdp\x9b\xb5\xd4R)\xe3\"$\xd9\xf6\xe9@V\xa9\x88-*o\x81\xf2\xed\xce\x01\xad\x8d\xa5\xe6u{\x9b\xb8Wq\xdc\xc1\x18\xb6\x18\xc1\xbb\x17\xb5\xb7\xd5\xba\xb9\xdca!#\xda\x0d\xb6\xeddt\xabM\xdf\x08m\xae\xe9\x0f\x85,\x83\xfd\x8e\xee\x03\x85\xbc\x1f\x8f\xdf\x13\xe1\xf1DY\x0b\xf0z9\xe5\xac\x06\xa0\x96\xc3nI1\xeba\x8b^hAlA%\xac\x88-u\x89%\xc1\xa4\x14\xb4&\x80x-\xfe]Z\x8e\xa7\"\xe6\xf2\xb6n\xc7\xd5\xfd\xd7\xd7d\xac\xbe^%\xda.\xfa\xed\xa5\x91\"\x8b\xced\x11\xd2\x1a\x05\x89\xc1\xba\xab\x19|\x8a~/\xde\xa9\xdb\x9b\x86E(/\xa9\xd2_r\x96\x918\xa9[q\x8bf\xd5\xb6\xfb\xaa\x11\x04%P\xb7\xb3L\xa02\xad\x82\xb6\xd5\x97\xb4\x02B\xf2\xea\x90A\x93\xf2DT\x04\x95w\xdbU\xcd\xe5u\xd7nHh5\x10\xd2\xe8\x07\xb4SE\xb4\n\xf8\xb7P\x8d\xddV\xa2\x0f\xae\x9bn\xfdi\xa0k\xcc\xe5\x03\xa9\xfc[cghR\x14\xc7\xa9\x9b\xc8F\x08\xa5\xc5\x02\x15j+\xa7Xl8\x90N\xb98TF\x00\xe9\xe8\xc3\xb6\xdb\xec\x1b\xe2\xd2G\x16$\x97\xdd\xdbfoLU\x15\xf0\xc2\x94\x80\x07Y\xed\xde>\x93J\x99\xdc\xcd\x93:gw\xef<1\x1c\xdd:\xbf\x10\xd7\x9dV\xa3bb\xd3\xd3G\xbe\\A\xbb\x03\x17^\x0d\xee\xbd\x8f\xd7\x7f\x13\xafp\x01\xe7\xa9\xa4v~\xd4\x89\x0ef\xc8\xc1\x17\xac\n\x1bt\x08\x1b\xf5\x98\x12\xd2\x8c;\xb8\x0d|Ja1\xb2\xeb6Q\xb6\xcf\xe8\x83\xcb\xf0\xc7H\x8eX\x00\xc0\xb7\x08x\xcb\x08\x9fS\xc5/\x08\x07\xb8\x93\x97O\x0c\xdd\x7fy\xa5\xa8Y\xc4\xc4\x1b\xf6\xd7\xc3\xaeZ\xfb\x9d\x0d\xed\xb4\x13\xf9\xdd8K\xf1{m\x93mR\xaa\xab:\xb2u\xbb\xa9\xef\xeb\x0d[8\xe4,\x17\x9a\xcf\x19+8c\xa2*\x84\xbe\xc2\x05\x9d\xdb!x\xa3\x93\xb2\xad\x94\xcfHi6J\x1ebO\xadXd\x9c\xec\x01\x02\xb7\xc6\xc6\x1eK#Tg\xfeAs(o!\xc5e\x1f|\x10\x0dM\x1f\x16\xd9E\x07>\xd1\xc0G\xc2c=>\x91\x87R\xc9o\xfe\x90\xadqX {R\xd9\x0d\xcd3I\xb6BP\xc9\xb4\x1b\xa8\x9aF\xa6\x18\x0c\x0c\xb9\xab #_\xeaa\xa4C\xab\x9f\x10\xd0\x0f\xa4(\x13[\xf7\xa1n\xd7\xe4\xa5\x18\xeb\xaf\x86\xcd'\xf8\xef\xab\x7f\xfa\x7fl\xa5\xd2\xbd\x14k\xfc\xb5\xc9\xa4i\x1a\xf7\xa2'D.\x8b\x05L\xa4\xac=\xb9\xad\x87\x91\x1d8O\x8a1\xb5\x93\xb5\x91\xc7\x0e\x94\xda\x07\xaa\x8d+\xed\x07M\x1d\x12\xd44dj\x97\xa9q\xd0\xdc\xe6j\x85\xa2 \xa2\\]\x05D)\x81N\xfcu_\xf5\x15uc\xc9\xd4\x91\xa7\xfb\xb1\xc3\xe6\xbc3B~\xfa\xf1\xe2\xdd\xe5\xfb7\x1f\xce\xdf\xbd\xfd\xf0\x06\x8d\x91\xebo\x9c\xbez\xf5\xe6\xfc\xc2\xf1\xe3\xeb7\xaf~:{\xfbFt\x9c\x08b\xf9K\xc0\xe7\x9e\xda\x0c3\x805\xb7\x1a\xaa\xfd\xd8}5i\xb3\x08g\xa9\xe1mg\xd9\x9ej)S\xa5\x1aY4h?v\xdbj\xac\xd7\xf3\xc4\xb9\xab80\x9d\x91\x02L\xd4\x90rMb\\\xd5[R\xb5BF\xdb)2*Fh\xa8\xd06\x8f\xd5'\xd2\x9e@\xbd\"+\x06}\x17B\xf4\xb6\xd5\xd47\xbf9\x91M\xa3J%i.\x94\xfe\xb8&w\xd5}\xdd\xf5,\xd5F%O\xc0G\xd1\xec\x04\xfeW\xbd\xfd\x03i7\x83R\xdb\xa9!\xec\xd8^\xf2B\x9f\xc0\xf5\xc3\xae\x1a\x06jA\x14\xadt\x15/\xf4\xc4,_\xfc9\xa5\x02\x1b\xb2n\xea\x96\xa8\xeba`V\xbci\xc7\xfe!\xda\xce\x8c\xdde\xb5\xd9\xf4d\x08\xed\xf45=\x9e\xbf\x9a\xbd\xd25\xa9\xef\xd9>\x9e\xff \xfb\xe6\xa6\xef\xb6K\x8aP\xbf\x93\x85\xd0\xaeB\x8a\xe85{`I\xea\x0d\x07ZW\xbc\x81\x8c\xa3\\\x17\xc6;B\x17\x8d\xcf\x9d,\x81\x0c\xa6\x1b\x16\x8c\xbb\x87\xec\x0d\xf6\x96fs\xb0\x17T\xbb\x03\x0bm\x8f\xa5#\xda\x92I;EY7;F\xee-\xbb\x9f\x8e\x05T-Ki\xf3+\xe2\xff\x9e\xfe\xb4\xf9~\xdfn\xe2\xe3\xa3\xcb\xf4P\xf3el\xa5\xac\xdb\x91\xb0\x0d^O\xd6\xf5\xaeV\x98\xf0\xe9.|\x90\xcc#\xf7\x84\xd99E\xd4\xdc\xb2\xc9\xc4\xec[i\x0b.U\xd5\\\xe8GF7\xcaY,\xcb\xb0\x94\x93\x82\xf4jc&\xc3\xbd\xab\xfaQ\xbf\xb0\x80Z{&\x0f\x1e\xc8\xa8\xe8<\xeb\x0f\xd9T\xf6\x8fE\xcd\x8av\x8fQ\xb2\x08\xa7o\x8c\x13D\xc48\xbbF\xaa\xecb:\x08UH\x0e\x0f\x84d}P\xe5\xc5\xd3=hM\xe3*,m\x1a\xaf\xc9L\x18^\xb7\xaa\x12O\xbb-\xd4\xb6\xc95\xc6\x1c\xf2\xeb\xaekH\xd5\xe2\xc5\xcb\x8fX\x0d\xfa=\x81\xfaF\xe8\xd2\x0d\x9d\xf8\xf0\x99\xf4\x8c}\xfd\xbe\xee\xf6\x83\xb1\x90\xb9\x14\xde\xb4\x1e\xd3P\x0dc\xbf_\xd3.2(\xed\x06\xdfD6&\xb1\xcfh\x91\xfeA5\x8f\xe9\xbbJj>/\xa5\xf9<\xf0\xdc\xc1L\xa5*\x08\x99@\xcb\x97o\xfe\xe0\x8bxZ\xa1 \x0b:\x7f\xece\x1d\x91Zhq\xe7\x8f\xa7)\xae\x84\xc3\x98\xe5\x1e\x7f\xd7Z\xf4\xf1\xd7\xcc\xa5\x9f?\x91\x0e\x80|\xd9\xbd\xfd\xcf\xf4\n4Y\x8a\x87\x80\x14\xacO\x91i\xf9\xd2\x07\x8c\xb4c_\x13\xe1q\x8c\x9c\xb8\x85\x93\xf5\xb2\x0d\xbc\x14]\x00\xdda\x1f\n\x1c\x81\x1eG\xa0GY\xa0\x87\xce\xc1\x81\xae0\x9az\xbe?\x7f5\xcf6\x96K\xa8}#\x84M3!\xb0\xa2\x9d\x0d\xcaz\x9a\xbc\xa2\xd5\xc3\xa5\xb2\x80&\xb9\x06\xfa\xa7\x86\x83\xa0.>w\xd5\x00\x1d\xf5m\xf9m0\xe6\xc6\xda\xd3\x97h\xdb\xfc}\xd9\x82\xf6Qbg\x9a\xaeIr\x7f\xfe\x8a\xee\x8c\xe07\xeb# 9\xe5\x057X\xfcA\xb6Y\xfc\x89\xd8l\xf1\x07\xeb;\xfe\xa4E\xc8!\xa2\xf99[1K\x98\xd8\x9a\xc5m\xc8\xf8cl\xcb\xf8\xb3\xb0\x03\x16\xe1\xe3\x8f\x0c\x7f\x7f\x1d\x86\xbfp7,\xdd\x0bZ\x82\x1c\xae\xb1\xbdC\xe4\x0f\xbe\x18\xc8o\x8a\xed\x16\x11\x81F'\xe4\xef\x1c5q,z\x82Z'\xad\x12\xa6M\xa7\xad\xab\xdb\x9b\x0e\xaa\xebn?\x8a\x12\x86z\x14\x898H\xe4\xf8\xe8\xc8\x1e\x1dY\xfe\xf7\xbf!G\xd6\xe5\x0b\xf9\xfd\xafI\x821gL\x0flh\xaa\xe1\xaeno\x97f\xdaR\xdbJ6\x97\"\x87\xe7s\xddn\xba\xcf\xa6\xb3e\x8c\x99\x1c/u\xb8\xb6u{)D\xedH\x9f&G\x99\x0c\x9b\xees;\xd6[r\xf9oU\xdd\\n\xc4]\x02^9\xac\x03.oz~\x90v\xb9\xe9\xf6\xd7\x0dauI/\xde\x92\xc5k\x93*HS\x06\x91\xab\xa1\x98\xd2QO\xb2\x9bn\x11\xbc\xe6\xb3S\x8e\xa8\x9d\xc1j\x8duv\xc2\x8d\xe5WG\x19=\xbf\xce@\xcc\\7\xa7z@\x7f\xa2d\x1aF5\xa4K\x1e\x991:\xe5\xf9\xdcY\xa5\xa0~\xa5\x0b-\xaek\x93\xd1-\x95T\x17\xd2\xdf\x0f\xf5m[\xb7\xb7g\xed\x0d\x9a\xaa\xe0\xc78U\x0d\x1b\x96\xba\xbd\xbd\xa4\xae\xc4\"uF\xb7~.\xcd`\xbc\xd1w\x08\x0248l\xce\xd5\xedG&\x0d*\xe903HN5v=[\x83\xd8\xcd;P\xc1\xbaj7\xf4\xcf\x04\xde\xbdg?\xec\xdb\x7fc\x8c\xff\x8a\xc8\xba\xdd\x90/\x97\xdd\xcd\xcd@\xf2k\xe7\xf7\xa2\xcfhQ\xd2\xc3\xa7~\xdc\xbagN>\xd9\x00\xa9\xd6w@\xf5\x9ai\x85\xde\x9aJ$\x91\x9a\xabm\xdd\xd2\x9f\xa8Aa;\x8fm\xf5\xc0w\x84\xdc&0\x98(Yw\xdbm=\xf2[\x95Gq\x05\xb8u\x9b\xe4\xbak\xffM\xdc\x12\xc9\xf75\xc8e\x00W\x1f\x98\xd4\xef\x98\xfd\xfa#35WS\x92\xe1H\xfa\xed\xb4,\xb3\x0e\xc5/\x91\xbd\xfa\xb9\xa6\xae\x17\x17\xf2]=\x9e\xd2M\xe5\x95\xea\x03\xf1\xe1\xb9dw\x14$\x8e\x86\x8b\x15\xdf7\"\x17\xf5\x96\x0cc\xb5\xdd\xf1[\x11\xc4\xd8\xe8CP\x0f\xa2V\xb0\xd9\xb3\x08uS\xdf\x93\x96\x0c\xe6m\x12\xd22\xe9.\xdd\xf6z\x18;k_\xe3\xde\xd5\xf8\xeb\xfb\xc7;\xc2\xee\xae\xe5\xe3)\xefee\xd5\xbc\xab\x06\xbe\xa9\x98\xcb\x84g\x9f\xea\x86V\x9cn\x15\xac[\xe5\xe7O\x072>g^l\xcd\xdc7s\xe8\xbbvm\xaa%W+\xb6i\xa5\x9b\xcb\xfb\x8e_\xf8L\xeb\xc5\xb2\xd9\xda\x07\xee\xeb\xd9\x8avS\xdf\xee\xe9^t[\x0f\xd7\xe4\xae\xae\xee\xf5\xbb\xa4\xb7LA\xe42\xc9\\\xcdH\xe2\xdc\xe5\xd3\xf2\x14D9\xf0\x89\xec\xc6\xf9\xa2\xe8}\xdb\x925\x19\x06v\xc9:\xd5T\xe8I\xb5\x19,n\x82\xb7\xdd(\xf6~W\x1f\xf6\xdbg\x98\x8e?\xbf\x82\xaa\xf9\\=\x0c\xb4\xbb\xaa\xc6T\x1dm^\xbc\xe2\x95\xb9r\xec\x08u\x8c\xa2\x1c\x11eAPB\x15\xd3\x80=\x1d@\x98|\xbe{\xa4\x83\xb4\xed\xdaz\xec\x8c^\x1c\xefH\xad\xa5VJegIW\xf7\xf5\xa8\xb2^sSl\xae'\xd3\xc9\x9bZ \xdbfH\xf6\xec\xfb\xaaa\x80v\xb9\x8a\xfc\x0e\x90m\x8bk\x9d\x8bYO\x95\xcf\x84\xb4\xf7\xe7\xafd\xab\x92W\xd8t?\x11[V\xcbF_SC\xaf\xee5\xd8\x1b\x1esO\xa9\x03\xac\xc4\xbe\xb5xq-Cq\xad\xc2kr\xc1U9n].\xb42\xc7\xac\xcd\xbe\xd59j|\xf0\x15:rL.6\xee\xb5\xa23\x01_@\x8a\xfa\xa8\xb8w\x9aP\x1b\xf7\nP\xd0\x0b]\xee\x7f\xaa\xfe\xa6\"\x10\xf3<\xb3}N\xbf\xb7\x89\xfb\x99\xde\x9e\xb6}Kwo\x97\xf2'MO\x12\xf3!q\xef\xd1]\xb7R\x1ec\x94\xaf\x98\xe1%*^\xa1\"\x10\xf1\x0f\x03\x9ea\x91\xe9\x93\xe3\x01\xe6\xf9~\x1e\xaf\x0f\xafn\x86\xa7gGl4\x1f\x0f\xf5\xee\xa4\xe9\x1e\xabO\xaa\xe5\xfe\xaek7\x1f\x94\xcb\xc1\xadQ\xd0\x11\x8e\xdf\xbd{\xfb\xda}\xd3\xb5\xf9;\xfd\x97\xf7\xc7\xb3\xb7?\xa0\xbf*\x1fN\xd9\xc7\xbe\xa2q\x8fan\xdb\xe4\xc9*w\x02O\x1d\xacb\x1c\x1d\x85\xbc\x04\x94x\xab\x85\xba\x15\x14`\xf3$\xd3\xef\xc7\xc6\xbb\x84\xca\xe3\xff\x87\x8d8WBq]/\xb7\xeb^ygo\x7f\x90\x02\xcf\xde\xfe\xe0\x95\xb8o\xaf\xb9\xbf\xe3\x10(\xab\x17Q\xb9\xb9b\x0e\xd5zE\x0d\xc80\xd4 \xfcs\xeb\xe9\x13FM\xa2\xf8\x04\xee)o~\xa3e\x06\xd4m=\xd6,\xc8(_\x02\x01~e\x9e\xa3<\xd1S\xc5\xf5\xa4\xe2\x1cA\xaa\x86\xc8_\x13v4\xb4\x9cH\xc7]k\xdcS\xfa\xa1TX\xa3\xde\xb0\xbe\xab\xfa[\xbeO\xd9\x90\x86\xdc\xb2\x1d\xd8 0\x0f@\x9e\x8c\xae\x9e*\xc2\xb7\xd5\x97\xcb\xa55\xb1v\x0bR\x98\xd6\xc5\x92\x9d\xc5\xac\xaa\xe9`\xad\xad\x98 \xb9\xe7\x04\x1a\xfd-1\x9b`\xb4\x80\xb3l\x94m\x88\"\x13m\xcf\xa6\xaaY>\x1bU\x08\x9eVb\x07\xcb\x8c\xd5\x92\xb5\xde\xd1\x94\xfd\x8e:&\x97 \x19\x11\x01GF\x11(\xb5\xa5\xa9\x86q\xf6\x10\xad\x01\xa9\x06AW\x82\x01\xa3\xe6\xd9:\xf5\x86\"\xc0\xe0 \x93\x04\x87\xda\x0c \x9a\x81\xf7\xf3\x94\x8e\xb0\x05\xfa`\xa3=u\xa0yc\xcf\x99p\xe9\x9a\x8a\xe5\xce\x1363T\x9b\xe4\x9b#\xce\xf9\xb1\xa0\xd2\x8b\xe6\xc4\\w\xb5\xca\xfe\xf9\x80W\xc3P\x95\x0cK.\xad\xf8\x84\xf1\x8fR\xd5\xd7\\5RV\xacI\x9b\x96@\x8a\xad\x8f\xa5\xfa^\x93\xf5\xdd?~\xf3\x15i\xd7\x1d\xe3\x8c\x12\xbf\x8a\xfe\x9e>\x93*0\xb5mI%\xac\x8f\xe3*a-\x8e\xc3]\xd5\xdbpD_\xc9\xfc\x0b1\xcaj\xcb8\xa7C\xc5S\x96\xd7\xa4\xbeG-\xd6\xe4\xe7\xcd\xc3f&\xefP\x1fEl[\xbbO\xa4\x1d\xe0\x8e4,\xfb\x99\xa7\xc2\xb3\xecl\xbe!\x12\xa2\xba\xcf-O\x8f\xeeZ\xa5\x9f9\x9fv=@5\x0c\xdd\xbaf\xa1\x1c\xb9\x19\x9e(~\xbb\xcf[\xdc\x18\xda\x8a~\x86\x7f\n~\xcd\x85\xe8\x95\xb5\x94\x06\x83_\x8b\xe3+TJ\x9b\x01\xd1\xe8\xf8Z\xa4k\xb6%B\x8b{\x16\xd4p(\xad\xe5`j:}\xae\xab\xa6jmF\xa1H\xc5\x8c%<\xc4\xa0\x1ea\xb3\xa3\xf5\xecbH\xc7,\"\x07\xc9ac\xf4\xe3\xe0\x1a!C\xa8\xc6~Y\xf4\xa6jH\xcb\xe2!\x8a*\x91/\x0c\x0c\xc47t#\xac\xbbv\xac\xeav\x98\xd2\xa7\xc5 \xb2@\xe2fS\xb3o\xc6N\xea\xef\x14\xa3dG\x17\xc3\xbe\x1e\xd91\x01u\x0f\xd7\x0d\x83gM\x00\\\xaf\xfd\x9b\x1b\x12k\xf8\xb6][\x7f\n\x86\x8e\xb4.\x12\x9f(#}\xb7\xdfV\xedW=\xa96\xac\xda\x8c^J\xc6\xc4-u\xae7\xa4\x1d\xeb\xd1O6\xe8s\xae\xa4\x005v\xc0)\x96\xaaf\xfeq\x1a{xF\xbe\xac\xe0\xe3y\xd7\x8f\xa0\xb9U\xffB\x1e\xae\xab\x81<\x97\x15\xfbL\xae\x87:\xcd\xe9\x13\x9f\xa05\x91\xbf5u\xfbiZ\xd7\xc9z\xdf\xd7\xe3\xc3%\xd3\x8fu(\xe0\xad\xdbA\xe3[\xb4P\xb2\xad\xea\x86\xf5\xbd|\x1d\xc4\xeb\xb2\n\x1b2Vu\x93\xe4[\x88O\xa4 \xe6\x91\xca\xa9H\xf1+2\x97\x14}D\xa3\x1f\xca\xcb\x1e\xad\xfe\xb1\x1e\xc6\xae\xaf\xd7U\x93t\x12rG\xaa\x8d\xad\xd7QF\xf3\x9e\xf4Cm%h\xf3C\x8f\xebj\xa8\xd7\xe2$\xa0\x9e3\xa1\x96\x19\xd4T\xc3\xc8\x8f\x0d\xec\xdas\x0d\xdf\xf5\xe4\xbe\xa0\x82\xdfU\xc3\xddB\x85\xb4\x12\x128\xfc\xf6r \xe3\xa59\xbd\xe5\xe3\xac!\x04a\xb2Hz\xc0\xf4\x13\x93Z\xb7#\xb9ERy \x90j\x03\xcen\x80PW\x80\xbf;\xa6A;\xaf\xfaq \xe3\x8f\xacW\xcc\xe1\xe6\xa7O\x97v\x15\x82\x1ad\x15)\x8a\xa3\xa2\xf8\xdc\xe2\x8aB\xa7\xa0\xf2\x1e\xfdg\x81\xe2\xe6\xbc\x98\x02\xc2\xcc\xbaOL%\xd5n\x07\xdd~\xdc\xedG\x95\xbdD\xce\x01E\x02Kx)Z\xa7\xc9R\x16\x90U\xedv\x05\xa40}\x11Y\x15\x05\xc4\x91{\xea\x8f\xadI\x01Q\xd3\xf8\xcd\xcb\x8ba\x9ev\x8c\x16\x9a\xa4ny\xc3`&>\xab\xb4\x08\xda\x0cOf\xe7_|\x16p\x9b\xa4\x04\x92\x90\x94\x86\xb2 \xcb\xf4\xaf\xee]\xbe\xd7\xb6\xf8\x97v[\xb4\xd6|\xd7\x8e\xff\xa9\x9d\xc1'\xe5\xfc\x0f\x165\x00\x193\xa8[\xf8\xc3\x87woW\xda\xfb\xf3|\xd8\xed\xaf\xad\x046\xafq\xf7\x99v\xfa\xd9\xe5\xbeGm{\xc0\xfa\x86z \xd89\xf9\xc7\xf7?\xbd\xe8\xc9\xd0\xed{I\xae\xcb\xf6\x81\xfb\xb6\xfeuO\x9a\x07\xb1)\xb9\xa9E\xef\x8d\xe2J\x01,a\x18\xd8.\xa1\xaf\xab\xa6\xfew,\x11\x1ax[\xc7n\xdd5p\xbd\xbf\xb9!\xbdL\x9e\x12)'\xbc-\xb0\xdd\x0f\xd36\x14\xaa\x11\x1aR\x0dHJ+\xb0\x84\x05\x02O^\xac\xe0\xc7\xee3\xb9'=\xe7\xdb\xff\xf8\xfe'{\xee\xd2\x87\x87&\xa8\x184O\x9e>\xc3\xfa\x8el \\\xdd\x8d\xe3\xee\xea\x84\xffw\xb8:\xe1\xd96\xe2\xd7\x13\xa6eke\xaf\xda<\xb0\xc4\x19L\xe0~\x07\x15k\xab\xa3<\xd2\xdf\x13qx\xbe\xadv\x03W\x19\xda\x02vA\x87H%d\xf6\x8a9\xf0\x03Tx\xdbn\xba\xa6\xe9>\x0f/\x1dc\xf7\xf7pv3\xb7\x80\x0e\xb9d5\x9b\x1a)B|\xfb-\xd9`\xb7\xab2!\xa7-\xfcxqq\x0e?\xbc\xb9\x80\xae\x95\xd3\x88O\xd0\x07\x16\xde\xc25\xf3O\xa6\x8a_<\xec\xc8\x9f\xff\xf4g\xf4eA\xf7O\xc7Z\xe8\x10_\xd7\xd8(\xec\xfan\xb3_\x13\x96~\xd4\xf7f\x96\xb9|\xfe\x1eN\xe7m\x11'\xc7\xa9h\xff\xf0\xa3\xbeu\xb5\xa66\xa1\xeb>\xedwS\xde\xe5u5\x90\x0dt6\xc8\x02\xf8DwT\xf5\xe3\xfb\x9fX\xbdX\n\xddxG\xb6\xca\\\x10\x9cR\x95l\xc6\x94vT\xb5&S\x8e|x\xa5\xd8\xb4\xef\xc9M\xd7\x93\x13\xf91\x95Y\x8d\xf5u\xdd\xd4\xe3\x03\xbb\xd2\\\x9eq1\x13\xd5\xdf\x1bP\x99\xf9\xe9Zq\xbe\xcb>`\xf3n\x05\xcf>\x0eDF\x07h\xafP\xb5\xa3v\x86\xeb]\xd5V\xb7\xae\x16_\xf7\x84E5\xa4\xd0\xd5s\\[\xdev#y\xc9\xd8\xb6\xe1F$\x0dV\xac\xee\xc2\xde\xcc\xe40J21\x0e=\xa1O\xc7\xb2\xa8\xed\xd7\xe3\x9d\xc3\xe8?\xec\xc8\x8a\xebs\xb5\xab\x87\xd5\xba\xdb\xba,\xe6\x076\xdb\x06\x11f\x1a\xef\xaa\xd6\xb4,\xf0L\xc4\x16y28\x9f\x9e\xcfQa[\x96\xa2z\xed0$\xac\x81,@=\x05\x94\xc5\xb1\x02gN_\xc3@\xb6U;\xd6k\x8br\x12\xbd^\x83?\x01\x97\xc2\xbb\x9d\x8b\xf38~\xa6\xa6\xe3\x9a\xc8`\x9a\xe20X\xbe\x81XT\xab\xeb\xee\xde\xe1lH\x92x>F\xc6;\xa1\xda\\\x9d\xb6\x0fWJ\xb4\xbbU.\xf5\xf5\xd4J\xd8hK\\\xd5t\xed\xad82\xb0\x87\x8cZMf\xf4y\xad\xaemwJ-SzE\x88\x9a\x9dK\xc5o\xeakVUa\xd7\x07\x18\xf6\xbb]\xd7\xb3\x95sW\xad?\xbd\xd8\xb7\xf4?t\xbd\xe4\xe3\x8d\"\xca\x98G\x83:\x0f\xdd\x0d\xecGn|\xe4t\x1e\xa8\xe1\x93G\x00U\x03\xb7\xa4ew\x08l\xc4\xf1\xc5\xe4T\x9f\"\xf6\x8e\x0f\x91]\xce\x9b/\x15U`\xf8\xfa%\x9cW\"GYT\xbd\x9a\x16\xc4\xba\x85W\xff\xf0\x0f\x8ee\xea\xfb\xae\x83\x9b\xae\x83oa\xb5Z\xa1\xb9\xf4\xac\x13\xaa\xf6\x01\xff\xb1j\x1fV\xb4\xe8\xef\xfbn\xfb\xec\xa6\xeb\x9e\xe3\xaf\xadV\xf8\xdaS\xdf\xc03*\xe2#\xab\xf4E\xf7\xec\xef\xa8\x8c\xe78\x04\xc0#\xe7/\xee\xbe\xf9&\xd07\x7f\xa8\xee\xab\xec\xce\x81o\x99oE\xa5g\xf4B=<\xfb\xbe\xebV\xeb\xa6\x1a\x06O'\xf0*\xd1\x0fx{\x94\x8f\xf0r\x91\xde\x99\xba\xe7\x1f\x03\xdds\xfe0\xdeu\xad\xa3\x83xM\xbe\xef\xbag\xab\xd5\n\xb7\xc4S\xe7\xdf\xd2\xd3\x11\x91\x0dhg \xb2\xc8\x18\xfb\xedY\xdd\xae\x9b\x15\x0c\xa4\xb9\xf9jNO4F`N\xf0\xc4\xb2!\xe3\xeb\x83\xe8\xac)z>\xadaXt\xf1\xc7z\x18\xf6<\x9aWy\xcfl\xe6\xb4~\xb3\x01F\x82\x19\xfe\x93\xfa\x0f\xed\xd8H\xfd\xfbH\xfa\xed\xe0\xce\x12\x93\xcf\xb8\xec\xc8\xc7Jk\x9b\x9f@x\xa6d\xaa\xdb\xfc\xd8Io\xf3\x93R!G@hyJ\x1c*\xae\xeb\xad\xe4\xb8\xf9\xb1\xd2\xe4\xe6'\xa5%\xa9\xa9s\xf3\xe3N\xa2\x9b\x9f\x94\x9a8\xfata\xbe\x1d*\xcb\xc8\xc1\x9b\x1f+\x1bo~R\x9a\x90\x90\xa1'\x9fi\xbd(\xcb_\x15\xeaY\xb3X\xd9\xab't\xe39\xfdx\xc2\xe6\xd3\x9dA0\xa0\\B9?\xe6\x92|\xbbo\x11d\xa6Q\xb6\x9dL\x15\xd5\xe0\xa5TKz\xc1\xbe&o\xe9\xd6\xae\x9em\x8a\xa7\xb9c\xc7\x8e7\x1a2\x12W\x83\xd7\x06jt~\xb4\x1a\xafm\xb8\xda\xa8\xe3\xbef\xc8Z!\xfb\xec\x06\xa7\xceO\xa8W19 0'T\xdet\xe4\xe1\x87\xae\xce\x8f\xa7\xfd\x10\xcc8\xc3\x80\xad\xf3\x130\x00\x10\xd9E \x8aY\x00\xe7s\xca\xc3\xb0\xa5\xf3\x83\xe3d\xe7\xa7`\xc3\xd2\xd1\x81NQ6j0\x8c\xa7\x9d\x1f/\xb2v~\n7=\x19c\xe8\x946\xda|\x15>\x04\xee\xfc\xa0X\\En\xdca\x98\xcb\xb2Bd\x87,\x01\xf0\xe2\x06E\x05\xf5\xca\x87q\x82\x93\xe6\xe6\x12\x83\x89\xf1'\xc3wG\xa4[\xbb\xbd\xa7\x03\xdb[\xb0\xfb5*\xce\x19\xd7\xd2\x11\xb6\x84\x89\xb7\xa4 \xec\xb0\xc8ss;\x7f|\x15\xfeE\xc9\xde\xb72\xfaO`\xecn\xf9ny\x02;\xf1\x0d\x88@\xf3\x88\xa4'\\\xe2S\xce\x83 7+\"\xeeZ\xf7@\xbe\xf0Q\xe1#\xc7\xd6\xbd\xba\x1dV\xf0AP'i\xe2dj\x82\x99=T\xd1\xbe\xe3\xd3@\x84O5\xb1'<\xd7\x81\x1d\xd1w}O\xd6#\xac\xabf\xbdod\xe4Q\x13v\xb3\xa7n\xb4^\xc0\xbe\x9d\xbb}`\xed\xef\xf6#\xd4#\xe9\xf9B\xd2\xdd\xb3\x8d\xc4\xb4\xb3\x82?\xde\x91V\xdc5R\xf5\xba\xe2\xab\xfbK\xbd\x14\x96\x1a\xa0v\xb8\xc9\xaf\xb3\xee\xc9\xa6\x9e\xe0f\x95\n\x96\xfb|\xd7\x0dz9\x13G\x99^\x88:\x0c\xf5\x9c\xe5\xc1\n\x9bG\x92W}\xae\xea\xa6\x9e\xc2\xf5\xa6\xe7\"\xd2\x16\xf4R\xb4\xfe_\xc1/**\x8e\x9a\xe0k2\x0d\x01\xd9\xb0\xb07j/\xb5\xe11\x9cfa\x11\xb9\x03-\x02\x89\xec|\xddt}\xe9\xba8gi\x9b%\x9a9\xfc\x11\xa4H\xba@Y\x11\xb9\xc4\xce4\x7f\xf3;jUD\xef\x16\xa8\x87\xc50 /\xdd\x9ck4\xdf\xc3\xd9\xf5@jf\x89\xa7\x0eRM\x81\x06\x84\xeb\x89\xf2\xcb\xb3\x1d\xe9aW\xd5\xfd\x8b\xb1\xaf\xbbi\xffo\xebK\x81\x16\xd9Be[\xe6^\x9d\xdf\x99\x1a7vr\x8e\xc8\xda\xd1\x16^\x1aX\xd7\xb0\x86\xcd_i\xce\x13\xfd3\x0b\xee\xac9\x9eu\x06\xaeN\xe3Z\xb7\x97\xc6\xce#P.\xc6\x19\x89H\x99|\xf4\xbb\xaan\xbf\xfa\\o\x88\\\xe8-\x07\x86\x9dC\xa8\x1e\xba\xee\xbfr\xd7U,\xa0\xf3\xaa\xf3;\xab\x13\xc4-\x19j\x07\x18\x04%\xb6\x01\xf1\x99\x87\xaek\xa2\x8dC\xdb\x8d\x97\xdc\x9c_\x9a\x11Z\xa4\x17c\xdf\xd4\x9a7\xe1yi\xc5h\xf7\xceIm}\xb5fm\x12\x0b\n5\xf3m7~%\xfe\xc9\xd1\xcb\xc3~\xb7k\x1e\xe4\xc1#\xfdI\x883\xd5\xc2\xd1\x1d\x8c\x11\xb2\x08/\xc1\xa5}\xad2\xba\xf7\xf4\x12\x14X\x8e\xabs\xfb\xea\xde\xb8\x06\xb8\x0e \xe4\xf4\x87\xdca\xa4\x84(\xde\x01\x9f s\xdb\x12\xa0G\x80\xfc6,\xe3N\xf0 2\xdb\x80\x9f!\xa4T|\x19\xb3\x82%\xc6j\x7f6\xc3\x82!\xaf\xb6 \x9a\xf9\xa4\x0b\xa6\xc0\xd6J\x8a@;\xde\xe2b\x80\xa5\x93\x08\xbd\x92\xd33v\xae\xab8\x9d\x9f\xf8\xc7h1W\x83!'\x87\xb0\xc1\x10\x85_\xbb\x19\xc7\xe1\x00\xde\x06\x97\xe1rP\x04j\xc9\xa9\x99\xd4\x0e\x8a$\x8d\xe4\xc1\xd5 \xc7\x92B\x8b\xf0\xb1\x12+\xed{\x7f\xfeJ\xc8\xe2]\x1e\xb9~u\xfd,#\xfd\xf6\x0ed%\xb3\x16\xf1\xb2\xe0\xb8P\xb0&9h\x1d\xb1\xf2\x05\x03^\xe1\x95\xa3\xe0\xfa\xe7Y\x01\xa3\xd6\xc0\"\xad)\xb6\x12z\xd6B\xf7j\x98\xd6\x84\xe5+b\xb8/\xf2VEK\x1c\xe7!\xb2\xfe\x9c\xb92\xda\xf2n\xb0\xb5\xd19\x14\xe8\xfa\x981\xe1\x1c\x17W{\xc7\xd4}iuF\x90\xb6\xd4zYt\xc5t_U\x1d\xbfj\xfa\x9b\x9e\xb5rj\x92\x14F$Lc\n\xac\x9e\xd6\xfa f\xe3\xb05h\x1e\xd4\xa61f\xfc\xf0t\xbe\xa6\xc3\xb6\xa4\x05\xae\x80\x16\x81\xe8\xe3\xbd\xcf\xc7\xcb>\xb2/\xfb\xc0\xc7/\xe8\xbf9\xddFU\xc0\x0bL\x82r\xb1j\xb4\x03\xf9Q\xc6\"s<\xc990\xfc\xb8\x8e\xe4Ah\x16\n\xf9z\x0e?/\xe8\xe3e\xd4\xbc\x90_\xe7p$\xac 3\x7f\xb0\xe1\xe5\x8f5\xc8\xea\x07\x8b\xf8\x80x\nH\xd7:\x12\xa4\xf8\x13\xf0.\xc3\xc4@\xe1\x8e\xe6\x8fQ\x1b\xd9\xd9\xe2_\xf3e\x1bs\xb8\x7f\xec\xbaO\x0ea\xbb\xa6Z[XO\xe0\xf94\xbb\x86\xb0r\\'\xfa m\xf6\x9d\xea\x1b\xed6\n\x96\xcd\xdb\xb7\xf5\x979!jn\xdb\xfc:\xd6\n\x91\xf8s\xe9\xf0F\xf9\x13lE\xdc\xb8\x18eiK\xbdp\xe7\xc5+\x0d\x87*o\xf6\x0d?\xd7\xc5\xe5\x89\xed\x05Tc\xa0\x91\x05\x1b\xe7\xa9\xfb\xd8\xc5\xd6(\xdc]\x88\xf5\x7f\xd3\x8e\xbd\x96\xb5:\x0f1\x9f\xb3\x1cSn\xcb\xeaIC\xee\xabv\xa4+P\xb5\xa9\xc6\xca\xbb\x1d\x93'G\x15\xbf\xb8\n?\x13\x13/\xc5\xba\xc6Hk\xf8Q/w%\x99\xd38\xd4\xedm\xa3\xec\xc3\x9e*\xb7Fh\xc2\xe8\xdf\x8c\xdd\x1c?\\\x16\x12\xd4\xf3g\xdaMtJ|\xd5\xf5\x1bB\x1d\xfbF9 ;z\xa5G\xaf\xf4o\xc3+5\xe7O\x86{\xea\x15\xb5\xc4O\x9dr\x92\x92\x9d\xd3i\xa2\x9a.i\xd4\xb4\xf1\xf3y95\xc5oz\x172y\xcd\xcc]\x86\xbc\x10\x8f\x97\x9f\xc5\xcb\xe9\x90\xb9\x9d\xb1\xd1\xc9\xdf\xe5]\xe6\xc2\x0bRa\xe6.?oWY\xd6\xae\xa2\x9c]^\xc6\xae1\x8f\xaf\xab$[W\x98\xabk1SWI\x9e\xae1\xcc\xd2U\x92\xa3+\xc8\xd0U\x98\x9f\xcb\xcb\xce\xb5\x84\x9b\xcb\xcf\xc3U\x80\x85+\x8a\x83+\x8do+\x93m\xab,\xd7\x96\xeb\xbc\xa4(\xcfVy\x96\xad\xa2\x1c[q\x0c[E\xf9\xb5|\xecZ\x85\xb9\xb5\\\xccZc,\xaf\xd6RV-\xce\xa0\x85\x08\xc49\xb52\x18\xb5\x1c|Z\xde%\xde\xcb\xa5\x15^\xff\xcb\xf1h\xf9X\xb4\xfc\xf5(\xca\xa0\xe5\xe3\xcf*\xc4\x9e\x95\xc7\x9d\x85\xcc$l)/\xcb\x9b5\xa2\xacY\xb9\x9cYAR(\x0f_V\x14[\x16N\x9e\x93\xc6\x94\x85\xcb\xb0\x883\xb29\xb2b;#\x86\x1f\xcb\xdf\xee(n\xacDf,\x9bH\xa4\x00+V\x90\x13\xcb\xcf\x88\x15\xe2\xc3r\xf6R\n\x17V\x0c\x13\x16\xc6\x83\x95\xc9\x82\x15\xc9\x81\xb5\x8c\x01\xcb\xc19\x15\xc3~U\x90\xfb\xcaQ\x0bK\xd3\xb2X\xaf0\x96\xab\x82\x1cW8\xc3U\x16\xbf\x15\xc6gU\x9a\xcd\xca\xcbe\x85\x91\xfc`\xcdu\xf8\xea\xae\x98\xf2\x8f<\xae\xa91\xfd`\xc2\xc92\xe5\x0dZh\xd5/\xc40\xe5\xe6\x97\x8a\xaf\n\x1a\"Y\xce,\xa5cL\xe5\xe3\xe2\x95r\xb2J\xc5\xd7\x7f)\xa3T\x98O*\xbe\x0eh\x1f\x96c\x92r\xf0H9Y\xa4\xe2+\x9e\xcc \xe5\xe7\x8fr\x16\xecN\x87\xf2\xf7ca\xde(s\xd1t\xb2F\xf98\xa3\x82\x8d\xc4\xf3\x9fb\x1b\x9a\xc9\x15571\xc8\x14\xe5\xe2\x8923\xb4\x16\xb2D-\xb0\xaca~\xa8\xf0\xb4+\xcb\x0d\x95\xc8\x0c\xe5l3\x04\xf3\x0e\xdd\xfcA\xde\xe9\x0cQ\x9d\x02\xc5\xd9\xa0|\xbcH~&\xa8B\xcd)\xc6\x01\xa5#\xe8\xd3\x18\xa0\xa2\xf8\x9f\n68\x99\xf9\xc9q\x86\x05z\xbbcy\x9f\xbc\xacO\xdef\x86sC\xb5NXB\xec\x84\x918\x05)\x9c\x16\xba\xc19\xe4M\xec\xaf\x86\xbcY\x8c}b\x11 nrW\xb44iSQ\xca&\x9c\xb0\xa9 ]\x93M\xd6T\x8e\xaaI\xdd\x8e\xa9%\x94\xa4iBI\x9a\xae\xcbR4!\x04M\xa5\xe9\x99\xd2\xc8\x99\x1c\x08\x16+1/\";\xd0\xfe8+%0\x1d\xb02\x13\x03\xcd&\x87\x7fV\x16\xa7\xe2\xcf\x1e\xf4\xda\xe5\xd0\xc2\xb30\x87\xd0)\xe7x\x1bhdNa(\xab\xb0t^a\xe1\xcc\xc2\xe3m\xa0\xdaS2\xcb0*\xcf\xb0l\xa6aD\xaea\xf1l\xc3\xe3m\xa0\xfcI\xcaN\xcc\xceO,\x9d\xa1x\xbc\x0dT}\xe2r\x15\x0bg+\x1eo\x03=\xde\x06z\xbc\x0d\xf4x\x1bh8\xb31\"\x9d\xefx\x1bhL\xe7\xc4d;\x86{!*\xe319\xe7\xf1x\x1b\xa8xbr \x8f\xb7\x81\xe6gD\x1eo\x03M\xce\x96\xb4\xab|\xbc\x0d\xb4T&\xe5!r)S\xb2)#\xf3)\x932*\xe3s*\x8f\xb7\x81.\xc9\xb0,\x9bcy\xbc\x0d\xd4\x97m\x19\x97oy\xbc\x0dta\xfe\xa5wcz\xbc\x0d\xd4\x94^<'\xd3N\xa0\\Z\x1f/\xa9X~n&8\xb23\xc1\x9d\x9fi\xa5\x96\x15\xca\xd0\\|\xe4s\xbc\x0d42g\x13\x15w\xbc\x0d\xb4h\x0e\xe7\xf16\xd0\xf9)\x9c\xd5\x99\x90\xd7y\xbc\x0dTy\n\xe5y.\xb6\xcf\xe1l\xcfp\xafbr\xf22>\x93s>\x03Y\x9f\xa1\xbc\xcf\xe3m\xa0\xb9\x0d+\x96 \x9a\x97\x0b\x1a\x99\x0dZ\xbc\xe9\xc99\xa1Ni\xe3\xf16P\xf6\x1co\x03\xf5W\xb8tbi\xe1\xd4\xd2\xe3m\xa0 i\xa6\xff\xf5n\x03\x05S\xbb\xe7DM\xcd~\xce\x7f>^\x08 \x9f#\xf5\xaaY\x87\xbf!\xeaUORs\x1c\xe1*\" !\xa7Z\xbf\xb669\x9d\xfa\xae\x1eF\xc7\x0c\xa3?isK\xb9=\x94\xcd\\\x9e*\xc9\xaf\xec\x15;\xcaE\x93\x8c_\x0d\x8ciS\x12\xccMd\x8f\xd9\xae\x92\xd0\xb2\xebj\xa8\xd7\xfcRaV\x7f\xfb=\xdf\x86\xc3\xbf\xdd`Rq\xcf4\xe8\x97z\xe6\x07\x7f\xaa\xdd\xee0\xa2C\xee\x13\xc0+\x996\x0e\xebj\xc7\xaf\xc0\xe1.\x9f\xfcs\xbfo\xc4\x0d\xb9\xbb\xbe[\x93a\xe0\x1b;\xd6\x1f\x88\x1220\x92\xa80\xd7-\x13\xfc\xc9\xc0J\xa2\xf2\xdc[\xd1\x00\x86\x12\x9c8Jpb)\xa10\x9e\x12\xb20\x95\xa88\x0cg K\xb1\x96\xa8$?\xfe\x12\x121\x98\x10\x8f\xc3\x84T,&$\xe11\xc1\xb3\x82y0w\x90\x80\xbb\x8b\xc1fB*>\x13R0\x9a\xe0nd\x06V3qre`8q\xddv\xe18a1\x96\x13\x17\xe5\xc2w\xc2\x010\x9ePH\xe7\"\xb1\x9e\x10\x8d\xf7\x04\x0b\xf3 N\xdc'L{c\x1c\xfb \x91{\xcb\xc5\x18PT\x9aLYt\xe3@\xc1\x89\x05\x05\xb3\xc6\xe5\xf0\xa0\x10\x0e$\xe0\xb8PH\xc2\x86bo;\xf0\xa1\xaeW1\x8c\xa8\xfd\xaeSh\x12V\x14\xc0\x85\x17\x85p\x7fi#U\n7\nsF\xb0\x13;\n\x89us\xe8}2\x8e\x14\x95\xa2\xa6\xad\xbb\xb0\xa4`\xd4(Ta\xe5\x1f\xb9\x98R\xc8?\xa2\xf4\xe0K!<\x12`6\xae\x18\xce\x14\x02XSXP9O`p \xee\xd4)L\xde!\xe2\xc1\x9e\x82\x1f\x7f\n\x0bZ\xb7\x1c\x87\n\x91XTXP+O\x9f\x97\xc4\xa5\x82\x0f\x9b\n\xacRN|*,h\xd6\x02\x9c*D`U!\\\x95P\xceZL\xefg`WQy:\x96#\x8c_\x85 \x86\x15\xe2;\"\x1bqU\x0e\xd3\n\xa9\xb8V\xf0b[\xc1lA\x0e\xbe\x15\xf2\xd7\x8a\x18\xac+D\xf6:d`^\x9d\x02\xafI:\xee\x15\xc2\xfd\x02\x11}\x03\x01\x0c,\x84\x15Z>\xb1\xdd\x07\x8b\xf0\xb0^q\x11\xf8P\x88\xc0\xc5\xc2a\x1a[\x10#\x0b\xd98Y\x88\xc7\xca\xc2\xe1\xba#\x0d7\xeb\x15g\xa6\xd9\xa5`g!\x8c\x9f\x85\xb8N\x88\xb1\xea\x90\xd0Q\xc5\xf0\xb4\xe0\xc2\xd4B\x1c\xae\x16\xc2\xed\x8fiSQ\x8c-\xc4\xe1l!\x06k\x0b\x11\x0d\xc8\xc5\xdcZ\x02\xd1,\xae\xc58\\K\x92\xc4\xe5:\xb1\xb8P\x1a\x8f\x0b\x0eL.\x14\xc6\xe5\xc2\xbcEF\xb0\xb9`\xf9tI\xf8\\K\x16\xc3\xeb\xba0\xbaP\x00\xa7\x8b)\x06\x8e\xd5\x85\\\xbc\xae%\x0d\xc3\xefB\x0c\x86\x17\x9f-\x1e\x98\xa1\x13\xe0(\xa0\x8d\xfaWB\xde\xfb\xf3W\xb2nq\x10\xc7s\xeaB\xa6\xdf\x14\xc3\xfag\x16`\xa3F\x12\xd8\xef\x02\x99\xaf\xe9B\xdd^\x1a\xbb\xae\xc8:\xb8\xb0\xe3\x88\xc4i?rW\xd5\xedW\x9f\xeb\x0d\x99n\x8d3\x9d\xad\xf1\xce\x02\x03T\x86_\xce]r\xb1p\xcf\xab\x1b\xfbJ\xeb\x18\xc4x\x85\x0c&\x7f;\x01\xfe}\xdeuM\xbae\xec\xba\xc6e\x17\xbb\xae\xd1 \x15\xe8\x1f\xea\xf6\xa6[d\x03\xdbn\xbc\xe4\xab\xd4e\xc2\xc5\xdd)_ =\xaetI\xb0\xbfi\xeb\xe2{\xfb\xbd2\xfd\xd3\x17$\xd5x\\\xcaZ)\xed\xe3\"\xca^c\xa6\x96in\x00\xc88,\x14\xcb*GN\xe3k\xb2\xbe\xfb\xc7o\xbe\x92x*\x1d\x95\xe5\x17e{\xd4\xd3\xdc\xbe\x1c\xfa\xf5\x81[\x84\x96e\x1f7j\x8b\x0c\x87O\xa1\xe2$\xbeL\xf6\x80\xafq\x9ba|\xb4\xc6)e\xf9\x1b\xe7\x18\xaba\x94\x14.\xe1\x16\xa2\xab+\x7f\xb0\xe94?(6Q\xfd0\x0b\x0c\xcec}]\xeb\x89\xaa\xf3'\"\x00\x11\x8e\xb0\xf3'f\x90\xf8c\xd4\x0et\x96\x0f\xfe7\xc9\x0b\xe9\x0b\xd1h\x8a:v\xdd'\xd85\xd5\x1a\xcd:\x07\x1e]\xdd5\x84\x15\xec\x8b\xc9$vJ(.\x93\xd41z\x0d\xb5~\xd9\xb7\xf5\x979\xd8\x1eTcS\xa0\xabSDT\xf9\xf2\xbaj\xaav\x9d\xdb)\xf1M5\xcaE#\xdd\xf2\xb7\xcfw\xc4\xd7H\xdd^\x8dU?b\x11)\xfe\xf0\xbd)5\x11\x8f\xd6\xd2\xb9Hi\x8d\xe6\x0d\xbcbl\xbe\x9alTP\x16\x9f@|\x1b\xad6\xdeu\xd0\x17SS\xd5\xb5x\xd3\x8e\xbdr\xf0\xabw0\xb7K\xee\xacA`X\xa1\xfb\xaa\x1d\xa9SSm\xaa\xb1\xc2\xea\xa5\xd5Jn\x0f\xaa\x9e\x87 \xb5\x02\xc5\x8f\xa6\x90P\xb3\xd4\x06\xe99\x89M=\x8c\x1cd\xbe\xab\xfa\xb1^\xef\x9bJq\xa4\x110\xf5\\\x9d\xf6\x969\x87Hp\x83\x11'h\x12\x05\xf0X;\xfe2\x8a\x9c\x06\xdf\x12\xe78\xa1q,7\xee\xa5\xc6\xb1\xccx\x97\x98\xc0\x8d\xf6\xaa\x17I\xeb\xe3\xc8F8\xaeb\xc7U\xec\xb8\x8a\x1dW1\xa7\xac\xff\x0c\xab\x98W\xdf\xbc#\x10n\x8f\xd5\x165\xa8@~\xdd\xd7\xf7UC\xda\x91/+\xd6\xcb\x88@\xf2eMv#O\x91\xaeQz\x87\x19q9i\xad\x91\xcf,\x86\x8e\xdb5\x16\x9fF\xceQ\x00\x86}=\xb2\xe0\x1bK\xe2U\xc4\xab\xa7\xb2r\x1bn\xaao\x85DZ$\x04\xc6\x9f\x0fN\x89^\xaf\xed\x15:z\x85T\xc2;\x8ftH8\x97Xh9\x08\xac\xd4\xfcI\x99\x97\xbf\x91#\xc2\xc3\xb6f\x99'\xe0\x17e\xb7\xa6\x0c\xc8M\xc3\xb0\xa9\x1e\x123\x04\xfcGa.\xad(J\xa8/\x14\x932!\x89\xe7\x9c\x12\x91\x8b\xc9\x8d\xf2\x1diX\xfc\xa2j\xa1Z3\xe7\xdbn\xf0\x19u\x04\xec\xddo\xf7\xb9\xe5\xb1\x8f\xaeUl\x94 \xd8b\x14s\xdd\xba\xae\xa6tB\xe6s)yy\xb6\xbc\x1b*\xc9.\xc71\x14\x8e\x05k\xf1\x84\xb3\x92w\xf8\xe3\x1dS\x1eAJ\xfc,4z\xafx\xba\x8f\x8c\xfa\xb0\x81\x9a32\xe7, \xd6\xd1t\xdc\xb6|\xd8\xec\x9e{\xfb\xee\xe2\xcdKF\xd2 B]\x9c\xed\xa0fK\xe4Y+=\xba\x89\x04L$\xf9\xe00vn\xc0\xedB&\x04\x1d\xd5\xd7_\xf7u\xcf\x95\xe2\xb6\xbb\xed\x18\xe2:62\x82/8z\\\xe4\xb5+&\xa2I\x9a\x83@\xbf\xc34&9\x16\"\xe3\x1f\xaa\xacc\xf8\xe3\x18\xfeP\xff\xfe[\x0b\x7f\x88\x8a\xea>\x9d\xcf\xbbrzw\xaa\x80\x17\x98\x04\xc5\xbd\x8b\xf5\xee\x92]\xbai\x0dX\x14\xf7\xf0S_\xc7E\x19\xac!_Ht=\x93Z\x1b\xf2B\x14\xd7~rk\xe7\xb2\xe7^\xf4F'\x95\xb5w\xcd\x0b-_\xcb\x89\xab\x1d\xce\x98\x9f\xb2z Y\xb5\x9b\x94z\x11\x1d5\x13\x8f\x08\xf3\x12Q\x8fy\x14\xd4\x0b\xc9\xa7QR\x840\xed\xf4b\xc2\xe9ET\xd3t\x03\x8c\xf5X\x98dz \xbd\xb4\x8b\xf45H,\xbd\x94R\x9aZqD\x9c\x97Lz \x8d\xb4\x9f.\xba\x00Qt\x14Et\x1a\x15t& \xf4R\xfag@O\xef]{\xc3\xa2\x94\xcf\xe5\xc9\x9e\x8b\xd2<\xc7\x11\xe6\x10\x08\x99\xc2\x18\x99X\x94La\x9c\x8c\x1f)S\x1c+\xe3F\xcbp\xc3\x15\x85\x97Y\x8e\x98A\x851\x14\x8d\x033\x93\x85\x9aq\xe2f\x82.\x85\x17;\x13\xe7q\x94\xc3\xcf\xf8\x114\xe1\xda\x14E\xd1\xf8q4\xc5\x904\xb9X\x1aK\x1c\xf3hP\xe7\xa1,\x9eFL#\xa4A\xb9\x98\x9a\x08 \x89\x17W\x13\x89\xacq\xa6\xe7'\xa2k\xdcr\x90\x9c\xe5l\x8cMJ\xe7\xc4\xe0l\xc2\xbd\x10\x85\xb5IF\xdb\xe0\x19\xdd\x05\x107\x11\x98\x9b\x10\xea&\x8c\xbb\xf1\xf6Z\n\xf6&\x0e}\x83\xe3o\xb2\x118\xd1\x18\x9c\xa5(\x1cw7E!q\x8abqN\xb3\x13^7\xb3\xec\x8aiG0\x0b\x12\xae\x826Ss\xed\x05\\\xab\xe7\x13\xb6\xa5\xd0J\xce2\n\x9a!\x88\x9d\x0e\xc98;\xb5\x1a\x97d\x9eQ\xe8\xe7\xb8\x08\x08\xb9\x12\xc1\xadC*\xa5Z\xe6\xd43\xa4Y\x0e\x84\xd7}\x086&\x82:\xad\xd8\xc4\x84\x90O\xe0\xac\xad\xbf\x83\xb3f\xad!\x0b\xf1\x05\\\x9e\xc0\xc2\xda\xe6\xceiC\x9c\xd3\x03p\xd7\"k\xa2+r\xcc\xb5?lp\xc3\x8b\xbcf\x17\xc0\xba\xef\xb1B*\xaf\\\xfc(\x04\xd6S\xe4i\x0e\xf2d_\xf68\x99 \xe5\x9e\xc7\x08C\x97e\xe3\x16Y\xb7\xc9 \xca\xb9\xdd\xce\x12\xb2p\xd7\x0d\xe1\x9dw|\xa5\xac\xb9\xb4|\x17n\x08r\xedl!\xbc\x1b\x87\x12\xd5\xcf\xdd\x99C\xa0\x0d(\xa1\x0e\xb6K\x07\x17\x99\x8ec\xb7\x0e\xa1\x83\xdf0\x8d\x8eg?\x05\x11\x14:\xa1=\x17d-\xc1\xa88\xdb#V\x8a\ns\xe8D68\xf7\x8cl\xd9\xba\x1d\x10e78\x82n'\xd0\xe0\x98\xc6\xa4\xae\xf1\xa8\x10\x9f\xb7>?\xee\x9d?\x94i\xcbR\x0f\xc0\xad\xda\xfc\x00\xc4\x15 \x80\x88z\x95\xf2 \xf8\x83\xd3\xf2,\x8c\x0cX\x9f:\x9d\x99\xb4\x08\x81\"D\x8d\x15\xd8l\x96T\x9f\xc2\x83!\x04\x1f\xdd\x9b\xa2\xee\x8d'9\x8b?K\xfd\x1f\x08\x8b6\\`\xf5\xe1\xa2\xf1\x1b\xafb|+\xb6\xc4\xcf\xabR=\xccX\x92O\xe4\xe1\xab\xd9\xe3\xa0\xbb\xfc\xa1[\xd7\xd5\xbc\xb7\xc1\x10\xb8L\xeb\x9c\xce\x92Q\x05\xee_0S?@5\xa7u\xc2\x86\xdc\x93\x86\x8e\"\x0b\x8eU\xe3X\xad\xef\xd43qeB\xe8\xfahd\x8dH \xe5w\xe4\xb6n\xbfk\xba\xf5\xa7\x93\xe9oo\xda\x8d\xf1\x97Wwd\xfd\xe9\xe2\x0b]oQ)\xafIS\xdf\x93\xfe\xe2\x8b\x91\x95\xf3S5\x12\xba\x15\xec\xabv\xa8D\x12\xd8\xb6z\xa0\x1b+I&\xbd\x1f\x18:\xe5\x8e\x0cDLL\x87k\xf6\xfb\xa2\xae\x99\"KZR\xe5O>\xeb\xa1Z\xc6G0\"6\x98\xc0;kBz]\x10R\xe0\x03\x15\x94\x84\x15\x14\x04\x16x\xa0\x05Y\xe0\x82r\xf0\x82\x10\xc0`!\xc4\xa04\xc8\xc0\x033(\x0d4pB\x0d\xb2\xc1\x06\x96\xbc\x8a\xb5\x15)\xa7(\xe0 \x1brP\x1ct\x90\x05;(\x0f<(\x08=(\x0d>(\x08?\x88\x01 \x14\x84 \xb8A\x08y0\x04K\x18\x06K\xe0KV\x10\x98\x90\x0bM\xb0\xc4\xd9P\x85\xc5`\x05\x87\x1b\xebY\x8a=\xaekh\x95^\x08[\xb0\x0d\x97\x841\xf0\xfeW~\xf7\xd5\xa00x\x01\x83/\x14\x010\x14\x860\xd8 \x86l\x18\x83&k\xb4 \x0dy\xa0\x86@\xa6\xbf\x13\xd8\x10\x01m\xc0/\xaa\x8f\x877\xe0\xdf\xff\x05o\xfb\"\x90Cl\xe3C@\x07\x7fK\x83`\x87$\xb8\x83\x9d\xdc\x9b y\x08\x80\x1e|\xb0\x07?\xf0\xc1\xd9+\xb1\xe0\x870\xfc\xc1\x06@dA \xa2@\x10K`\x10xW\x04\xa1\x10\xc5\xc0\x10\x8e\xf2\x0dM*\n\x89(\x0e\x8a(\x0c\x8b(\x0b\x8c\xf0@#lp\x84\x0d\x8f(\x05\x90(\x08\x91(\x0d\x92\x88\x85ID\x00%\xa2\xa1\x12q` \x04.\x81\xdf\xcd\x1f\x9bT\xef\x87LD\x83&\xa2`\x13V\xe5KB'\x8a\x83'J\xc2'J\x02(\xf2\xc6;\x08\xa2\x08\xc3(f \x85\xef,Y\x8b\x1a\xdag$2\xda8\xbf\xc2\x06\x9d\xce;~&t\xda>\xe8\xc7:\xb1\xf9N\xfc\xf4L\xe1\xe1\xe1\xa6EFn\xbfg\x9b\x14\xa6OF\xb0\xd7{&\xa7\x1c\xe6G\x1f\xcc\x8d\xda\x91\x08\xb2q\xc2\x8eA\xc6\x03\x84U\x91\x83\x0e\xe76.e\xd3\xe7\xcb~(v\xbc\xcd\x9f\xd0!\xb7\xab2\x05\x930\xb2\xd3/\x9c\xcau\xf1%\xf9N\x80\x8ctuqY ]\xc5\xaf\x9bn\xfdI\xc8\x92J\xfb\xe5\xae\x1a\xeeR\xce)\xa9 \xe5\xc0\x83\x9a\xe9\xbb\xd5\xd4g\x1b2\xec\xaa k\x94\xa8\x13]?\xd8\xebS\xd6\xf8\xabnC\x14Y\xa6\x98\x98\xe4/\xdb>\xf0\xd3Y*O\xd63\xe2\x0c]\x13\xf2TX\x196\xb6'r\xa3\xf0T\xbc\xdcW\x9f/\x13\x0f{\xb5\xd9C{\xb4\xdb\x8f\xbb\xfd\x14\xe5WN\xc4\x9e\x0e\xd0t\xb7\xb7\xa4\x87gT\xbb\xb8\xd0\xe7+\xf8\x99\x1d3)R\xda\xae\xfdjCF\xd2o\xeb\xb6\x1e\xc6z\xad\x9cC\x1f\xd8\xe0 \xc9z\xaa\x1cs\xd4\xf8\xe3\xa6\x90\xd4\xfaR\x15\x84\xd8/\xf3\xac[}\xfdq\x8e\x95G\xe4$\\\xf9\xc5}\xf8\xeb>\x15W?\xc7\x1a\xc1\x1fGS\xf8\xe3m\x10\x7f\xfc\xcd\xe2\x8f\xf3\xcc\x9c?\x81\x16\xf2\xc7s<\xce\x9f\x08)\xa1\x80\xa2|\x96.C\xfe\xca\xbb\xd2\x06\xf9\x13Z\x07\xf4'\xdc\x92\xc5k\x98W\xc7\xb0\xfag/q\xea\x13j\xd8\xf2#m\xbb\x0f\x95\xecC\xebG\xeb@\x1b\x02\x95\xcbMH\xd4\x84I'_\xa4%\x86J\x8f5\xfdt\x8al\x92\xac~=\xa5\xfaA\xd4\x8at\x8a\xe6X\xc8\x12\xdd\xe5$g z\xd6\xeaS-\x01\xaf\xe7\xd7&\xcb\xa3\x99\xd9\xe5X\x98 \x18]\xb0L\xf8c\xb9\xf5v\xb1\xa3\x95\x93\x1e\x85.\x1b\x8f\xcc\x89\xf9 \x0eG\xe6\xc4\x8c\xa4\x86#sbZ\"Cf\x1aC\xe1$\x86\x8c\x14\x86\xd2 \x0c\xc5\xd2\x17\xca&/\x14K]\x08'.\x14K[82'\x1e\x99\x13s\x92\x0f\x8e\xcc\x89yI\x061d\x82G\xe6D\xe592'\xc2\x919\xd1\nk\x84\xd2\x05\n%\x0b\x1c\x99\x13\xc5S2A\xe0\xc8\x9cxdN\xd4\xa5\xc4%\x01D\xa4\x00\x1c\x99\x13\x97\x1f\xfc/\x1f\xdd\xe0\xa1\x7f\xe8\xc8_\x9a\xef\xb1\xde\x92a\xac\xb6\xbb\x94@\xa2\x1e\xdf\xac\xe7\x10\xcc\xae'\xf7u\xb7\x1f\xf81\xe4\n\xbe\xa7;Hv\x169\xc0\xef\xe1\xeb\x13\xa8\xc7\xa7\xbc\xdb?\xb3\xbf2\x15\xd9\xd4\x1a\xd8lf\xcd\x00\x11\xf4\x99\xea\xa8\xdf\x91\x02\xf7\xdd8y\xa3\xa2\xc4\x9f\xaaa|\xd5m\xb7\xf5\xa8\x16\xae\x198\xf8\xfaD3\xb3\xb4F\xd4\x1b\x1d\xea\x81\x95$\x97\x9f#\xc4\xf2\x08\xb1\x0c\x1dx\x1c!\x96\x7f\x0b\x10\xcb\xe9\xb8G\xe4\x81p\x01\x13\x9a\x92\xfb\x8dk2\x0c|\xe9@\xce\x03@\xc4\x83N\x0c\xd34\xd5\x06\xeav\xdd\xec7\xd4\xe3\xeb\x06\xe2\x12-\xca\x17\x8b\xc5`t\x14\xfbT-@Ja\xa8p\x16\x9ehG\xc2\xe8\x8czR\x0d\xf0Swk\xa4a\xf1\xba\x9cX\xc1!e\xe4\xe5=Y'\x93t\xe6\xac\xea\x15U+\x19AQ\xf5\xcd\xea\xeb\xafO\xe8\xff\xfc\xd3\xea\x9f\xd9\x7f\xff\x99}\x81\x8f\xcb\x9c\x19\x13:\x86\x937|\xc1\xf8\x85\xb3\x07P\x97\xc1\xbc\xc4\xefb\x8eB\x8f\xd5\xed\xa0\x1ciro[:\xeb\x0ci?\xf0e~C\xd8\xe5YJ\x02\xcf\xba\x7f\xd8\x8d\xdd\x8a1\x13\x0c\xf5|e\xd8\xabn\xbb\xab\xd6\xe3w\xf5xJ\x8d+7!\x96\xf1\xb4\x0d&\xf92\xf6\xd5\xe5u=\x0e\x97\xec\xea?\xeb\xfc*&\xcb\x854\x9a\xedF\x8d\xa0e\xf8\xf0\x14\x14\xa3\x1d\x9c \xc2\x8c\xd7\xf1\x9b Y\xae\x0ea!Kj\\\xae\xeb\x91/,s\x87\xd7\x8c\xc3\x8a\x85\xfd\xc6\x0eH;\xec{2\x1d\x919\xc7\x93\x0d\x1c\xed?\xe5Pm\xdd\xb5C\xcdo\xac\xac\xe8V\xb1\x1e\xc6\xfe\x81\xa9^\xd3\x00\xdf\x19\xd0\xc2XI3\x93\x1c\xd3\xacWl\x08\x81\xac\xbb\xe1a\x18\xc9v\x05\xa7\xbb\x1d\x9f\xf1T\x05\xf8 \x17WJ\x19\xc3\x9c\x84Je\x9bD\x8b\x8fy\"\x02\xab\\S\x7f\"\xea\xb7bc\xa0|\xa3\xa40R\x1d\xdd\xf7\x95`\xbd\xecv\xa4\xa5\xbd\xb4\xa5\xe5\x9d\xbf\x87\xea\x96\x9a\xe4Q\x1c\x1e\xd4\x0d\x93[mh\x93[\xf2\x19\xd6\xd5DH\xc2v\x19\xf50w;\xeb.\x11\xd6\x15\xa6\xb8\xee\xe7:\xc0\xd0\x89\xc8o}sC\x18\x11\x1c\x17T\xd1\xd6\xb0\xc3\x9a\x8a\xf7\xf10R\xfd\x17\xa7\"\xdco\x16\xa31\xaf\xbe\x0e}vh\xcd\x14\x17\x17\xb72\xce\xa35\xa9\x06\x9d\x91bO&\xca\xe8\xc9\xbf\xb1QYaer\x95\x7fi\xfdE-\xc9\xd0=\xb6{c\xb1\x06\xdae\xaf\xbb\xb5\xb0\x02B\xfc=\xe9\xb9\xdd\xe7wD\xcaD\x16\xbe\x84\xf2\x84`\xab\x16bJ\xbe\xb4\xff\xc4\xdd9\xce>\xa8\xd7\x83\xeb\x0dm)+\xf1A\xa5\xc92X\xacG\xf2e\xdcW\x8d\x15\x04\xa1[\xcc\x9d\xdc\xd0\x88#1\xfd\x1d!\x8f\xd5\xdd\xec\"\xc6\xcaS\x0f\xca)\x14\x9d\x03Bw\xbd\xddM-\x8c\xdd\xe5\xf4\xaf1\xdd\xae\x98\x89\xd7\xdd\xfau\xdd\x93\xf5x\xba\xff\xb2\x82\xd3\x01\xba\xdd\xae\x13\xab\x82)\xfe\x84\xab\xdf\xac\xc7\x9b\x8e0\xcb=\xa9\xc9\xaf\xfbZt2\xf5Ze\xe9\x8c\xcb\x91\x1fU\x89\x9f\x9e\xc2\x15\xff\xbfK\xea\xc4^\xb1~`\xa4=\xdc\x0d\x96}&N\x1b\xda[\xb8\xa8w|\x93\xa8\xb8\x97\xd3\x14\xf0\xa4\xa0\x87,\xf0K\xdf\x8f\\s\xae\xab\xf5\xa7\xcfU\xbf\x19\x8c\xb3N\xbcOO\xb7u\xdbq\x0fE\x99G\xd0\x93mw\xcf3\xe8y8\x91j#:\xbeb\x01xi\xffI\x19Yv84\x0d\x03\xed\xa77g\xe7\xc0\xde\x91}\xae\x9a[\xd9O\xaf\xffe\x05\xef\xc9\xcdK`g\x84/_\xbc \xf5nX1zQ\xb2\xdf\xae\xba\xfe\xf6\xc5\x9b\xb3\xf3\x0f\xf4\xcf_\xd1EH|\xf7J\xaa\xe7 R\xa9z\x10+\x00_\x0eh\x8f\xe9v\xf0\xbe\xea\xeb\xaa\x1det\xe3z?\x9d\x8dN\xae\xcb\xcc\xa3\xf9\xe1\xf5\xbf(\x91\xde\x15\\PO\x82\xcd\xc07g\xe7\xb4N'\xf0\xd0\xed\xd99\xf3l|w\xd5\xa0\x84\xa0\xae.\xbe\xbc\xea\xda\x9b\xfa\xf6\x8aOp\x16\x9fB\xfc$\xf1\xfd\x95\xac\xed\x8f\x1c\xfap%\xbb\x93\x96\xc6SJh\xa5\xe8VfC\xd6\xf5\x86-\x03<\x1c,&\xaa\x10$>1\x87\xd8\xa7\xa3\xff\xbc\xfaF\xf3j&.\xbc\xfdx\x97D\x01\xa7L\xa6\xd4`\xc7\xff\xbf\xa7\n\xf1\xf4\xff\xf7B\xc9\xb6xaW\xe9\x03+\x81V\xea\xe9\xf4\xb1{\xcf\xa6\xd6Gc3V\x0d\xd2|{\x810\x1c\x1bi\x1eX\xb7+\xe2\x04!\xab\xf2\x17:\xb78=Yw\xc3|l\xe6\xe3\xb0\x84\x8dm5\x8aK\xa7M\xb1r\x01\xf9\xae\xdb<<\x1d\xf4\x98\xbf\xd8'\xb1\xf1faU)U\"Vv}\xbdeg\xbfL\xd6\xb4%\xe9Zb%\xd7\xec\xaa\x87\xc1\x0c\x7f\xdd\x90i\xba\xdf\x10%B\xe2\xee\xc3\xef\xc9D\x9a|C\xf8\xaa|[\x0d\xd0\xd4\xdbz\x9czN\xdd\xe6*u\x17\x95\xd4\x98\xea\x8dT\x9fP{X+\xa6\xaas\xd9\x84\x08:ZE\xceLL\xabJ\xd7\xc8q\xd7\xdd\xc0r\x93\xc8}\xd5\xec9\xbd/[.\xbb\x0d\x87\x96l:\x96%*\xefdP\xc4pO\x80\xc7CT\xf1\xf2||V\x17\xba\x8ar\xa6\\2\x8c\xf5\x96\x05h\xeek\xba\x02n\x05\x07\xf1\xca\x98\x17Q\xc9\x80|\xa3\x83e8\xd8\x81:4L\xe7 \xd2\xf9Bt\x1b\xd2v[,\xd8\xe5\x8d\xa2a\xb5\x0d~\x16\n\x8f\xbd\xea\xeaV\xd9\xe5\xb3kXf\xea\xe4\xb6\xdb\xd6-7\xa9t(\xabVT\x02#\xd5x\xfb\xee\xe2\x0d\xc7\xa5\x88\xfd\xe3\xc4\x00X\xb5p\xd6\xca\x1b\x16&K\xad\xc6\xf6-a\xfc\x98\xce.dR\xa3a\x9e\xff\xd7\x0fp\xdb\xddvlg\xaa\x1fL\n\x80\x8b\xa8\x8f\xc5/\xc9\xc9\x9cE>SU\x8b\xf5\xed\x86\xa8:z[\x0d\x97lR&\xe6\xc1\xecm@\x85\xa8\x8d5\x08S\x11`\x90r\xdeVb\xf7#t\x9f\xed\xe7u7\xc9V:\x11 2\xbb\xee\x9a%v\xb1KA\xf6S\x8a-KS\x83n\xbd\xde\xf7\xaa-\xd9U\x0f\xcb\xae\x8b\xb1\x9a\xc6n\xa8\x18\xc8\xc8y\xba\x0d\xeb%a\x885u\x00\xa8\xc9\xdbU\x0f\xd2z\xdc\xd0M*\x9c\xdd\x18\xf2&Q3\xe5H\xb5^\xb3\xf1d\xab\xc3\xaez\x98?7\xbb\x80\xd9y\xda4\xfe.\xcb:\x1a\xbf\xc8\xfa<\xe36r/\xb6h\xec\xaf\x1b\xb9=d\x8alv\xb6\\\xc7\x9f[%\x0dd\x14vp\xfa\x9a9\xd3\x7f\xdfv\xe3\xdf\x8bt6n\x8f\xe9\"\xc7\x1c\xba\x9bI\x9fMYruC\xd6\x04UO\xd9\xce>v\xd4\\\xaaX\xdf\xcc]L\xd7\x03\xde]\xcfH=\xdd\xb5\xa5\x8d\xe1|\xc9\xc8\xde\x91\xd6\xcc\x05\xb0.x.S\xc9%\xb2\x81\x95\xc0\xea=]\xbfa\x8eX\xc7F\x94\x8e\xa6\n~\xd0j\xf7\x94\x079\xae\xab\xa6j\xd7\x84\xea\x8c\x1d\xb5\xafv\xd4\x1a\xf7u5\x12\xa5P\xb9\xbf\x01\xf2\xa5\xa6K\x18o\xcc\xfa\xae\xa2\x16\xb1\xb3\xb1\x1a\xca\xeem\x962\x88m\x13\xdb\n\xdcTu#>\x1a\xeb]\x8c\x1bpQ\xef\xe4\xac\x9fn\xcb\x19\xeb\x9d\x92X\xa8F\xdcYG0KUS\x83\xcc6]\x8a0f\xafu\xd3\xacS\xb1\xd6\xb7mG-f}\xa36\xb5\xde\xb4OG\xe9\x88\x8f\xf5n8\x81zEV\xf2\x87j\xa3G\xc05?\xe3\xea\xa2\xde\xbd&\xeb\xae\xaf\xc6\xae\xbf\xa2\xd5\xaa\xc7\x01v\xdd0\n\xacqDlz\xda\xcb\x1d\x97n\xed9.\xdd\xee\xa5\x9bY\xc2Z\xe5\xd6\x1b\xeb\xdd.\xd5\xfe\xf1o&\xe9\x9bMO\x86\xe9\x00[.,bU\x9a\xec\xaf(\x15\x8f\xcd\xca5\xc1H\xfb\x94.\xbe\xb0\x9b|\x7f4\xe1\xbbd\x9c\x9e\xed\xf9e\x82\xbda\xe7\xed=\xdbw}Wm\xd6\xd50FG\xc6\xbf{\xff\xee\xf4\xf5\xab\xd3\x0f\x17\xee\xf0\xb8\xf1\xcaw?\xbd{\xf5/\xae\x1f?\xfc\xeb\xdbW\xae\xdfN\xa7\x1f\xe7\xdb\x04\xfd\xa5\xe3\xda\xae5\xd2\x08\x91\\\xcb\xdf\xe68 \xfd\xf3\xc5\x97\x0f\xa4\xbf\xaf\xd7d\xee!x\x7f.k\xca\xb5S\x8d\xaf\xba\xeb\xf5\x12\xfe\x9d\xf4\x9d8 f)$\xb4\x1c\xb9f;%\xb0N\xb3\xda\xcb\xfe\xaaN\xd7/s\x03\x94 \x9eDD\xae\x1bv\xb6\xf3\xb9\xa2\xf6\xf4f\xba\xe5\x88i\xe0\x17\xe1\xb3\xb2\xfbu\xd8\x01!]\x0eDz\x85\xb3ZtD\xacZ\xd1?fW\xaa\x02y\xac<\xd3\xaeOt\xdc]\xdb<\xb8+u\x8a\xd6\xeatQ\xb58_\xfat\"\xb1ei+#\xe1\xc5{\xe6\xcf\xc5\x97\xf7\xdc1\x89\x0e\xc8\x8c_.Y\xcc:\xf5\xe4\x0fL=\x97\x82\xa4 \xea\xab\xcf\x98\x83\xb7E@\xf8FY\xc8E\xa2\xa1\xd9\x8e\xbe\xa6\xcex\xf4\x05e\xd6\xa3\xbf\x9fj/\xc4\xce~\xf0\xaewe\xac\x80\"\xf0\xfd\xf9+\xcb\x12\xa0\xadI\xb6\x06\xa8\x94CZ\x04H\xb2\nh\xf5\x0eg\x19 \xc9:\xa0\x95;\x9c\x85\x00\xdbJ\xb8\x96u\xdbVL3V\xfc\x93N\xcaI\xf9,\xd5\x9b>\x13\xf2T\xfd\x0b\xda\xa6D\x92\x92\xf1\xcbD\x01d\xda\x8c(\xcf\xda$9\x81\x98p\x873\xda\xe1 \x0d\x0d\x94\xa7\xa2\x81<:\x1a\xc8\xa3\xa4\xc1\xa7(Z\xc9bD5P\x9c\xac\x06J\x12\xd6@\x14i\x0d\x94$\xae\x01\xef\x8d;y\x046\xd8\x1cGo\xdd\xe1\xa6&Hk\x03\xd9\xd46\x88@\xec\xee\x9d\xc5\x847\xe0\xde\x8ay\x97x/\xb61\xbc\xfe/$\xc1\xc1\xcc\x9e\xf3&\x9eP=\xf2\x08q\x0ca\x8c\x1e\x07\xbd\x8f\xa7\x081\x0e\x94&\xc7\x01\x84 \x07\xf2Ir\x0ci#r3O\x1em\x0e\x84\xd8d\xc0w?O\x04\x85\x0e\xb8.\x06I\xa0\xd2q\xcb\xb0\xa8\x14\xb2hu \xa13B\xf4:\x10lw\x90f\x07\xd2\xa8v\x00\xbde\"\x93r\x07B\xb4;\x10\xb8\xbd't\x7f\x8f\xa7\x97bix \x82\x8a\x07\xd0{|\xb2(y \x8e\x96\x07\x16Q\xf3\x80\xb3c\x82\x14=P\x8e\xa6\x07\xdc\xb5\xb04\xad(e\x0fd\xd2\xf6\x18\xa2\xb0\x9b~\n\x13\xf9@a2\x1f\xf0\xdf\xf7\x83\xdd\xf8\x83\xdd\xf9S\x8a\xdc\x07J\x12\xfc@q\x92\x1f\x88&\xfa\x81\x18\xb2\x1f\x88'\xfc\x81H\xd2\x1f\xc0\xef\x00\xc2o\x85\x89\xa7\x88 \xdd\x03\x14M\x02\x04qD@\x805\xa3$!\x10\xe4\x92\x02\x19\xb2\x90\xfb\x81J\xd2\x04AQ\xaa \xc8\xd6\x87 e\x10D\xd0\x06\x81vW\x10J\x1f\x04\xd1Ah;^\x9eE%d\xd9\xf0M]\xb5\x06\x9d\x10@.\xa5\x90!\x8cW\xc9\xa6\x15\x02\x1f\xb5\x108\x0e\\\xc7G8w\x1e\x1dG\xaa\xde=\xa8\xff\x18\xd5Um\xfe\x1c\xf6\x84\xd8{P\xeam\x93|\x02\xdcA\x10>A-U\x8c\x87\xa6\x08\xa6b\\TE\x10\x9c]\xf2YNY\xe4\x10X\xb56m\x11DTg)}\x91%H9\xc2\xb2(\x8c\xa0\x18\x8d\x11DP\x19A>\x9d\x11\x04{\xae(\xad\x11`\xd4F\x00\x05\xe8\x8d\x0cy\xdc\xff\xeelo\xbe\x18\xcd\x11\x14\xa5:\x82D\xba#\xf0cq\xb2h\x8f\x14A\x13\x01\xd2\xff\xc7\xde\xbbu\xc7\x8dci\xa2\xef\xf9+0~h\xdb\xddr\xb8\x9cU\x95\xb3Z3\xee\xd5\xb2,g\xaa\xcb\x17\x1d)\x94\xd9uj\xd5 A\x0cD\x88m\x06\x19\xc5\x8b\xac\xa8>\xf9\xdfg\x01\x1b A\\\x08\x90\x84\x9c\x995\xc0\x8b\xad \xb9q\x076\xf6\xf7\xed\x0d9\xf4\x11MS\xc2\x1fyQ\x12y\xb1[N\"\xff\xbb\x96H\x89\xfc3\x035q\x90\x89\xf8\x8e\xf83\x0fU/\x17\xd3\xa637\x1c\x9f\xd1\xff\xc6\xba\xba\x9b\xfdn,\xaf\x0f1\xdd&\xfb\xda\xc8B\xe6\xb8\xd9\x08\xb7\x1aY\x9e\xbfG\xcdLGX\x83\x13\xac\xb1\x0d-\xce\xaf&o\xc3@N\xaff\x87W_gW\xc5\xd1\xd5X'\xfbz1\xdb\xb9\x95\xf9Z\xcaSa\x82ck\x1d\xd0\xa9\xd5\xec\xd0:\xcb\x99U\xb8\xafJ\xf2\x06\x1cY5'Vc\x8f\x98\x86SP\xc7\xd5\xe9N\xab\x93\x1dVe\x17U\xb9\xad&9\xab\x8epT5\x8fn\x16\x9c\x02\xd4 \xf3R!|PYE\xc5\x06\xc6g\xf07\xdd\xa2\xc1\x97\x146\x85o\x0fZ\xaf\xb3\xa5\x90\x9du\x113\xa3\xb1u\x90l6$\xa9\xd3\x16\xdez\xb2\xc5\xd5\xbeL\x13\xf2\xa4U\x7f`\x99l\xc7;\\\x08Q\xec\x08\xda\xa5\xbb4ov<[A\xb5\xe8\xe8\x12;\xb2\xdb\x17Ef\xde\xe9\xbe'5\xd32\x7fJ\xeb\xbb\xe5C5\x81w\xffH\xc1Y\x96\x0f\x96\xa0,\xf5\x03h\x13J\xbb*\x87c\xfe-\xfbc\x95ja\x0e\xfd|\x02\xfc\x89\xfa\x96C\xd4\x1e\x97\xf5\xaa\"\xf5\xea\x8e\xe0\xb5\xd9Os\x1c\x19\xab\xa8\xb1\x95\x89ece\x0fq\xb2\xf5*\"\xd7\xb9\xd1z^\xe4+\xd4\x05.\xeb\x8a\xd4?\xb0\x1a\x7f\xa3\x19\x9e\xe7u\xba\x9bJ\xefX\xe3\x9a\xbc\xa0\xdf+o0\xe8@__E\x9a13\xcc\x8b\x12r\x15\x18\xb9\x8dY\x83\xeb\xb1H\x01\x8c\x7f\x96u\xba}<\xb8^\x8b4\xecK\x03\xc9\xdeT\xc8\xa7\xb9\x90\xbb\xc9\x1c\xeb\xba\xf2R\x7f}\x17\x89\x0d\x15\xf0\xd0\\\x85\xdcqP\x971\x15\x0b\xf3\x19Vk:\xed\x95w\xe9O\x81\xb3g\xb6y\\\x17e\x15X\xb0Z\xaf\xce\x04\xb5\xdf\x0b\xe6~\xfb\xdb\xbe$\xf7\xc6\xe53'\x0f\xf5\xea\xd1\xca\xd8\xae\xe8\x81\xe5\xe2\xfd>\xb0D6\xfe8\x953\xb0hr\x9f\xaeI\x9e\x90\xc0b\xdb\xfe\xef\xb6M\x83\xfeA\x17\xa2\xa2\"\xe5\x8a\x07\xf1\x08\x95\x7fo\x93\x87 \xdf\x8fx\xd8\x9a\x07Yhn\xe1\xe9J\xdf\xf3q\xe6\x1c\xa5\xa2\xf5\x0e\x18\xed\x8f\x86\x83\x86HVX\xc7\xb1\x1a\xce\xa43.\x1f*)\xf8\xee-\xf7\xa8\x81# \xe8\n\xff\xce\x8f&?\xb0\xcd\xfd_^\x19\x89K`-c\x9c\xf9,cg\x1dp\xd6*9\x10\xb8@\xe8'\xf2\xb4$\xe8\xbf\xe8A\x10oKB\xba\xd0\xa8\x06y\x10P\x92\x99\x01\x8c\xf91\xff\x87\x1d\xc19/=\x14\xf1d\xbf\xff\x01Ww\xdd\xf9\xbb3\xb3\x93\x8a\xd0b\xf5\xd5\x1c>X\xdfR]\xabg\x11\xaf\x08xB\xf4\xcfiL\xd6\xba\xa7\xd9I\xe2\xc4\x94\x9a=t\xcc\x82\xd0\xbc\xf1c\xd5\x0b\\Z\xc1\xba\x01\x8d\x92\xac\xee\x8b\x9a\xac\xec\x85\x83\xe4\xd4B\xdc9\xd2\xc4\xf2\xc2\xf6\xe7^\x19!\xcf\xcc\x90\x107\xf8\x86s&\xcaI\x8d\xf0aN\x10l\xf8\xec\xed\xea\xc3\xd5\xf7\xab\xe5\x9f/\xceV\xd7\x1f\xff\xf4\xf1\xd3O\x1f'|yqy\xf6\xe3\xa7\xe5\xd9\xb4/O?}\xf8p\xbe\x9c\xf4\xed\xa7\x8bOWm\xfc}[\xea\x85\xcf\x1f__\xf72\xd6O,B\xee\xfaC\xb5]rZ\x0c\xf8\x9e\xd0)\xcd\xad\xaa\x12\xdf\xd0\xeeL\xda\xa5v;3.FJ\xb2\xf6\xcd1\xfa\xb1\xa85\x1c\xcfS\x02\xb4\xf31\xba`\x9b'\xce\x86\xc5\xd8Nb\xfd4b@\xfbh\xf5\x90\xca\xa2\xc9\x0d\x07\xab~\xf2;J@\x92\xb2\xfe\xfd\xb7\x83\xef\xda\x8fu\xfd\xe4\xb9v\xa0\x11\xeb\x07r\x9ej\xba4\xa2\xd9\x91k\x87W\x93\xd7Q\xb1\x9fF\xb4\x06\x1a\xd9\"49\x0e\x95\xfd4f\\\x88\xe4?4E\xf2\xed(4\xbe\xb3\xd0\xd8\x0e\xf3<\xa8\x1a?1\x1f[\xd5W\x8d\x8c2\xc3\x8b\xfe\x15u\xd9X\xfa\xa9=\xc9\xd9\xf5\xfd~\x9aP\x12gSw\x85p\xb0\x82 \x8d\x19\x87\xfe\xebS\x8b\x01\xfbe\x1f\xb2\x05\xc6l\xa1t\x97\xea\xdfWC\x8f\xeaEM\x8e\xb8/\xe0.\x05\xafE\xf8/S\xd6\x06\x05\xb2#\x7fw\xa0\xa7e\x1e\xdeG\xa5\xfd\xd6\xf6\n\xd3\x10o\xa3\x86\xe8\xffe\xd4\x10\xbb\x145\xc4\xa8!\x0e%\xcf\xb5\x03\x8dX?\xd0\x08\xc5cD\xb3#\xff=\x00R\xd4\x10=\x92oG\xa1\xf1\x9d\x85\xc6vX\xd4\x10\xb54\xa1$\xce\xa6\x8e\x1a\xa2\xff\x16\xfa\x9b\xd0\x10\xd9\xb2\xb2\xba/\xea4\xdf\xae\xf6\xc5\x97\xe1\xb5\xce\xb3A\xfd\x96\x92n,}\xdd|\xbd\xa6\xf2\xc8\x1c]S\xd8w\xdc\xbc\x15\x86d:x\xce\xb8\x19\xb93\xbb\x0b\xc32`3m\x03Z\xc5 z\xe4\x97\x82\xca\xd8di\xc2\x88\x8f\xcc\xe1\xc8<&2\xaa\xf0\xac \xba\xec\x8a9A|\xfeZ\xe6l\xa9\x84\xab\x01\x02\x0d$\x8f<\x91g\xbe\xa8m(\xcf\xdd\xde3o4\"\x7fd\xe1=\x99\xd3\x88\x02\xa0\x91\x85@C\xcc)s\xf2\xe5S\x99\xd3\xc8\xba\xa0 \xf5A\xc3\x8c,s\xf2\\\x00\xd4$\x16\x04+{\xcb\x9c\xac\x9c.s\xfa\x9a\x85\xf3]\xba\xd4\xe4\xc7\x15\xf3\x16g\xe6\x94\xf9\x9e\xa2v\x90\xc6\xd4\x11\xd2\xe85n\xd2d\x9c0\x11\xf7\xcd\xed\xca\xe3\xee\xc3.\x8dn,4\xa9\xc1h\"\xebo\xff\xf8\xc7W\xff:\xe6\x93\x89\x0d\x87\xa65\x1eb\x01\xb5\x92\xfd\xb7\x7f\xfc\xee\xf3\xab_s1\xa7h\x0d\x17\xcdm\x96&\x7f\"\x87\x9e\xb1\xe439T\xd256\xe3\xf6\xfc\xa6\"\x10f\xef\xc7v\xe2{~\xedK\xd2\xea\xa7I\x8d<\xe5\xc0\xd6\xda\xc8\xf6eZ\x94i=z>=j\x19E\xe9|\n5rz\x8f\x9d\xd8#\xd7\xc1 \x8d3z~\x8c\\\x01G6\x10\x9a\xd0Hh\xda\xda7\xa1\xb1\xd0\x94\x06CSW\xbd\xafW\xc0\xf1\xeb]\xf0\xd5n\xeaZ7e\xa5\x9b\xd0\xb0\xe3V\x104g\x8d{\xf4\xd2\x8d\xe3\xf1\xb6_\x8d+\x96_\x91\xa8N^\xe4+\xb7\xa1\xdd3w\xbf\\o\x0f\x7f\xc7y\x9d\xe6d\xe5\xa7Q\xfbi\xd2\x1e\x1a\xb4\xf7b\xe8\xbf\x04z\xef\x10\x9e-\x08i\xc4\x1a\xe2\xbd\x1fxW\x1e\x8dj\x004v\xf5\x1f\xd5\x10h\\c\xa0\xf1k\xfd\xe3\x16g\xcc\xca\xee\xbb\xa6\xb3\x1b\x8f\\\xc2\xd0\xc8\xd5|\xdcr4\xaa\xd1\xfc\x16\x05H\x13V\xedG(\xcb\xb8\x15\xda\xb3\x00\x9eY\xfb\x18\xeeF\xe6\xe82\xca\xf9\xda\x87\xde\xd3]\xe2\x94\xf9\x1b\x9c0w\x03\x97\xd7\x03\xd8x\xac\xf2$\xff\x18\\\xd7d\xb7\x87P\xcf\x05\xda\xa5UF\xf0\x1aa\xf0q@\xe0\xe3 \x1b\x88$\x8aW\xbf\xa1\xac\xab\x9c}E\x0b\x1e\xe5\xd1\x02\x12\x83<\x8f\xf0\xb0\x1a\xf0\x1b\xc3;\x1aS\x0c\xef\xd8KC`\xe3\x90\n\xf5X\x91\xb6\xbc\xe0?\x8f\xc6\x1b\x86\xf4\xc6\x82w#a\xba\x91\x80\x9c?\xf46 d\xeb\xf5\xbf\x13\x1d\xf3\xc3\xbb\xbc\x91-\x8f\xae\xf2\x18\xe5\x8e\xedmD&\xc3\xdb\x9a\x13U\x1a\x91\x93\xa5:\xbd}\xd3\x17\xe6\xb1\x837\xc3\xdb\xb07 c\x01[\x14qF\xe8e\x8f\xb7\xfc*\x91\xae\xd1z\xa5\xea^\x90\xee#\x91~\x14\xd7+\x88K`\xa4K@Lk\x89y\x05a\x0cX\xedHc\xed.k'\x0d]\xa9\xd2\xe5\"\xae\x00\xa1\xff\x15\x97\x90T\x15\xdcA\x7f\x81\xb7\xe4\x12\xae`X\xc0sE\xc8\xdf\x1aR\xc2\xd5\x02T\x1cm \x82vEU#\xc2\xaeEg\xb7\xa8/\xd0y-\xdd9\xb8\xaf\x0f(Uu\xa2\xba\x8d.\x99\x17hW\x94D\xdcx/\x8f\x10\xc3v\xe7l\x14\x83\xfb\x98\xedH\xc2\xc4\xb3\xd6`\xff\xc9\x9b\xdd-\xdc\xf9-.\xdf\x97n\x83W\xcb/7\x14\xbb8d\xc5\x84\xa8\xaeQtd\xb2\x9b2\xd2\xba\xe2\xf7_\xa4\x15jr\x18Kk\xb8\x98\xfdK\xca\x8f7\xe6\xf9`\xb9)a\xcc\xf5C\x8a\x88\xf6\n\"Q\\\xe3\xadN\x7f\\|k\xbb\xb9\xa1\xbb\x04 zG\x1b\xeb\xfa8\xaf%\x7f\xfc\xf971\x88\xca\x8b\xeb\xc3\x0c\xb7\x9b\xd4\x0f\xab\xb2WHcA\xcd\x85E\x16%\xd99\xfa\xac\x83o)\xe2\x8dr\xb9\xf2(\x7f\xd0\xb5-kF\xbdvX\xf6o\xa2`Z\x9b<\x7f\x92bM\xaa=6G45\x08\xe7e\xfd\x88w\xf0Y\xbb\xba\x9d\x16k\xa2\xc85\x894i\x9e\xf6\x96\xe9U\xa5\x1d\xd5T\xb6O,_W\xdb<\xbdd\x93\x98-\x92\xd5\x91\xb8\"\xfb\xa9\xf4A\x89\xbf\xac\xb2b;E\xb8\xb6\xb6\xd2\x9e\xe0\xee\x12&\x8fI\x94\x15\xdb-)\xd1\xb3\x12\x7f\xe1\xc2\x9f/\xd0\x07v\x05\x9e\")/\xf2\x17kR\x93r\x97\xe6iU\xa7I\xef\x10Xl+Sy\x1f\xf7\x82\xcc]\xb5\xb5G\xd4p\x9d9\x86O\x1bZ\xfbC\x1a\xd4TL\xf7\x84\xca\x9f\xfd2\xd7n2\x11\x96g^\xaa\xd7\xf0\x8d\xa2\x90\x86+\x08\xc9i\x15vV\x16\x92\xbb\xca\x90\xbf\xa1\x19[\xef\xc0\xbfy\x0e\xe5f\x02\xab\xbb\xa2\xc9\xd6t\x8e\xd1\xca\x1bd%8/\xf24\xc1\x19\x1b\xad\xa6\xdc\x9e\x91\xc5vqD\x9b\x8dE(y\xb2xB\x0fn\xec\x92 ~[\xe0s\xfd\x8aa\x9a\xces\xb4\xa7\x0d\x99&\xe4\x08\xd5\x04\xef*\xd4T\x0d\xa6\xd5\x86\xf0d\xfb\x94\x9e\xca\xc5M\x83\xb7i\x8e\xcb\x03\xdd\xc0L-v\xd8\x13\xbe\xd0\xd7w\xe4`\xca\x8e<\xec\xe9v\x98\xd6\xa8.\x181\x84\xdf\x10A\xbb\x9e<\xb0\xae;\xc9\x0f\x0b\xf4C\xf1\x85\xdc\x93\xf2\x88M\xf4\xeb\xcb\xf7&\x8c\x11\x8c\x92TH}g\xdc-\xaa\xe4\x8e\xec\x08\xba\xb9\xab\xeb\xfd\xcd\x11\xfc[\xdd\xb0`ky\xc1\x9f\x1e\xb1\x11\x95\xe0\x1c\x15{X\xfc\xb2\x83\xc1\xa6ES\xb3\xe7\xa1O\x8dy\x91\xf2\x9e\xdd\xc8\x89k\xb4\xc3\xfb\n\x86\x07-9C\x9d\xf8^!\x1d\xc3\x11\xae\xd0\xa6`\x17\x90\x1f\x1b{\xe6\x9f\xd1\xf9\xa6+'\xed\xce}Y\xdc\xa7k\xb2n\xab\xc2\xec\x81\x15[\x89\x8c\xb1T\xff\x19\x9d\xe4\xe8\x87\xe5\xf2\x02}\x7f\xb6\xe47\x88\xd02\xc1tcwS\"\x8c\xfe\xa2\x0e\xd2\xe5aO\xfe\xfa\x97\xbf\x1a\x04\"aZ\xc9\xc5H\x80\xe5\x93\xb5\xe9\xbe,\xd6M\xc2\xae\xa7ew\xd2\x9aT\x8a\x7fF'\xddn\x07\xb7<\xb2K\xd8\xc1(\x96\xe0\x84]\x18]|n\xf6\xadm\xe8\x16W\xec\xfel\xf3\x145\x16\xf2\xfa\xf2=+\x11\xbb\x1c\xb6\xbe#;i,\xf3k3\xb1\xa8\x00\xfd\xff}\x91\xae\xe9\xa9\xd5(\x0c\x8a\xc3\xa6k\xc9\xee\xdf=\x12\x9fR\x89\xb8No\xd3,\xad\x0f('d\xdd^6L\x97\x95\xf2\xde\xa2\xe4\x149\xbf^\x96\xbd\xcef\xcd\x02=\xbb\xae\x88\x88\xb9E[\x83\x0e\x1f\xba:\xc0\xf8\xc19\xde\x9a\xebz[\x12\xfc\x99\xcey.r\xf1\xdcx\xe9LQ\x93c~\xd5m\x93'0\xd2i\xa9\xf9*\x914e\xc9L\x8a\xb2)\xcez\xe3\x0c\xb3@\xea68$V\xfb\xdbf\xc3\xee\x12\xc7\x159b\x1a:\\\x87L3bw\xa6\xb2\xdd\xbb\x9d\x1f\xb7d\x9b\xe6\xb9\xf9\x8ca\xbag\x1d\xf1\xa5f\x01\xe3\x16\xef\xd3j\x91\x14;\xf3\xfav\xc5fO\x05\xf6?:=su=@\xcf\xb8\xed\x05\x0c\xa80\xdd\x9e\xa3\x9db<\x12\xe9\xd68\xfdY\xb5\x18-\xa2\xbd\xe5\x1a\xcc\xd6\xfc\xde\xe3\x04Ud\x87\xf3:M4M\xdbr\x14\x1b\xdc\xe2\x07\xd1\x03\xf7\xfe\xff\xa1\xbdR\x99\x19\xea\xa5\x0d\\\xdb\xad\x85\x8ez[\xdc\x1b\xb7\xfe\xf6Zg\xd6#\xbd7\x86\xcbqs\x92\x1fn\xa4sC\x8epy\x9b\xd6%\x9dX\x03\xe5\xe1+\xa9\"\x0cgE\xbe\xe5\xf7\x96\xab\xddC\xd7;\xb6,Cynu\x95F\xceOh'\xda`\xba\x10\x03;KoY!\xf9j\\\xb5W\x12\xb3K\x92\x93\xcf/\x9b\x9c\xfeC\xf71\xe8\xdb\xca<\x93L[y\xb1AM\x0d\xcb\x89\x98\xa6\x15]\xc8p\xa7\x9aoINJ\\\xb3\xa2\xd6w\xc5\xba\x8d\x1cv\xa2\xad_\xd0%j\x1eg\x0f\x98\x0eQ\xf4\xea\x18]\xd0r\xd2\xf9\xc9\x8b\x8c\xe5P\xde\xa7\xff\xf2/\xc6M\xe5]Q\xa0MQ\xa0\xd7h\xb1X\xfc/\xc3\x0b\xb4\xe28?\x98\x1e\xe1\xfc\xb0\xa0\x99\xbe+\x8b\xdd\xb3MQ<7\xbd\xb4X\x98v\x8et\x83\x9e\xd1\xcf\xafYQ\x97\xc5\xb3\x7f\xa2\xdf?G\xffm\\\x15\xcd2~\xb6\xb5\xc5\xb7\x8e\xb6\xf8\x0f|\x8fg5\x06z\xcd\xf4\x1a*yb\xbd\xd3\xea\xd9\xbb\xa2X$\x19\xae*k\xb5\xa1(\xf4u\xa8\x85\xf4\x89)G\xad=\xda\x06\xf9\xbd\xa3A.\x0e\xf5\x1d=\xef\x18\x84B\x19\xde\x15\xc5\xb3\xc5b\xf1\xdc<\x08\xa09\x9eY\x9e\xb2!\xc2\x9aiL+\xd1\x0f\xcf\xa1\x91\xde\x9e]\x9d^\x9e_,?]>7\xdb\xb8\xba\x81d\xcb\x062\xb25\xcf\x1f\x1c\xcd\xf3}a\xb4^\xd0\xa69~\x8d\xfei\x7f\xbbxW\x14\xff\xbdX,~6\xbd\x86\xf3\xc3\x11U\xa1\xe8\xbb{P\x0f>\xe0\xb2\xba\xc3\x19m4[\x81\xcd\x0d\xa3\xe6i\xcc0\xdd(\xd9]\xe7\xbb.CV\x1c6h\xd9[\xff\xe35\xca\xd3\xcc2\x00m\xa5\xd0F\xda\x92\xdd\xef\x9f|nW1\xa1\xdc\xd2#\xf5^]k\x01\xe3<\x08f\x03\xa3)*\x02\x9f\x1aT\x82\x97\xf4\xfc\xb6`\x0f\xa8*\xf5\x94\xea\xdc\xed\xdaO\xf7\x05\x1e\x82Q\x11\x05=\xaaf\xd0.\xb7yv\x10\xa7\x0e\xed\x80\xd8\xaam\x08oj~C?;\x9b>}\xf9T\x15\xc8\x8f>\xa20p\xd6!|\x84=\xd9\x14\xc5\xe2\x16\x97\xac\x1a\x0f/\x0f\x8b\xbf?\x81V\x00\xad\xddt\x0ca\xd9>\xa1o\xd2E_y\xf8\x1fW\x9f>\xaa\xbf\xbd~\xfd\xfa\xb5\xa9_\xe8\xbb\xdd\xd9\x18\xf4\x98\x82\xdd\xe6\x0f\x9b6\x9c\x01\x9a\x8a\x08\\t\xdbdX\x0bB\xad\x0b\xa0/\xafI\xb7\xd9\x1e!\xb2\xbb%\xebu\xb7\xed\x1e\xf1=\\;QK\x1b\xe0\x865\xc0\xcd\xbf\xd3&\xb8\xe1\x87\xc0\x1euA4\xe8BL\xd7c\xa3\xa2\x8a\x93\xcft\xb6v\x87\x9fM\x9a\x11\xd3*)\xe6\xf5\x05)\xab\"\xb7\x0c|n\xc7`\xd70\xaeXO\xbcF\xafL\xd2\xdaW\x19\xcf\x90\xbf\xf9\xad\xdf\xea\x8c\x90%\xf7'\xac5\x9e\x1c\xa3'\xa6Y\xd0\xaf\xe2\x02\xea\xf1\xe4\xc8,\x89\xd5\xe0#\xdeQi\xff\x1b\x8a\xfao\x96Wi\x0d\x947}\xaaq\xbe\xe1\xeav\xbf\xef\xa1\xdf\xd2\n}!Y\xf6\xe2s^|\xc9\xd9\xfc\xbc\xc3\x15\xc2(i\xaa\xba\xd8\x19\x87q\x7f\xa0\x1d\x81\xb2\xa7\x8c\xbe\x8e$\xc13\xa5\x03*\xdfj\xd6\x166\xb8\xd4\x0cn\xd8\x80\x17c\xed\xae\xc8\xd6<\x88mW&f\xb9\xe1c\x14q[ \x1f\xa2\xaa4\x96E;6\xd13:\xebE\x13hGqa/\xfa\xeb_\xfe\xfa\xdc8\x8c\xe7\x8d\x87~&\xb6!\xc1\xaaO\x85\xbdZ|\xfb\xea\xdb\xea\x89\xb1\x93\xbb\xff[\x88X\xd6\x03\x8d\xc3^\x9ev&\xb7}I\xee\xd3\xa2\xa9\xf8\x1d\xa6\xe8\x1d=\xe33\x94\xbfB\xff\x86^\x1d\xa1\xb4~\n]\xf3\x85\xfd\xaa\x9d\xbfwd\x9db\xba\x16\xa9M\xc9\xd6\x0dQ\xeeV\xa5\x86\xd3\x11\x8b\x93\xde\xbb\xcat\xf1\x1eW5\xe7Y\xd1B(\xc2\xa0H\xe8\xf5k\xf4J\x8b/\xccJHu\xf8*\xadX\x8e\xf2vi\x02\\\xeb\xaf\x80;\xd7\x16Hu\xf0\x0c:\x0c\xa3\xda\x8a\x0d\xe9q\x11\xe2A\xa0t\xb0N\"9\xc9\x86N\x045T6\x8e\x0b6 \x9b\xdb\xa2\xc8\x086Y\xc8\\\xb3K$\x86\xd5u\xe0,\xe3\x19Vi\xbe\xcd\x18\x02\xfb\xa2\x03\x19\x8f\x10\xae\xaa\"I\xd9 \x94Yg\xcc\x02q\x0e\xa3y,L\x07\xa0!\xb3 V22\x86\xd6\xe4\x9ed\xb4\xe7\x99A\x8c\xddIp'\xa9\x05\x9a \xc2B\xb5A\x1d\x17<\x987d\x9b\xe6\x8c\xb3u\xd4\xfev\x96\xaf\x95_N\xefH\xf2y\xf9@7$\xab\xa4\xb7$K\xefI\xb9|0\x1c\x8b\xde\xe3\x9a\x94G\xfd[\x92w\x80\xab fU\xc3\"\x9a\xc3\xf5\xcb\xb0\x10\x8c1\xa7p\x1c\xb6\x855\x01a\xe6\x82\x04\xe4\xcau\xeb.z\xba\x05\xbbB\xdcb\xa8\xad^r\xe9\xa4\x1b\xa3\x8b\x8a\xd8\xb2\xe0\xe5\xe0\x1b\x9cj\xc6\x07\xfd\xbb\xd0\xb5y!\xad\x0b\x93\x9a\xd7d\x81~\xba#%\xc1\x15z_l+\x95\xafJ\xcbtd4\x18J\xca\xe3\x8e\xd4x\x8dk|\xd4\xe6\xc0\xd4\xf9~\xa1\xe5\x02\xeb\xf6\x13#\x85\xef\xdb\xc5\xabWG\xf4?\x7fX\xfc\x91\xfd\xfb\xc7\xf6+{\xbfu\xe4>\x17\x1c]\x92\x8c\xdc\xe3\xbcF\xf5\x03\x84\x9b\xef\x8fCQ+f\xa4\x96\xcb[\xe3m%\x11\x02\xe0\x8c\"\xe2\xceg\x05#\x143\xf5eM\x92bM\xd60\x08z%\xee\x91\x10\x07\xa9\x902 r\xf9\xd0R\x1f\xad\xcc\xc6\x8a\x8d\xd9 \xfcFi\xe17m3\xda\xe62\x9f\x05Y\x89\x8agi\xc5@*\x03\x1d\xb22\xf0!\xc7\x96\xd4\xb2\xf9\xd9\xb6<\xb3{\xd1\xc0\xd63\xc4\x11s\xf3'\xcd\x0c\xca\xc1\x0cG\xb1(\xad<\xca\xc1,|\xb9\x94&6\xa5\x10lf\xd5\x0d\xb5\x96\x1f\xa7\xd2\xc4\xaa\xf4o/7\xb3\xd2\xc2\xad\xf4\xcfB\xe3W\xfa\x13T\xdc\x0cK\x07O\xc5\xc4\xb2\x1cR\x1c-J\xe3\xa0\xc28\xac,\x0e\xf2-\x87\xc7\x06$\x97\x87\x97\x85u\xe9T\x0f\xed\xccK\x97f\xed\xa0%:\xb5kW\x93A\xb2\x1d\x18z\xcf]\xda\xaf\x0f\x0f\xd3]]HN.\xa6W\xd5!\xf94\x00$\x0fF\xa6g[@\xf2be\x8e\x90\xe8R\xb6\xfbi,7\xd3\xdd8c\xd8\x99S\xf8\x99\xfe5\x9c\xc4\xd1\xb4\x0f\x7f\xba(:Y\x9a\xa1y\x9a\x03LM\xdf\x86\x08\xcc\xd6\xf4\xe0k\x0e06}\n=\x83\xb5i\x91f\xe7m\xba\xcb\xe3\xbb5\xd6C\xdcM\xe7\xae\xa8\xf37\xfd\xf7\xf39\x1cN;\x8bs\xb2N9\x97\xcbics>F\x81<8\x9d:\xabsp[q\x99\xdc\xcc\xdcN\xe7\xfa\xee\x1a\xa5(<\xc3\xd3\xc5\xf1\x0c\xcd\xf2\x0c\xcc\xf3t0=gs=\xc3\xb2=}\xf8\x9e3\x18\x9fa9\x9f0Z\x1d\xac\xcf\xb0\xbcO\x0f\xe6gp\xee\xa7\x83\xfd9\x8d\xffi\x144\xc8 \x0d\xc2\n\xf5\xe4\x85\x1a\xbf\x1c\xc5\x15\x9d\xcd\x16\x0d\xcd\x17\xb53F\x03sF\x1f\x835\x1a\x987\xea\xcb\x1c\x0d\xcc\x1d\x1df\x8f\x06\xe7\x8f\xda\x19\xa4\xb0pyqH\xa7\xb3H\x8d\xc2\x18\xb3\xd4\xc2#\x9d\xc5$\x1d8@:T\x8a1\xd1(,\x1aG8N\xe90\xab\xd4]\x9a\xa0\xcc\xd2ani0v\xe9\\~\xa9&\x8ei4F\xe5!,\xc7\x94O#C\x85\xe6\xf2L=\xc8\x95\x83\\SO\xb6\xa9\x95\xb26\x92qj\x97c\xe0\xf1\xcc\xe6\x9d\x8ei\x1c\x1f\xee\xa9\xbb\x15\xbc\xf8\xa7\xa3\x19\xa8f\x96S\x00\x16\xaa\x07\x0f\xd5\xc5DusQ\x07[m\x0c\x1f\xd5\x8f\x91j\xe6\xa4\xcef\xa5z\xf3R\xa72S\xed\xcd\xe4\xc5N\x0d\xcaO\x1d(\x8ba$\xceb\xa9j\xd2\x0c\xac\xd5\xa0\xbcU\x1bsu&wU/\xb2\xcee\x0d\xcffu\xf0Y\xcd\x8cV3\xa75$\xab50\xaf\xf51\x98\xadc\xb8\xad\x9e\xec\xd6Q\xfcV\x7f\x86\xab\x85\xe3jc5\xfa\xf3\x1a\xdd<\xd7QLWo\xae\xab\xb1B\xa1\xf9\xaea\x19\xaf\x16\xcekh\xd6kh\xde\xeb\xfc1\xe2\xc5}\xf5c\xbf\xf6\xf9\xaf\x03\xa1\x08\x07\x8ea\xaeSMP\x1e\xec\x00\x1360\x17v\x98\x0d\xeb\xe0\xc3\xda\x80\xf0\xfa+r\x04j+\xd4\xed8S\xbb\xe0m{% }\x0d\x1c\xdf\x01`;j(\x92\x07\x8d\xd5\x03\xdb\x0e\x99\x99\x835+2\x1b\xe2\xcd\xbag\xa4H\x93\xb8\xb3Vi\\Q0\xb3g}\n\x15\x8cA\xeb\xe6\xd0\x86d\xd1\xfa\xf1h\x830i\xdd\xad8\x89M\xab\xaf:\x12\x8ah\xe6\xd3Ng\xd4j\x82\x04a\xd5\xce\xa9\x0d\xcc\xaa\x0d\xcd\xab\x1d\xcf\xac\x1d\xee\xc7`\xec\xda\x01~\xedT\x86\xadVv\x993j\xe3\x9av\x15\xf2\x8f\xaa\xdb\xe3\xf7M\x0f\xb1\x8b\xd0[\xaa]&\xcc&\xb8/\xaa\x1a\xdd\xffn\xf1\x87\xef\x16\x0f\xc7p\x9a\x84\xf8\xae@b\xe9\xbe2nR10/O10\xaf!0\xafVccm-5\xe5\xb5\xf4\xaf\x14\xfb\xd2Q\x83\xe7\xeb\"V'\xd7\xff9\xf0\xd2\xfb\xb3\xefON\xff\xbc:\xf9p\xfe\xf1\xd3\x8a\x9d\xd3\xed\xef\x9e\x9d_\xac^\xfd\xeb\xab\xde\x1bm\x94~w\x05\x87\xd5\x95\xabt\x9b\xd31\xa4\xf6\xbc\xd4'\x80'\xd6\x15*\xbe\xe4\xa8\"IS\xa6\xb5z\xea\xd86\xb8\xa4\xfa\x80iof$\x1e\xda=\x12{%)\xf2*]\x93\x92\xeew\xa8$\xdb\xb4\xaa\xcb\x03\xb3\xa8e\x19\x02s\x06-\x03+\x80\xc6\x91\xe4'\xc8S6\x19\x10I\x8a\xeaP\xd5d\xb7@'\xfb}\xc5\x17\xc5\x9a\xb3F`]\x16(\x98NCay\xa9\x19tYs\x99\xc0hd\x85\xcf\xd2\xcfD\x16\xc9-\x1a\xd27&\xba*\xc9\x93\xa2)\xf1\x16\x8aS\xecIN[yGk{q\x89\xf0\x96*M5\x87\xc9\xd3\x8ce\x80\xd7\xb4mr\xf2\x05%X\xd7\xc7\x98\xc1$\xad\xba\xfec\x0d\xccqD\xae/\xa5eW*T\x15\x1cjL7\x1bR\x1a\xc8L\x98V\x94\x11\x180tOU\xd3\xb3\x00g\x07\xc0\xcc\xe4\x1d\xa9\xeb\xd6\x96\x99f\x19\x9f-F\xcb\x00\xd6&\xef:\xbc\x1dtT\x0b\xe2{\xa0\x92WI\xfe\x8b\xf5\xabv\xbc\xd1'\xe1\xb1\xf6\x8b\x9c\xb32\xca\x05-\x08\xda\xf4m\x91\x00y@%\xda\xdc\x93\x12\xf446-Z\x8e1\xa8\xc5\xf4\xd81P*\xbex\x1c\xeb?\xc1\xc1o\xd30\xed\xb2W.\x18z\xb4%X\xce\x07\xa0\xcb\xf6\xf3\xb8kv8\x7fQ\x12\xbcf\xbbmM\x1e\xea\x06g\x9a\xb1\x99\x9e\xc3\xf6b\x89\x05B\x89\xd6\xb6\xf2\x17j/\xb3*\xaa-\xca\xb4\x95\xb4\x92\x88\x1ct\xf2\xf1\xd9\xe1\xd5Kt\xa9\xd4{\x8a\xfe\xea\xd3[\x86\xf5\xecm\x91\xbcMK\x92\xd4'\xcd\xc3\x02\x9dT\xa8\xd8\xef\x0b\xae\xa1\xa9\xd9\x1c\xc1\xa8n\xe7\x89\"n]\x10F\x1dQ\x9b\xa2$\x7fk\xd2R\xecPU[\xb8\xe2\x9ej\x0b\x8c\xe8\xc1\x1f=E7\xf0\xbf\x15=\x10\xdf\xb0\xe6\xc2Y\xa5*\x86p\xe4\xd6Z\x9cC\xeb\xf9\x16-\xd3=\xd8\xcezNc\xea\x07\xc6S\xd0wC}\xa0\xedD\xc7C\x0fa\x9c\xde\xe2\xe4\xf3\x17\\\xae+\x85\x8f4\xdc5'\xbb4/\xe0<#\xcdnT\x92]q\x0f\xf7\x8f\x00\xb0D\xe7\xc0\xe0\xb0\xe1\x1b\xe2\xb1\xfe\x934`\x84j\x01\x85\xa2\x0dyv~\x81\xd8;\xa2\xaf\xe4mDm\xc7\xb7\x7fZ\xa0K\xb29F\x8c\xa1s\xfc\xf2%I\xf7\xd5\x820\xbd\xbb\xd9-\x8ar\xfb\xf2\xec\xfc\xe2\x8a\xfe\xfc\x82n\xcejG\x9c\x8a\xe9pd(eZ\xf1=\x0f6@\xda\xa4\xbdu\\]rp\x99\xe2\xbc\xd6\x0c\x00\xb7M\xcblj\xe9>\xec\xf0\x0c:\xe5\xdb?Ix\xe2\x02-\x0bDr\xb6>\x9c\x9d_\xd02\xabF\xebC\xd10F\x99a\xab\xa1'\x9c\x0eW\xb8Y>\x9c\x16\xf9&\xdd\xde\xc0\xea\xc4@\x87\\\xa5\x1c\xe9\xd6\xe1\x1bQ\xc9\x1fp\xbe\xceHy#\xba\x85\x96\x06H\xae\xb4\xd0;LK\x9d\xa4k\xb6\x0d\x02\xd0\xc8\xd7\x13U\"\xffV\x1d;^\xb3\x02.\xce\xa0i\xd7du\xda\xe9\x8e\x9e\x1e\x9fB\xf3^|\xa0\x9fw\x8euB\xc5\xa5\xbf\xf6\xb5\xaa\x1c(\xeb\xecI\x95ne\xf5\xb6\xa7\xa4 \xc9\n-HU\x94[\x05\xbd(-\xa2\x87\x8f\x0cPp\xef\x83\xc3mZ3{\xb3vt\x10\x0f\xa4\xc9\x07k\x00\xbb\"\x92n\x93\xbc{\xda\xd2\xe1n\x93\xeb\x84\xf9\x1f+\xc8C]\xe2\xd5mZW\xab\xaa.Js\xb4\xe61\xb7\\\x90L\xb3\x96;\x0f\xca=\xeb\x81]\xc7>\xa5\x0bdR\xbfI\xeb\x13\xd6F\xa9y\xaa\xd0\xced\xfe\xa7\xad\x1d\x806+\x18\xf8{\xda\x16S\xa6\xe9\x19\x98\xefg$\xaf\x9a\x92\xb4\x0cj\xa6h\xae\xc9\x1aLQ5\xfeL* \x1f\xef\xd2<\xdd\xe1\x0ca\xe6\xb6 \xecOS(\x04@\xf8\x0c\xe6\x02\x81i\xbe5\x96\x82.=\xf5\x1dU\xffS\x0cjI\x9c\x00\xdf\xe9qAXe\xd5 \xbag\x97\xaa\xb2)\xf2\x8dT,6%E!\xe0\x94L\x17\x02\xa6\x07\x98>\x1e\x9e\xe0W\xd2Y\xdfc\x86\xf7\x8f\xeb\xb3\x8e\xea\xc6\xe9\xa3\x1e\xd1\xdd\xc7\xf3\xc1\xa3\xf9\xf0\xb1\xdcy$\xf7=\x8e\x0f\x1d\xc5=\x8f\xe1\xf6\xe5a\xea\xf1\xdbz\xe0\x0ez\xd8\x9eu\xd0\xd6\x8f\xd6a\x8f\xd5A\x8f\xd4a\x8f\xd3\x93\x8f\xd2_\xe9\x18m\xe0\xa4\xb2\xb2w\xd9\x12\x04\x95j_\xd6;\xf8\x8a\x0d\xbcQ0&\x1c\x81\xfa\xa8\xff\x08\x13Bm\xf4\xa0\xb7\x1e\xfa\x871\xb4\x80\xfe\xf2vO\xf9p>\xf2\xc1\xbc\xe3\xad~\xf1\xf5t\x8f\xf8P\xbe\xf0\xc3^\xf0\x93\xfc\xdf'{\xbe\xb3\xfa\xaa\x16f\xab\xcf\xfbdowP7\x14i\x16?\xf79\x1e\xee\xcc\xa0\xa4\xd6\xc6\xe0\x841\xc5\xab\xdd\xee\xc1>\xd3w\xdd\xcbk\xdd\xdfC}\x86o\xfa\x0c\xaft\xc3\x82\x11\xd0\xf7<\xac\xd7y0\x7fs\xb7\xa7y0\x1fs\x9bw\xf9\x1c\xbfr#\xfd\xb1\xf6\xf1\x1e\x9f\xea7n\xf5\x11\x9f\xe8\x1dn _\x8f\xb3\x8e#\xe7\x0e:\xd1\xff\xbb\xf3\xf56\xb5\xef7\xee\xbc\xe7y{\x83w\xb7$N\xf7\xf3\x0e\xe0\xe1=\xcf\xb7[\x19\xe5\xeaf8\xd3\x9f\x9b7t\xef\xe42\xc3s{\xd0-\xd9\xe2\xad\xed\xf4\xd3\xd6]6\xfd}\xb3\xf5o\x7f6\xd5u\x92'\xb6Oe]\xde\xd7\xf6\xba9=\xaeG\xf8Z\xf7\xdd\xd2f\xfaW\x0fzV\xdb}\xaa\x87\xbc\xa9\x8d\xad\xe0\xebA\xed\xf2\x9dV\xbd\xa6g\xf8K{xJ\x8f\xf7\x916x$\xbb\xfc\xa2\x03yD\x1br\xee\x8d\x94Y\xfe\xcf\xaa\xbf\xf3\x1cOg\x83g\xf3,\x9ff\xd5\x879\xa4\xf7\xb2\xd5oYu\xe6T}\x95\xc3x)\x07\xf3O\x0e\xeb\x99\xec\xe7\x93\xec\xf4F\xf6\xf4C\xf6\xf1@\xd6\\u\xf5\xdc|}I\x87\xfd\x8d==\x8d=|\x8c{E\x0e\xe9W<\xcb\xa3X\xf7 \x0e\xe7;\x1c\xcekxz\xef:=\x85]>\xc2b\xf9nI\n\x93\x99J\x9e$\x08\x07\xed\xa8n9\x10\x82\xed!\x98M\x92\xb0\xceUJ\xc2t\x0d\x1c\x88\xcf\xe4\xf0TXF*\xf2\xb7\x86\xe4r\xf8v\xe31\xc3\xe2\xbfaW\xf2\x85\xdc\x96\xc5 \xfe\x16G\x89\x84y\xa0\x1c\xf1\x01\xd7\xab}oin}A\x12\xe6\xb1\\+!\xfc\xa1U\xd8\xee\x86\xd16\xbd'l\x8c\x96\xa4\xaa\x84I\x9d\x1e\xc0$\x81}\xaf\xc8}\xc9\x1c\xe6\xe8\xc4\xc9\xf0\x01\\,?\xf3\x83\x98\xd9\xcd\xa43\x9b*]\xd6\xf1CX\x17Y\x89cu\xb1\x7f\x91\x91{\"H\xf1C\xac\xb1\xabt\xd7d\xb8\x16^;\xde\x86Z9\xe8\xa9\xe7x\xb5\xde\xa5\xd0\x1b\xaf\xf5\x83\xe8P\xf9Z\x00:\xdcxI\xcd\x1eh\x0btE\xf25C\x9f\xeb\x07\x0e@+\xceg\xf5\xc3\x8a\xfd\xee9\x10\xedL0\xa5\xbc+\x9e\x1b\xdf}i \xe4P\xb1\xd2\xbbFl\xe7\xf7\x83C\xa1\xd7;\x9d\xb7\x11\xfcY\x9b\x9c\x8d\xc47\\\x84\xcb\xd3\xa8\xcbb\xe4\x9d\x1b[\\)\xebV\xaf\x0e\xe2\xb1(\xb3\xec7\x8co\x8b\xa6f\x01v\x99\xed\x82+Z\xbc\x83{\xf1u\xb5r\x98\xcb\x82\x06\x02\x15;\x8d\x1a\x06\xdf\xb8a\xb3\xc6\xf7\xb8\xfa\x89e\xd4\x02o\xf8!\xdd5;\xd4\xe4\x8c\xd0\xb3A_\x8a\xf23\xfa\xc2-\x80`\xc2\xaa\x1ft\xb7\xc1=)i!\x16J-\xf4\xd8\xc6\xb3\xeb\xf0=\xae\xae\xab\xae\xc0\xb8\x17\xe5\x18'5\xd8\xbbE\xb8cQ 0TZ:\x18\x1evC\x92\xfd\xd5n$\xb3\xbaR\xbf \xc3\xd9\x00#-Soq\x8d\x81\xb4y\x00NeI\xea\xa6\xcc\x85\x1f\xb4\xd0&\x98\xfd\x95\xe1\xbeR\xc8rt\xae\"&\x1f\xae\xaf\x96\x06\xc3\\F\xf2m}Gw\x80M\xfa\x00\xe3\x9c\x81el5#{\\\xe2\x9a@\xee\x90)\xdd@\xa9Bc\x8e:\xd8\x16\xa0\xd2\x0e\xdf\xf22\xb8\x04f\x14\xd5\xcd\x19\x80D\x15\xb4}\xb1gs|}\xc40wZ R\xb2\x9b.\x84\xdf\xa2\"\x90\xaf\x9cj>\xb7$\xc1\x0c\xba\x00BG_\xdd\xffPm\xfbn\x90T\xf5\x97\x04h\xf7^X\xfbt\xb8\xeb\xde\x17\xdb~\xc6\xcc\xa3ZZ],=hkN\xb9\x8c\xf1\xb2\xbd\xc1*@r\x07\x11\x89\x97\xedIix4\x8b4)`H\xbcl\xaf'\xe9\xd7p\xd9\xde\x84k4\xf8\x85\x19\x8a<\xb1 (?\x1bw$\xe9\x1dyQ\xff\x85\x16\xb2x\x85A\xbc\xc2\xc0(-^a\x80\xe2\x15\x06\xe6|f\xd2@\x84\x10\x0f2\x88\xf1Ko\x82\x08\xa4\x194\x11H3\xc8\"\xb6\x89n)j0\x1a \xa4\xb0d\x12H\xc1(%\x90\xdc\xc4\x12H\xc1\xe8%\x90\xe2\x15\x06\xf1\n\x03\xb0\xad\xc4+\x0c\xa44\x8f\xe6\xa2\x89\xab\xe3\x15\x06nj\x0c$W\xf0~7M\x06R\xbc\xc2`\x1c\xad\x06R\xbc\xc2\x80%\x17\x0d\x07R\xbc\xc2\xa0\x9eA\xd8\x81\x14\xaf0\x18I\xf7\xd1\x8b\x1c\xaf0\x08A\x11\x82\x14\x96(\x04\xc9\x8f.\x04\xc9I\x1a\x82\xe4I\x1d\xea\xbd\x1c\xaf0`)$\xd5\x08\xd2,\xc2\x91&-^a\x10\xfa\n\x83\xe1\x13J\xcf\xf2\xacC\x83\xc2j\xdd\xbd\xc2\x06\n\x9d\xd3\xcc\x10\xa1H;\xc9\x0f\xe3\xe2\xa0\xb9\x08\x14\x1e\xf1Z\xc57\n\x91\xc2\xc5\xa0X\xa6{o\xd2\x04\x00\xef*\x03\xc5\x11\x14G\x93\x0b\xc9f\x87_\x93\xbc\xd0b\xa6ZO\xcaj\x89\x06_\xb7\xd3`\x10:-R9\x86t]|&9\xdf\x1f\xa0H\"\xa44]\x0d\xe8\xe9\x95e\xac(~\x1f?-\xcf\x8e\xd9\xbe\xc6)\n-\x92\x8dst\x9e\xd7|\xda\xb66\x84\xde\xdc\x85N\xea\xc9\xab\xd2m\x8e\xeb\xa6$\x95\x08`\xc0t\x9cm\xb1-\xd8\\\x91\xe8 \xc0\x9d\xe3\xd9j< \xb6O\xa5\xfbo\xc4\xcb\xfb=)\xd5nT\x9aK\x84\x1ef\xef\xb6\x12\x81:\xa6\x90\xd4\xd0\x1e\x1f\xd8>\xc4\xad-\"'s{/\xd3}\xcb\x91J\xf7\x9d\xedhGj\xfcB\x89\x9a\x00_X\xe7\x8d\x89\xa5\xe5=\x98o\x8b\xb5!lX\xb1nci\xf38\xef\xcct\xc6L\xe2R[v\xc5TZ\xd1\x8b!\"B\xc6G\x84\xab\x8e\x08WD\xb8\"\xc2\x15\x11\xae\x88pE\x84\xab\x9f\"\xc2\x15\x11.\xb7\xc6\x11\x11\xae\x88p\xf5RD\xb8x\x8a\x08WD\xb8\"\xc2\xe5\x95sD\xb8\"\xc2\xd5\xa6\x88pE\x84KI\xbe\xe8ED\xb8\"\xc2\xe5\x1a#\xbf\x1c\xc2\xc5\x8d\xce\xe0\x9d#\xae\xe2l\x7f\x05[\x00xe\x80'\x1eiA\x07ERw\xd9\x80\xdaDp\xcdk+\x13\xf0\x14\xd6?\xdcO\x9d\x8e4\xf0\",6\xec\x86\x0b~K\xac\"\xe7\xa4\xa9\xefXp\x00\xed\xf2!)63\\\x0c\xbb|xZI@\xc9\x02\x9d\xe1\xe4\xae\x83KDP\x00\x0e\\h7ga6Hu\x0f\x9f\xba\xbd\xae\x94\xee]l\xdf\xa2\xf3\x96]\xe8\x8d\xd2\x1a\x15I\xd2\x94\xba/\xe3\x1b\xe6\x86zOr1C\xc4wj\x81\x9e \xd3g{s-{\xcf\xdcg\x9a\x85\x84\x05,\xde\x90\xb2\x04\x83\x14\x16\x80E\xbac\xa7\xcd.\x10\xc2\x1e\x1f\xe0\xd9\x86\xa8@\xa1H_\xee\x8aL\xbf\xc9M\xf6\x01\xef~\xde\x91]a\x02-F\xbbARA\xc2\x83\xb5;&\xe7EM^&\xc5\x8e\x99\xd9a@\x8a\xfeA\n\xec\xa2\xb5\xfcO'\x97\x1f\xcf?~\x7fL\x17\x87$K\xe1\x96_*\x1e\xe2\x0ed\x07D\x1ex4q\xf2P\x0bsx^\xd4\xfaen \xce2\xb6`\xee\n\xe3\xf5F\xd2%\x06\xf0\xe6\x0d-\xf8\x8d\xf0;E\xcf*\xa2\x8a\x14\x11\xab\xb7i}\xd7\xdc\xb2%\x01\xc0\xab\x97\x1d\xaa\xf52\xad\xaa\x86T/\xff\xf5\xd5\xb7\xdf>\x97[\x9d\x8e\xb9\xa2\xa9Wp\xef\xbcg\xfb\xbb\xefN\xd5.\x93\xe5\xf9\x08\xfc\x8b]\x86/.\xbb\x07\xdd\x0c\x16e\xf0\x08\x97\xe2\x1b\xb0E?/\xd4\xd1{\xdbbh\xa0\xb9\xb2e\xfc\x0e\xf7f;y\xa8I^\xa5E\xbe\x02cx\xc4\xc4\"&\x161\xb1\x88\x89EL,bb\x11\x13SS\xc4\xc4\"&\xe6\xd68\"&\x161\xb1^\x8a\x98\x18O\x11\x13\x8b\x98X\xc4\xc4\xbcr\x8e\x98X\xc4\xc4\xda\x141\xb1\x88\x89)\xc9\x17\xef\x88\x98X\xc4\xc4\\c\xe4\x1111n\xeb\xd6\xce&\x9a\xdd\x19\x0c\x1a\xed\xe9D\xfc\xca\xfa3\xc1y\x87E\xdc\xaaz73ik@\xd5\x97;\x92\xf3\xe5\x08\xee\x0e\x90\xf3\x81+V\xc5\xad\xea\x0b:.\xa9\x02\x04\x0bXE\xd8\xad\xc2}q\x16\x9b)\x1d\xa5 \xce\x9f\xb2\xf3\x1e8\xa9\xad\x01v\xd2\xec\xf3\xec:O\xb8bW\x12\x92\x17\xf9*)\xd3:Mp\xb6\x8a\xc6\xf8h\x8c\xef\xa5h\x8c\x8f\xc6\xf8h\x8c\x8f\xc6\xf8h\x8c7\xa4h\x8c\x8f\xc6x\xb7\xc6\x11\x8d\xf1\xd1\x18\xdfK\xd1\x18\xcfS4\xc6Gc|4\xc6{\xe5\x1c\x8d\xf1\xd1\x18\xdf\xa6h\x8c\x8f\xc6x%\xf9\x1aZ\xa31>\x1a\xe3]c$\x1a\xe3\x83\x1b\xe3\x0f\xed\xa8K\xb7y!;\xd4\xf4\xcel\xcb\x877R\xf8)\x16\x8a\x8a]\x8c\xd8\xbbJ\x906\x01\xce\xb2\xd6 \x87\xfe\x8b\x8a{\xd2\x1a\x8apS\xdfM\xbb\x92\xb3\xf5\xbai?4y\n\xb4\xf2\xdb\xa0`M}W\x94\xe9\xdfa~\x95\x84\xdd\xd55\x101K\x1eQ\xe2\xf0\x0e&F\xa8\xd2\x91p`\xd9\x15\xf4\x98G[u\xd3:Tt\x9e>\x93\xc2\xd0i'}\xf7}\x89\xfdk;\xbb\x88l=7*\xe9wn\xea\xac\x93;\xbec\xf2\x8b\xd4Z\xd7'I\\\xdf\x83\xaa\xf3z\xea\xbb9\xd5\x05\xbf\x8a/)\xf2\x9c$5]\xae\xda\x0cY\xdc4\xf9\xba\"I`\x96~\xee\xe9\x0fCWpR\xc5\xad\xa8\xd8\xd8\x80\x91\xa4\x0cM\xd1\xdbU\x8d\xf35.\xb9Z\xd4\x9a\x9dn\xcb\x02\xaf\x13\\\xb1\xc2\xf5\xa3\xb8\x99\xe3\xb4\xbdi\xc3\xaf\xd5\xeeXm\xc2\xa3lR\x9f{\x87\x1e\xac-\xd8\xd3\x80\x91\xc8es \x8a7\x0daM!q\xa6\x80\x18\xd3\x00\xbe4\x0b[\n\x87+\xb90\xa5\x89x\xd2d, \xec\xd7\x86\xd6\xb2\xe2H\x931$Pa4yV\xfch\x0ev\x84\x9a\xbd&\xcf\x86\x1bM\xc1\x8c\x86\xf0\xa1\xd9\xd8\x90\x17.4\x06\x03\x9a\x85\xff\xcc\xc0~\x8c\xcbJP\x8c'4\xbe\x13\x10\xdb\xf1\xc1u\x02b:v<'(\x96c\xc6q\x0c*\xbei\x95\x9a\x8a\xdf\x00V\xa3\x893a7\x93q\x1b#f3\xb0\x15\x0f`5\xae]:\x14Fc\xc7g\x86J0\x0f\x97\x01\x1c\xa6'\xd0\x84\xc9\x04\xc1c\xe6a1\xda,\xd17\xdc\x90\x18Lm\xc0_\xe6a/\x0eh\xc1\x8a\xb9x\xe0-&\xe3\xeb\x18\x9c\xc5\xf4\xfd\xcf\xe6\xbaO\xc4V\xfc*\xef\xc6T\x86j\xea\x81\xa5\x8c\xc2QT\xa3\xd3l\xfc\xc4\x81\x9d\x0c\xe1&\xc3\x98\x89\xa5U\xfc\xb1\x127N\xa2c$\xb3\xf0\x11/ld\n.b\xc4!\xdcxH0,\xc4\x98\xbf2\x92f\xe1\x1f:\xde1\x07\xeb0b\x1b\xb3p\x0d\x1d\xc7\x08\x8ba\x0c\xe0\x17\xbaYW\xc7-Ba\x16\x01\xf1\x8a\xd0X\x85/N\xe1\x81Qx\xe3\x13~\xd8\x84\xc1\x8co\xca\xd5\xd7\xd6\xec\xc2#\xbc\xb1\x08/\x1cB)|X\xfca\x16\xf6`\xc2\x1aB\xe2\x0c!1\x869\xfd\xed\x81-\xb8q\x85n\xf1\xb7k\xd5\xc2`8!\xd8\x95%\xbc\xd5\xe4\xd0V\x96`V!\x03Y\x99\x82X\xd5\xe3\x03X\x05\x0c^\xc5\xdb\xa9w\xc2\x9e\x15\xb4J\x0fSe\x08Q\xd5\x0fOe<+\x0e\x0d\x99\x90!\xa9\xe6\x84\xa3\xe2a\xa5\x94\xb2\xf5BQM\x08C59\x04\x95-\xfc\x94\xb1}-a\xa7L@R\xc8pS\xaePS\x03\x9e-uD\x12z)\" \x11I\x88HBD\x12\"\x92\x10\x91\x84\x88$(\x8f\\\xbbtD\x12\xea\x88$D$!\" \x11I\x88HBD\x12\"\x92\x10\x91\x84\x88$D$\xe1W\x8f$\x98<\x13\xe6x%\x18\xfc\x10\x02\xfa \x18,a\xb3\x02\x01\xf9\x06\x01\xaa\xa3\xa9\xb4\x97\xa2\xa94\x9aJ\xa3\xa94\x9aJ\xa3\xa94\x9aJ\xa3\xa9Ty\xe4\xda\xa5\xa3\xa94\x9aJ\xa3\xa94\x9aJ\xa3\xa94\x9aJ\xa3\xa94\x9aJ\xa3\xa94\x9aJ\xa3\xa94\x9aJ\x7f9S\xa99LK\xe0\x10-5\xc9\xd7\xa4\xdc\xa5y\xbd\xc0\xb7I\xba8\xbb'y\xed\x1d\x08\x83\xbd\xd2u\x85~0\xc3u]\xa6\xb7M\xfd\xd8\xb12>\x93C\x88cb\xb0\xf3f\x9a\xaf\xc9\x83Y\xd0mQd\x04\xcbqQz]\xfa\x94\xf5\xc0\x89h7\xf0\x18\xa8\xd2|\x9b\x11Z\xcb\x17\xb0\xad\xedqZ\x1e!\\UE\x92\xb2\x83\x10\xdf\x93\x10\xa1_/\x9e\xeaC\xa5\x9d L>X\x8b*\x84;+\x12Z\x93{\x92\xd1\xe6\x85\x10/u\x8d\x93;y[\x93\x82\xbaH\xe4\xfeKR\xed\x8b\xbc\"o\xc86\xcd\xdfdE\xf2\xf9\xa8\xfd\xed,_+\xbf\x9c\xde\x91\xe4\xf3\xf2\x81\x0ez\xe5\xfb\xb7$K\xefI\xb9|h5\xd4\xf7\xb8&\xe5Q/z\x0b\xda\xe1\x03\x9d\x10\x7fkHI\xd5\x97\xa6b\xf1]\xd8Dc5\xaf\xacc\xbamQ\xef\xc1\xdd\x1bS\xc6A`\xe8~e\x04\xf9~\xa6\x8c\x17}\xa4<\xce\x18\xe1\x91p\x9a\xfd\xb6\xc4k\xd2\x86\xc3\xf9P\xac\x9b\x8c\xfc\x08\xc65\xef\xf6\xa2\x1a\x83\xa3\xe6|\xb1\x96M\xfcx\xbfG;\x96\x9fhA9[\xab\xa4ab\x7fBGT^5\x95\x90f\xc9\xad\xd7\xaa\xad7@\xaf\xfa\xadA\x87\xb61|\xcaMu\x95\x9eM;v\xaf\xd2\x0c\xfc\xb5\x9e\x96\xa2\xb4pUl\xea/tc\xa4k\xc8~\x9f\x81I\x805\"\xce\xd0\x93\"\x7f\xc1\x85\xc1\x1ap\xdd\xbe\xb9\x90\xde4\xf4DVl\xd3D\xaed\xdb\x07%\xd9\x15\xf7d\xdd9\x8e]\xbd\xfdS\xcfB\xc2\xce\x07i\xc5\x8ft\xdc6\xcf\xcc\xe0G-:\xd3\xce\x94\xfa\xae,\xbe\xb4\xde_\xa3\xfc\x93\xfa\xab\x98yYB\xfc\xe0+|\x92\xea\xd6!\xa9\xeb\xe5\x1d\xb7b\xefIIE\x93\xb5l\xc7\xf8\xc4-\x01k\x94n\xa0\xc5x\x85*\xd2\xf6j?R\xdf\xd0\"\xde+\x173\xfdI\xbaDk\xd8o'f\xbe)8\xd4\x93\xe6I\xd6\xac\x19\x16\xf5B\xbd\x7f\xbfj\xa8\xceQ\xb1iV\xb3\x05\"\xada\xb03\xab<\xae\x8b\x92.\xbbM\xb6F\xb8\xa9\x0b\xaa\x8d@\xb0>\x91O-&\xbbX\xaaV\xe0\xee\xb6\xaaj\\k\xe3U\xd1/\xcd\xdaem\xa4\x04X\xb5\xc2a\xa0! \x19\xc0N\x05\x08G\x04\x08F\x03\xb0\x92\x00LV)O\n@(\x02\xc00\xfc? \xfc\x0f\x0b\xfd[\x81\xff\xb0\xb0\xbf\x05\xf4\x9f \xf9k\xcd]\x1b\x00\xff\xb0p\xffL\xb0?0\xd4?\x03\xe8\x0f\x0d\xf3\x07\x03\xf9\xc3B\xfc\xc1\x00~7\xbc\x1f\x0c\xdc\xb7A\xfbs\x80}#\x90o\xb0\xa6\xe9\xeb\xcd<\x10\xdf\x00\xdaO\x84\xec\x0d\x06\x14\xebFi5\x9e\x0c\xef\xa0\x13\x81\xfa\x0e\x987\xb5\xef7\xee\xbc\x03C\xf4:@\x1f\x00\x9e\x0f\n\xce\xab\x9b\xe1L`\x9e7\xb4,q\x0e\x14?\x88E[`x'\x08\xaf\xe3~\xfe\x00\xbc\xfe\xed\xcf\xa6\xbaN\x82\xde}*\xeb\x82\xdd\xedusB\xee#\x00\xf7>\xbe2\x13l\x1f\x84\xda\xed@\xfb\x10\xccnl\x05_\x88\xdd\x05\xb0\xab\xf0\xfa\x0cp\xdd\x03Z\x1f\x0f\xac\x1b`m\x17\xa8\x1e\x08R7\xe4\xdc\x1b)A\xc1\xf4\xc0PzP =$\x8cn\x05\xd1UdR\x05\xd0\xc3\xc0\xe7\xc1\xc0\xf3\xb0\xd0\xb9\x1fp\xee\x84\xcd=As\x1f\xc8\\\x03\xcc\xf5\xdc|\xc1\xd3a\xb0\xdc\x13*\xf7\x00\xca{E\x0e \x92\x07\x86\xc8\xc3\x01\xe4\xe1\xe0\xf1\xe9\xbd\xeb\x84\xc6]\xc08,\xdff\x1d\xf6\"\xc32\x98!\xa3y\xf8\xb6hj\x84\xd1>\xc3y\xde\x19[Yo2Cr*\xc2;qY,Z\xd7\xc2\x0eg\xfc?\x0d)\x0f'`\x88\xa7\xf9\n\x90\xcf\x1b\xe2\x08e<\xbd\x136n0\xb3\xf4b:\xc9\xf6SZs\xf4\x05w\xe8\xc1@C\xda*'r)\xc5\xdfl~\x08\x84\x85}\xf5R\xfa\x8cK\xbb\xbc8\x15\x03\x086\x1ag\xb3\xc2\xfd&\xf5at\xa3\xf2`m\x03\xad\xdaG\x19\x8d \xd6w\x80-s#\xb0\xb9D\xf6\x96\xe0\xad >pT\xf5\x14\x0e\xe3\x93F\xd0\xbeE\xd4\xb4\x8a\xb1\xceN\xc5B\xc1\xb2h\x87<}\xb6P\x1a\xc8\xcbJ\xdc\x07\xe5\xd0t\x0b\xf1\x1c\x80N\x11e\x82\xebPh\xc8\x0e\x19a;4\x1f\xbaS\xa4\xe1\xac*4\xf8\x0e\x05\x80\xf0\x14q\x1a\xa0\x87\xe6\x83z\x8a4\xdesj&!\xc1=d\x03\xf8\xd0(\x90\x0fi@\x1f\xf21\xe9\xe8\x80\x1fr\x8e\xfb\x80\xc0\x1f\x1a\x02\xff\xd0(\x00\x10\xcd\x03\x01\x91a/C>-\xd8\xdf\xd3\xd0 (\x88\xe6\x02\x83\xc8\x13\x1cD\x1a@\x88\x86\xeab\xbb_m\x1eX\xa8\x08\x9b\x8b\x18j\x8b\x89\xf4\x83\x03CD\xb6\x1d\x02\x0d0\xd5lx\"\x1ajI\xe4\x9c=(,\xb6\x88\x1c\xae\xc6!1F\x14\x12gD\xc3\x0e\xc7\xb3\xf0F\x14\x10sDN\xdc\x11M\xc5\x1e\xd1\x1c\xfc\xd1\xd4b\x87\xbd\xb8G\xce\xec|<\x03\x874\xc8\x82\xb5\xcb\xea\x80<\x0f\x8f4\x88k\xf6V'\xe4\xd0\xb8$\x9a\x8fM\xa2\xf0\xf8$\x9a\x87Q\xa2y8\xa5y\x8a\x1a\x0b\x19\x0c\xbdD\xc1\x11L\x14\x12\xc5D^H&\n\x89f\xa2Ag\xe5y\xa8\xa6i\x8e\x1b\x1d\x96=\xb1N4\x1b\xef4\x084\xb9-OFA\x91\x8dJ\xee\xd8\xe2\x07\xdc\x97}\xf6\xff\x89\xc8\xa8i\xd9\xb3:1\xbb\xca1\x0f%U\x841\xcc\xd4\xe8\xca\x1c\x04-E\xa1\x11Sdth\x9e\x8d\x9c*\xd2j\x83S\xf3<,\x15\xb9 F4\xe4\xda\xec\x81\xab\"\xeb\x85\xfe\xfe\xf8\xaa]\x86f_\x9f\x85\xb5\xa2\x11\x8d\xe1\xc2\\\x91\xb3\xdeN\xec\x15\x8d\xc3_\x91\xd1Ao&\x0e\x8b\\X,r8>\xbb\\\x9f\x07Z\xc9\x17\x9bE\x1e\xf8,2\xba@\xcf\xc2i\x91\x1fV\x8b&\xe1\xb5\xc8\xda0N\xdc\x16\x85\xc3n\x91\xbd\x14\xdaH\x0b\x8a\xe3\xa2\x99X\xae\"\xca\xe4$\x1d\x18\xddE\x81\x11^4\xec*mr\x966\xb9K\x87B|QH\xd4\x17\x05G~\x917\xfa\x8b|\x10`\xe4\x8f\x02#O$\x18\x99\xdd\xa7\xcd\x0e\xb5\xfe\xb8\xa1\xcb\x85\xda\x1b\x19F~\xe802U#$J\x8c\xe6\"\xc5\x8a,\x83kuH\xec\x18\x05\xc5\x8f\xd1\xec\xf1\xe0\xc4\x91\x91\x07\x96\x8c\x1cx\xb2\x0d\xa1\xf3\x81A\xa5\xcf\xb8\xb4\xd10h\xcfI\xaf\x1a\x0d\x0f\x82'\xdf\x8a\x9f\xcc\x1f\xd9;Y\xc7\x06\x07\x0f\x82>^\x92\x904_I\xa7l\x8b\xdf$\xffn\x9c\xf7$\xa4!\\\"\x80'%$\xab?\xa5\xa1\x0c\xfdk\xb4\xfa\xfd\x8c\xfa\x17\xb0A!\xc0\x1e\xc3!=\x92\x96]i$AB\x82\x93\x19`\x1e\x98\x83\xb3B\xfe\xfce\xff\xfbo\xba\xf9\xd1N\x0c\xf1\xdbX'S&\xff\x9ac\x1a\xa7\xa2\x8eW5\xae\xc9\xe8\xf9\xd3A#B\x8e\xd9\xc3\xca\xed\xfa\xdc\x0e;3\xdf\xe0\xf7C\xbe\xec\x1e5\xf2ox\xb3\x1cS\x07\xb4-\x8c\x9b\xfa\xee\xefm\xfb~_\xe2\x11\xb1\x130\x90 \xfe\xce\xd6h\xb5\xd9\xbc(\x07utL\x9b\x0f\x18E\xc74?`\x08\x0c\xdaZ\x1bE\xc7\xb41\x00\xd0L\xf0'0\xf03\x03\xf4\x99\x01\xf8\x18\x16\x8c\x80\xd0NXX'\x18\xa4\xe3\x86s\x82A9\xd11-:\xa6\x8d\x80\\\xa2c\x1aohY\xe2\x1c0\xc5\xc7W+:\xa6I):\xa6\xa1\xe8\x98\x16\x1d\xd3\xa2cZ(\x98\"\x18D\x11\x16\x9e\xf0\x83&\x9c\xb0\x84'$\xe1\x03GD\xc7\xb4N\xd6,\xb8!:\xa6y9\xa61\xbb@Z\x1a-^\x16C\xa1\xeeI`b\x95\xd3\x17:\xaf\x88m\x89\xf3Z\xf82\xec\xd3\x92;\xb2\xf1\xce\xdb\x97M\xce|L6(o\xb2\x8c\x85ZU5L&An\xbeuA\xaa\xfci\x0d\xe7S\x0c\xf9uuA\xcf\xe0x\x93\x14\xf9\x9a[\x01h\xe7\xde\xf4L|\xbdN\xdd\xe1\x03w\xb5\xa9\x0b\x94\xe6\x9c\xaaN\xba\xe2\xc3\x16i\xb6\xad3;#sb\xa9\xd0\x9e\x94\xbb\xb4\x02\xdbv] \xf2@\x92\xa6=\xb5Pi|\xbf\x12~7l\x95\x93JNk2l\xd0<\xd1\xed\x94\xb5\xdb\xba\xc9*A\xca\xc1^\x86w\x1c\x01d\xa3\x954ZI\xa3\x954ZI\xa3\x954ZI\xa3\x954ZI\xa3\x954ZI\xa3\x95\x14E+)O\xd1J\x1a\xad\xa4\xd1J\x1a\xad\xa4J\x8aV\xd26E+\xe9\xffeVR\xd5B\xaa\x9b\xf1\xe0\x86\xac5\x8b\x9a\xc1\x8d\xa5\xf5\x1d\xba-\x80\x01+\x02E\x91V\xff\xe3\x96:.\x8f\x8e\x12n\xdfk7L\xb8l\x03\xe2\x87\xe4L\x91\xac\xd2\n\x1a\x85\xbd\xff\xb7\x86\x94\x07\xf8\xdbfmd,\xcc\xef!'\xf6\xcfxV7+\xd5#\x93\xb95\xcb\xa6,\xc5pN\xd2\xac\x9c\x8e\xf7-\x16O\xf9+C\xb8\x91\xa1\x80#6\x1bh\xf7\xcc\xea\x91\xec:\xcdA\nh\x15\x854\x1cz$\xa4\x85\x14R0;)\xa4\xc1\x00$\xb3l\xa6\x90BYN!\xb9\xc3\x90L\xb4\xa2B\x9alK5\xb7\x9d;\x18\xc9\x0c\xbb\xaaQ\x9a3 \xc9<\x1b\xabQ\xe0`P\x92i\xf6V\xa3\xa0\xc1P%\xb3-\xb1B\x88\x87=\xd6\xf8\xa5\xb7\x8d\x16\xd2\x0cK-\xa4\x19\xf6Z\xdbD\xb7\x145\x98%\x17RX{.\xa4`V]Hn\xdb.\xa4`\x16^HCAM\xe6Y{\xcdk\x85%\xb0\x89\xa7\x1d\x18\xd2Tk\xb0Q\x98\xcdB\x0ci\xa2\x9d\x18\x92%\xc4\x89S\xa5\x18\x0cs\xe2\xa7qL\xb4\"\x9b\x17\xd3\x81`'\xee\xd2\xcc\xb3+k\xe2\x86B\x9e\x04\xb11C\x9agi\xd6\xc41\x8d\xc6\xa8<\xcc\xb4:\xeb9\x19\x83\x9f\xcc\xb3ECr\xc6\xfc\x18\x0c\x81\xe2a\x9d\x86d\x89\xa40\xc2R\x0d\xc9&\xc7\xe0^>\xcbv\x0d\xc9\xbfq\\vlH\xaeVp\xda\xb4!\x8d\xb0lC29\xdf\xcf\xb4rCr\x86Gq\x05Hq\x87H\x19l5_\x1b8$\x97%\x1c\x92)T\xca,\xab8$\x0f\xdb8\xa4\xf1\x16rH\xb6frZ\xcb!\x05\xb2\x99C\xb2\x96\xc50\x12gY\xd15i\x86`*\xf3l\xebz\x0e\xc6\x80*3-\xeez\x91\xf5\x10+a\xed\xf0\x90\x06\xc3\xac\x98\x03\xad\x98C\xad\x84\xb2\xcfC\nf\xa5\x87\x14\xd6V\x0f\xc9\xcfb\x0f\xc9i\xb7\x87\xe4i\xbd\xef\xbd\xec\x0c\xbcb \xbdb\x0b\xb6\xe1o\xf7u\x87_\xf1\xb6\xed\xf3\x97\xdd\x16~H\x86\n\x85\xb4\xf6C\x9ae\xf3\xd7\xa4\x19C\xb1\x84D\x02\xa4l\x82\xe0\x01\x90\xe6\x8e\x11'6\xc0\xc5y\x04e\xe9p\x02H&\xb4\x00\xd2\xc09l(R\xbb9n\xf7l\x14\xa1'\x8d[\xc5\x8dX\x02\xa4q\x88\x02\xa4\xdea\x0d\xc0\x80~\xc4\x0f\xfe\x1b\xe4\xb8\x16\xa3\x88\x17F\xec\xd4{\xbcMs\xa5A\xfb\x17H\xb4/\x80\x8d\x8d\xb0s\x9f\xf4\xab\x88v#\x82Nt:@\xedO\\\xce\xc9C\xbd\xfaL\x0eA\xf9XZ\xe0x\x91\x8b\x88\x93A\xff\xcb\x0dD\xb8\xaa\xa0\x8d.\xf0\x96\\\x92\xbf5\xa4\xaa\x17\xf0\\\x11\xc2\xfa\x82}N\xc5\xd1\x96 hWT5\"\xcc\xfc\xc2\xec5\xecV\x87n\x9d\xd8\xd7\x07\x94n\xd4\x01{GJ\xc2\xecny\x81vEI\x84\x9dMV\xa2\xea\xa2\xc6\xbel\xee\x81p7\xb6\xe8\xf4L\xf7\x89p\"eZF\x88.Bt\x11\xa2\x8b\x10\x9d\x96\xea\x08\xd1E\x88\xce\xf8r\x84\xe8\"D\xa7\xa7\x08\xd1E\x88\x0eE\x88N\x15\x14!:-\xf9\xa3P\x11\xa23\xbd\x12!\xba\x08\xd1E\x88NM\x11\xa2\x8b\x10]\x84\xe8\xda\x14!\xba\x08\xd1E\x88.Bt\xbf\x12\x88\x0e.6\xef\n\x00R\"D\x17!\xba\xdf\x12DW\xce\x83\xe8\xca)\x10\xdd\xaf\x13\x9b\x8b\xd8Y\xc4\xce\"v\x16\xb1\xb3\x88\x9dE\xec,bg\x11;\x8b\xd8\x99!E\xec,bg\xbd\x14\xb13\x9e\"v\x16\xb1\xb3\x88\x9dy\xe5\x1c\xb1\xb3\x88\x9d\xb5)bg\x11;S\x92/.\x12\xb1\xb3\x88\x9d\xb9\xc6\xc8o ;C\x03\xc8\xc2\xbc\xabDta=9z\xd3O\xb9VD\x1f[\xf6kF y^6\x02i\x08\x8a\xf2\xbex\x04R\xed\x7f\xfd\x88!\xef^\xad\x861B:\xfa\x05Zy{\x88Xa\xc4\n\xa5\xdf\x7fkX\xa1\x1fH\xc8\xe1\xc1\x93\xfe$\xb9\xbc8\xe5\xf24\x94pC\x08\x9b\x17\x13o+\xd7\\\xea\x8c}d\xb7&\xf1\xefE\x9d8\xf1@(|ME\xf8\x0cf\xbbj\x0evi\x9c'\xc2\xee\x93\x96\x92\xacM\x93\xaf\xdb!\xa4\xf9\xeeM*\x18\x19*\xd8-\xa1\xa5\x12\x8b\x8dR:I\x14\xcea\xbd\xa6\x1f=\xad\xfa\xc5l?\xb1\xacAO;\x99 \xce\x99\xed/?\xd0R\xdc\xe2*M\x8e\xe8r\x9b\x16k\xfa?a\xb2\xdf\x10\xd2I]tD/\xa6^\ny\x00\xf1:\x82\x84=\x84x\x1cC\x82\x1fD\xa2\x17\x13\xa4Q\x07\x97\xd9G\x97\xd0\x87\x97\xe8\xc5$'\xbfcL\xe0\x83L\xf4b\x8a^L3\xbc\x98\xc2\x1er\x90\x15w\xe0\x1b\x1a[\x0cn \xc9QZUM\x0cP\x81\"\xe9\xec\xb7M:\x1b8K\x0f\x9e\xe2e\x19/\x0dBz\xc1*D\xe9\xafRz\x18\xe3\x87\xfb\x17\xd5\xfa3\xfa\xdd\xe2\x0f\xdf\xf9\x1f\xf7\xe3)?\x9e\xf2\xe3)?\x9e\xf2\xe3)?\x9e\xf2\xe3)?\x9e\xf2\xe3)?\x9e\xf2\xe3)?\x9e\xf2\x03\x9e\xf2\xdb\xa5\x86\xfe\xf94:\x99\xc5\xf3\xbe\xf4\xcd?\xd4y\xdf\xeb\x98\xaf\x1d\xf0\x87\xd0\xfa|\xd3\x9d\xdcO3\\U\xde\xe7\xf4t\xad\x9e\xcf\x95\x9e15}\xban\xe7\x12;\\\xb2#Nw\xc0i\x17\x9c\x8f\xef\x96\x88\x85M\xa1\xcb(w\xf3\xae\xd2]\x9a\xe1\xb2?.\x85fJ\x95d\xf9\xe8zvy\xfa?\xbf}\xc5_\xa4\x9b\xec\x84\xc2\xb23\x80\\\xdc\xbbf\x87\xf3\x17%\xc1k60\xe4c\x87^`y0\x7f\xe2J+\xff\xa9:\xecn\x8b\xcc\xafD\xf0.S\x14s\x84ooKr\x9f\xb2\xd8D,w\xda\xe5\xf9\xa6V\xb3V3\x94\x07\x97W\xae\xd2\x07,kt[\xa6d\xd3\xfb\xb9\xd8\xf8\xe4\xdc\x94\xa9_\x8e\xbd\x96o\xca\xb4\x1d\xcd,\x03:p\xf1\x1a\xd7Xl#\xc5fC\x95\xa14g\x0b\x0e=\x18@G\xc1v\x8e\xe9\xd7\x92<6\xb2\x99zB\xfb\xe9\xe6-\xae\xf1\x0d\xc2u]\xa6\xb7MMU3\xbd\xd4\xab;\\\xdd\xf9\x15]\xbc\x0d-\xc5\xfe'\xc2b\x14I\xc3\xce\xbf\xfb\"\x15\xb1\x91\x9b2\xd5\xfb\x07\xd7X\xcd\xcbk\xf70\xdb&\xac\x0be\xf4\x07\x8c\xfe\x80\xd3m\np\x8e\xd1\xda(\xfa\x03\x8e\xa1(\xcf\xb4\nx\xd9\x03\xfc\xcf\xfe3N\xfd3\xce\xfb\xc6\x03g\xb0S}\xd8\xf3|\xb0\x93\xbc\xfb\x0c\x1f\xec\xf4\x1e\xfd\x01\xff\xe1\xfc\x01\x87\xf3\x9e\x17\xf3\x13b|J\xe2\xf4h\x9f\x01\xe2|\xce\x8b\xf0\xa9\x8cru3\x9c\x19\xd5\x937\xb4,qN\xfc\xce\xc1\xe0\x94\x96\x98\x9d\xceh\x9dz\xe0>\xff\x08\x9d\xfa\xb7?\x9b\xea:)\x1e\xa7Oe]18\xedus\xc6\xdd\x1c\x11q\xb3\x1f\x9clf\x94\xcd\xc1\xf8\x9a\xf6\xc8\x9aC15\x8d\xad\xe0\x1bG\xd3\x15AS\x8d\x9d9#j\xa6G\xbc\xcc\xf1\x912\x0dq)]\xd11\x03\xc5\xc54\xe4\xdc\x1b)\xb3\xa2`\"%\xea\xe5\x9cx\x97\x86\xf8\x96\xb3\"[\xaa\x91,C\xc6\xb0\xb4F\xafTC\xfa\xa9\x11+\xc3\xc4\xaa\x0c\x16\xa52l|J\xbf\xc8\x94\xce\x98\x94\xfc\x94\xec\x8aF\xc9_\x1b\x8cC\xa9\x05l\xd4s\xf3\x8d(8\x1cu\xd23\xde\xa4G\xa4\xc9^\x91CF\x97\x9c\x15WR\x8f#\x19.\x82d\xb8\xd8\x91\xd3{\xd7\x19/\xd2\x15)\xb2[\xbe\x85\x8d\x11\xd7\xb8%\x1d\xed\xf7\x9d\xf6\xde\xda\xfaT\xdb\xaab7\xeb\xe9\xc3`\xe2\x93\xad\xb5`8\xe42\xf2M\xddj\xd3\x06\xd3\xf7\xc7wKo\xc37\x93\xbb\x9ad\xfe\x16\x9f\xd2\xe3}\x91\x80\x1d\x97-:\xbc\x9a\xb2\x91\xbb5\x96(\xa6mI\\\xcf\xc8\xed[\xa0t\x0dv\xca!\xdb;\xff\xc6\xdbx+\xdbkioY\xad\xb5\x9d\xe0\xc7\xb4\xafr\x01\xd1\xaa\x1a\xad\xaa\xd1\xaa\x1a\xad\xaa\xd1\xaa\x1a\xad\xaa\xd1\xaa\x1a\xad\xaa\"\xd5\xd1\xaa\x1a\xad\xaa\x0bd;6\x18\xa8\xdd\xbc\xa2\xa6\xbc}x\xd1\xfc\x13\xc9\xd9y\xa0n\xcc\xaa<\xbafIG\xa46~a\xfe\n)F\\4t\x94\xb2\xf1\xdag\x10\xac\x15InKt\xdf\xfe\x8c4\xa25\x9aR\x81p\xa4k\xa4\x13\xaf\x91\x81|\x8d\x19\xa20\xc8r\xba\x8f\xcf\x81*\x0c\xe2b\xac\xeay\xf0\x05\x9a\x07a\x98\xa7\xa8\xb1\x90\xc1\x80\x0d\x14\x1c\xdc@!\x01\x0e\xe4\x05r\xa0\x90@\x07\x8a\xb1\xaaM\xd9L\x04H\xd0?j\xacj\xe4,\xc7<\x00E\x11\xc6\xe0\x14\x03\x88\x82\xc2\x00)(4\x98\x82\x0c\x80\n\x9a\x0f\xaa(\xd2j\x0dbA3a\x16\xe4B\x1f\x90\x1dnA>\x90\x0b\xb2\xdeB\xef\x0f\xbd\xd8eh\xa6\xb7Y0\x0c\x1a\xd1\x18.8\x069\xeb\xed\x84e\xd08h\x06\x19o\xe4\x9e \xd1 \x17L\x83\x06\xa1\x1a\xe4\x80k\xd0P+\xf9\xc26\xc8\x03\xbaA\x06\xf8\x06\xcd\x83p\x90\x1f\x8c\x83&A9\xc8\xda0NH\x07\x85\x83u\x90\xbd\x14\xdaH\x0b\n\xf1\xa0\x990\x8f\"J\x07}Ph\xe0\x07\x05\x06\x7f\xd0\x10\x00\x84\x0c \x102\x00A(\x18\x18\x84B\x02B(8(\x84\xbc\x81!\xe4\x03\x0e!\x7f\x80\x08y\x82D\xc8\xb8:[\xaf\xf6\xf7\x85\x14\x86\x01#\xe4\x0f\x1a!?\xe0\x08\x99\xaa\x11\x12@BsA$E\x96\x06)\xa1\xa0\xb0\x12\n\n-\xa1\xd9\xe3\xc1 1!\x0f\x98 \xf5\xa0&\xa4\xc3M\xf5T\x12?\x9aA\xe4\xefcA=\xac\xc6\x07 \x82\x9c\xfcq\xa0 \xc1l\x13\xf8N\xc5\x82\xc2F\xb2UA\xa3\xc1\x83\xa5\xcd\xa2\x1f\x12:\x9a\x06\x1e\x99\xe0\xa3\x89U \x0b\"\x19a$3\x90\xe4S\xde``\xd2\x00\x9c\xe4S\x0eC\xbbMF\x99\xfcZL\xc3\x9d&\x16s:\xfa\xa4\x0f\xd46\\\xd0$\xfc\xc9\x86@\xf9T,\x0c\ne\xc2\xa1\x06\x90\xa8\x18\xeax:.\x15\x18\x99\x8a\xa1\x8e{)$J\xe5\x85S\x85E\xaa<\xb0\xaa\xe0hU\x0cu\x0ci\x14\xba5\x1b\xdf\n\x8dp\xc5P\xc7r\xf2\xc3\xba\x02\xa3]1\xd4q\x0cu\xec@\xc1\x86q0wi\x82ba\xc3hX0\xef+\xf4\x85\xb5u_\x95I\x8a\x12^d\xebU 5i\x97\n\xba<2\x1b\x89\\\xcd^\xdd\xc4\x9bW\xc5\xae+\x94q\xb5(\xc9\x9e\xb0c\xee\x1b\\\xb6-k\xdb\xd7zud\x83K\xdd\xd5`\x11\xd0\x80\xf2q\x97\xc9\xf0O\xfc\xc0\xf2\x8f\xef\x96\xa3\x81\xf2|\xa3y\x82z\xad\x04z0>44\xe8m#{v`>E\x9e \xdf\x1e[\xc8\x11\x81\xfa\xd0\x14\xa7\xbe\xb1A\xfb\xd0\xd7\xf3\xb7\x93\x84D/\xbb6\x85E3\x83b\x99\xd1\xcb.(~Y\xbb\xd1\xcb\x90\xd8\xa5\x13\xb9\x0c\x8c[F/\xbb\xf9(dX\x0c2z\xd9\xf9\"\x8fAq\xc7\xe8e\xa7\xa5\x19Hc\xf4\xb2\x9b\x8f,F/\xbb\xe8e\xe7!#z\xd9\xf5S\xf4\xb2\x8b^v(,&\x18\xbd\xec\xa2\x97\x1d\xa4\xa0\xd8^xd\xcf\x1f\xd7\xf3B\xf5F`z\xbe\x88^\xf4\xb2\xf3\xc3\xf0B\"x\xd1\xcb\xce \xcc\x03\xb5\x1b\xf4\xb2\x1b\x17\xd4\x11y\x06v\xec\xa3\x03\x92\xf5\xde\x07\x19\xa02}Q\x81\xf1\xfes\xf9\xa6~d\xe793\x80\xe0\xe3\xbfc8\x10N\x86\x114I\xc3\x8erS\x8a;\x12N\x98\xe8\xad5\x05T\xf8\xaaNT=1\xd1u*\xbaN\x99\x05\x84\x04\x1d|`\x87\x19\xc0CX\xe8\xc1\x0b|\x08\x0b?x\x00\x10\xc1!\x88\xe8:\x05i\x14d1\x1b\xb4\x08\x0d[D\xd7)9\xf9\x01\x18\x81!\x8c\xe8:\x15]\xa7\xa2\xebTt\x9dr\xc3\x1d\x1e6\xfe\xe8:\xe5\xd38>\x10\x88\xbb\x15\xbc`\x90\xd1@Ht\x9d\xe2\xc9\x07\x18\x89\xaeS\xf3a\x92\xe8:5\x1aB\xd1\x8b\x1c]\xa7B\xc1+\x8f\x01\xb0\x8c\x81X\xf5\x82/\x8c\x15\xdb\x96E\xb3_\xdc\xbfZ|O\xffs\x9eo\n\xef\xba\xc8\xa8\xabo=\x90:\x0b\x01\x86\xad\xbb\x00\xab\xe7o\xc5\xc6\x01E\x13\x0d\xb7\xde\xa5\xda\x06\xa0\xe4\xd7\x13\xcc>\x10\xb2q\xc2\xd6\x11\x19FnsxZ\xc1\xbb\"'\x01\xd1\x8e\xc9\xac\x85u\xd9^x\x90\x0cr\xed\x93\xba@\xb8\xaeqr\x07\x9b\x81VAn\xe2\x9e\xd6\xa4\xa6U\x98\x0bdk\"\xdf\x81\xea\x92\x9e\xf5$\x1b;n\x9b`G\xd7\xba\xb2\xbaK\xf74\xcb&\xa9\x9bR^\xd2\xa8\x06&\xafQ_\x00\xe3+ \xfe\x8c\xc8CZ\xd5t\xa5\xa3C\xa4\xa8pV-\xd0Ow$'\xf7\xa4d\xad\xc1e\xa3/\x84Y\x86S\x19\x18\x82\xc2\xac\x8fd\xe1\x85\xfc\x19kR\xb6\x08\x17t\x89\xdb\x15\xf7\xb4.wi%W0\xcd\x93\x92\x99\x92\xa9\x1e\x98\xaf\xd9\x06)KL0=\x14\xb6\xe5k1\x14TdkR\nI|\\\xa4\x15\xb4\nm\xa0\x0dN\x85\xce\xc2\xf6\xa0\x15\xd4a\xcc\xd8\x90\xbf\x13\xe3\xb1jv\xbd1(\x9a\xe8)o\xa3v\xa7NJ\xb6\xbe\xaf\xb0+K10\xd6\xb8&/\xeatg\xd9\xf4:y\xc0\x10\xa0oV5\xde \xdd\xed@\xfb\xf1\xcb\x1d\xc9\xc5\xd0`\xbb,\xff\n\n\xd5\x93\xd7\xae\x1a\x9dz\x0e5\xbcK\xb7w/2rO2T\xe4/\x18\xc5\x01\xa59\x94\x92v\x1a]\xa0p7\x03\x8c\xab\xd1\x07\xd6(\xde\xeb\x11\xfbx\x15dU\x12\xa2\xfc\xd6\xa6\x9dTNMT7\x8a\xe9w\xfc/\xba\",\x94bzi\xb3|\x01\xf3\xd4\xdb\x94\x05\x11\xd6\xbe^I\xe8\xe2\xd7_\x1be\x1dQ\x1d\xec\xfe\x99i\xfa`\x7f\xfc\xb7\x99\xdf\x17l\xe5\xe0O\xd99\xaf\xe3\x0fl\xd9\xb0\x03W\x91\xa1\xd3\xf2\xe3\x1d9\x19G\x9e\\#\x1f\x96\x91F\xc7\xd1s\xf3\xe5\x8b\x0cs\x8a<\xd9D\x1e<\xa2^\x91Cr\x87f\xb1\x86t\x96P8~P8f\xd0\xf4\xdeu\xb2\x81\\< \xb1|\xff\xf2\x88\x14\xd8\xdb\x1c\xc0T\xab_+v\xc1\x198\x15\x17\xd8\xd9>U;\xf8H\xbcj\xbe\xf9\xdb\x85\xe1\x8c\x07+\xe7\xc24\x1d0\xf3H&\xe9a0F\x07b\xe6\x0c\xcd\xa0\xe0KK7\xd2\x01\x18\xdc\x03\x81\xc56\xa6v\xaa\x98\xb2y\x91\xbf\xf8;)\x0b\xde\x1bGR\xdb\xe4\xeb\xae\xccmqM#\xf5\x82\xdb\xb2\xbd\xc7j\x10\xecDCM\xd2\xb5\xd8\xdaU\xe3:\xc0,0\xd5V\x13&\x8a\xe9\xfb\x91\xa0\xd1#\x0d`\x03\xa6\xa2V\x1e\xfe&\xe5\xb4P\x0fC\xe5k%3{\x9f\xa11H\xd5\xef\x11R\xb6\x8bI\xd5\xdc\xee\xd2zE\xc7\x95\xe7XpL/I\xa0{\x86\x896\x02\xd6\x1f\xfb\xb2]\xf8\xc5p \x87C\xf5\xb8\x81=\xe1\x00A\xc1@\x12?\xf5q\xb9\xba+++gU)7\x023\xa4\x04\xb4\xd1\xb4B\x8c?Hw\x19i\xd7\xc1\x19\xda7%m~8\x1a\x18gE\xb8\xda\xfe\x9b^\xdb~\x1e\xceJ\xf3-\x19\xab\x11F\xb4V\x90\x9e\xff\x04\x1d+`4!#\xadZN\x90\x81Y\x846e\xc1,\xcb\xf7i\xd1\xc8X\x06|-\xcboI=\\\xe7K\n:\xcer\xb0\xec\xb5QHnN\xde|\xba\\\x9e\xbd\xbdAU\x8d\xeb\xa6Z|\x13\xa4\xa7@\xd8\xb1G{\xc3\x9b&\xb5\x04\x81Z\xb2/*P0\xf9\xf9.K7\x04%\x87$3`w\xedJ\x82\xce\xf3\xb4Nq\xd6\xf1b\xaf\xd49c\x190$ov\xf2\xa2\xf2\x02]\\~\xba\xf8tu\xf2~u\xb5\xddU0\x9a ?\xbf\xc5@\xd7\xc1\xd8/\x84\x99y%\x8c&\x8dn\x8d\xa6Ka\xc2]\x0b3t1\x8c\xff\xc8\x9b}9\xcc\xd7\xe0\xf8\x8c\xdfE\xb4Kb\x0cRM\xcc\x9d\x10\xdb\x05R'\xa6\x0es+\xbc\x1c92E\x0d\xf7n\xc6\xe8\x14\x91z\xd3K\xbf9\xea\x8dGX\n\xed\x1510\xb90/\xb5\x08\xaet\x88zQ\xd4\x8b\xa2^\x14\xf5\"\x14\xf5\xa2\xff\x0b\xf4\"\xb9\xf9\xa3z\x14\xd5\xa3_\xbbz4\xa0\xb0\x98\xd5\xa2\xf6F8\x7f\x03Q\xd4\x80\xa2\x06\x145\xa0\xa8\x01\xa1\xa8\x01\xfd65\xa0\xa77\xb0\"\x03o4\xcbd=\x88gK5\x94\xaa\xc65Y<\xe52\xa2\xd6\x13\xb5\x9e_\xab\xd6\xf3\xff\x1b\x8cBCj\x8f\xaa\xee\xf0\xaf\xaf\xd2k\xbd\xcc@9\x85\xb1*~\xea\xbb\x00\xd6mmL5\xa9\xe8\x17\x1a\x15\x919O\x01\x15*\xad\x10\xdb)\xa82 )\x078C\xfb\xa6\xa4\x9d\n\x9c5\xeb\xbc\xfc\xaa\x8d\xd2\xcf\xd3\xd96\x9c\xac\x81M\xf1^Y\xf7\x0f4\xd1O0R\x84W\x9e\x90\x95V\xad&n\xd0\xea\xd1\xa6,vZf\xe4>-\x9a\x8aKP\xf3i\xd5f\xbe\x9f'\x05\x1d\xc09P\xeb[\xd3\xd2\xcd\xc9\x9bO\x97\xcb\xb3\xb77L\xbfj\xaa\xc0=\nB\x8fG\xf4\x07|a\xd28\x11h\x9c\xfb\xa2Je\xfd-K7\x04%\x87$\xb3\xb8\x02\xb6k\x1d:\xcf\xd3:\xc5Y\xb7\x9b_\x99\xa6\xe4\xc0 #y\xb3S\x97\xbc\x17\xe8\xe2\xf2\xd3\xc5\xa7\xab\x93\xf7\xab\xab\xe5\xc9\xf2\xfaju\xfd\xf1\xea\xe2\xec\xf4\xfc\xdd\xf9\xd9[\xe7\xbbW\xd7o>\x9c/\x97\x1eo\x9e\x9c\x9e\x9e]\xf8\xbcxy\xf6\x1fg\xa7>/\xf2\x9ew\xbe\xf7\xd3\xf9\xf2\x87\xb7\x97'?}Tz\x91q^\x8f=\xab\xbfIs\x9c\xadj\x9ce\x87\x15(ic\x06\x85\xfeu\x9f[Y5;\xb6e\xd0\xd3\x03\x9c\xc8\xc8\x1a\xdd\x175\xa9\xe8`U\x84\xd13\xa4:\xca\xdb\x89\xcb\xc8\xae8\xb9c_s\xe7;\xa6\x15\xa7\x15W\x88q-\xcdn\xf0#\xa2\xa3^\x93X\xec\x9b\x8c9Dp\xca--|\x9ao\x8f\xa8\x80\xfb\x02\xa67)\xd3b\x8dH\xce\x0e\xd5\xf6\x05\x97<\x90\xa4\xa1%\xd1\x0e\xe9\x8c\xfb\xc7\x0e\xfbwx\xbf'y\x05TO\xd3x\x1e\xe5\xe3{ \xd5\x8am\xc9\xea\x83\xc1#\xac\xd2\x8d\xad\x10\xa1\x13\xb5]\xc3\xcf\xd3\x07RA/-\x14A\xf8\xb6\xa2\x9d;\xbf\x08=A\xb6b\xf0\x97\x14-\x83\xa6\xbc\x98_\x04!\xc3\x96{^\x98\xdb /Vt\x95^\xdd\x93:L)\x14i\xb6\xf2\xd07\xe4\xb2\xc0X]\xc1X]\x91|\xac\xee>E\xe7\xd2\xf2\x14\x85\xedt\xaf[\xe6\xf4\xc8\xc9\xaf|>\xed\xc0\x81L\x11\xb6.r\xa2mk\xd7yF\xb5r\x8c\xaa&IHUm\x9a,C\x1f\xaa\xed\xd9\x03I\xd8^\x8c\xb3\x8c\xacE.\xcf\xea\x82OB\xa2qs;\x95\xef\xae\xd0\xf9\xffl\xdaS\x81]>B(\xd3.\xd4\x85\xa0z~\xd4.\x15`.\xeb\xcb\xbb%\xac>\x1a\x11\xbd\x06\xd3\xd8\xbeH\xf3\xfaH8\x10\xa0\x1b}\xe1\xbc\xa1\xcfn`\x97\xbd\x81\x8d]\xd5T\xb9\xc2\xa0\xe5\x91$E\xb9N\xf3mv@\xcd~\xadZ\x92\xa0}\x8ar\xc2\xf2\xae|*:\x9b\x15\xbe\x0ds\xd0?\xceuk\xa2:o\xb4]\xfecQ_6y\x98-\xfe\xec?\xcfN\xaf\x97\x9f.W\x97gW\xd7\xef\x97~{\xbd\xfa\xd1\xc7O\xcb\xd5\xe5\xf5G\xff\x0f\xae\xaeOO\xcf\xae\xae\xfc?xwr\xfe\xfe\xfa\xf2L\xe9\x02u\xc3\xf6\xab\x0c'\x81\xcf?\x16\xc6\x1b\x12\x82\x86\x9c\x08\x1ct\"\xde\x90\xd0K!\x03P\xc0h\xad\xe2\x0d \x13\x82Q\x18\x05\xc5\x1b\x12\xe4\x146xE\xbc!AN~a,\x02\x07\xb2\x887$\xc4\x1b\x12\x1c\x01.\xe2\x0d \xe3\x83]h\xe2\xeaxC\x82_\xe8\x0b\xf7\xdd\x00~\xe1/\xe2\x0d ,\x8d\x0c\x87\x11oH\xe0\xc9'SO<\xf5tM\x10\xaf\x8b\xb0\xea\x10\xfcb23\xef\x1f\xc4[a\x98\x95\xe7\xc3\xcb\x1b,\xd9cp\xf3~)O\x06s\x03Y8zv8\xc6\x02\xc8\xf8\x97~.Wo\x80\xad\xe75\xca\xbc\xd8\xf0\x06\x03G(\xd6\x9e\x85\xb77\xc8\xdc\xf3\xaa\x98{\xfa\x18j\x15\x8a\xc17\xc8\xe1\x9b\xcb\xf9\xf2\xe1\xf1=v\x13\x05\xe2\xf3\xf91\xfa\x02r\xfa\x9c\xac\xbe`\xbc\xbe\xf9\xbdl\xe2\xf6\xb9\xfbg\x1e\xbfO_\xc7\xe4\xb5\xd2\x9b\xe178\xfcL\x14\x80q<\xbf1L\xbf\x11\\\xbf\x11l?\x7f\xbe\x9f?\xe3o4\xe7\xcf\xcd\xfas\x0f\x97\xa0\xcc?3\xf7o&\xfbO\x93fb\x03\x86\xe5\x03\x0e1\x02=9\x81\x93I\x19\x03\xcc@'\x84\x12\x88\x1d\xe8\xe4\x07\x8e+\xc8L\x8e\xe0\x10Kp\\A\xa62\x05\xfd\xb8\x82\xa3\xcb2\x91/\xe8d\x0cz\xed\xfdS\xf5\xbe\xa0\xccA\x0bw0${\xd0\xc5\x1f\x9c\xc7 \xd4\xd7\x06~b6q\x08\x03\xb3\x08\xad\xf4\xe3\x1f\xceb \x86\xe6 z\xb2\x10C\xf3\x10\xbd\x98\x88\xd3\xb9\x886q\xa4v\xb1\x11\x03\xf2\x11\x9d\x8c\xc4@\x9c\xc49\xac\xc4\xd1\xbc\xc4\x00\xcc\xc4\xe9\xdcD\xeb\xca\x83\xec\xec\xc4\xe0\xfc\xc4\xc7a(\x06\xe7(\xfa\xb3\x14\x83\xf3\x14]L\xc5)\\E\x8b\xa0\x96\xc18\xc0V\x1c\xc5W\x0c\xccXtq\x16g\xb2\x16\x07x\x8b\x1e\xea\x89\x83\xbb\xe8\xab\xbf\x84\xe4/\xba\x18\x8c>e\n\xccbt\xf1\x18\x032\x19\x83s\x19\x87\xd8\x8c\xb3\xf8\x8c\x06i\xb4$\xb5\x85\xd1\x18\x82\xd3\xe8E\xdcs\xf0\x1a\xbd\x99\x8d\x03d\xa7\xd1\xec\xc6!YF\x16G\x00\x8e\xe3\xb8\xc6\xf2\xe39\xfa\xb4\x89'\xd7q\x02\xdb\xd1\xc6x \xc2x\xf4\xe2<\xbaY\x8f>\xbcGG+\x8e\xe3>\xfa\xb2\x1fm\xfc\xc7\x00\x0c\xc8\x11\x1c\xc8\xe9,\xc8\xa1F\xf3dB\x06\xe6B\x0e\x96\xc88R\xc32\"-\x9c\xc8\xc0\xacH;/243\xd2\xc2\x8d\x9c\xc5\x8e4H\xb3\x1d\xfc\x1c\x8cI\x1bg\xd2\xc6\x9a\x0c\xcb\x9b\x0c\xce\x9c|\x1c\xee\xe48\xf6\xa47\x7fr$\x83r\x0c\x87\xd2\xca\xa2\xb4s\xe4\xfcYr>L\xca\x91\\\xca\x11lJK\xd5f0*GL\x8a\xb0,K+\xcf2<\xd32<\xd72\xc4H\xf2\xe4[\xfa2.U\xce\xa5\xfbL\x15\x92wia^B\x1a*H\xcb\xb5\x14\x81;\xdb;\xd2Z\xa2\xc7I\x17\x97\x96\xae\xb3\xfcy\x82sN\xd7\xea\xc9\xeb\x08^\xfd\x9ecaJ\xfb\x0c\xa0\xba`$\x9e5A\xcd^\xd3!O\xba\n%E^\xa5U\x0d\xd4\x03fm\xf4k\xa6\x9e<\x19\x7f\xea\xe7\x04\xedE7o:M\xe9\xbfU\xb1#\xad\xd9\xb3c\x0e\xe2\xaa*\x92\x94\x1d\xdb\x04\xf5\xa7/H\xa3\x11\x1a\xd8}8\xeb\xd8}\xdd/L\xde6\xbd'\xb9\x91\x1c\x19\x03\xb0\xc6\x00\xac\xbf\xd6\x00\xac\xfd[y\x86\xb9\xd8f\x9aw\xefU.\xcc\x8f\xf9\xbd\xc4YF\x857Y=\x9a\xee\xcd\x10v\xcbt\x02\x1a\x80<\x93JhC:\xfa\xe8\xb3I\xd3\xc7\xc2\xe6\xb1\x0e\x95\x00\x0c\x9e\x01\xee\x8e_\xb63\xf8:f\xa6\x8e_\xb6S\xd89.^\x8ew\xce\xa3\xb98\xba\x7f\x83a`\xf6\xc7\xbe\xf4\x82\xe7X\xff\xb1\xa8\xc9\x9bv~\xd1\xbf\xc6\xdfGE\xdb\xcc2\xe4\x19 \x8f\x17\x91\xfd_\xda\x92\xc4>\xb5b\x08\x0eP\xfe\xcaIS@\x92\xe4\xd99\xbe\xb1F[m\xa1\xef\x05\xd1\xdd\xd9`\"\xf9\xb3\x8ax\x16Dk\xad\x12\xd9=\x1cD+\xf6\xb0:\xd0(\x8e\xedR\xe1\x05\xb9\x17\xca\xa7\x15J\xee\x8a4!\x02\x7f4\xfb*XKm\x0e\xb1\xf5\xe3\xa7\xe5\xd9\xea\xd3\xc5\xf2\xfc\xd3\xc7\xc1\xa8Z\xf2{\x7f>\xd3cb\xc9\xcfO\xde\\-O\xce\xf5@[\xf2;\x1f?9\x1e36\xec\xea\xc7\xb3\xe5'\xa5\x958\xdd\xc5]\xf0\xaf\x1f\xdf\x99\xf6\x93\xdc\x1b\x8f\x1a1Yq\xb5\xa8{d;\xe6a\xd1M`\xba\xa7\xf7)\xd9\x03\x1b\xf7\xc0\xf2\xd2_\xb9\x0c/\x8e\xbaR\x8f~Q\xbd\x99\x1e@\x9d\xad\xfb\xc7\xca\xe2\x13\xd6E\xcb\xbaJy\xb1)\xdd\x9e\x14\x93\xd6*\xe3j5X\xa0\xf9+\x96y\xcd\n\xb6jM\xa2\xf1\xb9\x97\x00\xd3\x9b\xfa\xea\xe5\xb7~9W0\xef5l\xd4*\xf6Ky\xc2\xa9k\xd9W\xf1(\x9b\xb7\xa6\x19\x04\xd2%Fvz\xc1\xf01\x98 \x1c\xa7tpg\x10'ta\x94i\x9d\x1ct\x8d(\x9e\xd1\xe3\x19\xfd\xd7~F\x1f\xdax\xf5\xdd]zi\xc4\xd9\xa4zs\x98~(\x89\x1bz\x9b|\x17\xf8\xb8\xa1;\xde\x88\x1bz\xdc\xd0\x1d\x1b\xfa\xed\x81\xef\xe7\xbd\x89\x12w\xf2\xb8\x93\xff\x16vr\xc7!]\xbc\xe1\xde\xc3%c\xa4\xf7\xaem0f\x1b{a\xa6\x11\xdbb\xc0vg5\xd1p\xad\x1b\xad\xddY\x8d5V\x0f\x19\xaa\xbdr\x9bg\xa0\xee\x9b\x9e{\x91\x01\xf8\x87\xbao\xb7\xee\xa8m\x18F?\xb6\xd6e\x8f\xf1cT\xc9\x8c\xb5\xb7\xcc\xe1y\n\x98\xa2z\xb9\x9b}\xb4\xba\xa5*Z\x01T,c)U\xb5\xcaG\xa1\x1aR\xa5\\J\xd4\x80\xfa\xe4\xa18y\xaaL\xba\xb2\xe4\xee\xa1\xf9\n\x92Q5\x1a\x1c\x93\xba:\x14P\x11\x9a\xa6\x02\x99f\xe5'i,j\xf5\x91\x07\x90k\xf0\xd8\x06\xce\xd0\xa0\xb1\x0c\x18\xc7`\xf1\x1a(f\xd5\xa6\xab1\xab\x1bc\xda\xf3\x89\xc6<)\xa4e\xac\x12\xbc\x94\x9eU\xa7\xa3\xa2X\xdb\xc3Z\xae\x8eG\x93\xa3&\xef\xdc.\xa4L9\xafJ\xbap\xa6$uS\xe6\xba\x83\x97\xd6\xdc\xc7\xea\x0f\x12mG\xec\x9d\xd2\nm\xef\x9bc\xd3\x8fr\xd9\xf9\xd6\xe8!\xf1\xe3\xa7c\xe5o\xa9P|\xefsI\xe8:_\x95\xd5=\xe9Ke\xf0$\xdd\xe1T\xf9\xfb\xb2\xb8'9\xce\x13\xb2\xc0u]\xa6\xb7MM\xe8<8\x11\x7fxoQ9v\xae\x03\xfd\x8d\x95\xee\x0c\"\x17\xf6u\xbb\xdb\xf4\xdd\x89\x06\x97\x94\x9e\xfe> \x9f\xc9l\xd5$\xf1\xf3\x8a\xc9\xf6\xfe\xbeU \x07\n\xa6o0'\xcb\xe5\xe5\xf9\x9b\xeb\xe5\xd9j\xf9\xe7\x8b3\xeb\x1e\xa3\xbev}>\xf8\x9c\xd1\x1e\x07\x9e_-/\xcf?~?\x98\xc3\xe5\xf9\xd0\xe3\xf3\x8f\xcb\xa1\xc7\xef\xde\x7f:\x19|\xe1\xe2\xf2\xd3\xf2\xd3\xd0\x0bo\xfe\xbc\x94\xb6\xd2v\x05\xf3h.\xd3a\xa4\x1d\xb0\xcb\xc3\x9e\xf4\x0e\x9a\xb5\xe4\x02\xc5\xf6\xb5\xaa.J\xe6t \xda\x89\xf8P\x12\xc6z[\x0c\x165\xd8\x9f\xb1\xdby\x91\xd8\xa0\xe1jNO8\xdd\xd0n\x8b&_w\xc7A\xf2\xb0O\x81M\xb9\xa2\x1b\xa3\xe7\x80w\xec\xa1\xcbT8\xee\xd3U\xa9\xcd\x9d\x9d$Y\x86|\xfc\xaaM\xd86\x1f'\xb0\x82{\xf2\x9a\x1ep_\xc2\xd8\xef\xee\xfe\xa7\x9b\x80\x91\xe2\x87\x85\xf3\x17\xd7\xf8|\xd6\x97e;\x03\x1d[\xads\\\x0cM\xa1\xa1\xe93T\xdb\x93\xf5\xda\x17\xd1\xed\x15\xcb\xf2io-\xfdPm_\xca/\xe9\xde~\x03\x05{K2R\x93\xa9e\xb3|\xad\x15OyoB \xdf\xb2\xeb|\x93zRI\xdb\xb1\xe8\x16g)\xba\xf6>\x178\xa6\"W\xa4\xf6gG\xa9-m\xfeX+m\xff\xb5Q\xc5\xbbf7\x03\xb6\x15\xb0\xff]\xd5\xb8n\xfc\xcc\x0f\x1fN.\xfftv\xb9\xbaZ\x9e,\xaf\xcdV\x88\xfe\x1b\x17\x97\x9f.>]Y\x1f\xbf;\xffx\xf2\xfe\xfc\xff\xb5>?9]\x9e\xffxfyxz\xf2\xf1\xf4\xec\xfd{\xeb\xc7o\xe9\xb2\xf4\xe9\xcf\xad\x9d\x81\xebv\xc3U0\x0fa\xb9\x9dz\xfd~\x8f\xcb\xb4h*\xd8\x94\xaaNIh\x97!\x9c\xc3%\xf5\xb2\xad\xc1Z\x82\x81\xc2\xa1\x17\xe8\x9a\xbb\x9b\x9fsws(\x14\x82R\xd9\xa4\x8b\x0ePE\x8b\xdf\xd1\x0bt\x9e\xa7u\n\x17&m\xd2-\xbf\xd8\x8a\xae\xbfi\xb1>B\x0d\xb3hU\xe2N\xf0#>s\xb9\x1a\xc6.\xa4. \x96\xc30Z;Y-C\xfb\x00\xbd@\xa7\xbd\xcc[\x8d\xe0\x08\x95\x04\xaf\xd9\xbd\xe6\"K\x96]\xef|j\x1a3j^\xf0+z\x81\xae@JZ\x89r\x1f\xa1\xb2\xc9x\xac\xc7\x94\xa1E \xb1\xd6\xa5\x1ds\xaa\xfc\xf6\x01\xfd\x04:\xe6\x0eW\xe8\x96\x90\xbcSC\x8e\xd0\x9e\xe4lb\xaeIU\x97\xc5\xc1\x96M;t\xd5l\xda\x07]6\xbcY\xd8\xedp\x0c\xd0!9*I\x82!C\xa1GUp\x1f\xd6\x9a\xd0%\x8e\xe7N\xe0^\x8e\xbc@\x9b\xa6\xa4\x1a^\xbb\x8c\xd4p\x15|K\xf6tM\xa9\xc9\xfa\x18]\xb0\xa3'=@4\x15A74S\xc8\xee\x06\xa5yU\x13\xbc^\xa0\x0f\x9c\xae\xc6\x07~!\x83\xb3|\xc1j\xa9k\xed\xa6 \x873\xbe,\xcbl\x02\x92\xe3\xdb\x8c\xac\xb6\xc5=)\xf3Nm\xef*v[\x14\x19\xc1\xb9V3\xf5\xf7\xdetO\xf3u\x9a\xb0\xbd-\xdd\xa0N6\xbae\x83\x8e\x1f \xaa\xee\xc4\x067.vk\"b!`J\xba\x96\xa6 \x1d0lYX\x95dK\x1e\x1c-o\xa6\xb0\xf1;g\x11y\xd8S\xcd\x0fNo\x80\xfa\xb3\x1d\x9f\x1e\xd4xS\xb1\xac8c\x02\xce\xee9\xadr&\xc9\x83v\x14\xac\x87\n=\xeb\xaa(/R\xeds\xba\xff\xf1\xb3\x1d\x1bc4\xdb}\x86k\xda\x96`#\xe0\x85\x00\xcaE\xde\xbb\x84\xb4+\xf2si,z\x8dB\x85\xe0\xd8\x8e\x98\xd1\xe3\xa4\xd7\xb9\xfe\xd1*\xb8\xa6f\x8cU\xd1\xcdD\x11\xd8\x81T\xe3\x19\xa9\xb3\xa2+x\x9f\xeb\xc6r\x9a\x8c&c\xf9\x9b~\xf1 i\x85\x94?0\xb2\x98l\x8c<\x0f\x83\xb2\xf2\x9afVV\x9ek\xc6e\xe5\xb9\xc9\xc4\xac\xbcb44kb4s\xb3\xf2\x86ntV^0\x98\x9e!\xf9\x19\xa0\xc5\xbbv34\xa4\xa9\xc6h\x83\xa8]\xa7k\x8e0ICz\x04\xc3\xb4\"8\xb8yZ\x91\xff\xf5\x8d\xd4J\x01~AS\xb5R\x92_\xd0`\x0d)\x94\xd9Z\xa9\xd7/l\xbc\x86\x14\xd2\x84\xad\xd4\xef\x171d+e\xf8\xa5\xcd\xd9\x90f\x1a\xb5!\xb91\xc09\x06\xee\x9e8\xd1\xc5.3\xb7\xadP\x06\xcd\x05y\xf0\x14\xc1\xba\x00\xdfqIF\xd6\xa2\xae\"y\xb2\xd5\x02\xc4\xf2Z\xde\xf5Cx\xe9!\xbb\x98\x9e\xda3\xbd\x86\x8b\xd05\xd0\x08Y\x06\xcd7^W\xe4c\xedqUE\xfa\xfa\xaa)3o]\xd1\xadm\x9c\xa0\xeb\xcb\xf7/KR\x15M\x99\x80\xfb h\x13\x10S?;\xa0tM\xf2:\xdd\xa4:\x9b_\x13\xd6\x85\x0c\xd4\x15\nf\xebH\x8a\x0c\xdd6T\x95\x11\xd7\xe1\xc3\x9a*\xbc\x80vM\xd5\x06\x01D\xb8F\x19\xc1U\xad\xcb*r\x82\x9e\xbc|\x82\x92;\\\xe2\xa4&\xe5\x82\x0d)v\xa8\xaf\xc8vG\xe0 B\x8b{}\xf9\xfei\x85\xf6\xb8\xbec\xc25Qm0N=\x17\xfa\xf9\xa6\xc9\xb2\x03\xfa[\x833\x88\xeb\xcd\xda\x87\x8bf-\xf1\x8c\x05}\xd4?\xbe\xa1Y\xbe\xdc\x16\xc56#\x0bV\xf7\xdbf\xb3x\xcb\x0d\xa57\xcf\xa1\xc4L\\uW4T\xad`fL]\x89Kp^\xe4i\x823v:\xd6szF\x16\xdb\xc5\x11m*f\xa1|\xb2xB\xa7D^\xd4l\xb1\xdc\xd7d\xfd|\xf1\x8d\xfe\xd9y\x8e\xf6\xb4\xf1\xd2\x84\x1c\xa1\x9a\xd0\xa3US5\xec&\xec}I\x92b\xb7O3\xd2m\x85\xb7i\x8e\xcb\x03\xb3T2\x0d\xd4\xd0Z\x98\xf9g\x1d\xf4\xac\xc8\xc3\x9e\x9eK\xd3Z\xd8\x1e\xb8\x1aC;\x9a<\xb0\xae:\xc9\x0f\x0b\xf4C\xf1\x85\xdc\x93\xf2\x88\xcd\xde\xeb\xcb\xf7\x15\x18\xda4y\x0dhkzFUrGv\x04\xdd\xdc\xd5\xf5\xfe\xe6\x08\xfe\xadn\x8e\xe8\x0e\x9f\x17\xfc\xe9\x11\x1b=\x89\xe4s@w\x10R\xa3f\xaf\xc9\x03G\nC>\xa4\xbc\x176\xaf\x1d\xdeW0\x14X\x89\xa9\xaa\x01#\x1b\xd4\xe2\x94\x9bd\xe9\x8e\xcc4\x83cC_\xfc3:\xdft%\xa4\xddG\xd7\xa8tM\xf5\x14^ f\xc3\xa8\xaafG\xd6\x06m\xfd\x9f\xd1I\x8e~X./\xd0\xf7gKq\xa3\xc4\xf5\xe5{\x98P\x07f\xbe\xc4\xe8/\xeap\\\x1e\xf6\xe4\xaf\x7f\xf9\xab&\x0e B}.\xfa\x1d\x0c3\xac%\xf7e\xb1n\x12\x82\xb4\xf0\xf5ri\xf6\xfb,M0\xaf{I\x84\xf9\x856O\x82\x13:W\x8b\xe2s\xb3o\xfd\x01\xc0vS\x98=\x82\x10\xad\n\xcb\x9bE\x1f\xac\xef\xc8N\x1a\xa3k\x18\xa4X\x14\x95\xfe\xff\xbe`wW\xab\xa7`\x9a c6\xfdJ\xb2)Jr$>\xa4\xf2p\x9dr]&'d-\x946\xb6D\x94\xf7dm\x90W\xe4\x08t\x16P\xb3\xe8\xdcX\xa0g\xd7\x15A\xf7\xa4\xa4Z\x02\x81\x00\xc3l\xae\xc3\xf8\xc09\xde\x9ajy[\x12\xfc\x99\x9d5@\xe0\xe2\xb9\xde\xd3\x1f\x8b\x9a\x1c\x83\xe6\xbci\xf2\x04F0-/\x9f\xf3IS\x96\xccUDv\xb107k\xc1\xfcJt\xcf\n\xbeV\xdf6\x1bT\x12\xba\x02\x93#f,Ok\x91I\xc3\xad\xbb\xd2\xb8\xbf%\xdb4\xcfMf\x04f\xe7\xd1\x97\x8b\xc3\x9e,`<\xe2}Z-\x12\xba\xdd\xeb\xaf]\xb1\x19Q\x817\x07\x9dp\xb9:\xbb\xd13\xbe\xd5\x83;\x0cL\xa1\xe7hG5DM\xdc\xada2\xb3\xca0E\xae5\xe8\x83\xca\xcaosHPEv\xf4\xd4\x9c(j\xf2c9f\x1av\xe9\x0ft\x1a\xdfR\xb5\x1a D56\xaf\xb4\xaf\n\x84\xe0\xb6\xb87l\xd0\xed\x05\x15\xb5\xe2\x9d9T\x82\x9b\x93\xfcp\xd3\x8b\xcd\xdb]\xb62P\x12\xb1\x0e\xe2\xacP\xda\x02\xd4\xe6~W\xd0\xd5\x8a-\xa8P\x92[\x83\xf3\xa0\x94\x97\xd0\x1e\x94!s!\x06n\x96\xde\xb2\xe2\xf1u\xb4\x12g\x04f\x1c\xc5\xc9\xe7\x97MN\xff\xa1\xfb\x8e\xb0\xc6\x1af\x89\xbe\xe1\x16\x1b\xd4\xd4\xb0@\x88\xe9W\xb1\x03\xe4z\x9d\xc2\\\x84\x18\xd7\xa0\xf33\xbd\xbbEJN\x94\xf5\x08\xba\xa0/\xff\xec\x01\xd3A\x88^\x1d\xa3\x0bZ>:\xefxQq\xdb\xa0i\x8eN\xff\xe5_\x0c\xdb\xc0\xbb\xa2@\x9b\xa2@\xaf\xd1b\xb1\xf8_\xdacZY\x9c\x1f\xf4\x078?,hv\xef\xcab\xf7lS\x14\xcf\xf5W\x16\x0b}\x9dO7\xe8\x19\xfd\xf4\x9a\x15pY<\xfb'\xfa\xeds\xf4\xdf\x86\xb5\xcd\xf4\xfd\xcf\xe6\xba\x7f\xeb\xa8\xfb\x7f\xe0{<\xb9\xf2\xe85\xd35\xa8\xd4 5M\xabg\xef\x8ab\x91d\xb8\xaa,\x15\x85\"\xd0\x97\xa1\xec\xd2\x07z^J\x0b\xb4M\xf0{G\x13\\\x1c\xea\xbb\"74\x02\xe4\xfe\xae(\x9e-\x16\x8b\xe7\xa6\x8e\x86\x06xf|\xc6\x06\x01k\x16\xdfV\xa1\x1f\x9dC\xa3\xbc=\xbb:\xbd<\xbfX~\xba|n\xb2\x1aw\x03\xc5\x9c\x01dan\x8e?8\x9a\xe3\xfb\xc2`\xc0\xa4Mq\xfc\x1a\xfd\xd3\xfev\xf1\xae(\xfe{\xb1X\xfc\xac\xbf\x84\xf3\xc3\x11Uc\xe8\x9b{\xd8\xbc?\xe0\xb2\xba\xc3\x19m$sAMM\xa1\xe6f\xc8*\xdd(\x19]\xe7\xbb.+V\x106 \xd9[\xff\xe35\xca\xd3\xcc8\xc0\xcc\xf9+#i\xc9\x02d$\x9f\xdb5H(\x94\xe8\xf6\xd0m\xefb\x95\x04\x8f\xd2\x830c\xd3-\xb1/\xee\xa9a\xbb~I\xcfF\x0b\xf6\x80\xaa6O\xa9\x8e\xdb\xae\xd8t5\xa7\xbdU\xb3\xdb\xd9hI\xfa\x02\xdb\xa51\xcf\x0eB\x9f\xd7\x0e[\xad\xda\x84\xf0\xa6&\xb0\xcb\xb33\xde\xd3\x97O\xfb\xe2\xf8\x81Bd\x0d'\x08\xc2G\xcf\x93MQ,nq\xc9\n\xfd\xf0\xf2\xb0\xf8\xfb\x13\xa81\xe8\xc5\xba\x8a\xcf\xb2|B\xdf\xa3\xcbs\xef\x11\x8b\x15\xd0\xfb\xe5\xf5\xeb\xd7\xaf\xf5\xb6gW3\x18.z\xc8\xf9f\n\xfauS\x11a\xbc`\x08b_\x8e\xfe9}uM\xbam\xf0\x08\x91\xdd-Y\xaf\xbb\x0d\xf1\xa8\xbd\xc8\xa4'J\xda\x9e\x80\xcbp\xf3\xef\xb4\xda7\x9c\xc9\xd0n\xedr#.\xc4\xf4;6(\x888\xf9L\xe7^w\xa0\xd8\xa4\x19\xd1\xd771G/HY\x15\xb9q8\xf3\x93\xff&-\xabz\xc5Z\xfe5z\xa5Kj_\xa4\x03@\xbc\xf7\xad{EE\xc8\x98\xeb\x13V\xff'\xc7\xe8\x89id\xf7\xab\xb5\x80\xd2?92\xc9a\xe5\xfe\x88wT\xd6\xff\x86\"\xfe\x9b\xf1EZn\xe5=W\xe1\xcf7\\\xb1\xed\xf71\xf4PZ\xa1/$\xcb^0n\x19\x9bk\x8cI$h(\xfa@\xed\x0f\xa7#P\xb6\x941\xd6\xb9\x96\xf3,\xe9\xc0a|D6l\xfa\x02o\xd8 \x16c\x08B\x0f\xd4\x1d\x11\x86M\x814o\xc7\x9e\xb0B\xf3\xa1\xd7\x97\xc5\xc4\xb7#\x0e=\xa3\xf3WTW;\xb6\n+\xca_\xff\xf2\xd7\xe7\x86\xc19\xa7\xbf\xfb\x19\x98\xbb\x9cU\x9b\x8az\xb5\xf8\xf6\xd5\xb7\xd5\x13C7\xc2\xbf1\x16B\x8c\x85\xf0k\x8f\x85\xa0\xc0\x0d\x9a\x01\xdc\xcb\xbc\xde~\xc5e\xf9\x00\x0eoI^\xec>p\xe4p\xbc\xb9\xddrc\xa8\xd7D\x90\x1b\xc0\xa7\xdb\x81\x1f\xd4\xe4im\xb0\xc7\x9b\xf8\x16F\xb6\x85\xb1\x80\x90l\xf6~d\xa3\xfd#\x17w\xc3mR@B\xb6v+0\xec\xb3\xb2\xa5\x1b\xee\xcb\x84\xb7i3\x18\x85=#\x8b-jp]\xec\x9e\xeb\xfa)y\xd8\x179\xd1\x1d\x12P[\x11St5H\xf6\x18k\x90|j*\xf2\x97+\xbb/\xbe\xc0D|\xf5\xbb\xee9\xdb\x15\x8b\x9c\x00Z`\x94U\xe2\x94+\xac\xb7\xb8\"\xc0\x1e\x03\xe8\x13\x15\xe5\x1axN\x84j\xb1]\xdb\x19\xe5\xb0\x19p\x9d\xa7\xf5\xd3\x8a\xf3\xb2\x8c\xaf\xbd\xe2\x0d\xff\x1a\xbd\xfa\xdd\xff\xd7\x96\xb3\xcb\xda\xfc\x15C\x05\x04\xefU*g\xb1\x81>\xea\xcc\xe0\x9co\x86\xbb\xf2\x18\x05\x16\x1b\xf4\x94~\xf8\xd4b\xd0Cr+\xbfF\xdfQ\x05\xa3\xa9\x8e\xd1+D\xbf\x82\xd2\x7fg\x1d\x1f8Kqe\x1a\xfeC\xa4&d'6!\xf7\x04\x11\x11\x8d gf_o\x11r>\x07\xc43\xb1\xdcI\xf3@\x118\xbc'J}\xdd\xbf\xd4\x17\xe22\xa9v/l\x194=\xfa2\x9d\x87\x9dOR\x95&\xc0\xcb\xed7-\xaf\xa3\xb4\x80\xa9\xb3]\xd4X\x1e\x8c\x1b\xe9\x92\xdc\xa4H\xe5\x82\xd0\x91\xe4\xb5f\xba\xa6%\x15\xa4\x96\x85\xfd\x06\xc3\xf4Y\x07\x8a\xd1']\xf3i\xb1\xaaz\xa3\xeew\xbd\xa1\xb5N\xab}\x86}\xf5\x9d\xe1.\xe4\xb2$\xf2)[+\x9b\xedV\x02\xac\xa1#\xdb\xa2\x9bE\x80\x13P\x92Q-\xa6\xa7\x87\xe8A\x04\xfd\xca\xfa\xf4*\xcd\x13r\xcc\xaf\x82~Q\xad?\xa3\xdf-\xfe\xf0\xfb\xa7\xa6\x91\xf0\x94-\xeb2\xb3\xac\x87h2\xb2\xfc3\xb2=F\xa7L\x18:\xa1\xd3U\x96T\x1dv\xb7\x85\xaf\xb64<\x04@\x94P0\xb8{\n\xfc&\x90\xc8\xea\x8e\x1en\x8a\xbc%\x86TP\xba\x93\xe5\xa7\x0f\xda*\xc2\x80\xe4D=\xfa\"1\x86*fH\x10t:\xd6\x15:\xe2`nJ\xe9\xa5\xa6L\x83T\xfe\xfa\xf2\x9c!Uh]$\x0d\x03\xac\x9f\xd13>\xdd\x8f6/\x92;\x9c\xe6\xcf\xd5+\xf1\xda3\xbc\"*\xcda\x87L\x8b|\x81>\xf1\xc3\x83g\xd5\xbe\xebWmu\x87+c\xec\xd3)\xf5\xfb\x01Ww\xb0\xacVw\xf8\xdb?~G\x0f\xa8w\xc0\xddk+\xbd/\xe8\xbe\xcf\xacR\xd7\x97\xe7T\xa5\x7fZ1DF\x11W\x17\xe8\x9e\x94\xe9\xe6\xc0\x1aE\xad\x1a\xebR!r\x9d\xae\xf3\xa75\xc7\xd0f4\x88}=\x10Z\xab\xc7Z.}\x85\xf5eZ=2\xd8\x15\xe3A5\\\xfe\xfae\xef\xf3A\x0d\xfc\xacJ\xca\xe2\xcbh\xd5\x9b\xb0\xcf\x1e\x97\xe8\x12}]\xbb\xe4\xe7?4p\xa8\xeb\xf7\xb3\xcf\x81\x0eNs\xf0\x1d\x97\xe4s\xa4\xfb\x01\xbcWG\x8f(\xee%\xfb\xc8\xe4\xa9\xb1<{\xa5M\xa7x\x85CR|\xc3\xe5lGS\xf5\x8d\xc7\xc7\xe1\x03\xe4\xc0\x11\xd2\xa9#\x9bg\x96\xc7\xa7\xee3\xd9\xe4\xb9f\x905g\xca\x19\xc4\xf1Ihx\xe27\x17\xbb\xd4k\x86\xe9\xde\xe7\x90\xcc\x06\xa6\x00\x9e\xe8=y=\xe2\xb0\xc1\x1f\x1dE\xc3f4l\xc2\xef\xbf!\xc3\xa6\xb25\xf9o\x82?\xb0\x05}\x8ca\x13\xbe\x1b\xbd \x82\x14u\x0b\xf4\x1a\xff\xb5\x91\x16\xed\xd6c\x06\xf6\xa4~\x87\xf9\x8c\x08\x1es\x9f}\xc7%u##\xbdM\x16XB\x8c\x16\", \xbf\xedq\xc9\xff\xf4\x1e\x19\x00S\xaf\xf6E\xe9\n\x1e\xcd\xa7\x0d\xdb\x8f\x8a\xb2F\xf4\x1c\xc7\x9a\xb2\xe6\xa6\x1d\xd2\xb1.\x18\x0e\xdc\xcb \xb9\xc3yN2\xff<\xf8\x07\xb4\xb5}\xb2aC\xe5\xd8$\x88ke0\x87Es\x95\x92\x16\x16'C\xd0\xc9\x80X\xe8B\x9d\x8aa\xefjx_ \x02X\x92\x84\xa4\xf7c\xc4\x94$I\xf7)\x91\x88[\x1c\xd9]\x93\xaa\x16m\xc5\xa8\xe1b\xcc\xa4;R4\xb5\xa6V\xdb\xbbd _\x08\x8d\xb8$\x19f\x81/E\xbcK\xc0\x16{j\xb3\xbc\xc1\xd3\x8e\xe0\x99\xd2>X\xa7\x15UM\xd6\xe8\xcb\x1d\xc9\x19\x98^\x17\xe8w\x93\x94\xba\x92\xdc\xa7tc\\\xc1i\xc4c\xdc!/\x9d\x0e\xda\x15d\xb7,\x05\xee|A\xab\xd0\xa1\xa9\xbd8\x9cmqL'\x96\x99\xc5\xe1mO'\x02\xb7\xb5\x82\xa7\x8d\xc8S\x1d\x1a=}\xe8\x87\xf6x\x83\xd1\xae\xc8\x8b\x1ax!\xd9A\xc4\xc9e\x97T\xe0Z\x8f\\\x06\xce\x04p\xad \x83\xc0\xe9\xe8\xc7[L\xb5\xd76\xe4*\x17\xdf\x9e\xe7\x9ar_T,\xdc\xb0$\x89\x9d\x9f\x98\xb9+\xef\x9d\x9c7%!\x7fg\xd86\xf8\xb6(\xa3\x94\xfe[\xd5x\xb7w\xcc\x08K+\xba\x07u\x9b\x013\xa8\xddVE\xd6\xd4\x04\xe58/*\x92\x14\xf9\xbaBUJ\xd5\xda&O\x1f\x10\xd9\x17\xc9\xdd\xe4\xb1\xbd#\xbb\xc2ob\xb7\x9cG\xfa\xc97\xc8\xd0\xad\xd2\xae'\x1f:+\x16\xa3\xb9\x8d\x9a\xbbi\xf2m\xfa\x7f\xd8{\xbb\xde\xb8\x8d%\x7f\xf8\xfe|\x8a\x06\x9e\x8b\xe3\x00\xb6\xe4$NNb\xe0\x7f\xa1u\xec]a\xb3\xb6a\xc99\xd8+\x81\xe2\xf4H<\xa2\xc81\xc9\x91=\xc1~\xf8\x07\xdd\xd5\xdd\xec\x97\xea7\x923\xd2f\xa7\x81\x00\xf1\x88\xac\xae~auu\xd5\xaf\xaa\xaeku\x1c<\xabN(\x97\xb0\xfdw\xe4\x9a\x0e_)U\x16\xeb\xf37\x17?\xbc\x14\xd7\x86\x15\x08\x8c\xfe\x84\\P\xca\xfeB.6\xb4$\xb7\xb4\x1b\xed\xd2\xdc\xfd\xfe\xfa\xf4\xf4\xa6\x1an\xb7\xd7\xdc\xe2\x071\x11\xa7\xd5uy:t\x94\x9e\xde\x17\xfd@\xbb\xd3~C\xcb\xd3b\xb39\xad\xca\xfe\xc5\xcb\x1f^\xbe\x90\\\xbd\xe0\\\xbd\x90\xdc\xfe\x7fl\x07\xbe\x80\x80\x08&n\xc5\xc1_\xb6\x1d=\x81\xcd\xc1\x8e\xfb\xff\xd0\xbe\xad\x84\x93\xde+\"rv\xd1t\xb1\xe0\x15 \x13\xbbO\x12\x03\xf8\xa6\x7f\xcf\xef05\xe0H?\x89\xe7G\xc9\xc0%\x01;\x0d\xd9\x07>\x10Z\x94\xb7\xaa\xb7\xdb\xaa\xa6\xe4\x8e\xd2\xcd\xc8\xac$\xf0\x1e\xec@\x83\x08\x95R\x98,\xd2\xb7\xf7\x1c\xb0\xd5\xd3\xa6\xdf\xb2\x8b\xdfM\xdbU\xc3\xed}O\xee\x8b\x1d)o\xdb\x96)\x82\xad\x9a'\xfe\xb1\x8c#\xac\x1aR\xd2\x8e\x83\xf8\xd8W(\xd0Q<<\xf5\xb6\xe88\xd4\xe4\xae\x7f\x0e\x19\x96_\xdc\x17\xe5m\xd5H\xbb\x86\x8d\x96!\xe7|\xa6zJ\xca\xa2\xa7\xfdsc\x02\x04\xff\xd6\x04\xf4-D\x0c\x01A\xc1\x12\xbb\xd2V\xcd\x96\xaa\xa4\xccmS\xa1\x92\x94\xb2\x15\x11\x8e8s\xa2\x05\xc1\x1b\xcaC*{:\xa0\xdf\xf64q=ST+\x01-\xc8ib\x1a\x11\xd1\x81\xfab\x97lgk\x9f\xa7\xb6\xc7\xd1\xeab\x97\xff\xfd\xf1m\xa8\xb6\x18\xff\xfb\x9b\x0fZ\xe1q\xfd\x0fc\xe2f\xb1\xf5\xcd\xba_(q\xfc\xe2\xe2e\xc8KLh\x94\x15T\xe8:\xddB\xc5.AN^\xa2u\x18\x8e;\xa6\xd7\xce/b\xd953\xaa\x15/V4+\xfe\x01H\xd1\xcem\x08\xcf\xc4\xc0\xbf\xf3\xf45N\xd3k\xcf\xef\x91~\xc1B\xc2\xf5p\xa6\xb8^\xc9\x83\xe2\xff\x91uQ\xf7b\x88b#\x8f\xfb\xc0\xb0\xe7\x0d\xb2\x06\x81\x8a\n\xc0wQ\x7fsV\x0e\xd5C\x11-\xdb/\xbbs_0\xfa\xfd\xaf\xfe\xe6T>`\xdeAC,\xacVI\xf5?4\x1e\xec7\\&\xe4\x139\\\xbc\x13\x19\xe7\xe5\x08\x92\x80\xe2\x88\xb6\x10%\xa5\xb3;\xbev\xea}/g\x109Lco`S\x99\xcfE\x96G\x0f\x9fB\x9c\x04\xc6\x9e\xe9\x17\x90\\JY\x1d\xe6\xf5\xdf\xb6]\x93\xc6\x9e\xf5\xb0\xc3\x08\xfbc\xf2\x14\xbd\xe1\xd5\x07\x92;6\x1fw\xba\x86?'w\xfe\x1b\xad\xe9@3\xbf9\xec%\x87\x11\xfd\xa1Lv2\x19\x89\xb0\x90\xdc\xb9\xfc\xe0\x92\xbb\xb7_p\x18\x90\x0f$\xb3\xc0\x0b\x1a\x9c\xd5u\xfb\x95\xfd-\xc2\x08~\xa0z\xa98\xdcy\x1eC>\x1a\xaf\x9d\xf8\xbf\xfa\x9b\xf3\xebR^\x89\x92'\x0ey\xc7\xe1N{&y\xfa\xfe\xabj\x86d&\xf4\x87\x9d\xde\xd9\x1f\x93\xbb\xbd\xa0CzI\x86\x91\x01\xfc5\x87\x15\xf3\xb1\x1c\xa6r\xd26\x19l\xe1a\xed\x08c\xc6\x83\xe9\xacq{\xf3\xb9(\x16\xf4\x91\x97y)\x12%\xa0\xbe\xcf\xc3t\\~\xd1\xc7\x05\xb9T\xde\xb3\xf7zt\xa3g\xef\xf2\xcf\xdc'\xfe\xae\xedJ\xba\xcacG\x9f\xbc\x10\x15\x87G\xec\xe1\xcc\x89\x03\x12\x9f\x84-\xf7l\x18\xba\xeaz;LQ\x08b\x94<*\x95\xef\xb5\xcc\x11\\\xd0f\xf5\x1bmv\xbfW}\xa2\xacqy\xc7hx\xe6\\\x7f4S\x9f\xf9g5\xdc\xae\xba\"\x965b\xdc\xaa\xf6\x0b\x0eK\xf2\x81\xe0\x94 i\xc0\x98H\x97\x8a\x07)T#Y\x1bK\xa99D\xd4Z\xcd\xaf^C>}|#\x88\xe1\xdeV}\xa2\xb6\xabjxW\xd1z\xd5'O\x90p\x11_\xb1]\x92h\xe1b\x8f\xbe\x18\xaa{\x07\x0e\xc3\x06\xc0\xfex\xca\xfe\x08fT\x1e\xd5D\x9b\xa1\xdb\xf1\xc8^\xd1\x9b\xd5\xf7ub\xe1\xc9\xc1\xcd\xc2\xa0\x8a\x88q\x03\x8apw\xf3N;Z\xb6\x9d\xecI\"\x86\x0e3J\x0dsd3\xb0\xd4P\xef\xdb\x95\x00y;c\x15\xf1iv?v\xb6=<\xc7\x9e\xe8^\x0f\xdd\x17\xf4$j\x8cwo\x99\xdc\xb8i\x81[\x1d\x0d\xa4\x95\xc0\xaa\xa6\x8dX\xefR\x05(\xc9\x9a\\\xa2\x0b6\x0d\xb2\xa8\xf8)t\xc5\x0dv\xb8\xa0\xd4>\x07R\x16\x9ba\xcb\xcb\x86\xab\xccI\xa4\xb8\xe6>\x04\x89\x8a\xd7\xea\xd1\xdd\x17wj\x0c|\xa2U\xb4c\xb3\x92+Nw\x80\x91\xbb/V\x01\xd9\xf5\xa6m\x86\xae(\x87\x8b\x0d-\xcfW\xe7\xcd\xbaM\xff2\xc5\xabW\xfd\x86\x96W\xd5*q\xdf\x1a\xa1k~\x8c\xa6M]\x89\xa5\xe2+'\xa1\xb6\x9d|PE\xd9\xf1\xa9\xd0(\x19b\xd0%|\xb5\xe9\xe8\xba\x8a\x95\xd9\x8ds\xef\xa1+\xf9\x16\xff\xda\xb4\xdd\xa0!\xff\xed\x97\xbcL\x9a?l\xb7\xfb\x9en\xa4\xc3\x11D\xea\xfcE\x8cJ\xa3\x975>\xb6B9g`\x80sFJA-iy\xfb\xe3\x0f\x82\x92\x12\x13N^\x804\x1e\x13\xa6<\x95G}2\xf9\xff\xdf\xd2o\x92\xcb(w\xc8\xc2\x84\x0ex\xf7\xfb\x1ecC\x1e\x8a\xaej\xb7=\x979\xa4\xa37E'B\x06d\x17\x82\x86\xf1a\x91*t\xc2\xeb\xdd\xa9W\x92%\x8a\xd1\xd1D\x89\"\x84,\xa4\x86\x18\x13CtB\x83\xa9zk8\x16\x80A\x9fE\x9b\xe6o\xe3\xdf\x0c9\xad(GD\x913\x03\xc4\x9f?fb\xe2\xc53\xf2\xbe\xb8\xa7#G\xc3m\xd5\xdc\xe8>et|9\xf4\xb5\x7f\xc2f\xc5z\xf9J\xaf\xfbj\x98\x9c\x01\x84G\xf5\xb6d]5+\xf0w\xf1t2l\xca\xf5N\xaa\xb2m\xe6\xf4\x00\x91[\x8c\xcaI\xc2'\xac/>\xc4\xcc@\xe0nml\x05\xa9|\xdc\x16\xcd\n`\x8fRC\xb01\xb42;\xdf\xb6S.\x9c\xf6kC\xbb+q\\M\x0c\xea\xc0u\x97\xb3\x80\xa6\xd6~\x95i\x9e\xd4\xae\xad\x06\xb5k7\x05\xdf\x9bWU\xf3\xd0\xd6\x0f\xd4\xf9$\xa70\x85\xd5\xf1~A>\x9e}\xba\xfco\x9f\xc3\x0ey\xe8\xc3\xa7\xf3\x7f?\x7f\x7fv\xf9\xe1\x93\xff\x99\x8b\xb7\x9f\xfe8\x7f\xf36\xf0\xc4\xf9\xfb?\xde^\x04i\xbc\xf9|q\xf9\xe1\xb7\xf3\xb3\xf7\x01V\xfe\xf9>\xd4\xc7\xd9\xbbw\xe7\xbf\x9f\x9fY\x95\xbeM\n\xff\xf5\xfe\xfc\xdf>_\xf8\x1f\xf8\xf8\xe9\xc3\x1fo\xdf\x9f\xbd\x7f\x13 \xf2\xe6\xc3\xfb\xcbO\x1f~\xff=\xc4\xcb\x1fg\xbf\x9f\xfffM\x9a\xf2kF\x97 \x14R\xe3[A\x1fY\xe1\xe0\xe49<\x0cB\xca%o\xfc\xea[\xfc\xd7\xf8\xcf\x82:\x04?\xb4]\xc5\xb3{\xd9U\xa0\xd1\xbd\xf2\x1a\xfb\x91\xa8\xa4\x00+z=\xf0\xd4+U\xc9c\x1deT\x7f\x80\xb2\xdcc\xaf\xb1\x1f\xc15\xcaeIU\x92\xaay\xa0}\x98O\xb5\x1f_\xa3\xbf\xcaim\x06^\xfe\x98}\xe1\x8aw\x8eB\\UE#\x06 \x02B\xf8$\x85\x06\xc0\xf7\xf7k\xe7\x17#\xf3p\xd5sY\xb1\x13\xfdsY&\xe5\x0d\x13\x0b\x01\xf2\xea\xfbx\x8d\xfe\n3\x04\xc4Uu\xe3\xf5\xba\xaa\xb9X-n:\xca\xafy!\xfe\xe1\xebz\x8d\xfc\x06\xc4\xb9\xe4+ \x91\x10c\x1a:S\xc9ny\xac\x01\x1b\xd4}S]o{r]4wR\x88\x06\xba\x1d\xbf\xd9\xd7\xf8\xcf\xacs\x195 \xe7R\x9fJ;\x19u\xdd\x96w\\Y\xe1)$H\x7f_t\xc3\xa8t\x14e\xe4\xa3\x19\xe5\xc3k\xfcgs\xef\x8c\x91\xc40\xfe1\x8d\x89\xf8\xaa\x84\xe6D\x9eU\x94\xd0\xa6\x1d\xe8w\x81\xce\x95\xe0y\x8d\xfe\x8au-\x03)z\x81C\x82}jkld<\xe6>\xb2Y\xe3\xd0\x80\xa2\x83\x90\xf31\x9da\xd7\xd6\xb4\x97\xc7\x19\xa31\xaa\xb9\x1c4\xb4\xed\x1d\x1d\xd2\x10pc&v\xb16\x9c\x1e\xd8:Dv\x95\xf5\xb6\xae\xd7U=B\xf4\xaa\x1b\x9e\xa1FO\xe6\xc4\xf1h\xb0R\x89z\xa3\xcc{6O\x1f\xb6\xec5\x850\xc9\x8c\x8bh\xa32\x0c\xc6\x0453\x15s\xcc<$s+\xab\xd1\x89lG\xcfT>i\x1e\xa7\xc4\xee\x10r\xe3\xf0\xfc\x18W\xa6\xf2\x1b\xeaFOz\xc6\xdf=\x1dT\xe6\x17}^\xe97Zn\x87\xe2\xba\x86y\xb1\xd7\x17\xbd\xb5\x18fa\x05\xce\x16\x1b\xe8\xb9Z\x96\xfe\xb9\x86!\x93\xd1\xc7E\xc3\x83\x94\xfaj%\x8b\xcd\xb7\xdba\xb3\x95E\xf0\xf5y\xcd\xb9H\x89\xf8\xbb}\xdd\xa7\xfc\xa7\xbbM\x88C\xc1h\xc5Q_<\xad\x10\xbb\x86>\xe7\x08:\xed\xad\x15]\xff|]\xbe,^\x94\xbf\xaeV/^\xfd\xf2\x8fW/~}\xf5\xcb\xfa\xc5O?\xbc\xfc\x99\xfe\xfc\xf2\xe7\x97\xc5/\xaf@\x92\x89\x9b\xba\xb5\xdd\x9c\x9e\xad\x04\x08l3C\xa7\xeaM\xf6\xf8\xf7_^\xbe|\xb9z\xf9\xe5\x07\xfa\xcb\xd7\x9f\xfab\xf7\xd3\x8f\xc5\xfa\xcb\x8a\xf6\xdf6?\xfc\xf9\xe5\xcf\xbb\xee\xd5\xda\xcc\x9ar\x0e\xc8\xb6\xa2\xee[\x18\x8c\xf84\xcc{\xa2\xd1\x9f\x19\x9d_B\xaf\xb7\xde^\xbf\xbe\xfaa\xf5\xe5\x87\x7f\xad\x1e\xeeW\xc5\x9f\xdb\xaf\x7f\x96\xc5ju{\xfb\xcb\xcd\xfd\xf6\xcb-\xfd\xf3\xd5+9iUS\xd6\xdb\x15\xbd\x02\x06\xf8\x05\xdf\xb9\x05\xd8\xa1s\xbe\x90:\xffbb\xbd\xc01\xb8\xae\x8b\x1b\xbeC\xbf\xdeRH\x8a\xd5\xca\xa7\xf9W\x00_\x82f\xca%\xf6\n\xf5\xee\xb7\xe7]A\x811\x95~\x069 \xf4\x1b\xb0W\xad\xae*e\x8e$\x0b\x8e\xdf\xea\xc0?t\xf1 \xa80+0\x93\xac;^S\x81\x1a\x1b\xc0\x1c\xc08\xbf\xda\xd7\xba\xe4\x00\xac\x0eR\xd6\xae\xea\x89z\xba!\xbbv\xdb\x99\x03\xd8\xd6C\xaa\x15\xc9\x96F\xa3\xbf\x08\xfe9\xf8\x82\x81q)\xeb\x0f\xd3\x8bK\xc3L_\x9ba9\xb3\xacS\xce\xd8\xf1\x87\xe5`\xbfv\xc5f##Q\x9d]>)\xde\xc4\xc3\x94\x97\x8a\x9f\x92C\xcd\x10\xfd\xb2y\x0d%$\x94g\x8e\xcc7\xb1\xc9\xe65E\x91\xe5Ln\x8a\x9co\x06I4U\xb9k\x8a\x93-8\x83$\xdb4\x87\xbd\xb5H\xbfI&;\xd9\xbc\xa6;\xd9\xf2zO3\xe5\xc9\x86\x9b\xf4d\xcb\xef\x193\xf1\xa1\x0f#))\xa6\x9a\xfc\x10B\xa6\x11\x103\xfd\xc9\xe65\x01\xca\x86Y\xddd\xf3\x16\x9e\nN\xdcl\x13\xa1l~S\xa1\xeajq\xe61S\xa2lI&E\xf4a\x8fi\x11}\x1651\xa2O\xa2\xa6F\xf4I\xdc\xe4\x88>\xea\x9a\x1e\xd1\xc7p\x13$\xfa(f\x8aD\x1f\xf4\x98$\xd1g=\xa6I\xf4Y\xccD)[\xba\xa9r|#\xf6\x99/h\xba\x94\xcdc\xc2\x94m\x0f\xa6L\x94\xf4\xa2&M\xb4\x87\xd9\xa6M\x94\xeaaL\x9ch\xd7\xcb\x9b:\xd1n\xf6a\xf2D;:\x9c\xe9\x13\xed\xfe \x98@Q\xbe\x0em\nE\x99\xd8\xabIT6\xcc\x969\xb6\x19\x16S\x94\x9enE\x95-\xc4\xc1BVU\xd5U\xb6uU6\xd4\xca\xaa\xc8\xce\xbd*-b}\x95\xcd-\x88Gb<\xce\xb3\xca\xca\x86YgU\x0f \xddO\xb4\xd6ZT\x9c}4\xcfzk\x8fq\xb4\xe5\xe6Xq\xc7f\xe8\x1b\xa8\xed\xa0m\xc4<\xe3\x1bs\xccRnp5\x1a#\\\xcb\x98\xd3\xaf3G(\x01\xa2\x90@8\xea\xb0Z\x9d\x8e\x9b\xd6\"\xe8G\xde\x99W\x9a \xa6\x0b\x9b\xd7\xcc\xcd\x16\xfc\x1e\xe3\xda\xa0\xdd;\x89\xe1\x0e\x11\x1a\xa8\xe5\xd8\xbe\xea9Kb\xe3\x10e;\xe4hS@\x8b d\xa2c5\x7f0\xb1u\xb2\x1dt\xdc.C\xc4\x0bwD\xe8%\xc2:\xf1\xeeM\xf8\xa3l\xc1\xe1\xe7\x8ep\x02,\x12'\x14\x1b\xcb\x84\xa5\xcc\x1dK\x16|\x12'\x11\x80Q\x12\xa2\xf9EF\xbf\x86m\xc5O\xc2=9b\xcf'\xf4z\xbf\xed7h\xbb\x0cY.\xfb\xa8\x058j[ ~d\x8b\xd9\x81\xfd&\xd6\x08{a\xd5\xf6\xbd\xd06>\x81\xaa\x05IcE\x963\x95wP\x05G\x84tCh\x15Z\x05\x98\x90\xaaa*\x81\x9f\xfd\xc9e\xe2=\xd6j\x12\xb5X\x93\xa0\xd5\x9a\xc4'V<\xa4imjE\xf9h=\xaf0\xaa\x1e\xf5P\x7f$\xb5\xe3b$H\x9e\x0d\xbb\x8dHSR\x10\x9e\x19Mh\x93\x9d\xa6\x95z\xee\\\xf2\x83\xc6?\x01hI|E>\x07h\xe1M)\x9e\x89]\x07\x9e\xbdk;\xf2\xb6g:p\xd5\xdf\"\x9bnl\xb0\xbd{\xdf\xd8\xf1\xcb\x82lI\xc3N\x1d\x91\xaa\xa5\xdd\x8c\x15\xc3\xc9\xa6\xa2%\xff\x0cu\x15\x93\x0d/@\x0cbV\xe9*<\xb6\x18[\xe7l\xa7\xe2w\x82\x82o\x9e\xe7\xe3\xfe\x02]\x1f\xae\x04\xdeM\xc4\xaf\xc5%%\xcf\x04\xf0A\x15G'\xb28z+z\x80\xc4-\xecC\xf1\x10S\xd9\x87\xb3\xa5\xda\x19O\xfc\xc5\xe7\x93\xdfD\x8c\xabr_\x0cU/\xc2\xaeZR@\xea\xd5\xdb\n\xb7{\xc1\xe4\x9a\x13\xc4o<\x90\x87\xb6\x10\x0f8\xef\x06\xbf\xf2Y\x12\xfb\x0c\x8cPJ\xe0@\x10\xa3\x13b\xa5\xe2\xc94\xa9\x80\xd2+@6\x88\xa2qBd\xb8\x9b \xdc\xdfW\x9c\xf5l\x9e/\xc5\x0d\x16\x88\x8cr\x12\xc5\x90<\x93\x0b\xf5\xe9\xed\x9b\x0f\x9fpGD\xdb\x89\xbf^\xfd~~q\x89m\xfd\x19\x0e\x91\xdf\xde\xbe;\x7f\x7f~y\xfe\xe1}\x8a\xe9\x1c{\xe3\xe3\xa7\x0f\x1f?\\$?\x1e\x18\xa8\xefa>n\xe4\x0de\xfa\xcf\x1bD\\\xbb\x8cLLn\x87D\xb3\x15\x17\x0d\x11\x89\x9bNE\"'+O\xbb\xd1\xfc\xb3\xed\xf2 \xff\xa2w\xc6\xd5\x02.8\xc7\xba\xb4\xec_\xbc0\xef3u\xcaH\x87e\xd3\x0e\xca\x16\xe15W\xe2k\xe4\xf2\x03\xbf\x1bfrYe\xe0^\x15\x9f\x1be(/\x9ah\x9dzy,\xf0m\xe2\xe3\x83\xff\x11g\xa6\xd8a\xac\x94m]\xd3R^\xe1D\xaenu\x1c\xdf\x16\x0f\xbe\x03r\x10\xb9\xe9\xb8l\xc1D\xcb\xa6m\xfa\xea\xba\xa6W\xc2\xf6\xb4\x07E1p~\xfb\x85A\xa6\x7f4\xcfC\x9a\xe3#\xcd\xf1\x92f\xf9I\x93=\xa5Y\xbe\xd2\x0coi\x9e\xbf4\xcfc\x9a\xe13\x9d\xe25M\x93\x9bS=\xa7\x1eb\xdc\x9fJ\"\xbe\xd3\xbdzO\x0f\xe1?\xdd\x97\x07\xf5Q}\xa8\x07\xf3\xa2\x1e\xd0\x8f\xfa\xe8\x9e\xd4\xa7\xebK}\"\xde\xd4\xc7\xf0\xa7\xc6.\x04\x93}\xaa\x1ejvl\xca\xd8\x04\x1f\x97\xc6v\xd34\x0e\xfb2\xf27\xf4ud\x18\xd8\xcdPw\xa7\x99\x97\x1a\xf0\x85y.\x8b\x80@\xe6\xeeZ(\xcer\xaa|qp\x81=\x15.5\xeb\xcd\x0c/\x1az\xd3B|h\x86a\x17\xf7\xa1\xc5O=\x84\xc4\x1c/\x1a\x11\xeeIw\x04\xb6\x95}\xa2\x11\xd8\xe4w\xc2\x8d=b\xf3\x8a\xcd\x17qx\xf0\xfa\xd3\xb0Y@\xe9y\xfci\xce\xe2\xf8\xfci\x87\x1f\xf3D\xaf\x9aM&:\xe2\x14W\xcc#\x8c~\xa6o\xcd\xf1\xaee\xce\xcam\xd1\xdf\xd2\xd5T\x93\xd5\xf2\xd3\xa1\xf1#\xe7\x01~\x02\xcb\xd7\xc2\xdb\x02w.F\x87\x9d;\xae\x19\x0e\xc6\xf8x\x12\xe0\x0f\xd0R\xb8\xc6\xa1\x10\x95O|\x1b \x07\x94`\xb5\x1a\xad \xab1\xaa)<\xa4\xa8\x93'\xe6\xe2\x89\xa3$\xa0EM\x04\xd1-N\x12'\x96,\x84\x9e\x80\xd6\xa7`(\xa0%#)\xa0=\xee\x8cL>\x0b0b\x89\xf3\x91v.\x90\xc7\x9f\x1b\xefI\x91HBOF\x95\x8a\xc5\xc0\xe8\xf8\x84&I\x9b\xa2i\xe3\x9f%B]ri#\x9d\xb9\x19\xa6\x8dt\x12\x82\x03#\x14\xc4q\x90\x08\x7f\xc85\xe7\x9f<\xac\xaf\x1bU\xfa\x82\xf4UsS'h\xa7\xe0@\xbc\xa7\x84~\x1b\xbaBCC\x80\xf1\xa4\xd2\xaaV\xf9\x99B\xa1'po\xdd\xa9\xaa\xb8\x9d\x8a>\xc4\x98\xd2oR\xa8\xef\xcc\x98\xb0q,\xdak\xcf\xaa\xf5\x98N\xf4\xbb\x11\x16c\x85\xb3\x1a\xc30bQ\xcbv\xb3\x1b\x955\x11\x16z[\x0c\"~\nR`RH\xc2\xbd\xad\x87~~\xbc\xa4s\x00z\xf7nx\xc7\xdaD\xd3\x02\xdd\x81\xee\xfc`w\x87\x03\xdb\x7f\xbc\\\xd0;\x99\x1f\xf8N\x96\x0d~'\xd1\x00x\xa2\xd6uVmYg\xd1g\x05\xc4\xbb\x13\xe2~\x92.\xc49\xb8\xca\x9e\xc0x\x12\n\x8e'{\x9a\x9b\xd9\xc1\xf2\x16=\xdf\xc0\xbcA\xf3dO\x03\x9b\x1bDo\x91\xdb\xb5\xdbnz }B\xe6eA`?\xa1\xf4\xe2\xdc\x83Ywdp$\xb1\xc8\xb8V\xfb\x97\xde^\xe5s\x01,\xa2\xbeb\x18\xed%b\xd1\xbd\x97>\xffuoB\xbc\x85\xb1\xf7\x92\xe2\xce\xbdc'\x99}i\xff$\xc1X\xf3`\x9cyz\x8f\xe9\xf1\xe5\xfe\xd8\xf2\xbc\xde|1\xe5aq3-\x96|\xd4\xe0\xec\xd9\x0b\xc4\x91\x07c\xc8}P\x03\x14d\xe0\x9d\x98Eb\xc6\xc3\xf1\xe2\xcb0\xeaC@$c\x1fRQ\x0fix\x874\xa4C\"\xc6!\x01\xdd\x90\x88kHB4\xa4b\x19RQ\x0cI\xf8\x85<\xe4B\xec6:\x0d\xad\xe0\x89\xf3\x0e\xe0\x14\xf6\x84P\xd8/6ayT\xc2#\xe1\x11\x0e\x80D8\x08\x06\xe1\x11\xd1\x07O\x11w\xf0\xe8\x88\x83\xc3b\x0d\x16Ni\xa9\x91t\x04\xe3\x82A\xd8\xd3\x02\xb0\xbd\xc1\xd7\x93\xef\x01C,\xc2\"5\xe0\xda\x8d\x9f\x88)E\xc3\xf4 k_\x80u\xac\xcb\x19\x81\xd5\xd8\x9eX,\xa0zV0\xf5*\x1d\x02\x82o0\x1b\x04\xe2\x98\x8eM\x0b\x8a_y@_\x1cm\xc4 \xb0\x0f\x82\xd7t\xf0A>2\xee\xd16o\x89\x1b\xc7\xfb\xfd\x84U(\xbb\xb7\xa8{/\x08\xe7@\x9cz\xceTc\xae\xbc}\x8fj\x82\x8b.\xe4zIpY\x1cx|~o\x8c\xfb\x17\x8bV\x86s\xcd\xfc\x9b\xebR\x9bh\xa2w\xc9Np\x9a\xa5\xf3\x9d\xb1<9|g\xbb\xc0\x12\x161\xdb\xf0\xe8wwy$j\xd1\xc8\xfd\x10uu\xe5\x98\x1f\xfb\xb3\xba\xce\xcdm|L\x13\x1b\x1b\xc0\\\x0bw(M,!\x9b\x82\xdfS+o\x02\xd5\xf1\x01\xa5:\xa8\xfai\xda\xdfT(d/-\xdd\x92\x85I\xe7\xe1\x1d\xdd%~\xadi\xc2\xf4\x7f\xec\xcf\xf8\x8e\xc2=Q\xc4\x13ut\xd8v\x0d\xe0~>\x167\xaa\xbe\xf0IC\xbf\x0dW\xec\xe1\xa1%\xd7\xf4\xc6\xd1\xef\xbfli\xb7c_;\x1b1{\x98M\n%\xf7m?\x10\xba^WeE\x9b\xa1\xde\x9d\x90\x0fM\xbd#m\xc3\xd5\xb9v\xbd\x06#\x01c\xc3\"\xd8\xdf\xb6\xdbz\xc5\xc3@\xe9`\xd8\x03\xf9K\x99\xb3\xb25\xcb\xee\x91\xa8x\x13\xac\xf1\xa9i\xb6\xf7\xdc4 ~\x83Kh\xd10\xde\xf8=\x91_!`\"-*\xdb\xa6x(\xaa\x9a\xa9\xa8\x98\xbb\xb2\xeaI\xcd4*5A\x8cvC\xb6Lj1\x82\xb9\xb3ew\xe1N^]\xddW{\x9f;\xde\x89<\x0d\x86v(j\xe5\xec\x97\x91\xa5=\xec#c\xbf _\xd1\xb6v\x10\xc1\\\xeb\xb5\xa7oMj\xba\x1e\x08\xbd\xdf\x0c;R\x89d\x08\xc2\xc0\x06Qp\xb0\xa5\xa1#6s\xd7;(\x9aXl6\xe6\x01\xb9m\x86+\xce'63\xcb\xfa\xe9\xb4\xce\xd8\x0c\xf1\xfd\xd4\x92\xa1\xdbRb\xd9\x1b\x8aA\x9b\x11\xfe\xa0Xd\x93\xa0\x90v\x8ek\x1dz\x92\x8a\x8d\xbd\n\xdc\xecL\xd4\xe6\xe4\xb2J\x93b\xce\xe7\xfd\xf9\xbcwV\xc0\x1aJ\xcb\xb6*;\x15h\xa9rQ\x8c_\x11\xfbPN\xc4\x9e\xafn\x9a\xb6s\xb2\x00\xc8\xaf\xc8q\x1f\xf7\xf6&\xee(S\x86\xd0K\xe5\xb2\x8b%:\xb2\x17\xaa\x1aw1\xb7b`;\xd9\xa2\xc4\xfa\xa1\x0d\x8f\x1ch\xbb\x15\xedN\xfef\x0f\xf3\xa2jJ\xfa\x9a\x94m\x7f\xdf\xf6/\xfa\xd5\x1dyy\xf2\xeaG\xf5\x90\xb8\xd8\x1a\xf2\x1b\x04\xf4\x98\xb5\x9c\xf3A\xef\xaf\xe9j\x05|\xdc|\xfa\xf8F\x9d\x80\xe2\xfa\x08\xe7\x92\x928\x1a\xb9q\xfdO\xc8[qG\xc9\xd0\xbe4\xad\x87\xcc\xca\xa1\xce\x08\xcdp\xfe\x02\x1f\x8b\xe4R?\xa6\xc9\xc1\xde\x9e\xef\x9a\x86\x16t\xd2.\xea\xa6\x16\x04\xfd3J\x12\x10\xca\xa1L0\x91\x19%\x93\x9c\xd8\xee{\x8b\xf5\x9e\xec\xd6\x86\x16M\xa2\x9e\xcfC\xba\xa3\x1bZ8\x95\xfa\xb4\xfeC\xe9\xd4cG\x03\xb4inp\x94TzR\xf5\x84\xb4\xea~\x7f3\xb4\xe91\xf8\x8b\xb8\xca\xa1\xc5\x13\xac\xefo\x18\xc7T\x02\xb6\x0b1\xe5\xc1c*\x01\x92(\x18\xa6:\xe7=\xc4\x8e\xa9\x04\xec\x1fS\x9d\xf6\x08\xdd\xc3\xb9\xee\x91\xce\xf7\xe3\xc0G:\xda\x97\x1b\x1f\xe9\xea\xb0\xce|\x84\x81'\xe2\xd2G8{\x0c\xc7>\xc2\xc6\xde\xdd\xfb\xd0\x9eX*\x01\x0f\x1f\x0b\xe2\x03\xa0MC @\x0b&j\x8f\xaa1iW5\xcfN\x88\x17\xf2\x97\x07\x86\xf4\xedA%*|'\xca(\x97\x1c\xfe\xf5\x89\xd2\x04\x8f\xbd \xbf\x16\xfcs|N\xaa\xa1\xd7\xaa$4\xa0`\xaeH\xcb\xe6\xe1k%\x8c\xe4\xf8W\x1e\x05Q\x91yy<\xd2\xe1\\\xbf1\xae\xb9\x05\xf3r\xb71\xb0[\xda\x82\xe8~\xfb\x94\n \xd1\x9a\x1f\x91*\x1f\xf1\xba\x1e\xc9\x95<\xf0\xf9\x9f^\xad\xc3\xa8\xcc!\xa8\x05\xebs<\x85\x8a\x1c\x8fR\x83\xe3\xc9U\xdd\x18\x9c:\x1b\n\xee\xa6\x7f\x03V\xef\xca\x1d\xb0RO\xe9\x1fc\xd5\xab\xe5\xf6~a\x16\x98\xcc9E\xdc\x13\xc4t\x9f\xa0\x02r\x95\x8cm[ah\xb6\x14\x9a\xab\x18b\x0d\xc5\xa8\xc5)\xc7qh.\xf2,\x8d\xaa\x8d.\xc3\xbf\xfei\x082\x07-&\xa8\x19\x981\xcf\x1e\xf8w:\xfc\xdb\xeel\xb5\xea\xb2A\xb2}\xd9n\xe8\xbe1\xb1\xac\x0f\xc4S\x12\xb8\x00\x06\x8c\x14~\xdf\xdbg\x80\xaf\x9e\xff\xa6\xc1VY\xd7'\x84\x9c\xdfoj\x0e~\xe8I\xbf\xba;\x91P\xb7\xaa\x19h\xb7.J\x8ah\x89\xbcR8S\xe7;\xe5R\xa5\xa0\x11k\xf9\xe1\xc9\x1b\xae\xc8\x9a\xc3\x8d\xc0u\x97\x1e6\x97;l\x9c\x96\x01Va1\xb8\xe9v\xb0\xb3\xe2c\x9a1\x81\x82fT\xce\x95\xc8\x88\xcf7d\xd5hSj\xbc\xc7\xc1,\x9e\x847\x18\xcc\xcf\x03\xf1\x0b\xf8\xe3bH^\xb1>\xd8\x9f\xa2\xd6z5\xb5\x05\x8e\x7f|&\xa5=\x06\xcb\xe8\xda\xda\x03 \x8e\xe1&\n\xfe\xee\xb8Sewj\xaa\xc1\xc8\xce4\xe6`\xbe\xd1M\xd7\x96\xb05\xd1\xd2\x9f$e\x02\x8e\xb8I\x13\xb9\x95\xf2\xe0\x117I\x92\x8c\x0c\xe4\x88\x9b<\xe2&\xf1\xce\x8f\xb8\xc9#n\xf2/\x8a\x9b\x94^\xc0\x90F\xe23\xdc\x92\x04\xe3.\x195\x0c\xe5\xf0TK\xf8\xf7\x9e\x83\x1b\x0bvi\xe0fm\xc1\x8cC%\xa4\xa3\x9c\x01\xb2S\nF\xa1\x1b\xb1\xcf\xe3\xb4j\x98\xb8\xe13\xc1u\x18;\x877\x86\xa8\xf4\xc0\x90b\xe7\xc7%\xf7\xbbK\x04\xa9\xda\x9adh7\xa4\xa6\x0f\xb4\x16\xca\xe7\xe8\xbe/\xdbn\xd5\x0b-\xea\x84D\x08r\x9b\x03\x9b+\x9e\xbd\\\x98\x8e%n\xb4]Uk\xc4\x8c\xcf\xba\xe1Z\xb2\xa6\xa9\xc9k\x06P7o\x0bUC\xb6\x0d\xd3\xc7\xd9\xf3\x0e5\xc9I]\xf5l\xfeZA\x8f\xf6\xbd\x0ef\x80\xc6z\xbd*\xca\x12\xd1t'*\xdb\xa8N\x18[\x9335\xed\xb8\xac\nK\xb2\xd8\xaa\xfcS\x08\xb7\xa2\xae\xd5\xa7b\\D\xf9TV\xecZ/>\x19iGt\xbf4!:\x8c\xd7\xff\xdeK\x01\xb2\xaeh\xbdB\x9c\x840\xcdu\xdf\x12\xda\x14\xd75\xdc\xb3\xb8\x97\\H\xd5\xff\xc7\x9df\xc0\x8c\xa0\xc5\xa1O\xc0\x8f\xef\x83O\xc5e]p\xbacU\xf8\xaem\x07\xcdF)\x91\xbf\xba\x91R\xca@\xc6\x0c[s\x83\x1ew\xf1w`\x11sX1\x18\x01\xcb\x90\x86\xfdjv\xf27\xfe\x81|\xa5\x1d\x1dk0\xf01\xaf\xdbm\xa3\x10grA\xf6l[\x82^\x162\xb3\xec\xc7t\x13\xdb\xe4*\xec:\x8c_\x16\xd3^\x8c\xea\x1b\xd4\xc1w\xe8\xc1\xf6\x86\x891?\xe6\x0d^\xe0y\xe2\xe9q4\xd5\x1cM5\xf1g\x8f\xa6\x1a\xfb\x9e\x93\xf2\xf4\xd1Ts4\xd5\x1cM5\xce\xafGS\xcd\xd1Ts4\xd5X\xed0\xa6\x1a\xc1\x81\xba\x85 \x97\"\xe5J\x0fDw\xf0?\xf0\xef\x83\x0d\x8a\x87M\xe0)\xab\xf1\xcc;\x01\xe5\xc7?|\x1e\xcb\xe9z\xcfu\x1dX\xc4q\xb2\xff\x13z\xdas\xd6WU\x16u\xed\xe2\x9by@\xa8\x82p\xa8_\xe1\xc5\x0c\x96g\\\x08\xa46 \x91\x0d\xecb\xca\xe55;*\xd8yT\xd6\x1c\xc2\xad>2n\x8d\x1aW\xd6!'\x93\xcf`7\x82b\xbbr\xb1\xd91\xfe\xde\xf0{\x07\xbb[>'\xdb\xcdJ\xfd\xffP\xdd\xd3~(\xee7\xfds\x15\x8e\x05\x109\x88^\xedh\x8d2\x88\xa5\x08\n\\1\x82\xd1\x99\xc0\xda\x15c\n\xfb\xa2#\n\xb6\\4\xf6\xfa\x0b6\x1c\x8c\xc4\x18\xc9\xcc\x1e;e\x8f\x01\xec\x97o:\xda\x0c\xdd\x8e_\xd3\x04/^\x1e\xaf\x91\xd0\x8d(\x87x\xf6\xf8\xc1N\xd8#\xfa\x90H\xd3\x12\xab|/\x96\xeeI\xccU]\xf4\x83d\xc8\xcb\xe8>'\x8c\x9b{\xab\xc8\x8c\x89M\xed\xe7\xa2j\x06zC1\x85N\xce\x16\x1e\xcf\x10\xbb\x8425C\x1c\x0d\xd6\x97\xa5pDUSv\\\xf9\x91\xb2\xdd\x13^ \xa7\xd3\xf9\x93@LO\x9f\xe2\x04\xdeE\x1f\x88)\x95\xc2\x96\xad\xda\xe6\xd4\xc3\x1fk\xf4\xc1\xd5\xee\xfc\xbd\x9f1\xe1\xf6\x8e\xc9\xcf\x9e\x94\xc5\x06\x0eY4\xa6\x9co?\xb5\x1fZr_\xdc!\x13\xc4\xb7\x88\x8aGlVr'\xd3\x1dX\xc3\xee\x8b\x95\xfeV\xd0\xae'\x8e&e\xd9CN)R\xdc\x14U\xc3\x18S\xb2\xde\xa0aZ\x87d\xca\x04\xcb\x86y\xa9Y'8\xc0\xfa\xb6x\xa0\x1aE-X\x93\xaa\x04\x1a`f\x85\x14^6\x8e\xff\xfd\x07\xa6\x9c\xffS\x8c\xdcBd\x7f2\xfc/\xa4\x10\xf6K\x91uBZ-\x0bl*\x9e\xf3\x85\x90\xf3\xc2\xbf\xc1\xfb\x96\xf7/\x1f4\xcd\x93\xd2\xdek\x1a(\xe5\xafI&Ja)\xdd\xaf\x85rQM\xe7\x14\xcb\xcd\x08\xc38!\xe4\x0f\x03H+\xcc\x8a\xb0\x14\x0e5\xddmuV\xf7-\xe1\x18cR0\x95\xe3\x1d\xbb.8*\xd0\xb2\xa6\xd6\xd0HGK\xa8\xd4\xe0\xe4\x0e\xf6\x98@\x83B\xfb\x19\xc7\xf11)\xb3\xae\xea\x81vtE\xee\x1ed\xd2\x80\x81v\xc5\xd0v\xe6}D\x18\xea\x9caz\x19\x16/\xc8\xbdh\x08\x19\xc9),G\xbd\x93\x91\xd0;\xccn2\xca\x80\x96_\xe2\xda\xf5ZO\xdc\xa2\x07$\xfb\x06\xbe\xaf=\x81\xdf\x98\xd3\xc6/\n*Zrs\x9e.\xa4\x19\xd3\x1eB\x1fe|L`\x1c\xeaC\x9e7\x18^\x92C\xc5\xe3\xac\xb9\x07P\xa2\xff\xe1\x08\xb9/\xba\xfe\x161\x0f\x11\xd2\x0f\xc5\xb0\xf5\xb9\xd5\"su\xae\xac\xb8\x95\x08\xc3\xe0\x9f\x18\xff\x00\xe5\x02K\x86\xd4:{'\xec_[\xae\xb2\x82\xd5\x89-\x01G\xcao\xb6\xb8Q4:7a\x8f\x97\x08\xeb9\x7f\xff\xf1\xf3\xe5\xd5\xc5\xe5\xd9\xe5\xe7\x8b\xa8'\xc4\xf7\x9e\x15D\x96\xf6\x92\x11Xf6\xe5\xa5\x99\xc2d\xcc\x06\x03-:\x01\xbe\x99\x8b\xbc\x86\x85\xa1\xd9M\x84\xa3\x9d\x8a\xf04\xb1g\xd8zz^\x88L;>OH\xcc\x9a\xbdC\x1bRt\xd7\xd5\xd0\x15\xddn\xfc\xba\xb9QL\x1d#\xb0\x0d\xf3\xf8\x921l\xfe\xbf\xe1\xa5TR)gC\n\xa1_\xcf\xd6\x889\xda \xe3\xbb\xbc\xad\xe8\x03d\x86\x81\\s\x00PC\x02\xe0\x91\x9eEv\xba\xc7<\xa7\xf7x\xbe\xfc\x878[\x14\x1cPL\x90\xdar\xf0\xef\xd3Qe\x97\xba\xaf\x87\xa0\xe7\xf62O\xb2_\xf0w%\x9fri\xc7[\x06\x06i\xf3\x90\x12\xe3\x13[\xbejnH\xbf\xe5X\xc4\xe7\xeb\xa2\xaa\xb7\x1d}\xce\xf4\xc1\x0d\xa4\x00\x986\xe71Q\x7f\xf1\xf9\xf7d\xf9\xe9\xbe\xf1\xf1\xec\xc2\xef\xd67\x1f\xbd\xf8\xcf\xf3\x8f\x89\x8f\xbe;;\xff=&\xfds\xf8N\x97\xfb\x1e\xaa\xb9\x1d\x12C\xd6\x93m\xd3\xd3!\xc5g\xefN\xae\xdd3\xfb\xcd\x8av\x1dw\x1e\x0fs\x87\x0d\xb4\xdeb\n\x0d\xda\x0d[\x18\xbb\x1b\xf6\x9b\xd6\x8db\xfd\xd4L\x96\xc9;\xbc\xabx\xee\xb0\x15\xa4b\xb9\xafz\x9e\x01G\xc8\xc1\xb6#+Z\x17;\xba\x1a\xf9Le\x8cm\x03\x9b1\xf6\x9bw\xfc#\xd4\x0b\xe5\x96}S\x14IP\x17\xfa\xe0AA\xfd\x00\xdf)m\xcab\xd3ok\xd5\xb5\x14\xe0k\x88\xf9\xe6\x82@\x9dE\xe8@\x035\x7f\x91\xfe\x81\xfe\xb3\xfe;2\xe6\x16\xe0)\x11\xda\xb5\x1c\xb6H\xf4${\x17`g\xae(b\x82\xbd\x84\x10M\x89R\x95\xc0MDP>\x0en\xcf\xeeU\x0e|4_\x89\xbb\x11\x0e\xe3s\xe8\xf9mZF\x86\x0bt\x05\xced_\xcf\xda5\xd7,\xf82\x14\xc3P\x94\xb7@U9\x00\xd9>\xe75\xb3\x8c\x1dgP\x13{\x85\xdfGbuo%\xdc\xd4\xb0\xc3\xca\x1f\x93\xcc\xb0\x10\x1f\xcc&i\xcf\xa6\xd8\xfdl\x13\xb1 \xf3\x8a\xe7\xa0A\xf4\xee\x9f\xc8\xbf#\x91\xe4\xd2\x8d\xa10\xd1\xfd\x1e\xd2\xdeN4\x92\xa5\xa7\x11\xc0\xdf\x99\xddc4\xc9\xc0\xd8\"%qr\xfa\x8d\xa7 \x18[\xa8\x0cNn\x9f\xbe\xf27\x91\xea2\x13u\xf2x\xa0\xc5\x18+\"$\xa2\x1e\xae\x93\xb2_c\x05e\x96\xe4\xdc\xaf\x80f k\xd3Q\xb5\xa9\x88\xdaT4m2\x926 E\x9b\x8c\xa0MD\xcf\xa6#g\xd3Q\xb3\x89\x88\xd9\\\xb4l\xec\xdc\xf7\xef\x870J\x16\xf0\xb0\x08\xb9\xa0\xb6\xbd7t\xec\xbe\x91\xb1\xfb@\xc5>\x1a\"\xf6 h\xd8\x03!a\x1f\x15\x05\xfb4\x11\xb0O\x00\xfdzh\xe4k\xe8&9\xb1\xa8\x0b\xb9GK\xba\xb9\x05]\x02\x90\x1d\xa3\x90Ke\x17q\x91A|\xbc[P\x1e\x9e\xb1\xfb\xc4pK+W`h\xd6-\xce\xb59\xef\xa5\x95=}\xafZ\xd1\xac\x8b\xe68)\x9e\xc8\xb0j5\xe6\xea\x81\x90@\x98\x9b\xeb\x02\xf3w\xb7\x0dr-\x89_19\x94g\x81\xba&\xb3j\x9a\xf8\xa7J\xbbE\x127h\xd1\xce}\xe4\xb9\x98j\xf4\x8c+\xaa\xb1W\x8e\xb7T\xe4f&\xe8/S\xda\xf5xG\xfd?xG\x8d\xcb\xc1\xb9\x89\xf5\xdc\xd9\x0c\x96e=\xe0\xa5Y|=sK\xb0\x1eo\xcb\xc7\xdb\xf2\xf1\xb6\x8cl\xa8\xe3m\xf9x[>\xde\x96\x8f\xb7\xe5\xbf\xd8my\xd1\xb2\xa7SK\x9e\x06\xca\x9d\xce\xba\xaaD\x91\xa6\"\xaa\xc4\xa8e\xea\xde\x18\x89\x07\x07\x14\xd7\xc6\x869\xb5J\xfduJ\xe3\x1d\xcf\xaaO\x8a\xef\x96\x05\xeb\x92\xee\xe9\xfen^\xb1\xcd+<\xbe\x0b\xb5[\xbcF'\x1a\xef\xf3\x7f\xfc\x0e?mK\"\xc2\xe7\xbd\xd8\xa5\x9f\xb4\n\x01\xb2\x84\x87\x0c?\x1dC=-\x83\x15\x8e~0~\xfc_\x0d\xf3\xd7\xbfd\xb5:>\x14\xf4\xb0\x0c|\xbc\x18 \xf1\xd8\x12\x08p\x07\xb8\xcf\xd0\xaa\x90\x8dQ2\xa1i\x92\x8eq\x04\xe6\xdf\x0f\x17G\x10b\xe5\x9cm\x1d\\xC0\xce\xf3q\xf1\x9f\x8b\xda\x9bLv\xa3+,\xa1\xc9\xcfh\xc5\xf3QX\xb1f\xdf1\xfd\x11\xa8sK\x0d\xdf\xb5\x08!U\xd60Yd\x9c\xa9|\x11\x80\xb834\x95\xbe\x18\xaa^\xc4`\xb7\xa4\xd8l\xea\x1d\x8eG\x85 \xbcp\x02X\x05n\xaa\x10\x0f\x18\xefy?\xb1I\xa2\xef\x0c\xf4{\xf5u[\x19\x18\xb4\x14\x14\x12\x95\xa5}\x8e\x0e5\xfeAr\xf0\xac\xfaN\xcd\xcd\x01H\xba+\xcej2\x8f\x97Be\x10\xd5\xd8\xachQK\x1e?\x93\x8b \x80\xed\xc8.\xd6\n\xa8\xd8[70\x87\xb8\x8d(\xa5\xc8O\xe8io\xa4F\xa4\xf0O\xfc\xc1\xb1\x04\xd0\xd8\x92\x8b\x01\xe9\xaf\x84\x8d\"\xd3\x0b\x049\xa4,\xf4n\xa0TP\xa8\xfb\xc3\x17\x0d\nqs\x80\xf2A\xf1\xee\x1f\xad\x90\xd0\xd8\x06\xa7\xa4\x90l\xa2pWu]Cj\xd6\xe5\xb2:\x1e\x8d\xbd\x86\x99*\xf6\xd0\xd1\xd8k\xb6\xa3\xb1\xf7h\xec=\x1a{\x8f\xc6\xde\xa3\xb1w\x96\xb1\xf7\xd2\xd8R\xdaio+\xf2\x7fs^\xb5\xd8\xc6nK\xba\xdd\xcf\xbc\x08\x80\xd9N\x98t '\x81\xad\x0d\x08\xa0\xd1\xa9V\x06\x8d]\xe6N\x85\xe5O=\xeb?+t;\x1cA\xe2F\x12\xc0:\x1a5\xc7\xcc\xd7\xb4\xc3\x15\xffa\x92\x8d\xcf\xd2\x7f\x8cQ(\xca&\xd7\xc5j\xd5\xf5\x9a\xe1\xf1z;p\x9dW\xe3\xca\n\xe2\xb0\xca\xf5\x91P9VQ\x88U\xbd\x94Vx\xd55\x18\xc0P\x1ds\xd8\xa4\xea\x90Q\xf3\x16r\xdf\x0e\x11\x9al\xbaB\xccUh?\xa8Yj)S\x94i~\n\x8d3\xdf\x9a\xe4\xb1\x1b\xd9\xdf\xfaT\xfb\x90k\x0d\x12\x043lB\xba\x1d\xc8\xb3\x1b?\\\x9c\xd5\xf5\xefm\xc9\x14\xae^\x14AN\xde\x8f\"5\xcaU\xa7\xbf7\xbek'^\xf5%d\xf5\xcb#\xab\x038\xdc\xd7uq\xc3\xf7\xb6\xca\xda\xda\xca\x07\xa5\xfc\x15O7d\xd7nu\x9d\x0cL.'jz\xd2\xeb\x87\xcb5S \xf3\xf4\xda\xe2r\x9a{%\x19\x04\x0b'\xd6\x8c$U\x10\xdf{\xf1pQ7\xbc\x10\xf7b\xbd\x82<\x94\xc2\x16\xe5\xcfU\x91q^Y\xfc\xc69\x8fy\xfdp\x19;\x1a*!\xfe\xa1\xa9wP\xe0b-\xcb\xf6\xb7\x1d\xaf\xc5o\x12\xeco\xdbm\xbd\xe2&G\xb3\xda \xbc\x949+H\xf5\xf0\xf0-I\xb0\x06i\x94\xb6\xf7\\\xb1\x17\xbf\x81\xeaX4<}[/\xfd'0\x91\x16\x95m\xa3J\x8d;UK\xce9\xf5\x9a\x87\xfa\xcb b\xb4\x1b\xb2\xe5\xf1\xcew4{\xb6\xec.\xdc\xc9\xab\xab{;\x93\xec\xf2s\xc7;\x91\xa7\xa5\xaf\x06;T\xa8\xd7\xf7\xdb\xa0\xc2\x91-zl'\xb9\xd3\xb7&5]\x0f\xb2\x0e\xbd\xf0j\x89\xab2Xs`KCGl\xe6\xaew\x10R[l6\xfa\x9che\xde\xb1\x99\xc1RF\x87\xd2I\x87\xe7F\xeb\x8c@%\x1f\x9e>\xaa\xdbRb\xdd\x11\x8aA\x9b\x11\xfe\xa0Xd\x93\xa0\x90v\xf6\xf4\x14\xd0\x93\xbc\xb1\xd9\xab\xc0\xd5*\xad\x0e>\x93U\x9a\x14s>\xef\xcf\xe7\xbd\xb3\x02\xd6Px]#\xa6\x1b\xd1R9\x15\xc7\xaf\x88}('b\xcfW7M\xdb9\x90w\xf9\x15\xd9\xdd\xb8\xf5\x8e:\xfa@\xbb\x9e\xee\x7f\xb1DG\xf6BU\xe3..:\x8a\xefd\x8b\x12\xeb\x07\x92n\x90\xb6[\xd1\xce\xce\xd7I\xc8E\xd5\x94\xf45)y\xa9\xe2\x17\xfd\xea\x8e\xbc\x05\xf9~\"\x1e\x9c 2#n\xd8\x9f\xae\xce#\xc4\xe6\xe9\xf48wX?^U\x9fx\xd4}\x92:\x8b\x88\xeaJ\x92\xe6q\x19\xf5\x9fD\xae\x00d\xe9k\x00\xf1^\x05\x08z\x1d \xf8\x95\x80\xec}~\xa7_\x11\x10bBEF\xaf d\xd6U\x01!f]\x1eH\xe8\x02A\x822\x96$\xd4\xa5\x89\xcf\xe4\x8c\x0b\x85\xffS\xf4\\*\xc8\xdc\x8b\x05B\xafj\xd0\xcb\x05\x99q\xc1\xc0z\x81+\x87\xf7\x92A\xd0\x8b\x06\xf1]6\xc8\x01\x16v\xb9\xcb\x07I\xba\x80\x90\xf8%\x84 6M\xd9\xa6_F,B\xe3\xd5\xc4\xfa\x03v?\xc93\xdc)\xf7)\xdb\xb9\xc6uw4\xe7J\xfc\xb6\xb4\xf2O\xd2\xba\xe4\xf1\x9aii\xc9\xb4\xdd\xa9C\\\xc8Qu\x9e\x93M\xd1\x0b\xa7\xa3\xb6,'\xf0w\x8b\x08?\xe4\xa3'\xfc\xf9\x88\x1a\x16Rtm\xd1q\xcbL8j+!^#K\x9e\xf9\xc9\xb7\x07\x95\xa8\xf0\x9d(\xa3\\r\xf8\xd7'J\x13<\xf6&\xe4 \n\xe9\xf0\x9c\x973\x1eqU\x0d(\x98+\xd2\xb2y\xf8Z \x8fa\xd2\xf59\xdd\xbb4\xe3\x02-\xde\xfa\xc8t\xdd>\xf9\xea|_|c\x97\xcc\xab\x9a67\x83\xe3=\xb1\xeb\xe8\xc8\x15\x1b\x17\xcc\x18?\xf4m\xb85\x11\xd5[\xb2\xfdB\xdeS\xef\xdb\xd5\xb6\xa6b\x8c}\xfa \xffO90|\x1b\x0d\x9b\x91$K\x8d\xf1\xa2 \x97\xe4\xd2t\xfa\xcc\xb4\xd6l\xb4\x1d\xea\x8cl\x13\xdcBB%\x81\x0d3In\xfb\xf6;\xf1\xeey\xe2\xa9\x1au\xb4\x03\xcc\xdc\xed\x169\xc7\x0e\x90\xb8\xe3s\x84\xeb\xfc=\x9f+s,\xa3!r\x16\xfeu\xa4\x92=I9b({\x192\x85\x8e8k\xec\xd9M\xfa\xb2\x10\xc3\xafW\xa7\x99n\xf4\xf5\x9a|c}E\xcc\xbd!co\x8ct\x9a\xa1\xd7\xbf\xf5\x166\xf2N7\xf1\xfeo6\xf0\xa6o\xbf\xbf\xf6\x11\xe0|\xfcY2?S\xc0\xf4\xff\xb6\xe3\x89\xb6r\xe5=\xa4\xbf\x8a\xe0\xa0\xfe:\"\xdf=\x96\xad\x89\x0b\x9d\x01\xe2Uk\xa1$\x81\xc9\x0b6\xedd8:\x0f\x93N\x93\xa3\xf3\xf0/\xe5\xe2[!rt'\x1c\xdd Gw\x82\xd9\xe2\xf3;]\xcdD\x88\x1d\xdd \xd3\x94R\xff\xa7xt'\xa8vt'\x1c\xdd \xd024\xb4\xa3;\xe1\xe8N \x81+X\x86\xff \xef\x12\x16\xcc\x0eap\x83\xe4r\xe8\xa9\xacq\xca\xfe\xb2\xb6\xd29hQ\xf9\xfe\xce\x87]\xf2\xad/\xc50=z)\xf0\x8a\xed\xcf\xa4\xd5^\xe5)ok=\x15\xba\xccz\xce~\x16\x03b*\x9dx\x9b\xdd\xfa\x84\xed\xaal\x9b\x81m\x7fA\x7f\xd3\xb5%/Fo\x96KD9\xb4+\xf6$T\xea\x89W\xe8\x89U\xe6\x89U\xe4\x89V\xe2 V\xe0\x89V\xde\x89T\xdc\x89W\xda\x89W\xd8\x89T\xd6I\xad\xa8\xe3\x97\xa0s*\xe8\xe0\xf5r\x16\xaf\x93\xb3\xaf\xfa8K\xd6\xc59x=\x9c\xbd\xd6\xc1\xd9s\xfd\x9bG\xa9{\xf3\xb4\xea\xdd\x84\x7f\x0c\x0e\x03\xfe!\xd6UsG\xae\x8b\xf2\x8e\xfb\xb3\xd9U\x83\x1d\xca\xdc\xea\xc0\xe7\xca\xf4\xbe#Ug\xa3\\\x04\x00T\xbe\x0f*\xb7\x0e-4\xf7\xbb\n\xf2\xe5\xdbD$\xf0\xb9E\x8b\xd5:\xb4PN\x07\xb7H\xf0\xf8\x870\xbb\xeb\xae\xbd\xe7\xfb\x83\x97\xc6]\x17\xfdvu\xfe\xfe\xe3\xe7\xcb\xab\x8b\xcb\xb3\xcb\xcf\x17\x1e\xe7^\xe8\x8d\x8f\x9f>|\xfcp\x91\xfc8\xfc\xe6|f\xc2:\x98\xc7R\x0cp\x12\x1d\xa2;+\x91\x174#\x16/e\xccM\x12\xa7\xdb\x86\xab-\x0e1Xq\xb62\xce\x9f\"\x93\x89\xcf\x84\xfc\xabmJ\xd3vVC\x8a\xee\xba\x1a\xba\xa2\xdb\x8d\xdf\x1awu+\xa1\x0b\x9b(\x95#\xf8\x0d\xe7\x07~\xc3\xb9\xa9`\x9b\x1b'\xc7\xa6\xa3\x0fU\xbb\xed\xeb\x9d\xfb\x01\x8c\x06,\x8d3\xf1y]vEy\x07j2\x9c\x11J \xa2R\xa2\xe1j\x0c\xbc\xae\xbf\xe3\x1c\xc1\x8c\xb1\xf2\xb6\xa2\x0f`&\x82J\xf4\x8c!\x97\xa0\xa8R\xbf\xdf\x03j1\x11\xfc\x1fB\xfc\xf2\x13\x89m\x0014\xb5\x0f\xe0\xdf\xa7\xa3&(\x15.\x87\x945\xadd\x828\xbc\xe0\xcf[X\x04M]u\xb5\xa4Jr\x88|V|\xbfU\xcd\x0d\xe9\xb7%#\xf4|]T\xf5\xb6\xa3\xcf\x99\x04\xdd\x00&,}\x0e\xfd\xf2\xf1\xe2\xf3\xef b\xc8}\xf6\xe3\xd9\xc5E\xf4\xa1\x8b\xff<\xff\x18}\xe8\xdd\xd9\xf9\xef~a\x99\xc6_\x8a\x98\xf4P\nt2~\xf6\x0e\xc1\xa2!\xdb\xa6\xa7\x03\x0e)\xc0;eSf\xf7\xc6~3\xa4\x0b\xd5v\x0cG-\xc1\xf2\xaf\xb7u\xbc\x036\xddv\x07\xec7\xad\x03\xc5.SU\xfbj%\xd5[\xde\xd5]\xb5\xd9\xd0\x15Y\x01\xa6\xf6\xbe\xea9\x94Y\xc8\x96\xb6#+Z\x17;\xba\x1a9\x8c\xb3\xc4\x16\xd7f\x89\xfd\xe6\x1d\xb3\xf8|||\xb2\xaf\x80\xaeN\\\x01\n*\xd4\x07\x10\x00\xb4)\x8bM\xbf\xad\x15})\xf3\xd6\\D\xc3\x97\xa9\xe4\xb3\xe5 \xc6\xbfq\xa0\xf0\xac\xffN\xb3fppZ\xbb\x96\xdc\x0b\xc8\xbd\xa4\xdf\x02F \xd4\x1a\xf7\x82''@\xa0?mI,\xaf |\xd8\x13\x0d\x02\xfe\xaf\xc2\xa6.\x075\x9a\x0b\x84vm|\xfe\xff\xb7\xf4\x9bd\xc1\xeaZ\x9b0\xc4_\xa2\x9fK\xa3\x97\xf4\xa1\xe8\xd8-\x92\x7fy\xa4\xa37E\xc7c@\x94\xd1\xab\x8a\xfbG\xce\x99\xda\x96|\xd2%\x88\xca%\x0d\x85\xb3\x0e\xd6\xa5\x0c\x82K\xe0 \xa2\xb6>\xbbS\xc4\xaa\x17\xea9\xd7\x92g_Z1Uv\x9e\xdd\x0e\x14Z\x8d\x1cj\xb1C\xc7\xe4b\xee\xd3\x8da\xc9\x96\xb9$\x9b\\\xb65\xce\x7fZF\x07\x91\xde \xda\xde<\xb6\xb6\xa7de{R\xf6\xb5\xc9\x96\xb5\xb8X\xbd\xd0\xbe2g\xa7\x9bh\xda\xb4\xb5O\xda\xdd\xd1\x9d\x9d\xb5\xab\xf1\x1d\x1de8\x8dx\xf6N~*\xbb\xf8)\xee\xe03\x19\xe2\xc5&\x0c\xb0\xa9\xd2&\xd3\x8c\xc7\x9e\xbaO\xc7v0\xd8H\x925\x83\xf4Sr\xba9\xd62}$\x9ca3\x8c\xad\x9aqU#\x98bfM>\xd4\xe2F\xcb\x8895bH\x0d\x9aP3\x8c\xa7\xe1Sm\x8e\xc1\x94\x18\x08\xa5\x80\xa9t\x8fF\xd2'e\x1e=\xa4atQ\x93hP\x96\\lh9\xcd\xa6\xc6n\x03\x13\xf5\x7f\xcc<\"\xc8\xc5\xack\xa69\x13\xb7\xb5 R\x0b\x1b\xdcL\xaaIV7\xf1\x8a\x87=i\xf2\x84\x1f\x16\xb1\xc3\x19\x12 \xda\x9d\x1c\x03\xf2\x97q<\x1a\xc1\xe4\x91-i\xbd\x0b\x0d)\xcb\x9e\xe7%\x83\x0db\xa2\x8dO\xbd\x9bd\xda\xc0\xd90W#\xc3\xea\x87\xbe\xe8\xb7\xfe)\xa4\x13{\x9cT+c~,{\xa0\x87\xd5,S\xa0\xcd^\xa2\xe9&\xcd\xb6\xe5x\x0c\xed\xde\xbcr\xc5\x98\x06%Y,j\x88)\xd1\x99\xed=\x98\x14\xa3\xa3J\x11F\x11\x12\xc11\x85D\x149\xc4\xf8r\x84\x96E\xcb\x12\xc9\xe9\xe3\x9ej\xd8\x8c\x8c&U,L^\xaf\x8c\xe5\xc9\xe1;n(\xcd_D\xc4\x8c\xaa\xb8\xb0\xf5\x92t\x9b*\xf6\x05'\xd8Y/\xf4\xd7\x92\xb5\xa0~\x01\xf7\xaeP\xf3\x04\xf0\x1c\x83\xaa\x1b\xbd\xd8~\xee\x84\xf3U\xf4\xc0\x8d\xbd\xedZ\xcc\xad\xb8\xcf\x89\xbc2\xe0\xf7\x15)\x9a\x90N+\xc3Y\xfc\x04\xf1\xafb\x8c\xfc\xc8W\x13\xe7\xc2\x0f\x11\xbbm*\xe9b|\x99\xc3\xa1\xab\xb2\xa8\xeb\x1d\xa8\xddC\xab0\xc8\x1cA|eC\x9e\x8fp\xda\xa8\x89\xdd\xa1\x85p\x8aw\x0c\xc6y-\xa2M\xc4\xfcU\xd75\xbd\x12\xf1\xe8\x93N\x7f\xe74q\x11\xad\x91\xcc%\xc8Ch\x0e/\x12\xccb\x82<\x81\xe4\xf1\"\xe1\x8c&\xc8#v./\x12\xcen\x82Qp\xf2y\x91H\xa6\x13\xe4\x194\xa7\x17 e=\x81\x96\x92\xfbD>\xe9\xbf;\xcc\xc9\x83b\x10\xf2\x80r\x17\xcd\x8c\x82\x90\\(?\nByF\x96\x14\x84\xda\xbes\xa5 ].\x991\x05!\xbfl\xde\x14\x8c\xff\x03dOA\xba}\xd4\x1c*\x08?\x87\xcb\xa4\x82t\xbe\xa7|*\xd0\x84\xea8?\xab\x8aA\xee\xd2\xd8\x0d\xdaa\x88\xba>m\xf5\x15S\xd8\x8d\xcc\x9f\xc6_\xf8w(\xef\xe8\x00\xca\x1a\x8f\xcc\xa2\xae\xdb\xaftu\xda\xd1/\xdb\xaa\x93p\x9d\xfeT\x04\xfcd\x199r\x93id\xda:\x02\xc0T\x1b\xb5^\xb2\xf5\x86;\x17\xd7_\xb6\xdbj\xf5\x9c\xd0\x93\x9b\x13\xf3l\xfa\xf9\xba|Y\xbc(\x7f]\xad^\xbc\xfa\xe5\x1f\xaf^\xfc\xfa\xea\x97\xf5\x8b\x9f~x\xf93\xfd\xf9\xe5\xcf/\x8b_^\xc1\xd7 \xecj\x1a\x98\x1c\xedY\xbf\xbb\x89+4t\xaa\xded\x8f\x7f\xff\xe5\xe5\xcb\x97\xab\x97_~\xa0\xbf|\xfd\xa9/v?\xfdX\xac\xbf\xach\xffm\xf3\xc3\x9f_\xfe\xbc\xeb^\xad\x1b#}\xf69\xa4\xa7/\xea\xbe\x95\xda\xa0\xdfe%\x06\xa9\xbd\xde\xd1\x12z\xbd\xf5\xf6\xfa\xf5\xd5\x0f\xab/?\xfck\xf5p\xbf*\xfe\xdc~\xfd\xb3,V\xab\xdb\xdb_n\xee\xb7_n\xe9\x9f\xaf^\xc9IK\xb8\xf4\xf9\xd7H\xf7\xa64\xc2n\xa49\xe0\x86\x96\xd4m{G\xb6\x1b{\xf0\"\xe5\x04l\xce\xb5\xbb\xbf\x85\x15\x1e\x16\x99@\xe2\xb3\xe02\x8dfw\xa7#\x99W<\xd8O\xc0,H\\\xa3>\xfd\x06I6\x1c\x8f\x0b\xcc\xdf\xfc\x8c1V\x07\xc4\x9b\xc5C<\x08\xc7\xe6\n\xcc\x9d\x1cFh{\xb1\xccL\xd9\x7f\x9d\x947!\xf9\x99\x90\x03G\xbc\x0cyI0\x11\x9c\x91\xa4\xc4\xea;3=\x8e\xe66\xb3,\xcc(\x01\x9c\x08\xb1\xbf\x0b\xcc\xc8\x9fU\x1e\xc5/\xcfe\x0bX\x00#\xb5Rf\xda\xb4e\xc3\x0c\xb5\x11\xb6\xfc\xa6I2\xd3\x00\x8e\x10\xdc\xbafK\xdb4.\x1bvIV\xef\xd8\x97e\xf3%dU\xa1\xf9\xd7\x16\x1a>}\xd0\x82\x93(\x1eI3\xa9\xab\xc7}\xa6u\xfd\x81\xb4.g\x9a\xdae\xf3\x9a\xdceK\xe0(\xb8\xcd\xa1\x85\xb6\x9cx\"\xd7J\xef\xa5\x84Z\xefe\xc3\xac\xf8\xb2%\x0c5m\x1cy\xd6}/)\x1f\xb0^oa\x86\xa6Z\xfd=[E\x9a\xcc&[\xffes\xbd\x00\xb2\x85\xc63\xd7+ [\x9ew@\xb6\xc0\xb7;Y\xda\xce\xf2\x1e \xf4\xa4?\x01\xf1\"\xc8\x86z\x13d\x0b\xf1:\xc7\xbb\x80\x90k;\xdc\xcb [pN\xf1 \xfft\xef\x83\xffyo\x02\x94$oD\xecQ\xd3+![\x96wb|)\x84L \xb1\xc9\xc8\xeb\xcc\x02\xb9G\xbd\x16~\x16\x1e\xc7{\xe1\xe7\xe7@^\x8c\x18\x03\x8f\xea\xcd\x90\x0d\xf3j\xc8\x16\xf4n(\x02\xd3\x158\xef\xa9\xeb\xfb\xd4\x13\xbd \xe8\xc3\x1eo\x08\xfa,\xea\x15A\x9fD\xbd#\xe8\x93\xb8\x97\x04}\xd4\xf5\x96\xa0\x8f\xe1^\x13\xf4Q\xcc{\x82>\xe8\xf1\xa2\xa0\xcfz\xbc)\xe8\xb3\x98WE\xb6t\xef\xca\xf8FL\x0e.\xe8m\x91-\x90\n\x85\xec\xc7\xfb\x82\x92^\xd4\x0b\x83\xf60\xdb\x1b\x83R=\x8cW\x06\xedzy\xef\x0c\xda\xcd>\xbc4hG\x87\xf3\xd6\xa0\xdd?\x01\xaf\x0d\xca\xd7\xa1\xbd7(\x13{\xf5\xe2\xc8\x16\xbe\x00\xcep\xf2\xa0\xf4\xcc,\xb9\xd0&9\x80\xac\x97\x1d\xf6\xa7;\x84,B\xca=\x94\xe0\x18\x1a\x9b\x99\x07\xc1\x86y2\x06\xa4\x9e\x89\xdf\x7fD\xf9H\xdd9c\x85d\x98\xa6s\xa7OgB\x90\xd7G\xc0/\x1e\xb8P\xadNG\x8b\x8aEn\xf0D\n\x9d\x98\xab\x93o25\xf9\xcc\xbc+\x07-I\xf1\x93\xde\xec\x9b\xe4DH!\xd4\x90X\x06\xb7\x134\x9a\x81\x1ct\x9c\x13b\x1b\\\"\x91Q\xc6\x00\xf4\xe4\xb0#\x9e\x11\xed@\xdc\x88\x87\xac\x99@C\xb6d;\xe0\x14L\x0d\xe9\xc2\xc9\x85\x06\xed\x86y\x90\xd8P\xf3F31\xe0\xc3&\xe3\x8e\xc2 \xb0p\x85.I\xe2\x16%D\xd2\"\xc6\x10rX&\xa9\xc8P\"\x8e\x8e\xb0\x9b\xc3f\x1f\xbfOG\xcd\xdfQ;\x7f|\"\xc9\x94\x883\x0f\x9d\x1ew\x81\xbb\x0f:\xab\xe7\x93\xd9\xe4\x11ga\xa2\x14\xc7H%\xcdA\x8aD'\x8f9\x1f^\x19\x9fD\x00K\xd1\x95?O\xb8\xe8#)\xd32e\xd43\x04\xa1K,e|3\x96}\xca\xf8\xb2#\xe4p2\x81H9\x12\xe4\x0c\xb9T\xfc\xb3+6\x1b\xda\x8d\xaatA\xfa\xaa\xb9\xa9}q\xf4\x8d\xaeO\xf4\xed=%\xf4\xdb\xd0\x15\x9a\xff^\xa4-\x19\xc6h\xe9\xa7U\x94\xa7\x0fb\x1a\xbc\xeb\x1e^o\x9bh\x1a^\x8d,\x84Ys8\xb0\xbd\x8e\xcba\xd7\xc8|\xfc\x1aY\x16\xc3F\xf2\n\x9c\x84\xd7q\x1a\xa6\x8d,\x87k#I\xd86\xb2(\xbe\x8d\xe0\x97=/\xce\x8d\xa8\xf9]\xb6z\xd5l\xdc\x9bEo\xd0*Q\xe9\x03\xfbk\x97\xe5\nc\xe2\x12\xeat\x89\xd7\x97G\xc5\x89\xb3\x06\xe6\xdb\x91\xd8\x114\xf1\xb8J\xfb\x97\xf5^\x95n\x01\xccZ\x86\xa8\xf2\xd9\x06\x17\xc5\xa8Y\xf84\x0c\x9b\xe6sk\xa2.\xcd\xc05-tI\xf3\x81\xc2\"zX\x06\xfel\x08a\xcf\xd2\xbaY\x00s\x16\xc4\x9bE\xb8\x88\xdc4\x96\x0d\xf7&\x01\xb0X\xdaly\x94\xe2!\x1b \xe6\x0f\xff&~\xb8\x9b\x9f\x89\xc5@a\x0b\x01\xc2p0\x98\x8f\xff%@`\xf9\x000\xcf\xc7\x93-\xba\x96\x04|E\xc0^^\xa0\x97\x8f\xb79\x00/\xe2\xf8}\xbd\xe0.\xef\x9caH\x8f\x1c@W2\x98+\x11\xc8\x95\x0e\xe2\xca\x06p\x85\xd5\xa5\xc5\x80[\xf9\xa0\xad\xa7\x04\xd8zT\xb0\xd6\x93\x05j\x0d\x1e\x90V\x14\xa05A\x8bAO8_\x81\xa5D\xc4N*\x10+\x0d\x84\x95\x06\xc0J\x04_%\x00\xaf\x12AWI\x80\xabT\xb0U*\xd0* d\x95\x07\xb0\x8a\x19\xf9\x16\x05V\x05\xebK\xa1\x9bf.\xa0j\xbf`\xaa\xe5\x81T\x8f\x04\xa2:\x00\x80\xea \xe0\xa9G\x04N=E\xd0\xd4\xa3\x03\xa6\x0e\x0b\x96Z8\xec]#\x99\x8f|\xc2T\xf0\xe9\x88''\x04\x9e$\x85\xc1C[\x18\xe9\x14D9\xf9\x8f\x93\xb9\xe8&\x82\xe5\xad\xf5!\x9b2\x0ch!4S\xbe\xf9,\x07\xe1\xe5\xf5\x8aO3f'\xa0\x96f\x8e'\x1f\x94\x14\x80\x8b\xb2\x1e\x03\xdb\xf9\xbd\x98\xee_,JSG\xec\x85\x19\xedy\xa8s`Ei\x83[,k\xec\x02\x10\"?\xc7\x8e\xc79\x17\xaf\x89\x12 i\x90!\xe2l\xa3<\xb8\x90\xd7\x06\xed\xb7@\xdb\xec\xba7\xbc\xa0\xd1s&\xb6\xcd\xee\xdd+\xfc<\x1eC\x92\n\x07rV\xe51\xe0\x9b\x1e&\x82\xb22\x81Lt\xac1)J\x0e=\xee\x19r\x95\xe4%\xaev\xbb\xdf\x0b\xaa\xd1\xedb\x82Pr \xc5\xc62a)s\xc72 \xba\x93\xb0\xe0\x99\x0e\xdc|\xb8\x0e\x07\xe9\x08\x82!\xa8N\xba\x1b\xb7?\xab\xeb\xdc\xc4P\xc7\x9c9\xb1\x01\xcc\xc5\x08\x84r\xe6\x10\xb2)\xb8\x11\xc8\xf0\xa3\x1b\xbc\x8c\x0f\xa8KW\xcb\xffT\xd4\xfa\xdf\x94\xaf\xaa\x97h\x01\xc9\xc2\xa4\xeb\xc5\x1d\xdd%\xaaBi\xda\xdd\xff\xd8\x1f\xf2\x1d\x05#\x8c0Cwt\xd8v\x0d(\x11\x1f\x8b\x1b*\xf1\x10'\x0d\xfd6\\\xb1\x87\x87\x96\\\xd3\x1b\xe7\xf2\xfceK\xbb\x9d,\xfb\xcd\x1ef\x93B\xc9}\xdb\x0f\x84\xae\xd7UY\xd1f\xa8w'\xe4CS\xefH\xdb\xf0\xfbp\xbb^\x83\x05\x8e\xb1a\x11\xeco\xdbm\xbd\xe2>;:\xe8\xd2\x0d^\xca\x9c\x95m\xd5\x0c?\xbf\xf2\xcf\x8b#\xe0\x04k|j\x9a\xed=\xb7\xbb\x89\xdf\xc0\xc2S4\x8c7n\x84\xe1P\x02\x98H\x8b\xca\xb6)\x1e\x8a\xaa.\xaek\xea\x81'\xd5\xbcV\x97\x9c F\xbb![^L\xe9\x8ef\xcf\x96\xdd\x85;yuu_\xed}\xeex'\xf2,\x18\xda\xa1\xa8\xd9\x14^\x83\x89O\xd6\x8a\xe7\xfb\xc8\xd8o\xf0\xad\xb0\xbfZ\xf46\xdc|`O\xdf\x9a\xd4t=\x10z\xbf\x19v\xa4\x12\x00\x0fa\xbd\x06\xe7 li\xe8\x88\xcd\xdc\xf5\x0ej\xa8\x17\x9b\x8d\xa9\xc9o\x9b\xe1\x8a\xf3\x89\xcd\xcc\xb2H'\xad36C|?\xb5d\xe8\xb6\x94X\xc6\xbcb\xd0f\x84?(\x16\xd9$(\xa4\x9d\x03h\x84\x9e\xa4\xeac\xaf\x02\xf7\xe9\x10\xb59\xb9\xac\xd2\xa4\x98\xf3y\x7f>w\xf1u\xd6P\xda\xa6\x06\xdb\x16-\x15\xbef\xfc\x8a\xd8\x87rbB\xf2,r\xf2+\xb2\xbb\x81Y2/\x8bL=B\xaf\xbd\xcb.\x96\xe8\xc8^\xa8j\xdc\xc5EG\xf1\x9dlQb\xfd@\x05=\xd2v+\xda\x9d\xfc\xcd\x1e\xe6E\xd5\x94\xf45)\xdb\xfe\xbe\xed_\xf4\xab;\xf2\xf2\xe4\xd5\x8f\xea!a\x104\xe47\x08\xe81\x89\x1b\xe7\x83\xde_\xd3\xd5\n\xf8\xb8\xf9\xf4\xf1\x8d:\x01\x85\x1d\x0e\xce%%q4r\xe3\xfa\x9f\x90\xb7\xe2\xc6\x93\xacwi:\x0f\x99\x91P\x8e\x91\x99\x0c\x9f\x03\x1e\x16H,7-/wrU\x8e\x1e\x87\xed\x05i\x85\xe8\x91(\x90\x0fZP\xdfOEO\xcd\x81\xf6A\x9b\x071\xf3\x80\xa6f@\xfePzf\xad\x16\xedwOj:\xbf\xfb\\\xbc\xe7\xcbn\x12\\qh\xb1\x14u\xe1$u\xd1\x89\x15\x0f\xa5\x03\x05\xc5\x0b\xb1Tuy\x1d/\x00\x1d\x84\x16\x04\x10BK\xe2+\x1a\xb6D\xa2\x9bR<\x93\x8b7\x0c\xd0\n\xa6\xae\x0b'\xafK\x1cv\xea\x88\xf2\x10\x8a\x01b)I\xec\xe2l-\x86Y\x84\xb6\x10r\x11\x9a?\x99]l\\K`\x19\xa1\xe5#\x1a\xa1\x05\xbf\xf2Y\x12{\x06\xd2\x11\xa5W\xc4\xf0\x8e\xd0\x82\xe9\xedb<\xcfA@\xa2\x04cI\xee\xa2s\x1c\xca~\x95\x8e\x03\xf4\xbd\x11Hv\x97\x8c\x92\x0c?\x8c'\xbc\x9b\x80\x98\x94\xaf\xc5\xac\x96\x0b\xa2'\xa1\xe5c(\xa1=%$\xa5\x8f\xa3\x03\xe2)\xc3,<:\xaa\x12\xda\x10H\x80\x97\x80\xb0\x14D\xe6)\x8a\x81\xf3\xdb/\x0c2\xf0\x97\xc8\xe3\x91tx\xa9XL\xe4\xd9`J\xbcd\\&\xf2\xb0?-^2F\x13y8\x94\x1a/\x1d\xaf\x89<\x1dI\x8f\x97\x88\xdd\x84\x96\x87\xe0\x94\xef\xc4\xe5\xe6T4\xa7\x87\x18\xc7x\x860\x9d\xd0\xf6\x84\xecD\x88/\x8e\xefD\xfaX\x04\xe5\x89\xd0=\x1c\xd6\x13\xe9|?\x88O\xa4\xa3}\xe1>\x91\xae\x0e\x8b\xfeD\x18x\"\x18P\x84\xb3\xc7@\x82\"l\xec\x1d\x0f\n-v\xf1\x9c\x08\x17\xf5P\xc3@\xa4\xd0&CI\x8d\xd7\x91aL\x87\x95:\xa4\xa6\xa5\xd2[\x1cb\n-\x084E\xfaE\xa6f.\xe8\xd4!8$%\xd5\x9bl\x04\x0e\x81Q\xa1\xcd3\x01\xc7\xe6\x8b,\nR\x85\xe6M\xdb\x94\x00X\x85v\xe81ODk\x85\xc0\x86h7)\x10\x9fG\x18\xfdL\xccV\x06\"\x16\xed>\x98~\xef\x11\xa6c\x0e^\x16#\x18\x9e\x00_>\xaa\xc8\xb0s\xc75\x03\xb8\x16\x1fO\x02\xb2\x16Z\n\xd7sP\xb6(\xc1I\xa9\xf9\xa2N\x9e\x98\x8b'\x8e\xbe\x85\x165\x11D\xb78I\x9cX\xb2\x10*\x17Z\x9f\x82\xcd\x85\x96\x8c\xd0\x85\xf6\xb832\xf9,\xc0\x88%\xceG\xda\xb9@\x1e\x7fn\xbc'E\"\x89\xa9)\xfc\xd2\x90\xbe\xd0\x12\xa6h\xda\xf8g\x89P\x97\\\xdaHgn\x86i#\x9d\x84\x0c\xc6\x08E\xd2\xfa\x85\xf9\x9b\x8d\x156\xa8I\xdcp4\xb9\x9f\xc5\x14\x8a6Q7\xc7\xaf\x9c\xa1\x15\xda\xbd\x92\x86O-]\xe01\x0b\xdaR\x03\x9b\x8bp\xb6\xc89Y\xd0\xa0aHg\xb2G\xb43 i?~\xbd\xc7A>\x93\x98\x8c\x9a\x19|2\x1d \x8d\x10\x9b\x07\x87\xc6\xb9\xc3\xfa\xf1\xa2\xa4\x89\x07)MRg\x11A\xfd\x92\xa4y\\\x069M\"\xe8i\xb24\x82\x9axQ\xd4\x04ER\x13\x1cMM\xf6>\xbf\xd3\xd1\xd5\x081\x01d@\x11\xd6d\x16\xca\x1a!f\xe1\xaeI\x08{M\x822\x96D\xe4,I\x9a\xc9\x19Xl\xff\xa7\xe8\xc1c\x93\xb9\x98l\x84^\xd5\xa0\xb8l2\x03\x9b\x8d\xf5\"\x12\xa8\xfa\xf0\xd9\x04\xc5h\x13\x1fN\x9b\x1c`a\x97\xc3m\x93$\xec6\x89\xe3\xb7I\xc0\x06?\x1d\xc7m\x11\x1aQ\xdd\xd6\x1f0hw^\xcc\x93r\x17\xf2k\xac\x1e)0\x9aQ\xf8\xbe\xe6\x1a\x89\xadLe\xa8\x93\xf2x\xc5\xd4\xad\xe5\xc2\x9e\xd4!.\xe4\xa8:\xcf\xc9\xa6\xe8\x85SM[\x96\x13\xf8\xbbE\x84\x1f\xf2\xd1\x13\xfe|\x04\"\x0b)\x8a\x94\xda\xe9\xc0}\xd4\xb4\xe4\xbe\xed\x10}\x9c\x10o|J^\xe4\x8eo\x0f*Q\xe1;QF\xb9\xe4\xf0\xafO\x94&x\xecM\xf8\xb5\xe0\x9f\xe3sR\x0d\xbd\x86Yj@\xc1\\\x91\x96\xcd\xc3\xd7Jx\xbf\xf0\xaf<\x82\xfa's\xd2\xf6\xce\x8b=x\xd7vo\xc4\xe5\x14\xab\xb2\x0f+\xe7|\x07\x91\x9c\xbe\x86\xa5\x0f]n\xbf0\xb4 \xa5e\\_\"\xdb\xbaym\xd6\xfe\xb0\\\x96\xf5\x99\x19\xd6\x17\xcc\xae~\x0c\xdc\x8d\x0d`\xee\xb56\x14\xb8\x9b.%\"\xdf'A\x82\x96\xfe\xa6F\xcc\xa70*?|]\xecC\xa8\x1c\xa3\x9c\x8eQN\xc7('\xbd%\xd8\xaf\x8fQNNK\xf0\xef\xc46\xa5x\xe6\x18\xe5t\x8cr:F9\xd9\xed\x18\xe5t\x8cr:F9\x1d\xa3\x9c\x92X8F9\xc5E\xc11\xca\xc9\x8e\xb3Hy\xf0\x18\xe5D\x12\xe5\xe61\xca\xe9\x18\xe5\x84v~\x8cr:F9\x1d\xa3\x9c\x8eQN\xc7(\xa7c\x94\x139F9MF\xb6\xc7b%\xa2\xe1DOb\xf4~\x88\xb3\xfb\x17\x94\xde1\xca)D\xf0\x18\xe5\xa4Z\n\xd7(1r\x8cr\xdaK\xdc\x8aW\xdaG\xb4\x9ec\x94S\x12\xb1\xc4\xf9H;\x17\xc8\xe3\xcf\x8d\xf7\xa4H$q\x8cr\xca\x1d\xe9\xcc\xcd0m\xa4\xc7('\x0fSx\xc8\x13\xbf\xb7\xee4\x9ca0\xf6I#\x87\xfa\xce\x8c s\xee\x1b\xf8_\xadm\x82n\x0c\xff\xa8\x024\x8d] 1\xb1\xc8\xd9\x90\xc3\xab\xf9\xf1.\xc3\xab\xfem\x1a\x1a\x81sD\x91\xa27\x8aK\x8b/\x996e\xbb\xa2+\xc1\x88\x1c\xc1S\x8bO\xeb\x83\x98!\xaf<\x08K\x01\x9bh\x1a\xb8\x92,\x04\xb0t8p\xb2\xbf/\x06\xb4$\xf3\xc1\x96dY\xc0%9\xc6\x1c.8\xb0\xb9\xe0L\x8b\x9c\x13s\xb8\x04@3\x80\xed\x16\xf4\x0e\n\xd1\x14\x07*\xac\x8d#\x88|\xd8K[z'\xc9.\xf7~\xef\x95W>\xd3'#q\x8a\xe1\x18\x81\xad\x13B\xfe0<\xda\x02\xf9\xc8\xceV4\"\xa6/\xdb\x0d=!\xe4\x8cI\x03\x0e\x03\xe0\xa7\x03y\xc7N\x0f\xcba\xdc\xd3\xbe\xcf\x12\xba^=\xdc7:y\xc2R\xd9\x17\x17v\xf4\x9b8G\xbe\x16\xa3\xf7@`\x80\x0c\xac\x8dk\x84\xe6:\xc5\xba\xaa\x07\xda\xd1\x15\xb9{\x907\x9b\x81\x9dGm\xa7\x1b\xed7][\xd2\xde\xba\xca\xfa\x18\x15\x0fK-\xcc\xb8\x98K\x0ea\xea\xeb\x9dT\xb4v\xdc\xb4\xff\x8d\x96[G3#6R\xcb=7q\x1b\xb9\xf7\x86\xee\xbf\x9b\x0b\xc1\xeej\xd4A]ZL\xc4`\xc3\x02-\x8f\x0c\x1f\xbb\xda\x93b\x96,b8\xde/\xb5w\x1c\xc2'\x96\x03A\xbf\xe26\xb5\x94\xde\xd0\xbb\x02\xe0\xf2\x10\xb5\x95*&\x9e\x0d#f\x8f\xa3\xbb\x04\x0c\x14\xa1f\xcd^\xd9\xde\xdf\xb7\x0d\x8a\x01\x03a\xb6\xe0@\x80 \x1c\x10\x12\xa7j!N\xa4B@\xd8V\x92\xf00\xfe\x1a\xe6\xec\x12\"F \xdaN\xd5\xa0\xd4\x97[5\x0f\xed\x9d\xb1>\x18.\xd9\x074A!&\x13]\x0e\x13\xed\xac\xc6\xe1\xc7\xb1\xdb\x02\xc4\x04g)\xff\xe8\xeb\xaa\xb9#\xd7Ey\xc7\xc3\x14o)x\xa58X\x80\xcf\xa7\xd7\x12\xba\x0fC\xb8\xef\xa3\xb5PM\x0e\x96\xd7\x8fj\xf5au#|\x86\xfd\x91\xfe\xcf\xda\x87\xccU\xf8[\x94\x9e\x97\xfba>.\x94G\xe0(\x10\xdb\x9a\xdf\xb1\x86n[\x0e[\x08A\xdd6\xf7E\xd7\xdf\x16\xb5C\xa2\x1f\x8aa\x8bc\xab\x82\xb3s\xae\x00 \xd5Z\x03\xb8\xf3OJ.\xa5dE\xadh\xdb\x91\x7fm{\xdctY\x08G\xb2\xb0\xe8\xfb\xe0\xf2\x91\xf9\x08a8\x05\xc2\xed\xfc\xfd\xc7\xcf\x97W\x17\x97g\x97\x9f/\xa2`\x1e\xfc\xad\x08\x96\x13{\xc5\x8b\xe7T\x10\xa3|\xf6RlH\xd1a\xe3\xb3\x15y\xc9Dj\n\x84\xe6\xa9@l\xa2\x04awh\xda\xb4\xd5\xc2\x13\x8d\xcf\x0e\x02\xe5\xb4wbC\x8a\xee\xba\x1a\xba\xa2\xdb\x8d\xdf-\x87G\xab\x03\x006\\\x0eW\x12\xca\xe9\xff\x1b\xceQ\xd5\xbb\xf8\xc9MG\x1f\xaav\xdb\xf3\xd0y\xeb\xa3\x19q&\x16w\x12\xc0\xd0\x15\xe5\x1d\xdcW\x04\xa8]*yTJM\xbf\x8a\x06$\xf4\xf7\x1c\x95\x811X\xdeV\xf4\x01n\x9a\x008`\x8c\xe1D\x05 \xe1\xf0\x07\xe7^\xc4\xff\x7f\x08\xd1\xcfOK\xb6a\xc4\xf0\xd5\xbe\x81\x7f\x9f\x8e\xda\xb0T0Qr\xc8\x12\x90\x19\xe2\xf7\x82\xbf'\xf9\x93\x0b\xa6\xd4x\xdc\x80 \x1c{>O\xbe_\xab\xe6\x86\xf4\xdb\x92\x11{\xbe.\xaaz\xdb\xd1\xe7<\n\x00R\x13\xe4\xcfqX\x1e_|\xfe=Q\xd4\xb9\xcf\x7f<\xbb\xc0\xc1\xa3\xf6\x83\x17\xffy\xfe1\xe9\xc1wg\xe7\xbf\x87\x05t:\xbf\xa9\xa2\xd9C1\xaf3\x078\xdfS\xae\xbe\x87\xd0\xa0\xd8t\xda\xbd\xb2\xdf,,\xf8\xb8\xc3x\xa0=l\x95\xf5\xd6\xd5+\xd0N\xd8R\xd8\x9d\xb0\xdf\xb4N\x14\xdbLU\xef\xab\x95T\xf1yww\x15\xb7\xde\xaf \x15\xcc}\xd5\xf3\x0cS\xe6\xa0v\xcd\x0fx>\xe9\xc50\x14\xe5\xad\x88:S\xd6\xa5\xb6\x83\x12\xb2\xc6\xae\xd2h\x89\x1d\xc1\x95~\x04\x06i\xb9Bx\xaf\x1e\xec\x9f\x85\xf6S\x97>\xcb\xe0\x1d\xf5\xbbMA\xf4\x11;c\x805\x89\xe9\x06L\xcf]u\xe6FRTc\xc0;\x04b1N\x0b\x86\xa8X\x8a\xb1\x14D\xc4\xf84\xce\x1f\xb7\xb4\"\xce\xec\xc5x\x1c;\x90|j\xbf\xe4\xf1\xea\xc5\xa3-\xc6l\x16\xc2,\xc8\xae\x0b\x84\xf02\x89\xf1\x92\ni\xc0y\x80\x19F\xfdV\x11Y\xa9\xbf\xe8\x87v\xf1\xc7P\xf8\x96E\x0f\xe5n\x92}Xr\x96ik\x0c\x9a\xa2\x8c\xb9\x90\x1dx\xbfv\x18\xb4\x07O\xa5&\xce\x87\xa0Z\x98\xcb\xa4O_>\xece\xd6\xf7\xf1\x93=0\x9c#\x07\xc2\x8c\xe3\x18\xa3 \xbf\x08K\xc9_X\x98\x99 \x93\x870\x13\xc7\xf5`\x13i\x1d\xdb=\x8a\xf1\x8c\x9e\xdd\xfak\xf3\x0fp\x1f\x18?\xff8\xef1x\xe6LI\xef\x08<\xb3\xaf\xd8I\xef\x071\xa1\xe0zk\x82\xf7\xa7\n\xf4\xe9\x08I\xeb\x95\x00\xbb1\xb8\xdbL\xd6#K1\x07\xfb>u\xc4\xfb\xd2/bC\xcd\xd2:\x82\xa4|\x83\x9b\xaa\x8d\x049O\x95\xa0\xc9\x1c;\x10\xc5\\\xf5\x05%\xe0Wc\x0c<\x1aq\xb6Q\x1e6}\x82Nc\xb3\x9by\x8e\xa4\x1f\xc2\xa8\x01\xca\xee\xdd+\xfc<\xd80\x92\x8a5wVe\xcf:R\xd2hSde\x02\x99\xe8XcR\x94\x1cz\xdc3\xe4*qek\xde|\xcc\xd5\xdc\x12F8Q(\xb9\x84bc\x99\xb0\x94\xb9c\x89k\x85Q\x12\x01\x9c\xb7\xc1\x8d\x01:\x1b\xb5@\x1b\xc5\x9d\x84\xd5\x0e\xe2\xda \x91j^\xb6\xd2c\xf6\xcb\xd8\x00\xe6\x02,C\xd9/\xf3R8\xcf-\xe4\x90qO\xd8{\xfe\xe6\xe9\xc5\x1aL:\xf3\xea4 e\x04\xbc\x05\x19\xb0b\x0c\xd1YA\x128\x87%\xd52\xc5\x17B\x85\x17\x16-\xba\x80\x17\\p'\x0f)\xb4\xb0\xfc\xdcM/\xac \xfej\xd1C\x8b*L/\xa8`\x97O\xf0\x96N\xc0%\x17\x99\x05\x13\x9fQ*A.\xb2I\xd0S&aV\x89\x04\x17\x8e\x8e\x95G\x98Z\x1aA\x16B\xb0\xc8\xf9\xca\"\xb8%\x11\xd0r\x08\xfbX\xac\xe5\xca\x1f\xac\xa2\xa5\x0f\"e\x0f\x84\xbf\xcd\x90\xdf\xd3\xcb\x1dh\x05\x0eFrXm\x03D\x81\xd2\xb4\x1bu\x9d\xd2r=\xabcO\x8f\x1d\xc8\xcd\x08\xaf\xa7\xa2OV\xa1@\x7f\xdbs2f\xe8\xc4V\x8a\xbd\x17\xe3\xbd\xa0a\xc3\xb0\x99iq\n(\xa91v!+Z\x01\x9a/f\x01Zd\x88i\xf0Z\xcf\x0cL\x8be@I\x89;\xc1\xb3mOs\"\x1a\xa0\xa1q\x0d\xd0\xc2\x03\x98\x1a\xe3\x80\x12\xd3 \x1d\xd3\"\x1d\xa0\x05\xb68I\xc8H\xe0\x8d}\x80\x16\xd9\x0e\x84,\x1a\x07\x01-\x94\xfd8\x8f\x9f\xdc\xc8\x08h\xbe\xaf\x9fd\xf5\xef\xd9BdF\xc4\x84\x97`\xdbe\xc4M@\xf3EO@[d\x98\x93\xe2)\xbc\xd4d\x9c\xc5\x94\xa8\nh\x7f\xd9\x9c\xefq\xdb\x8el\xf9!\x1a\x01b=\x1a\xbc\x01M9\xdfg\x0e-r\xde@\x8bo\xc5c\xf6\xf6E\xb2\xb7\x03\xf7\x0bV\x0b\x98\x1e;\x02\xcd\x0fa\x86\x962\x8fS\xa2I\x02\xe4D\x9cIjL \xb4\xa49\x0b\xa5\x05&)\xe1\x16\xd9\xef\x06cMB/\x062\x88\x13.\xb0&\xc7\x9dH\x02\xa9\x12/:)\xa1\x19\xcd\x89D \x90\xb1bT\xa2\xf1(\xd0\x9efTJ\x88\xb7G\x8fM\x81\xb6@\x84\x8aA\xc8w\x9b\x99\x1c\xbe\x82\x92\xf3p\x81\x06\xb6@{L\xad\xe5@g\xdc\xa4@\x98\x00\xbd\xf0z\x93\x85N\x94)\x012\xa1\x13\x05\xc6<9L\x06Z\xd2\x9a\xa4\x1c19\xb1!\xf8[\xde\xf0\x19\xfcqo\x10\x0d\xfe\xb8'\x94\x06\xda\xa4\x80\x1a\xf9j\xce\x99\xb3Pp\x0d\xb4\xfc\x10\x1bh\x07 \xb4\xc1\xbbz\"\xe168s\x87 \xba\x81\x16\x13\x18\xd3\x03p<\x04\x03:j\x98\x97\xa9!9(1P~3\x02s\xa0\xf5\xc1\xf0\x1ch\xf3L\x94)\x9f\xb1\xcd\x05I\x0f\xdbA\xe9\x85\xb3\xb4\xd8\xfb\xc6\xbfJ\x13\x02y\x1c\x1a\xe6\xce\x8d\x84\xf3@C`\x9f$-\xa8\x07\x9a2\x06\xcc\xc9\xde\xbd\x00JX\xa7\xe7L\xfa4OA\xd0\xce\xb1\xe0FU\xfd\xc4`\xc2\xb1<\xdb\x87J\xb1mw\x97\x04\x0c\x0e\xa4E\x8eD\x0c,\xca\xbbG*`\x9c\x90XI\xcc\xf9\xc2y\xa0%\x85FA\xdb\xc3@\x027\x9a)qTQb\xa19X2\x93\xf3B\x11W\xd0\x16Y\x99\xbci\x9f\x04\xc4\xd5 \xa0a[\xd0\x84$\xf1\x87<\xc4\x99EHd\xa9h\x0eA\x9fv\xbd\xac\xc2\xe6\x8dz\x88\xae\xe0r\x07k,\xf6\x01\x9b\x05\x94^L\xe7\x8b\xc5>\x1c~\xcc)\xd22\x81Lt\xc4)\xb0\xf9G\x18\xfd\xcc8\x88\x8c(3\xb4\xfb\xa7\xa2Y\"\xfc\xccS/\x93&` E3a\\\xa9'N\x94\x106\x1e'\xd0b\x8e\x96\x8a\x12\xf3k\xab\xa5\x1e\xb9\x86\x12<\xd6V\x19\x9b\xcd\x95W\xda{l/c\xebS\xe2\xdd\xa09+zp\xb58cF&\x9f\x05\x18\xb1\xc4\xf9H;\x17\xc8\xe3\xcf\x8d\xf7\xa4H$\x81e8\x982gKj\xe4\xc9\xe3\x9f%B]ri#\x9d\xb9\x19\xa6\x8dt\x86\x92\x9f\xb0e\xf4a\x87\xf8\x9b\x1d\x88gP\x0b\x16P\x01R\xbd*\xb6i\x16GQb\xed\xa9\x95\xda8\x96eXj`s\xa3\x06-rNY\x06hX\xf4 \xb1y[0\x82\x90\x84\xd4\x18\xbf\x02\xe3D\x13\x92\x98\xb0 \x9e9q\x194=\xba\x10!6/\xc4\x10\xe7\x0e\xeb\xc7\x1byH<\xd1\x87$u\x16\x91H:\x924\x8f\xcbD#\x92HD\"Y:*\x91x#\x13 \x1a\x9dH\xf0\x08E\xb2\xf7\xf9\x9d\x1e\xb1\x88\x10\x13\x11{h\xd4\"\x99\x15\xb9\x88\x10\xb3b\x19I(\x9e\x91\x04e,\x89\xc8Y\x924\x933\xe2\x1b\xfd\x9f\xa2'\xc6\x91\xcc\x8dsD\xe8U\x0d\x1a\xebHf\xc4;b\xbd@\x04\xa47\xe6\x91\xa0q\x8f\x84o.$\xf6\x91\x1c`a\x97\x8b\x85\x94\xfd\x85\xe3!I<&\x925\x1f\x88azl\xa4Eh\x8c\x94\xb4\xfe\x80\x85K\xe6\xe5\x11\xd8t\xedC\xb5\x12\x08\x1c#\xfav\xb4\x87\xb4k\xa9\x91\xd8\xcaT\x86:)\x8fWL\xddBD\xaa\xf7\xd07\x06\xe2\xa4\x12P\x87\xb8\x90\xa3\xea<'\x9b\xa2\x17\xa8\x14mYN\xe0\xef\x16\x11~\xc8GO\xf8s!4\xf9\x9ar)j\x1b\xf8\x99Z\x075\xf6\x9b\x96\xdc\xb7\x1d\xa2\x8f\x13\xe2\x8d\xf9\x0eL\nr\xc6\xf8\xf6\xa0\x12\x15\xbe\x13e\x94K\x0e\xff\xfaDi\x82\xc7\xde\x84\x1c\xc3F\x87\xe7\xa4\x1az\x19p\xd3\x93m\x03\n\xe6\x8a\xb4l\x1e\xbeV\xa2:\x13\xfe\x95\xbb!\xb7d\x04f\x8d\x15\xc2H(\xb0W\x90J\x0d\xef\xcd\xcd\x8e\x82\xfa\xee\xd1\xa5\xf2\x0b2\xcbi\xaf\xca\x02\xca\xdb\xa6Qu\xcf~\xed\xfb/?\xdc\xd0\x97\x7f\x16\x7f\x0e\xdb\x9f\x7f\x1a\xbe\xfd\xf4\xed\xa7\xba~\xf8\xe9[\xf9\xeb\x9fC\xef\xaf\xb7\xf7\xe3}\xf9'\xfdQRs}\xde\x99#P\x1e\xec\xa4\x1a\x89\xbf~\xff\xeb?~\xb9.~x\xf1\xd3\xfa\xc7\x9f^\xbc\xfa\xe9\xd7\xe2\xc5/?\x17\xffx\xb1\xa6e\xf1\xfd\xf5\xcb\x9f\xbe\xff\x81\xbe4\xea#\x1a\xfeo\xb7\xfa \xff\xf3\xf7_\xfe\xf4\xce\xc3\x97o\xf5\xddWZ\xab\xe1\"\xf1\xbf\xb9\x03V$\xd2\x86\xfc\xd3//\x7f\\\xffr]\xbe\xf8\xf9\xe5\xcf\xffx\xf1\x8a^\xff\xf4\xe2\xd7\x9f\xbe_\xbf\xf8\xe1\xfb\x1f\xbe\xff\xf9\x1f\xdf\x97?\xd0\xd2\x1a\xb2\x00\xb1\xf9\x07\x0d\x0f|\xff\xe5\x9bw\xd8\xbf\xf6_\xea\xf2\xf6\xc7\xfe\xdb\xd7\xe6\xd5\xab\x7f\xfd\xf4\xf2_\x7f\xde\x0c\xbft\xfd\xed\xc3\x97\xdd\xba\xfbW\xd9\xe9\x0c^\xf2\n\xa6E\x03\xca\x83\x1a\x08;E\x0b\x03\xa0P\xd4}\xab\xf3!\x8e\neb1}*\xe8\xb4\n\xa9\xa4{; :\xd2\xf0\xfa\x0d-\xa9\xdb\xf6N;\xf8\xe4\x8d\x99\xb3c\xf7\xb1\\\x1a\x1f\x01F\x89_\xc7\xe1?\xfe\xb8FFX\x8d\xc4u\xb0\xa7\xca\xd23&\x12A\xd3\x13\x89\x05u\xb2\x04,80\xd1C\xda\xd8\xe4\xd33\x86vL\x1d\x15\x1b\xc0\\#P(u\x94\xd1\xaby\xbc\x91\xe4\xf4\x18Y\xb91\xb2\x13cX_\xb2\xc1\xb1\xfa\x0cu\xeb(\xfc\xc8\xafh\xb7m\xbd\xea\xad}\xf8\x8c+\xfd|Lt\xf5\xdd$\x8d\xd4b\xc9\xfb\xb6\x9f\x02 \xa2\xc8\x92\xcc\x05\xa8Q+\x84]\xff\x0c%=\xcf\x7f#*\xc1\x00g\xe1\x84\x90\xf3\xfbMM\xefi3\xf4\xa4_\xdd\x9d\x9c \xe0I\xd5\x0c\xb4[\x17%v\x07b4x^ PU\xe1\x0d\n\xf7(6\xbdU\x077\x957\xfc\xfac\x0f=\n`\xdf\xcf\x14(Y\x8c\x95\x92P\xa6|\xfe\x94Q/\x16\xb9Y\xb1\xc6\x83\xad\xa8\x9c\xb9\xa2\xae\xdb\xaf\"ZI\x02\xf8m\xc1\xcfZ\xfb\xb5\xa1\x1d\xe2\x9e\x85!\xe3\x91j\xde85\xef\xbe\x83\x16s\x07/\x96\xd8\xc2\x84\x93\x93\xa2\x04\xc3\xc63\x19g\x81\x87Jwm\xedO\"\x11\x8d!+\xf8\xfb\xe3n\x96\x9dj\x89_J\x91:%\xe2\x1e\x12\x11\x184\x90\xef\"i2\xc2\x91a/\xc8\xc7\xb3O\x97\xff}u\xf9\xdf\x1f\xdf&\xc54\x19/|\xf8t\xfe\xef\xe7\xef\xcf.?|J{\xfe\xe2\xed\xa7?\xce\xdf\xbcM|\xfa\xfc\xfd\x1fo/\x92i\xbf\xf9|q\xf9\xe1\xb7\xf3\xb3\xf7i\x8f\x7f\xf8\xe7\xfbT>\xce\xde\xbd;\xff\xfd\xfc\xec\xf2m\xda\xe3\x1f\xfe\xeb\xfd\xf9\xbf}\xf6\xc7\xc9\x19\x0f\x7f\xfc\xf4\xe1\x8f\xb7\xef\xcf\xde\xbfI$\xfe\xe6\xc3\xfb\xcbO\x1f~\xff=\x95\xf7?\xce~?\xff-\xb0@*\xac.k\x1b\xc4\x8d[\xd0|\xbb\xcb\xd7\x1d\xd7#\xfc\x01a\xb4\xeb\xda.%\x80\xce\xb3I_\xe3?C\xaf\xa4\xe8\xc1\xec^q\xb3Q(\x1a\x15\xdd\xd3\xaf\xb1\x1fG\x8b\xd4\x8a^\x0f\xa4\xa7\xddCU2=t\xbdm\xca\xc1RSC\xbd\xc8o\xe15\xf6#\xa8_\xdc]Z\x95\xa4j\x1eh\x9f\xce\xbf\xfan^\xa3\xbf\x8a\xc9\xa1\xcdP\x0d;8\x94\xd4\x98\xcam?\xb4\xab\xaah\xc4\xc0D*4>\x91\xa9\x03\xe3\xdf\xe1k\xe7\x17;\xde|St\xc3N\xf0\xc2\x0f+)\xd5\xd9)\x94\xd8\x95\xfa\x8e_\xa3\xbf\xc2,BG<\xba\x97\xed\x89\xf5\xba\xaa\xabb\xa0\xa4\xb8\xe9(?T\x13;\x13R\xe05\xf2\x1bt\xc4O\xf3\xa2\x06\x1d\xb6]\x8b\x8e\xd5\xa9\xdf\xb55\x0c\xf6\xbe\xa9\xae\xb7=\xb9.\x9a;y\x9a$\xb20\xca\x96\xd7\xf8\xcf\xd2\xacn\xf9\x11\xd4twt\xd3\xd1\x9e+\x12\x1c\xc9\xa1\xa2\xf7\xe1\xcaoe.*\xca\x8c\x8fr\x94c\xaf\xf1\x9f\xcd}\xf7\xf5\xb6\x82\xd2Vb^\xc6\xfcC\xe2\xabU\xc9`*Jh\xd3\x0e\x9e\xe4I.#J@\xbeF\x7f\xc5\xd8\xe09!\xf8\xd6\x84XI\xd8\xef\xe1\x14#\xd2/\x1d\xd6j|\xce\x04h1\x97\x034\xa1\xa3\xa8;\x98Z\xce\xbf\xf7\xa4\xafn\x9a\x82\xa7h\xa9FW9B'\xac\xe7\x9c\x91\x8f\xda\xd7(\xb5,\xf6\xc9\x9c\xf2\xf4N0+\\\x0f\xb2\x03\xe6\x0b\x94\xa0\x17A\x17?g.\xf9=\x8a\x8d\xaf\xa2\xfd\xb8a\xc9\xd0nHM\x1fh-T[\xd3H\x83 '\xd0\xd0Nl\x92<\xa9\x1f\x9b7R4;yI\xeb\xe1;\xbdoW\xd5\x1au5\x0d2\xed\x01\x92\xf0\x0f\xe8\x9b\xb7\x93\xaa!\xdb\x86\x87PW\x03\xe6\x94\x94\xdc\xd4\x15\xbb!\xca\x8az\xd2\xd4a\xbb\xb4X\xcfWE\x89\xa7\xca\x9b\xa1\xd2{\xf4\xcc\xf8*\x9d\xa9\xc1\xcaK\x08\x1b\x08\x1fy\xb1\x1dn\xdb\xae\xfa\x13\xa4PGKZ=\xd01'\x12B\x8c\xcf+Z\xaa\x14f\xd8z\x85\xdb\xf0\xaf\xf8.\xb8\xf2^-\xe6\x8eo\xfc\x0cL\x01\xceV \x9c\x08\x89\xa9#\xe4&\xb9\x18\x8afUt+]\xe8\n\xe9\xdfs\x1c\xd7}\xd1\xdd\xd1N\xfd\x86zr;J\xfa\xedf\xd3vz\xa1Y\xe0\xe7D\x98N\x8ba\xe8\xaa\xeb\xed@\xc9}\xb1\x93FT\x84Vy[47tE\xae\xc1\x95$\xe4\xe1\x18\x1d\xde6%;\x8b\xbd>V~\xf9\xbe\xe2r\xe8\xaak\xebz\xbb\xf1-\xc2\xfe\x1c\xae\xff\x14\"\xb1\xa8k\xf5A\x19\x97b>\xb1\xd5\xd0\xab\x0fK\xa6\xf4\xc4\xbeH!h\x0c\x02\x7f\xef\xa5\xb8YW\xb4^\xa1No\x98\xf6\xbao m\x8a\xeb\x1a\xeew\x1c\xf7!\xe4\xf1\xff\xe3n``HP+\x1a\xec\x02(m\xedb,\xe6\xa4\x87\xa7\xe3\x82S\x97\xa0)vim\x07-\x91\x0f\xd7\xe2H\xd9\xd65-\xa5\xd3T\xda\xaa\x18K67\xd7\x02\xbc\xd2\x81c\x10aHE4\xe5Vg1^\xcc\nPBn\xd7\x88\x80x\"F2\xd7\x8489\xb0R\xcd\xd8\x9e+\xa6X\xfd\x90I\x01\x94\x8a\x08\xfc\xcf\x84\xfa\x18\x13\x18\x1e\xfb\"\xb1h\xc8\x08\xe38\xba:\xfd4\x19\xa9\x90\x04\xb8t\x84\x99 \x93\x870\x13G4c\x13\xe9~\xeb\xfe\x00\x97\x94\x0f\xde\x88h\xc9\xf9\xea-j\x1e\xeb\xeaB2\xc0\x1b\xb0\xb2\xd0\x8eE\x8f1\xa3\xeb\x88\x88\xe8#!\x88A\x11\x12\x8bA9\xd8 S$L\x8cFx\x88\xda\xbf&|E\x8b\x8e\xd5b\xc5\x94PNh\x08B\xce\xf8H\xe3\xa3\x9f+\xc2bCJ\x15n\xb1\x89 \x0ca\xc2\x92e\x0d!.\x12c\xcc\xdb\xabjA:\x1c\xe7\xf9\xb2)\xf6E/\xf6\x14\x05\xfc4!\x1f\xcd\xfe\xd2\xd0\xc7]p\xf3\xe8\xa7,;\x11y\xe0\xb5\x94QF\xdc\xa4[\xeb]\x9a\xd0Pbz\xd2(1q\xf6FV\xb7{\xffp\x1f'Ud\xc4\x11\x17]\x0b\xf1\xd0\x1cg\\\xd8\x1d\x173TA[\xce%\x97\xe8\x94K\x9c\x98x\xca\xc6,\x9f\x8c\xf3J\x82s.\xd7=\x97\xeb\xa0\xcbv\xd1e9\xe9\xb2\xddt\x99\x8e\xba|W]\xbe\xb3.\xd3]7\xd5a\x97&\xf9\xa0-\xec\xb4\xcbp\xdb\xed\xddqw(\xd7\xdd>\x9dw\x8f\xee\xbe;\xa8\x03\xef\xc0.\xbc'\xe1\xc4{\xdan\xbc'\xe4\xc8{\x1cW^\xdc\x99\x173e\xcb\x163i\xcb\xb6\x84K/\xae+Mw\xeby\x08z\xd3\x92\x92\x91\x1be\x87\x17\xb7\\\xca\x950\x88L\xe7\x7f\xe0\xdf\x12\x1b\xa4\xac\xa7\xe4\xa591+N\xbc\xf6\x17\x8f \x94#\xd6\xddM\xc2\xfe>f\x1b\x16z\xe4s|\xfew\x9b\xaa,\xeaz\x17\xa8\x0b$\x08L\x18\xc6\x02\x17\x1f\xa9\x05\x03\x98\xb7\xa258\x8d\xd8q\xc4\xce\xbd\xb2\xe6\xa1\xab\xea\x83]\x15\x03\xee\xceu\x9c\xbe\xfe\x9bO\xb1]a\xb1\xa9i\xfc\xbe\xe1w\xab\x15\xb9\xde='\xdb\xcdJ\xfd\xffP\xdd\xd3~(\xee7\xfdsec\x80P\xa1\xe7\x1eO\n!\x1d\xadE\x96\xe3u\xeb\xb2\x19\xbdN\xc5.Sp\x0d\\]1&}R#\xe1\xd2 \x17\x99\x91y\xc1\x86\xe9#5V\xd2b\x8f\x9e\xb2G!4\x92o\\\xda\x0c\xdd\x8e_]\x05_A\x9e\xaf\x91\x10wh \x1c\xe35\xc6\xf4\x1b \x1c\x96b)ci\xfe\xc52?\xb9y\xac\x8b~\x90\xcc\x05\x19?\xc4drXB\x950\x9b\xe2\xe3\x08sT5\x03\xbd\xa1>\x85T\xce$\x1e'\x0e-v\xf0\x10\xae\"\x89\xa3\xcb\xfabaD<\xa5U\xd9q\xe5MJ\x15O\xf864\x98n\xf4\xcf\"2u\xfe2$\x8eG\xf4\x87\x16\x8d\xe3\xdb\xbej\x9b\xd3\x00\xbf\xac\xd1\x07\\k\x8d\xe0t\x98p}\xc7\xe4xO\xcab\x03J\x02\xeaA\xe1\xdbW\xed!\xdc\x92u_\xdcQ\xb1\xb5$\x02\xbdhV\xf2k\xa0;\xf2\x95v\x94\xdc\x17+{\x181Q~!\x8eQ\xe5\x8dFNTR\xdc\x14U\xc3\x8b3\xc9\xf3\xc7\xa1cZ\xe7\xd8\xd3<\xde\xc3\xf5\xc1_j\x16\x1f\x1e\xf4z[\xef*!C\x91\xc8\x899i?\x0d\"d\xa9\xe4\xf4\xa1\x15\x98}\xff\x91,\xcf\xd0\xba\"\x1f\x13Y\x16D\x05M\xcd\xf4\xc1\xd3pZ=G\xe4\xb7x82\x88\xb0\xfc&\xfb\x19H@KF\x18\x9b&\xcdub\xa19X2\x17\xe6H1M\xbe%\xb07se\xf2\xa6}\xa2\xa4#\xf8\xba\x99\xc3r2[N\xd1?Q\"\xe9Z\xa8C/\xe8\xcc_H)\xb5y\x9epZ-\xa03\xd8\\x%`\xc0<\x9b\x9c\xb5\xd8Y\xa6\x83(\xb8\x89#O\x91\x9eI\x84\x12\xc6]&%\xa3}\x849p\x19\x13\xf3\x91\xf0:\x86\x88\xcd\x9d\x9b%\xf4\xc9\xa4\xb1\xa6\n\xe3\x04R\xf1QM\\\xe0\xfcQM\x14\xd4 \xdb@\x1fb\x88/ah\xf0\xa7\x17V\xf7\xdf\x05\xf2\x0b\x1b}\x0b\xc2\x90\xf9\xa5\xd9i \xbfTb\x0d\xf9\x84\xca\xadA\x9c\xd4\x1a\x1aA7\xc9\x86xn\\JX\xc0e\x01x\xd0\x89\xbd]&\x1e2{\xf3s\x9d\x8e\x0b6\xa2\xa5\x80\xf5\x13B\xfe\xe0aL\" F\x80\xe4PRH\xc8\xdbY\xdd\xb7\x04Jh\xf3\xda\xec\xef\xd8\x01\x8c:\xc0\xf6i\xd6\x89T\x85^\xd9\xe6\x1ci#\xb4\x81~:\xa8\x0f%%2A=\xe3\xb9H\xaa\xe1\x96\xac\xabz\xa0\x1d]\x91\xbb\x07y\"\x0d\xb4+\x86\xb6s\xbd\xdb\x02b\x86\x0e?8\x00Y\xb2S|\x9f\x86\x86$9\x87e\xabw\xf2\xdb\xdc\xf9\x90;Z\x05U\x0e\x13h\xd7k\xe1\x98wRs\x07\x0bq\xcf\xbb6.\x96\nd0]&\x0e\xe8\x81\xcf\x8f\xda\xf3b&Q\x92\xa1\xfa\xd9y\xfc\xdc\x8a\x12\xd9E3\x86;\xaaE\xf4\xc2\x0b}_?\xc9\xea?\xe4\xbb\x00\x9f7\xe6\xae\x90\xcc=c\x1d\xf1\xaa/\xcf\x03\xbel\xd9\xda\xce\x9e\xed\xb2\xbd\xbfo\x1b\xde\x0f\x8e\xee\x80\xc4M{\x1d&t\x01\xben\xc8\xe2\xd6\xd9\x95\xec\x95\xa7\x9bmR\x7f\x11'\xd6\x9e\x01\xb9\xefF\x8b>\x9f\x97S5d%C\xaa\xe6\xa1\xbdC\xd6\x16\n$\xfb\x85\xde\xe3@\x80C\xbb-i!\xd24\x1eh\xef\xd92\x88\x94\x9b\x906\x0c\xb2\xe9U\xcd\x1d\xb9.\xca;\x9e\"\xf7V\x84-\xc6\xa0\x84l\xf1\xf0\xcb\xb6J\\\xe9\xbfm'\x0e-\xc1\x16\x92\xb2\x15\x89+\x9e\xc4 2\xa2\xa1\xde\xb5\x1dy\xdb\x0f\xc5u]\xf5\xb7\x01\xe4\xb1J>\xea\x83M\x1d\xa8\xfe\xbf_\xc0U\xb4\xe4P9\xfdxb\xc3\x0b\x10\xfb\xd8\xb5\x9b\x96I\xe8\xc8\xd8\x94XZf\x80<\xd1\xdeF\xf6\xbd\xe6F\x83\xa1\xdb\x96\xdc\x91\xca\x0f\xd3\xfb\xa2\xebo=p+B\xfa\xa1\x18\xb6!\xf8|\xc2<\x9e+\x14e\xb5\x86\xc3\x89\x0b\n.J\xe46\x91\xccEPk\x04d\xf1\xbf\xb6\xdc\x8d\nh.Q\xad\x8b\xd3\x9c7gq4\xfb\xa7\xb7o>|\xfa\xed\xea\xfc\xfd\xc7\xcf\x99U\xf4\xf1w?~\xfa\xf0\xf1\xc3\xc5\x84\x17\xe1\xb7\x80\xc0\x12\x08\xeb\xa9\x0c\xa7K\xbc\xe8\xa4\x84f4\xf2\xaa\x06\xc0\x0d\xa2\xb3\xab\x86\x83 O\xb7\x0d(\xe8\xb0\xbf\xd8\x9a\x07^\x8a, >w\xf2\xaf64X\xdb\xd1\x0d)\xba\xebj\xe8\x8an7J\n\x9e\xf0A\x1d\xa0\xb0e\xf3y\x83\xdfp\xce\xe07\x9c\xaf\n>4C3\xd8t\xf4\xa1j\xb7=\xcfWo}\x82#\xbc\x16\xe5Q|\xf4\x97]Q\xde\xc1\xa5\x1eN~\xa5\xa2S)\xe5c\xaau\xf86\xa3\x11u\xf476\x86\xf2\xb6\xa2\x0f\x90XT\x94\x92o\x9b\xd8\x8d\xc6\xf9\x13\xbc\xf9\xe4\xb4\x96\x03\x9dq\xff!\xce\xb7A&{\x11\x13\xa9\xb6*\xfc\xfbt\xbc,\xe1)\x1be\x8b\xc1\xb5\x969Q.8\x0d\xc9\xb7\xdc\x12\xe3}/1Q\x89lb\xcc\xe2\xd3\xa9\x9a\x1b\xd2oy\xe6\x99\xe7\xeb\xa2\xaa\xb7\x1d}\xce\x0e\x9d\x0d\x94&\x98\xb7&)G\xcc\xc5\xe7\xdf\xb3d\xb5\xfb\xd6\xc7\xb3\x8bpH\x90\xf9\xf8\xc5\x7f\x9e\x7f\xccx\xfc\xdd\xd9\xf9\xef)'O\xee8\xf2\xce\x1c\x0f\xf5)\x1d\x13\xe3\x9c!\xdb\xa6\xa7\xfc\xa6\x97\x12\xeb\xe3N\xbc\xcd\x01\xfb\xcd\x10\xcbT\xdb\xa9\xd9\xe6BN\xc8h\xc0\x14\xb7\xc7\xde WF\xe9y,\x9b\xd2\xf2k\xbd\xe3_\xa53e\xf7l\xd7\\\x1b\xe2KU\x0cCQ\xde\x02u\x15\x08\xc0\xbe\x11\x14\xbfk\xee\\\xb1\xbf\xf8=\xcc\x1bLbL\x99\xe0@\xccH\xdb\x88K\xa7\xf8Y@q\xcd!)c\xc0$/\xb5\xf9v\xba{\xda\xe7\xba\xf1L\xfa4OA\xd0\xce\xb1\xe0FU\xfdx=\xcdb\x01\xbc^\xe4q\x1a\x0f\xe2>\xb6\xbb\x0b\xc3k\xd4\xd3!\xbe\x1f\x1b\x1c\x89q2\x0dM\x934\xdc\xa7R\x89|\xc9\"\xe4\x81\x11/\xe1\xc3\xd6H\xa5x\xa9C\\=a\x18a\x80\xeb\xe8\xdd.v\xb3;\xa2\x08'r\x9f$\xe6R jQAG\xf63\x90(\x9cm\x01\xb9G\x8e(\xc2\xeci\x9f\x08N!)(B!I\xe6`\x08\x11\x12Y*\x9aC\xd0\xa7]/\xab\xb0=*z\xd0\xe4!\xa6\xd1\xf5\x91\x04s\xde\x14s\xce\xe2\x1cD\xf1K\x1as\x8a\xb4L \x13\x1d\xf1\"\x80\xb2\xe5G\xef\x87\x9f\xb9\x7fA\xe9\xe1\xeas\xea\xac<\x15\xcd\x12\xe1g\x9ez\x994\x01K(\x9a \xe3J=q\xa2\x84\xe2@\xc9\x99Z*J\xcc\xaf\xad\x96:\xbc\x1b%hh\xb2\x96\xd6\xea\x1d\xd2l\xd5\xd5\x1e\xc6\x8c\x13zA\xdd\xc9\xe6j\x1aR\x9c\xa4\xa3\xc5I\x1eb\x9c<\xfa\x8cL>\x0b0b\x89\xf3\x91v.\x90\xc7\x9f\x1b\xefI\x91Hb\x1a\xba\xdc\xa5\xb3\xa4F\x9e<\xfeY\"\xd4%\x976\xd2\x99\x9ba\xdaHg(\xf9 [&\x15\x85\x0e\xae\x08?\x08](\xa4K`\xd0\x81T\x00u.\xfa\xb2\n`;\xd56-\xa2Z\xa9\xcc\xb2\xdd\xecF\xfdH\x14\xb3\xb4\x81\xb5\x00c\xb7jl\xa3\xe7\x10~\xfaxmW\xde\x9d\x12\xde\x1f\x96\x01+\xb5\xde\xf3\xf8\xea\x125\x9f\x89\xd7\x0e4qT\xca\xc2\x93T\x10\x99\xcc\xaf\x03mQ\xb3\x0b$\x13\xc9SFeh\x12@\xceO\x9d\x961\xedA\xf2\xc4\xcc\xae\x16\x9d25\xf0\xcaR\xf5\xa3\xc9\x9c\x1a\xd2\x04\xa9#MP\xd4\xacw\x11\x84;-\xbb\xa64\xf1\xd7\x95\x1e\xfb\xc3\xf2\xca)}\x00\xf9[xG,Tg\x9a\x88c!\xa7 3 \xd6\x9b&\xfb\x1e\xb0\x1e\x85\x14\x1d\xb3|z\x81!{\xebP\x93=\x8dxv]j\x8b^l-\x9d\x13\x93\xeci`s\xebU[\xe4v\xed\xb6K\xa8Y\x0d\x03'#`\x01\xfe=\xcc\xaeZ\xcd:\xbe\xd0\xb0`\x8ex\xd1\xa1RqXT\x00\x02\x15\x80;y\xa1M\x890&|\xc5\xa6\xc1\x93\x10\x10\x92\xa0\xe7@\x91\xf6\x04;z2\x10\xa3C\xc2\x89\xc4\xe9\xa5oH\x8b\xecC\xd1U\xed\xb6\xe7\xc8E\xaa\x83u\xa0# a\xf7o\xf6\x8b\xf1psT_OavC\x05BO^\xf4R\x8c![f\xd4''\xa2\x1a\xb9F-\xab.y\xef\x05\x1b\xcd\x1b\xd2\x80\x17\x87I\xaa7n\xe7\xfcL\xae3n\xd7\x17\x87!,\x1bd\xeb \x16\x0c\\\x8b\xc5\xf4\x14Y\xa5 \xb0\x82\x04\x92Pn\xa5\x01\x12\xac'\x10\xe0\x1c\x87\xc2&W\x0cH\xad\x13\x90V\x1d \xad&@b%\x80\x84\xfc\xff\x89Y\xff\x93r\xfd\xa7f\xf8O\xcd\xeb\x9f\x94\xcd?/\x87\x7f\xcch3-_?d\xe5w\x88\x05\x90\xbb{\xca\xcd\xbf\xdf\x8c\xfc\xcb\xe7\xe1\x7f\xa4\xec\xfb\x07\xc8\xb9\x7f\x90L\xfb\x8f\x98_\xff)f\xd5\x7f\xf4\\\xfa\x87\xcc\xa0\xef\xcb\x9b\xef\xbf\xf1\x85\xef|\xea\xe0\x9d\x94\x19\x1fG2O\xcf\x82o\x15\xb7v\\f~9>\xb1\x88\xb5U\xb2Z#8\xadx\xf50\xa7h5\x92$7\\\xac\x1a-R\x9d\xa1\x17Zz\x91\x7fv\xa7\x17\x9f\xb6\xd5\xddx\xd1\xe9`\xb1\xe9\\\xbe\xa7\x15\x95\x96K\xa6Q\x9aSLzr\x11i\xadd\xb4\xb5\xc7\x12\x8bG\x87\x8bF\xe32\xc3'-\xfc\xb3<\xbd(\xb4V\x02\xda\x18`J1\xe8\x89E\xa0%\x0f\xee\xa7\x8e\x98\xa1\xd40\xe7\x16{\x1e\xcb;\x0b\x82v\x91\xe7\xd0\xd5\xfd|u\xaeL\x97{\xbe\xc0\xaf\\de.pW\x81\x12mL\xc2L.R`\x04\x0e\xba\x13AH.\xc7\x90\x8b\xdf\xd4~Ib\xcc\xf4+\xc6\x85\xdb\xf8V\x8a\xdb\xdc\xd3i\xc2$ \x9d\xc6=\xd8~8\xaaAN\xdb\xd2\xa3\xadD\xda\xb8\xb8\x15\xbc\xa37E\xb7b\x84\x85\xef\x88T![.'\xf8I7\x83/\xf7\x91\xf8\x85\x9e\xfa8\x92\\{3\xfd\x9d\x8e\x1b\x8f\xff9\xc3\xbb\x89'\xa6\x8e\x0f\xf9\x7f\x8c![\x19\xa9\xbd>H{\xf4\x0b:\x1cQ\x87||\x1c\xc6\xd2Mt\xc2/\xe7\x80\xf7\xfb\xe1\x96:\x92\xe7\xf9\xdd\\H\x8a\xf8\x0cq?\xdb\xe8\x19*\xb1<\x80\xcb\x8dF\x07\x95D\x07\xa3\xce\xdf\xac\xb1x\xfd\x85K\x8de\xb6\x7f\xd0\x1a\x8d\x7f1,\xbf\xe0R\x03\x98\xeb\x07\xe4\x9e?s\x00\xb8\x0fP\x97\xeb\xa3\x03P\x00}\\\xff\x1f\xa8hI\xde?A\x19&.\xef\xc8x\x8d\xcf\x90\xd8S\xc0\xa4J\xac\xc9\x7f\xd4\xc7\x87\xf6\x84\xf7\x86\xf4\xe8}\xdbO\x81x\xa1>$l\x1d\x8f\xa0\x1eC\x81\xe63\x1c>\x085\xe1\x02\xcas\xfc\xc8\xe6w\x00\xc9\xb6\x9f)\x98\xe3 B\xc8q\x8bB\xb2\xa3H6\xdba$\x1bf \x90\xcd\x9bT\xc6\xbb\xef\xa0\xc5\xb0\xdb\x1e\xc7\x92l\xc1U\x10\x8fLq4\xc9\x16\xaa\x80\x1d\xcb\xdf\xb0d\xf5\xeb\xa4\xda\xd7 \x93\x11N\xe3\x92\xec\xc3B_H\xa8y\x9d\xe6\xd3B\x9f\x8e\xd6\xbbN\xf4q\xa1\x8f\x87k]'\xfa\xbc\xd0\xc7cu\xaeS}`\xe8\xf3 5\xae\x93|b\xb2\xe5\xf9\xc6\xc6\xb7\xc2>2\xd9\xa6\xf9\xca\xbc\xe4\x92+[\xef\xc9w\x86\x92_\xdc\x87\x86\xf6\xb2\x88/\x0d\xa5|8\x9f\x1a\xda\xfd~|khW\xfb\xf2\xb1\xa1\x9d\x1d\xd6\xd7\x86\xb2\xf0D|n(o\x8f\xe1{C\x19\xd9\xb3\x0fN\xb6X\x0d\xeb\x90ON\xb6\xb0oN\xb69>:\x8b\x86G\xb2/\xe6\xb3\x93\xcd\xf1\xdd\xc9\x16?g&\xfa\xf2\x10J\xa6wo\xa2OO\xb6Y\xbe=\x84\x9e\xe4&\xec\xe3\x93\x0d\xf5\xf5\xc96C\xa5\xf7\xe8\x99\xf1U\x9a\xee\x13D\x88\xf1y\x8d\xfb\x06e\x0b\xfa\x08e\x9b;\xbei\xbeC\x84\x92\xdc$s|\x88\xb2M\xf6%\"\xb4F\xef\"\xc9\xf0)\xca\x16\xf6-\xca\x16\x96\x811\xe9\x17_\xa5\xe9\xbeG\xec\x8bT\xde\xc8D\x1f\xa4l\x13}\x91\xd8^\x11\xa6t\xc3')[x:\xe6\xfa(-r\xcac\xe9\xf8*eS\xfe\x1e7\xb2\"\xcc\xa9\xf1\xe2h\x0fI\xc8&\x82\xdc\xae\x11\x01\xf1D\x8cd\x0b8SeS3\xe6\x0b\xf4^\x98\xcbI\xceV\xd9\x10?\xe0\xfe\x19\x1e\xfb\xcaw\xc6\xca\x06\xbf\xbb\xc1\x9e$\xc6\xef\x92NZ\xd9&O\xde\xc2\xce[h\xf0k\xef\xcdF\x91\xf2\xc1\xebog}\xf5\x165\x8fuu!\x19\xe0\xcd.\xb1\xd0\x8eE\x8f1\xa3\xeb\x88\x88\xe8#\xf9\x82\x82\"$\x960\xe2`\x83L\x9101\x1a\xe1!j\xff\x9a\xf0\x15-:V\x8b\x15SB9y\x1c\x10r\xc6G\x1a\x1f\xfd\\\x11\x16\x1bR\xaap\x8bML`\x08\x13\x96,k\x08q\x91\x18c\xde^U\x0bk1\x0d\x92\x99\x1c\xaa#z\xb1\xa7(\xe0\xa7 \xf9h\xf6W3.\xee\x82\x9bG?e\xd9\x89(\xda\xa6\xe5w.\xf5$G\x96\x83N\xcb\xe6\x8c\x12\xd33<\x8b\x89\xb37\xb2\xba\xdd\xfb\x87\xfb8u\x1d\"\x8e\xb8\xe8Z\x88\x87\xe68\xe3\xc2\xee\xb8\x98\xa1\n\xdar.\xb9D\xa7\\\xe2\xc4\xc4\xeb+d\xf9d\x9cW\x12\x9cs\xb9\xee\xb9\\\x07]\xb6\x8b.\xcbI\x97\xed\xa6\xcbt\xd4\xe5\xbb\xea\xf2\x9du\x99\xee\xba\xa9\x0e\xbb4\xc9\x07ma\xa7]\x86\xdbn\xef\x8e\xbbC\xb9\xee\xf6\xe9\xbc{t\xf7\xddA\x1dx\x07v\xe1= '\xde\xd3v\xe3=!G\xde\xe3\xb8\xf2\xe2\xce\xbc\x98)[\xb6\x98I[\xb6%\\zq]i\xba[\xcfC\xd0[C\x84\x8c\xdc(;\xbc\xb8\xe5R\xae\x84A\x1a9\xfe\x07\xfe-\xb1A\xca\xe2\xc7^\x9a\x13S\xd8\x86\xa7\x85g\x87\x1a\xdaq\xc4\xba\xbbI\xd8\xdf\xc7\x14\x1bB\x8f\xb4\x13kA\x1bv\x9b\xaa,\xeaz\x17(\xe2+\x08L\x18\xc6\x02\x17\x1f\xa9\x05\x03J\xb7\xa258\x8d\xd8q\xc4\xce\xbd\xb2\xaeh3^\x82\xech\xbd\xb19N_\xff\xcd\xa7\xd8\xae*t\xb4)\xfc\xbe\xe1w\xab\x15\xb9\xde='\xdb\xcdJ\xfd\xffP\xdd\xd3~(\xee7\xfdsec\x80\xa4\x82\xcf=\x9e\x14B:Z\x8b\x92D\xeb\xd6e3z\x9d\x8a]\xa6\xe0\x1a\xb8\xbabL\xfa\xa4F\xc2\xa5A.2#\xf3\x82\x0d\xd3G\n\xf6\xb4\xf0B\xd3S\xf6(\x13\x1f\xc2WE\x9b\xa1\xdb\xf1\xab\xab\xe0+\xc8\xf3\xf5n\x06\xc7\x1a'\x9e\x1b \x1c\x96b)\x05\xfe\xbb\xf4\xd5\xe4\x13\xcb\xfc\xe4\xe6\xb1.\xfaA2\x17d\xfc\x10\x93\xc9a U\xc2l\x8a\x8f#\xccQ\xd5\x0c\xf4\x86\xfa\x14R9\x93U3\xfc\xfc*\xccv\x085\xdb\xa8\xa3\xcb\xfabaD<\xfft\xd9q\xe5MJ\x15\xb4\xa0\x95l0\xdd\xe8\x9fEU\xaa\xf9\xcb\x908\x1e\xd1\x1fZ\xe1\x9do\xfb\xaamN\x03\xfc\xb2F\x1fp\xad5\x82\xd3a\xc2\xf5\x1d\x93\xe3=)\x8b\x0d( \xa8\x07\x85o_\xb5\x87pK\xd6}qG\xc5\xd6\x92\x08\xf4\xa2Y\xc9\xaf\x81\xee\xc8W\xdaQr_\xac\xeca\xc4D\xf9\x858F\x957\x1a9QIqST\x0d\xaf\xa4,\xcf\x1f\x87\x8ei\x9dcO\xf3`\x0e\xd7\x07\x7f\xa9Y|\xbeVuMn\x8b\x07\xaaQ\x16\xb3\xc3\xe3\x7f\x07\xa9\xd5\x9b\xb1\xf6\xd0\xaa\xe6\xa1\xad\x1f\xe8\xea\xe4on'\xef?\xb0K\xca?\xc5\xect\x94\xa31\x9a\x16\x1c\xe4\x9f\x8c\x8c\x02\xa4\x10\xfex\xb4f\xdc\xe8\x95/\xe4T=\xe7\x8b&\xe7\x8d\x7f\xe3\xf7\xed\x83]tp\xb4\xd3N*\xe6a\xbd\x9e\xe5\x84C\xadh\x1e%\xe0\xc9Y\xa1m\xaf\xe8\x98\x99\xd5\xebk3c\x1a]\xfdA\x9b\xcb\x83\xd4\xddp\xfa\x8b8\xe7\xd5\xe3A\xd6C^\xe6E\xd9\xf7\xc83\x94\x95\xa8\x07?B*\x90[\xd0\x16Y\x99\xbci\x9f(\xe9\x08\xben\xe6\xb0J\xbb\x0c\xc5\x14\xfd\x13%\x92\xae\x85:\xf4\x82\xce\xfc\x85\x94R\x9b\xe7 \xa7\xd5\x02:\x83\xcd\x85W\x02\x06\xcc\xb3\xc9%\x86\x9ce:\x88\x82\x9b8\xf2\x14\xe9\x99D(a\xdceR\xe5\x98G\x98\x03\x9711\x1f \xafc\x88\xd8\xdc\xb9YB\x9fL\x1ak\xaa0N \x15\x1f\xd5\xc4\x05\xce\x1f\xd5DA\x9d\xb0\x0d\xf4!\x86\xf8\x12\x86\x06\x7f- u\xff]\xa0\x18\x90\xd1\xb7 \x1c(\x0d4>a\x04r<\xabt \xb0\x88\xda\xa2\xab\xefd\xa7\x9e\xf43\xcb\xc2\xee\xa0\x13{\x93L\x80\xc6\xa1\xa4\x90@\xb7\xb3\xbao\xc9]\xd3~mH\xc1v\xcb;v\xec\xa2n\xaf}\x1as\xc230\xa2\xfc\xe4&\x96\x96A\x1b\xde\xa7C\xf9PR\"%\xd43\x9e\x81\xa4\x1an\xc9\xba\xaa\x07\xda\xd1\x15\xb9{\x90\xe7\xd0@\xbbbh;\xd7\xa7-\x80e\xe8\xf0\x83\x03\x10/\xca\xaf\xd2\xd0\x8b$\xe7\xb0l\xf5N~\x91;\x1f^G+\x16\xc0\xc1\x01\xedz-\xdc\xf1N\xb5\xac\x90ga\xe6eq\xb1\x04 \x83\xe9(q\xa0\x0e|~\xd4\x9e\x173\x89\x92\xbc-\xfa\xdbe\xf8a\x9483\x8d\x96\xf8T.\xa2\x17T\xe8\xfb\xfaIV\xff!\x8f\x05x\xba1'\x85d\xee\x19\xeb\x88\x17f}\x1e\xf0`\xcb\xd6v\xf6l\x97\xed\xfd}\xdb\xf0~pL\x07\xe4b\xda\xeb0\xa1\x0b\xf0pC\xe1\xa7N3\xb0\x0f\xad\x0eHa\x9b\xd4_g\x99\xb5g@\xee\xbb\xd1\x8e\xcf\xe7\xe5T\x0dY\xc9\x90\xaayh\xef\x90\xb5\x85\x02#~\xa1\xf78\xc0\xdf\xd0nKZ\x884=\x07\xda{\xb6\x0c\x10#+\xb2\x80A\x01\xae\xaa\xb9#\xd7Ey\xc7\xfe\xc1\xb6!w\x0f\xc5\x00\x84l\xf1\xf0+\xb6J\xe5\xe7\xbfc'\x0e-\xc1\x02\x92\xb2\x15\x89+\x9e\xc4 2b\xa0\xde\xb5\x1dy\xdb\x0f\xc5u]\xf5\xb7\x01\xbc\xb1,\xd7\xd8\xfb\xc0R!\xf9\x958\xec\xd4\x11\xe1\x02\xae\xa2%\x07\xc8\xe9\xc7\x13\x1b^\x80\xd8\xc7\xae\xdd\xb4LBG\xc6\xa6\xc4\xd22\x03\xe4y\xf36\xb2\xef57\x15\x0c\xdd\xb6\xe4\xeeS~\x98\xde\x17]\x7f\xeb\x01Y\x11^zg\x1b\x02\xcd'\xcc\xe3\xb9\xc2NVk8\x9c\xb8\xa0\xe0\xa2Dn\x13\xc9\\\x04\xabF@\x16\xffk\xcb\x9d\xa7\x80\xe1\x12\x05\xb59\xcdys\x16\xc7\xb0\x7fz\xfb\xe6\xc3\xa7\xdf\xae\xce\xdf\x7f\xfc\x1c(\x89\x95\xfe\xee\xc7O\x1f>~\xb8\x98\xf0\"\xfc\x16\x10X\xaa\x8a\xd64\x86\xd3%^tRB3\x1ay\xd5\xa8\xcc\x15 S5\x1c\xfax\xbam@A\x87\xfd\xc5\xd6<\xf0RdI\xf0\xb9\x93\x7f\xb5\x01\xc1\xda\x8enH\xd1]WCWt\xbbQR\xf04\x0f\xea\x00\x85-\x9b\xcf\x1b\xfc\x86s\x06\xbf\xe1|U\xf0\xa1\x19\x9a\xc1\xa6\xa3\x0fU\xbb\xed\xeb\x9d\xfb \x8e\xa0Z\x94G\xf1\xd1_vEy\x07WyQZL\xaa\xe8TJ\xf9\x98j\x1d\xbe\xcdhD\x1d\xfd\x8d\x8d\xa1\xbc\xad\xe8\x03\xe4 m\xb7\x03\x1bh\xdb\xc4n4\xce\x9f\xe0\xcd'\xa7\xb5\x1c\xe8\x8c\xfb\x0fq\xbe\x0d2\xc5\x8b\x98H\xb5U\xe1\xdf\xa7\xe3e O\xd4([\x0c\xa4\xb5\xcc\x89\"\xaa\xc5 \xbe\xe5\x96\x18\xef{\x89\xe9Id\x13c\x16\x9fN\xd5\xdc\xc8\x12}\xcf\xd7EUo;\xfa\x9c\x1d:\x1b\xda\xac\x82\xd3\x9d\xb2&)GL\xb8\xdeb\xca[Z\x0d\xc6\x94\xc7\xb5\xba\x8c)\x8f\xabZ\x8dXK\xac\xdf\x88\xbf\x9as\xe6\xe4\xd7y\x0c\x1e v\x05H\x92\x18\xe1\xb3\x97z\x90)]=Z\x8d\xc8\x14\xe6\xf6Y7\xd2n1\x81\x01\n\xf7\x07\xf8\xc6iS\x16\x9b~[+\x16\xe4\xc1\xb1\xe6G\"\x08\x12y\x1ez\x08\x06t\xd40/\xd0\xd7\xb3\xfe;\xe5\xc3\x82\x02\xedd\xacl \x15~\x15'\x90\x1a\x0b%\x06\xca\xaf|p\xcc]$M\xc7\x1e!\xfc4\xa2\x92m.\xe4\x84\x8c\x06Lq{\xec\x9d e\x94\x9e\xc7\xb2)-\xbf\xd6;\xfeU:Sv\xcfv\xcd\xb5!\xbeT\xc50\x14\xe5-PW\xf0\x7f\xf6\x8d\xa0\xa8]s\xe7\x8a\xfd\xc5\xefa\xde\x10\x12c\xca\x04\x07bF\xdaF\\:\xc5\xcf\x02\x80k\x0eI\x19\x03&\xf9\xa6\xcd\xb7\xd3\x9d\xd2>\x87\x8dg\xd2\xa7y\n\x82v\x8e\x057\xaa\xea\xc7\xeb_\x16\x0b\xe0\xf5\x1d\x8f\xd3x\x10\xa7\xb1\xdd]\x18T\xa3\x9e\x0e\xf1\xfd\xd8\x90H\x8c\x93i\x18\x9a\xa4\xe1\xc2-\xcckl9\xe4x5V\xe4\x80\x85Y\x83\xff\xb4\xc8\x88\x97\xf0\\k\xa4R|\xd3!\xae\x9e0x0\xc0u\xf4n\x17\xbb\xd9\x1d\xb1\x83\x13\xb9O\x12s)\xc0\xb4\xa8\xa0#\xfb\x19H\x14\xc4\xb6\x80\xdc#G\xec`\xf6\xb4O\x84\xa4\x90\x14\xec\xa0\x90$s\x90\x83\x08\x89,\x15\xcd!\xe8\xd3\xae\x97U\xd8\x1e\x153h\xf2\x10\xd3\xe8\xfaHZ9ob9gq\x0e\xa2\xf8%\x8d9EZ&\x90\x89\x8ex\x11\x18\xd9\xf2\xa3\xf7\x83\xce\xdc\xbf\xa0\xf4p\xf59uV\x9e\x8af\x89\xf03O\xbdL\x9a\x80%\x14\xcd\x84q\xa5\x9e8QBqx\xe4L-\x15%\xe6\xd7VK\x1d\xd4\x8d\x1244YKk\xf5\x0ei\xb6\xeaj\x0fc\xc6 \xbd\xa0\xeeds5\x0d\x1fN\xd21\xe2$\x0f'N\x1e}F&\x9f\x05\x18\xb1\xc4\xf9H;\x17\xc8\xe3\xcf\x8d\xf7\xa4H$1\x0dS\xee\xd2YR#O\x1e\xff,\x11\xea\x92K\x1b\xe9\xcc\xcd0m\xa43\x94\xfc\x84-\x93\x8a=\x07W\x84\x1fz.\x14\xd2%\x90\xe7@*\x805W\x0f\xd8Ps\x14`n\x95\xd4\xb4\xba\xd2\xeaa\x96\xedf7jM\xa2f\xa5\x0d\xb7\xa5\xbd\xf2}\x8c\xec\xa3\xa7S\xa0<\xa4s\x02y7Ox\xcb(\xf3HR=c2\xbf\xa6\xb1E\xcd\xaepL$O\x19U\x8e ES\xacx\xe8\xb3\x91\xb2\x9eQ\xab\x9el\xe9\xd6\xbd\xf1\x0d\xbf\x95O\xb6\xc5\xac}\xb2E\xc2\xfa\xf7`\xfdCI/j\x05D{\x98m\x0dD\xa9\x1e\xc6*\x88v\xbd\xbcu\x10\xedf\x1fVB\xb4\xa3\xc3Y\x0b\xd1\xee\x9f\x80\xd5\x10\xe5\xeb\xd0\xd6C\x94\x89\xbdZ\x11e\x0b'\x8b\x98adD\xe9\xe9\x86G\xd9B\x1c,d\x88\x94\xcdg\x90\x94-`\x98\x94m\x1f:J\xf0\xfe\x12?\xa0\xe6\x192\x11\x82m\x83\\\x8cLu\xce\xb7h3\x0c\x9c\xeeZHsg\x8e\xa1sl\xe6\xd5\x18\xbb\x14\xab\xb4\x16\x98\xa5\x07\xc9qaBA\\t]x\xa9\xdc\xb7GlH~\x08\xa5\xc7@e\xad\xd2\x84\xabx\x89b\xbed\x9b~\x0f\x8f\xefc\xa3\xeb\x1c\x80\x18\xf6\xa5\xe3\x813\xe6\x1a\xf8\xe2e\x0e6\xc8\x14\x9cY\x8cFx\x88^\xf4\x94l\x87\x1b\xab\x07\x80e\xff,F\x8f\x89\xa5\x00J\x0c\xe9\x12\x0ffI\xbf\xf2\xc6\x86\x94\nk\x8bML`\x08\x13\x96,k\x08Y@8\xe4}\x1f \xce:I\xa7\xf9\xf7\x92\x8b\x02\xf4~[d\xd0\x92\x16\xac\x85\x18\xb5H\xce;\xd5\x17\xb3KF\xedo\xa2\x9f\xdf4\xa3\x9a~\xda\xa8\xdep\xdd\xc1;\xec\xc9\x01\x9c3\xf3\xaeO\xb3QFg\x89L\xea=\xcbR\x99`\xab\xcc\xe7!\xcf^\x19\xb3XN\xeb\xdfo\xb5t\x1e\xf7\\3\xf4\xcdy\xdb\xd6\xab^\x04j\x99FtY\xb7\xf8\xb6hV;_M]\xab\xaa|AT\xa6i\x97\xb9\xa8Q5\xac\xee'd_\xf5N\xa4\xf8*\xcf\x025\xa7\xdb\xafMo \x82j@\xbe\xc8\xb8]u\x7f\xc3\x08\xe7\x12\xcd\xb2\xb0\xe6\xd9Xs\xac\xac9v\xd6,Kk\xb2\xad5\xcb\xda\x9aao\xcd\xb3\xb8\xe6\xd9\\3\xac\xaeS\xec\xaei\x82a\xaa\xed\xd5C\x8c[dc\xd6\xd7\xbd\xda_\x0fa\x81\xdd\x97\x0d\xf6Q\xad\xb0\x07\xb3\xc3\x1e\xd0\x12\xfb\xe8\xb6\xd8\xa7k\x8d}\"\xf6\xd8\xc7\xb0\xc8\xc6l\xb2\x93\xad\xb2\x1ej6 tla>\n\xc36\xbb\x13=\x1b\xf6\xd9\xf5\xb6\xae\xd7U]\xd3\x15\xf9zK\x1b\xd2W7\x0d\x8fy\xc2\x87\xdd\x15M\x0f\x9b\"\xfbZ\"\xcd\x88\xfb\xbc\xaay\xe6a\x88\xd5\x9c\xe1Sb|\x1a\x9eD\xf0\xde\x85\xf2\xe5\xe1OS2\x07\xad\x84\x8c\x9a\xce\xeb\xaa)\xba\x1dy6\x16\x94\xa9\x9a~(\x9a\x12\xf9\x14x \xa6\xa9\x89\xa4\x04\x13P\x1aJ\xa4&\xe1%\x9d\xa4\x143\x97\x19RP\x17\xd7\xb5\xbd\x10\xfeEx#\xde]\xc4\xec\xbc\x80\xe19\xcb\xf4\xec1\xd9\xa3\x19\x96m\xf7\xc4\xa4T~(\x91tK\xb4C\xcfJ7\x13\xc6\x80L\xb4\xc6\xd8\xb1\xc7\x0cwnG\x13\x0d\xdd\xf9+>q\x81\xf3G5\xc9\xf6\x9d\xb4\x0d\xf4!\x86\xf8B\xcf-\x7f^&T\xaaX\xd2\xc4\x9b\xb3\x89\xa4W\x086\x06\x14\xc8\xdd\x84K\xfc\xdeL|\xe1\x03\x07zs<\xf9#\x06\x8eN\x83\x05\x9d\x06\xf3\x949\xcf\x07\xf6^\xe8x\x90aL\x94o\xaa\xea\x9a]\x05\xa0 \x86\xb8\x07\xb8\x9c\xa2\xf4*O\xf9\xd6\xbfl\x89O]WV+\x1a\xaa)8,[\xa8\xb1\x18 \xf2\xda\xb4UY\xd4\xf5\x0e\x8a\xd2\x0c\xad*\xef:\xde\x07|\xd7f\xf1\x1d\x1f\x0b\x83:-i\xd8\xa9#:pa\xd0\x18[\xe7l\xa7\xe2\xb70\xa8\x0d\xfc|\xdc_\xcf\xc5a\xc5.a\xdeM$k\x05>\x13q\x80\xadYB\xfb;2\xb4\xa2\x07\xee\x81\xe2\x1f\x8a\x87\xd8\xa6\xe8\x8a{:Pl\x16\xc2\xe3:#\xdcd\xb4\x96e\xa9\x0c\x0bG_\x0cU\xcfD0\xd4\xe5\xd9l\xea\x9d\xff~\x0f\x93{\xe1\x1c\xd9\xa2XP!\x1ep\xde\x0d~\xe5\xb3$\xf6\x19\xd8\x12\x95\xc0\xe1\xd5\xf7\xb0\xc2u\xf2{\x19\xa5\x02JO\x94k\xe6\x95\xea\x94\xc8p7\x13Dl]q\xd6\xb3y\xbe\x14v\x03 2\xcaI,\x91:y&\x17*P\xab\xb4\xedd-\xcb\xdf\xcf/.\xb1\xad\x1f\x99\xe3P\x8c\xc1oo\xdf\x9d\xbf?\xbf<\xff\xf0>\xcd+\xe2\xbe\x11,\xcf\xea>\x1e\x18\xa8\xefa>n\xe4\x0d\xe5\xd5\xc9\x1bD\x8a.\x1e\x9c\x98\xdc\x0e\x9d\xd2y\xbc\x00\xeb\xa9(\xc8\n\x95\xb0q\xb1\xe0\x9fm\x97\x07\xa4\xea*?\x18Ap\x8ar\xdb\xc2>~K;J\x9e\xa9SFz\xae\x9bvP\x96\x1f/\x02\x18_#\x97\x1f\xa4\xd6\xaa\xac\xfa-7\xbdSo\xd5:\xf5\xf2X\xe0\xdb\xc4\xc7\x07\xff#\xceL\xb1\xc3X)\xdb\xba\xa6\xa5\xbc\x00\xf3GG\xb38\xb9-\x1e|\x07$\xc7\x801\x81\x85\x16\xcb\x17\xd1\xac\xd5uM\xaf\x84\xb5o\x0f\x8a\xe2\xd1!\x9e\xf2\xf0\xd1!~t\x88\x1f\x1d\xe2G\x87\xf8\xd1!~t\x88\xff\x95\x1c\xe2\x97\xc6v\xd34\x0e\xfb2\xf27\xf4ud\x18\xd8\xcdPw`\x9a\x97\x1a\xf0=z.\x8b\x904\x83{\xd9!\xe4\xe7Ty?\xe1\x02{*\\\x98\xd6\x9b\x19>K\xf4\xa6\x15\xaa \xdb\xcf\xf0W\"$\xd2\xbd\x95\xc7\xd2c\xc7\xd2cy\x1e\xcaX\x01#\xab\x9bE\x9cW\xcb\x8f\xde\xef\xear\xff\x82\xd2;\x96\x1e\x0b\x11\x0cO\xc0\x1e\x1d\xb3v7\x93\xdd\xb2\xb1\xf18n\xcec\xe9\xb1$\x14\n\x89\xaf3\x89oq\x928\xb1d1T\nIG\xa6\x900C2\x8c!\x15\xc0\x90\x06]H\x03-$\xc2\x15\x12\x80\n\x89\x10\x85$pB*,!\x15\x90\x90\x04E\xc8\x03!\xc4.\x96\xd3\x80\x07\x9e,\xa8\x01\xc8\xc1\x9e\xc0\x06\xfb\x85\x19,\x0f0x$h\xc1\x01@\x05\x07\x81\x13<\"\x90\xe0)B\x08\x1e\x1d\x167T\x9a\x82O\x1a\xfam\xb8b\x0f\x0f-\xb9\xa67\x8e\x9a\xfaeK\xbb\x1d\xfb\xbc\xd9\x88\xd9\xc3lR(\xb9o\xfb\x81\xd0\xf5\xba*+\xda\x0c\xf5\xee\x84|h\xea\x1di\x1b~7i\xd7k\xb8\xeb26l\x89p\xdbn\xeb\x15\x0fL\xa4\x83a\xdd\xe2/e\xce\xca\xb6j\x86\x9f_\xf9\xe7\xc5\x11f\x825>5\xcd\xf6\x9e\xdfp\xc5op\x97*\x1a\xc6\x1b\xbf\xee\xf0@p\x98H\x8b\xca\xb6)\x1e\x8a\xaa.\xaekzb\xfbR\xce9\xf5\x9a\xe9+j\x82\x18\xed\x86l\x99\xbcb\x04sg\xcb\xee\xc2\x9d\xbc\xba\xba\xaf\xf6>w\xbc\x13)\xfe\x87v(j\xcd\x0f-<\xbb\xb0\x8f\x8c\xfd&\\\x0d\xdb\xda\xc1\xa8rm\xd2\x9e\xbe5\xa9\xe9z \xf4~\xc3n\xec\"<_\xd8\x89 .\x0b\xb64t\xc4f\xeezGhQ\xde\x92b\xb3\xd1\xe7\x84\xdf}\xaf8\x9f\xd8\xcc,\xeb\xe3\xd1:c3\xc4\xf7SK\x86nK\x89um.\x06mF\xf8\x83b\x91M\x82B\xda9>a\xe8I*\x02\xf6*\xf0\xcb\x14Q\x9b\x93\xcb*M\x8a9\x9f\xf7\xe7\xf3\xdeY\x01k(-\xdb\xaa\x1d\xf7<\xab\xec\x08\xe3W\xc4>\x94\x13\xb1\xe7\xab\x9b\xa6\xed\x9c\xb8t\xf9\x15\xd9\xdd\xc0,\xe9\x0b\xd6Q\xa6\xe6\xd0\xfd/\x96\xe8\xc8^\xa8j\xdc\xc5\xfc2\x8e\xedd\x8b\x12\xeb\x876\x1c\xcb\xdev+\xda\x9d\xfc\xcd\x1e\xe6E\xd5\x94\xf45)\xdb\xfe\xbe\xed_\xf4\xab;\xf2\xf2\xe4\xd5\x8f\xea!qU4\xe47\x08\xe8\xb1\xfa#\xe7\x83\xde_\xd3\xd5\n\xf8\xb8\xf9\xf4\xf1\x8d:\x01\xc5\xb5\x0c\xce%%q4r\xe3\xfa\x9f\x90\xb7\xe2\xbe\x90\xaati*\xcf\xe85\xcc.D\xc9\xa8Lu\x1c\x02\x07\xf3\x0bR\x1e\x93\xb5`o\xcf\xf7iB\x0b\xfa\xf6\x16\xf6oB\x0b\xcc)I@\xca\x1es\xbc/\x9dc=\xcdC\n\xed\xd09\xde\x1f1\x8d\xfa\x92\x1eUh\xc7l\xea\xc7\xe0q\xdbE\x93\xf2\xf41x\xdc\xf3\xd4\xbe\xfc\xb9\x08\xf1\xc5\xbd\xbaH\x1f\x8b\xf8v\x11\xba\x87\xf3\xf0\"\x9d\xef\xc7\xcf\x8bt\xb4/o/\xd2\xd5a}\xbe\x08\x03O\xc4\xf3\x8bp\xf6\x18\xfe_\x84\x8d\xbd{\x81\xa1=\xb1\xe0q\x0f\x1f\x0b\xba\x91\xa1\xc5j]F]\xca\xd0\xf6\xa7\xeb,\x10\xe69\xd5\xed\x8c\x12\x93\xae\xe8\xa0\xf3\x19\x9a\x7f)\x97qDC\x9b\xe5\x8e\x86\xb6\xacS\x1aZ\xc85\x8d\xf4\x8aL\xd2L7\xb5C\xcf\xe3\xe4uVm\xa2\xa1\xa0\xf4\xba\xaf\xa1\xcd\xb3\x12\xc4&\x8b\xd8,\xe4\xf8\xb9q\xc9\xe0\x8b\xec\x8c{\xc0\xa1\x1dx\xc0)\xae\xf28\x95\xd8p\x83\xce]h\x87\x1ew\x9es\x1d%\x18si;\xdd\xee1x}\x82/>F'2\x98\x89\x0b\x999\x98l\xbf\xbdC\xc1_G\x93D\xd8Y\xc0\x93o\xd0\x9b\x12O\x89\xd9\x83\x95.%#(\x91\xce\x95\x04zj\xb1\x90\xc7\xe0\xac\xa5\x066\x17~`\x91s\x82\xb3\xa0a0\x04\xb2G(\x02 )\x13~U\xc2\x81%\x90\x98(\n\x9e(q)5\x1d\xa6\x80\x10\x9b\x87U\xc0\xb9\xc3\xfa\xf1B\x18\x88\x07\xc6@Rg\x11q\xc9\x93\xa4y\\\x06\xd6@\"\xd0\x06\xb24\xbc\x81x!\x0e\x04\x859\x10\x1c\xea@\xf6>\xbf\xd3\xa1\x0f\x081\xe1\xfaG\xe1\x0fd\x16\x04\x02!f\x81\"H\x08\x18A\x822\x96D\xe4,I\x9a\xc9\x19@ \xff\xa7\xe8\x01K\x90\xb9\x80 \x84^\xd5\xa0\xa0 2\x038\x81\xf5\x02P\n/x\x82\xa0\x00\n\xe2\x03Q\x90\x03,\xecr\xa0\n\x92\x04\xac qp\x05 \x18B\xa6\x83,,B#\xe4\xc2\xfa\x03\x86\xbb\xc8\x03$*\xfb9\xbfT\xea0\x9e\xd1\x02!]\x87\xae2\x95\xa1N\xca\xe3\x15S\xb7\x96\xc3$\xaaC\\\xc8Qu\x9e\x93M\xd1\x0b+\xb3\xb6,'\xf0w\x8b\x08?\xe4\xa3'\xfc\xf9X\xd6EHQ\x04\xd6\xdd\x81=\xb5i\xc1]\xed\xe8\xe3\x84x\xc1cy\xb0:\xdf\x1eT\xa2\xc2w\xa2\x8cr\xc9\xe1_\x9f(M\xf0\xd8\x9b\xf0k\xc1?\xc7\xe7\xa4\x1az-\x03|\x03\n\xe6\x8a\xb4l\x1e\xbeV\xc2\x1c\x8c\x7f\xe5aP\x0e\x99\x91M \x13\x18\x94\x9dD\x80\xbd\xf4z\xca\x07\x01\x17\xc6d\x00},i\x80\xb3\xf2\x9f\x01qs\xfe\x9b\x86\xb4a]\x9e\x10r~\xbf\xa9\xb9\x0f\xa9'\xfd\xea\xeeD\xd6_\xaf\x9a\x81v\xeb\xa2\xa4\x88\x08\xda\xf6\x94\x9d\x14\x9d* CA\xd8jiU\xc9\x1b.#\xf5!\x06\xd1E\xcb\x0d\xd5c\xf7\x1b\xddX\xdc\x040\xd8)d\xe1r\xc3\x96\xdf\xa2G\xe5\xecH\xab\xf9\xd7j\xb8\xad\x1am\x12\xb5\x17\x00_\x81\x0d-+\x10-\xdb\"+\xd6a\x82eG\xd69\xc2\xcb\xee?\x93~$\xa4bI[O(UR\xf0\xf7\xc6](;R\x93\n\xb9\xd2\x98\xa8\x15\xacl\xba\xb6\xe4[\x0c\xa5\x87V\xc3\x8a\x0e:T\x9e$\x03a\x90\x03(I\x87\x93\xa4\x83I2\xa0$\x89@\x92\x0c\x18I2\x88$\x07B\x92\x03 I\x86\x8f\xe4\x83G\xe2\xda\xe7T\xe0\x88'\xf8\x9f\x84\x13\x00\x90\xfd\x82F\xf6\x0f\x19\xd9\x0f`\xe4\x11\xe1\"\x07\x02\x8b\x1c\x0c*\xf2\xc8@\x91\xa7\n\x13y\x12 \x91CCD\xa4\x11\xd8\xafM\xf8n\xed\xf1{\xbd\xd2\x0f\x94\xa5[-\xdb\xdf{^\x0d\xbf\x18\xb6\x1dW\xf2%\x1b\x7fC\xdfG\xa4\xf2\x19\x00W\xa4\xe8\x13\x1a\x0d\xfb\x14N\xab\x86 \x15>z\xae\x7f\xd8\x05\xdd\xdc\xf2\xfb(| |.\\r7\x8b\x04\xab\xa8MH\x86vCj\xfa@k\x03~+S\xeb\xf5B\xf79!Ar\x1c\xf6\xc2\xe6\x87\xe7\xe3\x14v\x02 \x89iW\xd5\xda\xb1\xd9\xb0.x\xf5:M\xb7\x92J?\xd06\xb5\xf7\xaa!\xdb\x86i\xca\xecy\x8b\x96\xe4\xa2\xae\xfaABB(\xe9i\xdf\xeb^+\xd6X\x8fWEY:\xda\xe8\x0458;tx\xc4;K\x05\x9d\xa3a\xd8\xe8\x8a\xedp\xdbv\xd5\x9f 5:Z\xd2\xea\x81j5\"\x11\xcd\x1f\xad\xfa\x073\xa8=\xc9/\xb9W\x06\xe2;\xf1j\x13\x19\xcb\xb8\x85M\xc1:\xa8:b\x01\x06\xed\xcdt1\x14\xcd\xaa\xe8V\xba0\x14\x12\xb9\xff\xff\xd9{\xb7&\xb9m$_\xfc}?E\xc6\xbeX\x8e\xbf\xd4\x96lkf\xac\x88y\xe8\x95\xe4]\xc5\xdf\x17\xad\xba5s\xf6\xa9\x86]\x85\xea\xa6\x9bE\x96IVK\xed8\x1f\xfe\x04q!qI\\\x89\xea.y\x89'\xa9\x9aH$\x12@\"\x91\xf9C\x82BivE{K\xda\xf17\xe3\x1eXK\xa0;\xec\xf7M\xdbs\xe0\x10m\x8c\xf2A\xe7\xd3\xb0\xe8\xfa\xbe-\xaf\x0e=}\xe0\x8c\xb9#\xaf\xc8\xa0c\xeak\xc3H\xbfb>\x15\xae\xa7\x84\x12\x1ff\xd6z\xd8\x0f\x11g#?\\\xd2'\xcc\xeeWmSU\x87\xfd\xf1\xc3u\xff\xe4j\xaa\xa8\xaaq\x11(\x87?*\xbcr8<\xf3\xc5\xa0\x03\xd44\x82J\xe5\xaf:\xa1\x12\xb6%\xa96\x86\x97\x97\x89\xb5\xea\x1a uqU\x11\xba\xed\xd1 \x07\xd7\x8d\x7f\xa7>O\xc6\x08\xa7Do\xa23^\xf0El\x89Y+]\xa7\xee\x0f\xe9\xa9\xd2\xb6iz\xe9\xb9:\x81\xb0\x92\xdf\xab\x13\xdal`c\x03W\xf2\"\xa7\x91\x99\x96y\xbd4&\x84\xe3\xc3\x95\xd0C\xf9&\n\x17%\xe9\xbc\x91\xf4#xe\xd0\xde\x80\x1b\xb4dM\xc7q\xccL\x1ca\xc8\xa2\xf1c\x949\xf6\x8f\x88l\x10\x91\x0cN\xf4AA\xff`i\xd7\xed\x8c\xce\xcb\x9e\x11\n\xd6q0\x10! \x84\x01?\xc0\x06\x13\x98\xba\xe8p\xbc\xa2]'\x98\xb5\xa2\xd7\xa2NmI\xa53\x95\x9c\xdcF\xacb\x044\xa6\x92\x92\xfe\x171g\xc3\x986\xb6[g\xd3\xb0\xa4\xd2\x89\xb4!-\x02\x0c\x82\xe49\x87\x821\xae4~\xc1l\x0e\x1f\xd8N\x98K\x82\x8a+\xfe\xb2$\xc0Y\x12\xe0, pb@eK\x02\x9c\x99\x08\xb1%\x01N8\xfakI\x80\xe3\xc3s- pX\xc9\x87\xd5\xdaxqZ_@\x02\x1c\xdd\xb8\x01G\xaa\x1b\x19\xcb\x12 _IOes\xec\xe45k\x05'\xe3\xa4\xe1\xa2\x03V\x1f\x0d+V\x8d\xcc\x8a\xe7v\x92\x1b\xc80\x03Q\x83\xd2\x1b\xa8$\xe1jXq\xa3kX9\xa68lG\xe1\x10\xd4\x0dJ\x90F.\xa2\xb07\xac`\x08\x1cVl\x01\x08V\xbc\xf7v\xad\xa9\x7f\xfc\xc9\x7f\x9c\xf8\x1cV\xa1\xd48\x1e*\x01\x01\xc5\x8a\x1f\x07\xc5\x8aOo\xfa5f\xc8\xa8\xa5\xe3\xa3PrB-E\xa1\xa4XI\xc4J\xe1\xf3\x87\xf1j\"\xa6X\xf1 f.z\xca xu\xef\xc2P\xb1\"\x1ca3\xf2Oe\x80uH\xe4t\xa9\x9d\x9c\x83/\x13,\x8b\x95Q\x82\x0f\x92$Jk-\x04\xe8aO\x0c\xe4\x00m\xb1\x92\x91q\x8b\x1aA\x18\x01\x1f\xba\xcbI\xc8\xd6\xd7\x1c\xc9\x9c&J\x11\x88\x0f;K\x89R\x0f\x17k4\xf4C\xae\x8c\x02\xc8\xe4\x0fp\x18\x19+>\x1eM\nQ\xba\xc7\xa0g\xf1Bg\xd5D(\xd4\x8c\x95\x87Z' \x984\x94\xda\x92\xf6\x0ekD\xfa_\xe2\xd2\xcc\xdeo\x8d%X\xd2\xde\xe9t\xfe\xf7\xa5\xbdK\xc4\xdb!\x94\xe8\x87\x96\xfctKF:Qf\xe3\xfe4z\xfd\x92\x91\x0et\xde2b\x03\xc1\xb5\xcf/\x19\xe9\x96\x8ctKF:\xa9\xf8\xe5\x9b\x8eED\x88-\x19\xe9\xd2\x90\x8b\xf6\xa5\xb8d\xa4\x1b\xcb\x92\x91n\xc9H\xc7J\x849\xb9d\xa4[2\xd2\x81q\xc4Zk(Zp\xe5\x9eKD\xea\xb2c\x1c\x13\xb11a\x11|.\xfb^\xf1@\xa1#\x82LQ;H4\x94\x82K\x0d\x8e\xaf:\x8a\x9d\xc6\xf2\x02 \xdd\xd7\x86Q\x11@\x8buK\x04\xccB\"\xc7;z6j\x0dm\xcdbQ\xd9\xb9\xc0dK\x84\xd3aY%A21 \xa6 \x14\x8b\xb0t\xe3(\x1d\x9c\xe3\x98\xc9`\xa4d(>2\x0c\x15\x19\x86\x85\x0cD@\x06\xe0\x1e\x03\xd1\x8eA\x18\xc7Pdc(\x9e1\x08\xc5\x18\x87]\xf4\xd9.i8EK.3\x07:\xf1H\x98\xc4\xe3\"\x11\xf3\xe3\x0f\x1f u\xf8\x00X\xc3\x07A\x18>\"\xae\xf0\x14\xd1\x84\x8f\x8e!|H\xe4\xa0\x0d/\xe8:\xdb\xb9Ovs\x10\x81\xb8Y\x9b\x8e\xfe\xd3\xf2\x90\x19\x98?\xde\xde\x08\xb4\xe1a B\x8d\x01\xf6:M?>\x9470O]\xede\xa7SR_\xa3F\x8d\x04\xackC5\xea}\x11\\\xcb\xf81\x0e\x99!\x9f\xc9\xfa@\xedf\xfd\x95&\x8e\xa0\xa3,3\xa77\xab\xa1\xcd\xc8\x93\xdbP\xe5\xd9\xc0\xa1Z\x8dM\x85\x9e\xe14\xc97\xc3\x07\xcc/CG\x9e\xd4}{O\x0dz\xde2\xc2\xcbU\xe8\xc1Zj\xcbbS3\xcd\xcd\xa5\xcb\x0f\xfe\xeb\xa6\x95[\xe5\xf2~\x04 TE\xd7\x8b\xe6\x11\x86r\x8b\x81\xc2jK\xab\x1c\xf8\xfc\xc2\xda,\xeb\x9e\\\x93\x16\x95\x81\xf5\xf8>l\x93\"\x1c\xa3\xce]\xc6N\xd9AY\xaf[\xbam\x8b\xf5C/\x8a\xb3\xfeK$\xb97)N\x1ar\xf3\x9c\x00\x02 %lr\x94M\xfd\x0dk\x96\xbe\x10o\xe8PEQ\x9c\x0f+\xf9\xc7AAt\xb0.\xf6L\xe3\xa3\xf8\x13:\xc0\xe3\x184\xb0+n\x95~\xd1\x01\x19_\xfc\xaa7b\x96\x90{\xf8DZ\x02\xbbb\x838\x1aFf\xb87`\xc2\x0e\"\n\x15\x8a\xeb\xa2\xac\x076&\xd5\x85a=\x86o\xa8\xbbAxD.\xa5\xc3#\xf5\x0d\xdd\x14wD\xa2\xc2\xfbJ\xf3\xc7)\xef\xae\x8a\x97\xdf'\x17\xe4/\xbf\x0eV\xdd?y\xdf47\xd2\x07%\xaf%\x14\x1c\x0fI\xa7\xc8\x84\x82,\xd4.?\xa5\xe2\x15\xfd\xa7\xb3y\xd7\xd06}\xae\x93w\x9bwc\x00\xf8\xc8\x0e\x14=p>\x92\x02+\x14\x87wH\x83\xdaL5\x0dt\xcdl^BP2\xd2\xe7\x08K\x18.m>[&\x18M\xfa%\x8a=\xfe\xcf\xec\x0cJt'\xdbJ\xfa-\x98I\x15;\x83r\x862\x11\x8a\x86q4\x1d \x14\xb4i?v\xc5&\xaa\x91\x83u\xe3M\xe5\xa8h]\xa5\x02\xdb<0\x95\xcbp,\xe5f\xd2\x1dH\xd4\xc4\x14H\x94\xcd$X \xdc\x90\xc2\xdc\xff\x82\xa8]9\xac\x97L\x8f\xb4\xe0\x0cF)\x0c+\xa3\xf3\x92\xbb\x85\xafH+\x03\x11\x02B\x18\x08Y\x97\xa6\xc0\x18\x17\x18^\x8c\xed\x96\x13\\\xec\xaeh\xcb\xe6\xc0\xd6\x1e\xb4\xe4\xbahi\xc8\xb1\x18w.w\x9a6\xf6Q\xf4C9\xac\x9a\xae\xa7\xc2\x96*\xb2\x7fC\xc2\xac\xb3\x07CR\xa8\xf9|\xac3\x83#\x1a5\xf9\xc4\xad\x82\xac\x90\xa0\x06\x16(\x81\xe5\xfd\x99\xe8\xf8\nJoy\x7f\xc6\xf0X\xfb?]\xde\x9fY\xde\x9f\xb1\x92\xce\x1e\xbf1Z\xc8\x12\xc31\xa8>\\\x1c\xc7h\xfa8\xb1\x1c\xa3\x99c\xc5s\x8c\x86\x1e6\xa6c4\x7f\"q\x1d\x83\xaf\xc7\x88\xed\x18L\x1c9\xbe\x03\xcb\xfb3\xac\xa0\xf9\x1er\xc5\x7f\xc0\x88\x01A\x80\xff\xd5\xe8r\xc6x\x10 1!\xc0\xe2Bp\x04\xe3|^\x9cH#\xc6\xc6\x13\xb3\xce\xb5x\x11x\xf9\x9a\x19C\xd2\xa8\xd1\x88\x12\"\xc7\xa8\xfb\x11\xf6\xe0\x12\xf8\xcc^w\x88\x05 \"\xccb\x06\x9a\xc0\x11l\x02\x1fg\x81\xd1\x16w\xd0 \x9c\x81'\xf0\xf1\x90Q:\x96 \x148\x02Q\xe0c/PD\x9e\x80\x14\xd8\x82R\x00\xae\xc0\x148\x83S\xe09\xf4\xa5F\xad\x10RF\x1c\x0bl\xb1,\x08\x14h\x9e\x98\x168D\x90-\xb6\x05Q\xf1-pj\xb7\x9cq.\xc0b]\x907\xde\x05yc^\x10\x16\xf7\x025\xee\x11\xecWW\xabD]\xa9\xc7\xdd\xeai>\xf5L\x8e\xbalA7p\x07\xde 'oa\xdeu$h\xe1\x0d\xc4AV6\xa3|\xec^v\xd1\x18\x14\xccg\xd8Pj\xe9Q;+!\xacO\xc9\xb1\x03\xa9~X\xf4\xc0\xc9F\x84<\xc3\xc4\xe6\x0f*\x84\xc9\x1b\x0d\x0b\x99\xd7\xb1=Le\x0c\x03\x82uH\x93\xac]<,\x08\xbe\xbd\xdd:\xa9!_\x88\x10|aB\xc8\xcfe\x98RC\x02a\x10\x126\x84#0\x1c\xa5\xde\x9c\x8c\xe3\xe90\x9c\xfc\xe6\x0e'\x82#\xa4\x08 \xcc\xf8\xb5\x80=\xb489(, \x80\xeck\x1e\xad8\xc7H\xc1CiI6\x8b\xce[\xa0\xd6M\xdc\xc5\xf4\xd6\xacZ\x00\xef\xa1F\x0dQ\x12\x86\xa8\x8f`\xf3x{\x15\xa25<$\x9c}R\x7f\x88\xd8+s\xf5\xcfd@\xf4\xd5\xfc\x8bFKSA\xe1\xfdN\xb5L<\xbd \xd5O\xc9\xe3\x151<1|\xfbUY\xfc \"\x18\n\x1d\xa7\xe9H\xbe#\x8e \xc3\xe9\xb2\xd9\x11 \x9f\xfb\xb6\xe0>\xdam\xc9S\x97pje\xef\x87X,o\xe1-o\xe1-o\xe1\xc5\xa4\xb3Y\xde\xc2\x9b\x99\x9bfy\x0b/<\xef\xcc\xf2\x16\x9e/\x93\xcc\xf2\x16\x1e+\xf9\xb2\xc4l\xbc\x19b\xbe\x80\xb7\xf0t\x93*\xf2Y\xbc\xa9\x12|x\xff\x9a\x93\nM\xba\x91\xf6@\x1e\xaf\xab\x1b#y3Q\xf0V\xf4C\xbe\xd5\x87\xe5\xc9\\kq\xc5\xb3\xe2\xf4\x1bx\xdc.n\x14-+s\xe8\xfbV\x13+\xa9\xe8Z\x94\x98\x94\x8e\x04\x8b\xe2\xb3\x82\"mY\xc1\xa6\xc3T\xbc\xcfE,\xef\xcc%\xa0t\x1d\xb4\x96w\xe60\x98c\xe8\xf7\xcb;s\xbe\x1a\xcb;s\xe8\x04\xce\x81\x0bF\x1a8\n:\x18i'\x1bF\x18\xa1\xfd\xb0Ha\x84\x81\xe3\xe1\x85\x91\xc6\x8e\x89\x1aF\x9a{x\xec0\xc2\xc4 !\x88\x11\xee\x1e\x0bG\x8c\xb0\xf2\x00hbV\x96w\xe6\xe6\xbf3\x97\x13}\xcc\x8a\x89A\xe6-\xb9\x8d1\xb7X\xd2P\xc9\xb8\xfc\x05R\x19\xc5&\xb3\xb2\xc6\x10\xcac\xf5#\x1f|\xd6\xf1\xc8e\x94\x8e\x81G\xb7\x9f|\x10\x143+!\xfc&\"\x9aQZJ\xa6\x1c\xab\xf4\xad\xc7)\xdfa\xca\x8dtf%\xe0\xd0\xe0\xc7\xf5\xb2\x12\x8c\xee\xc5\xb1\xcf\xac\xb8\x10\xd0\xac\x04p\x1c\x08\xf5\xf5\xa3\xa1Yqc\xa2Y \xe0*\xbb\x1c\x1d(iV\\XiV\x02\xd8\x0e\x14f\x00n\x9a\x15+z\x9a\x15\xc6\x91\x0dC\xcd\x8a\x1bI\xcd\x8ao\xe3\xc9\x8a\xaaf\x05\xc5V\xb3bEX\xb3\x12>\x0c\x81\xfd\x89\x00b[\xe9\x99\x00mV\xdc\x9c$\x83\xb5q\xb9\x15\xb7$\x12\xb2\xcd\x8aO\x95'\xc2\xb7\x0d:\xaaw\x0e\x05q\xb3\x92\n\xe56\x08\xe1\xd0nVR\x01\xde\x06! \xf0\x1d\n\xf3fECn\xeb\x13\xde7,Z\xf59\x98*\x85\xa0\xbe\xbb\x9e\x9c\x17\xda\x02\xbfM\x83\x88\xb3b@\xb9\x1f\x8c\xe7\x10\xbc\x94\x15\xe4\na\x00rV2\xb2o\xd1g(+\xe0\x83bzH\xb9{,\xc1\x93\x1f\xbf\xcf\x123\x10\x8dM\xd7\xc9\xd9\xfb\x8d\x83S\xbd\x9dE'`(\x04,\x88\xb1\xc4Q\x88\x11s4\xf4K\xad\xee@\xb4\xf3\x0f\xed\xb8vV\x82\x98M\xc6\xb8\xa3\xe4\xca\xda;\x02\xb3\xcf?v\xec;+\x01V\x97g1\x01\x0eKNE\xc3\xb3\xe2\xc5\xc4\xb3rD\xee\xc3\xf4\xb7\x05\xdf\xcdJ\x10V\x9e\x95#t\xc4a%\xa7\x00\xeb\xbd\xc4\\2\xb0i6\x08\xeb:2@\xe1\xfa-\x80\xbd\x99#\x13'\xf6DM\x07\xf8\xb8\xa9\xdd2\xf0\xb0)\xf6'J$\xdc\n5\xe89\x83\xf9\x99\x8cR\x9d\xe7\x84\xdd*\x83\xcd\xa0sa\xd5\x80\x0e\xf7\xac~\x9c\xb2\xaaIc\x98\x1e\xc4\xc0\x0d\xecy\x88\xf6\x0c\"\x14\xd0o\x1f4\x9d\x95G\x90\x81\x15\xdc\x1eP\x1d\xbb\xda\x14+\x9b\x1c\xf6dP_C\x95q\x00)\x7f\xaf\x12\x078\xbeW\x89\x8a:`\x1a\xc8]t\xf1\x95\xe1~\x82Bo\xdb\xb4\xfc\x96\x02\xd26\xa7\x85\xbc \xcc\xff \xea-o\x08\xcf\xbd/\xa1\xd1\xeb\x977\x84A\xe7-\xe3\x9d\np\x99\x15\xcb\x1b\xc2\xcb\x1b\xc2\xcb\x1b\xc2R\xf1\xcb7\xfd\x0e\x07BlyC8\xed\xc6\x87}).o\x08\x8feyCxyC\x98\x95\x08sryCxyC\x18\xac\xab\x1c\xb9s4zy\\\xcf O\xd5Rn8\xc5^\x197\xbd\xfe\xe8P\xd9\x15\xd9\xe8\xc3\x1f\x8c%RR\xbb\xff\x8a@A\x0f\xc2O\x81\x9c]\xcbS\xe3\x87\x17?\xfc\xf5oW\xc5\xb7\xcf^n\xbf{\xf9\xec\xfb\x97?\x14\xcf\xfe\xf6\x97\xe2\xaf\xcf\xb6d]\xbc\xb8z\xfe\xf2\xc5\xb7\xe49\x83\xbc\n\xef\x80\x1c\x01\xe0\xe4\xf4\xd6_\xfc\xfe\xc75y\xfeG\xf1G\x7f\xf8\xcb\xcb\xfe\xf3\xcb\xcf/\xab\xea\xee\xe5\xe7\xf5\x0f\x7f\xf4\xdd\xef\x9f\xab\xdbO\xa4\x12<`!\xe9\xd8\x0eO\xc1\xe6\xa0.\xbf\xfc\xdb\xf3\xef\xb6\x7f\xbbZ?\xfb\xcb\xf3\xbf\xfc\xf5\xd9\xf7\xe4\xea\xe5\xb3\x1f^\xbe\xd8>\xfb\xf6\xc5\xb7/\xfe\xf2\xd7\x17\xebo\xc9Z\xeb\xb2\x1a\xaaF:\xcd>x\xf1\xfbgk\xb7\x7f\xe8~\xaf\xd67\xdfu\x9f?\xd5\xdf\x7f\xff\xdb\xcb\xe7\xbf\xfdq\xdd\xff\xad\xedn\xee~\xbf\xdf\xb6\xbf\xad[\x99\xc1K\n\xd9,j\xb6\xf5\x8e\x1d\x19\xf6\xa0B \xd1\x14U\xd7\xc8|pE\xbb\x99\x8e\xfb\xeb\xa6\xddD?\x86\xa3HW\xa2\xc1\xecr.\x14\xf6\xbb&\x13\xbd\xda\x8b\xdf\xbf\xb5J\xe4\xd3\xf7\xdfn~\xff\xf6\xb7\xcd\xddnS\xfcq\xf8\xf4\xc7\xba\xd8lnn\xfev\xbd;|\xb7[\xffA\xbe\xd3:\x11\xf0b\xa2\xb7\x13\x14\x98\xca\x97<\xfd\xf7\xe8\xdf\xa0}\xe9\x1b\xd8\x965\xf5k\x8ccN\xfd.\xfa\xee.\xe4\xcc\x06C0*\x8e\xe0\xf4G\x9d\xd5|\x99\x12x\x88\xd2\x7f\xbe\x17\xd1:\xee{R\x15=\xf3\xdd\x8c\x8e\xa3\xe9\xa2\xb6%\xfd\x03\xc5\xde\x1c\xafS\xbc\x81\xb0n\x89\x8f\xd9\xe8u\xc4\\\x89\xd6\x0e- 9|\x1d\x98\xebHr%\xe4PZ\xd56H\x08\xbfk\x1c\xf5\xae\x7f\x17\x7f\xbfX]\xbf\x1b#\x82)X\x1d\xbd\xac\xeb1\xd3\xe8MSm:}q=\xa1\x87\x07\xda-\xb2\xf9:\xc9\xb2\xd5x\xb2\xd6\xb6S\x00'\xa2 \xc8\xed\x80:\xc7\\p\xca\x8f\xecV\xf0\xbb70^$e\x1a\x13\xe0\xddn_Qhj\x07\xdd\xe6\xf6\xec\x9c\xc3\xe0\xca\xba'\xed\xb6Xcg\xa9\x81\xc6\xa1\xa3X\xe2v\xb4@\x08;\x8f\x0d\xe2-[v\xe2yM\x8fQz\xd7\xbd\xd7\xa4\x8f#\x82I\x0d#\x17\xa1\xc7\xa8\x00\xfdJ\xfe;vB\x1b\n\xcd|N\x84\xe4\x8a\xaaj>qllYK\x02\xd6j\xd2\xbbcH\xe8\x95u\x19\xbf(m\xbd&m\x9dw\xac\xf8P-\x9e\x0b\xd2\x9eP\x10\xc0\xcc\xcb\xd1\xae\xab\xd1\x01\x18\xe5l\xd7\xa2\x83.E\x07\x08\xc3}!:\xfa:t\xece\xe8\xb8\xab\xd0q\x17\xa1#\xafAG\\\x82\x8e\xbc\x02\x1du\x01:\xf6\xfas\xec\xe5\xe7\xa8\xab\xcfi\x17\x9f\xfdN2V2_z\x0e\xbe\xf2|\xe4\x0b\xcf\x0fs\xdd\xf9x\x97\x9d\x1f\xf9\xaa\xf3\x03^t~\xd0k\xce'p\xc9\xf9\x94\xaf8\x9f\xcc\x05\xe7\xc7\xb8\xde\xec\xbb\xdc\xec\x0eZ\xb0\xe2\x0b]\xb0\x92\xe3Z\xb3\xe7\x06T\xf2\x95f\xe4\x11%\x90P5\xc6\x1f\xfd\xfb\xcc%=H\x89\xfb\xce\xe3\x84\x85\xbe\xd9CE\xeeH\xc5M[\xd5\x91\x83)'f\xa1\x9d\xe9$w\x87\x8e\xdd\x94\x86\xa2\xbe\x17\x87\xb4N\xba\x12\x88\x86\xaczv\xb7\xb1\x90\xed>q\xb0a\xf4\xd5\xd3IY\xc3\xa1\x1e\xac~\xcb\xc5o\xc1MU\x0e'Dh\x14p\xb9\x81\x1f\x1cZ^\x15\xeb5jE\xcf0\xe9-v\xa6\x7f\x94\xce\xc7\xce\x8aC\xc8\xd0\x11\xda\xf3\xe2\xd0\xdf4m\xf9\x07\xd3B-Y\x93\xf2n\xd0\x87\xdbg\xb6\xc5\xa4\xbd\xed$_\x1d\x97\x9dm\xa2\xd0X\xc0\x8a\xce\x82\x95\xf5h1\xb7\x7f\xd32P\x15\xf80J,\x18\x811\x8cP\x12\x93\xe4\xa2/\xeaM\xd1nd\xa5\xcb\xb5?\xbb!\xb8+\xda[\xd2\x8e\xbf\xa1\x11\xe1\x96@w\xd8\xef\x9bvhu<\x92P~\xce\xb8\x13\xb9\xe8\xfb\xb6\xbc:\xf4\x04v\xc5\xbdp'#\xb4\xd67E}Mo\x82\xd3>q}(6\x8daF\xae\x87\xbd\xd8\x1a\xab\xa5\x87\xef\x15\xd5C\xab\xb6\xa9\xaa\xc3\xde6\x08\xc7\x0b\xdc\xfe\x93\xab\xc4\xa2\xaa\xa4wd\xa4\xa9C\x05[\xf6\xdd\xe4\x98\xa1\xab\x1f\x15\x88P4\n\x81\xaf:\xa1n\xe8E\x7f4x\xce\xc4^u\x0d\x90\xba\xb8\xaa\xd8\xf9\x8e\xe2G\xb8>\xfe;\x0d'3\x8685\xfc~\xbd\xf0@\xf3\xbe\xa8Bw\x8b\x83\xdd\x8c\x1c\xef\x86B\xdb4\xf2#8\xd4\x8a\x83uSUd-\x82\xaf\xa3O\xf5Sm\x1cG\xaf8\x08\xa6e\x01F\x84\xa1\x11\xdd>\xef\xe9\x89`\xa0:~\xbaF\x14\xc4\x898\xc9L\x1fb\xf2%\x9bQb\xcb\x93\x13\x16\x86\xa7\xb6\xc0w3\xc6\xc38\x8e\xc2\x0e\xdfM&*\x10\x00\xb1\xf60\x93 <\x84\x19?2\x1a\x13\xa4\xb9\xd6\xed\xb7TB\x16|\xda\xf5\x14h\x10\xf4\x03\xe6]\xcd\xa4\x03\xac\x17R2\xcdXt\x1bS\x9a\xf6\xa8\x88\xce\xf1\x18\x05\xe0\x0fR\x18M\x1c[\x8f\xf8;\x19\xa2a|4\xdc]\x94\xfe\x97\xb0\x8a\xb2\xf6UcE\xd5P\xc6\xf5\x11\x84\x9c\xb2H\xfd\xbd\x9f\xab\xc2|]\nUn>\xc18\xba\x900dQ]\xf0\xabD\x1f\xf3\xfa\xa8j\xe0\x96%S1-\xfe\x10\xdc<\xfa!\xc3\xbed*\x1e\x8b'\x10\xe7\x1d\x0b\xfe\xd1\x9c`\xdc\x92\xa988&cT \x08\xce\xc5\x86\xe7b\x03t\xd1!\xba\xa8 ]t\x98.2P\x17\x1f\xaa\x8b\x0f\xd6E\x86\xebR\x03va\x9a\x8f\x95\xccA\xbb\x88\xb0\xdd\xd1\x03w\x0f\x15\xba;f\xf0\xee\xd1\xc3w\x0f\x1a\xc0{\xe0\x10\xdeI\x04\xf1N;\x8cwB\x81\xbc\xc7 \xe5\xf9\x83y>W\xb6(>\x97\xb6(9Bz~[)=\xacg!\xb8d*V\xca\x92\xa9\xd8<\xf9,\x99\x8a\xa7\x12ph\x10\x83\x9c-\xc3\xee\x92\xa98\x8f\x1c\x97L\xc5K\xa6b\xa9,\x99\x8a\xa5\xe2S\xe5K\xa6\xe2%S\xf1)y\xa1\xf5\xa8\xe8ty\xd4\x1akS\xaf\x7f\x9a\xf6\x83$\xcb\x07I\xe4f\xb4\xe7 \xce\x8f\x9f;YwE\x99\xb3\xb2o\xd1g(+\xde\x08\xbe\x87\x94\xbb\xc7\xfc\x9f\xa7\xd1g\x89\x99\xe9\xbc$\xfd\x96\xa9\xdf92\xcb\xc9\xb4B\x82\x7fA\x8c%\x8eB\x8c\x98\x93\"}K\xa6b\xef\xf9G\xb0<\xc3\xea\xf2,&\xc8\x0b\xa2be\x94\xf4\x92\xa9X\x94\xd8\x8e8\xacd\x84\xb14m.\x13s\xc9\xc0\xa6\xd9 \xac\xeb\xc7@n\xb1\x92ed\xe2\xc4\x9e\xa8\xe9\x00\x1f7\xb5[F\x96\xcc\x14\xfb\x13%\x12n\x85\x1a\xf4\x9c\xc1\xfcLF\xa9\xces\xc2n\x95\xc1f\xd0\xb9\xb0j@\x87{v\xc9T\x1c\x95\xb7v\xc9Tl\xa7\x91\xc3\x9e\x0c\xeak\xa82\x0e \xe5\xefU\xe2\x00\xc7\xf7*QQ\x07L\x03\xb9\x8b.\xbe\x1e'S1\x0dC\xddK\xb9\xc2\xb4\x94\xc5z\x1a3K\x06\x9a\xbcH:\xd6\x88>\xee\x89\xbb\xc5\xd1\x02V\xdfL\x92\x9f`O\x8c\xf53\x80\x7f\xd0\xfbH\xfcf\x0bG\xbb\xa1\xa4\x90\xbbk\xe7U\xd7\xc0m\xdd|\xaa\xa1\x18&\xc0\x8f\xc3N\x8aF\xb2\x8e\xe9\x9fqK`\x02\xee\x89\x99\"\x9c}:bOF\xe7\xa1\xa4x\xda\xa7'4\xa9H\xd9\xdf\xc0\xb6\xacz\xd2\x92\x0d\xdc\xde\x89\xad\xa5'm\xd17\xad\x19\xa6\xe6X1\xb4\xfb\xce\x0e\xf0\x8ab\xa1)\xa6\x8e\xe0\x9c\x0d[u/\x16\xd9\xbd\x0d\x823\xb9R\x1b\x1a\xefo\xb6[\x1ea7ru\xbb\x82\x053\xcf\x7f\xd9rz\xf4j\xec\xc3@/P\xf9\x8cs\x9eK\x12%ySt7y\xf8\x19(Qf\xea\xe9\xde\xe28\x88V\x9c\xa0m\xf5CT\xfb\xae \x04\x0b^cq\x07\xc1\xdc\x93\xa1!\x9a\xf4\xec\xa9#(-J\xd3\xea\xd2^7\xbb]S\xd3vp\x98\x06\xcb\xc1t\xd4n\xb2&X\xd0\x9a\xa5lk%\x9f9M\"<\x86\xac\x87Ij\xbb\n\xc1\xca\x13F\xee\xeb\xc95O\xe5\xf2\xcd\xd8\xe5Q\x87\x94\xf5]s\x8b\x8cmY\xef\x0f\xfd\xc9ay]\xb3-h \xc2L\x17V~\x19\x86\x81\xe7\xe0d9\xc0\xa8\xce\xaa\xca\xfa\x16\xae\x8a\xf5-\xcd\x99{\xc3\xef\x1f\xfa0\x81\xc3\xe0\xe1\xa7\xe61k\x9f\xfd\xd8\x1c\xd8\xb5\x00\xa7F\xc8T\x04S=\xf1\x1dd\x825\xfd\xd8\xb4\xf0\xb6\xeb\x8b\xab\xaa\xecn\x1c\x10b\x10A\"\x1b\xfe\xc9\xa5\xbf\x02\xbb\x1d\xda#\\\xc1\x95dM1o\xf2\xf64t\xcfA\xec}\xdb\xec\x9bAC{\xfa6\xaa\xa5<\x1d\xa4Y\xf3\xf6\xa2\xed-=\xfd\xf7\xedaM#\xa2t3\xdd\x15mwc\xc1M\x01t}\xd1\x1f\\8\xf8\x009\xbe\x1b\xe1\x90\xe5\x96mNTQPU\"\xa6\x89`\xce\x03?\x03\xa6\x8b\x7f;\xd0x(\x83e\x0dCD\xe1+\xfb\x83\x1dY\x18$3?,\xfd\xc3\xdb\xd7\xbf~x\xb3z\xf7\xcb\xfb\x8f\x97\xab\x8b\xcb\xf3\xcb\x8f\x17A\x10d[\xdd\xf7\x1f~}\xff\xebEBE\xf6\x9bCaq\xa8t*\xc3\xe1\x1a\xcf+\x14\x97D=U%$\xad\x13f]\xd6\x14\xcd\xf8\xcd\xa1f\x06:\x9b_\xc3\x98;*y\x86\x04\x97\x9d\xf8\xab\x8e\xf1\x95ft\x0dE{U\xf6m\xd1\xdeO\x9a\x82fn\x187P6e\xe3yc\xbf\xe1\x9c\xb1\xdfp\xbeJ\xb6\xd0\x14\xcb`\xdf\x92\xbb\xb29t4\x81\xbd\xb6\x04'\x9c,\xca#_\xf4\x97m\xb1\xbee\xa7s\xb6\xf3\x8f&:\x11Z\xdegZ\xbbO3\x12Q\xc3~\x1b\xfa\xb0\xbe)\xc9\x1d\xcb\x12\xda\x1c\xfa\xa1\xa3M\xed;\xd1\x18\x7fb5O\xcejy\xa0=\xee\xbf\xf8\xfe\xd6\x8b\xac-\\\x90\xe3Te\xff\xfff:,\xe1\xb9\x17E\xf1\xe1\xae\xf2\xec(\x17\x94\x86\xe0[L\x89\xe9\xbc\x17\x98qD\x14\xdeg\xbet\xca\xfa\x1a\xba\x03M!\xf3t[\x94\xd5\xa1%O\x87Mg\xcf\xde*\x987&![\xcc\xc5\xc7\x9f\xa2t\xb5Y\xeb\xfd\xf9\x85\xfbn\x8f\xfa\xf9\xc5\xff\xff\xee}\xc4\xe7?\x9e\xbf\xfb)d\xe7\x89\xedG\xdc\x9ec\xa1\x9e\xd20(\xfb\x0c\x1c\xea\x8e\xd0\x93^\xc8\xa5\x1dS\xf0:\x07\xc3o\x8aZ&\xd2L\xa5\xb9\xfb\xd9d\xdb\x1elF\x18\xda\xd40hzS\xc3oRSc\x17\x863\\Wn\xf8 \x905z[R\x97\xe2\x86\xbd1\xb3+;\xfa\xb4\x0f\xd7\xb9M\x0b\x1bR\x15\xf7d3\xf1\x1a\xc3\xdc0Et\xe6\x86\xdf\xacr\x98\x1c\xec(\xc7\xc3:\xd4A^\xa2\xf8\x14\x063\xb8\x7fek\x9c\xd4\xebb\xdf\x1d\xaa\x91\x05\xb1ql\xe9\x96\xc8\x14\x89\xd8\x0f-\x04\x1d6\xaa\x9b\x17\xd6\xd6\x93\xeek\x98\x9eV\xa0o@4[!\n\xfe\xb2\x95\xe0\xa4\xb1\xbd\x9d\x02\xdc\xf8\x15\x1fN\xe9\x88DV\x1e\x8b\x12>\x8d\x8b\xc6:\x17B \x93\x03\x93\x9f\x1e;\xe3\xde1J\xcf\xe2\xd9\x14\x9e_\xad\x8e}\x94\xceG\xbfg\xb3\xa5\xd6\x10\x1d\xaa\xa2\xef\x8b\xf5\x0d\xa3>\"\xfa\x875\x82\x02q\xd5\x99\xcb\xe7\x17=\x87Yo\x85(\"\xe3\x1cp\x8945?t\xf2\x9f9\xa6V\xed\xd2\xe8\x0cH\n7\xab\xb5\xc3\xe3\xcc\xb6\x18\x8cE\xe8i\x91\x02\xa7\x9f#\xe3D\x1d\xdb\xb1\x86\x8c\xf9\x00X\xc3\xc1\x93\x18\x1f$\x0e\xac7\xe7\xc6\xc9\x8c_\xbb\xf8~l\x94#\xc6I\x1a,&\xa8\xbb\xec\x14fu\xb6\x18\x93X\x8d\xe9\xbf\xa4\xbf\xed\x8b\xb6\xd8\x91\x9e>\x0f\xc5\xc7\x8b\xb3\x90d\xda\xdd\x92\xfb\xc0\x9d\xc5ztTz\xf1\x7f\xf5y~KX2P~\x95\xa3%\xfd\xa1\xe5V\xf1\xfb\xe2\x9a\x88\x19yV\x93\xcf\xfdj\xf8\xb8o\xe0\x8a\\\x1b\x13\xfc\xf7aR\nP\xcf\xf0\xf1 \x14\x02\xbb\xa6\xeb\x81l\xb7\xe5\xba$u_\xdd\x9f\xc1\xaf\x83QA\x1f\x92\xdaB\xb3\xdd\xb2\xac\xce\x03\x1b\x1a\xc1\xee\xa69T\x9b\xc1\xfc\xd0\xde\x1ac\x95\"\xa5r0s\xd7\xb9\xd7?g\x8d\xdd8:\xech\xdef\xfe\x1bKfX\xd4\xf4f%\x85\xec\xde\x90\x9a\x0bR\xa3r\xa8\x8b\xbb\xa2\xac\x8a\xab\xca\xccQ\xf6\x8eR\xaf(\x9eS\x08h\xa0]\xc3\x81\x02\xd4nI\xb4\xb4\xf4&L\xe1U\xe5N\xcf\xc5\x99_v\xb4\x11\xa1\xc3\xfa\xa6/*\xe9 &\x90`t\x1e)\xf3\xad\x1fqb\x1a\xbd=\x85\x01\xe9\xe2\xdbBE\xb6=\x90\xdd\xbe\xbf\x87\x92gy\xe3\xa0L\x86_bS\x9a54H\xee\xea\x9e\xe2\x98\xa0\xd8\xefe\x99\xd0\x8cx+\xca\xe7\xf1\xf7\x15\xa91`\xef\xe8\xd1\x8bR\xed\x81\x80\x96\xda\xb9\xe8%\x89\xd0\x0f\xf9 \xab\x04\xb9\xb6\xd3\xc5S\xb0\x96\x84\xe9\xa8\x8f\x02\x85t\xc389\xa9\xae\x92\xb4\x98\xb1\xbc?\xbe\xeb\x8c\x11\xd0\xbaBO\x0b\xc3\x8eE\xd6\xbdX\x14\xd3*\x1a\x16\xca\x19\x9f\xf3\xe5u\xdd\xb4\x060A\xac\"\xbd\x19\xf3\xb5\xc1\x96\xdc\x91\xb6{\x00\x9b\x957\xa4\x0fT9\xcd\xe2\xa2%\xf8L\xd6(\x0d\xed0T54\xed\x86\xb4f6\xc1\x8b\xb2^\x93W\xb0n\xba]\xd3=\xeb6\xb7\xf0\xfc\xec\xfb\xef\xc6\x8f8\x92O\xd1\xdfLA\x8f\xbb!\xe3\x83\xec\xae\xc8f\xc3\xf8\xb8\x1el\x03\xb1\x03r<\x1d\xdb\x97F\x8d#\x91\x9b\xc6\xff\x0c\xder\x0f/.\x1f\xd4\x9a\x98\x8c\x16\xee\xe60m\x16\xb5\xde`\xb9p\x82\xd1\xf6\x0b\xdb\x95\x82\x0d\x98)j\x9d\x96;@\xd3\x8e\x8aT\xce\xd9;\xa7\xcdv\x04\xdft\xf0dh\xeak\xf4Z.\xcfA\xae\xfa\xdcO\xcd\xa3\x84^\xe5\xb6l\x14\x7f:\xd3\x9c\x15\xcc \x84#\x1a\x85`\x1b!p\x06\xac\x0c\x03\x11|\xceeg\x8c\xc1\xefsN7\x18\x11b\xf3\xacF\x9c;\xac\x1d\xab1 \x16\x83\x12B\xa5\x88\x18G\x10$\xc7<\x06&x\x8cL\xc8mh\x82\xd5\xd8\x04\xd4\xe0\x04\xdc\xe8\x84\xa3\xcb7\xdd\x08E\x88q#\x0c5Da\x961\x8a\x10\xd3\xccSp\x99\xa8\xe0\xd4\xb1\xe0\xd1\xb3\x10$\xc9\x19&\xab})Z\xccV\x98k\xba\"\xf4\xca\x1a5_a\x86 \x8b\xb5\xc2\x8cZ\xab\x19\x0b\xa8)\x0b6s\x16\x1e``\xf3\x99\xb7\x10d\xe2\x82\xdf\xcc\x05\xb0_ZI7w5B\x93\xf1\xab\xfd\x01\xb3\x80\xe3\\C\xe3\xbbK\xd4\xf7.\x1f\xa8&\xfcK\xb3\x15\x16\x89\xee\xe7\x8c\xb0\xd0\xc4\xf6\x1ad\xa2%{\x87\xc6M\x9c\xeb\xd1q?\x87}\xd1\xf1[H\xd2\xb0\x9c\xb1\xbfkD\xe8&\xef\xdd\xe1\xdfq\xa5I\xc7\x94jQ\xc4\x0d\xae\xa5X7L\\\x00\xeb1>\xce\xc1a\x9b\x83\xa3\xaa\xb0\xed(\x93^2\xf8\x97\x05%)\x1e}\x12\xd2;\x8b\xa4\x7fJ\x1f\xa9\xe7 V:8\xd4\xcc\xc0\xdc@3\xc8\xe1S\xd9\xb1q\x0c;\xa0\x85\xbb\x95\xd5\x9a\x9c\\\x90\x8b\xf9\xe7\xee\xfa|\xb3y\xcd!\x03\x17{\xb2\xbelh\xca\xfb\xe1\x9f\x9e\x13\x1b\xde\x8b \x8a\xceNM\x84\xbe\xb1S\x8a\xe9\x1e\xad\xf4\xa6\xe8\x8bsz\x93\xd5\xd3-}\x169i\x18\x1d\xa1\xef\xb4m\xa8B\xa5\x17\xd6\x8b\xb5\x94\xe7\xea|\xbd>\xe7\xc8\xe7\xbe\x19#e~\xde\xe9\xd0\xa6\xb3\xadTwq\xcc\x9e\x7f\x9b\xb8\x1cT\xa2P\x0c~n\xff\xa3\xac7\xbf^\xfc\xd4\xac\x8b\xbe\xf1qk\xea\xc7\x8a\xd5\xd3O\xfaAz\x95\xf2\x1d\xa8?\xb8\x9c\xc4\x9b\x1a\x02\x89>\x88\x84\xd4\x9b}S\xd6t\xa3\x1aHn\xe0J\xb6\xa49\x87\xabC[\xc6\xb5\xc5+N\xf4\x0fm)}G\xeau{O\x97Q\xc4\xde\xc0I\xd3\x9ew\x12 \xaa\xed\xf5L\xbbv\xab\xe2\xcd\xf4\xb4\x07\x1f8.q\xfa\xb8\x14\x99\xf2#=\xe5\xef\xb2IO\xd6\x15\xac\xf5'\x129\xe9\x05;\xce\x83\xe9O)F9\xc8J\xf4\xd0\x96\xc0\x10C_ur\x15\x99\x1b$\x08'Owt\xfe\xb9\xd5\xe7\xa0c\x94j\x9cZ\xa8ryC*\xd2\x13YE\xfd\xd86\xbb\xd9\n4\x90l\xa8\x16\xf5\x90\xa3.=\xfeyZ\xa7GXj\xbe\x0e\x1b$\xd3;;\x81f\xe3\x86u\xd1eH[\x8b.{\x10]f\x99\x81q\x8b`\xac\x1e9\xf1\x19\x0et\xdeZVi\xf8\xb5\xb0\\+&\xc6\xa05\x98Q\x1b9\x08\xc6\x0d\x03B(r@f\x1b\xb0N2\xa6E\xd86;\xf6rU\x1e3Vj=\xd1\x92\xb5Q\xf0\xb0\xae\xdb\xb3\x9c`,\xd3\xf3\xe6\x91B\"t!\xb0g\xc7R\xd6\xc1\xb8\xadgZ\x06vzq\xab\xc0\xa4\x13\xbc\x08~.\xaf\xdb\xa2'\xd3)7\xb5GVB\xf8<\xda\xd1\xcf\x87\x89$<\xc1tBqzR4\xd0\xc1z\xb3)\xb7\xf7\x8b%\x81\xb4\xb5X\x12\x0fbIXf`\xe8\xe2\xd5\xaa\x07\xaf\xd9\x0b\xd2\x9f\xb3\xc94l8\xa9\x0b\x16\xa7\x82\xaf\xd6\x8e\xf4\xc3Ze/v\x0e\xff\xd8\x0cz\x87\xae^N\x90*\xfdA\x90|\x96Sv\x9d\xbd\xf8H_\xff\x94\xdck\xa9\x1d\xb1\x12\xc2\xfb\"\xfa`W;\x84B\x8ai\xec\xafe~U\xda;\x8f:\xfag[\xa6\x9d\xdcL\x0d\xe3\xb9\x82j_\\\xf8u\xd3\x98\x9c\x01\xa0\xc62\x94\x0b\xa9j6.\x96\xf2\x94\x86\x1a\x9aVYX\xec\xd5\xdf$/\xbe\xfb\xa2\xa9U7\x85y\xf3\x0d\xe7u\xf4\x05Rg\xc2\x00\xe4\xdah\xd0e\xd1c\xf7*\xe1\x12\xa8\xebr_\xc0\xa5\xb8\x07\xee\x9f\xfd\xbe\x9f\xf9\x17\x8dV\xc4\xf5M\xdf\xa5Mk7cz\x93x-3\x9c\xef\x88\xe1\x89\xe1;\xfa\x92e\xc0 z6_\xbf\xbe\x0d\xdd\x87\xed\x94\x82\xb7dJ\"\xe8lo\xea=~\xc9)B\xcd\xab5\xa2\xf5;\xaf/\x114\xf4\xf9,%>\xb2\x178\xd1\xc2\xf4\xc0H\xd5\xaa\xa5\xd5\xebng\xa8\xc0\x8e\xa0\x80u\xe2NE;}\x8d\xf3g\xcb\x82\x94\x8d\xc7\x88^\xad\xb9N\xb21\x1b\x95\xbd\xc4\xc9n\xaa\xbe\xd6\xaeV\xfa\xf42\xce\xc3\x98,\xca\xbc\x83\xe6V\xb2JE\x88Ir\x87\xc3RP\xee,k\x1b\x9c0K\xfc\x8a5\xb8\xe4\nQXK\xf9^-\xba\xda\x95K\xd3g6\xc1\xd9\xb2sd\xe62h\xe9\x8b\x8f\xad\xcc\xda\x16?\x1c\x81\xe1\x18=\xe0f\x1c\xcf_\xe1\xe4\x17a)x\x85\xb9\x99I\x10\x1e\xc2\x8c\xdf\x9c\xc1\x04\x19b\xb0\xc4\xfa\xee\xa5J\x91.K\xa9\xe6\xbc\xd3(W\x1a\xb1gQ\xa4Z\xb4\xa5b\x90{\xa8s\xa8\xca|\xe0\xc6\x11\xb6\xbb\x19J^m\xcbg\xddD\x9e@\xb5Q8\x9e\xf9\x13q\xd8\xd4\xaa8\xd8M>\xbb\x84\xb1\xee\x19\x8a\x8c\x07\xcb\xe0\x1e\x1f\xcb\xa6\xf2u5\xca\xd2r\x92\xb2u.\xd5\x02sr\x1e\xbak\x04s\xecq\xbd\x81\x97;\xdc\x05\x17\x96\xf1\x0d\x8ci\x14\x97\xeb-\xc1\x8es;\xdc\xc0\xb7w\x86\x1b\x1e\x86\xa0 \xc5\x01\x87\xd0\xe8Br\xb7\x059\xe2\xe0\x81{\x1b\xa2+\x03\xc8x\xfb\xea\xd3\xa2\xf0\xd0\xfd\x9e\xa1W!\xceig6?\xd7Z\x0d\xe8a\xa2R2 \xf9\xfa\x920\x94\xb1}\xf1[\xc2^\x12\xc9\xce\xbd\x0c(\x11\x1b\x9d8\xc7^\x08R\xc1v\xc76\xc2bVO\xfb\xb1\xb6\xb2\x91\x1d(\xafS\x0f?\xf0\xcf\xb4J\xd2\x0f\xf9\xce\x03~&\xaeB\xf43z\x1c\xf5\x1e\xe8s18\xf7\x10o;\xc0\x87Yf\x19\x0e\xee\xd1\x02z\xd0\xc3z$\xbch\xaa\x93rT\x8f\x06\x17\xd94\x0en\xb9\xfa\xd4\x8eb\xae\xc6\xea\x1e\x9d\x9a\xef\x98\x9eA\x13\xa1\xf6j\x96u\xe5\xb3E\xb1\x1eZ5\x94\xcb\xde\xcc\xc9m\x84\xaeBL\x0b\x95\x94\xf4\xbf\x88\x95\x19\xc6\xb4\xc5\xc5miZ\xd5j\xd3\xcfS\xbf4j\xa1\xbd\x9c\xa7\xf2\xa2\xac\xbc ~\"\xe4\x1c,N\xbf^\x8c\x19\x8a`}9\xdf^\x9b\x01gd\xf5Y&\xb8x\xf59\xa64\x0d\xd7\x9dj\x95Y\x8a\x93g\xae\xcc\xac,c\xb3\xbe\x86-c)\xfb\xabUM\xaa\xb9[\xcfp\xa9\x1dC/\xea\xd4=Jq\xfc\xdc\xc2b\xb4\x8d\x12\xcd\xa6i\xc7H\xbfD\xb3\xcb\xff\x99\x9daS\xd7\xe0\x8d\x8e}\x90\x7fsz5\xdc}J\xd6\xd5R\xfd0E\xedd#B\x9eab\x8b\xd7\xcf\xb8\xbc\xd1\x93G\xac\xdbR\xa9hwW2\xdb' \xd2\x8c\xcb2\xc9E)X\x8b\xf4t\x84;\xadD\x03\xf1\xa7P\xf0\x9dD!?\x97aJ\x8d\x7fle\xd6\xa6\xd4\xe0\x08\x0cG\xa97'\xe3s\x9dw\x13\x950\x85\xe0d&Ax\x0f{zU\xcd\xa0\xd0\xf3+\xdf\xb6#N\xb0\xbc\xca\xbb\xcd\xebf\xb7ojR\x8b7\xf5Cl.D\x94\xa8\x08\xb1\x04\x14\x16 r\xe9\x89\xae\xd1\xaf\x8c\xe4\xe7\xff\x1e\x92\xa5\xff\xdf\x15.\xd5\xd9\x17\xc9%2\xe9\x84)\x88\xf3{\xad\xf1K\x7f\x0e\xc8\xf9?\xf2\x8c\xee[\xe1\\k\xbbTa\xc8\x96?N\xc1\xd3\x95\xd1\x9c9\xb4\x8a)\xea\x90\xbc\xff\xff\xee\x98\xd1b\x82\xbd\x99&\xd8dioKRm\xa6\x8a/g\xab\xffJY\xab\x1fH\xd7Tw\xf1\x91\xc6B\xcfN\xeb\xda\xe5\x0b\xb1\x87\xaf\x95\x97C\x94{\xa9T4-\xe3F\xcaS4 /U[*\"\xfd'\xcfL\xcb\xae\x92w\xe5u]\xf4\x87\x96\x8a\xb5%\xbf\x1f\xca\x96\xe5S+6\x1b\xe8\x0eW\xcf\xe8\xd4@\x0c_Lt!C\xc3\xab\x04\x8d\x0b\xcd\x0b\xf8S\xd3\xdc\x1e\xe2s-\xabX.V!%\xc9\xb2\x18\xbf\x9a\xd5\x16k\xb2\x83\xab\xe6Po\xa0\xb8\x1e\xcc\xa1\x1e\n5\xa32\xaf\x1e\x93\xb3o3]\xd1\xc5\x92\xf7Z3\xf6Fh\xf8%[\xdf\x92\xad\x0f\xac\xf6\xbe}\xd1E\xackN\xcb\xb9\xbayf\xab_\x8a\x9dO\xe9*\x8c\"\xd5\x14cH\xa4\xbe\xfa\x85\xab\xd1\x89U;\x1f\xaf[R\xf4\xe4C\xd3\xf4 \xdc\xe0\x95G\x9e\x06~\xd4OB\xb9b\xf9%\x1282+\x1a\x12\x9a> \xe5\x86]\x98O\xe0\xc6\xachp3}\x12\xc4\x0d\xa3\xb5n\xda\xcd\xac}\x00W\xf1\xd4G@\x95\xfaP\x87\xff5j\x93wm\xe8\x9b\x9c\x1b:o\xefS\xc2V\xfeo\x80h\xaaI\xae\xcc\xe9\xd0\xf5\xedaMI\x1d\xb8\xc2\xbe*\xeb\x0d\xcfBqS\xee\x07\xe5U\xb0\xee\xdd\x94\xa4-\xda\xf5\x0d\xd5\xf1\xe2\xb8\xbfn\xaa\x8a\xac\x85\x87\xb1\x10\x17\xfc5gVw\xbd%\xa4\x1b\xc6\xf5uQ\xad\x0fU\xd1\x93\xcb\xcf?\x12\xd2\xc5>\x0c\xd5\x7f^Q\xcf\xb1g\x94\xd0\xfdL\x99\xb4\x82\x90Px}[\xd4]\xc1:\xd27\xd0\x95;\xca\xa5\xd8;xf\xed\xd5U\xd1\x91\xd5\x86\xd4\xcd\xce\xc3\x81}\xe34I\x01\xcf\xf5L\x1b&,\x10\xc0\xfe@\x7f\x1d\xb4\xefu\xd1\xc1 B\x89N\xb9\x1d\x0ciZ\x01\xc9\xff]\xdf\x14\xdd\x8d\xe0\xfe\xba\xe8V\xc5\xe6\xb7C\xd7\x0f\x87\x0b\x9ds\xb6K\x19\xb2\xdbVM\xd1\xeb\xf3P\xd9\xf1T\xb2B\x92\xd2/\xdbb\xdd7-7 v\x87\xaa/\xf7UI&SJ\x0dk\x90\xae/wEO\xa6\xd4\xcfW\xcc.\xe8?\x8b\xf1\x10W2\x14\xe9\xe2Sj\xda\xc9l\xaf\x838\x82\xde\x8e\xf9\x1ai\x9c\x16\x9bM\xc9L\xbc\xd5@2\xc9N5\xa29\xb6X\x8e61e\x1a\x88}S\xec\x06+#\xf0s\x97!\xf8\xba)%\xa3\x16\xfa\xe6\x96\xd4\"3\x0ceIX\xb6\xc5`F\xd7\xbca-?\xfa/\xbf^\xbe}\x05\x97\xc3\x04\xa2\x7ff~G\xaa\xa4jxW\xf7<[M9\x1c\x85\xf9\x11\xf9\x86\xc0\xfa\xd0\xf5\xcd\x8e\x8f\xa2Bo\xd4\x91\x92\x86\xbc\xba\x87\xeb\xe6\xba\xd9\xb7M\xdf\x9c\xe9S[\x1b)\x96\x8a|b\xa7\xd9\xc2\xba)k\x91\x06\\d\x9e\xa5\xfd\xdau\xd7\xf2\xea\xa4v\xdb2\xdc':\xdc\xf68\xed4n\xe3\xe03;~\x9a\x02\xf4\x8f5!\x1b\xae\x97\xb5\xadCy\xcaY\xcc\n\xf8\xff\x84\xfa\xfeZ\x96A\xdd\xf4\xdc\x90\xe0\x7f\x05\xfa\n.W7\xd2\x03>\xdb\xaa\xe1;\xc0\xbe-\xd7\xc2\xf1\xc7<\x82\xa2[BwnV\xd7E\xe8\xe6x@=\xc5\n\xa5Q\xa7\x8f\x02\x18~\xb4\xf7\x9f\x12s+\xe7\xe0sF\x90z\xfe\xb9\xbb\xfe\x91\x84k\xe3]w\xbd\x1a>[\x1d\xda\xca)%U\x19\xbc\xd2\x85\xa4L\x1b\xf5[\xfa\x8e\xca\xbe\xb8\x87\xb2\xa6/\x82\x0f\xab\xe5 \\\x15]\xb9.\xaa\xea\x1e\nxC\xb7\xf6aq\x9cS\xb1\xca\xaf.\xb3_\xc4[,\x7f\x90\xb6\xf9Zc3\xe8\xf8\x8fh\x06\xcbB\xc7\xb4B\xa4A\x93A\x1f\xe4\xd5\x06\xa1\xba\xa0%\xebr_\"\x16\x91\xd2\xf5\xf1\xab\xc1b+\xbb\x15Mt\x96\x10\x8dARC\xfeH\xc6\x85\xb0nZ\xfa\x14\xce'\xf6\xb2V\xdfI\xe9\xda\xe8qe\xcc\xc3&\xe4V\xf6\xb0n\xea\xae\xecz\x1a\x12\xd86\x87v\xd0 }'>xqFk\x0ej\x88\xae\xaeC[=\x85\xf2\x8c\x9c\xc17\xec5\x8c\xb3\xab\xa2\xbe=\xbb{qE\xfa\x82'>\xab\xc7Wv\xbe=\x1b\xdd\xe7\xd3\x04\xa7\xfa\xec \x9f\x9d\x83\xbd_\xdf\xb3!\x1e5\xdbwg\x93Ok\x94\x1c\xd7\x9c\xc3*`=\xfa\x17.\xd4\x7f *\xdf\x9f\xb1GAD\xfd\xb2\x83\x0dYWE\xcb\x1e\x1b\xbbg\xe6\xee\xf0wr\xc7\x145%\x03\x8c\x8c\x88\x90l\x890/\x9f<\x7f\xf6\xe2\xf9\xd3\xe7\xcf\x9f\x7fm\xd5$\x91aI\xaa\x94\x07\x15\xb9\xa2J\xd9\xad!\x86q\xea\x8b\xba\x1f\xad\xfcQ\xcd\xb3\x0d\x82>\xe1\"L|\xe8n\x8aa\xea\xb2\x13\x00]G\xbbN^\xb2S\xb7\x16\x9d\x90]'\xd0s\xd3jO\xda\xd5\xa1\xdb\xacv\xa5{\x9b\xf0m\xa6\xdc\x0d8\xd0\x84=i\xe1\xd0m`WVt\xa3[7\xf5\xdd0J\xf55\xfdY\x9c\xd98 \xf6W\x8a\x10\xd8\x92\xb0#'os\xaaI\xd7\xdcx\xc2\x94O\x95T\x8c\x9c\x81\xc9_\x91\x16'\xe5k\x08\x0f\x95J\x0b\x8cn\xea\xe7U\xc5\xf4^\xfcIj\xd8\xbb\x8foS\xe3\x16\x82L\n3\xad-\xd6\x02\xaffq\x16\xcf\xb0\x1c\x0cJ\xdc\x92p\xda\x0f\xacX\xe4\x01N\x88\xa0\xe5\xac\x01.\xa1\x80\xf5\xcc\xe1\xa9\xe6\x06R\xce\xd0-\x06\xa59*\xc6 \xc6U\x8e\xf1{\xa8\xe6a\x05\xb1IX\xb1\n\xccg\x9f\xc8\xd5\xb1\xd8\xb5=z\x8dO\xdc\xb9v\x0b+^\xeb\x85\x95y6\x0c+\x89\x96\x0c+y\xec\x19Vf[5\n5n\xe1\xa8\xb6\x0d,\xa1\xc8%\x14\xc9~?\xb5P$gTy\xbbi|\x8e\xb5\xa8\xaaa\x91\x7f\xd51E\xce|0\xe6c\xc6;\xb7u\xf1%b\xb1\xac\x07 p\x0c\xee\xcc\x03\x85F\x8d;\xaa\x90\xa9\x18u\x8f\xc0b\"8vz\x9by`\xad\xe26\x0dr\x19\x06\x19\xcd\x02\xdc(\x881 \x1c\x07\x12\x98\xa72\xd2\x0f'\xe0=\xa0\x80\x8b\xb5\xbc\x07\x95#\x01\xc7Z\xf2\xa9h7\x83j9\xa7\xae\xcd7\xa4\"\xd7E\x1f\xaeU\xb8\xcd\xb1\xe2\x9e\xe1\xb4s\xa4\x1ah\xe7V\xcc\xa4\xcb\xbb\x03}\xdbd{\xa8`\xc3\xf9\x1b\xf7\x18\x8e0\xcc\xd9\xbe\x81Yt\xb6\xcf\xfb\xcf\xff@o\x8ai\xeb\xdd\xbe\x9cEW\xf9\x02\x1c_\xfa=t\xa4\x85\xdd\xa1\xeb\xe1\xa6\xb8\x1b\xd6\\\xdb\xd2]yl^\x18\x9e\x12\xad\xbb\xa2*7E\xdf\xb4I\xfa9|b\x07\xbaM\xc4\xb8\xa4\xc9\x85\x0f\xc1\x9fQ.\xd2z\xb9#\xab\xae/n\xc9\xa0\xf7\xd6\xa4\xee\xcb\xca\x87\xec@\x17\xcaT[y\xe2\xfc\x8aT\xcd'*\xb6Q\x04_\x0dV\xf6'\xd2B[\xd4\xb7e}\x8d-\xa2\xb9L\xf1\xa1\x9b\xcb\x14>9T%%?\xea\xc1\xd4)\xff\x9d#\x1c\x86\x0dp0\xda\xc6\xdd\x87T\xe5uyUVe/\\)\xeb\xb6\xecI[\xea\x8fL\xe8j\xf1\xb2-\xean+\xdej9=\xb5\xd8s\xfe\x1eK-\x9a\xed\xe7P\x8bS\xe5I\x13\x90\xf1m\x90I\x0f\x14\xeb\xb6\xe9:j]\x8f\xb3\xaa\x93\xe3MJ\x0coKZ\x98 0W\x84\xbdV\x9ex1>\xb3~p\xcdz1\x07\xf5Y\xafw*\xdf\xac\xffGs\xba\x86\xc0]\xf3xF\x80\xda\xf6I\xce\xf4\x81\xc5\x93\x9d\xe5\xd2\xa6\xbc\x1a\xf1R\xbe;\x91\xee\x10\x84\"\xd5\xf7MW\x0e\x1b\xd9\x04\xc6j\xd9&4\x9cm\xf6\x0c\x9c\xd57\xcc\xd9\xcf\x0e\x8d\xc50\xf1'(\xd6 =\xb9SL\x8e\xf4\xb8\xb9nj\xe6\\S\\xEK/\x00<\xd9\xd2{\x1fl\x83k\xea\xea~\xfa|\xf4$2\x9b\x85\x0d\x10\xc5&\x8e\xb2\x90\xe8\x0d\x7f\xfd\xfa\x0c\xce\xc5\xebB[\xea\xe4\x16\x0e\x19:\x15(\xf7\xc5\x8e(lt\xd0\xd4.H\xf4\xb4\xaeu-r\xd7\xb0\x87\x98\xf2\xe9\x90\xd7UQ\xee\xc8\xe6\x03\xfd\xe1=i\xcbf\xf3\x86\xf4\x858\xda\x05\xa8\x94\xf5@a\xb0C\xcaF}\x07\"a\x82PZ\xc0hM R\x19\x16\x86M\x82Y\xe4\xd9)S\xcc\xa6\xf5\xfa\xb0\xe3\xf8\x16z\xcc\x94\xda\xc6\xba\xc6d\xf6\n\xa7\xc9\xfe\xd8\xd9\x08=\xee:\xfe\x13\x06G\xf1\x85c\x9d\xcc\xa0%\xb9h\xc9u\xd1\xd2\xa7\xda\x8bz\x02\n\x7f\xc5\xa7\x06\xa76\xf4\x98\x8d+C\xd8)\x03k]P?w\xd7\x94\x8d\xf3\xaab|\xc4\xfb\x00\xd9|g$W\xb4Q}\xd6\xe7\x8d3.\xd8\xbd\xa9\x84M?\xa3k\xaa\x16X\xb3y\xc8\xa6MUq\xbd\\\x11e\x06\xd1\x87\xe7\x86\xbf\xee\xdb\xe6\xba-v\xa3\xad\xc2\xb4\xce\x86N\xdc#G\x98\xf9\x1c\xe3\x1c yW\xac\xb3\xc0\xe9\xd4\xd3\x84\xc3\x17\x11o\x05\x8c41\x8e\xe9\xce\xbf\xc0{\x05K\x9c8n\xc2\x1b\xc4\xf2\xc4\x89\xf9t\x17C\xc8\xf7Kc\x02\xb3\x82McV\x8c\xc9,W@\x07\xdf=\xfc\x0e\xd3D.\xce\xc9\x00\xbe\x89\xce\x8a\xd3|Q>DM\x19\xe5\x8b|\xec\xc4\x9a;rq\x9a>\xd6\xb6\xbcf\x90R\xd35\xb0\xe0\x1d\\p\xadpV\xbc\xb2t\xadvV\xbc$|+\x9f\x95|\xeb\x9f\x95$-`\xa1\xa5n\x86\x96\x8f\xe24\x02\x04 &\xc9\\C\xe8\x18\x06\x9c\xdft\x93\x8b\xc2\xa8\xb2\x84\xb9\n\x93+\xb8z\xc5;\xc2\xb69\xda\xb9\xa9;J\x08\x88\xcdy{\xb7x\x87\xbe\xea\xac\xfdQ\xb7T\x8bQ\xc2\xd9\xe7)\xdd\x8a5\x1f\xb6q\x91\xb6\xcd\xceB\n\xc1\x07\xd3\xfeh\x16\xad=\xb6E\xc58\xf4Lm\xac\xaa8A\xb5\xd1\x8eg\xa0r\x1a\xd4\xa9\xd6\xb4\xd3\x9c\n:\x90y\xac$\xab\x8aph\xeap\xfb\xc8g\x1dY\xb5\xe8\x12\x1c\x8f\xb1\x85\xe6\x07\xc7#\xac \x9b\x0d\x84Z@\x89\xc6o\x80\xed\xe3\xd9\xdb\xbc\x86F\xa0\xd5\xe3\xb3y2\xb1\x91n\xed\x04\xda:\xa9\x96\x8e\xc7\xce\xf1Y9N\x1b\xc7k\x9e\xb8\xed\x1bOu\xff\x16\x9eo-\xb3\x92qE\xb3\xe2\xb2jbV7\x04\x88#\xc9\x9e\x91\xac\x17\x83`\xbc5\x13a\xcb\xd8{3\xc7\x8e\x99l\x97\x7f\xf3\xf7\xe3!L\x0fjypZ\xdaF\xeb1:X\x12\x0dY\x16\xd1\xb6G\xaa;\xdcm\x1e\x8ca\x92kR\x93V\x00*\xd6\x03\xbfeS\xdb\xc5h\xef\x90C\x9a\x94\xea0\xb4\x9c\x0fN/L\x86o\xebM\x8c\x00\x11\xa6m\x14\xec\x1c\x93z\x93\xc6\xef\x7f\x1f\x8a\x8a\xa5\xb8d\x01\x98\xe0Q\xde(p,\xb4\x12^\x11\\\x11Y\xc8aZ\xc6\xc1\xb4\xc0\x15\xa5\x85,\xfcD\xc1\xb6 ,rk\xb4b\xe8\xe6\x9c\x10.\xc0\xe1Jp\xa2\xb6x\x00\xc4\x0b\xfc\xf2\xcb\x08\xf5\x82/L~\xa1P0\x07\x0d\xafx\x931b\x1a!\x0d1\x061\xa81\x98\xc1\x7f2\x9c\xcc\xc7\xbf\xbd\xddy 3\x89\x90\x144W\xc2\xe5C\xe9\x15D\x19|1\x9a\xdd@z\xc1\xe3jv\x9c\x9f\x9c\x9a=\x1e\x9b\xa3\x11\x92\x90:\x10\x89D\x83\x93T]\xbe\xa5\x93\x8aT\x0b\\:w\xcd\x97g\x10)\x901x\xdc%c\xf2r\xf2\xcb%\x00\xce\x06'\xb9T|P7G\xc5\x90\xcb-\xc6(\xcc\x85\xbe\xe9c!\xc9\xdd\x01\x7f\x83y\x108\xdb\x04H\x85\xc1\x81\n\x85\x83\x00\x95\x95\x05\x12\x07\x1a,NkV?\x05\n#\xa6\xa9i\xd7vE}?\xce\xf1\xfb=[\xa2\xe8q\xd2\xe6Ea\xd3*\xe0X\xe9\x08;\xe4\xf3%\x8c\x96\x8e\x17s\x14\xa4\xbc\x17\xecZ\x88C10T\xc0\xc4\x91\x17\xf1\xe3 \x0b84\xaa\xd3\x0f\x1f\x10\np\x85\x01f6\x9b\xe6\xfa\x0fp\xfb\xa7\xb8\xfc\xad\xdb\xdb\x02V\x8aZ^\x06\xb1\x1c`%W\xd7\xb3\xba\xef\xe3\\\xf7\x81n{\x9c\xfb\xac\xee\xfapW\xbd\x1dN\xced\xc7\xd8zS\x0eS\xf0\xea\xd0\xc78T\xadJ*y\xdf\x1b\xa6\xa0\xa6\x9c\x84\xf4\x8f\xb1\xcd^\xd2\xf7?\xc5\x8d|m\xd3\xe5\xde3\x9a.\x99\xfe\xe1\x8aTM}\xdd\xc14U\xe5\xdd\xb8[\xed\x9b\xa6Zm\x9bv%\xcb\xe5\x95\xbd\xe5\xee\xb0\xa3iD\xaa\x8a6\x7f\xdd\x16\x83\x11\xae\xe8/\xda<>\x17\x1fs\xb7\x97{\xec\xe8\xe0\xb6\xac\x8b\x8a\xbe\xe2\xb1.\xbe\x90\x9e%\xdca\xf0\xcc/=\x97\x86\x18f\xbe\x82\x9d\xb2P\x16\x18\xa97\xe99\xc1\x15\xb6\xcea[\x15\xd7\x93\x85>\xe8\x98\x92\xad\x01e\xf1\x8d\xbc\xf1\x05pSt@\xb9p\xa99\xa7jy\xca\xe8\x95\x9dx@\x17\xb8\x87\x9a\xd4t\x19\x92;\xd2\xde3&8\xb9I \x96`\x10i\xef\x9dMv\xffq\xff\xeeMtD\x101:V\x1bCG\xa2\xc4p\x82\x06\xd1\xec\xf0$\x87\xee\x84\x07@G\xcd\xd5\xa5\x10\xabOQ\x0e\xe6\xe9Tp\xd9\x85\x8f\xe8\xf6\xc0\xf5-d\xd4\xb9p\xa2=\xb7\x9fF\xbc\x136`\xbe\xce\xd1\xcd\xe0\xd1\xcf`\xd5\xd1\xe0\xd0\xd3p$]m\xd0U\x8c\xea\xf9:[\x17\x8a\",\xdd\xbf\x16\xa9\xb4yr\x7fa%\xd3\x1cS\xa3\x7f\xdbIf\xc6\xae\x91}\xc7\xf82\x1d\x061;\x8cw\x8f\xc9\xc8\xc6\xdc\x9d&a\xaf9\xcans\xfa~\x08\xfb\xde\x93w\xf79}I\xcc\xf4\x8c\x05\xcd\xeb\xb9;\x92\x7fOr\xedJ\xee}\xc9\xef\x91\x9a\xb1Y\x19\xb4\xb4\xcd+\xc0'\x94k\x03\xc3\xb60\x83\x81\x9f\xca\xae\x17k\xdf\xd9:\x9f\xd0\x1dM\\X2g\xa7\xa0y\x94\xb4\x9bl\xdfJ:N/y7\x97\xbc\x9b\x90f\xb0\xa1\xc6Z\xc5\x17 \x9f\xa7aF[\xe7\xb4\xdaP[\xf1\x9cyG\xa3m\xb6\xb0\xf7\xdb\x0c%=\xbd\xe2\xc6\x15 Wc\".o\x9a\x1c\xdc0\xe2\x1f\xac\xba\x1e\x81\xa1\xe6\xb5\x07O\xca\x14c\xfa4\xc4\x12\xc3/\x96+m\xa6\xde\xe17.\xe1\xf1\xd6O\xdc\xea`\xfb\xf90c\x0e\xc8\xf1[7H\xe9gb ]z\x06\xaf\x7f:\x7f\xf7\xf3\xea\xe2\xf2\xfc\xf2\xe3\xc5\xea\xe3/\x17\xef\xdf\xbe~\xf7\xe3\xbb\xb7o\xfc\x9f\xd2\xff\x9e\xff\xc7Oo}\x9f\xc6}\xe8o\xfa\xed\xffy\xff\xee\x83\xf1\x19\x7f\xec\xeeUh\x8f\xb8\x1e\xa4\xea\xea\x82\x89\x96\xefYt\x05\x8fq\"\x1axBF\xed\x88\xabN\xb1\xa7FE\xe4Zh.\xfb\x89i\xd5s\xa6\x9ed\xec\xbe\x14z\x12\xcb\x9b\xb5!\x87\x9b\x14R|\x99}e\xb3QQ+Ji\x7f1\x95\xc6\xb2\x98J:\x0f\xa7f*\xe1\xab*\xd8T\xd1|[\xd3\x02cg\xbd\xf1\x04\xa6\x98\x1c\xbb\xa2_\xdfL\xeb\x9do\x0e\xee\x18\x89\x12}N\n\x89\xa8\x16\x85n\xbd\x04-\x9a\xecA\x87\xf3Z\xbcO1\xac\x84C]\xfe~ \xd5=\x94\x9b\xe1\xf0\xb9\xbdG\xac\x12e\xae\xd2I\x16\xc6\x11\x9f\x90\xf41\xe2\xbe\x81\x1bR\xed\xd5f\x98\x80\x81K\xf8\xec\xc9\xcf\xc5\xe7\xcb\xa1\xceO\xa4\xbe\xeeo\xfe\xfe\xe2\xfb\xe7\xf2\xb3*r/\xa2\x180V\xc4\xc5M\xd3\xf6\xd0\x1dv\xbb\xa2\xbd\xe7t\xaf\x86\x83\xf7\xc4\x94\xbe\x0e$\x16\xdfL|\x08F\x9fK\xefB\x0cetb\x92\xd5\xb6mv+\xc3h\x06\x17\xdb\xcap)\xb3x\x98\xab\xe5\x86t\xb0=\xd4\x9b\xe9m \xd9\xbc\x83}\xd3T\xb6\x00QH4\x84\x11\xe3\xa0\x96fK\x9b\x1ad\xc3\x9e\xe3\xe7\xd8Mei\x9c~H\xa4%\xbb\xa2\xac\xcb\xfa\x9a9-\xaf\x8ajX\xef\x0eA\x18S\xe6\x92\xae\nN\x85\x8b\x7f\xd2\x9b}#\x0d9\x14\xdb\x9e\xb4P\xab\xe6\xb4F\x8fbG\xd9\x86t\xfa\xe2\xe3\x07\x86\x04P66\xa1\x86#\x07\x13\xa08\x88\\\xdd\xd3Gi\xcau\xb9/\x06[F\x1cL\xf6E\xa7\x9f/\x14\x99\x9e\xbe\xe4v\xc5g\xb1\xf4\xae\xeeq5\xa0\x08P\xe0\xe6\xc5Z&*\x1e\x8f\xfe\xc0\xa9|\x01\x9d\xe7\xb0\xfe\xb6\xa9\xaa\xe6\x8e\xb4\xfe\xf9\xa3]f\x1c\x1f\x03f\x96\xf3xHn@\x90<}!(\xbe\xe5\x8e\xac\x9bz\x13\xba\x0f\x84\xee\xe8\xbfL\x97,\x18}\xb6Y\xa8\x88<\xa8\x8aN5;\x85\x9f\xa3\xeb\x8b\xb6_\xf5\xe5.t_\x17|m\x8a\x9e<\x1b\xea\xc5(\x84rG\x04{\xca&\x02\xddMs\xa86@\xb9\xa1 \xbd\xeeSIm\xb6\x06..\xcf?\\\x1a'Mz\x9e\x94;D>\xef\xc9\xba'\x93\x07\x87\xd4\x9b\xc7\xefW\xd9\x8d\x8c\x0d}!\xf5\xe6\xe9\xf4\xa2\xd5\xa6\xe8\x0bz\xb1A#W\xf6\xc2\xa6>\xec\xb11\x13=[\xed\x8a\xcf\xf9{g\xef\xcc\xcf\x1f/.\x87.\x9c\xd9f\xf8\x03\x8a\xfc\xe3 \xc3\xc1\x94mk\xfap\x9f\xf2>\xcf0\x81\xfa\xb6X\xdf2\x9b\x9f\xdf\xf8\x90\x17\xc4W\xfa\x96\xcco\xd7\x0f\\\xc8\xdd+\xd6\xfd\xa1\xa8\x1eqN\x11}\xa1\xd0u\xc1f\xd3\x8f\xef~yw\xf1_o\xdf\xf0\xc5\x00\xef\xea\xb2/\xa94:\xa2o\x9dE\xc7N\xa9\xb6\xb1;\x9eZR\xfd\x8a\xd4\xbd)t9{\x8b\xedP\xcb~\x0b\xa0\x99\xdb\xe8\x889b\xcb\xb3\xf9{\x8dL\n\xe1\x06T\xed\xdba\xaau\xf0\xe2\x19[\xb6e\xbd!\x9f\xcd\xab\xc8\xd36wL\xa1\xee\x8c\x8bu\xaapu83\xbd\xe3\x83\xed\x96\x9a?\xddhHH\x87y\xedP\xb1\x84\xf5\x0c\xf3\x90>\x1bT\xfa\xe5[\x87\x1bQ|\xf1\xfe\xed/o\xde\xfd\xf2\x9f\x96\xbf\xe2\x1b\x83\xf8\xabX\x1c\x96?c\xfe\xce\xd1\xdb\xe9\xe6\x8f\x1f,/\xa8l\x14\xf7&\xea\xd6\x97\xea\x92\xcf\xfb\xb2eW\x1c\x9b\xed\xb6#~x-+\xa1\xf3\xc3P\"\xff\xd9\x16k\"f7;\x99\xe8J]\xc8\x89:\xb4xo\xc6\xf9\xa5\x91\x13\x16\xc6\xa1\xee\xcb\xca\xa0Dj\xfa\\\xdb@@HW\xaf\xafo\xda\xbf\x8f\x97\xd2\\WQ\x8f\x9bP\xcc\xccr2\x15\x07I\xf0\x90\x05\xdf\x95\xdf\xa98\xecMQ\x9c\xbenQPk:<#\xc9T\x9c\xd7\x83\xa7r\x1c\xbe\xa33\xa9L%\xf0*\xf1T\xdc\xabg*\xb1yV\xac\x84\xf4\xfc!\xb6\x8c!S\xf1LA\x08\x98\x86`?\xeaL%`0}Y\xd7 \x84L`\xde\x96\xa9\x04\x8fQd.\x97e\x8c\xecc\x14\x93\x1bf*^\xba\x11\x83\x99\x909\xc6J\x0c}uL-Q\xd9d\xa6\x92\xb3\xc7 \xb9f\xd2{\x1c\xc2UR&\x1a\x94\x12vk\xddH\xac1\x153;\x8d\xf4\xb7?\xe9\x8e\x8cf\x92\x99\xca\xc9\xee\xc8>\xbe\x8f\xbd#\xc7%\xfc\xb0+M\xf5M+H\xc8\x943\x95?\xd7v\x102\"I\x99wr\xa8\n5\x1b\xcfT\xfe\xacj\xc2\xc8\x9e3\x95\x93U\x11.\x9e\xbfp\xf5\x10\x98\x19h*\x7f.\xd5\x10\x92_h*^b\x813\xcf\x16\xd8\x9fJt\x1e\"\xbb\x88>\x19O\xf3\x05f$\x92h$\xe5&\xb2\x92S\x9f\xed\x0b\xccRd\xa5\x86e/\x9a\x8a\x96\xc7h*!\xeb05\xb7\x11J,d;P\x98J\xc9wd!\xc5\x08\xf0\xc0\x1a\xd3\x1c}\xc3\xd0\xb4\x93\xbbo\xaa\xcf\xe7\xa7\xe2\x1c\xfb7\x94\xf0\xa5\x89\x01f\xaa\x815\xf5\xa9@\x90}\x9c\xba\x1b\xa34L\xad\xbe(\xf5+\x80\x06O\x81\xf0\xa7N\xa0\xc0\xe8\x9f.\x87u\xccx\xd1W\xb4\xec\xee}\x06\xff\xfd\xf1\xed\x87\xffY]\xfe\xcf{\xcc\x97\xaa\xfc\xf9\xfc\xa7\x9f\xb0\x9fU\xff\xafZ\xe3\xf5\xe5\xbb\x7f\xbc\xc5\xfe\xf2\xeb\xc7\xcb\x8b\xcbskE\xc5/x\xfa\xd2\x0c\x00\x13Z\x0e\xe5\xd1p\xc2/@\x18!\xe0B\x8b_%\x11^x\xfaB\xf1\x83\x0d3\xd9\x11\x89\x90C?\xe80\x88?\x1cL\x15\xa0P\xf2\x81\x0f\x11$C0\x00\xf1\x91\xfa\x98\x04D\xb4C\x11C\xc0\x88\xf9z\x1a\x07I\x0c\x02%\xe6c\x0e\x19\x86Tp\xa2AH\x80\x15m\xf0\xc4@\x80\xe2Q;\x9b\x19\xa8\x88C\x15\x9d`\xc5\xa0\xee\xc5\xa8\xb5\x04\xc8b\x08h1\x13\x9f\xc8\x18$\xa2\x19\xcd\xe5\x8e\xa0\x1b\xc3\xf1\x8d\x99\xfa\x97\x0d\xe5\x88\xe2\x1c\xb5\xc6b\x91\x8e\xce>\xda\xee\x83\xfb\xf0\x8e>\xc4\xa3\x0f\xf3\xe8E=\xbaq\x8f\x10\x8a|\x9c\x83}\xf4\xa2\x1f3\xcd\x1edudFA\xce\xc7A\xa2\xf6\x83\x0f\x0b\x89\xbb\x9aX\xc9\xff\xc4\xbc\x0b\x15\xe9!\x0c^\xe2\x10\x1eb\xf5\x98\xc9\xa2\x04\x05\x8e,\x07\x82\x14\xa4ap\xb8\xf5\x98\xfc\xcf@J&\x84^\xfd\xebl*\xd9\xf0\x92\x90\x80\xc6\x0b\x98\x9e\x104E\xc1uz\x9bJ\xd0\x00\x87D[\x83HE\xe3'\xa3\xc6-\x17\x86r\x197\xbd$b*\x83hG\x0dqNde\x00\xd2\x10\xd2\xd1\x95\xf9\xfb\x9e\x13c\x19\xd4\xf70\xde\xf2!-\xa3\x01Tn\xb4e\xc0\x92\xf4/\xc7\x13\xdf\xe9=\x08\xc6\x93\xdf\xe9\xfd\xfc?\xc4N\x9f h\x95\x19\x89\x190\x7f!h\x0e\xc3\xe9m)ac\x94\x0f\x99\x99\xa0Zl\xe8\xcc\x80a\xf1\x0f\xc9\x89\xab\x15\x07\xea\xf1\xe4U\x8a\x9b\xf7?\x85:\x89Fn\x06\xccY\x08\x9a\xb7pz\xaa$\x0e\xc7\x19D0xV\xfa\xd1\x9c9\xf1\x9cy\x10\x9d\xd91\x9d\xb9Q\x9d>\\\xa7\x03\xd9\x19\xbaZ\xb3\xa2;\xc3\xf0\x9d\x1ak\xf3\x10\x9e\x1a\xb1H\x8cg\x0c\xcaS\xcd\xd9(\xbc\x84L\x8ft,7\x9d\xc0\x1cQ\x18\xdbj\xe0V4\xb5\xa4q\xec\x974\x8e'\x98\xc6\x11\xcdx\x8dc,U0r\x85\xad\x86i\x19pr\xe3bp\xbfB\xab%`e\xf27f\xb3\x0f\xcd\xf9\xa0o\xe3\xb1X\x96\xfbi<=\xa9\xb3\xd2N\xb6t\xce\x11\xab>\xfb\xb3q,\x8a\xa8'i6\xe4\xe9L\xcf\x8cr\xa0\x87\xe0\x82\x921\x07\xa5a\x0eH\xc0\xecM\xbd\xecI\xba\x1c\x9en9%\xd12\x93y\xb6\xb9\xae\xc4\x9a= \x95q\x93\x02]\xc3\xc1I\x94aL\x9c,\xf7OJ\x9f\xec\xd7\x1b4\x8cy& QV \x92X\xd4k\x0d\x9e\xc1\xf1N&\xcfDrN\"\xc7\x04\n\x9b<\xf8\xbek\xef\xd4+i\x07\xd0\xee \xd8\xba9T\xa1cA\xb7#\xb6\x84-\xb5,u\xc6\x7f\xb9\xaa1\xde\xb4J\x93}\x89KJ\xadB#\xd2\xbcJ\xec\x8a\xf2\xbf\x8d\x1e\xbc\x1beY\x92\xe9\xe0~\x0d\xd6\x8fr\x90\x11\xca/s\x1d\xd4\xa8b\x0c\xc5A\xf6#\xc0\xfa^\x98>\xca\xa22\x04\xb3\xa0\xf9\x0eP\xbe\xa1z\xe7\xc0\xf1\x1fs\xd7\xf7\x81\xec\xed'\xcfd`\xbd\x02\xa4\x97\x08\xea\x90\xfa\xc7\x14\x0b\xd7\\\x86\x1b\xcd-\x8edp\xbc\x9eYW\x91\xd3iH\xc4\x03vG=\x99Q0\xf7G\xed\x9c\x0f\xbc\x8e:\x99\x13`\xeb\x8f\xd9I\x05\x82k\x80\xd1\x93w\xb9\x04\xe8\xb9\x0bt\xee\xe4\xc3\x04\xc4\xaa\x8ax\x06\x9e\\\xc5\x7f\x05 \xc7\xe70\xaaj\x8e\x99\x08q(e\xd5a`\xc3\xdd\xa8\xf09\xbd\x08\xc7\x80+S\xef\x88\xa2LEyc)h\x11|\xb7\x17\xd9\x9d\xad#s\x11\xdc\xa0\xbcF\xa8`\xb7-h\xe1\x0c\xeb_\xdd\xdd\xbd\x18m7:;\x99\x9fD\xe0\xb5\x01\xb4\x0e\x81X'3\x99\x05P\xadA\xa9Q)\x84\x80\xa8\x03\xbd6. \xb2\x1d,m\x87I;\x00\xd26ht\x00(:\x0d\x0e\xed\x00B'\x8f\xb1\xb2\xa03\x02\x9e\xe7@\x9d\x95M\xce\x05of\xbd\xce{\x87\xde\x06`\xb6\x90q\x91\x820\xb4\x81\xc5`\x12\xc5\x1b\x0fE\xcd\xbeXpo\x00\xb2 /\x9f\x89 \xe4(\x14\x81}\xa6O%\x16r<\x01TQr!\xa0U\xc7T\x82\x00(\x80\x13\x04\xe0\x19$_\xe0\xdfY=\nB\x1c$\xfbH\xd8\xf0\xfff\xd9\xc7\xc3\x80=\xec\x04\x0dP\x02\xe8\x97\xc1[QrV\xc8k\x02\xd07G\xef\x12`\xbd\xf1\xbd\xf3q\x92\x04\xe2\xb5\xc3\"\xfc\x80\x08\x1bp\xf7\xcf\xb0\xc39@\xad'\xb5\xc3\xb9\xf8<\xd6\x0e\x17\x87\x8dC\xd3a\x80\x0c\xf9\x9a\x03\xb2\xfd2\xd5\xb0O\xdaI\xa0\xd99K\x19\x03\xca\xfe\x19\x96\xb1\x05HzRK\xd8\xc6\xe3\x17\xb8|\xa3@\xad_\xe6\xd2\x0d\x87\xaa\xce\x9dAn`j4$U\x02\x9e\xa2\x04\xf5\xf1\x0b\x02\xa3\xa6\xc1P)\x8f\xeeI\x15\x08@u\xc0LQ\x80\xa9o\x9dd\x03\x95\xfaT\xae\xc2H:\x90T!\x13\x01!\xb5\x80G\xfd\x98\x82\xb3\x8b\xc9\x0dgLp\x15\xa6b\xf3Z\xe1\xde3\xdcsf\xf1\x9aa\x1e3\x8f\xb7L\x91\x94\x84;1\xbe\x96\x01'\x96\x04\x95\n\xffS^JKzH\xbd\x1e\xef\xe1+\x16\x9f\x11\xb0\x16\x7f=$\x83e`\xcd\x11{\xc2\xf1&\xcez\xb1.\xc5\xff\x17\x00\x00\xff\xffPK\x07\x08+\xfb\xd7CZ\xb6\x04\x000\xda8\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xd4`4t\xc7\x01\x00\x00\xbd\x01\x00\x00\x11\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00favicon-16x16.pngUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(6B\xc8\xd7\x7f\x04\x00\x00u\x04\x00\x00\x11\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x0f\x02\x00\x00favicon-32x32.pngUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xb9\xb1\xf1mT\x02\x00\x008\x05\x00\x00\n\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd6\x06\x00\x00index.htmlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(]\x12r 9\x03\x00\x00T \x00\x00\x14\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81k \x00\x00oauth2-redirect.htmlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(-\xe3\xb5\x97=9\x05\x00\xf7\x0c\x1b\x00\x14\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xef\x0c\x00\x00swagger-ui-bundle.jsUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(v\xf2\x8aA\x86\xba\x01\x00\xc5\x87\x08\x00\x1f\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81wF\x05\x00swagger-ui-standalone-preset.jsUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(_;\x94/\xe8Y\x00\x00\xa8X\x02\x00\x0e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81S\x01\x07\x00swagger-ui.cssUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(+\xfb\xd7CZ\xb6\x04\x000\xda8\x00\x0c\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x80[\x07\x00swagger.yamlUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x08\x00\x08\x00E\x02\x00\x00\x1d\x12\x0c\x00\x00\x00" fs.Register(data) } diff --git a/client/docs/swagger-ui/swagger.yaml b/client/docs/swagger-ui/swagger.yaml index 75ba1c56f8..7310a906ff 100644 --- a/client/docs/swagger-ui/swagger.yaml +++ b/client/docs/swagger-ui/swagger.yaml @@ -36409,8 +36409,8 @@ paths: type: string format: uint64 title: >- - maximum amount of supply to allow a marker to be created - with + Deprecated: Prefer to use `max_supply` instead. Maximum + amount of supply to allow a marker to be created with enable_governance: type: boolean format: boolean @@ -36425,6 +36425,11 @@ paths: requests are only subject to platform coin validation denom expression) + max_supply: + type: string + title: >- + maximum amount of supply to allow a marker to be created + with description: >- QueryParamsResponse is the response type for the Query/Params RPC method. @@ -83319,7 +83324,9 @@ definitions: max_total_supply: type: string format: uint64 - title: maximum amount of supply to allow a marker to be created with + title: >- + Deprecated: Prefer to use `max_supply` instead. Maximum amount of + supply to allow a marker to be created with enable_governance: type: boolean format: boolean @@ -83332,6 +83339,9 @@ definitions: requests are only subject to platform coin validation denom expression) + max_supply: + type: string + title: maximum amount of supply to allow a marker to be created with description: Params defines the set of params for the account module. provenance.marker.v1.QueryAccessResponse: type: object @@ -83950,7 +83960,9 @@ definitions: max_total_supply: type: string format: uint64 - title: maximum amount of supply to allow a marker to be created with + title: >- + Deprecated: Prefer to use `max_supply` instead. Maximum amount of + supply to allow a marker to be created with enable_governance: type: boolean format: boolean @@ -83963,6 +83975,9 @@ definitions: requests are only subject to platform coin validation denom expression) + max_supply: + type: string + title: maximum amount of supply to allow a marker to be created with description: QueryParamsResponse is the response type for the Query/Params RPC method. provenance.marker.v1.QuerySupplyResponse: type: object From c30a6cc20875e1fe9029b14c30baf9373fab9d26 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 9 Oct 2023 14:31:51 -0600 Subject: [PATCH 272/309] [1658]: Add the bare beginnings of the spec doc. --- x/exchange/spec/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 x/exchange/spec/README.md diff --git a/x/exchange/spec/README.md b/x/exchange/spec/README.md new file mode 100644 index 0000000000..898659b4a8 --- /dev/null +++ b/x/exchange/spec/README.md @@ -0,0 +1,16 @@ +# `Exchange` + +## Overview + +The exchange module is used to facilitate the buying and selling of on-chain assets. + + + +## Contents + +1. **[Concepts](01_concepts.md)** +2. **[State](02_state.md)** +3. **[Messages](03_messages.md)** +4. **[Events](04_events.md)** +5. **[Queries](05_queries.md)** +6. **[Params](06_params.md)** From 7b48bbc6d85511f4d3f4660a9f0648c0c7628df0 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 9 Oct 2023 14:41:38 -0600 Subject: [PATCH 273/309] [1658]: Create TODOs for all the keeper unit tests. --- x/exchange/keeper/fulfillment_test.go | 7 +++ x/exchange/keeper/genesis_test.go | 5 ++ x/exchange/keeper/grpc_query_test.go | 27 +++++++++ x/exchange/keeper/market_test.go | 87 +++++++++++++++++++++++++++ x/exchange/keeper/msg_server_test.go | 33 ++++++++++ x/exchange/keeper/orders_test.go | 17 ++++++ x/exchange/keeper/params_test.go | 9 +++ 7 files changed, 185 insertions(+) create mode 100644 x/exchange/keeper/fulfillment_test.go create mode 100644 x/exchange/keeper/genesis_test.go create mode 100644 x/exchange/keeper/grpc_query_test.go create mode 100644 x/exchange/keeper/market_test.go create mode 100644 x/exchange/keeper/msg_server_test.go create mode 100644 x/exchange/keeper/orders_test.go create mode 100644 x/exchange/keeper/params_test.go diff --git a/x/exchange/keeper/fulfillment_test.go b/x/exchange/keeper/fulfillment_test.go new file mode 100644 index 0000000000..545847e6f2 --- /dev/null +++ b/x/exchange/keeper/fulfillment_test.go @@ -0,0 +1,7 @@ +package keeper_test + +// TODO[1658]: func (s *TestSuite) TestKeeper_FillBids() + +// TODO[1658]: func (s *TestSuite) TestKeeper_FillAsks() + +// TODO[1658]: func (s *TestSuite) TestKeeper_SettleOrders() diff --git a/x/exchange/keeper/genesis_test.go b/x/exchange/keeper/genesis_test.go new file mode 100644 index 0000000000..e822f9416d --- /dev/null +++ b/x/exchange/keeper/genesis_test.go @@ -0,0 +1,5 @@ +package keeper_test + +// TODO[1658]: func (s *TestSuite) TestKeeper_InitGenesis() + +// TODO[1658]: func (s *TestSuite) TestKeeper_ExportGenesis() diff --git a/x/exchange/keeper/grpc_query_test.go b/x/exchange/keeper/grpc_query_test.go new file mode 100644 index 0000000000..8c345f04b9 --- /dev/null +++ b/x/exchange/keeper/grpc_query_test.go @@ -0,0 +1,27 @@ +package keeper_test + +// TODO[1658]: func (s *TestSuite) TestNewQueryServer() + +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryOrderFeeCalc() + +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetOrder() + +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetMarketOrders() + +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetOwnerOrders() + +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetAssetOrders() + +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetAllOrders() + +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetMarket() + +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetAllMarkets() + +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryParams() + +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryValidateCreateMarket() + +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryValidateMarket() + +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryValidateManageFees() diff --git a/x/exchange/keeper/market_test.go b/x/exchange/keeper/market_test.go new file mode 100644 index 0000000000..b50542ba0e --- /dev/null +++ b/x/exchange/keeper/market_test.go @@ -0,0 +1,87 @@ +package keeper_test + +// TODO[1658]: func (s *TestSuite) TestKeeper_IterateKnownMarketIDs() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetCreateAskFlatFees() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetCreateBidFlatFees() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetSellerSettlementFlatFees() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetSellerSettlementRatios() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetBuyerSettlementFlatFees() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetBuyerSettlementRatios() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CalculateSellerSettlementRatioFee() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CalculateBuyerSettlementRatioFeeOptions() + +// TODO[1658]: func (s *TestSuite) TestKeeper_ValidateCreateAskFlatFee() + +// TODO[1658]: func (s *TestSuite) TestKeeper_ValidateCreateBidFlatFee() + +// TODO[1658]: func (s *TestSuite) TestKeeper_ValidateSellerSettlementFlatFee() + +// TODO[1658]: func (s *TestSuite) TestKeeper_ValidateAskPrice() + +// TODO[1658]: func (s *TestSuite) TestKeeper_ValidateBuyerSettlementFee() + +// TODO[1658]: func (s *TestSuite) TestKeeper_UpdateFees() + +// TODO[1658]: func (s *TestSuite) TestKeeper_IsMarketActive() + +// TODO[1658]: func (s *TestSuite) TestKeeper_UpdateMarketActive() + +// TODO[1658]: func (s *TestSuite) TestKeeper_IsUserSettlementAllowed() + +// TODO[1658]: func (s *TestSuite) TestKeeper_UpdateUserSettlementAllowed() + +// TODO[1658]: func (s *TestSuite) TestKeeper_HasPermission() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CanSettleOrders() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CanCancelOrdersForMarket() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CanWithdrawMarketFunds() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CanUpdateMarket() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CanManagePermissions() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CanManageReqAttrs() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetUserPermissions() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetAccessGrants() + +// TODO[1658]: func (s *TestSuite) TestKeeper_UpdatePermissions() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetReqAttrsAsk() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetReqAttrsBid() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CanCreateAsk() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CanCreateBid() + +// TODO[1658]: func (s *TestSuite) TestKeeper_UpdateReqAttrs() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetMarketAccount() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetMarketDetails() + +// TODO[1658]: func (s *TestSuite) TestKeeper_UpdateMarketDetails() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CreateMarket() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetMarket() + +// TODO[1658]: func (s *TestSuite) TestKeeper_IterateMarkets() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetMarketBrief() + +// TODO[1658]: func (s *TestSuite) TestKeeper_WithdrawMarketFunds() + +// TODO[1658]: func (s *TestSuite) TestKeeper_ValidateMarket() diff --git a/x/exchange/keeper/msg_server_test.go b/x/exchange/keeper/msg_server_test.go new file mode 100644 index 0000000000..77e9b49ce0 --- /dev/null +++ b/x/exchange/keeper/msg_server_test.go @@ -0,0 +1,33 @@ +package keeper_test + +// TODO[1658]: func (s *TestSuite) TestNewMsgServer() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_CreateAsk() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_CreateBid() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_CancelOrder() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_FillBids() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_FillAsks() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_MarketSettle() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_MarketWithdraw() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_MarketUpdateDetails() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_MarketUpdateEnabled() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_MarketUpdateUserSettle() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_MarketManagePermissions() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_MarketManageReqAttrs() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_GovCreateMarket() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_GovManageFees() + +// TODO[1658]: func (s *TestSuite) TestMsgServer_GovUpdateParams() diff --git a/x/exchange/keeper/orders_test.go b/x/exchange/keeper/orders_test.go new file mode 100644 index 0000000000..f254497902 --- /dev/null +++ b/x/exchange/keeper/orders_test.go @@ -0,0 +1,17 @@ +package keeper_test + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetOrder() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CreateAskOrder() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CreateBidOrder() + +// TODO[1658]: func (s *TestSuite) TestKeeper_CancelOrder() + +// TODO[1658]: func (s *TestSuite) TestKeeper_IterateOrders() + +// TODO[1658]: func (s *TestSuite) TestKeeper_IterateMarketOrders() + +// TODO[1658]: func (s *TestSuite) TestKeeper_IterateAddressOrders() + +// TODO[1658]: func (s *TestSuite) TestKeeper_IterateAssetOrders() diff --git a/x/exchange/keeper/params_test.go b/x/exchange/keeper/params_test.go new file mode 100644 index 0000000000..21a49d4b71 --- /dev/null +++ b/x/exchange/keeper/params_test.go @@ -0,0 +1,9 @@ +package keeper_test + +// TODO[1658]: func (s *TestSuite) TestKeeper_SetParams() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetParams() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetParamsOrDefaults() + +// TODO[1658]: func (s *TestSuite) TestKeeper_GetExchangeSplit() From fd535bcd1caae5bb4ecf0d3c2cdab34cef418662 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 9 Oct 2023 16:46:43 -0600 Subject: [PATCH 274/309] Some small tweaks from initial code review. --- proto/provenance/exchange/v1/market.proto | 5 +- x/exchange/events_test.go | 2 +- x/exchange/market.pb.go | 129 +++++++++++----------- 3 files changed, 66 insertions(+), 70 deletions(-) diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index d7544cff33..f9221cb860 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -11,16 +11,13 @@ import "cosmos/base/v1beta1/coin.proto"; import "cosmos_proto/cosmos.proto"; import "gogoproto/gogo.proto"; -// TODO[1658]: Investigate whether the yaml gogoproto.moretags are still needed. - // MarketAccount is an account type for use with the accounts module to hold some basic information about a market. message MarketAccount { option (gogoproto.goproto_getters) = false; option (gogoproto.goproto_stringer) = false; // base_account is the base cosmos account information. - cosmos.auth.v1beta1.BaseAccount base_account = 1 - [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"base_account\""]; + cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true]; // market_id is the numerical identifier for this market. uint32 market_id = 2; diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go index 6f9d178c9c..5fd2bfc999 100644 --- a/x/exchange/events_test.go +++ b/x/exchange/events_test.go @@ -25,7 +25,7 @@ func assertEverythingSet(t *testing.T, tev proto.Message, typeString string) boo } expType := "provenance.exchange.v1." + typeString - rv := assert.Equal(t, expType, event.Type, "%T event.Type") + rv := assert.Equal(t, expType, event.Type, "%T event.Type", tev) for i, attrs := range event.Attributes { rv = assert.NotEmpty(t, attrs.Key, "%T event.attributes[%d].Key", tev, i) && rv rv = assert.NotEqual(t, `""`, attrs.Key, "%T event.attributes[%d].Key", tev, i) && rv diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index 3d20922a32..ea0caf8a0d 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -77,7 +77,7 @@ func (Permission) EnumDescriptor() ([]byte, []int) { // MarketAccount is an account type for use with the accounts module to hold some basic information about a market. type MarketAccount struct { // base_account is the base cosmos account information. - *types.BaseAccount `protobuf:"bytes,1,opt,name=base_account,json=baseAccount,proto3,embedded=base_account" json:"base_account,omitempty" yaml:"base_account"` + *types.BaseAccount `protobuf:"bytes,1,opt,name=base_account,json=baseAccount,proto3,embedded=base_account" json:"base_account,omitempty"` // market_id is the numerical identifier for this market. MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // market_details is some human-consumable information about this market. @@ -559,71 +559,70 @@ func init() { } var fileDescriptor_d5cf198f1dd7e167 = []byte{ - // 1016 bytes of a gzipped FileDescriptorProto + // 1001 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x41, 0x6f, 0x1a, 0x47, - 0x14, 0x66, 0x0d, 0xb1, 0x61, 0xb0, 0x1d, 0x3a, 0x4e, 0xdc, 0x35, 0xae, 0x60, 0x4b, 0x14, 0x89, - 0xb4, 0x32, 0xc8, 0xae, 0x7a, 0xf1, 0xa5, 0x02, 0x1b, 0xb7, 0x48, 0x89, 0x63, 0x2d, 0xa0, 0x48, - 0x51, 0xa5, 0xed, 0xb0, 0xfb, 0xc0, 0x23, 0x2f, 0xbb, 0x64, 0x66, 0xb0, 0x93, 0xfe, 0x81, 0x56, - 0x3e, 0xf5, 0xd8, 0x8b, 0x25, 0xf7, 0x1f, 0xf4, 0xd0, 0x7b, 0xaf, 0x39, 0x5a, 0x3d, 0xf5, 0x64, - 0x55, 0xf6, 0xa5, 0x87, 0x9e, 0xfa, 0x0b, 0xaa, 0x9d, 0x59, 0x96, 0x0d, 0xc1, 0x75, 0xa2, 0xaa, - 0xb7, 0x79, 0xef, 0xfb, 0xe6, 0x7b, 0xdf, 0x7b, 0x3c, 0x46, 0x8b, 0x1e, 0x0c, 0x99, 0x7f, 0x0c, - 0x1e, 0xf1, 0x6c, 0xa8, 0xc2, 0x4b, 0xfb, 0x90, 0x78, 0x7d, 0xa8, 0x1e, 0x6f, 0x56, 0x07, 0x84, - 0x1d, 0x81, 0xa8, 0x0c, 0x99, 0x2f, 0x7c, 0xbc, 0x3a, 0x21, 0x55, 0xc6, 0xa4, 0xca, 0xf1, 0x66, - 0xbe, 0x60, 0xfb, 0x7c, 0xe0, 0xf3, 0x2a, 0x19, 0x89, 0xc3, 0xea, 0xf1, 0x66, 0x17, 0x04, 0xd9, - 0x94, 0x81, 0xba, 0x17, 0xe1, 0x5d, 0xc2, 0x21, 0xc2, 0x6d, 0x9f, 0x7a, 0x21, 0xbe, 0xa6, 0x70, - 0x4b, 0x46, 0x55, 0x15, 0x84, 0xd0, 0xbd, 0xbe, 0xdf, 0xf7, 0x55, 0x3e, 0x38, 0xa9, 0x6c, 0xe9, - 0x2f, 0x0d, 0x2d, 0x3d, 0x91, 0xce, 0x6a, 0xb6, 0xed, 0x8f, 0x3c, 0x81, 0xbf, 0x41, 0x8b, 0x81, - 0xba, 0x45, 0x54, 0xac, 0x6b, 0x86, 0x56, 0xce, 0x6e, 0x19, 0x95, 0x50, 0x4c, 0x9a, 0x09, 0x2b, - 0x57, 0xea, 0x84, 0x43, 0x78, 0xaf, 0xbe, 0x7e, 0x71, 0x59, 0xd4, 0xfe, 0xbe, 0x2c, 0xae, 0xbc, - 0x22, 0x03, 0x77, 0xbb, 0x14, 0xd7, 0x28, 0x99, 0xd9, 0xee, 0x84, 0x89, 0xd7, 0x51, 0x46, 0x0d, - 0xc3, 0xa2, 0x8e, 0x3e, 0x67, 0x68, 0xe5, 0x25, 0x33, 0xad, 0x12, 0x4d, 0x07, 0x9b, 0x68, 0x39, - 0x04, 0x1d, 0x10, 0x84, 0xba, 0x5c, 0x4f, 0x4a, 0x03, 0x0f, 0x2b, 0xb3, 0x47, 0x56, 0x51, 0xee, - 0x77, 0x15, 0xb9, 0x9e, 0x7a, 0x7d, 0x59, 0x4c, 0x98, 0x4b, 0x83, 0x78, 0x72, 0x3b, 0xfd, 0xfd, - 0x79, 0x31, 0xf1, 0xe3, 0x79, 0x31, 0x51, 0xfa, 0x2e, 0x6a, 0x37, 0xc4, 0x30, 0x46, 0x29, 0x8f, - 0x0c, 0x40, 0xb6, 0x99, 0x31, 0xe5, 0x19, 0x1b, 0x28, 0xeb, 0x00, 0xb7, 0x19, 0x1d, 0x0a, 0xea, - 0x7b, 0xd2, 0x62, 0xc6, 0x8c, 0xa7, 0x70, 0x11, 0x65, 0x4f, 0xa0, 0xcb, 0xa9, 0x00, 0x6b, 0xc4, - 0x5c, 0x69, 0x31, 0x63, 0xa2, 0x30, 0xd5, 0x61, 0x2e, 0x5e, 0x43, 0x69, 0x6a, 0xfb, 0x9e, 0x35, - 0x62, 0x54, 0x4f, 0x49, 0x74, 0x21, 0x88, 0x3b, 0x8c, 0x6e, 0xa7, 0xfe, 0x3c, 0x2f, 0x6a, 0xa5, - 0x5f, 0x35, 0x94, 0x55, 0x4e, 0xea, 0x8c, 0x42, 0xef, 0xcd, 0xa1, 0x68, 0x53, 0x43, 0xf9, 0x22, - 0x1a, 0x0a, 0x71, 0x1c, 0x06, 0x9c, 0x2b, 0x4f, 0x75, 0xfd, 0xb7, 0x5f, 0x36, 0xee, 0x85, 0x3f, - 0x4c, 0x4d, 0x21, 0x2d, 0xc1, 0xa8, 0xd7, 0x1f, 0x4f, 0x20, 0x4c, 0xfe, 0x1f, 0x53, 0x2d, 0xfd, - 0xbc, 0x80, 0xe6, 0x15, 0xed, 0xdf, 0xcd, 0xbf, 0x5d, 0x7b, 0xee, 0xbf, 0xd6, 0xc6, 0xfb, 0x68, - 0xa5, 0x07, 0x60, 0xd9, 0x0c, 0x88, 0x00, 0x8b, 0xf0, 0x23, 0xab, 0xe7, 0x12, 0xa1, 0x27, 0x8d, - 0x64, 0x39, 0xbb, 0xb5, 0x36, 0xde, 0xd5, 0x60, 0xe9, 0xa2, 0x5d, 0xdd, 0xf1, 0xa9, 0x17, 0x8a, - 0xe5, 0x7a, 0x00, 0x3b, 0xf2, 0x6a, 0x8d, 0x1f, 0xed, 0xb9, 0x44, 0x4c, 0xe9, 0x75, 0xa9, 0xa3, - 0xf4, 0x52, 0xef, 0xab, 0x57, 0xa7, 0x8e, 0xd4, 0xfb, 0x1a, 0xe5, 0x03, 0x3d, 0x0e, 0xae, 0x0b, - 0xcc, 0xe2, 0x20, 0x84, 0x0b, 0x03, 0xf0, 0x84, 0x92, 0xbd, 0xf3, 0x6e, 0xb2, 0x1f, 0xf6, 0x00, - 0x5a, 0x52, 0xa1, 0x15, 0x09, 0x48, 0xf5, 0x3e, 0xfa, 0x68, 0xb6, 0x3a, 0x23, 0x82, 0xfa, 0x5c, - 0x9f, 0x97, 0xfa, 0xc6, 0x4d, 0xf3, 0xdd, 0x03, 0x30, 0x03, 0x62, 0x58, 0x66, 0x6d, 0x46, 0x19, - 0x89, 0x73, 0xfc, 0x1c, 0x05, 0xa0, 0xd5, 0x1d, 0xbd, 0x9a, 0xd1, 0xc5, 0xc2, 0xbb, 0x75, 0xb1, - 0xda, 0x03, 0xa8, 0x07, 0x02, 0x53, 0x4d, 0x00, 0x5a, 0x9f, 0xa9, 0x1d, 0xf6, 0x90, 0x7e, 0xaf, - 0x1e, 0xf4, 0xb7, 0x8b, 0x84, 0x2d, 0x3c, 0x42, 0x39, 0x62, 0xdb, 0x30, 0x14, 0xd4, 0xeb, 0x5b, - 0x3e, 0x73, 0x80, 0x71, 0x3d, 0x63, 0x68, 0xe5, 0xb4, 0x79, 0x37, 0xca, 0x3f, 0x95, 0x69, 0xbc, - 0x85, 0xee, 0x13, 0xd7, 0xf5, 0x4f, 0xac, 0x11, 0x7f, 0xc3, 0x92, 0x8e, 0x24, 0x7f, 0x45, 0x82, - 0x1d, 0x1e, 0x2f, 0x82, 0xf7, 0xd1, 0x52, 0x20, 0xc3, 0xb9, 0xd5, 0x67, 0xc4, 0x13, 0x5c, 0xcf, - 0x4a, 0xdf, 0x0f, 0x6e, 0xf2, 0x5d, 0x93, 0xe4, 0x2f, 0x03, 0x6e, 0x68, 0x7d, 0x91, 0x4c, 0x52, - 0x1c, 0x6f, 0xa0, 0x15, 0x06, 0x2f, 0x2c, 0x22, 0x04, 0x8b, 0x6d, 0xb7, 0xbe, 0x68, 0x24, 0xcb, - 0x19, 0x33, 0xc7, 0xe0, 0x45, 0x4d, 0x08, 0x16, 0xed, 0xee, 0x2c, 0x7a, 0x97, 0x3a, 0xfa, 0xd2, - 0x0c, 0x7a, 0x9d, 0x3a, 0xa5, 0x6f, 0x51, 0x7a, 0x3c, 0x38, 0xfc, 0x39, 0xba, 0x33, 0x64, 0xd4, - 0x86, 0xf0, 0x81, 0xbf, 0xf5, 0x77, 0x54, 0x6c, 0xbc, 0x89, 0x92, 0x3d, 0x80, 0xf0, 0x2f, 0x7c, - 0xeb, 0xa5, 0x80, 0xbb, 0x9d, 0x1a, 0x3f, 0xbd, 0xd9, 0x58, 0xf7, 0x78, 0x0b, 0x2d, 0x8c, 0x1f, - 0x33, 0xed, 0x96, 0xc7, 0x6c, 0x4c, 0xc4, 0xbb, 0x28, 0x3b, 0x04, 0x36, 0xa0, 0x9c, 0x53, 0xdf, - 0x0b, 0xde, 0x91, 0x64, 0x79, 0x79, 0xab, 0x74, 0xd3, 0xac, 0x0f, 0x22, 0xaa, 0x19, 0xbf, 0xf6, - 0xc9, 0x4f, 0x73, 0x08, 0x4d, 0x30, 0xfc, 0x29, 0x5a, 0x3d, 0x68, 0x98, 0x4f, 0x9a, 0xad, 0x56, - 0xf3, 0xe9, 0xbe, 0xd5, 0xd9, 0x6f, 0x1d, 0x34, 0x76, 0x9a, 0x7b, 0xcd, 0xc6, 0x6e, 0x2e, 0x91, - 0xbf, 0x7b, 0x7a, 0x66, 0x64, 0x47, 0x1e, 0x1f, 0x82, 0x4d, 0x7b, 0x14, 0x1c, 0xfc, 0x31, 0xfa, - 0x20, 0x46, 0x6e, 0x35, 0xda, 0xed, 0xc7, 0x8d, 0x9c, 0x96, 0x47, 0xa7, 0x67, 0xc6, 0xbc, 0xda, - 0x98, 0x29, 0xca, 0x4e, 0x6d, 0x7f, 0xa7, 0xf1, 0x38, 0x37, 0xa7, 0x28, 0x76, 0x60, 0xd2, 0xc5, - 0x0f, 0xd1, 0x4a, 0x8c, 0xf2, 0xac, 0xd9, 0xfe, 0x6a, 0xd7, 0xac, 0x3d, 0xcb, 0x25, 0xf3, 0x8b, - 0xa7, 0x67, 0x46, 0xfa, 0x84, 0x8a, 0x43, 0x87, 0x91, 0x93, 0x29, 0xa5, 0xce, 0xc1, 0x6e, 0xad, - 0xdd, 0xc8, 0xa5, 0x94, 0xd2, 0x68, 0xe8, 0x10, 0x01, 0x53, 0xe6, 0x27, 0xc7, 0x56, 0xee, 0x8e, - 0x32, 0x1f, 0x6b, 0x1c, 0x3f, 0x42, 0xf7, 0x63, 0xe4, 0x5a, 0xbb, 0x6d, 0x36, 0xeb, 0x9d, 0x76, - 0xa3, 0x95, 0x9b, 0xcf, 0x2f, 0x9f, 0x9e, 0x19, 0x28, 0x58, 0x23, 0xda, 0x1d, 0x09, 0xe0, 0x75, - 0x78, 0x7d, 0x55, 0xd0, 0x2e, 0xae, 0x0a, 0xda, 0x1f, 0x57, 0x05, 0xed, 0x87, 0xeb, 0x42, 0xe2, - 0xe2, 0xba, 0x90, 0xf8, 0xfd, 0xba, 0x90, 0x40, 0x6b, 0xd4, 0xbf, 0x61, 0xe0, 0x07, 0xda, 0xf3, - 0x4a, 0x9f, 0x8a, 0xc3, 0x51, 0xb7, 0x62, 0xfb, 0x83, 0xea, 0x84, 0xb4, 0x41, 0xfd, 0x58, 0x54, - 0x7d, 0x19, 0x7d, 0x17, 0x75, 0xe7, 0xe5, 0x57, 0xc8, 0x67, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, - 0xb9, 0xe4, 0xfe, 0x0f, 0x35, 0x09, 0x00, 0x00, + 0x14, 0x66, 0x0d, 0xb6, 0x61, 0xb0, 0x1d, 0x3a, 0x4e, 0xdc, 0x35, 0xa9, 0x60, 0x4b, 0x14, 0x89, + 0xb4, 0x32, 0xc8, 0xae, 0x7a, 0xf1, 0xa5, 0x02, 0x1b, 0xb7, 0x48, 0x89, 0x83, 0x16, 0x50, 0xa4, + 0xa8, 0xd2, 0x6a, 0xd8, 0x7d, 0xe0, 0x91, 0x97, 0x5d, 0x32, 0x33, 0xd8, 0x69, 0xff, 0x40, 0x2b, + 0x9f, 0x7a, 0xec, 0xc5, 0x92, 0xfb, 0x0f, 0x7a, 0xe8, 0xbd, 0xd7, 0x1c, 0xad, 0x4a, 0x95, 0x7a, + 0x8a, 0x2a, 0xfb, 0xd2, 0x9f, 0x51, 0xed, 0xcc, 0xb2, 0x6c, 0x08, 0xae, 0x13, 0x55, 0xb9, 0xcd, + 0x7b, 0xdf, 0x37, 0xdf, 0xfb, 0xde, 0xe3, 0x31, 0x5a, 0xf4, 0x60, 0xc4, 0xfc, 0x13, 0xf0, 0x88, + 0x67, 0x43, 0x15, 0x5e, 0xda, 0x47, 0xc4, 0x1b, 0x40, 0xf5, 0x64, 0xbb, 0x3a, 0x24, 0xec, 0x18, + 0x44, 0x65, 0xc4, 0x7c, 0xe1, 0xe3, 0x8d, 0x29, 0xa9, 0x32, 0x21, 0x55, 0x4e, 0xb6, 0xf3, 0x05, + 0xdb, 0xe7, 0x43, 0x9f, 0x57, 0xc9, 0x58, 0x1c, 0x55, 0x4f, 0xb6, 0x7b, 0x20, 0xc8, 0xb6, 0x0c, + 0xd4, 0xbd, 0x08, 0xef, 0x11, 0x0e, 0x11, 0x6e, 0xfb, 0xd4, 0x0b, 0xf1, 0x4d, 0x85, 0x5b, 0x32, + 0xaa, 0xaa, 0x20, 0x84, 0xee, 0x0e, 0xfc, 0x81, 0xaf, 0xf2, 0xc1, 0x49, 0x65, 0x4b, 0x7f, 0x6a, + 0x68, 0xf5, 0x89, 0x74, 0x56, 0xb3, 0x6d, 0x7f, 0xec, 0x09, 0xdc, 0x44, 0x2b, 0x81, 0xba, 0x45, + 0x54, 0xac, 0x6b, 0x86, 0x56, 0xce, 0xee, 0x18, 0x95, 0x50, 0x4c, 0x9a, 0x09, 0x2b, 0x57, 0xea, + 0x84, 0x43, 0x78, 0xaf, 0x9e, 0xba, 0x7c, 0x5d, 0xd4, 0xcc, 0x6c, 0x6f, 0x9a, 0xc2, 0xf7, 0x51, + 0x46, 0x75, 0x6d, 0x51, 0x47, 0x5f, 0x30, 0xb4, 0xf2, 0xaa, 0x99, 0x56, 0x89, 0xa6, 0x83, 0x4d, + 0xb4, 0x16, 0x82, 0x0e, 0x08, 0x42, 0x5d, 0xae, 0x27, 0x65, 0xa5, 0x87, 0x95, 0xf9, 0xb3, 0xa9, + 0x28, 0x9b, 0xfb, 0x8a, 0x5c, 0x4f, 0xbd, 0x7a, 0x5d, 0x4c, 0x98, 0xab, 0xc3, 0x78, 0x72, 0x37, + 0xfd, 0xe3, 0x45, 0x31, 0xf1, 0xf3, 0x45, 0x31, 0x51, 0xfa, 0x21, 0xea, 0x2b, 0xc4, 0x30, 0x46, + 0x29, 0x8f, 0x0c, 0x41, 0xf6, 0x93, 0x31, 0xe5, 0x19, 0x1b, 0x28, 0xeb, 0x00, 0xb7, 0x19, 0x1d, + 0x09, 0xea, 0x7b, 0xd2, 0x62, 0xc6, 0x8c, 0xa7, 0x70, 0x11, 0x65, 0x4f, 0xa1, 0xc7, 0xa9, 0x00, + 0x6b, 0xcc, 0x5c, 0x69, 0x31, 0x63, 0xa2, 0x30, 0xd5, 0x65, 0x2e, 0xde, 0x44, 0x69, 0x6a, 0xfb, + 0x9e, 0x35, 0x66, 0x54, 0x4f, 0x49, 0x74, 0x39, 0x88, 0xbb, 0x8c, 0xee, 0xa6, 0xfe, 0xb9, 0x28, + 0x6a, 0xa5, 0xdf, 0x35, 0x94, 0x55, 0x4e, 0xea, 0x8c, 0x42, 0xff, 0xcd, 0xa1, 0x68, 0x33, 0x43, + 0xf9, 0x2a, 0x1a, 0x0a, 0x71, 0x1c, 0x06, 0x9c, 0x2b, 0x4f, 0x75, 0xfd, 0x8f, 0xdf, 0xb6, 0xee, + 0x86, 0xbf, 0x40, 0x4d, 0x21, 0x6d, 0xc1, 0xa8, 0x37, 0x98, 0x4c, 0x20, 0x4c, 0x7e, 0x88, 0xa9, + 0x96, 0x7e, 0x5d, 0x46, 0x4b, 0x8a, 0xf6, 0xdf, 0xe6, 0xdf, 0xae, 0xbd, 0xf0, 0x7f, 0x6b, 0xe3, + 0x43, 0xb4, 0xde, 0x07, 0xb0, 0x6c, 0x06, 0x44, 0x80, 0x45, 0xf8, 0xb1, 0xd5, 0x77, 0x89, 0xd0, + 0x93, 0x46, 0xb2, 0x9c, 0xdd, 0xd9, 0x9c, 0x2c, 0x65, 0xb0, 0x74, 0xd1, 0x52, 0xee, 0xf9, 0xd4, + 0x0b, 0xc5, 0x72, 0x7d, 0x80, 0x3d, 0x79, 0xb5, 0xc6, 0x8f, 0x0f, 0x5c, 0x22, 0x66, 0xf4, 0x7a, + 0xd4, 0x51, 0x7a, 0xa9, 0xf7, 0xd5, 0xab, 0x53, 0x47, 0xea, 0x7d, 0x8b, 0xf2, 0x81, 0x1e, 0x07, + 0xd7, 0x05, 0x66, 0x71, 0x10, 0xc2, 0x85, 0x21, 0x78, 0x42, 0xc9, 0x2e, 0xbe, 0x9b, 0xec, 0xc7, + 0x7d, 0x80, 0xb6, 0x54, 0x68, 0x47, 0x02, 0x52, 0x7d, 0x80, 0x3e, 0x99, 0xaf, 0xce, 0x88, 0xa0, + 0x3e, 0xd7, 0x97, 0xa4, 0xbe, 0x71, 0xd3, 0x7c, 0x0f, 0x00, 0xcc, 0x80, 0x18, 0x96, 0xd9, 0x9c, + 0x53, 0x46, 0xe2, 0x1c, 0x3f, 0x47, 0x01, 0x68, 0xf5, 0xc6, 0xdf, 0xcd, 0xe9, 0x62, 0xf9, 0xdd, + 0xba, 0xd8, 0xe8, 0x03, 0xd4, 0x03, 0x81, 0x99, 0x26, 0x00, 0xdd, 0x9f, 0xab, 0x1d, 0xf6, 0x90, + 0x7e, 0xaf, 0x1e, 0xf4, 0xb7, 0x8b, 0x84, 0x2d, 0x3c, 0x42, 0x39, 0x62, 0xdb, 0x30, 0x12, 0xd4, + 0x1b, 0x58, 0x3e, 0x73, 0x80, 0x71, 0x3d, 0x63, 0x68, 0xe5, 0xb4, 0x79, 0x27, 0xca, 0x3f, 0x95, + 0x69, 0xbc, 0x83, 0xee, 0x11, 0xd7, 0xf5, 0x4f, 0xad, 0x31, 0x7f, 0xc3, 0x92, 0x8e, 0x24, 0x7f, + 0x5d, 0x82, 0x5d, 0x1e, 0x2f, 0x82, 0x0f, 0xd1, 0x6a, 0x20, 0xc3, 0xb9, 0x35, 0x60, 0xc4, 0x13, + 0x5c, 0xcf, 0x4a, 0xdf, 0x0f, 0x6e, 0xf2, 0x5d, 0x93, 0xe4, 0xaf, 0x03, 0x6e, 0x68, 0x7d, 0x85, + 0x4c, 0x53, 0x1c, 0x6f, 0xa1, 0x75, 0x06, 0x2f, 0x2c, 0x22, 0x04, 0x8b, 0x6d, 0xb7, 0xbe, 0x62, + 0x24, 0xcb, 0x19, 0x33, 0xc7, 0xe0, 0x45, 0x4d, 0x08, 0x16, 0xed, 0xee, 0x3c, 0x7a, 0x8f, 0x3a, + 0xfa, 0xea, 0x1c, 0x7a, 0x9d, 0x3a, 0xa5, 0xef, 0x51, 0x7a, 0x32, 0x38, 0xfc, 0x25, 0x5a, 0x1c, + 0x31, 0x6a, 0x43, 0xf8, 0x92, 0xdf, 0xfa, 0x3b, 0x2a, 0x36, 0xde, 0x46, 0xc9, 0x3e, 0x40, 0xf8, + 0x17, 0xbe, 0xf5, 0x52, 0xc0, 0xdd, 0x4d, 0x4d, 0x9e, 0xde, 0x6c, 0xac, 0x7b, 0xbc, 0x83, 0x96, + 0x27, 0x8f, 0x99, 0x76, 0xcb, 0x63, 0x36, 0x21, 0xe2, 0x7d, 0x94, 0x1d, 0x01, 0x1b, 0x52, 0xce, + 0xa9, 0xef, 0x05, 0xef, 0x48, 0xb2, 0xbc, 0xb6, 0x53, 0xba, 0x69, 0xd6, 0xad, 0x88, 0x6a, 0xc6, + 0xaf, 0x7d, 0xf6, 0xcb, 0x02, 0x42, 0x53, 0x0c, 0x7f, 0x8e, 0x36, 0x5a, 0x0d, 0xf3, 0x49, 0xb3, + 0xdd, 0x6e, 0x3e, 0x3d, 0xb4, 0xba, 0x87, 0xed, 0x56, 0x63, 0xaf, 0x79, 0xd0, 0x6c, 0xec, 0xe7, + 0x12, 0xf9, 0x3b, 0x67, 0xe7, 0x46, 0x76, 0xec, 0xf1, 0x11, 0xd8, 0xb4, 0x4f, 0xc1, 0xc1, 0x9f, + 0xa2, 0x8f, 0x62, 0xe4, 0x76, 0xa3, 0xd3, 0x79, 0xdc, 0xc8, 0x69, 0x79, 0x74, 0x76, 0x6e, 0x2c, + 0xa9, 0x8d, 0x99, 0xa1, 0xec, 0xd5, 0x0e, 0xf7, 0x1a, 0x8f, 0x73, 0x0b, 0x8a, 0x62, 0x07, 0x26, + 0x5d, 0xfc, 0x10, 0xad, 0xc7, 0x28, 0xcf, 0x9a, 0x9d, 0x6f, 0xf6, 0xcd, 0xda, 0xb3, 0x5c, 0x32, + 0xbf, 0x72, 0x76, 0x6e, 0xa4, 0x4f, 0xa9, 0x38, 0x72, 0x18, 0x39, 0x9d, 0x51, 0xea, 0xb6, 0xf6, + 0x6b, 0x9d, 0x46, 0x2e, 0xa5, 0x94, 0xc6, 0x23, 0x87, 0x08, 0x98, 0x31, 0x3f, 0x3d, 0xb6, 0x73, + 0x8b, 0xca, 0x7c, 0xac, 0x71, 0xfc, 0x08, 0xdd, 0x8b, 0x91, 0x6b, 0x9d, 0x8e, 0xd9, 0xac, 0x77, + 0x3b, 0x8d, 0x76, 0x6e, 0x29, 0xbf, 0x76, 0x76, 0x6e, 0xa0, 0x60, 0x8d, 0x68, 0x6f, 0x2c, 0x80, + 0xd7, 0xe1, 0xd5, 0x55, 0x41, 0xbb, 0xbc, 0x2a, 0x68, 0x7f, 0x5f, 0x15, 0xb4, 0x9f, 0xae, 0x0b, + 0x89, 0xcb, 0xeb, 0x42, 0xe2, 0xaf, 0xeb, 0x42, 0x02, 0x6d, 0x52, 0xff, 0x86, 0x81, 0xb7, 0xb4, + 0xe7, 0x95, 0x01, 0x15, 0x47, 0xe3, 0x5e, 0xc5, 0xf6, 0x87, 0xd5, 0x29, 0x69, 0x8b, 0xfa, 0xb1, + 0xa8, 0xfa, 0x32, 0xfa, 0x00, 0xea, 0x2d, 0xc9, 0xcf, 0x8d, 0x2f, 0xfe, 0x0d, 0x00, 0x00, 0xff, + 0xff, 0x5f, 0x52, 0x0e, 0xab, 0x1e, 0x09, 0x00, 0x00, } func (this *MarketDetails) Equal(that interface{}) bool { From 1863befb30c182b32d81dcde2c60eca2a8b222ce Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 10 Oct 2023 13:23:26 -0600 Subject: [PATCH 275/309] [1658]: Move some of the generic unit test helper functions into helpers_test.go. --- x/exchange/fulfillment_test.go | 12 ---- x/exchange/helpers_test.go | 66 +++++++++++++++++++ x/exchange/market_test.go | 6 -- x/exchange/msg_test.go | 1 + x/exchange/orders_test.go | 113 ++++++++++----------------------- 5 files changed, 101 insertions(+), 97 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index c39087f035..bba53eaa3d 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -25,18 +25,6 @@ import ( // So when an object has an sdkmath.Int that should have been reduced to zero, you'll need to use this. var ZeroAmtAfterSub = sdkmath.NewInt(1).SubRaw(1) -// copySlice copies a slice using the provided copier for each entry. -func copySlice[T any](vals []T, copier func(T) T) []T { - if vals == nil { - return nil - } - rv := make([]T, len(vals)) - for i, v := range vals { - rv[i] = copier(v) - } - return rv -} - // copyDistribution copies a distribution. func copyDistribution(dist *distribution) *distribution { if dist == nil { diff --git a/x/exchange/helpers_test.go b/x/exchange/helpers_test.go index 0a1f1ec224..69165546c0 100644 --- a/x/exchange/helpers_test.go +++ b/x/exchange/helpers_test.go @@ -1,7 +1,9 @@ package exchange import ( + "fmt" "regexp" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -9,9 +11,11 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/provenance-io/provenance/testutil/assertions" ) +// amtRx is a regex matching characters that can be removed from an amount string. var amtRx = regexp.MustCompile(`[,_ ]`) // newInt converts the provided string into an Int, stipping out any commas, underscores or spaces first. @@ -22,6 +26,68 @@ func newInt(t *testing.T, amount string) sdkmath.Int { return rv } +// copySlice copies a slice using the provided copier for each entry. +func copySlice[T any](vals []T, copier func(T) T) []T { + if vals == nil { + return nil + } + rv := make([]T, len(vals)) + for i, v := range vals { + rv[i] = copier(v) + } + return rv +} + +// joinErrs joines the provided error strings into a single one to match what errors.Join does. +func joinErrs(errs ...string) string { + return strings.Join(errs, "\n") +} + +// copySDKInt creates a copy of the provided sdkmath.Int +func copySDKInt(i sdkmath.Int) (copy sdkmath.Int) { + defer func() { + if r := recover(); r != nil { + copy = sdkmath.Int{} + } + }() + return i.AddRaw(0) +} + +// copyCoins creates a copy of the provided coins slice with copies of each entry. +func copyCoins(coins sdk.Coins) sdk.Coins { + return copySlice(coins, copyCoin) +} + +// copyCoin returns a copy of the provided coin. +func copyCoin(coin sdk.Coin) sdk.Coin { + return sdk.Coin{Denom: coin.Denom, Amount: copySDKInt(coin.Amount)} +} + +// copyCoinP returns a copy of the provided *coin. +func copyCoinP(coin *sdk.Coin) *sdk.Coin { + if coin == nil { + return nil + } + rv := copyCoin(*coin) + return &rv +} + +// coinPString returns either "nil" or the quoted string version of the provided coins. +func coinPString(coin *sdk.Coin) string { + if coin == nil { + return "nil" + } + return fmt.Sprintf("%q", coin) +} + +// coinsString returns either "nil" or the quoted string version of the provided coins. +func coinsString(coins sdk.Coins) string { + if coins == nil { + return "nil" + } + return fmt.Sprintf("%q", coins) +} + func TestEqualsUint64(t *testing.T) { tests := []struct { name string diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index e5372085ba..6e34964b0c 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -17,11 +17,6 @@ import ( "github.com/provenance-io/provenance/testutil/assertions" ) -// joinErrs joines the provided error strings into a single one to match what errors.Join does. -func joinErrs(errs ...string) string { - return strings.Join(errs, "\n") -} - func TestMarket_Validate(t *testing.T) { coins := func(coins string) sdk.Coins { rv, err := sdk.ParseCoinsNormalized(coins) @@ -294,7 +289,6 @@ func TestValidateFeeOptions(t *testing.T) { } func TestMarketDetails_Validate(t *testing.T) { - nameErr := func(over int) string { return fmt.Sprintf("name length %d exceeds maximum length of %d", MaxName+over, MaxName) } diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 1927f1ee3b..220352248b 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -10,6 +10,7 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/provenance-io/provenance/testutil/assertions" ) diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index 157642924a..c375f67102 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -70,51 +70,6 @@ func copyBidOrder(bidOrder *BidOrder) *BidOrder { } } -// copySDKInt creates a copy of the provided sdkmath.Int -func copySDKInt(i sdkmath.Int) (copy sdkmath.Int) { - defer func() { - if r := recover(); r != nil { - copy = sdkmath.Int{} - } - }() - return i.AddRaw(0) -} - -// copyCoins creates a copy of the provided coins slice with copies of each entry. -func copyCoins(coins sdk.Coins) sdk.Coins { - return copySlice(coins, copyCoin) -} - -// copyCoin returns a copy of the provided coin. -func copyCoin(coin sdk.Coin) sdk.Coin { - return sdk.Coin{Denom: coin.Denom, Amount: copySDKInt(coin.Amount)} -} - -// copyCoinP returns a copy of the provided *coin. -func copyCoinP(coin *sdk.Coin) *sdk.Coin { - if coin == nil { - return nil - } - rv := copyCoin(*coin) - return &rv -} - -// coinPString returns either "nil" or the quoted string version of the provided coins. -func coinPString(coin *sdk.Coin) string { - if coin == nil { - return "nil" - } - return fmt.Sprintf("%q", coin) -} - -// coinsString returns either "nil" or the quoted string version of the provided coins. -func coinsString(coins sdk.Coins) string { - if coins == nil { - return "nil" - } - return fmt.Sprintf("%q", coins) -} - // orderString is similar to %v except with easier to understand Coin and Int entries. func orderString(order *Order) string { if order == nil { @@ -173,6 +128,40 @@ func bidOrderString(bidOrder *BidOrder) string { return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) } +// unknownOrderType is thing that implements isOrder_Order so it can be +// added to an Order, but isn't a type that there is anything defined for. +type unknownOrderType struct{} + +var _ isOrder_Order = (*unknownOrderType)(nil) + +func (o *unknownOrderType) isOrder_Order() {} +func (o *unknownOrderType) MarshalTo([]byte) (int, error) { + return 0, nil +} +func (o *unknownOrderType) Size() int { + return 0 +} + +// newUnknownOrder returns a new order with the given id and an unknownOrderType. +func newUnknownOrder(orderID uint64) *Order { + return &Order{OrderId: orderID, Order: &unknownOrderType{}} +} + +// badSubTypeErr creates the expected error when a sub-order type is bad. +func badSubTypeErr(orderID uint64, badType string) string { + return fmt.Sprintf("order %d has unknown sub-order type %s: does not implement SubOrderI", orderID, badType) +} + +// nilSubTypeErr creates the expected error when a sub-order type is nil. +func nilSubTypeErr(orderID uint64) string { + return badSubTypeErr(orderID, "") +} + +// unknownSubTypeErr creates the expected error when a sub-order type is the unknownOrderType. +func unknownSubTypeErr(orderID uint64) string { + return badSubTypeErr(orderID, "*exchange.unknownOrderType") +} + func TestOrderTypesAndBytes(t *testing.T) { values := []struct { name string @@ -538,25 +527,6 @@ func TestOrder_WithBid(t *testing.T) { assert.Equal(t, origBid, orderBid, "the bid in the resulting order (actual) vs what the bid was before being provided (expected)") } -// unknownOrderType is thing that implements isOrder_Order so it can be -// added to an Order, but isn't a type that there is anything defined for. -type unknownOrderType struct{} - -var _ isOrder_Order = (*unknownOrderType)(nil) - -func (o *unknownOrderType) isOrder_Order() {} -func (o *unknownOrderType) MarshalTo([]byte) (int, error) { - return 0, nil -} -func (o *unknownOrderType) Size() int { - return 0 -} - -// newUnknownOrder returns a new order with the given id and an unknownOrderType. -func newUnknownOrder(orderID uint64) *Order { - return &Order{OrderId: orderID, Order: &unknownOrderType{}} -} - func TestOrder_IsAskOrder(t *testing.T) { tests := []struct { name string @@ -662,21 +632,6 @@ func TestOrder_GetOrderID(t *testing.T) { } } -// badSubTypeErr creates the expected error when a sub-order type is bad. -func badSubTypeErr(orderID uint64, badType string) string { - return fmt.Sprintf("order %d has unknown sub-order type %s: does not implement SubOrderI", orderID, badType) -} - -// nilSubTypeErr creates the expected error when a sub-order type is nil. -func nilSubTypeErr(orderID uint64) string { - return badSubTypeErr(orderID, "") -} - -// unknownSubTypeErr creates the expected error when a sub-order type is the unknownOrderType. -func unknownSubTypeErr(orderID uint64) string { - return badSubTypeErr(orderID, "*exchange.unknownOrderType") -} - func TestOrder_GetSubOrder(t *testing.T) { askOrder := &AskOrder{ MarketId: 1, From d22f41aa0069baf23e9ca4367431f326afcf56f5 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 10 Oct 2023 14:57:52 -0600 Subject: [PATCH 276/309] [1658]: Prevent an admin from revoking their own ability to manage permissions. --- x/exchange/msg.go | 7 +++++++ x/exchange/msg_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 1bb37ccdcf..8cd13b79c7 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -307,6 +307,9 @@ func (m MsgMarketManagePermissionsRequest) ValidateBasic() error { errs = append(errs, fmt.Errorf("invalid revoke-all address %q: %w", addrStr, err)) } } + if ContainsString(m.RevokeAll, m.Admin) { + errs = append(errs, fmt.Errorf("message administrator %s cannot revoke all of their permissions", m.Admin)) + } if err := ValidateAccessGrantsField("to-revoke", m.ToRevoke); err != nil { errs = append(errs, err) @@ -317,6 +320,10 @@ func (m MsgMarketManagePermissionsRequest) ValidateBasic() error { if ContainsString(m.RevokeAll, ag.Address) { errs = append(errs, fmt.Errorf("address %s appears in both the revoke-all and to-revoke fields", ag.Address)) } + if ag.Address == m.Admin && ag.Contains(Permission_permissions) { + errs = append(errs, fmt.Errorf("message administrator %s cannot revoke their own ability to manage permissions", + ag.Address)) + } toRevokeByAddr[ag.Address] = ag } diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 220352248b..311d8e98fa 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -1317,6 +1317,43 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { "address " + goodAddr2 + " has both revoke and grant \"permissions\"", }, }, + { + name: "admin in revoke-all", + msg: MsgMarketManagePermissionsRequest{ + Admin: goodAdminAddr, + MarketId: 1, + RevokeAll: []string{goodAddr1, goodAdminAddr, goodAddr2}, + }, + expErr: []string{"message administrator " + goodAdminAddr + " cannot revoke all of their permissions"}, + }, + { + name: "admin revoking all their permissions except permissions", + msg: MsgMarketManagePermissionsRequest{ + Admin: goodAdminAddr, + MarketId: 1, + ToRevoke: []AccessGrant{ + { + Address: goodAdminAddr, + Permissions: []Permission{Permission_settle, Permission_cancel, + Permission_withdraw, Permission_update, Permission_attributes}, + }, + }, + }, + expErr: nil, + }, + { + name: "admin revoking own ability to manage permissions", + msg: MsgMarketManagePermissionsRequest{ + Admin: goodAdminAddr, + MarketId: 1, + ToRevoke: []AccessGrant{ + {Address: goodAddr1, Permissions: []Permission{Permission_permissions}}, + {Address: goodAdminAddr, Permissions: []Permission{Permission_permissions}}, + {Address: goodAddr2, Permissions: []Permission{Permission_permissions}}, + }, + }, + expErr: []string{"message administrator " + goodAdminAddr + " cannot revoke their own ability to manage permissions"}, + }, { name: "multiple errs", msg: MsgMarketManagePermissionsRequest{ From a4a9d24d8a6ea0d0fab7e618ee330c1a4d405d1f Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 10 Oct 2023 16:06:49 -0600 Subject: [PATCH 277/309] [1658]: Reorg the module.go file a bit. --- x/exchange/module/module.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/x/exchange/module/module.go b/x/exchange/module/module.go index 646a1a8341..e25c059426 100644 --- a/x/exchange/module/module.go +++ b/x/exchange/module/module.go @@ -25,23 +25,11 @@ import ( ) var ( - _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} _ module.AppModuleSimulation = AppModule{} ) -type AppModule struct { - AppModuleBasic - keeper keeper.Keeper -} - -func NewAppModule(cdc codec.Codec, exchangeKeeper keeper.Keeper) AppModule { - return AppModule{ - AppModuleBasic: AppModuleBasic{cdc: cdc}, - keeper: exchangeKeeper, - } -} - type AppModuleBasic struct { cdc codec.Codec } @@ -89,6 +77,18 @@ func (AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) { // RegisterLegacyAminoCodec registers the exchange module's types for the given codec. func (AppModuleBasic) RegisterLegacyAminoCodec(_ *codec.LegacyAmino) {} +type AppModule struct { + AppModuleBasic + keeper keeper.Keeper +} + +func NewAppModule(cdc codec.Codec, exchangeKeeper keeper.Keeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{cdc: cdc}, + keeper: exchangeKeeper, + } +} + // RegisterInvariants registers the invariants for the exchange module. func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} From 9cd198759abfa416c4e9cff71bbb76cdd90a7a23 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 10 Oct 2023 16:26:39 -0600 Subject: [PATCH 278/309] [1658]: Add a changelog entry. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b2f5d4104..78ebc87505 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* Create the `x/exchange` module which facilitates the buying and selling of assets [#1658](https://github.com/provenance-io/provenance/issues/1658). + Assets and funds remain in their owner's account (with a hold on them) until the order is settled (or cancelled). + Market's are created to manage order matching and define fees. + The chain will receive a portion of the fees a market collects. * Allow marker's transfer authority to prevent transfer of restricted coin with deny list on send [#1518](https://github.com/provenance-io/provenance/issues/1518). * Add net asset value to markers [#1328](https://github.com/provenance-io/provenance/issues/1328). * Add ICQHost and Oracle module to allow cross chain oracle queries [#1497](https://github.com/provenance-io/provenance/issues/1497). From 444982ef3a4b34b0c496acbfc9795bb15ac239fd Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 11 Oct 2023 09:22:51 -0600 Subject: [PATCH 279/309] [1658]: Don't allow a single star wildcard in required attributes since that's exactly the same as having an empty list. --- docs/proto-docs.md | 8 ++------ proto/provenance/exchange/v1/market.proto | 4 ---- x/exchange/market.go | 8 -------- x/exchange/market.pb.go | 4 ---- x/exchange/market_test.go | 6 +++--- 5 files changed, 5 insertions(+), 25 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 899b6bc907..0b03ea9e06 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1663,14 +1663,10 @@ Market contains all information about a market. | `access_grants` | [AccessGrant](#provenance.exchange.v1.AccessGrant) | repeated | access_grants is the list of addresses and permissions granted for this market. | | `req_attr_create_ask` | [string](#string) | repeated | req_attr_create_ask is a list of attributes required on an account for it to be allowed to create an ask order. An account must have all of these attributes in order to create an ask order in this market. If the list is empty, any account can create ask orders in this market. -An entry that starts with "*." will match any attributes that end with the rest of it. E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "b.x.a", or "c.b.a.x". - -An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. | +An entry that starts with "*." will match any attributes that end with the rest of it. E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "b.x.a", or "c.b.a.x". | | `req_attr_create_bid` | [string](#string) | repeated | req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. An account must have all of these attributes in order to create a bid order in this market. If the list is empty, any account can create bid orders in this market. -An entry that starts with "*." will match any attributes that end with the rest of it. E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "c.b.x.a", or "c.b.a.x". - -An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. | +An entry that starts with "*." will match any attributes that end with the rest of it. E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "c.b.x.a", or "c.b.a.x". | diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index f9221cb860..d53135c51b 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -92,8 +92,6 @@ message Market { // // An entry that starts with "*." will match any attributes that end with the rest of it. // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "b.x.a", or "c.b.a.x". - // - // An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. repeated string req_attr_create_ask = 12; // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. // An account must have all of these attributes in order to create a bid order in this market. @@ -101,8 +99,6 @@ message Market { // // An entry that starts with "*." will match any attributes that end with the rest of it. // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "c.b.x.a", or "c.b.a.x". - // - // An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. repeated string req_attr_create_bid = 13; } diff --git a/x/exchange/market.go b/x/exchange/market.go index fd917f01e8..6e815944f6 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -613,11 +613,6 @@ func ValidateAddRemoveReqAttrs(field string, toAdd, toRemove []string) error { // IsValidReqAttr returns true if the provided string is a valid required attribute entry. // Assumes that the provided reqAttr has already been normalized. func IsValidReqAttr(reqAttr string) bool { - // Allow it to just be the wildcard character. - if reqAttr == "*" { - return true - } - // A leading wildcard segment is valid for us, but not the name module. So, remove it if it's there. reqAttr = strings.TrimPrefix(reqAttr, "*.") @@ -658,9 +653,6 @@ func IsReqAttrMatch(reqAttr, accAttr string) bool { if len(reqAttr) == 0 || len(accAttr) == 0 { return false } - if reqAttr == "*" { - return true - } if strings.HasPrefix(reqAttr, "*.") { // reqAttr[1:] is used here (instead of [2:]) because we need that . to be // part of the match. Otherwise "*.b.a" would match "c.b.a" as well as "c.evilb.a". diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index ea0caf8a0d..2121da6eb0 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -296,8 +296,6 @@ type Market struct { // // An entry that starts with "*." will match any attributes that end with the rest of it. // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "b.x.a", or "c.b.a.x". - // - // An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. ReqAttrCreateAsk []string `protobuf:"bytes,12,rep,name=req_attr_create_ask,json=reqAttrCreateAsk,proto3" json:"req_attr_create_ask,omitempty"` // req_attr_create_ask is a list of attributes required on an account for it to be allowed to create a bid order. // An account must have all of these attributes in order to create a bid order in this market. @@ -305,8 +303,6 @@ type Market struct { // // An entry that starts with "*." will match any attributes that end with the rest of it. // E.g. "*.b.a" will match all of "c.b.a", "x.b.a", and "e.d.c.b.a"; but not "b.a", "xb.a", "c.b.x.a", or "c.b.a.x". - // - // An entry of exactly "*" will match any attribute, which is equivalent to leaving this list empty. ReqAttrCreateBid []string `protobuf:"bytes,13,rep,name=req_attr_create_bid,json=reqAttrCreateBid,proto3" json:"req_attr_create_bid,omitempty"` } diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index 6e34964b0c..f230a9c273 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -3630,7 +3630,7 @@ func TestIsValidReqAttr(t *testing.T) { {name: "already valid and normalized", reqAttr: "x.y.z", exp: true}, {name: "already valid but not normalized", reqAttr: " x . y . z ", exp: false}, {name: "invalid character", reqAttr: "x._y.z", exp: false}, - {name: "just the wildcard", reqAttr: "*", exp: true}, + {name: "just the wildcard", reqAttr: "*", exp: false}, {name: "just the wildcard not normalized", reqAttr: " * ", exp: false}, {name: "just star dot", reqAttr: "*.", exp: false}, {name: "star dot valid", reqAttr: "*.x.y.z", exp: true}, @@ -4157,10 +4157,10 @@ func TestIsReqAttrMatch(t *testing.T) { exp: false, }, { - name: "just a star: account attribute has value", + name: "just a star", reqAttr: "*", accAttr: "penny.dime.quarter", - exp: true, + exp: false, }, } From 5b5e61eac4d81139565fbd9e751373ce84749a02 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 11 Oct 2023 12:31:37 -0600 Subject: [PATCH 280/309] [1658]: Fix a few comments. --- x/exchange/orders.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 97fb8e3d84..10caa617f1 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -218,9 +218,6 @@ func (o Order) Validate() error { } // Split splits this order by the provided assets filled. -// If assets filled is zero, this order is returned as unfilled. -// If assets filled equals this order's assets, this order is returned as filled. -// Otherwise, two new orders are returned (copied from this order) with the appropriate assets, price, and fees. func (o Order) Split(assetsFilledAmt sdkmath.Int) (filled *Order, unfilled *Order, err error) { orderAssets := o.GetAssets() orderAssetsAmt := orderAssets.Amount @@ -501,7 +498,7 @@ func (b BidOrder) CopyChange(newAssets, newPrice sdk.Coin, newFees sdk.Coins) *B } // FilledOrder holds an order that has been filled (either in full or partially). -// The price and fees will indicate the amounts actually involved in the fulfillment. +// The GetPrice() and GetSettlementFees() methods indicate the actual amounts involved in the fulfillment. type FilledOrder struct { order *Order actualPrice sdk.Coin From d45ba46811c5b9ac0d71be88ae0e4fb319735660 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 11 Oct 2023 13:16:37 -0600 Subject: [PATCH 281/309] [1658]: Add a last_order_id field to the genesis state. --- docs/proto-docs.md | 1 + proto/provenance/exchange/v1/genesis.proto | 3 ++ x/exchange/genesis.go | 10 ++++ x/exchange/genesis.pb.go | 47 ++++++++++++++---- x/exchange/genesis_test.go | 55 ++++++++++++++++++++++ x/exchange/keeper/genesis.go | 6 ++- 6 files changed, 113 insertions(+), 9 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 0b03ea9e06..abd741cd0e 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1891,6 +1891,7 @@ GenesisState is the data that should be loaded into the exchange module during g | `markets` | [Market](#provenance.exchange.v1.Market) | repeated | markets are all of the markets to create at genesis. | | `orders` | [Order](#provenance.exchange.v1.Order) | repeated | orders are all the orders to create at genesis. | | `last_market_id` | [uint32](#uint32) | | last_market_id is the value of the last auto-selected market id. | +| `last_order_id` | [uint64](#uint64) | | last_order_id is the value of the last order id created. | diff --git a/proto/provenance/exchange/v1/genesis.proto b/proto/provenance/exchange/v1/genesis.proto index aca884d657..3ccf87d24d 100644 --- a/proto/provenance/exchange/v1/genesis.proto +++ b/proto/provenance/exchange/v1/genesis.proto @@ -27,4 +27,7 @@ message GenesisState { // last_market_id is the value of the last auto-selected market id. uint32 last_market_id = 4; + + // last_order_id is the value of the last order id created. + uint64 last_order_id = 5; } \ No newline at end of file diff --git a/x/exchange/genesis.go b/x/exchange/genesis.go index af65dbaef9..97d7c9bdc2 100644 --- a/x/exchange/genesis.go +++ b/x/exchange/genesis.go @@ -38,6 +38,7 @@ func (g GenesisState) Validate() error { } } + maxOrderID := uint64(0) orderIDs := make(map[uint64]int) for i, order := range g.Orders { if order.OrderId != 0 { @@ -58,6 +59,15 @@ func (g GenesisState) Validate() error { if !knownMarket { errs = append(errs, fmt.Errorf("invalid order[%d]: unknown market id %d", i, order.GetMarketID())) } + + if order.OrderId > maxOrderID { + maxOrderID = order.OrderId + } + } + + if g.LastOrderId < maxOrderID { + errs = append(errs, fmt.Errorf("last order id %d is less than the largest id in the provided orders %d", + g.LastOrderId, maxOrderID)) } // No validation to do on LastMarketId. diff --git a/x/exchange/genesis.pb.go b/x/exchange/genesis.pb.go index 8417f9d9e6..dc3556a30f 100644 --- a/x/exchange/genesis.pb.go +++ b/x/exchange/genesis.pb.go @@ -33,6 +33,8 @@ type GenesisState struct { Orders []Order `protobuf:"bytes,3,rep,name=orders,proto3" json:"orders"` // last_market_id is the value of the last auto-selected market id. LastMarketId uint32 `protobuf:"varint,4,opt,name=last_market_id,json=lastMarketId,proto3" json:"last_market_id,omitempty"` + // last_order_id is the value of the last order id created. + LastOrderId uint64 `protobuf:"varint,5,opt,name=last_order_id,json=lastOrderId,proto3" json:"last_order_id,omitempty"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -77,26 +79,28 @@ func init() { } var fileDescriptor_087ceebafabf03c9 = []byte{ - // 301 bytes of a gzipped FileDescriptorProto + // 324 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x29, 0x28, 0xca, 0x2f, 0x4b, 0xcd, 0x4b, 0xcc, 0x4b, 0x4e, 0xd5, 0x4f, 0xad, 0x48, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0xd5, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x43, 0xa8, 0xd2, 0x83, 0xa9, 0xd2, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x2b, 0xd1, 0x07, 0xb1, 0x20, 0xaa, 0xa5, 0x94, 0x71, 0x98, 0x99, 0x9b, 0x58, 0x94, 0x9d, 0x5a, 0x42, 0x40, 0x51, 0x7e, 0x51, 0x4a, 0x6a, 0x51, 0x31, 0x01, 0x45, 0x05, 0x89, 0x45, 0x89, - 0xb9, 0x50, 0x45, 0x4a, 0x9f, 0x19, 0xb9, 0x78, 0xdc, 0x21, 0xce, 0x0d, 0x2e, 0x49, 0x2c, 0x49, + 0xb9, 0x50, 0x45, 0x4a, 0xd3, 0x99, 0xb8, 0x78, 0xdc, 0x21, 0xce, 0x0d, 0x2e, 0x49, 0x2c, 0x49, 0x15, 0x32, 0xe3, 0x62, 0x83, 0x28, 0x90, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd3, 0xc3, 0xee, 0x7c, 0xbd, 0x00, 0xb0, 0xaa, 0x20, 0xa8, 0x6a, 0x21, 0x3b, 0x2e, 0x76, 0x88, 0x13, 0x8b, 0x25, 0x98, 0x14, 0x98, 0xf1, 0x69, 0xf4, 0x05, 0x2b, 0x73, 0x62, 0x39, 0x71, 0x4f, 0x9e, 0x21, 0x08, 0xa6, 0x49, 0xc8, 0x9a, 0x8b, 0x0d, 0xe2, 0x7a, 0x09, 0x66, 0xb0, 0x76, 0x59, 0x5c, 0xda, 0xfd, 0x41, 0xaa, 0xa0, 0xba, 0xa1, 0x5a, 0x84, 0x54, 0xb8, 0xf8, 0x72, 0x12, 0x8b, 0x4b, 0xe2, 0x21, 0x86, 0xc5, 0x67, 0xa6, 0x48, 0xb0, 0x28, 0x30, 0x6a, 0xf0, 0x06, 0xf1, 0x80, 0x44, 0x21, - 0xf6, 0x79, 0xa6, 0x58, 0x71, 0x74, 0x2c, 0x90, 0x67, 0x78, 0xb1, 0x40, 0x9e, 0xc1, 0x29, 0xf5, - 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, - 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xb8, 0x24, 0x33, 0xf3, 0x71, 0x58, 0x1c, - 0xc0, 0x18, 0xa5, 0x97, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x8f, 0x50, - 0xa4, 0x9b, 0x99, 0x8f, 0xc4, 0xd3, 0xaf, 0x80, 0x07, 0x76, 0x12, 0x1b, 0x38, 0x8c, 0x8d, 0x01, - 0x01, 0x00, 0x00, 0xff, 0xff, 0x74, 0x34, 0x75, 0x11, 0x28, 0x02, 0x00, 0x00, + 0xf6, 0x79, 0xa6, 0x08, 0x29, 0x71, 0xf1, 0x82, 0x55, 0x81, 0x35, 0x81, 0x14, 0xb1, 0x2a, 0x30, + 0x6a, 0xb0, 0x04, 0x71, 0x83, 0x04, 0xc1, 0xa6, 0x7a, 0xa6, 0x58, 0x71, 0x74, 0x2c, 0x90, 0x67, + 0x78, 0xb1, 0x40, 0x9e, 0xc1, 0x29, 0xf5, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, + 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, + 0xb8, 0x24, 0x33, 0xf3, 0x71, 0x38, 0x2e, 0x80, 0x31, 0x4a, 0x2f, 0x3d, 0xb3, 0x24, 0xa3, 0x34, + 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0xa1, 0x48, 0x37, 0x33, 0x1f, 0x89, 0xa7, 0x5f, 0x01, 0x8f, + 0x90, 0x24, 0x36, 0x70, 0x3c, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x92, 0x09, 0xe8, 0xf5, + 0x4c, 0x02, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -119,6 +123,11 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.LastOrderId != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.LastOrderId)) + i-- + dAtA[i] = 0x28 + } if m.LastMarketId != 0 { i = encodeVarintGenesis(dAtA, i, uint64(m.LastMarketId)) i-- @@ -203,6 +212,9 @@ func (m *GenesisState) Size() (n int) { if m.LastMarketId != 0 { n += 1 + sovGenesis(uint64(m.LastMarketId)) } + if m.LastOrderId != 0 { + n += 1 + sovGenesis(uint64(m.LastOrderId)) + } return n } @@ -364,6 +376,25 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { break } } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LastOrderId", wireType) + } + m.LastOrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LastOrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/x/exchange/genesis_test.go b/x/exchange/genesis_test.go index 86769835ce..6db54503e0 100644 --- a/x/exchange/genesis_test.go +++ b/x/exchange/genesis_test.go @@ -105,6 +105,7 @@ func TestGenesisState_Validate(t *testing.T) { bidOrder(8, 3, "5wong", "50nibbler"), bidOrder(9, 3, "5wong", "50nibbler"), }, + LastOrderId: 9, }, expErr: nil, }, @@ -177,6 +178,7 @@ func TestGenesisState_Validate(t *testing.T) { bidOrder(2, 2, "28fry", "2bender"), askOrder(3, 3, "28fry", "2bender"), }, + LastOrderId: 3, }, expErr: []string{ `invalid order[0]: unknown market id 1`, @@ -193,6 +195,7 @@ func TestGenesisState_Validate(t *testing.T) { bidOrder(1, 5, "28fry", "2bender"), askOrder(1, 6, "28fry", "2bender"), }, + LastOrderId: 1, }, expErr: []string{ `invalid order[1]: duplicate order id 1 seen at [0]`, @@ -213,11 +216,13 @@ func TestGenesisState_Validate(t *testing.T) { bidOrder(2, 4, "28fry", "2bender"), askOrder(3, 3, "28fry", "2bender"), }, + LastOrderId: 1, }, expErr: []string{ "invalid params: default split 10001 cannot be greater than 10000", `invalid market[1]: invalid create-bid flat fee option "-1zapp": negative coin amount: -1`, `invalid order[1]: unknown market id 4`, + "last order id 1 is less than the largest id in the provided orders 3", }, }, { @@ -225,6 +230,14 @@ func TestGenesisState_Validate(t *testing.T) { genState: GenesisState{LastMarketId: 1}, expErr: nil, }, + { + name: "last market id less than largest market id", + genState: GenesisState{ + Markets: []Market{{MarketId: 3}, {MarketId: 1}}, + LastMarketId: 1, + }, + expErr: nil, + }, { name: "last market id 256", genState: GenesisState{LastMarketId: 256}, @@ -245,6 +258,48 @@ func TestGenesisState_Validate(t *testing.T) { genState: GenesisState{LastMarketId: 4_294_967_295}, expErr: nil, }, + { + name: "last order id less than largest order id", + genState: GenesisState{ + Markets: []Market{{MarketId: 1}}, + Orders: []Order{ + askOrder(1, 1, "28fry", "2bender"), + bidOrder(88, 1, "28fry", "2bender"), + bidOrder(2, 1, "28fry", "2bender"), + askOrder(3, 1, "28fry", "2bender"), + }, + LastOrderId: 87, + }, + expErr: []string{"last order id 87 is less than the largest id in the provided orders 88"}, + }, + { + name: "last order id equals largest order id", + genState: GenesisState{ + Markets: []Market{{MarketId: 1}}, + Orders: []Order{ + askOrder(1, 1, "28fry", "2bender"), + bidOrder(88, 1, "28fry", "2bender"), + bidOrder(2, 1, "28fry", "2bender"), + askOrder(3, 1, "28fry", "2bender"), + }, + LastOrderId: 88, + }, + expErr: nil, + }, + { + name: "last order id more than largest order id", + genState: GenesisState{ + Markets: []Market{{MarketId: 1}}, + Orders: []Order{ + askOrder(1, 1, "28fry", "2bender"), + bidOrder(88, 1, "28fry", "2bender"), + bidOrder(2, 1, "28fry", "2bender"), + askOrder(3, 1, "28fry", "2bender"), + }, + LastOrderId: 89, + }, + expErr: nil, + }, } for _, tc := range tests { diff --git a/x/exchange/keeper/genesis.go b/x/exchange/keeper/genesis.go index cd3c5dfcda..afcff92f8a 100644 --- a/x/exchange/keeper/genesis.go +++ b/x/exchange/keeper/genesis.go @@ -38,6 +38,8 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *exchange.GenesisState) { amounts[addr] = amounts[addr].Add(order.GetHoldAmount()...) } + setLastOrderID(store, genState.LastOrderId) + // Make sure all the needed funds have holds on them. These should have been placed during initialization of the hold module. for _, addr := range addrs { for _, reqAmt := range amounts[addr] { @@ -54,9 +56,11 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *exchange.GenesisState) { // ExportGenesis creates a genesis state from the current state store. func (k Keeper) ExportGenesis(ctx sdk.Context) *exchange.GenesisState { + store := k.getStore(ctx) genState := &exchange.GenesisState{ Params: k.GetParams(ctx), - LastMarketId: getLastAutoMarketID(k.getStore(ctx)), + LastMarketId: getLastAutoMarketID(store), + LastOrderId: getLastOrderID(store), } k.IterateMarkets(ctx, func(market *exchange.Market) bool { From 56acefff5ae01262374f00706cb5186a0273bcbe Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 11 Oct 2023 14:09:44 -0600 Subject: [PATCH 282/309] [1658]: Create stringerJoin to clean up some of the unit test helper funcs. --- x/exchange/fulfillment_test.go | 39 +++++----------------------------- x/exchange/helpers_test.go | 12 +++++++++++ 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index bba53eaa3d..b1f433f13b 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -109,14 +109,7 @@ func distributionString(dist *distribution) string { // distributionsString is similar to %v except with easier to understand Int entries. func distributionsString(dists []*distribution) string { - if dists == nil { - return "nil" - } - vals := make([]string, len(dists)) - for i, d := range dists { - vals[i] = distributionString(d) - } - return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) + return stringerJoin(dists, distributionString, ", ") } // orderFulfillmentString is similar to %v except with easier to understand Coin and Int entries. @@ -140,15 +133,7 @@ func orderFulfillmentString(f *orderFulfillment) string { // orderFulfillmentsString is similar to %v except with easier to understand Coin entries. func orderFulfillmentsString(ofs []*orderFulfillment) string { - if ofs == nil { - return "nil" - } - - vals := make([]string, len(ofs)) - for i, f := range ofs { - vals[i] = orderFulfillmentString(f) - } - return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) + return stringerJoin(ofs, orderFulfillmentString, ", ") } // bankInputString is similar to %v except with easier to understand Coin entries. @@ -158,14 +143,7 @@ func bankInputString(i banktypes.Input) string { // bankInputsString returns a string with all the provided inputs. func bankInputsString(ins []banktypes.Input) string { - if ins == nil { - return "nil" - } - vals := make([]string, len(ins)) - for i, input := range ins { - vals[i] = bankInputString(input) - } - return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) + return stringerJoin(ins, bankInputString, ", ") } // bankOutputString is similar to %v except with easier to understand Coin entries. @@ -175,14 +153,7 @@ func bankOutputString(o banktypes.Output) string { // bankOutputsString returns a string with all the provided outputs. func bankOutputsString(outs []banktypes.Output) string { - if outs == nil { - return "nil" - } - vals := make([]string, len(outs)) - for i, output := range outs { - vals[i] = bankOutputString(output) - } - return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) + return stringerJoin(outs, bankOutputString, ", ") } // transferString is similar to %v except with easier to understand Coin entries. @@ -229,7 +200,7 @@ func indexedAddrAmtsString(i *indexedAddrAmts) string { for j, amt := range i.amts { amtsVals[j] = fmt.Sprintf("%q", amt) } - amts = fmt.Sprintf("[]%T{%s}", i.amts, strings.Join(amtsVals, ", ")) + amts = fmt.Sprintf("%T{%s}", i.amts, strings.Join(amtsVals, ", ")) } indexes := "nil" diff --git a/x/exchange/helpers_test.go b/x/exchange/helpers_test.go index 69165546c0..bf8b3cfa57 100644 --- a/x/exchange/helpers_test.go +++ b/x/exchange/helpers_test.go @@ -38,6 +38,18 @@ func copySlice[T any](vals []T, copier func(T) T) []T { return rv } +// stringerJoin runs the stringer on each of the provided vals and joins them using the provided separator. +func stringerJoin[T any](vals []T, stringer func(T) string, sep string) string { + if vals == nil { + return "nil" + } + strs := make([]string, len(vals)) + for i, val := range vals { + strs[i] = stringer(val) + } + return "[" + strings.Join(strs, sep) + "]" +} + // joinErrs joines the provided error strings into a single one to match what errors.Join does. func joinErrs(errs ...string) string { return strings.Join(errs, "\n") From 03d779b44afda3124a7f48c1bdc80a0ad607b17f Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 11 Oct 2023 14:18:03 -0600 Subject: [PATCH 283/309] [1658]: Tweak to assertEqualOrderFulfillmentSlices to the string comparison better. --- x/exchange/fulfillment_test.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index b1f433f13b..933ac6e963 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -286,16 +286,14 @@ func assertEqualOrderFulfillmentSlices(t *testing.T, expected, actual []*orderFu } // Try the comparisons as strings, one per line because that's easier with ints and coins. - expStrVals := make([]string, len(expected)) + expStrs := make([]string, len(expected)) for i, exp := range expected { - expStrVals[i] = orderFulfillmentString(exp) + expStrs[i] = orderFulfillmentString(exp) } - expStrs := strings.Join(expStrVals, "\n") - actStrVals := make([]string, len(actual)) + actStrs := make([]string, len(actual)) for i, act := range actual { - actStrVals[i] = orderFulfillmentString(act) + actStrs[i] = orderFulfillmentString(act) } - actStrs := strings.Join(actStrVals, "\n") if !assert.Equalf(t, expStrs, actStrs, msg("orderFulfillment strings"), args...) { // Wooo, should have actionable info in the failure, so we can be done. return false @@ -305,8 +303,8 @@ func assertEqualOrderFulfillmentSlices(t *testing.T, expected, actual []*orderFu for i := range expected { assertEqualOrderFulfillments(t, expected[i], actual[i], fmt.Sprintf("[%d]%s", i, message), args...) } - t.Logf(" Actual: %s", orderFulfillmentsString(actual)) - t.Logf("Expected: %s", orderFulfillmentsString(expected)) + t.Logf(" Actual:\n%s", strings.Join(expStrs, "\n")) + t.Logf("Expected:\n%s", strings.Join(actStrs, "\n")) return false } From 86d4d7b2291f8fd15fbca983e3f13242019b77a2 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 11 Oct 2023 14:21:16 -0600 Subject: [PATCH 284/309] [1658]: Fix a call to feeCoins in TestBuildSettlement that was missing it's tracer. --- x/exchange/fulfillment_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 933ac6e963..4a9d484810 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -329,7 +329,7 @@ func TestBuildSettlement(t *testing.T) { t.Fatalf("cannot create ask order %d with more than 1 fees %v", orderID, fees) } var fee *sdk.Coin - if fc := feeCoins("", fees); !fc.IsZero() { + if fc := feeCoins(fmt.Sprintf("ask order %d", orderID), fees); !fc.IsZero() { fee = &fc[0] } return NewOrder(orderID).WithAsk(&AskOrder{ From 7fa39e26543f3a4950f29665a66732c3d330f349 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 11 Oct 2023 14:42:41 -0600 Subject: [PATCH 285/309] [1658]: Create stringerLines and use it to clean up some more unit test stuff. --- x/exchange/fulfillment_test.go | 47 ++++++++++------------------------ x/exchange/helpers_test.go | 10 +++++++- 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 4a9d484810..5ddeac7c47 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -168,14 +168,7 @@ func transferString(t *Transfer) string { // transfersStringsLines creates a string for each transfer. func transfersStringsLines(ts []*Transfer) []string { - if ts == nil { - return nil - } - rv := make([]string, len(ts)) - for i, t := range ts { - rv[i] = transferString(t) - } - return rv + return stringerLines(ts, transferString) } // String converts a indexedAddrAmtsString to a string. @@ -272,28 +265,19 @@ func assertEqualOrderFulfillmentSlices(t *testing.T, expected, actual []*orderFu } // Check the order ids (and lengths) since that's gonna be a huge clue to a problem - expIDs := make([]string, len(expected)) - for i, exp := range expected { - expIDs[i] = fmt.Sprintf("%d", exp.GetOrderID()) - } - actIDs := make([]string, len(actual)) - for i, act := range actual { - actIDs[i] = fmt.Sprintf("%d", act.GetOrderID()) + idStringer := func(of *orderFulfillment) string { + return fmt.Sprintf("%d", of.GetOrderID()) } + expIDs := stringerLines(expected, idStringer) + actIDs := stringerLines(actual, idStringer) if !assert.Equalf(t, expIDs, actIDs, msg("OrderIDs"), args...) { // Wooo, should have actionable info in the failure, so we can be done. return false } // Try the comparisons as strings, one per line because that's easier with ints and coins. - expStrs := make([]string, len(expected)) - for i, exp := range expected { - expStrs[i] = orderFulfillmentString(exp) - } - actStrs := make([]string, len(actual)) - for i, act := range actual { - actStrs[i] = orderFulfillmentString(act) - } + expStrs := stringerLines(expected, orderFulfillmentString) + actStrs := stringerLines(actual, orderFulfillmentString) if !assert.Equalf(t, expStrs, actStrs, msg("orderFulfillment strings"), args...) { // Wooo, should have actionable info in the failure, so we can be done. return false @@ -1010,23 +994,20 @@ func TestBuildSettlement(t *testing.T) { assertions.RequireErrorValue(t, err, tc.expErr, "BuildSettlement error") if !assert.Equal(t, tc.expSettlement, settlement, "BuildSettlement result") { // Doing each field on its own now to try to help pinpoint the differences. - expTrans := transfersStringsLines(tc.expSettlement.Transfers) - actTrans := transfersStringsLines(settlement.Transfers) + expTrans := stringerLines(tc.expSettlement.Transfers, transferString) + actTrans := stringerLines(settlement.Transfers, transferString) assert.Equal(t, expTrans, actTrans, "Transfers (as strings)") expFeeInputs := bankInputsString(tc.expSettlement.FeeInputs) actFeeInputs := bankInputsString(settlement.FeeInputs) assert.Equal(t, expFeeInputs, actFeeInputs, "FeeInputs (as strings)") - expFilledIds := make([]uint64, len(tc.expSettlement.FullyFilledOrders)) - for i, fo := range tc.expSettlement.FullyFilledOrders { - expFilledIds[i] = fo.GetOrderID() - } - actFilledIds := make([]uint64, len(settlement.FullyFilledOrders)) - for i, fo := range settlement.FullyFilledOrders { - actFilledIds[i] = fo.GetOrderID() + idStringer := func(of *FilledOrder) string { + return fmt.Sprintf("%d", of.GetOrderID()) } - if assert.Equal(t, expFilledIds, actFilledIds, "FullyFilledOrders ids") { + expFilledIDs := stringerLines(tc.expSettlement.FullyFilledOrders, idStringer) + actFilledIDs := stringerLines(settlement.FullyFilledOrders, idStringer) + if assert.Equal(t, expFilledIDs, actFilledIDs, "FullyFilledOrders ids") { // If they're the same ids, compare each individually. for i := range tc.expSettlement.FullyFilledOrders { assert.Equal(t, tc.expSettlement.FullyFilledOrders[i], settlement.FullyFilledOrders[i], "FullyFilledOrders[%d]", i) diff --git a/x/exchange/helpers_test.go b/x/exchange/helpers_test.go index bf8b3cfa57..fd9b6d4beb 100644 --- a/x/exchange/helpers_test.go +++ b/x/exchange/helpers_test.go @@ -43,11 +43,19 @@ func stringerJoin[T any](vals []T, stringer func(T) string, sep string) string { if vals == nil { return "nil" } + return "[" + strings.Join(stringerLines(vals, stringer), sep) + "]" +} + +// stringerLines returns a slice where each of the vals has been converted using the given stringer. +func stringerLines[T any](vals []T, stringer func(T) string) []string { + if vals == nil { + return nil + } strs := make([]string, len(vals)) for i, val := range vals { strs[i] = stringer(val) } - return "[" + strings.Join(strs, sep) + "]" + return strs } // joinErrs joines the provided error strings into a single one to match what errors.Join does. From 9a19d4d0282c559472bd2130d934852014a1543e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 11 Oct 2023 14:58:15 -0600 Subject: [PATCH 286/309] [1658]: Delete some unwanted empty lines. --- x/exchange/fulfillment_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 5ddeac7c47..1b8e007781 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -2621,7 +2621,6 @@ func TestOrderFulfillment_DistributeAssets(t *testing.T) { rv.AssetDists = dists } return rv - } askOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*distribution) *orderFulfillment { order := NewOrder(orderID).WithAsk(&AskOrder{Assets: sdk.NewInt64Coin("apple", 999)}) @@ -2767,7 +2766,6 @@ func TestDistributeAssets(t *testing.T) { rv.AssetDists = dists } return rv - } askOF := func(orderID uint64, assetsUnfilled, assetsFilled int64, dists ...*distribution) *orderFulfillment { order := NewOrder(orderID).WithAsk(&AskOrder{ @@ -2908,7 +2906,6 @@ func TestOrderFulfillment_DistributePrice(t *testing.T) { rv.PriceDists = dists } return rv - } askOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*distribution) *orderFulfillment { order := NewOrder(orderID).WithAsk(&AskOrder{Price: sdk.NewInt64Coin("peach", 999)}) @@ -3054,7 +3051,6 @@ func TestDistributePrice(t *testing.T) { rv.PriceDists = dists } return rv - } askOF := func(orderID uint64, priceLeft, priceApplied int64, dists ...*distribution) *orderFulfillment { order := NewOrder(orderID).WithAsk(&AskOrder{ From 49c3bc5000e721eb3a7847a6e694e96ec1ca6ea5 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Wed, 11 Oct 2023 17:12:23 -0600 Subject: [PATCH 287/309] [1658]: Provide suggested sizes when making maps. --- x/exchange/genesis.go | 4 ++-- x/exchange/keeper/fulfillment.go | 8 ++++---- x/exchange/market.go | 20 ++++++++++---------- x/exchange/msg.go | 2 +- x/exchange/orders.go | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/x/exchange/genesis.go b/x/exchange/genesis.go index 97d7c9bdc2..1d1662cf16 100644 --- a/x/exchange/genesis.go +++ b/x/exchange/genesis.go @@ -19,7 +19,7 @@ func (g GenesisState) Validate() error { } } - marketIDs := make(map[uint32]int) + marketIDs := make(map[uint32]int, len(g.Markets)) for i, market := range g.Markets { if market.MarketId == 0 { errs = append(errs, fmt.Errorf("invalid market[%d]: market id cannot be zero", i)) @@ -39,7 +39,7 @@ func (g GenesisState) Validate() error { } maxOrderID := uint64(0) - orderIDs := make(map[uint64]int) + orderIDs := make(map[uint64]int, len(g.Orders)) for i, order := range g.Orders { if order.OrderId != 0 { j, seen := orderIDs[order.OrderId] diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index 45f2f0cedc..e6733c48bc 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -62,9 +62,9 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro var totalSellerFee sdk.Coins assetOutputs := make([]banktypes.Output, 0, len(msg.BidOrderIds)) priceInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)) - addrIndex := make(map[string]int) + addrIndex := make(map[string]int, len(msg.BidOrderIds)) feeInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)+1) - feeAddrIndex := make(map[string]int) + feeAddrIndex := make(map[string]int, len(msg.BidOrderIds)) filledOrders := make([]*exchange.FilledOrder, 0, len(msg.BidOrderIds)) for _, order := range orders { bidOrder := order.GetBidOrder() @@ -192,9 +192,9 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro var errs []error assetInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)) priceOutputs := make([]banktypes.Output, 0, len(msg.AskOrderIds)) - addrIndex := make(map[string]int) + addrIndex := make(map[string]int, len(msg.AskOrderIds)) feeInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)+1) - feeAddrIndex := make(map[string]int) + feeAddrIndex := make(map[string]int, len(msg.AskOrderIds)) filledOrders := make([]*exchange.FilledOrder, 0, len(msg.AskOrderIds)) for _, order := range orders { askOrder := order.GetAskOrder() diff --git a/x/exchange/market.go b/x/exchange/market.go index 6e815944f6..ff9cd863bf 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -52,8 +52,8 @@ func (m Market) Validate() error { // ValidateFeeOptions returns an error if any of the provide coin values is not a valid fee option. func ValidateFeeOptions(field string, options []sdk.Coin) error { var errs []error - denoms := make(map[string]bool) - dups := make(map[string]bool) + denoms := make(map[string]bool, len(options)) + dups := make(map[string]bool, len(options)) for _, coin := range options { if denoms[coin.Denom] { if !dups[coin.Denom] { @@ -115,7 +115,7 @@ func ValidateFeeRatios(sellerRatios, buyerRatios []FeeRatio) error { } // For the buyer ones, there can be multiple per price denom. - buyerPriceDenomsMap := make(map[string]bool) + buyerPriceDenomsMap := make(map[string]bool, len(buyerRatios)) buyerPriceDenoms := make([]string, 0) for _, ratio := range buyerRatios { if !buyerPriceDenomsMap[ratio.Price.Denom] { @@ -145,7 +145,7 @@ func ValidateSellerFeeRatios(ratios []FeeRatio) error { return nil } - seen := make(map[string]bool) + seen := make(map[string]bool, len(ratios)) dups := make(map[string]bool) var errs []error for _, ratio := range ratios { @@ -177,7 +177,7 @@ func ValidateBuyerFeeRatios(ratios []FeeRatio) error { return nil } - seen := make(map[string]bool) + seen := make(map[string]bool, len(ratios)) dups := make(map[string]bool) var errs []error for _, ratio := range ratios { @@ -337,14 +337,14 @@ func ValidateRatioDenoms(sellerRatios, buyerRatios []FeeRatio) []error { if len(sellerRatios) > 0 && len(buyerRatios) > 0 { // We only need to check the price denoms if *both* types have an entry. sellerPriceDenoms := make([]string, len(sellerRatios)) - sellerPriceDenomsKnown := make(map[string]bool) + sellerPriceDenomsKnown := make(map[string]bool, len(sellerRatios)) for i, ratio := range sellerRatios { sellerPriceDenoms[i] = ratio.Price.Denom sellerPriceDenomsKnown[ratio.Price.Denom] = true } buyerPriceDenoms := make([]string, 0, len(sellerRatios)) - buyerPriceDenomsKnown := make(map[string]bool) + buyerPriceDenomsKnown := make(map[string]bool, len(sellerRatios)) for _, ratio := range buyerRatios { if !buyerPriceDenomsKnown[ratio.Price.Denom] { buyerPriceDenoms = append(buyerPriceDenoms, ratio.Price.Denom) @@ -414,7 +414,7 @@ func ValidateAccessGrantsField(field string, accessGrants []AccessGrant) error { field += " " } errs := make([]error, len(accessGrants)) - seen := make(map[string]bool) + seen := make(map[string]bool, len(accessGrants)) dups := make(map[string]bool) for i, ag := range accessGrants { if seen[ag.Address] && !dups[ag.Address] { @@ -446,7 +446,7 @@ func (a AccessGrant) ValidateInField(field string) error { if len(a.Permissions) == 0 { return fmt.Errorf("invalid %saccess grant: no permissions provided for %s", field, a.Address) } - seen := make(map[Permission]bool) + seen := make(map[Permission]bool, len(a.Permissions)) for _, perm := range a.Permissions { if seen[perm] { return fmt.Errorf("invalid %saccess grant: %s appears multiple times for %s", field, perm.SimpleString(), a.Address) @@ -568,7 +568,7 @@ func ValidateReqAttrsAreNormalized(field string, attrs []string) error { // ValidateReqAttrs makes sure that each provided attribute is valid and that no duplicate entries are provided. func ValidateReqAttrs(field string, attrs []string) error { var errs []error - seen := make(map[string]bool) + seen := make(map[string]bool, len(attrs)) bad := make(map[string]bool) for _, attr := range attrs { normalized := nametypes.NormalizeName(attr) diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 8cd13b79c7..1d0bd7893c 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -315,7 +315,7 @@ func (m MsgMarketManagePermissionsRequest) ValidateBasic() error { errs = append(errs, err) } - toRevokeByAddr := make(map[string]AccessGrant) + toRevokeByAddr := make(map[string]AccessGrant, len(m.ToRevoke)) for _, ag := range m.ToRevoke { if ContainsString(m.RevokeAll, ag.Address) { errs = append(errs, fmt.Errorf("address %s appears in both the revoke-all and to-revoke fields", ag.Address)) diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 10caa617f1..74e1877377 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -51,7 +51,7 @@ var _ OrderI = (*Order)(nil) // findDuplicateIds returns all order ids that appear two or more times in the provided slice. func findDuplicateIds(orderIDs []uint64) []uint64 { var rv []uint64 - seen := make(map[uint64]bool) + seen := make(map[uint64]bool, len(orderIDs)) dups := make(map[uint64]bool) for _, orderID := range orderIDs { if seen[orderID] && !dups[orderID] { From e0ca21545fc09c241083712b65df038f78542fa6 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 12 Oct 2023 11:55:02 -0600 Subject: [PATCH 288/309] [1658]: Flatten a for loop in UpdatePermissions a little bit. --- x/exchange/keeper/market.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index cf64aaacd0..c7cfdd65b2 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -884,13 +884,12 @@ func (k Keeper) UpdatePermissions(ctx sdk.Context, msg *exchange.MsgMarketManage for _, addrStr := range msg.RevokeAll { addr := sdk.MustAccAddressFromBech32(addrStr) perms := getUserPermissions(store, marketID, addr) - if len(perms) > 0 { - if len(errs) == 0 { - revokeUserPermissions(store, marketID, addr) - } - } else { + if len(perms) == 0 { errs = append(errs, fmt.Errorf("account %s does not have any permissions for market %d", addrStr, marketID)) } + if len(errs) == 0 { + revokeUserPermissions(store, marketID, addr) + } } for _, ag := range msg.ToRevoke { From 911c023af91c90cdbc846e0e7dfeca980611e20e Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 12 Oct 2023 12:00:47 -0600 Subject: [PATCH 289/309] [1658]: Fix the field comments in MsgMarketManageReqAttrsRequest. --- docs/proto-docs.md | 6 +++--- proto/provenance/exchange/v1/tx.proto | 6 +++--- x/exchange/tx.pb.go | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index abd741cd0e..181d64c91c 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2192,9 +2192,9 @@ MsgMarketManageReqAttrsRequest is a request message for the MarketManageReqAttrs | `admin` | [string](#string) | | admin is the account with "attributes" permission requesting this change. | | `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | | `create_ask_to_add` | [string](#string) | repeated | create_ask_to_add are the attributes that should now also be required to create an ask order. | -| `create_ask_to_remove` | [string](#string) | repeated | create_ask_to_add are the attributes that should no longer be required to create an ask order. | -| `create_bid_to_add` | [string](#string) | repeated | create_ask_to_add are the attributes that should now also be required to create a bid order. | -| `create_bid_to_remove` | [string](#string) | repeated | create_ask_to_add are the attributes that should no longer be required to create a bid order. | +| `create_ask_to_remove` | [string](#string) | repeated | create_ask_to_remove are the attributes that should no longer be required to create an ask order. | +| `create_bid_to_add` | [string](#string) | repeated | create_bid_to_add are the attributes that should now also be required to create a bid order. | +| `create_bid_to_remove` | [string](#string) | repeated | create_bid_to_remove are the attributes that should no longer be required to create a bid order. | diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index e78689fdf6..8ff9ccc55a 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -282,11 +282,11 @@ message MsgMarketManageReqAttrsRequest { // create_ask_to_add are the attributes that should now also be required to create an ask order. repeated string create_ask_to_add = 3; - // create_ask_to_add are the attributes that should no longer be required to create an ask order. + // create_ask_to_remove are the attributes that should no longer be required to create an ask order. repeated string create_ask_to_remove = 4; - // create_ask_to_add are the attributes that should now also be required to create a bid order. + // create_bid_to_add are the attributes that should now also be required to create a bid order. repeated string create_bid_to_add = 5; - // create_ask_to_add are the attributes that should no longer be required to create a bid order. + // create_bid_to_remove are the attributes that should no longer be required to create a bid order. repeated string create_bid_to_remove = 6; } diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index a06b3317aa..7d02d6f000 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -1254,11 +1254,11 @@ type MsgMarketManageReqAttrsRequest struct { MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // create_ask_to_add are the attributes that should now also be required to create an ask order. CreateAskToAdd []string `protobuf:"bytes,3,rep,name=create_ask_to_add,json=createAskToAdd,proto3" json:"create_ask_to_add,omitempty"` - // create_ask_to_add are the attributes that should no longer be required to create an ask order. + // create_ask_to_remove are the attributes that should no longer be required to create an ask order. CreateAskToRemove []string `protobuf:"bytes,4,rep,name=create_ask_to_remove,json=createAskToRemove,proto3" json:"create_ask_to_remove,omitempty"` - // create_ask_to_add are the attributes that should now also be required to create a bid order. + // create_bid_to_add are the attributes that should now also be required to create a bid order. CreateBidToAdd []string `protobuf:"bytes,5,rep,name=create_bid_to_add,json=createBidToAdd,proto3" json:"create_bid_to_add,omitempty"` - // create_ask_to_add are the attributes that should no longer be required to create a bid order. + // create_bid_to_remove are the attributes that should no longer be required to create a bid order. CreateBidToRemove []string `protobuf:"bytes,6,rep,name=create_bid_to_remove,json=createBidToRemove,proto3" json:"create_bid_to_remove,omitempty"` } From 09e346a0991f64a9d70bccad1384efecc88d9024 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 12 Oct 2023 12:10:48 -0600 Subject: [PATCH 290/309] [1658]: Double check that the market details are valid in CreateMarket since it's a public keeper function. --- x/exchange/keeper/market.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index c7cfdd65b2..5aad8a9c08 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -1196,8 +1196,9 @@ func (k Keeper) CreateMarket(ctx sdk.Context, market exchange.Market) (uint32, e var errAsk, errBid error market.ReqAttrCreateAsk, errAsk = exchange.NormalizeReqAttrs(market.ReqAttrCreateAsk) market.ReqAttrCreateBid, errBid = exchange.NormalizeReqAttrs(market.ReqAttrCreateBid) - if errAsk != nil || errBid != nil { - return 0, errors.Join(errAsk, errBid) + errDets := market.MarketDetails.Validate() + if errAsk != nil || errBid != nil || errDets != nil { + return 0, errors.Join(errAsk, errBid, errDets) } store := k.getStore(ctx) From 79641ca910fb986041e9216ffe821a5842f7c868 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 12 Oct 2023 12:24:52 -0600 Subject: [PATCH 291/309] [1658]: Allow an admin to revoke their permission to manage permissions. --- x/exchange/msg.go | 7 ------- x/exchange/msg_test.go | 6 ++++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 1d0bd7893c..5efbd00166 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -307,9 +307,6 @@ func (m MsgMarketManagePermissionsRequest) ValidateBasic() error { errs = append(errs, fmt.Errorf("invalid revoke-all address %q: %w", addrStr, err)) } } - if ContainsString(m.RevokeAll, m.Admin) { - errs = append(errs, fmt.Errorf("message administrator %s cannot revoke all of their permissions", m.Admin)) - } if err := ValidateAccessGrantsField("to-revoke", m.ToRevoke); err != nil { errs = append(errs, err) @@ -320,10 +317,6 @@ func (m MsgMarketManagePermissionsRequest) ValidateBasic() error { if ContainsString(m.RevokeAll, ag.Address) { errs = append(errs, fmt.Errorf("address %s appears in both the revoke-all and to-revoke fields", ag.Address)) } - if ag.Address == m.Admin && ag.Contains(Permission_permissions) { - errs = append(errs, fmt.Errorf("message administrator %s cannot revoke their own ability to manage permissions", - ag.Address)) - } toRevokeByAddr[ag.Address] = ag } diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index 311d8e98fa..bfa473d6ff 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -1318,13 +1318,14 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { }, }, { + // We allow this because it can be fixed via gov prop if no one is left that can manage permissions. name: "admin in revoke-all", msg: MsgMarketManagePermissionsRequest{ Admin: goodAdminAddr, MarketId: 1, RevokeAll: []string{goodAddr1, goodAdminAddr, goodAddr2}, }, - expErr: []string{"message administrator " + goodAdminAddr + " cannot revoke all of their permissions"}, + expErr: nil, }, { name: "admin revoking all their permissions except permissions", @@ -1342,6 +1343,7 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { expErr: nil, }, { + // We allow this because it can be fixed via gov prop if no one is left that can manage permissions. name: "admin revoking own ability to manage permissions", msg: MsgMarketManagePermissionsRequest{ Admin: goodAdminAddr, @@ -1352,7 +1354,7 @@ func TestMsgMarketManagePermissionsRequest_ValidateBasic(t *testing.T) { {Address: goodAddr2, Permissions: []Permission{Permission_permissions}}, }, }, - expErr: []string{"message administrator " + goodAdminAddr + " cannot revoke their own ability to manage permissions"}, + expErr: nil, }, { name: "multiple errs", From 6a5d4797802c83a381583e3f73717a2a698e4a73 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Thu, 12 Oct 2023 16:52:19 -0600 Subject: [PATCH 292/309] [1658]: Add an external id field to the orders and create a tx for setting it and query for looking up orders using them. --- docs/proto-docs.md | 99 ++- proto/provenance/exchange/v1/events.proto | 16 + proto/provenance/exchange/v1/market.proto | 12 +- proto/provenance/exchange/v1/orders.proto | 6 + proto/provenance/exchange/v1/query.proto | 22 + proto/provenance/exchange/v1/tx.proto | 22 + x/exchange/events.go | 37 +- x/exchange/events.pb.go | 510 +++++++++++++-- x/exchange/events_test.go | 392 +++++++++--- x/exchange/fulfillment.go | 5 + x/exchange/fulfillment_test.go | 42 +- x/exchange/keeper/grpc_query.go | 19 + x/exchange/keeper/grpc_query_test.go | 4 +- x/exchange/keeper/keys.go | 18 + x/exchange/keeper/keys_test.go | 67 ++ x/exchange/keeper/market.go | 6 + x/exchange/keeper/market_test.go | 2 + x/exchange/keeper/msg_server.go | 13 + x/exchange/keeper/msg_server_test.go | 2 + x/exchange/keeper/orders.go | 116 +++- x/exchange/keeper/orders_test.go | 4 + x/exchange/market.go | 19 +- x/exchange/market.pb.go | 161 ++--- x/exchange/market_test.go | 25 + x/exchange/msg.go | 28 + x/exchange/msg_test.go | 119 ++++ x/exchange/orders.go | 41 ++ x/exchange/orders.pb.go | 160 ++++- x/exchange/orders_test.go | 135 ++++ x/exchange/query.pb.go | 649 +++++++++++++++---- x/exchange/query.pb.gw.go | 240 ++++++++ x/exchange/tx.pb.go | 719 ++++++++++++++++++---- 32 files changed, 3166 insertions(+), 544 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 181d64c91c..1769c35b1e 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -65,6 +65,7 @@ - [EventMarketWithdraw](#provenance.exchange.v1.EventMarketWithdraw) - [EventOrderCancelled](#provenance.exchange.v1.EventOrderCancelled) - [EventOrderCreated](#provenance.exchange.v1.EventOrderCreated) + - [EventOrderExternalIDUpdated](#provenance.exchange.v1.EventOrderExternalIDUpdated) - [EventOrderFilled](#provenance.exchange.v1.EventOrderFilled) - [EventOrderPartiallyFilled](#provenance.exchange.v1.EventOrderPartiallyFilled) - [EventParamsUpdated](#provenance.exchange.v1.EventParamsUpdated) @@ -112,6 +113,8 @@ - [MsgMarketManagePermissionsResponse](#provenance.exchange.v1.MsgMarketManagePermissionsResponse) - [MsgMarketManageReqAttrsRequest](#provenance.exchange.v1.MsgMarketManageReqAttrsRequest) - [MsgMarketManageReqAttrsResponse](#provenance.exchange.v1.MsgMarketManageReqAttrsResponse) + - [MsgMarketSetOrderExternalIDRequest](#provenance.exchange.v1.MsgMarketSetOrderExternalIDRequest) + - [MsgMarketSetOrderExternalIDResponse](#provenance.exchange.v1.MsgMarketSetOrderExternalIDResponse) - [MsgMarketSettleRequest](#provenance.exchange.v1.MsgMarketSettleRequest) - [MsgMarketSettleResponse](#provenance.exchange.v1.MsgMarketSettleResponse) - [MsgMarketUpdateDetailsRequest](#provenance.exchange.v1.MsgMarketUpdateDetailsRequest) @@ -136,6 +139,8 @@ - [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) - [QueryGetMarketRequest](#provenance.exchange.v1.QueryGetMarketRequest) - [QueryGetMarketResponse](#provenance.exchange.v1.QueryGetMarketResponse) + - [QueryGetOrderByExternalIDRequest](#provenance.exchange.v1.QueryGetOrderByExternalIDRequest) + - [QueryGetOrderByExternalIDResponse](#provenance.exchange.v1.QueryGetOrderByExternalIDResponse) - [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) - [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) - [QueryGetOwnerOrdersRequest](#provenance.exchange.v1.QueryGetOwnerOrdersRequest) @@ -1524,6 +1529,7 @@ EventOrderCancelled is an event emitted when an order is cancelled. | ----- | ---- | ----- | ----------- | | `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order cancelled. | | `cancelled_by` | [string](#string) | | cancelled_by is the account that triggered the cancellation of the order. | +| `external_id` | [string](#string) | | external_id is the order's external id. | @@ -1540,6 +1546,23 @@ EventOrderCreated is an event emitted when an order is created. | ----- | ---- | ----- | ----------- | | `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order created. | | `order_type` | [string](#string) | | order_type is the type of order, e.g. "ask" or "bid". | +| `external_id` | [string](#string) | | external_id is the order's external id. | + + + + + + + + +### EventOrderExternalIDUpdated +EventOrderExternalIDUpdated is an event emitted when an order's external id is updated. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order partially filled. | +| `external_id` | [string](#string) | | external_id is the order's new external id. | @@ -1559,6 +1582,7 @@ This event is also used for orders that were previously partially filled, but ha | `assets` | [string](#string) | | assets is the coins amount string of assets bought/sold for this order. | | `price` | [string](#string) | | price is the coins amount string of the price payed/received for this order. | | `fees` | [string](#string) | | fees is the coins amount string of settlement fees paid with this order. | +| `external_id` | [string](#string) | | external_id is the order's external id. | @@ -1577,6 +1601,7 @@ EventOrderPartiallyFilled is an event emitted when an order filled in part and s | `assets` | [string](#string) | | assets is the coins amount string of assets that were filled and removed from the order. | | `price` | [string](#string) | | price is the coins amount string of the price payed/received for this order. For ask orders, this might be more than the amount that was removed from the order's price. | | `fees` | [string](#string) | | fees is the coins amount string of settlement fees paid with this partial order. For ask orders, this might be more than the amount that was removed from the order's settlement fees. | +| `external_id` | [string](#string) | | external_id is the order's external id. | @@ -1736,11 +1761,12 @@ Permission defines the different types of permission that can be given to an acc | ---- | ------ | ----------- | | PERMISSION_UNSPECIFIED | 0 | PERMISSION_UNSPECIFIED is the zero-value Permission; it is an error to use it. | | PERMISSION_SETTLE | 1 | PERMISSION_SETTLE is the ability to use the Settle Tx endpoint on behalf of a market. | -| PERMISSION_CANCEL | 2 | PERMISSION_CANCEL is the ability to use the Cancel Tx endpoint on behalf of a market. | -| PERMISSION_WITHDRAW | 3 | PERMISSION_WITHDRAW is the ability to use the MarketWithdraw Tx endpoint. | -| PERMISSION_UPDATE | 4 | PERMISSION_UPDATE is the ability to use the MarketUpdate* Tx endpoints. | -| PERMISSION_PERMISSIONS | 5 | PERMISSION_PERMISSIONS is the ability to use the MarketManagePermissions Tx endpoint. | -| PERMISSION_ATTRIBUTES | 6 | PERMISSION_ATTRIBUTES is the ability to use the MarketManageReqAttrs Tx endpoint. | +| PERMISSION_SET_IDS | 2 | PERMISSION_SET_IDS is the ability to use the SetOrderExternalID Tx endpoint on behalf of a market. | +| PERMISSION_CANCEL | 3 | PERMISSION_CANCEL is the ability to use the Cancel Tx endpoint on behalf of a market. | +| PERMISSION_WITHDRAW | 4 | PERMISSION_WITHDRAW is the ability to use the MarketWithdraw Tx endpoint. | +| PERMISSION_UPDATE | 5 | PERMISSION_UPDATE is the ability to use the MarketUpdate* Tx endpoints. | +| PERMISSION_PERMISSIONS | 6 | PERMISSION_PERMISSIONS is the ability to use the MarketManagePermissions Tx endpoint. | +| PERMISSION_ATTRIBUTES | 7 | PERMISSION_ATTRIBUTES is the ability to use the MarketManageReqAttrs Tx endpoint. | @@ -1772,6 +1798,7 @@ AskOrder represents someone's desire to sell something at a minimum price. | `price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | price is the minimum amount that the seller is willing to accept for the assets. The seller's settlement proportional fee (and possibly the settlement flat fee) is taken out of the amount the seller receives, so it's possible that the seller will still receive less than this price. | | `seller_settlement_flat_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. If this denom is the same denom as the price, it will come out of the actual price received. If this denom is different, the amount must be in the seller's account and a hold is placed on it until the order is filled or cancelled. | | `allow_partial` | [bool](#bool) | | allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the order must be either filled in full or not filled at all. | +| `external_id` | [string](#string) | | external_id is an optional string used to externally identify this order. Max length is 40 characters. If an order in this market with this external id already exists, this order will be rejected. | @@ -1792,6 +1819,7 @@ BidOrder represents someone's desire to buy something at a specific price. | `price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | price is the amount that the buyer will pay for the assets. A hold is placed on this until the order is filled or cancelled. | | `buyer_settlement_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) when the order is settled. A hold is placed on this until the order is filled or cancelled. | | `allow_partial` | [bool](#bool) | | allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the order must be either filled in full or not filled at all. | +| `external_id` | [string](#string) | | external_id is an optional string used to externally identify this order. Max length is 40 characters. If an order in this market with this external id already exists, this order will be rejected. | @@ -2211,6 +2239,34 @@ MsgMarketManageReqAttrsResponse is a response message for the MarketManageReqAtt + + +### MsgMarketSetOrderExternalIDRequest +MsgMarketSetOrderExternalIDRequest is a request message for the MarketSetOrderExternalID endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `admin` | [string](#string) | | admin is the account with "set_ids" permission requesting this settlement. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | +| `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order to update. | +| `external_id` | [string](#string) | | external_id is the new external id to associate with the order. Max length is 40 characters. If the external id is already associated with another order in this market, this update will fail. | + + + + + + + + +### MsgMarketSetOrderExternalIDResponse +MsgMarketSetOrderExternalIDResponse is a response message for the MarketSetOrderExternalID endpoint. + + + + + + ### MsgMarketSettleRequest @@ -2368,6 +2424,7 @@ Msg is the service for exchange module's tx endpoints. | `FillBids` | [MsgFillBidsRequest](#provenance.exchange.v1.MsgFillBidsRequest) | [MsgFillBidsResponse](#provenance.exchange.v1.MsgFillBidsResponse) | FillBids uses the assets in your account to fulfill one or more bids (similar to a fill-or-cancel ask). | | | `FillAsks` | [MsgFillAsksRequest](#provenance.exchange.v1.MsgFillAsksRequest) | [MsgFillAsksResponse](#provenance.exchange.v1.MsgFillAsksResponse) | FillAsks uses the funds in your account to fulfill one or more asks (similar to a fill-or-cancel bid). | | | `MarketSettle` | [MsgMarketSettleRequest](#provenance.exchange.v1.MsgMarketSettleRequest) | [MsgMarketSettleResponse](#provenance.exchange.v1.MsgMarketSettleResponse) | MarketSettle is a market endpoint to trigger the settlement of orders. | | +| `MarketSetOrderExternalID` | [MsgMarketSetOrderExternalIDRequest](#provenance.exchange.v1.MsgMarketSetOrderExternalIDRequest) | [MsgMarketSetOrderExternalIDResponse](#provenance.exchange.v1.MsgMarketSetOrderExternalIDResponse) | MarketSetOrderExternalID updates an order's external id field. | | | `MarketWithdraw` | [MsgMarketWithdrawRequest](#provenance.exchange.v1.MsgMarketWithdrawRequest) | [MsgMarketWithdrawResponse](#provenance.exchange.v1.MsgMarketWithdrawResponse) | MarketWithdraw is a market endpoint to withdraw fees that have been collected. | | | `MarketUpdateDetails` | [MsgMarketUpdateDetailsRequest](#provenance.exchange.v1.MsgMarketUpdateDetailsRequest) | [MsgMarketUpdateDetailsResponse](#provenance.exchange.v1.MsgMarketUpdateDetailsResponse) | MarketUpdateDetails is a market endpoint to update its details. | | | `MarketUpdateEnabled` | [MsgMarketUpdateEnabledRequest](#provenance.exchange.v1.MsgMarketUpdateEnabledRequest) | [MsgMarketUpdateEnabledResponse](#provenance.exchange.v1.MsgMarketUpdateEnabledResponse) | MarketUpdateEnabled is a market endpoint to update whether its accepting orders. | | @@ -2550,6 +2607,37 @@ QueryGetMarketResponse is a response message for the QueryGetMarket endpoint. + + +### QueryGetOrderByExternalIDRequest +QueryGetOrderByExternalIDRequest is a request message for the QueryGetOrderByExternalID endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `market_id` | [uint32](#uint32) | | market_id is the id of the market that's expected to have the order. | +| `external_id` | [string](#string) | | external_id the external id to look up. | + + + + + + + + +### QueryGetOrderByExternalIDResponse +QueryGetOrderByExternalIDResponse is a response message for the QueryGetOrderByExternalID endpoint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `order` | [Order](#provenance.exchange.v1.Order) | | order is the requested order. | + + + + + + ### QueryGetOrderRequest @@ -2784,6 +2872,7 @@ Query is the service for exchange module's query endpoints. | ----------- | ------------ | ------------- | ------------| ------- | -------- | | `QueryOrderFeeCalc` | [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) | [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) | QueryOrderFeeCalc calculates the fees that will be associated with the provided order. | GET|/provenance/exchange/v1/fees/order| | `QueryGetOrder` | [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) | [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) | QueryGetOrder looks up an order by id. | GET|/provenance/exchange/v1/order/{order_id}| +| `QueryGetOrderByExternalID` | [QueryGetOrderByExternalIDRequest](#provenance.exchange.v1.QueryGetOrderByExternalIDRequest) | [QueryGetOrderByExternalIDResponse](#provenance.exchange.v1.QueryGetOrderByExternalIDResponse) | QueryGetOrderByExternalID looks up an order by market id and external id. | GET|/provenance/exchange/v1/orders/market/{market_id}/{external_id}GET|/provenance/exchange/v1/market/{market_id}/order/{external_id}| | `QueryGetMarketOrders` | [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) | [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) | QueryGetMarketOrders looks up the orders in a market. | GET|/provenance/exchange/v1/orders/market/{market_id}GET|/provenance/exchange/v1/market/{market_id}/orders| | `QueryGetOwnerOrders` | [QueryGetOwnerOrdersRequest](#provenance.exchange.v1.QueryGetOwnerOrdersRequest) | [QueryGetOwnerOrdersResponse](#provenance.exchange.v1.QueryGetOwnerOrdersResponse) | QueryGetOwnerOrders looks up the orders from the provided owner address. | GET|/provenance/exchange/v1/orders/owner/{owner}| | `QueryGetAssetOrders` | [QueryGetAssetOrdersRequest](#provenance.exchange.v1.QueryGetAssetOrdersRequest) | [QueryGetAssetOrdersResponse](#provenance.exchange.v1.QueryGetAssetOrdersResponse) | QueryGetAssetOrders looks up the orders for a specific asset denom. | GET|/provenance/exchange/v1/orders/asset/{asset}| diff --git a/proto/provenance/exchange/v1/events.proto b/proto/provenance/exchange/v1/events.proto index f3da3c78eb..07a55a63d8 100644 --- a/proto/provenance/exchange/v1/events.proto +++ b/proto/provenance/exchange/v1/events.proto @@ -14,6 +14,8 @@ message EventOrderCreated { uint64 order_id = 1; // order_type is the type of order, e.g. "ask" or "bid". string order_type = 2; + // external_id is the order's external id. + string external_id = 3; } // EventOrderCancelled is an event emitted when an order is cancelled. @@ -22,6 +24,8 @@ message EventOrderCancelled { uint64 order_id = 1; // cancelled_by is the account that triggered the cancellation of the order. string cancelled_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // external_id is the order's external id. + string external_id = 3; } // EventOrderFilled is an event emitted when an order has been filled in full. @@ -35,6 +39,8 @@ message EventOrderFilled { string price = 3; // fees is the coins amount string of settlement fees paid with this order. string fees = 4; + // external_id is the order's external id. + string external_id = 5; } // EventOrderPartiallyFilled is an event emitted when an order filled in part and still has more left to fill. @@ -49,6 +55,16 @@ message EventOrderPartiallyFilled { // fees is the coins amount string of settlement fees paid with this partial order. // For ask orders, this might be more than the amount that was removed from the order's settlement fees. string fees = 4; + // external_id is the order's external id. + string external_id = 5; +} + +// EventOrderExternalIDUpdated is an event emitted when an order's external id is updated. +message EventOrderExternalIDUpdated { + // order_id is the numerical identifier of the order partially filled. + uint64 order_id = 1; + // external_id is the order's new external id. + string external_id = 3; } // EventMarketWithdraw is an event emitted when a withdrawal of a market's collected fees is made. diff --git a/proto/provenance/exchange/v1/market.proto b/proto/provenance/exchange/v1/market.proto index d53135c51b..7628526395 100644 --- a/proto/provenance/exchange/v1/market.proto +++ b/proto/provenance/exchange/v1/market.proto @@ -126,14 +126,16 @@ enum Permission { PERMISSION_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "unspecified"]; // PERMISSION_SETTLE is the ability to use the Settle Tx endpoint on behalf of a market. PERMISSION_SETTLE = 1 [(gogoproto.enumvalue_customname) = "settle"]; + // PERMISSION_SET_IDS is the ability to use the SetOrderExternalID Tx endpoint on behalf of a market. + PERMISSION_SET_IDS = 2 [(gogoproto.enumvalue_customname) = "set_ids"]; // PERMISSION_CANCEL is the ability to use the Cancel Tx endpoint on behalf of a market. - PERMISSION_CANCEL = 2 [(gogoproto.enumvalue_customname) = "cancel"]; + PERMISSION_CANCEL = 3 [(gogoproto.enumvalue_customname) = "cancel"]; // PERMISSION_WITHDRAW is the ability to use the MarketWithdraw Tx endpoint. - PERMISSION_WITHDRAW = 3 [(gogoproto.enumvalue_customname) = "withdraw"]; + PERMISSION_WITHDRAW = 4 [(gogoproto.enumvalue_customname) = "withdraw"]; // PERMISSION_UPDATE is the ability to use the MarketUpdate* Tx endpoints. - PERMISSION_UPDATE = 4 [(gogoproto.enumvalue_customname) = "update"]; + PERMISSION_UPDATE = 5 [(gogoproto.enumvalue_customname) = "update"]; // PERMISSION_PERMISSIONS is the ability to use the MarketManagePermissions Tx endpoint. - PERMISSION_PERMISSIONS = 5 [(gogoproto.enumvalue_customname) = "permissions"]; + PERMISSION_PERMISSIONS = 6 [(gogoproto.enumvalue_customname) = "permissions"]; // PERMISSION_ATTRIBUTES is the ability to use the MarketManageReqAttrs Tx endpoint. - PERMISSION_ATTRIBUTES = 6 [(gogoproto.enumvalue_customname) = "attributes"]; + PERMISSION_ATTRIBUTES = 7 [(gogoproto.enumvalue_customname) = "attributes"]; } \ No newline at end of file diff --git a/proto/provenance/exchange/v1/orders.proto b/proto/provenance/exchange/v1/orders.proto index 29228ce166..7ba84762ea 100644 --- a/proto/provenance/exchange/v1/orders.proto +++ b/proto/provenance/exchange/v1/orders.proto @@ -47,6 +47,9 @@ message AskOrder { // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the // order must be either filled in full or not filled at all. bool allow_partial = 6; + // external_id is an optional string used to externally identify this order. Max length is 40 characters. + // If an order in this market with this external id already exists, this order will be rejected. + string external_id = 7; } // BidOrder represents someone's desire to buy something at a specific price. @@ -69,4 +72,7 @@ message BidOrder { // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the // order must be either filled in full or not filled at all. bool allow_partial = 6; + // external_id is an optional string used to externally identify this order. Max length is 40 characters. + // If an order in this market with this external id already exists, this order will be rejected. + string external_id = 7; } \ No newline at end of file diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index 8db771ac42..adeb6a6824 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -28,6 +28,14 @@ service Query { option (google.api.http).get = "/provenance/exchange/v1/order/{order_id}"; } + // QueryGetOrderByExternalID looks up an order by market id and external id. + rpc QueryGetOrderByExternalID(QueryGetOrderByExternalIDRequest) returns (QueryGetOrderByExternalIDResponse) { + option (google.api.http) = { + get: "/provenance/exchange/v1/orders/market/{market_id}/{external_id}" + additional_bindings: {get: "/provenance/exchange/v1/market/{market_id}/order/{external_id}"} + }; + } + // QueryGetMarketOrders looks up the orders in a market. rpc QueryGetMarketOrders(QueryGetMarketOrdersRequest) returns (QueryGetMarketOrdersResponse) { option (google.api.http) = { @@ -127,6 +135,20 @@ message QueryGetOrderResponse { Order order = 1; } +// QueryGetOrderByExternalIDRequest is a request message for the QueryGetOrderByExternalID endpoint. +message QueryGetOrderByExternalIDRequest { + // market_id is the id of the market that's expected to have the order. + uint32 market_id = 1; + // external_id the external id to look up. + string external_id = 2; +} + +// QueryGetOrderByExternalIDResponse is a response message for the QueryGetOrderByExternalID endpoint. +message QueryGetOrderByExternalIDResponse { + // order is the requested order. + Order order = 1; +} + // QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. message QueryGetMarketOrdersRequest { // market_id is the id of the market to get all the orders for. diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 8ff9ccc55a..0a23d47528 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -34,6 +34,9 @@ service Msg { // MarketSettle is a market endpoint to trigger the settlement of orders. rpc MarketSettle(MsgMarketSettleRequest) returns (MsgMarketSettleResponse); + // MarketSetOrderExternalID updates an order's external id field. + rpc MarketSetOrderExternalID(MsgMarketSetOrderExternalIDRequest) returns (MsgMarketSetOrderExternalIDResponse); + // MarketWithdraw is a market endpoint to withdraw fees that have been collected. rpc MarketWithdraw(MsgMarketWithdrawRequest) returns (MsgMarketWithdrawResponse); @@ -182,6 +185,25 @@ message MsgMarketSettleRequest { // MsgMarketSettleResponse is a response message for the MarketSettle endpoint. message MsgMarketSettleResponse {} +// MsgMarketSetOrderExternalIDRequest is a request message for the MarketSetOrderExternalID endpoint. +message MsgMarketSetOrderExternalIDRequest { + option (cosmos.msg.v1.signer) = "admin"; + + // admin is the account with "set_ids" permission requesting this settlement. + string admin = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market_id is the numerical identifier of the market to update required attributes for. + uint32 market_id = 2; + + // order_id is the numerical identifier of the order to update. + uint64 order_id = 3; + // external_id is the new external id to associate with the order. Max length is 40 characters. + // If the external id is already associated with another order in this market, this update will fail. + string external_id = 4; +} + +// MsgMarketSetOrderExternalIDResponse is a response message for the MarketSetOrderExternalID endpoint. +message MsgMarketSetOrderExternalIDResponse {} + // MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. message MsgMarketWithdrawRequest { option (cosmos.msg.v1.signer) = "admin"; diff --git a/x/exchange/events.go b/x/exchange/events.go index 436e81553a..b73b9367f9 100644 --- a/x/exchange/events.go +++ b/x/exchange/events.go @@ -6,35 +6,46 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func NewEventOrderCreated(order *Order) *EventOrderCreated { +func NewEventOrderCreated(order OrderI) *EventOrderCreated { return &EventOrderCreated{ - OrderId: order.GetOrderID(), - OrderType: order.GetOrderType(), + OrderId: order.GetOrderID(), + OrderType: order.GetOrderType(), + ExternalId: order.GetExternalID(), } } -func NewEventOrderCancelled(orderID uint64, cancelledBy sdk.AccAddress) *EventOrderCancelled { +func NewEventOrderCancelled(order OrderI, cancelledBy sdk.AccAddress) *EventOrderCancelled { return &EventOrderCancelled{ - OrderId: orderID, + OrderId: order.GetOrderID(), CancelledBy: cancelledBy.String(), + ExternalId: order.GetExternalID(), } } func NewEventOrderFilled(order OrderI) *EventOrderFilled { return &EventOrderFilled{ - OrderId: order.GetOrderID(), - Assets: order.GetAssets().String(), - Price: order.GetPrice().String(), - Fees: order.GetSettlementFees().String(), + OrderId: order.GetOrderID(), + Assets: order.GetAssets().String(), + Price: order.GetPrice().String(), + Fees: order.GetSettlementFees().String(), + ExternalId: order.GetExternalID(), } } func NewEventOrderPartiallyFilled(order OrderI) *EventOrderPartiallyFilled { return &EventOrderPartiallyFilled{ - OrderId: order.GetOrderID(), - Assets: order.GetAssets().String(), - Price: order.GetPrice().String(), - Fees: order.GetSettlementFees().String(), + OrderId: order.GetOrderID(), + Assets: order.GetAssets().String(), + Price: order.GetPrice().String(), + Fees: order.GetSettlementFees().String(), + ExternalId: order.GetExternalID(), + } +} + +func NewEventOrderExternalIDUpdated(order OrderI) *EventOrderExternalIDUpdated { + return &EventOrderExternalIDUpdated{ + OrderId: order.GetOrderID(), + ExternalId: order.GetExternalID(), } } diff --git a/x/exchange/events.pb.go b/x/exchange/events.pb.go index a7700ff0f7..0fd503f3d4 100644 --- a/x/exchange/events.pb.go +++ b/x/exchange/events.pb.go @@ -29,6 +29,8 @@ type EventOrderCreated struct { OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` // order_type is the type of order, e.g. "ask" or "bid". OrderType string `protobuf:"bytes,2,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` + // external_id is the order's external id. + ExternalId string `protobuf:"bytes,3,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } func (m *EventOrderCreated) Reset() { *m = EventOrderCreated{} } @@ -78,12 +80,21 @@ func (m *EventOrderCreated) GetOrderType() string { return "" } +func (m *EventOrderCreated) GetExternalId() string { + if m != nil { + return m.ExternalId + } + return "" +} + // EventOrderCancelled is an event emitted when an order is cancelled. type EventOrderCancelled struct { // order_id is the numerical identifier of the order cancelled. OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` // cancelled_by is the account that triggered the cancellation of the order. CancelledBy string `protobuf:"bytes,2,opt,name=cancelled_by,json=cancelledBy,proto3" json:"cancelled_by,omitempty"` + // external_id is the order's external id. + ExternalId string `protobuf:"bytes,3,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } func (m *EventOrderCancelled) Reset() { *m = EventOrderCancelled{} } @@ -133,6 +144,13 @@ func (m *EventOrderCancelled) GetCancelledBy() string { return "" } +func (m *EventOrderCancelled) GetExternalId() string { + if m != nil { + return m.ExternalId + } + return "" +} + // EventOrderFilled is an event emitted when an order has been filled in full. // This event is also used for orders that were previously partially filled, but have now been filled in full. type EventOrderFilled struct { @@ -144,6 +162,8 @@ type EventOrderFilled struct { Price string `protobuf:"bytes,3,opt,name=price,proto3" json:"price,omitempty"` // fees is the coins amount string of settlement fees paid with this order. Fees string `protobuf:"bytes,4,opt,name=fees,proto3" json:"fees,omitempty"` + // external_id is the order's external id. + ExternalId string `protobuf:"bytes,5,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } func (m *EventOrderFilled) Reset() { *m = EventOrderFilled{} } @@ -207,6 +227,13 @@ func (m *EventOrderFilled) GetFees() string { return "" } +func (m *EventOrderFilled) GetExternalId() string { + if m != nil { + return m.ExternalId + } + return "" +} + // EventOrderPartiallyFilled is an event emitted when an order filled in part and still has more left to fill. type EventOrderPartiallyFilled struct { // order_id is the numerical identifier of the order partially filled. @@ -219,6 +246,8 @@ type EventOrderPartiallyFilled struct { // fees is the coins amount string of settlement fees paid with this partial order. // For ask orders, this might be more than the amount that was removed from the order's settlement fees. Fees string `protobuf:"bytes,4,opt,name=fees,proto3" json:"fees,omitempty"` + // external_id is the order's external id. + ExternalId string `protobuf:"bytes,5,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } func (m *EventOrderPartiallyFilled) Reset() { *m = EventOrderPartiallyFilled{} } @@ -282,6 +311,68 @@ func (m *EventOrderPartiallyFilled) GetFees() string { return "" } +func (m *EventOrderPartiallyFilled) GetExternalId() string { + if m != nil { + return m.ExternalId + } + return "" +} + +// EventOrderExternalIDUpdated is an event emitted when an order's external id is updated. +type EventOrderExternalIDUpdated struct { + // order_id is the numerical identifier of the order partially filled. + OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + // external_id is the order's new external id. + ExternalId string `protobuf:"bytes,3,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` +} + +func (m *EventOrderExternalIDUpdated) Reset() { *m = EventOrderExternalIDUpdated{} } +func (m *EventOrderExternalIDUpdated) String() string { return proto.CompactTextString(m) } +func (*EventOrderExternalIDUpdated) ProtoMessage() {} +func (*EventOrderExternalIDUpdated) Descriptor() ([]byte, []int) { + return fileDescriptor_c1b69385a348cffa, []int{4} +} +func (m *EventOrderExternalIDUpdated) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventOrderExternalIDUpdated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventOrderExternalIDUpdated.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventOrderExternalIDUpdated) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventOrderExternalIDUpdated.Merge(m, src) +} +func (m *EventOrderExternalIDUpdated) XXX_Size() int { + return m.Size() +} +func (m *EventOrderExternalIDUpdated) XXX_DiscardUnknown() { + xxx_messageInfo_EventOrderExternalIDUpdated.DiscardUnknown(m) +} + +var xxx_messageInfo_EventOrderExternalIDUpdated proto.InternalMessageInfo + +func (m *EventOrderExternalIDUpdated) GetOrderId() uint64 { + if m != nil { + return m.OrderId + } + return 0 +} + +func (m *EventOrderExternalIDUpdated) GetExternalId() string { + if m != nil { + return m.ExternalId + } + return "" +} + // EventMarketWithdraw is an event emitted when a withdrawal of a market's collected fees is made. type EventMarketWithdraw struct { // market_id is the numerical identifier of the market. @@ -298,7 +389,7 @@ func (m *EventMarketWithdraw) Reset() { *m = EventMarketWithdraw{} } func (m *EventMarketWithdraw) String() string { return proto.CompactTextString(m) } func (*EventMarketWithdraw) ProtoMessage() {} func (*EventMarketWithdraw) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{4} + return fileDescriptor_c1b69385a348cffa, []int{5} } func (m *EventMarketWithdraw) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -367,7 +458,7 @@ func (m *EventMarketDetailsUpdated) Reset() { *m = EventMarketDetailsUpd func (m *EventMarketDetailsUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketDetailsUpdated) ProtoMessage() {} func (*EventMarketDetailsUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{5} + return fileDescriptor_c1b69385a348cffa, []int{6} } func (m *EventMarketDetailsUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -422,7 +513,7 @@ func (m *EventMarketEnabled) Reset() { *m = EventMarketEnabled{} } func (m *EventMarketEnabled) String() string { return proto.CompactTextString(m) } func (*EventMarketEnabled) ProtoMessage() {} func (*EventMarketEnabled) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{6} + return fileDescriptor_c1b69385a348cffa, []int{7} } func (m *EventMarketEnabled) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -477,7 +568,7 @@ func (m *EventMarketDisabled) Reset() { *m = EventMarketDisabled{} } func (m *EventMarketDisabled) String() string { return proto.CompactTextString(m) } func (*EventMarketDisabled) ProtoMessage() {} func (*EventMarketDisabled) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{7} + return fileDescriptor_c1b69385a348cffa, []int{8} } func (m *EventMarketDisabled) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -532,7 +623,7 @@ func (m *EventMarketUserSettleEnabled) Reset() { *m = EventMarketUserSet func (m *EventMarketUserSettleEnabled) String() string { return proto.CompactTextString(m) } func (*EventMarketUserSettleEnabled) ProtoMessage() {} func (*EventMarketUserSettleEnabled) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{8} + return fileDescriptor_c1b69385a348cffa, []int{9} } func (m *EventMarketUserSettleEnabled) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -587,7 +678,7 @@ func (m *EventMarketUserSettleDisabled) Reset() { *m = EventMarketUserSe func (m *EventMarketUserSettleDisabled) String() string { return proto.CompactTextString(m) } func (*EventMarketUserSettleDisabled) ProtoMessage() {} func (*EventMarketUserSettleDisabled) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{9} + return fileDescriptor_c1b69385a348cffa, []int{10} } func (m *EventMarketUserSettleDisabled) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -642,7 +733,7 @@ func (m *EventMarketPermissionsUpdated) Reset() { *m = EventMarketPermis func (m *EventMarketPermissionsUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketPermissionsUpdated) ProtoMessage() {} func (*EventMarketPermissionsUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{10} + return fileDescriptor_c1b69385a348cffa, []int{11} } func (m *EventMarketPermissionsUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -697,7 +788,7 @@ func (m *EventMarketReqAttrUpdated) Reset() { *m = EventMarketReqAttrUpd func (m *EventMarketReqAttrUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketReqAttrUpdated) ProtoMessage() {} func (*EventMarketReqAttrUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{11} + return fileDescriptor_c1b69385a348cffa, []int{12} } func (m *EventMarketReqAttrUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -750,7 +841,7 @@ func (m *EventMarketCreated) Reset() { *m = EventMarketCreated{} } func (m *EventMarketCreated) String() string { return proto.CompactTextString(m) } func (*EventMarketCreated) ProtoMessage() {} func (*EventMarketCreated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{12} + return fileDescriptor_c1b69385a348cffa, []int{13} } func (m *EventMarketCreated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -796,7 +887,7 @@ func (m *EventMarketFeesUpdated) Reset() { *m = EventMarketFeesUpdated{} func (m *EventMarketFeesUpdated) String() string { return proto.CompactTextString(m) } func (*EventMarketFeesUpdated) ProtoMessage() {} func (*EventMarketFeesUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{13} + return fileDescriptor_c1b69385a348cffa, []int{14} } func (m *EventMarketFeesUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -840,7 +931,7 @@ func (m *EventParamsUpdated) Reset() { *m = EventParamsUpdated{} } func (m *EventParamsUpdated) String() string { return proto.CompactTextString(m) } func (*EventParamsUpdated) ProtoMessage() {} func (*EventParamsUpdated) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b69385a348cffa, []int{14} + return fileDescriptor_c1b69385a348cffa, []int{15} } func (m *EventParamsUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -874,6 +965,7 @@ func init() { proto.RegisterType((*EventOrderCancelled)(nil), "provenance.exchange.v1.EventOrderCancelled") proto.RegisterType((*EventOrderFilled)(nil), "provenance.exchange.v1.EventOrderFilled") proto.RegisterType((*EventOrderPartiallyFilled)(nil), "provenance.exchange.v1.EventOrderPartiallyFilled") + proto.RegisterType((*EventOrderExternalIDUpdated)(nil), "provenance.exchange.v1.EventOrderExternalIDUpdated") proto.RegisterType((*EventMarketWithdraw)(nil), "provenance.exchange.v1.EventMarketWithdraw") proto.RegisterType((*EventMarketDetailsUpdated)(nil), "provenance.exchange.v1.EventMarketDetailsUpdated") proto.RegisterType((*EventMarketEnabled)(nil), "provenance.exchange.v1.EventMarketEnabled") @@ -892,41 +984,43 @@ func init() { } var fileDescriptor_c1b69385a348cffa = []byte{ - // 535 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x95, 0xc1, 0x6e, 0xd3, 0x4c, - 0x10, 0xc7, 0xe3, 0xef, 0x0b, 0xa5, 0x99, 0x82, 0x04, 0x26, 0x8a, 0x12, 0xa0, 0x56, 0x65, 0x2e, - 0xbd, 0xd4, 0x56, 0x84, 0x10, 0x12, 0x9c, 0x1a, 0xda, 0x4a, 0x1c, 0x2a, 0x22, 0x97, 0x0a, 0x89, - 0x4b, 0xb4, 0xb1, 0x87, 0x64, 0xa9, 0xbd, 0xeb, 0xee, 0x6e, 0xd2, 0xf8, 0x2d, 0x78, 0x18, 0xde, - 0x80, 0x0b, 0xc7, 0x8a, 0x13, 0x47, 0x94, 0xbc, 0x08, 0xf2, 0xda, 0x4e, 0x1c, 0xa9, 0x4a, 0x7b, - 0x31, 0xb7, 0xfc, 0x27, 0xff, 0x99, 0xdf, 0xce, 0xec, 0xc8, 0x0b, 0x2f, 0x62, 0xc1, 0xa7, 0xc8, - 0x08, 0xf3, 0xd1, 0xc5, 0x99, 0x3f, 0x26, 0x6c, 0x84, 0xee, 0xb4, 0xeb, 0xe2, 0x14, 0x99, 0x92, - 0x4e, 0x2c, 0xb8, 0xe2, 0x66, 0x6b, 0x65, 0x72, 0x0a, 0x93, 0x33, 0xed, 0x3e, 0xed, 0xf8, 0x5c, - 0x46, 0x5c, 0x0e, 0xb4, 0xcb, 0xcd, 0x44, 0x96, 0x62, 0x9f, 0xc2, 0xe3, 0xe3, 0xb4, 0xc4, 0x07, - 0x11, 0xa0, 0x78, 0x27, 0x90, 0x28, 0x0c, 0xcc, 0x0e, 0x6c, 0xf3, 0x54, 0x0f, 0x68, 0xd0, 0x36, - 0xf6, 0x8c, 0xfd, 0xba, 0x77, 0x5f, 0xeb, 0xf7, 0x81, 0xb9, 0x0b, 0x90, 0xfd, 0xa5, 0x92, 0x18, - 0xdb, 0xff, 0xed, 0x19, 0xfb, 0x0d, 0xaf, 0xa1, 0x23, 0x1f, 0x93, 0x18, 0xed, 0x08, 0x9e, 0x94, - 0xca, 0xa5, 0x07, 0x09, 0xc3, 0xcd, 0x05, 0xdf, 0xc2, 0x03, 0xbf, 0xf0, 0x0d, 0x86, 0x49, 0x56, - 0xb2, 0xd7, 0xfe, 0xf5, 0xfd, 0xa0, 0x99, 0x1f, 0xf4, 0x30, 0x08, 0x04, 0x4a, 0x79, 0xa6, 0x04, - 0x65, 0x23, 0x6f, 0x67, 0xe9, 0xee, 0x25, 0x36, 0x87, 0x47, 0x2b, 0xdc, 0x09, 0xbd, 0x8d, 0xd5, - 0x82, 0x2d, 0x22, 0x25, 0x2a, 0x99, 0x1f, 0x3c, 0x57, 0x66, 0x13, 0xee, 0xc5, 0x82, 0xfa, 0xd8, - 0xfe, 0x5f, 0x87, 0x33, 0x61, 0x9a, 0x50, 0xff, 0x82, 0x28, 0xdb, 0x75, 0x1d, 0xd4, 0xbf, 0xed, - 0x19, 0x74, 0x56, 0xc0, 0x3e, 0x11, 0x8a, 0x92, 0x30, 0x4c, 0xfe, 0x05, 0xf9, 0x87, 0x91, 0x8f, - 0xf6, 0x94, 0x88, 0x0b, 0x54, 0x9f, 0xa8, 0x1a, 0x07, 0x82, 0x5c, 0x99, 0xcf, 0xa0, 0x11, 0xe9, - 0x48, 0x41, 0x7d, 0xe8, 0x6d, 0x67, 0x81, 0x1c, 0x1b, 0xf1, 0x09, 0x53, 0x4b, 0xac, 0x56, 0xe6, - 0x1b, 0xd8, 0x09, 0x50, 0x2a, 0xca, 0x88, 0xa2, 0x9c, 0x65, 0xf0, 0x4d, 0x33, 0x2f, 0x99, 0xd3, - 0x0b, 0xbb, 0xca, 0xe1, 0x2c, 0xbd, 0xb0, 0xfa, 0x6d, 0xc9, 0x4b, 0x77, 0x2f, 0xb1, 0x2f, 0xf3, - 0xf9, 0x65, 0x4d, 0x1c, 0xa1, 0x22, 0x34, 0x94, 0xe7, 0x71, 0xa0, 0xd7, 0x6e, 0x63, 0x2b, 0xaf, - 0x01, 0x26, 0x99, 0xef, 0x2e, 0x5b, 0xd2, 0xc8, 0xbd, 0xbd, 0xc4, 0xfe, 0x0a, 0x66, 0x09, 0x79, - 0xcc, 0xc8, 0x30, 0xac, 0x8c, 0x75, 0xb1, 0x76, 0x47, 0x47, 0x54, 0x56, 0x09, 0x53, 0xf0, 0xbc, - 0x04, 0x3b, 0x97, 0x28, 0xce, 0x50, 0xa9, 0x10, 0xab, 0x6d, 0x71, 0x02, 0xbb, 0x37, 0x52, 0x2b, - 0x6e, 0x76, 0x1d, 0xdb, 0x47, 0x11, 0x51, 0x29, 0x29, 0x67, 0x15, 0x2f, 0xcf, 0xfa, 0xbe, 0x7a, - 0x78, 0x79, 0xa8, 0x94, 0xa8, 0x16, 0xd9, 0x5d, 0xdb, 0xd7, 0xe2, 0x93, 0xbc, 0x89, 0x65, 0xbf, - 0x82, 0x56, 0x29, 0xe5, 0x04, 0xf1, 0x4e, 0x53, 0xb1, 0x9b, 0x39, 0xa9, 0x4f, 0x04, 0x89, 0x8a, - 0x94, 0x1e, 0xfe, 0x9c, 0x5b, 0xc6, 0xf5, 0xdc, 0x32, 0xfe, 0xcc, 0x2d, 0xe3, 0xdb, 0xc2, 0xaa, - 0x5d, 0x2f, 0xac, 0xda, 0xef, 0x85, 0x55, 0x83, 0x0e, 0xe5, 0xce, 0xcd, 0x2f, 0x4c, 0xdf, 0xf8, - 0xec, 0x8c, 0xa8, 0x1a, 0x4f, 0x86, 0x8e, 0xcf, 0x23, 0x77, 0x65, 0x3a, 0xa0, 0xbc, 0xa4, 0xdc, - 0xd9, 0xf2, 0xed, 0x1a, 0x6e, 0xe9, 0xf7, 0xe7, 0xe5, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x24, - 0x2e, 0x9d, 0xf5, 0xd9, 0x06, 0x00, 0x00, + // 574 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x55, 0x4d, 0x6f, 0xd3, 0x40, + 0x10, 0xad, 0x21, 0x2d, 0xcd, 0x14, 0x24, 0x30, 0x55, 0x94, 0x50, 0x6a, 0x2a, 0x73, 0xe9, 0xa5, + 0xb6, 0x22, 0x84, 0x90, 0xe0, 0xd4, 0x90, 0x54, 0xea, 0x01, 0x11, 0xa5, 0x54, 0x08, 0x2e, 0xd1, + 0xc6, 0x1e, 0x92, 0xa5, 0xf6, 0xae, 0xbb, 0xbb, 0x49, 0xe3, 0x3f, 0x81, 0x7a, 0xe5, 0x7f, 0xf0, + 0x0f, 0xb8, 0x70, 0xac, 0x38, 0x71, 0x44, 0xc9, 0x1f, 0x41, 0xfe, 0x4a, 0x1c, 0xa8, 0x92, 0x5e, + 0x2c, 0x71, 0xf3, 0x8c, 0xdf, 0xec, 0x7b, 0x6f, 0xf7, 0x49, 0x03, 0x4f, 0x03, 0xc1, 0x47, 0xc8, + 0x08, 0x73, 0xd0, 0xc6, 0xb1, 0x33, 0x20, 0xac, 0x8f, 0xf6, 0xa8, 0x6e, 0xe3, 0x08, 0x99, 0x92, + 0x56, 0x20, 0xb8, 0xe2, 0x7a, 0x65, 0x0e, 0xb2, 0x32, 0x90, 0x35, 0xaa, 0x3f, 0xaa, 0x39, 0x5c, + 0xfa, 0x5c, 0x76, 0x63, 0x94, 0x9d, 0x14, 0xc9, 0x88, 0xc9, 0xe0, 0x41, 0x2b, 0x3a, 0xe2, 0xad, + 0x70, 0x51, 0xbc, 0x16, 0x48, 0x14, 0xba, 0x7a, 0x0d, 0x36, 0x79, 0x54, 0x77, 0xa9, 0x5b, 0xd5, + 0xf6, 0xb4, 0xfd, 0x52, 0xe7, 0x4e, 0x5c, 0x1f, 0xbb, 0xfa, 0x2e, 0x40, 0xf2, 0x4b, 0x85, 0x01, + 0x56, 0x6f, 0xed, 0x69, 0xfb, 0xe5, 0x4e, 0x39, 0xee, 0xbc, 0x0b, 0x03, 0xd4, 0x9f, 0xc0, 0x16, + 0x8e, 0x15, 0x0a, 0x46, 0xbc, 0x68, 0xf8, 0x76, 0xfc, 0x1f, 0xb2, 0xd6, 0xb1, 0x6b, 0x7e, 0xd1, + 0xe0, 0x61, 0x8e, 0x30, 0x92, 0xea, 0x79, 0xcb, 0x29, 0x5f, 0xc1, 0x5d, 0x27, 0xc3, 0x75, 0x7b, + 0x61, 0x42, 0xda, 0xa8, 0xfe, 0xfc, 0x76, 0xb0, 0x9d, 0x5a, 0x39, 0x74, 0x5d, 0x81, 0x52, 0x9e, + 0x28, 0x41, 0x59, 0xbf, 0xb3, 0x35, 0x43, 0x37, 0xc2, 0xd5, 0x82, 0x2e, 0x35, 0xb8, 0x3f, 0x17, + 0x74, 0x44, 0x57, 0xa9, 0xa9, 0xc0, 0x06, 0x91, 0x12, 0x95, 0x4c, 0xcd, 0xa7, 0x95, 0xbe, 0x0d, + 0xeb, 0x81, 0xa0, 0x0e, 0xa6, 0x14, 0x49, 0xa1, 0xeb, 0x50, 0xfa, 0x84, 0x28, 0xab, 0xa5, 0xb8, + 0x19, 0x7f, 0xff, 0x2d, 0x69, 0xfd, 0x1f, 0x49, 0x5f, 0x35, 0xa8, 0xcd, 0x25, 0xb5, 0x89, 0x50, + 0x94, 0x78, 0x5e, 0xf8, 0x5f, 0x68, 0xfb, 0x00, 0x3b, 0x73, 0x69, 0xad, 0xac, 0xdf, 0x3c, 0x0d, + 0xdc, 0x55, 0xc9, 0x59, 0xf9, 0x12, 0xdf, 0xb3, 0x68, 0xbc, 0x21, 0xe2, 0x0c, 0xd5, 0x7b, 0xaa, + 0x06, 0xae, 0x20, 0x17, 0xfa, 0x0e, 0x94, 0xfd, 0xb8, 0x93, 0x1d, 0x7a, 0xaf, 0xb3, 0x99, 0x34, + 0x52, 0xcb, 0x3e, 0x1f, 0x32, 0x35, 0xb3, 0x1c, 0x57, 0xfa, 0x4b, 0xd8, 0x72, 0x51, 0x2a, 0xca, + 0x88, 0xa2, 0x9c, 0x25, 0x6c, 0xcb, 0x32, 0x93, 0x03, 0x47, 0x81, 0xbb, 0x48, 0xc9, 0x59, 0x14, + 0xb8, 0xd2, 0xaa, 0xe1, 0x19, 0xba, 0x11, 0x9a, 0xe7, 0xe9, 0xdb, 0x25, 0x26, 0x9a, 0xa8, 0x08, + 0xf5, 0x64, 0x76, 0x3d, 0x4b, 0xad, 0xbc, 0x00, 0x18, 0x26, 0xb8, 0x9b, 0xa4, 0xbc, 0x9c, 0x62, + 0x1b, 0xa1, 0xf9, 0x19, 0xf4, 0x1c, 0x65, 0x8b, 0x91, 0x9e, 0x57, 0x18, 0xd7, 0xd9, 0xc2, 0x1b, + 0x35, 0xa9, 0x2c, 0x92, 0x4c, 0xc1, 0xe3, 0x1c, 0xd9, 0xa9, 0x44, 0x71, 0x82, 0x4a, 0x79, 0x58, + 0xac, 0xc5, 0x21, 0xec, 0x5e, 0xcb, 0x5a, 0xb0, 0xd9, 0x45, 0xda, 0x36, 0x0a, 0x9f, 0x4a, 0x49, + 0x39, 0x2b, 0x38, 0x3c, 0x8b, 0x79, 0xed, 0xe0, 0xf9, 0xa1, 0x52, 0xa2, 0x58, 0xca, 0xfa, 0x42, + 0x5e, 0xb3, 0xa5, 0xb3, 0x8c, 0xcb, 0x7c, 0x0e, 0x95, 0xdc, 0xc8, 0x11, 0xe2, 0x8d, 0x6e, 0xc5, + 0xdc, 0x4e, 0x99, 0xda, 0x44, 0x10, 0x3f, 0x1b, 0x69, 0xe0, 0x8f, 0x89, 0xa1, 0x5d, 0x4d, 0x0c, + 0xed, 0xf7, 0xc4, 0xd0, 0x2e, 0xa7, 0xc6, 0xda, 0xd5, 0xd4, 0x58, 0xfb, 0x35, 0x35, 0xd6, 0xa0, + 0x46, 0xb9, 0x75, 0xfd, 0x0e, 0x6d, 0x6b, 0x1f, 0xad, 0x3e, 0x55, 0x83, 0x61, 0xcf, 0x72, 0xb8, + 0x6f, 0xcf, 0x41, 0x07, 0x94, 0xe7, 0x2a, 0x7b, 0x3c, 0xdb, 0xce, 0xbd, 0x8d, 0x78, 0xc3, 0x3e, + 0xfb, 0x13, 0x00, 0x00, 0xff, 0xff, 0xf3, 0xcb, 0x11, 0xaf, 0xbb, 0x07, 0x00, 0x00, } func (m *EventOrderCreated) Marshal() (dAtA []byte, err error) { @@ -949,6 +1043,13 @@ func (m *EventOrderCreated) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ExternalId) > 0 { + i -= len(m.ExternalId) + copy(dAtA[i:], m.ExternalId) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ExternalId))) + i-- + dAtA[i] = 0x1a + } if len(m.OrderType) > 0 { i -= len(m.OrderType) copy(dAtA[i:], m.OrderType) @@ -984,6 +1085,13 @@ func (m *EventOrderCancelled) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ExternalId) > 0 { + i -= len(m.ExternalId) + copy(dAtA[i:], m.ExternalId) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ExternalId))) + i-- + dAtA[i] = 0x1a + } if len(m.CancelledBy) > 0 { i -= len(m.CancelledBy) copy(dAtA[i:], m.CancelledBy) @@ -1019,6 +1127,13 @@ func (m *EventOrderFilled) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ExternalId) > 0 { + i -= len(m.ExternalId) + copy(dAtA[i:], m.ExternalId) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ExternalId))) + i-- + dAtA[i] = 0x2a + } if len(m.Fees) > 0 { i -= len(m.Fees) copy(dAtA[i:], m.Fees) @@ -1068,6 +1183,13 @@ func (m *EventOrderPartiallyFilled) MarshalToSizedBuffer(dAtA []byte) (int, erro _ = i var l int _ = l + if len(m.ExternalId) > 0 { + i -= len(m.ExternalId) + copy(dAtA[i:], m.ExternalId) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ExternalId))) + i-- + dAtA[i] = 0x2a + } if len(m.Fees) > 0 { i -= len(m.Fees) copy(dAtA[i:], m.Fees) @@ -1097,6 +1219,41 @@ func (m *EventOrderPartiallyFilled) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } +func (m *EventOrderExternalIDUpdated) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventOrderExternalIDUpdated) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventOrderExternalIDUpdated) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ExternalId) > 0 { + i -= len(m.ExternalId) + copy(dAtA[i:], m.ExternalId) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ExternalId))) + i-- + dAtA[i] = 0x1a + } + if m.OrderId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.OrderId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *EventMarketWithdraw) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1494,6 +1651,10 @@ func (m *EventOrderCreated) Size() (n int) { if l > 0 { n += 1 + l + sovEvents(uint64(l)) } + l = len(m.ExternalId) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } return n } @@ -1510,6 +1671,10 @@ func (m *EventOrderCancelled) Size() (n int) { if l > 0 { n += 1 + l + sovEvents(uint64(l)) } + l = len(m.ExternalId) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } return n } @@ -1534,6 +1699,10 @@ func (m *EventOrderFilled) Size() (n int) { if l > 0 { n += 1 + l + sovEvents(uint64(l)) } + l = len(m.ExternalId) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } return n } @@ -1558,6 +1727,26 @@ func (m *EventOrderPartiallyFilled) Size() (n int) { if l > 0 { n += 1 + l + sovEvents(uint64(l)) } + l = len(m.ExternalId) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventOrderExternalIDUpdated) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OrderId != 0 { + n += 1 + sovEvents(uint64(m.OrderId)) + } + l = len(m.ExternalId) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } return n } @@ -1816,6 +2005,38 @@ func (m *EventOrderCreated) Unmarshal(dAtA []byte) error { } m.OrderType = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExternalId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -1917,6 +2138,38 @@ func (m *EventOrderCancelled) Unmarshal(dAtA []byte) error { } m.CancelledBy = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExternalId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -2082,6 +2335,38 @@ func (m *EventOrderFilled) Unmarshal(dAtA []byte) error { } m.Fees = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExternalId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -2247,6 +2532,139 @@ func (m *EventOrderPartiallyFilled) Unmarshal(dAtA []byte) error { } m.Fees = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExternalId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventOrderExternalIDUpdated) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventOrderExternalIDUpdated: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventOrderExternalIDUpdated: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) + } + m.OrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExternalId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go index 5fd2bfc999..cdd4ca14d2 100644 --- a/x/exchange/events_test.go +++ b/x/exchange/events_test.go @@ -26,11 +26,11 @@ func assertEverythingSet(t *testing.T, tev proto.Message, typeString string) boo expType := "provenance.exchange.v1." + typeString rv := assert.Equal(t, expType, event.Type, "%T event.Type", tev) - for i, attrs := range event.Attributes { - rv = assert.NotEmpty(t, attrs.Key, "%T event.attributes[%d].Key", tev, i) && rv - rv = assert.NotEqual(t, `""`, attrs.Key, "%T event.attributes[%d].Key", tev, i) && rv - rv = assert.NotEmpty(t, attrs.Value, "%T event.attributes[%d].Value", tev, i) && rv - rv = assert.NotEqual(t, `""`, attrs.Value, "%T event.attributes[%d].Value", tev, i) && rv + for i, attr := range event.Attributes { + rv = assert.NotEmpty(t, attr.Key, "%T event.attributes[%d].Key", tev, i) && rv + rv = assert.NotEqual(t, `""`, string(attr.Key), "%T event.attributes[%d].Key", tev, i) && rv + rv = assert.NotEmpty(t, attr.Value, "%T event.attributes[%d].Value", tev, i) && rv + rv = assert.NotEqual(t, `""`, string(attr.Value), "%T event.attributes[%d].Value", tev, i) && rv } return rv } @@ -40,29 +40,29 @@ func TestNewEventOrderCreated(t *testing.T) { name string order *Order expected *EventOrderCreated + expPanic string }{ { - name: "nil order", - order: NewOrder(3), - expected: &EventOrderCreated{ - OrderId: 3, - OrderType: "", - }, + name: "nil order", + order: NewOrder(3), + expPanic: "order 3 has unknown sub-order type : does not implement SubOrderI", }, { name: "order with ask", - order: NewOrder(1).WithAsk(&AskOrder{}), + order: NewOrder(1).WithAsk(&AskOrder{ExternalId: "oneoneone"}), expected: &EventOrderCreated{ - OrderId: 1, - OrderType: "ask", + OrderId: 1, + OrderType: "ask", + ExternalId: "oneoneone", }, }, { name: "order with bid", - order: NewOrder(2).WithBid(&BidOrder{}), + order: NewOrder(2).WithBid(&BidOrder{ExternalId: "twotwotwo"}), expected: &EventOrderCreated{ - OrderId: 2, - OrderType: "bid", + OrderId: 2, + OrderType: "bid", + ExternalId: "twotwotwo", }, }, } @@ -73,7 +73,10 @@ func TestNewEventOrderCreated(t *testing.T) { testFunc := func() { actual = NewEventOrderCreated(tc.order) } - require.NotPanics(t, testFunc, "NewEventOrderCreated") + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "NewEventOrderCreated") + if len(tc.expPanic) > 0 { + return + } assert.Equal(t, tc.expected, actual, "NewEventOrderCreated result") assertEverythingSet(t, actual, "EventOrderCreated") }) @@ -81,13 +84,45 @@ func TestNewEventOrderCreated(t *testing.T) { } func TestNewEventOrderCancelled(t *testing.T) { - orderID := uint64(101) - cancelledBy := sdk.AccAddress("cancelledBy_________") + tests := []struct { + name string + order OrderI + cancelledBy sdk.AccAddress + expected *EventOrderCancelled + }{ + { + name: "ask order", + order: NewOrder(11).WithAsk(&AskOrder{ExternalId: "an external identifier"}), + cancelledBy: sdk.AccAddress("CancelledBy_________"), + expected: &EventOrderCancelled{ + OrderId: 11, + CancelledBy: sdk.AccAddress("CancelledBy_________").String(), + ExternalId: "an external identifier", + }, + }, + { + name: "bid order", + order: NewOrder(55).WithAsk(&AskOrder{ExternalId: "another external identifier"}), + cancelledBy: sdk.AccAddress("cancelled_by________"), + expected: &EventOrderCancelled{ + OrderId: 55, + CancelledBy: sdk.AccAddress("cancelled_by________").String(), + ExternalId: "another external identifier", + }, + }, + } - event := NewEventOrderCancelled(orderID, cancelledBy) - assert.Equal(t, orderID, event.OrderId, "OrderId") - assert.Equal(t, cancelledBy.String(), event.CancelledBy, "CancelledBy") - assertEverythingSet(t, event, "EventOrderCancelled") + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var event *EventOrderCancelled + testFunc := func() { + event = NewEventOrderCancelled(tc.order, tc.cancelledBy) + } + require.NotPanics(t, testFunc, "NewEventOrderCancelled") + assert.Equal(t, tc.expected, event, "NewEventOrderCancelled result") + assertEverythingSet(t, event, "EventOrderCancelled") + }) + } } func TestNewEventOrderFilled(t *testing.T) { @@ -97,12 +132,9 @@ func TestNewEventOrderFilled(t *testing.T) { } tests := []struct { - name string - order OrderI - expOrderID uint64 - expAssets string - expPrice string - expFees string + name string + order OrderI + expected *EventOrderFilled }{ { name: "ask", @@ -110,11 +142,15 @@ func TestNewEventOrderFilled(t *testing.T) { Assets: sdk.NewInt64Coin("apple", 22), Price: sdk.NewInt64Coin("plum", 18), SellerSettlementFlatFee: coinP("fig", 57), + ExternalId: "one", }), - expOrderID: 4, - expAssets: "22apple", - expPrice: "18plum", - expFees: "57fig", + expected: &EventOrderFilled{ + OrderId: 4, + Assets: "22apple", + Price: "18plum", + Fees: "57fig", + ExternalId: "one", + }, }, { name: "filled ask", @@ -122,11 +158,15 @@ func TestNewEventOrderFilled(t *testing.T) { Assets: sdk.NewInt64Coin("apple", 22), Price: sdk.NewInt64Coin("plum", 18), SellerSettlementFlatFee: coinP("fig", 57), + ExternalId: "two", }), sdk.NewInt64Coin("plum", 88), sdk.NewCoins(sdk.NewInt64Coin("fig", 61), sdk.NewInt64Coin("grape", 12))), - expOrderID: 4, - expAssets: "22apple", - expPrice: "88plum", - expFees: "61fig,12grape", + expected: &EventOrderFilled{ + OrderId: 4, + Assets: "22apple", + Price: "88plum", + Fees: "61fig,12grape", + ExternalId: "two", + }, }, { name: "bid", @@ -134,23 +174,31 @@ func TestNewEventOrderFilled(t *testing.T) { Assets: sdk.NewInt64Coin("apple", 23), Price: sdk.NewInt64Coin("plum", 19), BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 58)), + ExternalId: "three", }), - expOrderID: 104, - expAssets: "23apple", - expPrice: "19plum", - expFees: "58fig", + expected: &EventOrderFilled{ + OrderId: 104, + Assets: "23apple", + Price: "19plum", + Fees: "58fig", + ExternalId: "three", + }, }, { name: "filled bid", - order: NewFilledOrder(NewOrder(104).WithBid(&BidOrder{ - Assets: sdk.NewInt64Coin("apple", 23), - Price: sdk.NewInt64Coin("plum", 19), - BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 58)), + order: NewFilledOrder(NewOrder(105).WithBid(&BidOrder{ + Assets: sdk.NewInt64Coin("apple", 24), + Price: sdk.NewInt64Coin("plum", 20), + BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 59)), + ExternalId: "four", }), sdk.NewInt64Coin("plum", 89), sdk.NewCoins(sdk.NewInt64Coin("fig", 62), sdk.NewInt64Coin("grape", 13))), - expOrderID: 104, - expAssets: "23apple", - expPrice: "89plum", - expFees: "62fig,13grape", + expected: &EventOrderFilled{ + OrderId: 105, + Assets: "24apple", + Price: "89plum", + Fees: "62fig,13grape", + ExternalId: "four", + }, }, } @@ -161,10 +209,7 @@ func TestNewEventOrderFilled(t *testing.T) { event = NewEventOrderFilled(tc.order) } require.NotPanics(t, testFunc, "NewEventOrderFilled") - assert.Equal(t, tc.expOrderID, event.OrderId, "OrderId") - assert.Equal(t, tc.expAssets, event.Assets, "Assets") - assert.Equal(t, tc.expPrice, event.Price, "Price") - assert.Equal(t, tc.expFees, event.Fees, "Fees") + assert.Equal(t, tc.expected, event, "NewEventOrderFilled result") assertEverythingSet(t, event, "EventOrderFilled") }) } @@ -177,12 +222,9 @@ func TestNewEventOrderPartiallyFilled(t *testing.T) { } tests := []struct { - name string - order OrderI - expOrderID uint64 - expAssets string - expPrice string - expFees string + name string + order OrderI + expected *EventOrderPartiallyFilled }{ { name: "ask", @@ -190,11 +232,15 @@ func TestNewEventOrderPartiallyFilled(t *testing.T) { Assets: sdk.NewInt64Coin("apple", 22), Price: sdk.NewInt64Coin("plum", 18), SellerSettlementFlatFee: coinP("fig", 57), + ExternalId: "five", }), - expOrderID: 4, - expAssets: "22apple", - expPrice: "18plum", - expFees: "57fig", + expected: &EventOrderPartiallyFilled{ + OrderId: 4, + Assets: "22apple", + Price: "18plum", + Fees: "57fig", + ExternalId: "five", + }, }, { name: "filled ask", @@ -202,11 +248,15 @@ func TestNewEventOrderPartiallyFilled(t *testing.T) { Assets: sdk.NewInt64Coin("apple", 22), Price: sdk.NewInt64Coin("plum", 18), SellerSettlementFlatFee: coinP("fig", 57), + ExternalId: "six", }), sdk.NewInt64Coin("plum", 88), sdk.NewCoins(sdk.NewInt64Coin("fig", 61), sdk.NewInt64Coin("grape", 12))), - expOrderID: 4, - expAssets: "22apple", - expPrice: "88plum", - expFees: "61fig,12grape", + expected: &EventOrderPartiallyFilled{ + OrderId: 4, + Assets: "22apple", + Price: "88plum", + Fees: "61fig,12grape", + ExternalId: "six", + }, }, { name: "bid", @@ -214,11 +264,15 @@ func TestNewEventOrderPartiallyFilled(t *testing.T) { Assets: sdk.NewInt64Coin("apple", 23), Price: sdk.NewInt64Coin("plum", 19), BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 58)), + ExternalId: "seven", }), - expOrderID: 104, - expAssets: "23apple", - expPrice: "19plum", - expFees: "58fig", + expected: &EventOrderPartiallyFilled{ + OrderId: 104, + Assets: "23apple", + Price: "19plum", + Fees: "58fig", + ExternalId: "seven", + }, }, { name: "filled bid", @@ -226,11 +280,15 @@ func TestNewEventOrderPartiallyFilled(t *testing.T) { Assets: sdk.NewInt64Coin("apple", 23), Price: sdk.NewInt64Coin("plum", 19), BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 58)), + ExternalId: "eight", }), sdk.NewInt64Coin("plum", 89), sdk.NewCoins(sdk.NewInt64Coin("fig", 62), sdk.NewInt64Coin("grape", 13))), - expOrderID: 104, - expAssets: "23apple", - expPrice: "89plum", - expFees: "62fig,13grape", + expected: &EventOrderPartiallyFilled{ + OrderId: 104, + Assets: "23apple", + Price: "89plum", + Fees: "62fig,13grape", + ExternalId: "eight", + }, }, } @@ -240,23 +298,62 @@ func TestNewEventOrderPartiallyFilled(t *testing.T) { testFunc := func() { event = NewEventOrderPartiallyFilled(tc.order) } - require.NotPanics(t, testFunc, "NewEventOrderFilled") - assert.Equal(t, tc.expOrderID, event.OrderId, "OrderId") - assert.Equal(t, tc.expAssets, event.Assets, "Assets") - assert.Equal(t, tc.expPrice, event.Price, "Price") - assert.Equal(t, tc.expFees, event.Fees, "Fees") + require.NotPanics(t, testFunc, "NewEventOrderPartiallyFilled") + assert.Equal(t, tc.expected, event, "NewEventOrderPartiallyFilled result") assertEverythingSet(t, event, "EventOrderPartiallyFilled") }) } } +func TestNewEventOrderExternalIDUpdated(t *testing.T) { + tests := []struct { + name string + order OrderI + expected *EventOrderExternalIDUpdated + }{ + { + name: "ask", + order: NewOrder(51).WithAsk(&AskOrder{ExternalId: "orange-red"}), + expected: &EventOrderExternalIDUpdated{ + OrderId: 51, + ExternalId: "orange-red", + }, + }, + { + name: "bid", + order: NewOrder(777).WithAsk(&AskOrder{ExternalId: "purple-purple"}), + expected: &EventOrderExternalIDUpdated{ + OrderId: 777, + ExternalId: "purple-purple", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var event *EventOrderExternalIDUpdated + testFunc := func() { + event = NewEventOrderExternalIDUpdated(tc.order) + } + require.NotPanics(t, testFunc, "NewEventOrderExternalIDUpdated") + assert.Equal(t, tc.expected, event, "NewEventOrderExternalIDUpdated result") + assertEverythingSet(t, event, "EventOrderExternalIDUpdated") + }) + } +} + func TestNewEventMarketWithdraw(t *testing.T) { marketID := uint32(55) amountWithdrawn := sdk.NewCoins(sdk.NewInt64Coin("mine", 188382), sdk.NewInt64Coin("yours", 3)) destination := sdk.AccAddress("destination_________") withdrawnBy := sdk.AccAddress("withdrawnBy_________") - event := NewEventMarketWithdraw(marketID, amountWithdrawn, destination, withdrawnBy) + var event *EventMarketWithdraw + testFunc := func() { + event = NewEventMarketWithdraw(marketID, amountWithdrawn, destination, withdrawnBy) + } + require.NotPanics(t, testFunc, "NewEventMarketWithdraw(%d, %q, %q, %q)", + marketID, amountWithdrawn, string(destination), string(withdrawnBy)) assert.Equal(t, marketID, event.MarketId, "MarketId") assert.Equal(t, amountWithdrawn.String(), event.Amount, "Amount") assert.Equal(t, destination.String(), event.Destination, "Destination") @@ -268,7 +365,11 @@ func TestNewEventMarketDetailsUpdated(t *testing.T) { marketID := uint32(84) updatedBy := sdk.AccAddress("updatedBy___________") - event := NewEventMarketDetailsUpdated(marketID, updatedBy) + var event *EventMarketDetailsUpdated + testFunc := func() { + event = NewEventMarketDetailsUpdated(marketID, updatedBy) + } + require.NotPanics(t, testFunc, "NewEventMarketDetailsUpdated(%d, %q)", marketID, string(updatedBy)) assert.Equal(t, marketID, event.MarketId, "MarketId") assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") assertEverythingSet(t, event, "EventMarketDetailsUpdated") @@ -302,8 +403,13 @@ func TestNewEventMarketActiveUpdated(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - actual := NewEventMarketActiveUpdated(tc.marketID, tc.updatedBy, tc.isActive) - assert.Equal(t, tc.expected, actual, "NewEventMarketActiveUpdated(%d, %q, %t) result", + var event proto.Message + testFunc := func() { + event = NewEventMarketActiveUpdated(tc.marketID, tc.updatedBy, tc.isActive) + } + require.NotPanics(t, testFunc, "NewEventMarketActiveUpdated(%d, %q, %t) result", + tc.marketID, tc.updatedBy.String(), tc.isActive) + assert.Equal(t, tc.expected, event, "NewEventMarketActiveUpdated(%d, %q, %t) result", tc.marketID, tc.updatedBy.String(), tc.isActive) }) } @@ -313,7 +419,11 @@ func TestNewEventMarketEnabled(t *testing.T) { marketID := uint32(919) updatedBy := sdk.AccAddress("updatedBy___________") - event := NewEventMarketEnabled(marketID, updatedBy) + var event *EventMarketEnabled + testFunc := func() { + event = NewEventMarketEnabled(marketID, updatedBy) + } + require.NotPanics(t, testFunc, "NewEventMarketEnabled(%d, %q)", marketID, string(updatedBy)) assert.Equal(t, marketID, event.MarketId, "MarketId") assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") assertEverythingSet(t, event, "EventMarketEnabled") @@ -323,7 +433,11 @@ func TestNewEventMarketDisabled(t *testing.T) { marketID := uint32(5555) updatedBy := sdk.AccAddress("updatedBy___________") - event := NewEventMarketDisabled(marketID, updatedBy) + var event *EventMarketDisabled + testFunc := func() { + event = NewEventMarketDisabled(marketID, updatedBy) + } + require.NotPanics(t, testFunc, "NewEventMarketDisabled(%d, %q)", marketID, string(updatedBy)) assert.Equal(t, marketID, event.MarketId, "MarketId") assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") assertEverythingSet(t, event, "EventMarketDisabled") @@ -357,8 +471,13 @@ func TestNewEventMarketUserSettleUpdated(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - actual := NewEventMarketUserSettleUpdated(tc.marketID, tc.updatedBy, tc.isAllowed) - assert.Equal(t, tc.expected, actual, "NewEventMarketUserSettleUpdated(%d, %q, %t) result", + var event proto.Message + testFunc := func() { + event = NewEventMarketUserSettleUpdated(tc.marketID, tc.updatedBy, tc.isAllowed) + } + require.NotPanics(t, testFunc, "NewEventMarketUserSettleUpdated(%d, %q, %t) result", + tc.marketID, tc.updatedBy.String(), tc.isAllowed) + assert.Equal(t, tc.expected, event, "NewEventMarketUserSettleUpdated(%d, %q, %t) result", tc.marketID, tc.updatedBy.String(), tc.isAllowed) }) } @@ -368,7 +487,11 @@ func TestNewEventMarketUserSettleEnabled(t *testing.T) { marketID := uint32(123) updatedBy := sdk.AccAddress("updatedBy___________") - event := NewEventMarketUserSettleEnabled(marketID, updatedBy) + var event *EventMarketUserSettleEnabled + testFunc := func() { + event = NewEventMarketUserSettleEnabled(marketID, updatedBy) + } + require.NotPanics(t, testFunc, "NewEventMarketUserSettleEnabled(%d, %q)", marketID, string(updatedBy)) assert.Equal(t, marketID, event.MarketId, "MarketId") assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") assertEverythingSet(t, event, "EventMarketUserSettleEnabled") @@ -378,7 +501,11 @@ func TestNewEventMarketUserSettleDisabled(t *testing.T) { marketID := uint32(123) updatedBy := sdk.AccAddress("updatedBy___________") - event := NewEventMarketUserSettleDisabled(marketID, updatedBy) + var event *EventMarketUserSettleDisabled + testFunc := func() { + event = NewEventMarketUserSettleDisabled(marketID, updatedBy) + } + require.NotPanics(t, testFunc, "NewEventMarketUserSettleDisabled(%d, %q)", marketID, string(updatedBy)) assert.Equal(t, marketID, event.MarketId, "MarketId") assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") assertEverythingSet(t, event, "EventMarketUserSettleDisabled") @@ -388,7 +515,11 @@ func TestNewEventMarketPermissionsUpdated(t *testing.T) { marketID := uint32(5432) updatedBy := sdk.AccAddress("updatedBy___________") - event := NewEventMarketPermissionsUpdated(marketID, updatedBy) + var event *EventMarketPermissionsUpdated + testFunc := func() { + event = NewEventMarketPermissionsUpdated(marketID, updatedBy) + } + require.NotPanics(t, testFunc, "NewEventMarketPermissionsUpdated(%d, %q)", marketID, string(updatedBy)) assert.Equal(t, marketID, event.MarketId, "MarketId") assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") assertEverythingSet(t, event, "EventMarketPermissionsUpdated") @@ -398,7 +529,11 @@ func TestNewEventMarketReqAttrUpdated(t *testing.T) { marketID := uint32(3334) updatedBy := sdk.AccAddress("updatedBy___________") - event := NewEventMarketReqAttrUpdated(marketID, updatedBy) + var event *EventMarketReqAttrUpdated + testFunc := func() { + event = NewEventMarketReqAttrUpdated(marketID, updatedBy) + } + require.NotPanics(t, testFunc, "NewEventMarketReqAttrUpdated(%d, %q)", marketID, string(updatedBy)) assert.Equal(t, marketID, event.MarketId, "MarketId") assert.Equal(t, updatedBy.String(), event.UpdatedBy, "UpdatedBy") assertEverythingSet(t, event, "EventMarketReqAttrUpdated") @@ -407,7 +542,11 @@ func TestNewEventMarketReqAttrUpdated(t *testing.T) { func TestNewEventMarketCreated(t *testing.T) { marketID := uint32(10111213) - event := NewEventMarketCreated(marketID) + var event *EventMarketCreated + testFunc := func() { + event = NewEventMarketCreated(marketID) + } + require.NotPanics(t, testFunc, "NewEventMarketCreated(%d)", marketID) assert.Equal(t, marketID, event.MarketId, "MarketId") assertEverythingSet(t, event, "EventMarketCreated") } @@ -415,13 +554,21 @@ func TestNewEventMarketCreated(t *testing.T) { func TestNewEventMarketFeesUpdated(t *testing.T) { marketID := uint32(1415) - event := NewEventMarketFeesUpdated(marketID) + var event *EventMarketFeesUpdated + testFunc := func() { + event = NewEventMarketFeesUpdated(marketID) + } + require.NotPanics(t, testFunc, "NewEventMarketFeesUpdated(%d)", marketID) assert.Equal(t, marketID, event.MarketId, "MarketId") assertEverythingSet(t, event, "EventMarketFeesUpdated") } func TestNewEventParamsUpdated(t *testing.T) { - event := NewEventParamsUpdated() + var event *EventParamsUpdated + testFunc := func() { + event = NewEventParamsUpdated() + } + require.NotPanics(t, testFunc, "NewEventParamsUpdated()") assertEverythingSet(t, event, "EventParamsUpdated") } @@ -453,10 +600,11 @@ func TestTypedEventToEvent(t *testing.T) { }{ { name: "EventOrderCreated ask", - tev: NewEventOrderCreated(NewOrder(1).WithAsk(&AskOrder{})), + tev: NewEventOrderCreated(NewOrder(1).WithAsk(&AskOrder{ExternalId: "stuff"})), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderCreated", Attributes: []abci.EventAttribute{ + {Key: []byte("external_id"), Value: quoteBz("stuff")}, {Key: []byte("order_id"), Value: quoteBz("1")}, {Key: []byte("order_type"), Value: quoteBz("ask")}, }, @@ -464,22 +612,36 @@ func TestTypedEventToEvent(t *testing.T) { }, { name: "EventOrderCreated bid", - tev: NewEventOrderCreated(NewOrder(2).WithBid(&BidOrder{})), + tev: NewEventOrderCreated(NewOrder(2).WithBid(&BidOrder{ExternalId: "something else"})), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderCreated", Attributes: []abci.EventAttribute{ + {Key: []byte("external_id"), Value: quoteBz("something else")}, {Key: []byte("order_id"), Value: quoteBz("2")}, {Key: []byte("order_type"), Value: quoteBz("bid")}, }, }, }, { - name: "EventOrderCancelled", - tev: NewEventOrderCancelled(3, cancelledBy), + name: "EventOrderCancelled ask", + tev: NewEventOrderCancelled(NewOrder(3).WithAsk(&AskOrder{ExternalId: "outside 8"}), cancelledBy), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderCancelled", Attributes: []abci.EventAttribute{ {Key: []byte("cancelled_by"), Value: cancelledByQ}, + {Key: []byte("external_id"), Value: quoteBz("outside 8")}, + {Key: []byte("order_id"), Value: quoteBz("3")}, + }, + }, + }, + { + name: "EventOrderCancelled bid", + tev: NewEventOrderCancelled(NewOrder(3).WithBid(&BidOrder{ExternalId: "outside 8"}), cancelledBy), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventOrderCancelled", + Attributes: []abci.EventAttribute{ + {Key: []byte("cancelled_by"), Value: cancelledByQ}, + {Key: []byte("external_id"), Value: quoteBz("outside 8")}, {Key: []byte("order_id"), Value: quoteBz("3")}, }, }, @@ -490,11 +652,13 @@ func TestTypedEventToEvent(t *testing.T) { Assets: acoin, Price: pcoin, SellerSettlementFlatFee: &fcoin, + ExternalId: "eeeeiiiiiddddd", })), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderFilled", Attributes: []abci.EventAttribute{ {Key: []byte("assets"), Value: acoinQ}, + {Key: []byte("external_id"), Value: quoteBz("eeeeiiiiiddddd")}, {Key: []byte("fees"), Value: fcoinQ}, {Key: []byte("order_id"), Value: quoteBz("4")}, {Key: []byte("price"), Value: pcoinQ}, @@ -507,11 +671,13 @@ func TestTypedEventToEvent(t *testing.T) { Assets: acoin, Price: pcoin, BuyerSettlementFees: sdk.Coins{fcoin}, + ExternalId: "that one thing", })), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderFilled", Attributes: []abci.EventAttribute{ {Key: []byte("assets"), Value: acoinQ}, + {Key: []byte("external_id"), Value: quoteBz("that one thing")}, {Key: []byte("fees"), Value: fcoinQ}, {Key: []byte("order_id"), Value: quoteBz("104")}, {Key: []byte("price"), Value: pcoinQ}, @@ -524,11 +690,13 @@ func TestTypedEventToEvent(t *testing.T) { Assets: acoin, Price: pcoin, SellerSettlementFlatFee: &fcoin, + ExternalId: "12345", })), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderPartiallyFilled", Attributes: []abci.EventAttribute{ {Key: []byte("assets"), Value: acoinQ}, + {Key: []byte("external_id"), Value: quoteBz("12345")}, {Key: []byte("fees"), Value: fcoinQ}, {Key: []byte("order_id"), Value: quoteBz("5")}, {Key: []byte("price"), Value: pcoinQ}, @@ -541,17 +709,41 @@ func TestTypedEventToEvent(t *testing.T) { Assets: acoin, Price: pcoin, BuyerSettlementFees: sdk.Coins{fcoin}, + ExternalId: "67890", })), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderPartiallyFilled", Attributes: []abci.EventAttribute{ {Key: []byte("assets"), Value: acoinQ}, + {Key: []byte("external_id"), Value: quoteBz("67890")}, {Key: []byte("fees"), Value: fcoinQ}, {Key: []byte("order_id"), Value: quoteBz("5")}, {Key: []byte("price"), Value: pcoinQ}, }, }, }, + { + name: "EventOrderExternalIDUpdated ask", + tev: NewEventOrderExternalIDUpdated(NewOrder(8).WithAsk(&AskOrder{ExternalId: "yellow"})), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventOrderExternalIDUpdated", + Attributes: []abci.EventAttribute{ + {Key: []byte("external_id"), Value: quoteBz("yellow")}, + {Key: []byte("order_id"), Value: quoteBz("8")}, + }, + }, + }, + { + name: "EventOrderExternalIDUpdated bid", + tev: NewEventOrderExternalIDUpdated(NewOrder(8).WithBid(&BidOrder{ExternalId: "yellow"})), + expEvent: sdk.Event{ + Type: "provenance.exchange.v1.EventOrderExternalIDUpdated", + Attributes: []abci.EventAttribute{ + {Key: []byte("external_id"), Value: quoteBz("yellow")}, + {Key: []byte("order_id"), Value: quoteBz("8")}, + }, + }, + }, { name: "EventMarketWithdraw", tev: NewEventMarketWithdraw(6, coins1, destination, withdrawnBy), diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index c1d2b1181f..eb4c69cfdd 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -280,6 +280,11 @@ func (f orderFulfillment) PartialFillAllowed() bool { return f.Order.PartialFillAllowed() } +// GetExternalID gets this fulfillment's external id. +func (f orderFulfillment) GetExternalID() string { + return f.Order.GetExternalID() +} + // GetOrderType gets this fulfillment's order's type string. func (f orderFulfillment) GetOrderType() string { return f.Order.GetOrderType() diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 1b8e007781..cb4b94ba4b 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -166,11 +166,6 @@ func transferString(t *Transfer) string { return fmt.Sprintf("T{Inputs:%s, Outputs: %s}", inputs, outputs) } -// transfersStringsLines creates a string for each transfer. -func transfersStringsLines(ts []*Transfer) []string { - return stringerLines(ts, transferString) -} - // String converts a indexedAddrAmtsString to a string. // This is mostly because test failure output of sdk.Coin and sdk.Coins is impossible to understand. func indexedAddrAmtsString(i *indexedAddrAmts) string { @@ -2520,6 +2515,43 @@ func TestOrderFulfillment_PartialFillAllowed(t *testing.T) { } } +func TestOrderFulfillment_GetExternalID(t *testing.T) { + askOrder := func(externalID string) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithAsk(&AskOrder{ExternalId: externalID}), + } + } + bidOrder := func(externalID string) orderFulfillment { + return orderFulfillment{ + Order: NewOrder(999).WithBid(&BidOrder{ExternalId: externalID}), + } + } + + tests := []struct { + name string + f orderFulfillment + exp string + }{ + {name: "ask empty", f: askOrder(""), exp: ""}, + {name: "ask something", f: askOrder("something"), exp: "something"}, + {name: "ask uuid", f: askOrder("5E50E61A-43E7-4C35-86BF-04040B49C1CB"), exp: "5E50E61A-43E7-4C35-86BF-04040B49C1CB"}, + {name: "bid empty", f: bidOrder(""), exp: ""}, + {name: "bid something", f: bidOrder("SOMETHING"), exp: "SOMETHING"}, + {name: "bid uuid", f: bidOrder("1AD88BB9-86FA-42F1-A824-66AB27547904"), exp: "1AD88BB9-86FA-42F1-A824-66AB27547904"}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.f.GetExternalID() + } + require.NotPanics(t, testFunc, "GetExternalID()") + assert.Equal(t, tc.exp, actual, "GetExternalID() result") + }) + } +} + func TestOrderFulfillment_GetOrderType(t *testing.T) { tests := []struct { name string diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 7d1ba69434..a7177b9e81 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -88,6 +88,25 @@ func (k QueryServer) QueryGetOrder(goCtx context.Context, req *exchange.QueryGet return &exchange.QueryGetOrderResponse{Order: order}, nil } +// QueryGetOrderByExternalID looks up an order by market id and external id. +func (k QueryServer) QueryGetOrderByExternalID(goCtx context.Context, req *exchange.QueryGetOrderByExternalIDRequest) (*exchange.QueryGetOrderByExternalIDResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + order, err := k.GetOrderByExternalID(ctx, req.MarketId, req.ExternalId) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + if order == nil { + return nil, status.Errorf(codes.InvalidArgument, "order not found in market %d with external id %q", + req.MarketId, req.ExternalId) + } + + return &exchange.QueryGetOrderByExternalIDResponse{Order: order}, nil +} + // QueryGetMarketOrders looks up the orders in a market. func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.QueryGetMarketOrdersRequest) (*exchange.QueryGetMarketOrdersResponse, error) { if req == nil || req.MarketId == 0 { diff --git a/x/exchange/keeper/grpc_query_test.go b/x/exchange/keeper/grpc_query_test.go index 8c345f04b9..c78e483e08 100644 --- a/x/exchange/keeper/grpc_query_test.go +++ b/x/exchange/keeper/grpc_query_test.go @@ -1,11 +1,11 @@ package keeper_test -// TODO[1658]: func (s *TestSuite) TestNewQueryServer() - // TODO[1658]: func (s *TestSuite) TestQueryServer_QueryOrderFeeCalc() // TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetOrder() +// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetOrderByExternalID() + // TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetMarketOrders() // TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetOwnerOrders() diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 29760a938b..2af00d2028 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -68,6 +68,9 @@ import ( // // An asset type to order index is maintained with the following format: // 0x05 | | (8 bytes) => +// +// A market external id to order index is maintained with the following format: +// 0x09 | (4 bytes) | => (8 bytes) const ( // KeyTypeParams is the type byte for params entries. @@ -88,6 +91,8 @@ const ( KeyTypeAddressToOrderIndex = byte(0x04) // KeyTypeAssetToOrderIndex is the type byte for entries in the asset to order index. KeyTypeAssetToOrderIndex = byte(0x05) + // KeyTypeMarketExternalIDToOrderIndex is the type byte for entries in the market and uuid to order index. + KeyTypeMarketExternalIDToOrderIndex = byte(0x09) // MarketKeyTypeCreateAskFlat is the market-specific type byte for the create-ask flat fees. MarketKeyTypeCreateAskFlat = byte(0x00) @@ -724,3 +729,16 @@ func ParseIndexKeyAssetToOrder(key []byte) (string, uint64, error) { } return denom, orderID, nil } + +// MakeIndexKeyMarketExternalIDToOrder creates the key to use for the market and uuid to order index for the provided values. +func MakeIndexKeyMarketExternalIDToOrder(marketID uint32, externalID string) []byte { + if len(externalID) == 0 { + panic(errors.New("cannot create market external id to order index with empty external id")) + } + if err := exchange.ValidateExternalID(externalID); err != nil { + panic(fmt.Errorf("cannot create market external id to order index: %w", err)) + } + rv := prepKey(KeyTypeMarketExternalIDToOrderIndex, uint32Bz(marketID), len(externalID)) + rv = append(rv, externalID...) + return rv +} diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index 51b5bc13f3..b329a423ab 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -2,6 +2,7 @@ package keeper_test import ( "bytes" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -96,6 +97,7 @@ func TestKeyTypeUniqueness(t *testing.T) { {name: "KeyTypeMarketToOrderIndex", value: keeper.KeyTypeMarketToOrderIndex}, {name: "KeyTypeAddressToOrderIndex", value: keeper.KeyTypeAddressToOrderIndex}, {name: "KeyTypeAssetToOrderIndex", value: keeper.KeyTypeAssetToOrderIndex}, + {name: "KeyTypeMarketExternalIDToOrderIndex", value: keeper.KeyTypeMarketExternalIDToOrderIndex}, }, }, { @@ -3728,3 +3730,68 @@ func TestParseIndexKeyAssetToOrder(t *testing.T) { }) } } + +func TestMakeIndexKeyMarketExternalIDToOrder(t *testing.T) { + tests := []struct { + name string + marketID uint32 + externalID string + expected []byte + expPanic string + }{ + { + name: "empty external id", + marketID: 1, + externalID: "", + expPanic: "cannot create market external id to order index with empty external id", + }, + { + name: "41 char external id", + marketID: 2, + externalID: "This id has forty-one chars. That's long!", + expPanic: "cannot create market external id to order index: invalid external id " + + "\"This id has forty-one chars. That's long!\": max length 40", + }, + { + name: "market 0, a zeroed uuid", + marketID: 0, + externalID: "00000000-0000-0000-0000-000000000000", + expected: append([]byte{keeper.KeyTypeMarketExternalIDToOrderIndex, 0, 0, 0, 0}, + "00000000-0000-0000-0000-000000000000"...), + }, + { + name: "market 1, random uuid", + marketID: 1, + externalID: "814348F5-DE62-4954-81D1-65874E37C0BE", + expected: append([]byte{keeper.KeyTypeMarketExternalIDToOrderIndex, 0, 0, 0, 1}, + "814348F5-DE62-4954-81D1-65874E37C0BE"...), + }, + { + name: "market 67,305,985, a wierd external id", + marketID: 67_305_985, + externalID: "ThisIsWierd", + expected: append([]byte{keeper.KeyTypeMarketExternalIDToOrderIndex, 4, 3, 2, 1}, "ThisIsWierd"...), + }, + { + name: "max market id and lots of Zs", + marketID: 4_294_967_295, + externalID: strings.Repeat("Z", 40), + expected: append([]byte{keeper.KeyTypeMarketExternalIDToOrderIndex, 255, 255, 255, 255}, + strings.Repeat("Z", 40)...), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ktc := keyTestCase{ + maker: func() []byte { + return keeper.MakeIndexKeyMarketExternalIDToOrder(tc.marketID, tc.externalID) + }, + expected: tc.expected, + expPanic: tc.expPanic, + } + + checkKey(t, ktc, "MakeIndexKeyMarketExternalIDToOrder(%d, %v)", tc.marketID, tc.externalID) + }) + } +} diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 5aad8a9c08..16459ac13e 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -833,6 +833,12 @@ func (k Keeper) CanSettleOrders(ctx sdk.Context, marketID uint32, admin string) return k.HasPermission(ctx, marketID, admin, exchange.Permission_settle) } +// CanSetIDs returns true if the provided admin bech32 address has permission to +// set UUIDs on orders for a market. Also returns true if the provided address is the authority address. +func (k Keeper) CanSetIDs(ctx sdk.Context, marketID uint32, admin string) bool { + return k.HasPermission(ctx, marketID, admin, exchange.Permission_set_ids) +} + // CanCancelOrdersForMarket returns true if the provided admin bech32 address has permission to // cancel orders for a market. Also returns true if the provided address is the authority address. func (k Keeper) CanCancelOrdersForMarket(ctx sdk.Context, marketID uint32, admin string) bool { diff --git a/x/exchange/keeper/market_test.go b/x/exchange/keeper/market_test.go index b50542ba0e..c71d155bf3 100644 --- a/x/exchange/keeper/market_test.go +++ b/x/exchange/keeper/market_test.go @@ -42,6 +42,8 @@ package keeper_test // TODO[1658]: func (s *TestSuite) TestKeeper_CanSettleOrders() +// TODO[1658]: func (s *TestSuite) TestKeeper_CanSetIDs() + // TODO[1658]: func (s *TestSuite) TestKeeper_CanCancelOrdersForMarket() // TODO[1658]: func (s *TestSuite) TestKeeper_CanWithdrawMarketFunds() diff --git a/x/exchange/keeper/msg_server.go b/x/exchange/keeper/msg_server.go index 50ac8b9096..16c5756736 100644 --- a/x/exchange/keeper/msg_server.go +++ b/x/exchange/keeper/msg_server.go @@ -90,6 +90,19 @@ func (k MsgServer) MarketSettle(goCtx context.Context, msg *exchange.MsgMarketSe return &exchange.MsgMarketSettleResponse{}, nil } +// MarketSetOrderExternalID updates an order's external id field. +func (k MsgServer) MarketSetOrderExternalID(goCtx context.Context, msg *exchange.MsgMarketSetOrderExternalIDRequest) (*exchange.MsgMarketSetOrderExternalIDResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + if !k.CanSetIDs(ctx, msg.MarketId, msg.Admin) { + return nil, permError("set uuids on orders for", msg.Admin, msg.MarketId) + } + err := k.SetOrderExternalID(ctx, msg.MarketId, msg.OrderId, msg.ExternalId) + if err != nil { + return nil, sdkerrors.ErrInvalidRequest.Wrap(err.Error()) + } + return &exchange.MsgMarketSetOrderExternalIDResponse{}, nil +} + // MarketWithdraw is a market endpoint to withdraw fees that have been collected. func (k MsgServer) MarketWithdraw(goCtx context.Context, msg *exchange.MsgMarketWithdrawRequest) (*exchange.MsgMarketWithdrawResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) diff --git a/x/exchange/keeper/msg_server_test.go b/x/exchange/keeper/msg_server_test.go index 77e9b49ce0..73ed058097 100644 --- a/x/exchange/keeper/msg_server_test.go +++ b/x/exchange/keeper/msg_server_test.go @@ -14,6 +14,8 @@ package keeper_test // TODO[1658]: func (s *TestSuite) TestMsgServer_MarketSettle() +// TODO[1658]: func (s *TestSuite) TestMsgServer_MarketSetOrderExternalID() + // TODO[1658]: func (s *TestSuite) TestMsgServer_MarketWithdraw() // TODO[1658]: func (s *TestSuite) TestMsgServer_MarketUpdateDetails() diff --git a/x/exchange/keeper/orders.go b/x/exchange/keeper/orders.go index a85ed3b7e6..5f20e1a2df 100644 --- a/x/exchange/keeper/orders.go +++ b/x/exchange/keeper/orders.go @@ -112,8 +112,9 @@ func (k Keeper) parseOrderStoreKeyValue(key, value []byte) (*exchange.Order, err return k.parseOrderStoreValue(orderID, value) } -// createIndexEntries creates all the key/value index entries for an order. -func createIndexEntries(order exchange.Order) []sdk.KVPair { +// createConstantIndexEntries creates all the key/value index entries for an order that don't change. +// See also: createMarketExternalIDToOrderEntry. +func createConstantIndexEntries(order exchange.Order) []sdk.KVPair { marketID := order.GetMarketID() orderID := order.GetOrderID() orderTypeByte := order.GetOrderTypeByte() @@ -137,6 +138,19 @@ func createIndexEntries(order exchange.Order) []sdk.KVPair { } } +// createMarketExternalIDToOrderEntry creates the market external id to order store entry. +// See also createConstantIndexEntries +func createMarketExternalIDToOrderEntry(order exchange.OrderI) *sdk.KVPair { + externalID := order.GetExternalID() + if len(externalID) == 0 { + return nil + } + return &sdk.KVPair{ + Key: MakeIndexKeyMarketExternalIDToOrder(order.GetMarketID(), externalID), + Value: uint64Bz(order.GetOrderID()), + } +} + // getOrderFromStore looks up an order from the store. Returns nil, nil if the order does not exist. func (k Keeper) getOrderFromStore(store sdk.KVStore, orderID uint64) (*exchange.Order, error) { key := MakeKeyOrder(orderID) @@ -157,17 +171,29 @@ func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { if err != nil { return fmt.Errorf("failed to create order %d store key/value: %w", order.GetOrderID(), err) } + + externalIDEntry := createMarketExternalIDToOrderEntry(order) + if externalIDEntry != nil && store.Has(externalIDEntry.Key) { + orderIDBz := store.Get(externalIDEntry.Value) + otherOrderID, ok := uint64FromBz(orderIDBz) + if ok && otherOrderID != order.GetOrderID() { + return fmt.Errorf("external id %q is already in use by order %d: cannot be used for order %d", + order.GetExternalID(), otherOrderID, order.GetOrderID()) + } + } + isUpdate := store.Has(key) store.Set(key, value) + if !isUpdate { - indexEntries := createIndexEntries(order) + indexEntries := createConstantIndexEntries(order) for _, entry := range indexEntries { store.Set(entry.Key, entry.Value) } - // It is assumed that these index entries cannot change over the life of an order. - // The only change that is allowed to an order is the assets (due to partial fulfillment). - // Partial fulfillment is only allowed when there's a single asset type (denom), though. - // That's why no attempt is made in here to update index entries when the order already exists. + } + + if externalIDEntry != nil { + store.Set(externalIDEntry.Key, externalIDEntry.Value) } return nil } @@ -176,10 +202,14 @@ func (k Keeper) setOrderInStore(store sdk.KVStore, order exchange.Order) error { func deleteAndDeIndexOrder(store sdk.KVStore, order exchange.Order) { key := MakeKeyOrder(order.OrderId) store.Delete(key) - indexEntries := createIndexEntries(order) + indexEntries := createConstantIndexEntries(order) for _, entry := range indexEntries { store.Delete(entry.Key) } + externalIDEntry := createMarketExternalIDToOrderEntry(order) + if externalIDEntry != nil { + store.Delete(externalIDEntry.Key) + } } // iterateOrderIndex iterates over a -to-order index with keys that have the provided prefixBz. @@ -562,6 +592,26 @@ func (k Keeper) GetOrder(ctx sdk.Context, orderID uint64) (*exchange.Order, erro return k.getOrderFromStore(k.getStore(ctx), orderID) } +// GetOrderByExternalID gets an order by its market id and UUID. +func (k Keeper) GetOrderByExternalID(ctx sdk.Context, marketID uint32, externalID string) (*exchange.Order, error) { + if marketID == 0 { + return nil, errors.New("invalid market id: cannot be zero") + } + if len(externalID) == 0 || len(externalID) > exchange.MaxExternalIDLength { + return nil, nil + } + + store := k.getStore(ctx) + key := MakeIndexKeyMarketExternalIDToOrder(marketID, externalID) + orderIDBz := store.Get(key) + orderID, ok := uint64FromBz(orderIDBz) + if !ok { + return nil, nil + } + + return k.getOrderFromStore(store, orderID) +} + // CreateAskOrder creates an ask order, collects the creation fee, and places all needed holds. func (k Keeper) CreateAskOrder(ctx sdk.Context, askOrder exchange.AskOrder, creationFee *sdk.Coin) (uint64, error) { if err := askOrder.Validate(); err != nil { @@ -671,8 +721,56 @@ func (k Keeper) CancelOrder(ctx sdk.Context, orderID uint64, signer string) erro } deleteAndDeIndexOrder(k.getStore(ctx), *order) - k.emitEvent(ctx, exchange.NewEventOrderCancelled(orderID, signerAddr)) + k.emitEvent(ctx, exchange.NewEventOrderCancelled(order, signerAddr)) + + return nil +} + +// SetOrderExternalID updates an order's external id. +func (k Keeper) SetOrderExternalID(ctx sdk.Context, marketID uint32, orderID uint64, newExternalID string) error { + if err := exchange.ValidateExternalID(newExternalID); err != nil { + return err + } + + store := k.getStore(ctx) + order, err := k.getOrderFromStore(store, orderID) + if err != nil { + return err + } + if order == nil { + return fmt.Errorf("order %d not found", orderID) + } + + orderMarketID := order.GetMarketID() + if marketID != orderMarketID { + return fmt.Errorf("order %d has market id %d, expected %d", orderID, orderMarketID, marketID) + } + + orderExternalID := order.GetExternalID() + if orderExternalID == newExternalID { + return fmt.Errorf("order %d already has external id %q", orderID, orderExternalID) + } + + switch { + case order.IsAskOrder(): + order.GetAskOrder().ExternalId = newExternalID + case order.IsBidOrder(): + order.GetBidOrder().ExternalId = newExternalID + default: + return fmt.Errorf("order %d has unexpected type %s", orderID, order.GetOrderType()) + } + + if len(orderExternalID) > 0 { + oldIDIndex := MakeIndexKeyMarketExternalIDToOrder(orderMarketID, orderExternalID) + store.Delete(oldIDIndex) + } + + err = k.setOrderInStore(store, *order) + if err != nil { + return err + } + k.emitEvent(ctx, exchange.NewEventOrderExternalIDUpdated(order)) return nil } diff --git a/x/exchange/keeper/orders_test.go b/x/exchange/keeper/orders_test.go index f254497902..9e07d297e8 100644 --- a/x/exchange/keeper/orders_test.go +++ b/x/exchange/keeper/orders_test.go @@ -2,12 +2,16 @@ package keeper_test // TODO[1658]: func (s *TestSuite) TestKeeper_GetOrder() +// TODO[1658]: func (s *TestSuite) TestKeeper_GetOrderByExternalID() + // TODO[1658]: func (s *TestSuite) TestKeeper_CreateAskOrder() // TODO[1658]: func (s *TestSuite) TestKeeper_CreateBidOrder() // TODO[1658]: func (s *TestSuite) TestKeeper_CancelOrder() +// TODO[1658]: func (s *TestSuite) TestKeeper_SetOrderExternalID() + // TODO[1658]: func (s *TestSuite) TestKeeper_IterateOrders() // TODO[1658]: func (s *TestSuite) TestKeeper_IterateMarketOrders() diff --git a/x/exchange/market.go b/x/exchange/market.go index ff9cd863bf..5e9a5e245d 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -506,16 +506,15 @@ func AllPermissions() []Permission { // Example inputs: "settle", "CanCel", "WITHDRAW", "permission_update", "PermISSion_PErmissioNs", "PERMISSION_ATTRIBUTES" func ParsePermission(permission string) (Permission, error) { permUC := strings.ToUpper(strings.TrimSpace(permission)) - val, found := Permission_value["PERMISSION_"+permUC] - if found { - if val != 0 { - return Permission(val), nil - } - } else { - val, found = Permission_value[permUC] - if found && val != 0 { - return Permission(val), nil - } + if !strings.HasPrefix(permUC, "PERMISSION_") { + permUC = "PERMISSION_" + permUC + } + if val, found := Permission_value[permUC]; found && val != int32(Permission_unspecified) { + return Permission(val), nil + } + // special case to allow the underscore to be optional in "set_ids". + if permUC == "PERMISSION_SETIDS" { + return Permission_set_ids, nil } return Permission_unspecified, fmt.Errorf("invalid permission: %q", permission) } diff --git a/x/exchange/market.pb.go b/x/exchange/market.pb.go index 2121da6eb0..1fbb6cf1d8 100644 --- a/x/exchange/market.pb.go +++ b/x/exchange/market.pb.go @@ -34,36 +34,40 @@ const ( Permission_unspecified Permission = 0 // PERMISSION_SETTLE is the ability to use the Settle Tx endpoint on behalf of a market. Permission_settle Permission = 1 + // PERMISSION_SET_IDS is the ability to use the SetOrderExternalID Tx endpoint on behalf of a market. + Permission_set_ids Permission = 2 // PERMISSION_CANCEL is the ability to use the Cancel Tx endpoint on behalf of a market. - Permission_cancel Permission = 2 + Permission_cancel Permission = 3 // PERMISSION_WITHDRAW is the ability to use the MarketWithdraw Tx endpoint. - Permission_withdraw Permission = 3 + Permission_withdraw Permission = 4 // PERMISSION_UPDATE is the ability to use the MarketUpdate* Tx endpoints. - Permission_update Permission = 4 + Permission_update Permission = 5 // PERMISSION_PERMISSIONS is the ability to use the MarketManagePermissions Tx endpoint. - Permission_permissions Permission = 5 + Permission_permissions Permission = 6 // PERMISSION_ATTRIBUTES is the ability to use the MarketManageReqAttrs Tx endpoint. - Permission_attributes Permission = 6 + Permission_attributes Permission = 7 ) var Permission_name = map[int32]string{ 0: "PERMISSION_UNSPECIFIED", 1: "PERMISSION_SETTLE", - 2: "PERMISSION_CANCEL", - 3: "PERMISSION_WITHDRAW", - 4: "PERMISSION_UPDATE", - 5: "PERMISSION_PERMISSIONS", - 6: "PERMISSION_ATTRIBUTES", + 2: "PERMISSION_SET_IDS", + 3: "PERMISSION_CANCEL", + 4: "PERMISSION_WITHDRAW", + 5: "PERMISSION_UPDATE", + 6: "PERMISSION_PERMISSIONS", + 7: "PERMISSION_ATTRIBUTES", } var Permission_value = map[string]int32{ "PERMISSION_UNSPECIFIED": 0, "PERMISSION_SETTLE": 1, - "PERMISSION_CANCEL": 2, - "PERMISSION_WITHDRAW": 3, - "PERMISSION_UPDATE": 4, - "PERMISSION_PERMISSIONS": 5, - "PERMISSION_ATTRIBUTES": 6, + "PERMISSION_SET_IDS": 2, + "PERMISSION_CANCEL": 3, + "PERMISSION_WITHDRAW": 4, + "PERMISSION_UPDATE": 5, + "PERMISSION_PERMISSIONS": 6, + "PERMISSION_ATTRIBUTES": 7, } func (x Permission) String() string { @@ -555,70 +559,71 @@ func init() { } var fileDescriptor_d5cf198f1dd7e167 = []byte{ - // 1001 bytes of a gzipped FileDescriptorProto + // 1022 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x41, 0x6f, 0x1a, 0x47, - 0x14, 0x66, 0x0d, 0xb6, 0x61, 0xb0, 0x1d, 0x3a, 0x4e, 0xdc, 0x35, 0xa9, 0x60, 0x4b, 0x14, 0x89, - 0xb4, 0x32, 0xc8, 0xae, 0x7a, 0xf1, 0xa5, 0x02, 0x1b, 0xb7, 0x48, 0x89, 0x83, 0x16, 0x50, 0xa4, - 0xa8, 0xd2, 0x6a, 0xd8, 0x7d, 0xe0, 0x91, 0x97, 0x5d, 0x32, 0x33, 0xd8, 0x69, 0xff, 0x40, 0x2b, - 0x9f, 0x7a, 0xec, 0xc5, 0x92, 0xfb, 0x0f, 0x7a, 0xe8, 0xbd, 0xd7, 0x1c, 0xad, 0x4a, 0x95, 0x7a, - 0x8a, 0x2a, 0xfb, 0xd2, 0x9f, 0x51, 0xed, 0xcc, 0xb2, 0x6c, 0x08, 0xae, 0x13, 0x55, 0xb9, 0xcd, - 0x7b, 0xdf, 0x37, 0xdf, 0xfb, 0xde, 0xe3, 0x31, 0x5a, 0xf4, 0x60, 0xc4, 0xfc, 0x13, 0xf0, 0x88, - 0x67, 0x43, 0x15, 0x5e, 0xda, 0x47, 0xc4, 0x1b, 0x40, 0xf5, 0x64, 0xbb, 0x3a, 0x24, 0xec, 0x18, - 0x44, 0x65, 0xc4, 0x7c, 0xe1, 0xe3, 0x8d, 0x29, 0xa9, 0x32, 0x21, 0x55, 0x4e, 0xb6, 0xf3, 0x05, - 0xdb, 0xe7, 0x43, 0x9f, 0x57, 0xc9, 0x58, 0x1c, 0x55, 0x4f, 0xb6, 0x7b, 0x20, 0xc8, 0xb6, 0x0c, - 0xd4, 0xbd, 0x08, 0xef, 0x11, 0x0e, 0x11, 0x6e, 0xfb, 0xd4, 0x0b, 0xf1, 0x4d, 0x85, 0x5b, 0x32, - 0xaa, 0xaa, 0x20, 0x84, 0xee, 0x0e, 0xfc, 0x81, 0xaf, 0xf2, 0xc1, 0x49, 0x65, 0x4b, 0x7f, 0x6a, - 0x68, 0xf5, 0x89, 0x74, 0x56, 0xb3, 0x6d, 0x7f, 0xec, 0x09, 0xdc, 0x44, 0x2b, 0x81, 0xba, 0x45, - 0x54, 0xac, 0x6b, 0x86, 0x56, 0xce, 0xee, 0x18, 0x95, 0x50, 0x4c, 0x9a, 0x09, 0x2b, 0x57, 0xea, - 0x84, 0x43, 0x78, 0xaf, 0x9e, 0xba, 0x7c, 0x5d, 0xd4, 0xcc, 0x6c, 0x6f, 0x9a, 0xc2, 0xf7, 0x51, - 0x46, 0x75, 0x6d, 0x51, 0x47, 0x5f, 0x30, 0xb4, 0xf2, 0xaa, 0x99, 0x56, 0x89, 0xa6, 0x83, 0x4d, - 0xb4, 0x16, 0x82, 0x0e, 0x08, 0x42, 0x5d, 0xae, 0x27, 0x65, 0xa5, 0x87, 0x95, 0xf9, 0xb3, 0xa9, - 0x28, 0x9b, 0xfb, 0x8a, 0x5c, 0x4f, 0xbd, 0x7a, 0x5d, 0x4c, 0x98, 0xab, 0xc3, 0x78, 0x72, 0x37, - 0xfd, 0xe3, 0x45, 0x31, 0xf1, 0xf3, 0x45, 0x31, 0x51, 0xfa, 0x21, 0xea, 0x2b, 0xc4, 0x30, 0x46, - 0x29, 0x8f, 0x0c, 0x41, 0xf6, 0x93, 0x31, 0xe5, 0x19, 0x1b, 0x28, 0xeb, 0x00, 0xb7, 0x19, 0x1d, - 0x09, 0xea, 0x7b, 0xd2, 0x62, 0xc6, 0x8c, 0xa7, 0x70, 0x11, 0x65, 0x4f, 0xa1, 0xc7, 0xa9, 0x00, - 0x6b, 0xcc, 0x5c, 0x69, 0x31, 0x63, 0xa2, 0x30, 0xd5, 0x65, 0x2e, 0xde, 0x44, 0x69, 0x6a, 0xfb, - 0x9e, 0x35, 0x66, 0x54, 0x4f, 0x49, 0x74, 0x39, 0x88, 0xbb, 0x8c, 0xee, 0xa6, 0xfe, 0xb9, 0x28, - 0x6a, 0xa5, 0xdf, 0x35, 0x94, 0x55, 0x4e, 0xea, 0x8c, 0x42, 0xff, 0xcd, 0xa1, 0x68, 0x33, 0x43, - 0xf9, 0x2a, 0x1a, 0x0a, 0x71, 0x1c, 0x06, 0x9c, 0x2b, 0x4f, 0x75, 0xfd, 0x8f, 0xdf, 0xb6, 0xee, - 0x86, 0xbf, 0x40, 0x4d, 0x21, 0x6d, 0xc1, 0xa8, 0x37, 0x98, 0x4c, 0x20, 0x4c, 0x7e, 0x88, 0xa9, - 0x96, 0x7e, 0x5d, 0x46, 0x4b, 0x8a, 0xf6, 0xdf, 0xe6, 0xdf, 0xae, 0xbd, 0xf0, 0x7f, 0x6b, 0xe3, - 0x43, 0xb4, 0xde, 0x07, 0xb0, 0x6c, 0x06, 0x44, 0x80, 0x45, 0xf8, 0xb1, 0xd5, 0x77, 0x89, 0xd0, - 0x93, 0x46, 0xb2, 0x9c, 0xdd, 0xd9, 0x9c, 0x2c, 0x65, 0xb0, 0x74, 0xd1, 0x52, 0xee, 0xf9, 0xd4, - 0x0b, 0xc5, 0x72, 0x7d, 0x80, 0x3d, 0x79, 0xb5, 0xc6, 0x8f, 0x0f, 0x5c, 0x22, 0x66, 0xf4, 0x7a, - 0xd4, 0x51, 0x7a, 0xa9, 0xf7, 0xd5, 0xab, 0x53, 0x47, 0xea, 0x7d, 0x8b, 0xf2, 0x81, 0x1e, 0x07, - 0xd7, 0x05, 0x66, 0x71, 0x10, 0xc2, 0x85, 0x21, 0x78, 0x42, 0xc9, 0x2e, 0xbe, 0x9b, 0xec, 0xc7, - 0x7d, 0x80, 0xb6, 0x54, 0x68, 0x47, 0x02, 0x52, 0x7d, 0x80, 0x3e, 0x99, 0xaf, 0xce, 0x88, 0xa0, - 0x3e, 0xd7, 0x97, 0xa4, 0xbe, 0x71, 0xd3, 0x7c, 0x0f, 0x00, 0xcc, 0x80, 0x18, 0x96, 0xd9, 0x9c, - 0x53, 0x46, 0xe2, 0x1c, 0x3f, 0x47, 0x01, 0x68, 0xf5, 0xc6, 0xdf, 0xcd, 0xe9, 0x62, 0xf9, 0xdd, - 0xba, 0xd8, 0xe8, 0x03, 0xd4, 0x03, 0x81, 0x99, 0x26, 0x00, 0xdd, 0x9f, 0xab, 0x1d, 0xf6, 0x90, - 0x7e, 0xaf, 0x1e, 0xf4, 0xb7, 0x8b, 0x84, 0x2d, 0x3c, 0x42, 0x39, 0x62, 0xdb, 0x30, 0x12, 0xd4, - 0x1b, 0x58, 0x3e, 0x73, 0x80, 0x71, 0x3d, 0x63, 0x68, 0xe5, 0xb4, 0x79, 0x27, 0xca, 0x3f, 0x95, - 0x69, 0xbc, 0x83, 0xee, 0x11, 0xd7, 0xf5, 0x4f, 0xad, 0x31, 0x7f, 0xc3, 0x92, 0x8e, 0x24, 0x7f, - 0x5d, 0x82, 0x5d, 0x1e, 0x2f, 0x82, 0x0f, 0xd1, 0x6a, 0x20, 0xc3, 0xb9, 0x35, 0x60, 0xc4, 0x13, - 0x5c, 0xcf, 0x4a, 0xdf, 0x0f, 0x6e, 0xf2, 0x5d, 0x93, 0xe4, 0xaf, 0x03, 0x6e, 0x68, 0x7d, 0x85, - 0x4c, 0x53, 0x1c, 0x6f, 0xa1, 0x75, 0x06, 0x2f, 0x2c, 0x22, 0x04, 0x8b, 0x6d, 0xb7, 0xbe, 0x62, - 0x24, 0xcb, 0x19, 0x33, 0xc7, 0xe0, 0x45, 0x4d, 0x08, 0x16, 0xed, 0xee, 0x3c, 0x7a, 0x8f, 0x3a, - 0xfa, 0xea, 0x1c, 0x7a, 0x9d, 0x3a, 0xa5, 0xef, 0x51, 0x7a, 0x32, 0x38, 0xfc, 0x25, 0x5a, 0x1c, - 0x31, 0x6a, 0x43, 0xf8, 0x92, 0xdf, 0xfa, 0x3b, 0x2a, 0x36, 0xde, 0x46, 0xc9, 0x3e, 0x40, 0xf8, - 0x17, 0xbe, 0xf5, 0x52, 0xc0, 0xdd, 0x4d, 0x4d, 0x9e, 0xde, 0x6c, 0xac, 0x7b, 0xbc, 0x83, 0x96, - 0x27, 0x8f, 0x99, 0x76, 0xcb, 0x63, 0x36, 0x21, 0xe2, 0x7d, 0x94, 0x1d, 0x01, 0x1b, 0x52, 0xce, - 0xa9, 0xef, 0x05, 0xef, 0x48, 0xb2, 0xbc, 0xb6, 0x53, 0xba, 0x69, 0xd6, 0xad, 0x88, 0x6a, 0xc6, - 0xaf, 0x7d, 0xf6, 0xcb, 0x02, 0x42, 0x53, 0x0c, 0x7f, 0x8e, 0x36, 0x5a, 0x0d, 0xf3, 0x49, 0xb3, - 0xdd, 0x6e, 0x3e, 0x3d, 0xb4, 0xba, 0x87, 0xed, 0x56, 0x63, 0xaf, 0x79, 0xd0, 0x6c, 0xec, 0xe7, - 0x12, 0xf9, 0x3b, 0x67, 0xe7, 0x46, 0x76, 0xec, 0xf1, 0x11, 0xd8, 0xb4, 0x4f, 0xc1, 0xc1, 0x9f, - 0xa2, 0x8f, 0x62, 0xe4, 0x76, 0xa3, 0xd3, 0x79, 0xdc, 0xc8, 0x69, 0x79, 0x74, 0x76, 0x6e, 0x2c, - 0xa9, 0x8d, 0x99, 0xa1, 0xec, 0xd5, 0x0e, 0xf7, 0x1a, 0x8f, 0x73, 0x0b, 0x8a, 0x62, 0x07, 0x26, - 0x5d, 0xfc, 0x10, 0xad, 0xc7, 0x28, 0xcf, 0x9a, 0x9d, 0x6f, 0xf6, 0xcd, 0xda, 0xb3, 0x5c, 0x32, - 0xbf, 0x72, 0x76, 0x6e, 0xa4, 0x4f, 0xa9, 0x38, 0x72, 0x18, 0x39, 0x9d, 0x51, 0xea, 0xb6, 0xf6, - 0x6b, 0x9d, 0x46, 0x2e, 0xa5, 0x94, 0xc6, 0x23, 0x87, 0x08, 0x98, 0x31, 0x3f, 0x3d, 0xb6, 0x73, - 0x8b, 0xca, 0x7c, 0xac, 0x71, 0xfc, 0x08, 0xdd, 0x8b, 0x91, 0x6b, 0x9d, 0x8e, 0xd9, 0xac, 0x77, - 0x3b, 0x8d, 0x76, 0x6e, 0x29, 0xbf, 0x76, 0x76, 0x6e, 0xa0, 0x60, 0x8d, 0x68, 0x6f, 0x2c, 0x80, - 0xd7, 0xe1, 0xd5, 0x55, 0x41, 0xbb, 0xbc, 0x2a, 0x68, 0x7f, 0x5f, 0x15, 0xb4, 0x9f, 0xae, 0x0b, - 0x89, 0xcb, 0xeb, 0x42, 0xe2, 0xaf, 0xeb, 0x42, 0x02, 0x6d, 0x52, 0xff, 0x86, 0x81, 0xb7, 0xb4, - 0xe7, 0x95, 0x01, 0x15, 0x47, 0xe3, 0x5e, 0xc5, 0xf6, 0x87, 0xd5, 0x29, 0x69, 0x8b, 0xfa, 0xb1, - 0xa8, 0xfa, 0x32, 0xfa, 0x00, 0xea, 0x2d, 0xc9, 0xcf, 0x8d, 0x2f, 0xfe, 0x0d, 0x00, 0x00, 0xff, - 0xff, 0x5f, 0x52, 0x0e, 0xab, 0x1e, 0x09, 0x00, 0x00, + 0x14, 0x66, 0x0d, 0x36, 0x30, 0x6b, 0x3b, 0xdb, 0x71, 0xe2, 0xae, 0x49, 0x05, 0x5b, 0xac, 0x48, + 0xa4, 0x95, 0x41, 0x76, 0xd5, 0x8b, 0x2f, 0x15, 0x18, 0xdc, 0x22, 0x25, 0x8e, 0xb5, 0x80, 0x22, + 0x45, 0x95, 0x56, 0xc3, 0xee, 0x03, 0x8f, 0xbc, 0xec, 0x92, 0x99, 0xc1, 0x4e, 0xfb, 0x07, 0x5a, + 0xf9, 0xd4, 0x63, 0x2f, 0x96, 0xfc, 0x13, 0x7a, 0xe8, 0xbd, 0xb7, 0x2a, 0x47, 0xab, 0x52, 0xa5, + 0x9e, 0xa2, 0xca, 0xbe, 0xf4, 0x67, 0x54, 0x3b, 0xbb, 0xc0, 0x9a, 0xe0, 0x3a, 0x51, 0x95, 0xdb, + 0xce, 0xfb, 0xbe, 0xf9, 0xde, 0xfb, 0xde, 0x3c, 0x9e, 0x40, 0x9b, 0x43, 0xe6, 0x9f, 0x80, 0x47, + 0x3c, 0x1b, 0x2a, 0xf0, 0xca, 0x3e, 0x22, 0x5e, 0x1f, 0x2a, 0x27, 0xdb, 0x95, 0x01, 0x61, 0xc7, + 0x20, 0xca, 0x43, 0xe6, 0x0b, 0x1f, 0xaf, 0x4f, 0x49, 0xe5, 0x31, 0xa9, 0x7c, 0xb2, 0x9d, 0xcb, + 0xdb, 0x3e, 0x1f, 0xf8, 0xbc, 0x42, 0x46, 0xe2, 0xa8, 0x72, 0xb2, 0xdd, 0x05, 0x41, 0xb6, 0xe5, + 0x21, 0xbc, 0x37, 0xc1, 0xbb, 0x84, 0xc3, 0x04, 0xb7, 0x7d, 0xea, 0x45, 0xf8, 0x46, 0x88, 0x5b, + 0xf2, 0x54, 0x09, 0x0f, 0x11, 0x74, 0xbf, 0xef, 0xf7, 0xfd, 0x30, 0x1e, 0x7c, 0x85, 0xd1, 0xe2, + 0x9f, 0x0a, 0x5a, 0x79, 0x2a, 0x2b, 0xab, 0xda, 0xb6, 0x3f, 0xf2, 0x04, 0x6e, 0xa2, 0xe5, 0x40, + 0xdd, 0x22, 0xe1, 0x59, 0x57, 0x0c, 0xa5, 0xa4, 0xee, 0x18, 0xe5, 0x48, 0x4c, 0x16, 0x13, 0x65, + 0x2e, 0xd7, 0x08, 0x87, 0xe8, 0x5e, 0x2d, 0x75, 0xf9, 0xa6, 0xa0, 0x98, 0x6a, 0x77, 0x1a, 0xc2, + 0x0f, 0x51, 0x36, 0x74, 0x6d, 0x51, 0x47, 0x5f, 0x30, 0x94, 0xd2, 0x8a, 0x99, 0x09, 0x03, 0x4d, + 0x07, 0x9b, 0x68, 0x35, 0x02, 0x1d, 0x10, 0x84, 0xba, 0x5c, 0x4f, 0xca, 0x4c, 0x8f, 0xca, 0xf3, + 0x7b, 0x53, 0x0e, 0xcb, 0xac, 0x87, 0xe4, 0x5a, 0xea, 0xf5, 0x9b, 0x42, 0xc2, 0x5c, 0x19, 0xc4, + 0x83, 0xbb, 0x99, 0x1f, 0x2f, 0x0a, 0x89, 0x9f, 0x2f, 0x0a, 0x89, 0xe2, 0x0f, 0x13, 0x5f, 0x11, + 0x86, 0x31, 0x4a, 0x79, 0x64, 0x00, 0xd2, 0x4f, 0xd6, 0x94, 0xdf, 0xd8, 0x40, 0xaa, 0x03, 0xdc, + 0x66, 0x74, 0x28, 0xa8, 0xef, 0xc9, 0x12, 0xb3, 0x66, 0x3c, 0x84, 0x0b, 0x48, 0x3d, 0x85, 0x2e, + 0xa7, 0x02, 0xac, 0x11, 0x73, 0x65, 0x89, 0x59, 0x13, 0x45, 0xa1, 0x0e, 0x73, 0xf1, 0x06, 0xca, + 0x50, 0xdb, 0xf7, 0xac, 0x11, 0xa3, 0x7a, 0x4a, 0xa2, 0xe9, 0xe0, 0xdc, 0x61, 0x74, 0x37, 0xf5, + 0xcf, 0x45, 0x41, 0x29, 0xfe, 0xa6, 0x20, 0x35, 0xac, 0xa4, 0xc6, 0x28, 0xf4, 0x6e, 0x36, 0x45, + 0x99, 0x69, 0xca, 0x57, 0x93, 0xa6, 0x10, 0xc7, 0x61, 0xc0, 0x79, 0x58, 0x53, 0x4d, 0xff, 0xe3, + 0xd7, 0xad, 0xfb, 0xd1, 0x0b, 0x54, 0x43, 0xa4, 0x25, 0x18, 0xf5, 0xfa, 0xe3, 0x0e, 0x44, 0xc1, + 0x0f, 0xd1, 0xd5, 0xe2, 0x2f, 0x69, 0xb4, 0x14, 0xd2, 0xfe, 0xbb, 0xf8, 0xb7, 0x73, 0x2f, 0xfc, + 0xdf, 0xdc, 0xf8, 0x00, 0xad, 0xf5, 0x00, 0x2c, 0x9b, 0x01, 0x11, 0x60, 0x11, 0x7e, 0x6c, 0xf5, + 0x5c, 0x22, 0xf4, 0xa4, 0x91, 0x2c, 0xa9, 0x3b, 0x1b, 0xe3, 0xa1, 0x0c, 0x86, 0x6e, 0x32, 0x94, + 0x7b, 0x3e, 0xf5, 0x22, 0x31, 0xad, 0x07, 0xb0, 0x27, 0xaf, 0x56, 0xf9, 0xf1, 0xbe, 0x4b, 0xc4, + 0x8c, 0x5e, 0x97, 0x3a, 0xa1, 0x5e, 0xea, 0x7d, 0xf5, 0x6a, 0xd4, 0x91, 0x7a, 0xdf, 0xa2, 0x5c, + 0xa0, 0xc7, 0xc1, 0x75, 0x81, 0x59, 0x1c, 0x84, 0x70, 0x61, 0x00, 0x9e, 0x08, 0x65, 0x17, 0xdf, + 0x4d, 0xf6, 0xe3, 0x1e, 0x40, 0x4b, 0x2a, 0xb4, 0x26, 0x02, 0x52, 0xbd, 0x8f, 0x3e, 0x99, 0xaf, + 0xce, 0x88, 0xa0, 0x3e, 0xd7, 0x97, 0xa4, 0xbe, 0x71, 0x5b, 0x7f, 0xf7, 0x01, 0xcc, 0x80, 0x18, + 0xa5, 0xd9, 0x98, 0x93, 0x46, 0xe2, 0x1c, 0xbf, 0x40, 0x01, 0x68, 0x75, 0x47, 0xdf, 0xcd, 0x71, + 0x91, 0x7e, 0x37, 0x17, 0xeb, 0x3d, 0x80, 0x5a, 0x20, 0x30, 0x63, 0x02, 0xd0, 0xc3, 0xb9, 0xda, + 0x91, 0x87, 0xcc, 0x7b, 0x79, 0xd0, 0xdf, 0x4e, 0x12, 0x59, 0x78, 0x8c, 0x34, 0x62, 0xdb, 0x30, + 0x14, 0xd4, 0xeb, 0x5b, 0x3e, 0x73, 0x80, 0x71, 0x3d, 0x6b, 0x28, 0xa5, 0x8c, 0x79, 0x6f, 0x12, + 0x7f, 0x26, 0xc3, 0x78, 0x07, 0x3d, 0x20, 0xae, 0xeb, 0x9f, 0x5a, 0x23, 0x7e, 0xa3, 0x24, 0x1d, + 0x49, 0xfe, 0x9a, 0x04, 0x3b, 0x3c, 0x9e, 0x04, 0x1f, 0xa0, 0x95, 0x40, 0x86, 0x73, 0xab, 0xcf, + 0x88, 0x27, 0xb8, 0xae, 0xca, 0xba, 0x37, 0x6f, 0xab, 0xbb, 0x2a, 0xc9, 0x5f, 0x07, 0xdc, 0xa8, + 0xf4, 0x65, 0x32, 0x0d, 0x71, 0xbc, 0x85, 0xd6, 0x18, 0xbc, 0xb4, 0x88, 0x10, 0x2c, 0x36, 0xdd, + 0xfa, 0xb2, 0x91, 0x2c, 0x65, 0x4d, 0x8d, 0xc1, 0xcb, 0xaa, 0x10, 0x6c, 0x32, 0xbb, 0xf3, 0xe8, + 0x5d, 0xea, 0xe8, 0x2b, 0x73, 0xe8, 0x35, 0xea, 0x14, 0xbf, 0x47, 0x99, 0x71, 0xe3, 0xf0, 0x97, + 0x68, 0x71, 0xc8, 0xa8, 0x0d, 0xd1, 0x26, 0xbf, 0xf3, 0x1d, 0x43, 0x36, 0xde, 0x46, 0xc9, 0x1e, + 0x40, 0xf4, 0x13, 0xbe, 0xf3, 0x52, 0xc0, 0xdd, 0x4d, 0x8d, 0x57, 0xaf, 0x1a, 0x73, 0x8f, 0x77, + 0x50, 0x7a, 0xbc, 0xcc, 0x94, 0x3b, 0x96, 0xd9, 0x98, 0x88, 0xeb, 0x48, 0x1d, 0x02, 0x1b, 0x50, + 0xce, 0xa9, 0xef, 0x05, 0x7b, 0x24, 0x59, 0x5a, 0xdd, 0x29, 0xde, 0xd6, 0xeb, 0xc3, 0x09, 0xd5, + 0x8c, 0x5f, 0xfb, 0xec, 0xf7, 0x05, 0x84, 0xa6, 0x18, 0xfe, 0x1c, 0xad, 0x1f, 0x36, 0xcc, 0xa7, + 0xcd, 0x56, 0xab, 0xf9, 0xec, 0xc0, 0xea, 0x1c, 0xb4, 0x0e, 0x1b, 0x7b, 0xcd, 0xfd, 0x66, 0xa3, + 0xae, 0x25, 0x72, 0xf7, 0xce, 0xce, 0x0d, 0x75, 0xe4, 0xf1, 0x21, 0xd8, 0xb4, 0x47, 0xc1, 0xc1, + 0x9f, 0xa2, 0x8f, 0x62, 0xe4, 0x56, 0xa3, 0xdd, 0x7e, 0xd2, 0xd0, 0x94, 0x1c, 0x3a, 0x3b, 0x37, + 0x96, 0xc2, 0x89, 0xc1, 0x9b, 0x08, 0xdf, 0xa4, 0x58, 0xcd, 0x7a, 0x4b, 0x5b, 0xc8, 0xa9, 0x67, + 0xe7, 0x46, 0x9a, 0xcb, 0x1d, 0xc9, 0x67, 0x74, 0xf6, 0xaa, 0x07, 0x7b, 0x8d, 0x27, 0x5a, 0x32, + 0xd4, 0xb1, 0x03, 0x27, 0x2e, 0x7e, 0x84, 0xd6, 0x62, 0x94, 0xe7, 0xcd, 0xf6, 0x37, 0x75, 0xb3, + 0xfa, 0x5c, 0x4b, 0xe5, 0x96, 0xcf, 0xce, 0x8d, 0xcc, 0x29, 0x15, 0x47, 0x0e, 0x23, 0xa7, 0x33, + 0x4a, 0x9d, 0xc3, 0x7a, 0xb5, 0xdd, 0xd0, 0x16, 0x43, 0xa5, 0xd1, 0xd0, 0x21, 0x02, 0x66, 0x1c, + 0x4e, 0x3f, 0x5b, 0xda, 0x52, 0xe8, 0x30, 0xd6, 0x1d, 0xfc, 0x18, 0x3d, 0x88, 0x91, 0xab, 0xed, + 0xb6, 0xd9, 0xac, 0x75, 0xda, 0x8d, 0x96, 0x96, 0xce, 0xad, 0x9e, 0x9d, 0x1b, 0x28, 0x98, 0x35, + 0xda, 0x1d, 0x09, 0xe0, 0x35, 0x78, 0x7d, 0x95, 0x57, 0x2e, 0xaf, 0xf2, 0xca, 0xdf, 0x57, 0x79, + 0xe5, 0xa7, 0xeb, 0x7c, 0xe2, 0xf2, 0x3a, 0x9f, 0xf8, 0xeb, 0x3a, 0x9f, 0x40, 0x1b, 0xd4, 0xbf, + 0xe5, 0x55, 0x0e, 0x95, 0x17, 0xe5, 0x3e, 0x15, 0x47, 0xa3, 0x6e, 0xd9, 0xf6, 0x07, 0x95, 0x29, + 0x69, 0x8b, 0xfa, 0xb1, 0x53, 0xe5, 0xd5, 0xe4, 0x5f, 0x52, 0x77, 0x49, 0xfe, 0x27, 0xf9, 0xe2, + 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xbd, 0x2e, 0x9b, 0x71, 0x43, 0x09, 0x00, 0x00, } func (this *MarketDetails) Equal(that interface{}) bool { diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index f230a9c273..f4c61ff317 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -2843,6 +2843,11 @@ func TestPermission_Validate(t *testing.T) { p: Permission_settle, exp: "", }, + { + name: "set_ids", + p: Permission_set_ids, + exp: "", + }, { name: "cancel", p: Permission_cancel, @@ -2911,6 +2916,7 @@ func TestPermission_Validate(t *testing.T) { func TestAllPermissions(t *testing.T) { expected := []Permission{ Permission_settle, + Permission_set_ids, Permission_cancel, Permission_withdraw, Permission_update, @@ -2938,6 +2944,25 @@ func TestParsePermission(t *testing.T) { {permission: "PERMISSION_SETTLE", expected: Permission_settle}, {permission: "pERmiSSion_seTTle", expected: Permission_settle}, + // Permission_set_ids + {permission: "set_ids", expected: Permission_set_ids}, + {permission: " set_ids", expected: Permission_set_ids}, + {permission: "set_ids ", expected: Permission_set_ids}, + {permission: "SET_IDS", expected: Permission_set_ids}, + {permission: "Set_Ids", expected: Permission_set_ids}, + {permission: "setids", expected: Permission_set_ids}, + {permission: "setids ", expected: Permission_set_ids}, + {permission: " setids", expected: Permission_set_ids}, + {permission: "setIds", expected: Permission_set_ids}, + {permission: "SetIds", expected: Permission_set_ids}, + {permission: "SETIDS", expected: Permission_set_ids}, + {permission: "permission_set_ids", expected: Permission_set_ids}, + {permission: "PERMISSION_SET_IDS", expected: Permission_set_ids}, + {permission: "peRMissiOn_sEt_iDs", expected: Permission_set_ids}, + {permission: "permission_setids", expected: Permission_set_ids}, + {permission: "PERMISSION_SETIDS", expected: Permission_set_ids}, + {permission: "peRMissiOn_sEtiDs", expected: Permission_set_ids}, + // Permission_cancel {permission: "cancel", expected: Permission_cancel}, {permission: " cancel", expected: Permission_cancel}, diff --git a/x/exchange/msg.go b/x/exchange/msg.go index 5efbd00166..1f4e0c81a9 100644 --- a/x/exchange/msg.go +++ b/x/exchange/msg.go @@ -14,6 +14,7 @@ var allRequestMsgs = []sdk.Msg{ (*MsgFillBidsRequest)(nil), (*MsgFillAsksRequest)(nil), (*MsgMarketSettleRequest)(nil), + (*MsgMarketSetOrderExternalIDRequest)(nil), (*MsgMarketWithdrawRequest)(nil), (*MsgMarketUpdateDetailsRequest)(nil), (*MsgMarketUpdateEnabledRequest)(nil), @@ -196,6 +197,33 @@ func (m MsgMarketSettleRequest) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{addr} } +func (m MsgMarketSetOrderExternalIDRequest) ValidateBasic() error { + var errs []error + + if _, err := sdk.AccAddressFromBech32(m.Admin); err != nil { + errs = append(errs, fmt.Errorf("invalid administrator %q: %w", m.Admin, err)) + } + + if m.MarketId == 0 { + errs = append(errs, errors.New("invalid market id: cannot be zero")) + } + + if err := ValidateExternalID(m.ExternalId); err != nil { + errs = append(errs, err) + } + + if m.OrderId == 0 { + errs = append(errs, errors.New("invalid order id: cannot be zero")) + } + + return errors.Join(errs...) +} + +func (m MsgMarketSetOrderExternalIDRequest) GetSigners() []sdk.AccAddress { + addr := sdk.MustAccAddressFromBech32(m.Admin) + return []sdk.AccAddress{addr} +} + func (m MsgMarketWithdrawRequest) ValidateBasic() error { var errs []error diff --git a/x/exchange/msg_test.go b/x/exchange/msg_test.go index bfa473d6ff..03c4fa953c 100644 --- a/x/exchange/msg_test.go +++ b/x/exchange/msg_test.go @@ -53,6 +53,9 @@ func TestAllMsgsGetSigners(t *testing.T) { func(signer string) sdk.Msg { return &MsgMarketSettleRequest{Admin: signer} }, + func(signer string) sdk.Msg { + return &MsgMarketSetOrderExternalIDRequest{Admin: signer} + }, func(signer string) sdk.Msg { return &MsgMarketWithdrawRequest{Admin: signer} }, @@ -779,6 +782,122 @@ func TestMsgMarketSettleRequest_ValidateBasic(t *testing.T) { } } +func TestMsgMarketSetOrderExternalIDRequest_ValidateBasic(t *testing.T) { + admin := sdk.AccAddress("admin_address_______").String() + + tests := []struct { + name string + msg MsgMarketSetOrderExternalIDRequest + expErr []string + }{ + { + name: "control", + msg: MsgMarketSetOrderExternalIDRequest{ + Admin: admin, + MarketId: 5, + OrderId: 12, + ExternalId: "twenty-eight", + }, + expErr: nil, + }, + { + name: "no admin", + msg: MsgMarketSetOrderExternalIDRequest{ + Admin: "", + MarketId: 5, + OrderId: 12, + ExternalId: "twenty-eight", + }, + expErr: []string{"invalid administrator \"\"", emptyAddrErr}, + }, + { + name: "bad admin", + msg: MsgMarketSetOrderExternalIDRequest{ + Admin: "badadmin", + MarketId: 5, + OrderId: 12, + ExternalId: "twenty-eight", + }, + expErr: []string{"invalid administrator \"badadmin\"", bech32Err}, + }, + { + name: "market zero", + msg: MsgMarketSetOrderExternalIDRequest{ + Admin: admin, + MarketId: 0, + OrderId: 12, + ExternalId: "twenty-eight", + }, + expErr: []string{"invalid market id: cannot be zero"}, + }, + { + name: "empty external id", + msg: MsgMarketSetOrderExternalIDRequest{ + Admin: admin, + MarketId: 5, + OrderId: 12, + ExternalId: "", + }, + expErr: nil, + }, + { + name: "external id at max length", + msg: MsgMarketSetOrderExternalIDRequest{ + Admin: admin, + MarketId: 5, + OrderId: 12, + ExternalId: strings.Repeat("y", MaxExternalIDLength), + }, + expErr: nil, + }, + { + name: "external id more than max length", + msg: MsgMarketSetOrderExternalIDRequest{ + Admin: admin, + MarketId: 5, + OrderId: 12, + ExternalId: strings.Repeat("r", MaxExternalIDLength+1), + }, + expErr: []string{ + fmt.Sprintf("invalid external id %q: max length %d", + strings.Repeat("r", MaxExternalIDLength+1), MaxExternalIDLength), + }, + }, + { + name: "order zero", + msg: MsgMarketSetOrderExternalIDRequest{ + Admin: admin, + MarketId: 5, + OrderId: 0, + ExternalId: "twenty-eight", + }, + expErr: []string{"invalid order id: cannot be zero"}, + }, + { + name: "multiple errors", + msg: MsgMarketSetOrderExternalIDRequest{ + Admin: "", + MarketId: 0, + OrderId: 0, + ExternalId: strings.Repeat("V", MaxExternalIDLength+1), + }, + expErr: []string{ + "invalid administrator \"\"", emptyAddrErr, + "invalid market id: cannot be zero", + fmt.Sprintf("invalid external id %q: max length %d", + strings.Repeat("V", MaxExternalIDLength+1), MaxExternalIDLength), + "invalid order id: cannot be zero", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testValidateBasic(t, &tc.msg, tc.expErr) + }) + } +} + func TestMsgMarketWithdrawRequest_ValidateBasic(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 74e1877377..5a014bffc1 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -19,6 +19,8 @@ const ( OrderTypeByteBid = byte(0x01) ) +const MaxExternalIDLength = 40 + // SubOrderI is an interface with getters for the fields in a sub-order (i.e. AskOrder or BidOrder). type SubOrderI interface { GetMarketID() uint32 @@ -27,6 +29,7 @@ type SubOrderI interface { GetPrice() sdk.Coin GetSettlementFees() sdk.Coins PartialFillAllowed() bool + GetExternalID() string GetOrderType() string GetOrderTypeByte() byte GetHoldAmount() sdk.Coins @@ -90,6 +93,14 @@ func ValidateOrderIDs(field string, orderIDs []uint64) error { return nil } +// ValidateExternalID makes sure an external id is okay. +func ValidateExternalID(externalID string) error { + if len(externalID) > MaxExternalIDLength { + return fmt.Errorf("invalid external id %q: max length %d", externalID, MaxExternalIDLength) + } + return nil +} + // NewOrder creates a new empty Order with the provided order id. // The order details are set using one of: WithAsk, WithBid. func NewOrder(orderID uint64) *Order { @@ -182,6 +193,11 @@ func (o Order) PartialFillAllowed() bool { return o.MustGetSubOrder().PartialFillAllowed() } +// GetUUID returns this order's UUID. +func (o Order) GetExternalID() string { + return o.MustGetSubOrder().GetExternalID() +} + // GetOrderType returns a string indicating what type this order is. // E.g: OrderTypeAsk or OrderTypeBid func (o Order) GetOrderType() string { @@ -327,6 +343,11 @@ func (a AskOrder) PartialFillAllowed() bool { return a.AllowPartial } +// GetExternalID returns this ask order's external id. +func (a AskOrder) GetExternalID() string { + return a.ExternalId +} + // GetOrderType returns the order type string for this ask order: "ask". func (a AskOrder) GetOrderType() string { return OrderTypeAsk @@ -386,6 +407,10 @@ func (a AskOrder) Validate() error { // Nothing to check on the AllowPartial boolean. + if err := ValidateExternalID(a.ExternalId); err != nil { + errs = append(errs, err) + } + return errors.Join(errs...) } @@ -398,6 +423,7 @@ func (a AskOrder) CopyChange(newAssets, newPrice sdk.Coin, newFee *sdk.Coin) *As Price: newPrice, SellerSettlementFlatFee: newFee, AllowPartial: a.AllowPartial, + ExternalId: a.ExternalId, } } @@ -431,6 +457,11 @@ func (b BidOrder) PartialFillAllowed() bool { return b.AllowPartial } +// GetExternalID returns this bid order's external id. +func (b BidOrder) GetExternalID() string { + return b.ExternalId +} + // GetOrderType returns the order type string for this bid order: "bid". func (b BidOrder) GetOrderType() string { return OrderTypeBid @@ -482,6 +513,10 @@ func (b BidOrder) Validate() error { // Nothing to check on the AllowPartial boolean. + if err := ValidateExternalID(b.ExternalId); err != nil { + errs = append(errs, err) + } + return errors.Join(errs...) } @@ -494,6 +529,7 @@ func (b BidOrder) CopyChange(newAssets, newPrice sdk.Coin, newFees sdk.Coins) *B Price: newPrice, BuyerSettlementFees: newFees, AllowPartial: b.AllowPartial, + ExternalId: b.ExternalId, } } @@ -576,6 +612,11 @@ func (o FilledOrder) PartialFillAllowed() bool { return o.order.PartialFillAllowed() } +// GetExternalID returns this order's external id. +func (o FilledOrder) GetExternalID() string { + return o.order.GetExternalID() +} + // GetOrderType returns a string indicating what type this order is. // E.g: OrderTypeAsk or OrderTypeBid func (o FilledOrder) GetOrderType() string { diff --git a/x/exchange/orders.pb.go b/x/exchange/orders.pb.go index 606965dab3..3598427aa7 100644 --- a/x/exchange/orders.pb.go +++ b/x/exchange/orders.pb.go @@ -136,6 +136,9 @@ type AskOrder struct { // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the // order must be either filled in full or not filled at all. AllowPartial bool `protobuf:"varint,6,opt,name=allow_partial,json=allowPartial,proto3" json:"allow_partial,omitempty"` + // external_id is an optional string used to externally identify this order. Max length is 40 characters. + // If an order in this market with this external id already exists, this order will be rejected. + ExternalId string `protobuf:"bytes,7,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } func (m *AskOrder) Reset() { *m = AskOrder{} } @@ -188,6 +191,9 @@ type BidOrder struct { // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the // order must be either filled in full or not filled at all. AllowPartial bool `protobuf:"varint,6,opt,name=allow_partial,json=allowPartial,proto3" json:"allow_partial,omitempty"` + // external_id is an optional string used to externally identify this order. Max length is 40 characters. + // If an order in this market with this external id already exists, this order will be rejected. + ExternalId string `protobuf:"bytes,7,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } func (m *BidOrder) Reset() { *m = BidOrder{} } @@ -234,40 +240,42 @@ func init() { } var fileDescriptor_dab7cbe63f582471 = []byte{ - // 525 bytes of a gzipped FileDescriptorProto + // 545 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0x31, 0x6f, 0xd3, 0x40, - 0x14, 0xb6, 0xd3, 0x38, 0x75, 0x0e, 0xba, 0x98, 0x02, 0x4e, 0x90, 0x9c, 0x28, 0x5d, 0xb2, 0xe4, - 0xdc, 0x80, 0x10, 0x12, 0x0b, 0x6a, 0x90, 0x2a, 0x3a, 0x51, 0xb9, 0x12, 0x03, 0x8b, 0x75, 0xb6, - 0x5f, 0xdd, 0x53, 0x1c, 0x9f, 0xe5, 0xbb, 0x86, 0x76, 0x62, 0x65, 0xe4, 0x27, 0x30, 0xb3, 0xc2, - 0x8f, 0xe8, 0xc0, 0x50, 0x31, 0x31, 0x01, 0x4a, 0x7e, 0x02, 0x7f, 0x00, 0xf9, 0xee, 0x92, 0x06, - 0x09, 0x42, 0x59, 0x98, 0xfc, 0xde, 0xbb, 0xef, 0xfb, 0xde, 0xa7, 0x4f, 0x7a, 0x46, 0x3b, 0x45, - 0xc9, 0xa6, 0x90, 0x93, 0x3c, 0x06, 0x1f, 0xce, 0xe2, 0x13, 0x92, 0xa7, 0xe0, 0x4f, 0x87, 0x3e, - 0x2b, 0x13, 0x28, 0x39, 0x2e, 0x4a, 0x26, 0x98, 0x73, 0xe7, 0x0a, 0x84, 0x17, 0x20, 0x3c, 0x1d, - 0xb6, 0xbd, 0x98, 0xf1, 0x09, 0xe3, 0x7e, 0x44, 0x78, 0x45, 0x8a, 0x40, 0x90, 0xa1, 0x1f, 0x33, - 0x9a, 0x2b, 0x5e, 0xbb, 0xa5, 0xde, 0x43, 0xd9, 0xf9, 0xaa, 0xd1, 0x4f, 0xdb, 0x29, 0x4b, 0x99, - 0x9a, 0x57, 0x95, 0x9a, 0xf6, 0x3e, 0x98, 0xc8, 0x7a, 0x5e, 0x6d, 0x76, 0x5a, 0xc8, 0x96, 0x16, - 0x42, 0x9a, 0xb8, 0x66, 0xd7, 0xec, 0xd7, 0x83, 0x4d, 0xd9, 0x1f, 0x24, 0xce, 0x13, 0xd4, 0x24, - 0x7c, 0x1c, 0xca, 0xd6, 0xad, 0x75, 0xcd, 0xfe, 0x8d, 0xfb, 0x5d, 0xfc, 0x7b, 0x87, 0x78, 0x8f, - 0x8f, 0xa5, 0xde, 0x33, 0x23, 0xb0, 0x89, 0xae, 0x2b, 0x81, 0x88, 0x26, 0x5a, 0x60, 0x63, 0xbd, - 0xc0, 0x88, 0x26, 0x4b, 0x81, 0x48, 0xd7, 0x8f, 0xeb, 0x6f, 0xde, 0x75, 0x8c, 0xd1, 0x26, 0xb2, - 0xa4, 0x44, 0xef, 0x53, 0x0d, 0xd9, 0x8b, 0x45, 0xce, 0x3d, 0xd4, 0x9c, 0x90, 0x72, 0x0c, 0x62, - 0xe1, 0x7c, 0x2b, 0xb0, 0xd5, 0xe0, 0x20, 0x71, 0x76, 0x51, 0x83, 0x43, 0x96, 0x69, 0xdf, 0xcd, - 0x91, 0xfb, 0xf9, 0xe3, 0x60, 0x5b, 0xe7, 0xb2, 0x97, 0x24, 0x25, 0x70, 0x7e, 0x24, 0x4a, 0x9a, - 0xa7, 0x81, 0xc6, 0x39, 0x8f, 0x50, 0x83, 0x70, 0x0e, 0x82, 0x6b, 0xa3, 0x2d, 0xac, 0xe1, 0x55, - 0xe6, 0x58, 0x67, 0x8e, 0x9f, 0x32, 0x9a, 0x8f, 0xea, 0x17, 0x5f, 0x3b, 0x46, 0xa0, 0xe1, 0xce, - 0x43, 0x64, 0x15, 0x25, 0x8d, 0xc1, 0xad, 0x5f, 0x8f, 0xa7, 0xd0, 0xce, 0x0b, 0xd4, 0x56, 0x9b, - 0x43, 0x0e, 0x42, 0x64, 0x30, 0x81, 0x5c, 0x84, 0xc7, 0x19, 0x11, 0xe1, 0x31, 0x80, 0x6b, 0xfd, - 0x45, 0x2b, 0xb8, 0xab, 0xc8, 0x47, 0x4b, 0xee, 0x7e, 0x46, 0xc4, 0x3e, 0x80, 0xb3, 0x83, 0xb6, - 0x48, 0x96, 0xb1, 0x57, 0x61, 0x41, 0x4a, 0x41, 0x49, 0xe6, 0x36, 0xba, 0x66, 0xdf, 0x0e, 0x6e, - 0xca, 0xe1, 0xa1, 0x9a, 0xa9, 0x5c, 0x7b, 0x3f, 0x6a, 0xc8, 0x5e, 0xc4, 0xbe, 0x3e, 0x4e, 0x8c, - 0xac, 0xe8, 0xf4, 0xfc, 0x1a, 0x69, 0x2a, 0xd8, 0x7f, 0x0f, 0xf3, 0x35, 0xba, 0x2d, 0x17, 0xff, - 0x92, 0x25, 0x00, 0x77, 0xad, 0xee, 0xc6, 0x7a, 0x99, 0xdd, 0x4a, 0xe6, 0xfd, 0xb7, 0x4e, 0x3f, - 0xa5, 0xe2, 0xe4, 0x34, 0xc2, 0x31, 0x9b, 0xe8, 0xfb, 0xd1, 0x9f, 0x01, 0x4f, 0xc6, 0xbe, 0x38, - 0x2f, 0x80, 0x4b, 0x02, 0x0f, 0x6e, 0xc9, 0x4d, 0x2b, 0xc1, 0x03, 0xf0, 0x7f, 0x48, 0x7d, 0x04, - 0x17, 0x33, 0xcf, 0xbc, 0x9c, 0x79, 0xe6, 0xf7, 0x99, 0x67, 0xbe, 0x9d, 0x7b, 0xc6, 0xe5, 0xdc, - 0x33, 0xbe, 0xcc, 0x3d, 0x03, 0xb5, 0x28, 0xfb, 0xc3, 0x75, 0x1c, 0x9a, 0x2f, 0xf1, 0x8a, 0xc1, - 0x2b, 0xd0, 0x80, 0xb2, 0x95, 0xce, 0x3f, 0x5b, 0xfe, 0x5a, 0xa2, 0x86, 0x3c, 0xf4, 0x07, 0x3f, - 0x03, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x3d, 0x62, 0x47, 0x78, 0x04, 0x00, 0x00, + 0x18, 0xb5, 0x9b, 0x38, 0x71, 0xae, 0x74, 0x31, 0x05, 0x9c, 0x20, 0x39, 0x51, 0xba, 0x64, 0xc9, + 0xb9, 0x01, 0x21, 0x24, 0x16, 0xd4, 0x20, 0x55, 0x64, 0xa2, 0x72, 0x25, 0x06, 0x16, 0xeb, 0x6c, + 0x7f, 0x4d, 0x4f, 0x71, 0x7c, 0x91, 0xef, 0x1a, 0xd2, 0x89, 0x95, 0x91, 0x81, 0x1f, 0xc0, 0xcc, + 0x0a, 0x3f, 0xa2, 0x63, 0xc5, 0xc4, 0x04, 0x28, 0x99, 0xf9, 0x0f, 0xc8, 0x77, 0x97, 0x34, 0x48, + 0x10, 0x3a, 0xa0, 0x4e, 0xbe, 0xef, 0xdd, 0x7b, 0xef, 0xfb, 0xfc, 0x4e, 0x77, 0x68, 0x6f, 0x92, + 0xb3, 0x29, 0x64, 0x24, 0x8b, 0xc1, 0x87, 0x59, 0x7c, 0x4a, 0xb2, 0x21, 0xf8, 0xd3, 0x9e, 0xcf, + 0xf2, 0x04, 0x72, 0x8e, 0x27, 0x39, 0x13, 0xcc, 0xb9, 0x7b, 0x45, 0xc2, 0x4b, 0x12, 0x9e, 0xf6, + 0x1a, 0x5e, 0xcc, 0xf8, 0x98, 0x71, 0x3f, 0x22, 0xbc, 0x10, 0x45, 0x20, 0x48, 0xcf, 0x8f, 0x19, + 0xcd, 0x94, 0xae, 0x51, 0x57, 0xfb, 0xa1, 0xac, 0x7c, 0x55, 0xe8, 0xad, 0xdd, 0x21, 0x1b, 0x32, + 0x85, 0x17, 0x2b, 0x85, 0xb6, 0x3f, 0x99, 0xc8, 0x7a, 0x51, 0x74, 0x76, 0xea, 0xc8, 0x96, 0x23, + 0x84, 0x34, 0x71, 0xcd, 0x96, 0xd9, 0x29, 0x07, 0x55, 0x59, 0x0f, 0x12, 0xe7, 0x29, 0xaa, 0x11, + 0x3e, 0x0a, 0x65, 0xe9, 0x6e, 0xb5, 0xcc, 0xce, 0xf6, 0x83, 0x16, 0xfe, 0xf3, 0x84, 0xf8, 0x80, + 0x8f, 0xa4, 0xdf, 0x73, 0x23, 0xb0, 0x89, 0x5e, 0x17, 0x06, 0x11, 0x4d, 0xb4, 0x41, 0x69, 0xb3, + 0x41, 0x9f, 0x26, 0x2b, 0x83, 0x48, 0xaf, 0x9f, 0x94, 0xdf, 0x7e, 0x68, 0x1a, 0xfd, 0x2a, 0xb2, + 0xa4, 0x45, 0xfb, 0xe7, 0x16, 0xb2, 0x97, 0x8d, 0x9c, 0xfb, 0xa8, 0x36, 0x26, 0xf9, 0x08, 0xc4, + 0x72, 0xf2, 0x9d, 0xc0, 0x56, 0xc0, 0x20, 0x71, 0xf6, 0x51, 0x85, 0x43, 0x9a, 0xea, 0xb9, 0x6b, + 0x7d, 0xf7, 0xcb, 0xe7, 0xee, 0xae, 0xce, 0xe5, 0x20, 0x49, 0x72, 0xe0, 0xfc, 0x58, 0xe4, 0x34, + 0x1b, 0x06, 0x9a, 0xe7, 0x3c, 0x46, 0x15, 0xc2, 0x39, 0x08, 0xae, 0x07, 0xad, 0x63, 0x4d, 0x2f, + 0x32, 0xc7, 0x3a, 0x73, 0xfc, 0x8c, 0xd1, 0xac, 0x5f, 0xbe, 0xf8, 0xd6, 0x34, 0x02, 0x4d, 0x77, + 0x1e, 0x21, 0x6b, 0x92, 0xd3, 0x18, 0xdc, 0xf2, 0xf5, 0x74, 0x8a, 0xed, 0xbc, 0x44, 0x0d, 0xd5, + 0x39, 0xe4, 0x20, 0x44, 0x0a, 0x63, 0xc8, 0x44, 0x78, 0x92, 0x12, 0x11, 0x9e, 0x00, 0xb8, 0xd6, + 0x3f, 0xbc, 0x82, 0x7b, 0x4a, 0x7c, 0xbc, 0xd2, 0x1e, 0xa6, 0x44, 0x1c, 0x02, 0x38, 0x7b, 0x68, + 0x87, 0xa4, 0x29, 0x7b, 0x1d, 0x4e, 0x48, 0x2e, 0x28, 0x49, 0xdd, 0x4a, 0xcb, 0xec, 0xd8, 0xc1, + 0x2d, 0x09, 0x1e, 0x29, 0xcc, 0x69, 0xa2, 0x6d, 0x98, 0x09, 0xc8, 0x33, 0x92, 0x16, 0xe9, 0x55, + 0x8b, 0x8c, 0x02, 0xb4, 0x84, 0x06, 0x89, 0x0a, 0xbe, 0xfd, 0xbe, 0x84, 0xec, 0xe5, 0xb9, 0x6c, + 0xce, 0x1b, 0x23, 0x2b, 0x3a, 0x3b, 0xbf, 0x46, 0xdc, 0x8a, 0x76, 0xe3, 0x69, 0xbf, 0x41, 0x77, + 0x64, 0xe3, 0xdf, 0xc2, 0x06, 0xe0, 0xae, 0xd5, 0x2a, 0x6d, 0xb6, 0xd9, 0x2f, 0x6c, 0x3e, 0x7e, + 0x6f, 0x76, 0x86, 0x54, 0x9c, 0x9e, 0x45, 0x38, 0x66, 0x63, 0x7d, 0xc1, 0xf4, 0xa7, 0xcb, 0x93, + 0x91, 0x2f, 0xce, 0x27, 0xc0, 0xa5, 0x80, 0x07, 0xb7, 0x65, 0xa7, 0xb5, 0x93, 0x01, 0xe0, 0xff, + 0xf3, 0x58, 0xfa, 0x70, 0x31, 0xf7, 0xcc, 0xcb, 0xb9, 0x67, 0xfe, 0x98, 0x7b, 0xe6, 0xbb, 0x85, + 0x67, 0x5c, 0x2e, 0x3c, 0xe3, 0xeb, 0xc2, 0x33, 0x50, 0x9d, 0xb2, 0xbf, 0xdc, 0xaf, 0x23, 0xf3, + 0x15, 0x5e, 0xfb, 0x83, 0x2b, 0x52, 0x97, 0xb2, 0xb5, 0xca, 0x9f, 0xad, 0x1e, 0xa7, 0xa8, 0x22, + 0x9f, 0x8a, 0x87, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0xa1, 0x1d, 0x88, 0x31, 0xba, 0x04, 0x00, + 0x00, } func (m *Order) Marshal() (dAtA []byte, err error) { @@ -369,6 +377,13 @@ func (m *AskOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ExternalId) > 0 { + i -= len(m.ExternalId) + copy(dAtA[i:], m.ExternalId) + i = encodeVarintOrders(dAtA, i, uint64(len(m.ExternalId))) + i-- + dAtA[i] = 0x3a + } if m.AllowPartial { i-- if m.AllowPartial { @@ -446,6 +461,13 @@ func (m *BidOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ExternalId) > 0 { + i -= len(m.ExternalId) + copy(dAtA[i:], m.ExternalId) + i = encodeVarintOrders(dAtA, i, uint64(len(m.ExternalId))) + i-- + dAtA[i] = 0x3a + } if m.AllowPartial { i-- if m.AllowPartial { @@ -579,6 +601,10 @@ func (m *AskOrder) Size() (n int) { if m.AllowPartial { n += 2 } + l = len(m.ExternalId) + if l > 0 { + n += 1 + l + sovOrders(uint64(l)) + } return n } @@ -608,6 +634,10 @@ func (m *BidOrder) Size() (n int) { if m.AllowPartial { n += 2 } + l = len(m.ExternalId) + if l > 0 { + n += 1 + l + sovOrders(uint64(l)) + } return n } @@ -958,6 +988,38 @@ func (m *AskOrder) Unmarshal(dAtA []byte) error { } } m.AllowPartial = bool(v != 0) + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOrders + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOrders + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExternalId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipOrders(dAtA[iNdEx:]) @@ -1179,6 +1241,38 @@ func (m *BidOrder) Unmarshal(dAtA []byte) error { } } m.AllowPartial = bool(v != 0) + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOrders + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOrders + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOrders + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExternalId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipOrders(dAtA[iNdEx:]) diff --git a/x/exchange/orders_test.go b/x/exchange/orders_test.go index c375f67102..dc671745ae 100644 --- a/x/exchange/orders_test.go +++ b/x/exchange/orders_test.go @@ -52,6 +52,7 @@ func copyAskOrder(askOrder *AskOrder) *AskOrder { Price: copyCoin(askOrder.Price), SellerSettlementFlatFee: copyCoinP(askOrder.SellerSettlementFlatFee), AllowPartial: askOrder.AllowPartial, + ExternalId: askOrder.ExternalId, } } @@ -67,6 +68,7 @@ func copyBidOrder(bidOrder *BidOrder) *BidOrder { Price: copyCoin(bidOrder.Price), BuyerSettlementFees: copyCoins(bidOrder.BuyerSettlementFees), AllowPartial: bidOrder.AllowPartial, + ExternalId: bidOrder.ExternalId, } } @@ -107,6 +109,7 @@ func askOrderString(askOrder *AskOrder) string { fmt.Sprintf("Price:%q", askOrder.Price), fmt.Sprintf("SellerSettlementFlatFee:%s", coinPString(askOrder.SellerSettlementFlatFee)), fmt.Sprintf("AllowPartial:%t", askOrder.AllowPartial), + fmt.Sprintf("ExternalID:%s", askOrder.ExternalId), } return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) } @@ -124,6 +127,7 @@ func bidOrderString(bidOrder *BidOrder) string { fmt.Sprintf("Price:%q", bidOrder.Price), fmt.Sprintf("BuyerSettlementFees:%s", coinsString(bidOrder.BuyerSettlementFees)), fmt.Sprintf("AllowPartial:%t", bidOrder.AllowPartial), + fmt.Sprintf("ExternalID:%s", bidOrder.ExternalId), } return fmt.Sprintf("{%s}", strings.Join(fields, ", ")) } @@ -258,6 +262,42 @@ func TestValidateOrderIDs(t *testing.T) { } } +func TestValidateExternalID(t *testing.T) { + tests := []struct { + name string + externalID string + expErr string + }{ + { + name: "empty", + externalID: "", + expErr: "", + }, + { + name: "max length", + externalID: strings.Repeat("m", MaxExternalIDLength), + expErr: "", + }, + { + name: "max length + 1", + externalID: strings.Repeat("n", MaxExternalIDLength+1), + expErr: fmt.Sprintf("invalid external id %q: max length %d", + strings.Repeat("n", MaxExternalIDLength+1), MaxExternalIDLength), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + testFunc := func() { + err = ValidateExternalID(tc.externalID) + } + require.NotPanics(t, testFunc, "ValidateExternalID(%q)", tc.externalID) + assertions.AssertErrorValue(t, err, tc.expErr, "ValidateExternalID(%q)", tc.externalID) + }) + } +} + func TestOrderSizes(t *testing.T) { // This unit test is mostly just to see the sizes of different orders and compare // that to the initial array size used in getOrderStoreKeyValue. @@ -945,6 +985,47 @@ func TestOrder_PartialFillAllowed(t *testing.T) { } } +func TestOrder_GetExternalID(t *testing.T) { + tests := []struct { + name string + order *Order + expected string + expPanic string + }{ + { + name: "AskOrder", + order: NewOrder(1).WithAsk(&AskOrder{ExternalId: "ask12345"}), + expected: "ask12345", + }, + { + name: "BidOrder", + order: NewOrder(2).WithBid(&BidOrder{ExternalId: "bid987654"}), + expected: "bid987654", + }, + { + name: "nil inside order", + order: NewOrder(3), + expPanic: nilSubTypeErr(3), + }, + { + name: "unknown order type", + order: newUnknownOrder(4), + expPanic: unknownSubTypeErr(4), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.order.GetExternalID() + } + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetExternalID()") + assert.Equal(t, tc.expected, actual, "GetExternalID() result") + }) + } +} + func TestOrder_GetOrderType(t *testing.T) { tests := []struct { name string @@ -1481,6 +1562,29 @@ func TestAskOrder_PartialFillAllowed(t *testing.T) { } } +func TestAskOrder_GetExternalID(t *testing.T) { + tests := []struct { + name string + order AskOrder + exp string + }{ + {name: "empty", order: AskOrder{ExternalId: ""}, exp: ""}, + {name: "something", order: AskOrder{ExternalId: "something"}, exp: "something"}, + {name: "a uuid", order: AskOrder{ExternalId: "36585FC1-C11D-42A4-B1F7-92B0D8229BC7"}, exp: "36585FC1-C11D-42A4-B1F7-92B0D8229BC7"}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.order.GetExternalID() + } + require.NotPanics(t, testFunc, "GetExternalID()") + assert.Equal(t, tc.exp, actual, "GetExternalID() result") + }) + } +} + func TestAskOrder_GetOrderType(t *testing.T) { expected := OrderTypeAsk order := AskOrder{} @@ -2068,6 +2172,29 @@ func TestBidOrder_PartialFillAllowed(t *testing.T) { } } +func TestBidOrder_GetExternalID(t *testing.T) { + tests := []struct { + name string + order BidOrder + exp string + }{ + {name: "empty", order: BidOrder{ExternalId: ""}, exp: ""}, + {name: "something", order: BidOrder{ExternalId: "something"}, exp: "something"}, + {name: "a uuid", order: BidOrder{ExternalId: "36585FC1-C11D-42A4-B1F7-92B0D8229BC7"}, exp: "36585FC1-C11D-42A4-B1F7-92B0D8229BC7"}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var actual string + testFunc := func() { + actual = tc.order.GetExternalID() + } + require.NotPanics(t, testFunc, "GetExternalID()") + assert.Equal(t, tc.exp, actual, "GetExternalID() result") + }) + } +} + func TestBidOrder_GetOrderType(t *testing.T) { expected := OrderTypeBid order := BidOrder{} @@ -2510,6 +2637,7 @@ func TestFilledOrderGetters(t *testing.T) { Price: sdk.NewInt64Coin("peach", 111), SellerSettlementFlatFee: &sdk.Coin{Denom: "fig", Amount: sdkmath.NewInt(8)}, AllowPartial: true, + ExternalId: "ask order abc", } ask := NewOrder(51).WithAsk(askOrder) askActualPrice := sdk.NewInt64Coin("peach", 123) @@ -2523,6 +2651,7 @@ func TestFilledOrderGetters(t *testing.T) { Price: sdk.NewInt64Coin("peach", 112), BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 9)), AllowPartial: true, + ExternalId: "bid order def", } bid := NewOrder(52).WithBid(bidOrder) bidActualPrice := sdk.NewInt64Coin("peach", 124) @@ -2607,6 +2736,12 @@ func TestFilledOrderGetters(t *testing.T) { expAsk: askOrder.AllowPartial, expBid: bidOrder.AllowPartial, }, + { + name: "GetExternalID", + getter: func(of *FilledOrder) interface{} { return of.GetExternalID() }, + expAsk: askOrder.ExternalId, + expBid: bidOrder.ExternalId, + }, { name: "GetOrderType", getter: func(of *FilledOrder) interface{} { return of.GetOrderType() }, diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index b801156ecf..5b93f1b8ad 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -255,6 +255,107 @@ func (m *QueryGetOrderResponse) GetOrder() *Order { return nil } +// QueryGetOrderByExternalIDRequest is a request message for the QueryGetOrderByExternalID endpoint. +type QueryGetOrderByExternalIDRequest struct { + // market_id is the id of the market that's expected to have the order. + MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // external_id the external id to look up. + ExternalId string `protobuf:"bytes,2,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` +} + +func (m *QueryGetOrderByExternalIDRequest) Reset() { *m = QueryGetOrderByExternalIDRequest{} } +func (m *QueryGetOrderByExternalIDRequest) String() string { return proto.CompactTextString(m) } +func (*QueryGetOrderByExternalIDRequest) ProtoMessage() {} +func (*QueryGetOrderByExternalIDRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{4} +} +func (m *QueryGetOrderByExternalIDRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetOrderByExternalIDRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetOrderByExternalIDRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetOrderByExternalIDRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetOrderByExternalIDRequest.Merge(m, src) +} +func (m *QueryGetOrderByExternalIDRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryGetOrderByExternalIDRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetOrderByExternalIDRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetOrderByExternalIDRequest proto.InternalMessageInfo + +func (m *QueryGetOrderByExternalIDRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *QueryGetOrderByExternalIDRequest) GetExternalId() string { + if m != nil { + return m.ExternalId + } + return "" +} + +// QueryGetOrderByExternalIDResponse is a response message for the QueryGetOrderByExternalID endpoint. +type QueryGetOrderByExternalIDResponse struct { + // order is the requested order. + Order *Order `protobuf:"bytes,1,opt,name=order,proto3" json:"order,omitempty"` +} + +func (m *QueryGetOrderByExternalIDResponse) Reset() { *m = QueryGetOrderByExternalIDResponse{} } +func (m *QueryGetOrderByExternalIDResponse) String() string { return proto.CompactTextString(m) } +func (*QueryGetOrderByExternalIDResponse) ProtoMessage() {} +func (*QueryGetOrderByExternalIDResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00949b75b1c10bfe, []int{5} +} +func (m *QueryGetOrderByExternalIDResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetOrderByExternalIDResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetOrderByExternalIDResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetOrderByExternalIDResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetOrderByExternalIDResponse.Merge(m, src) +} +func (m *QueryGetOrderByExternalIDResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryGetOrderByExternalIDResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetOrderByExternalIDResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetOrderByExternalIDResponse proto.InternalMessageInfo + +func (m *QueryGetOrderByExternalIDResponse) GetOrder() *Order { + if m != nil { + return m.Order + } + return nil +} + // QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. type QueryGetMarketOrdersRequest struct { // market_id is the id of the market to get all the orders for. @@ -271,7 +372,7 @@ func (m *QueryGetMarketOrdersRequest) Reset() { *m = QueryGetMarketOrder func (m *QueryGetMarketOrdersRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetMarketOrdersRequest) ProtoMessage() {} func (*QueryGetMarketOrdersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{4} + return fileDescriptor_00949b75b1c10bfe, []int{6} } func (m *QueryGetMarketOrdersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -340,7 +441,7 @@ func (m *QueryGetMarketOrdersResponse) Reset() { *m = QueryGetMarketOrde func (m *QueryGetMarketOrdersResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetMarketOrdersResponse) ProtoMessage() {} func (*QueryGetMarketOrdersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{5} + return fileDescriptor_00949b75b1c10bfe, []int{7} } func (m *QueryGetMarketOrdersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -399,7 +500,7 @@ func (m *QueryGetOwnerOrdersRequest) Reset() { *m = QueryGetOwnerOrdersR func (m *QueryGetOwnerOrdersRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetOwnerOrdersRequest) ProtoMessage() {} func (*QueryGetOwnerOrdersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{6} + return fileDescriptor_00949b75b1c10bfe, []int{8} } func (m *QueryGetOwnerOrdersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -468,7 +569,7 @@ func (m *QueryGetOwnerOrdersResponse) Reset() { *m = QueryGetOwnerOrders func (m *QueryGetOwnerOrdersResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetOwnerOrdersResponse) ProtoMessage() {} func (*QueryGetOwnerOrdersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{7} + return fileDescriptor_00949b75b1c10bfe, []int{9} } func (m *QueryGetOwnerOrdersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -527,7 +628,7 @@ func (m *QueryGetAssetOrdersRequest) Reset() { *m = QueryGetAssetOrdersR func (m *QueryGetAssetOrdersRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetAssetOrdersRequest) ProtoMessage() {} func (*QueryGetAssetOrdersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{8} + return fileDescriptor_00949b75b1c10bfe, []int{10} } func (m *QueryGetAssetOrdersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -596,7 +697,7 @@ func (m *QueryGetAssetOrdersResponse) Reset() { *m = QueryGetAssetOrders func (m *QueryGetAssetOrdersResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetAssetOrdersResponse) ProtoMessage() {} func (*QueryGetAssetOrdersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{9} + return fileDescriptor_00949b75b1c10bfe, []int{11} } func (m *QueryGetAssetOrdersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -649,7 +750,7 @@ func (m *QueryGetAllOrdersRequest) Reset() { *m = QueryGetAllOrdersReque func (m *QueryGetAllOrdersRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetAllOrdersRequest) ProtoMessage() {} func (*QueryGetAllOrdersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{10} + return fileDescriptor_00949b75b1c10bfe, []int{12} } func (m *QueryGetAllOrdersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -697,7 +798,7 @@ func (m *QueryGetAllOrdersResponse) Reset() { *m = QueryGetAllOrdersResp func (m *QueryGetAllOrdersResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetAllOrdersResponse) ProtoMessage() {} func (*QueryGetAllOrdersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{11} + return fileDescriptor_00949b75b1c10bfe, []int{13} } func (m *QueryGetAllOrdersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -750,7 +851,7 @@ func (m *QueryGetMarketRequest) Reset() { *m = QueryGetMarketRequest{} } func (m *QueryGetMarketRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetMarketRequest) ProtoMessage() {} func (*QueryGetMarketRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{12} + return fileDescriptor_00949b75b1c10bfe, []int{14} } func (m *QueryGetMarketRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -798,7 +899,7 @@ func (m *QueryGetMarketResponse) Reset() { *m = QueryGetMarketResponse{} func (m *QueryGetMarketResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetMarketResponse) ProtoMessage() {} func (*QueryGetMarketResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{13} + return fileDescriptor_00949b75b1c10bfe, []int{15} } func (m *QueryGetMarketResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -851,7 +952,7 @@ func (m *QueryGetAllMarketsRequest) Reset() { *m = QueryGetAllMarketsReq func (m *QueryGetAllMarketsRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetAllMarketsRequest) ProtoMessage() {} func (*QueryGetAllMarketsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{14} + return fileDescriptor_00949b75b1c10bfe, []int{16} } func (m *QueryGetAllMarketsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -899,7 +1000,7 @@ func (m *QueryGetAllMarketsResponse) Reset() { *m = QueryGetAllMarketsRe func (m *QueryGetAllMarketsResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetAllMarketsResponse) ProtoMessage() {} func (*QueryGetAllMarketsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{15} + return fileDescriptor_00949b75b1c10bfe, []int{17} } func (m *QueryGetAllMarketsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -950,7 +1051,7 @@ func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } func (*QueryParamsRequest) ProtoMessage() {} func (*QueryParamsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{16} + return fileDescriptor_00949b75b1c10bfe, []int{18} } func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -989,7 +1090,7 @@ func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } func (*QueryParamsResponse) ProtoMessage() {} func (*QueryParamsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{17} + return fileDescriptor_00949b75b1c10bfe, []int{19} } func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1035,7 +1136,7 @@ func (m *QueryValidateCreateMarketRequest) Reset() { *m = QueryValidateC func (m *QueryValidateCreateMarketRequest) String() string { return proto.CompactTextString(m) } func (*QueryValidateCreateMarketRequest) ProtoMessage() {} func (*QueryValidateCreateMarketRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{18} + return fileDescriptor_00949b75b1c10bfe, []int{20} } func (m *QueryValidateCreateMarketRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1087,7 +1188,7 @@ func (m *QueryValidateCreateMarketResponse) Reset() { *m = QueryValidate func (m *QueryValidateCreateMarketResponse) String() string { return proto.CompactTextString(m) } func (*QueryValidateCreateMarketResponse) ProtoMessage() {} func (*QueryValidateCreateMarketResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{19} + return fileDescriptor_00949b75b1c10bfe, []int{21} } func (m *QueryValidateCreateMarketResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1140,7 +1241,7 @@ func (m *QueryValidateMarketRequest) Reset() { *m = QueryValidateMarketR func (m *QueryValidateMarketRequest) String() string { return proto.CompactTextString(m) } func (*QueryValidateMarketRequest) ProtoMessage() {} func (*QueryValidateMarketRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{20} + return fileDescriptor_00949b75b1c10bfe, []int{22} } func (m *QueryValidateMarketRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1186,7 +1287,7 @@ func (m *QueryValidateMarketResponse) Reset() { *m = QueryValidateMarket func (m *QueryValidateMarketResponse) String() string { return proto.CompactTextString(m) } func (*QueryValidateMarketResponse) ProtoMessage() {} func (*QueryValidateMarketResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{21} + return fileDescriptor_00949b75b1c10bfe, []int{23} } func (m *QueryValidateMarketResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1232,7 +1333,7 @@ func (m *QueryValidateManageFeesRequest) Reset() { *m = QueryValidateMan func (m *QueryValidateManageFeesRequest) String() string { return proto.CompactTextString(m) } func (*QueryValidateManageFeesRequest) ProtoMessage() {} func (*QueryValidateManageFeesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{22} + return fileDescriptor_00949b75b1c10bfe, []int{24} } func (m *QueryValidateManageFeesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1284,7 +1385,7 @@ func (m *QueryValidateManageFeesResponse) Reset() { *m = QueryValidateMa func (m *QueryValidateManageFeesResponse) String() string { return proto.CompactTextString(m) } func (*QueryValidateManageFeesResponse) ProtoMessage() {} func (*QueryValidateManageFeesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00949b75b1c10bfe, []int{23} + return fileDescriptor_00949b75b1c10bfe, []int{25} } func (m *QueryValidateManageFeesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1332,6 +1433,8 @@ func init() { proto.RegisterType((*QueryOrderFeeCalcResponse)(nil), "provenance.exchange.v1.QueryOrderFeeCalcResponse") proto.RegisterType((*QueryGetOrderRequest)(nil), "provenance.exchange.v1.QueryGetOrderRequest") proto.RegisterType((*QueryGetOrderResponse)(nil), "provenance.exchange.v1.QueryGetOrderResponse") + proto.RegisterType((*QueryGetOrderByExternalIDRequest)(nil), "provenance.exchange.v1.QueryGetOrderByExternalIDRequest") + proto.RegisterType((*QueryGetOrderByExternalIDResponse)(nil), "provenance.exchange.v1.QueryGetOrderByExternalIDResponse") proto.RegisterType((*QueryGetMarketOrdersRequest)(nil), "provenance.exchange.v1.QueryGetMarketOrdersRequest") proto.RegisterType((*QueryGetMarketOrdersResponse)(nil), "provenance.exchange.v1.QueryGetMarketOrdersResponse") proto.RegisterType((*QueryGetOwnerOrdersRequest)(nil), "provenance.exchange.v1.QueryGetOwnerOrdersRequest") @@ -1359,94 +1462,100 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 1387 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4d, 0x6f, 0x1b, 0x45, - 0x18, 0xce, 0x24, 0x4d, 0x9a, 0x4c, 0x69, 0x51, 0xa7, 0x6e, 0xb1, 0xdd, 0xd6, 0x71, 0xb7, 0x55, - 0x1b, 0xa5, 0xcd, 0x6e, 0x6c, 0xa7, 0x81, 0x1e, 0x7a, 0x48, 0x22, 0x25, 0xaa, 0x44, 0x95, 0x74, - 0x41, 0x20, 0xf5, 0x80, 0x19, 0xdb, 0x93, 0xcd, 0x2a, 0xeb, 0x9d, 0xed, 0xee, 0xc6, 0x4d, 0x14, - 0xe5, 0x00, 0xe2, 0xc2, 0x05, 0x21, 0x71, 0x41, 0x42, 0x54, 0x48, 0xf4, 0x07, 0x70, 0x28, 0x67, - 0xb8, 0x91, 0x63, 0x05, 0x17, 0xb8, 0x20, 0x94, 0xf0, 0x07, 0xe0, 0x17, 0xa0, 0x9d, 0x99, 0xb5, - 0x77, 0xed, 0xfd, 0x32, 0xca, 0x21, 0xa7, 0x78, 0x67, 0xdf, 0x8f, 0xe7, 0x7d, 0xde, 0x99, 0x77, - 0x9e, 0x0d, 0x94, 0x2c, 0x9b, 0x76, 0x88, 0x89, 0xcd, 0x26, 0x51, 0xc8, 0x6e, 0x73, 0x0b, 0x9b, - 0x1a, 0x51, 0x3a, 0x15, 0xe5, 0xd9, 0x0e, 0xb1, 0xf7, 0x64, 0xcb, 0xa6, 0x2e, 0x45, 0x57, 0x7a, - 0x36, 0xb2, 0x6f, 0x23, 0x77, 0x2a, 0xc5, 0x42, 0x93, 0x3a, 0x6d, 0xea, 0xd4, 0x99, 0x95, 0xc2, - 0x1f, 0xb8, 0x4b, 0x71, 0x96, 0x3f, 0x29, 0x0d, 0xec, 0x10, 0x1e, 0x4b, 0xe9, 0x54, 0x1a, 0xc4, - 0xc5, 0x15, 0xc5, 0xc2, 0x9a, 0x6e, 0x62, 0x57, 0xa7, 0xa6, 0xb0, 0x2d, 0x05, 0x6d, 0x7d, 0xab, - 0x26, 0xd5, 0xfd, 0xf7, 0xd7, 0x34, 0x4a, 0x35, 0x83, 0x28, 0xd8, 0xd2, 0x15, 0x6c, 0x9a, 0xd4, - 0x65, 0xce, 0x7e, 0xa6, 0x9c, 0x46, 0x35, 0xca, 0x11, 0x78, 0xbf, 0xc4, 0xea, 0xcd, 0x98, 0xb2, - 0xda, 0xd8, 0xde, 0x26, 0x6e, 0x8a, 0x11, 0xb5, 0x5b, 0xc4, 0x76, 0x52, 0x8c, 0x2c, 0x6c, 0xe3, - 0xb6, 0x6f, 0x34, 0x1d, 0x63, 0xe4, 0xee, 0x72, 0x03, 0xe9, 0x6b, 0x00, 0xf3, 0x4f, 0x3c, 0x1a, - 0xd6, 0xbd, 0xd8, 0xab, 0x84, 0xac, 0x60, 0xa3, 0xa9, 0x92, 0x67, 0x3b, 0xc4, 0x71, 0xd1, 0x43, - 0x38, 0x85, 0x9d, 0xed, 0x3a, 0x4b, 0x9b, 0x1f, 0x2d, 0x83, 0x99, 0x73, 0xd5, 0xb2, 0x1c, 0xcd, - 0xb9, 0xbc, 0xe4, 0x6c, 0xb3, 0x10, 0xea, 0x24, 0x16, 0xbf, 0x3c, 0xf7, 0x86, 0xde, 0x12, 0xee, - 0x63, 0xc9, 0xee, 0xcb, 0x7a, 0x4b, 0xb8, 0x37, 0xc4, 0x2f, 0xe9, 0x87, 0x51, 0x58, 0x88, 0x80, - 0xe6, 0x58, 0xd4, 0x74, 0x08, 0x7a, 0x02, 0x73, 0x4d, 0x9b, 0x30, 0xc6, 0xeb, 0x9b, 0x84, 0xd4, - 0xa9, 0xc5, 0xc8, 0xcf, 0x83, 0xf2, 0xd8, 0xcc, 0xb9, 0x6a, 0x41, 0x16, 0x5d, 0xf7, 0x7a, 0x27, - 0x8b, 0xde, 0xc9, 0x2b, 0x54, 0x37, 0x97, 0xcf, 0x1c, 0xfe, 0x39, 0x3d, 0xa2, 0x22, 0xdf, 0x79, - 0x95, 0x90, 0x75, 0xee, 0x8a, 0x3e, 0x82, 0x57, 0x1d, 0xe2, 0xba, 0x06, 0x69, 0x13, 0xd3, 0xad, - 0x6f, 0x1a, 0xd8, 0x0d, 0x45, 0x1e, 0xcd, 0x16, 0x39, 0xdf, 0x8b, 0xb1, 0x6a, 0x60, 0x37, 0x10, - 0xff, 0x63, 0x78, 0x2d, 0x10, 0xdf, 0xf6, 0xd2, 0x87, 0x12, 0x8c, 0x65, 0x4b, 0x50, 0xe8, 0x05, - 0x51, 0xbd, 0x18, 0xbd, 0x0c, 0x52, 0x05, 0xe6, 0x18, 0x63, 0x6b, 0xc4, 0xe5, 0x6c, 0x8a, 0x46, - 0x16, 0xe0, 0x24, 0xeb, 0x42, 0x5d, 0x6f, 0xe5, 0x41, 0x19, 0xcc, 0x9c, 0x51, 0xcf, 0xb2, 0xe7, - 0x47, 0x2d, 0xe9, 0x5d, 0x78, 0xb9, 0xcf, 0x45, 0x10, 0x5c, 0x83, 0xe3, 0xbc, 0x73, 0x80, 0x75, - 0xee, 0x7a, 0x5c, 0xe7, 0xb8, 0x17, 0xb7, 0x95, 0x7e, 0x01, 0xf0, 0xaa, 0x1f, 0xee, 0x31, 0xdb, - 0xd2, 0xec, 0xb5, 0xe3, 0x03, 0xb9, 0x0a, 0xa7, 0xf8, 0x4e, 0xf7, 0x91, 0x9c, 0x57, 0x27, 0xf9, - 0xc2, 0xa3, 0x16, 0xba, 0x0e, 0x21, 0x47, 0xe9, 0xee, 0x59, 0x84, 0xed, 0xb7, 0x29, 0x75, 0x8a, - 0xad, 0xbc, 0xbf, 0x67, 0x11, 0x74, 0x0b, 0x5e, 0xc0, 0x9b, 0x2e, 0xb1, 0xeb, 0xdd, 0x52, 0xc6, - 0x58, 0x29, 0x6f, 0xb0, 0xd5, 0x75, 0x5e, 0x0f, 0x5a, 0x85, 0xb0, 0x77, 0x90, 0xf3, 0x4d, 0x86, - 0xfd, 0x76, 0x88, 0x52, 0x3e, 0x41, 0x7c, 0x62, 0x37, 0xb0, 0x46, 0x04, 0x3a, 0x35, 0xe0, 0x29, - 0xbd, 0x00, 0xf0, 0x5a, 0x74, 0x25, 0x82, 0x9f, 0xfb, 0x70, 0x82, 0x9f, 0x47, 0xb1, 0xe5, 0x52, - 0x08, 0x12, 0xc6, 0x68, 0x2d, 0x02, 0xdf, 0x9d, 0x54, 0x7c, 0x3c, 0x67, 0x08, 0xe0, 0x1f, 0x00, - 0x16, 0xbb, 0x9d, 0x7b, 0x6e, 0x0a, 0x06, 0xba, 0x4c, 0xcb, 0x70, 0x9c, 0x7a, 0xab, 0x8c, 0xe5, - 0xa9, 0xe5, 0xfc, 0xaf, 0xaf, 0xe6, 0x72, 0x22, 0xcb, 0x52, 0xab, 0x65, 0x13, 0xc7, 0x79, 0xcf, - 0xb5, 0x75, 0x53, 0x53, 0xb9, 0xd9, 0xe9, 0x22, 0xff, 0xdb, 0xc0, 0x36, 0x0a, 0xd5, 0x76, 0x4a, - 0xb8, 0xff, 0x29, 0xc0, 0xfd, 0x92, 0xe3, 0xf4, 0xef, 0xf2, 0x1c, 0x1c, 0xc7, 0xde, 0x2a, 0xe7, - 0x5e, 0xe5, 0x0f, 0xa7, 0x97, 0xe1, 0x50, 0x05, 0xa7, 0x84, 0xe1, 0x86, 0xb8, 0x96, 0x3c, 0x78, - 0x86, 0x11, 0xa6, 0xf7, 0xa4, 0x38, 0xf8, 0x06, 0x88, 0x0b, 0x26, 0x9c, 0xe4, 0x94, 0x30, 0xb0, - 0xd0, 0x1b, 0xcc, 0x7c, 0xfe, 0x64, 0x99, 0xa1, 0xd2, 0x67, 0x00, 0x5e, 0xe9, 0x77, 0x13, 0x05, - 0x55, 0xe1, 0x59, 0xcc, 0x4f, 0x7e, 0xea, 0x4c, 0xf0, 0x0d, 0xd1, 0x22, 0x9c, 0xe0, 0xa1, 0xc5, - 0xf5, 0x5f, 0x8a, 0x23, 0x41, 0xe4, 0x12, 0xd6, 0x52, 0x33, 0xc4, 0x2c, 0x7f, 0x79, 0xe2, 0xfd, - 0x7b, 0x19, 0x3c, 0x85, 0x81, 0x2c, 0xa2, 0xde, 0x87, 0xf0, 0x2c, 0x47, 0xe3, 0x77, 0xf0, 0x66, - 0x32, 0xf8, 0x65, 0x5b, 0x27, 0x9b, 0xaa, 0xef, 0x73, 0x72, 0x8d, 0xcc, 0x41, 0xc4, 0x50, 0x6e, - 0x30, 0x61, 0x26, 0x0a, 0x91, 0x1e, 0xc3, 0x4b, 0xa1, 0x55, 0x01, 0x7a, 0x11, 0x4e, 0x70, 0x01, - 0x27, 0xae, 0xdd, 0x58, 0xc2, 0x85, 0x9f, 0xb0, 0x96, 0x3e, 0x07, 0xb0, 0xcc, 0xe2, 0x7d, 0x80, - 0x0d, 0xbd, 0x85, 0x5d, 0xb2, 0xe2, 0xe9, 0x1b, 0x12, 0xde, 0x39, 0x04, 0x5e, 0x66, 0xb2, 0x87, - 0xd4, 0xc5, 0x06, 0xb2, 0xf9, 0x0b, 0x91, 0xab, 0x12, 0xcb, 0x8f, 0xa3, 0xad, 0xd1, 0x4e, 0x44, - 0x44, 0xf5, 0x52, 0x73, 0x70, 0x51, 0xda, 0x84, 0x37, 0x12, 0xa0, 0x88, 0x42, 0x73, 0x70, 0x9c, - 0xd8, 0x36, 0xb5, 0xfd, 0x19, 0xc9, 0x1e, 0xd0, 0x5d, 0x88, 0x34, 0xda, 0xf1, 0x84, 0xbb, 0x55, - 0x7f, 0xae, 0x1b, 0x46, 0xdd, 0xc2, 0x8e, 0xc3, 0xf6, 0xde, 0xa4, 0xfa, 0xa6, 0x46, 0x3b, 0x1b, - 0x36, 0xb5, 0x3e, 0xd4, 0x0d, 0x63, 0x03, 0x3b, 0x8e, 0xf4, 0x40, 0xb4, 0xdf, 0xcf, 0x33, 0xc4, - 0x31, 0xa9, 0x89, 0xe9, 0xd7, 0xef, 0x9a, 0x04, 0x4e, 0xfa, 0x04, 0xc0, 0x52, 0x9f, 0x97, 0x89, - 0x35, 0xb2, 0x4a, 0x48, 0x77, 0x6b, 0xd7, 0xe1, 0xa5, 0x36, 0x5b, 0xf4, 0x84, 0x9d, 0xd3, 0xc7, - 0xaf, 0x92, 0xcc, 0xef, 0x40, 0x34, 0xf5, 0x62, 0xbb, 0x7f, 0x49, 0x6a, 0xc1, 0xe9, 0x58, 0x08, - 0x27, 0xc6, 0x6c, 0xf5, 0xc7, 0x8b, 0x70, 0x9c, 0xa5, 0x41, 0x2f, 0x01, 0xbc, 0x38, 0x20, 0xc2, - 0xd1, 0x7c, 0x5c, 0x25, 0x71, 0x9f, 0x12, 0xc5, 0xca, 0x10, 0x1e, 0xbc, 0x0e, 0x69, 0xf6, 0xd3, - 0xdf, 0xfe, 0xfe, 0x6a, 0xf4, 0x16, 0x92, 0x94, 0x98, 0x8f, 0x18, 0x8f, 0x62, 0xfe, 0x4d, 0x84, - 0x5e, 0x00, 0x78, 0x3e, 0x24, 0x63, 0xd1, 0xbd, 0xc4, 0x84, 0x7d, 0x02, 0xb9, 0x38, 0x97, 0xd1, - 0x5a, 0x40, 0x9b, 0x67, 0xd0, 0x66, 0xd1, 0x8c, 0x92, 0xf4, 0xa5, 0xa6, 0xec, 0xfb, 0x57, 0xf9, - 0x01, 0xfa, 0x07, 0xf4, 0xa4, 0x79, 0x50, 0x4e, 0xa2, 0x5a, 0x5a, 0xe6, 0x08, 0x19, 0x5d, 0x5c, - 0x18, 0xce, 0x49, 0xa0, 0x36, 0x19, 0xea, 0x2d, 0x54, 0x49, 0x44, 0xed, 0x88, 0x6f, 0x51, 0x65, - 0xbf, 0x7b, 0x7c, 0x0e, 0x9e, 0xd6, 0xe2, 0x9d, 0x06, 0xad, 0x45, 0x1c, 0xf4, 0x0a, 0x88, 0x19, - 0x17, 0x56, 0x71, 0xa8, 0x9a, 0x4a, 0xf6, 0x80, 0x9c, 0x2d, 0xd6, 0x86, 0xf2, 0x11, 0x05, 0x2f, - 0xb0, 0x82, 0x65, 0x74, 0x2f, 0xa5, 0x60, 0xa6, 0x80, 0x95, 0x7d, 0xf6, 0xe7, 0x20, 0x04, 0x3b, - 0x20, 0x8d, 0xd2, 0x61, 0x0f, 0x2a, 0xc1, 0x74, 0xd8, 0x11, 0xda, 0x2b, 0x33, 0x6c, 0x26, 0x2b, - 0x95, 0x7d, 0xf6, 0xe7, 0x00, 0x7d, 0xe7, 0x9f, 0xd4, 0xa0, 0x9a, 0x49, 0x39, 0xa9, 0x11, 0xea, - 0x2a, 0xe5, 0xa4, 0x46, 0x49, 0x25, 0xe9, 0x36, 0x03, 0x5c, 0x46, 0xa5, 0x64, 0xc0, 0xe8, 0x7b, - 0x00, 0x2f, 0x84, 0x77, 0x28, 0x9a, 0xcb, 0xb6, 0x93, 0x7d, 0x70, 0x72, 0x56, 0x73, 0x81, 0xac, - 0xca, 0x90, 0xdd, 0x43, 0xb3, 0xd9, 0x77, 0xaf, 0x37, 0xf2, 0xd0, 0xa0, 0xac, 0x40, 0x59, 0x78, - 0x09, 0x0b, 0x9d, 0x62, 0x75, 0x18, 0x17, 0x81, 0xf8, 0x0e, 0x43, 0x7c, 0x03, 0x4d, 0x27, 0x23, - 0x76, 0xd0, 0x17, 0x00, 0x9e, 0x0b, 0x28, 0x08, 0x34, 0x9b, 0x98, 0x2c, 0x24, 0x3e, 0x8a, 0x77, - 0x33, 0xd9, 0x66, 0xed, 0x2e, 0x97, 0x20, 0xe8, 0xd0, 0x97, 0xd3, 0x51, 0xf7, 0x3e, 0x7a, 0x27, - 0x31, 0x65, 0x82, 0x6a, 0x29, 0x3e, 0xf8, 0x1f, 0x9e, 0x02, 0xfa, 0x22, 0x83, 0x3e, 0x8f, 0xe4, - 0x38, 0xe8, 0x1d, 0xe1, 0xad, 0x84, 0x74, 0x11, 0xfa, 0xd7, 0x1f, 0x01, 0x61, 0x7d, 0x90, 0x32, - 0x02, 0x22, 0x75, 0x48, 0xca, 0x08, 0x88, 0x16, 0x20, 0x92, 0xcd, 0x80, 0x1b, 0xa8, 0x96, 0x0a, - 0x3c, 0x62, 0x58, 0xdf, 0x8f, 0x77, 0x8b, 0x18, 0xd6, 0x7e, 0x24, 0xf4, 0x33, 0x80, 0x6f, 0xc5, - 0x68, 0x0b, 0xb4, 0x98, 0xb1, 0x88, 0x3e, 0xb9, 0x52, 0x7c, 0x7b, 0x68, 0xbf, 0xac, 0x33, 0x30, - 0x40, 0x40, 0x57, 0x6f, 0x2d, 0x93, 0xc3, 0xa3, 0x12, 0x78, 0x7d, 0x54, 0x02, 0x7f, 0x1d, 0x95, - 0xc0, 0x97, 0xc7, 0xa5, 0x91, 0xd7, 0xc7, 0xa5, 0x91, 0xdf, 0x8f, 0x4b, 0x23, 0xb0, 0xa0, 0xd3, - 0x18, 0x28, 0x1b, 0xe0, 0xa9, 0xac, 0xe9, 0xee, 0xd6, 0x4e, 0x43, 0x6e, 0xd2, 0x76, 0x20, 0xdd, - 0x9c, 0x4e, 0x83, 0xc9, 0x77, 0xbb, 0xe9, 0x1b, 0x13, 0xec, 0x7f, 0xa7, 0xb5, 0xff, 0x02, 0x00, - 0x00, 0xff, 0xff, 0x14, 0xc9, 0x3c, 0x3d, 0xa4, 0x16, 0x00, 0x00, + // 1480 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4d, 0x6f, 0x13, 0xc7, + 0x1b, 0xcf, 0x24, 0x24, 0x24, 0x93, 0x3f, 0xfc, 0xc5, 0xc4, 0x50, 0xc7, 0x80, 0x13, 0x16, 0x04, + 0x51, 0x20, 0xbb, 0xd8, 0x86, 0xb4, 0x1c, 0x68, 0x4b, 0x68, 0x83, 0x22, 0x15, 0x11, 0xb6, 0x55, + 0x5b, 0x71, 0xa8, 0x19, 0xdb, 0x93, 0x65, 0xc5, 0x7a, 0x67, 0xd9, 0x59, 0x4c, 0xa2, 0x28, 0x87, + 0xbe, 0x5c, 0xda, 0x43, 0x55, 0xa9, 0x97, 0x4a, 0x15, 0xa8, 0x52, 0xf9, 0x00, 0x3d, 0x70, 0x6f, + 0x6f, 0xe5, 0x88, 0x5a, 0x55, 0x6a, 0x2f, 0x55, 0x05, 0xfd, 0x02, 0xed, 0x27, 0xa8, 0x76, 0x66, + 0xd6, 0xde, 0xb5, 0xf7, 0xcd, 0x6d, 0x0e, 0x39, 0xc5, 0x3b, 0xfb, 0xbc, 0xfc, 0x9e, 0xdf, 0x33, + 0xfb, 0xcc, 0x6f, 0x02, 0x15, 0xc7, 0xa5, 0x1d, 0x62, 0x63, 0xbb, 0x49, 0x34, 0xb2, 0xd9, 0xbc, + 0x83, 0x6d, 0x83, 0x68, 0x9d, 0x8a, 0x76, 0xef, 0x3e, 0x71, 0xb7, 0x54, 0xc7, 0xa5, 0x1e, 0x45, + 0x47, 0x7a, 0x36, 0x6a, 0x60, 0xa3, 0x76, 0x2a, 0xa5, 0xd9, 0x26, 0x65, 0x6d, 0xca, 0xea, 0xdc, + 0x4a, 0x13, 0x0f, 0xc2, 0xa5, 0xb4, 0x28, 0x9e, 0xb4, 0x06, 0x66, 0x44, 0xc4, 0xd2, 0x3a, 0x95, + 0x06, 0xf1, 0x70, 0x45, 0x73, 0xb0, 0x61, 0xda, 0xd8, 0x33, 0xa9, 0x2d, 0x6d, 0xcb, 0x61, 0xdb, + 0xc0, 0xaa, 0x49, 0xcd, 0xe0, 0xfd, 0x31, 0x83, 0x52, 0xc3, 0x22, 0x1a, 0x76, 0x4c, 0x0d, 0xdb, + 0x36, 0xf5, 0xb8, 0x73, 0x90, 0xa9, 0x60, 0x50, 0x83, 0x0a, 0x04, 0xfe, 0x2f, 0xb9, 0x7a, 0x32, + 0xa1, 0xac, 0x36, 0x76, 0xef, 0x12, 0x2f, 0xc3, 0x88, 0xba, 0x2d, 0xe2, 0xb2, 0x0c, 0x23, 0x07, + 0xbb, 0xb8, 0x1d, 0x18, 0xcd, 0x25, 0x18, 0x79, 0x9b, 0xc2, 0x40, 0xf9, 0x0a, 0xc0, 0xe2, 0x4d, + 0x9f, 0x86, 0x1b, 0x7e, 0xec, 0x55, 0x42, 0xae, 0x62, 0xab, 0xa9, 0x93, 0x7b, 0xf7, 0x09, 0xf3, + 0xd0, 0x65, 0x38, 0x85, 0xd9, 0xdd, 0x3a, 0x4f, 0x5b, 0x1c, 0x9d, 0x07, 0x0b, 0xd3, 0xd5, 0x79, + 0x35, 0x9e, 0x73, 0xf5, 0x0a, 0xbb, 0xcb, 0x43, 0xe8, 0x93, 0x58, 0xfe, 0xf2, 0xdd, 0x1b, 0x66, + 0x4b, 0xba, 0x8f, 0xa5, 0xbb, 0xaf, 0x98, 0x2d, 0xe9, 0xde, 0x90, 0xbf, 0x94, 0xef, 0x46, 0xe1, + 0x6c, 0x0c, 0x34, 0xe6, 0x50, 0x9b, 0x11, 0x74, 0x13, 0x16, 0x9a, 0x2e, 0xe1, 0x8c, 0xd7, 0x37, + 0x08, 0xa9, 0x53, 0x87, 0x93, 0x5f, 0x04, 0xf3, 0x63, 0x0b, 0xd3, 0xd5, 0x59, 0x55, 0x76, 0xdd, + 0xef, 0x9d, 0x2a, 0x7b, 0xa7, 0x5e, 0xa5, 0xa6, 0xbd, 0xb2, 0xef, 0xe9, 0xef, 0x73, 0x23, 0x3a, + 0x0a, 0x9c, 0x57, 0x09, 0xb9, 0x21, 0x5c, 0xd1, 0x07, 0xf0, 0x28, 0x23, 0x9e, 0x67, 0x91, 0x36, + 0xb1, 0xbd, 0xfa, 0x86, 0x85, 0xbd, 0x48, 0xe4, 0xd1, 0x7c, 0x91, 0x8b, 0xbd, 0x18, 0xab, 0x16, + 0xf6, 0x42, 0xf1, 0x6f, 0xc3, 0x63, 0xa1, 0xf8, 0xae, 0x9f, 0x3e, 0x92, 0x60, 0x2c, 0x5f, 0x82, + 0xd9, 0x5e, 0x10, 0xdd, 0x8f, 0xd1, 0xcb, 0xa0, 0x54, 0x60, 0x81, 0x33, 0x76, 0x8d, 0x78, 0x82, + 0x4d, 0xd9, 0xc8, 0x59, 0x38, 0xc9, 0xbb, 0x50, 0x37, 0x5b, 0x45, 0x30, 0x0f, 0x16, 0xf6, 0xe9, + 0xfb, 0xf9, 0xf3, 0x5a, 0x4b, 0x79, 0x0b, 0x1e, 0xee, 0x73, 0x91, 0x04, 0xd7, 0xe0, 0xb8, 0xe8, + 0x1c, 0xe0, 0x9d, 0x3b, 0x9e, 0xd4, 0x39, 0xe1, 0x25, 0x6c, 0x95, 0xdb, 0x70, 0x3e, 0x12, 0x6d, + 0x65, 0xeb, 0xcd, 0x4d, 0x8f, 0xb8, 0x36, 0xb6, 0xd6, 0xde, 0x08, 0xc0, 0x1c, 0x85, 0x53, 0x62, + 0xb7, 0x07, 0x68, 0x0e, 0xe8, 0x93, 0x62, 0x61, 0xad, 0x85, 0xe6, 0xe0, 0x34, 0x91, 0x1e, 0xfe, + 0x6b, 0x7f, 0xd3, 0x4d, 0xe9, 0x30, 0x58, 0x5a, 0x6b, 0x29, 0xef, 0xc3, 0x13, 0x29, 0x19, 0xfe, + 0x0b, 0xf6, 0x1f, 0x01, 0x3c, 0x1a, 0x84, 0xbe, 0xce, 0xf1, 0xf0, 0xd7, 0x2c, 0x17, 0xee, 0xe3, + 0x10, 0x0a, 0x86, 0xbd, 0x2d, 0x87, 0x48, 0xd8, 0x53, 0x7c, 0xe5, 0x9d, 0x2d, 0x87, 0xa0, 0x53, + 0xf0, 0x20, 0xde, 0xf0, 0x88, 0x5b, 0xef, 0xb6, 0x61, 0x8c, 0xb7, 0xe1, 0x7f, 0x7c, 0xf5, 0x86, + 0xe8, 0x05, 0x5a, 0x85, 0xb0, 0x37, 0x84, 0x8a, 0x4d, 0x8e, 0xfd, 0x74, 0x64, 0x3b, 0x88, 0xe9, + 0x17, 0x6c, 0x8a, 0x75, 0x6c, 0x10, 0x89, 0x4e, 0x0f, 0x79, 0x2a, 0x8f, 0x00, 0x3c, 0x16, 0x5f, + 0x89, 0xe4, 0xe7, 0x22, 0x9c, 0x10, 0xb3, 0x44, 0x7e, 0x2e, 0x19, 0x04, 0x49, 0x63, 0x74, 0x2d, + 0x06, 0xdf, 0x99, 0x4c, 0x7c, 0x22, 0x67, 0x04, 0xe0, 0x6f, 0x00, 0x96, 0xba, 0x5d, 0x7c, 0x60, + 0x4b, 0x06, 0xba, 0x4c, 0xab, 0x70, 0x9c, 0xfa, 0xab, 0x9c, 0xe5, 0xa9, 0x95, 0xe2, 0x4f, 0x4f, + 0x96, 0x0a, 0x32, 0xcb, 0x95, 0x56, 0xcb, 0x25, 0x8c, 0xbd, 0xed, 0xb9, 0xa6, 0x6d, 0xe8, 0xc2, + 0x6c, 0x6f, 0x91, 0xff, 0x30, 0xb4, 0x8d, 0x22, 0xb5, 0xed, 0x11, 0xee, 0xbf, 0x0f, 0x71, 0x7f, + 0x85, 0xb1, 0xfe, 0x5d, 0x5e, 0x80, 0xe3, 0xd8, 0x5f, 0x15, 0xdc, 0xeb, 0xe2, 0x61, 0xef, 0x32, + 0x1c, 0xa9, 0x60, 0x8f, 0x30, 0xdc, 0x90, 0x47, 0xaa, 0x0f, 0xcf, 0xb2, 0xa2, 0xf4, 0xee, 0x16, + 0x07, 0x5f, 0x03, 0x79, 0x38, 0x46, 0x93, 0xec, 0x11, 0x06, 0x2e, 0xf4, 0x0e, 0x15, 0x31, 0x7f, + 0xf2, 0xcc, 0x50, 0xe5, 0x13, 0x00, 0x8f, 0xf4, 0xbb, 0xc9, 0x82, 0xaa, 0x70, 0x3f, 0x16, 0x5f, + 0x7e, 0xe6, 0x4c, 0x08, 0x0c, 0xd1, 0x32, 0x9c, 0x10, 0xa1, 0xa5, 0x74, 0x29, 0x27, 0x91, 0x20, + 0x73, 0x49, 0x6b, 0xa5, 0x19, 0x61, 0x56, 0xbc, 0xdc, 0xf5, 0xfe, 0x3d, 0x0e, 0x7f, 0x85, 0xa1, + 0x2c, 0xb2, 0xde, 0xcb, 0x70, 0xbf, 0x40, 0x13, 0x74, 0xf0, 0x64, 0x3a, 0xf8, 0x15, 0xd7, 0x24, + 0x1b, 0x7a, 0xe0, 0xb3, 0x7b, 0x8d, 0x2c, 0x40, 0xc4, 0x51, 0xae, 0x73, 0x51, 0x29, 0x0b, 0x51, + 0xae, 0xc3, 0x99, 0xc8, 0xaa, 0x04, 0xbd, 0x0c, 0x27, 0x84, 0xf8, 0x94, 0xc7, 0x6e, 0x22, 0xe1, + 0xd2, 0x4f, 0x5a, 0x2b, 0x9f, 0x02, 0xa9, 0x1a, 0xde, 0xc5, 0x96, 0xd9, 0xc2, 0x1e, 0xb9, 0xea, + 0x6b, 0x33, 0x12, 0xdd, 0x39, 0x04, 0x1e, 0xe6, 0x92, 0x8d, 0xd4, 0xe5, 0x06, 0x72, 0xc5, 0x0b, + 0x99, 0xab, 0x92, 0xc8, 0x0f, 0x33, 0xae, 0xd1, 0x4e, 0x4c, 0x44, 0x7d, 0xa6, 0x39, 0xb8, 0xa8, + 0x6c, 0x48, 0x79, 0x11, 0x0f, 0x45, 0x16, 0x5a, 0x80, 0xe3, 0xc4, 0x75, 0xa9, 0x1b, 0xcc, 0x48, + 0xfe, 0x80, 0xce, 0x42, 0x64, 0xd0, 0x8e, 0x7f, 0xe9, 0x70, 0xea, 0x0f, 0x4c, 0xcb, 0xaa, 0x3b, + 0x98, 0x31, 0xbe, 0xf7, 0x26, 0xf5, 0xff, 0x1b, 0xb4, 0xb3, 0xee, 0x52, 0xe7, 0x3d, 0xd3, 0xb2, + 0xd6, 0x31, 0x63, 0xca, 0x25, 0xd9, 0xfe, 0x20, 0xcf, 0x10, 0x9f, 0x49, 0x4d, 0x4e, 0xbf, 0x7e, + 0xd7, 0x34, 0x70, 0xca, 0x87, 0x00, 0x96, 0xfb, 0xbc, 0x6c, 0x6c, 0x90, 0x55, 0x42, 0xba, 0x5b, + 0xbb, 0x0e, 0x67, 0xda, 0x7c, 0xd1, 0x17, 0xa5, 0xac, 0x8f, 0x5f, 0x2d, 0x9d, 0xdf, 0x81, 0x68, + 0xfa, 0xa1, 0x76, 0xff, 0x92, 0xd2, 0x82, 0x73, 0x89, 0x10, 0x76, 0x8d, 0xd9, 0xea, 0x2f, 0x33, + 0x70, 0x9c, 0xa7, 0x41, 0x8f, 0x01, 0x3c, 0x34, 0x70, 0x81, 0x40, 0xe7, 0x93, 0x2a, 0x49, 0xba, + 0x06, 0x95, 0x2a, 0x43, 0x78, 0x88, 0x3a, 0x94, 0xc5, 0x8f, 0x7e, 0xfe, 0xf3, 0xcb, 0xd1, 0x53, + 0x48, 0xd1, 0x12, 0x2e, 0x60, 0x3e, 0xc5, 0xe2, 0x3e, 0x87, 0x1e, 0x01, 0x78, 0x20, 0x22, 0x69, + 0xd1, 0xb9, 0xd4, 0x84, 0x7d, 0xe2, 0xbe, 0xb4, 0x94, 0xd3, 0x5a, 0x42, 0x3b, 0xcf, 0xa1, 0x2d, + 0xa2, 0x05, 0x2d, 0xed, 0x96, 0xa9, 0x6d, 0x07, 0x47, 0xf9, 0x0e, 0x7a, 0x38, 0xda, 0x9b, 0x88, + 0x03, 0x9a, 0x1b, 0xbd, 0x92, 0x2b, 0x7d, 0xcc, 0x45, 0xa0, 0x74, 0xe9, 0x5f, 0x78, 0xca, 0x22, + 0x3e, 0x03, 0xbc, 0x8a, 0x8f, 0x01, 0x7a, 0x2d, 0xb5, 0x0c, 0x26, 0x2f, 0xd6, 0xda, 0x76, 0xf7, + 0x7b, 0xda, 0xd1, 0xb6, 0x43, 0x37, 0x8c, 0x9d, 0x5b, 0xaf, 0xa3, 0x57, 0xb5, 0xd4, 0x4b, 0x79, + 0xc4, 0x57, 0x92, 0x13, 0x8e, 0x80, 0xfe, 0x02, 0xbd, 0x6b, 0x57, 0x58, 0x6e, 0xa3, 0x5a, 0x56, + 0x81, 0x31, 0xd7, 0x8c, 0xd2, 0x85, 0xe1, 0x9c, 0x24, 0x21, 0x36, 0xe7, 0xe3, 0x0e, 0xaa, 0x0c, + 0x4d, 0xc7, 0xad, 0x5a, 0xb2, 0x53, 0x12, 0x01, 0x0c, 0x3d, 0x01, 0xf2, 0x0c, 0x88, 0xaa, 0x5c, + 0x54, 0xcd, 0xec, 0xe9, 0x80, 0xdc, 0x2f, 0xd5, 0x86, 0xf2, 0x91, 0x05, 0x5f, 0xe0, 0x05, 0xab, + 0xe8, 0x5c, 0x46, 0xc1, 0xfc, 0x86, 0xa0, 0x6d, 0xf3, 0x3f, 0x3b, 0x11, 0xd8, 0x21, 0xe9, 0x98, + 0x0d, 0x7b, 0x50, 0x29, 0x67, 0xc3, 0x8e, 0xd1, 0xa6, 0xb9, 0x61, 0x73, 0xd9, 0xad, 0x6d, 0xf3, + 0x3f, 0x3b, 0xe8, 0x9b, 0x60, 0x92, 0x85, 0xd5, 0x5e, 0xc6, 0x24, 0x8b, 0x51, 0x9f, 0x19, 0x93, + 0x2c, 0x4e, 0x4a, 0x2a, 0xa7, 0x39, 0xe0, 0x79, 0x54, 0x4e, 0x07, 0x8c, 0xbe, 0x05, 0xf0, 0x60, + 0x74, 0x87, 0xa2, 0xa5, 0x7c, 0x3b, 0x39, 0x00, 0xa7, 0xe6, 0x35, 0x97, 0xc8, 0xaa, 0x1c, 0xd9, + 0x39, 0xb4, 0x98, 0x7f, 0xf7, 0xfa, 0x47, 0x02, 0x1a, 0x94, 0x5d, 0x28, 0x0f, 0x2f, 0x51, 0x21, + 0x58, 0xaa, 0x0e, 0xe3, 0x22, 0x11, 0x9f, 0xe1, 0x88, 0x4f, 0xa0, 0xb9, 0x74, 0xc4, 0x0c, 0x7d, + 0x0e, 0xe0, 0x74, 0x48, 0x61, 0xa1, 0xc5, 0xd4, 0x64, 0x11, 0x71, 0x56, 0x3a, 0x9b, 0xcb, 0x36, + 0x6f, 0x77, 0x85, 0x44, 0x43, 0x4f, 0x83, 0xeb, 0x46, 0x9c, 0x2e, 0xca, 0x38, 0x02, 0x52, 0x54, + 0x5d, 0xc6, 0x11, 0x90, 0x26, 0xc2, 0x94, 0x65, 0x0e, 0xfd, 0x3c, 0x52, 0x93, 0xa0, 0x77, 0xa4, + 0xb7, 0x16, 0xd1, 0x8d, 0xe8, 0xef, 0x60, 0x04, 0x44, 0xf5, 0x53, 0xc6, 0x08, 0x88, 0xd5, 0x69, + 0x19, 0x23, 0x20, 0x5e, 0xa0, 0x29, 0x2e, 0x07, 0x6e, 0xa1, 0x5a, 0x26, 0xf0, 0x98, 0x61, 0x7d, + 0x31, 0xd9, 0x2d, 0x66, 0x58, 0x07, 0x91, 0xd0, 0x0f, 0x00, 0xbe, 0x94, 0xa0, 0xbd, 0xd0, 0x72, + 0xce, 0x22, 0xfa, 0xe4, 0x5c, 0xe9, 0xe5, 0xa1, 0xfd, 0xf2, 0xce, 0xc0, 0x10, 0x01, 0x5d, 0x3d, + 0xba, 0x42, 0x9e, 0x3e, 0x2f, 0x83, 0x67, 0xcf, 0xcb, 0xe0, 0x8f, 0xe7, 0x65, 0xf0, 0xc5, 0x8b, + 0xf2, 0xc8, 0xb3, 0x17, 0xe5, 0x91, 0x5f, 0x5f, 0x94, 0x47, 0xe0, 0xac, 0x49, 0x13, 0xa0, 0xac, + 0x83, 0x5b, 0xaa, 0x61, 0x7a, 0x77, 0xee, 0x37, 0xd4, 0x26, 0x6d, 0x87, 0xd2, 0x2d, 0x99, 0x34, + 0x9c, 0x7c, 0xb3, 0x9b, 0xbe, 0x31, 0xc1, 0xff, 0x2f, 0x5e, 0xfb, 0x27, 0x00, 0x00, 0xff, 0xff, + 0x3b, 0xd0, 0x8c, 0xac, 0x80, 0x18, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1465,6 +1574,8 @@ type QueryClient interface { QueryOrderFeeCalc(ctx context.Context, in *QueryOrderFeeCalcRequest, opts ...grpc.CallOption) (*QueryOrderFeeCalcResponse, error) // QueryGetOrder looks up an order by id. QueryGetOrder(ctx context.Context, in *QueryGetOrderRequest, opts ...grpc.CallOption) (*QueryGetOrderResponse, error) + // QueryGetOrderByExternalID looks up an order by market id and external id. + QueryGetOrderByExternalID(ctx context.Context, in *QueryGetOrderByExternalIDRequest, opts ...grpc.CallOption) (*QueryGetOrderByExternalIDResponse, error) // QueryGetMarketOrders looks up the orders in a market. QueryGetMarketOrders(ctx context.Context, in *QueryGetMarketOrdersRequest, opts ...grpc.CallOption) (*QueryGetMarketOrdersResponse, error) // QueryGetOwnerOrders looks up the orders from the provided owner address. @@ -1513,6 +1624,15 @@ func (c *queryClient) QueryGetOrder(ctx context.Context, in *QueryGetOrderReques return out, nil } +func (c *queryClient) QueryGetOrderByExternalID(ctx context.Context, in *QueryGetOrderByExternalIDRequest, opts ...grpc.CallOption) (*QueryGetOrderByExternalIDResponse, error) { + out := new(QueryGetOrderByExternalIDResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetOrderByExternalID", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *queryClient) QueryGetMarketOrders(ctx context.Context, in *QueryGetMarketOrdersRequest, opts ...grpc.CallOption) (*QueryGetMarketOrdersResponse, error) { out := new(QueryGetMarketOrdersResponse) err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetMarketOrders", in, out, opts...) @@ -1609,6 +1729,8 @@ type QueryServer interface { QueryOrderFeeCalc(context.Context, *QueryOrderFeeCalcRequest) (*QueryOrderFeeCalcResponse, error) // QueryGetOrder looks up an order by id. QueryGetOrder(context.Context, *QueryGetOrderRequest) (*QueryGetOrderResponse, error) + // QueryGetOrderByExternalID looks up an order by market id and external id. + QueryGetOrderByExternalID(context.Context, *QueryGetOrderByExternalIDRequest) (*QueryGetOrderByExternalIDResponse, error) // QueryGetMarketOrders looks up the orders in a market. QueryGetMarketOrders(context.Context, *QueryGetMarketOrdersRequest) (*QueryGetMarketOrdersResponse, error) // QueryGetOwnerOrders looks up the orders from the provided owner address. @@ -1641,6 +1763,9 @@ func (*UnimplementedQueryServer) QueryOrderFeeCalc(ctx context.Context, req *Que func (*UnimplementedQueryServer) QueryGetOrder(ctx context.Context, req *QueryGetOrderRequest) (*QueryGetOrderResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryGetOrder not implemented") } +func (*UnimplementedQueryServer) QueryGetOrderByExternalID(ctx context.Context, req *QueryGetOrderByExternalIDRequest) (*QueryGetOrderByExternalIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryGetOrderByExternalID not implemented") +} func (*UnimplementedQueryServer) QueryGetMarketOrders(ctx context.Context, req *QueryGetMarketOrdersRequest) (*QueryGetMarketOrdersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryGetMarketOrders not implemented") } @@ -1712,6 +1837,24 @@ func _Query_QueryGetOrder_Handler(srv interface{}, ctx context.Context, dec func return interceptor(ctx, in, info, handler) } +func _Query_QueryGetOrderByExternalID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryGetOrderByExternalIDRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryGetOrderByExternalID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Query/QueryGetOrderByExternalID", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryGetOrderByExternalID(ctx, req.(*QueryGetOrderByExternalIDRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Query_QueryGetMarketOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryGetMarketOrdersRequest) if err := dec(in); err != nil { @@ -1904,6 +2047,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "QueryGetOrder", Handler: _Query_QueryGetOrder_Handler, }, + { + MethodName: "QueryGetOrderByExternalID", + Handler: _Query_QueryGetOrderByExternalID_Handler, + }, { MethodName: "QueryGetMarketOrders", Handler: _Query_QueryGetMarketOrders_Handler, @@ -2124,6 +2271,76 @@ func (m *QueryGetOrderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *QueryGetOrderByExternalIDRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetOrderByExternalIDRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetOrderByExternalIDRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ExternalId) > 0 { + i -= len(m.ExternalId) + copy(dAtA[i:], m.ExternalId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ExternalId))) + i-- + dAtA[i] = 0x12 + } + if m.MarketId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryGetOrderByExternalIDResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetOrderByExternalIDResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetOrderByExternalIDResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Order != nil { + { + size, err := m.Order.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *QueryGetMarketOrdersRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -3035,6 +3252,35 @@ func (m *QueryGetOrderResponse) Size() (n int) { return n } +func (m *QueryGetOrderByExternalIDRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MarketId != 0 { + n += 1 + sovQuery(uint64(m.MarketId)) + } + l = len(m.ExternalId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryGetOrderByExternalIDResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Order != nil { + l = m.Order.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + func (m *QueryGetMarketOrdersRequest) Size() (n int) { if m == nil { return 0 @@ -3796,6 +4042,193 @@ func (m *QueryGetOrderResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryGetOrderByExternalIDRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetOrderByExternalIDRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetOrderByExternalIDRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExternalId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetOrderByExternalIDResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetOrderByExternalIDResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetOrderByExternalIDResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Order", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Order == nil { + m.Order = &Order{} + } + if err := m.Order.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *QueryGetMarketOrdersRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/exchange/query.pb.gw.go b/x/exchange/query.pb.gw.go index 33f2a941e8..7eacc45cdf 100644 --- a/x/exchange/query.pb.gw.go +++ b/x/exchange/query.pb.gw.go @@ -121,6 +121,158 @@ func local_request_Query_QueryGetOrder_0(ctx context.Context, marshaler runtime. } +func request_Query_QueryGetOrderByExternalID_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetOrderByExternalIDRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + val, ok = pathParams["external_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "external_id") + } + + protoReq.ExternalId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "external_id", err) + } + + msg, err := client.QueryGetOrderByExternalID(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryGetOrderByExternalID_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetOrderByExternalIDRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + val, ok = pathParams["external_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "external_id") + } + + protoReq.ExternalId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "external_id", err) + } + + msg, err := server.QueryGetOrderByExternalID(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_QueryGetOrderByExternalID_1(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetOrderByExternalIDRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + val, ok = pathParams["external_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "external_id") + } + + protoReq.ExternalId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "external_id", err) + } + + msg, err := client.QueryGetOrderByExternalID(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryGetOrderByExternalID_1(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetOrderByExternalIDRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["market_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "market_id") + } + + protoReq.MarketId, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) + } + + val, ok = pathParams["external_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "external_id") + } + + protoReq.ExternalId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "external_id", err) + } + + msg, err := server.QueryGetOrderByExternalID(ctx, &protoReq) + return msg, metadata, err + +} + var ( filter_Query_QueryGetMarketOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{"market_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} ) @@ -779,6 +931,46 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_QueryGetOrderByExternalID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryGetOrderByExternalID_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetOrderByExternalID_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetOrderByExternalID_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryGetOrderByExternalID_1(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetOrderByExternalID_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_QueryGetMarketOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1100,6 +1292,46 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_QueryGetOrderByExternalID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryGetOrderByExternalID_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetOrderByExternalID_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryGetOrderByExternalID_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryGetOrderByExternalID_1(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryGetOrderByExternalID_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_QueryGetMarketOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1348,6 +1580,10 @@ var ( pattern_Query_QueryGetOrder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "order", "order_id"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryGetOrderByExternalID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 1, 0, 4, 1, 5, 6}, []string{"provenance", "exchange", "v1", "orders", "market", "market_id", "external_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryGetOrderByExternalID_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"provenance", "exchange", "v1", "market", "market_id", "order", "external_id"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryGetMarketOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"provenance", "exchange", "v1", "orders", "market", "market_id"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryGetMarketOrders_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"provenance", "exchange", "v1", "market", "market_id", "orders"}, "", runtime.AssumeColonVerbOpt(false))) @@ -1378,6 +1614,10 @@ var ( forward_Query_QueryGetOrder_0 = runtime.ForwardResponseMessage + forward_Query_QueryGetOrderByExternalID_0 = runtime.ForwardResponseMessage + + forward_Query_QueryGetOrderByExternalID_1 = runtime.ForwardResponseMessage + forward_Query_QueryGetMarketOrders_0 = runtime.ForwardResponseMessage forward_Query_QueryGetMarketOrders_1 = runtime.ForwardResponseMessage diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 7d02d6f000..6c9f57c84d 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -712,6 +712,117 @@ func (m *MsgMarketSettleResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgMarketSettleResponse proto.InternalMessageInfo +// MsgMarketSetOrderExternalIDRequest is a request message for the MarketSetOrderExternalID endpoint. +type MsgMarketSetOrderExternalIDRequest struct { + // admin is the account with "set_ids" permission requesting this settlement. + Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty"` + // market_id is the numerical identifier of the market to update required attributes for. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` + // order_id is the numerical identifier of the order to update. + OrderId uint64 `protobuf:"varint,3,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + // external_id is the new external id to associate with the order. Max length is 40 characters. + // If the external id is already associated with another order in this market, this update will fail. + ExternalId string `protobuf:"bytes,4,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` +} + +func (m *MsgMarketSetOrderExternalIDRequest) Reset() { *m = MsgMarketSetOrderExternalIDRequest{} } +func (m *MsgMarketSetOrderExternalIDRequest) String() string { return proto.CompactTextString(m) } +func (*MsgMarketSetOrderExternalIDRequest) ProtoMessage() {} +func (*MsgMarketSetOrderExternalIDRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{12} +} +func (m *MsgMarketSetOrderExternalIDRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketSetOrderExternalIDRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketSetOrderExternalIDRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketSetOrderExternalIDRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketSetOrderExternalIDRequest.Merge(m, src) +} +func (m *MsgMarketSetOrderExternalIDRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketSetOrderExternalIDRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketSetOrderExternalIDRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketSetOrderExternalIDRequest proto.InternalMessageInfo + +func (m *MsgMarketSetOrderExternalIDRequest) GetAdmin() string { + if m != nil { + return m.Admin + } + return "" +} + +func (m *MsgMarketSetOrderExternalIDRequest) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + +func (m *MsgMarketSetOrderExternalIDRequest) GetOrderId() uint64 { + if m != nil { + return m.OrderId + } + return 0 +} + +func (m *MsgMarketSetOrderExternalIDRequest) GetExternalId() string { + if m != nil { + return m.ExternalId + } + return "" +} + +// MsgMarketSetOrderExternalIDResponse is a response message for the MarketSetOrderExternalID endpoint. +type MsgMarketSetOrderExternalIDResponse struct { +} + +func (m *MsgMarketSetOrderExternalIDResponse) Reset() { *m = MsgMarketSetOrderExternalIDResponse{} } +func (m *MsgMarketSetOrderExternalIDResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMarketSetOrderExternalIDResponse) ProtoMessage() {} +func (*MsgMarketSetOrderExternalIDResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e333fcffc093bd1b, []int{13} +} +func (m *MsgMarketSetOrderExternalIDResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMarketSetOrderExternalIDResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMarketSetOrderExternalIDResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMarketSetOrderExternalIDResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMarketSetOrderExternalIDResponse.Merge(m, src) +} +func (m *MsgMarketSetOrderExternalIDResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMarketSetOrderExternalIDResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMarketSetOrderExternalIDResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMarketSetOrderExternalIDResponse proto.InternalMessageInfo + // MsgMarketWithdrawRequest is a request message for the MarketWithdraw endpoint. type MsgMarketWithdrawRequest struct { // admin is the account with withdraw permission requesting the withdrawal. @@ -728,7 +839,7 @@ func (m *MsgMarketWithdrawRequest) Reset() { *m = MsgMarketWithdrawReque func (m *MsgMarketWithdrawRequest) String() string { return proto.CompactTextString(m) } func (*MsgMarketWithdrawRequest) ProtoMessage() {} func (*MsgMarketWithdrawRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{12} + return fileDescriptor_e333fcffc093bd1b, []int{14} } func (m *MsgMarketWithdrawRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -793,7 +904,7 @@ func (m *MsgMarketWithdrawResponse) Reset() { *m = MsgMarketWithdrawResp func (m *MsgMarketWithdrawResponse) String() string { return proto.CompactTextString(m) } func (*MsgMarketWithdrawResponse) ProtoMessage() {} func (*MsgMarketWithdrawResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{13} + return fileDescriptor_e333fcffc093bd1b, []int{15} } func (m *MsgMarketWithdrawResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -836,7 +947,7 @@ func (m *MsgMarketUpdateDetailsRequest) Reset() { *m = MsgMarketUpdateDe func (m *MsgMarketUpdateDetailsRequest) String() string { return proto.CompactTextString(m) } func (*MsgMarketUpdateDetailsRequest) ProtoMessage() {} func (*MsgMarketUpdateDetailsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{14} + return fileDescriptor_e333fcffc093bd1b, []int{16} } func (m *MsgMarketUpdateDetailsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -894,7 +1005,7 @@ func (m *MsgMarketUpdateDetailsResponse) Reset() { *m = MsgMarketUpdateD func (m *MsgMarketUpdateDetailsResponse) String() string { return proto.CompactTextString(m) } func (*MsgMarketUpdateDetailsResponse) ProtoMessage() {} func (*MsgMarketUpdateDetailsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{15} + return fileDescriptor_e333fcffc093bd1b, []int{17} } func (m *MsgMarketUpdateDetailsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -937,7 +1048,7 @@ func (m *MsgMarketUpdateEnabledRequest) Reset() { *m = MsgMarketUpdateEn func (m *MsgMarketUpdateEnabledRequest) String() string { return proto.CompactTextString(m) } func (*MsgMarketUpdateEnabledRequest) ProtoMessage() {} func (*MsgMarketUpdateEnabledRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{16} + return fileDescriptor_e333fcffc093bd1b, []int{18} } func (m *MsgMarketUpdateEnabledRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -995,7 +1106,7 @@ func (m *MsgMarketUpdateEnabledResponse) Reset() { *m = MsgMarketUpdateE func (m *MsgMarketUpdateEnabledResponse) String() string { return proto.CompactTextString(m) } func (*MsgMarketUpdateEnabledResponse) ProtoMessage() {} func (*MsgMarketUpdateEnabledResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{17} + return fileDescriptor_e333fcffc093bd1b, []int{19} } func (m *MsgMarketUpdateEnabledResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1040,7 +1151,7 @@ func (m *MsgMarketUpdateUserSettleRequest) Reset() { *m = MsgMarketUpdat func (m *MsgMarketUpdateUserSettleRequest) String() string { return proto.CompactTextString(m) } func (*MsgMarketUpdateUserSettleRequest) ProtoMessage() {} func (*MsgMarketUpdateUserSettleRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{18} + return fileDescriptor_e333fcffc093bd1b, []int{20} } func (m *MsgMarketUpdateUserSettleRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1098,7 +1209,7 @@ func (m *MsgMarketUpdateUserSettleResponse) Reset() { *m = MsgMarketUpda func (m *MsgMarketUpdateUserSettleResponse) String() string { return proto.CompactTextString(m) } func (*MsgMarketUpdateUserSettleResponse) ProtoMessage() {} func (*MsgMarketUpdateUserSettleResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{19} + return fileDescriptor_e333fcffc093bd1b, []int{21} } func (m *MsgMarketUpdateUserSettleResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1145,7 +1256,7 @@ func (m *MsgMarketManagePermissionsRequest) Reset() { *m = MsgMarketMana func (m *MsgMarketManagePermissionsRequest) String() string { return proto.CompactTextString(m) } func (*MsgMarketManagePermissionsRequest) ProtoMessage() {} func (*MsgMarketManagePermissionsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{20} + return fileDescriptor_e333fcffc093bd1b, []int{22} } func (m *MsgMarketManagePermissionsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1217,7 +1328,7 @@ func (m *MsgMarketManagePermissionsResponse) Reset() { *m = MsgMarketMan func (m *MsgMarketManagePermissionsResponse) String() string { return proto.CompactTextString(m) } func (*MsgMarketManagePermissionsResponse) ProtoMessage() {} func (*MsgMarketManagePermissionsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{21} + return fileDescriptor_e333fcffc093bd1b, []int{23} } func (m *MsgMarketManagePermissionsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1266,7 +1377,7 @@ func (m *MsgMarketManageReqAttrsRequest) Reset() { *m = MsgMarketManageR func (m *MsgMarketManageReqAttrsRequest) String() string { return proto.CompactTextString(m) } func (*MsgMarketManageReqAttrsRequest) ProtoMessage() {} func (*MsgMarketManageReqAttrsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{22} + return fileDescriptor_e333fcffc093bd1b, []int{24} } func (m *MsgMarketManageReqAttrsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1345,7 +1456,7 @@ func (m *MsgMarketManageReqAttrsResponse) Reset() { *m = MsgMarketManage func (m *MsgMarketManageReqAttrsResponse) String() string { return proto.CompactTextString(m) } func (*MsgMarketManageReqAttrsResponse) ProtoMessage() {} func (*MsgMarketManageReqAttrsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{23} + return fileDescriptor_e333fcffc093bd1b, []int{25} } func (m *MsgMarketManageReqAttrsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1388,7 +1499,7 @@ func (m *MsgGovCreateMarketRequest) Reset() { *m = MsgGovCreateMarketReq func (m *MsgGovCreateMarketRequest) String() string { return proto.CompactTextString(m) } func (*MsgGovCreateMarketRequest) ProtoMessage() {} func (*MsgGovCreateMarketRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{24} + return fileDescriptor_e333fcffc093bd1b, []int{26} } func (m *MsgGovCreateMarketRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1439,7 +1550,7 @@ func (m *MsgGovCreateMarketResponse) Reset() { *m = MsgGovCreateMarketRe func (m *MsgGovCreateMarketResponse) String() string { return proto.CompactTextString(m) } func (*MsgGovCreateMarketResponse) ProtoMessage() {} func (*MsgGovCreateMarketResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{25} + return fileDescriptor_e333fcffc093bd1b, []int{27} } func (m *MsgGovCreateMarketResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1504,7 +1615,7 @@ func (m *MsgGovManageFeesRequest) Reset() { *m = MsgGovManageFeesRequest func (m *MsgGovManageFeesRequest) String() string { return proto.CompactTextString(m) } func (*MsgGovManageFeesRequest) ProtoMessage() {} func (*MsgGovManageFeesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{26} + return fileDescriptor_e333fcffc093bd1b, []int{28} } func (m *MsgGovManageFeesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1639,7 +1750,7 @@ func (m *MsgGovManageFeesResponse) Reset() { *m = MsgGovManageFeesRespon func (m *MsgGovManageFeesResponse) String() string { return proto.CompactTextString(m) } func (*MsgGovManageFeesResponse) ProtoMessage() {} func (*MsgGovManageFeesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{27} + return fileDescriptor_e333fcffc093bd1b, []int{29} } func (m *MsgGovManageFeesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1680,7 +1791,7 @@ func (m *MsgGovUpdateParamsRequest) Reset() { *m = MsgGovUpdateParamsReq func (m *MsgGovUpdateParamsRequest) String() string { return proto.CompactTextString(m) } func (*MsgGovUpdateParamsRequest) ProtoMessage() {} func (*MsgGovUpdateParamsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{28} + return fileDescriptor_e333fcffc093bd1b, []int{30} } func (m *MsgGovUpdateParamsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1731,7 +1842,7 @@ func (m *MsgGovUpdateParamsResponse) Reset() { *m = MsgGovUpdateParamsRe func (m *MsgGovUpdateParamsResponse) String() string { return proto.CompactTextString(m) } func (*MsgGovUpdateParamsResponse) ProtoMessage() {} func (*MsgGovUpdateParamsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e333fcffc093bd1b, []int{29} + return fileDescriptor_e333fcffc093bd1b, []int{31} } func (m *MsgGovUpdateParamsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1773,6 +1884,8 @@ func init() { proto.RegisterType((*MsgFillAsksResponse)(nil), "provenance.exchange.v1.MsgFillAsksResponse") proto.RegisterType((*MsgMarketSettleRequest)(nil), "provenance.exchange.v1.MsgMarketSettleRequest") proto.RegisterType((*MsgMarketSettleResponse)(nil), "provenance.exchange.v1.MsgMarketSettleResponse") + proto.RegisterType((*MsgMarketSetOrderExternalIDRequest)(nil), "provenance.exchange.v1.MsgMarketSetOrderExternalIDRequest") + proto.RegisterType((*MsgMarketSetOrderExternalIDResponse)(nil), "provenance.exchange.v1.MsgMarketSetOrderExternalIDResponse") proto.RegisterType((*MsgMarketWithdrawRequest)(nil), "provenance.exchange.v1.MsgMarketWithdrawRequest") proto.RegisterType((*MsgMarketWithdrawResponse)(nil), "provenance.exchange.v1.MsgMarketWithdrawResponse") proto.RegisterType((*MsgMarketUpdateDetailsRequest)(nil), "provenance.exchange.v1.MsgMarketUpdateDetailsRequest") @@ -1796,118 +1909,122 @@ func init() { func init() { proto.RegisterFile("provenance/exchange/v1/tx.proto", fileDescriptor_e333fcffc093bd1b) } var fileDescriptor_e333fcffc093bd1b = []byte{ - // 1766 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0x4b, 0x6f, 0xdc, 0x5e, - 0x15, 0x8f, 0xf3, 0xea, 0xcc, 0x49, 0x93, 0xf6, 0x7f, 0xf3, 0x9a, 0xb8, 0xcd, 0x64, 0x3a, 0xa1, - 0x52, 0x69, 0xc9, 0x4c, 0x12, 0x44, 0x81, 0x88, 0x05, 0x99, 0x94, 0x44, 0x5d, 0x04, 0x22, 0x97, - 0x82, 0x04, 0x0b, 0xeb, 0x8e, 0x7d, 0x3b, 0xb1, 0xe2, 0xb1, 0xa7, 0xbe, 0x37, 0x69, 0xba, 0x42, - 0x42, 0x48, 0xac, 0x90, 0xba, 0x64, 0x8b, 0x60, 0x03, 0x42, 0x02, 0x24, 0x36, 0xf0, 0x09, 0x2a, - 0xc4, 0xa2, 0x62, 0xc5, 0x0a, 0xaa, 0x76, 0xc1, 0x47, 0x60, 0x8b, 0xee, 0xc3, 0x1e, 0x7b, 0xc6, - 0x1e, 0x7b, 0x42, 0xb2, 0x4a, 0x7c, 0xef, 0x79, 0xfc, 0x7e, 0xe7, 0x9c, 0xfb, 0x38, 0x77, 0x60, - 0xa3, 0x17, 0xf8, 0x17, 0xc4, 0xc3, 0x9e, 0x45, 0x9a, 0xe4, 0xd2, 0x3a, 0xc5, 0x5e, 0x87, 0x34, - 0x2f, 0x76, 0x9a, 0xec, 0xb2, 0xd1, 0x0b, 0x7c, 0xe6, 0xa3, 0x95, 0xbe, 0x40, 0x23, 0x14, 0x68, - 0x5c, 0xec, 0xe8, 0x55, 0xcb, 0xa7, 0x5d, 0x9f, 0x36, 0xdb, 0x98, 0x72, 0x85, 0x36, 0x61, 0x78, - 0xa7, 0x69, 0xf9, 0x8e, 0x27, 0xf5, 0xf4, 0x55, 0x35, 0xdf, 0xa5, 0x1d, 0x6e, 0xaf, 0x4b, 0x3b, - 0x6a, 0x62, 0x4d, 0x4e, 0x98, 0xe2, 0xab, 0x29, 0x3f, 0xd4, 0xd4, 0x52, 0xc7, 0xef, 0xf8, 0x72, - 0x9c, 0xff, 0xa7, 0x46, 0x37, 0x33, 0x20, 0x76, 0x71, 0x70, 0x46, 0x58, 0x8e, 0x90, 0x1f, 0xd8, - 0x24, 0xa0, 0x39, 0x42, 0x3d, 0x1c, 0xe0, 0xae, 0x12, 0xaa, 0xff, 0x55, 0x83, 0xc5, 0x63, 0xda, - 0x39, 0x08, 0x08, 0x66, 0x64, 0x9f, 0x9e, 0x19, 0xe4, 0xf5, 0x39, 0xa1, 0x0c, 0x1d, 0x40, 0x19, - 0xd3, 0x33, 0x53, 0x18, 0xac, 0x68, 0x35, 0xed, 0xd1, 0xdc, 0x6e, 0xad, 0x91, 0x1e, 0x9c, 0xc6, - 0x3e, 0x3d, 0xfb, 0x1e, 0x97, 0x6b, 0x4d, 0xbf, 0xff, 0xd7, 0xc6, 0x84, 0x51, 0xc2, 0xea, 0x1b, - 0x1d, 0x01, 0x12, 0x06, 0x4c, 0x8b, 0x9b, 0x77, 0x7c, 0xcf, 0x7c, 0x45, 0x48, 0x65, 0x52, 0x58, - 0x5b, 0x6b, 0xa8, 0x60, 0xf0, 0x90, 0x36, 0x54, 0x48, 0x1b, 0x07, 0xbe, 0xe3, 0x19, 0x77, 0x85, - 0xd2, 0x81, 0xd2, 0x39, 0x24, 0x64, 0x6f, 0xf9, 0xa7, 0xff, 0xf9, 0xe3, 0xe3, 0xbb, 0x11, 0xa0, - 0x06, 0x25, 0xae, 0x4b, 0x82, 0xfa, 0x0e, 0x2c, 0x25, 0xb1, 0xd3, 0x9e, 0xef, 0x51, 0x82, 0xd6, - 0xa0, 0x24, 0xfd, 0x3a, 0xb6, 0xc0, 0x3e, 0x6d, 0xdc, 0x12, 0xdf, 0xcf, 0xed, 0xfa, 0x5f, 0xe2, - 0x7c, 0x5b, 0x8e, 0x1d, 0xe3, 0xdb, 0x76, 0xec, 0x62, 0x7c, 0x5b, 0x8e, 0x9d, 0xe0, 0xdb, 0x56, - 0xdf, 0xd7, 0xc7, 0x77, 0x89, 0xf3, 0xbd, 0x13, 0x01, 0x6a, 0xb4, 0xcf, 0xdf, 0x0e, 0xd0, 0x15, - 0xd0, 0xf3, 0xe9, 0xbe, 0x86, 0x65, 0xae, 0xc2, 0x29, 0xb8, 0x02, 0x63, 0xc8, 0x77, 0x1b, 0x66, - 0xa9, 0xd3, 0xf1, 0x14, 0xd9, 0x72, 0xab, 0xf2, 0x8f, 0x3f, 0x6f, 0x2d, 0x29, 0x84, 0xfb, 0xb6, - 0x1d, 0x10, 0x4a, 0x5f, 0xb0, 0xc0, 0xf1, 0x3a, 0x86, 0x92, 0x4b, 0x78, 0x99, 0x4c, 0x78, 0xd9, - 0x9b, 0xe3, 0x70, 0x95, 0x5c, 0xbd, 0x02, 0x2b, 0x83, 0x2e, 0x25, 0xce, 0xfa, 0xef, 0xa7, 0x00, - 0x1d, 0xd3, 0xce, 0xa1, 0xe3, 0xba, 0x2d, 0xc7, 0xa6, 0x71, 0x28, 0x22, 0x9f, 0x05, 0xa0, 0x08, - 0x39, 0x74, 0x0f, 0xca, 0x72, 0x39, 0x84, 0x58, 0xe6, 0x8d, 0x92, 0x1c, 0x78, 0x6e, 0x23, 0x0f, - 0x6e, 0x33, 0x9f, 0x61, 0xd7, 0xc4, 0x94, 0x12, 0x46, 0x2b, 0x53, 0xb5, 0xa9, 0x91, 0xe1, 0x6f, - 0x6d, 0xf3, 0x2c, 0xfe, 0xee, 0xdf, 0x1b, 0x8f, 0x3a, 0x0e, 0x3b, 0x3d, 0x6f, 0x37, 0x2c, 0xbf, - 0xab, 0x16, 0xaa, 0xfa, 0xb3, 0x45, 0xed, 0xb3, 0x26, 0x7b, 0xdb, 0x23, 0x54, 0x28, 0x50, 0x63, - 0x4e, 0x38, 0xd8, 0x17, 0xf6, 0x51, 0x1d, 0xe6, 0xa3, 0x44, 0x99, 0x8e, 0x4d, 0x2b, 0xd3, 0xb5, - 0xa9, 0x47, 0xd3, 0xc6, 0x5c, 0x58, 0x15, 0xcf, 0x6d, 0x8a, 0x7e, 0x00, 0xba, 0x84, 0x6e, 0x52, - 0xc2, 0x98, 0x4b, 0xba, 0xc4, 0x63, 0xe6, 0x2b, 0x17, 0x33, 0x51, 0x20, 0x33, 0x79, 0x05, 0xb2, - 0x2a, 0x95, 0x5f, 0x44, 0xba, 0x87, 0x2e, 0x66, 0x87, 0x84, 0xa0, 0xef, 0xc2, 0x4a, 0xb4, 0x28, - 0x92, 0x45, 0x37, 0x9b, 0x67, 0x73, 0x31, 0x5c, 0xa5, 0xf1, 0xba, 0x53, 0x89, 0x94, 0xab, 0x6b, - 0x59, 0xac, 0x94, 0x7e, 0xb6, 0x54, 0x16, 0x7f, 0xd3, 0xcf, 0xe2, 0x3e, 0x3d, 0x8b, 0xb2, 0xd8, - 0x80, 0x19, 0x51, 0xa5, 0xb9, 0x49, 0x94, 0x62, 0xa3, 0x73, 0xf8, 0x6d, 0x90, 0x21, 0x36, 0x7b, - 0x81, 0x63, 0x91, 0xca, 0x54, 0x0e, 0x19, 0xb5, 0x10, 0x41, 0xe8, 0x9c, 0x70, 0x15, 0x9e, 0x95, - 0x7e, 0x64, 0x62, 0x59, 0x09, 0x59, 0xf3, 0xac, 0xfc, 0x04, 0x96, 0x05, 0x96, 0x44, 0x52, 0x08, - 0xa1, 0x95, 0x99, 0xeb, 0x2f, 0x99, 0x45, 0xe1, 0x29, 0x96, 0x41, 0x42, 0x28, 0x4f, 0x5f, 0xbf, - 0x74, 0xc6, 0x4c, 0x5f, 0x58, 0x5e, 0xf1, 0xf4, 0x01, 0x4f, 0x9f, 0x8c, 0x6f, 0x2c, 0x7b, 0x32, - 0x4b, 0x2a, 0x7b, 0x1f, 0x35, 0xb1, 0x3c, 0x8f, 0x45, 0xa4, 0x25, 0x9c, 0x58, 0x06, 0xb1, 0xdd, - 0x75, 0xbc, 0xfc, 0x0c, 0x0a, 0xb1, 0xd1, 0x19, 0x1c, 0x8a, 0xff, 0xd4, 0x70, 0xfc, 0x8b, 0xac, - 0x9c, 0x87, 0xb0, 0x40, 0x2e, 0x7b, 0xc4, 0x62, 0x66, 0x0f, 0x07, 0xcc, 0xc1, 0xae, 0x58, 0x2d, - 0x25, 0x63, 0x5e, 0x8e, 0x9e, 0xc8, 0x41, 0xc5, 0x5c, 0xe0, 0xaa, 0xaf, 0xc1, 0xea, 0x10, 0x43, - 0xc5, 0xfe, 0xbf, 0x1a, 0x54, 0xa2, 0xb9, 0x1f, 0x3a, 0xec, 0xd4, 0x0e, 0xf0, 0x9b, 0x1b, 0xe1, - 0xbf, 0x0e, 0xc0, 0x7c, 0x13, 0x4b, 0x3d, 0x51, 0xc0, 0x65, 0xa3, 0xcc, 0x7c, 0x65, 0x08, 0x59, - 0x30, 0x8b, 0xbb, 0xfe, 0xb9, 0xc7, 0x04, 0xe7, 0x6b, 0xae, 0x35, 0x65, 0x3a, 0x11, 0x94, 0x7b, - 0xb0, 0x96, 0x42, 0x5c, 0x85, 0xe5, 0xef, 0x1a, 0xac, 0x47, 0xb3, 0x2f, 0x7b, 0x36, 0x66, 0xe4, - 0x19, 0x61, 0xd8, 0x71, 0xe9, 0x8d, 0xc4, 0xc6, 0x80, 0x05, 0x35, 0x69, 0x4b, 0x2f, 0x6a, 0x81, - 0x3f, 0xcc, 0x3a, 0x70, 0x25, 0x30, 0x05, 0x49, 0x2d, 0xf6, 0xf9, 0x6e, 0x7c, 0x30, 0xc1, 0xb5, - 0x06, 0xd5, 0x2c, 0x36, 0x8a, 0xf0, 0xaf, 0x87, 0x09, 0x7f, 0xc7, 0xc3, 0x6d, 0x97, 0xd8, 0x37, - 0x42, 0xf8, 0xcb, 0x70, 0x17, 0x5b, 0x16, 0xe9, 0x31, 0xc7, 0xeb, 0xc8, 0x72, 0x97, 0x94, 0x4b, - 0xc6, 0x9d, 0x68, 0x5c, 0x54, 0x7c, 0x1e, 0x8f, 0x08, 0xa4, 0xe2, 0xf1, 0x07, 0x0d, 0x6a, 0x03, - 0x22, 0x2f, 0x69, 0xb8, 0xcd, 0xdc, 0x08, 0x95, 0x5d, 0x58, 0xc6, 0xae, 0xeb, 0xbf, 0x31, 0xcf, - 0x69, 0x62, 0xe3, 0x54, 0x7c, 0x16, 0xc5, 0x64, 0x1f, 0x03, 0x9f, 0x4a, 0x70, 0xda, 0x84, 0x07, - 0x23, 0x00, 0x2b, 0x5a, 0xbf, 0x9d, 0x8c, 0x49, 0x1d, 0x63, 0x0f, 0x77, 0xc8, 0x09, 0x09, 0xba, - 0x0e, 0xa5, 0x8e, 0xef, 0xd1, 0x9b, 0x5a, 0xaf, 0x01, 0xb9, 0xf0, 0xcf, 0x88, 0x89, 0x5d, 0x57, - 0x6c, 0x56, 0x65, 0xa3, 0x2c, 0x47, 0xf6, 0x5d, 0x17, 0x1d, 0x42, 0x99, 0xf9, 0xa6, 0xfc, 0x56, - 0x4b, 0x76, 0x33, 0xf3, 0x3a, 0x6c, 0x59, 0x84, 0xd2, 0xa3, 0x00, 0x7b, 0x2c, 0xbc, 0x21, 0x32, - 0xdf, 0x10, 0xaa, 0xe8, 0x19, 0x94, 0x98, 0x6f, 0x76, 0xf8, 0x9c, 0x3a, 0x65, 0xc6, 0x30, 0x73, - 0x8b, 0xf9, 0xe2, 0x33, 0x11, 0xd0, 0x2f, 0x41, 0x7d, 0x54, 0xa8, 0x54, 0x44, 0xff, 0x34, 0x19, - 0xab, 0x25, 0x29, 0x66, 0x90, 0xd7, 0xfb, 0x8c, 0x05, 0xf4, 0x86, 0x2a, 0xfe, 0x0b, 0x71, 0x9e, - 0x11, 0x93, 0x9f, 0x02, 0x72, 0x27, 0x54, 0x51, 0x5d, 0xb0, 0xc2, 0xfb, 0xfa, 0xf7, 0xf9, 0x76, - 0x88, 0x9a, 0xb0, 0x94, 0x14, 0x0d, 0x48, 0xd7, 0xbf, 0x90, 0x51, 0x2e, 0x1b, 0x5f, 0xc4, 0xa4, - 0x0d, 0x31, 0x11, 0xb3, 0xcd, 0x4f, 0x0f, 0x65, 0x7b, 0x26, 0x6e, 0xbb, 0xe5, 0xd8, 0x83, 0xb6, - 0x95, 0xa8, 0xb2, 0x3d, 0x1b, 0xb7, 0x2d, 0xa4, 0xa5, 0xed, 0x44, 0x64, 0x1f, 0xc0, 0x46, 0x66, - 0xc8, 0x54, 0x58, 0x7f, 0xa5, 0x89, 0x6d, 0xf5, 0xc8, 0xbf, 0x90, 0xb7, 0x72, 0x29, 0x1c, 0x46, - 0xf4, 0x29, 0x94, 0xf1, 0x39, 0x3b, 0xf5, 0x03, 0x87, 0xbd, 0xcd, 0x8d, 0x6a, 0x5f, 0x14, 0x7d, - 0x0b, 0x66, 0x65, 0x20, 0x55, 0xeb, 0x50, 0x1d, 0xbd, 0x2f, 0xaa, 0xea, 0x50, 0x3a, 0x7b, 0x0b, - 0x9c, 0x42, 0xdf, 0x5a, 0xfd, 0x3e, 0xe8, 0x69, 0x10, 0x15, 0x83, 0xbf, 0x81, 0x38, 0x2d, 0x8f, - 0xfc, 0x0b, 0x49, 0x91, 0xdf, 0x4b, 0xfe, 0x5f, 0xfc, 0x23, 0x2b, 0xe3, 0x25, 0xac, 0x62, 0xdb, - 0xe6, 0x97, 0x1c, 0x33, 0x96, 0x76, 0x7e, 0x17, 0xce, 0xbf, 0xa9, 0x4b, 0xa2, 0x8b, 0xd8, 0xb6, - 0x0f, 0x09, 0x89, 0xda, 0x3e, 0x7e, 0x19, 0x46, 0x3f, 0x06, 0x5d, 0xe6, 0x36, 0xd5, 0xf2, 0x74, - 0x31, 0xcb, 0x2b, 0xd2, 0xc4, 0x90, 0xf1, 0x61, 0xcc, 0xbc, 0x9c, 0x84, 0xe5, 0x99, 0x2b, 0x60, - 0x6e, 0x39, 0x76, 0x36, 0xe6, 0xc8, 0xf2, 0xec, 0xd5, 0x30, 0x87, 0xc6, 0x2d, 0xa8, 0x86, 0x98, - 0xd3, 0x5b, 0x8f, 0xca, 0xad, 0x62, 0x0e, 0x74, 0x09, 0xfd, 0x45, 0x4a, 0x0b, 0x82, 0x1c, 0x78, - 0x10, 0x63, 0x90, 0xe1, 0xa7, 0x54, 0xcc, 0xcf, 0x7a, 0x44, 0x24, 0xd5, 0x95, 0x07, 0xb5, 0x6c, - 0x3e, 0x01, 0xbf, 0x02, 0xd3, 0x4a, 0x59, 0x78, 0xca, 0xec, 0xdb, 0x0f, 0x09, 0x31, 0xb8, 0xa0, - 0x72, 0x78, 0x3f, 0x9d, 0x98, 0x10, 0xa1, 0x88, 0xc1, 0xe6, 0x48, 0x6a, 0xca, 0x25, 0x8c, 0xe5, - 0x72, 0x23, 0x93, 0xa3, 0xf2, 0x8a, 0x61, 0x3d, 0x64, 0x39, 0xdc, 0x9a, 0xf0, 0x60, 0xce, 0x15, - 0x0b, 0xe6, 0x9a, 0xe4, 0xd6, 0x1a, 0x68, 0x3a, 0x78, 0x20, 0x3b, 0x50, 0x8b, 0x11, 0x4b, 0xf7, - 0x72, 0xbb, 0x98, 0x97, 0xfb, 0x11, 0x9d, 0x34, 0x47, 0x2e, 0x6c, 0x64, 0x72, 0x51, 0xd1, 0x9b, - 0x1f, 0x2b, 0x7a, 0xf7, 0x52, 0x49, 0xa9, 0xc8, 0x05, 0x50, 0x1f, 0x45, 0x4b, 0x39, 0x5c, 0x18, - 0xcb, 0x61, 0x35, 0x8b, 0x9f, 0xf4, 0x39, 0xb4, 0xd5, 0xea, 0xa2, 0xbb, 0x18, 0xd8, 0x4b, 0x87, - 0x8e, 0x0a, 0x79, 0xed, 0x39, 0x11, 0xaf, 0x70, 0xd7, 0x70, 0x54, 0xc8, 0xe7, 0xbc, 0xbc, 0xa3, - 0x42, 0xba, 0x0b, 0x8f, 0x0a, 0xa9, 0x93, 0x7d, 0x54, 0x24, 0x21, 0x4a, 0x06, 0xbb, 0xbf, 0x5c, - 0x80, 0xa9, 0x63, 0xda, 0x41, 0xaf, 0xa0, 0x1c, 0x6d, 0x8f, 0xe8, 0x49, 0xe6, 0xd9, 0x34, 0xfc, - 0xa8, 0xa8, 0x7f, 0xa5, 0x98, 0xb0, 0x7a, 0xd6, 0x8a, 0xfc, 0xb4, 0x1c, 0xbb, 0x80, 0x9f, 0xfe, - 0x63, 0x5e, 0x01, 0x3f, 0xf1, 0xe7, 0x33, 0x17, 0xe6, 0x62, 0xaf, 0x55, 0x68, 0x6b, 0x94, 0xf2, - 0xd0, 0x43, 0x9a, 0xde, 0x28, 0x2a, 0xae, 0xbc, 0x59, 0x50, 0x0a, 0x9f, 0x54, 0xd0, 0xe3, 0x11, - 0xba, 0x03, 0xaf, 0x64, 0xfa, 0x93, 0x42, 0xb2, 0x49, 0x27, 0xbc, 0xf3, 0xcf, 0x75, 0x12, 0x7b, - 0xc4, 0xc9, 0x75, 0x12, 0x7f, 0x4a, 0x40, 0x3e, 0xdc, 0x8e, 0x37, 0xd9, 0x68, 0x54, 0x24, 0x52, - 0xde, 0x1b, 0xf4, 0x66, 0x61, 0x79, 0xe5, 0xf0, 0x1c, 0x16, 0x92, 0x0d, 0x2c, 0xda, 0xce, 0x35, - 0x31, 0xd0, 0xe4, 0xeb, 0x3b, 0x63, 0x68, 0x28, 0xb7, 0x3f, 0xd3, 0x60, 0x31, 0xa5, 0x99, 0x44, - 0x5f, 0xcb, 0x35, 0x95, 0xd6, 0x4a, 0xeb, 0x4f, 0xc7, 0x55, 0xcb, 0x80, 0xa1, 0x7a, 0xc1, 0xc2, - 0x30, 0x92, 0x0d, 0x6e, 0x61, 0x18, 0x03, 0x2d, 0x27, 0xfa, 0x85, 0x06, 0x2b, 0xe9, 0xed, 0x1b, - 0xfa, 0x46, 0x41, 0x93, 0x43, 0x2d, 0xaa, 0xfe, 0xcd, 0x2b, 0x68, 0x2a, 0x3c, 0xef, 0x34, 0x58, - 0xcd, 0xe8, 0x7e, 0x50, 0xbe, 0xd9, 0xac, 0xe6, 0x52, 0xdf, 0xbb, 0x8a, 0xaa, 0x82, 0xf4, 0x73, - 0x0d, 0x96, 0xd2, 0xda, 0x06, 0xf4, 0xb4, 0xa0, 0xd1, 0x81, 0xd6, 0x4c, 0xff, 0xfa, 0xd8, 0x7a, - 0x0a, 0xc9, 0x25, 0xdc, 0x19, 0xb8, 0xf8, 0xa3, 0x51, 0x0b, 0x20, 0xbd, 0x8f, 0xd1, 0x77, 0xc7, - 0x51, 0x51, 0x9e, 0x03, 0x98, 0x4f, 0x9c, 0x83, 0xa8, 0x39, 0xda, 0xc8, 0x50, 0xf7, 0xa1, 0x6f, - 0x17, 0x57, 0x48, 0xb0, 0x8d, 0x9f, 0x5d, 0x79, 0x6c, 0x53, 0x8e, 0xe2, 0x3c, 0xb6, 0x69, 0x47, - 0x63, 0x8b, 0xbc, 0xff, 0x54, 0xd5, 0x3e, 0x7c, 0xaa, 0x6a, 0x1f, 0x3f, 0x55, 0xb5, 0x77, 0x9f, - 0xab, 0x13, 0x1f, 0x3e, 0x57, 0x27, 0xfe, 0xf9, 0xb9, 0x3a, 0x01, 0x6b, 0x8e, 0x9f, 0x61, 0xef, - 0x44, 0xfb, 0x51, 0x23, 0xf6, 0xa4, 0xd7, 0x17, 0xda, 0x72, 0xfc, 0xd8, 0x57, 0xf3, 0x32, 0xfa, - 0xf1, 0xae, 0x3d, 0x2b, 0x7e, 0xb3, 0xfb, 0xea, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x6f, 0xf0, - 0x0a, 0x36, 0xc7, 0x1c, 0x00, 0x00, + // 1840 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcd, 0x6f, 0x1b, 0x5b, + 0x15, 0xef, 0xd4, 0x49, 0x6a, 0x9f, 0x34, 0x69, 0xdf, 0xcd, 0x97, 0x33, 0x6d, 0x1d, 0xd7, 0xa1, + 0x52, 0x79, 0x8f, 0xda, 0x4d, 0x10, 0x05, 0x02, 0x0b, 0xec, 0xf4, 0x25, 0xca, 0x22, 0x10, 0x4d, + 0x29, 0x48, 0xb0, 0x18, 0x5d, 0xcf, 0xdc, 0x3a, 0x23, 0x8f, 0x67, 0xdc, 0xb9, 0x37, 0x79, 0xe9, + 0x0a, 0x09, 0x21, 0xb1, 0x42, 0x7a, 0x12, 0x7f, 0x01, 0x82, 0x0d, 0x08, 0xf1, 0x21, 0xb1, 0x79, + 0xfc, 0x05, 0x4f, 0x88, 0xc5, 0x13, 0x2b, 0x56, 0x50, 0xb5, 0x0b, 0xfe, 0x04, 0xb6, 0xe8, 0x7e, + 0xcc, 0x78, 0xc6, 0x9e, 0xf1, 0x8c, 0x43, 0xb2, 0x4a, 0xe6, 0xde, 0xf3, 0xf1, 0xfb, 0x9d, 0x73, + 0xbf, 0xce, 0x31, 0x6c, 0x0d, 0x03, 0xff, 0x9c, 0x78, 0xd8, 0xb3, 0x48, 0x8b, 0x5c, 0x58, 0xa7, + 0xd8, 0xeb, 0x91, 0xd6, 0xf9, 0x4e, 0x8b, 0x5d, 0x34, 0x87, 0x81, 0xcf, 0x7c, 0xb4, 0x3e, 0x12, + 0x68, 0x86, 0x02, 0xcd, 0xf3, 0x1d, 0xbd, 0x66, 0xf9, 0x74, 0xe0, 0xd3, 0x56, 0x17, 0x53, 0xae, + 0xd0, 0x25, 0x0c, 0xef, 0xb4, 0x2c, 0xdf, 0xf1, 0xa4, 0x9e, 0xbe, 0xa1, 0xe6, 0x07, 0xb4, 0xc7, + 0xed, 0x0d, 0x68, 0x4f, 0x4d, 0x6c, 0xca, 0x09, 0x53, 0x7c, 0xb5, 0xe4, 0x87, 0x9a, 0x5a, 0xed, + 0xf9, 0x3d, 0x5f, 0x8e, 0xf3, 0xff, 0xd4, 0xe8, 0x76, 0x06, 0xc4, 0x01, 0x0e, 0xfa, 0x84, 0xe5, + 0x08, 0xf9, 0x81, 0x4d, 0x02, 0x9a, 0x23, 0x34, 0xc4, 0x01, 0x1e, 0x28, 0xa1, 0xc6, 0x5f, 0x35, + 0x58, 0x39, 0xa6, 0xbd, 0xfd, 0x80, 0x60, 0x46, 0xda, 0xb4, 0x6f, 0x90, 0xd7, 0x67, 0x84, 0x32, + 0xb4, 0x0f, 0x15, 0x4c, 0xfb, 0xa6, 0x30, 0x58, 0xd5, 0xea, 0xda, 0xe3, 0xc5, 0xdd, 0x7a, 0x33, + 0x3d, 0x38, 0xcd, 0x36, 0xed, 0x7f, 0x8f, 0xcb, 0x75, 0xe6, 0x3e, 0xff, 0xd7, 0xd6, 0x0d, 0xa3, + 0x8c, 0xd5, 0x37, 0x3a, 0x04, 0x24, 0x0c, 0x98, 0x16, 0x37, 0xef, 0xf8, 0x9e, 0xf9, 0x8a, 0x90, + 0xea, 0x4d, 0x61, 0x6d, 0xb3, 0xa9, 0x82, 0xc1, 0x43, 0xda, 0x54, 0x21, 0x6d, 0xee, 0xfb, 0x8e, + 0x67, 0xdc, 0x15, 0x4a, 0xfb, 0x4a, 0xe7, 0x80, 0x90, 0xbd, 0xb5, 0x9f, 0xfe, 0xe7, 0x4f, 0x1f, + 0xde, 0x8d, 0x00, 0x35, 0x29, 0x71, 0x5d, 0x12, 0x34, 0x76, 0x60, 0x35, 0x89, 0x9d, 0x0e, 0x7d, + 0x8f, 0x12, 0xb4, 0x09, 0x65, 0xe9, 0xd7, 0xb1, 0x05, 0xf6, 0x39, 0xe3, 0x96, 0xf8, 0x3e, 0xb2, + 0x1b, 0x9f, 0xc5, 0xf9, 0x76, 0x1c, 0x3b, 0xc6, 0xb7, 0xeb, 0xd8, 0xc5, 0xf8, 0x76, 0x1c, 0x3b, + 0xc1, 0xb7, 0xab, 0xbe, 0xaf, 0x8e, 0xef, 0x2a, 0xe7, 0x7b, 0x27, 0x02, 0xd4, 0xec, 0x9e, 0xbd, + 0x19, 0xa3, 0x2b, 0xa0, 0xe7, 0xd3, 0x7d, 0x0d, 0x6b, 0x5c, 0x85, 0x53, 0x70, 0x05, 0xc6, 0x90, + 0xef, 0x53, 0x58, 0xa0, 0x4e, 0xcf, 0x53, 0x64, 0x2b, 0x9d, 0xea, 0x3f, 0xfe, 0xf2, 0x64, 0x55, + 0x21, 0x6c, 0xdb, 0x76, 0x40, 0x28, 0x7d, 0xc1, 0x02, 0xc7, 0xeb, 0x19, 0x4a, 0x2e, 0xe1, 0xe5, + 0x66, 0xc2, 0xcb, 0xde, 0x22, 0x87, 0xab, 0xe4, 0x1a, 0x55, 0x58, 0x1f, 0x77, 0x29, 0x71, 0x36, + 0x7e, 0x5f, 0x02, 0x74, 0x4c, 0x7b, 0x07, 0x8e, 0xeb, 0x76, 0x1c, 0x9b, 0xc6, 0xa1, 0x88, 0x7c, + 0x16, 0x80, 0x22, 0xe4, 0xd0, 0x3d, 0xa8, 0xc8, 0xed, 0x10, 0x62, 0x59, 0x32, 0xca, 0x72, 0xe0, + 0xc8, 0x46, 0x1e, 0xdc, 0x66, 0x3e, 0xc3, 0xae, 0x89, 0x29, 0x25, 0x8c, 0x56, 0x4b, 0xf5, 0xd2, + 0xd4, 0xf0, 0x77, 0x9e, 0xf2, 0x2c, 0xfe, 0xee, 0xdf, 0x5b, 0x8f, 0x7b, 0x0e, 0x3b, 0x3d, 0xeb, + 0x36, 0x2d, 0x7f, 0xa0, 0x36, 0xaa, 0xfa, 0xf3, 0x84, 0xda, 0xfd, 0x16, 0x7b, 0x33, 0x24, 0x54, + 0x28, 0x50, 0x63, 0x51, 0x38, 0x68, 0x0b, 0xfb, 0xa8, 0x01, 0x4b, 0x51, 0xa2, 0x4c, 0xc7, 0xa6, + 0xd5, 0xb9, 0x7a, 0xe9, 0xf1, 0x9c, 0xb1, 0x18, 0xae, 0x8a, 0x23, 0x9b, 0xa2, 0x1f, 0x80, 0x2e, + 0xa1, 0x9b, 0x94, 0x30, 0xe6, 0x92, 0x01, 0xf1, 0x98, 0xf9, 0xca, 0xc5, 0x4c, 0x2c, 0x90, 0xf9, + 0xbc, 0x05, 0xb2, 0x21, 0x95, 0x5f, 0x44, 0xba, 0x07, 0x2e, 0x66, 0x07, 0x84, 0xa0, 0xef, 0xc2, + 0x7a, 0xb4, 0x29, 0x92, 0x8b, 0x6e, 0x21, 0xcf, 0xe6, 0x4a, 0xb8, 0x4b, 0xe3, 0xeb, 0x4e, 0x25, + 0x52, 0xee, 0xae, 0x35, 0xb1, 0x53, 0x46, 0xd9, 0x52, 0x59, 0xfc, 0xcd, 0x28, 0x8b, 0x6d, 0xda, + 0x8f, 0xb2, 0xd8, 0x84, 0x79, 0xb1, 0x4a, 0x73, 0x93, 0x28, 0xc5, 0xa6, 0xe7, 0xf0, 0x3b, 0x20, + 0x43, 0x6c, 0x0e, 0x03, 0xc7, 0x22, 0xd5, 0x52, 0x0e, 0x19, 0xb5, 0x11, 0x41, 0xe8, 0x9c, 0x70, + 0x15, 0x9e, 0x95, 0x51, 0x64, 0x62, 0x59, 0x09, 0x59, 0xf3, 0xac, 0xfc, 0x04, 0xd6, 0x04, 0x96, + 0x44, 0x52, 0x08, 0xa1, 0xd5, 0xf9, 0xab, 0x5f, 0x32, 0x2b, 0xc2, 0x53, 0x2c, 0x83, 0x84, 0x50, + 0x9e, 0xbe, 0xd1, 0xd2, 0x99, 0x31, 0x7d, 0xe1, 0xf2, 0x8a, 0xa7, 0x0f, 0x78, 0xfa, 0x64, 0x7c, + 0x63, 0xd9, 0x93, 0x59, 0x52, 0xd9, 0x7b, 0xab, 0x89, 0xed, 0x79, 0x2c, 0x22, 0x2d, 0xe1, 0xc4, + 0x32, 0x88, 0xed, 0x81, 0xe3, 0xe5, 0x67, 0x50, 0x88, 0x4d, 0xcf, 0xe0, 0x44, 0xfc, 0x4b, 0x93, + 0xf1, 0x2f, 0xb2, 0x73, 0x1e, 0xc1, 0x32, 0xb9, 0x18, 0x12, 0x8b, 0x99, 0x43, 0x1c, 0x30, 0x07, + 0xbb, 0x62, 0xb7, 0x94, 0x8d, 0x25, 0x39, 0x7a, 0x22, 0x07, 0x15, 0x73, 0x81, 0xab, 0xb1, 0x09, + 0x1b, 0x13, 0x0c, 0x15, 0xfb, 0xcf, 0x34, 0x68, 0xc4, 0xe7, 0x84, 0x9b, 0x8f, 0x2f, 0x18, 0x09, + 0x3c, 0xec, 0x1e, 0x3d, 0xbf, 0x96, 0x48, 0xc4, 0xcf, 0xcd, 0x52, 0xe2, 0xdc, 0x44, 0x5b, 0xb0, + 0x48, 0x94, 0x73, 0x3e, 0x3b, 0xc7, 0xbd, 0x19, 0x10, 0x0e, 0x1d, 0xd9, 0x09, 0x5a, 0x8f, 0x60, + 0x7b, 0x2a, 0x74, 0x45, 0xf1, 0xbf, 0x1a, 0x54, 0x23, 0xb9, 0x1f, 0x3a, 0xec, 0xd4, 0x0e, 0xf0, + 0x27, 0xd7, 0x42, 0xec, 0x01, 0x00, 0xf3, 0x4d, 0x2c, 0xf5, 0x04, 0xb5, 0x8a, 0x51, 0x61, 0xbe, + 0x32, 0x84, 0x2c, 0x58, 0xc0, 0x03, 0xff, 0xcc, 0x63, 0x22, 0xad, 0x57, 0xbc, 0x9d, 0x94, 0xe9, + 0x44, 0x80, 0xee, 0xc1, 0x66, 0x0a, 0x71, 0x15, 0x96, 0xbf, 0x6b, 0xf0, 0x20, 0x9a, 0x7d, 0x39, + 0xb4, 0x31, 0x23, 0xcf, 0x09, 0xc3, 0x8e, 0x4b, 0xaf, 0x25, 0x36, 0x06, 0x2c, 0xab, 0x49, 0x5b, + 0x7a, 0x51, 0x67, 0xd8, 0xa3, 0xac, 0x37, 0x85, 0x04, 0xa6, 0x20, 0xa9, 0xf3, 0x6c, 0x69, 0x10, + 0x1f, 0x4c, 0x70, 0xad, 0x43, 0x2d, 0x8b, 0x8d, 0x22, 0xfc, 0xeb, 0x49, 0xc2, 0x1f, 0x7b, 0xb8, + 0xeb, 0x12, 0xfb, 0x5a, 0x08, 0x7f, 0x19, 0xee, 0x62, 0xcb, 0x22, 0x43, 0xe6, 0x78, 0x3d, 0xb9, + 0xa3, 0x25, 0xe5, 0xb2, 0x71, 0x27, 0x1a, 0x17, 0x4b, 0x36, 0x8f, 0x47, 0x04, 0x52, 0xf1, 0xf8, + 0xa3, 0x06, 0xf5, 0x31, 0x91, 0x97, 0x34, 0x3c, 0x49, 0xaf, 0x85, 0xca, 0x2e, 0xac, 0x61, 0xd7, + 0xf5, 0x3f, 0x31, 0xcf, 0x68, 0xe2, 0x6e, 0x50, 0x7c, 0x56, 0xc4, 0xe4, 0x08, 0x03, 0x9f, 0x4a, + 0x70, 0xda, 0x86, 0x87, 0x53, 0x00, 0x2b, 0x5a, 0xbf, 0xbd, 0x19, 0x93, 0x3a, 0xc6, 0x1e, 0xee, + 0x91, 0x13, 0x12, 0x0c, 0x1c, 0x4a, 0x1d, 0xdf, 0xa3, 0xd7, 0xb5, 0x5f, 0x03, 0x72, 0xee, 0xf7, + 0x89, 0x89, 0x5d, 0x57, 0x9c, 0xc7, 0x15, 0xa3, 0x22, 0x47, 0xda, 0xae, 0x8b, 0x0e, 0xa0, 0xc2, + 0x7c, 0x53, 0x7e, 0xab, 0x2d, 0xbb, 0x9d, 0xf9, 0xe2, 0xb7, 0x2c, 0x42, 0xe9, 0x61, 0x80, 0x3d, + 0x16, 0x3e, 0x82, 0x99, 0x6f, 0x08, 0x55, 0xf4, 0x1c, 0xca, 0xcc, 0x37, 0x7b, 0x7c, 0x4e, 0x5d, + 0xa4, 0x33, 0x98, 0xb9, 0xc5, 0x7c, 0xf1, 0x99, 0x08, 0xe8, 0x97, 0x62, 0x87, 0x76, 0x4a, 0xa8, + 0x54, 0x44, 0xff, 0x7c, 0x33, 0xb6, 0x96, 0xa4, 0x98, 0x41, 0x5e, 0xb7, 0x19, 0x0b, 0xe8, 0x35, + 0xad, 0xf8, 0x0f, 0xc4, 0x95, 0x4d, 0x4c, 0x7e, 0xd1, 0xc9, 0x93, 0x50, 0x45, 0x75, 0xd9, 0x0a, + 0x4b, 0x92, 0xef, 0xf3, 0xe3, 0x10, 0xb5, 0x60, 0x35, 0x29, 0x1a, 0x90, 0x81, 0x7f, 0x2e, 0xa3, + 0x5c, 0x31, 0x3e, 0x88, 0x49, 0x1b, 0x62, 0x22, 0x66, 0x9b, 0x5f, 0x90, 0xca, 0xf6, 0x7c, 0xdc, + 0x76, 0xc7, 0xb1, 0xc7, 0x6d, 0x2b, 0x51, 0x65, 0x7b, 0x21, 0x6e, 0x5b, 0x48, 0x4b, 0xdb, 0x89, + 0xc8, 0x3e, 0x84, 0xad, 0xcc, 0x90, 0xa9, 0xb0, 0xfe, 0x4a, 0x13, 0xc7, 0xea, 0xa1, 0x7f, 0x2e, + 0x0b, 0x0f, 0x29, 0x1c, 0x46, 0xf4, 0x19, 0x54, 0xf0, 0x19, 0x3b, 0xf5, 0x03, 0x87, 0xbd, 0xc9, + 0x8d, 0xea, 0x48, 0x14, 0x7d, 0x1b, 0x16, 0x64, 0x20, 0x55, 0x75, 0x54, 0x9b, 0x7e, 0x2e, 0xaa, + 0xd5, 0xa1, 0x74, 0xf6, 0x96, 0x39, 0x85, 0x91, 0xb5, 0xc6, 0x7d, 0xd0, 0xd3, 0x20, 0x2a, 0x06, + 0x7f, 0x03, 0xf1, 0x20, 0x38, 0xf4, 0xcf, 0x25, 0x45, 0xfe, 0xf4, 0xfa, 0x7f, 0xf1, 0x4f, 0x5d, + 0x19, 0x2f, 0x61, 0x03, 0xdb, 0x36, 0x7f, 0xc7, 0x99, 0xb1, 0xb4, 0xf3, 0xe7, 0x7e, 0x7e, 0x31, + 0x22, 0x89, 0xae, 0x60, 0xdb, 0x3e, 0x20, 0x24, 0xaa, 0x6c, 0xf9, 0x7b, 0x1f, 0xfd, 0x18, 0x74, + 0x99, 0xdb, 0x54, 0xcb, 0x73, 0xc5, 0x2c, 0xaf, 0x4b, 0x13, 0x13, 0xc6, 0x27, 0x31, 0xf3, 0xe5, + 0x24, 0x2c, 0xcf, 0x5f, 0x02, 0x73, 0xc7, 0xb1, 0xb3, 0x31, 0x47, 0x96, 0x17, 0x2e, 0x87, 0x39, + 0x34, 0x6e, 0x41, 0x2d, 0xc4, 0x9c, 0x5e, 0x5d, 0x55, 0x6f, 0x15, 0x73, 0xa0, 0x4b, 0xe8, 0x2f, + 0x52, 0xaa, 0x2c, 0xe4, 0xc0, 0xc3, 0x18, 0x83, 0x0c, 0x3f, 0xe5, 0x62, 0x7e, 0x1e, 0x44, 0x44, + 0x52, 0x5d, 0x79, 0x50, 0xcf, 0xe6, 0x13, 0xf0, 0x57, 0x3e, 0xad, 0x56, 0x84, 0xa7, 0xcc, 0xd6, + 0xc4, 0x01, 0x21, 0x06, 0x17, 0x54, 0x0e, 0xef, 0xa7, 0x13, 0x13, 0x22, 0x14, 0x31, 0xd8, 0x9e, + 0x4a, 0x4d, 0xb9, 0x84, 0x99, 0x5c, 0x6e, 0x65, 0x72, 0x54, 0x5e, 0x31, 0x3c, 0x08, 0x59, 0x4e, + 0x56, 0x5f, 0x3c, 0x98, 0x8b, 0xc5, 0x82, 0xb9, 0x29, 0xb9, 0x75, 0xc6, 0xea, 0x2a, 0x1e, 0xc8, + 0x1e, 0xd4, 0x63, 0xc4, 0xd2, 0xbd, 0xdc, 0x2e, 0xe6, 0xe5, 0x7e, 0x44, 0x27, 0xcd, 0x91, 0x0b, + 0x5b, 0x99, 0x5c, 0x54, 0xf4, 0x96, 0x66, 0x8a, 0xde, 0xbd, 0x54, 0x52, 0x2a, 0x72, 0x01, 0x34, + 0xa6, 0xd1, 0x52, 0x0e, 0x97, 0x67, 0x72, 0x58, 0xcb, 0xe2, 0x27, 0x7d, 0x4e, 0x1c, 0xb5, 0xba, + 0xa8, 0x2e, 0xc6, 0xce, 0xd2, 0x89, 0xab, 0x42, 0x3e, 0x7b, 0x4e, 0x44, 0xa3, 0xf1, 0x0a, 0xae, + 0x0a, 0xd9, 0xb1, 0xcc, 0xbb, 0x2a, 0xa4, 0xbb, 0xf0, 0xaa, 0x90, 0x3a, 0xd9, 0x57, 0x45, 0x12, + 0xa2, 0x64, 0xb0, 0xfb, 0x87, 0x3b, 0x50, 0x3a, 0xa6, 0x3d, 0xf4, 0x0a, 0x2a, 0xd1, 0xf1, 0x88, + 0x3e, 0xca, 0xbc, 0x9b, 0x26, 0xfb, 0xa6, 0xfa, 0x57, 0x8a, 0x09, 0xab, 0xce, 0x5d, 0xe4, 0xa7, + 0xe3, 0xd8, 0x05, 0xfc, 0x8c, 0xfa, 0x95, 0x05, 0xfc, 0xc4, 0x3b, 0x84, 0x2e, 0x2c, 0xc6, 0x1a, + 0x72, 0xe8, 0xc9, 0x34, 0xe5, 0x89, 0x5e, 0xa1, 0xde, 0x2c, 0x2a, 0xae, 0xbc, 0x59, 0x50, 0x0e, + 0xbb, 0x46, 0xe8, 0xc3, 0x29, 0xba, 0x63, 0x8d, 0x40, 0xfd, 0xa3, 0x42, 0xb2, 0x49, 0x27, 0x6d, + 0xda, 0xcf, 0x77, 0x12, 0xeb, 0x53, 0xe5, 0x3a, 0x89, 0x77, 0x4b, 0x90, 0x0f, 0xb7, 0xe3, 0x7d, + 0x04, 0x34, 0x2d, 0x12, 0x29, 0x2d, 0x15, 0xbd, 0x55, 0x58, 0x5e, 0x39, 0xfc, 0x25, 0xaf, 0xde, + 0x33, 0x4a, 0x7c, 0xb4, 0x57, 0xc4, 0x5a, 0x7a, 0x4b, 0x43, 0xff, 0xd6, 0xa5, 0x74, 0x15, 0xaa, + 0x33, 0x58, 0x4e, 0x96, 0xd5, 0xe8, 0x69, 0xae, 0xb9, 0xb1, 0xd6, 0x83, 0xbe, 0x33, 0x83, 0x86, + 0x72, 0xfb, 0x33, 0x0d, 0x56, 0x52, 0x4a, 0x5c, 0xf4, 0xb5, 0x5c, 0x53, 0x69, 0x05, 0xbe, 0xfe, + 0x6c, 0x56, 0xb5, 0x0c, 0x18, 0xaa, 0x42, 0x2d, 0x0c, 0x23, 0x59, 0x76, 0x17, 0x86, 0x31, 0x56, + 0x08, 0xa3, 0x5f, 0x68, 0xb0, 0x9e, 0x5e, 0x54, 0xa2, 0x6f, 0x14, 0x34, 0x39, 0x51, 0x38, 0xeb, + 0xdf, 0xbc, 0x84, 0xa6, 0xc2, 0xf3, 0xa9, 0x06, 0x1b, 0x19, 0x35, 0x19, 0xca, 0x37, 0x9b, 0x55, + 0xf2, 0xea, 0x7b, 0x97, 0x51, 0x55, 0x90, 0x7e, 0xae, 0xc1, 0x6a, 0x5a, 0x31, 0x83, 0x9e, 0x15, + 0x34, 0x3a, 0x56, 0x30, 0xea, 0x5f, 0x9f, 0x59, 0x4f, 0x21, 0xb9, 0x80, 0x3b, 0x63, 0xe5, 0x08, + 0x9a, 0xb6, 0x01, 0xd2, 0xab, 0x2b, 0x7d, 0x77, 0x16, 0x15, 0xe5, 0x39, 0x80, 0xa5, 0xc4, 0xed, + 0x8c, 0x5a, 0xd3, 0x8d, 0x4c, 0xd4, 0x44, 0xfa, 0xd3, 0xe2, 0x0a, 0x09, 0xb6, 0xf1, 0x1b, 0x35, + 0x8f, 0x6d, 0xca, 0x03, 0x21, 0x8f, 0x6d, 0xda, 0x85, 0xdd, 0x21, 0x9f, 0xbf, 0xab, 0x69, 0x5f, + 0xbc, 0xab, 0x69, 0x6f, 0xdf, 0xd5, 0xb4, 0x4f, 0xdf, 0xd7, 0x6e, 0x7c, 0xf1, 0xbe, 0x76, 0xe3, + 0x9f, 0xef, 0x6b, 0x37, 0x60, 0xd3, 0xf1, 0x33, 0xec, 0x9d, 0x68, 0x3f, 0x6a, 0xc6, 0x1a, 0x8d, + 0x23, 0xa1, 0x27, 0x8e, 0x1f, 0xfb, 0x6a, 0x5d, 0x44, 0xbf, 0x9a, 0x76, 0x17, 0xc4, 0x8f, 0xa5, + 0x5f, 0xfd, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa9, 0x4b, 0x96, 0xdc, 0x40, 0x1e, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1934,6 +2051,8 @@ type MsgClient interface { FillAsks(ctx context.Context, in *MsgFillAsksRequest, opts ...grpc.CallOption) (*MsgFillAsksResponse, error) // MarketSettle is a market endpoint to trigger the settlement of orders. MarketSettle(ctx context.Context, in *MsgMarketSettleRequest, opts ...grpc.CallOption) (*MsgMarketSettleResponse, error) + // MarketSetOrderExternalID updates an order's external id field. + MarketSetOrderExternalID(ctx context.Context, in *MsgMarketSetOrderExternalIDRequest, opts ...grpc.CallOption) (*MsgMarketSetOrderExternalIDResponse, error) // MarketWithdraw is a market endpoint to withdraw fees that have been collected. MarketWithdraw(ctx context.Context, in *MsgMarketWithdrawRequest, opts ...grpc.CallOption) (*MsgMarketWithdrawResponse, error) // MarketUpdateDetails is a market endpoint to update its details. @@ -2016,6 +2135,15 @@ func (c *msgClient) MarketSettle(ctx context.Context, in *MsgMarketSettleRequest return out, nil } +func (c *msgClient) MarketSetOrderExternalID(ctx context.Context, in *MsgMarketSetOrderExternalIDRequest, opts ...grpc.CallOption) (*MsgMarketSetOrderExternalIDResponse, error) { + out := new(MsgMarketSetOrderExternalIDResponse) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/MarketSetOrderExternalID", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *msgClient) MarketWithdraw(ctx context.Context, in *MsgMarketWithdrawRequest, opts ...grpc.CallOption) (*MsgMarketWithdrawResponse, error) { out := new(MsgMarketWithdrawResponse) err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Msg/MarketWithdraw", in, out, opts...) @@ -2111,6 +2239,8 @@ type MsgServer interface { FillAsks(context.Context, *MsgFillAsksRequest) (*MsgFillAsksResponse, error) // MarketSettle is a market endpoint to trigger the settlement of orders. MarketSettle(context.Context, *MsgMarketSettleRequest) (*MsgMarketSettleResponse, error) + // MarketSetOrderExternalID updates an order's external id field. + MarketSetOrderExternalID(context.Context, *MsgMarketSetOrderExternalIDRequest) (*MsgMarketSetOrderExternalIDResponse, error) // MarketWithdraw is a market endpoint to withdraw fees that have been collected. MarketWithdraw(context.Context, *MsgMarketWithdrawRequest) (*MsgMarketWithdrawResponse, error) // MarketUpdateDetails is a market endpoint to update its details. @@ -2153,6 +2283,9 @@ func (*UnimplementedMsgServer) FillAsks(ctx context.Context, req *MsgFillAsksReq func (*UnimplementedMsgServer) MarketSettle(ctx context.Context, req *MsgMarketSettleRequest) (*MsgMarketSettleResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method MarketSettle not implemented") } +func (*UnimplementedMsgServer) MarketSetOrderExternalID(ctx context.Context, req *MsgMarketSetOrderExternalIDRequest) (*MsgMarketSetOrderExternalIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MarketSetOrderExternalID not implemented") +} func (*UnimplementedMsgServer) MarketWithdraw(ctx context.Context, req *MsgMarketWithdrawRequest) (*MsgMarketWithdrawResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method MarketWithdraw not implemented") } @@ -2293,6 +2426,24 @@ func _Msg_MarketSettle_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } +func _Msg_MarketSetOrderExternalID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMarketSetOrderExternalIDRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MarketSetOrderExternalID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provenance.exchange.v1.Msg/MarketSetOrderExternalID", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MarketSetOrderExternalID(ctx, req.(*MsgMarketSetOrderExternalIDRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Msg_MarketWithdraw_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MsgMarketWithdrawRequest) if err := dec(in); err != nil { @@ -2483,6 +2634,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "MarketSettle", Handler: _Msg_MarketSettle_Handler, }, + { + MethodName: "MarketSetOrderExternalID", + Handler: _Msg_MarketSetOrderExternalID_Handler, + }, { MethodName: "MarketWithdraw", Handler: _Msg_MarketWithdraw_Handler, @@ -3058,6 +3213,76 @@ func (m *MsgMarketSettleResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *MsgMarketSetOrderExternalIDRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketSetOrderExternalIDRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketSetOrderExternalIDRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ExternalId) > 0 { + i -= len(m.ExternalId) + copy(dAtA[i:], m.ExternalId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ExternalId))) + i-- + dAtA[i] = 0x22 + } + if m.OrderId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.OrderId)) + i-- + dAtA[i] = 0x18 + } + if m.MarketId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Admin) > 0 { + i -= len(m.Admin) + copy(dAtA[i:], m.Admin) + i = encodeVarintTx(dAtA, i, uint64(len(m.Admin))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgMarketSetOrderExternalIDResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMarketSetOrderExternalIDResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMarketSetOrderExternalIDResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func (m *MsgMarketWithdrawRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -4104,6 +4329,38 @@ func (m *MsgMarketSettleResponse) Size() (n int) { return n } +func (m *MsgMarketSetOrderExternalIDRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Admin) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.MarketId != 0 { + n += 1 + sovTx(uint64(m.MarketId)) + } + if m.OrderId != 0 { + n += 1 + sovTx(uint64(m.OrderId)) + } + l = len(m.ExternalId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgMarketSetOrderExternalIDResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func (m *MsgMarketWithdrawRequest) Size() (n int) { if m == nil { return 0 @@ -5978,6 +6235,208 @@ func (m *MsgMarketSettleResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgMarketSetOrderExternalIDRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketSetOrderExternalIDRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketSetOrderExternalIDRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Admin", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Admin = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", wireType) + } + m.OrderId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExternalId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMarketSetOrderExternalIDResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMarketSetOrderExternalIDResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMarketSetOrderExternalIDResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *MsgMarketWithdrawRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 From d1373c01e8663e403433f7c3918cdd4f7d838498 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 13 Oct 2023 11:03:23 -0600 Subject: [PATCH 293/309] [1658]: Add the market id to severl order-related events. --- docs/proto-docs.md | 5 + proto/provenance/exchange/v1/events.proto | 18 +- x/exchange/events.go | 5 + x/exchange/events.pb.go | 272 ++++++++++++++++++---- x/exchange/events_test.go | 64 ++++- 5 files changed, 303 insertions(+), 61 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 1769c35b1e..587f6077d1 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1529,6 +1529,7 @@ EventOrderCancelled is an event emitted when an order is cancelled. | ----- | ---- | ----- | ----------- | | `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order cancelled. | | `cancelled_by` | [string](#string) | | cancelled_by is the account that triggered the cancellation of the order. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | | `external_id` | [string](#string) | | external_id is the order's external id. | @@ -1546,6 +1547,7 @@ EventOrderCreated is an event emitted when an order is created. | ----- | ---- | ----- | ----------- | | `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order created. | | `order_type` | [string](#string) | | order_type is the type of order, e.g. "ask" or "bid". | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | | `external_id` | [string](#string) | | external_id is the order's external id. | @@ -1562,6 +1564,7 @@ EventOrderExternalIDUpdated is an event emitted when an order's external id is u | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order partially filled. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | | `external_id` | [string](#string) | | external_id is the order's new external id. | @@ -1582,6 +1585,7 @@ This event is also used for orders that were previously partially filled, but ha | `assets` | [string](#string) | | assets is the coins amount string of assets bought/sold for this order. | | `price` | [string](#string) | | price is the coins amount string of the price payed/received for this order. | | `fees` | [string](#string) | | fees is the coins amount string of settlement fees paid with this order. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | | `external_id` | [string](#string) | | external_id is the order's external id. | @@ -1601,6 +1605,7 @@ EventOrderPartiallyFilled is an event emitted when an order filled in part and s | `assets` | [string](#string) | | assets is the coins amount string of assets that were filled and removed from the order. | | `price` | [string](#string) | | price is the coins amount string of the price payed/received for this order. For ask orders, this might be more than the amount that was removed from the order's price. | | `fees` | [string](#string) | | fees is the coins amount string of settlement fees paid with this partial order. For ask orders, this might be more than the amount that was removed from the order's settlement fees. | +| `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market. | | `external_id` | [string](#string) | | external_id is the order's external id. | diff --git a/proto/provenance/exchange/v1/events.proto b/proto/provenance/exchange/v1/events.proto index 07a55a63d8..3839b76b6b 100644 --- a/proto/provenance/exchange/v1/events.proto +++ b/proto/provenance/exchange/v1/events.proto @@ -14,8 +14,10 @@ message EventOrderCreated { uint64 order_id = 1; // order_type is the type of order, e.g. "ask" or "bid". string order_type = 2; + // market_id is the numerical identifier of the market. + uint32 market_id = 3; // external_id is the order's external id. - string external_id = 3; + string external_id = 4; } // EventOrderCancelled is an event emitted when an order is cancelled. @@ -24,8 +26,10 @@ message EventOrderCancelled { uint64 order_id = 1; // cancelled_by is the account that triggered the cancellation of the order. string cancelled_by = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // market_id is the numerical identifier of the market. + uint32 market_id = 3; // external_id is the order's external id. - string external_id = 3; + string external_id = 4; } // EventOrderFilled is an event emitted when an order has been filled in full. @@ -39,8 +43,10 @@ message EventOrderFilled { string price = 3; // fees is the coins amount string of settlement fees paid with this order. string fees = 4; + // market_id is the numerical identifier of the market. + uint32 market_id = 5; // external_id is the order's external id. - string external_id = 5; + string external_id = 6; } // EventOrderPartiallyFilled is an event emitted when an order filled in part and still has more left to fill. @@ -55,14 +61,18 @@ message EventOrderPartiallyFilled { // fees is the coins amount string of settlement fees paid with this partial order. // For ask orders, this might be more than the amount that was removed from the order's settlement fees. string fees = 4; + // market_id is the numerical identifier of the market. + uint32 market_id = 5; // external_id is the order's external id. - string external_id = 5; + string external_id = 6; } // EventOrderExternalIDUpdated is an event emitted when an order's external id is updated. message EventOrderExternalIDUpdated { // order_id is the numerical identifier of the order partially filled. uint64 order_id = 1; + // market_id is the numerical identifier of the market. + uint32 market_id = 2; // external_id is the order's new external id. string external_id = 3; } diff --git a/x/exchange/events.go b/x/exchange/events.go index b73b9367f9..2667ce70a4 100644 --- a/x/exchange/events.go +++ b/x/exchange/events.go @@ -10,6 +10,7 @@ func NewEventOrderCreated(order OrderI) *EventOrderCreated { return &EventOrderCreated{ OrderId: order.GetOrderID(), OrderType: order.GetOrderType(), + MarketId: order.GetMarketID(), ExternalId: order.GetExternalID(), } } @@ -18,6 +19,7 @@ func NewEventOrderCancelled(order OrderI, cancelledBy sdk.AccAddress) *EventOrde return &EventOrderCancelled{ OrderId: order.GetOrderID(), CancelledBy: cancelledBy.String(), + MarketId: order.GetMarketID(), ExternalId: order.GetExternalID(), } } @@ -28,6 +30,7 @@ func NewEventOrderFilled(order OrderI) *EventOrderFilled { Assets: order.GetAssets().String(), Price: order.GetPrice().String(), Fees: order.GetSettlementFees().String(), + MarketId: order.GetMarketID(), ExternalId: order.GetExternalID(), } } @@ -38,6 +41,7 @@ func NewEventOrderPartiallyFilled(order OrderI) *EventOrderPartiallyFilled { Assets: order.GetAssets().String(), Price: order.GetPrice().String(), Fees: order.GetSettlementFees().String(), + MarketId: order.GetMarketID(), ExternalId: order.GetExternalID(), } } @@ -45,6 +49,7 @@ func NewEventOrderPartiallyFilled(order OrderI) *EventOrderPartiallyFilled { func NewEventOrderExternalIDUpdated(order OrderI) *EventOrderExternalIDUpdated { return &EventOrderExternalIDUpdated{ OrderId: order.GetOrderID(), + MarketId: order.GetMarketID(), ExternalId: order.GetExternalID(), } } diff --git a/x/exchange/events.pb.go b/x/exchange/events.pb.go index 0fd503f3d4..afe695a1be 100644 --- a/x/exchange/events.pb.go +++ b/x/exchange/events.pb.go @@ -29,8 +29,10 @@ type EventOrderCreated struct { OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` // order_type is the type of order, e.g. "ask" or "bid". OrderType string `protobuf:"bytes,2,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,3,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // external_id is the order's external id. - ExternalId string `protobuf:"bytes,3,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` + ExternalId string `protobuf:"bytes,4,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } func (m *EventOrderCreated) Reset() { *m = EventOrderCreated{} } @@ -80,6 +82,13 @@ func (m *EventOrderCreated) GetOrderType() string { return "" } +func (m *EventOrderCreated) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + func (m *EventOrderCreated) GetExternalId() string { if m != nil { return m.ExternalId @@ -93,8 +102,10 @@ type EventOrderCancelled struct { OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` // cancelled_by is the account that triggered the cancellation of the order. CancelledBy string `protobuf:"bytes,2,opt,name=cancelled_by,json=cancelledBy,proto3" json:"cancelled_by,omitempty"` + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,3,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // external_id is the order's external id. - ExternalId string `protobuf:"bytes,3,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` + ExternalId string `protobuf:"bytes,4,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } func (m *EventOrderCancelled) Reset() { *m = EventOrderCancelled{} } @@ -144,6 +155,13 @@ func (m *EventOrderCancelled) GetCancelledBy() string { return "" } +func (m *EventOrderCancelled) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + func (m *EventOrderCancelled) GetExternalId() string { if m != nil { return m.ExternalId @@ -162,8 +180,10 @@ type EventOrderFilled struct { Price string `protobuf:"bytes,3,opt,name=price,proto3" json:"price,omitempty"` // fees is the coins amount string of settlement fees paid with this order. Fees string `protobuf:"bytes,4,opt,name=fees,proto3" json:"fees,omitempty"` + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,5,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // external_id is the order's external id. - ExternalId string `protobuf:"bytes,5,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` + ExternalId string `protobuf:"bytes,6,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } func (m *EventOrderFilled) Reset() { *m = EventOrderFilled{} } @@ -227,6 +247,13 @@ func (m *EventOrderFilled) GetFees() string { return "" } +func (m *EventOrderFilled) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + func (m *EventOrderFilled) GetExternalId() string { if m != nil { return m.ExternalId @@ -246,8 +273,10 @@ type EventOrderPartiallyFilled struct { // fees is the coins amount string of settlement fees paid with this partial order. // For ask orders, this might be more than the amount that was removed from the order's settlement fees. Fees string `protobuf:"bytes,4,opt,name=fees,proto3" json:"fees,omitempty"` + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,5,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // external_id is the order's external id. - ExternalId string `protobuf:"bytes,5,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` + ExternalId string `protobuf:"bytes,6,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } func (m *EventOrderPartiallyFilled) Reset() { *m = EventOrderPartiallyFilled{} } @@ -311,6 +340,13 @@ func (m *EventOrderPartiallyFilled) GetFees() string { return "" } +func (m *EventOrderPartiallyFilled) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + func (m *EventOrderPartiallyFilled) GetExternalId() string { if m != nil { return m.ExternalId @@ -322,6 +358,8 @@ func (m *EventOrderPartiallyFilled) GetExternalId() string { type EventOrderExternalIDUpdated struct { // order_id is the numerical identifier of the order partially filled. OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + // market_id is the numerical identifier of the market. + MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // external_id is the order's new external id. ExternalId string `protobuf:"bytes,3,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } @@ -366,6 +404,13 @@ func (m *EventOrderExternalIDUpdated) GetOrderId() uint64 { return 0 } +func (m *EventOrderExternalIDUpdated) GetMarketId() uint32 { + if m != nil { + return m.MarketId + } + return 0 +} + func (m *EventOrderExternalIDUpdated) GetExternalId() string { if m != nil { return m.ExternalId @@ -984,43 +1029,45 @@ func init() { } var fileDescriptor_c1b69385a348cffa = []byte{ - // 574 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x55, 0x4d, 0x6f, 0xd3, 0x40, - 0x10, 0xad, 0x21, 0x2d, 0xcd, 0x14, 0x24, 0x30, 0x55, 0x94, 0x50, 0x6a, 0x2a, 0x73, 0xe9, 0xa5, - 0xb6, 0x22, 0x84, 0x90, 0xe0, 0xd4, 0x90, 0x54, 0xea, 0x01, 0x11, 0xa5, 0x54, 0x08, 0x2e, 0xd1, - 0xc6, 0x1e, 0x92, 0xa5, 0xf6, 0xae, 0xbb, 0xbb, 0x49, 0xe3, 0x3f, 0x81, 0x7a, 0xe5, 0x7f, 0xf0, - 0x0f, 0xb8, 0x70, 0xac, 0x38, 0x71, 0x44, 0xc9, 0x1f, 0x41, 0xfe, 0x4a, 0x1c, 0xa8, 0x92, 0x5e, - 0x2c, 0x71, 0xf3, 0x8c, 0xdf, 0xec, 0x7b, 0x6f, 0xf7, 0x49, 0x03, 0x4f, 0x03, 0xc1, 0x47, 0xc8, - 0x08, 0x73, 0xd0, 0xc6, 0xb1, 0x33, 0x20, 0xac, 0x8f, 0xf6, 0xa8, 0x6e, 0xe3, 0x08, 0x99, 0x92, - 0x56, 0x20, 0xb8, 0xe2, 0x7a, 0x65, 0x0e, 0xb2, 0x32, 0x90, 0x35, 0xaa, 0x3f, 0xaa, 0x39, 0x5c, - 0xfa, 0x5c, 0x76, 0x63, 0x94, 0x9d, 0x14, 0xc9, 0x88, 0xc9, 0xe0, 0x41, 0x2b, 0x3a, 0xe2, 0xad, - 0x70, 0x51, 0xbc, 0x16, 0x48, 0x14, 0xba, 0x7a, 0x0d, 0x36, 0x79, 0x54, 0x77, 0xa9, 0x5b, 0xd5, - 0xf6, 0xb4, 0xfd, 0x52, 0xe7, 0x4e, 0x5c, 0x1f, 0xbb, 0xfa, 0x2e, 0x40, 0xf2, 0x4b, 0x85, 0x01, - 0x56, 0x6f, 0xed, 0x69, 0xfb, 0xe5, 0x4e, 0x39, 0xee, 0xbc, 0x0b, 0x03, 0xd4, 0x9f, 0xc0, 0x16, - 0x8e, 0x15, 0x0a, 0x46, 0xbc, 0x68, 0xf8, 0x76, 0xfc, 0x1f, 0xb2, 0xd6, 0xb1, 0x6b, 0x7e, 0xd1, - 0xe0, 0x61, 0x8e, 0x30, 0x92, 0xea, 0x79, 0xcb, 0x29, 0x5f, 0xc1, 0x5d, 0x27, 0xc3, 0x75, 0x7b, - 0x61, 0x42, 0xda, 0xa8, 0xfe, 0xfc, 0x76, 0xb0, 0x9d, 0x5a, 0x39, 0x74, 0x5d, 0x81, 0x52, 0x9e, - 0x28, 0x41, 0x59, 0xbf, 0xb3, 0x35, 0x43, 0x37, 0xc2, 0xd5, 0x82, 0x2e, 0x35, 0xb8, 0x3f, 0x17, - 0x74, 0x44, 0x57, 0xa9, 0xa9, 0xc0, 0x06, 0x91, 0x12, 0x95, 0x4c, 0xcd, 0xa7, 0x95, 0xbe, 0x0d, - 0xeb, 0x81, 0xa0, 0x0e, 0xa6, 0x14, 0x49, 0xa1, 0xeb, 0x50, 0xfa, 0x84, 0x28, 0xab, 0xa5, 0xb8, - 0x19, 0x7f, 0xff, 0x2d, 0x69, 0xfd, 0x1f, 0x49, 0x5f, 0x35, 0xa8, 0xcd, 0x25, 0xb5, 0x89, 0x50, - 0x94, 0x78, 0x5e, 0xf8, 0x5f, 0x68, 0xfb, 0x00, 0x3b, 0x73, 0x69, 0xad, 0xac, 0xdf, 0x3c, 0x0d, - 0xdc, 0x55, 0xc9, 0x59, 0xf9, 0x12, 0xdf, 0xb3, 0x68, 0xbc, 0x21, 0xe2, 0x0c, 0xd5, 0x7b, 0xaa, - 0x06, 0xae, 0x20, 0x17, 0xfa, 0x0e, 0x94, 0xfd, 0xb8, 0x93, 0x1d, 0x7a, 0xaf, 0xb3, 0x99, 0x34, - 0x52, 0xcb, 0x3e, 0x1f, 0x32, 0x35, 0xb3, 0x1c, 0x57, 0xfa, 0x4b, 0xd8, 0x72, 0x51, 0x2a, 0xca, - 0x88, 0xa2, 0x9c, 0x25, 0x6c, 0xcb, 0x32, 0x93, 0x03, 0x47, 0x81, 0xbb, 0x48, 0xc9, 0x59, 0x14, - 0xb8, 0xd2, 0xaa, 0xe1, 0x19, 0xba, 0x11, 0x9a, 0xe7, 0xe9, 0xdb, 0x25, 0x26, 0x9a, 0xa8, 0x08, - 0xf5, 0x64, 0x76, 0x3d, 0x4b, 0xad, 0xbc, 0x00, 0x18, 0x26, 0xb8, 0x9b, 0xa4, 0xbc, 0x9c, 0x62, - 0x1b, 0xa1, 0xf9, 0x19, 0xf4, 0x1c, 0x65, 0x8b, 0x91, 0x9e, 0x57, 0x18, 0xd7, 0xd9, 0xc2, 0x1b, - 0x35, 0xa9, 0x2c, 0x92, 0x4c, 0xc1, 0xe3, 0x1c, 0xd9, 0xa9, 0x44, 0x71, 0x82, 0x4a, 0x79, 0x58, - 0xac, 0xc5, 0x21, 0xec, 0x5e, 0xcb, 0x5a, 0xb0, 0xd9, 0x45, 0xda, 0x36, 0x0a, 0x9f, 0x4a, 0x49, - 0x39, 0x2b, 0x38, 0x3c, 0x8b, 0x79, 0xed, 0xe0, 0xf9, 0xa1, 0x52, 0xa2, 0x58, 0xca, 0xfa, 0x42, - 0x5e, 0xb3, 0xa5, 0xb3, 0x8c, 0xcb, 0x7c, 0x0e, 0x95, 0xdc, 0xc8, 0x11, 0xe2, 0x8d, 0x6e, 0xc5, - 0xdc, 0x4e, 0x99, 0xda, 0x44, 0x10, 0x3f, 0x1b, 0x69, 0xe0, 0x8f, 0x89, 0xa1, 0x5d, 0x4d, 0x0c, - 0xed, 0xf7, 0xc4, 0xd0, 0x2e, 0xa7, 0xc6, 0xda, 0xd5, 0xd4, 0x58, 0xfb, 0x35, 0x35, 0xd6, 0xa0, - 0x46, 0xb9, 0x75, 0xfd, 0x0e, 0x6d, 0x6b, 0x1f, 0xad, 0x3e, 0x55, 0x83, 0x61, 0xcf, 0x72, 0xb8, - 0x6f, 0xcf, 0x41, 0x07, 0x94, 0xe7, 0x2a, 0x7b, 0x3c, 0xdb, 0xce, 0xbd, 0x8d, 0x78, 0xc3, 0x3e, - 0xfb, 0x13, 0x00, 0x00, 0xff, 0xff, 0xf3, 0xcb, 0x11, 0xaf, 0xbb, 0x07, 0x00, 0x00, + // 601 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x96, 0xcf, 0x4f, 0xd4, 0x40, + 0x14, 0xc7, 0x19, 0x58, 0x90, 0x7d, 0x68, 0xa2, 0x95, 0x10, 0x56, 0xa4, 0x92, 0x7a, 0xe1, 0x42, + 0x1b, 0x62, 0x8c, 0x89, 0x9e, 0x58, 0x81, 0x84, 0x83, 0x71, 0x53, 0x24, 0x26, 0x5e, 0xc8, 0x6c, + 0xfb, 0x84, 0x91, 0x76, 0xa6, 0xcc, 0xcc, 0x2e, 0xf4, 0x6f, 0xf0, 0xe2, 0xff, 0x61, 0xbc, 0x19, + 0xff, 0x01, 0x2f, 0x1e, 0x89, 0x27, 0x8f, 0x06, 0xfe, 0x11, 0xd3, 0x5f, 0xbb, 0xad, 0x90, 0x42, + 0x62, 0x9a, 0x78, 0xdb, 0xf7, 0xfa, 0x7d, 0xf3, 0xf9, 0xbe, 0xd7, 0xb7, 0x99, 0xc2, 0xe3, 0x48, + 0x8a, 0x21, 0x72, 0xca, 0x3d, 0x74, 0xf0, 0xd4, 0x3b, 0xa4, 0xfc, 0x00, 0x9d, 0xe1, 0xba, 0x83, + 0x43, 0xe4, 0x5a, 0xd9, 0x91, 0x14, 0x5a, 0x18, 0x0b, 0x63, 0x91, 0x5d, 0x88, 0xec, 0xe1, 0xfa, + 0x83, 0x8e, 0x27, 0x54, 0x28, 0xd4, 0x7e, 0xaa, 0x72, 0xb2, 0x20, 0x2b, 0xb1, 0x3e, 0x12, 0xb8, + 0xb7, 0x95, 0x9c, 0xf1, 0x5a, 0xfa, 0x28, 0x5f, 0x4a, 0xa4, 0x1a, 0x7d, 0xa3, 0x03, 0xb3, 0x22, + 0x89, 0xf7, 0x99, 0xbf, 0x48, 0x56, 0xc8, 0x6a, 0xcb, 0xbd, 0x95, 0xc6, 0x3b, 0xbe, 0xb1, 0x0c, + 0x90, 0x3d, 0xd2, 0x71, 0x84, 0x8b, 0x93, 0x2b, 0x64, 0xb5, 0xed, 0xb6, 0xd3, 0xcc, 0x9b, 0x38, + 0x42, 0x63, 0x09, 0xda, 0x21, 0x95, 0x47, 0xa8, 0x93, 0xd2, 0xa9, 0x15, 0xb2, 0x7a, 0xc7, 0x9d, + 0xcd, 0x12, 0x3b, 0xbe, 0xf1, 0x08, 0xe6, 0xf0, 0x54, 0xa3, 0xe4, 0x34, 0x48, 0x1e, 0xb7, 0xd2, + 0x62, 0x28, 0x52, 0x3b, 0xbe, 0xf5, 0x99, 0xc0, 0xfd, 0x92, 0x9b, 0xa4, 0x91, 0x20, 0xa8, 0xf7, + 0xf3, 0x02, 0x6e, 0x7b, 0x85, 0x6e, 0xbf, 0x1f, 0x67, 0x8e, 0xba, 0x8b, 0x3f, 0xbf, 0xae, 0xcd, + 0xe7, 0x8d, 0x6e, 0xf8, 0xbe, 0x44, 0xa5, 0x76, 0xb5, 0x64, 0xfc, 0xc0, 0x9d, 0x1b, 0xa9, 0xbb, + 0xf1, 0x3f, 0xba, 0xfd, 0x42, 0xe0, 0xee, 0xd8, 0xed, 0x36, 0xbb, 0xce, 0xea, 0x02, 0xcc, 0x50, + 0xa5, 0x50, 0xab, 0x7c, 0x6c, 0x79, 0x64, 0xcc, 0xc3, 0x74, 0x24, 0x99, 0x87, 0xa9, 0x83, 0xb6, + 0x9b, 0x05, 0x86, 0x01, 0xad, 0xf7, 0x88, 0x2a, 0xe7, 0xa6, 0xbf, 0xab, 0x7e, 0xa7, 0xeb, 0xfd, + 0xce, 0x5c, 0xf2, 0xfb, 0x8d, 0x40, 0x67, 0xec, 0xb7, 0x47, 0xa5, 0x66, 0x34, 0x08, 0xe2, 0xff, + 0xdf, 0xf8, 0x10, 0x96, 0xc6, 0xbe, 0xb7, 0x8a, 0xfc, 0xe6, 0x5e, 0xe4, 0x5f, 0xb7, 0xad, 0x15, + 0xee, 0x64, 0x3d, 0x77, 0xea, 0x12, 0xf7, 0x7b, 0xb1, 0x8e, 0xaf, 0xd2, 0x92, 0xb7, 0x4c, 0x1f, + 0xfa, 0x92, 0x9e, 0x54, 0x4f, 0x25, 0x7f, 0x9d, 0x9a, 0x0c, 0x2b, 0x14, 0x03, 0xae, 0x47, 0xc3, + 0x4a, 0x23, 0xe3, 0x39, 0xcc, 0xf9, 0xa8, 0x34, 0xe3, 0x54, 0x33, 0xc1, 0x33, 0x5a, 0xdd, 0x9e, + 0x96, 0xc4, 0xc9, 0x92, 0x9f, 0xe4, 0x70, 0x9e, 0x2c, 0x79, 0xeb, 0xba, 0xe2, 0x91, 0xba, 0x1b, + 0x5b, 0xc7, 0xf9, 0x5b, 0xcf, 0x9a, 0xd8, 0x44, 0x4d, 0x59, 0xa0, 0x8a, 0xd9, 0xd5, 0xb6, 0xf2, + 0x0c, 0x60, 0x90, 0xe9, 0x6e, 0xf2, 0xcf, 0x6a, 0xe7, 0xda, 0x6e, 0x6c, 0x7d, 0x00, 0xa3, 0x84, + 0xdc, 0xe2, 0xb4, 0x1f, 0x34, 0xc6, 0x3a, 0xaa, 0xbc, 0xa3, 0x4d, 0xa6, 0x9a, 0x84, 0x69, 0x78, + 0x58, 0x82, 0xed, 0x29, 0x94, 0xbb, 0xa8, 0x75, 0x80, 0xcd, 0xb6, 0x38, 0x80, 0xe5, 0x2b, 0xa9, + 0x0d, 0x37, 0x5b, 0xc5, 0xf6, 0x50, 0x86, 0x4c, 0x29, 0x26, 0x78, 0xc3, 0xcb, 0x53, 0xdd, 0x57, + 0x17, 0x8f, 0x37, 0xb4, 0x96, 0xcd, 0x22, 0xd7, 0x2b, 0xfb, 0x5a, 0xdc, 0x82, 0x75, 0x2c, 0xeb, + 0x29, 0x2c, 0x94, 0x4a, 0xb6, 0x11, 0x6f, 0x34, 0x15, 0x6b, 0x3e, 0x27, 0xf5, 0xa8, 0xa4, 0x61, + 0x51, 0xd2, 0xc5, 0x1f, 0xe7, 0x26, 0x39, 0x3b, 0x37, 0xc9, 0xef, 0x73, 0x93, 0x7c, 0xba, 0x30, + 0x27, 0xce, 0x2e, 0xcc, 0x89, 0x5f, 0x17, 0xe6, 0x04, 0x74, 0x98, 0xb0, 0xaf, 0xbe, 0xd5, 0x7b, + 0xe4, 0x9d, 0x7d, 0xc0, 0xf4, 0xe1, 0xa0, 0x6f, 0x7b, 0x22, 0x74, 0xc6, 0xa2, 0x35, 0x26, 0x4a, + 0x91, 0x73, 0x3a, 0xfa, 0x5e, 0xe8, 0xcf, 0xa4, 0x77, 0xfe, 0x93, 0x3f, 0x01, 0x00, 0x00, 0xff, + 0xff, 0x63, 0xb9, 0x7f, 0xd7, 0x4d, 0x08, 0x00, 0x00, } func (m *EventOrderCreated) Marshal() (dAtA []byte, err error) { @@ -1048,7 +1095,12 @@ func (m *EventOrderCreated) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.ExternalId) i = encodeVarintEvents(dAtA, i, uint64(len(m.ExternalId))) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x22 + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x18 } if len(m.OrderType) > 0 { i -= len(m.OrderType) @@ -1090,7 +1142,12 @@ func (m *EventOrderCancelled) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.ExternalId) i = encodeVarintEvents(dAtA, i, uint64(len(m.ExternalId))) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x22 + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x18 } if len(m.CancelledBy) > 0 { i -= len(m.CancelledBy) @@ -1132,7 +1189,12 @@ func (m *EventOrderFilled) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.ExternalId) i = encodeVarintEvents(dAtA, i, uint64(len(m.ExternalId))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x32 + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x28 } if len(m.Fees) > 0 { i -= len(m.Fees) @@ -1188,7 +1250,12 @@ func (m *EventOrderPartiallyFilled) MarshalToSizedBuffer(dAtA []byte) (int, erro copy(dAtA[i:], m.ExternalId) i = encodeVarintEvents(dAtA, i, uint64(len(m.ExternalId))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x32 + } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x28 } if len(m.Fees) > 0 { i -= len(m.Fees) @@ -1246,6 +1313,11 @@ func (m *EventOrderExternalIDUpdated) MarshalToSizedBuffer(dAtA []byte) (int, er i-- dAtA[i] = 0x1a } + if m.MarketId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketId)) + i-- + dAtA[i] = 0x10 + } if m.OrderId != 0 { i = encodeVarintEvents(dAtA, i, uint64(m.OrderId)) i-- @@ -1651,6 +1723,9 @@ func (m *EventOrderCreated) Size() (n int) { if l > 0 { n += 1 + l + sovEvents(uint64(l)) } + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } l = len(m.ExternalId) if l > 0 { n += 1 + l + sovEvents(uint64(l)) @@ -1671,6 +1746,9 @@ func (m *EventOrderCancelled) Size() (n int) { if l > 0 { n += 1 + l + sovEvents(uint64(l)) } + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } l = len(m.ExternalId) if l > 0 { n += 1 + l + sovEvents(uint64(l)) @@ -1699,6 +1777,9 @@ func (m *EventOrderFilled) Size() (n int) { if l > 0 { n += 1 + l + sovEvents(uint64(l)) } + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } l = len(m.ExternalId) if l > 0 { n += 1 + l + sovEvents(uint64(l)) @@ -1727,6 +1808,9 @@ func (m *EventOrderPartiallyFilled) Size() (n int) { if l > 0 { n += 1 + l + sovEvents(uint64(l)) } + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } l = len(m.ExternalId) if l > 0 { n += 1 + l + sovEvents(uint64(l)) @@ -1743,6 +1827,9 @@ func (m *EventOrderExternalIDUpdated) Size() (n int) { if m.OrderId != 0 { n += 1 + sovEvents(uint64(m.OrderId)) } + if m.MarketId != 0 { + n += 1 + sovEvents(uint64(m.MarketId)) + } l = len(m.ExternalId) if l > 0 { n += 1 + l + sovEvents(uint64(l)) @@ -2006,6 +2093,25 @@ func (m *EventOrderCreated) Unmarshal(dAtA []byte) error { m.OrderType = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) } @@ -2139,6 +2245,25 @@ func (m *EventOrderCancelled) Unmarshal(dAtA []byte) error { m.CancelledBy = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) } @@ -2336,6 +2461,25 @@ func (m *EventOrderFilled) Unmarshal(dAtA []byte) error { m.Fees = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) } @@ -2533,6 +2677,25 @@ func (m *EventOrderPartiallyFilled) Unmarshal(dAtA []byte) error { m.Fees = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) } @@ -2633,6 +2796,25 @@ func (m *EventOrderExternalIDUpdated) Unmarshal(dAtA []byte) error { break } } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketId", wireType) + } + m.MarketId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketId |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExternalId", wireType) diff --git a/x/exchange/events_test.go b/x/exchange/events_test.go index cdd4ca14d2..43a19626b4 100644 --- a/x/exchange/events_test.go +++ b/x/exchange/events_test.go @@ -29,8 +29,12 @@ func assertEverythingSet(t *testing.T, tev proto.Message, typeString string) boo for i, attr := range event.Attributes { rv = assert.NotEmpty(t, attr.Key, "%T event.attributes[%d].Key", tev, i) && rv rv = assert.NotEqual(t, `""`, string(attr.Key), "%T event.attributes[%d].Key", tev, i) && rv + rv = assert.NotEqual(t, `0`, string(attr.Key), "%T event.attributes[%d].Key", tev, i) && rv + rv = assert.NotEqual(t, `"0"`, string(attr.Key), "%T event.attributes[%d].Key", tev, i) && rv rv = assert.NotEmpty(t, attr.Value, "%T event.attributes[%d].Value", tev, i) && rv rv = assert.NotEqual(t, `""`, string(attr.Value), "%T event.attributes[%d].Value", tev, i) && rv + rv = assert.NotEqual(t, `0`, string(attr.Value), "%T event.attributes[%d].Value", tev, i) && rv + rv = assert.NotEqual(t, `"0"`, string(attr.Value), "%T event.attributes[%d].Value", tev, i) && rv } return rv } @@ -49,19 +53,21 @@ func TestNewEventOrderCreated(t *testing.T) { }, { name: "order with ask", - order: NewOrder(1).WithAsk(&AskOrder{ExternalId: "oneoneone"}), + order: NewOrder(1).WithAsk(&AskOrder{MarketId: 97, ExternalId: "oneoneone"}), expected: &EventOrderCreated{ OrderId: 1, OrderType: "ask", + MarketId: 97, ExternalId: "oneoneone", }, }, { name: "order with bid", - order: NewOrder(2).WithBid(&BidOrder{ExternalId: "twotwotwo"}), + order: NewOrder(2).WithBid(&BidOrder{MarketId: 33, ExternalId: "twotwotwo"}), expected: &EventOrderCreated{ OrderId: 2, OrderType: "bid", + MarketId: 33, ExternalId: "twotwotwo", }, }, @@ -92,21 +98,23 @@ func TestNewEventOrderCancelled(t *testing.T) { }{ { name: "ask order", - order: NewOrder(11).WithAsk(&AskOrder{ExternalId: "an external identifier"}), + order: NewOrder(11).WithAsk(&AskOrder{MarketId: 71, ExternalId: "an external identifier"}), cancelledBy: sdk.AccAddress("CancelledBy_________"), expected: &EventOrderCancelled{ OrderId: 11, CancelledBy: sdk.AccAddress("CancelledBy_________").String(), + MarketId: 71, ExternalId: "an external identifier", }, }, { name: "bid order", - order: NewOrder(55).WithAsk(&AskOrder{ExternalId: "another external identifier"}), + order: NewOrder(55).WithAsk(&AskOrder{MarketId: 88, ExternalId: "another external identifier"}), cancelledBy: sdk.AccAddress("cancelled_by________"), expected: &EventOrderCancelled{ OrderId: 55, CancelledBy: sdk.AccAddress("cancelled_by________").String(), + MarketId: 88, ExternalId: "another external identifier", }, }, @@ -139,6 +147,7 @@ func TestNewEventOrderFilled(t *testing.T) { { name: "ask", order: NewOrder(4).WithAsk(&AskOrder{ + MarketId: 57, Assets: sdk.NewInt64Coin("apple", 22), Price: sdk.NewInt64Coin("plum", 18), SellerSettlementFlatFee: coinP("fig", 57), @@ -149,12 +158,14 @@ func TestNewEventOrderFilled(t *testing.T) { Assets: "22apple", Price: "18plum", Fees: "57fig", + MarketId: 57, ExternalId: "one", }, }, { name: "filled ask", order: NewFilledOrder(NewOrder(4).WithAsk(&AskOrder{ + MarketId: 1234, Assets: sdk.NewInt64Coin("apple", 22), Price: sdk.NewInt64Coin("plum", 18), SellerSettlementFlatFee: coinP("fig", 57), @@ -165,12 +176,14 @@ func TestNewEventOrderFilled(t *testing.T) { Assets: "22apple", Price: "88plum", Fees: "61fig,12grape", + MarketId: 1234, ExternalId: "two", }, }, { name: "bid", order: NewOrder(104).WithBid(&BidOrder{ + MarketId: 87878, Assets: sdk.NewInt64Coin("apple", 23), Price: sdk.NewInt64Coin("plum", 19), BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 58)), @@ -181,12 +194,14 @@ func TestNewEventOrderFilled(t *testing.T) { Assets: "23apple", Price: "19plum", Fees: "58fig", + MarketId: 87878, ExternalId: "three", }, }, { name: "filled bid", order: NewFilledOrder(NewOrder(105).WithBid(&BidOrder{ + MarketId: 9119, Assets: sdk.NewInt64Coin("apple", 24), Price: sdk.NewInt64Coin("plum", 20), BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 59)), @@ -197,6 +212,7 @@ func TestNewEventOrderFilled(t *testing.T) { Assets: "24apple", Price: "89plum", Fees: "62fig,13grape", + MarketId: 9119, ExternalId: "four", }, }, @@ -229,6 +245,7 @@ func TestNewEventOrderPartiallyFilled(t *testing.T) { { name: "ask", order: NewOrder(4).WithAsk(&AskOrder{ + MarketId: 432, Assets: sdk.NewInt64Coin("apple", 22), Price: sdk.NewInt64Coin("plum", 18), SellerSettlementFlatFee: coinP("fig", 57), @@ -239,12 +256,14 @@ func TestNewEventOrderPartiallyFilled(t *testing.T) { Assets: "22apple", Price: "18plum", Fees: "57fig", + MarketId: 432, ExternalId: "five", }, }, { name: "filled ask", order: NewFilledOrder(NewOrder(4).WithAsk(&AskOrder{ + MarketId: 456, Assets: sdk.NewInt64Coin("apple", 22), Price: sdk.NewInt64Coin("plum", 18), SellerSettlementFlatFee: coinP("fig", 57), @@ -255,12 +274,14 @@ func TestNewEventOrderPartiallyFilled(t *testing.T) { Assets: "22apple", Price: "88plum", Fees: "61fig,12grape", + MarketId: 456, ExternalId: "six", }, }, { name: "bid", order: NewOrder(104).WithBid(&BidOrder{ + MarketId: 765, Assets: sdk.NewInt64Coin("apple", 23), Price: sdk.NewInt64Coin("plum", 19), BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 58)), @@ -271,12 +292,14 @@ func TestNewEventOrderPartiallyFilled(t *testing.T) { Assets: "23apple", Price: "19plum", Fees: "58fig", + MarketId: 765, ExternalId: "seven", }, }, { name: "filled bid", order: NewFilledOrder(NewOrder(104).WithBid(&BidOrder{ + MarketId: 818, Assets: sdk.NewInt64Coin("apple", 23), Price: sdk.NewInt64Coin("plum", 19), BuyerSettlementFees: sdk.NewCoins(sdk.NewInt64Coin("fig", 58)), @@ -287,6 +310,7 @@ func TestNewEventOrderPartiallyFilled(t *testing.T) { Assets: "23apple", Price: "89plum", Fees: "62fig,13grape", + MarketId: 818, ExternalId: "eight", }, }, @@ -313,17 +337,19 @@ func TestNewEventOrderExternalIDUpdated(t *testing.T) { }{ { name: "ask", - order: NewOrder(51).WithAsk(&AskOrder{ExternalId: "orange-red"}), + order: NewOrder(51).WithAsk(&AskOrder{MarketId: 9, ExternalId: "orange-red"}), expected: &EventOrderExternalIDUpdated{ OrderId: 51, + MarketId: 9, ExternalId: "orange-red", }, }, { name: "bid", - order: NewOrder(777).WithAsk(&AskOrder{ExternalId: "purple-purple"}), + order: NewOrder(777).WithAsk(&AskOrder{MarketId: 53, ExternalId: "purple-purple"}), expected: &EventOrderExternalIDUpdated{ OrderId: 777, + MarketId: 53, ExternalId: "purple-purple", }, }, @@ -600,11 +626,12 @@ func TestTypedEventToEvent(t *testing.T) { }{ { name: "EventOrderCreated ask", - tev: NewEventOrderCreated(NewOrder(1).WithAsk(&AskOrder{ExternalId: "stuff"})), + tev: NewEventOrderCreated(NewOrder(1).WithAsk(&AskOrder{MarketId: 88, ExternalId: "stuff"})), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderCreated", Attributes: []abci.EventAttribute{ {Key: []byte("external_id"), Value: quoteBz("stuff")}, + {Key: []byte("market_id"), Value: []byte("88")}, {Key: []byte("order_id"), Value: quoteBz("1")}, {Key: []byte("order_type"), Value: quoteBz("ask")}, }, @@ -612,11 +639,12 @@ func TestTypedEventToEvent(t *testing.T) { }, { name: "EventOrderCreated bid", - tev: NewEventOrderCreated(NewOrder(2).WithBid(&BidOrder{ExternalId: "something else"})), + tev: NewEventOrderCreated(NewOrder(2).WithBid(&BidOrder{MarketId: 77, ExternalId: "something else"})), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderCreated", Attributes: []abci.EventAttribute{ {Key: []byte("external_id"), Value: quoteBz("something else")}, + {Key: []byte("market_id"), Value: []byte("77")}, {Key: []byte("order_id"), Value: quoteBz("2")}, {Key: []byte("order_type"), Value: quoteBz("bid")}, }, @@ -624,24 +652,26 @@ func TestTypedEventToEvent(t *testing.T) { }, { name: "EventOrderCancelled ask", - tev: NewEventOrderCancelled(NewOrder(3).WithAsk(&AskOrder{ExternalId: "outside 8"}), cancelledBy), + tev: NewEventOrderCancelled(NewOrder(3).WithAsk(&AskOrder{MarketId: 66, ExternalId: "outside 8"}), cancelledBy), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderCancelled", Attributes: []abci.EventAttribute{ {Key: []byte("cancelled_by"), Value: cancelledByQ}, {Key: []byte("external_id"), Value: quoteBz("outside 8")}, + {Key: []byte("market_id"), Value: []byte("66")}, {Key: []byte("order_id"), Value: quoteBz("3")}, }, }, }, { name: "EventOrderCancelled bid", - tev: NewEventOrderCancelled(NewOrder(3).WithBid(&BidOrder{ExternalId: "outside 8"}), cancelledBy), + tev: NewEventOrderCancelled(NewOrder(3).WithBid(&BidOrder{MarketId: 55, ExternalId: "outside 8"}), cancelledBy), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderCancelled", Attributes: []abci.EventAttribute{ {Key: []byte("cancelled_by"), Value: cancelledByQ}, {Key: []byte("external_id"), Value: quoteBz("outside 8")}, + {Key: []byte("market_id"), Value: []byte("55")}, {Key: []byte("order_id"), Value: quoteBz("3")}, }, }, @@ -649,6 +679,7 @@ func TestTypedEventToEvent(t *testing.T) { { name: "EventOrderFilled ask", tev: NewEventOrderFilled(NewOrder(4).WithAsk(&AskOrder{ + MarketId: 33, Assets: acoin, Price: pcoin, SellerSettlementFlatFee: &fcoin, @@ -660,6 +691,7 @@ func TestTypedEventToEvent(t *testing.T) { {Key: []byte("assets"), Value: acoinQ}, {Key: []byte("external_id"), Value: quoteBz("eeeeiiiiiddddd")}, {Key: []byte("fees"), Value: fcoinQ}, + {Key: []byte("market_id"), Value: []byte("33")}, {Key: []byte("order_id"), Value: quoteBz("4")}, {Key: []byte("price"), Value: pcoinQ}, }, @@ -668,6 +700,7 @@ func TestTypedEventToEvent(t *testing.T) { { name: "EventOrderFilled bid", tev: NewEventOrderFilled(NewOrder(104).WithBid(&BidOrder{ + MarketId: 44, Assets: acoin, Price: pcoin, BuyerSettlementFees: sdk.Coins{fcoin}, @@ -679,6 +712,7 @@ func TestTypedEventToEvent(t *testing.T) { {Key: []byte("assets"), Value: acoinQ}, {Key: []byte("external_id"), Value: quoteBz("that one thing")}, {Key: []byte("fees"), Value: fcoinQ}, + {Key: []byte("market_id"), Value: []byte("44")}, {Key: []byte("order_id"), Value: quoteBz("104")}, {Key: []byte("price"), Value: pcoinQ}, }, @@ -687,6 +721,7 @@ func TestTypedEventToEvent(t *testing.T) { { name: "EventOrderPartiallyFilled ask", tev: NewEventOrderPartiallyFilled(NewOrder(5).WithAsk(&AskOrder{ + MarketId: 22, Assets: acoin, Price: pcoin, SellerSettlementFlatFee: &fcoin, @@ -698,6 +733,7 @@ func TestTypedEventToEvent(t *testing.T) { {Key: []byte("assets"), Value: acoinQ}, {Key: []byte("external_id"), Value: quoteBz("12345")}, {Key: []byte("fees"), Value: fcoinQ}, + {Key: []byte("market_id"), Value: []byte("22")}, {Key: []byte("order_id"), Value: quoteBz("5")}, {Key: []byte("price"), Value: pcoinQ}, }, @@ -706,6 +742,7 @@ func TestTypedEventToEvent(t *testing.T) { { name: "EventOrderPartiallyFilled bid", tev: NewEventOrderPartiallyFilled(NewOrder(5).WithBid(&BidOrder{ + MarketId: 11, Assets: acoin, Price: pcoin, BuyerSettlementFees: sdk.Coins{fcoin}, @@ -717,6 +754,7 @@ func TestTypedEventToEvent(t *testing.T) { {Key: []byte("assets"), Value: acoinQ}, {Key: []byte("external_id"), Value: quoteBz("67890")}, {Key: []byte("fees"), Value: fcoinQ}, + {Key: []byte("market_id"), Value: []byte("11")}, {Key: []byte("order_id"), Value: quoteBz("5")}, {Key: []byte("price"), Value: pcoinQ}, }, @@ -724,22 +762,24 @@ func TestTypedEventToEvent(t *testing.T) { }, { name: "EventOrderExternalIDUpdated ask", - tev: NewEventOrderExternalIDUpdated(NewOrder(8).WithAsk(&AskOrder{ExternalId: "yellow"})), + tev: NewEventOrderExternalIDUpdated(NewOrder(8).WithAsk(&AskOrder{MarketId: 99, ExternalId: "yellow"})), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderExternalIDUpdated", Attributes: []abci.EventAttribute{ {Key: []byte("external_id"), Value: quoteBz("yellow")}, + {Key: []byte("market_id"), Value: []byte("99")}, {Key: []byte("order_id"), Value: quoteBz("8")}, }, }, }, { name: "EventOrderExternalIDUpdated bid", - tev: NewEventOrderExternalIDUpdated(NewOrder(8).WithBid(&BidOrder{ExternalId: "yellow"})), + tev: NewEventOrderExternalIDUpdated(NewOrder(8).WithBid(&BidOrder{MarketId: 111, ExternalId: "yellow"})), expEvent: sdk.Event{ Type: "provenance.exchange.v1.EventOrderExternalIDUpdated", Attributes: []abci.EventAttribute{ {Key: []byte("external_id"), Value: quoteBz("yellow")}, + {Key: []byte("market_id"), Value: []byte("111")}, {Key: []byte("order_id"), Value: quoteBz("8")}, }, }, From 714139ddcbab1c97e308bdca245646a1df1ce85f Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 13 Oct 2023 14:04:21 -0600 Subject: [PATCH 294/309] [1658]: Move the sumAssetsAndPrice calls and validation to before the orders loop in both FillAsks and FillBids. --- x/exchange/keeper/fulfillment.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index e6733c48bc..a52d4c5365 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -58,6 +58,12 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro return oerrs } + totalAssets, totalPrice := sumAssetsAndPrice(orders) + + if !exchange.CoinsEquals(totalAssets, msg.TotalAssets) { + return fmt.Errorf("total assets %q does not equal sum of bid order assets %q", msg.TotalAssets, totalAssets) + } + var errs []error var totalSellerFee sdk.Coins assetOutputs := make([]banktypes.Output, 0, len(msg.BidOrderIds)) @@ -115,12 +121,6 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro return errors.Join(errs...) } - totalAssets, totalPrice := sumAssetsAndPrice(orders) - - if !exchange.CoinsEquals(totalAssets, msg.TotalAssets) { - return fmt.Errorf("total assets %q does not equal sum of bid order assets %q", msg.TotalAssets, totalAssets) - } - if msg.SellerSettlementFlatFee != nil { totalSellerFee = totalSellerFee.Add(*msg.SellerSettlementFlatFee) } @@ -189,6 +189,12 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro return oerrs } + totalAssets, totalPrice := sumAssetsAndPrice(orders) + + if !exchange.CoinsEquals(totalPrice, sdk.Coins{msg.TotalPrice}) { + return fmt.Errorf("total price %q does not equal sum of ask order prices %q", msg.TotalPrice, totalPrice) + } + var errs []error assetInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)) priceOutputs := make([]banktypes.Output, 0, len(msg.AskOrderIds)) @@ -249,12 +255,6 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro return errors.Join(errs...) } - totalAssets, totalPrice := sumAssetsAndPrice(orders) - - if !exchange.CoinsEquals(totalPrice, sdk.Coins{msg.TotalPrice}) { - return fmt.Errorf("total price %q does not equal sum of ask order prices %q", msg.TotalPrice, totalPrice) - } - if !msg.BuyerSettlementFees.IsZero() { feeInputs = append(feeInputs, banktypes.Input{Address: msg.Buyer, Coins: msg.BuyerSettlementFees}) } From 043954c2ec815b41793e52ecfbd3c4ee95570ea7 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 13 Oct 2023 14:28:49 -0600 Subject: [PATCH 295/309] [1658]: Make IndexedAddrAmts public. --- x/exchange/fulfillment.go | 42 +++++------ x/exchange/fulfillment_test.go | 126 ++++++++++++++++----------------- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index eb4c69cfdd..36a6b2fdd3 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -84,8 +84,8 @@ func BuildSettlement(askOrders, bidOrders []*Order, sellerFeeRatioLookup func(de return settlement, nil } -// indexedAddrAmts is a set of addresses and amounts. -type indexedAddrAmts struct { +// IndexedAddrAmts is a set of addresses and amounts. +type IndexedAddrAmts struct { // addrs are a list of all addresses that have amounts. addrs []string // amts are a list of the coin amounts for each address (by slice index). @@ -94,15 +94,15 @@ type indexedAddrAmts struct { indexes map[string]int } -func newIndexedAddrAmts() *indexedAddrAmts { - return &indexedAddrAmts{ +func NewIndexedAddrAmts() *IndexedAddrAmts { + return &IndexedAddrAmts{ indexes: make(map[string]int), } } -// add adds the coins to the given address. +// Add adds the coins to the given address. // Panics if a provided coin is invalid. -func (i *indexedAddrAmts) add(addr string, coins ...sdk.Coin) { +func (i *IndexedAddrAmts) Add(addr string, coins ...sdk.Coin) { for _, coin := range coins { if err := coin.Validate(); err != nil { panic(fmt.Errorf("cannot index and add invalid coin amount %q", coin)) @@ -118,9 +118,9 @@ func (i *indexedAddrAmts) add(addr string, coins ...sdk.Coin) { i.amts[n] = i.amts[n].Add(coins...) } -// getAsInputs returns all the entries as bank Inputs. +// GetAsInputs returns all the entries as bank Inputs. // Panics if this is nil, has no addrs, or has a negative coin amount. -func (i *indexedAddrAmts) getAsInputs() []banktypes.Input { +func (i *IndexedAddrAmts) GetAsInputs() []banktypes.Input { if i == nil || len(i.addrs) == 0 { return nil } @@ -134,9 +134,9 @@ func (i *indexedAddrAmts) getAsInputs() []banktypes.Input { return rv } -// getAsOutputs returns all the entries as bank Outputs. +// GetAsOutputs returns all the entries as bank Outputs. // Panics if this is nil, has no addrs, or has a negative coin amount. -func (i *indexedAddrAmts) getAsOutputs() []banktypes.Output { +func (i *IndexedAddrAmts) GetAsOutputs() []banktypes.Output { if i == nil || len(i.addrs) == 0 { return nil } @@ -728,7 +728,7 @@ func (f orderFulfillment) Validate() (err error) { // This will populate the Transfers and FeeInputs fields in the provided Settlement. func buildTransfers(askOFs, bidOFs []*orderFulfillment, settlement *Settlement) error { var errs []error - indexedFees := newIndexedAddrAmts() + indexedFees := NewIndexedAddrAmts() transfers := make([]*Transfer, 0, len(askOFs)+len(bidOFs)) record := func(of *orderFulfillment, getter func(fulfillment *orderFulfillment) (*Transfer, error)) { @@ -744,7 +744,7 @@ func buildTransfers(askOFs, bidOFs []*orderFulfillment, settlement *Settlement) errs = append(errs, fmt.Errorf("%s order %d cannot pay %q in fees: negative amount", of.GetOrderType(), of.GetOrderID(), of.FeesToPay)) } else { - indexedFees.add(of.GetOwner(), of.FeesToPay...) + indexedFees.Add(of.GetOwner(), of.FeesToPay...) } } } @@ -770,7 +770,7 @@ func buildTransfers(askOFs, bidOFs []*orderFulfillment, settlement *Settlement) } settlement.Transfers = transfers - settlement.FeeInputs = indexedFees.getAsInputs() + settlement.FeeInputs = indexedFees.GetAsInputs() return nil } @@ -805,14 +805,14 @@ func getAssetTransfer(f *orderFulfillment) (*Transfer, error) { f.GetOrderType(), f.GetOrderID(), assetsFilled) } - indexedDists := newIndexedAddrAmts() + indexedDists := NewIndexedAddrAmts() sumDists := sdkmath.ZeroInt() for _, dist := range f.AssetDists { if !dist.Amount.IsPositive() { return nil, fmt.Errorf("%s order %d cannot have %q assets in a transfer: amount not positive", f.GetOrderType(), f.GetOrderID(), f.AssetCoin(dist.Amount)) } - indexedDists.add(dist.Address, f.AssetCoin(dist.Amount)) + indexedDists.Add(dist.Address, f.AssetCoin(dist.Amount)) sumDists = sumDists.Add(dist.Amount) } @@ -824,12 +824,12 @@ func getAssetTransfer(f *orderFulfillment) (*Transfer, error) { if f.IsAskOrder() { return &Transfer{ Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(assetsFilled)}}, - Outputs: indexedDists.getAsOutputs(), + Outputs: indexedDists.GetAsOutputs(), }, nil } if f.IsBidOrder() { return &Transfer{ - Inputs: indexedDists.getAsInputs(), + Inputs: indexedDists.GetAsInputs(), Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(assetsFilled)}}, }, nil } @@ -846,14 +846,14 @@ func getPriceTransfer(f *orderFulfillment) (*Transfer, error) { f.GetOrderType(), f.GetOrderID(), priceApplied) } - indexedDists := newIndexedAddrAmts() + indexedDists := NewIndexedAddrAmts() sumDists := sdkmath.ZeroInt() for _, dist := range f.PriceDists { if !dist.Amount.IsPositive() { return nil, fmt.Errorf("%s order %d cannot have price %q in a transfer: amount not positive", f.GetOrderType(), f.GetOrderID(), f.PriceCoin(dist.Amount)) } - indexedDists.add(dist.Address, f.PriceCoin(dist.Amount)) + indexedDists.Add(dist.Address, f.PriceCoin(dist.Amount)) sumDists = sumDists.Add(dist.Amount) } @@ -864,14 +864,14 @@ func getPriceTransfer(f *orderFulfillment) (*Transfer, error) { if f.IsAskOrder() { return &Transfer{ - Inputs: indexedDists.getAsInputs(), + Inputs: indexedDists.GetAsInputs(), Outputs: []banktypes.Output{{Address: f.GetOwner(), Coins: sdk.NewCoins(priceApplied)}}, }, nil } if f.IsBidOrder() { return &Transfer{ Inputs: []banktypes.Input{{Address: f.GetOwner(), Coins: sdk.NewCoins(priceApplied)}}, - Outputs: indexedDists.getAsOutputs(), + Outputs: indexedDists.GetAsOutputs(), }, nil } diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index cb4b94ba4b..61402f0b0f 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -65,13 +65,13 @@ func copyOrderFulfillments(fs []*orderFulfillment) []*orderFulfillment { return copySlice(fs, copyOrderFulfillment) } -// copyIndexedAddrAmts creates a deep copy of an indexedAddrAmts. -func copyIndexedAddrAmts(orig *indexedAddrAmts) *indexedAddrAmts { +// copyIndexedAddrAmts creates a deep copy of an IndexedAddrAmts. +func copyIndexedAddrAmts(orig *IndexedAddrAmts) *IndexedAddrAmts { if orig == nil { return nil } - rv := &indexedAddrAmts{ + rv := &IndexedAddrAmts{ addrs: nil, amts: nil, indexes: nil, @@ -168,7 +168,7 @@ func transferString(t *Transfer) string { // String converts a indexedAddrAmtsString to a string. // This is mostly because test failure output of sdk.Coin and sdk.Coins is impossible to understand. -func indexedAddrAmtsString(i *indexedAddrAmts) string { +func indexedAddrAmtsString(i *IndexedAddrAmts) string { if i == nil { return "nil" } @@ -1017,13 +1017,13 @@ func TestBuildSettlement(t *testing.T) { } func TestNewIndexedAddrAmts(t *testing.T) { - expected := &indexedAddrAmts{ + expected := &IndexedAddrAmts{ addrs: nil, amts: nil, indexes: make(map[string]int), } - actual := newIndexedAddrAmts() - assert.Equal(t, expected, actual, "newIndexedAddrAmts result") + actual := NewIndexedAddrAmts() + assert.Equal(t, expected, actual, "NewIndexedAddrAmts result") key := "test" require.NotPanics(t, func() { _ = actual.indexes[key] @@ -1040,18 +1040,18 @@ func TestIndexedAddrAmts_Add(t *testing.T) { tests := []struct { name string - receiver *indexedAddrAmts + receiver *IndexedAddrAmts addr string coins []sdk.Coin - expected *indexedAddrAmts + expected *IndexedAddrAmts expPanic string }{ { name: "empty, add one coin", - receiver: newIndexedAddrAmts(), + receiver: NewIndexedAddrAmts(), addr: "addr1", coins: coins("1one"), - expected: &indexedAddrAmts{ + expected: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{coins("1one")}, indexes: map[string]int{"addr1": 0}, @@ -1059,10 +1059,10 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "empty, add two coins", - receiver: newIndexedAddrAmts(), + receiver: NewIndexedAddrAmts(), addr: "addr1", coins: coins("1one,2two"), - expected: &indexedAddrAmts{ + expected: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{coins("1one,2two")}, indexes: map[string]int{"addr1": 0}, @@ -1070,21 +1070,21 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "empty, add neg coins", - receiver: newIndexedAddrAmts(), + receiver: NewIndexedAddrAmts(), addr: "addr1", coins: negCoins, expPanic: "cannot index and add invalid coin amount \"-1neg\"", }, { name: "one addr, add to existing new denom", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{coins("1one")}, indexes: map[string]int{"addr1": 0}, }, addr: "addr1", coins: coins("2two"), - expected: &indexedAddrAmts{ + expected: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{coins("1one,2two")}, indexes: map[string]int{"addr1": 0}, @@ -1092,14 +1092,14 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "one addr, add to existing same denom", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{coins("1one")}, indexes: map[string]int{"addr1": 0}, }, addr: "addr1", coins: coins("3one"), - expected: &indexedAddrAmts{ + expected: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{coins("4one")}, indexes: map[string]int{"addr1": 0}, @@ -1107,7 +1107,7 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "one addr, add negative to existing", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{coins("1one")}, indexes: map[string]int{"addr1": 0}, @@ -1118,14 +1118,14 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "one addr, add to new", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{coins("1one")}, indexes: map[string]int{"addr1": 0}, }, addr: "addr2", coins: coins("2two"), - expected: &indexedAddrAmts{ + expected: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2"}, amts: []sdk.Coins{coins("1one"), coins("2two")}, indexes: map[string]int{"addr1": 0, "addr2": 1}, @@ -1133,14 +1133,14 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "one addr, add to new opposite order", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr2"}, amts: []sdk.Coins{coins("2two")}, indexes: map[string]int{"addr2": 0}, }, addr: "addr1", coins: coins("1one"), - expected: &indexedAddrAmts{ + expected: &IndexedAddrAmts{ addrs: []string{"addr2", "addr1"}, amts: []sdk.Coins{coins("2two"), coins("1one")}, indexes: map[string]int{"addr2": 0, "addr1": 1}, @@ -1148,7 +1148,7 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "one addr, add negative to new", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{coins("1one")}, indexes: map[string]int{"addr1": 0}, @@ -1159,14 +1159,14 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "three addrs, add to first", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, }, addr: "addr1", coins: coins("10one"), - expected: &indexedAddrAmts{ + expected: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("11one"), coins("2two"), coins("3three")}, indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, @@ -1174,14 +1174,14 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "three addrs, add to second", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, }, addr: "addr2", coins: coins("10two"), - expected: &indexedAddrAmts{ + expected: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("1one"), coins("12two"), coins("3three")}, indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, @@ -1189,14 +1189,14 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "three addrs, add to third", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, }, addr: "addr3", coins: coins("10three"), - expected: &indexedAddrAmts{ + expected: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("1one"), coins("2two"), coins("13three")}, indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, @@ -1204,14 +1204,14 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "three addrs, add two coins to second", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, }, addr: "addr2", coins: coins("10four,20two"), - expected: &indexedAddrAmts{ + expected: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("1one"), coins("10four,22two"), coins("3three")}, indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, @@ -1219,14 +1219,14 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "three addrs, add to new", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, }, addr: "good buddy", coins: coins("10four"), - expected: &indexedAddrAmts{ + expected: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3", "good buddy"}, amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three"), coins("10four")}, indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2, "good buddy": 3}, @@ -1234,7 +1234,7 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "three addrs, add negative to second", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, @@ -1245,7 +1245,7 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }, { name: "three addrs, add negative to new", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, @@ -1268,11 +1268,11 @@ func TestIndexedAddrAmts_Add(t *testing.T) { }() testFunc := func() { - tc.receiver.add(tc.addr, tc.coins...) + tc.receiver.Add(tc.addr, tc.coins...) } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "add(%q, %q)", tc.addr, tc.coins) + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "Add(%q, %q)", tc.addr, tc.coins) if len(tc.expPanic) == 0 { - assert.Equal(t, tc.expected, tc.receiver, "receiver after add(%q, %q)", tc.addr, tc.coins) + assert.Equal(t, tc.expected, tc.receiver, "receiver after Add(%q, %q)", tc.addr, tc.coins) } }) } @@ -1287,15 +1287,15 @@ func TestIndexedAddrAmts_GetAsInputs(t *testing.T) { tests := []struct { name string - receiver *indexedAddrAmts + receiver *IndexedAddrAmts expected []banktypes.Input expPanic string }{ {name: "nil receiver", receiver: nil, expected: nil}, - {name: "no addrs", receiver: newIndexedAddrAmts(), expected: nil}, + {name: "no addrs", receiver: NewIndexedAddrAmts(), expected: nil}, { name: "one addr negative amount", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{{{Denom: "neg", Amount: sdkmath.NewInt(-1)}}}, indexes: map[string]int{ @@ -1306,7 +1306,7 @@ func TestIndexedAddrAmts_GetAsInputs(t *testing.T) { }, { name: "one addr zero amount", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{{{Denom: "zero", Amount: sdkmath.NewInt(0)}}}, indexes: map[string]int{ @@ -1317,7 +1317,7 @@ func TestIndexedAddrAmts_GetAsInputs(t *testing.T) { }, { name: "one addr positive amount", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{coins("1one")}, indexes: map[string]int{ @@ -1330,7 +1330,7 @@ func TestIndexedAddrAmts_GetAsInputs(t *testing.T) { }, { name: "two addrs", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2"}, amts: []sdk.Coins{coins("1one"), coins("2two,3three")}, indexes: map[string]int{ @@ -1345,7 +1345,7 @@ func TestIndexedAddrAmts_GetAsInputs(t *testing.T) { }, { name: "three addrs", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("1one"), coins("2two,3three"), coins("4four,5five,6six")}, indexes: map[string]int{ @@ -1362,7 +1362,7 @@ func TestIndexedAddrAmts_GetAsInputs(t *testing.T) { }, { name: "three addrs, negative in third", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{ coins("1one"), @@ -1389,11 +1389,11 @@ func TestIndexedAddrAmts_GetAsInputs(t *testing.T) { orig := copyIndexedAddrAmts(tc.receiver) var actual []banktypes.Input testFunc := func() { - actual = tc.receiver.getAsInputs() + actual = tc.receiver.GetAsInputs() } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAsInputs()") - assert.Equal(t, tc.expected, actual, "getAsInputs() result") - if !assert.Equal(t, orig, tc.receiver, "receiver before and after getAsInputs()") { + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetAsInputs()") + assert.Equal(t, tc.expected, actual, "GetAsInputs() result") + if !assert.Equal(t, orig, tc.receiver, "receiver before and after GetAsInputs()") { t.Logf("Before: %s", indexedAddrAmtsString(orig)) t.Logf(" After: %s", indexedAddrAmtsString(tc.receiver)) } @@ -1410,15 +1410,15 @@ func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { tests := []struct { name string - receiver *indexedAddrAmts + receiver *IndexedAddrAmts expected []banktypes.Output expPanic string }{ {name: "nil receiver", receiver: nil, expected: nil}, - {name: "no addrs", receiver: newIndexedAddrAmts(), expected: nil}, + {name: "no addrs", receiver: NewIndexedAddrAmts(), expected: nil}, { name: "one addr negative amount", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{{{Denom: "neg", Amount: sdkmath.NewInt(-1)}}}, indexes: map[string]int{ @@ -1429,7 +1429,7 @@ func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { }, { name: "one addr zero amount", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{{{Denom: "zero", Amount: sdkmath.NewInt(0)}}}, indexes: map[string]int{ @@ -1440,7 +1440,7 @@ func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { }, { name: "one addr positive amount", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1"}, amts: []sdk.Coins{coins("1one")}, indexes: map[string]int{ @@ -1453,7 +1453,7 @@ func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { }, { name: "two addrs", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2"}, amts: []sdk.Coins{coins("1one"), coins("2two,3three")}, indexes: map[string]int{ @@ -1468,7 +1468,7 @@ func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { }, { name: "three addrs", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{coins("1one"), coins("2two,3three"), coins("4four,5five,6six")}, indexes: map[string]int{ @@ -1485,7 +1485,7 @@ func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { }, { name: "three addrs, negative in third", - receiver: &indexedAddrAmts{ + receiver: &IndexedAddrAmts{ addrs: []string{"addr1", "addr2", "addr3"}, amts: []sdk.Coins{ coins("1one"), @@ -1512,11 +1512,11 @@ func TestIndexedAddrAmts_GetAsOutputs(t *testing.T) { orig := copyIndexedAddrAmts(tc.receiver) var actual []banktypes.Output testFunc := func() { - actual = tc.receiver.getAsOutputs() + actual = tc.receiver.GetAsOutputs() } - assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "getAsOutputs()") - assert.Equal(t, tc.expected, actual, "getAsOutputs() result") - if !assert.Equal(t, orig, tc.receiver, "receiver before and after getAsInputs()") { + assertions.RequirePanicEquals(t, testFunc, tc.expPanic, "GetAsOutputs()") + assert.Equal(t, tc.expected, actual, "GetAsOutputs() result") + if !assert.Equal(t, orig, tc.receiver, "receiver before and after GetAsInputs()") { t.Logf("Before: %s", indexedAddrAmtsString(orig)) t.Logf(" After: %s", indexedAddrAmtsString(tc.receiver)) } From 2dcbd450c4510d25af129a01251788a946eab0b4 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 13 Oct 2023 15:35:32 -0600 Subject: [PATCH 296/309] [1658]: Extract a closeSettlement method from the end of SettleOrders, and in FillAsks and FillBids, create a settlement and use that function. Use IndexedAddrAmts for identifying inputs and outputs in FillBids and FillAsks. --- x/exchange/keeper/fulfillment.go | 213 +++++++++++-------------------- 1 file changed, 75 insertions(+), 138 deletions(-) diff --git a/x/exchange/keeper/fulfillment.go b/x/exchange/keeper/fulfillment.go index a52d4c5365..8c8cda994c 100644 --- a/x/exchange/keeper/fulfillment.go +++ b/x/exchange/keeper/fulfillment.go @@ -59,88 +59,62 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro } totalAssets, totalPrice := sumAssetsAndPrice(orders) - if !exchange.CoinsEquals(totalAssets, msg.TotalAssets) { return fmt.Errorf("total assets %q does not equal sum of bid order assets %q", msg.TotalAssets, totalAssets) } - var errs []error var totalSellerFee sdk.Coins - assetOutputs := make([]banktypes.Output, 0, len(msg.BidOrderIds)) - priceInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)) - addrIndex := make(map[string]int, len(msg.BidOrderIds)) - feeInputs := make([]banktypes.Input, 0, len(msg.BidOrderIds)+1) - feeAddrIndex := make(map[string]int, len(msg.BidOrderIds)) - filledOrders := make([]*exchange.FilledOrder, 0, len(msg.BidOrderIds)) + if msg.SellerSettlementFlatFee != nil { + totalSellerFee = totalSellerFee.Add(*msg.SellerSettlementFlatFee) + } + + var errs []error + feeAddrIdx := exchange.NewIndexedAddrAmts() + assetsAddrIdx := exchange.NewIndexedAddrAmts() + priceAddrIdx := exchange.NewIndexedAddrAmts() + settlement := &exchange.Settlement{FullyFilledOrders: make([]*exchange.FilledOrder, 0, len(msg.BidOrderIds))} for _, order := range orders { bidOrder := order.GetBidOrder() buyer := bidOrder.Buyer assets := bidOrder.Assets price := bidOrder.Price - buyerSettlementFees := bidOrder.BuyerSettlementFees + buyerFees := bidOrder.BuyerSettlementFees - sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, price) + sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, order.GetPrice()) if rerr != nil { errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", order.OrderId, rerr)) - continue } - if err := k.releaseHoldOnOrder(ctx, order); err != nil { - errs = append(errs, err) - continue - } - if sellerRatioFee != nil { totalSellerFee = totalSellerFee.Add(*sellerRatioFee) } - i, seen := addrIndex[buyer] - if !seen { - i = len(assetOutputs) - addrIndex[buyer] = i - assetOutputs = append(assetOutputs, banktypes.Output{Address: buyer}) - priceInputs = append(priceInputs, banktypes.Input{Address: buyer}) - } - assetOutputs[i].Coins = assetOutputs[i].Coins.Add(assets) - priceInputs[i].Coins = priceInputs[i].Coins.Add(price) - - if !buyerSettlementFees.IsZero() { - j, known := feeAddrIndex[buyer] - if !known { - j = len(feeInputs) - feeAddrIndex[buyer] = j - feeInputs = append(feeInputs, banktypes.Input{Address: buyer}) - } - feeInputs[j].Coins = feeInputs[j].Coins.Add(buyerSettlementFees...) - } - - filledOrders = append(filledOrders, exchange.NewFilledOrder(order, price, buyerSettlementFees)) + assetsAddrIdx.Add(buyer, assets) + priceAddrIdx.Add(buyer, price) + feeAddrIdx.Add(buyer, buyerFees...) + settlement.FullyFilledOrders = append(settlement.FullyFilledOrders, exchange.NewFilledOrder(order, price, buyerFees)) } if len(errs) > 0 { return errors.Join(errs...) } - if msg.SellerSettlementFlatFee != nil { - totalSellerFee = totalSellerFee.Add(*msg.SellerSettlementFlatFee) - } - if !totalSellerFee.IsZero() { - feeInputs = append(feeInputs, banktypes.Input{Address: msg.Seller, Coins: totalSellerFee}) - } - - assetInputs := []banktypes.Input{{Address: msg.Seller, Coins: msg.TotalAssets}} - priceOutputs := []banktypes.Output{{Address: msg.Seller, Coins: totalPrice}} + feeAddrIdx.Add(msg.Seller, totalSellerFee...) - if err := k.DoTransfer(ctx, assetInputs, assetOutputs); err != nil { - return fmt.Errorf("error transferring assets from seller to buyers: %w", err) + settlement.Transfers = []*exchange.Transfer{ + { + Inputs: []banktypes.Input{{Address: msg.Seller, Coins: msg.TotalAssets}}, + Outputs: assetsAddrIdx.GetAsOutputs(), + }, + { + Inputs: priceAddrIdx.GetAsInputs(), + Outputs: []banktypes.Output{{Address: msg.Seller, Coins: totalPrice}}, + }, } + settlement.FeeInputs = feeAddrIdx.GetAsInputs() - if err := k.DoTransfer(ctx, priceInputs, priceOutputs); err != nil { - return fmt.Errorf("error transferring price from buyers to seller: %w", err) - } - - if err := k.CollectFees(ctx, marketID, feeInputs); err != nil { - return fmt.Errorf("error collecting settlement fees: %w", err) + if err := k.closeSettlement(ctx, store, marketID, settlement); err != nil { + return err } // Collected last so that it's easier for a seller to fill bids without needing those funds first. @@ -151,13 +125,6 @@ func (k Keeper) FillBids(ctx sdk.Context, msg *exchange.MsgFillBidsRequest) erro } } - events := make([]proto.Message, len(orders)) - for i, order := range filledOrders { - deleteAndDeIndexOrder(store, *order.GetOriginalOrder()) - events[i] = exchange.NewEventOrderFilled(order) - } - - k.emitEvents(ctx, events) return nil } @@ -173,10 +140,7 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro if err := validateAcceptingOrdersAndCanUserSettle(store, marketID); err != nil { return err } - buyer, serr := sdk.AccAddressFromBech32(msg.Buyer) - if serr != nil { - return fmt.Errorf("invalid buyer %q: %w", msg.Buyer, serr) - } + buyer := sdk.MustAccAddressFromBech32(msg.Buyer) if err := k.validateUserCanCreateBid(ctx, marketID, buyer); err != nil { return err } @@ -190,88 +154,58 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro } totalAssets, totalPrice := sumAssetsAndPrice(orders) - if !exchange.CoinsEquals(totalPrice, sdk.Coins{msg.TotalPrice}) { return fmt.Errorf("total price %q does not equal sum of ask order prices %q", msg.TotalPrice, totalPrice) } var errs []error - assetInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)) - priceOutputs := make([]banktypes.Output, 0, len(msg.AskOrderIds)) - addrIndex := make(map[string]int, len(msg.AskOrderIds)) - feeInputs := make([]banktypes.Input, 0, len(msg.AskOrderIds)+1) - feeAddrIndex := make(map[string]int, len(msg.AskOrderIds)) - filledOrders := make([]*exchange.FilledOrder, 0, len(msg.AskOrderIds)) + assetsAddrIdx := exchange.NewIndexedAddrAmts() + priceAddrIdx := exchange.NewIndexedAddrAmts() + feeAddrIdx := exchange.NewIndexedAddrAmts() + settlement := &exchange.Settlement{FullyFilledOrders: make([]*exchange.FilledOrder, 0, len(msg.AskOrderIds))} for _, order := range orders { askOrder := order.GetAskOrder() seller := askOrder.Seller assets := askOrder.Assets price := askOrder.Price - sellerSettlementFlatFee := askOrder.SellerSettlementFlatFee + sellerFees := askOrder.GetSettlementFees() sellerRatioFee, rerr := calculateSellerSettlementRatioFee(store, marketID, price) if rerr != nil { errs = append(errs, fmt.Errorf("error calculating seller settlement ratio fee for order %d: %w", order.OrderId, rerr)) - continue - } - if err := k.releaseHoldOnOrder(ctx, order); err != nil { - errs = append(errs, err) - continue } - - var totalSellerFee sdk.Coins - if sellerSettlementFlatFee != nil && !sellerSettlementFlatFee.IsZero() { - totalSellerFee = totalSellerFee.Add(*sellerSettlementFlatFee) - } - if sellerRatioFee != nil && !sellerRatioFee.IsZero() { - totalSellerFee = totalSellerFee.Add(*sellerRatioFee) - } - - i, seen := addrIndex[seller] - if !seen { - i = len(assetInputs) - addrIndex[seller] = i - assetInputs = append(assetInputs, banktypes.Input{Address: seller}) - priceOutputs = append(priceOutputs, banktypes.Output{Address: seller}) - } - assetInputs[i].Coins = assetInputs[i].Coins.Add(assets) - priceOutputs[i].Coins = priceOutputs[i].Coins.Add(price) - - if !totalSellerFee.IsZero() { - j, known := feeAddrIndex[seller] - if !known { - j = len(feeInputs) - feeAddrIndex[seller] = j - feeInputs = append(feeInputs, banktypes.Input{Address: seller}) - } - feeInputs[j].Coins = feeInputs[j].Coins.Add(totalSellerFee...) + if sellerRatioFee != nil { + sellerFees = sellerFees.Add(*sellerRatioFee) } - filledOrders = append(filledOrders, exchange.NewFilledOrder(order, price, totalSellerFee)) + assetsAddrIdx.Add(seller, assets) + priceAddrIdx.Add(seller, price) + feeAddrIdx.Add(seller, sellerFees...) + settlement.FullyFilledOrders = append(settlement.FullyFilledOrders, exchange.NewFilledOrder(order, price, sellerFees)) } if len(errs) > 0 { return errors.Join(errs...) } - if !msg.BuyerSettlementFees.IsZero() { - feeInputs = append(feeInputs, banktypes.Input{Address: msg.Buyer, Coins: msg.BuyerSettlementFees}) - } - - assetOutputs := []banktypes.Output{{Address: msg.Buyer, Coins: totalAssets}} - priceInputs := []banktypes.Input{{Address: msg.Buyer, Coins: sdk.Coins{msg.TotalPrice}}} - - if err := k.DoTransfer(ctx, assetInputs, assetOutputs); err != nil { - return fmt.Errorf("error transferring assets from sellers to buyer: %w", err) - } + // Done after the loop so that it's always last like it has to be in FillBids. + feeAddrIdx.Add(msg.Buyer, msg.BuyerSettlementFees...) - if err := k.DoTransfer(ctx, priceInputs, priceOutputs); err != nil { - return fmt.Errorf("error transferring price from buyer to sellers: %w", err) + settlement.Transfers = []*exchange.Transfer{ + { + Inputs: assetsAddrIdx.GetAsInputs(), + Outputs: []banktypes.Output{{Address: msg.Buyer, Coins: totalAssets}}, + }, + { + Inputs: []banktypes.Input{{Address: msg.Buyer, Coins: sdk.Coins{msg.TotalPrice}}}, + Outputs: priceAddrIdx.GetAsOutputs(), + }, } + settlement.FeeInputs = feeAddrIdx.GetAsInputs() - if err := k.CollectFees(ctx, marketID, feeInputs); err != nil { - return fmt.Errorf("error collecting settlement fees: %w", err) + if err := k.closeSettlement(ctx, store, marketID, settlement); err != nil { + return err } // Collected last so that it's easier for a seller to fill asks without needing those funds first. @@ -282,13 +216,6 @@ func (k Keeper) FillAsks(ctx sdk.Context, msg *exchange.MsgFillAsksRequest) erro } } - events := make([]proto.Message, len(orders)) - for i, order := range filledOrders { - deleteAndDeIndexOrder(store, *order.GetOriginalOrder()) - events[i] = exchange.NewEventOrderFilled(order) - } - - k.emitEvents(ctx, events) return nil } @@ -321,15 +248,21 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO return errors.New("settlement unexpectedly resulted in all orders fully filled") } + return k.closeSettlement(ctx, store, marketID, settlement) +} + +// closeSettlement does all the processing needed to complete a settlement. +// It releases all the holds, does all the transfers, collects the fees, deletes/updates the orders, and emits events. +func (k Keeper) closeSettlement(ctx sdk.Context, store sdk.KVStore, marketID uint32, settlement *exchange.Settlement) error { // Release the holds!!!! var errs []error for _, order := range settlement.FullyFilledOrders { - if err = k.releaseHoldOnOrder(ctx, order); err != nil { + if err := k.releaseHoldOnOrder(ctx, order); err != nil { errs = append(errs, err) } } if settlement.PartialOrderFilled != nil { - if err = k.releaseHoldOnOrder(ctx, settlement.PartialOrderFilled); err != nil { + if err := k.releaseHoldOnOrder(ctx, settlement.PartialOrderFilled); err != nil { errs = append(errs, err) } } @@ -339,30 +272,34 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO // Transfer all the things!!!! for _, transfer := range settlement.Transfers { - if err = k.DoTransfer(ctx, transfer.Inputs, transfer.Outputs); err != nil { - return err + if err := k.DoTransfer(ctx, transfer.Inputs, transfer.Outputs); err != nil { + errs = append(errs, err) } } // Collect all the fees (not as exciting). - if err = k.CollectFees(ctx, marketID, settlement.FeeInputs); err != nil { - return err + if err := k.CollectFees(ctx, marketID, settlement.FeeInputs); err != nil { + errs = append(errs, err) } - // Delete all the fully filled orders. - for _, order := range settlement.FullyFilledOrders { - deleteAndDeIndexOrder(store, *order.GetOriginalOrder()) + if len(errs) > 0 { + return errors.Join(errs...) } + // Update the partial order if there was one. if settlement.PartialOrderLeft != nil { - if err = k.setOrderInStore(store, *settlement.PartialOrderLeft); err != nil { + if err := k.setOrderInStore(store, *settlement.PartialOrderLeft); err != nil { return fmt.Errorf("could not update partial %s order %d: %w", settlement.PartialOrderLeft.GetOrderType(), settlement.PartialOrderLeft.OrderId, err) } } + // Delete all the fully filled orders. + for _, order := range settlement.FullyFilledOrders { + deleteAndDeIndexOrder(store, *order.GetOriginalOrder()) + } // And emit all the needed events. - events := make([]proto.Message, 0, len(askOrders)+len(bidOrders)) + events := make([]proto.Message, 0, len(settlement.FullyFilledOrders)+1) for _, order := range settlement.FullyFilledOrders { events = append(events, exchange.NewEventOrderFilled(order)) } From 994815e36c94b45109e7306bfc6da2d7bfee21b2 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 13 Oct 2023 15:44:01 -0600 Subject: [PATCH 297/309] [1658]: In IndexedAddrAmts.Add, if the coins to add is zero, don't do anything. --- x/exchange/fulfillment.go | 3 ++ x/exchange/fulfillment_test.go | 69 ++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/x/exchange/fulfillment.go b/x/exchange/fulfillment.go index 36a6b2fdd3..3d4ad50f1e 100644 --- a/x/exchange/fulfillment.go +++ b/x/exchange/fulfillment.go @@ -103,6 +103,9 @@ func NewIndexedAddrAmts() *IndexedAddrAmts { // Add adds the coins to the given address. // Panics if a provided coin is invalid. func (i *IndexedAddrAmts) Add(addr string, coins ...sdk.Coin) { + if sdk.Coins(coins).IsZero() { + return + } for _, coin := range coins { if err := coin.Validate(); err != nil { panic(fmt.Errorf("cannot index and add invalid coin amount %q", coin)) diff --git a/x/exchange/fulfillment_test.go b/x/exchange/fulfillment_test.go index 61402f0b0f..23a0777ac4 100644 --- a/x/exchange/fulfillment_test.go +++ b/x/exchange/fulfillment_test.go @@ -1046,6 +1046,45 @@ func TestIndexedAddrAmts_Add(t *testing.T) { expected *IndexedAddrAmts expPanic string }{ + { + name: "empty, add zero coins", + receiver: NewIndexedAddrAmts(), + addr: "addr1", + coins: nil, + expected: NewIndexedAddrAmts(), + }, + { + name: "empty, add one coin with a zero value", + receiver: NewIndexedAddrAmts(), + addr: "addr1", + coins: []sdk.Coin{{Denom: "zero", Amount: sdkmath.ZeroInt()}}, + expected: NewIndexedAddrAmts(), + }, + { + name: "empty, add two coins with a zero value.", + receiver: NewIndexedAddrAmts(), + addr: "addr1", + coins: []sdk.Coin{ + {Denom: "zeroa", Amount: sdkmath.ZeroInt()}, + {Denom: "zerob", Amount: sdkmath.ZeroInt()}, + }, + expected: NewIndexedAddrAmts(), + }, + { + name: "empty, add three coins, only one is not zero", + receiver: NewIndexedAddrAmts(), + addr: "addr1", + coins: []sdk.Coin{ + {Denom: "coina", Amount: sdkmath.ZeroInt()}, + {Denom: "coinb", Amount: sdkmath.OneInt()}, + {Denom: "coinc", Amount: sdkmath.ZeroInt()}, + }, + expected: &IndexedAddrAmts{ + addrs: []string{"addr1"}, + amts: []sdk.Coins{coins("1coinb")}, + indexes: map[string]int{"addr1": 0}, + }, + }, { name: "empty, add one coin", receiver: NewIndexedAddrAmts(), @@ -1254,6 +1293,36 @@ func TestIndexedAddrAmts_Add(t *testing.T) { coins: negCoins, expPanic: "cannot index and add invalid coin amount \"-1neg\"", }, + { + name: "three addrs, add zero to existing", + receiver: &IndexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + addr: "addr2", + coins: []sdk.Coin{{Denom: "zero", Amount: sdkmath.ZeroInt()}}, + expected: &IndexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + }, + { + name: "three addrs, add zero to new", + receiver: &IndexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + addr: "addr4", + coins: []sdk.Coin{{Denom: "zero", Amount: sdkmath.ZeroInt()}}, + expected: &IndexedAddrAmts{ + addrs: []string{"addr1", "addr2", "addr3"}, + amts: []sdk.Coins{coins("1one"), coins("2two"), coins("3three")}, + indexes: map[string]int{"addr1": 0, "addr2": 1, "addr3": 2}, + }, + }, } for _, tc := range tests { From d03a9b246ad4102719aa898d9b93f1ff91c6238c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Fri, 13 Oct 2023 16:36:50 -0600 Subject: [PATCH 298/309] [1658]: Increase the maximmum external id length to allow for 32 byte bech32 addresses. --- docs/proto-docs.md | 6 +++--- proto/provenance/exchange/v1/orders.proto | 4 ++-- proto/provenance/exchange/v1/tx.proto | 2 +- x/exchange/keeper/keys_test.go | 13 +++++++------ x/exchange/orders.go | 7 ++++++- x/exchange/orders.pb.go | 4 ++-- x/exchange/tx.pb.go | 2 +- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index 587f6077d1..ef4607d280 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -1803,7 +1803,7 @@ AskOrder represents someone's desire to sell something at a minimum price. | `price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | price is the minimum amount that the seller is willing to accept for the assets. The seller's settlement proportional fee (and possibly the settlement flat fee) is taken out of the amount the seller receives, so it's possible that the seller will still receive less than this price. | | `seller_settlement_flat_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | seller_settlement_flat_fee is the flat fee for sellers that will be charged during settlement. If this denom is the same denom as the price, it will come out of the actual price received. If this denom is different, the amount must be in the seller's account and a hold is placed on it until the order is filled or cancelled. | | `allow_partial` | [bool](#bool) | | allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the order must be either filled in full or not filled at all. | -| `external_id` | [string](#string) | | external_id is an optional string used to externally identify this order. Max length is 40 characters. If an order in this market with this external id already exists, this order will be rejected. | +| `external_id` | [string](#string) | | external_id is an optional string used to externally identify this order. Max length is 100 characters. If an order in this market with this external id already exists, this order will be rejected. | @@ -1824,7 +1824,7 @@ BidOrder represents someone's desire to buy something at a specific price. | `price` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | price is the amount that the buyer will pay for the assets. A hold is placed on this until the order is filled or cancelled. | | `buyer_settlement_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | buyer_settlement_fees are the fees (both flat and proportional) that the buyer will pay (in addition to the price) when the order is settled. A hold is placed on this until the order is filled or cancelled. | | `allow_partial` | [bool](#bool) | | allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the order must be either filled in full or not filled at all. | -| `external_id` | [string](#string) | | external_id is an optional string used to externally identify this order. Max length is 40 characters. If an order in this market with this external id already exists, this order will be rejected. | +| `external_id` | [string](#string) | | external_id is an optional string used to externally identify this order. Max length is 100 characters. If an order in this market with this external id already exists, this order will be rejected. | @@ -2255,7 +2255,7 @@ MsgMarketSetOrderExternalIDRequest is a request message for the MarketSetOrderEx | `admin` | [string](#string) | | admin is the account with "set_ids" permission requesting this settlement. | | `market_id` | [uint32](#uint32) | | market_id is the numerical identifier of the market to update required attributes for. | | `order_id` | [uint64](#uint64) | | order_id is the numerical identifier of the order to update. | -| `external_id` | [string](#string) | | external_id is the new external id to associate with the order. Max length is 40 characters. If the external id is already associated with another order in this market, this update will fail. | +| `external_id` | [string](#string) | | external_id is the new external id to associate with the order. Max length is 100 characters. If the external id is already associated with another order in this market, this update will fail. | diff --git a/proto/provenance/exchange/v1/orders.proto b/proto/provenance/exchange/v1/orders.proto index 7ba84762ea..538dbdce13 100644 --- a/proto/provenance/exchange/v1/orders.proto +++ b/proto/provenance/exchange/v1/orders.proto @@ -47,7 +47,7 @@ message AskOrder { // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the // order must be either filled in full or not filled at all. bool allow_partial = 6; - // external_id is an optional string used to externally identify this order. Max length is 40 characters. + // external_id is an optional string used to externally identify this order. Max length is 100 characters. // If an order in this market with this external id already exists, this order will be rejected. string external_id = 7; } @@ -72,7 +72,7 @@ message BidOrder { // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the // order must be either filled in full or not filled at all. bool allow_partial = 6; - // external_id is an optional string used to externally identify this order. Max length is 40 characters. + // external_id is an optional string used to externally identify this order. Max length is 100 characters. // If an order in this market with this external id already exists, this order will be rejected. string external_id = 7; } \ No newline at end of file diff --git a/proto/provenance/exchange/v1/tx.proto b/proto/provenance/exchange/v1/tx.proto index 0a23d47528..d5a18ba497 100644 --- a/proto/provenance/exchange/v1/tx.proto +++ b/proto/provenance/exchange/v1/tx.proto @@ -196,7 +196,7 @@ message MsgMarketSetOrderExternalIDRequest { // order_id is the numerical identifier of the order to update. uint64 order_id = 3; - // external_id is the new external id to associate with the order. Max length is 40 characters. + // external_id is the new external id to associate with the order. Max length is 100 characters. // If the external id is already associated with another order in this market, this update will fail. string external_id = 4; } diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index b329a423ab..58f9bd7c8f 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -2,6 +2,7 @@ package keeper_test import ( "bytes" + "fmt" "strings" "testing" @@ -3746,11 +3747,11 @@ func TestMakeIndexKeyMarketExternalIDToOrder(t *testing.T) { expPanic: "cannot create market external id to order index with empty external id", }, { - name: "41 char external id", + name: "external id too long", marketID: 2, - externalID: "This id has forty-one chars. That's long!", - expPanic: "cannot create market external id to order index: invalid external id " + - "\"This id has forty-one chars. That's long!\": max length 40", + externalID: strings.Repeat("H", exchange.MaxExternalIDLength+1), + expPanic: fmt.Sprintf("cannot create market external id to order index: invalid external id %q: max length %d", + strings.Repeat("H", exchange.MaxExternalIDLength+1), exchange.MaxExternalIDLength), }, { name: "market 0, a zeroed uuid", @@ -3775,9 +3776,9 @@ func TestMakeIndexKeyMarketExternalIDToOrder(t *testing.T) { { name: "max market id and lots of Zs", marketID: 4_294_967_295, - externalID: strings.Repeat("Z", 40), + externalID: strings.Repeat("Z", exchange.MaxExternalIDLength), expected: append([]byte{keeper.KeyTypeMarketExternalIDToOrderIndex, 255, 255, 255, 255}, - strings.Repeat("Z", 40)...), + strings.Repeat("Z", exchange.MaxExternalIDLength)...), }, } diff --git a/x/exchange/orders.go b/x/exchange/orders.go index 5a014bffc1..7496a12672 100644 --- a/x/exchange/orders.go +++ b/x/exchange/orders.go @@ -19,7 +19,12 @@ const ( OrderTypeByteBid = byte(0x01) ) -const MaxExternalIDLength = 40 +// MaxExternalIDLength is the maximum length that an external id can have. +// A 32 byte address as a bech32 string is 59 characters + the hrp. +// E.g. a 32 byte address with hrp "pb" will be 61 characters long. +// Technically, a bech32 HRP can be 1 to 83 characters. This 100 was chosen as a balance meant +// to allow most of those while still limiting the length of keys that use these external ids. +const MaxExternalIDLength = 100 // SubOrderI is an interface with getters for the fields in a sub-order (i.e. AskOrder or BidOrder). type SubOrderI interface { diff --git a/x/exchange/orders.pb.go b/x/exchange/orders.pb.go index 3598427aa7..b712c8cab8 100644 --- a/x/exchange/orders.pb.go +++ b/x/exchange/orders.pb.go @@ -136,7 +136,7 @@ type AskOrder struct { // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the // order must be either filled in full or not filled at all. AllowPartial bool `protobuf:"varint,6,opt,name=allow_partial,json=allowPartial,proto3" json:"allow_partial,omitempty"` - // external_id is an optional string used to externally identify this order. Max length is 40 characters. + // external_id is an optional string used to externally identify this order. Max length is 100 characters. // If an order in this market with this external id already exists, this order will be rejected. ExternalId string `protobuf:"bytes,7,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } @@ -191,7 +191,7 @@ type BidOrder struct { // allow_partial should be true if partial fulfillment of this order should be allowed, and should be false if the // order must be either filled in full or not filled at all. AllowPartial bool `protobuf:"varint,6,opt,name=allow_partial,json=allowPartial,proto3" json:"allow_partial,omitempty"` - // external_id is an optional string used to externally identify this order. Max length is 40 characters. + // external_id is an optional string used to externally identify this order. Max length is 100 characters. // If an order in this market with this external id already exists, this order will be rejected. ExternalId string `protobuf:"bytes,7,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } diff --git a/x/exchange/tx.pb.go b/x/exchange/tx.pb.go index 6c9f57c84d..5cf2b5235d 100644 --- a/x/exchange/tx.pb.go +++ b/x/exchange/tx.pb.go @@ -720,7 +720,7 @@ type MsgMarketSetOrderExternalIDRequest struct { MarketId uint32 `protobuf:"varint,2,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` // order_id is the numerical identifier of the order to update. OrderId uint64 `protobuf:"varint,3,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` - // external_id is the new external id to associate with the order. Max length is 40 characters. + // external_id is the new external id to associate with the order. Max length is 100 characters. // If the external id is already associated with another order in this market, this update will fail. ExternalId string `protobuf:"bytes,4,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` } From 2e00120b7cccfa4a321ed79ab56a9b7f021b852a Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 17 Oct 2023 15:00:45 -0600 Subject: [PATCH 299/309] [1658]: Remove the 'Query' from the name of all the query endpoints. --- docs/proto-docs.md | 78 +-- proto/provenance/exchange/v1/query.proto | 104 ++-- x/exchange/keeper/grpc_query.go | 68 +-- x/exchange/keeper/grpc_query_test.go | 26 +- x/exchange/query.pb.go | 603 +++++++++++------------ x/exchange/query.pb.gw.go | 438 ++++++++-------- 6 files changed, 658 insertions(+), 659 deletions(-) diff --git a/docs/proto-docs.md b/docs/proto-docs.md index ef4607d280..616df193b1 100644 --- a/docs/proto-docs.md +++ b/docs/proto-docs.md @@ -2454,7 +2454,7 @@ Msg is the service for exchange module's tx endpoints. ### QueryGetAllMarketsRequest -QueryGetAllMarketsRequest is a request message for the QueryGetAllMarkets endpoint. +QueryGetAllMarketsRequest is a request message for the GetAllMarkets query. | Field | Type | Label | Description | @@ -2469,7 +2469,7 @@ QueryGetAllMarketsRequest is a request message for the QueryGetAllMarkets endpoi ### QueryGetAllMarketsResponse -QueryGetAllMarketsResponse is a response message for the QueryGetAllMarkets endpoint. +QueryGetAllMarketsResponse is a response message for the GetAllMarkets query. | Field | Type | Label | Description | @@ -2485,7 +2485,7 @@ QueryGetAllMarketsResponse is a response message for the QueryGetAllMarkets endp ### QueryGetAllOrdersRequest -QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint. +QueryGetAllOrdersRequest is a request message for the GetAllOrders query. | Field | Type | Label | Description | @@ -2500,7 +2500,7 @@ QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint ### QueryGetAllOrdersResponse -QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoint. +QueryGetAllOrdersResponse is a response message for the GetAllOrders query. | Field | Type | Label | Description | @@ -2516,7 +2516,7 @@ QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoi ### QueryGetAssetOrdersRequest -QueryGetAssetOrdersRequest is a request message for the QueryGetAssetOrders endpoint. +QueryGetAssetOrdersRequest is a request message for the GetAssetOrders query. | Field | Type | Label | Description | @@ -2534,7 +2534,7 @@ QueryGetAssetOrdersRequest is a request message for the QueryGetAssetOrders endp ### QueryGetAssetOrdersResponse -QueryGetAssetOrdersResponse is a response message for the QueryGetAssetOrders endpoint. +QueryGetAssetOrdersResponse is a response message for the GetAssetOrders query. | Field | Type | Label | Description | @@ -2550,7 +2550,7 @@ QueryGetAssetOrdersResponse is a response message for the QueryGetAssetOrders en ### QueryGetMarketOrdersRequest -QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. +QueryGetMarketOrdersRequest is a request message for the GetMarketOrders query. | Field | Type | Label | Description | @@ -2568,7 +2568,7 @@ QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders en ### QueryGetMarketOrdersResponse -QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders endpoint. +QueryGetMarketOrdersResponse is a response message for the GetMarketOrders query. | Field | Type | Label | Description | @@ -2584,7 +2584,7 @@ QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders ### QueryGetMarketRequest -QueryGetMarketRequest is a request message for the QueryGetMarket endpoint. +QueryGetMarketRequest is a request message for the GetMarket query. | Field | Type | Label | Description | @@ -2599,7 +2599,7 @@ QueryGetMarketRequest is a request message for the QueryGetMarket endpoint. ### QueryGetMarketResponse -QueryGetMarketResponse is a response message for the QueryGetMarket endpoint. +QueryGetMarketResponse is a response message for the GetMarket query. | Field | Type | Label | Description | @@ -2615,7 +2615,7 @@ QueryGetMarketResponse is a response message for the QueryGetMarket endpoint. ### QueryGetOrderByExternalIDRequest -QueryGetOrderByExternalIDRequest is a request message for the QueryGetOrderByExternalID endpoint. +QueryGetOrderByExternalIDRequest is a request message for the GetOrderByExternalID query. | Field | Type | Label | Description | @@ -2631,7 +2631,7 @@ QueryGetOrderByExternalIDRequest is a request message for the QueryGetOrderByExt ### QueryGetOrderByExternalIDResponse -QueryGetOrderByExternalIDResponse is a response message for the QueryGetOrderByExternalID endpoint. +QueryGetOrderByExternalIDResponse is a response message for the GetOrderByExternalID query. | Field | Type | Label | Description | @@ -2646,7 +2646,7 @@ QueryGetOrderByExternalIDResponse is a response message for the QueryGetOrderByE ### QueryGetOrderRequest -QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. +QueryGetOrderRequest is a request message for the GetOrder query. | Field | Type | Label | Description | @@ -2661,7 +2661,7 @@ QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. ### QueryGetOrderResponse -QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. +QueryGetOrderResponse is a response message for the GetOrder query. | Field | Type | Label | Description | @@ -2676,7 +2676,7 @@ QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. ### QueryGetOwnerOrdersRequest -QueryGetOwnerOrdersRequest is a request message for the QueryGetOwnerOrders endpoint. +QueryGetOwnerOrdersRequest is a request message for the GetOwnerOrders query. | Field | Type | Label | Description | @@ -2694,7 +2694,7 @@ QueryGetOwnerOrdersRequest is a request message for the QueryGetOwnerOrders endp ### QueryGetOwnerOrdersResponse -QueryGetOwnerOrdersResponse is a response message for the QueryGetOwnerOrders endpoint. +QueryGetOwnerOrdersResponse is a response message for the GetOwnerOrders query. | Field | Type | Label | Description | @@ -2710,7 +2710,7 @@ QueryGetOwnerOrdersResponse is a response message for the QueryGetOwnerOrders en ### QueryOrderFeeCalcRequest -QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint. +QueryOrderFeeCalcRequest is a request message for the OrderFeeCalc query. Exactly one of ask_order or bid_order must be provided. @@ -2727,7 +2727,7 @@ Exactly one of ask_order or bid_order must be provided. ### QueryOrderFeeCalcResponse -QueryOrderFeeCalcResponse is a response message for the QueryOrderFeeCalc endpoint. +QueryOrderFeeCalcResponse is a response message for the OrderFeeCalc query. | Field | Type | Label | Description | @@ -2748,7 +2748,7 @@ If the provided order was an ask order, these are purely informational and repre ### QueryParamsRequest -QueryParamsRequest is a request message for the QueryParams endpoint. +QueryParamsRequest is a request message for the Params query. @@ -2758,7 +2758,7 @@ QueryParamsRequest is a request message for the QueryParams endpoint. ### QueryParamsResponse -QueryParamsResponse is a response message for the QueryParams endpoint. +QueryParamsResponse is a response message for the Params query. | Field | Type | Label | Description | @@ -2773,7 +2773,7 @@ QueryParamsResponse is a response message for the QueryParams endpoint. ### QueryValidateCreateMarketRequest -QueryValidateCreateMarketRequest is a request message for the QueryValidateCreateMarket endpoint. +QueryValidateCreateMarketRequest is a request message for the ValidateCreateMarket query. | Field | Type | Label | Description | @@ -2788,7 +2788,7 @@ QueryValidateCreateMarketRequest is a request message for the QueryValidateCreat ### QueryValidateCreateMarketResponse -QueryValidateCreateMarketResponse is a response message for the QueryValidateCreateMarket endpoint. +QueryValidateCreateMarketResponse is a response message for the ValidateCreateMarket query. | Field | Type | Label | Description | @@ -2804,7 +2804,7 @@ QueryValidateCreateMarketResponse is a response message for the QueryValidateCre ### QueryValidateManageFeesRequest -QueryValidateManageFeesRequest is a request message for the QueryValidateManageFees endpoint. +QueryValidateManageFeesRequest is a request message for the ValidateManageFees query. | Field | Type | Label | Description | @@ -2819,7 +2819,7 @@ QueryValidateManageFeesRequest is a request message for the QueryValidateManageF ### QueryValidateManageFeesResponse -QueryValidateManageFeesResponse is a response message for the QueryValidateManageFees endpoint. +QueryValidateManageFeesResponse is a response message for the ValidateManageFees query. | Field | Type | Label | Description | @@ -2835,7 +2835,7 @@ QueryValidateManageFeesResponse is a response message for the QueryValidateManag ### QueryValidateMarketRequest -QueryValidateMarketRequest is a request message for the QueryValidateMarket endpoint. +QueryValidateMarketRequest is a request message for the ValidateMarket query. | Field | Type | Label | Description | @@ -2850,7 +2850,7 @@ QueryValidateMarketRequest is a request message for the QueryValidateMarket endp ### QueryValidateMarketResponse -QueryValidateMarketResponse is a response message for the QueryValidateMarket endpoint. +QueryValidateMarketResponse is a response message for the ValidateMarket query. | Field | Type | Label | Description | @@ -2875,19 +2875,19 @@ Query is the service for exchange module's query endpoints. | Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | | ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `QueryOrderFeeCalc` | [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) | [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) | QueryOrderFeeCalc calculates the fees that will be associated with the provided order. | GET|/provenance/exchange/v1/fees/order| -| `QueryGetOrder` | [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) | [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) | QueryGetOrder looks up an order by id. | GET|/provenance/exchange/v1/order/{order_id}| -| `QueryGetOrderByExternalID` | [QueryGetOrderByExternalIDRequest](#provenance.exchange.v1.QueryGetOrderByExternalIDRequest) | [QueryGetOrderByExternalIDResponse](#provenance.exchange.v1.QueryGetOrderByExternalIDResponse) | QueryGetOrderByExternalID looks up an order by market id and external id. | GET|/provenance/exchange/v1/orders/market/{market_id}/{external_id}GET|/provenance/exchange/v1/market/{market_id}/order/{external_id}| -| `QueryGetMarketOrders` | [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) | [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) | QueryGetMarketOrders looks up the orders in a market. | GET|/provenance/exchange/v1/orders/market/{market_id}GET|/provenance/exchange/v1/market/{market_id}/orders| -| `QueryGetOwnerOrders` | [QueryGetOwnerOrdersRequest](#provenance.exchange.v1.QueryGetOwnerOrdersRequest) | [QueryGetOwnerOrdersResponse](#provenance.exchange.v1.QueryGetOwnerOrdersResponse) | QueryGetOwnerOrders looks up the orders from the provided owner address. | GET|/provenance/exchange/v1/orders/owner/{owner}| -| `QueryGetAssetOrders` | [QueryGetAssetOrdersRequest](#provenance.exchange.v1.QueryGetAssetOrdersRequest) | [QueryGetAssetOrdersResponse](#provenance.exchange.v1.QueryGetAssetOrdersResponse) | QueryGetAssetOrders looks up the orders for a specific asset denom. | GET|/provenance/exchange/v1/orders/asset/{asset}| -| `QueryGetAllOrders` | [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) | [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) | QueryGetAllOrders gets all orders in the exchange module. | GET|/provenance/exchange/v1/orders| -| `QueryGetMarket` | [QueryGetMarketRequest](#provenance.exchange.v1.QueryGetMarketRequest) | [QueryGetMarketResponse](#provenance.exchange.v1.QueryGetMarketResponse) | QueryGetMarket returns all the information and details about a market. | GET|/provenance/exchange/v1/market/{market_id}| -| `QueryGetAllMarkets` | [QueryGetAllMarketsRequest](#provenance.exchange.v1.QueryGetAllMarketsRequest) | [QueryGetAllMarketsResponse](#provenance.exchange.v1.QueryGetAllMarketsResponse) | QueryGetAllMarkets returns brief information about each market. | GET|/provenance/exchange/v1/markets| -| `QueryParams` | [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) | [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) | QueryParams returns the exchange module parameters. | GET|/provenance/exchange/v1/params| -| `QueryValidateCreateMarket` | [QueryValidateCreateMarketRequest](#provenance.exchange.v1.QueryValidateCreateMarketRequest) | [QueryValidateCreateMarketResponse](#provenance.exchange.v1.QueryValidateCreateMarketResponse) | QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. | GET|/provenance/exchange/v1/validate/create_market| -| `QueryValidateMarket` | [QueryValidateMarketRequest](#provenance.exchange.v1.QueryValidateMarketRequest) | [QueryValidateMarketResponse](#provenance.exchange.v1.QueryValidateMarketResponse) | QueryValidateMarket checks for any problems with a market's setup. | GET|/provenance/exchange/v1/validate/market/{market_id}GET|/provenance/exchange/v1/market/{market_id}/validate| -| `QueryValidateManageFees` | [QueryValidateManageFeesRequest](#provenance.exchange.v1.QueryValidateManageFeesRequest) | [QueryValidateManageFeesResponse](#provenance.exchange.v1.QueryValidateManageFeesResponse) | QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. | GET|/provenance/exchange/v1/validate/manage_fees| +| `OrderFeeCalc` | [QueryOrderFeeCalcRequest](#provenance.exchange.v1.QueryOrderFeeCalcRequest) | [QueryOrderFeeCalcResponse](#provenance.exchange.v1.QueryOrderFeeCalcResponse) | OrderFeeCalc calculates the fees that will be associated with the provided order. | GET|/provenance/exchange/v1/fees/order| +| `GetOrder` | [QueryGetOrderRequest](#provenance.exchange.v1.QueryGetOrderRequest) | [QueryGetOrderResponse](#provenance.exchange.v1.QueryGetOrderResponse) | GetOrder looks up an order by id. | GET|/provenance/exchange/v1/order/{order_id}| +| `GetOrderByExternalID` | [QueryGetOrderByExternalIDRequest](#provenance.exchange.v1.QueryGetOrderByExternalIDRequest) | [QueryGetOrderByExternalIDResponse](#provenance.exchange.v1.QueryGetOrderByExternalIDResponse) | GetOrderByExternalID looks up an order by market id and external id. | GET|/provenance/exchange/v1/orders/market/{market_id}/{external_id}GET|/provenance/exchange/v1/market/{market_id}/order/{external_id}| +| `GetMarketOrders` | [QueryGetMarketOrdersRequest](#provenance.exchange.v1.QueryGetMarketOrdersRequest) | [QueryGetMarketOrdersResponse](#provenance.exchange.v1.QueryGetMarketOrdersResponse) | GetMarketOrders looks up the orders in a market. | GET|/provenance/exchange/v1/orders/market/{market_id}GET|/provenance/exchange/v1/market/{market_id}/orders| +| `GetOwnerOrders` | [QueryGetOwnerOrdersRequest](#provenance.exchange.v1.QueryGetOwnerOrdersRequest) | [QueryGetOwnerOrdersResponse](#provenance.exchange.v1.QueryGetOwnerOrdersResponse) | GetOwnerOrders looks up the orders from the provided owner address. | GET|/provenance/exchange/v1/orders/owner/{owner}| +| `GetAssetOrders` | [QueryGetAssetOrdersRequest](#provenance.exchange.v1.QueryGetAssetOrdersRequest) | [QueryGetAssetOrdersResponse](#provenance.exchange.v1.QueryGetAssetOrdersResponse) | GetAssetOrders looks up the orders for a specific asset denom. | GET|/provenance/exchange/v1/orders/asset/{asset}| +| `GetAllOrders` | [QueryGetAllOrdersRequest](#provenance.exchange.v1.QueryGetAllOrdersRequest) | [QueryGetAllOrdersResponse](#provenance.exchange.v1.QueryGetAllOrdersResponse) | GetAllOrders gets all orders in the exchange module. | GET|/provenance/exchange/v1/orders| +| `GetMarket` | [QueryGetMarketRequest](#provenance.exchange.v1.QueryGetMarketRequest) | [QueryGetMarketResponse](#provenance.exchange.v1.QueryGetMarketResponse) | GetMarket returns all the information and details about a market. | GET|/provenance/exchange/v1/market/{market_id}| +| `GetAllMarkets` | [QueryGetAllMarketsRequest](#provenance.exchange.v1.QueryGetAllMarketsRequest) | [QueryGetAllMarketsResponse](#provenance.exchange.v1.QueryGetAllMarketsResponse) | GetAllMarkets returns brief information about each market. | GET|/provenance/exchange/v1/markets| +| `Params` | [QueryParamsRequest](#provenance.exchange.v1.QueryParamsRequest) | [QueryParamsResponse](#provenance.exchange.v1.QueryParamsResponse) | Params returns the exchange module parameters. | GET|/provenance/exchange/v1/params| +| `ValidateCreateMarket` | [QueryValidateCreateMarketRequest](#provenance.exchange.v1.QueryValidateCreateMarketRequest) | [QueryValidateCreateMarketResponse](#provenance.exchange.v1.QueryValidateCreateMarketResponse) | ValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. | GET|/provenance/exchange/v1/validate/create_market| +| `ValidateMarket` | [QueryValidateMarketRequest](#provenance.exchange.v1.QueryValidateMarketRequest) | [QueryValidateMarketResponse](#provenance.exchange.v1.QueryValidateMarketResponse) | ValidateMarket checks for any problems with a market's setup. | GET|/provenance/exchange/v1/validate/market/{market_id}GET|/provenance/exchange/v1/market/{market_id}/validate| +| `ValidateManageFees` | [QueryValidateManageFeesRequest](#provenance.exchange.v1.QueryValidateManageFeesRequest) | [QueryValidateManageFeesResponse](#provenance.exchange.v1.QueryValidateManageFeesResponse) | ValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. | GET|/provenance/exchange/v1/validate/manage_fees| diff --git a/proto/provenance/exchange/v1/query.proto b/proto/provenance/exchange/v1/query.proto index adeb6a6824..22eeb4b775 100644 --- a/proto/provenance/exchange/v1/query.proto +++ b/proto/provenance/exchange/v1/query.proto @@ -18,82 +18,82 @@ import "provenance/exchange/v1/tx.proto"; // Query is the service for exchange module's query endpoints. service Query { - // QueryOrderFeeCalc calculates the fees that will be associated with the provided order. - rpc QueryOrderFeeCalc(QueryOrderFeeCalcRequest) returns (QueryOrderFeeCalcResponse) { + // OrderFeeCalc calculates the fees that will be associated with the provided order. + rpc OrderFeeCalc(QueryOrderFeeCalcRequest) returns (QueryOrderFeeCalcResponse) { option (google.api.http).get = "/provenance/exchange/v1/fees/order"; } - // QueryGetOrder looks up an order by id. - rpc QueryGetOrder(QueryGetOrderRequest) returns (QueryGetOrderResponse) { + // GetOrder looks up an order by id. + rpc GetOrder(QueryGetOrderRequest) returns (QueryGetOrderResponse) { option (google.api.http).get = "/provenance/exchange/v1/order/{order_id}"; } - // QueryGetOrderByExternalID looks up an order by market id and external id. - rpc QueryGetOrderByExternalID(QueryGetOrderByExternalIDRequest) returns (QueryGetOrderByExternalIDResponse) { + // GetOrderByExternalID looks up an order by market id and external id. + rpc GetOrderByExternalID(QueryGetOrderByExternalIDRequest) returns (QueryGetOrderByExternalIDResponse) { option (google.api.http) = { get: "/provenance/exchange/v1/orders/market/{market_id}/{external_id}" additional_bindings: {get: "/provenance/exchange/v1/market/{market_id}/order/{external_id}"} }; } - // QueryGetMarketOrders looks up the orders in a market. - rpc QueryGetMarketOrders(QueryGetMarketOrdersRequest) returns (QueryGetMarketOrdersResponse) { + // GetMarketOrders looks up the orders in a market. + rpc GetMarketOrders(QueryGetMarketOrdersRequest) returns (QueryGetMarketOrdersResponse) { option (google.api.http) = { get: "/provenance/exchange/v1/orders/market/{market_id}" additional_bindings: {get: "/provenance/exchange/v1/market/{market_id}/orders"} }; } - // QueryGetOwnerOrders looks up the orders from the provided owner address. - rpc QueryGetOwnerOrders(QueryGetOwnerOrdersRequest) returns (QueryGetOwnerOrdersResponse) { + // GetOwnerOrders looks up the orders from the provided owner address. + rpc GetOwnerOrders(QueryGetOwnerOrdersRequest) returns (QueryGetOwnerOrdersResponse) { option (google.api.http).get = "/provenance/exchange/v1/orders/owner/{owner}"; } - // QueryGetAssetOrders looks up the orders for a specific asset denom. - rpc QueryGetAssetOrders(QueryGetAssetOrdersRequest) returns (QueryGetAssetOrdersResponse) { + // GetAssetOrders looks up the orders for a specific asset denom. + rpc GetAssetOrders(QueryGetAssetOrdersRequest) returns (QueryGetAssetOrdersResponse) { option (google.api.http).get = "/provenance/exchange/v1/orders/asset/{asset}"; } - // QueryGetAllOrders gets all orders in the exchange module. - rpc QueryGetAllOrders(QueryGetAllOrdersRequest) returns (QueryGetAllOrdersResponse) { + // GetAllOrders gets all orders in the exchange module. + rpc GetAllOrders(QueryGetAllOrdersRequest) returns (QueryGetAllOrdersResponse) { option (google.api.http).get = "/provenance/exchange/v1/orders"; } - // QueryGetMarket returns all the information and details about a market. - rpc QueryGetMarket(QueryGetMarketRequest) returns (QueryGetMarketResponse) { + // GetMarket returns all the information and details about a market. + rpc GetMarket(QueryGetMarketRequest) returns (QueryGetMarketResponse) { option (google.api.http).get = "/provenance/exchange/v1/market/{market_id}"; } - // QueryGetAllMarkets returns brief information about each market. - rpc QueryGetAllMarkets(QueryGetAllMarketsRequest) returns (QueryGetAllMarketsResponse) { + // GetAllMarkets returns brief information about each market. + rpc GetAllMarkets(QueryGetAllMarketsRequest) returns (QueryGetAllMarketsResponse) { option (google.api.http).get = "/provenance/exchange/v1/markets"; } - // QueryParams returns the exchange module parameters. - rpc QueryParams(QueryParamsRequest) returns (QueryParamsResponse) { + // Params returns the exchange module parameters. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/provenance/exchange/v1/params"; } - // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. - rpc QueryValidateCreateMarket(QueryValidateCreateMarketRequest) returns (QueryValidateCreateMarketResponse) { + // ValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. + rpc ValidateCreateMarket(QueryValidateCreateMarketRequest) returns (QueryValidateCreateMarketResponse) { option (google.api.http).get = "/provenance/exchange/v1/validate/create_market"; } - // QueryValidateMarket checks for any problems with a market's setup. - rpc QueryValidateMarket(QueryValidateMarketRequest) returns (QueryValidateMarketResponse) { + // ValidateMarket checks for any problems with a market's setup. + rpc ValidateMarket(QueryValidateMarketRequest) returns (QueryValidateMarketResponse) { option (google.api.http) = { get: "/provenance/exchange/v1/validate/market/{market_id}" additional_bindings: {get: "/provenance/exchange/v1/market/{market_id}/validate"} }; } - // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. - rpc QueryValidateManageFees(QueryValidateManageFeesRequest) returns (QueryValidateManageFeesResponse) { + // ValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. + rpc ValidateManageFees(QueryValidateManageFeesRequest) returns (QueryValidateManageFeesResponse) { option (google.api.http).get = "/provenance/exchange/v1/validate/manage_fees"; } } -// QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint. +// QueryOrderFeeCalcRequest is a request message for the OrderFeeCalc query. // Exactly one of ask_order or bid_order must be provided. message QueryOrderFeeCalcRequest { // ask_order is the ask order to calculate the fees for. @@ -102,7 +102,7 @@ message QueryOrderFeeCalcRequest { BidOrder bid_order = 3; } -// QueryOrderFeeCalcResponse is a response message for the QueryOrderFeeCalc endpoint. +// QueryOrderFeeCalcResponse is a response message for the OrderFeeCalc query. message QueryOrderFeeCalcResponse { // creation_fee_options are the order creation flat fee options available for creating the provided order. // If it's empty, no order creation fee is required. @@ -123,19 +123,19 @@ message QueryOrderFeeCalcResponse { repeated cosmos.base.v1beta1.Coin settlement_ratio_fee_options = 3 [(gogoproto.nullable) = false]; } -// QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. +// QueryGetOrderRequest is a request message for the GetOrder query. message QueryGetOrderRequest { // order_id is the id of the order to look up. uint64 order_id = 1; } -// QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. +// QueryGetOrderResponse is a response message for the GetOrder query. message QueryGetOrderResponse { // order is the requested order. Order order = 1; } -// QueryGetOrderByExternalIDRequest is a request message for the QueryGetOrderByExternalID endpoint. +// QueryGetOrderByExternalIDRequest is a request message for the GetOrderByExternalID query. message QueryGetOrderByExternalIDRequest { // market_id is the id of the market that's expected to have the order. uint32 market_id = 1; @@ -143,13 +143,13 @@ message QueryGetOrderByExternalIDRequest { string external_id = 2; } -// QueryGetOrderByExternalIDResponse is a response message for the QueryGetOrderByExternalID endpoint. +// QueryGetOrderByExternalIDResponse is a response message for the GetOrderByExternalID query. message QueryGetOrderByExternalIDResponse { // order is the requested order. Order order = 1; } -// QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. +// QueryGetMarketOrdersRequest is a request message for the GetMarketOrders query. message QueryGetMarketOrdersRequest { // market_id is the id of the market to get all the orders for. uint32 market_id = 1; @@ -162,7 +162,7 @@ message QueryGetMarketOrdersRequest { cosmos.base.query.v1beta1.PageRequest pagination = 99; } -// QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders endpoint. +// QueryGetMarketOrdersResponse is a response message for the GetMarketOrders query. message QueryGetMarketOrdersResponse { // orders are a page of the orders in the provided market. repeated Order orders = 1; @@ -171,7 +171,7 @@ message QueryGetMarketOrdersResponse { cosmos.base.query.v1beta1.PageResponse pagination = 99; } -// QueryGetOwnerOrdersRequest is a request message for the QueryGetOwnerOrders endpoint. +// QueryGetOwnerOrdersRequest is a request message for the GetOwnerOrders query. message QueryGetOwnerOrdersRequest { // owner is the bech32 address string of the owner to get the orders for. string owner = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; @@ -184,7 +184,7 @@ message QueryGetOwnerOrdersRequest { cosmos.base.query.v1beta1.PageRequest pagination = 99; } -// QueryGetOwnerOrdersResponse is a response message for the QueryGetOwnerOrders endpoint. +// QueryGetOwnerOrdersResponse is a response message for the GetOwnerOrders query. message QueryGetOwnerOrdersResponse { // orders are a page of the orders for the provided address. repeated Order orders = 1; @@ -193,7 +193,7 @@ message QueryGetOwnerOrdersResponse { cosmos.base.query.v1beta1.PageResponse pagination = 99; } -// QueryGetAssetOrdersRequest is a request message for the QueryGetAssetOrders endpoint. +// QueryGetAssetOrdersRequest is a request message for the GetAssetOrders query. message QueryGetAssetOrdersRequest { // asset is the denom of assets to get orders for. string asset = 1; @@ -206,7 +206,7 @@ message QueryGetAssetOrdersRequest { cosmos.base.query.v1beta1.PageRequest pagination = 99; } -// QueryGetAssetOrdersResponse is a response message for the QueryGetAssetOrders endpoint. +// QueryGetAssetOrdersResponse is a response message for the GetAssetOrders query. message QueryGetAssetOrdersResponse { // orders are a page of the orders for the provided asset. repeated Order orders = 1; @@ -215,13 +215,13 @@ message QueryGetAssetOrdersResponse { cosmos.base.query.v1beta1.PageResponse pagination = 99; } -// QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint. +// QueryGetAllOrdersRequest is a request message for the GetAllOrders query. message QueryGetAllOrdersRequest { // pagination defines an optional pagination for the request. cosmos.base.query.v1beta1.PageRequest pagination = 99; } -// QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoint. +// QueryGetAllOrdersResponse is a response message for the GetAllOrders query. message QueryGetAllOrdersResponse { // orders are a page of the all orders. repeated Order orders = 1; @@ -230,13 +230,13 @@ message QueryGetAllOrdersResponse { cosmos.base.query.v1beta1.PageResponse pagination = 99; } -// QueryGetMarketRequest is a request message for the QueryGetMarket endpoint. +// QueryGetMarketRequest is a request message for the GetMarket query. message QueryGetMarketRequest { // market_id is the id of the market to look up. uint32 market_id = 1; } -// QueryGetMarketResponse is a response message for the QueryGetMarket endpoint. +// QueryGetMarketResponse is a response message for the GetMarket query. message QueryGetMarketResponse { // address is the bech32 address string of this market's account. string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; @@ -244,13 +244,13 @@ message QueryGetMarketResponse { Market market = 2; } -// QueryGetAllMarketsRequest is a request message for the QueryGetAllMarkets endpoint. +// QueryGetAllMarketsRequest is a request message for the GetAllMarkets query. message QueryGetAllMarketsRequest { // pagination defines an optional pagination for the request. cosmos.base.query.v1beta1.PageRequest pagination = 99; } -// QueryGetAllMarketsResponse is a response message for the QueryGetAllMarkets endpoint. +// QueryGetAllMarketsResponse is a response message for the GetAllMarkets query. message QueryGetAllMarketsResponse { // markets are a page of the briefs for all markets. repeated MarketBrief markets = 1; @@ -259,22 +259,22 @@ message QueryGetAllMarketsResponse { cosmos.base.query.v1beta1.PageResponse pagination = 99; } -// QueryParamsRequest is a request message for the QueryParams endpoint. +// QueryParamsRequest is a request message for the Params query. message QueryParamsRequest {} -// QueryParamsResponse is a response message for the QueryParams endpoint. +// QueryParamsResponse is a response message for the Params query. message QueryParamsResponse { // params are the exchange module parameter values. Params params = 1; } -// QueryValidateCreateMarketRequest is a request message for the QueryValidateCreateMarket endpoint. +// QueryValidateCreateMarketRequest is a request message for the ValidateCreateMarket query. message QueryValidateCreateMarketRequest { // create_market_request is the request to run validation on. MsgGovCreateMarketRequest create_market_request = 1; } -// QueryValidateCreateMarketResponse is a response message for the QueryValidateCreateMarket endpoint. +// QueryValidateCreateMarketResponse is a response message for the ValidateCreateMarket query. message QueryValidateCreateMarketResponse { // error is any problems or inconsistencies in the provided gov prop msg. // This goes above and beyond the validation done when actually processing the governance proposal. @@ -286,25 +286,25 @@ message QueryValidateCreateMarketResponse { bool gov_prop_will_pass = 2; } -// QueryValidateMarketRequest is a request message for the QueryValidateMarket endpoint. +// QueryValidateMarketRequest is a request message for the ValidateMarket query. message QueryValidateMarketRequest { // market_id is the id of the market to check. uint32 market_id = 1; } -// QueryValidateMarketResponse is a response message for the QueryValidateMarket endpoint. +// QueryValidateMarketResponse is a response message for the ValidateMarket query. message QueryValidateMarketResponse { // error is any problems or inconsistencies in the provided market. string error = 1; } -// QueryValidateManageFeesRequest is a request message for the QueryValidateManageFees endpoint. +// QueryValidateManageFeesRequest is a request message for the ValidateManageFees query. message QueryValidateManageFeesRequest { // manage_fees_request is the request to run validation on. MsgGovManageFeesRequest manage_fees_request = 1; } -// QueryValidateManageFeesResponse is a response message for the QueryValidateManageFees endpoint. +// QueryValidateManageFeesResponse is a response message for the ValidateManageFees query. message QueryValidateManageFeesResponse { // error is any problems or inconsistencies in the provided gov prop msg. // This goes above and beyond the validation done when actually processing the governance proposal. diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index a7177b9e81..6824bf1967 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -26,8 +26,8 @@ func NewQueryServer(k Keeper) exchange.QueryServer { var _ exchange.QueryServer = QueryServer{} -// QueryOrderFeeCalc calculates the fees that will be associated with the provided order. -func (k QueryServer) QueryOrderFeeCalc(goCtx context.Context, req *exchange.QueryOrderFeeCalcRequest) (*exchange.QueryOrderFeeCalcResponse, error) { +// OrderFeeCalc calculates the fees that will be associated with the provided order. +func (k QueryServer) OrderFeeCalc(goCtx context.Context, req *exchange.QueryOrderFeeCalcRequest) (*exchange.QueryOrderFeeCalcResponse, error) { if req == nil || (req.AskOrder == nil && req.BidOrder == nil) { return nil, status.Error(codes.InvalidArgument, "empty request") } @@ -64,20 +64,20 @@ func (k QueryServer) QueryOrderFeeCalc(goCtx context.Context, req *exchange.Quer resp.CreationFeeOptions = getCreateBidFlatFees(store, order.MarketId) default: // This case should have been caught right off the bat in this query. - panic(fmt.Errorf("missing QueryOrderFeeCalc case")) + panic(fmt.Errorf("missing OrderFeeCalc case")) } return resp, nil } -// QueryGetOrder looks up an order by id. -func (k QueryServer) QueryGetOrder(goCtx context.Context, req *exchange.QueryGetOrderRequest) (*exchange.QueryGetOrderResponse, error) { +// GetOrder looks up an order by id. +func (k QueryServer) GetOrder(goCtx context.Context, req *exchange.QueryGetOrderRequest) (*exchange.QueryGetOrderResponse, error) { if req == nil || req.OrderId == 0 { return nil, status.Error(codes.InvalidArgument, "empty request") } ctx := sdk.UnwrapSDKContext(goCtx) - order, err := k.GetOrder(ctx, req.OrderId) + order, err := k.Keeper.GetOrder(ctx, req.OrderId) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } @@ -88,14 +88,14 @@ func (k QueryServer) QueryGetOrder(goCtx context.Context, req *exchange.QueryGet return &exchange.QueryGetOrderResponse{Order: order}, nil } -// QueryGetOrderByExternalID looks up an order by market id and external id. -func (k QueryServer) QueryGetOrderByExternalID(goCtx context.Context, req *exchange.QueryGetOrderByExternalIDRequest) (*exchange.QueryGetOrderByExternalIDResponse, error) { +// GetOrderByExternalID looks up an order by market id and external id. +func (k QueryServer) GetOrderByExternalID(goCtx context.Context, req *exchange.QueryGetOrderByExternalIDRequest) (*exchange.QueryGetOrderByExternalIDResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") } ctx := sdk.UnwrapSDKContext(goCtx) - order, err := k.GetOrderByExternalID(ctx, req.MarketId, req.ExternalId) + order, err := k.Keeper.GetOrderByExternalID(ctx, req.MarketId, req.ExternalId) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } @@ -107,8 +107,8 @@ func (k QueryServer) QueryGetOrderByExternalID(goCtx context.Context, req *excha return &exchange.QueryGetOrderByExternalIDResponse{Order: order}, nil } -// QueryGetMarketOrders looks up the orders in a market. -func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.QueryGetMarketOrdersRequest) (*exchange.QueryGetMarketOrdersResponse, error) { +// GetMarketOrders looks up the orders in a market. +func (k QueryServer) GetMarketOrders(goCtx context.Context, req *exchange.QueryGetMarketOrdersRequest) (*exchange.QueryGetMarketOrdersResponse, error) { if req == nil || req.MarketId == 0 { return nil, status.Error(codes.InvalidArgument, "empty request") } @@ -128,8 +128,8 @@ func (k QueryServer) QueryGetMarketOrders(goCtx context.Context, req *exchange.Q return resp, nil } -// QueryGetOwnerOrders looks up the orders from the provided owner address. -func (k QueryServer) QueryGetOwnerOrders(goCtx context.Context, req *exchange.QueryGetOwnerOrdersRequest) (*exchange.QueryGetOwnerOrdersResponse, error) { +// GetOwnerOrders looks up the orders from the provided owner address. +func (k QueryServer) GetOwnerOrders(goCtx context.Context, req *exchange.QueryGetOwnerOrdersRequest) (*exchange.QueryGetOwnerOrdersResponse, error) { if req == nil || len(req.Owner) == 0 { return nil, status.Error(codes.InvalidArgument, "empty request") } @@ -154,8 +154,8 @@ func (k QueryServer) QueryGetOwnerOrders(goCtx context.Context, req *exchange.Qu return resp, nil } -// QueryGetAssetOrders looks up the orders for a specific asset denom. -func (k QueryServer) QueryGetAssetOrders(goCtx context.Context, req *exchange.QueryGetAssetOrdersRequest) (*exchange.QueryGetAssetOrdersResponse, error) { +// GetAssetOrders looks up the orders for a specific asset denom. +func (k QueryServer) GetAssetOrders(goCtx context.Context, req *exchange.QueryGetAssetOrdersRequest) (*exchange.QueryGetAssetOrdersResponse, error) { if req == nil || len(req.Asset) == 0 { return nil, status.Error(codes.InvalidArgument, "empty request") } @@ -175,8 +175,8 @@ func (k QueryServer) QueryGetAssetOrders(goCtx context.Context, req *exchange.Qu return resp, nil } -// QueryGetAllOrders gets all orders in the exchange module. -func (k QueryServer) QueryGetAllOrders(goCtx context.Context, req *exchange.QueryGetAllOrdersRequest) (*exchange.QueryGetAllOrdersResponse, error) { +// GetAllOrders gets all orders in the exchange module. +func (k QueryServer) GetAllOrders(goCtx context.Context, req *exchange.QueryGetAllOrdersRequest) (*exchange.QueryGetAllOrdersResponse, error) { var pagination *query.PageRequest if req != nil { pagination = req.Pagination @@ -212,14 +212,14 @@ func (k QueryServer) QueryGetAllOrders(goCtx context.Context, req *exchange.Quer return resp, nil } -// QueryGetMarket returns all the information and details about a market. -func (k QueryServer) QueryGetMarket(goCtx context.Context, req *exchange.QueryGetMarketRequest) (*exchange.QueryGetMarketResponse, error) { +// GetMarket returns all the information and details about a market. +func (k QueryServer) GetMarket(goCtx context.Context, req *exchange.QueryGetMarketRequest) (*exchange.QueryGetMarketResponse, error) { if req == nil || req.MarketId == 0 { return nil, status.Error(codes.InvalidArgument, "empty request") } ctx := sdk.UnwrapSDKContext(goCtx) - market := k.GetMarket(ctx, req.MarketId) + market := k.Keeper.GetMarket(ctx, req.MarketId) if market == nil { return nil, status.Errorf(codes.InvalidArgument, "market %d not found", req.MarketId) } @@ -227,8 +227,8 @@ func (k QueryServer) QueryGetMarket(goCtx context.Context, req *exchange.QueryGe return &exchange.QueryGetMarketResponse{Market: market}, nil } -// QueryGetAllMarkets returns brief information about each market. -func (k QueryServer) QueryGetAllMarkets(goCtx context.Context, req *exchange.QueryGetAllMarketsRequest) (*exchange.QueryGetAllMarketsResponse, error) { +// GetAllMarkets returns brief information about each market. +func (k QueryServer) GetAllMarkets(goCtx context.Context, req *exchange.QueryGetAllMarketsRequest) (*exchange.QueryGetAllMarketsResponse, error) { var pagination *query.PageRequest if req != nil { pagination = req.Pagination @@ -264,15 +264,15 @@ func (k QueryServer) QueryGetAllMarkets(goCtx context.Context, req *exchange.Que return resp, nil } -// QueryParams returns the exchange module parameters. -func (k QueryServer) QueryParams(goCtx context.Context, _ *exchange.QueryParamsRequest) (*exchange.QueryParamsResponse, error) { +// Params returns the exchange module parameters. +func (k QueryServer) Params(goCtx context.Context, _ *exchange.QueryParamsRequest) (*exchange.QueryParamsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) resp := &exchange.QueryParamsResponse{Params: k.GetParamsOrDefaults(ctx)} return resp, nil } -// QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. -func (k QueryServer) QueryValidateCreateMarket(goCtx context.Context, req *exchange.QueryValidateCreateMarketRequest) (*exchange.QueryValidateCreateMarketResponse, error) { +// ValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. +func (k QueryServer) ValidateCreateMarket(goCtx context.Context, req *exchange.QueryValidateCreateMarketRequest) (*exchange.QueryValidateCreateMarketResponse, error) { if req == nil || req.CreateMarketRequest == nil { return nil, status.Error(codes.InvalidArgument, "empty request") } @@ -292,7 +292,7 @@ func (k QueryServer) QueryValidateCreateMarket(goCtx context.Context, req *excha // The SDK *should* already be using a cache context for queries, but I'm doing it here too just to be on the safe side. ctx, _ := sdk.UnwrapSDKContext(goCtx).CacheContext() - marketID, err := k.CreateMarket(ctx, msg.Market) + marketID, err := k.Keeper.CreateMarket(ctx, msg.Market) if err != nil { resp.Error = err.Error() return resp, nil @@ -308,7 +308,7 @@ func (k QueryServer) QueryValidateCreateMarket(goCtx context.Context, req *excha errs = append(errs, err) } - if err = k.ValidateMarket(ctx, marketID); err != nil { + if err = k.Keeper.ValidateMarket(ctx, marketID); err != nil { errs = append(errs, err) } @@ -319,23 +319,23 @@ func (k QueryServer) QueryValidateCreateMarket(goCtx context.Context, req *excha return resp, nil } -// QueryValidateMarket checks for any problems with a market's setup. -func (k QueryServer) QueryValidateMarket(goCtx context.Context, req *exchange.QueryValidateMarketRequest) (*exchange.QueryValidateMarketResponse, error) { +// ValidateMarket checks for any problems with a market's setup. +func (k QueryServer) ValidateMarket(goCtx context.Context, req *exchange.QueryValidateMarketRequest) (*exchange.QueryValidateMarketResponse, error) { if req == nil || req.MarketId == 0 { return nil, status.Error(codes.InvalidArgument, "empty request") } ctx := sdk.UnwrapSDKContext(goCtx) resp := &exchange.QueryValidateMarketResponse{} - if err := k.ValidateMarket(ctx, req.MarketId); err != nil { + if err := k.Keeper.ValidateMarket(ctx, req.MarketId); err != nil { resp.Error = err.Error() } return resp, nil } -// QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. -func (k QueryServer) QueryValidateManageFees(goCtx context.Context, req *exchange.QueryValidateManageFeesRequest) (*exchange.QueryValidateManageFeesResponse, error) { +// ValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. +func (k QueryServer) ValidateManageFees(goCtx context.Context, req *exchange.QueryValidateManageFeesRequest) (*exchange.QueryValidateManageFeesResponse, error) { if req == nil || req.ManageFeesRequest == nil { return nil, status.Error(codes.InvalidArgument, "empty request") } @@ -401,7 +401,7 @@ func (k QueryServer) QueryValidateManageFees(goCtx context.Context, req *exchang } k.UpdateFees(ctx, msg) - if err := k.ValidateMarket(ctx, msg.MarketId); err != nil { + if err := k.Keeper.ValidateMarket(ctx, msg.MarketId); err != nil { errs = append(errs, err) } diff --git a/x/exchange/keeper/grpc_query_test.go b/x/exchange/keeper/grpc_query_test.go index c78e483e08..c3aa0cfa80 100644 --- a/x/exchange/keeper/grpc_query_test.go +++ b/x/exchange/keeper/grpc_query_test.go @@ -1,27 +1,27 @@ package keeper_test -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryOrderFeeCalc() +// TODO[1658]: func (s *TestSuite) TestQueryServer_OrderFeeCalc() -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetOrder() +// TODO[1658]: func (s *TestSuite) TestQueryServer_GetOrder() -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetOrderByExternalID() +// TODO[1658]: func (s *TestSuite) TestQueryServer_GetOrderByExternalID() -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetMarketOrders() +// TODO[1658]: func (s *TestSuite) TestQueryServer_GetMarketOrders() -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetOwnerOrders() +// TODO[1658]: func (s *TestSuite) TestQueryServer_GetOwnerOrders() -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetAssetOrders() +// TODO[1658]: func (s *TestSuite) TestQueryServer_GetAssetOrders() -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetAllOrders() +// TODO[1658]: func (s *TestSuite) TestQueryServer_GetAllOrders() -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetMarket() +// TODO[1658]: func (s *TestSuite) TestQueryServer_GetMarket() -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryGetAllMarkets() +// TODO[1658]: func (s *TestSuite) TestQueryServer_GetAllMarkets() -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryParams() +// TODO[1658]: func (s *TestSuite) TestQueryServer_Params() -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryValidateCreateMarket() +// TODO[1658]: func (s *TestSuite) TestQueryServer_ValidateCreateMarket() -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryValidateMarket() +// TODO[1658]: func (s *TestSuite) TestQueryServer_ValidateMarket() -// TODO[1658]: func (s *TestSuite) TestQueryServer_QueryValidateManageFees() +// TODO[1658]: func (s *TestSuite) TestQueryServer_ValidateManageFees() diff --git a/x/exchange/query.pb.go b/x/exchange/query.pb.go index 5b93f1b8ad..5cd58e502c 100644 --- a/x/exchange/query.pb.go +++ b/x/exchange/query.pb.go @@ -32,7 +32,7 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// QueryOrderFeeCalcRequest is a request message for the QueryOrderFeeCalc endpoint. +// QueryOrderFeeCalcRequest is a request message for the OrderFeeCalc query. // Exactly one of ask_order or bid_order must be provided. type QueryOrderFeeCalcRequest struct { // ask_order is the ask order to calculate the fees for. @@ -88,7 +88,7 @@ func (m *QueryOrderFeeCalcRequest) GetBidOrder() *BidOrder { return nil } -// QueryOrderFeeCalcResponse is a response message for the QueryOrderFeeCalc endpoint. +// QueryOrderFeeCalcResponse is a response message for the OrderFeeCalc query. type QueryOrderFeeCalcResponse struct { // creation_fee_options are the order creation flat fee options available for creating the provided order. // If it's empty, no order creation fee is required. @@ -163,7 +163,7 @@ func (m *QueryOrderFeeCalcResponse) GetSettlementRatioFeeOptions() []types.Coin return nil } -// QueryGetOrderRequest is a request message for the QueryGetOrder endpoint. +// QueryGetOrderRequest is a request message for the GetOrder query. type QueryGetOrderRequest struct { // order_id is the id of the order to look up. OrderId uint64 `protobuf:"varint,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` @@ -209,7 +209,7 @@ func (m *QueryGetOrderRequest) GetOrderId() uint64 { return 0 } -// QueryGetOrderResponse is a response message for the QueryGetOrder endpoint. +// QueryGetOrderResponse is a response message for the GetOrder query. type QueryGetOrderResponse struct { // order is the requested order. Order *Order `protobuf:"bytes,1,opt,name=order,proto3" json:"order,omitempty"` @@ -255,7 +255,7 @@ func (m *QueryGetOrderResponse) GetOrder() *Order { return nil } -// QueryGetOrderByExternalIDRequest is a request message for the QueryGetOrderByExternalID endpoint. +// QueryGetOrderByExternalIDRequest is a request message for the GetOrderByExternalID query. type QueryGetOrderByExternalIDRequest struct { // market_id is the id of the market that's expected to have the order. MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` @@ -310,7 +310,7 @@ func (m *QueryGetOrderByExternalIDRequest) GetExternalId() string { return "" } -// QueryGetOrderByExternalIDResponse is a response message for the QueryGetOrderByExternalID endpoint. +// QueryGetOrderByExternalIDResponse is a response message for the GetOrderByExternalID query. type QueryGetOrderByExternalIDResponse struct { // order is the requested order. Order *Order `protobuf:"bytes,1,opt,name=order,proto3" json:"order,omitempty"` @@ -356,7 +356,7 @@ func (m *QueryGetOrderByExternalIDResponse) GetOrder() *Order { return nil } -// QueryGetMarketOrdersRequest is a request message for the QueryGetMarketOrders endpoint. +// QueryGetMarketOrdersRequest is a request message for the GetMarketOrders query. type QueryGetMarketOrdersRequest struct { // market_id is the id of the market to get all the orders for. MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` @@ -429,7 +429,7 @@ func (m *QueryGetMarketOrdersRequest) GetPagination() *query.PageRequest { return nil } -// QueryGetMarketOrdersResponse is a response message for the QueryGetMarketOrders endpoint. +// QueryGetMarketOrdersResponse is a response message for the GetMarketOrders query. type QueryGetMarketOrdersResponse struct { // orders are a page of the orders in the provided market. Orders []*Order `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` @@ -484,7 +484,7 @@ func (m *QueryGetMarketOrdersResponse) GetPagination() *query.PageResponse { return nil } -// QueryGetOwnerOrdersRequest is a request message for the QueryGetOwnerOrders endpoint. +// QueryGetOwnerOrdersRequest is a request message for the GetOwnerOrders query. type QueryGetOwnerOrdersRequest struct { // owner is the bech32 address string of the owner to get the orders for. Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` @@ -557,7 +557,7 @@ func (m *QueryGetOwnerOrdersRequest) GetPagination() *query.PageRequest { return nil } -// QueryGetOwnerOrdersResponse is a response message for the QueryGetOwnerOrders endpoint. +// QueryGetOwnerOrdersResponse is a response message for the GetOwnerOrders query. type QueryGetOwnerOrdersResponse struct { // orders are a page of the orders for the provided address. Orders []*Order `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` @@ -612,7 +612,7 @@ func (m *QueryGetOwnerOrdersResponse) GetPagination() *query.PageResponse { return nil } -// QueryGetAssetOrdersRequest is a request message for the QueryGetAssetOrders endpoint. +// QueryGetAssetOrdersRequest is a request message for the GetAssetOrders query. type QueryGetAssetOrdersRequest struct { // asset is the denom of assets to get orders for. Asset string `protobuf:"bytes,1,opt,name=asset,proto3" json:"asset,omitempty"` @@ -685,7 +685,7 @@ func (m *QueryGetAssetOrdersRequest) GetPagination() *query.PageRequest { return nil } -// QueryGetAssetOrdersResponse is a response message for the QueryGetAssetOrders endpoint. +// QueryGetAssetOrdersResponse is a response message for the GetAssetOrders query. type QueryGetAssetOrdersResponse struct { // orders are a page of the orders for the provided asset. Orders []*Order `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` @@ -740,7 +740,7 @@ func (m *QueryGetAssetOrdersResponse) GetPagination() *query.PageResponse { return nil } -// QueryGetAllOrdersRequest is a request message for the QueryGetAllOrders endpoint. +// QueryGetAllOrdersRequest is a request message for the GetAllOrders query. type QueryGetAllOrdersRequest struct { // pagination defines an optional pagination for the request. Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` @@ -786,7 +786,7 @@ func (m *QueryGetAllOrdersRequest) GetPagination() *query.PageRequest { return nil } -// QueryGetAllOrdersResponse is a response message for the QueryGetAllOrders endpoint. +// QueryGetAllOrdersResponse is a response message for the GetAllOrders query. type QueryGetAllOrdersResponse struct { // orders are a page of the all orders. Orders []*Order `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` @@ -841,7 +841,7 @@ func (m *QueryGetAllOrdersResponse) GetPagination() *query.PageResponse { return nil } -// QueryGetMarketRequest is a request message for the QueryGetMarket endpoint. +// QueryGetMarketRequest is a request message for the GetMarket query. type QueryGetMarketRequest struct { // market_id is the id of the market to look up. MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` @@ -887,7 +887,7 @@ func (m *QueryGetMarketRequest) GetMarketId() uint32 { return 0 } -// QueryGetMarketResponse is a response message for the QueryGetMarket endpoint. +// QueryGetMarketResponse is a response message for the GetMarket query. type QueryGetMarketResponse struct { // address is the bech32 address string of this market's account. Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` @@ -942,7 +942,7 @@ func (m *QueryGetMarketResponse) GetMarket() *Market { return nil } -// QueryGetAllMarketsRequest is a request message for the QueryGetAllMarkets endpoint. +// QueryGetAllMarketsRequest is a request message for the GetAllMarkets query. type QueryGetAllMarketsRequest struct { // pagination defines an optional pagination for the request. Pagination *query.PageRequest `protobuf:"bytes,99,opt,name=pagination,proto3" json:"pagination,omitempty"` @@ -988,7 +988,7 @@ func (m *QueryGetAllMarketsRequest) GetPagination() *query.PageRequest { return nil } -// QueryGetAllMarketsResponse is a response message for the QueryGetAllMarkets endpoint. +// QueryGetAllMarketsResponse is a response message for the GetAllMarkets query. type QueryGetAllMarketsResponse struct { // markets are a page of the briefs for all markets. Markets []*MarketBrief `protobuf:"bytes,1,rep,name=markets,proto3" json:"markets,omitempty"` @@ -1043,7 +1043,7 @@ func (m *QueryGetAllMarketsResponse) GetPagination() *query.PageResponse { return nil } -// QueryParamsRequest is a request message for the QueryParams endpoint. +// QueryParamsRequest is a request message for the Params query. type QueryParamsRequest struct { } @@ -1080,7 +1080,7 @@ func (m *QueryParamsRequest) XXX_DiscardUnknown() { var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo -// QueryParamsResponse is a response message for the QueryParams endpoint. +// QueryParamsResponse is a response message for the Params query. type QueryParamsResponse struct { // params are the exchange module parameter values. Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` @@ -1126,7 +1126,7 @@ func (m *QueryParamsResponse) GetParams() *Params { return nil } -// QueryValidateCreateMarketRequest is a request message for the QueryValidateCreateMarket endpoint. +// QueryValidateCreateMarketRequest is a request message for the ValidateCreateMarket query. type QueryValidateCreateMarketRequest struct { // create_market_request is the request to run validation on. CreateMarketRequest *MsgGovCreateMarketRequest `protobuf:"bytes,1,opt,name=create_market_request,json=createMarketRequest,proto3" json:"create_market_request,omitempty"` @@ -1172,7 +1172,7 @@ func (m *QueryValidateCreateMarketRequest) GetCreateMarketRequest() *MsgGovCreat return nil } -// QueryValidateCreateMarketResponse is a response message for the QueryValidateCreateMarket endpoint. +// QueryValidateCreateMarketResponse is a response message for the ValidateCreateMarket query. type QueryValidateCreateMarketResponse struct { // error is any problems or inconsistencies in the provided gov prop msg. // This goes above and beyond the validation done when actually processing the governance proposal. @@ -1231,7 +1231,7 @@ func (m *QueryValidateCreateMarketResponse) GetGovPropWillPass() bool { return false } -// QueryValidateMarketRequest is a request message for the QueryValidateMarket endpoint. +// QueryValidateMarketRequest is a request message for the ValidateMarket query. type QueryValidateMarketRequest struct { // market_id is the id of the market to check. MarketId uint32 `protobuf:"varint,1,opt,name=market_id,json=marketId,proto3" json:"market_id,omitempty"` @@ -1277,7 +1277,7 @@ func (m *QueryValidateMarketRequest) GetMarketId() uint32 { return 0 } -// QueryValidateMarketResponse is a response message for the QueryValidateMarket endpoint. +// QueryValidateMarketResponse is a response message for the ValidateMarket query. type QueryValidateMarketResponse struct { // error is any problems or inconsistencies in the provided market. Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` @@ -1323,7 +1323,7 @@ func (m *QueryValidateMarketResponse) GetError() string { return "" } -// QueryValidateManageFeesRequest is a request message for the QueryValidateManageFees endpoint. +// QueryValidateManageFeesRequest is a request message for the ValidateManageFees query. type QueryValidateManageFeesRequest struct { // manage_fees_request is the request to run validation on. ManageFeesRequest *MsgGovManageFeesRequest `protobuf:"bytes,1,opt,name=manage_fees_request,json=manageFeesRequest,proto3" json:"manage_fees_request,omitempty"` @@ -1369,7 +1369,7 @@ func (m *QueryValidateManageFeesRequest) GetManageFeesRequest() *MsgGovManageFee return nil } -// QueryValidateManageFeesResponse is a response message for the QueryValidateManageFees endpoint. +// QueryValidateManageFeesResponse is a response message for the ValidateManageFees query. type QueryValidateManageFeesResponse struct { // error is any problems or inconsistencies in the provided gov prop msg. // This goes above and beyond the validation done when actually processing the governance proposal. @@ -1462,100 +1462,99 @@ func init() { } var fileDescriptor_00949b75b1c10bfe = []byte{ - // 1480 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4d, 0x6f, 0x13, 0xc7, - 0x1b, 0xcf, 0x24, 0x24, 0x24, 0x93, 0x3f, 0xfc, 0xc5, 0xc4, 0x50, 0xc7, 0x80, 0x13, 0x16, 0x04, - 0x51, 0x20, 0xbb, 0xd8, 0x86, 0xb4, 0x1c, 0x68, 0x4b, 0x68, 0x83, 0x22, 0x15, 0x11, 0xb6, 0x55, - 0x5b, 0x71, 0xa8, 0x19, 0xdb, 0x93, 0x65, 0xc5, 0x7a, 0x67, 0xd9, 0x59, 0x4c, 0xa2, 0x28, 0x87, - 0xbe, 0x5c, 0xda, 0x43, 0x55, 0xa9, 0x97, 0x4a, 0x15, 0xa8, 0x52, 0xf9, 0x00, 0x3d, 0x70, 0x6f, - 0x6f, 0xe5, 0x88, 0x5a, 0x55, 0x6a, 0x2f, 0x55, 0x05, 0xfd, 0x02, 0xed, 0x27, 0xa8, 0x76, 0x66, - 0xd6, 0xde, 0xb5, 0xf7, 0xcd, 0x6d, 0x0e, 0x39, 0xc5, 0x3b, 0xfb, 0xbc, 0xfc, 0x9e, 0xdf, 0x33, - 0xfb, 0xcc, 0x6f, 0x02, 0x15, 0xc7, 0xa5, 0x1d, 0x62, 0x63, 0xbb, 0x49, 0x34, 0xb2, 0xd9, 0xbc, - 0x83, 0x6d, 0x83, 0x68, 0x9d, 0x8a, 0x76, 0xef, 0x3e, 0x71, 0xb7, 0x54, 0xc7, 0xa5, 0x1e, 0x45, - 0x47, 0x7a, 0x36, 0x6a, 0x60, 0xa3, 0x76, 0x2a, 0xa5, 0xd9, 0x26, 0x65, 0x6d, 0xca, 0xea, 0xdc, - 0x4a, 0x13, 0x0f, 0xc2, 0xa5, 0xb4, 0x28, 0x9e, 0xb4, 0x06, 0x66, 0x44, 0xc4, 0xd2, 0x3a, 0x95, - 0x06, 0xf1, 0x70, 0x45, 0x73, 0xb0, 0x61, 0xda, 0xd8, 0x33, 0xa9, 0x2d, 0x6d, 0xcb, 0x61, 0xdb, - 0xc0, 0xaa, 0x49, 0xcd, 0xe0, 0xfd, 0x31, 0x83, 0x52, 0xc3, 0x22, 0x1a, 0x76, 0x4c, 0x0d, 0xdb, - 0x36, 0xf5, 0xb8, 0x73, 0x90, 0xa9, 0x60, 0x50, 0x83, 0x0a, 0x04, 0xfe, 0x2f, 0xb9, 0x7a, 0x32, - 0xa1, 0xac, 0x36, 0x76, 0xef, 0x12, 0x2f, 0xc3, 0x88, 0xba, 0x2d, 0xe2, 0xb2, 0x0c, 0x23, 0x07, - 0xbb, 0xb8, 0x1d, 0x18, 0xcd, 0x25, 0x18, 0x79, 0x9b, 0xc2, 0x40, 0xf9, 0x0a, 0xc0, 0xe2, 0x4d, - 0x9f, 0x86, 0x1b, 0x7e, 0xec, 0x55, 0x42, 0xae, 0x62, 0xab, 0xa9, 0x93, 0x7b, 0xf7, 0x09, 0xf3, - 0xd0, 0x65, 0x38, 0x85, 0xd9, 0xdd, 0x3a, 0x4f, 0x5b, 0x1c, 0x9d, 0x07, 0x0b, 0xd3, 0xd5, 0x79, - 0x35, 0x9e, 0x73, 0xf5, 0x0a, 0xbb, 0xcb, 0x43, 0xe8, 0x93, 0x58, 0xfe, 0xf2, 0xdd, 0x1b, 0x66, - 0x4b, 0xba, 0x8f, 0xa5, 0xbb, 0xaf, 0x98, 0x2d, 0xe9, 0xde, 0x90, 0xbf, 0x94, 0xef, 0x46, 0xe1, - 0x6c, 0x0c, 0x34, 0xe6, 0x50, 0x9b, 0x11, 0x74, 0x13, 0x16, 0x9a, 0x2e, 0xe1, 0x8c, 0xd7, 0x37, - 0x08, 0xa9, 0x53, 0x87, 0x93, 0x5f, 0x04, 0xf3, 0x63, 0x0b, 0xd3, 0xd5, 0x59, 0x55, 0x76, 0xdd, - 0xef, 0x9d, 0x2a, 0x7b, 0xa7, 0x5e, 0xa5, 0xa6, 0xbd, 0xb2, 0xef, 0xe9, 0xef, 0x73, 0x23, 0x3a, - 0x0a, 0x9c, 0x57, 0x09, 0xb9, 0x21, 0x5c, 0xd1, 0x07, 0xf0, 0x28, 0x23, 0x9e, 0x67, 0x91, 0x36, - 0xb1, 0xbd, 0xfa, 0x86, 0x85, 0xbd, 0x48, 0xe4, 0xd1, 0x7c, 0x91, 0x8b, 0xbd, 0x18, 0xab, 0x16, - 0xf6, 0x42, 0xf1, 0x6f, 0xc3, 0x63, 0xa1, 0xf8, 0xae, 0x9f, 0x3e, 0x92, 0x60, 0x2c, 0x5f, 0x82, - 0xd9, 0x5e, 0x10, 0xdd, 0x8f, 0xd1, 0xcb, 0xa0, 0x54, 0x60, 0x81, 0x33, 0x76, 0x8d, 0x78, 0x82, - 0x4d, 0xd9, 0xc8, 0x59, 0x38, 0xc9, 0xbb, 0x50, 0x37, 0x5b, 0x45, 0x30, 0x0f, 0x16, 0xf6, 0xe9, - 0xfb, 0xf9, 0xf3, 0x5a, 0x4b, 0x79, 0x0b, 0x1e, 0xee, 0x73, 0x91, 0x04, 0xd7, 0xe0, 0xb8, 0xe8, - 0x1c, 0xe0, 0x9d, 0x3b, 0x9e, 0xd4, 0x39, 0xe1, 0x25, 0x6c, 0x95, 0xdb, 0x70, 0x3e, 0x12, 0x6d, - 0x65, 0xeb, 0xcd, 0x4d, 0x8f, 0xb8, 0x36, 0xb6, 0xd6, 0xde, 0x08, 0xc0, 0x1c, 0x85, 0x53, 0x62, - 0xb7, 0x07, 0x68, 0x0e, 0xe8, 0x93, 0x62, 0x61, 0xad, 0x85, 0xe6, 0xe0, 0x34, 0x91, 0x1e, 0xfe, - 0x6b, 0x7f, 0xd3, 0x4d, 0xe9, 0x30, 0x58, 0x5a, 0x6b, 0x29, 0xef, 0xc3, 0x13, 0x29, 0x19, 0xfe, - 0x0b, 0xf6, 0x1f, 0x01, 0x3c, 0x1a, 0x84, 0xbe, 0xce, 0xf1, 0xf0, 0xd7, 0x2c, 0x17, 0xee, 0xe3, - 0x10, 0x0a, 0x86, 0xbd, 0x2d, 0x87, 0x48, 0xd8, 0x53, 0x7c, 0xe5, 0x9d, 0x2d, 0x87, 0xa0, 0x53, - 0xf0, 0x20, 0xde, 0xf0, 0x88, 0x5b, 0xef, 0xb6, 0x61, 0x8c, 0xb7, 0xe1, 0x7f, 0x7c, 0xf5, 0x86, - 0xe8, 0x05, 0x5a, 0x85, 0xb0, 0x37, 0x84, 0x8a, 0x4d, 0x8e, 0xfd, 0x74, 0x64, 0x3b, 0x88, 0xe9, - 0x17, 0x6c, 0x8a, 0x75, 0x6c, 0x10, 0x89, 0x4e, 0x0f, 0x79, 0x2a, 0x8f, 0x00, 0x3c, 0x16, 0x5f, - 0x89, 0xe4, 0xe7, 0x22, 0x9c, 0x10, 0xb3, 0x44, 0x7e, 0x2e, 0x19, 0x04, 0x49, 0x63, 0x74, 0x2d, - 0x06, 0xdf, 0x99, 0x4c, 0x7c, 0x22, 0x67, 0x04, 0xe0, 0x6f, 0x00, 0x96, 0xba, 0x5d, 0x7c, 0x60, - 0x4b, 0x06, 0xba, 0x4c, 0xab, 0x70, 0x9c, 0xfa, 0xab, 0x9c, 0xe5, 0xa9, 0x95, 0xe2, 0x4f, 0x4f, - 0x96, 0x0a, 0x32, 0xcb, 0x95, 0x56, 0xcb, 0x25, 0x8c, 0xbd, 0xed, 0xb9, 0xa6, 0x6d, 0xe8, 0xc2, - 0x6c, 0x6f, 0x91, 0xff, 0x30, 0xb4, 0x8d, 0x22, 0xb5, 0xed, 0x11, 0xee, 0xbf, 0x0f, 0x71, 0x7f, - 0x85, 0xb1, 0xfe, 0x5d, 0x5e, 0x80, 0xe3, 0xd8, 0x5f, 0x15, 0xdc, 0xeb, 0xe2, 0x61, 0xef, 0x32, - 0x1c, 0xa9, 0x60, 0x8f, 0x30, 0xdc, 0x90, 0x47, 0xaa, 0x0f, 0xcf, 0xb2, 0xa2, 0xf4, 0xee, 0x16, - 0x07, 0x5f, 0x03, 0x79, 0x38, 0x46, 0x93, 0xec, 0x11, 0x06, 0x2e, 0xf4, 0x0e, 0x15, 0x31, 0x7f, - 0xf2, 0xcc, 0x50, 0xe5, 0x13, 0x00, 0x8f, 0xf4, 0xbb, 0xc9, 0x82, 0xaa, 0x70, 0x3f, 0x16, 0x5f, - 0x7e, 0xe6, 0x4c, 0x08, 0x0c, 0xd1, 0x32, 0x9c, 0x10, 0xa1, 0xa5, 0x74, 0x29, 0x27, 0x91, 0x20, - 0x73, 0x49, 0x6b, 0xa5, 0x19, 0x61, 0x56, 0xbc, 0xdc, 0xf5, 0xfe, 0x3d, 0x0e, 0x7f, 0x85, 0xa1, - 0x2c, 0xb2, 0xde, 0xcb, 0x70, 0xbf, 0x40, 0x13, 0x74, 0xf0, 0x64, 0x3a, 0xf8, 0x15, 0xd7, 0x24, - 0x1b, 0x7a, 0xe0, 0xb3, 0x7b, 0x8d, 0x2c, 0x40, 0xc4, 0x51, 0xae, 0x73, 0x51, 0x29, 0x0b, 0x51, - 0xae, 0xc3, 0x99, 0xc8, 0xaa, 0x04, 0xbd, 0x0c, 0x27, 0x84, 0xf8, 0x94, 0xc7, 0x6e, 0x22, 0xe1, - 0xd2, 0x4f, 0x5a, 0x2b, 0x9f, 0x02, 0xa9, 0x1a, 0xde, 0xc5, 0x96, 0xd9, 0xc2, 0x1e, 0xb9, 0xea, - 0x6b, 0x33, 0x12, 0xdd, 0x39, 0x04, 0x1e, 0xe6, 0x92, 0x8d, 0xd4, 0xe5, 0x06, 0x72, 0xc5, 0x0b, - 0x99, 0xab, 0x92, 0xc8, 0x0f, 0x33, 0xae, 0xd1, 0x4e, 0x4c, 0x44, 0x7d, 0xa6, 0x39, 0xb8, 0xa8, - 0x6c, 0x48, 0x79, 0x11, 0x0f, 0x45, 0x16, 0x5a, 0x80, 0xe3, 0xc4, 0x75, 0xa9, 0x1b, 0xcc, 0x48, - 0xfe, 0x80, 0xce, 0x42, 0x64, 0xd0, 0x8e, 0x7f, 0xe9, 0x70, 0xea, 0x0f, 0x4c, 0xcb, 0xaa, 0x3b, - 0x98, 0x31, 0xbe, 0xf7, 0x26, 0xf5, 0xff, 0x1b, 0xb4, 0xb3, 0xee, 0x52, 0xe7, 0x3d, 0xd3, 0xb2, - 0xd6, 0x31, 0x63, 0xca, 0x25, 0xd9, 0xfe, 0x20, 0xcf, 0x10, 0x9f, 0x49, 0x4d, 0x4e, 0xbf, 0x7e, - 0xd7, 0x34, 0x70, 0xca, 0x87, 0x00, 0x96, 0xfb, 0xbc, 0x6c, 0x6c, 0x90, 0x55, 0x42, 0xba, 0x5b, - 0xbb, 0x0e, 0x67, 0xda, 0x7c, 0xd1, 0x17, 0xa5, 0xac, 0x8f, 0x5f, 0x2d, 0x9d, 0xdf, 0x81, 0x68, - 0xfa, 0xa1, 0x76, 0xff, 0x92, 0xd2, 0x82, 0x73, 0x89, 0x10, 0x76, 0x8d, 0xd9, 0xea, 0x2f, 0x33, - 0x70, 0x9c, 0xa7, 0x41, 0x8f, 0x01, 0x3c, 0x34, 0x70, 0x81, 0x40, 0xe7, 0x93, 0x2a, 0x49, 0xba, - 0x06, 0x95, 0x2a, 0x43, 0x78, 0x88, 0x3a, 0x94, 0xc5, 0x8f, 0x7e, 0xfe, 0xf3, 0xcb, 0xd1, 0x53, - 0x48, 0xd1, 0x12, 0x2e, 0x60, 0x3e, 0xc5, 0xe2, 0x3e, 0x87, 0x1e, 0x01, 0x78, 0x20, 0x22, 0x69, - 0xd1, 0xb9, 0xd4, 0x84, 0x7d, 0xe2, 0xbe, 0xb4, 0x94, 0xd3, 0x5a, 0x42, 0x3b, 0xcf, 0xa1, 0x2d, - 0xa2, 0x05, 0x2d, 0xed, 0x96, 0xa9, 0x6d, 0x07, 0x47, 0xf9, 0x0e, 0x7a, 0x38, 0xda, 0x9b, 0x88, - 0x03, 0x9a, 0x1b, 0xbd, 0x92, 0x2b, 0x7d, 0xcc, 0x45, 0xa0, 0x74, 0xe9, 0x5f, 0x78, 0xca, 0x22, - 0x3e, 0x03, 0xbc, 0x8a, 0x8f, 0x01, 0x7a, 0x2d, 0xb5, 0x0c, 0x26, 0x2f, 0xd6, 0xda, 0x76, 0xf7, - 0x7b, 0xda, 0xd1, 0xb6, 0x43, 0x37, 0x8c, 0x9d, 0x5b, 0xaf, 0xa3, 0x57, 0xb5, 0xd4, 0x4b, 0x79, - 0xc4, 0x57, 0x92, 0x13, 0x8e, 0x80, 0xfe, 0x02, 0xbd, 0x6b, 0x57, 0x58, 0x6e, 0xa3, 0x5a, 0x56, - 0x81, 0x31, 0xd7, 0x8c, 0xd2, 0x85, 0xe1, 0x9c, 0x24, 0x21, 0x36, 0xe7, 0xe3, 0x0e, 0xaa, 0x0c, - 0x4d, 0xc7, 0xad, 0x5a, 0xb2, 0x53, 0x12, 0x01, 0x0c, 0x3d, 0x01, 0xf2, 0x0c, 0x88, 0xaa, 0x5c, - 0x54, 0xcd, 0xec, 0xe9, 0x80, 0xdc, 0x2f, 0xd5, 0x86, 0xf2, 0x91, 0x05, 0x5f, 0xe0, 0x05, 0xab, - 0xe8, 0x5c, 0x46, 0xc1, 0xfc, 0x86, 0xa0, 0x6d, 0xf3, 0x3f, 0x3b, 0x11, 0xd8, 0x21, 0xe9, 0x98, - 0x0d, 0x7b, 0x50, 0x29, 0x67, 0xc3, 0x8e, 0xd1, 0xa6, 0xb9, 0x61, 0x73, 0xd9, 0xad, 0x6d, 0xf3, - 0x3f, 0x3b, 0xe8, 0x9b, 0x60, 0x92, 0x85, 0xd5, 0x5e, 0xc6, 0x24, 0x8b, 0x51, 0x9f, 0x19, 0x93, - 0x2c, 0x4e, 0x4a, 0x2a, 0xa7, 0x39, 0xe0, 0x79, 0x54, 0x4e, 0x07, 0x8c, 0xbe, 0x05, 0xf0, 0x60, - 0x74, 0x87, 0xa2, 0xa5, 0x7c, 0x3b, 0x39, 0x00, 0xa7, 0xe6, 0x35, 0x97, 0xc8, 0xaa, 0x1c, 0xd9, - 0x39, 0xb4, 0x98, 0x7f, 0xf7, 0xfa, 0x47, 0x02, 0x1a, 0x94, 0x5d, 0x28, 0x0f, 0x2f, 0x51, 0x21, - 0x58, 0xaa, 0x0e, 0xe3, 0x22, 0x11, 0x9f, 0xe1, 0x88, 0x4f, 0xa0, 0xb9, 0x74, 0xc4, 0x0c, 0x7d, - 0x0e, 0xe0, 0x74, 0x48, 0x61, 0xa1, 0xc5, 0xd4, 0x64, 0x11, 0x71, 0x56, 0x3a, 0x9b, 0xcb, 0x36, - 0x6f, 0x77, 0x85, 0x44, 0x43, 0x4f, 0x83, 0xeb, 0x46, 0x9c, 0x2e, 0xca, 0x38, 0x02, 0x52, 0x54, - 0x5d, 0xc6, 0x11, 0x90, 0x26, 0xc2, 0x94, 0x65, 0x0e, 0xfd, 0x3c, 0x52, 0x93, 0xa0, 0x77, 0xa4, - 0xb7, 0x16, 0xd1, 0x8d, 0xe8, 0xef, 0x60, 0x04, 0x44, 0xf5, 0x53, 0xc6, 0x08, 0x88, 0xd5, 0x69, - 0x19, 0x23, 0x20, 0x5e, 0xa0, 0x29, 0x2e, 0x07, 0x6e, 0xa1, 0x5a, 0x26, 0xf0, 0x98, 0x61, 0x7d, - 0x31, 0xd9, 0x2d, 0x66, 0x58, 0x07, 0x91, 0xd0, 0x0f, 0x00, 0xbe, 0x94, 0xa0, 0xbd, 0xd0, 0x72, - 0xce, 0x22, 0xfa, 0xe4, 0x5c, 0xe9, 0xe5, 0xa1, 0xfd, 0xf2, 0xce, 0xc0, 0x10, 0x01, 0x5d, 0x3d, - 0xba, 0x42, 0x9e, 0x3e, 0x2f, 0x83, 0x67, 0xcf, 0xcb, 0xe0, 0x8f, 0xe7, 0x65, 0xf0, 0xc5, 0x8b, - 0xf2, 0xc8, 0xb3, 0x17, 0xe5, 0x91, 0x5f, 0x5f, 0x94, 0x47, 0xe0, 0xac, 0x49, 0x13, 0xa0, 0xac, - 0x83, 0x5b, 0xaa, 0x61, 0x7a, 0x77, 0xee, 0x37, 0xd4, 0x26, 0x6d, 0x87, 0xd2, 0x2d, 0x99, 0x34, - 0x9c, 0x7c, 0xb3, 0x9b, 0xbe, 0x31, 0xc1, 0xff, 0x2f, 0x5e, 0xfb, 0x27, 0x00, 0x00, 0xff, 0xff, - 0x3b, 0xd0, 0x8c, 0xac, 0x80, 0x18, 0x00, 0x00, + // 1472 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xcd, 0x73, 0x14, 0x45, + 0x14, 0x4f, 0x27, 0x24, 0x24, 0x8f, 0xaf, 0xb2, 0xb3, 0x50, 0x9b, 0x05, 0x36, 0x61, 0xa0, 0x20, + 0x15, 0xc8, 0x0c, 0xbb, 0x0b, 0x51, 0x0e, 0xa8, 0x04, 0x0d, 0x95, 0x2a, 0x29, 0xc2, 0x68, 0xa9, + 0xc5, 0xc1, 0xa5, 0x77, 0xb7, 0x33, 0x4c, 0x31, 0x3b, 0xbd, 0xcc, 0x0c, 0x4b, 0x52, 0xa9, 0x1c, + 0xfc, 0xb8, 0xe0, 0xc9, 0x2a, 0x3d, 0x60, 0x51, 0xe2, 0xc5, 0xbb, 0x1e, 0xbc, 0x79, 0xf0, 0xe0, + 0x41, 0x8e, 0x94, 0x5e, 0xf4, 0x62, 0x59, 0xe0, 0x51, 0xff, 0x07, 0x6b, 0xba, 0x7b, 0x36, 0x33, + 0x9b, 0xf9, 0x5a, 0xcd, 0x21, 0xa7, 0xec, 0xf4, 0xbc, 0x8f, 0xdf, 0xfb, 0xbd, 0xee, 0x37, 0xbf, + 0x0e, 0x28, 0x1d, 0x87, 0x75, 0xa9, 0x4d, 0xec, 0x26, 0xd5, 0xe8, 0x5a, 0xf3, 0x0e, 0xb1, 0x0d, + 0xaa, 0x75, 0x2b, 0xda, 0xbd, 0xfb, 0xd4, 0x59, 0x57, 0x3b, 0x0e, 0xf3, 0x18, 0x3e, 0xb2, 0x65, + 0xa3, 0x06, 0x36, 0x6a, 0xb7, 0x52, 0x9a, 0x6a, 0x32, 0xb7, 0xcd, 0xdc, 0x3a, 0xb7, 0xd2, 0xc4, + 0x83, 0x70, 0x29, 0xcd, 0x89, 0x27, 0xad, 0x41, 0x5c, 0x2a, 0x62, 0x69, 0xdd, 0x4a, 0x83, 0x7a, + 0xa4, 0xa2, 0x75, 0x88, 0x61, 0xda, 0xc4, 0x33, 0x99, 0x2d, 0x6d, 0xcb, 0x61, 0xdb, 0xc0, 0xaa, + 0xc9, 0xcc, 0xe0, 0xfd, 0x31, 0x83, 0x31, 0xc3, 0xa2, 0x1a, 0xe9, 0x98, 0x1a, 0xb1, 0x6d, 0xe6, + 0x71, 0xe7, 0x20, 0x53, 0xc1, 0x60, 0x06, 0x13, 0x08, 0xfc, 0x5f, 0x72, 0xf5, 0x64, 0x42, 0x59, + 0x6d, 0xe2, 0xdc, 0xa5, 0x5e, 0x86, 0x11, 0x73, 0x5a, 0xd4, 0x71, 0x33, 0x8c, 0x3a, 0xc4, 0x21, + 0xed, 0xc0, 0x68, 0x3a, 0xc1, 0xc8, 0x5b, 0x13, 0x06, 0xca, 0x23, 0x04, 0xc5, 0x9b, 0x3e, 0x0d, + 0x37, 0xfc, 0xd8, 0x4b, 0x94, 0x5e, 0x25, 0x56, 0x53, 0xa7, 0xf7, 0xee, 0x53, 0xd7, 0xc3, 0x97, + 0x61, 0x82, 0xb8, 0x77, 0xeb, 0x3c, 0x6d, 0x71, 0x78, 0x06, 0xcd, 0xee, 0xab, 0xce, 0xa8, 0xf1, + 0x9c, 0xab, 0x57, 0xdc, 0xbb, 0x3c, 0x84, 0x3e, 0x4e, 0xe4, 0x2f, 0xdf, 0xbd, 0x61, 0xb6, 0xa4, + 0xfb, 0x48, 0xba, 0xfb, 0xa2, 0xd9, 0x92, 0xee, 0x0d, 0xf9, 0x4b, 0xf9, 0x6e, 0x18, 0xa6, 0x62, + 0xa0, 0xb9, 0x1d, 0x66, 0xbb, 0x14, 0xdf, 0x84, 0x42, 0xd3, 0xa1, 0x9c, 0xf1, 0xfa, 0x2a, 0xa5, + 0x75, 0xd6, 0xe1, 0xe4, 0x17, 0xd1, 0xcc, 0xc8, 0xec, 0xbe, 0xea, 0x94, 0x2a, 0xbb, 0xee, 0xf7, + 0x4e, 0x95, 0xbd, 0x53, 0xaf, 0x32, 0xd3, 0x5e, 0xdc, 0xf3, 0xf4, 0x8f, 0xe9, 0x21, 0x1d, 0x07, + 0xce, 0x4b, 0x94, 0xde, 0x10, 0xae, 0xf8, 0x03, 0x38, 0xea, 0x52, 0xcf, 0xb3, 0x68, 0x9b, 0xda, + 0x5e, 0x7d, 0xd5, 0x22, 0x5e, 0x24, 0xf2, 0x70, 0xbe, 0xc8, 0xc5, 0xad, 0x18, 0x4b, 0x16, 0xf1, + 0x42, 0xf1, 0x6f, 0xc3, 0xb1, 0x50, 0x7c, 0xc7, 0x4f, 0x1f, 0x49, 0x30, 0x92, 0x2f, 0xc1, 0xd4, + 0x56, 0x10, 0xdd, 0x8f, 0xb1, 0x95, 0x41, 0xa9, 0x40, 0x81, 0x33, 0x76, 0x8d, 0x7a, 0x82, 0x4d, + 0xd9, 0xc8, 0x29, 0x18, 0xe7, 0x5d, 0xa8, 0x9b, 0xad, 0x22, 0x9a, 0x41, 0xb3, 0x7b, 0xf4, 0xbd, + 0xfc, 0x79, 0xb9, 0xa5, 0xbc, 0x05, 0x87, 0xfb, 0x5c, 0x24, 0xc1, 0x35, 0x18, 0x15, 0x9d, 0x43, + 0xbc, 0x73, 0xc7, 0x93, 0x3a, 0x27, 0xbc, 0x84, 0xad, 0x72, 0x1b, 0x66, 0x22, 0xd1, 0x16, 0xd7, + 0xdf, 0x5c, 0xf3, 0xa8, 0x63, 0x13, 0x6b, 0xf9, 0x8d, 0x00, 0xcc, 0x51, 0x98, 0x10, 0xbb, 0x3d, + 0x40, 0x73, 0x40, 0x1f, 0x17, 0x0b, 0xcb, 0x2d, 0x3c, 0x0d, 0xfb, 0xa8, 0xf4, 0xf0, 0x5f, 0xfb, + 0x9b, 0x6e, 0x42, 0x87, 0x60, 0x69, 0xb9, 0xa5, 0xbc, 0x0f, 0x27, 0x52, 0x32, 0xfc, 0x1f, 0xec, + 0x3f, 0x23, 0x38, 0x1a, 0x84, 0xbe, 0xce, 0xf1, 0xf0, 0xd7, 0x6e, 0x2e, 0xdc, 0xc7, 0x01, 0x04, + 0xc3, 0xde, 0x7a, 0x87, 0x4a, 0xd8, 0x13, 0x7c, 0xe5, 0x9d, 0xf5, 0x0e, 0xc5, 0xa7, 0xe0, 0x20, + 0x59, 0xf5, 0xa8, 0x53, 0xef, 0xb5, 0x61, 0x84, 0xb7, 0x61, 0x3f, 0x5f, 0xbd, 0x21, 0x7a, 0x81, + 0x97, 0x00, 0xb6, 0x86, 0x50, 0xb1, 0xc9, 0xb1, 0x9f, 0x8e, 0x6c, 0x07, 0x31, 0xfd, 0x82, 0x4d, + 0xb1, 0x42, 0x0c, 0x2a, 0xd1, 0xe9, 0x21, 0x4f, 0xe5, 0x09, 0x82, 0x63, 0xf1, 0x95, 0x48, 0x7e, + 0x2e, 0xc2, 0x98, 0x98, 0x25, 0xf2, 0xb8, 0x64, 0x10, 0x24, 0x8d, 0xf1, 0xb5, 0x18, 0x7c, 0x67, + 0x32, 0xf1, 0x89, 0x9c, 0x11, 0x80, 0xbf, 0x23, 0x28, 0xf5, 0xba, 0xf8, 0xc0, 0x96, 0x0c, 0xf4, + 0x98, 0x56, 0x61, 0x94, 0xf9, 0xab, 0x9c, 0xe5, 0x89, 0xc5, 0xe2, 0x2f, 0xdf, 0xcf, 0x17, 0x64, + 0x96, 0x2b, 0xad, 0x96, 0x43, 0x5d, 0xf7, 0x6d, 0xcf, 0x31, 0x6d, 0x43, 0x17, 0x66, 0xbb, 0x8b, + 0xfc, 0xaf, 0x42, 0xdb, 0x28, 0x52, 0xdb, 0x2e, 0xe1, 0xfe, 0xc7, 0x10, 0xf7, 0x57, 0x5c, 0xb7, + 0x7f, 0x97, 0x17, 0x60, 0x94, 0xf8, 0xab, 0x82, 0x7b, 0x5d, 0x3c, 0xec, 0x5e, 0x86, 0x23, 0x15, + 0xec, 0x12, 0x86, 0x1b, 0xf2, 0x93, 0xea, 0xc3, 0xb3, 0xac, 0x28, 0xbd, 0x3b, 0xc5, 0xc1, 0x63, + 0x24, 0x3f, 0x8e, 0xd1, 0x24, 0xbb, 0x84, 0x81, 0x0b, 0x5b, 0x1f, 0x15, 0x31, 0x7f, 0xf2, 0xcc, + 0x50, 0xe5, 0x13, 0x04, 0x47, 0xfa, 0xdd, 0x64, 0x41, 0x55, 0xd8, 0x4b, 0xc4, 0xc9, 0xcf, 0x9c, + 0x09, 0x81, 0x21, 0x5e, 0x80, 0x31, 0x11, 0x5a, 0x4a, 0x97, 0x72, 0x12, 0x09, 0x32, 0x97, 0xb4, + 0x56, 0x9a, 0x11, 0x66, 0xc5, 0xcb, 0x1d, 0xef, 0xdf, 0x37, 0xe1, 0x53, 0x18, 0xca, 0x22, 0xeb, + 0xbd, 0x0c, 0x7b, 0x05, 0x9a, 0xa0, 0x83, 0x27, 0xd3, 0xc1, 0x2f, 0x3a, 0x26, 0x5d, 0xd5, 0x03, + 0x9f, 0x9d, 0x6b, 0x64, 0x01, 0x30, 0x47, 0xb9, 0xc2, 0x45, 0xa5, 0x2c, 0x44, 0xb9, 0x0e, 0x93, + 0x91, 0x55, 0x09, 0x7a, 0x01, 0xc6, 0x84, 0xf8, 0x94, 0x9f, 0xdd, 0x44, 0xc2, 0xa5, 0x9f, 0xb4, + 0x56, 0x1e, 0x22, 0xa9, 0x1a, 0xde, 0x25, 0x96, 0xd9, 0x22, 0x1e, 0xbd, 0xea, 0x6b, 0x33, 0x1a, + 0xdd, 0x39, 0x14, 0x0e, 0x73, 0xc9, 0x46, 0xeb, 0x72, 0x03, 0x39, 0xe2, 0x85, 0xcc, 0x55, 0x49, + 0xe4, 0xc7, 0x35, 0xae, 0xb1, 0x6e, 0x4c, 0x44, 0x7d, 0xb2, 0xb9, 0x7d, 0x51, 0x59, 0x95, 0xf2, + 0x22, 0x1e, 0x8a, 0x2c, 0xb4, 0x00, 0xa3, 0xd4, 0x71, 0x98, 0x13, 0xcc, 0x48, 0xfe, 0x80, 0xcf, + 0x02, 0x36, 0x58, 0xd7, 0xbf, 0x74, 0x74, 0xea, 0x0f, 0x4c, 0xcb, 0xaa, 0x77, 0x88, 0xeb, 0xf2, + 0xbd, 0x37, 0xae, 0x1f, 0x32, 0x58, 0x77, 0xc5, 0x61, 0x9d, 0xf7, 0x4c, 0xcb, 0x5a, 0x21, 0xae, + 0xab, 0x5c, 0x92, 0xed, 0x0f, 0xf2, 0x0c, 0x70, 0x4c, 0x6a, 0x72, 0xfa, 0xf5, 0xbb, 0xa6, 0x81, + 0x53, 0x3e, 0x44, 0x50, 0xee, 0xf3, 0xb2, 0x89, 0x41, 0x97, 0x28, 0xed, 0x6d, 0xed, 0x3a, 0x4c, + 0xb6, 0xf9, 0xa2, 0x2f, 0x4a, 0xdd, 0x3e, 0x7e, 0xb5, 0x74, 0x7e, 0xb7, 0x45, 0xd3, 0x5f, 0x6a, + 0xf7, 0x2f, 0x29, 0x2d, 0x98, 0x4e, 0x84, 0xb0, 0x63, 0xcc, 0x56, 0xbf, 0x98, 0x84, 0x51, 0x9e, + 0x06, 0x7f, 0x8d, 0x60, 0x7f, 0xf8, 0xee, 0x80, 0xcf, 0x27, 0x15, 0x91, 0x74, 0x03, 0x2a, 0x55, + 0x06, 0xf0, 0x10, 0x25, 0x28, 0x73, 0x1f, 0xfd, 0xfa, 0xd7, 0xe7, 0xc3, 0xa7, 0xb0, 0xa2, 0x25, + 0xdc, 0xbd, 0x7c, 0x76, 0xc5, 0x55, 0x0e, 0x7f, 0x89, 0x60, 0x3c, 0x10, 0xb2, 0xf8, 0x5c, 0x6a, + 0xae, 0x3e, 0x49, 0x5f, 0x9a, 0xcf, 0x69, 0x2d, 0x51, 0x9d, 0xe7, 0xa8, 0xe6, 0xf0, 0xac, 0x96, + 0x76, 0xb7, 0xd4, 0x36, 0x82, 0x0f, 0xf8, 0x26, 0x7e, 0x34, 0x0c, 0x85, 0x38, 0x91, 0x8d, 0x5f, + 0xc9, 0x95, 0x39, 0x46, 0xf9, 0x97, 0x2e, 0xfd, 0x07, 0x4f, 0x89, 0xff, 0x53, 0xc4, 0x0b, 0xf8, + 0x18, 0xe1, 0xd7, 0x52, 0x2b, 0x70, 0xe5, 0x4d, 0x5a, 0xdb, 0xe8, 0x1d, 0xa0, 0x4d, 0x6d, 0x23, + 0x74, 0xa5, 0xd8, 0xbc, 0xf5, 0x3a, 0x7e, 0x55, 0x4b, 0xbd, 0x85, 0x47, 0x7c, 0x25, 0x2f, 0xe1, + 0x08, 0xf8, 0x6f, 0x04, 0x87, 0xfa, 0xa4, 0x35, 0xae, 0x65, 0xd5, 0x16, 0x73, 0xa5, 0x28, 0x5d, + 0x18, 0xcc, 0x49, 0x72, 0x61, 0x73, 0x2a, 0xee, 0xe0, 0xca, 0xc0, 0x4c, 0xdc, 0xaa, 0x25, 0x3b, + 0x25, 0xd5, 0xee, 0xe2, 0x6f, 0x11, 0x1c, 0x8c, 0x8a, 0x59, 0x5c, 0xcd, 0xec, 0xe4, 0x36, 0x55, + 0x5f, 0xaa, 0x0d, 0xe4, 0x23, 0x6b, 0xbd, 0xc0, 0x6b, 0x55, 0xf1, 0xb9, 0x8c, 0x5a, 0xf9, 0x45, + 0x40, 0xdb, 0xe0, 0x7f, 0x36, 0x03, 0xc4, 0x21, 0x71, 0x98, 0x8d, 0x78, 0xbb, 0x16, 0xce, 0x46, + 0x1c, 0xa3, 0x3e, 0x73, 0x23, 0xe6, 0xc2, 0x5a, 0xdb, 0xe0, 0x7f, 0x36, 0xf1, 0x63, 0x04, 0xfb, + 0xc3, 0x52, 0x2e, 0x63, 0x56, 0xc5, 0x48, 0xcb, 0x8c, 0x59, 0x15, 0xa7, 0x13, 0x95, 0xd3, 0x1c, + 0xeb, 0x0c, 0x2e, 0xa7, 0x63, 0xc5, 0x4f, 0x10, 0x4c, 0xf4, 0x76, 0x23, 0x9e, 0xcf, 0xb7, 0x6b, + 0x03, 0x5c, 0x6a, 0x5e, 0x73, 0x09, 0xaa, 0xca, 0x41, 0x9d, 0xc3, 0x73, 0xf9, 0x77, 0xaa, 0x3f, + 0xea, 0x0f, 0x44, 0x94, 0x14, 0xce, 0xc3, 0x46, 0x54, 0xdb, 0x95, 0xaa, 0x83, 0xb8, 0x48, 0xb0, + 0x67, 0x38, 0xd8, 0x13, 0x78, 0x3a, 0x1d, 0xac, 0x8b, 0x1f, 0x22, 0x18, 0x13, 0xba, 0x07, 0xcf, + 0xa5, 0xe6, 0x89, 0x48, 0xad, 0xd2, 0xd9, 0x5c, 0xb6, 0x79, 0xdb, 0x29, 0x04, 0x17, 0xfe, 0x09, + 0x41, 0x21, 0x4e, 0xe0, 0x64, 0x8c, 0xf6, 0x14, 0x79, 0x96, 0x31, 0xda, 0xd3, 0xd4, 0x94, 0xb2, + 0xc0, 0x51, 0x9f, 0xc7, 0x6a, 0x12, 0xea, 0xae, 0xf4, 0xd6, 0x22, 0x02, 0x10, 0xff, 0x83, 0xe0, + 0x60, 0x54, 0x03, 0x65, 0x1c, 0xf2, 0x58, 0xad, 0x95, 0x71, 0xc8, 0xe3, 0x45, 0x96, 0xe2, 0x70, + 0xcc, 0x16, 0xae, 0x65, 0x62, 0x8e, 0x19, 0xc2, 0x17, 0x93, 0xdd, 0x62, 0x86, 0x70, 0x10, 0x09, + 0xff, 0x80, 0x00, 0x6f, 0x97, 0x4e, 0x78, 0x21, 0x27, 0xfe, 0x3e, 0x35, 0x56, 0x7a, 0x79, 0x60, + 0xbf, 0xbc, 0x03, 0x2e, 0x54, 0x7b, 0x4f, 0x4e, 0x2e, 0xd2, 0xa7, 0xcf, 0xcb, 0xe8, 0xd9, 0xf3, + 0x32, 0xfa, 0xf3, 0x79, 0x19, 0x7d, 0xf6, 0xa2, 0x3c, 0xf4, 0xec, 0x45, 0x79, 0xe8, 0xb7, 0x17, + 0xe5, 0x21, 0x98, 0x32, 0x59, 0x02, 0x94, 0x15, 0x74, 0x4b, 0x35, 0x4c, 0xef, 0xce, 0xfd, 0x86, + 0xda, 0x64, 0xed, 0x50, 0xba, 0x79, 0x93, 0x85, 0x93, 0xaf, 0xf5, 0xd2, 0x37, 0xc6, 0xf8, 0xbf, + 0xb5, 0x6b, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xee, 0xee, 0x11, 0x25, 0x3f, 0x18, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1570,32 +1569,32 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type QueryClient interface { - // QueryOrderFeeCalc calculates the fees that will be associated with the provided order. - QueryOrderFeeCalc(ctx context.Context, in *QueryOrderFeeCalcRequest, opts ...grpc.CallOption) (*QueryOrderFeeCalcResponse, error) - // QueryGetOrder looks up an order by id. - QueryGetOrder(ctx context.Context, in *QueryGetOrderRequest, opts ...grpc.CallOption) (*QueryGetOrderResponse, error) - // QueryGetOrderByExternalID looks up an order by market id and external id. - QueryGetOrderByExternalID(ctx context.Context, in *QueryGetOrderByExternalIDRequest, opts ...grpc.CallOption) (*QueryGetOrderByExternalIDResponse, error) - // QueryGetMarketOrders looks up the orders in a market. - QueryGetMarketOrders(ctx context.Context, in *QueryGetMarketOrdersRequest, opts ...grpc.CallOption) (*QueryGetMarketOrdersResponse, error) - // QueryGetOwnerOrders looks up the orders from the provided owner address. - QueryGetOwnerOrders(ctx context.Context, in *QueryGetOwnerOrdersRequest, opts ...grpc.CallOption) (*QueryGetOwnerOrdersResponse, error) - // QueryGetAssetOrders looks up the orders for a specific asset denom. - QueryGetAssetOrders(ctx context.Context, in *QueryGetAssetOrdersRequest, opts ...grpc.CallOption) (*QueryGetAssetOrdersResponse, error) - // QueryGetAllOrders gets all orders in the exchange module. - QueryGetAllOrders(ctx context.Context, in *QueryGetAllOrdersRequest, opts ...grpc.CallOption) (*QueryGetAllOrdersResponse, error) - // QueryGetMarket returns all the information and details about a market. - QueryGetMarket(ctx context.Context, in *QueryGetMarketRequest, opts ...grpc.CallOption) (*QueryGetMarketResponse, error) - // QueryGetAllMarkets returns brief information about each market. - QueryGetAllMarkets(ctx context.Context, in *QueryGetAllMarketsRequest, opts ...grpc.CallOption) (*QueryGetAllMarketsResponse, error) - // QueryParams returns the exchange module parameters. - QueryParams(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) - // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. - QueryValidateCreateMarket(ctx context.Context, in *QueryValidateCreateMarketRequest, opts ...grpc.CallOption) (*QueryValidateCreateMarketResponse, error) - // QueryValidateMarket checks for any problems with a market's setup. - QueryValidateMarket(ctx context.Context, in *QueryValidateMarketRequest, opts ...grpc.CallOption) (*QueryValidateMarketResponse, error) - // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. - QueryValidateManageFees(ctx context.Context, in *QueryValidateManageFeesRequest, opts ...grpc.CallOption) (*QueryValidateManageFeesResponse, error) + // OrderFeeCalc calculates the fees that will be associated with the provided order. + OrderFeeCalc(ctx context.Context, in *QueryOrderFeeCalcRequest, opts ...grpc.CallOption) (*QueryOrderFeeCalcResponse, error) + // GetOrder looks up an order by id. + GetOrder(ctx context.Context, in *QueryGetOrderRequest, opts ...grpc.CallOption) (*QueryGetOrderResponse, error) + // GetOrderByExternalID looks up an order by market id and external id. + GetOrderByExternalID(ctx context.Context, in *QueryGetOrderByExternalIDRequest, opts ...grpc.CallOption) (*QueryGetOrderByExternalIDResponse, error) + // GetMarketOrders looks up the orders in a market. + GetMarketOrders(ctx context.Context, in *QueryGetMarketOrdersRequest, opts ...grpc.CallOption) (*QueryGetMarketOrdersResponse, error) + // GetOwnerOrders looks up the orders from the provided owner address. + GetOwnerOrders(ctx context.Context, in *QueryGetOwnerOrdersRequest, opts ...grpc.CallOption) (*QueryGetOwnerOrdersResponse, error) + // GetAssetOrders looks up the orders for a specific asset denom. + GetAssetOrders(ctx context.Context, in *QueryGetAssetOrdersRequest, opts ...grpc.CallOption) (*QueryGetAssetOrdersResponse, error) + // GetAllOrders gets all orders in the exchange module. + GetAllOrders(ctx context.Context, in *QueryGetAllOrdersRequest, opts ...grpc.CallOption) (*QueryGetAllOrdersResponse, error) + // GetMarket returns all the information and details about a market. + GetMarket(ctx context.Context, in *QueryGetMarketRequest, opts ...grpc.CallOption) (*QueryGetMarketResponse, error) + // GetAllMarkets returns brief information about each market. + GetAllMarkets(ctx context.Context, in *QueryGetAllMarketsRequest, opts ...grpc.CallOption) (*QueryGetAllMarketsResponse, error) + // Params returns the exchange module parameters. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // ValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. + ValidateCreateMarket(ctx context.Context, in *QueryValidateCreateMarketRequest, opts ...grpc.CallOption) (*QueryValidateCreateMarketResponse, error) + // ValidateMarket checks for any problems with a market's setup. + ValidateMarket(ctx context.Context, in *QueryValidateMarketRequest, opts ...grpc.CallOption) (*QueryValidateMarketResponse, error) + // ValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. + ValidateManageFees(ctx context.Context, in *QueryValidateManageFeesRequest, opts ...grpc.CallOption) (*QueryValidateManageFeesResponse, error) } type queryClient struct { @@ -1606,117 +1605,117 @@ func NewQueryClient(cc grpc1.ClientConn) QueryClient { return &queryClient{cc} } -func (c *queryClient) QueryOrderFeeCalc(ctx context.Context, in *QueryOrderFeeCalcRequest, opts ...grpc.CallOption) (*QueryOrderFeeCalcResponse, error) { +func (c *queryClient) OrderFeeCalc(ctx context.Context, in *QueryOrderFeeCalcRequest, opts ...grpc.CallOption) (*QueryOrderFeeCalcResponse, error) { out := new(QueryOrderFeeCalcResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryOrderFeeCalc", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/OrderFeeCalc", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) QueryGetOrder(ctx context.Context, in *QueryGetOrderRequest, opts ...grpc.CallOption) (*QueryGetOrderResponse, error) { +func (c *queryClient) GetOrder(ctx context.Context, in *QueryGetOrderRequest, opts ...grpc.CallOption) (*QueryGetOrderResponse, error) { out := new(QueryGetOrderResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetOrder", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/GetOrder", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) QueryGetOrderByExternalID(ctx context.Context, in *QueryGetOrderByExternalIDRequest, opts ...grpc.CallOption) (*QueryGetOrderByExternalIDResponse, error) { +func (c *queryClient) GetOrderByExternalID(ctx context.Context, in *QueryGetOrderByExternalIDRequest, opts ...grpc.CallOption) (*QueryGetOrderByExternalIDResponse, error) { out := new(QueryGetOrderByExternalIDResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetOrderByExternalID", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/GetOrderByExternalID", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) QueryGetMarketOrders(ctx context.Context, in *QueryGetMarketOrdersRequest, opts ...grpc.CallOption) (*QueryGetMarketOrdersResponse, error) { +func (c *queryClient) GetMarketOrders(ctx context.Context, in *QueryGetMarketOrdersRequest, opts ...grpc.CallOption) (*QueryGetMarketOrdersResponse, error) { out := new(QueryGetMarketOrdersResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetMarketOrders", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/GetMarketOrders", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) QueryGetOwnerOrders(ctx context.Context, in *QueryGetOwnerOrdersRequest, opts ...grpc.CallOption) (*QueryGetOwnerOrdersResponse, error) { +func (c *queryClient) GetOwnerOrders(ctx context.Context, in *QueryGetOwnerOrdersRequest, opts ...grpc.CallOption) (*QueryGetOwnerOrdersResponse, error) { out := new(QueryGetOwnerOrdersResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetOwnerOrders", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/GetOwnerOrders", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) QueryGetAssetOrders(ctx context.Context, in *QueryGetAssetOrdersRequest, opts ...grpc.CallOption) (*QueryGetAssetOrdersResponse, error) { +func (c *queryClient) GetAssetOrders(ctx context.Context, in *QueryGetAssetOrdersRequest, opts ...grpc.CallOption) (*QueryGetAssetOrdersResponse, error) { out := new(QueryGetAssetOrdersResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetAssetOrders", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/GetAssetOrders", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) QueryGetAllOrders(ctx context.Context, in *QueryGetAllOrdersRequest, opts ...grpc.CallOption) (*QueryGetAllOrdersResponse, error) { +func (c *queryClient) GetAllOrders(ctx context.Context, in *QueryGetAllOrdersRequest, opts ...grpc.CallOption) (*QueryGetAllOrdersResponse, error) { out := new(QueryGetAllOrdersResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetAllOrders", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/GetAllOrders", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) QueryGetMarket(ctx context.Context, in *QueryGetMarketRequest, opts ...grpc.CallOption) (*QueryGetMarketResponse, error) { +func (c *queryClient) GetMarket(ctx context.Context, in *QueryGetMarketRequest, opts ...grpc.CallOption) (*QueryGetMarketResponse, error) { out := new(QueryGetMarketResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetMarket", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/GetMarket", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) QueryGetAllMarkets(ctx context.Context, in *QueryGetAllMarketsRequest, opts ...grpc.CallOption) (*QueryGetAllMarketsResponse, error) { +func (c *queryClient) GetAllMarkets(ctx context.Context, in *QueryGetAllMarketsRequest, opts ...grpc.CallOption) (*QueryGetAllMarketsResponse, error) { out := new(QueryGetAllMarketsResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryGetAllMarkets", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/GetAllMarkets", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) QueryParams(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { out := new(QueryParamsResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryParams", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/Params", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) QueryValidateCreateMarket(ctx context.Context, in *QueryValidateCreateMarketRequest, opts ...grpc.CallOption) (*QueryValidateCreateMarketResponse, error) { +func (c *queryClient) ValidateCreateMarket(ctx context.Context, in *QueryValidateCreateMarketRequest, opts ...grpc.CallOption) (*QueryValidateCreateMarketResponse, error) { out := new(QueryValidateCreateMarketResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryValidateCreateMarket", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/ValidateCreateMarket", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) QueryValidateMarket(ctx context.Context, in *QueryValidateMarketRequest, opts ...grpc.CallOption) (*QueryValidateMarketResponse, error) { +func (c *queryClient) ValidateMarket(ctx context.Context, in *QueryValidateMarketRequest, opts ...grpc.CallOption) (*QueryValidateMarketResponse, error) { out := new(QueryValidateMarketResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryValidateMarket", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/ValidateMarket", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) QueryValidateManageFees(ctx context.Context, in *QueryValidateManageFeesRequest, opts ...grpc.CallOption) (*QueryValidateManageFeesResponse, error) { +func (c *queryClient) ValidateManageFees(ctx context.Context, in *QueryValidateManageFeesRequest, opts ...grpc.CallOption) (*QueryValidateManageFeesResponse, error) { out := new(QueryValidateManageFeesResponse) - err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/QueryValidateManageFees", in, out, opts...) + err := c.cc.Invoke(ctx, "/provenance.exchange.v1.Query/ValidateManageFees", in, out, opts...) if err != nil { return nil, err } @@ -1725,312 +1724,312 @@ func (c *queryClient) QueryValidateManageFees(ctx context.Context, in *QueryVali // QueryServer is the server API for Query service. type QueryServer interface { - // QueryOrderFeeCalc calculates the fees that will be associated with the provided order. - QueryOrderFeeCalc(context.Context, *QueryOrderFeeCalcRequest) (*QueryOrderFeeCalcResponse, error) - // QueryGetOrder looks up an order by id. - QueryGetOrder(context.Context, *QueryGetOrderRequest) (*QueryGetOrderResponse, error) - // QueryGetOrderByExternalID looks up an order by market id and external id. - QueryGetOrderByExternalID(context.Context, *QueryGetOrderByExternalIDRequest) (*QueryGetOrderByExternalIDResponse, error) - // QueryGetMarketOrders looks up the orders in a market. - QueryGetMarketOrders(context.Context, *QueryGetMarketOrdersRequest) (*QueryGetMarketOrdersResponse, error) - // QueryGetOwnerOrders looks up the orders from the provided owner address. - QueryGetOwnerOrders(context.Context, *QueryGetOwnerOrdersRequest) (*QueryGetOwnerOrdersResponse, error) - // QueryGetAssetOrders looks up the orders for a specific asset denom. - QueryGetAssetOrders(context.Context, *QueryGetAssetOrdersRequest) (*QueryGetAssetOrdersResponse, error) - // QueryGetAllOrders gets all orders in the exchange module. - QueryGetAllOrders(context.Context, *QueryGetAllOrdersRequest) (*QueryGetAllOrdersResponse, error) - // QueryGetMarket returns all the information and details about a market. - QueryGetMarket(context.Context, *QueryGetMarketRequest) (*QueryGetMarketResponse, error) - // QueryGetAllMarkets returns brief information about each market. - QueryGetAllMarkets(context.Context, *QueryGetAllMarketsRequest) (*QueryGetAllMarketsResponse, error) - // QueryParams returns the exchange module parameters. - QueryParams(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) - // QueryValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. - QueryValidateCreateMarket(context.Context, *QueryValidateCreateMarketRequest) (*QueryValidateCreateMarketResponse, error) - // QueryValidateMarket checks for any problems with a market's setup. - QueryValidateMarket(context.Context, *QueryValidateMarketRequest) (*QueryValidateMarketResponse, error) - // QueryValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. - QueryValidateManageFees(context.Context, *QueryValidateManageFeesRequest) (*QueryValidateManageFeesResponse, error) + // OrderFeeCalc calculates the fees that will be associated with the provided order. + OrderFeeCalc(context.Context, *QueryOrderFeeCalcRequest) (*QueryOrderFeeCalcResponse, error) + // GetOrder looks up an order by id. + GetOrder(context.Context, *QueryGetOrderRequest) (*QueryGetOrderResponse, error) + // GetOrderByExternalID looks up an order by market id and external id. + GetOrderByExternalID(context.Context, *QueryGetOrderByExternalIDRequest) (*QueryGetOrderByExternalIDResponse, error) + // GetMarketOrders looks up the orders in a market. + GetMarketOrders(context.Context, *QueryGetMarketOrdersRequest) (*QueryGetMarketOrdersResponse, error) + // GetOwnerOrders looks up the orders from the provided owner address. + GetOwnerOrders(context.Context, *QueryGetOwnerOrdersRequest) (*QueryGetOwnerOrdersResponse, error) + // GetAssetOrders looks up the orders for a specific asset denom. + GetAssetOrders(context.Context, *QueryGetAssetOrdersRequest) (*QueryGetAssetOrdersResponse, error) + // GetAllOrders gets all orders in the exchange module. + GetAllOrders(context.Context, *QueryGetAllOrdersRequest) (*QueryGetAllOrdersResponse, error) + // GetMarket returns all the information and details about a market. + GetMarket(context.Context, *QueryGetMarketRequest) (*QueryGetMarketResponse, error) + // GetAllMarkets returns brief information about each market. + GetAllMarkets(context.Context, *QueryGetAllMarketsRequest) (*QueryGetAllMarketsResponse, error) + // Params returns the exchange module parameters. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // ValidateCreateMarket checks the provided MsgGovCreateMarketResponse and returns any errors it might have. + ValidateCreateMarket(context.Context, *QueryValidateCreateMarketRequest) (*QueryValidateCreateMarketResponse, error) + // ValidateMarket checks for any problems with a market's setup. + ValidateMarket(context.Context, *QueryValidateMarketRequest) (*QueryValidateMarketResponse, error) + // ValidateManageFees checks the provided MsgGovManageFeesRequest and returns any errors that it might have. + ValidateManageFees(context.Context, *QueryValidateManageFeesRequest) (*QueryValidateManageFeesResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. type UnimplementedQueryServer struct { } -func (*UnimplementedQueryServer) QueryOrderFeeCalc(ctx context.Context, req *QueryOrderFeeCalcRequest) (*QueryOrderFeeCalcResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryOrderFeeCalc not implemented") +func (*UnimplementedQueryServer) OrderFeeCalc(ctx context.Context, req *QueryOrderFeeCalcRequest) (*QueryOrderFeeCalcResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method OrderFeeCalc not implemented") } -func (*UnimplementedQueryServer) QueryGetOrder(ctx context.Context, req *QueryGetOrderRequest) (*QueryGetOrderResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryGetOrder not implemented") +func (*UnimplementedQueryServer) GetOrder(ctx context.Context, req *QueryGetOrderRequest) (*QueryGetOrderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetOrder not implemented") } -func (*UnimplementedQueryServer) QueryGetOrderByExternalID(ctx context.Context, req *QueryGetOrderByExternalIDRequest) (*QueryGetOrderByExternalIDResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryGetOrderByExternalID not implemented") +func (*UnimplementedQueryServer) GetOrderByExternalID(ctx context.Context, req *QueryGetOrderByExternalIDRequest) (*QueryGetOrderByExternalIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetOrderByExternalID not implemented") } -func (*UnimplementedQueryServer) QueryGetMarketOrders(ctx context.Context, req *QueryGetMarketOrdersRequest) (*QueryGetMarketOrdersResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryGetMarketOrders not implemented") +func (*UnimplementedQueryServer) GetMarketOrders(ctx context.Context, req *QueryGetMarketOrdersRequest) (*QueryGetMarketOrdersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetMarketOrders not implemented") } -func (*UnimplementedQueryServer) QueryGetOwnerOrders(ctx context.Context, req *QueryGetOwnerOrdersRequest) (*QueryGetOwnerOrdersResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryGetOwnerOrders not implemented") +func (*UnimplementedQueryServer) GetOwnerOrders(ctx context.Context, req *QueryGetOwnerOrdersRequest) (*QueryGetOwnerOrdersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetOwnerOrders not implemented") } -func (*UnimplementedQueryServer) QueryGetAssetOrders(ctx context.Context, req *QueryGetAssetOrdersRequest) (*QueryGetAssetOrdersResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryGetAssetOrders not implemented") +func (*UnimplementedQueryServer) GetAssetOrders(ctx context.Context, req *QueryGetAssetOrdersRequest) (*QueryGetAssetOrdersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetAssetOrders not implemented") } -func (*UnimplementedQueryServer) QueryGetAllOrders(ctx context.Context, req *QueryGetAllOrdersRequest) (*QueryGetAllOrdersResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryGetAllOrders not implemented") +func (*UnimplementedQueryServer) GetAllOrders(ctx context.Context, req *QueryGetAllOrdersRequest) (*QueryGetAllOrdersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetAllOrders not implemented") } -func (*UnimplementedQueryServer) QueryGetMarket(ctx context.Context, req *QueryGetMarketRequest) (*QueryGetMarketResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryGetMarket not implemented") +func (*UnimplementedQueryServer) GetMarket(ctx context.Context, req *QueryGetMarketRequest) (*QueryGetMarketResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetMarket not implemented") } -func (*UnimplementedQueryServer) QueryGetAllMarkets(ctx context.Context, req *QueryGetAllMarketsRequest) (*QueryGetAllMarketsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryGetAllMarkets not implemented") +func (*UnimplementedQueryServer) GetAllMarkets(ctx context.Context, req *QueryGetAllMarketsRequest) (*QueryGetAllMarketsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetAllMarkets not implemented") } -func (*UnimplementedQueryServer) QueryParams(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryParams not implemented") +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") } -func (*UnimplementedQueryServer) QueryValidateCreateMarket(ctx context.Context, req *QueryValidateCreateMarketRequest) (*QueryValidateCreateMarketResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryValidateCreateMarket not implemented") +func (*UnimplementedQueryServer) ValidateCreateMarket(ctx context.Context, req *QueryValidateCreateMarketRequest) (*QueryValidateCreateMarketResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidateCreateMarket not implemented") } -func (*UnimplementedQueryServer) QueryValidateMarket(ctx context.Context, req *QueryValidateMarketRequest) (*QueryValidateMarketResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryValidateMarket not implemented") +func (*UnimplementedQueryServer) ValidateMarket(ctx context.Context, req *QueryValidateMarketRequest) (*QueryValidateMarketResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidateMarket not implemented") } -func (*UnimplementedQueryServer) QueryValidateManageFees(ctx context.Context, req *QueryValidateManageFeesRequest) (*QueryValidateManageFeesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryValidateManageFees not implemented") +func (*UnimplementedQueryServer) ValidateManageFees(ctx context.Context, req *QueryValidateManageFeesRequest) (*QueryValidateManageFeesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidateManageFees not implemented") } func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) } -func _Query_QueryOrderFeeCalc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_OrderFeeCalc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryOrderFeeCalcRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryOrderFeeCalc(ctx, in) + return srv.(QueryServer).OrderFeeCalc(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryOrderFeeCalc", + FullMethod: "/provenance.exchange.v1.Query/OrderFeeCalc", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryOrderFeeCalc(ctx, req.(*QueryOrderFeeCalcRequest)) + return srv.(QueryServer).OrderFeeCalc(ctx, req.(*QueryOrderFeeCalcRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_QueryGetOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_GetOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryGetOrderRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryGetOrder(ctx, in) + return srv.(QueryServer).GetOrder(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryGetOrder", + FullMethod: "/provenance.exchange.v1.Query/GetOrder", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryGetOrder(ctx, req.(*QueryGetOrderRequest)) + return srv.(QueryServer).GetOrder(ctx, req.(*QueryGetOrderRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_QueryGetOrderByExternalID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_GetOrderByExternalID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryGetOrderByExternalIDRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryGetOrderByExternalID(ctx, in) + return srv.(QueryServer).GetOrderByExternalID(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryGetOrderByExternalID", + FullMethod: "/provenance.exchange.v1.Query/GetOrderByExternalID", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryGetOrderByExternalID(ctx, req.(*QueryGetOrderByExternalIDRequest)) + return srv.(QueryServer).GetOrderByExternalID(ctx, req.(*QueryGetOrderByExternalIDRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_QueryGetMarketOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_GetMarketOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryGetMarketOrdersRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryGetMarketOrders(ctx, in) + return srv.(QueryServer).GetMarketOrders(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryGetMarketOrders", + FullMethod: "/provenance.exchange.v1.Query/GetMarketOrders", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryGetMarketOrders(ctx, req.(*QueryGetMarketOrdersRequest)) + return srv.(QueryServer).GetMarketOrders(ctx, req.(*QueryGetMarketOrdersRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_QueryGetOwnerOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_GetOwnerOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryGetOwnerOrdersRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryGetOwnerOrders(ctx, in) + return srv.(QueryServer).GetOwnerOrders(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryGetOwnerOrders", + FullMethod: "/provenance.exchange.v1.Query/GetOwnerOrders", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryGetOwnerOrders(ctx, req.(*QueryGetOwnerOrdersRequest)) + return srv.(QueryServer).GetOwnerOrders(ctx, req.(*QueryGetOwnerOrdersRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_QueryGetAssetOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_GetAssetOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryGetAssetOrdersRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryGetAssetOrders(ctx, in) + return srv.(QueryServer).GetAssetOrders(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryGetAssetOrders", + FullMethod: "/provenance.exchange.v1.Query/GetAssetOrders", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryGetAssetOrders(ctx, req.(*QueryGetAssetOrdersRequest)) + return srv.(QueryServer).GetAssetOrders(ctx, req.(*QueryGetAssetOrdersRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_QueryGetAllOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_GetAllOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryGetAllOrdersRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryGetAllOrders(ctx, in) + return srv.(QueryServer).GetAllOrders(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryGetAllOrders", + FullMethod: "/provenance.exchange.v1.Query/GetAllOrders", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryGetAllOrders(ctx, req.(*QueryGetAllOrdersRequest)) + return srv.(QueryServer).GetAllOrders(ctx, req.(*QueryGetAllOrdersRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_QueryGetMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_GetMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryGetMarketRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryGetMarket(ctx, in) + return srv.(QueryServer).GetMarket(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryGetMarket", + FullMethod: "/provenance.exchange.v1.Query/GetMarket", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryGetMarket(ctx, req.(*QueryGetMarketRequest)) + return srv.(QueryServer).GetMarket(ctx, req.(*QueryGetMarketRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_QueryGetAllMarkets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_GetAllMarkets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryGetAllMarketsRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryGetAllMarkets(ctx, in) + return srv.(QueryServer).GetAllMarkets(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryGetAllMarkets", + FullMethod: "/provenance.exchange.v1.Query/GetAllMarkets", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryGetAllMarkets(ctx, req.(*QueryGetAllMarketsRequest)) + return srv.(QueryServer).GetAllMarkets(ctx, req.(*QueryGetAllMarketsRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_QueryParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryParamsRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryParams(ctx, in) + return srv.(QueryServer).Params(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryParams", + FullMethod: "/provenance.exchange.v1.Query/Params", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryParams(ctx, req.(*QueryParamsRequest)) + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_QueryValidateCreateMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_ValidateCreateMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryValidateCreateMarketRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryValidateCreateMarket(ctx, in) + return srv.(QueryServer).ValidateCreateMarket(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryValidateCreateMarket", + FullMethod: "/provenance.exchange.v1.Query/ValidateCreateMarket", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryValidateCreateMarket(ctx, req.(*QueryValidateCreateMarketRequest)) + return srv.(QueryServer).ValidateCreateMarket(ctx, req.(*QueryValidateCreateMarketRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_QueryValidateMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_ValidateMarket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryValidateMarketRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryValidateMarket(ctx, in) + return srv.(QueryServer).ValidateMarket(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryValidateMarket", + FullMethod: "/provenance.exchange.v1.Query/ValidateMarket", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryValidateMarket(ctx, req.(*QueryValidateMarketRequest)) + return srv.(QueryServer).ValidateMarket(ctx, req.(*QueryValidateMarketRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_QueryValidateManageFees_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Query_ValidateManageFees_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryValidateManageFeesRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryValidateManageFees(ctx, in) + return srv.(QueryServer).ValidateManageFees(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/provenance.exchange.v1.Query/QueryValidateManageFees", + FullMethod: "/provenance.exchange.v1.Query/ValidateManageFees", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryValidateManageFees(ctx, req.(*QueryValidateManageFeesRequest)) + return srv.(QueryServer).ValidateManageFees(ctx, req.(*QueryValidateManageFeesRequest)) } return interceptor(ctx, in, info, handler) } @@ -2040,56 +2039,56 @@ var _Query_serviceDesc = grpc.ServiceDesc{ HandlerType: (*QueryServer)(nil), Methods: []grpc.MethodDesc{ { - MethodName: "QueryOrderFeeCalc", - Handler: _Query_QueryOrderFeeCalc_Handler, + MethodName: "OrderFeeCalc", + Handler: _Query_OrderFeeCalc_Handler, }, { - MethodName: "QueryGetOrder", - Handler: _Query_QueryGetOrder_Handler, + MethodName: "GetOrder", + Handler: _Query_GetOrder_Handler, }, { - MethodName: "QueryGetOrderByExternalID", - Handler: _Query_QueryGetOrderByExternalID_Handler, + MethodName: "GetOrderByExternalID", + Handler: _Query_GetOrderByExternalID_Handler, }, { - MethodName: "QueryGetMarketOrders", - Handler: _Query_QueryGetMarketOrders_Handler, + MethodName: "GetMarketOrders", + Handler: _Query_GetMarketOrders_Handler, }, { - MethodName: "QueryGetOwnerOrders", - Handler: _Query_QueryGetOwnerOrders_Handler, + MethodName: "GetOwnerOrders", + Handler: _Query_GetOwnerOrders_Handler, }, { - MethodName: "QueryGetAssetOrders", - Handler: _Query_QueryGetAssetOrders_Handler, + MethodName: "GetAssetOrders", + Handler: _Query_GetAssetOrders_Handler, }, { - MethodName: "QueryGetAllOrders", - Handler: _Query_QueryGetAllOrders_Handler, + MethodName: "GetAllOrders", + Handler: _Query_GetAllOrders_Handler, }, { - MethodName: "QueryGetMarket", - Handler: _Query_QueryGetMarket_Handler, + MethodName: "GetMarket", + Handler: _Query_GetMarket_Handler, }, { - MethodName: "QueryGetAllMarkets", - Handler: _Query_QueryGetAllMarkets_Handler, + MethodName: "GetAllMarkets", + Handler: _Query_GetAllMarkets_Handler, }, { - MethodName: "QueryParams", - Handler: _Query_QueryParams_Handler, + MethodName: "Params", + Handler: _Query_Params_Handler, }, { - MethodName: "QueryValidateCreateMarket", - Handler: _Query_QueryValidateCreateMarket_Handler, + MethodName: "ValidateCreateMarket", + Handler: _Query_ValidateCreateMarket_Handler, }, { - MethodName: "QueryValidateMarket", - Handler: _Query_QueryValidateMarket_Handler, + MethodName: "ValidateMarket", + Handler: _Query_ValidateMarket_Handler, }, { - MethodName: "QueryValidateManageFees", - Handler: _Query_QueryValidateManageFees_Handler, + MethodName: "ValidateManageFees", + Handler: _Query_ValidateManageFees_Handler, }, }, Streams: []grpc.StreamDesc{}, diff --git a/x/exchange/query.pb.gw.go b/x/exchange/query.pb.gw.go index 7eacc45cdf..240e0f5861 100644 --- a/x/exchange/query.pb.gw.go +++ b/x/exchange/query.pb.gw.go @@ -32,42 +32,42 @@ var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage var ( - filter_Query_QueryOrderFeeCalc_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + filter_Query_OrderFeeCalc_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) -func request_Query_QueryOrderFeeCalc_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_OrderFeeCalc_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryOrderFeeCalcRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryOrderFeeCalc_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_OrderFeeCalc_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.QueryOrderFeeCalc(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.OrderFeeCalc(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryOrderFeeCalc_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_OrderFeeCalc_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryOrderFeeCalcRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryOrderFeeCalc_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_OrderFeeCalc_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.QueryOrderFeeCalc(ctx, &protoReq) + msg, err := server.OrderFeeCalc(ctx, &protoReq) return msg, metadata, err } -func request_Query_QueryGetOrder_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_GetOrder_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetOrderRequest var metadata runtime.ServerMetadata @@ -89,12 +89,12 @@ func request_Query_QueryGetOrder_0(ctx context.Context, marshaler runtime.Marsha return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "order_id", err) } - msg, err := client.QueryGetOrder(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.GetOrder(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryGetOrder_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_GetOrder_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetOrderRequest var metadata runtime.ServerMetadata @@ -116,12 +116,12 @@ func local_request_Query_QueryGetOrder_0(ctx context.Context, marshaler runtime. return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "order_id", err) } - msg, err := server.QueryGetOrder(ctx, &protoReq) + msg, err := server.GetOrder(ctx, &protoReq) return msg, metadata, err } -func request_Query_QueryGetOrderByExternalID_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_GetOrderByExternalID_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetOrderByExternalIDRequest var metadata runtime.ServerMetadata @@ -154,12 +154,12 @@ func request_Query_QueryGetOrderByExternalID_0(ctx context.Context, marshaler ru return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "external_id", err) } - msg, err := client.QueryGetOrderByExternalID(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.GetOrderByExternalID(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryGetOrderByExternalID_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_GetOrderByExternalID_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetOrderByExternalIDRequest var metadata runtime.ServerMetadata @@ -192,12 +192,12 @@ func local_request_Query_QueryGetOrderByExternalID_0(ctx context.Context, marsha return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "external_id", err) } - msg, err := server.QueryGetOrderByExternalID(ctx, &protoReq) + msg, err := server.GetOrderByExternalID(ctx, &protoReq) return msg, metadata, err } -func request_Query_QueryGetOrderByExternalID_1(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_GetOrderByExternalID_1(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetOrderByExternalIDRequest var metadata runtime.ServerMetadata @@ -230,12 +230,12 @@ func request_Query_QueryGetOrderByExternalID_1(ctx context.Context, marshaler ru return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "external_id", err) } - msg, err := client.QueryGetOrderByExternalID(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.GetOrderByExternalID(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryGetOrderByExternalID_1(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_GetOrderByExternalID_1(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetOrderByExternalIDRequest var metadata runtime.ServerMetadata @@ -268,16 +268,16 @@ func local_request_Query_QueryGetOrderByExternalID_1(ctx context.Context, marsha return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "external_id", err) } - msg, err := server.QueryGetOrderByExternalID(ctx, &protoReq) + msg, err := server.GetOrderByExternalID(ctx, &protoReq) return msg, metadata, err } var ( - filter_Query_QueryGetMarketOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{"market_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Query_GetMarketOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{"market_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} ) -func request_Query_QueryGetMarketOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_GetMarketOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetMarketOrdersRequest var metadata runtime.ServerMetadata @@ -302,16 +302,16 @@ func request_Query_QueryGetMarketOrders_0(ctx context.Context, marshaler runtime if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetMarketOrders_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GetMarketOrders_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.QueryGetMarketOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.GetMarketOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryGetMarketOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_GetMarketOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetMarketOrdersRequest var metadata runtime.ServerMetadata @@ -336,20 +336,20 @@ func local_request_Query_QueryGetMarketOrders_0(ctx context.Context, marshaler r if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetMarketOrders_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GetMarketOrders_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.QueryGetMarketOrders(ctx, &protoReq) + msg, err := server.GetMarketOrders(ctx, &protoReq) return msg, metadata, err } var ( - filter_Query_QueryGetMarketOrders_1 = &utilities.DoubleArray{Encoding: map[string]int{"market_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Query_GetMarketOrders_1 = &utilities.DoubleArray{Encoding: map[string]int{"market_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} ) -func request_Query_QueryGetMarketOrders_1(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_GetMarketOrders_1(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetMarketOrdersRequest var metadata runtime.ServerMetadata @@ -374,16 +374,16 @@ func request_Query_QueryGetMarketOrders_1(ctx context.Context, marshaler runtime if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetMarketOrders_1); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GetMarketOrders_1); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.QueryGetMarketOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.GetMarketOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryGetMarketOrders_1(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_GetMarketOrders_1(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetMarketOrdersRequest var metadata runtime.ServerMetadata @@ -408,20 +408,20 @@ func local_request_Query_QueryGetMarketOrders_1(ctx context.Context, marshaler r if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetMarketOrders_1); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GetMarketOrders_1); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.QueryGetMarketOrders(ctx, &protoReq) + msg, err := server.GetMarketOrders(ctx, &protoReq) return msg, metadata, err } var ( - filter_Query_QueryGetOwnerOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{"owner": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Query_GetOwnerOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{"owner": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} ) -func request_Query_QueryGetOwnerOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_GetOwnerOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetOwnerOrdersRequest var metadata runtime.ServerMetadata @@ -446,16 +446,16 @@ func request_Query_QueryGetOwnerOrders_0(ctx context.Context, marshaler runtime. if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetOwnerOrders_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GetOwnerOrders_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.QueryGetOwnerOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.GetOwnerOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryGetOwnerOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_GetOwnerOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetOwnerOrdersRequest var metadata runtime.ServerMetadata @@ -480,20 +480,20 @@ func local_request_Query_QueryGetOwnerOrders_0(ctx context.Context, marshaler ru if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetOwnerOrders_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GetOwnerOrders_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.QueryGetOwnerOrders(ctx, &protoReq) + msg, err := server.GetOwnerOrders(ctx, &protoReq) return msg, metadata, err } var ( - filter_Query_QueryGetAssetOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{"asset": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Query_GetAssetOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{"asset": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} ) -func request_Query_QueryGetAssetOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_GetAssetOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetAssetOrdersRequest var metadata runtime.ServerMetadata @@ -518,16 +518,16 @@ func request_Query_QueryGetAssetOrders_0(ctx context.Context, marshaler runtime. if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetAssetOrders_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GetAssetOrders_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.QueryGetAssetOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.GetAssetOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryGetAssetOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_GetAssetOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetAssetOrdersRequest var metadata runtime.ServerMetadata @@ -552,52 +552,52 @@ func local_request_Query_QueryGetAssetOrders_0(ctx context.Context, marshaler ru if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetAssetOrders_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GetAssetOrders_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.QueryGetAssetOrders(ctx, &protoReq) + msg, err := server.GetAssetOrders(ctx, &protoReq) return msg, metadata, err } var ( - filter_Query_QueryGetAllOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + filter_Query_GetAllOrders_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) -func request_Query_QueryGetAllOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_GetAllOrders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetAllOrdersRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetAllOrders_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GetAllOrders_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.QueryGetAllOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.GetAllOrders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryGetAllOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_GetAllOrders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetAllOrdersRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetAllOrders_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GetAllOrders_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.QueryGetAllOrders(ctx, &protoReq) + msg, err := server.GetAllOrders(ctx, &protoReq) return msg, metadata, err } -func request_Query_QueryGetMarket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_GetMarket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetMarketRequest var metadata runtime.ServerMetadata @@ -619,12 +619,12 @@ func request_Query_QueryGetMarket_0(ctx context.Context, marshaler runtime.Marsh return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) } - msg, err := client.QueryGetMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.GetMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryGetMarket_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_GetMarket_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetMarketRequest var metadata runtime.ServerMetadata @@ -646,102 +646,102 @@ func local_request_Query_QueryGetMarket_0(ctx context.Context, marshaler runtime return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) } - msg, err := server.QueryGetMarket(ctx, &protoReq) + msg, err := server.GetMarket(ctx, &protoReq) return msg, metadata, err } var ( - filter_Query_QueryGetAllMarkets_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + filter_Query_GetAllMarkets_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) -func request_Query_QueryGetAllMarkets_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_GetAllMarkets_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetAllMarketsRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetAllMarkets_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GetAllMarkets_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.QueryGetAllMarkets(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.GetAllMarkets(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryGetAllMarkets_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_GetAllMarkets_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetAllMarketsRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryGetAllMarkets_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GetAllMarkets_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.QueryGetAllMarkets(ctx, &protoReq) + msg, err := server.GetAllMarkets(ctx, &protoReq) return msg, metadata, err } -func request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryParamsRequest var metadata runtime.ServerMetadata - msg, err := client.QueryParams(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryParamsRequest var metadata runtime.ServerMetadata - msg, err := server.QueryParams(ctx, &protoReq) + msg, err := server.Params(ctx, &protoReq) return msg, metadata, err } var ( - filter_Query_QueryValidateCreateMarket_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + filter_Query_ValidateCreateMarket_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) -func request_Query_QueryValidateCreateMarket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_ValidateCreateMarket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryValidateCreateMarketRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryValidateCreateMarket_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ValidateCreateMarket_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.QueryValidateCreateMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.ValidateCreateMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryValidateCreateMarket_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_ValidateCreateMarket_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryValidateCreateMarketRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryValidateCreateMarket_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ValidateCreateMarket_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.QueryValidateCreateMarket(ctx, &protoReq) + msg, err := server.ValidateCreateMarket(ctx, &protoReq) return msg, metadata, err } -func request_Query_QueryValidateMarket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_ValidateMarket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryValidateMarketRequest var metadata runtime.ServerMetadata @@ -763,12 +763,12 @@ func request_Query_QueryValidateMarket_0(ctx context.Context, marshaler runtime. return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) } - msg, err := client.QueryValidateMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.ValidateMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryValidateMarket_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_ValidateMarket_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryValidateMarketRequest var metadata runtime.ServerMetadata @@ -790,12 +790,12 @@ func local_request_Query_QueryValidateMarket_0(ctx context.Context, marshaler ru return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) } - msg, err := server.QueryValidateMarket(ctx, &protoReq) + msg, err := server.ValidateMarket(ctx, &protoReq) return msg, metadata, err } -func request_Query_QueryValidateMarket_1(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_ValidateMarket_1(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryValidateMarketRequest var metadata runtime.ServerMetadata @@ -817,12 +817,12 @@ func request_Query_QueryValidateMarket_1(ctx context.Context, marshaler runtime. return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) } - msg, err := client.QueryValidateMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.ValidateMarket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryValidateMarket_1(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_ValidateMarket_1(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryValidateMarketRequest var metadata runtime.ServerMetadata @@ -844,43 +844,43 @@ func local_request_Query_QueryValidateMarket_1(ctx context.Context, marshaler ru return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "market_id", err) } - msg, err := server.QueryValidateMarket(ctx, &protoReq) + msg, err := server.ValidateMarket(ctx, &protoReq) return msg, metadata, err } var ( - filter_Query_QueryValidateManageFees_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + filter_Query_ValidateManageFees_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) -func request_Query_QueryValidateManageFees_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Query_ValidateManageFees_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryValidateManageFeesRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryValidateManageFees_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ValidateManageFees_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.QueryValidateManageFees(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.ValidateManageFees(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryValidateManageFees_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Query_ValidateManageFees_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryValidateManageFeesRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryValidateManageFees_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ValidateManageFees_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.QueryValidateManageFees(ctx, &protoReq) + msg, err := server.ValidateManageFees(ctx, &protoReq) return msg, metadata, err } @@ -891,7 +891,7 @@ func local_request_Query_QueryValidateManageFees_0(ctx context.Context, marshale // Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { - mux.Handle("GET", pattern_Query_QueryOrderFeeCalc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_OrderFeeCalc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -900,18 +900,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryOrderFeeCalc_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_OrderFeeCalc_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryOrderFeeCalc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_OrderFeeCalc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetOrder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetOrder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -920,18 +920,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryGetOrder_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_GetOrder_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetOrder_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetOrder_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetOrderByExternalID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetOrderByExternalID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -940,18 +940,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryGetOrderByExternalID_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_GetOrderByExternalID_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetOrderByExternalID_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetOrderByExternalID_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetOrderByExternalID_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetOrderByExternalID_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -960,18 +960,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryGetOrderByExternalID_1(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_GetOrderByExternalID_1(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetOrderByExternalID_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetOrderByExternalID_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetMarketOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetMarketOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -980,18 +980,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryGetMarketOrders_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_GetMarketOrders_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetMarketOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetMarketOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetMarketOrders_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetMarketOrders_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1000,18 +1000,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryGetMarketOrders_1(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_GetMarketOrders_1(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetMarketOrders_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetMarketOrders_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetOwnerOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetOwnerOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1020,18 +1020,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryGetOwnerOrders_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_GetOwnerOrders_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetOwnerOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetOwnerOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetAssetOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetAssetOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1040,18 +1040,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryGetAssetOrders_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_GetAssetOrders_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetAssetOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetAssetOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetAllOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetAllOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1060,18 +1060,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryGetAllOrders_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_GetAllOrders_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetAllOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetAllOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1080,18 +1080,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryGetMarket_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_GetMarket_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetAllMarkets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetAllMarkets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1100,18 +1100,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryGetAllMarkets_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_GetAllMarkets_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetAllMarkets_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetAllMarkets_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryParams_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1120,18 +1120,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryParams_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryParams_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryValidateCreateMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_ValidateCreateMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1140,18 +1140,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryValidateCreateMarket_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_ValidateCreateMarket_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryValidateCreateMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_ValidateCreateMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryValidateMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_ValidateMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1160,18 +1160,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryValidateMarket_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_ValidateMarket_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryValidateMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_ValidateMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryValidateMarket_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_ValidateMarket_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1180,18 +1180,18 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryValidateMarket_1(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_ValidateMarket_1(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryValidateMarket_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_ValidateMarket_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryValidateManageFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_ValidateManageFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1200,14 +1200,14 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryValidateManageFees_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_ValidateManageFees_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryValidateManageFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_ValidateManageFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -1252,7 +1252,7 @@ func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc // "QueryClient" to call the correct interceptors. func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { - mux.Handle("GET", pattern_Query_QueryOrderFeeCalc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_OrderFeeCalc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1261,18 +1261,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryOrderFeeCalc_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_OrderFeeCalc_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryOrderFeeCalc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_OrderFeeCalc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetOrder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetOrder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1281,18 +1281,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryGetOrder_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_GetOrder_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetOrder_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetOrder_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetOrderByExternalID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetOrderByExternalID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1301,18 +1301,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryGetOrderByExternalID_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_GetOrderByExternalID_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetOrderByExternalID_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetOrderByExternalID_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetOrderByExternalID_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetOrderByExternalID_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1321,18 +1321,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryGetOrderByExternalID_1(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_GetOrderByExternalID_1(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetOrderByExternalID_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetOrderByExternalID_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetMarketOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetMarketOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1341,18 +1341,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryGetMarketOrders_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_GetMarketOrders_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetMarketOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetMarketOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetMarketOrders_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetMarketOrders_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1361,18 +1361,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryGetMarketOrders_1(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_GetMarketOrders_1(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetMarketOrders_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetMarketOrders_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetOwnerOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetOwnerOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1381,18 +1381,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryGetOwnerOrders_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_GetOwnerOrders_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetOwnerOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetOwnerOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetAssetOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetAssetOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1401,18 +1401,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryGetAssetOrders_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_GetAssetOrders_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetAssetOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetAssetOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetAllOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetAllOrders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1421,18 +1421,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryGetAllOrders_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_GetAllOrders_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetAllOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetAllOrders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1441,18 +1441,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryGetMarket_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_GetMarket_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryGetAllMarkets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetAllMarkets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1461,18 +1461,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryGetAllMarkets_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_GetAllMarkets_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryGetAllMarkets_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetAllMarkets_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryParams_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1481,18 +1481,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryParams_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryParams_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryValidateCreateMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_ValidateCreateMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1501,18 +1501,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryValidateCreateMarket_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_ValidateCreateMarket_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryValidateCreateMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_ValidateCreateMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryValidateMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_ValidateMarket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1521,18 +1521,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryValidateMarket_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_ValidateMarket_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryValidateMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_ValidateMarket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryValidateMarket_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_ValidateMarket_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1541,18 +1541,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryValidateMarket_1(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_ValidateMarket_1(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryValidateMarket_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_ValidateMarket_1(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryValidateManageFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_ValidateManageFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1561,14 +1561,14 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryValidateManageFees_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_ValidateManageFees_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_QueryValidateManageFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_ValidateManageFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -1576,69 +1576,69 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_QueryOrderFeeCalc_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "fees", "order"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_OrderFeeCalc_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "fees", "order"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetOrder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "order", "order_id"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_GetOrder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "order", "order_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetOrderByExternalID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 1, 0, 4, 1, 5, 6}, []string{"provenance", "exchange", "v1", "orders", "market", "market_id", "external_id"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_GetOrderByExternalID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 1, 0, 4, 1, 5, 6}, []string{"provenance", "exchange", "v1", "orders", "market", "market_id", "external_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetOrderByExternalID_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"provenance", "exchange", "v1", "market", "market_id", "order", "external_id"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_GetOrderByExternalID_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"provenance", "exchange", "v1", "market", "market_id", "order", "external_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetMarketOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"provenance", "exchange", "v1", "orders", "market", "market_id"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_GetMarketOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"provenance", "exchange", "v1", "orders", "market", "market_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetMarketOrders_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"provenance", "exchange", "v1", "market", "market_id", "orders"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_GetMarketOrders_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"provenance", "exchange", "v1", "market", "market_id", "orders"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetOwnerOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "orders", "owner"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_GetOwnerOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "orders", "owner"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetAssetOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "orders", "asset"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_GetAssetOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "orders", "asset"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetAllOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "orders"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_GetAllOrders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "orders"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "market", "market_id"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_GetMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"provenance", "exchange", "v1", "market", "market_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryGetAllMarkets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "markets"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_GetAllMarkets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "markets"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"provenance", "exchange", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryValidateCreateMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "validate", "create_market"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_ValidateCreateMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "validate", "create_market"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryValidateMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"provenance", "exchange", "v1", "validate", "market", "market_id"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_ValidateMarket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"provenance", "exchange", "v1", "validate", "market", "market_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryValidateMarket_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"provenance", "exchange", "v1", "market", "market_id", "validate"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_ValidateMarket_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"provenance", "exchange", "v1", "market", "market_id", "validate"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryValidateManageFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "validate", "manage_fees"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_ValidateManageFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"provenance", "exchange", "v1", "validate", "manage_fees"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( - forward_Query_QueryOrderFeeCalc_0 = runtime.ForwardResponseMessage + forward_Query_OrderFeeCalc_0 = runtime.ForwardResponseMessage - forward_Query_QueryGetOrder_0 = runtime.ForwardResponseMessage + forward_Query_GetOrder_0 = runtime.ForwardResponseMessage - forward_Query_QueryGetOrderByExternalID_0 = runtime.ForwardResponseMessage + forward_Query_GetOrderByExternalID_0 = runtime.ForwardResponseMessage - forward_Query_QueryGetOrderByExternalID_1 = runtime.ForwardResponseMessage + forward_Query_GetOrderByExternalID_1 = runtime.ForwardResponseMessage - forward_Query_QueryGetMarketOrders_0 = runtime.ForwardResponseMessage + forward_Query_GetMarketOrders_0 = runtime.ForwardResponseMessage - forward_Query_QueryGetMarketOrders_1 = runtime.ForwardResponseMessage + forward_Query_GetMarketOrders_1 = runtime.ForwardResponseMessage - forward_Query_QueryGetOwnerOrders_0 = runtime.ForwardResponseMessage + forward_Query_GetOwnerOrders_0 = runtime.ForwardResponseMessage - forward_Query_QueryGetAssetOrders_0 = runtime.ForwardResponseMessage + forward_Query_GetAssetOrders_0 = runtime.ForwardResponseMessage - forward_Query_QueryGetAllOrders_0 = runtime.ForwardResponseMessage + forward_Query_GetAllOrders_0 = runtime.ForwardResponseMessage - forward_Query_QueryGetMarket_0 = runtime.ForwardResponseMessage + forward_Query_GetMarket_0 = runtime.ForwardResponseMessage - forward_Query_QueryGetAllMarkets_0 = runtime.ForwardResponseMessage + forward_Query_GetAllMarkets_0 = runtime.ForwardResponseMessage - forward_Query_QueryParams_0 = runtime.ForwardResponseMessage + forward_Query_Params_0 = runtime.ForwardResponseMessage - forward_Query_QueryValidateCreateMarket_0 = runtime.ForwardResponseMessage + forward_Query_ValidateCreateMarket_0 = runtime.ForwardResponseMessage - forward_Query_QueryValidateMarket_0 = runtime.ForwardResponseMessage + forward_Query_ValidateMarket_0 = runtime.ForwardResponseMessage - forward_Query_QueryValidateMarket_1 = runtime.ForwardResponseMessage + forward_Query_ValidateMarket_1 = runtime.ForwardResponseMessage - forward_Query_QueryValidateManageFees_0 = runtime.ForwardResponseMessage + forward_Query_ValidateManageFees_0 = runtime.ForwardResponseMessage ) From 0e00405cb8b648e4c2876c02b22bab50ec191afc Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 17 Oct 2023 15:03:11 -0600 Subject: [PATCH 300/309] [1658]: Add the hold queries to the stargate whitelist. --- internal/provwasm/stargate_whitelist.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/provwasm/stargate_whitelist.go b/internal/provwasm/stargate_whitelist.go index 6846eb76c3..d0bcc9ff7f 100644 --- a/internal/provwasm/stargate_whitelist.go +++ b/internal/provwasm/stargate_whitelist.go @@ -16,6 +16,7 @@ import ( ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" attributetypes "github.com/provenance-io/provenance/x/attribute/types" + "github.com/provenance-io/provenance/x/hold" markertypes "github.com/provenance-io/provenance/x/marker/types" metadatatypes "github.com/provenance-io/provenance/x/metadata/types" msgfeestypes "github.com/provenance-io/provenance/x/msgfees/types" @@ -81,6 +82,10 @@ func init() { setWhitelistedQuery("/provenance.attribute.v1.Query/Attributes", &attributetypes.QueryAttributesResponse{}) setWhitelistedQuery("/provenance.attribute.v1.Query/Scan", &attributetypes.QueryScanResponse{}) + // hold + setWhitelistedQuery("/provenance.hold.v1.Query/GetHolds", &hold.GetHoldsResponse{}) + setWhitelistedQuery("/provenance.hold.v1.Query/GetAllHolds", &hold.GetAllHoldsResponse{}) + // marker setWhitelistedQuery("/provenance.marker.v1.Query/Params", &markertypes.QueryParamsResponse{}) setWhitelistedQuery("/provenance.marker.v1.Query/Marker", &markertypes.QueryMarkerResponse{}) From 0c9b27e880851ce24c7f2b8db71de5e17df8ca78 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 17 Oct 2023 15:08:09 -0600 Subject: [PATCH 301/309] [1658]: Add all the exchange queries to the stargate whitelist. --- internal/provwasm/stargate_whitelist.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/internal/provwasm/stargate_whitelist.go b/internal/provwasm/stargate_whitelist.go index d0bcc9ff7f..9f454d5795 100644 --- a/internal/provwasm/stargate_whitelist.go +++ b/internal/provwasm/stargate_whitelist.go @@ -16,6 +16,7 @@ import ( ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" attributetypes "github.com/provenance-io/provenance/x/attribute/types" + "github.com/provenance-io/provenance/x/exchange" "github.com/provenance-io/provenance/x/hold" markertypes "github.com/provenance-io/provenance/x/marker/types" metadatatypes "github.com/provenance-io/provenance/x/metadata/types" @@ -82,6 +83,21 @@ func init() { setWhitelistedQuery("/provenance.attribute.v1.Query/Attributes", &attributetypes.QueryAttributesResponse{}) setWhitelistedQuery("/provenance.attribute.v1.Query/Scan", &attributetypes.QueryScanResponse{}) + // exchange + setWhitelistedQuery("/provenance.exchange.v1.Query/OrderFeeCalc", &exchange.QueryOrderFeeCalcResponse{}) + setWhitelistedQuery("/provenance.exchange.v1.Query/GetOrder", &exchange.QueryGetOrderResponse{}) + setWhitelistedQuery("/provenance.exchange.v1.Query/GetOrderByExternalID", &exchange.QueryGetOrderByExternalIDResponse{}) + setWhitelistedQuery("/provenance.exchange.v1.Query/GetMarketOrders", &exchange.QueryGetMarketOrdersResponse{}) + setWhitelistedQuery("/provenance.exchange.v1.Query/GetOwnerOrders", &exchange.QueryGetOwnerOrdersResponse{}) + setWhitelistedQuery("/provenance.exchange.v1.Query/GetAssetOrders", &exchange.QueryGetAssetOrdersResponse{}) + setWhitelistedQuery("/provenance.exchange.v1.Query/GetAllOrders", &exchange.QueryGetAllOrdersResponse{}) + setWhitelistedQuery("/provenance.exchange.v1.Query/GetMarket", &exchange.QueryGetMarketResponse{}) + setWhitelistedQuery("/provenance.exchange.v1.Query/GetAllMarkets", &exchange.QueryGetAllMarketsResponse{}) + setWhitelistedQuery("/provenance.exchange.v1.Query/Params", &exchange.QueryParamsResponse{}) + setWhitelistedQuery("/provenance.exchange.v1.Query/ValidateCreateMarket", &exchange.QueryValidateCreateMarketResponse{}) + setWhitelistedQuery("/provenance.exchange.v1.Query/ValidateMarket", &exchange.QueryValidateMarketResponse{}) + setWhitelistedQuery("/provenance.exchange.v1.Query/ValidateManageFees", &exchange.QueryValidateManageFeesResponse{}) + // hold setWhitelistedQuery("/provenance.hold.v1.Query/GetHolds", &hold.GetHoldsResponse{}) setWhitelistedQuery("/provenance.hold.v1.Query/GetAllHolds", &hold.GetAllHoldsResponse{}) From 44acefe4a6a2f3f905e1e5ad96f796c12ccc45bd Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Sun, 15 Oct 2023 00:30:20 -0600 Subject: [PATCH 302/309] [1658]: Fix the market keeper to set an empty byte slice instead of nil (which the store doesn't allow). --- x/exchange/keeper/market.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 16459ac13e..b8718673b3 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -52,7 +52,7 @@ func isMarketKnown(store sdk.KVStore, marketID uint32) bool { // setMarketKnown sets the known market id indicator in the store. func setMarketKnown(store sdk.KVStore, marketID uint32) { key := MakeKeyKnownMarketID(marketID) - store.Set(key, nil) + store.Set(key, []byte{}) } // validateMarketExists returns an error if the provided marketID does not exist. @@ -688,7 +688,7 @@ func setMarketActive(store sdk.KVStore, marketID uint32, active bool) { if active { store.Delete(key) } else { - store.Set(key, nil) + store.Set(key, []byte{}) } } @@ -702,7 +702,7 @@ func isUserSettlementAllowed(store sdk.KVStore, marketID uint32) bool { func setUserSettlementAllowed(store sdk.KVStore, marketID uint32, allowed bool) { key := MakeKeyMarketUserSettle(marketID) if allowed { - store.Set(key, nil) + store.Set(key, []byte{}) } else { store.Delete(key) } @@ -754,7 +754,7 @@ func storeHasPermission(store sdk.KVStore, marketID uint32, addr sdk.AccAddress, func grantPermissions(store sdk.KVStore, marketID uint32, addr sdk.AccAddress, permissions []exchange.Permission) { for _, perm := range permissions { key := MakeKeyMarketPermissions(marketID, addr, perm) - store.Set(key, nil) + store.Set(key, []byte{}) } } From b8728ab77e867ebf8b9830a13db7411087689764 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 16 Oct 2023 13:47:48 -0600 Subject: [PATCH 303/309] [1658]: Fix getBuyerSettlementFeeRatiosForPriceDenom to return an error if there are no ratios found but the market does have ratios. --- x/exchange/keeper/market.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index b8718673b3..e707fd4c55 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -443,7 +443,7 @@ func updateBuyerSettlementRatios(store sdk.KVStore, marketID uint32, toDelete, t } // getBuyerSettlementFeeRatiosForPriceDenom gets all the buyer settlement fee ratios in a market that have the give price denom. -func getBuyerSettlementFeeRatiosForPriceDenom(store sdk.KVStore, marketID uint32, priceDenom string) []exchange.FeeRatio { +func getBuyerSettlementFeeRatiosForPriceDenom(store sdk.KVStore, marketID uint32, priceDenom string) ([]exchange.FeeRatio, error) { var ratios []exchange.FeeRatio iterate(store, GetKeyPrefixMarketBuyerSettlementRatioForPriceDenom(marketID, priceDenom), func(key, value []byte) bool { feeDenom := string(key) @@ -456,12 +456,18 @@ func getBuyerSettlementFeeRatiosForPriceDenom(store sdk.KVStore, marketID uint32 } return false }) - return ratios + if len(ratios) == 0 && hasFeeRatio(store, marketID, buyerSettlementRatioKeyMakers) { + return nil, fmt.Errorf("no buyer settlement fee ratios found for denom %q", priceDenom) + } + return ratios, nil } // calcBuyerSettlementRatioFeeOptions calculates the buyer settlement ratio fee options available for the given price. func calcBuyerSettlementRatioFeeOptions(store sdk.KVStore, marketID uint32, price sdk.Coin) ([]sdk.Coin, error) { - ratios := getBuyerSettlementFeeRatiosForPriceDenom(store, marketID, price.Denom) + ratios, err := getBuyerSettlementFeeRatiosForPriceDenom(store, marketID, price.Denom) + if err != nil { + return nil, err + } if len(ratios) == 0 { return nil, nil } @@ -469,9 +475,9 @@ func calcBuyerSettlementRatioFeeOptions(store sdk.KVStore, marketID uint32, pric var errs []error rv := make([]sdk.Coin, 0, len(ratios)) for _, ratio := range ratios { - fee, err := ratio.ApplyTo(price) - if err != nil { - errs = append(errs, fmt.Errorf("buyer settlement fees: %w", err)) + fee, ferr := ratio.ApplyTo(price) + if ferr != nil { + errs = append(errs, fmt.Errorf("buyer settlement fees: %w", ferr)) } else { rv = append(rv, fee) } From 145d2c1ad8ccda924798e01e351f4284531ef10d Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 16 Oct 2023 14:20:51 -0600 Subject: [PATCH 304/309] [1658]: Add the fee to an error in validateFlatFee. Fix validateSellerSettlementFlatFee to use the correct key maker. --- x/exchange/keeper/market.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index e707fd4c55..c64e3f1db1 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -142,7 +142,7 @@ func validateFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin, name str reqFee := getFlatFee(store, marketID, fee.Denom, maker) if reqFee == nil { opts := getAllFlatFees(store, marketID, maker) - return fmt.Errorf("invalid %s fee, must be one of: %s", name, sdk.NewCoins(opts...).String()) + return fmt.Errorf("invalid %s fee %q, must be one of: %s", name, fee, sdk.NewCoins(opts...).String()) } if fee.Amount.LT(reqFee.Amount) { return fmt.Errorf("insufficient %s fee: %q is less than required amount %q", name, fee, reqFee) @@ -227,7 +227,7 @@ func updateCreateBidFlatFees(store sdk.KVStore, marketID uint32, toDelete, toAdd // validateSellerSettlementFlatFee returns an error if the provided fee is not a sufficient seller settlement flat fee. func validateSellerSettlementFlatFee(store sdk.KVStore, marketID uint32, fee *sdk.Coin) error { - return validateFlatFee(store, marketID, fee, "seller settlement flat", createBidFlatKeyMakers) + return validateFlatFee(store, marketID, fee, "seller settlement flat", sellerSettlementFlatKeyMakers) } // getSellerSettlementFlatFees gets the seller settlement flat fee options for a market. From a726b010cc48d7ee2709a315b8a939c86a7aca2f Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 16 Oct 2023 15:50:26 -0600 Subject: [PATCH 305/309] [1658]: Refactor validateAskPrice to clean it up a bit. --- x/exchange/keeper/market.go | 46 ++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index c64e3f1db1..35c14d7055 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -388,24 +388,38 @@ func validateAskPrice(store sdk.KVStore, marketID uint32, price sdk.Coin, settle // If there is a settlement flat fee with a different denom as the price, a hold is placed on it. // If there's a settlement flat fee with the same denom as the price, it's paid out of the price along - // with the ratio amount. Assuming the ratio is less than one, the price will always cover the ratio fee amount. - // But if the flat fee is coming out of the price too, it's possible that the price might be less than the total - // fee that will need to come out of it. We want to return an error if that's the case. - if settlementFlatFee != nil && price.Denom == settlementFlatFee.Denom { - if price.Amount.LT(settlementFlatFee.Amount) { - return fmt.Errorf("price %s is less than seller settlement flat fee %s", price, settlementFlatFee) + // with the ratio amount. Assuming the ratio is less than one, the price will always be at least the ratio fee amount. + // Here, we make sure that the price is more than any fee that is to come out of it. + // Since the ask price is a minimum, and the ratio is less than 1 (fee amount goes up slower than price amount), + // if it's okay with the provided price, it'll also be okay for a larger price too. + + checkFlat := settlementFlatFee != nil && !settlementFlatFee.Amount.IsZero() && price.Denom == settlementFlatFee.Denom + if ratio == nil { + // There's no ratio aspect to check, just maybe look at the flat. + if checkFlat && price.Amount.LTE(settlementFlatFee.Amount) { + return fmt.Errorf("price %s is not more than seller settlement flat fee %s", price, settlementFlatFee) } - if ratio != nil { - ratioFee, err := ratio.ApplyToLoosely(price) - if err != nil { - return err - } - reqPrice := settlementFlatFee.Add(ratioFee) - if price.IsLT(reqPrice) { - return fmt.Errorf("price %s is less than total required seller settlement fee of %s = %s flat + %s ratio", - price, reqPrice, settlementFlatFee, ratioFee) - } + return nil + } + + ratioFee, rerr := ratio.ApplyToLoosely(price) + if rerr != nil { + return rerr + } + + if !checkFlat { + // There's no flat aspect to check, just check the ratio. + if price.Amount.LTE(ratioFee.Amount) { + return fmt.Errorf("price %s is not more than seller settlement ratio fee %s", price, ratioFee) } + return nil + } + + // Check both together. + reqPriceAmt := settlementFlatFee.Amount.Add(ratioFee.Amount) + if price.Amount.LTE(reqPriceAmt) { + return fmt.Errorf("price %s is not more than total required seller settlement fee %s = %s flat + %s ratio", + price, sdk.NewCoin(price.Denom, reqPriceAmt), settlementFlatFee, ratioFee) } return nil From a2ae3bddb96f7f4cf5ba78b7f58fe63ad848e7dc Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 17 Oct 2023 11:30:38 -0600 Subject: [PATCH 306/309] [1658]: In validateBuyerSettlementFee, when a fee is required but one isn't provided, have the error include the required options. --- x/exchange/keeper/market.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index 35c14d7055..dc57a09bcb 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -518,11 +518,6 @@ func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Co return nil } - if len(fee) == 0 { - // a fee is required, but we have none. - return errors.New("insufficient buyer settlement fee: no fee provided") - } - // Loop through each coin in the fee looking for entries that satisfy the fee requirements. var flatFeeOk, ratioFeeOk bool var errs []error @@ -596,14 +591,14 @@ func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Co // Programmer Sanity check. // Either we returned earlier or we added at least one entry to errs. - if len(errs) == 0 { + if len(errs) == 0 && len(fee) > 0 { panic("no specific errors identified") } // If a fee type was required, but not satisfied, add that to the errors for easier identification by users. if flatFeeReq && !flatFeeOk { flatFeeOptions := getAllFlatFees(store, marketID, flatKeyMaker) - errs = append(errs, fmt.Errorf("required flat fee not satisfied, valid options: %s", flatFeeOptions)) + errs = append(errs, fmt.Errorf("required flat fee not satisfied, valid options: %s", sdk.Coins(flatFeeOptions))) } if ratioFeeReq && !ratioFeeOk { allRatioOptions := getAllFeeRatios(store, marketID, ratioKeyMaker) @@ -612,7 +607,11 @@ func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Co } // And add an error with the overall reason for this failure. - errs = append(errs, fmt.Errorf("insufficient buyer settlement fee %s", fee)) + if len(fee) > 0 { + errs = append(errs, fmt.Errorf("insufficient buyer settlement fee %s", fee)) + } else { + errs = append(errs, errors.New("insufficient buyer settlement fee: no fee provided")) + } return errors.Join(errs...) } From 13820fb5ce5a7be4a6feb27f3f7530d730b86b5c Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 17 Oct 2023 12:44:46 -0600 Subject: [PATCH 307/309] [1658]: in validateBuyerSettlementFee, only include errors for a type that isn't satisfied. --- x/exchange/keeper/market.go | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/x/exchange/keeper/market.go b/x/exchange/keeper/market.go index dc57a09bcb..5bf0c64868 100644 --- a/x/exchange/keeper/market.go +++ b/x/exchange/keeper/market.go @@ -520,7 +520,9 @@ func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Co // Loop through each coin in the fee looking for entries that satisfy the fee requirements. var flatFeeOk, ratioFeeOk bool - var errs []error + var flatErrs []error + var ratioErrs []error + var combErrs []error for _, feeCoin := range fee { var flatFeeAmt, ratioFeeAmt *sdkmath.Int @@ -528,9 +530,9 @@ func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Co flatFee := getFlatFee(store, marketID, feeCoin.Denom, flatKeyMaker) switch { case flatFee == nil: - errs = append(errs, fmt.Errorf("no flat fee options available for denom %s", feeCoin.Denom)) + flatErrs = append(flatErrs, fmt.Errorf("no flat fee options available for denom %s", feeCoin.Denom)) case feeCoin.Amount.LT(flatFee.Amount): - errs = append(errs, fmt.Errorf("%s is less than required flat fee %s", feeCoin, flatFee)) + flatErrs = append(flatErrs, fmt.Errorf("%s is less than required flat fee %s", feeCoin, flatFee)) case !ratioFeeReq: // This fee coin covers the flat fee, and there is no ratio fee needed, so we're all good. return nil @@ -545,15 +547,15 @@ func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Co if ratioFeeReq { ratio := getFeeRatio(store, marketID, price.Denom, feeCoin.Denom, ratioKeyMaker) if ratio == nil { - errs = append(errs, fmt.Errorf("no ratio from price denom %s to fee denom %s", + ratioErrs = append(ratioErrs, fmt.Errorf("no ratio from price denom %s to fee denom %s", price.Denom, feeCoin.Denom)) } else { ratioFee, err := ratio.ApplyTo(price) switch { case err != nil: - errs = append(errs, err) + ratioErrs = append(ratioErrs, err) case feeCoin.Amount.LT(ratioFee.Amount): - errs = append(errs, fmt.Errorf("%s is less than required ratio fee %s (based on price %s and ratio %s)", + ratioErrs = append(ratioErrs, fmt.Errorf("%s is less than required ratio fee %s (based on price %s and ratio %s)", feeCoin, ratioFee, price, ratio)) case !flatFeeReq: // This fee coin covers the ratio fee, and there's no flat fee needed, so we're all good. @@ -571,8 +573,8 @@ func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Co if flatFeeAmt != nil && ratioFeeAmt != nil { reqAmt := flatFeeAmt.Add(*ratioFeeAmt) if feeCoin.Amount.LT(reqAmt) { - errs = append(errs, fmt.Errorf("%s is less than combined fee %s%s = flat %s%s + ratio %s%s (based on price %s)", - feeCoin, reqAmt, fee[0].Denom, flatFeeAmt, fee[0].Denom, ratioFeeAmt, fee[0].Denom, price)) + combErrs = append(combErrs, fmt.Errorf("%s is less than combined fee %s%s = %s%s (flat) + %s%s (ratio based on price %s)", + feeCoin, reqAmt, feeCoin.Denom, flatFeeAmt, feeCoin.Denom, ratioFeeAmt, feeCoin.Denom, price)) } else { // This one coin fee is so satisfying. (How satisfying was it?) // It's so satisfying, it covers both! Thank you for coming to my TED talk. @@ -589,22 +591,27 @@ func validateBuyerSettlementFee(store sdk.KVStore, marketID uint32, price sdk.Co } } - // Programmer Sanity check. - // Either we returned earlier or we added at least one entry to errs. - if len(errs) == 0 && len(fee) > 0 { - panic("no specific errors identified") - } - - // If a fee type was required, but not satisfied, add that to the errors for easier identification by users. + // If we get here, the fee is insufficient. + // Combine all the known errors and add some to help users fix problems. + var errs []error if flatFeeReq && !flatFeeOk { + errs = append(errs, flatErrs...) flatFeeOptions := getAllFlatFees(store, marketID, flatKeyMaker) errs = append(errs, fmt.Errorf("required flat fee not satisfied, valid options: %s", sdk.Coins(flatFeeOptions))) } if ratioFeeReq && !ratioFeeOk { + errs = append(errs, ratioErrs...) allRatioOptions := getAllFeeRatios(store, marketID, ratioKeyMaker) errs = append(errs, fmt.Errorf("required ratio fee not satisfied, valid ratios: %s", exchange.FeeRatiosString(allRatioOptions))) } + if len(combErrs) > 0 { + // Both flatFeeOk and ratioFeeOk will be true here, but we'll include + // all those errors since only one of those is actually okay. + errs = append(errs, flatErrs...) + errs = append(errs, ratioErrs...) + errs = append(errs, combErrs...) + } // And add an error with the overall reason for this failure. if len(fee) > 0 { From 30e1ceb256eece27edd110738a15ffa6bee49cf4 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Mon, 16 Oct 2023 17:04:47 -0600 Subject: [PATCH 308/309] [1658]: Create ParseFeeRatio. --- x/exchange/market.go | 39 ++++++++++ x/exchange/market_test.go | 158 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) diff --git a/x/exchange/market.go b/x/exchange/market.go index 5e9a5e245d..2ac61d3e4b 100644 --- a/x/exchange/market.go +++ b/x/exchange/market.go @@ -198,6 +198,45 @@ func ValidateBuyerFeeRatios(ratios []FeeRatio) error { return errors.Join(errs...) } +// parseCoin parses a string into an sdk.Coin +func parseCoin(coinStr string) (sdk.Coin, error) { + // The sdk.ParseCoinNormalized allows for decimals and just truncates if there are some. + // But I want an error if there's a decimal portion. + // It's errors also always have "invalid decimal coin expression", and I don't want "decimal" in these errors. + decCoin, err := sdk.ParseDecCoin(coinStr) + if err != nil || !decCoin.Amount.IsInteger() { + return sdk.Coin{}, fmt.Errorf("invalid coin expression: %q", coinStr) + } + coin, _ := decCoin.TruncateDecimal() + return coin, nil +} + +// ParseFeeRatio parses a ":" string into a FeeRatio. +func ParseFeeRatio(ratio string) (*FeeRatio, error) { + parts := strings.Split(ratio, ":") + if len(parts) != 2 { + return nil, fmt.Errorf("cannot create FeeRatio from %q: expected exactly one colon", ratio) + } + price, err := parseCoin(parts[0]) + if err != nil { + return nil, fmt.Errorf("cannot create FeeRatio from %q: price: %w", ratio, err) + } + fee, err := parseCoin(parts[1]) + if err != nil { + return nil, fmt.Errorf("cannot create FeeRatio from %q: fee: %w", ratio, err) + } + return &FeeRatio{Price: price, Fee: fee}, nil +} + +// MustParseFeeRatio parses a ":" string into a FeeRatio, panicking if there's a problem. +func MustParseFeeRatio(ratio string) FeeRatio { + rv, err := ParseFeeRatio(ratio) + if err != nil { + panic(err) + } + return *rv +} + // String returns a string representation of this FeeRatio. func (r FeeRatio) String() string { return fmt.Sprintf("%s:%s", r.Price, r.Fee) diff --git a/x/exchange/market_test.go b/x/exchange/market_test.go index f4c61ff317..da4d04de7e 100644 --- a/x/exchange/market_test.go +++ b/x/exchange/market_test.go @@ -722,6 +722,164 @@ func TestValidateBuyerFeeRatios(t *testing.T) { } } +func TestParseFeeRatio(t *testing.T) { + ratioStr := func(ratio *FeeRatio) string { + if ratio == nil { + return "" + } + return fmt.Sprintf("%q", ratio.String()) + } + + tests := []struct { + name string + ratio string + expRatio *FeeRatio + expErr string + }{ + { + name: "no colons", + ratio: "8banana", + expErr: "expected exactly one colon", + }, + { + name: "two colons", + ratio: "8apple:5banana:3cactus", + expErr: "expected exactly one colon", + }, + { + name: "one colon: first char", + ratio: ":18banana", + expErr: "price: invalid coin expression: \"\"", + }, + { + name: "one colon: las char", + ratio: "33apple:", + expErr: "fee: invalid coin expression: \"\"", + }, + { + name: "bad price coin", + ratio: "1234:5banana", + expErr: "price: invalid coin expression: \"1234\"", + }, + { + name: "bad fee coin", + ratio: "1234apple:banana", + expErr: "fee: invalid coin expression: \"banana\"", + }, + { + name: "neg price coin", + ratio: "-55apple:3banana", + expErr: "price: invalid coin expression: \"-55apple\"", + }, + { + name: "neg fee coin", + ratio: "55apple:-3banana", + expRatio: nil, + expErr: "fee: invalid coin expression: \"-3banana\"", + }, + { + name: "zero price coin", + ratio: "0apple:21banana", + expRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("apple", 0), + Fee: sdk.NewInt64Coin("banana", 21), + }, + }, + { + name: "zero fee coin", + ratio: "5apple:0banana", + expRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("apple", 5), + Fee: sdk.NewInt64Coin("banana", 0), + }, + }, + { + name: "same denoms: price more", + ratio: "30apple:29apple", + expRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("apple", 30), + Fee: sdk.NewInt64Coin("apple", 29), + }, + }, + { + name: "same denoms: price same", + ratio: "30apple:30apple", + expRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("apple", 30), + Fee: sdk.NewInt64Coin("apple", 30), + }, + }, + { + name: "same denoms: price less", + ratio: "30apple:31apple", + expRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("apple", 30), + Fee: sdk.NewInt64Coin("apple", 31), + }, + }, + { + name: "diff denoms: price more", + ratio: "30apple:29banana", + expRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("apple", 30), + Fee: sdk.NewInt64Coin("banana", 29), + }, + }, + { + name: "diff denoms: price same", + ratio: "30apple:30banana", + expRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("apple", 30), + Fee: sdk.NewInt64Coin("banana", 30), + }, + }, + { + name: "diff denoms: price less", + ratio: "30apple:31banana", + expRatio: &FeeRatio{ + Price: sdk.NewInt64Coin("apple", 30), + Fee: sdk.NewInt64Coin("banana", 31), + }, + }, + { + name: "price has decimal", + ratio: "123.4apple:5banana", + expErr: "price: invalid coin expression: \"123.4apple\"", + }, + { + name: "fee has decimal", + ratio: "123apple:5.6banana", + expErr: "fee: invalid coin expression: \"5.6banana\"", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if len(tc.expErr) > 0 { + tc.expErr = fmt.Sprintf("cannot create FeeRatio from %q: %s", tc.ratio, tc.expErr) + } + + var ratio *FeeRatio + var err error + testFunc := func() { + ratio, err = ParseFeeRatio(tc.ratio) + } + require.NotPanics(t, testFunc, "ParseFeeRatio(%q)", tc.ratio) + assertions.AssertErrorValue(t, err, tc.expErr, "ParseFeeRatio(%q)", tc.ratio) + assert.Equal(t, ratioStr(tc.expRatio), ratioStr(ratio), "ParseFeeRatio(%q)", tc.ratio) + + var ratioMust FeeRatio + testFuncMust := func() { + ratioMust = MustParseFeeRatio(tc.ratio) + } + assertions.RequirePanicEquals(t, testFuncMust, tc.expErr, "MustParseFeeRatio(%q)", tc.ratio) + if tc.expRatio != nil { + assert.Equal(t, ratioStr(tc.expRatio), ratioStr(&ratioMust), "MustParseFeeRatio(%q)", tc.ratio) + } + }) + } +} + func TestFeeRatio_String(t *testing.T) { coin := func(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} From f48c014aba69f7fecf7f0c2459fce71278b86fd7 Mon Sep 17 00:00:00 2001 From: Daniel Wedul Date: Tue, 17 Oct 2023 15:53:17 -0600 Subject: [PATCH 309/309] [1658]: Set the exchange params to the defaults in the upgrade. --- app/upgrades.go | 23 +++++++++++-- app/upgrades_test.go | 78 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/app/upgrades.go b/app/upgrades.go index 32606cba94..38d4e0abae 100644 --- a/app/upgrades.go +++ b/app/upgrades.go @@ -114,6 +114,7 @@ var upgrades = map[string]appUpgrade{ removeInactiveValidatorDelegations(ctx, app) setupICQ(ctx, app) updateMaxSupply(ctx, app) + setExchangeParams(ctx, app) return vm, nil }, @@ -133,6 +134,7 @@ var upgrades = map[string]appUpgrade{ removeInactiveValidatorDelegations(ctx, app) setupICQ(ctx, app) updateMaxSupply(ctx, app) + setExchangeParams(ctx, app) return vm, nil }, @@ -325,14 +327,16 @@ func setAccountDataNameRecord(ctx sdk.Context, accountK attributetypes.AccountKe return attributekeeper.EnsureModuleAccountAndAccountDataNameRecord(ctx, accountK, nameK) } -// setupICQ sets the correct default values for ICQKeeper +// setupICQ sets the correct default values for ICQKeeper. +// TODO: Remove with the saffron handlers. func setupICQ(ctx sdk.Context, app *App) { ctx.Logger().Info("Updating ICQ params") app.ICQKeeper.SetParams(ctx, icqtypes.NewParams(true, []string{"/provenance.oracle.v1.Query/Oracle"})) ctx.Logger().Info("Done updating ICQ params") } -// updateMaxSupply sets the value of max supply to the current value of MaxTotalSupply +// updateMaxSupply sets the value of max supply to the current value of MaxTotalSupply. +// TODO: Remove with the saffron handlers. func updateMaxSupply(ctx sdk.Context, app *App) { ctx.Logger().Info("Updating MaxSupply marker param") params := app.MarkerKeeper.GetParams(ctx) @@ -341,3 +345,18 @@ func updateMaxSupply(ctx sdk.Context, app *App) { app.MarkerKeeper.SetParams(ctx, params) ctx.Logger().Info("Done updating MaxSupply marker param") } + +// setExchangeParams sets exchange module's params to the defaults. +// TODO: Remove with the saffron handlers. +func setExchangeParams(ctx sdk.Context, app *App) { + ctx.Logger().Info("Ensuring exchange module params are set.") + params := app.ExchangeKeeper.GetParams(ctx) + if params != nil { + ctx.Logger().Info("Exchange module params are already defined.") + } else { + params = exchange.DefaultParams() + ctx.Logger().Info("Setting exchange module params to defaults.") + app.ExchangeKeeper.SetParams(ctx, params) + } + ctx.Logger().Info("Done ensuring exchange module params are set.") +} diff --git a/app/upgrades_test.go b/app/upgrades_test.go index 769330a3b9..f264b24dee 100644 --- a/app/upgrades_test.go +++ b/app/upgrades_test.go @@ -24,6 +24,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/provenance-io/provenance/x/exchange" msgfeetypes "github.com/provenance-io/provenance/x/msgfees/types" ) @@ -426,6 +427,7 @@ func (s *UpgradeTestSuite) TestSaffronRC1() { "INF Done updating ICQ params", "INF Updating MaxSupply marker param", "INF Done updating MaxSupply marker param", + "INF Ensuring exchange module params are set.", } s.AssertUpgradeHandlerLogs("saffron-rc1", expInLog, nil) @@ -442,6 +444,7 @@ func (s *UpgradeTestSuite) TestSaffron() { "INF Done updating ICQ params", "INF Updating MaxSupply marker param", "INF Done updating MaxSupply marker param", + "INF Ensuring exchange module params are set.", } s.AssertUpgradeHandlerLogs("saffron", expInLog, nil) @@ -841,3 +844,78 @@ func (s *UpgradeTestSuite) TestSetAccountDataNameRecord() { s.Require().NoError(err, "setAccountDataNameRecord") s.AssertLogContents(logOutput, expInLog, nil, true, "setAccountDataNameRecord") } + +func (s *UpgradeTestSuite) TestSetExchangeParams() { + startMsg := "INF Ensuring exchange module params are set." + noopMsg := "INF Exchange module params are already defined." + updateMsg := "INF Setting exchange module params to defaults." + doneMsg := "INF Done ensuring exchange module params are set." + + tests := []struct { + name string + existingParams *exchange.Params + expectedParams *exchange.Params + expInLog []string + }{ + { + name: "no params set yet", + existingParams: nil, + expectedParams: exchange.DefaultParams(), + expInLog: []string{startMsg, updateMsg, doneMsg}, + }, + { + name: "params set with no splits and default zero", + existingParams: &exchange.Params{DefaultSplit: 0}, + expectedParams: &exchange.Params{DefaultSplit: 0}, + expInLog: []string{startMsg, noopMsg, doneMsg}, + }, + { + name: "params set with no splits and different default", + existingParams: &exchange.Params{DefaultSplit: exchange.DefaultDefaultSplit + 100}, + expectedParams: &exchange.Params{DefaultSplit: exchange.DefaultDefaultSplit + 100}, + expInLog: []string{startMsg, noopMsg, doneMsg}, + }, + { + name: "params set with some splits", + existingParams: &exchange.Params{ + DefaultSplit: exchange.DefaultDefaultSplit + 100, + DenomSplits: []exchange.DenomSplit{ + {Denom: "peach", Split: 3000}, + {Denom: "plum", Split: 100}, + }, + }, + expectedParams: &exchange.Params{ + DefaultSplit: exchange.DefaultDefaultSplit + 100, + DenomSplits: []exchange.DenomSplit{ + {Denom: "peach", Split: 3000}, + {Denom: "plum", Split: 100}, + }, + }, + expInLog: []string{startMsg, noopMsg, doneMsg}, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + s.app.ExchangeKeeper.SetParams(s.ctx, tc.existingParams) + + // Reset the log buffer and call the fun. Relog the output if it panics. + s.logBuffer.Reset() + testFunc := func() { + setExchangeParams(s.ctx, s.app) + } + didNotPanic := s.Assert().NotPanics(testFunc, "setExchangeParams") + logOutput := s.GetLogOutput("setExchangeParams") + if !didNotPanic { + return + } + + // Make sure the log has the expected lines. + s.AssertLogContents(logOutput, tc.expInLog, nil, true, "setExchangeParams") + + // Make sure the params are as expected now. + params := s.app.ExchangeKeeper.GetParams(s.ctx) + s.Assert().Equal(tc.expectedParams, params, "params after setExchangeParams") + }) + } +}